Repository: AGProjects/python-sipsimple Branch: master Commit: a644f25948de Files: 1819 Total size: 18.8 MB Directory structure: gitextract_iouho92m/ ├── .boring ├── AUTHORS ├── LICENSE ├── MANIFEST.in ├── README ├── TODO ├── build_inplace ├── debian/ │ ├── changelog │ ├── compat │ ├── control │ ├── copyright │ ├── docs │ ├── python-sipsimple.lintian-overrides │ ├── rules │ └── source/ │ └── format ├── deps/ │ └── pjsip/ │ ├── COPYING │ ├── Makefile │ ├── README.txt │ ├── aconfigure │ ├── aconfigure.ac │ ├── base_rev │ ├── build/ │ │ ├── cc-auto.mak.in │ │ ├── cc-gcc.mak │ │ ├── common.mak │ │ ├── host-mingw.mak │ │ ├── host-unix.mak │ │ ├── host-win32.mak │ │ ├── m-arm.mak │ │ ├── m-auto.mak │ │ ├── m-i386.mak │ │ ├── m-x86_64.mak │ │ ├── os-auto.mak.in │ │ └── rules.mak │ ├── build.mak.in │ ├── config.guess │ ├── config.sub │ ├── configure │ ├── install-sh │ ├── pjlib/ │ │ ├── build/ │ │ │ ├── Makefile │ │ │ └── os-auto.mak.in │ │ ├── docs/ │ │ │ ├── doxygen.cfg │ │ │ ├── doxygen.css │ │ │ ├── footer.html │ │ │ └── header.html │ │ ├── include/ │ │ │ ├── pj/ │ │ │ │ ├── activesock.h │ │ │ │ ├── addr_resolv.h │ │ │ │ ├── array.h │ │ │ │ ├── assert.h │ │ │ │ ├── compat/ │ │ │ │ │ ├── assert.h │ │ │ │ │ ├── cc_armcc.h │ │ │ │ │ ├── cc_codew.h │ │ │ │ │ ├── cc_gcc.h │ │ │ │ │ ├── cc_gcce.h │ │ │ │ │ ├── cc_msvc.h │ │ │ │ │ ├── cc_mwcc.h │ │ │ │ │ ├── ctype.h │ │ │ │ │ ├── errno.h │ │ │ │ │ ├── high_precision.h │ │ │ │ │ ├── m_alpha.h │ │ │ │ │ ├── m_armv4.h │ │ │ │ │ ├── m_auto.h.in │ │ │ │ │ ├── m_i386.h │ │ │ │ │ ├── m_m68k.h │ │ │ │ │ ├── m_powerpc.h │ │ │ │ │ ├── m_sparc.h │ │ │ │ │ ├── m_x86_64.h │ │ │ │ │ ├── malloc.h │ │ │ │ │ ├── os_auto.h.in │ │ │ │ │ ├── os_darwinos.h │ │ │ │ │ ├── os_linux.h │ │ │ │ │ ├── os_linux_kernel.h │ │ │ │ │ ├── os_palmos.h │ │ │ │ │ ├── os_rtems.h │ │ │ │ │ ├── os_sunos.h │ │ │ │ │ ├── os_symbian.h │ │ │ │ │ ├── os_win32.h │ │ │ │ │ ├── os_win32_wince.h │ │ │ │ │ ├── rand.h │ │ │ │ │ ├── setjmp.h │ │ │ │ │ ├── size_t.h │ │ │ │ │ ├── socket.h │ │ │ │ │ ├── stdarg.h │ │ │ │ │ ├── stdfileio.h │ │ │ │ │ ├── string.h │ │ │ │ │ └── time.h │ │ │ │ ├── config.h │ │ │ │ ├── config_site_sample.h │ │ │ │ ├── ctype.h │ │ │ │ ├── doxygen.h │ │ │ │ ├── errno.h │ │ │ │ ├── except.h │ │ │ │ ├── fifobuf.h │ │ │ │ ├── file_access.h │ │ │ │ ├── file_io.h │ │ │ │ ├── guid.h │ │ │ │ ├── hash.h │ │ │ │ ├── ioqueue.h │ │ │ │ ├── ip_helper.h │ │ │ │ ├── list.h │ │ │ │ ├── list_i.h │ │ │ │ ├── lock.h │ │ │ │ ├── log.h │ │ │ │ ├── math.h │ │ │ │ ├── os.h │ │ │ │ ├── pool.h │ │ │ │ ├── pool_alt.h │ │ │ │ ├── pool_buf.h │ │ │ │ ├── pool_i.h │ │ │ │ ├── rand.h │ │ │ │ ├── rbtree.h │ │ │ │ ├── sock.h │ │ │ │ ├── sock_qos.h │ │ │ │ ├── sock_select.h │ │ │ │ ├── ssl_sock.h │ │ │ │ ├── string.h │ │ │ │ ├── string_i.h │ │ │ │ ├── timer.h │ │ │ │ ├── types.h │ │ │ │ └── unicode.h │ │ │ ├── pj++/ │ │ │ │ ├── file.hpp │ │ │ │ ├── hash.hpp │ │ │ │ ├── list.hpp │ │ │ │ ├── lock.hpp │ │ │ │ ├── os.hpp │ │ │ │ ├── pool.hpp │ │ │ │ ├── proactor.hpp │ │ │ │ ├── scanner.hpp │ │ │ │ ├── sock.hpp │ │ │ │ ├── string.hpp │ │ │ │ ├── timer.hpp │ │ │ │ ├── tree.hpp │ │ │ │ └── types.hpp │ │ │ ├── pjlib++.hpp │ │ │ └── pjlib.h │ │ └── src/ │ │ └── pj/ │ │ ├── activesock.c │ │ ├── addr_resolv_linux_kernel.c │ │ ├── addr_resolv_sock.c │ │ ├── addr_resolv_symbian.cpp │ │ ├── array.c │ │ ├── compat/ │ │ │ ├── longjmp_i386.S │ │ │ ├── setjmp_i386.S │ │ │ ├── sigjmp.c │ │ │ ├── string.c │ │ │ └── string_compat.c │ │ ├── config.c │ │ ├── ctype.c │ │ ├── errno.c │ │ ├── except.c │ │ ├── exception_symbian.cpp │ │ ├── extra-exports.c │ │ ├── fifobuf.c │ │ ├── file_access_unistd.c │ │ ├── file_access_win32.c │ │ ├── file_io_ansi.c │ │ ├── file_io_win32.c │ │ ├── guid.c │ │ ├── guid_simple.c │ │ ├── guid_uuid.c │ │ ├── guid_win32.c │ │ ├── hash.c │ │ ├── ioqueue_common_abs.c │ │ ├── ioqueue_common_abs.h │ │ ├── ioqueue_dummy.c │ │ ├── ioqueue_epoll.c │ │ ├── ioqueue_linux_kernel.c │ │ ├── ioqueue_select.c │ │ ├── ioqueue_symbian.cpp │ │ ├── ioqueue_winnt.c │ │ ├── ip_helper_generic.c │ │ ├── ip_helper_symbian.cpp │ │ ├── ip_helper_win32.c │ │ ├── list.c │ │ ├── lock.c │ │ ├── log.c │ │ ├── log_writer_printk.c │ │ ├── log_writer_stdout.c │ │ ├── log_writer_symbian_console.cpp │ │ ├── os_core_darwin.m │ │ ├── os_core_linux_kernel.c │ │ ├── os_core_symbian.cpp │ │ ├── os_core_unix.c │ │ ├── os_core_win32.c │ │ ├── os_error_linux_kernel.c │ │ ├── os_error_symbian.cpp │ │ ├── os_error_unix.c │ │ ├── os_error_win32.c │ │ ├── os_info.c │ │ ├── os_info_iphone.m │ │ ├── os_info_symbian.cpp │ │ ├── os_rwmutex.c │ │ ├── os_symbian.h │ │ ├── os_time_bsd.c │ │ ├── os_time_common.c │ │ ├── os_time_linux_kernel.c │ │ ├── os_time_unix.c │ │ ├── os_time_win32.c │ │ ├── os_timestamp_common.c │ │ ├── os_timestamp_linux_kernel.c │ │ ├── os_timestamp_posix.c │ │ ├── os_timestamp_win32.c │ │ ├── pool.c │ │ ├── pool_buf.c │ │ ├── pool_caching.c │ │ ├── pool_dbg.c │ │ ├── pool_policy_kmalloc.c │ │ ├── pool_policy_malloc.c │ │ ├── pool_policy_new.cpp │ │ ├── pool_signature.h │ │ ├── rand.c │ │ ├── rbtree.c │ │ ├── sock_bsd.c │ │ ├── sock_common.c │ │ ├── sock_linux_kernel.c │ │ ├── sock_qos_bsd.c │ │ ├── sock_qos_common.c │ │ ├── sock_qos_dummy.c │ │ ├── sock_qos_symbian.cpp │ │ ├── sock_qos_wm.c │ │ ├── sock_select.c │ │ ├── sock_select_symbian.cpp │ │ ├── sock_symbian.cpp │ │ ├── ssl_sock_common.c │ │ ├── ssl_sock_dump.c │ │ ├── ssl_sock_ossl.c │ │ ├── ssl_sock_symbian.cpp │ │ ├── string.c │ │ ├── symbols.c │ │ ├── timer.c │ │ ├── timer_symbian.cpp │ │ ├── types.c │ │ ├── unicode_symbian.cpp │ │ └── unicode_win32.c │ ├── pjlib-util/ │ │ ├── build/ │ │ │ └── Makefile │ │ ├── docs/ │ │ │ ├── doxygen.cfg │ │ │ ├── doxygen.css │ │ │ ├── footer.html │ │ │ └── header.html │ │ ├── include/ │ │ │ ├── pjlib-util/ │ │ │ │ ├── base64.h │ │ │ │ ├── config.h │ │ │ │ ├── crc32.h │ │ │ │ ├── dns.h │ │ │ │ ├── dns_server.h │ │ │ │ ├── errno.h │ │ │ │ ├── getopt.h │ │ │ │ ├── hmac_md5.h │ │ │ │ ├── hmac_sha1.h │ │ │ │ ├── http_client.h │ │ │ │ ├── json.h │ │ │ │ ├── md5.h │ │ │ │ ├── pcap.h │ │ │ │ ├── resolver.h │ │ │ │ ├── scanner.h │ │ │ │ ├── scanner_cis_bitwise.h │ │ │ │ ├── scanner_cis_uint.h │ │ │ │ ├── sha1.h │ │ │ │ ├── srv_resolver.h │ │ │ │ ├── string.h │ │ │ │ ├── stun_simple.h │ │ │ │ ├── types.h │ │ │ │ └── xml.h │ │ │ └── pjlib-util.h │ │ └── src/ │ │ └── pjlib-util/ │ │ ├── base64.c │ │ ├── crc32.c │ │ ├── dns.c │ │ ├── dns_dump.c │ │ ├── dns_server.c │ │ ├── errno.c │ │ ├── getopt.c │ │ ├── hmac_md5.c │ │ ├── hmac_sha1.c │ │ ├── http_client.c │ │ ├── json.c │ │ ├── md5.c │ │ ├── pcap.c │ │ ├── resolver.c │ │ ├── resolver_wrap.cpp │ │ ├── scanner.c │ │ ├── scanner_cis_bitwise.c │ │ ├── scanner_cis_uint.c │ │ ├── sha1.c │ │ ├── srv_resolver.c │ │ ├── string.c │ │ ├── stun_simple.c │ │ ├── stun_simple_client.c │ │ ├── symbols.c │ │ ├── xml.c │ │ └── xml_wrap.cpp │ ├── pjmedia/ │ │ ├── README.txt │ │ ├── build/ │ │ │ ├── Makefile │ │ │ └── os-auto.mak.in │ │ ├── docs/ │ │ │ ├── doxygen.cfg │ │ │ ├── footer.html │ │ │ ├── header.html │ │ │ └── media-flow.vsd │ │ ├── include/ │ │ │ ├── pjmedia/ │ │ │ │ ├── alaw_ulaw.h │ │ │ │ ├── avi.h │ │ │ │ ├── avi_stream.h │ │ │ │ ├── bidirectional.h │ │ │ │ ├── circbuf.h │ │ │ │ ├── clock.h │ │ │ │ ├── codec.h │ │ │ │ ├── conference.h │ │ │ │ ├── config.h │ │ │ │ ├── config_auto.h.in │ │ │ │ ├── converter.h │ │ │ │ ├── delaybuf.h │ │ │ │ ├── doxygen.h │ │ │ │ ├── echo.h │ │ │ │ ├── echo_port.h │ │ │ │ ├── endpoint.h │ │ │ │ ├── errno.h │ │ │ │ ├── event.h │ │ │ │ ├── format.h │ │ │ │ ├── frame.h │ │ │ │ ├── g711.h │ │ │ │ ├── jbuf.h │ │ │ │ ├── master_port.h │ │ │ │ ├── mem_port.h │ │ │ │ ├── mixer_port.h │ │ │ │ ├── null_port.h │ │ │ │ ├── plc.h │ │ │ │ ├── port.h │ │ │ │ ├── resample.h │ │ │ │ ├── rtcp.h │ │ │ │ ├── rtcp_xr.h │ │ │ │ ├── rtp.h │ │ │ │ ├── sdp.h │ │ │ │ ├── sdp_neg.h │ │ │ │ ├── session.h │ │ │ │ ├── signatures.h │ │ │ │ ├── silencedet.h │ │ │ │ ├── sound.h │ │ │ │ ├── sound_port.h │ │ │ │ ├── splitcomb.h │ │ │ │ ├── stereo.h │ │ │ │ ├── stream.h │ │ │ │ ├── stream_common.h │ │ │ │ ├── symbian_sound_aps.h │ │ │ │ ├── tonegen.h │ │ │ │ ├── transport.h │ │ │ │ ├── transport_adapter_sample.h │ │ │ │ ├── transport_ice.h │ │ │ │ ├── transport_loop.h │ │ │ │ ├── transport_srtp.h │ │ │ │ ├── transport_udp.h │ │ │ │ ├── transport_zrtp.h │ │ │ │ ├── types.h │ │ │ │ ├── vid_codec.h │ │ │ │ ├── vid_codec_util.h │ │ │ │ ├── vid_port.h │ │ │ │ ├── vid_stream.h │ │ │ │ ├── vid_tee.h │ │ │ │ ├── wav_playlist.h │ │ │ │ ├── wav_port.h │ │ │ │ ├── wave.h │ │ │ │ └── wsola.h │ │ │ ├── pjmedia-audiodev/ │ │ │ │ ├── audiodev.h │ │ │ │ ├── audiodev_imp.h │ │ │ │ ├── config.h │ │ │ │ └── errno.h │ │ │ ├── pjmedia-codec/ │ │ │ │ ├── audio_codecs.h │ │ │ │ ├── config.h │ │ │ │ ├── config_auto.h.in │ │ │ │ ├── ffmpeg_vid_codecs.h │ │ │ │ ├── g722.h │ │ │ │ ├── g7221.h │ │ │ │ ├── g7221_sdp_match.h │ │ │ │ ├── gsm.h │ │ │ │ ├── h263_packetizer.h │ │ │ │ ├── h264_packetizer.h │ │ │ │ ├── ilbc.h │ │ │ │ ├── openh264.h │ │ │ │ ├── opus.h │ │ │ │ ├── passthrough.h │ │ │ │ ├── speex.h │ │ │ │ ├── types.h │ │ │ │ └── vpx.h │ │ │ ├── pjmedia-codec.h │ │ │ ├── pjmedia-videodev/ │ │ │ │ ├── avi_dev.h │ │ │ │ ├── config.h │ │ │ │ ├── errno.h │ │ │ │ ├── fb_dev.h │ │ │ │ ├── videodev.h │ │ │ │ └── videodev_imp.h │ │ │ ├── pjmedia.h │ │ │ ├── pjmedia_audiodev.h │ │ │ └── pjmedia_videodev.h │ │ └── src/ │ │ ├── pjmedia/ │ │ │ ├── alaw_ulaw.c │ │ │ ├── alaw_ulaw_table.c │ │ │ ├── avi_player.c │ │ │ ├── bidirectional.c │ │ │ ├── clock_thread.c │ │ │ ├── codec.c │ │ │ ├── conf_switch.c │ │ │ ├── conference.c │ │ │ ├── converter.c │ │ │ ├── converter_libswscale.c │ │ │ ├── converter_libyuv.c │ │ │ ├── delaybuf.c │ │ │ ├── dummy.c │ │ │ ├── echo_common.c │ │ │ ├── echo_internal.h │ │ │ ├── echo_port.c │ │ │ ├── echo_speex.c │ │ │ ├── echo_suppress.c │ │ │ ├── echo_webrtc_aec.c │ │ │ ├── endpoint.c │ │ │ ├── errno.c │ │ │ ├── event.c │ │ │ ├── ffmpeg_util.c │ │ │ ├── ffmpeg_util.h │ │ │ ├── format.c │ │ │ ├── g711.c │ │ │ ├── jbuf.c │ │ │ ├── master_port.c │ │ │ ├── mem_capture.c │ │ │ ├── mem_player.c │ │ │ ├── mixer_port.c │ │ │ ├── null_port.c │ │ │ ├── plc_common.c │ │ │ ├── port.c │ │ │ ├── resample_libsamplerate.c │ │ │ ├── resample_port.c │ │ │ ├── resample_resample.c │ │ │ ├── resample_speex.c │ │ │ ├── rtcp.c │ │ │ ├── rtcp_xr.c │ │ │ ├── rtp.c │ │ │ ├── sdp.c │ │ │ ├── sdp_cmp.c │ │ │ ├── sdp_neg.c │ │ │ ├── sdp_wrap.cpp │ │ │ ├── session.c │ │ │ ├── silencedet.c │ │ │ ├── sound_legacy.c │ │ │ ├── sound_port.c │ │ │ ├── splitcomb.c │ │ │ ├── stereo_port.c │ │ │ ├── stream.c │ │ │ ├── stream_common.c │ │ │ ├── stream_info.c │ │ │ ├── tonegen.c │ │ │ ├── transport_adapter_sample.c │ │ │ ├── transport_ice.c │ │ │ ├── transport_loop.c │ │ │ ├── transport_srtp.c │ │ │ ├── transport_udp.c │ │ │ ├── transport_zrtp.c │ │ │ ├── types.c │ │ │ ├── vid_codec.c │ │ │ ├── vid_codec_util.c │ │ │ ├── vid_port.c │ │ │ ├── vid_stream.c │ │ │ ├── vid_stream_info.c │ │ │ ├── vid_tee.c │ │ │ ├── wav_player.c │ │ │ ├── wav_playlist.c │ │ │ ├── wav_writer.c │ │ │ ├── wave.c │ │ │ └── wsola.c │ │ ├── pjmedia-audiodev/ │ │ │ ├── alsa_dev.c │ │ │ ├── audiodev.c │ │ │ ├── coreaudio_dev.m │ │ │ ├── errno.c │ │ │ ├── null_dev.c │ │ │ └── wmme_dev.c │ │ ├── pjmedia-codec/ │ │ │ ├── audio_codecs.c │ │ │ ├── ffmpeg_vid_codecs.c │ │ │ ├── g722/ │ │ │ │ ├── g722_dec.c │ │ │ │ ├── g722_dec.h │ │ │ │ ├── g722_enc.c │ │ │ │ └── g722_enc.h │ │ │ ├── g722.c │ │ │ ├── g7221.c │ │ │ ├── g7221_sdp_match.c │ │ │ ├── gsm.c │ │ │ ├── h263_packetizer.c │ │ │ ├── h264_packetizer.c │ │ │ ├── ilbc.c │ │ │ ├── openh264.cpp │ │ │ ├── opus.c │ │ │ ├── passthrough.c │ │ │ ├── speex_codec.c │ │ │ └── vpx.c │ │ └── pjmedia-videodev/ │ │ ├── avf_dev.m │ │ ├── avi_dev.c │ │ ├── colorbar_dev.c │ │ ├── dshow_dev.c │ │ ├── dshow_filter.cpp │ │ ├── errno.c │ │ ├── fb_dev.c │ │ ├── null_dev.c │ │ ├── util.c │ │ ├── util.h │ │ ├── v4l2_dev.c │ │ └── videodev.c │ ├── pjnath/ │ │ ├── build/ │ │ │ └── Makefile │ │ ├── docs/ │ │ │ ├── UML-class-diagram.dia │ │ │ ├── doc_ice.h │ │ │ ├── doc_mainpage.h │ │ │ ├── doc_nat.h │ │ │ ├── doc_samples.h │ │ │ ├── doc_stun.h │ │ │ ├── doc_turn.h │ │ │ ├── doxygen.cfg │ │ │ ├── doxygen.css │ │ │ ├── footer.html │ │ │ └── header.html │ │ ├── include/ │ │ │ ├── pjnath/ │ │ │ │ ├── config.h │ │ │ │ ├── errno.h │ │ │ │ ├── ice_session.h │ │ │ │ ├── ice_strans.h │ │ │ │ ├── nat_detect.h │ │ │ │ ├── stun_auth.h │ │ │ │ ├── stun_config.h │ │ │ │ ├── stun_msg.h │ │ │ │ ├── stun_session.h │ │ │ │ ├── stun_sock.h │ │ │ │ ├── stun_transaction.h │ │ │ │ ├── turn_session.h │ │ │ │ ├── turn_sock.h │ │ │ │ └── types.h │ │ │ └── pjnath.h │ │ └── src/ │ │ ├── pjnath/ │ │ │ ├── errno.c │ │ │ ├── ice_session.c │ │ │ ├── ice_strans.c │ │ │ ├── nat_detect.c │ │ │ ├── stun_auth.c │ │ │ ├── stun_msg.c │ │ │ ├── stun_msg_dump.c │ │ │ ├── stun_session.c │ │ │ ├── stun_sock.c │ │ │ ├── stun_transaction.c │ │ │ ├── turn_session.c │ │ │ └── turn_sock.c │ │ ├── pjturn-client/ │ │ │ └── client_main.c │ │ └── pjturn-srv/ │ │ ├── allocation.c │ │ ├── auth.c │ │ ├── auth.h │ │ ├── listener_tcp.c │ │ ├── listener_udp.c │ │ ├── main.c │ │ ├── server.c │ │ └── turn.h │ ├── pjsip/ │ │ ├── build/ │ │ │ ├── Makefile │ │ │ └── os-auto.mak.in │ │ ├── docs/ │ │ │ ├── PJSUA-TESTING.txt │ │ │ ├── TRANSPORT-PROBLEMS.TXT │ │ │ ├── doxygen.cfg │ │ │ ├── doxygen.h │ │ │ ├── footer.html │ │ │ └── header.html │ │ ├── include/ │ │ │ ├── pjsip/ │ │ │ │ ├── print_util.h │ │ │ │ ├── sip_auth.h │ │ │ │ ├── sip_auth_aka.h │ │ │ │ ├── sip_auth_msg.h │ │ │ │ ├── sip_auth_parser.h │ │ │ │ ├── sip_autoconf.h.in │ │ │ │ ├── sip_config.h │ │ │ │ ├── sip_dialog.h │ │ │ │ ├── sip_endpoint.h │ │ │ │ ├── sip_errno.h │ │ │ │ ├── sip_event.h │ │ │ │ ├── sip_module.h │ │ │ │ ├── sip_msg.h │ │ │ │ ├── sip_multipart.h │ │ │ │ ├── sip_parser.h │ │ │ │ ├── sip_private.h │ │ │ │ ├── sip_resolve.h │ │ │ │ ├── sip_tel_uri.h │ │ │ │ ├── sip_transaction.h │ │ │ │ ├── sip_transport.h │ │ │ │ ├── sip_transport_loop.h │ │ │ │ ├── sip_transport_tcp.h │ │ │ │ ├── sip_transport_tls.h │ │ │ │ ├── sip_transport_udp.h │ │ │ │ ├── sip_types.h │ │ │ │ ├── sip_ua_layer.h │ │ │ │ ├── sip_uri.h │ │ │ │ └── sip_util.h │ │ │ ├── pjsip-simple/ │ │ │ │ ├── errno.h │ │ │ │ ├── evsub.h │ │ │ │ ├── evsub_msg.h │ │ │ │ ├── iscomposing.h │ │ │ │ ├── mwi.h │ │ │ │ ├── pidf.h │ │ │ │ ├── presence.h │ │ │ │ ├── publish.h │ │ │ │ ├── rpid.h │ │ │ │ ├── types.h │ │ │ │ └── xpidf.h │ │ │ ├── pjsip-ua/ │ │ │ │ ├── sip_100rel.h │ │ │ │ ├── sip_inv.h │ │ │ │ ├── sip_regc.h │ │ │ │ ├── sip_replaces.h │ │ │ │ ├── sip_timer.h │ │ │ │ └── sip_xfer.h │ │ │ ├── pjsip.h │ │ │ ├── pjsip_auth.h │ │ │ ├── pjsip_simple.h │ │ │ └── pjsip_ua.h │ │ └── src/ │ │ ├── pjsip/ │ │ │ ├── sip_auth_aka.c │ │ │ ├── sip_auth_client.c │ │ │ ├── sip_auth_msg.c │ │ │ ├── sip_auth_parser.c │ │ │ ├── sip_auth_parser_wrap.cpp │ │ │ ├── sip_auth_server.c │ │ │ ├── sip_config.c │ │ │ ├── sip_dialog.c │ │ │ ├── sip_dialog_wrap.cpp │ │ │ ├── sip_endpoint.c │ │ │ ├── sip_endpoint_wrap.cpp │ │ │ ├── sip_errno.c │ │ │ ├── sip_msg.c │ │ │ ├── sip_multipart.c │ │ │ ├── sip_parser.c │ │ │ ├── sip_parser_wrap.cpp │ │ │ ├── sip_resolve.c │ │ │ ├── sip_tel_uri.c │ │ │ ├── sip_tel_uri_wrap.cpp │ │ │ ├── sip_transaction.c │ │ │ ├── sip_transport.c │ │ │ ├── sip_transport_loop.c │ │ │ ├── sip_transport_tcp.c │ │ │ ├── sip_transport_tls.c │ │ │ ├── sip_transport_udp.c │ │ │ ├── sip_transport_wrap.cpp │ │ │ ├── sip_ua_layer.c │ │ │ ├── sip_uri.c │ │ │ ├── sip_util.c │ │ │ ├── sip_util_proxy.c │ │ │ ├── sip_util_proxy_wrap.cpp │ │ │ ├── sip_util_statefull.c │ │ │ └── sip_util_wrap.cpp │ │ ├── pjsip-simple/ │ │ │ ├── errno.c │ │ │ ├── evsub.c │ │ │ ├── evsub_msg.c │ │ │ ├── iscomposing.c │ │ │ ├── mwi.c │ │ │ ├── pidf.c │ │ │ ├── presence.c │ │ │ ├── presence_body.c │ │ │ ├── publishc.c │ │ │ ├── rpid.c │ │ │ └── xpidf.c │ │ └── pjsip-ua/ │ │ ├── sip_100rel.c │ │ ├── sip_inv.c │ │ ├── sip_reg.c │ │ ├── sip_replaces.c │ │ ├── sip_timer.c │ │ └── sip_xfer.c │ ├── third_party/ │ │ ├── README.txt │ │ ├── bdsound/ │ │ │ └── include/ │ │ │ └── bdimad.h │ │ ├── build/ │ │ │ ├── Makefile │ │ │ ├── g7221/ │ │ │ │ └── Makefile │ │ │ ├── gsm/ │ │ │ │ ├── Makefile │ │ │ │ └── config.h │ │ │ ├── ilbc/ │ │ │ │ └── Makefile │ │ │ ├── milenage/ │ │ │ │ └── Makefile │ │ │ ├── opus/ │ │ │ │ └── Makefile │ │ │ ├── os-auto.mak.in │ │ │ ├── resample/ │ │ │ │ ├── Makefile │ │ │ │ └── config.h │ │ │ ├── speex/ │ │ │ │ ├── Makefile │ │ │ │ ├── config.h │ │ │ │ └── speex/ │ │ │ │ └── speex_config_types.h │ │ │ ├── srtp/ │ │ │ │ ├── Makefile │ │ │ │ └── srtp_config.h │ │ │ ├── webrtc/ │ │ │ │ ├── Makefile │ │ │ │ └── os-auto.mak.in │ │ │ └── zsrtp/ │ │ │ └── Makefile │ │ ├── g7221/ │ │ │ ├── common/ │ │ │ │ ├── basic_op.c │ │ │ │ ├── basic_op.h │ │ │ │ ├── basic_op_i.h │ │ │ │ ├── common.c │ │ │ │ ├── config.h │ │ │ │ ├── count.h │ │ │ │ ├── defs.h │ │ │ │ ├── huff_def.h │ │ │ │ ├── huff_tab.c │ │ │ │ ├── huff_tab.h │ │ │ │ ├── tables.c │ │ │ │ ├── tables.h │ │ │ │ └── typedef.h │ │ │ ├── decode/ │ │ │ │ ├── coef2sam.c │ │ │ │ ├── dct4_s.c │ │ │ │ ├── dct4_s.h │ │ │ │ └── decoder.c │ │ │ └── encode/ │ │ │ ├── dct4_a.c │ │ │ ├── dct4_a.h │ │ │ ├── encoder.c │ │ │ └── sam2coef.c │ │ ├── gsm/ │ │ │ ├── COPYRIGHT │ │ │ ├── ChangeLog │ │ │ ├── INSTALL │ │ │ ├── MACHINES │ │ │ ├── MANIFEST │ │ │ ├── Makefile │ │ │ ├── README │ │ │ ├── add-test/ │ │ │ │ ├── add_test.c │ │ │ │ └── add_test.dta │ │ │ ├── inc/ │ │ │ │ ├── config.h │ │ │ │ ├── gsm.h │ │ │ │ ├── private.h │ │ │ │ ├── proto.h │ │ │ │ ├── toast.h │ │ │ │ └── unproto.h │ │ │ ├── man/ │ │ │ │ ├── bitter.1 │ │ │ │ ├── gsm.3 │ │ │ │ ├── gsm_explode.3 │ │ │ │ ├── gsm_option.3 │ │ │ │ ├── gsm_print.3 │ │ │ │ └── toast.1 │ │ │ ├── src/ │ │ │ │ ├── add.c │ │ │ │ ├── code.c │ │ │ │ ├── debug.c │ │ │ │ ├── decode.c │ │ │ │ ├── gsm_create.c │ │ │ │ ├── gsm_decode.c │ │ │ │ ├── gsm_destroy.c │ │ │ │ ├── gsm_encode.c │ │ │ │ ├── gsm_explode.c │ │ │ │ ├── gsm_implode.c │ │ │ │ ├── gsm_option.c │ │ │ │ ├── gsm_print.c │ │ │ │ ├── long_term.c │ │ │ │ ├── lpc.c │ │ │ │ ├── preprocess.c │ │ │ │ ├── rpe.c │ │ │ │ ├── short_term.c │ │ │ │ ├── table.c │ │ │ │ ├── toast.c │ │ │ │ ├── toast_alaw.c │ │ │ │ ├── toast_audio.c │ │ │ │ ├── toast_lin.c │ │ │ │ └── toast_ulaw.c │ │ │ ├── tls/ │ │ │ │ ├── bitter.c │ │ │ │ ├── bitter.dta │ │ │ │ ├── ginger.c │ │ │ │ ├── sour.c │ │ │ │ ├── sour1.dta │ │ │ │ ├── sour2.dta │ │ │ │ ├── sweet.c │ │ │ │ ├── taste.c │ │ │ │ └── taste.h │ │ │ └── tst/ │ │ │ ├── cod2lin.c │ │ │ ├── cod2txt.c │ │ │ ├── gsm2cod.c │ │ │ ├── lin2cod.c │ │ │ ├── lin2txt.c │ │ │ └── run │ │ ├── ilbc/ │ │ │ ├── FrameClassify.c │ │ │ ├── FrameClassify.h │ │ │ ├── LPCdecode.c │ │ │ ├── LPCdecode.h │ │ │ ├── LPCencode.c │ │ │ ├── LPCencode.h │ │ │ ├── StateConstructW.c │ │ │ ├── StateConstructW.h │ │ │ ├── StateSearchW.c │ │ │ ├── StateSearchW.h │ │ │ ├── anaFilter.c │ │ │ ├── anaFilter.h │ │ │ ├── constants.c │ │ │ ├── constants.h │ │ │ ├── createCB.c │ │ │ ├── createCB.h │ │ │ ├── doCPLC.c │ │ │ ├── doCPLC.h │ │ │ ├── enhancer.c │ │ │ ├── enhancer.h │ │ │ ├── filter.c │ │ │ ├── filter.h │ │ │ ├── gainquant.c │ │ │ ├── gainquant.h │ │ │ ├── getCBvec.c │ │ │ ├── getCBvec.h │ │ │ ├── helpfun.c │ │ │ ├── helpfun.h │ │ │ ├── hpInput.c │ │ │ ├── hpInput.h │ │ │ ├── hpOutput.c │ │ │ ├── hpOutput.h │ │ │ ├── iCBConstruct.c │ │ │ ├── iCBConstruct.h │ │ │ ├── iCBSearch.c │ │ │ ├── iCBSearch.h │ │ │ ├── iLBC_decode.c │ │ │ ├── iLBC_decode.h │ │ │ ├── iLBC_define.h │ │ │ ├── iLBC_encode.c │ │ │ ├── iLBC_encode.h │ │ │ ├── iLBC_test.c │ │ │ ├── lsf.c │ │ │ ├── lsf.h │ │ │ ├── packing.c │ │ │ ├── packing.h │ │ │ ├── syntFilter.c │ │ │ └── syntFilter.h │ │ ├── milenage/ │ │ │ ├── milenage.c │ │ │ ├── milenage.h │ │ │ ├── rijndael.c │ │ │ └── rijndael.h │ │ ├── mp3/ │ │ │ ├── BladeMP3EncDLL.h │ │ │ ├── mp3_port.h │ │ │ └── mp3_writer.c │ │ ├── opus/ │ │ │ ├── .gitignore │ │ │ ├── AUTHORS │ │ │ ├── COPYING │ │ │ ├── ChangeLog │ │ │ ├── INSTALL │ │ │ ├── Makefile.am │ │ │ ├── Makefile.mips │ │ │ ├── Makefile.unix │ │ │ ├── NEWS │ │ │ ├── README │ │ │ ├── celt/ │ │ │ │ ├── _kiss_fft_guts.h │ │ │ │ ├── arch.h │ │ │ │ ├── arm/ │ │ │ │ │ ├── arm2gnu.pl │ │ │ │ │ ├── arm_celt_map.c │ │ │ │ │ ├── armcpu.c │ │ │ │ │ ├── armcpu.h │ │ │ │ │ ├── armopts.s.in │ │ │ │ │ ├── celt_ne10_fft.c │ │ │ │ │ ├── celt_ne10_mdct.c │ │ │ │ │ ├── celt_neon_intr.c │ │ │ │ │ ├── celt_pitch_xcorr_arm-gnu.S │ │ │ │ │ ├── celt_pitch_xcorr_arm.s │ │ │ │ │ ├── fft_arm.h │ │ │ │ │ ├── fixed_armv4.h │ │ │ │ │ ├── fixed_armv5e.h │ │ │ │ │ ├── kiss_fft_armv4.h │ │ │ │ │ ├── kiss_fft_armv5e.h │ │ │ │ │ ├── mdct_arm.h │ │ │ │ │ └── pitch_arm.h │ │ │ │ ├── bands.c │ │ │ │ ├── bands.h │ │ │ │ ├── celt.c │ │ │ │ ├── celt.h │ │ │ │ ├── celt_decoder.c │ │ │ │ ├── celt_encoder.c │ │ │ │ ├── celt_lpc.c │ │ │ │ ├── celt_lpc.h │ │ │ │ ├── cpu_support.h │ │ │ │ ├── cwrs.c │ │ │ │ ├── cwrs.h │ │ │ │ ├── ecintrin.h │ │ │ │ ├── entcode.c │ │ │ │ ├── entcode.h │ │ │ │ ├── entdec.c │ │ │ │ ├── entdec.h │ │ │ │ ├── entenc.c │ │ │ │ ├── entenc.h │ │ │ │ ├── fixed_debug.h │ │ │ │ ├── fixed_generic.h │ │ │ │ ├── float_cast.h │ │ │ │ ├── kiss_fft.c │ │ │ │ ├── kiss_fft.h │ │ │ │ ├── laplace.c │ │ │ │ ├── laplace.h │ │ │ │ ├── mathops.c │ │ │ │ ├── mathops.h │ │ │ │ ├── mdct.c │ │ │ │ ├── mdct.h │ │ │ │ ├── mfrngcod.h │ │ │ │ ├── mips/ │ │ │ │ │ ├── celt_mipsr1.h │ │ │ │ │ ├── fixed_generic_mipsr1.h │ │ │ │ │ ├── kiss_fft_mipsr1.h │ │ │ │ │ ├── mdct_mipsr1.h │ │ │ │ │ ├── pitch_mipsr1.h │ │ │ │ │ └── vq_mipsr1.h │ │ │ │ ├── modes.c │ │ │ │ ├── modes.h │ │ │ │ ├── opus_custom_demo.c │ │ │ │ ├── os_support.h │ │ │ │ ├── pitch.c │ │ │ │ ├── pitch.h │ │ │ │ ├── quant_bands.c │ │ │ │ ├── quant_bands.h │ │ │ │ ├── rate.c │ │ │ │ ├── rate.h │ │ │ │ ├── stack_alloc.h │ │ │ │ ├── static_modes_fixed.h │ │ │ │ ├── static_modes_fixed_arm_ne10.h │ │ │ │ ├── static_modes_float.h │ │ │ │ ├── static_modes_float_arm_ne10.h │ │ │ │ ├── tests/ │ │ │ │ │ ├── test_unit_cwrs32.c │ │ │ │ │ ├── test_unit_dft.c │ │ │ │ │ ├── test_unit_entropy.c │ │ │ │ │ ├── test_unit_laplace.c │ │ │ │ │ ├── test_unit_mathops.c │ │ │ │ │ ├── test_unit_mdct.c │ │ │ │ │ ├── test_unit_rotation.c │ │ │ │ │ └── test_unit_types.c │ │ │ │ ├── vq.c │ │ │ │ ├── vq.h │ │ │ │ └── x86/ │ │ │ │ ├── celt_lpc_sse.c │ │ │ │ ├── celt_lpc_sse.h │ │ │ │ ├── pitch_sse.c │ │ │ │ ├── pitch_sse.h │ │ │ │ ├── pitch_sse2.c │ │ │ │ ├── pitch_sse4_1.c │ │ │ │ ├── x86_celt_map.c │ │ │ │ ├── x86cpu.c │ │ │ │ └── x86cpu.h │ │ │ ├── celt_headers.mk │ │ │ ├── celt_sources.mk │ │ │ ├── configure.ac │ │ │ ├── doc/ │ │ │ │ ├── Doxyfile.in │ │ │ │ ├── Makefile.am │ │ │ │ ├── TODO │ │ │ │ ├── customdoxygen.css │ │ │ │ ├── footer.html │ │ │ │ ├── header.html │ │ │ │ └── trivial_example.c │ │ │ ├── include/ │ │ │ │ ├── opus.h │ │ │ │ ├── opus_custom.h │ │ │ │ ├── opus_defines.h │ │ │ │ ├── opus_multistream.h │ │ │ │ └── opus_types.h │ │ │ ├── m4/ │ │ │ │ ├── as-gcc-inline-assembly.m4 │ │ │ │ ├── libtool.m4 │ │ │ │ ├── ltoptions.m4 │ │ │ │ ├── ltsugar.m4 │ │ │ │ ├── ltversion.m4 │ │ │ │ ├── lt~obsolete.m4 │ │ │ │ └── opus-intrinsics.m4 │ │ │ ├── opus-uninstalled.pc.in │ │ │ ├── opus.m4 │ │ │ ├── opus.pc.in │ │ │ ├── opus_headers.mk │ │ │ ├── opus_sources.mk │ │ │ ├── package_version │ │ │ ├── silk/ │ │ │ │ ├── A2NLSF.c │ │ │ │ ├── API.h │ │ │ │ ├── CNG.c │ │ │ │ ├── HP_variable_cutoff.c │ │ │ │ ├── Inlines.h │ │ │ │ ├── LPC_analysis_filter.c │ │ │ │ ├── LPC_inv_pred_gain.c │ │ │ │ ├── LP_variable_cutoff.c │ │ │ │ ├── MacroCount.h │ │ │ │ ├── MacroDebug.h │ │ │ │ ├── NLSF2A.c │ │ │ │ ├── NLSF_VQ.c │ │ │ │ ├── NLSF_VQ_weights_laroia.c │ │ │ │ ├── NLSF_decode.c │ │ │ │ ├── NLSF_del_dec_quant.c │ │ │ │ ├── NLSF_encode.c │ │ │ │ ├── NLSF_stabilize.c │ │ │ │ ├── NLSF_unpack.c │ │ │ │ ├── NSQ.c │ │ │ │ ├── NSQ_del_dec.c │ │ │ │ ├── PLC.c │ │ │ │ ├── PLC.h │ │ │ │ ├── SigProc_FIX.h │ │ │ │ ├── VAD.c │ │ │ │ ├── VQ_WMat_EC.c │ │ │ │ ├── ana_filt_bank_1.c │ │ │ │ ├── arm/ │ │ │ │ │ ├── SigProc_FIX_armv4.h │ │ │ │ │ ├── SigProc_FIX_armv5e.h │ │ │ │ │ ├── macros_armv4.h │ │ │ │ │ └── macros_armv5e.h │ │ │ │ ├── biquad_alt.c │ │ │ │ ├── bwexpander.c │ │ │ │ ├── bwexpander_32.c │ │ │ │ ├── check_control_input.c │ │ │ │ ├── code_signs.c │ │ │ │ ├── control.h │ │ │ │ ├── control_SNR.c │ │ │ │ ├── control_audio_bandwidth.c │ │ │ │ ├── control_codec.c │ │ │ │ ├── debug.c │ │ │ │ ├── debug.h │ │ │ │ ├── dec_API.c │ │ │ │ ├── decode_core.c │ │ │ │ ├── decode_frame.c │ │ │ │ ├── decode_indices.c │ │ │ │ ├── decode_parameters.c │ │ │ │ ├── decode_pitch.c │ │ │ │ ├── decode_pulses.c │ │ │ │ ├── decoder_set_fs.c │ │ │ │ ├── define.h │ │ │ │ ├── enc_API.c │ │ │ │ ├── encode_indices.c │ │ │ │ ├── encode_pulses.c │ │ │ │ ├── errors.h │ │ │ │ ├── fixed/ │ │ │ │ │ ├── LTP_analysis_filter_FIX.c │ │ │ │ │ ├── LTP_scale_ctrl_FIX.c │ │ │ │ │ ├── apply_sine_window_FIX.c │ │ │ │ │ ├── autocorr_FIX.c │ │ │ │ │ ├── burg_modified_FIX.c │ │ │ │ │ ├── corrMatrix_FIX.c │ │ │ │ │ ├── encode_frame_FIX.c │ │ │ │ │ ├── find_LPC_FIX.c │ │ │ │ │ ├── find_LTP_FIX.c │ │ │ │ │ ├── find_pitch_lags_FIX.c │ │ │ │ │ ├── find_pred_coefs_FIX.c │ │ │ │ │ ├── k2a_FIX.c │ │ │ │ │ ├── k2a_Q16_FIX.c │ │ │ │ │ ├── main_FIX.h │ │ │ │ │ ├── mips/ │ │ │ │ │ │ ├── noise_shape_analysis_FIX_mipsr1.h │ │ │ │ │ │ ├── prefilter_FIX_mipsr1.h │ │ │ │ │ │ └── warped_autocorrelation_FIX_mipsr1.h │ │ │ │ │ ├── noise_shape_analysis_FIX.c │ │ │ │ │ ├── pitch_analysis_core_FIX.c │ │ │ │ │ ├── prefilter_FIX.c │ │ │ │ │ ├── process_gains_FIX.c │ │ │ │ │ ├── regularize_correlations_FIX.c │ │ │ │ │ ├── residual_energy16_FIX.c │ │ │ │ │ ├── residual_energy_FIX.c │ │ │ │ │ ├── schur64_FIX.c │ │ │ │ │ ├── schur_FIX.c │ │ │ │ │ ├── solve_LS_FIX.c │ │ │ │ │ ├── structs_FIX.h │ │ │ │ │ ├── vector_ops_FIX.c │ │ │ │ │ ├── warped_autocorrelation_FIX.c │ │ │ │ │ └── x86/ │ │ │ │ │ ├── burg_modified_FIX_sse.c │ │ │ │ │ ├── prefilter_FIX_sse.c │ │ │ │ │ └── vector_ops_FIX_sse.c │ │ │ │ ├── float/ │ │ │ │ │ ├── LPC_analysis_filter_FLP.c │ │ │ │ │ ├── LPC_inv_pred_gain_FLP.c │ │ │ │ │ ├── LTP_analysis_filter_FLP.c │ │ │ │ │ ├── LTP_scale_ctrl_FLP.c │ │ │ │ │ ├── SigProc_FLP.h │ │ │ │ │ ├── apply_sine_window_FLP.c │ │ │ │ │ ├── autocorrelation_FLP.c │ │ │ │ │ ├── burg_modified_FLP.c │ │ │ │ │ ├── bwexpander_FLP.c │ │ │ │ │ ├── corrMatrix_FLP.c │ │ │ │ │ ├── encode_frame_FLP.c │ │ │ │ │ ├── energy_FLP.c │ │ │ │ │ ├── find_LPC_FLP.c │ │ │ │ │ ├── find_LTP_FLP.c │ │ │ │ │ ├── find_pitch_lags_FLP.c │ │ │ │ │ ├── find_pred_coefs_FLP.c │ │ │ │ │ ├── inner_product_FLP.c │ │ │ │ │ ├── k2a_FLP.c │ │ │ │ │ ├── levinsondurbin_FLP.c │ │ │ │ │ ├── main_FLP.h │ │ │ │ │ ├── noise_shape_analysis_FLP.c │ │ │ │ │ ├── pitch_analysis_core_FLP.c │ │ │ │ │ ├── prefilter_FLP.c │ │ │ │ │ ├── process_gains_FLP.c │ │ │ │ │ ├── regularize_correlations_FLP.c │ │ │ │ │ ├── residual_energy_FLP.c │ │ │ │ │ ├── scale_copy_vector_FLP.c │ │ │ │ │ ├── scale_vector_FLP.c │ │ │ │ │ ├── schur_FLP.c │ │ │ │ │ ├── solve_LS_FLP.c │ │ │ │ │ ├── sort_FLP.c │ │ │ │ │ ├── structs_FLP.h │ │ │ │ │ ├── warped_autocorrelation_FLP.c │ │ │ │ │ └── wrappers_FLP.c │ │ │ │ ├── gain_quant.c │ │ │ │ ├── init_decoder.c │ │ │ │ ├── init_encoder.c │ │ │ │ ├── inner_prod_aligned.c │ │ │ │ ├── interpolate.c │ │ │ │ ├── lin2log.c │ │ │ │ ├── log2lin.c │ │ │ │ ├── macros.h │ │ │ │ ├── main.h │ │ │ │ ├── mips/ │ │ │ │ │ ├── NSQ_del_dec_mipsr1.h │ │ │ │ │ ├── macros_mipsr1.h │ │ │ │ │ └── sigproc_fix_mipsr1.h │ │ │ │ ├── pitch_est_defines.h │ │ │ │ ├── pitch_est_tables.c │ │ │ │ ├── process_NLSFs.c │ │ │ │ ├── quant_LTP_gains.c │ │ │ │ ├── resampler.c │ │ │ │ ├── resampler_down2.c │ │ │ │ ├── resampler_down2_3.c │ │ │ │ ├── resampler_private.h │ │ │ │ ├── resampler_private_AR2.c │ │ │ │ ├── resampler_private_IIR_FIR.c │ │ │ │ ├── resampler_private_down_FIR.c │ │ │ │ ├── resampler_private_up2_HQ.c │ │ │ │ ├── resampler_rom.c │ │ │ │ ├── resampler_rom.h │ │ │ │ ├── resampler_structs.h │ │ │ │ ├── shell_coder.c │ │ │ │ ├── sigm_Q15.c │ │ │ │ ├── sort.c │ │ │ │ ├── stereo_LR_to_MS.c │ │ │ │ ├── stereo_MS_to_LR.c │ │ │ │ ├── stereo_decode_pred.c │ │ │ │ ├── stereo_encode_pred.c │ │ │ │ ├── stereo_find_predictor.c │ │ │ │ ├── stereo_quant_pred.c │ │ │ │ ├── structs.h │ │ │ │ ├── sum_sqr_shift.c │ │ │ │ ├── table_LSF_cos.c │ │ │ │ ├── tables.h │ │ │ │ ├── tables_LTP.c │ │ │ │ ├── tables_NLSF_CB_NB_MB.c │ │ │ │ ├── tables_NLSF_CB_WB.c │ │ │ │ ├── tables_gain.c │ │ │ │ ├── tables_other.c │ │ │ │ ├── tables_pitch_lag.c │ │ │ │ ├── tables_pulses_per_block.c │ │ │ │ ├── tuning_parameters.h │ │ │ │ ├── typedef.h │ │ │ │ └── x86/ │ │ │ │ ├── NSQ_del_dec_sse.c │ │ │ │ ├── NSQ_sse.c │ │ │ │ ├── SigProc_FIX_sse.h │ │ │ │ ├── VAD_sse.c │ │ │ │ ├── VQ_WMat_EC_sse.c │ │ │ │ ├── main_sse.h │ │ │ │ └── x86_silk_map.c │ │ │ ├── silk_headers.mk │ │ │ ├── silk_sources.mk │ │ │ ├── src/ │ │ │ │ ├── analysis.c │ │ │ │ ├── analysis.h │ │ │ │ ├── mlp.c │ │ │ │ ├── mlp.h │ │ │ │ ├── mlp_data.c │ │ │ │ ├── opus.c │ │ │ │ ├── opus_compare.c │ │ │ │ ├── opus_decoder.c │ │ │ │ ├── opus_demo.c │ │ │ │ ├── opus_encoder.c │ │ │ │ ├── opus_multistream.c │ │ │ │ ├── opus_multistream_decoder.c │ │ │ │ ├── opus_multistream_encoder.c │ │ │ │ ├── opus_private.h │ │ │ │ ├── repacketizer.c │ │ │ │ ├── repacketizer_demo.c │ │ │ │ └── tansig_table.h │ │ │ ├── test-driver │ │ │ ├── tests/ │ │ │ │ ├── run_vectors.sh │ │ │ │ ├── test_opus_api.c │ │ │ │ ├── test_opus_common.h │ │ │ │ ├── test_opus_decode.c │ │ │ │ ├── test_opus_encode.c │ │ │ │ └── test_opus_padding.c │ │ │ ├── version.mk │ │ │ └── win32/ │ │ │ ├── VS2010/ │ │ │ │ ├── celt.vcxproj │ │ │ │ ├── celt.vcxproj.filters │ │ │ │ ├── opus.sln │ │ │ │ ├── opus.vcxproj │ │ │ │ ├── opus.vcxproj.filters │ │ │ │ ├── opus_demo.vcxproj │ │ │ │ ├── opus_demo.vcxproj.filters │ │ │ │ ├── silk_common.vcxproj │ │ │ │ ├── silk_common.vcxproj.filters │ │ │ │ ├── silk_fixed.vcxproj │ │ │ │ ├── silk_fixed.vcxproj.filters │ │ │ │ ├── silk_float.vcxproj │ │ │ │ ├── silk_float.vcxproj.filters │ │ │ │ ├── test_opus_api.vcxproj │ │ │ │ ├── test_opus_api.vcxproj.filters │ │ │ │ ├── test_opus_decode.vcxproj │ │ │ │ ├── test_opus_decode.vcxproj.filters │ │ │ │ ├── test_opus_encode.vcxproj │ │ │ │ └── test_opus_encode.vcxproj.filters │ │ │ └── genversion.bat │ │ ├── resample/ │ │ │ ├── COPYING │ │ │ ├── README.resample │ │ │ ├── include/ │ │ │ │ └── resamplesubs.h │ │ │ └── src/ │ │ │ ├── largefilter.h │ │ │ ├── libresample_dll.c │ │ │ ├── resample.h │ │ │ ├── resamplesubs.c │ │ │ ├── smallfilter.h │ │ │ └── stddefs.h │ │ ├── speex/ │ │ │ ├── AUTHORS │ │ │ ├── COPYING │ │ │ ├── include/ │ │ │ │ └── speex/ │ │ │ │ ├── speex.h │ │ │ │ ├── speex_bits.h │ │ │ │ ├── speex_buffer.h │ │ │ │ ├── speex_callbacks.h │ │ │ │ ├── speex_config_types.h.in │ │ │ │ ├── speex_echo.h │ │ │ │ ├── speex_header.h │ │ │ │ ├── speex_jitter.h │ │ │ │ ├── speex_preprocess.h │ │ │ │ ├── speex_resampler.h │ │ │ │ ├── speex_stereo.h │ │ │ │ └── speex_types.h │ │ │ ├── libspeex/ │ │ │ │ ├── _kiss_fft_guts.h │ │ │ │ ├── arch.h │ │ │ │ ├── bits.c │ │ │ │ ├── buffer.c │ │ │ │ ├── cb_search.c │ │ │ │ ├── cb_search.h │ │ │ │ ├── cb_search_arm4.h │ │ │ │ ├── cb_search_bfin.h │ │ │ │ ├── cb_search_sse.h │ │ │ │ ├── echo_diagnostic.m │ │ │ │ ├── exc_10_16_table.c │ │ │ │ ├── exc_10_32_table.c │ │ │ │ ├── exc_20_32_table.c │ │ │ │ ├── exc_5_256_table.c │ │ │ │ ├── exc_5_64_table.c │ │ │ │ ├── exc_8_128_table.c │ │ │ │ ├── fftwrap.c │ │ │ │ ├── fftwrap.h │ │ │ │ ├── filterbank.c │ │ │ │ ├── filterbank.h │ │ │ │ ├── filters.c │ │ │ │ ├── filters.h │ │ │ │ ├── filters_arm4.h │ │ │ │ ├── filters_bfin.h │ │ │ │ ├── filters_sse.h │ │ │ │ ├── fixed_arm4.h │ │ │ │ ├── fixed_arm5e.h │ │ │ │ ├── fixed_bfin.h │ │ │ │ ├── fixed_debug.h │ │ │ │ ├── fixed_generic.h │ │ │ │ ├── gain_table.c │ │ │ │ ├── gain_table_lbr.c │ │ │ │ ├── hexc_10_32_table.c │ │ │ │ ├── hexc_table.c │ │ │ │ ├── high_lsp_tables.c │ │ │ │ ├── jitter.c │ │ │ │ ├── kiss_fft.c │ │ │ │ ├── kiss_fft.h │ │ │ │ ├── kiss_fftr.c │ │ │ │ ├── kiss_fftr.h │ │ │ │ ├── lpc.c │ │ │ │ ├── lpc.h │ │ │ │ ├── lpc_bfin.h │ │ │ │ ├── lsp.c │ │ │ │ ├── lsp.h │ │ │ │ ├── lsp_bfin.h │ │ │ │ ├── lsp_tables_nb.c │ │ │ │ ├── ltp.c │ │ │ │ ├── ltp.h │ │ │ │ ├── ltp_arm4.h │ │ │ │ ├── ltp_bfin.h │ │ │ │ ├── ltp_sse.h │ │ │ │ ├── math_approx.h │ │ │ │ ├── mdf.c │ │ │ │ ├── misc_bfin.h │ │ │ │ ├── modes.c │ │ │ │ ├── modes.h │ │ │ │ ├── modes_wb.c │ │ │ │ ├── nb_celp.c │ │ │ │ ├── nb_celp.h │ │ │ │ ├── os_support.h │ │ │ │ ├── preprocess.c │ │ │ │ ├── pseudofloat.h │ │ │ │ ├── quant_lsp.c │ │ │ │ ├── quant_lsp.h │ │ │ │ ├── quant_lsp_bfin.h │ │ │ │ ├── resample.c │ │ │ │ ├── resample_sse.h │ │ │ │ ├── sb_celp.c │ │ │ │ ├── sb_celp.h │ │ │ │ ├── scal.c │ │ │ │ ├── smallft.c │ │ │ │ ├── smallft.h │ │ │ │ ├── speex.c │ │ │ │ ├── speex_callbacks.c │ │ │ │ ├── speex_header.c │ │ │ │ ├── stack_alloc.h │ │ │ │ ├── stereo.c │ │ │ │ ├── testdenoise.c │ │ │ │ ├── testecho.c │ │ │ │ ├── testenc.c │ │ │ │ ├── testenc_uwb.c │ │ │ │ ├── testenc_wb.c │ │ │ │ ├── testjitter.c │ │ │ │ ├── testresample.c │ │ │ │ ├── vbr.c │ │ │ │ ├── vbr.h │ │ │ │ ├── vorbis_psy.c │ │ │ │ ├── vorbis_psy.h │ │ │ │ ├── vq.c │ │ │ │ ├── vq.h │ │ │ │ ├── vq_arm4.h │ │ │ │ ├── vq_bfin.h │ │ │ │ ├── vq_sse.h │ │ │ │ └── window.c │ │ │ ├── symbian/ │ │ │ │ └── config.h │ │ │ └── win32/ │ │ │ └── config.h │ │ ├── srtp/ │ │ │ ├── CHANGES │ │ │ ├── LICENSE │ │ │ ├── Makefile.in │ │ │ ├── README │ │ │ ├── TODO │ │ │ ├── VERSION │ │ │ ├── config.h_win32vc7 │ │ │ ├── config.hw │ │ │ ├── config_in.h │ │ │ ├── configure │ │ │ ├── configure.in │ │ │ ├── crypto/ │ │ │ │ ├── Makefile │ │ │ │ ├── Makefile.in │ │ │ │ ├── VERSION │ │ │ │ ├── ae_xfm/ │ │ │ │ │ └── xfm.c │ │ │ │ ├── cipher/ │ │ │ │ │ ├── aes.c │ │ │ │ │ ├── aes_cbc.c │ │ │ │ │ ├── aes_icm.c │ │ │ │ │ ├── cipher.c │ │ │ │ │ └── null_cipher.c │ │ │ │ ├── hash/ │ │ │ │ │ ├── auth.c │ │ │ │ │ ├── hmac.c │ │ │ │ │ ├── null_auth.c │ │ │ │ │ └── sha1.c │ │ │ │ ├── include/ │ │ │ │ │ ├── aes.h │ │ │ │ │ ├── aes_cbc.h │ │ │ │ │ ├── aes_icm.h │ │ │ │ │ ├── alloc.h │ │ │ │ │ ├── auth.h │ │ │ │ │ ├── cipher.h │ │ │ │ │ ├── crypto.h │ │ │ │ │ ├── crypto_kernel.h │ │ │ │ │ ├── crypto_math.h │ │ │ │ │ ├── crypto_types.h │ │ │ │ │ ├── cryptoalg.h │ │ │ │ │ ├── datatypes.h │ │ │ │ │ ├── err.h │ │ │ │ │ ├── gf2_8.h │ │ │ │ │ ├── hmac.h │ │ │ │ │ ├── integers.h │ │ │ │ │ ├── kernel_compat.h │ │ │ │ │ ├── key.h │ │ │ │ │ ├── null_auth.h │ │ │ │ │ ├── null_cipher.h │ │ │ │ │ ├── prng.h │ │ │ │ │ ├── rand_source.h │ │ │ │ │ ├── rdb.h │ │ │ │ │ ├── rdbx.h │ │ │ │ │ ├── sha1.h │ │ │ │ │ ├── stat.h │ │ │ │ │ └── xfm.h │ │ │ │ ├── kernel/ │ │ │ │ │ ├── alloc.c │ │ │ │ │ ├── crypto_kernel.c │ │ │ │ │ ├── err.c │ │ │ │ │ └── key.c │ │ │ │ ├── math/ │ │ │ │ │ ├── datatypes.c │ │ │ │ │ ├── gf2_8.c │ │ │ │ │ ├── math.c │ │ │ │ │ └── stat.c │ │ │ │ ├── replay/ │ │ │ │ │ ├── rdb.c │ │ │ │ │ ├── rdbx.c │ │ │ │ │ └── ut_sim.c │ │ │ │ └── rng/ │ │ │ │ ├── ctr_prng.c │ │ │ │ ├── prng.c │ │ │ │ ├── rand_linux_kernel.c │ │ │ │ └── rand_source.c │ │ │ ├── doc/ │ │ │ │ ├── Doxyfile │ │ │ │ ├── Makefile │ │ │ │ ├── Makefile.in │ │ │ │ ├── crypto_kernel.txt │ │ │ │ ├── header.template │ │ │ │ ├── intro.txt │ │ │ │ └── references.txt │ │ │ ├── include/ │ │ │ │ ├── getopt_s.h │ │ │ │ ├── rtp.h │ │ │ │ ├── rtp_priv.h │ │ │ │ ├── srtp.h │ │ │ │ ├── srtp_priv.h │ │ │ │ └── ut_sim.h │ │ │ ├── install-sh │ │ │ ├── pjlib/ │ │ │ │ └── srtp_err.c │ │ │ ├── srtp/ │ │ │ │ └── srtp.c │ │ │ ├── srtp.def │ │ │ ├── srtp7.sln │ │ │ ├── tables/ │ │ │ │ └── aes_tables.c │ │ │ ├── timing │ │ │ ├── undos.sh │ │ │ └── update.sh │ │ ├── webrtc/ │ │ │ └── src/ │ │ │ ├── common_audio/ │ │ │ │ └── signal_processing_library/ │ │ │ │ ├── OWNERS │ │ │ │ └── main/ │ │ │ │ ├── interface/ │ │ │ │ │ ├── signal_processing_library.h │ │ │ │ │ ├── spl_inl.h │ │ │ │ │ └── spl_inl_armv7.h │ │ │ │ └── source/ │ │ │ │ ├── auto_corr_to_refl_coef.c │ │ │ │ ├── auto_correlation.c │ │ │ │ ├── complex_bit_reverse.c │ │ │ │ ├── complex_fft.c │ │ │ │ ├── complex_ifft.c │ │ │ │ ├── copy_set_operations.c │ │ │ │ ├── cos_table.c │ │ │ │ ├── cross_correlation.c │ │ │ │ ├── division_operations.c │ │ │ │ ├── dot_product_with_scale.c │ │ │ │ ├── downsample_fast.c │ │ │ │ ├── energy.c │ │ │ │ ├── filter_ar.c │ │ │ │ ├── filter_ar_fast_q12.c │ │ │ │ ├── filter_ma_fast_q12.c │ │ │ │ ├── get_hanning_window.c │ │ │ │ ├── get_scaling_square.c │ │ │ │ ├── hanning_table.c │ │ │ │ ├── ilbc_specific_functions.c │ │ │ │ ├── levinson_durbin.c │ │ │ │ ├── lpc_to_refl_coef.c │ │ │ │ ├── min_max_operations.c │ │ │ │ ├── min_max_operations_neon.c │ │ │ │ ├── randn_table.c │ │ │ │ ├── randomization_functions.c │ │ │ │ ├── refl_coef_to_lpc.c │ │ │ │ ├── resample.c │ │ │ │ ├── resample_48khz.c │ │ │ │ ├── resample_by_2.c │ │ │ │ ├── resample_by_2_internal.c │ │ │ │ ├── resample_by_2_internal.h │ │ │ │ ├── resample_fractional.c │ │ │ │ ├── sin_table.c │ │ │ │ ├── sin_table_1024.c │ │ │ │ ├── spl_sqrt.c │ │ │ │ ├── spl_sqrt_floor.c │ │ │ │ ├── spl_version.c │ │ │ │ ├── splitting_filter.c │ │ │ │ ├── sqrt_of_one_minus_x_squared.c │ │ │ │ ├── vector_scaling_operations.c │ │ │ │ ├── webrtc_fft_t_1024_8.c │ │ │ │ └── webrtc_fft_t_rad.c │ │ │ ├── common_types.h │ │ │ ├── engine_configurations.h │ │ │ ├── modules/ │ │ │ │ └── audio_processing/ │ │ │ │ ├── aec/ │ │ │ │ │ └── main/ │ │ │ │ │ ├── interface/ │ │ │ │ │ │ └── echo_cancellation.h │ │ │ │ │ ├── matlab/ │ │ │ │ │ │ └── fullaec.m │ │ │ │ │ └── source/ │ │ │ │ │ ├── aec_core.c │ │ │ │ │ ├── aec_core.h │ │ │ │ │ ├── aec_core_sse2.c │ │ │ │ │ ├── aec_rdft.c │ │ │ │ │ ├── aec_rdft.h │ │ │ │ │ ├── aec_rdft_sse2.c │ │ │ │ │ ├── echo_cancellation.c │ │ │ │ │ ├── resampler.c │ │ │ │ │ └── resampler.h │ │ │ │ ├── agc/ │ │ │ │ │ └── main/ │ │ │ │ │ ├── interface/ │ │ │ │ │ │ └── gain_control.h │ │ │ │ │ └── source/ │ │ │ │ │ ├── analog_agc.c │ │ │ │ │ ├── analog_agc.h │ │ │ │ │ ├── digital_agc.c │ │ │ │ │ └── digital_agc.h │ │ │ │ ├── ns/ │ │ │ │ │ └── main/ │ │ │ │ │ ├── interface/ │ │ │ │ │ │ ├── noise_suppression.h │ │ │ │ │ │ └── noise_suppression_x.h │ │ │ │ │ └── source/ │ │ │ │ │ ├── defines.h │ │ │ │ │ ├── noise_suppression.c │ │ │ │ │ ├── noise_suppression_x.c │ │ │ │ │ ├── ns_core.c │ │ │ │ │ ├── ns_core.h │ │ │ │ │ ├── nsx_core.c │ │ │ │ │ ├── nsx_core.h │ │ │ │ │ ├── nsx_core_neon.c │ │ │ │ │ ├── nsx_defines.h │ │ │ │ │ └── windows_private.h │ │ │ │ └── utility/ │ │ │ │ ├── fft4g.c │ │ │ │ ├── fft4g.h │ │ │ │ ├── ring_buffer.c │ │ │ │ └── ring_buffer.h │ │ │ ├── system_wrappers/ │ │ │ │ ├── OWNERS │ │ │ │ ├── interface/ │ │ │ │ │ ├── aligned_malloc.h │ │ │ │ │ ├── atomic32_wrapper.h │ │ │ │ │ ├── condition_variable_wrapper.h │ │ │ │ │ ├── constructor_magic.h │ │ │ │ │ ├── cpu_features_wrapper.h │ │ │ │ │ ├── cpu_wrapper.h │ │ │ │ │ ├── critical_section_wrapper.h │ │ │ │ │ ├── data_log.h │ │ │ │ │ ├── data_log_impl.h │ │ │ │ │ ├── event_wrapper.h │ │ │ │ │ ├── file_wrapper.h │ │ │ │ │ ├── fix_interlocked_exchange_pointer_windows.h │ │ │ │ │ ├── list_wrapper.h │ │ │ │ │ ├── map_wrapper.h │ │ │ │ │ ├── ref_count.h │ │ │ │ │ ├── rpcsal.h │ │ │ │ │ ├── rw_lock_wrapper.h │ │ │ │ │ ├── scoped_ptr.h │ │ │ │ │ ├── sort.h │ │ │ │ │ ├── thread_wrapper.h │ │ │ │ │ ├── tick_util.h │ │ │ │ │ └── trace.h │ │ │ │ └── source/ │ │ │ │ ├── aligned_malloc.cc │ │ │ │ ├── atomic32.cc │ │ │ │ ├── atomic32_linux.h │ │ │ │ ├── atomic32_mac.h │ │ │ │ ├── atomic32_windows.h │ │ │ │ ├── condition_variable.cc │ │ │ │ ├── condition_variable_posix.cc │ │ │ │ ├── condition_variable_posix.h │ │ │ │ ├── condition_variable_windows.cc │ │ │ │ ├── condition_variable_windows.h │ │ │ │ ├── cpu.cc │ │ │ │ ├── cpu_features.cc │ │ │ │ ├── cpu_linux.cc │ │ │ │ ├── cpu_linux.h │ │ │ │ ├── cpu_mac.cc │ │ │ │ ├── cpu_mac.h │ │ │ │ ├── cpu_windows.cc │ │ │ │ ├── cpu_windows.h │ │ │ │ ├── critical_section.cc │ │ │ │ ├── critical_section_posix.cc │ │ │ │ ├── critical_section_posix.h │ │ │ │ ├── critical_section_windows.cc │ │ │ │ ├── critical_section_windows.h │ │ │ │ ├── data_log.cc │ │ │ │ ├── data_log_dummy.cc │ │ │ │ ├── data_log_helpers_unittest.cc │ │ │ │ ├── data_log_unittest.cc │ │ │ │ ├── event.cc │ │ │ │ ├── event_posix.cc │ │ │ │ ├── event_posix.h │ │ │ │ ├── event_windows.cc │ │ │ │ ├── event_windows.h │ │ │ │ ├── file_impl.cc │ │ │ │ ├── file_impl.h │ │ │ │ ├── list_no_stl.cc │ │ │ │ ├── list_no_stl.h │ │ │ │ ├── list_stl.cc │ │ │ │ ├── list_stl.h │ │ │ │ ├── list_unittest.cc │ │ │ │ ├── map.cc │ │ │ │ ├── map_no_stl.cc │ │ │ │ ├── map_no_stl.h │ │ │ │ ├── map_unittest.cc │ │ │ │ ├── rw_lock.cc │ │ │ │ ├── rw_lock_generic.cc │ │ │ │ ├── rw_lock_generic.h │ │ │ │ ├── rw_lock_posix.cc │ │ │ │ ├── rw_lock_posix.h │ │ │ │ ├── rw_lock_windows.cc │ │ │ │ ├── rw_lock_windows.h │ │ │ │ ├── sort.cc │ │ │ │ ├── spreadsortlib/ │ │ │ │ │ ├── constants.hpp │ │ │ │ │ └── spreadsort.hpp │ │ │ │ ├── thread.cc │ │ │ │ ├── thread_posix.cc │ │ │ │ ├── thread_posix.h │ │ │ │ ├── thread_windows.cc │ │ │ │ ├── thread_windows.h │ │ │ │ ├── thread_windows_set_name.h │ │ │ │ ├── trace_impl.cc │ │ │ │ ├── trace_impl.h │ │ │ │ ├── trace_posix.cc │ │ │ │ ├── trace_posix.h │ │ │ │ ├── trace_windows.cc │ │ │ │ └── trace_windows.h │ │ │ └── typedefs.h │ │ └── zsrtp/ │ │ ├── include/ │ │ │ ├── ZsrtpCWrapper.h │ │ │ └── openssl_compat.h │ │ ├── srtp/ │ │ │ └── ZsrtpCWrapper.cpp │ │ └── zrtp/ │ │ ├── COPYING │ │ ├── README.md │ │ ├── base_version │ │ ├── common/ │ │ │ ├── EventClass.cpp │ │ │ ├── EventClass.h │ │ │ ├── MutexClass.cpp │ │ │ ├── MutexClass.h │ │ │ ├── Thread.cpp │ │ │ ├── Thread.h │ │ │ ├── osSpecifics.c │ │ │ └── osSpecifics.h │ │ ├── cryptcommon/ │ │ │ ├── ZrtpRandom.cpp │ │ │ ├── ZrtpRandom.h │ │ │ ├── aes.h │ │ │ ├── aes_modes.c │ │ │ ├── aescpp.h │ │ │ ├── aescrypt.c │ │ │ ├── aeskey.c │ │ │ ├── aesopt.h │ │ │ ├── aestab.c │ │ │ ├── aestab.h │ │ │ ├── brg_endian.h │ │ │ ├── brg_types.h │ │ │ ├── macSkein.cpp │ │ │ ├── macSkein.h │ │ │ ├── skein.c │ │ │ ├── skein.h │ │ │ ├── skeinApi.c │ │ │ ├── skeinApi.h │ │ │ ├── skein_block.c │ │ │ ├── skein_iv.h │ │ │ ├── skein_port.h │ │ │ ├── twofish.c │ │ │ ├── twofish.h │ │ │ └── twofish_cfb.c │ │ ├── srtp/ │ │ │ ├── CryptoContext.cpp │ │ │ ├── CryptoContext.h │ │ │ ├── CryptoContextCtrl.cpp │ │ │ ├── CryptoContextCtrl.h │ │ │ ├── SrtpHandler.cpp │ │ │ ├── SrtpHandler.h │ │ │ └── crypto/ │ │ │ ├── SrtpSymCrypto.cpp │ │ │ ├── SrtpSymCrypto.h │ │ │ ├── gcrypt/ │ │ │ │ ├── InitializeGcrypt.cpp │ │ │ │ ├── gcryptSrtpSymCrypto.cpp │ │ │ │ └── gcrypthmac.cpp │ │ │ ├── hmac.cpp │ │ │ ├── hmac.h │ │ │ ├── openssl/ │ │ │ │ ├── SrtpSymCrypto.cpp │ │ │ │ └── hmac.cpp │ │ │ ├── sha1.c │ │ │ └── sha1.h │ │ └── zrtp/ │ │ ├── Base32.cpp │ │ ├── ZIDCacheDb.cpp │ │ ├── ZIDCacheFile.cpp │ │ ├── ZIDRecordDb.cpp │ │ ├── ZIDRecordFile.cpp │ │ ├── ZRtp.cpp │ │ ├── ZrtpCWrapper.cpp │ │ ├── ZrtpCallbackWrapper.cpp │ │ ├── ZrtpConfigure.cpp │ │ ├── ZrtpCrc32.cpp │ │ ├── ZrtpPacketClearAck.cpp │ │ ├── ZrtpPacketCommit.cpp │ │ ├── ZrtpPacketConf2Ack.cpp │ │ ├── ZrtpPacketConfirm.cpp │ │ ├── ZrtpPacketDHPart.cpp │ │ ├── ZrtpPacketError.cpp │ │ ├── ZrtpPacketErrorAck.cpp │ │ ├── ZrtpPacketGoClear.cpp │ │ ├── ZrtpPacketHello.cpp │ │ ├── ZrtpPacketHelloAck.cpp │ │ ├── ZrtpPacketPing.cpp │ │ ├── ZrtpPacketPingAck.cpp │ │ ├── ZrtpPacketRelayAck.cpp │ │ ├── ZrtpPacketSASrelay.cpp │ │ ├── ZrtpSdesStream.cpp │ │ ├── ZrtpStateClass.cpp │ │ ├── ZrtpTextData.cpp │ │ ├── crypto/ │ │ │ ├── aesCFB.cpp │ │ │ ├── aesCFB.h │ │ │ ├── gcrypt/ │ │ │ │ ├── InitializeGcrypt.cpp │ │ │ │ ├── gcryptAesCFB.cpp │ │ │ │ ├── gcryptZrtpDH.cpp │ │ │ │ ├── gcrypthmac256.cpp │ │ │ │ ├── gcrypthmac384.cpp │ │ │ │ ├── gcryptsha256.cpp │ │ │ │ └── gcryptsha384.cpp │ │ │ ├── hmac256.cpp │ │ │ ├── hmac256.h │ │ │ ├── hmac384.cpp │ │ │ ├── hmac384.h │ │ │ ├── openssl/ │ │ │ │ ├── InitializeOpenSSL.cpp │ │ │ │ ├── aesCFB.cpp │ │ │ │ ├── hmac256.cpp │ │ │ │ ├── hmac384.cpp │ │ │ │ ├── sha256.cpp │ │ │ │ ├── sha384.cpp │ │ │ │ └── zrtpDH.cpp │ │ │ ├── sha2.c │ │ │ ├── sha2.h │ │ │ ├── sha256.cpp │ │ │ ├── sha256.h │ │ │ ├── sha384.cpp │ │ │ ├── sha384.h │ │ │ ├── skein256.cpp │ │ │ ├── skein256.h │ │ │ ├── skein384.cpp │ │ │ ├── skein384.h │ │ │ ├── skeinMac256.cpp │ │ │ ├── skeinMac256.h │ │ │ ├── skeinMac384.cpp │ │ │ ├── skeinMac384.h │ │ │ ├── twoCFB.cpp │ │ │ ├── twoCFB.h │ │ │ ├── zrtpDH.cpp │ │ │ └── zrtpDH.h │ │ ├── libzrtpcpp/ │ │ │ ├── Base32.h │ │ │ ├── ZIDCache.h │ │ │ ├── ZIDCacheDb.h │ │ │ ├── ZIDCacheFile.h │ │ │ ├── ZIDRecord.h │ │ │ ├── ZIDRecordDb.h │ │ │ ├── ZIDRecordFile.h │ │ │ ├── ZRtp.h │ │ │ ├── ZrtpCWrapper.h │ │ │ ├── ZrtpCallback.h │ │ │ ├── ZrtpCallbackWrapper.h │ │ │ ├── ZrtpCodes.h │ │ │ ├── ZrtpConfigure.h │ │ │ ├── ZrtpCrc32.h │ │ │ ├── ZrtpPacketBase.h │ │ │ ├── ZrtpPacketClearAck.h │ │ │ ├── ZrtpPacketCommit.h │ │ │ ├── ZrtpPacketConf2Ack.h │ │ │ ├── ZrtpPacketConfirm.h │ │ │ ├── ZrtpPacketDHPart.h │ │ │ ├── ZrtpPacketError.h │ │ │ ├── ZrtpPacketErrorAck.h │ │ │ ├── ZrtpPacketGoClear.h │ │ │ ├── ZrtpPacketHello.h │ │ │ ├── ZrtpPacketHelloAck.h │ │ │ ├── ZrtpPacketPing.h │ │ │ ├── ZrtpPacketPingAck.h │ │ │ ├── ZrtpPacketRelayAck.h │ │ │ ├── ZrtpPacketSASrelay.h │ │ │ ├── ZrtpSdesStream.h │ │ │ ├── ZrtpStateClass.h │ │ │ ├── ZrtpStates.h │ │ │ ├── ZrtpTextData.h │ │ │ ├── ZrtpUserCallback.h │ │ │ ├── zrtpB64Decode.h │ │ │ ├── zrtpB64Encode.h │ │ │ ├── zrtpCacheDbBackend.h │ │ │ └── zrtpPacket.h │ │ ├── zrtpB64Decode.c │ │ ├── zrtpB64Encode.c │ │ └── zrtpCacheSqliteBackend.c │ └── version.mak ├── docs/ │ ├── Dependencies.txt │ ├── DeveloperGuide.txt │ ├── Install.debian │ ├── Install.linux │ ├── Install.osx │ ├── Install.rasbian │ ├── Install.ubuntu │ ├── Install.windows │ ├── Licenses.txt │ └── Uninstall.txt ├── setup.py ├── setup_pjsip.py └── sipsimple/ ├── __info__.py ├── __init__.py ├── account/ │ ├── __init__.py │ ├── bonjour/ │ │ ├── __init__.py │ │ └── _bonjour.py │ ├── publication.py │ ├── registration.py │ ├── subscription.py │ └── xcap/ │ ├── __init__.py │ └── storage/ │ ├── __init__.py │ ├── file.py │ └── memory.py ├── addressbook.py ├── application.py ├── audio.py ├── configuration/ │ ├── __init__.py │ ├── backend/ │ │ ├── __init__.py │ │ ├── file.py │ │ └── memory.py │ ├── datatypes.py │ └── settings.py ├── core/ │ ├── __init__.py │ ├── _core.error.pxi │ ├── _core.event.pxi │ ├── _core.headers.pxi │ ├── _core.helper.pxi │ ├── _core.invitation.pxi │ ├── _core.lib.pxi │ ├── _core.mediatransport.pxi │ ├── _core.pxd │ ├── _core.pyx │ ├── _core.referral.pxi │ ├── _core.request.pxi │ ├── _core.sdp.pxi │ ├── _core.sound.pxi │ ├── _core.subscription.pxi │ ├── _core.ua.pxi │ ├── _core.util.pxi │ ├── _core.video.pxi │ ├── _engine.py │ ├── _helpers.py │ └── _primitives.py ├── logging.py ├── lookup.py ├── payloads/ │ ├── README.txt │ ├── __init__.py │ ├── addressbook.py │ ├── caps.py │ ├── cipid.py │ ├── commonpolicy.py │ ├── conference.py │ ├── datatypes.py │ ├── dialoginfo.py │ ├── dialogrules.py │ ├── directory.py │ ├── imdn.py │ ├── iscomposing.py │ ├── messagesummary.py │ ├── omapolicy.py │ ├── pidf.py │ ├── prescontent.py │ ├── presrules.py │ ├── resourcelists.py │ ├── rlmi.py │ ├── rlsnotify.py │ ├── rlsservices.py │ ├── rpid.py │ ├── watcherinfo.py │ ├── xcapcaps.py │ ├── xcapdiff.py │ └── xml-schemas/ │ ├── addressbook.xsd │ ├── caps.xsd │ ├── cipid.xsd │ ├── common-policy.xsd │ ├── common-schema.xsd │ ├── conference.xsd │ ├── data-model.xsd │ ├── dialog-info.xsd │ ├── dialog-rules.xsd │ ├── im-iscomposing.xsd │ ├── imdn.xsd │ ├── oma-common-policy.xsd │ ├── oma-pres-content.xsd │ ├── patchops.xsd │ ├── pidf.xsd │ ├── pres-rules.xsd │ ├── resourcelists.xsd │ ├── rlmi.xsd │ ├── rlsservices.xsd │ ├── rpid.xsd │ ├── watcherinfo.xsd │ ├── xcap-caps.xsd │ ├── xcap-directory.xsd │ ├── xcapdiff.xsd │ └── xml.xsd ├── session.py ├── storage.py ├── streams/ │ ├── __init__.py │ ├── msrp/ │ │ ├── __init__.py │ │ ├── chat.py │ │ ├── filetransfer.py │ │ └── screensharing.py │ └── rtp/ │ ├── __init__.py │ ├── audio.py │ └── video.py ├── threading/ │ ├── __init__.py │ └── green.py ├── util/ │ ├── __init__.py │ ├── _sha1.h │ └── _sha1.pyx └── video.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .boring ================================================ # # Boring file regular expresions # ~$ \# (^|/)\.DS_Store$ (^|/)Thumbs\.db$ (^|/)core(\.[0-9]+)?$ \.(pyc|pyo|o|so|orig|rej|prof|bak|BAK|tmp|wpr|wpu|swp|swo|komodoproject)$ (^|/)\.idea($|/) (^|/)\.komodotools($|/) (^|/)_darcs($|/) (^|/)autom4te.cache($|/) (^|/)tags($|/) ^MANIFEST$ ^build($|/) ^dist($|/) ^test($|/) ^sipsimple/core/_core.c$ ^sipsimple/util/_sha1.c$ ================================================ FILE: AUTHORS ================================================ Adrian Georgescu Dan Pascu Ruud Klaver Denis Bilenko Lucian Stanescu Saúl Ibarra ================================================ FILE: LICENSE ================================================ Copyright: 2008-2020 AG Projects License: GPL-3+ This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. For a copy of the license see https://www.gnu.org/licenses/gpl.html ================================================ FILE: MANIFEST.in ================================================ include AUTHORS include LICENSE include README include MANIFEST.in include build_inplace include setup_pjsip.py include sipsimple/payloads/xml-schemas/*.xsd include sipsimple/util/_sha1.h include debian/changelog include debian/compat include debian/control include debian/copyright include debian/dirs include debian/docs include debian/pycompat include debian/pyversions include debian/python-sipsimple.lintian-overrides include debian/python-sipsimple-dbg.lintian-overrides include debian/rules include debian/source/format recursive-include docs * recursive-include deps * ================================================ FILE: README ================================================ SIP SIMPLE Client SDK --------------------- Copyright (c) 2008-2020 AG Projects http://ag-projects.com Description ----------- SIP SIMPLE client SDK is a Software Development Kit for easy development of SIP end-points that support rich media like Audio, Video, Instant Messaging, File Transfers, Desktop Sharing and Presence. Other media types can be easily added by using an extensible high-level API. The software has undergone in the past years several interoperability tests at SIPIT (http://www.sipit.net) and today is of industry strength quality. Background ---------- SIP stands for 'Sessions Initiation Protocol', an IETF standard described by RFC 3261. SIP is an application-layer control protocol that can establish, modify and terminate multimedia sessions such as Internet telephony calls (VoIP). Media can be added to (and removed from) an existing session. SIP allows the endpoints to negotiate and combine any type of session they mutually understand like video, instant messaging (IM), file transfer, desktop sharing and provides a generic event notification system with real-time publications and subscriptions about state changes that can be used for asynchronous services like presence, message waiting indicator and busy line appearance. Features -------- The library has cross platform capabilities on Linux OS, Mac OSX and Microsoft Windows. The library should work with minimal changes on any platform that supports C and Python development environments. The SDK is suitable for building end-points like SIP clients or SIP Application Servers. To see what the SDK is capable of, you can try Blink from http://icanblink.com General * Written in Python * Non-blocking asynchronous engine * Built-in configuration framework * TLS Security for signaling (SIP) and media (MSRP, XCAP) * Support for multiple SIP accounts * Multiple Media Types per Session (e.g. Video, Audio and IM) * Failover support for DNS lookups, SIP and MSRP routing * Implements re-INVITEs for adding and removing media streams * Automatically handling if IP Address changes * Audio conference bridge * Wav player and recorder * Acoustic Echo Cancelation * Answering machine * Wide-band Internet audio codecs: Opus and Speex * PSTN compatible codecs: G722, G711, iLBC, GSM * Video codecs: H.264, VP8 Supported media * Audio and Video (RTP/SRTP/ZRTP) * Instant Messaging (MSRP and its relay extension) * File Transfer (MSRP and its relay extension) * Screen Sharing (VNC over MSRP) All media types can be combined together in the same SIP session. Normative References -------------------- SIP Signaling * SIP, Session Initiation Protocol (RFC 3261) * SDP, Session Description Protocol (RFC 4566) * An Offer/Answer Model with Session Description Protocol (SDP) (RFC 3264) * Reliability of Provisional Responses in Session Initiation Protocol (RFC 3262) * HTTP Authentication: Basic and Digest Access Authentication (RFC 2617) * The Reason Header Field for the Session Initiation Protocol (RFC 3326) * The Session Initiation Protocol (SIP) Refer Method (RFC 3515) * The Session Initiation Protocol (SIP) "Replaces" Header (RFC 3891) * Session Initiation Protocol (SIP) Call Control - Transfer (RFC 5589) Address Resolution * DNS resolution (RFC 3263) * Bonjour multicast DNS (draft-lee-sip-dns-sd-uri-03) NAT Traversal * SIP Signaling: Symmetric Response Routing Symmetric media (RFC 3581) * RTP media (Audio and Video): ICE, Interactive Connectivity Establishment (RFC 5245) * MSRP media (Instant Messaging and File Transfer): MSRP protocol relay extension (RFC 4976) Audio and Video * RTP, A Transport Protocol for Real-Time Applications (RFC 3550) * Real Time Control Protocol (RTCP) attribute in Session Description Protocol (SDP) (RFC 3605) * SRTP, The Secure Real-time Transport Protocol (RFC 3711) * Generation and parsing of telephone-events payload in both RTP and SDP (RFC 2833) * ZRTP: Media Path Key Agreement for Unicast Secure RTP (RFC 6189) Instant Messaging * CPIM, Common Presence and Instant Messaging: (RFC 3862) * Session Initiation Protocol (SIP) Extension for Instant Messaging (RFC 3428) * MSRP Protocol (RFC 4975) * Indication of Message Composition for Instant Messaging (RFC 3994) * Message Summary Event Package (RFC 3842) * File Transfer (RFC 5547) Screen Sharing * Variation of draft-garcia-mmusic-sdp-collaboration-00 using RFB over MSRP Conferencing * Conference Event Package (RFC 4575) * A Framework for Conferencing with the Session Initiation Protocol (RFC 4353) * SIP Call Control - Conferencing for User Agents (RFC 4579) * MSRP ad-hoc multi-party chat sessions (RFC 7701) Presence * SIP Specific Event Notification (RFC 3265) * SIP Extension for Event State Publication (RFC 3903) * PIDF: Presence Data Model (RFC 3863, RFC 3379, RFC 4479) * Watcher-info Event Package (RFC 3857, RFC 3858) * Rich Presence Extensions to PIDF (RFC 4480) * Contact Information Extension to PIDF (RFC 4482) * User Agent Capability Extension to PIDF (RFC 5196) * XCAP Protocol (RFC 4825) * Common Policy (RFC 4745) * Presence Rules (RFC 5025) * Resource Lists (RFC 4826) * RLS Services (RFC 4826) * PIDF manipulation (RFC 4827) * XCAP Diff (RFC 5874) * OMA Reference Release Definition for XDM v1.1 and Presence SIMPLE v1.1 Implementation Guidelines * OMA XML Document Management V1.1 Support ------- The project is developed and supported by AG Projects. The support is provided on a best-effort basis. "best-effort" means that we try to solve the bugs you report or help fix your problems as soon as we can, subject to available resources. To request support you must use the mailing list available at http://lists.ag-projects.com/mailman/listinfo/sipbeyondvoip Patches and bug reports must be submitted by opening a ticket at http://sipsimpleclient.org/projects/sipsimpleclient/issues To open ticket please Register first. Acknowledgments --------------- SIP SIMPLE client SDK has been partly funded by the European Commission under grant number FP7-IST-216217. ================================================ FILE: TODO ================================================ - If the SIP account password is changed on the server, then the client modifies some contact, xcap PUT will keep failing with 401 even after the password is reverted on the server. This will only be fixed by deactivating/reactivating the account or restarting the application. - Warning header takes a code (300-399) a hostname (not user agent) and a message. Class allows any code (100-999) and we use engine.user_agent for the hostname where we use this header in session.py - 202 is deprecated (see https://tools.ietf.org/html/rfc6665#page-43) - After ICE negotiation completed we do not save state and later re-INVITEs can fail, we must send re-INVITE or UPDATE with chosen candidate + Allow resume of failed file transfers (a=file-range SDP attribute) + combine account.uri and account.contact into account.uri.aor and account.uri.contact. This will also remove the confusion between account.contact and account.contact_manager + change settings so they store changes in a thread local container which is made public (accessible for all threads) when save is called. This will make changes be atomic inside a thread (everything from attribute changes up to the save operation is contained in the thread and will not mix with changes done in parallel from other threads). + there is a race condition that can prevent an account to start if it is created while the start method of SIPApplication runs and it passed the point where it called AccountManager.start. A quick workaround is to never create an account before SIPApplicationDidStart is posted + SIPApplication.start attempts to be restartable in case of errors, but there are cases where it may enter into a state where it will raise exceptions perpetually, even if the condition that led to the exception was fixed. - allow deleting transport on SIPURI - modify Invitation/Request/Subscription to reset transport on request_uri - in application.py it subscribes to notifications but doesn't remove them when it stops - End a session while in accepting state (requires session serialization) - Model file transfer after desktop sharing (use handlers on termination) have internal/external handlers (notifications based or file bridges) - Use an ordered dict for keeping accounts in the AccountManager ?!? see http://code.activestate.com/recipes/576693/ - send notifications when local/remote party becomes focus SIPSessionLocalPartyIsFocus, SIPSessionRemotePartyIsFocus - have higher level notifications on conference events To fix this exception: sip:nwpsefvl@10.0.0.1:52067 52067 sip:nwpsefvl@10.0.0.1:52067 52067 sip:nwpsefvl@10.0.0.1;transport=tcp None Traceback (most recent call last): File "/usr/lib/python2.5/site-packages/twisted/internet/base.py", line 778, in runUntilCurrent call.func(*call.args, **call.kw) File "/usr/lib/pymodules/python2.5/eventlet/hubs/twistedr.py", line 158, in call_if_greenlet_alive return func(*args1, **kwargs1) File "/usr/lib/pymodules/python2.5/eventlet/proc.py", line 571, in _run result = function(*args, **kwargs) File "/home/dan/work/voip/python-sipsimple/sipsimple/account.py", line 683, in _handle_commands handler(command) File "/home/dan/work/voip/python-sipsimple/sipsimple/account.py", line 720, in _CH_register txtRecord=bonjour.TXTRecord(items=txtdata)) File "/home/dan/work/voip/blink-qt/sipsimple/bonjour.py", line 1125, in DNSServiceRegister TypeError: an integer is required - Notification from SIPApplication when a transport is added/removed to the engine. - SIPApplication should provide a list of supported transports that need to be used instead of the ones from settings because not everything enabled in settings may actually be available at runtime on the engine - Build contacts on above notification instead of building them on the fly - Use OpenH264 implementation - We need the binaries?! - Ability to start stream deactivated / on-hold - VideoStream(direction='sendrecv') like MSRP? - Review stream API, s/deactivate/shutdown/ ? - Open the camera at its native framerate ================================================ FILE: build_inplace ================================================ #!/bin/sh python setup.py build_ext --inplace "$@" test -d build && python setup.py clean ================================================ FILE: debian/changelog ================================================ python-sipsimple (3.6.0) unstable; urgency=medium * Added support for creating and parsing IMDN (RFC5438) documents * Fixed CPIM decode if envelope has content type message/imdn+xml -- Tijmen de Mes Wed, 16 Jun 2021 15:04:04 +0200 python-sipsimple (3.5.1) unstable; urgency=medium * Do not run account delete inside green thread -- Adrian Georgescu Sun, 10 Jan 2021 20:26:12 +0100 python-sipsimple (3.5.0) unstable; urgency=medium * Fixed issue with clients locking when using PulseAudio as input device * Fixed correctness and consistency issue with getting the display name * Use identity tests instead of equality tests for None * Fixed timer callback function signatures * Added support for digest passwords in credentials objects * Simplified setting attribute in init * Use an internal attribute for digest for symmetry with FrozenCredentials * Fix build with LibreSSL 2.7 * Adapted the MSRP logger to the API changes in msrplib * Replaced parse_uri with the new URI.parse in msrplib * Added note about illegal character used by filetransfer MSRP extension * Use r-strings for regular expressions * Removed redundant parentheses * Added OffsetHeader type to encode/decode MSRP file offset headers * Changed how headers are added to MSRPData to be consistent * Use named attributes instead of tuple indexes when accessing byte_range * Preserve quoted arguments with build_inplace * Cleanup after build_inplace * Updated shebang line in setup.py * Updated debian build flags * Removed DH_VERBOSE definition in debian rules * Explicitly use python2 in shebang lines * Updated license and copyright years * Updated minimum version of debian dependencies * Increased debian compatibility level to 11 * Increased debian standards version to 4.5.0 -- Dan Pascu Fri, 14 Feb 2020 12:46:51 +0200 python-sipsimple (3.4.2) unstable; urgency=medium * Fixed crash when using sha1 hmac with zrtp * Added __dir__ method to Engine to correctly reflect UA attributes * Updated windows installation instructions -- Dan Pascu Tue, 26 Mar 2019 11:30:05 +0200 python-sipsimple (3.4.1) unstable; urgency=medium * Fixed cleanup of media stream when ending to avoid memory leak * Fixed inconsistency in session state at the time of posting notification * Fixed memory leak caused by out of order execution from race condition -- Dan Pascu Wed, 27 Feb 2019 10:14:48 +0200 python-sipsimple (3.4.0) unstable; urgency=medium * Fixed attribute name in comparison * Added dialog_id property on the Session class * Added logic to handle early-only and RFC2543 style tags for Replaces * Avoid unnecessary list copy * Removed unnecessary boolean variable * Do not reset the proposed_streams attribute when the session is rejected * Reordered some operations for consistency * Make sure the greenlet attribute is always removed when greenlet exits * Removed code that duplicated cancel handling inside reject handler * Simplified RTPStreamEncryption properties * Ordered RTPStreamEncryption properties alphabetically * Fixed potential access to uninitialized variable * Removed unnecessary attribute from RTPStream * Fixed memory leak for unused streams that bleeded out audio resources * Fixed memory leak that did not release streams when canceling proposals * Use setter syntax for defining properties * PEP-8 compliance changes * Use set literals instead of set instances * Do not use mutable value for function default argument * Improved error message * Use local variable instead of instance attribute * Fixed right hand value in assignment * Use super when instantiating data types * Always clean up proposed streams when cancelling proposal * Added language_level for cython sources -- Dan Pascu Mon, 25 Feb 2019 13:51:22 +0200 python-sipsimple (3.3.0) unstable; urgency=medium * Updated to support openssl-1.1 in pjsip and zrtp * Updated to support openh264-1.6+ in pjmedia * Removed duplicate declaration of pjmedia_dir enum in _core.pxd -- Dan Pascu Wed, 12 Dec 2018 07:02:37 +0200 python-sipsimple (3.2.1) unstable; urgency=medium * Fixed building sipsimple when python-application is not installed -- Dan Pascu Fri, 05 Oct 2018 18:57:56 +0300 python-sipsimple (3.2.0) unstable; urgency=medium * Shutdown already started subsystems if application is stopped early * Fixed a bug that would hang the application if the engine fails to start * Stop the engine thread on engine failure * Reset encryption type when the RTP stream ends * Use a named logger for sipsimple logging * Use modern syntax for catching exception * Removed underscore prefix from argument name * Fixed typo in notification name * Order imports alphabetically * Import statement cleanup * Removed obsolete build option * [core] Removed unnecessary local variables * [core] Removed test for failures that cannot happen * [core] Fixed the transport being used within an incoming SUBSCRIBE dialog * [core] Removed unnecessary semicolon at the end of statement * [core] Added missing cdef statement * [core] Minimize releasing/acquiring the GIL * [core] Propagate exceptions * [core] Undo setting incoming subscription transport as it conflicts * [core] Removed unused code from setting transport * Use the new logging API in python-application 2.5.0 * Fixed compiling with newer ffmpeg * Removed duplicate dependency * Update macOS installation instructions * Added deb building instructions for Rasberry Pi * Removed copyright notices from install files * Updated copyright years * Replaced deprecated debian package priority * Switched from dbg package to dbgsym package -- Dan Pascu Fri, 05 Oct 2018 17:01:31 +0300 python-sipsimple (3.1.1) unstable; urgency=medium * Fix processing audio / video SDPs with non numeric formats * Document how to compile ffmpeg without SecureTransport * Removed obsolete pycompat/pyversions files * Updated debian uploaders * Updated debian standards version * Simplified condition * PEP-8 compliance changes * Improved object representation * Simplified building the Route's URI * Made output from Route.__str__ be consitent with SIPURI.__str__ * Fixed debian build dependency for libssl * Updated copyright years * Increased debian compatibility level to 9 * Removed unnecessary phony targets * Updated debian copyright * Updated debian package description * Fixed python-sipsimple-dbg dependencies * Simplified debian build and fixed missing module in the debug package -- Dan Pascu Fri, 05 May 2017 17:29:13 +0300 python-sipsimple (3.1.0) unstable; urgency=medium * Update Opus to version 1.1.2 * pjsip: fix handling empty re-INVITE requests * Updated dependency versions * Improved MANIFEST.in * Removed build time dependency on python-application * Updated building documentation * Added interface for scheduling functions to run in a thread * Added fake xcap settings to the BonjourAccount * Fix race conditions when processing ICE callbacks * Ignore NAPTR and SRV timeouts when doing DNS lookups * Fixed setting Referred-By header -- Saul Ibarra Tue, 18 Oct 2016 01:28:40 +0200 python-sipsimple (3.0.0) unstable; urgency=medium * Added OTR encryption support in the chat stream * Use openfile where we need control over the file creation * Use defaultweakobjectmap defined in python-application * Simplified verifying the transferred file's hash * Added FileSelectorHash class to simplify code and improve readability * Fixed recovering session state in certain failure conditions * Do not wait for notifications if we couldn't notify transfer progress * Capture unhandled exceptions and log errors from new_from_sdp * Capture errors while parsing the file selector * Handle the MedisStreamDidNotInitialize notification when adding streams * Read code and reason from the notification when posting SIPSessionDidFail * Don't rely on the failure reason being set for failed transfers * Modified the SimplePayload and CPIMPayload to only work with bytes * Handle parsing errors for is-composing documents * Added package info module * Fixed MediaStreamBase not posting MediaStreamDidNotInitialize sometimes * Remove transfer_source from notifications * Set transfer origin to the remote identity if Referred-By is missing * Don't add a Referred-By header if it wasn't specified * Handle exception when closing a file that is being read in another thread * Prevent Session.transfer from being called while a transfer is in progress * Changed default transfer reject code from 486 (Busy) to 603 (Decline) * Protect SIPPublicationWillExpire from being called by an older publication * Handle race condition where state is SameState for initial PUBLISH * Remove bundled RFC/draft files * zrtp: prefer standard AES to Twofish cipher * Log Engine failures using application.log * Set locks to NULL after destroying them * pjsip: fix compilation warnings with recent versions of FFmpeg * pjsip: removed unused files * pjsip: update to revision 5249 * Always build pjsip in non-debug mode * Avoid running timers if subscription dialog was destroyed * Suppress some compilation warnings * Avoid lockups on Engine shutdown * Post notification when SIPApplication gets a fatal error -- Saul Ibarra Tue, 08 Mar 2016 12:46:06 +0100 python-sipsimple (2.6.0) unstable; urgency=medium * pjsip: fix crash when TLS transport is reset while data is being sent * Switched is-composing failure_report to no * Refactor CPIM envelope usage * Enable lrint functions on Opus * Refactored sipsimple.streams package structure * Changed MediaStreamRegistry from being a class to being an instance * Added prefer_cpim boolean class attribute to ChatStream * Fixed stripping data in the MSRP trace * Only post chat delivery notifications when notify_progress is True * Do not post MediaStreamDidNotInitialize twice in some cases * Fixed file transfer failure reasons * Prefer CPIM for chat messages for the time being * Fixed Registrar/Publisher getting stuck under certain conditions -- Saul Ibarra Fri, 04 Dec 2015 12:15:35 +0100 python-sipsimple (2.5.1) unstable; urgency=medium * Fixed opening files in binary mode when using os.open * pjsip: enumerate supported resolutions in AVF video backend * pjsip: fix opening camera with the right resolution on AVF backend * Made stream fetch the local uri from the MSRP transport or connector * Updated dependency to latest python-msrplib * Return the ZRTP peer id as is and let the application format it as it needs -- Saul Ibarra Fri, 28 Aug 2015 12:59:49 +0200 python-sipsimple (2.5.0) unstable; urgency=medium * Added VP8 video codec support * Added basic RTCP PLI support, used for requesting keyframes * Fixed crash when handling bogus Opus packets * Simplified registering audio codecs in the core * Handle socket errors when fetching XCAP documents * Fixed several compilation warnings * Added python-application as a build dependency * Fixed getting file offset on Windows -- Saul Ibarra Wed, 10 Jun 2015 13:57:35 +0200 python-sipsimple (2.4.0) unstable; urgency=medium * Refactor file transfers and add resume support * Interrupt stream.initialize if session is cancelled * Add ISIPSimpleApplicationDataStorage interface * Adapt to API changes in MSRPlib * Simplified RTP streams initialization code -- Saul Ibarra Wed, 29 Apr 2015 12:05:30 +0200 python-sipsimple (2.3.1) unstable; urgency=medium * Report sent but unconfirmed chat messages as not delivered * Don't stop and start video ports when accessing the tee * Compile PJSIP with -fno-omit-frame-pointer * Updated Windows compilation instructions * pjsip: fix compilation of ZRTP library on Windows * pjsip: don't run AVFoundation functions in the main thread * pjsip: make the video tee thread-safe * pjsip: remove tons of unneeded code -- Saul Ibarra Wed, 25 Mar 2015 12:40:20 +0100 python-sipsimple (2.3.0) unstable; urgency=medium * Added ZRTP support * Add setting for opportunistic SRTP encryption * Fix Opus SDP encoding * Reject video streams without a profile-level-id attribute * Renamed RTP stream related notifications * Renamed audio stream recording related notifications * Fix setting FFmpeg libraries path in some cases on OSX * Fix posting ICE state change notifications * Fix sending initial keyframes when ICE is used * Run _send_keyframes on the Twisted thread * Add ability to override the sender for chat messages * pjsip: fix rendering on OSX * pjsip: avoid crashes on OSX if video size changes * Work around issue with the RTP transport lock being hold for too long * Stop the VideoTransport in the device-io thread * Removed caching of statistics on the RTP transports when stopping * Fix draining the message queue in ChatStream * Post SIPApplicationWillStart before starting the core * Make SIPApplication start / stop consistent * Fix race conditions when handling SIPApplication.state * Avoid exceptions when un-pickling XCAP journal * Add chatroom_capabilities property to ChatStream -- Saul Ibarra Tue, 17 Mar 2015 09:27:02 +0100 python-sipsimple (2.2.0) unstable; urgency=medium * Make sure ICE status change notification is only sent while waiting for it * Listen for notifications on the RTPTransport until the stream ends * pjsip: skip all surround device configurations in ALSA backend * Hold the stream lock just for checking / changing states * Adapt to API change in MSRPLib * Refactor message handling in ChatStream * Check remote supported content types in ChatStream * Update supported types in ChatStream * Raised python-msrplib version dependency -- Saul Ibarra Mon, 26 Jan 2015 12:59:03 +0100 python-sipsimple (2.1.1) unstable; urgency=medium * Add a base class for Audio and Video streams * Add / support file-transfer-id SDP attribute to FileTransferStream * Refactor video pausing capability * Relax handling some errors in the core * Compile PJSIP with -O2 optimization -- Saul Ibarra Mon, 05 Jan 2015 12:36:56 +0100 python-sipsimple (2.1.0) unstable; urgency=medium * Use the Engine IP address also for media * Extend Session.connect and Session.accept with an extra_headers parameter * Do not reset the Echo Canceller unless the audio stream changes direction * Also call stream.update on the remaining streams when removing streams * Fix unnecessarily waiting for a timeout on Session.reject_proposal -- Saul Ibarra Fri, 05 Dec 2014 13:26:06 +0100 python-sipsimple (2.0.0) unstable; urgency=medium * Add video support (H264 codec) * Add support for handling initial INVITE requests without SDP * Restart TCP and TLS transports when network conditions change * Reuse disabled SDP streams * Fix parsing makefile options * Fix linking with OSX frameworks * Unregister and unpublish when network conditions change, before restarting * Leave symbols in even on release builds * Fix not ending streams in some cases when Session.remove_streams fails * Make stream initialization and ending consistent * Report stream failure in MediaStreamDidEnd * Add ExponentialTimer helper class to util * Improved handling failures when processing remote proposals * Reply with 488 if a remote SDP offer has deleted streams * Fix race conditions when handling remote proposals * Refresh SRTP crypto lines when updating local SDP * Simplify code for obtaining the path to the OSX SDK * Use 488 code when a proposal with streams fails * Use timezone aware timestamps for Session start_time and stop_time * Parse and generate bandwidth info attributes (b=) on the SDP * Include address information with the MSRPTransportTrace notification * Use SSLv23 method for TLSi (SSLv2 and SSLv3 are disabled) * Updated bundled PJSIP to revision 4961 * Make sure a removed stream always has a connection line * Remove bandwidth attributes when disabling a stream * Reject incoming re-INVITE if we couldn't reply to it * Fix setting SDP connection line when accepting a proposal -- Saul Ibarra Thu, 20 Nov 2014 18:30:35 +0100 python-sipsimple (1.4.2) unstable; urgency=medium * Avoid recompiling the whole PJSIP every time the core is recompiled * Fix encoding when expanding user home path * Made the XMLDocument schema path configurable -- Saul Ibarra Mon, 28 Jul 2014 13:51:42 +0200 python-sipsimple (1.4.1) unstable; urgency=medium * Close external VNC viewer socket when the stream has ended * Don't try to set TLS options if there is no default account * Increased the connect timeout for external screen sharing handlers -- Saul Ibarra Fri, 27 Jun 2014 09:43:19 +0200 python-sipsimple (1.4.0) unstable; urgency=medium * Add support for adding/removing multiple streams at once * Refactored SDP handling * Send re-INVITE after ICE negotiation is done * Refactored API for creating screen sharing streams and handlers * Enable RTP keep-alive using empty RTP packets * Disabled speex codec by default * Fixed race condition when saving ICE state * Made the VNC handler connection timeout a class attribute * Allow the default VNC server and viewer handlers to be configurable * Fix closing media transport to avoid leaking STUN sockets * Store our Python object in the user_data field of pjmedia_transport * Simplified srtp_active property * Make ice_active property dynamic * Make sure session state and proposed_streams are set to correct values when processing proposals * Use a shorter timeout for re-INVITEs that need to be answered without user interaction * Silence unused-function warning caused by cython -- Saul Ibarra Wed, 28 May 2014 11:51:06 +0200 python-sipsimple (1.3.0) unstable; urgency=medium * Initialize core from main thread * Add AudioStream.recorder property and remove obsolete ones * Allow AudioStream.start_recording to be called early * Ensure MediaStreamDidEnd is always posted for MSRP streams * Fixed cancel_proposal when no streams were proposed * Fixed setting proposed streams on hold when holding during a re-INVITE * Fixed originator for SIPSessionProposalRejected * Fixed pickling for some core objects * Fixed compilation with latest Cython version * Fixed processing AudioPortDidChangeSlots if bridge was stopped * Avoid sending failure reports for MSRP keep-alive chunks * Avoid resetting greenlet when session is about to end or cancel a proposal * Removed unused tls_timeout configuration parameter * pjsip: don't compile libmilenage -- Saul Ibarra Thu, 10 Apr 2014 14:55:40 +0200 python-sipsimple (1.2.1) unstable; urgency=medium * Handle errors when sending hold/unhold requests * Fix crash if sdp_done callback is processed too late * Make sure code and reason are always set when state is disconnected * Stop AudioStream bridge when deactivating stream * pjsip: fixed compilation warning * pjsip: fix build on Windows -- Saul Ibarra Wed, 05 Mar 2014 15:03:20 +0100 python-sipsimple (1.2.0) unstable; urgency=medium * pjsip: updated bundled version to revision 4738 * pjsip: return base address as ICE transport address * Use 101 as the telephone-event payload type * Always open file in binary mode for file transfers * Accept unicode in SIPURI objects * Avoid exceptions in IncomingSubscription.end * End session if an unrecoverable error happens in remove_stream * Initialize transport to None in MSRPStreamBase.__init__ * Fixed compile warnings with Cython 0.20 * Post NetowrkConditionsDidChange if system IP address changes or the DNS Manager changes the resolvers * Fake ICE gathering states since we might get them too early * Use 127.0.0.1 for bonjour when the default IP address is not available -- Saul Ibarra Wed, 19 Feb 2014 13:22:57 +0100 python-sipsimple (1.1.0) unstable; urgency=medium * Updated opus codec to version 1.1 * Cleanup opus.c and fix compilation warnings * Always put useinbandfec in SDP for opus codec * Relax codec matching when doing SDP negotiation * Use single global c line when creating SDP * Added function to manually refresh sound devices * Added trace_msrp setting * Fixed SIP and PJSIP logging * Fixed not posting state change notifications for different provisional responses * Changed notification API for renegotiating streams * Renamed streams to proposed_streams on SIPSessionNewProposal * Renamed streams to proposed_streams on SIPSessionHadProposalFailure * Added audio.muted runtime setting to SIPSimpleSettings * Post SIPApplicationWillEnd even if Engine failed * Renamed MediaStreamRegistrar to MediaStreamType * Properly handle mutex creation failures * Added missing context attribute to MediaStreamDidFail notification * Fixed memory leak by initializing the handler after the stream initialized * Moved AudioConference to audio module * Added helper functions to allocate and release memory pools * Create null sound port only once and reuse it * Simplified audio device fallback code * Fixed crash when in-dialog request fails to be sent within a subscription * Properly patch dnspython to make it nonblocking * Added initial_delay to WavePlayer, replacing initial_play * Always use timezone aware timestamps in MSRP streams * Make sure MSRPlib always gets bytes, not unicode * Always return unicode as the received chat message body * Post SIPEngineGotException also if Engine fails to start * Make send_composing_indication refresh argument optional * Return default refresh value in ChatStreamGotComposingIndication if not specified * Don't set last active timestamp automatically * Always pass copies of stream lists in Session notifications * Don't compile WebRTC AEC if machine is not x86 or x86_64 * Raised Cython version dependency to 0.19.0 * Cleanup Cython imports and remove no longer needed workarounds -- Saul Ibarra Fri, 13 Dec 2013 13:45:31 +0100 python-sipsimple (1.0.0) unstable; urgency=low * Updated core to PJSIP 2 * Added gain control and high pass filter to audio processing * Added Opus codec support * Added support for RFC5768 (ICE option tag) * Added enabled setting for echo canceller and echo_canceller settings group * Fixed echo cancelling when using 32kHz sample rate * Always disable sound device when idle * Removed unused ignore_missing_ack feature * Removed engine shutdown workaround * Removed TLS protocol setting * Removed NAT detector from SIPApplication * Don't cap codecs based on sample rate, let PJSIP resample * Disabled narrowband speex * Fixed starting media stream if ICE fails early * Don't reset stream statistics, always report absolute values * Don't add BonjourAccount to AccountManager if there is no bonjour support * Set session state to terminated when ended before starting * Prevent PJSIP from switching transports automagically * Dropped support for Python 2.6 -- Saul Ibarra Fri, 09 Aug 2013 11:17:47 +0200 python-sipsimple (0.35.0) unstable; urgency=low * Added default URI implementation for contacts * Added extension to PIDF to include the status type * Added ItemCollection settings type * Added RuntimeSetting to the configuration framework * Publish instance id over Bonjour * Refactored bonjour discovery notifications * Return SIP Request headers in SIPMessageDidFail and SIPMessageDidSucceed notification data * Removed unnecessary end method on Message * Fixed building XML schemas on all platforms * Fixed parsing sip.instance from a REGISTER reply * Fixed compilation with Cython 0.19 * Fixed posting PublicationDidSucceed after a failure * Catch ValueError when parsing XML documents -- Saul Ibarra Wed, 26 Jun 2013 16:08:30 +0200 python-sipsimple (0.34.0) unstable; urgency=low * Added Bonjour presence * Added presence subscriber for itself to Account * Added SDPNegotiator class * Added ability to properly stringify SDPSession objects * Added ability to parse a SDPSession object from a string * Disable PJSIP assertions on recoverable errors * Removed extra checks on SDP origin field * Fixed deadlock when cross instantiating conditional singletons * Avoid sending adding participant progress if the answer is final * Avoid doing mDNS lookups with dnspython * Define __nonzero__ for XCAP Document objects * Made AddressbookManager.transaction a class method -- Saul Ibarra Tue, 19 Mar 2013 11:31:27 +0100 python-sipsimple (0.33.0) unstable; urgency=low * Added notification when incoming subscription times out * Added call_id attribute to Subscription and IncomingSubscription * Allow incoming subscriptions to have expires 0 * Fixed session transfer after API changes * Fixed setting initial timeout for incoming subscriptions * Renamed desktop-sharing media type to screen-sharing * Provide direct access to stream types on MediaStreamRegistry * Post notifications about removed neighbours when stopping bonjour services * Only set reason for NOTIFY if state is terminated -- Saul Ibarra Fri, 25 Jan 2013 16:22:05 +0100 python-sipsimple (0.32.0) unstable; urgency=low * Fixed updating local SDP direction if answer is inactive * Fixed test for supported audio codecs * Adjusted pres-rules auid to org.openmobilealliance.pres-rules * Removed icon alternative location since it was custom and is now unused * Use digits only for contact usernames to improve interoperability * Disable NAT detection * Allow more types to be directly used as configuration data types -- Saul Ibarra Fri, 11 Jan 2013 12:03:11 +0100 python-sipsimple (0.31.1) unstable; urgency=low * Terminate session if a stream fails and can't be removed from the session * Removed extended-away state from PIDF extension * Fixed setting local hold state after direction was inactive * Prevent account's activate/deactivate methods to run at the same time * Fixed race conditions when deleting an account * Fixed some lintian warnings -- Saul Ibarra Wed, 28 Nov 2012 12:37:01 +0100 python-sipsimple (0.31.0) unstable; urgency=low * Refactored streams and account relationship * Added Supported header indication for GRUU on REGISTERs * Allow extra headers to be passed to Registration * Don't use GRUU for conference subscriptions and referrals * Fixed XML datatypes for CIPID extensions * Fixed sending extra headers when unsubscribing * Fixed parsing RLS NOTIFY fayload if advertised CID is not present * Fixed thread safety issues with ChatStream functions * Moved identity attributes from MSRPStreamBase to ChatStream -- Saul Ibarra Fri, 26 Oct 2012 12:33:23 +0200 python-sipsimple (0.30.1) unstable; urgency=low * Allow unicode filenames on WaveFile and RecordingWaveFile * Fixed handling stream hold edge cases * Fixed unitialized variable error when using Cython >= 0.15 -- Saul Ibarra Mon, 17 Sep 2012 11:46:56 +0200 python-sipsimple (0.30.0) unstable; urgency=low * Added PUBLISH for presence and dialog events * Added SUBSCRIBE using RLS for presence and dialog events * Added SUBSCRIBE for presence.winfo and dialog.winfo events * Added handing for RLS NOTIFYs for presence and dialog events * Added Offline Presence capability using XCAP pidf-manipulation * Added Icon Storage using XCAP oma-pres-content * Added Presence policy management using XCAP oma-common-policy * Added Addressbook with Presence enabled Contacts * Added MSRP Switch NICKNAME support * Added GRUU support (RFC 5627) * Added ability to configure SIP loop detection * Refactored XML payloads framework and datatypes * Refactored the XCAP manager and its API * Refactored contact management into addressbook management * Refactored interaction between the account and its handlers * Fixed building sipfrag payloads * Fixed crash when bogus G722 payload is received * Fixed crash on SDP version overflow * Fixed removing a stream if a negative response was received * Fixed engine failure on bogus incoming REFER requests * Fixed crash on RTCP decryption when using SRTP * Fixed handling re-INVITEs with empty body * Fixed subscribing to conference event from Bonjour account * Reply with 200 OK to in-dialog OPTIONS requests * Support hostnames in STUN servers list * Skip processing bogus NOTIFY requests * Honor Min-Expires header for REGISTER requests * Adapted to eventlet package rename * Raised dependency on msrplib and xcaplib due to API changes -- Saul Ibarra Thu, 06 Sep 2012 18:13:17 +0200 python-sipsimple (0.20.0) unstable; urgency=low * Refactored XML document manipulation framework and payloads * Added screen image extension to User from conference * Accumulate chunks in ChatStream if they were segmented * Do not assume content type is text in CPIM messages * Validate participant URI when inviting to a conference * Fixed race condition when stopping uninitialized MSRP stream * Add reason attribute to notification data for SIPSessionTransferDidFail when rejecting * Fixed handling incoming call transfer failures * Dropped support for Python 2.5 * Fixed issue with notifications being posted in the wrong order * Updated build instructions -- Saul Ibarra Mon, 19 Dec 2011 14:34:44 +0100 python-sipsimple (0.19.0) unstable; urgency=low * Added Contact Management API * Integrated XCAP manager into Account * Added WebRTC AEC * Added Call Transfer support * Refactored PJSIP build process to avoid using svn * Allow TLS MSRP transport also for Bonjour accounts * Allow account to be restartable * Added ability to query the system default device name * Added the ability to get the old value of a setting * Added the ability for an XMLApplication to unregister a namespace * Added XCAPTransaction context manager for XCAPManager transactions * Workaround lxml exception when unicode strings contain encoding declaration * Automatically singleton-ize SettingsObject subclasses with static IDs * Avoid processing notification if audio bridges are not created yet * Fixed passing headers dictionary to subscription callback * Fixed getting Subscription-State header parameters * Fixed handling bogus responses to SUBSCRIBE requests * Call the notification handlers in the appropriate threads in application.py * Execute function immediately if already in the requested thread * Fixed parsing Refer-To header * Fixed __eq__ methods to properly compare with unknown type objects * Added the missing __ne__ methods to all the classes defining __eq__ * Avoid setting sound devices if none was removed * Handle error if remote ends session while we try to end it as well * Fixed race condition when reading/using the invitation state in Session.end * Added the ability to specify fallback policies when adding an XCAP contact * Fixed bug that changed empty strings into None when loading configuration * Terminate conference subscription at the same stage if session failed or ended * Consider XCAP applications unsupported until proven otherwise * Set default register interval to 3600 * Apply changes to audio tail_length in realtime * Don't fail to start audio device if starting the EC fails * Reset stream statistics after retrieving them -- Saul Ibarra Fri, 16 Sep 2011 10:52:41 +0200 python-sipsimple (0.18.2) unstable; urgency=low * Added compatibility with Python 2.7 * Adapted code for Cython >= 0.13 * Don't depend on the debug interpreter, recommend it * Removed unused SSL methods * Removed use of pysupport, use dh_python2 instead * Updated import paths for Null and Singleton for latest python-application * Fixed race condition that resulted in multiple bonjour accounts -- Saul Ibarra Wed, 08 Jun 2011 09:11:50 +0200 python-sipsimple (0.18.1) unstable; urgency=low * Added push file transfers (RFC5547) * Implemented support for MSRP ACM (RFC6135) * Added file transfers information to conference event package * Fixed string representation of SIP URIs with special characters * Implemented multi-level key based access to configuration objects * Log exceptions that occur while saving the configuration when deleting * Fixed SDP negotiation on bogus answer * Remove SDP attributes when a stream is disabled * Added format list validation for MSRP streams * Added DuplicateIDError to replace ValueError for duplicate ID's * Post notifications when settings objects are created/deleted * Protect AccountManager.load_accounts against being called multiple times * Removed internal AccountManager methods for adding/removing accounts * Refactored API to provide storage backends to SIPApplication * Start accounts in parallel * Protect Session.end against being called multiple times * Don't instantiate DNS resolver if no DNS query will be done * Break circular reference between streams and Session -- Saul Ibarra Mon, 23 May 2011 16:34:20 +0200 python-sipsimple (0.18.0) unstable; urgency=low * Added support for out-of-dialog REFER * Added add/remove participants functionality to Session * Added support for Subject header * Fixed accepting incoming subscription without any initial content * Fixed exception when NAT type detection is attempted without connectivity * Fixed conference event subscription for Bonjour account * Fixed exceptions when contact URI can't be built for the desired route * Fixed subscription locking issues * Set remote focus on incoming session, if applicable * Fixed building header body with parameters without value * Fixed building UAC and UAS dialogs when headers contain parameters * Fixed setting contact header parameters for subscription * Hide route header for outbound SUBSCRIBE requests * Fixed crashes and increased resilience when connectivity is lost * Fixed exception classes not to update their internal dict * Relax check on SDP origin to increase interoperability * Save XCAP journal after each committed operation to avoid loosing it * Reset XCAP journal when account id changes * Don't use contact URI when building conference Referral and Subscription * Fixed bug where settings with dynamc IDs were not saved in some cases -- Saul Ibarra Fri, 18 Mar 2011 17:00:29 +0100 python-sipsimple (0.17.1) unstable; urgency=low * Simplified FileSelector interface and interaction with the application * Fixed Timestamp formatting with offset-aware datetime objects * Return unicode for username and fullname from sipsimple.util.user_info * Made display_name on accounts support unicode * Fixed CPIMIdentity parsing to be unicode aware * Changed SIP headers to handle unicode display names * Made Path datatype inherit from unicode * Simplified disconnect reason on failures * Fixed exception when the session is ended on error conditions * Fixed support for audio devices containing unicode symbols in their name * Fixed detecting backslash in regex * Added request_uri attribute to Invitation -- Saul Ibarra Wed, 16 Feb 2011 14:25:28 +0100 python-sipsimple (0.17.0) unstable; urgency=low * Added blocking API to WavePlayer * Added peer_address attribute to all request objects * Do not enforce transport on request URI from route * Fixed crash on IncomingRequest deallocation * Simplified and made account contact management consistent * Fixed conference payload to accept multiple Media elements * Build PJSIP with debugging symbols, if specified * Fixed parsing conference-info payload * Removed handling of impossible invitation state transition * Wait for things to stabilize for bonjour after returning from sleep * Don't send SIPSessionDidFail in end if state is None * Only handle records in the local. domain for bonjour * Added ability to compute a FileSelector's file hash later * Added AudioStreamDidTimeout notification * Make request_uri the first argument for Request object * Added remote_contact_header attribute to Invitation * Added generation of -dbg debian package * Fixed exception when an empty SEND is received in the MSRP stream * Added request_uri attribute to Invitation and Subscription * Added conference event support to Session * Added recipients to ChatStreamGotComposingIndication notification * Added remote_media attribute to MSRP streams * Fixed private chat message detection * Send 500 response if we fail to create incoming invitation * Added lock to IncomingSubscription and released GIL where appropriate * Terminate incoming subscription if NOTIFY got 408, 481 or 7xx * Handle local timeout for outgoing NOTIFY requests in IncomingSubscription * Interrupt commands instead of killing and restarting greenlets * Only refresh subscription on events if we already have one * Properly schedule events after system is stable when waking up from sleep * Added missing notification handlers in XCAPManager for system events * Properly perform NAT detection considering all system event triggers * Allow Command to send specific results and also propagate exceptions * Fixed UTC offset in Timestamp class * If the received chat doesn't have a timestamp, build it offset-aware * Added python-dateutil dependency * Added build time dependency on cython-dbg * Reverted wrong changes and made xcap manager test script work again * Fixed race conditions in subscription handlers * Don't have XCAPManager as a singleton to avoid retaining the account forever * Terminate session conference subscription on SIPSessionWillEnd * Reduced subscription retry interval on fatal failures * Fixed receiving empty SEND in file transfer stream -- Saul Ibarra Thu, 27 Jan 2011 13:56:55 +0100 python-sipsimple (0.16.5) unstable; urgency=low * Fixed matching of media codecs on incoming calls * Allow ip_address to be specified on engine start * Fixed accepted types checking when using CPIM * Fixed CPIM support detection * Generate InvalidStreamError if no compatible payload types are found in ChatStream * Added nameservers used for lookup to the DNSLookupTrace notification * Added InterruptCommand exception * Added DNS resolver autodetection capabilities * Made accessing to the transport parameter of a SIP URI easier * Fixed TLS transport initialization * Fixed XCAPManager shutdown * Adapt XCAPManager test script to changes in the middleware * Handle ConnectionLost error in XCAPManager * Only use fallback candidate list as the last resort * Avoid creating a external reference on the subscriptions lists * Avoid moving external references to resource-lists toplevel * Don't initialize XCAPManager if the server doesn't support certain auids * Reset cached documents if XCAP root changes * Added ThreadManager and moved thread related stuff from util to threading * Reformatted some module docstrings * Made configuration thread safe * Automated finding python packages in setup.py * Updated documentation -- Saul Ibarra Tue, 14 Dec 2010 16:57:07 +0100 python-sipsimple (0.16.4) unstable; urgency=low * Fixed accessing Message-Account in MWI payload as it could be None * Fixed building codec list when rtpmap line is missing * Match codec names case insensitive -- Saul Ibarra Tue, 30 Nov 2010 10:32:03 +0100 python-sipsimple (0.16.3) unstable; urgency=low * Changed some option defaults to False * Do not impose limits on the subscription interval * Added all parsed SIP headers to SIPEngineGotMessage notification * Refactored bonjour code to be more efficient and handle all use cases * Fixed crash when parsing Retry-After header * Fixed MSRP chunk transaction status callback * Set the response code and reason when outgoing session times out * Don't answer SUBSCRIBE while deallocating * Fixed crash when Content-Type header is missing for MESSAGE * Do not create an audio stream if not compatible codecs are found * Created ContentType object for representing content type in notifications * Added extra attributes to SIPSubscriptionGotNotify notification * Fixed race condition in Session that caused exceptions in some situations -- Saul Ibarra Fri, 26 Nov 2010 15:18:48 +0100 python-sipsimple (0.16.2) unstable; urgency=low * Fixed memory and file descriptor leaks in BonjourServices * Added notifications for Bonjour discovery failures * Refactored Bonjour re-discovery after settings change * Ignore TLS contacts if the Boujour account doesn't have a certificate * Refresh MWI subscription if always_use_my_proxy setting changes * Use always_use_my_proxy setting for MWI subscriptions * Set minimum time for refreshing a subscription to 30 seconds * Wait for 3 hours if MWI subscription fails instead of stopping it * Fixed bonjour discovery when SIP transport list is changed * Made accounts also listen for config changes from SIPSimpleSettings * Do not return routes with unsupported transport from the DNS lookup * Set MSRPRelayAddress setting default port to 2855 * Moved server_advertised_uri attribute to the mwi handler * Added reregister method on Account * Added reactivate methods for registrar and mwi * Prefer the server advertised voicemail URI over the user setting * Added account.sip.always_use_my_proxy setting * Use None when the server advertised voicemail URI is an empty string * Reset the server advertised voicemail URI when MWI is disabled * Fixed handling of multiple settings changed at the same time * Remove sip: from the server advertised voicemail uri when saving it * Use capital case letters for acronyms * Remove transport_list setting from BonjourAccount * Reset bonjour neighbours on account deactivation * Turn off ICE by default * Limit PJSIP log level setting value between 0 and 5 to avoid crashes * Fixed handling of Account id change in AccountManager * Fixed handling of the id change of an Account and other SettingsObjects * Made XCAPManager not transition to insync if journal is not empty * Made audio device settings strings and removed unnecessary empty subclases * Made SampleRate only accept valid rates * Added SIPAccountWillActivate and SIPAccountWillDeactivate notifications * Set XCAP User-Agent on application start * Use xml.xsd from local folder instead of importing it remotely * Trigger a XCAP document fetch on some subscription errors * Make port test consistent with the rest of the code * Simplified port range handling and fixed case for odd ports number * Fixed port boundary checks * Fixed incorrect __hash__ method * Use UA string as User-Agent header for XCAP requests * Avoind unnecessary conversion to unicode in PortRange conversion * Added missing __ne__ method to some data types * Fixed saving configuration after assigning DefaultValue to a setting * Added PositiveInteger datatype * Enhanced xcapdiff subscription process * Removed use_xcap_diff setting * Rollback: Changed visibility of command and data channels * Rollback: Avoid using SubHandlingValue object inside XCAPManager * Fixed account elements reload on settings change * Synced Engine default options with settings * Improved default values for various global settings * Use the specific version of cython 0.12.1 for building the package * Enhanced xcapdiff subscription termination * Don't try to unregister if we weren't registered at all * Changed visibility of command and data channels to private * Fixed handling bogus TXT records for XCAP server lookups * Fixed contact edit in XCAPManager when it needs to be removed and readded * Avoid using SubHandlingValue object inside XCAPManager * Fixed building contact name on XCAP manager * Fixed use of identity conditions * Fixed handling of SIPRegistrationDidFail and SIPSubscriptionDidFail exceptions * Fixed handling of SDP c line inside the media stream * Don't wait for pending operations to finish on shutdown * Added cached_cocuments property to XCAPManager * Handle BadStatusLine exception when fetching/updating documents * Added equal and hash methods to Contact, Policy and condition classes * Raise RuntimeError if no cache directory is provided for XCAP documents * Don't keep old transformations if updated rule's action is not 'allow' * Removed some unnecessary NotificationCenter instantiations * Added properties for handling the server advertised voicemail URI * Disable dialog event by default * Increase default subscribe and publish intervals * Added back thread attribute in SIPApplication * Properly fix race condition when first NOTIFY for MWI arrives * Avoid adding more than one MWI subscribe operation to the command channel * Fixed waiting timeout for engine shutdown * Changed name for reactor thread attribute and join thread on stop * Moved Changelog back to toplevel * Fixed boolean parameters in xcap_manager test script * End MWI subscription before ending registration -- Adrian Georgescu Thu, 11 Nov 2010 13:31:38 +0100 python-sipsimple (0.16.1) unstable; urgency=low * Fixed XML document parsing for unicode objects * Changed default audio sample rate to 44100 * Stop using audio device when idle on Snow Leopard * Added short description for a legal sip address format * Send MWI subscription to voicemail_uri if specified * Fixed broken dependency to python-aplication for non-Debian systems -- Saul Ibarra Mon, 06 Sep 2010 15:55:28 +0200 python-sipsimple (0.16.0) unstable; urgency=low * Added XCAP contacts management based on OMA specifications * Added parser/generator for OMA pres-rules extensions * Added custom extension for extra attributes to entries in resource-lists * Added xcap-caps payload support * Added support for Message Waiting Indicator (MWI) * Added SIPAccountMWIDidFail and SIPAccountMWIDidGetSummary notifications * Added min_expires attribute to SipSubscriptionDidFail notification * Added audio device change detection capability in Windows * Improved logic for determining source IP address used in signalling * Added lookup_xcap_server method to DNSLookup * Added timestamp to sipsimple.util.Command objects * Added generic All and Any objects to sipsimple.util * Added support for deleting children by id in RuleSet and RLSServices * Added extension to add a display-name to common policy rules * Renamed Icon payload definition to PresenceContent * Added support for finding the parent of an element to sipsimple.payloads * Added support for XPath to sipsimple.payloads * Fixed parsing of IdentityMany policy elements * Added support for parsing file-like objects with XML payloads * Improved XMLElement hashes to allow list elements to be hashable * Delegated encoding/decoding of URI values to sipsimple.payloads * Improved unicode support of XML payloads * Removed IP address from rtcp SDP attribute * Avoid refreshing subscription if no NOTIFY was received after an un-SUBSCRIBE -- Saul Ibarra Fri, 03 Sep 2010 10:08:12 +0200 python-sipsimple (0.15.3) unstable; urgency=low * Changed default codec list to have G722 as first choice * Fixed handling of case when session is ended before INVITE is sent * Fixed subversion command execution for Windows * Set all devices to None before shutdown * Made the reactor thread a daemon thread * Bumped Standards-Version to 3.9.1 -- Saul Ibarra Fri, 13 Aug 2010 11:06:45 +0200 python-sipsimple (0.15.2) unstable; urgency=low * Added check to ensure uniqueness of account IDs * Revert G722 adaptive bitshifting that broke re-INVITES * Added python-lxml dependency and sorted dependencies order * Fixed handling unicode characters in the bonjour neighbour display names * Made use of the normalized property of Path configuration datatype * Fixed handling the case when an internal pjsip invitation error occurs * Fixed falling back to the None device when opening an AudioMixer * Null is already an instance, no need to instantiate it anymore * Added exponential timeout to DNS lookups for register * Lower PortAudio revision to 1412 and removed pulse patches * Bumped Standards-Version to 3.9.0 -- Saul Ibarra Wed, 28 Jul 2010 10:39:05 +0200 python-sipsimple (0.15.1) unstable; urgency=low * Added support for Microsoft Windows * Added PJSIP patch for adaptive G722 bitshifting * Improved the initialization of the TLS options when starting the Engine * Added support for terminating sessions in SessionManager * Don't enable bonjour account if bonjour support is not detected * Modified account matching rule for incoming sessions to ignore IP address * Added thread attribute to SIPApplication as the Thread the reactor runs in * Improved synchronization of the audio devices with corresponding entries -- Saul Ibarra Wed, 07 Jul 2010 17:17:42 +0200 python-sipsimple (0.15.0) unstable; urgency=low * Updated documentation * Added Acknowledgments section * Added support for MSRP Alternative Connection Model * Added NAT detector to SIPApplication object * Don't fail a DNS lookup if an IP address is provided * Made filename argument of start_recording mandatory * Added proposed_streams attribute to SIPSessionGotAcceptProposal * Hide Route header when sending requests out * Fixed adding/removing ports on a bridge after stop was called * Improved SIPURI with pickling and matching capabilities * Improved hold/unhold cascading in Session * Modified WavePlayer and WaveRecorder to allow unicode objects as filename * Fixed crash when system has no soundcard at all * Added ability to select PortAudio revision * Fixed use of the Reason header for determining call disconnect reason * Fixed handling of stream added to conference * Only accept IP addresses for the STUN server list * Moved MSRP transport setting to the account * Fixed ICE support detection * Added configuration instructions for audio device sharing in Linux * Fixed starting of recording on AudioStream while on hold * Fixed MSRP transport used for contacting the relay * Fixed error message in SIPProxyAddress configuration data type * Modified DNSLookup to not timeout completely if NS queries fail * Modified AudioMixer so that it keeps the sound device open on MacOSX 10.6 * Added permanent references to AudioStream to keep pjsip from crashing * Fixed Bonjour neighbour discovery handling * Improved sRTP negotiation capabilities * Fixed the build process on MacOSX 10.6 * Split the installation instructions for Debian and Ubuntu * Split the installation instructions for MacOSX 10.5 and 10.6 * Allow use of cython 0.11.2 * Fixed handling of missing ACK in Session * Fixed dialog rules payload namespace * Added pjmedia patch not to close a media stream too fast * Allocate thread descriptor from pool in portaudio callback * Fixed receiving a BYE without receiving a final response to INVITE -- Saul Ibarra Mon, 21 Jun 2010 12:59:04 +0200 python-sipsimple (0.14.2) unstable; urgency=low * Improved organization of attributes/properties in accounts * Added patch to not consider SDP in all requests/responses * Added host name to the BonjourAccountDidAddNeighbour notification * Improved default value of BonjourAccount's display_name setting * Added support for authentication username * Raised required cython version * Added RootAudioBridge to skip not needed audio processing -- Saul Ibarra Tue, 20 Apr 2010 10:02:32 +0200 python-sipsimple (0.14.1) unstable; urgency=low * Added missing dependency for AVAHI's Bonjour compatibility -- Saul Ibarra Fri, 09 Apr 2010 18:29:03 +0200 python-sipsimple (0.14.0) unstable; urgency=low * Added handling for PJMEDIA_SDPNEG errors in Session * Use transport in general settings when not using a MSRP Relay * Added missing presence and dialog settings * Do not post MSRPLibraryLog for debug level * Added reason argument to reject and reject_proposal methods of Session * Add ability to CANCEL re-INVITEs * Changed the way NotificationProxyLogger sends MSRP trace notifications * Modified the AudioStream to initialize the audio device once it starts * Added Intel architecture to Prerequisites * Calculate ICE candidate priority according to draft * Use regular nomination in ICE instead of aggressive * Changed DNSLookup to fallback to using local nameservers if NS queries fail * Added instructions for installing the cli scripts * Added new setting account.sip.enable_outbound_proxy * Fixed CPIM implementation and improved ChatStream interface * Added notifications for ICE negotiation status * Refactored audio support * Don't initialize ICE transport if remote SDP doesn't offer ICE * Proper forward declaration of Cython objects * Save session start time when media stream did timeout * Added audio conference support * Set disconnect_reason and failure_reason to the Reason header value if present (RFC 3326) * Added sip.transport_list setting to BonjourAccount * Added MSRP settings to Bonjour account * Added publishing and discovery support to BonjourAccount * Modified AudioTransport to not pause/resume the stream on hold/unhold * Add support for ICE keepalive * Changed defauls for use sRTP and codec list for better interoperability * Added a timeout when sending (re-)INVITEs * Updated documentation * Numerous bugfixes -- Saul Ibarra Fri, 09 Apr 2010 13:36:50 +0200 python-sipsimple (0.12.0) unstable; urgency=low * Removed obsolete desktopsharing.py file * Use OMA standard auids for icon and directory applications * Added slot property to AudioStream * Refactored DNS lookup implementation * Don't bit-shift g722 audio samples * Updated installation procedures * Added IVirtualAudioDevice interface and support for it in AudioStream * Modified DNSLookup to offer both a synchronous and an asynchronous API * Improved logging in DNSLookup.lookup_service * Added the request URI to the SIPEngineGotMessage notification data * Added CIPID (RFC4482) application * Added check in MSRPStreamBase for transport mismatch in settings * Added checks for SDP media stream transport for incoming sessions * Made Registration always communicate via notifications * Added capabilities application (RFC5196) * Added conference XML application (RFC4575) * Added message summary application (RFC3842) * Modified AudioStream to support changing the rtp port in reINVITEs * Pass code and reason of SIP MESSAGE response to its notification * Added dialog-info application (RFC4235) * Added call_in_(twisted|green)_thread utility functions * Added limit utility function * Refactored sipsimple.account using a green model * Restrucutred SIPApplication to simplify the code * Added support for detecting default IP address changes * Added redirect_identities attribute to SIPSessionDidFail notifications * Modified Account to re-register when some settings change * Removed sip.ip_address and rtp.ip_address global settings * Removed msrp.port global setting * Reorganized account registration notifications * Reorganized settings * Patched dns.entropy module which is not thread-safe * Modified SilenceableWaveFile to use a green model * Made Account.credentials a property * Reorganized the contents of the sipsimple.util module * Modified MSRPStreamBase to stop other operations when an end is requested * Added support for SystemDidWakeUpFromSleep notification in registration * Moved Timestamp from sipsimple.applications.util to sipsimple.util * Removed sipclients related modules, scripts and data from the project * Reorganized packages and modules * Numerous bug fixes -- Lucian Stanescu Wed, 20 Jan 2010 12:21:35 +0000 python-sipsimple (0.9.1) unstable; urgency=low * Prepare next version -- Adrian Georgescu Wed, 26 Aug 2009 15:13:16 +0200 python-sipsimple (0.9.0) unstable; urgency=low * Added upgrade guide: Read this wiki page when upgrading from a previous version * Added developer guide: http://sipsimpleclient.org/projects/sipsimpleclient/wiki/SipDeveloperGuide * Implemented notifications system from python-application * Added event-driven middleware API based on notifications: http://sipsimpleclient.org/projects/sipsimpleclient/wiki/SipMiddlewareApi * Added high-level configuration API: http://sipsimpleclient.org/projects/sipsimpleclient/wiki/SipConfigurationAPI Added sip_settings to manage the configuration framework * Configuration file config.ini is now deprecated. Use sip_migrate_settings to migrate settings from previous config.ini to the new configuration API * Added synchronous API for non-blocking operations * Adapted all command line tools to the new APIs * Added re-INVITE (hold/un-hold, adding and removing other media types) * Added sip_session script that combines MSRP chat and RTP audio in one session, media can be added/subtracted to/from active session. The script can be used for both incoming and outgoing sessions * Unify command line arguments for command line tools * Added dummy audio device support for systems without sound card * Added --auto-answer and --auto-hangup to sip_audio_session script. sip_audio_session script can now be used for scripting alarms that check both siganling and media paths, it can detect missing ACK, negative SIP response codes and missing RTP media after call setup * sip_send_file and sip_desktop_sharing temporarily disabled until migrated to the new APIs * Added asynchronous DNS lookup module and removed DNS lookups from core * Added Session manager to manage multiple SIP sessions * Added Account manager to manage multiple SIP accounts * Integrated SIP registration into the SIP account * Added next hop failover for INVITE and REGISTER methods * Made Route argument mandatory in SIP core object creation * Allow settable Contact URI for objects in SIP core * Better support for server-less scenarios (P2P and bonjour) * Added support for TLS in P2P/bonjour mode for both SIP and MSRP * Integrated various patches into PJSIP, Eventlet and Greenlet projects * Ported eventlet to ARM CPUs, msrp works now on Nokia 810 * Improved ICE/STUN support and related DNS lookups * Improved logging to file and console for SIP, MSRP, PJSIP an core notifications, added runtime control for traces in scripts * Added support for draft-ietf-mmusic-file-transfer-mech-11 in msrplib * Added support for chunking in msrplib * Splited sipclient Debian package into python-sipsimple and sipsimple-cli * Numerous bug fixes -- Dan Pascu Mon, 13 Apr 2009 16:56:50 +0300 sipclient (0.4.0) unstable; urgency=low * Added desktop sharing using VNC over MSRP * Added PJSIP patch to write and parse i= line to SDP and updated SDP wrapper classes to use it * Added --pjsip-clean-compile option to setup.py * Continue with present PJSIP tree if SVN update fails in setup.py * Revised Invitation state machine, method names, events and internals * Only ring on 180 in sip_audio_session.py * Allow Credentials object to not have a password set * Improved scripts to print any remaining logging message after the Engine has been shut down * Catch uncaught exceptions in PJSIP callbacks and generate an event to inform the application * Disconnect if no audio is received for 10 seconds in sip_audio_session.py * Use default_host_ip from python-application if local_ip is set to None in Engine * Integrated notification system from python-application * Renamed all event names to new notification naming scheme * Changed interface of Engine class in order to use Singleton metaclass from python-application * Fixed some threading bugs in the core * Renamed anything related to PyPJUA to SIP core * Renamed pypjua module to sipsimple * Fixed various bugs in XML payload handling * Made small, cosmetic changes in presence-related scripts * Set return code of scripts based on SIP status codes * Added --auto-hangup option to sip_audio_session to terminate a call after a specified amount of time * Enhanced XML applications in order to be more robust in usage and allow for future implementation of features such as xcap-diff * Updated eventlet-based interface to engine and moved it to green package * Moved logging from green classes into logstate.py module * Fixed bug: in CPIM headers 'sips' was parsed as 'sip' * Fixed bug: CPIM content was not generated / parsed properly (must add space before Content-Type line) * sip_im_session: throttle played sounds and play them at specified volume * sip_im_session: calling from command line is equivalent to issuing :call * sip_im_session: do things in the background when possible (like registrations and closing sessions) * sip_im_session: print a calming message if it takes longer than 1 sec to finish SIP transactions before exit * sip_im_session: added --no-register option * sip_im_session and sip_send_file: added --msrp-tcp option * sip_im_session: save chat history under .sipclient/history * sip_im_session: hide "Early session" messages * sip_im_session and sip_send file: relay can no longer be used for outgoing session. -r auto option is deprecated. * sip_send_file: fixed -m option to work * sip_im_session and sip_send_file: added --trace-engine option * sip_im_session: fixed to ignore path in incoming file transfers * sip_send_file: fixed not to put path in file transfer description * Added iso8601.py by Michael Twomey for parsing CPIM's DateTime header * CPIM now puts DateTime header in every message. DateTime is also used now when displaying incoming messages * Fixed green package to end session with 500 in case of exception (not 488) * sip_im_session: don't exit if failed to parse an incoming message * Fixed shutdown functions not to silent pypjua errors * Avoid hiding tracebacks by not using potentially blocking code in finally * Fixed a number of bugs in chat scripts * Removed debug_util.py * Removed chatroom.py * Removed trafficlog.py -- Adrian Georgescu Mon, 19 Jan 2009 17:26:02 +0100 sipclient (0.3.0) unstable; urgency=low * This version requires eventlet for twisted, which is not yet available as debian package. See doc/INSTALL.x for how to manually install eventlet * Purge the old debian package and/or remove any command line tools starting with sip_ and xcap_ from the path where they have been installed * Renamed sip_rtp_audio_session to sip_audio_session * Removed sip_msrp_im_session and sip_msrp_file_transfer scripts * Added sip_im_session script, supporting chats with multiple other parties and incoming file transfers * Added sip_send_file for sending files over SIP/MSRP in a batch mode * Added auto_accept_file_transfers General option * Added rtp_port_range General option * Added listen_udp General option * Added file_transfer_directory General option * Added history_directory General option * Added log_directory General option * Added msrp_relay SIP account option * Play sounds when send and receive IM * Added xcap_rls_services script * Added rtp_port_range init and runtime option to Engine/PJSIPUA * Added realtime echo cancellation adjustment to audio session script * Remodeled Invitation class to mirror PJSIP states and communicate with application by means of SDPSession objects * Adjusted audio and IM session scripts to use this new Invitation class * Implemented RTPTransport and AudioTransport classes, which wrap PJSIP media transport and audio stream functionality respectively * Refactored SIPURI, Credentials and Route classes to be dynamic * Added foreign thread and deallocation checking to PyPJUA * Prevented PJLIB calles in import to improve Windows compatibility * Removed dependency on python-application for PyPJUA * Added msrplib: MSRP protocol implementation powered by twisted/eventlet * Added twisted/eventlet wrapper for Engine * Added creation of history, log and file transfer directories in enrollment * Made sip traces get logged in //sip_trace.txt * Added script sip_auto_publish_presence * Made sip_subscribe_rls script subscribe by default to username-buddies@domain -- Adrian Georgescu Sat, 29 Nov 2008 10:44:33 +0100 sipclient (0.2.3) unstable; urgency=low * Updated MacOSX installation procedure from sources * Added fink installation from binary packages * Update the server software the client is tested against * Put version in default User-Agent header * Renamed obsolete setting name presence to use_presence_agent * Improved documentation * Define log directory in config file * More comments about settings from sample config file * Update linux install procedure with debian package installation instructions * Use the etag returned after an XCAP PUT so as not to need a GET afterwards * Change the name of the config option presence to use_presence_agent and document it * Fix buggy interconnections between conference bridge slots * Make the sip_subscribe_winfo script also use the listen_udp option * Do a GET after a HTTP error in PUT for pres-rules * Ported new-style SRV lookup to sip_msrp_file_transfer script * Ported new-style SRV lookup to sip_msrp_im_session script * Change order of display (available accounts, current account) * Record audio files to the history directory * Fix sip_subscribe_winfo when polite-blocking and change output * Add xcap_pres_rules script * Toggle logging at runtime in sip_register * Better description of what the scripts do * Make sip_register script exit after a number of REGISTERs * Print current pres rules when sip_subscribe_winfo starts * Add polite-block option to sip_subscribe_winfo script * Added PJSIP version string to default User-Agent header * Fix for bug in accepting SDP streams * Added LICENSE file to MANIFEST.in -- Adrian Georgescu Wed, 29 Oct 2008 19:46:25 +0100 sipclient (0.2.2) unstable; urgency=low * Small bug fixes * Recording capability added * Make sip_subscribe_rls script accept multipart/related content-type * Allow setting basic presence in sip_publish_presence script -- Lucian Stanescu Wed, 15 Oct 2008 17:39:34 +0200 sipclient (0.2.1) unstable; urgency=low * Include the xml schemas * Include Debian packages instructions in docs/INSTALL.linux * A minimal configuration file is generated if not found * Add 'Supported' header when subscribing to RLS services URIs * Added local IP address and port to listen on for UDP packets to configuration file * Added registerless "bonjour" account to relevant scripts * Improved SRV lookup procedure and do full lookup for outbound proxy * Do not consider 404 an error when getting the pres-rules document in sip_subscribe_winfo * Make the UDP limit 64K -- Lucian Stanescu Mon, 13 Oct 2008 17:29:48 +0200 sipclient (0.2) unstable; urgency=low * Initial release -- Lucian Stanescu Mon, 13 Oct 2008 10:29:08 +0200 ================================================ FILE: debian/compat ================================================ 11 ================================================ FILE: debian/control ================================================ Source: python-sipsimple Section: python Priority: optional Maintainer: Dan Pascu Uploaders: Adrian Georgescu , Tijmen de Mes Build-Depends: debhelper (>= 11), dh-python, python-all-dev (>= 2.7), python-all-dbg (>= 2.7), cython-dbg (>= 0.19), libasound2-dev, libssl-dev, libv4l-dev, libavcodec-dev (>= 7:2.7), libavformat-dev (>= 7:2.7), libavutil-dev (>= 7:2.7), libswscale-dev (>= 7:2.7), libx264-dev, libvpx-dev, libsqlite3-dev, pkg-config, uuid-dev Standards-Version: 4.5.0 Homepage: http://sipsimpleclient.org Package: python-sipsimple Architecture: any Depends: ${python:Depends}, ${shlibs:Depends}, ${misc:Depends}, python-application (>= 2.8.0), python-dateutil, python-dnspython (>= 1.9), python-eventlib, python-gnutls, python-lxml, python-msrplib (>= 0.20.0), python-otr (>= 1.2.0), python-twisted-core (>= 8.1.0), python-xcaplib (>= 1.0.17) Suggests: libavahi-compat-libdnssd1 Provides: ${python:Provides} Description: Python SDK for development of SIP end-points SIP SIMPLE client SDK is a Software Development Kit for easy development of SIP end-points that support rich media like Audio, Video, Instant Messaging, File Transfers, Desktop Sharing and Presence. Other media types can be added by using an extensible high-level API. ================================================ FILE: debian/copyright ================================================ Copyright: 2008-2020 AG Projects License: GPL-3+ This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. For a copy of the license see /usr/share/common-licenses/GPL-3 ================================================ FILE: debian/docs ================================================ docs/DeveloperGuide.txt ================================================ FILE: debian/python-sipsimple.lintian-overrides ================================================ binary: possible-gpl-code-linked-with-openssl ================================================ FILE: debian/rules ================================================ #!/usr/bin/make -f export DEB_BUILD_HARDENING = 1 export PYBUILD_NAME = sipsimple %: dh $@ --with python2 --buildsystem=pybuild override_dh_clean: dh_clean rm -rf build dist MANIFEST ================================================ FILE: debian/source/format ================================================ 3.0 (native) ================================================ FILE: deps/pjsip/COPYING ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ================================================ FILE: deps/pjsip/Makefile ================================================ include build.mak include build/host-$(HOST_NAME).mak -include user.mak include version.mak LIB_DIRS = pjlib/build pjlib-util/build pjnath/build third_party/build pjmedia/build pjsip/build DIRS = $(LIB_DIRS) $(EXTRA_DIRS) ifdef MINSIZE MAKE_FLAGS := MINSIZE=1 endif all clean dep depend print: for dir in $(DIRS); do \ if $(MAKE) $(MAKE_FLAGS) -C $$dir $@; then \ true; \ else \ exit 1; \ fi; \ done distclean realclean: for dir in $(DIRS); do \ if $(MAKE) $(MAKE_FLAGS) -C $$dir $@; then \ true; \ else \ exit 1; \ fi; \ done $(HOST_RM) config.log $(HOST_RM) config.status lib: for dir in $(LIB_DIRS); do \ if $(MAKE) $(MAKE_FLAGS) -C $$dir all; then \ true; \ else \ exit 1; \ fi; \ done; \ .PHONY: lib doc doc: @if test \( ! "$(WWWDIR)" == "" \) -a \( ! -d $(WWWDIR)/pjlib/docs/html \) ; then \ echo 'Directory "$(WWWDIR)" does not look like a valid pjsip web directory'; \ exit 1; \ fi for dir in $(DIRS); do \ if $(MAKE) $(MAKE_FLAGS) -C $$dir $@; then \ true; \ else \ exit 1; \ fi; \ done LIBS = pjlib/lib/libpj-$(TARGET_NAME).a \ pjlib-util/lib/libpjlib-util-$(TARGET_NAME).a \ pjnath/lib/libpjnath-$(TARGET_NAME).a \ pjmedia/lib/libpjmedia-$(TARGET_NAME).a \ pjmedia/lib/libpjmedia-audiodev-$(TARGET_NAME).a \ pjmedia/lib/libpjmedia-codec-$(TARGET_NAME).a \ pjsip/lib/libpjsip-$(TARGET_NAME).a \ pjsip/lib/libpjsip-ua-$(TARGET_NAME).a \ pjsip/lib/libpjsip-simple-$(TARGET_NAME).a \ pjsip/lib/libpjsua-$(TARGET_NAME).a BINS = pjsip-apps/bin/pjsua-$(TARGET_NAME)$(HOST_EXE) size: @echo -n 'Date: ' @date @echo @for lib in $(LIBS); do \ echo "$$lib:"; \ size -t $$lib | awk '{print $$1 "\t" $$2 "\t" $$3 "\t" $$6}'; \ echo; \ done @echo @for bin in $(BINS); do \ echo "size $$bin:"; \ size $$bin; \ done #dos2unix: # for f in `find . | egrep '(mak|h|c|S|s|Makefile)$$'`; do \ # dos2unix "$$f" > dos2unix.tmp; \ # cp dos2unix.tmp "$$f"; \ # done # rm -f dos2unix.tmp xhdrid: for f in `find . | egrep '\.(h|c|S|s|cpp|hpp)$$'`; do \ echo Processing $$f...; \ cat $$f | sed 's/.*\$$Author\$$/ */' > /tmp/id; \ cp /tmp/id $$f; \ done selftest: pjlib-test pjlib-util-test pjnath-test pjmedia-test pjsip-test pjsua-test pjlib-test: pjlib/bin/pjlib-test-$(TARGET_NAME) cd pjlib/build && ../bin/pjlib-test-$(TARGET_NAME) pjlib-util-test: pjlib-util/bin/pjlib-util-test-$(TARGET_NAME) cd pjlib-util/build && ../bin/pjlib-util-test-$(TARGET_NAME) pjnath-test: pjnath/bin/pjnath-test-$(TARGET_NAME) cd pjnath/build && ../bin/pjnath-test-$(TARGET_NAME) pjmedia-test: pjmedia/bin/pjmedia-test-$(TARGET_NAME) cd pjmedia/build && ../bin/pjmedia-test-$(TARGET_NAME) pjsip-test: pjsip/bin/pjsip-test-$(TARGET_NAME) cd pjsip/build && ../bin/pjsip-test-$(TARGET_NAME) pjsua-test: cd tests/pjsua && python runall.py prefix = $(ac_prefix) install: mkdir -p $(DESTDIR)$(prefix)/lib cp -f $(APP_LIB_FILES) $(DESTDIR)$(prefix)/lib/ mkdir -p $(DESTDIR)$(prefix)/include for d in pjlib pjlib-util pjnath pjmedia pjsip; do \ cp -RLf $$d/include/* $(DESTDIR)$(prefix)/include/; \ done mkdir -p $(DESTDIR)$(prefix)/lib/pkgconfig sed -e "s!@PREFIX@!$(DESTDIR)$(prefix)!" libpjproject.pc.in | \ sed -e "s/@PJ_VERSION@/$(PJ_VERSION)/" | \ sed -e "s!@PJ_LDLIBS@!$(PJ_LDLIBS)!" | \ sed -e "s!@PJ_INSTALL_CFLAGS@!$(PJ_INSTALL_CFLAGS)!" > $(DESTDIR)/$(prefix)/lib/pkgconfig/libpjproject.pc ================================================ FILE: deps/pjsip/README.txt ================================================ Copyright (C) 2003-2008 Benny Prijono Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. Getting Started: Building and Using PJSIP and PJMEDIA [Last Update: $Date: 2007-02-02 20:42:44 +0000 (Fri, 02 Feb 2007) $] Print Friendly Page _________________________________________________________________ This article describes how to download, customize, build, and use the open source PJSIP and PJMEDIA SIP and media stack. The online (and HTML) version of this file can be downloaded from http://www.pjsip.org/using.htm Quick Info _________________________________________________________________ Building with GNU tools (Linux, *BSD, MacOS X, mingw, etc.) Generally these should be all that are needed to build the libraries, applications, and samples: $ ./configure $ make dep && make clean && make Building Win32 Target with Microsoft Visual Studio Generally we can just do these steps: 1. Visual Studio 6: open pjproject.dsw workspace, 2. Visual Studio 2005: open pjproject-vs8.sln solution, 3. Create an empty pjlib/include/pj/config_site.h, and 4. build the pjsua application. Building for Windows Mobile Generally these are all that are needed: 1. Open pjsip-apps/build/wince-evc4/wince_demos.vcw EVC4 workspace, 2. Create an empty pjlib/include/pj/config_site.h, and 3. build the pjsua_wince application. Invoking Older Build System (e.g. for RTEMS) Generally these should be all that are needed to build the libraries, applications, and samples: $ ./configure-legacy $ make dep && make clean && make Locating Output Binaries/Libraries Libraries will be put in lib directory, and binaries will be put in bin directory, under each projects. Running the Applications After successful build, you can try running pjsua application on pjsip-apps/bin directory. PJSUA manual can be found in http://www.pjsip.org/pjsua.htm page. Table of Contents: _________________________________________________________________ 1. Getting the Source Distribution 1.1 Getting the Release tarball 1.2 Getting from Subversion trunk 1.3 Source Directories Layout 2. Build Preparation 2.1 config_site.h file 2.2 Disk Space Requirements 3. Building Linux, *nix, *BSD, and MacOS X Targets with GNU Build Systems 3.1 Supported Targets 3.2 Requirements 3.3 Running configure 3.4 Running make 3.5 Cross Compilation 3.6 Build Customizations 4. Building for Windows Targets with Microsoft Visual Studio 4.1 Requirements 4.2 Building the Projects 4.3 Debugging the Sample Application 5. Building for Windows Mobile Targets (Windows CE/WinCE/PDA/SmartPhone) 5.1 Requirements 5.2 Building the Projects 6. Older PJLIB Build System for Non-Autoconf Targets (e.g. RTEMS) 6.1 Supported Targets 6.2 Invoking the Build System 7. Running the Applications 7.1 pjsua 7.2 Sample Applications 7.3 pjlib-test 7.4 pjsip-test 8. Using PJPROJECT with Applications Appendix I: Common Problems/Frequently Asked Question (FAQ) I.1 fatal error C1083: Cannot open include file: 'pj/config_site.h': No such file or directory 1. Getting the Source Code Distribution _________________________________________________________________ All libraries (PJLIB, PJLIB-UTIL, PJSIP, PJMEDIA, and PJMEDIA-CODEC) are currently distributed under a single source tree, collectively named as PJPROJECT or just PJ libraries. These libraries can be obtained by either downloading the release tarball or getting them from the Subversion trunk. 1.1 Getting the Release tarball _________________________________________________________________ Getting the released tarball is a convenient way to obtain stable version of PJPROJECT. The tarball may not contain the latest features or bug-fixes, but normally it is considered more stable as each will be tested more rigorously before released. The latest released tarball can be downloaded from the http://www.pjsip.org/download.htm. 1.2 Getting from Subversion trunk _________________________________________________________________ PJPROJECT Subversion repository will always contain the latest/most up-to-date version of the sources. Normally the Subversion repository is always kept in a "good" state. However, there's always a chance that things break and the tree doesn't build correctly (particularly for the "not-so-popular" targets), so please consult the mailing list should there be any problems. Using Subversion also has benefits of keeping the local copy of the source up to date with the main PJ source tree and to easily track the changes made to the local copy, if any. What is Subversion Subversion (SVN) is Open Source version control system similar to CVS. Subversion homepage is in http://subversion.tigris.org/ Getting Subversion Client A Subversion (SVN) client is needed to download the PJ source files from pjsip.org SVN tree. SVN client binaries can be downloaded from http://subversion.tigris.org/, and the program should be available for Windows, Linux, MacOS X, and many more platforms. Getting the Source for The First Time Once Subversion client is installed, we can use these commands to initially retrieve the latest sources from the Subversion trunk: $ svn co http://svn.pjproject.net/repos/pjproject/trunk pjproject $ cd pjproject Keeping The Local Copy Up-to-Date Once sources have been downloaded, we can keep the local copy up to date by periodically synchronizing the local source with the latest revision from the PJ's Subversion trunk. The mailing list provides best source of information about the availability of new updates in the trunk. To update the local copy with the latest changes in the main PJ's repository: $ cd pjproject $ svn update Tracking Local and Remote Changes To see what files have been changed locally: $ cd pjproject $ svn status The above command only compares local file against the original local copy, so it doesn't require Internet connection while performing the check. To see both what files have been changed locally and what files have been updated in the PJ's Subversion repository: $ cd pjproject $ svn status -u Note that this command requires active Internet connection to query the status of PJPROJECT's source repository. 1.3 Source Directories Layout _________________________________________________________________ Top-Level Directory Layout The top-level directories (denoted as $TOP here) in the source distribution contains the following sub-directories: $TOP/build Contains makefiles that are common for all projects. $TOP/pjlib Contains header and source files of PJLIB. PJLIB is the base portability and framework library which is used by all other libraries $TOP/pjlib-util Contains PJLIB-UTIL header and source files. PJLIB-UTIL is an auxiliary library that contains utility functions such as scanner, XML, STUN, MD5 algorithm, getopt() implementation, etc. $TOP/pjmedia Contains PJMEDIA and PJMEDIA-CODEC header and source files. The sources of various codecs (such as GSM, Speex, and iLBC) can be found under this directory. $TOP/pjsip Contains PJSIP header and source files. $TOP/pjsip-apps Contains source code for PJSUA and various sample applications. Individual Directory Inside Each Project Each library directory further contains these sub-directories: bin Contains binaries produced by the build process. build Contains build scripts/makefiles, project files, project workspace, etc. to build the project. In particular, it contains one Makefile file to build the project with GNU build systems, and a *.dsw workspace file to build the library with Microsoft Visual Studio 6 or later. build/output The build/output directory contains the object files and other files generated by the build process. To support building multiple targets with a single source tree, each build target will occupy a different subdirectory under this directory. build/wince-evc4 This directory contains the project/workspace files to build Windows CE/WinCE version of the project using Microsoft Embedded Visual C++ 4. build/wince-evc4/output This directory contains the library, executable, and object files generated by Windows Mobile build process. docs Contains Doxygen configuration file (doxygen.cfg) to generate online documentation from the source files. The output documentation will be put in this directory as well (for example, docs/html directory for the HTML files). (to generate Doxygen documentation from the source tree, just run "doxygen docs/doxygen.cfg" in the individual project directory. The generated files will reside in docs directory). include Contains the header files for the project. lib Contains libraries produced by the build process. src Contains the source files of the project. 2. Build Preparation _________________________________________________________________ 2.1 Create config_site.h file _________________________________________________________________ Before source files can be built, the pjlib/include/pj/config_site.h file must be created (it can just be an empty file). Note: When the Makefile based build system is used, this process is taken care by the Makefiles. But when non-Makefile based build system (such as Visual Studio) is used, the config_site.h file must be created manually. What is config_site.h File The pjlib/include/pj/config_site.h contains local customizations to the libraries. All customizations should be put in this file instead of modifying PJ's files, because if PJ's files get modified, then those modified files will not be updated the next time the source is synchronized. Or in other case, the local modification may be overwritten with the fresh copy from the SVN. Putting the local customization to the config_site.h solves this problem, because this file is not included in the version control, so it will never be overwritten by "svn update" command. Please find list of configuration macros that can be overriden from these files: * PJLIB Configuration (the pjlib/config.h file) * PJLIB-UTIL Configuration (the pjlib-util/config.h file) * PJMEDIA Configuration (the pjmedia/config.h file) * PJSIP Configuration (the pjsip/sip_config.h file) A sample config_site.h file is also available in pjlib/include/config_site_sample.h. Creating config_site.h file The simplest way is just to create an empty file, to use whetever default values set by the libraries. Another way to create the config_site.h file is to write something like the following: // Uncomment to get minimum footprint (suitable for 1-2 concurrent calls only) //#define PJ_CONFIG_MINIMAL_SIZE // Uncomment to get maximum performance //#define PJ_CONFIG_MAXIMUM_SPEED #include 2.2 Disk Space Requirements _________________________________________________________________ The building process needs: about 50-60 MB of disk space to store the uncompressed source files, and * about 30-50 MB of additional space for building each target (Visual Studio Debug and Release are considered as separate targets) 3. Building Linux, *nix, *BSD, and MacOS X Targets with GNU Build Systems _________________________________________________________________ 3.1 Supported Targets _________________________________________________________________ The new, autoconf based GNU build system can be used to build the libraries/applications for the following targets: * Linux/uC-Linux (i386, Opteron, Itanium, MIPS, PowerPC, etc.), * MacOS X (PowerPC), * mingw (i386), * FreeBSD and maybe other BSD's (i386, Opteron, etc.), * RTEMS with cross compilation (ARM, powerpc), * etc. 3.2 Requirements _________________________________________________________________ In order to use PJ's GNU build system, these typical GNU tools are needed: * GNU make (other make will not work), * GNU binutils for the target, and * GNU gcc for the target. * OpenSSL header files/libraries (optional) if TLS support is wanted. In addition, the appropriate "SDK" must be installed for the particular target (this could just be a libc and the appropriate system abstraction library such as Posix). The build system is known to work on the following hosts: * Linux, many types of distributions. * MacOS X 10.2 * mingw (Win2K, XP) * FreeBSD (must use gmake instead of make) Building Win32 applications with Cygwin is currently not supported by the autoconf script (there is some Windows header conflicts), but one can still use the old configure script by calling ./configure-legacy. More over, cross-compilations might also work with Cygwin. 3.3 Running configure _________________________________________________________________ Using Default Settings Run "./configure" without any options to let the script detect the appropriate settings for the host: $ cd pjproject $ ./configure ... Notes: The default settings build the libraries in "release" mode, with default CFLAGS set to "-O2 -DNDEBUG". To change the default CFLAGS, we can use the usual "./configure CFLAGS='-g'" construct. Features Customization With the new autoconf based build system, most configuration/customization can be specified as configure arguments. The list of customizable features can be viewed by running "./configure --help" command: $ cd pjproject $ ./configure --help ... Optional Features: --enable-epoll Use epoll on Linux instead of select --disable-floating-point Disable floating point where possible --disable-sound Exclude sound (i.e. use null sound) --disable-small-filter Exclude small filter in resampling --disable-large-filter Exclude large filter in resampling --disable-g711-plc Exclude G.711 Annex A PLC --disable-speex-aec Exclude Speex Acoustic Echo Canceller/AEC --disable-g711-codec Exclude G.711 codecs from the build --disable-l16-codec Exclude Linear/L16 codec family from the build --disable-gsm-codec Exclude GSM codec in the build --disable-speex-codec Exclude Speex codecs in the build --disable-ilbc-codec Exclude iLBC codec in the build --disable-tls Force excluding TLS support (default is autodetected based on OpenSSL availability) ... Configuring Debug Version and Other Customizations The configure script accepts standard customization, which details can be obtained by executing ./configure --help. Below is an example of specifying CFLAGS in configure: $ ./configure CFLAGS="-O3 -DNDEBUG -msoft-float -fno-builtin" ... Configuring TLS Support By default, TLS support is configured based on the availability of OpenSSL header files and libraries. If OpenSSL is available at the default include and library path locations, TLS will be enabled by the configure script. You can explicitly disable TLS support by giving the configure script --disable-tls option. 3.4 Cross Compilation _________________________________________________________________ Cross compilation should be supported, using the usual autoconf syntax: $ ./configure --host=arm-elf-linux ... Since cross-compilation is not tested as often as the "normal" build, please watch for the ./configure output for incorrect settings (well ideally this should be done for normal build too). Please refer to Porting Guide for further information about porting PJ software. 3.5 Running make _________________________________________________________________ Once the configure script completes successfully, start the build process by invoking these commands: $ cd pjproject $ make dep $ make Note: gmake may need to be specified instead of make for some hosts, to invoke GNU make instead of the native make. Description of all make targets supported by the Makefile's: all The default (or first) target to build the libraries/binaries. dep, depend Build dependencies rule from the source files. clean Clean the object files for current target, but keep the output library/binary files intact. distclean, realclean Remove all generated files (object, libraries, binaries, and dependency files) for current target. Note: make can be invoked either in the top-level PJ directory or in build directory under each project to build only the particular project. 3.6 Build Customizations _________________________________________________________________ Build features can be customized by specifying the options when running ./configure as described in Running Configure above. In addition, additional CFLAGS and LDFLAGS options can be put in user.mak file in PJ root directory (this file may need to be created if it doesn't exist). Below is a sample of user.mak file contents: export CFLAGS += -msoft-float -fno-builtin export LDFLAGS += 4. Building for Windows Targets with Microsoft Visual Studio _________________________________________________________________ 4.1 Requirements _________________________________________________________________ The Microsoft Visual Studio based project files can be used with one of the following: * Microsoft Visual Studio 6, * Microsoft Visual Studio .NET 2002, * Microsoft Visual Studio .NET 2003, * Microsoft Visual C++ 2005 (including Express edition), In addition, the following SDK's are needed: * Platform SDK, if you're using Visual Studio 2005 Express (tested with Platform SDK for Windows Server 2003 SP1), * DirectX SDK (tested with DirectX version 8 and 9), * OpenSSL development kit would be needed if TLS support is wanted, or otherwise this is optional. For the host, the following are required: * Windows NT, 2000, XP, 2003, or later , * Windows 95/98 should work too, but this has not been tested, * Sufficient amount of RAM for the build process (at least 256MB). Enabling TLS Support with OpenSSL If TLS support is wanted, then OpenSSL SDK must be installed in the development host. To install OpenSSL SDK from the Win32 binary distribution: 1. Install OpenSSL SDK to any folder (e.g. C:\OpenSSL) 2. Add OpenSSL DLL location to the system PATH. 3. Add OpenSSL include path to Visual Studio includes search directory. Make sure that OpenSSL header files can be accessed from the program with #include construct. 4. Add OpenSSL library path to Visual Studio library search directory. Make sure the following libraries are accessible: + For Debug build: libeay32MTd and ssleay32MTd. + For Release build: libeay32MT and ssleay32MT. Then to enable TLS transport support in PJSIP, just add #define PJSIP_HAS_TLS_TRANSPORT 1 in your pj/config_site.h. When this macro is defined, OpenSSL libraries will be automatically linked to the application via the #pragma construct in sip_transport_tls_ossl.c file. 4.2 Building the Projects _________________________________________________________________ Follow the steps below to build the libraries/application using Visual Studio: 1. For Visual Studio 6: open pjproject.dsw workspace file. 2. For Visual Studio 8 (VS 2005): open pjproject-vs8.sln solution file. 3. Set pjsua as Active Project. 4. Select Debug or Release build as appropriate. 5. Build the project. This will build pjsua application and all libraries needed by pjsua. 6. After successful build, the pjsua application will be placed in pjsip-apps/bin directory, and the libraries in lib directory under each projects. To build the samples: 1. (Still using the same workspace) 2. Set samples project as Active Project 3. Select Debug or Release build as appropriate. 4. Build the project. This will build all sample applications and all libraries needed. 5. After successful build, the sample applications will be placed in pjsip-apps/bin/samples directory, and the libraries in lib directory under each projects. 4.3 Debugging the Sample Application _________________________________________________________________ The sample applications are build using Samples.mak makefile, therefore it is difficult to setup debugging session in Visual Studio for these applications. To solve this issue, the pjsip_apps workspace contain one project called sample_debug which can be used to debug the sample application. To setup debugging using sample_debug project: 1. (Still using pjsip_apps workspace) 2. Set sample_debug project as Active Project 3. Edit debug.c file inside this project. 4. Modify the #include line to include the particular sample application to debug 5. Select Debug build. 6. Build and debug the project. 5. Building for Windows Mobile Targets (Windows CE/WinCE/PDA/SmartPhone) _________________________________________________________________ PJ supports building SIP and media stacks and applications for Windows Mobile targets. A very simple WinCE SIP user agent (with media) application is provided just as proof of concept that the port works. 5.1 Requirements _________________________________________________________________ One of the following development tools is needed to build SIP and media components for Windows Mobile: * Microsoft Embedded Visual C++ 4 with appropriate SDKs, or * Microsoft Visual Studio 2005 for Windows Mobile with appropriate SDKs. Note that VS2005 is not directly supported (as I don't have the tools), but it is reported to work (I assumed that VS2005 for Windows Mobile can import EVC4 workspace file). 5.2 Building the Projects _________________________________________________________________ The Windows Mobile port is included in the main source distribution. Please follow the following steps to build the WinCE libraries and sample application: 1. Open pjsip-apps/build/wince-evc4/wince_demos.vcw workspace file. If later version of EVC4 is being used, this may cause the workspace file to be converted to the appropriate format. 2. Select pjsua_wince project as the Active Project. 3. Select the appropriate SDK (for example Pocket PC 2003 SDK or SmartPhone 2003 SDK) 4. Select the appropriate configuration (for example, Win32 (WCE Emulator Debug) to debug the program in emulator, or other configurations such as ARMV4, MIPS, SH3, SH4, or whatever suitable for the device) 5. Select the appropriate device (Emulator or the actual Device). 6. Build the project. This will build the sample WinCE application and all libraries (SIP, Media, etc.) needed by this application. Notes + If the config_site.h includes config_site_sample.h file, then there are certain configuration in config_site_sample.h that get activated for Windows CE targets. Please make sure that these configurations are suitable for the application. + The libraries, binaries and object files produced by the build process are located under build/wince-evc4/output directory of each projects. 6. Older PJLIB Build System for Non-Autoconf Targets (e.g. RTEMS) _________________________________________________________________ The old PJLIB build system can still be used for building PJ libraries, for example for RTEMS target. Please see the Porting PJLIB page in PJLIB Reference documentation for information on how to support new target using this build system. 6.1 Supported Targets _________________________________________________________________ The older build system supports building PJ libraries for the following operating systems: * RTEMS * Linux * MacOS X * Cygwin and Mingw And it supports the following target architectures: * i386, x86_64, itanium * ARM * mips * powerpc * mpc860 * etc. For other targets, specific files need to be added to the build system, please see the Porting PJLIB page in PJLIB Reference documentation for details. 6.2 Invoking the Build System _________________________________________________________________ To invoke the older build system, run the following: $ cd pjproject $ ./configure-legacy $ make dep && make clean && make 7. Running the Applications _________________________________________________________________ Upon successful build, the output libraries (PJLIB, PJLIB-UTIL, PJMEDIA, PJSIP, etc.) are put under ./lib sub-directory under each project directory. In addition, some applications may also be built, and such applications will be put in ./bin sub-directory under each project directory. 7.1 pjsua _________________________________________________________________ pjsua is the reference implementation for both PJSIP and PJMEDIA stack, and is the main target of the build system. Upon successful build, pjsua application will be put in pjsip-apps/bin directory. pjsua manual can be found in pjsua Manual Page. 7.2 Sample Applications _________________________________________________________________ Sample applications will be built with the Makefile build system. For Visual Studio, you have to build the samples manually by selecting and building the Samples project inside pjsip-apps/build/pjsip_apps.dsw project workspace. Upon successful build, the sample applications are put in pjsip-apps/bin/samples directory. The sample applications are described in PJMEDIA Samples Page and PJSIP Samples Page in the website. 7.3 pjlib-test _________________________________________________________________ pjlib-test contains comprehensive tests for testing PJLIB functionality. This application will only be built when the Makefile build system is used; with Visual Studio, one has to open pjlib.dsw project in pjlib/build directory to build this application. If you're porting PJLIB to new target, it is recommended to run this application to make sure that all functionalities works as expected. 7.4 pjsip-test _________________________________________________________________ pjsip-test contains codes for testing various SIP functionalities in PJSIP and also to benchmark static performance metrics such as message parsing per second. 8. Using PJPROJECT with Applications _________________________________________________________________ Regardless of the build system being used, the following tasks are normally needed to be done in order to build application to use PJSIP and PJMEDIA: 1. Put these include directories in the include search path: + pjlib/include + pjlib-util/include + pjmedia/include + pjsip/include 2. Put these library directories in the library search path: + pjlib/lib + pjlib-util/lib + pjmedia/lib + pjsip/lib 3. Include the relevant PJ header files in the application source file. For example, using these would include ALL APIs exported by PJ: #include #include #include #include #include #include #include #include (Note: the documentation of the relevant libraries should say which header files should be included to get the declaration of the APIs). 4. Declare the OS macros. + For Windows applications built with Visual Studio, we need to declare PJ_WIN32=1 macro in the project settings (declaring the macro in the source file may not be sufficient). + For Windows Mobile applications build with Visual C++, we need to declare PJ_WIN32_WINCE=1 macro in the project settings. + For GNU build system/autoconf based build system, we need to declare PJ_AUTOCONF=1 macro when compiling the applications. (Note: the old PJ build system requires declaring the target processor with PJ_M_XXX=1 macro, but this has been made obsolete. The target processor will be detected from compiler's predefined macro by pjlib/config.h file). 5. Link with the appropriate PJ libraries. The following libraries will need to be included in the library link specifications: pjlib Base library used by all libraries. pjlib-util Auxiliary library containing scanner, XML, STUN, MD5, getopt, etc, used by the SIP and media stack. pjsip SIP core stack library. pjsip-ua SIP user agent library containing INVITE session, call transfer, client registration, etc. pjsip-simple SIP SIMPLE library for base event framework, presence, instant messaging, etc. pjsua High level SIP UA library, combining SIP and media stack into high-level easy to use API. pjmedia The media framework. pjmedia-codec Container library for various codecs such as GSM, Speex, and iLBC. Note: the actual library names will be appended with the target name and the build configuration. For example: For Visual Studio builds The actual library names will look like pjlib-i386-win32-vc6-debug.lib, pjlib-i386-win32-vc6-release.lib, etc., depending on whether we are building the Debug or Release version of the library. An easier way to link with the libraries is to include PJ project files in the workspace, and to configure project dependencies so that the application depends on the PJ libraries. This way, we don't need to manually add each PJ libraries to the input library file specification, since VS will automatically link the dependency libraries with the application. For Windows Mobile builds Unfortunately the PJ libraries built for Windows Mobile will not be placed in the usual lib directory, but rather under the output directory under build/wince-evc4 project directory. An easier way to link with the libraries is to include PJ project files in the workspace, and to configure project dependencies so that the application depends on the PJ libraries. This way, we don't need to manually add each PJ libraries to the input library file specification, since VS will automatically link the dependency libraries with the application. For GNU builds Application's Makefile can get the PJ library suffix by including PJ's build.mak file from the root PJ directory (the suffix is contained in TARGET_NAME variable). For example, to link with PJLIB and PJMEDIA, we can use this syntax in the LDFLAGS: "-lpj-$(TARGET_NAME) -lpjmedia-$(TARGET_NAME)" 6. Link with system spesific libraries: Windows Add (among other things): wsock32.lib, ws2_32.lib, ole32.lib, dsound.lib Linux, *nix, *BSD Add (among other things): '-lpthread -lm' (at least). MacOS X Add (among other things): '-framework CoreAudio -lpthread -lm'. Appendix I: Common Problems/Frequently Asked Question (FAQ) _________________________________________________________________ I.1 fatal error C1083: Cannot open include file: 'pj/config_site.h': No such file or directory This error normally occurs when the config_site.h file has not been created. This file needs to be created manually (an empty file is sufficient). Please follow the Build Preparation instructions above to create this file. _________________________________________________________________ Feedback: Thanks for using PJ libraries and for reading this document. Please send feedbacks or general comments to . ================================================ FILE: deps/pjsip/aconfigure ================================================ #!/bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for pjproject 2.x. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='pjproject' PACKAGE_TARNAME='pjproject' PACKAGE_VERSION='2.x' PACKAGE_STRING='pjproject 2.x' PACKAGE_BUGREPORT='' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='LTLIBOBJS LIBOBJS ac_main_obj ac_host libcrypto_present libssl_present openssl_h_present ac_no_ssl ac_webrtc_platform ac_libyuv_ldflags ac_libyuv_cflags ac_openh264_ldflags ac_openh264_cflags ac_v4l2_ldflags ac_v4l2_cflags ac_vpx_ldflags ac_vpx_cflags SAVED_PKG_CONFIG_PATH ac_ffmpeg_ldflags ac_ffmpeg_cflags PKG_CONFIG ac_resample_dll ac_no_g7221_codec ac_no_speex_aec ac_no_large_filter ac_no_small_filter ac_dshow_ldflags ac_dshow_cflags ac_pjmedia_video_has_dshow ac_avf_cflags ac_pjmedia_video_has_avf ac_ios_cflags ac_pjmedia_video_has_ios ac_pjmedia_video ac_pjmedia_audiodev_objs ac_linux_poll ac_os_objs EGREP GREP CPP ac_cross_compile ac_shlib_suffix ac_build_mak_vars ac_pjdir CC_CFLAGS CC_OPTIMIZE CC_DEF CC_INC CC_OUT LIBEXT2 LIBEXT LDOUT LD AR_FLAGS ac_ct_AR AR RANLIB ac_ct_CXX CXXFLAGS CXX OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC target_os target_vendor target_cpu target host_os host_vendor host_cpu host build_os build_vendor build_cpu build target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_epoll enable_small_filter enable_large_filter enable_speex_aec enable_g7221_codec enable_libsamplerate enable_resample_dll with_ffmpeg with_vpx enable_v4l2 with_openh264 enable_openh264 with_libyuv enable_libyuv with_ssl enable_ssl ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CXX CXXFLAGS CCC CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures pjproject 2.x to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/pjproject] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] --target=TARGET configure for building compilers for TARGET [HOST] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of pjproject 2.x:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-epoll Use /dev/epoll ioqueue on Linux (experimental) --disable-small-filter Exclude small filter in resampling --disable-large-filter Exclude large filter in resampling --disable-speex-aec Exclude Speex Acoustic Echo Canceller/AEC --disable-g7221-codec Exclude G.7221 codec in the build --enable-libsamplerate Link with libsamplerate when available. Note that PJMEDIA_RESAMPLE_IMP must also be configured --enable-resample-dll Build libresample as shared library --disable-v4l2 Disable Video4Linux2 (default: not disabled) --disable-openh264 Disable OpenH264 (default: not disabled) --disable-libyuv Exclude libyuv in the build --disable-ssl Exclude SSL support the build (default: autodetect) Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-ffmpeg=DIR Specify alternate FFMPEG prefix --with-vpx=DIR Specify alternate libVPX prefix --with-openh264=DIR Specify alternate OpenH264 prefix --with-libyuv=DIR Specify alternate libyuv prefix --with-ssl=DIR Specify alternate libssl prefix Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CXX C++ compiler command CXXFLAGS C++ compiler flags CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF pjproject configure 2.x generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_cxx_try_compile LINENO # ---------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by pjproject $as_me 2.x, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu host_orig="$host" ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 $as_echo_n "checking target system type... " >&6; } if ${ac_cv_target+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$target_alias" = x; then ac_cv_target=$ac_cv_host else ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 $as_echo "$ac_cv_target" >&6; } case $ac_cv_target in *-*-*) ;; *) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;; esac target=$ac_cv_target ac_save_IFS=$IFS; IFS='-' set x $ac_cv_target shift target_cpu=$1 target_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: target_os=$* IFS=$ac_save_IFS case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac # The aliases save the names the user supplied, while $host etc. # will get canonicalized. test -n "$target_alias" && test "$program_prefix$program_suffix$program_transform_name" = \ NONENONEs,x,x, && program_prefix=${target_alias}- ac_config_headers="$ac_config_headers pjlib/include/pj/compat/os_auto.h pjlib/include/pj/compat/m_auto.h pjmedia/include/pjmedia/config_auto.h pjmedia/include/pjmedia-codec/config_auto.h pjsip/include/pjsip/sip_autoconf.h" ac_config_files="$ac_config_files build.mak build/os-auto.mak build/cc-auto.mak pjlib/build/os-auto.mak pjmedia/build/os-auto.mak pjsip/build/os-auto.mak third_party/build/webrtc/os-auto.mak third_party/build/os-auto.mak" if test "$CFLAGS" = ""; then CFLAGS="-O2" else CFLAGS="$CFLAGS" fi CXXFLAGS="$CFLAGS $CXXFLAGS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 $as_echo "$CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 $as_echo "$ac_ct_CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } if ${ac_cv_cxx_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 $as_echo "$ac_cv_cxx_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } if ${ac_cv_prog_cxx_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 $as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi if test -n "$ac_tool_prefix"; then for ac_prog in ar gar do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar gar do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi if test "$AR_FLAGS" = ""; then AR_FLAGS="rv"; fi if test "$LD" = ""; then LD="$CC"; fi if test "$LDOUT" = ""; then LDOUT="-o "; fi if test "$OBJEXT" = ""; then OBJEXT='o'; fi if test "$LIBEXT" = ""; then LIBEXT='a'; fi if test "$LIBEXT2" = ""; then LIBEXT2=""; fi if test "$CC_OUT" = ""; then CC_OUT="-o "; fi if test "$CC_INC" = ""; then CC_INC="-I"; fi if test "$CC_DEF" = ""; then CC_DEF="-D"; fi if test "$CC_OPTIMIZE" = ""; then CC_OPTIMIZE="-O2"; fi if test "$CC_CFLAGS" = ""; then CC_CFLAGS="-Wall"; fi case $host in *mingw* | *cygw* | *win32* | *w32* ) if pwd -W 2&> /dev/null; then ac_pjdir=`pwd -W` else # We're probably cross-compiling mingw on Linux ac_pjdir=`pwd` fi ;; *) ac_pjdir=`pwd` ;; esac case $target in *mingw* | *cygw* | *win32* | *w32* ) ac_shlib_suffix=dll ;; *darwin*) ac_shlib_suffix=dylib ;; *) ac_shlib_suffix=so ;; esac if test "$build" = "$host"; then ac_cross_compile= else ac_cross_compile=${host_orig}- fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5 $as_echo_n "checking for pthread_create in -lpthread... " >&6; } if ${ac_cv_lib_pthread_pthread_create+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_create (); int main () { return pthread_create (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pthread_pthread_create=yes else ac_cv_lib_pthread_pthread_create=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5 $as_echo "$ac_cv_lib_pthread_pthread_create" >&6; } if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBPTHREAD 1 _ACEOF LIBS="-lpthread $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for puts in -lwsock32" >&5 $as_echo_n "checking for puts in -lwsock32... " >&6; } if ${ac_cv_lib_wsock32_puts+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lwsock32 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char puts (); int main () { return puts (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_wsock32_puts=yes else ac_cv_lib_wsock32_puts=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_wsock32_puts" >&5 $as_echo "$ac_cv_lib_wsock32_puts" >&6; } if test "x$ac_cv_lib_wsock32_puts" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBWSOCK32 1 _ACEOF LIBS="-lwsock32 $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for puts in -lws2_32" >&5 $as_echo_n "checking for puts in -lws2_32... " >&6; } if ${ac_cv_lib_ws2_32_puts+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lws2_32 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char puts (); int main () { return puts (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ws2_32_puts=yes else ac_cv_lib_ws2_32_puts=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ws2_32_puts" >&5 $as_echo "$ac_cv_lib_ws2_32_puts" >&6; } if test "x$ac_cv_lib_ws2_32_puts" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBWS2_32 1 _ACEOF LIBS="-lws2_32 $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for puts in -lole32" >&5 $as_echo_n "checking for puts in -lole32... " >&6; } if ${ac_cv_lib_ole32_puts+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lole32 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char puts (); int main () { return puts (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ole32_puts=yes else ac_cv_lib_ole32_puts=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ole32_puts" >&5 $as_echo "$ac_cv_lib_ole32_puts" >&6; } if test "x$ac_cv_lib_ole32_puts" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBOLE32 1 _ACEOF LIBS="-lole32 $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for puts in -lwinmm" >&5 $as_echo_n "checking for puts in -lwinmm... " >&6; } if ${ac_cv_lib_winmm_puts+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lwinmm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char puts (); int main () { return puts (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_winmm_puts=yes else ac_cv_lib_winmm_puts=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_winmm_puts" >&5 $as_echo "$ac_cv_lib_winmm_puts" >&6; } if test "x$ac_cv_lib_winmm_puts" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBWINMM 1 _ACEOF LIBS="-lwinmm $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for puts in -lsocket" >&5 $as_echo_n "checking for puts in -lsocket... " >&6; } if ${ac_cv_lib_socket_puts+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char puts (); int main () { return puts (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_socket_puts=yes else ac_cv_lib_socket_puts=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_puts" >&5 $as_echo "$ac_cv_lib_socket_puts" >&6; } if test "x$ac_cv_lib_socket_puts" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBSOCKET 1 _ACEOF LIBS="-lsocket $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for puts in -lrt" >&5 $as_echo_n "checking for puts in -lrt... " >&6; } if ${ac_cv_lib_rt_puts+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lrt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char puts (); int main () { return puts (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_rt_puts=yes else ac_cv_lib_rt_puts=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_puts" >&5 $as_echo "$ac_cv_lib_rt_puts" >&6; } if test "x$ac_cv_lib_rt_puts" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBRT 1 _ACEOF LIBS="-lrt $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sin in -lm" >&5 $as_echo_n "checking for sin in -lm... " >&6; } if ${ac_cv_lib_m_sin+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char sin (); int main () { return sin (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_m_sin=yes else ac_cv_lib_m_sin=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_sin" >&5 $as_echo "$ac_cv_lib_m_sin" >&6; } if test "x$ac_cv_lib_m_sin" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBM 1 _ACEOF LIBS="-lm $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uuid_generate in -luuid" >&5 $as_echo_n "checking for uuid_generate in -luuid... " >&6; } if ${ac_cv_lib_uuid_uuid_generate+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-luuid $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char uuid_generate (); int main () { return uuid_generate (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_uuid_uuid_generate=yes else ac_cv_lib_uuid_uuid_generate=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_uuid_uuid_generate" >&5 $as_echo "$ac_cv_lib_uuid_uuid_generate" >&6; } if test "x$ac_cv_lib_uuid_uuid_generate" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBUUID 1 _ACEOF LIBS="-luuid $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uuid_generate in -luuid" >&5 $as_echo_n "checking for uuid_generate in -luuid... " >&6; } if ${ac_cv_lib_uuid_uuid_generate+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-luuid $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char uuid_generate (); int main () { return uuid_generate (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_uuid_uuid_generate=yes else ac_cv_lib_uuid_uuid_generate=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_uuid_uuid_generate" >&5 $as_echo "$ac_cv_lib_uuid_uuid_generate" >&6; } if test "x$ac_cv_lib_uuid_uuid_generate" = xyes; then : ac_has_uuid_lib=1 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gethostbyname" >&5 $as_echo_n "checking for library containing gethostbyname... " >&6; } if ${ac_cv_search_gethostbyname+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char gethostbyname (); int main () { return gethostbyname (); ; return 0; } _ACEOF for ac_lib in '' nsl; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_gethostbyname=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_gethostbyname+:} false; then : break fi done if ${ac_cv_search_gethostbyname+:} false; then : else ac_cv_search_gethostbyname=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostbyname" >&5 $as_echo "$ac_cv_search_gethostbyname" >&6; } ac_res=$ac_cv_search_gethostbyname if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: Setting PJ_M_NAME to $target_cpu" >&5 $as_echo "Setting PJ_M_NAME to $target_cpu" >&6; } cat >>confdefs.h <<_ACEOF #define PJ_M_NAME "$target_cpu" _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: checking memory alignment" >&5 $as_echo_n "checking memory alignment... " >&6; } case $target in sparc64-* | ia64-* | x86_64-* ) $as_echo "#define PJ_POOL_ALIGNMENT 8" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: 8 bytes" >&5 $as_echo "8 bytes" >&6; } ;; * ) $as_echo "#define PJ_POOL_ALIGNMENT 4" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: 4 bytes (default)" >&5 $as_echo "4 bytes (default)" >&6; } ;; esac ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 $as_echo_n "checking whether byte ordering is bigendian... " >&6; } if ${ac_cv_c_bigendian+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes; then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main () { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_c_bigendian=no else ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 $as_echo "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac if test "x$ac_cv_c_bigendian" = "xyes"; then CFLAGS="$CFLAGS -DPJ_IS_BIG_ENDIAN=1 -DPJ_IS_LITTLE_ENDIAN=0" else CFLAGS="$CFLAGS -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1" fi case $target in *android*) $as_echo "#define PJ_ANDROID 1" >>confdefs.h ;; *mingw* | *cygw* | *win32* | *w32* ) $as_echo "#define PJ_WIN32 1" >>confdefs.h $as_echo "#define PJ_WIN32_WINNT 0x0400" >>confdefs.h $as_echo "#define WIN32_LEAN_AND_MEAN 1" >>confdefs.h ;; *darwin*) $as_echo "#define PJ_DARWINOS 1" >>confdefs.h ;; *linux*) $as_echo "#define PJ_LINUX 1" >>confdefs.h ;; *rtems*) $as_echo "#define PJ_RTEMS 1" >>confdefs.h ;; *sunos* | *solaris* ) $as_echo "#define PJ_SUNOS 1" >>confdefs.h ;; *) ;; esac ac_fn_c_check_header_mongrel "$LINENO" "arpa/inet.h" "ac_cv_header_arpa_inet_h" "$ac_includes_default" if test "x$ac_cv_header_arpa_inet_h" = xyes; then : $as_echo "#define PJ_HAS_ARPA_INET_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "assert.h" "ac_cv_header_assert_h" "$ac_includes_default" if test "x$ac_cv_header_assert_h" = xyes; then : $as_echo "#define PJ_HAS_ASSERT_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "ctype.h" "ac_cv_header_ctype_h" "$ac_includes_default" if test "x$ac_cv_header_ctype_h" = xyes; then : $as_echo "#define PJ_HAS_CTYPE_H 1" >>confdefs.h fi case $target in *mingw* | *cygw* | *win32* | *w32* ) $as_echo "#define PJ_HAS_ERRNO_H 0" >>confdefs.h ;; *) ac_fn_c_check_header_mongrel "$LINENO" "errno.h" "ac_cv_header_errno_h" "$ac_includes_default" if test "x$ac_cv_header_errno_h" = xyes; then : $as_echo "#define PJ_HAS_ERRNO_H 1" >>confdefs.h fi ;; esac ac_fn_c_check_header_mongrel "$LINENO" "fcntl.h" "ac_cv_header_fcntl_h" "$ac_includes_default" if test "x$ac_cv_header_fcntl_h" = xyes; then : $as_echo "#define PJ_HAS_FCNTL_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "linux/socket.h" "ac_cv_header_linux_socket_h" "$ac_includes_default" if test "x$ac_cv_header_linux_socket_h" = xyes; then : $as_echo "#define PJ_HAS_LINUX_SOCKET_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default" if test "x$ac_cv_header_limits_h" = xyes; then : $as_echo "#define PJ_HAS_LIMITS_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "malloc.h" "ac_cv_header_malloc_h" "$ac_includes_default" if test "x$ac_cv_header_malloc_h" = xyes; then : $as_echo "#define PJ_HAS_MALLOC_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "netdb.h" "ac_cv_header_netdb_h" "$ac_includes_default" if test "x$ac_cv_header_netdb_h" = xyes; then : $as_echo "#define PJ_HAS_NETDB_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "netinet/in_systm.h" "ac_cv_header_netinet_in_systm_h" "$ac_includes_default" if test "x$ac_cv_header_netinet_in_systm_h" = xyes; then : $as_echo "#define PJ_HAS_NETINET_IN_SYSTM_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "netinet/in.h" "ac_cv_header_netinet_in_h" "$ac_includes_default" if test "x$ac_cv_header_netinet_in_h" = xyes; then : $as_echo "#define PJ_HAS_NETINET_IN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "netinet/ip.h" "ac_cv_header_netinet_ip_h" "#if PJ_HAS_SYS_TYPES_H # include #endif #if PJ_HAS_NETINET_IN_SYSTM_H # include #endif #if PJ_HAS_NETINET_IN_H # include #endif " if test "x$ac_cv_header_netinet_ip_h" = xyes; then : $as_echo "#define PJ_HAS_NETINET_IP_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "netinet/tcp.h" "ac_cv_header_netinet_tcp_h" "$ac_includes_default" if test "x$ac_cv_header_netinet_tcp_h" = xyes; then : $as_echo "#define PJ_HAS_NETINET_TCP_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "ifaddrs.h" "ac_cv_header_ifaddrs_h" "$ac_includes_default" if test "x$ac_cv_header_ifaddrs_h" = xyes; then : ac_fn_c_check_func "$LINENO" "getifaddrs" "ac_cv_func_getifaddrs" if test "x$ac_cv_func_getifaddrs" = xyes; then : $as_echo "#define PJ_HAS_IFADDRS_H 1" >>confdefs.h fi fi ac_fn_c_check_header_mongrel "$LINENO" "semaphore.h" "ac_cv_header_semaphore_h" "$ac_includes_default" if test "x$ac_cv_header_semaphore_h" = xyes; then : $as_echo "#define PJ_HAS_SEMAPHORE_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "setjmp.h" "ac_cv_header_setjmp_h" "$ac_includes_default" if test "x$ac_cv_header_setjmp_h" = xyes; then : $as_echo "#define PJ_HAS_SETJMP_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "stdarg.h" "ac_cv_header_stdarg_h" "$ac_includes_default" if test "x$ac_cv_header_stdarg_h" = xyes; then : $as_echo "#define PJ_HAS_STDARG_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "stddef.h" "ac_cv_header_stddef_h" "$ac_includes_default" if test "x$ac_cv_header_stddef_h" = xyes; then : $as_echo "#define PJ_HAS_STDDEF_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "stdio.h" "ac_cv_header_stdio_h" "$ac_includes_default" if test "x$ac_cv_header_stdio_h" = xyes; then : $as_echo "#define PJ_HAS_STDIO_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default" if test "x$ac_cv_header_stdint_h" = xyes; then : $as_echo "#define PJ_HAS_STDINT_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" if test "x$ac_cv_header_stdlib_h" = xyes; then : $as_echo "#define PJ_HAS_STDLIB_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "string.h" "ac_cv_header_string_h" "$ac_includes_default" if test "x$ac_cv_header_string_h" = xyes; then : $as_echo "#define PJ_HAS_STRING_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "sys/ioctl.h" "ac_cv_header_sys_ioctl_h" "$ac_includes_default" if test "x$ac_cv_header_sys_ioctl_h" = xyes; then : $as_echo "#define PJ_HAS_SYS_IOCTL_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "sys/select.h" "ac_cv_header_sys_select_h" "$ac_includes_default" if test "x$ac_cv_header_sys_select_h" = xyes; then : $as_echo "#define PJ_HAS_SYS_SELECT_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "sys/socket.h" "ac_cv_header_sys_socket_h" "$ac_includes_default" if test "x$ac_cv_header_sys_socket_h" = xyes; then : $as_echo "#define PJ_HAS_SYS_SOCKET_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "sys/time.h" "ac_cv_header_sys_time_h" "$ac_includes_default" if test "x$ac_cv_header_sys_time_h" = xyes; then : $as_echo "#define PJ_HAS_SYS_TIME_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "sys/timeb.h" "ac_cv_header_sys_timeb_h" "$ac_includes_default" if test "x$ac_cv_header_sys_timeb_h" = xyes; then : $as_echo "#define PJ_HAS_SYS_TIMEB_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default" if test "x$ac_cv_header_sys_types_h" = xyes; then : $as_echo "#define PJ_HAS_SYS_TYPES_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "sys/filio.h" "ac_cv_header_sys_filio_h" "$ac_includes_default" if test "x$ac_cv_header_sys_filio_h" = xyes; then : $as_echo "#define PJ_HAS_SYS_FILIO_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "sys/sockio.h" "ac_cv_header_sys_sockio_h" "$ac_includes_default" if test "x$ac_cv_header_sys_sockio_h" = xyes; then : $as_echo "#define PJ_HAS_SYS_SOCKIO_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "sys/utsname.h" "ac_cv_header_sys_utsname_h" "$ac_includes_default" if test "x$ac_cv_header_sys_utsname_h" = xyes; then : $as_echo "#define PJ_HAS_SYS_UTSNAME_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "time.h" "ac_cv_header_time_h" "$ac_includes_default" if test "x$ac_cv_header_time_h" = xyes; then : $as_echo "#define PJ_HAS_TIME_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default" if test "x$ac_cv_header_unistd_h" = xyes; then : $as_echo "#define PJ_HAS_UNISTD_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "winsock.h" "ac_cv_header_winsock_h" "$ac_includes_default" if test "x$ac_cv_header_winsock_h" = xyes; then : $as_echo "#define PJ_HAS_WINSOCK_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "winsock2.h" "ac_cv_header_winsock2_h" "$ac_includes_default" if test "x$ac_cv_header_winsock2_h" = xyes; then : $as_echo "#define PJ_HAS_WINSOCK2_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "mswsock.h" "ac_cv_header_mswsock_h" "#if PJ_HAS_WINSOCK2_H # include #elif PJ_HAS_WINSOCK_H # include #endif " if test "x$ac_cv_header_mswsock_h" = xyes; then : $as_echo "#define PJ_HAS_MSWSOCK_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "ws2tcpip.h" "ac_cv_header_ws2tcpip_h" "$ac_includes_default" if test "x$ac_cv_header_ws2tcpip_h" = xyes; then : $as_echo "#define PJ_HAS_WS2TCPIP_H 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "uuid/uuid.h" "ac_cv_header_uuid_uuid_h" "$ac_includes_default" if test "x$ac_cv_header_uuid_uuid_h" = xyes; then : ac_has_uuid_h=1 fi ac_fn_c_check_header_compile "$LINENO" "net/if.h" "ac_cv_header_net_if_h" "#if PJ_HAS_SYS_TYPES_H # include #endif #if PJ_HAS_SYS_SOCKET_H # include #endif " if test "x$ac_cv_header_net_if_h" = xyes; then : $as_echo "#define PJ_HAS_NET_IF_H 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: Setting PJ_OS_NAME to $target" >&5 $as_echo "Setting PJ_OS_NAME to $target" >&6; } cat >>confdefs.h <<_ACEOF #define PJ_OS_NAME "$target" _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: result: Setting PJ_HAS_ERRNO_VAR to 1" >&5 $as_echo "Setting PJ_HAS_ERRNO_VAR to 1" >&6; } $as_echo "#define PJ_HAS_ERRNO_VAR 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: Setting PJ_HAS_HIGH_RES_TIMER to 1" >&5 $as_echo "Setting PJ_HAS_HIGH_RES_TIMER to 1" >&6; } $as_echo "#define PJ_HAS_HIGH_RES_TIMER 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: Setting PJ_HAS_MALLOC to 1" >&5 $as_echo "Setting PJ_HAS_MALLOC to 1" >&6; } $as_echo "#define PJ_HAS_MALLOC 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: Setting PJ_NATIVE_STRING_IS_UNICODE to 0" >&5 $as_echo "Setting PJ_NATIVE_STRING_IS_UNICODE to 0" >&6; } $as_echo "#define PJ_NATIVE_STRING_IS_UNICODE 0" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: Setting PJ_ATOMIC_VALUE_TYPE to long" >&5 $as_echo "Setting PJ_ATOMIC_VALUE_TYPE to long" >&6; } $as_echo "#define PJ_ATOMIC_VALUE_TYPE long" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: checking if inet_aton() is available" >&5 $as_echo_n "checking if inet_aton() is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { inet_aton(0, 0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define PJ_SOCK_HAS_INET_ATON 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: checking if inet_pton() is available" >&5 $as_echo_n "checking if inet_pton() is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { inet_pton(0, 0, 0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define PJ_SOCK_HAS_INET_PTON 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: checking if inet_ntop() is available" >&5 $as_echo_n "checking if inet_ntop() is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { inet_ntop(0, 0, 0, 0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define PJ_SOCK_HAS_INET_NTOP 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: checking if getaddrinfo() is available" >&5 $as_echo_n "checking if getaddrinfo() is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { getaddrinfo(0, 0, 0, 0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define PJ_SOCK_HAS_GETADDRINFO 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: checking if sockaddr_in has sin_len member" >&5 $as_echo_n "checking if sockaddr_in has sin_len member... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { struct sockaddr_in a; a.sin_len=0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define PJ_SOCKADDR_HAS_LEN 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: checking if socklen_t is available" >&5 $as_echo_n "checking if socklen_t is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { socklen_t xxx = 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define PJ_HAS_SOCKLEN_T 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: checking if SO_ERROR is available" >&5 $as_echo_n "checking if SO_ERROR is available... " >&6; } case $target in *mingw* | *cygw* | *win32* | *w32* ) $as_echo "#define PJ_HAS_SO_ERROR 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } ;; *) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { int i=SO_ERROR; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define PJ_HAS_SO_ERROR 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking if pthread_rwlock_t is available" >&5 $as_echo_n "checking if pthread_rwlock_t is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { pthread_rwlock_t *x; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define PJ_EMULATE_RWMUTEX 0" >>confdefs.h ac_rwmutex="yes" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else $as_echo "#define PJ_EMULATE_RWMUTEX 1" >>confdefs.h ac_rwmutex="no" { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test "$ac_rwmutex" = "no"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if pthread_rwlock_t is available with _POSIX_READER_WRITER_LOCKS" >&5 $as_echo_n "checking if pthread_rwlock_t is available with _POSIX_READER_WRITER_LOCKS... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _POSIX_READER_WRITER_LOCKS #include int main () { pthread_rwlock_t *x; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define PJ_EMULATE_RWMUTEX 0" >>confdefs.h CFLAGS="$CFLAGS -D_POSIX_THREADS -D_POSIX_READER_WRITER_LOCKS" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else $as_echo "#define PJ_EMULATE_RWMUTEX 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if pthread_mutexattr_settype() is available" >&5 $as_echo_n "checking if pthread_mutexattr_settype() is available... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { pthread_mutexattr_settype(0,PTHREAD_MUTEX_FAST_NP); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: checking if pthread_mutexattr_t has recursive member" >&5 $as_echo_n "checking if pthread_mutexattr_t has recursive member... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { pthread_mutexattr_t attr; attr.recursive=1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: checking ioqueue backend" >&5 $as_echo_n "checking ioqueue backend... " >&6; } # Check whether --enable-epoll was given. if test "${enable_epoll+set}" = set; then : enableval=$enable_epoll; ac_os_objs=ioqueue_epoll.o { $as_echo "$as_me:${as_lineno-$LINENO}: result: /dev/epoll" >&5 $as_echo "/dev/epoll" >&6; } $as_echo "#define PJ_HAS_LINUX_EPOLL 1" >>confdefs.h ac_linux_poll=epoll else ac_os_objs=ioqueue_select.o { $as_echo "$as_me:${as_lineno-$LINENO}: result: select()" >&5 $as_echo "select()" >&6; } ac_linux_poll=select fi case $target in *mingw* | *cygw* | *win32* | *w32* ) ac_os_objs="$ac_os_objs file_access_win32.o file_io_win32.o os_core_win32.o os_error_win32.o os_time_win32.o os_timestamp_win32.o guid_win32.o" ;; *) ac_os_objs="$ac_os_objs file_access_unistd.o file_io_ansi.o os_core_unix.o os_error_unix.o os_time_unix.o os_timestamp_posix.o" case $target in arm-apple-darwin*) ac_os_objs="$ac_os_objs os_info_iphone.o" ;; esac # UUID if test "$ac_has_uuid_lib" = "1" -a "$ac_has_uuid_h" = "1"; then ac_os_objs="$ac_os_objs guid_uuid.o" else ac_os_objs="$ac_os_objs guid_simple.o" fi ;; esac case $target in *darwin*) ac_os_objs="$ac_os_objs os_core_darwin.o" ;; esac case $target in *android*) LIBS="$LIBS -lOpenSLES" { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... OpenSL ES" >&5 $as_echo "Checking sound device backend... OpenSL ES" >&6; } ;; arm-apple-darwin*) LIBS="$LIBS -framework CoreAudio -framework CoreFoundation -framework AudioToolbox -framework CFNetwork -framework UIKit" ac_pjmedia_audiodev_objs="coreaudio_dev.o" { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... AudioUnit" >&5 $as_echo "Checking sound device backend... AudioUnit" >&6; } ;; *darwin*) LIBS="$LIBS -framework CoreAudio -framework CoreServices -framework AudioUnit -framework AudioToolbox" ac_pjmedia_audiodev_objs="coreaudio_dev.o" { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... CoreAudio" >&5 $as_echo "Checking sound device backend... CoreAudio" >&6; } ;; *cygwin* | *mingw*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... win32 sound" >&5 $as_echo "Checking sound device backend... win32 sound" >&6; } ;; *rtems*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... null sound" >&5 $as_echo "Checking sound device backend... null sound" >&6; } ;; *) ac_fn_c_check_header_mongrel "$LINENO" "alsa/version.h" "ac_cv_header_alsa_version_h" "$ac_includes_default" if test "x$ac_cv_header_alsa_version_h" = xyes; then : LIBS="$LIBS -lasound" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking sound device backend... unix" >&5 $as_echo "Checking sound device backend... unix" >&6; } ;; esac case $target in *android*) LIBS="$LIBS -llog -lgcc" ;; arm-apple-darwin*) LIBS="$LIBS -framework UIKit" ;; *darwin*) LIBS="$LIBS -framework Foundation -framework AppKit" ;; esac case $target in arm-apple-darwin*) ac_pjmedia_video=iphone_os SAVED_LIBS="$LIBS" LIBS="-framework AVFoundation -framework CoreGraphics -framework QuartzCore -framework CoreVideo -framework CoreMedia" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_pjmedia_video_has_ios=yes else ac_pjmedia_video_has_ios=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$SAVED_LIBS" if test "$ac_pjmedia_video_has_ios" = "yes"; then ac_ios_cflags="-DPJMEDIA_VIDEO_DEV_HAS_IOS=1 -DPJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL=1" LIBS="$LIBS -framework OpenGLES -framework AVFoundation -framework CoreGraphics -framework QuartzCore -framework CoreVideo -framework CoreMedia" { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if AVFoundation framework is available... yes" >&5 $as_echo "Checking if AVFoundation framework is available... yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if AVFoundation framework is available... no" >&5 $as_echo "Checking if AVFoundation framework is available... no" >&6; } fi ;; *darwin*) ac_pjmedia_video=mac_os SAVED_LIBS="$LIBS" LIBS="-framework AVFoundation" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_pjmedia_video_has_avf=yes else ac_pjmedia_video_has_avf=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$SAVED_LIBS" if test "$ac_pjmedia_video_has_avf" = "yes"; then ac_qt_cflags="-DPJMEDIA_VIDEO_DEV_HAS_AVF=1" LIBS="$LIBS -framework AVFoundation -framework QuartzCore" { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if AVFoundation framework is available... yes" >&5 $as_echo "Checking if AVFoundation framework is available... yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if AVFoundation framework is available... no" >&5 $as_echo "Checking if AVFoundation framework is available... no" >&6; } fi ;; *cygwin* | *mingw*) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_pjmedia_video_has_dshow=yes else ac_pjmedia_video_has_dshow=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$ac_pjmedia_video_has_dshow" = "yes"; then ac_dshow_cflags="-DPJMEDIA_VIDEO_DEV_HAS_DSHOW=1" ac_dshow_ldflags="-lstrmiids -lrpcrt4 -lquartz -lstrmbase -loleaut32 -lstdc++ -luuid" LIBS="$ac_dshow_ldflags $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if DirectShow is available... yes" >&5 $as_echo "Checking if DirectShow is available... yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if DirectShow is available... no" >&5 $as_echo "Checking if DirectShow is available... no" >&6; } fi ac_android_cflags="$ac_android_cflags -DPJMEDIA_VIDEO_DEV_HAS_ANDROID=1" ;; esac # Check whether --enable-small-filter was given. if test "${enable_small_filter+set}" = set; then : enableval=$enable_small_filter; if test "$enable_small_filter" = "no"; then ac_no_small_filter='-DPJMEDIA_HAS_SMALL_FILTER=0' { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if small filter is disabled... yes" >&5 $as_echo "Checking if small filter is disabled... yes" >&6; } fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if small filter is disabled... no" >&5 $as_echo "Checking if small filter is disabled... no" >&6; } fi # Check whether --enable-large-filter was given. if test "${enable_large_filter+set}" = set; then : enableval=$enable_large_filter; if test "$enable_large_filter" = "no"; then ac_no_large_filter='-DPJMEDIA_HAS_LARGE_FILTER=0' { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if large filter is disabled... yes" >&5 $as_echo "Checking if large filter is disabled... yes" >&6; } fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if large filter is disabled... no" >&5 $as_echo "Checking if large filter is disabled... no" >&6; } fi # Check whether --enable-speex-aec was given. if test "${enable_speex_aec+set}" = set; then : enableval=$enable_speex_aec; if test "$enable_speex_aec" = "no"; then ac_no_speex_aec='-DPJMEDIA_HAS_SPEEX_AEC=0' { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if Speex AEC is disabled...yes" >&5 $as_echo "Checking if Speex AEC is disabled...yes" >&6; } fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if Speex AEC is disabled...no" >&5 $as_echo "Checking if Speex AEC is disabled...no" >&6; } fi # Check whether --enable-g7221-codec was given. if test "${enable_g7221_codec+set}" = set; then : enableval=$enable_g7221_codec; if test "$enable_g7221_codec" = "no"; then ac_no_g7221_codec=1 $as_echo "#define PJMEDIA_HAS_G7221_CODEC 0" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if G.722.1 codec is disabled...yes" >&5 $as_echo "Checking if G.722.1 codec is disabled...yes" >&6; } fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if G.722.1 codec is disabled...no" >&5 $as_echo "Checking if G.722.1 codec is disabled...no" >&6; } fi # Check whether --enable-libsamplerate was given. if test "${enable_libsamplerate+set}" = set; then : enableval=$enable_libsamplerate; { $as_echo "$as_me:${as_lineno-$LINENO}: checking for src_new in -lsamplerate" >&5 $as_echo_n "checking for src_new in -lsamplerate... " >&6; } if ${ac_cv_lib_samplerate_src_new+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsamplerate $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char src_new (); int main () { return src_new (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_samplerate_src_new=yes else ac_cv_lib_samplerate_src_new=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_samplerate_src_new" >&5 $as_echo "$ac_cv_lib_samplerate_src_new" >&6; } if test "x$ac_cv_lib_samplerate_src_new" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBSAMPLERATE 1 _ACEOF LIBS="-lsamplerate $LIBS" fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: Skipping libsamplerate detection" >&5 $as_echo "Skipping libsamplerate detection" >&6; } fi # Check whether --enable-resample_dll was given. if test "${enable_resample_dll+set}" = set; then : enableval=$enable_resample_dll; if test "$enable_resample_dll" = "yes"; then ac_resample_dll=1 { $as_echo "$as_me:${as_lineno-$LINENO}: result: Building libresample as shared library... yes" >&5 $as_echo "Building libresample as shared library... yes" >&6; } fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: Building libresample as shared library... no" >&5 $as_echo "Building libresample as shared library... no" >&6; } fi for ac_prog in pkg-config do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$PKG_CONFIG"; then ac_cv_prog_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_PKG_CONFIG="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi PKG_CONFIG=$ac_cv_prog_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$PKG_CONFIG" && break done test -n "$PKG_CONFIG" || PKG_CONFIG="none" if test "$PKG_CONFIG" == "none"; then as_fn_error $? "*** Error: pkg-config not found ***" "$LINENO" 5 fi # Check whether --with-ffmpeg was given. if test "${with_ffmpeg+set}" = set; then : withval=$with_ffmpeg; else with_ffmpeg=check fi FFMPEG_PREFIX="" SAVED_PKG_CONFIG_PATH=$PKG_CONFIG_PATH if test "x$with_ffmpeg" != "xcheck" -a "x$with_ffmpeg" != "x"; then FFMPEG_PREFIX=$with_ffmpeg { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using ffmpeg prefix... $FFMPEG_PREFIX" >&5 $as_echo "Using ffmpeg prefix... $FFMPEG_PREFIX" >&6; } export PKG_CONFIG_PATH=$FFMPEG_PREFIX/lib/pkgconfig fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking ffmpeg packages" >&5 $as_echo_n "checking ffmpeg packages... " >&6; } av_pkg="" if $PKG_CONFIG --exists libavformat; then ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVFORMAT=1" av_pkg="$av_pkg libavformat" else as_fn_error $? "*** Error: libavformat not detected ***" "$LINENO" 5 fi if $PKG_CONFIG --exists libavcodec; then ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCODEC=1" av_pkg="$av_pkg libavcodec" else as_fn_error $? "*** Error: libavcodec not detected ***" "$LINENO" 5 fi if $PKG_CONFIG --exists libswscale; then ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBSWSCALE=1" av_pkg="$av_pkg libswscale" else as_fn_error $? "*** Error: libswscale not detected ***" "$LINENO" 5 fi if $PKG_CONFIG --exists libavutil; then ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVUTIL=1" av_pkg="$av_pkg libavutil" else as_fn_error $? "*** Error: libavutil not detected ***" "$LINENO" 5 fi if $PKG_CONFIG --exists libavcore; then ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCORE=1" av_pkg="$av_pkg libavcore" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $av_pkg" >&5 $as_echo "$av_pkg" >&6; } ac_ffmpeg_cflags="$ac_ffmpeg_cflags `$PKG_CONFIG --cflags $av_pkg`" ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags `$PKG_CONFIG --libs $av_pkg`" LIBS="$LIBS $ac_ffmpeg_ldflags" export PKG_CONFIG_PATH=$SAVED_PKG_CONFIG_PATH # Check whether --with-vpx was given. if test "${with_vpx+set}" = set; then : withval=$with_vpx; else with_vpx=check fi VPX_PREFIX="" SAVED_PKG_CONFIG_PATH=$PKG_CONFIG_PATH if test "x$with_vpx" != "xcheck" -a "x$with_vpx" != "x"; then VPX_PREFIX=$with_vpx { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using libVPX prefix... $VPX_PREFIX" >&5 $as_echo "Using libVPX prefix... $VPX_PREFIX" >&6; } export PKG_CONFIG_PATH=$VPX_PREFIX fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking libvpx packages" >&5 $as_echo_n "checking libvpx packages... " >&6; } if $PKG_CONFIG --exists vpx; then ac_vpx_cflags="`$PKG_CONFIG --cflags vpx` -DPJMEDIA_HAS_LIBVPX=1" ac_vpx_ldflags="`$PKG_CONFIG --libs vpx`" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else as_fn_error $? "*** Error: VPX library not detected ***" "$LINENO" 5 fi LIBS="$LIBS $ac_vpx_ldflags" export PKG_CONFIG_PATH=$SAVED_PKG_CONFIG_PATH # Check whether --enable-v4l2 was given. if test "${enable_v4l2+set}" = set; then : enableval=$enable_v4l2; if test "$enable_v4l2" = "no"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if V4L2 is disabled... yes" >&5 $as_echo "Checking if V4L2 is disabled... yes" >&6; } fi else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for v4l2_open in -lv4l2" >&5 $as_echo_n "checking for v4l2_open in -lv4l2... " >&6; } if ${ac_cv_lib_v4l2_v4l2_open+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lv4l2 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char v4l2_open (); int main () { return v4l2_open (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_v4l2_v4l2_open=yes else ac_cv_lib_v4l2_v4l2_open=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_v4l2_v4l2_open" >&5 $as_echo "$ac_cv_lib_v4l2_v4l2_open" >&6; } if test "x$ac_cv_lib_v4l2_v4l2_open" = xyes; then : ac_v4l2_cflags="-DPJMEDIA_VIDEO_DEV_HAS_V4L2=1" ac_v4l2_ldflags="-lv4l2" LIBS="$LIBS -lv4l2" fi fi # Check whether --with-openh264 was given. if test "${with_openh264+set}" = set; then : withval=$with_openh264; else with_openh264=no fi if test "x$ac_cross_compile" != "x" -a "x$with_openh264" = "xno"; then enable_openh264=no fi # Check whether --enable-openh264 was given. if test "${enable_openh264+set}" = set; then : enableval=$enable_openh264; if test "$enable_openh264" = "no"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if OpenH264 is disabled... yes" >&5 $as_echo "Checking if OpenH264 is disabled... yes" >&6; } fi else if test "x$with_openh264" != "xno" -a "x$with_openh264" != "x"; then OPENH264_PREFIX=$with_openh264 OPENH264_CFLAGS="-I$OPENH264_PREFIX/include" OPENH264_LDFLAGS="-L$OPENH264_PREFIX/lib" { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using OpenH264 prefix... $with_openh264" >&5 $as_echo "Using OpenH264 prefix... $with_openh264" >&6; } else OPENH264_CFLAGS="" OPENH264_LDFLAGS="" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking OpenH264 usability" >&5 $as_echo_n "checking OpenH264 usability... " >&6; } OPENH264_LIBS="-lopenh264" SAVED_LIBS="$LIBS" SAVED_LDFLAGS="$LDFLAGS" SAVED_CFLAGS="$CFLAGS" LIBS="$OPENH264_LIBS $LIBS" LDFLAGS="$OPENH264_LDFLAGS $LDFLAGS -lstdc++" CFLAGS="$OPENH264_CFLAGS $CFLAGS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { WelsCreateSVCEncoder(0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_openh264_cflags="-DPJMEDIA_HAS_OPENH264_CODEC=1 $OPENH264_CFLAGS" ac_openh264_ldflags="$OPENH264_LDFLAGS $OPENH264_LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } else LIBS="$SAVED_LIBS" LDFLAGS="$SAVED_LDFLAGS" CFLAGS="$SAVED_CFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi # Check whether --with-libyuv was given. if test "${with_libyuv+set}" = set; then : withval=$with_libyuv; else with_libyuv=no fi if test "x$ac_cross_compile" != "x" -a "x$with_libyuv" = "xno"; then enable_libyuv=no fi # Check whether --enable-libyuv was given. if test "${enable_libyuv+set}" = set; then : enableval=$enable_libyuv; if test "$enable_libyuv" = "no"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if libyuv is disabled...yes" >&5 $as_echo "Checking if libyuv is disabled...yes" >&6; } fi else if test "x$with_libyuv" != "xno" -a "x$with_libyuv" != "x"; then LIBYUV_PREFIX=$with_libyuv LIBYUV_CFLAGS="-I$LIBYUV_PREFIX/include" case $target in arm-apple-darwin*) LIBYUV_LDFLAGS="-L$LIBYUV_PREFIX/out_ios/Release-iphoneos" case $ARCH in *armv7*) LIBYUV_LIBS="-lyuv_neon" ;; *) ;; esac ;; *mingw* | *cygw* | *win32* | *w32* | *darwin* | *linux* | *android*) LIBYUV_LDFLAGS="-L$LIBYUV_PREFIX/out/Release" ;; *) LIBYUV_CFLAGS="" LIBYUV_LDFLAGS="" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using libyuv prefix... $with_libyuv" >&5 $as_echo "Using libyuv prefix... $with_libyuv" >&6; } else LIBYUV_CFLAGS="" LIBYUV_LDFLAGS="" fi LIBYUV_LIBS="$LIBYUV_LIBS -lyuv" SAVED_LIBS="$LIBS" SAVED_LDFLAGS="$LDFLAGS" SAVED_CFLAGS="$CFLAGS" LIBS="$LIBYUV_LIBS $LIBS" LDFLAGS="$LIBYUV_LDFLAGS $LDFLAGS" CFLAGS="$LIBYUV_CFLAGS $CFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for I420Scale in -lyuv" >&5 $as_echo_n "checking for I420Scale in -lyuv... " >&6; } if ${ac_cv_lib_yuv_I420Scale+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lyuv $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char I420Scale (); int main () { return I420Scale (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_yuv_I420Scale=yes else ac_cv_lib_yuv_I420Scale=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_yuv_I420Scale" >&5 $as_echo "$ac_cv_lib_yuv_I420Scale" >&6; } if test "x$ac_cv_lib_yuv_I420Scale" = xyes; then : ac_libyuv_cflags="-DPJMEDIA_HAS_LIBYUV=1 $LIBYUV_CFLAGS" ac_libyuv_ldflags="$LIBYUV_LDFLAGS $LIBYUV_LIBS" else LIBS="$SAVED_LIBS" LDFLAGS="$SAVED_LDFLAGS" CFLAGS="$SAVED_CFLAGS" fi fi case $target in *darwin*) ac_webrtc_platform=webrtc_darwinos ;; *mingw*) ac_webrtc_platform=webrtc_win32 ;; *linux*) ac_webrtc_platform=webrtc_linux ;; *) ac_webrtc_platform=null ;; esac # Check whether --with-ssl was given. if test "${with_ssl+set}" = set; then : withval=$with_ssl; else with_ssl=no fi if test "x$ac_cross_compile" != "x" -a "x$with_ssl" = "xno"; then enable_ssl=no fi # Check whether --enable-ssl was given. if test "${enable_ssl+set}" = set; then : enableval=$enable_ssl; if test "$enable_ssl" = "no"; then ac_no_ssl=1 { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if SSL support is disabled... yes" >&5 $as_echo "Checking if SSL support is disabled... yes" >&6; } fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: checking for OpenSSL installations.." >&5 $as_echo "checking for OpenSSL installations.." >&6; } if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then CFLAGS="$CFLAGS -I$with_ssl/include" LDFLAGS="$LDFLAGS -L$with_ssl/lib" { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using SSL prefix... $with_ssl" >&5 $as_echo "Using SSL prefix... $with_ssl" >&6; } fi ac_fn_c_check_header_mongrel "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default" if test "x$ac_cv_header_openssl_ssl_h" = xyes; then : openssl_h_present=1 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ERR_load_BIO_strings in -lcrypto" >&5 $as_echo_n "checking for ERR_load_BIO_strings in -lcrypto... " >&6; } if ${ac_cv_lib_crypto_ERR_load_BIO_strings+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypto $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char ERR_load_BIO_strings (); int main () { return ERR_load_BIO_strings (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_crypto_ERR_load_BIO_strings=yes else ac_cv_lib_crypto_ERR_load_BIO_strings=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_ERR_load_BIO_strings" >&5 $as_echo "$ac_cv_lib_crypto_ERR_load_BIO_strings" >&6; } if test "x$ac_cv_lib_crypto_ERR_load_BIO_strings" = xyes; then : libcrypto_present=1 && LIBS="-lcrypto $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_CTX_new in -lssl" >&5 $as_echo_n "checking for SSL_CTX_new in -lssl... " >&6; } if ${ac_cv_lib_ssl_SSL_CTX_new+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char SSL_CTX_new (); int main () { return SSL_CTX_new (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ssl_SSL_CTX_new=yes else ac_cv_lib_ssl_SSL_CTX_new=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_SSL_CTX_new" >&5 $as_echo "$ac_cv_lib_ssl_SSL_CTX_new" >&6; } if test "x$ac_cv_lib_ssl_SSL_CTX_new" = xyes; then : libssl_present=1 && LIBS="-lssl $LIBS" fi if test "x$openssl_h_present" = "x1" -a "x$libssl_present" = "x1" -a "x$libcrypto_present" = "x1"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: OpenSSL library found, SSL support enabled" >&5 $as_echo "OpenSSL library found, SSL support enabled" >&6; } # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1) $as_echo "#define PJ_HAS_SSL_SOCK 1" >>confdefs.h else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ** OpenSSL libraries not found, disabling SSL support **" >&5 $as_echo "** OpenSSL libraries not found, disabling SSL support **" >&6; } fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if select() needs correct nfds" >&5 $as_echo_n "checking if select() needs correct nfds... " >&6; } case $target in *rtems*) $as_echo "#define PJ_SELECT_NEEDS_NFDS 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } ;; *) $as_echo "#define PJ_SELECT_NEEDS_NFDS 0" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: no (default)" >&5 $as_echo "no (default)" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: ** Decided that select() doesn't need correct nfds (please check)" >&5 $as_echo "** Decided that select() doesn't need correct nfds (please check)" >&6; } ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking if pj_thread_create() should enforce stack size" >&5 $as_echo_n "checking if pj_thread_create() should enforce stack size... " >&6; } case $target in *rtems*) $as_echo "#define PJ_THREAD_SET_STACK_SIZE 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } ;; *) $as_echo "#define PJ_THREAD_SET_STACK_SIZE 0" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: no (default)" >&5 $as_echo "no (default)" >&6; } ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking if pj_thread_create() should allocate stack" >&5 $as_echo_n "checking if pj_thread_create() should allocate stack... " >&6; } case $target in *rtems*) $as_echo "#define PJ_THREAD_ALLOCATE_STACK 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } ;; *) $as_echo "#define PJ_THREAD_ALLOCATE_STACK 0" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: no (default)" >&5 $as_echo "no (default)" >&6; } ;; esac case $target in *mingw* | *cygw* | *win32* | *w32* ) $as_echo "#define PJ_BLOCKING_ERROR_VAL WSAEWOULDBLOCK" >>confdefs.h ;; *) $as_echo "#define PJ_BLOCKING_ERROR_VAL EAGAIN" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: ** Setting non-blocking recv() retval to EAGAIN (please check)" >&5 $as_echo "** Setting non-blocking recv() retval to EAGAIN (please check)" >&6; } ;; esac case $target in *mingw* | *cygw* | *win32* | *w32* ) $as_echo "#define PJ_BLOCKING_CONNECT_ERROR_VAL WSAEWOULDBLOCK" >>confdefs.h ;; *) $as_echo "#define PJ_BLOCKING_CONNECT_ERROR_VAL EINPROGRESS" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: ** Setting non-blocking connect() retval to EINPROGRESS (please check)" >&5 $as_echo "** Setting non-blocking connect() retval to EINPROGRESS (please check)" >&6; } ;; esac ac_linux_poll=select ac_host=unix case $target in *rtems*) ac_main_obj=main_rtems.o ;; *) ac_main_obj=main.o ;; esac ac_build_mak_vars=`echo $ac_build_mak_vars | sed 's/\\\\n/\n/g'` cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by pjproject $as_me 2.x, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ pjproject config.status 2.x configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "pjlib/include/pj/compat/os_auto.h") CONFIG_HEADERS="$CONFIG_HEADERS pjlib/include/pj/compat/os_auto.h" ;; "pjlib/include/pj/compat/m_auto.h") CONFIG_HEADERS="$CONFIG_HEADERS pjlib/include/pj/compat/m_auto.h" ;; "pjmedia/include/pjmedia/config_auto.h") CONFIG_HEADERS="$CONFIG_HEADERS pjmedia/include/pjmedia/config_auto.h" ;; "pjmedia/include/pjmedia-codec/config_auto.h") CONFIG_HEADERS="$CONFIG_HEADERS pjmedia/include/pjmedia-codec/config_auto.h" ;; "pjsip/include/pjsip/sip_autoconf.h") CONFIG_HEADERS="$CONFIG_HEADERS pjsip/include/pjsip/sip_autoconf.h" ;; "build.mak") CONFIG_FILES="$CONFIG_FILES build.mak" ;; "build/os-auto.mak") CONFIG_FILES="$CONFIG_FILES build/os-auto.mak" ;; "build/cc-auto.mak") CONFIG_FILES="$CONFIG_FILES build/cc-auto.mak" ;; "pjlib/build/os-auto.mak") CONFIG_FILES="$CONFIG_FILES pjlib/build/os-auto.mak" ;; "pjmedia/build/os-auto.mak") CONFIG_FILES="$CONFIG_FILES pjmedia/build/os-auto.mak" ;; "pjsip/build/os-auto.mak") CONFIG_FILES="$CONFIG_FILES pjsip/build/os-auto.mak" ;; "third_party/build/webrtc/os-auto.mak") CONFIG_FILES="$CONFIG_FILES third_party/build/webrtc/os-auto.mak" ;; "third_party/build/os-auto.mak") CONFIG_FILES="$CONFIG_FILES third_party/build/os-auto.mak" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: Configurations for current target have been written to 'build.mak', and 'os-auto.mak' in various build directories, and pjlib/include/pj/compat/os_auto.h. Further customizations can be put in: - 'user.mak' - 'pjlib/include/pj/config_site.h' The next step now is to run 'make dep' and 'make'. " >&5 $as_echo " Configurations for current target have been written to 'build.mak', and 'os-auto.mak' in various build directories, and pjlib/include/pj/compat/os_auto.h. Further customizations can be put in: - 'user.mak' - 'pjlib/include/pj/config_site.h' The next step now is to run 'make dep' and 'make'. " >&6; } ================================================ FILE: deps/pjsip/aconfigure.ac ================================================ AC_INIT(pjproject,2.x) host_orig="$host" AC_CANONICAL_BUILD AC_CANONICAL_HOST AC_CANONICAL_TARGET AC_CONFIG_HEADER([pjlib/include/pj/compat/os_auto.h pjlib/include/pj/compat/m_auto.h pjmedia/include/pjmedia/config_auto.h pjmedia/include/pjmedia-codec/config_auto.h pjsip/include/pjsip/sip_autoconf.h ]) AC_CONFIG_FILES([build.mak build/os-auto.mak build/cc-auto.mak pjlib/build/os-auto.mak pjmedia/build/os-auto.mak pjsip/build/os-auto.mak third_party/build/webrtc/os-auto.mak third_party/build/os-auto.mak ]) dnl dnl Setup default CFLAGS dnl if test "$CFLAGS" = ""; then CFLAGS="-O2" else CFLAGS="$CFLAGS" fi CXXFLAGS="$CFLAGS $CXXFLAGS" dnl # dnl # Configure tools dnl # AC_PROG_CC AC_PROG_CXX AC_LANG_C AC_PROG_RANLIB AC_CHECK_TOOLS([AR], [ar gar], :) if test "$AR_FLAGS" = ""; then AR_FLAGS="rv"; fi AC_SUBST(AR_FLAGS) if test "$LD" = ""; then LD="$CC"; fi AC_SUBST(LD) if test "$LDOUT" = ""; then LDOUT="-o "; fi AC_SUBST(LDOUT) if test "$OBJEXT" = ""; then OBJEXT='o'; fi AC_SUBST(OBJEXT) if test "$LIBEXT" = ""; then LIBEXT='a'; fi AC_SUBST(LIBEXT) if test "$LIBEXT2" = ""; then LIBEXT2=""; fi AC_SUBST(LIBEXT2) if test "$CC_OUT" = ""; then CC_OUT="-o "; fi AC_SUBST(CC_OUT) if test "$CC_INC" = ""; then CC_INC="-I"; fi AC_SUBST(CC_INC) if test "$CC_DEF" = ""; then CC_DEF="-D"; fi AC_SUBST(CC_DEF) if test "$CC_OPTIMIZE" = ""; then CC_OPTIMIZE="-O2"; fi AC_SUBST(CC_OPTIMIZE) if test "$CC_CFLAGS" = ""; then CC_CFLAGS="-Wall"; fi AC_SUBST(CC_CFLAGS) AC_SUBST(ac_pjdir) AC_SUBST(ac_build_mak_vars) case $host in *mingw* | *cygw* | *win32* | *w32* ) if pwd -W 2&> /dev/null; then ac_pjdir=`pwd -W` else # We're probably cross-compiling mingw on Linux ac_pjdir=`pwd` fi ;; *) ac_pjdir=`pwd` ;; esac AC_SUBST(ac_shlib_suffix) case $target in *mingw* | *cygw* | *win32* | *w32* ) ac_shlib_suffix=dll ;; *darwin*) ac_shlib_suffix=dylib ;; *) ac_shlib_suffix=so ;; esac AC_SUBST(ac_cross_compile) if test "$build" = "$host"; then ac_cross_compile= else ac_cross_compile=${host_orig}- fi AC_CHECK_LIB(pthread,pthread_create) AC_CHECK_LIB(wsock32,puts) AC_CHECK_LIB(ws2_32,puts) AC_CHECK_LIB(ole32,puts) AC_CHECK_LIB(winmm,puts) AC_CHECK_LIB(socket,puts) AC_CHECK_LIB(rt,puts) AC_CHECK_LIB(m,sin) AC_CHECK_LIB(uuid,uuid_generate) AC_CHECK_LIB(uuid,uuid_generate,[ac_has_uuid_lib=1]) AC_SEARCH_LIBS(gethostbyname,nsl) AC_MSG_RESULT([Setting PJ_M_NAME to $target_cpu]) AC_DEFINE_UNQUOTED(PJ_M_NAME,["$target_cpu"]) dnl dnl Memory alignment detection dnl AC_MSG_CHECKING([memory alignment]) case $target in sparc64-* | ia64-* | x86_64-* ) AC_DEFINE(PJ_POOL_ALIGNMENT, 8) AC_MSG_RESULT([8 bytes]) ;; * ) AC_DEFINE(PJ_POOL_ALIGNMENT, 4) AC_MSG_RESULT([4 bytes (default)]) ;; esac dnl dnl Endianness detection dnl AC_C_BIGENDIAN if test "x$ac_cv_c_bigendian" = "xyes"; then CFLAGS="$CFLAGS -DPJ_IS_BIG_ENDIAN=1 -DPJ_IS_LITTLE_ENDIAN=0" else CFLAGS="$CFLAGS -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1" fi dnl dnl Legacy macros dnl case $target in *android*) AC_DEFINE(PJ_ANDROID,1) ;; *mingw* | *cygw* | *win32* | *w32* ) AC_DEFINE(PJ_WIN32,1) AC_DEFINE(PJ_WIN32_WINNT,0x0400) AC_DEFINE(WIN32_LEAN_AND_MEAN) ;; *darwin*) AC_DEFINE(PJ_DARWINOS,1) ;; *linux*) AC_DEFINE(PJ_LINUX,1) ;; *rtems*) AC_DEFINE(PJ_RTEMS,1) ;; *sunos* | *solaris* ) AC_DEFINE(PJ_SUNOS,1) ;; *) ;; esac AC_CHECK_HEADER(arpa/inet.h,[AC_DEFINE(PJ_HAS_ARPA_INET_H,1)]) AC_CHECK_HEADER(assert.h,[AC_DEFINE(PJ_HAS_ASSERT_H,1)]) AC_CHECK_HEADER(ctype.h,[AC_DEFINE(PJ_HAS_CTYPE_H,1)]) case $target in *mingw* | *cygw* | *win32* | *w32* ) AC_DEFINE(PJ_HAS_ERRNO_H,0) ;; *) AC_CHECK_HEADER(errno.h,[AC_DEFINE(PJ_HAS_ERRNO_H,1)]) ;; esac AC_CHECK_HEADER(fcntl.h,[AC_DEFINE(PJ_HAS_FCNTL_H,1)]) AC_CHECK_HEADER(linux/socket.h,[AC_DEFINE(PJ_HAS_LINUX_SOCKET_H,1)]) AC_CHECK_HEADER(limits.h,[AC_DEFINE(PJ_HAS_LIMITS_H,1)]) AC_CHECK_HEADER(malloc.h,[AC_DEFINE(PJ_HAS_MALLOC_H,1)]) AC_CHECK_HEADER(netdb.h,[AC_DEFINE(PJ_HAS_NETDB_H,1)]) AC_CHECK_HEADER(netinet/in_systm.h,[AC_DEFINE(PJ_HAS_NETINET_IN_SYSTM_H,1)]) AC_CHECK_HEADER(netinet/in.h,[AC_DEFINE(PJ_HAS_NETINET_IN_H,1)]) AC_CHECK_HEADER(netinet/ip.h,[AC_DEFINE(PJ_HAS_NETINET_IP_H,1)],[], [#if PJ_HAS_SYS_TYPES_H # include #endif #if PJ_HAS_NETINET_IN_SYSTM_H # include #endif #if PJ_HAS_NETINET_IN_H # include #endif ]) AC_CHECK_HEADER(netinet/tcp.h,[AC_DEFINE(PJ_HAS_NETINET_TCP_H,1)]) AC_CHECK_HEADER(ifaddrs.h, [AC_CHECK_FUNC(getifaddrs,[AC_DEFINE(PJ_HAS_IFADDRS_H,1)])]) AC_CHECK_HEADER(semaphore.h,[AC_DEFINE(PJ_HAS_SEMAPHORE_H,1)]) AC_CHECK_HEADER(setjmp.h,[AC_DEFINE(PJ_HAS_SETJMP_H,1)]) AC_CHECK_HEADER(stdarg.h,[AC_DEFINE(PJ_HAS_STDARG_H,1)]) AC_CHECK_HEADER(stddef.h,[AC_DEFINE(PJ_HAS_STDDEF_H,1)]) AC_CHECK_HEADER(stdio.h,[AC_DEFINE(PJ_HAS_STDIO_H,1)]) AC_CHECK_HEADER(stdint.h,[AC_DEFINE(PJ_HAS_STDINT_H,1)]) AC_CHECK_HEADER(stdlib.h,[AC_DEFINE(PJ_HAS_STDLIB_H,1)]) AC_CHECK_HEADER(string.h,[AC_DEFINE(PJ_HAS_STRING_H,1)]) AC_CHECK_HEADER(sys/ioctl.h,[AC_DEFINE(PJ_HAS_SYS_IOCTL_H,1)]) AC_CHECK_HEADER(sys/select.h,[AC_DEFINE(PJ_HAS_SYS_SELECT_H,1)]) AC_CHECK_HEADER(sys/socket.h,[AC_DEFINE(PJ_HAS_SYS_SOCKET_H,1)]) AC_CHECK_HEADER(sys/time.h,[AC_DEFINE(PJ_HAS_SYS_TIME_H,1)]) AC_CHECK_HEADER(sys/timeb.h,[AC_DEFINE(PJ_HAS_SYS_TIMEB_H,1)]) AC_CHECK_HEADER(sys/types.h,[AC_DEFINE(PJ_HAS_SYS_TYPES_H,1)]) AC_CHECK_HEADER(sys/filio.h,[AC_DEFINE(PJ_HAS_SYS_FILIO_H,1)]) AC_CHECK_HEADER(sys/sockio.h,[AC_DEFINE(PJ_HAS_SYS_SOCKIO_H,1)]) AC_CHECK_HEADER(sys/utsname.h,[AC_DEFINE(PJ_HAS_SYS_UTSNAME_H,1)]) AC_CHECK_HEADER(time.h,[AC_DEFINE(PJ_HAS_TIME_H,1)]) AC_CHECK_HEADER(unistd.h,[AC_DEFINE(PJ_HAS_UNISTD_H,1)]) AC_CHECK_HEADER(winsock.h,[AC_DEFINE(PJ_HAS_WINSOCK_H,1)]) AC_CHECK_HEADER(winsock2.h,[AC_DEFINE(PJ_HAS_WINSOCK2_H,1)]) AC_CHECK_HEADER(mswsock.h,[AC_DEFINE(PJ_HAS_MSWSOCK_H,1)],[], [#if PJ_HAS_WINSOCK2_H # include #elif PJ_HAS_WINSOCK_H # include #endif ]) AC_CHECK_HEADER(ws2tcpip.h,[AC_DEFINE(PJ_HAS_WS2TCPIP_H,1)]) AC_CHECK_HEADER(uuid/uuid.h,[ac_has_uuid_h=1]) AC_CHECK_HEADER(net/if.h,[AC_DEFINE(PJ_HAS_NET_IF_H,1)],[], [#if PJ_HAS_SYS_TYPES_H # include #endif #if PJ_HAS_SYS_SOCKET_H # include #endif ]) AC_MSG_RESULT([Setting PJ_OS_NAME to $target]) AC_DEFINE_UNQUOTED(PJ_OS_NAME,["$target"]) AC_MSG_RESULT([Setting PJ_HAS_ERRNO_VAR to 1]) AC_DEFINE(PJ_HAS_ERRNO_VAR,1) AC_MSG_RESULT([Setting PJ_HAS_HIGH_RES_TIMER to 1]) AC_DEFINE(PJ_HAS_HIGH_RES_TIMER,1) AC_MSG_RESULT([Setting PJ_HAS_MALLOC to 1]) AC_DEFINE(PJ_HAS_MALLOC,1) AC_MSG_RESULT([Setting PJ_NATIVE_STRING_IS_UNICODE to 0]) AC_DEFINE(PJ_NATIVE_STRING_IS_UNICODE,0) AC_MSG_RESULT([Setting PJ_ATOMIC_VALUE_TYPE to long]) AC_DEFINE(PJ_ATOMIC_VALUE_TYPE,long) dnl # Determine if inet_aton() is available AC_MSG_CHECKING([if inet_aton() is available]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include #include ]], [inet_aton(0, 0);])], [AC_DEFINE(PJ_SOCK_HAS_INET_ATON,1) AC_MSG_RESULT(yes)], [AC_MSG_RESULT(no)]) dnl # Determine if inet_pton() is available AC_MSG_CHECKING([if inet_pton() is available]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include #include ]], [inet_pton(0, 0, 0);])], [AC_DEFINE(PJ_SOCK_HAS_INET_PTON,1) AC_MSG_RESULT(yes)], [AC_MSG_RESULT(no)]) dnl # Determine if inet_ntop() is available AC_MSG_CHECKING([if inet_ntop() is available]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include #include ]], [inet_ntop(0, 0, 0, 0);])], [AC_DEFINE(PJ_SOCK_HAS_INET_NTOP,1) AC_MSG_RESULT(yes)], [AC_MSG_RESULT(no)]) dnl # Determine if getaddrinfo() is available AC_MSG_CHECKING([if getaddrinfo() is available]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include #include ]], [getaddrinfo(0, 0, 0, 0);])], [AC_DEFINE(PJ_SOCK_HAS_GETADDRINFO,1) AC_MSG_RESULT(yes)], [AC_MSG_RESULT(no)]) dnl # Determine if sockaddr_in has sin_len member AC_MSG_CHECKING([if sockaddr_in has sin_len member]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include #include #include ]], [struct sockaddr_in a; a.sin_len=0;])], [AC_DEFINE(PJ_SOCKADDR_HAS_LEN,1) AC_MSG_RESULT(yes)], AC_MSG_RESULT(no)) dnl # Determine if socklen_t is available AC_MSG_CHECKING([if socklen_t is available]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include ]], [socklen_t xxx = 0;])], [AC_DEFINE(PJ_HAS_SOCKLEN_T,1) AC_MSG_RESULT(yes)], AC_MSG_RESULT(no)) dnl # Determine if SO_ERROR is available AC_MSG_CHECKING([if SO_ERROR is available]) case $target in *mingw* | *cygw* | *win32* | *w32* ) AC_DEFINE(PJ_HAS_SO_ERROR,1) AC_MSG_RESULT(yes) ;; *) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include #include #include ]], [int i=SO_ERROR;])], [AC_DEFINE(PJ_HAS_SO_ERROR,1) AC_MSG_RESULT(yes)], AC_MSG_RESULT(no)) ;; esac dnl # Determine if RW-mutex is available AC_MSG_CHECKING([if pthread_rwlock_t is available]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], [pthread_rwlock_t *x;])], [AC_DEFINE(PJ_EMULATE_RWMUTEX,0) ac_rwmutex="yes" AC_MSG_RESULT(yes)], [AC_DEFINE(PJ_EMULATE_RWMUTEX,1) ac_rwmutex="no" AC_MSG_RESULT(no)]) dnl # If rwmutex is not detected, check again but this time dnl # with _POSIX_READER_WRITER_LOCKS defined (newlib needs this) if test "$ac_rwmutex" = "no"; then AC_MSG_CHECKING([if pthread_rwlock_t is available with _POSIX_READER_WRITER_LOCKS]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#define _POSIX_READER_WRITER_LOCKS #include ]], [pthread_rwlock_t *x;])], [AC_DEFINE(PJ_EMULATE_RWMUTEX,0) CFLAGS="$CFLAGS -D_POSIX_THREADS -D_POSIX_READER_WRITER_LOCKS" AC_MSG_RESULT(yes)], [AC_DEFINE(PJ_EMULATE_RWMUTEX,1) AC_MSG_RESULT(no)]) fi dnl # Do we have pthread_mutexattr_settype()? AC_MSG_CHECKING([if pthread_mutexattr_settype() is available]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], [pthread_mutexattr_settype(0,PTHREAD_MUTEX_FAST_NP);])], [AC_DEFINE(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE,1) AC_MSG_RESULT(yes)], [AC_MSG_RESULT(no)]) dnl # Does pthread_mutexattr_t has "recursive" member? AC_MSG_CHECKING([if pthread_mutexattr_t has recursive member]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], [[pthread_mutexattr_t attr; attr.recursive=1;]])], [AC_DEFINE(PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE,1) AC_MSG_RESULT(yes)], [AC_MSG_RESULT(no)]) dnl ###################### dnl # ioqueue selection dnl # AC_SUBST(ac_os_objs) AC_SUBST(ac_linux_poll) AC_MSG_CHECKING([ioqueue backend]) AC_ARG_ENABLE(epoll, AC_HELP_STRING([--enable-epoll], [Use /dev/epoll ioqueue on Linux (experimental)]), [ ac_os_objs=ioqueue_epoll.o AC_MSG_RESULT([/dev/epoll]) AC_DEFINE(PJ_HAS_LINUX_EPOLL,1) ac_linux_poll=epoll ], [ ac_os_objs=ioqueue_select.o AC_MSG_RESULT([select()]) ac_linux_poll=select ]) dnl ###################### dnl # OS specific files dnl # case $target in *mingw* | *cygw* | *win32* | *w32* ) ac_os_objs="$ac_os_objs file_access_win32.o file_io_win32.o os_core_win32.o os_error_win32.o os_time_win32.o os_timestamp_win32.o guid_win32.o" ;; *) ac_os_objs="$ac_os_objs file_access_unistd.o file_io_ansi.o os_core_unix.o os_error_unix.o os_time_unix.o os_timestamp_posix.o" case $target in arm-apple-darwin*) ac_os_objs="$ac_os_objs os_info_iphone.o" ;; esac # UUID if test "$ac_has_uuid_lib" = "1" -a "$ac_has_uuid_h" = "1"; then ac_os_objs="$ac_os_objs guid_uuid.o" else ac_os_objs="$ac_os_objs guid_simple.o" fi ;; esac case $target in *darwin*) ac_os_objs="$ac_os_objs os_core_darwin.o" ;; esac dnl ########################################## dnl # dnl # PJMEDIA dnl # AC_SUBST(ac_pjmedia_audiodev_objs) case $target in *android*) LIBS="$LIBS -lOpenSLES" AC_MSG_RESULT([Checking sound device backend... OpenSL ES]) ;; arm-apple-darwin*) LIBS="$LIBS -framework CoreAudio -framework CoreFoundation -framework AudioToolbox -framework CFNetwork -framework UIKit" ac_pjmedia_audiodev_objs="coreaudio_dev.o" AC_MSG_RESULT([Checking sound device backend... AudioUnit]) ;; *darwin*) LIBS="$LIBS -framework CoreAudio -framework CoreServices -framework AudioUnit -framework AudioToolbox" ac_pjmedia_audiodev_objs="coreaudio_dev.o" AC_MSG_RESULT([Checking sound device backend... CoreAudio]) ;; *cygwin* | *mingw*) AC_MSG_RESULT([Checking sound device backend... win32 sound]) ;; *rtems*) AC_MSG_RESULT([Checking sound device backend... null sound]) ;; *) dnl # Check if ALSA is available AC_CHECK_HEADER(alsa/version.h,[LIBS="$LIBS -lasound"]) AC_MSG_RESULT([Checking sound device backend... unix]) ;; esac AC_SUBST(ac_pjmedia_video) case $target in *android*) LIBS="$LIBS -llog -lgcc" ;; arm-apple-darwin*) LIBS="$LIBS -framework UIKit" ;; *darwin*) LIBS="$LIBS -framework Foundation -framework AppKit" ;; esac case $target in arm-apple-darwin*) ac_pjmedia_video=iphone_os AC_SUBST(ac_pjmedia_video_has_ios) AC_SUBST(ac_ios_cflags) SAVED_LIBS="$LIBS" LIBS="-framework AVFoundation -framework CoreGraphics -framework QuartzCore -framework CoreVideo -framework CoreMedia" AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [])],[ac_pjmedia_video_has_ios=yes],[ac_pjmedia_video_has_ios=no]) LIBS="$SAVED_LIBS" if test "$ac_pjmedia_video_has_ios" = "yes"; then ac_ios_cflags="-DPJMEDIA_VIDEO_DEV_HAS_IOS=1 -DPJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL=1" LIBS="$LIBS -framework OpenGLES -framework AVFoundation -framework CoreGraphics -framework QuartzCore -framework CoreVideo -framework CoreMedia" AC_MSG_RESULT([Checking if AVFoundation framework is available... yes]) else AC_MSG_RESULT([Checking if AVFoundation framework is available... no]) fi ;; *darwin*) ac_pjmedia_video=mac_os AC_SUBST(ac_pjmedia_video_has_avf) AC_SUBST(ac_avf_cflags) SAVED_LIBS="$LIBS" LIBS="-framework AVFoundation" AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [])],[ac_pjmedia_video_has_avf=yes],[ac_pjmedia_video_has_avf=no]) LIBS="$SAVED_LIBS" if test "$ac_pjmedia_video_has_avf" = "yes"; then ac_qt_cflags="-DPJMEDIA_VIDEO_DEV_HAS_AVF=1" LIBS="$LIBS -framework AVFoundation -framework QuartzCore" AC_MSG_RESULT([Checking if AVFoundation framework is available... yes]) else AC_MSG_RESULT([Checking if AVFoundation framework is available... no]) fi ;; *cygwin* | *mingw*) AC_SUBST(ac_pjmedia_video_has_dshow) AC_SUBST(ac_dshow_cflags) AC_SUBST(ac_dshow_ldflags) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [])],[ac_pjmedia_video_has_dshow=yes],[ac_pjmedia_video_has_dshow=no]) if test "$ac_pjmedia_video_has_dshow" = "yes"; then ac_dshow_cflags="-DPJMEDIA_VIDEO_DEV_HAS_DSHOW=1" ac_dshow_ldflags="-lstrmiids -lrpcrt4 -lquartz -lstrmbase -loleaut32 -lstdc++ -luuid" LIBS="$ac_dshow_ldflags $LIBS" AC_MSG_RESULT([Checking if DirectShow is available... yes]) else AC_MSG_RESULT([Checking if DirectShow is available... no]) fi ac_android_cflags="$ac_android_cflags -DPJMEDIA_VIDEO_DEV_HAS_ANDROID=1" ;; esac dnl # Include resampling small filter AC_SUBST(ac_no_small_filter) AC_ARG_ENABLE(small-filter, AC_HELP_STRING([--disable-small-filter], [Exclude small filter in resampling]), [if test "$enable_small_filter" = "no"; then [ac_no_small_filter='-DPJMEDIA_HAS_SMALL_FILTER=0'] AC_MSG_RESULT([Checking if small filter is disabled... yes]) fi], AC_MSG_RESULT([Checking if small filter is disabled... no])) dnl # Include resampling large filter AC_SUBST(ac_no_large_filter) AC_ARG_ENABLE(large-filter, AC_HELP_STRING([--disable-large-filter], [Exclude large filter in resampling]), [if test "$enable_large_filter" = "no"; then [ac_no_large_filter='-DPJMEDIA_HAS_LARGE_FILTER=0'] AC_MSG_RESULT([Checking if large filter is disabled... yes]) fi], AC_MSG_RESULT([Checking if large filter is disabled... no])) dnl # Include Speex AEC AC_SUBST(ac_no_speex_aec) AC_ARG_ENABLE(speex-aec, AC_HELP_STRING([--disable-speex-aec], [Exclude Speex Acoustic Echo Canceller/AEC]), [if test "$enable_speex_aec" = "no"; then [ac_no_speex_aec='-DPJMEDIA_HAS_SPEEX_AEC=0'] AC_MSG_RESULT([Checking if Speex AEC is disabled...yes]) fi], AC_MSG_RESULT([Checking if Speex AEC is disabled...no])) dnl # Include G722.1 codec AC_SUBST(ac_no_g7221_codec) AC_ARG_ENABLE(g7221-codec, AC_HELP_STRING([--disable-g7221-codec], [Exclude G.7221 codec in the build]), [if test "$enable_g7221_codec" = "no"; then [ac_no_g7221_codec=1] AC_DEFINE(PJMEDIA_HAS_G7221_CODEC,0) AC_MSG_RESULT([Checking if G.722.1 codec is disabled...yes]) fi], AC_MSG_RESULT([Checking if G.722.1 codec is disabled...no])) dnl # Include libsamplerate AC_ARG_ENABLE(libsamplerate, AC_HELP_STRING([--enable-libsamplerate], [Link with libsamplerate when available. Note that PJMEDIA_RESAMPLE_IMP must also be configured]), [ AC_CHECK_LIB(samplerate,src_new) ], AC_MSG_RESULT([Skipping libsamplerate detection]) ) dnl # Include libsamplerate AC_SUBST(ac_resample_dll) AC_ARG_ENABLE(resample_dll, AC_HELP_STRING([--enable-resample-dll], [Build libresample as shared library]), [if test "$enable_resample_dll" = "yes"; then [ac_resample_dll=1] AC_MSG_RESULT([Building libresample as shared library... yes]) fi], AC_MSG_RESULT([Building libresample as shared library... no]) ) dnl # pkg-config AC_CHECK_PROGS(PKG_CONFIG,pkg-config,none) if test "$PKG_CONFIG" == "none"; then AC_MSG_ERROR([*** Error: pkg-config not found ***]) fi dnl # FFmpeg AC_ARG_WITH(ffmpeg, AC_HELP_STRING([--with-ffmpeg=DIR], [Specify alternate FFMPEG prefix]), [], [with_ffmpeg=check] ) AC_SUBST(ac_ffmpeg_cflags) AC_SUBST(ac_ffmpeg_ldflags) FFMPEG_PREFIX="" AC_SUBST(SAVED_PKG_CONFIG_PATH) SAVED_PKG_CONFIG_PATH=$PKG_CONFIG_PATH if test "x$with_ffmpeg" != "xcheck" -a "x$with_ffmpeg" != "x"; then FFMPEG_PREFIX=$with_ffmpeg AC_MSG_RESULT([Using ffmpeg prefix... $FFMPEG_PREFIX]) export PKG_CONFIG_PATH=$FFMPEG_PREFIX/lib/pkgconfig fi AC_MSG_CHECKING([ffmpeg packages]) av_pkg="" if $PKG_CONFIG --exists libavformat; then ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVFORMAT=1" av_pkg="$av_pkg libavformat" else AC_MSG_ERROR([*** Error: libavformat not detected ***]) fi if $PKG_CONFIG --exists libavcodec; then ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCODEC=1" av_pkg="$av_pkg libavcodec" else AC_MSG_ERROR([*** Error: libavcodec not detected ***]) fi if $PKG_CONFIG --exists libswscale; then ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBSWSCALE=1" av_pkg="$av_pkg libswscale" else AC_MSG_ERROR([*** Error: libswscale not detected ***]) fi if $PKG_CONFIG --exists libavutil; then ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVUTIL=1" av_pkg="$av_pkg libavutil" else AC_MSG_ERROR([*** Error: libavutil not detected ***]) fi if $PKG_CONFIG --exists libavcore; then ac_ffmpeg_cflags="$ac_ffmpeg_cflags -DPJMEDIA_HAS_LIBAVCORE=1" av_pkg="$av_pkg libavcore" fi AC_MSG_RESULT([$av_pkg]) ac_ffmpeg_cflags="$ac_ffmpeg_cflags `$PKG_CONFIG --cflags $av_pkg`" ac_ffmpeg_ldflags="$ac_ffmpeg_ldflags `$PKG_CONFIG --libs $av_pkg`" LIBS="$LIBS $ac_ffmpeg_ldflags" export PKG_CONFIG_PATH=$SAVED_PKG_CONFIG_PATH dnl # libvpx AC_ARG_WITH(vpx, AC_HELP_STRING([--with-vpx=DIR], [Specify alternate libVPX prefix]), [], [with_vpx=check] ) AC_SUBST(ac_vpx_cflags) AC_SUBST(ac_vpx_ldflags) VPX_PREFIX="" AC_SUBST(SAVED_PKG_CONFIG_PATH) SAVED_PKG_CONFIG_PATH=$PKG_CONFIG_PATH if test "x$with_vpx" != "xcheck" -a "x$with_vpx" != "x"; then VPX_PREFIX=$with_vpx AC_MSG_RESULT([Using libVPX prefix... $VPX_PREFIX]) export PKG_CONFIG_PATH=$VPX_PREFIX fi AC_MSG_CHECKING([libvpx packages]) if $PKG_CONFIG --exists vpx; then ac_vpx_cflags="`$PKG_CONFIG --cflags vpx` -DPJMEDIA_HAS_LIBVPX=1" ac_vpx_ldflags="`$PKG_CONFIG --libs vpx`" AC_MSG_RESULT(yes) else AC_MSG_ERROR([*** Error: VPX library not detected ***]) fi LIBS="$LIBS $ac_vpx_ldflags" export PKG_CONFIG_PATH=$SAVED_PKG_CONFIG_PATH dnl # Video for Linux 2 AC_ARG_ENABLE(v4l2, AC_HELP_STRING([--disable-v4l2], [Disable Video4Linux2 (default: not disabled)]), [ if test "$enable_v4l2" = "no"; then AC_MSG_RESULT([Checking if V4L2 is disabled... yes]) fi ], [ AC_SUBST(ac_v4l2_cflags) AC_SUBST(ac_v4l2_ldflags) AC_CHECK_LIB(v4l2, v4l2_open, [ac_v4l2_cflags="-DPJMEDIA_VIDEO_DEV_HAS_V4L2=1" ac_v4l2_ldflags="-lv4l2" LIBS="$LIBS -lv4l2" ] ) ]) dnl # OpenH264 alt prefix AC_ARG_WITH(openh264, AC_HELP_STRING([--with-openh264=DIR], [Specify alternate OpenH264 prefix]), [], [with_openh264=no] ) dnl # Do not use default OpenH264 installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_openh264" = "xno"; then enable_openh264=no fi dnl # OpenH264 AC_SUBST(ac_openh264_cflags) AC_SUBST(ac_openh264_ldflags) AC_ARG_ENABLE(openh264, AC_HELP_STRING([--disable-openh264], [Disable OpenH264 (default: not disabled)]), [ if test "$enable_openh264" = "no"; then AC_MSG_RESULT([Checking if OpenH264 is disabled... yes]) fi ], [ if test "x$with_openh264" != "xno" -a "x$with_openh264" != "x"; then OPENH264_PREFIX=$with_openh264 OPENH264_CFLAGS="-I$OPENH264_PREFIX/include" OPENH264_LDFLAGS="-L$OPENH264_PREFIX/lib" AC_MSG_RESULT([Using OpenH264 prefix... $with_openh264]) else OPENH264_CFLAGS="" OPENH264_LDFLAGS="" fi AC_MSG_CHECKING([OpenH264 usability]) OPENH264_LIBS="-lopenh264" SAVED_LIBS="$LIBS" SAVED_LDFLAGS="$LDFLAGS" SAVED_CFLAGS="$CFLAGS" LIBS="$OPENH264_LIBS $LIBS" LDFLAGS="$OPENH264_LDFLAGS $LDFLAGS -lstdc++" CFLAGS="$OPENH264_CFLAGS $CFLAGS" AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include #include ]], [WelsCreateSVCEncoder(0);] )], [ ac_openh264_cflags="-DPJMEDIA_HAS_OPENH264_CODEC=1 $OPENH264_CFLAGS" ac_openh264_ldflags="$OPENH264_LDFLAGS $OPENH264_LIBS" AC_MSG_RESULT(ok) ], [ LIBS="$SAVED_LIBS" LDFLAGS="$SAVED_LDFLAGS" CFLAGS="$SAVED_CFLAGS" AC_MSG_RESULT(no) ]) ]) dnl # libyuv alt prefix AC_ARG_WITH(libyuv, AC_HELP_STRING([--with-libyuv=DIR], [Specify alternate libyuv prefix]), [], [with_libyuv=no] ) dnl # Do not use default libyuv installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_libyuv" = "xno"; then enable_libyuv=no fi dnl # Include libyuv AC_SUBST(ac_libyuv_cflags) AC_SUBST(ac_libyuv_ldflags) AC_ARG_ENABLE(libyuv, AC_HELP_STRING([--disable-libyuv], [Exclude libyuv in the build]), [if test "$enable_libyuv" = "no"; then AC_MSG_RESULT([Checking if libyuv is disabled...yes]) fi], [ if test "x$with_libyuv" != "xno" -a "x$with_libyuv" != "x"; then LIBYUV_PREFIX=$with_libyuv LIBYUV_CFLAGS="-I$LIBYUV_PREFIX/include" case $target in arm-apple-darwin*) LIBYUV_LDFLAGS="-L$LIBYUV_PREFIX/out_ios/Release-iphoneos" case $ARCH in *armv7*) LIBYUV_LIBS="-lyuv_neon" ;; *) ;; esac ;; *mingw* | *cygw* | *win32* | *w32* | *darwin* | *linux* | *android*) LIBYUV_LDFLAGS="-L$LIBYUV_PREFIX/out/Release" ;; *) LIBYUV_CFLAGS="" LIBYUV_LDFLAGS="" ;; esac AC_MSG_RESULT([Using libyuv prefix... $with_libyuv]) else LIBYUV_CFLAGS="" LIBYUV_LDFLAGS="" fi LIBYUV_LIBS="$LIBYUV_LIBS -lyuv" SAVED_LIBS="$LIBS" SAVED_LDFLAGS="$LDFLAGS" SAVED_CFLAGS="$CFLAGS" LIBS="$LIBYUV_LIBS $LIBS" LDFLAGS="$LIBYUV_LDFLAGS $LDFLAGS" CFLAGS="$LIBYUV_CFLAGS $CFLAGS" AC_CHECK_LIB(yuv, I420Scale, [ ac_libyuv_cflags="-DPJMEDIA_HAS_LIBYUV=1 $LIBYUV_CFLAGS" ac_libyuv_ldflags="$LIBYUV_LDFLAGS $LIBYUV_LIBS" ], [ LIBS="$SAVED_LIBS" LDFLAGS="$SAVED_LDFLAGS" CFLAGS="$SAVED_CFLAGS" ], [] ) ]) dnl ########################################## dnl # dnl # WebRTC AEC Support dnl # AC_SUBST(ac_webrtc_platform) case $target in *darwin*) ac_webrtc_platform=webrtc_darwinos ;; *mingw*) ac_webrtc_platform=webrtc_win32 ;; *linux*) ac_webrtc_platform=webrtc_linux ;; *) ac_webrtc_platform=null ;; esac dnl ########################################## dnl # dnl # PJSIP CONFIG dnl # dnl # SSL alt prefix AC_ARG_WITH(ssl, AC_HELP_STRING([--with-ssl=DIR], [Specify alternate libssl prefix]), [], [with_ssl=no] ) dnl # Do not use default SSL installation if we are cross-compiling if test "x$ac_cross_compile" != "x" -a "x$with_ssl" = "xno"; then enable_ssl=no fi dnl # Include SSL support AC_SUBST(ac_no_ssl) AC_ARG_ENABLE(ssl, AC_HELP_STRING([--disable-ssl], [Exclude SSL support the build (default: autodetect)]) , [ if test "$enable_ssl" = "no"; then [ac_no_ssl=1] AC_MSG_RESULT([Checking if SSL support is disabled... yes]) fi ], [ AC_MSG_RESULT([checking for OpenSSL installations..]) if test "x$with_ssl" != "xno" -a "x$with_ssl" != "x"; then CFLAGS="$CFLAGS -I$with_ssl/include" LDFLAGS="$LDFLAGS -L$with_ssl/lib" AC_MSG_RESULT([Using SSL prefix... $with_ssl]) fi AC_SUBST(openssl_h_present) AC_SUBST(libssl_present) AC_SUBST(libcrypto_present) AC_CHECK_HEADER(openssl/ssl.h,[openssl_h_present=1]) AC_CHECK_LIB(crypto,ERR_load_BIO_strings,[libcrypto_present=1 && LIBS="-lcrypto $LIBS"]) AC_CHECK_LIB(ssl,SSL_CTX_new,[libssl_present=1 && LIBS="-lssl $LIBS"]) if test "x$openssl_h_present" = "x1" -a "x$libssl_present" = "x1" -a "x$libcrypto_present" = "x1"; then AC_MSG_RESULT([OpenSSL library found, SSL support enabled]) # PJSIP_HAS_TLS_TRANSPORT setting follows PJ_HAS_SSL_SOCK #AC_DEFINE(PJSIP_HAS_TLS_TRANSPORT, 1) AC_DEFINE(PJ_HAS_SSL_SOCK, 1) else AC_MSG_RESULT([** OpenSSL libraries not found, disabling SSL support **]) fi ]) dnl ########################################## dnl # dnl # MANUAL CONFIG dnl # dnl # Determine if select() requires nfds to be filled up with dnl # correct value (max_fd+1). If zero, nfds will be filled up with dnl # PJ_FD_SETSIZE AC_MSG_CHECKING([if select() needs correct nfds]) case $target in *rtems*) AC_DEFINE(PJ_SELECT_NEEDS_NFDS,1) AC_MSG_RESULT(yes) ;; *) AC_DEFINE(PJ_SELECT_NEEDS_NFDS,0) AC_MSG_RESULT([no (default)]) AC_MSG_RESULT([** Decided that select() doesn't need correct nfds (please check)]) ;; esac dnl # Determine if pj_thread_create() should enforce thread stack size when dnl # creating thread. Default is zero, to allow OS to allocate appropriate dnl # thread's stack size. AC_MSG_CHECKING([if pj_thread_create() should enforce stack size]) case $target in *rtems*) AC_DEFINE(PJ_THREAD_SET_STACK_SIZE,1) AC_MSG_RESULT(yes) ;; *) AC_DEFINE(PJ_THREAD_SET_STACK_SIZE,0) AC_MSG_RESULT([no (default)]) ;; esac dnl # Determine if pj_thread_create() should allocate thread's stack from dnl # the pool. Default is zero, to let OS allocate thread's stack. AC_MSG_CHECKING([if pj_thread_create() should allocate stack]) case $target in *rtems*) AC_DEFINE(PJ_THREAD_ALLOCATE_STACK,1) AC_MSG_RESULT(yes) ;; *) AC_DEFINE(PJ_THREAD_ALLOCATE_STACK,0) AC_MSG_RESULT([no (default)]) ;; esac dnl # This value specifies the value set in errno by the OS when a non-blocking dnl # socket recv() can not return immediate data. case $target in *mingw* | *cygw* | *win32* | *w32* ) AC_DEFINE(PJ_BLOCKING_ERROR_VAL,WSAEWOULDBLOCK) ;; *) AC_DEFINE(PJ_BLOCKING_ERROR_VAL,EAGAIN) AC_MSG_RESULT([** Setting non-blocking recv() retval to EAGAIN (please check)]) ;; esac dnl # This value specifies the value set in errno by the OS when a non-blocking dnl # socket connect() can not get connected immediately. case $target in *mingw* | *cygw* | *win32* | *w32* ) AC_DEFINE(PJ_BLOCKING_CONNECT_ERROR_VAL,WSAEWOULDBLOCK) ;; *) AC_DEFINE(PJ_BLOCKING_CONNECT_ERROR_VAL,EINPROGRESS) AC_MSG_RESULT([** Setting non-blocking connect() retval to EINPROGRESS (please check)]) ;; esac AC_SUBST(target) AC_SUBST(ac_linux_poll,select) AC_SUBST(ac_host,unix) AC_SUBST(ac_main_obj) case $target in *rtems*) ac_main_obj=main_rtems.o ;; *) ac_main_obj=main.o ;; esac AC_SUBST(CC) ac_build_mak_vars=`echo $ac_build_mak_vars | sed 's/\\\\n/\n/g'` AC_OUTPUT() AC_MSG_RESULT([ Configurations for current target have been written to 'build.mak', and 'os-auto.mak' in various build directories, and pjlib/include/pj/compat/os_auto.h. Further customizations can be put in: - 'user.mak' - 'pjlib/include/pj/config_site.h' The next step now is to run 'make dep' and 'make'. ]) ================================================ FILE: deps/pjsip/base_rev ================================================ 5249 ================================================ FILE: deps/pjsip/build/cc-auto.mak.in ================================================ export CC = @CC@ -c export CXX = @CXX@ -c export AR = @AR@ export AR_FLAGS = @AR_FLAGS@ export LD = @LD@ export LDOUT = -o export RANLIB = @RANLIB@ export OBJEXT := .@OBJEXT@ export LIBEXT := .@LIBEXT@ export LIBEXT2 := @LIBEXT2@ export CC_OUT := @CC_OUT@ export CC_INC := @CC_INC@ export CC_DEF := @CC_DEF@ export CC_OPTIMIZE := @CC_OPTIMIZE@ export CC_LIB := -l export CC_SOURCES := export CC_CFLAGS := @CC_CFLAGS@ export CC_LDFLAGS := ================================================ FILE: deps/pjsip/build/cc-gcc.mak ================================================ export CC = $(CROSS_COMPILE)gcc -c export AR = $(CROSS_COMPILE)ar rv export LD = $(CROSS_COMPILE)gcc export LDOUT = -o export RANLIB = $(CROSS_COMPILE)ranlib export OBJEXT := .o export LIBEXT := .a export LIBEXT2 := export CC_OUT := -o export CC_INC := -I export CC_DEF := -D export CC_OPTIMIZE := -O2 export CC_LIB := -l export CC_SOURCES := export CC_CFLAGS := -Wall #export CC_CFLAGS += -Wdeclaration-after-statement #export CC_CXXFLAGS := -Wdeclaration-after-statement export CC_LDFLAGS := ================================================ FILE: deps/pjsip/build/common.mak ================================================ # # Include host/target/compiler selection. # This will export CC_NAME, MACHINE_NAME, OS_NAME, and HOST_NAME variables. # include $(PJDIR)/build.mak # # Include global compiler specific definitions # include $(PJDIR)/build/cc-$(CC_NAME).mak # # (Optionally) Include compiler specific configuration that is # specific to this project. This configuration file is # located in this directory. # -include cc-$(CC_NAME).mak # # Include auto configured compiler specification. # This will override the compiler settings above. # Currently this is made OPTIONAL, to prevent people # from getting errors because they don't re-run ./configure # after downloading new PJSIP. # -include $(PJDIR)/build/cc-auto.mak # # Include global machine specific definitions # include $(PJDIR)/build/m-$(MACHINE_NAME).mak -include m-$(MACHINE_NAME).mak # # Include target OS specific definitions # include $(PJDIR)/build/os-$(OS_NAME).mak # # (Optionally) Include target OS specific configuration that is # specific to this project. This configuration file is # located in this directory. # -include os-$(OS_NAME).mak # # Include host specific definitions # include $(PJDIR)/build/host-$(HOST_NAME).mak # # (Optionally) Include host specific configuration that is # specific to this project. This configuration file is # located in this directory. # -include host-$(HOST_NAME).mak # # Include global user configuration, if any # -include $(PJDIR)/user.mak ================================================ FILE: deps/pjsip/build/host-mingw.mak ================================================ export HOST_MV := mv export HOST_RM := rm -f @@ export HOST_RMR := rm -rf @@ export HOST_RMDIR := rm -rf @@ export HOST_MKDIR := mkdir @@ export HOST_EXE := .exe export HOST_PSEP := / export HOST_SOURCES := export HOST_CFLAGS := export HOST_CXXFLAGS := export HOST_LDFLAGS := $(CC_LIB)stdc++$(LIBEXT2) ================================================ FILE: deps/pjsip/build/host-unix.mak ================================================ export HOST_MV := mv export HOST_RM := rm -f @@ export HOST_RMR := rm -rf @@ export HOST_RMDIR := rm -rf @@ export HOST_MKDIR := mkdir -p @@ export HOST_EXE := $(HOST_EXE) export HOST_PSEP := / export HOST_SOURCES := export HOST_CFLAGS := export HOST_CXXFLAGS := export HOST_LDFLAGS := ================================================ FILE: deps/pjsip/build/host-win32.mak ================================================ export HOST_MV := ren export HOST_RM := if exist @@; del /F /Q @@ export HOST_RMR := if exist @@; del /F /Q @@ export HOST_RMDIR := if exist @@; rmdir @@ export HOST_MKDIR := if not exist @@; mkdir @@ export HOST_EXE := .exe export HOST_PSEP := \\ export HOST_SOURCES := export HOST_CFLAGS := export HOST_CXXFLAGS := export HOST_LDFLAGS := ================================================ FILE: deps/pjsip/build/m-arm.mak ================================================ export M_CFLAGS := $(CC_DEF)PJ_M_ARMV4=1 export M_CXXFLAGS := export M_LDFLAGS := export M_SOURCES := ================================================ FILE: deps/pjsip/build/m-auto.mak ================================================ # Nothing needs to be defined here ================================================ FILE: deps/pjsip/build/m-i386.mak ================================================ export M_CFLAGS := $(CC_DEF)PJ_M_I386=1 export M_CXXFLAGS := export M_LDFLAGS := export M_SOURCES := ================================================ FILE: deps/pjsip/build/m-x86_64.mak ================================================ export M_CFLAGS := $(CC_DEF)PJ_M_X86_64=1 export M_CXXFLAGS := export M_LDFLAGS := export M_SOURCES := ================================================ FILE: deps/pjsip/build/os-auto.mak.in ================================================ # @configure_input@ export OS_CFLAGS := $(CC_DEF)PJ_AUTOCONF=1 @CFLAGS@ export OS_CXXFLAGS := $(CC_DEF)PJ_AUTOCONF=1 @CXXFLAGS@ export OS_LDFLAGS := @LDFLAGS@ @LIBS@ export OS_SOURCES := ================================================ FILE: deps/pjsip/build/rules.mak ================================================ ifeq ($(LIBDIR),) LIBDIR = ../lib endif ifeq ($(BINDIR),) BINDIR = ../bin endif # # The full path of output lib file (e.g. ../lib/libapp.a). # LIB = $($(APP)_LIB) # # The full path of output lib file (e.g. ../lib/libapp.a). # SHLIB = $($(APP)_SHLIB) # # The full path of output executable file (e.g. ../bin/app.exe). # EXE = $($(APP)_EXE) # # Source directory # SRCDIR = $($(APP)_SRCDIR) # # Output directory for object files (i.e. output/target) # OBJDIR = output/$(app)-$(TARGET_NAME) ifeq ($(OS_NAME),linux-kernel) export $(APP)_CFLAGS += -DKBUILD_MODNAME=$(app) -DKBUILD_BASENAME=$(app) endif # # OBJS is ./output/target/file.o # OBJS = $(foreach file, $($(APP)_OBJS), $(OBJDIR)/$(file)) OBJDIRS := $(sort $(dir $(OBJS))) # # FULL_SRCS is ../src/app/file1.c ../src/app/file1.S # FULL_SRCS = $(foreach file, $($(APP)_OBJS), $(SRCDIR)/$(basename $(file)).m $(SRCDIR)/$(basename $(file)).c $(SRCDIR)/$(basename $(file)).cpp $(SRCDIR)/$(basename $(file)).S) # # When generating dependency (gcc -MM), ideally we use only either # CFLAGS or CXXFLAGS (not both). But I just couldn't make if/ifeq to work. # #DEPFLAGS = $($(APP)_CXXFLAGS) $($(APP)_CFLAGS) DEPCFLAGS = $($(APP)_CFLAGS) DEPCXXFLAGS = $($(APP)_CXXFLAGS) # Dependency file DEP_FILE := .$(app)-$(TARGET_NAME).depend print_common: @echo "###" @echo "### DUMPING MAKE VARIABLES (I WON'T DO ANYTHING ELSE):" @echo "###" @echo APP=$(APP) @echo OBJDIR=$(OBJDIR) @echo OBJDIRS=$(OBJDIRS) @echo OBJS=$(OBJS) @echo SRCDIR=$(SRCDIR) @echo FULL_SRCS=$(FULL_SRCS) @echo $(APP)_CFLAGS=$($(APP)_CFLAGS) @echo $(APP)_CXXFLAGS=$($(APP)_CXXFLAGS) @echo $(APP)_LDFLAGS=$($(APP)_LDFLAGS) # @echo DEPFLAGS=$(DEPFLAGS) @echo CC=$(CC) @echo AR=$(AR) @echo AR_FLAGS=$(AR_FLAGS) @echo RANLIB=$(RANLIB) print_bin: print_common @echo EXE=$(EXE) @echo BINDIR=$(BINDIR) print_lib: print_common @echo LIB=$(LIB) @echo LIBDIR=$(LIBDIR) $(LIB): $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP) if test ! -d $(LIBDIR); then $(subst @@,$(subst /,$(HOST_PSEP),$(LIBDIR)),$(HOST_MKDIR)); fi $(AR) $(AR_FLAGS) $(LIB) $(OBJS) $(RANLIB) $(LIB) $(SHLIB): $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP) if test ! -d $(LIBDIR); then $(subst @@,$(subst /,$(HOST_PSEP),$(LIBDIR)),$(HOST_MKDIR)); fi $(LD) $(LDOUT)$(subst /,$(HOST_PSEP),$(SHLIB)) \ $(subst /,$(HOST_PSEP),$(OBJS)) $($(APP)_LDFLAGS) $(EXE): $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP) if test ! -d $(BINDIR); then $(subst @@,$(subst /,$(HOST_PSEP),$(BINDIR)),$(HOST_MKDIR)); fi $(LD) $(LDOUT)$(subst /,$(HOST_PSEP),$(EXE)) \ $(subst /,$(HOST_PSEP),$(OBJS)) $($(APP)_LDFLAGS) $(OBJDIR)/$(app).o: $(OBJDIRS) $(OBJS) $(CROSS_COMPILE)ld -r -o $@ $(OBJS) $(OBJDIR)/$(app).ko: $(OBJDIR)/$(app).o @echo Creating kbuild Makefile... @echo "# Our module name:" > $(OBJDIR)/Makefile @echo 'obj-m += $(app).o' >> $(OBJDIR)/Makefile @echo >> $(OBJDIR)/Makefile @echo "# Object members:" >> $(OBJDIR)/Makefile @echo -n '$(app)-objs += ' >> $(OBJDIR)/Makefile @for file in $($(APP)_OBJS); do \ echo -n "$$file " >> $(OBJDIR)/Makefile; \ done @echo >> $(OBJDIR)/Makefile @echo >> $(OBJDIR)/Makefile @echo "# Prevent .o files to be built by kbuild:" >> $(OBJDIR)/Makefile @for file in $($(APP)_OBJS); do \ echo ".PHONY: `pwd`/$(OBJDIR)/$$file" >> $(OBJDIR)/Makefile; \ done @echo >> $(OBJDIR)/Makefile @echo all: >> $(OBJDIR)/Makefile @echo -e "\tmake -C $(KERNEL_DIR) M=`pwd`/$(OBJDIR) modules $(KERNEL_ARCH)" >> $(OBJDIR)/Makefile @echo Invoking kbuild... make -C $(OBJDIR) ../lib/$(app).ko: $(LIB) $(OBJDIR)/$(app).ko cp $(OBJDIR)/$(app).ko ../lib $(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.m $(CC) $($(APP)_CFLAGS) \ $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ $(subst /,$(HOST_PSEP),$<) $(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.c $(CC) $($(APP)_CFLAGS) \ $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ $(subst /,$(HOST_PSEP),$<) $(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.S $(CC) $($(APP)_CFLAGS) \ $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ $(subst /,$(HOST_PSEP),$<) $(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cpp $(CC) $($(APP)_CXXFLAGS) \ $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ $(subst /,$(HOST_PSEP),$<) $(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cc $(CC) $($(APP)_CXXFLAGS) \ $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ $(subst /,$(HOST_PSEP),$<) $(OBJDIRS): $(subst @@,$(subst /,$(HOST_PSEP),$@),$(HOST_MKDIR)) $(LIBDIR): $(subst @@,$(subst /,$(HOST_PSEP),$(LIBDIR)),$(HOST_MKDIR)) $(BINDIR): $(subst @@,$(subst /,$(HOST_PSEP),$(BINDIR)),$(HOST_MKDIR)) clean: $(subst @@,$(subst /,$(HOST_PSEP),$(OBJDIR)/*),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),$(OBJDIR)),$(HOST_RMDIR)) ifeq ($(OS_NAME),linux-kernel) rm -f ../lib/$(app).o endif gcov-report: for file in $(FULL_SRCS); do \ gcov $$file -n -o $(OBJDIR); \ done realclean: clean $(subst @@,$(subst /,$(HOST_PSEP),$(LIB)) $(subst /,$(HOST_PSEP),$(EXE)),$(HOST_RM)) $(subst @@,$(DEP_FILE),$(HOST_RM)) ifeq ($(OS_NAME),linux-kernel) rm -f ../lib/$(app).ko endif depend: $(subst @@,$(DEP_FILE),$(HOST_RM)) for F in $(FULL_SRCS); do \ if test -f $$F; then \ echo "$(OBJDIR)/" | tr -d '\n' >> $(DEP_FILE); \ if echo $$F | grep -q .cpp$$; then \ dep="$(CC) -M $(DEPCXXFLAGS) $$F"; \ else \ dep="$(CC) -M $(DEPCFLAGS) $$F"; \ fi; \ if eval $$dep | sed '/^#/d' >> $(DEP_FILE); then \ true; \ else \ echo 'err:' >> $(DEP_FILE); \ rm -f $(DEP_FILE); \ exit 1; \ fi; \ fi; \ done; dep: depend -include $(DEP_FILE) ================================================ FILE: deps/pjsip/build.mak.in ================================================ export PJDIR := @ac_pjdir@ include $(PJDIR)/version.mak export PJ_DIR := $(PJDIR) # @configure_input@ export MACHINE_NAME := auto export OS_NAME := auto export HOST_NAME := unix export CC_NAME := gcc export TARGET_NAME := @target@ export CROSS_COMPILE := @ac_cross_compile@ export LINUX_POLL := @ac_linux_poll@ export SHLIB_SUFFIX := @ac_shlib_suffix@ export ac_prefix := @prefix@ LIB_SUFFIX := $(TARGET_NAME).a # Determine which party libraries to use export APP_THIRD_PARTY_LIBS := -lsrtp-$(TARGET_NAME) export APP_THIRD_PARTY_EXT := export APP_THIRD_PARTY_LIB_FILES := $(PJ_DIR)/third_party/lib/libsrtp-$(LIB_SUFFIX) ifeq (@ac_resample_dll@,1) export PJ_RESAMPLE_DLL := 1 export APP_THIRD_PARTY_LIBS := -lresample $(APP_THIRD_PARTY_LIBS) export APP_THIRD_PARTY_LIB_FILES := $(PJ_DIR)/third_party/lib/libresample.$(SHLIB_SUFFIX).$(PJ_VERSION_MAJOR) $(PJ_DIR)/third_party/lib/libresample.$(SHLIB_SUFFIX) $(APP_THIRD_PARTY_LIB_FILES) else export APP_THIRD_PARTY_LIBS := -lresample-$(TARGET_NAME) $(APP_THIRD_PARTY_LIBS) export APP_THIRD_PARTY_LIB_FILES := $(PJ_DIR)/third_party/lib/libresample-$(LIB_SUFFIX) $(APP_THIRD_PARTY_LIB_FILES) endif # WebRTC APP_THIRD_PARTY_LIBS += -lwebrtcaec-$(TARGET_NAME) APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libwebrtcaec-$(LIB_SUFFIX) # Opus ifneq (@ac_no_opus_codec@,1) APP_THIRD_PARTY_LIBS += -lopuscodec-$(TARGET_NAME) APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libopuscodec-$(LIB_SUFFIX) endif # ZRTP APP_THIRD_PARTY_LIBS += -lzsrtp-$(TARGET_NAME) -lsqlite3 -lstdc++ APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libzsrtp-$(LIB_SUFFIX) ifneq (@ac_no_gsm_codec@,1) ifeq (@ac_external_gsm@,1) # External GSM library APP_THIRD_PARTY_EXT += -lgsm else APP_THIRD_PARTY_LIBS += -lgsmcodec-$(TARGET_NAME) APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libgsmcodec-$(LIB_SUFFIX) endif endif ifneq (@ac_no_speex_codec@,1) ifeq (@ac_external_speex@,1) APP_THIRD_PARTY_EXT += -lspeex -lspeexdsp else APP_THIRD_PARTY_LIBS += -lspeex-$(TARGET_NAME) APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libspeex-$(LIB_SUFFIX) endif endif ifneq (@ac_no_ilbc_codec@,1) APP_THIRD_PARTY_LIBS += -lilbccodec-$(TARGET_NAME) APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libilbccodec-$(LIB_SUFFIX) endif ifneq (@ac_no_g7221_codec@,1) APP_THIRD_PARTY_LIBS += -lg7221codec-$(TARGET_NAME) APP_THIRD_PARTY_LIB_FILES += $(PJ_DIR)/third_party/lib/libg7221codec-$(LIB_SUFFIX) endif # Additional flags @ac_build_mak_vars@ # # Video # Note: there are duplicated macros in pjmedia/os-auto.mak.in (and that's not # good! # FFMPEG flags FFMPEG_CFLAGS = @ac_ffmpeg_cflags@ FFMPEG_LDFLAGS = @ac_ffmpeg_ldflags@ # VPX flags VPX_CFLAGS = @ac_vpx_cflags@ VPX_LDFLAGS = @ac_vpx_ldflags@ # Video4Linux2 V4L2_CFLAGS = @ac_v4l2_cflags@ V4L2_LDFLAGS = @ac_v4l2_ldflags@ # OPENH264 flags OPENH264_CFLAGS = @ac_openh264_cflags@ OPENH264_LDFLAGS = @ac_openh264_ldflags@ # AVF AC_PJMEDIA_VIDEO_HAS_AVF = @ac_pjmedia_video_has_avf@ AVF_CFLAGS = @ac_avf_cflags@ # iOS IOS_CFLAGS = @ac_ios_cflags@ # Dshow AC_PJMEDIA_VIDEO_HAS_DSHOW = @ac_pjmedia_video_has_dshow@ DSHOW_CFLAGS = @ac_dshow_cflags@ DSHOW_LDFLAGS = @ac_dshow_ldflags@ # Android ANDROID_CFLAGS = @ac_android_cflags@ # libyuv LIBYUV_CFLAGS = @ac_libyuv_cflags@ LIBYUV_LDFLAGS = @ac_libyuv_ldflags@ # PJMEDIA features exclusion PJ_VIDEO_CFLAGS += $(FFMPEG_CFLAGS) $(V4L2_CFLAGS) $(AVF_CFLAGS) \ $(OPENH264_CFLAGS) $(IOS_CFLAGS) $(DSHOW_CFLAGS) $(LIBYUV_CFLAGS) \ $(VPX_CFLAGS) PJ_VIDEO_LDFLAGS += $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS) \ $(OPENH264_LDFLAGS) $(DSHOW_LDFLAGS) $(LIBYUV_LDFLAGS) \ $(VPX_LDFLAGS) # CFLAGS, LDFLAGS, and LIBS to be used by applications export APP_CC := @CC@ export APP_CXX := @CXX@ export APP_CFLAGS := -DPJ_AUTOCONF=1\ @CFLAGS@\ $(PJ_VIDEO_CFLAGS) \ -I$(PJDIR)/pjlib/include\ -I$(PJDIR)/pjlib-util/include\ -I$(PJDIR)/pjnath/include\ -I$(PJDIR)/pjmedia/include\ -I$(PJDIR)/pjsip/include export APP_CXXFLAGS := $(APP_CFLAGS) export APP_LDFLAGS := -L$(PJDIR)/pjlib/lib\ -L$(PJDIR)/pjlib-util/lib\ -L$(PJDIR)/pjnath/lib\ -L$(PJDIR)/pjmedia/lib\ -L$(PJDIR)/pjsip/lib\ -L$(PJDIR)/third_party/lib\ $(PJ_VIDEO_LDFLAGS) \ @LDFLAGS@ export APP_LDXXFLAGS := $(APP_LDFLAGS) export APP_LDLIBS := \ -lpjsip-ua-$(TARGET_NAME)\ -lpjsip-simple-$(TARGET_NAME)\ -lpjsip-$(TARGET_NAME)\ -lpjmedia-codec-$(TARGET_NAME)\ -lpjmedia-videodev-$(TARGET_NAME)\ -lpjmedia-$(TARGET_NAME)\ -lpjmedia-audiodev-$(TARGET_NAME)\ -lpjnath-$(TARGET_NAME)\ -lpjlib-util-$(TARGET_NAME)\ $(APP_THIRD_PARTY_LIBS)\ $(APP_THIRD_PARTY_EXT)\ -lpj-$(TARGET_NAME)\ @LIBS@ export APP_LIB_FILES := \ $(PJ_DIR)/pjsip/lib/libpjsip-ua-$(LIB_SUFFIX) \ $(PJ_DIR)/pjsip/lib/libpjsip-simple-$(LIB_SUFFIX) \ $(PJ_DIR)/pjsip/lib/libpjsip-$(LIB_SUFFIX) \ $(PJ_DIR)/pjmedia/lib/libpjmedia-codec-$(LIB_SUFFIX) \ $(PJ_DIR)/pjmedia/lib/libpjmedia-videodev-$(LIB_SUFFIX) \ $(PJ_DIR)/pjmedia/lib/libpjmedia-$(LIB_SUFFIX) \ $(PJ_DIR)/pjmedia/lib/libpjmedia-audiodev-$(LIB_SUFFIX) \ $(PJ_DIR)/pjmedia/lib/libpjsdp-$(LIB_SUFFIX) \ $(PJ_DIR)/pjnath/lib/libpjnath-$(LIB_SUFFIX) \ $(PJ_DIR)/pjlib-util/lib/libpjlib-util-$(LIB_SUFFIX) \ $(APP_THIRD_PARTY_LIB_FILES) \ $(PJ_DIR)/pjlib/lib/libpj-$(LIB_SUFFIX) # Here are the variabels to use if application is using the library # from within the source distribution export PJ_CC := $(APP_CC) export PJ_CXX := $(APP_CXX) export PJ_CFLAGS := $(APP_CFLAGS) export PJ_CXXFLAGS := $(APP_CXXFLAGS) export PJ_LDFLAGS := $(APP_LDFLAGS) export PJ_LDLIBS := $(APP_LDLIBS) export PJ_LIB_FILES := $(APP_LIB_FILES) # And here are the variables to use if application is using the # library from the install location (i.e. --prefix) export PJ_INSTALL_DIR := @prefix@ export PJ_INSTALL_INC_DIR := $(PJ_INSTALL_DIR)/include export PJ_INSTALL_LIB_DIR := $(PJ_INSTALL_DIR)/lib export PJ_INSTALL_CFLAGS := -I$(PJ_INSTALL_INC_DIR) -DPJ_AUTOCONF=1 @CFLAGS@ export PJ_INSTALL_CXXFLAGS := $(PJ_INSTALL_CFLAGS) export PJ_INSTALL_LDFLAGS := -L$(PJ_INSTALL_LIB_DIR) $(APP_LDLIBS) ================================================ FILE: deps/pjsip/config.guess ================================================ #!/bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012 Free Software Foundation, Inc. timestamp='2012-06-17' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Per Bothner. Please send patches (context # diff format) to and include a ChangeLog # entry. # # This script attempts to guess a canonical system name similar to # config.sub. If it succeeds, it prints the system name on stdout, and # exits with 0. Otherwise, it exits with 1. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm:riscos:*:*|arm:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; i*:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-gnu else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-gnueabi else echo ${UNAME_MACHINE}-unknown-linux-gnueabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-gnu exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-gnu exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:Linux:*:*) LIBC=gnu eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __dietlibc__ LIBC=dietlibc #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` echo "${UNAME_MACHINE}-pc-linux-${LIBC}" exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } ;; or32:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; padre:Linux:*:*) echo sparc-unknown-linux-gnu exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-gnu exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-gnu ;; PA8*) echo hppa2.0-unknown-linux-gnu ;; *) echo hppa-unknown-linux-gnu ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-gnu exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-gnu exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-gnu exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown case $UNAME_PROCESSOR in i386) eval $set_cc_for_build if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then UNAME_PROCESSOR="x86_64" fi fi ;; unknown) UNAME_PROCESSOR=powerpc ;; esac echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; esac #echo '(No uname command or uname output not recognized.)' 1>&2 #echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix\n"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; c34*) echo c34-convex-bsd exit ;; c38*) echo c38-convex-bsd exit ;; c4*) echo c4-convex-bsd exit ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: ================================================ FILE: deps/pjsip/config.sub ================================================ #!/bin/sh # Configuration validation subroutine script. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012 Free Software Foundation, Inc. timestamp='2012-06-17' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software # can handle that machine. It does not imply ALL GNU software can. # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Please send patches to . Submit a context # diff and a properly formatted GNU ChangeLog entry. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ | be32 | be64 \ | bfin \ | c4x | clipper \ | d10v | d30v | dlx | dsp16xx \ | epiphany \ | fido | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 \ | ns16k | ns32k \ | open8 \ | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze) basic_machine=microblaze-xilinx ;; mingw32) basic_machine=i386-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i386-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -kaos*) os=-kaos ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: ================================================ FILE: deps/pjsip/configure ================================================ #!/bin/sh ./aconfigure "$@" # Note: # if you're looking for the old configure script, it has been renamed # to configure-legacy ================================================ FILE: deps/pjsip/install-sh ================================================ #!/bin/sh # install - install a program, script, or datafile # This comes from X11R5 (mit/util/scripts/install.sh). # # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. It can only install one file at a time, a restriction # shared with many OS's install programs. # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else : fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: chmodcmd="" else instcmd=$mkdirprog fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f "$src" ] || [ -d "$src" ] then : else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else : fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else : fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else : fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else : ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else : ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else : ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else : ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else : fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else :;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else :;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else :;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else :;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 ================================================ FILE: deps/pjsip/pjlib/build/Makefile ================================================ include ../../build.mak include ../../version.mak include $(PJDIR)/build/common.mak RULES_MAK := $(PJDIR)/build/rules.mak export PJLIB_LIB := ../lib/libpj-$(TARGET_NAME)$(LIBEXT) ############################################################################### # Gather all flags. # export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ $(CFLAGS) $(CC_INC)../include export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ $(HOST_CXXFLAGS) $(CXXFLAGS) export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJLIB_LIB)) \ $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ $(LDFLAGS) ############################################################################### # Defines for building PJLIB library # export PJLIB_SRCDIR = ../src/pj export PJLIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ activesock.o array.o config.o ctype.o errno.o except.o fifobuf.o \ guid.o hash.o ip_helper_generic.o list.o lock.o log.o os_time_common.o \ os_info.o pool.o pool_buf.o pool_caching.o pool_dbg.o rand.o \ rbtree.o sock_common.o sock_qos_common.o sock_qos_bsd.o \ ssl_sock_common.o ssl_sock_ossl.o ssl_sock_dump.o \ string.o timer.o types.o export PJLIB_CFLAGS += $(_CFLAGS) ############################################################################### # Defines for building test application # export TEST_SRCDIR = ../src/pjlib-test export TEST_OBJS += activesock.o atomic.o echo_clt.o errno.o exception.o \ fifobuf.o file.o hash_test.o ioq_perf.o ioq_udp.o \ ioq_unreg.o ioq_tcp.o \ list.o mutex.o os.o pool.o pool_perf.o rand.o rbtree.o \ select.o sleep.o sock.o sock_perf.o ssl_sock.o \ string.o test.o thread.o timer.o timestamp.o \ udp_echo_srv_sync.o udp_echo_srv_ioqueue.o \ util.o export TEST_CFLAGS += $(_CFLAGS) export TEST_LDFLAGS += $(_LDFLAGS) export TEST_EXE := ../bin/pjlib-test-$(TARGET_NAME)$(HOST_EXE) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Main entry # # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. # all: $(TARGETS) doc: cd .. && rm -rf docs/$(PJ_VERSION) && doxygen docs/doxygen.cfg @if [ -n "$(WWWDIR)" ] && ! [ -d "$(WWWDIR)/docs/$(PJ_VERSION)/pjlib/docs/html" ] ; then \ echo "Creating docs/$(PJ_VERSION)/pjlib/docs/html" ; \ mkdir -p $(WWWDIR)/docs/$(PJ_VERSION)/pjlib/docs/html ; \ fi @if [ -n "$(WWWDIR)" ] && [ -d "$(WWWDIR)/docs/$(PJ_VERSION)/pjlib/docs/html" ] ; then \ echo "Copying docs/$(PJ_VERSION) to $(WWWDIR)/docs/$(PJ_VERSION)/pjlib/docs/html.." ; \ cp -v -a ../docs/$(PJ_VERSION)/html/* $(WWWDIR)/docs/$(PJ_VERSION)/pjlib/docs/html/ ; \ fi print: $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib print_lib $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test print_bin depend: ../include/pj/config_site.h $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib depend $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test depend echo '$(TEST_EXE): $(PJLIB_LIB)' >> .pjlib-test-$(TARGET_NAME).depend .PHONY: dep depend pjlib pjlib-test clean realclean distclean dep: depend pjlib: ../include/pj/config_site.h $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib $(PJLIB_LIB) ../include/pj/config_site.h: touch ../include/pj/config_site.h pjlib-test: pjlib $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test $(TEST_EXE) .PHONY: ../lib/pjlib.ko ../lib/pjlib.ko: echo Making $@ $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib $@ .PHONY: ../lib/pjlib-test.ko ../lib/pjlib-test.ko: $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test $@ clean: $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib clean $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test clean realclean: $(subst @@,$(subst /,$(HOST_PSEP),.pjlib-$(TARGET_NAME).depend),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),.pjlib-test-$(TARGET_NAME).depend),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib realclean $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test realclean distclean: realclean gcov-report: $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib gcov-report $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test gcov-report ================================================ FILE: deps/pjsip/pjlib/build/os-auto.mak.in ================================================ # @configure_input@ # Determine OS specific files AC_OS_OBJS=@ac_os_objs@ # # PJLIB_OBJS specified here are object files to be included in PJLIB # (the library) for this specific operating system. Object files common # to all operating systems should go in Makefile instead. # export PJLIB_OBJS += $(AC_OS_OBJS) \ addr_resolv_sock.o \ log_writer_stdout.o \ os_timestamp_common.o \ pool_policy_malloc.o sock_bsd.o sock_select.o # # TEST_OBJS are operating system specific object files to be included in # the test application. # export TEST_OBJS += @ac_main_obj@ # # Additional LDFLAGS for pjlib-test # export TEST_LDFLAGS += @LDFLAGS@ @LIBS@ # # TARGETS are make targets in the Makefile, to be executed for this given # operating system. # export TARGETS = pjlib ================================================ FILE: deps/pjsip/pjlib/docs/doxygen.cfg ================================================ # Doxyfile 1.3-rc3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # General configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "PJLIB Reference" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = $(PJ_VERSION) # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = docs/$(PJ_VERSION) # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, # Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en # (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, # Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian. OUTPUT_LANGUAGE = English # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these class will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited # members of a class in the documentation of that class as if those members were # ordinary class members. Constructors, destructors and assignment operators of # the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. It is allowed to use relative paths in the argument list. STRIP_FROM_PATH = "c:\project\pjproject" # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower case letters. If set to YES upper case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # users are adviced to set this option to NO. CASE_SENSE_NAMES = YES # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explict @brief command for a brief description. JAVADOC_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = YES # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # reimplements. INHERIT_DOCS = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consist of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. # For instance some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources # only. Doxygen will then generate output that is more tailored for Java. # For instance namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES TYPEDEF_HIDES_STRUCT = YES #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = include/pj src/pjlib-samples src/pjlib-test # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp # *.h++ *.idl *.odl FILE_PATTERNS = *.h *.c # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories # that are symbolic links (a Unix filesystem feature) are excluded from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. EXCLUDE_PATTERNS = "*_i.h" "*/compat/*" "*/config_site.h" # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = . # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = YES # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. INPUT_FILTER = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .htm # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = docs/header.html # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = docs/footer.html # The HTML_STYLESHEET tag can be used to specify a user defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet HTML_STYLESHEET = docs/doxygen.css # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output dir. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non empty doxygen will try to run # the html help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the Html help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimised for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assigments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_XML = NO # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_PREDEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. PREDEFINED = PJ_DECL(x)=x PJ_DEF(x)=x PJ_IDECL(x)=x \ PJ_IDEF(x)=x PJ_INLINE(x)=x \ PJ_DECL_DATA(x)=x \ PJ_DECL_NO_RETURN(x)=x \ PJ_NO_RETURN=x \ PJ_HAS_HIGH_RES_TIMER=1 \ PJ_LOG_MAX_LEVEL=4 \ PJ_HAS_SEMAPHORE=1 \ PJ_HAS_EVENT_OBJ=1 \ PJ_HAS_TCP=1 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse the # parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::addtions related to external references #--------------------------------------------------------------------------- # The TAGFILES tag can be used to specify one or more tagfiles. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = docs/pjlib.tag # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). #PERL_PATH = /usr/bin/perl PERL_PATH = /c/Perl/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or # super classes. Setting the tag to NO turns the diagrams off. Note that this # option is superceded by the HAVE_DOT option below. This is only a fallback. It is # recommended to install and use dot, since it yield more powerful graphs. CLASS_DIAGRAMS = NO # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found on the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_WIDTH = 1024 # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_HEIGHT = 1024 # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermedate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::addtions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO # The CGI_NAME tag should be the name of the CGI script that # starts the search engine (doxysearch) with the correct parameters. # A script with this name will be generated by doxygen. #CGI_NAME = search.cgi # The CGI_URL tag should be the absolute URL to the directory where the # cgi binaries are located. See the documentation of your http daemon for # details. #CGI_URL = # The DOC_URL tag should be the absolute URL to the directory where the # documentation is located. If left blank the absolute path to the # documentation, with file:// prepended to it, will be used. #DOC_URL = # The DOC_ABSPATH tag should be the absolute path to the directory where the # documentation is located. If left blank the directory on the local machine # will be used. #DOC_ABSPATH = # The BIN_ABSPATH tag must point to the directory where the doxysearch binary # is installed. #BIN_ABSPATH = /usr/local/bin/ # The EXT_DOC_PATHS tag can be used to specify one or more paths to # documentation generated for other projects. This allows doxysearch to search # the documentation for these projects as well. #EXT_DOC_PATHS = ================================================ FILE: deps/pjsip/pjlib/docs/doxygen.css ================================================ BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV { font-family: Geneva, Arial, Helvetica, sans-serif; } BODY,TD { font-size: 80%; } CODE { font-size: 120%; font-family: monospace; } .fragment, pre { font-size: 110%; font-family: monospace; } H1 { text-align: center; font-size: 240%; } H2 { font-size: 200%; margin-top : 60px; } H3 { font-size: 160%; } H4 { font-size: 120%; } CAPTION { font-weight: bold } DIV.qindex { width: 100%; background-color: #eeeeff; border: 1px solid #b0b0b0; text-align: center; margin: 2px; padding: 2px; line-height: 140%; } DIV.nav { width: 100%; background-color: #eeeeff; border: 1px solid #b0b0b0; text-align: center; margin: 2px; padding: 2px; line-height: 140%; } A.qindex { text-decoration: none; font-size: 120%; color: #1A419D; } A.qindex:visited { text-decoration: none; color: #1A419D } A.qindex:hover { text-decoration: none; background-color: #ddddff; } A.qindexHL { text-decoration: none; font-weight: bold; background-color: #6666cc; color: #ffffff; border: 1px double #9295C2; } A.qindexHL:hover { text-decoration: none; background-color: #6666cc; color: #ffffff; } A.qindexHL:visited { text-decoration: none; background-color: #6666cc; color: #ffffff } A.el { text-decoration: none; font-weight: bold } A.elRef { font-weight: bold } A.code:link { text-decoration: none; font-weight: normal; color: #0000FF; } A.code:visited { text-decoration: none; font-weight: normal; color: #0000FF} A.codeRef:link { font-weight: normal; color: #0000FF} A.codeRef:visited { font-weight: normal; color: #0000FF} A:hover { text-decoration: none; background-color: #f2f2ff } DL.el { margin-left: -1cm } PRE.fragment { border: 1px solid #CCCCCC; background-color: #f5f5f5; margin-top: 4px; margin-bottom: 4px; margin-left: 2px; margin-right: 8px; padding-left: 6px; padding-right: 6px; padding-top: 4px; padding-bottom: 4px; } DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px } TD.md { background-color: #F4F4FB; font-weight: bold; } TD.mdPrefix { background-color: #F4F4FB; color: #606060; font-size: 80%; } TD.mdname1 { background-color: #F4F4FB; font-weight: bold; color: #602020; } TD.mdname { background-color: #F4F4FB; font-weight: bold; color: #602020; width: 600px; } DIV.groupHeader { margin-left: 16px; margin-top: 12px; margin-bottom: 6px; font-weight: bold; } DIV.groupText { margin-left: 16px; font-style: italic; font-size: 90% } BODY { background: white; color: black; margin-right: 20px; margin-left: 20px; } TD.indexkey { background-color: #eeeeff; font-weight: bold; padding-right : 10px; padding-top : 2px; padding-left : 10px; padding-bottom : 2px; margin-left : 0px; margin-right : 0px; margin-top : 2px; margin-bottom : 2px; border: 1px solid #CCCCCC; } TD.indexvalue { background-color: #eeeeff; font-style: italic; padding-right : 10px; padding-top : 2px; padding-left : 10px; padding-bottom : 2px; margin-left : 0px; margin-right : 0px; margin-top : 2px; margin-bottom : 2px; border: 1px solid #CCCCCC; } TR.memlist { background-color: #f0f0f0; } P.formulaDsp { text-align: center; } IMG.formulaDsp { } IMG.formulaInl { vertical-align: middle; } SPAN.keyword { color: #008000 } SPAN.keywordtype { color: #604020 } SPAN.keywordflow { color: #e08000 } SPAN.comment { color: #800000 } SPAN.preprocessor { color: #806020 } SPAN.stringliteral { color: #002080 } SPAN.charliteral { color: #008080 } .mdTable { border: 1px solid #868686; background-color: #F4F4FB; } .mdRow { padding: 8px 10px; } .mdescLeft { padding: 0px 8px 4px 8px; font-size: 80%; font-style: italic; background-color: #FAFAFA; border-top: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; margin: 0px; } .mdescRight { padding: 0px 8px 4px 8px; font-size: 80%; font-style: italic; background-color: #FAFAFA; border-top: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; margin: 0px; } .memItemLeft { padding: 1px 0px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: solid; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memItemRight { padding: 1px 8px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: solid; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memTemplItemLeft { padding: 1px 0px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memTemplItemRight { padding: 1px 8px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memTemplParams { padding: 1px 0px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: solid; border-right-style: none; border-bottom-style: none; border-left-style: none; color: #606060; background-color: #FAFAFA; font-size: 80%; } .search { color: #003399; font-weight: bold; } FORM.search { margin-bottom: 0px; margin-top: 0px; } INPUT.search { font-size: 75%; color: #000080; font-weight: normal; background-color: #eeeeff; } TD.tiny { font-size: 75%; } a { color: #252E78; } a:visited { color: #3D2185; } .dirtab { padding: 4px; border-collapse: collapse; border: 1px solid #b0b0b0; } TH.dirtab { background: #eeeeff; font-weight: bold; } HR { height: 1px; border: none; border-top: 1px solid black; } ================================================ FILE: deps/pjsip/pjlib/docs/footer.html ================================================

 


PJLIB Open Source, high performance, small footprint, and very very portable framework
Copyright (C) 2006-2009 Teluu Inc.
================================================ FILE: deps/pjsip/pjlib/docs/header.html ================================================ $title ($projectnumber)

Home --> Documentations --> PJLIB Reference

================================================ FILE: deps/pjsip/pjlib/include/pj/activesock.h ================================================ /* $Id: activesock.h 4461 2013-04-05 03:02:19Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_ASYNCSOCK_H__ #define __PJ_ASYNCSOCK_H__ /** * @file activesock.h * @brief Active socket */ #include #include PJ_BEGIN_DECL /** * @defgroup PJ_ACTIVESOCK Active socket I/O * @brief Active socket performs active operations on socket. * @ingroup PJ_IO * @{ * * Active socket is a higher level abstraction to the ioqueue. It provides * automation to socket operations which otherwise would have to be done * manually by the applications. For example with socket recv(), recvfrom(), * and accept() operations, application only needs to invoke these * operation once, and it will be notified whenever data or incoming TCP * connection (in the case of accept()) arrives. */ /** * This opaque structure describes the active socket. */ typedef struct pj_activesock_t pj_activesock_t; /** * This structure contains the callbacks to be called by the active socket. */ typedef struct pj_activesock_cb { /** * This callback is called when a data arrives as the result of * pj_activesock_start_read(). * * @param asock The active socket. * @param data The buffer containing the new data, if any. If * the status argument is non-PJ_SUCCESS, this * argument may be NULL. * @param size The length of data in the buffer. * @param status The status of the read operation. This may contain * non-PJ_SUCCESS for example when the TCP connection * has been closed. In this case, the buffer may * contain left over data from previous callback which * the application may want to process. * @param remainder If application wishes to leave some data in the * buffer (common for TCP applications), it should * move the remainder data to the front part of the * buffer and set the remainder length here. The value * of this parameter will be ignored for datagram * sockets. * * @return PJ_TRUE if further read is desired, and PJ_FALSE * when application no longer wants to receive data. * Application may destroy the active socket in the * callback and return PJ_FALSE here. */ pj_bool_t (*on_data_read)(pj_activesock_t *asock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder); /** * This callback is called when a packet arrives as the result of * pj_activesock_start_recvfrom(). * * @param asock The active socket. * @param data The buffer containing the packet, if any. If * the status argument is non-PJ_SUCCESS, this * argument will be set to NULL. * @param size The length of packet in the buffer. If * the status argument is non-PJ_SUCCESS, this * argument will be set to zero. * @param src_addr Source address of the packet. * @param addr_len Length of the source address. * @param status This contains * * @return PJ_TRUE if further read is desired, and PJ_FALSE * when application no longer wants to receive data. * Application may destroy the active socket in the * callback and return PJ_FALSE here. */ pj_bool_t (*on_data_recvfrom)(pj_activesock_t *asock, void *data, pj_size_t size, const pj_sockaddr_t *src_addr, int addr_len, pj_status_t status); /** * This callback is called when data has been sent. * * @param asock The active socket. * @param send_key Key associated with the send operation. * @param sent If value is positive non-zero it indicates the * number of data sent. When the value is negative, * it contains the error code which can be retrieved * by negating the value (i.e. status=-sent). * * @return Application may destroy the active socket in the * callback and return PJ_FALSE here. */ pj_bool_t (*on_data_sent)(pj_activesock_t *asock, pj_ioqueue_op_key_t *send_key, pj_ssize_t sent); /** * This callback is called when new connection arrives as the result * of pj_activesock_start_accept(). If the status of accept operation is * needed use on_accept_complete2 instead of this callback. * * @param asock The active socket. * @param newsock The new incoming socket. * @param src_addr The source address of the connection. * @param addr_len Length of the source address. * * @return PJ_TRUE if further accept() is desired, and PJ_FALSE * when application no longer wants to accept incoming * connection. Application may destroy the active socket * in the callback and return PJ_FALSE here. */ pj_bool_t (*on_accept_complete)(pj_activesock_t *asock, pj_sock_t newsock, const pj_sockaddr_t *src_addr, int src_addr_len); /** * This callback is called when new connection arrives as the result * of pj_activesock_start_accept(). * * @param asock The active socket. * @param newsock The new incoming socket. * @param src_addr The source address of the connection. * @param addr_len Length of the source address. * @param status The status of the accept operation. This may contain * non-PJ_SUCCESS for example when the TCP listener is in * bad state for example on iOS platform after the * application waking up from background. * * @return PJ_TRUE if further accept() is desired, and PJ_FALSE * when application no longer wants to accept incoming * connection. Application may destroy the active socket * in the callback and return PJ_FALSE here. */ pj_bool_t (*on_accept_complete2)(pj_activesock_t *asock, pj_sock_t newsock, const pj_sockaddr_t *src_addr, int src_addr_len, pj_status_t status); /** * This callback is called when pending connect operation has been * completed. * * @param asock The active socket. * @param status The connection result. If connection has been * successfully established, the status will contain * PJ_SUCCESS. * * @return Application may destroy the active socket in the * callback and return PJ_FALSE here. */ pj_bool_t (*on_connect_complete)(pj_activesock_t *asock, pj_status_t status); } pj_activesock_cb; /** * Settings that can be given during active socket creation. Application * must initialize this structure with #pj_activesock_cfg_default(). */ typedef struct pj_activesock_cfg { /** * Optional group lock to be assigned to the ioqueue key. */ pj_grp_lock_t *grp_lock; /** * Number of concurrent asynchronous operations that is to be supported * by the active socket. This value only affects socket receive and * accept operations -- the active socket will issue one or more * asynchronous read and accept operations based on the value of this * field. Setting this field to more than one will allow more than one * incoming data or incoming connections to be processed simultaneously * on multiprocessor systems, when the ioqueue is polled by more than * one threads. * * The default value is 1. */ unsigned async_cnt; /** * The ioqueue concurrency to be forced on the socket when it is * registered to the ioqueue. See #pj_ioqueue_set_concurrency() for more * info about ioqueue concurrency. * * When this value is -1, the concurrency setting will not be forced for * this socket, and the socket will inherit the concurrency setting of * the ioqueue. When this value is zero, the active socket will disable * concurrency for the socket. When this value is +1, the active socket * will enable concurrency for the socket. * * The default value is -1. */ int concurrency; /** * If this option is specified, the active socket will make sure that * asynchronous send operation with stream oriented socket will only * call the callback after all data has been sent. This means that the * active socket will automatically resend the remaining data until * all data has been sent. * * Please note that when this option is specified, it is possible that * error is reported after partial data has been sent. Also setting * this will disable the ioqueue concurrency for the socket. * * Default value is 1. */ pj_bool_t whole_data; } pj_activesock_cfg; /** * Initialize the active socket configuration with the default values. * * @param cfg The configuration to be initialized. */ PJ_DECL(void) pj_activesock_cfg_default(pj_activesock_cfg *cfg); /** * Create the active socket for the specified socket. This will register * the socket to the specified ioqueue. * * @param pool Pool to allocate memory from. * @param sock The socket handle. * @param sock_type Specify socket type, either pj_SOCK_DGRAM() or * pj_SOCK_STREAM(). The active socket needs this * information to handle connection closure for * connection oriented sockets. * @param ioqueue The ioqueue to use. * @param opt Optional settings. When this setting is not specifed, * the default values will be used. * @param cb Pointer to structure containing application * callbacks. * @param user_data Arbitrary user data to be associated with this * active socket. * @param p_asock Pointer to receive the active socket instance. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_activesock_create(pj_pool_t *pool, pj_sock_t sock, int sock_type, const pj_activesock_cfg *opt, pj_ioqueue_t *ioqueue, const pj_activesock_cb *cb, void *user_data, pj_activesock_t **p_asock); /** * Create UDP socket descriptor, bind it to the specified address, and * create the active socket for the socket descriptor. * * @param pool Pool to allocate memory from. * @param addr Specifies the address family of the socket and the * address where the socket should be bound to. If * this argument is NULL, then AF_INET is assumed and * the socket will be bound to any addresses and port. * @param opt Optional settings. When this setting is not specifed, * the default values will be used. * @param cb Pointer to structure containing application * callbacks. * @param user_data Arbitrary user data to be associated with this * active socket. * @param p_asock Pointer to receive the active socket instance. * @param bound_addr If this argument is specified, it will be filled with * the bound address on return. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_activesock_create_udp(pj_pool_t *pool, const pj_sockaddr *addr, const pj_activesock_cfg *opt, pj_ioqueue_t *ioqueue, const pj_activesock_cb *cb, void *user_data, pj_activesock_t **p_asock, pj_sockaddr *bound_addr); /** * Close the active socket. This will unregister the socket from the * ioqueue and ultimately close the socket. * * @param asock The active socket. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_activesock_close(pj_activesock_t *asock); #if (defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0) || \ defined(DOXYGEN) /** * Set iPhone OS background mode setting. Setting to 1 will enable TCP * active socket to receive incoming data when application is in the * background. Setting to 0 will disable it. Default value of this * setting is PJ_ACTIVESOCK_TCP_IPHONE_OS_BG. * * This API is only available if PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT * is set to non-zero. * * @param asock The active socket. * @param val The value of background mode setting. * */ PJ_DECL(void) pj_activesock_set_iphone_os_bg(pj_activesock_t *asock, int val); /** * Enable/disable support for iPhone OS background mode. This setting * will apply globally and will affect any active sockets created * afterwards, if you want to change the setting for a particular * active socket, use #pj_activesock_set_iphone_os_bg() instead. * By default, this setting is enabled. * * This API is only available if PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT * is set to non-zero. * * @param val The value of global background mode setting. * */ PJ_DECL(void) pj_activesock_enable_iphone_os_bg(pj_bool_t val); #endif /** * Associate arbitrary data with the active socket. Application may * inspect this data in the callbacks and associate it with higher * level processing. * * @param asock The active socket. * @param user_data The user data to be associated with the active * socket. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_activesock_set_user_data(pj_activesock_t *asock, void *user_data); /** * Retrieve the user data previously associated with this active * socket. * * @param asock The active socket. * * @return The user data. */ PJ_DECL(void*) pj_activesock_get_user_data(pj_activesock_t *asock); /** * Starts read operation on this active socket. This function will create * \a async_cnt number of buffers (the \a async_cnt parameter was given * in \a pj_activesock_create() function) where each buffer is \a buff_size * long. The buffers are allocated from the specified \a pool. Once the * buffers are created, it then issues \a async_cnt number of asynchronous * \a recv() operations to the socket and returns back to caller. Incoming * data on the socket will be reported back to application via the * \a on_data_read() callback. * * Application only needs to call this function once to initiate read * operations. Further read operations will be done automatically by the * active socket when \a on_data_read() callback returns non-zero. * * @param asock The active socket. * @param pool Pool used to allocate buffers for incoming data. * @param buff_size The size of each buffer, in bytes. * @param flags Flags to be given to pj_ioqueue_recv(). * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_activesock_start_read(pj_activesock_t *asock, pj_pool_t *pool, unsigned buff_size, pj_uint32_t flags); /** * Same as #pj_activesock_start_read(), except that the application * supplies the buffers for the read operation so that the acive socket * does not have to allocate the buffers. * * @param asock The active socket. * @param pool Pool used to allocate buffers for incoming data. * @param buff_size The size of each buffer, in bytes. * @param readbuf Array of packet buffers, each has buff_size size. * @param flags Flags to be given to pj_ioqueue_recv(). * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_activesock_start_read2(pj_activesock_t *asock, pj_pool_t *pool, unsigned buff_size, void *readbuf[], pj_uint32_t flags); /** * Same as pj_activesock_start_read(), except that this function is used * only for datagram sockets, and it will trigger \a on_data_recvfrom() * callback instead. * * @param asock The active socket. * @param pool Pool used to allocate buffers for incoming data. * @param buff_size The size of each buffer, in bytes. * @param flags Flags to be given to pj_ioqueue_recvfrom(). * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_activesock_start_recvfrom(pj_activesock_t *asock, pj_pool_t *pool, unsigned buff_size, pj_uint32_t flags); /** * Same as #pj_activesock_start_recvfrom() except that the recvfrom() * operation takes the buffer from the argument rather than creating * new ones. * * @param asock The active socket. * @param pool Pool used to allocate buffers for incoming data. * @param buff_size The size of each buffer, in bytes. * @param readbuf Array of packet buffers, each has buff_size size. * @param flags Flags to be given to pj_ioqueue_recvfrom(). * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_activesock_start_recvfrom2(pj_activesock_t *asock, pj_pool_t *pool, unsigned buff_size, void *readbuf[], pj_uint32_t flags); /** * Send data using the socket. * * @param asock The active socket. * @param send_key The operation key to send the data, which is useful * if application wants to submit multiple pending * send operations and want to track which exact data * has been sent in the \a on_data_sent() callback. * @param data The data to be sent. This data must remain valid * until the data has been sent. * @param size The size of the data. * @param flags Flags to be given to pj_ioqueue_send(). * * * @return PJ_SUCCESS if data has been sent immediately, or * PJ_EPENDING if data cannot be sent immediately. In * this case the \a on_data_sent() callback will be * called when data is actually sent. Any other return * value indicates error condition. */ PJ_DECL(pj_status_t) pj_activesock_send(pj_activesock_t *asock, pj_ioqueue_op_key_t *send_key, const void *data, pj_ssize_t *size, unsigned flags); /** * Send datagram using the socket. * * @param asock The active socket. * @param send_key The operation key to send the data, which is useful * if application wants to submit multiple pending * send operations and want to track which exact data * has been sent in the \a on_data_sent() callback. * @param data The data to be sent. This data must remain valid * until the data has been sent. * @param size The size of the data. * @param flags Flags to be given to pj_ioqueue_send(). * @param addr The destination address. * @param addr_len The length of the address. * * @return PJ_SUCCESS if data has been sent immediately, or * PJ_EPENDING if data cannot be sent immediately. In * this case the \a on_data_sent() callback will be * called when data is actually sent. Any other return * value indicates error condition. */ PJ_DECL(pj_status_t) pj_activesock_sendto(pj_activesock_t *asock, pj_ioqueue_op_key_t *send_key, const void *data, pj_ssize_t *size, unsigned flags, const pj_sockaddr_t *addr, int addr_len); #if PJ_HAS_TCP /** * Starts asynchronous socket accept() operations on this active socket. * Application must bind the socket before calling this function. This * function will issue \a async_cnt number of asynchronous \a accept() * operations to the socket and returns back to caller. Incoming * connection on the socket will be reported back to application via the * \a on_accept_complete() callback. * * Application only needs to call this function once to initiate accept() * operations. Further accept() operations will be done automatically by * the active socket when \a on_accept_complete() callback returns non-zero. * * @param asock The active socket. * @param pool Pool used to allocate some internal data for the * operation. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_activesock_start_accept(pj_activesock_t *asock, pj_pool_t *pool); /** * Starts asynchronous socket connect() operation for this socket. Once * the connection is done (either successfully or not), the * \a on_connect_complete() callback will be called. * * @param asock The active socket. * @param pool The pool to allocate some internal data for the * operation. * @param remaddr Remote address. * @param addr_len Length of the remote address. * * @return PJ_SUCCESS if connection can be established immediately, * or PJ_EPENDING if connection cannot be established * immediately. In this case the \a on_connect_complete() * callback will be called when connection is complete. * Any other return value indicates error condition. */ PJ_DECL(pj_status_t) pj_activesock_start_connect(pj_activesock_t *asock, pj_pool_t *pool, const pj_sockaddr_t *remaddr, int addr_len); #endif /* PJ_HAS_TCP */ /** * @} */ PJ_END_DECL #endif /* __PJ_ASYNCSOCK_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/addr_resolv.h ================================================ /* $Id: addr_resolv.h 4218 2012-08-07 02:18:15Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_ADDR_RESOLV_H__ #define __PJ_ADDR_RESOLV_H__ /** * @file addr_resolv.h * @brief IP address resolution. */ #include PJ_BEGIN_DECL /** * @defgroup pj_addr_resolve Network Address Resolution * @ingroup PJ_IO * @{ * * This module provides function to resolve Internet address of the * specified host name. To resolve a particular host name, application * can just call #pj_gethostbyname(). * * Example: *
 *   ...
 *   pj_hostent he;
 *   pj_status_t rc;
 *   pj_str_t host = pj_str("host.example.com");
 *   
 *   rc = pj_gethostbyname( &host, &he);
 *   if (rc != PJ_SUCCESS) {
 *      char errbuf[80];
 *      pj_strerror( rc, errbuf, sizeof(errbuf));
 *      PJ_LOG(2,("sample", "Unable to resolve host, error=%s", errbuf));
 *      return rc;
 *   }
 *
 *   // process address...
 *   addr.sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
 *   ...
 * 
* * It's pretty simple really... */ /** This structure describes an Internet host address. */ typedef struct pj_hostent { char *h_name; /**< The official name of the host. */ char **h_aliases; /**< Aliases list. */ int h_addrtype; /**< Host address type. */ int h_length; /**< Length of address. */ char **h_addr_list; /**< List of addresses. */ } pj_hostent; /** Shortcut to h_addr_list[0] */ #define h_addr h_addr_list[0] /** * This structure describes address information pj_getaddrinfo(). */ typedef struct pj_addrinfo { char ai_canonname[PJ_MAX_HOSTNAME]; /**< Canonical name for host*/ pj_sockaddr ai_addr; /**< Binary address. */ } pj_addrinfo; /** * This function fills the structure of type pj_hostent for a given host name. * For host resolution function that also works with IPv6, please see * #pj_getaddrinfo(). * * @param name Host name to resolve. Specifying IPv4 address here * may fail on some platforms (e.g. Windows) * @param he The pj_hostent structure to be filled. Note that * the pointers in this structure points to temporary * variables which value will be reset upon subsequent * invocation. * * @return PJ_SUCCESS, or the appropriate error codes. */ PJ_DECL(pj_status_t) pj_gethostbyname(const pj_str_t *name, pj_hostent *he); /** * Resolve the primary IP address of local host. * * @param af The desired address family to query. Valid values * are pj_AF_INET() or pj_AF_INET6(). * @param addr On successful resolution, the address family and address * part of this socket address will be filled up with the host * IP address, in network byte order. Other parts of the socket * address are untouched. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_gethostip(int af, pj_sockaddr *addr); /** * Get the interface IP address to send data to the specified destination. * * @param af The desired address family to query. Valid values * are pj_AF_INET() or pj_AF_INET6(). * @param dst The destination host. * @param itf_addr On successful resolution, the address family and address * part of this socket address will be filled up with the host * IP address, in network byte order. Other parts of the socket * address should be ignored. * @param allow_resolve If \a dst may contain hostname (instead of IP * address), specify whether hostname resolution should * be performed. If not, default interface address will * be returned. * @param p_dst_addr If not NULL, it will be filled with the IP address of * the destination host. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_getipinterface(int af, const pj_str_t *dst, pj_sockaddr *itf_addr, pj_bool_t allow_resolve, pj_sockaddr *p_dst_addr); /** * Get the IP address of the default interface. Default interface is the * interface of the default route. * * @param af The desired address family to query. Valid values * are pj_AF_INET() or pj_AF_INET6(). * @param addr On successful resolution, the address family and address * part of this socket address will be filled up with the host * IP address, in network byte order. Other parts of the socket * address are untouched. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_getdefaultipinterface(int af, pj_sockaddr *addr); /** * This function translates the name of a service location (for example, * a host name) and returns a set of addresses and associated information * to be used in creating a socket with which to address the specified * service. * * @param af The desired address family to query. Valid values * are pj_AF_INET(), pj_AF_INET6(), or pj_AF_UNSPEC(). * @param name Descriptive name or an address string, such as host * name. * @param count On input, it specifies the number of elements in * \a ai array. On output, this will be set with the * number of address informations found for the * specified name. * @param ai Array of address info to be filled with the information * about the host. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_getaddrinfo(int af, const pj_str_t *name, unsigned *count, pj_addrinfo ai[]); /** @} */ PJ_END_DECL #endif /* __PJ_ADDR_RESOLV_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/array.h ================================================ /* $Id: array.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_ARRAY_H__ #define __PJ_ARRAY_H__ /** * @file array.h * @brief PJLIB Array helper. */ #include PJ_BEGIN_DECL /** * @defgroup PJ_ARRAY Array helper. * @ingroup PJ_DS * @{ * * This module provides helper to manipulate array of elements of any size. * It provides most used array operations such as insert, erase, and search. */ /** * Insert value to the array at the given position, and rearrange the * remaining nodes after the position. * * @param array the array. * @param elem_size the size of the individual element. * @param count the CURRENT number of elements in the array. * @param pos the position where the new element is put. * @param value the value to copy to the new element. */ PJ_DECL(void) pj_array_insert( void *array, unsigned elem_size, unsigned count, unsigned pos, const void *value); /** * Erase a value from the array at given position, and rearrange the remaining * elements post the erased element. * * @param array the array. * @param elem_size the size of the individual element. * @param count the current number of elements in the array. * @param pos the index/position to delete. */ PJ_DECL(void) pj_array_erase( void *array, unsigned elem_size, unsigned count, unsigned pos); /** * Search the first value in the array according to matching function. * * @param array the array. * @param elem_size the individual size of the element. * @param count the number of elements. * @param matching the matching function, which MUST return PJ_SUCCESS if * the specified element match. * @param result the pointer to the value found. * * @return PJ_SUCCESS if value is found, otherwise the error code. */ PJ_DECL(pj_status_t) pj_array_find( const void *array, unsigned elem_size, unsigned count, pj_status_t (*matching)(const void *value), void **result); /** * @} */ PJ_END_DECL #endif /* __PJ_ARRAY_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/assert.h ================================================ /* $Id: assert.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_ASSERT_H__ #define __PJ_ASSERT_H__ /** * @file assert.h * @brief Assertion macro pj_assert(). */ #include #include /** * @defgroup pj_assert Assertion Macro * @ingroup PJ_MISC * @{ * * Assertion and other helper macros for sanity checking. */ /** * @hideinitializer * Check during debug build that an expression is true. If the expression * computes to false during run-time, then the program will stop at the * offending statements. * For release build, this macro will not do anything. * * @param expr The expression to be evaluated. */ #ifndef pj_assert # define pj_assert(expr) assert(expr) #endif /** * @hideinitializer * If #PJ_ENABLE_EXTRA_CHECK is declared and the value is non-zero, then * #PJ_ASSERT_RETURN macro will evaluate the expression in @a expr during * run-time. If the expression yields false, assertion will be triggered * and the current function will return with the specified return value. * * If #PJ_ENABLE_EXTRA_CHECK is not declared or is zero, then no run-time * checking will be performed. The macro simply evaluates to pj_assert(expr). */ #if defined(PJ_ENABLE_EXTRA_CHECK) && PJ_ENABLE_EXTRA_CHECK != 0 # define PJ_ASSERT_RETURN(expr,retval) \ do { \ if (!(expr)) { return retval; } \ } while (0) #else # define PJ_ASSERT_RETURN(expr,retval) pj_assert(expr) #endif /** * @hideinitializer * If #PJ_ENABLE_EXTRA_CHECK is declared and non-zero, then * #PJ_ASSERT_ON_FAIL macro will evaluate the expression in @a expr during * run-time. If the expression yields false, assertion will be triggered * and @a exec_on_fail will be executed. * * If #PJ_ENABLE_EXTRA_CHECK is not declared or is zero, then no run-time * checking will be performed. The macro simply evaluates to pj_assert(expr). */ #if defined(PJ_ENABLE_EXTRA_CHECK) && PJ_ENABLE_EXTRA_CHECK != 0 # define PJ_ASSERT_ON_FAIL(expr,exec_on_fail) \ do { \ if (!(expr)) exec_on_fail; \ } while (0) #else # define PJ_ASSERT_ON_FAIL(expr,exec_on_fail) pj_assert(expr) #endif /** @} */ #endif /* __PJ_ASSERT_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/assert.h ================================================ /* $Id: assert.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_ASSERT_H__ #define __PJ_COMPAT_ASSERT_H__ /** * @file assert.h * @brief Provides assert() macro. */ #if defined(PJ_HAS_ASSERT_H) && PJ_HAS_ASSERT_H != 0 # include #elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 # define assert(expr) do { \ if (!(expr)) \ printk("!!ASSERTION FAILED: [%s:%d] \"" #expr "\"\n",\ __FILE__, __LINE__); \ } while (0) #else # warning "assert() is not implemented" # define assert(expr) #endif #endif /* __PJ_COMPAT_ASSERT_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/cc_armcc.h ================================================ /* $Id: cc_armcc.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_CC_ARMCC_H__ #define __PJ_COMPAT_CC_ARMCC_H__ /** * @file cc_armcc.h * @brief Describes ARMCC compiler specifics. */ #ifndef __ARMCC__ # error "This file is only for armcc!" #endif #define PJ_CC_NAME "armcc" #define PJ_CC_VER_1 (__ARMCC_VERSION/100000) #define PJ_CC_VER_2 ((__ARMCC_VERSION%100000)/10000) #define PJ_CC_VER_3 (__ARMCC_VERSION%10000) #ifdef __cplusplus # define PJ_INLINE_SPECIFIER inline #else # define PJ_INLINE_SPECIFIER static __inline #endif #define PJ_THREAD_FUNC #define PJ_NORETURN #define PJ_ATTR_NORETURN __attribute__ ((noreturn)) #define PJ_HAS_INT64 1 typedef long long pj_int64_t; typedef unsigned long long pj_uint64_t; #define PJ_INT64_FMT "L" #define PJ_UNREACHED(x) #endif /* __PJ_COMPAT_CC_ARMCC_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/cc_codew.h ================================================ /* $Id: cc_codew.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_CC_CODEW_H__ #define __PJ_COMPAT_CC_CODEW_H__ /** * @file cc_codew.h * @brief Describes MetroWerks Code Warrior compiler specifics. */ #ifndef __MWERKS__ # error "This file is only for Code Warrior!" #endif #define PJ_CC_NAME "codewarrior" #define PJ_CC_VER_1 ((__MWERKS__ & 0xF000) >> 12) #define PJ_CC_VER_2 ((__MWERKS__ & 0x0F00) >> 8) #define PJ_CC_VER_3 ((__MWERKS__ & 0xFF)) #define PJ_INLINE_SPECIFIER static inline #define PJ_THREAD_FUNC #define PJ_NORETURN #define PJ_ATTR_NORETURN #define PJ_HAS_INT64 1 typedef long long pj_int64_t; typedef unsigned long long pj_uint64_t; #define PJ_INT64(val) val##LL #define PJ_UINT64(val) val##LLU #define PJ_INT64_FMT "L" #define PJ_UNREACHED(x) #endif /* __PJ_COMPAT_CC_CODEW_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/cc_gcc.h ================================================ /* $Id: cc_gcc.h 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_CC_GCC_H__ #define __PJ_COMPAT_CC_GCC_H__ /** * @file cc_gcc.h * @brief Describes GCC compiler specifics. */ #ifndef __GNUC__ # error "This file is only for gcc!" #endif #define PJ_CC_NAME "gcc" #define PJ_CC_VER_1 __GNUC__ #define PJ_CC_VER_2 __GNUC_MINOR__ /* __GNUC_PATCHLEVEL__ doesn't exist in gcc-2.9x.x */ #ifdef __GNUC_PATCHLEVEL__ # define PJ_CC_VER_3 __GNUC_PATCHLEVEL__ #else # define PJ_CC_VER_3 0 #endif #define PJ_THREAD_FUNC #define PJ_NORETURN #define PJ_HAS_INT64 1 #ifdef __STRICT_ANSI__ #include typedef int64_t pj_int64_t; typedef uint64_t pj_uint64_t; #define PJ_INLINE_SPECIFIER static __inline #define PJ_ATTR_NORETURN #else typedef long long pj_int64_t; typedef unsigned long long pj_uint64_t; #define PJ_INLINE_SPECIFIER static inline #define PJ_ATTR_NORETURN __attribute__ ((noreturn)) #endif #define PJ_INT64(val) val##LL #define PJ_UINT64(val) val##LLU #define PJ_INT64_FMT "L" #ifdef __GLIBC__ # define PJ_HAS_BZERO 1 #endif #define PJ_UNREACHED(x) #define PJ_ALIGN_DATA(declaration, alignment) declaration __attribute__((aligned (alignment))) #endif /* __PJ_COMPAT_CC_GCC_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/cc_gcce.h ================================================ /* $Id: cc_gcce.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_CC_GCCE_H__ #define __PJ_COMPAT_CC_GCCE_H__ /** * @file cc_gcce.h * @brief Describes GCCE compiler specifics. */ #ifndef __GCCE__ # error "This file is only for gcce!" #endif #define PJ_CC_NAME "gcce" #define PJ_CC_VER_1 __GCCE__ #define PJ_CC_VER_2 __GCCE_MINOR__ #define PJ_CC_VER_3 __GCCE_PATCHLEVEL__ #define PJ_INLINE_SPECIFIER static inline #define PJ_THREAD_FUNC #define PJ_NORETURN #define PJ_ATTR_NORETURN __attribute__ ((noreturn)) #define PJ_HAS_INT64 1 typedef long long pj_int64_t; typedef unsigned long long pj_uint64_t; #define PJ_INT64(val) val##LL #define PJ_UINT64(val) val##LLU #define PJ_INT64_FMT "L" #endif /* __PJ_COMPAT_CC_GCCE_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/cc_msvc.h ================================================ /* $Id: cc_msvc.h 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_CC_MSVC_H__ #define __PJ_COMPAT_CC_MSVC_H__ /** * @file cc_msvc.h * @brief Describes Microsoft Visual C compiler specifics. */ #ifndef _MSC_VER # error "This header file is only for Visual C compiler!" #endif #define PJ_CC_NAME "msvc" #define PJ_CC_VER_1 (_MSC_VER/100) #define PJ_CC_VER_2 (_MSC_VER%100) #define PJ_CC_VER_3 0 /* Disable CRT deprecation warnings. */ #if PJ_CC_VER_1 >= 8 && !defined(_CRT_SECURE_NO_DEPRECATE) # define _CRT_SECURE_NO_DEPRECATE #endif #if PJ_CC_VER_1 >= 8 && !defined(_CRT_SECURE_NO_WARNINGS) # define _CRT_SECURE_NO_WARNINGS /* The above doesn't seem to work, at least on VS2005, so lets use * this construct as well. */ # pragma warning(disable: 4996) #endif #pragma warning(disable: 4127) // conditional expression is constant #pragma warning(disable: 4611) // not wise to mix setjmp with C++ #pragma warning(disable: 4514) // unref. inline function has been removed #ifdef NDEBUG # pragma warning(disable: 4702) // unreachable code # pragma warning(disable: 4710) // function is not inlined. # pragma warning(disable: 4711) // function selected for auto inline expansion #endif #ifdef __cplusplus # define PJ_INLINE_SPECIFIER inline #else # define PJ_INLINE_SPECIFIER static __inline #endif #define PJ_EXPORT_DECL_SPECIFIER __declspec(dllexport) #define PJ_EXPORT_DEF_SPECIFIER __declspec(dllexport) #define PJ_IMPORT_DECL_SPECIFIER __declspec(dllimport) #define PJ_THREAD_FUNC #define PJ_NORETURN __declspec(noreturn) #define PJ_ATTR_NORETURN #define PJ_HAS_INT64 1 typedef __int64 pj_int64_t; typedef unsigned __int64 pj_uint64_t; #define PJ_INT64(val) val##i64 #define PJ_UINT64(val) val##ui64 #define PJ_INT64_FMT "I64" #define PJ_UNREACHED(x) #define PJ_ALIGN_DATA(declaration, alignment) __declspec(align(alignment)) declaration #endif /* __PJ_COMPAT_CC_MSVC_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/cc_mwcc.h ================================================ /* $Id: cc_mwcc.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_CC_MWCC_H__ #define __PJ_COMPAT_CC_MWCC_H__ /** * @file cc_mwcc.h * @brief Describes MWCC compiler specifics. */ #ifndef __CW32__ # error "This file is only for mwcc!" #endif #define PJ_CC_NAME "mwcc32sym" #define PJ_CC_VER_1 1 #define PJ_CC_VER_2 0 #define PJ_CC_VER_3 0 #define PJ_INLINE_SPECIFIER static inline #define PJ_THREAD_FUNC #define PJ_NORETURN #define PJ_ATTR_NORETURN __attribute__ ((noreturn)) #define PJ_HAS_INT64 1 typedef long long pj_int64_t; typedef unsigned long long pj_uint64_t; #define PJ_INT64(val) val##LL #define PJ_UINT64(val) val##LLU #define PJ_INT64_FMT "L" #define PJ_UNREACHED(x) #endif /* __PJ_COMPAT_CC_MWCC_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/ctype.h ================================================ /* $Id: ctype.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_CTYPE_H__ #define __PJ_COMPAT_CTYPE_H__ /** * @file ctype.h * @brief Provides ctype function family. */ #if defined(PJ_HAS_CTYPE_H) && PJ_HAS_CTYPE_H != 0 # include #else # define isalnum(c) (isalpha(c) || isdigit(c)) # define isalpha(c) (islower(c) || isupper(c)) # define isascii(c) (((unsigned char)(c))<=0x7f) # define isdigit(c) ((c)>='0' && (c)<='9') # define isspace(c) ((c)==' ' || (c)=='\t' ||\ (c)=='\n' || (c)=='\r' || (c)=='\v') # define islower(c) ((c)>='a' && (c)<='z') # define isupper(c) ((c)>='A' && (c)<='Z') # define isxdigit(c) (isdigit(c) || (tolower(c)>='a'&&tolower(c)<='f')) # define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)+('a'-'A') : (c)) # define toupper(c) (((c) >= 'a' && (c) <= 'z') ? (c)-('a'-'A') : (c)) #endif #ifndef isblank # define isblank(c) (c==' ' || c=='\t') #endif #endif /* __PJ_COMPAT_CTYPE_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/errno.h ================================================ /* $Id: errno.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_ERRNO_H__ #define __PJ_COMPAT_ERRNO_H__ #if defined(PJ_WIN32) && PJ_WIN32 != 0 || \ defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0 || \ defined(PJ_WIN64) && PJ_WIN64 != 0 typedef unsigned long pj_os_err_type; # define pj_get_native_os_error() GetLastError() # define pj_get_native_netos_error() WSAGetLastError() #elif defined(PJ_HAS_ERRNO_VAR) && PJ_HAS_ERRNO_VAR!= 0 typedef int pj_os_err_type; # define pj_get_native_os_error() (errno) # define pj_get_native_netos_error() (errno) #else # error "Please define how to get errno for this platform here!" #endif #endif /* __PJ_COMPAT_ERRNO_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/high_precision.h ================================================ /* $Id: high_precision.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_HIGH_PRECISION_H__ #define __PJ_COMPAT_HIGH_PRECISION_H__ #if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT != 0 /* * The first choice for high precision math is to use double. */ # include typedef double pj_highprec_t; # define PJ_HIGHPREC_VALUE_IS_ZERO(a) (a==0) # define pj_highprec_mod(a,b) (a=fmod(a,b)) #elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 # include typedef pj_int64_t pj_highprec_t; # define pj_highprec_div(a1,a2) do_div(a1,a2) # define pj_highprec_mod(a1,a2) (a1=do_mod(a1, a2)) PJ_INLINE(pj_int64_t) do_mod( pj_int64_t a1, pj_int64_t a2) { return do_div(a1,a2); } #elif defined(PJ_HAS_INT64) && PJ_HAS_INT64 != 0 /* * Next choice is to use 64-bit arithmatics. */ typedef pj_int64_t pj_highprec_t; #else # warning "High precision math is not available" /* * Last, fallback to 32-bit arithmetics. */ typedef pj_int32_t pj_highprec_t; #endif /** * @def pj_highprec_mul * pj_highprec_mul(a1, a2) - High Precision Multiplication * Multiply a1 and a2, and store the result in a1. */ #ifndef pj_highprec_mul # define pj_highprec_mul(a1,a2) (a1 = a1 * a2) #endif /** * @def pj_highprec_div * pj_highprec_div(a1, a2) - High Precision Division * Divide a2 from a1, and store the result in a1. */ #ifndef pj_highprec_div # define pj_highprec_div(a1,a2) (a1 = a1 / a2) #endif /** * @def pj_highprec_mod * pj_highprec_mod(a1, a2) - High Precision Modulus * Get the modulus a2 from a1, and store the result in a1. */ #ifndef pj_highprec_mod # define pj_highprec_mod(a1,a2) (a1 = a1 % a2) #endif /** * @def PJ_HIGHPREC_VALUE_IS_ZERO(a) * Test if the specified high precision value is zero. */ #ifndef PJ_HIGHPREC_VALUE_IS_ZERO # define PJ_HIGHPREC_VALUE_IS_ZERO(a) (a==0) #endif #endif /* __PJ_COMPAT_HIGH_PRECISION_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/m_alpha.h ================================================ /* $Id: m_alpha.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_M_ALPHA_H__ #define __PJ_COMPAT_M_ALPHA_H__ /** * @file m_alpha.h * @brief Describes Alpha processor family specifics. */ #define PJ_M_NAME "alpha" #define PJ_HAS_PENTIUM 0 #define PJ_IS_LITTLE_ENDIAN 1 #define PJ_IS_BIG_ENDIAN 0 #endif /* __PJ_COMPAT_M_ALPHA_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/m_armv4.h ================================================ /* $Id: m_armv4.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_M_ARMV4_H__ #define __PJ_COMPAT_M_ARMV4_H__ /** * @file m_armv4.h * @brief Describes ARM family processor specifics. */ /* * This file covers PJ_M_ARMV4 etc. */ #define PJ_M_NAME "armv4" #define PJ_HAS_PENTIUM 0 #define PJ_IS_LITTLE_ENDIAN 1 #define PJ_IS_BIG_ENDIAN 0 #endif /* __PJ_COMPAT_M_ARMV4_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/m_auto.h.in ================================================ /* $Id: m_auto.h.in 3295 2010-08-25 12:51:29Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_M_AUTO_H__ #define __PJ_COMPAT_M_AUTO_H__ /** * @file m_auto.h * @brief Automatically generated process definition file. */ /* Machine name, filled in by autoconf script */ #undef PJ_M_NAME /* Endianness. It's reported on pjsip list on 09/02/13 that autoconf * endianness detection failed for universal build, so special case * for it here. Thanks Ruud Klaver for the fix. */ #ifdef PJ_DARWINOS # ifdef __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else /* Endianness, as detected by autoconf */ # undef WORDS_BIGENDIAN #endif #ifdef WORDS_BIGENDIAN # define PJ_IS_LITTLE_ENDIAN 0 # define PJ_IS_BIG_ENDIAN 1 #else # define PJ_IS_LITTLE_ENDIAN 1 # define PJ_IS_BIG_ENDIAN 0 #endif /* Specify if floating point is present/desired */ #undef PJ_HAS_FLOATING_POINT /* Deprecated */ #define PJ_HAS_PENTIUM 0 #endif /* __PJ_COMPAT_M_AUTO_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/m_i386.h ================================================ /* $Id: m_i386.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_M_i386_H__ #define __PJ_COMPAT_M_i386_H__ /** * @file m_i386.h * @brief Describes Intel i386 family processor specifics. */ #define PJ_M_NAME "i386" #define PJ_HAS_PENTIUM 1 #define PJ_IS_LITTLE_ENDIAN 1 #define PJ_IS_BIG_ENDIAN 0 #endif /* __PJ_COMPAT_M_i386_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/m_m68k.h ================================================ /* $Id: m_m68k.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_M_M68K_H__ #define __PJ_COMPAT_M_M68K_H__ /** * @file m_m68k.h * @brief Describes Motorola m68k family processor specifics. */ #define PJ_M_NAME "m68k" #define PJ_HAS_PENTIUM 0 #define PJ_IS_LITTLE_ENDIAN 1 #define PJ_IS_BIG_ENDIAN 0 #endif /* __PJ_COMPAT_M_M68K_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/m_powerpc.h ================================================ /* $Id: m_powerpc.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_M_POWERPC_H__ #define __PJ_COMPAT_M_POWERPC_H__ /** * @file m_ppc.h * @brief Describes PowerPC family processor specifics. */ #define PJ_M_NAME "powerpc" #define PJ_HAS_PENTIUM 0 #define PJ_IS_LITTLE_ENDIAN 0 #define PJ_IS_BIG_ENDIAN 1 #endif /* __PJ_COMPAT_M_POWERPC_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/m_sparc.h ================================================ /* $Id: m_sparc.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_M_SPARC_H__ #define __PJ_COMPAT_M_SPARC_H__ /** * @file m_sparc.h * @brief Describes SPARC family processor specifics. */ #define PJ_M_NAME "sparc" #define PJ_HAS_PENTIUM 0 #define PJ_IS_LITTLE_ENDIAN 0 #define PJ_IS_BIG_ENDIAN 1 #endif /* __PJ_COMPAT_M_SPARC_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/m_x86_64.h ================================================ /* $Id: m_x86_64.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_M_x86_64_H__ #define __PJ_COMPAT_M_x86_64_H__ /** * @file m_i386.h * @brief Describes 64bit x86 Intel/AMD family processor specifics. */ #define PJ_M_NAME "x86_64" #define PJ_HAS_PENTIUM 1 #define PJ_IS_LITTLE_ENDIAN 1 #define PJ_IS_BIG_ENDIAN 0 #endif /* __PJ_COMPAT_M_x86_64_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/malloc.h ================================================ /* $Id: malloc.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_MALLOC_H__ #define __PJ_COMPAT_MALLOC_H__ /** * @file malloc.h * @brief Provides malloc() and free() functions. */ #if defined(PJ_HAS_MALLOC_H) && PJ_HAS_MALLOC_H != 0 # include #elif defined(PJ_HAS_STDLIB_H) && PJ_HAS_STDLIB_H != 0 # include #endif #endif /* __PJ_COMPAT_MALLOC_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/os_auto.h.in ================================================ /* $Id: os_auto.h.in 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_OS_AUTO_H__ #define __PJ_COMPAT_OS_AUTO_H__ /** * @file os_auto.h * @brief Describes operating system specifics (automatically detected by * autoconf) */ /* Canonical OS name */ #undef PJ_OS_NAME /* Legacy macros */ #undef PJ_WIN32 #undef PJ_WIN32_WINNT #undef WIN32_LEAN_AND_MEAN #undef PJ_DARWINOS #undef PJ_LINUX #undef PJ_RTEMS #undef PJ_SUNOS #if defined(PJ_WIN32_WINNT) && !defined(_WIN32_WINNT) # define _WIN32_WINNT PJ_WIN32_WINNT #endif /* Headers availability */ #undef PJ_HAS_ARPA_INET_H #undef PJ_HAS_ASSERT_H #undef PJ_HAS_CTYPE_H #undef PJ_HAS_ERRNO_H #undef PJ_HAS_FCNTL_H #undef PJ_HAS_LIMITS_H #undef PJ_HAS_LINUX_SOCKET_H #undef PJ_HAS_MALLOC_H #undef PJ_HAS_NETDB_H #undef PJ_HAS_NETINET_IN_SYSTM_H #undef PJ_HAS_NETINET_IN_H #undef PJ_HAS_NETINET_IP_H #undef PJ_HAS_NETINET_TCP_H #undef PJ_HAS_NET_IF_H #undef PJ_HAS_IFADDRS_H #undef PJ_HAS_SEMAPHORE_H #undef PJ_HAS_SETJMP_H #undef PJ_HAS_STDARG_H #undef PJ_HAS_STDDEF_H #undef PJ_HAS_STDIO_H #undef PJ_HAS_STDINT_H #undef PJ_HAS_STDLIB_H #undef PJ_HAS_STRING_H #undef PJ_HAS_SYS_IOCTL_H #undef PJ_HAS_SYS_SELECT_H #undef PJ_HAS_SYS_SOCKET_H #undef PJ_HAS_SYS_TIME_H #undef PJ_HAS_SYS_TIMEB_H #undef PJ_HAS_SYS_TYPES_H #undef PJ_HAS_SYS_FILIO_H #undef PJ_HAS_SYS_SOCKIO_H #undef PJ_HAS_SYS_UTSNAME_H #undef PJ_HAS_TIME_H #undef PJ_HAS_UNISTD_H #undef PJ_HAS_MSWSOCK_H #undef PJ_HAS_WINSOCK_H #undef PJ_HAS_WINSOCK2_H #undef PJ_HAS_WS2TCPIP_H #undef PJ_SOCK_HAS_INET_ATON #undef PJ_SOCK_HAS_INET_PTON #undef PJ_SOCK_HAS_INET_NTOP #undef PJ_SOCK_HAS_GETADDRINFO /* On these OSes, semaphore feature depends on semaphore.h */ #if defined(PJ_HAS_SEMAPHORE_H) && PJ_HAS_SEMAPHORE_H!=0 # define PJ_HAS_SEMAPHORE 1 #elif defined(PJ_WIN32) && PJ_WIN32!=0 # define PJ_HAS_SEMAPHORE 1 #else # define PJ_HAS_SEMAPHORE 0 #endif /* Do we have pthread_mutexattr_settype()? */ #undef PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE /* Does pthread_mutexattr_t has "recursive" member? */ #undef PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE /* Set 1 if native sockaddr_in has sin_len member. * Default: 0 */ #undef PJ_SOCKADDR_HAS_LEN /* Does the OS have socklen_t? */ #undef PJ_HAS_SOCKLEN_T #if !defined(socklen_t) && (!defined(PJ_HAS_SOCKLEN_T) || PJ_HAS_SOCKLEN_T==0) # define PJ_HAS_SOCKLEN_T 1 typedef int socklen_t; #endif /** * If this macro is set, it tells select I/O Queue that select() needs to * be given correct value of nfds (i.e. largest fd + 1). This requires * select ioqueue to re-scan the descriptors on each registration and * unregistration. * If this macro is not set, then ioqueue will always give FD_SETSIZE for * nfds argument when calling select(). * * Default: 0 */ #undef PJ_SELECT_NEEDS_NFDS /* Was Linux epoll support enabled */ #undef PJ_HAS_LINUX_EPOLL /* Is errno a good way to retrieve OS errors? */ #undef PJ_HAS_ERRNO_VAR /* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return * the status of non-blocking connect() operation. */ #undef PJ_HAS_SO_ERROR /* This value specifies the value set in errno by the OS when a non-blocking * socket recv() can not return immediate daata. */ #undef PJ_BLOCKING_ERROR_VAL /* This value specifies the value set in errno by the OS when a non-blocking * socket connect() can not get connected immediately. */ #undef PJ_BLOCKING_CONNECT_ERROR_VAL /* Default threading is enabled, unless it's overridden. */ #ifndef PJ_HAS_THREADS # define PJ_HAS_THREADS (1) #endif /* Do we need high resolution timer? */ #undef PJ_HAS_HIGH_RES_TIMER /* Is malloc() available? */ #undef PJ_HAS_MALLOC #ifndef PJ_OS_HAS_CHECK_STACK # define PJ_OS_HAS_CHECK_STACK 0 #endif /* Unicode? */ #undef PJ_NATIVE_STRING_IS_UNICODE /* Pool alignment in bytes */ #undef PJ_POOL_ALIGNMENT /* The type of atomic variable value: */ #undef PJ_ATOMIC_VALUE_TYPE #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 # include "TargetConditionals.h" # if TARGET_OS_IPHONE # include "Availability.h" /* Use CFHost API for pj_getaddrinfo() (see ticket #1246) */ # define PJ_GETADDRINFO_USE_CFHOST 1 /* Disable local host resolution in pj_gethostip() (see ticket #1342) */ # define PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION 1 # ifdef __IPHONE_4_0 /* Is multitasking support available? (see ticket #1107) */ # define PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT 1 /* Enable activesock TCP background mode support */ # define PJ_ACTIVESOCK_TCP_IPHONE_OS_BG 1 # endif # endif #endif /* If 1, use Read/Write mutex emulation for platforms that don't support it */ #undef PJ_EMULATE_RWMUTEX /* If 1, pj_thread_create() should enforce the stack size when creating * threads. * Default: 0 (let OS decide the thread's stack size). */ #undef PJ_THREAD_SET_STACK_SIZE /* If 1, pj_thread_create() should allocate stack from the pool supplied. * Default: 0 (let OS allocate memory for thread's stack). */ #undef PJ_THREAD_ALLOCATE_STACK /* SSL socket availability. */ #ifndef PJ_HAS_SSL_SOCK #undef PJ_HAS_SSL_SOCK #endif #endif /* __PJ_COMPAT_OS_AUTO_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/os_darwinos.h ================================================ /* $Id: os_darwinos.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_OS_DARWINOS_H__ #define __PJ_COMPAT_OS_DARWINOS_H__ /** * @file os_darwinos.h * @brief Describes Darwin/MacOSX operating system specifics. */ #define PJ_OS_NAME "darwin" #define PJ_HAS_ARPA_INET_H 1 #define PJ_HAS_ASSERT_H 1 #define PJ_HAS_CTYPE_H 1 #define PJ_HAS_ERRNO_H 1 #define PJ_HAS_LINUX_SOCKET_H 0 #define PJ_HAS_MALLOC_H 0 #define PJ_HAS_NETDB_H 1 #define PJ_HAS_NETINET_IN_H 1 #define PJ_HAS_NETINET_TCP_H 1 #define PJ_HAS_SETJMP_H 1 #define PJ_HAS_STDARG_H 1 #define PJ_HAS_STDDEF_H 1 #define PJ_HAS_STDIO_H 1 #define PJ_HAS_STDLIB_H 1 #define PJ_HAS_STRING_H 1 #define PJ_HAS_SYS_IOCTL_H 1 #define PJ_HAS_SYS_SELECT_H 1 #define PJ_HAS_SYS_SOCKET_H 1 #define PJ_HAS_SYS_TIME_H 1 #define PJ_HAS_SYS_TIMEB_H 1 #define PJ_HAS_SYS_TYPES_H 1 #define PJ_HAS_TIME_H 1 #define PJ_HAS_UNISTD_H 1 #define PJ_HAS_MSWSOCK_H 0 #define PJ_HAS_WINSOCK_H 0 #define PJ_HAS_WINSOCK2_H 0 /* Is errno a good way to retrieve OS errors? */ #define PJ_HAS_ERRNO_VAR 1 /* Has inet_aton() ? */ #define PJ_SOCK_HAS_INET_ATON 1 /* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return * the status of non-blocking connect() operation. */ #define PJ_HAS_SO_ERROR 1 /* This value specifies the value set in errno by the OS when a non-blocking * socket recv() can not return immediate daata. */ #define PJ_BLOCKING_ERROR_VAL EWOULDBLOCK /* This value specifies the value set in errno by the OS when a non-blocking * socket connect() can not get connected immediately. */ #define PJ_BLOCKING_CONNECT_ERROR_VAL EINPROGRESS /* Default threading is enabled, unless it's overridden. */ #ifndef PJ_HAS_THREADS # define PJ_HAS_THREADS (1) #endif #define PJ_HAS_HIGH_RES_TIMER 1 #define PJ_HAS_MALLOC 1 #ifndef PJ_OS_HAS_CHECK_STACK # define PJ_OS_HAS_CHECK_STACK 0 #endif #define PJ_NATIVE_STRING_IS_UNICODE 0 #define PJ_ATOMIC_VALUE_TYPE long /* * Socket related */ typedef int socklen_t; /* Set 1 if native sockaddr_in has sin_len member. * Default: 0 */ #define PJ_SOCKADDR_HAS_LEN 1 /* * gcc complains that it can not use precompiled header because * the value of FD_SETSIZE that we declare in pj/config.h is * different than the value in /usr/include/sys/types.h. * * This changes the default value for Darwin. */ #define PJ_IOQUEUE_MAX_HANDLES 1024 /** * If this macro is set, it tells select I/O Queue that select() needs to * be given correct value of nfds (i.e. largest fd + 1). This requires * select ioqueue to re-scan the descriptors on each registration and * unregistration. * If this macro is not set, then ioqueue will always give FD_SETSIZE for * nfds argument when calling select(). * * Default: 0 */ #define PJ_SELECT_NEEDS_NFDS 0 /* If 1, use Read/Write mutex emulation for platforms that don't support it */ #define PJ_EMULATE_RWMUTEX 0 /* If 1, pj_thread_create() should enforce the stack size when creating * threads. * Default: 0 (let OS decide the thread's stack size). */ #define PJ_THREAD_SET_STACK_SIZE 0 /* If 1, pj_thread_create() should allocate stack from the pool supplied. * Default: 0 (let OS allocate memory for thread's stack). */ #define PJ_THREAD_ALLOCATE_STACK 0 /* Oh well.. MacOS 10.2 doesn't have socklen_t, but 10.4 has! */ #define PJ_HAS_SOCKLEN_T 0 #endif /* __PJ_COMPAT_OS_DARWINOS_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/os_linux.h ================================================ /* $Id: os_linux.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_OS_LINUX_H__ #define __PJ_COMPAT_OS_LINUX_H__ /** * @file os_linux.h * @brief Describes Linux operating system specifics. */ #define PJ_OS_NAME "linux" #define PJ_HAS_ARPA_INET_H 1 #define PJ_HAS_ASSERT_H 1 #define PJ_HAS_CTYPE_H 1 #define PJ_HAS_ERRNO_H 1 #define PJ_HAS_LINUX_SOCKET_H 0 #define PJ_HAS_MALLOC_H 1 #define PJ_HAS_NETDB_H 1 #define PJ_HAS_NETINET_IN_H 1 #define PJ_HAS_SETJMP_H 1 #define PJ_HAS_STDARG_H 1 #define PJ_HAS_STDDEF_H 1 #define PJ_HAS_STDIO_H 1 #define PJ_HAS_STDLIB_H 1 #define PJ_HAS_STRING_H 1 #define PJ_HAS_SYS_IOCTL_H 1 #define PJ_HAS_SYS_SELECT_H 1 #define PJ_HAS_SYS_SOCKET_H 1 #define PJ_HAS_SYS_TIME_H 1 #define PJ_HAS_SYS_TIMEB_H 1 #define PJ_HAS_SYS_TYPES_H 1 #define PJ_HAS_TIME_H 1 #define PJ_HAS_UNISTD_H 1 #define PJ_HAS_SEMAPHORE_H 1 #define PJ_HAS_MSWSOCK_H 0 #define PJ_HAS_WINSOCK_H 0 #define PJ_HAS_WINSOCK2_H 0 #define PJ_SOCK_HAS_INET_ATON 1 /* Set 1 if native sockaddr_in has sin_len member. * Default: 0 */ #define PJ_SOCKADDR_HAS_LEN 0 /** * If this macro is set, it tells select I/O Queue that select() needs to * be given correct value of nfds (i.e. largest fd + 1). This requires * select ioqueue to re-scan the descriptors on each registration and * unregistration. * If this macro is not set, then ioqueue will always give FD_SETSIZE for * nfds argument when calling select(). * * Default: 0 */ #define PJ_SELECT_NEEDS_NFDS 0 /* Is errno a good way to retrieve OS errors? */ #define PJ_HAS_ERRNO_VAR 1 /* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return * the status of non-blocking connect() operation. */ #define PJ_HAS_SO_ERROR 1 /* This value specifies the value set in errno by the OS when a non-blocking * socket recv() can not return immediate daata. */ #define PJ_BLOCKING_ERROR_VAL EAGAIN /* This value specifies the value set in errno by the OS when a non-blocking * socket connect() can not get connected immediately. */ #define PJ_BLOCKING_CONNECT_ERROR_VAL EINPROGRESS /* Default threading is enabled, unless it's overridden. */ #ifndef PJ_HAS_THREADS # define PJ_HAS_THREADS (1) #endif #define PJ_HAS_HIGH_RES_TIMER 1 #define PJ_HAS_MALLOC 1 #ifndef PJ_OS_HAS_CHECK_STACK # define PJ_OS_HAS_CHECK_STACK 0 #endif #define PJ_NATIVE_STRING_IS_UNICODE 0 #define PJ_ATOMIC_VALUE_TYPE long /* If 1, use Read/Write mutex emulation for platforms that don't support it */ #define PJ_EMULATE_RWMUTEX 0 /* If 1, pj_thread_create() should enforce the stack size when creating * threads. * Default: 0 (let OS decide the thread's stack size). */ #define PJ_THREAD_SET_STACK_SIZE 0 /* If 1, pj_thread_create() should allocate stack from the pool supplied. * Default: 0 (let OS allocate memory for thread's stack). */ #define PJ_THREAD_ALLOCATE_STACK 0 /* Linux has socklen_t */ #define PJ_HAS_SOCKLEN_T 1 #endif /* __PJ_COMPAT_OS_LINUX_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/os_linux_kernel.h ================================================ /* $Id: os_linux_kernel.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_OS_LINUX_KERNEL_H__ #define __PJ_COMPAT_OS_LINUX_KERNEL_H__ /** * @file os_linux.h * @brief Describes Linux operating system specifics. */ #define PJ_OS_NAME "linux-module" #define PJ_HAS_ARPA_INET_H 0 #define PJ_HAS_ASSERT_H 0 #define PJ_HAS_CTYPE_H 0 #define PJ_HAS_ERRNO_H 0 #define PJ_HAS_LINUX_SOCKET_H 1 #define PJ_HAS_MALLOC_H 0 #define PJ_HAS_NETDB_H 0 #define PJ_HAS_NETINET_IN_H 0 #define PJ_HAS_SETJMP_H 0 #define PJ_HAS_STDARG_H 1 #define PJ_HAS_STDDEF_H 0 #define PJ_HAS_STDIO_H 0 #define PJ_HAS_STDLIB_H 0 #define PJ_HAS_STRING_H 0 #define PJ_HAS_SYS_IOCTL_H 0 #define PJ_HAS_SYS_SELECT_H 0 #define PJ_HAS_SYS_SOCKET_H 0 #define PJ_HAS_SYS_TIME_H 0 #define PJ_HAS_SYS_TIMEB_H 0 #define PJ_HAS_SYS_TYPES_H 0 #define PJ_HAS_TIME_H 0 #define PJ_HAS_UNISTD_H 0 #define PJ_HAS_MSWSOCK_H 0 #define PJ_HAS_WINSOCK_H 0 #define PJ_HAS_WINSOCK2_H 0 #define PJ_SOCK_HAS_INET_ATON 0 /* Set 1 if native sockaddr_in has sin_len member. * Default: 0 */ #define PJ_SOCKADDR_HAS_LEN 0 /* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return * the status of non-blocking connect() operation. */ #define PJ_HAS_SO_ERROR 1 /** * If this macro is set, it tells select I/O Queue that select() needs to * be given correct value of nfds (i.e. largest fd + 1). This requires * select ioqueue to re-scan the descriptors on each registration and * unregistration. * If this macro is not set, then ioqueue will always give FD_SETSIZE for * nfds argument when calling select(). * * Default: 0 */ #define PJ_SELECT_NEEDS_NFDS 0 /* Is errno a good way to retrieve OS errors? * (probably no for linux kernel) * If you answer no here, you'll need to tell pjlib how to get OS * error (a compile error will tell you exactly where) */ #define PJ_HAS_ERRNO_VAR 0 /* This value specifies the value set in errno by the OS when a non-blocking * socket recv() can not return immediate daata. */ #define PJ_BLOCKING_ERROR_VAL EAGAIN /* This value specifies the value set in errno by the OS when a non-blocking * socket connect() can not get connected immediately. */ #define PJ_BLOCKING_CONNECT_ERROR_VAL EINPROGRESS #ifndef PJ_HAS_THREADS # define PJ_HAS_THREADS (1) #endif /* * Declare __FD_SETSIZE now before including . */ #define __FD_SETSIZE PJ_IOQUEUE_MAX_HANDLES #define NULL ((void*)0) #include /* Needed by all modules */ #include /* Needed for KERN_INFO */ #define __PJ_EXPORT_SYMBOL(a) EXPORT_SYMBOL(a); /* * Override features. */ #define PJ_HAS_FLOATING_POINT 0 #define PJ_HAS_MALLOC 0 #define PJ_HAS_SEMAPHORE 0 #define PJ_HAS_EVENT_OBJ 0 #define PJ_HAS_HIGH_RES_TIMER 1 #ifndef PJ_OS_HAS_CHECK_STACK # define PJ_OS_HAS_CHECK_STACK 0 #endif #define PJ_TERM_HAS_COLOR 0 #define PJ_NATIVE_STRING_IS_UNICODE 0 #define PJ_ATOMIC_VALUE_TYPE int #define PJ_THREAD_DESC_SIZE 128 /* If 1, use Read/Write mutex emulation for platforms that don't support it */ #define PJ_EMULATE_RWMUTEX 0 /* If 1, pj_thread_create() should enforce the stack size when creating * threads. * Default: 0 (let OS decide the thread's stack size). */ #define PJ_THREAD_SET_STACK_SIZE 0 /* If 1, pj_thread_create() should allocate stack from the pool supplied. * Default: 0 (let OS allocate memory for thread's stack). */ #define PJ_THREAD_ALLOCATE_STACK 0 #endif /* __PJ_COMPAT_OS_LINUX_KERNEL_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/os_palmos.h ================================================ /* $Id: os_palmos.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_OS_PALMOS_H__ #define __PJ_COMPAT_OS_PALMOS_H__ /** * @file os_palmos.h * @brief Describes PalmOS operating system specifics. */ #define PJ_OS_NAME "palmos" #define PJ_HAS_ARPA_INET_H 0 #define PJ_HAS_ASSERT_H 1 #define PJ_HAS_CTYPE_H 1 #define PJ_HAS_ERRNO_H 0 #define PJ_HAS_MALLOC_H 1 #define PJ_HAS_NETDB_H 0 #define PJ_HAS_NETINET_IN_H 0 #define PJ_HAS_SETJMP_H 1 #define PJ_HAS_STDARG_H 1 #define PJ_HAS_STDDEF_H 1 #define PJ_HAS_STDIO_H 1 #define PJ_HAS_STDLIB_H 1 #define PJ_HAS_STRING_H 1 #define PJ_HAS_SYS_IOCTL_H 0 #define PJ_HAS_SYS_SELECT_H 0 #define PJ_HAS_SYS_SOCKET_H 0 #define PJ_HAS_SYS_TIMEB_H 0 #define PJ_HAS_SYS_TYPES_H 1 #define PJ_HAS_TIME_H 1 #define PJ_HAS_UNISTD_H 0 #define PJ_HAS_MSWSOCK_H 0 #define PJ_HAS_WINSOCK_H 0 #define PJ_HAS_WINSOCK2_H 0 #define PJ_SOCK_HAS_INET_ATON 0 /* Set 1 if native sockaddr_in has sin_len member. * Default: 0 */ #define PJ_SOCKADDR_HAS_LEN 0 /** * If this macro is set, it tells select I/O Queue that select() needs to * be given correct value of nfds (i.e. largest fd + 1). This requires * select ioqueue to re-scan the descriptors on each registration and * unregistration. * If this macro is not set, then ioqueue will always give FD_SETSIZE for * nfds argument when calling select(). * * Default: 0 */ #define PJ_SELECT_NEEDS_NFDS 0 /* Is errno a good way to retrieve OS errors? */ #define PJ_HAS_ERRNO_VAR 0 /* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return * the status of non-blocking connect() operation. */ #define PJ_HAS_SO_ERROR 0 /* This value specifies the value set in errno by the OS when a non-blocking * socket recv() can not return immediate daata. */ #define PJ_BLOCKING_ERROR_VAL xxx /* This value specifies the value set in errno by the OS when a non-blocking * socket connect() can not get connected immediately. */ #define PJ_BLOCKING_CONNECT_ERROR_VAL xxx /* Default threading is enabled, unless it's overridden. */ #ifndef PJ_HAS_THREADS # define PJ_HAS_THREADS (1) #endif #define PJ_HAS_HIGH_RES_TIMER 1 #ifndef PJ_OS_HAS_CHECK_STACK # define PJ_OS_HAS_CHECK_STACK 0 #define PJ_NATIVE_STRING_IS_UNICODE 0 /* If 1, use Read/Write mutex emulation for platforms that don't support it */ #define PJ_EMULATE_RWMUTEX 1 /* If 1, pj_thread_create() should enforce the stack size when creating * threads. * Default: 0 (let OS decide the thread's stack size). */ #define PJ_THREAD_SET_STACK_SIZE 0 /* If 1, pj_thread_create() should allocate stack from the pool supplied. * Default: 0 (let OS allocate memory for thread's stack). */ #define PJ_THREAD_ALLOCATE_STACK 0 #endif /* __PJ_COMPAT_OS_PALMOS_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/os_rtems.h ================================================ /* $Id: os_rtems.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Thanks Zetron, Inc and Phil Torre for donating PJLIB * port to RTEMS. */ #ifndef __PJ_COMPAT_OS_RTEMS_H__ #define __PJ_COMPAT_OS_RTEMS_H__ /** * @file os_linux.h * @brief Describes Linux operating system specifics. */ #define PJ_OS_NAME "rtems" #define PJ_HAS_ARPA_INET_H 1 #define PJ_HAS_ASSERT_H 1 #define PJ_HAS_CTYPE_H 1 #define PJ_HAS_ERRNO_H 1 #define PJ_HAS_LINUX_SOCKET_H 0 #define PJ_HAS_MALLOC_H 1 #define PJ_HAS_NETDB_H 1 #define PJ_HAS_NETINET_IN_H 1 #define PJ_HAS_SETJMP_H 1 #define PJ_HAS_STDARG_H 0 #define PJ_HAS_STDDEF_H 1 #define PJ_HAS_STDIO_H 1 #define PJ_HAS_STDLIB_H 1 #define PJ_HAS_STRING_H 1 #define PJ_HAS_SYS_IOCTL_H 1 #define PJ_HAS_SYS_SELECT_H 1 #define PJ_HAS_SYS_SOCKET_H 1 #define PJ_HAS_SYS_TIME_H 1 #define PJ_HAS_SYS_TIMEB_H 1 #define PJ_HAS_SYS_TYPES_H 1 #define PJ_HAS_TIME_H 1 #define PJ_HAS_UNISTD_H 1 #define PJ_HAS_MSWSOCK_H 0 #define PJ_HAS_WINSOCK_H 0 #define PJ_HAS_WINSOCK2_H 0 #define PJ_SOCK_HAS_INET_ATON 1 /* Set 1 if native sockaddr_in has sin_len member. * Default: 0 */ #define PJ_SOCKADDR_HAS_LEN 1 /* Is errno a good way to retrieve OS errors? */ #define PJ_HAS_ERRNO_VAR 1 /* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return * the status of non-blocking connect() operation. */ #define PJ_HAS_SO_ERROR 1 /** * If this macro is set, it tells select I/O Queue that select() needs to * be given correct value of nfds (i.e. largest fd + 1). This requires * select ioqueue to re-scan the descriptors on each registration and * unregistration. * If this macro is not set, then ioqueue will always give FD_SETSIZE for * nfds argument when calling select(). * * Default: 0 */ #define PJ_SELECT_NEEDS_NFDS 1 /* This value specifies the value set in errno by the OS when a non-blocking * socket recv() can not return immediate daata. */ #define PJ_BLOCKING_ERROR_VAL EWOULDBLOCK /* This value specifies the value set in errno by the OS when a non-blocking * socket connect() can not get connected immediately. */ #define PJ_BLOCKING_CONNECT_ERROR_VAL EINPROGRESS /* Default threading is enabled, unless it's overridden. */ #ifndef PJ_HAS_THREADS # define PJ_HAS_THREADS (1) #endif #define PJ_HAS_HIGH_RES_TIMER 1 #define PJ_HAS_MALLOC 1 #ifndef PJ_OS_HAS_CHECK_STACK # define PJ_OS_HAS_CHECK_STACK 0 #endif #define PJ_NATIVE_STRING_IS_UNICODE 0 #define PJ_ATOMIC_VALUE_TYPE int /* If 1, use Read/Write mutex emulation for platforms that don't support it */ #define PJ_EMULATE_RWMUTEX 1 /* Missing socklen_t */ typedef int socklen_t; /* If 1, pj_thread_create() should enforce the stack size when creating * threads. * Default: 0 (let OS decide the thread's stack size). */ #define PJ_THREAD_SET_STACK_SIZE 1 /* If 1, pj_thread_create() should allocate stack from the pool supplied. * Default: 0 (let OS allocate memory for thread's stack). */ #define PJ_THREAD_ALLOCATE_STACK 1 /* RTEMS has socklen_t (does it? )*/ #define PJ_HAS_SOCKLEN_T 1 #endif /* __PJ_COMPAT_OS_RTEMS_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/os_sunos.h ================================================ /* $Id: os_sunos.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_OS_SUNOS_H__ #define __PJ_COMPAT_OS_SUNOS_H__ /** * @file os_sunos.h * @brief Describes SunOS/Solaris operating system specifics. */ #define PJ_OS_NAME "sunos" #define PJ_HAS_ARPA_INET_H 1 #define PJ_HAS_ASSERT_H 1 #define PJ_HAS_CTYPE_H 1 #define PJ_HAS_ERRNO_H 1 #define PJ_HAS_LINUX_SOCKET_H 0 #define PJ_HAS_MALLOC_H 1 #define PJ_HAS_NETDB_H 1 #define PJ_HAS_NETINET_IN_H 1 #define PJ_HAS_SETJMP_H 1 #define PJ_HAS_STDARG_H 1 #define PJ_HAS_STDDEF_H 1 #define PJ_HAS_STDIO_H 1 #define PJ_HAS_STDLIB_H 1 #define PJ_HAS_STRING_H 1 #define PJ_HAS_SYS_IOCTL_H 1 #define PJ_HAS_SYS_SELECT_H 1 #define PJ_HAS_SYS_SOCKET_H 1 #define PJ_HAS_SYS_TIME_H 0 #define PJ_HAS_SYS_TIMEB_H 1 #define PJ_HAS_SYS_TYPES_H 1 #define PJ_HAS_TIME_H 1 #define PJ_HAS_UNISTD_H 1 #define PJ_HAS_MSWSOCK_H 0 #define PJ_HAS_WINSOCK_H 0 #define PJ_HAS_WINSOCK2_H 0 #define PJ_SOCK_HAS_INET_ATON 0 /* Set 1 if native sockaddr_in has sin_len member. * Default: 0 */ #define PJ_SOCKADDR_HAS_LEN 0 /* Is errno a good way to retrieve OS errors? */ #define PJ_HAS_ERRNO_VAR 1 /* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return * the status of non-blocking connect() operation. */ #define PJ_HAS_SO_ERROR 0 /* This value specifies the value set in errno by the OS when a non-blocking * socket recv() can not return immediate daata. */ #define PJ_BLOCKING_ERROR_VAL EWOULDBLOCK /* This value specifies the value set in errno by the OS when a non-blocking * socket connect() can not get connected immediately. */ #define PJ_BLOCKING_CONNECT_ERROR_VAL EINPROGRESS /** * If this macro is set, it tells select I/O Queue that select() needs to * be given correct value of nfds (i.e. largest fd + 1). This requires * select ioqueue to re-scan the descriptors on each registration and * unregistration. * If this macro is not set, then ioqueue will always give FD_SETSIZE for * nfds argument when calling select(). * * Default: 0 */ #define PJ_SELECT_NEEDS_NFDS 0 /* Default threading is enabled, unless it's overridden. */ #ifndef PJ_HAS_THREADS # define PJ_HAS_THREADS (1) #endif #define PJ_HAS_HIGH_RES_TIMER 1 #define PJ_HAS_MALLOC 1 #ifndef PJ_OS_HAS_CHECK_STACK # define PJ_OS_HAS_CHECK_STACK 0 #endif #define PJ_NATIVE_STRING_IS_UNICODE 0 #define PJ_ATOMIC_VALUE_TYPE long /* Get BSD related identifers in Sun's include files */ #define BSD_COMP /* If 1, use Read/Write mutex emulation for platforms that don't support it */ #define PJ_EMULATE_RWMUTEX 0 /* If 1, pj_thread_create() should enforce the stack size when creating * threads. * Default: 0 (let OS decide the thread's stack size). */ #define PJ_THREAD_SET_STACK_SIZE 0 /* If 1, pj_thread_create() should allocate stack from the pool supplied. * Default: 0 (let OS allocate memory for thread's stack). */ #define PJ_THREAD_ALLOCATE_STACK 0 /* SunOS has socklen_t (does it? )*/ #define PJ_HAS_SOCKLEN_T 1 #endif /* __PJ_COMPAT_OS_SUNOS_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/os_symbian.h ================================================ /* $Id: os_symbian.h 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_OS_SYMBIAN_H__ #define __PJ_COMPAT_OS_SYMBIAN_H__ /** * @file os_symbian.h * @brief Describes Symbian operating system specifics. */ #define PJ_OS_NAME "symbian" #define PJ_HAS_ARPA_INET_H 1 #define PJ_HAS_ASSERT_H 1 #define PJ_HAS_CTYPE_H 1 #define PJ_HAS_ERRNO_H 1 #define PJ_HAS_LINUX_SOCKET_H 0 #define PJ_HAS_MALLOC_H 0 #define PJ_HAS_NETDB_H 1 #define PJ_HAS_NETINET_IN_H 1 #define PJ_HAS_NETINET_TCP_H 0 #define PJ_HAS_SETJMP_H 1 #define PJ_HAS_STDARG_H 1 #define PJ_HAS_STDDEF_H 1 #define PJ_HAS_STDIO_H 1 #define PJ_HAS_STDLIB_H 1 #define PJ_HAS_STRING_H 1 #define PJ_HAS_NO_SNPRINTF 1 #define PJ_HAS_SYS_IOCTL_H 1 #define PJ_HAS_SYS_SELECT_H 0 #define PJ_HAS_SYS_SOCKET_H 1 #define PJ_HAS_SYS_TIME_H 1 #define PJ_HAS_SYS_TIMEB_H 0 #define PJ_HAS_SYS_TYPES_H 1 #define PJ_HAS_TIME_H 1 #define PJ_HAS_UNISTD_H 1 #define PJ_HAS_MSWSOCK_H 0 #define PJ_HAS_WINSOCK_H 0 #define PJ_HAS_WINSOCK2_H 0 #define PJ_SOCK_HAS_INET_ATON 0 /* Set 1 if native sockaddr_in has sin_len member. * Default: 0 */ #define PJ_SOCKADDR_HAS_LEN 0 /* Is errno a good way to retrieve OS errors? */ #define PJ_HAS_ERRNO_VAR 1 /* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return * the status of non-blocking connect() operation. */ #define PJ_HAS_SO_ERROR 1 /** * If this macro is set, it tells select I/O Queue that select() needs to * be given correct value of nfds (i.e. largest fd + 1). This requires * select ioqueue to re-scan the descriptors on each registration and * unregistration. * If this macro is not set, then ioqueue will always give FD_SETSIZE for * nfds argument when calling select(). * * Default: 0 */ #define PJ_SELECT_NEEDS_NFDS 0 /* This value specifies the value set in errno by the OS when a non-blocking * socket recv() can not return immediate daata. */ #define PJ_BLOCKING_ERROR_VAL EAGAIN /* This value specifies the value set in errno by the OS when a non-blocking * socket connect() can not get connected immediately. */ #define PJ_BLOCKING_CONNECT_ERROR_VAL EINPROGRESS /* * We don't want to use threads in Symbian */ #define PJ_HAS_THREADS 0 /* * Declare __FD_SETSIZE now before including . #define __FD_SETSIZE PJ_IOQUEUE_MAX_HANDLES */ #ifndef NULL # define NULL 0 #endif /* Endianness */ #ifndef PJ_IS_LITTLE_ENDIAN # define PJ_IS_LITTLE_ENDIAN 1 # define PJ_IS_BIG_ENDIAN 0 #endif /* Doesn't seem to allow more than this */ #define PJ_IOQUEUE_MAX_HANDLES 8 /* * Override features. */ #define PJ_HAS_FLOATING_POINT 0 #define PJ_HAS_MALLOC 0 #define PJ_HAS_SEMAPHORE 1 #define PJ_HAS_EVENT_OBJ 0 #define PJ_HAS_HIGH_RES_TIMER 1 #define PJ_OS_HAS_CHECK_STACK 0 #define PJ_TERM_HAS_COLOR 0 #define PJ_NATIVE_STRING_IS_UNICODE 0 #define PJ_NATIVE_ERR_POSITIVE 0 #define PJ_ATOMIC_VALUE_TYPE int #define PJ_THREAD_DESC_SIZE 128 /* If 1, use Read/Write mutex emulation for platforms that don't support it */ #define PJ_EMULATE_RWMUTEX 1 /* If 1, pj_thread_create() should enforce the stack size when creating * threads. * Default: 0 (let OS decide the thread's stack size). */ #define PJ_THREAD_SET_STACK_SIZE 0 /* If 1, pj_thread_create() should allocate stack from the pool supplied. * Default: 0 (let OS allocate memory for thread's stack). */ #define PJ_THREAD_ALLOCATE_STACK 0 /* Missing socklen_t */ #define PJ_HAS_SOCKLEN_T 1 typedef unsigned int socklen_t; #ifndef __GCCE__ #include #endif #define PJ_EXPORT_DECL_SPECIFIER IMPORT_C //#define PJ_EXPORT_DECL_SPECIFIER #define PJ_EXPORT_DEF_SPECIFIER EXPORT_C #define PJ_IMPORT_DECL_SPECIFIER IMPORT_C #endif /* __PJ_COMPAT_OS_SYMBIAN_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/os_win32.h ================================================ /* $Id: os_win32.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_OS_WIN32_H__ #define __PJ_COMPAT_OS_WIN32_H__ /** * @file os_win32.h * @brief Describes Win32 operating system family specifics. */ #define PJ_OS_NAME "win32" #define WIN32_LEAN_AND_MEAN #define PJ_WIN32_WINNT 0x0400 #ifndef _WIN32_WINNT # define _WIN32_WINNT PJ_WIN32_WINNT #endif #define PJ_HAS_ARPA_INET_H 0 #define PJ_HAS_ASSERT_H 1 #define PJ_HAS_CTYPE_H 1 #define PJ_HAS_ERRNO_H 0 /* Must be zero, otherwise errno_test() fails. */ #define PJ_HAS_LINUX_SOCKET_H 0 #define PJ_HAS_MALLOC_H 1 #define PJ_HAS_NETDB_H 0 #define PJ_HAS_NETINET_IN_H 0 #define PJ_HAS_NETINET_TCP_H 0 #define PJ_HAS_SETJMP_H 1 #define PJ_HAS_STDARG_H 1 #define PJ_HAS_STDDEF_H 1 #undef PJ_HAS_STDINT_H #define PJ_HAS_STDIO_H 1 #define PJ_HAS_STDLIB_H 1 #define PJ_HAS_STRING_H 1 #define PJ_HAS_SYS_IOCTL_H 0 #define PJ_HAS_SYS_SELECT_H 0 #define PJ_HAS_SYS_SOCKET_H 0 #define PJ_HAS_SYS_TIME_H 0 #define PJ_HAS_SYS_TIMEB_H 1 #define PJ_HAS_SYS_TYPES_H 1 #define PJ_HAS_TIME_H 1 #define PJ_HAS_UNISTD_H 0 #define PJ_HAS_MSWSOCK_H 1 #define PJ_HAS_WINSOCK_H 0 #define PJ_HAS_WINSOCK2_H 1 #define PJ_HAS_WS2TCPIP_H 1 #define PJ_SOCK_HAS_INET_ATON 0 /* Set 1 if native sockaddr_in has sin_len member. * Default: 0 */ #define PJ_SOCKADDR_HAS_LEN 0 /* Is errno a good way to retrieve OS errors? (No) */ #define PJ_HAS_ERRNO_VAR 0 /* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return * the status of non-blocking connect() operation. */ #define PJ_HAS_SO_ERROR 1 /* This value specifies the value set in errno by the OS when a non-blocking * socket recv() or send() can not return immediately. */ #define PJ_BLOCKING_ERROR_VAL WSAEWOULDBLOCK /* This value specifies the value set in errno by the OS when a non-blocking * socket connect() can not get connected immediately. */ #define PJ_BLOCKING_CONNECT_ERROR_VAL WSAEWOULDBLOCK /** * If this macro is set, it tells select I/O Queue that select() needs to * be given correct value of nfds (i.e. largest fd + 1). This requires * select ioqueue to re-scan the descriptors on each registration and * unregistration. * If this macro is not set, then ioqueue will always give FD_SETSIZE for * nfds argument when calling select(). * * Default: 0 */ #define PJ_SELECT_NEEDS_NFDS 0 /* Default threading is enabled, unless it's overridden. */ #ifndef PJ_HAS_THREADS # define PJ_HAS_THREADS (1) #endif #define PJ_HAS_HIGH_RES_TIMER 1 #define PJ_HAS_MALLOC 1 #ifndef PJ_OS_HAS_CHECK_STACK # define PJ_OS_HAS_CHECK_STACK 1 #endif #ifdef UNICODE # define PJ_NATIVE_STRING_IS_UNICODE 1 #else # define PJ_NATIVE_STRING_IS_UNICODE 0 #endif #define PJ_ATOMIC_VALUE_TYPE long /* If 1, use Read/Write mutex emulation for platforms that don't support it */ #define PJ_EMULATE_RWMUTEX 1 /* If 1, pj_thread_create() should enforce the stack size when creating * threads. * Default: 0 (let OS decide the thread's stack size). */ #define PJ_THREAD_SET_STACK_SIZE 0 /* If 1, pj_thread_create() should allocate stack from the pool supplied. * Default: 0 (let OS allocate memory for thread's stack). */ #define PJ_THREAD_ALLOCATE_STACK 0 #endif /* __PJ_COMPAT_OS_WIN32_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/os_win32_wince.h ================================================ /* $Id: os_win32_wince.h 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_OS_WIN32_WINCE_H__ #define __PJ_COMPAT_OS_WIN32_WINCE_H__ /** * @file os_win32.h * @brief Describes Win32 operating system family specifics. */ #define PJ_OS_NAME "win32-wince" #define WIN32_LEAN_AND_MEAN #define RPC_NO_WINDOWS_H //#define PJ_WIN32_WINNT 0x0400 //#define _WIN32_WINNT PJ_WIN32_WINNT #define PJ_HAS_ARPA_INET_H 0 #define PJ_HAS_ASSERT_H 1 #define PJ_HAS_CTYPE_H 1 #define PJ_HAS_ERRNO_H 0 /* Must be zero, otherwise errno_test() fails. */ #define PJ_HAS_LINUX_SOCKET_H 0 #define PJ_HAS_MALLOC_H 1 #define PJ_HAS_NETDB_H 0 #define PJ_HAS_NETINET_IN_H 0 #define PJ_HAS_NETINET_TCP_H 0 #define PJ_HAS_SETJMP_H 1 #define PJ_HAS_STDARG_H 1 #define PJ_HAS_STDDEF_H 1 #define PJ_HAS_STDIO_H 1 #define PJ_HAS_STDLIB_H 1 #define PJ_HAS_STRING_H 1 #define PJ_HAS_SYS_IOCTL_H 0 #define PJ_HAS_SYS_SELECT_H 0 #define PJ_HAS_SYS_SOCKET_H 0 #define PJ_HAS_SYS_TIME_H 0 #define PJ_HAS_SYS_TIMEB_H 0 /* Doesn't have sys/timeb.h */ #define PJ_HAS_SYS_TYPES_H 0 /* Doesn't have sys/types.h */ #define PJ_HAS_TIME_H 1 #define PJ_HAS_UNISTD_H 0 #define PJ_HAS_MSWSOCK_H 1 #define PJ_HAS_WINSOCK_H 0 #define PJ_HAS_WINSOCK2_H 1 #define PJ_SOCK_HAS_INET_ATON 0 /* Set 1 if native sockaddr_in has sin_len member. * Default: 0 */ #define PJ_SOCKADDR_HAS_LEN 0 /* Is errno a good way to retrieve OS errors? (no) */ #define PJ_HAS_ERRNO_VAR 0 /* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return * the status of non-blocking connect() operation. */ #define PJ_HAS_SO_ERROR 0 /* This value specifies the value set in errno by the OS when a non-blocking * socket recv() or send() can not return immediately. */ #define PJ_BLOCKING_ERROR_VAL WSAEWOULDBLOCK /* This value specifies the value set in errno by the OS when a non-blocking * socket connect() can not get connected immediately. */ #define PJ_BLOCKING_CONNECT_ERROR_VAL WSAEWOULDBLOCK /** * If this macro is set, it tells select I/O Queue that select() needs to * be given correct value of nfds (i.e. largest fd + 1). This requires * select ioqueue to re-scan the descriptors on each registration and * unregistration. * If this macro is not set, then ioqueue will always give FD_SETSIZE for * nfds argument when calling select(). * * Default: 0 */ #define PJ_SELECT_NEEDS_NFDS 0 /* Endianness */ #ifndef PJ_IS_LITTLE_ENDIAN # define PJ_IS_LITTLE_ENDIAN 1 # define PJ_IS_BIG_ENDIAN 0 #endif /* Default threading is enabled, unless it's overridden. */ #ifndef PJ_HAS_THREADS # define PJ_HAS_THREADS (1) #endif #define PJ_HAS_HIGH_RES_TIMER 1 #define PJ_HAS_MALLOC 1 #define PJ_OS_HAS_CHECK_STACK 1 #define PJ_ATOMIC_VALUE_TYPE long /* TlsAlloc() error value. */ #define TLS_OUT_OF_INDEXES 0xFFFFFFFF /* No console. */ #define PJ_TERM_HAS_COLOR 0 /* No rdtsc */ #define PJ_TIMESTAMP_USE_RDTSC 0 /* Native string is Unicode. */ #define PJ_NATIVE_STRING_IS_UNICODE 1 /* If 1, use Read/Write mutex emulation for platforms that don't support it */ #define PJ_EMULATE_RWMUTEX 1 /* If 1, pj_thread_create() should enforce the stack size when creating * threads. * Default: 0 (let OS decide the thread's stack size). */ #define PJ_THREAD_SET_STACK_SIZE 0 /* If 1, pj_thread_create() should allocate stack from the pool supplied. * Default: 0 (let OS allocate memory for thread's stack). */ #define PJ_THREAD_ALLOCATE_STACK 0 #endif /* __PJ_COMPAT_OS_WIN32_WINCE_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/rand.h ================================================ /* $Id: rand.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_RAND_H__ #define __PJ_COMPAT_RAND_H__ /** * @file rand.h * @brief Provides platform_rand() and platform_srand() functions. */ #if defined(PJ_HAS_STDLIB_H) && PJ_HAS_STDLIB_H != 0 /* * Use stdlib based rand() and srand(). */ # include # define platform_srand srand # if defined(RAND_MAX) && RAND_MAX <= 0xFFFF /* * When rand() is only 16 bit strong, double the strength * by calling it twice! */ PJ_INLINE(int) platform_rand(void) { return ((rand() & 0xFFFF) << 16) | (rand() & 0xFFFF); } # else # define platform_rand rand # endif #elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 /* * Linux kernel mode random number generator. */ # include # define platform_srand(seed) PJ_INLINE(int) platform_rand(void) { int value; get_random_bytes((void*)&value, sizeof(value)); return value; } #else # warning "platform_rand() is not implemented" # define platform_rand() 1 # define platform_srand(seed) #endif #endif /* __PJ_COMPAT_RAND_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/setjmp.h ================================================ /* $Id: setjmp.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_SETJMP_H__ #define __PJ_COMPAT_SETJMP_H__ /** * @file setjmp.h * @brief Provides setjmp.h functionality. */ #if defined(PJ_HAS_SETJMP_H) && PJ_HAS_SETJMP_H != 0 # include typedef jmp_buf pj_jmp_buf; # ifndef pj_setjmp # define pj_setjmp(buf) setjmp(buf) # endif # ifndef pj_longjmp # define pj_longjmp(buf,d) longjmp(buf,d) # endif #elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 && \ defined(PJ_M_I386) && PJ_M_I386 != 0 /* * These are taken from uClibc. * Copyright (C) 2000-2003 Erik Andersen */ # if defined __USE_MISC || defined _ASM # define JB_BX 0 # define JB_SI 1 # define JB_DI 2 # define JB_BP 3 # define JB_SP 4 # define JB_PC 5 # define JB_SIZE 24 # endif # ifndef _ASM typedef int __jmp_buf[6]; /* A `sigset_t' has a bit for each signal. */ # define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int))) typedef struct __sigset_t_tag { unsigned long int __val[_SIGSET_NWORDS]; } __sigset_t; /* Calling environment, plus possibly a saved signal mask. */ typedef struct __jmp_buf_tag /* C++ doesn't like tagless structs. */ { /* NOTE: The machine-dependent definitions of `__sigsetjmp' assume that a `jmp_buf' begins with a `__jmp_buf' and that `__mask_was_saved' follows it. Do not move these members or add others before it. */ __jmp_buf __jmpbuf; /* Calling environment. */ int __mask_was_saved; /* Saved the signal mask? */ // we never saved the mask. __sigset_t __saved_mask; /* Saved signal mask. */ } jmp_buf[1]; typedef jmp_buf sigjmp_buf; typedef jmp_buf pj_jmp_buf; PJ_DECL(int) pj_setjmp(pj_jmp_buf env); PJ_DECL(void) pj_longjmp(pj_jmp_buf env, int val) __attribute__((noreturn)); # endif /* _ASM */ #elif defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0 /* Symbian framework don't use setjmp/longjmp */ #else # warning "setjmp()/longjmp() is not implemented" typedef int pj_jmp_buf[1]; # define pj_setjmp(buf) 0 # define pj_longjmp(buf,d) 0 #endif #endif /* __PJ_COMPAT_SETJMP_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/size_t.h ================================================ /* $Id: size_t.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_SIZE_T_H__ #define __PJ_COMPAT_SIZE_T_H__ /** * @file size_t.h * @brief Provides size_t type. */ #if PJ_HAS_STDDEF_H # include #endif #endif /* __PJ_COMPAT_SIZE_T_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/socket.h ================================================ /* $Id: socket.h 4435 2013-03-11 06:32:58Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_SOCKET_H__ #define __PJ_COMPAT_SOCKET_H__ /** * @file socket.h * @brief Provides all socket related functions,data types, error codes, etc. */ #if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 # include #endif #if defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 # include #endif #if defined(PJ_HAS_WS2TCPIP_H) && PJ_HAS_WS2TCPIP_H != 0 # include #endif /* * IPv6 for Visual Studio's * * = Visual Studio 6 = * * Visual Studio 6 does not ship with IPv6 support, so you MUST * download and install IPv6 Tehnology Preview (IPv6Kit) from: * http://msdn.microsoft.com/downloads/sdks/platform/tpipv6/ReadMe.asp * Then put IPv6Kit\inc in your Visual Studio include path. * * In addition, by default IPv6Kit does not want to install on * Windows 2000 SP4. Please see: * http://msdn.microsoft.com/downloads/sdks/platform/tpipv6/faq.asp * on how to install IPv6Kit on Win2K SP4. * * * = Visual Studio 2003, 2005 (including Express) = * * These VS uses Microsoft Platform SDK for Windows Server 2003 SP1, and * it has built-in IPv6 support. */ #if defined(_MSC_VER) && defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0 # ifndef s_addr # define s_addr S_un.S_addr # endif # if !defined(IPPROTO_IPV6) /* Need to download and install IPv6Kit for this platform. * Please see the comments above about Visual Studio 6. */ # include # endif # define PJ_SOCK_HAS_GETADDRINFO 1 #endif /* _MSC_VER */ #if defined(PJ_HAS_SYS_TYPES_H) && PJ_HAS_SYS_TYPES_H != 0 # include #endif #if defined(PJ_HAS_SYS_SOCKET_H) && PJ_HAS_SYS_SOCKET_H != 0 # include #endif #if defined(PJ_HAS_LINUX_SOCKET_H) && PJ_HAS_LINUX_SOCKET_H != 0 # include #endif #if defined(PJ_HAS_SYS_SELECT_H) && PJ_HAS_SYS_SELECT_H != 0 # include #endif #if defined(PJ_HAS_NETINET_IN_H) && PJ_HAS_NETINET_IN_H != 0 # include #endif #if defined(PJ_HAS_NETINET_IN_SYSTM_H) && PJ_HAS_NETINET_IN_SYSTM_H != 0 /* Required to include netinet/ip.h in FreeBSD 7.0 */ # include #endif #if defined(PJ_HAS_NETINET_IP_H) && PJ_HAS_NETINET_IP_H != 0 /* To pull in IPTOS_* constants */ # include #endif #if defined(PJ_HAS_NETINET_TCP_H) && PJ_HAS_NETINET_TCP_H != 0 /* To pull in TCP_NODELAY constants */ # include #endif #if defined(PJ_HAS_NET_IF_H) && PJ_HAS_NET_IF_H != 0 /* For interface enumeration in ip_helper */ # include #endif #if defined(PJ_HAS_IFADDRS_H) && PJ_HAS_IFADDRS_H != 0 /* Interface enum with getifaddrs() which works with IPv6 */ # include #endif #if defined(PJ_HAS_ARPA_INET_H) && PJ_HAS_ARPA_INET_H != 0 # include #endif #if defined(PJ_HAS_SYS_IOCTL_H) && PJ_HAS_SYS_IOCTL_H != 0 # include /* FBIONBIO */ #endif #if defined(PJ_HAS_ERRNO_H) && PJ_HAS_ERRNO_H != 0 # include #endif #if defined(PJ_HAS_NETDB_H) && PJ_HAS_NETDB_H != 0 # include #endif #if defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0 # include #endif #if defined(PJ_HAS_SYS_FILIO_H) && PJ_HAS_SYS_FILIO_H != 0 # include #endif #if defined(PJ_HAS_SYS_SOCKIO_H) && PJ_HAS_SYS_SOCKIO_H != 0 # include #endif /* * Define common errors. */ #if (defined(PJ_WIN32) && PJ_WIN32!=0) || \ (defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0) || \ (defined(PJ_WIN64) && PJ_WIN64!=0) # define OSERR_EWOULDBLOCK WSAEWOULDBLOCK # define OSERR_EINPROGRESS WSAEINPROGRESS # define OSERR_ECONNRESET WSAECONNRESET # define OSERR_ENOTCONN WSAENOTCONN #elif defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0 # define OSERR_EWOULDBLOCK -1 # define OSERR_EINPROGRESS -1 # define OSERR_ECONNRESET -1 # define OSERR_ENOTCONN -1 #else # define OSERR_EWOULDBLOCK EWOULDBLOCK # define OSERR_EINPROGRESS EINPROGRESS # define OSERR_ECONNRESET ECONNRESET # define OSERR_ENOTCONN ENOTCONN #endif /* * And undefine these.. */ #undef s_addr #undef s6_addr #undef sin_zero /* * Linux kernel specifics */ #if defined(PJ_LINUX_KERNEL) # include # include /* FIONBIO */ # include /* sys_select() */ # include /* set/get_fs() */ typedef int socklen_t; # define getsockopt sys_getsockopt /* * Wrapper for select() in Linux kernel. */ PJ_INLINE(int) select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) { int count; mm_segment_t oldfs = get_fs(); set_fs(KERNEL_DS); count = sys_select(n, inp, outp, exp, tvp); set_fs(oldfs); return count; } #endif /* PJ_LINUX_KERNEL */ /* * This will finally be obsoleted, since it should be declared in * os_auto.h */ #if !defined(PJ_HAS_SOCKLEN_T) || PJ_HAS_SOCKLEN_T==0 typedef int socklen_t; #endif /* Regarding sin_len member of sockaddr_in: * BSD systems (including MacOS X requires that the sin_len member of * sockaddr_in be set to sizeof(sockaddr_in), while other systems (Windows * and Linux included) do not. * * To maintain compatibility between systems, PJLIB will automatically * set this field before invoking native OS socket API, and it will * always reset the field to zero before returning pj_sockaddr_in to * application (such as in pj_getsockname() and pj_recvfrom()). * * Application MUST always set this field to zero. * * This way we can avoid hard to find problem such as when the socket * address is used as hash table key. */ #if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0 # define PJ_SOCKADDR_SET_LEN(addr,len) (((pj_addr_hdr*)(addr))->sa_zero_len=(len)) # define PJ_SOCKADDR_RESET_LEN(addr) (((pj_addr_hdr*)(addr))->sa_zero_len=0) #else # define PJ_SOCKADDR_SET_LEN(addr,len) # define PJ_SOCKADDR_RESET_LEN(addr) #endif #endif /* __PJ_COMPAT_SOCKET_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/stdarg.h ================================================ /* $Id: stdarg.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_STDARG_H__ #define __PJ_COMPAT_STDARG_H__ /** * @file stdarg.h * @brief Provides stdarg functionality. */ #if defined(PJ_HAS_STDARG_H) && PJ_HAS_STDARG_H != 0 # include #endif #endif /* __PJ_COMPAT_STDARG_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/stdfileio.h ================================================ /* $Id: stdfileio.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_STDFILEIO_H__ #define __PJ_COMPAT_STDFILEIO_H__ /** * @file stdfileio.h * @brief Compatibility for ANSI file I/O like fputs, fflush, etc. */ #if defined(PJ_HAS_STDIO_H) && PJ_HAS_STDIO_H != 0 # include #endif #endif /* __PJ_COMPAT_STDFILEIO_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/string.h ================================================ /* $Id: string.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_STRING_H__ #define __PJ_COMPAT_STRING_H__ /** * @file string.h * @brief Provides string manipulation functions found in ANSI string.h. */ #if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H != 0 # include #else PJ_DECL(int) strcasecmp(const char *s1, const char *s2); PJ_DECL(int) strncasecmp(const char *s1, const char *s2, int len); #endif /* For sprintf family */ #include /* On WinCE, string stuffs are declared in stdlib.h */ #if defined(PJ_HAS_STDLIB_H) && PJ_HAS_STDLIB_H!=0 # include #endif #if defined(_MSC_VER) # define strcasecmp _stricmp # define strncasecmp _strnicmp # define snprintf _snprintf # define vsnprintf _vsnprintf # define snwprintf _snwprintf # define wcsicmp _wcsicmp # define wcsnicmp _wcsnicmp #else # define stricmp strcasecmp # define strnicmp strncasecmp # if defined(PJ_NATIVE_STRING_IS_UNICODE) && PJ_NATIVE_STRING_IS_UNICODE!=0 # error "Implement Unicode string functions" # endif #endif #define pj_ansi_strcmp strcmp #define pj_ansi_strncmp strncmp #define pj_ansi_strlen strlen #define pj_ansi_strcpy strcpy #define pj_ansi_strncpy strncpy #define pj_ansi_strcat strcat #define pj_ansi_strstr strstr #define pj_ansi_strchr strchr #define pj_ansi_strcasecmp strcasecmp #define pj_ansi_stricmp strcasecmp #define pj_ansi_strncasecmp strncasecmp #define pj_ansi_strnicmp strncasecmp #define pj_ansi_sprintf sprintf #if defined(PJ_HAS_NO_SNPRINTF) && PJ_HAS_NO_SNPRINTF != 0 # include # include PJ_BEGIN_DECL PJ_DECL(int) snprintf(char*s1, pj_size_t len, const char*s2, ...); PJ_DECL(int) vsnprintf(char*s1, pj_size_t len, const char*s2, va_list arg); PJ_END_DECL #endif #define pj_ansi_snprintf snprintf #define pj_ansi_vsprintf vsprintf #define pj_ansi_vsnprintf vsnprintf #define pj_unicode_strcmp wcscmp #define pj_unicode_strncmp wcsncmp #define pj_unicode_strlen wcslen #define pj_unicode_strcpy wcscpy #define pj_unicode_strncpy wcsncpy #define pj_unicode_strcat wcscat #define pj_unicode_strstr wcsstr #define pj_unicode_strchr wcschr #define pj_unicode_strcasecmp wcsicmp #define pj_unicode_stricmp wcsicmp #define pj_unicode_strncasecmp wcsnicmp #define pj_unicode_strnicmp wcsnicmp #define pj_unicode_sprintf swprintf #define pj_unicode_snprintf snwprintf #define pj_unicode_vsprintf vswprintf #define pj_unicode_vsnprintf vsnwprintf #if defined(PJ_NATIVE_STRING_IS_UNICODE) && PJ_NATIVE_STRING_IS_UNICODE!=0 # define pj_native_strcmp pj_unicode_strcmp # define pj_native_strncmp pj_unicode_strncmp # define pj_native_strlen pj_unicode_strlen # define pj_native_strcpy pj_unicode_strcpy # define pj_native_strncpy pj_unicode_strncpy # define pj_native_strcat pj_unicode_strcat # define pj_native_strstr pj_unicode_strstr # define pj_native_strchr pj_unicode_strchr # define pj_native_strcasecmp pj_unicode_strcasecmp # define pj_native_stricmp pj_unicode_stricmp # define pj_native_strncasecmp pj_unicode_strncasecmp # define pj_native_strnicmp pj_unicode_strnicmp # define pj_native_sprintf pj_unicode_sprintf # define pj_native_snprintf pj_unicode_snprintf # define pj_native_vsprintf pj_unicode_vsprintf # define pj_native_vsnprintf pj_unicode_vsnprintf #else # define pj_native_strcmp pj_ansi_strcmp # define pj_native_strncmp pj_ansi_strncmp # define pj_native_strlen pj_ansi_strlen # define pj_native_strcpy pj_ansi_strcpy # define pj_native_strncpy pj_ansi_strncpy # define pj_native_strcat pj_ansi_strcat # define pj_native_strstr pj_ansi_strstr # define pj_native_strchr pj_ansi_strchr # define pj_native_strcasecmp pj_ansi_strcasecmp # define pj_native_stricmp pj_ansi_stricmp # define pj_native_strncasecmp pj_ansi_strncasecmp # define pj_native_strnicmp pj_ansi_strnicmp # define pj_native_sprintf pj_ansi_sprintf # define pj_native_snprintf pj_ansi_snprintf # define pj_native_vsprintf pj_ansi_vsprintf # define pj_native_vsnprintf pj_ansi_vsnprintf #endif #endif /* __PJ_COMPAT_STRING_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/compat/time.h ================================================ /* $Id: time.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_COMPAT_TIME_H__ #define __PJ_COMPAT_TIME_H__ /** * @file time.h * @brief Provides ftime() and localtime() etc functions. */ #if defined(PJ_HAS_TIME_H) && PJ_HAS_TIME_H != 0 # include #endif #if defined(PJ_HAS_SYS_TIME_H) && PJ_HAS_SYS_TIME_H != 0 # include #endif #if defined(PJ_HAS_SYS_TIMEB_H) && PJ_HAS_SYS_TIMEB_H != 0 # include #endif #endif /* __PJ_COMPAT_TIME_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/config.h ================================================ /* $Id: config.h 4419 2013-03-05 11:23:52Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_CONFIG_H__ #define __PJ_CONFIG_H__ /** * @file config.h * @brief PJLIB Main configuration settings. */ /******************************************************************** * Include compiler specific configuration. */ #if defined(_MSC_VER) # include #elif defined(__GNUC__) # include #elif defined(__CW32__) # include #elif defined(__MWERKS__) # include #elif defined(__GCCE__) # include #elif defined(__ARMCC__) # include #else # error "Unknown compiler." #endif /* PJ_ALIGN_DATA is compiler specific directive to align data address */ #ifndef PJ_ALIGN_DATA # error "PJ_ALIGN_DATA is not defined!" #endif /******************************************************************** * Include target OS specific configuration. */ #if defined(PJ_AUTOCONF) /* * Autoconf */ # include #elif defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0 /* * SymbianOS */ # include #elif defined(PJ_WIN32_WINCE) || defined(_WIN32_WCE) || defined(UNDER_CE) /* * Windows CE */ # undef PJ_WIN32_WINCE # define PJ_WIN32_WINCE 1 # include /* Also define Win32 */ # define PJ_WIN32 1 #elif defined(PJ_WIN32) || defined(_WIN32) || defined(__WIN32__) || \ defined(WIN32) || defined(PJ_WIN64) || defined(_WIN64) || \ defined(WIN64) || defined(__TOS_WIN__) # if defined(PJ_WIN64) || defined(_WIN64) || defined(WIN64) /* * Win64 */ # undef PJ_WIN64 # define PJ_WIN64 1 # endif # undef PJ_WIN32 # define PJ_WIN32 1 # include #elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL!=0 /* * Linux kernel */ # include #elif defined(PJ_LINUX) || defined(linux) || defined(__linux) /* * Linux */ # undef PJ_LINUX # define PJ_LINUX 1 # include #elif defined(PJ_PALMOS) && PJ_PALMOS!=0 /* * Palm */ # include #elif defined(PJ_SUNOS) || defined(sun) || defined(__sun) /* * SunOS */ # undef PJ_SUNOS # define PJ_SUNOS 1 # include #elif defined(PJ_DARWINOS) || defined(__MACOSX__) || \ defined (__APPLE__) || defined (__MACH__) /* * MacOS X */ # undef PJ_DARWINOS # define PJ_DARWINOS 1 # include #elif defined(PJ_RTEMS) && PJ_RTEMS!=0 /* * RTEMS */ # include #else # error "Please specify target os." #endif /******************************************************************** * Target machine specific configuration. */ #if defined(PJ_AUTOCONF) /* * Autoconf configured */ #include #elif defined (PJ_M_I386) || defined(_i386_) || defined(i_386_) || \ defined(_X86_) || defined(x86) || defined(__i386__) || \ defined(__i386) || defined(_M_IX86) || defined(__I86__) /* * Generic i386 processor family, little-endian */ # undef PJ_M_I386 # define PJ_M_I386 1 # define PJ_M_NAME "i386" # define PJ_HAS_PENTIUM 1 # define PJ_IS_LITTLE_ENDIAN 1 # define PJ_IS_BIG_ENDIAN 0 #elif defined (PJ_M_X86_64) || defined(__amd64__) || defined(__amd64) || \ defined(__x86_64__) || defined(__x86_64) || \ defined(_M_X64) || defined(_M_AMD64) /* * AMD 64bit processor, little endian */ # undef PJ_M_X86_64 # define PJ_M_X86_64 1 # define PJ_M_NAME "x86_64" # define PJ_HAS_PENTIUM 1 # define PJ_IS_LITTLE_ENDIAN 1 # define PJ_IS_BIG_ENDIAN 0 #elif defined(PJ_M_IA64) || defined(__ia64__) || defined(_IA64) || \ defined(__IA64__) || defined( _M_IA64) /* * Intel IA64 processor, default to little endian */ # undef PJ_M_IA64 # define PJ_M_IA64 1 # define PJ_M_NAME "ia64" # define PJ_HAS_PENTIUM 1 # define PJ_IS_LITTLE_ENDIAN 1 # define PJ_IS_BIG_ENDIAN 0 #elif defined (PJ_M_M68K) && PJ_M_M68K != 0 /* * Motorola m68k processor, big endian */ # undef PJ_M_M68K # define PJ_M_M68K 1 # define PJ_M_NAME "m68k" # define PJ_HAS_PENTIUM 0 # define PJ_IS_LITTLE_ENDIAN 0 # define PJ_IS_BIG_ENDIAN 1 #elif defined (PJ_M_ALPHA) || defined (__alpha__) || defined (__alpha) || \ defined (_M_ALPHA) /* * DEC Alpha processor, little endian */ # undef PJ_M_ALPHA # define PJ_M_ALPHA 1 # define PJ_M_NAME "alpha" # define PJ_HAS_PENTIUM 0 # define PJ_IS_LITTLE_ENDIAN 1 # define PJ_IS_BIG_ENDIAN 0 #elif defined(PJ_M_MIPS) || defined(__mips__) || defined(__mips) || \ defined(__MIPS__) || defined(MIPS) || defined(_MIPS_) /* * MIPS, bi-endian, so raise error if endianness is not configured */ # undef PJ_M_MIPS # define PJ_M_MIPS 1 # define PJ_M_NAME "mips" # define PJ_HAS_PENTIUM 0 # if !PJ_IS_LITTLE_ENDIAN && !PJ_IS_BIG_ENDIAN # error Endianness must be declared for this processor # endif #elif defined (PJ_M_SPARC) || defined( __sparc__) || defined(__sparc) /* * Sun Sparc, big endian */ # undef PJ_M_SPARC # define PJ_M_SPARC 1 # define PJ_M_NAME "sparc" # define PJ_HAS_PENTIUM 0 # define PJ_IS_LITTLE_ENDIAN 0 # define PJ_IS_BIG_ENDIAN 1 #elif defined (PJ_M_ARMV4) || defined(ARM) || defined(_ARM_) || \ defined(ARMV4) || defined(__arm__) /* * ARM, bi-endian, so raise error if endianness is not configured */ # undef PJ_M_ARMV4 # define PJ_M_ARMV4 1 # define PJ_M_NAME "armv4" # define PJ_HAS_PENTIUM 0 # if !PJ_IS_LITTLE_ENDIAN && !PJ_IS_BIG_ENDIAN # error Endianness must be declared for this processor # endif #elif defined (PJ_M_POWERPC) || defined(__powerpc) || defined(__powerpc__) || \ defined(__POWERPC__) || defined(__ppc__) || defined(_M_PPC) || \ defined(_ARCH_PPC) /* * PowerPC, bi-endian, so raise error if endianness is not configured */ # undef PJ_M_POWERPC # define PJ_M_POWERPC 1 # define PJ_M_NAME "powerpc" # define PJ_HAS_PENTIUM 0 # if !PJ_IS_LITTLE_ENDIAN && !PJ_IS_BIG_ENDIAN # error Endianness must be declared for this processor # endif #elif defined (PJ_M_NIOS2) || defined(__nios2) || defined(__nios2__) || \ defined(__NIOS2__) || defined(__M_NIOS2) || defined(_ARCH_NIOS2) /* * Nios2, little endian */ # undef PJ_M_NIOS2 # define PJ_M_NIOS2 1 # define PJ_M_NAME "nios2" # define PJ_HAS_PENTIUM 0 # define PJ_IS_LITTLE_ENDIAN 1 # define PJ_IS_BIG_ENDIAN 0 #else # error "Please specify target machine." #endif /* Include size_t definition. */ #include /* Include site/user specific configuration to control PJLIB features. * YOU MUST CREATE THIS FILE YOURSELF!! */ #include /******************************************************************** * PJLIB Features. */ /* Overrides for DOXYGEN */ #ifdef DOXYGEN # undef PJ_FUNCTIONS_ARE_INLINED # undef PJ_HAS_FLOATING_POINT # undef PJ_LOG_MAX_LEVEL # undef PJ_LOG_MAX_SIZE # undef PJ_LOG_USE_STACK_BUFFER # undef PJ_TERM_HAS_COLOR # undef PJ_POOL_DEBUG # undef PJ_HAS_TCP # undef PJ_MAX_HOSTNAME # undef PJ_IOQUEUE_MAX_HANDLES # undef FD_SETSIZE # undef PJ_HAS_SEMAPHORE # undef PJ_HAS_EVENT_OBJ # undef PJ_ENABLE_EXTRA_CHECK # undef PJ_EXCEPTION_USE_WIN32_SEH # undef PJ_HAS_ERROR_STRING # define PJ_HAS_IPV6 1 #endif /** * @defgroup pj_config Build Configuration * @{ * * This section contains macros that can set during PJLIB build process * to controll various aspects of the library. * * Note: the values in this page does NOT necessarily reflect to the * macro values during the build process. */ /** * If this macro is set to 1, it will enable some debugging checking * in the library. * * Default: equal to (NOT NDEBUG). */ #ifndef PJ_DEBUG # ifndef NDEBUG # define PJ_DEBUG 1 # else # define PJ_DEBUG 0 # endif #endif /** * Enable this macro to activate logging to mutex/semaphore related events. * This is useful to troubleshoot concurrency problems such as deadlocks. * In addition, you should also add PJ_LOG_HAS_THREAD_ID flag to the * log decoration to assist the troubleshooting. * * Default: 0 */ #ifndef PJ_DEBUG_MUTEX # define PJ_DEBUG_MUTEX 0 #endif /** * Expand functions in *_i.h header files as inline. * * Default: 0. */ #ifndef PJ_FUNCTIONS_ARE_INLINED # define PJ_FUNCTIONS_ARE_INLINED 0 #endif /** * Use floating point computations in the library. * * Default: 1. */ #ifndef PJ_HAS_FLOATING_POINT # define PJ_HAS_FLOATING_POINT 1 #endif /** * Declare maximum logging level/verbosity. Lower number indicates higher * importance, with the highest importance has level zero. The least * important level is five in this implementation, but this can be extended * by supplying the appropriate implementation. * * The level conventions: * - 0: fatal error * - 1: error * - 2: warning * - 3: info * - 4: debug * - 5: trace * - 6: more detailed trace * * Default: 4 */ #ifndef PJ_LOG_MAX_LEVEL # define PJ_LOG_MAX_LEVEL 5 #endif /** * Maximum message size that can be sent to output device for each call * to PJ_LOG(). If the message size is longer than this value, it will be cut. * This may affect the stack usage, depending whether PJ_LOG_USE_STACK_BUFFER * flag is set. * * Default: 4000 */ #ifndef PJ_LOG_MAX_SIZE # define PJ_LOG_MAX_SIZE 4000 #endif /** * Log buffer. * Does the log get the buffer from the stack? (default is yes). * If the value is set to NO, then the buffer will be taken from static * buffer, which in this case will make the log function non-reentrant. * * Default: 1 */ #ifndef PJ_LOG_USE_STACK_BUFFER # define PJ_LOG_USE_STACK_BUFFER 1 #endif /** * Enable log indentation feature. * * Default: 1 */ #ifndef PJ_LOG_ENABLE_INDENT # define PJ_LOG_ENABLE_INDENT 1 #endif /** * Number of PJ_LOG_INDENT_CHAR to put every time pj_log_push_indent() * is called. * * Default: 1 */ #ifndef PJ_LOG_INDENT_SIZE # define PJ_LOG_INDENT_SIZE 1 #endif /** * Log indentation character. * * Default: space */ #ifndef PJ_LOG_INDENT_CHAR # define PJ_LOG_INDENT_CHAR '.' #endif /** * Colorfull terminal (for logging etc). * * Default: 1 */ #ifndef PJ_TERM_HAS_COLOR # define PJ_TERM_HAS_COLOR 1 #endif /** * Set this flag to non-zero to enable various checking for pool * operations. When this flag is set, assertion must be enabled * in the application. * * This will slow down pool creation and destruction and will add * few bytes of overhead, so application would normally want to * disable this feature on release build. * * Default: 0 */ #ifndef PJ_SAFE_POOL # define PJ_SAFE_POOL 0 #endif /** * If pool debugging is used, then each memory allocation from the pool * will call malloc(), and pool will release all memory chunks when it * is destroyed. This works better when memory verification programs * such as Rational Purify is used. * * Default: 0 */ #ifndef PJ_POOL_DEBUG # define PJ_POOL_DEBUG 0 #endif /** * Enable timer heap debugging facility. When this is enabled, application * can call pj_timer_heap_dump() to show the contents of the timer heap * along with the source location where the timer entries were scheduled. * See https://trac.pjsip.org/repos/ticket/1527 for more info. * * Default: 0 */ #ifndef PJ_TIMER_DEBUG # define PJ_TIMER_DEBUG 0 #endif /** * Set this to 1 to enable debugging on the group lock. Default: 0 */ #ifndef PJ_GRP_LOCK_DEBUG # define PJ_GRP_LOCK_DEBUG 0 #endif /** * Specify this as \a stack_size argument in #pj_thread_create() to specify * that thread should use default stack size for the current platform. * * Default: 8192 */ #ifndef PJ_THREAD_DEFAULT_STACK_SIZE # define PJ_THREAD_DEFAULT_STACK_SIZE 8192 #endif /** * Specify if PJ_CHECK_STACK() macro is enabled to check the sanity of * the stack. The OS implementation may check that no stack overflow * occurs, and it also may collect statistic about stack usage. Note * that this will increase the footprint of the libraries since it * tracks the filename and line number of each functions. */ #ifndef PJ_OS_HAS_CHECK_STACK # define PJ_OS_HAS_CHECK_STACK 0 #endif /** * Do we have alternate pool implementation? * * Default: 0 */ #ifndef PJ_HAS_POOL_ALT_API # define PJ_HAS_POOL_ALT_API PJ_POOL_DEBUG #endif /** * Support TCP in the library. * Disabling TCP will reduce the footprint slightly (about 6KB). * * Default: 1 */ #ifndef PJ_HAS_TCP # define PJ_HAS_TCP 1 #endif /** * Support IPv6 in the library. If this support is disabled, some IPv6 * related functions will return PJ_EIPV6NOTSUP. * * Default: 0 (disabled, for now) */ #ifndef PJ_HAS_IPV6 # define PJ_HAS_IPV6 0 #endif /** * Maximum hostname length. * Libraries sometimes needs to make copy of an address to stack buffer; * the value here affects the stack usage. * * Default: 128 */ #ifndef PJ_MAX_HOSTNAME # define PJ_MAX_HOSTNAME (128) #endif /** * Maximum consecutive identical error for accept() operation before * activesock stops calling the next ioqueue accept. * * Default: 50 */ #ifndef PJ_ACTIVESOCK_MAX_CONSECUTIVE_ACCEPT_ERROR # define PJ_ACTIVESOCK_MAX_CONSECUTIVE_ACCEPT_ERROR 50 #endif /** * Constants for declaring the maximum handles that can be supported by * a single IOQ framework. This constant might not be relevant to the * underlying I/O queue impelementation, but still, developers should be * aware of this constant, to make sure that the program will not break when * the underlying implementation changes. */ #ifndef PJ_IOQUEUE_MAX_HANDLES # define PJ_IOQUEUE_MAX_HANDLES (64) #endif /** * If PJ_IOQUEUE_HAS_SAFE_UNREG macro is defined, then ioqueue will do more * things to ensure thread safety of handle unregistration operation by * employing reference counter to each handle. * * In addition, the ioqueue will preallocate memory for the handles, * according to the maximum number of handles that is specified during * ioqueue creation. * * All applications would normally want this enabled, but you may disable * this if: * - there is no dynamic unregistration to all ioqueues. * - there is no threading, or there is no preemptive multitasking. * * Default: 1 */ #ifndef PJ_IOQUEUE_HAS_SAFE_UNREG # define PJ_IOQUEUE_HAS_SAFE_UNREG 1 #endif /** * Default concurrency setting for sockets/handles registered to ioqueue. * This controls whether the ioqueue is allowed to call the key's callback * concurrently/in parallel. The default is yes, which means that if there * are more than one pending operations complete simultaneously, more * than one threads may call the key's callback at the same time. This * generally would promote good scalability for application, at the * expense of more complexity to manage the concurrent accesses. * * Please see the ioqueue documentation for more info. */ #ifndef PJ_IOQUEUE_DEFAULT_ALLOW_CONCURRENCY # define PJ_IOQUEUE_DEFAULT_ALLOW_CONCURRENCY 1 #endif /* Sanity check: * if ioqueue concurrency is disallowed, PJ_IOQUEUE_HAS_SAFE_UNREG * must be enabled. */ #if (PJ_IOQUEUE_DEFAULT_ALLOW_CONCURRENCY==0) && (PJ_IOQUEUE_HAS_SAFE_UNREG==0) # error PJ_IOQUEUE_HAS_SAFE_UNREG must be enabled if ioqueue concurrency \ is disabled #endif /** * When safe unregistration (PJ_IOQUEUE_HAS_SAFE_UNREG) is configured in * ioqueue, the PJ_IOQUEUE_KEY_FREE_DELAY macro specifies how long the * ioqueue key is kept in closing state before it can be reused. * * The value is in miliseconds. * * Default: 500 msec. */ #ifndef PJ_IOQUEUE_KEY_FREE_DELAY # define PJ_IOQUEUE_KEY_FREE_DELAY 500 #endif /** * Determine if FD_SETSIZE is changeable/set-able. If so, then we will * set it to PJ_IOQUEUE_MAX_HANDLES. Currently we detect this by checking * for Winsock. */ #ifndef PJ_FD_SETSIZE_SETABLE # if (defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H!=0) || \ (defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H!=0) # define PJ_FD_SETSIZE_SETABLE 1 # else # define PJ_FD_SETSIZE_SETABLE 0 # endif #endif /** * Overrides FD_SETSIZE so it is consistent throughout the library. * We only do this if we detected that FD_SETSIZE is changeable. If * FD_SETSIZE is not set-able, then PJ_IOQUEUE_MAX_HANDLES must be * set to value lower than FD_SETSIZE. */ #if PJ_FD_SETSIZE_SETABLE /* Only override FD_SETSIZE if the value has not been set */ # ifndef FD_SETSIZE # define FD_SETSIZE PJ_IOQUEUE_MAX_HANDLES # endif #else /* When FD_SETSIZE is not changeable, check if PJ_IOQUEUE_MAX_HANDLES * is lower than FD_SETSIZE value. * * Update: Not all ioqueue backends require this (such as epoll), so * this check will be done on the ioqueue implementation itself, such as * ioqueue select. */ /* # ifdef FD_SETSIZE # if PJ_IOQUEUE_MAX_HANDLES > FD_SETSIZE # error "PJ_IOQUEUE_MAX_HANDLES is greater than FD_SETSIZE" # endif # endif */ #endif /** * Specify whether #pj_enum_ip_interface() function should exclude * loopback interfaces. * * Default: 1 */ #ifndef PJ_IP_HELPER_IGNORE_LOOPBACK_IF # define PJ_IP_HELPER_IGNORE_LOOPBACK_IF 1 #endif /** * Has semaphore functionality? * * Default: 1 */ #ifndef PJ_HAS_SEMAPHORE # define PJ_HAS_SEMAPHORE 1 #endif /** * Event object (for synchronization, e.g. in Win32) * * Default: 1 */ #ifndef PJ_HAS_EVENT_OBJ # define PJ_HAS_EVENT_OBJ 1 #endif /** * Maximum file name length. */ #ifndef PJ_MAXPATH # define PJ_MAXPATH 260 #endif /** * Enable library's extra check. * If this macro is enabled, #PJ_ASSERT_RETURN macro will expand to * run-time checking. If this macro is disabled, #PJ_ASSERT_RETURN * will simply evaluate to #pj_assert(). * * You can disable this macro to reduce size, at the risk of crashes * if invalid value (e.g. NULL) is passed to the library. * * Default: 1 */ #ifndef PJ_ENABLE_EXTRA_CHECK # define PJ_ENABLE_EXTRA_CHECK 1 #endif /** * Enable name registration for exceptions with #pj_exception_id_alloc(). * If this feature is enabled, then the library will keep track of * names associated with each exception ID requested by application via * #pj_exception_id_alloc(). * * Disabling this macro will reduce the code and .bss size by a tad bit. * See also #PJ_MAX_EXCEPTION_ID. * * Default: 1 */ #ifndef PJ_HAS_EXCEPTION_NAMES # define PJ_HAS_EXCEPTION_NAMES 1 #endif /** * Maximum number of unique exception IDs that can be requested * with #pj_exception_id_alloc(). For each entry, a small record will * be allocated in the .bss segment. * * Default: 16 */ #ifndef PJ_MAX_EXCEPTION_ID # define PJ_MAX_EXCEPTION_ID 16 #endif /** * Should we use Windows Structured Exception Handling (SEH) for the * PJLIB exceptions. * * Default: 0 */ #ifndef PJ_EXCEPTION_USE_WIN32_SEH # define PJ_EXCEPTION_USE_WIN32_SEH 0 #endif /** * Should we attempt to use Pentium's rdtsc for high resolution * timestamp. * * Default: 0 */ #ifndef PJ_TIMESTAMP_USE_RDTSC # define PJ_TIMESTAMP_USE_RDTSC 0 #endif /** * Is native platform error positive number? * Default: 1 (yes) */ #ifndef PJ_NATIVE_ERR_POSITIVE # define PJ_NATIVE_ERR_POSITIVE 1 #endif /** * Include error message string in the library (pj_strerror()). * This is very much desirable! * * Default: 1 */ #ifndef PJ_HAS_ERROR_STRING # define PJ_HAS_ERROR_STRING 1 #endif /** * Include pj_stricmp_alnum() and pj_strnicmp_alnum(), i.e. custom * functions to compare alnum strings. On some systems, they're faster * then stricmp/strcasecmp, but they can be slower on other systems. * When disabled, pjlib will fallback to stricmp/strnicmp. * * Default: 0 */ #ifndef PJ_HAS_STRICMP_ALNUM # define PJ_HAS_STRICMP_ALNUM 0 #endif /* * Types of QoS backend implementation. */ /** * Dummy QoS backend implementation, will always return error on all * the APIs. */ #define PJ_QOS_DUMMY 1 /** QoS backend based on setsockopt(IP_TOS) */ #define PJ_QOS_BSD 2 /** QoS backend for Windows Mobile 6 */ #define PJ_QOS_WM 3 /** QoS backend for Symbian */ #define PJ_QOS_SYMBIAN 4 /** * Force the use of some QoS backend API for some platforms. */ #ifndef PJ_QOS_IMPLEMENTATION # if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE && _WIN32_WCE >= 0x502 /* Windows Mobile 6 or later */ # define PJ_QOS_IMPLEMENTATION PJ_QOS_WM # endif #endif /** * Enable secure socket. For most platforms, this is implemented using * OpenSSL, so this will require OpenSSL to be installed. For Symbian * platform, this is implemented natively using CSecureSocket. * * Default: 0 (for now) */ #ifndef PJ_HAS_SSL_SOCK # define PJ_HAS_SSL_SOCK 0 #endif /** * Define the maximum number of ciphers supported by the secure socket. * * Default: 256 */ #ifndef PJ_SSL_SOCK_MAX_CIPHERS # define PJ_SSL_SOCK_MAX_CIPHERS 256 #endif /** * Specify what should be set as the available list of SSL_CIPHERs. For * example, set this as "DEFAULT" to use the default cipher list (Note: * PJSIP release 2.4 and before used this "DEFAULT" setting). * * Default: "HIGH:-COMPLEMENTOFDEFAULT" */ #ifndef PJ_SSL_SOCK_OSSL_CIPHERS # define PJ_SSL_SOCK_OSSL_CIPHERS "HIGH:-COMPLEMENTOFDEFAULT" #endif /** * Disable WSAECONNRESET error for UDP sockets on Win32 platforms. See * https://trac.pjsip.org/repos/ticket/1197. * * Default: 1 */ #ifndef PJ_SOCK_DISABLE_WSAECONNRESET # define PJ_SOCK_DISABLE_WSAECONNRESET 1 #endif /** @} */ /******************************************************************** * General macros. */ /** * @defgroup pj_dll_target Building Dynamic Link Libraries (DLL/DSO) * @ingroup pj_config * @{ * * The libraries support generation of dynamic link libraries for * Symbian ABIv2 target (.dso/Dynamic Shared Object files, in Symbian * terms). Similar procedures may be applied for Win32 DLL with some * modification. * * Depending on the platforms, these steps may be necessary in order to * produce the dynamic libraries: * - Create the (Visual Studio) projects to produce DLL output. PJLIB * does not provide ready to use project files to produce DLL, so * you need to create these projects yourself. For Symbian, the MMP * files have been setup to produce DSO files for targets that * require them. * - In the (Visual Studio) projects, some macros need to be declared * so that appropriate modifiers are added to symbol declarations * and definitions. Please see the macro section below for information * regarding these macros. For Symbian, these have been taken care by the * MMP files. * - Some build systems require .DEF file to be specified when creating * the DLL. For Symbian, .DEF files are included in pjlib distribution, * in pjlib/build.symbian directory. These DEF files are * created by running ./makedef.sh all from this directory, * inside Mingw. * * Macros related for building DLL/DSO files: * - For platforms that supports dynamic link libraries generation, * it must declare PJ_EXPORT_SPECIFIER macro which value contains * the prefix to be added to symbol definition, to export this * symbol in the DLL/DSO. For example, on Win32/Visual Studio, the * value of this macro is \a __declspec(dllexport), and for ARM * ABIv2/Symbian, the value is \a EXPORT_C. * - For platforms that supports linking with dynamic link libraries, * it must declare PJ_IMPORT_SPECIFIER macro which value contains * the prefix to be added to symbol declaration, to import this * symbol from a DLL/DSO. For example, on Win32/Visual Studio, the * value of this macro is \a __declspec(dllimport), and for ARM * ABIv2/Symbian, the value is \a IMPORT_C. * - Both PJ_EXPORT_SPECIFIER and PJ_IMPORT_SPECIFIER * macros above can be declared in your \a config_site.h if they are not * declared by pjlib. * - When PJLIB is built as DLL/DSO, both PJ_DLL and * PJ_EXPORTING macros must be declared, so that * PJ_EXPORT_SPECIFIER modifier will be added into function * definition. * - When application wants to link dynamically with PJLIB, then it * must declare PJ_DLL macro when using/including PJLIB header, * so that PJ_IMPORT_SPECIFIER modifier is properly added into * symbol declarations. * * When PJ_DLL macro is not declared, static linking is assumed. * * For example, here are some settings to produce DLLs with Visual Studio * on Windows/Win32: * - Create Visual Studio projects to produce DLL. Add the appropriate * project dependencies to avoid link errors. * - In the projects, declare PJ_DLL and PJ_EXPORTING * macros. * - Declare these macros in your config_site.h: \verbatim #define PJ_EXPORT_SPECIFIER __declspec(dllexport) #define PJ_IMPORT_SPECIFIER __declspec(dllimport) \endverbatim * - And in the application (that links with the DLL) project, add * PJ_DLL in the macro declarations. */ /** @} */ /** * @defgroup pj_config Build Configuration * @{ */ /** * @def PJ_INLINE(type) * @param type The return type of the function. * Expand the function as inline. */ #define PJ_INLINE(type) PJ_INLINE_SPECIFIER type /** * This macro declares platform/compiler specific specifier prefix * to be added to symbol declaration to export the symbol when PJLIB * is built as dynamic library. * * This macro should have been added by platform specific headers, * if the platform supports building dynamic library target. */ #ifndef PJ_EXPORT_DECL_SPECIFIER # define PJ_EXPORT_DECL_SPECIFIER #endif /** * This macro declares platform/compiler specific specifier prefix * to be added to symbol definition to export the symbol when PJLIB * is built as dynamic library. * * This macro should have been added by platform specific headers, * if the platform supports building dynamic library target. */ #ifndef PJ_EXPORT_DEF_SPECIFIER # define PJ_EXPORT_DEF_SPECIFIER #endif /** * This macro declares platform/compiler specific specifier prefix * to be added to symbol declaration to import the symbol. * * This macro should have been added by platform specific headers, * if the platform supports building dynamic library target. */ #ifndef PJ_IMPORT_DECL_SPECIFIER # define PJ_IMPORT_DECL_SPECIFIER #endif /** * This macro has been deprecated. It will evaluate to nothing. */ #ifndef PJ_EXPORT_SYMBOL # define PJ_EXPORT_SYMBOL(x) #endif /** * @def PJ_DECL(type) * @param type The return type of the function. * Declare a function. */ #if defined(PJ_DLL) # if defined(PJ_EXPORTING) # define PJ_DECL(type) PJ_EXPORT_DECL_SPECIFIER type # else # define PJ_DECL(type) PJ_IMPORT_DECL_SPECIFIER type # endif #elif !defined(PJ_DECL) # if defined(__cplusplus) # define PJ_DECL(type) type # else # define PJ_DECL(type) extern type # endif #endif /** * @def PJ_DEF(type) * @param type The return type of the function. * Define a function. */ #if defined(PJ_DLL) && defined(PJ_EXPORTING) # define PJ_DEF(type) PJ_EXPORT_DEF_SPECIFIER type #elif !defined(PJ_DEF) # define PJ_DEF(type) type #endif /** * @def PJ_DECL_NO_RETURN(type) * @param type The return type of the function. * Declare a function that will not return. */ /** * @def PJ_IDECL_NO_RETURN(type) * @param type The return type of the function. * Declare an inline function that will not return. */ /** * @def PJ_BEGIN_DECL * Mark beginning of declaration section in a header file. */ /** * @def PJ_END_DECL * Mark end of declaration section in a header file. */ #ifdef __cplusplus # define PJ_DECL_NO_RETURN(type) PJ_DECL(type) PJ_NORETURN # define PJ_IDECL_NO_RETURN(type) PJ_INLINE(type) PJ_NORETURN # define PJ_BEGIN_DECL extern "C" { # define PJ_END_DECL } #else # define PJ_DECL_NO_RETURN(type) PJ_NORETURN PJ_DECL(type) # define PJ_IDECL_NO_RETURN(type) PJ_NORETURN PJ_INLINE(type) # define PJ_BEGIN_DECL # define PJ_END_DECL #endif /** * @def PJ_DECL_DATA(type) * @param type The data type. * Declare a global data. */ #if defined(PJ_DLL) # if defined(PJ_EXPORTING) # define PJ_DECL_DATA(type) PJ_EXPORT_DECL_SPECIFIER extern type # else # define PJ_DECL_DATA(type) PJ_IMPORT_DECL_SPECIFIER extern type # endif #elif !defined(PJ_DECL_DATA) # define PJ_DECL_DATA(type) extern type #endif /** * @def PJ_DEF_DATA(type) * @param type The data type. * Define a global data. */ #if defined(PJ_DLL) && defined(PJ_EXPORTING) # define PJ_DEF_DATA(type) PJ_EXPORT_DEF_SPECIFIER type #elif !defined(PJ_DEF_DATA) # define PJ_DEF_DATA(type) type #endif /** * @def PJ_IDECL(type) * @param type The function's return type. * Declare a function that may be expanded as inline. */ /** * @def PJ_IDEF(type) * @param type The function's return type. * Define a function that may be expanded as inline. */ #if PJ_FUNCTIONS_ARE_INLINED # define PJ_IDECL(type) PJ_INLINE(type) # define PJ_IDEF(type) PJ_INLINE(type) #else # define PJ_IDECL(type) PJ_DECL(type) # define PJ_IDEF(type) PJ_DEF(type) #endif /** * @def PJ_UNUSED_ARG(arg) * @param arg The argument name. * PJ_UNUSED_ARG prevents warning about unused argument in a function. */ #define PJ_UNUSED_ARG(arg) (void)arg /** * @def PJ_TODO(id) * @param id Any identifier that will be printed as TODO message. * PJ_TODO macro will display TODO message as warning during compilation. * Example: PJ_TODO(CLEAN_UP_ERROR); */ #ifndef PJ_TODO # define PJ_TODO(id) TODO___##id: #endif /** * Simulate race condition by sleeping the thread in strategic locations. * Default: no! */ #ifndef PJ_RACE_ME # define PJ_RACE_ME(x) #endif /** * Function attributes to inform that the function may throw exception. * * @param x The exception list, enclosed in parenthesis. */ #define __pj_throw__(x) /** @} */ /******************************************************************** * Sanity Checks */ #ifndef PJ_HAS_HIGH_RES_TIMER # error "PJ_HAS_HIGH_RES_TIMER is not defined!" #endif #if !defined(PJ_HAS_PENTIUM) # error "PJ_HAS_PENTIUM is not defined!" #endif #if !defined(PJ_IS_LITTLE_ENDIAN) # error "PJ_IS_LITTLE_ENDIAN is not defined!" #endif #if !defined(PJ_IS_BIG_ENDIAN) # error "PJ_IS_BIG_ENDIAN is not defined!" #endif #if !defined(PJ_EMULATE_RWMUTEX) # error "PJ_EMULATE_RWMUTEX should be defined in compat/os_xx.h" #endif #if !defined(PJ_THREAD_SET_STACK_SIZE) # error "PJ_THREAD_SET_STACK_SIZE should be defined in compat/os_xx.h" #endif #if !defined(PJ_THREAD_ALLOCATE_STACK) # error "PJ_THREAD_ALLOCATE_STACK should be defined in compat/os_xx.h" #endif PJ_BEGIN_DECL /** PJLIB version major number. */ #define PJ_VERSION_NUM_MAJOR 2 /** PJLIB version minor number. */ #define PJ_VERSION_NUM_MINOR 4 /** PJLIB version revision number. */ #define PJ_VERSION_NUM_REV 5 /** * Extra suffix for the version (e.g. "-trunk"), or empty for * web release version. */ #define PJ_VERSION_NUM_EXTRA "-svn" /** * PJLIB version number consists of three bytes with the following format: * 0xMMIIRR00, where MM: major number, II: minor number, RR: revision * number, 00: always zero for now. */ #define PJ_VERSION_NUM ((PJ_VERSION_NUM_MAJOR << 24) | \ (PJ_VERSION_NUM_MINOR << 16) | \ (PJ_VERSION_NUM_REV << 8)) /** * PJLIB version string constant. @see pj_get_version() */ PJ_DECL_DATA(const char*) PJ_VERSION; /** * Get PJLIB version string. * * @return #PJ_VERSION constant. */ PJ_DECL(const char*) pj_get_version(void); /** * Dump configuration to log with verbosity equal to info(3). */ PJ_DECL(void) pj_dump_config(void); PJ_END_DECL #endif /* __PJ_CONFIG_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/config_site_sample.h ================================================ /* * This file contains several sample settings especially for Windows * Mobile and Symbian targets. You can include this file in your * file. * * The Windows Mobile and Symbian settings will be activated * automatically if you include this file. * * In addition, you may specify one of these macros (before including * this file) to activate additional settings: * * #define PJ_CONFIG_NOKIA_APS_DIRECT * Use this macro to activate the APS-Direct feature. Please see * http://trac.pjsip.org/repos/wiki/Nokia_APS_VAS_Direct for more * info. * * #define PJ_CONFIG_WIN32_WMME_DIRECT * Configuration to activate "APS-Direct" media mode on Windows or * Windows Mobile, useful for testing purposes only. */ /* * Typical configuration for WinCE target. */ #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 /* * PJLIB settings. */ /* Disable floating point support */ #define PJ_HAS_FLOATING_POINT 0 /* * PJMEDIA settings */ /* Select codecs to disable */ #define PJMEDIA_HAS_L16_CODEC 0 #define PJMEDIA_HAS_ILBC_CODEC 0 /* We probably need more buffers on WM, so increase the limit */ #define PJMEDIA_SOUND_BUFFER_COUNT 32 /* Fine tune Speex's default settings for best performance/quality */ #define PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY 5 /* For CPU reason, disable speex AEC and use the echo suppressor. */ #define PJMEDIA_HAS_SPEEX_AEC 0 /* Previously, resampling is disabled due to performance reason and * this condition prevented some 'light' wideband codecs (e.g: G722.1) * to work along with narrowband codecs. Lately, some tests showed * that 16kHz <-> 8kHz resampling using libresample small filter was * affordable on ARM9 260 MHz, so here we decided to enable resampling. * Note that it is important to make sure that libresample is created * using small filter. For example PJSUA_DEFAULT_CODEC_QUALITY must * be set to 3 or 4 so pjsua-lib will apply small filter resampling. */ //#define PJMEDIA_RESAMPLE_IMP PJMEDIA_RESAMPLE_NONE #define PJMEDIA_RESAMPLE_IMP PJMEDIA_RESAMPLE_LIBRESAMPLE /* Use the lighter WSOLA implementation */ #define PJMEDIA_WSOLA_IMP PJMEDIA_WSOLA_IMP_WSOLA_LITE /* * PJSIP settings. */ /* Set maximum number of dialog/transaction/calls to minimum to reduce * memory usage */ #define PJSIP_MAX_TSX_COUNT 31 #define PJSIP_MAX_DIALOG_COUNT 31 #define PJSUA_MAX_CALLS 4 /* * PJSUA settings */ /* Default codec quality, previously was set to 5, however it is now * set to 4 to make sure pjsua instantiates resampler with small filter. */ #define PJSUA_DEFAULT_CODEC_QUALITY 4 /* Set maximum number of objects to minimum to reduce memory usage */ #define PJSUA_MAX_ACC 4 #define PJSUA_MAX_PLAYERS 4 #define PJSUA_MAX_RECORDERS 4 #define PJSUA_MAX_CONF_PORTS (PJSUA_MAX_CALLS+2*PJSUA_MAX_PLAYERS) #define PJSUA_MAX_BUDDIES 32 #endif /* PJ_WIN32_WINCE */ /* * Typical configuration for Symbian OS target */ #if defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0 /* * PJLIB settings. */ /* Disable floating point support */ #define PJ_HAS_FLOATING_POINT 0 /* Misc PJLIB setting */ #define PJ_MAXPATH 80 /* This is important for Symbian. Symbian lacks vsnprintf(), so * if the log buffer is not long enough it's possible that * large incoming packet will corrupt memory when the log tries * to log the packet. */ #define PJ_LOG_MAX_SIZE (PJSIP_MAX_PKT_LEN+500) /* Since we don't have threads, log buffer can use static buffer * rather than stack */ #define PJ_LOG_USE_STACK_BUFFER 0 /* Disable check stack since it increases footprint */ #define PJ_OS_HAS_CHECK_STACK 0 /* * PJMEDIA settings */ /* Disable non-Symbian audio devices */ #define PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO 0 #define PJMEDIA_AUDIO_DEV_HAS_WMME 0 /* Select codecs to disable */ #define PJMEDIA_HAS_L16_CODEC 0 #define PJMEDIA_HAS_ILBC_CODEC 0 #define PJMEDIA_HAS_G722_CODEC 0 /* Fine tune Speex's default settings for best performance/quality */ #define PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY 5 /* For CPU reason, disable speex AEC and use the echo suppressor. */ #define PJMEDIA_HAS_SPEEX_AEC 0 /* Previously, resampling is disabled due to performance reason and * this condition prevented some 'light' wideband codecs (e.g: G722.1) * to work along with narrowband codecs. Lately, some tests showed * that 16kHz <-> 8kHz resampling using libresample small filter was * affordable on ARM9 222 MHz, so here we decided to enable resampling. * Note that it is important to make sure that libresample is created * using small filter. For example PJSUA_DEFAULT_CODEC_QUALITY must * be set to 3 or 4 so pjsua-lib will apply small filter resampling. */ //#define PJMEDIA_RESAMPLE_IMP PJMEDIA_RESAMPLE_NONE #define PJMEDIA_RESAMPLE_IMP PJMEDIA_RESAMPLE_LIBRESAMPLE /* Use the lighter WSOLA implementation */ #define PJMEDIA_WSOLA_IMP PJMEDIA_WSOLA_IMP_WSOLA_LITE /* We probably need more buffers especially if MDA audio backend * is used, so increase the limit */ #define PJMEDIA_SOUND_BUFFER_COUNT 32 /* * PJSIP settings. */ /* Disable safe module access, since we don't use multithreading */ #define PJSIP_SAFE_MODULE 0 /* Use large enough packet size */ #define PJSIP_MAX_PKT_LEN 2000 /* Symbian has problem with too many large blocks */ #define PJSIP_POOL_LEN_ENDPT 1000 #define PJSIP_POOL_INC_ENDPT 1000 #define PJSIP_POOL_RDATA_LEN 2000 #define PJSIP_POOL_RDATA_INC 2000 #define PJSIP_POOL_LEN_TDATA 2000 #define PJSIP_POOL_INC_TDATA 512 #define PJSIP_POOL_LEN_UA 2000 #define PJSIP_POOL_INC_UA 1000 #define PJSIP_POOL_TSX_LAYER_LEN 256 #define PJSIP_POOL_TSX_LAYER_INC 256 #define PJSIP_POOL_TSX_LEN 512 #define PJSIP_POOL_TSX_INC 128 /* * PJSUA settings. */ /* Default codec quality, previously was set to 5, however it is now * set to 4 to make sure pjsua instantiates resampler with small filter. */ #define PJSUA_DEFAULT_CODEC_QUALITY 4 /* Set maximum number of dialog/transaction/calls to minimum */ #define PJSIP_MAX_TSX_COUNT 31 #define PJSIP_MAX_DIALOG_COUNT 31 #define PJSUA_MAX_CALLS 4 /* Other pjsua settings */ #define PJSUA_MAX_ACC 4 #define PJSUA_MAX_PLAYERS 4 #define PJSUA_MAX_RECORDERS 4 #define PJSUA_MAX_CONF_PORTS (PJSUA_MAX_CALLS+2*PJSUA_MAX_PLAYERS) #define PJSUA_MAX_BUDDIES 32 #endif /* * Additional configuration to activate APS-Direct feature for * Nokia S60 target * * Please see http://trac.pjsip.org/repos/wiki/Nokia_APS_VAS_Direct */ #ifdef PJ_CONFIG_NOKIA_APS_DIRECT /* MUST use switchboard rather than the conference bridge */ #define PJMEDIA_CONF_USE_SWITCH_BOARD 1 /* Enable APS sound device backend and disable MDA & VAS */ #define PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA 0 #define PJMEDIA_AUDIO_DEV_HAS_SYMB_APS 1 #define PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS 0 /* Enable passthrough codec framework */ #define PJMEDIA_HAS_PASSTHROUGH_CODECS 1 /* And selectively enable which codecs are supported by the handset */ #define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_G729 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC 1 #endif /* * Additional configuration to activate VAS-Direct feature for * Nokia S60 target * * Please see http://trac.pjsip.org/repos/wiki/Nokia_APS_VAS_Direct */ #ifdef PJ_CONFIG_NOKIA_VAS_DIRECT /* MUST use switchboard rather than the conference bridge */ #define PJMEDIA_CONF_USE_SWITCH_BOARD 1 /* Enable VAS sound device backend and disable MDA & APS */ #define PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA 0 #define PJMEDIA_AUDIO_DEV_HAS_SYMB_APS 0 #define PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS 1 /* Enable passthrough codec framework */ #define PJMEDIA_HAS_PASSTHROUGH_CODECS 1 /* And selectively enable which codecs are supported by the handset */ #define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_G729 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC 1 #endif /* * Configuration to activate "APS-Direct" media mode on Windows, * useful for testing purposes only. */ #ifdef PJ_CONFIG_WIN32_WMME_DIRECT /* MUST use switchboard rather than the conference bridge */ #define PJMEDIA_CONF_USE_SWITCH_BOARD 1 /* Only WMME supports the "direct" feature */ #define PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO 0 #define PJMEDIA_AUDIO_DEV_HAS_WMME 1 /* Enable passthrough codec framework */ #define PJMEDIA_HAS_PASSTHROUGH_CODECS 1 /* Only PCMA and PCMU are supported by WMME-direct */ #define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA 1 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR 0 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_G729 0 #define PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC 0 #endif /* * iPhone sample settings. */ #if PJ_CONFIG_IPHONE /* * PJLIB settings. */ /* Both armv6 and armv7 has FP hardware support. * See https://trac.pjsip.org/repos/ticket/1589 for more info */ #define PJ_HAS_FLOATING_POINT 1 /* * PJMEDIA settings */ /* We have our own native CoreAudio backend */ #define PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO 0 #define PJMEDIA_AUDIO_DEV_HAS_WMME 0 #define PJMEDIA_AUDIO_DEV_HAS_COREAUDIO 1 /* The CoreAudio backend has built-in echo canceller! */ #define PJMEDIA_HAS_SPEEX_AEC 0 /* Disable some codecs */ #define PJMEDIA_HAS_L16_CODEC 0 //#define PJMEDIA_HAS_G722_CODEC 0 /* Use the built-in CoreAudio's iLBC codec (yay!) */ #define PJMEDIA_HAS_ILBC_CODEC 1 #define PJMEDIA_ILBC_CODEC_USE_COREAUDIO 1 /* Fine tune Speex's default settings for best performance/quality */ #define PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY 5 /* * PJSIP settings. */ /* Increase allowable packet size, just in case */ //#define PJSIP_MAX_PKT_LEN 2000 /* * PJSUA settings. */ /* Default codec quality, previously was set to 5, however it is now * set to 4 to make sure pjsua instantiates resampler with small filter. */ #define PJSUA_DEFAULT_CODEC_QUALITY 4 /* Set maximum number of dialog/transaction/calls to minimum */ #define PJSIP_MAX_TSX_COUNT 31 #define PJSIP_MAX_DIALOG_COUNT 31 #define PJSUA_MAX_CALLS 4 /* Other pjsua settings */ #define PJSUA_MAX_ACC 4 #define PJSUA_MAX_PLAYERS 4 #define PJSUA_MAX_RECORDERS 4 #define PJSUA_MAX_CONF_PORTS (PJSUA_MAX_CALLS+2*PJSUA_MAX_PLAYERS) #define PJSUA_MAX_BUDDIES 32 #endif /* * Android sample settings. */ #if PJ_CONFIG_ANDROID #define PJ_ANDROID 1 /* * PJLIB settings. */ /* Disable floating point support */ #undef PJ_HAS_FLOATING_POINT #define PJ_HAS_FLOATING_POINT 0 /* * PJMEDIA settings */ /* We have our own OpenSL ES backend */ #define PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO 0 #define PJMEDIA_AUDIO_DEV_HAS_WMME 0 #define PJMEDIA_AUDIO_DEV_HAS_OPENSL 0 #define PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI 1 /* Disable some codecs */ #define PJMEDIA_HAS_L16_CODEC 0 //#define PJMEDIA_HAS_G722_CODEC 0 /* Fine tune Speex's default settings for best performance/quality */ #define PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY 5 /* Increase number of video device's supported formats */ #define PJMEDIA_VID_DEV_INFO_FMT_CNT 128 /* * PJSIP settings. */ /* Increase allowable packet size, just in case */ //#define PJSIP_MAX_PKT_LEN 2000 /* * PJSUA settings. */ /* Default codec quality, previously was set to 5, however it is now * set to 4 to make sure pjsua instantiates resampler with small filter. */ #define PJSUA_DEFAULT_CODEC_QUALITY 4 /* Set maximum number of dialog/transaction/calls to minimum */ #define PJSIP_MAX_TSX_COUNT 31 #define PJSIP_MAX_DIALOG_COUNT 31 #define PJSUA_MAX_CALLS 4 /* Other pjsua settings */ #define PJSUA_MAX_ACC 4 #define PJSUA_MAX_PLAYERS 4 #define PJSUA_MAX_RECORDERS 4 #define PJSUA_MAX_CONF_PORTS (PJSUA_MAX_CALLS+2*PJSUA_MAX_PLAYERS) #define PJSUA_MAX_BUDDIES 32 #endif /* * BB10 */ #if defined(PJ_CONFIG_BB10) && PJ_CONFIG_BB10 /* Quality 3 - 4 to use resampling small filter */ #define PJSUA_DEFAULT_CODEC_QUALITY 4 #define PJMEDIA_HAS_LEGACY_SOUND_API 0 #undef PJMEDIA_HAS_SPEEX_AEC #define PJMEDIA_HAS_SPEEX_AEC 0 #undef PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO #define PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO 0 #endif /* * Minimum size */ #ifdef PJ_CONFIG_MINIMAL_SIZE # undef PJ_OS_HAS_CHECK_STACK # define PJ_OS_HAS_CHECK_STACK 0 # define PJ_LOG_MAX_LEVEL 0 # define PJ_ENABLE_EXTRA_CHECK 0 # define PJ_HAS_ERROR_STRING 0 # undef PJ_IOQUEUE_MAX_HANDLES /* Putting max handles to lower than 32 will make pj_fd_set_t size smaller * than native fdset_t and will trigger assertion on sock_select.c. */ # define PJ_IOQUEUE_MAX_HANDLES 32 # define PJ_CRC32_HAS_TABLES 0 # define PJSIP_MAX_TSX_COUNT 15 # define PJSIP_MAX_DIALOG_COUNT 15 # define PJSIP_UDP_SO_SNDBUF_SIZE 4000 # define PJSIP_UDP_SO_RCVBUF_SIZE 4000 # define PJMEDIA_HAS_ALAW_ULAW_TABLE 0 #elif defined(PJ_CONFIG_MAXIMUM_SPEED) # define PJ_SCANNER_USE_BITWISE 0 # undef PJ_OS_HAS_CHECK_STACK # define PJ_OS_HAS_CHECK_STACK 0 # define PJ_LOG_MAX_LEVEL 3 # define PJ_ENABLE_EXTRA_CHECK 0 # define PJ_IOQUEUE_MAX_HANDLES 5000 # define PJSIP_MAX_TSX_COUNT ((640*1024)-1) # define PJSIP_MAX_DIALOG_COUNT ((640*1024)-1) # define PJSIP_UDP_SO_SNDBUF_SIZE (24*1024*1024) # define PJSIP_UDP_SO_RCVBUF_SIZE (24*1024*1024) # define PJ_DEBUG 0 # define PJSIP_SAFE_MODULE 0 # define PJ_HAS_STRICMP_ALNUM 0 # define PJ_HASH_USE_OWN_TOLOWER 1 # define PJSIP_UNESCAPE_IN_PLACE 1 # if defined(PJ_WIN32) || defined(PJ_WIN64) # define PJSIP_MAX_NET_EVENTS 10 # endif # define PJSUA_MAX_CALLS 512 #endif ================================================ FILE: deps/pjsip/pjlib/include/pj/ctype.h ================================================ /* $Id: ctype.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_CTYPE_H__ #define __PJ_CTYPE_H__ /** * @file ctype.h * @brief C type helper macros. */ #include #include PJ_BEGIN_DECL /** * @defgroup pj_ctype ctype - Character Type * @ingroup PJ_MISC * @{ * * This module contains several inline functions/macros for testing or * manipulating character types. It is provided in PJLIB because PJLIB * must not depend to LIBC. */ /** * Returns a non-zero value if either isalpha or isdigit is true for c. * @param c The integer character to test. * @return Non-zero value if either isalpha or isdigit is true for c. */ PJ_INLINE(int) pj_isalnum(unsigned char c) { return isalnum(c); } /** * Returns a non-zero value if c is a particular representation of an * alphabetic character. * @param c The integer character to test. * @return Non-zero value if c is a particular representation of an * alphabetic character. */ PJ_INLINE(int) pj_isalpha(unsigned char c) { return isalpha(c); } /** * Returns a non-zero value if c is a particular representation of an * ASCII character. * @param c The integer character to test. * @return Non-zero value if c is a particular representation of * an ASCII character. */ PJ_INLINE(int) pj_isascii(unsigned char c) { return c<128; } /** * Returns a non-zero value if c is a particular representation of * a decimal-digit character. * @param c The integer character to test. * @return Non-zero value if c is a particular representation of * a decimal-digit character. */ PJ_INLINE(int) pj_isdigit(unsigned char c) { return isdigit(c); } /** * Returns a non-zero value if c is a particular representation of * a space character (0x09 - 0x0D or 0x20). * @param c The integer character to test. * @return Non-zero value if c is a particular representation of * a space character (0x09 - 0x0D or 0x20). */ PJ_INLINE(int) pj_isspace(unsigned char c) { return isspace(c); } /** * Returns a non-zero value if c is a particular representation of * a lowercase character. * @param c The integer character to test. * @return Non-zero value if c is a particular representation of * a lowercase character. */ PJ_INLINE(int) pj_islower(unsigned char c) { return islower(c); } /** * Returns a non-zero value if c is a particular representation of * a uppercase character. * @param c The integer character to test. * @return Non-zero value if c is a particular representation of * a uppercase character. */ PJ_INLINE(int) pj_isupper(unsigned char c) { return isupper(c); } /** * Returns a non-zero value if c is a either a space (' ') or horizontal * tab ('\\t') character. * @param c The integer character to test. * @return Non-zero value if c is a either a space (' ') or horizontal * tab ('\\t') character. */ PJ_INLINE(int) pj_isblank(unsigned char c) { return isblank(c); } /** * Converts character to lowercase. * @param c The integer character to convert. * @return Lowercase character of c. */ PJ_INLINE(int) pj_tolower(unsigned char c) { return tolower(c); } /** * Converts character to uppercase. * @param c The integer character to convert. * @return Uppercase character of c. */ PJ_INLINE(int) pj_toupper(unsigned char c) { return toupper(c); } /** * Returns a non-zero value if c is a particular representation of * an hexadecimal digit character. * @param c The integer character to test. * @return Non-zero value if c is a particular representation of * an hexadecimal digit character. */ PJ_INLINE(int) pj_isxdigit(unsigned char c){ return isxdigit(c); } /** * Array of hex digits, in lowerspace. */ /*extern char pj_hex_digits[];*/ #define pj_hex_digits "0123456789abcdef" /** * Convert a value to hex representation. * @param value Integral value to convert. * @param p Buffer to hold the hex representation, which must be * at least two bytes length. */ PJ_INLINE(void) pj_val_to_hex_digit(unsigned value, char *p) { *p++ = pj_hex_digits[ (value & 0xF0) >> 4 ]; *p = pj_hex_digits[ (value & 0x0F) ]; } /** * Convert hex digit c to integral value. * @param c The hex digit character. * @return The integral value between 0 and 15. */ PJ_INLINE(unsigned) pj_hex_digit_to_val(unsigned char c) { if (c <= '9') return (c-'0') & 0x0F; else if (c <= 'F') return (c-'A'+10) & 0x0F; else return (c-'a'+10) & 0x0F; } /** @} */ PJ_END_DECL #endif /* __PJ_CTYPE_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/doxygen.h ================================================ /* $Id: doxygen.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_DOXYGEN_H__ #define __PJ_DOXYGEN_H__ /** * @file doxygen.h * @brief Doxygen's mainpage. */ /*////////////////////////////////////////////////////////////////////////// */ /* INTRODUCTION PAGE */ /** * @mainpage Welcome to PJLIB! * * @section intro_sec What is PJLIB * * PJLIB is an Open Source, small footprint framework library written in C for * making scalable applications. Because of its small footprint, it can be used * in embedded applications (we hope so!), but yet the library is also aimed for * facilitating the creation of high performance protocol stacks. * * PJLIB is released under GPL terms. * * @section download_sec Download * * PJLIB and all documentation can be downloaded from * http://www.pjsip.org. * * * @section how_to_use_sec About This Documentation * * This document is generated directly from PJLIB source file using * \a doxygen (http://www.doxygen.org). Doxygen is a great (and free!) * tools for generating such documentation. * * * @subsection find_samples_subsec How to Read This Document * * This documentation is laid out more to be a reference guide instead * of tutorial, therefore first time users may find it difficult to * grasp PJLIB by reading this document alone. * * However, we've tried our best to make this document easy to follow. * For first time users, we would suggest that you follow these steps * when reading this documentation: * * - continue reading this introduction chapter. At the end of this * chapter, you'll find section called \ref pjlib_fundamentals_sec * which should guide you to understand basic things about PJLIB. * * - find information about specific features that you want to use * in PJLIB. Use the Module Index to find out about all * features in PJLIB (if you're browsing the HTML documentation, * click on the \a Module link on top of the page, or if you're * reading the PDF documentation, click on \a Module \a Documentation * on the navigation pane on the left). * * @subsection doc_organize_sec How To's * * Please find below links to specific tasks that you probably * want to do: * * - How to Build PJLIB *\n * Please refer to \ref pjlib_build_sys_pg page for more information. * * - How to Use PJLIB in My Application *\n * Please refer to \ref configure_app_sec for more information. * * - How to Port PJLIB *\n * Please refer to \ref porting_pjlib_pg page. * * - Where to Read Samples Documentation *\n * Most of the modules provide link to the corresponding sample file. * Alternatively, to get the list of all examples, you can click on * Related Pages on the top of HTML document or on * PJLIB Page Documentation on navigation pane of your PDF reader. * * - How to Submit Code to PJLIB Project *\n * Please read \ref pjlib_coding_convention_page before submitting * your code. Send your code as patch against current Subversion tree * to the appropriate mailing list. * * * @section features_sec Features * * @subsection open_source_feat It's Open Source! * * PJLIB is currently released on GPL license, but other arrangements * can be made with the author. * * @subsection extreme_portable_feat Extreme Portability * * PJLIB is designed to be extremely portable. It can run on any kind * of processors (16-bit, 32-bit, or 64-bit, big or little endian, single * or multi-processors) and operating systems. Floating point or no * floating point. Multi-threading or not. * It can even run in environment where no ANSI LIBC is available. * * Currently PJLIB is known to run on these platforms: * - Win32/x86 (Win95/98/ME, NT/2000/XP/2003, mingw). * - arm, WinCE and Windows Mobile. * - Linux/x86, (user mode and as kernel module(!)). * - Linux/alpha * - Solaris/ultra. * - MacOS X/powerpc * - RTEMS (x86 and powerpc). * * And efforts is under way to port PJLIB on: * - Symbian OS * * * @subsection small_size_feat Small in Size * * One of the primary objectives is to have library that is small in size for * typical embedded applications. As a rough guidance, we aim to keep the * library size below 100KB for it to be considered as small. * As the result, most of the functionalities in the library can be tailored * to meet the requirements; user can enable/disable specific functionalities * to get the desired size/performance/functionality balance. * * For more info, please see @ref pj_config. * * * @subsection big_perform_feat Big in Performance * * Almost everything in PJLIB is designed to achieve the highest possible * performance out of the target platform. * * * @subsection no_dyn_mem No Dynamic Memory Allocations * * The central idea of PJLIB is that for applications to run as fast as it can, * it should not use \a malloc() at all, but instead should get the memory * from a preallocated storage pool. There are few things that can be * optimized with this approach: * * - \a alloc() is a O(1) operation. * - no mutex is used inside alloc(). It is assumed that synchronization * will be used in higher abstraction by application anyway. * - no \a free() is required. All chunks will be deleted when the pool is * destroyed. * * The performance gained on some systems can be as high as 30x speed up * against \a malloc() and \a free() on certain configurations, but of * course your mileage may vary. * * For more information, see \ref PJ_POOL_GROUP * * * @subsection os_abstract_feat Operating System Abstraction * * PJLIB has abstractions for features that are normally not portable * across operating systems: * - @ref PJ_THREAD *\n * Portable thread manipulation. * - @ref PJ_TLS *\n * Storing data in thread's private data. * - @ref PJ_MUTEX *\n * Mutual exclusion protection. * - @ref PJ_SEM *\n * Semaphores. * - @ref PJ_ATOMIC *\n * Atomic variables and their operations. * - @ref PJ_CRIT_SEC *\n * Fast locking of critical sections. * - @ref PJ_LOCK *\n * High level abstraction for lock objects. * - @ref PJ_EVENT *\n * Event object. * - @ref PJ_TIME *\n * Portable time manipulation. * - @ref PJ_TIMESTAMP *\n * High resolution time value. * - etc. * * * @subsection ll_network_io_sec Low-Level Network I/O * * PJLIB has very portable abstraction and fairly complete set of API for * doing network I/O communications. At the lowest level, PJLIB provides: * * - @ref PJ_SOCK *\n * A highly portable socket abstraction, runs on all kind of * network APIs such as standard BSD socket, Windows socket, Linux * \b kernel socket, PalmOS networking API, etc. * * - @ref pj_addr_resolve *\n * Portable address resolution, which implements #pj_gethostbyname(). * * - @ref PJ_SOCK_SELECT *\n * A portable \a select() like API (#pj_sock_select()) which can be * implemented with various back-end. * * * * @subsection timer_mgmt_sec Timer Management * * A passive framework for managing timer, see @ref PJ_TIMER for more info. * There is also function to retrieve high resolution timestamp * from the system (see @ref PJ_TIMESTAMP). * * * @subsection data_struct_sec Various Data Structures * * Various data structures are provided in the library: * * - @ref PJ_PSTR * - @ref PJ_ARRAY * - @ref PJ_HASH * - @ref PJ_LIST * - @ref PJ_RBTREE * * * @subsection exception_sec Exception Construct * * A convenient TRY/CATCH like construct to propagate errors, which by * default are used by the @ref PJ_POOL_GROUP "memory pool" and * the lexical scanner in pjlib-util. The exception * construct can be used to write programs like below: * *
 *    #define SYNTAX_ERROR  1
 *
 *    PJ_TRY {
 *       msg = NULL;
 *       msg = parse_msg(buf, len);
 *    }
 *    PJ_CATCH ( SYNTAX_ERROR ) {
 *       .. handle error ..
 *    }
 *    PJ_END;
 * 
* * Please see @ref PJ_EXCEPT for more information. * * * @subsection logging_sec Logging Facility * * PJLIB @ref PJ_LOG consists of macros to write logging information to * some output device. Some of the features of the logging facility: * * - the verbosity can be fine-tuned both at compile time (to control * the library size) or run-time (to control the verbosity of the * information). * - output device is configurable (e.g. stdout, printk, file, etc.) * - log decoration is configurable. * * See @ref PJ_LOG for more information. * * * @subsection guid_gen_sec Random and GUID Generation * * PJLIB provides facility to create random string * (#pj_create_random_string()) or globally unique identifier * (see @ref PJ_GUID). * * * * @section configure_app_sec Configuring Application to use PJLIB * * @subsection pjlib_compil_sec Building PJLIB * * Follow the instructions in \ref pjlib_build_sys_pg to build * PJLIB. * * @subsection pjlib_compil_app_sec Building Applications with PJLIB * * Use the following settings when building applications with PJLIB. * * @subsubsection compil_inc_dir_sec Include Search Path * * Add this to your include search path ($PJLIB is PJLIB root directory): *
 *   $PJLIB/include
 * 
* * @subsubsection compil_inc_file_sec Include PJLIB Header * * To include all PJLIB headers: * \verbatim #include \endverbatim * * Alternatively, you can include individual PJLIB headers like this: * \verbatim #include #include \endverbatim * * * @subsubsection compil_lib_dir_sec Library Path * * Add this to your library search path: *
 *   $PJLIB/lib
 * 
* * Then add the appropriate PJLIB library to your link specification. For * example, you would add \c libpj-i386-linux-gcc.a when you're building * applications in Linux. * * * @subsection pjlib_fundamentals_sec Principles in Using PJLIB * * Few things that you \b MUST do when using PJLIB, to make sure that * you create trully portable applications. * * @subsubsection call_pjlib_init_sec Call pj_init() * * Before you do anything else, call \c pj_init(). This would make sure that * PJLIB system is properly set up. * * @subsubsection no_ansi_subsec Do NOT Use ANSI C * * Contrary to popular teaching, ANSI C (and LIBC) is not the most portable * library in the world, nor it's the most ubiquitous. For example, LIBC * is not available in Linux kernel. Also normally LIBC will be excluded * from compilation of RTOSes to reduce size. * * So for maximum portability, do NOT use ANSI C. Do not even try to include * any other header files outside . Stick with the functionalities * provided by PJLIB. * * * @subsubsection string_rep_subsubsec Use pj_str_t instead of C Strings * * PJLIB uses pj_str_t instead of normal C strings. You SHOULD follow this * convention too. Remember, ANSI string-h is not always available. And * PJLIB string is faster! * * @subsubsection mem_alloc_subsubsec Use Pool for Memory Allocations * * You MUST NOT use \a malloc() or any other memory allocation functions. * Use PJLIB @ref PJ_POOL_GROUP instead! It's faster and most portable. * * @subsection logging_subsubsec Use Logging for Text Display * * DO NOT use for text output. Use PJLIB @ref PJ_LOG instead. * * * @section porting_pjlib_sec0 Porting PJLIB * * Please see \ref porting_pjlib_pg page on more information to port * PJLIB to new target. * * @section enjoy_sec Enjoy Using PJLIB! * * We hope that you find PJLIB usefull for your application. If you * have any questions, suggestions, critics, bug fixes, or anything * else, we would be happy to hear it. * * Enjoy using PJLIB! * * Benny Prijono < bennylp at pjsip dot org > */ /*////////////////////////////////////////////////////////////////////////// */ /* CODING CONVENTION */ /** * @page pjlib_coding_convention_page Coding Convention * * Before you submit your code/patches to be included with PJLIB, you must * make sure that your code is compliant with PJLIB coding convention. * This is very important! Otherwise we would not accept your code. * * @section coding_conv_editor_sec Editor Settings * * The single most important thing in the whole coding convention is editor * settings. It's more important than the correctness of your code (bugs will * only crash the system, but incorrect tab size is mental!). * * Kindly set your editor as follows: * - tab size to \b 8. * - indentation to \b 4. * * With \c vi, you can do it with: *
 *  :se ts=8
 *  :se sts=4
 * 
* * You should replace tab with eight spaces. * * @section coding_conv_detail_sec Coding Style * * Coding style MUST strictly follow K&R style. The rest of coding style * must follow current style. You SHOULD be able to observe the style * currently used by PJLIB from PJLIB sources, and apply the style to your * code. If you're not able to do simple thing like to observe PJLIB * coding style from the sources, then logic dictates that your ability to * observe more difficult area in PJLIB such as memory allocation strategy, * concurrency, etc is questionable. * * @section coding_conv_comment_sec Commenting Your Code * * Public API (e.g. in header files) MUST have doxygen compliant comments. * */ /*////////////////////////////////////////////////////////////////////////// */ /* BUILDING AND INSTALLING PJLIB */ /** * @page pjlib_build_sys_pg Building, and Installing PJLIB * * @section build_sys_install_sec Build and Installation * * \note * The most up-to-date information on building and installing PJLIB * should be found in the website, under "Getting Started" document. * More over, the new PJLIB build system is now based on autoconf, * so some of the information here might not be relevant anymore * (although most still are, since the autoconf script still use * the old Makefile system as the backend). * * @subsection build_sys_install_win32_sec Visual Studio * * The PJLIB Visual Studio workspace supports the building of PJLIB * for Win32 target. Although currently only the Visual Studio 6 Workspace is * actively maintained, developers with later version of Visual Studio * can easily imports VS6 workspace into their IDE. * * To start building PJLIB projects with Visual Studio 6 or later, open * the \a workspace file in the corresponding \b \c build directory. You have * several choices on which \a dsw file to open: \verbatim $PJPROJECT/pjlib/build/pjlib.dsw $PJPROJECT/pjsip/build/pjsip.dsw ..etc \endverbatim * * The easiest way is to open pjsip_apps.dsw file in \b \c $PJPROJECT/pjsip-apps/build * directory, and build pjsua project or the samples project. * However this will not build the complete projects. * For example, the PJLIB test is not included in this workspace. * To build the complete projects, you must * open and build each \a dsw file in \c build directory in each * subprojects. For example, to open the complete PJLIB workspace, open * pjlib.dsw in $PJPROJECT/pjlib/build directory. * * * @subsubsection config_site_create_vc_sec Create config_site.h * * The file $PJPROJECT/pjlib/include/pj/config_site.h * is supposed to contain configuration that is specific to your site/target. * This file is not part of PJLIB, so you must create it yourself. Normally * you just need to create a blank file. * * The reason why it's not included in PJLIB is so that you would not accidently * overwrite your site configuration. * * If you fail to do this, Visual C will complain with error like: * * "fatal error C1083: Cannot open include file: 'pj/config_site.h': No such file * or directory". * * @subsubsection build_vc_subsubsec Build the Projects * * Just hit the build button! * * * @subsection build_sys_install_unix_sec Make System * * For other targets, PJLIB provides a rather comprehensive build system * that uses GNU \a make (and only GNU \a make will work). * Currently, the build system supports building * PJLIB for these targets: * - i386/Win32/mingw * - i386/Linux * - i386/Linux (kernel) * - alpha/linux * - sparc/SunOS * - etc.. * * * @subsubsection build_req_sec Requirements * * In order to use the \c make based build system, you MUST have: * * - GNU make *\n * The Makefiles heavily utilize GNU make commands which most likely * are not available in other \c make system. * - bash shell is recommended. *\n * Specificly, there is a command "echo -n" which may not work * in other shells. This command is used when generating dependencies * (make dep) and it's located in * $PJPROJECT/build/rules.mak. * - ar, ranlib from GNU binutils *\n * In your system has different ar or ranlib (e.g. they * may have been installed as gar and granlib), then * either you create the relevant symbolic links, or modify * $PJPROJECT/build/cc-gcc.mak and rename ar and * ranlib to the appropriate names. * - gcc to generate dependency. *\n * Currently the build system uses "gcc -MM" to generate build * dependencies. If gcc is not desired to generate dependency, * then either you don't run make dep, or edit * $PJPROJECT/build/rules.mak to calculate dependency using * your prefered method. (And let me know when you do so so that I can * update the file. :) ) * * @subsubsection build_overview_sec Building the Project * * Generally, steps required to build the PJLIB are: * \verbatim $ cd /home/user/pjproject $ ./configure $ touch pjlib/include/pj/config_site.h $ make dep $ make \endverbatim * * The above process will build all static libraries and all applications. * * \note the configure script is not a proper autoconf script, * but rather a simple shell script to detect current host. This script * currently does not support cross-compilation. * * \note For Linux kernel target, there are additional steps required, which * will be explained in section \ref linux_kern_target_subsec. * * @subsubsection build_mak_sec Cross Compilation * * For cross compilation, you will need to edit the \c build.mak file in * \c $PJPROJECT root directory manually. Please see README-configure file * in the root directory for more information. * * For Linux kernel target, you are also required to declare the following * variables in this file: * - \c KERNEL_DIR: full path of kernel source tree. * - \c KERNEL_ARCH: kernel ARCH options (e.g. "ARCH=um"), or leave blank * for default. * - \c PJPROJECT_DIR: full path of PJPROJECT source tree. * * Apart from these, there are also additional steps required to build * Linux kernel target, which will be explained in \ref linux_kern_target_subsec. * * @subsubsection build_dir_sec Files in "build" Directory * * The *.mak files in \c $PJPROJECT/build directory are used to specify * the configuration for the specified compiler, target machine target * operating system, and host options. These files will be executed * (included) by \a make during building process, depending on the values * specified in $PJPROJECT/build.mak file. * * Normally you don't need to edit these files, except when you're porting * PJLIB to new target. * * Below are the description of some files in this directory: * * - rules.mak: contains generic rules always included during make. * - cc-gcc.mak: rules when gcc is used for compiler. * - cc-vc.mak: rules when MSVC compiler is used. * - host-mingw.mak: rules for building in mingw host. * - host-unix.mak: rules for building in Unix/Posix host. * - host-win32.mak: rules for building in Win32 command console * (only valid when VC is used). * - m-i386.mak: rules when target machine is an i386 processor. * - m-m68k.mak: rules when target machine is an m68k processor. * - os-linux.mak: rules when target OS is Linux. * - os-linux-kernel.mak: rules when PJLIB is to be build as * part of Linux kernel. * - os-win32.mak: rules when target OS is Win32. * * * @subsubsection config_site_create_sec Create config_site.h * * The file $PJPROJECT/pjlib/include/pj/config_site.h * is supposed to contain configuration that is specific to your site/target. * This file is not part of PJLIB, so you must create it yourself. * * The reason why it's not included in PJLIB is so that you would not accidently * overwrite your site configuration. * * * @subsubsection invoking_make_sec Invoking make * * Normally, \a make is invoked in \c build directory under each project. * For example, to build PJLIB, you would invoke \a make in * \c $PJPROJECT/pjlib/build directory like below: * \verbatim $ cd pjlib/build $ make \endverbatim * * Alternatively you may invoke make in $PJPROJECT * directory, to build all projects under that directory (e.g. * PJLIB, PJSIP, etc.). * * * @subsubsection linux_kern_target_subsec Linux Kernel Target * * \note * BUILDING APPLICATIONS IN LINUX KERNEL MODE IS A VERY DANGEROUS BUSINESS. * YOU MAY CRASH THE WHOLE OF YOUR SYSTEM, CORRUPT YOUR HARDISK, ETC. PJLIB * KERNEL MODULES ARE STILL IN EXPERIMENTAL PHASE. DO NOT RUN IT IN PRODUCTION * SYSTEMS OR OTHER SYSTEMS WHERE RISK OF LOSS OF DATA IS NOT ACCEPTABLE. * YOU HAVE BEEN WARNED. * * \note * User Mode Linux (UML) provides excellent way to experiment with Linux * kernel without risking the stability of the host system. See * http://user-mode-linux.sourceforge.net for details. * * \note * I only use UML to experiment with PJLIB kernel modules. * I wouldn't be so foolish to use my host Linux machine to experiment * with this. * * \note * You have been warned. * * For building PJLIB for Linux kernel target, there are additional steps required. * In general, the additional tasks are: * - Declare some more variables in build.mak file (this * has been explained in \ref build_mak_sec above). * - Perform these two small modifications in kernel source tree. * * There are two small modification need to be applied to the kernel tree. * * 1. Edit Makefile in kernel root source tree. * * Add the following lines at the end of the Makefile in your * $KERNEL_SRC dir: \verbatim script: $(SCRIPT) \endverbatim * * \note Remember to replace spaces with tab in the Makefile. * * The modification above is needed to capture kernel's \c $CFLAGS and * \c $CFLAGS_MODULE which will be used for PJLIB's compilation. * * 2. Add Additional Exports. * * We need the kernel to export some more symbols for our use. So we declare * the additional symbols to be exported in extra-exports.c file, and add * a this file to be compiled into the kernel: * * - Copy the file extra-exports.c from pjlib/src/pj * directory to $KERNEL_SRC/kernel/ directory. * - Edit Makefile in that directory, and add this line * somewhere after the declaration of that variable: \verbatim obj-y += extra-exports.o \endverbatim * * To illustrate what have been done in your kernel source tree, below * is screenshot of my kernel source tree _after_ the modification. * \verbatim [root@vpc-linux linux-2.6.7]# pwd /usr/src/linux-2.6.7 [root@vpc-linux linux-2.6.7]# [root@vpc-linux linux-2.6.7]# [root@vpc-linux linux-2.6.7]# tail Makefile endif # skip-makefile FORCE: .PHONY: script script: $(SCRIPT) [root@vpc-linux linux-2.6.7]# [root@vpc-linux linux-2.6.7]# [root@vpc-linux linux-2.6.7]# head kernel/extra-exports.c #include #include EXPORT_SYMBOL(sys_select); EXPORT_SYMBOL(sys_epoll_create); EXPORT_SYMBOL(sys_epoll_ctl); EXPORT_SYMBOL(sys_epoll_wait); EXPORT_SYMBOL(sys_socket); [root@vpc-linux linux-2.6.7]# [root@vpc-linux linux-2.6.7]# [root@vpc-linux linux-2.6.7]# head -15 kernel/Makefile # # Makefile for the linux kernel. # obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ exit.o itimer.o time.o softirq.o resource.o \ sysctl.o capability.o ptrace.o timer.o user.o \ signal.o sys.o kmod.o workqueue.o pid.o \ rcupdate.o intermodule.o extable.o params.o posix-timers.o \ kthread.o obj-y += extra-exports.o obj-$(CONFIG_FUTEX) += futex.o obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o [root@vpc-linux linux-2.6.7]# \endverbatim * * Then you must rebuild the kernel. * If you fail to do this, you won't be able to insmod pjlib. * * \note You will see a lots of warning messages during pjlib-test compilation. * The warning messages complain about unresolved symbols which are defined * in pjlib module. You can safely ignore these warnings. However, you can not * ignore warnings about non-pjlib unresolved symbols. * * * @subsection makefile_explained_sec Makefile Explained * * The \a Makefile for each project (e.g. PJLIB, PJSIP, etc) should be * very similar in the contents. The Makefile is located under \c build * directory in each project subdir. * * @subsubsection pjlib_makefile_subsec PJLIB Makefile. * * Below is PJLIB's Makefile: * * \include build/Makefile * * @subsubsection pjlib_os_makefile_subsec PJLIB os-linux.mak. * * Below is file os-linux.mak file in * $PJPROJECT/pjlib/build directory, * which is OS specific configuration file for Linux target that is specific * for PJLIB project. For \b global OS specific configuration, please see * $PJPROJECT/build/os-*.mak. * * \include build/os-linux.mak * */ /*////////////////////////////////////////////////////////////////////////// */ /* PORTING PJLIB */ /** * @page porting_pjlib_pg Porting PJLIB * * \note * Since version 0.5.8, PJLIB build system is now based on autoconf, so * most of the time we shouldn't need to apply the tweakings below to get * PJLIB working on a new platform. However, since the autoconf build system * still uses the old Makefile build system, the information below may still * be useful for reference. * * * @section new_arch_sec Porting to New CPU Architecture * * Below is step-by-step guide to add support for new CPU architecture. * This sample is based on porting to Alpha architecture; however steps for * porting to other CPU architectures should be pretty similar. * * Also note that in this example, the operating system used is Linux. * Should you wish to add support for new operating system, then follow * the next section \ref porting_os_sec. * * Step-by-step guide to port to new CPU architecture: * - decide the name for the new architecture. In this case, we choose * alpha. * - edit file $PJPROJECT/build.mak, and add new section for * the new target: *
 *      #
 *      # Linux alpha, gcc
 *      #
 *      export MACHINE_NAME := alpha
 *      export OS_NAME := linux
 *      export CC_NAME := gcc
 *      export HOST_NAME := unix
 *    
* * - create a new file $PJPROJECT/build/m-alpha.mak. * Alternatively create a copy from other file in this directory. * The contents of this file will look something like: *
 *      export M_CFLAGS := $(CC_DEF)PJ_M_ALPHA=1
 *      export M_CXXFLAGS :=
 *      export M_LDFLAGS :=
 *      export M_SOURCES :=
 *    
* - create a new file $PJPROJECT/pjlib/include/pj/compat/m_alpha.h. * Alternatively create a copy from other header file in this directory. * The contents of this file will look something like: *
 *      #define PJ_HAS_PENTIUM          0
 *      #define PJ_IS_LITTLE_ENDIAN     1
 *      #define PJ_IS_BIG_ENDIAN        0
 *    
* - edit pjlib/include/pj/config.h. Add new processor * configuration in this header file, like follows: *
 *      ...
 *      #elif defined (PJ_M_ALPHA) && PJ_M_ALPHA != 0
 *      #   include 
 *      ...
 *    
* - done. Build PJLIB with: *
 *      $ cd $PJPROJECT/pjlib/build
 *      $ make dep
 *      $ make clean
 *      $ make
 *    
* * @section porting_os_sec Porting to New Operating System Target * * This section will try to give you rough guideline on how to * port PJLIB to a new target. As a sample, we give the target a name tag, * for example xos (for X OS). * * @subsection new_compat_os_h_file_sec Create New Compat Header File * * You'll need to create a new header file * include/pj/compat/os_xos.h. You can copy as a * template other header file and edit it accordingly. * * @subsection modify_config_h_file_sec Modify config.h * * Then modify file include/pj/config.h to include * this file accordingly (e.g. when macro PJ_XOS is * defined): * \verbatim ... #elif defined(PJ_XOS) # include #else #... \endverbatim * * @subsection new_target_mak_file_sec Create New Global Make Config File * * Then you'll need to create global configuration file that * is specific for this OS, i.e. os-xos.mak in * $PJPROJECT/build directory. * * At very minimum, the file will normally need to define * PJ_XOS=1 in the \c CFLAGS section: * \verbatim # # $PJPROJECT/build/os-xos.mak: # export OS_CFLAGS := $(CC_DEF)PJ_XOS=1 export OS_CXXFLAGS := export OS_LDFLAGS := export OS_SOURCES := \endverbatim * * * @subsection new_target_prj_mak_file_sec Create New Project's Make Config File * * Then you'll need to create xos-specific configuration file * for PJLIB. This file is also named os-xos.mak, * but its located in pjlib/build directory. * This file will specify source files that are specific to * this OS to be included in the build process. * * Below is a sample: \verbatim # # pjlib/build/os-xos.mak: # XOS specific configuration for PJLIB. # export PJLIB_OBJS += os_core_xos.o \ os_error_unix.o \ os_time_ansi.o export TEST_OBJS += main.o export TARGETS = pjlib pjlib-test \endverbatim * * @subsection new_target_src_sec Create and Edit Source Files * * You'll normally need to create at least these files: * - os_core_xos.c: core OS specific * functionality. * - os_timestamp_xos.c: how to get timestamp * in this OS. * * Depending on how things are done in your OS, you may need * to create these files: * - os_error_*.c: how to manipulate * OS error codes. Alternatively you may use existing * os_error_unix.c if the OS has \c errno and * \c strerror() function. * - ioqueue_*.c: if the OS has specific method * to perform asynchronous I/O. Alternatively you may * use existing ioqueue_select.c if the OS supports * \c select() function call. * - sock_*.c: if the OS has specific method * to perform socket communication. Alternatively you may * use existing sock_bsd.c if the OS supports * BSD socket API, and edit include/pj/compat/socket.h * file accordingly. * * You will also need to check various files in * include/pj/compat/*.h, to see if they're * compatible with your OS. * * @subsection new_target_build_file_sec Build The Project * * After basic building blocks have been created for the OS, then * the easiest way to see which parts need to be fixed is by building * the project and see the error messages. * * @subsection new_target_edit_vs_new_file_sec Editing Existing Files vs Creating New File * * When you encounter compatibility errors in PJLIB during porting, * you have three options on how to fix the error: * - edit the existing *.c file, and give it #ifdef * switch for the new OS, or * - edit include/pj/compat/*.h instead, or * - create a totally new file. * * Basicly there is no strict rule on which approach is the best * to use, however the following guidelines may be used: * - if the file is expected to be completely different than * any existing file, then perhaps you should create a completely * new file. For example, file os_core_xxx.c will * normally be different for each OS flavour. * - if the difference can be localized in include/compat * header file, and existing #ifdef switch is there, * then preferably you should edit this include/compat * header file. * - if the existing *.c file has #ifdef switch, * then you may add another #elif switch there. This * normally is used for behaviors that are not totally * different on each platform. * - other than that above, use your own judgement on whether * to edit the file or create new file etc. */ #endif /* __PJ_DOXYGEN_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/errno.h ================================================ /* $Id: errno.h 4461 2013-04-05 03:02:19Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_ERRNO_H__ #define __PJ_ERRNO_H__ /** * @file errno.h * @brief PJLIB Error Subsystem */ #include #include #include PJ_BEGIN_DECL /** * @defgroup pj_errno Error Subsystem * @{ * * The PJLIB Error Subsystem is a framework to unify all error codes * produced by all components into a single error space, and provide * uniform set of APIs to access them. With this framework, any error * codes are encoded as pj_status_t value. The framework is extensible, * application may register new error spaces to be recognized by * the framework. * * @section pj_errno_retval Return Values * * All functions that returns @a pj_status_t returns @a PJ_SUCCESS if the * operation was completed successfully, or non-zero value to indicate * error. If the error came from operating system, then the native error * code is translated/folded into PJLIB's error namespace by using * #PJ_STATUS_FROM_OS() macro. The function will do this automatically * before returning the error to caller. * * @section err_services Retrieving and Displaying Error Messages * * The framework provides the following APIs to retrieve and/or display * error messages: * * - #pj_strerror(): this is the base API to retrieve error string * description for the specified pj_status_t error code. * * - #PJ_PERROR() macro: use this macro similar to PJ_LOG to format * an error message and display them to the log * * - #pj_perror(): this function is similar to PJ_PERROR() but unlike * #PJ_PERROR(), this function will always be included in the * link process. Due to this reason, prefer to use #PJ_PERROR() * if the application is concerned about the executable size. * * Application MUST NOT pass native error codes (such as error code from * functions like GetLastError() or errno) to PJLIB functions expecting * @a pj_status_t. * * @section err_extending Extending the Error Space * * Application may register new error space to be recognized by the * framework by using #pj_register_strerror(). Use the range started * from PJ_ERRNO_START_USER to avoid conflict with existing error * spaces. * */ /** * Guidelines on error message length. */ #define PJ_ERR_MSG_SIZE 80 /** * Buffer for title string of #PJ_PERROR(). */ #ifndef PJ_PERROR_TITLE_BUF_SIZE # define PJ_PERROR_TITLE_BUF_SIZE 120 #endif /** * Get the last platform error/status, folded into pj_status_t. * @return OS dependent error code, folded into pj_status_t. * @remark This function gets errno, or calls GetLastError() function and * convert the code into pj_status_t with PJ_STATUS_FROM_OS. Do * not call this for socket functions! * @see pj_get_netos_error() */ PJ_DECL(pj_status_t) pj_get_os_error(void); /** * Set last error. * @param code pj_status_t */ PJ_DECL(void) pj_set_os_error(pj_status_t code); /** * Get the last error from socket operations. * @return Last socket error, folded into pj_status_t. */ PJ_DECL(pj_status_t) pj_get_netos_error(void); /** * Set error code. * @param code pj_status_t. */ PJ_DECL(void) pj_set_netos_error(pj_status_t code); /** * Get the error message for the specified error code. The message * string will be NULL terminated. * * @param statcode The error code. * @param buf Buffer to hold the error message string. * @param bufsize Size of the buffer. * * @return The error message as NULL terminated string, * wrapped with pj_str_t. */ PJ_DECL(pj_str_t) pj_strerror( pj_status_t statcode, char *buf, pj_size_t bufsize); /** * A utility macro to print error message pertaining to the specified error * code to the log. This macro will construct the error message title * according to the 'title_fmt' argument, and add the error string pertaining * to the error code after the title string. A colon (':') will be added * automatically between the title and the error string. * * This function is similar to pj_perror() function, but has the advantage * that the function call can be omitted from the link process if the * log level argument is below PJ_LOG_MAX_LEVEL threshold. * * Note that the title string constructed from the title_fmt will be built on * a string buffer which size is PJ_PERROR_TITLE_BUF_SIZE, which normally is * allocated from the stack. By default this buffer size is small (around * 120 characters). Application MUST ensure that the constructed title string * will not exceed this limit, since not all platforms support truncating * the string. * * @see pj_perror() * * @param level The logging verbosity level, valid values are 0-6. Lower * number indicates higher importance, with level zero * indicates fatal error. Only numeral argument is * permitted (e.g. not variable). * @param arg Enclosed 'printf' like arguments, with the following * arguments: * - the sender (NULL terminated string), * - the error code (pj_status_t) * - the format string (title_fmt), and * - optional variable number of arguments suitable for the * format string. * * Sample: * \verbatim PJ_PERROR(2, (__FILE__, PJ_EBUSY, "Error making %s", "coffee")); \endverbatim * @hideinitializer */ #define PJ_PERROR(level,arg) do { \ pj_perror_wrapper_##level(arg); \ } while (0) /** * A utility function to print error message pertaining to the specified error * code to the log. This function will construct the error message title * according to the 'title_fmt' argument, and add the error string pertaining * to the error code after the title string. A colon (':') will be added * automatically between the title and the error string. * * Unlike the PJ_PERROR() macro, this function takes the \a log_level argument * as a normal argument, unlike in PJ_PERROR() where a numeral value must be * given. However this function will always be linked to the executable, * unlike PJ_PERROR() which can be omitted when the level is below the * PJ_LOG_MAX_LEVEL. * * Note that the title string constructed from the title_fmt will be built on * a string buffer which size is PJ_PERROR_TITLE_BUF_SIZE, which normally is * allocated from the stack. By default this buffer size is small (around * 120 characters). Application MUST ensure that the constructed title string * will not exceed this limit, since not all platforms support truncating * the string. * * @see PJ_PERROR() */ PJ_DECL(void) pj_perror(int log_level, const char *sender, pj_status_t status, const char *title_fmt, ...); /** * Type of callback to be specified in #pj_register_strerror() * * @param e The error code to lookup. * @param msg Buffer to store the error message. * @param max Length of the buffer. * * @return The error string. */ typedef pj_str_t (*pj_error_callback)(pj_status_t e, char *msg, pj_size_t max); /** * Register strerror message handler for the specified error space. * Application can register its own handler to supply the error message * for the specified error code range. This handler will be called * by #pj_strerror(). * * @param start_code The starting error code where the handler should * be called to retrieve the error message. * @param err_space The size of error space. The error code range then * will fall in start_code to start_code+err_space-1 * range. * @param f The handler to be called when #pj_strerror() is * supplied with error code that falls into this range. * * @return PJ_SUCCESS or the specified error code. The * registration may fail when the error space has been * occupied by other handler, or when there are too many * handlers registered to PJLIB. */ PJ_DECL(pj_status_t) pj_register_strerror(pj_status_t start_code, pj_status_t err_space, pj_error_callback f); /** * @hideinitializer * Return platform os error code folded into pj_status_t code. This is * the macro that is used throughout the library for all PJLIB's functions * that returns error from operating system. Application may override * this macro to reduce size (e.g. by defining it to always return * #PJ_EUNKNOWN). * * Note: * This macro MUST return non-zero value regardless whether zero is * passed as the argument. The reason is to protect logic error when * the operating system doesn't report error codes properly. * * @param os_code Platform OS error code. This value may be evaluated * more than once. * @return The platform os error code folded into pj_status_t. */ #ifndef PJ_RETURN_OS_ERROR # define PJ_RETURN_OS_ERROR(os_code) (os_code ? \ PJ_STATUS_FROM_OS(os_code) : -1) #endif /** * @hideinitializer * Fold a platform specific error into an pj_status_t code. * * @param e The platform os error code. * @return pj_status_t * @warning Macro implementation; the syserr argument may be evaluated * multiple times. */ #if PJ_NATIVE_ERR_POSITIVE # define PJ_STATUS_FROM_OS(e) (e == 0 ? PJ_SUCCESS : e + PJ_ERRNO_START_SYS) #else # define PJ_STATUS_FROM_OS(e) (e == 0 ? PJ_SUCCESS : PJ_ERRNO_START_SYS - e) #endif /** * @hideinitializer * Fold an pj_status_t code back to the native platform defined error. * * @param e The pj_status_t folded platform os error code. * @return pj_os_err_type * @warning macro implementation; the statcode argument may be evaluated * multiple times. If the statcode was not created by * pj_get_os_error or PJ_STATUS_FROM_OS, the results are undefined. */ #if PJ_NATIVE_ERR_POSITIVE # define PJ_STATUS_TO_OS(e) (e == 0 ? PJ_SUCCESS : e - PJ_ERRNO_START_SYS) #else # define PJ_STATUS_TO_OS(e) (e == 0 ? PJ_SUCCESS : PJ_ERRNO_START_SYS - e) #endif /** * @defgroup pj_errnum PJLIB's Own Error Codes * @ingroup pj_errno * @{ */ /** * Use this macro to generate error message text for your error code, * so that they look uniformly as the rest of the libraries. * * @param code The error code * @param msg The error test. */ #ifndef PJ_BUILD_ERR # define PJ_BUILD_ERR(code,msg) { code, msg " (" #code ")" } #endif /** * @hideinitializer * Unknown error has been reported. */ #define PJ_EUNKNOWN (PJ_ERRNO_START_STATUS + 1) /* 70001 */ /** * @hideinitializer * The operation is pending and will be completed later. */ #define PJ_EPENDING (PJ_ERRNO_START_STATUS + 2) /* 70002 */ /** * @hideinitializer * Too many connecting sockets. */ #define PJ_ETOOMANYCONN (PJ_ERRNO_START_STATUS + 3) /* 70003 */ /** * @hideinitializer * Invalid argument. */ #define PJ_EINVAL (PJ_ERRNO_START_STATUS + 4) /* 70004 */ /** * @hideinitializer * Name too long (eg. hostname too long). */ #define PJ_ENAMETOOLONG (PJ_ERRNO_START_STATUS + 5) /* 70005 */ /** * @hideinitializer * Not found. */ #define PJ_ENOTFOUND (PJ_ERRNO_START_STATUS + 6) /* 70006 */ /** * @hideinitializer * Not enough memory. */ #define PJ_ENOMEM (PJ_ERRNO_START_STATUS + 7) /* 70007 */ /** * @hideinitializer * Bug detected! */ #define PJ_EBUG (PJ_ERRNO_START_STATUS + 8) /* 70008 */ /** * @hideinitializer * Operation timed out. */ #define PJ_ETIMEDOUT (PJ_ERRNO_START_STATUS + 9) /* 70009 */ /** * @hideinitializer * Too many objects. */ #define PJ_ETOOMANY (PJ_ERRNO_START_STATUS + 10)/* 70010 */ /** * @hideinitializer * Object is busy. */ #define PJ_EBUSY (PJ_ERRNO_START_STATUS + 11)/* 70011 */ /** * @hideinitializer * The specified option is not supported. */ #define PJ_ENOTSUP (PJ_ERRNO_START_STATUS + 12)/* 70012 */ /** * @hideinitializer * Invalid operation. */ #define PJ_EINVALIDOP (PJ_ERRNO_START_STATUS + 13)/* 70013 */ /** * @hideinitializer * Operation is cancelled. */ #define PJ_ECANCELLED (PJ_ERRNO_START_STATUS + 14)/* 70014 */ /** * @hideinitializer * Object already exists. */ #define PJ_EEXISTS (PJ_ERRNO_START_STATUS + 15)/* 70015 */ /** * @hideinitializer * End of file. */ #define PJ_EEOF (PJ_ERRNO_START_STATUS + 16)/* 70016 */ /** * @hideinitializer * Size is too big. */ #define PJ_ETOOBIG (PJ_ERRNO_START_STATUS + 17)/* 70017 */ /** * @hideinitializer * Error in gethostbyname(). This is a generic error returned when * gethostbyname() has returned an error. */ #define PJ_ERESOLVE (PJ_ERRNO_START_STATUS + 18)/* 70018 */ /** * @hideinitializer * Size is too small. */ #define PJ_ETOOSMALL (PJ_ERRNO_START_STATUS + 19)/* 70019 */ /** * @hideinitializer * Ignored */ #define PJ_EIGNORED (PJ_ERRNO_START_STATUS + 20)/* 70020 */ /** * @hideinitializer * IPv6 is not supported */ #define PJ_EIPV6NOTSUP (PJ_ERRNO_START_STATUS + 21)/* 70021 */ /** * @hideinitializer * Unsupported address family */ #define PJ_EAFNOTSUP (PJ_ERRNO_START_STATUS + 22)/* 70022 */ /** * @hideinitializer * Object no longer exists */ #define PJ_EGONE (PJ_ERRNO_START_STATUS + 23)/* 70023 */ /** * @hideinitializer * Socket is stopped */ #define PJ_ESOCKETSTOP (PJ_ERRNO_START_STATUS + 24)/* 70024 */ /** @} */ /* pj_errnum */ /** @} */ /* pj_errno */ /** * PJ_ERRNO_START is where PJLIB specific error values start. */ #define PJ_ERRNO_START 20000 /** * PJ_ERRNO_SPACE_SIZE is the maximum number of errors in one of * the error/status range below. */ #define PJ_ERRNO_SPACE_SIZE 50000 /** * PJ_ERRNO_START_STATUS is where PJLIB specific status codes start. * Effectively the error in this class would be 70000 - 119000. */ #define PJ_ERRNO_START_STATUS (PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) /** * PJ_ERRNO_START_SYS converts platform specific error codes into * pj_status_t values. * Effectively the error in this class would be 120000 - 169000. */ #define PJ_ERRNO_START_SYS (PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE) /** * PJ_ERRNO_START_USER are reserved for applications that use error * codes along with PJLIB codes. * Effectively the error in this class would be 170000 - 219000. */ #define PJ_ERRNO_START_USER (PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE) /* * Below are list of error spaces that have been taken so far: * - PJSIP_ERRNO_START (PJ_ERRNO_START_USER) * - PJMEDIA_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE) * - PJSIP_SIMPLE_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*2) * - PJLIB_UTIL_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*3) * - PJNATH_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*4) * - PJMEDIA_AUDIODEV_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*5) * - PJ_SSL_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*6) * - PJMEDIA_VIDEODEV_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*7) */ /* Internal */ void pj_errno_clear_handlers(void); /****** Internal for PJ_PERROR *******/ /** * @def pj_perror_wrapper_1(arg) * Internal function to write log with verbosity 1. Will evaluate to * empty expression if PJ_LOG_MAX_LEVEL is below 1. * @param arg Log expression. */ #if PJ_LOG_MAX_LEVEL >= 1 #define pj_perror_wrapper_1(arg) pj_perror_1 arg /** Internal function. */ PJ_DECL(void) pj_perror_1(const char *sender, pj_status_t status, const char *title_fmt, ...); #else #define pj_perror_wrapper_1(arg) #endif /** * @def pj_perror_wrapper_2(arg) * Internal function to write log with verbosity 2. Will evaluate to * empty expression if PJ_LOG_MAX_LEVEL is below 2. * @param arg Log expression. */ #if PJ_LOG_MAX_LEVEL >= 2 #define pj_perror_wrapper_2(arg) pj_perror_2 arg /** Internal function. */ PJ_DECL(void) pj_perror_2(const char *sender, pj_status_t status, const char *title_fmt, ...); #else #define pj_perror_wrapper_2(arg) #endif /** * @def pj_perror_wrapper_3(arg) * Internal function to write log with verbosity 3. Will evaluate to * empty expression if PJ_LOG_MAX_LEVEL is below 3. * @param arg Log expression. */ #if PJ_LOG_MAX_LEVEL >= 3 #define pj_perror_wrapper_3(arg) pj_perror_3 arg /** Internal function. */ PJ_DECL(void) pj_perror_3(const char *sender, pj_status_t status, const char *title_fmt, ...); #else #define pj_perror_wrapper_3(arg) #endif /** * @def pj_perror_wrapper_4(arg) * Internal function to write log with verbosity 4. Will evaluate to * empty expression if PJ_LOG_MAX_LEVEL is below 4. * @param arg Log expression. */ #if PJ_LOG_MAX_LEVEL >= 4 #define pj_perror_wrapper_4(arg) pj_perror_4 arg /** Internal function. */ PJ_DECL(void) pj_perror_4(const char *sender, pj_status_t status, const char *title_fmt, ...); #else #define pj_perror_wrapper_4(arg) #endif /** * @def pj_perror_wrapper_5(arg) * Internal function to write log with verbosity 5. Will evaluate to * empty expression if PJ_LOG_MAX_LEVEL is below 5. * @param arg Log expression. */ #if PJ_LOG_MAX_LEVEL >= 5 #define pj_perror_wrapper_5(arg) pj_perror_5 arg /** Internal function. */ PJ_DECL(void) pj_perror_5(const char *sender, pj_status_t status, const char *title_fmt, ...); #else #define pj_perror_wrapper_5(arg) #endif /** * @def pj_perror_wrapper_6(arg) * Internal function to write log with verbosity 6. Will evaluate to * empty expression if PJ_LOG_MAX_LEVEL is below 6. * @param arg Log expression. */ #if PJ_LOG_MAX_LEVEL >= 6 #define pj_perror_wrapper_6(arg) pj_perror_6 arg /** Internal function. */ PJ_DECL(void) pj_perror_6(const char *sender, pj_status_t status, const char *title_fmt, ...); #else #define pj_perror_wrapper_6(arg) #endif PJ_END_DECL #endif /* __PJ_ERRNO_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/except.h ================================================ /* $Id: except.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_EXCEPTION_H__ #define __PJ_EXCEPTION_H__ /** * @file except.h * @brief Exception Handling in C. */ #include #include #include PJ_BEGIN_DECL /** * @defgroup PJ_EXCEPT Exception Handling * @ingroup PJ_MISC * @{ * * \section pj_except_sample_sec Quick Example * * For the impatient, take a look at some examples: * - @ref page_pjlib_samples_except_c * - @ref page_pjlib_exception_test * * \section pj_except_except Exception Handling * * This module provides exception handling syntactically similar to C++ in * C language. In Win32 systems, it uses Windows Structured Exception * Handling (SEH) if macro PJ_EXCEPTION_USE_WIN32_SEH is non-zero. * Otherwise it will use setjmp() and longjmp(). * * On some platforms where setjmp/longjmp is not available, setjmp/longjmp * implementation is provided. See for compatibility. * * The exception handling mechanism is completely thread safe, so the exception * thrown by one thread will not interfere with other thread. * * The exception handling constructs are similar to C++. The blocks will be * constructed similar to the following sample: * * \verbatim #define NO_MEMORY 1 #define SYNTAX_ERROR 2 int sample1() { PJ_USE_EXCEPTION; // declare local exception stack. PJ_TRY { ...// do something.. } PJ_CATCH(NO_MEMORY) { ... // handle exception 1 } PJ_END; } int sample2() { PJ_USE_EXCEPTION; // declare local exception stack. PJ_TRY { ...// do something.. } PJ_CATCH_ANY { if (PJ_GET_EXCEPTION() == NO_MEMORY) ...; // handle no memory situation else if (PJ_GET_EXCEPTION() == SYNTAX_ERROR) ...; // handle syntax error } PJ_END; } \endverbatim * * The above sample uses hard coded exception ID. It is @b strongly * recommended that applications request a unique exception ID instead * of hard coded value like above. * * \section pj_except_reg Exception ID Allocation * * To ensure that exception ID (number) are used consistently and to * prevent ID collisions in an application, it is strongly suggested that * applications allocate an exception ID for each possible exception * type. As a bonus of this process, the application can identify * the name of the exception when the particular exception is thrown. * * Exception ID management are performed with the following APIs: * - #pj_exception_id_alloc(). * - #pj_exception_id_free(). * - #pj_exception_id_name(). * * * PJLIB itself automatically allocates one exception id, i.e. * #PJ_NO_MEMORY_EXCEPTION which is declared in . This exception * ID is raised by default pool policy when it fails to allocate memory. * * CAVEATS: * - unlike C++ exception, the scheme here won't call destructors of local * objects if exception is thrown. Care must be taken when a function * hold some resorce such as pool or mutex etc. * - You CAN NOT make nested exception in one single function without using * a nested PJ_USE_EXCEPTION. Samples: \verbatim void wrong_sample() { PJ_USE_EXCEPTION; PJ_TRY { // Do stuffs ... } PJ_CATCH_ANY { // Do other stuffs .... .. // The following block is WRONG! You MUST declare // PJ_USE_EXCEPTION once again in this block. PJ_TRY { .. } PJ_CATCH_ANY { .. } PJ_END; } PJ_END; } \endverbatim * - You MUST NOT exit the function inside the PJ_TRY block. The correct way * is to return from the function after PJ_END block is executed. * For example, the following code will yield crash not in this code, * but rather in the subsequent execution of PJ_TRY block: \verbatim void wrong_sample() { PJ_USE_EXCEPTION; PJ_TRY { // do some stuffs ... return; <======= DO NOT DO THIS! } PJ_CATCH_ANY { } PJ_END; } \endverbatim * - You can not provide more than PJ_CATCH or PJ_CATCH_ANY nor use PJ_CATCH * and PJ_CATCH_ANY for a single PJ_TRY. * - Exceptions will always be caught by the first handler (unlike C++ where * exception is only caught if the type matches. * \section PJ_EX_KEYWORDS Keywords * * \subsection PJ_THROW PJ_THROW(expression) * Throw an exception. The expression thrown is an integer as the result of * the \a expression. This keyword can be specified anywhere within the * program. * * \subsection PJ_USE_EXCEPTION PJ_USE_EXCEPTION * Specify this in the variable definition section of the function block * (or any blocks) to specify that the block has \a PJ_TRY/PJ_CATCH exception * block. * Actually, this is just a macro to declare local variable which is used to * push the exception state to the exception stack. * Note: you must specify PJ_USE_EXCEPTION as the last statement in the * local variable declarations, since it may evaluate to nothing. * * \subsection PJ_TRY PJ_TRY * The \a PJ_TRY keyword is typically followed by a block. If an exception is * thrown in this block, then the execution will resume to the \a PJ_CATCH * handler. * * \subsection PJ_CATCH PJ_CATCH(expression) * The \a PJ_CATCH is normally followed by a block. This block will be executed * if the exception being thrown is equal to the expression specified in the * \a PJ_CATCH. * * \subsection PJ_CATCH_ANY PJ_CATCH_ANY * The \a PJ_CATCH is normally followed by a block. This block will be executed * if any exception was raised in the TRY block. * * \subsection PJ_END PJ_END * Specify this keyword to mark the end of \a PJ_TRY / \a PJ_CATCH blocks. * * \subsection PJ_GET_EXCEPTION PJ_GET_EXCEPTION(void) * Get the last exception thrown. This macro is normally called inside the * \a PJ_CATCH or \a PJ_CATCH_ANY block, altough it can be used anywhere where * the \a PJ_USE_EXCEPTION definition is in scope. * * * \section pj_except_examples_sec Examples * * For some examples on how to use the exception construct, please see: * - @ref page_pjlib_samples_except_c * - @ref page_pjlib_exception_test */ /** * Allocate a unique exception id. * Applications don't have to allocate a unique exception ID before using * the exception construct. However, by doing so it ensures that there is * no collisions of exception ID. * * As a bonus, when exception number is acquired through this function, * the library can assign name to the exception (only if * PJ_HAS_EXCEPTION_NAMES is enabled (default is yes)) and find out the * exception name when it catches an exception. * * @param name Name to be associated with the exception ID. * @param id Pointer to receive the ID. * * @return PJ_SUCCESS on success or PJ_ETOOMANY if the library * is running out out ids. */ PJ_DECL(pj_status_t) pj_exception_id_alloc(const char *name, pj_exception_id_t *id); /** * Free an exception id. * * @param id The exception ID. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_exception_id_free(pj_exception_id_t id); /** * Retrieve name associated with the exception id. * * @param id The exception ID. * * @return The name associated with the specified ID. */ PJ_DECL(const char*) pj_exception_id_name(pj_exception_id_t id); /** @} */ #if defined(PJ_EXCEPTION_USE_WIN32_SEH) && PJ_EXCEPTION_USE_WIN32_SEH != 0 /***************************************************************************** ** ** IMPLEMENTATION OF EXCEPTION USING WINDOWS SEH ** ****************************************************************************/ #define WIN32_LEAN_AND_MEAN #include PJ_IDECL_NO_RETURN(void) pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN { RaiseException(id,1,0,NULL); } #define PJ_USE_EXCEPTION #define PJ_TRY __try #define PJ_CATCH(id) __except(GetExceptionCode()==id ? \ EXCEPTION_EXECUTE_HANDLER : \ EXCEPTION_CONTINUE_SEARCH) #define PJ_CATCH_ANY __except(EXCEPTION_EXECUTE_HANDLER) #define PJ_END #define PJ_THROW(id) pj_throw_exception_(id) #define PJ_GET_EXCEPTION() GetExceptionCode() #elif defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0 /***************************************************************************** ** ** IMPLEMENTATION OF EXCEPTION USING SYMBIAN LEAVE/TRAP FRAMEWORK ** ****************************************************************************/ /* To include this file, the source file must be compiled as * C++ code! */ #ifdef __cplusplus class TPjException { public: int code_; }; #define PJ_USE_EXCEPTION #define PJ_TRY try //#define PJ_CATCH(id) #define PJ_CATCH_ANY catch (const TPjException & pj_excp_) #define PJ_END #define PJ_THROW(x_id) do { TPjException e; e.code_=x_id; throw e;} \ while (0) #define PJ_GET_EXCEPTION() pj_excp_.code_ #else #define PJ_USE_EXCEPTION #define PJ_TRY #define PJ_CATCH_ANY if (0) #define PJ_END #define PJ_THROW(x_id) do { PJ_LOG(1,("PJ_THROW"," error code = %d",x_id)); } while (0) #define PJ_GET_EXCEPTION() 0 #endif /* __cplusplus */ #else /***************************************************************************** ** ** IMPLEMENTATION OF EXCEPTION USING GENERIC SETJMP/LONGJMP ** ****************************************************************************/ /** * This structure (which should be invisible to user) manages the TRY handler * stack. */ struct pj_exception_state_t { pj_jmp_buf state; /**< jmp_buf. */ struct pj_exception_state_t *prev; /**< Previous state in the list. */ }; /** * Throw exception. * @param id Exception Id. */ PJ_DECL_NO_RETURN(void) pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN; /** * Push exception handler. */ PJ_DECL(void) pj_push_exception_handler_(struct pj_exception_state_t *rec); /** * Pop exception handler. */ PJ_DECL(void) pj_pop_exception_handler_(struct pj_exception_state_t *rec); /** * Declare that the function will use exception. * @hideinitializer */ #define PJ_USE_EXCEPTION struct pj_exception_state_t pj_x_except__; int pj_x_code__ /** * Start exception specification block. * @hideinitializer */ #define PJ_TRY if (1) { \ pj_push_exception_handler_(&pj_x_except__); \ pj_x_code__ = pj_setjmp(pj_x_except__.state); \ if (pj_x_code__ == 0) /** * Catch the specified exception Id. * @param id The exception number to catch. * @hideinitializer */ #define PJ_CATCH(id) else if (pj_x_code__ == (id)) /** * Catch any exception number. * @hideinitializer */ #define PJ_CATCH_ANY else /** * End of exception specification block. * @hideinitializer */ #define PJ_END pj_pop_exception_handler_(&pj_x_except__); \ } else {} /** * Throw exception. * @param exception_id The exception number. * @hideinitializer */ #define PJ_THROW(exception_id) pj_throw_exception_(exception_id) /** * Get current exception. * @return Current exception code. * @hideinitializer */ #define PJ_GET_EXCEPTION() (pj_x_code__) #endif /* PJ_EXCEPTION_USE_WIN32_SEH */ PJ_END_DECL #endif /* __PJ_EXCEPTION_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/fifobuf.h ================================================ /* $Id: fifobuf.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_FIFOBUF_H__ #define __PJ_FIFOBUF_H__ #include PJ_BEGIN_DECL typedef struct pj_fifobuf_t pj_fifobuf_t; struct pj_fifobuf_t { char *first, *last; char *ubegin, *uend; int full; }; PJ_DECL(void) pj_fifobuf_init (pj_fifobuf_t *fb, void *buffer, unsigned size); PJ_DECL(unsigned) pj_fifobuf_max_size (pj_fifobuf_t *fb); PJ_DECL(void*) pj_fifobuf_alloc (pj_fifobuf_t *fb, unsigned size); PJ_DECL(pj_status_t) pj_fifobuf_unalloc (pj_fifobuf_t *fb, void *buf); PJ_DECL(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fb, void *buf); PJ_END_DECL #endif /* __PJ_FIFOBUF_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/file_access.h ================================================ /* $Id: file_access.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_FILE_ACCESS_H__ #define __PJ_FILE_ACCESS_H__ /** * @file file_access.h * @brief File manipulation and access. */ #include PJ_BEGIN_DECL /** * @defgroup PJ_FILE_ACCESS File Access * @ingroup PJ_IO * @{ * */ /** * This structure describes file information, to be obtained by * calling #pj_file_getstat(). The time information in this structure * is in local time. */ typedef struct pj_file_stat { pj_off_t size; /**< Total file size. */ pj_time_val atime; /**< Time of last access. */ pj_time_val mtime; /**< Time of last modification. */ pj_time_val ctime; /**< Time of last creation. */ } pj_file_stat; /** * Returns non-zero if the specified file exists. * * @param filename The file name. * * @return Non-zero if the file exists. */ PJ_DECL(pj_bool_t) pj_file_exists(const char *filename); /** * Returns the size of the file. * * @param filename The file name. * * @return The file size in bytes or -1 on error. */ PJ_DECL(pj_off_t) pj_file_size(const char *filename); /** * Delete a file. * * @param filename The filename. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_file_delete(const char *filename); /** * Move a \c oldname to \c newname. If \c newname already exists, * it will be overwritten. * * @param oldname The file to rename. * @param newname New filename to assign. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_file_move( const char *oldname, const char *newname); /** * Return information about the specified file. The time information in * the \c stat structure will be in local time. * * @param filename The filename. * @param stat Pointer to variable to receive file information. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_file_getstat(const char *filename, pj_file_stat *stat); /** @} */ PJ_END_DECL #endif /* __PJ_FILE_ACCESS_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/file_io.h ================================================ /* $Id: file_io.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_FILE_IO_H__ #define __PJ_FILE_IO_H__ /** * @file file_io.h * @brief Simple file I/O abstraction. */ #include PJ_BEGIN_DECL /** * @defgroup PJ_FILE_IO File I/O * @ingroup PJ_IO * @{ * * This file contains functionalities to perform file I/O. The file * I/O can be implemented with various back-end, either using native * file API or ANSI stream. * * @section pj_file_size_limit_sec Size Limits * * There may be limitation on the size that can be handled by the * #pj_file_setpos() or #pj_file_getpos() functions. The API itself * uses 64-bit integer for the file offset/position (where available); * however some backends (such as ANSI) may only support signed 32-bit * offset resolution. * * Reading and writing operation uses signed 32-bit integer to indicate * the size. * * */ /** * These enumerations are used when opening file. Values PJ_O_RDONLY, * PJ_O_WRONLY, and PJ_O_RDWR are mutually exclusive. Value PJ_O_APPEND * can only be used when the file is opened for writing. */ enum pj_file_access { PJ_O_RDONLY = 0x1101, /**< Open file for reading. */ PJ_O_WRONLY = 0x1102, /**< Open file for writing. */ PJ_O_RDWR = 0x1103, /**< Open file for reading and writing. File will be truncated. */ PJ_O_APPEND = 0x1108 /**< Append to existing file. */ }; /** * The seek directive when setting the file position with #pj_file_setpos. */ enum pj_file_seek_type { PJ_SEEK_SET = 0x1201, /**< Offset from beginning of the file. */ PJ_SEEK_CUR = 0x1202, /**< Offset from current position. */ PJ_SEEK_END = 0x1203 /**< Size of the file plus offset. */ }; /** * Open the file as specified in \c pathname with the specified * mode, and return the handle in \c fd. All files will be opened * as binary. * * @param pool Pool to allocate memory for the new file descriptor. * @param pathname The file name to open. * @param flags Open flags, which is bitmask combination of * #pj_file_access enum. The flag must be either * PJ_O_RDONLY, PJ_O_WRONLY, or PJ_O_RDWR. When file * writing is specified, existing file will be * truncated unless PJ_O_APPEND is specified. * @param fd The returned descriptor. * * @return PJ_SUCCESS or the appropriate error code on error. */ PJ_DECL(pj_status_t) pj_file_open(pj_pool_t *pool, const char *pathname, unsigned flags, pj_oshandle_t *fd); /** * Close an opened file descriptor. * * @param fd The file descriptor. * * @return PJ_SUCCESS or the appropriate error code on error. */ PJ_DECL(pj_status_t) pj_file_close(pj_oshandle_t fd); /** * Write data with the specified size to an opened file. * * @param fd The file descriptor. * @param data Data to be written to the file. * @param size On input, specifies the size of data to be written. * On return, it contains the number of data actually * written to the file. * * @return PJ_SUCCESS or the appropriate error code on error. */ PJ_DECL(pj_status_t) pj_file_write(pj_oshandle_t fd, const void *data, pj_ssize_t *size); /** * Read data from the specified file. When end-of-file condition is set, * this function will return PJ_SUCCESS but the size will contain zero. * * @param fd The file descriptor. * @param data Pointer to buffer to receive the data. * @param size On input, specifies the maximum number of data to * read from the file. On output, it contains the size * of data actually read from the file. It will contain * zero when EOF occurs. * * @return PJ_SUCCESS or the appropriate error code on error. * When EOF occurs, the return is PJ_SUCCESS but size * will report zero. */ PJ_DECL(pj_status_t) pj_file_read(pj_oshandle_t fd, void *data, pj_ssize_t *size); /** * Set file position to new offset according to directive \c whence. * * @param fd The file descriptor. * @param offset The new file position to set. * @param whence The directive. * * @return PJ_SUCCESS or the appropriate error code on error. */ PJ_DECL(pj_status_t) pj_file_setpos(pj_oshandle_t fd, pj_off_t offset, enum pj_file_seek_type whence); /** * Get current file position. * * @param fd The file descriptor. * @param pos On return contains the file position as measured * from the beginning of the file. * * @return PJ_SUCCESS or the appropriate error code on error. */ PJ_DECL(pj_status_t) pj_file_getpos(pj_oshandle_t fd, pj_off_t *pos); /** * Flush file buffers. * * @param fd The file descriptor. * * @return PJ_SUCCESS or the appropriate error code on error. */ PJ_DECL(pj_status_t) pj_file_flush(pj_oshandle_t fd); /** @} */ PJ_END_DECL #endif /* __PJ_FILE_IO_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/guid.h ================================================ /* $Id: guid.h 4208 2012-07-18 07:52:33Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_GUID_H__ #define __PJ_GUID_H__ /** * @file guid.h * @brief GUID Globally Unique Identifier. */ #include PJ_BEGIN_DECL /** * @defgroup PJ_DS Data Structure. */ /** * @defgroup PJ_GUID Globally Unique Identifier * @ingroup PJ_DS * @{ * * This module provides API to create string that is globally unique. * If application doesn't require that strong requirement, it can just * use #pj_create_random_string() instead. */ /** * PJ_GUID_STRING_LENGTH specifies length of GUID string. The value is * dependent on the algorithm used internally to generate the GUID string. * If real GUID generator is used, then the length will be between 32 and * 36 bytes. Application should not assume which algorithm will * be used by GUID generator. * * Regardless of the actual length of the GUID, it will not exceed * PJ_GUID_MAX_LENGTH characters. * * @see pj_GUID_STRING_LENGTH() * @see PJ_GUID_MAX_LENGTH */ PJ_DECL_DATA(const unsigned) PJ_GUID_STRING_LENGTH; /** * Get #PJ_GUID_STRING_LENGTH constant. */ PJ_DECL(unsigned) pj_GUID_STRING_LENGTH(void); /** * PJ_GUID_MAX_LENGTH specifies the maximum length of GUID string, * regardless of which algorithm to use. */ #define PJ_GUID_MAX_LENGTH 36 /** * Create a globally unique string, which length is PJ_GUID_STRING_LENGTH * characters. Caller is responsible for preallocating the storage used * in the string. * * @param str The string to store the result. * * @return The string. */ PJ_DECL(pj_str_t*) pj_generate_unique_string(pj_str_t *str); /** * Create a globally unique string in lowercase, which length is * PJ_GUID_STRING_LENGTH characters. Caller is responsible for preallocating * the storage used in the string. * * @param str The string to store the result. * * @return The string. */ PJ_DECL(pj_str_t*) pj_generate_unique_string_lower(pj_str_t *str); /** * Generate a unique string. * * @param pool Pool to allocate memory from. * @param str The string. */ PJ_DECL(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str); /** * Generate a unique string in lowercase. * * @param pool Pool to allocate memory from. * @param str The string. */ PJ_DECL(void) pj_create_unique_string_lower(pj_pool_t *pool, pj_str_t *str); /** * @} */ PJ_END_DECL #endif/* __PJ_GUID_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/hash.h ================================================ /* $Id: hash.h 4208 2012-07-18 07:52:33Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_HASH_H__ #define __PJ_HASH_H__ /** * @file hash.h * @brief Hash Table. */ #include PJ_BEGIN_DECL /** * @defgroup PJ_HASH Hash Table * @ingroup PJ_DS * @{ * A hash table is a dictionary in which keys are mapped to array positions by * hash functions. Having the keys of more than one item map to the same * position is called a collision. In this library, we will chain the nodes * that have the same key in a list. */ /** * If this constant is used as keylen, then the key is interpreted as * NULL terminated string. */ #define PJ_HASH_KEY_STRING ((unsigned)-1) /** * This indicates the size of of each hash entry. */ #define PJ_HASH_ENTRY_BUF_SIZE (3*sizeof(void*) + 2*sizeof(pj_uint32_t)) /** * Type declaration for entry buffer, used by #pj_hash_set_np() */ typedef void *pj_hash_entry_buf[(PJ_HASH_ENTRY_BUF_SIZE+sizeof(void*)-1)/(sizeof(void*))]; /** * This is the function that is used by the hash table to calculate hash value * of the specified key. * * @param hval the initial hash value, or zero. * @param key the key to calculate. * @param keylen the length of the key, or PJ_HASH_KEY_STRING to treat * the key as null terminated string. * * @return the hash value. */ PJ_DECL(pj_uint32_t) pj_hash_calc(pj_uint32_t hval, const void *key, unsigned keylen); /** * Convert the key to lowercase and calculate the hash value. The resulting * string is stored in \c result. * * @param hval The initial hash value, normally zero. * @param result Optional. Buffer to store the result, which must be enough * to hold the string. * @param key The input key to be converted and calculated. * * @return The hash value. */ PJ_DECL(pj_uint32_t) pj_hash_calc_tolower(pj_uint32_t hval, char *result, const pj_str_t *key); /** * Create a hash table with the specified 'bucket' size. * * @param pool the pool from which the hash table will be allocated from. * @param size the bucket size, which will be round-up to the nearest 2^n-1 * * @return the hash table. */ PJ_DECL(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size); /** * Get the value associated with the specified key. * * @param ht the hash table. * @param key the key to look for. * @param keylen the length of the key, or PJ_HASH_KEY_STRING to use the * string length of the key. * @param hval if this argument is not NULL and the value is not zero, * the value will be used as the computed hash value. If * the argument is not NULL and the value is zero, it will * be filled with the computed hash upon return. * * @return the value associated with the key, or NULL if the key is not found. */ PJ_DECL(void *) pj_hash_get( pj_hash_table_t *ht, const void *key, unsigned keylen, pj_uint32_t *hval ); /** * Variant of #pj_hash_get() with the key being converted to lowercase when * calculating the hash value. * * @see pj_hash_get() */ PJ_DECL(void *) pj_hash_get_lower( pj_hash_table_t *ht, const void *key, unsigned keylen, pj_uint32_t *hval ); /** * Associate/disassociate a value with the specified key. If value is not * NULL and entry already exists, the entry's value will be overwritten. * If value is not NULL and entry does not exist, a new one will be created * with the specified pool. Otherwise if value is NULL, entry will be * deleted if it exists. * * @param pool the pool to allocate the new entry if a new entry has to be * created. * @param ht the hash table. * @param key the key. If pool is not specified, the key MUST point to * buffer that remains valid for the duration of the entry. * @param keylen the length of the key, or PJ_HASH_KEY_STRING to use the * string length of the key. * @param hval if the value is not zero, then the hash table will use * this value to search the entry's index, otherwise it will * compute the key. This value can be obtained when calling * #pj_hash_get(). * @param value value to be associated, or NULL to delete the entry with * the specified key. */ PJ_DECL(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht, const void *key, unsigned keylen, pj_uint32_t hval, void *value ); /** * Variant of #pj_hash_set() with the key being converted to lowercase when * calculating the hash value. * * @see pj_hash_set() */ PJ_DECL(void) pj_hash_set_lower( pj_pool_t *pool, pj_hash_table_t *ht, const void *key, unsigned keylen, pj_uint32_t hval, void *value ); /** * Associate/disassociate a value with the specified key. This function works * like #pj_hash_set(), except that it doesn't use pool (hence the np -- no * pool suffix). If new entry needs to be allocated, it will use the entry_buf. * * @param ht the hash table. * @param key the key. * @param keylen the length of the key, or PJ_HASH_KEY_STRING to use the * string length of the key. * @param hval if the value is not zero, then the hash table will use * this value to search the entry's index, otherwise it will * compute the key. This value can be obtained when calling * #pj_hash_get(). * @param entry_buf Buffer which will be used for the new entry, when one needs * to be created. * @param value value to be associated, or NULL to delete the entry with * the specified key. */ PJ_DECL(void) pj_hash_set_np(pj_hash_table_t *ht, const void *key, unsigned keylen, pj_uint32_t hval, pj_hash_entry_buf entry_buf, void *value); /** * Variant of #pj_hash_set_np() with the key being converted to lowercase * when calculating the hash value. * * @see pj_hash_set_np() */ PJ_DECL(void) pj_hash_set_np_lower(pj_hash_table_t *ht, const void *key, unsigned keylen, pj_uint32_t hval, pj_hash_entry_buf entry_buf, void *value); /** * Get the total number of entries in the hash table. * * @param ht the hash table. * * @return the number of entries in the hash table. */ PJ_DECL(unsigned) pj_hash_count( pj_hash_table_t *ht ); /** * Get the iterator to the first element in the hash table. * * @param ht the hash table. * @param it the iterator for iterating hash elements. * * @return the iterator to the hash element, or NULL if no element presents. */ PJ_DECL(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht, pj_hash_iterator_t *it ); /** * Get the next element from the iterator. * * @param ht the hash table. * @param it the hash iterator. * * @return the next iterator, or NULL if there's no more element. */ PJ_DECL(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht, pj_hash_iterator_t *it ); /** * Get the value associated with a hash iterator. * * @param ht the hash table. * @param it the hash iterator. * * @return the value associated with the current element in iterator. */ PJ_DECL(void*) pj_hash_this( pj_hash_table_t *ht, pj_hash_iterator_t *it ); /** * @} */ PJ_END_DECL #endif ================================================ FILE: deps/pjsip/pjlib/include/pj/ioqueue.h ================================================ /* $Id: ioqueue.h 4359 2013-02-21 11:18:36Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_IOQUEUE_H__ #define __PJ_IOQUEUE_H__ /** * @file ioqueue.h * @brief I/O Dispatching Mechanism */ #include PJ_BEGIN_DECL /** * @defgroup PJ_IO Input/Output * @brief Input/Output * @ingroup PJ_OS * * This section contains API building blocks to perform network I/O and * communications. If provides: * - @ref PJ_SOCK *\n * A highly portable socket abstraction, runs on all kind of * network APIs such as standard BSD socket, Windows socket, Linux * \b kernel socket, PalmOS networking API, etc. * * - @ref pj_addr_resolve *\n * Portable address resolution, which implements #pj_gethostbyname(). * * - @ref PJ_SOCK_SELECT *\n * A portable \a select() like API (#pj_sock_select()) which can be * implemented with various back-ends. * * - @ref PJ_IOQUEUE *\n * Framework for dispatching network events. * * For more information see the modules below. */ /** * @defgroup PJ_IOQUEUE IOQueue: I/O Event Dispatching with Proactor Pattern * @ingroup PJ_IO * @{ * * I/O Queue provides API for performing asynchronous I/O operations. It * conforms to proactor pattern, which allows application to submit an * asynchronous operation and to be notified later when the operation has * completed. * * The I/O Queue can work on both socket and file descriptors. For * asynchronous file operations however, one must make sure that the correct * file I/O back-end is used, because not all file I/O back-end can be * used with the ioqueue. Please see \ref PJ_FILE_IO for more details. * * The framework works natively in platforms where asynchronous operation API * exists, such as in Windows NT with IoCompletionPort/IOCP. In other * platforms, the I/O queue abstracts the operating system's event poll API * to provide semantics similar to IoCompletionPort with minimal penalties * (i.e. per ioqueue and per handle mutex protection). * * The I/O queue provides more than just unified abstraction. It also: * - makes sure that the operation uses the most effective way to utilize * the underlying mechanism, to achieve the maximum theoritical * throughput possible on a given platform. * - choose the most efficient mechanism for event polling on a given * platform. * * Currently, the I/O Queue is implemented using: * - select(), as the common denominator, but the least * efficient. Also the number of descriptor is limited to * \c PJ_IOQUEUE_MAX_HANDLES (which by default is 64). * - /dev/epoll on Linux (user mode and kernel mode), * a much faster replacement for select() on Linux (and more importantly * doesn't have limitation on number of descriptors). * - I/O Completion ports on Windows NT/2000/XP, which is the most * efficient way to dispatch events in Windows NT based OSes, and most * importantly, it doesn't have the limit on how many handles to monitor. * And it works with files (not only sockets) as well. * * * \section pj_ioqueue_concurrency_sec Concurrency Rules * * The ioqueue has been fine tuned to allow multiple threads to poll the * handles simultaneously, to maximize scalability when the application is * running on multiprocessor systems. When more than one threads are polling * the ioqueue and there are more than one handles are signaled, more than * one threads will execute the callback simultaneously to serve the events. * These parallel executions are completely safe when the events happen for * two different handles. * * However, with multithreading, care must be taken when multiple events * happen on the same handle, or when event is happening on a handle (and * the callback is being executed) and application is performing * unregistration to the handle at the same time. * * The treatments of above scenario differ according to the concurrency * setting that are applied to the handle. * * \subsection pj_ioq_concur_set Concurrency Settings for Handles * * Concurrency can be set on per handle (key) basis, by using * #pj_ioqueue_set_concurrency() function. The default key concurrency value * for the handle is inherited from the key concurrency setting of the ioqueue, * and the key concurrency setting for the ioqueue can be changed by using * #pj_ioqueue_set_default_concurrency(). The default key concurrency setting * for ioqueue itself is controlled by compile time setting * PJ_IOQUEUE_DEFAULT_ALLOW_CONCURRENCY. * * Note that this key concurrency setting only controls whether multiple * threads are allowed to operate on the same key at the same time. * The ioqueue itself always allows multiple threads to enter the ioqeuue at * the same time, and also simultaneous callback calls to differrent * keys is always allowed regardless to the key concurrency setting. * * \subsection pj_ioq_parallel Parallel Callback Executions for the Same Handle * * Note that when key concurrency is enabled (i.e. parallel callback calls on * the same key is allowed; this is the default setting), the ioqueue will only * perform simultaneous callback executions on the same key when the key has * invoked multiple pending operations. This could be done for example by * calling #pj_ioqueue_recvfrom() more than once on the same key, each with * the same key but different operation key (pj_ioqueue_op_key_t). With this * scenario, when multiple packets arrive on the key at the same time, more * than one threads may execute the callback simultaneously, each with the * same key but different operation key. * * When there is only one pending operation on the key (e.g. there is only one * #pj_ioqueue_recvfrom() invoked on the key), then events occuring to the * same key will be queued by the ioqueue, thus no simultaneous callback calls * will be performed. * * \subsection pj_ioq_allow_concur Concurrency is Enabled (Default Value) * * The default setting for the ioqueue is to allow multiple threads to * execute callbacks for the same handle/key. This setting is selected to * promote good performance and scalability for application. * * However this setting has a major drawback with regard to synchronization, * and application MUST carefully follow the following guidelines to ensure * that parallel access to the key does not cause problems: * * - Always note that callback may be called simultaneously for the same * key. * - Care must be taken when unregistering a key from the * ioqueue. Application must take care that when one thread is issuing * an unregistration, other thread is not simultaneously invoking the * callback to the same key. *\n * This happens because the ioqueue functions are working with a pointer * to the key, and there is a possible race condition where the pointer * has been rendered invalid by other threads before the ioqueue has a * chance to acquire mutex on it. * * \subsection pj_ioq_disallow_concur Concurrency is Disabled * * Alternatively, application may disable key concurrency to make * synchronization easier. As noted above, there are three ways to control * key concurrency setting: * - by controlling on per handle/key basis, with #pj_ioqueue_set_concurrency(). * - by changing default key concurrency setting on the ioqueue, with * #pj_ioqueue_set_default_concurrency(). * - by changing the default concurrency on compile time, by declaring * PJ_IOQUEUE_DEFAULT_ALLOW_CONCURRENCY macro to zero in your config_site.h * * \section pj_ioqeuue_examples_sec Examples * * For some examples on how to use the I/O Queue, please see: * * - \ref page_pjlib_ioqueue_tcp_test * - \ref page_pjlib_ioqueue_udp_test * - \ref page_pjlib_ioqueue_perf_test */ /** * This structure describes operation specific key to be submitted to * I/O Queue when performing the asynchronous operation. This key will * be returned to the application when completion callback is called. * * Application normally wants to attach it's specific data in the * \c user_data field so that it can keep track of which operation has * completed when the callback is called. Alternatively, application can * also extend this struct to include its data, because the pointer that * is returned in the completion callback will be exactly the same as * the pointer supplied when the asynchronous function is called. */ typedef struct pj_ioqueue_op_key_t { void *internal__[32]; /**< Internal I/O Queue data. */ void *activesock_data; /**< Active socket data. */ void *user_data; /**< Application data. */ } pj_ioqueue_op_key_t; /** * This structure describes the callbacks to be called when I/O operation * completes. */ typedef struct pj_ioqueue_callback { /** * This callback is called when #pj_ioqueue_recv or #pj_ioqueue_recvfrom * completes. * * @param key The key. * @param op_key Operation key. * @param bytes_read >= 0 to indicate the amount of data read, * otherwise negative value containing the error * code. To obtain the pj_status_t error code, use * (pj_status_t code = -bytes_read). */ void (*on_read_complete)(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read); /** * This callback is called when #pj_ioqueue_send or #pj_ioqueue_sendto * completes. * * @param key The key. * @param op_key Operation key. * @param bytes_sent >= 0 to indicate the amount of data written, * otherwise negative value containing the error * code. To obtain the pj_status_t error code, use * (pj_status_t code = -bytes_sent). */ void (*on_write_complete)(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_sent); /** * This callback is called when #pj_ioqueue_accept completes. * * @param key The key. * @param op_key Operation key. * @param sock Newly connected socket. * @param status Zero if the operation completes successfully. */ void (*on_accept_complete)(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t sock, pj_status_t status); /** * This callback is called when #pj_ioqueue_connect completes. * * @param key The key. * @param status PJ_SUCCESS if the operation completes successfully. */ void (*on_connect_complete)(pj_ioqueue_key_t *key, pj_status_t status); } pj_ioqueue_callback; /** * Types of pending I/O Queue operation. This enumeration is only used * internally within the ioqueue. */ typedef enum pj_ioqueue_operation_e { PJ_IOQUEUE_OP_NONE = 0, /**< No operation. */ PJ_IOQUEUE_OP_READ = 1, /**< read() operation. */ PJ_IOQUEUE_OP_RECV = 2, /**< recv() operation. */ PJ_IOQUEUE_OP_RECV_FROM = 4, /**< recvfrom() operation. */ PJ_IOQUEUE_OP_WRITE = 8, /**< write() operation. */ PJ_IOQUEUE_OP_SEND = 16, /**< send() operation. */ PJ_IOQUEUE_OP_SEND_TO = 32, /**< sendto() operation. */ #if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 PJ_IOQUEUE_OP_ACCEPT = 64, /**< accept() operation. */ PJ_IOQUEUE_OP_CONNECT = 128 /**< connect() operation. */ #endif /* PJ_HAS_TCP */ } pj_ioqueue_operation_e; /** * This macro specifies the maximum number of events that can be * processed by the ioqueue on a single poll cycle, on implementation * that supports it. The value is only meaningfull when specified * during PJLIB build. */ #ifndef PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL # define PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL (16) #endif /** * This macro specifies the maximum event candidates collected by each * polling thread to be able to reach maximum number of processed events * (i.e: PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL) in each poll cycle. * An event candidate will be dispatched to application as event unless * it is already being dispatched by other polling thread. So in order to * anticipate such race condition, each poll operation should collects its * event candidates more than PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL, the * recommended value is (PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL * * number of polling threads). * * The value is only meaningfull when specified during PJLIB build and * is only effective on multiple polling threads environment. */ #if !defined(PJ_IOQUEUE_MAX_CAND_EVENTS) || \ PJ_IOQUEUE_MAX_CAND_EVENTS < PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL # undef PJ_IOQUEUE_MAX_CAND_EVENTS # define PJ_IOQUEUE_MAX_CAND_EVENTS PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL #endif /** * When this flag is specified in ioqueue's recv() or send() operations, * the ioqueue will always mark the operation as asynchronous. */ #define PJ_IOQUEUE_ALWAYS_ASYNC ((pj_uint32_t)1 << (pj_uint32_t)31) /** * Return the name of the ioqueue implementation. * * @return Implementation name. */ PJ_DECL(const char*) pj_ioqueue_name(void); /** * Create a new I/O Queue framework. * * @param pool The pool to allocate the I/O queue structure. * @param max_fd The maximum number of handles to be supported, which * should not exceed PJ_IOQUEUE_MAX_HANDLES. * @param ioqueue Pointer to hold the newly created I/O Queue. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, pj_size_t max_fd, pj_ioqueue_t **ioqueue); /** * Destroy the I/O queue. * * @param ioque The I/O Queue to be destroyed. * * @return PJ_SUCCESS if success. */ PJ_DECL(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioque ); /** * Set the lock object to be used by the I/O Queue. This function can only * be called right after the I/O queue is created, before any handle is * registered to the I/O queue. * * Initially the I/O queue is created with non-recursive mutex protection. * Applications can supply alternative lock to be used by calling this * function. * * @param ioque The ioqueue instance. * @param lock The lock to be used by the ioqueue. * @param auto_delete In non-zero, the lock will be deleted by the ioqueue. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, pj_lock_t *lock, pj_bool_t auto_delete ); /** * Set default concurrency policy for this ioqueue. If this function is not * called, the default concurrency policy for the ioqueue is controlled by * compile time setting PJ_IOQUEUE_DEFAULT_ALLOW_CONCURRENCY. * * Note that changing the concurrency setting to the ioqueue will only affect * subsequent key registrations. To modify the concurrency setting for * individual key, use #pj_ioqueue_set_concurrency(). * * @param ioqueue The ioqueue instance. * @param allow Non-zero to allow concurrent callback calls, or * PJ_FALSE to disallow it. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_ioqueue_set_default_concurrency(pj_ioqueue_t *ioqueue, pj_bool_t allow); /** * Register a socket to the I/O queue framework. * When a socket is registered to the IOQueue, it may be modified to use * non-blocking IO. If it is modified, there is no guarantee that this * modification will be restored after the socket is unregistered. * * @param pool To allocate the resource for the specified handle, * which must be valid until the handle/key is unregistered * from I/O Queue. * @param ioque The I/O Queue. * @param sock The socket. * @param user_data User data to be associated with the key, which can be * retrieved later. * @param cb Callback to be called when I/O operation completes. * @param key Pointer to receive the key to be associated with this * socket. Subsequent I/O queue operation will need this * key. * * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, pj_ioqueue_t *ioque, pj_sock_t sock, void *user_data, const pj_ioqueue_callback *cb, pj_ioqueue_key_t **key ); /** * Variant of pj_ioqueue_register_sock() with additional group lock parameter. * If group lock is set for the key, the key will add the reference counter * when the socket is registered and decrease it when it is destroyed. */ PJ_DECL(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool, pj_ioqueue_t *ioque, pj_sock_t sock, pj_grp_lock_t *grp_lock, void *user_data, const pj_ioqueue_callback *cb, pj_ioqueue_key_t **key ); /** * Unregister from the I/O Queue framework. Caller must make sure that * the key doesn't have any pending operations before calling this function, * by calling #pj_ioqueue_is_pending() for all previously submitted * operations except asynchronous connect, and if necessary call * #pj_ioqueue_post_completion() to cancel the pending operations. * * Note that asynchronous connect operation will automatically be * cancelled during the unregistration. * * Also note that when I/O Completion Port backend is used, application * MUST close the handle immediately after unregistering the key. This is * because there is no unregistering API for IOCP. The only way to * unregister the handle from IOCP is to close the handle. * * @param key The key that was previously obtained from registration. * * @return PJ_SUCCESS on success or the error code. * * @see pj_ioqueue_is_pending */ PJ_DECL(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key ); /** * Get user data associated with an ioqueue key. * * @param key The key that was previously obtained from registration. * * @return The user data associated with the descriptor, or NULL * on error or if no data is associated with the key during * registration. */ PJ_DECL(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ); /** * Set or change the user data to be associated with the file descriptor or * handle or socket descriptor. * * @param key The key that was previously obtained from registration. * @param user_data User data to be associated with the descriptor. * @param old_data Optional parameter to retrieve the old user data. * * @return PJ_SUCCESS on success or the error code. */ PJ_DECL(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key, void *user_data, void **old_data); /** * Configure whether the ioqueue is allowed to call the key's callback * concurrently/in parallel. The default concurrency setting for the key * is controlled by ioqueue's default concurrency value, which can be * changed by calling #pj_ioqueue_set_default_concurrency(). * * If concurrency is allowed for the key, it means that if there are more * than one pending operations complete simultaneously, more than one * threads may call the key's callback at the same time. This generally * would promote good scalability for application, at the expense of more * complexity to manage the concurrent accesses in application's code. * * Alternatively application may disable the concurrent access by * setting the \a allow flag to false. With concurrency disabled, only * one thread can call the key's callback at one time. * * @param key The key that was previously obtained from registration. * @param allow Set this to non-zero to allow concurrent callback calls * and zero (PJ_FALSE) to disallow it. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_ioqueue_set_concurrency(pj_ioqueue_key_t *key, pj_bool_t allow); /** * Acquire the key's mutex. When the key's concurrency is disabled, * application may call this function to synchronize its operation * with the key's callback (i.e. this function will block until the * key's callback returns). * * @param key The key that was previously obtained from registration. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_ioqueue_lock_key(pj_ioqueue_key_t *key); /** * Try to acquire the key's mutex. When the key's concurrency is disabled, * application may call this function to synchronize its operation * with the key's callback. * * @param key The key that was previously obtained from registration. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_ioqueue_trylock_key(pj_ioqueue_key_t *key); /** * Release the lock previously acquired with pj_ioqueue_lock_key(). * * @param key The key that was previously obtained from registration. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_ioqueue_unlock_key(pj_ioqueue_key_t *key); /** * Initialize operation key. * * @param op_key The operation key to be initialied. * @param size The size of the operation key. */ PJ_DECL(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key, pj_size_t size ); /** * Check if operation is pending on the specified operation key. * The \c op_key must have been initialized with #pj_ioqueue_op_key_init() * or submitted as pending operation before, or otherwise the result * is undefined. * * @param key The key. * @param op_key The operation key, previously submitted to any of * the I/O functions and has returned PJ_EPENDING. * * @return Non-zero if operation is still pending. */ PJ_DECL(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key ); /** * Post completion status to the specified operation key and call the * appropriate callback. When the callback is called, the number of bytes * received in read/write callback or the status in accept/connect callback * will be set from the \c bytes_status parameter. * * @param key The key. * @param op_key Pending operation key. * @param bytes_status Number of bytes or status to be set. A good value * to put here is -PJ_ECANCELLED. * * @return PJ_SUCCESS if completion status has been successfully * sent. */ PJ_DECL(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_status ); #if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 /** * Instruct I/O Queue to accept incoming connection on the specified * listening socket. This function will return immediately (i.e. non-blocking) * regardless whether a connection is immediately available. If the function * can't complete immediately, the caller will be notified about the incoming * connection when it calls pj_ioqueue_poll(). If a new connection is * immediately available, the function returns PJ_SUCCESS with the new * connection; in this case, the callback WILL NOT be called. * * @param key The key which registered to the server socket. * @param op_key An operation specific key to be associated with the * pending operation, so that application can keep track of * which operation has been completed when the callback is * called. * @param new_sock Argument which contain pointer to receive the new socket * for the incoming connection. * @param local Optional argument which contain pointer to variable to * receive local address. * @param remote Optional argument which contain pointer to variable to * receive the remote address. * @param addrlen On input, contains the length of the buffer for the * address, and on output, contains the actual length of the * address. This argument is optional. * @return * - PJ_SUCCESS When connection is available immediately, and the * parameters will be updated to contain information about * the new connection. In this case, a completion callback * WILL NOT be called. * - PJ_EPENDING If no connection is available immediately. When a new * connection arrives, the callback will be called. * - non-zero which indicates the appropriate error code. */ PJ_DECL(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t *new_sock, pj_sockaddr_t *local, pj_sockaddr_t *remote, int *addrlen ); /** * Initiate non-blocking socket connect. If the socket can NOT be connected * immediately, asynchronous connect() will be scheduled and caller will be * notified via completion callback when it calls pj_ioqueue_poll(). If * socket is connected immediately, the function returns PJ_SUCCESS and * completion callback WILL NOT be called. * * @param key The key associated with TCP socket * @param addr The remote address. * @param addrlen The remote address length. * * @return * - PJ_SUCCESS If socket is connected immediately. In this case, the * completion callback WILL NOT be called. * - PJ_EPENDING If operation is queued, or * - non-zero Indicates the error code. */ PJ_DECL(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key, const pj_sockaddr_t *addr, int addrlen ); #endif /* PJ_HAS_TCP */ /** * Poll the I/O Queue for completed events. * * Note: polling the ioqueue is not necessary in Symbian. Please see * @ref PJ_SYMBIAN_OS for more info. * * @param ioque the I/O Queue. * @param timeout polling timeout, or NULL if the thread wishes to wait * indefinetely for the event. * * @return * - zero if timed out (no event). * - (<0) if error occured during polling. Callback will NOT be called. * - (>1) to indicate numbers of events. Callbacks have been called. */ PJ_DECL(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout); /** * Instruct the I/O Queue to read from the specified handle. This function * returns immediately (i.e. non-blocking) regardless whether some data has * been transferred. If the operation can't complete immediately, caller will * be notified about the completion when it calls pj_ioqueue_poll(). If data * is immediately available, the function will return PJ_SUCCESS and the * callback WILL NOT be called. * * @param key The key that uniquely identifies the handle. * @param op_key An operation specific key to be associated with the * pending operation, so that application can keep track of * which operation has been completed when the callback is * called. Caller must make sure that this key remains * valid until the function completes. * @param buffer The buffer to hold the read data. The caller MUST make sure * that this buffer remain valid until the framework completes * reading the handle. * @param length On input, it specifies the size of the buffer. If data is * available to be read immediately, the function returns * PJ_SUCCESS and this argument will be filled with the * amount of data read. If the function is pending, caller * will be notified about the amount of data read in the * callback. This parameter can point to local variable in * caller's stack and doesn't have to remain valid for the * duration of pending operation. * @param flags Recv flag. If flags has PJ_IOQUEUE_ALWAYS_ASYNC then * the function will never return PJ_SUCCESS. * * @return * - PJ_SUCCESS If immediate data has been received in the buffer. In this * case, the callback WILL NOT be called. * - PJ_EPENDING If the operation has been queued, and the callback will be * called when data has been received. * - non-zero The return value indicates the error code. */ PJ_DECL(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, void *buffer, pj_ssize_t *length, pj_uint32_t flags ); /** * This function behaves similarly as #pj_ioqueue_recv(), except that it is * normally called for socket, and the remote address will also be returned * along with the data. Caller MUST make sure that both buffer and addr * remain valid until the framework completes reading the data. * * @param key The key that uniquely identifies the handle. * @param op_key An operation specific key to be associated with the * pending operation, so that application can keep track of * which operation has been completed when the callback is * called. * @param buffer The buffer to hold the read data. The caller MUST make sure * that this buffer remain valid until the framework completes * reading the handle. * @param length On input, it specifies the size of the buffer. If data is * available to be read immediately, the function returns * PJ_SUCCESS and this argument will be filled with the * amount of data read. If the function is pending, caller * will be notified about the amount of data read in the * callback. This parameter can point to local variable in * caller's stack and doesn't have to remain valid for the * duration of pending operation. * @param flags Recv flag. If flags has PJ_IOQUEUE_ALWAYS_ASYNC then * the function will never return PJ_SUCCESS. * @param addr Optional Pointer to buffer to receive the address. * @param addrlen On input, specifies the length of the address buffer. * On output, it will be filled with the actual length of * the address. This argument can be NULL if \c addr is not * specified. * * @return * - PJ_SUCCESS If immediate data has been received. In this case, the * callback must have been called before this function * returns, and no pending operation is scheduled. * - PJ_EPENDING If the operation has been queued. * - non-zero The return value indicates the error code. */ PJ_DECL(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, void *buffer, pj_ssize_t *length, pj_uint32_t flags, pj_sockaddr_t *addr, int *addrlen); /** * Instruct the I/O Queue to write to the handle. This function will return * immediately (i.e. non-blocking) regardless whether some data has been * transferred. If the function can't complete immediately, the caller will * be notified about the completion when it calls pj_ioqueue_poll(). If * operation completes immediately and data has been transferred, the function * returns PJ_SUCCESS and the callback will NOT be called. * * @param key The key that identifies the handle. * @param op_key An operation specific key to be associated with the * pending operation, so that application can keep track of * which operation has been completed when the callback is * called. * @param data The data to send. Caller MUST make sure that this buffer * remains valid until the write operation completes. * @param length On input, it specifies the length of data to send. When * data was sent immediately, this function returns PJ_SUCCESS * and this parameter contains the length of data sent. If * data can not be sent immediately, an asynchronous operation * is scheduled and caller will be notified via callback the * number of bytes sent. This parameter can point to local * variable on caller's stack and doesn't have to remain * valid until the operation has completed. * @param flags Send flags. If flags has PJ_IOQUEUE_ALWAYS_ASYNC then * the function will never return PJ_SUCCESS. * * @return * - PJ_SUCCESS If data was immediately transferred. In this case, no * pending operation has been scheduled and the callback * WILL NOT be called. * - PJ_EPENDING If the operation has been queued. Once data base been * transferred, the callback will be called. * - non-zero The return value indicates the error code. */ PJ_DECL(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, const void *data, pj_ssize_t *length, pj_uint32_t flags ); /** * Instruct the I/O Queue to write to the handle. This function will return * immediately (i.e. non-blocking) regardless whether some data has been * transferred. If the function can't complete immediately, the caller will * be notified about the completion when it calls pj_ioqueue_poll(). If * operation completes immediately and data has been transferred, the function * returns PJ_SUCCESS and the callback will NOT be called. * * @param key the key that identifies the handle. * @param op_key An operation specific key to be associated with the * pending operation, so that application can keep track of * which operation has been completed when the callback is * called. * @param data the data to send. Caller MUST make sure that this buffer * remains valid until the write operation completes. * @param length On input, it specifies the length of data to send. When * data was sent immediately, this function returns PJ_SUCCESS * and this parameter contains the length of data sent. If * data can not be sent immediately, an asynchronous operation * is scheduled and caller will be notified via callback the * number of bytes sent. This parameter can point to local * variable on caller's stack and doesn't have to remain * valid until the operation has completed. * @param flags send flags. If flags has PJ_IOQUEUE_ALWAYS_ASYNC then * the function will never return PJ_SUCCESS. * @param addr Optional remote address. * @param addrlen Remote address length, \c addr is specified. * * @return * - PJ_SUCCESS If data was immediately written. * - PJ_EPENDING If the operation has been queued. * - non-zero The return value indicates the error code. */ PJ_DECL(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, const void *data, pj_ssize_t *length, pj_uint32_t flags, const pj_sockaddr_t *addr, int addrlen); /** * !} */ PJ_END_DECL #endif /* __PJ_IOQUEUE_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/ip_helper.h ================================================ /* $Id: ip_helper.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_IP_ROUTE_H__ #define __PJ_IP_ROUTE_H__ /** * @file ip_helper.h * @brief IP helper API */ #include PJ_BEGIN_DECL /** * @defgroup pj_ip_helper IP Interface and Routing Helper * @ingroup PJ_IO * @{ * * This module provides functions to query local host's IP interface and * routing table. */ /** * This structure describes IP routing entry. */ typedef union pj_ip_route_entry { /** IP routing entry for IP version 4 routing */ struct { pj_in_addr if_addr; /**< Local interface IP address. */ pj_in_addr dst_addr; /**< Destination IP address. */ pj_in_addr mask; /**< Destination mask. */ } ipv4; } pj_ip_route_entry; /** * Enumerate the local IP interfaces currently active in the host. * * @param af Family of the address to be retrieved. Application * may specify pj_AF_UNSPEC() to retrieve all addresses, * or pj_AF_INET() or pj_AF_INET6() to retrieve interfaces * with specific address family. * @param count On input, specify the number of entries. On output, * it will be filled with the actual number of entries. * @param ifs Array of socket addresses, which address part will * be filled with the interface address. The address * family part will be initialized with the address * family of the IP address. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_enum_ip_interface(int af, unsigned *count, pj_sockaddr ifs[]); /** * Enumerate the IP routing table for this host. * * @param count On input, specify the number of routes entries. On output, * it will be filled with the actual number of route entries. * @param routes Array of IP routing entries. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_enum_ip_route(unsigned *count, pj_ip_route_entry routes[]); /** @} */ PJ_END_DECL #endif /* __PJ_IP_ROUTE_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/list.h ================================================ /* $Id: list.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_LIST_H__ #define __PJ_LIST_H__ /** * @file list.h * @brief Linked List data structure. */ #include PJ_BEGIN_DECL /* * @defgroup PJ_DS Data Structure. */ /** * @defgroup PJ_LIST Linked List * @ingroup PJ_DS * @{ * * List in PJLIB is implemented as doubly-linked list, and it won't require * dynamic memory allocation (just as all PJLIB data structures). The list here * should be viewed more like a low level C list instead of high level C++ list * (which normally are easier to use but require dynamic memory allocations), * therefore all caveats with C list apply here too (such as you can NOT put * a node in more than one lists). * * \section pj_list_example_sec Examples * * See below for examples on how to manipulate linked list: * - @ref page_pjlib_samples_list_c * - @ref page_pjlib_list_test */ /** * Use this macro in the start of the structure declaration to declare that * the structure can be used in the linked list operation. This macro simply * declares additional member @a prev and @a next to the structure. * @hideinitializer */ #define PJ_DECL_LIST_MEMBER(type) \ /** List @a prev. */ \ type *prev; \ /** List @a next. */ \ type *next /** * This structure describes generic list node and list. The owner of this list * must initialize the 'value' member to an appropriate value (typically the * owner itself). */ struct pj_list { PJ_DECL_LIST_MEMBER(void); } PJ_ATTR_MAY_ALIAS; /* may_alias avoids warning with gcc-4.4 -Wall -O2 */ /** * Initialize the list. * Initially, the list will have no member, and function pj_list_empty() will * always return nonzero (which indicates TRUE) for the newly initialized * list. * * @param node The list head. */ PJ_INLINE(void) pj_list_init(pj_list_type * node) { ((pj_list*)node)->next = ((pj_list*)node)->prev = node; } /** * Check that the list is empty. * * @param node The list head. * * @return Non-zero if the list is empty, or zero if it is not empty. * */ PJ_INLINE(int) pj_list_empty(const pj_list_type * node) { return ((pj_list*)node)->next == node; } /** * Insert the node to the list before the specified element position. * * @param pos The element to which the node will be inserted before. * @param node The element to be inserted. * * @return void. */ PJ_IDECL(void) pj_list_insert_before(pj_list_type *pos, pj_list_type *node); /** * Insert the node to the back of the list. This is just an alias for * #pj_list_insert_before(). * * @param list The list. * @param node The element to be inserted. */ PJ_INLINE(void) pj_list_push_back(pj_list_type *list, pj_list_type *node) { pj_list_insert_before(list, node); } /** * Inserts all nodes in \a nodes to the target list. * * @param lst The target list. * @param nodes Nodes list. */ PJ_IDECL(void) pj_list_insert_nodes_before(pj_list_type *lst, pj_list_type *nodes); /** * Insert a node to the list after the specified element position. * * @param pos The element in the list which will precede the inserted * element. * @param node The element to be inserted after the position element. * * @return void. */ PJ_IDECL(void) pj_list_insert_after(pj_list_type *pos, pj_list_type *node); /** * Insert the node to the front of the list. This is just an alias for * #pj_list_insert_after(). * * @param list The list. * @param node The element to be inserted. */ PJ_INLINE(void) pj_list_push_front(pj_list_type *list, pj_list_type *node) { pj_list_insert_after(list, node); } /** * Insert all nodes in \a nodes to the target list. * * @param lst The target list. * @param nodes Nodes list. */ PJ_IDECL(void) pj_list_insert_nodes_after(pj_list_type *lst, pj_list_type *nodes); /** * Remove elements from the source list, and insert them to the destination * list. The elements of the source list will occupy the * front elements of the target list. Note that the node pointed by \a list2 * itself is not considered as a node, but rather as the list descriptor, so * it will not be inserted to the \a list1. The elements to be inserted starts * at \a list2->next. If \a list2 is to be included in the operation, use * \a pj_list_insert_nodes_before. * * @param list1 The destination list. * @param list2 The source list. * * @return void. */ PJ_IDECL(void) pj_list_merge_first(pj_list_type *list1, pj_list_type *list2); /** * Remove elements from the second list argument, and insert them to the list * in the first argument. The elements from the second list will be appended * to the first list. Note that the node pointed by \a list2 * itself is not considered as a node, but rather as the list descriptor, so * it will not be inserted to the \a list1. The elements to be inserted starts * at \a list2->next. If \a list2 is to be included in the operation, use * \a pj_list_insert_nodes_before. * * @param list1 The element in the list which will precede the inserted * element. * @param list2 The element in the list to be inserted. * * @return void. */ PJ_IDECL(void) pj_list_merge_last( pj_list_type *list1, pj_list_type *list2); /** * Erase the node from the list it currently belongs. * * @param node The element to be erased. */ PJ_IDECL(void) pj_list_erase(pj_list_type *node); /** * Find node in the list. * * @param list The list head. * @param node The node element to be searched. * * @return The node itself if it is found in the list, or NULL if it is not * found in the list. */ PJ_IDECL(pj_list_type*) pj_list_find_node(pj_list_type *list, pj_list_type *node); /** * Search the list for the specified value, using the specified comparison * function. This function iterates on nodes in the list, started with the * first node, and call the user supplied comparison function until the * comparison function returns ZERO. * * @param list The list head. * @param value The user defined value to be passed in the comparison * function * @param comp The comparison function, which should return ZERO to * indicate that the searched value is found. * * @return The first node that matched, or NULL if it is not found. */ PJ_IDECL(pj_list_type*) pj_list_search(pj_list_type *list, void *value, int (*comp)(void *value, const pj_list_type *node) ); /** * Traverse the list to get the number of elements in the list. * * @param list The list head. * * @return Number of elements. */ PJ_IDECL(pj_size_t) pj_list_size(const pj_list_type *list); /** * @} */ #if PJ_FUNCTIONS_ARE_INLINED # include "list_i.h" #endif PJ_END_DECL #endif /* __PJ_LIST_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/list_i.h ================================================ /* $Id: list_i.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Internal */ PJ_INLINE(void) pj_link_node(pj_list_type *prev, pj_list_type *next) { ((pj_list*)prev)->next = next; ((pj_list*)next)->prev = prev; } PJ_IDEF(void) pj_list_insert_after(pj_list_type *pos, pj_list_type *node) { ((pj_list*)node)->prev = pos; ((pj_list*)node)->next = ((pj_list*)pos)->next; ((pj_list*) ((pj_list*)pos)->next) ->prev = node; ((pj_list*)pos)->next = node; } PJ_IDEF(void) pj_list_insert_before(pj_list_type *pos, pj_list_type *node) { pj_list_insert_after(((pj_list*)pos)->prev, node); } PJ_IDEF(void) pj_list_insert_nodes_after(pj_list_type *pos, pj_list_type *lst) { pj_list *lst_last = (pj_list *) ((pj_list*)lst)->prev; pj_list *pos_next = (pj_list *) ((pj_list*)pos)->next; pj_link_node(pos, lst); pj_link_node(lst_last, pos_next); } PJ_IDEF(void) pj_list_insert_nodes_before(pj_list_type *pos, pj_list_type *lst) { pj_list_insert_nodes_after(((pj_list*)pos)->prev, lst); } PJ_IDEF(void) pj_list_merge_last(pj_list_type *lst1, pj_list_type *lst2) { if (!pj_list_empty(lst2)) { pj_link_node(((pj_list*)lst1)->prev, ((pj_list*)lst2)->next); pj_link_node(((pj_list*)lst2)->prev, lst1); pj_list_init(lst2); } } PJ_IDEF(void) pj_list_merge_first(pj_list_type *lst1, pj_list_type *lst2) { if (!pj_list_empty(lst2)) { pj_link_node(((pj_list*)lst2)->prev, ((pj_list*)lst1)->next); pj_link_node(((pj_list*)lst1), ((pj_list*)lst2)->next); pj_list_init(lst2); } } PJ_IDEF(void) pj_list_erase(pj_list_type *node) { pj_link_node( ((pj_list*)node)->prev, ((pj_list*)node)->next); /* It'll be safer to init the next/prev fields to itself, to * prevent multiple erase() from corrupting the list. See * ticket #520 for one sample bug. */ pj_list_init(node); } PJ_IDEF(pj_list_type*) pj_list_find_node(pj_list_type *list, pj_list_type *node) { pj_list *p = (pj_list *) ((pj_list*)list)->next; while (p != list && p != node) p = (pj_list *) p->next; return p==node ? p : NULL; } PJ_IDEF(pj_list_type*) pj_list_search(pj_list_type *list, void *value, int (*comp)(void *value, const pj_list_type *node)) { pj_list *p = (pj_list *) ((pj_list*)list)->next; while (p != list && (*comp)(value, p) != 0) p = (pj_list *) p->next; return p==list ? NULL : p; } PJ_IDEF(pj_size_t) pj_list_size(const pj_list_type *list) { const pj_list *node = (const pj_list*) ((const pj_list*)list)->next; pj_size_t count = 0; while (node != list) { ++count; node = (pj_list*)node->next; } return count; } ================================================ FILE: deps/pjsip/pjlib/include/pj/lock.h ================================================ /* $Id: lock.h 4359 2013-02-21 11:18:36Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_LOCK_H__ #define __PJ_LOCK_H__ /** * @file lock.h * @brief Higher abstraction for locking objects. */ #include PJ_BEGIN_DECL /** * @defgroup PJ_LOCK Lock Objects * @ingroup PJ_OS * @{ * * Lock Objects are higher abstraction for different lock mechanisms. * It offers the same API for manipulating different lock types (e.g. * @ref PJ_MUTEX "mutex", @ref PJ_SEM "semaphores", or null locks). * Because Lock Objects have the same API for different types of lock * implementation, it can be passed around in function arguments. As the * result, it can be used to control locking policy for a particular * feature. */ /** * Create simple, non recursive mutex lock object. * * @param pool Memory pool. * @param name Lock object's name. * @param lock Pointer to store the returned handle. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool, const char *name, pj_lock_t **lock ); /** * Create recursive mutex lock object. * * @param pool Memory pool. * @param name Lock object's name. * @param lock Pointer to store the returned handle. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool, const char *name, pj_lock_t **lock ); /** * Create NULL mutex. A NULL mutex doesn't actually have any synchronization * object attached to it. * * @param pool Memory pool. * @param name Lock object's name. * @param lock Pointer to store the returned handle. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool, const char *name, pj_lock_t **lock ); #if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 /** * Create semaphore lock object. * * @param pool Memory pool. * @param name Lock object's name. * @param initial Initial value of the semaphore. * @param max Maximum value of the semaphore. * @param lock Pointer to store the returned handle. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool, const char *name, unsigned initial, unsigned max, pj_lock_t **lock ); #endif /* PJ_HAS_SEMAPHORE */ /** * Acquire lock on the specified lock object. * * @param lock The lock object. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_lock_acquire( pj_lock_t *lock ); /** * Try to acquire lock on the specified lock object. * * @param lock The lock object. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock ); /** * Release lock on the specified lock object. * * @param lock The lock object. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_lock_release( pj_lock_t *lock ); /** * Destroy the lock object. * * @param lock The lock object. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_lock_destroy( pj_lock_t *lock ); /** @} */ /** * @defgroup PJ_GRP_LOCK Group Lock * @ingroup PJ_LOCK * @{ * * Group lock is a synchronization object to manage concurrency among members * within the same logical group. Example of such groups are: * * - dialog, which has members such as the dialog itself, an invite session, * and several transactions * - ICE, which has members such as ICE stream transport, ICE session, STUN * socket, TURN socket, and down to ioqueue key * * Group lock has three functions: * * - mutual exclusion: to protect resources from being accessed by more than * one threads at the same time * - session management: to make sure that the resource is not destroyed * while others are still using or about to use it. * - lock coordinator: to provide uniform lock ordering among more than one * lock objects, which is necessary to avoid deadlock. * * The requirements of the group lock are: * * - must satisfy all the functions above * - must allow members to join or leave the group (for example, * transaction may be added or removed from a dialog) * - must be able to synchronize with external lock (for example, a dialog * lock must be able to sync itself with PJSUA lock) * * Please see https://trac.pjsip.org/repos/wiki/Group_Lock for more info. */ /** * Settings for creating the group lock. */ typedef struct pj_grp_lock_config { /** * Creation flags, currently must be zero. */ unsigned flags; } pj_grp_lock_config; /** * Initialize the config with the default values. * * @param cfg The config to be initialized. */ PJ_DECL(void) pj_grp_lock_config_default(pj_grp_lock_config *cfg); /** * Create a group lock object. Initially the group lock will have reference * counter of one. * * @param pool The group lock only uses the pool parameter to get * the pool factory, from which it will create its own * pool. * @param cfg Optional configuration. * @param p_grp_lock Pointer to receive the newly created group lock. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_grp_lock_create(pj_pool_t *pool, const pj_grp_lock_config *cfg, pj_grp_lock_t **p_grp_lock); /** * Create a group lock object, with the specified destructor handler, to be * called by the group lock when it is about to be destroyed. Initially the * group lock will have reference counter of one. * * @param pool The group lock only uses the pool parameter to get * the pool factory, from which it will create its own * pool. * @param cfg Optional configuration. * @param member A pointer to be passed to the handler. * @param handler The destroy handler. * @param p_grp_lock Pointer to receive the newly created group lock. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_grp_lock_create_w_handler(pj_pool_t *pool, const pj_grp_lock_config *cfg, void *member, void (*handler)(void *member), pj_grp_lock_t **p_grp_lock); /** * Forcibly destroy the group lock, ignoring the reference counter value. * * @param grp_lock The group lock. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_grp_lock_destroy( pj_grp_lock_t *grp_lock); /** * Move the contents of the old lock to the new lock and destroy the * old lock. * * @param old_lock The old group lock to be destroyed. * @param new_lock The new group lock. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_grp_lock_replace(pj_grp_lock_t *old_lock, pj_grp_lock_t *new_lock); /** * Acquire lock on the specified group lock. * * @param grp_lock The group lock. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_grp_lock_acquire( pj_grp_lock_t *grp_lock); /** * Acquire lock on the specified group lock if it is available, otherwise * return immediately wihout waiting. * * @param grp_lock The group lock. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_grp_lock_tryacquire( pj_grp_lock_t *grp_lock); /** * Release the previously held lock. This may cause the group lock * to be destroyed if it is the last one to hold the reference counter. * In that case, the function will return PJ_EGONE. * * @param grp_lock The group lock. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_grp_lock_release( pj_grp_lock_t *grp_lock); /** * Add a destructor handler, to be called by the group lock when it is * about to be destroyed. * * @param grp_lock The group lock. * @param pool Pool to allocate memory for the handler. * @param member A pointer to be passed to the handler. * @param handler The destroy handler. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_grp_lock_add_handler(pj_grp_lock_t *grp_lock, pj_pool_t *pool, void *member, void (*handler)(void *member)); /** * Remove previously registered handler. All parameters must be the same * as when the handler was added. * * @param grp_lock The group lock. * @param member A pointer to be passed to the handler. * @param handler The destroy handler. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_grp_lock_del_handler(pj_grp_lock_t *grp_lock, void *member, void (*handler)(void *member)); /** * Increment reference counter to prevent the group lock grom being destroyed. * * @param grp_lock The group lock. * * @return PJ_SUCCESS or the appropriate error code. */ #if !PJ_GRP_LOCK_DEBUG PJ_DECL(pj_status_t) pj_grp_lock_add_ref(pj_grp_lock_t *grp_lock); #define pj_grp_lock_add_ref_dbg(grp_lock, x, y) pj_grp_lock_add_ref(grp_lock) #else #define pj_grp_lock_add_ref(g) pj_grp_lock_add_ref_dbg(g, __FILE__, __LINE__) PJ_DECL(pj_status_t) pj_grp_lock_add_ref_dbg(pj_grp_lock_t *grp_lock, const char *file, int line); #endif /** * Decrement the reference counter. When the counter value reaches zero, the * group lock will be destroyed and all destructor handlers will be called. * * @param grp_lock The group lock. * * @return PJ_SUCCESS or the appropriate error code. */ #if !PJ_GRP_LOCK_DEBUG PJ_DECL(pj_status_t) pj_grp_lock_dec_ref(pj_grp_lock_t *grp_lock); #define pj_grp_lock_dec_ref_dbg(grp_lock, x, y) pj_grp_lock_dec_ref(grp_lock) #else #define pj_grp_lock_dec_ref(g) pj_grp_lock_dec_ref_dbg(g, __FILE__, __LINE__) PJ_DECL(pj_status_t) pj_grp_lock_dec_ref_dbg(pj_grp_lock_t *grp_lock, const char *file, int line); #endif /** * Get current reference count value. This normally is only used for * debugging purpose. * * @param grp_lock The group lock. * * @return The reference count value. */ PJ_DECL(int) pj_grp_lock_get_ref(pj_grp_lock_t *grp_lock); /** * Dump group lock info for debugging purpose. If group lock debugging is * enabled (via PJ_GRP_LOCK_DEBUG) macro, this will print the group lock * reference counter value along with the source file and line. If * debugging is disabled, this will only print the reference counter. * * @param grp_lock The group lock. */ PJ_DECL(void) pj_grp_lock_dump(pj_grp_lock_t *grp_lock); /** * Synchronize an external lock with the group lock, by adding it to the * list of locks to be acquired by the group lock when the group lock is * acquired. * * The ''pos'' argument specifies the lock order and also the relative * position with regard to lock ordering against the group lock. Locks with * lower ''pos'' value will be locked first, and those with negative value * will be locked before the group lock (the group lock's ''pos'' value is * zero). * * @param grp_lock The group lock. * @param ext_lock The external lock * @param pos The position. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_grp_lock_chain_lock(pj_grp_lock_t *grp_lock, pj_lock_t *ext_lock, int pos); /** * Remove an external lock from group lock's list of synchronized locks. * * @param grp_lock The group lock. * @param ext_lock The external lock * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_grp_lock_unchain_lock(pj_grp_lock_t *grp_lock, pj_lock_t *ext_lock); /** @} */ PJ_END_DECL #endif /* __PJ_LOCK_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/log.h ================================================ /* $Id: log.h 3752 2011-09-18 14:38:46Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_LOG_H__ #define __PJ_LOG_H__ /** * @file log.h * @brief Logging Utility. */ #include #include PJ_BEGIN_DECL /** * @defgroup PJ_MISC Miscelaneous */ /** * @defgroup PJ_LOG Logging Facility * @ingroup PJ_MISC * @{ * * The PJLIB logging facility is a configurable, flexible, and convenient * way to write logging or trace information. * * To write to the log, one uses construct like below: * *
 *   ...
 *   PJ_LOG(3, ("main.c", "Starting hello..."));
 *   ...
 *   PJ_LOG(3, ("main.c", "Hello world from process %d", pj_getpid()));
 *   ...
 * 
* * In the above example, the number @b 3 controls the verbosity level of * the information (which means "information", by convention). The string * "main.c" specifies the source or sender of the message. * * * \section pj_log_quick_sample_sec Examples * * For examples, see: * - @ref page_pjlib_samples_log_c. * */ /** * Log decoration flag, to be specified with #pj_log_set_decor(). */ enum pj_log_decoration { PJ_LOG_HAS_DAY_NAME = 1, /**< Include day name [default: no] */ PJ_LOG_HAS_YEAR = 2, /**< Include year digit [no] */ PJ_LOG_HAS_MONTH = 4, /**< Include month [no] */ PJ_LOG_HAS_DAY_OF_MON = 8, /**< Include day of month [no] */ PJ_LOG_HAS_TIME = 16, /**< Include time [yes] */ PJ_LOG_HAS_MICRO_SEC = 32, /**< Include microseconds [yes] */ PJ_LOG_HAS_SENDER = 64, /**< Include sender in the log [yes] */ PJ_LOG_HAS_NEWLINE = 128, /**< Terminate each call with newline [yes] */ PJ_LOG_HAS_CR = 256, /**< Include carriage return [no] */ PJ_LOG_HAS_SPACE = 512, /**< Include two spaces before log [yes] */ PJ_LOG_HAS_COLOR = 1024, /**< Colorize logs [yes on win32] */ PJ_LOG_HAS_LEVEL_TEXT = 2048, /**< Include level text string [no] */ PJ_LOG_HAS_THREAD_ID = 4096, /**< Include thread identification [no] */ PJ_LOG_HAS_THREAD_SWC = 8192, /**< Add mark when thread has switched [yes]*/ PJ_LOG_HAS_INDENT =16384 /**< Indentation. Say yes! [yes] */ }; /** * Write log message. * This is the main macro used to write text to the logging backend. * * @param level The logging verbosity level. Lower number indicates higher * importance, with level zero indicates fatal error. Only * numeral argument is permitted (e.g. not variable). * @param arg Enclosed 'printf' like arguments, with the first * argument is the sender, the second argument is format * string and the following arguments are variable number of * arguments suitable for the format string. * * Sample: * \verbatim PJ_LOG(2, (__FILE__, "current value is %d", value)); \endverbatim * @hideinitializer */ #define PJ_LOG(level,arg) do { \ if (level <= pj_log_get_level()) \ pj_log_wrapper_##level(arg); \ } while (0) /** * Signature for function to be registered to the logging subsystem to * write the actual log message to some output device. * * @param level Log level. * @param data Log message, which will be NULL terminated. * @param len Message length. */ typedef void pj_log_func(int level, const char *data, int len); /** * Default logging writer function used by front end logger function. * This function will print the log message to stdout only. * Application normally should NOT need to call this function, but * rather use the PJ_LOG macro. * * @param level Log level. * @param buffer Log message. * @param len Message length. */ PJ_DECL(void) pj_log_write(int level, const char *buffer, int len); #if PJ_LOG_MAX_LEVEL >= 1 /** * Write to log. * * @param sender Source of the message. * @param level Verbosity level. * @param format Format. * @param marker Marker. */ PJ_DECL(void) pj_log(const char *sender, int level, const char *format, va_list marker); /** * Change log output function. The front-end logging functions will call * this function to write the actual message to the desired device. * By default, the front-end functions use pj_log_write() to write * the messages, unless it's changed by calling this function. * * @param func The function that will be called to write the log * messages to the desired device. */ PJ_DECL(void) pj_log_set_log_func( pj_log_func *func ); /** * Get the current log output function that is used to write log messages. * * @return Current log output function. */ PJ_DECL(pj_log_func*) pj_log_get_log_func(void); /** * Set maximum log level. Application can call this function to set * the desired level of verbosity of the logging messages. The bigger the * value, the more verbose the logging messages will be printed. However, * the maximum level of verbosity can not exceed compile time value of * PJ_LOG_MAX_LEVEL. * * @param level The maximum level of verbosity of the logging * messages (6=very detailed..1=error only, 0=disabled) */ PJ_DECL(void) pj_log_set_level(int level); /** * Get current maximum log verbositylevel. * * @return Current log maximum level. */ #if 1 PJ_DECL(int) pj_log_get_level(void); #else PJ_DECL_DATA(int) pj_log_max_level; #define pj_log_get_level() pj_log_max_level #endif /** * Set log decoration. The log decoration flag controls what are printed * to output device alongside the actual message. For example, application * can specify that date/time information should be displayed with each * log message. * * @param decor Bitmask combination of #pj_log_decoration to control * the layout of the log message. */ PJ_DECL(void) pj_log_set_decor(unsigned decor); /** * Get current log decoration flag. * * @return Log decoration flag. */ PJ_DECL(unsigned) pj_log_get_decor(void); /** * Add indentation to log message. Indentation will add PJ_LOG_INDENT_CHAR * before the message, and is useful to show the depth of function calls. * * @param indent The indentation to add or substract. Positive value * adds current indent, negative value subtracts current * indent. */ PJ_DECL(void) pj_log_add_indent(int indent); /** * Push indentation to the right by default value (PJ_LOG_INDENT). */ PJ_DECL(void) pj_log_push_indent(void); /** * Pop indentation (to the left) by default value (PJ_LOG_INDENT). */ PJ_DECL(void) pj_log_pop_indent(void); /** * Set color of log messages. * * @param level Log level which color will be changed. * @param color Desired color. */ PJ_DECL(void) pj_log_set_color(int level, pj_color_t color); /** * Get color of log messages. * * @param level Log level which color will be returned. * @return Log color. */ PJ_DECL(pj_color_t) pj_log_get_color(int level); /** * Internal function to be called by pj_init() */ pj_status_t pj_log_init(void); #else /* #if PJ_LOG_MAX_LEVEL >= 1 */ /** * Change log output function. The front-end logging functions will call * this function to write the actual message to the desired device. * By default, the front-end functions use pj_log_write() to write * the messages, unless it's changed by calling this function. * * @param func The function that will be called to write the log * messages to the desired device. */ # define pj_log_set_log_func(func) /** * Write to log. * * @param sender Source of the message. * @param level Verbosity level. * @param format Format. * @param marker Marker. */ # define pj_log(sender, level, format, marker) /** * Set maximum log level. Application can call this function to set * the desired level of verbosity of the logging messages. The bigger the * value, the more verbose the logging messages will be printed. However, * the maximum level of verbosity can not exceed compile time value of * PJ_LOG_MAX_LEVEL. * * @param level The maximum level of verbosity of the logging * messages (6=very detailed..1=error only, 0=disabled) */ # define pj_log_set_level(level) /** * Set log decoration. The log decoration flag controls what are printed * to output device alongside the actual message. For example, application * can specify that date/time information should be displayed with each * log message. * * @param decor Bitmask combination of #pj_log_decoration to control * the layout of the log message. */ # define pj_log_set_decor(decor) /** * Add indentation to log message. Indentation will add PJ_LOG_INDENT_CHAR * before the message, and is useful to show the depth of function calls. * * @param indent The indentation to add or substract. Positive value * adds current indent, negative value subtracts current * indent. */ # define pj_log_add_indent(indent) /** * Push indentation to the right by default value (PJ_LOG_INDENT). */ # define pj_log_push_indent() /** * Pop indentation (to the left) by default value (PJ_LOG_INDENT). */ # define pj_log_pop_indent() /** * Set color of log messages. * * @param level Log level which color will be changed. * @param color Desired color. */ # define pj_log_set_color(level, color) /** * Get current maximum log verbositylevel. * * @return Current log maximum level. */ # define pj_log_get_level() 0 /** * Get current log decoration flag. * * @return Log decoration flag. */ # define pj_log_get_decor() 0 /** * Get color of log messages. * * @param level Log level which color will be returned. * @return Log color. */ # define pj_log_get_color(level) 0 /** * Internal. */ # define pj_log_init() PJ_SUCCESS #endif /* #if PJ_LOG_MAX_LEVEL >= 1 */ /** * @} */ /* **************************************************************************/ /* * Log functions implementation prototypes. * These functions are called by PJ_LOG macros according to verbosity * level specified when calling the macro. Applications should not normally * need to call these functions directly. */ /** * @def pj_log_wrapper_1(arg) * Internal function to write log with verbosity 1. Will evaluate to * empty expression if PJ_LOG_MAX_LEVEL is below 1. * @param arg Log expression. */ #if PJ_LOG_MAX_LEVEL >= 1 #define pj_log_wrapper_1(arg) pj_log_1 arg /** Internal function. */ PJ_DECL(void) pj_log_1(const char *src, const char *format, ...); #else #define pj_log_wrapper_1(arg) #endif /** * @def pj_log_wrapper_2(arg) * Internal function to write log with verbosity 2. Will evaluate to * empty expression if PJ_LOG_MAX_LEVEL is below 2. * @param arg Log expression. */ #if PJ_LOG_MAX_LEVEL >= 2 #define pj_log_wrapper_2(arg) pj_log_2 arg /** Internal function. */ PJ_DECL(void) pj_log_2(const char *src, const char *format, ...); #else #define pj_log_wrapper_2(arg) #endif /** * @def pj_log_wrapper_3(arg) * Internal function to write log with verbosity 3. Will evaluate to * empty expression if PJ_LOG_MAX_LEVEL is below 3. * @param arg Log expression. */ #if PJ_LOG_MAX_LEVEL >= 3 #define pj_log_wrapper_3(arg) pj_log_3 arg /** Internal function. */ PJ_DECL(void) pj_log_3(const char *src, const char *format, ...); #else #define pj_log_wrapper_3(arg) #endif /** * @def pj_log_wrapper_4(arg) * Internal function to write log with verbosity 4. Will evaluate to * empty expression if PJ_LOG_MAX_LEVEL is below 4. * @param arg Log expression. */ #if PJ_LOG_MAX_LEVEL >= 4 #define pj_log_wrapper_4(arg) pj_log_4 arg /** Internal function. */ PJ_DECL(void) pj_log_4(const char *src, const char *format, ...); #else #define pj_log_wrapper_4(arg) #endif /** * @def pj_log_wrapper_5(arg) * Internal function to write log with verbosity 5. Will evaluate to * empty expression if PJ_LOG_MAX_LEVEL is below 5. * @param arg Log expression. */ #if PJ_LOG_MAX_LEVEL >= 5 #define pj_log_wrapper_5(arg) pj_log_5 arg /** Internal function. */ PJ_DECL(void) pj_log_5(const char *src, const char *format, ...); #else #define pj_log_wrapper_5(arg) #endif /** * @def pj_log_wrapper_6(arg) * Internal function to write log with verbosity 6. Will evaluate to * empty expression if PJ_LOG_MAX_LEVEL is below 6. * @param arg Log expression. */ #if PJ_LOG_MAX_LEVEL >= 6 #define pj_log_wrapper_6(arg) pj_log_6 arg /** Internal function. */ PJ_DECL(void) pj_log_6(const char *src, const char *format, ...); #else #define pj_log_wrapper_6(arg) #endif PJ_END_DECL #endif /* __PJ_LOG_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/math.h ================================================ /* $Id: math.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_MATH_H__ #define __PJ_MATH_H__ /** * @file math.h * @brief Mathematics and Statistics. */ #include #include PJ_BEGIN_DECL /** * @defgroup pj_math Mathematics and Statistics * @ingroup PJ_MISC * @{ * * Provides common mathematics constants and operations, and also standard * statistics calculation (min, max, mean, standard deviation). Statistics * calculation is done in realtime (statistics state is updated on time each * new sample comes). */ /** * Mathematical constants */ #define PJ_PI 3.14159265358979323846 /* pi */ #define PJ_1_PI 0.318309886183790671538 /* 1/pi */ /** * Mathematical macro */ #define PJ_ABS(x) ((x) > 0 ? (x) : -(x)) #define PJ_MAX(x, y) ((x) > (y)? (x) : (y)) #define PJ_MIN(x, y) ((x) < (y)? (x) : (y)) /** * This structure describes statistics state. */ typedef struct pj_math_stat { int n; /* number of samples */ int max; /* maximum value */ int min; /* minimum value */ int last; /* last value */ int mean; /* mean */ /* Private members */ #if PJ_HAS_FLOATING_POINT float fmean_; /* mean(floating point) */ #else int mean_res_; /* mean residu */ #endif pj_highprec_t m2_; /* variance * n */ } pj_math_stat; /** * Calculate integer square root of an integer. * * @param i Integer to be calculated. * * @return Square root result. */ PJ_INLINE(unsigned) pj_isqrt(unsigned i) { unsigned res = 1, prev; /* Rough guess, calculate half bit of input */ prev = i >> 2; while (prev) { prev >>= 2; res <<= 1; } /* Babilonian method */ do { prev = res; res = (prev + i/prev) >> 1; } while ((prev+res)>>1 != res); return res; } /** * Initialize statistics state. * * @param stat Statistic state. */ PJ_INLINE(void) pj_math_stat_init(pj_math_stat *stat) { pj_bzero(stat, sizeof(pj_math_stat)); } /** * Update statistics state as a new sample comes. * * @param stat Statistic state. * @param val The new sample data. */ PJ_INLINE(void) pj_math_stat_update(pj_math_stat *stat, int val) { #if PJ_HAS_FLOATING_POINT float delta; #else int delta; #endif stat->last = val; if (stat->n++) { if (stat->min > val) stat->min = val; if (stat->max < val) stat->max = val; } else { stat->min = stat->max = val; } #if PJ_HAS_FLOATING_POINT delta = val - stat->fmean_; stat->fmean_ += delta/stat->n; /* Return mean value with 'rounding' */ stat->mean = (int) (stat->fmean_ + 0.5); stat->m2_ += (int)(delta * (val-stat->fmean_)); #else delta = val - stat->mean; stat->mean += delta/stat->n; stat->mean_res_ += delta % stat->n; if (stat->mean_res_ >= stat->n) { ++stat->mean; stat->mean_res_ -= stat->n; } else if (stat->mean_res_ <= -stat->n) { --stat->mean; stat->mean_res_ += stat->n; } stat->m2_ += delta * (val-stat->mean); #endif } /** * Get the standard deviation of specified statistics state. * * @param stat Statistic state. * * @return The standard deviation. */ PJ_INLINE(unsigned) pj_math_stat_get_stddev(const pj_math_stat *stat) { if (stat->n == 0) return 0; return (pj_isqrt((unsigned)(stat->m2_/stat->n))); } /** * Set the standard deviation of statistics state. This is useful when * the statistic state is operated in 'read-only' mode as a storage of * statistical data. * * @param stat Statistic state. * * @param dev The standard deviation. */ PJ_INLINE(void) pj_math_stat_set_stddev(pj_math_stat *stat, unsigned dev) { if (stat->n == 0) stat->n = 1; stat->m2_ = dev*dev*stat->n; } /** @} */ PJ_END_DECL #endif /* __PJ_MATH_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/os.h ================================================ /* $Id: os.h 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_OS_H__ #define __PJ_OS_H__ /** * @file os.h * @brief OS dependent functions */ #include PJ_BEGIN_DECL /** * @defgroup PJ_OS Operating System Dependent Functionality. */ /* **************************************************************************/ /** * @defgroup PJ_SYS_INFO System Information * @ingroup PJ_OS * @{ */ /** * These enumeration contains constants to indicate support of miscellaneous * system features. These will go in "flags" field of #pj_sys_info structure. */ typedef enum pj_sys_info_flag { /** * Support for Apple iOS background feature. */ PJ_SYS_HAS_IOS_BG = 1 } pj_sys_info_flag; /** * This structure contains information about the system. Use #pj_get_sys_info() * to obtain the system information. */ typedef struct pj_sys_info { /** * Null terminated string containing processor information (e.g. "i386", * "x86_64"). It may contain empty string if the value cannot be obtained. */ pj_str_t machine; /** * Null terminated string identifying the system operation (e.g. "Linux", * "win32", "wince"). It may contain empty string if the value cannot be * obtained. */ pj_str_t os_name; /** * A number containing the operating system version number. By convention, * this field is divided into four bytes, where the highest order byte * contains the most major version of the OS, the next less significant * byte contains the less major version, and so on. How the OS version * number is mapped into these four bytes would be specific for each OS. * For example, Linux-2.6.32-28 would yield "os_ver" value of 0x0206201c, * while for Windows 7 it will be 0x06010000 (because dwMajorVersion is * 6 and dwMinorVersion is 1 for Windows 7). * * This field may contain zero if the OS version cannot be obtained. */ pj_uint32_t os_ver; /** * Null terminated string identifying the SDK name that is used to build * the library (e.g. "glibc", "uclibc", "msvc", "wince"). It may contain * empty string if the value cannot eb obtained. */ pj_str_t sdk_name; /** * A number containing the SDK version, using the numbering convention as * the "os_ver" field. The value will be zero if the version cannot be * obtained. */ pj_uint32_t sdk_ver; /** * A longer null terminated string identifying the underlying system with * as much information as possible. */ pj_str_t info; /** * Other flags containing system specific information. The value is * bitmask of #pj_sys_info_flag constants. */ pj_uint32_t flags; } pj_sys_info; /** * Obtain the system information. * * @return System information structure. */ PJ_DECL(const pj_sys_info*) pj_get_sys_info(void); /* * @} */ /* **************************************************************************/ /** * @defgroup PJ_THREAD Threads * @ingroup PJ_OS * @{ * This module provides multithreading API. * * \section pj_thread_examples_sec Examples * * For examples, please see: * - \ref page_pjlib_thread_test * - \ref page_pjlib_sleep_test * */ /** * Thread creation flags: * - PJ_THREAD_SUSPENDED: specify that the thread should be created suspended. */ typedef enum pj_thread_create_flags { PJ_THREAD_SUSPENDED = 1 } pj_thread_create_flags; /** * Type of thread entry function. */ typedef int (PJ_THREAD_FUNC pj_thread_proc)(void*); /** * Size of thread struct. */ #if !defined(PJ_THREAD_DESC_SIZE) # define PJ_THREAD_DESC_SIZE (64) #endif /** * Thread structure, to thread's state when the thread is created by external * or native API. */ typedef long pj_thread_desc[PJ_THREAD_DESC_SIZE]; /** * Get process ID. * @return process ID. */ PJ_DECL(pj_uint32_t) pj_getpid(void); /** * Create a new thread. * * @param pool The memory pool from which the thread record * will be allocated from. * @param thread_name The optional name to be assigned to the thread. * @param proc Thread entry function. * @param arg Argument to be passed to the thread entry function. * @param stack_size The size of the stack for the new thread, or ZERO or * PJ_THREAD_DEFAULT_STACK_SIZE to let the * library choose the reasonable size for the stack. * For some systems, the stack will be allocated from * the pool, so the pool must have suitable capacity. * @param flags Flags for thread creation, which is bitmask combination * from enum pj_thread_create_flags. * @param thread Pointer to hold the newly created thread. * * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name, pj_thread_proc *proc, void *arg, pj_size_t stack_size, unsigned flags, pj_thread_t **thread ); /** * Register a thread that was created by external or native API to PJLIB. * This function must be called in the context of the thread being registered. * When the thread is created by external function or API call, * it must be 'registered' to PJLIB using pj_thread_register(), so that it can * cooperate with PJLIB's framework. During registration, some data needs to * be maintained, and this data must remain available during the thread's * lifetime. * * @param thread_name The optional name to be assigned to the thread. * @param desc Thread descriptor, which must be available throughout * the lifetime of the thread. * @param thread Pointer to hold the created thread handle. * * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_thread_register ( const char *thread_name, pj_thread_desc desc, pj_thread_t **thread); /** * Check if this thread has been registered to PJLIB. * * @return Non-zero if it is registered. */ PJ_DECL(pj_bool_t) pj_thread_is_registered(void); /** * Get thread priority value for the thread. * * @param thread Thread handle. * * @return Thread priority value, or -1 on error. */ PJ_DECL(int) pj_thread_get_prio(pj_thread_t *thread); /** * Set the thread priority. The priority value must be in the priority * value range, which can be retrieved with #pj_thread_get_prio_min() and * #pj_thread_get_prio_max() functions. * * @param thread Thread handle. * @param prio New priority to be set to the thread. * * @return PJ_SUCCESS on success or the error code. */ PJ_DECL(pj_status_t) pj_thread_set_prio(pj_thread_t *thread, int prio); /** * Get the lowest priority value available for this thread. * * @param thread Thread handle. * @return Minimum thread priority value, or -1 on error. */ PJ_DECL(int) pj_thread_get_prio_min(pj_thread_t *thread); /** * Get the highest priority value available for this thread. * * @param thread Thread handle. * @return Minimum thread priority value, or -1 on error. */ PJ_DECL(int) pj_thread_get_prio_max(pj_thread_t *thread); /** * Return native handle from pj_thread_t for manipulation using native * OS APIs. * * @param thread PJLIB thread descriptor. * * @return Native thread handle. For example, when the * backend thread uses pthread, this function will * return pointer to pthread_t, and on Windows, * this function will return HANDLE. */ PJ_DECL(void*) pj_thread_get_os_handle(pj_thread_t *thread); /** * Get thread name. * * @param thread The thread handle. * * @return Thread name as null terminated string. */ PJ_DECL(const char*) pj_thread_get_name(pj_thread_t *thread); /** * Resume a suspended thread. * * @param thread The thread handle. * * @return zero on success. */ PJ_DECL(pj_status_t) pj_thread_resume(pj_thread_t *thread); /** * Get the current thread. * * @return Thread handle of current thread. */ PJ_DECL(pj_thread_t*) pj_thread_this(void); /** * Join thread, and block the caller thread until the specified thread exits. * If it is called from within the thread itself, it will return immediately * with failure status. * If the specified thread has already been dead, or it does not exist, * the function will return immediately with successful status. * * @param thread The thread handle. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pj_thread_join(pj_thread_t *thread); /** * Destroy thread and release resources allocated for the thread. * However, the memory allocated for the pj_thread_t itself will only be released * when the pool used to create the thread is destroyed. * * @param thread The thread handle. * * @return zero on success. */ PJ_DECL(pj_status_t) pj_thread_destroy(pj_thread_t *thread); /** * Put the current thread to sleep for the specified miliseconds. * * @param msec Miliseconds delay. * * @return zero if successfull. */ PJ_DECL(pj_status_t) pj_thread_sleep(unsigned msec); /** * @def PJ_CHECK_STACK() * PJ_CHECK_STACK() macro is used to check the sanity of the stack. * The OS implementation may check that no stack overflow occurs, and * it also may collect statistic about stack usage. */ #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 # define PJ_CHECK_STACK() pj_thread_check_stack(__FILE__, __LINE__) /** @internal * The implementation of stack checking. */ PJ_DECL(void) pj_thread_check_stack(const char *file, int line); /** @internal * Get maximum stack usage statistic. */ PJ_DECL(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread); /** @internal * Dump thread stack status. */ PJ_DECL(pj_status_t) pj_thread_get_stack_info(pj_thread_t *thread, const char **file, int *line); #else # define PJ_CHECK_STACK() /** pj_thread_get_stack_max_usage() for the thread */ # define pj_thread_get_stack_max_usage(thread) 0 /** pj_thread_get_stack_info() for the thread */ # define pj_thread_get_stack_info(thread,f,l) (*(f)="",*(l)=0) #endif /* PJ_OS_HAS_CHECK_STACK */ /** * @} */ /* **************************************************************************/ /** * @defgroup PJ_SYMBIAN_OS Symbian OS Specific * @ingroup PJ_OS * @{ * Functionalities specific to Symbian OS. * * Symbian OS strongly discourages the use of polling since this wastes * CPU power, and instead provides Active Object and Active Scheduler * pattern to allow application (in this case, PJLIB) to register asynchronous * tasks. PJLIB port for Symbian complies to this recommended behavior. * As the result, few things have been changed in PJLIB for Symbian: * - the timer heap (see @ref PJ_TIMER) is implemented with active * object framework, and each timer entry registered to the timer * heap will register an Active Object to the Active Scheduler. * Because of this, polling the timer heap with pj_timer_heap_poll() * is no longer necessary, and this function will just evaluate * to nothing. * - the ioqueue (see @ref PJ_IOQUEUE) is also implemented with * active object framework, with each asynchronous operation will * register an Active Object to the Active Scheduler. Because of * this, polling the ioqueue with pj_ioqueue_poll() is no longer * necessary, and this function will just evaluate to nothing. * * Since timer heap and ioqueue polling are no longer necessary, Symbian * application can now poll for all events by calling * \a User::WaitForAnyRequest() and \a CActiveScheduler::RunIfReady(). * PJLIB provides a thin wrapper which calls these two functions, * called pj_symbianos_poll(). */ /** * Wait the completion of any Symbian active objects. When the timeout * value is not specified (the \a ms_timeout argument is -1), this * function is a thin wrapper which calls \a User::WaitForAnyRequest() * and \a CActiveScheduler::RunIfReady(). If the timeout value is * specified, this function will schedule a timer entry to the timer * heap (which is an Active Object), to limit the wait time for event * occurences. Scheduling a timer entry is an expensive operation, * therefore application should only specify a timeout value when it's * really necessary (for example, when it's not sure there are other * Active Objects currently running in the application). * * @param priority The minimum priority of the Active Objects to * poll, which values are from CActive::TPriority * constants. If -1 is given, CActive::EPriorityStandard. * priority will be used. * @param ms_timeout Optional timeout to wait. Application should * specify -1 to let the function wait indefinitely * for any events. * * @return PJ_TRUE if there have been any events executed * during the polling. This function will only return * PJ_FALSE if \a ms_timeout argument is specified * (i.e. the value is not -1) and there was no event * executed when the timeout timer elapsed. */ PJ_DECL(pj_bool_t) pj_symbianos_poll(int priority, int ms_timeout); /** * This structure declares Symbian OS specific parameters that can be * specified when calling #pj_symbianos_set_params(). */ typedef struct pj_symbianos_params { /** * Optional RSocketServ instance to be used by PJLIB. If this * value is NULL, PJLIB will create a new RSocketServ instance * when pj_init() is called. */ void *rsocketserv; /** * Optional RConnection instance to be used by PJLIB when creating * sockets. If this value is NULL, no RConnection will be * specified when creating sockets. */ void *rconnection; /** * Optional RHostResolver instance to be used by PJLIB. If this value * is NULL, a new RHostResolver instance will be created when * pj_init() is called. */ void *rhostresolver; /** * Optional RHostResolver for IPv6 instance to be used by PJLIB. * If this value is NULL, a new RHostResolver instance will be created * when pj_init() is called. */ void *rhostresolver6; } pj_symbianos_params; /** * Specify Symbian OS parameters to be used by PJLIB. This function MUST * be called before #pj_init() is called. * * @param prm Symbian specific parameters. * * @return PJ_SUCCESS if the parameters can be applied * successfully. */ PJ_DECL(pj_status_t) pj_symbianos_set_params(pj_symbianos_params *prm); /** * Notify PJLIB that the access point connection has been down or unusable * and PJLIB should not try to access the Symbian socket API (especially ones * that send packets). Sending packet when RConnection is reconnected to * different access point may cause the WaitForRequest() for the function to * block indefinitely. * * @param up If set to PJ_FALSE it will cause PJLIB to not try * to access socket API, and error will be returned * immediately instead. */ PJ_DECL(void) pj_symbianos_set_connection_status(pj_bool_t up); /** * @} */ /* **************************************************************************/ /** * @defgroup PJ_TLS Thread Local Storage. * @ingroup PJ_OS * @{ */ /** * Allocate thread local storage index. The initial value of the variable at * the index is zero. * * @param index Pointer to hold the return value. * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_thread_local_alloc(long *index); /** * Deallocate thread local variable. * * @param index The variable index. */ PJ_DECL(void) pj_thread_local_free(long index); /** * Set the value of thread local variable. * * @param index The index of the variable. * @param value The value. */ PJ_DECL(pj_status_t) pj_thread_local_set(long index, void *value); /** * Get the value of thread local variable. * * @param index The index of the variable. * @return The value. */ PJ_DECL(void*) pj_thread_local_get(long index); /** * @} */ /* **************************************************************************/ /** * @defgroup PJ_ATOMIC Atomic Variables * @ingroup PJ_OS * @{ * * This module provides API to manipulate atomic variables. * * \section pj_atomic_examples_sec Examples * * For some example codes, please see: * - @ref page_pjlib_atomic_test */ /** * Create atomic variable. * * @param pool The pool. * @param initial The initial value of the atomic variable. * @param atomic Pointer to hold the atomic variable upon return. * * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_atomic_create( pj_pool_t *pool, pj_atomic_value_t initial, pj_atomic_t **atomic ); /** * Destroy atomic variable. * * @param atomic_var the atomic variable. * * @return PJ_SUCCESS if success. */ PJ_DECL(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var ); /** * Set the value of an atomic type, and return the previous value. * * @param atomic_var the atomic variable. * @param value value to be set to the variable. */ PJ_DECL(void) pj_atomic_set( pj_atomic_t *atomic_var, pj_atomic_value_t value); /** * Get the value of an atomic type. * * @param atomic_var the atomic variable. * * @return the value of the atomic variable. */ PJ_DECL(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var); /** * Increment the value of an atomic type. * * @param atomic_var the atomic variable. */ PJ_DECL(void) pj_atomic_inc(pj_atomic_t *atomic_var); /** * Increment the value of an atomic type and get the result. * * @param atomic_var the atomic variable. * * @return The incremented value. */ PJ_DECL(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var); /** * Decrement the value of an atomic type. * * @param atomic_var the atomic variable. */ PJ_DECL(void) pj_atomic_dec(pj_atomic_t *atomic_var); /** * Decrement the value of an atomic type and get the result. * * @param atomic_var the atomic variable. * * @return The decremented value. */ PJ_DECL(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var); /** * Add a value to an atomic type. * * @param atomic_var The atomic variable. * @param value Value to be added. */ PJ_DECL(void) pj_atomic_add( pj_atomic_t *atomic_var, pj_atomic_value_t value); /** * Add a value to an atomic type and get the result. * * @param atomic_var The atomic variable. * @param value Value to be added. * * @return The result after the addition. */ PJ_DECL(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var, pj_atomic_value_t value); /** * @} */ /* **************************************************************************/ /** * @defgroup PJ_MUTEX Mutexes. * @ingroup PJ_OS * @{ * * Mutex manipulation. Alternatively, application can use higher abstraction * for lock objects, which provides uniform API for all kinds of lock * mechanisms, including mutex. See @ref PJ_LOCK for more information. */ /** * Mutex types: * - PJ_MUTEX_DEFAULT: default mutex type, which is system dependent. * - PJ_MUTEX_SIMPLE: non-recursive mutex. * - PJ_MUTEX_RECURSE: recursive mutex. */ typedef enum pj_mutex_type_e { PJ_MUTEX_DEFAULT, PJ_MUTEX_SIMPLE, PJ_MUTEX_RECURSE } pj_mutex_type_e; /** * Create mutex of the specified type. * * @param pool The pool. * @param name Name to be associated with the mutex (for debugging). * @param type The type of the mutex, of type #pj_mutex_type_e. * @param mutex Pointer to hold the returned mutex instance. * * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_mutex_create(pj_pool_t *pool, const char *name, int type, pj_mutex_t **mutex); /** * Create simple, non-recursive mutex. * This function is a simple wrapper for #pj_mutex_create to create * non-recursive mutex. * * @param pool The pool. * @param name Mutex name. * @param mutex Pointer to hold the returned mutex instance. * * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name, pj_mutex_t **mutex ); /** * Create recursive mutex. * This function is a simple wrapper for #pj_mutex_create to create * recursive mutex. * * @param pool The pool. * @param name Mutex name. * @param mutex Pointer to hold the returned mutex instance. * * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, const char *name, pj_mutex_t **mutex ); /** * Acquire mutex lock. * * @param mutex The mutex. * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex); /** * Release mutex lock. * * @param mutex The mutex. * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex); /** * Try to acquire mutex lock. * * @param mutex The mutex. * @return PJ_SUCCESS on success, or the error code if the * lock couldn't be acquired. */ PJ_DECL(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex); /** * Destroy mutex. * * @param mutex Te mutex. * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex); /** * Determine whether calling thread is owning the mutex (only available when * PJ_DEBUG is set). * @param mutex The mutex. * @return Non-zero if yes. */ PJ_DECL(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex); /** * @} */ /* **************************************************************************/ /** * @defgroup PJ_RW_MUTEX Reader/Writer Mutex * @ingroup PJ_OS * @{ * Reader/writer mutex is a classic synchronization object where multiple * readers can acquire the mutex, but only a single writer can acquire the * mutex. */ /** * Opaque declaration for reader/writer mutex. * Reader/writer mutex is a classic synchronization object where multiple * readers can acquire the mutex, but only a single writer can acquire the * mutex. */ typedef struct pj_rwmutex_t pj_rwmutex_t; /** * Create reader/writer mutex. * * @param pool Pool to allocate memory for the mutex. * @param name Name to be assigned to the mutex. * @param mutex Pointer to receive the newly created mutex. * * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_rwmutex_create(pj_pool_t *pool, const char *name, pj_rwmutex_t **mutex); /** * Lock the mutex for reading. * * @param mutex The mutex. * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_rwmutex_lock_read(pj_rwmutex_t *mutex); /** * Lock the mutex for writing. * * @param mutex The mutex. * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_rwmutex_lock_write(pj_rwmutex_t *mutex); /** * Release read lock. * * @param mutex The mutex. * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_rwmutex_unlock_read(pj_rwmutex_t *mutex); /** * Release write lock. * * @param mutex The mutex. * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_rwmutex_unlock_write(pj_rwmutex_t *mutex); /** * Destroy reader/writer mutex. * * @param mutex The mutex. * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_rwmutex_destroy(pj_rwmutex_t *mutex); /** * @} */ /* **************************************************************************/ /** * @defgroup PJ_CRIT_SEC Critical sections. * @ingroup PJ_OS * @{ * Critical section protection can be used to protect regions where: * - mutual exclusion protection is needed. * - it's rather too expensive to create a mutex. * - the time spent in the region is very very brief. * * Critical section is a global object, and it prevents any threads from * entering any regions that are protected by critical section once a thread * is already in the section. * * Critial section is \a not recursive! * * Application MUST NOT call any functions that may cause current * thread to block (such as allocating memory, performing I/O, locking mutex, * etc.) while holding the critical section. */ /** * Enter critical section. */ PJ_DECL(void) pj_enter_critical_section(void); /** * Leave critical section. */ PJ_DECL(void) pj_leave_critical_section(void); /** * @} */ /* **************************************************************************/ #if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 /** * @defgroup PJ_SEM Semaphores. * @ingroup PJ_OS * @{ * * This module provides abstraction for semaphores, where available. */ /** * Create semaphore. * * @param pool The pool. * @param name Name to be assigned to the semaphore (for logging purpose) * @param initial The initial count of the semaphore. * @param max The maximum count of the semaphore. * @param sem Pointer to hold the semaphore created. * * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_sem_create( pj_pool_t *pool, const char *name, unsigned initial, unsigned max, pj_sem_t **sem); /** * Wait for semaphore. * * @param sem The semaphore. * * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_sem_wait(pj_sem_t *sem); /** * Try wait for semaphore. * * @param sem The semaphore. * * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_sem_trywait(pj_sem_t *sem); /** * Release semaphore. * * @param sem The semaphore. * * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_sem_post(pj_sem_t *sem); /** * Destroy semaphore. * * @param sem The semaphore. * * @return PJ_SUCCESS on success, or the error code. */ PJ_DECL(pj_status_t) pj_sem_destroy(pj_sem_t *sem); /** * @} */ #endif /* PJ_HAS_SEMAPHORE */ /* **************************************************************************/ #if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 /** * @defgroup PJ_EVENT Event Object. * @ingroup PJ_OS * @{ * * This module provides abstraction to event object (e.g. Win32 Event) where * available. Event objects can be used for synchronization among threads. */ /** * Create event object. * * @param pool The pool. * @param name The name of the event object (for logging purpose). * @param manual_reset Specify whether the event is manual-reset * @param initial Specify the initial state of the event object. * @param event Pointer to hold the returned event object. * * @return event handle, or NULL if failed. */ PJ_DECL(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name, pj_bool_t manual_reset, pj_bool_t initial, pj_event_t **event); /** * Wait for event to be signaled. * * @param event The event object. * * @return zero if successfull. */ PJ_DECL(pj_status_t) pj_event_wait(pj_event_t *event); /** * Try wait for event object to be signalled. * * @param event The event object. * * @return zero if successfull. */ PJ_DECL(pj_status_t) pj_event_trywait(pj_event_t *event); /** * Set the event object state to signaled. For auto-reset event, this * will only release the first thread that are waiting on the event. For * manual reset event, the state remains signaled until the event is reset. * If there is no thread waiting on the event, the event object state * remains signaled. * * @param event The event object. * * @return zero if successfull. */ PJ_DECL(pj_status_t) pj_event_set(pj_event_t *event); /** * Set the event object to signaled state to release appropriate number of * waiting threads and then reset the event object to non-signaled. For * manual-reset event, this function will release all waiting threads. For * auto-reset event, this function will only release one waiting thread. * * @param event The event object. * * @return zero if successfull. */ PJ_DECL(pj_status_t) pj_event_pulse(pj_event_t *event); /** * Set the event object state to non-signaled. * * @param event The event object. * * @return zero if successfull. */ PJ_DECL(pj_status_t) pj_event_reset(pj_event_t *event); /** * Destroy the event object. * * @param event The event object. * * @return zero if successfull. */ PJ_DECL(pj_status_t) pj_event_destroy(pj_event_t *event); /** * @} */ #endif /* PJ_HAS_EVENT_OBJ */ /* **************************************************************************/ /** * @addtogroup PJ_TIME Time Data Type and Manipulation. * @ingroup PJ_OS * @{ * This module provides API for manipulating time. * * \section pj_time_examples_sec Examples * * For examples, please see: * - \ref page_pjlib_sleep_test */ /** * Get current time of day in local representation. * * @param tv Variable to store the result. * * @return zero if successfull. */ PJ_DECL(pj_status_t) pj_gettimeofday(pj_time_val *tv); /** * Parse time value into date/time representation. * * @param tv The time. * @param pt Variable to store the date time result. * * @return zero if successfull. */ PJ_DECL(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt); /** * Encode date/time to time value. * * @param pt The date/time. * @param tv Variable to store time value result. * * @return zero if successfull. */ PJ_DECL(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv); /** * Convert local time to GMT. * * @param tv Time to convert. * * @return zero if successfull. */ PJ_DECL(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv); /** * Convert GMT to local time. * * @param tv Time to convert. * * @return zero if successfull. */ PJ_DECL(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv); /** * @} */ /* **************************************************************************/ #if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 /** * @defgroup PJ_TERM Terminal * @ingroup PJ_OS * @{ */ /** * Set current terminal color. * * @param color The RGB color. * * @return zero on success. */ PJ_DECL(pj_status_t) pj_term_set_color(pj_color_t color); /** * Get current terminal foreground color. * * @return RGB color. */ PJ_DECL(pj_color_t) pj_term_get_color(void); /** * @} */ #endif /* PJ_TERM_HAS_COLOR */ /* **************************************************************************/ /** * @defgroup PJ_TIMESTAMP High Resolution Timestamp * @ingroup PJ_OS * @{ * * PJLIB provides High Resolution Timestamp API to access highest * resolution timestamp value provided by the platform. The API is usefull * to measure precise elapsed time, and can be used in applications such * as profiling. * * The timestamp value is represented in cycles, and can be related to * normal time (in seconds or sub-seconds) using various functions provided. * * \section pj_timestamp_examples_sec Examples * * For examples, please see: * - \ref page_pjlib_sleep_test * - \ref page_pjlib_timestamp_test */ /* * High resolution timer. */ #if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 /** * Get monotonic time since some unspecified starting point. * * @param tv Variable to store the result. * * @return PJ_SUCCESS if successful. */ PJ_DECL(pj_status_t) pj_gettickcount(pj_time_val *tv); /** * Acquire high resolution timer value. The time value are stored * in cycles. * * @param ts High resolution timer value. * @return PJ_SUCCESS or the appropriate error code. * * @see pj_get_timestamp_freq(). */ PJ_DECL(pj_status_t) pj_get_timestamp(pj_timestamp *ts); /** * Get high resolution timer frequency, in cycles per second. * * @param freq Timer frequency, in cycles per second. * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq); /** * Set timestamp from 32bit values. * @param t The timestamp to be set. * @param hi The high 32bit part. * @param lo The low 32bit part. */ PJ_INLINE(void) pj_set_timestamp32(pj_timestamp *t, pj_uint32_t hi, pj_uint32_t lo) { t->u32.hi = hi; t->u32.lo = lo; } /** * Compare timestamp t1 and t2. * @param t1 t1. * @param t2 t2. * @return -1 if (t1 < t2), 1 if (t1 > t2), or 0 if (t1 == t2) */ PJ_INLINE(int) pj_cmp_timestamp(const pj_timestamp *t1, const pj_timestamp *t2) { #if PJ_HAS_INT64 if (t1->u64 < t2->u64) return -1; else if (t1->u64 > t2->u64) return 1; else return 0; #else if (t1->u32.hi < t2->u32.hi || (t1->u32.hi == t2->u32.hi && t1->u32.lo < t2->u32.lo)) return -1; else if (t1->u32.hi > t2->u32.hi || (t1->u32.hi == t2->u32.hi && t1->u32.lo > t2->u32.lo)) return 1; else return 0; #endif } /** * Add timestamp t2 to t1. * @param t1 t1. * @param t2 t2. */ PJ_INLINE(void) pj_add_timestamp(pj_timestamp *t1, const pj_timestamp *t2) { #if PJ_HAS_INT64 t1->u64 += t2->u64; #else pj_uint32_t old = t1->u32.lo; t1->u32.hi += t2->u32.hi; t1->u32.lo += t2->u32.lo; if (t1->u32.lo < old) ++t1->u32.hi; #endif } /** * Add timestamp t2 to t1. * @param t1 t1. * @param t2 t2. */ PJ_INLINE(void) pj_add_timestamp32(pj_timestamp *t1, pj_uint32_t t2) { #if PJ_HAS_INT64 t1->u64 += t2; #else pj_uint32_t old = t1->u32.lo; t1->u32.lo += t2; if (t1->u32.lo < old) ++t1->u32.hi; #endif } /** * Substract timestamp t2 from t1. * @param t1 t1. * @param t2 t2. */ PJ_INLINE(void) pj_sub_timestamp(pj_timestamp *t1, const pj_timestamp *t2) { #if PJ_HAS_INT64 t1->u64 -= t2->u64; #else t1->u32.hi -= t2->u32.hi; if (t1->u32.lo >= t2->u32.lo) t1->u32.lo -= t2->u32.lo; else { t1->u32.lo -= t2->u32.lo; --t1->u32.hi; } #endif } /** * Substract timestamp t2 from t1. * @param t1 t1. * @param t2 t2. */ PJ_INLINE(void) pj_sub_timestamp32(pj_timestamp *t1, pj_uint32_t t2) { #if PJ_HAS_INT64 t1->u64 -= t2; #else if (t1->u32.lo >= t2) t1->u32.lo -= t2; else { t1->u32.lo -= t2; --t1->u32.hi; } #endif } /** * Get the timestamp difference between t2 and t1 (that is t2 minus t1), * and return a 32bit signed integer difference. */ PJ_INLINE(pj_int32_t) pj_timestamp_diff32(const pj_timestamp *t1, const pj_timestamp *t2) { /* Be careful with the signess (I think!) */ #if PJ_HAS_INT64 pj_int64_t diff = t2->u64 - t1->u64; return (pj_int32_t) diff; #else pj_int32 diff = t2->u32.lo - t1->u32.lo; return diff; #endif } /** * Calculate the elapsed time, and store it in pj_time_val. * This function calculates the elapsed time using highest precision * calculation that is available for current platform, considering * whether floating point or 64-bit precision arithmetic is available. * For maximum portability, application should prefer to use this function * rather than calculating the elapsed time by itself. * * @param start The starting timestamp. * @param stop The end timestamp. * * @return Elapsed time as #pj_time_val. * * @see pj_elapsed_usec(), pj_elapsed_cycle(), pj_elapsed_nanosec() */ PJ_DECL(pj_time_val) pj_elapsed_time( const pj_timestamp *start, const pj_timestamp *stop ); /** * Calculate the elapsed time as 32-bit miliseconds. * This function calculates the elapsed time using highest precision * calculation that is available for current platform, considering * whether floating point or 64-bit precision arithmetic is available. * For maximum portability, application should prefer to use this function * rather than calculating the elapsed time by itself. * * @param start The starting timestamp. * @param stop The end timestamp. * * @return Elapsed time in milisecond. * * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_nanosec() */ PJ_DECL(pj_uint32_t) pj_elapsed_msec( const pj_timestamp *start, const pj_timestamp *stop ); /** * Variant of #pj_elapsed_msec() which returns 64bit value. */ PJ_DECL(pj_uint64_t) pj_elapsed_msec64(const pj_timestamp *start, const pj_timestamp *stop ); /** * Calculate the elapsed time in 32-bit microseconds. * This function calculates the elapsed time using highest precision * calculation that is available for current platform, considering * whether floating point or 64-bit precision arithmetic is available. * For maximum portability, application should prefer to use this function * rather than calculating the elapsed time by itself. * * @param start The starting timestamp. * @param stop The end timestamp. * * @return Elapsed time in microsecond. * * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_nanosec() */ PJ_DECL(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start, const pj_timestamp *stop ); /** * Calculate the elapsed time in 32-bit nanoseconds. * This function calculates the elapsed time using highest precision * calculation that is available for current platform, considering * whether floating point or 64-bit precision arithmetic is available. * For maximum portability, application should prefer to use this function * rather than calculating the elapsed time by itself. * * @param start The starting timestamp. * @param stop The end timestamp. * * @return Elapsed time in nanoseconds. * * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_usec() */ PJ_DECL(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start, const pj_timestamp *stop ); /** * Calculate the elapsed time in 32-bit cycles. * This function calculates the elapsed time using highest precision * calculation that is available for current platform, considering * whether floating point or 64-bit precision arithmetic is available. * For maximum portability, application should prefer to use this function * rather than calculating the elapsed time by itself. * * @param start The starting timestamp. * @param stop The end timestamp. * * @return Elapsed time in cycles. * * @see pj_elapsed_usec(), pj_elapsed_time(), pj_elapsed_nanosec() */ PJ_DECL(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start, const pj_timestamp *stop ); #endif /* PJ_HAS_HIGH_RES_TIMER */ /** @} */ /* **************************************************************************/ /** * @defgroup PJ_APP_OS Application execution * @ingroup PJ_OS * @{ */ /** * Type for application main function. */ typedef int (*pj_main_func_ptr)(int argc, char *argv[]); /** * Run the application. This function has to be called in the main thread * and after doing the necessary initialization according to the flags * provided, it will call main_func() function. * * @param main_func Application's main function. * @param argc Number of arguments from the main() function, which * will be passed to main_func() function. * @param argv The arguments from the main() function, which will * be passed to main_func() function. * @param flags Flags for application execution, currently must be 0. * * @return main_func()'s return value. */ PJ_DECL(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], unsigned flags); /** @} */ /* **************************************************************************/ /** * Internal PJLIB function to initialize the threading subsystem. * @return PJ_SUCCESS or the appropriate error code. */ pj_status_t pj_thread_init(void); PJ_END_DECL #endif /* __PJ_OS_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/pool.h ================================================ /* $Id: pool.h 4298 2012-11-22 05:00:01Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include /* See if we use pool's alternate API. * The alternate API is used e.g. to implement pool debugging. */ #if PJ_HAS_POOL_ALT_API # include #endif #ifndef __PJ_POOL_H__ #define __PJ_POOL_H__ /** * @file pool.h * @brief Memory Pool. */ PJ_BEGIN_DECL /** * @defgroup PJ_POOL_GROUP Fast Memory Pool * @brief * Memory pools allow dynamic memory allocation comparable to malloc or the * new in operator C++. Those implementations are not desirable for very * high performance applications or real-time systems, because of the * performance bottlenecks and it suffers from fragmentation issue. * * \section PJ_POOL_INTRO_SEC PJLIB's Memory Pool * \subsection PJ_POOL_ADVANTAGE_SUBSEC Advantages * * PJLIB's pool has many advantages over traditional malloc/new operator and * over other memory pool implementations, because: * - unlike other memory pool implementation, it allows allocation of * memory chunks of different sizes, * - it's very very fast. * \n * Memory chunk allocation is not only an O(1) * operation, but it's also very simple (just * few pointer arithmetic operations) and it doesn't require locking * any mutex, * - it's memory efficient. * \n * Pool doesn't keep track individual memory chunks allocated by * applications, so there is no additional overhead needed for each * memory allocation (other than possible additional of few bytes, up to * PJ_POOL_ALIGNMENT-1, for aligning the memory). * But see the @ref PJ_POOL_CAVEATS_SUBSEC below. * - it prevents memory leaks. * \n * Memory pool inherently has garbage collection functionality. In fact, * there is no need to free the chunks allocated from the memory pool. * All chunks previously allocated from the pool will be freed once the * pool itself is destroyed. This would prevent memory leaks that haunt * programmers for decades, and it provides additional performance * advantage over traditional malloc/new operator. * * Even more, PJLIB's memory pool provides some additional usability and * flexibility for applications: * - memory leaks are easily traceable, since memory pool is assigned name, * and application can inspect what pools currently active in the system. * - by design, memory allocation from a pool is not thread safe. We assumed * that a pool will be owned by a higher level object, and thread safety * should be handled by that object. This enables very fast pool operations * and prevents unnecessary locking operations, * - by default, the memory pool API behaves more like C++ new operator, * in that it will throw PJ_NO_MEMORY_EXCEPTION exception (see * @ref PJ_EXCEPT) when memory chunk allocation fails. This enables failure * handling to be done on more high level function (instead of checking * the result of pj_pool_alloc() everytime). If application doesn't like * this, the default behavior can be changed on global basis by supplying * different policy to the pool factory. * - any memory allocation backend allocator/deallocator may be used. By * default, the policy uses malloc() and free() to manage the pool's block, * but application may use different strategy, for example to allocate * memory blocks from a globally static memory location. * * * \subsection PJ_POOL_PERFORMANCE_SUBSEC Performance * * The result of PJLIB's memory design and careful implementation is a * memory allocation strategy that can speed-up the memory allocations * and deallocations by up to 30 times compared to standard * malloc()/free() (more than 150 million allocations per second on a * P4/3.0GHz Linux machine). * * (Note: your mileage may vary, of course. You can see how much PJLIB's * pool improves the performance over malloc()/free() in your target * system by running pjlib-test application). * * * \subsection PJ_POOL_CAVEATS_SUBSEC Caveats * * There are some caveats though! * * When creating pool, PJLIB requires applications to specify the initial * pool size, and as soon as the pool is created, PJLIB allocates memory * from the system by that size. Application designers MUST choose the * initial pool size carefully, since choosing too big value will result in * wasting system's memory. * * But the pool can grow. Application designer can specify how the * pool will grow in size, by specifying the size increment when creating * the pool. * * The pool, however, cannot shrink! Since there is no * function to deallocate memory chunks, there is no way for the pool to * release back unused memory to the system. * Application designers must be aware that constant memory allocations * from pool that has infinite life-time may cause the memory usage of * the application to grow over time. * * * \section PJ_POOL_USING_SEC Using Memory Pool * * This section describes how to use PJLIB's memory pool framework. * As we hope the readers will witness, PJLIB's memory pool API is quite * straightforward. * * \subsection PJ_POOL_USING_F Create Pool Factory * First, application needs to initialize a pool factory (this normally * only needs to be done once in one application). PJLIB provides * a pool factory implementation called caching pool (see @ref * PJ_CACHING_POOL), and it is initialized by calling #pj_caching_pool_init(). * * \subsection PJ_POOL_USING_P Create The Pool * Then application creates the pool object itself with #pj_pool_create(), * specifying among other thing the pool factory where the pool should * be created from, the pool name, initial size, and increment/expansion * size. * * \subsection PJ_POOL_USING_M Allocate Memory as Required * Then whenever application needs to allocate dynamic memory, it would * call #pj_pool_alloc(), #pj_pool_calloc(), or #pj_pool_zalloc() to * allocate memory chunks from the pool. * * \subsection PJ_POOL_USING_DP Destroy the Pool * When application has finished with the pool, it should call * #pj_pool_release() to release the pool object back to the factory. * Depending on the types of the factory, this may release the memory back * to the operating system. * * \subsection PJ_POOL_USING_Dc Destroy the Pool Factory * And finally, before application quites, it should deinitialize the * pool factory, to make sure that all memory blocks allocated by the * factory are released back to the operating system. After this, of * course no more memory pool allocation can be requested. * * \subsection PJ_POOL_USING_EX Example * Below is a sample complete program that utilizes PJLIB's memory pool. * * \code #include #define THIS_FILE "pool_sample.c" static void my_perror(const char *title, pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(1,(THIS_FILE, "%s: %s [status=%d]", title, errmsg, status)); } static void pool_demo_1(pj_pool_factory *pfactory) { unsigned i; pj_pool_t *pool; // Must create pool before we can allocate anything pool = pj_pool_create(pfactory, // the factory "pool1", // pool's name 4000, // initial size 4000, // increment size NULL); // use default callback. if (pool == NULL) { my_perror("Error creating pool", PJ_ENOMEM); return; } // Demo: allocate some memory chunks for (i=0; i<1000; ++i) { void *p; p = pj_pool_alloc(pool, (pj_rand()+1) % 512); // Do something with p ... // Look! No need to free p!! } // Done with silly demo, must free pool to release all memory. pj_pool_release(pool); } int main() { pj_caching_pool cp; pj_status_t status; // Must init PJLIB before anything else status = pj_init(); if (status != PJ_SUCCESS) { my_perror("Error initializing PJLIB", status); return 1; } // Create the pool factory, in this case, a caching pool, // using default pool policy. pj_caching_pool_init(&cp, NULL, 1024*1024 ); // Do a demo pool_demo_1(&cp.factory); // Done with demos, destroy caching pool before exiting app. pj_caching_pool_destroy(&cp); return 0; } \endcode * * More information about pool factory, the pool object, and caching pool * can be found on the Module Links below. */ /** * @defgroup PJ_POOL Memory Pool Object * @ingroup PJ_POOL_GROUP * @brief * The memory pool is an opaque object created by pool factory. * Application uses this object to request a memory chunk, by calling * #pj_pool_alloc(), #pj_pool_calloc(), or #pj_pool_zalloc(). * When the application has finished using * the pool, it must call #pj_pool_release() to free all the chunks previously * allocated and release the pool back to the factory. * * A memory pool is initialized with an initial amount of memory, which is * called a block. Pool can be configured to dynamically allocate more memory * blocks when it runs out of memory. * * The pool doesn't keep track of individual memory allocations * by user, and the user doesn't have to free these indidual allocations. This * makes memory allocation simple and very fast. All the memory allocated from * the pool will be destroyed when the pool itself is destroyed. * * \section PJ_POOL_THREADING_SEC More on Threading Policies * - By design, memory allocation from a pool is not thread safe. We assumed * that a pool will be owned by an object, and thread safety should be * handled by that object. Thus these functions are not thread safe: * - #pj_pool_alloc, * - #pj_pool_calloc, * - and other pool statistic functions. * - Threading in the pool factory is decided by the policy set for the * factory when it was created. * * \section PJ_POOL_EXAMPLES_SEC Examples * * For some sample codes on how to use the pool, please see: * - @ref page_pjlib_pool_test * * @{ */ /** * The type for function to receive callback from the pool when it is unable * to allocate memory. The elegant way to handle this condition is to throw * exception, and this is what is expected by most of this library * components. */ typedef void pj_pool_callback(pj_pool_t *pool, pj_size_t size); /** * This class, which is used internally by the pool, describes a single * block of memory from which user memory allocations will be allocated from. */ typedef struct pj_pool_block { PJ_DECL_LIST_MEMBER(struct pj_pool_block); /**< List's prev and next. */ unsigned char *buf; /**< Start of buffer. */ unsigned char *cur; /**< Current alloc ptr. */ unsigned char *end; /**< End of buffer. */ } pj_pool_block; /** * This structure describes the memory pool. Only implementors of pool factory * need to care about the contents of this structure. */ struct pj_pool_t { PJ_DECL_LIST_MEMBER(struct pj_pool_t); /**< Standard list elements. */ /** Pool name */ char obj_name[PJ_MAX_OBJ_NAME]; /** Pool factory. */ pj_pool_factory *factory; /** Data put by factory */ void *factory_data; /** Current capacity allocated by the pool. */ pj_size_t capacity; /** Size of memory block to be allocated when the pool runs out of memory */ pj_size_t increment_size; /** List of memory blocks allcoated by the pool. */ pj_pool_block block_list; /** The callback to be called when the pool is unable to allocate memory. */ pj_pool_callback *callback; }; /** * Guidance on how much memory required for initial pool administrative data. */ #define PJ_POOL_SIZE (sizeof(struct pj_pool_t)) /** * Pool memory alignment (must be power of 2). */ #ifndef PJ_POOL_ALIGNMENT # define PJ_POOL_ALIGNMENT 4 #endif /** * Create a new pool from the pool factory. This wrapper will call create_pool * member of the pool factory. * * @param factory The pool factory. * @param name The name to be assigned to the pool. The name should * not be longer than PJ_MAX_OBJ_NAME (32 chars), or * otherwise it will be truncated. * @param initial_size The size of initial memory blocks taken by the pool. * Note that the pool will take 68+20 bytes for * administrative area from this block. * @param increment_size the size of each additional blocks to be allocated * when the pool is running out of memory. If user * requests memory which is larger than this size, then * an error occurs. * Note that each time a pool allocates additional block, * it needs PJ_POOL_SIZE more to store some * administrative info. * @param callback Callback to be called when error occurs in the pool. * If this value is NULL, then the callback from pool * factory policy will be used. * Note that when an error occurs during pool creation, * the callback itself is not called. Instead, NULL * will be returned. * * @return The memory pool, or NULL. */ PJ_IDECL(pj_pool_t*) pj_pool_create(pj_pool_factory *factory, const char *name, pj_size_t initial_size, pj_size_t increment_size, pj_pool_callback *callback); /** * Release the pool back to pool factory. * * @param pool Memory pool. */ PJ_IDECL(void) pj_pool_release( pj_pool_t *pool ); /** * Get pool object name. * * @param pool the pool. * * @return pool name as NULL terminated string. */ PJ_IDECL(const char *) pj_pool_getobjname( const pj_pool_t *pool ); /** * Reset the pool to its state when it was initialized. * This means that if additional blocks have been allocated during runtime, * then they will be freed. Only the original block allocated during * initialization is retained. This function will also reset the internal * counters, such as pool capacity and used size. * * @param pool the pool. */ PJ_DECL(void) pj_pool_reset( pj_pool_t *pool ); /** * Get the pool capacity, that is, the system storage that have been allocated * by the pool, and have been used/will be used to allocate user requests. * There's no guarantee that the returned value represent a single * contiguous block, because the capacity may be spread in several blocks. * * @param pool the pool. * * @return the capacity. */ PJ_IDECL(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool ); /** * Get the total size of user allocation request. * * @param pool the pool. * * @return the total size. */ PJ_IDECL(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool ); /** * Allocate storage with the specified size from the pool. * If there's no storage available in the pool, then the pool can allocate more * blocks if the increment size is larger than the requested size. * * @param pool the pool. * @param size the requested size. * * @return pointer to the allocated memory. * * @see PJ_POOL_ALLOC_T */ PJ_IDECL(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size); /** * Allocate storage from the pool, and initialize it to zero. * This function behaves like pj_pool_alloc(), except that the storage will * be initialized to zero. * * @param pool the pool. * @param count the number of elements in the array. * @param elem the size of individual element. * * @return pointer to the allocated memory. */ PJ_IDECL(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count, pj_size_t elem); /** * Allocate storage from the pool and initialize it to zero. * * @param pool The pool. * @param size The size to be allocated. * * @return Pointer to the allocated memory. * * @see PJ_POOL_ZALLOC_T */ PJ_INLINE(void*) pj_pool_zalloc(pj_pool_t *pool, pj_size_t size) { return pj_pool_calloc(pool, 1, size); } /** * This macro allocates memory from the pool and returns the instance of * the specified type. It provides a stricker type safety than pj_pool_alloc() * since the return value of this macro will be type-casted to the specified * type. * * @param pool The pool * @param type The type of object to be allocated * * @return Memory buffer of the specified type. */ #define PJ_POOL_ALLOC_T(pool,type) \ ((type*)pj_pool_alloc(pool, sizeof(type))) /** * This macro allocates memory from the pool, zeroes the buffer, and * returns the instance of the specified type. It provides a stricker type * safety than pj_pool_zalloc() since the return value of this macro will be * type-casted to the specified type. * * @param pool The pool * @param type The type of object to be allocated * * @return Memory buffer of the specified type. */ #define PJ_POOL_ZALLOC_T(pool,type) \ ((type*)pj_pool_zalloc(pool, sizeof(type))) /* * Internal functions */ PJ_IDECL(void*) pj_pool_alloc_from_block(pj_pool_block *block, pj_size_t size); PJ_DECL(void*) pj_pool_allocate_find(pj_pool_t *pool, pj_size_t size); /** * @} // PJ_POOL */ /* **************************************************************************/ /** * @defgroup PJ_POOL_FACTORY Pool Factory and Policy * @ingroup PJ_POOL_GROUP * @brief * A pool object must be created through a factory. A factory not only provides * generic interface functions to create and release pool, but also provides * strategy to manage the life time of pools. One sample implementation, * \a pj_caching_pool, can be set to keep the pools released by application for * future use as long as the total memory is below the limit. * * The pool factory interface declared in PJLIB is designed to be extensible. * Application can define its own strategy by creating it's own pool factory * implementation, and this strategy can be used even by existing library * without recompilation. * * \section PJ_POOL_FACTORY_ITF Pool Factory Interface * The pool factory defines the following interface: * - \a policy: the memory pool factory policy. * - \a create_pool(): create a new memory pool. * - \a release_pool(): release memory pool back to factory. * * \section PJ_POOL_FACTORY_POL Pool Factory Policy. * * A pool factory only defines functions to create and release pool and how * to manage pools, but the rest of the functionalities are controlled by * policy. A pool policy defines: * - how memory block is allocated and deallocated (the default implementation * allocates and deallocate memory by calling malloc() and free()). * - callback to be called when memory allocation inside a pool fails (the * default implementation will throw PJ_NO_MEMORY_EXCEPTION exception). * - concurrency when creating and releasing pool from/to the factory. * * A pool factory can be given different policy during creation to make * it behave differently. For example, caching pool factory can be configured * to allocate and deallocate from a static/contiguous/preallocated memory * instead of using malloc()/free(). * * What strategy/factory and what policy to use is not defined by PJLIB, but * instead is left to application to make use whichever is most efficient for * itself. * * The pool factory policy controls the behaviour of memory factories, and * defines the following interface: * - \a block_alloc(): allocate memory block from backend memory mgmt/system. * - \a block_free(): free memory block back to backend memory mgmt/system. * @{ */ /* We unfortunately don't have support for factory policy options as now, so we keep this commented at the moment. enum PJ_POOL_FACTORY_OPTION { PJ_POOL_FACTORY_SERIALIZE = 1 }; */ /** * This structure declares pool factory interface. */ typedef struct pj_pool_factory_policy { /** * Allocate memory block (for use by pool). This function is called * by memory pool to allocate memory block. * * @param factory Pool factory. * @param size The size of memory block to allocate. * * @return Memory block. */ void* (*block_alloc)(pj_pool_factory *factory, pj_size_t size); /** * Free memory block. * * @param factory Pool factory. * @param mem Memory block previously allocated by block_alloc(). * @param size The size of memory block. */ void (*block_free)(pj_pool_factory *factory, void *mem, pj_size_t size); /** * Default callback to be called when memory allocation fails. */ pj_pool_callback *callback; /** * Option flags. */ unsigned flags; } pj_pool_factory_policy; /** * \def PJ_NO_MEMORY_EXCEPTION * This constant denotes the exception number that will be thrown by default * memory factory policy when memory allocation fails. * * @see pj_NO_MEMORY_EXCEPTION() */ PJ_DECL_DATA(int) PJ_NO_MEMORY_EXCEPTION; /** * Get #PJ_NO_MEMORY_EXCEPTION constant. */ PJ_DECL(int) pj_NO_MEMORY_EXCEPTION(void); /** * This global variable points to default memory pool factory policy. * The behaviour of the default policy is: * - block allocation and deallocation use malloc() and free(). * - callback will raise PJ_NO_MEMORY_EXCEPTION exception. * - access to pool factory is not serialized (i.e. not thread safe). * * @see pj_pool_factory_get_default_policy */ PJ_DECL_DATA(pj_pool_factory_policy) pj_pool_factory_default_policy; /** * Get the default pool factory policy. * * @return the pool policy. */ PJ_DECL(const pj_pool_factory_policy*) pj_pool_factory_get_default_policy(void); /** * This structure contains the declaration for pool factory interface. */ struct pj_pool_factory { /** * Memory pool policy. */ pj_pool_factory_policy policy; /** * Create a new pool from the pool factory. * * @param factory The pool factory. * @param name the name to be assigned to the pool. The name should * not be longer than PJ_MAX_OBJ_NAME (32 chars), or * otherwise it will be truncated. * @param initial_size the size of initial memory blocks taken by the pool. * Note that the pool will take 68+20 bytes for * administrative area from this block. * @param increment_size the size of each additional blocks to be allocated * when the pool is running out of memory. If user * requests memory which is larger than this size, then * an error occurs. * Note that each time a pool allocates additional block, * it needs 20 bytes (equal to sizeof(pj_pool_block)) to * store some administrative info. * @param callback Cllback to be called when error occurs in the pool. * Note that when an error occurs during pool creation, * the callback itself is not called. Instead, NULL * will be returned. * * @return the memory pool, or NULL. */ pj_pool_t* (*create_pool)( pj_pool_factory *factory, const char *name, pj_size_t initial_size, pj_size_t increment_size, pj_pool_callback *callback); /** * Release the pool to the pool factory. * * @param factory The pool factory. * @param pool The pool to be released. */ void (*release_pool)( pj_pool_factory *factory, pj_pool_t *pool ); /** * Dump pool status to log. * * @param factory The pool factory. */ void (*dump_status)( pj_pool_factory *factory, pj_bool_t detail ); /** * This is optional callback to be called by allocation policy when * it allocates a new memory block. The factory may use this callback * for example to keep track of the total number of memory blocks * currently allocated by applications. * * @param factory The pool factory. * @param size Size requested by application. * * @return MUST return PJ_TRUE, otherwise the block * allocation is cancelled. */ pj_bool_t (*on_block_alloc)(pj_pool_factory *factory, pj_size_t size); /** * This is optional callback to be called by allocation policy when * it frees memory block. The factory may use this callback * for example to keep track of the total number of memory blocks * currently allocated by applications. * * @param factory The pool factory. * @param size Size freed. */ void (*on_block_free)(pj_pool_factory *factory, pj_size_t size); }; /** * This function is intended to be used by pool factory implementors. * @param factory Pool factory. * @param name Pool name. * @param initial_size Initial size. * @param increment_size Increment size. * @param callback Callback. * @return The pool object, or NULL. */ PJ_DECL(pj_pool_t*) pj_pool_create_int( pj_pool_factory *factory, const char *name, pj_size_t initial_size, pj_size_t increment_size, pj_pool_callback *callback); /** * This function is intended to be used by pool factory implementors. * @param pool The pool. * @param name Pool name. * @param increment_size Increment size. * @param callback Callback function. */ PJ_DECL(void) pj_pool_init_int( pj_pool_t *pool, const char *name, pj_size_t increment_size, pj_pool_callback *callback); /** * This function is intended to be used by pool factory implementors. * @param pool The memory pool. */ PJ_DECL(void) pj_pool_destroy_int( pj_pool_t *pool ); /** * Dump pool factory state. * @param pf The pool factory. * @param detail Detail state required. */ PJ_INLINE(void) pj_pool_factory_dump( pj_pool_factory *pf, pj_bool_t detail ) { (*pf->dump_status)(pf, detail); } /** * @} // PJ_POOL_FACTORY */ /* **************************************************************************/ /** * @defgroup PJ_CACHING_POOL Caching Pool Factory * @ingroup PJ_POOL_GROUP * @brief * Caching pool is one sample implementation of pool factory where the * factory can reuse memory to create a pool. Application defines what the * maximum memory the factory can hold, and when a pool is released the * factory decides whether to destroy the pool or to keep it for future use. * If the total amount of memory in the internal cache is still within the * limit, the factory will keep the pool in the internal cache, otherwise the * pool will be destroyed, thus releasing the memory back to the system. * * @{ */ /** * Number of unique sizes, to be used as index to the free list. * Each pool in the free list is organized by it's size. */ #define PJ_CACHING_POOL_ARRAY_SIZE 16 /** * Declaration for caching pool. Application doesn't normally need to * care about the contents of this struct, it is only provided here because * application need to define an instance of this struct (we can not allocate * the struct from a pool since there is no pool factory yet!). */ struct pj_caching_pool { /** Pool factory interface, must be declared first. */ pj_pool_factory factory; /** Current factory's capacity, i.e. number of bytes that are allocated * and available for application in this factory. The factory's * capacity represents the size of all pools kept by this factory * in it's free list, which will be returned to application when it * requests to create a new pool. */ pj_size_t capacity; /** Maximum size that can be held by this factory. Once the capacity * has exceeded @a max_capacity, further #pj_pool_release() will * flush the pool. If the capacity is still below the @a max_capacity, * #pj_pool_release() will save the pool to the factory's free list. */ pj_size_t max_capacity; /** * Number of pools currently held by applications. This number gets * incremented everytime #pj_pool_create() is called, and gets * decremented when #pj_pool_release() is called. */ pj_size_t used_count; /** * Total size of memory currently used by application. */ pj_size_t used_size; /** * The maximum size of memory used by application throughout the life * of the caching pool. */ pj_size_t peak_used_size; /** * Lists of pools in the cache, indexed by pool size. */ pj_list free_list[PJ_CACHING_POOL_ARRAY_SIZE]; /** * List of pools currently allocated by applications. */ pj_list used_list; /** * Internal pool. */ char pool_buf[256 * (sizeof(size_t) / 4)]; /** * Mutex. */ pj_lock_t *lock; }; /** * Initialize caching pool. * * @param ch_pool The caching pool factory to be initialized. * @param policy Pool factory policy. * @param max_capacity The total capacity to be retained in the cache. When * the pool is returned to the cache, it will be kept in * recycling list if the total capacity of pools in this * list plus the capacity of the pool is still below this * value. */ PJ_DECL(void) pj_caching_pool_init( pj_caching_pool *ch_pool, const pj_pool_factory_policy *policy, pj_size_t max_capacity); /** * Destroy caching pool, and release all the pools in the recycling list. * * @param ch_pool The caching pool. */ PJ_DECL(void) pj_caching_pool_destroy( pj_caching_pool *ch_pool ); /** * @} // PJ_CACHING_POOL */ # if PJ_FUNCTIONS_ARE_INLINED # include "pool_i.h" # endif PJ_END_DECL #endif /* __PJ_POOL_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/pool_alt.h ================================================ /* $Id: pool_alt.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_POOL_ALT_H__ #define __PJ_POOL_ALT_H__ #define __PJ_POOL_H__ PJ_BEGIN_DECL /** * The type for function to receive callback from the pool when it is unable * to allocate memory. The elegant way to handle this condition is to throw * exception, and this is what is expected by most of this library * components. */ typedef void pj_pool_callback(pj_pool_t *pool, pj_size_t size); struct pj_pool_mem { struct pj_pool_mem *next; /* data follows immediately */ }; struct pj_pool_t { struct pj_pool_mem *first_mem; pj_pool_factory *factory; char obj_name[32]; pj_size_t used_size; pj_pool_callback *cb; }; #define PJ_POOL_SIZE (sizeof(struct pj_pool_t)) /** * This constant denotes the exception number that will be thrown by default * memory factory policy when memory allocation fails. */ extern int PJ_NO_MEMORY_EXCEPTION; /* * Declare all pool API as macro that calls the implementation * function. */ #define pj_pool_create(fc,nm,init,inc,cb) \ pj_pool_create_imp(__FILE__, __LINE__, fc, nm, init, inc, cb) #define pj_pool_release(pool) pj_pool_release_imp(pool) #define pj_pool_getobjname(pool) pj_pool_getobjname_imp(pool) #define pj_pool_reset(pool) pj_pool_reset_imp(pool) #define pj_pool_get_capacity(pool) pj_pool_get_capacity_imp(pool) #define pj_pool_get_used_size(pool) pj_pool_get_used_size_imp(pool) #define pj_pool_alloc(pool,sz) \ pj_pool_alloc_imp(__FILE__, __LINE__, pool, sz) #define pj_pool_calloc(pool,cnt,elem) \ pj_pool_calloc_imp(__FILE__, __LINE__, pool, cnt, elem) #define pj_pool_zalloc(pool,sz) \ pj_pool_zalloc_imp(__FILE__, __LINE__, pool, sz) /* * Declare prototypes for pool implementation API. */ /* Create pool */ PJ_DECL(pj_pool_t*) pj_pool_create_imp(const char *file, int line, void *factory, const char *name, pj_size_t initial_size, pj_size_t increment_size, pj_pool_callback *callback); /* Release pool */ PJ_DECL(void) pj_pool_release_imp(pj_pool_t *pool); /* Get pool name */ PJ_DECL(const char*) pj_pool_getobjname_imp(pj_pool_t *pool); /* Reset pool */ PJ_DECL(void) pj_pool_reset_imp(pj_pool_t *pool); /* Get capacity */ PJ_DECL(pj_size_t) pj_pool_get_capacity_imp(pj_pool_t *pool); /* Get total used size */ PJ_DECL(pj_size_t) pj_pool_get_used_size_imp(pj_pool_t *pool); /* Allocate memory from the pool */ PJ_DECL(void*) pj_pool_alloc_imp(const char *file, int line, pj_pool_t *pool, pj_size_t sz); /* Allocate memory from the pool and zero the memory */ PJ_DECL(void*) pj_pool_calloc_imp(const char *file, int line, pj_pool_t *pool, unsigned cnt, unsigned elemsz); /* Allocate memory from the pool and zero the memory */ PJ_DECL(void*) pj_pool_zalloc_imp(const char *file, int line, pj_pool_t *pool, pj_size_t sz); #define PJ_POOL_ZALLOC_T(pool,type) \ ((type*)pj_pool_zalloc(pool, sizeof(type))) #define PJ_POOL_ALLOC_T(pool,type) \ ((type*)pj_pool_alloc(pool, sizeof(type))) #ifndef PJ_POOL_ALIGNMENT # define PJ_POOL_ALIGNMENT 4 #endif /** * This structure declares pool factory interface. */ typedef struct pj_pool_factory_policy { /** * Allocate memory block (for use by pool). This function is called * by memory pool to allocate memory block. * * @param factory Pool factory. * @param size The size of memory block to allocate. * * @return Memory block. */ void* (*block_alloc)(pj_pool_factory *factory, pj_size_t size); /** * Free memory block. * * @param factory Pool factory. * @param mem Memory block previously allocated by block_alloc(). * @param size The size of memory block. */ void (*block_free)(pj_pool_factory *factory, void *mem, pj_size_t size); /** * Default callback to be called when memory allocation fails. */ pj_pool_callback *callback; /** * Option flags. */ unsigned flags; } pj_pool_factory_policy; struct pj_pool_factory { pj_pool_factory_policy policy; int dummy; }; struct pj_caching_pool { pj_pool_factory factory; /* just to make it compilable */ unsigned used_count; unsigned used_size; unsigned peak_used_size; }; /* just to make it compilable */ typedef struct pj_pool_block { int dummy; } pj_pool_block; #define pj_caching_pool_init( cp, pol, mac) #define pj_caching_pool_destroy(cp) #define pj_pool_factory_dump(pf, detail) PJ_END_DECL #endif /* __PJ_POOL_ALT_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/pool_buf.h ================================================ /* $Id: pool_buf.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __POOL_STACK_H__ #define __POOL_STACK_H__ #include /** * @defgroup PJ_POOL_BUFFER Stack/Buffer Based Memory Pool Allocator * @ingroup PJ_POOL_GROUP * @brief Stack/buffer based pool. * * This section describes an implementation of memory pool which uses * memory allocated from the stack. Application creates this pool * by specifying a buffer (which can be allocated from static memory or * stack variable), and then use normal pool API to access/use the pool. * * If the buffer specified during pool creation is a buffer located in the * stack, the pool will be invalidated (or implicitly destroyed) when the * execution leaves the enclosing block containing the buffer. Note * that application must make sure that any objects allocated from this * pool (such as mutexes) have been destroyed before the pool gets * invalidated. * * Sample usage: * * \code #include static void test() { char buffer[500]; pj_pool_t *pool; void *p; pool = pj_pool_create_on_buf("thepool", buffer, sizeof(buffer)); // Use the pool as usual p = pj_pool_alloc(pool, ...); ... // No need to release the pool } int main() { pj_init(); test(); return 0; } \endcode * * @{ */ PJ_BEGIN_DECL /** * Create the pool using the specified buffer as the pool's memory. * Subsequent allocations made from the pool will use the memory from * this buffer. * * If the buffer specified in the parameter is a buffer located in the * stack, the pool will be invalid (or implicitly destroyed) when the * execution leaves the enclosing block containing the buffer. Note * that application must make sure that any objects allocated from this * pool (such as mutexes) have been destroyed before the pool gets * invalidated. * * @param name Optional pool name. * @param buf Buffer to be used by the pool. * @param size The size of the buffer. * * @return The memory pool instance. */ PJ_DECL(pj_pool_t*) pj_pool_create_on_buf(const char *name, void *buf, pj_size_t size); PJ_END_DECL /** * @} */ #endif /* __POOL_STACK_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/pool_i.h ================================================ /* $Id: pool_i.h 4298 2012-11-22 05:00:01Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include PJ_IDEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool ) { return pool->capacity; } PJ_IDEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool ) { pj_pool_block *b = pool->block_list.next; pj_size_t used_size = sizeof(pj_pool_t); while (b != &pool->block_list) { used_size += (b->cur - b->buf) + sizeof(pj_pool_block); b = b->next; } return used_size; } PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_block *block, pj_size_t size ) { /* The operation below is valid for size==0. * When size==0, the function will return the pointer to the pool * memory address, but no memory will be allocated. */ if (size & (PJ_POOL_ALIGNMENT-1)) { size = (size + PJ_POOL_ALIGNMENT) & ~(PJ_POOL_ALIGNMENT-1); } if ((pj_size_t)(block->end - block->cur) >= size) { void *ptr = block->cur; block->cur += size; return ptr; } return NULL; } PJ_IDEF(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size) { void *ptr = pj_pool_alloc_from_block(pool->block_list.next, size); if (!ptr) ptr = pj_pool_allocate_find(pool, size); return ptr; } PJ_IDEF(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count, pj_size_t size) { void *buf = pj_pool_alloc( pool, size*count); if (buf) pj_bzero(buf, size * count); return buf; } PJ_IDEF(const char *) pj_pool_getobjname( const pj_pool_t *pool ) { return pool->obj_name; } PJ_IDEF(pj_pool_t*) pj_pool_create( pj_pool_factory *f, const char *name, pj_size_t initial_size, pj_size_t increment_size, pj_pool_callback *callback) { return (*f->create_pool)(f, name, initial_size, increment_size, callback); } PJ_IDEF(void) pj_pool_release( pj_pool_t *pool ) { if (pool->factory->release_pool) (*pool->factory->release_pool)(pool->factory, pool); } ================================================ FILE: deps/pjsip/pjlib/include/pj/rand.h ================================================ /* $Id: rand.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_RAND_H__ #define __PJ_RAND_H__ /** * @file rand.h * @brief Random Number Generator. */ #include PJ_BEGIN_DECL /** * @defgroup PJ_RAND Random Number Generator * @ingroup PJ_MISC * @{ * This module contains functions for generating random numbers. * This abstraction is needed not only because not all platforms have * \a rand() and \a srand(), but also on some platforms \a rand() * only has 16-bit randomness, which is not good enough. */ /** * Put in seed to random number generator. * * @param seed Seed value. */ PJ_DECL(void) pj_srand(unsigned int seed); /** * Generate random integer with 32bit randomness. * * @return a random integer. */ PJ_DECL(int) pj_rand(void); /** @} */ PJ_END_DECL #endif /* __PJ_RAND_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/rbtree.h ================================================ /* $Id: rbtree.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_RBTREE_H__ #define __PJ_RBTREE_H__ /** * @file rbtree.h * @brief Red/Black Tree */ #include PJ_BEGIN_DECL /** * @defgroup PJ_RBTREE Red/Black Balanced Tree * @ingroup PJ_DS * @brief * Red/Black tree is the variant of balanced tree, where the search, insert, * and delete operation is \b guaranteed to take at most \a O( lg(n) ). * @{ */ /** * Color type for Red-Black tree. */ typedef enum pj_rbcolor_t { PJ_RBCOLOR_BLACK, PJ_RBCOLOR_RED } pj_rbcolor_t; /** * The type of the node of the R/B Tree. */ typedef struct pj_rbtree_node { /** Pointers to the node's parent, and left and right siblings. */ struct pj_rbtree_node *parent, *left, *right; /** Key associated with the node. */ const void *key; /** User data associated with the node. */ void *user_data; /** The R/B Tree node color. */ pj_rbcolor_t color; } pj_rbtree_node; /** * The type of function use to compare key value of tree node. * @return * 0 if the keys are equal * <0 if key1 is lower than key2 * >0 if key1 is greater than key2. */ typedef int pj_rbtree_comp(const void *key1, const void *key2); /** * Declaration of a red-black tree. All elements in the tree must have UNIQUE * key. * A red black tree always maintains the balance of the tree, so that the * tree height will not be greater than lg(N). Insert, search, and delete * operation will take lg(N) on the worst case. But for insert and delete, * there is additional time needed to maintain the balance of the tree. */ typedef struct pj_rbtree { pj_rbtree_node null_node; /**< Constant to indicate NULL node. */ pj_rbtree_node *null; /**< Constant to indicate NULL node. */ pj_rbtree_node *root; /**< Root tree node. */ unsigned size; /**< Number of elements in the tree. */ pj_rbtree_comp *comp; /**< Key comparison function. */ } pj_rbtree; /** * Guidance on how much memory required for each of the node. */ #define PJ_RBTREE_NODE_SIZE (sizeof(pj_rbtree_node)) /** * Guidance on memory required for the tree. */ #define PJ_RBTREE_SIZE (sizeof(pj_rbtree)) /** * Initialize the tree. * @param tree the tree to be initialized. * @param comp key comparison function to be used for this tree. */ PJ_DECL(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp); /** * Get the first element in the tree. * The first element always has the least value for the key, according to * the comparison function. * @param tree the tree. * @return the tree node, or NULL if the tree has no element. */ PJ_DECL(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree ); /** * Get the last element in the tree. * The last element always has the greatest key value, according to the * comparison function defined for the tree. * @param tree the tree. * @return the tree node, or NULL if the tree has no element. */ PJ_DECL(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree ); /** * Get the successive element for the specified node. * The successive element is an element with greater key value. * @param tree the tree. * @param node the node. * @return the successive node, or NULL if the node has no successor. */ PJ_DECL(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree, pj_rbtree_node *node ); /** * The the previous node for the specified node. * The previous node is an element with less key value. * @param tree the tree. * @param node the node. * @return the previous node, or NULL if the node has no previous node. */ PJ_DECL(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree, pj_rbtree_node *node ); /** * Insert a new node. * The node will be inserted at sorted location. The key of the node must * be UNIQUE, i.e. it hasn't existed in the tree. * @param tree the tree. * @param node the node to be inserted. * @return zero on success, or -1 if the key already exist. */ PJ_DECL(int) pj_rbtree_insert( pj_rbtree *tree, pj_rbtree_node *node ); /** * Find a node which has the specified key. * @param tree the tree. * @param key the key to search. * @return the tree node with the specified key, or NULL if the key can not * be found. */ PJ_DECL(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree, const void *key ); /** * Erase a node from the tree. * @param tree the tree. * @param node the node to be erased. * @return the tree node itself. */ PJ_DECL(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree, pj_rbtree_node *node ); /** * Get the maximum tree height from the specified node. * @param tree the tree. * @param node the node, or NULL to get the root of the tree. * @return the maximum height, which should be at most lg(N) */ PJ_DECL(unsigned) pj_rbtree_max_height( pj_rbtree *tree, pj_rbtree_node *node ); /** * Get the minumum tree height from the specified node. * @param tree the tree. * @param node the node, or NULL to get the root of the tree. * @return the height */ PJ_DECL(unsigned) pj_rbtree_min_height( pj_rbtree *tree, pj_rbtree_node *node ); /** * @} */ PJ_END_DECL #endif /* __PJ_RBTREE_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/sock.h ================================================ /* $Id: sock.h 4343 2013-02-07 09:35:34Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_SOCK_H__ #define __PJ_SOCK_H__ /** * @file sock.h * @brief Socket Abstraction. */ #include PJ_BEGIN_DECL /** * @defgroup PJ_SOCK Socket Abstraction * @ingroup PJ_IO * @{ * * The PJLIB socket abstraction layer is a thin and very portable abstraction * for socket API. It provides API similar to BSD socket API. The abstraction * is needed because BSD socket API is not always available on all platforms, * therefore it wouldn't be possible to create a trully portable network * programs unless we provide such abstraction. * * Applications can use this API directly in their application, just * as they would when using traditional BSD socket API, provided they * call #pj_init() first. * * \section pj_sock_examples_sec Examples * * For some examples on how to use the socket API, please see: * * - \ref page_pjlib_sock_test * - \ref page_pjlib_select_test * - \ref page_pjlib_sock_perf_test */ /** * Supported address families. * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL AF_*, BECAUSE * THE LIBRARY WILL DO TRANSLATION TO THE NATIVE VALUE. */ /** Address family is unspecified. @see pj_AF_UNSPEC() */ extern const pj_uint16_t PJ_AF_UNSPEC; /** Unix domain socket. @see pj_AF_UNIX() */ extern const pj_uint16_t PJ_AF_UNIX; /** POSIX name for AF_UNIX */ #define PJ_AF_LOCAL PJ_AF_UNIX; /** Internet IP protocol. @see pj_AF_INET() */ extern const pj_uint16_t PJ_AF_INET; /** IP version 6. @see pj_AF_INET6() */ extern const pj_uint16_t PJ_AF_INET6; /** Packet family. @see pj_AF_PACKET() */ extern const pj_uint16_t PJ_AF_PACKET; /** IRDA sockets. @see pj_AF_IRDA() */ extern const pj_uint16_t PJ_AF_IRDA; /* * Accessor functions for various address family constants. These * functions are provided because Symbian doesn't allow exporting * global variables from a DLL. */ #if defined(PJ_DLL) /** Get #PJ_AF_UNSPEC value */ PJ_DECL(pj_uint16_t) pj_AF_UNSPEC(void); /** Get #PJ_AF_UNIX value. */ PJ_DECL(pj_uint16_t) pj_AF_UNIX(void); /** Get #PJ_AF_INET value. */ PJ_DECL(pj_uint16_t) pj_AF_INET(void); /** Get #PJ_AF_INET6 value. */ PJ_DECL(pj_uint16_t) pj_AF_INET6(void); /** Get #PJ_AF_PACKET value. */ PJ_DECL(pj_uint16_t) pj_AF_PACKET(void); /** Get #PJ_AF_IRDA value. */ PJ_DECL(pj_uint16_t) pj_AF_IRDA(void); #else /* When pjlib is not built as DLL, these accessor functions are * simply a macro to get their constants */ /** Get #PJ_AF_UNSPEC value */ # define pj_AF_UNSPEC() PJ_AF_UNSPEC /** Get #PJ_AF_UNIX value. */ # define pj_AF_UNIX() PJ_AF_UNIX /** Get #PJ_AF_INET value. */ # define pj_AF_INET() PJ_AF_INET /** Get #PJ_AF_INET6 value. */ # define pj_AF_INET6() PJ_AF_INET6 /** Get #PJ_AF_PACKET value. */ # define pj_AF_PACKET() PJ_AF_PACKET /** Get #PJ_AF_IRDA value. */ # define pj_AF_IRDA() PJ_AF_IRDA #endif /** * Supported types of sockets. * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL SOCK_*, BECAUSE * THE LIBRARY WILL TRANSLATE THE VALUE TO THE NATIVE VALUE. */ /** Sequenced, reliable, connection-based byte streams. * @see pj_SOCK_STREAM() */ extern const pj_uint16_t PJ_SOCK_STREAM; /** Connectionless, unreliable datagrams of fixed maximum lengths. * @see pj_SOCK_DGRAM() */ extern const pj_uint16_t PJ_SOCK_DGRAM; /** Raw protocol interface. @see pj_SOCK_RAW() */ extern const pj_uint16_t PJ_SOCK_RAW; /** Reliably-delivered messages. @see pj_SOCK_RDM() */ extern const pj_uint16_t PJ_SOCK_RDM; /* * Accessor functions for various constants. These functions are provided * because Symbian doesn't allow exporting global variables from a DLL. */ #if defined(PJ_DLL) /** Get #PJ_SOCK_STREAM constant */ PJ_DECL(int) pj_SOCK_STREAM(void); /** Get #PJ_SOCK_DGRAM constant */ PJ_DECL(int) pj_SOCK_DGRAM(void); /** Get #PJ_SOCK_RAW constant */ PJ_DECL(int) pj_SOCK_RAW(void); /** Get #PJ_SOCK_RDM constant */ PJ_DECL(int) pj_SOCK_RDM(void); #else /** Get #PJ_SOCK_STREAM constant */ # define pj_SOCK_STREAM() PJ_SOCK_STREAM /** Get #PJ_SOCK_DGRAM constant */ # define pj_SOCK_DGRAM() PJ_SOCK_DGRAM /** Get #PJ_SOCK_RAW constant */ # define pj_SOCK_RAW() PJ_SOCK_RAW /** Get #PJ_SOCK_RDM constant */ # define pj_SOCK_RDM() PJ_SOCK_RDM #endif /** * Socket level specified in #pj_sock_setsockopt() or #pj_sock_getsockopt(). * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL SOL_*, BECAUSE * THE LIBRARY WILL TRANSLATE THE VALUE TO THE NATIVE VALUE. */ /** Socket level. @see pj_SOL_SOCKET() */ extern const pj_uint16_t PJ_SOL_SOCKET; /** IP level. @see pj_SOL_IP() */ extern const pj_uint16_t PJ_SOL_IP; /** TCP level. @see pj_SOL_TCP() */ extern const pj_uint16_t PJ_SOL_TCP; /** UDP level. @see pj_SOL_UDP() */ extern const pj_uint16_t PJ_SOL_UDP; /** IP version 6. @see pj_SOL_IPV6() */ extern const pj_uint16_t PJ_SOL_IPV6; /* * Accessor functions for various constants. These functions are provided * because Symbian doesn't allow exporting global variables from a DLL. */ #if defined(PJ_DLL) /** Get #PJ_SOL_SOCKET constant */ PJ_DECL(pj_uint16_t) pj_SOL_SOCKET(void); /** Get #PJ_SOL_IP constant */ PJ_DECL(pj_uint16_t) pj_SOL_IP(void); /** Get #PJ_SOL_TCP constant */ PJ_DECL(pj_uint16_t) pj_SOL_TCP(void); /** Get #PJ_SOL_UDP constant */ PJ_DECL(pj_uint16_t) pj_SOL_UDP(void); /** Get #PJ_SOL_IPV6 constant */ PJ_DECL(pj_uint16_t) pj_SOL_IPV6(void); #else /** Get #PJ_SOL_SOCKET constant */ # define pj_SOL_SOCKET() PJ_SOL_SOCKET /** Get #PJ_SOL_IP constant */ # define pj_SOL_IP() PJ_SOL_IP /** Get #PJ_SOL_TCP constant */ # define pj_SOL_TCP() PJ_SOL_TCP /** Get #PJ_SOL_UDP constant */ # define pj_SOL_UDP() PJ_SOL_UDP /** Get #PJ_SOL_IPV6 constant */ # define pj_SOL_IPV6() PJ_SOL_IPV6 #endif /* IP_TOS * * Note: * TOS CURRENTLY DOES NOT WORK IN Windows 2000 and above! * See http://support.microsoft.com/kb/248611 */ /** IP_TOS optname in setsockopt(). @see pj_IP_TOS() */ extern const pj_uint16_t PJ_IP_TOS; /* * IP TOS related constats. * * Note: * TOS CURRENTLY DOES NOT WORK IN Windows 2000 and above! * See http://support.microsoft.com/kb/248611 */ /** Minimize delays. @see pj_IPTOS_LOWDELAY() */ extern const pj_uint16_t PJ_IPTOS_LOWDELAY; /** Optimize throughput. @see pj_IPTOS_THROUGHPUT() */ extern const pj_uint16_t PJ_IPTOS_THROUGHPUT; /** Optimize for reliability. @see pj_IPTOS_RELIABILITY() */ extern const pj_uint16_t PJ_IPTOS_RELIABILITY; /** "filler data" where slow transmission does't matter. * @see pj_IPTOS_MINCOST() */ extern const pj_uint16_t PJ_IPTOS_MINCOST; #if defined(PJ_DLL) /** Get #PJ_IP_TOS constant */ PJ_DECL(int) pj_IP_TOS(void); /** Get #PJ_IPTOS_LOWDELAY constant */ PJ_DECL(int) pj_IPTOS_LOWDELAY(void); /** Get #PJ_IPTOS_THROUGHPUT constant */ PJ_DECL(int) pj_IPTOS_THROUGHPUT(void); /** Get #PJ_IPTOS_RELIABILITY constant */ PJ_DECL(int) pj_IPTOS_RELIABILITY(void); /** Get #PJ_IPTOS_MINCOST constant */ PJ_DECL(int) pj_IPTOS_MINCOST(void); #else /** Get #PJ_IP_TOS constant */ # define pj_IP_TOS() PJ_IP_TOS /** Get #PJ_IPTOS_LOWDELAY constant */ # define pj_IPTOS_LOWDELAY() PJ_IP_TOS_LOWDELAY /** Get #PJ_IPTOS_THROUGHPUT constant */ # define pj_IPTOS_THROUGHPUT() PJ_IP_TOS_THROUGHPUT /** Get #PJ_IPTOS_RELIABILITY constant */ # define pj_IPTOS_RELIABILITY() PJ_IP_TOS_RELIABILITY /** Get #PJ_IPTOS_MINCOST constant */ # define pj_IPTOS_MINCOST() PJ_IP_TOS_MINCOST #endif /** * Values to be specified as \c optname when calling #pj_sock_setsockopt() * or #pj_sock_getsockopt(). */ /** Socket type. @see pj_SO_TYPE() */ extern const pj_uint16_t PJ_SO_TYPE; /** Buffer size for receive. @see pj_SO_RCVBUF() */ extern const pj_uint16_t PJ_SO_RCVBUF; /** Buffer size for send. @see pj_SO_SNDBUF() */ extern const pj_uint16_t PJ_SO_SNDBUF; /** Disables the Nagle algorithm for send coalescing. @see pj_TCP_NODELAY */ extern const pj_uint16_t PJ_TCP_NODELAY; /** Allows the socket to be bound to an address that is already in use. * @see pj_SO_REUSEADDR */ extern const pj_uint16_t PJ_SO_REUSEADDR; /** Do not generate SIGPIPE. @see pj_SO_NOSIGPIPE */ extern const pj_uint16_t PJ_SO_NOSIGPIPE; /** Set the protocol-defined priority for all packets to be sent on socket. */ extern const pj_uint16_t PJ_SO_PRIORITY; /** IP multicast interface. @see pj_IP_MULTICAST_IF() */ extern const pj_uint16_t PJ_IP_MULTICAST_IF; /** IP multicast ttl. @see pj_IP_MULTICAST_TTL() */ extern const pj_uint16_t PJ_IP_MULTICAST_TTL; /** IP multicast loopback. @see pj_IP_MULTICAST_LOOP() */ extern const pj_uint16_t PJ_IP_MULTICAST_LOOP; /** Add an IP group membership. @see pj_IP_ADD_MEMBERSHIP() */ extern const pj_uint16_t PJ_IP_ADD_MEMBERSHIP; /** Drop an IP group membership. @see pj_IP_DROP_MEMBERSHIP() */ extern const pj_uint16_t PJ_IP_DROP_MEMBERSHIP; #if defined(PJ_DLL) /** Get #PJ_SO_TYPE constant */ PJ_DECL(pj_uint16_t) pj_SO_TYPE(void); /** Get #PJ_SO_RCVBUF constant */ PJ_DECL(pj_uint16_t) pj_SO_RCVBUF(void); /** Get #PJ_SO_SNDBUF constant */ PJ_DECL(pj_uint16_t) pj_SO_SNDBUF(void); /** Get #PJ_TCP_NODELAY constant */ PJ_DECL(pj_uint16_t) pj_TCP_NODELAY(void); /** Get #PJ_SO_REUSEADDR constant */ PJ_DECL(pj_uint16_t) pj_SO_REUSEADDR(void); /** Get #PJ_SO_NOSIGPIPE constant */ PJ_DECL(pj_uint16_t) pj_SO_NOSIGPIPE(void); /** Get #PJ_SO_PRIORITY constant */ PJ_DECL(pj_uint16_t) pj_SO_PRIORITY(void); /** Get #PJ_IP_MULTICAST_IF constant */ PJ_DECL(pj_uint16_t) pj_IP_MULTICAST_IF(void); /** Get #PJ_IP_MULTICAST_TTL constant */ PJ_DECL(pj_uint16_t) pj_IP_MULTICAST_TTL(void); /** Get #PJ_IP_MULTICAST_LOOP constant */ PJ_DECL(pj_uint16_t) pj_IP_MULTICAST_LOOP(void); /** Get #PJ_IP_ADD_MEMBERSHIP constant */ PJ_DECL(pj_uint16_t) pj_IP_ADD_MEMBERSHIP(void); /** Get #PJ_IP_DROP_MEMBERSHIP constant */ PJ_DECL(pj_uint16_t) pj_IP_DROP_MEMBERSHIP(void); #else /** Get #PJ_SO_TYPE constant */ # define pj_SO_TYPE() PJ_SO_TYPE /** Get #PJ_SO_RCVBUF constant */ # define pj_SO_RCVBUF() PJ_SO_RCVBUF /** Get #PJ_SO_SNDBUF constant */ # define pj_SO_SNDBUF() PJ_SO_SNDBUF /** Get #PJ_TCP_NODELAY constant */ # define pj_TCP_NODELAY() PJ_TCP_NODELAY /** Get #PJ_SO_REUSEADDR constant */ # define pj_SO_REUSEADDR() PJ_SO_REUSEADDR /** Get #PJ_SO_NOSIGPIPE constant */ # define pj_SO_NOSIGPIPE() PJ_SO_NOSIGPIPE /** Get #PJ_SO_PRIORITY constant */ # define pj_SO_PRIORITY() PJ_SO_PRIORITY /** Get #PJ_IP_MULTICAST_IF constant */ # define pj_IP_MULTICAST_IF() PJ_IP_MULTICAST_IF /** Get #PJ_IP_MULTICAST_TTL constant */ # define pj_IP_MULTICAST_TTL() PJ_IP_MULTICAST_TTL /** Get #PJ_IP_MULTICAST_LOOP constant */ # define pj_IP_MULTICAST_LOOP() PJ_IP_MULTICAST_LOOP /** Get #PJ_IP_ADD_MEMBERSHIP constant */ # define pj_IP_ADD_MEMBERSHIP() PJ_IP_ADD_MEMBERSHIP /** Get #PJ_IP_DROP_MEMBERSHIP constant */ # define pj_IP_DROP_MEMBERSHIP() PJ_IP_DROP_MEMBERSHIP #endif /* * Flags to be specified in #pj_sock_recv, #pj_sock_send, etc. */ /** Out-of-band messages. @see pj_MSG_OOB() */ extern const int PJ_MSG_OOB; /** Peek, don't remove from buffer. @see pj_MSG_PEEK() */ extern const int PJ_MSG_PEEK; /** Don't route. @see pj_MSG_DONTROUTE() */ extern const int PJ_MSG_DONTROUTE; #if defined(PJ_DLL) /** Get #PJ_MSG_OOB constant */ PJ_DECL(int) pj_MSG_OOB(void); /** Get #PJ_MSG_PEEK constant */ PJ_DECL(int) pj_MSG_PEEK(void); /** Get #PJ_MSG_DONTROUTE constant */ PJ_DECL(int) pj_MSG_DONTROUTE(void); #else /** Get #PJ_MSG_OOB constant */ # define pj_MSG_OOB() PJ_MSG_OOB /** Get #PJ_MSG_PEEK constant */ # define pj_MSG_PEEK() PJ_MSG_PEEK /** Get #PJ_MSG_DONTROUTE constant */ # define pj_MSG_DONTROUTE() PJ_MSG_DONTROUTE #endif /** * Flag to be specified in #pj_sock_shutdown(). */ typedef enum pj_socket_sd_type { PJ_SD_RECEIVE = 0, /**< No more receive. */ PJ_SHUT_RD = 0, /**< Alias for SD_RECEIVE. */ PJ_SD_SEND = 1, /**< No more sending. */ PJ_SHUT_WR = 1, /**< Alias for SD_SEND. */ PJ_SD_BOTH = 2, /**< No more send and receive. */ PJ_SHUT_RDWR = 2 /**< Alias for SD_BOTH. */ } pj_socket_sd_type; /** Address to accept any incoming messages. */ #define PJ_INADDR_ANY ((pj_uint32_t)0) /** Address indicating an error return */ #define PJ_INADDR_NONE ((pj_uint32_t)0xffffffff) /** Address to send to all hosts. */ #define PJ_INADDR_BROADCAST ((pj_uint32_t)0xffffffff) /** * Maximum length specifiable by #pj_sock_listen(). * If the build system doesn't override this value, then the lowest * denominator (five, in Win32 systems) will be used. */ #if !defined(PJ_SOMAXCONN) # define PJ_SOMAXCONN 5 #endif /** * Constant for invalid socket returned by #pj_sock_socket() and * #pj_sock_accept(). */ #define PJ_INVALID_SOCKET (-1) /* Must undefine s_addr because of pj_in_addr below */ #undef s_addr /** * This structure describes Internet address. */ typedef struct pj_in_addr { pj_uint32_t s_addr; /**< The 32bit IP address. */ } pj_in_addr; /** * Maximum length of text representation of an IPv4 address. */ #define PJ_INET_ADDRSTRLEN 16 /** * Maximum length of text representation of an IPv6 address. */ #define PJ_INET6_ADDRSTRLEN 46 /** * The size of sin_zero field in pj_sockaddr_in structure. Most OSes * use 8, but others such as the BSD TCP/IP stack in eCos uses 24. */ #ifndef PJ_SOCKADDR_IN_SIN_ZERO_LEN # define PJ_SOCKADDR_IN_SIN_ZERO_LEN 8 #endif /** * This structure describes Internet socket address. * If PJ_SOCKADDR_HAS_LEN is not zero, then sin_zero_len member is added * to this struct. As far the application is concerned, the value of * this member will always be zero. Internally, PJLIB may modify the value * before calling OS socket API, and reset the value back to zero before * returning the struct to application. */ struct pj_sockaddr_in { #if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0 pj_uint8_t sin_zero_len; /**< Just ignore this. */ pj_uint8_t sin_family; /**< Address family. */ #else pj_uint16_t sin_family; /**< Address family. */ #endif pj_uint16_t sin_port; /**< Transport layer port number. */ pj_in_addr sin_addr; /**< IP address. */ char sin_zero[PJ_SOCKADDR_IN_SIN_ZERO_LEN]; /**< Padding.*/ }; #undef s6_addr /** * This structure describes IPv6 address. */ typedef union pj_in6_addr { /* This is the main entry */ pj_uint8_t s6_addr[16]; /**< 8-bit array */ /* While these are used for proper alignment */ pj_uint32_t u6_addr32[4]; /* Do not use this with Winsock2, as this will align pj_sockaddr_in6 * to 64-bit boundary and Winsock2 doesn't like it! * Update 26/04/2010: * This is now disabled, see http://trac.pjsip.org/repos/ticket/1058 */ #if 0 && defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0 && \ (!defined(PJ_WIN32) || PJ_WIN32==0) pj_int64_t u6_addr64[2]; #endif } pj_in6_addr; /** Initializer value for pj_in6_addr. */ #define PJ_IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } } /** Initializer value for pj_in6_addr. */ #define PJ_IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } /** * This structure describes IPv6 socket address. * If PJ_SOCKADDR_HAS_LEN is not zero, then sin_zero_len member is added * to this struct. As far the application is concerned, the value of * this member will always be zero. Internally, PJLIB may modify the value * before calling OS socket API, and reset the value back to zero before * returning the struct to application. */ typedef struct pj_sockaddr_in6 { #if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0 pj_uint8_t sin6_zero_len; /**< Just ignore this. */ pj_uint8_t sin6_family; /**< Address family. */ #else pj_uint16_t sin6_family; /**< Address family */ #endif pj_uint16_t sin6_port; /**< Transport layer port number. */ pj_uint32_t sin6_flowinfo; /**< IPv6 flow information */ pj_in6_addr sin6_addr; /**< IPv6 address. */ pj_uint32_t sin6_scope_id; /**< Set of interfaces for a scope */ } pj_sockaddr_in6; /** * This structure describes common attributes found in transport addresses. * If PJ_SOCKADDR_HAS_LEN is not zero, then sa_zero_len member is added * to this struct. As far the application is concerned, the value of * this member will always be zero. Internally, PJLIB may modify the value * before calling OS socket API, and reset the value back to zero before * returning the struct to application. */ typedef struct pj_addr_hdr { #if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0 pj_uint8_t sa_zero_len; pj_uint8_t sa_family; #else pj_uint16_t sa_family; /**< Common data: address family. */ #endif } pj_addr_hdr; /** * This union describes a generic socket address. */ typedef union pj_sockaddr { pj_addr_hdr addr; /**< Generic transport address. */ pj_sockaddr_in ipv4; /**< IPv4 transport address. */ pj_sockaddr_in6 ipv6; /**< IPv6 transport address. */ } pj_sockaddr; /** * This structure provides multicast group information for IPv4 addresses. */ typedef struct pj_ip_mreq { pj_in_addr imr_multiaddr; /**< IP multicast address of group. */ pj_in_addr imr_interface; /**< local IP address of interface. */ } pj_ip_mreq; /* Maximum number of socket options. */ #define PJ_MAX_SOCKOPT_PARAMS 4 /** * Options to be set for the socket. */ typedef struct pj_sockopt_params { /* The number of options to be applied. */ unsigned cnt; /* Array of options to be applied. */ struct { /* The level at which the option is defined. */ int level; /* Option name. */ int optname; /* Pointer to the buffer in which the option is specified. */ void *optval; /* Buffer size of the buffer pointed by optval. */ int optlen; } options[PJ_MAX_SOCKOPT_PARAMS]; } pj_sockopt_params; /***************************************************************************** * * SOCKET ADDRESS MANIPULATION. * ***************************************************************************** */ /** * Convert 16-bit value from network byte order to host byte order. * * @param netshort 16-bit network value. * @return 16-bit host value. */ PJ_DECL(pj_uint16_t) pj_ntohs(pj_uint16_t netshort); /** * Convert 16-bit value from host byte order to network byte order. * * @param hostshort 16-bit host value. * @return 16-bit network value. */ PJ_DECL(pj_uint16_t) pj_htons(pj_uint16_t hostshort); /** * Convert 32-bit value from network byte order to host byte order. * * @param netlong 32-bit network value. * @return 32-bit host value. */ PJ_DECL(pj_uint32_t) pj_ntohl(pj_uint32_t netlong); /** * Convert 32-bit value from host byte order to network byte order. * * @param hostlong 32-bit host value. * @return 32-bit network value. */ PJ_DECL(pj_uint32_t) pj_htonl(pj_uint32_t hostlong); /** * Convert an Internet host address given in network byte order * to string in standard numbers and dots notation. * * @param inaddr The host address. * @return The string address. */ PJ_DECL(char*) pj_inet_ntoa(pj_in_addr inaddr); /** * This function converts the Internet host address cp from the standard * numbers-and-dots notation into binary data and stores it in the structure * that inp points to. * * @param cp IP address in standard numbers-and-dots notation. * @param inp Structure that holds the output of the conversion. * * @return nonzero if the address is valid, zero if not. */ PJ_DECL(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp); /** * This function converts an address in its standard text presentation form * into its numeric binary form. It supports both IPv4 and IPv6 address * conversion. * * @param af Specify the family of the address. The PJ_AF_INET and * PJ_AF_INET6 address families shall be supported. * @param src Points to the string being passed in. * @param dst Points to a buffer into which the function stores the * numeric address; this shall be large enough to hold the * numeric address (32 bits for PJ_AF_INET, 128 bits for * PJ_AF_INET6). * * @return PJ_SUCCESS if conversion was successful. */ PJ_DECL(pj_status_t) pj_inet_pton(int af, const pj_str_t *src, void *dst); /** * This function converts a numeric address into a text string suitable * for presentation. It supports both IPv4 and IPv6 address * conversion. * @see pj_sockaddr_print() * * @param af Specify the family of the address. This can be PJ_AF_INET * or PJ_AF_INET6. * @param src Points to a buffer holding an IPv4 address if the af argument * is PJ_AF_INET, or an IPv6 address if the af argument is * PJ_AF_INET6; the address must be in network byte order. * @param dst Points to a buffer where the function stores the resulting * text string; it shall not be NULL. * @param size Specifies the size of this buffer, which shall be large * enough to hold the text string (PJ_INET_ADDRSTRLEN characters * for IPv4, PJ_INET6_ADDRSTRLEN characters for IPv6). * * @return PJ_SUCCESS if conversion was successful. */ PJ_DECL(pj_status_t) pj_inet_ntop(int af, const void *src, char *dst, int size); /** * Converts numeric address into its text string representation. * @see pj_sockaddr_print() * * @param af Specify the family of the address. This can be PJ_AF_INET * or PJ_AF_INET6. * @param src Points to a buffer holding an IPv4 address if the af argument * is PJ_AF_INET, or an IPv6 address if the af argument is * PJ_AF_INET6; the address must be in network byte order. * @param dst Points to a buffer where the function stores the resulting * text string; it shall not be NULL. * @param size Specifies the size of this buffer, which shall be large * enough to hold the text string (PJ_INET_ADDRSTRLEN characters * for IPv4, PJ_INET6_ADDRSTRLEN characters for IPv6). * * @return The address string or NULL if failed. */ PJ_DECL(char*) pj_inet_ntop2(int af, const void *src, char *dst, int size); /** * Print socket address. * * @param addr The socket address. * @param buf Text buffer. * @param size Size of buffer. * @param flags Bitmask combination of these value: * - 1: port number is included. * - 2: square bracket is included for IPv6 address. * * @return The address string. */ PJ_DECL(char*) pj_sockaddr_print(const pj_sockaddr_t *addr, char *buf, int size, unsigned flags); /** * Convert address string with numbers and dots to binary IP address. * * @param cp The IP address in numbers and dots notation. * @return If success, the IP address is returned in network * byte order. If failed, PJ_INADDR_NONE will be * returned. * @remark * This is an obsolete interface to #pj_inet_aton(); it is obsolete * because -1 is a valid address (255.255.255.255), and #pj_inet_aton() * provides a cleaner way to indicate error return. */ PJ_DECL(pj_in_addr) pj_inet_addr(const pj_str_t *cp); /** * Convert address string with numbers and dots to binary IP address. * * @param cp The IP address in numbers and dots notation. * @return If success, the IP address is returned in network * byte order. If failed, PJ_INADDR_NONE will be * returned. * @remark * This is an obsolete interface to #pj_inet_aton(); it is obsolete * because -1 is a valid address (255.255.255.255), and #pj_inet_aton() * provides a cleaner way to indicate error return. */ PJ_DECL(pj_in_addr) pj_inet_addr2(const char *cp); /** * Initialize IPv4 socket address based on the address and port info. * The string address may be in a standard numbers and dots notation or * may be a hostname. If hostname is specified, then the function will * resolve the host into the IP address. * * @see pj_sockaddr_init() * * @param addr The IP socket address to be set. * @param cp The address string, which can be in a standard * dotted numbers or a hostname to be resolved. * @param port The port number, in host byte order. * * @return Zero on success. */ PJ_DECL(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr, const pj_str_t *cp, pj_uint16_t port); /** * Initialize IP socket address based on the address and port info. * The string address may be in a standard numbers and dots notation or * may be a hostname. If hostname is specified, then the function will * resolve the host into the IP address. * * @see pj_sockaddr_in_init() * * @param af Internet address family. * @param addr The IP socket address to be set. * @param cp The address string, which can be in a standard * dotted numbers or a hostname to be resolved. * @param port The port number, in host byte order. * * @return Zero on success. */ PJ_DECL(pj_status_t) pj_sockaddr_init(int af, pj_sockaddr *addr, const pj_str_t *cp, pj_uint16_t port); /** * Compare two socket addresses. * * @param addr1 First address. * @param addr2 Second address. * * @return Zero on equal, -1 if addr1 is less than addr2, * and +1 if addr1 is more than addr2. */ PJ_DECL(int) pj_sockaddr_cmp(const pj_sockaddr_t *addr1, const pj_sockaddr_t *addr2); /** * Get pointer to the address part of a socket address. * * @param addr Socket address. * * @return Pointer to address part (sin_addr or sin6_addr, * depending on address family) */ PJ_DECL(void*) pj_sockaddr_get_addr(const pj_sockaddr_t *addr); /** * Check that a socket address contains a non-zero address part. * * @param addr Socket address. * * @return Non-zero if address is set to non-zero. */ PJ_DECL(pj_bool_t) pj_sockaddr_has_addr(const pj_sockaddr_t *addr); /** * Get the address part length of a socket address, based on its address * family. For PJ_AF_INET, the length will be sizeof(pj_in_addr), and * for PJ_AF_INET6, the length will be sizeof(pj_in6_addr). * * @param addr Socket address. * * @return Length in bytes. */ PJ_DECL(unsigned) pj_sockaddr_get_addr_len(const pj_sockaddr_t *addr); /** * Get the socket address length, based on its address * family. For PJ_AF_INET, the length will be sizeof(pj_sockaddr_in), and * for PJ_AF_INET6, the length will be sizeof(pj_sockaddr_in6). * * @param addr Socket address. * * @return Length in bytes. */ PJ_DECL(unsigned) pj_sockaddr_get_len(const pj_sockaddr_t *addr); /** * Copy only the address part (sin_addr/sin6_addr) of a socket address. * * @param dst Destination socket address. * @param src Source socket address. * * @see @pj_sockaddr_cp() */ PJ_DECL(void) pj_sockaddr_copy_addr(pj_sockaddr *dst, const pj_sockaddr *src); /** * Copy socket address. This will copy the whole structure depending * on the address family of the source socket address. * * @param dst Destination socket address. * @param src Source socket address. * * @see @pj_sockaddr_copy_addr() */ PJ_DECL(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src); /** * Get the IP address of an IPv4 socket address. * The address is returned as 32bit value in host byte order. * * @param addr The IP socket address. * @return 32bit address, in host byte order. */ PJ_DECL(pj_in_addr) pj_sockaddr_in_get_addr(const pj_sockaddr_in *addr); /** * Set the IP address of an IPv4 socket address. * * @param addr The IP socket address. * @param hostaddr The host address, in host byte order. */ PJ_DECL(void) pj_sockaddr_in_set_addr(pj_sockaddr_in *addr, pj_uint32_t hostaddr); /** * Set the IP address of an IP socket address from string address, * with resolving the host if necessary. The string address may be in a * standard numbers and dots notation or may be a hostname. If hostname * is specified, then the function will resolve the host into the IP * address. * * @see pj_sockaddr_set_str_addr() * * @param addr The IP socket address to be set. * @param cp The address string, which can be in a standard * dotted numbers or a hostname to be resolved. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, const pj_str_t *cp); /** * Set the IP address of an IPv4 or IPv6 socket address from string address, * with resolving the host if necessary. The string address may be in a * standard IPv6 or IPv6 address or may be a hostname. If hostname * is specified, then the function will resolve the host into the IP * address according to the address family. * * @param af Address family. * @param addr The IP socket address to be set. * @param cp The address string, which can be in a standard * IP numbers (IPv4 or IPv6) or a hostname to be resolved. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pj_sockaddr_set_str_addr(int af, pj_sockaddr *addr, const pj_str_t *cp); /** * Get the port number of a socket address, in host byte order. * This function can be used for both IPv4 and IPv6 socket address. * * @param addr Socket address. * * @return Port number, in host byte order. */ PJ_DECL(pj_uint16_t) pj_sockaddr_get_port(const pj_sockaddr_t *addr); /** * Get the transport layer port number of an Internet socket address. * The port is returned in host byte order. * * @param addr The IP socket address. * @return Port number, in host byte order. */ PJ_DECL(pj_uint16_t) pj_sockaddr_in_get_port(const pj_sockaddr_in *addr); /** * Set the port number of an Internet socket address. * * @param addr The socket address. * @param hostport The port number, in host byte order. */ PJ_DECL(pj_status_t) pj_sockaddr_set_port(pj_sockaddr *addr, pj_uint16_t hostport); /** * Set the port number of an IPv4 socket address. * * @see pj_sockaddr_set_port() * * @param addr The IP socket address. * @param hostport The port number, in host byte order. */ PJ_DECL(void) pj_sockaddr_in_set_port(pj_sockaddr_in *addr, pj_uint16_t hostport); /** * Parse string containing IP address and optional port into socket address, * possibly also with address family detection. This function supports both * IPv4 and IPv6 parsing, however IPv6 parsing may only be done if IPv6 is * enabled during compilation. * * This function supports parsing several formats. Sample IPv4 inputs and * their default results:: * - "10.0.0.1:80": address 10.0.0.1 and port 80. * - "10.0.0.1": address 10.0.0.1 and port zero. * - "10.0.0.1:": address 10.0.0.1 and port zero. * - "10.0.0.1:0": address 10.0.0.1 and port zero. * - ":80": address 0.0.0.0 and port 80. * - ":": address 0.0.0.0 and port 0. * - "localhost": address 127.0.0.1 and port 0. * - "localhost:": address 127.0.0.1 and port 0. * - "localhost:80": address 127.0.0.1 and port 80. * * Sample IPv6 inputs and their default results: * - "[fec0::01]:80": address fec0::01 and port 80 * - "[fec0::01]": address fec0::01 and port 0 * - "[fec0::01]:": address fec0::01 and port 0 * - "[fec0::01]:0": address fec0::01 and port 0 * - "fec0::01": address fec0::01 and port 0 * - "fec0::01:80": address fec0::01:80 and port 0 * - "::": address zero (::) and port 0 * - "[::]": address zero (::) and port 0 * - "[::]:": address zero (::) and port 0 * - ":::": address zero (::) and port 0 * - "[::]:80": address zero (::) and port 0 * - ":::80": address zero (::) and port 80 * * Note: when the IPv6 socket address contains port number, the IP * part of the socket address should be enclosed with square brackets, * otherwise the port number will be included as part of the IP address * (see "fec0::01:80" example above). * * @param af Optionally specify the address family to be used. If the * address family is to be deducted from the input, specify * pj_AF_UNSPEC() here. Other supported values are * #pj_AF_INET() and #pj_AF_INET6() * @param options Additional options to assist the parsing, must be zero * for now. * @param str The input string to be parsed. * @param addr Pointer to store the result. * * @return PJ_SUCCESS if the parsing is successful. * * @see pj_sockaddr_parse2() */ PJ_DECL(pj_status_t) pj_sockaddr_parse(int af, unsigned options, const pj_str_t *str, pj_sockaddr *addr); /** * This function is similar to #pj_sockaddr_parse(), except that it will not * convert the hostpart into IP address (thus possibly resolving the hostname * into a #pj_sockaddr. * * Unlike #pj_sockaddr_parse(), this function has a limitation that if port * number is specified in an IPv6 input string, the IP part of the IPv6 socket * address MUST be enclosed in square brackets, otherwise the port number will * be considered as part of the IPv6 IP address. * * @param af Optionally specify the address family to be used. If the * address family is to be deducted from the input, specify * #pj_AF_UNSPEC() here. Other supported values are * #pj_AF_INET() and #pj_AF_INET6() * @param options Additional options to assist the parsing, must be zero * for now. * @param str The input string to be parsed. * @param hostpart Optional pointer to store the host part of the socket * address, with any brackets removed. * @param port Optional pointer to store the port number. If port number * is not found, this will be set to zero upon return. * @param raf Optional pointer to store the detected address family of * the input address. * * @return PJ_SUCCESS if the parsing is successful. * * @see pj_sockaddr_parse() */ PJ_DECL(pj_status_t) pj_sockaddr_parse2(int af, unsigned options, const pj_str_t *str, pj_str_t *hostpart, pj_uint16_t *port, int *raf); /***************************************************************************** * * HOST NAME AND ADDRESS. * ***************************************************************************** */ /** * Get system's host name. * * @return The hostname, or empty string if the hostname can not * be identified. */ PJ_DECL(const pj_str_t*) pj_gethostname(void); /** * Get host's IP address, which the the first IP address that is resolved * from the hostname. * * @return The host's IP address, PJ_INADDR_NONE if the host * IP address can not be identified. */ PJ_DECL(pj_in_addr) pj_gethostaddr(void); /***************************************************************************** * * SOCKET API. * ***************************************************************************** */ /** * Create new socket/endpoint for communication. * * @param family Specifies a communication domain; this selects the * protocol family which will be used for communication. * @param type The socket has the indicated type, which specifies the * communication semantics. * @param protocol Specifies a particular protocol to be used with the * socket. Normally only a single protocol exists to support * a particular socket type within a given protocol family, * in which a case protocol can be specified as 0. * @param sock New socket descriptor, or PJ_INVALID_SOCKET on error. * * @return Zero on success. */ PJ_DECL(pj_status_t) pj_sock_socket(int family, int type, int protocol, pj_sock_t *sock); /** * Close the socket descriptor. * * @param sockfd The socket descriptor. * * @return Zero on success. */ PJ_DECL(pj_status_t) pj_sock_close(pj_sock_t sockfd); /** * This function gives the socket sockfd the local address my_addr. my_addr is * addrlen bytes long. Traditionally, this is called assigning a name to * a socket. When a socket is created with #pj_sock_socket(), it exists in a * name space (address family) but has no name assigned. * * @param sockfd The socket desriptor. * @param my_addr The local address to bind the socket to. * @param addrlen The length of the address. * * @return Zero on success. */ PJ_DECL(pj_status_t) pj_sock_bind( pj_sock_t sockfd, const pj_sockaddr_t *my_addr, int addrlen); /** * Bind the IP socket sockfd to the given address and port. * * @param sockfd The socket descriptor. * @param addr Local address to bind the socket to, in host byte order. * @param port The local port to bind the socket to, in host byte order. * * @return Zero on success. */ PJ_DECL(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd, pj_uint32_t addr, pj_uint16_t port); /** * Bind the IP socket sockfd to the given address and a random port in the * specified range. * * @param sockfd The socket desriptor. * @param addr The local address and port to bind the socket to. * @param port_range The port range, relative the to start port number * specified in port field in #addr. Note that if the * port is zero, this param will be ignored. * @param max_try Maximum retries. * * @return Zero on success. */ PJ_DECL(pj_status_t) pj_sock_bind_random( pj_sock_t sockfd, const pj_sockaddr_t *addr, pj_uint16_t port_range, pj_uint16_t max_try); #if PJ_HAS_TCP /** * Listen for incoming connection. This function only applies to connection * oriented sockets (such as PJ_SOCK_STREAM or PJ_SOCK_SEQPACKET), and it * indicates the willingness to accept incoming connections. * * @param sockfd The socket descriptor. * @param backlog Defines the maximum length the queue of pending * connections may grow to. * * @return Zero on success. */ PJ_DECL(pj_status_t) pj_sock_listen( pj_sock_t sockfd, int backlog ); /** * Accept new connection on the specified connection oriented server socket. * * @param serverfd The server socket. * @param newsock New socket on success, of PJ_INVALID_SOCKET if failed. * @param addr A pointer to sockaddr type. If the argument is not NULL, * it will be filled by the address of connecting entity. * @param addrlen Initially specifies the length of the address, and upon * return will be filled with the exact address length. * * @return Zero on success, or the error number. */ PJ_DECL(pj_status_t) pj_sock_accept( pj_sock_t serverfd, pj_sock_t *newsock, pj_sockaddr_t *addr, int *addrlen); #endif /** * The file descriptor sockfd must refer to a socket. If the socket is of * type PJ_SOCK_DGRAM then the serv_addr address is the address to which * datagrams are sent by default, and the only address from which datagrams * are received. If the socket is of type PJ_SOCK_STREAM or PJ_SOCK_SEQPACKET, * this call attempts to make a connection to another socket. The * other socket is specified by serv_addr, which is an address (of length * addrlen) in the communications space of the socket. Each communications * space interprets the serv_addr parameter in its own way. * * @param sockfd The socket descriptor. * @param serv_addr Server address to connect to. * @param addrlen The length of server address. * * @return Zero on success. */ PJ_DECL(pj_status_t) pj_sock_connect( pj_sock_t sockfd, const pj_sockaddr_t *serv_addr, int addrlen); /** * Return the address of peer which is connected to socket sockfd. * * @param sockfd The socket descriptor. * @param addr Pointer to sockaddr structure to which the address * will be returned. * @param namelen Initially the length of the addr. Upon return the value * will be set to the actual length of the address. * * @return Zero on success. */ PJ_DECL(pj_status_t) pj_sock_getpeername(pj_sock_t sockfd, pj_sockaddr_t *addr, int *namelen); /** * Return the current name of the specified socket. * * @param sockfd The socket descriptor. * @param addr Pointer to sockaddr structure to which the address * will be returned. * @param namelen Initially the length of the addr. Upon return the value * will be set to the actual length of the address. * * @return Zero on success. */ PJ_DECL(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd, pj_sockaddr_t *addr, int *namelen); /** * Get socket option associated with a socket. Options may exist at multiple * protocol levels; they are always present at the uppermost socket level. * * @param sockfd The socket descriptor. * @param level The level which to get the option from. * @param optname The option name. * @param optval Identifies the buffer which the value will be * returned. * @param optlen Initially contains the length of the buffer, upon * return will be set to the actual size of the value. * * @return Zero on success. */ PJ_DECL(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd, pj_uint16_t level, pj_uint16_t optname, void *optval, int *optlen); /** * Manipulate the options associated with a socket. Options may exist at * multiple protocol levels; they are always present at the uppermost socket * level. * * @param sockfd The socket descriptor. * @param level The level which to get the option from. * @param optname The option name. * @param optval Identifies the buffer which contain the value. * @param optlen The length of the value. * * @return PJ_SUCCESS or the status code. */ PJ_DECL(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd, pj_uint16_t level, pj_uint16_t optname, const void *optval, int optlen); /** * Set socket options associated with a socket. This method will apply all the * options specified, and ignore any errors that might be raised. * * @param sockfd The socket descriptor. * @param params The socket options. * * @return PJ_SUCCESS or the last error code. */ PJ_DECL(pj_status_t) pj_sock_setsockopt_params( pj_sock_t sockfd, const pj_sockopt_params *params); /** * Helper function to set socket buffer size using #pj_sock_setsockopt() * with capability to auto retry with lower buffer setting value until * the highest possible value is successfully set. * * @param sockfd The socket descriptor. * @param optname The option name, valid values are pj_SO_RCVBUF() * and pj_SO_SNDBUF(). * @param auto_retry Option whether auto retry with lower value is * enabled. * @param buf_size On input, specify the prefered buffer size setting, * on output, the buffer size setting applied. * * @return PJ_SUCCESS or the status code. */ PJ_DECL(pj_status_t) pj_sock_setsockopt_sobuf( pj_sock_t sockfd, pj_uint16_t optname, pj_bool_t auto_retry, unsigned *buf_size); /** * Receives data stream or message coming to the specified socket. * * @param sockfd The socket descriptor. * @param buf The buffer to receive the data or message. * @param len On input, the length of the buffer. On return, * contains the length of data received. * @param flags Flags (such as pj_MSG_PEEK()). * * @return PJ_SUCCESS or the error code. */ PJ_DECL(pj_status_t) pj_sock_recv(pj_sock_t sockfd, void *buf, pj_ssize_t *len, unsigned flags); /** * Receives data stream or message coming to the specified socket. * * @param sockfd The socket descriptor. * @param buf The buffer to receive the data or message. * @param len On input, the length of the buffer. On return, * contains the length of data received. * @param flags Flags (such as pj_MSG_PEEK()). * @param from If not NULL, it will be filled with the source * address of the connection. * @param fromlen Initially contains the length of from address, * and upon return will be filled with the actual * length of the address. * * @return PJ_SUCCESS or the error code. */ PJ_DECL(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd, void *buf, pj_ssize_t *len, unsigned flags, pj_sockaddr_t *from, int *fromlen); /** * Transmit data to the socket. * * @param sockfd Socket descriptor. * @param buf Buffer containing data to be sent. * @param len On input, the length of the data in the buffer. * Upon return, it will be filled with the length * of data sent. * @param flags Flags (such as pj_MSG_DONTROUTE()). * * @return PJ_SUCCESS or the status code. */ PJ_DECL(pj_status_t) pj_sock_send(pj_sock_t sockfd, const void *buf, pj_ssize_t *len, unsigned flags); /** * Transmit data to the socket to the specified address. * * @param sockfd Socket descriptor. * @param buf Buffer containing data to be sent. * @param len On input, the length of the data in the buffer. * Upon return, it will be filled with the length * of data sent. * @param flags Flags (such as pj_MSG_DONTROUTE()). * @param to The address to send. * @param tolen The length of the address in bytes. * * @return PJ_SUCCESS or the status code. */ PJ_DECL(pj_status_t) pj_sock_sendto(pj_sock_t sockfd, const void *buf, pj_ssize_t *len, unsigned flags, const pj_sockaddr_t *to, int tolen); #if PJ_HAS_TCP /** * The shutdown call causes all or part of a full-duplex connection on the * socket associated with sockfd to be shut down. * * @param sockfd The socket descriptor. * @param how If how is PJ_SHUT_RD, further receptions will be * disallowed. If how is PJ_SHUT_WR, further transmissions * will be disallowed. If how is PJ_SHUT_RDWR, further * receptions andtransmissions will be disallowed. * * @return Zero on success. */ PJ_DECL(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd, int how); #endif /** * @} */ PJ_END_DECL #endif /* __PJ_SOCK_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/sock_qos.h ================================================ /* $Id: sock_qos.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_SOCK_QOS_H__ #define __PJ_SOCK_QOS_H__ /** * @file sock_qos.h * @brief Socket QoS API */ #include PJ_BEGIN_DECL /** * @defgroup socket_qos Socket Quality of Service (QoS) API: TOS, DSCP, WMM, IEEE 802.1p * @ingroup PJ_SOCK * @{ \section intro QoS Technologies QoS settings are available for both Layer 2 and 3 of TCP/IP protocols: \subsection intro_ieee8021p Layer 2: IEEE 802.1p for Ethernet IEEE 802.1p tagging will mark frames sent by a host for prioritized delivery using a 3-bit Priority field in the virtual local area network (VLAN) header of the Ethernet frame. The VLAN header is placed inside the Ethernet header, between the Source Address field and either the Length field (for an IEEE 802.3 frame) or the EtherType field (for an Ethernet II frame). \subsection intro_wmm Layer 2: WMM At the Network Interface layer for IEEE 802.11 wireless, the Wi-Fi Alliance certification for Wi-Fi Multimedia (WMM) defines four access categories for prioritizing network traffic. These access categories are (in order of highest to lowest priority) voice, video, best-effort, and background. Host support for WMM prioritization requires that both wireless network adapters and their drivers support WMM. Wireless access points (APs) must have WMM enabled. \subsection intro_dscp Layer 3: DSCP At the Internet layer, you can use Differentiated Services/Diffserv and set the value of the Differentiated Services Code Point (DSCP) in the IP header. As defined in RFC 2474, the DSCP value is the high-order 6 bits of the IP version 4 (IPv4) TOS field and the IP version 6 (IPv6) Traffic Class field. \subsection intro_other Layer 3: Other Other mechanisms exist (such as RSVP, IntServ) but this will not be implemented. \section availability QoS Availability \subsection linux Linux DSCP is available via IP TOS option. Ethernet 802.1p tagging is done by setting setsockopt(SO_PRIORITY) option of the socket, then with the set_egress_map option of the vconfig utility to convert this to set vlan-qos field of the packet. WMM is not known to be available. \subsection windows Windows and Windows Mobile (It's a mess!) DSCP is settable with setsockopt() on Windows 2000 or older, but Windows would silently ignore this call on WinXP or later, unless administrator modifies the registry. On Windows 2000, Windows XP, and Windows Server 2003, GQoS (Generic QoS) API is the standard API, but this API may not be supported in the future. On Vista and Windows 7, the is a new QoS2 API, also known as Quality Windows Audio-Video Experience (qWAVE). IEEE 802.1p tagging is available via Traffic Control (TC) API, available on Windows XP SP2, but this needs administrator access. For Vista and later, it's in qWAVE. WMM is available for mobile platforms on Windows Mobile 6 platform and Windows Embedded CE 6, via setsockopt(IP_DSCP_TRAFFIC_TYPE). qWAVE supports this as well. \subsection symbian Symbian S60 3rd Ed Both DSCP and WMM is supported via RSocket::SetOpt() with will set both Layer 2 and Layer 3 QoS settings accordingly. Internally, PJLIB sets the DSCP field of the socket, and based on certain DSCP values mapping, Symbian will set the WMM tag accordingly. \section api PJLIB's QoS API Abstraction Based on the above, the following API is implemented. Declare the following "standard" traffic types. \code typedef enum pj_qos_type { PJ_QOS_TYPE_BEST_EFFORT, PJ_QOS_TYPE_BACKGROUND, PJ_QOS_TYPE_VIDEO, PJ_QOS_TYPE_VOICE, PJ_QOS_TYPE_CONTROL } pj_qos_type; \endcode The traffic classes above will determine how the Layer 2 and 3 QoS settings will be used. The standard mapping between the classes above to the corresponding Layer 2 and 3 settings are as follows: \code ================================================================= PJLIB Traffic Type IP DSCP WMM 802.1p ----------------------------------------------------------------- BEST_EFFORT 0x00 BE (Bulk Effort) 0 BACKGROUND 0x08 BK (Bulk) 2 VIDEO 0x28 VI (Video) 5 VOICE 0x30 VO (Voice) 6 CONTROL 0x38 VO (Voice) 7 ================================================================= \endcode There are two sets of API provided to manipulate the QoS parameters. \subsection portable_api Portable API The first set of API is: \code // Set QoS parameters PJ_DECL(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, pj_qos_type val); // Get QoS parameters PJ_DECL(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, pj_qos_type *p_val); \endcode The API will set the traffic type according to the DSCP class, for both Layer 2 and Layer 3 QoS settings, where it's available. If any of the layer QoS setting is not settable, the API will silently ignore it. If both layers are not setable, the API will return error. The API above is the recommended use of QoS, since it is the most portable across all platforms. \subsection detail_api Fine Grained Control API The second set of API is intended for application that wants to fine tune the QoS parameters. The Layer 2 and 3 QoS parameters are stored in pj_qos_params structure: \code typedef enum pj_qos_flag { PJ_QOS_PARAM_HAS_DSCP = 1, PJ_QOS_PARAM_HAS_SO_PRIO = 2, PJ_QOS_PARAM_HAS_WMM = 4 } pj_qos_flag; typedef enum pj_qos_wmm_prio { PJ_QOS_WMM_PRIO_BULK_EFFORT, PJ_QOS_WMM_PRIO_BULK, PJ_QOS_WMM_PRIO_VIDEO, PJ_QOS_WMM_PRIO_VOICE } pj_qos_wmm_prio; typedef struct pj_qos_params { pj_uint8_t flags; // Determines which values to // set, bitmask of pj_qos_flag pj_uint8_t dscp_val; // The 6 bits DSCP value to set pj_uint8_t so_prio; // SO_PRIORITY value pj_qos_wmm_prio wmm_prio; // WMM priority value } pj_qos_params; \endcode The second set of API with more fine-grained control over the parameters are: \code // Retrieve QoS params for the specified traffic type PJ_DECL(pj_status_t) pj_qos_get_params(pj_qos_type type, pj_qos_params *p); // Set QoS parameters to the socket PJ_DECL(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, const pj_qos_params *p); // Get QoS parameters from the socket PJ_DECL(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, pj_qos_params *p); \endcode Important: The pj_sock_set/get_qos_params() APIs are not portable, and it's probably only going to be implemented on Linux. Application should always try to use pj_sock_set_qos_type() instead. */ /** * High level traffic classification. */ typedef enum pj_qos_type { PJ_QOS_TYPE_BEST_EFFORT, /**< Best effort traffic (default value). Any QoS function calls with specifying this value are effectively no-op */ PJ_QOS_TYPE_BACKGROUND, /**< Background traffic. */ PJ_QOS_TYPE_VIDEO, /**< Video traffic. */ PJ_QOS_TYPE_VOICE, /**< Voice traffic. */ PJ_QOS_TYPE_CONTROL /**< Control traffic. */ } pj_qos_type; /** * Bitmask flag to indicate which QoS layer setting is set in the * \a flags field of the #pj_qos_params structure. */ typedef enum pj_qos_flag { PJ_QOS_PARAM_HAS_DSCP = 1, /**< DSCP field is set. */ PJ_QOS_PARAM_HAS_SO_PRIO = 2, /**< Socket SO_PRIORITY */ PJ_QOS_PARAM_HAS_WMM = 4 /**< WMM field is set. */ } pj_qos_flag; /** * Standard WMM priorities. */ typedef enum pj_qos_wmm_prio { PJ_QOS_WMM_PRIO_BULK_EFFORT, /**< Bulk effort priority */ PJ_QOS_WMM_PRIO_BULK, /**< Bulk priority. */ PJ_QOS_WMM_PRIO_VIDEO, /**< Video priority */ PJ_QOS_WMM_PRIO_VOICE /**< Voice priority */ } pj_qos_wmm_prio; /** * QoS parameters to be set or retrieved to/from the socket. */ typedef struct pj_qos_params { pj_uint8_t flags; /**< Determines which values to set, bitmask of pj_qos_flag */ pj_uint8_t dscp_val; /**< The 6 bits DSCP value to set */ pj_uint8_t so_prio; /**< SO_PRIORITY value */ pj_qos_wmm_prio wmm_prio; /**< WMM priority value */ } pj_qos_params; /** * This is the high level and portable API to enable QoS on the specified * socket, by setting the traffic type to the specified parameter. * * @param sock The socket. * @param type Traffic type to be set. * * @return PJ_SUCCESS if at least Layer 2 or Layer 3 setting is * successfully set. If both Layer 2 and Layer 3 settings * can't be set, this function will return error. */ PJ_DECL(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, pj_qos_type type); /** * This is the high level and portable API to get the traffic type that has * been set on the socket. On occasions where the Layer 2 or Layer 3 settings * were modified by using low level API, this function may return approximation * of the closest QoS type that matches the settings. * * @param sock The socket. * @param p_type Pointer to receive the traffic type of the socket. * * @return PJ_SUCCESS if traffic type for the socket can be obtained * or approximated.. */ PJ_DECL(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, pj_qos_type *p_type); /** * This is a convenience function to apply QoS to the socket, and print error * logging if the operations failed. Both QoS traffic type and the low level * QoS parameters can be applied with this function. * * @param sock The socket handle. * @param qos_type QoS traffic type. The QoS traffic type will be applied * only if the value is not PJ_QOS_TYPE_BEST_EFFORT, * @param qos_params Optional low-level QoS parameters. This will be * applied only if this argument is not NULL and the * flags inside the structure is non-zero. Upon return, * the flags will indicate which parameters have been * applied successfully. * @param log_level This function will print to log at this level upon * encountering errors. * @param log_sender Optional sender name in the log. * @param sock_name Optional name to help identify the socket in the log. * * @return PJ_SUCCESS if at least Layer 2 or Layer 3 setting is * successfully set. If both Layer 2 and Layer 3 settings * can't be set, this function will return error. * * @see pj_sock_apply_qos2() */ PJ_DECL(pj_status_t) pj_sock_apply_qos(pj_sock_t sock, pj_qos_type qos_type, pj_qos_params *qos_params, unsigned log_level, const char *log_sender, const char *sock_name); /** * Variant of #pj_sock_apply_qos() where the \a qos_params parameter is * const. * * @see pj_sock_apply_qos() */ PJ_DECL(pj_status_t) pj_sock_apply_qos2(pj_sock_t sock, pj_qos_type qos_type, const pj_qos_params *qos_params, unsigned log_level, const char *log_sender, const char *sock_name); /** * Retrieve the standard mapping of QoS params for the specified traffic * type. * * @param type The traffic type from which the QoS parameters * are to be retrieved. * @param p_param Pointer to receive the QoS parameters. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_qos_get_params(pj_qos_type type, pj_qos_params *p_param); /** * Retrieve the traffic type that matches the specified QoS parameters. * If no exact matching is found, this function will return an * approximation of the closest matching traffic type for the specified * QoS parameters. * * @param param Structure containing QoS parameters to map into * "standard" traffic types. * @param p_type Pointer to receive the traffic type. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_qos_get_type(const pj_qos_params *param, pj_qos_type *p_type); /** * This is a low level API to set QoS parameters to the socket. * * @param sock The socket. * @param param Structure containing QoS parameters to be applied * to the socket. Upon return, the \a flags field * of this structure will be set with bitmask value * indicating which QoS settings have successfully * been applied to the socket. * * @return PJ_SUCCESS if at least one field setting has been * successfully set. If no setting can't be set, * this function will return error. */ PJ_DECL(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, pj_qos_params *param); /** * This is a low level API to get QoS parameters from the socket. * * @param sock The socket. * @param p_param Pointer to receive the parameters. Upon returning * successfully, the \a flags field of this structure * will be initialized with the appropriate bitmask * to indicate which fields have been successfully * retrieved. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, pj_qos_params *p_param); /** * @} */ PJ_END_DECL #endif /* __PJ_SOCK_QOS_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/sock_select.h ================================================ /* $Id: sock_select.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_SELECT_H__ #define __PJ_SELECT_H__ /** * @file sock_select.h * @brief Socket select(). */ #include PJ_BEGIN_DECL /** * @defgroup PJ_SOCK_SELECT Socket select() API. * @ingroup PJ_IO * @{ * This module provides portable abstraction for \a select() like API. * The abstraction is needed so that it can utilize various event * dispatching mechanisms that are available across platforms. * * The API is very similar to normal \a select() usage. * * \section pj_sock_select_examples_sec Examples * * For some examples on how to use the select API, please see: * * - \ref page_pjlib_select_test */ /** * Portable structure declarations for pj_fd_set. * The implementation of pj_sock_select() does not use this structure * per-se, but instead it will use the native fd_set structure. However, * we must make sure that the size of pj_fd_set_t can accomodate the * native fd_set structure. */ typedef struct pj_fd_set_t { pj_sock_t data[PJ_IOQUEUE_MAX_HANDLES+ 4]; /**< Opaque buffer for fd_set */ } pj_fd_set_t; /** * Initialize the descriptor set pointed to by fdsetp to the null set. * * @param fdsetp The descriptor set. */ PJ_DECL(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp); /** * This is an internal function, application shouldn't use this. * * Get the number of descriptors in the set. This is defined in sock_select.c * This function will only return the number of sockets set from PJ_FD_SET * operation. When the set is modified by other means (such as by select()), * the count will not be reflected here. * * @param fdsetp The descriptor set. * * @return Number of descriptors in the set. */ PJ_DECL(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp); /** * Add the file descriptor fd to the set pointed to by fdsetp. * If the file descriptor fd is already in this set, there shall be no effect * on the set, nor will an error be returned. * * @param fd The socket descriptor. * @param fdsetp The descriptor set. */ PJ_DECL(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp); /** * Remove the file descriptor fd from the set pointed to by fdsetp. * If fd is not a member of this set, there shall be no effect on the set, * nor will an error be returned. * * @param fd The socket descriptor. * @param fdsetp The descriptor set. */ PJ_DECL(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp); /** * Evaluate to non-zero if the file descriptor fd is a member of the set * pointed to by fdsetp, and shall evaluate to zero otherwise. * * @param fd The socket descriptor. * @param fdsetp The descriptor set. * * @return Nonzero if fd is member of the descriptor set. */ PJ_DECL(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp); /** * This function wait for a number of file descriptors to change status. * The behaviour is the same as select() function call which appear in * standard BSD socket libraries. * * @param n On Unices, this specifies the highest-numbered * descriptor in any of the three set, plus 1. On Windows, * the value is ignored. * @param readfds Optional pointer to a set of sockets to be checked for * readability. * @param writefds Optional pointer to a set of sockets to be checked for * writability. * @param exceptfds Optional pointer to a set of sockets to be checked for * errors. * @param timeout Maximum time for select to wait, or null for blocking * operations. * * @return The total number of socket handles that are ready, or * zero if the time limit expired, or -1 if an error occurred. */ PJ_DECL(int) pj_sock_select( int n, pj_fd_set_t *readfds, pj_fd_set_t *writefds, pj_fd_set_t *exceptfds, const pj_time_val *timeout); /** * @} */ PJ_END_DECL #endif /* __PJ_SELECT_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/ssl_sock.h ================================================ /* $Id: ssl_sock.h 4506 2013-04-26 06:01:43Z bennylp $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_SSL_SOCK_H__ #define __PJ_SSL_SOCK_H__ /** * @file ssl_sock.h * @brief Secure socket */ #include #include #include PJ_BEGIN_DECL /** * @defgroup PJ_SSL_SOCK Secure socket I/O * @brief Secure socket provides security on socket operation using standard * security protocols such as SSL and TLS. * @ingroup PJ_IO * @{ * * Secure socket wraps normal socket and applies security features, i.e: * privacy and data integrity, on the socket traffic, using standard security * protocols such as SSL and TLS. * * Secure socket employs active socket operations, which is similar to (and * described more detail) in \ref PJ_ACTIVESOCK. */ /** * This opaque structure describes the secure socket. */ typedef struct pj_ssl_sock_t pj_ssl_sock_t; /** * Opaque declaration of endpoint certificate or credentials. This may contains * certificate, private key, and trusted Certificate Authorities list. */ typedef struct pj_ssl_cert_t pj_ssl_cert_t; typedef enum pj_ssl_cert_verify_flag_t { /** * No error in verification. */ PJ_SSL_CERT_ESUCCESS = 0, /** * The issuer certificate cannot be found. */ PJ_SSL_CERT_EISSUER_NOT_FOUND = (1 << 0), /** * The certificate is untrusted. */ PJ_SSL_CERT_EUNTRUSTED = (1 << 1), /** * The certificate has expired or not yet valid. */ PJ_SSL_CERT_EVALIDITY_PERIOD = (1 << 2), /** * One or more fields of the certificate cannot be decoded due to * invalid format. */ PJ_SSL_CERT_EINVALID_FORMAT = (1 << 3), /** * The certificate cannot be used for the specified purpose. */ PJ_SSL_CERT_EINVALID_PURPOSE = (1 << 4), /** * The issuer info in the certificate does not match to the (candidate) * issuer certificate, e.g: issuer name not match to subject name * of (candidate) issuer certificate. */ PJ_SSL_CERT_EISSUER_MISMATCH = (1 << 5), /** * The CRL certificate cannot be found or cannot be read properly. */ PJ_SSL_CERT_ECRL_FAILURE = (1 << 6), /** * The certificate has been revoked. */ PJ_SSL_CERT_EREVOKED = (1 << 7), /** * The certificate chain length is too long. */ PJ_SSL_CERT_ECHAIN_TOO_LONG = (1 << 8), /** * The server identity does not match to any identities specified in * the certificate, e.g: subjectAltName extension, subject common name. * This flag will only be set by application as SSL socket does not * perform server identity verification. */ PJ_SSL_CERT_EIDENTITY_NOT_MATCH = (1 << 30), /** * Unknown verification error. */ PJ_SSL_CERT_EUNKNOWN = (1 << 31) } pj_ssl_cert_verify_flag_t; typedef enum pj_ssl_cert_name_type { PJ_SSL_CERT_NAME_UNKNOWN = 0, PJ_SSL_CERT_NAME_RFC822, PJ_SSL_CERT_NAME_DNS, PJ_SSL_CERT_NAME_URI, PJ_SSL_CERT_NAME_IP } pj_ssl_cert_name_type; /** * Describe structure of certificate info. */ typedef struct pj_ssl_cert_info { unsigned version; /**< Certificate version */ pj_uint8_t serial_no[20]; /**< Serial number, array of octets, first index is MSB */ struct { pj_str_t cn; /**< Common name */ pj_str_t info; /**< One line subject, fields are separated by slash, e.g: "CN=sample.org/OU=HRD" */ } subject; /**< Subject */ struct { pj_str_t cn; /**< Common name */ pj_str_t info; /**< One line subject, fields are separated by slash.*/ } issuer; /**< Issuer */ struct { pj_time_val start; /**< Validity start */ pj_time_val end; /**< Validity end */ pj_bool_t gmt; /**< Flag if validity date/time use GMT */ } validity; /**< Validity */ struct { unsigned cnt; /**< # of entry */ struct { pj_ssl_cert_name_type type; /**< Name type */ pj_str_t name; /**< The name */ } *entry; /**< Subject alt name entry */ } subj_alt_name; /**< Subject alternative name extension */ pj_str_t raw; /**< Raw certificate in PEM format, only available for remote certificate. */ } pj_ssl_cert_info; /** * Create credential from files. TLS server application can provide multiple * certificates (RSA, ECC, and DSA) by supplying certificate name with "_rsa" * suffix, e.g: "pjsip_rsa.pem", the library will automatically check for * other certificates with "_ecc" and "_dsa" suffix. * * @param CA_file The file of trusted CA list. * @param cert_file The file of certificate. * @param privkey_file The file of private key. * @param privkey_pass The password of private key, if any. * @param p_cert Pointer to credential instance to be created. * * @return PJ_SUCCESS when successful. */ PJ_DECL(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool, const pj_str_t *CA_file, const pj_str_t *cert_file, const pj_str_t *privkey_file, const pj_str_t *privkey_pass, pj_ssl_cert_t **p_cert); /** * Create credential from files. TLS server application can provide multiple * certificates (RSA, ECC, and DSA) by supplying certificate name with "_rsa" * suffix, e.g: "pjsip_rsa.pem", the library will automatically check for * other certificates with "_ecc" and "_dsa" suffix. * * This is the same as pj_ssl_cert_load_from_files() but also * accepts an additional param CA_path to load CA certificates from * a directory. * * @param CA_file The file of trusted CA list. * @param CA_path The path to a directory of trusted CA list. * @param cert_file The file of certificate. * @param privkey_file The file of private key. * @param privkey_pass The password of private key, if any. * @param p_cert Pointer to credential instance to be created. * * @return PJ_SUCCESS when successful. */ PJ_DECL(pj_status_t) pj_ssl_cert_load_from_files2( pj_pool_t *pool, const pj_str_t *CA_file, const pj_str_t *CA_path, const pj_str_t *cert_file, const pj_str_t *privkey_file, const pj_str_t *privkey_pass, pj_ssl_cert_t **p_cert); /** * Dump SSL certificate info. * * @param ci The certificate info. * @param indent String for left indentation. * @param buf The buffer where certificate info will be printed on. * @param buf_size The buffer size. * * @return The length of the dump result, or -1 when buffer size * is not sufficient. */ PJ_DECL(pj_ssize_t) pj_ssl_cert_info_dump(const pj_ssl_cert_info *ci, const char *indent, char *buf, pj_size_t buf_size); /** * Get SSL certificate verification error messages from verification status. * * @param verify_status The SSL certificate verification status. * @param error_strings Array of strings to receive the verification error * messages. * @param count On input it specifies maximum error messages should be * retrieved. On output it specifies the number of error * messages retrieved. * * @return PJ_SUCCESS when successful. */ PJ_DECL(pj_status_t) pj_ssl_cert_get_verify_status_strings( pj_uint32_t verify_status, const char *error_strings[], unsigned *count); /** * Cipher suites enumeration. */ typedef enum pj_ssl_cipher { /* Unsupported cipher */ PJ_TLS_UNKNOWN_CIPHER = -1, /* NULL */ PJ_TLS_NULL_WITH_NULL_NULL = 0x00000000, /* TLS/SSLv3 */ PJ_TLS_RSA_WITH_NULL_MD5 = 0x00000001, PJ_TLS_RSA_WITH_NULL_SHA = 0x00000002, PJ_TLS_RSA_WITH_NULL_SHA256 = 0x0000003B, PJ_TLS_RSA_WITH_RC4_128_MD5 = 0x00000004, PJ_TLS_RSA_WITH_RC4_128_SHA = 0x00000005, PJ_TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x0000000A, PJ_TLS_RSA_WITH_AES_128_CBC_SHA = 0x0000002F, PJ_TLS_RSA_WITH_AES_256_CBC_SHA = 0x00000035, PJ_TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x0000003C, PJ_TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x0000003D, PJ_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x0000000D, PJ_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x00000010, PJ_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x00000013, PJ_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x00000016, PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x00000030, PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x00000031, PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x00000032, PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x00000033, PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x00000036, PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x00000037, PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x00000038, PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x00000039, PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x0000003E, PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x0000003F, PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x00000040, PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x00000067, PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x00000068, PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x00000069, PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x0000006A, PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x0000006B, PJ_TLS_DH_anon_WITH_RC4_128_MD5 = 0x00000018, PJ_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x0000001B, PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x00000034, PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x0000003A, PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x0000006C, PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x0000006D, /* TLS (deprecated) */ PJ_TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x00000003, PJ_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x00000006, PJ_TLS_RSA_WITH_IDEA_CBC_SHA = 0x00000007, PJ_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x00000008, PJ_TLS_RSA_WITH_DES_CBC_SHA = 0x00000009, PJ_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0000000B, PJ_TLS_DH_DSS_WITH_DES_CBC_SHA = 0x0000000C, PJ_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0000000E, PJ_TLS_DH_RSA_WITH_DES_CBC_SHA = 0x0000000F, PJ_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x00000011, PJ_TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x00000012, PJ_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x00000014, PJ_TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x00000015, PJ_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x00000017, PJ_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x00000019, PJ_TLS_DH_anon_WITH_DES_CBC_SHA = 0x0000001A, /* SSLv3 */ PJ_SSL_FORTEZZA_KEA_WITH_NULL_SHA = 0x0000001C, PJ_SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA = 0x0000001D, PJ_SSL_FORTEZZA_KEA_WITH_RC4_128_SHA = 0x0000001E, /* SSLv2 */ PJ_SSL_CK_RC4_128_WITH_MD5 = 0x00010080, PJ_SSL_CK_RC4_128_EXPORT40_WITH_MD5 = 0x00020080, PJ_SSL_CK_RC2_128_CBC_WITH_MD5 = 0x00030080, PJ_SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5 = 0x00040080, PJ_SSL_CK_IDEA_128_CBC_WITH_MD5 = 0x00050080, PJ_SSL_CK_DES_64_CBC_WITH_MD5 = 0x00060040, PJ_SSL_CK_DES_192_EDE3_CBC_WITH_MD5 = 0x000700C0 } pj_ssl_cipher; /** * Get cipher list supported by SSL/TLS backend. * * @param ciphers The ciphers buffer to receive cipher list. * @param cipher_num Maximum number of ciphers to be received. * * @return PJ_SUCCESS when successful. */ PJ_DECL(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[], unsigned *cipher_num); /** * Check if the specified cipher is supported by SSL/TLS backend. * * @param cipher The cipher. * * @return PJ_TRUE when supported. */ PJ_DECL(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher); /** * Get cipher name string. * * @param cipher The cipher. * * @return The cipher name or NULL if cipher is not recognized/ * supported. */ PJ_DECL(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher); /** * Get cipher ID from cipher name string. Note that on different backends * (e.g. OpenSSL or Symbian implementation), cipher names may not be * equivalent for the same cipher ID. * * @param cipher_name The cipher name string. * * @return The cipher ID or PJ_TLS_UNKNOWN_CIPHER if the cipher * name string is not recognized/supported. */ PJ_DECL(pj_ssl_cipher) pj_ssl_cipher_id(const char *cipher_name); /** * This structure contains the callbacks to be called by the secure socket. */ typedef struct pj_ssl_sock_cb { /** * This callback is called when a data arrives as the result of * pj_ssl_sock_start_read(). * * @param ssock The secure socket. * @param data The buffer containing the new data, if any. If * the status argument is non-PJ_SUCCESS, this * argument may be NULL. * @param size The length of data in the buffer. * @param status The status of the read operation. This may contain * non-PJ_SUCCESS for example when the TCP connection * has been closed. In this case, the buffer may * contain left over data from previous callback which * the application may want to process. * @param remainder If application wishes to leave some data in the * buffer (common for TCP applications), it should * move the remainder data to the front part of the * buffer and set the remainder length here. The value * of this parameter will be ignored for datagram * sockets. * * @return PJ_TRUE if further read is desired, and PJ_FALSE * when application no longer wants to receive data. * Application may destroy the secure socket in the * callback and return PJ_FALSE here. */ pj_bool_t (*on_data_read)(pj_ssl_sock_t *ssock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder); /** * This callback is called when a packet arrives as the result of * pj_ssl_sock_start_recvfrom(). * * @param ssock The secure socket. * @param data The buffer containing the packet, if any. If * the status argument is non-PJ_SUCCESS, this * argument will be set to NULL. * @param size The length of packet in the buffer. If * the status argument is non-PJ_SUCCESS, this * argument will be set to zero. * @param src_addr Source address of the packet. * @param addr_len Length of the source address. * @param status This contains * * @return PJ_TRUE if further read is desired, and PJ_FALSE * when application no longer wants to receive data. * Application may destroy the secure socket in the * callback and return PJ_FALSE here. */ pj_bool_t (*on_data_recvfrom)(pj_ssl_sock_t *ssock, void *data, pj_size_t size, const pj_sockaddr_t *src_addr, int addr_len, pj_status_t status); /** * This callback is called when data has been sent. * * @param ssock The secure socket. * @param send_key Key associated with the send operation. * @param sent If value is positive non-zero it indicates the * number of data sent. When the value is negative, * it contains the error code which can be retrieved * by negating the value (i.e. status=-sent). * * @return Application may destroy the secure socket in the * callback and return PJ_FALSE here. */ pj_bool_t (*on_data_sent)(pj_ssl_sock_t *ssock, pj_ioqueue_op_key_t *send_key, pj_ssize_t sent); /** * This callback is called when new connection arrives as the result * of pj_ssl_sock_start_accept(). * * @param ssock The secure socket. * @param newsock The new incoming secure socket. * @param src_addr The source address of the connection. * @param addr_len Length of the source address. * * @return PJ_TRUE if further accept() is desired, and PJ_FALSE * when application no longer wants to accept incoming * connection. Application may destroy the secure socket * in the callback and return PJ_FALSE here. */ pj_bool_t (*on_accept_complete)(pj_ssl_sock_t *ssock, pj_ssl_sock_t *newsock, const pj_sockaddr_t *src_addr, int src_addr_len); /** * This callback is called when pending connect operation has been * completed. * * @param ssock The secure socket. * @param status The connection result. If connection has been * successfully established, the status will contain * PJ_SUCCESS. * * @return Application may destroy the secure socket in the * callback and return PJ_FALSE here. */ pj_bool_t (*on_connect_complete)(pj_ssl_sock_t *ssock, pj_status_t status); } pj_ssl_sock_cb; /** * Enumeration of secure socket protocol types. * This can be combined using bitwise OR operation. */ typedef enum pj_ssl_sock_proto { /** * Default protocol of backend. */ PJ_SSL_SOCK_PROTO_DEFAULT = 0, /** * SSLv2.0 protocol. */ PJ_SSL_SOCK_PROTO_SSL2 = (1 << 0), /** * SSLv3.0 protocol. */ PJ_SSL_SOCK_PROTO_SSL3 = (1 << 1), /** * TLSv1.0 protocol. */ PJ_SSL_SOCK_PROTO_TLS1 = (1 << 2), /** * TLSv1.1 protocol. */ PJ_SSL_SOCK_PROTO_TLS1_1 = (1 << 3), /** * TLSv1.2 protocol. */ PJ_SSL_SOCK_PROTO_TLS1_2 = (1 << 4), /** * Certain backend implementation e.g:OpenSSL, has feature to enable all * protocol. */ PJ_SSL_SOCK_PROTO_SSL23 = (1 << 16) - 1, /** * DTLSv1.0 protocol. */ PJ_SSL_SOCK_PROTO_DTLS1 = (1 << 16), } pj_ssl_sock_proto; /** * Definition of secure socket info structure. */ typedef struct pj_ssl_sock_info { /** * Describes whether secure socket connection is established, i.e: TLS/SSL * handshaking has been done successfully. */ pj_bool_t established; /** * Describes secure socket protocol being used, see #pj_ssl_sock_proto. * Use bitwise OR operation to combine the protocol type. */ pj_uint32_t proto; /** * Describes cipher suite being used, this will only be set when connection * is established. */ pj_ssl_cipher cipher; /** * Describes local address. */ pj_sockaddr local_addr; /** * Describes remote address. */ pj_sockaddr remote_addr; /** * Describes active local certificate info. */ pj_ssl_cert_info *local_cert_info; /** * Describes active remote certificate info. */ pj_ssl_cert_info *remote_cert_info; /** * Status of peer certificate verification. */ pj_uint32_t verify_status; /** * Last native error returned by the backend. */ unsigned long last_native_err; /** * Group lock assigned to the ioqueue key. */ pj_grp_lock_t *grp_lock; } pj_ssl_sock_info; /** * Definition of secure socket creation parameters. */ typedef struct pj_ssl_sock_param { /** * Optional group lock to be assigned to the ioqueue key. * * Note that when a secure socket listener is configured with a group * lock, any new secure socket of an accepted incoming connection * will have its own group lock created automatically by the library, * this group lock can be queried via pj_ssl_sock_get_info() in the info * field pj_ssl_sock_info::grp_lock. */ pj_grp_lock_t *grp_lock; /** * Specifies socket address family, either pj_AF_INET() and pj_AF_INET6(). * * Default is pj_AF_INET(). */ int sock_af; /** * Specify socket type, either pj_SOCK_DGRAM() or pj_SOCK_STREAM(). * * Default is pj_SOCK_STREAM(). */ int sock_type; /** * Specify the ioqueue to use. Secure socket uses the ioqueue to perform * active socket operations, see \ref PJ_ACTIVESOCK for more detail. */ pj_ioqueue_t *ioqueue; /** * Specify the timer heap to use. Secure socket uses the timer to provide * auto cancelation on asynchronous operation when it takes longer time * than specified timeout period, e.g: security negotiation timeout. */ pj_timer_heap_t *timer_heap; /** * Specify secure socket callbacks, see #pj_ssl_sock_cb. */ pj_ssl_sock_cb cb; /** * Specify secure socket user data. */ void *user_data; /** * Specify security protocol to use, see #pj_ssl_sock_proto. Use bitwise OR * operation to combine the protocol type. * * Default is PJ_SSL_SOCK_PROTO_DEFAULT. */ pj_uint32_t proto; /** * Number of concurrent asynchronous operations that is to be supported * by the secure socket. This value only affects socket receive and * accept operations -- the secure socket will issue one or more * asynchronous read and accept operations based on the value of this * field. Setting this field to more than one will allow more than one * incoming data or incoming connections to be processed simultaneously * on multiprocessor systems, when the ioqueue is polled by more than * one threads. * * The default value is 1. */ unsigned async_cnt; /** * The ioqueue concurrency to be forced on the socket when it is * registered to the ioqueue. See #pj_ioqueue_set_concurrency() for more * info about ioqueue concurrency. * * When this value is -1, the concurrency setting will not be forced for * this socket, and the socket will inherit the concurrency setting of * the ioqueue. When this value is zero, the secure socket will disable * concurrency for the socket. When this value is +1, the secure socket * will enable concurrency for the socket. * * The default value is -1. */ int concurrency; /** * If this option is specified, the secure socket will make sure that * asynchronous send operation with stream oriented socket will only * call the callback after all data has been sent. This means that the * secure socket will automatically resend the remaining data until * all data has been sent. * * Please note that when this option is specified, it is possible that * error is reported after partial data has been sent. Also setting * this will disable the ioqueue concurrency for the socket. * * Default value is 1. */ pj_bool_t whole_data; /** * Specify buffer size for sending operation. Buffering sending data * is used for allowing application to perform multiple outstanding * send operations. Whenever application specifies this setting too * small, sending operation may return PJ_ENOMEM. * * Default value is 8192 bytes. */ pj_size_t send_buffer_size; /** * Specify buffer size for receiving encrypted (and perhaps compressed) * data on underlying socket. This setting is unused on Symbian, since * SSL/TLS Symbian backend, CSecureSocket, can use application buffer * directly. * * Default value is 1500. */ pj_size_t read_buffer_size; /** * Number of ciphers contained in the specified cipher preference. * If this is set to zero, then the cipher list used will be determined * by the backend default (for OpenSSL backend, setting * PJ_SSL_SOCK_OSSL_CIPHERS will be used). */ unsigned ciphers_num; /** * Ciphers and order preference. If empty, then default cipher list and * its default order of the backend will be used. */ pj_ssl_cipher *ciphers; /** * Security negotiation timeout. If this is set to zero (both sec and * msec), the negotiation doesn't have a timeout. * * Default value is zero. */ pj_time_val timeout; /** * Specify whether endpoint should verify peer certificate. * * Default value is PJ_FALSE. */ pj_bool_t verify_peer; /** * When secure socket is acting as server (handles incoming connection), * it will require the client to provide certificate. * * Default value is PJ_FALSE. */ pj_bool_t require_client_cert; /** * Server name indication. When secure socket is acting as client * (perform outgoing connection) and the server may host multiple * 'virtual' servers at a single underlying network address, setting * this will allow client to tell the server a name of the server * it is contacting. * * Default value is zero/not-set. */ pj_str_t server_name; /** * Specify if SO_REUSEADDR should be used for listening socket. This * option will only be used with accept() operation. * * Default is PJ_FALSE. */ pj_bool_t reuse_addr; /** * QoS traffic type to be set on this transport. When application wants * to apply QoS tagging to the transport, it's preferable to set this * field rather than \a qos_param fields since this is more portable. * * Default value is PJ_QOS_TYPE_BEST_EFFORT. */ pj_qos_type qos_type; /** * Set the low level QoS parameters to the transport. This is a lower * level operation than setting the \a qos_type field and may not be * supported on all platforms. * * By default all settings in this structure are disabled. */ pj_qos_params qos_params; /** * Specify if the transport should ignore any errors when setting the QoS * traffic type/parameters. * * Default: PJ_TRUE */ pj_bool_t qos_ignore_error; /** * Specify options to be set on the transport. * * By default there is no options. * */ pj_sockopt_params sockopt_params; /** * Specify if the transport should ignore any errors when setting the * sockopt parameters. * * Default: PJ_TRUE * */ pj_bool_t sockopt_ignore_error; } pj_ssl_sock_param; /** * Initialize the secure socket parameters for its creation with * the default values. * * @param param The parameter to be initialized. */ PJ_DECL(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param); /** * Duplicate pj_ssl_sock_param. * * @param pool Pool to allocate memory. * @param dst Destination parameter. * @param src Source parameter. */ PJ_DECL(void) pj_ssl_sock_param_copy(pj_pool_t *pool, pj_ssl_sock_param *dst, const pj_ssl_sock_param *src); /** * Create secure socket instance. * * @param pool The pool for allocating secure socket instance. * @param param The secure socket parameter, see #pj_ssl_sock_param. * @param p_ssock Pointer to secure socket instance to be created. * * @return PJ_SUCCESS when successful. */ PJ_DECL(pj_status_t) pj_ssl_sock_create(pj_pool_t *pool, const pj_ssl_sock_param *param, pj_ssl_sock_t **p_ssock); /** * Set secure socket certificate or credentials. Credentials may include * certificate, private key and trusted Certification Authorities list. * Normally, server socket must provide certificate (and private key). * Socket client may also need to provide certificate in case requested * by the server. * * @param ssock The secure socket instance. * @param pool The pool. * @param cert The endpoint certificate/credentials, see * #pj_ssl_cert_t. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_ssl_sock_set_certificate( pj_ssl_sock_t *ssock, pj_pool_t *pool, const pj_ssl_cert_t *cert); /** * Close and destroy the secure socket. * * @param ssock The secure socket. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock); /** * Associate arbitrary data with the secure socket. Application may * inspect this data in the callbacks and associate it with higher * level processing. * * @param ssock The secure socket. * @param user_data The user data to be associated with the secure * socket. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_ssl_sock_set_user_data(pj_ssl_sock_t *ssock, void *user_data); /** * Retrieve the user data previously associated with this secure * socket. * * @param ssock The secure socket. * * @return The user data. */ PJ_DECL(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock); /** * Retrieve the local address and port used by specified secure socket. * * @param ssock The secure socket. * @param info The info buffer to be set, see #pj_ssl_sock_info. * * @return PJ_SUCCESS on successful. */ PJ_DECL(pj_status_t) pj_ssl_sock_get_info(pj_ssl_sock_t *ssock, pj_ssl_sock_info *info); /** * Starts read operation on this secure socket. This function will create * \a async_cnt number of buffers (the \a async_cnt parameter was given * in \a pj_ssl_sock_create() function) where each buffer is \a buff_size * long. The buffers are allocated from the specified \a pool. Once the * buffers are created, it then issues \a async_cnt number of asynchronous * \a recv() operations to the socket and returns back to caller. Incoming * data on the socket will be reported back to application via the * \a on_data_read() callback. * * Application only needs to call this function once to initiate read * operations. Further read operations will be done automatically by the * secure socket when \a on_data_read() callback returns non-zero. * * @param ssock The secure socket. * @param pool Pool used to allocate buffers for incoming data. * @param buff_size The size of each buffer, in bytes. * @param flags Flags to be given to pj_ioqueue_recv(). * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_ssl_sock_start_read(pj_ssl_sock_t *ssock, pj_pool_t *pool, unsigned buff_size, pj_uint32_t flags); /** * Same as #pj_ssl_sock_start_read(), except that the application * supplies the buffers for the read operation so that the acive socket * does not have to allocate the buffers. * * @param ssock The secure socket. * @param pool Pool used to allocate buffers for incoming data. * @param buff_size The size of each buffer, in bytes. * @param readbuf Array of packet buffers, each has buff_size size. * @param flags Flags to be given to pj_ioqueue_recv(). * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_ssl_sock_start_read2(pj_ssl_sock_t *ssock, pj_pool_t *pool, unsigned buff_size, void *readbuf[], pj_uint32_t flags); /** * Same as pj_ssl_sock_start_read(), except that this function is used * only for datagram sockets, and it will trigger \a on_data_recvfrom() * callback instead. * * @param ssock The secure socket. * @param pool Pool used to allocate buffers for incoming data. * @param buff_size The size of each buffer, in bytes. * @param flags Flags to be given to pj_ioqueue_recvfrom(). * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_ssl_sock_start_recvfrom(pj_ssl_sock_t *ssock, pj_pool_t *pool, unsigned buff_size, pj_uint32_t flags); /** * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() * operation takes the buffer from the argument rather than creating * new ones. * * @param ssock The secure socket. * @param pool Pool used to allocate buffers for incoming data. * @param buff_size The size of each buffer, in bytes. * @param readbuf Array of packet buffers, each has buff_size size. * @param flags Flags to be given to pj_ioqueue_recvfrom(). * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_ssl_sock_start_recvfrom2(pj_ssl_sock_t *ssock, pj_pool_t *pool, unsigned buff_size, void *readbuf[], pj_uint32_t flags); /** * Send data using the socket. * * @param ssock The secure socket. * @param send_key The operation key to send the data, which is useful * if application wants to submit multiple pending * send operations and want to track which exact data * has been sent in the \a on_data_sent() callback. * @param data The data to be sent. This data must remain valid * until the data has been sent. * @param size The size of the data. * @param flags Flags to be given to pj_ioqueue_send(). * * @return PJ_SUCCESS if data has been sent immediately, or * PJ_EPENDING if data cannot be sent immediately or * PJ_ENOMEM when sending buffer could not handle all * queued data, see \a send_buffer_size. The callback * \a on_data_sent() will be called when data is actually * sent. Any other return value indicates error condition. */ PJ_DECL(pj_status_t) pj_ssl_sock_send(pj_ssl_sock_t *ssock, pj_ioqueue_op_key_t *send_key, const void *data, pj_ssize_t *size, unsigned flags); /** * Send datagram using the socket. * * @param ssock The secure socket. * @param send_key The operation key to send the data, which is useful * if application wants to submit multiple pending * send operations and want to track which exact data * has been sent in the \a on_data_sent() callback. * @param data The data to be sent. This data must remain valid * until the data has been sent. * @param size The size of the data. * @param flags Flags to be given to pj_ioqueue_send(). * @param addr The destination address. * @param addr_len Length of buffer containing destination address. * * @return PJ_SUCCESS if data has been sent immediately, or * PJ_EPENDING if data cannot be sent immediately. In * this case the \a on_data_sent() callback will be * called when data is actually sent. Any other return * value indicates error condition. */ PJ_DECL(pj_status_t) pj_ssl_sock_sendto(pj_ssl_sock_t *ssock, pj_ioqueue_op_key_t *send_key, const void *data, pj_ssize_t *size, unsigned flags, const pj_sockaddr_t *addr, int addr_len); /** * Starts asynchronous socket accept() operations on this secure socket. * This function will issue \a async_cnt number of asynchronous \a accept() * operations to the socket and returns back to caller. Incoming * connection on the socket will be reported back to application via the * \a on_accept_complete() callback. * * Application only needs to call this function once to initiate accept() * operations. Further accept() operations will be done automatically by * the secure socket when \a on_accept_complete() callback returns non-zero. * * @param ssock The secure socket. * @param pool Pool used to allocate some internal data for the * operation. * @param localaddr Local address to bind on. * @param addr_len Length of buffer containing local address. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_ssl_sock_start_accept(pj_ssl_sock_t *ssock, pj_pool_t *pool, const pj_sockaddr_t *local_addr, int addr_len); /** * Same as #pj_ssl_sock_start_accept(), but application can provide * a secure socket parameter, which will be used to create a new secure * socket reported in \a on_accept_complete() callback when there is * an incoming connection. * * @param ssock The secure socket. * @param pool Pool used to allocate some internal data for the * operation. * @param localaddr Local address to bind on. * @param addr_len Length of buffer containing local address. * @param newsock_param Secure socket parameter for new accepted sockets. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_ssl_sock_start_accept2(pj_ssl_sock_t *ssock, pj_pool_t *pool, const pj_sockaddr_t *local_addr, int addr_len, const pj_ssl_sock_param *newsock_param); /** * Starts asynchronous socket connect() operation and SSL/TLS handshaking * for this socket. Once the connection is done (either successfully or not), * the \a on_connect_complete() callback will be called. * * @param ssock The secure socket. * @param pool The pool to allocate some internal data for the * operation. * @param localaddr Local address. * @param remaddr Remote address. * @param addr_len Length of buffer containing above addresses. * * @return PJ_SUCCESS if connection can be established immediately * or PJ_EPENDING if connection cannot be established * immediately. In this case the \a on_connect_complete() * callback will be called when connection is complete. * Any other return value indicates error condition. */ PJ_DECL(pj_status_t) pj_ssl_sock_start_connect(pj_ssl_sock_t *ssock, pj_pool_t *pool, const pj_sockaddr_t *localaddr, const pj_sockaddr_t *remaddr, int addr_len); /** * Starts SSL/TLS renegotiation over an already established SSL connection * for this socket. This operation is performed transparently, no callback * will be called once the renegotiation completed successfully. However, * when the renegotiation fails, the connection will be closed and callback * \a on_data_read() will be invoked with non-PJ_SUCCESS status code. * * @param ssock The secure socket. * * @return PJ_SUCCESS if renegotiation is completed immediately, * or PJ_EPENDING if renegotiation has been started and * waiting for completion, or the appropriate error code * on failure. */ PJ_DECL(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock); /** * @} */ PJ_END_DECL #endif /* __PJ_SSL_SOCK_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/string.h ================================================ /* $Id: string.h 4440 2013-03-14 07:18:13Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_STRING_H__ #define __PJ_STRING_H__ /** * @file string.h * @brief PJLIB String Operations. */ #include #include PJ_BEGIN_DECL /** * @defgroup PJ_PSTR String Operations * @ingroup PJ_DS * @{ * This module provides string manipulation API. * * \section pj_pstr_not_null_sec PJLIB String is NOT Null Terminated! * * That is the first information that developers need to know. Instead * of using normal C string, strings in PJLIB are represented as * pj_str_t structure below: * *
 *   typedef struct pj_str_t
 *   {
 *       char      *ptr;
 *       pj_size_t  slen;
 *   } pj_str_t;
 * 
* * There are some advantages of using this approach: * - the string can point to arbitrary location in memory even * if the string in that location is not null terminated. This is * most usefull for text parsing, where the parsed text can just * point to the original text in the input. If we use C string, * then we will have to copy the text portion from the input * to a string variable. * - because the length of the string is known, string copy operation * can be made more efficient. * * Most of APIs in PJLIB that expect or return string will represent * the string as pj_str_t instead of normal C string. * * \section pj_pstr_examples_sec Examples * * For some examples, please see: * - @ref page_pjlib_string_test */ /** * Create string initializer from a normal C string. * * @param str Null terminated string to be stored. * * @return pj_str_t. */ PJ_IDECL(pj_str_t) pj_str(char *str); /** * Create constant string from normal C string. * * @param str The string to be initialized. * @param s Null terminated string. * * @return pj_str_t. */ PJ_INLINE(const pj_str_t*) pj_cstr(pj_str_t *str, const char *s) { str->ptr = (char*)s; str->slen = s ? (pj_ssize_t)strlen(s) : 0; return str; } /** * Set the pointer and length to the specified value. * * @param str the string. * @param ptr pointer to set. * @param length length to set. * * @return the string. */ PJ_INLINE(pj_str_t*) pj_strset( pj_str_t *str, char *ptr, pj_size_t length) { str->ptr = ptr; str->slen = (pj_ssize_t)length; return str; } /** * Set the pointer and length of the string to the source string, which * must be NULL terminated. * * @param str the string. * @param src pointer to set. * * @return the string. */ PJ_INLINE(pj_str_t*) pj_strset2( pj_str_t *str, char *src) { str->ptr = src; str->slen = src ? (pj_ssize_t)strlen(src) : 0; return str; } /** * Set the pointer and the length of the string. * * @param str The target string. * @param begin The start of the string. * @param end The end of the string. * * @return the target string. */ PJ_INLINE(pj_str_t*) pj_strset3( pj_str_t *str, char *begin, char *end ) { str->ptr = begin; str->slen = (pj_ssize_t)(end-begin); return str; } /** * Assign string. * * @param dst The target string. * @param src The source string. * * @return the target string. */ PJ_IDECL(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src ); /** * Copy string contents. * * @param dst The target string. * @param src The source string. * * @return the target string. */ PJ_IDECL(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src); /** * Copy string contents. * * @param dst The target string. * @param src The source string. * * @return the target string. */ PJ_IDECL(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src); /** * Copy source string to destination up to the specified max length. * * @param dst The target string. * @param src The source string. * @param max Maximum characters to copy. * * @return the target string. */ PJ_IDECL(pj_str_t*) pj_strncpy(pj_str_t *dst, const pj_str_t *src, pj_ssize_t max); /** * Copy source string to destination up to the specified max length, * and NULL terminate the destination. If source string length is * greater than or equal to max, then max-1 will be copied. * * @param dst The target string. * @param src The source string. * @param max Maximum characters to copy. * * @return the target string. */ PJ_IDECL(pj_str_t*) pj_strncpy_with_null(pj_str_t *dst, const pj_str_t *src, pj_ssize_t max); /** * Duplicate string. * * @param pool The pool. * @param dst The string result. * @param src The string to duplicate. * * @return the string result. */ PJ_IDECL(pj_str_t*) pj_strdup(pj_pool_t *pool, pj_str_t *dst, const pj_str_t *src); /** * Duplicate string and NULL terminate the destination string. * * @param pool The pool. * @param dst The string result. * @param src The string to duplicate. * * @return The string result. */ PJ_IDECL(pj_str_t*) pj_strdup_with_null(pj_pool_t *pool, pj_str_t *dst, const pj_str_t *src); /** * Duplicate string. * * @param pool The pool. * @param dst The string result. * @param src The string to duplicate. * * @return the string result. */ PJ_IDECL(pj_str_t*) pj_strdup2(pj_pool_t *pool, pj_str_t *dst, const char *src); /** * Duplicate string and NULL terminate the destination string. * * @param pool The pool. * @param dst The string result. * @param src The string to duplicate. * * @return The string result. */ PJ_IDECL(pj_str_t*) pj_strdup2_with_null(pj_pool_t *pool, pj_str_t *dst, const char *src); /** * Duplicate string. * * @param pool The pool. * @param src The string to duplicate. * * @return the string result. */ PJ_IDECL(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src); /** * Return the length of the string. * * @param str The string. * * @return the length of the string. */ PJ_INLINE(pj_size_t) pj_strlen( const pj_str_t *str ) { return str->slen; } /** * Return the pointer to the string data. * * @param str The string. * * @return the pointer to the string buffer. */ PJ_INLINE(const char*) pj_strbuf( const pj_str_t *str ) { return str->ptr; } /** * Compare strings. * * @param str1 The string to compare. * @param str2 The string to compare. * * @return * - < 0 if str1 is less than str2 * - 0 if str1 is identical to str2 * - > 0 if str1 is greater than str2 */ PJ_IDECL(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2); /** * Compare strings. * * @param str1 The string to compare. * @param str2 The string to compare. * * @return * - < 0 if str1 is less than str2 * - 0 if str1 is identical to str2 * - > 0 if str1 is greater than str2 */ PJ_IDECL(int) pj_strcmp2( const pj_str_t *str1, const char *str2 ); /** * Compare strings. * * @param str1 The string to compare. * @param str2 The string to compare. * @param len The maximum number of characters to compare. * * @return * - < 0 if str1 is less than str2 * - 0 if str1 is identical to str2 * - > 0 if str1 is greater than str2 */ PJ_IDECL(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2, pj_size_t len); /** * Compare strings. * * @param str1 The string to compare. * @param str2 The string to compare. * @param len The maximum number of characters to compare. * * @return * - < 0 if str1 is less than str2 * - 0 if str1 is identical to str2 * - > 0 if str1 is greater than str2 */ PJ_IDECL(int) pj_strncmp2( const pj_str_t *str1, const char *str2, pj_size_t len); /** * Perform case-insensitive comparison to the strings. * * @param str1 The string to compare. * @param str2 The string to compare. * * @return * - < 0 if str1 is less than str2 * - 0 if str1 is equal to str2 * - > 0 if str1 is greater than str2 */ PJ_IDECL(int) pj_stricmp(const pj_str_t *str1, const pj_str_t *str2); /** * Perform lowercase comparison to the strings which consists of only * alnum characters. More over, it will only return non-zero if both * strings are not equal, not the usual negative or positive value. * * If non-alnum inputs are given, then the function may mistakenly * treat two strings as equal. * * @param str1 The string to compare. * @param str2 The string to compare. * @param len The length to compare. * * @return * - 0 if str1 is equal to str2 * - (-1) if not equal. */ #if defined(PJ_HAS_STRICMP_ALNUM) && PJ_HAS_STRICMP_ALNUM!=0 PJ_IDECL(int) strnicmp_alnum(const char *str1, const char *str2, int len); #else #define strnicmp_alnum pj_ansi_strnicmp #endif /** * Perform lowercase comparison to the strings which consists of only * alnum characters. More over, it will only return non-zero if both * strings are not equal, not the usual negative or positive value. * * If non-alnum inputs are given, then the function may mistakenly * treat two strings as equal. * * @param str1 The string to compare. * @param str2 The string to compare. * * @return * - 0 if str1 is equal to str2 * - (-1) if not equal. */ #if defined(PJ_HAS_STRICMP_ALNUM) && PJ_HAS_STRICMP_ALNUM!=0 PJ_IDECL(int) pj_stricmp_alnum(const pj_str_t *str1, const pj_str_t *str2); #else #define pj_stricmp_alnum pj_stricmp #endif /** * Perform case-insensitive comparison to the strings. * * @param str1 The string to compare. * @param str2 The string to compare. * * @return * - < 0 if str1 is less than str2 * - 0 if str1 is identical to str2 * - > 0 if str1 is greater than str2 */ PJ_IDECL(int) pj_stricmp2( const pj_str_t *str1, const char *str2); /** * Perform case-insensitive comparison to the strings. * * @param str1 The string to compare. * @param str2 The string to compare. * @param len The maximum number of characters to compare. * * @return * - < 0 if str1 is less than str2 * - 0 if str1 is identical to str2 * - > 0 if str1 is greater than str2 */ PJ_IDECL(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2, pj_size_t len); /** * Perform case-insensitive comparison to the strings. * * @param str1 The string to compare. * @param str2 The string to compare. * @param len The maximum number of characters to compare. * * @return * - < 0 if str1 is less than str2 * - 0 if str1 is identical to str2 * - > 0 if str1 is greater than str2 */ PJ_IDECL(int) pj_strnicmp2( const pj_str_t *str1, const char *str2, pj_size_t len); /** * Concatenate strings. * * @param dst The destination string. * @param src The source string. */ PJ_IDECL(void) pj_strcat(pj_str_t *dst, const pj_str_t *src); /** * Concatenate strings. * * @param dst The destination string. * @param src The source string. */ PJ_IDECL(void) pj_strcat2(pj_str_t *dst, const char *src); /** * Finds a character in a string. * * @param str The string. * @param chr The character to find. * * @return the pointer to first character found, or NULL. */ PJ_INLINE(char*) pj_strchr( const pj_str_t *str, int chr) { return (char*) memchr((char*)str->ptr, chr, str->slen); } /** * Find the occurence of a substring substr in string str. * * @param str The string to search. * @param substr The string to search fo. * * @return the pointer to the position of substr in str, or NULL. Note * that if str is not NULL terminated, the returned pointer * is pointing to non-NULL terminated string. */ PJ_DECL(char*) pj_strstr(const pj_str_t *str, const pj_str_t *substr); /** * Performs substring lookup like pj_strstr() but ignores the case of * both strings. * * @param str The string to search. * @param substr The string to search fo. * * @return the pointer to the position of substr in str, or NULL. Note * that if str is not NULL terminated, the returned pointer * is pointing to non-NULL terminated string. */ PJ_DECL(char*) pj_stristr(const pj_str_t *str, const pj_str_t *substr); /** * Remove (trim) leading whitespaces from the string. * * @param str The string. * * @return the string. */ PJ_DECL(pj_str_t*) pj_strltrim( pj_str_t *str ); /** * Remove (trim) the trailing whitespaces from the string. * * @param str The string. * * @return the string. */ PJ_DECL(pj_str_t*) pj_strrtrim( pj_str_t *str ); /** * Remove (trim) leading and trailing whitespaces from the string. * * @param str The string. * * @return the string. */ PJ_IDECL(pj_str_t*) pj_strtrim( pj_str_t *str ); /** * Initialize the buffer with some random string. Note that the * generated string is not NULL terminated. * * @param str the string to store the result. * @param length the length of the random string to generate. * * @return the string. */ PJ_DECL(char*) pj_create_random_string(char *str, pj_size_t length); /** * Convert string to signed integer. The conversion will stop as * soon as non-digit character is found or all the characters have * been processed. * * @param str the string. * * @return the integer. */ PJ_DECL(long) pj_strtol(const pj_str_t *str); /** * Convert string to unsigned integer. The conversion will stop as * soon as non-digit character is found or all the characters have * been processed. * * @param str the string. * * @return the unsigned integer. */ PJ_DECL(unsigned long) pj_strtoul(const pj_str_t *str); /** * Convert strings to an unsigned long-integer value. * This function stops reading the string input either when the number * of characters has exceeded the length of the input or it has read * the first character it cannot recognize as part of a number, that is * a character greater than or equal to base. * * @param str The input string. * @param endptr Optional pointer to receive the remainder/unparsed * portion of the input. * @param base Number base to use. * * @return the unsigned integer number. */ PJ_DECL(unsigned long) pj_strtoul2(const pj_str_t *str, pj_str_t *endptr, unsigned base); /** * Convert string to float. * * @param str the string. * * @return the value. */ PJ_DECL(float) pj_strtof(const pj_str_t *str); /** * Utility to convert unsigned integer to string. Note that the * string will be NULL terminated. * * @param val the unsigned integer value. * @param buf the buffer * * @return the number of characters written */ PJ_DECL(int) pj_utoa(unsigned long val, char *buf); /** * Convert unsigned integer to string with minimum digits. Note that the * string will be NULL terminated. * * @param val The unsigned integer value. * @param buf The buffer. * @param min_dig Minimum digits to be printed, or zero to specify no * minimum digit. * @param pad The padding character to be put in front of the string * when the digits is less than minimum. * * @return the number of characters written. */ PJ_DECL(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad); /** * Fill the memory location with zero. * * @param dst The destination buffer. * @param size The number of bytes. */ PJ_INLINE(void) pj_bzero(void *dst, pj_size_t size) { #if defined(PJ_HAS_BZERO) && PJ_HAS_BZERO!=0 bzero(dst, size); #else memset(dst, 0, size); #endif } /** * Fill the memory location with value. * * @param dst The destination buffer. * @param c Character to set. * @param size The number of characters. * * @return the value of dst. */ PJ_INLINE(void*) pj_memset(void *dst, int c, pj_size_t size) { return memset(dst, c, size); } /** * Copy buffer. * * @param dst The destination buffer. * @param src The source buffer. * @param size The size to copy. * * @return the destination buffer. */ PJ_INLINE(void*) pj_memcpy(void *dst, const void *src, pj_size_t size) { return memcpy(dst, src, size); } /** * Move memory. * * @param dst The destination buffer. * @param src The source buffer. * @param size The size to copy. * * @return the destination buffer. */ PJ_INLINE(void*) pj_memmove(void *dst, const void *src, pj_size_t size) { return memmove(dst, src, size); } /** * Compare buffers. * * @param buf1 The first buffer. * @param buf2 The second buffer. * @param size The size to compare. * * @return negative, zero, or positive value. */ PJ_INLINE(int) pj_memcmp(const void *buf1, const void *buf2, pj_size_t size) { return memcmp(buf1, buf2, size); } /** * Find character in the buffer. * * @param buf The buffer. * @param c The character to find. * @param size The size to check. * * @return the pointer to location where the character is found, or NULL if * not found. */ PJ_INLINE(void*) pj_memchr(const void *buf, int c, pj_size_t size) { return (void*)memchr((void*)buf, c, size); } /** * @} */ #if PJ_FUNCTIONS_ARE_INLINED # include #endif PJ_END_DECL #endif /* __PJ_STRING_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/string_i.h ================================================ /* $Id: string_i.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include PJ_IDEF(pj_str_t) pj_str(char *str) { pj_str_t dst; dst.ptr = str; dst.slen = str ? pj_ansi_strlen(str) : 0; return dst; } PJ_IDEF(pj_str_t*) pj_strdup(pj_pool_t *pool, pj_str_t *dst, const pj_str_t *src) { /* Without this, destination will be corrupted */ if (dst == src) return dst; if (src->slen) { dst->ptr = (char*)pj_pool_alloc(pool, src->slen); pj_memcpy(dst->ptr, src->ptr, src->slen); } dst->slen = src->slen; return dst; } PJ_IDEF(pj_str_t*) pj_strdup_with_null( pj_pool_t *pool, pj_str_t *dst, const pj_str_t *src) { dst->ptr = (char*)pj_pool_alloc(pool, src->slen+1); if (src->slen) { pj_memcpy(dst->ptr, src->ptr, src->slen); } dst->slen = src->slen; dst->ptr[dst->slen] = '\0'; return dst; } PJ_IDEF(pj_str_t*) pj_strdup2(pj_pool_t *pool, pj_str_t *dst, const char *src) { dst->slen = src ? pj_ansi_strlen(src) : 0; if (dst->slen) { dst->ptr = (char*)pj_pool_alloc(pool, dst->slen); pj_memcpy(dst->ptr, src, dst->slen); } else { dst->ptr = NULL; } return dst; } PJ_IDEF(pj_str_t*) pj_strdup2_with_null( pj_pool_t *pool, pj_str_t *dst, const char *src) { dst->slen = src ? pj_ansi_strlen(src) : 0; dst->ptr = (char*)pj_pool_alloc(pool, dst->slen+1); if (dst->slen) { pj_memcpy(dst->ptr, src, dst->slen); } dst->ptr[dst->slen] = '\0'; return dst; } PJ_IDEF(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src) { pj_str_t temp; pj_strdup2(pool, &temp, src); return temp; } PJ_IDEF(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src ) { dst->ptr = src->ptr; dst->slen = src->slen; return dst; } PJ_IDEF(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src) { dst->slen = src->slen; if (src->slen > 0) pj_memcpy(dst->ptr, src->ptr, src->slen); return dst; } PJ_IDEF(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src) { dst->slen = src ? pj_ansi_strlen(src) : 0; if (dst->slen > 0) pj_memcpy(dst->ptr, src, dst->slen); return dst; } PJ_IDEF(pj_str_t*) pj_strncpy( pj_str_t *dst, const pj_str_t *src, pj_ssize_t max) { pj_assert(max >= 0); if (max > src->slen) max = src->slen; pj_memcpy(dst->ptr, src->ptr, max); dst->slen = max; return dst; } PJ_IDEF(pj_str_t*) pj_strncpy_with_null( pj_str_t *dst, const pj_str_t *src, pj_ssize_t max) { pj_assert(max > 0); if (max <= src->slen) max = max-1; else max = src->slen; pj_memcpy(dst->ptr, src->ptr, max); dst->ptr[max] = '\0'; dst->slen = max; return dst; } PJ_IDEF(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2) { if (str1->slen == 0) { return str2->slen==0 ? 0 : -1; } else if (str2->slen == 0) { return 1; } else { pj_size_t min = (str1->slen < str2->slen)? str1->slen : str2->slen; int res = pj_memcmp(str1->ptr, str2->ptr, min); if (res == 0) { return (str1->slen < str2->slen) ? -1 : (str1->slen == str2->slen ? 0 : 1); } else { return res; } } } PJ_IDEF(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2, pj_size_t len) { pj_str_t copy1, copy2; if (len < (unsigned)str1->slen) { copy1.ptr = str1->ptr; copy1.slen = len; str1 = ©1; } if (len < (unsigned)str2->slen) { copy2.ptr = str2->ptr; copy2.slen = len; str2 = ©2; } return pj_strcmp(str1, str2); } PJ_IDEF(int) pj_strncmp2( const pj_str_t *str1, const char *str2, pj_size_t len) { pj_str_t copy2; if (str2) { copy2.ptr = (char*)str2; copy2.slen = pj_ansi_strlen(str2); } else { copy2.slen = 0; } return pj_strncmp(str1, ©2, len); } PJ_IDEF(int) pj_strcmp2( const pj_str_t *str1, const char *str2 ) { pj_str_t copy2; if (str2) { copy2.ptr = (char*)str2; copy2.slen = pj_ansi_strlen(str2); } else { copy2.ptr = NULL; copy2.slen = 0; } return pj_strcmp(str1, ©2); } PJ_IDEF(int) pj_stricmp( const pj_str_t *str1, const pj_str_t *str2) { if (str1->slen == 0) { return str2->slen==0 ? 0 : -1; } else if (str2->slen == 0) { return 1; } else { pj_size_t min = (str1->slen < str2->slen)? str1->slen : str2->slen; int res = pj_ansi_strnicmp(str1->ptr, str2->ptr, min); if (res == 0) { return (str1->slen < str2->slen) ? -1 : (str1->slen == str2->slen ? 0 : 1); } else { return res; } } } #if defined(PJ_HAS_STRICMP_ALNUM) && PJ_HAS_STRICMP_ALNUM!=0 PJ_IDEF(int) strnicmp_alnum( const char *str1, const char *str2, int len) { if (len==0) return 0; else { register const pj_uint32_t *p1 = (pj_uint32_t*)str1, *p2 = (pj_uint32_t*)str2; while (len > 3 && (*p1 & 0x5F5F5F5F)==(*p2 & 0x5F5F5F5F)) ++p1, ++p2, len-=4; if (len > 3) return -1; #if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0 else if (len==3) return ((*p1 & 0x005F5F5F)==(*p2 & 0x005F5F5F)) ? 0 : -1; else if (len==2) return ((*p1 & 0x00005F5F)==(*p2 & 0x00005F5F)) ? 0 : -1; else if (len==1) return ((*p1 & 0x0000005F)==(*p2 & 0x0000005F)) ? 0 : -1; #else else if (len==3) return ((*p1 & 0x5F5F5F00)==(*p2 & 0x5F5F5F00)) ? 0 : -1; else if (len==2) return ((*p1 & 0x5F5F0000)==(*p2 & 0x5F5F0000)) ? 0 : -1; else if (len==1) return ((*p1 & 0x5F000000)==(*p2 & 0x5F000000)) ? 0 : -1; #endif else return 0; } } PJ_IDEF(int) pj_stricmp_alnum(const pj_str_t *str1, const pj_str_t *str2) { register int len = str1->slen; if (len != str2->slen) { return (len < str2->slen) ? -1 : 1; } else if (len == 0) { return 0; } else { register const pj_uint32_t *p1 = (pj_uint32_t*)str1->ptr, *p2 = (pj_uint32_t*)str2->ptr; while (len > 3 && (*p1 & 0x5F5F5F5F)==(*p2 & 0x5F5F5F5F)) ++p1, ++p2, len-=4; if (len > 3) return -1; #if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0 else if (len==3) return ((*p1 & 0x005F5F5F)==(*p2 & 0x005F5F5F)) ? 0 : -1; else if (len==2) return ((*p1 & 0x00005F5F)==(*p2 & 0x00005F5F)) ? 0 : -1; else if (len==1) return ((*p1 & 0x0000005F)==(*p2 & 0x0000005F)) ? 0 : -1; #else else if (len==3) return ((*p1 & 0x5F5F5F00)==(*p2 & 0x5F5F5F00)) ? 0 : -1; else if (len==2) return ((*p1 & 0x5F5F0000)==(*p2 & 0x5F5F0000)) ? 0 : -1; else if (len==1) return ((*p1 & 0x5F000000)==(*p2 & 0x5F000000)) ? 0 : -1; #endif else return 0; } } #endif /* PJ_HAS_STRICMP_ALNUM */ PJ_IDEF(int) pj_stricmp2( const pj_str_t *str1, const char *str2) { pj_str_t copy2; if (str2) { copy2.ptr = (char*)str2; copy2.slen = pj_ansi_strlen(str2); } else { copy2.ptr = NULL; copy2.slen = 0; } return pj_stricmp(str1, ©2); } PJ_IDEF(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2, pj_size_t len) { pj_str_t copy1, copy2; if (len < (unsigned)str1->slen) { copy1.ptr = str1->ptr; copy1.slen = len; str1 = ©1; } if (len < (unsigned)str2->slen) { copy2.ptr = str2->ptr; copy2.slen = len; str2 = ©2; } return pj_stricmp(str1, str2); } PJ_IDEF(int) pj_strnicmp2( const pj_str_t *str1, const char *str2, pj_size_t len) { pj_str_t copy2; if (str2) { copy2.ptr = (char*)str2; copy2.slen = pj_ansi_strlen(str2); } else { copy2.slen = 0; } return pj_strnicmp(str1, ©2, len); } PJ_IDEF(void) pj_strcat(pj_str_t *dst, const pj_str_t *src) { if (src->slen) { pj_memcpy(dst->ptr + dst->slen, src->ptr, src->slen); dst->slen += src->slen; } } PJ_IDEF(void) pj_strcat2(pj_str_t *dst, const char *str) { pj_size_t len = str? pj_ansi_strlen(str) : 0; if (len) { pj_memcpy(dst->ptr + dst->slen, str, len); dst->slen += len; } } PJ_IDEF(pj_str_t*) pj_strtrim( pj_str_t *str ) { pj_strltrim(str); pj_strrtrim(str); return str; } ================================================ FILE: deps/pjsip/pjlib/include/pj/timer.h ================================================ /* $Id: timer.h 4359 2013-02-21 11:18:36Z bennylp $ */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_TIMER_H__ #define __PJ_TIMER_H__ /** * @file timer.h * @brief Timer Heap */ #include #include PJ_BEGIN_DECL /** * @defgroup PJ_TIMER Timer Heap Management. * @ingroup PJ_MISC * @brief * The timer scheduling implementation here is based on ACE library's * ACE_Timer_Heap, with only little modification to suit our library's style * (I even left most of the comments in the original source). * * To quote the original quote in ACE_Timer_Heap_T class: * * This implementation uses a heap-based callout queue of * absolute times. Therefore, in the average and worst case, * scheduling, canceling, and expiring timers is O(log N) (where * N is the total number of timers). In addition, we can also * preallocate as many \a ACE_Timer_Nodes as there are slots in * the heap. This allows us to completely remove the need for * dynamic memory allocation, which is important for real-time * systems. * * You can find the fine ACE library at: * http://www.cs.wustl.edu/~schmidt/ACE.html * * ACE is Copyright (C)1993-2006 Douglas C. Schmidt * * @{ * * \section pj_timer_examples_sec Examples * * For some examples on how to use the timer heap, please see the link below. * * - \ref page_pjlib_timer_test */ /** * The type for internal timer ID. */ typedef int pj_timer_id_t; /** * Forward declaration for pj_timer_entry. */ struct pj_timer_entry; /** * The type of callback function to be called by timer scheduler when a timer * has expired. * * @param timer_heap The timer heap. * @param entry Timer entry which timer's has expired. */ typedef void pj_timer_heap_callback(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry); /** * This structure represents an entry to the timer. */ typedef struct pj_timer_entry { /** * User data to be associated with this entry. * Applications normally will put the instance of object that * owns the timer entry in this field. */ void *user_data; /** * Arbitrary ID assigned by the user/owner of this entry. * Applications can use this ID to distinguish multiple * timer entries that share the same callback and user_data. */ int id; /** * Callback to be called when the timer expires. */ pj_timer_heap_callback *cb; /** * Internal unique timer ID, which is assigned by the timer heap. * Application should not touch this ID. */ pj_timer_id_t _timer_id; /** * The future time when the timer expires, which the value is updated * by timer heap when the timer is scheduled. */ pj_time_val _timer_value; /** * Internal: the group lock used by this entry, set when * pj_timer_heap_schedule_w_lock() is used. */ pj_grp_lock_t *_grp_lock; #if PJ_TIMER_DEBUG const char *src_file; int src_line; #endif } pj_timer_entry; /** * Calculate memory size required to create a timer heap. * * @param count Number of timer entries to be supported. * @return Memory size requirement in bytes. */ PJ_DECL(pj_size_t) pj_timer_heap_mem_size(pj_size_t count); /** * Create a timer heap. * * @param pool The pool where allocations in the timer heap will be * allocated. The timer heap will dynamicly allocate * more storate from the pool if the number of timer * entries registered is more than the size originally * requested when calling this function. * @param count The maximum number of timer entries to be supported * initially. If the application registers more entries * during runtime, then the timer heap will resize. * @param ht Pointer to receive the created timer heap. * * @return PJ_SUCCESS, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_timer_heap_create( pj_pool_t *pool, pj_size_t count, pj_timer_heap_t **ht); /** * Destroy the timer heap. * * @param ht The timer heap. */ PJ_DECL(void) pj_timer_heap_destroy( pj_timer_heap_t *ht ); /** * Set lock object to be used by the timer heap. By default, the timer heap * uses dummy synchronization. * * @param ht The timer heap. * @param lock The lock object to be used for synchronization. * @param auto_del If nonzero, the lock object will be destroyed when * the timer heap is destroyed. */ PJ_DECL(void) pj_timer_heap_set_lock( pj_timer_heap_t *ht, pj_lock_t *lock, pj_bool_t auto_del ); /** * Set maximum number of timed out entries to process in a single poll. * * @param ht The timer heap. * @param count Number of entries. * * @return The old number. */ PJ_DECL(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht, unsigned count ); /** * Initialize a timer entry. Application should call this function at least * once before scheduling the entry to the timer heap, to properly initialize * the timer entry. * * @param entry The timer entry to be initialized. * @param id Arbitrary ID assigned by the user/owner of this entry. * Applications can use this ID to distinguish multiple * timer entries that share the same callback and user_data. * @param user_data User data to be associated with this entry. * Applications normally will put the instance of object that * owns the timer entry in this field. * @param cb Callback function to be called when the timer elapses. * * @return The timer entry itself. */ PJ_DECL(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry, int id, void *user_data, pj_timer_heap_callback *cb ); /** * Queries whether a timer entry is currently running. * * @param entry The timer entry to query. * * @return PJ_TRUE if the timer is running. PJ_FALSE if not. */ PJ_DECL(pj_bool_t) pj_timer_entry_running( pj_timer_entry *entry ); /** * Schedule a timer entry which will expire AFTER the specified delay. * * @param ht The timer heap. * @param entry The entry to be registered. * @param delay The interval to expire. * @return PJ_SUCCESS, or the appropriate error code. */ #if PJ_TIMER_DEBUG # define pj_timer_heap_schedule(ht,e,d) \ pj_timer_heap_schedule_dbg(ht,e,d,__FILE__,__LINE__) PJ_DECL(pj_status_t) pj_timer_heap_schedule_dbg( pj_timer_heap_t *ht, pj_timer_entry *entry, const pj_time_val *delay, const char *src_file, int src_line); #else PJ_DECL(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht, pj_timer_entry *entry, const pj_time_val *delay); #endif /* PJ_TIMER_DEBUG */ /** * Schedule a timer entry which will expire AFTER the specified delay, and * increment the reference counter of the group lock while the timer entry * is active. The group lock reference counter will automatically be released * after the timer callback is called or when the timer is cancelled. * * @param ht The timer heap. * @param entry The entry to be registered. * @param id_val The value to be set to the "id" field of the timer entry * once the timer is scheduled. * @param delay The interval to expire. * @param grp_lock The group lock. * * @return PJ_SUCCESS, or the appropriate error code. */ #if PJ_TIMER_DEBUG # define pj_timer_heap_schedule_w_grp_lock(ht,e,d,id,g) \ pj_timer_heap_schedule_w_grp_lock_dbg(ht,e,d,id,g,__FILE__,__LINE__) PJ_DECL(pj_status_t) pj_timer_heap_schedule_w_grp_lock_dbg( pj_timer_heap_t *ht, pj_timer_entry *entry, const pj_time_val *delay, int id_val, pj_grp_lock_t *grp_lock, const char *src_file, int src_line); #else PJ_DECL(pj_status_t) pj_timer_heap_schedule_w_grp_lock( pj_timer_heap_t *ht, pj_timer_entry *entry, const pj_time_val *delay, int id_val, pj_grp_lock_t *grp_lock); #endif /* PJ_TIMER_DEBUG */ /** * Cancel a previously registered timer. This will also decrement the * reference counter of the group lock associated with the timer entry, * if the entry was scheduled with one. * * @param ht The timer heap. * @param entry The entry to be cancelled. * @return The number of timer cancelled, which should be one if the * entry has really been registered, or zero if no timer was * cancelled. */ PJ_DECL(int) pj_timer_heap_cancel( pj_timer_heap_t *ht, pj_timer_entry *entry); /** * Cancel only if the previously registered timer is active. This will * also decrement the reference counter of the group lock associated * with the timer entry, if the entry was scheduled with one. In any * case, set the "id" to the specified value. * * @param ht The timer heap. * @param entry The entry to be cancelled. * @param id_val Value to be set to "id" * * @return The number of timer cancelled, which should be one if the * entry has really been registered, or zero if no timer was * cancelled. */ PJ_DECL(int) pj_timer_heap_cancel_if_active(pj_timer_heap_t *ht, pj_timer_entry *entry, int id_val); /** * Get the number of timer entries. * * @param ht The timer heap. * @return The number of timer entries. */ PJ_DECL(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht ); /** * Get the earliest time registered in the timer heap. The timer heap * MUST have at least one timer being scheduled (application should use * #pj_timer_heap_count() before calling this function). * * @param ht The timer heap. * @param timeval The time deadline of the earliest timer entry. * * @return PJ_SUCCESS, or PJ_ENOTFOUND if no entry is scheduled. */ PJ_DECL(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t *ht, pj_time_val *timeval); /** * Poll the timer heap, check for expired timers and call the callback for * each of the expired timers. * * Note: polling the timer heap is not necessary in Symbian. Please see * @ref PJ_SYMBIAN_OS for more info. * * @param ht The timer heap. * @param next_delay If this parameter is not NULL, it will be filled up with * the time delay until the next timer elapsed, or * PJ_MAXINT32 in the sec part if no entry exist. * * @return The number of timers expired. */ PJ_DECL(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht, pj_time_val *next_delay); #if PJ_TIMER_DEBUG /** * Dump timer heap entries. * * @param ht The timer heap. */ PJ_DECL(void) pj_timer_heap_dump(pj_timer_heap_t *ht); #endif /** * @} */ PJ_END_DECL #endif /* __PJ_TIMER_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/types.h ================================================ /* $Id: types.h 4359 2013-02-21 11:18:36Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_TYPES_H__ #define __PJ_TYPES_H__ /** * @file types.h * @brief Declaration of basic types and utility. */ /** * @defgroup PJ_BASIC Basic Data Types and Library Functionality. * @ingroup PJ_DS * @{ */ #include PJ_BEGIN_DECL /* ************************************************************************* */ /** Signed 32bit integer. */ typedef int pj_int32_t; /** Unsigned 32bit integer. */ typedef unsigned int pj_uint32_t; /** Signed 16bit integer. */ typedef short pj_int16_t; /** Unsigned 16bit integer. */ typedef unsigned short pj_uint16_t; /** Signed 8bit integer. */ typedef signed char pj_int8_t; /** Unsigned 8bit integer. */ typedef unsigned char pj_uint8_t; /** Large unsigned integer. */ typedef size_t pj_size_t; /** Large signed integer. */ #if defined(PJ_WIN64) && PJ_WIN64!=0 typedef pj_int64_t pj_ssize_t; #else typedef long pj_ssize_t; #endif /** Status code. */ typedef int pj_status_t; /** Boolean. */ typedef int pj_bool_t; /** Native char type, which will be equal to wchar_t for Unicode * and char for ANSI. */ #if defined(PJ_NATIVE_STRING_IS_UNICODE) && PJ_NATIVE_STRING_IS_UNICODE!=0 typedef wchar_t pj_char_t; #else typedef char pj_char_t; #endif /** This macro creates Unicode or ANSI literal string depending whether * native platform string is Unicode or ANSI. */ #if defined(PJ_NATIVE_STRING_IS_UNICODE) && PJ_NATIVE_STRING_IS_UNICODE!=0 # define PJ_T(literal_str) L##literal_str #else # define PJ_T(literal_str) literal_str #endif /** Some constants */ enum pj_constants_ { /** Status is OK. */ PJ_SUCCESS=0, /** True value. */ PJ_TRUE=1, /** False value. */ PJ_FALSE=0 }; /** * File offset type. */ #if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0 typedef pj_int64_t pj_off_t; #else typedef pj_ssize_t pj_off_t; #endif /* ************************************************************************* */ /* * Data structure types. */ /** * This type is used as replacement to legacy C string, and used throughout * the library. By convention, the string is NOT null terminated. */ struct pj_str_t { /** Buffer pointer, which is by convention NOT null terminated. */ char *ptr; /** The length of the string. */ pj_ssize_t slen; }; /** * This structure represents high resolution (64bit) time value. The time * values represent time in cycles, which is retrieved by calling * #pj_get_timestamp(). */ typedef union pj_timestamp { struct { #if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0 pj_uint32_t lo; /**< Low 32-bit value of the 64-bit value. */ pj_uint32_t hi; /**< high 32-bit value of the 64-bit value. */ #else pj_uint32_t hi; /**< high 32-bit value of the 64-bit value. */ pj_uint32_t lo; /**< Low 32-bit value of the 64-bit value. */ #endif } u32; /**< The 64-bit value as two 32-bit values. */ #if PJ_HAS_INT64 pj_uint64_t u64; /**< The whole 64-bit value, where available. */ #endif } pj_timestamp; /** * The opaque data type for linked list, which is used as arguments throughout * the linked list operations. */ typedef void pj_list_type; /** * List. */ typedef struct pj_list pj_list; /** * Opaque data type for hash tables. */ typedef struct pj_hash_table_t pj_hash_table_t; /** * Opaque data type for hash entry (only used internally by hash table). */ typedef struct pj_hash_entry pj_hash_entry; /** * Data type for hash search iterator. * This structure should be opaque, however applications need to declare * concrete variable of this type, that's why the declaration is visible here. */ typedef struct pj_hash_iterator_t { pj_uint32_t index; /**< Internal index. */ pj_hash_entry *entry; /**< Internal entry. */ } pj_hash_iterator_t; /** * Forward declaration for memory pool factory. */ typedef struct pj_pool_factory pj_pool_factory; /** * Opaque data type for memory pool. */ typedef struct pj_pool_t pj_pool_t; /** * Forward declaration for caching pool, a pool factory implementation. */ typedef struct pj_caching_pool pj_caching_pool; /** * This type is used as replacement to legacy C string, and used throughout * the library. */ typedef struct pj_str_t pj_str_t; /** * Opaque data type for I/O Queue structure. */ typedef struct pj_ioqueue_t pj_ioqueue_t; /** * Opaque data type for key that identifies a handle registered to the * I/O queue framework. */ typedef struct pj_ioqueue_key_t pj_ioqueue_key_t; /** * Opaque data to identify timer heap. */ typedef struct pj_timer_heap_t pj_timer_heap_t; /** * Opaque data type for atomic operations. */ typedef struct pj_atomic_t pj_atomic_t; /** * Value type of an atomic variable. */ typedef PJ_ATOMIC_VALUE_TYPE pj_atomic_value_t; /* ************************************************************************* */ /** Thread handle. */ typedef struct pj_thread_t pj_thread_t; /** Lock object. */ typedef struct pj_lock_t pj_lock_t; /** Group lock */ typedef struct pj_grp_lock_t pj_grp_lock_t; /** Mutex handle. */ typedef struct pj_mutex_t pj_mutex_t; /** Semaphore handle. */ typedef struct pj_sem_t pj_sem_t; /** Event object. */ typedef struct pj_event_t pj_event_t; /** Unidirectional stream pipe object. */ typedef struct pj_pipe_t pj_pipe_t; /** Operating system handle. */ typedef void *pj_oshandle_t; /** Socket handle. */ #if defined(PJ_WIN64) && PJ_WIN64!=0 typedef pj_int64_t pj_sock_t; #else typedef long pj_sock_t; #endif /** Generic socket address. */ typedef void pj_sockaddr_t; /** Forward declaration. */ typedef struct pj_sockaddr_in pj_sockaddr_in; /** Color type. */ typedef unsigned int pj_color_t; /** Exception id. */ typedef int pj_exception_id_t; /* ************************************************************************* */ /** Utility macro to compute the number of elements in static array. */ #define PJ_ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) /** Maximum value for signed 32-bit integer. */ #define PJ_MAXINT32 0x7FFFFFFFL /** * Length of object names. */ #define PJ_MAX_OBJ_NAME 32 /* ************************************************************************* */ /* * General. */ /** * Initialize the PJ Library. * This function must be called before using the library. The purpose of this * function is to initialize static library data, such as character table used * in random string generation, and to initialize operating system dependent * functionality (such as WSAStartup() in Windows). * * Apart from calling pj_init(), application typically should also initialize * the random seed by calling pj_srand(). * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pj_init(void); /** * Shutdown PJLIB. */ PJ_DECL(void) pj_shutdown(void); /** * Type of callback to register to pj_atexit(). */ typedef void (*pj_exit_callback)(void); /** * Register cleanup function to be called by PJLIB when pj_shutdown() is * called. * * @param func The function to be registered. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pj_atexit(pj_exit_callback func); /** * Swap the byte order of an 16bit data. * * @param val16 The 16bit data. * * @return An 16bit data with swapped byte order. */ PJ_INLINE(pj_int16_t) pj_swap16(pj_int16_t val16) { pj_uint8_t *p = (pj_uint8_t*)&val16; pj_uint8_t tmp = *p; *p = *(p+1); *(p+1) = tmp; return val16; } /** * Swap the byte order of an 32bit data. * * @param val32 The 32bit data. * * @return An 32bit data with swapped byte order. */ PJ_INLINE(pj_int32_t) pj_swap32(pj_int32_t val32) { pj_uint8_t *p = (pj_uint8_t*)&val32; pj_uint8_t tmp = *p; *p = *(p+3); *(p+3) = tmp; tmp = *(p+1); *(p+1) = *(p+2); *(p+2) = tmp; return val32; } /** * @} */ /** * @addtogroup PJ_TIME Time Data Type and Manipulation. * @ingroup PJ_MISC * @{ */ /** * Representation of time value in this library. * This type can be used to represent either an interval or a specific time * or date. */ typedef struct pj_time_val { /** The seconds part of the time. */ long sec; /** The miliseconds fraction of the time. */ long msec; } pj_time_val; /** * Normalize the value in time value. * @param t Time value to be normalized. */ PJ_DECL(void) pj_time_val_normalize(pj_time_val *t); /** * Get the total time value in miliseconds. This is the same as * multiplying the second part with 1000 and then add the miliseconds * part to the result. * * @param t The time value. * @return Total time in miliseconds. * @hideinitializer */ #define PJ_TIME_VAL_MSEC(t) ((t).sec * 1000 + (t).msec) /** * This macro will check if \a t1 is equal to \a t2. * * @param t1 The first time value to compare. * @param t2 The second time value to compare. * @return Non-zero if both time values are equal. * @hideinitializer */ #define PJ_TIME_VAL_EQ(t1, t2) ((t1).sec==(t2).sec && (t1).msec==(t2).msec) /** * This macro will check if \a t1 is greater than \a t2 * * @param t1 The first time value to compare. * @param t2 The second time value to compare. * @return Non-zero if t1 is greater than t2. * @hideinitializer */ #define PJ_TIME_VAL_GT(t1, t2) ((t1).sec>(t2).sec || \ ((t1).sec==(t2).sec && (t1).msec>(t2).msec)) /** * This macro will check if \a t1 is greater than or equal to \a t2 * * @param t1 The first time value to compare. * @param t2 The second time value to compare. * @return Non-zero if t1 is greater than or equal to t2. * @hideinitializer */ #define PJ_TIME_VAL_GTE(t1, t2) (PJ_TIME_VAL_GT(t1,t2) || \ PJ_TIME_VAL_EQ(t1,t2)) /** * This macro will check if \a t1 is less than \a t2 * * @param t1 The first time value to compare. * @param t2 The second time value to compare. * @return Non-zero if t1 is less than t2. * @hideinitializer */ #define PJ_TIME_VAL_LT(t1, t2) (!(PJ_TIME_VAL_GTE(t1,t2))) /** * This macro will check if \a t1 is less than or equal to \a t2. * * @param t1 The first time value to compare. * @param t2 The second time value to compare. * @return Non-zero if t1 is less than or equal to t2. * @hideinitializer */ #define PJ_TIME_VAL_LTE(t1, t2) (!PJ_TIME_VAL_GT(t1, t2)) /** * Add \a t2 to \a t1 and store the result in \a t1. Effectively * * this macro will expand as: (\a t1 += \a t2). * @param t1 The time value to add. * @param t2 The time value to be added to \a t1. * @hideinitializer */ #define PJ_TIME_VAL_ADD(t1, t2) do { \ (t1).sec += (t2).sec; \ (t1).msec += (t2).msec; \ pj_time_val_normalize(&(t1)); \ } while (0) /** * Substract \a t2 from \a t1 and store the result in \a t1. Effectively * this macro will expand as (\a t1 -= \a t2). * * @param t1 The time value to subsctract. * @param t2 The time value to be substracted from \a t1. * @hideinitializer */ #define PJ_TIME_VAL_SUB(t1, t2) do { \ (t1).sec -= (t2).sec; \ (t1).msec -= (t2).msec; \ pj_time_val_normalize(&(t1)); \ } while (0) /** * This structure represent the parsed representation of time. * It is acquired by calling #pj_time_decode(). */ typedef struct pj_parsed_time { /** This represents day of week where value zero means Sunday */ int wday; /* This represents day of the year, 0-365, where zero means * 1st of January. */ /*int yday; */ /** This represents day of month: 1-31 */ int day; /** This represents month, with the value is 0 - 11 (zero is January) */ int mon; /** This represent the actual year (unlike in ANSI libc where * the value must be added by 1900). */ int year; /** This represents the second part, with the value is 0-59 */ int sec; /** This represents the minute part, with the value is: 0-59 */ int min; /** This represents the hour part, with the value is 0-23 */ int hour; /** This represents the milisecond part, with the value is 0-999 */ int msec; } pj_parsed_time; /** * @} // Time Management */ /* ************************************************************************* */ /* * Terminal. */ /** * Color code combination. */ enum { PJ_TERM_COLOR_R = 2, /**< Red */ PJ_TERM_COLOR_G = 4, /**< Green */ PJ_TERM_COLOR_B = 1, /**< Blue. */ PJ_TERM_COLOR_BRIGHT = 8 /**< Bright mask. */ }; PJ_END_DECL #endif /* __PJ_TYPES_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj/unicode.h ================================================ /* $Id: unicode.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_UNICODE_H__ #define __PJ_UNICODE_H__ #include /** * @defgroup PJ_UNICODE Unicode Support * @ingroup PJ_MISC * @{ */ PJ_BEGIN_DECL /** * @file unicode.h * @brief Provides Unicode conversion for Unicode OSes */ /** * Convert ANSI strings to Unicode strings. * * @param str The ANSI string to be converted. * @param len The length of the input string. * @param wbuf Buffer to hold the Unicode string output. * @param wbuf_count Buffer size, in number of elements (not bytes). * * @return The Unicode string, NULL terminated. */ PJ_DECL(wchar_t*) pj_ansi_to_unicode(const char *str, int len, wchar_t *wbuf, int wbuf_count); /** * Convert Unicode string to ANSI string. * * @param wstr The Unicode string to be converted. * @param len The length of the input string. * @param buf Buffer to hold the ANSI string output. * @param buf_size Size of the output buffer. * * @return The ANSI string, NULL terminated. */ PJ_DECL(char*) pj_unicode_to_ansi(const wchar_t *wstr, pj_ssize_t len, char *buf, int buf_size); #if defined(PJ_NATIVE_STRING_IS_UNICODE) && PJ_NATIVE_STRING_IS_UNICODE!=0 /** * This macro is used to declare temporary Unicode buffer for ANSI to * Unicode conversion, and should be put in declaration section of a block. * When PJ_NATIVE_STRING_IS_UNICODE macro is not defined, this * macro will expand to nothing. */ # define PJ_DECL_UNICODE_TEMP_BUF(buf,size) wchar_t buf[size]; /** * This macro will convert ANSI string to native, when the platform's * native string is Unicode (PJ_NATIVE_STRING_IS_UNICODE is non-zero). */ # define PJ_STRING_TO_NATIVE(s,buf,max) pj_ansi_to_unicode( \ s, strlen(s), \ buf, max) /** * This macro is used to declare temporary ANSI buffer for Unicode to * ANSI conversion, and should be put in declaration section of a block. * When PJ_NATIVE_STRING_IS_UNICODE macro is not defined, this * macro will expand to nothing. */ # define PJ_DECL_ANSI_TEMP_BUF(buf,size) char buf[size]; /** * This macro will convert Unicode string to ANSI, when the platform's * native string is Unicode (PJ_NATIVE_STRING_IS_UNICODE is non-zero). */ # define PJ_NATIVE_TO_STRING(cs,buf,max) pj_unicode_to_ansi( \ cs, wcslen(cs), \ buf, max) #else /** * This macro is used to declare temporary Unicode buffer for ANSI to * Unicode conversion, and should be put in declaration section of a block. * When PJ_NATIVE_STRING_IS_UNICODE macro is not defined, this * macro will expand to nothing. */ # define PJ_DECL_UNICODE_TEMP_BUF(var,size) /** * This macro will convert ANSI string to native, when the platform's * native string is Unicode (PJ_NATIVE_STRING_IS_UNICODE is non-zero). */ # define PJ_STRING_TO_NATIVE(s,buf,max) ((char*)s) /** * This macro is used to declare temporary ANSI buffer for Unicode to * ANSI conversion, and should be put in declaration section of a block. * When PJ_NATIVE_STRING_IS_UNICODE macro is not defined, this * macro will expand to nothing. */ # define PJ_DECL_ANSI_TEMP_BUF(buf,size) /** * This macro will convert Unicode string to ANSI, when the platform's * native string is Unicode (PJ_NATIVE_STRING_IS_UNICODE is non-zero). */ # define PJ_NATIVE_TO_STRING(cs,buf,max) ((char*)(const char*)cs) #endif PJ_END_DECL /* * @} */ #endif /* __PJ_UNICODE_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj++/file.hpp ================================================ /* $Id: file.hpp 2394 2008-12-23 17:27:53Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJPP_FILE_HPP__ #define __PJPP_FILE_HPP__ #include #include #include #include // // File API. // class Pj_File_API { public: // // Check file existance. // static bool file_exists(const char *filename) { return pj_file_exists(filename) != 0; } // // Get file size. // static pj_off_t file_size(const char *filename) { return pj_file_size(filename); } // // Delete file. // static pj_status_t file_delete(const char *filename) { return pj_file_delete(filename); } // // Move/rename file. // static pj_status_t file_move(const char *oldname, const char *newname) { return pj_file_move(oldname, newname); } // // Get stat. // static pj_status_t file_stat(const char *filename, pj_file_stat *buf) { return pj_file_getstat(filename, buf); } }; // // File. // class Pj_File : public Pj_Object { public: // // Offset type to be used in setpos. // enum Offset_Type { PJ_SEEK_SET = PJ_SEEK_SET, PJ_SEEK_CUR = PJ_SEEK_CUR, PJ_SEEK_END = PJ_SEEK_END, }; // // Default constructor. // Pj_File() : hnd_(0) { } // // Construct and open a file. // Pj_File(Pj_Pool *pool, const char *filename, unsigned access = PJ_O_RDONLY) : hnd_(NULL) { open(pool, filename, access); } // // Destructor closes the file. // ~Pj_File() { close(); } // // Open a file. // pj_status_t open(Pj_Pool *pool, const char *filename, unsigned access = PJ_O_RDONLY ) { close(); return pj_file_open(pool->pool_(), filename, access, &hnd_); } // // Close a file. // void close() { if (hnd_ != 0) { pj_file_close(hnd_); hnd_ = 0; } } // // Write data. // pj_ssize_t write(const void *buff, pj_size_t size) { pj_ssize_t bytes = size; if (pj_file_write(hnd_, buff, &bytes) != PJ_SUCCESS) return -1; return bytes; } // // Read data. // pj_ssize_t read(void *buf, pj_size_t size) { pj_ssize_t bytes = size; if (pj_file_read(hnd_, buf, &bytes) != PJ_SUCCESS) return -1; return bytes; } // // Set file position. // pj_status_t setpos(pj_off_t offset, Offset_Type whence) { return pj_file_setpos(hnd_, offset, (enum pj_file_seek_type)whence); } // // Get file position. // pj_off_t getpos() { pj_off_t pos; if (pj_file_getpos(hnd_, &pos) != PJ_SUCCESS) return -1; return pos; } private: pj_oshandle_t hnd_; }; #endif /* __PJPP_FILE_HPP__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj++/hash.hpp ================================================ /* $Id: hash.hpp 2394 2008-12-23 17:27:53Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJPP_HASH_HPP__ #define __PJPP_HASH_HPP__ #include #include #include // // Hash table. // class Pj_Hash_Table : public Pj_Object { public: // // Hash table iterator. // class iterator { public: iterator() { } explicit iterator(pj_hash_table_t *h, pj_hash_iterator_t *i) : ht_(h), it_(i) { } iterator(const iterator &rhs) : ht_(rhs.ht_), it_(rhs.it_) { } void operator++() { it_ = pj_hash_next(ht_, it_); } bool operator==(const iterator &rhs) { return ht_ == rhs.ht_ && it_ == rhs.it_; } iterator & operator=(const iterator &rhs) { ht_=rhs.ht_; it_=rhs.it_; return *this; } private: pj_hash_table_t *ht_; pj_hash_iterator_t it_val_; pj_hash_iterator_t *it_; friend class Pj_Hash_Table; }; // // Construct hash table. // Pj_Hash_Table(Pj_Pool *pool, unsigned size) { table_ = pj_hash_create(pool->pool_(), size); } // // Destroy hash table. // ~Pj_Hash_Table() { } // // Calculate hash value. // static pj_uint32_t calc( pj_uint32_t initial_hval, const void *key, unsigned keylen = PJ_HASH_KEY_STRING) { return pj_hash_calc(initial_hval, key, keylen); } // // Return pjlib compatible hash table object. // pj_hash_table_t *pj_hash_table_t_() { return table_; } // // Get the value associated with the specified key. // void *get(const void *key, unsigned keylen = PJ_HASH_KEY_STRING) { return pj_hash_get(table_, key, keylen); } // // Associate a value with a key. // Set the value to NULL to delete the key from the hash table. // void set(Pj_Pool *pool, const void *key, void *value, unsigned keylen = PJ_HASH_KEY_STRING) { pj_hash_set(pool->pool_(), table_, key, keylen, value); } // // Get number of items in the hash table. // unsigned count() { return pj_hash_count(table_); } // // Iterate hash table. // iterator begin() { iterator it(table_, NULL); it.it_ = pj_hash_first(table_, &it.it_val_); return it; } // // End of items. // iterator end() { return iterator(table_, NULL); } private: pj_hash_table_t *table_; }; #endif /* __PJPP_HASH_HPP__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj++/list.hpp ================================================ /* $Id: list.hpp 2394 2008-12-23 17:27:53Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJPP_LIST_HPP__ #define __PJPP_LIST_HPP__ #include #include // // Linked-list. // // Note: // List_Node must have public member next and prev. Normally // it will be declared like: // // struct my_node // { // PJ_DECL_LIST_MEMBER(struct my_node); // .. // }; // // template class Pj_List : public Pj_Object { public: // // List const_iterator. // class const_iterator { public: const_iterator() : node_(NULL) {} const_iterator(const List_Node *nd) : node_((List_Node*)nd) {} const List_Node * operator *() { return node_; } const List_Node * operator -> () { return node_; } const_iterator operator++() { return const_iterator((const List_Node *)node_->next); } bool operator==(const const_iterator &rhs) { return node_ == rhs.node_; } bool operator!=(const const_iterator &rhs) { return node_ != rhs.node_; } protected: List_Node *node_; }; // // List iterator. // class iterator : public const_iterator { public: iterator() {} iterator(List_Node *nd) : const_iterator(nd) {} List_Node * operator *() { return node_; } List_Node * operator -> () { return node_; } iterator operator++() { return iterator((List_Node*)node_->next); } bool operator==(const iterator &rhs) { return node_ == rhs.node_; } bool operator!=(const iterator &rhs) { return node_ != rhs.node_; } }; // // Default constructor. // Pj_List() { pj_list_init(&root_); if (0) compiletest(); } // // You can cast Pj_List to pj_list // operator pj_list&() { return (pj_list&)root_; } operator const pj_list&() { return (const pj_list&)root_; } // // You can cast Pj_List to pj_list* too // operator pj_list*() { return (pj_list*)&root_; } operator const pj_list*() { return (const pj_list*)&root_; } // // Check if list is empty. // bool empty() const { return pj_list_empty(&root_); } // // Get first element. // iterator begin() { return iterator(root_.next); } // // Get first element. // const_iterator begin() const { return const_iterator(root_.next); } // // Get end-of-element // const_iterator end() const { return const_iterator((List_Node*)&root_); } // // Get end-of-element // iterator end() { return iterator((List_Node*)&root_); } // // Insert node. // void insert_before (iterator &pos, List_Node *node) { pj_list_insert_before( *pos, node ); } // // Insert node. // void insert_after(iterator &pos, List_Node *node) { pj_list_insert_after(*pos, node); } // // Merge list. // void merge_first(List_Node *list2) { pj_list_merge_first(&root_, list2); } // // Merge list. // void merge_last(Pj_List *list) { pj_list_merge_last(&root_, &list->root_); } // // Insert list. // void insert_nodes_before(iterator &pos, Pj_List *list2) { pj_list_insert_nodes_before(*pos, &list2->root_); } // // Insert list. // void insert_nodes_after(iterator &pos, Pj_List *list2) { pj_list_insert_nodes_after(*pos, &list2->root_); } // // Erase an element. // void erase(iterator &it) { pj_list_erase(*it); } // // Get first element. // List_Node *front() { return root_.next; } // // Get first element. // const List_Node *front() const { return root_.next; } // // Remove first element. // void pop_front() { pj_list_erase(root_.next); } // // Get last element. // List_Node *back() { return root_.prev; } // // Get last element. // const List_Node *back() const { return root_.prev; } // // Remove last element. // void pop_back() { pj_list_erase(root_.prev); } // // Find a node. // iterator find(List_Node *node) { List_Node *n = pj_list_find_node(&root_, node); return n ? iterator(n) : end(); } // // Find a node. // const_iterator find(List_Node *node) const { List_Node *n = pj_list_find_node(&root_, node); return n ? const_iterator(n) : end(); } // // Insert a node in the back. // void push_back(List_Node *node) { pj_list_insert_after(root_.prev, node); } // // Insert a node in the front. // void push_front(List_Node *node) { pj_list_insert_before(root_.next, node); } // // Remove all elements. // void clear() { root_.next = &root_; root_.prev = &root_; } private: struct RootNode { PJ_DECL_LIST_MEMBER(List_Node); } root_; void compiletest() { // If you see error in this line, // it's because List_Node is not derived from Pj_List_Node. List_Node *n = (List_Node*)0; n = (List_Node *)n->next; n = (List_Node *)n->prev; } }; #endif /* __PJPP_LIST_HPP__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj++/lock.hpp ================================================ /* $Id: lock.hpp 2394 2008-12-23 17:27:53Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJPP_LOCK_HPP__ #define __PJPP_LOCK_HPP__ #include #include #include ////////////////////////////////////////////////////////////////////////////// // Lock object. // class Pj_Lock : public Pj_Object { public: // // Constructor. // explicit Pj_Lock(pj_lock_t *lock) : lock_(lock) { } // // Destructor. // ~Pj_Lock() { if (lock_) pj_lock_destroy(lock_); } // // Get pjlib compatible lock object. // pj_lock_t *pj_lock_t_() { return lock_; } // // acquire lock. // pj_status_t acquire() { return pj_lock_acquire(lock_); } // // release lock,. // pj_status_t release() { return pj_lock_release(lock_); } protected: pj_lock_t *lock_; }; ////////////////////////////////////////////////////////////////////////////// // Null lock object. // class Pj_Null_Lock : public Pj_Lock { public: // // Default constructor. // explicit Pj_Null_Lock(Pj_Pool *pool, const char *name = NULL) : Pj_Lock(NULL) { pj_lock_create_null_mutex(pool->pool_(), name, &lock_); } }; ////////////////////////////////////////////////////////////////////////////// // Simple mutex lock object. // class Pj_Simple_Mutex_Lock : public Pj_Lock { public: // // Default constructor. // explicit Pj_Simple_Mutex_Lock(Pj_Pool *pool, const char *name = NULL) : Pj_Lock(NULL) { pj_lock_create_simple_mutex(pool->pool_(), name, &lock_); } }; ////////////////////////////////////////////////////////////////////////////// // Recursive mutex lock object. // class Pj_Recursive_Mutex_Lock : public Pj_Lock { public: // // Default constructor. // explicit Pj_Recursive_Mutex_Lock(Pj_Pool *pool, const char *name = NULL) : Pj_Lock(NULL) { pj_lock_create_recursive_mutex(pool->pool_(), name, &lock_); } }; ////////////////////////////////////////////////////////////////////////////// // Semaphore lock object. // class Pj_Semaphore_Lock : public Pj_Lock { public: // // Default constructor. // explicit Pj_Semaphore_Lock(Pj_Pool *pool, unsigned max=PJ_MAXINT32, unsigned initial=0, const char *name=NULL) : Pj_Lock(NULL) { pj_lock_create_semaphore(pool->pool_(), name, initial, max, &lock_); } }; #endif /* __PJPP_LOCK_HPP__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj++/os.hpp ================================================ /* $Id: os.hpp 2394 2008-12-23 17:27:53Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJPP_OS_HPP__ #define __PJPP_OS_HPP__ #include #include #include #include class Pj_Thread; // // Thread API. // class Pj_Thread_API { public: // // Create a thread. // static pj_status_t create( Pj_Pool *pool, pj_thread_t **thread, pj_thread_proc *proc, void *arg, unsigned flags = 0, const char *name = NULL, pj_size_t stack_size = 0 ) { return pj_thread_create(pool->pool_(), name, proc, arg, stack_size, flags, thread); } // // Register a thread. // static pj_status_t register_this_thread( pj_thread_desc desc, pj_thread_t **thread, const char *name = NULL ) { return pj_thread_register( name, desc, thread ); } // // Get current thread. // Will return pj_thread_t (sorry folks, not Pj_Thread). // static pj_thread_t *this_thread() { return pj_thread_this(); } // // Get thread name. // static const char *get_name(pj_thread_t *thread) { return pj_thread_get_name(thread); } // // Resume thread. // static pj_status_t resume(pj_thread_t *thread) { return pj_thread_resume(thread); } // // Sleep. // static pj_status_t sleep(unsigned msec) { return pj_thread_sleep(msec); } // // Join the specified thread. // static pj_status_t join(pj_thread_t *thread) { return pj_thread_join(thread); } // // Destroy thread // static pj_status_t destroy(pj_thread_t *thread) { return pj_thread_destroy(thread); } }; // // Thread object. // // How to use: // Derive a class from this class, then override main(). // class Pj_Thread : public Pj_Object { public: enum Flags { FLAG_SUSPENDED = PJ_THREAD_SUSPENDED }; // // Default constructor. // Pj_Thread() : thread_(NULL) { } // // Destroy thread. // ~Pj_Thread() { destroy(); } // // This is the main thread function. // virtual int main() = 0; // // Start a thread. // pj_status_t create( Pj_Pool *pool, unsigned flags = 0, const char *thread_name = NULL, pj_size_t stack_size = PJ_THREAD_DEFAULT_STACK_SIZE) { destroy(); return Pj_Thread_API::create( pool, &thread_, &thread_proc, this, flags, thread_name, stack_size); } // // Get pjlib compatible thread object. // pj_thread_t *pj_thread_t_() { return thread_; } // // Get thread name. // const char *get_name() { return Pj_Thread_API::get_name(thread_); } // // Resume a suspended thread. // pj_status_t resume() { return Pj_Thread_API::resume(thread_); } // // Join this thread. // pj_status_t join() { return Pj_Thread_API::join(thread_); } // // Destroy thread. // pj_status_t destroy() { if (thread_) { Pj_Thread_API::destroy(thread_); thread_ = NULL; } } protected: pj_thread_t *thread_; static int PJ_THREAD_FUNC thread_proc(void *obj) { Pj_Thread *thread_class = (Pj_Thread*)obj; return thread_class->main(); } }; // // External Thread // (threads that were started by external means, i.e. not // with Pj_Thread::create). // // This class will normally be defined as local variable in // external thread's stack, normally inside thread's main proc. // But be aware that the handle will be destroyed on destructor! // class Pj_External_Thread : public Pj_Thread { public: Pj_External_Thread() { } // // Register external thread so that pjlib functions can work // in that thread. // pj_status_t register_this_thread( const char *name=NULL ) { return Pj_Thread_API::register_this_thread(desc_, &thread_,name); } private: pj_thread_desc desc_; }; // // Thread specific data/thread local storage/TLS. // class Pj_Thread_Local_API { public: // // Allocate thread local storage (TLS) index. // static pj_status_t alloc(long *index) { return pj_thread_local_alloc(index); } // // Free TLS index. // static void free(long index) { pj_thread_local_free(index); } // // Set thread specific data. // static pj_status_t set(long index, void *value) { return pj_thread_local_set(index, value); } // // Get thread specific data. // static void *get(long index) { return pj_thread_local_get(index); } }; // // Atomic variable // // How to use: // Pj_Atomic_Var var(pool, 0); // var.set(..); // class Pj_Atomic_Var : public Pj_Object { public: // // Default constructor, initialize variable with NULL. // Pj_Atomic_Var() : var_(NULL) { } // // Construct atomic variable. // Pj_Atomic_Var(Pj_Pool *pool, pj_atomic_value_t value) : var_(NULL) { create(pool, value); } // // Destructor. // ~Pj_Atomic_Var() { destroy(); } // // Create atomic variable. // pj_status_t create( Pj_Pool *pool, pj_atomic_value_t value) { destroy(); return pj_atomic_create(pool->pool_(), value, &var_); } // // Destroy. // void destroy() { if (var_) { pj_atomic_destroy(var_); var_ = NULL; } } // // Get pjlib compatible atomic variable. // pj_atomic_t *pj_atomic_t_() { return var_; } // // Set the value. // void set(pj_atomic_value_t val) { pj_atomic_set(var_, val); } // // Get the value. // pj_atomic_value_t get() { return pj_atomic_get(var_); } // // Increment. // void inc() { pj_atomic_inc(var_); } // // Increment and get the result. // pj_atomic_value_t inc_and_get() { return pj_atomic_inc_and_get(var_); } // // Decrement. // void dec() { pj_atomic_dec(var_); } // // Decrement and get the result. // pj_atomic_value_t dec_and_get() { return pj_atomic_dec_and_get(var_); } // // Add the variable. // void add(pj_atomic_value_t value) { pj_atomic_add(var_, value); } // // Add the variable and get the value. // pj_atomic_value_t add_and_get(pj_atomic_value_t value) { return pj_atomic_add_and_get(var_, value ); } private: pj_atomic_t *var_; }; // // Mutex // class Pj_Mutex : public Pj_Object { public: // // Mutex type. // enum Type { DEFAULT = PJ_MUTEX_DEFAULT, SIMPLE = PJ_MUTEX_SIMPLE, RECURSE = PJ_MUTEX_RECURSE, }; // // Default constructor will create default mutex. // explicit Pj_Mutex(Pj_Pool *pool, Type type = DEFAULT, const char *name = NULL) : mutex_(NULL) { create(pool, type, name); } // // Destructor. // ~Pj_Mutex() { destroy(); } // // Create mutex. // pj_status_t create( Pj_Pool *pool, Type type, const char *name = NULL) { destroy(); return pj_mutex_create( pool->pool_(), name, type, &mutex_ ); } // // Create simple mutex. // pj_status_t create_simple( Pj_Pool *pool,const char *name = NULL) { return create(pool, SIMPLE, name); } // // Create recursive mutex. // pj_status_t create_recursive( Pj_Pool *pool, const char *name = NULL ) { return create(pool, RECURSE, name); } // // Get pjlib compatible mutex object. // pj_mutex_t *pj_mutex_t_() { return mutex_; } // // Destroy mutex. // void destroy() { if (mutex_) { pj_mutex_destroy(mutex_); mutex_ = NULL; } } // // Lock mutex. // pj_status_t acquire() { return pj_mutex_lock(mutex_); } // // Unlock mutex. // pj_status_t release() { return pj_mutex_unlock(mutex_); } // // Try locking the mutex. // pj_status_t tryacquire() { return pj_mutex_trylock(mutex_); } private: pj_mutex_t *mutex_; }; // // Semaphore // class Pj_Semaphore : public Pj_Object { public: // // Construct semaphore // Pj_Semaphore(Pj_Pool *pool, unsigned max, unsigned initial = 0, const char *name = NULL) : sem_(NULL) { create(pool, max, initial, name); } // // Destructor. // ~Pj_Semaphore() { destroy(); } // // Create semaphore // pj_status_t create( Pj_Pool *pool, unsigned max, unsigned initial = 0, const char *name = NULL ) { destroy(); return pj_sem_create( pool->pool_(), name, initial, max, &sem_); } // // Destroy semaphore. // void destroy() { if (sem_) { pj_sem_destroy(sem_); sem_ = NULL; } } // // Get pjlib compatible semaphore object. // pj_sem_t *pj_sem_t_() { return (pj_sem_t*)this; } // // Wait semaphore. // pj_status_t wait() { return pj_sem_wait(this->pj_sem_t_()); } // // Wait semaphore. // pj_status_t acquire() { return wait(); } // // Try wait semaphore. // pj_status_t trywait() { return pj_sem_trywait(this->pj_sem_t_()); } // // Try wait semaphore. // pj_status_t tryacquire() { return trywait(); } // // Post semaphore. // pj_status_t post() { return pj_sem_post(this->pj_sem_t_()); } // // Post semaphore. // pj_status_t release() { return post(); } private: pj_sem_t *sem_; }; // // Event object. // class Pj_Event { public: // // Construct event object. // Pj_Event( Pj_Pool *pool, bool manual_reset = false, bool initial = false, const char *name = NULL ) : event_(NULL) { create(pool, manual_reset, initial, name); } // // Destructor. // ~Pj_Event() { destroy(); } // // Create event object. // pj_status_t create( Pj_Pool *pool, bool manual_reset = false, bool initial = false, const char *name = NULL) { destroy(); return pj_event_create(pool->pool_(), name, manual_reset, initial, &event_); } // // Get pjlib compatible event object. // pj_event_t *pj_event_t_() { return event_; } // // Destroy event object. // void destroy() { if (event_) { pj_event_destroy(event_); event_ = NULL; } } // // Wait. // pj_status_t wait() { return pj_event_wait(event_); } // // Try wait. // pj_status_t trywait() { return pj_event_trywait(event_); } // // Set event state to signalled. // pj_status_t set() { return pj_event_set(this->pj_event_t_()); } // // Release one waiting thread. // pj_status_t pulse() { return pj_event_pulse(this->pj_event_t_()); } // // Set a non-signalled. // pj_status_t reset() { return pj_event_reset(this->pj_event_t_()); } private: pj_event_t *event_; }; // // Timestamp // class Pj_Timestamp { public: pj_status_t get_timestamp() { return pj_get_timestamp(&ts_); } Pj_Timestamp& operator += (const Pj_Timestamp &rhs) { pj_add_timestamp(&ts_, &rhs.ts_); return *this; } Pj_Timestamp& operator -= (const Pj_Timestamp &rhs) { pj_sub_timestamp(&ts_, &rhs.ts_); return *this; } Pj_Time_Val to_time() const { Pj_Timestamp zero; pj_memset(&zero, 0, sizeof(zero)); return Pj_Time_Val(pj_elapsed_time(&zero.ts_, &ts_)); } pj_uint32_t to_msec() const { Pj_Timestamp zero; pj_memset(&zero, 0, sizeof(zero)); return pj_elapsed_msec(&zero.ts_, &ts_); } pj_uint32_t to_usec() const { Pj_Timestamp zero; pj_memset(&zero, 0, sizeof(zero)); return pj_elapsed_usec(&zero.ts_, &ts_); } pj_uint32_t to_nanosec() const { Pj_Timestamp zero; pj_memset(&zero, 0, sizeof(zero)); return pj_elapsed_nanosec(&zero.ts_, &ts_); } pj_uint32_t to_cycle() const { Pj_Timestamp zero; pj_memset(&zero, 0, sizeof(zero)); return pj_elapsed_cycle(&zero.ts_, &ts_); } private: pj_timestamp ts_; }; // // OS abstraction. // class Pj_OS_API { public: // // Get current time. // static pj_status_t gettimeofday( Pj_Time_Val *tv ) { return pj_gettimeofday(tv); } // // Parse to time of day. // static pj_status_t time_decode( const Pj_Time_Val *tv, pj_parsed_time *pt ) { return pj_time_decode(tv, pt); } // // Parse from time of day. // static pj_status_t time_encode( const pj_parsed_time *pt, Pj_Time_Val *tv) { return pj_time_encode(pt, tv); } // // Convert to GMT. // static pj_status_t time_local_to_gmt( Pj_Time_Val *tv ) { return pj_time_local_to_gmt( tv ); } // // Convert time to local. // static pj_status_t time_gmt_to_local( Pj_Time_Val *tv) { return pj_time_gmt_to_local( tv ); } }; // // Timeval inlines. // inline pj_status_t Pj_Time_Val::gettimeofday() { return Pj_OS_API::gettimeofday(this); } inline pj_parsed_time Pj_Time_Val::decode() { pj_parsed_time pt; Pj_OS_API::time_decode(this, &pt); return pt; } inline pj_status_t Pj_Time_Val::encode(const pj_parsed_time *pt) { return Pj_OS_API::time_encode(pt, this); } inline pj_status_t Pj_Time_Val::to_gmt() { return Pj_OS_API::time_local_to_gmt(this); } inline pj_status_t Pj_Time_Val::to_local() { return Pj_OS_API::time_gmt_to_local(this); } #endif /* __PJPP_OS_HPP__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj++/pool.hpp ================================================ /* $Id: pool.hpp 2394 2008-12-23 17:27:53Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJPP_POOL_HPP__ #define __PJPP_POOL_HPP__ #include class Pj_Pool; class Pj_Caching_Pool; // // Base class for all Pjlib objects // class Pj_Object { public: void *operator new(unsigned int class_size, Pj_Pool *pool); void *operator new(unsigned int class_size, Pj_Pool &pool); void operator delete(void*) { } void operator delete(void*, Pj_Pool*) { } void operator delete(void*, Pj_Pool&) { } // // Inline implementations at the end of this file. // private: // Can not use normal new operator; must use pool. // e.g.: // obj = new(pool) Pj_The_Object(pool, ...); // void *operator new(unsigned int) {} }; // // Pool. // class Pj_Pool : public Pj_Object { public: // // Default constructor, initializes internal pool to NULL. // Application must call attach() some time later. // Pj_Pool() : p_(NULL) { } // // Create pool. // Pj_Pool(Pj_Caching_Pool &caching_pool, pj_size_t initial_size, pj_size_t increment_size, const char *name = NULL, pj_pool_callback *callback = NULL); // // Construct from existing pool. // explicit Pj_Pool(pj_pool_t *pool) : p_(pool) { } // // Attach existing pool. // void attach(pj_pool_t *pool) { p_ = pool; } // // Destructor. // // Release pool back to factory. Remember: if you delete pool, then // make sure that all objects that have been allocated from this pool // have been properly destroyed. // // This is where C++ is trickier than plain C!! // ~Pj_Pool() { if (p_) pj_pool_release(p_); } // // Get name. // const char *getobjname() const { return pj_pool_getobjname(p_); } // // You can cast Pj_Pool to pj_pool_t* // operator pj_pool_t*() { return p_; } // // Get pjlib compatible pool object. // pj_pool_t *pool_() { return p_; } // // Get pjlib compatible pool object. // const pj_pool_t *pool_() const { return p_; } // // Get pjlib compatible pool object. // pj_pool_t *pj_pool_t_() { return p_; } // // Reset pool. // void reset() { pj_pool_reset(p_); } // // Get current capacity. // pj_size_t get_capacity() { pj_pool_get_capacity(p_); } // // Get current total bytes allocated from the pool. // pj_size_t get_used_size() { pj_pool_get_used_size(p_); } // // Allocate. // void *alloc(pj_size_t size) { return pj_pool_alloc(p_, size); } // // Allocate elements and zero fill the memory. // void *calloc(pj_size_t count, pj_size_t elem) { return pj_pool_calloc(p_, count, elem); } // // Allocate and zero fill memory. // void *zalloc(pj_size_t size) { return pj_pool_zalloc(p_, size); } private: pj_pool_t *p_; }; // // Caching pool. // class Pj_Caching_Pool { public: // // Construct caching pool. // Pj_Caching_Pool( pj_size_t cache_capacity = 0, const pj_pool_factory_policy *pol=&pj_pool_factory_default_policy) { pj_caching_pool_init(&cp_, pol, cache_capacity); } // // Destroy caching pool. // ~Pj_Caching_Pool() { pj_caching_pool_destroy(&cp_); } // // Create pool. // pj_pool_t *create_pool( pj_size_t initial_size, pj_size_t increment_size, const char *name = NULL, pj_pool_callback *callback = NULL) { return (pj_pool_t*)(*cp_.factory.create_pool)(&cp_.factory, name, initial_size, increment_size, callback); } private: pj_caching_pool cp_; }; // // Inlines for Pj_Object // inline void *Pj_Object::operator new(unsigned int class_size, Pj_Pool *pool) { return pool->alloc(class_size); } inline void *Pj_Object::operator new(unsigned int class_size, Pj_Pool &pool) { return pool.alloc(class_size); } // // Inlines for Pj_Pool // inline Pj_Pool::Pj_Pool( Pj_Caching_Pool &caching_pool, pj_size_t initial_size, pj_size_t increment_size, const char *name, pj_pool_callback *callback) { p_ = caching_pool.create_pool(initial_size, increment_size, name, callback); } #endif /* __PJPP_POOL_HPP__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj++/proactor.hpp ================================================ /* $Id: proactor.hpp 2394 2008-12-23 17:27:53Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJPP_PROACTOR_HPP__ #define __PJPP_PROACTOR_HPP__ #include #include #include #include #include class Pj_Proactor; class Pj_Event_Handler; ////////////////////////////////////////////////////////////////////////////// // Asynchronous operation key. // // Applications may inheric this class to put their application // specific data. // class Pj_Async_Op : public pj_ioqueue_op_key_t { public: // // Construct with null handler. // App must call set_handler() before use. // Pj_Async_Op() : handler_(NULL) { pj_ioqueue_op_key_init(this, sizeof(*this)); } // // Constructor. // explicit Pj_Async_Op(Pj_Event_Handler *handler) : handler_(handler) { pj_ioqueue_op_key_init(this, sizeof(*this)); } // // Set handler. // void set_handler(Pj_Event_Handler *handler) { handler_ = handler; } // // Check whether operation is still pending for this key. // bool is_pending(); // // Cancel the operation. // bool cancel(pj_ssize_t bytes_status=-PJ_ECANCELLED); protected: Pj_Event_Handler *handler_; }; ////////////////////////////////////////////////////////////////////////////// // Event handler. // // Applications should inherit this class to receive various event // notifications. // // Applications should implement get_socket_handle(). // class Pj_Event_Handler : public Pj_Object { friend class Pj_Proactor; public: // // Default constructor. // Pj_Event_Handler() : key_(NULL) { pj_memset(&timer_, 0, sizeof(timer_)); timer_.user_data = this; timer_.cb = &timer_callback; } // // Destroy. // virtual ~Pj_Event_Handler() { unregister(); } // // Unregister this handler from the ioqueue. // void unregister() { if (key_) { pj_ioqueue_unregister(key_); key_ = NULL; } } // // Get socket handle associated with this. // virtual pj_sock_t get_socket_handle() { return PJ_INVALID_SOCKET; } // // Start async receive. // pj_status_t recv( Pj_Async_Op *op_key, void *buf, pj_ssize_t *len, unsigned flags) { return pj_ioqueue_recv( key_, op_key, buf, len, flags); } // // Start async recvfrom() // pj_status_t recvfrom( Pj_Async_Op *op_key, void *buf, pj_ssize_t *len, unsigned flags, Pj_Inet_Addr *addr) { addr->addrlen_ = sizeof(Pj_Inet_Addr); return pj_ioqueue_recvfrom( key_, op_key, buf, len, flags, addr, &addr->addrlen_ ); } // // Start async send() // pj_status_t send( Pj_Async_Op *op_key, const void *data, pj_ssize_t *len, unsigned flags) { return pj_ioqueue_send( key_, op_key, data, len, flags); } // // Start async sendto() // pj_status_t sendto( Pj_Async_Op *op_key, const void *data, pj_ssize_t *len, unsigned flags, const Pj_Inet_Addr &addr) { return pj_ioqueue_sendto(key_, op_key, data, len, flags, &addr, sizeof(addr)); } #if PJ_HAS_TCP // // Start async connect() // pj_status_t connect(const Pj_Inet_Addr &addr) { return pj_ioqueue_connect(key_, &addr, sizeof(addr)); } // // Start async accept(). // pj_status_t accept( Pj_Async_Op *op_key, Pj_Socket *sock, Pj_Inet_Addr *local = NULL, Pj_Inet_Addr *remote = NULL) { int *addrlen = local ? &local->addrlen_ : NULL; return pj_ioqueue_accept( key_, op_key, &sock->sock_, local, remote, addrlen ); } #endif protected: ////////////////// // Overridables ////////////////// // // Timeout callback. // virtual void on_timeout(int) { } // // On read complete callback. // virtual void on_read_complete( Pj_Async_Op*, pj_ssize_t) { } // // On write complete callback. // virtual void on_write_complete( Pj_Async_Op *, pj_ssize_t) { } #if PJ_HAS_TCP // // On connect complete callback. // virtual void on_connect_complete(pj_status_t) { } // // On new connection callback. // virtual void on_accept_complete( Pj_Async_Op*, pj_sock_t, pj_status_t) { } #endif private: pj_ioqueue_key_t *key_; pj_timer_entry timer_; friend class Pj_Proactor; friend class Pj_Async_Op; // // Static timer callback. // static void timer_callback( pj_timer_heap_t*, struct pj_timer_entry *entry) { Pj_Event_Handler *handler = (Pj_Event_Handler*) entry->user_data; handler->on_timeout(entry->id); } }; inline bool Pj_Async_Op::is_pending() { return pj_ioqueue_is_pending(handler_->key_, this) != 0; } inline bool Pj_Async_Op::cancel(pj_ssize_t bytes_status) { return pj_ioqueue_post_completion(handler_->key_, this, bytes_status) == PJ_SUCCESS; } ////////////////////////////////////////////////////////////////////////////// // Proactor // class Pj_Proactor : public Pj_Object { public: // // Default constructor, initializes to NULL. // Pj_Proactor() : ioq_(NULL), th_(NULL) { cb_.on_read_complete = &read_complete_cb; cb_.on_write_complete = &write_complete_cb; cb_.on_accept_complete = &accept_complete_cb; cb_.on_connect_complete = &connect_complete_cb; } // // Construct proactor. // Pj_Proactor( Pj_Pool *pool, pj_size_t max_fd, pj_size_t max_timer_entries ) : ioq_(NULL), th_(NULL) { cb_.on_read_complete = &read_complete_cb; cb_.on_write_complete = &write_complete_cb; cb_.on_accept_complete = &accept_complete_cb; cb_.on_connect_complete = &connect_complete_cb; create(pool, max_fd, max_timer_entries); } // // Destructor. // ~Pj_Proactor() { destroy(); } // // Create proactor. // pj_status_t create( Pj_Pool *pool, pj_size_t max_fd, pj_size_t timer_entry_count) { pj_status_t status; destroy(); status = pj_ioqueue_create(pool->pool_(), max_fd, &ioq_); if (status != PJ_SUCCESS) return status; status = pj_timer_heap_create(pool->pool_(), timer_entry_count, &th_); if (status != PJ_SUCCESS) { pj_ioqueue_destroy(ioq_); ioq_ = NULL; return NULL; } return status; } // // Destroy proactor. // void destroy() { if (ioq_) { pj_ioqueue_destroy(ioq_); ioq_ = NULL; } if (th_) { pj_timer_heap_destroy(th_); th_ = NULL; } } // // Register handler. // This will call handler->get_socket_handle() // pj_status_t register_socket_handler(Pj_Pool *pool, Pj_Event_Handler *handler) { return pj_ioqueue_register_sock( pool->pool_(), ioq_, handler->get_socket_handle(), handler, &cb_, &handler->key_ ); } // // Unregister handler. // static void unregister_handler(Pj_Event_Handler *handler) { if (handler->key_) { pj_ioqueue_unregister( handler->key_ ); handler->key_ = NULL; } } // // Scheduler timer. // bool schedule_timer( Pj_Event_Handler *handler, const Pj_Time_Val &delay, int id=-1) { return schedule_timer(th_, handler, delay, id); } // // Cancel timer. // bool cancel_timer(Pj_Event_Handler *handler) { return pj_timer_heap_cancel(th_, &handler->timer_) == 1; } // // Handle events. // int handle_events(Pj_Time_Val *max_timeout) { Pj_Time_Val timeout(0, 0); int timer_count; timer_count = pj_timer_heap_poll( th_, &timeout ); if (timeout.get_sec() < 0) timeout.sec = PJ_MAXINT32; /* If caller specifies maximum time to wait, then compare the value * with the timeout to wait from timer, and use the minimum value. */ if (max_timeout && timeout >= *max_timeout) { timeout = *max_timeout; } /* Poll events in ioqueue. */ int ioqueue_count; ioqueue_count = pj_ioqueue_poll(ioq_, &timeout); if (ioqueue_count < 0) return ioqueue_count; return ioqueue_count + timer_count; } // // Get the internal ioqueue object. // pj_ioqueue_t *get_io_queue() { return ioq_; } // // Get the internal timer heap object. // pj_timer_heap_t *get_timer_heap() { return th_; } private: pj_ioqueue_t *ioq_; pj_timer_heap_t *th_; pj_ioqueue_callback cb_; static bool schedule_timer( pj_timer_heap_t *timer, Pj_Event_Handler *handler, const Pj_Time_Val &delay, int id=-1) { handler->timer_.id = id; return pj_timer_heap_schedule(timer, &handler->timer_, &delay) == 0; } // // Static read completion callback. // static void read_complete_cb( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { Pj_Event_Handler *handler = (Pj_Event_Handler*) pj_ioqueue_get_user_data(key); handler->on_read_complete((Pj_Async_Op*)op_key, bytes_read); } // // Static write completion callback. // static void write_complete_cb(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_sent) { Pj_Event_Handler *handler = (Pj_Event_Handler*) pj_ioqueue_get_user_data(key); handler->on_write_complete((Pj_Async_Op*)op_key, bytes_sent); } // // Static accept completion callback. // static void accept_complete_cb(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t new_sock, pj_status_t status) { Pj_Event_Handler *handler = (Pj_Event_Handler*) pj_ioqueue_get_user_data(key); handler->on_accept_complete((Pj_Async_Op*)op_key, new_sock, status); } // // Static connect completion callback. // static void connect_complete_cb(pj_ioqueue_key_t *key, pj_status_t status) { Pj_Event_Handler *handler = (Pj_Event_Handler*) pj_ioqueue_get_user_data(key); handler->on_connect_complete(status); } }; #endif /* __PJPP_PROACTOR_HPP__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj++/scanner.hpp ================================================ /* $Id: scanner.hpp 2394 2008-12-23 17:27:53Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJPP_SCANNER_HPP__ #define __PJPP_SCANNER_HPP__ #include #include class Pj_Cis; class Pj_Cis_Buffer; class Pj_Scanner; class Pj_Cis_Buffer { friend class Pj_Cis; public: Pj_Cis_Buffer() { pj_cis_buf_init(&buf_); } private: pj_cis_buf_t buf_; }; class Pj_Cis { friend class Pj_Scanner; public: Pj_Cis(Pj_Cis_Buffer *buf) { pj_cis_init(&buf->buf_, &cis_); } Pj_Cis(const Pj_Cis &rhs) { pj_cis_dup(&cis_, (pj_cis_t*)&rhs.cis_); } void add_range(int start, int end) { pj_cis_add_range(&cis_, start, end); } void add_alpha() { pj_cis_add_alpha(&cis_); } void add_num() { pj_cis_add_num(&cis_); } void add_str(const char *str) { pj_cis_add_str(&cis_, str); } void add_cis(const Pj_Cis &rhs) { pj_cis_add_cis(&cis_, &rhs.cis_); } void del_range(int start, int end) { pj_cis_del_range(&cis_, start, end); } void del_str(const char *str) { pj_cis_del_str(&cis_, str); } void invert() { pj_cis_invert(&cis_); } bool match(int c) const { return pj_cis_match(&cis_, c) != 0; } private: pj_cis_t cis_; }; class Pj_Scanner { public: Pj_Scanner() {} enum { SYNTAX_ERROR = 101 }; static void syntax_error_handler_throw_pj(pj_scanner *); typedef pj_scan_state State; void init(char *buf, int len, unsigned options=PJ_SCAN_AUTOSKIP_WS, pj_syn_err_func_ptr callback = &syntax_error_handler_throw_pj) { pj_scan_init(&scanner_, buf, len, options, callback); } void fini() { pj_scan_fini(&scanner_); } int eof() const { return pj_scan_is_eof(&scanner_); } int peek_char() const { return *scanner_.curptr; } int peek(const Pj_Cis *cis, Pj_String *out) { return pj_scan_peek(&scanner_, &cis->cis_, out); } int peek_n(pj_size_t len, Pj_String *out) { return pj_scan_peek_n(&scanner_, len, out); } int peek_until(const Pj_Cis *cis, Pj_String *out) { return pj_scan_peek_until(&scanner_, &cis->cis_, out); } void get(const Pj_Cis *cis, Pj_String *out) { pj_scan_get(&scanner_, &cis->cis_, out); } void get_n(unsigned N, Pj_String *out) { pj_scan_get_n(&scanner_, N, out); } int get_char() { return pj_scan_get_char(&scanner_); } void get_quote(int begin_quote, int end_quote, Pj_String *out) { pj_scan_get_quote(&scanner_, begin_quote, end_quote, out); } void get_newline() { pj_scan_get_newline(&scanner_); } void get_until(const Pj_Cis *cis, Pj_String *out) { pj_scan_get_until(&scanner_, &cis->cis_, out); } void get_until_ch(int until_ch, Pj_String *out) { pj_scan_get_until_ch(&scanner_, until_ch, out); } void get_until_chr(const char *spec, Pj_String *out) { pj_scan_get_until_chr(&scanner_, spec, out); } void advance_n(unsigned N, bool skip_ws=true) { pj_scan_advance_n(&scanner_, N, skip_ws); } int strcmp(const char *s, int len) { return pj_scan_strcmp(&scanner_, s, len); } int stricmp(const char *s, int len) { return pj_scan_stricmp(&scanner_, s, len); } void skip_ws() { pj_scan_skip_whitespace(&scanner_); } void save_state(State *state) const { pj_scan_save_state(&scanner_, state); } void restore_state(State *state) { pj_scan_restore_state(&scanner_, state); } int get_pos_line() const { return scanner_.line; } int get_pos_col() const { return pj_scan_get_col(&scanner_); } private: pj_scanner scanner_; }; #endif /* __PJPP_SCANNER_HPP__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj++/sock.hpp ================================================ /* $Id: sock.hpp 2394 2008-12-23 17:27:53Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJPP_SOCK_HPP__ #define __PJPP_SOCK_HPP__ #include #include class Pj_Event_Handler; // // Base class for address. // class Pj_Addr { }; // // Internet address. // class Pj_Inet_Addr : public pj_sockaddr_in, public Pj_Addr { public: // // Get port number. // pj_uint16_t get_port_number() const { return pj_sockaddr_in_get_port(this); } // // Set port number. // void set_port_number(pj_uint16_t port) { sin_family = PJ_AF_INET; pj_sockaddr_in_set_port(this, port); } // // Get IP address. // pj_uint32_t get_ip_address() const { return pj_sockaddr_in_get_addr(this).s_addr; } // // Get address string. // const char *get_address() const { return pj_inet_ntoa(sin_addr); } // // Set IP address. // void set_ip_address(pj_uint32_t addr) { sin_family = PJ_AF_INET; pj_sockaddr_in_set_addr(this, addr); } // // Set address. // pj_status_t set_address(const pj_str_t *addr) { return pj_sockaddr_in_set_str_addr(this, addr); } // // Set address. // pj_status_t set_address(const char *addr) { pj_str_t s; return pj_sockaddr_in_set_str_addr(this, pj_cstr(&s, addr)); } // // Compare for equality. // bool operator==(const Pj_Inet_Addr &rhs) const { return sin_family == rhs.sin_family && sin_addr.s_addr == rhs.sin_addr.s_addr && sin_port == rhs.sin_port; } private: // // Dummy length used in pj_ioqueue_recvfrom() etc // friend class Pj_Event_Handler; friend class Pj_Socket; friend class Pj_Sock_Stream; friend class Pj_Sock_Dgram; int addrlen_; }; // // Socket base class. // // Note: // socket will not automatically be closed on destructor. // class Pj_Socket { public: // // Default constructor. // Pj_Socket() : sock_(PJ_INVALID_SOCKET) { } // // Initialize from a socket handle. // explicit Pj_Socket(pj_sock_t sock) : sock_(sock) { } // // Copy constructor. // Pj_Socket(const Pj_Socket &rhs) : sock_(rhs.sock_) { } // // Destructor will not close the socket. // You must call close() explicitly. // ~Pj_Socket() { } // // Set socket handle. // void set_handle(pj_sock_t sock) { sock_ = sock; } // // Get socket handle. // pj_sock_t get_handle() const { return sock_; } // // Get socket handle. // pj_sock_t& get_handle() { return sock_; } // // See if the socket is valid. // bool is_valid() const { return sock_ != PJ_INVALID_SOCKET; } // // Create the socket. // pj_status_t create(int af, int type, int proto) { return pj_sock_socket(af, type, proto, &sock_); } // // Bind socket. // pj_status_t bind(const Pj_Inet_Addr &addr) { return pj_sock_bind(sock_, &addr, sizeof(Pj_Inet_Addr)); } // // Close socket. // pj_status_t close() { pj_sock_close(sock_); } // // Get peer socket name. // pj_status_t getpeername(Pj_Inet_Addr *addr) { return pj_sock_getpeername(sock_, addr, &addr->addrlen_); } // // getsockname // pj_status_t getsockname(Pj_Inet_Addr *addr) { return pj_sock_getsockname(sock_, addr, &addr->addrlen_); } // // getsockopt. // pj_status_t getsockopt(pj_uint16_t level, pj_uint16_t optname, void *optval, int *optlen) { return pj_sock_getsockopt(sock_, level, optname, optval, optlen); } // // setsockopt // pj_status_t setsockopt(pj_uint16_t level, pj_uint16_t optname, const void *optval, int optlen) { return pj_sock_setsockopt(sock_, level, optname, optval, optlen); } // // receive data. // pj_ssize_t recv(void *buf, pj_size_t len, int flag = 0) { pj_ssize_t bytes = len; if (pj_sock_recv(sock_, buf, &bytes, flag) != PJ_SUCCESS) return -1; return bytes; } // // send data. // pj_ssize_t send(const void *buf, pj_ssize_t len, int flag = 0) { pj_ssize_t bytes = len; if (pj_sock_send(sock_, buf, &bytes, flag) != PJ_SUCCESS) return -1; return bytes; } // // connect. // pj_status_t connect(const Pj_Inet_Addr &addr) { return pj_sock_connect(sock_, &addr, sizeof(Pj_Inet_Addr)); } // // assignment. // Pj_Socket &operator=(const Pj_Socket &rhs) { sock_ = rhs.sock_; return *this; } protected: friend class Pj_Event_Handler; pj_sock_t sock_; }; #if PJ_HAS_TCP // // Stream socket. // class Pj_Sock_Stream : public Pj_Socket { public: // // Default constructor. // Pj_Sock_Stream() { } // // Initialize from a socket handle. // explicit Pj_Sock_Stream(pj_sock_t sock) : Pj_Socket(sock) { } // // Copy constructor. // Pj_Sock_Stream(const Pj_Sock_Stream &rhs) : Pj_Socket(rhs) { } // // Assignment. // Pj_Sock_Stream &operator=(const Pj_Sock_Stream &rhs) { sock_ = rhs.sock_; return *this; } // // listen() // pj_status_t listen(int backlog = 5) { return pj_sock_listen(sock_, backlog); } // // blocking accept() // Pj_Sock_Stream accept(Pj_Inet_Addr *remote_addr = NULL) { pj_sock_t newsock; int *addrlen = remote_addr ? &remote_addr->addrlen_ : NULL; pj_status_t status; status = pj_sock_accept(sock_, &newsock, remote_addr, addrlen); if (status != PJ_SUCCESS) return Pj_Sock_Stream(-1); return Pj_Sock_Stream(newsock); } // // shutdown() // pj_status_t shutdown(int how = PJ_SHUT_RDWR) { return pj_sock_shutdown(sock_, how); } }; #endif // // Datagram socket. // class Pj_Sock_Dgram : public Pj_Socket { public: // // Default constructor. // Pj_Sock_Dgram() { } // // Initialize from a socket handle. // explicit Pj_Sock_Dgram(pj_sock_t sock) : Pj_Socket(sock) { } // // Copy constructor. // Pj_Sock_Dgram(const Pj_Sock_Dgram &rhs) : Pj_Socket(rhs) { } // // Assignment. // Pj_Sock_Dgram &operator=(const Pj_Sock_Dgram &rhs) { Pj_Socket::operator =(rhs); return *this; } // // recvfrom() // pj_ssize_t recvfrom( void *buf, pj_size_t len, int flag = 0, Pj_Inet_Addr *fromaddr = NULL) { pj_ssize_t bytes = len; int *addrlen = fromaddr ? &fromaddr->addrlen_ : NULL; if (pj_sock_recvfrom( sock_, buf, &bytes, flag, fromaddr, addrlen) != PJ_SUCCESS) { return -1; } return bytes; } // // sendto() // pj_ssize_t sendto( const void *buf, pj_size_t len, int flag, const Pj_Inet_Addr &addr) { pj_ssize_t bytes = len; if (pj_sock_sendto( sock_, buf, &bytes, flag, &addr, sizeof(pj_sockaddr_in)) != PJ_SUCCESS) { return -1; } return bytes; } }; #endif /* __PJPP_SOCK_HPP__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj++/string.hpp ================================================ /* $Id: string.hpp 2394 2008-12-23 17:27:53Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJPP_STRING_HPP__ #define __PJPP_STRING_HPP__ #include #include #include // // String wrapper class for pj_str_t. // class Pj_String : public pj_str_t { public: // // Default constructor. // Pj_String() { pj_assert(sizeof(Pj_String) == sizeof(pj_str_t)); ptr=NULL; slen=0; } // // Construct the buffer from a char* (use with care) // Pj_String(char *str) { set(str); } // // Construct from a const char*. // Pj_String(Pj_Pool &pool, const char *src) { set(pool, src); } // // Construct from pj_str_t&. // explicit Pj_String(pj_str_t &s) { ptr = s.ptr; slen = s.slen; } // // Construct from const pj_str_t& (use with care!). // explicit Pj_String(const pj_str_t &s) { ptr = (char*)s.ptr; slen = s.slen; } // // Construct by copying from const pj_str_t*. // Pj_String(Pj_Pool &pool, const pj_str_t *s) { set(pool, s); } // // Construct by copying from Pj_String // Pj_String(Pj_Pool &pool, const Pj_String &rhs) { set(pool, rhs); } // // Construct from another Pj_String, use with care! // explicit Pj_String(const Pj_String &rhs) { ptr = rhs.ptr; slen = rhs.slen; } // // Construct from a char* and a length. // Pj_String(char *str, pj_size_t len) { set(str, len); } // // Construct from pair of pointer. // Pj_String(char *begin, char *end) { pj_strset3(this, begin, end); } // // You can cast Pj_String to pj_str_t* // operator pj_str_t*() { return this; } // // You can cast const Pj_String to const pj_str_t* // operator const pj_str_t*() const { return this; } // // Get the length of the string. // pj_size_t length() const { return pj_strlen(this); } // // Get the length of the string. // pj_size_t size() const { return length(); } // // Get the string buffer. // const char *buf() const { return ptr; } // // Initialize buffer from char*. // void set(char *str) { pj_strset2(this, str); } // // Initialize by copying from a const char*. // void set(Pj_Pool &pool, const char *s) { pj_strdup2(pool, this, s); } // // Initialize from pj_str_t*. // void set(pj_str_t *s) { pj_strassign(this, s); } // // Initialize by copying from const pj_str_t*. // void set(Pj_Pool &pool, const pj_str_t *s) { pj_strdup(pool, this, s); } // // Initialize from char* and length. // void set(char *str, pj_size_t len) { pj_strset(this, str, len); } // // Initialize from pair of pointers. // void set(char *begin, char *end) { pj_strset3(this, begin, end); } // // Initialize from other Pj_String. // void set(Pj_String &rhs) { pj_strassign(this, &rhs); } // // Initialize by copying from a Pj_String*. // void set(Pj_Pool &pool, const Pj_String *s) { pj_strdup(pool, this, s); } // // Initialize by copying from other Pj_String. // void set(Pj_Pool &pool, const Pj_String &s) { pj_strdup(pool, this, &s); } // // Copy the contents of other string. // void strcpy(const pj_str_t *s) { pj_strcpy(this, s); } // // Copy the contents of other string. // void strcpy(const Pj_String &rhs) { pj_strcpy(this, &rhs); } // // Copy the contents of other string. // void strcpy(const char *s) { pj_strcpy2(this, s); } // // Compare string. // int strcmp(const char *s) const { return pj_strcmp2(this, s); } // // Compare string. // int strcmp(const pj_str_t *s) const { return pj_strcmp(this, s); } // // Compare string. // int strcmp(const Pj_String &rhs) const { return pj_strcmp(this, &rhs); } // // Compare string. // int strncmp(const char *s, pj_size_t len) const { return pj_strncmp2(this, s, len); } // // Compare string. // int strncmp(const pj_str_t *s, pj_size_t len) const { return pj_strncmp(this, s, len); } // // Compare string. // int strncmp(const Pj_String &rhs, pj_size_t len) const { return pj_strncmp(this, &rhs, len); } // // Compare string. // int stricmp(const char *s) const { return pj_stricmp2(this, s); } // // Compare string. // int stricmp(const pj_str_t *s) const { return pj_stricmp(this, s); } // // Compare string. // int stricmp(const Pj_String &rhs) const { return stricmp(&rhs); } // // Compare string. // int strnicmp(const char *s, pj_size_t len) const { return pj_strnicmp2(this, s, len); } // // Compare string. // int strnicmp(const pj_str_t *s, pj_size_t len) const { return pj_strnicmp(this, s, len); } // // Compare string. // int strnicmp(const Pj_String &rhs, pj_size_t len) const { return strnicmp(&rhs, len); } // // Compare contents for equality. // bool operator==(const char *s) const { return strcmp(s) == 0; } // // Compare contents for equality. // bool operator==(const pj_str_t *s) const { return strcmp(s) == 0; } // // Compare contents for equality. // bool operator==(const Pj_String &rhs) const { return pj_strcmp(this, &rhs) == 0; } // // Assign from char* // Pj_String& operator=(char *s) { set(s); return *this; } /// // Assign from another Pj_String, use with care! // Pj_String& operator=(const Pj_String &rhs) { ptr = rhs.ptr; slen = rhs.slen; return *this; } // // Find a character in the string. // char *strchr(int chr) { return pj_strchr(this, chr); } // // Find a character in the string. // char *find(int chr) { return strchr(chr); } // // Concatenate string. // void strcat(const Pj_String &rhs) { pj_strcat(this, &rhs); } // // Left trim. // void ltrim() { pj_strltrim(this); } // // Right trim. // void rtrim() { pj_strrtrim(this); } // // Left and right trim. // void trim() { pj_strtrim(this); } // // Convert to unsigned long. // unsigned long to_ulong() const { return pj_strtoul(this); } // // Convert from unsigned long. // void from_ulong(unsigned long value) { slen = pj_utoa(value, ptr); } // // Convert from unsigned long with padding. // void from_ulong_with_pad(unsigned long value, int min_dig=0, int pad=' ') { slen = pj_utoa_pad(value, ptr, min_dig, pad); } }; #endif /* __PJPP_STRING_HPP__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj++/timer.hpp ================================================ /* $Id: timer.hpp 2394 2008-12-23 17:27:53Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJPP_TIMER_HPP__ #define __PJPP_TIMER_HPP__ #include #include #include #include class Pj_Timer_Heap; ////////////////////////////////////////////////////////////////////////////// // Timer entry. // // How to use: // Derive class from Pj_Timer_Entry and override on_timeout(). // Scheduler timer in Pj_Timer_Heap. // class Pj_Timer_Entry : public Pj_Object { friend class Pj_Timer_Heap; public: // // Default constructor. // Pj_Timer_Entry() { entry_.user_data = this; entry_.cb = &timer_heap_callback; } // // Destructor, do nothing. // ~Pj_Timer_Entry() { } // // Override this to get the timeout notification. // virtual void on_timeout(int id) = 0; private: pj_timer_entry entry_; static void timer_heap_callback(pj_timer_heap_t*, pj_timer_entry *e) { Pj_Timer_Entry *entry = (Pj_Timer_Entry*) e->user_data; entry->on_timeout(e->id); } }; ////////////////////////////////////////////////////////////////////////////// // Timer heap. // class Pj_Timer_Heap : public Pj_Object { public: // // Default constructor. // Pj_Timer_Heap() : ht_(NULL) { } // // Construct timer heap. // Pj_Timer_Heap(Pj_Pool *pool, pj_size_t initial_count) : ht_(NULL) { create(pool, initial_count); } // // Destructor. // ~Pj_Timer_Heap() { destroy(); } // // Create // pj_status_t create(Pj_Pool *pool, pj_size_t initial_count) { destroy(); return pj_timer_heap_create(pool->pool_(), initial_count, &ht_); } // // Destroy // void destroy() { if (ht_) { pj_timer_heap_destroy(ht_); ht_ = NULL; } } // // Get pjlib compatible timer heap object. // pj_timer_heap_t *get_timer_heap() { return ht_; } // // Set the lock object. // void set_lock( Pj_Lock *lock, bool auto_delete ) { pj_timer_heap_set_lock( ht_, lock->pj_lock_t_(), auto_delete); } // // Set maximum number of timed out entries to be processed per poll. // unsigned set_max_timed_out_per_poll(unsigned count) { return pj_timer_heap_set_max_timed_out_per_poll(ht_, count); } // // Schedule a timer. // bool schedule( Pj_Timer_Entry *ent, const Pj_Time_Val &delay, int id) { ent->entry_.id = id; return pj_timer_heap_schedule(ht_, &ent->entry_, &delay) == 0; } // // Cancel a timer. // bool cancel(Pj_Timer_Entry *ent) { return pj_timer_heap_cancel(ht_, &ent->entry_) == 1; } // // Get current number of timers // pj_size_t count() { return pj_timer_heap_count(ht_); } // // Get the earliest time. // Return false if no timer is found. // bool earliest_time(Pj_Time_Val *t) { return pj_timer_heap_earliest_time(ht_, t) == PJ_SUCCESS; } // // Poll the timer. // Return number of timed out entries has been called. // unsigned poll(Pj_Time_Val *next_delay = NULL) { return pj_timer_heap_poll(ht_, next_delay); } private: pj_timer_heap_t *ht_; }; #endif /* __PJPP_TIMER_HPP__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj++/tree.hpp ================================================ /* $Id: tree.hpp 2394 2008-12-23 17:27:53Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJPP_TREE_HPP__ #define __PJPP_TREE_HPP__ #include // // Tree. // class PJ_Tree { public: typedef pj_rbtree_comp Comp; class iterator; class reverse_iterator; class Node : private pj_rbtree_node { friend class PJ_Tree; friend class iterator; friend class reverse_iterator; public: Node() {} explicit Node(void *data) { user_data = data; } void set_user_data(void *data) { user_data = data; } void *get_user_data() const { return user_data; } }; class iterator { public: iterator() {} iterator(const iterator &rhs) : tr_(rhs.tr_), nd_(rhs.nd_) {} iterator(pj_rbtree *tr, pj_rbtree_node *nd) : tr_(tr), nd_(nd) {} Node *operator*() { return (Node*)nd_; } bool operator==(const iterator &rhs) const { return tr_==rhs.tr_ && nd_==rhs.nd_; } iterator &operator=(const iterator &rhs) { tr_=rhs.tr_; nd_=rhs.nd_; return *this; } void operator++() { nd_=pj_rbtree_next(tr_, nd_); } void operator--() { nd_=pj_rbtree_prev(tr_, nd_); } protected: pj_rbtree *tr_; pj_rbtree_node *nd_; }; class reverse_iterator : public iterator { public: reverse_iterator() {} reverse_iterator(const reverse_iterator &it) : iterator(it) {} reverse_iterator(pj_rbtree *t, pj_rbtree_node *n) : iterator(t, n) {} reverse_iterator &operator=(const reverse_iterator &rhs) { iterator::operator=(rhs); return *this; } Node *operator*() { return (Node*)nd_; } bool operator==(const reverse_iterator &rhs) const { return iterator::operator==(rhs); } void operator++() { nd_=pj_rbtree_prev(tr_, nd_); } void operator--() { nd_=pj_rbtree_next(tr_, nd_); } }; explicit PJ_Tree(Comp *comp) { pj_rbtree_init(&t_, comp); } iterator begin() { return iterator(&t_, pj_rbtree_first(&t_)); } iterator end() { return iterator(&t_, NULL); } reverse_iterator rbegin() { return reverse_iterator(&t_, pj_rbtree_last(&t_)); } reverse_iterator rend() { return reverse_iterator(&t_, NULL); } bool insert(Node *node) { return pj_rbtree_insert(&t_, node)==0 ? true : false; } Node *find(const void *key) { return (Node*)pj_rbtree_find(&t_, key); } Node *erase(Node *node) { return (Node*)pj_rbtree_erase(&t_, node); } unsigned max_height(Node *node=NULL) { return pj_rbtree_max_height(&t_, node); } unsigned min_height(Node *node=NULL) { return pj_rbtree_min_height(&t_, node); } private: pj_rbtree t_; }; #endif /* __PJPP_TREE_HPP__ */ ================================================ FILE: deps/pjsip/pjlib/include/pj++/types.hpp ================================================ /* $Id: types.hpp 2394 2008-12-23 17:27:53Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJPP_TYPES_HPP__ #define __PJPP_TYPES_HPP__ #include class Pj_Pool; class Pj_Socket ; class Pj_Lock; // // PJLIB initializer. // class Pjlib { public: Pjlib() { pj_init(); } }; // // Class Pj_Object is declared in pool.hpp // // // Time value wrapper. // class Pj_Time_Val : public pj_time_val { public: Pj_Time_Val() { } Pj_Time_Val(long init_sec, long init_msec) { sec = init_sec; msec = init_msec; } Pj_Time_Val(const Pj_Time_Val &rhs) { sec=rhs.sec; msec=rhs.msec; } explicit Pj_Time_Val(const pj_time_val &tv) { sec = tv.sec; msec = tv.msec; } long get_sec() const { return sec; } long get_msec() const { return msec; } void set_sec (long s) { sec = s; } void set_msec(long ms) { msec = ms; normalize(); } long to_msec() const { return PJ_TIME_VAL_MSEC((*this)); } bool operator == (const Pj_Time_Val &rhs) const { return PJ_TIME_VAL_EQ((*this), rhs); } bool operator > (const Pj_Time_Val &rhs) const { return PJ_TIME_VAL_GT((*this), rhs); } bool operator >= (const Pj_Time_Val &rhs) const { return PJ_TIME_VAL_GTE((*this), rhs); } bool operator < (const Pj_Time_Val &rhs) const { return PJ_TIME_VAL_LT((*this), rhs); } bool operator <= (const Pj_Time_Val &rhs) const { return PJ_TIME_VAL_LTE((*this), rhs); } Pj_Time_Val & operator = (const Pj_Time_Val &rhs) { sec = rhs.sec; msec = rhs.msec; return *this; } Pj_Time_Val & operator += (const Pj_Time_Val &rhs) { PJ_TIME_VAL_ADD((*this), rhs); return *this; } Pj_Time_Val & operator -= (const Pj_Time_Val &rhs) { PJ_TIME_VAL_SUB((*this), rhs); return *this; } /* Must include os.hpp to use these, otherwise unresolved in linking */ inline pj_status_t gettimeofday(); inline pj_parsed_time decode(); inline pj_status_t encode(const pj_parsed_time *pt); inline pj_status_t to_gmt(); inline pj_status_t to_local(); private: void normalize() { pj_time_val_normalize(this); } }; // // Macro to declare common object comparison operators. // #define PJ_DECLARE_OPERATORS(rhs_type) \ bool operator!=(rhs_type rhs) const { \ return !operator==(rhs); } \ bool operator<=(rhs_type rhs) const { \ return operator<(rhs) || operator==(rhs); } \ bool operator>(rhs_type rhs) const { \ return !operator<=(rhs); } \ bool operator>=(rhs_type rhs) const { \ return !operator<(rhs); } #endif /* __PJPP_TYPES_HPP__ */ ================================================ FILE: deps/pjsip/pjlib/include/pjlib++.hpp ================================================ /* $Id: pjlib++.hpp 2394 2008-12-23 17:27:53Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIBPP_H__ #define __PJLIBPP_H__ #include #include #include #include #include #include #include #include #include #include #include #endif /* __PJLIBPP_H__ */ ================================================ FILE: deps/pjsip/pjlib/include/pjlib.h ================================================ /* $Id: pjlib.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_H__ #define __PJLIB_H__ /** * @file pjlib.h * @brief Include all PJLIB header files. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* __PJLIB_H__ */ ================================================ FILE: deps/pjsip/pjlib/src/pj/activesock.c ================================================ /* $Id: activesock.c 4461 2013-04-05 03:02:19Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 # include static pj_bool_t ios_bg_support = PJ_TRUE; #endif #define PJ_ACTIVESOCK_MAX_LOOP 50 enum read_type { TYPE_NONE, TYPE_RECV, TYPE_RECV_FROM }; enum shutdown_dir { SHUT_NONE = 0, SHUT_RX = 1, SHUT_TX = 2 }; struct read_op { pj_ioqueue_op_key_t op_key; pj_uint8_t *pkt; unsigned max_size; pj_size_t size; pj_sockaddr src_addr; int src_addr_len; }; struct accept_op { pj_ioqueue_op_key_t op_key; pj_sock_t new_sock; pj_sockaddr rem_addr; int rem_addr_len; }; struct send_data { pj_uint8_t *data; pj_ssize_t len; pj_ssize_t sent; unsigned flags; }; struct pj_activesock_t { pj_ioqueue_key_t *key; pj_bool_t stream_oriented; pj_bool_t whole_data; pj_ioqueue_t *ioqueue; void *user_data; unsigned async_count; unsigned shutdown; unsigned max_loop; pj_activesock_cb cb; #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 int bg_setting; pj_sock_t sock; CFReadStreamRef readStream; #endif unsigned err_counter; pj_status_t last_err; struct send_data send_data; struct read_op *read_op; pj_uint32_t read_flags; enum read_type read_type; struct accept_op *accept_op; }; static void ioqueue_on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read); static void ioqueue_on_write_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_sent); #if PJ_HAS_TCP static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t sock, pj_status_t status); static void ioqueue_on_connect_complete(pj_ioqueue_key_t *key, pj_status_t status); #endif PJ_DEF(void) pj_activesock_cfg_default(pj_activesock_cfg *cfg) { pj_bzero(cfg, sizeof(*cfg)); cfg->async_cnt = 1; cfg->concurrency = -1; cfg->whole_data = PJ_TRUE; } #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 static void activesock_destroy_iphone_os_stream(pj_activesock_t *asock) { if (asock->readStream) { CFReadStreamClose(asock->readStream); CFRelease(asock->readStream); asock->readStream = NULL; } } static void activesock_create_iphone_os_stream(pj_activesock_t *asock) { if (ios_bg_support && asock->bg_setting && asock->stream_oriented) { activesock_destroy_iphone_os_stream(asock); CFStreamCreatePairWithSocket(kCFAllocatorDefault, asock->sock, &asock->readStream, NULL); if (!asock->readStream || CFReadStreamSetProperty(asock->readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP) != TRUE || CFReadStreamOpen(asock->readStream) != TRUE) { PJ_LOG(2,("", "Failed to configure TCP transport for VoIP " "usage. Usage of THIS particular TCP transport in " "background mode will not be supported.")); activesock_destroy_iphone_os_stream(asock); } } } PJ_DEF(void) pj_activesock_set_iphone_os_bg(pj_activesock_t *asock, int val) { asock->bg_setting = val; if (asock->bg_setting) activesock_create_iphone_os_stream(asock); else activesock_destroy_iphone_os_stream(asock); } PJ_DEF(void) pj_activesock_enable_iphone_os_bg(pj_bool_t val) { ios_bg_support = val; } #endif PJ_DEF(pj_status_t) pj_activesock_create( pj_pool_t *pool, pj_sock_t sock, int sock_type, const pj_activesock_cfg *opt, pj_ioqueue_t *ioqueue, const pj_activesock_cb *cb, void *user_data, pj_activesock_t **p_asock) { pj_activesock_t *asock; pj_ioqueue_callback ioq_cb; pj_status_t status; PJ_ASSERT_RETURN(pool && ioqueue && cb && p_asock, PJ_EINVAL); PJ_ASSERT_RETURN(sock!=0 && sock!=PJ_INVALID_SOCKET, PJ_EINVAL); PJ_ASSERT_RETURN(sock_type==pj_SOCK_STREAM() || sock_type==pj_SOCK_DGRAM(), PJ_EINVAL); PJ_ASSERT_RETURN(!opt || opt->async_cnt >= 1, PJ_EINVAL); asock = PJ_POOL_ZALLOC_T(pool, pj_activesock_t); asock->ioqueue = ioqueue; asock->stream_oriented = (sock_type == pj_SOCK_STREAM()); asock->async_count = (opt? opt->async_cnt : 1); asock->whole_data = (opt? opt->whole_data : 1); asock->max_loop = PJ_ACTIVESOCK_MAX_LOOP; asock->user_data = user_data; pj_memcpy(&asock->cb, cb, sizeof(*cb)); pj_bzero(&ioq_cb, sizeof(ioq_cb)); ioq_cb.on_read_complete = &ioqueue_on_read_complete; ioq_cb.on_write_complete = &ioqueue_on_write_complete; #if PJ_HAS_TCP ioq_cb.on_connect_complete = &ioqueue_on_connect_complete; ioq_cb.on_accept_complete = &ioqueue_on_accept_complete; #endif status = pj_ioqueue_register_sock2(pool, ioqueue, sock, (opt? opt->grp_lock : NULL), asock, &ioq_cb, &asock->key); if (status != PJ_SUCCESS) { pj_activesock_close(asock); return status; } if (asock->whole_data) { /* Must disable concurrency otherwise there is a race condition */ pj_ioqueue_set_concurrency(asock->key, 0); } else if (opt && opt->concurrency >= 0) { pj_ioqueue_set_concurrency(asock->key, opt->concurrency); } #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 asock->sock = sock; asock->bg_setting = PJ_ACTIVESOCK_TCP_IPHONE_OS_BG; #endif *p_asock = asock; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_activesock_create_udp( pj_pool_t *pool, const pj_sockaddr *addr, const pj_activesock_cfg *opt, pj_ioqueue_t *ioqueue, const pj_activesock_cb *cb, void *user_data, pj_activesock_t **p_asock, pj_sockaddr *bound_addr) { pj_sock_t sock_fd; pj_sockaddr default_addr; pj_status_t status; if (addr == NULL) { pj_sockaddr_init(pj_AF_INET(), &default_addr, NULL, 0); addr = &default_addr; } status = pj_sock_socket(addr->addr.sa_family, pj_SOCK_DGRAM(), 0, &sock_fd); if (status != PJ_SUCCESS) { return status; } status = pj_sock_bind(sock_fd, addr, pj_sockaddr_get_len(addr)); if (status != PJ_SUCCESS) { pj_sock_close(sock_fd); return status; } status = pj_activesock_create(pool, sock_fd, pj_SOCK_DGRAM(), opt, ioqueue, cb, user_data, p_asock); if (status != PJ_SUCCESS) { pj_sock_close(sock_fd); return status; } if (bound_addr) { int addr_len = sizeof(*bound_addr); status = pj_sock_getsockname(sock_fd, bound_addr, &addr_len); if (status != PJ_SUCCESS) { pj_activesock_close(*p_asock); return status; } } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_activesock_close(pj_activesock_t *asock) { PJ_ASSERT_RETURN(asock, PJ_EINVAL); asock->shutdown = SHUT_RX | SHUT_TX; if (asock->key) { pj_ioqueue_unregister(asock->key); #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 activesock_destroy_iphone_os_stream(asock); #endif asock->key = NULL; } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_activesock_set_user_data( pj_activesock_t *asock, void *user_data) { PJ_ASSERT_RETURN(asock, PJ_EINVAL); asock->user_data = user_data; return PJ_SUCCESS; } PJ_DEF(void*) pj_activesock_get_user_data(pj_activesock_t *asock) { PJ_ASSERT_RETURN(asock, NULL); return asock->user_data; } PJ_DEF(pj_status_t) pj_activesock_start_read(pj_activesock_t *asock, pj_pool_t *pool, unsigned buff_size, pj_uint32_t flags) { void **readbuf; unsigned i; PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL); readbuf = (void**) pj_pool_calloc(pool, asock->async_count, sizeof(void*)); for (i=0; iasync_count; ++i) { readbuf[i] = pj_pool_alloc(pool, buff_size); } return pj_activesock_start_read2(asock, pool, buff_size, readbuf, flags); } PJ_DEF(pj_status_t) pj_activesock_start_read2( pj_activesock_t *asock, pj_pool_t *pool, unsigned buff_size, void *readbuf[], pj_uint32_t flags) { unsigned i; pj_status_t status; PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL); PJ_ASSERT_RETURN(asock->read_type == TYPE_NONE, PJ_EINVALIDOP); PJ_ASSERT_RETURN(asock->read_op == NULL, PJ_EINVALIDOP); asock->read_op = (struct read_op*) pj_pool_calloc(pool, asock->async_count, sizeof(struct read_op)); asock->read_type = TYPE_RECV; asock->read_flags = flags; for (i=0; iasync_count; ++i) { struct read_op *r = &asock->read_op[i]; pj_ssize_t size_to_read; r->pkt = (pj_uint8_t*)readbuf[i]; size_to_read = r->max_size = buff_size; status = pj_ioqueue_recv(asock->key, &r->op_key, r->pkt, &size_to_read, PJ_IOQUEUE_ALWAYS_ASYNC | flags); PJ_ASSERT_RETURN(status != PJ_SUCCESS, PJ_EBUG); if (status != PJ_EPENDING) return status; } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_activesock_start_recvfrom(pj_activesock_t *asock, pj_pool_t *pool, unsigned buff_size, pj_uint32_t flags) { void **readbuf; unsigned i; PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL); readbuf = (void**) pj_pool_calloc(pool, asock->async_count, sizeof(void*)); for (i=0; iasync_count; ++i) { readbuf[i] = pj_pool_alloc(pool, buff_size); } return pj_activesock_start_recvfrom2(asock, pool, buff_size, readbuf, flags); } PJ_DEF(pj_status_t) pj_activesock_start_recvfrom2( pj_activesock_t *asock, pj_pool_t *pool, unsigned buff_size, void *readbuf[], pj_uint32_t flags) { unsigned i; pj_status_t status; PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL); PJ_ASSERT_RETURN(asock->read_type == TYPE_NONE, PJ_EINVALIDOP); asock->read_op = (struct read_op*) pj_pool_calloc(pool, asock->async_count, sizeof(struct read_op)); asock->read_type = TYPE_RECV_FROM; asock->read_flags = flags; for (i=0; iasync_count; ++i) { struct read_op *r = &asock->read_op[i]; pj_ssize_t size_to_read; r->pkt = (pj_uint8_t*) readbuf[i]; size_to_read = r->max_size = buff_size; r->src_addr_len = sizeof(r->src_addr); status = pj_ioqueue_recvfrom(asock->key, &r->op_key, r->pkt, &size_to_read, PJ_IOQUEUE_ALWAYS_ASYNC | flags, &r->src_addr, &r->src_addr_len); PJ_ASSERT_RETURN(status != PJ_SUCCESS, PJ_EBUG); if (status != PJ_EPENDING) return status; } return PJ_SUCCESS; } static void ioqueue_on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { pj_activesock_t *asock; struct read_op *r = (struct read_op*)op_key; unsigned loop = 0; pj_status_t status; asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key); /* Ignore if we've been shutdown */ if (asock->shutdown & SHUT_RX) return; do { unsigned flags; if (bytes_read > 0) { /* * We've got new data. */ pj_size_t remainder; pj_bool_t ret; /* Append this new data to existing data. If socket is stream * oriented, user might have left some data in the buffer. * Otherwise if socket is datagram there will be nothing in * existing packet hence the packet will contain only the new * packet. */ r->size += bytes_read; /* Set default remainder to zero */ remainder = 0; /* And return value to TRUE */ ret = PJ_TRUE; /* Notify callback */ if (asock->read_type == TYPE_RECV && asock->cb.on_data_read) { ret = (*asock->cb.on_data_read)(asock, r->pkt, r->size, PJ_SUCCESS, &remainder); } else if (asock->read_type == TYPE_RECV_FROM && asock->cb.on_data_recvfrom) { ret = (*asock->cb.on_data_recvfrom)(asock, r->pkt, r->size, &r->src_addr, r->src_addr_len, PJ_SUCCESS); } /* If callback returns false, we have been destroyed! */ if (!ret) return; /* Only stream oriented socket may leave data in the packet */ if (asock->stream_oriented) { r->size = remainder; } else { r->size = 0; } } else if (bytes_read <= 0 && -bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) && -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && (asock->stream_oriented || -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET))) { pj_size_t remainder; pj_bool_t ret; if (bytes_read == 0) { /* For stream/connection oriented socket, this means the * connection has been closed. For datagram sockets, it means * we've received datagram with zero length. */ if (asock->stream_oriented) status = PJ_EEOF; else status = PJ_SUCCESS; } else { /* This means we've got an error. If this is stream/connection * oriented, it means connection has been closed. For datagram * sockets, it means we've got some error (e.g. EWOULDBLOCK). */ status = (pj_status_t)-bytes_read; } /* Set default remainder to zero */ remainder = 0; /* And return value to TRUE */ ret = PJ_TRUE; /* Notify callback */ if (asock->read_type == TYPE_RECV && asock->cb.on_data_read) { /* For connection oriented socket, we still need to report * the remainder data (if any) to the user to let user do * processing with the remainder data before it closes the * connection. * If there is no remainder data, set the packet to NULL. */ /* Shouldn't set the packet to NULL, as there may be active * socket user, such as SSL socket, that needs to have access * to the read buffer packet. */ //ret = (*asock->cb.on_data_read)(asock, (r->size? r->pkt:NULL), // r->size, status, &remainder); ret = (*asock->cb.on_data_read)(asock, r->pkt, r->size, status, &remainder); } else if (asock->read_type == TYPE_RECV_FROM && asock->cb.on_data_recvfrom) { /* This would always be datagram oriented hence there's * nothing in the packet. We can't be sure if there will be * anything useful in the source_addr, so just put NULL * there too. */ /* In some scenarios, status may be PJ_SUCCESS. The upper * layer application may not expect the callback to be called * with successful status and NULL data, so lets not call the * callback if the status is PJ_SUCCESS. */ if (status != PJ_SUCCESS ) { ret = (*asock->cb.on_data_recvfrom)(asock, NULL, 0, NULL, 0, status); } } /* If callback returns false, we have been destroyed! */ if (!ret) return; /* Also stop further read if we've been shutdown */ if (asock->shutdown & SHUT_RX) return; /* Only stream oriented socket may leave data in the packet */ if (asock->stream_oriented) { r->size = remainder; } else { r->size = 0; } } /* Read next data. We limit ourselves to processing max_loop immediate * data, so when the loop counter has exceeded this value, force the * read()/recvfrom() to return pending operation to allow the program * to do other jobs. */ bytes_read = r->max_size - r->size; flags = asock->read_flags; if (++loop >= asock->max_loop) flags |= PJ_IOQUEUE_ALWAYS_ASYNC; if (asock->read_type == TYPE_RECV) { status = pj_ioqueue_recv(key, op_key, r->pkt + r->size, &bytes_read, flags); } else { r->src_addr_len = sizeof(r->src_addr); status = pj_ioqueue_recvfrom(key, op_key, r->pkt + r->size, &bytes_read, flags, &r->src_addr, &r->src_addr_len); } if (status == PJ_SUCCESS) { /* Immediate data */ ; } else if (status != PJ_EPENDING && status != PJ_ECANCELLED) { /* Error */ bytes_read = -status; } else { break; } } while (1); } static pj_status_t send_remaining(pj_activesock_t *asock, pj_ioqueue_op_key_t *send_key) { struct send_data *sd = (struct send_data*)send_key->activesock_data; pj_status_t status; do { pj_ssize_t size; size = sd->len - sd->sent; status = pj_ioqueue_send(asock->key, send_key, sd->data+sd->sent, &size, sd->flags); if (status != PJ_SUCCESS) { /* Pending or error */ break; } sd->sent += size; if (sd->sent == sd->len) { /* The whole data has been sent. */ return PJ_SUCCESS; } } while (sd->sent < sd->len); return status; } PJ_DEF(pj_status_t) pj_activesock_send( pj_activesock_t *asock, pj_ioqueue_op_key_t *send_key, const void *data, pj_ssize_t *size, unsigned flags) { PJ_ASSERT_RETURN(asock && send_key && data && size, PJ_EINVAL); if (asock->shutdown & SHUT_TX) return PJ_EINVALIDOP; send_key->activesock_data = NULL; if (asock->whole_data) { pj_ssize_t whole; pj_status_t status; whole = *size; status = pj_ioqueue_send(asock->key, send_key, data, size, flags); if (status != PJ_SUCCESS) { /* Pending or error */ return status; } if (*size == whole) { /* The whole data has been sent. */ return PJ_SUCCESS; } /* Data was partially sent */ asock->send_data.data = (pj_uint8_t*)data; asock->send_data.len = whole; asock->send_data.sent = *size; asock->send_data.flags = flags; send_key->activesock_data = &asock->send_data; /* Try again */ status = send_remaining(asock, send_key); if (status == PJ_SUCCESS) { *size = whole; } return status; } else { return pj_ioqueue_send(asock->key, send_key, data, size, flags); } } PJ_DEF(pj_status_t) pj_activesock_sendto( pj_activesock_t *asock, pj_ioqueue_op_key_t *send_key, const void *data, pj_ssize_t *size, unsigned flags, const pj_sockaddr_t *addr, int addr_len) { PJ_ASSERT_RETURN(asock && send_key && data && size && addr && addr_len, PJ_EINVAL); if (asock->shutdown & SHUT_TX) return PJ_EINVALIDOP; return pj_ioqueue_sendto(asock->key, send_key, data, size, flags, addr, addr_len); } static void ioqueue_on_write_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_sent) { pj_activesock_t *asock; asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key); /* Ignore if we've been shutdown. This may cause data to be partially * sent even when 'wholedata' was requested if the OS only sent partial * buffer. */ if (asock->shutdown & SHUT_TX) return; if (bytes_sent > 0 && op_key->activesock_data) { /* whole_data is requested. Make sure we send all the data */ struct send_data *sd = (struct send_data*)op_key->activesock_data; sd->sent += bytes_sent; if (sd->sent == sd->len) { /* all has been sent */ bytes_sent = sd->sent; op_key->activesock_data = NULL; } else { /* send remaining data */ pj_status_t status; status = send_remaining(asock, op_key); if (status == PJ_EPENDING) return; else if (status == PJ_SUCCESS) bytes_sent = sd->sent; else bytes_sent = -status; op_key->activesock_data = NULL; } } if (asock->cb.on_data_sent) { pj_bool_t ret; ret = (*asock->cb.on_data_sent)(asock, op_key, bytes_sent); /* If callback returns false, we have been destroyed! */ if (!ret) return; } } #if PJ_HAS_TCP PJ_DEF(pj_status_t) pj_activesock_start_accept(pj_activesock_t *asock, pj_pool_t *pool) { unsigned i; PJ_ASSERT_RETURN(asock, PJ_EINVAL); PJ_ASSERT_RETURN(asock->accept_op==NULL, PJ_EINVALIDOP); /* Ignore if we've been shutdown */ if (asock->shutdown) return PJ_EINVALIDOP; asock->accept_op = (struct accept_op*) pj_pool_calloc(pool, asock->async_count, sizeof(struct accept_op)); for (i=0; iasync_count; ++i) { struct accept_op *a = &asock->accept_op[i]; pj_status_t status; do { a->new_sock = PJ_INVALID_SOCKET; a->rem_addr_len = sizeof(a->rem_addr); status = pj_ioqueue_accept(asock->key, &a->op_key, &a->new_sock, NULL, &a->rem_addr, &a->rem_addr_len); if (status == PJ_SUCCESS) { /* We've got immediate connection. Not sure if it's a good * idea to call the callback now (probably application will * not be prepared to process it), so lets just silently * close the socket. */ pj_sock_close(a->new_sock); } } while (status == PJ_SUCCESS); if (status != PJ_EPENDING) { return status; } } return PJ_SUCCESS; } static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t new_sock, pj_status_t status) { pj_activesock_t *asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key); struct accept_op *accept_op = (struct accept_op*) op_key; PJ_UNUSED_ARG(new_sock); /* Ignore if we've been shutdown */ if (asock->shutdown) return; do { if (status == asock->last_err && status != PJ_SUCCESS) { asock->err_counter++; if (asock->err_counter >= PJ_ACTIVESOCK_MAX_CONSECUTIVE_ACCEPT_ERROR) { PJ_LOG(3, ("", "Received %d consecutive errors: %d for the accept()" " operation, stopping further ioqueue accepts.", asock->err_counter, asock->last_err)); if ((status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) && (asock->cb.on_accept_complete2)) { (*asock->cb.on_accept_complete2)(asock, accept_op->new_sock, &accept_op->rem_addr, accept_op->rem_addr_len, PJ_ESOCKETSTOP); } return; } } else { asock->err_counter = 0; asock->last_err = status; } if (status==PJ_SUCCESS && (asock->cb.on_accept_complete2 || asock->cb.on_accept_complete)) { pj_bool_t ret; /* Notify callback */ if (asock->cb.on_accept_complete2) { ret = (*asock->cb.on_accept_complete2)(asock, accept_op->new_sock, &accept_op->rem_addr, accept_op->rem_addr_len, status); } else { ret = (*asock->cb.on_accept_complete)(asock, accept_op->new_sock, &accept_op->rem_addr, accept_op->rem_addr_len); } /* If callback returns false, we have been destroyed! */ if (!ret) return; #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 activesock_create_iphone_os_stream(asock); #endif } else if (status==PJ_SUCCESS) { /* Application doesn't handle the new socket, we need to * close it to avoid resource leak. */ pj_sock_close(accept_op->new_sock); } /* Don't start another accept() if we've been shutdown */ if (asock->shutdown) return; /* Prepare next accept() */ accept_op->new_sock = PJ_INVALID_SOCKET; accept_op->rem_addr_len = sizeof(accept_op->rem_addr); status = pj_ioqueue_accept(asock->key, op_key, &accept_op->new_sock, NULL, &accept_op->rem_addr, &accept_op->rem_addr_len); } while (status != PJ_EPENDING && status != PJ_ECANCELLED); } PJ_DEF(pj_status_t) pj_activesock_start_connect( pj_activesock_t *asock, pj_pool_t *pool, const pj_sockaddr_t *remaddr, int addr_len) { PJ_UNUSED_ARG(pool); if (asock->shutdown) return PJ_EINVALIDOP; return pj_ioqueue_connect(asock->key, remaddr, addr_len); } static void ioqueue_on_connect_complete(pj_ioqueue_key_t *key, pj_status_t status) { pj_activesock_t *asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key); /* Ignore if we've been shutdown */ if (asock->shutdown) return; if (asock->cb.on_connect_complete) { pj_bool_t ret; ret = (*asock->cb.on_connect_complete)(asock, status); if (!ret) { /* We've been destroyed */ return; } #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 activesock_create_iphone_os_stream(asock); #endif } } #endif /* PJ_HAS_TCP */ ================================================ FILE: deps/pjsip/pjlib/src/pj/addr_resolv_linux_kernel.c ================================================ /* $Id: addr_resolv_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe) { return -1; } ================================================ FILE: deps/pjsip/pjlib/src/pj/addr_resolv_sock.c ================================================ /* $Id: addr_resolv_sock.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #if defined(PJ_GETADDRINFO_USE_CFHOST) && PJ_GETADDRINFO_USE_CFHOST!=0 # include # include #endif PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe) { struct hostent *he; char copy[PJ_MAX_HOSTNAME]; pj_assert(hostname && hostname ->slen < PJ_MAX_HOSTNAME); if (hostname->slen >= PJ_MAX_HOSTNAME) return PJ_ENAMETOOLONG; pj_memcpy(copy, hostname->ptr, hostname->slen); copy[ hostname->slen ] = '\0'; he = gethostbyname(copy); if (!he) { return PJ_ERESOLVE; /* DO NOT use pj_get_netos_error() since host resolution error * is reported in h_errno instead of errno! return pj_get_netos_error(); */ } phe->h_name = he->h_name; phe->h_aliases = he->h_aliases; phe->h_addrtype = he->h_addrtype; phe->h_length = he->h_length; phe->h_addr_list = he->h_addr_list; return PJ_SUCCESS; } /* Resolve IPv4/IPv6 address */ PJ_DEF(pj_status_t) pj_getaddrinfo(int af, const pj_str_t *nodename, unsigned *count, pj_addrinfo ai[]) { #if defined(PJ_SOCK_HAS_GETADDRINFO) && PJ_SOCK_HAS_GETADDRINFO!=0 char nodecopy[PJ_MAX_HOSTNAME]; pj_bool_t has_addr = PJ_FALSE; unsigned i; #if defined(PJ_GETADDRINFO_USE_CFHOST) && PJ_GETADDRINFO_USE_CFHOST!=0 CFStringRef hostname; CFHostRef hostRef; pj_status_t status = PJ_SUCCESS; #else int rc; struct addrinfo hint, *res, *orig_res; #endif PJ_ASSERT_RETURN(nodename && count && *count && ai, PJ_EINVAL); PJ_ASSERT_RETURN(nodename->ptr && nodename->slen, PJ_EINVAL); PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6 || af==PJ_AF_UNSPEC, PJ_EINVAL); /* Check if nodename is IP address */ pj_bzero(&ai[0], sizeof(ai[0])); if ((af==PJ_AF_INET || af==PJ_AF_UNSPEC) && pj_inet_pton(PJ_AF_INET, nodename, &ai[0].ai_addr.ipv4.sin_addr) == PJ_SUCCESS) { af = PJ_AF_INET; has_addr = PJ_TRUE; } else if ((af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) && pj_inet_pton(PJ_AF_INET6, nodename, &ai[0].ai_addr.ipv6.sin6_addr) == PJ_SUCCESS) { af = PJ_AF_INET6; has_addr = PJ_TRUE; } if (has_addr) { pj_str_t tmp; tmp.ptr = ai[0].ai_canonname; pj_strncpy_with_null(&tmp, nodename, PJ_MAX_HOSTNAME); ai[0].ai_addr.addr.sa_family = (pj_uint16_t)af; *count = 1; return PJ_SUCCESS; } /* Copy node name to null terminated string. */ if (nodename->slen >= PJ_MAX_HOSTNAME) return PJ_ENAMETOOLONG; pj_memcpy(nodecopy, nodename->ptr, nodename->slen); nodecopy[nodename->slen] = '\0'; #if defined(PJ_GETADDRINFO_USE_CFHOST) && PJ_GETADDRINFO_USE_CFHOST!=0 hostname = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, nodecopy, kCFStringEncodingASCII, kCFAllocatorNull); hostRef = CFHostCreateWithName(kCFAllocatorDefault, hostname); if (CFHostStartInfoResolution(hostRef, kCFHostAddresses, nil)) { CFArrayRef addrRef = CFHostGetAddressing(hostRef, nil); i = 0; if (addrRef != nil) { CFIndex idx, naddr; naddr = CFArrayGetCount(addrRef); for (idx = 0; idx < naddr && i < *count; idx++) { struct sockaddr *addr; size_t addr_size; addr = (struct sockaddr *) CFDataGetBytePtr(CFArrayGetValueAtIndex(addrRef, idx)); /* This should not happen. */ pj_assert(addr); /* Ignore unwanted address families */ if (af!=PJ_AF_UNSPEC && addr->sa_family != af) continue; /* Store canonical name */ pj_ansi_strcpy(ai[i].ai_canonname, nodecopy); /* Store address */ addr_size = sizeof(*addr); if (af == PJ_AF_INET6) { addr_size = addr->sa_len; } PJ_ASSERT_ON_FAIL(addr_size <= sizeof(pj_sockaddr), continue); pj_memcpy(&ai[i].ai_addr, addr, addr_size); PJ_SOCKADDR_RESET_LEN(&ai[i].ai_addr); i++; } } *count = i; } else { status = PJ_ERESOLVE; } CFRelease(hostRef); CFRelease(hostname); return status; #else /* Call getaddrinfo() */ pj_bzero(&hint, sizeof(hint)); hint.ai_family = af; rc = getaddrinfo(nodecopy, NULL, &hint, &res); if (rc != 0) return PJ_ERESOLVE; orig_res = res; /* Enumerate each item in the result */ for (i=0; i<*count && res; res=res->ai_next) { /* Ignore unwanted address families */ if (af!=PJ_AF_UNSPEC && res->ai_family != af) continue; /* Store canonical name (possibly truncating the name) */ if (res->ai_canonname) { pj_ansi_strncpy(ai[i].ai_canonname, res->ai_canonname, sizeof(ai[i].ai_canonname)); ai[i].ai_canonname[sizeof(ai[i].ai_canonname)-1] = '\0'; } else { pj_ansi_strcpy(ai[i].ai_canonname, nodecopy); } /* Store address */ PJ_ASSERT_ON_FAIL(res->ai_addrlen <= sizeof(pj_sockaddr), continue); pj_memcpy(&ai[i].ai_addr, res->ai_addr, res->ai_addrlen); PJ_SOCKADDR_RESET_LEN(&ai[i].ai_addr); /* Next slot */ ++i; } *count = i; freeaddrinfo(orig_res); /* Done */ return PJ_SUCCESS; #endif #else /* PJ_SOCK_HAS_GETADDRINFO */ pj_bool_t has_addr = PJ_FALSE; PJ_ASSERT_RETURN(count && *count, PJ_EINVAL); /* Check if nodename is IP address */ pj_bzero(&ai[0], sizeof(ai[0])); if ((af==PJ_AF_INET || af==PJ_AF_UNSPEC) && pj_inet_pton(PJ_AF_INET, nodename, &ai[0].ai_addr.ipv4.sin_addr) == PJ_SUCCESS) { af = PJ_AF_INET; has_addr = PJ_TRUE; } else if ((af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) && pj_inet_pton(PJ_AF_INET6, nodename, &ai[0].ai_addr.ipv6.sin6_addr) == PJ_SUCCESS) { af = PJ_AF_INET6; has_addr = PJ_TRUE; } if (has_addr) { pj_str_t tmp; tmp.ptr = ai[0].ai_canonname; pj_strncpy_with_null(&tmp, nodename, PJ_MAX_HOSTNAME); ai[0].ai_addr.addr.sa_family = (pj_uint16_t)af; *count = 1; return PJ_SUCCESS; } if (af == PJ_AF_INET || af == PJ_AF_UNSPEC) { pj_hostent he; unsigned i, max_count; pj_status_t status; /* VC6 complains that "he" is uninitialized */ #ifdef _MSC_VER pj_bzero(&he, sizeof(he)); #endif status = pj_gethostbyname(nodename, &he); if (status != PJ_SUCCESS) return status; max_count = *count; *count = 0; pj_bzero(ai, max_count * sizeof(pj_addrinfo)); for (i=0; he.h_addr_list[i] && *count * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include "os_symbian.h" #define THIS_FILE "addr_resolv_symbian.cpp" #define TRACE_ME 0 // PJLIB API: resolve hostname PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *name, pj_hostent *he) { static pj_addrinfo ai; static char *aliases[2]; static char *addrlist[2]; unsigned count = 1; pj_status_t status; status = pj_getaddrinfo(PJ_AF_INET, name, &count, &ai); if (status != PJ_SUCCESS) return status; aliases[0] = ai.ai_canonname; aliases[1] = NULL; addrlist[0] = (char*) &ai.ai_addr.ipv4.sin_addr; addrlist[1] = NULL; pj_bzero(he, sizeof(*he)); he->h_name = aliases[0]; he->h_aliases = aliases; he->h_addrtype = PJ_AF_INET; he->h_length = 4; he->h_addr_list = addrlist; return PJ_SUCCESS; } // Resolve for specific address family static pj_status_t getaddrinfo_by_af(int af, const pj_str_t *name, unsigned *count, pj_addrinfo ai[]) { unsigned i; pj_status_t status; PJ_ASSERT_RETURN(name && count && ai, PJ_EINVAL); #if !defined(PJ_HAS_IPV6) || !PJ_HAS_IPV6 if (af == PJ_AF_INET6) return PJ_EIPV6NOTSUP; #endif // Return failure if access point is marked as down by app. PJ_SYMBIAN_CHECK_CONNECTION(); // Get resolver for the specified address family RHostResolver &resv = PjSymbianOS::Instance()->GetResolver(af); // Convert name to Unicode wchar_t name16[PJ_MAX_HOSTNAME]; pj_ansi_to_unicode(name->ptr, name->slen, name16, PJ_ARRAY_SIZE(name16)); TPtrC16 data((const TUint16*)name16); // Resolve! TNameEntry nameEntry; TRequestStatus reqStatus; resv.GetByName(data, nameEntry, reqStatus); User::WaitForRequest(reqStatus); // Iterate each result i = 0; while (reqStatus == KErrNone && i < *count) { // Get the resolved TInetAddr TInetAddr inetAddr(nameEntry().iAddr); int addrlen; #if TRACE_ME if (1) { pj_sockaddr a; char ipaddr[PJ_INET6_ADDRSTRLEN+2]; int namelen; namelen = sizeof(pj_sockaddr); if (PjSymbianOS::Addr2pj(inetAddr, a, &namelen, PJ_FALSE) == PJ_SUCCESS) { PJ_LOG(5,(THIS_FILE, "resolve %.*s: %s", (int)name->slen, name->ptr, pj_sockaddr_print(&a, ipaddr, sizeof(ipaddr), 2))); } } #endif // Ignore if this is not the same address family // Not a good idea, as Symbian mapps IPv4 to IPv6. //fam = inetAddr.Family(); //if (fam != af) { // resv.Next(nameEntry, reqStatus); // User::WaitForRequest(reqStatus); // continue; //} // Convert IP address first to get IPv4 mapped address addrlen = sizeof(ai[i].ai_addr); status = PjSymbianOS::Addr2pj(inetAddr, ai[i].ai_addr, &addrlen, PJ_TRUE); if (status != PJ_SUCCESS) return status; // Ignore if address family doesn't match if (ai[i].ai_addr.addr.sa_family != af) { resv.Next(nameEntry, reqStatus); User::WaitForRequest(reqStatus); continue; } // Convert the official address to ANSI. pj_unicode_to_ansi((const wchar_t*)nameEntry().iName.Ptr(), nameEntry().iName.Length(), ai[i].ai_canonname, sizeof(ai[i].ai_canonname)); // Next ++i; resv.Next(nameEntry, reqStatus); User::WaitForRequest(reqStatus); } *count = i; return PJ_SUCCESS; } /* Resolve IPv4/IPv6 address */ PJ_DEF(pj_status_t) pj_getaddrinfo(int af, const pj_str_t *nodename, unsigned *count, pj_addrinfo ai[]) { unsigned start; pj_status_t status = PJ_EAFNOTSUP; PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6 || af==PJ_AF_UNSPEC, PJ_EAFNOTSUP); PJ_ASSERT_RETURN(nodename && count && *count && ai, PJ_EINVAL); start = 0; if (af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) { unsigned max = *count; status = getaddrinfo_by_af(PJ_AF_INET6, nodename, &max, &ai[start]); if (status == PJ_SUCCESS) { (*count) -= max; start += max; } } if (af==PJ_AF_INET || af==PJ_AF_UNSPEC) { unsigned max = *count; status = getaddrinfo_by_af(PJ_AF_INET, nodename, &max, &ai[start]); if (status == PJ_SUCCESS) { (*count) -= max; start += max; } } *count = start; if (*count) { return PJ_SUCCESS; } else { return status!=PJ_SUCCESS ? status : PJ_ENOTFOUND; } } ================================================ FILE: deps/pjsip/pjlib/src/pj/array.c ================================================ /* $Id: array.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include PJ_DEF(void) pj_array_insert( void *array, unsigned elem_size, unsigned count, unsigned pos, const void *value) { if (count && pos < count) { pj_memmove( (char*)array + (pos+1)*elem_size, (char*)array + pos*elem_size, (count-pos)*elem_size); } pj_memmove((char*)array + pos*elem_size, value, elem_size); } PJ_DEF(void) pj_array_erase( void *array, unsigned elem_size, unsigned count, unsigned pos) { pj_assert(count != 0); if (pos < count-1) { pj_memmove( (char*)array + pos*elem_size, (char*)array + (pos+1)*elem_size, (count-pos-1)*elem_size); } } PJ_DEF(pj_status_t) pj_array_find( const void *array, unsigned elem_size, unsigned count, pj_status_t (*matching)(const void *value), void **result) { unsigned i; const char *char_array = (const char*)array; for (i=0; i .global __longjmp .type __longjmp,%function .align 4 __longjmp: movl 4(%esp), %ecx /* User's jmp_buf in %ecx. */ movl 8(%esp), %eax /* Second argument is return value. */ /* Save the return address now. */ movl (JB_PC*4)(%ecx), %edx /* Restore registers. */ movl (JB_BX*4)(%ecx), %ebx movl (JB_SI*4)(%ecx), %esi movl (JB_DI*4)(%ecx), %edi movl (JB_BP*4)(%ecx), %ebp movl (JB_SP*4)(%ecx), %esp /* Jump to saved PC. */ jmp *%edx .size __longjmp,.-__longjmp ================================================ FILE: deps/pjsip/pjlib/src/pj/compat/setjmp_i386.S ================================================ /* setjmp for i386, ELF version. Copyright (C) 1995, 1996, 1997, 2000, 2001 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #define _ASM #define _SETJMP_H #define PJ_LINUX_KERNEL 1 #include .global __sigsetjmp .type __sigsetjmp,%function .align 4 __sigsetjmp: movl 4 (%esp), %eax /* Save registers. */ movl %ebx, (0 *4)(%eax) movl %esi, (1 *4)(%eax) movl %edi, (2 *4)(%eax) /* Save SP as it will be after we return. */ leal 4(%esp), %ecx movl %ecx, (4 *4)(%eax) /* Save PC we are returning to now. */ movl 0(%esp), %ecx movl %ecx, (5 *4)(%eax) /* Save caller's frame pointer. */ movl %ebp, (3 *4)(%eax) /* Make a tail call to __sigjmp_save; it takes the same args. */ #ifdef __PIC__ /* We cannot use the PLT, because it requires that %ebx be set, but we can't save and restore our caller's value. Instead, we do an indirect jump through the GOT, using for the temporary register %ecx, which is call-clobbered. */ call .Lhere .Lhere: popl %ecx addl $_GLOBAL_OFFSET_TABLE_+[.- .Lhere ], %ecx movl __sigjmp_save @GOT (%ecx), %ecx jmp *%ecx #else jmp __sigjmp_save #endif .size __sigsetjmp,.-__sigsetjmp ================================================ FILE: deps/pjsip/pjlib/src/pj/compat/sigjmp.c ================================================ /* $Id: sigjmp.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include int __sigjmp_save(sigjmp_buf env, int savemask) { return 0; } extern int __sigsetjmp(pj_jmp_buf env, int savemask); extern void __longjmp(pj_jmp_buf env, int val) __attribute__((noreturn)); PJ_DEF(int) pj_setjmp(pj_jmp_buf env) { return __sigsetjmp(env, 0); } PJ_DEF(void) pj_longjmp(pj_jmp_buf env, int val) { __longjmp(env, val); } ================================================ FILE: deps/pjsip/pjlib/src/pj/compat/string.c ================================================ /* $Id: string.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include PJ_DEF(int) strcasecmp(const char *s1, const char *s2) { while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) { if (!*s1++) return 0; ++s2; } return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1; } PJ_DEF(int) strncasecmp(const char *s1, const char *s2, int len) { if (!len) return 0; while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) { if (!*s1++ || --len <= 0) return 0; ++s2; } return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1; } ================================================ FILE: deps/pjsip/pjlib/src/pj/compat/string_compat.c ================================================ /* $Id: string_compat.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H != 0 /* Nothing to do */ #else PJ_DEF(int) strcasecmp(const char *s1, const char *s2) { while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) { if (!*s1++) return 0; ++s2; } return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1; } PJ_DEF(int) strncasecmp(const char *s1, const char *s2, int len) { if (!len) return 0; while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) { if (!*s1++ || --len <= 0) return 0; ++s2; } return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1; } #endif #if defined(PJ_HAS_NO_SNPRINTF) && PJ_HAS_NO_SNPRINTF != 0 PJ_DEF(int) snprintf(char *s1, pj_size_t len, const char *s2, ...) { int ret; va_list arg; PJ_UNUSED_ARG(len); va_start(arg, s2); ret = vsprintf(s1, s2, arg); va_end(arg); return ret; } PJ_DEF(int) vsnprintf(char *s1, pj_size_t len, const char *s2, va_list arg) { #define MARK_CHAR ((char)255) int rc; s1[len-1] = MARK_CHAR; rc = vsprintf(s1,s2,arg); pj_assert(s1[len-1] == MARK_CHAR || s1[len-1] == '\0'); return rc; } #endif ================================================ FILE: deps/pjsip/pjlib/src/pj/config.c ================================================ /* $Id: config.c 4112 2012-04-27 09:47:20Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include static const char *id = "config.c"; #define PJ_MAKE_VERSION3_1(a,b,d) #a "." #b d #define PJ_MAKE_VERSION3_2(a,b,d) PJ_MAKE_VERSION3_1(a,b,d) #define PJ_MAKE_VERSION4_1(a,b,c,d) #a "." #b "." #c d #define PJ_MAKE_VERSION4_2(a,b,c,d) PJ_MAKE_VERSION4_1(a,b,c,d) #if PJ_VERSION_NUM_REV PJ_DEF_DATA(const char*) PJ_VERSION = PJ_MAKE_VERSION4_2(PJ_VERSION_NUM_MAJOR, PJ_VERSION_NUM_MINOR, PJ_VERSION_NUM_REV, PJ_VERSION_NUM_EXTRA); #else PJ_DEF_DATA(const char*) PJ_VERSION = PJ_MAKE_VERSION3_2(PJ_VERSION_NUM_MAJOR, PJ_VERSION_NUM_MINOR, PJ_VERSION_NUM_EXTRA); #endif /* * Get PJLIB version string. */ PJ_DEF(const char*) pj_get_version(void) { return PJ_VERSION; } PJ_DEF(void) pj_dump_config(void) { PJ_LOG(3, (id, "PJLIB (c)2008-2009 Teluu Inc.")); PJ_LOG(3, (id, "Dumping configurations:")); PJ_LOG(3, (id, " PJ_VERSION : %s", PJ_VERSION)); PJ_LOG(3, (id, " PJ_M_NAME : %s", PJ_M_NAME)); PJ_LOG(3, (id, " PJ_HAS_PENTIUM : %d", PJ_HAS_PENTIUM)); PJ_LOG(3, (id, " PJ_OS_NAME : %s", PJ_OS_NAME)); PJ_LOG(3, (id, " PJ_CC_NAME/VER_(1,2,3) : %s-%d.%d.%d", PJ_CC_NAME, PJ_CC_VER_1, PJ_CC_VER_2, PJ_CC_VER_3)); PJ_LOG(3, (id, " PJ_IS_(BIG/LITTLE)_ENDIAN : %s", (PJ_IS_BIG_ENDIAN?"big-endian":"little-endian"))); PJ_LOG(3, (id, " PJ_HAS_INT64 : %d", PJ_HAS_INT64)); PJ_LOG(3, (id, " PJ_HAS_FLOATING_POINT : %d", PJ_HAS_FLOATING_POINT)); PJ_LOG(3, (id, " PJ_DEBUG : %d", PJ_DEBUG)); PJ_LOG(3, (id, " PJ_FUNCTIONS_ARE_INLINED : %d", PJ_FUNCTIONS_ARE_INLINED)); PJ_LOG(3, (id, " PJ_LOG_MAX_LEVEL : %d", PJ_LOG_MAX_LEVEL)); PJ_LOG(3, (id, " PJ_LOG_MAX_SIZE : %d", PJ_LOG_MAX_SIZE)); PJ_LOG(3, (id, " PJ_LOG_USE_STACK_BUFFER : %d", PJ_LOG_USE_STACK_BUFFER)); PJ_LOG(3, (id, " PJ_POOL_DEBUG : %d", PJ_POOL_DEBUG)); PJ_LOG(3, (id, " PJ_HAS_POOL_ALT_API : %d", PJ_HAS_POOL_ALT_API)); PJ_LOG(3, (id, " PJ_HAS_TCP : %d", PJ_HAS_TCP)); PJ_LOG(3, (id, " PJ_MAX_HOSTNAME : %d", PJ_MAX_HOSTNAME)); PJ_LOG(3, (id, " ioqueue type : %s", pj_ioqueue_name())); PJ_LOG(3, (id, " PJ_IOQUEUE_MAX_HANDLES : %d", PJ_IOQUEUE_MAX_HANDLES)); PJ_LOG(3, (id, " PJ_IOQUEUE_HAS_SAFE_UNREG : %d", PJ_IOQUEUE_HAS_SAFE_UNREG)); PJ_LOG(3, (id, " PJ_HAS_THREADS : %d", PJ_HAS_THREADS)); PJ_LOG(3, (id, " PJ_LOG_USE_STACK_BUFFER : %d", PJ_LOG_USE_STACK_BUFFER)); PJ_LOG(3, (id, " PJ_HAS_SEMAPHORE : %d", PJ_HAS_SEMAPHORE)); PJ_LOG(3, (id, " PJ_HAS_EVENT_OBJ : %d", PJ_HAS_EVENT_OBJ)); PJ_LOG(3, (id, " PJ_ENABLE_EXTRA_CHECK : %d", PJ_ENABLE_EXTRA_CHECK)); PJ_LOG(3, (id, " PJ_HAS_EXCEPTION_NAMES : %d", PJ_HAS_EXCEPTION_NAMES)); PJ_LOG(3, (id, " PJ_MAX_EXCEPTION_ID : %d", PJ_MAX_EXCEPTION_ID)); PJ_LOG(3, (id, " PJ_EXCEPTION_USE_WIN32_SEH: %d", PJ_EXCEPTION_USE_WIN32_SEH)); PJ_LOG(3, (id, " PJ_TIMESTAMP_USE_RDTSC: : %d", PJ_TIMESTAMP_USE_RDTSC)); PJ_LOG(3, (id, " PJ_OS_HAS_CHECK_STACK : %d", PJ_OS_HAS_CHECK_STACK)); PJ_LOG(3, (id, " PJ_HAS_HIGH_RES_TIMER : %d", PJ_HAS_HIGH_RES_TIMER)); } ================================================ FILE: deps/pjsip/pjlib/src/pj/ctype.c ================================================ /* $Id: ctype.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include /* char pj_hex_digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; */ int pjlib_ctype_c_dummy_symbol; ================================================ FILE: deps/pjsip/pjlib/src/pj/errno.c ================================================ /* $Id: errno.c 4461 2013-04-05 03:02:19Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include /* Prototype for platform specific error message, which will be defined * in separate file. */ PJ_BEGIN_DECL PJ_DECL(int) platform_strerror(pj_os_err_type code, char *buf, pj_size_t bufsize ); PJ_END_DECL #ifndef PJLIB_MAX_ERR_MSG_HANDLER # define PJLIB_MAX_ERR_MSG_HANDLER 10 #endif /* Error message handler. */ static unsigned err_msg_hnd_cnt; static struct err_msg_hnd { pj_status_t begin; pj_status_t end; pj_str_t (*strerror)(pj_status_t, char*, pj_size_t); } err_msg_hnd[PJLIB_MAX_ERR_MSG_HANDLER]; /* PJLIB's own error codes/messages */ #if defined(PJ_HAS_ERROR_STRING) && PJ_HAS_ERROR_STRING!=0 static const struct { int code; const char *msg; } err_str[] = { PJ_BUILD_ERR(PJ_EUNKNOWN, "Unknown Error" ), PJ_BUILD_ERR(PJ_EPENDING, "Pending operation" ), PJ_BUILD_ERR(PJ_ETOOMANYCONN, "Too many connecting sockets" ), PJ_BUILD_ERR(PJ_EINVAL, "Invalid value or argument" ), PJ_BUILD_ERR(PJ_ENAMETOOLONG, "Name too long" ), PJ_BUILD_ERR(PJ_ENOTFOUND, "Not found" ), PJ_BUILD_ERR(PJ_ENOMEM, "Not enough memory" ), PJ_BUILD_ERR(PJ_EBUG, "BUG DETECTED!" ), PJ_BUILD_ERR(PJ_ETIMEDOUT, "Operation timed out" ), PJ_BUILD_ERR(PJ_ETOOMANY, "Too many objects of the specified type"), PJ_BUILD_ERR(PJ_EBUSY, "Object is busy"), PJ_BUILD_ERR(PJ_ENOTSUP, "Option/operation is not supported"), PJ_BUILD_ERR(PJ_EINVALIDOP, "Invalid operation"), PJ_BUILD_ERR(PJ_ECANCELLED, "Operation cancelled"), PJ_BUILD_ERR(PJ_EEXISTS, "Object already exists" ), PJ_BUILD_ERR(PJ_EEOF, "End of file" ), PJ_BUILD_ERR(PJ_ETOOBIG, "Size is too big"), PJ_BUILD_ERR(PJ_ERESOLVE, "gethostbyname() has returned error"), PJ_BUILD_ERR(PJ_ETOOSMALL, "Size is too short"), PJ_BUILD_ERR(PJ_EIGNORED, "Ignored"), PJ_BUILD_ERR(PJ_EIPV6NOTSUP, "IPv6 is not supported"), PJ_BUILD_ERR(PJ_EAFNOTSUP, "Unsupported address family"), PJ_BUILD_ERR(PJ_EGONE, "Object no longer exists"), PJ_BUILD_ERR(PJ_ESOCKETSTOP, "Socket is in bad state") }; #endif /* PJ_HAS_ERROR_STRING */ /* * pjlib_error() * * Retrieve message string for PJLIB's own error code. */ static int pjlib_error(pj_status_t code, char *buf, pj_size_t size) { int len; #if defined(PJ_HAS_ERROR_STRING) && PJ_HAS_ERROR_STRING!=0 unsigned i; for (i=0; i= size) len2 = size-1; pj_memcpy(buf, err_str[i].msg, len2); buf[len2] = '\0'; return (int)len2; } } #endif len = pj_ansi_snprintf( buf, size, "Unknown pjlib error %d", code); if (len < 1 || len >= (int)size) len = (int)(size - 1); return len; } #define IN_RANGE(val,start,end) ((val)>=(start) && (val)<(end)) /* Register strerror handle. */ PJ_DEF(pj_status_t) pj_register_strerror( pj_status_t start, pj_status_t space, pj_error_callback f) { unsigned i; /* Check arguments. */ PJ_ASSERT_RETURN(start && space && f, PJ_EINVAL); /* Check if there aren't too many handlers registered. */ PJ_ASSERT_RETURN(err_msg_hnd_cnt < PJ_ARRAY_SIZE(err_msg_hnd), PJ_ETOOMANY); /* Start error must be greater than PJ_ERRNO_START_USER */ PJ_ASSERT_RETURN(start >= PJ_ERRNO_START_USER, PJ_EEXISTS); /* Check that no existing handler has covered the specified range. */ for (i=0; i= (int)bufsize) { len = (int)(bufsize - 1); buf[len] = '\0'; } errstr.ptr = buf; errstr.slen = len; return errstr; } #if PJ_LOG_MAX_LEVEL >= 1 static void invoke_log(const char *sender, int level, const char *format, ...) { va_list arg; va_start(arg, format); pj_log(sender, level, format, arg); va_end(arg); } static void pj_perror_imp(int log_level, const char *sender, pj_status_t status, const char *title_fmt, va_list marker) { char titlebuf[PJ_PERROR_TITLE_BUF_SIZE]; char errmsg[PJ_ERR_MSG_SIZE]; int len; /* Build the title */ len = pj_ansi_vsnprintf(titlebuf, sizeof(titlebuf), title_fmt, marker); if (len < 0 || len >= (int)sizeof(titlebuf)) pj_ansi_strcpy(titlebuf, "Error"); /* Get the error */ pj_strerror(status, errmsg, sizeof(errmsg)); /* Send to log */ invoke_log(sender, log_level, "%s: %s", titlebuf, errmsg); } PJ_DEF(void) pj_perror(int log_level, const char *sender, pj_status_t status, const char *title_fmt, ...) { va_list marker; va_start(marker, title_fmt); pj_perror_imp(log_level, sender, status, title_fmt, marker); va_end(marker); } PJ_DEF(void) pj_perror_1(const char *sender, pj_status_t status, const char *title_fmt, ...) { va_list marker; va_start(marker, title_fmt); pj_perror_imp(1, sender, status, title_fmt, marker); va_end(marker); } #else /* #if PJ_LOG_MAX_LEVEL >= 1 */ PJ_DEF(void) pj_perror(int log_level, const char *sender, pj_status_t status, const char *title_fmt, ...) { } #endif /* #if PJ_LOG_MAX_LEVEL >= 1 */ #if PJ_LOG_MAX_LEVEL >= 2 PJ_DEF(void) pj_perror_2(const char *sender, pj_status_t status, const char *title_fmt, ...) { va_list marker; va_start(marker, title_fmt); pj_perror_imp(2, sender, status, title_fmt, marker); va_end(marker); } #endif #if PJ_LOG_MAX_LEVEL >= 3 PJ_DEF(void) pj_perror_3(const char *sender, pj_status_t status, const char *title_fmt, ...) { va_list marker; va_start(marker, title_fmt); pj_perror_imp(3, sender, status, title_fmt, marker); va_end(marker); } #endif #if PJ_LOG_MAX_LEVEL >= 4 PJ_DEF(void) pj_perror_4(const char *sender, pj_status_t status, const char *title_fmt, ...) { va_list marker; va_start(marker, title_fmt); pj_perror_imp(4, sender, status, title_fmt, marker); va_end(marker); } #endif #if PJ_LOG_MAX_LEVEL >= 5 PJ_DEF(void) pj_perror_5(const char *sender, pj_status_t status, const char *title_fmt, ...) { va_list marker; va_start(marker, title_fmt); pj_perror_imp(5, sender, status, title_fmt, marker); va_end(marker); } #endif #if PJ_LOG_MAX_LEVEL >= 6 PJ_DEF(void) pj_perror_6(const char *sender, pj_status_t status, const char *title_fmt, ...) { va_list marker; va_start(marker, title_fmt); pj_perror_imp(6, sender, status, title_fmt, marker); va_end(marker); } #endif ================================================ FILE: deps/pjsip/pjlib/src/pj/except.c ================================================ /* $Id: except.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include static long thread_local_id = -1; #if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 static const char *exception_id_names[PJ_MAX_EXCEPTION_ID]; #else /* * Start from 1 (not 0)!!! * Exception 0 is reserved for normal path of setjmp()!!! */ static int last_exception_id = 1; #endif /* PJ_HAS_EXCEPTION_NAMES */ #if !defined(PJ_EXCEPTION_USE_WIN32_SEH) || PJ_EXCEPTION_USE_WIN32_SEH==0 PJ_DEF(void) pj_throw_exception_(int exception_id) { struct pj_exception_state_t *handler; handler = (struct pj_exception_state_t*) pj_thread_local_get(thread_local_id); if (handler == NULL) { PJ_LOG(1,("except.c", "!!!FATAL: unhandled exception %s!\n", pj_exception_id_name(exception_id))); pj_assert(handler != NULL); /* This will crash the system! */ } pj_pop_exception_handler_(handler); pj_longjmp(handler->state, exception_id); } static void exception_cleanup(void) { if (thread_local_id != -1) { pj_thread_local_free(thread_local_id); thread_local_id = -1; } #if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 { unsigned i; for (i=0; iprev = parent_handler; pj_thread_local_set(thread_local_id, rec); } PJ_DEF(void) pj_pop_exception_handler_(struct pj_exception_state_t *rec) { struct pj_exception_state_t *handler; handler = (struct pj_exception_state_t *) pj_thread_local_get(thread_local_id); if (handler && handler==rec) { pj_thread_local_set(thread_local_id, handler->prev); } } #endif #if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name, pj_exception_id_t *id) { unsigned i; pj_enter_critical_section(); /* * Start from 1 (not 0)!!! * Exception 0 is reserved for normal path of setjmp()!!! */ for (i=1; i0 && id0 && id"); if (exception_id_names[id] == NULL) { pj_ansi_snprintf(unknown_name, sizeof(unknown_name), "exception %d", id); return unknown_name; } return exception_id_names[id]; } #else /* PJ_HAS_EXCEPTION_NAMES */ PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name, pj_exception_id_t *id) { PJ_ASSERT_RETURN(last_exception_id < PJ_MAX_EXCEPTION_ID-1, PJ_ETOOMANY); *id = last_exception_id++; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id ) { return PJ_SUCCESS; } PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id) { return ""; } #endif /* PJ_HAS_EXCEPTION_NAMES */ ================================================ FILE: deps/pjsip/pjlib/src/pj/exception_symbian.cpp ================================================ /* $Id: exception_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 static const char *exception_id_names[PJ_MAX_EXCEPTION_ID]; #else /* * Start from 1 (not 0)!!! * Exception 0 is reserved for normal path of setjmp()!!! */ static int last_exception_id = 1; #endif /* PJ_HAS_EXCEPTION_NAMES */ #if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name, pj_exception_id_t *id) { unsigned i; pj_enter_critical_section(); /* * Start from 1 (not 0)!!! * Exception 0 is reserved for normal path of setjmp()!!! */ for (i=1; i0 && id0 && id"); if (exception_id_names[id] == NULL) return ""; return exception_id_names[id]; } #else /* PJ_HAS_EXCEPTION_NAMES */ PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name, pj_exception_id_t *id) { PJ_ASSERT_RETURN(last_exception_id < PJ_MAX_EXCEPTION_ID-1, PJ_ETOOMANY); *id = last_exception_id++; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id ) { return PJ_SUCCESS; } PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id) { return ""; } #endif /* PJ_HAS_EXCEPTION_NAMES */ ================================================ FILE: deps/pjsip/pjlib/src/pj/extra-exports.c ================================================ /* $Id: extra-exports.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include EXPORT_SYMBOL(sys_select); EXPORT_SYMBOL(sys_epoll_create); EXPORT_SYMBOL(sys_epoll_ctl); EXPORT_SYMBOL(sys_epoll_wait); EXPORT_SYMBOL(sys_socket); EXPORT_SYMBOL(sys_bind); EXPORT_SYMBOL(sys_getpeername); EXPORT_SYMBOL(sys_getsockname); EXPORT_SYMBOL(sys_sendto); EXPORT_SYMBOL(sys_recvfrom); EXPORT_SYMBOL(sys_getsockopt); EXPORT_SYMBOL(sys_setsockopt); EXPORT_SYMBOL(sys_listen); EXPORT_SYMBOL(sys_shutdown); EXPORT_SYMBOL(sys_connect); EXPORT_SYMBOL(sys_accept); ================================================ FILE: deps/pjsip/pjlib/src/pj/fifobuf.c ================================================ /* $Id: fifobuf.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #define THIS_FILE "fifobuf" #define SZ sizeof(unsigned) PJ_DEF(void) pj_fifobuf_init (pj_fifobuf_t *fifobuf, void *buffer, unsigned size) { PJ_CHECK_STACK(); PJ_LOG(6, (THIS_FILE, "fifobuf_init fifobuf=%p buffer=%p, size=%d", fifobuf, buffer, size)); fifobuf->first = (char*)buffer; fifobuf->last = fifobuf->first + size; fifobuf->ubegin = fifobuf->uend = fifobuf->first; fifobuf->full = 0; } PJ_DEF(unsigned) pj_fifobuf_max_size (pj_fifobuf_t *fifobuf) { unsigned s1, s2; PJ_CHECK_STACK(); if (fifobuf->uend >= fifobuf->ubegin) { s1 = (unsigned)(fifobuf->last - fifobuf->uend); s2 = (unsigned)(fifobuf->ubegin - fifobuf->first); } else { s1 = s2 = (unsigned)(fifobuf->ubegin - fifobuf->uend); } return s1full) { PJ_LOG(6, (THIS_FILE, "fifobuf_alloc fifobuf=%p, size=%d: full!", fifobuf, size)); return NULL; } /* try to allocate from the end part of the fifo */ if (fifobuf->uend >= fifobuf->ubegin) { available = (unsigned)(fifobuf->last - fifobuf->uend); if (available >= size+SZ) { char *ptr = fifobuf->uend; fifobuf->uend += (size+SZ); if (fifobuf->uend == fifobuf->last) fifobuf->uend = fifobuf->first; if (fifobuf->uend == fifobuf->ubegin) fifobuf->full = 1; *(unsigned*)ptr = size+SZ; ptr += SZ; PJ_LOG(6, (THIS_FILE, "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend)); return ptr; } } /* try to allocate from the start part of the fifo */ start = (fifobuf->uend <= fifobuf->ubegin) ? fifobuf->uend : fifobuf->first; available = (unsigned)(fifobuf->ubegin - start); if (available >= size+SZ) { char *ptr = start; fifobuf->uend = start + size + SZ; if (fifobuf->uend == fifobuf->ubegin) fifobuf->full = 1; *(unsigned*)ptr = size+SZ; ptr += SZ; PJ_LOG(6, (THIS_FILE, "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend)); return ptr; } PJ_LOG(6, (THIS_FILE, "fifobuf_alloc fifobuf=%p, size=%d: no space left! p1=%p, p2=%p", fifobuf, size, fifobuf->ubegin, fifobuf->uend)); return NULL; } PJ_DEF(pj_status_t) pj_fifobuf_unalloc (pj_fifobuf_t *fifobuf, void *buf) { char *ptr = (char*)buf; char *endptr; unsigned sz; PJ_CHECK_STACK(); ptr -= SZ; sz = *(unsigned*)ptr; endptr = fifobuf->uend; if (endptr == fifobuf->first) endptr = fifobuf->last; if (ptr+sz != endptr) { pj_assert(!"Invalid pointer to undo alloc"); return -1; } fifobuf->uend = ptr; fifobuf->full = 0; PJ_LOG(6, (THIS_FILE, "fifobuf_unalloc fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p", fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend)); return 0; } PJ_DEF(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fifobuf, void *buf) { char *ptr = (char*)buf; char *end; unsigned sz; PJ_CHECK_STACK(); ptr -= SZ; if (ptr < fifobuf->first || ptr >= fifobuf->last) { pj_assert(!"Invalid pointer to free"); return -1; } if (ptr != fifobuf->ubegin && ptr != fifobuf->first) { pj_assert(!"Invalid free() sequence!"); return -1; } end = (fifobuf->uend > fifobuf->ubegin) ? fifobuf->uend : fifobuf->last; sz = *(unsigned*)ptr; if (ptr+sz > end) { pj_assert(!"Invalid size!"); return -1; } fifobuf->ubegin = ptr + sz; /* Rollover */ if (fifobuf->ubegin == fifobuf->last) fifobuf->ubegin = fifobuf->first; /* Reset if fifobuf is empty */ if (fifobuf->ubegin == fifobuf->uend) fifobuf->ubegin = fifobuf->uend = fifobuf->first; fifobuf->full = 0; PJ_LOG(6, (THIS_FILE, "fifobuf_free fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p", fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend)); return 0; } ================================================ FILE: deps/pjsip/pjlib/src/pj/file_access_unistd.c ================================================ /* $Id: file_access_unistd.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include /* rename() */ #include /* * pj_file_exists() */ PJ_DEF(pj_bool_t) pj_file_exists(const char *filename) { struct stat buf; PJ_ASSERT_RETURN(filename, 0); if (stat(filename, &buf) != 0) return 0; return PJ_TRUE; } /* * pj_file_size() */ PJ_DEF(pj_off_t) pj_file_size(const char *filename) { struct stat buf; PJ_ASSERT_RETURN(filename, -1); if (stat(filename, &buf) != 0) return -1; return buf.st_size; } /* * pj_file_delete() */ PJ_DEF(pj_status_t) pj_file_delete(const char *filename) { PJ_ASSERT_RETURN(filename, PJ_EINVAL); if (unlink(filename)!=0) { return PJ_RETURN_OS_ERROR(errno); } return PJ_SUCCESS; } /* * pj_file_move() */ PJ_DEF(pj_status_t) pj_file_move( const char *oldname, const char *newname) { PJ_ASSERT_RETURN(oldname && newname, PJ_EINVAL); if (rename(oldname, newname) != 0) { return PJ_RETURN_OS_ERROR(errno); } return PJ_SUCCESS; } /* * pj_file_getstat() */ PJ_DEF(pj_status_t) pj_file_getstat(const char *filename, pj_file_stat *statbuf) { struct stat buf; PJ_ASSERT_RETURN(filename && statbuf, PJ_EINVAL); if (stat(filename, &buf) != 0) { return PJ_RETURN_OS_ERROR(errno); } statbuf->size = buf.st_size; statbuf->ctime.sec = buf.st_ctime; statbuf->ctime.msec = 0; statbuf->mtime.sec = buf.st_mtime; statbuf->mtime.msec = 0; statbuf->atime.sec = buf.st_atime; statbuf->atime.msec = 0; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib/src/pj/file_access_win32.c ================================================ /* $Id: file_access_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 /* WinCE lacks READ_CONTROL so we must use GENERIC_READ */ # define CONTROL_ACCESS GENERIC_READ #else # define CONTROL_ACCESS READ_CONTROL #endif /* * pj_file_exists() */ PJ_DEF(pj_bool_t) pj_file_exists(const char *filename) { PJ_DECL_UNICODE_TEMP_BUF(wfilename,256) HANDLE hFile; PJ_ASSERT_RETURN(filename != NULL, 0); hFile = CreateFile(PJ_STRING_TO_NATIVE(filename,wfilename,sizeof(wfilename)), CONTROL_ACCESS, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return 0; CloseHandle(hFile); return PJ_TRUE; } /* * pj_file_size() */ PJ_DEF(pj_off_t) pj_file_size(const char *filename) { PJ_DECL_UNICODE_TEMP_BUF(wfilename,256) HANDLE hFile; DWORD sizeLo, sizeHi; pj_off_t size; PJ_ASSERT_RETURN(filename != NULL, -1); hFile = CreateFile(PJ_STRING_TO_NATIVE(filename, wfilename,sizeof(wfilename)), CONTROL_ACCESS, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return -1; sizeLo = GetFileSize(hFile, &sizeHi); if (sizeLo == INVALID_FILE_SIZE) { DWORD dwStatus = GetLastError(); if (dwStatus != NO_ERROR) { CloseHandle(hFile); return -1; } } size = sizeHi; size = (size << 32) + sizeLo; CloseHandle(hFile); return size; } /* * pj_file_delete() */ PJ_DEF(pj_status_t) pj_file_delete(const char *filename) { PJ_DECL_UNICODE_TEMP_BUF(wfilename,256) PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL); if (DeleteFile(PJ_STRING_TO_NATIVE(filename,wfilename,sizeof(wfilename))) == FALSE) return PJ_RETURN_OS_ERROR(GetLastError()); return PJ_SUCCESS; } /* * pj_file_move() */ PJ_DEF(pj_status_t) pj_file_move( const char *oldname, const char *newname) { PJ_DECL_UNICODE_TEMP_BUF(woldname,256) PJ_DECL_UNICODE_TEMP_BUF(wnewname,256) BOOL rc; PJ_ASSERT_RETURN(oldname!=NULL && newname!=NULL, PJ_EINVAL); #if PJ_WIN32_WINNT >= 0x0400 rc = MoveFileEx(PJ_STRING_TO_NATIVE(oldname,woldname,sizeof(woldname)), PJ_STRING_TO_NATIVE(newname,wnewname,sizeof(wnewname)), MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING); #else rc = MoveFile(PJ_STRING_TO_NATIVE(oldname,woldname,sizeof(woldname)), PJ_STRING_TO_NATIVE(newname,wnewname,sizeof(wnewname))); #endif if (!rc) return PJ_RETURN_OS_ERROR(GetLastError()); return PJ_SUCCESS; } static pj_status_t file_time_to_time_val(const FILETIME *file_time, pj_time_val *time_val) { FILETIME local_file_time; SYSTEMTIME localTime; pj_parsed_time pt; if (!FileTimeToLocalFileTime(file_time, &local_file_time)) return PJ_RETURN_OS_ERROR(GetLastError()); if (!FileTimeToSystemTime(file_time, &localTime)) return PJ_RETURN_OS_ERROR(GetLastError()); //if (!SystemTimeToTzSpecificLocalTime(NULL, &systemTime, &localTime)) // return PJ_RETURN_OS_ERROR(GetLastError()); pj_bzero(&pt, sizeof(pt)); pt.year = localTime.wYear; pt.mon = localTime.wMonth-1; pt.day = localTime.wDay; pt.wday = localTime.wDayOfWeek; pt.hour = localTime.wHour; pt.min = localTime.wMinute; pt.sec = localTime.wSecond; pt.msec = localTime.wMilliseconds; return pj_time_encode(&pt, time_val); } /* * pj_file_getstat() */ PJ_DEF(pj_status_t) pj_file_getstat(const char *filename, pj_file_stat *stat) { PJ_DECL_UNICODE_TEMP_BUF(wfilename,256) HANDLE hFile; DWORD sizeLo, sizeHi; FILETIME creationTime, accessTime, writeTime; PJ_ASSERT_RETURN(filename!=NULL && stat!=NULL, PJ_EINVAL); hFile = CreateFile(PJ_STRING_TO_NATIVE(filename,wfilename,sizeof(wfilename)), CONTROL_ACCESS, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return PJ_RETURN_OS_ERROR(GetLastError()); sizeLo = GetFileSize(hFile, &sizeHi); if (sizeLo == INVALID_FILE_SIZE) { DWORD dwStatus = GetLastError(); if (dwStatus != NO_ERROR) { CloseHandle(hFile); return PJ_RETURN_OS_ERROR(dwStatus); } } stat->size = sizeHi; stat->size = (stat->size << 32) + sizeLo; if (GetFileTime(hFile, &creationTime, &accessTime, &writeTime)==FALSE) { DWORD dwStatus = GetLastError(); CloseHandle(hFile); return PJ_RETURN_OS_ERROR(dwStatus); } CloseHandle(hFile); if (file_time_to_time_val(&creationTime, &stat->ctime) != PJ_SUCCESS) return PJ_RETURN_OS_ERROR(GetLastError()); file_time_to_time_val(&accessTime, &stat->atime); file_time_to_time_val(&writeTime, &stat->mtime); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib/src/pj/file_io_ansi.c ================================================ /* $Id: file_io_ansi.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool, const char *pathname, unsigned flags, pj_oshandle_t *fd) { char mode[8]; char *p = mode; PJ_ASSERT_RETURN(pathname && fd, PJ_EINVAL); PJ_UNUSED_ARG(pool); if ((flags & PJ_O_APPEND) == PJ_O_APPEND) { if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) { *p++ = 'a'; if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) *p++ = '+'; } else { /* This is invalid. * Can not specify PJ_O_RDONLY with PJ_O_APPEND! */ } } else { if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) { *p++ = 'r'; if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) *p++ = '+'; } else { *p++ = 'w'; } } if (p==mode) return PJ_EINVAL; *p++ = 'b'; *p++ = '\0'; *fd = fopen(pathname, mode); if (*fd == NULL) return PJ_RETURN_OS_ERROR(errno); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd) { PJ_ASSERT_RETURN(fd, PJ_EINVAL); if (fclose((FILE*)fd) != 0) return PJ_RETURN_OS_ERROR(errno); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd, const void *data, pj_ssize_t *size) { size_t written; clearerr((FILE*)fd); written = fwrite(data, 1, *size, (FILE*)fd); if (ferror((FILE*)fd)) { *size = -1; return PJ_RETURN_OS_ERROR(errno); } *size = written; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd, void *data, pj_ssize_t *size) { size_t bytes; clearerr((FILE*)fd); bytes = fread(data, 1, *size, (FILE*)fd); if (ferror((FILE*)fd)) { *size = -1; return PJ_RETURN_OS_ERROR(errno); } *size = bytes; return PJ_SUCCESS; } /* PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access) { PJ_UNUSED_ARG(access); return feof((FILE*)fd) ? PJ_TRUE : 0; } */ PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd, pj_off_t offset, enum pj_file_seek_type whence) { int mode; switch (whence) { case PJ_SEEK_SET: mode = SEEK_SET; break; case PJ_SEEK_CUR: mode = SEEK_CUR; break; case PJ_SEEK_END: mode = SEEK_END; break; default: pj_assert(!"Invalid whence in file_setpos"); return PJ_EINVAL; } if (fseek((FILE*)fd, (long)offset, mode) != 0) return PJ_RETURN_OS_ERROR(errno); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd, pj_off_t *pos) { long offset; offset = ftell((FILE*)fd); if (offset == -1) { *pos = -1; return PJ_RETURN_OS_ERROR(errno); } *pos = offset; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_file_flush(pj_oshandle_t fd) { int rc; rc = fflush((FILE*)fd); if (rc == EOF) { return PJ_RETURN_OS_ERROR(errno); } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib/src/pj/file_io_win32.c ================================================ /* $Id: file_io_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #ifndef INVALID_SET_FILE_POINTER # define INVALID_SET_FILE_POINTER ((DWORD)-1) #endif /** * Check for end-of-file condition on the specified descriptor. * * @param fd The file descriptor. * @param access The desired access. * * @return Non-zero if file is EOF. */ PJ_DECL(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access); PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool, const char *pathname, unsigned flags, pj_oshandle_t *fd) { PJ_DECL_UNICODE_TEMP_BUF(wpathname,256) HANDLE hFile; DWORD dwDesiredAccess = 0; DWORD dwShareMode = 0; DWORD dwCreationDisposition = 0; DWORD dwFlagsAndAttributes = 0; PJ_UNUSED_ARG(pool); PJ_ASSERT_RETURN(pathname!=NULL, PJ_EINVAL); if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) { dwDesiredAccess |= GENERIC_WRITE; if ((flags & PJ_O_APPEND) == PJ_O_APPEND) { #if !defined(PJ_WIN32_WINCE) || !PJ_WIN32_WINCE /* FILE_APPEND_DATA is invalid on WM2003 and WM5, but it seems * to be working on WM6. All are tested on emulator though. * Removing this also seem to work (i.e. data is appended), so * I guess this flag is "optional". * See http://trac.pjsip.org/repos/ticket/825 */ dwDesiredAccess |= FILE_APPEND_DATA; #endif dwCreationDisposition |= OPEN_ALWAYS; } else { dwDesiredAccess &= ~(FILE_APPEND_DATA); dwCreationDisposition |= CREATE_ALWAYS; } } if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) { dwDesiredAccess |= GENERIC_READ; if (flags == PJ_O_RDONLY) dwCreationDisposition |= OPEN_EXISTING; } if (dwDesiredAccess == 0) { pj_assert(!"Invalid file open flags"); return PJ_EINVAL; } dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; hFile = CreateFile(PJ_STRING_TO_NATIVE(pathname,wpathname,sizeof(wpathname)), dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); if (hFile == INVALID_HANDLE_VALUE) { *fd = 0; return PJ_RETURN_OS_ERROR(GetLastError()); } if ((flags & PJ_O_APPEND) == PJ_O_APPEND) { pj_status_t status; status = pj_file_setpos(hFile, 0, PJ_SEEK_END); if (status != PJ_SUCCESS) { pj_file_close(hFile); return status; } } *fd = hFile; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd) { if (CloseHandle(fd)==0) return PJ_RETURN_OS_ERROR(GetLastError()); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd, const void *data, pj_ssize_t *size) { BOOL rc; DWORD bytesWritten; rc = WriteFile(fd, data, (DWORD)*size, &bytesWritten, NULL); if (!rc) { *size = -1; return PJ_RETURN_OS_ERROR(GetLastError()); } *size = bytesWritten; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd, void *data, pj_ssize_t *size) { BOOL rc; DWORD bytesRead; rc = ReadFile(fd, data, (DWORD)*size, &bytesRead, NULL); if (!rc) { *size = -1; return PJ_RETURN_OS_ERROR(GetLastError()); } *size = bytesRead; return PJ_SUCCESS; } /* PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access) { BOOL rc; DWORD dummy = 0, bytes; DWORD dwStatus; if ((access & PJ_O_RDONLY) == PJ_O_RDONLY) { rc = ReadFile(fd, &dummy, 0, &bytes, NULL); } else if ((access & PJ_O_WRONLY) == PJ_O_WRONLY) { rc = WriteFile(fd, &dummy, 0, &bytes, NULL); } else { pj_assert(!"Invalid access"); return PJ_TRUE; } dwStatus = GetLastError(); if (dwStatus==ERROR_HANDLE_EOF) return PJ_TRUE; return 0; } */ PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd, pj_off_t offset, enum pj_file_seek_type whence) { DWORD dwMoveMethod; DWORD dwNewPos; LONG hi32; if (whence == PJ_SEEK_SET) dwMoveMethod = FILE_BEGIN; else if (whence == PJ_SEEK_CUR) dwMoveMethod = FILE_CURRENT; else if (whence == PJ_SEEK_END) dwMoveMethod = FILE_END; else { pj_assert(!"Invalid whence in file_setpos"); return PJ_EINVAL; } hi32 = (LONG)(offset >> 32); dwNewPos = SetFilePointer(fd, (long)offset, &hi32, dwMoveMethod); if (dwNewPos == (DWORD)INVALID_SET_FILE_POINTER) { DWORD dwStatus = GetLastError(); if (dwStatus != 0) return PJ_RETURN_OS_ERROR(dwStatus); /* dwNewPos actually is not an error. */ } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd, pj_off_t *pos) { LONG hi32 = 0; DWORD lo32; lo32 = SetFilePointer(fd, 0, &hi32, FILE_CURRENT); if (lo32 == (DWORD)INVALID_SET_FILE_POINTER) { DWORD dwStatus = GetLastError(); if (dwStatus != 0) return PJ_RETURN_OS_ERROR(dwStatus); } *pos = hi32; *pos = (*pos << 32) + lo32; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_file_flush(pj_oshandle_t fd) { BOOL rc; rc = FlushFileBuffers(fd); if (!rc) { DWORD dwStatus = GetLastError(); if (dwStatus != 0) return PJ_RETURN_OS_ERROR(dwStatus); } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib/src/pj/guid.c ================================================ /* $Id: guid.c 4208 2012-07-18 07:52:33Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include PJ_DEF(pj_str_t*) pj_generate_unique_string_lower(pj_str_t *str) { int i; pj_generate_unique_string(str); for (i = 0; i < str->slen; i++) str->ptr[i] = (char)pj_tolower(str->ptr[i]); return str; } PJ_DEF(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str) { str->ptr = (char*)pj_pool_alloc(pool, PJ_GUID_STRING_LENGTH); pj_generate_unique_string(str); } PJ_DEF(void) pj_create_unique_string_lower(pj_pool_t *pool, pj_str_t *str) { int i; pj_create_unique_string(pool, str); for (i = 0; i < str->slen; i++) str->ptr[i] = (char)pj_tolower(str->ptr[i]); } ================================================ FILE: deps/pjsip/pjlib/src/pj/guid_simple.c ================================================ /* $Id: guid_simple.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include PJ_DEF_DATA(const unsigned) PJ_GUID_STRING_LENGTH=32; static char guid_chars[64]; PJ_DEF(unsigned) pj_GUID_STRING_LENGTH() { return PJ_GUID_STRING_LENGTH; } static void init_guid_chars(void) { char *p = guid_chars; unsigned i; for (i=0; i<10; ++i) *p++ = '0'+i; for (i=0; i<26; ++i) { *p++ = 'a'+i; *p++ = 'A'+i; } *p++ = '-'; *p++ = '.'; } PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str) { char *p, *end; PJ_CHECK_STACK(); if (guid_chars[0] == '\0') { pj_enter_critical_section(); if (guid_chars[0] == '\0') { init_guid_chars(); } pj_leave_critical_section(); } /* This would only work if PJ_GUID_STRING_LENGTH is multiple of 2 bytes */ pj_assert(PJ_GUID_STRING_LENGTH % 2 == 0); for (p=str->ptr, end=p+PJ_GUID_STRING_LENGTH; p0 && p>=8, rand_val>>=8, p++) { *p = guid_chars[(rand_val & 0xFF) & 63]; } } str->slen = PJ_GUID_STRING_LENGTH; return str; } ================================================ FILE: deps/pjsip/pjlib/src/pj/guid_uuid.c ================================================ /* $Id: guid_uuid.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include PJ_DEF_DATA(const unsigned) PJ_GUID_STRING_LENGTH=36; PJ_DEF(unsigned) pj_GUID_STRING_LENGTH() { return PJ_GUID_STRING_LENGTH; } PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str) { enum {GUID_LEN = 36}; char sguid[GUID_LEN + 1]; uuid_t uuid = {0}; PJ_ASSERT_RETURN(GUID_LEN <= PJ_GUID_STRING_LENGTH, NULL); PJ_ASSERT_RETURN(str->ptr != NULL, NULL); PJ_CHECK_STACK(); uuid_generate(uuid); uuid_unparse(uuid, sguid); pj_memcpy(str->ptr, sguid, GUID_LEN); str->slen = GUID_LEN; return str; } ================================================ FILE: deps/pjsip/pjlib/src/pj/guid_win32.c ================================================ /* $Id: guid_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include PJ_DEF_DATA(const unsigned) PJ_GUID_STRING_LENGTH=32; PJ_DEF(unsigned) pj_GUID_STRING_LENGTH() { return PJ_GUID_STRING_LENGTH; } PJ_INLINE(void) hex2digit(unsigned value, char *p) { static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; *p++ = hex[ (value & 0xF0) >> 4 ]; *p++ = hex[ (value & 0x0F) ]; } static void guid_to_str( GUID *guid, pj_str_t *str ) { unsigned i; const unsigned char *src = (const unsigned char*)guid; char *dst = str->ptr; guid->Data1 = pj_ntohl(guid->Data1); guid->Data2 = pj_ntohs(guid->Data2); guid->Data3 = pj_ntohs(guid->Data3); for (i=0; i<16; ++i) { hex2digit( *src, dst ); dst += 2; ++src; } str->slen = 32; } PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str) { GUID guid; PJ_CHECK_STACK(); CoCreateGuid(&guid); guid_to_str( &guid, str ); return str; } ================================================ FILE: deps/pjsip/pjlib/src/pj/hash.c ================================================ /* $Id: hash.c 4296 2012-11-07 04:56:26Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include /** * The hash multiplier used to calculate hash value. */ #define PJ_HASH_MULTIPLIER 33 struct pj_hash_entry { struct pj_hash_entry *next; void *key; pj_uint32_t hash; pj_uint32_t keylen; void *value; }; struct pj_hash_table_t { pj_hash_entry **table; unsigned count, rows; pj_hash_iterator_t iterator; }; PJ_DEF(pj_uint32_t) pj_hash_calc(pj_uint32_t hash, const void *key, unsigned keylen) { PJ_CHECK_STACK(); if (keylen==PJ_HASH_KEY_STRING) { const pj_uint8_t *p = (const pj_uint8_t*)key; for ( ; *p; ++p ) { hash = (hash * PJ_HASH_MULTIPLIER) + *p; } } else { const pj_uint8_t *p = (const pj_uint8_t*)key, *end = p + keylen; for ( ; p!=end; ++p) { hash = (hash * PJ_HASH_MULTIPLIER) + *p; } } return hash; } PJ_DEF(pj_uint32_t) pj_hash_calc_tolower( pj_uint32_t hval, char *result, const pj_str_t *key) { long i; #if defined(PJ_HASH_USE_OWN_TOLOWER) && PJ_HASH_USE_OWN_TOLOWER != 0 for (i=0; islen; ++i) { pj_uint8_t c = key->ptr[i]; char lower; if (c & 64) lower = (char)(c | 32); else lower = (char)c; if (result) result[i] = lower; hval = hval * PJ_HASH_MULTIPLIER + lower; } #else for (i=0; islen; ++i) { char lower = (char)pj_tolower(key->ptr[i]); if (result) result[i] = lower; hval = hval * PJ_HASH_MULTIPLIER + lower; } #endif return hval; } PJ_DEF(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size) { pj_hash_table_t *h; unsigned table_size; /* Check that PJ_HASH_ENTRY_BUF_SIZE is correct. */ PJ_ASSERT_RETURN(sizeof(pj_hash_entry)<=PJ_HASH_ENTRY_BUF_SIZE, NULL); h = PJ_POOL_ALLOC_T(pool, pj_hash_table_t); h->count = 0; PJ_LOG( 6, ("hashtbl", "hash table %p created from pool %s", h, pj_pool_getobjname(pool))); /* size must be 2^n - 1. round-up the size to this rule, except when size is 2^n, then size will be round-down to 2^n-1. */ table_size = 8; do { table_size <<= 1; } while (table_size < size); table_size -= 1; h->rows = table_size; h->table = (pj_hash_entry**) pj_pool_calloc(pool, table_size+1, sizeof(pj_hash_entry*)); return h; } static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht, const void *key, unsigned keylen, void *val, pj_uint32_t *hval, void *entry_buf, pj_bool_t lower) { pj_uint32_t hash; pj_hash_entry **p_entry, *entry; if (hval && *hval != 0) { hash = *hval; if (keylen==PJ_HASH_KEY_STRING) { keylen = (unsigned)pj_ansi_strlen((const char*)key); } } else { /* This slightly differs with pj_hash_calc() because we need * to get the keylen when keylen is PJ_HASH_KEY_STRING. */ hash=0; if (keylen==PJ_HASH_KEY_STRING) { const pj_uint8_t *p = (const pj_uint8_t*)key; for ( ; *p; ++p ) { if (lower) hash = hash * PJ_HASH_MULTIPLIER + pj_tolower(*p); else hash = hash * PJ_HASH_MULTIPLIER + *p; } keylen = (unsigned)(p - (const unsigned char*)key); } else { const pj_uint8_t *p = (const pj_uint8_t*)key, *end = p + keylen; for ( ; p!=end; ++p) { if (lower) hash = hash * PJ_HASH_MULTIPLIER + pj_tolower(*p); else hash = hash * PJ_HASH_MULTIPLIER + *p; } } /* Report back the computed hash. */ if (hval) *hval = hash; } /* scan the linked list */ for (p_entry = &ht->table[hash & ht->rows], entry=*p_entry; entry; p_entry = &entry->next, entry = *p_entry) { if (entry->hash==hash && entry->keylen==keylen && ((lower && pj_ansi_strnicmp((const char*)entry->key, (const char*)key, keylen)==0) || (!lower && pj_memcmp(entry->key, key, keylen)==0))) { break; } } if (entry || val==NULL) return p_entry; /* Entry not found, create a new one. * If entry_buf is specified, use it. Otherwise allocate from pool. */ if (entry_buf) { entry = (pj_hash_entry*)entry_buf; } else { /* Pool must be specified! */ PJ_ASSERT_RETURN(pool != NULL, NULL); entry = PJ_POOL_ALLOC_T(pool, pj_hash_entry); PJ_LOG(6, ("hashtbl", "%p: New p_entry %p created, pool used=%u, cap=%u", ht, entry, pj_pool_get_used_size(pool), pj_pool_get_capacity(pool))); } entry->next = NULL; entry->hash = hash; if (pool) { entry->key = pj_pool_alloc(pool, keylen); pj_memcpy(entry->key, key, keylen); } else { entry->key = (void*)key; } entry->keylen = keylen; entry->value = val; *p_entry = entry; ++ht->count; return p_entry; } PJ_DEF(void *) pj_hash_get( pj_hash_table_t *ht, const void *key, unsigned keylen, pj_uint32_t *hval) { pj_hash_entry *entry; entry = *find_entry( NULL, ht, key, keylen, NULL, hval, NULL, PJ_FALSE); return entry ? entry->value : NULL; } PJ_DEF(void *) pj_hash_get_lower( pj_hash_table_t *ht, const void *key, unsigned keylen, pj_uint32_t *hval) { pj_hash_entry *entry; entry = *find_entry( NULL, ht, key, keylen, NULL, hval, NULL, PJ_TRUE); return entry ? entry->value : NULL; } static void hash_set( pj_pool_t *pool, pj_hash_table_t *ht, const void *key, unsigned keylen, pj_uint32_t hval, void *value, void *entry_buf, pj_bool_t lower ) { pj_hash_entry **p_entry; p_entry = find_entry( pool, ht, key, keylen, value, &hval, entry_buf, lower); if (*p_entry) { if (value == NULL) { /* delete entry */ PJ_LOG(6, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry)); *p_entry = (*p_entry)->next; --ht->count; } else { /* overwrite */ (*p_entry)->value = value; PJ_LOG(6, ("hashtbl", "%p: p_entry %p value set to %p", ht, *p_entry, value)); } } } PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht, const void *key, unsigned keylen, pj_uint32_t hval, void *value ) { hash_set(pool, ht, key, keylen, hval, value, NULL, PJ_FALSE); } PJ_DEF(void) pj_hash_set_lower( pj_pool_t *pool, pj_hash_table_t *ht, const void *key, unsigned keylen, pj_uint32_t hval, void *value ) { hash_set(pool, ht, key, keylen, hval, value, NULL, PJ_TRUE); } PJ_DEF(void) pj_hash_set_np( pj_hash_table_t *ht, const void *key, unsigned keylen, pj_uint32_t hval, pj_hash_entry_buf entry_buf, void *value) { hash_set(NULL, ht, key, keylen, hval, value, (void *)entry_buf, PJ_FALSE); } PJ_DEF(void) pj_hash_set_np_lower( pj_hash_table_t *ht, const void *key, unsigned keylen, pj_uint32_t hval, pj_hash_entry_buf entry_buf, void *value) { hash_set(NULL, ht, key, keylen, hval, value, (void *)entry_buf, PJ_TRUE); } PJ_DEF(unsigned) pj_hash_count( pj_hash_table_t *ht ) { return ht->count; } PJ_DEF(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht, pj_hash_iterator_t *it ) { it->index = 0; it->entry = NULL; for (; it->index <= ht->rows; ++it->index) { it->entry = ht->table[it->index]; if (it->entry) { break; } } return it->entry ? it : NULL; } PJ_DEF(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht, pj_hash_iterator_t *it ) { it->entry = it->entry->next; if (it->entry) { return it; } for (++it->index; it->index <= ht->rows; ++it->index) { it->entry = ht->table[it->index]; if (it->entry) { break; } } return it->entry ? it : NULL; } PJ_DEF(void*) pj_hash_this( pj_hash_table_t *ht, pj_hash_iterator_t *it ) { PJ_CHECK_STACK(); PJ_UNUSED_ARG(ht); return it->entry->value; } #if 0 void pj_hash_dump_collision( pj_hash_table_t *ht ) { unsigned min=0xFFFFFFFF, max=0; unsigned i; char line[120]; int len, totlen = 0; for (i=0; i<=ht->rows; ++i) { unsigned count = 0; pj_hash_entry *entry = ht->table[i]; while (entry) { ++count; entry = entry->next; } if (count < min) min = count; if (count > max) max = count; len = pj_snprintf( line+totlen, sizeof(line)-totlen, "%3d:%3d ", i, count); if (len < 1) break; totlen += len; if ((i+1) % 10 == 0) { line[totlen] = '\0'; PJ_LOG(4,(__FILE__, line)); } } PJ_LOG(4,(__FILE__,"Count: %d, min: %d, max: %d\n", ht->count, min, max)); } #endif ================================================ FILE: deps/pjsip/pjlib/src/pj/ioqueue_common_abs.c ================================================ /* $Id: ioqueue_common_abs.c 4359 2013-02-21 11:18:36Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * ioqueue_common_abs.c * * This contains common functionalities to emulate proactor pattern with * various event dispatching mechanisms (e.g. select, epoll). * * This file will be included by the appropriate ioqueue implementation. * This file is NOT supposed to be compiled as stand-alone source. */ #define PENDING_RETRY 2 static void ioqueue_init( pj_ioqueue_t *ioqueue ) { ioqueue->lock = NULL; ioqueue->auto_delete_lock = 0; ioqueue->default_concurrency = PJ_IOQUEUE_DEFAULT_ALLOW_CONCURRENCY; } static pj_status_t ioqueue_destroy(pj_ioqueue_t *ioqueue) { if (ioqueue->auto_delete_lock && ioqueue->lock ) { pj_lock_release(ioqueue->lock); return pj_lock_destroy(ioqueue->lock); } return PJ_SUCCESS; } /* * pj_ioqueue_set_lock() */ PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioqueue, pj_lock_t *lock, pj_bool_t auto_delete ) { PJ_ASSERT_RETURN(ioqueue && lock, PJ_EINVAL); if (ioqueue->auto_delete_lock && ioqueue->lock) { pj_lock_destroy(ioqueue->lock); } ioqueue->lock = lock; ioqueue->auto_delete_lock = auto_delete; return PJ_SUCCESS; } static pj_status_t ioqueue_init_key( pj_pool_t *pool, pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *key, pj_sock_t sock, pj_grp_lock_t *grp_lock, void *user_data, const pj_ioqueue_callback *cb) { pj_status_t rc; int optlen; PJ_UNUSED_ARG(pool); key->ioqueue = ioqueue; key->fd = sock; key->user_data = user_data; pj_list_init(&key->read_list); pj_list_init(&key->write_list); #if PJ_HAS_TCP pj_list_init(&key->accept_list); key->connecting = 0; #endif /* Save callback. */ pj_memcpy(&key->cb, cb, sizeof(pj_ioqueue_callback)); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Set initial reference count to 1 */ pj_assert(key->ref_count == 0); ++key->ref_count; key->closing = 0; #endif rc = pj_ioqueue_set_concurrency(key, ioqueue->default_concurrency); if (rc != PJ_SUCCESS) return rc; /* Get socket type. When socket type is datagram, some optimization * will be performed during send to allow parallel send operations. */ optlen = sizeof(key->fd_type); rc = pj_sock_getsockopt(sock, pj_SOL_SOCKET(), pj_SO_TYPE(), &key->fd_type, &optlen); if (rc != PJ_SUCCESS) key->fd_type = pj_SOCK_STREAM(); /* Create mutex for the key. */ #if !PJ_IOQUEUE_HAS_SAFE_UNREG rc = pj_lock_create_simple_mutex(poll, NULL, &key->lock); #endif if (rc != PJ_SUCCESS) return rc; /* Group lock */ key->grp_lock = grp_lock; if (key->grp_lock) { pj_grp_lock_add_ref_dbg(key->grp_lock, "ioqueue", 0); } return PJ_SUCCESS; } /* * pj_ioqueue_get_user_data() * * Obtain value associated with a key. */ PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) { PJ_ASSERT_RETURN(key != NULL, NULL); return key->user_data; } /* * pj_ioqueue_set_user_data() */ PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key, void *user_data, void **old_data) { PJ_ASSERT_RETURN(key, PJ_EINVAL); if (old_data) *old_data = key->user_data; key->user_data = user_data; return PJ_SUCCESS; } PJ_INLINE(int) key_has_pending_write(pj_ioqueue_key_t *key) { return !pj_list_empty(&key->write_list); } PJ_INLINE(int) key_has_pending_read(pj_ioqueue_key_t *key) { return !pj_list_empty(&key->read_list); } PJ_INLINE(int) key_has_pending_accept(pj_ioqueue_key_t *key) { #if PJ_HAS_TCP return !pj_list_empty(&key->accept_list); #else PJ_UNUSED_ARG(key); return 0; #endif } PJ_INLINE(int) key_has_pending_connect(pj_ioqueue_key_t *key) { return key->connecting; } #if PJ_IOQUEUE_HAS_SAFE_UNREG # define IS_CLOSING(key) (key->closing) #else # define IS_CLOSING(key) (0) #endif /* * ioqueue_dispatch_event() * * Report occurence of an event in the key to be processed by the * framework. */ pj_bool_t ioqueue_dispatch_write_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h) { pj_status_t rc; /* Try lock the key. */ rc = pj_ioqueue_trylock_key(h); if (rc != PJ_SUCCESS) { return PJ_FALSE; } if (IS_CLOSING(h)) { pj_ioqueue_unlock_key(h); return PJ_TRUE; } #if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0 if (h->connecting) { /* Completion of connect() operation */ pj_status_t status; pj_bool_t has_lock; /* Clear operation. */ h->connecting = 0; ioqueue_remove_from_set(ioqueue, h, WRITEABLE_EVENT); ioqueue_remove_from_set(ioqueue, h, EXCEPTION_EVENT); #if (defined(PJ_HAS_SO_ERROR) && PJ_HAS_SO_ERROR!=0) /* from connect(2): * On Linux, use getsockopt to read the SO_ERROR option at * level SOL_SOCKET to determine whether connect() completed * successfully (if SO_ERROR is zero). */ { int value; int vallen = sizeof(value); int gs_rc = pj_sock_getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &value, &vallen); if (gs_rc != 0) { /* Argh!! What to do now??? * Just indicate that the socket is connected. The * application will get error as soon as it tries to use * the socket to send/receive. */ status = PJ_SUCCESS; } else { status = PJ_STATUS_FROM_OS(value); } } #elif (defined(PJ_WIN32) && PJ_WIN32!=0) || (defined(PJ_WIN64) && PJ_WIN64!=0) status = PJ_SUCCESS; /* success */ #else /* Excellent information in D.J. Bernstein page: * http://cr.yp.to/docs/connect.html * * Seems like the most portable way of detecting connect() * failure is to call getpeername(). If socket is connected, * getpeername() will return 0. If the socket is not connected, * it will return ENOTCONN, and read(fd, &ch, 1) will produce * the right errno through error slippage. This is a combination * of suggestions from Douglas C. Schmidt and Ken Keys. */ { struct sockaddr_in addr; int addrlen = sizeof(addr); status = pj_sock_getpeername(h->fd, (struct sockaddr*)&addr, &addrlen); } #endif /* Unlock; from this point we don't need to hold key's mutex * (unless concurrency is disabled, which in this case we should * hold the mutex while calling the callback) */ if (h->allow_concurrent) { /* concurrency may be changed while we're in the callback, so * save it to a flag. */ has_lock = PJ_FALSE; pj_ioqueue_unlock_key(h); } else { has_lock = PJ_TRUE; } /* Call callback. */ if (h->cb.on_connect_complete && !IS_CLOSING(h)) (*h->cb.on_connect_complete)(h, status); /* Unlock if we still hold the lock */ if (has_lock) { pj_ioqueue_unlock_key(h); } /* Done. */ } else #endif /* PJ_HAS_TCP */ if (key_has_pending_write(h)) { /* Socket is writable. */ struct write_operation *write_op; pj_ssize_t sent; pj_status_t send_rc = PJ_SUCCESS; /* Get the first in the queue. */ write_op = h->write_list.next; /* For datagrams, we can remove the write_op from the list * so that send() can work in parallel. */ if (h->fd_type == pj_SOCK_DGRAM()) { pj_list_erase(write_op); if (pj_list_empty(&h->write_list)) ioqueue_remove_from_set(ioqueue, h, WRITEABLE_EVENT); } /* Send the data. * Unfortunately we must do this while holding key's mutex, thus * preventing parallel write on a single key.. :-(( */ sent = write_op->size - write_op->written; if (write_op->op == PJ_IOQUEUE_OP_SEND) { send_rc = pj_sock_send(h->fd, write_op->buf+write_op->written, &sent, write_op->flags); /* Can't do this. We only clear "op" after we're finished sending * the whole buffer. */ //write_op->op = 0; } else if (write_op->op == PJ_IOQUEUE_OP_SEND_TO) { int retry = 2; while (--retry >= 0) { send_rc = pj_sock_sendto(h->fd, write_op->buf+write_op->written, &sent, write_op->flags, &write_op->rmt_addr, write_op->rmt_addrlen); #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 /* Special treatment for dead UDP sockets here, see ticket #1107 */ if (send_rc==PJ_STATUS_FROM_OS(EPIPE) && !IS_CLOSING(h) && h->fd_type==pj_SOCK_DGRAM()) { PJ_PERROR(4,(THIS_FILE, send_rc, "Send error for socket %d, retrying", h->fd)); replace_udp_sock(h); continue; } #endif break; } /* Can't do this. We only clear "op" after we're finished sending * the whole buffer. */ //write_op->op = 0; } else { pj_assert(!"Invalid operation type!"); write_op->op = PJ_IOQUEUE_OP_NONE; send_rc = PJ_EBUG; } if (send_rc == PJ_SUCCESS) { write_op->written += sent; } else { pj_assert(send_rc > 0); write_op->written = -send_rc; } /* Are we finished with this buffer? */ if (send_rc!=PJ_SUCCESS || write_op->written == (pj_ssize_t)write_op->size || h->fd_type == pj_SOCK_DGRAM()) { pj_bool_t has_lock; write_op->op = PJ_IOQUEUE_OP_NONE; if (h->fd_type != pj_SOCK_DGRAM()) { /* Write completion of the whole stream. */ pj_list_erase(write_op); /* Clear operation if there's no more data to send. */ if (pj_list_empty(&h->write_list)) ioqueue_remove_from_set(ioqueue, h, WRITEABLE_EVENT); } /* Unlock; from this point we don't need to hold key's mutex * (unless concurrency is disabled, which in this case we should * hold the mutex while calling the callback) */ if (h->allow_concurrent) { /* concurrency may be changed while we're in the callback, so * save it to a flag. */ has_lock = PJ_FALSE; pj_ioqueue_unlock_key(h); PJ_RACE_ME(5); } else { has_lock = PJ_TRUE; } /* Call callback. */ if (h->cb.on_write_complete && !IS_CLOSING(h)) { (*h->cb.on_write_complete)(h, (pj_ioqueue_op_key_t*)write_op, write_op->written); } if (has_lock) { pj_ioqueue_unlock_key(h); } } else { pj_ioqueue_unlock_key(h); } /* Done. */ } else { /* * This is normal; execution may fall here when multiple threads * are signalled for the same event, but only one thread eventually * able to process the event. */ pj_ioqueue_unlock_key(h); return PJ_FALSE; } return PJ_TRUE; } pj_bool_t ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h ) { pj_status_t rc; /* Try lock the key. */ rc = pj_ioqueue_trylock_key(h); if (rc != PJ_SUCCESS) { return PJ_FALSE; } if (IS_CLOSING(h)) { pj_ioqueue_unlock_key(h); return PJ_TRUE; } # if PJ_HAS_TCP if (!pj_list_empty(&h->accept_list)) { struct accept_operation *accept_op; pj_bool_t has_lock; /* Get one accept operation from the list. */ accept_op = h->accept_list.next; pj_list_erase(accept_op); accept_op->op = PJ_IOQUEUE_OP_NONE; /* Clear bit in fdset if there is no more pending accept */ if (pj_list_empty(&h->accept_list)) ioqueue_remove_from_set(ioqueue, h, READABLE_EVENT); rc=pj_sock_accept(h->fd, accept_op->accept_fd, accept_op->rmt_addr, accept_op->addrlen); if (rc==PJ_SUCCESS && accept_op->local_addr) { rc = pj_sock_getsockname(*accept_op->accept_fd, accept_op->local_addr, accept_op->addrlen); } /* Unlock; from this point we don't need to hold key's mutex * (unless concurrency is disabled, which in this case we should * hold the mutex while calling the callback) */ if (h->allow_concurrent) { /* concurrency may be changed while we're in the callback, so * save it to a flag. */ has_lock = PJ_FALSE; pj_ioqueue_unlock_key(h); PJ_RACE_ME(5); } else { has_lock = PJ_TRUE; } /* Call callback. */ if (h->cb.on_accept_complete && !IS_CLOSING(h)) { (*h->cb.on_accept_complete)(h, (pj_ioqueue_op_key_t*)accept_op, *accept_op->accept_fd, rc); } if (has_lock) { pj_ioqueue_unlock_key(h); } } else # endif if (key_has_pending_read(h)) { struct read_operation *read_op; pj_ssize_t bytes_read; pj_bool_t has_lock; /* Get one pending read operation from the list. */ read_op = h->read_list.next; pj_list_erase(read_op); /* Clear fdset if there is no pending read. */ if (pj_list_empty(&h->read_list)) ioqueue_remove_from_set(ioqueue, h, READABLE_EVENT); bytes_read = read_op->size; if (read_op->op == PJ_IOQUEUE_OP_RECV_FROM) { read_op->op = PJ_IOQUEUE_OP_NONE; rc = pj_sock_recvfrom(h->fd, read_op->buf, &bytes_read, read_op->flags, read_op->rmt_addr, read_op->rmt_addrlen); } else if (read_op->op == PJ_IOQUEUE_OP_RECV) { read_op->op = PJ_IOQUEUE_OP_NONE; rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, read_op->flags); } else { pj_assert(read_op->op == PJ_IOQUEUE_OP_READ); read_op->op = PJ_IOQUEUE_OP_NONE; /* * User has specified pj_ioqueue_read(). * On Win32, we should do ReadFile(). But because we got * here because of select() anyway, user must have put a * socket descriptor on h->fd, which in this case we can * just call pj_sock_recv() instead of ReadFile(). * On Unix, user may put a file in h->fd, so we'll have * to call read() here. * This may not compile on systems which doesn't have * read(). That's why we only specify PJ_LINUX here so * that error is easier to catch. */ # if defined(PJ_WIN32) && PJ_WIN32 != 0 || \ defined(PJ_WIN64) && PJ_WIN64 != 0 || \ defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0 rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, read_op->flags); //rc = ReadFile((HANDLE)h->fd, read_op->buf, read_op->size, // &bytes_read, NULL); # elif (defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0) bytes_read = read(h->fd, read_op->buf, bytes_read); rc = (bytes_read >= 0) ? PJ_SUCCESS : pj_get_os_error(); # elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 bytes_read = sys_read(h->fd, read_op->buf, bytes_read); rc = (bytes_read >= 0) ? PJ_SUCCESS : -bytes_read; # else # error "Implement read() for this platform!" # endif } if (rc != PJ_SUCCESS) { # if (defined(PJ_WIN32) && PJ_WIN32 != 0) || \ (defined(PJ_WIN64) && PJ_WIN64 != 0) /* On Win32, for UDP, WSAECONNRESET on the receive side * indicates that previous sending has triggered ICMP Port * Unreachable message. * But we wouldn't know at this point which one of previous * key that has triggered the error, since UDP socket can * be shared! * So we'll just ignore it! */ if (rc == PJ_STATUS_FROM_OS(WSAECONNRESET)) { //PJ_LOG(4,(THIS_FILE, // "Ignored ICMP port unreach. on key=%p", h)); } # endif /* In any case we would report this to caller. */ bytes_read = -rc; #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 /* Special treatment for dead UDP sockets here, see ticket #1107 */ if (rc == PJ_STATUS_FROM_OS(ENOTCONN) && !IS_CLOSING(h) && h->fd_type==pj_SOCK_DGRAM()) { replace_udp_sock(h); } #endif } /* Unlock; from this point we don't need to hold key's mutex * (unless concurrency is disabled, which in this case we should * hold the mutex while calling the callback) */ if (h->allow_concurrent) { /* concurrency may be changed while we're in the callback, so * save it to a flag. */ has_lock = PJ_FALSE; pj_ioqueue_unlock_key(h); PJ_RACE_ME(5); } else { has_lock = PJ_TRUE; } /* Call callback. */ if (h->cb.on_read_complete && !IS_CLOSING(h)) { (*h->cb.on_read_complete)(h, (pj_ioqueue_op_key_t*)read_op, bytes_read); } if (has_lock) { pj_ioqueue_unlock_key(h); } } else { /* * This is normal; execution may fall here when multiple threads * are signalled for the same event, but only one thread eventually * able to process the event. */ pj_ioqueue_unlock_key(h); return PJ_FALSE; } return PJ_TRUE; } pj_bool_t ioqueue_dispatch_exception_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h ) { pj_bool_t has_lock; pj_status_t rc; /* Try lock the key. */ rc = pj_ioqueue_trylock_key(h); if (rc != PJ_SUCCESS) { return PJ_FALSE; } if (!h->connecting) { /* It is possible that more than one thread was woken up, thus * the remaining thread will see h->connecting as zero because * it has been processed by other thread. */ pj_ioqueue_unlock_key(h); return PJ_TRUE; } if (IS_CLOSING(h)) { pj_ioqueue_unlock_key(h); return PJ_TRUE; } /* Clear operation. */ h->connecting = 0; ioqueue_remove_from_set(ioqueue, h, WRITEABLE_EVENT); ioqueue_remove_from_set(ioqueue, h, EXCEPTION_EVENT); /* Unlock; from this point we don't need to hold key's mutex * (unless concurrency is disabled, which in this case we should * hold the mutex while calling the callback) */ if (h->allow_concurrent) { /* concurrency may be changed while we're in the callback, so * save it to a flag. */ has_lock = PJ_FALSE; pj_ioqueue_unlock_key(h); PJ_RACE_ME(5); } else { has_lock = PJ_TRUE; } /* Call callback. */ if (h->cb.on_connect_complete && !IS_CLOSING(h)) { pj_status_t status = -1; #if (defined(PJ_HAS_SO_ERROR) && PJ_HAS_SO_ERROR!=0) int value; int vallen = sizeof(value); int gs_rc = pj_sock_getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &value, &vallen); if (gs_rc == 0) { status = PJ_RETURN_OS_ERROR(value); } #endif (*h->cb.on_connect_complete)(h, status); } if (has_lock) { pj_ioqueue_unlock_key(h); } return PJ_TRUE; } /* * pj_ioqueue_recv() * * Start asynchronous recv() from the socket. */ PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, void *buffer, pj_ssize_t *length, unsigned flags ) { struct read_operation *read_op; PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL); PJ_CHECK_STACK(); /* Check if key is closing (need to do this first before accessing * other variables, since they might have been destroyed. See ticket * #469). */ if (IS_CLOSING(key)) return PJ_ECANCELLED; read_op = (struct read_operation*)op_key; read_op->op = PJ_IOQUEUE_OP_NONE; /* Try to see if there's data immediately available. */ if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) { pj_status_t status; pj_ssize_t size; size = *length; status = pj_sock_recv(key->fd, buffer, &size, flags); if (status == PJ_SUCCESS) { /* Yes! Data is available! */ *length = size; return PJ_SUCCESS; } else { /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report * the error to caller. */ if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) return status; } } flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); /* * No data is immediately available. * Must schedule asynchronous operation to the ioqueue. */ read_op->op = PJ_IOQUEUE_OP_RECV; read_op->buf = buffer; read_op->size = *length; read_op->flags = flags; pj_ioqueue_lock_key(key); /* Check again. Handle may have been closed after the previous check * in multithreaded app. If we add bad handle to the set it will * corrupt the ioqueue set. See #913 */ if (IS_CLOSING(key)) { pj_ioqueue_unlock_key(key); return PJ_ECANCELLED; } pj_list_insert_before(&key->read_list, read_op); ioqueue_add_to_set(key->ioqueue, key, READABLE_EVENT); pj_ioqueue_unlock_key(key); return PJ_EPENDING; } /* * pj_ioqueue_recvfrom() * * Start asynchronous recvfrom() from the socket. */ PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, void *buffer, pj_ssize_t *length, unsigned flags, pj_sockaddr_t *addr, int *addrlen) { struct read_operation *read_op; PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL); PJ_CHECK_STACK(); /* Check if key is closing. */ if (IS_CLOSING(key)) return PJ_ECANCELLED; read_op = (struct read_operation*)op_key; read_op->op = PJ_IOQUEUE_OP_NONE; /* Try to see if there's data immediately available. */ if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) { pj_status_t status; pj_ssize_t size; size = *length; status = pj_sock_recvfrom(key->fd, buffer, &size, flags, addr, addrlen); if (status == PJ_SUCCESS) { /* Yes! Data is available! */ *length = size; return PJ_SUCCESS; } else { /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report * the error to caller. */ if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) return status; } } flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); /* * No data is immediately available. * Must schedule asynchronous operation to the ioqueue. */ read_op->op = PJ_IOQUEUE_OP_RECV_FROM; read_op->buf = buffer; read_op->size = *length; read_op->flags = flags; read_op->rmt_addr = addr; read_op->rmt_addrlen = addrlen; pj_ioqueue_lock_key(key); /* Check again. Handle may have been closed after the previous check * in multithreaded app. If we add bad handle to the set it will * corrupt the ioqueue set. See #913 */ if (IS_CLOSING(key)) { pj_ioqueue_unlock_key(key); return PJ_ECANCELLED; } pj_list_insert_before(&key->read_list, read_op); ioqueue_add_to_set(key->ioqueue, key, READABLE_EVENT); pj_ioqueue_unlock_key(key); return PJ_EPENDING; } /* * pj_ioqueue_send() * * Start asynchronous send() to the descriptor. */ PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, const void *data, pj_ssize_t *length, unsigned flags) { struct write_operation *write_op; pj_status_t status; unsigned retry; pj_ssize_t sent; PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL); PJ_CHECK_STACK(); /* Check if key is closing. */ if (IS_CLOSING(key)) return PJ_ECANCELLED; /* We can not use PJ_IOQUEUE_ALWAYS_ASYNC for socket write. */ flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); /* Fast track: * Try to send data immediately, only if there's no pending write! * Note: * We are speculating that the list is empty here without properly * acquiring ioqueue's mutex first. This is intentional, to maximize * performance via parallelism. * * This should be safe, because: * - by convention, we require caller to make sure that the * key is not unregistered while other threads are invoking * an operation on the same key. * - pj_list_empty() is safe to be invoked by multiple threads, * even when other threads are modifying the list. */ if (pj_list_empty(&key->write_list)) { /* * See if data can be sent immediately. */ sent = *length; status = pj_sock_send(key->fd, data, &sent, flags); if (status == PJ_SUCCESS) { /* Success! */ *length = sent; return PJ_SUCCESS; } else { /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report * the error to caller. */ if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) { return status; } } } /* * Schedule asynchronous send. */ write_op = (struct write_operation*)op_key; /* Spin if write_op has pending operation */ for (retry=0; write_op->op != 0 && retryop) { /* Unable to send packet because there is already pending write in the * write_op. We could not put the operation into the write_op * because write_op already contains a pending operation! And * we could not send the packet directly with send() either, * because that will break the order of the packet. So we can * only return error here. * * This could happen for example in multithreads program, * where polling is done by one thread, while other threads are doing * the sending only. If the polling thread runs on lower priority * than the sending thread, then it's possible that the pending * write flag is not cleared in-time because clearing is only done * during polling. * * Aplication should specify multiple write operation keys on * situation like this. */ //pj_assert(!"ioqueue: there is pending operation on this key!"); return PJ_EBUSY; } write_op->op = PJ_IOQUEUE_OP_SEND; write_op->buf = (char*)data; write_op->size = *length; write_op->written = 0; write_op->flags = flags; pj_ioqueue_lock_key(key); /* Check again. Handle may have been closed after the previous check * in multithreaded app. If we add bad handle to the set it will * corrupt the ioqueue set. See #913 */ if (IS_CLOSING(key)) { pj_ioqueue_unlock_key(key); return PJ_ECANCELLED; } pj_list_insert_before(&key->write_list, write_op); ioqueue_add_to_set(key->ioqueue, key, WRITEABLE_EVENT); pj_ioqueue_unlock_key(key); return PJ_EPENDING; } /* * pj_ioqueue_sendto() * * Start asynchronous write() to the descriptor. */ PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, const void *data, pj_ssize_t *length, pj_uint32_t flags, const pj_sockaddr_t *addr, int addrlen) { struct write_operation *write_op; unsigned retry; pj_bool_t restart_retry = PJ_FALSE; pj_status_t status; pj_ssize_t sent; PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL); PJ_CHECK_STACK(); #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 retry_on_restart: #else PJ_UNUSED_ARG(restart_retry); #endif /* Check if key is closing. */ if (IS_CLOSING(key)) return PJ_ECANCELLED; /* We can not use PJ_IOQUEUE_ALWAYS_ASYNC for socket write */ flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); /* Fast track: * Try to send data immediately, only if there's no pending write! * Note: * We are speculating that the list is empty here without properly * acquiring ioqueue's mutex first. This is intentional, to maximize * performance via parallelism. * * This should be safe, because: * - by convention, we require caller to make sure that the * key is not unregistered while other threads are invoking * an operation on the same key. * - pj_list_empty() is safe to be invoked by multiple threads, * even when other threads are modifying the list. */ if (pj_list_empty(&key->write_list)) { /* * See if data can be sent immediately. */ sent = *length; status = pj_sock_sendto(key->fd, data, &sent, flags, addr, addrlen); if (status == PJ_SUCCESS) { /* Success! */ *length = sent; return PJ_SUCCESS; } else { /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report * the error to caller. */ if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) { #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 /* Special treatment for dead UDP sockets here, see ticket #1107 */ if (status==PJ_STATUS_FROM_OS(EPIPE) && !IS_CLOSING(key) && key->fd_type==pj_SOCK_DGRAM() && !restart_retry) { PJ_PERROR(4,(THIS_FILE, status, "Send error for socket %d, retrying", key->fd)); replace_udp_sock(key); restart_retry = PJ_TRUE; goto retry_on_restart; } #endif return status; } } } /* * Check that address storage can hold the address parameter. */ PJ_ASSERT_RETURN(addrlen <= (int)sizeof(pj_sockaddr_in), PJ_EBUG); /* * Schedule asynchronous send. */ write_op = (struct write_operation*)op_key; /* Spin if write_op has pending operation */ for (retry=0; write_op->op != 0 && retryop) { /* Unable to send packet because there is already pending write on the * write_op. We could not put the operation into the write_op * because write_op already contains a pending operation! And * we could not send the packet directly with sendto() either, * because that will break the order of the packet. So we can * only return error here. * * This could happen for example in multithreads program, * where polling is done by one thread, while other threads are doing * the sending only. If the polling thread runs on lower priority * than the sending thread, then it's possible that the pending * write flag is not cleared in-time because clearing is only done * during polling. * * Aplication should specify multiple write operation keys on * situation like this. */ //pj_assert(!"ioqueue: there is pending operation on this key!"); return PJ_EBUSY; } write_op->op = PJ_IOQUEUE_OP_SEND_TO; write_op->buf = (char*)data; write_op->size = *length; write_op->written = 0; write_op->flags = flags; pj_memcpy(&write_op->rmt_addr, addr, addrlen); write_op->rmt_addrlen = addrlen; pj_ioqueue_lock_key(key); /* Check again. Handle may have been closed after the previous check * in multithreaded app. If we add bad handle to the set it will * corrupt the ioqueue set. See #913 */ if (IS_CLOSING(key)) { pj_ioqueue_unlock_key(key); return PJ_ECANCELLED; } pj_list_insert_before(&key->write_list, write_op); ioqueue_add_to_set(key->ioqueue, key, WRITEABLE_EVENT); pj_ioqueue_unlock_key(key); return PJ_EPENDING; } #if PJ_HAS_TCP /* * Initiate overlapped accept() operation. */ PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t *new_sock, pj_sockaddr_t *local, pj_sockaddr_t *remote, int *addrlen) { struct accept_operation *accept_op; pj_status_t status; /* check parameters. All must be specified! */ PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL); /* Check if key is closing. */ if (IS_CLOSING(key)) return PJ_ECANCELLED; accept_op = (struct accept_operation*)op_key; accept_op->op = PJ_IOQUEUE_OP_NONE; /* Fast track: * See if there's new connection available immediately. */ if (pj_list_empty(&key->accept_list)) { status = pj_sock_accept(key->fd, new_sock, remote, addrlen); if (status == PJ_SUCCESS) { /* Yes! New connection is available! */ if (local && addrlen) { status = pj_sock_getsockname(*new_sock, local, addrlen); if (status != PJ_SUCCESS) { pj_sock_close(*new_sock); *new_sock = PJ_INVALID_SOCKET; return status; } } return PJ_SUCCESS; } else { /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report * the error to caller. */ if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) { return status; } } } /* * No connection is available immediately. * Schedule accept() operation to be completed when there is incoming * connection available. */ accept_op->op = PJ_IOQUEUE_OP_ACCEPT; accept_op->accept_fd = new_sock; accept_op->rmt_addr = remote; accept_op->addrlen= addrlen; accept_op->local_addr = local; pj_ioqueue_lock_key(key); /* Check again. Handle may have been closed after the previous check * in multithreaded app. If we add bad handle to the set it will * corrupt the ioqueue set. See #913 */ if (IS_CLOSING(key)) { pj_ioqueue_unlock_key(key); return PJ_ECANCELLED; } pj_list_insert_before(&key->accept_list, accept_op); ioqueue_add_to_set(key->ioqueue, key, READABLE_EVENT); pj_ioqueue_unlock_key(key); return PJ_EPENDING; } /* * Initiate overlapped connect() operation (well, it's non-blocking actually, * since there's no overlapped version of connect()). */ PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key, const pj_sockaddr_t *addr, int addrlen ) { pj_status_t status; /* check parameters. All must be specified! */ PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL); /* Check if key is closing. */ if (IS_CLOSING(key)) return PJ_ECANCELLED; /* Check if socket has not been marked for connecting */ if (key->connecting != 0) return PJ_EPENDING; status = pj_sock_connect(key->fd, addr, addrlen); if (status == PJ_SUCCESS) { /* Connected! */ return PJ_SUCCESS; } else { if (status == PJ_STATUS_FROM_OS(PJ_BLOCKING_CONNECT_ERROR_VAL)) { /* Pending! */ pj_ioqueue_lock_key(key); /* Check again. Handle may have been closed after the previous * check in multithreaded app. See #913 */ if (IS_CLOSING(key)) { pj_ioqueue_unlock_key(key); return PJ_ECANCELLED; } key->connecting = PJ_TRUE; ioqueue_add_to_set(key->ioqueue, key, WRITEABLE_EVENT); ioqueue_add_to_set(key->ioqueue, key, EXCEPTION_EVENT); pj_ioqueue_unlock_key(key); return PJ_EPENDING; } else { /* Error! */ return status; } } } #endif /* PJ_HAS_TCP */ PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key, pj_size_t size ) { pj_bzero(op_key, size); } /* * pj_ioqueue_is_pending() */ PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key ) { struct generic_operation *op_rec; PJ_UNUSED_ARG(key); op_rec = (struct generic_operation*)op_key; return op_rec->op != 0; } /* * pj_ioqueue_post_completion() */ PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_status ) { struct generic_operation *op_rec; /* * Find the operation key in all pending operation list to * really make sure that it's still there; then call the callback. */ pj_ioqueue_lock_key(key); /* Find the operation in the pending read list. */ op_rec = (struct generic_operation*)key->read_list.next; while (op_rec != (void*)&key->read_list) { if (op_rec == (void*)op_key) { pj_list_erase(op_rec); op_rec->op = PJ_IOQUEUE_OP_NONE; pj_ioqueue_unlock_key(key); (*key->cb.on_read_complete)(key, op_key, bytes_status); return PJ_SUCCESS; } op_rec = op_rec->next; } /* Find the operation in the pending write list. */ op_rec = (struct generic_operation*)key->write_list.next; while (op_rec != (void*)&key->write_list) { if (op_rec == (void*)op_key) { pj_list_erase(op_rec); op_rec->op = PJ_IOQUEUE_OP_NONE; pj_ioqueue_unlock_key(key); (*key->cb.on_write_complete)(key, op_key, bytes_status); return PJ_SUCCESS; } op_rec = op_rec->next; } /* Find the operation in the pending accept list. */ op_rec = (struct generic_operation*)key->accept_list.next; while (op_rec != (void*)&key->accept_list) { if (op_rec == (void*)op_key) { pj_list_erase(op_rec); op_rec->op = PJ_IOQUEUE_OP_NONE; pj_ioqueue_unlock_key(key); (*key->cb.on_accept_complete)(key, op_key, PJ_INVALID_SOCKET, (pj_status_t)bytes_status); return PJ_SUCCESS; } op_rec = op_rec->next; } pj_ioqueue_unlock_key(key); return PJ_EINVALIDOP; } PJ_DEF(pj_status_t) pj_ioqueue_set_default_concurrency( pj_ioqueue_t *ioqueue, pj_bool_t allow) { PJ_ASSERT_RETURN(ioqueue != NULL, PJ_EINVAL); ioqueue->default_concurrency = allow; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_ioqueue_set_concurrency(pj_ioqueue_key_t *key, pj_bool_t allow) { PJ_ASSERT_RETURN(key, PJ_EINVAL); /* PJ_IOQUEUE_HAS_SAFE_UNREG must be enabled if concurrency is * disabled. */ PJ_ASSERT_RETURN(allow || PJ_IOQUEUE_HAS_SAFE_UNREG, PJ_EINVAL); key->allow_concurrent = allow; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_ioqueue_lock_key(pj_ioqueue_key_t *key) { if (key->grp_lock) return pj_grp_lock_acquire(key->grp_lock); else return pj_lock_acquire(key->lock); } PJ_DEF(pj_status_t) pj_ioqueue_trylock_key(pj_ioqueue_key_t *key) { if (key->grp_lock) return pj_grp_lock_tryacquire(key->grp_lock); else return pj_lock_tryacquire(key->lock); } PJ_DEF(pj_status_t) pj_ioqueue_unlock_key(pj_ioqueue_key_t *key) { if (key->grp_lock) return pj_grp_lock_release(key->grp_lock); else return pj_lock_release(key->lock); } ================================================ FILE: deps/pjsip/pjlib/src/pj/ioqueue_common_abs.h ================================================ /* $Id */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* ioqueue_common_abs.h * * This file contains private declarations for abstracting various * event polling/dispatching mechanisms (e.g. select, poll, epoll) * to the ioqueue. */ #include /* * The select ioqueue relies on socket functions (pj_sock_xxx()) to return * the correct error code. */ #if PJ_RETURN_OS_ERROR(100) != PJ_STATUS_FROM_OS(100) # error "Proper error reporting must be enabled for ioqueue to work!" #endif struct generic_operation { PJ_DECL_LIST_MEMBER(struct generic_operation); pj_ioqueue_operation_e op; }; struct read_operation { PJ_DECL_LIST_MEMBER(struct read_operation); pj_ioqueue_operation_e op; void *buf; pj_size_t size; unsigned flags; pj_sockaddr_t *rmt_addr; int *rmt_addrlen; }; struct write_operation { PJ_DECL_LIST_MEMBER(struct write_operation); pj_ioqueue_operation_e op; char *buf; pj_size_t size; pj_ssize_t written; unsigned flags; pj_sockaddr_in rmt_addr; int rmt_addrlen; }; struct accept_operation { PJ_DECL_LIST_MEMBER(struct accept_operation); pj_ioqueue_operation_e op; pj_sock_t *accept_fd; pj_sockaddr_t *local_addr; pj_sockaddr_t *rmt_addr; int *addrlen; }; union operation_key { struct generic_operation generic; struct read_operation read; struct write_operation write; #if PJ_HAS_TCP struct accept_operation accept; #endif }; #if PJ_IOQUEUE_HAS_SAFE_UNREG # define UNREG_FIELDS \ unsigned ref_count; \ pj_bool_t closing; \ pj_time_val free_time; \ #else # define UNREG_FIELDS #endif #define DECLARE_COMMON_KEY \ PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t); \ pj_ioqueue_t *ioqueue; \ pj_grp_lock_t *grp_lock; \ pj_lock_t *lock; \ pj_bool_t inside_callback; \ pj_bool_t destroy_requested; \ pj_bool_t allow_concurrent; \ pj_sock_t fd; \ int fd_type; \ void *user_data; \ pj_ioqueue_callback cb; \ int connecting; \ struct read_operation read_list; \ struct write_operation write_list; \ struct accept_operation accept_list; \ UNREG_FIELDS #define DECLARE_COMMON_IOQUEUE \ pj_lock_t *lock; \ pj_bool_t auto_delete_lock; \ pj_bool_t default_concurrency; enum ioqueue_event_type { NO_EVENT, READABLE_EVENT, WRITEABLE_EVENT, EXCEPTION_EVENT, }; static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *key, enum ioqueue_event_type event_type ); static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *key, enum ioqueue_event_type event_type); ================================================ FILE: deps/pjsip/pjlib/src/pj/ioqueue_dummy.c ================================================ /* $Id: ioqueue_dummy.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #define THIS_FILE "ioqueue" #define PJ_IOQUEUE_IS_READ_OP(op) \ ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM)) #define PJ_IOQUEUE_IS_WRITE_OP(op) \ ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO)) #if PJ_HAS_TCP # define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT) # define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT) #else # define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0 # define PJ_IOQUEUE_IS_CONNECT_OP(op) 0 #endif #if defined(PJ_DEBUG) && PJ_DEBUG != 0 # define VALIDATE_FD_SET 1 #else # define VALIDATE_FD_SET 0 #endif struct pj_ioqueue_key_t { PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t) pj_sock_t fd; pj_ioqueue_operation_e op; void *user_data; pj_ioqueue_callback cb; }; struct pj_ioqueue_t { }; PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, pj_size_t max_fd, int max_threads, pj_ioqueue_t **ptr_ioqueue) { return PJ_ENOTSUP; } PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque) { return PJ_ENOTSUP; } PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, pj_lock_t *lock, pj_bool_t auto_delete ) { return PJ_ENOTSUP; } PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, pj_ioqueue_t *ioque, pj_sock_t sock, void *user_data, const pj_ioqueue_callback *cb, pj_ioqueue_key_t **ptr_key) { return PJ_ENOTSUP; } PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key) { return PJ_ENOTSUP; } PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) { return NULL; } PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) { return -1; } PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key, void *buffer, pj_size_t buflen) { return -1; } PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key, void *buffer, pj_size_t buflen, unsigned flags) { return -1; } PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key, void *buffer, pj_size_t buflen, unsigned flags, pj_sockaddr_t *addr, int *addrlen) { return -1; } PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key, const void *data, pj_size_t datalen) { return -1; } PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key, const void *data, pj_size_t datalen, unsigned flags) { return -1; } PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key, const void *data, pj_size_t datalen, unsigned flags, const pj_sockaddr_t *addr, int addrlen) { return -1; } #if PJ_HAS_TCP /* * Initiate overlapped accept() operation. */ PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *key, pj_sock_t *new_sock, pj_sockaddr_t *local, pj_sockaddr_t *remote, int *addrlen) { return -1; } /* * Initiate overlapped connect() operation (well, it's non-blocking actually, * since there's no overlapped version of connect()). */ PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *key, const pj_sockaddr_t *addr, int addrlen ) { return -1; } #endif /* PJ_HAS_TCP */ ================================================ FILE: deps/pjsip/pjlib/src/pj/ioqueue_epoll.c ================================================ /* $Id: ioqueue_epoll.c 4528 2013-05-30 07:01:11Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * ioqueue_epoll.c * * This is the implementation of IOQueue framework using /dev/epoll * API in _both_ Linux user-mode and kernel-mode. */ #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(PJ_LINUX_KERNEL) || PJ_LINUX_KERNEL==0 /* * Linux user mode */ # include # include # include # define epoll_data data.ptr # define epoll_data_type void* # define ioctl_val_type unsigned long # define getsockopt_val_ptr int* # define os_getsockopt getsockopt # define os_ioctl ioctl # define os_read read # define os_close close # define os_epoll_create epoll_create # define os_epoll_ctl epoll_ctl # define os_epoll_wait epoll_wait #else /* * Linux kernel mode. */ # include # include # if defined(MODVERSIONS) # include # endif # include # include # include # include # include # include # include enum EPOLL_EVENTS { EPOLLIN = 0x001, EPOLLOUT = 0x004, EPOLLERR = 0x008, }; # define os_epoll_create sys_epoll_create static int os_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) { long rc; mm_segment_t oldfs = get_fs(); set_fs(KERNEL_DS); rc = sys_epoll_ctl(epfd, op, fd, event); set_fs(oldfs); if (rc) { errno = -rc; return -1; } else { return 0; } } static int os_epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) { int count; mm_segment_t oldfs = get_fs(); set_fs(KERNEL_DS); count = sys_epoll_wait(epfd, events, maxevents, timeout); set_fs(oldfs); return count; } # define os_close sys_close # define os_getsockopt pj_sock_getsockopt static int os_read(int fd, void *buf, size_t len) { long rc; mm_segment_t oldfs = get_fs(); set_fs(KERNEL_DS); rc = sys_read(fd, buf, len); set_fs(oldfs); if (rc) { errno = -rc; return -1; } else { return 0; } } # define socklen_t unsigned # define ioctl_val_type unsigned long int ioctl(int fd, int opt, ioctl_val_type value); static int os_ioctl(int fd, int opt, ioctl_val_type value) { int rc; mm_segment_t oldfs = get_fs(); set_fs(KERNEL_DS); rc = ioctl(fd, opt, value); set_fs(oldfs); if (rc < 0) { errno = -rc; return rc; } else return rc; } # define getsockopt_val_ptr char* # define epoll_data data # define epoll_data_type __u32 #endif #define THIS_FILE "ioq_epoll" //#define TRACE_(expr) PJ_LOG(3,expr) #define TRACE_(expr) /* * Include common ioqueue abstraction. */ #include "ioqueue_common_abs.h" /* * This describes each key. */ struct pj_ioqueue_key_t { DECLARE_COMMON_KEY }; struct queue { pj_ioqueue_key_t *key; enum ioqueue_event_type event_type; }; /* * This describes the I/O queue. */ struct pj_ioqueue_t { DECLARE_COMMON_IOQUEUE unsigned max, count; //pj_ioqueue_key_t hlist; pj_ioqueue_key_t active_list; int epfd; //struct epoll_event *events; //struct queue *queue; #if PJ_IOQUEUE_HAS_SAFE_UNREG pj_mutex_t *ref_cnt_mutex; pj_ioqueue_key_t closing_list; pj_ioqueue_key_t free_list; #endif }; /* Include implementation for common abstraction after we declare * pj_ioqueue_key_t and pj_ioqueue_t. */ #include "ioqueue_common_abs.c" #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Scan closing keys to be put to free list again */ static void scan_closing_keys(pj_ioqueue_t *ioqueue); #endif /* * pj_ioqueue_name() */ PJ_DEF(const char*) pj_ioqueue_name(void) { #if defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL!=0 return "epoll-kernel"; #else return "epoll"; #endif } /* * pj_ioqueue_create() * * Create select ioqueue. */ PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, pj_size_t max_fd, pj_ioqueue_t **p_ioqueue) { pj_ioqueue_t *ioqueue; pj_status_t rc; pj_lock_t *lock; int i; /* Check that arguments are valid. */ PJ_ASSERT_RETURN(pool != NULL && p_ioqueue != NULL && max_fd > 0, PJ_EINVAL); /* Check that size of pj_ioqueue_op_key_t is sufficient */ PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >= sizeof(union operation_key), PJ_EBUG); ioqueue = pj_pool_alloc(pool, sizeof(pj_ioqueue_t)); ioqueue_init(ioqueue); ioqueue->max = max_fd; ioqueue->count = 0; pj_list_init(&ioqueue->active_list); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* When safe unregistration is used (the default), we pre-create * all keys and put them in the free list. */ /* Mutex to protect key's reference counter * We don't want to use key's mutex or ioqueue's mutex because * that would create deadlock situation in some cases. */ rc = pj_mutex_create_simple(pool, NULL, &ioqueue->ref_cnt_mutex); if (rc != PJ_SUCCESS) return rc; /* Init key list */ pj_list_init(&ioqueue->free_list); pj_list_init(&ioqueue->closing_list); /* Pre-create all keys according to max_fd */ for ( i=0; iref_count = 0; rc = pj_lock_create_recursive_mutex(pool, NULL, &key->lock); if (rc != PJ_SUCCESS) { key = ioqueue->free_list.next; while (key != &ioqueue->free_list) { pj_lock_destroy(key->lock); key = key->next; } pj_mutex_destroy(ioqueue->ref_cnt_mutex); return rc; } pj_list_push_back(&ioqueue->free_list, key); } #endif rc = pj_lock_create_simple_mutex(pool, "ioq%p", &lock); if (rc != PJ_SUCCESS) return rc; rc = pj_ioqueue_set_lock(ioqueue, lock, PJ_TRUE); if (rc != PJ_SUCCESS) return rc; ioqueue->epfd = os_epoll_create(max_fd); if (ioqueue->epfd < 0) { ioqueue_destroy(ioqueue); return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); } /*ioqueue->events = pj_pool_calloc(pool, max_fd, sizeof(struct epoll_event)); PJ_ASSERT_RETURN(ioqueue->events != NULL, PJ_ENOMEM); ioqueue->queue = pj_pool_calloc(pool, max_fd, sizeof(struct queue)); PJ_ASSERT_RETURN(ioqueue->queue != NULL, PJ_ENOMEM); */ PJ_LOG(4, ("pjlib", "epoll I/O Queue created (%p)", ioqueue)); *p_ioqueue = ioqueue; return PJ_SUCCESS; } /* * pj_ioqueue_destroy() * * Destroy ioqueue. */ PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue) { pj_ioqueue_key_t *key; PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL); PJ_ASSERT_RETURN(ioqueue->epfd > 0, PJ_EINVALIDOP); pj_lock_acquire(ioqueue->lock); os_close(ioqueue->epfd); ioqueue->epfd = 0; #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Destroy reference counters */ key = ioqueue->active_list.next; while (key != &ioqueue->active_list) { pj_lock_destroy(key->lock); key = key->next; } key = ioqueue->closing_list.next; while (key != &ioqueue->closing_list) { pj_lock_destroy(key->lock); key = key->next; } key = ioqueue->free_list.next; while (key != &ioqueue->free_list) { pj_lock_destroy(key->lock); key = key->next; } pj_mutex_destroy(ioqueue->ref_cnt_mutex); #endif return ioqueue_destroy(ioqueue); } /* * pj_ioqueue_register_sock() * * Register a socket to ioqueue. */ PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool, pj_ioqueue_t *ioqueue, pj_sock_t sock, pj_grp_lock_t *grp_lock, void *user_data, const pj_ioqueue_callback *cb, pj_ioqueue_key_t **p_key) { pj_ioqueue_key_t *key = NULL; pj_uint32_t value; struct epoll_event ev; int status; pj_status_t rc = PJ_SUCCESS; PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET && cb && p_key, PJ_EINVAL); pj_lock_acquire(ioqueue->lock); if (ioqueue->count >= ioqueue->max) { rc = PJ_ETOOMANY; TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: too many files")); goto on_return; } /* Set socket to nonblocking. */ value = 1; if ((rc=os_ioctl(sock, FIONBIO, (ioctl_val_type)&value))) { TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: ioctl rc=%d", rc)); rc = pj_get_netos_error(); goto on_return; } /* If safe unregistration (PJ_IOQUEUE_HAS_SAFE_UNREG) is used, get * the key from the free list. Otherwise allocate a new one. */ #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Scan closing_keys first to let them come back to free_list */ scan_closing_keys(ioqueue); pj_assert(!pj_list_empty(&ioqueue->free_list)); if (pj_list_empty(&ioqueue->free_list)) { rc = PJ_ETOOMANY; goto on_return; } key = ioqueue->free_list.next; pj_list_erase(key); #else /* Create key. */ key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); #endif rc = ioqueue_init_key(pool, ioqueue, key, sock, grp_lock, user_data, cb); if (rc != PJ_SUCCESS) { key = NULL; goto on_return; } /* Create key's mutex */ /* rc = pj_mutex_create_recursive(pool, NULL, &key->mutex); if (rc != PJ_SUCCESS) { key = NULL; goto on_return; } */ /* os_epoll_ctl. */ ev.events = EPOLLIN | EPOLLERR; ev.epoll_data = (epoll_data_type)key; status = os_epoll_ctl(ioqueue->epfd, EPOLL_CTL_ADD, sock, &ev); if (status < 0) { rc = pj_get_os_error(); pj_lock_destroy(key->lock); key = NULL; TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: os_epoll_ctl rc=%d", status)); goto on_return; } /* Register */ pj_list_insert_before(&ioqueue->active_list, key); ++ioqueue->count; //TRACE_((THIS_FILE, "socket registered, count=%d", ioqueue->count)); on_return: if (rc != PJ_SUCCESS) { if (key && key->grp_lock) pj_grp_lock_dec_ref_dbg(key->grp_lock, "ioqueue", 0); } *p_key = key; pj_lock_release(ioqueue->lock); return rc; } PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, pj_ioqueue_t *ioqueue, pj_sock_t sock, void *user_data, const pj_ioqueue_callback *cb, pj_ioqueue_key_t **p_key) { return pj_ioqueue_register_sock2(pool, ioqueue, sock, NULL, user_data, cb, p_key); } #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Increment key's reference counter */ static void increment_counter(pj_ioqueue_key_t *key) { pj_mutex_lock(key->ioqueue->ref_cnt_mutex); ++key->ref_count; pj_mutex_unlock(key->ioqueue->ref_cnt_mutex); } /* Decrement the key's reference counter, and when the counter reach zero, * destroy the key. * * Note: MUST NOT CALL THIS FUNCTION WHILE HOLDING ioqueue's LOCK. */ static void decrement_counter(pj_ioqueue_key_t *key) { pj_lock_acquire(key->ioqueue->lock); pj_mutex_lock(key->ioqueue->ref_cnt_mutex); --key->ref_count; if (key->ref_count == 0) { pj_assert(key->closing == 1); pj_gettickcount(&key->free_time); key->free_time.msec += PJ_IOQUEUE_KEY_FREE_DELAY; pj_time_val_normalize(&key->free_time); pj_list_erase(key); pj_list_push_back(&key->ioqueue->closing_list, key); } pj_mutex_unlock(key->ioqueue->ref_cnt_mutex); pj_lock_release(key->ioqueue->lock); } #endif /* * pj_ioqueue_unregister() * * Unregister handle from ioqueue. */ PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key) { pj_ioqueue_t *ioqueue; struct epoll_event ev; int status; PJ_ASSERT_RETURN(key != NULL, PJ_EINVAL); ioqueue = key->ioqueue; /* Lock the key to make sure no callback is simultaneously modifying * the key. We need to lock the key before ioqueue here to prevent * deadlock. */ pj_ioqueue_lock_key(key); /* Also lock ioqueue */ pj_lock_acquire(ioqueue->lock); pj_assert(ioqueue->count > 0); --ioqueue->count; #if !PJ_IOQUEUE_HAS_SAFE_UNREG pj_list_erase(key); #endif ev.events = 0; ev.epoll_data = (epoll_data_type)key; status = os_epoll_ctl( ioqueue->epfd, EPOLL_CTL_DEL, key->fd, &ev); if (status != 0) { pj_status_t rc = pj_get_os_error(); pj_lock_release(ioqueue->lock); return rc; } /* Destroy the key. */ pj_sock_close(key->fd); pj_lock_release(ioqueue->lock); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Mark key is closing. */ key->closing = 1; /* Decrement counter. */ decrement_counter(key); /* Done. */ if (key->grp_lock) { /* just dec_ref and unlock. we will set grp_lock to NULL * elsewhere */ pj_grp_lock_t *grp_lock = key->grp_lock; // Don't set grp_lock to NULL otherwise the other thread // will crash. Just leave it as dangling pointer, but this // should be safe //key->grp_lock = NULL; pj_grp_lock_dec_ref_dbg(grp_lock, "ioqueue", 0); pj_grp_lock_release(grp_lock); } else { pj_ioqueue_unlock_key(key); } #else if (key->grp_lock) { /* set grp_lock to NULL and unlock */ pj_grp_lock_t *grp_lock = key->grp_lock; // Don't set grp_lock to NULL otherwise the other thread // will crash. Just leave it as dangling pointer, but this // should be safe //key->grp_lock = NULL; pj_grp_lock_dec_ref_dbg(grp_lock, "ioqueue", 0); pj_grp_lock_release(grp_lock); } else { pj_ioqueue_unlock_key(key); } pj_lock_destroy(key->lock); #endif return PJ_SUCCESS; } /* ioqueue_remove_from_set() * This function is called from ioqueue_dispatch_event() to instruct * the ioqueue to remove the specified descriptor from ioqueue's descriptor * set for the specified event. */ static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *key, enum ioqueue_event_type event_type) { if (event_type == WRITEABLE_EVENT) { struct epoll_event ev; ev.events = EPOLLIN | EPOLLERR; ev.epoll_data = (epoll_data_type)key; os_epoll_ctl( ioqueue->epfd, EPOLL_CTL_MOD, key->fd, &ev); } } /* * ioqueue_add_to_set() * This function is called from pj_ioqueue_recv(), pj_ioqueue_send() etc * to instruct the ioqueue to add the specified handle to ioqueue's descriptor * set for the specified event. */ static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *key, enum ioqueue_event_type event_type ) { if (event_type == WRITEABLE_EVENT) { struct epoll_event ev; ev.events = EPOLLIN | EPOLLOUT | EPOLLERR; ev.epoll_data = (epoll_data_type)key; os_epoll_ctl( ioqueue->epfd, EPOLL_CTL_MOD, key->fd, &ev); } } #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Scan closing keys to be put to free list again */ static void scan_closing_keys(pj_ioqueue_t *ioqueue) { pj_time_val now; pj_ioqueue_key_t *h; pj_gettickcount(&now); h = ioqueue->closing_list.next; while (h != &ioqueue->closing_list) { pj_ioqueue_key_t *next = h->next; pj_assert(h->closing != 0); if (PJ_TIME_VAL_GTE(now, h->free_time)) { pj_list_erase(h); // Don't set grp_lock to NULL otherwise the other thread // will crash. Just leave it as dangling pointer, but this // should be safe //h->grp_lock = NULL; pj_list_push_back(&ioqueue->free_list, h); } h = next; } } #endif /* * pj_ioqueue_poll() * */ PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout) { int i, count, event_cnt, processed_cnt; int msec; //struct epoll_event *events = ioqueue->events; //struct queue *queue = ioqueue->queue; enum { MAX_EVENTS = PJ_IOQUEUE_MAX_CAND_EVENTS }; struct epoll_event events[MAX_EVENTS]; struct queue queue[MAX_EVENTS]; pj_timestamp t1, t2; PJ_CHECK_STACK(); msec = timeout ? PJ_TIME_VAL_MSEC(*timeout) : 9000; TRACE_((THIS_FILE, "start os_epoll_wait, msec=%d", msec)); pj_get_timestamp(&t1); //count = os_epoll_wait( ioqueue->epfd, events, ioqueue->max, msec); count = os_epoll_wait( ioqueue->epfd, events, MAX_EVENTS, msec); if (count == 0) { #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Check the closing keys only when there's no activity and when there are * pending closing keys. */ if (count == 0 && !pj_list_empty(&ioqueue->closing_list)) { pj_lock_acquire(ioqueue->lock); scan_closing_keys(ioqueue); pj_lock_release(ioqueue->lock); } #endif TRACE_((THIS_FILE, "os_epoll_wait timed out")); return count; } else if (count < 0) { TRACE_((THIS_FILE, "os_epoll_wait error")); return -pj_get_netos_error(); } pj_get_timestamp(&t2); TRACE_((THIS_FILE, "os_epoll_wait returns %d, time=%d usec", count, pj_elapsed_usec(&t1, &t2))); /* Lock ioqueue. */ pj_lock_acquire(ioqueue->lock); for (event_cnt=0, i=0; iconnecting) && !IS_CLOSING(h)) { #if PJ_IOQUEUE_HAS_SAFE_UNREG increment_counter(h); #endif queue[event_cnt].key = h; queue[event_cnt].event_type = WRITEABLE_EVENT; ++event_cnt; continue; } #endif /* PJ_HAS_TCP */ /* * Check for error condition. */ if ((events[i].events & EPOLLERR) && !IS_CLOSING(h)) { /* * We need to handle this exception event. If it's related to us * connecting, report it as such. If not, just report it as a * read event and the higher layers will handle it. */ if (h->connecting) { #if PJ_IOQUEUE_HAS_SAFE_UNREG increment_counter(h); #endif queue[event_cnt].key = h; queue[event_cnt].event_type = EXCEPTION_EVENT; ++event_cnt; } else if (key_has_pending_read(h) || key_has_pending_accept(h)) { #if PJ_IOQUEUE_HAS_SAFE_UNREG increment_counter(h); #endif queue[event_cnt].key = h; queue[event_cnt].event_type = READABLE_EVENT; ++event_cnt; } continue; } } for (i=0; igrp_lock) pj_grp_lock_add_ref_dbg(queue[i].key->grp_lock, "ioqueue", 0); } PJ_RACE_ME(5); pj_lock_release(ioqueue->lock); PJ_RACE_ME(5); processed_cnt = 0; /* Now process the events. */ for (i=0; igrp_lock) pj_grp_lock_dec_ref_dbg(queue[i].key->grp_lock, "ioqueue", 0); } /* Special case: * When epoll returns > 0 but no descriptors are actually set! */ if (count > 0 && !event_cnt && msec > 0) { pj_thread_sleep(msec); } TRACE_((THIS_FILE, " poll: count=%d events=%d processed=%d", count, event_cnt, processed_cnt)); pj_get_timestamp(&t1); TRACE_((THIS_FILE, "ioqueue_poll() returns %d, time=%d usec", processed, pj_elapsed_usec(&t2, &t1))); return processed_cnt; } ================================================ FILE: deps/pjsip/pjlib/src/pj/ioqueue_linux_kernel.c ================================================ /* $Id: ioqueue_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #define THIS_FILE "ioqueue" #define PJ_IOQUEUE_IS_READ_OP(op) \ ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM)) #define PJ_IOQUEUE_IS_WRITE_OP(op) \ ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO)) #if PJ_HAS_TCP # define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT) # define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT) #else # define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0 # define PJ_IOQUEUE_IS_CONNECT_OP(op) 0 #endif #if defined(PJ_DEBUG) && PJ_DEBUG != 0 # define VALIDATE_FD_SET 1 #else # define VALIDATE_FD_SET 0 #endif struct pj_ioqueue_key_t { PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t) pj_sock_t fd; pj_ioqueue_operation_e op; void *user_data; pj_ioqueue_callback cb; }; struct pj_ioqueue_t { }; PJ_DEF(pj_ioqueue_t*) pj_ioqueue_create(pj_pool_t *pool, pj_size_t max_fd) { return NULL; } PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque) { return 0; } PJ_DEF(pj_ioqueue_key_t*) pj_ioqueue_register( pj_pool_t *pool, pj_ioqueue_t *ioque, pj_oshandle_t sock, void *user_data, const pj_ioqueue_callback *cb) { return NULL; } PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key) { return -1; } PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) { return NULL; } PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) { return -1; } PJ_DEF(int) pj_ioqueue_read( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key, void *buffer, pj_size_t buflen) { return -1; } PJ_DEF(int) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key, void *buffer, pj_size_t buflen, pj_sockaddr_t *addr, int *addrlen) { return -1; } PJ_DEF(int) pj_ioqueue_write( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key, const void *data, pj_size_t datalen) { return -1; } PJ_DEF(int) pj_ioqueue_sendto( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key, const void *data, pj_size_t datalen, const pj_sockaddr_t *addr, int addrlen) { return -1; } #if PJ_HAS_TCP /* * Initiate overlapped accept() operation. */ PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *key, pj_sock_t *new_sock, pj_sockaddr_t *local, pj_sockaddr_t *remote, int *addrlen) { return -1; } /* * Initiate overlapped connect() operation (well, it's non-blocking actually, * since there's no overlapped version of connect()). */ PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *key, const pj_sockaddr_t *addr, int addrlen ) { return -1; } #endif /* PJ_HAS_TCP */ ================================================ FILE: deps/pjsip/pjlib/src/pj/ioqueue_select.c ================================================ /* $Id: ioqueue_select.c 4514 2013-05-03 09:07:43Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * sock_select.c * * This is the implementation of IOQueue using pj_sock_select(). * It runs anywhere where pj_sock_select() is available (currently * Win32, Linux, Linux kernel, etc.). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Now that we have access to OS'es , lets check again that * PJ_IOQUEUE_MAX_HANDLES is not greater than FD_SETSIZE */ #if PJ_IOQUEUE_MAX_HANDLES > FD_SETSIZE # error "PJ_IOQUEUE_MAX_HANDLES cannot be greater than FD_SETSIZE" #endif /* * Include declaration from common abstraction. */ #include "ioqueue_common_abs.h" /* * ISSUES with ioqueue_select() * * EAGAIN/EWOULDBLOCK error in recv(): * - when multiple threads are working with the ioqueue, application * may receive EAGAIN or EWOULDBLOCK in the receive callback. * This error happens because more than one thread is watching for * the same descriptor set, so when all of them call recv() or recvfrom() * simultaneously, only one will succeed and the rest will get the error. * */ #define THIS_FILE "ioq_select" /* * The select ioqueue relies on socket functions (pj_sock_xxx()) to return * the correct error code. */ #if PJ_RETURN_OS_ERROR(100) != PJ_STATUS_FROM_OS(100) # error "Error reporting must be enabled for this function to work!" #endif /* * During debugging build, VALIDATE_FD_SET is set. * This will check the validity of the fd_sets. */ /* #if defined(PJ_DEBUG) && PJ_DEBUG != 0 # define VALIDATE_FD_SET 1 #else # define VALIDATE_FD_SET 0 #endif */ #define VALIDATE_FD_SET 0 #if 0 # define TRACE__(args) PJ_LOG(3,args) #else # define TRACE__(args) #endif /* * This describes each key. */ struct pj_ioqueue_key_t { DECLARE_COMMON_KEY }; /* * This describes the I/O queue itself. */ struct pj_ioqueue_t { DECLARE_COMMON_IOQUEUE unsigned max, count; /* Max and current key count */ int nfds; /* The largest fd value (for select)*/ pj_ioqueue_key_t active_list; /* List of active keys. */ pj_fd_set_t rfdset; pj_fd_set_t wfdset; #if PJ_HAS_TCP pj_fd_set_t xfdset; #endif #if PJ_IOQUEUE_HAS_SAFE_UNREG pj_mutex_t *ref_cnt_mutex; pj_ioqueue_key_t closing_list; pj_ioqueue_key_t free_list; #endif }; /* Proto */ #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 static pj_status_t replace_udp_sock(pj_ioqueue_key_t *h); #endif /* Include implementation for common abstraction after we declare * pj_ioqueue_key_t and pj_ioqueue_t. */ #include "ioqueue_common_abs.c" #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Scan closing keys to be put to free list again */ static void scan_closing_keys(pj_ioqueue_t *ioqueue); #endif /* * pj_ioqueue_name() */ PJ_DEF(const char*) pj_ioqueue_name(void) { return "select"; } /* * Scan the socket descriptor sets for the largest descriptor. * This value is needed by select(). */ #if defined(PJ_SELECT_NEEDS_NFDS) && PJ_SELECT_NEEDS_NFDS!=0 static void rescan_fdset(pj_ioqueue_t *ioqueue) { pj_ioqueue_key_t *key = ioqueue->active_list.next; int max = 0; while (key != &ioqueue->active_list) { if (key->fd > max) max = key->fd; key = key->next; } ioqueue->nfds = max; } #else static void rescan_fdset(pj_ioqueue_t *ioqueue) { ioqueue->nfds = FD_SETSIZE-1; } #endif /* * pj_ioqueue_create() * * Create select ioqueue. */ PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, pj_size_t max_fd, pj_ioqueue_t **p_ioqueue) { pj_ioqueue_t *ioqueue; pj_lock_t *lock; unsigned i; pj_status_t rc; /* Check that arguments are valid. */ PJ_ASSERT_RETURN(pool != NULL && p_ioqueue != NULL && max_fd > 0 && max_fd <= PJ_IOQUEUE_MAX_HANDLES, PJ_EINVAL); /* Check that size of pj_ioqueue_op_key_t is sufficient */ PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >= sizeof(union operation_key), PJ_EBUG); /* Create and init common ioqueue stuffs */ ioqueue = PJ_POOL_ALLOC_T(pool, pj_ioqueue_t); ioqueue_init(ioqueue); ioqueue->max = (unsigned)max_fd; ioqueue->count = 0; PJ_FD_ZERO(&ioqueue->rfdset); PJ_FD_ZERO(&ioqueue->wfdset); #if PJ_HAS_TCP PJ_FD_ZERO(&ioqueue->xfdset); #endif pj_list_init(&ioqueue->active_list); rescan_fdset(ioqueue); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* When safe unregistration is used (the default), we pre-create * all keys and put them in the free list. */ /* Mutex to protect key's reference counter * We don't want to use key's mutex or ioqueue's mutex because * that would create deadlock situation in some cases. */ rc = pj_mutex_create_simple(pool, NULL, &ioqueue->ref_cnt_mutex); if (rc != PJ_SUCCESS) return rc; /* Init key list */ pj_list_init(&ioqueue->free_list); pj_list_init(&ioqueue->closing_list); /* Pre-create all keys according to max_fd */ for (i=0; iref_count = 0; rc = pj_lock_create_recursive_mutex(pool, NULL, &key->lock); if (rc != PJ_SUCCESS) { key = ioqueue->free_list.next; while (key != &ioqueue->free_list) { pj_lock_destroy(key->lock); key = key->next; } pj_mutex_destroy(ioqueue->ref_cnt_mutex); return rc; } pj_list_push_back(&ioqueue->free_list, key); } #endif /* Create and init ioqueue mutex */ rc = pj_lock_create_simple_mutex(pool, "ioq%p", &lock); if (rc != PJ_SUCCESS) return rc; rc = pj_ioqueue_set_lock(ioqueue, lock, PJ_TRUE); if (rc != PJ_SUCCESS) return rc; PJ_LOG(4, ("pjlib", "select() I/O Queue created (%p)", ioqueue)); *p_ioqueue = ioqueue; return PJ_SUCCESS; } /* * pj_ioqueue_destroy() * * Destroy ioqueue. */ PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue) { pj_ioqueue_key_t *key; PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL); pj_lock_acquire(ioqueue->lock); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Destroy reference counters */ key = ioqueue->active_list.next; while (key != &ioqueue->active_list) { pj_lock_destroy(key->lock); key = key->next; } key = ioqueue->closing_list.next; while (key != &ioqueue->closing_list) { pj_lock_destroy(key->lock); key = key->next; } key = ioqueue->free_list.next; while (key != &ioqueue->free_list) { pj_lock_destroy(key->lock); key = key->next; } pj_mutex_destroy(ioqueue->ref_cnt_mutex); #endif return ioqueue_destroy(ioqueue); } /* * pj_ioqueue_register_sock() * * Register socket handle to ioqueue. */ PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool, pj_ioqueue_t *ioqueue, pj_sock_t sock, pj_grp_lock_t *grp_lock, void *user_data, const pj_ioqueue_callback *cb, pj_ioqueue_key_t **p_key) { pj_ioqueue_key_t *key = NULL; #if defined(PJ_WIN32) && PJ_WIN32!=0 || \ defined(PJ_WIN64) && PJ_WIN64 != 0 || \ defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 u_long value; #else pj_uint32_t value; #endif pj_status_t rc = PJ_SUCCESS; PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET && cb && p_key, PJ_EINVAL); /* On platforms with fd_set containing fd bitmap such as *nix family, * avoid potential memory corruption caused by select() when given * an fd that is higher than FD_SETSIZE. */ if (sizeof(fd_set) < FD_SETSIZE && sock >= FD_SETSIZE) { PJ_LOG(4, ("pjlib", "Failed to register socket to ioqueue because " "socket fd is too big (fd=%d/FD_SETSIZE=%d)", sock, FD_SETSIZE)); return PJ_ETOOBIG; } pj_lock_acquire(ioqueue->lock); if (ioqueue->count >= ioqueue->max) { rc = PJ_ETOOMANY; goto on_return; } /* If safe unregistration (PJ_IOQUEUE_HAS_SAFE_UNREG) is used, get * the key from the free list. Otherwise allocate a new one. */ #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Scan closing_keys first to let them come back to free_list */ scan_closing_keys(ioqueue); pj_assert(!pj_list_empty(&ioqueue->free_list)); if (pj_list_empty(&ioqueue->free_list)) { rc = PJ_ETOOMANY; goto on_return; } key = ioqueue->free_list.next; pj_list_erase(key); #else key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); #endif rc = ioqueue_init_key(pool, ioqueue, key, sock, grp_lock, user_data, cb); if (rc != PJ_SUCCESS) { key = NULL; goto on_return; } /* Set socket to nonblocking. */ value = 1; #if defined(PJ_WIN32) && PJ_WIN32!=0 || \ defined(PJ_WIN64) && PJ_WIN64 != 0 || \ defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 if (ioctlsocket(sock, FIONBIO, &value)) { #else if (ioctl(sock, FIONBIO, &value)) { #endif rc = pj_get_netos_error(); goto on_return; } /* Put in active list. */ pj_list_insert_before(&ioqueue->active_list, key); ++ioqueue->count; /* Rescan fdset to get max descriptor */ rescan_fdset(ioqueue); on_return: /* On error, socket may be left in non-blocking mode. */ if (rc != PJ_SUCCESS) { if (key && key->grp_lock) pj_grp_lock_dec_ref_dbg(key->grp_lock, "ioqueue", 0); } *p_key = key; pj_lock_release(ioqueue->lock); return rc; } PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, pj_ioqueue_t *ioqueue, pj_sock_t sock, void *user_data, const pj_ioqueue_callback *cb, pj_ioqueue_key_t **p_key) { return pj_ioqueue_register_sock2(pool, ioqueue, sock, NULL, user_data, cb, p_key); } #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Increment key's reference counter */ static void increment_counter(pj_ioqueue_key_t *key) { pj_mutex_lock(key->ioqueue->ref_cnt_mutex); ++key->ref_count; pj_mutex_unlock(key->ioqueue->ref_cnt_mutex); } /* Decrement the key's reference counter, and when the counter reach zero, * destroy the key. * * Note: MUST NOT CALL THIS FUNCTION WHILE HOLDING ioqueue's LOCK. */ static void decrement_counter(pj_ioqueue_key_t *key) { pj_lock_acquire(key->ioqueue->lock); pj_mutex_lock(key->ioqueue->ref_cnt_mutex); --key->ref_count; if (key->ref_count == 0) { pj_assert(key->closing == 1); pj_gettickcount(&key->free_time); key->free_time.msec += PJ_IOQUEUE_KEY_FREE_DELAY; pj_time_val_normalize(&key->free_time); pj_list_erase(key); pj_list_push_back(&key->ioqueue->closing_list, key); /* Rescan fdset to get max descriptor */ rescan_fdset(key->ioqueue); } pj_mutex_unlock(key->ioqueue->ref_cnt_mutex); pj_lock_release(key->ioqueue->lock); } #endif /* * pj_ioqueue_unregister() * * Unregister handle from ioqueue. */ PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key) { pj_ioqueue_t *ioqueue; PJ_ASSERT_RETURN(key, PJ_EINVAL); ioqueue = key->ioqueue; /* Lock the key to make sure no callback is simultaneously modifying * the key. We need to lock the key before ioqueue here to prevent * deadlock. */ pj_ioqueue_lock_key(key); /* Also lock ioqueue */ pj_lock_acquire(ioqueue->lock); pj_assert(ioqueue->count > 0); --ioqueue->count; #if !PJ_IOQUEUE_HAS_SAFE_UNREG /* Ticket #520, key will be erased more than once */ pj_list_erase(key); #endif PJ_FD_CLR(key->fd, &ioqueue->rfdset); PJ_FD_CLR(key->fd, &ioqueue->wfdset); #if PJ_HAS_TCP PJ_FD_CLR(key->fd, &ioqueue->xfdset); #endif /* Close socket. */ pj_sock_close(key->fd); /* Clear callback */ key->cb.on_accept_complete = NULL; key->cb.on_connect_complete = NULL; key->cb.on_read_complete = NULL; key->cb.on_write_complete = NULL; /* Must release ioqueue lock first before decrementing counter, to * prevent deadlock. */ pj_lock_release(ioqueue->lock); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Mark key is closing. */ key->closing = 1; /* Decrement counter. */ decrement_counter(key); /* Done. */ if (key->grp_lock) { /* just dec_ref and unlock. we will set grp_lock to NULL * elsewhere */ pj_grp_lock_t *grp_lock = key->grp_lock; // Don't set grp_lock to NULL otherwise the other thread // will crash. Just leave it as dangling pointer, but this // should be safe //key->grp_lock = NULL; pj_grp_lock_dec_ref_dbg(grp_lock, "ioqueue", 0); pj_grp_lock_release(grp_lock); } else { pj_ioqueue_unlock_key(key); } #else if (key->grp_lock) { /* set grp_lock to NULL and unlock */ pj_grp_lock_t *grp_lock = key->grp_lock; // Don't set grp_lock to NULL otherwise the other thread // will crash. Just leave it as dangling pointer, but this // should be safe //key->grp_lock = NULL; pj_grp_lock_dec_ref_dbg(grp_lock, "ioqueue", 0); pj_grp_lock_release(grp_lock); } else { pj_ioqueue_unlock_key(key); } pj_lock_destroy(key->lock); #endif return PJ_SUCCESS; } /* This supposed to check whether the fd_set values are consistent * with the operation currently set in each key. */ #if VALIDATE_FD_SET static void validate_sets(const pj_ioqueue_t *ioqueue, const pj_fd_set_t *rfdset, const pj_fd_set_t *wfdset, const pj_fd_set_t *xfdset) { pj_ioqueue_key_t *key; /* * This basicly would not work anymore. * We need to lock key before performing the check, but we can't do * so because we're holding ioqueue mutex. If we acquire key's mutex * now, the will cause deadlock. */ pj_assert(0); key = ioqueue->active_list.next; while (key != &ioqueue->active_list) { if (!pj_list_empty(&key->read_list) #if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 || !pj_list_empty(&key->accept_list) #endif ) { pj_assert(PJ_FD_ISSET(key->fd, rfdset)); } else { pj_assert(PJ_FD_ISSET(key->fd, rfdset) == 0); } if (!pj_list_empty(&key->write_list) #if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 || key->connecting #endif ) { pj_assert(PJ_FD_ISSET(key->fd, wfdset)); } else { pj_assert(PJ_FD_ISSET(key->fd, wfdset) == 0); } #if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 if (key->connecting) { pj_assert(PJ_FD_ISSET(key->fd, xfdset)); } else { pj_assert(PJ_FD_ISSET(key->fd, xfdset) == 0); } #endif /* PJ_HAS_TCP */ key = key->next; } } #endif /* VALIDATE_FD_SET */ /* ioqueue_remove_from_set() * This function is called from ioqueue_dispatch_event() to instruct * the ioqueue to remove the specified descriptor from ioqueue's descriptor * set for the specified event. */ static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *key, enum ioqueue_event_type event_type) { pj_lock_acquire(ioqueue->lock); if (event_type == READABLE_EVENT) PJ_FD_CLR((pj_sock_t)key->fd, &ioqueue->rfdset); else if (event_type == WRITEABLE_EVENT) PJ_FD_CLR((pj_sock_t)key->fd, &ioqueue->wfdset); #if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0 else if (event_type == EXCEPTION_EVENT) PJ_FD_CLR((pj_sock_t)key->fd, &ioqueue->xfdset); #endif else pj_assert(0); pj_lock_release(ioqueue->lock); } /* * ioqueue_add_to_set() * This function is called from pj_ioqueue_recv(), pj_ioqueue_send() etc * to instruct the ioqueue to add the specified handle to ioqueue's descriptor * set for the specified event. */ static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *key, enum ioqueue_event_type event_type ) { pj_lock_acquire(ioqueue->lock); if (event_type == READABLE_EVENT) PJ_FD_SET((pj_sock_t)key->fd, &ioqueue->rfdset); else if (event_type == WRITEABLE_EVENT) PJ_FD_SET((pj_sock_t)key->fd, &ioqueue->wfdset); #if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0 else if (event_type == EXCEPTION_EVENT) PJ_FD_SET((pj_sock_t)key->fd, &ioqueue->xfdset); #endif else pj_assert(0); pj_lock_release(ioqueue->lock); } #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Scan closing keys to be put to free list again */ static void scan_closing_keys(pj_ioqueue_t *ioqueue) { pj_time_val now; pj_ioqueue_key_t *h; pj_gettickcount(&now); h = ioqueue->closing_list.next; while (h != &ioqueue->closing_list) { pj_ioqueue_key_t *next = h->next; pj_assert(h->closing != 0); if (PJ_TIME_VAL_GTE(now, h->free_time)) { pj_list_erase(h); // Don't set grp_lock to NULL otherwise the other thread // will crash. Just leave it as dangling pointer, but this // should be safe //h->grp_lock = NULL; pj_list_push_back(&ioqueue->free_list, h); } h = next; } } #endif #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 static pj_status_t replace_udp_sock(pj_ioqueue_key_t *h) { enum flags { HAS_PEER_ADDR = 1, HAS_QOS = 2 }; pj_sock_t old_sock, new_sock = PJ_INVALID_SOCKET; pj_sockaddr local_addr, rem_addr; int val, addr_len; pj_fd_set_t *fds[3]; unsigned i, fds_cnt, flags=0; pj_qos_params qos_params; unsigned msec; pj_status_t status; pj_lock_acquire(h->ioqueue->lock); old_sock = h->fd; /* Can only replace UDP socket */ pj_assert(h->fd_type == pj_SOCK_DGRAM()); PJ_LOG(4,(THIS_FILE, "Attempting to replace UDP socket %d", old_sock)); /* Investigate the old socket */ addr_len = sizeof(local_addr); status = pj_sock_getsockname(old_sock, &local_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; addr_len = sizeof(rem_addr); status = pj_sock_getpeername(old_sock, &rem_addr, &addr_len); if (status == PJ_SUCCESS) flags |= HAS_PEER_ADDR; status = pj_sock_get_qos_params(old_sock, &qos_params); if (status == PJ_SUCCESS) flags |= HAS_QOS; /* We're done with the old socket, close it otherwise we'll get * error in bind() */ pj_sock_close(old_sock); /* Prepare the new socket */ status = pj_sock_socket(local_addr.addr.sa_family, PJ_SOCK_DGRAM, 0, &new_sock); if (status != PJ_SUCCESS) goto on_error; /* Even after the socket is closed, we'll still get "Address in use" * errors, so force it with SO_REUSEADDR */ val = 1; status = pj_sock_setsockopt(new_sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); if (status != PJ_SUCCESS) goto on_error; /* The loop is silly, but what else can we do? */ addr_len = pj_sockaddr_get_len(&local_addr); for (msec=20; ; msec<1000? msec=msec*2 : 1000) { status = pj_sock_bind(new_sock, &local_addr, addr_len); if (status != PJ_STATUS_FROM_OS(EADDRINUSE)) break; PJ_LOG(4,(THIS_FILE, "Address is still in use, retrying..")); pj_thread_sleep(msec); } if (status != PJ_SUCCESS) goto on_error; if (flags & HAS_QOS) { status = pj_sock_set_qos_params(new_sock, &qos_params); if (status != PJ_SUCCESS) goto on_error; } if (flags & HAS_PEER_ADDR) { status = pj_sock_connect(new_sock, &rem_addr, addr_len); if (status != PJ_SUCCESS) goto on_error; } /* Set socket to nonblocking. */ val = 1; #if defined(PJ_WIN32) && PJ_WIN32!=0 || \ defined(PJ_WIN64) && PJ_WIN64 != 0 || \ defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 if (ioctlsocket(new_sock, FIONBIO, &val)) { #else if (ioctl(new_sock, FIONBIO, &val)) { #endif status = pj_get_netos_error(); goto on_error; } /* Replace the occurrence of old socket with new socket in the * fd sets. */ fds_cnt = 0; fds[fds_cnt++] = &h->ioqueue->rfdset; fds[fds_cnt++] = &h->ioqueue->wfdset; #if PJ_HAS_TCP fds[fds_cnt++] = &h->ioqueue->xfdset; #endif for (i=0; ifd = new_sock; PJ_LOG(4,(THIS_FILE, "UDP has been replaced successfully!")); pj_lock_release(h->ioqueue->lock); return PJ_SUCCESS; on_error: if (new_sock != PJ_INVALID_SOCKET) pj_sock_close(new_sock); PJ_PERROR(1,(THIS_FILE, status, "Error replacing socket")); pj_lock_release(h->ioqueue->lock); return status; } #endif /* * pj_ioqueue_poll() * * Few things worth written: * * - we used to do only one callback called per poll, but it didn't go * very well. The reason is because on some situation, the write * callback gets called all the time, thus doesn't give the read * callback to get called. This happens, for example, when user * submit write operation inside the write callback. * As the result, we changed the behaviour so that now multiple * callbacks are called in a single poll. It should be fast too, * just that we need to be carefull with the ioqueue data structs. * * - to guarantee preemptiveness etc, the poll function must strictly * work on fd_set copy of the ioqueue (not the original one). */ PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout) { pj_fd_set_t rfdset, wfdset, xfdset; int nfds; int i, count, event_cnt, processed_cnt; pj_ioqueue_key_t *h; enum { MAX_EVENTS = PJ_IOQUEUE_MAX_CAND_EVENTS }; struct event { pj_ioqueue_key_t *key; enum ioqueue_event_type event_type; } event[MAX_EVENTS]; PJ_ASSERT_RETURN(ioqueue, -PJ_EINVAL); /* Lock ioqueue before making fd_set copies */ pj_lock_acquire(ioqueue->lock); /* We will only do select() when there are sockets to be polled. * Otherwise select() will return error. */ if (PJ_FD_COUNT(&ioqueue->rfdset)==0 && PJ_FD_COUNT(&ioqueue->wfdset)==0 #if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0 && PJ_FD_COUNT(&ioqueue->xfdset)==0 #endif ) { #if PJ_IOQUEUE_HAS_SAFE_UNREG scan_closing_keys(ioqueue); #endif pj_lock_release(ioqueue->lock); TRACE__((THIS_FILE, " poll: no fd is set")); if (timeout) pj_thread_sleep(PJ_TIME_VAL_MSEC(*timeout)); return 0; } /* Copy ioqueue's pj_fd_set_t to local variables. */ pj_memcpy(&rfdset, &ioqueue->rfdset, sizeof(pj_fd_set_t)); pj_memcpy(&wfdset, &ioqueue->wfdset, sizeof(pj_fd_set_t)); #if PJ_HAS_TCP pj_memcpy(&xfdset, &ioqueue->xfdset, sizeof(pj_fd_set_t)); #else PJ_FD_ZERO(&xfdset); #endif #if VALIDATE_FD_SET validate_sets(ioqueue, &rfdset, &wfdset, &xfdset); #endif nfds = ioqueue->nfds; /* Unlock ioqueue before select(). */ pj_lock_release(ioqueue->lock); count = pj_sock_select(nfds+1, &rfdset, &wfdset, &xfdset, timeout); if (count == 0) return 0; else if (count < 0) return -pj_get_netos_error(); /* Scan descriptor sets for event and add the events in the event * array to be processed later in this function. We do this so that * events can be processed in parallel without holding ioqueue lock. */ pj_lock_acquire(ioqueue->lock); event_cnt = 0; /* Scan for writable sockets first to handle piggy-back data * coming with accept(). */ for (h = ioqueue->active_list.next; h != &ioqueue->active_list && event_cnt < MAX_EVENTS; h = h->next) { if ( (key_has_pending_write(h) || key_has_pending_connect(h)) && PJ_FD_ISSET(h->fd, &wfdset) && !IS_CLOSING(h)) { #if PJ_IOQUEUE_HAS_SAFE_UNREG increment_counter(h); #endif event[event_cnt].key = h; event[event_cnt].event_type = WRITEABLE_EVENT; ++event_cnt; } /* Scan for readable socket. */ if ((key_has_pending_read(h) || key_has_pending_accept(h)) && PJ_FD_ISSET(h->fd, &rfdset) && !IS_CLOSING(h) && event_cnt < MAX_EVENTS) { #if PJ_IOQUEUE_HAS_SAFE_UNREG increment_counter(h); #endif event[event_cnt].key = h; event[event_cnt].event_type = READABLE_EVENT; ++event_cnt; } #if PJ_HAS_TCP if (key_has_pending_connect(h) && PJ_FD_ISSET(h->fd, &xfdset) && !IS_CLOSING(h) && event_cnt < MAX_EVENTS) { #if PJ_IOQUEUE_HAS_SAFE_UNREG increment_counter(h); #endif event[event_cnt].key = h; event[event_cnt].event_type = EXCEPTION_EVENT; ++event_cnt; } #endif } for (i=0; igrp_lock) pj_grp_lock_add_ref_dbg(event[i].key->grp_lock, "ioqueue", 0); } PJ_RACE_ME(5); pj_lock_release(ioqueue->lock); PJ_RACE_ME(5); processed_cnt = 0; /* Now process all events. The dispatch functions will take care * of locking in each of the key */ for (i=0; igrp_lock) pj_grp_lock_dec_ref_dbg(event[i].key->grp_lock, "ioqueue", 0); } TRACE__((THIS_FILE, " poll: count=%d events=%d processed=%d", count, event_cnt, processed_cnt)); return processed_cnt; } ================================================ FILE: deps/pjsip/pjlib/src/pj/ioqueue_symbian.cpp ================================================ /* $Id: ioqueue_symbian.cpp 4374 2013-02-27 07:15:57Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include "os_symbian.h" class CIoqueueCallback; /* * IO Queue structure. */ struct pj_ioqueue_t { int eventCount; }; ///////////////////////////////////////////////////////////////////////////// // Class to encapsulate asynchronous socket operation. // class CIoqueueCallback : public CActive { public: static CIoqueueCallback* NewL(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *key, pj_sock_t sock, const pj_ioqueue_callback *cb, void *user_data); // // Start asynchronous recv() operation // pj_status_t StartRead(pj_ioqueue_op_key_t *op_key, void *buf, pj_ssize_t *size, unsigned flags, pj_sockaddr_t *addr, int *addrlen); // // Start asynchronous accept() operation. // pj_status_t StartAccept(pj_ioqueue_op_key_t *op_key, pj_sock_t *new_sock, pj_sockaddr_t *local, pj_sockaddr_t *remote, int *addrlen ); // // Completion callback. // void RunL(); // // CActive's DoCancel() // void DoCancel(); // // Cancel operation and call callback. // void CancelOperation(pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_status); // // Accessors // void* get_user_data() const { return user_data_; } void set_user_data(void *user_data) { user_data_ = user_data; } pj_ioqueue_op_key_t *get_op_key() const { return pending_data_.common_.op_key_; } CPjSocket* get_pj_socket() { return sock_; } private: // Type of pending operation. enum Type { TYPE_NONE, TYPE_READ, TYPE_ACCEPT, }; // Static data. pj_ioqueue_t *ioqueue_; pj_ioqueue_key_t *key_; CPjSocket *sock_; pj_ioqueue_callback cb_; void *user_data_; // Symbian data. TPtr8 aBufferPtr_; TInetAddr aAddress_; // Application data. Type type_; union Pending_Data { struct Common { pj_ioqueue_op_key_t *op_key_; } common_; struct Pending_Read { pj_ioqueue_op_key_t *op_key_; pj_sockaddr_t *addr_; int *addrlen_; } read_; struct Pending_Accept { pj_ioqueue_op_key_t *op_key_; pj_sock_t *new_sock_; pj_sockaddr_t *local_; pj_sockaddr_t *remote_; int *addrlen_; } accept_; }; union Pending_Data pending_data_; RSocket blank_sock_; CIoqueueCallback(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *key, pj_sock_t sock, const pj_ioqueue_callback *cb, void *user_data) : CActive(CActive::EPriorityStandard), ioqueue_(ioqueue), key_(key), sock_((CPjSocket*)sock), user_data_(user_data), aBufferPtr_(NULL, 0), type_(TYPE_NONE) { pj_memcpy(&cb_, cb, sizeof(*cb)); } void ConstructL() { CActiveScheduler::Add(this); } void HandleReadCompletion(); CPjSocket *HandleAcceptCompletion(); }; CIoqueueCallback* CIoqueueCallback::NewL(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *key, pj_sock_t sock, const pj_ioqueue_callback *cb, void *user_data) { CIoqueueCallback *self = new CIoqueueCallback(ioqueue, key, sock, cb, user_data); CleanupStack::PushL(self); self->ConstructL(); CleanupStack::Pop(self); return self; } // // Start asynchronous recv() operation // pj_status_t CIoqueueCallback::StartRead(pj_ioqueue_op_key_t *op_key, void *buf, pj_ssize_t *size, unsigned flags, pj_sockaddr_t *addr, int *addrlen) { PJ_ASSERT_RETURN(IsActive()==false, PJ_EBUSY); PJ_ASSERT_RETURN(pending_data_.common_.op_key_==NULL, PJ_EBUSY); flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC; pending_data_.read_.op_key_ = op_key; pending_data_.read_.addr_ = addr; pending_data_.read_.addrlen_ = addrlen; aBufferPtr_.Set((TUint8*)buf, 0, (TInt)*size); type_ = TYPE_READ; if (addr && addrlen) { sock_->Socket().RecvFrom(aBufferPtr_, aAddress_, flags, iStatus); } else { aAddress_.SetAddress(0); aAddress_.SetPort(0); if (sock_->IsDatagram()) { sock_->Socket().Recv(aBufferPtr_, flags, iStatus); } else { // Using static like this is not pretty, but we don't need to use // the value anyway, hence doing it like this is probably most // optimal. static TSockXfrLength len; sock_->Socket().RecvOneOrMore(aBufferPtr_, flags, iStatus, len); } } SetActive(); return PJ_EPENDING; } // // Start asynchronous accept() operation. // pj_status_t CIoqueueCallback::StartAccept(pj_ioqueue_op_key_t *op_key, pj_sock_t *new_sock, pj_sockaddr_t *local, pj_sockaddr_t *remote, int *addrlen ) { PJ_ASSERT_RETURN(IsActive()==false, PJ_EBUSY); PJ_ASSERT_RETURN(pending_data_.common_.op_key_==NULL, PJ_EBUSY); // addrlen must be specified if local or remote is specified PJ_ASSERT_RETURN((!local && !remote) || (addrlen && *addrlen), PJ_EINVAL); pending_data_.accept_.op_key_ = op_key; pending_data_.accept_.new_sock_ = new_sock; pending_data_.accept_.local_ = local; pending_data_.accept_.remote_ = remote; pending_data_.accept_.addrlen_ = addrlen; // Create blank socket blank_sock_.Open(PjSymbianOS::Instance()->SocketServ()); type_ = TYPE_ACCEPT; sock_->Socket().Accept(blank_sock_, iStatus); SetActive(); return PJ_EPENDING; } // // Handle asynchronous RecvFrom() completion // void CIoqueueCallback::HandleReadCompletion() { if (pending_data_.read_.addr_ && pending_data_.read_.addrlen_) { PjSymbianOS::Addr2pj(aAddress_, *(pj_sockaddr*)pending_data_.read_.addr_, pending_data_.read_.addrlen_); pending_data_.read_.addr_ = NULL; pending_data_.read_.addrlen_ = NULL; } pending_data_.read_.op_key_ = NULL; } // // Handle asynchronous Accept() completion. // CPjSocket *CIoqueueCallback::HandleAcceptCompletion() { CPjSocket *pjNewSock = new CPjSocket(get_pj_socket()->GetAf(), get_pj_socket()->GetSockType(), blank_sock_); int addrlen = 0; if (pending_data_.accept_.new_sock_) { *pending_data_.accept_.new_sock_ = (pj_sock_t)pjNewSock; pending_data_.accept_.new_sock_ = NULL; } if (pending_data_.accept_.local_) { TInetAddr aAddr; pj_sockaddr *ptr_sockaddr; blank_sock_.LocalName(aAddr); ptr_sockaddr = (pj_sockaddr*)pending_data_.accept_.local_; addrlen = *pending_data_.accept_.addrlen_; PjSymbianOS::Addr2pj(aAddr, *ptr_sockaddr, &addrlen); pending_data_.accept_.local_ = NULL; } if (pending_data_.accept_.remote_) { TInetAddr aAddr; pj_sockaddr *ptr_sockaddr; blank_sock_.RemoteName(aAddr); ptr_sockaddr = (pj_sockaddr*)pending_data_.accept_.remote_; addrlen = *pending_data_.accept_.addrlen_; PjSymbianOS::Addr2pj(aAddr, *ptr_sockaddr, &addrlen); pending_data_.accept_.remote_ = NULL; } if (pending_data_.accept_.addrlen_) { if (addrlen == 0) { if (pjNewSock->GetAf() == PJ_AF_INET) addrlen = sizeof(pj_sockaddr_in); else if (pjNewSock->GetAf() == PJ_AF_INET6) addrlen = sizeof(pj_sockaddr_in6); else { pj_assert(!"Unsupported address family"); } } *pending_data_.accept_.addrlen_ = addrlen; pending_data_.accept_.addrlen_ = NULL; } return pjNewSock; } // // Completion callback. // void CIoqueueCallback::RunL() { pj_ioqueue_t *ioq = ioqueue_; Type cur_type = type_; type_ = TYPE_NONE; if (cur_type == TYPE_READ) { // // Completion of asynchronous RecvFrom() // /* Clear op_key (save it to temp variable first!) */ pj_ioqueue_op_key_t *op_key = pending_data_.read_.op_key_; pending_data_.read_.op_key_ = NULL; // Handle failure condition if (iStatus != KErrNone) { if (cb_.on_read_complete) { cb_.on_read_complete( key_, op_key, -PJ_RETURN_OS_ERROR(iStatus.Int())); } return; } HandleReadCompletion(); /* Call callback */ if (cb_.on_read_complete) { cb_.on_read_complete(key_, op_key, aBufferPtr_.Length()); } } else if (cur_type == TYPE_ACCEPT) { // // Completion of asynchronous Accept() // /* Clear op_key (save it to temp variable first!) */ pj_ioqueue_op_key_t *op_key = pending_data_.read_.op_key_; pending_data_.read_.op_key_ = NULL; // Handle failure condition if (iStatus != KErrNone) { if (pending_data_.accept_.new_sock_) *pending_data_.accept_.new_sock_ = PJ_INVALID_SOCKET; if (cb_.on_accept_complete) { cb_.on_accept_complete( key_, op_key, PJ_INVALID_SOCKET, -PJ_RETURN_OS_ERROR(iStatus.Int())); } return; } CPjSocket *pjNewSock = HandleAcceptCompletion(); // Call callback. if (cb_.on_accept_complete) { cb_.on_accept_complete( key_, op_key, (pj_sock_t)pjNewSock, PJ_SUCCESS); } } ioq->eventCount++; } // // CActive's DoCancel() // void CIoqueueCallback::DoCancel() { if (type_ == TYPE_READ) sock_->Socket().CancelRecv(); else if (type_ == TYPE_ACCEPT) sock_->Socket().CancelAccept(); type_ = TYPE_NONE; pending_data_.common_.op_key_ = NULL; } // // Cancel operation and call callback. // void CIoqueueCallback::CancelOperation(pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_status) { Type cur_type = type_; pj_assert(op_key == pending_data_.common_.op_key_); Cancel(); if (cur_type == TYPE_READ) { if (cb_.on_read_complete) cb_.on_read_complete(key_, op_key, bytes_status); } else if (cur_type == TYPE_ACCEPT) ; } ///////////////////////////////////////////////////////////////////////////// /* * IO Queue key structure. */ struct pj_ioqueue_key_t { CIoqueueCallback *cbObj; }; /* * Return the name of the ioqueue implementation. */ PJ_DEF(const char*) pj_ioqueue_name(void) { return "ioqueue-symbian"; } /* * Create a new I/O Queue framework. */ PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, pj_size_t max_fd, pj_ioqueue_t **p_ioqueue) { pj_ioqueue_t *ioq; PJ_UNUSED_ARG(max_fd); ioq = PJ_POOL_ZALLOC_T(pool, pj_ioqueue_t); *p_ioqueue = ioq; return PJ_SUCCESS; } /* * Destroy the I/O queue. */ PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioq ) { PJ_UNUSED_ARG(ioq); return PJ_SUCCESS; } /* * Set the lock object to be used by the I/O Queue. */ PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioq, pj_lock_t *lock, pj_bool_t auto_delete ) { /* Don't really need lock for now */ PJ_UNUSED_ARG(ioq); if (auto_delete) { pj_lock_destroy(lock); } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_ioqueue_set_default_concurrency(pj_ioqueue_t *ioqueue, pj_bool_t allow) { /* Not supported, just return PJ_SUCCESS silently */ PJ_UNUSED_ARG(ioqueue); PJ_UNUSED_ARG(allow); return PJ_SUCCESS; } /* * Register a socket to the I/O queue framework. */ PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, pj_ioqueue_t *ioq, pj_sock_t sock, void *user_data, const pj_ioqueue_callback *cb, pj_ioqueue_key_t **p_key ) { pj_ioqueue_key_t *key; key = PJ_POOL_ZALLOC_T(pool, pj_ioqueue_key_t); key->cbObj = CIoqueueCallback::NewL(ioq, key, sock, cb, user_data); *p_key = key; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool, pj_ioqueue_t *ioqueue, pj_sock_t sock, pj_grp_lock_t *grp_lock, void *user_data, const pj_ioqueue_callback *cb, pj_ioqueue_key_t **p_key) { PJ_UNUSED_ARG(grp_lock); return pj_ioqueue_register_sock(pool, ioqueue, sock, user_data, cb, p_key); } /* * Unregister from the I/O Queue framework. */ PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key ) { if (key == NULL || key->cbObj == NULL) return PJ_SUCCESS; // Cancel pending async object if (key->cbObj) { key->cbObj->Cancel(); } // Close socket. key->cbObj->get_pj_socket()->Socket().Close(); delete key->cbObj->get_pj_socket(); // Delete async object. if (key->cbObj) { delete key->cbObj; key->cbObj = NULL; } return PJ_SUCCESS; } /* * Get user data associated with an ioqueue key. */ PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) { return key->cbObj->get_user_data(); } /* * Set or change the user data to be associated with the file descriptor or * handle or socket descriptor. */ PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key, void *user_data, void **old_data) { if (old_data) *old_data = key->cbObj->get_user_data(); key->cbObj->set_user_data(user_data); return PJ_SUCCESS; } /* * Initialize operation key. */ PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key, pj_size_t size ) { pj_bzero(op_key, size); } /* * Check if operation is pending on the specified operation key. */ PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key ) { return key->cbObj->get_op_key()==op_key && key->cbObj->IsActive(); } /* * Post completion status to the specified operation key and call the * appropriate callback. */ PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_status ) { if (pj_ioqueue_is_pending(key, op_key)) { key->cbObj->CancelOperation(op_key, bytes_status); } return PJ_SUCCESS; } #if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 /** * Instruct I/O Queue to accept incoming connection on the specified * listening socket. */ PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t *new_sock, pj_sockaddr_t *local, pj_sockaddr_t *remote, int *addrlen ) { return key->cbObj->StartAccept(op_key, new_sock, local, remote, addrlen); } /* * Initiate non-blocking socket connect. */ PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key, const pj_sockaddr_t *addr, int addrlen ) { pj_status_t status; RSocket &rSock = key->cbObj->get_pj_socket()->Socket(); TInetAddr inetAddr; TRequestStatus reqStatus; // Return failure if access point is marked as down by app. PJ_SYMBIAN_CHECK_CONNECTION(); // Convert address status = PjSymbianOS::pj2Addr(*(const pj_sockaddr*)addr, addrlen, inetAddr); if (status != PJ_SUCCESS) return status; // We don't support async connect for now. PJ_TODO(IOQUEUE_SUPPORT_ASYNC_CONNECT); rSock.Connect(inetAddr, reqStatus); User::WaitForRequest(reqStatus); if (reqStatus == KErrNone) return PJ_SUCCESS; return PJ_RETURN_OS_ERROR(reqStatus.Int()); } #endif /* PJ_HAS_TCP */ /* * Poll the I/O Queue for completed events. */ PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioq, const pj_time_val *timeout) { /* Polling is not necessary on Symbian, since all async activities * are registered to active scheduler. */ PJ_UNUSED_ARG(ioq); PJ_UNUSED_ARG(timeout); return 0; } /* * Instruct the I/O Queue to read from the specified handle. */ PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, void *buffer, pj_ssize_t *length, pj_uint32_t flags ) { // If socket has reader, delete it. if (key->cbObj->get_pj_socket()->Reader()) key->cbObj->get_pj_socket()->DestroyReader(); // Clear flag flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC; return key->cbObj->StartRead(op_key, buffer, length, flags, NULL, NULL); } /* * This function behaves similarly as #pj_ioqueue_recv(), except that it is * normally called for socket, and the remote address will also be returned * along with the data. */ PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, void *buffer, pj_ssize_t *length, pj_uint32_t flags, pj_sockaddr_t *addr, int *addrlen) { CPjSocket *sock = key->cbObj->get_pj_socket(); // If address is specified, check that the length match the // address family if (addr || addrlen) { PJ_ASSERT_RETURN(addr && addrlen && *addrlen, PJ_EINVAL); if (sock->GetAf() == PJ_AF_INET) { PJ_ASSERT_RETURN(*addrlen>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL); } else if (sock->GetAf() == PJ_AF_INET6) { PJ_ASSERT_RETURN(*addrlen>=(int)sizeof(pj_sockaddr_in6), PJ_EINVAL); } } // If socket has reader, delete it. if (sock->Reader()) sock->DestroyReader(); if (key->cbObj->IsActive()) return PJ_EBUSY; // Clear flag flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC; return key->cbObj->StartRead(op_key, buffer, length, flags, addr, addrlen); } /* * Instruct the I/O Queue to write to the handle. */ PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, const void *data, pj_ssize_t *length, pj_uint32_t flags ) { TRequestStatus reqStatus; TPtrC8 aBuffer((const TUint8*)data, (TInt)*length); TSockXfrLength aLen; PJ_UNUSED_ARG(op_key); // Forcing pending operation is not supported. PJ_ASSERT_RETURN((flags & PJ_IOQUEUE_ALWAYS_ASYNC)==0, PJ_EINVAL); // Return failure if access point is marked as down by app. PJ_SYMBIAN_CHECK_CONNECTION(); // Clear flag flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC; key->cbObj->get_pj_socket()->Socket().Send(aBuffer, flags, reqStatus, aLen); User::WaitForRequest(reqStatus); if (reqStatus.Int() != KErrNone) return PJ_RETURN_OS_ERROR(reqStatus.Int()); //At least in UIQ Emulator, aLen.Length() reports incorrect length //for UDP (some newlc.com users seem to have reported this too). //*length = aLen.Length(); return PJ_SUCCESS; } /* * Instruct the I/O Queue to write to the handle. */ PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, const void *data, pj_ssize_t *length, pj_uint32_t flags, const pj_sockaddr_t *addr, int addrlen) { TRequestStatus reqStatus; TPtrC8 aBuffer; TInetAddr inetAddr; TSockXfrLength aLen; pj_status_t status; PJ_UNUSED_ARG(op_key); // Forcing pending operation is not supported. PJ_ASSERT_RETURN((flags & PJ_IOQUEUE_ALWAYS_ASYNC)==0, PJ_EINVAL); // Return failure if access point is marked as down by app. PJ_SYMBIAN_CHECK_CONNECTION(); // Convert address status = PjSymbianOS::pj2Addr(*(const pj_sockaddr*)addr, addrlen, inetAddr); if (status != PJ_SUCCESS) return status; // Clear flag flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC; aBuffer.Set((const TUint8*)data, (TInt)*length); CPjSocket *pjSock = key->cbObj->get_pj_socket(); pjSock->Socket().SendTo(aBuffer, inetAddr, flags, reqStatus, aLen); User::WaitForRequest(reqStatus); if (reqStatus.Int() != KErrNone) return PJ_RETURN_OS_ERROR(reqStatus.Int()); //At least in UIQ Emulator, aLen.Length() reports incorrect length //for UDP (some newlc.com users seem to have reported this too). //*length = aLen.Length(); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_ioqueue_set_concurrency(pj_ioqueue_key_t *key, pj_bool_t allow) { /* Not supported, just return PJ_SUCCESS silently */ PJ_UNUSED_ARG(key); PJ_UNUSED_ARG(allow); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_ioqueue_lock_key(pj_ioqueue_key_t *key) { /* Not supported, just return PJ_SUCCESS silently */ PJ_UNUSED_ARG(key); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_ioqueue_unlock_key(pj_ioqueue_key_t *key) { /* Not supported, just return PJ_SUCCESS silently */ PJ_UNUSED_ARG(key); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib/src/pj/ioqueue_winnt.c ================================================ /* $Id: ioqueue_winnt.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 # include #elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 # include #endif #if defined(PJ_HAS_MSWSOCK_H) && PJ_HAS_MSWSOCK_H != 0 # include #endif /* The address specified in AcceptEx() must be 16 more than the size of * SOCKADDR (source: MSDN). */ #define ACCEPT_ADDR_LEN (sizeof(pj_sockaddr_in)+16) typedef struct generic_overlapped { WSAOVERLAPPED overlapped; pj_ioqueue_operation_e operation; } generic_overlapped; /* * OVERLAPPPED structure for send and receive. */ typedef struct ioqueue_overlapped { WSAOVERLAPPED overlapped; pj_ioqueue_operation_e operation; WSABUF wsabuf; pj_sockaddr_in dummy_addr; int dummy_addrlen; } ioqueue_overlapped; #if PJ_HAS_TCP /* * OVERLAP structure for accept. */ typedef struct ioqueue_accept_rec { WSAOVERLAPPED overlapped; pj_ioqueue_operation_e operation; pj_sock_t newsock; pj_sock_t *newsock_ptr; int *addrlen; void *remote; void *local; char accept_buf[2 * ACCEPT_ADDR_LEN]; } ioqueue_accept_rec; #endif /* * Structure to hold pending operation key. */ union operation_key { generic_overlapped generic; ioqueue_overlapped overlapped; #if PJ_HAS_TCP ioqueue_accept_rec accept; #endif }; /* Type of handle in the key. */ enum handle_type { HND_IS_UNKNOWN, HND_IS_FILE, HND_IS_SOCKET, }; enum { POST_QUIT_LEN = 0xFFFFDEADUL }; /* * Structure for individual socket. */ struct pj_ioqueue_key_t { PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t); pj_ioqueue_t *ioqueue; HANDLE hnd; void *user_data; enum handle_type hnd_type; pj_ioqueue_callback cb; pj_bool_t allow_concurrent; #if PJ_HAS_TCP int connecting; #endif #if PJ_IOQUEUE_HAS_SAFE_UNREG pj_atomic_t *ref_count; pj_bool_t closing; pj_time_val free_time; pj_mutex_t *mutex; #endif }; /* * IO Queue structure. */ struct pj_ioqueue_t { HANDLE iocp; pj_lock_t *lock; pj_bool_t auto_delete_lock; pj_bool_t default_concurrency; #if PJ_IOQUEUE_HAS_SAFE_UNREG pj_ioqueue_key_t active_list; pj_ioqueue_key_t free_list; pj_ioqueue_key_t closing_list; #endif /* These are to keep track of connecting sockets */ #if PJ_HAS_TCP unsigned event_count; HANDLE event_pool[MAXIMUM_WAIT_OBJECTS+1]; unsigned connecting_count; HANDLE connecting_handles[MAXIMUM_WAIT_OBJECTS+1]; pj_ioqueue_key_t *connecting_keys[MAXIMUM_WAIT_OBJECTS+1]; #endif }; #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Prototype */ static void scan_closing_keys(pj_ioqueue_t *ioqueue); #endif #if PJ_HAS_TCP /* * Process the socket when the overlapped accept() completed. */ static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key, ioqueue_accept_rec *accept_overlapped) { struct sockaddr *local; struct sockaddr *remote; int locallen, remotelen; pj_status_t status; PJ_CHECK_STACK(); /* On WinXP or later, use SO_UPDATE_ACCEPT_CONTEXT so that socket * addresses can be obtained with getsockname() and getpeername(). */ status = setsockopt(accept_overlapped->newsock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char*)&key->hnd, sizeof(SOCKET)); /* SO_UPDATE_ACCEPT_CONTEXT is for WinXP or later. * So ignore the error status. */ /* Operation complete immediately. */ if (accept_overlapped->addrlen) { GetAcceptExSockaddrs( accept_overlapped->accept_buf, 0, ACCEPT_ADDR_LEN, ACCEPT_ADDR_LEN, &local, &locallen, &remote, &remotelen); if (*accept_overlapped->addrlen >= locallen) { if (accept_overlapped->local) pj_memcpy(accept_overlapped->local, local, locallen); if (accept_overlapped->remote) pj_memcpy(accept_overlapped->remote, remote, locallen); } else { if (accept_overlapped->local) pj_bzero(accept_overlapped->local, *accept_overlapped->addrlen); if (accept_overlapped->remote) pj_bzero(accept_overlapped->remote, *accept_overlapped->addrlen); } *accept_overlapped->addrlen = locallen; } if (accept_overlapped->newsock_ptr) *accept_overlapped->newsock_ptr = accept_overlapped->newsock; accept_overlapped->operation = 0; } static void erase_connecting_socket( pj_ioqueue_t *ioqueue, unsigned pos) { pj_ioqueue_key_t *key = ioqueue->connecting_keys[pos]; HANDLE hEvent = ioqueue->connecting_handles[pos]; /* Remove key from array of connecting handles. */ pj_array_erase(ioqueue->connecting_keys, sizeof(key), ioqueue->connecting_count, pos); pj_array_erase(ioqueue->connecting_handles, sizeof(HANDLE), ioqueue->connecting_count, pos); --ioqueue->connecting_count; /* Disassociate the socket from the event. */ WSAEventSelect((pj_sock_t)key->hnd, hEvent, 0); /* Put event object to pool. */ if (ioqueue->event_count < MAXIMUM_WAIT_OBJECTS) { ioqueue->event_pool[ioqueue->event_count++] = hEvent; } else { /* Shouldn't happen. There should be no more pending connections * than max. */ pj_assert(0); CloseHandle(hEvent); } } /* * Poll for the completion of non-blocking connect(). * If there's a completion, the function return the key of the completed * socket, and 'result' argument contains the connect() result. If connect() * succeeded, 'result' will have value zero, otherwise will have the error * code. */ static int check_connecting( pj_ioqueue_t *ioqueue ) { if (ioqueue->connecting_count) { int i, count; struct { pj_ioqueue_key_t *key; pj_status_t status; } events[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL-1]; pj_lock_acquire(ioqueue->lock); for (count=0; countconnecting_count, ioqueue->connecting_handles, FALSE, 0); if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0+ioqueue->connecting_count) { WSANETWORKEVENTS net_events; /* Got completed connect(). */ unsigned pos = result - WAIT_OBJECT_0; events[count].key = ioqueue->connecting_keys[pos]; /* See whether connect has succeeded. */ WSAEnumNetworkEvents((pj_sock_t)events[count].key->hnd, ioqueue->connecting_handles[pos], &net_events); events[count].status = PJ_STATUS_FROM_OS(net_events.iErrorCode[FD_CONNECT_BIT]); /* Erase socket from pending connect. */ erase_connecting_socket(ioqueue, pos); } else { /* No more events */ break; } } pj_lock_release(ioqueue->lock); /* Call callbacks. */ for (i=0; icb.on_connect_complete) { events[i].key->cb.on_connect_complete(events[i].key, events[i].status); } } return count; } return 0; } #endif /* * pj_ioqueue_name() */ PJ_DEF(const char*) pj_ioqueue_name(void) { return "iocp"; } /* * pj_ioqueue_create() */ PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, pj_size_t max_fd, pj_ioqueue_t **p_ioqueue) { pj_ioqueue_t *ioqueue; unsigned i; pj_status_t rc; PJ_UNUSED_ARG(max_fd); PJ_ASSERT_RETURN(pool && p_ioqueue, PJ_EINVAL); rc = sizeof(union operation_key); /* Check that sizeof(pj_ioqueue_op_key_t) makes sense. */ PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >= sizeof(union operation_key), PJ_EBUG); /* Create IOCP */ ioqueue = pj_pool_zalloc(pool, sizeof(*ioqueue)); ioqueue->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); if (ioqueue->iocp == NULL) return PJ_RETURN_OS_ERROR(GetLastError()); /* Create IOCP mutex */ rc = pj_lock_create_recursive_mutex(pool, NULL, &ioqueue->lock); if (rc != PJ_SUCCESS) { CloseHandle(ioqueue->iocp); return rc; } ioqueue->auto_delete_lock = PJ_TRUE; ioqueue->default_concurrency = PJ_IOQUEUE_DEFAULT_ALLOW_CONCURRENCY; #if PJ_IOQUEUE_HAS_SAFE_UNREG /* * Create and initialize key pools. */ pj_list_init(&ioqueue->active_list); pj_list_init(&ioqueue->free_list); pj_list_init(&ioqueue->closing_list); /* Preallocate keys according to max_fd setting, and put them * in free_list. */ for (i=0; iref_count); if (rc != PJ_SUCCESS) { key = ioqueue->free_list.next; while (key != &ioqueue->free_list) { pj_atomic_destroy(key->ref_count); pj_mutex_destroy(key->mutex); key = key->next; } CloseHandle(ioqueue->iocp); return rc; } rc = pj_mutex_create_recursive(pool, "ioqkey", &key->mutex); if (rc != PJ_SUCCESS) { pj_atomic_destroy(key->ref_count); key = ioqueue->free_list.next; while (key != &ioqueue->free_list) { pj_atomic_destroy(key->ref_count); pj_mutex_destroy(key->mutex); key = key->next; } CloseHandle(ioqueue->iocp); return rc; } pj_list_push_back(&ioqueue->free_list, key); } #endif *p_ioqueue = ioqueue; PJ_LOG(4, ("pjlib", "WinNT IOCP I/O Queue created (%p)", ioqueue)); return PJ_SUCCESS; } /* * pj_ioqueue_destroy() */ PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioqueue ) { #if PJ_HAS_TCP unsigned i; #endif pj_ioqueue_key_t *key; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL); pj_lock_acquire(ioqueue->lock); #if PJ_HAS_TCP /* Destroy events in the pool */ for (i=0; ievent_count; ++i) { CloseHandle(ioqueue->event_pool[i]); } ioqueue->event_count = 0; #endif if (CloseHandle(ioqueue->iocp) != TRUE) return PJ_RETURN_OS_ERROR(GetLastError()); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Destroy reference counters */ key = ioqueue->active_list.next; while (key != &ioqueue->active_list) { pj_atomic_destroy(key->ref_count); pj_mutex_destroy(key->mutex); key = key->next; } key = ioqueue->closing_list.next; while (key != &ioqueue->closing_list) { pj_atomic_destroy(key->ref_count); pj_mutex_destroy(key->mutex); key = key->next; } key = ioqueue->free_list.next; while (key != &ioqueue->free_list) { pj_atomic_destroy(key->ref_count); pj_mutex_destroy(key->mutex); key = key->next; } #endif if (ioqueue->auto_delete_lock) pj_lock_destroy(ioqueue->lock); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_ioqueue_set_default_concurrency(pj_ioqueue_t *ioqueue, pj_bool_t allow) { PJ_ASSERT_RETURN(ioqueue != NULL, PJ_EINVAL); ioqueue->default_concurrency = allow; return PJ_SUCCESS; } /* * pj_ioqueue_set_lock() */ PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioqueue, pj_lock_t *lock, pj_bool_t auto_delete ) { PJ_ASSERT_RETURN(ioqueue && lock, PJ_EINVAL); if (ioqueue->auto_delete_lock) { pj_lock_destroy(ioqueue->lock); } ioqueue->lock = lock; ioqueue->auto_delete_lock = auto_delete; return PJ_SUCCESS; } /* * pj_ioqueue_register_sock() */ PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, pj_ioqueue_t *ioqueue, pj_sock_t sock, void *user_data, const pj_ioqueue_callback *cb, pj_ioqueue_key_t **key ) { HANDLE hioq; pj_ioqueue_key_t *rec; u_long value; int rc; PJ_ASSERT_RETURN(pool && ioqueue && cb && key, PJ_EINVAL); pj_lock_acquire(ioqueue->lock); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Scan closing list first to release unused keys. * Must do this with lock acquired. */ scan_closing_keys(ioqueue); /* If safe unregistration is used, then get the key record from * the free list. */ if (pj_list_empty(&ioqueue->free_list)) { pj_lock_release(ioqueue->lock); return PJ_ETOOMANY; } rec = ioqueue->free_list.next; pj_list_erase(rec); /* Set initial reference count to 1 */ pj_assert(pj_atomic_get(rec->ref_count) == 0); pj_atomic_inc(rec->ref_count); rec->closing = 0; #else rec = pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); #endif /* Build the key for this socket. */ rec->ioqueue = ioqueue; rec->hnd = (HANDLE)sock; rec->hnd_type = HND_IS_SOCKET; rec->user_data = user_data; pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback)); /* Set concurrency for this handle */ rc = pj_ioqueue_set_concurrency(rec, ioqueue->default_concurrency); if (rc != PJ_SUCCESS) { pj_lock_release(ioqueue->lock); return rc; } #if PJ_HAS_TCP rec->connecting = 0; #endif /* Set socket to nonblocking. */ value = 1; rc = ioctlsocket(sock, FIONBIO, &value); if (rc != 0) { pj_lock_release(ioqueue->lock); return PJ_RETURN_OS_ERROR(WSAGetLastError()); } /* Associate with IOCP */ hioq = CreateIoCompletionPort((HANDLE)sock, ioqueue->iocp, (DWORD)rec, 0); if (!hioq) { pj_lock_release(ioqueue->lock); return PJ_RETURN_OS_ERROR(GetLastError()); } *key = rec; #if PJ_IOQUEUE_HAS_SAFE_UNREG pj_list_push_back(&ioqueue->active_list, rec); #endif pj_lock_release(ioqueue->lock); return PJ_SUCCESS; } /* * pj_ioqueue_get_user_data() */ PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) { PJ_ASSERT_RETURN(key, NULL); return key->user_data; } /* * pj_ioqueue_set_user_data() */ PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key, void *user_data, void **old_data ) { PJ_ASSERT_RETURN(key, PJ_EINVAL); if (old_data) *old_data = key->user_data; key->user_data = user_data; return PJ_SUCCESS; } #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Decrement the key's reference counter, and when the counter reach zero, * destroy the key. */ static void decrement_counter(pj_ioqueue_key_t *key) { if (pj_atomic_dec_and_get(key->ref_count) == 0) { pj_lock_acquire(key->ioqueue->lock); pj_assert(key->closing == 1); pj_gettickcount(&key->free_time); key->free_time.msec += PJ_IOQUEUE_KEY_FREE_DELAY; pj_time_val_normalize(&key->free_time); pj_list_erase(key); pj_list_push_back(&key->ioqueue->closing_list, key); pj_lock_release(key->ioqueue->lock); } } #endif /* * Poll the I/O Completion Port, execute callback, * and return the key and bytes transferred of the last operation. */ static pj_bool_t poll_iocp( HANDLE hIocp, DWORD dwTimeout, pj_ssize_t *p_bytes, pj_ioqueue_key_t **p_key ) { DWORD dwBytesTransferred, dwKey; generic_overlapped *pOv; pj_ioqueue_key_t *key; pj_ssize_t size_status = -1; BOOL rcGetQueued; /* Poll for completion status. */ rcGetQueued = GetQueuedCompletionStatus(hIocp, &dwBytesTransferred, &dwKey, (OVERLAPPED**)&pOv, dwTimeout); /* The return value is: * - nonzero if event was dequeued. * - zero and pOv==NULL if no event was dequeued. * - zero and pOv!=NULL if event for failed I/O was dequeued. */ if (pOv) { pj_bool_t has_lock; /* Event was dequeued for either successfull or failed I/O */ key = (pj_ioqueue_key_t*)dwKey; size_status = dwBytesTransferred; /* Report to caller regardless */ if (p_bytes) *p_bytes = size_status; if (p_key) *p_key = key; #if PJ_IOQUEUE_HAS_SAFE_UNREG /* We shouldn't call callbacks if key is quitting. */ if (key->closing) return PJ_TRUE; /* If concurrency is disabled, lock the key * (and save the lock status to local var since app may change * concurrency setting while in the callback) */ if (key->allow_concurrent == PJ_FALSE) { pj_mutex_lock(key->mutex); has_lock = PJ_TRUE; } else { has_lock = PJ_FALSE; } /* Now that we get the lock, check again that key is not closing */ if (key->closing) { if (has_lock) { pj_mutex_unlock(key->mutex); } return PJ_TRUE; } /* Increment reference counter to prevent this key from being * deleted */ pj_atomic_inc(key->ref_count); #else PJ_UNUSED_ARG(has_lock); #endif /* Carry out the callback */ switch (pOv->operation) { case PJ_IOQUEUE_OP_READ: case PJ_IOQUEUE_OP_RECV: case PJ_IOQUEUE_OP_RECV_FROM: pOv->operation = 0; if (key->cb.on_read_complete) key->cb.on_read_complete(key, (pj_ioqueue_op_key_t*)pOv, size_status); break; case PJ_IOQUEUE_OP_WRITE: case PJ_IOQUEUE_OP_SEND: case PJ_IOQUEUE_OP_SEND_TO: pOv->operation = 0; if (key->cb.on_write_complete) key->cb.on_write_complete(key, (pj_ioqueue_op_key_t*)pOv, size_status); break; #if PJ_HAS_TCP case PJ_IOQUEUE_OP_ACCEPT: /* special case for accept. */ ioqueue_on_accept_complete(key, (ioqueue_accept_rec*)pOv); if (key->cb.on_accept_complete) { ioqueue_accept_rec *accept_rec = (ioqueue_accept_rec*)pOv; pj_status_t status = PJ_SUCCESS; pj_sock_t newsock; newsock = accept_rec->newsock; accept_rec->newsock = PJ_INVALID_SOCKET; if (newsock == PJ_INVALID_SOCKET) { int dwError = WSAGetLastError(); if (dwError == 0) dwError = OSERR_ENOTCONN; status = PJ_RETURN_OS_ERROR(dwError); } key->cb.on_accept_complete(key, (pj_ioqueue_op_key_t*)pOv, newsock, status); } break; case PJ_IOQUEUE_OP_CONNECT: #endif case PJ_IOQUEUE_OP_NONE: pj_assert(0); break; } #if PJ_IOQUEUE_HAS_SAFE_UNREG decrement_counter(key); if (has_lock) pj_mutex_unlock(key->mutex); #endif return PJ_TRUE; } /* No event was queued. */ return PJ_FALSE; } /* * pj_ioqueue_unregister() */ PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key ) { unsigned i; pj_bool_t has_lock; enum { RETRY = 10 }; PJ_ASSERT_RETURN(key, PJ_EINVAL); #if PJ_HAS_TCP if (key->connecting) { unsigned pos; pj_ioqueue_t *ioqueue; ioqueue = key->ioqueue; /* Erase from connecting_handles */ pj_lock_acquire(ioqueue->lock); for (pos=0; pos < ioqueue->connecting_count; ++pos) { if (ioqueue->connecting_keys[pos] == key) { erase_connecting_socket(ioqueue, pos); break; } } key->connecting = 0; pj_lock_release(ioqueue->lock); } #endif #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Mark key as closing before closing handle. */ key->closing = 1; /* If concurrency is disabled, wait until the key has finished * processing the callback */ if (key->allow_concurrent == PJ_FALSE) { pj_mutex_lock(key->mutex); has_lock = PJ_TRUE; } else { has_lock = PJ_FALSE; } #else PJ_UNUSED_ARG(has_lock); #endif /* Close handle (the only way to disassociate handle from IOCP). * We also need to close handle to make sure that no further events * will come to the handle. */ /* Update 2008/07/18 (http://trac.pjsip.org/repos/ticket/575): * - It seems that CloseHandle() in itself does not actually close * the socket (i.e. it will still appear in "netstat" output). Also * if we only use CloseHandle(), an "Invalid Handle" exception will * be raised in WSACleanup(). * - MSDN documentation says that CloseHandle() must be called after * closesocket() call (see * http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx). * But turns out that this will raise "Invalid Handle" exception * in debug mode. * So because of this, we replaced CloseHandle() with closesocket() * instead. These was tested on WinXP SP2. */ //CloseHandle(key->hnd); pj_sock_close((pj_sock_t)key->hnd); /* Reset callbacks */ key->cb.on_accept_complete = NULL; key->cb.on_connect_complete = NULL; key->cb.on_read_complete = NULL; key->cb.on_write_complete = NULL; #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Even after handle is closed, I suspect that IOCP may still try to * do something with the handle, causing memory corruption when pool * debugging is enabled. * * Forcing context switch seems to have fixed that, but this is quite * an ugly solution.. * * Update 2008/02/13: * This should not happen if concurrency is disallowed for the key. * So at least application has a solution for this (i.e. by disallowing * concurrency in the key). */ //This will loop forever if unregistration is done on the callback. //Doing this with RETRY I think should solve the IOCP setting the //socket signalled, without causing the deadlock. //while (pj_atomic_get(key->ref_count) != 1) // pj_thread_sleep(0); for (i=0; pj_atomic_get(key->ref_count) != 1 && imutex); #endif return PJ_SUCCESS; } #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Scan the closing list, and put pending closing keys to free list. * Must do this with ioqueue mutex held. */ static void scan_closing_keys(pj_ioqueue_t *ioqueue) { if (!pj_list_empty(&ioqueue->closing_list)) { pj_time_val now; pj_ioqueue_key_t *key; pj_gettickcount(&now); /* Move closing keys to free list when they've finished the closing * idle time. */ key = ioqueue->closing_list.next; while (key != &ioqueue->closing_list) { pj_ioqueue_key_t *next = key->next; pj_assert(key->closing != 0); if (PJ_TIME_VAL_GTE(now, key->free_time)) { pj_list_erase(key); pj_list_push_back(&ioqueue->free_list, key); } key = next; } } } #endif /* * pj_ioqueue_poll() * * Poll for events. */ PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout) { DWORD dwMsec; #if PJ_HAS_TCP int connect_count = 0; #endif int event_count = 0; PJ_ASSERT_RETURN(ioqueue, -PJ_EINVAL); /* Calculate miliseconds timeout for GetQueuedCompletionStatus */ dwMsec = timeout ? timeout->sec*1000 + timeout->msec : INFINITE; /* Poll for completion status. */ event_count = poll_iocp(ioqueue->iocp, dwMsec, NULL, NULL); #if PJ_HAS_TCP /* Check the connecting array, only when there's no activity. */ if (event_count == 0) { connect_count = check_connecting(ioqueue); if (connect_count > 0) event_count += connect_count; } #endif #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Check the closing keys only when there's no activity and when there are * pending closing keys. */ if (event_count == 0 && !pj_list_empty(&ioqueue->closing_list)) { pj_lock_acquire(ioqueue->lock); scan_closing_keys(ioqueue); pj_lock_release(ioqueue->lock); } #endif /* Return number of events. */ return event_count; } /* * pj_ioqueue_recv() * * Initiate overlapped WSARecv() operation. */ PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, void *buffer, pj_ssize_t *length, pj_uint32_t flags ) { /* * Ideally we should just call pj_ioqueue_recvfrom() with NULL addr and * addrlen here. But unfortunately it generates EINVAL... :-( * -bennylp */ int rc; DWORD bytesRead; DWORD dwFlags = 0; union operation_key *op_key_rec; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Check key is not closing */ if (key->closing) return PJ_ECANCELLED; #endif op_key_rec = (union operation_key*)op_key->internal__; op_key_rec->overlapped.wsabuf.buf = buffer; op_key_rec->overlapped.wsabuf.len = *length; dwFlags = flags; /* Try non-overlapped received first to see if data is * immediately available. */ if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) { rc = WSARecv((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, &bytesRead, &dwFlags, NULL, NULL); if (rc == 0) { *length = bytesRead; return PJ_SUCCESS; } else { DWORD dwError = WSAGetLastError(); if (dwError != WSAEWOULDBLOCK) { *length = -1; return PJ_RETURN_OS_ERROR(dwError); } } } dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); /* * No immediate data available. * Register overlapped Recv() operation. */ pj_bzero( &op_key_rec->overlapped.overlapped, sizeof(op_key_rec->overlapped.overlapped)); op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV; rc = WSARecv((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, &bytesRead, &dwFlags, &op_key_rec->overlapped.overlapped, NULL); if (rc == SOCKET_ERROR) { DWORD dwStatus = WSAGetLastError(); if (dwStatus!=WSA_IO_PENDING) { *length = -1; return PJ_STATUS_FROM_OS(dwStatus); } } /* Pending operation has been scheduled. */ return PJ_EPENDING; } /* * pj_ioqueue_recvfrom() * * Initiate overlapped RecvFrom() operation. */ PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, void *buffer, pj_ssize_t *length, pj_uint32_t flags, pj_sockaddr_t *addr, int *addrlen) { int rc; DWORD bytesRead; DWORD dwFlags = 0; union operation_key *op_key_rec; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(key && op_key && buffer, PJ_EINVAL); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Check key is not closing */ if (key->closing) return PJ_ECANCELLED; #endif op_key_rec = (union operation_key*)op_key->internal__; op_key_rec->overlapped.wsabuf.buf = buffer; op_key_rec->overlapped.wsabuf.len = *length; dwFlags = flags; /* Try non-overlapped received first to see if data is * immediately available. */ if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) { rc = WSARecvFrom((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, &bytesRead, &dwFlags, addr, addrlen, NULL, NULL); if (rc == 0) { *length = bytesRead; return PJ_SUCCESS; } else { DWORD dwError = WSAGetLastError(); if (dwError != WSAEWOULDBLOCK) { *length = -1; return PJ_RETURN_OS_ERROR(dwError); } } } dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); /* * No immediate data available. * Register overlapped Recv() operation. */ pj_bzero( &op_key_rec->overlapped.overlapped, sizeof(op_key_rec->overlapped.overlapped)); op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV; rc = WSARecvFrom((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, &bytesRead, &dwFlags, addr, addrlen, &op_key_rec->overlapped.overlapped, NULL); if (rc == SOCKET_ERROR) { DWORD dwStatus = WSAGetLastError(); if (dwStatus!=WSA_IO_PENDING) { *length = -1; return PJ_STATUS_FROM_OS(dwStatus); } } /* Pending operation has been scheduled. */ return PJ_EPENDING; } /* * pj_ioqueue_send() * * Initiate overlapped Send operation. */ PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, const void *data, pj_ssize_t *length, pj_uint32_t flags ) { return pj_ioqueue_sendto(key, op_key, data, length, flags, NULL, 0); } /* * pj_ioqueue_sendto() * * Initiate overlapped SendTo operation. */ PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, const void *data, pj_ssize_t *length, pj_uint32_t flags, const pj_sockaddr_t *addr, int addrlen) { int rc; DWORD bytesWritten; DWORD dwFlags; union operation_key *op_key_rec; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(key && op_key && data, PJ_EINVAL); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Check key is not closing */ if (key->closing) return PJ_ECANCELLED; #endif op_key_rec = (union operation_key*)op_key->internal__; /* * First try blocking write. */ op_key_rec->overlapped.wsabuf.buf = (void*)data; op_key_rec->overlapped.wsabuf.len = *length; dwFlags = flags; if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) { rc = WSASendTo((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, &bytesWritten, dwFlags, addr, addrlen, NULL, NULL); if (rc == 0) { *length = bytesWritten; return PJ_SUCCESS; } else { DWORD dwStatus = WSAGetLastError(); if (dwStatus != WSAEWOULDBLOCK) { *length = -1; return PJ_RETURN_OS_ERROR(dwStatus); } } } dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); /* * Data can't be sent immediately. * Schedule asynchronous WSASend(). */ pj_bzero( &op_key_rec->overlapped.overlapped, sizeof(op_key_rec->overlapped.overlapped)); op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_SEND; rc = WSASendTo((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, &bytesWritten, dwFlags, addr, addrlen, &op_key_rec->overlapped.overlapped, NULL); if (rc == SOCKET_ERROR) { DWORD dwStatus = WSAGetLastError(); if (dwStatus!=WSA_IO_PENDING) return PJ_STATUS_FROM_OS(dwStatus); } /* Asynchronous operation successfully submitted. */ return PJ_EPENDING; } #if PJ_HAS_TCP /* * pj_ioqueue_accept() * * Initiate overlapped accept() operation. */ PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t *new_sock, pj_sockaddr_t *local, pj_sockaddr_t *remote, int *addrlen) { BOOL rc; DWORD bytesReceived; pj_status_t status; union operation_key *op_key_rec; SOCKET sock; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Check key is not closing */ if (key->closing) return PJ_ECANCELLED; #endif /* * See if there is a new connection immediately available. */ sock = WSAAccept((SOCKET)key->hnd, remote, addrlen, NULL, 0); if (sock != INVALID_SOCKET) { /* Yes! New socket is available! */ if (local && addrlen) { int status; /* On WinXP or later, use SO_UPDATE_ACCEPT_CONTEXT so that socket * addresses can be obtained with getsockname() and getpeername(). */ status = setsockopt(sock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char*)&key->hnd, sizeof(SOCKET)); /* SO_UPDATE_ACCEPT_CONTEXT is for WinXP or later. * So ignore the error status. */ status = getsockname(sock, local, addrlen); if (status != 0) { DWORD dwError = WSAGetLastError(); closesocket(sock); return PJ_RETURN_OS_ERROR(dwError); } } *new_sock = sock; return PJ_SUCCESS; } else { DWORD dwError = WSAGetLastError(); if (dwError != WSAEWOULDBLOCK) { return PJ_RETURN_OS_ERROR(dwError); } } /* * No connection is immediately available. * Must schedule an asynchronous operation. */ op_key_rec = (union operation_key*)op_key->internal__; status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &op_key_rec->accept.newsock); if (status != PJ_SUCCESS) return status; op_key_rec->accept.operation = PJ_IOQUEUE_OP_ACCEPT; op_key_rec->accept.addrlen = addrlen; op_key_rec->accept.local = local; op_key_rec->accept.remote = remote; op_key_rec->accept.newsock_ptr = new_sock; pj_bzero( &op_key_rec->accept.overlapped, sizeof(op_key_rec->accept.overlapped)); rc = AcceptEx( (SOCKET)key->hnd, (SOCKET)op_key_rec->accept.newsock, op_key_rec->accept.accept_buf, 0, ACCEPT_ADDR_LEN, ACCEPT_ADDR_LEN, &bytesReceived, &op_key_rec->accept.overlapped ); if (rc == TRUE) { ioqueue_on_accept_complete(key, &op_key_rec->accept); return PJ_SUCCESS; } else { DWORD dwStatus = WSAGetLastError(); if (dwStatus!=WSA_IO_PENDING) return PJ_STATUS_FROM_OS(dwStatus); } /* Asynchronous Accept() has been submitted. */ return PJ_EPENDING; } /* * pj_ioqueue_connect() * * Initiate overlapped connect() operation (well, it's non-blocking actually, * since there's no overlapped version of connect()). */ PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key, const pj_sockaddr_t *addr, int addrlen ) { HANDLE hEvent; pj_ioqueue_t *ioqueue; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Check key is not closing */ if (key->closing) return PJ_ECANCELLED; #endif /* Initiate connect() */ if (connect((pj_sock_t)key->hnd, addr, addrlen) != 0) { DWORD dwStatus; dwStatus = WSAGetLastError(); if (dwStatus != WSAEWOULDBLOCK) { return PJ_RETURN_OS_ERROR(dwStatus); } } else { /* Connect has completed immediately! */ return PJ_SUCCESS; } ioqueue = key->ioqueue; /* Add to the array of connecting socket to be polled */ pj_lock_acquire(ioqueue->lock); if (ioqueue->connecting_count >= MAXIMUM_WAIT_OBJECTS) { pj_lock_release(ioqueue->lock); return PJ_ETOOMANYCONN; } /* Get or create event object. */ if (ioqueue->event_count) { hEvent = ioqueue->event_pool[ioqueue->event_count - 1]; --ioqueue->event_count; } else { hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (hEvent == NULL) { DWORD dwStatus = GetLastError(); pj_lock_release(ioqueue->lock); return PJ_STATUS_FROM_OS(dwStatus); } } /* Mark key as connecting. * We can't use array index since key can be removed dynamically. */ key->connecting = 1; /* Associate socket events to the event object. */ if (WSAEventSelect((pj_sock_t)key->hnd, hEvent, FD_CONNECT) != 0) { CloseHandle(hEvent); pj_lock_release(ioqueue->lock); return PJ_RETURN_OS_ERROR(WSAGetLastError()); } /* Add to array. */ ioqueue->connecting_keys[ ioqueue->connecting_count ] = key; ioqueue->connecting_handles[ ioqueue->connecting_count ] = hEvent; ioqueue->connecting_count++; pj_lock_release(ioqueue->lock); return PJ_EPENDING; } #endif /* #if PJ_HAS_TCP */ PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key, pj_size_t size ) { pj_bzero(op_key, size); } PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key ) { BOOL rc; DWORD bytesTransferred; rc = GetOverlappedResult( key->hnd, (LPOVERLAPPED)op_key, &bytesTransferred, FALSE ); if (rc == FALSE) { return GetLastError()==ERROR_IO_INCOMPLETE; } return FALSE; } PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_status ) { BOOL rc; rc = PostQueuedCompletionStatus(key->ioqueue->iocp, bytes_status, (long)key, (OVERLAPPED*)op_key ); if (rc == FALSE) { return PJ_RETURN_OS_ERROR(GetLastError()); } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_ioqueue_set_concurrency(pj_ioqueue_key_t *key, pj_bool_t allow) { PJ_ASSERT_RETURN(key, PJ_EINVAL); /* PJ_IOQUEUE_HAS_SAFE_UNREG must be enabled if concurrency is * disabled. */ PJ_ASSERT_RETURN(allow || PJ_IOQUEUE_HAS_SAFE_UNREG, PJ_EINVAL); key->allow_concurrent = allow; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_ioqueue_lock_key(pj_ioqueue_key_t *key) { #if PJ_IOQUEUE_HAS_SAFE_UNREG return pj_mutex_lock(key->mutex); #else PJ_ASSERT_RETURN(!"PJ_IOQUEUE_HAS_SAFE_UNREG is disabled", PJ_EINVALIDOP); #endif } PJ_DEF(pj_status_t) pj_ioqueue_unlock_key(pj_ioqueue_key_t *key) { #if PJ_IOQUEUE_HAS_SAFE_UNREG return pj_mutex_unlock(key->mutex); #else PJ_ASSERT_RETURN(!"PJ_IOQUEUE_HAS_SAFE_UNREG is disabled", PJ_EINVALIDOP); #endif } ================================================ FILE: deps/pjsip/pjlib/src/pj/ip_helper_generic.c ================================================ /* $Id: ip_helper_generic.c 4355 2013-02-19 16:27:37Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include /* Set to 1 to enable tracing */ #if 0 # include # define THIS_FILE "ip_helper_generic.c" # define TRACE_(exp) PJ_LOG(5,exp) static const char *get_os_errmsg(void) { static char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(pj_get_os_error(), errmsg, sizeof(errmsg)); return errmsg; } static const char *get_addr(void *addr) { static char txt[PJ_INET6_ADDRSTRLEN]; struct sockaddr *ad = (struct sockaddr*)addr; if (ad->sa_family != PJ_AF_INET && ad->sa_family != PJ_AF_INET6) return "?"; return pj_inet_ntop2(ad->sa_family, pj_sockaddr_get_addr(ad), txt, sizeof(txt)); } #else # define TRACE_(exp) #endif #if 0 /* dummy */ #elif defined(PJ_HAS_IFADDRS_H) && PJ_HAS_IFADDRS_H != 0 && \ defined(PJ_HAS_NET_IF_H) && PJ_HAS_NET_IF_H != 0 /* Using getifaddrs() is preferred since it can work with both IPv4 and IPv6 */ static pj_status_t if_enum_by_af(int af, unsigned *p_cnt, pj_sockaddr ifs[]) { struct ifaddrs *ifap = NULL, *it; unsigned max; PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL); TRACE_((THIS_FILE, "Starting interface enum with getifaddrs() for af=%d", af)); if (getifaddrs(&ifap) != 0) { TRACE_((THIS_FILE, " getifarrds() failed: %s", get_os_errmsg())); return PJ_RETURN_OS_ERROR(pj_get_netos_error()); } it = ifap; max = *p_cnt; *p_cnt = 0; for (; it!=NULL && *p_cnt < max; it = it->ifa_next) { struct sockaddr *ad = it->ifa_addr; TRACE_((THIS_FILE, " checking %s", it->ifa_name)); if ((it->ifa_flags & IFF_UP)==0) { TRACE_((THIS_FILE, " interface is down")); continue; /* Skip when interface is down */ } #if PJ_IP_HELPER_IGNORE_LOOPBACK_IF if (it->ifa_flags & IFF_LOOPBACK) { TRACE_((THIS_FILE, " loopback interface")); continue; /* Skip loopback interface */ } #endif if (ad==NULL) { TRACE_((THIS_FILE, " NULL address ignored")); continue; /* reported to happen on Linux 2.6.25.9 with ppp interface */ } if (ad->sa_family != af) { TRACE_((THIS_FILE, " address %s ignored (af=%d)", get_addr(ad), ad->sa_family)); continue; /* Skip when interface is down */ } /* Ignore 0.0.0.0/8 address. This is a special address * which doesn't seem to have practical use. */ if (af==pj_AF_INET() && (pj_ntohl(((pj_sockaddr_in*)ad)->sin_addr.s_addr) >> 24) == 0) { TRACE_((THIS_FILE, " address %s ignored (0.0.0.0/8 class)", get_addr(ad), ad->sa_family)); continue; } TRACE_((THIS_FILE, " address %s (af=%d) added at index %d", get_addr(ad), ad->sa_family, *p_cnt)); pj_bzero(&ifs[*p_cnt], sizeof(ifs[0])); pj_memcpy(&ifs[*p_cnt], ad, pj_sockaddr_get_len(ad)); PJ_SOCKADDR_RESET_LEN(&ifs[*p_cnt]); (*p_cnt)++; } freeifaddrs(ifap); TRACE_((THIS_FILE, "done, found %d address(es)", *p_cnt)); return (*p_cnt != 0) ? PJ_SUCCESS : PJ_ENOTFOUND; } #elif defined(SIOCGIFCONF) && \ defined(PJ_HAS_NET_IF_H) && PJ_HAS_NET_IF_H != 0 /* Note: this does not work with IPv6 */ static pj_status_t if_enum_by_af(int af, unsigned *p_cnt, pj_sockaddr ifs[]) { pj_sock_t sock; char buf[512]; struct ifconf ifc; struct ifreq *ifr; int i, count; pj_status_t status; PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL); TRACE_((THIS_FILE, "Starting interface enum with SIOCGIFCONF for af=%d", af)); status = pj_sock_socket(af, PJ_SOCK_DGRAM, 0, &sock); if (status != PJ_SUCCESS) return status; /* Query available interfaces */ ifc.ifc_len = sizeof(buf); ifc.ifc_buf = buf; if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { int oserr = pj_get_netos_error(); TRACE_((THIS_FILE, " ioctl(SIOCGIFCONF) failed: %s", get_os_errmsg())); pj_sock_close(sock); return PJ_RETURN_OS_ERROR(oserr); } /* Interface interfaces */ ifr = (struct ifreq*) ifc.ifc_req; count = ifc.ifc_len / sizeof(struct ifreq); if (count > *p_cnt) count = *p_cnt; *p_cnt = 0; for (i=0; iifr_addr; TRACE_((THIS_FILE, " checking interface %s", itf->ifr_name)); /* Skip address with different family */ if (ad->sa_family != af) { TRACE_((THIS_FILE, " address %s (af=%d) ignored", get_addr(ad), (int)ad->sa_family)); continue; } if (ioctl(sock, SIOCGIFFLAGS, &iff) != 0) { TRACE_((THIS_FILE, " ioctl(SIOCGIFFLAGS) failed: %s", get_os_errmsg())); continue; /* Failed to get flags, continue */ } if ((iff.ifr_flags & IFF_UP)==0) { TRACE_((THIS_FILE, " interface is down")); continue; /* Skip when interface is down */ } #if PJ_IP_HELPER_IGNORE_LOOPBACK_IF if (iff.ifr_flags & IFF_LOOPBACK) { TRACE_((THIS_FILE, " loopback interface")); continue; /* Skip loopback interface */ } #endif /* Ignore 0.0.0.0/8 address. This is a special address * which doesn't seem to have practical use. */ if (af==pj_AF_INET() && (pj_ntohl(((pj_sockaddr_in*)ad)->sin_addr.s_addr) >> 24) == 0) { TRACE_((THIS_FILE, " address %s ignored (0.0.0.0/8 class)", get_addr(ad), ad->sa_family)); continue; } TRACE_((THIS_FILE, " address %s (af=%d) added at index %d", get_addr(ad), ad->sa_family, *p_cnt)); pj_bzero(&ifs[*p_cnt], sizeof(ifs[0])); pj_memcpy(&ifs[*p_cnt], ad, pj_sockaddr_get_len(ad)); PJ_SOCKADDR_RESET_LEN(&ifs[*p_cnt]); (*p_cnt)++; } /* Done with socket */ pj_sock_close(sock); TRACE_((THIS_FILE, "done, found %d address(es)", *p_cnt)); return (*p_cnt != 0) ? PJ_SUCCESS : PJ_ENOTFOUND; } #elif defined(PJ_HAS_NET_IF_H) && PJ_HAS_NET_IF_H != 0 /* Note: this does not work with IPv6 */ static pj_status_t if_enum_by_af(int af, unsigned *p_cnt, pj_sockaddr ifs[]) { struct if_nameindex *if_list; struct ifreq ifreq; pj_sock_t sock; unsigned i, max_count; pj_status_t status; PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL); TRACE_((THIS_FILE, "Starting if_nameindex() for af=%d", af)); status = pj_sock_socket(af, PJ_SOCK_DGRAM, 0, &sock); if (status != PJ_SUCCESS) return status; if_list = if_nameindex(); if (if_list == NULL) return PJ_ENOTFOUND; max_count = *p_cnt; *p_cnt = 0; for (i=0; if_list[i].if_index && *p_cntsa_family != af) { TRACE_((THIS_FILE, " address %s family %d ignored", get_addr(&ifreq.ifr_addr), ifreq.ifr_addr.sa_family)); continue; /* Not address family that we want, continue */ } /* Ignore 0.0.0.0/8 address. This is a special address * which doesn't seem to have practical use. */ if (af==pj_AF_INET() && (pj_ntohl(((pj_sockaddr_in*)ad)->sin_addr.s_addr) >> 24) == 0) { TRACE_((THIS_FILE, " address %s ignored (0.0.0.0/8 class)", get_addr(ad), ad->sa_family)); continue; } /* Got an address ! */ TRACE_((THIS_FILE, " address %s (af=%d) added at index %d", get_addr(ad), ad->sa_family, *p_cnt)); pj_bzero(&ifs[*p_cnt], sizeof(ifs[0])); pj_memcpy(&ifs[*p_cnt], ad, pj_sockaddr_get_len(ad)); PJ_SOCKADDR_RESET_LEN(&ifs[*p_cnt]); (*p_cnt)++; } if_freenameindex(if_list); pj_sock_close(sock); TRACE_((THIS_FILE, "done, found %d address(es)", *p_cnt)); return (*p_cnt != 0) ? PJ_SUCCESS : PJ_ENOTFOUND; } #else static pj_status_t if_enum_by_af(int af, unsigned *p_cnt, pj_sockaddr ifs[]) { pj_status_t status; PJ_ASSERT_RETURN(p_cnt && *p_cnt > 0 && ifs, PJ_EINVAL); pj_bzero(ifs, sizeof(ifs[0]) * (*p_cnt)); /* Just get one default route */ status = pj_getdefaultipinterface(af, &ifs[0]); if (status != PJ_SUCCESS) return status; *p_cnt = 1; return PJ_SUCCESS; } #endif /* SIOCGIFCONF */ /* * Enumerate the local IP interface currently active in the host. */ PJ_DEF(pj_status_t) pj_enum_ip_interface(int af, unsigned *p_cnt, pj_sockaddr ifs[]) { unsigned start; pj_status_t status; start = 0; if (af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) { unsigned max = *p_cnt; status = if_enum_by_af(PJ_AF_INET6, &max, &ifs[start]); if (status == PJ_SUCCESS) { start += max; (*p_cnt) -= max; } } if (af==PJ_AF_INET || af==PJ_AF_UNSPEC) { unsigned max = *p_cnt; status = if_enum_by_af(PJ_AF_INET, &max, &ifs[start]); if (status == PJ_SUCCESS) { start += max; (*p_cnt) -= max; } } *p_cnt = start; return (*p_cnt != 0) ? PJ_SUCCESS : PJ_ENOTFOUND; } /* * Enumerate the IP routing table for this host. */ PJ_DEF(pj_status_t) pj_enum_ip_route(unsigned *p_cnt, pj_ip_route_entry routes[]) { pj_sockaddr itf; pj_status_t status; PJ_ASSERT_RETURN(p_cnt && *p_cnt > 0 && routes, PJ_EINVAL); pj_bzero(routes, sizeof(routes[0]) * (*p_cnt)); /* Just get one default route */ status = pj_getdefaultipinterface(PJ_AF_INET, &itf); if (status != PJ_SUCCESS) return status; routes[0].ipv4.if_addr.s_addr = itf.ipv4.sin_addr.s_addr; routes[0].ipv4.dst_addr.s_addr = 0; routes[0].ipv4.mask.s_addr = 0; *p_cnt = 1; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib/src/pj/ip_helper_symbian.cpp ================================================ /* $Id: ip_helper_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include "os_symbian.h" #define THIS_FILE "ip_helper_symbian.cpp" #define TRACE_ME 0 static pj_status_t rsock_enum_interface(int af, unsigned *p_cnt, pj_sockaddr ifs[]) { TInt rc; RSocket rSock; TPckgBuf info; unsigned i; if (PjSymbianOS::Instance()->Connection()) { rc = rSock.Open(PjSymbianOS::Instance()->SocketServ(), af, PJ_SOCK_DGRAM, KProtocolInetUdp, *PjSymbianOS::Instance()->Connection()); } else { rc = rSock.Open(PjSymbianOS::Instance()->SocketServ(), af, PJ_SOCK_DGRAM, KProtocolInetUdp); } if (rc != KErrNone) return PJ_RETURN_OS_ERROR(rc); rSock.SetOpt(KSoInetEnumInterfaces, KSolInetIfCtrl); for (i=0; i<*p_cnt && rSock.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, info) == KErrNone; ) { TInetAddr &iAddress = info().iAddress; int namelen; #if TRACE_ME if (1) { pj_sockaddr a; char ipaddr[PJ_INET6_ADDRSTRLEN+2]; namelen = sizeof(pj_sockaddr); if (PjSymbianOS::Addr2pj(iAddress, a, &namelen, PJ_FALSE) == PJ_SUCCESS) { PJ_LOG(5,(THIS_FILE, "Enum: found address %s", pj_sockaddr_print(&a, ipaddr, sizeof(ipaddr), 2))); } } #endif namelen = sizeof(ifs[i]); if (PjSymbianOS::Addr2pj(iAddress, ifs[i], &namelen, PJ_TRUE) != PJ_SUCCESS) { continue; } if (ifs[i].addr.sa_family != af) continue; ++i; } rSock.Close(); // Done *p_cnt = i; return PJ_SUCCESS; } /* * Enumerate the local IP interface currently active in the host. */ PJ_DEF(pj_status_t) pj_enum_ip_interface(int af, unsigned *p_cnt, pj_sockaddr ifs[]) { unsigned start; pj_status_t status = PJ_SUCCESS; start = 0; /* Get IPv6 interface first. */ if (af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) { unsigned max = *p_cnt; status = rsock_enum_interface(PJ_AF_INET6, &max, &ifs[start]); if (status == PJ_SUCCESS) { (*p_cnt) -= max; start += max; } } /* Get IPv4 interface. */ if (af==PJ_AF_INET || af==PJ_AF_UNSPEC) { unsigned max = *p_cnt; status = rsock_enum_interface(PJ_AF_INET, &max, &ifs[start]); if (status == PJ_SUCCESS) { (*p_cnt) -= max; start += max; } } *p_cnt = start; return start ? PJ_SUCCESS : PJ_ENOTFOUND; } /* * Enumerate the IP routing table for this host. */ PJ_DEF(pj_status_t) pj_enum_ip_route(unsigned *p_cnt, pj_ip_route_entry routes[]) { PJ_ASSERT_RETURN(p_cnt && *p_cnt > 0 && routes, PJ_EINVAL); *p_cnt = 0; return PJ_ENOTSUP; } ================================================ FILE: deps/pjsip/pjlib/src/pj/ip_helper_win32.c ================================================ /* $Id: ip_helper_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #define WIN32_LEAN_AND_MEAN #include /* PMIB_ICMP_EX is not declared in VC6, causing error. * But EVC4, which also claims to be VC6, does have it! */ #if defined(_MSC_VER) && _MSC_VER==1200 && !defined(PJ_WIN32_WINCE) # define PMIB_ICMP_EX void* #endif #include /* If you encounter error "Cannot open include file: 'Iphlpapi.h' here, * you need to install newer Platform SDK. Presumably you're using * Microsoft Visual Studio 6? */ #include #include #include #include #include /* Dealing with Unicode quirks: There seems to be a difference with GetProcAddress() API signature between Windows (i.e. Win32) and Windows CE (e.g. Windows Mobile). On Windows, the API is declared as: FARPROC GetProcAddress( HMODULE hModule, LPCSTR lpProcName); while on Windows CE: FARPROC GetProcAddress( HMODULE hModule, LPCWSTR lpProcName); Notice the difference with lpProcName argument type. This means that on Windows, even on Unicode Windows, the lpProcName always takes ANSI format, while on Windows CE, the argument follows the UNICODE setting. Because of this, we use a different Unicode treatment here than the usual PJ_NATIVE_STRING_IS_UNICODE PJLIB setting (): - GPA_TEXT macro: convert literal string to platform's native literal string - gpa_char: the platform native character type Note that "GPA" and "gpa" are abbreviations for GetProcAddress. */ #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 /* on CE, follow the PJLIB Unicode setting */ # define GPA_TEXT(x) PJ_T(x) # define gpa_char pj_char_t #else /* on non-CE, always use ANSI format */ # define GPA_TEXT(x) x # define gpa_char char #endif typedef DWORD (WINAPI *PFN_GetIpAddrTable)(PMIB_IPADDRTABLE pIpAddrTable, PULONG pdwSize, BOOL bOrder); typedef DWORD (WINAPI *PFN_GetAdapterAddresses)(ULONG Family, ULONG Flags, PVOID Reserved, PIP_ADAPTER_ADDRESSES AdapterAddresses, PULONG SizePointer); typedef DWORD (WINAPI *PFN_GetIpForwardTable)(PMIB_IPFORWARDTABLE pIpForwardTable, PULONG pdwSize, BOOL bOrder); typedef DWORD (WINAPI *PFN_GetIfEntry)(PMIB_IFROW pIfRow); static HANDLE s_hDLL; static PFN_GetIpAddrTable s_pfnGetIpAddrTable; static PFN_GetAdapterAddresses s_pfnGetAdapterAddresses; static PFN_GetIpForwardTable s_pfnGetIpForwardTable; static PFN_GetIfEntry s_pfnGetIfEntry; static void unload_iphlp_module(void) { FreeLibrary(s_hDLL); s_hDLL = NULL; s_pfnGetIpAddrTable = NULL; s_pfnGetIpForwardTable = NULL; s_pfnGetIfEntry = NULL; s_pfnGetAdapterAddresses = NULL; } static FARPROC GetIpHlpApiProc(gpa_char *lpProcName) { if(NULL == s_hDLL) { s_hDLL = LoadLibrary(PJ_T("IpHlpApi")); if(NULL != s_hDLL) { pj_atexit(&unload_iphlp_module); } } if(NULL != s_hDLL) return GetProcAddress(s_hDLL, lpProcName); return NULL; } static DWORD MyGetIpAddrTable(PMIB_IPADDRTABLE pIpAddrTable, PULONG pdwSize, BOOL bOrder) { if(NULL == s_pfnGetIpAddrTable) { s_pfnGetIpAddrTable = (PFN_GetIpAddrTable) GetIpHlpApiProc(GPA_TEXT("GetIpAddrTable")); } if(NULL != s_pfnGetIpAddrTable) { return s_pfnGetIpAddrTable(pIpAddrTable, pdwSize, bOrder); } return ERROR_NOT_SUPPORTED; } static DWORD MyGetAdapterAddresses(ULONG Family, ULONG Flags, PVOID Reserved, PIP_ADAPTER_ADDRESSES AdapterAddresses, PULONG SizePointer) { if(NULL == s_pfnGetAdapterAddresses) { s_pfnGetAdapterAddresses = (PFN_GetAdapterAddresses) GetIpHlpApiProc(GPA_TEXT("GetAdaptersAddresses")); } if(NULL != s_pfnGetAdapterAddresses) { return s_pfnGetAdapterAddresses(Family, Flags, Reserved, AdapterAddresses, SizePointer); } return ERROR_NOT_SUPPORTED; } #if PJ_IP_HELPER_IGNORE_LOOPBACK_IF static DWORD MyGetIfEntry(MIB_IFROW *pIfRow) { if(NULL == s_pfnGetIfEntry) { s_pfnGetIfEntry = (PFN_GetIfEntry) GetIpHlpApiProc(GPA_TEXT("GetIfEntry")); } if(NULL != s_pfnGetIfEntry) { return s_pfnGetIfEntry(pIfRow); } return ERROR_NOT_SUPPORTED; } #endif static DWORD MyGetIpForwardTable(PMIB_IPFORWARDTABLE pIpForwardTable, PULONG pdwSize, BOOL bOrder) { if(NULL == s_pfnGetIpForwardTable) { s_pfnGetIpForwardTable = (PFN_GetIpForwardTable) GetIpHlpApiProc(GPA_TEXT("GetIpForwardTable")); } if(NULL != s_pfnGetIpForwardTable) { return s_pfnGetIpForwardTable(pIpForwardTable, pdwSize, bOrder); } return ERROR_NOT_SUPPORTED; } /* Enumerate local IP interface using GetIpAddrTable() * for IPv4 addresses only. */ static pj_status_t enum_ipv4_interface(unsigned *p_cnt, pj_sockaddr ifs[]) { char ipTabBuff[512]; MIB_IPADDRTABLE *pTab = (MIB_IPADDRTABLE*)ipTabBuff; ULONG tabSize = sizeof(ipTabBuff); unsigned i, count; DWORD rc = NO_ERROR; PJ_ASSERT_RETURN(p_cnt && ifs, PJ_EINVAL); /* Get IP address table */ rc = MyGetIpAddrTable(pTab, &tabSize, FALSE); if (rc != NO_ERROR) { if (rc == ERROR_INSUFFICIENT_BUFFER) { /* Retry with larger buffer */ pTab = (MIB_IPADDRTABLE*)malloc(tabSize); if (pTab) rc = MyGetIpAddrTable(pTab, &tabSize, FALSE); } if (rc != NO_ERROR) { if (pTab != (MIB_IPADDRTABLE*)ipTabBuff) free(pTab); return PJ_RETURN_OS_ERROR(rc); } } /* Reset result */ pj_bzero(ifs, sizeof(ifs[0]) * (*p_cnt)); /* Now fill out the entries */ count = (pTab->dwNumEntries < *p_cnt) ? pTab->dwNumEntries : *p_cnt; *p_cnt = 0; for (i=0; itable[i].dwAddr == 0) continue; /* Ignore 0.0.0.0/8 address. This is a special address * which doesn't seem to have practical use. */ if ((pj_ntohl(pTab->table[i].dwAddr) >> 24) == 0) continue; #if PJ_IP_HELPER_IGNORE_LOOPBACK_IF /* Investigate the type of this interface */ pj_bzero(&ifRow, sizeof(ifRow)); ifRow.dwIndex = pTab->table[i].dwIndex; if (MyGetIfEntry(&ifRow) != 0) continue; if (ifRow.dwType == MIB_IF_TYPE_LOOPBACK) continue; #endif ifs[*p_cnt].ipv4.sin_family = PJ_AF_INET; ifs[*p_cnt].ipv4.sin_addr.s_addr = pTab->table[i].dwAddr; (*p_cnt)++; } if (pTab != (MIB_IPADDRTABLE*)ipTabBuff) free(pTab); return (*p_cnt) ? PJ_SUCCESS : PJ_ENOTFOUND; } /* Enumerate local IP interface using GetAdapterAddresses(), * which works for both IPv4 and IPv6. */ static pj_status_t enum_ipv4_ipv6_interface(int af, unsigned *p_cnt, pj_sockaddr ifs[]) { pj_uint8_t buffer[600]; IP_ADAPTER_ADDRESSES *adapter = (IP_ADAPTER_ADDRESSES*)buffer; void *adapterBuf = NULL; ULONG size = sizeof(buffer); ULONG flags; unsigned i; DWORD rc; flags = GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_MULTICAST; rc = MyGetAdapterAddresses(af, flags, NULL, adapter, &size); if (rc != ERROR_SUCCESS) { if (rc == ERROR_BUFFER_OVERFLOW) { /* Retry with larger memory size */ adapterBuf = malloc(size); adapter = (IP_ADAPTER_ADDRESSES*) adapterBuf; if (adapter != NULL) rc = MyGetAdapterAddresses(af, flags, NULL, adapter, &size); } if (rc != ERROR_SUCCESS) { if (adapterBuf) free(adapterBuf); return PJ_RETURN_OS_ERROR(rc); } } /* Reset result */ pj_bzero(ifs, sizeof(ifs[0]) * (*p_cnt)); /* Enumerate interface */ for (i=0; i<*p_cnt && adapter; adapter = adapter->Next) { if (adapter->FirstUnicastAddress) { SOCKET_ADDRESS *pAddr = &adapter->FirstUnicastAddress->Address; /* Ignore address family which we didn't request, just in case */ if (pAddr->lpSockaddr->sa_family != PJ_AF_INET && pAddr->lpSockaddr->sa_family != PJ_AF_INET6) { continue; } /* Apply some filtering to known IPv4 unusable addresses */ if (pAddr->lpSockaddr->sa_family == PJ_AF_INET) { const pj_sockaddr_in *addr_in = (const pj_sockaddr_in*)pAddr->lpSockaddr; /* Ignore 0.0.0.0 address (interface is down?) */ if (addr_in->sin_addr.s_addr == 0) continue; /* Ignore 0.0.0.0/8 address. This is a special address * which doesn't seem to have practical use. */ if ((pj_ntohl(addr_in->sin_addr.s_addr) >> 24) == 0) continue; } #if PJ_IP_HELPER_IGNORE_LOOPBACK_IF /* Ignore loopback interfaces */ /* This should have been IF_TYPE_SOFTWARE_LOOPBACK according to * MSDN, and this macro should have been declared in Ipifcons.h, * but some SDK versions don't have it. */ if (adapter->IfType == MIB_IF_TYPE_LOOPBACK) continue; #endif /* Ignore down interface */ if (adapter->OperStatus != IfOperStatusUp) continue; ifs[i].addr.sa_family = pAddr->lpSockaddr->sa_family; pj_memcpy(&ifs[i], pAddr->lpSockaddr, pAddr->iSockaddrLength); ++i; } } if (adapterBuf) free(adapterBuf); *p_cnt = i; return (*p_cnt) ? PJ_SUCCESS : PJ_ENOTFOUND; } /* * Enumerate the local IP interface currently active in the host. */ PJ_DEF(pj_status_t) pj_enum_ip_interface(int af, unsigned *p_cnt, pj_sockaddr ifs[]) { pj_status_t status = -1; PJ_ASSERT_RETURN(p_cnt && ifs, PJ_EINVAL); PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC || af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EAFNOTSUP); status = enum_ipv4_ipv6_interface(af, p_cnt, ifs); if (status != PJ_SUCCESS && (af==PJ_AF_INET || af==PJ_AF_UNSPEC)) status = enum_ipv4_interface(p_cnt, ifs); return status; } /* * Enumerate the IP routing table for this host. */ PJ_DEF(pj_status_t) pj_enum_ip_route(unsigned *p_cnt, pj_ip_route_entry routes[]) { char ipTabBuff[1024]; MIB_IPADDRTABLE *pIpTab; char rtabBuff[1024]; MIB_IPFORWARDTABLE *prTab; ULONG tabSize; unsigned i, count; DWORD rc = NO_ERROR; PJ_ASSERT_RETURN(p_cnt && routes, PJ_EINVAL); pIpTab = (MIB_IPADDRTABLE *)ipTabBuff; prTab = (MIB_IPFORWARDTABLE *)rtabBuff; /* First get IP address table */ tabSize = sizeof(ipTabBuff); rc = MyGetIpAddrTable(pIpTab, &tabSize, FALSE); if (rc != NO_ERROR) return PJ_RETURN_OS_ERROR(rc); /* Next get IP route table */ tabSize = sizeof(rtabBuff); rc = MyGetIpForwardTable(prTab, &tabSize, 1); if (rc != NO_ERROR) return PJ_RETURN_OS_ERROR(rc); /* Reset routes */ pj_bzero(routes, sizeof(routes[0]) * (*p_cnt)); /* Now fill out the route entries */ count = (prTab->dwNumEntries < *p_cnt) ? prTab->dwNumEntries : *p_cnt; *p_cnt = 0; for (i=0; idwNumEntries; ++j) { if (pIpTab->table[j].dwIndex == prTab->table[i].dwForwardIfIndex) break; } if (j==pIpTab->dwNumEntries) continue; /* Interface not found */ routes[*p_cnt].ipv4.if_addr.s_addr = pIpTab->table[j].dwAddr; routes[*p_cnt].ipv4.dst_addr.s_addr = prTab->table[i].dwForwardDest; routes[*p_cnt].ipv4.mask.s_addr = prTab->table[i].dwForwardMask; (*p_cnt)++; } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib/src/pj/list.c ================================================ /* $Id: list.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #if !PJ_FUNCTIONS_ARE_INLINED # include #endif ================================================ FILE: deps/pjsip/pjlib/src/pj/lock.c ================================================ /* $Id: lock.c 4412 2013-03-05 03:12:32Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #define THIS_FILE "lock.c" typedef void LOCK_OBJ; /* * Lock structure. */ struct pj_lock_t { LOCK_OBJ *lock_object; pj_status_t (*acquire) (LOCK_OBJ*); pj_status_t (*tryacquire) (LOCK_OBJ*); pj_status_t (*release) (LOCK_OBJ*); pj_status_t (*destroy) (LOCK_OBJ*); }; typedef pj_status_t (*FPTR)(LOCK_OBJ*); /****************************************************************************** * Implementation of lock object with mutex. */ static pj_lock_t mutex_lock_template = { NULL, (FPTR) &pj_mutex_lock, (FPTR) &pj_mutex_trylock, (FPTR) &pj_mutex_unlock, (FPTR) &pj_mutex_destroy }; static pj_status_t create_mutex_lock( pj_pool_t *pool, const char *name, int type, pj_lock_t **lock ) { pj_lock_t *p_lock; pj_mutex_t *mutex; pj_status_t rc; PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL); p_lock = PJ_POOL_ALLOC_T(pool, pj_lock_t); if (!p_lock) return PJ_ENOMEM; pj_memcpy(p_lock, &mutex_lock_template, sizeof(pj_lock_t)); rc = pj_mutex_create(pool, name, type, &mutex); if (rc != PJ_SUCCESS) return rc; p_lock->lock_object = mutex; *lock = p_lock; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool, const char *name, pj_lock_t **lock ) { return create_mutex_lock(pool, name, PJ_MUTEX_SIMPLE, lock); } PJ_DEF(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool, const char *name, pj_lock_t **lock ) { return create_mutex_lock(pool, name, PJ_MUTEX_RECURSE, lock); } /****************************************************************************** * Implementation of NULL lock object. */ static pj_status_t null_op(void *arg) { PJ_UNUSED_ARG(arg); return PJ_SUCCESS; } static pj_lock_t null_lock_template = { NULL, &null_op, &null_op, &null_op, &null_op }; PJ_DEF(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool, const char *name, pj_lock_t **lock ) { PJ_UNUSED_ARG(name); PJ_UNUSED_ARG(pool); PJ_ASSERT_RETURN(lock, PJ_EINVAL); *lock = &null_lock_template; return PJ_SUCCESS; } /****************************************************************************** * Implementation of semaphore lock object. */ #if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 static pj_lock_t sem_lock_template = { NULL, (FPTR) &pj_sem_wait, (FPTR) &pj_sem_trywait, (FPTR) &pj_sem_post, (FPTR) &pj_sem_destroy }; PJ_DEF(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool, const char *name, unsigned initial, unsigned max, pj_lock_t **lock ) { pj_lock_t *p_lock; pj_sem_t *sem; pj_status_t rc; PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL); p_lock = PJ_POOL_ALLOC_T(pool, pj_lock_t); if (!p_lock) return PJ_ENOMEM; pj_memcpy(p_lock, &sem_lock_template, sizeof(pj_lock_t)); rc = pj_sem_create( pool, name, initial, max, &sem); if (rc != PJ_SUCCESS) return rc; p_lock->lock_object = sem; *lock = p_lock; return PJ_SUCCESS; } #endif /* PJ_HAS_SEMAPHORE */ PJ_DEF(pj_status_t) pj_lock_acquire( pj_lock_t *lock ) { PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); return (*lock->acquire)(lock->lock_object); } PJ_DEF(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock ) { PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); return (*lock->tryacquire)(lock->lock_object); } PJ_DEF(pj_status_t) pj_lock_release( pj_lock_t *lock ) { PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); return (*lock->release)(lock->lock_object); } PJ_DEF(pj_status_t) pj_lock_destroy( pj_lock_t *lock ) { PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); return (*lock->destroy)(lock->lock_object); } /****************************************************************************** * Group lock */ /* Individual lock in the group lock */ typedef struct grp_lock_item { PJ_DECL_LIST_MEMBER(struct grp_lock_item); int prio; pj_lock_t *lock; } grp_lock_item; /* Destroy callbacks */ typedef struct grp_destroy_callback { PJ_DECL_LIST_MEMBER(struct grp_destroy_callback); void *comp; void (*handler)(void*); } grp_destroy_callback; #if PJ_GRP_LOCK_DEBUG /* Store each add_ref caller */ typedef struct grp_lock_ref { PJ_DECL_LIST_MEMBER(struct grp_lock_ref); const char *file; int line; } grp_lock_ref; #endif /* The group lock */ struct pj_grp_lock_t { pj_lock_t base; pj_pool_t *pool; pj_atomic_t *ref_cnt; pj_lock_t *own_lock; pj_thread_t *owner; int owner_cnt; grp_lock_item lock_list; grp_destroy_callback destroy_list; #if PJ_GRP_LOCK_DEBUG grp_lock_ref ref_list; grp_lock_ref ref_free_list; #endif }; PJ_DEF(void) pj_grp_lock_config_default(pj_grp_lock_config *cfg) { pj_bzero(cfg, sizeof(*cfg)); } static void grp_lock_set_owner_thread(pj_grp_lock_t *glock) { if (!glock->owner) { glock->owner = pj_thread_this(); glock->owner_cnt = 1; } else { pj_assert(glock->owner == pj_thread_this()); glock->owner_cnt++; } } static void grp_lock_unset_owner_thread(pj_grp_lock_t *glock) { pj_assert(glock->owner == pj_thread_this()); pj_assert(glock->owner_cnt > 0); if (--glock->owner_cnt <= 0) { glock->owner = NULL; glock->owner_cnt = 0; } } static pj_status_t grp_lock_acquire(LOCK_OBJ *p) { pj_grp_lock_t *glock = (pj_grp_lock_t*)p; grp_lock_item *lck; pj_assert(pj_atomic_get(glock->ref_cnt) > 0); lck = glock->lock_list.next; while (lck != &glock->lock_list) { pj_lock_acquire(lck->lock); lck = lck->next; } grp_lock_set_owner_thread(glock); pj_grp_lock_add_ref(glock); return PJ_SUCCESS; } static pj_status_t grp_lock_tryacquire(LOCK_OBJ *p) { pj_grp_lock_t *glock = (pj_grp_lock_t*)p; grp_lock_item *lck; pj_assert(pj_atomic_get(glock->ref_cnt) > 0); lck = glock->lock_list.next; while (lck != &glock->lock_list) { pj_status_t status = pj_lock_tryacquire(lck->lock); if (status != PJ_SUCCESS) { lck = lck->prev; while (lck != &glock->lock_list) { pj_lock_release(lck->lock); lck = lck->prev; } return status; } lck = lck->next; } grp_lock_set_owner_thread(glock); pj_grp_lock_add_ref(glock); return PJ_SUCCESS; } static pj_status_t grp_lock_release(LOCK_OBJ *p) { pj_grp_lock_t *glock = (pj_grp_lock_t*)p; grp_lock_item *lck; grp_lock_unset_owner_thread(glock); lck = glock->lock_list.prev; while (lck != &glock->lock_list) { pj_lock_release(lck->lock); lck = lck->prev; } return pj_grp_lock_dec_ref(glock); } static pj_status_t grp_lock_add_handler( pj_grp_lock_t *glock, pj_pool_t *pool, void *comp, void (*destroy)(void *comp), pj_bool_t acquire_lock) { grp_destroy_callback *cb; if (acquire_lock) grp_lock_acquire(glock); if (pool == NULL) pool = glock->pool; cb = PJ_POOL_ZALLOC_T(pool, grp_destroy_callback); cb->comp = comp; cb->handler = destroy; pj_list_push_back(&glock->destroy_list, cb); if (acquire_lock) grp_lock_release(glock); return PJ_SUCCESS; } static pj_status_t grp_lock_destroy(LOCK_OBJ *p) { pj_grp_lock_t *glock = (pj_grp_lock_t*)p; pj_pool_t *pool = glock->pool; grp_lock_item *lck; grp_destroy_callback *cb; if (!glock->pool) { /* already destroyed?! */ return PJ_EINVAL; } /* Release all chained locks */ lck = glock->lock_list.next; while (lck != &glock->lock_list) { if (lck->lock != glock->own_lock) { int i; for (i=0; iowner_cnt; ++i) pj_lock_release(lck->lock); } lck = lck->next; } /* Call callbacks */ cb = glock->destroy_list.next; while (cb != &glock->destroy_list) { grp_destroy_callback *next = cb->next; cb->handler(cb->comp); cb = next; } pj_lock_destroy(glock->own_lock); pj_atomic_destroy(glock->ref_cnt); glock->pool = NULL; pj_pool_release(pool); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_grp_lock_create( pj_pool_t *pool, const pj_grp_lock_config *cfg, pj_grp_lock_t **p_grp_lock) { pj_grp_lock_t *glock; grp_lock_item *own_lock; pj_status_t status; PJ_ASSERT_RETURN(pool && p_grp_lock, PJ_EINVAL); PJ_UNUSED_ARG(cfg); pool = pj_pool_create(pool->factory, "glck%p", 512, 512, NULL); if (!pool) return PJ_ENOMEM; glock = PJ_POOL_ZALLOC_T(pool, pj_grp_lock_t); glock->base.lock_object = glock; glock->base.acquire = &grp_lock_acquire; glock->base.tryacquire = &grp_lock_tryacquire; glock->base.release = &grp_lock_release; glock->base.destroy = &grp_lock_destroy; glock->pool = pool; pj_list_init(&glock->lock_list); pj_list_init(&glock->destroy_list); #if PJ_GRP_LOCK_DEBUG pj_list_init(&glock->ref_list); pj_list_init(&glock->ref_free_list); #endif status = pj_atomic_create(pool, 0, &glock->ref_cnt); if (status != PJ_SUCCESS) goto on_error; status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &glock->own_lock); if (status != PJ_SUCCESS) goto on_error; own_lock = PJ_POOL_ZALLOC_T(pool, grp_lock_item); own_lock->lock = glock->own_lock; pj_list_push_back(&glock->lock_list, own_lock); *p_grp_lock = glock; return PJ_SUCCESS; on_error: grp_lock_destroy(glock); return status; } PJ_DEF(pj_status_t) pj_grp_lock_create_w_handler( pj_pool_t *pool, const pj_grp_lock_config *cfg, void *member, void (*handler)(void *member), pj_grp_lock_t **p_grp_lock) { pj_status_t status; status = pj_grp_lock_create(pool, cfg, p_grp_lock); if (status == PJ_SUCCESS) { grp_lock_add_handler(*p_grp_lock, pool, member, handler, PJ_FALSE); } return status; } PJ_DEF(pj_status_t) pj_grp_lock_destroy( pj_grp_lock_t *grp_lock) { return grp_lock_destroy(grp_lock); } PJ_DEF(pj_status_t) pj_grp_lock_acquire( pj_grp_lock_t *grp_lock) { return grp_lock_acquire(grp_lock); } PJ_DEF(pj_status_t) pj_grp_lock_tryacquire( pj_grp_lock_t *grp_lock) { return grp_lock_tryacquire(grp_lock); } PJ_DEF(pj_status_t) pj_grp_lock_release( pj_grp_lock_t *grp_lock) { return grp_lock_release(grp_lock); } PJ_DEF(pj_status_t) pj_grp_lock_replace( pj_grp_lock_t *old_lock, pj_grp_lock_t *new_lock) { grp_destroy_callback *ocb; /* Move handlers from old to new */ ocb = old_lock->destroy_list.next; while (ocb != &old_lock->destroy_list) { grp_destroy_callback *ncb; ncb = PJ_POOL_ALLOC_T(new_lock->pool, grp_destroy_callback); ncb->comp = ocb->comp; ncb->handler = ocb->handler; pj_list_push_back(&new_lock->destroy_list, ncb); ocb = ocb->next; } pj_list_init(&old_lock->destroy_list); grp_lock_destroy(old_lock); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_grp_lock_add_handler( pj_grp_lock_t *glock, pj_pool_t *pool, void *comp, void (*destroy)(void *comp)) { return grp_lock_add_handler(glock, pool, comp, destroy, PJ_TRUE); } PJ_DEF(pj_status_t) pj_grp_lock_del_handler( pj_grp_lock_t *glock, void *comp, void (*destroy)(void *comp)) { grp_destroy_callback *cb; grp_lock_acquire(glock); cb = glock->destroy_list.next; while (cb != &glock->destroy_list) { if (cb->comp == comp && cb->handler == destroy) break; cb = cb->next; } if (cb != &glock->destroy_list) pj_list_erase(cb); grp_lock_release(glock); return PJ_SUCCESS; } static pj_status_t grp_lock_add_ref(pj_grp_lock_t *glock) { pj_atomic_inc(glock->ref_cnt); return PJ_SUCCESS; } static pj_status_t grp_lock_dec_ref(pj_grp_lock_t *glock) { int cnt; /* for debugging */ if ((cnt=pj_atomic_dec_and_get(glock->ref_cnt)) == 0) { grp_lock_destroy(glock); return PJ_EGONE; } pj_assert(cnt > 0); pj_grp_lock_dump(glock); return PJ_SUCCESS; } #if PJ_GRP_LOCK_DEBUG PJ_DEF(pj_status_t) pj_grp_lock_add_ref_dbg(pj_grp_lock_t *glock, const char *file, int line) { grp_lock_ref *ref; pj_status_t status; pj_enter_critical_section(); if (!pj_list_empty(&glock->ref_free_list)) { ref = glock->ref_free_list.next; pj_list_erase(ref); } else { ref = PJ_POOL_ALLOC_T(glock->pool, grp_lock_ref); } ref->file = file; ref->line = line; pj_list_push_back(&glock->ref_list, ref); pj_leave_critical_section(); status = grp_lock_add_ref(glock); if (status != PJ_SUCCESS) { pj_enter_critical_section(); pj_list_erase(ref); pj_list_push_back(&glock->ref_free_list, ref); pj_leave_critical_section(); } return status; } PJ_DEF(pj_status_t) pj_grp_lock_dec_ref_dbg(pj_grp_lock_t *glock, const char *file, int line) { grp_lock_ref *ref; pj_enter_critical_section(); /* Find the same source file */ ref = glock->ref_list.next; while (ref != &glock->ref_list) { if (strcmp(ref->file, file) == 0) { pj_list_erase(ref); pj_list_push_back(&glock->ref_free_list, ref); break; } ref = ref->next; } pj_leave_critical_section(); if (ref == &glock->ref_list) { PJ_LOG(2,(THIS_FILE, "pj_grp_lock_dec_ref_dbg() could not find " "matching ref for %s", file)); } return grp_lock_dec_ref(glock); } #else PJ_DEF(pj_status_t) pj_grp_lock_add_ref(pj_grp_lock_t *glock) { return grp_lock_add_ref(glock); } PJ_DEF(pj_status_t) pj_grp_lock_dec_ref(pj_grp_lock_t *glock) { return grp_lock_dec_ref(glock); } #endif PJ_DEF(int) pj_grp_lock_get_ref(pj_grp_lock_t *glock) { return pj_atomic_get(glock->ref_cnt); } PJ_DEF(pj_status_t) pj_grp_lock_chain_lock( pj_grp_lock_t *glock, pj_lock_t *lock, int pos) { grp_lock_item *lck, *new_lck; int i; grp_lock_acquire(glock); for (i=0; iowner_cnt; ++i) pj_lock_acquire(lock); lck = glock->lock_list.next; while (lck != &glock->lock_list) { if (lck->prio >= pos) break; lck = lck->next; } new_lck = PJ_POOL_ZALLOC_T(glock->pool, grp_lock_item); new_lck->prio = pos; new_lck->lock = lock; pj_list_insert_before(lck, new_lck); /* this will also release the new lock */ grp_lock_release(glock); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_grp_lock_unchain_lock( pj_grp_lock_t *glock, pj_lock_t *lock) { grp_lock_item *lck; grp_lock_acquire(glock); lck = glock->lock_list.next; while (lck != &glock->lock_list) { if (lck->lock == lock) break; lck = lck->next; } if (lck != &glock->lock_list) { int i; pj_list_erase(lck); for (i=0; iowner_cnt; ++i) pj_lock_release(lck->lock); } grp_lock_release(glock); return PJ_SUCCESS; } PJ_DEF(void) pj_grp_lock_dump(pj_grp_lock_t *grp_lock) { #if PJ_GRP_LOCK_DEBUG grp_lock_ref *ref = grp_lock->ref_list.next; char info_buf[1000]; pj_str_t info; info.ptr = info_buf; info.slen = 0; pj_grp_lock_acquire(grp_lock); pj_enter_critical_section(); while (ref != &grp_lock->ref_list && info.slen < sizeof(info_buf)) { char *start = info.ptr + info.slen; int max_len = sizeof(info_buf) - info.slen; int len; len = pj_ansi_snprintf(start, max_len, "%s:%d ", ref->file, ref->line); if (len < 1 || len >= max_len) { len = strlen(ref->file); if (len > max_len - 1) len = max_len - 1; memcpy(start, ref->file, len); start[len++] = ' '; } info.slen += len; ref = ref->next; } if (ref != &grp_lock->ref_list) { int i; for (i=0; i<4; ++i) info_buf[sizeof(info_buf)-i-1] = '.'; } info.ptr[info.slen-1] = '\0'; pj_leave_critical_section(); pj_grp_lock_release(grp_lock); PJ_LOG(4,(THIS_FILE, "Group lock %p, ref_cnt=%d. Reference holders: %s", grp_lock, pj_grp_lock_get_ref(grp_lock), info.ptr)); #else PJ_UNUSED_ARG(grp_lock); #endif } ================================================ FILE: deps/pjsip/pjlib/src/pj/log.c ================================================ /* $Id: log.c 3752 2011-09-18 14:38:46Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #if PJ_LOG_MAX_LEVEL >= 1 #if 0 PJ_DEF_DATA(int) pj_log_max_level = PJ_LOG_MAX_LEVEL; #else static int pj_log_max_level = PJ_LOG_MAX_LEVEL; #endif static void *g_last_thread; #if PJ_HAS_THREADS static long thread_suspended_tls_id = -1; # if PJ_LOG_ENABLE_INDENT static long thread_indent_tls_id = -1; # endif #endif #if !PJ_LOG_ENABLE_INDENT || !PJ_HAS_THREADS static int log_indent; #endif static pj_log_func *log_writer = &pj_log_write; static unsigned log_decor = PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_SPACE | PJ_LOG_HAS_THREAD_SWC | PJ_LOG_HAS_INDENT #if (defined(PJ_WIN32) && PJ_WIN32!=0) || \ (defined(PJ_WIN64) && PJ_WIN64!=0) | PJ_LOG_HAS_COLOR #endif ; static pj_color_t PJ_LOG_COLOR_0 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R; static pj_color_t PJ_LOG_COLOR_1 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R; static pj_color_t PJ_LOG_COLOR_2 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G; static pj_color_t PJ_LOG_COLOR_3 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B; static pj_color_t PJ_LOG_COLOR_4 = PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B; static pj_color_t PJ_LOG_COLOR_5 = PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B; static pj_color_t PJ_LOG_COLOR_6 = PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B; /* Default terminal color */ static pj_color_t PJ_LOG_COLOR_77 = PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B; #if PJ_LOG_USE_STACK_BUFFER==0 static char log_buffer[PJ_LOG_MAX_SIZE]; #endif #define LOG_MAX_INDENT 80 #if PJ_HAS_THREADS static void logging_shutdown(void) { if (thread_suspended_tls_id != -1) { pj_thread_local_free(thread_suspended_tls_id); thread_suspended_tls_id = -1; } # if PJ_LOG_ENABLE_INDENT if (thread_indent_tls_id != -1) { pj_thread_local_free(thread_indent_tls_id); thread_indent_tls_id = -1; } # endif } #endif /* PJ_HAS_THREADS */ #if PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS static void log_set_indent(int indent) { if (indent < 0) indent = 0; pj_thread_local_set(thread_indent_tls_id, (void*)(pj_ssize_t)indent); } static int log_get_raw_indent(void) { return (long)(pj_ssize_t)pj_thread_local_get(thread_indent_tls_id); } #else static void log_set_indent(int indent) { log_indent = indent; if (log_indent < 0) log_indent = 0; } static int log_get_raw_indent(void) { return log_indent; } #endif /* PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS */ static int log_get_indent(void) { int indent = log_get_raw_indent(); return indent > LOG_MAX_INDENT ? LOG_MAX_INDENT : indent; } PJ_DEF(void) pj_log_add_indent(int indent) { log_set_indent(log_get_raw_indent() + indent); } PJ_DEF(void) pj_log_push_indent(void) { pj_log_add_indent(PJ_LOG_INDENT_SIZE); } PJ_DEF(void) pj_log_pop_indent(void) { pj_log_add_indent(-PJ_LOG_INDENT_SIZE); } pj_status_t pj_log_init(void) { #if PJ_HAS_THREADS if (thread_suspended_tls_id == -1) { pj_status_t status; status = pj_thread_local_alloc(&thread_suspended_tls_id); if (status != PJ_SUCCESS) return status; # if PJ_LOG_ENABLE_INDENT status = pj_thread_local_alloc(&thread_indent_tls_id); if (status != PJ_SUCCESS) { pj_thread_local_free(thread_suspended_tls_id); thread_suspended_tls_id = -1; return status; } # endif pj_atexit(&logging_shutdown); } #endif g_last_thread = NULL; return PJ_SUCCESS; } PJ_DEF(void) pj_log_set_decor(unsigned decor) { log_decor = decor; } PJ_DEF(unsigned) pj_log_get_decor(void) { return log_decor; } PJ_DEF(void) pj_log_set_color(int level, pj_color_t color) { switch (level) { case 0: PJ_LOG_COLOR_0 = color; break; case 1: PJ_LOG_COLOR_1 = color; break; case 2: PJ_LOG_COLOR_2 = color; break; case 3: PJ_LOG_COLOR_3 = color; break; case 4: PJ_LOG_COLOR_4 = color; break; case 5: PJ_LOG_COLOR_5 = color; break; case 6: PJ_LOG_COLOR_6 = color; break; /* Default terminal color */ case 77: PJ_LOG_COLOR_77 = color; break; default: /* Do nothing */ break; } } PJ_DEF(pj_color_t) pj_log_get_color(int level) { switch (level) { case 0: return PJ_LOG_COLOR_0; case 1: return PJ_LOG_COLOR_1; case 2: return PJ_LOG_COLOR_2; case 3: return PJ_LOG_COLOR_3; case 4: return PJ_LOG_COLOR_4; case 5: return PJ_LOG_COLOR_5; case 6: return PJ_LOG_COLOR_6; default: /* Return default terminal color */ return PJ_LOG_COLOR_77; } } PJ_DEF(void) pj_log_set_level(int level) { pj_log_max_level = level; } #if 1 PJ_DEF(int) pj_log_get_level(void) { return pj_log_max_level; } #endif PJ_DEF(void) pj_log_set_log_func( pj_log_func *func ) { log_writer = func; } PJ_DEF(pj_log_func*) pj_log_get_log_func(void) { return log_writer; } /* Temporarily suspend logging facility for this thread. * If thread local storage/variable is not used or not initialized, then * we can only suspend the logging globally across all threads. This may * happen e.g. when log function is called before PJLIB is fully initialized * or after PJLIB is shutdown. */ static void suspend_logging(int *saved_level) { /* Save the level regardless, just in case PJLIB is shutdown * between suspend and resume. */ *saved_level = pj_log_max_level; #if PJ_HAS_THREADS if (thread_suspended_tls_id != -1) { pj_thread_local_set(thread_suspended_tls_id, (void*)(pj_ssize_t)PJ_TRUE); } else #endif { pj_log_max_level = 0; } } /* Resume logging facility for this thread */ static void resume_logging(int *saved_level) { #if PJ_HAS_THREADS if (thread_suspended_tls_id != -1) { pj_thread_local_set(thread_suspended_tls_id, (void*)(pj_size_t)PJ_FALSE); } else #endif { /* Only revert the level if application doesn't change the * logging level between suspend and resume. */ if (pj_log_max_level==0 && *saved_level) pj_log_max_level = *saved_level; } } /* Is logging facility suspended for this thread? */ static pj_bool_t is_logging_suspended(void) { #if PJ_HAS_THREADS if (thread_suspended_tls_id != -1) { return pj_thread_local_get(thread_suspended_tls_id) != NULL; } else #endif { return pj_log_max_level == 0; } } PJ_DEF(void) pj_log( const char *sender, int level, const char *format, va_list marker) { pj_time_val now; pj_parsed_time ptime; char *pre; #if PJ_LOG_USE_STACK_BUFFER char log_buffer[PJ_LOG_MAX_SIZE]; #endif int saved_level, len, print_len, indent; PJ_CHECK_STACK(); if (level > pj_log_max_level) return; if (is_logging_suspended()) return; /* Temporarily disable logging for this thread. Some of PJLIB APIs that * this function calls below will recursively call the logging function * back, hence it will cause infinite recursive calls if we allow that. */ suspend_logging(&saved_level); /* Get current date/time. */ pj_gettimeofday(&now); pj_time_decode(&now, &ptime); pre = log_buffer; if (log_decor & PJ_LOG_HAS_LEVEL_TEXT) { static const char *ltexts[] = { "FATAL:", "ERROR:", " WARN:", " INFO:", "DEBUG:", "TRACE:", "DETRC:"}; pj_ansi_strcpy(pre, ltexts[level]); pre += 6; } if (log_decor & PJ_LOG_HAS_DAY_NAME) { static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; pj_ansi_strcpy(pre, wdays[ptime.wday]); pre += 3; } if (log_decor & PJ_LOG_HAS_YEAR) { if (pre!=log_buffer) *pre++ = ' '; pre += pj_utoa(ptime.year, pre); } if (log_decor & PJ_LOG_HAS_MONTH) { *pre++ = '-'; pre += pj_utoa_pad(ptime.mon+1, pre, 2, '0'); } if (log_decor & PJ_LOG_HAS_DAY_OF_MON) { *pre++ = '-'; pre += pj_utoa_pad(ptime.day, pre, 2, '0'); } if (log_decor & PJ_LOG_HAS_TIME) { if (pre!=log_buffer) *pre++ = ' '; pre += pj_utoa_pad(ptime.hour, pre, 2, '0'); *pre++ = ':'; pre += pj_utoa_pad(ptime.min, pre, 2, '0'); *pre++ = ':'; pre += pj_utoa_pad(ptime.sec, pre, 2, '0'); } if (log_decor & PJ_LOG_HAS_MICRO_SEC) { *pre++ = '.'; pre += pj_utoa_pad(ptime.msec, pre, 3, '0'); } if (log_decor & PJ_LOG_HAS_SENDER) { enum { SENDER_WIDTH = 14 }; pj_size_t sender_len = strlen(sender); if (pre!=log_buffer) *pre++ = ' '; if (sender_len <= SENDER_WIDTH) { while (sender_len < SENDER_WIDTH) *pre++ = ' ', ++sender_len; while (*sender) *pre++ = *sender++; } else { int i; for (i=0; i 0) { pj_memset(pre, PJ_LOG_INDENT_CHAR, indent); pre += indent; } } #endif len = (int)(pre - log_buffer); /* Print the whole message to the string log_buffer. */ print_len = pj_ansi_vsnprintf(pre, sizeof(log_buffer)-len, format, marker); if (print_len < 0) { level = 1; print_len = pj_ansi_snprintf(pre, sizeof(log_buffer)-len, ""); } if (print_len < 1 || print_len >= (int)(sizeof(log_buffer)-len)) { print_len = sizeof(log_buffer) - len - 1; } len = len + print_len; if (len > 0 && len < (int)sizeof(log_buffer)-2) { if (log_decor & PJ_LOG_HAS_CR) { log_buffer[len++] = '\r'; } if (log_decor & PJ_LOG_HAS_NEWLINE) { log_buffer[len++] = '\n'; } log_buffer[len] = '\0'; } else { len = sizeof(log_buffer)-1; if (log_decor & PJ_LOG_HAS_CR) { log_buffer[sizeof(log_buffer)-3] = '\r'; } if (log_decor & PJ_LOG_HAS_NEWLINE) { log_buffer[sizeof(log_buffer)-2] = '\n'; } log_buffer[sizeof(log_buffer)-1] = '\0'; } /* It should be safe to resume logging at this point. Application can * recursively call the logging function inside the callback. */ resume_logging(&saved_level); if (log_writer) (*log_writer)(level, log_buffer, len); } /* PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...) { va_list arg; va_start(arg, format); pj_log(obj, 0, format, arg); va_end(arg); } */ PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...) { va_list arg; va_start(arg, format); pj_log(obj, 1, format, arg); va_end(arg); } #endif /* PJ_LOG_MAX_LEVEL >= 1 */ #if PJ_LOG_MAX_LEVEL >= 2 PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...) { va_list arg; va_start(arg, format); pj_log(obj, 2, format, arg); va_end(arg); } #endif #if PJ_LOG_MAX_LEVEL >= 3 PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...) { va_list arg; va_start(arg, format); pj_log(obj, 3, format, arg); va_end(arg); } #endif #if PJ_LOG_MAX_LEVEL >= 4 PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...) { va_list arg; va_start(arg, format); pj_log(obj, 4, format, arg); va_end(arg); } #endif #if PJ_LOG_MAX_LEVEL >= 5 PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...) { va_list arg; va_start(arg, format); pj_log(obj, 5, format, arg); va_end(arg); } #endif #if PJ_LOG_MAX_LEVEL >= 6 PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...) { va_list arg; va_start(arg, format); pj_log(obj, 6, format, arg); va_end(arg); } #endif ================================================ FILE: deps/pjsip/pjlib/src/pj/log_writer_printk.c ================================================ /* $Id: log_writer_printk.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include PJ_DEF(void) pj_log_write(int level, const char *buffer, int len) { PJ_CHECK_STACK(); printk(KERN_INFO "%s", buffer); } ================================================ FILE: deps/pjsip/pjlib/src/pj/log_writer_stdout.c ================================================ /* $Id: log_writer_stdout.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include static void term_set_color(int level) { #if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 pj_term_set_color(pj_log_get_color(level)); #else PJ_UNUSED_ARG(level); #endif } static void term_restore_color(void) { #if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 /* Set terminal to its default color */ pj_term_set_color(pj_log_get_color(77)); #endif } PJ_DEF(void) pj_log_write(int level, const char *buffer, int len) { PJ_CHECK_STACK(); PJ_UNUSED_ARG(len); /* Copy to terminal/file. */ if (pj_log_get_decor() & PJ_LOG_HAS_COLOR) { term_set_color(level); printf("%s", buffer); term_restore_color(); } else { printf("%s", buffer); } } ================================================ FILE: deps/pjsip/pjlib/src/pj/log_writer_symbian_console.cpp ================================================ /* $Id: log_writer_symbian_console.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "os_symbian.h" #include PJ_DEF(void) pj_log_write(int level, const char *buffer, int len) { #if 0 wchar_t wbuffer[PJ_LOG_MAX_SIZE]; CConsoleBase *cons = PjSymbianOS::Instance->Console(); pj_ansi_to_unicode(buffer, len, wbuffer, PJ_ARRAY_SIZE(wbuffer)); TPtrC16 aPtr((TUint16*)wbuffer, len); console->Write(aPtr); #else PJ_UNUSED_ARG(level); PJ_UNUSED_ARG(buffer); PJ_UNUSED_ARG(len); #endif } ================================================ FILE: deps/pjsip/pjlib/src/pj/os_core_darwin.m ================================================ /* $Id: os_core_darwin.m 3670 2011-07-20 03:00:48Z ming $ */ /* * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "TargetConditionals.h" #if TARGET_OS_IPHONE PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], unsigned flags) { return (*main_func)(argc, argv); } #else #include #include #include #include #define THIS_FILE "os_core_darwin.m" typedef struct run_app_t { pj_main_func_ptr main_func; int argc; char **argv; int retval; } run_app_t; @interface DeadThread: NSObject { ;; } + (void)enterMultiThreadedMode; + (void)emptyThreadMethod:(id)obj; @end @implementation DeadThread + (void)enterMultiThreadedMode { [NSThread detachNewThreadSelector:@selector(emptyThreadMethod:) toTarget:[DeadThread class] withObject:nil]; } + (void)emptyThreadMethod:(id)obj { ; } @end static void* main_thread(void *data) { run_app_t *param = (run_app_t *)data; param->retval = (*param->main_func)(param->argc, param->argv); CFRunLoopStop(CFRunLoopGetMain()); return NULL; } /* * pj_run_app() * This function has to be called from the main thread. The purpose of * this function is to initialize the application's memory pool, event * loop management, and multi-threading environment. */ PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], unsigned flags) { pthread_t thread; run_app_t param; NSAutoreleasePool *pool; pool = [[NSAutoreleasePool alloc] init]; [NSApplication sharedApplication]; [DeadThread enterMultiThreadedMode]; param.argc = argc; param.argv = (char **)argv; param.main_func = main_func; if (pthread_create(&thread, NULL, &main_thread, ¶m) == 0) { CFRunLoopRun(); } PJ_UNUSED_ARG(pool); return param.retval; } #endif ================================================ FILE: deps/pjsip/pjlib/src/pj/os_core_linux_kernel.c ================================================ /* $Id: os_core_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #if defined(MODVERSIONS) #include #endif #include #include //#include #include #include #include #include #include #define THIS_FILE "oslinuxkern" struct pj_thread_t { /** Thread's name. */ char obj_name[PJ_MAX_OBJ_NAME]; /** Linux task structure for thread. */ struct task_struct *thread; /** Flags (specified in pj_thread_create) */ unsigned flags; /** Task queue needed to launch thread. */ //struct tq_struct tq; /** Semaphore needed to control thread startup. */ struct semaphore startstop_sem; /** Semaphore to suspend thread during startup. */ struct semaphore suspend_sem; /** Queue thread is waiting on. Gets initialized by thread_initialize, can be used by thread itself. */ wait_queue_head_t queue; /** Flag to tell thread whether to die or not. When the thread receives a signal, it must check the value of terminate and call thread_deinitialize and terminate if set. */ int terminate; /** Thread's entry. */ pj_thread_proc *func; /** Argument. */ void *arg; }; struct pj_atomic_t { atomic_t atom; }; struct pj_mutex_t { struct semaphore sem; pj_bool_t recursive; pj_thread_t *owner; int own_count; }; struct pj_sem_t { struct semaphore sem; }; /* * Static global variables. */ #define MAX_TLS_ID 32 static void *tls_values[MAX_TLS_ID]; static int tls_id; static long thread_tls_id; static spinlock_t critical_section = SPIN_LOCK_UNLOCKED; static unsigned long spinlock_flags; static pj_thread_t main_thread; /* private functions */ //#define TRACE_(expr) PJ_LOG(3,expr) #define TRACE_(x) /* This must be called in the context of the new thread. */ static void thread_initialize( pj_thread_t *thread ) { TRACE_((THIS_FILE, "---new thread initializing...")); /* Set TLS */ pj_thread_local_set(thread_tls_id, thread); /* fill in thread structure */ thread->thread = current; pj_assert(thread->thread != NULL); /* set signal mask to what we want to respond */ siginitsetinv(¤t->blocked, sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)); /* initialise wait queue */ init_waitqueue_head(&thread->queue); /* initialise termination flag */ thread->terminate = 0; /* set name of this process (making sure obj_name is null * terminated first) */ thread->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; sprintf(current->comm, thread->obj_name); /* tell the creator that we are ready and let him continue */ up(&thread->startstop_sem); } /* cleanup of thread. Called by the exiting thread. */ static void thread_deinitialize(pj_thread_t *thread) { /* we are terminating */ /* lock the kernel, the exit will unlock it */ thread->thread = NULL; mb(); /* notify the stop_kthread() routine that we are terminating. */ up(&thread->startstop_sem); /* the kernel_thread that called clone() does a do_exit here. */ /* there is no race here between execution of the "killer" and real termination of the thread (race window between up and do_exit), since both the thread and the "killer" function are running with the kernel lock held. The kernel lock will be freed after the thread exited, so the code is really not executed anymore as soon as the unload functions gets the kernel lock back. The init process may not have made the cleanup of the process here, but the cleanup can be done safely with the module unloaded. */ } static int thread_proc(void *arg) { pj_thread_t *thread = arg; TRACE_((THIS_FILE, "---new thread starting!")); /* Initialize thread. */ thread_initialize( thread ); /* Wait if created suspended. */ if (thread->flags & PJ_THREAD_SUSPENDED) { TRACE_((THIS_FILE, "---new thread suspended...")); down(&thread->suspend_sem); } TRACE_((THIS_FILE, "---new thread running...")); pj_assert(thread->func != NULL); /* Call thread's entry. */ (*thread->func)(thread->arg); TRACE_((THIS_FILE, "---thread exiting...")); /* Cleanup thread. */ thread_deinitialize(thread); return 0; } /* The very task entry. */ static void kthread_launcher(void *arg) { TRACE_((THIS_FILE, "...launching thread!...")); kernel_thread(&thread_proc, arg, 0); } PJ_DEF(pj_status_t) pj_init(void) { pj_status_t rc; PJ_LOG(5, ("pj_init", "Initializing PJ Library..")); rc = pj_thread_init(); if (rc != PJ_SUCCESS) return rc; /* Initialize exception ID for the pool. * Must do so after critical section is configured. */ rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); if (rc != PJ_SUCCESS) return rc; return PJ_SUCCESS; } PJ_DEF(pj_uint32_t) pj_getpid(void) { return 1; } PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, pj_thread_desc desc, pj_thread_t **ptr_thread) { char stack_ptr; pj_thread_t *thread = (pj_thread_t *)desc; pj_str_t thread_name = pj_str((char*)cstr_thread_name); /* Size sanity check. */ if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { pj_assert(!"Not enough pj_thread_desc size!"); return PJ_EBUG; } /* If a thread descriptor has been registered before, just return it. */ if (pj_thread_local_get (thread_tls_id) != 0) { // 2006-02-26 bennylp: // This wouldn't work in all cases!. // If thread is created by external module (e.g. sound thread), // thread may be reused while the pool used for the thread descriptor // has been deleted by application. //*thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id); //return PJ_SUCCESS; } /* Initialize and set the thread entry. */ pj_bzero(desc, sizeof(struct pj_thread_t)); if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread); else pj_snprintf(thread->obj_name, sizeof(thread->obj_name), "thr%p", (void*)thread->thread); /* Initialize. */ thread_initialize(thread); /* Eat semaphore. */ down(&thread->startstop_sem); #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 thread->stk_start = &stack_ptr; thread->stk_size = 0xFFFFFFFFUL; thread->stk_max_usage = 0; #else stack_ptr = '\0'; #endif *ptr_thread = thread; return PJ_SUCCESS; } pj_status_t pj_thread_init(void) { pj_status_t rc; pj_thread_t *dummy; rc = pj_thread_local_alloc(&thread_tls_id); if (rc != PJ_SUCCESS) return rc; return pj_thread_register("pjlib-main", (long*)&main_thread, &dummy); } PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name, pj_thread_proc *proc, void *arg, pj_size_t stack_size, unsigned flags, pj_thread_t **ptr_thread) { pj_thread_t *thread; TRACE_((THIS_FILE, "pj_thread_create()")); PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL); thread = pj_pool_zalloc(pool, sizeof(pj_thread_t)); if (!thread) return PJ_ENOMEM; PJ_UNUSED_ARG(stack_size); /* Thread name. */ if (!thread_name) thread_name = "thr%p"; if (strchr(thread_name, '%')) { pj_snprintf(thread->obj_name, PJ_MAX_OBJ_NAME, thread_name, thread); } else { strncpy(thread->obj_name, thread_name, PJ_MAX_OBJ_NAME); thread->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; } /* Init thread's semaphore. */ TRACE_((THIS_FILE, "...init semaphores...")); init_MUTEX_LOCKED(&thread->startstop_sem); init_MUTEX_LOCKED(&thread->suspend_sem); thread->flags = flags; if ((flags & PJ_THREAD_SUSPENDED) == 0) { up(&thread->suspend_sem); } /* Store the functions and argument. */ thread->func = proc; thread->arg = arg; /* Save return value. */ *ptr_thread = thread; /* Create the new thread by running a task through keventd. */ #if 0 /* Initialize the task queue struct. */ thread->tq.sync = 0; INIT_LIST_HEAD(&thread->tq.list); thread->tq.routine = kthread_launcher; thread->tq.data = thread; /* and schedule it for execution. */ schedule_task(&thread->tq); #endif kthread_launcher(thread); /* Wait until thread has reached the setup_thread routine. */ TRACE_((THIS_FILE, "...wait for the new thread...")); down(&thread->startstop_sem); TRACE_((THIS_FILE, "...main thread resumed...")); return PJ_SUCCESS; } PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *thread) { return thread->obj_name; } PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *thread) { up(&thread->suspend_sem); return PJ_SUCCESS; } PJ_DEF(pj_thread_t*) pj_thread_this(void) { return (pj_thread_t*)pj_thread_local_get(thread_tls_id); } PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) { TRACE_((THIS_FILE, "pj_thread_join()")); down(&p->startstop_sem); TRACE_((THIS_FILE, " joined!")); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *thread) { PJ_ASSERT_RETURN(thread != NULL, PJ_EINVALIDOP); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) { pj_highprec_t ticks; pj_thread_t *thread = pj_thread_this(); PJ_ASSERT_RETURN(thread != NULL, PJ_EBUG); /* Use high precision calculation to make sure we don't * crop values: * * ticks = HZ * msec / 1000 */ ticks = HZ; pj_highprec_mul(ticks, msec); pj_highprec_div(ticks, 1000); TRACE_((THIS_FILE, "this thread will sleep for %u ticks", ticks)); interruptible_sleep_on_timeout( &thread->queue, ticks); return PJ_SUCCESS; } /////////////////////////////////////////////////////////////////////////////// PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, pj_atomic_value_t value, pj_atomic_t **ptr_var) { pj_atomic_t *t = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t)); if (!t) return PJ_ENOMEM; atomic_set(&t->atom, value); *ptr_var = t; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var ) { return PJ_SUCCESS; } PJ_DEF(void) pj_atomic_set(pj_atomic_t *var, pj_atomic_value_t value) { atomic_set(&var->atom, value); } PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *var) { return atomic_read(&var->atom); } PJ_DEF(void) pj_atomic_inc(pj_atomic_t *var) { atomic_inc(&var->atom); } PJ_DEF(void) pj_atomic_dec(pj_atomic_t *var) { atomic_dec(&var->atom); } PJ_DEF(void) pj_atomic_add( pj_atomic_t *var, pj_atomic_value_t value ) { atomic_add(value, &var->atom); } /////////////////////////////////////////////////////////////////////////////// PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index) { if (tls_id >= MAX_TLS_ID) return PJ_ETOOMANY; *index = tls_id++; return PJ_SUCCESS; } PJ_DEF(void) pj_thread_local_free(long index) { pj_assert(index >= 0 && index < MAX_TLS_ID); } PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value) { pj_assert(index >= 0 && index < MAX_TLS_ID); tls_values[index] = value; return PJ_SUCCESS; } PJ_DEF(void*) pj_thread_local_get(long index) { pj_assert(index >= 0 && index < MAX_TLS_ID); return tls_values[index]; } /////////////////////////////////////////////////////////////////////////////// PJ_DEF(void) pj_enter_critical_section(void) { spin_lock_irqsave(&critical_section, spinlock_flags); } PJ_DEF(void) pj_leave_critical_section(void) { spin_unlock_irqrestore(&critical_section, spinlock_flags); } /////////////////////////////////////////////////////////////////////////////// PJ_DEF(pj_status_t) pj_mutex_create( pj_pool_t *pool, const char *name, int type, pj_mutex_t **ptr_mutex) { pj_mutex_t *mutex; PJ_UNUSED_ARG(name); mutex = pj_pool_alloc(pool, sizeof(pj_mutex_t)); if (!mutex) return PJ_ENOMEM; init_MUTEX(&mutex->sem); mutex->recursive = (type == PJ_MUTEX_RECURSE); mutex->owner = NULL; mutex->own_count = 0; /* Done. */ *ptr_mutex = mutex; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name, pj_mutex_t **mutex ) { return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); } PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, const char *name, pj_mutex_t **mutex ) { return pj_mutex_create( pool, name, PJ_MUTEX_RECURSE, mutex); } PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) { PJ_ASSERT_RETURN(mutex, PJ_EINVAL); if (mutex->recursive) { pj_thread_t *this_thread = pj_thread_this(); if (mutex->owner == this_thread) { ++mutex->own_count; } else { down(&mutex->sem); pj_assert(mutex->own_count == 0); mutex->owner = this_thread; mutex->own_count = 1; } } else { down(&mutex->sem); } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) { long rc; PJ_ASSERT_RETURN(mutex, PJ_EINVAL); if (mutex->recursive) { pj_thread_t *this_thread = pj_thread_this(); if (mutex->owner == this_thread) { ++mutex->own_count; } else { rc = down_interruptible(&mutex->sem); if (rc != 0) return PJ_RETURN_OS_ERROR(-rc); pj_assert(mutex->own_count == 0); mutex->owner = this_thread; mutex->own_count = 1; } } else { int rc = down_trylock(&mutex->sem); if (rc != 0) return PJ_RETURN_OS_ERROR(-rc); } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) { PJ_ASSERT_RETURN(mutex, PJ_EINVAL); if (mutex->recursive) { pj_thread_t *this_thread = pj_thread_this(); if (mutex->owner == this_thread) { pj_assert(mutex->own_count > 0); --mutex->own_count; if (mutex->own_count == 0) { mutex->owner = NULL; up(&mutex->sem); } } else { pj_assert(!"Not owner!"); return PJ_EINVALIDOP; } } else { up(&mutex->sem); } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) { PJ_ASSERT_RETURN(mutex != NULL, PJ_EINVAL); return PJ_SUCCESS; } #if defined(PJ_DEBUG) && PJ_DEBUG != 0 PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) { if (mutex->recursive) return mutex->owner == pj_thread_this(); else return 1; } #endif /* PJ_DEBUG */ #if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, const char *name, unsigned initial, unsigned max, pj_sem_t **sem) { pj_sem_t *sem; PJ_UNUSED_ARG(max); PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); sem = pj_pool_alloc(pool, sizeof(pj_sem_t)); sema_init(&sem->sem, initial); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) { PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); down(&sem->sem); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) { int rc; PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); rc = down_trylock(&sem->sem); if (rc != 0) { return PJ_RETURN_OS_ERROR(-rc); } else { return PJ_SUCCESS; } } PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) { PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); up(&sem->sem); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) { PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); return PJ_SUCCESS; } #endif /* PJ_HAS_SEMAPHORE */ ================================================ FILE: deps/pjsip/pjlib/src/pj/os_core_symbian.cpp ================================================ /* $Id: os_core_symbian.cpp 3999 2012-03-30 07:10:13Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include "os_symbian.h" #define PJ_MAX_TLS 32 #define DUMMY_MUTEX ((pj_mutex_t*)101) #define DUMMY_SEMAPHORE ((pj_sem_t*)102) #define THIS_FILE "os_core_symbian.c" /* Default message slot number for RSocketServ::Connect(). * Increase it to 32 from the default 8 (KESockDefaultMessageSlots) */ #ifndef PJ_SYMBIAN_SOCK_MSG_SLOTS # define PJ_SYMBIAN_SOCK_MSG_SLOTS 32 #endif /* * Note: * * The Symbian implementation does not support threading! */ struct pj_thread_t { char obj_name[PJ_MAX_OBJ_NAME]; void *tls_values[PJ_MAX_TLS]; #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 pj_uint32_t stk_size; pj_uint32_t stk_max_usage; char *stk_start; const char *caller_file; int caller_line; #endif } main_thread; struct pj_atomic_t { pj_atomic_value_t value; }; struct pj_sem_t { int value; int max; }; /* Flag and reference counter for PJLIB instance */ static int initialized; /* Flags to indicate which TLS variables have been used */ static int tls_vars[PJ_MAX_TLS]; /* atexit handlers */ static unsigned atexit_count; static void (*atexit_func[32])(void); ///////////////////////////////////////////////////////////////////////////// // // CPjTimeoutTimer implementation // CPjTimeoutTimer::CPjTimeoutTimer() : CActive(PJ_SYMBIAN_TIMER_PRIORITY), hasTimedOut_(PJ_FALSE) { } CPjTimeoutTimer::~CPjTimeoutTimer() { Cancel(); timer_.Close(); } void CPjTimeoutTimer::ConstructL() { hasTimedOut_ = PJ_FALSE; timer_.CreateLocal(); CActiveScheduler::Add(this); } CPjTimeoutTimer *CPjTimeoutTimer::NewL() { CPjTimeoutTimer *self = new CPjTimeoutTimer; CleanupStack::PushL(self); self->ConstructL(); CleanupStack::Pop(self); return self; } void CPjTimeoutTimer::StartTimer(TUint miliSeconds) { Cancel(); hasTimedOut_ = PJ_FALSE; timer_.After(iStatus, miliSeconds * 1000); SetActive(); } bool CPjTimeoutTimer::HasTimedOut() const { return hasTimedOut_ != 0; } void CPjTimeoutTimer::RunL() { hasTimedOut_ = PJ_TRUE; } void CPjTimeoutTimer::DoCancel() { timer_.Cancel(); } TInt CPjTimeoutTimer::RunError(TInt aError) { PJ_UNUSED_ARG(aError); return KErrNone; } ///////////////////////////////////////////////////////////////////////////// // // PjSymbianOS implementation // PjSymbianOS::PjSymbianOS() : isConnectionUp_(false), isSocketServInitialized_(false), isResolverInitialized_(false), console_(NULL), selectTimeoutTimer_(NULL), appSocketServ_(NULL), appConnection_(NULL), appHostResolver_(NULL), appHostResolver6_(NULL) { } // Set parameters void PjSymbianOS::SetParameters(pj_symbianos_params *params) { appSocketServ_ = (RSocketServ*) params->rsocketserv; appConnection_ = (RConnection*) params->rconnection; appHostResolver_ = (RHostResolver*) params->rhostresolver; appHostResolver6_ = (RHostResolver*) params->rhostresolver6; } // Get PjSymbianOS instance PjSymbianOS *PjSymbianOS::Instance() { static PjSymbianOS instance_; return &instance_; } // Initialize TInt PjSymbianOS::Initialize() { TInt err; selectTimeoutTimer_ = CPjTimeoutTimer::NewL(); #if 0 pj_assert(console_ == NULL); TRAPD(err, console_ = Console::NewL(_L("PJLIB"), TSize(KConsFullScreen,KConsFullScreen))); return err; #endif /* Only create RSocketServ if application doesn't specify it * in the parameters */ if (!isSocketServInitialized_ && appSocketServ_ == NULL) { err = socketServ_.Connect(PJ_SYMBIAN_SOCK_MSG_SLOTS); if (err != KErrNone) goto on_error; isSocketServInitialized_ = true; } if (!isResolverInitialized_) { if (appHostResolver_ == NULL) { if (Connection()) err = hostResolver_.Open(SocketServ(), KAfInet, KSockStream, *Connection()); else err = hostResolver_.Open(SocketServ(), KAfInet, KSockStream); if (err != KErrNone) goto on_error; } #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0 if (appHostResolver6_ == NULL) { if (Connection()) err = hostResolver6_.Open(SocketServ(), KAfInet6, KSockStream, *Connection()); else err = hostResolver6_.Open(SocketServ(), KAfInet6, KSockStream); if (err != KErrNone) goto on_error; } #endif isResolverInitialized_ = true; } isConnectionUp_ = true; return KErrNone; on_error: Shutdown(); return err; } // Shutdown void PjSymbianOS::Shutdown() { isConnectionUp_ = false; if (isResolverInitialized_) { hostResolver_.Close(); #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0 hostResolver6_.Close(); #endif isResolverInitialized_ = false; } if (isSocketServInitialized_) { socketServ_.Close(); isSocketServInitialized_ = false; } delete console_; console_ = NULL; delete selectTimeoutTimer_; selectTimeoutTimer_ = NULL; appSocketServ_ = NULL; appConnection_ = NULL; appHostResolver_ = NULL; appHostResolver6_ = NULL; } // Convert to Unicode TInt PjSymbianOS::ConvertToUnicode(TDes16 &aUnicode, const TDesC8 &aForeign) { #if 0 pj_assert(conv_ != NULL); return conv_->ConvertToUnicode(aUnicode, aForeign, convToUnicodeState_); #else return CnvUtfConverter::ConvertToUnicodeFromUtf8(aUnicode, aForeign); #endif } // Convert from Unicode TInt PjSymbianOS::ConvertFromUnicode(TDes8 &aForeign, const TDesC16 &aUnicode) { #if 0 pj_assert(conv_ != NULL); return conv_->ConvertFromUnicode(aForeign, aUnicode, convToAnsiState_); #else return CnvUtfConverter::ConvertFromUnicodeToUtf8(aForeign, aUnicode); #endif } ///////////////////////////////////////////////////////////////////////////// // // PJLIB os.h implementation // PJ_DEF(pj_uint32_t) pj_getpid(void) { return 0; } /* Set Symbian specific parameters */ PJ_DEF(pj_status_t) pj_symbianos_set_params(pj_symbianos_params *prm) { PJ_ASSERT_RETURN(prm != NULL, PJ_EINVAL); PjSymbianOS::Instance()->SetParameters(prm); return PJ_SUCCESS; } /* Set connection status */ PJ_DEF(void) pj_symbianos_set_connection_status(pj_bool_t up) { PjSymbianOS::Instance()->SetConnectionStatus(up != 0); } /* * pj_init(void). * Init PJLIB! */ PJ_DEF(pj_status_t) pj_init(void) { char stack_ptr; pj_status_t status; /* Check if PJLIB have been initialized */ if (initialized) { ++initialized; return PJ_SUCCESS; } pj_ansi_strcpy(main_thread.obj_name, "pjthread"); // Init main thread pj_memset(&main_thread, 0, sizeof(main_thread)); // Initialize PjSymbianOS instance PjSymbianOS *os = PjSymbianOS::Instance(); PJ_LOG(4,(THIS_FILE, "Initializing PJLIB for Symbian OS..")); TInt err; err = os->Initialize(); if (err != KErrNone) return PJ_RETURN_OS_ERROR(err); /* Init logging */ pj_log_init(); /* Initialize exception ID for the pool. * Must do so after critical section is configured. */ status = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); if (status != PJ_SUCCESS) goto on_error; #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 main_thread.stk_start = &stack_ptr; main_thread.stk_size = 0xFFFFFFFFUL; main_thread.stk_max_usage = 0; #else stack_ptr = '\0'; #endif /* Flag PJLIB as initialized */ ++initialized; pj_assert(initialized == 1); PJ_LOG(5,(THIS_FILE, "PJLIB initialized.")); return PJ_SUCCESS; on_error: pj_shutdown(); return PJ_RETURN_OS_ERROR(err); } PJ_DEF(pj_status_t) pj_atexit(pj_exit_callback func) { if (atexit_count >= PJ_ARRAY_SIZE(atexit_func)) return PJ_ETOOMANY; atexit_func[atexit_count++] = func; return PJ_SUCCESS; } PJ_DEF(void) pj_shutdown(void) { /* Only perform shutdown operation when 'initialized' reaches zero */ pj_assert(initialized > 0); if (--initialized != 0) return; /* Call atexit() functions */ while (atexit_count > 0) { (*atexit_func[atexit_count-1])(); --atexit_count; } /* Free exception ID */ if (PJ_NO_MEMORY_EXCEPTION != -1) { pj_exception_id_free(PJ_NO_MEMORY_EXCEPTION); PJ_NO_MEMORY_EXCEPTION = -1; } /* Clear static variables */ pj_errno_clear_handlers(); PjSymbianOS *os = PjSymbianOS::Instance(); os->Shutdown(); } ///////////////////////////////////////////////////////////////////////////// class CPollTimeoutTimer : public CActive { public: static CPollTimeoutTimer* NewL(int msec, TInt prio); ~CPollTimeoutTimer(); virtual void RunL(); virtual void DoCancel(); private: RTimer rtimer_; explicit CPollTimeoutTimer(TInt prio); void ConstructL(int msec); }; CPollTimeoutTimer::CPollTimeoutTimer(TInt prio) : CActive(prio) { } CPollTimeoutTimer::~CPollTimeoutTimer() { rtimer_.Close(); } void CPollTimeoutTimer::ConstructL(int msec) { rtimer_.CreateLocal(); CActiveScheduler::Add(this); rtimer_.After(iStatus, msec*1000); SetActive(); } CPollTimeoutTimer* CPollTimeoutTimer::NewL(int msec, TInt prio) { CPollTimeoutTimer *self = new CPollTimeoutTimer(prio); CleanupStack::PushL(self); self->ConstructL(msec); CleanupStack::Pop(self); return self; } void CPollTimeoutTimer::RunL() { } void CPollTimeoutTimer::DoCancel() { rtimer_.Cancel(); } /* * Wait the completion of any Symbian active objects. */ PJ_DEF(pj_bool_t) pj_symbianos_poll(int priority, int ms_timeout) { CPollTimeoutTimer *timer = NULL; if (priority==-1) priority = EPriorityNull; if (ms_timeout >= 0) { timer = CPollTimeoutTimer::NewL(ms_timeout, priority); } PjSymbianOS::Instance()->WaitForActiveObjects(priority); if (timer) { bool timer_is_active = timer->IsActive(); timer->Cancel(); delete timer; return timer_is_active ? PJ_TRUE : PJ_FALSE; } else { return PJ_TRUE; } } /* * pj_thread_is_registered() */ PJ_DEF(pj_bool_t) pj_thread_is_registered(void) { return PJ_FALSE; } /* * Get thread priority value for the thread. */ PJ_DEF(int) pj_thread_get_prio(pj_thread_t *thread) { PJ_UNUSED_ARG(thread); return 1; } /* * Set the thread priority. */ PJ_DEF(pj_status_t) pj_thread_set_prio(pj_thread_t *thread, int prio) { PJ_UNUSED_ARG(thread); PJ_UNUSED_ARG(prio); return PJ_SUCCESS; } /* * Get the lowest priority value available on this system. */ PJ_DEF(int) pj_thread_get_prio_min(pj_thread_t *thread) { PJ_UNUSED_ARG(thread); return 1; } /* * Get the highest priority value available on this system. */ PJ_DEF(int) pj_thread_get_prio_max(pj_thread_t *thread) { PJ_UNUSED_ARG(thread); return 1; } /* * pj_thread_get_os_handle() */ PJ_DEF(void*) pj_thread_get_os_handle(pj_thread_t *thread) { PJ_UNUSED_ARG(thread); return NULL; } /* * pj_thread_register(..) */ PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, pj_thread_desc desc, pj_thread_t **thread_ptr) { PJ_UNUSED_ARG(cstr_thread_name); PJ_UNUSED_ARG(desc); PJ_UNUSED_ARG(thread_ptr); return PJ_EINVALIDOP; } /* * pj_thread_create(...) */ PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name, pj_thread_proc *proc, void *arg, pj_size_t stack_size, unsigned flags, pj_thread_t **ptr_thread) { PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(thread_name); PJ_UNUSED_ARG(proc); PJ_UNUSED_ARG(arg); PJ_UNUSED_ARG(stack_size); PJ_UNUSED_ARG(flags); PJ_UNUSED_ARG(ptr_thread); /* Sorry mate, we don't support threading */ return PJ_ENOTSUP; } /* * pj_thread-get_name() */ PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p) { pj_assert(p == &main_thread); return p->obj_name; } /* * pj_thread_resume() */ PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) { PJ_UNUSED_ARG(p); return PJ_EINVALIDOP; } /* * pj_thread_this() */ PJ_DEF(pj_thread_t*) pj_thread_this(void) { return &main_thread; } /* * pj_thread_join() */ PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *rec) { PJ_UNUSED_ARG(rec); return PJ_EINVALIDOP; } /* * pj_thread_destroy() */ PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *rec) { PJ_UNUSED_ARG(rec); return PJ_EINVALIDOP; } /* * pj_thread_sleep() */ PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) { User::After(msec*1000); return PJ_SUCCESS; } /////////////////////////////////////////////////////////////////////////////// /* * pj_thread_local_alloc() */ PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index) { unsigned i; /* Find unused TLS variable */ for (i=0; i= 0 && index < (int)PJ_ARRAY_SIZE(tls_vars) && tls_vars[index] != 0, return); tls_vars[index] = 0; } /* * pj_thread_local_set() */ PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value) { pj_thread_t *rec = pj_thread_this(); PJ_ASSERT_RETURN(index >= 0 && index < (int)PJ_ARRAY_SIZE(tls_vars) && tls_vars[index] != 0, PJ_EINVAL); rec->tls_values[index] = value; return PJ_SUCCESS; } /* * pj_thread_local_get() */ PJ_DEF(void*) pj_thread_local_get(long index) { pj_thread_t *rec = pj_thread_this(); PJ_ASSERT_RETURN(index >= 0 && index < (int)PJ_ARRAY_SIZE(tls_vars) && tls_vars[index] != 0, NULL); return rec->tls_values[index]; } /////////////////////////////////////////////////////////////////////////////// /* * Create atomic variable. */ PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, pj_atomic_value_t initial, pj_atomic_t **atomic ) { *atomic = (pj_atomic_t*)pj_pool_alloc(pool, sizeof(struct pj_atomic_t)); (*atomic)->value = initial; return PJ_SUCCESS; } /* * Destroy atomic variable. */ PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var ) { PJ_UNUSED_ARG(atomic_var); return PJ_SUCCESS; } /* * Set the value of an atomic type, and return the previous value. */ PJ_DEF(void) pj_atomic_set( pj_atomic_t *atomic_var, pj_atomic_value_t value) { atomic_var->value = value; } /* * Get the value of an atomic type. */ PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var) { return atomic_var->value; } /* * Increment the value of an atomic type. */ PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var) { ++atomic_var->value; } /* * Increment the value of an atomic type and get the result. */ PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var) { return ++atomic_var->value; } /* * Decrement the value of an atomic type. */ PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var) { --atomic_var->value; } /* * Decrement the value of an atomic type and get the result. */ PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var) { return --atomic_var->value; } /* * Add a value to an atomic type. */ PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var, pj_atomic_value_t value) { atomic_var->value += value; } /* * Add a value to an atomic type and get the result. */ PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var, pj_atomic_value_t value) { atomic_var->value += value; return atomic_var->value; } ///////////////////////////////////////////////////////////////////////////// PJ_DEF(pj_status_t) pj_mutex_create( pj_pool_t *pool, const char *name, int type, pj_mutex_t **mutex) { PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(name); PJ_UNUSED_ARG(type); *mutex = DUMMY_MUTEX; return PJ_SUCCESS; } /* * pj_mutex_create_simple() */ PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name, pj_mutex_t **mutex ) { return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); } PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, const char *name, pj_mutex_t **mutex ) { return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex); } /* * pj_mutex_lock() */ PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) { pj_assert(mutex == DUMMY_MUTEX); return PJ_SUCCESS; } /* * pj_mutex_trylock() */ PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) { pj_assert(mutex == DUMMY_MUTEX); return PJ_SUCCESS; } /* * pj_mutex_unlock() */ PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) { pj_assert(mutex == DUMMY_MUTEX); return PJ_SUCCESS; } /* * pj_mutex_destroy() */ PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) { pj_assert(mutex == DUMMY_MUTEX); return PJ_SUCCESS; } ///////////////////////////////////////////////////////////////////////////// /* * RW Mutex */ #include "os_rwmutex.c" ///////////////////////////////////////////////////////////////////////////// /* * Enter critical section. */ PJ_DEF(void) pj_enter_critical_section(void) { /* Nothing to do */ } /* * Leave critical section. */ PJ_DEF(void) pj_leave_critical_section(void) { /* Nothing to do */ } ///////////////////////////////////////////////////////////////////////////// /* * Create semaphore. */ PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, const char *name, unsigned initial, unsigned max, pj_sem_t **p_sem) { pj_sem_t *sem; PJ_UNUSED_ARG(name); sem = (pj_sem_t*) pj_pool_zalloc(pool, sizeof(pj_sem_t)); sem->value = initial; sem->max = max; *p_sem = sem; return PJ_SUCCESS; } /* * Wait for semaphore. */ PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) { if (sem->value > 0) { sem->value--; return PJ_SUCCESS; } else { pj_assert(!"Unexpected!"); return PJ_EINVALIDOP; } } /* * Try wait for semaphore. */ PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) { if (sem->value > 0) { sem->value--; return PJ_SUCCESS; } else { pj_assert(!"Unexpected!"); return PJ_EINVALIDOP; } } /* * Release semaphore. */ PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) { sem->value++; return PJ_SUCCESS; } /* * Destroy semaphore. */ PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) { PJ_UNUSED_ARG(sem); return PJ_SUCCESS; } #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0 /* * The implementation of stack checking. */ PJ_DEF(void) pj_thread_check_stack(const char *file, int line) { char stk_ptr; pj_uint32_t usage; pj_thread_t *thread = pj_thread_this(); pj_assert(thread); /* Calculate current usage. */ usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start : thread->stk_start - &stk_ptr; /* Assert if stack usage is dangerously high. */ pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128)); /* Keep statistic. */ if (usage > thread->stk_max_usage) { thread->stk_max_usage = usage; thread->caller_file = file; thread->caller_line = line; } } /* * Get maximum stack usage statistic. */ PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread) { return thread->stk_max_usage; } /* * Dump thread stack status. */ PJ_DEF(pj_status_t) pj_thread_get_stack_info(pj_thread_t *thread, const char **file, int *line) { pj_assert(thread); *file = thread->caller_file; *line = thread->caller_line; return 0; } #endif /* PJ_OS_HAS_CHECK_STACK */ /* * pj_run_app() */ PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], unsigned flags) { return (*main_func)(argc, argv); } ================================================ FILE: deps/pjsip/pjlib/src/pj/os_core_unix.c ================================================ /* $Id: os_core_unix.c 4359 2013-02-21 11:18:36Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Contributors: * - Thanks for Zetron, Inc. (Phil Torre, ptorre@zetron.com) for donating * the RTEMS port. */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #if defined(PJ_HAS_SEMAPHORE_H) && PJ_HAS_SEMAPHORE_H != 0 # include # if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 # include # include # include # endif #endif #include // getpid() #include // errno #include #define THIS_FILE "os_core_unix.c" #define SIGNATURE1 0xDEAFBEEF #define SIGNATURE2 0xDEADC0DE #ifndef PJ_JNI_HAS_JNI_ONLOAD # define PJ_JNI_HAS_JNI_ONLOAD PJ_ANDROID #endif #if defined(PJ_JNI_HAS_JNI_ONLOAD) && PJ_JNI_HAS_JNI_ONLOAD != 0 #include JavaVM *pj_jni_jvm = NULL; JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { pj_jni_jvm = vm; return JNI_VERSION_1_4; } #endif struct pj_thread_t { char obj_name[PJ_MAX_OBJ_NAME]; pthread_t thread; pj_thread_proc *proc; void *arg; pj_uint32_t signature1; pj_uint32_t signature2; pj_mutex_t *suspended_mutex; #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 pj_uint32_t stk_size; pj_uint32_t stk_max_usage; char *stk_start; const char *caller_file; int caller_line; #endif }; struct pj_atomic_t { pj_mutex_t *mutex; pj_atomic_value_t value; }; struct pj_mutex_t { pthread_mutex_t mutex; char obj_name[PJ_MAX_OBJ_NAME]; #if PJ_DEBUG int nesting_level; pj_thread_t *owner; char owner_name[PJ_MAX_OBJ_NAME]; #endif }; #if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 struct pj_sem_t { #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 semaphore_t *sem; #else sem_t *sem; #endif char obj_name[PJ_MAX_OBJ_NAME]; }; #endif /* PJ_HAS_SEMAPHORE */ #if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 struct pj_event_t { enum event_state { EV_STATE_OFF, EV_STATE_SET, EV_STATE_PULSED } state; pj_mutex_t mutex; pthread_cond_t cond; pj_bool_t auto_reset; unsigned threads_waiting; unsigned threads_to_release; }; #endif /* PJ_HAS_EVENT_OBJ */ /* * Flag and reference counter for PJLIB instance. */ static int initialized; #if PJ_HAS_THREADS static pj_thread_t main_thread; static long thread_tls_id; static pj_mutex_t critical_section; #else # define MAX_THREADS 32 static int tls_flag[MAX_THREADS]; static void *tls[MAX_THREADS]; #endif static unsigned atexit_count; static void (*atexit_func[32])(void); static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type); /* * pj_init(void). * Init PJLIB! */ PJ_DEF(pj_status_t) pj_init(void) { char dummy_guid[PJ_GUID_MAX_LENGTH]; pj_str_t guid; pj_status_t rc; /* Check if PJLIB have been initialized */ if (initialized) { ++initialized; return PJ_SUCCESS; } #if PJ_HAS_THREADS /* Init this thread's TLS. */ if ((rc=pj_thread_init()) != 0) { return rc; } /* Critical section. */ if ((rc=init_mutex(&critical_section, "critsec", PJ_MUTEX_RECURSE)) != 0) return rc; #endif /* Init logging */ pj_log_init(); /* Initialize exception ID for the pool. * Must do so after critical section is configured. */ rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); if (rc != PJ_SUCCESS) return rc; /* Init random seed. */ /* Or probably not. Let application in charge of this */ /* pj_srand( clock() ); */ /* Startup GUID. */ guid.ptr = dummy_guid; pj_generate_unique_string( &guid ); /* Startup timestamp */ #if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 { pj_timestamp dummy_ts; if ((rc=pj_get_timestamp(&dummy_ts)) != 0) { return rc; } } #endif /* Flag PJLIB as initialized */ ++initialized; pj_assert(initialized == 1); PJ_LOG(4,(THIS_FILE, "pjlib %s for POSIX initialized", PJ_VERSION)); return PJ_SUCCESS; } /* * pj_atexit() */ PJ_DEF(pj_status_t) pj_atexit(void (*func)(void)) { if (atexit_count >= PJ_ARRAY_SIZE(atexit_func)) return PJ_ETOOMANY; atexit_func[atexit_count++] = func; return PJ_SUCCESS; } /* * pj_shutdown(void) */ PJ_DEF(void) pj_shutdown() { int i; /* Only perform shutdown operation when 'initialized' reaches zero */ pj_assert(initialized > 0); if (--initialized != 0) return; /* Call atexit() functions */ for (i=atexit_count-1; i>=0; --i) { (*atexit_func[i])(); } atexit_count = 0; /* Free exception ID */ if (PJ_NO_MEMORY_EXCEPTION != -1) { pj_exception_id_free(PJ_NO_MEMORY_EXCEPTION); PJ_NO_MEMORY_EXCEPTION = -1; } #if PJ_HAS_THREADS /* Destroy PJLIB critical section */ pj_mutex_destroy(&critical_section); /* Free PJLIB TLS */ if (thread_tls_id != -1) { pj_thread_local_free(thread_tls_id); thread_tls_id = -1; } /* Ticket #1132: Assertion when (re)starting PJLIB on different thread */ pj_bzero(&main_thread, sizeof(main_thread)); #endif /* Clear static variables */ pj_errno_clear_handlers(); } /* * pj_getpid(void) */ PJ_DEF(pj_uint32_t) pj_getpid(void) { PJ_CHECK_STACK(); return getpid(); } /* * Check if this thread has been registered to PJLIB. */ PJ_DEF(pj_bool_t) pj_thread_is_registered(void) { #if PJ_HAS_THREADS return pj_thread_local_get(thread_tls_id) != 0; #else pj_assert("pj_thread_is_registered() called in non-threading mode!"); return PJ_TRUE; #endif } /* * Get thread priority value for the thread. */ PJ_DEF(int) pj_thread_get_prio(pj_thread_t *thread) { #if PJ_HAS_THREADS struct sched_param param; int policy; int rc; rc = pthread_getschedparam (thread->thread, &policy, ¶m); if (rc != 0) return -1; return param.sched_priority; #else PJ_UNUSED_ARG(thread); return 1; #endif } /* * Set the thread priority. */ PJ_DEF(pj_status_t) pj_thread_set_prio(pj_thread_t *thread, int prio) { #if PJ_HAS_THREADS struct sched_param param; int policy; int rc; rc = pthread_getschedparam (thread->thread, &policy, ¶m); if (rc != 0) return PJ_RETURN_OS_ERROR(rc); param.sched_priority = prio; rc = pthread_setschedparam(thread->thread, policy, ¶m); if (rc != 0) return PJ_RETURN_OS_ERROR(rc); return PJ_SUCCESS; #else PJ_UNUSED_ARG(thread); PJ_UNUSED_ARG(prio); pj_assert("pj_thread_set_prio() called in non-threading mode!"); return 1; #endif } /* * Get the lowest priority value available on this system. */ PJ_DEF(int) pj_thread_get_prio_min(pj_thread_t *thread) { struct sched_param param; int policy; int rc; rc = pthread_getschedparam(thread->thread, &policy, ¶m); if (rc != 0) return -1; #if defined(_POSIX_PRIORITY_SCHEDULING) return sched_get_priority_min(policy); #elif defined __OpenBSD__ /* Thread prio min/max are declared in OpenBSD private hdr */ return 0; #else pj_assert("pj_thread_get_prio_min() not supported!"); return 0; #endif } /* * Get the highest priority value available on this system. */ PJ_DEF(int) pj_thread_get_prio_max(pj_thread_t *thread) { struct sched_param param; int policy; int rc; rc = pthread_getschedparam(thread->thread, &policy, ¶m); if (rc != 0) return -1; #if defined(_POSIX_PRIORITY_SCHEDULING) return sched_get_priority_max(policy); #elif defined __OpenBSD__ /* Thread prio min/max are declared in OpenBSD private hdr */ return 31; #else pj_assert("pj_thread_get_prio_max() not supported!"); return 0; #endif } /* * Get native thread handle */ PJ_DEF(void*) pj_thread_get_os_handle(pj_thread_t *thread) { PJ_ASSERT_RETURN(thread, NULL); #if PJ_HAS_THREADS return &thread->thread; #else pj_assert("pj_thread_is_registered() called in non-threading mode!"); return NULL; #endif } /* * pj_thread_register(..) */ PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, pj_thread_desc desc, pj_thread_t **ptr_thread) { #if PJ_HAS_THREADS char stack_ptr; pj_status_t rc; pj_thread_t *thread = (pj_thread_t *)desc; pj_str_t thread_name = pj_str((char*)cstr_thread_name); /* Size sanity check. */ if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { pj_assert(!"Not enough pj_thread_desc size!"); return PJ_EBUG; } /* Warn if this thread has been registered before */ if (pj_thread_local_get (thread_tls_id) != 0) { // 2006-02-26 bennylp: // This wouldn't work in all cases!. // If thread is created by external module (e.g. sound thread), // thread may be reused while the pool used for the thread descriptor // has been deleted by application. //*thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id); //return PJ_SUCCESS; PJ_LOG(4,(THIS_FILE, "Info: possibly re-registering existing " "thread")); } /* On the other hand, also warn if the thread descriptor buffer seem to * have been used to register other threads. */ pj_assert(thread->signature1 != SIGNATURE1 || thread->signature2 != SIGNATURE2 || (thread->thread == pthread_self())); /* Initialize and set the thread entry. */ pj_bzero(desc, sizeof(struct pj_thread_t)); thread->thread = pthread_self(); thread->signature1 = SIGNATURE1; thread->signature2 = SIGNATURE2; if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), cstr_thread_name, thread->thread); else pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), "thr%p", (void*)thread->thread); rc = pj_thread_local_set(thread_tls_id, thread); if (rc != PJ_SUCCESS) { pj_bzero(desc, sizeof(struct pj_thread_t)); return rc; } #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 thread->stk_start = &stack_ptr; thread->stk_size = 0xFFFFFFFFUL; thread->stk_max_usage = 0; #else PJ_UNUSED_ARG(stack_ptr); #endif *ptr_thread = thread; return PJ_SUCCESS; #else pj_thread_t *thread = (pj_thread_t*)desc; *ptr_thread = thread; return PJ_SUCCESS; #endif } /* * pj_thread_init(void) */ pj_status_t pj_thread_init(void) { #if PJ_HAS_THREADS pj_status_t rc; pj_thread_t *dummy; rc = pj_thread_local_alloc(&thread_tls_id ); if (rc != PJ_SUCCESS) { return rc; } return pj_thread_register("thr%p", (long*)&main_thread, &dummy); #else PJ_LOG(2,(THIS_FILE, "Thread init error. Threading is not enabled!")); return PJ_EINVALIDOP; #endif } #if PJ_HAS_THREADS /* * thread_main() * * This is the main entry for all threads. */ static void *thread_main(void *param) { pj_thread_t *rec = (pj_thread_t*)param; void *result; pj_status_t rc; #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 rec->stk_start = (char*)&rec; #endif /* Set current thread id. */ rc = pj_thread_local_set(thread_tls_id, rec); if (rc != PJ_SUCCESS) { pj_assert(!"Thread TLS ID is not set (pj_init() error?)"); } /* Check if suspension is required. */ if (rec->suspended_mutex) { pj_mutex_lock(rec->suspended_mutex); pj_mutex_unlock(rec->suspended_mutex); } PJ_LOG(6,(rec->obj_name, "Thread started")); /* Call user's entry! */ result = (void*)(long)(*rec->proc)(rec->arg); /* Done. */ PJ_LOG(6,(rec->obj_name, "Thread quitting")); return result; } #endif /* * pj_thread_create(...) */ PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name, pj_thread_proc *proc, void *arg, pj_size_t stack_size, unsigned flags, pj_thread_t **ptr_thread) { #if PJ_HAS_THREADS pj_thread_t *rec; pthread_attr_t thread_attr; void *stack_addr; int rc; PJ_UNUSED_ARG(stack_addr); PJ_CHECK_STACK(); PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL); /* Create thread record and assign name for the thread */ rec = (struct pj_thread_t*) pj_pool_zalloc(pool, sizeof(pj_thread_t)); PJ_ASSERT_RETURN(rec, PJ_ENOMEM); /* Set name. */ if (!thread_name) thread_name = "thr%p"; if (strchr(thread_name, '%')) { pj_ansi_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec); } else { strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME); rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; } /* Set default stack size */ if (stack_size == 0) stack_size = PJ_THREAD_DEFAULT_STACK_SIZE; #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 rec->stk_size = stack_size; rec->stk_max_usage = 0; #endif /* Emulate suspended thread with mutex. */ if (flags & PJ_THREAD_SUSPENDED) { rc = pj_mutex_create_simple(pool, NULL, &rec->suspended_mutex); if (rc != PJ_SUCCESS) { return rc; } pj_mutex_lock(rec->suspended_mutex); } else { pj_assert(rec->suspended_mutex == NULL); } /* Init thread attributes */ pthread_attr_init(&thread_attr); #if defined(PJ_THREAD_SET_STACK_SIZE) && PJ_THREAD_SET_STACK_SIZE!=0 /* Set thread's stack size */ rc = pthread_attr_setstacksize(&thread_attr, stack_size); if (rc != 0) return PJ_RETURN_OS_ERROR(rc); #endif /* PJ_THREAD_SET_STACK_SIZE */ #if defined(PJ_THREAD_ALLOCATE_STACK) && PJ_THREAD_ALLOCATE_STACK!=0 /* Allocate memory for the stack */ stack_addr = pj_pool_alloc(pool, stack_size); PJ_ASSERT_RETURN(stack_addr, PJ_ENOMEM); rc = pthread_attr_setstackaddr(&thread_attr, stack_addr); if (rc != 0) return PJ_RETURN_OS_ERROR(rc); #endif /* PJ_THREAD_ALLOCATE_STACK */ /* Create the thread. */ rec->proc = proc; rec->arg = arg; rc = pthread_create( &rec->thread, &thread_attr, &thread_main, rec); if (rc != 0) { return PJ_RETURN_OS_ERROR(rc); } *ptr_thread = rec; PJ_LOG(6, (rec->obj_name, "Thread created")); return PJ_SUCCESS; #else pj_assert(!"Threading is disabled!"); return PJ_EINVALIDOP; #endif } /* * pj_thread-get_name() */ PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p) { #if PJ_HAS_THREADS pj_thread_t *rec = (pj_thread_t*)p; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(p, ""); return rec->obj_name; #else return ""; #endif } /* * pj_thread_resume() */ PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) { pj_status_t rc; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(p, PJ_EINVAL); rc = pj_mutex_unlock(p->suspended_mutex); return rc; } /* * pj_thread_this() */ PJ_DEF(pj_thread_t*) pj_thread_this(void) { #if PJ_HAS_THREADS pj_thread_t *rec = (pj_thread_t*)pj_thread_local_get(thread_tls_id); if (rec == NULL) { pj_assert(!"Calling pjlib from unknown/external thread. You must " "register external threads with pj_thread_register() " "before calling any pjlib functions."); } /* * MUST NOT check stack because this function is called * by PJ_CHECK_STACK() itself!!! * */ return rec; #else pj_assert(!"Threading is not enabled!"); return NULL; #endif } /* * pj_thread_join() */ PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) { #if PJ_HAS_THREADS pj_thread_t *rec = (pj_thread_t *)p; void *ret; int result; PJ_CHECK_STACK(); if (p == pj_thread_this()) return PJ_ECANCELLED; PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name)); result = pthread_join( rec->thread, &ret); if (result == 0) return PJ_SUCCESS; else { /* Calling pthread_join() on a thread that no longer exists and * getting back ESRCH isn't an error (in this context). * Thanks Phil Torre . */ return result==ESRCH ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(result); } #else PJ_CHECK_STACK(); pj_assert(!"No multithreading support!"); return PJ_EINVALIDOP; #endif } /* * pj_thread_destroy() */ PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p) { PJ_CHECK_STACK(); /* Destroy mutex used to suspend thread */ if (p->suspended_mutex) { pj_mutex_destroy(p->suspended_mutex); p->suspended_mutex = NULL; } return PJ_SUCCESS; } /* * pj_thread_sleep() */ PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) { /* TODO: should change this to something like PJ_OS_HAS_NANOSLEEP */ #if defined(PJ_RTEMS) && PJ_RTEMS!=0 enum { NANOSEC_PER_MSEC = 1000000 }; struct timespec req; PJ_CHECK_STACK(); req.tv_sec = msec / 1000; req.tv_nsec = (msec % 1000) * NANOSEC_PER_MSEC; if (nanosleep(&req, NULL) == 0) return PJ_SUCCESS; return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); #else PJ_CHECK_STACK(); pj_set_os_error(0); usleep(msec * 1000); /* MacOS X (reported on 10.5) seems to always set errno to ETIMEDOUT. * It does so because usleep() is declared to return int, and we're * supposed to check for errno only when usleep() returns non-zero. * Unfortunately, usleep() is declared to return void in other platforms * so it's not possible to always check for the return value (unless * we add a detection routine in autoconf). * * As a workaround, here we check if ETIMEDOUT is returned and * return successfully if it is. */ if (pj_get_native_os_error() == ETIMEDOUT) return PJ_SUCCESS; return pj_get_os_error(); #endif /* PJ_RTEMS */ } #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 /* * pj_thread_check_stack() * Implementation for PJ_CHECK_STACK() */ PJ_DEF(void) pj_thread_check_stack(const char *file, int line) { char stk_ptr; pj_uint32_t usage; pj_thread_t *thread = pj_thread_this(); /* Calculate current usage. */ usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start : thread->stk_start - &stk_ptr; /* Assert if stack usage is dangerously high. */ pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128)); /* Keep statistic. */ if (usage > thread->stk_max_usage) { thread->stk_max_usage = usage; thread->caller_file = file; thread->caller_line = line; } } /* * pj_thread_get_stack_max_usage() */ PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread) { return thread->stk_max_usage; } /* * pj_thread_get_stack_info() */ PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread, const char **file, int *line ) { pj_assert(thread); *file = thread->caller_file; *line = thread->caller_line; return 0; } #endif /* PJ_OS_HAS_CHECK_STACK */ /////////////////////////////////////////////////////////////////////////////// /* * pj_atomic_create() */ PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, pj_atomic_value_t initial, pj_atomic_t **ptr_atomic) { pj_status_t rc; pj_atomic_t *atomic_var; atomic_var = PJ_POOL_ZALLOC_T(pool, pj_atomic_t); PJ_ASSERT_RETURN(atomic_var, PJ_ENOMEM); #if PJ_HAS_THREADS rc = pj_mutex_create(pool, "atm%p", PJ_MUTEX_SIMPLE, &atomic_var->mutex); if (rc != PJ_SUCCESS) return rc; #endif atomic_var->value = initial; *ptr_atomic = atomic_var; return PJ_SUCCESS; } /* * pj_atomic_destroy() */ PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var ) { PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL); #if PJ_HAS_THREADS return pj_mutex_destroy( atomic_var->mutex ); #else return 0; #endif } /* * pj_atomic_set() */ PJ_DEF(void) pj_atomic_set(pj_atomic_t *atomic_var, pj_atomic_value_t value) { PJ_CHECK_STACK(); #if PJ_HAS_THREADS pj_mutex_lock( atomic_var->mutex ); #endif atomic_var->value = value; #if PJ_HAS_THREADS pj_mutex_unlock( atomic_var->mutex); #endif } /* * pj_atomic_get() */ PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var) { pj_atomic_value_t oldval; PJ_CHECK_STACK(); #if PJ_HAS_THREADS pj_mutex_lock( atomic_var->mutex ); #endif oldval = atomic_var->value; #if PJ_HAS_THREADS pj_mutex_unlock( atomic_var->mutex); #endif return oldval; } /* * pj_atomic_inc_and_get() */ PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var) { pj_atomic_value_t new_value; PJ_CHECK_STACK(); #if PJ_HAS_THREADS pj_mutex_lock( atomic_var->mutex ); #endif new_value = ++atomic_var->value; #if PJ_HAS_THREADS pj_mutex_unlock( atomic_var->mutex); #endif return new_value; } /* * pj_atomic_inc() */ PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var) { pj_atomic_inc_and_get(atomic_var); } /* * pj_atomic_dec_and_get() */ PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var) { pj_atomic_value_t new_value; PJ_CHECK_STACK(); #if PJ_HAS_THREADS pj_mutex_lock( atomic_var->mutex ); #endif new_value = --atomic_var->value; #if PJ_HAS_THREADS pj_mutex_unlock( atomic_var->mutex); #endif return new_value; } /* * pj_atomic_dec() */ PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var) { pj_atomic_dec_and_get(atomic_var); } /* * pj_atomic_add_and_get() */ PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var, pj_atomic_value_t value ) { pj_atomic_value_t new_value; #if PJ_HAS_THREADS pj_mutex_lock(atomic_var->mutex); #endif atomic_var->value += value; new_value = atomic_var->value; #if PJ_HAS_THREADS pj_mutex_unlock(atomic_var->mutex); #endif return new_value; } /* * pj_atomic_add() */ PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var, pj_atomic_value_t value ) { pj_atomic_add_and_get(atomic_var, value); } /////////////////////////////////////////////////////////////////////////////// /* * pj_thread_local_alloc() */ PJ_DEF(pj_status_t) pj_thread_local_alloc(long *p_index) { #if PJ_HAS_THREADS pthread_key_t key; int rc; PJ_ASSERT_RETURN(p_index != NULL, PJ_EINVAL); pj_assert( sizeof(pthread_key_t) <= sizeof(long)); if ((rc=pthread_key_create(&key, NULL)) != 0) return PJ_RETURN_OS_ERROR(rc); *p_index = key; return PJ_SUCCESS; #else int i; for (i=0; i= 0 && index < MAX_THREADS); tls[index] = value; return PJ_SUCCESS; #endif } PJ_DEF(void*) pj_thread_local_get(long index) { //Can't check stack because this function is called //by PJ_CHECK_STACK() itself!!! //PJ_CHECK_STACK(); #if PJ_HAS_THREADS return pthread_getspecific(index); #else pj_assert(index >= 0 && index < MAX_THREADS); return tls[index]; #endif } /////////////////////////////////////////////////////////////////////////////// PJ_DEF(void) pj_enter_critical_section(void) { #if PJ_HAS_THREADS pj_mutex_lock(&critical_section); #endif } PJ_DEF(void) pj_leave_critical_section(void) { #if PJ_HAS_THREADS pj_mutex_unlock(&critical_section); #endif } /////////////////////////////////////////////////////////////////////////////// #if defined(PJ_LINUX) && PJ_LINUX!=0 PJ_BEGIN_DECL PJ_DECL(int) pthread_mutexattr_settype(pthread_mutexattr_t*,int); PJ_END_DECL #endif static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type) { #if PJ_HAS_THREADS pthread_mutexattr_t attr; int rc; PJ_CHECK_STACK(); rc = pthread_mutexattr_init(&attr); if (rc != 0) return PJ_RETURN_OS_ERROR(rc); if (type == PJ_MUTEX_SIMPLE) { #if (defined(PJ_LINUX) && PJ_LINUX!=0) || \ defined(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE) rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_FAST_NP); #elif (defined(PJ_RTEMS) && PJ_RTEMS!=0) || \ defined(PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE) /* Nothing to do, default is simple */ #else rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); #endif } else { #if (defined(PJ_LINUX) && PJ_LINUX!=0) || \ defined(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE) rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); #elif (defined(PJ_RTEMS) && PJ_RTEMS!=0) || \ defined(PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE) // Phil Torre : // The RTEMS implementation of POSIX mutexes doesn't include // pthread_mutexattr_settype(), so what follows is a hack // until I get RTEMS patched to support the set/get functions. // // More info: // newlib's pthread also lacks pthread_mutexattr_settype(), // but it seems to have mutexattr.recursive. PJ_TODO(FIX_RTEMS_RECURSIVE_MUTEX_TYPE) attr.recursive = 1; #else rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); #endif } if (rc != 0) { return PJ_RETURN_OS_ERROR(rc); } rc = pthread_mutex_init(&mutex->mutex, &attr); if (rc != 0) { return PJ_RETURN_OS_ERROR(rc); } rc = pthread_mutexattr_destroy(&attr); if (rc != 0) { pj_status_t status = PJ_RETURN_OS_ERROR(rc); pthread_mutex_destroy(&mutex->mutex); return status; } #if PJ_DEBUG /* Set owner. */ mutex->nesting_level = 0; mutex->owner = NULL; mutex->owner_name[0] = '\0'; #endif /* Set name. */ if (!name) { name = "mtx%p"; } if (strchr(name, '%')) { pj_ansi_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex); } else { strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME); mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; } PJ_LOG(6, (mutex->obj_name, "Mutex created")); return PJ_SUCCESS; #else /* PJ_HAS_THREADS */ return PJ_SUCCESS; #endif } /* * pj_mutex_create() */ PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool, const char *name, int type, pj_mutex_t **ptr_mutex) { #if PJ_HAS_THREADS pj_status_t rc; pj_mutex_t *mutex; PJ_ASSERT_RETURN(pool && ptr_mutex, PJ_EINVAL); mutex = PJ_POOL_ALLOC_T(pool, pj_mutex_t); PJ_ASSERT_RETURN(mutex, PJ_ENOMEM); if ((rc=init_mutex(mutex, name, type)) != PJ_SUCCESS) return rc; *ptr_mutex = mutex; return PJ_SUCCESS; #else /* PJ_HAS_THREADS */ *ptr_mutex = (pj_mutex_t*)1; return PJ_SUCCESS; #endif } /* * pj_mutex_create_simple() */ PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name, pj_mutex_t **mutex ) { return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); } /* * pj_mutex_create_recursive() */ PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, const char *name, pj_mutex_t **mutex ) { return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex); } /* * pj_mutex_lock() */ PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) { #if PJ_HAS_THREADS pj_status_t status; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(mutex, PJ_EINVAL); #if PJ_DEBUG PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting (mutex owner=%s)", pj_thread_this()->obj_name, mutex->owner_name)); #else PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting", pj_thread_this()->obj_name)); #endif status = pthread_mutex_lock( &mutex->mutex ); #if PJ_DEBUG if (status == PJ_SUCCESS) { mutex->owner = pj_thread_this(); pj_ansi_strcpy(mutex->owner_name, mutex->owner->obj_name); ++mutex->nesting_level; } PJ_LOG(6,(mutex->obj_name, (status==0 ? "Mutex acquired by thread %s (level=%d)" : "Mutex acquisition FAILED by %s (level=%d)"), pj_thread_this()->obj_name, mutex->nesting_level)); #else PJ_LOG(6,(mutex->obj_name, (status==0 ? "Mutex acquired by thread %s" : "FAILED by %s"), pj_thread_this()->obj_name)); #endif if (status == 0) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(status); #else /* PJ_HAS_THREADS */ pj_assert( mutex == (pj_mutex_t*)1 ); return PJ_SUCCESS; #endif } /* * pj_mutex_unlock() */ PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) { #if PJ_HAS_THREADS pj_status_t status; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(mutex, PJ_EINVAL); #if PJ_DEBUG pj_assert(mutex->owner == pj_thread_this()); if (--mutex->nesting_level == 0) { mutex->owner = NULL; mutex->owner_name[0] = '\0'; } PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s (level=%d)", pj_thread_this()->obj_name, mutex->nesting_level)); #else PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s", pj_thread_this()->obj_name)); #endif status = pthread_mutex_unlock( &mutex->mutex ); if (status == 0) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(status); #else /* PJ_HAS_THREADS */ pj_assert( mutex == (pj_mutex_t*)1 ); return PJ_SUCCESS; #endif } /* * pj_mutex_trylock() */ PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) { #if PJ_HAS_THREADS int status; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(mutex, PJ_EINVAL); PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is trying", pj_thread_this()->obj_name)); status = pthread_mutex_trylock( &mutex->mutex ); if (status==0) { #if PJ_DEBUG mutex->owner = pj_thread_this(); pj_ansi_strcpy(mutex->owner_name, mutex->owner->obj_name); ++mutex->nesting_level; PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s (level=%d)", pj_thread_this()->obj_name, mutex->nesting_level)); #else PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s", pj_thread_this()->obj_name)); #endif } else { PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s's trylock() failed", pj_thread_this()->obj_name)); } if (status==0) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(status); #else /* PJ_HAS_THREADS */ pj_assert( mutex == (pj_mutex_t*)1); return PJ_SUCCESS; #endif } /* * pj_mutex_destroy() */ PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) { enum { RETRY = 4 }; int status = 0; unsigned retry; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(mutex, PJ_EINVAL); #if PJ_HAS_THREADS PJ_LOG(6,(mutex->obj_name, "Mutex destroyed by thread %s", pj_thread_this()->obj_name)); for (retry=0; retrymutex ); if (status == PJ_SUCCESS) break; else if (retrymutex); } if (status == 0) return PJ_SUCCESS; else { return PJ_RETURN_OS_ERROR(status); } #else pj_assert( mutex == (pj_mutex_t*)1 ); status = PJ_SUCCESS; return status; #endif } #if PJ_DEBUG PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) { #if PJ_HAS_THREADS return mutex->owner == pj_thread_this(); #else return 1; #endif } #endif /////////////////////////////////////////////////////////////////////////////// /* * Include Read/Write mutex emulation for POSIX platforms that lack it (e.g. * RTEMS). Otherwise use POSIX rwlock. */ #if defined(PJ_EMULATE_RWMUTEX) && PJ_EMULATE_RWMUTEX!=0 /* We need semaphore functionality to emulate rwmutex */ # if !defined(PJ_HAS_SEMAPHORE) || PJ_HAS_SEMAPHORE==0 # error "Semaphore support needs to be enabled to emulate rwmutex" # endif # include "os_rwmutex.c" #else struct pj_rwmutex_t { pthread_rwlock_t rwlock; }; PJ_DEF(pj_status_t) pj_rwmutex_create(pj_pool_t *pool, const char *name, pj_rwmutex_t **p_mutex) { pj_rwmutex_t *rwm; pj_status_t status; PJ_UNUSED_ARG(name); rwm = PJ_POOL_ALLOC_T(pool, pj_rwmutex_t); PJ_ASSERT_RETURN(rwm, PJ_ENOMEM); status = pthread_rwlock_init(&rwm->rwlock, NULL); if (status != 0) return PJ_RETURN_OS_ERROR(status); *p_mutex = rwm; return PJ_SUCCESS; } /* * Lock the mutex for reading. * */ PJ_DEF(pj_status_t) pj_rwmutex_lock_read(pj_rwmutex_t *mutex) { pj_status_t status; status = pthread_rwlock_rdlock(&mutex->rwlock); if (status != 0) return PJ_RETURN_OS_ERROR(status); return PJ_SUCCESS; } /* * Lock the mutex for writing. * */ PJ_DEF(pj_status_t) pj_rwmutex_lock_write(pj_rwmutex_t *mutex) { pj_status_t status; status = pthread_rwlock_wrlock(&mutex->rwlock); if (status != 0) return PJ_RETURN_OS_ERROR(status); return PJ_SUCCESS; } /* * Release read lock. * */ PJ_DEF(pj_status_t) pj_rwmutex_unlock_read(pj_rwmutex_t *mutex) { return pj_rwmutex_unlock_write(mutex); } /* * Release write lock. * */ PJ_DEF(pj_status_t) pj_rwmutex_unlock_write(pj_rwmutex_t *mutex) { pj_status_t status; status = pthread_rwlock_unlock(&mutex->rwlock); if (status != 0) return PJ_RETURN_OS_ERROR(status); return PJ_SUCCESS; } /* * Destroy reader/writer mutex. * */ PJ_DEF(pj_status_t) pj_rwmutex_destroy(pj_rwmutex_t *mutex) { pj_status_t status; status = pthread_rwlock_destroy(&mutex->rwlock); if (status != 0) return PJ_RETURN_OS_ERROR(status); return PJ_SUCCESS; } #endif /* PJ_EMULATE_RWMUTEX */ /////////////////////////////////////////////////////////////////////////////// #if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 /* * pj_sem_create() */ PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, const char *name, unsigned initial, unsigned max, pj_sem_t **ptr_sem) { #if PJ_HAS_THREADS pj_sem_t *sem; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(pool != NULL && ptr_sem != NULL, PJ_EINVAL); sem = PJ_POOL_ALLOC_T(pool, pj_sem_t); PJ_ASSERT_RETURN(sem, PJ_ENOMEM); #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 { kern_return_t err; sem->sem = PJ_POOL_ALLOC_T(pool, semaphore_t); err = semaphore_create(mach_task_self(), sem->sem, SYNC_POLICY_FIFO, initial); if (err != KERN_SUCCESS) { if (err == KERN_RESOURCE_SHORTAGE) return PJ_RETURN_OS_ERROR(ENOMEM); else return PJ_RETURN_OS_ERROR(EINVAL); } } #else sem->sem = PJ_POOL_ALLOC_T(pool, sem_t); if (sem_init( sem->sem, 0, initial) != 0) return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); #endif /* Set name. */ if (!name) { name = "sem%p"; } if (strchr(name, '%')) { pj_ansi_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem); } else { strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME); sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; } PJ_LOG(6, (sem->obj_name, "Semaphore created")); *ptr_sem = sem; return PJ_SUCCESS; #else *ptr_sem = (pj_sem_t*)1; return PJ_SUCCESS; #endif } /* * pj_sem_wait() */ PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) { #if PJ_HAS_THREADS int result; int error; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sem, PJ_EINVAL); PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting", pj_thread_this()->obj_name)); #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 { do result = semaphore_wait(*(sem->sem)); while (result == KERN_ABORTED); if (result == KERN_SUCCESS) { result = error = 0; } else { result = -1; error = EINVAL; } } #else result = sem_wait( sem->sem ); if (result == 0) { PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", pj_thread_this()->obj_name)); } else { PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire", pj_thread_this()->obj_name)); error = pj_get_native_os_error(); } #endif if (result == 0) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(error); #else pj_assert( sem == (pj_sem_t*) 1 ); return PJ_SUCCESS; #endif } /* * pj_sem_trywait() */ PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) { #if PJ_HAS_THREADS int result; int error; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sem, PJ_EINVAL); #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 { mach_timespec_t interval; kern_return_t err; interval.tv_sec = 0; interval.tv_nsec = 0; err = semaphore_timedwait(*(sem->sem), interval); if (err == KERN_SUCCESS) { result = error = 0; } else if (err == KERN_OPERATION_TIMED_OUT) { result = -1; error = EAGAIN; } else { result = -1; error = EINVAL; } } #else result = sem_trywait( sem->sem ); if (result == 0) { PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", pj_thread_this()->obj_name)); } else { error = pj_get_native_os_error(); } #endif if (result == 0) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(error); #else pj_assert( sem == (pj_sem_t*)1 ); return PJ_SUCCESS; #endif } /* * pj_sem_post() */ PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) { #if PJ_HAS_THREADS int result; int error; PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s", pj_thread_this()->obj_name)); #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 { kern_return_t err; err = semaphore_signal(*(sem->sem)); if (err == KERN_SUCCESS) { result = error = 0; } else { result = -1; error = EINVAL; } } #else result = sem_post( sem->sem ); if (result != 0) error = pj_get_native_os_error(); #endif if (result == 0) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(error); #else pj_assert( sem == (pj_sem_t*) 1); return PJ_SUCCESS; #endif } /* * pj_sem_destroy() */ PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) { #if PJ_HAS_THREADS int result; int error; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sem, PJ_EINVAL); PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s", pj_thread_this()->obj_name)); #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 { kern_return_t err; err = semaphore_destroy(mach_task_self(), *(sem->sem)); if (err == KERN_SUCCESS) { result = error = -1; } else { result = -1; error = EINVAL; } } #else result = sem_destroy( sem->sem ); if (result != 0) error = pj_get_native_os_error(); #endif if (result == 0) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(error); #else pj_assert( sem == (pj_sem_t*) 1 ); return PJ_SUCCESS; #endif } #endif /* PJ_HAS_SEMAPHORE */ /////////////////////////////////////////////////////////////////////////////// #if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 /* * pj_event_create() */ PJ_DEF(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name, pj_bool_t manual_reset, pj_bool_t initial, pj_event_t **ptr_event) { pj_event_t *event; event = PJ_POOL_ALLOC_T(pool, pj_event_t); init_mutex(&event->mutex, name, PJ_MUTEX_SIMPLE); pthread_cond_init(&event->cond, 0); event->auto_reset = !manual_reset; event->threads_waiting = 0; if (initial) { event->state = EV_STATE_SET; event->threads_to_release = 1; } else { event->state = EV_STATE_OFF; event->threads_to_release = 0; } *ptr_event = event; return PJ_SUCCESS; } static void event_on_one_release(pj_event_t *event) { if (event->state == EV_STATE_SET) { if (event->auto_reset) { event->threads_to_release = 0; event->state = EV_STATE_OFF; } else { /* Manual reset remains on */ } } else { if (event->auto_reset) { /* Only release one */ event->threads_to_release = 0; event->state = EV_STATE_OFF; } else { event->threads_to_release--; pj_assert(event->threads_to_release >= 0); if (event->threads_to_release==0) event->state = EV_STATE_OFF; } } } /* * pj_event_wait() */ PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event) { pthread_mutex_lock(&event->mutex.mutex); event->threads_waiting++; while (event->state == EV_STATE_OFF) pthread_cond_wait(&event->cond, &event->mutex.mutex); event->threads_waiting--; event_on_one_release(event); pthread_mutex_unlock(&event->mutex.mutex); return PJ_SUCCESS; } /* * pj_event_trywait() */ PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event) { pj_status_t status; pthread_mutex_lock(&event->mutex.mutex); status = event->state != EV_STATE_OFF ? PJ_SUCCESS : -1; if (status==PJ_SUCCESS) { event_on_one_release(event); } pthread_mutex_unlock(&event->mutex.mutex); return status; } /* * pj_event_set() */ PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event) { pthread_mutex_lock(&event->mutex.mutex); event->threads_to_release = 1; event->state = EV_STATE_SET; if (event->auto_reset) pthread_cond_signal(&event->cond); else pthread_cond_broadcast(&event->cond); pthread_mutex_unlock(&event->mutex.mutex); return PJ_SUCCESS; } /* * pj_event_pulse() */ PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event) { pthread_mutex_lock(&event->mutex.mutex); if (event->threads_waiting) { event->threads_to_release = event->auto_reset ? 1 : event->threads_waiting; event->state = EV_STATE_PULSED; if (event->threads_to_release==1) pthread_cond_signal(&event->cond); else pthread_cond_broadcast(&event->cond); } pthread_mutex_unlock(&event->mutex.mutex); return PJ_SUCCESS; } /* * pj_event_reset() */ PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event) { pthread_mutex_lock(&event->mutex.mutex); event->state = EV_STATE_OFF; event->threads_to_release = 0; pthread_mutex_unlock(&event->mutex.mutex); return PJ_SUCCESS; } /* * pj_event_destroy() */ PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event) { pj_mutex_destroy(&event->mutex); pthread_cond_destroy(&event->cond); return PJ_SUCCESS; } #endif /* PJ_HAS_EVENT_OBJ */ /////////////////////////////////////////////////////////////////////////////// #if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 /* * Terminal */ /** * Set terminal color. */ PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color) { /* put bright prefix to ansi_color */ char ansi_color[12] = "\033[01;3"; if (color & PJ_TERM_COLOR_BRIGHT) { color ^= PJ_TERM_COLOR_BRIGHT; } else { strcpy(ansi_color, "\033[00;3"); } switch (color) { case 0: /* black color */ strcat(ansi_color, "0m"); break; case PJ_TERM_COLOR_R: /* red color */ strcat(ansi_color, "1m"); break; case PJ_TERM_COLOR_G: /* green color */ strcat(ansi_color, "2m"); break; case PJ_TERM_COLOR_B: /* blue color */ strcat(ansi_color, "4m"); break; case PJ_TERM_COLOR_R | PJ_TERM_COLOR_G: /* yellow color */ strcat(ansi_color, "3m"); break; case PJ_TERM_COLOR_R | PJ_TERM_COLOR_B: /* magenta color */ strcat(ansi_color, "5m"); break; case PJ_TERM_COLOR_G | PJ_TERM_COLOR_B: /* cyan color */ strcat(ansi_color, "6m"); break; case PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B: /* white color */ strcat(ansi_color, "7m"); break; default: /* default console color */ strcpy(ansi_color, "\033[00m"); break; } fputs(ansi_color, stdout); return PJ_SUCCESS; } /** * Get current terminal foreground color. */ PJ_DEF(pj_color_t) pj_term_get_color(void) { return 0; } #endif /* PJ_TERM_HAS_COLOR */ #if !defined(PJ_DARWINOS) || PJ_DARWINOS == 0 /* * pj_run_app() */ PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], unsigned flags) { return (*main_func)(argc, argv); } #endif ================================================ FILE: deps/pjsip/pjlib/src/pj/os_core_win32.c ================================================ /* $Id: os_core_win32.c 3999 2012-03-30 07:10:13Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #if defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 # include #endif #if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 # include #endif /* Activate mutex related logging if PJ_DEBUG_MUTEX is set, otherwise * use default level 6 logging. */ #if defined(PJ_DEBUG_MUTEX) && PJ_DEBUG_MUTEX # undef PJ_DEBUG # define PJ_DEBUG 1 # define LOG_MUTEX(expr) PJ_LOG(5,expr) #else # define LOG_MUTEX(expr) PJ_LOG(6,expr) #endif #define THIS_FILE "os_core_win32.c" /* * Implementation of pj_thread_t. */ struct pj_thread_t { char obj_name[PJ_MAX_OBJ_NAME]; HANDLE hthread; DWORD idthread; pj_thread_proc *proc; void *arg; #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 pj_uint32_t stk_size; pj_uint32_t stk_max_usage; char *stk_start; const char *caller_file; int caller_line; #endif }; /* * Implementation of pj_mutex_t. */ struct pj_mutex_t { #if PJ_WIN32_WINNT >= 0x0400 CRITICAL_SECTION crit; #else HANDLE hMutex; #endif char obj_name[PJ_MAX_OBJ_NAME]; #if PJ_DEBUG int nesting_level; pj_thread_t *owner; #endif }; /* * Implementation of pj_sem_t. */ typedef struct pj_sem_t { HANDLE hSemaphore; char obj_name[PJ_MAX_OBJ_NAME]; } pj_mem_t; /* * Implementation of pj_event_t. */ struct pj_event_t { HANDLE hEvent; char obj_name[PJ_MAX_OBJ_NAME]; }; /* * Implementation of pj_atomic_t. */ struct pj_atomic_t { long value; }; /* * Flag and reference counter for PJLIB instance. */ static int initialized; /* * Static global variables. */ static pj_thread_desc main_thread; static long thread_tls_id = -1; static pj_mutex_t critical_section_mutex; static unsigned atexit_count; static void (*atexit_func[32])(void); /* * Some static prototypes. */ static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name); /* * pj_init(void). * Init PJLIB! */ PJ_DEF(pj_status_t) pj_init(void) { WSADATA wsa; char dummy_guid[32]; /* use maximum GUID length */ pj_str_t guid; pj_status_t rc; /* Check if PJLIB have been initialized */ if (initialized) { ++initialized; return PJ_SUCCESS; } /* Init Winsock.. */ if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) { return PJ_RETURN_OS_ERROR(WSAGetLastError()); } /* Init this thread's TLS. */ if ((rc=pj_thread_init()) != PJ_SUCCESS) { return rc; } /* Init logging */ pj_log_init(); /* Init random seed. */ /* Or probably not. Let application in charge of this */ /* pj_srand( GetCurrentProcessId() ); */ /* Initialize critical section. */ if ((rc=init_mutex(&critical_section_mutex, "pj%p")) != PJ_SUCCESS) return rc; /* Startup GUID. */ guid.ptr = dummy_guid; pj_generate_unique_string( &guid ); /* Initialize exception ID for the pool. * Must do so after critical section is configured. */ rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); if (rc != PJ_SUCCESS) return rc; /* Startup timestamp */ #if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 { pj_timestamp dummy_ts; if ((rc=pj_get_timestamp_freq(&dummy_ts)) != PJ_SUCCESS) { return rc; } if ((rc=pj_get_timestamp(&dummy_ts)) != PJ_SUCCESS) { return rc; } } #endif /* Flag PJLIB as initialized */ ++initialized; pj_assert(initialized == 1); PJ_LOG(4,(THIS_FILE, "pjlib %s for win32 initialized", PJ_VERSION)); return PJ_SUCCESS; } /* * pj_atexit() */ PJ_DEF(pj_status_t) pj_atexit(void (*func)(void)) { if (atexit_count >= PJ_ARRAY_SIZE(atexit_func)) return PJ_ETOOMANY; atexit_func[atexit_count++] = func; return PJ_SUCCESS; } /* * pj_shutdown(void) */ PJ_DEF(void) pj_shutdown() { int i; /* Only perform shutdown operation when 'initialized' reaches zero */ pj_assert(initialized > 0); if (--initialized != 0) return; /* Display stack usage */ #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 { pj_thread_t *rec = (pj_thread_t*)main_thread; PJ_LOG(5,(rec->obj_name, "Main thread stack max usage=%u by %s:%d", rec->stk_max_usage, rec->caller_file, rec->caller_line)); } #endif /* Call atexit() functions */ for (i=atexit_count-1; i>=0; --i) { (*atexit_func[i])(); } atexit_count = 0; /* Free exception ID */ if (PJ_NO_MEMORY_EXCEPTION != -1) { pj_exception_id_free(PJ_NO_MEMORY_EXCEPTION); PJ_NO_MEMORY_EXCEPTION = -1; } /* Destroy PJLIB critical section */ pj_mutex_destroy(&critical_section_mutex); /* Free PJLIB TLS */ if (thread_tls_id != -1) { pj_thread_local_free(thread_tls_id); thread_tls_id = -1; } /* Clear static variables */ pj_errno_clear_handlers(); /* Ticket #1132: Assertion when (re)starting PJLIB on different thread */ pj_bzero(main_thread, sizeof(main_thread)); /* Shutdown Winsock */ WSACleanup(); } /* * pj_getpid(void) */ PJ_DEF(pj_uint32_t) pj_getpid(void) { PJ_CHECK_STACK(); return GetCurrentProcessId(); } /* * Check if this thread has been registered to PJLIB. */ PJ_DEF(pj_bool_t) pj_thread_is_registered(void) { return pj_thread_local_get(thread_tls_id) != 0; } /* * Get thread priority value for the thread. */ PJ_DEF(int) pj_thread_get_prio(pj_thread_t *thread) { return GetThreadPriority(thread->hthread); } /* * Set the thread priority. */ PJ_DEF(pj_status_t) pj_thread_set_prio(pj_thread_t *thread, int prio) { #if PJ_HAS_THREADS PJ_ASSERT_RETURN(thread, PJ_EINVAL); PJ_ASSERT_RETURN(prio>=THREAD_PRIORITY_IDLE && prio<=THREAD_PRIORITY_TIME_CRITICAL, PJ_EINVAL); if (SetThreadPriority(thread->hthread, prio) == FALSE) return PJ_RETURN_OS_ERROR(GetLastError()); return PJ_SUCCESS; #else PJ_UNUSED_ARG(thread); PJ_UNUSED_ARG(prio); pj_assert("pj_thread_set_prio() called in non-threading mode!"); return PJ_EINVALIDOP; #endif } /* * Get the lowest priority value available on this system. */ PJ_DEF(int) pj_thread_get_prio_min(pj_thread_t *thread) { PJ_UNUSED_ARG(thread); return THREAD_PRIORITY_IDLE; } /* * Get the highest priority value available on this system. */ PJ_DEF(int) pj_thread_get_prio_max(pj_thread_t *thread) { PJ_UNUSED_ARG(thread); return THREAD_PRIORITY_TIME_CRITICAL; } /* * Get native thread handle */ PJ_DEF(void*) pj_thread_get_os_handle(pj_thread_t *thread) { PJ_ASSERT_RETURN(thread, NULL); #if PJ_HAS_THREADS return thread->hthread; #else pj_assert("pj_thread_is_registered() called in non-threading mode!"); return NULL; #endif } /* * pj_thread_register(..) */ PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, pj_thread_desc desc, pj_thread_t **thread_ptr) { char stack_ptr; pj_status_t rc; pj_thread_t *thread = (pj_thread_t *)desc; pj_str_t thread_name = pj_str((char*)cstr_thread_name); /* Size sanity check. */ if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { pj_assert(!"Not enough pj_thread_desc size!"); return PJ_EBUG; } /* If a thread descriptor has been registered before, just return it. */ if (pj_thread_local_get (thread_tls_id) != 0) { // 2006-02-26 bennylp: // This wouldn't work in all cases!. // If thread is created by external module (e.g. sound thread), // thread may be reused while the pool used for the thread descriptor // has been deleted by application. //*thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id); //return PJ_SUCCESS; } /* Initialize and set the thread entry. */ pj_bzero(desc, sizeof(struct pj_thread_t)); thread->hthread = GetCurrentThread(); thread->idthread = GetCurrentThreadId(); #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 thread->stk_start = &stack_ptr; thread->stk_size = 0xFFFFFFFFUL; thread->stk_max_usage = 0; #else stack_ptr = '\0'; #endif if (cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), cstr_thread_name, thread->idthread); else pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), "thr%p", (void*)(pj_ssize_t)thread->idthread); rc = pj_thread_local_set(thread_tls_id, thread); if (rc != PJ_SUCCESS) return rc; *thread_ptr = thread; return PJ_SUCCESS; } /* * pj_thread_init(void) */ pj_status_t pj_thread_init(void) { pj_status_t rc; pj_thread_t *thread; rc = pj_thread_local_alloc(&thread_tls_id); if (rc != PJ_SUCCESS) return rc; return pj_thread_register("thr%p", main_thread, &thread); } static DWORD WINAPI thread_main(void *param) { pj_thread_t *rec = param; DWORD result; #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 rec->stk_start = (char*)&rec; #endif if (pj_thread_local_set(thread_tls_id, rec) != PJ_SUCCESS) { pj_assert(!"TLS is not set (pj_init() error?)"); } PJ_LOG(6,(rec->obj_name, "Thread started")); result = (*rec->proc)(rec->arg); PJ_LOG(6,(rec->obj_name, "Thread quitting")); #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 PJ_LOG(5,(rec->obj_name, "Thread stack max usage=%u by %s:%d", rec->stk_max_usage, rec->caller_file, rec->caller_line)); #endif return (DWORD)result; } /* * pj_thread_create(...) */ PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name, pj_thread_proc *proc, void *arg, pj_size_t stack_size, unsigned flags, pj_thread_t **thread_ptr) { DWORD dwflags = 0; pj_thread_t *rec; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(pool && proc && thread_ptr, PJ_EINVAL); /* Set flags */ if (flags & PJ_THREAD_SUSPENDED) dwflags |= CREATE_SUSPENDED; /* Create thread record and assign name for the thread */ rec = (struct pj_thread_t*) pj_pool_calloc(pool, 1, sizeof(pj_thread_t)); if (!rec) return PJ_ENOMEM; /* Set name. */ if (!thread_name) thread_name = "thr%p"; if (strchr(thread_name, '%')) { pj_ansi_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec); } else { pj_ansi_strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME); rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; } PJ_LOG(6, (rec->obj_name, "Thread created")); #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 rec->stk_size = stack_size ? (pj_uint32_t)stack_size : 0xFFFFFFFFUL; rec->stk_max_usage = 0; #endif /* Create the thread. */ rec->proc = proc; rec->arg = arg; rec->hthread = CreateThread(NULL, stack_size, thread_main, rec, dwflags, &rec->idthread); if (rec->hthread == NULL) return PJ_RETURN_OS_ERROR(GetLastError()); /* Success! */ *thread_ptr = rec; return PJ_SUCCESS; } /* * pj_thread-get_name() */ PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p) { pj_thread_t *rec = (pj_thread_t*)p; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(p, ""); return rec->obj_name; } /* * pj_thread_resume() */ PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) { pj_thread_t *rec = (pj_thread_t*)p; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(p, PJ_EINVAL); if (ResumeThread(rec->hthread) == (DWORD)-1) return PJ_RETURN_OS_ERROR(GetLastError()); else return PJ_SUCCESS; } /* * pj_thread_this() */ PJ_DEF(pj_thread_t*) pj_thread_this(void) { pj_thread_t *rec = pj_thread_local_get(thread_tls_id); if (rec == NULL) { pj_assert(!"Calling pjlib from unknown/external thread. You must " "register external threads with pj_thread_register() " "before calling any pjlib functions."); } /* * MUST NOT check stack because this function is called * by PJ_CHECK_STACK() itself!!! * */ return rec; } /* * pj_thread_join() */ PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) { pj_thread_t *rec = (pj_thread_t *)p; DWORD rc; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(p, PJ_EINVAL); if (p == pj_thread_this()) return PJ_ECANCELLED; PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name)); rc = WaitForSingleObject(rec->hthread, INFINITE); if (rc==WAIT_OBJECT_0) return PJ_SUCCESS; else if (rc==WAIT_TIMEOUT) return PJ_ETIMEDOUT; else return PJ_RETURN_OS_ERROR(GetLastError()); } /* * pj_thread_destroy() */ PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p) { pj_thread_t *rec = (pj_thread_t *)p; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(p, PJ_EINVAL); if (CloseHandle(rec->hthread) == TRUE) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(GetLastError()); } /* * pj_thread_sleep() */ PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) { PJ_CHECK_STACK(); Sleep(msec); return PJ_SUCCESS; } #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0 /* * pj_thread_check_stack() * Implementation for PJ_CHECK_STACK() */ PJ_DEF(void) pj_thread_check_stack(const char *file, int line) { char stk_ptr; pj_uint32_t usage; pj_thread_t *thread = pj_thread_this(); pj_assert(thread); /* Calculate current usage. */ usage = (&stk_ptr > thread->stk_start) ? (pj_uint32_t)(&stk_ptr - thread->stk_start) : (pj_uint32_t)(thread->stk_start - &stk_ptr); /* Assert if stack usage is dangerously high. */ pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128)); /* Keep statistic. */ if (usage > thread->stk_max_usage) { thread->stk_max_usage = usage; thread->caller_file = file; thread->caller_line = line; } } /* * pj_thread_get_stack_max_usage() */ PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread) { return thread->stk_max_usage; } /* * pj_thread_get_stack_info() */ PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread, const char **file, int *line ) { pj_assert(thread); *file = thread->caller_file; *line = thread->caller_line; return 0; } #endif /* PJ_OS_HAS_CHECK_STACK */ /////////////////////////////////////////////////////////////////////////////// /* * pj_atomic_create() */ PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, pj_atomic_value_t initial, pj_atomic_t **atomic_ptr) { pj_atomic_t *atomic_var = pj_pool_alloc(pool, sizeof(pj_atomic_t)); if (!atomic_var) return PJ_ENOMEM; atomic_var->value = initial; *atomic_ptr = atomic_var; return PJ_SUCCESS; } /* * pj_atomic_destroy() */ PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var ) { PJ_UNUSED_ARG(var); PJ_ASSERT_RETURN(var, PJ_EINVAL); return 0; } /* * pj_atomic_set() */ PJ_DEF(void) pj_atomic_set( pj_atomic_t *atomic_var, pj_atomic_value_t value) { PJ_CHECK_STACK(); InterlockedExchange(&atomic_var->value, value); } /* * pj_atomic_get() */ PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(atomic_var, 0); return atomic_var->value; } /* * pj_atomic_inc_and_get() */ PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var) { PJ_CHECK_STACK(); #if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 return InterlockedIncrement(&atomic_var->value); #else return InterlockedIncrement(&atomic_var->value); #endif } /* * pj_atomic_inc() */ PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var) { pj_atomic_inc_and_get(atomic_var); } /* * pj_atomic_dec_and_get() */ PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var) { PJ_CHECK_STACK(); #if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 return InterlockedDecrement(&atomic_var->value); #else return InterlockedDecrement(&atomic_var->value); #endif } /* * pj_atomic_dec() */ PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var) { pj_atomic_dec_and_get(atomic_var); } /* * pj_atomic_add() */ PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var, pj_atomic_value_t value ) { #if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 InterlockedExchangeAdd( &atomic_var->value, value ); #else InterlockedExchangeAdd( &atomic_var->value, value ); #endif } /* * pj_atomic_add_and_get() */ PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var, pj_atomic_value_t value) { #if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 long oldValue = InterlockedExchangeAdd( &atomic_var->value, value); return oldValue + value; #else long oldValue = InterlockedExchangeAdd( &atomic_var->value, value); return oldValue + value; #endif } /////////////////////////////////////////////////////////////////////////////// /* * pj_thread_local_alloc() */ PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index) { PJ_ASSERT_RETURN(index != NULL, PJ_EINVAL); //Can't check stack because this function is called in the //beginning before main thread is initialized. //PJ_CHECK_STACK(); *index = TlsAlloc(); if (*index == TLS_OUT_OF_INDEXES) return PJ_RETURN_OS_ERROR(GetLastError()); else return PJ_SUCCESS; } /* * pj_thread_local_free() */ PJ_DEF(void) pj_thread_local_free(long index) { PJ_CHECK_STACK(); TlsFree(index); } /* * pj_thread_local_set() */ PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value) { BOOL rc; //Can't check stack because this function is called in the //beginning before main thread is initialized. //PJ_CHECK_STACK(); rc = TlsSetValue(index, value); return rc!=0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError()); } /* * pj_thread_local_get() */ PJ_DEF(void*) pj_thread_local_get(long index) { //Can't check stack because this function is called //by PJ_CHECK_STACK() itself!!! //PJ_CHECK_STACK(); return TlsGetValue(index); } /////////////////////////////////////////////////////////////////////////////// static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name) { PJ_CHECK_STACK(); #if PJ_WIN32_WINNT >= 0x0400 InitializeCriticalSection(&mutex->crit); #else mutex->hMutex = CreateMutex(NULL, FALSE, NULL); if (!mutex->hMutex) { return PJ_RETURN_OS_ERROR(GetLastError()); } #endif #if PJ_DEBUG /* Set owner. */ mutex->nesting_level = 0; mutex->owner = NULL; #endif /* Set name. */ if (!name) { name = "mtx%p"; } if (strchr(name, '%')) { pj_ansi_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex); } else { pj_ansi_strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME); mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; } PJ_LOG(6, (mutex->obj_name, "Mutex created")); return PJ_SUCCESS; } /* * pj_mutex_create() */ PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool, const char *name, int type, pj_mutex_t **mutex_ptr) { pj_status_t rc; pj_mutex_t *mutex; PJ_UNUSED_ARG(type); PJ_ASSERT_RETURN(pool && mutex_ptr, PJ_EINVAL); mutex = pj_pool_alloc(pool, sizeof(*mutex)); if (!mutex) return PJ_ENOMEM; rc = init_mutex(mutex, name); if (rc != PJ_SUCCESS) return rc; *mutex_ptr = mutex; return PJ_SUCCESS; } /* * pj_mutex_create_simple() */ PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name, pj_mutex_t **mutex ) { return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); } /* * pj_mutex_create_recursive() */ PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, const char *name, pj_mutex_t **mutex ) { return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex); } /* * pj_mutex_lock() */ PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) { pj_status_t status; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(mutex, PJ_EINVAL); LOG_MUTEX((mutex->obj_name, "Mutex: thread %s is waiting", pj_thread_this()->obj_name)); #if PJ_WIN32_WINNT >= 0x0400 EnterCriticalSection(&mutex->crit); status=PJ_SUCCESS; #else if (WaitForSingleObject(mutex->hMutex, INFINITE)==WAIT_OBJECT_0) status = PJ_SUCCESS; else status = PJ_STATUS_FROM_OS(GetLastError()); #endif LOG_MUTEX((mutex->obj_name, (status==PJ_SUCCESS ? "Mutex acquired by thread %s" : "FAILED by %s"), pj_thread_this()->obj_name)); #if PJ_DEBUG if (status == PJ_SUCCESS) { mutex->owner = pj_thread_this(); ++mutex->nesting_level; } #endif return status; } /* * pj_mutex_unlock() */ PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) { pj_status_t status; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(mutex, PJ_EINVAL); #if PJ_DEBUG pj_assert(mutex->owner == pj_thread_this()); if (--mutex->nesting_level == 0) { mutex->owner = NULL; } #endif LOG_MUTEX((mutex->obj_name, "Mutex released by thread %s", pj_thread_this()->obj_name)); #if PJ_WIN32_WINNT >= 0x0400 LeaveCriticalSection(&mutex->crit); status=PJ_SUCCESS; #else status = ReleaseMutex(mutex->hMutex) ? PJ_SUCCESS : PJ_STATUS_FROM_OS(GetLastError()); #endif return status; } /* * pj_mutex_trylock() */ PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) { pj_status_t status; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(mutex, PJ_EINVAL); LOG_MUTEX((mutex->obj_name, "Mutex: thread %s is trying", pj_thread_this()->obj_name)); #if PJ_WIN32_WINNT >= 0x0400 status=TryEnterCriticalSection(&mutex->crit) ? PJ_SUCCESS : PJ_EUNKNOWN; #else status = WaitForSingleObject(mutex->hMutex, 0)==WAIT_OBJECT_0 ? PJ_SUCCESS : PJ_ETIMEDOUT; #endif if (status==PJ_SUCCESS) { LOG_MUTEX((mutex->obj_name, "Mutex acquired by thread %s", pj_thread_this()->obj_name)); #if PJ_DEBUG mutex->owner = pj_thread_this(); ++mutex->nesting_level; #endif } else { LOG_MUTEX((mutex->obj_name, "Mutex: thread %s's trylock() failed", pj_thread_this()->obj_name)); } return status; } /* * pj_mutex_destroy() */ PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(mutex, PJ_EINVAL); LOG_MUTEX((mutex->obj_name, "Mutex destroyed")); #if PJ_WIN32_WINNT >= 0x0400 DeleteCriticalSection(&mutex->crit); return PJ_SUCCESS; #else return CloseHandle(mutex->hMutex) ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError()); #endif } /* * pj_mutex_is_locked() */ PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) { #if PJ_DEBUG return mutex->owner == pj_thread_this(); #else PJ_UNUSED_ARG(mutex); pj_assert(!"PJ_DEBUG is not set!"); return 1; #endif } /////////////////////////////////////////////////////////////////////////////// /* * Win32 lacks Read/Write mutex, so include the emulation. */ #include "os_rwmutex.c" /////////////////////////////////////////////////////////////////////////////// /* * pj_enter_critical_section() */ PJ_DEF(void) pj_enter_critical_section(void) { pj_mutex_lock(&critical_section_mutex); } /* * pj_leave_critical_section() */ PJ_DEF(void) pj_leave_critical_section(void) { pj_mutex_unlock(&critical_section_mutex); } /////////////////////////////////////////////////////////////////////////////// #if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 /* * pj_sem_create() */ PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, const char *name, unsigned initial, unsigned max, pj_sem_t **sem_ptr) { pj_sem_t *sem; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(pool && sem_ptr, PJ_EINVAL); sem = pj_pool_alloc(pool, sizeof(*sem)); sem->hSemaphore = CreateSemaphore(NULL, initial, max, NULL); if (!sem->hSemaphore) return PJ_RETURN_OS_ERROR(GetLastError()); /* Set name. */ if (!name) { name = "sem%p"; } if (strchr(name, '%')) { pj_ansi_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem); } else { pj_ansi_strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME); sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; } LOG_MUTEX((sem->obj_name, "Semaphore created")); *sem_ptr = sem; return PJ_SUCCESS; } static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout) { DWORD result; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sem, PJ_EINVAL); LOG_MUTEX((sem->obj_name, "Semaphore: thread %s is waiting", pj_thread_this()->obj_name)); result = WaitForSingleObject(sem->hSemaphore, timeout); if (result == WAIT_OBJECT_0) { LOG_MUTEX((sem->obj_name, "Semaphore acquired by thread %s", pj_thread_this()->obj_name)); } else { LOG_MUTEX((sem->obj_name, "Semaphore: thread %s FAILED to acquire", pj_thread_this()->obj_name)); } if (result==WAIT_OBJECT_0) return PJ_SUCCESS; else if (result==WAIT_TIMEOUT) return PJ_ETIMEDOUT; else return PJ_RETURN_OS_ERROR(GetLastError()); } /* * pj_sem_wait() */ PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sem, PJ_EINVAL); return pj_sem_wait_for(sem, INFINITE); } /* * pj_sem_trywait() */ PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sem, PJ_EINVAL); return pj_sem_wait_for(sem, 0); } /* * pj_sem_post() */ PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sem, PJ_EINVAL); LOG_MUTEX((sem->obj_name, "Semaphore released by thread %s", pj_thread_this()->obj_name)); if (ReleaseSemaphore(sem->hSemaphore, 1, NULL)) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(GetLastError()); } /* * pj_sem_destroy() */ PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sem, PJ_EINVAL); LOG_MUTEX((sem->obj_name, "Semaphore destroyed by thread %s", pj_thread_this()->obj_name)); if (CloseHandle(sem->hSemaphore)) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(GetLastError()); } #endif /* PJ_HAS_SEMAPHORE */ /////////////////////////////////////////////////////////////////////////////// #if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 /* * pj_event_create() */ PJ_DEF(pj_status_t) pj_event_create( pj_pool_t *pool, const char *name, pj_bool_t manual_reset, pj_bool_t initial, pj_event_t **event_ptr) { pj_event_t *event; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(pool && event_ptr, PJ_EINVAL); event = pj_pool_alloc(pool, sizeof(*event)); if (!event) return PJ_ENOMEM; event->hEvent = CreateEvent(NULL, manual_reset?TRUE:FALSE, initial?TRUE:FALSE, NULL); if (!event->hEvent) return PJ_RETURN_OS_ERROR(GetLastError()); /* Set name. */ if (!name) { name = "evt%p"; } if (strchr(name, '%')) { pj_ansi_snprintf(event->obj_name, PJ_MAX_OBJ_NAME, name, event); } else { pj_ansi_strncpy(event->obj_name, name, PJ_MAX_OBJ_NAME); event->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; } PJ_LOG(6, (event->obj_name, "Event created")); *event_ptr = event; return PJ_SUCCESS; } static pj_status_t pj_event_wait_for(pj_event_t *event, unsigned timeout) { DWORD result; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(event, PJ_EINVAL); PJ_LOG(6, (event->obj_name, "Event: thread %s is waiting", pj_thread_this()->obj_name)); result = WaitForSingleObject(event->hEvent, timeout); if (result == WAIT_OBJECT_0) { PJ_LOG(6, (event->obj_name, "Event: thread %s is released", pj_thread_this()->obj_name)); } else { PJ_LOG(6, (event->obj_name, "Event: thread %s FAILED to acquire", pj_thread_this()->obj_name)); } if (result==WAIT_OBJECT_0) return PJ_SUCCESS; else if (result==WAIT_TIMEOUT) return PJ_ETIMEDOUT; else return PJ_RETURN_OS_ERROR(GetLastError()); } /* * pj_event_wait() */ PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event) { PJ_ASSERT_RETURN(event, PJ_EINVAL); return pj_event_wait_for(event, INFINITE); } /* * pj_event_trywait() */ PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event) { PJ_ASSERT_RETURN(event, PJ_EINVAL); return pj_event_wait_for(event, 0); } /* * pj_event_set() */ PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(event, PJ_EINVAL); PJ_LOG(6, (event->obj_name, "Setting event")); if (SetEvent(event->hEvent)) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(GetLastError()); } /* * pj_event_pulse() */ PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(event, PJ_EINVAL); PJ_LOG(6, (event->obj_name, "Pulsing event")); if (PulseEvent(event->hEvent)) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(GetLastError()); } /* * pj_event_reset() */ PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(event, PJ_EINVAL); PJ_LOG(6, (event->obj_name, "Event is reset")); if (ResetEvent(event->hEvent)) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(GetLastError()); } /* * pj_event_destroy() */ PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(event, PJ_EINVAL); PJ_LOG(6, (event->obj_name, "Event is destroying")); if (CloseHandle(event->hEvent)) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(GetLastError()); } #endif /* PJ_HAS_EVENT_OBJ */ /////////////////////////////////////////////////////////////////////////////// #if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 /* * Terminal color */ static WORD pj_color_to_os_attr(pj_color_t color) { WORD attr = 0; if (color & PJ_TERM_COLOR_R) attr |= FOREGROUND_RED; if (color & PJ_TERM_COLOR_G) attr |= FOREGROUND_GREEN; if (color & PJ_TERM_COLOR_B) attr |= FOREGROUND_BLUE; if (color & PJ_TERM_COLOR_BRIGHT) attr |= FOREGROUND_INTENSITY; return attr; } static pj_color_t os_attr_to_pj_color(WORD attr) { int color = 0; if (attr & FOREGROUND_RED) color |= PJ_TERM_COLOR_R; if (attr & FOREGROUND_GREEN) color |= PJ_TERM_COLOR_G; if (attr & FOREGROUND_BLUE) color |= PJ_TERM_COLOR_B; if (attr & FOREGROUND_INTENSITY) color |= PJ_TERM_COLOR_BRIGHT; return color; } /* * pj_term_set_color() */ PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color) { BOOL rc; WORD attr = 0; PJ_CHECK_STACK(); attr = pj_color_to_os_attr(color); rc = SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), attr); return rc ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError()); } /* * pj_term_get_color() * Get current terminal foreground color. */ PJ_DEF(pj_color_t) pj_term_get_color(void) { CONSOLE_SCREEN_BUFFER_INFO info; PJ_CHECK_STACK(); GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &info); return os_attr_to_pj_color(info.wAttributes); } #endif /* PJ_TERM_HAS_COLOR */ /* * pj_run_app() */ PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], unsigned flags) { PJ_UNUSED_ARG(flags); return (*main_func)(argc, argv); } ================================================ FILE: deps/pjsip/pjlib/src/pj/os_error_linux_kernel.c ================================================ /* $Id: os_error_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #if defined(MODVERSIONS) #include #endif #include #include int kernel_errno; PJ_DEF(pj_status_t) pj_get_os_error(void) { return errno; } PJ_DEF(void) pj_set_os_error(pj_status_t code) { errno = code; } PJ_DEF(pj_status_t) pj_get_netos_error(void) { return errno; } PJ_DEF(void) pj_set_netos_error(pj_status_t code) { errno = code; } /* * platform_strerror() * * Platform specific error message. This file is called by pj_strerror() * in errno.c */ int platform_strerror( pj_os_err_type os_errcode, char *buf, pj_size_t bufsize) { char errmsg[PJ_ERR_MSG_SIZE]; int len; /* Handle EINVAL as special case so that it'll pass errno test. */ if (os_errcode==EINVAL) strcpy(errmsg, "Invalid value"); else snprintf(errmsg, sizeof(errmsg), "errno=%d", os_errcode); len = strlen(errmsg); if (len >= bufsize) len = bufsize-1; pj_memcpy(buf, errmsg, len); buf[len] = '\0'; return len; } ================================================ FILE: deps/pjsip/pjlib/src/pj/os_error_symbian.cpp ================================================ /* $Id: os_error_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING!=0) static const struct { pj_os_err_type code; const char *msg; } gaErrorList[] = { /* * Generic error -1 to -46 */ PJ_BUILD_ERR( KErrNotFound, "Unable to find the specified object"), PJ_BUILD_ERR( KErrGeneral, "General (unspecified) error"), PJ_BUILD_ERR( KErrCancel, "The operation was cancelled"), PJ_BUILD_ERR( KErrNoMemory, "Not enough memory"), PJ_BUILD_ERR( KErrNotSupported, "The operation requested is not supported"), PJ_BUILD_ERR( KErrArgument, "Bad request"), PJ_BUILD_ERR( KErrTotalLossOfPrecision, "Total loss of precision"), PJ_BUILD_ERR( KErrBadHandle, "Bad object"), PJ_BUILD_ERR( KErrOverflow, "Overflow"), PJ_BUILD_ERR( KErrUnderflow, "Underflow"), PJ_BUILD_ERR( KErrAlreadyExists,"Already exists"), PJ_BUILD_ERR( KErrPathNotFound, "Unable to find the specified folder"), PJ_BUILD_ERR( KErrDied, "Closed"), PJ_BUILD_ERR( KErrInUse, "The specified object is currently in use by another program"), PJ_BUILD_ERR( KErrServerTerminated, "Server has closed"), PJ_BUILD_ERR( KErrServerBusy, "Server busy"), PJ_BUILD_ERR( KErrCompletion, "Completion error"), PJ_BUILD_ERR( KErrNotReady, "Not ready"), PJ_BUILD_ERR( KErrUnknown, "Unknown error"), PJ_BUILD_ERR( KErrCorrupt, "Corrupt"), PJ_BUILD_ERR( KErrAccessDenied, "Access denied"), PJ_BUILD_ERR( KErrLocked, "Locked"), PJ_BUILD_ERR( KErrWrite, "Failed to write"), PJ_BUILD_ERR( KErrDisMounted, "Wrong disk present"), PJ_BUILD_ERR( KErrEof, "Unexpected end of file"), PJ_BUILD_ERR( KErrDiskFull, "Disk full"), PJ_BUILD_ERR( KErrBadDriver, "Bad device driver"), PJ_BUILD_ERR( KErrBadName, "Bad name"), PJ_BUILD_ERR( KErrCommsLineFail,"Comms line failed"), PJ_BUILD_ERR( KErrCommsFrame, "Comms frame error"), PJ_BUILD_ERR( KErrCommsOverrun, "Comms overrun error"), PJ_BUILD_ERR( KErrCommsParity, "Comms parity error"), PJ_BUILD_ERR( KErrTimedOut, "Timed out"), PJ_BUILD_ERR( KErrCouldNotConnect, "Failed to connect"), PJ_BUILD_ERR( KErrCouldNotDisconnect, "Failed to disconnect"), PJ_BUILD_ERR( KErrDisconnected, "Disconnected"), PJ_BUILD_ERR( KErrBadLibraryEntryPoint, "Bad library entry point"), PJ_BUILD_ERR( KErrBadDescriptor,"Bad descriptor"), PJ_BUILD_ERR( KErrAbort, "Interrupted"), PJ_BUILD_ERR( KErrTooBig, "Too big"), PJ_BUILD_ERR( KErrDivideByZero, "Divide by zero"), PJ_BUILD_ERR( KErrBadPower, "Batteries too low"), PJ_BUILD_ERR( KErrDirFull, "Folder full"), PJ_BUILD_ERR( KErrHardwareNotAvailable, ""), PJ_BUILD_ERR( KErrSessionClosed, ""), PJ_BUILD_ERR( KErrPermissionDenied, ""), /* * Socket errors (-190 - -1000) */ PJ_BUILD_ERR( KErrNetUnreach, "Could not connect to the network. Currently unreachable"), PJ_BUILD_ERR( KErrHostUnreach, "Could not connect to the specified server"), PJ_BUILD_ERR( KErrNoProtocolOpt,"The specified server refuses the selected protocol"), PJ_BUILD_ERR( KErrUrgentData, ""), PJ_BUILD_ERR( KErrWouldBlock, "Conflicts with KErrExtended, but cannot occur in practice"), {0, NULL} }; #endif /* PJ_HAS_ERROR_STRING */ PJ_DEF(pj_status_t) pj_get_os_error(void) { return -1; } PJ_DEF(void) pj_set_os_error(pj_status_t code) { PJ_UNUSED_ARG(code); } PJ_DEF(pj_status_t) pj_get_netos_error(void) { return -1; } PJ_DEF(void) pj_set_netos_error(pj_status_t code) { PJ_UNUSED_ARG(code); } PJ_BEGIN_DECL PJ_DECL(int) platform_strerror( pj_os_err_type os_errcode, char *buf, pj_size_t bufsize); PJ_END_DECL /* * platform_strerror() * * Platform specific error message. This file is called by pj_strerror() * in errno.c */ PJ_DEF(int) platform_strerror( pj_os_err_type os_errcode, char *buf, pj_size_t bufsize) { int len = 0; pj_assert(buf != NULL); pj_assert(bufsize >= 0); /* * MUST NOT check stack here. * This function might be called from PJ_CHECK_STACK() itself! //PJ_CHECK_STACK(); */ if (!len) { #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING!=0) int i; for (i = 0; gaErrorList[i].msg; ++i) { if (gaErrorList[i].code == os_errcode) { len = strlen(gaErrorList[i].msg); if ((pj_size_t)len >= bufsize) { len = bufsize-1; } pj_memcpy(buf, gaErrorList[i].msg, len); buf[len] = '\0'; break; } } #endif /* PJ_HAS_ERROR_STRING */ } if (!len) { len = pj_ansi_snprintf( buf, bufsize-1, "Symbian native error %d", os_errcode); buf[len] = '\0'; } return len; } ================================================ FILE: deps/pjsip/pjlib/src/pj/os_error_unix.c ================================================ /* $Id: os_error_unix.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include PJ_DEF(pj_status_t) pj_get_os_error(void) { return PJ_STATUS_FROM_OS(errno); } PJ_DEF(void) pj_set_os_error(pj_status_t code) { errno = PJ_STATUS_TO_OS(code); } PJ_DEF(pj_status_t) pj_get_netos_error(void) { return PJ_STATUS_FROM_OS(errno); } PJ_DEF(void) pj_set_netos_error(pj_status_t code) { errno = PJ_STATUS_TO_OS(code); } PJ_BEGIN_DECL PJ_DECL(int) platform_strerror(pj_os_err_type code, char *buf, pj_size_t bufsize ); PJ_END_DECL /* * platform_strerror() * * Platform specific error message. This file is called by pj_strerror() * in errno.c */ int platform_strerror( pj_os_err_type os_errcode, char *buf, pj_size_t bufsize) { const char *syserr = strerror(os_errcode); pj_size_t len = syserr ? strlen(syserr) : 0; if (len >= bufsize) len = bufsize - 1; if (len > 0) pj_memcpy(buf, syserr, len); buf[len] = '\0'; return len; } ================================================ FILE: deps/pjsip/pjlib/src/pj/os_error_win32.c ================================================ /* $Id: os_error_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 # include #elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 # include #endif /* * From Apache's APR: */ #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING!=0) static const struct { pj_os_err_type code; const char *msg; } gaErrorList[] = { PJ_BUILD_ERR( WSAEINTR, "Interrupted system call"), PJ_BUILD_ERR( WSAEBADF, "Bad file number"), PJ_BUILD_ERR( WSAEACCES, "Permission denied"), PJ_BUILD_ERR( WSAEFAULT, "Bad address"), PJ_BUILD_ERR( WSAEINVAL, "Invalid argument"), PJ_BUILD_ERR( WSAEMFILE, "Too many open sockets"), PJ_BUILD_ERR( WSAEWOULDBLOCK, "Operation would block"), PJ_BUILD_ERR( WSAEINPROGRESS, "Operation now in progress"), PJ_BUILD_ERR( WSAEALREADY, "Operation already in progress"), PJ_BUILD_ERR( WSAENOTSOCK, "Socket operation on non-socket"), PJ_BUILD_ERR( WSAEDESTADDRREQ, "Destination address required"), PJ_BUILD_ERR( WSAEMSGSIZE, "Message too long"), PJ_BUILD_ERR( WSAEPROTOTYPE, "Protocol wrong type for socket"), PJ_BUILD_ERR( WSAENOPROTOOPT, "Bad protocol option"), PJ_BUILD_ERR( WSAEPROTONOSUPPORT, "Protocol not supported"), PJ_BUILD_ERR( WSAESOCKTNOSUPPORT, "Socket type not supported"), PJ_BUILD_ERR( WSAEOPNOTSUPP, "Operation not supported on socket"), PJ_BUILD_ERR( WSAEPFNOSUPPORT, "Protocol family not supported"), PJ_BUILD_ERR( WSAEAFNOSUPPORT, "Address family not supported"), PJ_BUILD_ERR( WSAEADDRINUSE, "Address already in use"), PJ_BUILD_ERR( WSAEADDRNOTAVAIL, "Can't assign requested address"), PJ_BUILD_ERR( WSAENETDOWN, "Network is down"), PJ_BUILD_ERR( WSAENETUNREACH, "Network is unreachable"), PJ_BUILD_ERR( WSAENETRESET, "Net connection reset"), PJ_BUILD_ERR( WSAECONNABORTED, "Software caused connection abort"), PJ_BUILD_ERR( WSAECONNRESET, "Connection reset by peer"), PJ_BUILD_ERR( WSAENOBUFS, "No buffer space available"), PJ_BUILD_ERR( WSAEISCONN, "Socket is already connected"), PJ_BUILD_ERR( WSAENOTCONN, "Socket is not connected"), PJ_BUILD_ERR( WSAESHUTDOWN, "Can't send after socket shutdown"), PJ_BUILD_ERR( WSAETOOMANYREFS, "Too many references, can't splice"), PJ_BUILD_ERR( WSAETIMEDOUT, "Connection timed out"), PJ_BUILD_ERR( WSAECONNREFUSED, "Connection refused"), PJ_BUILD_ERR( WSAELOOP, "Too many levels of symbolic links"), PJ_BUILD_ERR( WSAENAMETOOLONG, "File name too long"), PJ_BUILD_ERR( WSAEHOSTDOWN, "Host is down"), PJ_BUILD_ERR( WSAEHOSTUNREACH, "No route to host"), PJ_BUILD_ERR( WSAENOTEMPTY, "Directory not empty"), PJ_BUILD_ERR( WSAEPROCLIM, "Too many processes"), PJ_BUILD_ERR( WSAEUSERS, "Too many users"), PJ_BUILD_ERR( WSAEDQUOT, "Disc quota exceeded"), PJ_BUILD_ERR( WSAESTALE, "Stale NFS file handle"), PJ_BUILD_ERR( WSAEREMOTE, "Too many levels of remote in path"), PJ_BUILD_ERR( WSASYSNOTREADY, "Network system is unavailable"), PJ_BUILD_ERR( WSAVERNOTSUPPORTED, "Winsock version out of range"), PJ_BUILD_ERR( WSANOTINITIALISED, "WSAStartup not yet called"), PJ_BUILD_ERR( WSAEDISCON, "Graceful shutdown in progress"), /* #define WSAENOMORE (WSABASEERR+102) #define WSAECANCELLED (WSABASEERR+103) #define WSAEINVALIDPROCTABLE (WSABASEERR+104) #define WSAEINVALIDPROVIDER (WSABASEERR+105) #define WSAEPROVIDERFAILEDINIT (WSABASEERR+106) #define WSASYSCALLFAILURE (WSABASEERR+107) #define WSASERVICE_NOT_FOUND (WSABASEERR+108) #define WSATYPE_NOT_FOUND (WSABASEERR+109) #define WSA_E_NO_MORE (WSABASEERR+110) #define WSA_E_CANCELLED (WSABASEERR+111) #define WSAEREFUSED (WSABASEERR+112) */ PJ_BUILD_ERR( WSAHOST_NOT_FOUND, "Host not found"), /* #define WSATRY_AGAIN (WSABASEERR+1002) #define WSANO_RECOVERY (WSABASEERR+1003) */ PJ_BUILD_ERR( WSANO_DATA, "No host data of that type was found"), {0, NULL} }; #endif /* PJ_HAS_ERROR_STRING */ PJ_DEF(pj_status_t) pj_get_os_error(void) { return PJ_STATUS_FROM_OS(GetLastError()); } PJ_DEF(void) pj_set_os_error(pj_status_t code) { SetLastError(PJ_STATUS_TO_OS(code)); } PJ_DEF(pj_status_t) pj_get_netos_error(void) { return PJ_STATUS_FROM_OS(WSAGetLastError()); } PJ_DEF(void) pj_set_netos_error(pj_status_t code) { WSASetLastError(PJ_STATUS_TO_OS(code)); } /* * platform_strerror() * * Platform specific error message. This file is called by pj_strerror() * in errno.c */ int platform_strerror( pj_os_err_type os_errcode, char *buf, pj_size_t bufsize) { pj_size_t len = 0; PJ_DECL_UNICODE_TEMP_BUF(wbuf,128); pj_assert(buf != NULL); pj_assert(bufsize >= 0); /* * MUST NOT check stack here. * This function might be called from PJ_CHECK_STACK() itself! //PJ_CHECK_STACK(); */ if (!len) { #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING!=0) int i; for (i = 0; gaErrorList[i].msg; ++i) { if (gaErrorList[i].code == os_errcode) { len = strlen(gaErrorList[i].msg); if ((pj_size_t)len >= bufsize) { len = bufsize-1; } pj_memcpy(buf, gaErrorList[i].msg, len); buf[len] = '\0'; break; } } #endif /* PJ_HAS_ERROR_STRING */ } if (!len) { #if PJ_NATIVE_STRING_IS_UNICODE len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, os_errcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), wbuf, sizeof(wbuf), NULL); if (len) { pj_unicode_to_ansi(wbuf, len, buf, bufsize); } #else len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, os_errcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (int)bufsize, NULL); buf[bufsize-1] = '\0'; #endif if (len) { /* Remove trailing newlines. */ while (len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { buf[len-1] = '\0'; --len; } } } if (!len) { len = pj_ansi_snprintf( buf, bufsize, "Win32 error code %u", (unsigned)os_errcode); if (len < 0 || len >= (int)bufsize) len = bufsize-1; buf[len] = '\0'; } return (int)len; } ================================================ FILE: deps/pjsip/pjlib/src/pj/os_info.c ================================================ /* $Id: os_info.c 4411 2013-03-04 04:34:38Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include /* * FYI these links contain useful infos about predefined macros across * platforms: * - http://predef.sourceforge.net/preos.html */ #if defined(PJ_HAS_SYS_UTSNAME_H) && PJ_HAS_SYS_UTSNAME_H != 0 /* For uname() */ # include # include # define PJ_HAS_UNAME 1 #endif #if defined(PJ_HAS_LIMITS_H) && PJ_HAS_LIMITS_H != 0 /* Include to get to get various glibc macros. * See http://predef.sourceforge.net/prelib.html */ # include #endif #if defined(_MSC_VER) /* For all Windows including mobile */ # include #endif #if defined(PJ_DARWINOS) && PJ_DARWINOS != 0 # include "TargetConditionals.h" #endif #ifndef PJ_SYS_INFO_BUFFER_SIZE # define PJ_SYS_INFO_BUFFER_SIZE 64 #endif #if defined(PJ_DARWINOS) && PJ_DARWINOS != 0 && TARGET_OS_IPHONE # include # include void pj_iphone_os_get_sys_info(pj_sys_info *si, pj_str_t *si_buffer); #endif #if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0 PJ_BEGIN_DECL unsigned pj_symbianos_get_model_info(char *buf, unsigned buf_size); unsigned pj_symbianos_get_platform_info(char *buf, unsigned buf_size); void pj_symbianos_get_sdk_info(pj_str_t *name, pj_uint32_t *ver); PJ_END_DECL #endif static char *ver_info(pj_uint32_t ver, char *buf) { pj_size_t len; if (ver == 0) { *buf = '\0'; return buf; } sprintf(buf, "-%u.%u", (ver & 0xFF000000) >> 24, (ver & 0x00FF0000) >> 16); len = strlen(buf); if (ver & 0xFFFF) { sprintf(buf+len, ".%u", (ver & 0xFF00) >> 8); len = strlen(buf); if (ver & 0x00FF) { sprintf(buf+len, ".%u", (ver & 0xFF)); } } return buf; } static pj_uint32_t parse_version(char *str) { char *tok; int i, maxtok; pj_uint32_t version = 0; while (*str && !pj_isdigit(*str)) str++; maxtok = 4; for (tok = strtok(str, ".-"), i=0; tok && i= len+1) { \ si.field.ptr = si_buffer + PJ_SYS_INFO_BUFFER_SIZE - left; \ si.field.slen = len; \ pj_memcpy(si.field.ptr, str, len+1); \ left -= (len+1); \ } \ } while (0) /* * Machine and OS info. */ #if defined(PJ_HAS_UNAME) && PJ_HAS_UNAME #if defined(PJ_DARWINOS) && PJ_DARWINOS != 0 && TARGET_OS_IPHONE && \ (!defined TARGET_IPHONE_SIMULATOR || TARGET_IPHONE_SIMULATOR == 0) { pj_str_t buf = {si_buffer + PJ_SYS_INFO_BUFFER_SIZE - left, left}; pj_str_t machine = {"arm-", 4}; pj_str_t sdk_name = {"iOS-SDK", 7}; size_t size = PJ_SYS_INFO_BUFFER_SIZE - machine.slen; char tmp[PJ_SYS_INFO_BUFFER_SIZE]; int name[] = {CTL_HW,HW_MACHINE}; pj_iphone_os_get_sys_info(&si, &buf); left -= si.os_name.slen + 1; si.os_ver = parse_version(si.machine.ptr); pj_memcpy(tmp, machine.ptr, machine.slen); sysctl(name, 2, tmp+machine.slen, &size, NULL, 0); ALLOC_CP_STR(tmp, machine); si.sdk_name = sdk_name; #ifdef PJ_SDK_NAME pj_memcpy(tmp, PJ_SDK_NAME, pj_ansi_strlen(PJ_SDK_NAME) + 1); si.sdk_ver = parse_version(tmp); #endif } #else { struct utsname u; /* Successful uname() returns zero on Linux and positive value * on OpenSolaris. */ if (uname(&u) == -1) goto get_sdk_info; ALLOC_CP_STR(u.machine, machine); ALLOC_CP_STR(u.sysname, os_name); si.os_ver = parse_version(u.release); } #endif #elif defined(_MSC_VER) { OSVERSIONINFO ovi; ovi.dwOSVersionInfoSize = sizeof(ovi); if (GetVersionEx(&ovi) == FALSE) goto get_sdk_info; si.os_ver = (ovi.dwMajorVersion << 24) | (ovi.dwMinorVersion << 16); #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE si.os_name = pj_str("wince"); #else si.os_name = pj_str("win32"); #endif } { SYSTEM_INFO wsi; GetSystemInfo(&wsi); switch (wsi.wProcessorArchitecture) { #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE case PROCESSOR_ARCHITECTURE_ARM: si.machine = pj_str("arm"); break; case PROCESSOR_ARCHITECTURE_SHX: si.machine = pj_str("shx"); break; #else case PROCESSOR_ARCHITECTURE_AMD64: si.machine = pj_str("x86_64"); break; case PROCESSOR_ARCHITECTURE_IA64: si.machine = pj_str("ia64"); break; case PROCESSOR_ARCHITECTURE_INTEL: si.machine = pj_str("i386"); break; #endif /* PJ_WIN32_WINCE */ } } #elif defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0 { pj_symbianos_get_model_info(si_buffer, sizeof(si_buffer)); ALLOC_CP_STR(si_buffer, machine); char *p = si_buffer + sizeof(si_buffer) - left; unsigned plen; plen = pj_symbianos_get_platform_info(p, left); if (plen) { /* Output format will be "Series60vX.X" */ si.os_name = pj_str("S60"); si.os_ver = parse_version(p+9); } else { si.os_name = pj_str("Unknown"); } /* Avoid compile warning on Symbian. */ goto get_sdk_info; } #endif /* * SDK info. */ get_sdk_info: #if defined(__GLIBC__) si.sdk_ver = (__GLIBC__ << 24) | (__GLIBC_MINOR__ << 16); si.sdk_name = pj_str("glibc"); #elif defined(__GNU_LIBRARY__) si.sdk_ver = (__GNU_LIBRARY__ << 24) | (__GNU_LIBRARY_MINOR__ << 16); si.sdk_name = pj_str("libc"); #elif defined(__UCLIBC__) si.sdk_ver = (__UCLIBC_MAJOR__ << 24) | (__UCLIBC_MINOR__ << 16); si.sdk_name = pj_str("uclibc"); #elif defined(_WIN32_WCE) && _WIN32_WCE /* Old window mobile declares _WIN32_WCE as decimal (e.g. 300, 420, etc.), * but then it was changed to use hex, e.g. 0x420, etc. See * http://social.msdn.microsoft.com/forums/en-US/vssmartdevicesnative/thread/8a97c59f-5a1c-4bc6-99e6-427f065ff439/ */ #if _WIN32_WCE <= 500 si.sdk_ver = ( (_WIN32_WCE / 100) << 24) | ( ((_WIN32_WCE % 100) / 10) << 16) | ( (_WIN32_WCE % 10) << 8); #else si.sdk_ver = ( ((_WIN32_WCE & 0xFF00) >> 8) << 24) | ( ((_WIN32_WCE & 0x00F0) >> 4) << 16) | ( ((_WIN32_WCE & 0x000F) >> 0) << 8); #endif si.sdk_name = pj_str("cesdk"); #elif defined(_MSC_VER) /* No SDK info is easily obtainable for Visual C, so lets just use * _MSC_VER. The _MSC_VER macro reports the major and minor versions * of the compiler. For example, 1310 for Microsoft Visual C++ .NET 2003. * 1310 represents version 13 and a 1.0 point release. * The Visual C++ 2005 compiler version is 1400. */ si.sdk_ver = ((_MSC_VER / 100) << 24) | (((_MSC_VER % 100) / 10) << 16) | ((_MSC_VER % 10) << 8); si.sdk_name = pj_str("msvc"); #elif defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0 pj_symbianos_get_sdk_info(&si.sdk_name, &si.sdk_ver); #endif /* * Build the info string. */ { char tmp[PJ_SYS_INFO_BUFFER_SIZE]; char os_ver[20], sdk_ver[20]; int cnt; cnt = pj_ansi_snprintf(tmp, sizeof(tmp), "%s%s%s%s%s%s%s", si.os_name.ptr, ver_info(si.os_ver, os_ver), (si.machine.slen ? "/" : ""), si.machine.ptr, (si.sdk_name.slen ? "/" : ""), si.sdk_name.ptr, ver_info(si.sdk_ver, sdk_ver)); if (cnt > 0 && cnt < (int)sizeof(tmp)) { ALLOC_CP_STR(tmp, info); } } si_initialized = PJ_TRUE; return &si; } ================================================ FILE: deps/pjsip/pjlib/src/pj/os_info_iphone.m ================================================ /* $Id: os_info_iphone.m 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "TargetConditionals.h" #if !defined TARGET_IPHONE_SIMULATOR || TARGET_IPHONE_SIMULATOR == 0 #include #include #include void pj_iphone_os_get_sys_info(pj_sys_info *si, pj_str_t *si_buffer) { unsigned buf_len = si_buffer->slen, left = si_buffer->slen, len; UIDevice *device = [UIDevice currentDevice]; if ([device respondsToSelector:@selector(isMultitaskingSupported)]) si->flags |= PJ_SYS_HAS_IOS_BG; #define ALLOC_CP_STR(str,field) \ do { \ len = [str length]; \ if (len && left >= len+1) { \ si->field.ptr = si_buffer->ptr + buf_len - left; \ si->field.slen = len; \ [str getCString:si->field.ptr maxLength:len+1 \ encoding:NSASCIIStringEncoding]; \ left -= (len+1); \ } \ } while (0) ALLOC_CP_STR([device systemName], os_name); ALLOC_CP_STR([device systemVersion], machine); } #endif ================================================ FILE: deps/pjsip/pjlib/src/pj/os_info_symbian.cpp ================================================ /* $Id: os_info_symbian.cpp 3437 2011-03-08 06:30:34Z nanang $ */ /* * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #if !defined(PJ_SYMBIAN) || PJ_SYMBIAN == 0 # error This file is only for Symbian platform #endif #include #include #include /* link against efsrv.lib */ #include /* link against hal.lib */ #include /* link against charconv.lib */ PJ_BEGIN_DECL unsigned pj_symbianos_get_model_info(char *buf, unsigned buf_size); unsigned pj_symbianos_get_platform_info(char *buf, unsigned buf_size); void pj_symbianos_get_sdk_info(pj_str_t *name, pj_uint32_t *ver); PJ_END_DECL /* Get Symbian phone model info, returning length of model info */ unsigned pj_symbianos_get_model_info(char *buf, unsigned buf_size) { pj_str_t model_name; /* Get machine UID */ TInt hal_val; HAL::Get(HAL::EMachineUid, hal_val); pj_ansi_snprintf(buf, buf_size, "0x%08X", hal_val); pj_strset2(&model_name, buf); /* Get model name */ const pj_str_t st_copyright = {"(C)", 3}; const pj_str_t st_nokia = {"Nokia", 5}; char tmp_buf[64]; pj_str_t tmp_str; _LIT(KModelFilename,"Z:\\resource\\versions\\model.txt"); RFile file; RFs fs; TInt err; fs.Connect(1); err = file.Open(fs, KModelFilename, EFileRead); if (err == KErrNone) { TFileText text; text.Set(file); TBuf16<64> ModelName16; err = text.Read(ModelName16); if (err == KErrNone) { TPtr8 ptr8((TUint8*)tmp_buf, sizeof(tmp_buf)); ptr8.Copy(ModelName16); pj_strset(&tmp_str, tmp_buf, ptr8.Length()); pj_strtrim(&tmp_str); } file.Close(); } fs.Close(); if (err != KErrNone) goto on_return; /* The retrieved model name is usually in long format, e.g: * " Nokia N95 (01.01)", "(C) Nokia E52". As we need only * the short version, let's clean it up. */ /* Remove preceding non-ASCII chars, e.g: "" */ char *p = tmp_str.ptr; while (!pj_isascii(*p)) { p++; } pj_strset(&tmp_str, p, tmp_str.slen - (p - tmp_str.ptr)); /* Remove "(C)" */ p = pj_stristr(&tmp_str, &st_copyright); if (p) { p += st_copyright.slen; pj_strset(&tmp_str, p, tmp_str.slen - (p - tmp_str.ptr)); } /* Remove "Nokia" */ p = pj_stristr(&tmp_str, &st_nokia); if (p) { p += st_nokia.slen; pj_strset(&tmp_str, p, tmp_str.slen - (p - tmp_str.ptr)); } /* Remove language version, e.g: "(01.01)" */ p = pj_strchr(&tmp_str, '('); if (p) { tmp_str.slen = p - tmp_str.ptr; } pj_strtrim(&tmp_str); if (tmp_str.slen == 0) goto on_return; if ((unsigned)tmp_str.slen > buf_size - model_name.slen - 3) tmp_str.slen = buf_size - model_name.slen - 3; pj_strcat2(&model_name, "("); pj_strcat(&model_name, &tmp_str); pj_strcat2(&model_name, ")"); /* Zero terminate */ buf[model_name.slen] = '\0'; on_return: return model_name.slen; } /* Get platform info, returned format will be "Series60vX.X" */ unsigned pj_symbianos_get_platform_info(char *buf, unsigned buf_size) { /* OS info */ _LIT(KS60ProductIDFile, "Series60v*.sis"); _LIT(KROMInstallDir, "z:\\system\\install\\"); RFs fs; TFindFile ff(fs); CDir* result; pj_str_t plat_info = {NULL, 0}; TInt err; fs.Connect(1); err = ff.FindWildByDir(KS60ProductIDFile, KROMInstallDir, result); if (err == KErrNone) { err = result->Sort(ESortByName|EDescending); if (err == KErrNone) { TPtr8 tmp_ptr8((TUint8*)buf, buf_size); const pj_str_t tmp_ext = {".sis", 4}; char *p; tmp_ptr8.Copy((*result)[0].iName); pj_strset(&plat_info, buf, (pj_size_t)tmp_ptr8.Length()); p = pj_stristr(&plat_info, &tmp_ext); if (p) plat_info.slen -= (p - plat_info.ptr); } delete result; } fs.Close(); buf[plat_info.slen] = '\0'; return plat_info.slen; } /* Get SDK info */ void pj_symbianos_get_sdk_info(pj_str_t *name, pj_uint32_t *ver) { const pj_str_t S60 = {"S60", 3}; #if defined(__SERIES60_30__) *name = S60; *ver = (3 << 24); #elif defined(__SERIES60_31__) *name = S60; *ver = (3 << 24) | (1 << 16); #elif defined(__S60_32__) *name = S60; *ver = (3 << 24) | (2 << 16); #elif defined(__S60_50__) *name = S60; *ver = (5 << 24); #elif defined(__NOKIA_N97__) *name = pj_str("N97"); *ver = (1 << 24); #else *name = pj_str("Unknown"); *ver = 0; #endif } ================================================ FILE: deps/pjsip/pjlib/src/pj/os_rwmutex.c ================================================ /* $Id: os_rwmutex.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Note: * * DO NOT BUILD THIS FILE DIRECTLY. THIS FILE WILL BE INCLUDED BY os_core_*.c * WHEN MACRO PJ_EMULATE_RWMUTEX IS SET. */ /* * os_rwmutex.c: * * Implementation of Read-Write mutex for platforms that lack it (e.g. * Win32, RTEMS). */ struct pj_rwmutex_t { pj_mutex_t *read_lock; /* write_lock must use semaphore, because write_lock may be released * by thread other than the thread that acquire the write_lock in the * first place. */ pj_sem_t *write_lock; pj_int32_t reader_count; }; /* * Create reader/writer mutex. * */ PJ_DEF(pj_status_t) pj_rwmutex_create(pj_pool_t *pool, const char *name, pj_rwmutex_t **p_mutex) { pj_status_t status; pj_rwmutex_t *rwmutex; PJ_ASSERT_RETURN(pool && p_mutex, PJ_EINVAL); *p_mutex = NULL; rwmutex = PJ_POOL_ALLOC_T(pool, pj_rwmutex_t); status = pj_mutex_create_simple(pool, name, &rwmutex ->read_lock); if (status != PJ_SUCCESS) return status; status = pj_sem_create(pool, name, 1, 1, &rwmutex->write_lock); if (status != PJ_SUCCESS) { pj_mutex_destroy(rwmutex->read_lock); return status; } rwmutex->reader_count = 0; *p_mutex = rwmutex; return PJ_SUCCESS; } /* * Lock the mutex for reading. * */ PJ_DEF(pj_status_t) pj_rwmutex_lock_read(pj_rwmutex_t *mutex) { pj_status_t status; PJ_ASSERT_RETURN(mutex, PJ_EINVAL); status = pj_mutex_lock(mutex->read_lock); if (status != PJ_SUCCESS) { pj_assert(!"This pretty much is unexpected"); return status; } mutex->reader_count++; pj_assert(mutex->reader_count < 0x7FFFFFF0L); if (mutex->reader_count == 1) pj_sem_wait(mutex->write_lock); status = pj_mutex_unlock(mutex->read_lock); return status; } /* * Lock the mutex for writing. * */ PJ_DEF(pj_status_t) pj_rwmutex_lock_write(pj_rwmutex_t *mutex) { PJ_ASSERT_RETURN(mutex, PJ_EINVAL); return pj_sem_wait(mutex->write_lock); } /* * Release read lock. * */ PJ_DEF(pj_status_t) pj_rwmutex_unlock_read(pj_rwmutex_t *mutex) { pj_status_t status; PJ_ASSERT_RETURN(mutex, PJ_EINVAL); status = pj_mutex_lock(mutex->read_lock); if (status != PJ_SUCCESS) { pj_assert(!"This pretty much is unexpected"); return status; } pj_assert(mutex->reader_count >= 1); --mutex->reader_count; if (mutex->reader_count == 0) pj_sem_post(mutex->write_lock); status = pj_mutex_unlock(mutex->read_lock); return status; } /* * Release write lock. * */ PJ_DEF(pj_status_t) pj_rwmutex_unlock_write(pj_rwmutex_t *mutex) { PJ_ASSERT_RETURN(mutex, PJ_EINVAL); pj_assert(mutex->reader_count <= 1); return pj_sem_post(mutex->write_lock); } /* * Destroy reader/writer mutex. * */ PJ_DEF(pj_status_t) pj_rwmutex_destroy(pj_rwmutex_t *mutex) { PJ_ASSERT_RETURN(mutex, PJ_EINVAL); pj_mutex_destroy(mutex->read_lock); pj_sem_destroy(mutex->write_lock); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib/src/pj/os_symbian.h ================================================ /* $Id: os_symbian.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __OS_SYMBIAN_H__ #define __OS_SYMBIAN_H__ #include #include #include #include #include #include #include #include #include #include #include #include #include // Forward declarations class CPjSocketReader; #ifndef PJ_SYMBIAN_TIMER_PRIORITY # define PJ_SYMBIAN_TIMER_PRIORITY EPriorityNormal #endif // // PJLIB Symbian's Socket // class CPjSocket { public: enum { MAX_LEN = 1500, }; // Construct CPjSocket CPjSocket(int af, int sock_type, RSocket &sock) : af_(af), sock_(sock), sock_type_(sock_type), connected_(false), sockReader_(NULL) { } // Destroy CPjSocket ~CPjSocket(); // Get address family int GetAf() const { return af_; } // Get the internal RSocket RSocket& Socket() { return sock_; } // Get socket connected flag. bool IsConnected() const { return connected_; } // Set socket connected flag. void SetConnected(bool connected) { connected_ = connected; } // Get socket type int GetSockType() const { return sock_type_; } // Returns true if socket is a datagram bool IsDatagram() const { return sock_type_ == KSockDatagram; } // Get socket reader, if any. // May return NULL. CPjSocketReader *Reader() { return sockReader_; } // Create socket reader. CPjSocketReader *CreateReader(unsigned max_len=CPjSocket::MAX_LEN); // Delete socket reader when it's not wanted. void DestroyReader(); private: int af_; RSocket sock_; // Must not be reference, or otherwise // it may point to local variable! unsigned sock_type_; bool connected_; CPjSocketReader *sockReader_; }; // // Socket reader, used by select() and ioqueue abstraction // class CPjSocketReader : public CActive { public: // Construct. static CPjSocketReader *NewL(CPjSocket &sock, unsigned max_len=CPjSocket::MAX_LEN); // Destroy; ~CPjSocketReader(); // Start asynchronous read from the socket. void StartRecv(void (*cb)(void *key)=NULL, void *key=NULL, TDes8 *aDesc = NULL, TUint flags = 0); // Start asynchronous read from the socket. void StartRecvFrom(void (*cb)(void *key)=NULL, void *key=NULL, TDes8 *aDesc = NULL, TUint flags = 0, TSockAddr *fromAddr = NULL); // Cancel asynchronous read. void DoCancel(); // Implementation: called when read has completed. void RunL(); // Check if there's pending data. bool HasData() const { return buffer_.Length() != 0; } // Append data to aDesc, up to aDesc's maximum size. // If socket is datagram based, buffer_ will be clared. void ReadData(TDes8 &aDesc, TInetAddr *addr=NULL); private: CPjSocket &sock_; bool isDatagram_; TPtr8 buffer_; TInetAddr recvAddr_; void (*readCb_)(void *key); void *key_; // // Constructor // CPjSocketReader(CPjSocket &sock); void ConstructL(unsigned max_len); }; // // Time-out Timer Active Object // class CPjTimeoutTimer : public CActive { public: static CPjTimeoutTimer *NewL(); ~CPjTimeoutTimer(); void StartTimer(TUint miliSeconds); bool HasTimedOut() const; protected: virtual void RunL(); virtual void DoCancel(); virtual TInt RunError(TInt aError); private: RTimer timer_; pj_bool_t hasTimedOut_; CPjTimeoutTimer(); void ConstructL(); }; // // Symbian OS helper for PJLIB // class PjSymbianOS { public: // // Get the singleton instance of PjSymbianOS // static PjSymbianOS *Instance(); // // Set parameters // void SetParameters(pj_symbianos_params *params); // // Initialize. // TInt Initialize(); // // Shutdown. // void Shutdown(); // // Socket helper. // // Get RSocketServ instance to be used by all sockets. RSocketServ &SocketServ() { return appSocketServ_ ? *appSocketServ_ : socketServ_; } // Get RConnection instance, if any. RConnection *Connection() { return appConnection_; } // Convert TInetAddr to pj_sockaddr_in static inline pj_status_t Addr2pj(const TInetAddr & sym_addr, pj_sockaddr &pj_addr, int *addr_len, pj_bool_t convert_ipv4_mapped_addr = PJ_FALSE) { TUint fam = sym_addr.Family(); pj_bzero(&pj_addr, *addr_len); if (fam == PJ_AF_INET || (convert_ipv4_mapped_addr && fam == PJ_AF_INET6 && sym_addr.IsV4Mapped())) { pj_addr.addr.sa_family = PJ_AF_INET; PJ_ASSERT_RETURN(*addr_len>=(int)sizeof(pj_sockaddr_in), PJ_ETOOSMALL); pj_addr.ipv4.sin_addr.s_addr = pj_htonl(sym_addr.Address()); pj_addr.ipv4.sin_port = pj_htons((pj_uint16_t) sym_addr.Port()); *addr_len = sizeof(pj_sockaddr_in); } else if (fam == PJ_AF_INET6) { PJ_ASSERT_RETURN(*addr_len>=(int)sizeof(pj_sockaddr_in6), PJ_ETOOSMALL); const TIp6Addr & ip6 = sym_addr.Ip6Address(); pj_addr.addr.sa_family = PJ_AF_INET6; pj_memcpy(&pj_addr.ipv6.sin6_addr, ip6.u.iAddr8, 16); pj_addr.ipv6.sin6_port = pj_htons((pj_uint16_t) sym_addr.Port()); pj_addr.ipv6.sin6_scope_id = pj_htonl(sym_addr.Scope()); pj_addr.ipv6.sin6_flowinfo = pj_htonl(sym_addr.FlowLabel()); *addr_len = sizeof(pj_sockaddr_in6); } else { pj_assert(!"Unsupported address family"); return PJ_EAFNOTSUP; } return PJ_SUCCESS; } // Convert pj_sockaddr_in to TInetAddr static inline pj_status_t pj2Addr(const pj_sockaddr &pj_addr, int addrlen, TInetAddr & sym_addr) { if (pj_addr.addr.sa_family == PJ_AF_INET) { PJ_ASSERT_RETURN(addrlen >= (int)sizeof(pj_sockaddr_in), PJ_EINVAL); sym_addr.Init(KAfInet); sym_addr.SetAddress((TUint32)pj_ntohl(pj_addr.ipv4.sin_addr.s_addr)); sym_addr.SetPort(pj_ntohs(pj_addr.ipv4.sin_port)); } else if (pj_addr.addr.sa_family == PJ_AF_INET6) { TIp6Addr ip6; PJ_ASSERT_RETURN(addrlen>=(int)sizeof(pj_sockaddr_in6), PJ_EINVAL); pj_memcpy(ip6.u.iAddr8, &pj_addr.ipv6.sin6_addr, 16); sym_addr.Init(KAfInet6); sym_addr.SetAddress(ip6); sym_addr.SetScope(pj_ntohl(pj_addr.ipv6.sin6_scope_id)); sym_addr.SetFlowLabel(pj_ntohl(pj_addr.ipv6.sin6_flowinfo)); } else { pj_assert(!"Unsupported address family"); } return PJ_SUCCESS; } // // Resolver helper // // Get RHostResolver instance RHostResolver & GetResolver(int af) { if (af==PJ_AF_INET6) { return appHostResolver6_ ? *appHostResolver6_ : hostResolver6_; } else { return appHostResolver_ ? *appHostResolver_ : hostResolver_; } } // // Return true if the access point connection is up // bool IsConnectionUp() const { return isConnectionUp_; } // // Set access point connection status // void SetConnectionStatus(bool up) { isConnectionUp_ = up; } // // Unicode Converter // // Convert to Unicode TInt ConvertToUnicode(TDes16 &aUnicode, const TDesC8 &aForeign); // Convert from Unicode TInt ConvertFromUnicode(TDes8 &aForeign, const TDesC16 &aUnicode); // // Get console // // Get console CConsoleBase *Console() { return console_; } // // Get select() timeout timer. // CPjTimeoutTimer *SelectTimeoutTimer() { return selectTimeoutTimer_; } // // Wait for any active objects to run. // void WaitForActiveObjects(TInt aPriority = CActive::EPriorityStandard) { TInt aError; CActiveScheduler::Current()->WaitForAnyRequest(); CActiveScheduler::RunIfReady(aError, aPriority); } private: bool isConnectionUp_; bool isSocketServInitialized_; RSocketServ socketServ_; bool isResolverInitialized_; RHostResolver hostResolver_; RHostResolver hostResolver6_; CConsoleBase* console_; CPjTimeoutTimer *selectTimeoutTimer_; // App parameters RSocketServ *appSocketServ_; RConnection *appConnection_; RHostResolver *appHostResolver_; RHostResolver *appHostResolver6_; private: PjSymbianOS(); }; // This macro is used to check the access point connection status and return // failure if the AP connection is down or unusable. See the documentation // of pj_symbianos_set_connection_status() for more info #define PJ_SYMBIAN_CHECK_CONNECTION() \ PJ_SYMBIAN_CHECK_CONNECTION2(PJ_ECANCELLED) #define PJ_SYMBIAN_CHECK_CONNECTION2(retval) \ do { \ if (!PjSymbianOS::Instance()->IsConnectionUp()) \ return retval; \ } while (0); #endif /* __OS_SYMBIAN_H__ */ ================================================ FILE: deps/pjsip/pjlib/src/pj/os_time_bsd.c ================================================ /* $Id: os_time_bsd.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include /////////////////////////////////////////////////////////////////////////////// PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv) { struct timeb tb; PJ_CHECK_STACK(); ftime(&tb); tv->sec = tb.time; tv->msec = tb.millitm; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib/src/pj/os_time_common.c ================================================ /* $Id: os_time_common.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include /////////////////////////////////////////////////////////////////////////////// #if !defined(PJ_WIN32) || PJ_WIN32==0 PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt) { struct tm *local_time; PJ_CHECK_STACK(); local_time = localtime((time_t*)&tv->sec); pt->year = local_time->tm_year+1900; pt->mon = local_time->tm_mon; pt->day = local_time->tm_mday; pt->hour = local_time->tm_hour; pt->min = local_time->tm_min; pt->sec = local_time->tm_sec; pt->wday = local_time->tm_wday; pt->msec = tv->msec; return PJ_SUCCESS; } /** * Encode parsed time to time value. */ PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv) { struct tm local_time; local_time.tm_year = pt->year-1900; local_time.tm_mon = pt->mon; local_time.tm_mday = pt->day; local_time.tm_hour = pt->hour; local_time.tm_min = pt->min; local_time.tm_sec = pt->sec; local_time.tm_isdst = 0; tv->sec = mktime(&local_time); tv->msec = pt->msec; return PJ_SUCCESS; } #endif /* !PJ_WIN32 */ /** * Convert local time to GMT. */ PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv) { PJ_UNUSED_ARG(tv); return PJ_EBUG; } /** * Convert GMT to local time. */ PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv) { PJ_UNUSED_ARG(tv); return PJ_EBUG; } ================================================ FILE: deps/pjsip/pjlib/src/pj/os_time_linux_kernel.c ================================================ /* $Id: os_time_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include /////////////////////////////////////////////////////////////////////////////// PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv) { struct timeval tval; do_gettimeofday(&tval); tv->sec = tval.tv_sec; tv->msec = tval.tv_usec / 1000; return 0; } PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt) { pt->year = 2005; pt->mon = 8; pt->day = 20; pt->hour = 16; pt->min = 30; pt->sec = 30; pt->wday = 3; pt->yday = 200; pt->msec = 777; return -1; } /** * Encode parsed time to time value. */ PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv); /** * Convert local time to GMT. */ PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv); /** * Convert GMT to local time. */ PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv); ================================================ FILE: deps/pjsip/pjlib/src/pj/os_time_unix.c ================================================ /* $Id: os_time_unix.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #if defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H!=0 # include #endif #include /////////////////////////////////////////////////////////////////////////////// PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *p_tv) { struct timeval the_time; int rc; PJ_CHECK_STACK(); rc = gettimeofday(&the_time, NULL); if (rc != 0) return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); p_tv->sec = the_time.tv_sec; p_tv->msec = the_time.tv_usec / 1000; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib/src/pj/os_time_win32.c ================================================ /* $Id: os_time_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include /////////////////////////////////////////////////////////////////////////////// #define SECS_TO_FT_MULT 10000000 static LARGE_INTEGER base_time; #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE # define WINCE_TIME #endif #ifdef WINCE_TIME /* Note: * In Windows CE/Windows Mobile platforms, the availability of milliseconds * time resolution in SYSTEMTIME.wMilliseconds depends on the OEM, and most * likely it won't be available. When it's not available, the * SYSTEMTIME.wMilliseconds will contain a constant arbitrary value. * * Because of that, we need to emulate the milliseconds time resolution * using QueryPerformanceCounter() (via pj_get_timestamp() API). However * there is limitation on using this, i.e. the time returned by * pj_gettimeofday() may be off by up to plus/minus 999 msec (the second * part will be correct, however the msec part may be off), because we're * not synchronizing the msec field with the change of value of the "second" * field of the system time. * * Also there is other caveat which need to be handled (and they are * handled by this implementation): * - user may change system time, so pj_gettimeofday() needs to periodically * checks if system time has changed. The period on which system time is * checked is controlled by PJ_WINCE_TIME_CHECK_INTERVAL macro. */ static LARGE_INTEGER g_start_time; /* Time gettimeofday() is first called */ static pj_timestamp g_start_tick; /* TS gettimeofday() is first called */ static pj_timestamp g_last_update; /* Last time check_system_time() is called, to periodically synchronize with up-to-date system time (in case user changes system time). */ static pj_uint64_t g_update_period; /* Period (in TS) check_system_time() should be called. */ /* Period on which check_system_time() is called, in seconds */ #ifndef PJ_WINCE_TIME_CHECK_INTERVAL # define PJ_WINCE_TIME_CHECK_INTERVAL (10) #endif #endif #ifdef WINCE_TIME static pj_status_t init_start_time(void) { SYSTEMTIME st; FILETIME ft; pj_timestamp freq; pj_status_t status; GetLocalTime(&st); SystemTimeToFileTime(&st, &ft); g_start_time.LowPart = ft.dwLowDateTime; g_start_time.HighPart = ft.dwHighDateTime; g_start_time.QuadPart /= SECS_TO_FT_MULT; g_start_time.QuadPart -= base_time.QuadPart; status = pj_get_timestamp(&g_start_tick); if (status != PJ_SUCCESS) return status; g_last_update.u64 = g_start_tick.u64; status = pj_get_timestamp_freq(&freq); if (status != PJ_SUCCESS) return status; g_update_period = PJ_WINCE_TIME_CHECK_INTERVAL * freq.u64; PJ_LOG(4,("os_time_win32.c", "WinCE time (re)started")); return PJ_SUCCESS; } static pj_status_t check_system_time(pj_uint64_t ts_elapsed) { enum { MIS = 5 }; SYSTEMTIME st; FILETIME ft; LARGE_INTEGER cur, calc; DWORD diff; pj_timestamp freq; pj_status_t status; /* Get system's current time */ GetLocalTime(&st); SystemTimeToFileTime(&st, &ft); cur.LowPart = ft.dwLowDateTime; cur.HighPart = ft.dwHighDateTime; cur.QuadPart /= SECS_TO_FT_MULT; cur.QuadPart -= base_time.QuadPart; /* Get our calculated system time */ status = pj_get_timestamp_freq(&freq); if (status != PJ_SUCCESS) return status; calc.QuadPart = g_start_time.QuadPart + ts_elapsed / freq.u64; /* See the difference between calculated and actual system time */ if (calc.QuadPart >= cur.QuadPart) { diff = (DWORD)(calc.QuadPart - cur.QuadPart); } else { diff = (DWORD)(cur.QuadPart - calc.QuadPart); } if (diff > MIS) { /* System time has changed */ PJ_LOG(3,("os_time_win32.c", "WinCE system time changed detected " "(diff=%u)", diff)); status = init_start_time(); } else { status = PJ_SUCCESS; } return status; } #endif // Find 1st Jan 1970 as a FILETIME static pj_status_t get_base_time(void) { SYSTEMTIME st; FILETIME ft; pj_status_t status = PJ_SUCCESS; memset(&st,0,sizeof(st)); st.wYear=1970; st.wMonth=1; st.wDay=1; SystemTimeToFileTime(&st, &ft); base_time.LowPart = ft.dwLowDateTime; base_time.HighPart = ft.dwHighDateTime; base_time.QuadPart /= SECS_TO_FT_MULT; #ifdef WINCE_TIME pj_enter_critical_section(); status = init_start_time(); pj_leave_critical_section(); #endif return status; } PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv) { #ifdef WINCE_TIME pj_timestamp tick; pj_uint64_t msec_elapsed; #else SYSTEMTIME st; FILETIME ft; LARGE_INTEGER li; #endif pj_status_t status; if (base_time.QuadPart == 0) { status = get_base_time(); if (status != PJ_SUCCESS) return status; } #ifdef WINCE_TIME do { status = pj_get_timestamp(&tick); if (status != PJ_SUCCESS) return status; if (tick.u64 - g_last_update.u64 >= g_update_period) { pj_enter_critical_section(); if (tick.u64 - g_last_update.u64 >= g_update_period) { g_last_update.u64 = tick.u64; check_system_time(tick.u64 - g_start_tick.u64); } pj_leave_critical_section(); } else { break; } } while (1); msec_elapsed = pj_elapsed_msec64(&g_start_tick, &tick); tv->sec = (long)(g_start_time.QuadPart + msec_elapsed/1000); tv->msec = (long)(msec_elapsed % 1000); #else /* Standard Win32 GetLocalTime */ GetLocalTime(&st); SystemTimeToFileTime(&st, &ft); li.LowPart = ft.dwLowDateTime; li.HighPart = ft.dwHighDateTime; li.QuadPart /= SECS_TO_FT_MULT; li.QuadPart -= base_time.QuadPart; tv->sec = li.LowPart; tv->msec = st.wMilliseconds; #endif /* WINCE_TIME */ return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt) { LARGE_INTEGER li; FILETIME ft; SYSTEMTIME st; li.QuadPart = tv->sec; li.QuadPart += base_time.QuadPart; li.QuadPart *= SECS_TO_FT_MULT; ft.dwLowDateTime = li.LowPart; ft.dwHighDateTime = li.HighPart; FileTimeToSystemTime(&ft, &st); pt->year = st.wYear; pt->mon = st.wMonth-1; pt->day = st.wDay; pt->wday = st.wDayOfWeek; pt->hour = st.wHour; pt->min = st.wMinute; pt->sec = st.wSecond; pt->msec = tv->msec; return PJ_SUCCESS; } /** * Encode parsed time to time value. */ PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv) { SYSTEMTIME st; FILETIME ft; LARGE_INTEGER li; pj_bzero(&st, sizeof(st)); st.wYear = (pj_uint16_t) pt->year; st.wMonth = (pj_uint16_t) (pt->mon + 1); st.wDay = (pj_uint16_t) pt->day; st.wHour = (pj_uint16_t) pt->hour; st.wMinute = (pj_uint16_t) pt->min; st.wSecond = (pj_uint16_t) pt->sec; st.wMilliseconds = (pj_uint16_t) pt->msec; SystemTimeToFileTime(&st, &ft); li.LowPart = ft.dwLowDateTime; li.HighPart = ft.dwHighDateTime; li.QuadPart /= SECS_TO_FT_MULT; li.QuadPart -= base_time.QuadPart; tv->sec = li.LowPart; tv->msec = st.wMilliseconds; return PJ_SUCCESS; } /** * Convert local time to GMT. */ PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv); /** * Convert GMT to local time. */ PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv); ================================================ FILE: deps/pjsip/pjlib/src/pj/os_timestamp_common.c ================================================ /* $Id: os_timestamp_common.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 #define U32MAX (0xFFFFFFFFUL) #define NANOSEC (1000000000UL) #define USEC (1000000UL) #define MSEC (1000) #define u64tohighprec(u64) ((pj_highprec_t)((pj_int64_t)(u64))) static pj_highprec_t get_elapsed( const pj_timestamp *start, const pj_timestamp *stop ) { #if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0 return u64tohighprec(stop->u64 - start->u64); #else pj_highprec_t elapsed_hi, elapsed_lo; elapsed_hi = stop->u32.hi - start->u32.hi; elapsed_lo = stop->u32.lo - start->u32.lo; /* elapsed_hi = elapsed_hi * U32MAX */ pj_highprec_mul(elapsed_hi, U32MAX); return elapsed_hi + elapsed_lo; #endif } static pj_highprec_t elapsed_msec( const pj_timestamp *start, const pj_timestamp *stop ) { pj_timestamp ts_freq; pj_highprec_t freq, elapsed; if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS) return 0; /* Convert frequency timestamp */ #if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0 freq = u64tohighprec(ts_freq.u64); #else freq = ts_freq.u32.hi; pj_highprec_mul(freq, U32MAX); freq += ts_freq.u32.lo; #endif /* Avoid division by zero. */ if (freq == 0) freq = 1; /* Get elapsed time in cycles. */ elapsed = get_elapsed(start, stop); /* usec = elapsed * MSEC / freq */ pj_highprec_mul(elapsed, MSEC); pj_highprec_div(elapsed, freq); return elapsed; } static pj_highprec_t elapsed_usec( const pj_timestamp *start, const pj_timestamp *stop ) { pj_timestamp ts_freq; pj_highprec_t freq, elapsed; if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS) return 0; /* Convert frequency timestamp */ #if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0 freq = u64tohighprec(ts_freq.u64); #else freq = ts_freq.u32.hi; pj_highprec_mul(freq, U32MAX); freq += ts_freq.u32.lo; #endif /* Avoid division by zero. */ if (freq == 0) freq = 1; /* Get elapsed time in cycles. */ elapsed = get_elapsed(start, stop); /* usec = elapsed * USEC / freq */ pj_highprec_mul(elapsed, USEC); pj_highprec_div(elapsed, freq); return elapsed; } PJ_DEF(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start, const pj_timestamp *stop ) { pj_timestamp ts_freq; pj_highprec_t freq, elapsed; if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS) return 0; /* Convert frequency timestamp */ #if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0 freq = u64tohighprec(ts_freq.u64); #else freq = ts_freq.u32.hi; pj_highprec_mul(freq, U32MAX); freq += ts_freq.u32.lo; #endif /* Avoid division by zero. */ if (freq == 0) freq = 1; /* Get elapsed time in cycles. */ elapsed = get_elapsed(start, stop); /* usec = elapsed * USEC / freq */ pj_highprec_mul(elapsed, NANOSEC); pj_highprec_div(elapsed, freq); return (pj_uint32_t)elapsed; } PJ_DEF(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start, const pj_timestamp *stop ) { return (pj_uint32_t)elapsed_usec(start, stop); } PJ_DEF(pj_uint32_t) pj_elapsed_msec( const pj_timestamp *start, const pj_timestamp *stop ) { return (pj_uint32_t)elapsed_msec(start, stop); } PJ_DEF(pj_uint64_t) pj_elapsed_msec64(const pj_timestamp *start, const pj_timestamp *stop ) { return (pj_uint64_t)elapsed_msec(start, stop); } PJ_DEF(pj_time_val) pj_elapsed_time( const pj_timestamp *start, const pj_timestamp *stop ) { pj_highprec_t elapsed = elapsed_msec(start, stop); pj_time_val tv_elapsed; if (PJ_HIGHPREC_VALUE_IS_ZERO(elapsed)) { tv_elapsed.sec = tv_elapsed.msec = 0; return tv_elapsed; } else { pj_highprec_t sec, msec; sec = elapsed; pj_highprec_div(sec, MSEC); tv_elapsed.sec = (long)sec; msec = elapsed; pj_highprec_mod(msec, MSEC); tv_elapsed.msec = (long)msec; return tv_elapsed; } } PJ_DEF(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start, const pj_timestamp *stop ) { return stop->u32.lo - start->u32.lo; } PJ_DEF(pj_status_t) pj_gettickcount(pj_time_val *tv) { pj_timestamp ts, start; pj_status_t status; if ((status = pj_get_timestamp(&ts)) != PJ_SUCCESS) return status; pj_set_timestamp32(&start, 0, 0); *tv = pj_elapsed_time(&start, &ts); return PJ_SUCCESS; } #endif /* PJ_HAS_HIGH_RES_TIMER */ ================================================ FILE: deps/pjsip/pjlib/src/pj/os_timestamp_linux_kernel.c ================================================ /* $Id: os_timestamp_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #if 0 PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) { ts->u32.hi = 0; ts->u32.lo = jiffies; return 0; } PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) { freq->u32.hi = 0; freq->u32.lo = HZ; return 0; } #elif 0 PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) { struct timespec tv; tv = CURRENT_TIME; ts->u64 = tv.tv_sec; ts->u64 *= NSEC_PER_SEC; ts->u64 += tv.tv_nsec; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) { freq->u32.hi = 0; freq->u32.lo = NSEC_PER_SEC; return 0; } #else PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) { struct timeval tv; do_gettimeofday(&tv); ts->u64 = tv.tv_sec; ts->u64 *= USEC_PER_SEC; ts->u64 += tv.tv_usec; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) { freq->u32.hi = 0; freq->u32.lo = USEC_PER_SEC; return 0; } #endif ================================================ FILE: deps/pjsip/pjlib/src/pj/os_timestamp_posix.c ================================================ /* $Id: os_timestamp_posix.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #if defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0 # include # if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && \ defined(_POSIX_MONOTONIC_CLOCK) # define USE_POSIX_TIMERS 1 # endif #endif #if defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM!=0 && \ defined(PJ_TIMESTAMP_USE_RDTSC) && PJ_TIMESTAMP_USE_RDTSC!=0 && \ defined(PJ_M_I386) && PJ_M_I386!=0 && \ defined(PJ_LINUX) && PJ_LINUX!=0 static int machine_speed_mhz; static pj_timestamp machine_speed; static __inline__ unsigned long long int rdtsc() { unsigned long long int x; __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); return x; } /* Determine machine's CPU MHz to get the counter's frequency. */ static int get_machine_speed_mhz() { FILE *strm; char buf[512]; int len; char *pos, *end; PJ_CHECK_STACK(); /* Open /proc/cpuinfo and read the file */ strm = fopen("/proc/cpuinfo", "r"); if (!strm) return -1; len = fread(buf, 1, sizeof(buf), strm); fclose(strm); if (len < 1) { return -1; } buf[len] = '\0'; /* Locate the MHz digit. */ pos = strstr(buf, "cpu MHz"); if (!pos) return -1; pos = strchr(pos, ':'); if (!pos) return -1; end = (pos += 2); while (isdigit(*end)) ++end; *end = '\0'; /* Return the Mhz part, and give it a +1. */ return atoi(pos)+1; } PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) { if (machine_speed_mhz == 0) { machine_speed_mhz = get_machine_speed_mhz(); if (machine_speed_mhz > 0) { machine_speed.u64 = machine_speed_mhz * 1000000.0; } } if (machine_speed_mhz == -1) { ts->u64 = 0; return -1; } ts->u64 = rdtsc(); return 0; } PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) { if (machine_speed_mhz == 0) { machine_speed_mhz = get_machine_speed_mhz(); if (machine_speed_mhz > 0) { machine_speed.u64 = machine_speed_mhz * 1000000.0; } } if (machine_speed_mhz == -1) { freq->u64 = 1; /* return 1 to prevent division by zero in apps. */ return -1; } freq->u64 = machine_speed.u64; return 0; } #elif defined(PJ_DARWINOS) && PJ_DARWINOS != 0 #include #include #include #ifndef NSEC_PER_SEC # define NSEC_PER_SEC 1000000000 #endif PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) { mach_timespec_t tp; int ret; clock_serv_t serv; ret = host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &serv); if (ret != KERN_SUCCESS) { return PJ_RETURN_OS_ERROR(EINVAL); } ret = clock_get_time(serv, &tp); if (ret != KERN_SUCCESS) { return PJ_RETURN_OS_ERROR(EINVAL); } ts->u64 = tp.tv_sec; ts->u64 *= NSEC_PER_SEC; ts->u64 += tp.tv_nsec; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) { freq->u32.hi = 0; freq->u32.lo = NSEC_PER_SEC; return PJ_SUCCESS; } #elif defined(USE_POSIX_TIMERS) && USE_POSIX_TIMERS != 0 #include #include #include #define NSEC_PER_SEC 1000000000 PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) { struct timespec tp; if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) { return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); } ts->u64 = tp.tv_sec; ts->u64 *= NSEC_PER_SEC; ts->u64 += tp.tv_nsec; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) { freq->u32.hi = 0; freq->u32.lo = NSEC_PER_SEC; return PJ_SUCCESS; } #else #include #include #define USEC_PER_SEC 1000000 PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) { struct timeval tv; if (gettimeofday(&tv, NULL) != 0) { return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); } ts->u64 = tv.tv_sec; ts->u64 *= USEC_PER_SEC; ts->u64 += tv.tv_usec; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) { freq->u32.hi = 0; freq->u32.lo = USEC_PER_SEC; return PJ_SUCCESS; } #endif ================================================ FILE: deps/pjsip/pjlib/src/pj/os_timestamp_win32.c ================================================ /* $Id: os_timestamp_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #define THIS_FILE "os_timestamp_win32.c" #if 1 # define TRACE_(x) PJ_LOG(3,x) #else # define TRACE_(x) ; #endif ///////////////////////////////////////////////////////////////////////////// #if defined(PJ_TIMESTAMP_USE_RDTSC) && PJ_TIMESTAMP_USE_RDTSC!=0 && \ defined(PJ_M_I386) && PJ_M_I386 != 0 && \ defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM!=0 && \ defined(_MSC_VER) /* * Use rdtsc to get the OS timestamp. */ static LONG CpuMhz; static pj_int64_t CpuHz; static pj_status_t GetCpuHz(void) { HKEY key; LONG rc; DWORD size; #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, 0, &key); #else rc = RegOpenKey( HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", &key); #endif if (rc != ERROR_SUCCESS) return PJ_RETURN_OS_ERROR(rc); size = sizeof(CpuMhz); rc = RegQueryValueEx(key, "~MHz", NULL, NULL, (BYTE*)&CpuMhz, &size); RegCloseKey(key); if (rc != ERROR_SUCCESS) { return PJ_RETURN_OS_ERROR(rc); } CpuHz = CpuMhz; CpuHz = CpuHz * 1000000; return PJ_SUCCESS; } /* __int64 is nicely returned in EDX:EAX */ __declspec(naked) __int64 rdtsc() { __asm { RDTSC RET } } PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) { ts->u64 = rdtsc(); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) { pj_status_t status; if (CpuHz == 0) { status = GetCpuHz(); if (status != PJ_SUCCESS) return status; } freq->u64 = CpuHz; return PJ_SUCCESS; } ///////////////////////////////////////////////////////////////////////////// #elif defined(PJ_TIMESTAMP_WIN32_USE_SAFE_QPC) && \ PJ_TIMESTAMP_WIN32_USE_SAFE_QPC!=0 /* Use safe QueryPerformanceCounter. * This implementation has some protection against bug in KB Q274323: * Performance counter value may unexpectedly leap forward * http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323 * * THIS SHOULD NOT BE USED YET AS IT DOESN'T HANDLE SYSTEM TIME * CHANGE. */ static pj_timestamp g_ts_freq; static pj_timestamp g_ts_base; static pj_int64_t g_time_base; PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) { enum { MAX_RETRY = 10 }; unsigned i; /* pj_get_timestamp_freq() must have been called before. * This is done when application called pj_init(). */ pj_assert(g_ts_freq.u64 != 0); /* Retry QueryPerformanceCounter() until we're sure that the * value returned makes sense. */ i = 0; do { LARGE_INTEGER val; pj_int64_t counter64, time64, diff; pj_time_val time_now; /* Retrieve the counter */ if (!QueryPerformanceCounter(&val)) return PJ_RETURN_OS_ERROR(GetLastError()); /* Regardless of the goodness of the value, we should put * the counter here, because normally application wouldn't * check the error result of this function. */ ts->u64 = val.QuadPart; /* Retrieve time */ pj_gettimeofday(&time_now); /* Get the counter elapsed time in miliseconds */ counter64 = (val.QuadPart - g_ts_base.u64) * 1000 / g_ts_freq.u64; /* Get the time elapsed in miliseconds. * We don't want to use PJ_TIME_VAL_MSEC() since it's using * 32bit calculation, which limits the maximum elapsed time * to around 49 days only. */ time64 = time_now.sec; time64 = time64 * 1000 + time_now.msec; //time64 = GetTickCount(); /* It's good if the difference between two clocks are within * some compile time constant (default: 20ms, which to allow * context switch happen between QueryPerformanceCounter and * pj_gettimeofday()). */ diff = (time64 - g_time_base) - counter64; if (diff >= -20 && diff <= 20) { /* It's good */ return PJ_SUCCESS; } ++i; } while (i < MAX_RETRY); TRACE_((THIS_FILE, "QueryPerformanceCounter returned bad value")); return PJ_ETIMEDOUT; } static pj_status_t init_performance_counter(void) { LARGE_INTEGER val; pj_time_val time_base; pj_status_t status; /* Get the frequency */ if (!QueryPerformanceFrequency(&val)) return PJ_RETURN_OS_ERROR(GetLastError()); g_ts_freq.u64 = val.QuadPart; /* Get the base timestamp */ if (!QueryPerformanceCounter(&val)) return PJ_RETURN_OS_ERROR(GetLastError()); g_ts_base.u64 = val.QuadPart; /* Get the base time */ status = pj_gettimeofday(&time_base); if (status != PJ_SUCCESS) return status; /* Convert time base to 64bit value in msec */ g_time_base = time_base.sec; g_time_base = g_time_base * 1000 + time_base.msec; //g_time_base = GetTickCount(); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) { if (g_ts_freq.u64 == 0) { enum { MAX_REPEAT = 10 }; unsigned i; pj_status_t status; /* Make unellegant compiler happy */ status = 0; /* Repeat initializing performance counter until we're sure * the base timing is correct. It is possible that the system * returns bad counter during this initialization! */ for (i=0; iu64 = g_ts_freq.u64; return PJ_SUCCESS; } ///////////////////////////////////////////////////////////////////////////// #else /* * Use QueryPerformanceCounter and QueryPerformanceFrequency. * This should be the default implementation to be used on Windows. */ PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) { LARGE_INTEGER val; if (!QueryPerformanceCounter(&val)) return PJ_RETURN_OS_ERROR(GetLastError()); ts->u64 = val.QuadPart; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) { LARGE_INTEGER val; if (!QueryPerformanceFrequency(&val)) return PJ_RETURN_OS_ERROR(GetLastError()); freq->u64 = val.QuadPart; return PJ_SUCCESS; } #endif /* PJ_TIMESTAMP_USE_RDTSC */ ================================================ FILE: deps/pjsip/pjlib/src/pj/pool.c ================================================ /* $Id: pool.c 4298 2012-11-22 05:00:01Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #if !PJ_HAS_POOL_ALT_API /* Include inline definitions when inlining is disabled. */ #if !PJ_FUNCTIONS_ARE_INLINED # include #endif #define LOG(expr) PJ_LOG(6,expr) #define ALIGN_PTR(PTR,ALIGNMENT) (PTR + (-(pj_ssize_t)(PTR) & (ALIGNMENT-1))) PJ_DEF_DATA(int) PJ_NO_MEMORY_EXCEPTION; PJ_DEF(int) pj_NO_MEMORY_EXCEPTION() { return PJ_NO_MEMORY_EXCEPTION; } /* * Create new block. * Create a new big chunk of memory block, from which user allocation will be * taken from. */ static pj_pool_block *pj_pool_create_block( pj_pool_t *pool, pj_size_t size) { pj_pool_block *block; PJ_CHECK_STACK(); pj_assert(size >= sizeof(pj_pool_block)); LOG((pool->obj_name, "create_block(sz=%u), cur.cap=%u, cur.used=%u", size, pool->capacity, pj_pool_get_used_size(pool))); /* Request memory from allocator. */ block = (pj_pool_block*) (*pool->factory->policy.block_alloc)(pool->factory, size); if (block == NULL) { (*pool->callback)(pool, size); return NULL; } /* Add capacity. */ pool->capacity += size; /* Set start and end of buffer. */ block->buf = ((unsigned char*)block) + sizeof(pj_pool_block); block->end = ((unsigned char*)block) + size; /* Set the start pointer, aligning it as needed */ block->cur = ALIGN_PTR(block->buf, PJ_POOL_ALIGNMENT); /* Insert in the front of the list. */ pj_list_insert_after(&pool->block_list, block); LOG((pool->obj_name," block created, buffer=%p-%p",block->buf, block->end)); return block; } /* * Allocate memory chunk for user from available blocks. * This will iterate through block list to find space to allocate the chunk. * If no space is available in all the blocks, a new block might be created * (depending on whether the pool is allowed to resize). */ PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, pj_size_t size) { pj_pool_block *block = pool->block_list.next; void *p; pj_size_t block_size; PJ_CHECK_STACK(); while (block != &pool->block_list) { p = pj_pool_alloc_from_block(block, size); if (p != NULL) return p; block = block->next; } /* No available space in all blocks. */ /* If pool is configured NOT to expand, return error. */ if (pool->increment_size == 0) { LOG((pool->obj_name, "Can't expand pool to allocate %u bytes " "(used=%u, cap=%u)", size, pj_pool_get_used_size(pool), pool->capacity)); (*pool->callback)(pool, size); return NULL; } /* If pool is configured to expand, but the increment size * is less than the required size, expand the pool by multiple * increment size. Also count the size wasted due to aligning * the block. */ if (pool->increment_size < size + sizeof(pj_pool_block) + PJ_POOL_ALIGNMENT) { pj_size_t count; count = (size + pool->increment_size + sizeof(pj_pool_block) + PJ_POOL_ALIGNMENT) / pool->increment_size; block_size = count * pool->increment_size; } else { block_size = pool->increment_size; } LOG((pool->obj_name, "%u bytes requested, resizing pool by %u bytes (used=%u, cap=%u)", size, block_size, pj_pool_get_used_size(pool), pool->capacity)); block = pj_pool_create_block(pool, block_size); if (!block) return NULL; p = pj_pool_alloc_from_block(block, size); pj_assert(p != NULL); #if PJ_DEBUG if (p == NULL) { PJ_UNUSED_ARG(p); } #endif return p; } /* * Internal function to initialize pool. */ PJ_DEF(void) pj_pool_init_int( pj_pool_t *pool, const char *name, pj_size_t increment_size, pj_pool_callback *callback) { PJ_CHECK_STACK(); pool->increment_size = increment_size; pool->callback = callback; if (name) { if (strchr(name, '%') != NULL) { pj_ansi_snprintf(pool->obj_name, sizeof(pool->obj_name), name, pool); } else { pj_ansi_strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME); pool->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; } } else { pool->obj_name[0] = '\0'; } } /* * Create new memory pool. */ PJ_DEF(pj_pool_t*) pj_pool_create_int( pj_pool_factory *f, const char *name, pj_size_t initial_size, pj_size_t increment_size, pj_pool_callback *callback) { pj_pool_t *pool; pj_pool_block *block; pj_uint8_t *buffer; PJ_CHECK_STACK(); /* Size must be at least sizeof(pj_pool)+sizeof(pj_pool_block) */ PJ_ASSERT_RETURN(initial_size >= sizeof(pj_pool_t)+sizeof(pj_pool_block), NULL); /* If callback is NULL, set calback from the policy */ if (callback == NULL) callback = f->policy.callback; /* Allocate initial block */ buffer = (pj_uint8_t*) (*f->policy.block_alloc)(f, initial_size); if (!buffer) return NULL; /* Set pool administrative data. */ pool = (pj_pool_t*)buffer; pj_bzero(pool, sizeof(*pool)); pj_list_init(&pool->block_list); pool->factory = f; /* Create the first block from the memory. */ block = (pj_pool_block*) (buffer + sizeof(*pool)); block->buf = ((unsigned char*)block) + sizeof(pj_pool_block); block->end = buffer + initial_size; /* Set the start pointer, aligning it as needed */ block->cur = ALIGN_PTR(block->buf, PJ_POOL_ALIGNMENT); pj_list_insert_after(&pool->block_list, block); pj_pool_init_int(pool, name, increment_size, callback); /* Pool initial capacity and used size */ pool->capacity = initial_size; LOG((pool->obj_name, "pool created, size=%u", pool->capacity)); return pool; } /* * Reset the pool to the state when it was created. * All blocks will be deallocated except the first block. All memory areas * are marked as free. */ static void reset_pool(pj_pool_t *pool) { pj_pool_block *block; PJ_CHECK_STACK(); block = pool->block_list.prev; if (block == &pool->block_list) return; /* Skip the first block because it is occupying the same memory as the pool itself. */ block = block->prev; while (block != &pool->block_list) { pj_pool_block *prev = block->prev; pj_list_erase(block); (*pool->factory->policy.block_free)(pool->factory, block, block->end - (unsigned char*)block); block = prev; } block = pool->block_list.next; /* Set the start pointer, aligning it as needed */ block->cur = ALIGN_PTR(block->buf, PJ_POOL_ALIGNMENT); pool->capacity = block->end - (unsigned char*)pool; } /* * The public function to reset pool. */ PJ_DEF(void) pj_pool_reset(pj_pool_t *pool) { LOG((pool->obj_name, "reset(): cap=%d, used=%d(%d%%)", pool->capacity, pj_pool_get_used_size(pool), pj_pool_get_used_size(pool)*100/pool->capacity)); reset_pool(pool); } /* * Destroy the pool. */ PJ_DEF(void) pj_pool_destroy_int(pj_pool_t *pool) { pj_size_t initial_size; LOG((pool->obj_name, "destroy(): cap=%d, used=%d(%d%%), block0=%p-%p", pool->capacity, pj_pool_get_used_size(pool), pj_pool_get_used_size(pool)*100/pool->capacity, ((pj_pool_block*)pool->block_list.next)->buf, ((pj_pool_block*)pool->block_list.next)->end)); reset_pool(pool); initial_size = ((pj_pool_block*)pool->block_list.next)->end - (unsigned char*)pool; if (pool->factory->policy.block_free) (*pool->factory->policy.block_free)(pool->factory, pool, initial_size); } #endif /* PJ_HAS_POOL_ALT_API */ ================================================ FILE: deps/pjsip/pjlib/src/pj/pool_buf.c ================================================ /* $Id: pool_buf.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include static struct pj_pool_factory stack_based_factory; struct creation_param { void *stack_buf; pj_size_t size; }; static int is_initialized; static long tls = -1; static void* stack_alloc(pj_pool_factory *factory, pj_size_t size); static void pool_buf_cleanup(void) { if (tls != -1) { pj_thread_local_free(tls); tls = -1; } if (is_initialized) is_initialized = 0; } static pj_status_t pool_buf_initialize(void) { pj_atexit(&pool_buf_cleanup); stack_based_factory.policy.block_alloc = &stack_alloc; return pj_thread_local_alloc(&tls); } static void* stack_alloc(pj_pool_factory *factory, pj_size_t size) { struct creation_param *param; void *buf; PJ_UNUSED_ARG(factory); param = (struct creation_param*) pj_thread_local_get(tls); if (param == NULL) { /* Don't assert(), this is normal no-memory situation */ return NULL; } pj_thread_local_set(tls, NULL); PJ_ASSERT_RETURN(size <= param->size, NULL); buf = param->stack_buf; /* Prevent the buffer from being reused */ param->stack_buf = NULL; return buf; } PJ_DEF(pj_pool_t*) pj_pool_create_on_buf(const char *name, void *buf, pj_size_t size) { #if PJ_HAS_POOL_ALT_API == 0 struct creation_param param; pj_size_t align_diff; PJ_ASSERT_RETURN(buf && size, NULL); if (!is_initialized) { if (pool_buf_initialize() != PJ_SUCCESS) return NULL; is_initialized = 1; } /* Check and align buffer */ align_diff = (pj_size_t)buf; if (align_diff & (PJ_POOL_ALIGNMENT-1)) { align_diff &= (PJ_POOL_ALIGNMENT-1); buf = (void*) (((char*)buf) + align_diff); size -= align_diff; } param.stack_buf = buf; param.size = size; pj_thread_local_set(tls, ¶m); return pj_pool_create_int(&stack_based_factory, name, size, 0, pj_pool_factory_default_policy.callback); #else PJ_UNUSED_ARG(buf); return pj_pool_create(NULL, name, size, size, NULL); #endif } ================================================ FILE: deps/pjsip/pjlib/src/pj/pool_caching.c ================================================ /* $Id: pool_caching.c 4298 2012-11-22 05:00:01Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #if !PJ_HAS_POOL_ALT_API static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, const char *name, pj_size_t initial_size, pj_size_t increment_sz, pj_pool_callback *callback); static void cpool_release_pool(pj_pool_factory *pf, pj_pool_t *pool); static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail ); static pj_bool_t cpool_on_block_alloc(pj_pool_factory *f, pj_size_t sz); static void cpool_on_block_free(pj_pool_factory *f, pj_size_t sz); static pj_size_t pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE] = { 256, 512, 1024, 2048, 4096, 8192, 12288, 16384, 20480, 24576, 28672, 32768, 40960, 49152, 57344, 65536 }; /* Index where the search for size should begin. * Start with pool_sizes[5], which is 8192. */ #define START_SIZE 5 PJ_DEF(void) pj_caching_pool_init( pj_caching_pool *cp, const pj_pool_factory_policy *policy, pj_size_t max_capacity) { int i; pj_pool_t *pool; PJ_CHECK_STACK(); pj_bzero(cp, sizeof(*cp)); cp->max_capacity = max_capacity; pj_list_init(&cp->used_list); for (i=0; ifree_list[i]); if (policy == NULL) { policy = &pj_pool_factory_default_policy; } pj_memcpy(&cp->factory.policy, policy, sizeof(pj_pool_factory_policy)); cp->factory.create_pool = &cpool_create_pool; cp->factory.release_pool = &cpool_release_pool; cp->factory.dump_status = &cpool_dump_status; cp->factory.on_block_alloc = &cpool_on_block_alloc; cp->factory.on_block_free = &cpool_on_block_free; pool = pj_pool_create_on_buf("cachingpool", cp->pool_buf, sizeof(cp->pool_buf)); pj_lock_create_simple_mutex(pool, "cachingpool", &cp->lock); } PJ_DEF(void) pj_caching_pool_destroy( pj_caching_pool *cp ) { int i; pj_pool_t *pool; PJ_CHECK_STACK(); /* Delete all pool in free list */ for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE; ++i) { pj_pool_t *next; pool = (pj_pool_t*) cp->free_list[i].next; for (; pool != (void*)&cp->free_list[i]; pool = next) { next = pool->next; pj_list_erase(pool); pj_pool_destroy_int(pool); } } /* Delete all pools in used list */ pool = (pj_pool_t*) cp->used_list.next; while (pool != (pj_pool_t*) &cp->used_list) { pj_pool_t *next = pool->next; pj_list_erase(pool); PJ_LOG(4,(pool->obj_name, "Pool is not released by application, releasing now")); pj_pool_destroy_int(pool); pool = next; } if (cp->lock) { pj_lock_destroy(cp->lock); pj_lock_create_null_mutex(NULL, "cachingpool", &cp->lock); } } static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, const char *name, pj_size_t initial_size, pj_size_t increment_sz, pj_pool_callback *callback) { pj_caching_pool *cp = (pj_caching_pool*)pf; pj_pool_t *pool; int idx; PJ_CHECK_STACK(); pj_lock_acquire(cp->lock); /* Use pool factory's policy when callback is NULL */ if (callback == NULL) { callback = pf->policy.callback; } /* Search the suitable size for the pool. * We'll just do linear search to the size array, as the array size itself * is only a few elements. Binary search I suspect will be less efficient * for this purpose. */ if (initial_size <= pool_sizes[START_SIZE]) { for (idx=START_SIZE-1; idx >= 0 && pool_sizes[idx] >= initial_size; --idx) ; ++idx; } else { for (idx=START_SIZE+1; idx < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[idx] < initial_size; ++idx) ; } /* Check whether there's a pool in the list. */ if (idx==PJ_CACHING_POOL_ARRAY_SIZE || pj_list_empty(&cp->free_list[idx])) { /* No pool is available. */ /* Set minimum size. */ if (idx < PJ_CACHING_POOL_ARRAY_SIZE) initial_size = pool_sizes[idx]; /* Create new pool */ pool = pj_pool_create_int(&cp->factory, name, initial_size, increment_sz, callback); if (!pool) { pj_lock_release(cp->lock); return NULL; } } else { /* Get one pool from the list. */ pool = (pj_pool_t*) cp->free_list[idx].next; pj_list_erase(pool); /* Initialize the pool. */ pj_pool_init_int(pool, name, increment_sz, callback); /* Update pool manager's free capacity. */ if (cp->capacity > pj_pool_get_capacity(pool)) { cp->capacity -= pj_pool_get_capacity(pool); } else { cp->capacity = 0; } PJ_LOG(6, (pool->obj_name, "pool reused, size=%u", pool->capacity)); } /* Put in used list. */ pj_list_insert_before( &cp->used_list, pool ); /* Mark factory data */ pool->factory_data = (void*) (pj_ssize_t) idx; /* Increment used count. */ ++cp->used_count; pj_lock_release(cp->lock); return pool; } static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool) { pj_caching_pool *cp = (pj_caching_pool*)pf; pj_size_t pool_capacity; unsigned i; PJ_CHECK_STACK(); PJ_ASSERT_ON_FAIL(pf && pool, return); pj_lock_acquire(cp->lock); #if PJ_SAFE_POOL /* Make sure pool is still in our used list */ if (pj_list_find_node(&cp->used_list, pool) != pool) { pj_assert(!"Attempt to destroy pool that has been destroyed before"); return; } #endif /* Erase from the used list. */ pj_list_erase(pool); /* Decrement used count. */ --cp->used_count; pool_capacity = pj_pool_get_capacity(pool); /* Destroy the pool if the size is greater than our size or if the total * capacity in our recycle list (plus the size of the pool) exceeds * maximum capacity. . */ if (pool_capacity > pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE-1] || cp->capacity + pool_capacity > cp->max_capacity) { pj_pool_destroy_int(pool); pj_lock_release(cp->lock); return; } /* Reset pool. */ PJ_LOG(6, (pool->obj_name, "recycle(): cap=%d, used=%d(%d%%)", pool_capacity, pj_pool_get_used_size(pool), pj_pool_get_used_size(pool)*100/pool_capacity)); pj_pool_reset(pool); pool_capacity = pj_pool_get_capacity(pool); /* * Otherwise put the pool in our recycle list. */ i = (unsigned) (unsigned long) (pj_ssize_t) pool->factory_data; pj_assert(i= PJ_CACHING_POOL_ARRAY_SIZE ) { /* Something has gone wrong with the pool. */ pj_pool_destroy_int(pool); pj_lock_release(cp->lock); return; } pj_list_insert_after(&cp->free_list[i], pool); cp->capacity += pool_capacity; pj_lock_release(cp->lock); } static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail ) { #if PJ_LOG_MAX_LEVEL >= 3 pj_caching_pool *cp = (pj_caching_pool*)factory; pj_lock_acquire(cp->lock); PJ_LOG(3,("cachpool", " Dumping caching pool:")); PJ_LOG(3,("cachpool", " Capacity=%u, max_capacity=%u, used_cnt=%u", \ cp->capacity, cp->max_capacity, cp->used_count)); if (detail) { pj_pool_t *pool = (pj_pool_t*) cp->used_list.next; pj_size_t total_used = 0, total_capacity = 0; PJ_LOG(3,("cachpool", " Dumping all active pools:")); while (pool != (void*)&cp->used_list) { pj_size_t pool_capacity = pj_pool_get_capacity(pool); PJ_LOG(3,("cachpool", " %16s: %8d of %8d (%d%%) used", pj_pool_getobjname(pool), pj_pool_get_used_size(pool), pool_capacity, pj_pool_get_used_size(pool)*100/pool_capacity)); total_used += pj_pool_get_used_size(pool); total_capacity += pool_capacity; pool = pool->next; } if (total_capacity) { PJ_LOG(3,("cachpool", " Total %9d of %9d (%d %%) used!", total_used, total_capacity, total_used * 100 / total_capacity)); } } pj_lock_release(cp->lock); #else PJ_UNUSED_ARG(factory); PJ_UNUSED_ARG(detail); #endif } static pj_bool_t cpool_on_block_alloc(pj_pool_factory *f, pj_size_t sz) { pj_caching_pool *cp = (pj_caching_pool*)f; //Can't lock because mutex is not recursive //if (cp->mutex) pj_mutex_lock(cp->mutex); cp->used_size += sz; if (cp->used_size > cp->peak_used_size) cp->peak_used_size = cp->used_size; //if (cp->mutex) pj_mutex_unlock(cp->mutex); return PJ_TRUE; } static void cpool_on_block_free(pj_pool_factory *f, pj_size_t sz) { pj_caching_pool *cp = (pj_caching_pool*)f; //pj_mutex_lock(cp->mutex); cp->used_size -= sz; //pj_mutex_unlock(cp->mutex); } #endif /* PJ_HAS_POOL_ALT_API */ ================================================ FILE: deps/pjsip/pjlib/src/pj/pool_dbg.c ================================================ /* $Id: pool_dbg.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #if PJ_HAS_POOL_ALT_API #if PJ_HAS_MALLOC_H # include #endif #if PJ_HAS_STDLIB_H # include #endif #if ((defined(PJ_WIN32) && PJ_WIN32!=0) || \ (defined(PJ_WIN64) && PJ_WIN64 != 0)) && \ defined(PJ_DEBUG) && PJ_DEBUG!=0 && !PJ_NATIVE_STRING_IS_UNICODE # include # define TRACE_(msg) OutputDebugString(msg) #endif /* Uncomment this to enable TRACE_ */ //#undef TRACE_ int PJ_NO_MEMORY_EXCEPTION; PJ_DEF(int) pj_NO_MEMORY_EXCEPTION() { return PJ_NO_MEMORY_EXCEPTION; } /* Create pool */ PJ_DEF(pj_pool_t*) pj_pool_create_imp( const char *file, int line, void *factory, const char *name, pj_size_t initial_size, pj_size_t increment_size, pj_pool_callback *callback) { pj_pool_t *pool; PJ_UNUSED_ARG(file); PJ_UNUSED_ARG(line); PJ_UNUSED_ARG(factory); PJ_UNUSED_ARG(initial_size); PJ_UNUSED_ARG(increment_size); pool = malloc(sizeof(struct pj_pool_t)); if (!pool) return NULL; if (name) { pj_ansi_strncpy(pool->obj_name, name, sizeof(pool->obj_name)); pool->obj_name[sizeof(pool->obj_name)-1] = '\0'; } else { strcpy(pool->obj_name, "altpool"); } pool->factory = NULL; pool->first_mem = NULL; pool->used_size = 0; pool->cb = callback; return pool; } /* Release pool */ PJ_DEF(void) pj_pool_release_imp(pj_pool_t *pool) { pj_pool_reset(pool); free(pool); } /* Get pool name */ PJ_DEF(const char*) pj_pool_getobjname_imp(pj_pool_t *pool) { PJ_UNUSED_ARG(pool); return "pooldbg"; } /* Reset pool */ PJ_DEF(void) pj_pool_reset_imp(pj_pool_t *pool) { struct pj_pool_mem *mem; mem = pool->first_mem; while (mem) { struct pj_pool_mem *next = mem->next; free(mem); mem = next; } pool->first_mem = NULL; } /* Get capacity */ PJ_DEF(pj_size_t) pj_pool_get_capacity_imp(pj_pool_t *pool) { PJ_UNUSED_ARG(pool); /* Unlimited capacity */ return 0x7FFFFFFFUL; } /* Get total used size */ PJ_DEF(pj_size_t) pj_pool_get_used_size_imp(pj_pool_t *pool) { return pool->used_size; } /* Allocate memory from the pool */ PJ_DEF(void*) pj_pool_alloc_imp( const char *file, int line, pj_pool_t *pool, pj_size_t sz) { struct pj_pool_mem *mem; PJ_UNUSED_ARG(file); PJ_UNUSED_ARG(line); mem = malloc(sz + sizeof(struct pj_pool_mem)); if (!mem) { if (pool->cb) (*pool->cb)(pool, sz); return NULL; } mem->next = pool->first_mem; pool->first_mem = mem; #ifdef TRACE_ { char msg[120]; pj_ansi_sprintf(msg, "Mem %X (%d+%d bytes) allocated by %s:%d\r\n", mem, sz, sizeof(struct pj_pool_mem), file, line); TRACE_(msg); } #endif return ((char*)mem) + sizeof(struct pj_pool_mem); } /* Allocate memory from the pool and zero the memory */ PJ_DEF(void*) pj_pool_calloc_imp( const char *file, int line, pj_pool_t *pool, unsigned cnt, unsigned elemsz) { void *mem; mem = pj_pool_alloc_imp(file, line, pool, cnt*elemsz); if (!mem) return NULL; pj_bzero(mem, cnt*elemsz); return mem; } /* Allocate memory from the pool and zero the memory */ PJ_DEF(void*) pj_pool_zalloc_imp( const char *file, int line, pj_pool_t *pool, pj_size_t sz) { return pj_pool_calloc_imp(file, line, pool, 1, sz); } #endif /* PJ_HAS_POOL_ALT_API */ ================================================ FILE: deps/pjsip/pjlib/src/pj/pool_policy_kmalloc.c ================================================ /* $Id: pool_policy_kmalloc.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size) { PJ_CHECK_STACK(); PJ_UNUSED_ARG(factory); return kmalloc(size, GFP_ATOMIC); } static void default_block_free(pj_pool_factory *factory, void *mem, pj_size_t size) { PJ_CHECK_STACK(); PJ_UNUSED_ARG(factory); PJ_UNUSED_ARG(size); kfree(mem); } static void default_pool_callback(pj_pool_t *pool, pj_size_t size) { PJ_CHECK_STACK(); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(size); PJ_THROW(PJ_NO_MEMORY_EXCEPTION); } pj_pool_factory_policy pj_pool_factory_default_policy = { &default_block_alloc, &default_block_free, &default_pool_callback, 0 }; PJ_DEF(const pj_pool_factory_policy*) pj_pool_factory_get_default_policy(void) { return &pj_pool_factory_default_policy; } ================================================ FILE: deps/pjsip/pjlib/src/pj/pool_policy_malloc.c ================================================ /* $Id: pool_policy_malloc.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #if !PJ_HAS_POOL_ALT_API /* * This file contains pool default policy definition and implementation. */ #include "pool_signature.h" static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size) { void *p; PJ_CHECK_STACK(); if (factory->on_block_alloc) { int rc; rc = factory->on_block_alloc(factory, size); if (!rc) return NULL; } p = malloc(size+(SIG_SIZE << 1)); if (p == NULL) { if (factory->on_block_free) factory->on_block_free(factory, size); } else { /* Apply signature when PJ_SAFE_POOL is set. It will move * "p" pointer forward. */ APPLY_SIG(p, size); } return p; } static void default_block_free(pj_pool_factory *factory, void *mem, pj_size_t size) { PJ_CHECK_STACK(); if (factory->on_block_free) factory->on_block_free(factory, size); /* Check and remove signature when PJ_SAFE_POOL is set. It will * move "mem" pointer backward. */ REMOVE_SIG(mem, size); /* Note that when PJ_SAFE_POOL is set, the actual size of the block * is size + SIG_SIZE*2. */ free(mem); } static void default_pool_callback(pj_pool_t *pool, pj_size_t size) { PJ_CHECK_STACK(); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(size); PJ_THROW(PJ_NO_MEMORY_EXCEPTION); } PJ_DEF_DATA(pj_pool_factory_policy) pj_pool_factory_default_policy = { &default_block_alloc, &default_block_free, &default_pool_callback, 0 }; PJ_DEF(const pj_pool_factory_policy*) pj_pool_factory_get_default_policy(void) { return &pj_pool_factory_default_policy; } #endif /* PJ_HAS_POOL_ALT_API */ ================================================ FILE: deps/pjsip/pjlib/src/pj/pool_policy_new.cpp ================================================ /* $Id: pool_policy_new.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #if !PJ_HAS_POOL_ALT_API /* * This file contains pool default policy definition and implementation. */ #include "pool_signature.h" static void *operator_new(pj_pool_factory *factory, pj_size_t size) { void *mem; PJ_CHECK_STACK(); if (factory->on_block_alloc) { int rc; rc = factory->on_block_alloc(factory, size); if (!rc) return NULL; } mem = (void*) new char[size+(SIG_SIZE << 1)]; /* Exception for new operator may be disabled, so.. */ if (mem) { /* Apply signature when PJ_SAFE_POOL is set. It will move * "mem" pointer forward. */ APPLY_SIG(mem, size); } return mem; } static void operator_delete(pj_pool_factory *factory, void *mem, pj_size_t size) { PJ_CHECK_STACK(); if (factory->on_block_free) factory->on_block_free(factory, size); /* Check and remove signature when PJ_SAFE_POOL is set. It will * move "mem" pointer backward. */ REMOVE_SIG(mem, size); /* Note that when PJ_SAFE_POOL is set, the actual size of the block * is size + SIG_SIZE*2. */ char *p = (char*)mem; delete [] p; } static void default_pool_callback(pj_pool_t *pool, pj_size_t size) { PJ_CHECK_STACK(); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(size); PJ_THROW(PJ_NO_MEMORY_EXCEPTION); } PJ_DEF_DATA(pj_pool_factory_policy) pj_pool_factory_default_policy = { &operator_new, &operator_delete, &default_pool_callback, 0 }; PJ_DEF(const pj_pool_factory_policy*) pj_pool_factory_get_default_policy(void) { return &pj_pool_factory_default_policy; } #endif /* PJ_HAS_POOL_ALT_API */ ================================================ FILE: deps/pjsip/pjlib/src/pj/pool_signature.h ================================================ /* $Id: pool_signature.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #if PJ_SAFE_POOL # define SIG_SIZE sizeof(pj_uint32_t) static void apply_signature(void *p, pj_size_t size); static void check_pool_signature(void *p, pj_size_t size); # define APPLY_SIG(p,sz) apply_signature(p,sz), \ p=(void*)(((char*)p)+SIG_SIZE) # define REMOVE_SIG(p,sz) check_pool_signature(p,sz), \ p=(void*)(((char*)p)-SIG_SIZE) # define SIG_BEGIN 0x600DC0DE # define SIG_END 0x0BADC0DE static void apply_signature(void *p, pj_size_t size) { pj_uint32_t sig; sig = SIG_BEGIN; pj_memcpy(p, &sig, SIG_SIZE); sig = SIG_END; pj_memcpy(((char*)p)+SIG_SIZE+size, &sig, SIG_SIZE); } static void check_pool_signature(void *p, pj_size_t size) { pj_uint32_t sig; pj_uint8_t *mem = (pj_uint8_t*)p; /* Check that signature at the start of the block is still intact */ sig = SIG_BEGIN; pj_assert(!pj_memcmp(mem-SIG_SIZE, &sig, SIG_SIZE)); /* Check that signature at the end of the block is still intact. * Note that "mem" has been incremented by SIG_SIZE */ sig = SIG_END; pj_assert(!pj_memcmp(mem+size, &sig, SIG_SIZE)); } #else # define SIG_SIZE 0 # define APPLY_SIG(p,sz) # define REMOVE_SIG(p,sz) #endif ================================================ FILE: deps/pjsip/pjlib/src/pj/rand.c ================================================ /* $Id: rand.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include PJ_DEF(void) pj_srand(unsigned int seed) { PJ_CHECK_STACK(); platform_srand(seed); } PJ_DEF(int) pj_rand(void) { PJ_CHECK_STACK(); return platform_rand(); } ================================================ FILE: deps/pjsip/pjlib/src/pj/rbtree.c ================================================ /* $Id: rbtree.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include static void left_rotate( pj_rbtree *tree, pj_rbtree_node *node ) { pj_rbtree_node *rnode, *parent; PJ_CHECK_STACK(); rnode = node->right; if (rnode == tree->null) return; node->right = rnode->left; if (rnode->left != tree->null) rnode->left->parent = node; parent = node->parent; rnode->parent = parent; if (parent != tree->null) { if (parent->left == node) parent->left = rnode; else parent->right = rnode; } else { tree->root = rnode; } rnode->left = node; node->parent = rnode; } static void right_rotate( pj_rbtree *tree, pj_rbtree_node *node ) { pj_rbtree_node *lnode, *parent; PJ_CHECK_STACK(); lnode = node->left; if (lnode == tree->null) return; node->left = lnode->right; if (lnode->right != tree->null) lnode->right->parent = node; parent = node->parent; lnode->parent = parent; if (parent != tree->null) { if (parent->left == node) parent->left = lnode; else parent->right = lnode; } else { tree->root = lnode; } lnode->right = node; node->parent = lnode; } static void insert_fixup( pj_rbtree *tree, pj_rbtree_node *node ) { pj_rbtree_node *temp, *parent; PJ_CHECK_STACK(); while (node != tree->root && node->parent->color == PJ_RBCOLOR_RED) { parent = node->parent; if (parent == parent->parent->left) { temp = parent->parent->right; if (temp->color == PJ_RBCOLOR_RED) { temp->color = PJ_RBCOLOR_BLACK; node = parent; node->color = PJ_RBCOLOR_BLACK; node = node->parent; node->color = PJ_RBCOLOR_RED; } else { if (node == parent->right) { node = parent; left_rotate(tree, node); } temp = node->parent; temp->color = PJ_RBCOLOR_BLACK; temp = temp->parent; temp->color = PJ_RBCOLOR_RED; right_rotate( tree, temp); } } else { temp = parent->parent->left; if (temp->color == PJ_RBCOLOR_RED) { temp->color = PJ_RBCOLOR_BLACK; node = parent; node->color = PJ_RBCOLOR_BLACK; node = node->parent; node->color = PJ_RBCOLOR_RED; } else { if (node == parent->left) { node = parent; right_rotate(tree, node); } temp = node->parent; temp->color = PJ_RBCOLOR_BLACK; temp = temp->parent; temp->color = PJ_RBCOLOR_RED; left_rotate(tree, temp); } } } tree->root->color = PJ_RBCOLOR_BLACK; } static void delete_fixup( pj_rbtree *tree, pj_rbtree_node *node ) { pj_rbtree_node *temp; PJ_CHECK_STACK(); while (node != tree->root && node->color == PJ_RBCOLOR_BLACK) { if (node->parent->left == node) { temp = node->parent->right; if (temp->color == PJ_RBCOLOR_RED) { temp->color = PJ_RBCOLOR_BLACK; node->parent->color = PJ_RBCOLOR_RED; left_rotate(tree, node->parent); temp = node->parent->right; } if (temp->left->color == PJ_RBCOLOR_BLACK && temp->right->color == PJ_RBCOLOR_BLACK) { temp->color = PJ_RBCOLOR_RED; node = node->parent; } else { if (temp->right->color == PJ_RBCOLOR_BLACK) { temp->left->color = PJ_RBCOLOR_BLACK; temp->color = PJ_RBCOLOR_RED; right_rotate( tree, temp); temp = node->parent->right; } temp->color = node->parent->color; temp->right->color = PJ_RBCOLOR_BLACK; node->parent->color = PJ_RBCOLOR_BLACK; left_rotate(tree, node->parent); node = tree->root; } } else { temp = node->parent->left; if (temp->color == PJ_RBCOLOR_RED) { temp->color = PJ_RBCOLOR_BLACK; node->parent->color = PJ_RBCOLOR_RED; right_rotate( tree, node->parent); temp = node->parent->left; } if (temp->right->color == PJ_RBCOLOR_BLACK && temp->left->color == PJ_RBCOLOR_BLACK) { temp->color = PJ_RBCOLOR_RED; node = node->parent; } else { if (temp->left->color == PJ_RBCOLOR_BLACK) { temp->right->color = PJ_RBCOLOR_BLACK; temp->color = PJ_RBCOLOR_RED; left_rotate( tree, temp); temp = node->parent->left; } temp->color = node->parent->color; node->parent->color = PJ_RBCOLOR_BLACK; temp->left->color = PJ_RBCOLOR_BLACK; right_rotate(tree, node->parent); node = tree->root; } } } node->color = PJ_RBCOLOR_BLACK; } PJ_DEF(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp ) { PJ_CHECK_STACK(); tree->null = tree->root = &tree->null_node; tree->null->key = NULL; tree->null->user_data = NULL; tree->size = 0; tree->null->left = tree->null->right = tree->null->parent = tree->null; tree->null->color = PJ_RBCOLOR_BLACK; tree->comp = comp; } PJ_DEF(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree ) { register pj_rbtree_node *node = tree->root; register pj_rbtree_node *null = tree->null; PJ_CHECK_STACK(); while (node->left != null) node = node->left; return node != null ? node : NULL; } PJ_DEF(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree ) { register pj_rbtree_node *node = tree->root; register pj_rbtree_node *null = tree->null; PJ_CHECK_STACK(); while (node->right != null) node = node->right; return node != null ? node : NULL; } PJ_DEF(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree, register pj_rbtree_node *node ) { register pj_rbtree_node *null = tree->null; PJ_CHECK_STACK(); if (node->right != null) { for (node=node->right; node->left!=null; node = node->left) /* void */; } else { register pj_rbtree_node *temp = node->parent; while (temp!=null && temp->right==node) { node = temp; temp = temp->parent; } node = temp; } return node != null ? node : NULL; } PJ_DEF(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree, register pj_rbtree_node *node ) { register pj_rbtree_node *null = tree->null; PJ_CHECK_STACK(); if (node->left != null) { for (node=node->left; node->right!=null; node=node->right) /* void */; } else { register pj_rbtree_node *temp = node->parent; while (temp!=null && temp->left==node) { node = temp; temp = temp->parent; } node = temp; } return node != null ? node : NULL; } PJ_DEF(int) pj_rbtree_insert( pj_rbtree *tree, pj_rbtree_node *element ) { int rv = 0; pj_rbtree_node *node, *parent = tree->null, *null = tree->null; pj_rbtree_comp *comp = tree->comp; PJ_CHECK_STACK(); node = tree->root; while (node != null) { rv = (*comp)(element->key, node->key); if (rv == 0) { /* found match, i.e. entry with equal key already exist */ return -1; } parent = node; node = rv < 0 ? node->left : node->right; } element->color = PJ_RBCOLOR_RED; element->left = element->right = null; node = element; if (parent != null) { node->parent = parent; if (rv < 0) parent->left = node; else parent->right = node; insert_fixup( tree, node); } else { tree->root = node; node->parent = null; node->color = PJ_RBCOLOR_BLACK; } ++tree->size; return 0; } PJ_DEF(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree, const void *key ) { int rv; pj_rbtree_node *node = tree->root; pj_rbtree_node *null = tree->null; pj_rbtree_comp *comp = tree->comp; while (node != null) { rv = (*comp)(key, node->key); if (rv == 0) return node; node = rv < 0 ? node->left : node->right; } return node != null ? node : NULL; } PJ_DEF(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree, pj_rbtree_node *node ) { pj_rbtree_node *succ; pj_rbtree_node *null = tree->null; pj_rbtree_node *child; pj_rbtree_node *parent; PJ_CHECK_STACK(); if (node->left == null || node->right == null) { succ = node; } else { for (succ=node->right; succ->left!=null; succ=succ->left) /* void */; } child = succ->left != null ? succ->left : succ->right; parent = succ->parent; child->parent = parent; if (parent != null) { if (parent->left == succ) parent->left = child; else parent->right = child; } else tree->root = child; if (succ != node) { succ->parent = node->parent; succ->left = node->left; succ->right = node->right; succ->color = node->color; parent = node->parent; if (parent != null) { if (parent->left==node) parent->left=succ; else parent->right=succ; } if (node->left != null) node->left->parent = succ;; if (node->right != null) node->right->parent = succ; if (tree->root == node) tree->root = succ; } if (succ->color == PJ_RBCOLOR_BLACK) { if (child != null) delete_fixup(tree, child); tree->null->color = PJ_RBCOLOR_BLACK; } --tree->size; return node; } PJ_DEF(unsigned) pj_rbtree_max_height( pj_rbtree *tree, pj_rbtree_node *node ) { unsigned l, r; PJ_CHECK_STACK(); if (node==NULL) node = tree->root; l = node->left != tree->null ? pj_rbtree_max_height(tree,node->left)+1 : 0; r = node->right != tree->null ? pj_rbtree_max_height(tree,node->right)+1 : 0; return l > r ? l : r; } PJ_DEF(unsigned) pj_rbtree_min_height( pj_rbtree *tree, pj_rbtree_node *node ) { unsigned l, r; PJ_CHECK_STACK(); if (node==NULL) node=tree->root; l = (node->left != tree->null) ? pj_rbtree_max_height(tree,node->left)+1 : 0; r = (node->right != tree->null) ? pj_rbtree_max_height(tree,node->right)+1 : 0; return l > r ? r : l; } ================================================ FILE: deps/pjsip/pjlib/src/pj/sock_bsd.c ================================================ /* $Id: sock_bsd.c 4233 2012-08-21 11:16:06Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #define THIS_FILE "sock_bsd.c" /* * Address families conversion. * The values here are indexed based on pj_addr_family. */ const pj_uint16_t PJ_AF_UNSPEC = AF_UNSPEC; const pj_uint16_t PJ_AF_UNIX = AF_UNIX; const pj_uint16_t PJ_AF_INET = AF_INET; const pj_uint16_t PJ_AF_INET6 = AF_INET6; #ifdef AF_PACKET const pj_uint16_t PJ_AF_PACKET = AF_PACKET; #else const pj_uint16_t PJ_AF_PACKET = 0xFFFF; #endif #ifdef AF_IRDA const pj_uint16_t PJ_AF_IRDA = AF_IRDA; #else const pj_uint16_t PJ_AF_IRDA = 0xFFFF; #endif /* * Socket types conversion. * The values here are indexed based on pj_sock_type */ const pj_uint16_t PJ_SOCK_STREAM= SOCK_STREAM; const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM; const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW; const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM; /* * Socket level values. */ const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET; #ifdef SOL_IP const pj_uint16_t PJ_SOL_IP = SOL_IP; #elif (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64) const pj_uint16_t PJ_SOL_IP = IPPROTO_IP; #else const pj_uint16_t PJ_SOL_IP = 0; #endif /* SOL_IP */ #if defined(SOL_TCP) const pj_uint16_t PJ_SOL_TCP = SOL_TCP; #elif defined(IPPROTO_TCP) const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP; #elif (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64) const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP; #else const pj_uint16_t PJ_SOL_TCP = 6; #endif /* SOL_TCP */ #ifdef SOL_UDP const pj_uint16_t PJ_SOL_UDP = SOL_UDP; #elif defined(IPPROTO_UDP) const pj_uint16_t PJ_SOL_UDP = IPPROTO_UDP; #elif (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64) const pj_uint16_t PJ_SOL_UDP = IPPROTO_UDP; #else const pj_uint16_t PJ_SOL_UDP = 17; #endif /* SOL_UDP */ #ifdef SOL_IPV6 const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6; #elif (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64) # if defined(IPPROTO_IPV6) || (_WIN32_WINNT >= 0x0501) const pj_uint16_t PJ_SOL_IPV6 = IPPROTO_IPV6; # else const pj_uint16_t PJ_SOL_IPV6 = 41; # endif #else const pj_uint16_t PJ_SOL_IPV6 = 41; #endif /* SOL_IPV6 */ /* IP_TOS */ #ifdef IP_TOS const pj_uint16_t PJ_IP_TOS = IP_TOS; #else const pj_uint16_t PJ_IP_TOS = 1; #endif /* TOS settings (declared in netinet/ip.h) */ #ifdef IPTOS_LOWDELAY const pj_uint16_t PJ_IPTOS_LOWDELAY = IPTOS_LOWDELAY; #else const pj_uint16_t PJ_IPTOS_LOWDELAY = 0x10; #endif #ifdef IPTOS_THROUGHPUT const pj_uint16_t PJ_IPTOS_THROUGHPUT = IPTOS_THROUGHPUT; #else const pj_uint16_t PJ_IPTOS_THROUGHPUT = 0x08; #endif #ifdef IPTOS_RELIABILITY const pj_uint16_t PJ_IPTOS_RELIABILITY = IPTOS_RELIABILITY; #else const pj_uint16_t PJ_IPTOS_RELIABILITY = 0x04; #endif #ifdef IPTOS_MINCOST const pj_uint16_t PJ_IPTOS_MINCOST = IPTOS_MINCOST; #else const pj_uint16_t PJ_IPTOS_MINCOST = 0x02; #endif /* optname values. */ const pj_uint16_t PJ_SO_TYPE = SO_TYPE; const pj_uint16_t PJ_SO_RCVBUF = SO_RCVBUF; const pj_uint16_t PJ_SO_SNDBUF = SO_SNDBUF; const pj_uint16_t PJ_TCP_NODELAY= TCP_NODELAY; const pj_uint16_t PJ_SO_REUSEADDR= SO_REUSEADDR; #ifdef SO_NOSIGPIPE const pj_uint16_t PJ_SO_NOSIGPIPE = SO_NOSIGPIPE; #else const pj_uint16_t PJ_SO_NOSIGPIPE = 0xFFFF; #endif #if defined(SO_PRIORITY) const pj_uint16_t PJ_SO_PRIORITY = SO_PRIORITY; #else /* This is from Linux, YMMV */ const pj_uint16_t PJ_SO_PRIORITY = 12; #endif /* Multicasting is not supported e.g. in PocketPC 2003 SDK */ #ifdef IP_MULTICAST_IF const pj_uint16_t PJ_IP_MULTICAST_IF = IP_MULTICAST_IF; const pj_uint16_t PJ_IP_MULTICAST_TTL = IP_MULTICAST_TTL; const pj_uint16_t PJ_IP_MULTICAST_LOOP = IP_MULTICAST_LOOP; const pj_uint16_t PJ_IP_ADD_MEMBERSHIP = IP_ADD_MEMBERSHIP; const pj_uint16_t PJ_IP_DROP_MEMBERSHIP = IP_DROP_MEMBERSHIP; #else const pj_uint16_t PJ_IP_MULTICAST_IF = 0xFFFF; const pj_uint16_t PJ_IP_MULTICAST_TTL = 0xFFFF; const pj_uint16_t PJ_IP_MULTICAST_LOOP = 0xFFFF; const pj_uint16_t PJ_IP_ADD_MEMBERSHIP = 0xFFFF; const pj_uint16_t PJ_IP_DROP_MEMBERSHIP = 0xFFFF; #endif /* recv() and send() flags */ const int PJ_MSG_OOB = MSG_OOB; const int PJ_MSG_PEEK = MSG_PEEK; const int PJ_MSG_DONTROUTE = MSG_DONTROUTE; #if 0 static void CHECK_ADDR_LEN(const pj_sockaddr *addr, int len) { pj_sockaddr *a = (pj_sockaddr*)addr; pj_assert((a->addr.sa_family==PJ_AF_INET && len==sizeof(pj_sockaddr_in)) || (a->addr.sa_family==PJ_AF_INET6 && len==sizeof(pj_sockaddr_in6))); } #else #define CHECK_ADDR_LEN(addr,len) #endif /* * Convert 16-bit value from network byte order to host byte order. */ PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort) { return ntohs(netshort); } /* * Convert 16-bit value from host byte order to network byte order. */ PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort) { return htons(hostshort); } /* * Convert 32-bit value from network byte order to host byte order. */ PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong) { return ntohl(netlong); } /* * Convert 32-bit value from host byte order to network byte order. */ PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong) { return htonl(hostlong); } /* * Convert an Internet host address given in network byte order * to string in standard numbers and dots notation. */ PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr) { #if !defined(PJ_LINUX) && !defined(PJ_LINUX_KERNEL) return inet_ntoa(*(struct in_addr*)&inaddr); #else struct in_addr addr; addr.s_addr = inaddr.s_addr; return inet_ntoa(addr); #endif } /* * This function converts the Internet host address cp from the standard * numbers-and-dots notation into binary data and stores it in the structure * that inp points to. */ PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp) { char tempaddr[PJ_INET_ADDRSTRLEN]; /* Initialize output with PJ_INADDR_NONE. * Some apps relies on this instead of the return value * (and anyway the return value is quite confusing!) */ inp->s_addr = PJ_INADDR_NONE; /* Caution: * this function might be called with cp->slen >= 16 * (i.e. when called with hostname to check if it's an IP addr). */ PJ_ASSERT_RETURN(cp && cp->slen && inp, 0); if (cp->slen >= PJ_INET_ADDRSTRLEN) { return 0; } pj_memcpy(tempaddr, cp->ptr, cp->slen); tempaddr[cp->slen] = '\0'; #if defined(PJ_SOCK_HAS_INET_ATON) && PJ_SOCK_HAS_INET_ATON != 0 return inet_aton(tempaddr, (struct in_addr*)inp); #else inp->s_addr = inet_addr(tempaddr); return inp->s_addr == PJ_INADDR_NONE ? 0 : 1; #endif } /* * Convert text to IPv4/IPv6 address. */ PJ_DEF(pj_status_t) pj_inet_pton(int af, const pj_str_t *src, void *dst) { char tempaddr[PJ_INET6_ADDRSTRLEN]; PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EAFNOTSUP); PJ_ASSERT_RETURN(src && src->slen && dst, PJ_EINVAL); /* Initialize output with PJ_IN_ADDR_NONE for IPv4 (to be * compatible with pj_inet_aton() */ if (af==PJ_AF_INET) { ((pj_in_addr*)dst)->s_addr = PJ_INADDR_NONE; } /* Caution: * this function might be called with cp->slen >= 46 * (i.e. when called with hostname to check if it's an IP addr). */ if (src->slen >= PJ_INET6_ADDRSTRLEN) { return PJ_ENAMETOOLONG; } pj_memcpy(tempaddr, src->ptr, src->slen); tempaddr[src->slen] = '\0'; #if defined(PJ_SOCK_HAS_INET_PTON) && PJ_SOCK_HAS_INET_PTON != 0 /* * Implementation using inet_pton() */ if (inet_pton(af, tempaddr, dst) != 1) { pj_status_t status = pj_get_netos_error(); if (status == PJ_SUCCESS) status = PJ_EUNKNOWN; return status; } return PJ_SUCCESS; #elif defined(PJ_WIN32) || defined(PJ_WIN64) || defined(PJ_WIN32_WINCE) /* * Implementation on Windows, using WSAStringToAddress(). * Should also work on Unicode systems. */ { PJ_DECL_UNICODE_TEMP_BUF(wtempaddr,PJ_INET6_ADDRSTRLEN) pj_sockaddr sock_addr; int addr_len = sizeof(sock_addr); int rc; sock_addr.addr.sa_family = (pj_uint16_t)af; rc = WSAStringToAddress( PJ_STRING_TO_NATIVE(tempaddr,wtempaddr,sizeof(wtempaddr)), af, NULL, (LPSOCKADDR)&sock_addr, &addr_len); if (rc != 0) { /* If you get rc 130022 Invalid argument (WSAEINVAL) with IPv6, * check that you have IPv6 enabled (install it in the network * adapter). */ pj_status_t status = pj_get_netos_error(); if (status == PJ_SUCCESS) status = PJ_EUNKNOWN; return status; } if (sock_addr.addr.sa_family == PJ_AF_INET) { pj_memcpy(dst, &sock_addr.ipv4.sin_addr, 4); return PJ_SUCCESS; } else if (sock_addr.addr.sa_family == PJ_AF_INET6) { pj_memcpy(dst, &sock_addr.ipv6.sin6_addr, 16); return PJ_SUCCESS; } else { pj_assert(!"Shouldn't happen"); return PJ_EBUG; } } #elif !defined(PJ_HAS_IPV6) || PJ_HAS_IPV6==0 /* IPv6 support is disabled, just return error without raising assertion */ return PJ_EIPV6NOTSUP; #else pj_assert(!"Not supported"); return PJ_EIPV6NOTSUP; #endif } /* * Convert IPv4/IPv6 address to text. */ PJ_DEF(pj_status_t) pj_inet_ntop(int af, const void *src, char *dst, int size) { PJ_ASSERT_RETURN(src && dst && size, PJ_EINVAL); *dst = '\0'; PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EAFNOTSUP); #if defined(PJ_SOCK_HAS_INET_NTOP) && PJ_SOCK_HAS_INET_NTOP != 0 /* * Implementation using inet_ntop() */ if (inet_ntop(af, src, dst, size) == NULL) { pj_status_t status = pj_get_netos_error(); if (status == PJ_SUCCESS) status = PJ_EUNKNOWN; return status; } return PJ_SUCCESS; #elif defined(PJ_WIN32) || defined(PJ_WIN64) || defined(PJ_WIN32_WINCE) /* * Implementation on Windows, using WSAAddressToString(). * Should also work on Unicode systems. */ { PJ_DECL_UNICODE_TEMP_BUF(wtempaddr,PJ_INET6_ADDRSTRLEN) pj_sockaddr sock_addr; DWORD addr_len, addr_str_len; int rc; pj_bzero(&sock_addr, sizeof(sock_addr)); sock_addr.addr.sa_family = (pj_uint16_t)af; if (af == PJ_AF_INET) { if (size < PJ_INET_ADDRSTRLEN) return PJ_ETOOSMALL; pj_memcpy(&sock_addr.ipv4.sin_addr, src, 4); addr_len = sizeof(pj_sockaddr_in); addr_str_len = PJ_INET_ADDRSTRLEN; } else if (af == PJ_AF_INET6) { if (size < PJ_INET6_ADDRSTRLEN) return PJ_ETOOSMALL; pj_memcpy(&sock_addr.ipv6.sin6_addr, src, 16); addr_len = sizeof(pj_sockaddr_in6); addr_str_len = PJ_INET6_ADDRSTRLEN; } else { pj_assert(!"Unsupported address family"); return PJ_EAFNOTSUP; } #if PJ_NATIVE_STRING_IS_UNICODE rc = WSAAddressToString((LPSOCKADDR)&sock_addr, addr_len, NULL, wtempaddr, &addr_str_len); if (rc == 0) { pj_unicode_to_ansi(wtempaddr, wcslen(wtempaddr), dst, size); } #else rc = WSAAddressToString((LPSOCKADDR)&sock_addr, addr_len, NULL, dst, &addr_str_len); #endif if (rc != 0) { pj_status_t status = pj_get_netos_error(); if (status == PJ_SUCCESS) status = PJ_EUNKNOWN; return status; } return PJ_SUCCESS; } #elif !defined(PJ_HAS_IPV6) || PJ_HAS_IPV6==0 /* IPv6 support is disabled, just return error without raising assertion */ return PJ_EIPV6NOTSUP; #else pj_assert(!"Not supported"); return PJ_EIPV6NOTSUP; #endif } /* * Get hostname. */ PJ_DEF(const pj_str_t*) pj_gethostname(void) { static char buf[PJ_MAX_HOSTNAME]; static pj_str_t hostname; PJ_CHECK_STACK(); if (hostname.ptr == NULL) { hostname.ptr = buf; if (gethostname(buf, sizeof(buf)) != 0) { hostname.ptr[0] = '\0'; hostname.slen = 0; } else { hostname.slen = strlen(buf); } } return &hostname; } #if defined(PJ_WIN32) || defined(PJ_WIN64) /* * Create new socket/endpoint for communication and returns a descriptor. */ PJ_DEF(pj_status_t) pj_sock_socket(int af, int type, int proto, pj_sock_t *sock) { PJ_CHECK_STACK(); /* Sanity checks. */ PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL); PJ_ASSERT_RETURN((SOCKET)PJ_INVALID_SOCKET==INVALID_SOCKET, (*sock=PJ_INVALID_SOCKET, PJ_EINVAL)); *sock = WSASocket(af, type, proto, NULL, 0, WSA_FLAG_OVERLAPPED); if (*sock == PJ_INVALID_SOCKET) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); #if PJ_SOCK_DISABLE_WSAECONNRESET && \ (!defined(PJ_WIN32_WINCE) || PJ_WIN32_WINCE==0) #ifndef SIO_UDP_CONNRESET #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) #endif /* Disable WSAECONNRESET for UDP. * See https://trac.pjsip.org/repos/ticket/1197 */ if (type==PJ_SOCK_DGRAM) { DWORD dwBytesReturned = 0; BOOL bNewBehavior = FALSE; DWORD rc; rc = WSAIoctl(*sock, SIO_UDP_CONNRESET, &bNewBehavior, sizeof(bNewBehavior), NULL, 0, &dwBytesReturned, NULL, NULL); if (rc==SOCKET_ERROR) { // Ignored.. } } #endif return PJ_SUCCESS; } #else /* * Create new socket/endpoint for communication and returns a descriptor. */ PJ_DEF(pj_status_t) pj_sock_socket(int af, int type, int proto, pj_sock_t *sock) { PJ_CHECK_STACK(); /* Sanity checks. */ PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL); PJ_ASSERT_RETURN(PJ_INVALID_SOCKET==-1, (*sock=PJ_INVALID_SOCKET, PJ_EINVAL)); *sock = socket(af, type, proto); if (*sock == PJ_INVALID_SOCKET) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else { pj_int32_t val = 1; if (type == pj_SOCK_STREAM()) { pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), pj_SO_NOSIGPIPE(), &val, sizeof(val)); } #if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 if (type == pj_SOCK_DGRAM()) { pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), SO_NOSIGPIPE, &val, sizeof(val)); } #endif return PJ_SUCCESS; } } #endif /* * Bind socket. */ PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock, const pj_sockaddr_t *addr, int len) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(addr && len >= (int)sizeof(struct sockaddr_in), PJ_EINVAL); CHECK_ADDR_LEN(addr, len); if (bind(sock, (struct sockaddr*)addr, len) != 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else return PJ_SUCCESS; } /* * Bind socket. */ PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock, pj_uint32_t addr32, pj_uint16_t port) { pj_sockaddr_in addr; PJ_CHECK_STACK(); PJ_SOCKADDR_SET_LEN(&addr, sizeof(pj_sockaddr_in)); addr.sin_family = PJ_AF_INET; pj_bzero(addr.sin_zero, sizeof(addr.sin_zero)); addr.sin_addr.s_addr = pj_htonl(addr32); addr.sin_port = pj_htons(port); return pj_sock_bind(sock, &addr, sizeof(pj_sockaddr_in)); } /* * Close socket. */ PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sock) { int rc; PJ_CHECK_STACK(); #if defined(PJ_WIN32) && PJ_WIN32!=0 || \ defined(PJ_WIN64) && PJ_WIN64 != 0 || \ defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 rc = closesocket(sock); #else rc = close(sock); #endif if (rc != 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else return PJ_SUCCESS; } /* * Get remote's name. */ PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sock, pj_sockaddr_t *addr, int *namelen) { PJ_CHECK_STACK(); if (getpeername(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else { PJ_SOCKADDR_RESET_LEN(addr); return PJ_SUCCESS; } } /* * Get socket name. */ PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock, pj_sockaddr_t *addr, int *namelen) { PJ_CHECK_STACK(); if (getsockname(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else { PJ_SOCKADDR_RESET_LEN(addr); return PJ_SUCCESS; } } /* * Send data */ PJ_DEF(pj_status_t) pj_sock_send(pj_sock_t sock, const void *buf, pj_ssize_t *len, unsigned flags) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(len, PJ_EINVAL); #ifdef MSG_NOSIGNAL /* Suppress SIGPIPE. See https://trac.pjsip.org/repos/ticket/1538 */ flags |= MSG_NOSIGNAL; #endif *len = send(sock, (const char*)buf, (int)(*len), flags); if (*len < 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else return PJ_SUCCESS; } /* * Send data. */ PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock, const void *buf, pj_ssize_t *len, unsigned flags, const pj_sockaddr_t *to, int tolen) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(len, PJ_EINVAL); CHECK_ADDR_LEN(to, tolen); *len = sendto(sock, (const char*)buf, (int)(*len), flags, (const struct sockaddr*)to, tolen); if (*len < 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else return PJ_SUCCESS; } /* * Receive data. */ PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock, void *buf, pj_ssize_t *len, unsigned flags) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(buf && len, PJ_EINVAL); *len = recv(sock, (char*)buf, (int)(*len), flags); if (*len < 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else return PJ_SUCCESS; } /* * Receive data. */ PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock, void *buf, pj_ssize_t *len, unsigned flags, pj_sockaddr_t *from, int *fromlen) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(buf && len, PJ_EINVAL); *len = recvfrom(sock, (char*)buf, (int)(*len), flags, (struct sockaddr*)from, (socklen_t*)fromlen); if (*len < 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else { if (from) { PJ_SOCKADDR_RESET_LEN(from); } return PJ_SUCCESS; } } /* * Get socket option. */ PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sock, pj_uint16_t level, pj_uint16_t optname, void *optval, int *optlen) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(optval && optlen, PJ_EINVAL); if (getsockopt(sock, level, optname, (char*)optval, (socklen_t*)optlen)!=0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else return PJ_SUCCESS; } /* * Set socket option. */ PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock, pj_uint16_t level, pj_uint16_t optname, const void *optval, int optlen) { int status; PJ_CHECK_STACK(); #if (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_SUNOS) && PJ_SUNOS) /* Some opt may still need int value (e.g:SO_EXCLUSIVEADDRUSE in win32). */ status = setsockopt(sock, level, ((optname&0xff00)==0xff00)?(int)optname|0xffff0000:optname, (const char*)optval, optlen); #else status = setsockopt(sock, level, optname, (const char*)optval, optlen); #endif if (status != 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else return PJ_SUCCESS; } /* * Set socket option. */ PJ_DEF(pj_status_t) pj_sock_setsockopt_params( pj_sock_t sockfd, const pj_sockopt_params *params) { unsigned int i = 0; pj_status_t retval = PJ_SUCCESS; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(params, PJ_EINVAL); for (;icnt && ioptions[i].level, (pj_uint16_t)params->options[i].optname, params->options[i].optval, params->options[i].optlen); if (status != PJ_SUCCESS) { retval = status; PJ_PERROR(4,(THIS_FILE, status, "Warning: error applying sock opt %d", params->options[i].optname)); } } return retval; } /* * Connect socket. */ PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sock, const pj_sockaddr_t *addr, int namelen) { PJ_CHECK_STACK(); if (connect(sock, (struct sockaddr*)addr, namelen) != 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else return PJ_SUCCESS; } /* * Shutdown socket. */ #if PJ_HAS_TCP PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sock, int how) { PJ_CHECK_STACK(); if (shutdown(sock, how) != 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else return PJ_SUCCESS; } /* * Start listening to incoming connections. */ PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sock, int backlog) { PJ_CHECK_STACK(); if (listen(sock, backlog) != 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else return PJ_SUCCESS; } /* * Accept incoming connections */ PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd, pj_sock_t *newsock, pj_sockaddr_t *addr, int *addrlen) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(newsock != NULL, PJ_EINVAL); #if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0 if (addr) { PJ_SOCKADDR_SET_LEN(addr, *addrlen); } #endif *newsock = accept(serverfd, (struct sockaddr*)addr, (socklen_t*)addrlen); if (*newsock==PJ_INVALID_SOCKET) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else { #if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0 if (addr) { PJ_SOCKADDR_RESET_LEN(addr); } #endif return PJ_SUCCESS; } } #endif /* PJ_HAS_TCP */ ================================================ FILE: deps/pjsip/pjlib/src/pj/sock_common.c ================================================ /* $Id: sock_common.c 4343 2013-02-07 09:35:34Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #if 0 /* Enable some tracing */ #include #define THIS_FILE "sock_common.c" #define TRACE_(arg) PJ_LOG(4,arg) #else #define TRACE_(arg) #endif /* * Convert address string with numbers and dots to binary IP address. */ PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp) { pj_in_addr addr; pj_inet_aton(cp, &addr); return addr; } /* * Convert address string with numbers and dots to binary IP address. */ PJ_DEF(pj_in_addr) pj_inet_addr2(const char *cp) { pj_str_t str = pj_str((char*)cp); return pj_inet_addr(&str); } /* * Get text representation. */ PJ_DEF(char*) pj_inet_ntop2( int af, const void *src, char *dst, int size) { pj_status_t status; status = pj_inet_ntop(af, src, dst, size); return (status==PJ_SUCCESS)? dst : NULL; } /* * Print socket address. */ PJ_DEF(char*) pj_sockaddr_print( const pj_sockaddr_t *addr, char *buf, int size, unsigned flags) { enum { WITH_PORT = 1, WITH_BRACKETS = 2 }; char txt[PJ_INET6_ADDRSTRLEN]; char port[32]; const pj_addr_hdr *h = (const pj_addr_hdr*)addr; char *bquote, *equote; pj_status_t status; status = pj_inet_ntop(h->sa_family, pj_sockaddr_get_addr(addr), txt, sizeof(txt)); if (status != PJ_SUCCESS) return ""; if (h->sa_family != PJ_AF_INET6 || (flags & WITH_BRACKETS)==0) { bquote = ""; equote = ""; } else { bquote = "["; equote = "]"; } if (flags & WITH_PORT) { pj_ansi_snprintf(port, sizeof(port), ":%d", pj_sockaddr_get_port(addr)); } else { port[0] = '\0'; } pj_ansi_snprintf(buf, size, "%s%s%s%s", bquote, txt, equote, port); return buf; } /* * Set the IP address of an IP socket address from string address, * with resolving the host if necessary. The string address may be in a * standard numbers and dots notation or may be a hostname. If hostname * is specified, then the function will resolve the host into the IP * address. */ PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, const pj_str_t *str_addr) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(!str_addr || str_addr->slen < PJ_MAX_HOSTNAME, (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL)); PJ_SOCKADDR_RESET_LEN(addr); addr->sin_family = PJ_AF_INET; pj_bzero(addr->sin_zero, sizeof(addr->sin_zero)); if (str_addr && str_addr->slen) { addr->sin_addr = pj_inet_addr(str_addr); if (addr->sin_addr.s_addr == PJ_INADDR_NONE) { pj_hostent he; pj_status_t rc; rc = pj_gethostbyname(str_addr, &he); if (rc == 0) { addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr; } else { addr->sin_addr.s_addr = PJ_INADDR_NONE; return rc; } } } else { addr->sin_addr.s_addr = 0; } return PJ_SUCCESS; } /* Set address from a name */ PJ_DEF(pj_status_t) pj_sockaddr_set_str_addr(int af, pj_sockaddr *addr, const pj_str_t *str_addr) { pj_status_t status; if (af == PJ_AF_INET) { return pj_sockaddr_in_set_str_addr(&addr->ipv4, str_addr); } PJ_ASSERT_RETURN(af==PJ_AF_INET6, PJ_EAFNOTSUP); /* IPv6 specific */ addr->ipv6.sin6_family = PJ_AF_INET6; PJ_SOCKADDR_RESET_LEN(addr); if (str_addr && str_addr->slen) { status = pj_inet_pton(PJ_AF_INET6, str_addr, &addr->ipv6.sin6_addr); if (status != PJ_SUCCESS) { pj_addrinfo ai; unsigned count = 1; status = pj_getaddrinfo(PJ_AF_INET6, str_addr, &count, &ai); if (status==PJ_SUCCESS) { pj_memcpy(&addr->ipv6.sin6_addr, &ai.ai_addr.ipv6.sin6_addr, sizeof(addr->ipv6.sin6_addr)); } } } else { status = PJ_SUCCESS; } return status; } /* * Set the IP address and port of an IP socket address. * The string address may be in a standard numbers and dots notation or * may be a hostname. If hostname is specified, then the function will * resolve the host into the IP address. */ PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr, const pj_str_t *str_addr, pj_uint16_t port) { PJ_ASSERT_RETURN(addr, (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL)); PJ_SOCKADDR_RESET_LEN(addr); addr->sin_family = PJ_AF_INET; pj_bzero(addr->sin_zero, sizeof(addr->sin_zero)); pj_sockaddr_in_set_port(addr, port); return pj_sockaddr_in_set_str_addr(addr, str_addr); } /* * Initialize IP socket address based on the address and port info. */ PJ_DEF(pj_status_t) pj_sockaddr_init(int af, pj_sockaddr *addr, const pj_str_t *cp, pj_uint16_t port) { pj_status_t status; if (af == PJ_AF_INET) { return pj_sockaddr_in_init(&addr->ipv4, cp, port); } /* IPv6 specific */ PJ_ASSERT_RETURN(af==PJ_AF_INET6, PJ_EAFNOTSUP); pj_bzero(addr, sizeof(pj_sockaddr_in6)); addr->addr.sa_family = PJ_AF_INET6; status = pj_sockaddr_set_str_addr(af, addr, cp); if (status != PJ_SUCCESS) return status; addr->ipv6.sin6_port = pj_htons(port); return PJ_SUCCESS; } /* * Compare two socket addresses. */ PJ_DEF(int) pj_sockaddr_cmp( const pj_sockaddr_t *addr1, const pj_sockaddr_t *addr2) { const pj_sockaddr *a1 = (const pj_sockaddr*) addr1; const pj_sockaddr *a2 = (const pj_sockaddr*) addr2; int port1, port2; int result; /* Compare address family */ if (a1->addr.sa_family < a2->addr.sa_family) return -1; else if (a1->addr.sa_family > a2->addr.sa_family) return 1; /* Compare addresses */ result = pj_memcmp(pj_sockaddr_get_addr(a1), pj_sockaddr_get_addr(a2), pj_sockaddr_get_addr_len(a1)); if (result != 0) return result; /* Compare port number */ port1 = pj_sockaddr_get_port(a1); port2 = pj_sockaddr_get_port(a2); if (port1 < port2) return -1; else if (port1 > port2) return 1; /* TODO: * Do we need to compare flow label and scope id in IPv6? */ /* Looks equal */ return 0; } /* * Get first IP address associated with the hostname. */ PJ_DEF(pj_in_addr) pj_gethostaddr(void) { pj_sockaddr_in addr; const pj_str_t *hostname = pj_gethostname(); pj_sockaddr_in_set_str_addr(&addr, hostname); return addr.sin_addr; } /* * Get port number of a pj_sockaddr_in */ PJ_DEF(pj_uint16_t) pj_sockaddr_in_get_port(const pj_sockaddr_in *addr) { return pj_ntohs(addr->sin_port); } /* * Get the address part */ PJ_DEF(void*) pj_sockaddr_get_addr(const pj_sockaddr_t *addr) { const pj_sockaddr *a = (const pj_sockaddr*)addr; PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET || a->addr.sa_family == PJ_AF_INET6, NULL); if (a->addr.sa_family == PJ_AF_INET6) return (void*) &a->ipv6.sin6_addr; else return (void*) &a->ipv4.sin_addr; } /* * Check if sockaddr contains a non-zero address */ PJ_DEF(pj_bool_t) pj_sockaddr_has_addr(const pj_sockaddr_t *addr) { const pj_sockaddr *a = (const pj_sockaddr*)addr; /* It's probably not wise to raise assertion here if * the address doesn't contain a valid address family, and * just return PJ_FALSE instead. * * The reason is because application may need to distinguish * these three conditions with sockaddr: * a) sockaddr is not initialized. This is by convention * indicated by sa_family==0. * b) sockaddr is initialized with zero address. This is * indicated with the address field having zero address. * c) sockaddr is initialized with valid address/port. * * If we enable this assertion, then application will loose * the capability to specify condition a), since it will be * forced to always initialize sockaddr (even with zero address). * This may break some parts of upper layer libraries. */ //PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET || // a->addr.sa_family == PJ_AF_INET6, PJ_FALSE); if (a->addr.sa_family!=PJ_AF_INET && a->addr.sa_family!=PJ_AF_INET6) { return PJ_FALSE; } else if (a->addr.sa_family == PJ_AF_INET6) { pj_uint8_t zero[24]; pj_bzero(zero, sizeof(zero)); return pj_memcmp(a->ipv6.sin6_addr.s6_addr, zero, sizeof(pj_in6_addr)) != 0; } else return a->ipv4.sin_addr.s_addr != PJ_INADDR_ANY; } /* * Get port number */ PJ_DEF(pj_uint16_t) pj_sockaddr_get_port(const pj_sockaddr_t *addr) { const pj_sockaddr *a = (const pj_sockaddr*) addr; PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET || a->addr.sa_family == PJ_AF_INET6, (pj_uint16_t)0xFFFF); return pj_ntohs((pj_uint16_t)(a->addr.sa_family == PJ_AF_INET6 ? a->ipv6.sin6_port : a->ipv4.sin_port)); } /* * Get the length of the address part. */ PJ_DEF(unsigned) pj_sockaddr_get_addr_len(const pj_sockaddr_t *addr) { const pj_sockaddr *a = (const pj_sockaddr*) addr; PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET || a->addr.sa_family == PJ_AF_INET6, 0); return a->addr.sa_family == PJ_AF_INET6 ? sizeof(pj_in6_addr) : sizeof(pj_in_addr); } /* * Get socket address length. */ PJ_DEF(unsigned) pj_sockaddr_get_len(const pj_sockaddr_t *addr) { const pj_sockaddr *a = (const pj_sockaddr*) addr; PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET || a->addr.sa_family == PJ_AF_INET6, 0); return a->addr.sa_family == PJ_AF_INET6 ? sizeof(pj_sockaddr_in6) : sizeof(pj_sockaddr_in); } /* * Copy only the address part (sin_addr/sin6_addr) of a socket address. */ PJ_DEF(void) pj_sockaddr_copy_addr( pj_sockaddr *dst, const pj_sockaddr *src) { /* Destination sockaddr might not be initialized */ const char *srcbuf = (char*)pj_sockaddr_get_addr(src); char *dstbuf = ((char*)dst) + (srcbuf - (char*)src); pj_memcpy(dstbuf, srcbuf, pj_sockaddr_get_addr_len(src)); } /* * Copy socket address. */ PJ_DEF(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src) { pj_memcpy(dst, src, pj_sockaddr_get_len(src)); } /* * Set port number of pj_sockaddr_in */ PJ_DEF(void) pj_sockaddr_in_set_port(pj_sockaddr_in *addr, pj_uint16_t hostport) { addr->sin_port = pj_htons(hostport); } /* * Set port number of pj_sockaddr */ PJ_DEF(pj_status_t) pj_sockaddr_set_port(pj_sockaddr *addr, pj_uint16_t hostport) { int af = addr->addr.sa_family; PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL); if (af == PJ_AF_INET6) addr->ipv6.sin6_port = pj_htons(hostport); else addr->ipv4.sin_port = pj_htons(hostport); return PJ_SUCCESS; } /* * Get IPv4 address */ PJ_DEF(pj_in_addr) pj_sockaddr_in_get_addr(const pj_sockaddr_in *addr) { pj_in_addr in_addr; in_addr.s_addr = pj_ntohl(addr->sin_addr.s_addr); return in_addr; } /* * Set IPv4 address */ PJ_DEF(void) pj_sockaddr_in_set_addr(pj_sockaddr_in *addr, pj_uint32_t hostaddr) { addr->sin_addr.s_addr = pj_htonl(hostaddr); } /* * Parse address */ PJ_DEF(pj_status_t) pj_sockaddr_parse2(int af, unsigned options, const pj_str_t *str, pj_str_t *p_hostpart, pj_uint16_t *p_port, int *raf) { const char *end = str->ptr + str->slen; const char *last_colon_pos = NULL; unsigned colon_cnt = 0; const char *p; PJ_ASSERT_RETURN((af==PJ_AF_INET || af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) && options==0 && str!=NULL, PJ_EINVAL); /* Special handling for empty input */ if (str->slen==0 || str->ptr==NULL) { if (p_hostpart) p_hostpart->slen = 0; if (p_port) *p_port = 0; if (raf) *raf = PJ_AF_INET; return PJ_SUCCESS; } /* Count the colon and get the last colon */ for (p=str->ptr; p!=end; ++p) { if (*p == ':') { ++colon_cnt; last_colon_pos = p; } } /* Deduce address family if it's not given */ if (af == PJ_AF_UNSPEC) { if (colon_cnt > 1) af = PJ_AF_INET6; else af = PJ_AF_INET; } else if (af == PJ_AF_INET && colon_cnt > 1) return PJ_EINVAL; if (raf) *raf = af; if (af == PJ_AF_INET) { /* Parse as IPv4. Supported formats: * - "10.0.0.1:80" * - "10.0.0.1" * - "10.0.0.1:" * - ":80" * - ":" */ pj_str_t hostpart; unsigned long port; hostpart.ptr = (char*)str->ptr; if (last_colon_pos) { pj_str_t port_part; int i; hostpart.slen = last_colon_pos - str->ptr; port_part.ptr = (char*)last_colon_pos + 1; port_part.slen = end - port_part.ptr; /* Make sure port number is valid */ for (i=0; i 65535) return PJ_EINVAL; } else { hostpart.slen = str->slen; port = 0; } if (p_hostpart) *p_hostpart = hostpart; if (p_port) *p_port = (pj_uint16_t)port; return PJ_SUCCESS; } else if (af == PJ_AF_INET6) { /* Parse as IPv6. Supported formats: * - "fe::01:80" ==> note: port number is zero in this case, not 80! * - "[fe::01]:80" * - "fe::01" * - "fe::01:" * - "[fe::01]" * - "[fe::01]:" * - "[::]:80" * - ":::80" * - "[::]" * - "[::]:" * - ":::" * - "::" */ pj_str_t hostpart, port_part; if (*str->ptr == '[') { char *end_bracket; int i; unsigned long port; if (last_colon_pos == NULL) return PJ_EINVAL; end_bracket = pj_strchr(str, ']'); if (end_bracket == NULL) return PJ_EINVAL; hostpart.ptr = (char*)str->ptr + 1; hostpart.slen = end_bracket - hostpart.ptr; if (last_colon_pos < end_bracket) { port_part.ptr = NULL; port_part.slen = 0; } else { port_part.ptr = (char*)last_colon_pos + 1; port_part.slen = end - port_part.ptr; } /* Make sure port number is valid */ for (i=0; i 65535) return PJ_EINVAL; if (p_hostpart) *p_hostpart = hostpart; if (p_port) *p_port = (pj_uint16_t)port; return PJ_SUCCESS; } else { /* Treat everything as part of the IPv6 IP address */ if (p_hostpart) *p_hostpart = *str; if (p_port) *p_port = 0; return PJ_SUCCESS; } } else { return PJ_EAFNOTSUP; } } /* * Parse address */ PJ_DEF(pj_status_t) pj_sockaddr_parse( int af, unsigned options, const pj_str_t *str, pj_sockaddr *addr) { pj_str_t hostpart; pj_uint16_t port; pj_status_t status; PJ_ASSERT_RETURN(addr, PJ_EINVAL); PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC || af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL); PJ_ASSERT_RETURN(options == 0, PJ_EINVAL); status = pj_sockaddr_parse2(af, options, str, &hostpart, &port, &af); if (status != PJ_SUCCESS) return status; #if !defined(PJ_HAS_IPV6) || !PJ_HAS_IPV6 if (af==PJ_AF_INET6) return PJ_EIPV6NOTSUP; #endif status = pj_sockaddr_init(af, addr, &hostpart, port); #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 if (status != PJ_SUCCESS && af == PJ_AF_INET6) { /* Parsing does not yield valid address. Try to treat the last * portion after the colon as port number. */ const char *last_colon_pos=NULL, *p; const char *end = str->ptr + str->slen; unsigned long long_port; pj_str_t port_part; int i; /* Parse as IPv6:port */ for (p=str->ptr; p!=end; ++p) { if (*p == ':') last_colon_pos = p; } if (last_colon_pos == NULL) return status; hostpart.ptr = (char*)str->ptr; hostpart.slen = last_colon_pos - str->ptr; port_part.ptr = (char*)last_colon_pos + 1; port_part.slen = end - port_part.ptr; /* Make sure port number is valid */ for (i=0; i 65535) return status; port = (pj_uint16_t)long_port; status = pj_sockaddr_init(PJ_AF_INET6, addr, &hostpart, port); } #endif return status; } /* Resolve the IP address of local machine */ PJ_DEF(pj_status_t) pj_gethostip(int af, pj_sockaddr *addr) { unsigned i, count, cand_cnt; enum { CAND_CNT = 8, /* Weighting to be applied to found addresses */ WEIGHT_HOSTNAME = 1, /* hostname IP is not always valid! */ WEIGHT_DEF_ROUTE = 2, WEIGHT_INTERFACE = 1, WEIGHT_LOOPBACK = -5, WEIGHT_LINK_LOCAL = -4, WEIGHT_DISABLED = -50, MIN_WEIGHT = WEIGHT_DISABLED+1 /* minimum weight to use */ }; /* candidates: */ pj_sockaddr cand_addr[CAND_CNT]; int cand_weight[CAND_CNT]; int selected_cand; char strip[PJ_INET6_ADDRSTRLEN+10]; /* Special IPv4 addresses. */ struct spec_ipv4_t { pj_uint32_t addr; pj_uint32_t mask; int weight; } spec_ipv4[] = { /* 127.0.0.0/8, loopback addr will be used if there is no other * addresses. */ { 0x7f000000, 0xFF000000, WEIGHT_LOOPBACK }, /* 0.0.0.0/8, special IP that doesn't seem to be practically useful */ { 0x00000000, 0xFF000000, WEIGHT_DISABLED }, /* 169.254.0.0/16, a zeroconf/link-local address, which has higher * priority than loopback and will be used if there is no other * valid addresses. */ { 0xa9fe0000, 0xFFFF0000, WEIGHT_LINK_LOCAL } }; /* Special IPv6 addresses */ struct spec_ipv6_t { pj_uint8_t addr[16]; pj_uint8_t mask[16]; int weight; } spec_ipv6[] = { /* Loopback address, ::1/128 */ { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, WEIGHT_LOOPBACK }, /* Link local, fe80::/10 */ { {0xfe,0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0xff,0xc0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, WEIGHT_LINK_LOCAL }, /* Disabled, ::/128 */ { {0x0,0x0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, WEIGHT_DISABLED } }; pj_addrinfo ai; pj_status_t status; /* May not be used if TRACE_ is disabled */ PJ_UNUSED_ARG(strip); #ifdef _MSC_VER /* Get rid of "uninitialized he variable" with MS compilers */ pj_bzero(&ai, sizeof(ai)); #endif cand_cnt = 0; pj_bzero(cand_addr, sizeof(cand_addr)); pj_bzero(cand_weight, sizeof(cand_weight)); for (i=0; iaddr.sa_family = (pj_uint16_t)af; PJ_SOCKADDR_RESET_LEN(addr); #if !defined(PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION) || \ PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION == 0 /* Get hostname's IP address */ count = 1; status = pj_getaddrinfo(af, pj_gethostname(), &count, &ai); if (status == PJ_SUCCESS) { pj_assert(ai.ai_addr.addr.sa_family == (pj_uint16_t)af); pj_sockaddr_copy_addr(&cand_addr[cand_cnt], &ai.ai_addr); pj_sockaddr_set_port(&cand_addr[cand_cnt], 0); cand_weight[cand_cnt] += WEIGHT_HOSTNAME; ++cand_cnt; TRACE_((THIS_FILE, "hostname IP is %s", pj_sockaddr_print(&ai.ai_addr, strip, sizeof(strip), 0))); } #else PJ_UNUSED_ARG(ai); #endif /* Get default interface (interface for default route) */ if (cand_cnt < PJ_ARRAY_SIZE(cand_addr)) { status = pj_getdefaultipinterface(af, addr); if (status == PJ_SUCCESS) { TRACE_((THIS_FILE, "default IP is %s", pj_sockaddr_print(addr, strip, sizeof(strip), 0))); pj_sockaddr_set_port(addr, 0); for (i=0; i= cand_cnt) { pj_sockaddr_copy_addr(&cand_addr[i], addr); ++cand_cnt; } } } /* Enumerate IP interfaces */ if (cand_cnt < PJ_ARRAY_SIZE(cand_addr)) { unsigned start_if = cand_cnt; count = PJ_ARRAY_SIZE(cand_addr) - start_if; status = pj_enum_ip_interface(af, &count, &cand_addr[start_if]); if (status == PJ_SUCCESS && count) { /* Clear the port number */ for (i=0; i cand_weight[selected_cand]) selected_cand = i; } /* If else fails, returns loopback interface as the last resort */ if (selected_cand == -1) { if (af==PJ_AF_INET) { addr->ipv4.sin_addr.s_addr = pj_htonl (0x7f000001); } else { pj_in6_addr *s6_addr; s6_addr = (pj_in6_addr*) pj_sockaddr_get_addr(addr); pj_bzero(s6_addr, sizeof(pj_in6_addr)); s6_addr->s6_addr[15] = 1; } TRACE_((THIS_FILE, "Loopback IP %s returned", pj_sockaddr_print(addr, strip, sizeof(strip), 0))); } else { pj_sockaddr_copy_addr(addr, &cand_addr[selected_cand]); TRACE_((THIS_FILE, "Candidate %s selected", pj_sockaddr_print(addr, strip, sizeof(strip), 0))); } return PJ_SUCCESS; } /* Get IP interface for sending to the specified destination */ PJ_DEF(pj_status_t) pj_getipinterface(int af, const pj_str_t *dst, pj_sockaddr *itf_addr, pj_bool_t allow_resolve, pj_sockaddr *p_dst_addr) { pj_sockaddr dst_addr; pj_sock_t fd; int len; pj_uint8_t zero[64]; pj_status_t status; pj_sockaddr_init(af, &dst_addr, NULL, 53); status = pj_inet_pton(af, dst, pj_sockaddr_get_addr(&dst_addr)); if (status != PJ_SUCCESS) { /* "dst" is not an IP address. */ if (allow_resolve) { status = pj_sockaddr_init(af, &dst_addr, dst, 53); } else { pj_str_t cp; if (af == PJ_AF_INET) { cp = pj_str("1.1.1.1"); } else { cp = pj_str("1::1"); } status = pj_sockaddr_init(af, &dst_addr, &cp, 53); } if (status != PJ_SUCCESS) return status; } /* Create UDP socket and connect() to the destination IP */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &fd); if (status != PJ_SUCCESS) { return status; } status = pj_sock_connect(fd, &dst_addr, pj_sockaddr_get_len(&dst_addr)); if (status != PJ_SUCCESS) { pj_sock_close(fd); return status; } len = sizeof(*itf_addr); status = pj_sock_getsockname(fd, itf_addr, &len); if (status != PJ_SUCCESS) { pj_sock_close(fd); return status; } pj_sock_close(fd); /* Check that the address returned is not zero */ pj_bzero(zero, sizeof(zero)); if (pj_memcmp(pj_sockaddr_get_addr(itf_addr), zero, pj_sockaddr_get_addr_len(itf_addr))==0) { return PJ_ENOTFOUND; } if (p_dst_addr) *p_dst_addr = dst_addr; return PJ_SUCCESS; } /* Get the default IP interface */ PJ_DEF(pj_status_t) pj_getdefaultipinterface(int af, pj_sockaddr *addr) { pj_str_t cp; if (af == PJ_AF_INET) { cp = pj_str("1.1.1.1"); } else { cp = pj_str("1::1"); } return pj_getipinterface(af, &cp, addr, PJ_FALSE, NULL); } /* * Bind socket at random port. */ PJ_DEF(pj_status_t) pj_sock_bind_random( pj_sock_t sockfd, const pj_sockaddr_t *addr, pj_uint16_t port_range, pj_uint16_t max_try) { pj_sockaddr bind_addr; int addr_len; pj_uint16_t base_port; pj_status_t status = PJ_SUCCESS; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(addr, PJ_EINVAL); pj_sockaddr_cp(&bind_addr, addr); addr_len = pj_sockaddr_get_len(addr); base_port = pj_sockaddr_get_port(addr); if (base_port == 0 || port_range == 0) { return pj_sock_bind(sockfd, &bind_addr, addr_len); } for (; max_try; --max_try) { pj_uint16_t port; port = (pj_uint16_t)(base_port + pj_rand() % (port_range + 1)); pj_sockaddr_set_port(&bind_addr, port); status = pj_sock_bind(sockfd, &bind_addr, addr_len); if (status == PJ_SUCCESS) break; } return status; } /* * Adjust socket send/receive buffer size. */ PJ_DEF(pj_status_t) pj_sock_setsockopt_sobuf( pj_sock_t sockfd, pj_uint16_t optname, pj_bool_t auto_retry, unsigned *buf_size) { pj_status_t status; int try_size, cur_size, i, step, size_len; enum { MAX_TRY = 20 }; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sockfd != PJ_INVALID_SOCKET && buf_size && *buf_size > 0 && (optname == pj_SO_RCVBUF() || optname == pj_SO_SNDBUF()), PJ_EINVAL); size_len = sizeof(cur_size); status = pj_sock_getsockopt(sockfd, pj_SOL_SOCKET(), optname, &cur_size, &size_len); if (status != PJ_SUCCESS) return status; try_size = *buf_size; step = (try_size - cur_size) / MAX_TRY; if (step < 4096) step = 4096; for (i = 0; i < (MAX_TRY-1); ++i) { if (try_size <= cur_size) { /* Done, return current size */ *buf_size = cur_size; break; } status = pj_sock_setsockopt(sockfd, pj_SOL_SOCKET(), optname, &try_size, sizeof(try_size)); if (status == PJ_SUCCESS) { status = pj_sock_getsockopt(sockfd, pj_SOL_SOCKET(), optname, &cur_size, &size_len); if (status != PJ_SUCCESS) { /* Ops! No info about current size, just return last try size * and quit. */ *buf_size = try_size; break; } } if (!auto_retry) break; try_size -= step; } return status; } /* Only need to implement these in DLL build */ #if defined(PJ_DLL) PJ_DEF(pj_uint16_t) pj_AF_UNSPEC(void) { return PJ_AF_UNSPEC; } PJ_DEF(pj_uint16_t) pj_AF_UNIX(void) { return PJ_AF_UNIX; } PJ_DEF(pj_uint16_t) pj_AF_INET(void) { return PJ_AF_INET; } PJ_DEF(pj_uint16_t) pj_AF_INET6(void) { return PJ_AF_INET6; } PJ_DEF(pj_uint16_t) pj_AF_PACKET(void) { return PJ_AF_PACKET; } PJ_DEF(pj_uint16_t) pj_AF_IRDA(void) { return PJ_AF_IRDA; } PJ_DEF(int) pj_SOCK_STREAM(void) { return PJ_SOCK_STREAM; } PJ_DEF(int) pj_SOCK_DGRAM(void) { return PJ_SOCK_DGRAM; } PJ_DEF(int) pj_SOCK_RAW(void) { return PJ_SOCK_RAW; } PJ_DEF(int) pj_SOCK_RDM(void) { return PJ_SOCK_RDM; } PJ_DEF(pj_uint16_t) pj_SOL_SOCKET(void) { return PJ_SOL_SOCKET; } PJ_DEF(pj_uint16_t) pj_SOL_IP(void) { return PJ_SOL_IP; } PJ_DEF(pj_uint16_t) pj_SOL_TCP(void) { return PJ_SOL_TCP; } PJ_DEF(pj_uint16_t) pj_SOL_UDP(void) { return PJ_SOL_UDP; } PJ_DEF(pj_uint16_t) pj_SOL_IPV6(void) { return PJ_SOL_IPV6; } PJ_DEF(int) pj_IP_TOS(void) { return PJ_IP_TOS; } PJ_DEF(int) pj_IPTOS_LOWDELAY(void) { return PJ_IPTOS_LOWDELAY; } PJ_DEF(int) pj_IPTOS_THROUGHPUT(void) { return PJ_IPTOS_THROUGHPUT; } PJ_DEF(int) pj_IPTOS_RELIABILITY(void) { return PJ_IPTOS_RELIABILITY; } PJ_DEF(int) pj_IPTOS_MINCOST(void) { return PJ_IPTOS_MINCOST; } PJ_DEF(pj_uint16_t) pj_SO_TYPE(void) { return PJ_SO_TYPE; } PJ_DEF(pj_uint16_t) pj_SO_RCVBUF(void) { return PJ_SO_RCVBUF; } PJ_DEF(pj_uint16_t) pj_SO_SNDBUF(void) { return PJ_SO_SNDBUF; } PJ_DEF(pj_uint16_t) pj_TCP_NODELAY(void) { return PJ_TCP_NODELAY; } PJ_DEF(pj_uint16_t) pj_SO_REUSEADDR(void) { return PJ_SO_REUSEADDR; } PJ_DEF(pj_uint16_t) pj_SO_NOSIGPIPE(void) { return PJ_SO_NOSIGPIPE; } PJ_DEF(pj_uint16_t) pj_SO_PRIORITY(void) { return PJ_SO_PRIORITY; } PJ_DEF(pj_uint16_t) pj_IP_MULTICAST_IF(void) { return PJ_IP_MULTICAST_IF; } PJ_DEF(pj_uint16_t) pj_IP_MULTICAST_TTL(void) { return PJ_IP_MULTICAST_TTL; } PJ_DEF(pj_uint16_t) pj_IP_MULTICAST_LOOP(void) { return PJ_IP_MULTICAST_LOOP; } PJ_DEF(pj_uint16_t) pj_IP_ADD_MEMBERSHIP(void) { return PJ_IP_ADD_MEMBERSHIP; } PJ_DEF(pj_uint16_t) pj_IP_DROP_MEMBERSHIP(void) { return PJ_IP_DROP_MEMBERSHIP; } PJ_DEF(int) pj_MSG_OOB(void) { return PJ_MSG_OOB; } PJ_DEF(int) pj_MSG_PEEK(void) { return PJ_MSG_PEEK; } PJ_DEF(int) pj_MSG_DONTROUTE(void) { return PJ_MSG_DONTROUTE; } #endif /* PJ_DLL */ ================================================ FILE: deps/pjsip/pjlib/src/pj/sock_linux_kernel.c ================================================ /* $Id: sock_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include /* pj_memcpy() */ #include /* PJ_CHECK_STACK() */ #include /* pj_gethostbyname() */ #include #include #include #include /* Linux kernel specific. */ #include #include //#include #include #include /* sys_xxx() */ #include /* FIONBIO */ #include /* for pj_gethostname() */ #define THIS_FILE "sock_linux_kernel.c" /* * Address families conversion. * The values here are indexed based on pj_addr_family-0xFF00. */ const pj_uint16_t PJ_AF_UNIX = AF_UNIX; const pj_uint16_t PJ_AF_INET = AF_INET; const pj_uint16_t PJ_AF_INET6 = AF_INET6; #ifdef AF_PACKET const pj_uint16_t PJ_AF_PACKET = AF_PACKET; #else # error "AF_PACKET undeclared!" #endif #ifdef AF_IRDA const pj_uint16_t PJ_AF_IRDA = AF_IRDA; #else # error "AF_IRDA undeclared!" #endif /* * Socket types conversion. * The values here are indexed based on pj_sock_type-0xFF00 */ const pj_uint16_t PJ_SOCK_STREAM= SOCK_STREAM; const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM; const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW; const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM; /* * Socket level values. */ const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET; #ifdef SOL_IP const pj_uint16_t PJ_SOL_IP = SOL_IP; #else # error "SOL_IP undeclared!" #endif /* SOL_IP */ #if defined(SOL_TCP) const pj_uint16_t PJ_SOL_TCP = SOL_TCP; #else # error "SOL_TCP undeclared!" #endif /* SOL_TCP */ #ifdef SOL_UDP const pj_uint16_t PJ_SOL_UDP = SOL_UDP; #else # error "SOL_UDP undeclared!" #endif #ifdef SOL_IPV6 const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6; #else # error "SOL_IPV6 undeclared!" #endif /* optname values. */ const pj_uint16_t PJ_SO_TYPE = SO_TYPE; const pj_uint16_t PJ_SO_RCVBUF = SO_RCVBUF; const pj_uint16_t PJ_SO_SNDBUF = SO_SNDBUF; /* * Convert 16-bit value from network byte order to host byte order. */ PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort) { return ntohs(netshort); } /* * Convert 16-bit value from host byte order to network byte order. */ PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort) { return htons(hostshort); } /* * Convert 32-bit value from network byte order to host byte order. */ PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong) { return ntohl(netlong); } /* * Convert 32-bit value from host byte order to network byte order. */ PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong) { return htonl(hostlong); } /* * Convert an Internet host address given in network byte order * to string in standard numbers and dots notation. */ PJ_DEF(char*) pj_inet_ntoa(pj_in_addr in) { #define UC(b) (((int)b)&0xff) static char b[18]; char *p; p = (char *)∈ pj_snprintf(b, sizeof(b), "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); return b; } /* * This function converts the Internet host address ccp from the standard * numbers-and-dots notation into binary data and stores it in the structure * that inp points to. */ PJ_DEF(int) pj_inet_aton(const pj_str_t *ccp, struct pj_in_addr *addr) { pj_uint32_t val; int base, n; char c; unsigned parts[4]; unsigned *pp = parts; char cp_copy[18]; char *cp = cp_copy; addr->s_addr = PJ_INADDR_NONE; if (ccp->slen > 15) return 0; pj_memcpy(cp, ccp->ptr, ccp->slen); cp[ccp->slen] = '\0'; c = *cp; for (;;) { /* * Collect number up to ``.''. * Values are specified as for C: * 0x=hex, 0=octal, isdigit=decimal. */ if (!pj_isdigit((int)c)) return (0); val = 0; base = 10; if (c == '0') { c = *++cp; if (c == 'x' || c == 'X') base = 16, c = *++cp; else base = 8; } for (;;) { if (pj_isascii((int)c) && pj_isdigit((int)c)) { val = (val * base) + (c - '0'); c = *++cp; } else if (base==16 && pj_isascii((int)c) && pj_isxdigit((int)c)) { val = (val << 4) | (c + 10 - (pj_islower((int)c) ? 'a' : 'A')); c = *++cp; } else break; } if (c == '.') { /* * Internet format: * a.b.c.d * a.b.c (with c treated as 16 bits) * a.b (with b treated as 24 bits) */ if (pp >= parts + 3) return (0); *pp++ = val; c = *++cp; } else break; } /* * Check for trailing characters. */ if (c != '\0' && (!pj_isascii((int)c) || !pj_isspace((int)c))) return (0); /* * Concoct the address according to * the number of parts specified. */ n = pp - parts + 1; switch (n) { case 0: return (0); /* initial nondigit */ case 1: /* a -- 32 bits */ break; case 2: /* a.b -- 8.24 bits */ if (val > 0xffffff) return (0); val |= parts[0] << 24; break; case 3: /* a.b.c -- 8.8.16 bits */ if (val > 0xffff) return (0); val |= (parts[0] << 24) | (parts[1] << 16); break; case 4: /* a.b.c.d -- 8.8.8.8 bits */ if (val > 0xff) return (0); val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); break; } if (addr) addr->s_addr = pj_htonl(val); return (1); } /* * Convert address string with numbers and dots to binary IP address. */ PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp) { pj_in_addr addr; pj_inet_aton(cp, &addr); return addr; } /* * Set the IP address of an IP socket address from string address, * with resolving the host if necessary. The string address may be in a * standard numbers and dots notation or may be a hostname. If hostname * is specified, then the function will resolve the host into the IP * address. */ PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, const pj_str_t *str_addr) { PJ_CHECK_STACK(); pj_assert(str_addr && str_addr->slen < PJ_MAX_HOSTNAME); addr->sin_family = AF_INET; if (str_addr && str_addr->slen) { addr->sin_addr = pj_inet_addr(str_addr); if (addr->sin_addr.s_addr == PJ_INADDR_NONE) { pj_hostent he; if (pj_gethostbyname(str_addr, &he) == 0) { addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr; } else { addr->sin_addr.s_addr = PJ_INADDR_NONE; return -1; } } } else { addr->sin_addr.s_addr = 0; } return PJ_SUCCESS; } /* * Set the IP address and port of an IP socket address. * The string address may be in a standard numbers and dots notation or * may be a hostname. If hostname is specified, then the function will * resolve the host into the IP address. */ PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr, const pj_str_t *str_addr, pj_uint16_t port) { pj_assert(addr && str_addr); addr->sin_family = PJ_AF_INET; pj_sockaddr_in_set_port(addr, port); return pj_sockaddr_in_set_str_addr(addr, str_addr); } /* * Get hostname. */ PJ_DEF(const pj_str_t*) pj_gethostname(void) { static char buf[PJ_MAX_HOSTNAME]; static pj_str_t hostname; PJ_CHECK_STACK(); if (hostname.ptr == NULL) { hostname.ptr = buf; down_read(&uts_sem); hostname.slen = strlen(system_utsname.nodename); if (hostname.slen > PJ_MAX_HOSTNAME) { hostname.ptr[0] = '\0'; hostname.slen = 0; } else { pj_memcpy(hostname.ptr, system_utsname.nodename, hostname.slen); } up_read(&uts_sem); } return &hostname; } /* * Get first IP address associated with the hostname. */ PJ_DEF(pj_in_addr) pj_gethostaddr(void) { pj_sockaddr_in addr; const pj_str_t *hostname = pj_gethostname(); pj_sockaddr_in_set_str_addr(&addr, hostname); return addr.sin_addr; } /* * Create new socket/endpoint for communication and returns a descriptor. */ PJ_DEF(pj_status_t) pj_sock_socket(int af, int type, int proto, pj_sock_t *sock_fd) { long result; PJ_CHECK_STACK(); /* Sanity checks. */ PJ_ASSERT_RETURN(PJ_INVALID_SOCKET == -1 && sock_fd != NULL, PJ_EINVAL); /* Initialize returned socket */ *sock_fd = PJ_INVALID_SOCKET; /* Create socket. */ result = sys_socket(af, type, proto); if (result < 0) { return PJ_RETURN_OS_ERROR((-result)); } *sock_fd = result; return PJ_SUCCESS; } /* * Bind socket. */ PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sockfd, const pj_sockaddr_t *addr, int len) { long err; mm_segment_t oldfs; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(addr!=NULL && len >= sizeof(struct pj_sockaddr), PJ_EINVAL); oldfs = get_fs(); set_fs(KERNEL_DS); err = sys_bind(sockfd, (struct sockaddr*)addr, len); set_fs(oldfs); if (err) return PJ_RETURN_OS_ERROR(-err); else return PJ_SUCCESS; } /* * Bind socket. */ PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd, pj_uint32_t addr32, pj_uint16_t port) { pj_sockaddr_in addr; PJ_CHECK_STACK(); addr.sin_family = PJ_AF_INET; addr.sin_addr.s_addr = pj_htonl(addr32); addr.sin_port = pj_htons(port); return pj_sock_bind(sockfd, &addr, sizeof(pj_sockaddr_in)); } /* * Close socket. */ PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sockfd) { long err; err = sys_close(sockfd); if (err != 0) return PJ_RETURN_OS_ERROR(-err); else return PJ_SUCCESS; } /* * Get remote's name. */ PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sockfd, pj_sockaddr_t *addr, int *namelen) { mm_segment_t oldfs; long err; PJ_CHECK_STACK(); oldfs = get_fs(); set_fs(KERNEL_DS); err = sys_getpeername( sockfd, addr, namelen); set_fs(oldfs); if (err) return PJ_RETURN_OS_ERROR(-err); else return PJ_SUCCESS; } /* * Get socket name. */ PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd, pj_sockaddr_t *addr, int *namelen) { mm_segment_t oldfs; int err; PJ_CHECK_STACK(); oldfs = get_fs(); set_fs(KERNEL_DS); err = sys_getsockname( sockfd, addr, namelen ); set_fs(oldfs); if (err) return PJ_RETURN_OS_ERROR(-err); else return PJ_SUCCESS; } /* * Send data */ PJ_DEF(pj_status_t) pj_sock_send( pj_sock_t sockfd, const void *buf, pj_ssize_t *len, unsigned flags) { return pj_sock_sendto(sockfd, buf, len, flags, NULL, 0); } /* * Send data. */ PJ_DEF(pj_status_t) pj_sock_sendto( pj_sock_t sockfd, const void *buff, pj_ssize_t *len, unsigned flags, const pj_sockaddr_t *addr, int addr_len) { long err; mm_segment_t oldfs; PJ_CHECK_STACK(); oldfs = get_fs(); set_fs(KERNEL_DS); err = *len = sys_sendto( sockfd, (void*)buff, *len, flags, (void*)addr, addr_len ); set_fs(oldfs); if (err >= 0) { return PJ_SUCCESS; } else { return PJ_RETURN_OS_ERROR(-err); } } /* * Receive data. */ PJ_DEF(pj_status_t) pj_sock_recv( pj_sock_t sockfd, void *buf, pj_ssize_t *len, unsigned flags) { return pj_sock_recvfrom(sockfd, buf, len, flags, NULL, NULL); } /* * Receive data. */ PJ_DEF(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd, void *buff, pj_ssize_t *size, unsigned flags, pj_sockaddr_t *from, int *fromlen) { mm_segment_t oldfs; long err; PJ_CHECK_STACK(); oldfs = get_fs(); set_fs(KERNEL_DS); err = *size = sys_recvfrom( sockfd, buff, *size, flags, from, fromlen); set_fs(oldfs); if (err >= 0) { return PJ_SUCCESS; } else { return PJ_RETURN_OS_ERROR(-err); } } /* * Get socket option. */ PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd, pj_uint16_t level, pj_uint16_t optname, void *optval, int *optlen) { mm_segment_t oldfs; long err; PJ_CHECK_STACK(); oldfs = get_fs(); set_fs(KERNEL_DS); err = sys_getsockopt( sockfd, level, optname, optval, optlen); set_fs(oldfs); if (err) return PJ_RETURN_OS_ERROR(-err); else return PJ_SUCCESS; } /* * Set socket option. */ PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd, pj_uint16_t level, pj_uint16_t optname, const void *optval, int optlen) { long err; mm_segment_t oldfs; PJ_CHECK_STACK(); oldfs = get_fs(); set_fs(KERNEL_DS); err = sys_setsockopt( sockfd, level, optname, (void*)optval, optlen); set_fs(oldfs); if (err) return PJ_RETURN_OS_ERROR(-err); else return PJ_SUCCESS; } /* * Set socket option. */ PJ_DEF(pj_status_t) pj_sock_setsockopt_params( pj_sock_t sockfd, const pj_sockopt_params *params) { unsigned int i = 0; pj_status_t retval = PJ_SUCCESS; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(params, PJ_EINVAL); for (;icnt && ioptions[i].level, params->options[i].optname, params->options[i].optval, params->options[i].optlen); if (status != PJ_SUCCESS) { retval = status; PJ_PERROR(4,(THIS_FILE, status, "Warning: error applying sock opt %d", params->options[i].optname)); } } return retval; } /* * Shutdown socket. */ #if PJ_HAS_TCP PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd, int how) { long err; PJ_CHECK_STACK(); err = sys_shutdown(sockfd, how); if (err) return PJ_RETURN_OS_ERROR(-err); else return PJ_SUCCESS; } /* * Start listening to incoming connections. */ PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sockfd, int backlog) { long err; PJ_CHECK_STACK(); err = sys_listen( sockfd, backlog ); if (err) return PJ_RETURN_OS_ERROR(-err); else return PJ_SUCCESS; } /* * Connect socket. */ PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sockfd, const pj_sockaddr_t *addr, int namelen) { long err; mm_segment_t oldfs; PJ_CHECK_STACK(); oldfs = get_fs(); set_fs(KERNEL_DS); err = sys_connect( sockfd, (void*)addr, namelen ); set_fs(oldfs); if (err) return PJ_RETURN_OS_ERROR(-err); else return PJ_SUCCESS; } /* * Accept incoming connections */ PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t sockfd, pj_sock_t *newsockfd, pj_sockaddr_t *addr, int *addrlen) { long err; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(newsockfd != NULL, PJ_EINVAL); err = sys_accept( sockfd, addr, addrlen); if (err < 0) { *newsockfd = PJ_INVALID_SOCKET; return PJ_RETURN_OS_ERROR(-err); } else { *newsockfd = err; return PJ_SUCCESS; } } #endif /* PJ_HAS_TCP */ /* * Permission to steal inet_ntoa() and inet_aton() as long as this notice below * is included: */ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ ================================================ FILE: deps/pjsip/pjlib/src/pj/sock_qos_bsd.c ================================================ /* $Id: sock_qos_bsd.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include /* This is the implementation of QoS with BSD socket's setsockopt(), * using IP_TOS and SO_PRIORITY */ #if !defined(PJ_QOS_IMPLEMENTATION) || PJ_QOS_IMPLEMENTATION==PJ_QOS_BSD PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, pj_qos_params *param) { pj_status_t last_err = PJ_ENOTSUP; pj_status_t status; /* No op? */ if (!param->flags) return PJ_SUCCESS; /* Clear WMM field since we don't support it */ param->flags &= ~(PJ_QOS_PARAM_HAS_WMM); /* Set TOS/DSCP */ if (param->flags & PJ_QOS_PARAM_HAS_DSCP) { /* Value is dscp_val << 2 */ int val = (param->dscp_val << 2); status = pj_sock_setsockopt(sock, pj_SOL_IP(), pj_IP_TOS(), &val, sizeof(val)); if (status != PJ_SUCCESS) { param->flags &= ~(PJ_QOS_PARAM_HAS_DSCP); last_err = status; } } /* Set SO_PRIORITY */ if (param->flags & PJ_QOS_PARAM_HAS_SO_PRIO) { int val = param->so_prio; status = pj_sock_setsockopt(sock, pj_SOL_SOCKET(), pj_SO_PRIORITY(), &val, sizeof(val)); if (status != PJ_SUCCESS) { param->flags &= ~(PJ_QOS_PARAM_HAS_SO_PRIO); last_err = status; } } return param->flags ? PJ_SUCCESS : last_err; } PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, pj_qos_type type) { pj_qos_params param; pj_status_t status; status = pj_qos_get_params(type, ¶m); if (status != PJ_SUCCESS) return status; return pj_sock_set_qos_params(sock, ¶m); } PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, pj_qos_params *p_param) { pj_status_t last_err = PJ_ENOTSUP; int val, optlen; pj_status_t status; pj_bzero(p_param, sizeof(*p_param)); /* Get DSCP/TOS value */ optlen = sizeof(val); status = pj_sock_getsockopt(sock, pj_SOL_IP(), pj_IP_TOS(), &val, &optlen); if (status == PJ_SUCCESS) { p_param->flags |= PJ_QOS_PARAM_HAS_DSCP; p_param->dscp_val = (pj_uint8_t)(val >> 2); } else { last_err = status; } /* Get SO_PRIORITY */ optlen = sizeof(val); status = pj_sock_getsockopt(sock, pj_SOL_SOCKET(), pj_SO_PRIORITY(), &val, &optlen); if (status == PJ_SUCCESS) { p_param->flags |= PJ_QOS_PARAM_HAS_SO_PRIO; p_param->so_prio = (pj_uint8_t)val; } else { last_err = status; } /* WMM is not supported */ return p_param->flags ? PJ_SUCCESS : last_err; } PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, pj_qos_type *p_type) { pj_qos_params param; pj_status_t status; status = pj_sock_get_qos_params(sock, ¶m); if (status != PJ_SUCCESS) return status; return pj_qos_get_type(¶m, p_type); } #endif /* PJ_QOS_IMPLEMENTATION */ ================================================ FILE: deps/pjsip/pjlib/src/pj/sock_qos_common.c ================================================ /* $Id: sock_qos_common.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #define THIS_FILE "sock_qos_common.c" #define ALL_FLAGS (PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_SO_PRIO | \ PJ_QOS_PARAM_HAS_WMM) /* "Standard" mapping between traffic type and QoS params */ static const pj_qos_params qos_map[] = { /* flags dscp prio wmm_prio */ {ALL_FLAGS, 0x00, 0, PJ_QOS_WMM_PRIO_BULK_EFFORT}, /* BE */ {ALL_FLAGS, 0x08, 2, PJ_QOS_WMM_PRIO_BULK}, /* BK */ {ALL_FLAGS, 0x28, 5, PJ_QOS_WMM_PRIO_VIDEO}, /* VI */ {ALL_FLAGS, 0x30, 6, PJ_QOS_WMM_PRIO_VOICE}, /* VO */ {ALL_FLAGS, 0x38, 7, PJ_QOS_WMM_PRIO_VOICE} /* CO */ }; /* Retrieve the mapping for the specified type */ PJ_DEF(pj_status_t) pj_qos_get_params(pj_qos_type type, pj_qos_params *p_param) { PJ_ASSERT_RETURN(type<=PJ_QOS_TYPE_CONTROL && p_param, PJ_EINVAL); pj_memcpy(p_param, &qos_map[type], sizeof(*p_param)); return PJ_SUCCESS; } /* Get the matching traffic type */ PJ_DEF(pj_status_t) pj_qos_get_type( const pj_qos_params *param, pj_qos_type *p_type) { unsigned dscp_type = PJ_QOS_TYPE_BEST_EFFORT, prio_type = PJ_QOS_TYPE_BEST_EFFORT, wmm_type = PJ_QOS_TYPE_BEST_EFFORT; unsigned i, count=0; PJ_ASSERT_RETURN(param && p_type, PJ_EINVAL); if (param->flags & PJ_QOS_PARAM_HAS_DSCP) { for (i=0; i<=PJ_QOS_TYPE_CONTROL; ++i) { if (param->dscp_val >= qos_map[i].dscp_val) dscp_type = (pj_qos_type)i; } ++count; } if (param->flags & PJ_QOS_PARAM_HAS_SO_PRIO) { for (i=0; i<=PJ_QOS_TYPE_CONTROL; ++i) { if (param->so_prio >= qos_map[i].so_prio) prio_type = (pj_qos_type)i; } ++count; } if (param->flags & PJ_QOS_PARAM_HAS_WMM) { for (i=0; i<=PJ_QOS_TYPE_CONTROL; ++i) { if (param->wmm_prio >= qos_map[i].wmm_prio) wmm_type = (pj_qos_type)i; } ++count; } if (count) *p_type = (pj_qos_type)((dscp_type + prio_type + wmm_type) / count); else *p_type = PJ_QOS_TYPE_BEST_EFFORT; return PJ_SUCCESS; } /* Apply QoS */ PJ_DEF(pj_status_t) pj_sock_apply_qos( pj_sock_t sock, pj_qos_type qos_type, pj_qos_params *qos_params, unsigned log_level, const char *log_sender, const char *sock_name) { pj_status_t qos_type_rc = PJ_SUCCESS, qos_params_rc = PJ_SUCCESS; if (!log_sender) log_sender = THIS_FILE; if (!sock_name) sock_name = "socket"; if (qos_type != PJ_QOS_TYPE_BEST_EFFORT) { qos_type_rc = pj_sock_set_qos_type(sock, qos_type); if (qos_type_rc != PJ_SUCCESS) { pj_perror(log_level, log_sender, qos_type_rc, "Error setting QoS type %d to %s", qos_type, sock_name); } } if (qos_params && qos_params->flags) { qos_params_rc = pj_sock_set_qos_params(sock, qos_params); if (qos_params_rc != PJ_SUCCESS) { pj_perror(log_level, log_sender, qos_params_rc, "Error setting QoS params (flags=%d) to %s", qos_params->flags, sock_name); if (qos_type_rc != PJ_SUCCESS) return qos_params_rc; } } else if (qos_type_rc != PJ_SUCCESS) return qos_type_rc; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_sock_apply_qos2( pj_sock_t sock, pj_qos_type qos_type, const pj_qos_params *qos_params, unsigned log_level, const char *log_sender, const char *sock_name) { pj_qos_params qos_params_buf, *qos_params_copy = NULL; if (qos_params) { pj_memcpy(&qos_params_buf, qos_params, sizeof(*qos_params)); qos_params_copy = &qos_params_buf; } return pj_sock_apply_qos(sock, qos_type, qos_params_copy, log_level, log_sender, sock_name); } ================================================ FILE: deps/pjsip/pjlib/src/pj/sock_qos_dummy.c ================================================ /* $Id: sock_qos_dummy.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include /* Dummy implementation of QoS API. * (this is controlled by pjlib's config.h) */ #if defined(PJ_QOS_IMPLEMENTATION) && PJ_QOS_IMPLEMENTATION==PJ_QOS_DUMMY #define THIS_FILE "sock_qos_dummy.c" PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, pj_qos_params *param) { PJ_UNUSED_ARG(sock); PJ_UNUSED_ARG(param); PJ_LOG(4,(THIS_FILE, "pj_sock_set_qos_params() is not implemented " "for this platform")); return PJ_ENOTSUP; } PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, pj_qos_type type) { PJ_UNUSED_ARG(sock); PJ_UNUSED_ARG(type); PJ_LOG(4,(THIS_FILE, "pj_sock_set_qos_type() is not implemented " "for this platform")); return PJ_ENOTSUP; } PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, pj_qos_params *p_param) { PJ_UNUSED_ARG(sock); PJ_UNUSED_ARG(p_param); PJ_LOG(4,(THIS_FILE, "pj_sock_get_qos_params() is not implemented " "for this platform")); return PJ_ENOTSUP; } PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, pj_qos_type *p_type) { PJ_UNUSED_ARG(sock); PJ_UNUSED_ARG(p_type); PJ_LOG(4,(THIS_FILE, "pj_sock_get_qos_type() is not implemented " "for this platform")); return PJ_ENOTSUP; } #endif /* PJ_QOS_DUMMY */ ================================================ FILE: deps/pjsip/pjlib/src/pj/sock_qos_symbian.cpp ================================================ /* $Id: sock_qos_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "os_symbian.h" PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, pj_qos_params *param) { PJ_ASSERT_RETURN(sock!=0 && sock!=PJ_INVALID_SOCKET, PJ_EINVAL); CPjSocket *pjsock = (CPjSocket*)sock; RSocket & rsock = pjsock->Socket(); pj_status_t last_err = PJ_ENOTSUP; /* SO_PRIORITY and WMM are not supported */ param->flags &= ~(PJ_QOS_PARAM_HAS_SO_PRIO | PJ_QOS_PARAM_HAS_WMM); if (param->flags & PJ_QOS_PARAM_HAS_DSCP) { TInt err; err = rsock.SetOpt(KSoIpTOS, KProtocolInetIp, (param->dscp_val << 2)); if (err != KErrNone) { last_err = PJ_RETURN_OS_ERROR(err); param->flags &= ~(PJ_QOS_PARAM_HAS_DSCP); } } return param->flags ? PJ_SUCCESS : last_err; } PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, pj_qos_type type) { pj_qos_params param; pj_status_t status; status = pj_qos_get_params(type, ¶m); if (status != PJ_SUCCESS) return status; return pj_sock_set_qos_params(sock, ¶m); } PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, pj_qos_params *p_param) { PJ_ASSERT_RETURN(sock!=0 && sock!=PJ_INVALID_SOCKET, PJ_EINVAL); CPjSocket *pjsock = (CPjSocket*)sock; RSocket & rsock = pjsock->Socket(); TInt err, dscp; pj_bzero(p_param, sizeof(*p_param)); err = rsock.GetOpt(KSoIpTOS, KProtocolInetIp, dscp); if (err == KErrNone) { p_param->flags |= PJ_QOS_PARAM_HAS_DSCP; p_param->dscp_val = (dscp >> 2); return PJ_SUCCESS; } else { return PJ_RETURN_OS_ERROR(err); } } PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, pj_qos_type *p_type) { pj_qos_params param; pj_status_t status; status = pj_sock_get_qos_params(sock, ¶m); if (status != PJ_SUCCESS) return status; return pj_qos_get_type(¶m, p_type); } ================================================ FILE: deps/pjsip/pjlib/src/pj/sock_qos_wm.c ================================================ /* $Id: sock_qos_wm.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include /* QoS implementation for Windows Mobile 6, must be enabled explicitly * (this is controlled by pjlib's config.h) */ #if defined(PJ_QOS_IMPLEMENTATION) && PJ_QOS_IMPLEMENTATION==PJ_QOS_WM #define THIS_FILE "sock_qos_wm.c" /* Mapping between our traffic type and WM's DSCP traffic types */ static const int dscp_map[] = { DSCPBestEffort, DSCPBackground, DSCPVideo, DSCPAudio, DSCPControl }; PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, pj_qos_params *param) { PJ_UNUSED_ARG(sock); PJ_UNUSED_ARG(param); PJ_LOG(4,(THIS_FILE, "pj_sock_set_qos_params() is not implemented " "for this platform")); return PJ_ENOTSUP; } PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, pj_qos_type type) { int value; PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(dscp_map), PJ_EINVAL); value = dscp_map[type]; return pj_sock_setsockopt(sock, IPPROTO_IP, IP_DSCP_TRAFFIC_TYPE, &value, sizeof(value)); } PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, pj_qos_params *p_param) { PJ_UNUSED_ARG(sock); PJ_UNUSED_ARG(p_param); PJ_LOG(4,(THIS_FILE, "pj_sock_get_qos_params() is not implemented " "for this platform")); return PJ_ENOTSUP; } PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, pj_qos_type *p_type) { pj_status_t status; int value, optlen; unsigned i; optlen = sizeof(value); value = 0; status = pj_sock_getsockopt(sock, IPPROTO_IP, IP_DSCP_TRAFFIC_TYPE, &value, &optlen); if (status != PJ_SUCCESS) return status; *p_type = PJ_QOS_TYPE_BEST_EFFORT; for (i=0; i * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H!=0 # include #endif #if defined(PJ_HAS_SYS_TIME_H) && PJ_HAS_SYS_TIME_H!=0 # include #endif #ifdef _MSC_VER # pragma warning(disable: 4018) // Signed/unsigned mismatch in FD_* # pragma warning(disable: 4389) // Signed/unsigned mismatch in FD_* #endif #define PART_FDSET(ps) ((fd_set*)&ps->data[1]) #define PART_FDSET_OR_NULL(ps) (ps ? PART_FDSET(ps) : NULL) #define PART_COUNT(ps) (ps->data[0]) PJ_DEF(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp) { PJ_CHECK_STACK(); pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set)); FD_ZERO(PART_FDSET(fdsetp)); PART_COUNT(fdsetp) = 0; } PJ_DEF(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp) { PJ_CHECK_STACK(); pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set)); if (!PJ_FD_ISSET(fd, fdsetp)) ++PART_COUNT(fdsetp); FD_SET(fd, PART_FDSET(fdsetp)); } PJ_DEF(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp) { PJ_CHECK_STACK(); pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set)); if (PJ_FD_ISSET(fd, fdsetp)) --PART_COUNT(fdsetp); FD_CLR(fd, PART_FDSET(fdsetp)); } PJ_DEF(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set), 0); return FD_ISSET(fd, PART_FDSET(fdsetp)); } PJ_DEF(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp) { return PART_COUNT(fdsetp); } PJ_DEF(int) pj_sock_select( int n, pj_fd_set_t *readfds, pj_fd_set_t *writefds, pj_fd_set_t *exceptfds, const pj_time_val *timeout) { struct timeval os_timeout, *p_os_timeout; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set), PJ_EBUG); if (timeout) { os_timeout.tv_sec = timeout->sec; os_timeout.tv_usec = timeout->msec * 1000; p_os_timeout = &os_timeout; } else { p_os_timeout = NULL; } return select(n, PART_FDSET_OR_NULL(readfds), PART_FDSET_OR_NULL(writefds), PART_FDSET_OR_NULL(exceptfds), p_os_timeout); } ================================================ FILE: deps/pjsip/pjlib/src/pj/sock_select_symbian.cpp ================================================ /* $Id: sock_select_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "os_symbian.h" struct symbian_fd_set { unsigned count; CPjSocket *sock[PJ_IOQUEUE_MAX_HANDLES]; }; PJ_DEF(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp) { symbian_fd_set *fds = (symbian_fd_set *)fdsetp; fds->count = 0; } PJ_DEF(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp) { symbian_fd_set *fds = (symbian_fd_set *)fdsetp; PJ_ASSERT_ON_FAIL(fds->count < PJ_IOQUEUE_MAX_HANDLES, return); fds->sock[fds->count++] = (CPjSocket*)fd; } PJ_DEF(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp) { symbian_fd_set *fds = (symbian_fd_set *)fdsetp; unsigned i; for (i=0; icount; ++i) { if (fds->sock[i] == (CPjSocket*)fd) { pj_array_erase(fds->sock, sizeof(fds->sock[0]), fds->count, i); --fds->count; return; } } } PJ_DEF(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp) { symbian_fd_set *fds = (symbian_fd_set *)fdsetp; unsigned i; for (i=0; icount; ++i) { if (fds->sock[i] == (CPjSocket*)fd) { return PJ_TRUE; } } return PJ_FALSE; } PJ_DEF(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp) { symbian_fd_set *fds = (symbian_fd_set *)fdsetp; return fds->count; } PJ_DEF(int) pj_sock_select( int n, pj_fd_set_t *readfds, pj_fd_set_t *writefds, pj_fd_set_t *exceptfds, const pj_time_val *timeout) { CPjTimeoutTimer *pjTimer; unsigned i; PJ_UNUSED_ARG(n); PJ_UNUSED_ARG(writefds); PJ_UNUSED_ARG(exceptfds); if (timeout) { pjTimer = PjSymbianOS::Instance()->SelectTimeoutTimer(); pjTimer->StartTimer(timeout->sec*1000 + timeout->msec); } else { pjTimer = NULL; } /* Scan for readable sockets */ if (readfds) { symbian_fd_set *fds = (symbian_fd_set *)readfds; do { /* Scan sockets for readily available data */ for (i=0; icount; ++i) { CPjSocket *pjsock = fds->sock[i]; if (pjsock->Reader()) { if (pjsock->Reader()->HasData() && !pjsock->Reader()->IsActive()) { /* Found socket with data ready */ PJ_FD_ZERO(readfds); PJ_FD_SET((pj_sock_t)pjsock, readfds); /* Cancel timer, if any */ if (pjTimer) { pjTimer->Cancel(); } /* Clear writable and exception fd_set */ if (writefds) PJ_FD_ZERO(writefds); if (exceptfds) PJ_FD_ZERO(exceptfds); return 1; } else if (!pjsock->Reader()->IsActive()) pjsock->Reader()->StartRecvFrom(); } else { pjsock->CreateReader(); pjsock->Reader()->StartRecvFrom(); } } PjSymbianOS::Instance()->WaitForActiveObjects(); } while (pjTimer==NULL || !pjTimer->HasTimedOut()); } /* Timeout */ if (readfds) PJ_FD_ZERO(readfds); if (writefds) PJ_FD_ZERO(writefds); if (exceptfds) PJ_FD_ZERO(exceptfds); return 0; } ================================================ FILE: deps/pjsip/pjlib/src/pj/sock_symbian.cpp ================================================ /* $Id: sock_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include "os_symbian.h" /* * Address families. */ const pj_uint16_t PJ_AF_UNSPEC = KAFUnspec; const pj_uint16_t PJ_AF_UNIX = 0xFFFF; const pj_uint16_t PJ_AF_INET = KAfInet; const pj_uint16_t PJ_AF_INET6 = KAfInet6; const pj_uint16_t PJ_AF_PACKET = 0xFFFF; const pj_uint16_t PJ_AF_IRDA = 0xFFFF; /* * Socket types conversion. * The values here are indexed based on pj_sock_type */ const pj_uint16_t PJ_SOCK_STREAM= KSockStream; const pj_uint16_t PJ_SOCK_DGRAM = KSockDatagram; const pj_uint16_t PJ_SOCK_RAW = 0xFFFF; const pj_uint16_t PJ_SOCK_RDM = 0xFFFF; /* we don't support setsockopt(), these are just dummy values */ const pj_uint16_t PJ_SOL_SOCKET = 0xFFFF; const pj_uint16_t PJ_SOL_IP = 0xFFFF; const pj_uint16_t PJ_SOL_TCP = 0xFFFF; const pj_uint16_t PJ_SOL_UDP = 0xFFFF; const pj_uint16_t PJ_SOL_IPV6 = 0xFFFF; const pj_uint16_t PJ_SO_NOSIGPIPE = 0xFFFF; /* TOS */ const pj_uint16_t PJ_IP_TOS = 0; const pj_uint16_t PJ_IPTOS_LOWDELAY = 0; const pj_uint16_t PJ_IPTOS_THROUGHPUT = 0; const pj_uint16_t PJ_IPTOS_RELIABILITY = 0; const pj_uint16_t PJ_IPTOS_MINCOST = 0; /* Misc */ const pj_uint16_t PJ_TCP_NODELAY = 0xFFFF; const pj_uint16_t PJ_SO_REUSEADDR = 0xFFFF; const pj_uint16_t PJ_SO_PRIORITY = 0xFFFF; /* ioctl() is also not supported. */ const pj_uint16_t PJ_SO_TYPE = 0xFFFF; const pj_uint16_t PJ_SO_RCVBUF = 0xFFFF; const pj_uint16_t PJ_SO_SNDBUF = 0xFFFF; /* IP multicast is also not supported. */ const pj_uint16_t PJ_IP_MULTICAST_IF = 0xFFFF; const pj_uint16_t PJ_IP_MULTICAST_TTL = 0xFFFF; const pj_uint16_t PJ_IP_MULTICAST_LOOP = 0xFFFF; const pj_uint16_t PJ_IP_ADD_MEMBERSHIP = 0xFFFF; const pj_uint16_t PJ_IP_DROP_MEMBERSHIP = 0xFFFF; /* Flags */ const int PJ_MSG_OOB = 0; const int PJ_MSG_PEEK = KSockReadPeek; const int PJ_MSG_DONTROUTE = 0; ///////////////////////////////////////////////////////////////////////////// // // CPjSocket implementation. // (declaration is in os_symbian.h) // CPjSocket::~CPjSocket() { DestroyReader(); sock_.Close(); } // Create socket reader. CPjSocketReader *CPjSocket::CreateReader(unsigned max_len) { pj_assert(sockReader_ == NULL); return sockReader_ = CPjSocketReader::NewL(*this, max_len); } // Delete socket reader when it's not wanted. void CPjSocket::DestroyReader() { if (sockReader_) { sockReader_->Cancel(); delete sockReader_; sockReader_ = NULL; } } ///////////////////////////////////////////////////////////////////////////// // // CPjSocketReader implementation // (declaration in os_symbian.h) // CPjSocketReader::CPjSocketReader(CPjSocket &sock) : CActive(EPriorityStandard), sock_(sock), buffer_(NULL, 0), readCb_(NULL), key_(NULL) { } void CPjSocketReader::ConstructL(unsigned max_len) { isDatagram_ = sock_.IsDatagram(); TUint8 *ptr = new TUint8[max_len]; buffer_.Set(ptr, 0, (TInt)max_len); CActiveScheduler::Add(this); } CPjSocketReader *CPjSocketReader::NewL(CPjSocket &sock, unsigned max_len) { CPjSocketReader *self = new (ELeave) CPjSocketReader(sock); CleanupStack::PushL(self); self->ConstructL(max_len); CleanupStack::Pop(self); return self; } CPjSocketReader::~CPjSocketReader() { const TUint8 *data = buffer_.Ptr(); delete [] data; } void CPjSocketReader::StartRecv(void (*cb)(void *key), void *key, TDes8 *aDesc, TUint flags) { StartRecvFrom(cb, key, aDesc, flags, NULL); } void CPjSocketReader::StartRecvFrom(void (*cb)(void *key), void *key, TDes8 *aDesc, TUint flags, TSockAddr *fromAddr) { readCb_ = cb; key_ = key; if (aDesc == NULL) aDesc = &buffer_; if (fromAddr == NULL) fromAddr = &recvAddr_; sock_.Socket().RecvFrom(*aDesc, *fromAddr, flags, iStatus); SetActive(); } void CPjSocketReader::DoCancel() { sock_.Socket().CancelRecv(); } void CPjSocketReader::RunL() { void (*old_cb)(void *key) = readCb_; void *old_key = key_; readCb_ = NULL; key_ = NULL; if (old_cb) { (*old_cb)(old_key); } } // Append data to aDesc, up to aDesc's maximum size. // If socket is datagram based, buffer_ will be clared. void CPjSocketReader::ReadData(TDes8 &aDesc, TInetAddr *addr) { if (isDatagram_) aDesc.Zero(); if (buffer_.Length() == 0) return; TInt size_to_copy = aDesc.MaxLength() - aDesc.Length(); if (size_to_copy > buffer_.Length()) size_to_copy = buffer_.Length(); aDesc.Append(buffer_.Ptr(), size_to_copy); if (isDatagram_) buffer_.Zero(); else buffer_.Delete(0, size_to_copy); if (addr) *addr = recvAddr_; } ///////////////////////////////////////////////////////////////////////////// // // PJLIB's sock.h implementation // /* * Convert 16-bit value from network byte order to host byte order. */ PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort) { #if PJ_IS_LITTLE_ENDIAN return pj_swap16(netshort); #else return netshort; #endif } /* * Convert 16-bit value from host byte order to network byte order. */ PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort) { #if PJ_IS_LITTLE_ENDIAN return pj_swap16(hostshort); #else return hostshort; #endif } /* * Convert 32-bit value from network byte order to host byte order. */ PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong) { #if PJ_IS_LITTLE_ENDIAN return pj_swap32(netlong); #else return netlong; #endif } /* * Convert 32-bit value from host byte order to network byte order. */ PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong) { #if PJ_IS_LITTLE_ENDIAN return pj_swap32(hostlong); #else return netlong; #endif } /* * Convert an Internet host address given in network byte order * to string in standard numbers and dots notation. */ PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr) { static char str8[PJ_INET_ADDRSTRLEN]; TBuf str16(0); /* (Symbian IP address is in host byte order) */ TInetAddr temp_addr((TUint32)pj_ntohl(inaddr.s_addr), (TUint)0); temp_addr.Output(str16); return pj_unicode_to_ansi((const wchar_t*)str16.PtrZ(), str16.Length(), str8, sizeof(str8)); } /* * This function converts the Internet host address cp from the standard * numbers-and-dots notation into binary data and stores it in the structure * that inp points to. */ PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp) { enum { MAXIPLEN = PJ_INET_ADDRSTRLEN }; /* Initialize output with PJ_INADDR_NONE. * Some apps relies on this instead of the return value * (and anyway the return value is quite confusing!) */ inp->s_addr = PJ_INADDR_NONE; /* Caution: * this function might be called with cp->slen >= 16 * (i.e. when called with hostname to check if it's an IP addr). */ PJ_ASSERT_RETURN(cp && cp->slen && inp, 0); if (cp->slen >= 16) { return 0; } char tempaddr8[MAXIPLEN]; pj_memcpy(tempaddr8, cp->ptr, cp->slen); tempaddr8[cp->slen] = '\0'; wchar_t tempaddr16[MAXIPLEN]; pj_ansi_to_unicode(tempaddr8, pj_ansi_strlen(tempaddr8), tempaddr16, sizeof(tempaddr16)); TBuf ip_addr((const TText*)tempaddr16); TInetAddr addr; addr.Init(KAfInet); if (addr.Input(ip_addr) == KErrNone) { /* Success (Symbian IP address is in host byte order) */ inp->s_addr = pj_htonl(addr.Address()); return 1; } else { /* Error */ return 0; } } /* * Convert text to IPv4/IPv6 address. */ PJ_DEF(pj_status_t) pj_inet_pton(int af, const pj_str_t *src, void *dst) { char tempaddr[PJ_INET6_ADDRSTRLEN]; PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL); PJ_ASSERT_RETURN(src && src->slen && dst, PJ_EINVAL); /* Initialize output with PJ_IN_ADDR_NONE for IPv4 (to be * compatible with pj_inet_aton() */ if (af==PJ_AF_INET) { ((pj_in_addr*)dst)->s_addr = PJ_INADDR_NONE; } /* Caution: * this function might be called with cp->slen >= 46 * (i.e. when called with hostname to check if it's an IP addr). */ if (src->slen >= PJ_INET6_ADDRSTRLEN) { return PJ_ENAMETOOLONG; } pj_memcpy(tempaddr, src->ptr, src->slen); tempaddr[src->slen] = '\0'; wchar_t tempaddr16[PJ_INET6_ADDRSTRLEN]; pj_ansi_to_unicode(tempaddr, pj_ansi_strlen(tempaddr), tempaddr16, sizeof(tempaddr16)); TBuf ip_addr((const TText*)tempaddr16); TInetAddr addr; addr.Init(KAfInet6); if (addr.Input(ip_addr) == KErrNone) { if (af==PJ_AF_INET) { /* Success (Symbian IP address is in host byte order) */ pj_uint32_t ip = pj_htonl(addr.Address()); pj_memcpy(dst, &ip, 4); } else if (af==PJ_AF_INET6) { const TIp6Addr & ip6 = addr.Ip6Address(); pj_memcpy(dst, ip6.u.iAddr8, 16); } else { pj_assert(!"Unexpected!"); return PJ_EBUG; } return PJ_SUCCESS; } else { /* Error */ return PJ_EINVAL; } } /* * Convert IPv4/IPv6 address to text. */ PJ_DEF(pj_status_t) pj_inet_ntop(int af, const void *src, char *dst, int size) { PJ_ASSERT_RETURN(src && dst && size, PJ_EINVAL); *dst = '\0'; if (af==PJ_AF_INET) { TBuf str16; pj_in_addr inaddr; if (size < PJ_INET_ADDRSTRLEN) return PJ_ETOOSMALL; pj_memcpy(&inaddr, src, 4); /* Symbian IP address is in host byte order */ TInetAddr temp_addr((TUint32)pj_ntohl(inaddr.s_addr), (TUint)0); temp_addr.Output(str16); pj_unicode_to_ansi((const wchar_t*)str16.PtrZ(), str16.Length(), dst, size); return PJ_SUCCESS; } else if (af==PJ_AF_INET6) { TBuf str16; if (size < PJ_INET6_ADDRSTRLEN) return PJ_ETOOSMALL; TIp6Addr ip6; pj_memcpy(ip6.u.iAddr8, src, 16); TInetAddr temp_addr(ip6, (TUint)0); temp_addr.Output(str16); pj_unicode_to_ansi((const wchar_t*)str16.PtrZ(), str16.Length(), dst, size); return PJ_SUCCESS; } else { pj_assert(!"Unsupport address family"); return PJ_EINVAL; } } /* * Get hostname. */ PJ_DEF(const pj_str_t*) pj_gethostname(void) { static char buf[PJ_MAX_HOSTNAME]; static pj_str_t hostname; PJ_CHECK_STACK(); if (hostname.ptr == NULL) { RHostResolver &resv = PjSymbianOS::Instance()->GetResolver(PJ_AF_INET); TRequestStatus reqStatus; THostName tmpName; // Return empty hostname if access point is marked as down by app. PJ_SYMBIAN_CHECK_CONNECTION2(&hostname); resv.GetHostName(tmpName, reqStatus); User::WaitForRequest(reqStatus); hostname.ptr = pj_unicode_to_ansi((const wchar_t*)tmpName.Ptr(), tmpName.Length(), buf, sizeof(buf)); hostname.slen = tmpName.Length(); } return &hostname; } /* * Create new socket/endpoint for communication and returns a descriptor. */ PJ_DEF(pj_status_t) pj_sock_socket(int af, int type, int proto, pj_sock_t *p_sock) { TInt rc; PJ_CHECK_STACK(); /* Sanity checks. */ PJ_ASSERT_RETURN(p_sock!=NULL, PJ_EINVAL); // Return failure if access point is marked as down by app. PJ_SYMBIAN_CHECK_CONNECTION(); /* Set proto if none is specified. */ if (proto == 0) { if (type == pj_SOCK_STREAM()) proto = KProtocolInetTcp; else if (type == pj_SOCK_DGRAM()) proto = KProtocolInetUdp; } /* Create Symbian RSocket */ RSocket rSock; if (PjSymbianOS::Instance()->Connection()) rc = rSock.Open(PjSymbianOS::Instance()->SocketServ(), af, type, proto, *PjSymbianOS::Instance()->Connection()); else rc = rSock.Open(PjSymbianOS::Instance()->SocketServ(), af, type, proto); if (rc != KErrNone) return PJ_RETURN_OS_ERROR(rc); /* Wrap Symbian RSocket into PJLIB's CPjSocket, and return to caller */ CPjSocket *pjSock = new CPjSocket(af, type, rSock); *p_sock = (pj_sock_t)pjSock; return PJ_SUCCESS; } /* * Bind socket. */ PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock, const pj_sockaddr_t *addr, int len) { pj_status_t status; TInt rc; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sock != 0, PJ_EINVAL); PJ_ASSERT_RETURN(addr && len>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL); // Convert PJLIB's pj_sockaddr into Symbian's TInetAddr TInetAddr inetAddr; status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)addr, len, inetAddr); if (status != PJ_SUCCESS) return status; // Get the RSocket instance RSocket &rSock = ((CPjSocket*)sock)->Socket(); // Bind rc = rSock.Bind(inetAddr); return (rc==KErrNone) ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(rc); } /* * Bind socket. */ PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock, pj_uint32_t addr32, pj_uint16_t port) { pj_sockaddr_in addr; PJ_CHECK_STACK(); pj_bzero(&addr, sizeof(addr)); addr.sin_family = PJ_AF_INET; addr.sin_addr.s_addr = pj_htonl(addr32); addr.sin_port = pj_htons(port); return pj_sock_bind(sock, &addr, sizeof(pj_sockaddr_in)); } /* * Close socket. */ PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sock) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sock != 0, PJ_EINVAL); CPjSocket *pjSock = (CPjSocket*)sock; // This will close the socket. delete pjSock; return PJ_SUCCESS; } /* * Get remote's name. */ PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sock, pj_sockaddr_t *addr, int *namelen) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sock && addr && namelen && *namelen>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL); CPjSocket *pjSock = (CPjSocket*)sock; RSocket &rSock = pjSock->Socket(); // Socket must be connected. PJ_ASSERT_RETURN(pjSock->IsConnected(), PJ_EINVALIDOP); TInetAddr inetAddr; rSock.RemoteName(inetAddr); return PjSymbianOS::Addr2pj(inetAddr, *(pj_sockaddr*)addr, namelen); } /* * Get socket name. */ PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock, pj_sockaddr_t *addr, int *namelen) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sock && addr && namelen && *namelen>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL); CPjSocket *pjSock = (CPjSocket*)sock; RSocket &rSock = pjSock->Socket(); TInetAddr inetAddr; rSock.LocalName(inetAddr); return PjSymbianOS::Addr2pj(inetAddr, *(pj_sockaddr*)addr, namelen); } /* * Send data */ PJ_DEF(pj_status_t) pj_sock_send(pj_sock_t sock, const void *buf, pj_ssize_t *len, unsigned flags) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sock && buf && len, PJ_EINVAL); // Return failure if access point is marked as down by app. PJ_SYMBIAN_CHECK_CONNECTION(); CPjSocket *pjSock = (CPjSocket*)sock; RSocket &rSock = pjSock->Socket(); // send() should only be called to connected socket PJ_ASSERT_RETURN(pjSock->IsConnected(), PJ_EINVALIDOP); TPtrC8 data((const TUint8*)buf, (TInt)*len); TRequestStatus reqStatus; TSockXfrLength sentLen; rSock.Send(data, flags, reqStatus, sentLen); User::WaitForRequest(reqStatus); if (reqStatus.Int()==KErrNone) { //*len = (TInt) sentLen.Length(); return PJ_SUCCESS; } else return PJ_RETURN_OS_ERROR(reqStatus.Int()); } /* * Send data. */ PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock, const void *buf, pj_ssize_t *len, unsigned flags, const pj_sockaddr_t *to, int tolen) { pj_status_t status; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sock && buf && len, PJ_EINVAL); // Return failure if access point is marked as down by app. PJ_SYMBIAN_CHECK_CONNECTION(); CPjSocket *pjSock = (CPjSocket*)sock; RSocket &rSock = pjSock->Socket(); // Only supports AF_INET for now PJ_ASSERT_RETURN(tolen>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL); TInetAddr inetAddr; status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)to, tolen, inetAddr); if (status != PJ_SUCCESS) return status; TPtrC8 data((const TUint8*)buf, (TInt)*len); TRequestStatus reqStatus; TSockXfrLength sentLen; rSock.SendTo(data, inetAddr, flags, reqStatus, sentLen); User::WaitForRequest(reqStatus); if (reqStatus.Int()==KErrNone) { //For some reason TSockXfrLength is not returning correctly! //*len = (TInt) sentLen.Length(); return PJ_SUCCESS; } else return PJ_RETURN_OS_ERROR(reqStatus.Int()); } /* * Receive data. */ PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock, void *buf, pj_ssize_t *len, unsigned flags) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sock && buf && len, PJ_EINVAL); PJ_ASSERT_RETURN(*len > 0, PJ_EINVAL); // Return failure if access point is marked as down by app. PJ_SYMBIAN_CHECK_CONNECTION(); CPjSocket *pjSock = (CPjSocket*)sock; if (pjSock->Reader()) { CPjSocketReader *reader = pjSock->Reader(); while (reader->IsActive() && !reader->HasData()) { User::WaitForAnyRequest(); } if (reader->HasData()) { TPtr8 data((TUint8*)buf, (TInt)*len); TInetAddr inetAddr; reader->ReadData(data, &inetAddr); *len = data.Length(); return PJ_SUCCESS; } } TRequestStatus reqStatus; TSockXfrLength recvLen; TPtr8 data((TUint8*)buf, (TInt)*len, (TInt)*len); if (pjSock->IsDatagram()) { pjSock->Socket().Recv(data, flags, reqStatus); } else { // Using static like this is not pretty, but we don't need to use // the value anyway, hence doing it like this is probably most // optimal. static TSockXfrLength len; pjSock->Socket().RecvOneOrMore(data, flags, reqStatus, len); } User::WaitForRequest(reqStatus); if (reqStatus == KErrNone) { //*len = (TInt)recvLen.Length(); *len = data.Length(); return PJ_SUCCESS; } else { *len = -1; return PJ_RETURN_OS_ERROR(reqStatus.Int()); } } /* * Receive data. */ PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock, void *buf, pj_ssize_t *len, unsigned flags, pj_sockaddr_t *from, int *fromlen) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sock && buf && len && from && fromlen, PJ_EINVAL); PJ_ASSERT_RETURN(*len > 0, PJ_EINVAL); PJ_ASSERT_RETURN(*fromlen >= (int)sizeof(pj_sockaddr_in), PJ_EINVAL); // Return failure if access point is marked as down by app. PJ_SYMBIAN_CHECK_CONNECTION(); CPjSocket *pjSock = (CPjSocket*)sock; RSocket &rSock = pjSock->Socket(); if (pjSock->Reader()) { CPjSocketReader *reader = pjSock->Reader(); while (reader->IsActive() && !reader->HasData()) { User::WaitForAnyRequest(); } if (reader->HasData()) { TPtr8 data((TUint8*)buf, (TInt)*len); TInetAddr inetAddr; reader->ReadData(data, &inetAddr); *len = data.Length(); if (from && fromlen) { return PjSymbianOS::Addr2pj(inetAddr, *(pj_sockaddr*)from, fromlen); } else { return PJ_SUCCESS; } } } TInetAddr inetAddr; TRequestStatus reqStatus; TSockXfrLength recvLen; TPtr8 data((TUint8*)buf, (TInt)*len, (TInt)*len); rSock.RecvFrom(data, inetAddr, flags, reqStatus, recvLen); User::WaitForRequest(reqStatus); if (reqStatus == KErrNone) { //*len = (TInt)recvLen.Length(); *len = data.Length(); return PjSymbianOS::Addr2pj(inetAddr, *(pj_sockaddr*)from, fromlen); } else { *len = -1; *fromlen = -1; return PJ_RETURN_OS_ERROR(reqStatus.Int()); } } /* * Get socket option. */ PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sock, pj_uint16_t level, pj_uint16_t optname, void *optval, int *optlen) { // Not supported for now. PJ_UNUSED_ARG(sock); PJ_UNUSED_ARG(level); PJ_UNUSED_ARG(optname); PJ_UNUSED_ARG(optval); PJ_UNUSED_ARG(optlen); return PJ_EINVALIDOP; } /* * Set socket option. */ PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock, pj_uint16_t level, pj_uint16_t optname, const void *optval, int optlen) { // Not supported for now. PJ_UNUSED_ARG(sock); PJ_UNUSED_ARG(level); PJ_UNUSED_ARG(optname); PJ_UNUSED_ARG(optval); PJ_UNUSED_ARG(optlen); return PJ_EINVALIDOP; } /* * Connect socket. */ PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sock, const pj_sockaddr_t *addr, int namelen) { pj_status_t status; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sock && addr && namelen, PJ_EINVAL); PJ_ASSERT_RETURN(((pj_sockaddr*)addr)->addr.sa_family == PJ_AF_INET, PJ_EINVAL); // Return failure if access point is marked as down by app. PJ_SYMBIAN_CHECK_CONNECTION(); CPjSocket *pjSock = (CPjSocket*)sock; RSocket &rSock = pjSock->Socket(); TInetAddr inetAddr; TRequestStatus reqStatus; status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)addr, namelen, inetAddr); if (status != PJ_SUCCESS) return status; rSock.Connect(inetAddr, reqStatus); User::WaitForRequest(reqStatus); if (reqStatus == KErrNone) { pjSock->SetConnected(true); return PJ_SUCCESS; } else { return PJ_RETURN_OS_ERROR(reqStatus.Int()); } } /* * Shutdown socket. */ #if PJ_HAS_TCP PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sock, int how) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sock, PJ_EINVAL); CPjSocket *pjSock = (CPjSocket*)sock; RSocket &rSock = pjSock->Socket(); RSocket::TShutdown aHow; if (how == PJ_SD_RECEIVE) aHow = RSocket::EStopInput; else if (how == PJ_SHUT_WR) aHow = RSocket::EStopOutput; else aHow = RSocket::ENormal; TRequestStatus reqStatus; rSock.Shutdown(aHow, reqStatus); User::WaitForRequest(reqStatus); if (reqStatus == KErrNone) { return PJ_SUCCESS; } else { return PJ_RETURN_OS_ERROR(reqStatus.Int()); } } /* * Start listening to incoming connections. */ PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sock, int backlog) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(sock && backlog, PJ_EINVAL); CPjSocket *pjSock = (CPjSocket*)sock; RSocket &rSock = pjSock->Socket(); TInt rc = rSock.Listen((TUint)backlog); if (rc == KErrNone) { return PJ_SUCCESS; } else { return PJ_RETURN_OS_ERROR(rc); } } /* * Accept incoming connections */ PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd, pj_sock_t *newsock, pj_sockaddr_t *addr, int *addrlen) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(serverfd && newsock, PJ_EINVAL); CPjSocket *pjSock = (CPjSocket*)serverfd; RSocket &rSock = pjSock->Socket(); // Create a 'blank' socket RSocket newSock; newSock.Open(PjSymbianOS::Instance()->SocketServ()); // Call Accept() TRequestStatus reqStatus; rSock.Accept(newSock, reqStatus); User::WaitForRequest(reqStatus); if (reqStatus != KErrNone) { return PJ_RETURN_OS_ERROR(reqStatus.Int()); } // Create PJ socket CPjSocket *newPjSock = new CPjSocket(pjSock->GetAf(), pjSock->GetSockType(), newSock); newPjSock->SetConnected(true); *newsock = (pj_sock_t) newPjSock; if (addr && addrlen) { return pj_sock_getpeername(*newsock, addr, addrlen); } return PJ_SUCCESS; } #endif /* PJ_HAS_TCP */ ================================================ FILE: deps/pjsip/pjlib/src/pj/ssl_sock_common.c ================================================ /* $Id: ssl_sock_common.c 3999 2012-03-30 07:10:13Z bennylp $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include /* * Initialize the SSL socket configuration with the default values. */ PJ_DEF(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param) { pj_bzero(param, sizeof(*param)); /* Socket config */ param->sock_af = PJ_AF_INET; param->sock_type = pj_SOCK_STREAM(); param->async_cnt = 1; param->concurrency = -1; param->whole_data = PJ_TRUE; param->send_buffer_size = 8192; #if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0 param->read_buffer_size = 1500; #endif param->qos_type = PJ_QOS_TYPE_BEST_EFFORT; param->qos_ignore_error = PJ_TRUE; param->sockopt_ignore_error = PJ_TRUE; /* Security config */ param->proto = PJ_SSL_SOCK_PROTO_DEFAULT; } /* * Duplicate SSL socket parameter. */ PJ_DEF(void) pj_ssl_sock_param_copy( pj_pool_t *pool, pj_ssl_sock_param *dst, const pj_ssl_sock_param *src) { /* Init secure socket param */ pj_memcpy(dst, src, sizeof(*dst)); if (src->ciphers_num > 0) { unsigned i; dst->ciphers = (pj_ssl_cipher*) pj_pool_calloc(pool, src->ciphers_num, sizeof(pj_ssl_cipher)); for (i = 0; i < src->ciphers_num; ++i) dst->ciphers[i] = src->ciphers[i]; } if (src->server_name.slen) { /* Server name must be null-terminated */ pj_strdup_with_null(pool, &dst->server_name, &src->server_name); } } PJ_DEF(pj_status_t) pj_ssl_cert_get_verify_status_strings( pj_uint32_t verify_status, const char *error_strings[], unsigned *count) { unsigned i = 0, shift_idx = 0; unsigned unknown = 0; pj_uint32_t errs; PJ_ASSERT_RETURN(error_strings && count, PJ_EINVAL); if (verify_status == PJ_SSL_CERT_ESUCCESS && *count) { error_strings[0] = "OK"; *count = 1; return PJ_SUCCESS; } errs = verify_status; while (errs && i < *count) { pj_uint32_t err; const char *p = NULL; if ((errs & 1) == 0) { shift_idx++; errs >>= 1; continue; } err = (1 << shift_idx); switch (err) { case PJ_SSL_CERT_EISSUER_NOT_FOUND: p = "The issuer certificate cannot be found"; break; case PJ_SSL_CERT_EUNTRUSTED: p = "The certificate is untrusted"; break; case PJ_SSL_CERT_EVALIDITY_PERIOD: p = "The certificate has expired or not yet valid"; break; case PJ_SSL_CERT_EINVALID_FORMAT: p = "One or more fields of the certificate cannot be decoded " "due to invalid format"; break; case PJ_SSL_CERT_EISSUER_MISMATCH: p = "The issuer info in the certificate does not match to the " "(candidate) issuer certificate"; break; case PJ_SSL_CERT_ECRL_FAILURE: p = "The CRL certificate cannot be found or cannot be read " "properly"; break; case PJ_SSL_CERT_EREVOKED: p = "The certificate has been revoked"; break; case PJ_SSL_CERT_EINVALID_PURPOSE: p = "The certificate or CA certificate cannot be used for the " "specified purpose"; break; case PJ_SSL_CERT_ECHAIN_TOO_LONG: p = "The certificate chain length is too long"; break; case PJ_SSL_CERT_EIDENTITY_NOT_MATCH: p = "The server identity does not match to any identities " "specified in the certificate"; break; case PJ_SSL_CERT_EUNKNOWN: default: unknown++; break; } /* Set error string */ if (p) error_strings[i++] = p; /* Next */ shift_idx++; errs >>= 1; } /* Unknown error */ if (unknown && i < *count) error_strings[i++] = "Unknown verification error"; *count = i; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib/src/pj/ssl_sock_dump.c ================================================ /* $Id: ssl_sock_dump.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include /* Only build when PJ_HAS_SSL_SOCK is enabled */ #if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK!=0 #define THIS_FILE "ssl_sock_dump.c" #define CHECK_BUF_LEN() \ if ((len < 0) || (len >= end-p)) { \ *p = '\0'; \ return -1; \ } \ p += len; PJ_DEF(pj_ssize_t) pj_ssl_cert_info_dump(const pj_ssl_cert_info *ci, const char *indent, char *buf, pj_size_t buf_size) { const char *wdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; pj_parsed_time pt1; pj_parsed_time pt2; unsigned i; int len = 0; char *p, *end; p = buf; end = buf + buf_size; pj_time_decode(&ci->validity.start, &pt1); pj_time_decode(&ci->validity.end, &pt2); /* Version */ len = pj_ansi_snprintf(p, end-p, "%sVersion : v%d\n", indent, ci->version); CHECK_BUF_LEN(); /* Serial number */ len = pj_ansi_snprintf(p, end-p, "%sSerial : ", indent); CHECK_BUF_LEN(); for (i = 0; i < sizeof(ci->serial_no) && !ci->serial_no[i]; ++i); for (; i < sizeof(ci->serial_no); ++i) { len = pj_ansi_snprintf(p, end-p, "%02X ", ci->serial_no[i] & 0xFF); CHECK_BUF_LEN(); } *(p-1) = '\n'; /* Subject */ len = pj_ansi_snprintf( p, end-p, "%sSubject : %.*s\n", indent, (int)ci->subject.cn.slen, ci->subject.cn.ptr); CHECK_BUF_LEN(); len = pj_ansi_snprintf( p, end-p, "%s %.*s\n", indent, (int)ci->subject.info.slen, ci->subject.info.ptr); CHECK_BUF_LEN(); /* Issuer */ len = pj_ansi_snprintf( p, end-p, "%sIssuer : %.*s\n", indent, (int)ci->issuer.cn.slen, ci->issuer.cn.ptr); CHECK_BUF_LEN(); len = pj_ansi_snprintf( p, end-p, "%s %.*s\n", indent, (int)ci->issuer.info.slen, ci->issuer.info.ptr); CHECK_BUF_LEN(); /* Validity period */ len = pj_ansi_snprintf( p, end-p, "%sValid from : %s %4d-%02d-%02d " "%02d:%02d:%02d.%03d %s\n", indent, wdays[pt1.wday], pt1.year, pt1.mon+1, pt1.day, pt1.hour, pt1.min, pt1.sec, pt1.msec, (ci->validity.gmt? "GMT":"")); CHECK_BUF_LEN(); len = pj_ansi_snprintf( p, end-p, "%sValid to : %s %4d-%02d-%02d " "%02d:%02d:%02d.%03d %s\n", indent, wdays[pt2.wday], pt2.year, pt2.mon+1, pt2.day, pt2.hour, pt2.min, pt2.sec, pt2.msec, (ci->validity.gmt? "GMT":"")); CHECK_BUF_LEN(); /* Subject alternative name extension */ if (ci->subj_alt_name.cnt) { unsigned i; len = pj_ansi_snprintf(p, end-p, "%ssubjectAltName extension\n", indent); CHECK_BUF_LEN(); for (i = 0; i < ci->subj_alt_name.cnt; ++i) { const char *type = NULL; switch(ci->subj_alt_name.entry[i].type) { case PJ_SSL_CERT_NAME_RFC822: type = "MAIL"; break; case PJ_SSL_CERT_NAME_DNS: type = " DNS"; break; case PJ_SSL_CERT_NAME_URI: type = " URI"; break; case PJ_SSL_CERT_NAME_IP: type = " IP"; break; default: break; } if (type) { len = pj_ansi_snprintf( p, end-p, "%s %s : %.*s\n", indent, type, (int)ci->subj_alt_name.entry[i].name.slen, ci->subj_alt_name.entry[i].name.ptr); CHECK_BUF_LEN(); } } } return (p-buf); } #endif /* PJ_HAS_SSL_SOCK */ ================================================ FILE: deps/pjsip/pjlib/src/pj/ssl_sock_ossl.c ================================================ /* $Id: ssl_sock_ossl.c 4506 2013-04-26 06:01:43Z bennylp $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Only build when PJ_HAS_SSL_SOCK is enabled */ #if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK!=0 #define THIS_FILE "ssl_sock_ossl.c" /* Workaround for ticket #985 */ #define DELAYED_CLOSE_TIMEOUT 200 /* * Include OpenSSL headers */ #include #include #include #include #include #if !USING_LIBRESSL && OPENSSL_VERSION_NUMBER >= 0x10100000L # define OPENSSL_NO_SSL2 /* seems to be removed in 1.1.0 */ # define M_ASN1_STRING_data(x) ASN1_STRING_get0_data(x) # define M_ASN1_STRING_length(x) ASN1_STRING_length(x) # if defined(OPENSSL_API_COMPAT) && OPENSSL_API_COMPAT >= 0x10100000L # define X509_get_notBefore(x) X509_get0_notBefore(x) # define X509_get_notAfter(x) X509_get0_notAfter(x) # endif #else # define SSL_CIPHER_get_id(c) (c)->id # define SSL_set_session(ssl, s) (ssl)->session = (s) #endif #ifdef _MSC_VER # pragma comment( lib, "libeay32") # pragma comment( lib, "ssleay32") # pragma comment( lib, "crypt32") #endif /* Suppress compile warning of OpenSSL deprecation (OpenSSL is deprecated * since MacOSX 10.7). */ #if defined(PJ_DARWINOS) && PJ_DARWINOS==1 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif /* * SSL/TLS state enumeration. */ enum ssl_state { SSL_STATE_NULL, SSL_STATE_HANDSHAKING, SSL_STATE_ESTABLISHED }; /* * Internal timer types. */ enum timer_id { TIMER_NONE, TIMER_HANDSHAKE_TIMEOUT, TIMER_CLOSE }; /* * Structure of SSL socket read buffer. */ typedef struct read_data_t { void *data; pj_size_t len; } read_data_t; /* * Get the offset of pointer to read-buffer of SSL socket from read-buffer * of active socket. Note that both SSL socket and active socket employ * different but correlated read-buffers (as much as async_cnt for each), * and to make it easier/faster to find corresponding SSL socket's read-buffer * from known active socket's read-buffer, the pointer of corresponding * SSL socket's read-buffer is stored right after the end of active socket's * read-buffer. */ #define OFFSET_OF_READ_DATA_PTR(ssock, asock_rbuf) \ (read_data_t**) \ ((pj_int8_t*)(asock_rbuf) + \ ssock->param.read_buffer_size) /* * Structure of SSL socket write data. */ typedef struct write_data_t { PJ_DECL_LIST_MEMBER(struct write_data_t); pj_ioqueue_op_key_t key; pj_size_t record_len; pj_ioqueue_op_key_t *app_key; pj_size_t plain_data_len; pj_size_t data_len; unsigned flags; union { char content[1]; const char *ptr; } data; } write_data_t; /* * Structure of SSL socket write buffer (circular buffer). */ typedef struct send_buf_t { char *buf; pj_size_t max_len; char *start; pj_size_t len; } send_buf_t; /* * Secure socket structure definition. */ struct pj_ssl_sock_t { pj_pool_t *pool; pj_ssl_sock_t *parent; pj_ssl_sock_param param; pj_ssl_sock_param newsock_param; pj_ssl_cert_t *cert; pj_ssl_cert_info local_cert_info; pj_ssl_cert_info remote_cert_info; pj_bool_t is_server; enum ssl_state ssl_state; pj_ioqueue_op_key_t handshake_op_key; pj_timer_entry timer; pj_status_t verify_status; unsigned long last_err; pj_sock_t sock; pj_activesock_t *asock; pj_sockaddr local_addr; pj_sockaddr rem_addr; int addr_len; pj_bool_t read_started; pj_size_t read_size; pj_uint32_t read_flags; void **asock_rbuf; read_data_t *ssock_rbuf; write_data_t write_pending;/* list of pending write to OpenSSL */ write_data_t write_pending_empty; /* cache for write_pending */ pj_bool_t flushing_write_pend; /* flag of flushing is ongoing*/ send_buf_t send_buf; write_data_t send_pending; /* list of pending write to network */ pj_lock_t *write_mutex; /* protect write BIO and send_buf */ pj_lock_t *state_mutex; /* protect the socket state (sending on one thread, destroying it in another */ SSL_CTX *ossl_ctx; SSL *ossl_ssl; BIO *ossl_rbio; BIO *ossl_wbio; }; /* * Certificate/credential structure definition. */ struct pj_ssl_cert_t { pj_str_t CA_file; pj_str_t CA_path; pj_str_t cert_file; pj_str_t privkey_file; pj_str_t privkey_pass; }; static write_data_t* alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len); static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata); static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock); /* ******************************************************************* * Static/internal functions. ******************************************************************* */ /** * Mapping from OpenSSL error codes to pjlib error space. */ #define PJ_SSL_ERRNO_START (PJ_ERRNO_START_USER + \ PJ_ERRNO_SPACE_SIZE*6) #define PJ_SSL_ERRNO_SPACE_SIZE PJ_ERRNO_SPACE_SIZE /* Expected maximum value of reason component in OpenSSL error code */ #define MAX_OSSL_ERR_REASON 1200 static pj_status_t STATUS_FROM_SSL_ERR(pj_ssl_sock_t *ssock, unsigned long err) { pj_status_t status; /* General SSL error, dig more from OpenSSL error queue */ if (err == SSL_ERROR_SSL) err = ERR_get_error(); /* OpenSSL error range is much wider than PJLIB errno space, so * if it exceeds the space, only the error reason will be kept. * Note that the last native error will be kept as is and can be * retrieved via SSL socket info. */ status = ERR_GET_LIB(err)*MAX_OSSL_ERR_REASON + ERR_GET_REASON(err); if (status > PJ_SSL_ERRNO_SPACE_SIZE) status = ERR_GET_REASON(err); status += PJ_SSL_ERRNO_START; ssock->last_err = err; return status; } static pj_status_t GET_SSL_STATUS(pj_ssl_sock_t *ssock) { return STATUS_FROM_SSL_ERR(ssock, ERR_get_error()); } /* * Get error string of OpenSSL. */ static pj_str_t ssl_strerror(pj_status_t status, char *buf, pj_size_t bufsize) { pj_str_t errstr; unsigned long ssl_err = status; if (ssl_err) { unsigned long l, r; ssl_err -= PJ_SSL_ERRNO_START; l = ssl_err / MAX_OSSL_ERR_REASON; r = ssl_err % MAX_OSSL_ERR_REASON; ssl_err = ERR_PACK(l, 0, r); } #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) { const char *tmp = NULL; tmp = ERR_reason_error_string(ssl_err); if (tmp) { pj_ansi_strncpy(buf, tmp, bufsize); errstr = pj_str(buf); return errstr; } } #endif /* PJ_HAS_ERROR_STRING */ errstr.ptr = buf; errstr.slen = pj_ansi_snprintf(buf, bufsize, "Unknown OpenSSL error %lu", ssl_err); if (errstr.slen < 1 || errstr.slen >= (int)bufsize) errstr.slen = bufsize - 1; return errstr; } /* OpenSSL library initialization counter */ static int openssl_init_count; /* OpenSSL available ciphers */ static unsigned openssl_cipher_num; static struct openssl_ciphers_t { pj_ssl_cipher id; const char *name; } openssl_ciphers[PJ_SSL_SOCK_MAX_CIPHERS]; /* OpenSSL application data index */ static int sslsock_idx; /* Initialize OpenSSL */ static pj_status_t init_openssl(void) { pj_status_t status; if (openssl_init_count) return PJ_SUCCESS; openssl_init_count = 1; /* Register error subsystem */ status = pj_register_strerror(PJ_SSL_ERRNO_START, PJ_SSL_ERRNO_SPACE_SIZE, &ssl_strerror); pj_assert(status == PJ_SUCCESS); /* Init OpenSSL lib */ #if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_library_init(); SSL_load_error_strings(); #else OPENSSL_init_ssl(0, NULL); #endif #if OPENSSL_VERSION_NUMBER < 0x009080ffL /* This is now synonym of SSL_library_init() */ OpenSSL_add_all_algorithms(); #endif /* Init available ciphers */ if (openssl_cipher_num == 0) { SSL_METHOD *meth = NULL; SSL_CTX *ctx; SSL *ssl; STACK_OF(SSL_CIPHER) *sk_cipher; unsigned i, n; #if OPENSSL_VERSION_NUMBER < 0x10100000L meth = (SSL_METHOD*)SSLv23_server_method(); if (!meth) meth = (SSL_METHOD*)TLSv1_server_method(); #ifndef OPENSSL_NO_SSL3_METHOD if (!meth) meth = (SSL_METHOD*)SSLv3_server_method(); #endif #ifndef OPENSSL_NO_SSL2 if (!meth) meth = (SSL_METHOD*)SSLv2_server_method(); #endif #else /* Specific version methods are deprecated in 1.1.0 */ meth = (SSL_METHOD*)TLS_method(); #endif pj_assert(meth); ctx=SSL_CTX_new(meth); SSL_CTX_set_cipher_list(ctx, "ALL:COMPLEMENTOFALL"); ssl = SSL_new(ctx); sk_cipher = SSL_get_ciphers(ssl); n = sk_SSL_CIPHER_num(sk_cipher); if (n > PJ_ARRAY_SIZE(openssl_ciphers)) n = PJ_ARRAY_SIZE(openssl_ciphers); for (i = 0; i < n; ++i) { const SSL_CIPHER *c; c = sk_SSL_CIPHER_value(sk_cipher,i); openssl_ciphers[i].id = (pj_ssl_cipher) (pj_uint32_t)SSL_CIPHER_get_id(c) & 0x00FFFFFF; openssl_ciphers[i].name = SSL_CIPHER_get_name(c); } SSL_free(ssl); SSL_CTX_free(ctx); openssl_cipher_num = n; } /* Create OpenSSL application data index for SSL socket */ sslsock_idx = SSL_get_ex_new_index(0, "SSL socket", NULL, NULL, NULL); return PJ_SUCCESS; } /* Shutdown OpenSSL */ static void shutdown_openssl(void) { PJ_UNUSED_ARG(openssl_init_count); } /* SSL password callback. */ static int password_cb(char *buf, int num, int rwflag, void *user_data) { pj_ssl_cert_t *cert = (pj_ssl_cert_t*) user_data; PJ_UNUSED_ARG(rwflag); if(num < cert->privkey_pass.slen) return 0; pj_memcpy(buf, cert->privkey_pass.ptr, cert->privkey_pass.slen); return (int)cert->privkey_pass.slen; } /* SSL password callback. */ static int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) { pj_ssl_sock_t *ssock; SSL *ossl_ssl; int err; /* Get SSL instance */ ossl_ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); pj_assert(ossl_ssl); /* Get SSL socket instance */ ssock = SSL_get_ex_data(ossl_ssl, sslsock_idx); pj_assert(ssock); /* Store verification status */ err = X509_STORE_CTX_get_error(x509_ctx); switch (err) { case X509_V_OK: break; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: ssock->verify_status |= PJ_SSL_CERT_EISSUER_NOT_FOUND; break; case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT; break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_CERT_HAS_EXPIRED: ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD; break; case X509_V_ERR_UNABLE_TO_GET_CRL: case X509_V_ERR_CRL_NOT_YET_VALID: case X509_V_ERR_CRL_HAS_EXPIRED: case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: case X509_V_ERR_CRL_SIGNATURE_FAILURE: case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: ssock->verify_status |= PJ_SSL_CERT_ECRL_FAILURE; break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: case X509_V_ERR_CERT_UNTRUSTED: case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED; break; case X509_V_ERR_CERT_SIGNATURE_FAILURE: case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: case X509_V_ERR_AKID_SKID_MISMATCH: case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: ssock->verify_status |= PJ_SSL_CERT_EISSUER_MISMATCH; break; case X509_V_ERR_CERT_REVOKED: ssock->verify_status |= PJ_SSL_CERT_EREVOKED; break; case X509_V_ERR_INVALID_PURPOSE: case X509_V_ERR_CERT_REJECTED: case X509_V_ERR_INVALID_CA: ssock->verify_status |= PJ_SSL_CERT_EINVALID_PURPOSE; break; case X509_V_ERR_CERT_CHAIN_TOO_LONG: /* not really used */ case X509_V_ERR_PATH_LENGTH_EXCEEDED: ssock->verify_status |= PJ_SSL_CERT_ECHAIN_TOO_LONG; break; /* Unknown errors */ case X509_V_ERR_OUT_OF_MEM: default: ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN; break; } /* When verification is not requested just return ok here, however * application can still get the verification status. */ if (PJ_FALSE == ssock->param.verify_peer) preverify_ok = 1; return preverify_ok; } /* Setting SSL sock cipher list */ static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock); /* Create and initialize new SSL context and instance */ static pj_status_t create_ssl(pj_ssl_sock_t *ssock) { BIO *bio; DH *dh; long options; #if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x10000000L EC_KEY *ecdh; #endif SSL_METHOD *ssl_method = NULL; SSL_CTX *ctx; pj_uint32_t ssl_opt = 0; pj_ssl_cert_t *cert; int mode, rc; pj_status_t status; pj_assert(ssock); cert = ssock->cert; /* Make sure OpenSSL library has been initialized */ init_openssl(); if (ssock->param.proto == PJ_SSL_SOCK_PROTO_DEFAULT) ssock->param.proto = PJ_SSL_SOCK_PROTO_SSL23; /* Determine SSL method to use */ #if OPENSSL_VERSION_NUMBER < 0x10100000L switch (ssock->param.proto) { case PJ_SSL_SOCK_PROTO_TLS1: ssl_method = (SSL_METHOD*)TLSv1_method(); break; #ifndef OPENSSL_NO_SSL2 case PJ_SSL_SOCK_PROTO_SSL2: ssl_method = (SSL_METHOD*)SSLv2_method(); break; #endif #ifndef OPENSSL_NO_SSL3_METHOD case PJ_SSL_SOCK_PROTO_SSL3: ssl_method = (SSL_METHOD*)SSLv3_method(); #endif break; } #else /* Specific version methods are deprecated in 1.1.0 */ ssl_method = (SSL_METHOD*)TLS_method(); #endif if (!ssl_method) { ssl_method = (SSL_METHOD*)SSLv23_method(); #ifdef SSL_OP_NO_SSLv2 /** Check if SSLv2 is enabled */ ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_SSL2)==0)? SSL_OP_NO_SSLv2:0; #endif #ifdef SSL_OP_NO_SSLv3 /** Check if SSLv3 is enabled */ ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_SSL3)==0)? SSL_OP_NO_SSLv3:0; #endif #ifdef SSL_OP_NO_TLSv1 /** Check if TLSv1 is enabled */ ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1)==0)? SSL_OP_NO_TLSv1:0; #endif #ifdef SSL_OP_NO_TLSv1_1 /** Check if TLSv1_1 is enabled */ ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_1)==0)? SSL_OP_NO_TLSv1_1:0; #endif #ifdef SSL_OP_NO_TLSv1_2 /** Check if TLSv1_2 is enabled */ ssl_opt |= ((ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2)==0)? SSL_OP_NO_TLSv1_2:0; #endif } /* Create SSL context */ ctx = SSL_CTX_new(ssl_method); if (ctx == NULL) { return GET_SSL_STATUS(ssock); } if (ssl_opt) SSL_CTX_set_options(ctx, ssl_opt); /* Apply credentials */ if (cert) { /* Load CA list if one is specified. */ if (cert->CA_file.slen || cert->CA_path.slen) { rc = SSL_CTX_load_verify_locations( ctx, cert->CA_file.slen == 0 ? NULL : cert->CA_file.ptr, cert->CA_path.slen == 0 ? NULL : cert->CA_path.ptr); if (rc != 1) { status = GET_SSL_STATUS(ssock); if (cert->CA_file.slen) { PJ_LOG(1,(ssock->pool->obj_name, "Error loading CA list file '%s'", cert->CA_file.ptr)); } if (cert->CA_path.slen) { PJ_LOG(1,(ssock->pool->obj_name, "Error loading CA path '%s'", cert->CA_path.ptr)); } SSL_CTX_free(ctx); return status; } } /* Set password callback */ if (cert->privkey_pass.slen) { SSL_CTX_set_default_passwd_cb(ctx, password_cb); SSL_CTX_set_default_passwd_cb_userdata(ctx, cert); } /* Load certificate if one is specified */ if (cert->cert_file.slen) { /* Load certificate chain from file into ctx */ rc = SSL_CTX_use_certificate_chain_file(ctx, cert->cert_file.ptr); if(rc != 1) { status = GET_SSL_STATUS(ssock); PJ_LOG(1,(ssock->pool->obj_name, "Error loading certificate " "chain file '%s'", cert->cert_file.ptr)); SSL_CTX_free(ctx); return status; } } /* Load private key if one is specified */ if (cert->privkey_file.slen) { /* Adds the first private key found in file to ctx */ rc = SSL_CTX_use_PrivateKey_file(ctx, cert->privkey_file.ptr, SSL_FILETYPE_PEM); if(rc != 1) { status = GET_SSL_STATUS(ssock); PJ_LOG(1,(ssock->pool->obj_name, "Error adding private key " "from '%s'", cert->privkey_file.ptr)); SSL_CTX_free(ctx); return status; } if (ssock->is_server) { bio = BIO_new_file(cert->privkey_file.ptr, "r"); if (bio != NULL) { dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); if (dh != NULL) { if (SSL_CTX_set_tmp_dh(ctx, dh)) { options = SSL_OP_CIPHER_SERVER_PREFERENCE | #if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x10000000L SSL_OP_SINGLE_ECDH_USE | #endif SSL_OP_SINGLE_DH_USE; options = SSL_CTX_set_options(ctx, options); PJ_LOG(4,(ssock->pool->obj_name, "SSL DH " "initialized, PFS cipher-suites enabled")); } DH_free(dh); } BIO_free(bio); } } } } if (ssock->is_server) { char *p = NULL; /* If certificate file name contains "_rsa.", let's check if there are * ecc and dsa certificates too. */ if (cert && cert->cert_file.slen) { const pj_str_t RSA = {"_rsa.", 5}; p = pj_strstr(&cert->cert_file, &RSA); if (p) p++; /* Skip underscore */ } if (p) { /* Certificate type string length must be exactly 3 */ enum { CERT_TYPE_LEN = 3 }; const char* cert_types[] = { "ecc", "dsa" }; char *cf = cert->cert_file.ptr; int i; /* Check and load ECC & DSA certificates & private keys */ for (i = 0; i < PJ_ARRAY_SIZE(cert_types); ++i) { int err; pj_memcpy(p, cert_types[i], CERT_TYPE_LEN); if (!pj_file_exists(cf)) continue; err = SSL_CTX_use_certificate_chain_file(ctx, cf); if (err == 1) err = SSL_CTX_use_PrivateKey_file(ctx, cf, SSL_FILETYPE_PEM); if (err == 1) { PJ_LOG(4,(ssock->pool->obj_name, "Additional certificate '%s' loaded.", cf)); } else { pj_perror(1, ssock->pool->obj_name, GET_SSL_STATUS(ssock), "Error loading certificate file '%s'", cf); ERR_clear_error(); } } /* Put back original name */ pj_memcpy(p, "rsa", CERT_TYPE_LEN); } #ifndef SSL_CTRL_SET_ECDH_AUTO #define SSL_CTRL_SET_ECDH_AUTO 94 #endif /* SSL_CTX_set_ecdh_auto(ctx,on) requires OpenSSL 1.0.2 which wraps: */ if (SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, 1, NULL)) { PJ_LOG(4,(ssock->pool->obj_name, "SSL ECDH initialized " "(automatic), faster PFS ciphers enabled")); #if !defined(OPENSSL_NO_ECDH) && OPENSSL_VERSION_NUMBER >= 0x10000000L } else { /* enables AES-128 ciphers, to get AES-256 use NID_secp384r1 */ ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); if (ecdh != NULL) { if (SSL_CTX_set_tmp_ecdh(ctx, ecdh)) { PJ_LOG(4,(ssock->pool->obj_name, "SSL ECDH initialized " "(secp256r1), faster PFS cipher-suites enabled")); } EC_KEY_free(ecdh); } #endif } } else { X509_STORE *pkix_validation_store = SSL_CTX_get_cert_store(ctx); if (NULL != pkix_validation_store) { #if defined(X509_V_FLAG_TRUSTED_FIRST) X509_STORE_set_flags(pkix_validation_store, X509_V_FLAG_TRUSTED_FIRST); #endif #if defined(X509_V_FLAG_PARTIAL_CHAIN) X509_STORE_set_flags(pkix_validation_store, X509_V_FLAG_PARTIAL_CHAIN); #endif } } /* Create SSL instance */ ssock->ossl_ctx = ctx; ssock->ossl_ssl = SSL_new(ssock->ossl_ctx); if (ssock->ossl_ssl == NULL) { return GET_SSL_STATUS(ssock); } /* Set SSL sock as application data of SSL instance */ SSL_set_ex_data(ssock->ossl_ssl, sslsock_idx, ssock); /* SSL verification options */ mode = SSL_VERIFY_PEER; if (ssock->is_server && ssock->param.require_client_cert) mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; SSL_set_verify(ssock->ossl_ssl, mode, &verify_cb); /* Set cipher list */ status = set_cipher_list(ssock); if (status != PJ_SUCCESS) return status; /* Setup SSL BIOs */ ssock->ossl_rbio = BIO_new(BIO_s_mem()); ssock->ossl_wbio = BIO_new(BIO_s_mem()); (void)BIO_set_close(ssock->ossl_rbio, BIO_CLOSE); (void)BIO_set_close(ssock->ossl_wbio, BIO_CLOSE); SSL_set_bio(ssock->ossl_ssl, ssock->ossl_rbio, ssock->ossl_wbio); return PJ_SUCCESS; } /* Destroy SSL context and instance */ static void destroy_ssl(pj_ssl_sock_t *ssock) { /* Destroy SSL instance */ if (ssock->ossl_ssl) { SSL_shutdown(ssock->ossl_ssl); SSL_free(ssock->ossl_ssl); /* this will also close BIOs */ ssock->ossl_ssl = NULL; } /* Destroy SSL context */ if (ssock->ossl_ctx) { SSL_CTX_free(ssock->ossl_ctx); ssock->ossl_ctx = NULL; } /* Potentially shutdown OpenSSL library if this is the last * context exists. */ shutdown_openssl(); } /* Reset SSL socket state */ static void reset_ssl_sock_state(pj_ssl_sock_t *ssock) { pj_lock_acquire(ssock->state_mutex); ssock->ssl_state = SSL_STATE_NULL; destroy_ssl(ssock); if (ssock->asock) { pj_activesock_close(ssock->asock); ssock->asock = NULL; ssock->sock = PJ_INVALID_SOCKET; } if (ssock->sock != PJ_INVALID_SOCKET) { pj_sock_close(ssock->sock); ssock->sock = PJ_INVALID_SOCKET; } /* Upon error, OpenSSL may leave any error description in the thread * error queue, which sometime may cause next call to SSL API returning * false error alarm, e.g: in Linux, SSL_CTX_use_certificate_chain_file() * returning false error after a handshake error (in different SSL_CTX!). * For now, just clear thread error queue here. */ ERR_clear_error(); pj_lock_release(ssock->state_mutex); } /* Generate cipher list with user preference order in OpenSSL format */ static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock) { char buf[1024]; pj_str_t cipher_list; STACK_OF(SSL_CIPHER) *sk_cipher; unsigned i; int j, ret; if (ssock->param.ciphers_num == 0) { ret = SSL_set_cipher_list(ssock->ossl_ssl, PJ_SSL_SOCK_OSSL_CIPHERS); if (ret < 1) { return GET_SSL_STATUS(ssock); } return PJ_SUCCESS; } pj_strset(&cipher_list, buf, 0); /* Set SSL with ALL available ciphers */ SSL_set_cipher_list(ssock->ossl_ssl, "ALL:COMPLEMENTOFALL"); /* Generate user specified cipher list in OpenSSL format */ sk_cipher = SSL_get_ciphers(ssock->ossl_ssl); for (i = 0; i < ssock->param.ciphers_num; ++i) { for (j = 0; j < sk_SSL_CIPHER_num(sk_cipher); ++j) { const SSL_CIPHER *c; c = sk_SSL_CIPHER_value(sk_cipher, j); if (ssock->param.ciphers[i] == (pj_ssl_cipher) ((pj_uint32_t)SSL_CIPHER_get_id(c) & 0x00FFFFFF)) { const char *c_name; c_name = SSL_CIPHER_get_name(c); /* Check buffer size */ if (cipher_list.slen + pj_ansi_strlen(c_name) + 2 > sizeof(buf)) { pj_assert(!"Insufficient temporary buffer for cipher"); return PJ_ETOOMANY; } /* Add colon separator */ if (cipher_list.slen) pj_strcat2(&cipher_list, ":"); /* Add the cipher */ pj_strcat2(&cipher_list, c_name); break; } } } /* Put NULL termination in the generated cipher list */ cipher_list.ptr[cipher_list.slen] = '\0'; /* Finally, set chosen cipher list */ ret = SSL_set_cipher_list(ssock->ossl_ssl, buf); if (ret < 1) { return GET_SSL_STATUS(ssock); } return PJ_SUCCESS; } /* Parse OpenSSL ASN1_TIME to pj_time_val and GMT info */ static pj_bool_t parse_ossl_asn1_time(pj_time_val *tv, pj_bool_t *gmt, const ASN1_TIME *tm) { unsigned long parts[7] = {0}; char *p, *end; unsigned len; pj_bool_t utc; pj_parsed_time pt; int i; utc = tm->type == V_ASN1_UTCTIME; p = (char*)tm->data; len = tm->length; end = p + len - 1; /* GMT */ *gmt = (*end == 'Z'); /* parse parts */ for (i = 0; i < 7 && p < end; ++i) { pj_str_t st; if (i==0 && !utc) { /* 4 digits year part for non-UTC time format */ st.slen = 4; } else if (i==6) { /* fraction of seconds */ if (*p == '.') ++p; st.slen = end - p + 1; } else { /* other parts always 2 digits length */ st.slen = 2; } st.ptr = p; parts[i] = pj_strtoul(&st); p += st.slen; } /* encode parts to pj_time_val */ pt.year = parts[0]; if (utc) pt.year += (pt.year < 50)? 2000:1900; pt.mon = parts[1] - 1; pt.day = parts[2]; pt.hour = parts[3]; pt.min = parts[4]; pt.sec = parts[5]; pt.msec = parts[6]; pj_time_encode(&pt, tv); return PJ_TRUE; } /* Get Common Name field string from a general name string */ static void get_cn_from_gen_name(const pj_str_t *gen_name, pj_str_t *cn) { pj_str_t CN_sign = {"/CN=", 4}; char *p, *q; pj_bzero(cn, sizeof(pj_str_t)); p = pj_strstr(gen_name, &CN_sign); if (!p) return; p += 4; /* shift pointer to value part */ pj_strset(cn, p, gen_name->slen - (p - gen_name->ptr)); q = pj_strchr(cn, '/'); if (q) cn->slen = q - p; } /* Get certificate info from OpenSSL X509, in case the certificate info * hal already populated, this function will check if the contents need * to be updated by inspecting the issuer and the serial number. */ static void get_cert_info(pj_pool_t *pool, pj_ssl_cert_info *ci, X509 *x, pj_bool_t get_pem) { pj_bool_t update_needed; char buf[512]; pj_uint8_t serial_no[64] = {0}; /* should be >= sizeof(ci->serial_no) */ const pj_uint8_t *q; unsigned len; GENERAL_NAMES *names = NULL; pj_assert(pool && ci && x); /* Get issuer */ X509_NAME_oneline(X509_get_issuer_name(x), buf, sizeof(buf)); /* Get serial no */ q = (const pj_uint8_t*) M_ASN1_STRING_data(X509_get_serialNumber(x)); len = M_ASN1_STRING_length(X509_get_serialNumber(x)); if (len > sizeof(ci->serial_no)) len = sizeof(ci->serial_no); pj_memcpy(serial_no + sizeof(ci->serial_no) - len, q, len); /* Check if the contents need to be updated. */ update_needed = pj_strcmp2(&ci->issuer.info, buf) || pj_memcmp(ci->serial_no, serial_no, sizeof(ci->serial_no)); if (!update_needed) return; /* Update cert info */ pj_bzero(ci, sizeof(pj_ssl_cert_info)); /* Version */ ci->version = X509_get_version(x) + 1; /* Issuer */ pj_strdup2(pool, &ci->issuer.info, buf); get_cn_from_gen_name(&ci->issuer.info, &ci->issuer.cn); /* Serial number */ pj_memcpy(ci->serial_no, serial_no, sizeof(ci->serial_no)); /* Subject */ pj_strdup2(pool, &ci->subject.info, X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf))); get_cn_from_gen_name(&ci->subject.info, &ci->subject.cn); /* Validity */ parse_ossl_asn1_time(&ci->validity.start, &ci->validity.gmt, X509_get_notBefore(x)); parse_ossl_asn1_time(&ci->validity.end, &ci->validity.gmt, X509_get_notAfter(x)); /* Subject Alternative Name extension */ if (ci->version >= 3) { names = (GENERAL_NAMES*) X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL); } if (names) { unsigned i, cnt; cnt = sk_GENERAL_NAME_num(names); ci->subj_alt_name.entry = pj_pool_calloc(pool, cnt, sizeof(*ci->subj_alt_name.entry)); for (i = 0; i < cnt; ++i) { unsigned char *p = 0; pj_ssl_cert_name_type type = PJ_SSL_CERT_NAME_UNKNOWN; const GENERAL_NAME *name; name = sk_GENERAL_NAME_value(names, i); switch (name->type) { case GEN_EMAIL: len = ASN1_STRING_to_UTF8(&p, name->d.ia5); type = PJ_SSL_CERT_NAME_RFC822; break; case GEN_DNS: len = ASN1_STRING_to_UTF8(&p, name->d.ia5); type = PJ_SSL_CERT_NAME_DNS; break; case GEN_URI: len = ASN1_STRING_to_UTF8(&p, name->d.ia5); type = PJ_SSL_CERT_NAME_URI; break; case GEN_IPADD: p = (unsigned char*)M_ASN1_STRING_data(name->d.ip); len = M_ASN1_STRING_length(name->d.ip); type = PJ_SSL_CERT_NAME_IP; break; default: break; } if (p && len && type != PJ_SSL_CERT_NAME_UNKNOWN) { ci->subj_alt_name.entry[ci->subj_alt_name.cnt].type = type; if (type == PJ_SSL_CERT_NAME_IP) { int af = pj_AF_INET(); if (len == sizeof(pj_in6_addr)) af = pj_AF_INET6(); pj_inet_ntop2(af, p, buf, sizeof(buf)); pj_strdup2(pool, &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name, buf); } else { pj_strdup2(pool, &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name, (char*)p); OPENSSL_free(p); } ci->subj_alt_name.cnt++; } } } if (get_pem) { /* Update raw Certificate info in PEM format. */ BIO *bio; BUF_MEM *ptr; bio = BIO_new(BIO_s_mem()); if (!PEM_write_bio_X509(bio, x)) { PJ_LOG(3,(THIS_FILE, "Error retrieving raw certificate info")); ci->raw.ptr = NULL; ci->raw.slen = 0; } else { BIO_write(bio, "\0", 1); BIO_get_mem_ptr(bio, &ptr); pj_strdup2(pool, &ci->raw, ptr->data); } BIO_free(bio); } } /* Update local & remote certificates info. This function should be * called after handshake or renegotiation successfully completed. */ static void update_certs_info(pj_ssl_sock_t *ssock) { X509 *x; pj_assert(ssock->ssl_state == SSL_STATE_ESTABLISHED); /* Active local certificate */ x = SSL_get_certificate(ssock->ossl_ssl); if (x) { get_cert_info(ssock->pool, &ssock->local_cert_info, x, PJ_FALSE); /* Don't free local's X509! */ } else { pj_bzero(&ssock->local_cert_info, sizeof(pj_ssl_cert_info)); } /* Active remote certificate */ x = SSL_get_peer_certificate(ssock->ossl_ssl); if (x) { get_cert_info(ssock->pool, &ssock->remote_cert_info, x, PJ_TRUE); /* Free peer's X509 */ X509_free(x); } else { pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info)); } } /* When handshake completed: * - notify application * - if handshake failed, reset SSL state * - return PJ_FALSE when SSL socket instance is destroyed by application. */ static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock, pj_status_t status) { /* Cancel handshake timer */ if (ssock->timer.id == TIMER_HANDSHAKE_TIMEOUT) { pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer); ssock->timer.id = TIMER_NONE; } /* Update certificates info on successful handshake */ if (status == PJ_SUCCESS) update_certs_info(ssock); /* Accepting */ if (ssock->is_server) { if (status != PJ_SUCCESS) { /* Handshake failed in accepting, destroy our self silently. */ char errmsg[PJ_ERR_MSG_SIZE]; char buf[PJ_INET6_ADDRSTRLEN+10]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(3,(ssock->pool->obj_name, "Handshake failed in accepting " "%s: %s", pj_sockaddr_print(&ssock->rem_addr, buf, sizeof(buf), 3), errmsg)); /* Workaround for ticket #985 */ #if (defined(PJ_WIN32) && PJ_WIN32!=0) || (defined(PJ_WIN64) && PJ_WIN64!=0) if (ssock->param.timer_heap) { pj_time_val interval = {0, DELAYED_CLOSE_TIMEOUT}; reset_ssl_sock_state(ssock); ssock->timer.id = TIMER_CLOSE; pj_time_val_normalize(&interval); if (pj_timer_heap_schedule(ssock->param.timer_heap, &ssock->timer, &interval) != 0) { ssock->timer.id = TIMER_NONE; pj_ssl_sock_close(ssock); } } else #endif /* PJ_WIN32 */ { pj_ssl_sock_close(ssock); } return PJ_FALSE; } /* Notify application the newly accepted SSL socket */ if (ssock->param.cb.on_accept_complete) { pj_bool_t ret; ret = (*ssock->param.cb.on_accept_complete) (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr, pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr)); if (ret == PJ_FALSE) return PJ_FALSE; } } /* Connecting */ else { /* On failure, reset SSL socket state first, as app may try to * reconnect in the callback. */ if (status != PJ_SUCCESS) { /* Server disconnected us, possibly due to SSL nego failure */ if (status == PJ_EEOF) { unsigned long err; err = ERR_get_error(); if (err != SSL_ERROR_NONE) status = STATUS_FROM_SSL_ERR(ssock, err); } reset_ssl_sock_state(ssock); } if (ssock->param.cb.on_connect_complete) { pj_bool_t ret; ret = (*ssock->param.cb.on_connect_complete)(ssock, status); if (ret == PJ_FALSE) return PJ_FALSE; } } return PJ_TRUE; } static write_data_t* alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len) { send_buf_t *send_buf = &ssock->send_buf; pj_size_t avail_len, skipped_len = 0; char *reg1, *reg2; pj_size_t reg1_len, reg2_len; write_data_t *p; /* Check buffer availability */ avail_len = send_buf->max_len - send_buf->len; if (avail_len < len) return NULL; /* If buffer empty, reset start pointer and return it */ if (send_buf->len == 0) { send_buf->start = send_buf->buf; send_buf->len = len; p = (write_data_t*)send_buf->start; goto init_send_data; } /* Free space may be wrapped/splitted into two regions, so let's * analyze them if any region can hold the write data. */ reg1 = send_buf->start + send_buf->len; if (reg1 >= send_buf->buf + send_buf->max_len) reg1 -= send_buf->max_len; reg1_len = send_buf->max_len - send_buf->len; if (reg1 + reg1_len > send_buf->buf + send_buf->max_len) { reg1_len = send_buf->buf + send_buf->max_len - reg1; reg2 = send_buf->buf; reg2_len = send_buf->start - send_buf->buf; } else { reg2 = NULL; reg2_len = 0; } /* More buffer availability check, note that the write data must be in * a contigue buffer. */ avail_len = PJ_MAX(reg1_len, reg2_len); if (avail_len < len) return NULL; /* Get the data slot */ if (reg1_len >= len) { p = (write_data_t*)reg1; } else { p = (write_data_t*)reg2; skipped_len = reg1_len; } /* Update buffer length */ send_buf->len += len + skipped_len; init_send_data: /* Init the new send data */ pj_bzero(p, sizeof(*p)); pj_list_init(p); pj_list_push_back(&ssock->send_pending, p); return p; } static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata) { send_buf_t *buf = &ssock->send_buf; write_data_t *spl = &ssock->send_pending; pj_assert(!pj_list_empty(&ssock->send_pending)); /* Free slot from the buffer */ if (spl->next == wdata && spl->prev == wdata) { /* This is the only data, reset the buffer */ buf->start = buf->buf; buf->len = 0; } else if (spl->next == wdata) { /* This is the first data, shift start pointer of the buffer and * adjust the buffer length. */ buf->start = (char*)wdata->next; if (wdata->next > wdata) { buf->len -= ((char*)wdata->next - buf->start); } else { /* Overlapped */ pj_size_t right_len, left_len; right_len = buf->buf + buf->max_len - (char*)wdata; left_len = (char*)wdata->next - buf->buf; buf->len -= (right_len + left_len); } } else if (spl->prev == wdata) { /* This is the last data, just adjust the buffer length */ if (wdata->prev < wdata) { pj_size_t jump_len; jump_len = (char*)wdata - ((char*)wdata->prev + wdata->prev->record_len); buf->len -= (wdata->record_len + jump_len); } else { /* Overlapped */ pj_size_t right_len, left_len; right_len = buf->buf + buf->max_len - ((char*)wdata->prev + wdata->prev->record_len); left_len = (char*)wdata + wdata->record_len - buf->buf; buf->len -= (right_len + left_len); } } /* For data in the middle buffer, just do nothing on the buffer. The slot * will be freed later when freeing the first/last data. */ /* Remove the data from send pending list */ pj_list_erase(wdata); } #if 0 /* Just for testing send buffer alloc/free */ #include pj_status_t pj_ssl_sock_ossl_test_send_buf(pj_pool_t *pool) { enum { MAX_CHUNK_NUM = 20 }; unsigned chunk_size, chunk_cnt, i; write_data_t *wdata[MAX_CHUNK_NUM] = {0}; pj_time_val now; pj_ssl_sock_t *ssock = NULL; pj_ssl_sock_param param; pj_status_t status; pj_gettimeofday(&now); pj_srand((unsigned)now.sec); pj_ssl_sock_param_default(¶m); status = pj_ssl_sock_create(pool, ¶m, &ssock); if (status != PJ_SUCCESS) { return status; } if (ssock->send_buf.max_len == 0) { ssock->send_buf.buf = (char*) pj_pool_alloc(ssock->pool, ssock->param.send_buffer_size); ssock->send_buf.max_len = ssock->param.send_buffer_size; ssock->send_buf.start = ssock->send_buf.buf; ssock->send_buf.len = 0; } chunk_size = ssock->param.send_buffer_size / MAX_CHUNK_NUM / 2; chunk_cnt = 0; for (i = 0; i < MAX_CHUNK_NUM; i++) { wdata[i] = alloc_send_data(ssock, pj_rand() % chunk_size + 321); if (wdata[i]) chunk_cnt++; else break; } while (chunk_cnt) { i = pj_rand() % MAX_CHUNK_NUM; if (wdata[i]) { free_send_data(ssock, wdata[i]); wdata[i] = NULL; chunk_cnt--; } } if (ssock->send_buf.len != 0) status = PJ_EBUG; pj_ssl_sock_close(ssock); return status; } #endif /* Flush write BIO to network socket. Note that any access to write BIO * MUST be serialized, so mutex protection must cover any call to OpenSSL * API (that possibly generate data for write BIO) along with the call to * this function (flushing all data in write BIO generated by above * OpenSSL API call). */ static pj_status_t flush_write_bio(pj_ssl_sock_t *ssock, pj_ioqueue_op_key_t *send_key, pj_size_t orig_len, unsigned flags) { char *data; pj_ssize_t len; write_data_t *wdata; pj_size_t needed_len; pj_status_t status; pj_lock_acquire(ssock->write_mutex); /* Check if there is data in write BIO, flush it if any */ if (!BIO_pending(ssock->ossl_wbio)) { pj_lock_release(ssock->write_mutex); return PJ_SUCCESS; } /* Get data and its length */ len = BIO_get_mem_data(ssock->ossl_wbio, &data); if (len == 0) { pj_lock_release(ssock->write_mutex); return PJ_SUCCESS; } /* Calculate buffer size needed, and align it to 8 */ needed_len = len + sizeof(write_data_t); needed_len = ((needed_len + 7) >> 3) << 3; /* Allocate buffer for send data */ wdata = alloc_send_data(ssock, needed_len); if (wdata == NULL) { pj_lock_release(ssock->write_mutex); return PJ_ENOMEM; } /* Copy the data and set its properties into the send data */ pj_ioqueue_op_key_init(&wdata->key, sizeof(pj_ioqueue_op_key_t)); wdata->key.user_data = wdata; wdata->app_key = send_key; wdata->record_len = needed_len; wdata->data_len = len; wdata->plain_data_len = orig_len; wdata->flags = flags; pj_memcpy(&wdata->data, data, len); /* Reset write BIO */ (void)BIO_reset(ssock->ossl_wbio); /* Ticket #1573: Don't hold mutex while calling PJLIB socket send(). */ pj_lock_release(ssock->write_mutex); /* Send it */ if (ssock->param.sock_type == pj_SOCK_STREAM()) { status = pj_activesock_send(ssock->asock, &wdata->key, wdata->data.content, &len, flags); } else { status = pj_activesock_sendto(ssock->asock, &wdata->key, wdata->data.content, &len, flags, (pj_sockaddr_t*)&ssock->rem_addr, ssock->addr_len); } if (status != PJ_EPENDING) { /* When the sending is not pending, remove the wdata from send * pending list. */ pj_lock_acquire(ssock->write_mutex); free_send_data(ssock, wdata); pj_lock_release(ssock->write_mutex); } return status; } static void on_timer(pj_timer_heap_t *th, struct pj_timer_entry *te) { pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)te->user_data; int timer_id = te->id; te->id = TIMER_NONE; PJ_UNUSED_ARG(th); switch (timer_id) { case TIMER_HANDSHAKE_TIMEOUT: PJ_LOG(1,(ssock->pool->obj_name, "SSL timeout after %d.%ds", ssock->param.timeout.sec, ssock->param.timeout.msec)); on_handshake_complete(ssock, PJ_ETIMEDOUT); break; case TIMER_CLOSE: pj_ssl_sock_close(ssock); break; default: pj_assert(!"Unknown timer"); break; } } /* Asynchronouse handshake */ static pj_status_t do_handshake(pj_ssl_sock_t *ssock) { pj_status_t status; int err; /* Perform SSL handshake */ pj_lock_acquire(ssock->write_mutex); err = SSL_do_handshake(ssock->ossl_ssl); pj_lock_release(ssock->write_mutex); /* SSL_do_handshake() may put some pending data into SSL write BIO, * flush it if any. */ status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0); if (status != PJ_SUCCESS && status != PJ_EPENDING) { return status; } if (err < 0) { err = SSL_get_error(ssock->ossl_ssl, err); if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) { /* Handshake fails */ status = STATUS_FROM_SSL_ERR(ssock, err); return status; } } /* Check if handshake has been completed */ if (SSL_is_init_finished(ssock->ossl_ssl)) { ssock->ssl_state = SSL_STATE_ESTABLISHED; return PJ_SUCCESS; } return PJ_EPENDING; } /* ******************************************************************* * Active socket callbacks. ******************************************************************* */ static pj_bool_t asock_on_data_read (pj_activesock_t *asock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder) { pj_ssl_sock_t *ssock = (pj_ssl_sock_t*) pj_activesock_get_user_data(asock); pj_size_t nwritten; /* Socket error or closed */ if (data && size > 0) { /* Consume the whole data */ nwritten = BIO_write(ssock->ossl_rbio, data, (int)size); if (nwritten < size) { status = GET_SSL_STATUS(ssock); goto on_error; } } /* Check if SSL handshake hasn't finished yet */ if (ssock->ssl_state == SSL_STATE_HANDSHAKING) { pj_bool_t ret = PJ_TRUE; if (status == PJ_SUCCESS) status = do_handshake(ssock); /* Not pending is either success or failed */ if (status != PJ_EPENDING) ret = on_handshake_complete(ssock, status); return ret; } /* See if there is any decrypted data for the application */ if (ssock->read_started) { do { read_data_t *buf = *(OFFSET_OF_READ_DATA_PTR(ssock, data)); void *data_ = (pj_int8_t*)buf->data + buf->len; int size_ = (int)(ssock->read_size - buf->len); /* SSL_read() may write some data to BIO write when re-negotiation * is on progress, so let's protect it with write mutex. */ pj_lock_acquire(ssock->write_mutex); size_ = SSL_read(ssock->ossl_ssl, data_, size_); pj_lock_release(ssock->write_mutex); if (size_ > 0 || status != PJ_SUCCESS) { if (ssock->param.cb.on_data_read) { pj_bool_t ret; pj_size_t remainder_ = 0; if (size_ > 0) buf->len += size_; ret = (*ssock->param.cb.on_data_read)(ssock, buf->data, buf->len, status, &remainder_); if (!ret) { /* We've been destroyed */ return PJ_FALSE; } /* Application may have left some data to be consumed * later. */ buf->len = remainder_; } /* Active socket signalled connection closed/error, this has * been signalled to the application along with any remaining * buffer. So, let's just reset SSL socket now. */ if (status != PJ_SUCCESS) { reset_ssl_sock_state(ssock); return PJ_FALSE; } } else { int err = SSL_get_error(ssock->ossl_ssl, size_); /* SSL might just return SSL_ERROR_WANT_READ in * re-negotiation. */ if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) { /* Reset SSL socket state, then return PJ_FALSE */ status = STATUS_FROM_SSL_ERR(ssock, err); reset_ssl_sock_state(ssock); goto on_error; } status = do_handshake(ssock); if (status == PJ_SUCCESS) { /* Renegotiation completed */ /* Update certificates */ update_certs_info(ssock); // Ticket #1573: Don't hold mutex while calling // PJLIB socket send(). //pj_lock_acquire(ssock->write_mutex); status = flush_delayed_send(ssock); //pj_lock_release(ssock->write_mutex); /* If flushing is ongoing, treat it as success */ if (status == PJ_EBUSY) status = PJ_SUCCESS; if (status != PJ_SUCCESS && status != PJ_EPENDING) { PJ_PERROR(1,(ssock->pool->obj_name, status, "Failed to flush delayed send")); goto on_error; } } else if (status != PJ_EPENDING) { PJ_PERROR(1,(ssock->pool->obj_name, status, "Renegotiation failed")); goto on_error; } break; } } while (1); } return PJ_TRUE; on_error: if (ssock->ssl_state == SSL_STATE_HANDSHAKING) return on_handshake_complete(ssock, status); if (ssock->read_started && ssock->param.cb.on_data_read) { pj_bool_t ret; ret = (*ssock->param.cb.on_data_read)(ssock, NULL, 0, status, remainder); if (!ret) { /* We've been destroyed */ return PJ_FALSE; } } reset_ssl_sock_state(ssock); return PJ_FALSE; } static pj_bool_t asock_on_data_sent (pj_activesock_t *asock, pj_ioqueue_op_key_t *send_key, pj_ssize_t sent) { pj_ssl_sock_t *ssock = (pj_ssl_sock_t*) pj_activesock_get_user_data(asock); PJ_UNUSED_ARG(send_key); PJ_UNUSED_ARG(sent); if (ssock->ssl_state == SSL_STATE_HANDSHAKING) { /* Initial handshaking */ pj_status_t status; status = do_handshake(ssock); /* Not pending is either success or failed */ if (status != PJ_EPENDING) return on_handshake_complete(ssock, status); } else if (send_key != &ssock->handshake_op_key) { /* Some data has been sent, notify application */ write_data_t *wdata = (write_data_t*)send_key->user_data; if (ssock->param.cb.on_data_sent) { pj_bool_t ret; pj_ssize_t sent_len; sent_len = (sent > 0)? wdata->plain_data_len : sent; ret = (*ssock->param.cb.on_data_sent)(ssock, wdata->app_key, sent_len); if (!ret) { /* We've been destroyed */ return PJ_FALSE; } } /* Update write buffer state */ pj_lock_acquire(ssock->write_mutex); free_send_data(ssock, wdata); pj_lock_release(ssock->write_mutex); } else { /* SSL re-negotiation is on-progress, just do nothing */ } return PJ_TRUE; } static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock, pj_sock_t newsock, const pj_sockaddr_t *src_addr, int src_addr_len) { pj_ssl_sock_t *ssock_parent = (pj_ssl_sock_t*) pj_activesock_get_user_data(asock); pj_ssl_sock_t *ssock; pj_activesock_cb asock_cb; pj_activesock_cfg asock_cfg; unsigned i; pj_status_t status; /* Create new SSL socket instance */ status = pj_ssl_sock_create(ssock_parent->pool, &ssock_parent->newsock_param, &ssock); if (status != PJ_SUCCESS) goto on_return; /* Update new SSL socket attributes */ ssock->sock = newsock; ssock->parent = ssock_parent; ssock->is_server = PJ_TRUE; if (ssock_parent->cert) { status = pj_ssl_sock_set_certificate(ssock, ssock->pool, ssock_parent->cert); if (status != PJ_SUCCESS) goto on_return; } /* Apply QoS, if specified */ status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type, &ssock->param.qos_params, 1, ssock->pool->obj_name, NULL); if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error) goto on_return; /* Apply socket options, if specified */ if (ssock->param.sockopt_params.cnt) { status = pj_sock_setsockopt_params(ssock->sock, &ssock->param.sockopt_params); if (status != PJ_SUCCESS && !ssock->param.sockopt_ignore_error) goto on_return; } /* Update local address */ ssock->addr_len = src_addr_len; status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, &ssock->addr_len); if (status != PJ_SUCCESS) { /* This fails on few envs, e.g: win IOCP, just tolerate this and * use parent local address instead. */ pj_sockaddr_cp(&ssock->local_addr, &ssock_parent->local_addr); } /* Set remote address */ pj_sockaddr_cp(&ssock->rem_addr, src_addr); /* Create SSL context */ status = create_ssl(ssock); if (status != PJ_SUCCESS) goto on_return; /* Prepare read buffer */ ssock->asock_rbuf = (void**)pj_pool_calloc(ssock->pool, ssock->param.async_cnt, sizeof(void*)); for (i = 0; iparam.async_cnt; ++i) { ssock->asock_rbuf[i] = (void*) pj_pool_alloc( ssock->pool, ssock->param.read_buffer_size + sizeof(read_data_t*)); } /* Create active socket */ pj_activesock_cfg_default(&asock_cfg); asock_cfg.async_cnt = ssock->param.async_cnt; asock_cfg.concurrency = ssock->param.concurrency; asock_cfg.whole_data = PJ_TRUE; /* If listener socket has group lock, automatically create group lock * for the new socket. */ if (ssock_parent->param.grp_lock) { pj_grp_lock_t *glock; status = pj_grp_lock_create(ssock->pool, NULL, &glock); if (status != PJ_SUCCESS) goto on_return; /* Temporarily add ref the group lock until active socket creation, * to make sure that group lock is destroyed if the active socket * creation fails. */ pj_grp_lock_add_ref(glock); asock_cfg.grp_lock = ssock->param.grp_lock = glock; } pj_bzero(&asock_cb, sizeof(asock_cb)); asock_cb.on_data_read = asock_on_data_read; asock_cb.on_data_sent = asock_on_data_sent; status = pj_activesock_create(ssock->pool, ssock->sock, ssock->param.sock_type, &asock_cfg, ssock->param.ioqueue, &asock_cb, ssock, &ssock->asock); /* This will destroy the group lock if active socket creation fails */ if (asock_cfg.grp_lock) { pj_grp_lock_dec_ref(asock_cfg.grp_lock); } if (status != PJ_SUCCESS) goto on_return; /* Start read */ status = pj_activesock_start_read2(ssock->asock, ssock->pool, (unsigned)ssock->param.read_buffer_size, ssock->asock_rbuf, PJ_IOQUEUE_ALWAYS_ASYNC); if (status != PJ_SUCCESS) goto on_return; /* Prepare write/send state */ pj_assert(ssock->send_buf.max_len == 0); ssock->send_buf.buf = (char*) pj_pool_alloc(ssock->pool, ssock->param.send_buffer_size); ssock->send_buf.max_len = ssock->param.send_buffer_size; ssock->send_buf.start = ssock->send_buf.buf; ssock->send_buf.len = 0; /* Start handshake timer */ if (ssock->param.timer_heap && (ssock->param.timeout.sec != 0 || ssock->param.timeout.msec != 0)) { pj_assert(ssock->timer.id == TIMER_NONE); ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT; status = pj_timer_heap_schedule(ssock->param.timer_heap, &ssock->timer, &ssock->param.timeout); if (status != PJ_SUCCESS) ssock->timer.id = TIMER_NONE; } /* Start SSL handshake */ ssock->ssl_state = SSL_STATE_HANDSHAKING; SSL_set_accept_state(ssock->ossl_ssl); status = do_handshake(ssock); on_return: if (ssock && status != PJ_EPENDING) on_handshake_complete(ssock, status); /* Must return PJ_TRUE whatever happened, as active socket must * continue listening. */ return PJ_TRUE; } static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock, pj_status_t status) { pj_ssl_sock_t *ssock = (pj_ssl_sock_t*) pj_activesock_get_user_data(asock); unsigned i; if (status != PJ_SUCCESS) goto on_return; /* Update local address */ ssock->addr_len = sizeof(pj_sockaddr); status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, &ssock->addr_len); if (status != PJ_SUCCESS) goto on_return; /* Create SSL context */ status = create_ssl(ssock); if (status != PJ_SUCCESS) goto on_return; /* Prepare read buffer */ ssock->asock_rbuf = (void**)pj_pool_calloc(ssock->pool, ssock->param.async_cnt, sizeof(void*)); for (i = 0; iparam.async_cnt; ++i) { ssock->asock_rbuf[i] = (void*) pj_pool_alloc( ssock->pool, ssock->param.read_buffer_size + sizeof(read_data_t*)); } /* Start read */ status = pj_activesock_start_read2(ssock->asock, ssock->pool, (unsigned)ssock->param.read_buffer_size, ssock->asock_rbuf, PJ_IOQUEUE_ALWAYS_ASYNC); if (status != PJ_SUCCESS) goto on_return; /* Prepare write/send state */ pj_assert(ssock->send_buf.max_len == 0); ssock->send_buf.buf = (char*) pj_pool_alloc(ssock->pool, ssock->param.send_buffer_size); ssock->send_buf.max_len = ssock->param.send_buffer_size; ssock->send_buf.start = ssock->send_buf.buf; ssock->send_buf.len = 0; #ifdef SSL_set_tlsext_host_name /* Set server name to connect */ if (ssock->param.server_name.slen) { /* Server name is null terminated already */ if (!SSL_set_tlsext_host_name(ssock->ossl_ssl, ssock->param.server_name.ptr)) { char err_str[PJ_ERR_MSG_SIZE]; ERR_error_string_n(ERR_get_error(), err_str, sizeof(err_str)); PJ_LOG(3,(ssock->pool->obj_name, "SSL_set_tlsext_host_name() " "failed: %s", err_str)); } } #endif /* Start SSL handshake */ ssock->ssl_state = SSL_STATE_HANDSHAKING; SSL_set_connect_state(ssock->ossl_ssl); status = do_handshake(ssock); if (status != PJ_EPENDING) goto on_return; return PJ_TRUE; on_return: return on_handshake_complete(ssock, status); } /* ******************************************************************* * API ******************************************************************* */ /* Load credentials from files. */ PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files (pj_pool_t *pool, const pj_str_t *CA_file, const pj_str_t *cert_file, const pj_str_t *privkey_file, const pj_str_t *privkey_pass, pj_ssl_cert_t **p_cert) { return pj_ssl_cert_load_from_files2(pool, CA_file, NULL, cert_file, privkey_file, privkey_pass, p_cert); } PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files2(pj_pool_t *pool, const pj_str_t *CA_file, const pj_str_t *CA_path, const pj_str_t *cert_file, const pj_str_t *privkey_file, const pj_str_t *privkey_pass, pj_ssl_cert_t **p_cert) { pj_ssl_cert_t *cert; PJ_ASSERT_RETURN(pool && (CA_file || CA_path) && cert_file && privkey_file, PJ_EINVAL); cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t); if (CA_file) { pj_strdup_with_null(pool, &cert->CA_file, CA_file); } if (CA_path) { pj_strdup_with_null(pool, &cert->CA_path, CA_path); } pj_strdup_with_null(pool, &cert->cert_file, cert_file); pj_strdup_with_null(pool, &cert->privkey_file, privkey_file); pj_strdup_with_null(pool, &cert->privkey_pass, privkey_pass); *p_cert = cert; return PJ_SUCCESS; } /* Set SSL socket credentials. */ PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate( pj_ssl_sock_t *ssock, pj_pool_t *pool, const pj_ssl_cert_t *cert) { pj_ssl_cert_t *cert_; PJ_ASSERT_RETURN(ssock && pool && cert, PJ_EINVAL); cert_ = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t); pj_memcpy(cert_, cert, sizeof(pj_ssl_cert_t)); pj_strdup_with_null(pool, &cert_->CA_file, &cert->CA_file); pj_strdup_with_null(pool, &cert_->CA_path, &cert->CA_path); pj_strdup_with_null(pool, &cert_->cert_file, &cert->cert_file); pj_strdup_with_null(pool, &cert_->privkey_file, &cert->privkey_file); pj_strdup_with_null(pool, &cert_->privkey_pass, &cert->privkey_pass); ssock->cert = cert_; return PJ_SUCCESS; } /* Get available ciphers. */ PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[], unsigned *cipher_num) { unsigned i; PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL); if (openssl_cipher_num == 0) { init_openssl(); shutdown_openssl(); } if (openssl_cipher_num == 0) { *cipher_num = 0; return PJ_ENOTFOUND; } *cipher_num = PJ_MIN(*cipher_num, openssl_cipher_num); for (i = 0; i < *cipher_num; ++i) ciphers[i] = openssl_ciphers[i].id; return PJ_SUCCESS; } /* Get cipher name string */ PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher) { unsigned i; if (openssl_cipher_num == 0) { init_openssl(); shutdown_openssl(); } for (i = 0; i < openssl_cipher_num; ++i) { if (cipher == openssl_ciphers[i].id) return openssl_ciphers[i].name; } return NULL; } /* Get cipher identifier */ PJ_DEF(pj_ssl_cipher) pj_ssl_cipher_id(const char *cipher_name) { unsigned i; if (openssl_cipher_num == 0) { init_openssl(); shutdown_openssl(); } for (i = 0; i < openssl_cipher_num; ++i) { if (!pj_ansi_stricmp(openssl_ciphers[i].name, cipher_name)) return openssl_ciphers[i].id; } return PJ_TLS_UNKNOWN_CIPHER; } /* Check if the specified cipher is supported by SSL/TLS backend. */ PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher) { unsigned i; if (openssl_cipher_num == 0) { init_openssl(); shutdown_openssl(); } for (i = 0; i < openssl_cipher_num; ++i) { if (cipher == openssl_ciphers[i].id) return PJ_TRUE; } return PJ_FALSE; } /* * Create SSL socket instance. */ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool, const pj_ssl_sock_param *param, pj_ssl_sock_t **p_ssock) { pj_ssl_sock_t *ssock; pj_status_t status; PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL); PJ_ASSERT_RETURN(param->sock_type == pj_SOCK_STREAM(), PJ_ENOTSUP); pool = pj_pool_create(pool->factory, "ssl%p", 512, 512, NULL); /* Create secure socket */ ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t); ssock->pool = pool; ssock->sock = PJ_INVALID_SOCKET; ssock->ssl_state = SSL_STATE_NULL; pj_list_init(&ssock->write_pending); pj_list_init(&ssock->write_pending_empty); pj_list_init(&ssock->send_pending); pj_timer_entry_init(&ssock->timer, 0, ssock, &on_timer); pj_ioqueue_op_key_init(&ssock->handshake_op_key, sizeof(pj_ioqueue_op_key_t)); /* Create secure socket mutex */ status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &ssock->write_mutex); if (status != PJ_SUCCESS) return status; /* Create socket state mutex */ status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &ssock->state_mutex); if (status != PJ_SUCCESS) return status; /* Init secure socket param */ pj_ssl_sock_param_copy(pool, &ssock->param, param); ssock->param.read_buffer_size = ((ssock->param.read_buffer_size+7)>>3)<<3; /* Finally */ *p_ssock = ssock; return PJ_SUCCESS; } /* * Close the secure socket. This will unregister the socket from the * ioqueue and ultimately close the socket. */ PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock) { pj_pool_t *pool; PJ_ASSERT_RETURN(ssock, PJ_EINVAL); if (!ssock->pool) return PJ_SUCCESS; if (ssock->timer.id != TIMER_NONE) { pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer); ssock->timer.id = TIMER_NONE; } reset_ssl_sock_state(ssock); pj_lock_destroy(ssock->write_mutex); pj_lock_destroy(ssock->state_mutex); pool = ssock->pool; ssock->pool = NULL; if (pool) pj_pool_release(pool); return PJ_SUCCESS; } /* * Associate arbitrary data with the secure socket. */ PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data(pj_ssl_sock_t *ssock, void *user_data) { PJ_ASSERT_RETURN(ssock, PJ_EINVAL); ssock->param.user_data = user_data; return PJ_SUCCESS; } /* * Retrieve the user data previously associated with this secure * socket. */ PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock) { PJ_ASSERT_RETURN(ssock, NULL); return ssock->param.user_data; } /* * Retrieve the local address and port used by specified SSL socket. */ PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock, pj_ssl_sock_info *info) { pj_bzero(info, sizeof(*info)); /* Established flag */ info->established = (ssock->ssl_state == SSL_STATE_ESTABLISHED); /* Protocol */ info->proto = ssock->param.proto; /* Local address */ pj_sockaddr_cp(&info->local_addr, &ssock->local_addr); if (info->established) { const SSL_CIPHER *cipher; /* Current cipher */ cipher = SSL_get_current_cipher(ssock->ossl_ssl); info->cipher = (SSL_CIPHER_get_id(cipher) & 0x00FFFFFF); /* Remote address */ pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr); /* Certificates info */ info->local_cert_info = &ssock->local_cert_info; info->remote_cert_info = &ssock->remote_cert_info; /* Verification status */ info->verify_status = ssock->verify_status; } /* Last known OpenSSL error code */ info->last_native_err = ssock->last_err; /* Group lock */ info->grp_lock = ssock->param.grp_lock; return PJ_SUCCESS; } /* * Starts read operation on this secure socket. */ PJ_DEF(pj_status_t) pj_ssl_sock_start_read (pj_ssl_sock_t *ssock, pj_pool_t *pool, unsigned buff_size, pj_uint32_t flags) { void **readbuf; unsigned i; PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL); if (ssock->ssl_state != SSL_STATE_ESTABLISHED) return PJ_EINVALIDOP; readbuf = (void**) pj_pool_calloc(pool, ssock->param.async_cnt, sizeof(void*)); for (i=0; iparam.async_cnt; ++i) { readbuf[i] = pj_pool_alloc(pool, buff_size); } return pj_ssl_sock_start_read2(ssock, pool, buff_size, readbuf, flags); } /* * Same as #pj_ssl_sock_start_read(), except that the application * supplies the buffers for the read operation so that the acive socket * does not have to allocate the buffers. */ PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock, pj_pool_t *pool, unsigned buff_size, void *readbuf[], pj_uint32_t flags) { unsigned i; PJ_ASSERT_RETURN(ssock && pool && buff_size && readbuf, PJ_EINVAL); if (ssock->ssl_state != SSL_STATE_ESTABLISHED) return PJ_EINVALIDOP; /* Create SSL socket read buffer */ ssock->ssock_rbuf = (read_data_t*)pj_pool_calloc(pool, ssock->param.async_cnt, sizeof(read_data_t)); /* Store SSL socket read buffer pointer in the activesock read buffer */ for (i=0; iparam.async_cnt; ++i) { read_data_t **p_ssock_rbuf = OFFSET_OF_READ_DATA_PTR(ssock, ssock->asock_rbuf[i]); ssock->ssock_rbuf[i].data = readbuf[i]; ssock->ssock_rbuf[i].len = 0; *p_ssock_rbuf = &ssock->ssock_rbuf[i]; } ssock->read_size = buff_size; ssock->read_started = PJ_TRUE; ssock->read_flags = flags; return PJ_SUCCESS; } /* * Same as pj_ssl_sock_start_read(), except that this function is used * only for datagram sockets, and it will trigger \a on_data_recvfrom() * callback instead. */ PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock, pj_pool_t *pool, unsigned buff_size, pj_uint32_t flags) { PJ_UNUSED_ARG(ssock); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(buff_size); PJ_UNUSED_ARG(flags); return PJ_ENOTSUP; } /* * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() * operation takes the buffer from the argument rather than creating * new ones. */ PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock, pj_pool_t *pool, unsigned buff_size, void *readbuf[], pj_uint32_t flags) { PJ_UNUSED_ARG(ssock); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(buff_size); PJ_UNUSED_ARG(readbuf); PJ_UNUSED_ARG(flags); return PJ_ENOTSUP; } /* Write plain data to SSL and flush write BIO. */ static pj_status_t ssl_write(pj_ssl_sock_t *ssock, pj_ioqueue_op_key_t *send_key, const void *data, pj_ssize_t size, unsigned flags) { pj_status_t status; int nwritten; /* Write the plain data to SSL, after SSL encrypts it, write BIO will * contain the secured data to be sent via socket. Note that re- * negotitation may be on progress, so sending data should be delayed * until re-negotiation is completed. */ pj_lock_acquire(ssock->write_mutex); nwritten = SSL_write(ssock->ossl_ssl, data, (int)size); pj_lock_release(ssock->write_mutex); if (nwritten == size) { /* All data written, flush write BIO to network socket */ status = flush_write_bio(ssock, send_key, size, flags); } else if (nwritten <= 0) { /* SSL failed to process the data, it may just that re-negotiation * is on progress. */ int err; err = SSL_get_error(ssock->ossl_ssl, nwritten); if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_NONE) { /* Re-negotiation is on progress, flush re-negotiation data */ status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0); if (status == PJ_SUCCESS || status == PJ_EPENDING) /* Just return PJ_EBUSY when re-negotiation is on progress */ status = PJ_EBUSY; } else { /* Some problem occured */ status = STATUS_FROM_SSL_ERR(ssock, err); } } else { /* nwritten < *size, shouldn't happen, unless write BIO cannot hold * the whole secured data, perhaps because of insufficient memory. */ status = PJ_ENOMEM; } return status; } /* Flush delayed data sending in the write pending list. */ static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock) { /* Check for another ongoing flush */ if (ssock->flushing_write_pend) return PJ_EBUSY; pj_lock_acquire(ssock->write_mutex); /* Again, check for another ongoing flush */ if (ssock->flushing_write_pend) { pj_lock_release(ssock->write_mutex); return PJ_EBUSY; } /* Set ongoing flush flag */ ssock->flushing_write_pend = PJ_TRUE; while (!pj_list_empty(&ssock->write_pending)) { write_data_t *wp; pj_status_t status; wp = ssock->write_pending.next; /* Ticket #1573: Don't hold mutex while calling socket send. */ pj_lock_release(ssock->write_mutex); status = ssl_write(ssock, &wp->key, wp->data.ptr, wp->plain_data_len, wp->flags); if (status != PJ_SUCCESS) { /* Reset ongoing flush flag first. */ ssock->flushing_write_pend = PJ_FALSE; return status; } pj_lock_acquire(ssock->write_mutex); pj_list_erase(wp); pj_list_push_back(&ssock->write_pending_empty, wp); } /* Reset ongoing flush flag */ ssock->flushing_write_pend = PJ_FALSE; pj_lock_release(ssock->write_mutex); return PJ_SUCCESS; } /* Sending is delayed, push back the sending data into pending list. */ static pj_status_t delay_send (pj_ssl_sock_t *ssock, pj_ioqueue_op_key_t *send_key, const void *data, pj_ssize_t size, unsigned flags) { write_data_t *wp; pj_lock_acquire(ssock->write_mutex); /* Init write pending instance */ if (!pj_list_empty(&ssock->write_pending_empty)) { wp = ssock->write_pending_empty.next; pj_list_erase(wp); } else { wp = PJ_POOL_ZALLOC_T(ssock->pool, write_data_t); } wp->app_key = send_key; wp->plain_data_len = size; wp->data.ptr = data; wp->flags = flags; pj_list_push_back(&ssock->write_pending, wp); pj_lock_release(ssock->write_mutex); /* Must return PJ_EPENDING */ return PJ_EPENDING; } /** * Send data using the socket. */ PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock, pj_ioqueue_op_key_t *send_key, const void *data, pj_ssize_t *size, unsigned flags) { pj_status_t status; PJ_ASSERT_RETURN(ssock && data && size && (*size>0), PJ_EINVAL); pj_lock_acquire(ssock->state_mutex); if (ssock->ssl_state != SSL_STATE_ESTABLISHED) { status = PJ_EINVALIDOP; goto on_return; } // Ticket #1573: Don't hold mutex while calling PJLIB socket send(). //pj_lock_acquire(ssock->write_mutex); /* Flush delayed send first. Sending data might be delayed when * re-negotiation is on-progress. */ status = flush_delayed_send(ssock); if (status == PJ_EBUSY) { /* Re-negotiation or flushing is on progress, delay sending */ status = delay_send(ssock, send_key, data, *size, flags); goto on_return; } else if (status != PJ_SUCCESS) { goto on_return; } /* Write data to SSL */ status = ssl_write(ssock, send_key, data, *size, flags); if (status == PJ_EBUSY) { /* Re-negotiation is on progress, delay sending */ status = delay_send(ssock, send_key, data, *size, flags); } on_return: //pj_lock_release(ssock->write_mutex); pj_lock_release(ssock->state_mutex); return status; } /** * Send datagram using the socket. */ PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock, pj_ioqueue_op_key_t *send_key, const void *data, pj_ssize_t *size, unsigned flags, const pj_sockaddr_t *addr, int addr_len) { PJ_UNUSED_ARG(ssock); PJ_UNUSED_ARG(send_key); PJ_UNUSED_ARG(data); PJ_UNUSED_ARG(size); PJ_UNUSED_ARG(flags); PJ_UNUSED_ARG(addr); PJ_UNUSED_ARG(addr_len); return PJ_ENOTSUP; } /** * Starts asynchronous socket accept() operations on this secure socket. */ PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock, pj_pool_t *pool, const pj_sockaddr_t *localaddr, int addr_len) { return pj_ssl_sock_start_accept2(ssock, pool, localaddr, addr_len, &ssock->param); } /** * Same as #pj_ssl_sock_start_accept(), but application provides parameter * for new accepted secure sockets. */ PJ_DEF(pj_status_t) pj_ssl_sock_start_accept2(pj_ssl_sock_t *ssock, pj_pool_t *pool, const pj_sockaddr_t *localaddr, int addr_len, const pj_ssl_sock_param *newsock_param) { pj_activesock_cb asock_cb; pj_activesock_cfg asock_cfg; pj_status_t status; PJ_ASSERT_RETURN(ssock && pool && localaddr && addr_len, PJ_EINVAL); /* Verify new socket parameters */ if (newsock_param->grp_lock != ssock->param.grp_lock || newsock_param->sock_af != ssock->param.sock_af || newsock_param->sock_type != ssock->param.sock_type) { return PJ_EINVAL; } /* Create socket */ status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, &ssock->sock); if (status != PJ_SUCCESS) goto on_error; /* Apply SO_REUSEADDR */ if (ssock->param.reuse_addr) { int enabled = 1; status = pj_sock_setsockopt(ssock->sock, pj_SOL_SOCKET(), pj_SO_REUSEADDR(), &enabled, sizeof(enabled)); if (status != PJ_SUCCESS) { PJ_PERROR(4,(ssock->pool->obj_name, status, "Warning: error applying SO_REUSEADDR")); } } /* Apply QoS, if specified */ status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type, &ssock->param.qos_params, 2, ssock->pool->obj_name, NULL); if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error) goto on_error; /* Apply socket options, if specified */ if (ssock->param.sockopt_params.cnt) { status = pj_sock_setsockopt_params(ssock->sock, &ssock->param.sockopt_params); if (status != PJ_SUCCESS && !ssock->param.sockopt_ignore_error) goto on_error; } /* Bind socket */ status = pj_sock_bind(ssock->sock, localaddr, addr_len); if (status != PJ_SUCCESS) goto on_error; /* Start listening to the address */ status = pj_sock_listen(ssock->sock, PJ_SOMAXCONN); if (status != PJ_SUCCESS) goto on_error; /* Create active socket */ pj_activesock_cfg_default(&asock_cfg); asock_cfg.async_cnt = ssock->param.async_cnt; asock_cfg.concurrency = ssock->param.concurrency; asock_cfg.whole_data = PJ_TRUE; asock_cfg.grp_lock = ssock->param.grp_lock; pj_bzero(&asock_cb, sizeof(asock_cb)); asock_cb.on_accept_complete = asock_on_accept_complete; status = pj_activesock_create(pool, ssock->sock, ssock->param.sock_type, &asock_cfg, ssock->param.ioqueue, &asock_cb, ssock, &ssock->asock); if (status != PJ_SUCCESS) goto on_error; /* Start accepting */ pj_ssl_sock_param_copy(pool, &ssock->newsock_param, newsock_param); status = pj_activesock_start_accept(ssock->asock, pool); if (status != PJ_SUCCESS) goto on_error; /* Update local address */ ssock->addr_len = addr_len; status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, &ssock->addr_len); if (status != PJ_SUCCESS) pj_sockaddr_cp(&ssock->local_addr, localaddr); ssock->is_server = PJ_TRUE; return PJ_SUCCESS; on_error: reset_ssl_sock_state(ssock); return status; } /** * Starts asynchronous socket connect() operation. */ PJ_DEF(pj_status_t) pj_ssl_sock_start_connect( pj_ssl_sock_t *ssock, pj_pool_t *pool, const pj_sockaddr_t *localaddr, const pj_sockaddr_t *remaddr, int addr_len) { pj_activesock_cb asock_cb; pj_activesock_cfg asock_cfg; pj_status_t status; PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len, PJ_EINVAL); /* Create socket */ status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, &ssock->sock); if (status != PJ_SUCCESS) goto on_error; /* Apply QoS, if specified */ status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type, &ssock->param.qos_params, 2, ssock->pool->obj_name, NULL); if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error) goto on_error; /* Apply socket options, if specified */ if (ssock->param.sockopt_params.cnt) { status = pj_sock_setsockopt_params(ssock->sock, &ssock->param.sockopt_params); if (status != PJ_SUCCESS && !ssock->param.sockopt_ignore_error) goto on_error; } /* Bind socket */ status = pj_sock_bind(ssock->sock, localaddr, addr_len); if (status != PJ_SUCCESS) goto on_error; /* Create active socket */ pj_activesock_cfg_default(&asock_cfg); asock_cfg.async_cnt = ssock->param.async_cnt; asock_cfg.concurrency = ssock->param.concurrency; asock_cfg.whole_data = PJ_TRUE; asock_cfg.grp_lock = ssock->param.grp_lock; pj_bzero(&asock_cb, sizeof(asock_cb)); asock_cb.on_connect_complete = asock_on_connect_complete; asock_cb.on_data_read = asock_on_data_read; asock_cb.on_data_sent = asock_on_data_sent; status = pj_activesock_create(pool, ssock->sock, ssock->param.sock_type, &asock_cfg, ssock->param.ioqueue, &asock_cb, ssock, &ssock->asock); if (status != PJ_SUCCESS) goto on_error; /* Save remote address */ pj_sockaddr_cp(&ssock->rem_addr, remaddr); /* Start timer */ if (ssock->param.timer_heap && (ssock->param.timeout.sec != 0 || ssock->param.timeout.msec != 0)) { pj_assert(ssock->timer.id == TIMER_NONE); ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT; status = pj_timer_heap_schedule(ssock->param.timer_heap, &ssock->timer, &ssock->param.timeout); if (status != PJ_SUCCESS) ssock->timer.id = TIMER_NONE; } status = pj_activesock_start_connect(ssock->asock, pool, remaddr, addr_len); if (status == PJ_SUCCESS) asock_on_connect_complete(ssock->asock, PJ_SUCCESS); else if (status != PJ_EPENDING) goto on_error; /* Update local address */ ssock->addr_len = addr_len; status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, &ssock->addr_len); /* Note that we may not get an IP address here. This can * happen for example on Windows, where getsockname() * would return 0.0.0.0 if socket has just started the * async connect. In this case, just leave the local * address with 0.0.0.0 for now; it will be updated * once the socket is established. */ /* Update SSL state */ ssock->is_server = PJ_FALSE; return PJ_EPENDING; on_error: reset_ssl_sock_state(ssock); return status; } PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock) { int ret; pj_status_t status; PJ_ASSERT_RETURN(ssock, PJ_EINVAL); if (ssock->ssl_state != SSL_STATE_ESTABLISHED) return PJ_EINVALIDOP; if (SSL_renegotiate_pending(ssock->ossl_ssl)) return PJ_EPENDING; ret = SSL_renegotiate(ssock->ossl_ssl); if (ret <= 0) { status = GET_SSL_STATUS(ssock); } else { status = do_handshake(ssock); } return status; } /* Put back deprecation warning setting */ #if defined(PJ_DARWINOS) && PJ_DARWINOS==1 # pragma GCC diagnostic pop #endif #endif /* PJ_HAS_SSL_SOCK */ ================================================ FILE: deps/pjsip/pjlib/src/pj/ssl_sock_symbian.cpp ================================================ /* $Id: ssl_sock_symbian.cpp 3999 2012-03-30 07:10:13Z bennylp $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include "os_symbian.h" #include #include #include #define THIS_FILE "ssl_sock_symbian.cpp" /* Cipher name structure */ typedef struct cipher_name_t { pj_ssl_cipher cipher; const char *name; } cipher_name_t; /* Cipher name constants */ static cipher_name_t cipher_names[] = { {PJ_TLS_UNKNOWN_CIPHER, "UNKNOWN"}, {PJ_TLS_NULL_WITH_NULL_NULL, "NULL"}, /* TLS/SSLv3 */ {PJ_TLS_RSA_WITH_NULL_MD5, "TLS_RSA_WITH_NULL_MD5"}, {PJ_TLS_RSA_WITH_NULL_SHA, "TLS_RSA_WITH_NULL_SHA"}, {PJ_TLS_RSA_WITH_NULL_SHA256, "TLS_RSA_WITH_NULL_SHA256"}, {PJ_TLS_RSA_WITH_RC4_128_MD5, "TLS_RSA_WITH_RC4_128_MD5"}, {PJ_TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA"}, {PJ_TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"}, {PJ_TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA"}, {PJ_TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA"}, {PJ_TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256"}, {PJ_TLS_RSA_WITH_AES_256_CBC_SHA256, "TLS_RSA_WITH_AES_256_CBC_SHA256"}, {PJ_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"}, {PJ_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"}, {PJ_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"}, {PJ_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"}, {PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA, "TLS_DH_DSS_WITH_AES_128_CBC_SHA"}, {PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA, "TLS_DH_RSA_WITH_AES_128_CBC_SHA"}, {PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"}, {PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"}, {PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA, "TLS_DH_DSS_WITH_AES_256_CBC_SHA"}, {PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA, "TLS_DH_RSA_WITH_AES_256_CBC_SHA"}, {PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"}, {PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"}, {PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA256, "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"}, {PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA256, "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"}, {PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"}, {PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"}, {PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA256, "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"}, {PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA256, "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"}, {PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"}, {PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"}, {PJ_TLS_DH_anon_WITH_RC4_128_MD5, "TLS_DH_anon_WITH_RC4_128_MD5"}, {PJ_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"}, {PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA, "TLS_DH_anon_WITH_AES_128_CBC_SHA"}, {PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA, "TLS_DH_anon_WITH_AES_256_CBC_SHA"}, {PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA256, "TLS_DH_anon_WITH_AES_128_CBC_SHA256"}, {PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA256, "TLS_DH_anon_WITH_AES_256_CBC_SHA256"}, /* TLS (deprecated) */ {PJ_TLS_RSA_EXPORT_WITH_RC4_40_MD5, "TLS_RSA_EXPORT_WITH_RC4_40_MD5"}, {PJ_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"}, {PJ_TLS_RSA_WITH_IDEA_CBC_SHA, "TLS_RSA_WITH_IDEA_CBC_SHA"}, {PJ_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"}, {PJ_TLS_RSA_WITH_DES_CBC_SHA, "TLS_RSA_WITH_DES_CBC_SHA"}, {PJ_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"}, {PJ_TLS_DH_DSS_WITH_DES_CBC_SHA, "TLS_DH_DSS_WITH_DES_CBC_SHA"}, {PJ_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"}, {PJ_TLS_DH_RSA_WITH_DES_CBC_SHA, "TLS_DH_RSA_WITH_DES_CBC_SHA"}, {PJ_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"}, {PJ_TLS_DHE_DSS_WITH_DES_CBC_SHA, "TLS_DHE_DSS_WITH_DES_CBC_SHA"}, {PJ_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"}, {PJ_TLS_DHE_RSA_WITH_DES_CBC_SHA, "TLS_DHE_RSA_WITH_DES_CBC_SHA"}, {PJ_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"}, {PJ_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"}, {PJ_TLS_DH_anon_WITH_DES_CBC_SHA, "TLS_DH_anon_WITH_DES_CBC_SHA"}, /* SSLv3 */ {PJ_SSL_FORTEZZA_KEA_WITH_NULL_SHA, "SSL_FORTEZZA_KEA_WITH_NULL_SHA"}, {PJ_SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA,"SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA"}, {PJ_SSL_FORTEZZA_KEA_WITH_RC4_128_SHA, "SSL_FORTEZZA_KEA_WITH_RC4_128_SHA"}, /* SSLv2 */ {PJ_SSL_CK_RC4_128_WITH_MD5, "SSL_CK_RC4_128_WITH_MD5"}, {PJ_SSL_CK_RC4_128_EXPORT40_WITH_MD5, "SSL_CK_RC4_128_EXPORT40_WITH_MD5"}, {PJ_SSL_CK_RC2_128_CBC_WITH_MD5, "SSL_CK_RC2_128_CBC_WITH_MD5"}, {PJ_SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5, "SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5"}, {PJ_SSL_CK_IDEA_128_CBC_WITH_MD5, "SSL_CK_IDEA_128_CBC_WITH_MD5"}, {PJ_SSL_CK_DES_64_CBC_WITH_MD5, "SSL_CK_DES_64_CBC_WITH_MD5"}, {PJ_SSL_CK_DES_192_EDE3_CBC_WITH_MD5, "SSL_CK_DES_192_EDE3_CBC_WITH_MD5"} }; /* Get cipher name string */ static const char* get_cipher_name(pj_ssl_cipher cipher) { unsigned i, n; n = PJ_ARRAY_SIZE(cipher_names); for (i = 0; i < n; ++i) { if (cipher == cipher_names[i].cipher) return cipher_names[i].name; } return "CIPHER_UNKNOWN"; } typedef void (*CPjSSLSocket_cb)(int err, void *key); class CPjSSLSocketReader : public CActive { public: static CPjSSLSocketReader *NewL(CSecureSocket &sock) { CPjSSLSocketReader *self = new (ELeave) CPjSSLSocketReader(sock); CleanupStack::PushL(self); self->ConstructL(); CleanupStack::Pop(self); return self; } ~CPjSSLSocketReader() { Cancel(); } /* Asynchronous read from the socket. */ int Read(CPjSSLSocket_cb cb, void *key, TPtr8 &data, TUint flags) { PJ_ASSERT_RETURN(!IsActive(), PJ_EBUSY); cb_ = cb; key_ = key; sock_.RecvOneOrMore(data, iStatus, len_received_); SetActive(); return PJ_EPENDING; } private: CSecureSocket &sock_; CPjSSLSocket_cb cb_; void *key_; TSockXfrLength len_received_; /* not really useful? */ void DoCancel() { sock_.CancelAll(); } void RunL() { (*cb_)(iStatus.Int(), key_); } CPjSSLSocketReader(CSecureSocket &sock) : CActive(0), sock_(sock), cb_(NULL), key_(NULL) {} void ConstructL() { CActiveScheduler::Add(this); } }; class CPjSSLSocket : public CActive { public: enum ssl_state { SSL_STATE_NULL, SSL_STATE_CONNECTING, SSL_STATE_HANDSHAKING, SSL_STATE_ESTABLISHED }; static CPjSSLSocket *NewL(const TDesC8 &ssl_proto, pj_qos_type qos_type, const pj_qos_params &qos_params) { CPjSSLSocket *self = new (ELeave) CPjSSLSocket(qos_type, qos_params); CleanupStack::PushL(self); self->ConstructL(ssl_proto); CleanupStack::Pop(self); return self; } ~CPjSSLSocket() { Cancel(); CleanupSubObjects(); } int Connect(CPjSSLSocket_cb cb, void *key, const TInetAddr &local_addr, const TInetAddr &rem_addr, const TDesC8 &servername = TPtrC8(NULL,0), const TDesC8 &ciphers = TPtrC8(NULL,0)); int Send(CPjSSLSocket_cb cb, void *key, const TDesC8 &aDesc, TUint flags); int SendSync(const TDesC8 &aDesc, TUint flags); CPjSSLSocketReader* GetReader(); enum ssl_state GetState() const { return state_; } const TInetAddr* GetLocalAddr() const { return &local_addr_; } int GetCipher(TDes8 &cipher) const { if (securesock_) return securesock_->CurrentCipherSuite(cipher); return KErrNotFound; } const CX509Certificate *GetPeerCert() { if (securesock_) return securesock_->ServerCert(); return NULL; } private: enum ssl_state state_; pj_sock_t sock_; CSecureSocket *securesock_; bool is_connected_; pj_qos_type qos_type_; pj_qos_params qos_params_; CPjSSLSocketReader *reader_; TBuf<32> ssl_proto_; TInetAddr rem_addr_; TPtrC8 servername_; TPtrC8 ciphers_; TInetAddr local_addr_; TSockXfrLength sent_len_; CPjSSLSocket_cb cb_; void *key_; void DoCancel(); void RunL(); CPjSSLSocket(pj_qos_type qos_type, const pj_qos_params &qos_params) : CActive(0), state_(SSL_STATE_NULL), sock_(PJ_INVALID_SOCKET), securesock_(NULL), is_connected_(false), qos_type_(qos_type), qos_params_(qos_params), reader_(NULL), cb_(NULL), key_(NULL) {} void ConstructL(const TDesC8 &ssl_proto) { ssl_proto_.Copy(ssl_proto); CActiveScheduler::Add(this); } void CleanupSubObjects() { delete reader_; reader_ = NULL; if (securesock_) { if (state_ == SSL_STATE_ESTABLISHED) securesock_->Close(); delete securesock_; securesock_ = NULL; } if (sock_ != PJ_INVALID_SOCKET) { pj_sock_close(sock_); sock_ = PJ_INVALID_SOCKET; } } }; int CPjSSLSocket::Connect(CPjSSLSocket_cb cb, void *key, const TInetAddr &local_addr, const TInetAddr &rem_addr, const TDesC8 &servername, const TDesC8 &ciphers) { pj_status_t status; PJ_ASSERT_RETURN(state_ == SSL_STATE_NULL, PJ_EINVALIDOP); status = pj_sock_socket(rem_addr.Family(), pj_SOCK_STREAM(), 0, &sock_); if (status != PJ_SUCCESS) return status; // Apply QoS status = pj_sock_apply_qos2(sock_, qos_type_, &qos_params_, 2, THIS_FILE, NULL); RSocket &rSock = ((CPjSocket*)sock_)->Socket(); local_addr_ = local_addr; if (!local_addr_.IsUnspecified()) { TInt err = rSock.Bind(local_addr_); if (err != KErrNone) return PJ_RETURN_OS_ERROR(err); } cb_ = cb; key_ = key; rem_addr_ = rem_addr; /* Note: the following members only keep the pointer, not the data */ servername_.Set(servername); ciphers_.Set(ciphers); rSock.Connect(rem_addr_, iStatus); SetActive(); state_ = SSL_STATE_CONNECTING; rSock.LocalName(local_addr_); return PJ_EPENDING; } int CPjSSLSocket::Send(CPjSSLSocket_cb cb, void *key, const TDesC8 &aDesc, TUint flags) { PJ_UNUSED_ARG(flags); PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, PJ_EINVALIDOP); if (IsActive()) return PJ_EBUSY; cb_ = cb; key_ = key; securesock_->Send(aDesc, iStatus, sent_len_); SetActive(); return PJ_EPENDING; } int CPjSSLSocket::SendSync(const TDesC8 &aDesc, TUint flags) { PJ_UNUSED_ARG(flags); PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, PJ_EINVALIDOP); TRequestStatus reqStatus; securesock_->Send(aDesc, reqStatus, sent_len_); User::WaitForRequest(reqStatus); return PJ_RETURN_OS_ERROR(reqStatus.Int()); } CPjSSLSocketReader* CPjSSLSocket::GetReader() { PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, NULL); if (reader_) return reader_; TRAPD(err, reader_ = CPjSSLSocketReader::NewL(*securesock_)); if (err != KErrNone) return NULL; return reader_; } void CPjSSLSocket::DoCancel() { /* Operation to be cancelled depends on current state */ switch (state_) { case SSL_STATE_CONNECTING: { RSocket &rSock = ((CPjSocket*)sock_)->Socket(); rSock.CancelConnect(); CleanupSubObjects(); state_ = SSL_STATE_NULL; } break; case SSL_STATE_HANDSHAKING: { securesock_->CancelHandshake(); CleanupSubObjects(); state_ = SSL_STATE_NULL; } break; case SSL_STATE_ESTABLISHED: securesock_->CancelSend(); break; default: break; } } void CPjSSLSocket::RunL() { switch (state_) { case SSL_STATE_CONNECTING: if (iStatus != KErrNone) { CleanupSubObjects(); state_ = SSL_STATE_NULL; /* Dispatch connect failure notification */ if (cb_) (*cb_)(iStatus.Int(), key_); } else { RSocket &rSock = ((CPjSocket*)sock_)->Socket(); /* Get local addr */ rSock.LocalName(local_addr_); /* Prepare and start handshake */ securesock_ = CSecureSocket::NewL(rSock, ssl_proto_); securesock_->SetDialogMode(EDialogModeAttended); if (servername_.Length() > 0) securesock_->SetOpt(KSoSSLDomainName, KSolInetSSL, servername_); if (ciphers_.Length() > 0) securesock_->SetAvailableCipherSuites(ciphers_); // FlushSessionCache() seems to also fire signals to all // completed AOs (something like CActiveScheduler::RunIfReady()) // which may cause problem, e.g: we've experienced that when // SSL timeout is set to 1s, the SSL timeout timer fires up // at this point and securesock_ instance gets deleted here! // So be careful using this. And we don't think we need it here. //securesock_->FlushSessionCache(); securesock_->StartClientHandshake(iStatus); SetActive(); state_ = SSL_STATE_HANDSHAKING; } break; case SSL_STATE_HANDSHAKING: if (iStatus == KErrNone) { state_ = SSL_STATE_ESTABLISHED; } else { state_ = SSL_STATE_NULL; CleanupSubObjects(); } /* Dispatch connect status notification */ if (cb_) (*cb_)(iStatus.Int(), key_); break; case SSL_STATE_ESTABLISHED: /* Dispatch data sent notification */ if (cb_) (*cb_)(iStatus.Int(), key_); break; default: pj_assert(0); break; } } typedef void (*CPjTimer_cb)(void *user_data); class CPjTimer : public CActive { public: CPjTimer(const pj_time_val *delay, CPjTimer_cb cb, void *user_data) : CActive(0), cb_(cb), user_data_(user_data) { CActiveScheduler::Add(this); rtimer_.CreateLocal(); pj_int32_t interval = PJ_TIME_VAL_MSEC(*delay) * 1000; if (interval < 0) { interval = 0; } rtimer_.After(iStatus, interval); SetActive(); } ~CPjTimer() { Cancel(); } private: RTimer rtimer_; CPjTimer_cb cb_; void *user_data_; void RunL() { if (cb_) (*cb_)(user_data_); } void DoCancel() { rtimer_.Cancel(); } }; /* * Structure of recv/read state. */ typedef struct read_state_t { TPtr8 *read_buf; TPtr8 *orig_buf; pj_uint32_t flags; } read_state_t; /* * Structure of send/write data. */ typedef struct write_data_t { pj_size_t len; pj_ioqueue_op_key_t *key; pj_size_t data_len; char data[1]; } write_data_t; /* * Structure of send/write state. */ typedef struct write_state_t { char *buf; pj_size_t max_len; char *start; pj_size_t len; write_data_t *current_data; TPtrC8 send_ptr; } write_state_t; /* * Secure socket structure definition. */ struct pj_ssl_sock_t { pj_pool_t *pool; pj_ssl_sock_cb cb; void *user_data; pj_bool_t established; write_state_t write_state; read_state_t read_state; CPjTimer *connect_timer; CPjSSLSocket *sock; int sock_af; int sock_type; pj_sockaddr local_addr; pj_sockaddr rem_addr; /* QoS settings */ pj_qos_type qos_type; pj_qos_params qos_params; pj_bool_t qos_ignore_error; pj_ssl_sock_proto proto; pj_time_val timeout; pj_str_t servername; pj_str_t ciphers; pj_ssl_cert_info remote_cert_info; }; static pj_str_t get_cert_name(char *buf, unsigned buf_len, const CX500DistinguishedName &name) { TInt i; TUint8 *p; TInt l = buf_len; p = (TUint8*)buf; for(i = 0; i < name.Count(); ++i) { const CX520AttributeTypeAndValue &attr = name.Element(i); /* Print element separator */ *p++ = '/'; if (0 == --l) break; /* Print the type. */ TPtr8 type(p, l); type.Copy(attr.Type()); p += type.Length(); l -= type.Length(); if (0 >= --l) break; /* Print equal sign */ *p++ = '='; if (0 == --l) break; /* Print the value. Let's just get the raw data here */ TPtr8 value(p, l); value.Copy(attr.EncodedValue().Mid(2)); p += value.Length(); l -= value.Length(); if (0 >= --l) break; } pj_str_t src; pj_strset(&src, buf, buf_len - l); return src; } /* Get certificate info from CX509Certificate. */ static void get_cert_info(pj_pool_t *pool, pj_ssl_cert_info *ci, const CX509Certificate *x) { enum { tmp_buf_len = 512 }; char *tmp_buf; unsigned len; pj_assert(pool && ci && x); /* Init */ tmp_buf = new char[tmp_buf_len]; pj_bzero(ci, sizeof(*ci)); /* Version */ ci->version = x->Version(); /* Serial number */ len = x->SerialNumber().Length(); if (len > sizeof(ci->serial_no)) len = sizeof(ci->serial_no); pj_memcpy(ci->serial_no + sizeof(ci->serial_no) - len, x->SerialNumber().Ptr(), len); /* Subject */ { HBufC *subject = NULL; TRAPD(err, subject = x->SubjectL()); if (err == KErrNone) { TPtr16 ptr16(subject->Des()); len = ptr16.Length(); TPtr8 ptr8((TUint8*)pj_pool_alloc(pool, len), len); ptr8.Copy(ptr16); pj_strset(&ci->subject.cn, (char*)ptr8.Ptr(), ptr8.Length()); } pj_str_t tmp = get_cert_name(tmp_buf, tmp_buf_len, x->SubjectName()); pj_strdup(pool, &ci->subject.info, &tmp); } /* Issuer */ { HBufC *issuer = NULL; TRAPD(err, issuer = x->IssuerL()); if (err == KErrNone) { TPtr16 ptr16(issuer->Des()); len = ptr16.Length(); TPtr8 ptr8((TUint8*)pj_pool_alloc(pool, len), len); ptr8.Copy(ptr16); pj_strset(&ci->issuer.cn, (char*)ptr8.Ptr(), ptr8.Length()); } pj_str_t tmp = get_cert_name(tmp_buf, tmp_buf_len, x->IssuerName()); pj_strdup(pool, &ci->issuer.info, &tmp); } /* Validity */ const CValidityPeriod &valid_period = x->ValidityPeriod(); TTime base_time(TDateTime(1970, EJanuary, 0, 0, 0, 0, 0)); TTimeIntervalSeconds tmp_sec; valid_period.Start().SecondsFrom(base_time, tmp_sec); ci->validity.start.sec = tmp_sec.Int(); valid_period.Finish().SecondsFrom(base_time, tmp_sec); ci->validity.end.sec = tmp_sec.Int(); /* Deinit */ delete [] tmp_buf; } /* Update certificates info. This function should be called after handshake * or renegotiation successfully completed. */ static void update_certs_info(pj_ssl_sock_t *ssock) { const CX509Certificate *x; pj_assert(ssock && ssock->sock && ssock->sock->GetState() == CPjSSLSocket::SSL_STATE_ESTABLISHED); /* Active remote certificate */ x = ssock->sock->GetPeerCert(); if (x) { get_cert_info(ssock->pool, &ssock->remote_cert_info, x); } else { pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info)); } } /* Available ciphers */ static unsigned ciphers_num_ = 0; static struct ciphers_t { pj_ssl_cipher id; const char *name; } ciphers_[64]; /* * Get cipher list supported by SSL/TLS backend. */ PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables (pj_ssl_cipher ciphers[], unsigned *cipher_num) { unsigned i; PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL); if (ciphers_num_ == 0) { RSocket sock; CSecureSocket *secure_sock; TPtrC16 proto(_L16("TLS1.0")); secure_sock = CSecureSocket::NewL(sock, proto); if (secure_sock) { TBuf8<128> ciphers_buf(0); secure_sock->AvailableCipherSuites(ciphers_buf); ciphers_num_ = ciphers_buf.Length() / 2; if (ciphers_num_ > PJ_ARRAY_SIZE(ciphers_)) ciphers_num_ = PJ_ARRAY_SIZE(ciphers_); for (i = 0; i < ciphers_num_; ++i) { ciphers_[i].id = (pj_ssl_cipher)(ciphers_buf[i*2]*10 + ciphers_buf[i*2+1]); ciphers_[i].name = get_cipher_name(ciphers_[i].id); } } delete secure_sock; } if (ciphers_num_ == 0) { *cipher_num = 0; return PJ_ENOTFOUND; } *cipher_num = PJ_MIN(*cipher_num, ciphers_num_); for (i = 0; i < *cipher_num; ++i) ciphers[i] = ciphers_[i].id; return PJ_SUCCESS; } /* Get cipher name string */ PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher) { unsigned i; if (ciphers_num_ == 0) { pj_ssl_cipher c[1]; i = 0; pj_ssl_cipher_get_availables(c, &i); } for (i = 0; i < ciphers_num_; ++i) { if (cipher == ciphers_[i].id) return ciphers_[i].name; } return NULL; } /* Get cipher identifier */ PJ_DEF(pj_ssl_cipher) pj_ssl_cipher_id(const char *cipher_name) { unsigned i; if (ciphers_num_ == 0) { pj_ssl_cipher c[1]; i = 0; pj_ssl_cipher_get_availables(c, &i); } for (i = 0; i < ciphers_num_; ++i) { if (!pj_ansi_stricmp(ciphers_[i].name, cipher_name)) return ciphers_[i].id; } return PJ_TLS_UNKNOWN_CIPHER; } /* Check if the specified cipher is supported by SSL/TLS backend. */ PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher) { unsigned i; if (ciphers_num_ == 0) { pj_ssl_cipher c[1]; i = 0; pj_ssl_cipher_get_availables(c, &i); } for (i = 0; i < ciphers_num_; ++i) { if (cipher == ciphers_[i].id) return PJ_TRUE; } return PJ_FALSE; } /* * Create SSL socket instance. */ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool, const pj_ssl_sock_param *param, pj_ssl_sock_t **p_ssock) { pj_ssl_sock_t *ssock; PJ_ASSERT_RETURN(param->async_cnt == 1, PJ_EINVAL); PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL); /* Allocate secure socket */ ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t); /* Allocate write buffer */ ssock->write_state.buf = (char*)pj_pool_alloc(pool, param->send_buffer_size); ssock->write_state.max_len = param->send_buffer_size; ssock->write_state.start = ssock->write_state.buf; /* Init secure socket */ ssock->pool = pool; ssock->sock_af = param->sock_af; ssock->sock_type = param->sock_type; ssock->cb = param->cb; ssock->user_data = param->user_data; ssock->timeout = param->timeout; if (param->ciphers_num > 0) { /* Cipher list in Symbian is represented as array of two-octets. */ ssock->ciphers.slen = param->ciphers_num*2; ssock->ciphers.ptr = (char*)pj_pool_alloc(pool, ssock->ciphers.slen); pj_uint8_t *c = (pj_uint8_t*)ssock->ciphers.ptr; for (unsigned i = 0; i < param->ciphers_num; ++i) { *c++ = (pj_uint8_t)(param->ciphers[i] & 0xFF00) >> 8; *c++ = (pj_uint8_t)(param->ciphers[i] & 0xFF); } } pj_strdup_with_null(pool, &ssock->servername, ¶m->server_name); ssock->qos_type = param->qos_type; ssock->qos_ignore_error = param->qos_ignore_error; pj_memcpy(&ssock->qos_params, ¶m->qos_params, sizeof(param->qos_params)); /* Finally */ *p_ssock = ssock; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool, const pj_str_t *CA_file, const pj_str_t *cert_file, const pj_str_t *privkey_file, const pj_str_t *privkey_pass, pj_ssl_cert_t **p_cert) { return pj_ssl_cert_load_from_files2(pool, CA_file, NULL, cert_file, privkey_file, privkey_pass, p_cert); } PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files2(pj_pool_t *pool, const pj_str_t *CA_file, const pj_str_t *CA_path, const pj_str_t *cert_file, const pj_str_t *privkey_file, const pj_str_t *privkey_pass, pj_ssl_cert_t **p_cert) { PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(CA_file); PJ_UNUSED_ARG(CA_path); PJ_UNUSED_ARG(cert_file); PJ_UNUSED_ARG(privkey_file); PJ_UNUSED_ARG(privkey_pass); PJ_UNUSED_ARG(p_cert); return PJ_ENOTSUP; } /* * Set SSL socket credential. */ PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate( pj_ssl_sock_t *ssock, pj_pool_t *pool, const pj_ssl_cert_t *cert) { PJ_UNUSED_ARG(ssock); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(cert); return PJ_ENOTSUP; } /* * Close the SSL socket. */ PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock) { PJ_ASSERT_RETURN(ssock, PJ_EINVAL); delete ssock->connect_timer; ssock->connect_timer = NULL; delete ssock->sock; ssock->sock = NULL; delete ssock->read_state.read_buf; delete ssock->read_state.orig_buf; ssock->read_state.read_buf = NULL; ssock->read_state.orig_buf = NULL; return PJ_SUCCESS; } /* * Associate arbitrary data with the SSL socket. */ PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data (pj_ssl_sock_t *ssock, void *user_data) { PJ_ASSERT_RETURN(ssock, PJ_EINVAL); ssock->user_data = user_data; return PJ_SUCCESS; } /* * Retrieve the user data previously associated with this SSL * socket. */ PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock) { PJ_ASSERT_RETURN(ssock, NULL); return ssock->user_data; } /* * Retrieve the local address and port used by specified SSL socket. */ PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock, pj_ssl_sock_info *info) { PJ_ASSERT_RETURN(ssock && info, PJ_EINVAL); pj_bzero(info, sizeof(*info)); info->established = ssock->established; /* Local address */ if (ssock->sock) { const TInetAddr* local_addr_ = ssock->sock->GetLocalAddr(); int addrlen = sizeof(pj_sockaddr); pj_status_t status; status = PjSymbianOS::Addr2pj(*local_addr_, info->local_addr, &addrlen); if (status != PJ_SUCCESS) return status; } else { pj_sockaddr_cp(&info->local_addr, &ssock->local_addr); } if (info->established) { /* Cipher suite */ TBuf8<4> cipher; if (ssock->sock->GetCipher(cipher) == KErrNone) { info->cipher = (pj_ssl_cipher)cipher[1]; } /* Remote address */ pj_sockaddr_cp((pj_sockaddr_t*)&info->remote_addr, (pj_sockaddr_t*)&ssock->rem_addr); /* Certificates info */ info->remote_cert_info = &ssock->remote_cert_info; } /* Protocol */ info->proto = ssock->proto; return PJ_SUCCESS; } /* * Starts read operation on this SSL socket. */ PJ_DEF(pj_status_t) pj_ssl_sock_start_read (pj_ssl_sock_t *ssock, pj_pool_t *pool, unsigned buff_size, pj_uint32_t flags) { PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL); PJ_ASSERT_RETURN(ssock->established, PJ_EINVALIDOP); /* Reading is already started */ if (ssock->read_state.orig_buf) { return PJ_SUCCESS; } void *readbuf[1]; readbuf[0] = pj_pool_alloc(pool, buff_size); return pj_ssl_sock_start_read2(ssock, pool, buff_size, readbuf, flags); } static void read_cb(int err, void *key) { pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key; pj_status_t status; status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err); /* Check connection status */ if (err == KErrEof || !PjSymbianOS::Instance()->IsConnectionUp() || !ssock->established) { status = PJ_EEOF; } /* Notify data arrival */ if (ssock->cb.on_data_read) { pj_size_t remainder = 0; char *data = (char*)ssock->read_state.orig_buf->Ptr(); pj_size_t data_len = ssock->read_state.read_buf->Length() + ssock->read_state.read_buf->Ptr() - ssock->read_state.orig_buf->Ptr(); if (data_len > 0) { /* Notify received data */ pj_bool_t ret = (*ssock->cb.on_data_read)(ssock, data, data_len, status, &remainder); if (!ret) { /* We've been destroyed */ return; } /* Calculate available data for next READ operation */ if (remainder > 0) { pj_size_t data_maxlen = ssock->read_state.orig_buf->MaxLength(); /* There is some data left unconsumed by application, we give * smaller buffer for next READ operation. */ ssock->read_state.read_buf->Set((TUint8*)data+remainder, 0, data_maxlen - remainder); } else { /* Give all buffer for next READ operation. */ ssock->read_state.read_buf->Set(*ssock->read_state.orig_buf); } } } if (status == PJ_SUCCESS) { /* Perform the "next" READ operation */ CPjSSLSocketReader *reader = ssock->sock->GetReader(); ssock->read_state.read_buf->SetLength(0); status = reader->Read(&read_cb, ssock, *ssock->read_state.read_buf, ssock->read_state.flags); } /* Connection closed or something goes wrong */ if (status != PJ_SUCCESS && status != PJ_EPENDING) { /* Notify error */ if (ssock->cb.on_data_read) { pj_bool_t ret = (*ssock->cb.on_data_read)(ssock, NULL, 0, status, NULL); if (!ret) { /* We've been destroyed */ return; } } delete ssock->read_state.read_buf; delete ssock->read_state.orig_buf; ssock->read_state.read_buf = NULL; ssock->read_state.orig_buf = NULL; ssock->established = PJ_FALSE; } } /* * Same as #pj_ssl_sock_start_read(), except that the application * supplies the buffers for the read operation so that the acive socket * does not have to allocate the buffers. */ PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock, pj_pool_t *pool, unsigned buff_size, void *readbuf[], pj_uint32_t flags) { PJ_ASSERT_RETURN(ssock && buff_size && readbuf, PJ_EINVAL); PJ_ASSERT_RETURN(ssock->established, PJ_EINVALIDOP); /* Return failure if access point is marked as down by app. */ PJ_SYMBIAN_CHECK_CONNECTION(); /* Reading is already started */ if (ssock->read_state.orig_buf) { return PJ_SUCCESS; } PJ_UNUSED_ARG(pool); /* Get reader instance */ CPjSSLSocketReader *reader = ssock->sock->GetReader(); if (!reader) return PJ_ENOMEM; /* We manage two buffer pointers here: * 1. orig_buf keeps the orginal buffer address (and its max length). * 2. read_buf provides buffer for READ operation, mind that there may be * some remainder data left by application. */ ssock->read_state.read_buf = new TPtr8((TUint8*)readbuf[0], 0, buff_size); ssock->read_state.orig_buf = new TPtr8((TUint8*)readbuf[0], 0, buff_size); ssock->read_state.flags = flags; pj_status_t status; status = reader->Read(&read_cb, ssock, *ssock->read_state.read_buf, ssock->read_state.flags); if (status != PJ_SUCCESS && status != PJ_EPENDING) { delete ssock->read_state.read_buf; delete ssock->read_state.orig_buf; ssock->read_state.read_buf = NULL; ssock->read_state.orig_buf = NULL; return status; } return PJ_SUCCESS; } /* * Same as pj_ssl_sock_start_read(), except that this function is used * only for datagram sockets, and it will trigger \a on_data_recvfrom() * callback instead. */ PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock, pj_pool_t *pool, unsigned buff_size, pj_uint32_t flags) { PJ_UNUSED_ARG(ssock); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(buff_size); PJ_UNUSED_ARG(flags); return PJ_ENOTSUP; } /* * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() * operation takes the buffer from the argument rather than creating * new ones. */ PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock, pj_pool_t *pool, unsigned buff_size, void *readbuf[], pj_uint32_t flags) { PJ_UNUSED_ARG(ssock); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(buff_size); PJ_UNUSED_ARG(readbuf); PJ_UNUSED_ARG(flags); return PJ_ENOTSUP; } static void send_cb(int err, void *key) { pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key; write_state_t *st = &ssock->write_state; /* Check connection status */ if (err != KErrNone || !PjSymbianOS::Instance()->IsConnectionUp() || !ssock->established) { ssock->established = PJ_FALSE; return; } /* Remove sent data from buffer */ st->start += st->current_data->len; st->len -= st->current_data->len; /* Reset current outstanding send */ st->current_data = NULL; /* Let's check if there is pending data to send */ if (st->len) { write_data_t *wdata = (write_data_t*)st->start; pj_status_t status; st->send_ptr.Set((TUint8*)wdata->data, (TInt)wdata->data_len); st->current_data = wdata; status = ssock->sock->Send(&send_cb, ssock, st->send_ptr, 0); if (status != PJ_EPENDING) { ssock->established = PJ_FALSE; st->len = 0; return; } } else { /* Buffer empty, reset the start position */ st->start = st->buf; } } /* * Send data using the socket. */ PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock, pj_ioqueue_op_key_t *send_key, const void *data, pj_ssize_t *size, unsigned flags) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(ssock && data && size, PJ_EINVAL); PJ_ASSERT_RETURN(ssock->write_state.max_len == 0 || ssock->write_state.max_len >= (pj_size_t)*size, PJ_ETOOSMALL); /* Check connection status */ if (!PjSymbianOS::Instance()->IsConnectionUp() || !ssock->established) { ssock->established = PJ_FALSE; return PJ_ECANCELLED; } write_state_t *st = &ssock->write_state; /* Synchronous mode */ if (st->max_len == 0) { st->send_ptr.Set((TUint8*)data, (TInt)*size); return ssock->sock->SendSync(st->send_ptr, flags); } /* CSecureSocket only allows one outstanding send operation, so * we use buffering mechanism to allow application to perform send * operations at any time. */ pj_size_t needed_len = *size + sizeof(write_data_t) - 1; /* Align needed_len to be multiplication of 4 */ needed_len = ((needed_len + 3) >> 2) << 2; /* Block until there is buffer slot available and contiguous! */ while (st->start + st->len + needed_len > st->buf + st->max_len) { pj_symbianos_poll(-1, -1); } /* Push back the send data into the buffer */ write_data_t *wdata = (write_data_t*)(st->start + st->len); wdata->len = needed_len; wdata->key = send_key; wdata->data_len = (pj_size_t)*size; pj_memcpy(wdata->data, data, *size); st->len += needed_len; /* If no outstanding send, send it */ if (st->current_data == NULL) { pj_status_t status; wdata = (write_data_t*)st->start; st->current_data = wdata; st->send_ptr.Set((TUint8*)wdata->data, (TInt)wdata->data_len); status = ssock->sock->Send(&send_cb, ssock, st->send_ptr, flags); if (status != PJ_EPENDING) { *size = -status; return status; } } return PJ_SUCCESS; } /* * Send datagram using the socket. */ PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock, pj_ioqueue_op_key_t *send_key, const void *data, pj_ssize_t *size, unsigned flags, const pj_sockaddr_t *addr, int addr_len) { PJ_UNUSED_ARG(ssock); PJ_UNUSED_ARG(send_key); PJ_UNUSED_ARG(data); PJ_UNUSED_ARG(size); PJ_UNUSED_ARG(flags); PJ_UNUSED_ARG(addr); PJ_UNUSED_ARG(addr_len); return PJ_ENOTSUP; } /* * Starts asynchronous socket accept() operations on this SSL socket. */ PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock, pj_pool_t *pool, const pj_sockaddr_t *local_addr, int addr_len) { PJ_UNUSED_ARG(ssock); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(local_addr); PJ_UNUSED_ARG(addr_len); return PJ_ENOTSUP; } static void connect_cb(int err, void *key) { pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key; pj_status_t status; if (ssock->connect_timer) { delete ssock->connect_timer; ssock->connect_timer = NULL; } status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err); if (status == PJ_SUCCESS) { ssock->established = PJ_TRUE; update_certs_info(ssock); } else { delete ssock->sock; ssock->sock = NULL; if (err == KErrTimedOut) status = PJ_ETIMEDOUT; } if (ssock->cb.on_connect_complete) { pj_bool_t ret = (*ssock->cb.on_connect_complete)(ssock, status); if (!ret) { /* We've been destroyed */ return; } } } static void connect_timer_cb(void *key) { connect_cb(KErrTimedOut, key); } /* * Starts asynchronous socket connect() operation and SSL/TLS handshaking * for this socket. Once the connection is done (either successfully or not), * the \a on_connect_complete() callback will be called. */ PJ_DEF(pj_status_t) pj_ssl_sock_start_connect (pj_ssl_sock_t *ssock, pj_pool_t *pool, const pj_sockaddr_t *localaddr, const pj_sockaddr_t *remaddr, int addr_len) { CPjSSLSocket *sock = NULL; pj_status_t status; PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len, PJ_EINVAL); /* Check connection status */ PJ_SYMBIAN_CHECK_CONNECTION(); if (ssock->sock != NULL) { CPjSSLSocket::ssl_state state = ssock->sock->GetState(); switch (state) { case CPjSSLSocket::SSL_STATE_ESTABLISHED: return PJ_SUCCESS; default: return PJ_EPENDING; } } /* Set SSL protocol */ TPtrC8 proto; if (ssock->proto == PJ_SSL_SOCK_PROTO_DEFAULT) ssock->proto = PJ_SSL_SOCK_PROTO_TLS1; /* CSecureSocket only support TLS1.0 and SSL3.0 */ if (ssock->proto & PJ_SSL_SOCK_PROTO_TLS1==PJ_SSL_SOCK_PROTO_TLS1) { proto.Set((const TUint8*)"TLS1.0", 6); } else if (ssock->proto & PJ_SSL_SOCK_PROTO_SSL3==PJ_SSL_SOCK_PROTO_SSL3) { proto.Set((const TUint8*)"SSL3.0", 6); } else { return PJ_ENOTSUP; } /* Prepare addresses */ TInetAddr localaddr_, remaddr_; status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)localaddr, addr_len, localaddr_); if (status != PJ_SUCCESS) return status; status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)remaddr, addr_len, remaddr_); if (status != PJ_SUCCESS) return status; pj_sockaddr_cp((pj_sockaddr_t*)&ssock->rem_addr, remaddr); /* Init SSL engine */ TRAPD(err, sock = CPjSSLSocket::NewL(proto, ssock->qos_type, ssock->qos_params)); if (err != KErrNone) return PJ_ENOMEM; if (ssock->timeout.sec != 0 || ssock->timeout.msec != 0) { ssock->connect_timer = new CPjTimer(&ssock->timeout, &connect_timer_cb, ssock); } /* Convert server name to Symbian descriptor */ TPtrC8 servername_((TUint8*)ssock->servername.ptr, ssock->servername.slen); /* Convert cipher list to Symbian descriptor */ TPtrC8 ciphers_((TUint8*)ssock->ciphers.ptr, ssock->ciphers.slen); /* Try to connect */ status = sock->Connect(&connect_cb, ssock, localaddr_, remaddr_, servername_, ciphers_); if (status != PJ_SUCCESS && status != PJ_EPENDING) { delete sock; return status; } ssock->sock = sock; return status; } PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock) { PJ_UNUSED_ARG(ssock); return PJ_ENOTSUP; } ================================================ FILE: deps/pjsip/pjlib/src/pj/string.c ================================================ /* $Id: string.c 4440 2013-03-14 07:18:13Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #if PJ_FUNCTIONS_ARE_INLINED==0 # include #endif PJ_DEF(char*) pj_strstr(const pj_str_t *str, const pj_str_t *substr) { const char *s, *ends; /* Special case when substr is zero */ if (substr->slen == 0) { return (char*)str->ptr; } s = str->ptr; ends = str->ptr + str->slen - substr->slen; for (; s<=ends; ++s) { if (pj_ansi_strncmp(s, substr->ptr, substr->slen)==0) return (char*)s; } return NULL; } PJ_DEF(char*) pj_stristr(const pj_str_t *str, const pj_str_t *substr) { const char *s, *ends; /* Special case when substr is zero */ if (substr->slen == 0) { return (char*)str->ptr; } s = str->ptr; ends = str->ptr + str->slen - substr->slen; for (; s<=ends; ++s) { if (pj_ansi_strnicmp(s, substr->ptr, substr->slen)==0) return (char*)s; } return NULL; } PJ_DEF(pj_str_t*) pj_strltrim( pj_str_t *str ) { char *end = str->ptr + str->slen; register char *p = str->ptr; while (p < end && pj_isspace(*p)) ++p; str->slen -= (p - str->ptr); str->ptr = p; return str; } PJ_DEF(pj_str_t*) pj_strrtrim( pj_str_t *str ) { char *end = str->ptr + str->slen; register char *p = end - 1; while (p >= str->ptr && pj_isspace(*p)) --p; str->slen -= ((end - p) - 1); return str; } PJ_DEF(char*) pj_create_random_string(char *str, pj_size_t len) { unsigned i; char *p = str; PJ_CHECK_STACK(); for (i=0; i> 24, p+0 ); pj_val_to_hex_digit( (val & 0x00FF0000) >> 16, p+2 ); pj_val_to_hex_digit( (val & 0x0000FF00) >> 8, p+4 ); pj_val_to_hex_digit( (val & 0x000000FF) >> 0, p+6 ); p += 8; } for (i=i * 8; islen > 0 && (str->ptr[0] == '+' || str->ptr[0] == '-')) { pj_str_t s; s.ptr = str->ptr + 1; s.slen = str->slen - 1; return (str->ptr[0] == '-'? -(long)pj_strtoul(&s) : pj_strtoul(&s)); } else return pj_strtoul(str); } PJ_DEF(unsigned long) pj_strtoul(const pj_str_t *str) { unsigned long value; unsigned i; PJ_CHECK_STACK(); value = 0; for (i=0; i<(unsigned)str->slen; ++i) { if (!pj_isdigit(str->ptr[i])) break; value = value * 10 + (str->ptr[i] - '0'); } return value; } PJ_DEF(unsigned long) pj_strtoul2(const pj_str_t *str, pj_str_t *endptr, unsigned base) { unsigned long value; unsigned i; PJ_CHECK_STACK(); value = 0; if (base <= 10) { for (i=0; i<(unsigned)str->slen; ++i) { unsigned c = (str->ptr[i] - '0'); if (c >= base) break; value = value * base + c; } } else if (base == 16) { for (i=0; i<(unsigned)str->slen; ++i) { if (!pj_isxdigit(str->ptr[i])) break; value = value * 16 + pj_hex_digit_to_val(str->ptr[i]); } } else { pj_assert(!"Unsupported base"); i = 0; value = 0xFFFFFFFFUL; } if (endptr) { endptr->ptr = str->ptr + i; endptr->slen = str->slen - i; } return value; } PJ_DEF(float) pj_strtof(const pj_str_t *str) { pj_str_t part; char *pdot; float val; if (str->slen == 0) return 0; pdot = (char*)pj_memchr(str->ptr, '.', str->slen); part.ptr = str->ptr; part.slen = pdot ? pdot - str->ptr : str->slen; if (part.slen) val = (float)pj_strtol(&part); else val = 0; if (pdot) { part.ptr = pdot + 1; part.slen = (str->ptr + str->slen - pdot - 1); if (part.slen) { pj_str_t endptr; float fpart, fdiv; int i; fpart = (float)pj_strtoul2(&part, &endptr, 10); fdiv = 1.0; for (i=0; i<(part.slen - endptr.slen); ++i) fdiv = fdiv * 10; if (val >= 0) val += (fpart / fdiv); else val -= (fpart / fdiv); } } return val; } PJ_DEF(int) pj_utoa(unsigned long val, char *buf) { return pj_utoa_pad(val, buf, 0, 0); } PJ_DEF(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad) { char *p; int len; PJ_CHECK_STACK(); p = buf; do { unsigned long digval = (unsigned long) (val % 10); val /= 10; *p++ = (char) (digval + '0'); } while (val > 0); len = (int)(p-buf); while (len < min_dig) { *p++ = (char)pad; ++len; } *p-- = '\0'; do { char temp = *p; *p = *buf; *buf = temp; --p; ++buf; } while (buf < p); return len; } ================================================ FILE: deps/pjsip/pjlib/src/pj/symbols.c ================================================ /* $Id: symbols.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include /* * addr_resolv.h */ PJ_EXPORT_SYMBOL(pj_gethostbyname) /* * array.h */ PJ_EXPORT_SYMBOL(pj_array_insert) PJ_EXPORT_SYMBOL(pj_array_erase) PJ_EXPORT_SYMBOL(pj_array_find) /* * config.h */ PJ_EXPORT_SYMBOL(pj_dump_config) /* * errno.h */ PJ_EXPORT_SYMBOL(pj_get_os_error) PJ_EXPORT_SYMBOL(pj_set_os_error) PJ_EXPORT_SYMBOL(pj_get_netos_error) PJ_EXPORT_SYMBOL(pj_set_netos_error) PJ_EXPORT_SYMBOL(pj_strerror) /* * except.h */ PJ_EXPORT_SYMBOL(pj_throw_exception_) PJ_EXPORT_SYMBOL(pj_push_exception_handler_) PJ_EXPORT_SYMBOL(pj_pop_exception_handler_) PJ_EXPORT_SYMBOL(pj_setjmp) PJ_EXPORT_SYMBOL(pj_longjmp) PJ_EXPORT_SYMBOL(pj_exception_id_alloc) PJ_EXPORT_SYMBOL(pj_exception_id_free) PJ_EXPORT_SYMBOL(pj_exception_id_name) /* * fifobuf.h */ PJ_EXPORT_SYMBOL(pj_fifobuf_init) PJ_EXPORT_SYMBOL(pj_fifobuf_max_size) PJ_EXPORT_SYMBOL(pj_fifobuf_alloc) PJ_EXPORT_SYMBOL(pj_fifobuf_unalloc) PJ_EXPORT_SYMBOL(pj_fifobuf_free) /* * guid.h */ PJ_EXPORT_SYMBOL(pj_generate_unique_string) PJ_EXPORT_SYMBOL(pj_create_unique_string) /* * hash.h */ PJ_EXPORT_SYMBOL(pj_hash_calc) PJ_EXPORT_SYMBOL(pj_hash_create) PJ_EXPORT_SYMBOL(pj_hash_get) PJ_EXPORT_SYMBOL(pj_hash_set) PJ_EXPORT_SYMBOL(pj_hash_count) PJ_EXPORT_SYMBOL(pj_hash_first) PJ_EXPORT_SYMBOL(pj_hash_next) PJ_EXPORT_SYMBOL(pj_hash_this) /* * ioqueue.h */ PJ_EXPORT_SYMBOL(pj_ioqueue_create) PJ_EXPORT_SYMBOL(pj_ioqueue_destroy) PJ_EXPORT_SYMBOL(pj_ioqueue_set_lock) PJ_EXPORT_SYMBOL(pj_ioqueue_register_sock) PJ_EXPORT_SYMBOL(pj_ioqueue_unregister) PJ_EXPORT_SYMBOL(pj_ioqueue_get_user_data) PJ_EXPORT_SYMBOL(pj_ioqueue_poll) PJ_EXPORT_SYMBOL(pj_ioqueue_read) PJ_EXPORT_SYMBOL(pj_ioqueue_recv) PJ_EXPORT_SYMBOL(pj_ioqueue_recvfrom) PJ_EXPORT_SYMBOL(pj_ioqueue_write) PJ_EXPORT_SYMBOL(pj_ioqueue_send) PJ_EXPORT_SYMBOL(pj_ioqueue_sendto) #if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 PJ_EXPORT_SYMBOL(pj_ioqueue_accept) PJ_EXPORT_SYMBOL(pj_ioqueue_connect) #endif /* * list.h */ PJ_EXPORT_SYMBOL(pj_list_insert_before) PJ_EXPORT_SYMBOL(pj_list_insert_nodes_before) PJ_EXPORT_SYMBOL(pj_list_insert_after) PJ_EXPORT_SYMBOL(pj_list_insert_nodes_after) PJ_EXPORT_SYMBOL(pj_list_merge_first) PJ_EXPORT_SYMBOL(pj_list_merge_last) PJ_EXPORT_SYMBOL(pj_list_erase) PJ_EXPORT_SYMBOL(pj_list_find_node) PJ_EXPORT_SYMBOL(pj_list_search) /* * log.h */ PJ_EXPORT_SYMBOL(pj_log_write) #if PJ_LOG_MAX_LEVEL >= 1 PJ_EXPORT_SYMBOL(pj_log_set_log_func) PJ_EXPORT_SYMBOL(pj_log_get_log_func) PJ_EXPORT_SYMBOL(pj_log_set_level) PJ_EXPORT_SYMBOL(pj_log_get_level) PJ_EXPORT_SYMBOL(pj_log_set_decor) PJ_EXPORT_SYMBOL(pj_log_get_decor) PJ_EXPORT_SYMBOL(pj_log_1) #endif #if PJ_LOG_MAX_LEVEL >= 2 PJ_EXPORT_SYMBOL(pj_log_2) #endif #if PJ_LOG_MAX_LEVEL >= 3 PJ_EXPORT_SYMBOL(pj_log_3) #endif #if PJ_LOG_MAX_LEVEL >= 4 PJ_EXPORT_SYMBOL(pj_log_4) #endif #if PJ_LOG_MAX_LEVEL >= 5 PJ_EXPORT_SYMBOL(pj_log_5) #endif #if PJ_LOG_MAX_LEVEL >= 6 PJ_EXPORT_SYMBOL(pj_log_6) #endif /* * os.h */ PJ_EXPORT_SYMBOL(pj_init) PJ_EXPORT_SYMBOL(pj_getpid) PJ_EXPORT_SYMBOL(pj_thread_register) PJ_EXPORT_SYMBOL(pj_thread_create) PJ_EXPORT_SYMBOL(pj_thread_get_name) PJ_EXPORT_SYMBOL(pj_thread_resume) PJ_EXPORT_SYMBOL(pj_thread_this) PJ_EXPORT_SYMBOL(pj_thread_join) PJ_EXPORT_SYMBOL(pj_thread_destroy) PJ_EXPORT_SYMBOL(pj_thread_sleep) #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0 PJ_EXPORT_SYMBOL(pj_thread_check_stack) PJ_EXPORT_SYMBOL(pj_thread_get_stack_max_usage) PJ_EXPORT_SYMBOL(pj_thread_get_stack_info) #endif PJ_EXPORT_SYMBOL(pj_atomic_create) PJ_EXPORT_SYMBOL(pj_atomic_destroy) PJ_EXPORT_SYMBOL(pj_atomic_set) PJ_EXPORT_SYMBOL(pj_atomic_get) PJ_EXPORT_SYMBOL(pj_atomic_inc) PJ_EXPORT_SYMBOL(pj_atomic_dec) PJ_EXPORT_SYMBOL(pj_thread_local_alloc) PJ_EXPORT_SYMBOL(pj_thread_local_free) PJ_EXPORT_SYMBOL(pj_thread_local_set) PJ_EXPORT_SYMBOL(pj_thread_local_get) PJ_EXPORT_SYMBOL(pj_enter_critical_section) PJ_EXPORT_SYMBOL(pj_leave_critical_section) PJ_EXPORT_SYMBOL(pj_mutex_create) PJ_EXPORT_SYMBOL(pj_mutex_lock) PJ_EXPORT_SYMBOL(pj_mutex_unlock) PJ_EXPORT_SYMBOL(pj_mutex_trylock) PJ_EXPORT_SYMBOL(pj_mutex_destroy) #if defined(PJ_DEBUG) && PJ_DEBUG != 0 PJ_EXPORT_SYMBOL(pj_mutex_is_locked) #endif #if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 PJ_EXPORT_SYMBOL(pj_sem_create) PJ_EXPORT_SYMBOL(pj_sem_wait) PJ_EXPORT_SYMBOL(pj_sem_trywait) PJ_EXPORT_SYMBOL(pj_sem_post) PJ_EXPORT_SYMBOL(pj_sem_destroy) #endif PJ_EXPORT_SYMBOL(pj_gettimeofday) PJ_EXPORT_SYMBOL(pj_time_decode) #if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 PJ_EXPORT_SYMBOL(pj_gettickcount) PJ_EXPORT_SYMBOL(pj_get_timestamp) PJ_EXPORT_SYMBOL(pj_get_timestamp_freq) PJ_EXPORT_SYMBOL(pj_elapsed_time) PJ_EXPORT_SYMBOL(pj_elapsed_usec) PJ_EXPORT_SYMBOL(pj_elapsed_nanosec) PJ_EXPORT_SYMBOL(pj_elapsed_cycle) #endif /* * pool.h */ PJ_EXPORT_SYMBOL(pj_pool_create) PJ_EXPORT_SYMBOL(pj_pool_release) PJ_EXPORT_SYMBOL(pj_pool_getobjname) PJ_EXPORT_SYMBOL(pj_pool_reset) PJ_EXPORT_SYMBOL(pj_pool_get_capacity) PJ_EXPORT_SYMBOL(pj_pool_get_used_size) PJ_EXPORT_SYMBOL(pj_pool_alloc) PJ_EXPORT_SYMBOL(pj_pool_calloc) PJ_EXPORT_SYMBOL(pj_pool_factory_default_policy) PJ_EXPORT_SYMBOL(pj_pool_create_int) PJ_EXPORT_SYMBOL(pj_pool_init_int) PJ_EXPORT_SYMBOL(pj_pool_destroy_int) PJ_EXPORT_SYMBOL(pj_caching_pool_init) PJ_EXPORT_SYMBOL(pj_caching_pool_destroy) /* * rand.h */ PJ_EXPORT_SYMBOL(pj_rand) PJ_EXPORT_SYMBOL(pj_srand) /* * rbtree.h */ PJ_EXPORT_SYMBOL(pj_rbtree_init) PJ_EXPORT_SYMBOL(pj_rbtree_first) PJ_EXPORT_SYMBOL(pj_rbtree_last) PJ_EXPORT_SYMBOL(pj_rbtree_next) PJ_EXPORT_SYMBOL(pj_rbtree_prev) PJ_EXPORT_SYMBOL(pj_rbtree_insert) PJ_EXPORT_SYMBOL(pj_rbtree_find) PJ_EXPORT_SYMBOL(pj_rbtree_erase) PJ_EXPORT_SYMBOL(pj_rbtree_max_height) PJ_EXPORT_SYMBOL(pj_rbtree_min_height) /* * sock.h */ PJ_EXPORT_SYMBOL(PJ_AF_UNIX) PJ_EXPORT_SYMBOL(PJ_AF_INET) PJ_EXPORT_SYMBOL(PJ_AF_INET6) PJ_EXPORT_SYMBOL(PJ_AF_PACKET) PJ_EXPORT_SYMBOL(PJ_AF_IRDA) PJ_EXPORT_SYMBOL(PJ_SOCK_STREAM) PJ_EXPORT_SYMBOL(PJ_SOCK_DGRAM) PJ_EXPORT_SYMBOL(PJ_SOCK_RAW) PJ_EXPORT_SYMBOL(PJ_SOCK_RDM) PJ_EXPORT_SYMBOL(PJ_SOL_SOCKET) PJ_EXPORT_SYMBOL(PJ_SOL_IP) PJ_EXPORT_SYMBOL(PJ_SOL_TCP) PJ_EXPORT_SYMBOL(PJ_SOL_UDP) PJ_EXPORT_SYMBOL(PJ_SOL_IPV6) PJ_EXPORT_SYMBOL(pj_ntohs) PJ_EXPORT_SYMBOL(pj_htons) PJ_EXPORT_SYMBOL(pj_ntohl) PJ_EXPORT_SYMBOL(pj_htonl) PJ_EXPORT_SYMBOL(pj_inet_ntoa) PJ_EXPORT_SYMBOL(pj_inet_aton) PJ_EXPORT_SYMBOL(pj_inet_addr) PJ_EXPORT_SYMBOL(pj_sockaddr_in_set_str_addr) PJ_EXPORT_SYMBOL(pj_sockaddr_in_init) PJ_EXPORT_SYMBOL(pj_gethostname) PJ_EXPORT_SYMBOL(pj_gethostaddr) PJ_EXPORT_SYMBOL(pj_sock_socket) PJ_EXPORT_SYMBOL(pj_sock_close) PJ_EXPORT_SYMBOL(pj_sock_bind) PJ_EXPORT_SYMBOL(pj_sock_bind_in) #if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 PJ_EXPORT_SYMBOL(pj_sock_listen) PJ_EXPORT_SYMBOL(pj_sock_accept) PJ_EXPORT_SYMBOL(pj_sock_shutdown) #endif PJ_EXPORT_SYMBOL(pj_sock_connect) PJ_EXPORT_SYMBOL(pj_sock_getpeername) PJ_EXPORT_SYMBOL(pj_sock_getsockname) PJ_EXPORT_SYMBOL(pj_sock_getsockopt) PJ_EXPORT_SYMBOL(pj_sock_setsockopt) PJ_EXPORT_SYMBOL(pj_sock_recv) PJ_EXPORT_SYMBOL(pj_sock_recvfrom) PJ_EXPORT_SYMBOL(pj_sock_send) PJ_EXPORT_SYMBOL(pj_sock_sendto) /* * sock_select.h */ PJ_EXPORT_SYMBOL(PJ_FD_ZERO) PJ_EXPORT_SYMBOL(PJ_FD_SET) PJ_EXPORT_SYMBOL(PJ_FD_CLR) PJ_EXPORT_SYMBOL(PJ_FD_ISSET) PJ_EXPORT_SYMBOL(pj_sock_select) /* * string.h */ PJ_EXPORT_SYMBOL(pj_str) PJ_EXPORT_SYMBOL(pj_strassign) PJ_EXPORT_SYMBOL(pj_strcpy) PJ_EXPORT_SYMBOL(pj_strcpy2) PJ_EXPORT_SYMBOL(pj_strdup) PJ_EXPORT_SYMBOL(pj_strdup_with_null) PJ_EXPORT_SYMBOL(pj_strdup2) PJ_EXPORT_SYMBOL(pj_strdup3) PJ_EXPORT_SYMBOL(pj_strcmp) PJ_EXPORT_SYMBOL(pj_strcmp2) PJ_EXPORT_SYMBOL(pj_strncmp) PJ_EXPORT_SYMBOL(pj_strncmp2) PJ_EXPORT_SYMBOL(pj_stricmp) PJ_EXPORT_SYMBOL(pj_stricmp2) PJ_EXPORT_SYMBOL(pj_strnicmp) PJ_EXPORT_SYMBOL(pj_strnicmp2) PJ_EXPORT_SYMBOL(pj_strcat) PJ_EXPORT_SYMBOL(pj_strltrim) PJ_EXPORT_SYMBOL(pj_strrtrim) PJ_EXPORT_SYMBOL(pj_strtrim) PJ_EXPORT_SYMBOL(pj_create_random_string) PJ_EXPORT_SYMBOL(pj_strtoul) PJ_EXPORT_SYMBOL(pj_utoa) PJ_EXPORT_SYMBOL(pj_utoa_pad) /* * timer.h */ PJ_EXPORT_SYMBOL(pj_timer_heap_mem_size) PJ_EXPORT_SYMBOL(pj_timer_heap_create) PJ_EXPORT_SYMBOL(pj_timer_entry_init) PJ_EXPORT_SYMBOL(pj_timer_heap_schedule) PJ_EXPORT_SYMBOL(pj_timer_heap_cancel) PJ_EXPORT_SYMBOL(pj_timer_heap_count) PJ_EXPORT_SYMBOL(pj_timer_heap_earliest_time) PJ_EXPORT_SYMBOL(pj_timer_heap_poll) /* * types.h */ PJ_EXPORT_SYMBOL(pj_time_val_normalize) ================================================ FILE: deps/pjsip/pjlib/src/pj/timer.c ================================================ /* $Id: timer.c 4449 2013-03-22 03:16:35Z bennylp $ */ /* * The PJLIB's timer heap is based (or more correctly, copied and modied) * from ACE library by Douglas C. Schmidt. ACE is an excellent OO framework * that implements many core patterns for concurrent communication software. * If you're looking for C++ alternative of PJLIB, then ACE is your best * solution. * * You may use this file according to ACE open source terms or PJLIB open * source terms. You can find the fine ACE library at: * http://www.cs.wustl.edu/~schmidt/ACE.html * * ACE is Copyright (C)1993-2006 Douglas C. Schmidt * * GNU Public License: * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #define THIS_FILE "timer.c" #define HEAP_PARENT(X) (X == 0 ? 0 : (((X) - 1) / 2)) #define HEAP_LEFT(X) (((X)+(X))+1) #define DEFAULT_MAX_TIMED_OUT_PER_POLL (64) enum { F_DONT_CALL = 1, F_DONT_ASSERT = 2, F_SET_ID = 4 }; /** * The implementation of timer heap. */ struct pj_timer_heap_t { /** Pool from which the timer heap resize will get the storage from */ pj_pool_t *pool; /** Maximum size of the heap. */ pj_size_t max_size; /** Current size of the heap. */ pj_size_t cur_size; /** Max timed out entries to process per poll. */ unsigned max_entries_per_poll; /** Lock object. */ pj_lock_t *lock; /** Autodelete lock. */ pj_bool_t auto_delete_lock; /** * Current contents of the Heap, which is organized as a "heap" of * pj_timer_entry *'s. In this context, a heap is a "partially * ordered, almost complete" binary tree, which is stored in an * array. */ pj_timer_entry **heap; /** * An array of "pointers" that allows each pj_timer_entry in the * to be located in O(1) time. Basically, * contains the slot in the array where an pj_timer_entry * with timer id resides. Thus, the timer id passed back from * is really an slot into the array. The * array serves two purposes: negative values are * treated as "pointers" for the , whereas positive * values are treated as "pointers" into the array. */ pj_timer_id_t *timer_ids; /** * "Pointer" to the first element in the freelist contained within * the array, which is organized as a stack. */ pj_timer_id_t timer_ids_freelist; /** Callback to be called when a timer expires. */ pj_timer_heap_callback *callback; }; PJ_INLINE(void) lock_timer_heap( pj_timer_heap_t *ht ) { if (ht->lock) { pj_lock_acquire(ht->lock); } } PJ_INLINE(void) unlock_timer_heap( pj_timer_heap_t *ht ) { if (ht->lock) { pj_lock_release(ht->lock); } } static void copy_node( pj_timer_heap_t *ht, pj_size_t slot, pj_timer_entry *moved_node ) { PJ_CHECK_STACK(); // Insert into its new location in the heap. ht->heap[slot] = moved_node; // Update the corresponding slot in the parallel array. ht->timer_ids[moved_node->_timer_id] = (int)slot; } static pj_timer_id_t pop_freelist( pj_timer_heap_t *ht ) { // We need to truncate this to for backwards compatibility. pj_timer_id_t new_id = ht->timer_ids_freelist; PJ_CHECK_STACK(); // The freelist values in the are negative, so we need // to negate them to get the next freelist "pointer." ht->timer_ids_freelist = -ht->timer_ids[ht->timer_ids_freelist]; return new_id; } static void push_freelist (pj_timer_heap_t *ht, pj_timer_id_t old_id) { PJ_CHECK_STACK(); // The freelist values in the are negative, so we need // to negate them to get the next freelist "pointer." ht->timer_ids[old_id] = -ht->timer_ids_freelist; ht->timer_ids_freelist = old_id; } static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node, size_t slot, size_t child) { PJ_CHECK_STACK(); // Restore the heap property after a deletion. while (child < ht->cur_size) { // Choose the smaller of the two children. if (child + 1 < ht->cur_size && PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value, ht->heap[child]->_timer_value)) child++; // Perform a if the child has a larger timeout value than // the . if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value, moved_node->_timer_value)) { copy_node( ht, slot, ht->heap[child]); slot = child; child = HEAP_LEFT(child); } else // We've found our location in the heap. break; } copy_node( ht, slot, moved_node); } static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node, size_t slot, size_t parent) { // Restore the heap property after an insertion. while (slot > 0) { // If the parent node is greater than the we need // to copy it down. if (PJ_TIME_VAL_LT(moved_node->_timer_value, ht->heap[parent]->_timer_value)) { copy_node(ht, slot, ht->heap[parent]); slot = parent; parent = HEAP_PARENT(slot); } else break; } // Insert the new node into its proper resting place in the heap and // update the corresponding slot in the parallel array. copy_node(ht, slot, moved_node); } static pj_timer_entry * remove_node( pj_timer_heap_t *ht, size_t slot) { pj_timer_entry *removed_node = ht->heap[slot]; // Return this timer id to the freelist. push_freelist( ht, removed_node->_timer_id ); // Decrement the size of the heap by one since we're removing the // "slot"th node. ht->cur_size--; // Set the ID removed_node->_timer_id = -1; // Only try to reheapify if we're not deleting the last entry. if (slot < ht->cur_size) { pj_size_t parent; pj_timer_entry *moved_node = ht->heap[ht->cur_size]; // Move the end node to the location being removed and update // the corresponding slot in the parallel array. copy_node( ht, slot, moved_node); // If the time_value_> is great than or equal its // parent it needs be moved down the heap. parent = HEAP_PARENT (slot); if (PJ_TIME_VAL_GTE(moved_node->_timer_value, ht->heap[parent]->_timer_value)) reheap_down( ht, moved_node, slot, HEAP_LEFT(slot)); else reheap_up( ht, moved_node, slot, parent); } return removed_node; } static void grow_heap(pj_timer_heap_t *ht) { // All the containers will double in size from max_size_ size_t new_size = ht->max_size * 2; pj_timer_id_t *new_timer_ids; pj_size_t i; // First grow the heap itself. pj_timer_entry **new_heap = 0; new_heap = (pj_timer_entry**) pj_pool_alloc(ht->pool, sizeof(pj_timer_entry*) * new_size); memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry*)); //delete [] this->heap_; ht->heap = new_heap; // Grow the array of timer ids. new_timer_ids = 0; new_timer_ids = (pj_timer_id_t*) pj_pool_alloc(ht->pool, new_size * sizeof(pj_timer_id_t)); memcpy( new_timer_ids, ht->timer_ids, ht->max_size * sizeof(pj_timer_id_t)); //delete [] timer_ids_; ht->timer_ids = new_timer_ids; // And add the new elements to the end of the "freelist". for (i = ht->max_size; i < new_size; i++) ht->timer_ids[i] = -((pj_timer_id_t) (i + 1)); ht->max_size = new_size; } static void insert_node(pj_timer_heap_t *ht, pj_timer_entry *new_node) { if (ht->cur_size + 2 >= ht->max_size) grow_heap(ht); reheap_up( ht, new_node, ht->cur_size, HEAP_PARENT(ht->cur_size)); ht->cur_size++; } static pj_status_t schedule_entry( pj_timer_heap_t *ht, pj_timer_entry *entry, const pj_time_val *future_time ) { if (ht->cur_size < ht->max_size) { // Obtain the next unique sequence number. // Set the entry entry->_timer_id = pop_freelist(ht); entry->_timer_value = *future_time; insert_node( ht, entry); return 0; } else return -1; } static int cancel( pj_timer_heap_t *ht, pj_timer_entry *entry, unsigned flags) { long timer_node_slot; PJ_CHECK_STACK(); // Check to see if the timer_id is out of range if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size) { entry->_timer_id = -1; return 0; } timer_node_slot = ht->timer_ids[entry->_timer_id]; if (timer_node_slot < 0) { // Check to see if timer_id is still valid. entry->_timer_id = -1; return 0; } if (entry != ht->heap[timer_node_slot]) { if ((flags & F_DONT_ASSERT) == 0) pj_assert(entry == ht->heap[timer_node_slot]); entry->_timer_id = -1; return 0; } else { remove_node( ht, timer_node_slot); if ((flags & F_DONT_CALL) == 0) // Call the close hook. (*ht->callback)(ht, entry); return 1; } } /* * Calculate memory size required to create a timer heap. */ PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count) { return /* size of the timer heap itself: */ sizeof(pj_timer_heap_t) + /* size of each entry: */ (count+2) * (sizeof(pj_timer_entry*)+sizeof(pj_timer_id_t)) + /* lock, pool etc: */ 132; } /* * Create a new timer heap. */ PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool, pj_size_t size, pj_timer_heap_t **p_heap) { pj_timer_heap_t *ht; pj_size_t i; PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL); *p_heap = NULL; /* Magic? */ size += 2; /* Allocate timer heap data structure from the pool */ ht = PJ_POOL_ALLOC_T(pool, pj_timer_heap_t); if (!ht) return PJ_ENOMEM; /* Initialize timer heap sizes */ ht->max_size = size; ht->cur_size = 0; ht->max_entries_per_poll = DEFAULT_MAX_TIMED_OUT_PER_POLL; ht->timer_ids_freelist = 1; ht->pool = pool; /* Lock. */ ht->lock = NULL; ht->auto_delete_lock = 0; // Create the heap array. ht->heap = (pj_timer_entry**) pj_pool_alloc(pool, sizeof(pj_timer_entry*) * size); if (!ht->heap) return PJ_ENOMEM; // Create the parallel ht->timer_ids = (pj_timer_id_t *) pj_pool_alloc( pool, sizeof(pj_timer_id_t) * size); if (!ht->timer_ids) return PJ_ENOMEM; // Initialize the "freelist," which uses negative values to // distinguish freelist elements from "pointers" into the // array. for (i=0; itimer_ids[i] = -((pj_timer_id_t) (i + 1)); *p_heap = ht; return PJ_SUCCESS; } PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *ht ) { if (ht->lock && ht->auto_delete_lock) { pj_lock_destroy(ht->lock); ht->lock = NULL; } } PJ_DEF(void) pj_timer_heap_set_lock( pj_timer_heap_t *ht, pj_lock_t *lock, pj_bool_t auto_del ) { if (ht->lock && ht->auto_delete_lock) pj_lock_destroy(ht->lock); ht->lock = lock; ht->auto_delete_lock = auto_del; } PJ_DEF(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht, unsigned count ) { unsigned old_count = ht->max_entries_per_poll; ht->max_entries_per_poll = count; return old_count; } PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry, int id, void *user_data, pj_timer_heap_callback *cb ) { pj_assert(entry && cb); entry->_timer_id = -1; entry->id = id; entry->user_data = user_data; entry->cb = cb; entry->_grp_lock = NULL; return entry; } PJ_DEF(pj_bool_t) pj_timer_entry_running( pj_timer_entry *entry ) { return (entry->_timer_id >= 1); } #if PJ_TIMER_DEBUG static pj_status_t schedule_w_grp_lock_dbg(pj_timer_heap_t *ht, pj_timer_entry *entry, const pj_time_val *delay, pj_bool_t set_id, int id_val, pj_grp_lock_t *grp_lock, const char *src_file, int src_line) #else static pj_status_t schedule_w_grp_lock(pj_timer_heap_t *ht, pj_timer_entry *entry, const pj_time_val *delay, pj_bool_t set_id, int id_val, pj_grp_lock_t *grp_lock) #endif { pj_status_t status; pj_time_val expires; PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL); PJ_ASSERT_RETURN(entry->cb != NULL, PJ_EINVAL); /* Prevent same entry from being scheduled more than once */ PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP); #if PJ_TIMER_DEBUG entry->src_file = src_file; entry->src_line = src_line; #endif pj_gettickcount(&expires); PJ_TIME_VAL_ADD(expires, *delay); lock_timer_heap(ht); status = schedule_entry(ht, entry, &expires); if (status == PJ_SUCCESS) { if (set_id) entry->id = id_val; entry->_grp_lock = grp_lock; if (entry->_grp_lock) { pj_grp_lock_add_ref(entry->_grp_lock); } } unlock_timer_heap(ht); return status; } #if PJ_TIMER_DEBUG PJ_DEF(pj_status_t) pj_timer_heap_schedule_dbg( pj_timer_heap_t *ht, pj_timer_entry *entry, const pj_time_val *delay, const char *src_file, int src_line) { return schedule_w_grp_lock_dbg(ht, entry, delay, PJ_FALSE, 1, NULL, src_file, src_line); } PJ_DEF(pj_status_t) pj_timer_heap_schedule_w_grp_lock_dbg( pj_timer_heap_t *ht, pj_timer_entry *entry, const pj_time_val *delay, int id_val, pj_grp_lock_t *grp_lock, const char *src_file, int src_line) { return schedule_w_grp_lock_dbg(ht, entry, delay, PJ_TRUE, id_val, grp_lock, src_file, src_line); } #else PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht, pj_timer_entry *entry, const pj_time_val *delay) { return schedule_w_grp_lock(ht, entry, delay, PJ_FALSE, 1, NULL); } PJ_DEF(pj_status_t) pj_timer_heap_schedule_w_grp_lock(pj_timer_heap_t *ht, pj_timer_entry *entry, const pj_time_val *delay, int id_val, pj_grp_lock_t *grp_lock) { return schedule_w_grp_lock(ht, entry, delay, PJ_TRUE, id_val, grp_lock); } #endif static int cancel_timer(pj_timer_heap_t *ht, pj_timer_entry *entry, unsigned flags, int id_val) { int count; PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL); lock_timer_heap(ht); count = cancel(ht, entry, flags | F_DONT_CALL); if (flags & F_SET_ID) { entry->id = id_val; } if (entry->_grp_lock) { pj_grp_lock_t *grp_lock = entry->_grp_lock; entry->_grp_lock = NULL; pj_grp_lock_dec_ref(grp_lock); } unlock_timer_heap(ht); return count; } PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht, pj_timer_entry *entry) { return cancel_timer(ht, entry, 0, 0); } PJ_DEF(int) pj_timer_heap_cancel_if_active(pj_timer_heap_t *ht, pj_timer_entry *entry, int id_val) { return cancel_timer(ht, entry, F_SET_ID | F_DONT_ASSERT, id_val); } PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht, pj_time_val *next_delay ) { pj_time_val now; unsigned count; PJ_ASSERT_RETURN(ht, 0); lock_timer_heap(ht); if (!ht->cur_size && next_delay) { next_delay->sec = next_delay->msec = PJ_MAXINT32; unlock_timer_heap(ht); return 0; } count = 0; pj_gettickcount(&now); while ( ht->cur_size && PJ_TIME_VAL_LTE(ht->heap[0]->_timer_value, now) && count < ht->max_entries_per_poll ) { pj_timer_entry *node = remove_node(ht, 0); pj_grp_lock_t *grp_lock; ++count; grp_lock = node->_grp_lock; node->_grp_lock = NULL; unlock_timer_heap(ht); PJ_RACE_ME(5); if (node->cb) (*node->cb)(ht, node); if (grp_lock) pj_grp_lock_dec_ref(grp_lock); lock_timer_heap(ht); } if (ht->cur_size && next_delay) { *next_delay = ht->heap[0]->_timer_value; PJ_TIME_VAL_SUB(*next_delay, now); if (next_delay->sec < 0 || next_delay->msec < 0) next_delay->sec = next_delay->msec = 0; } else if (next_delay) { next_delay->sec = next_delay->msec = PJ_MAXINT32; } unlock_timer_heap(ht); return count; } PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht ) { PJ_ASSERT_RETURN(ht, 0); return ht->cur_size; } PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht, pj_time_val *timeval) { pj_assert(ht->cur_size != 0); if (ht->cur_size == 0) return PJ_ENOTFOUND; lock_timer_heap(ht); *timeval = ht->heap[0]->_timer_value; unlock_timer_heap(ht); return PJ_SUCCESS; } #if PJ_TIMER_DEBUG PJ_DEF(void) pj_timer_heap_dump(pj_timer_heap_t *ht) { lock_timer_heap(ht); PJ_LOG(3,(THIS_FILE, "Dumping timer heap:")); PJ_LOG(3,(THIS_FILE, " Cur size: %d entries, max: %d", (int)ht->cur_size, (int)ht->max_size)); if (ht->cur_size) { unsigned i; pj_time_val now; PJ_LOG(3,(THIS_FILE, " Entries: ")); PJ_LOG(3,(THIS_FILE, " _id\tId\tElapsed\tSource")); PJ_LOG(3,(THIS_FILE, " ----------------------------------")); pj_gettickcount(&now); for (i=0; i<(unsigned)ht->cur_size; ++i) { pj_timer_entry *e = ht->heap[i]; pj_time_val delta; if (PJ_TIME_VAL_LTE(e->_timer_value, now)) delta.sec = delta.msec = 0; else { delta = e->_timer_value; PJ_TIME_VAL_SUB(delta, now); } PJ_LOG(3,(THIS_FILE, " %d\t%d\t%d.%03d\t%s:%d", e->_timer_id, e->id, (int)delta.sec, (int)delta.msec, e->src_file, e->src_line)); } } unlock_timer_heap(ht); } #endif ================================================ FILE: deps/pjsip/pjlib/src/pj/timer_symbian.cpp ================================================ /* $Id: timer_symbian.cpp 4374 2013-02-27 07:15:57Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "os_symbian.h" #define DEFAULT_MAX_TIMED_OUT_PER_POLL (64) // Maximum number of miliseconds that RTimer.At() supports #define MAX_RTIMER_INTERVAL 2147 /* Absolute maximum number of timer entries */ #ifndef PJ_SYMBIAN_TIMER_MAX_COUNT # define PJ_SYMBIAN_TIMER_MAX_COUNT 65535 #endif /* Get the number of free slots in the timer heap */ #define FREECNT(th) (th->max_size - th->cur_size) // Forward declaration class CPjTimerEntry; /** * The implementation of timer heap. */ struct pj_timer_heap_t { /** Maximum size of the heap. */ pj_size_t max_size; /** Current size of the heap. */ pj_size_t cur_size; /** Array of timer entries. A scheduled timer will occupy one slot, and * the slot number will be saved in entry->_timer_id */ CPjTimerEntry **entries; /** Array of free slot indexes in the "entries" array */ int *free_slots; }; /** * Active object for each timer entry. */ class CPjTimerEntry : public CActive { public: pj_timer_entry *entry_; static CPjTimerEntry* NewL( pj_timer_heap_t *timer_heap, pj_timer_entry *entry, const pj_time_val *delay); ~CPjTimerEntry(); virtual void RunL(); virtual void DoCancel(); private: pj_timer_heap_t *timer_heap_; RTimer rtimer_; pj_uint32_t interval_left_; CPjTimerEntry(pj_timer_heap_t *timer_heap, pj_timer_entry *entry); void ConstructL(const pj_time_val *delay); void Schedule(); }; ////////////////////////////////////////////////////////////////////////////// /* * Implementation. */ /* Grow timer heap to the specified size */ static pj_status_t realloc_timer_heap(pj_timer_heap_t *th, pj_size_t new_size) { typedef CPjTimerEntry *entry_ptr; CPjTimerEntry **entries = NULL; int *free_slots = NULL; unsigned i, j; if (new_size > PJ_SYMBIAN_TIMER_MAX_COUNT) { /* Just some sanity limit */ new_size = PJ_SYMBIAN_TIMER_MAX_COUNT; if (new_size <= th->max_size) { /* We've grown large enough */ pj_assert(!"Too many timer heap entries"); return PJ_ETOOMANY; } } /* Allocate entries, move entries from the old array if there is one */ entries = new entry_ptr[new_size]; if (th->entries) { pj_memcpy(entries, th->entries, th->max_size * sizeof(th->entries[0])); } /* Initialize the remaining new area */ pj_bzero(&entries[th->max_size], (new_size - th->max_size) * sizeof(th->entries[0])); /* Allocate free slots array */ free_slots = new int[new_size]; if (th->free_slots) { pj_memcpy(free_slots, th->free_slots, FREECNT(th) * sizeof(th->free_slots[0])); } /* Initialize the remaining new area */ for (i=FREECNT(th), j=th->max_size; jentries; th->entries = entries; th->max_size = new_size; delete [] th->free_slots; th->free_slots = free_slots; return PJ_SUCCESS; } /* Allocate and register an entry to timer heap for newly scheduled entry */ static pj_status_t add_entry(pj_timer_heap_t *th, CPjTimerEntry *entry) { pj_status_t status; int slot; /* Check that there's still capacity left in the timer heap */ if (FREECNT(th) < 1) { // Grow the timer heap twice the capacity status = realloc_timer_heap(th, th->max_size * 2); if (status != PJ_SUCCESS) return status; } /* Allocate one free slot. Use LIFO */ slot = th->free_slots[FREECNT(th)-1]; PJ_ASSERT_RETURN((slot >= 0) && (slot < (int)th->max_size) && (th->entries[slot]==NULL), PJ_EBUG); th->free_slots[FREECNT(th)-1] = -1; th->entries[slot] = entry; entry->entry_->_timer_id = slot; ++th->cur_size; return PJ_SUCCESS; } /* Free a slot when an entry's timer has elapsed or cancel */ static pj_status_t remove_entry(pj_timer_heap_t *th, CPjTimerEntry *entry) { int slot = entry->entry_->_timer_id; PJ_ASSERT_RETURN(slot >= 0 && slot < (int)th->max_size, PJ_EBUG); PJ_ASSERT_RETURN(FREECNT(th) < th->max_size, PJ_EBUG); PJ_ASSERT_RETURN(th->entries[slot]==entry, PJ_EBUG); PJ_ASSERT_RETURN(th->free_slots[FREECNT(th)]==-1, PJ_EBUG); th->entries[slot] = NULL; th->free_slots[FREECNT(th)] = slot; entry->entry_->_timer_id = -1; --th->cur_size; return PJ_SUCCESS; } CPjTimerEntry::CPjTimerEntry(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) : CActive(PJ_SYMBIAN_TIMER_PRIORITY), entry_(entry), timer_heap_(timer_heap), interval_left_(0) { } CPjTimerEntry::~CPjTimerEntry() { Cancel(); rtimer_.Close(); } void CPjTimerEntry::Schedule() { pj_int32_t interval; if (interval_left_ > MAX_RTIMER_INTERVAL) { interval = MAX_RTIMER_INTERVAL; } else { interval = interval_left_; } interval_left_ -= interval; rtimer_.After(iStatus, interval * 1000); SetActive(); } void CPjTimerEntry::ConstructL(const pj_time_val *delay) { rtimer_.CreateLocal(); CActiveScheduler::Add(this); interval_left_ = PJ_TIME_VAL_MSEC(*delay); Schedule(); } CPjTimerEntry* CPjTimerEntry::NewL(pj_timer_heap_t *timer_heap, pj_timer_entry *entry, const pj_time_val *delay) { CPjTimerEntry *self = new CPjTimerEntry(timer_heap, entry); CleanupStack::PushL(self); self->ConstructL(delay); CleanupStack::Pop(self); return self; } void CPjTimerEntry::RunL() { if (interval_left_ > 0) { Schedule(); return; } remove_entry(timer_heap_, this); entry_->cb(timer_heap_, entry_); // Finger's crossed! delete this; } void CPjTimerEntry::DoCancel() { /* It's possible that _timer_id is -1, see schedule(). In this case, * the entry has not been added to the timer heap, so don't remove * it. */ if (entry_ && entry_->_timer_id != -1) remove_entry(timer_heap_, this); rtimer_.Cancel(); } ////////////////////////////////////////////////////////////////////////////// /* * Calculate memory size required to create a timer heap. */ PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count) { return /* size of the timer heap itself: */ sizeof(pj_timer_heap_t) + /* size of each entry: */ (count+2) * (sizeof(void*)+sizeof(int)) + /* lock, pool etc: */ 132; } /* * Create a new timer heap. */ PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool, pj_size_t size, pj_timer_heap_t **p_heap) { pj_timer_heap_t *ht; pj_status_t status; PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL); *p_heap = NULL; /* Allocate timer heap data structure from the pool */ ht = PJ_POOL_ZALLOC_T(pool, pj_timer_heap_t); if (!ht) return PJ_ENOMEM; /* Allocate slots */ status = realloc_timer_heap(ht, size); if (status != PJ_SUCCESS) return status; *p_heap = ht; return PJ_SUCCESS; } PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *ht ) { /* Cancel and delete pending active objects */ if (ht->entries) { unsigned i; for (i=0; imax_size; ++i) { if (ht->entries[i]) { ht->entries[i]->entry_ = NULL; ht->entries[i]->Cancel(); delete ht->entries[i]; ht->entries[i] = NULL; } } } delete [] ht->entries; delete [] ht->free_slots; ht->entries = NULL; ht->free_slots = NULL; } PJ_DEF(void) pj_timer_heap_set_lock( pj_timer_heap_t *ht, pj_lock_t *lock, pj_bool_t auto_del ) { PJ_UNUSED_ARG(ht); if (auto_del) pj_lock_destroy(lock); } PJ_DEF(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht, unsigned count ) { /* Not applicable */ PJ_UNUSED_ARG(count); return ht->max_size; } PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry, int id, void *user_data, pj_timer_heap_callback *cb ) { pj_assert(entry && cb); entry->_timer_id = -1; entry->id = id; entry->user_data = user_data; entry->cb = cb; return entry; } PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht, pj_timer_entry *entry, const pj_time_val *delay) { CPjTimerEntry *timerObj; pj_status_t status; PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL); PJ_ASSERT_RETURN(entry->cb != NULL, PJ_EINVAL); /* Prevent same entry from being scheduled more than once */ PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP); entry->_timer_id = -1; timerObj = CPjTimerEntry::NewL(ht, entry, delay); status = add_entry(ht, timerObj); if (status != PJ_SUCCESS) { timerObj->Cancel(); delete timerObj; return status; } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_timer_heap_schedule_w_grp_lock(pj_timer_heap_t *ht, pj_timer_entry *entry, const pj_time_val *delay, int id_val, pj_grp_lock_t *grp_lock) { pj_status_t status; PJ_UNUSED_ARG(grp_lock); status = pj_timer_heap_schedule(ht, entry, delay); if (status == PJ_SUCCESS) entry->id = id_val; return status; } PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht, pj_timer_entry *entry) { PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL); if (entry->_timer_id >= 0 && entry->_timer_id < (int)ht->max_size) { CPjTimerEntry *timerObj = ht->entries[entry->_timer_id]; if (timerObj) { timerObj->Cancel(); delete timerObj; return 1; } else { return 0; } } else { return 0; } } PJ_DEF(int) pj_timer_heap_cancel_if_active(pj_timer_heap_t *ht, pj_timer_entry *entry, int id_val) { int count = pj_timer_heap_cancel(ht, entry); if (count == 1) entry->id = id_val; return count; } PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht, pj_time_val *next_delay ) { /* Polling is not necessary on Symbian, since all async activities * are registered to active scheduler. */ PJ_UNUSED_ARG(ht); if (next_delay) { next_delay->sec = 1; next_delay->msec = 0; } return 0; } PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht ) { PJ_ASSERT_RETURN(ht, 0); return ht->cur_size; } PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht, pj_time_val *timeval) { /* We don't support this! */ PJ_UNUSED_ARG(ht); timeval->sec = 1; timeval->msec = 0; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib/src/pj/types.c ================================================ /* $Id: types.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include PJ_DEF(void) pj_time_val_normalize(pj_time_val *t) { PJ_CHECK_STACK(); if (t->msec >= 1000) { t->sec += (t->msec / 1000); t->msec = (t->msec % 1000); } else if (t->msec <= -1000) { do { t->sec--; t->msec += 1000; } while (t->msec <= -1000); } if (t->sec >= 1 && t->msec < 0) { t->sec--; t->msec += 1000; } else if (t->sec < 0 && t->msec > 0) { t->sec++; t->msec -= 1000; } } ================================================ FILE: deps/pjsip/pjlib/src/pj/unicode_symbian.cpp ================================================ /* $Id: unicode_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "os_symbian.h" /* * Convert ANSI strings to Unicode strings. */ PJ_DEF(wchar_t*) pj_ansi_to_unicode( const char *str, pj_size_t len, wchar_t *wbuf, pj_size_t wbuf_count) { TPtrC8 aForeign((const TUint8*)str, (TInt)len); TPtr16 aUnicode((TUint16*)wbuf, (TInt)(wbuf_count-1)); TInt left; left = PjSymbianOS::Instance()->ConvertToUnicode(aUnicode, aForeign); if (left != 0) { // Error, or there are unconvertable characters *wbuf = 0; } else { if (len < wbuf_count) wbuf[len] = 0; else wbuf[len-1] = 0; } return wbuf; } /* * Convert Unicode string to ANSI string. */ PJ_DEF(char*) pj_unicode_to_ansi( const wchar_t *wstr, pj_size_t len, char *buf, pj_size_t buf_size) { TPtrC16 aUnicode((const TUint16*)wstr, (TInt)len); TPtr8 aForeign((TUint8*)buf, (TInt)(buf_size-1)); TInt left; left = PjSymbianOS::Instance()->ConvertFromUnicode(aForeign, aUnicode); if (left != 0) { // Error, or there are unconvertable characters buf[0] = '\0'; } else { if (len < buf_size) buf[len] = '\0'; else buf[len-1] = '\0'; } return buf; } ================================================ FILE: deps/pjsip/pjlib/src/pj/unicode_win32.c ================================================ /* $Id: unicode_win32.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include PJ_DEF(wchar_t*) pj_ansi_to_unicode(const char *s, int len, wchar_t *buf, int buf_count) { PJ_ASSERT_RETURN(s && buf, NULL); len = MultiByteToWideChar(CP_ACP, 0, s, len, buf, buf_count); if (buf_count) { if (len < buf_count) buf[len] = 0; else buf[len-1] = 0; } return buf; } PJ_DEF(char*) pj_unicode_to_ansi( const wchar_t *wstr, pj_ssize_t len, char *buf, int buf_size) { PJ_ASSERT_RETURN(wstr && buf, NULL); len = WideCharToMultiByte(CP_ACP, 0, wstr, (int)len, buf, buf_size, NULL, NULL); if (buf_size) { if (len < buf_size) buf[len] = '\0'; else buf[len-1] = '\0'; } return buf; } ================================================ FILE: deps/pjsip/pjlib-util/build/Makefile ================================================ include ../../build.mak include ../../version.mak include $(PJDIR)/build/common.mak RULES_MAK := $(PJDIR)/build/rules.mak PJLIB_LIB:=$(PJDIR)/pjlib/lib/libpj-$(TARGET_NAME)$(LIBEXT) export PJLIB_UTIL_LIB:=../lib/libpjlib-util-$(TARGET_NAME)$(LIBEXT) ############################################################################### # Gather all flags. # export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ $(CFLAGS) $(CC_INC)../include $(CC_INC)../../pjlib/include export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ $(HOST_CXXFLAGS) $(CXXFLAGS) export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJLIB_UTIL_LIB)) \ $(subst /,$(HOST_PSEP),$(PJLIB_LIB)) \ $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ $(LDFLAGS) ############################################################################### # Defines for building PJLIB-UTIL library # export PJLIB_UTIL_SRCDIR = ../src/pjlib-util export PJLIB_UTIL_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ base64.o crc32.o errno.o dns.o \ dns_dump.o dns_server.o getopt.o hmac_md5.o hmac_sha1.o \ http_client.o md5.o pcap.o resolver.o scanner.o sha1.o \ srv_resolver.o string.o stun_simple.o \ stun_simple_client.o xml.o export PJLIB_UTIL_CFLAGS += $(_CFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Main entry # # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. # TARGETS := pjlib-util all: $(TARGETS) doc: cd .. && rm -rf docs/$(PJ_VERSION) && doxygen docs/doxygen.cfg @if [ -n "$(WWWDIR)" ] && ! [ -d "$(WWWDIR)/docs/$(PJ_VERSION)/pjlib-util/docs/html" ] ; then \ echo "Creating docs/$(PJ_VERSION)/pjlib-util/docs/html" ; \ mkdir -p $(WWWDIR)/docs/$(PJ_VERSION)/pjlib-util/docs/html ; \ fi @if [ -n "$(WWWDIR)" ] && [ -d "$(WWWDIR)/docs/$(PJ_VERSION)/pjlib-util/docs/html" ] ; then \ echo "Copying docs/$(PJ_VERSION) to $(WWWDIR)/docs/$(PJ_VERSION)/pjlib-util/docs/html.." ; \ cp -v -a ../docs/$(PJ_VERSION)/html/* $(WWWDIR)/docs/$(PJ_VERSION)/pjlib-util/docs/html/ ; \ fi dep: depend distclean: realclean .PHONY: dep depend pjlib pjlib-test clean realclean distclean pjlib-util: $(MAKE) -f $(RULES_MAK) APP=PJLIB_UTIL app=pjlib-util $(PJLIB_UTIL_LIB) .PHONY: ../lib/pjlib-util.ko ../lib/pjlib-util.ko: echo Making $@ $(MAKE) -f $(RULES_MAK) APP=PJLIB_UTIL app=pjlib-util $@ clean: $(MAKE) -f $(RULES_MAK) APP=PJLIB_UTIL app=pjlib-util $@ realclean: $(subst @@,$(subst /,$(HOST_PSEP),.pjlib-util-$(TARGET_NAME).depend),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),.pjlib-util-test-$(TARGET_NAME).depend),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=PJLIB_UTIL app=pjlib-util $@ depend: $(MAKE) -f $(RULES_MAK) APP=PJLIB_UTIL app=pjlib-util $@ ================================================ FILE: deps/pjsip/pjlib-util/docs/doxygen.cfg ================================================ # Doxyfile 1.3-rc3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # General configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "PJLIB-UTIL Reference" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = $(PJ_VERSION) # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = docs/$(PJ_VERSION) # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, # Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en # (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, # Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian. OUTPUT_LANGUAGE = English # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these class will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited # members of a class in the documentation of that class as if those members were # ordinary class members. Constructors, destructors and assignment operators of # the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. It is allowed to use relative paths in the argument list. STRIP_FROM_PATH = "c:\project\pjproject" # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower case letters. If set to YES upper case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # users are adviced to set this option to NO. CASE_SENSE_NAMES = YES # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explict @brief command for a brief description. JAVADOC_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = YES # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # reimplements. INHERIT_DOCS = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consist of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. # For instance some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources # only. Doxygen will then generate output that is more tailored for Java. # For instance namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES TYPEDEF_HIDES_STRUCT = YES #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = include/pjlib-util # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp # *.h++ *.idl *.odl FILE_PATTERNS = *.h *.c # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories # that are symbolic links (a Unix filesystem feature) are excluded from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. EXCLUDE_PATTERNS = "*_i.h" "*/compat/*" # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = . # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = YES # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. INPUT_FILTER = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .htm # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = docs/header.html # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = docs/footer.html # The HTML_STYLESHEET tag can be used to specify a user defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet HTML_STYLESHEET = docs/doxygen.css # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output dir. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non empty doxygen will try to run # the html help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the Html help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimised for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assigments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_XML = NO # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_PREDEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. PREDEFINED = PJ_DECL(x)=x PJ_DEF(x)=x PJ_IDECL(x)=x \ PJ_IDEF(x)=x PJ_INLINE(x)=x \ PJ_DECL_DATA(x)=x \ PJ_DECL_NO_RETURN(x)=x \ PJ_NO_RETURN=x \ PJ_HAS_HIGH_RES_TIMER=1 \ PJ_LOG_MAX_LEVEL=4 \ PJ_HAS_SEMAPHORE=1 \ PJ_HAS_EVENT_OBJ=1 \ PJ_HAS_TCP=1 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse the # parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::addtions related to external references #--------------------------------------------------------------------------- # The TAGFILES tag can be used to specify one or more tagfiles. TAGFILES = ../pjlib/docs/pjlib.tag=../../../pjlib/docs/html ../pjnath/docs/pjnath.tag=../../../pjnath/docs/html ../pjsip/docs/pjsip.tag=../../../pjsip/docs/html ../pjmedia/docs/pjmedia.tag=../../../pjmedia/docs/html # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = docs/pjlib-util.tag # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = NO # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). #PERL_PATH = /usr/bin/perl PERL_PATH = /c/Perl/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or # super classes. Setting the tag to NO turns the diagrams off. Note that this # option is superceded by the HAVE_DOT option below. This is only a fallback. It is # recommended to install and use dot, since it yield more powerful graphs. CLASS_DIAGRAMS = NO # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found on the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_WIDTH = 1024 # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_HEIGHT = 1024 # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermedate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::addtions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO # The CGI_NAME tag should be the name of the CGI script that # starts the search engine (doxysearch) with the correct parameters. # A script with this name will be generated by doxygen. #CGI_NAME = search.cgi # The CGI_URL tag should be the absolute URL to the directory where the # cgi binaries are located. See the documentation of your http daemon for # details. #CGI_URL = # The DOC_URL tag should be the absolute URL to the directory where the # documentation is located. If left blank the absolute path to the # documentation, with file:// prepended to it, will be used. #DOC_URL = # The DOC_ABSPATH tag should be the absolute path to the directory where the # documentation is located. If left blank the directory on the local machine # will be used. #DOC_ABSPATH = # The BIN_ABSPATH tag must point to the directory where the doxysearch binary # is installed. #BIN_ABSPATH = /usr/local/bin/ # The EXT_DOC_PATHS tag can be used to specify one or more paths to # documentation generated for other projects. This allows doxysearch to search # the documentation for these projects as well. #EXT_DOC_PATHS = ================================================ FILE: deps/pjsip/pjlib-util/docs/doxygen.css ================================================ BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV { font-family: Geneva, Arial, Helvetica, sans-serif; } BODY,TD { font-size: 80%; } CODE { font-size: 120%; font-family: monospace; } .fragment, pre { font-size: 110%; font-family: monospace; } H1 { text-align: center; font-size: 240%; } H2 { font-size: 200%; margin-top : 60px; } H3 { font-size: 160%; } H4 { font-size: 120%; } CAPTION { font-weight: bold } DIV.qindex { width: 100%; background-color: #eeeeff; border: 1px solid #b0b0b0; text-align: center; margin: 2px; padding: 2px; line-height: 140%; } DIV.nav { width: 100%; background-color: #eeeeff; border: 1px solid #b0b0b0; text-align: center; margin: 2px; padding: 2px; line-height: 140%; } A.qindex { text-decoration: none; font-size: 120%; color: #1A419D; } A.qindex:visited { text-decoration: none; color: #1A419D } A.qindex:hover { text-decoration: none; background-color: #ddddff; } A.qindexHL { text-decoration: none; font-weight: bold; background-color: #6666cc; color: #ffffff; border: 1px double #9295C2; } A.qindexHL:hover { text-decoration: none; background-color: #6666cc; color: #ffffff; } A.qindexHL:visited { text-decoration: none; background-color: #6666cc; color: #ffffff } A.el { text-decoration: none; font-weight: bold } A.elRef { font-weight: bold } A.code:link { text-decoration: none; font-weight: normal; color: #0000FF; } A.code:visited { text-decoration: none; font-weight: normal; color: #0000FF} A.codeRef:link { font-weight: normal; color: #0000FF} A.codeRef:visited { font-weight: normal; color: #0000FF} A:hover { text-decoration: none; background-color: #f2f2ff } DL.el { margin-left: -1cm } PRE.fragment { border: 1px solid #CCCCCC; background-color: #f5f5f5; margin-top: 4px; margin-bottom: 4px; margin-left: 2px; margin-right: 8px; padding-left: 6px; padding-right: 6px; padding-top: 4px; padding-bottom: 4px; } DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px } TD.md { background-color: #F4F4FB; font-weight: bold; } TD.mdPrefix { background-color: #F4F4FB; color: #606060; font-size: 80%; } TD.mdname1 { background-color: #F4F4FB; font-weight: bold; color: #602020; } TD.mdname { background-color: #F4F4FB; font-weight: bold; color: #602020; width: 600px; } DIV.groupHeader { margin-left: 16px; margin-top: 12px; margin-bottom: 6px; font-weight: bold; } DIV.groupText { margin-left: 16px; font-style: italic; font-size: 90% } BODY { background: white; color: black; margin-right: 20px; margin-left: 20px; } TD.indexkey { background-color: #eeeeff; font-weight: bold; padding-right : 10px; padding-top : 2px; padding-left : 10px; padding-bottom : 2px; margin-left : 0px; margin-right : 0px; margin-top : 2px; margin-bottom : 2px; border: 1px solid #CCCCCC; } TD.indexvalue { background-color: #eeeeff; font-style: italic; padding-right : 10px; padding-top : 2px; padding-left : 10px; padding-bottom : 2px; margin-left : 0px; margin-right : 0px; margin-top : 2px; margin-bottom : 2px; border: 1px solid #CCCCCC; } TR.memlist { background-color: #f0f0f0; } P.formulaDsp { text-align: center; } IMG.formulaDsp { } IMG.formulaInl { vertical-align: middle; } SPAN.keyword { color: #008000 } SPAN.keywordtype { color: #604020 } SPAN.keywordflow { color: #e08000 } SPAN.comment { color: #800000 } SPAN.preprocessor { color: #806020 } SPAN.stringliteral { color: #002080 } SPAN.charliteral { color: #008080 } .mdTable { border: 1px solid #868686; background-color: #F4F4FB; } .mdRow { padding: 8px 10px; } .mdescLeft { padding: 0px 8px 4px 8px; font-size: 80%; font-style: italic; background-color: #FAFAFA; border-top: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; margin: 0px; } .mdescRight { padding: 0px 8px 4px 8px; font-size: 80%; font-style: italic; background-color: #FAFAFA; border-top: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; margin: 0px; } .memItemLeft { padding: 1px 0px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: solid; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memItemRight { padding: 1px 8px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: solid; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memTemplItemLeft { padding: 1px 0px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memTemplItemRight { padding: 1px 8px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memTemplParams { padding: 1px 0px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: solid; border-right-style: none; border-bottom-style: none; border-left-style: none; color: #606060; background-color: #FAFAFA; font-size: 80%; } .search { color: #003399; font-weight: bold; } FORM.search { margin-bottom: 0px; margin-top: 0px; } INPUT.search { font-size: 75%; color: #000080; font-weight: normal; background-color: #eeeeff; } TD.tiny { font-size: 75%; } a { color: #252E78; } a:visited { color: #3D2185; } .dirtab { padding: 4px; border-collapse: collapse; border: 1px solid #b0b0b0; } TH.dirtab { background: #eeeeff; font-weight: bold; } HR { height: 1px; border: none; border-top: 1px solid black; } ================================================ FILE: deps/pjsip/pjlib-util/docs/footer.html ================================================

 


PJLIB-UTIL Open Source, small footprint, and portable asynchronous/caching DNS resolver, text scanner, STUN client, and XML library
Copyright (C) 2006-2009 Teluu Inc.
================================================ FILE: deps/pjsip/pjlib-util/docs/header.html ================================================ $title ($projectnumber)

Home --> Documentations --> PJLIB-UTIL Reference

================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/base64.h ================================================ /* $Id: base64.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_BASE64_H__ #define __PJLIB_UTIL_BASE64_H__ /** * @file base64.h * @brief Base64 encoding and decoding */ #include PJ_BEGIN_DECL /** * @defgroup PJLIB_UTIL_BASE64 Base64 Encoding/Decoding * @ingroup PJLIB_UTIL_ENCRYPTION * @{ * This module implements base64 encoding and decoding. */ /** * Helper macro to calculate the approximate length required for base256 to * base64 conversion. */ #define PJ_BASE256_TO_BASE64_LEN(len) (len * 4 / 3 + 3) /** * Helper macro to calculate the approximage length required for base64 to * base256 conversion. */ #define PJ_BASE64_TO_BASE256_LEN(len) (len * 3 / 4) /** * Encode a buffer into base64 encoding. * * @param input The input buffer. * @param in_len Size of the input buffer. * @param output Output buffer. Caller must allocate this buffer with * the appropriate size. * @param out_len On entry, it specifies the length of the output buffer. * Upon return, this will be filled with the actual * length of the output buffer. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pj_base64_encode(const pj_uint8_t *input, int in_len, char *output, int *out_len); /** * Decode base64 string. * * @param input Input string. * @param out Buffer to store the output. Caller must allocate * this buffer with the appropriate size. * @param out_len On entry, it specifies the length of the output buffer. * Upon return, this will be filled with the actual * length of the output. */ PJ_DECL(pj_status_t) pj_base64_decode(const pj_str_t *input, pj_uint8_t *out, int *out_len); /** * @} */ PJ_END_DECL #endif /* __PJLIB_UTIL_BASE64_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/config.h ================================================ /* $Id: config.h 4461 2013-04-05 03:02:19Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_CONFIG_H__ #define __PJLIB_UTIL_CONFIG_H__ /** * @file config.h * @brief Compile time settings */ /** * @defgroup PJLIB_UTIL_CONFIG Configuration * @ingroup PJLIB_UTIL_BASE * @{ */ /* ************************************************************************** * DNS CONFIGURATION */ /** * Maximum number of IP addresses in DNS A response. */ #ifndef PJ_DNS_MAX_IP_IN_A_REC # define PJ_DNS_MAX_IP_IN_A_REC 8 #endif /** * Maximum server address entries per one SRV record */ #ifndef PJ_DNS_SRV_MAX_ADDR # define PJ_DNS_SRV_MAX_ADDR 8 #endif /** * This constant specifies the maximum names to keep in the temporary name * table when performing name compression scheme when duplicating DNS packet * (the #pj_dns_packet_dup() function). * * Generally name compression is desired, since it saves some memory (see * PJ_DNS_RESOLVER_RES_BUF_SIZE setting). However it comes at the expense of * a little processing overhead to perform name scanning and also a little * bit more stack usage (8 bytes per entry on 32bit platform). * * Default: 16 */ #ifndef PJ_DNS_MAX_NAMES_IN_NAMETABLE # define PJ_DNS_MAX_NAMES_IN_NAMETABLE 16 #endif /* ************************************************************************** * RESOLVER CONFIGURATION */ /** * Maximum numbers of DNS nameservers that can be configured in resolver. */ #ifndef PJ_DNS_RESOLVER_MAX_NS # define PJ_DNS_RESOLVER_MAX_NS 16 #endif /** * Default retransmission delay, in miliseconds. The combination of * retransmission delay and count determines the query timeout. * * Default: 2000 (2 seconds, according to RFC 1035) */ #ifndef PJ_DNS_RESOLVER_QUERY_RETRANSMIT_DELAY # define PJ_DNS_RESOLVER_QUERY_RETRANSMIT_DELAY 2000 #endif /** * Maximum number of transmissions before timeout is declared for * the query. * * Default: 5 */ #ifndef PJ_DNS_RESOLVER_QUERY_RETRANSMIT_COUNT # define PJ_DNS_RESOLVER_QUERY_RETRANSMIT_COUNT 5 #endif /** * Maximum life-time of DNS response in the resolver response cache, * in seconds. If the value is zero, then DNS response caching will be * disabled. * * Default is 300 seconds (5 minutes). * * @see PJ_DNS_RESOLVER_INVALID_TTL */ #ifndef PJ_DNS_RESOLVER_MAX_TTL # define PJ_DNS_RESOLVER_MAX_TTL (5*60) #endif /** * The life-time of invalid DNS response in the resolver response cache. * An invalid DNS response is a response which RCODE is non-zero and * response without any answer section. These responses can be put in * the cache too to minimize message round-trip. * * Default: 60 (one minute). * * @see PJ_DNS_RESOLVER_MAX_TTL */ #ifndef PJ_DNS_RESOLVER_INVALID_TTL # define PJ_DNS_RESOLVER_INVALID_TTL 60 #endif /** * The interval on which nameservers which are known to be good to be * probed again to determine whether they are still good. Note that * this applies to both active nameserver (the one currently being used) * and idle nameservers (good nameservers that are not currently selected). * The probing to query the "goodness" of nameservers involves sending * the same query to multiple servers, so it's probably not a good idea * to send this probing too often. * * Default: 600 (ten minutes) * * @see PJ_DNS_RESOLVER_BAD_NS_TTL */ #ifndef PJ_DNS_RESOLVER_GOOD_NS_TTL # define PJ_DNS_RESOLVER_GOOD_NS_TTL (10*60) #endif /** * The interval on which nameservers which known to be bad to be probed * again to determine whether it is still bad. * * Default: 60 (one minute) * * @see PJ_DNS_RESOLVER_GOOD_NS_TTL */ #ifndef PJ_DNS_RESOLVER_BAD_NS_TTL # define PJ_DNS_RESOLVER_BAD_NS_TTL (1*60) #endif /** * Maximum size of UDP packet. RFC 1035 states that maximum size of * DNS packet carried over UDP is 512 bytes. * * Default: 512 byes */ #ifndef PJ_DNS_RESOLVER_MAX_UDP_SIZE # define PJ_DNS_RESOLVER_MAX_UDP_SIZE 512 #endif /** * Size of memory pool allocated for each individual DNS response cache. * This value here should be more or less the same as maximum UDP packet * size (PJ_DNS_RESOLVER_MAX_UDP_SIZE), since the DNS replicator function * (#pj_dns_packet_dup()) is also capable of performing name compressions. * * Default: 512 */ #ifndef PJ_DNS_RESOLVER_RES_BUF_SIZE # define PJ_DNS_RESOLVER_RES_BUF_SIZE 512 #endif /** * Size of temporary pool buffer for parsing DNS packets in resolver. * * default: 4000 */ #ifndef PJ_DNS_RESOLVER_TMP_BUF_SIZE # define PJ_DNS_RESOLVER_TMP_BUF_SIZE 4000 #endif /* ************************************************************************** * SCANNER CONFIGURATION */ /** * Macro PJ_SCANNER_USE_BITWISE is defined and non-zero (by default yes) * will enable the use of bitwise for character input specification (cis). * This would save several kilobytes of .bss memory in the SIP parser. */ #ifndef PJ_SCANNER_USE_BITWISE # define PJ_SCANNER_USE_BITWISE 1 #endif /* ************************************************************************** * STUN CLIENT CONFIGURATION */ /** * Maximum number of attributes in the STUN packet (for the old STUN * library). * * Default: 16 */ #ifndef PJSTUN_MAX_ATTR # define PJSTUN_MAX_ATTR 16 #endif /** * Maximum number of attributes in the STUN packet (for the new STUN * library). * * Default: 16 */ #ifndef PJ_STUN_MAX_ATTR # define PJ_STUN_MAX_ATTR 16 #endif /* ************************************************************************** * ENCRYPTION */ /** * Specifies whether CRC32 algorithm should use the table based lookup table * for faster calculation, at the expense of about 1KB table size on the * executable. If zero, the CRC32 will use non-table based which is more than * an order of magnitude slower. * * Default: 1 */ #ifndef PJ_CRC32_HAS_TABLES # define PJ_CRC32_HAS_TABLES 1 #endif /* ************************************************************************** * HTTP Client configuration */ /** * Timeout value for HTTP request operation. The value is in ms. * Default: 60000ms */ #ifndef PJ_HTTP_DEFAULT_TIMEOUT # define PJ_HTTP_DEFAULT_TIMEOUT (60000) #endif /** * @} */ #endif /* __PJLIB_UTIL_CONFIG_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/crc32.h ================================================ /* $Id: crc32.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_CRC32_H__ #define __PJLIB_UTIL_CRC32_H__ /** * @file crc32.h * @brief CRC32 implementation */ #include PJ_BEGIN_DECL /** * @defgroup PJLIB_UTIL_CRC32 CRC32 (Cyclic Redundancy Check) * @ingroup PJLIB_UTIL_ENCRYPTION * @{ * This implements CRC32 algorithm. See ITU-T V.42 for the formal * specification. */ /** CRC32 context. */ typedef struct pj_crc32_context { pj_uint32_t crc_state; /**< Current state. */ } pj_crc32_context; /** * Initialize CRC32 context. * * @param ctx CRC32 context. */ PJ_DECL(void) pj_crc32_init(pj_crc32_context *ctx); /** * Feed data incrementally to the CRC32 algorithm. * * @param ctx CRC32 context. * @param data Input data. * @param nbytes Length of the input data. * * @return The current CRC32 value. */ PJ_DECL(pj_uint32_t) pj_crc32_update(pj_crc32_context *ctx, const pj_uint8_t *data, pj_size_t nbytes); /** * Finalize CRC32 calculation and retrieve the CRC32 value. * * @param ctx CRC32 context. * * @return The current CRC value. */ PJ_DECL(pj_uint32_t) pj_crc32_final(pj_crc32_context *ctx); /** * Perform one-off CRC32 calculation to the specified data. * * @param data Input data. * @param nbytes Length of input data. * * @return CRC value of the data. */ PJ_DECL(pj_uint32_t) pj_crc32_calc(const pj_uint8_t *data, pj_size_t nbytes); /** * @} */ PJ_END_DECL #endif /* __PJLIB_UTIL_CRC32_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/dns.h ================================================ /* $Id: dns.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_DNS_H__ #define __PJLIB_UTIL_DNS_H__ /** * @file dns.h * @brief Low level DNS message parsing and packetization. */ #include #include PJ_BEGIN_DECL /** * @defgroup PJ_DNS DNS and Asynchronous DNS Resolver * @ingroup PJ_PROTOCOLS */ /** * @defgroup PJ_DNS_PARSING Low-level DNS Message Parsing and Packetization * @ingroup PJ_DNS * @{ * * This module provides low-level services to parse and packetize DNS queries * and responses. The functions support building a DNS query packet and parse * the data in the DNS response. This implementation conforms to the * following specifications: * - RFC 1035: DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION * - RFC 1886: DNS Extensions to support IP version 6 * * To create a DNS query packet, application should call #pj_dns_make_query() * function, specifying the desired DNS query type, the name to be resolved, * and the buffer where the DNS packet will be built into. * * When incoming DNS query or response packet arrives, application can use * #pj_dns_parse_packet() to parse the TCP/UDP payload into parsed DNS packet * structure. * * This module does not provide any networking functionalities to send or * receive DNS packets. This functionality should be provided by higher layer * modules such as @ref PJ_DNS_RESOLVER. */ enum { PJ_DNS_CLASS_IN = 1 /**< DNS class IN. */ }; /** * This enumeration describes standard DNS record types as described by * RFC 1035, RFC 2782, and others. */ typedef enum pj_dns_type { PJ_DNS_TYPE_A = 1, /**< Host address (A) record. */ PJ_DNS_TYPE_NS = 2, /**< Authoritative name server (NS) */ PJ_DNS_TYPE_MD = 3, /**< Mail destination (MD) record. */ PJ_DNS_TYPE_MF = 4, /**< Mail forwarder (MF) record. */ PJ_DNS_TYPE_CNAME = 5, /**< Canonical name (CNAME) record. */ PJ_DNS_TYPE_SOA = 6, /**< Marks start of zone authority. */ PJ_DNS_TYPE_MB = 7, /**< Mailbox domain name (MB). */ PJ_DNS_TYPE_MG = 8, /**< Mail group member (MG). */ PJ_DNS_TYPE_MR = 9, /**< Mail rename domain name. */ PJ_DNS_TYPE_NULL = 10, /**< NULL RR. */ PJ_DNS_TYPE_WKS = 11, /**< Well known service description */ PJ_DNS_TYPE_PTR = 12, /**< Domain name pointer. */ PJ_DNS_TYPE_HINFO = 13, /**< Host information. */ PJ_DNS_TYPE_MINFO = 14, /**< Mailbox or mail list information. */ PJ_DNS_TYPE_MX = 15, /**< Mail exchange record. */ PJ_DNS_TYPE_TXT = 16, /**< Text string. */ PJ_DNS_TYPE_RP = 17, /**< Responsible person. */ PJ_DNS_TYPE_AFSB = 18, /**< AFS cell database. */ PJ_DNS_TYPE_X25 = 19, /**< X.25 calling address. */ PJ_DNS_TYPE_ISDN = 20, /**< ISDN calling address. */ PJ_DNS_TYPE_RT = 21, /**< Router. */ PJ_DNS_TYPE_NSAP = 22, /**< NSAP address. */ PJ_DNS_TYPE_NSAP_PTR= 23, /**< NSAP reverse address. */ PJ_DNS_TYPE_SIG = 24, /**< Signature. */ PJ_DNS_TYPE_KEY = 25, /**< Key. */ PJ_DNS_TYPE_PX = 26, /**< X.400 mail mapping. */ PJ_DNS_TYPE_GPOS = 27, /**< Geographical position (withdrawn) */ PJ_DNS_TYPE_AAAA = 28, /**< IPv6 address. */ PJ_DNS_TYPE_LOC = 29, /**< Location. */ PJ_DNS_TYPE_NXT = 30, /**< Next valid name in the zone. */ PJ_DNS_TYPE_EID = 31, /**< Endpoint idenfitier. */ PJ_DNS_TYPE_NIMLOC = 32, /**< Nimrod locator. */ PJ_DNS_TYPE_SRV = 33, /**< Server selection (SRV) record. */ PJ_DNS_TYPE_ATMA = 34, /**< DNS ATM address record. */ PJ_DNS_TYPE_NAPTR = 35, /**< DNS Naming authority pointer record. */ PJ_DNS_TYPE_KX = 36, /**< DNS key exchange record. */ PJ_DNS_TYPE_CERT = 37, /**< DNS certificate record. */ PJ_DNS_TYPE_A6 = 38, /**< DNS IPv6 address (experimental) */ PJ_DNS_TYPE_DNAME = 39, /**< DNS non-terminal name redirection rec. */ PJ_DNS_TYPE_OPT = 41, /**< DNS options - contains EDNS metadata. */ PJ_DNS_TYPE_APL = 42, /**< DNS Address Prefix List (APL) record. */ PJ_DNS_TYPE_DS = 43, /**< DNS Delegation Signer (DS) */ PJ_DNS_TYPE_SSHFP = 44, /**< DNS SSH Key Fingerprint */ PJ_DNS_TYPE_IPSECKEY= 45, /**< DNS IPSEC Key. */ PJ_DNS_TYPE_RRSIG = 46, /**< DNS Resource Record signature. */ PJ_DNS_TYPE_NSEC = 47, /**< DNS Next Secure Name. */ PJ_DNS_TYPE_DNSKEY = 48 /**< DNSSEC Key. */ } pj_dns_type; /** * Standard DNS header, according to RFC 1035, which will be present in * both DNS query and DNS response. * * Note that all values seen by application would be in * host by order. The library would convert them to network * byte order as necessary. */ typedef struct pj_dns_hdr { pj_uint16_t id; /**< Transaction ID. */ pj_uint16_t flags; /**< Flags. */ pj_uint16_t qdcount; /**< Nb. of queries. */ pj_uint16_t anscount; /**< Nb. of res records */ pj_uint16_t nscount; /**< Nb. of NS records. */ pj_uint16_t arcount; /**< Nb. of additional records */ } pj_dns_hdr; /** Create RCODE flag */ #define PJ_DNS_SET_RCODE(c) ((pj_uint16_t)((c) & 0x0F)) /** Create RA (Recursion Available) bit */ #define PJ_DNS_SET_RA(on) ((pj_uint16_t)((on) << 7)) /** Create RD (Recursion Desired) bit */ #define PJ_DNS_SET_RD(on) ((pj_uint16_t)((on) << 8)) /** Create TC (Truncated) bit */ #define PJ_DNS_SET_TC(on) ((pj_uint16_t)((on) << 9)) /** Create AA (Authoritative Answer) bit */ #define PJ_DNS_SET_AA(on) ((pj_uint16_t)((on) << 10)) /** Create four bits opcode */ #define PJ_DNS_SET_OPCODE(o) ((pj_uint16_t)((o) << 11)) /** Create query/response bit */ #define PJ_DNS_SET_QR(on) ((pj_uint16_t)((on) << 15)) /** Get RCODE value */ #define PJ_DNS_GET_RCODE(val) (((val) & PJ_DNS_SET_RCODE(0x0F)) >> 0) /** Get RA bit */ #define PJ_DNS_GET_RA(val) (((val) & PJ_DNS_SET_RA(1)) >> 7) /** Get RD bit */ #define PJ_DNS_GET_RD(val) (((val) & PJ_DNS_SET_RD(1)) >> 8) /** Get TC bit */ #define PJ_DNS_GET_TC(val) (((val) & PJ_DNS_SET_TC(1)) >> 9) /** Get AA bit */ #define PJ_DNS_GET_AA(val) (((val) & PJ_DNS_SET_AA(1)) >> 10) /** Get OPCODE value */ #define PJ_DNS_GET_OPCODE(val) (((val) & PJ_DNS_SET_OPCODE(0x0F)) >> 11) /** Get QR bit */ #define PJ_DNS_GET_QR(val) (((val) & PJ_DNS_SET_QR(1)) >> 15) /** * These constants describe DNS RCODEs. Application can fold these constants * into PJLIB pj_status_t namespace by calling #PJ_STATUS_FROM_DNS_RCODE() * macro. */ typedef enum pj_dns_rcode { PJ_DNS_RCODE_FORMERR = 1, /**< Format error. */ PJ_DNS_RCODE_SERVFAIL = 2, /**< Server failure. */ PJ_DNS_RCODE_NXDOMAIN = 3, /**< Name Error. */ PJ_DNS_RCODE_NOTIMPL = 4, /**< Not Implemented. */ PJ_DNS_RCODE_REFUSED = 5, /**< Refused. */ PJ_DNS_RCODE_YXDOMAIN = 6, /**< The name exists. */ PJ_DNS_RCODE_YXRRSET = 7, /**< The RRset (name, type) exists. */ PJ_DNS_RCODE_NXRRSET = 8, /**< The RRset (name, type) doesn't exist*/ PJ_DNS_RCODE_NOTAUTH = 9, /**< Not authorized. */ PJ_DNS_RCODE_NOTZONE = 10 /**< The zone specified is not a zone. */ } pj_dns_rcode; /** * This structure describes a DNS query record. */ typedef struct pj_dns_parsed_query { pj_str_t name; /**< The domain in the query. */ pj_uint16_t type; /**< Type of the query (pj_dns_type) */ pj_uint16_t dnsclass; /**< Network class (PJ_DNS_CLASS_IN=1) */ } pj_dns_parsed_query; /** * This structure describes a Resource Record parsed from the DNS packet. * All integral values are in host byte order. */ typedef struct pj_dns_parsed_rr { pj_str_t name; /**< The domain name which this rec pertains. */ pj_uint16_t type; /**< RR type code. */ pj_uint16_t dnsclass; /**< Class of data (PJ_DNS_CLASS_IN=1). */ pj_uint32_t ttl; /**< Time to live. */ pj_uint16_t rdlength; /**< Resource data length. */ void *data; /**< Pointer to the raw resource data, only when the type is not known. If it is known, the data will be put in rdata below. */ /** For resource types that are recognized/supported by this library, * the parsed resource data will be placed in this rdata union. */ union rdata { /** SRV Resource Data (PJ_DNS_TYPE_SRV, 33) */ struct srv { pj_uint16_t prio; /**< Target priority (lower is higher). */ pj_uint16_t weight; /**< Weight/proportion */ pj_uint16_t port; /**< Port number of the service */ pj_str_t target; /**< Target name. */ } srv; /** CNAME Resource Data (PJ_DNS_TYPE_CNAME, 5) */ struct cname { pj_str_t name; /**< Primary canonical name for an alias. */ } cname; /** NS Resource Data (PJ_DNS_TYPE_NS, 2) */ struct ns { pj_str_t name; /**< Primary name server. */ } ns; /** PTR Resource Data (PJ_DNS_TYPE_PTR, 12) */ struct ptr { pj_str_t name; /**< PTR name. */ } ptr; /** A Resource Data (PJ_DNS_TYPE_A, 1) */ struct a { pj_in_addr ip_addr;/**< IPv4 address in network byte order. */ } a; /** AAAA Resource Data (PJ_DNS_TYPE_AAAA, 28) */ struct aaaa { pj_in6_addr ip_addr;/**< IPv6 address in network byte order. */ } aaaa; } rdata; } pj_dns_parsed_rr; /** * This structure describes the parsed repersentation of the raw DNS packet. * Note that all integral values in the parsed packet are represented in * host byte order. */ typedef struct pj_dns_parsed_packet { pj_dns_hdr hdr; /**< Pointer to DNS hdr, in host byte order */ pj_dns_parsed_query *q; /**< Array of DNS queries. */ pj_dns_parsed_rr *ans; /**< Array of DNS RR answer. */ pj_dns_parsed_rr *ns; /**< Array of NS record in the answer. */ pj_dns_parsed_rr *arr; /**< Array of additional RR answer. */ } pj_dns_parsed_packet; /** * Option flags to be specified when calling #pj_dns_packet_dup() function. * These flags can be combined with bitwise OR operation. */ enum pj_dns_dup_options { PJ_DNS_NO_QD = 1, /**< Do not duplicate the query section. */ PJ_DNS_NO_ANS = 2, /**< Do not duplicate the answer section. */ PJ_DNS_NO_NS = 4, /**< Do not duplicate the NS section. */ PJ_DNS_NO_AR = 8 /**< Do not duplicate the additional rec section */ }; /** * Create DNS query packet to resolve the specified names. This function * can be used to build any types of DNS query, such as A record or DNS SRV * record. * * Application specifies the type of record and the name to be queried, * and the function will build the DNS query packet into the buffer * specified. Once the packet is successfully built, application can send * the packet via TCP or UDP connection. * * @param packet The buffer to put the DNS query packet. * @param size On input, it specifies the size of the buffer. * On output, it will be filled with the actual size of * the DNS query packet. * @param id DNS query ID to associate DNS response with the * query. * @param qtype DNS type of record to be queried (see #pj_dns_type). * @param name Name to be queried from the DNS server. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_dns_make_query(void *packet, unsigned *size, pj_uint16_t id, int qtype, const pj_str_t *name); /** * Parse raw DNS packet into parsed DNS packet structure. This function is * able to parse few DNS resource records such as A record, PTR record, * CNAME record, NS record, and SRV record. * * @param pool Pool to allocate memory for the parsed packet. * @param packet Pointer to the DNS packet (the TCP/UDP payload of * the raw packet). * @param size The size of the DNS packet. * @param p_res Pointer to store the resulting parsed packet. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_dns_parse_packet(pj_pool_t *pool, const void *packet, unsigned size, pj_dns_parsed_packet **p_res); /** * Duplicate DNS packet. * * @param pool The pool to allocate memory for the duplicated packet. * @param p The DNS packet to be cloned. * @param options Option flags, from pj_dns_dup_options. * @param p_dst Pointer to store the cloned DNS packet. */ PJ_DECL(void) pj_dns_packet_dup(pj_pool_t *pool, const pj_dns_parsed_packet*p, unsigned options, pj_dns_parsed_packet **p_dst); /** * Utility function to get the type name string of the specified DNS type. * * @param type DNS type (see #pj_dns_type). * * @return String name of the type (e.g. "A", "SRV", etc.). */ PJ_DECL(const char *) pj_dns_get_type_name(int type); /** * Initialize DNS record as DNS SRV record. * * @param rec The DNS resource record to be initialized as DNS * SRV record. * @param res_name Resource name. * @param dnsclass DNS class. * @param ttl Resource TTL value. * @param prio DNS SRV priority. * @param weight DNS SRV weight. * @param port Target port. * @param target Target name. */ PJ_DECL(void) pj_dns_init_srv_rr(pj_dns_parsed_rr *rec, const pj_str_t *res_name, unsigned dnsclass, unsigned ttl, unsigned prio, unsigned weight, unsigned port, const pj_str_t *target); /** * Initialize DNS record as DNS CNAME record. * * @param rec The DNS resource record to be initialized as DNS * CNAME record. * @param res_name Resource name. * @param dnsclass DNS class. * @param ttl Resource TTL value. * @param name Host name. */ PJ_DECL(void) pj_dns_init_cname_rr(pj_dns_parsed_rr *rec, const pj_str_t *res_name, unsigned dnsclass, unsigned ttl, const pj_str_t *name); /** * Initialize DNS record as DNS A record. * * @param rec The DNS resource record to be initialized as DNS * A record. * @param res_name Resource name. * @param dnsclass DNS class. * @param ttl Resource TTL value. * @param ip_addr Host address. */ PJ_DECL(void) pj_dns_init_a_rr(pj_dns_parsed_rr *rec, const pj_str_t *res_name, unsigned dnsclass, unsigned ttl, const pj_in_addr *ip_addr); /** * Dump DNS packet to standard log. * * @param res The DNS packet. */ PJ_DECL(void) pj_dns_dump_packet(const pj_dns_parsed_packet *res); /** * @} */ PJ_END_DECL #endif /* __PJLIB_UTIL_DNS_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/dns_server.h ================================================ /* $Id: dns_server.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_DNS_SERVER_H__ #define __PJLIB_UTIL_DNS_SERVER_H__ /** * @file dns_server.h * @brief Simple DNS server */ #include #include PJ_BEGIN_DECL /** * @defgroup PJ_DNS_SERVER Simple DNS Server * @ingroup PJ_DNS * @{ * This contains a simple but fully working DNS server implementation, * mostly for testing purposes. It supports serving various DNS resource * records such as SRV, CNAME, A, and AAAA. */ /** * Opaque structure to hold DNS server instance. */ typedef struct pj_dns_server pj_dns_server; /** * Create the DNS server instance. The instance will run immediately. * * @param pf The pool factory to create memory pools. * @param ioqueue Ioqueue instance where the server socket will be * registered to. * @param af Address family of the server socket (valid values * are pj_AF_INET() for IPv4 and pj_AF_INET6() for IPv6). * @param port The UDP port to listen. * @param flags Flags, currently must be zero. * @param p_srv Pointer to receive the DNS server instance. * * @return PJ_SUCCESS if server has been created successfully, * otherwise the function will return the appropriate * error code. */ PJ_DECL(pj_status_t) pj_dns_server_create(pj_pool_factory *pf, pj_ioqueue_t *ioqueue, int af, unsigned port, unsigned flags, pj_dns_server **p_srv); /** * Destroy DNS server instance. * * @param srv The DNS server instance. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_dns_server_destroy(pj_dns_server *srv); /** * Add generic resource record entries to the server. * * @param srv The DNS server instance. * @param count Number of records to be added. * @param rr Array of records to be added. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_dns_server_add_rec(pj_dns_server *srv, unsigned count, const pj_dns_parsed_rr rr[]); /** * Remove the specified record from the server. * * @param srv The DNS server instance. * @param dns_class The resource's DNS class. Valid value is PJ_DNS_CLASS_IN. * @param type The resource type. * @param name The resource name to be removed. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_dns_server_del_rec(pj_dns_server *srv, int dns_class, pj_dns_type type, const pj_str_t *name); /** * @} */ PJ_END_DECL #endif /* __PJLIB_UTIL_DNS_SERVER_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/errno.h ================================================ /* $Id: errno.h 4440 2013-03-14 07:18:13Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_ERRNO_H__ #define __PJLIB_UTIL_ERRNO_H__ #include /** * @defgroup PJLIB_UTIL_ERROR Error Codes * @ingroup PJLIB_UTIL_BASE * @{ */ /** * Start of error code relative to PJ_ERRNO_START_USER. * This value is 320000. */ #define PJLIB_UTIL_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*3) /************************************************************ * STUN ERROR ***********************************************************/ /** * @hideinitializer * Unable to resolve STUN server */ #define PJLIB_UTIL_ESTUNRESOLVE (PJLIB_UTIL_ERRNO_START+1) /* 320001 */ /** * @hideinitializer * Unknown STUN message type. */ #define PJLIB_UTIL_ESTUNINMSGTYPE (PJLIB_UTIL_ERRNO_START+2) /* 320002 */ /** * @hideinitializer * Invalid STUN message length */ #define PJLIB_UTIL_ESTUNINMSGLEN (PJLIB_UTIL_ERRNO_START+3) /* 320003 */ /** * @hideinitializer * Invalid STUN attribute length */ #define PJLIB_UTIL_ESTUNINATTRLEN (PJLIB_UTIL_ERRNO_START+4) /* 320004 */ /** * @hideinitializer * Invalid STUN attribute type */ #define PJLIB_UTIL_ESTUNINATTRTYPE (PJLIB_UTIL_ERRNO_START+5) /* 320005 */ /** * @hideinitializer * Invalid STUN server/socket index */ #define PJLIB_UTIL_ESTUNININDEX (PJLIB_UTIL_ERRNO_START+6) /* 320006 */ /** * @hideinitializer * No STUN binding response in the message */ #define PJLIB_UTIL_ESTUNNOBINDRES (PJLIB_UTIL_ERRNO_START+7) /* 320007 */ /** * @hideinitializer * Received STUN error attribute */ #define PJLIB_UTIL_ESTUNRECVERRATTR (PJLIB_UTIL_ERRNO_START+8) /* 320008 */ /** * @hideinitializer * No STUN mapped address attribute */ #define PJLIB_UTIL_ESTUNNOMAP (PJLIB_UTIL_ERRNO_START+9) /* 320009 */ /** * @hideinitializer * Received no response from STUN server */ #define PJLIB_UTIL_ESTUNNOTRESPOND (PJLIB_UTIL_ERRNO_START+10) /* 320010 */ /** * @hideinitializer * Symetric NAT detected by STUN */ #define PJLIB_UTIL_ESTUNSYMMETRIC (PJLIB_UTIL_ERRNO_START+11) /* 320011 */ /** * @hideinitializer * Invalid STUN magic value */ #define PJLIB_UTIL_ESTUNNOTMAGIC (PJLIB_UTIL_ERRNO_START+12) /* 320012 */ /** * @hideinitializer * Invalid STUN fingerprint value */ #define PJLIB_UTIL_ESTUNFINGERPRINT (PJLIB_UTIL_ERRNO_START+13) /* 320013 */ /************************************************************ * XML ERROR ***********************************************************/ /** * @hideinitializer * General invalid XML message. */ #define PJLIB_UTIL_EINXML (PJLIB_UTIL_ERRNO_START+20) /* 320020 */ /************************************************************ * JSON ERROR ***********************************************************/ /** * @hideinitializer * General invalid JSON message. */ #define PJLIB_UTIL_EINJSON (PJLIB_UTIL_ERRNO_START+30) /* 320030 */ /************************************************************ * DNS ERROR ***********************************************************/ /** * @hideinitializer * DNS query packet buffer is too small. * This error occurs when the user supplied buffer for creating DNS * query (#pj_dns_make_query() function) is too small. */ #define PJLIB_UTIL_EDNSQRYTOOSMALL (PJLIB_UTIL_ERRNO_START+40) /* 320040 */ /** * @hideinitializer * Invalid DNS packet length. * This error occurs when the received DNS response packet does not * match all the fields length. */ #define PJLIB_UTIL_EDNSINSIZE (PJLIB_UTIL_ERRNO_START+41) /* 320041 */ /** * @hideinitializer * Invalid DNS class. * This error occurs when the received DNS response contains network * class other than IN (Internet). */ #define PJLIB_UTIL_EDNSINCLASS (PJLIB_UTIL_ERRNO_START+42) /* 320042 */ /** * @hideinitializer * Invalid DNS name pointer. * This error occurs when parsing the compressed names inside DNS * response packet, when the name pointer points to an invalid address * or the parsing has triggerred too much recursion. */ #define PJLIB_UTIL_EDNSINNAMEPTR (PJLIB_UTIL_ERRNO_START+43) /* 320043 */ /** * @hideinitializer * Invalid DNS nameserver address. If hostname was specified for nameserver * address, this error means that the function was unable to resolve * the nameserver hostname. */ #define PJLIB_UTIL_EDNSINNSADDR (PJLIB_UTIL_ERRNO_START+44) /* 320044 */ /** * @hideinitializer * No nameserver is in DNS resolver. No nameserver is configured in the * resolver. */ #define PJLIB_UTIL_EDNSNONS (PJLIB_UTIL_ERRNO_START+45) /* 320045 */ /** * @hideinitializer * No working DNS nameserver. All nameservers have been queried, * but none was able to serve any DNS requests. These "bad" nameservers * will be re-tested again for "goodness" after some period. */ #define PJLIB_UTIL_EDNSNOWORKINGNS (PJLIB_UTIL_ERRNO_START+46) /* 320046 */ /** * @hideinitializer * No answer record in the DNS response. */ #define PJLIB_UTIL_EDNSNOANSWERREC (PJLIB_UTIL_ERRNO_START+47) /* 320047 */ /** * @hideinitializer * Invalid DNS answer. This error is raised for example when the DNS * answer does not have a query section, or the type of RR in the answer * doesn't match the query. */ #define PJLIB_UTIL_EDNSINANSWER (PJLIB_UTIL_ERRNO_START+48) /* 320048 */ /* DNS ERRORS MAPPED FROM RCODE: */ /** * Start of error code mapped from DNS RCODE */ #define PJLIB_UTIL_DNS_RCODE_START (PJLIB_UTIL_ERRNO_START+50) /* 320050 */ /** * Map DNS RCODE status into pj_status_t. */ #define PJ_STATUS_FROM_DNS_RCODE(rcode) (rcode==0 ? PJ_SUCCESS : \ PJLIB_UTIL_DNS_RCODE_START+rcode) /** * @hideinitializer * Format error - The name server was unable to interpret the query. * This corresponds to DNS RCODE 1. */ #define PJLIB_UTIL_EDNS_FORMERR PJ_STATUS_FROM_DNS_RCODE(1) /* 320051 */ /** * @hideinitializer * Server failure - The name server was unable to process this query due to a * problem with the name server. * This corresponds to DNS RCODE 2. */ #define PJLIB_UTIL_EDNS_SERVFAIL PJ_STATUS_FROM_DNS_RCODE(2) /* 320052 */ /** * @hideinitializer * Name Error - Meaningful only for responses from an authoritative name * server, this code signifies that the domain name referenced in the query * does not exist. * This corresponds to DNS RCODE 3. */ #define PJLIB_UTIL_EDNS_NXDOMAIN PJ_STATUS_FROM_DNS_RCODE(3) /* 320053 */ /** * @hideinitializer * Not Implemented - The name server does not support the requested kind of * query. * This corresponds to DNS RCODE 4. */ #define PJLIB_UTIL_EDNS_NOTIMPL PJ_STATUS_FROM_DNS_RCODE(4) /* 320054 */ /** * @hideinitializer * Refused - The name server refuses to perform the specified operation for * policy reasons. * This corresponds to DNS RCODE 5. */ #define PJLIB_UTIL_EDNS_REFUSED PJ_STATUS_FROM_DNS_RCODE(5) /* 320055 */ /** * @hideinitializer * The name exists. * This corresponds to DNS RCODE 6. */ #define PJLIB_UTIL_EDNS_YXDOMAIN PJ_STATUS_FROM_DNS_RCODE(6) /* 320056 */ /** * @hideinitializer * The RRset (name, type) exists. * This corresponds to DNS RCODE 7. */ #define PJLIB_UTIL_EDNS_YXRRSET PJ_STATUS_FROM_DNS_RCODE(7) /* 320057 */ /** * @hideinitializer * The RRset (name, type) does not exist. * This corresponds to DNS RCODE 8. */ #define PJLIB_UTIL_EDNS_NXRRSET PJ_STATUS_FROM_DNS_RCODE(8) /* 320058 */ /** * @hideinitializer * The requestor is not authorized to perform this operation. * This corresponds to DNS RCODE 9. */ #define PJLIB_UTIL_EDNS_NOTAUTH PJ_STATUS_FROM_DNS_RCODE(9) /* 320059 */ /** * @hideinitializer * The zone specified is not a zone. * This corresponds to DNS RCODE 10. */ #define PJLIB_UTIL_EDNS_NOTZONE PJ_STATUS_FROM_DNS_RCODE(10)/* 320060 */ /************************************************************ * NEW STUN ERROR ***********************************************************/ /* Messaging errors */ /** * @hideinitializer * Too many STUN attributes. */ #define PJLIB_UTIL_ESTUNTOOMANYATTR (PJLIB_UTIL_ERRNO_START+110)/* 320110 */ /** * @hideinitializer * Unknown STUN attribute. This error happens when the decoder encounters * mandatory attribute type which it doesn't understand. */ #define PJLIB_UTIL_ESTUNUNKNOWNATTR (PJLIB_UTIL_ERRNO_START+111)/* 320111 */ /** * @hideinitializer * Invalid STUN socket address length. */ #define PJLIB_UTIL_ESTUNINADDRLEN (PJLIB_UTIL_ERRNO_START+112)/* 320112 */ /** * @hideinitializer * STUN IPv6 attribute not supported */ #define PJLIB_UTIL_ESTUNIPV6NOTSUPP (PJLIB_UTIL_ERRNO_START+113)/* 320113 */ /** * @hideinitializer * Expecting STUN response message. */ #define PJLIB_UTIL_ESTUNNOTRESPONSE (PJLIB_UTIL_ERRNO_START+114)/* 320114 */ /** * @hideinitializer * STUN transaction ID mismatch. */ #define PJLIB_UTIL_ESTUNINVALIDID (PJLIB_UTIL_ERRNO_START+115)/* 320115 */ /** * @hideinitializer * Unable to find handler for the request. */ #define PJLIB_UTIL_ESTUNNOHANDLER (PJLIB_UTIL_ERRNO_START+116)/* 320116 */ /** * @hideinitializer * Found non-FINGERPRINT attribute after MESSAGE-INTEGRITY. This is not * valid since MESSAGE-INTEGRITY MUST be the last attribute or the * attribute right before FINGERPRINT before the message. */ #define PJLIB_UTIL_ESTUNMSGINTPOS (PJLIB_UTIL_ERRNO_START+118)/* 320118 */ /** * @hideinitializer * Found attribute after FINGERPRINT. This is not valid since FINGERPRINT * MUST be the last attribute in the message. */ #define PJLIB_UTIL_ESTUNFINGERPOS (PJLIB_UTIL_ERRNO_START+119)/* 320119 */ /** * @hideinitializer * Missing STUN USERNAME attribute. * When credential is included in the STUN message (MESSAGE-INTEGRITY is * present), the USERNAME attribute must be present in the message. */ #define PJLIB_UTIL_ESTUNNOUSERNAME (PJLIB_UTIL_ERRNO_START+120)/* 320120 */ /** * @hideinitializer * Unknown STUN username/credential. */ #define PJLIB_UTIL_ESTUNUSERNAME (PJLIB_UTIL_ERRNO_START+121)/* 320121 */ /** * @hideinitializer * Missing/invalidSTUN MESSAGE-INTEGRITY attribute. */ #define PJLIB_UTIL_ESTUNMSGINT (PJLIB_UTIL_ERRNO_START+122)/* 320122 */ /** * @hideinitializer * Found duplicate STUN attribute. */ #define PJLIB_UTIL_ESTUNDUPATTR (PJLIB_UTIL_ERRNO_START+123)/* 320123 */ /** * @hideinitializer * Missing STUN REALM attribute. */ #define PJLIB_UTIL_ESTUNNOREALM (PJLIB_UTIL_ERRNO_START+124)/* 320124 */ /** * @hideinitializer * Missing/stale STUN NONCE attribute value. */ #define PJLIB_UTIL_ESTUNNONCE (PJLIB_UTIL_ERRNO_START+125)/* 320125 */ /** * @hideinitializer * STUN transaction terminates with failure. */ #define PJLIB_UTIL_ESTUNTSXFAILED (PJLIB_UTIL_ERRNO_START+126)/* 320126 */ //#define PJ_STATUS_FROM_STUN_CODE(code) (PJLIB_UTIL_ERRNO_START+code) /************************************************************ * HTTP Client ERROR ***********************************************************/ /** * @hideinitializer * Invalid URL format */ #define PJLIB_UTIL_EHTTPINURL (PJLIB_UTIL_ERRNO_START+151)/* 320151 */ /** * @hideinitializer * Invalid port number */ #define PJLIB_UTIL_EHTTPINPORT (PJLIB_UTIL_ERRNO_START+152)/* 320152 */ /** * @hideinitializer * Incomplete headers received */ #define PJLIB_UTIL_EHTTPINCHDR (PJLIB_UTIL_ERRNO_START+153)/* 320153 */ /** * @hideinitializer * Insufficient buffer */ #define PJLIB_UTIL_EHTTPINSBUF (PJLIB_UTIL_ERRNO_START+154)/* 320154 */ /** * @hideinitializer * Connection lost */ #define PJLIB_UTIL_EHTTPLOST (PJLIB_UTIL_ERRNO_START+155)/* 320155 */ /************************************************************ * CLI ERROR ***********************************************************/ /** * @hideinitializer * End the current session. This is a special error code returned by * pj_cli_sess_exec() to indicate that "exit" or equivalent command has been * called to end the current session. */ #define PJ_CLI_EEXIT (PJLIB_UTIL_ERRNO_START+201)/* 320201 */ /** * @hideinitializer * A required CLI argument is not specified. */ #define PJ_CLI_EMISSINGARG (PJLIB_UTIL_ERRNO_START+202)/* 320202 */ /** * @hideinitializer * Too many CLI arguments. */ #define PJ_CLI_ETOOMANYARGS (PJLIB_UTIL_ERRNO_START+203)/* 320203 */ /** * @hideinitializer * Invalid CLI argument. Typically this is caused by extra characters * specified in the command line which does not match any arguments. */ #define PJ_CLI_EINVARG (PJLIB_UTIL_ERRNO_START+204)/* 320204 */ /** * @hideinitializer * CLI command with the specified name already exist. */ #define PJ_CLI_EBADNAME (PJLIB_UTIL_ERRNO_START+205)/* 320205 */ /** * @hideinitializer * CLI command with the specified id already exist. */ #define PJ_CLI_EBADID (PJLIB_UTIL_ERRNO_START+206)/* 320206 */ /** * @hideinitializer * Invalid XML format for CLI command specification. */ #define PJ_CLI_EBADXML (PJLIB_UTIL_ERRNO_START+207)/* 320207 */ /** * @hideinitializer * CLI command entered by user match with more than one command/argument * specification. */ #define PJ_CLI_EAMBIGUOUS (PJLIB_UTIL_ERRNO_START+208)/* 320208 */ /** * @hideinitializer * Telnet connection lost. */ #define PJ_CLI_ETELNETLOST (PJLIB_UTIL_ERRNO_START+211)/* 320211 */ /** * @} */ #endif /* __PJLIB_UTIL_ERRNO_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/getopt.h ================================================ /* $Id: getopt.h 2037 2008-06-20 21:39:02Z bennylp $ */ /* Declarations for pj_getopt. Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __PJ_GETOPT_H__ #define __PJ_GETOPT_H__ 1 /** * @file getopt.h * @brief Compile time settings */ /** * @defgroup PJLIB_UTIL_GETOPT Getopt * @ingroup PJLIB_TEXT * @{ */ #ifdef __cplusplus extern "C" { #endif /* For communication from `pj_getopt' to the caller. When `pj_getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *pj_optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `pj_getopt'. On entry to `pj_getopt', zero means this is the first call; initialize. When `pj_getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `pj_optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int pj_optind; /* Set to an option character which was unrecognized. */ extern int pj_optopt; /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to pj_getopt_long or pj_getopt_long_only is a vector of `struct pj_getopt_option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `pj_optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `pj_getopt' returns the contents of the `val' field. */ struct pj_getopt_option { const char *name; /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct pj_getopt_option'. */ # define no_argument 0 # define required_argument 1 # define optional_argument 2 /* Get definitions and prototypes for functions to process the arguments in ARGV (ARGC of them, minus the program name) for options given in OPTS. Return the option character from OPTS just read. Return -1 when there are no more options. For unrecognized options, or options missing arguments, `pj_optopt' is set to the option letter, and '?' is returned. The OPTS string is a list of characters which are recognized option letters, optionally followed by colons, specifying that that letter takes an argument, to be placed in `pj_optarg'. If a letter in OPTS is followed by two colons, its argument is optional. This behavior is specific to the GNU `pj_getopt'. The argument `--' causes premature termination of argument scanning, explicitly telling `pj_getopt' that there are no more options. If OPTS begins with `--', then non-option arguments are treated as arguments to the option '\0'. This behavior is specific to the GNU `pj_getopt'. */ int pj_getopt (int argc, char *const *argv, const char *shortopts); int pj_getopt_long (int argc, char *const *argv, const char *options, const struct pj_getopt_option *longopts, int *longind); int pj_getopt_long_only (int argc, char *const *argv, const char *shortopts, const struct pj_getopt_option *longopts, int *longind); #ifdef __cplusplus } #endif /** * @} */ #endif /* pj_getopt.h */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/hmac_md5.h ================================================ /* $Id: hmac_md5.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_HMAC_MD5_H__ #define __PJLIB_UTIL_HMAC_MD5_H__ /** * @file hmac_md5.h * @brief HMAC MD5 Message Authentication */ /** * @defgroup PJLIB_UTIL_ENCRYPTION Encryption Algorithms */ #include #include PJ_BEGIN_DECL /** * @defgroup PJLIB_UTIL_HMAC_MD5 HMAC MD5 Message Authentication * @ingroup PJLIB_UTIL_ENCRYPTION * @{ * * This module contains the implementation of HMAC: Keyed-Hashing * for Message Authentication, as described in RFC 2104 */ /** * The HMAC-MD5 context used in the incremental HMAC calculation. */ typedef struct pj_hmac_md5_context { pj_md5_context context; /**< MD5 context */ pj_uint8_t k_opad[64]; /**< opad xor-ed with key */ } pj_hmac_md5_context; /** * Calculate HMAC MD5 digest for the specified input and key. * * @param input Pointer to the input stream. * @param input_len Length of input stream in bytes. * @param key Pointer to the authentication key. * @param key_len Length of the authentication key. * @param digest Buffer to be filled with HMAC MD5 digest. */ PJ_DECL(void) pj_hmac_md5(const pj_uint8_t *input, unsigned input_len, const pj_uint8_t *key, unsigned key_len, pj_uint8_t digest[16]); /** * Initiate HMAC-MD5 context for incremental hashing. * * @param hctx HMAC-MD5 context. * @param key Pointer to the authentication key. * @param key_len Length of the authentication key. */ PJ_DECL(void) pj_hmac_md5_init(pj_hmac_md5_context *hctx, const pj_uint8_t *key, unsigned key_len); /** * Append string to the message. * * @param hctx HMAC-MD5 context. * @param input Pointer to the input stream. * @param input_len Length of input stream in bytes. */ PJ_DECL(void) pj_hmac_md5_update(pj_hmac_md5_context *hctx, const pj_uint8_t *input, unsigned input_len); /** * Finish the message and return the digest. * * @param hctx HMAC-MD5 context. * @param digest Buffer to be filled with HMAC MD5 digest. */ PJ_DECL(void) pj_hmac_md5_final(pj_hmac_md5_context *hctx, pj_uint8_t digest[16]); /** * @} */ PJ_END_DECL #endif /* __PJLIB_UTIL_HMAC_MD5_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/hmac_sha1.h ================================================ /* $Id: hmac_sha1.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_HMAC_SHA1_H__ #define __PJLIB_UTIL_HMAC_SHA1_H__ /** * @file hmac_sha1.h * @brief HMAC SHA1 Message Authentication */ #include #include PJ_BEGIN_DECL /** * @defgroup PJLIB_UTIL_HMAC_SHA1 HMAC SHA1 Message Authentication * @ingroup PJLIB_UTIL_ENCRYPTION * @{ * * This module contains the implementation of HMAC: Keyed-Hashing * for Message Authentication, as described in RFC 2104. */ /** * The HMAC-SHA1 context used in the incremental HMAC calculation. */ typedef struct pj_hmac_sha1_context { pj_sha1_context context; /**< SHA1 context */ pj_uint8_t k_opad[64]; /**< opad xor-ed with key */ } pj_hmac_sha1_context; /** * Calculate HMAC-SHA1 digest for the specified input and key with this * single function call. * * @param input Pointer to the input stream. * @param input_len Length of input stream in bytes. * @param key Pointer to the authentication key. * @param key_len Length of the authentication key. * @param digest Buffer to be filled with HMAC SHA1 digest. */ PJ_DECL(void) pj_hmac_sha1(const pj_uint8_t *input, unsigned input_len, const pj_uint8_t *key, unsigned key_len, pj_uint8_t digest[20]); /** * Initiate HMAC-SHA1 context for incremental hashing. * * @param hctx HMAC-SHA1 context. * @param key Pointer to the authentication key. * @param key_len Length of the authentication key. */ PJ_DECL(void) pj_hmac_sha1_init(pj_hmac_sha1_context *hctx, const pj_uint8_t *key, unsigned key_len); /** * Append string to the message. * * @param hctx HMAC-SHA1 context. * @param input Pointer to the input stream. * @param input_len Length of input stream in bytes. */ PJ_DECL(void) pj_hmac_sha1_update(pj_hmac_sha1_context *hctx, const pj_uint8_t *input, unsigned input_len); /** * Finish the message and return the digest. * * @param hctx HMAC-SHA1 context. * @param digest Buffer to be filled with HMAC SHA1 digest. */ PJ_DECL(void) pj_hmac_sha1_final(pj_hmac_sha1_context *hctx, pj_uint8_t digest[20]); /** * @} */ PJ_END_DECL #endif /* __PJLIB_UTIL_HMAC_SHA1_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/http_client.h ================================================ /* $Id: http_client.h 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_HTTP_CLIENT_H__ #define __PJLIB_UTIL_HTTP_CLIENT_H__ /** * @file http_client.h * @brief Simple HTTP Client */ #include #include PJ_BEGIN_DECL /** * @defgroup PJ_HTTP_CLIENT Simple HTTP Client * @ingroup PJ_PROTOCOLS * @{ * This contains a simple HTTP client implementation. * Some known limitations: * - Does not support chunked Transfer-Encoding. */ /** * This opaque structure describes the http request. */ typedef struct pj_http_req pj_http_req; /** * Defines the maximum number of elements in a pj_http_headers * structure. */ #define PJ_HTTP_HEADER_SIZE 32 /** * HTTP header representation. */ typedef struct pj_http_header_elmt { pj_str_t name; /**< Header name */ pj_str_t value; /**< Header value */ } pj_http_header_elmt; /** * This structure describes http request/response headers. * Application should call #pj_http_headers_add_elmt() to * add a header field. */ typedef struct pj_http_headers { /**< Number of header fields */ unsigned count; /** Header elements/fields */ pj_http_header_elmt header[PJ_HTTP_HEADER_SIZE]; } pj_http_headers; /** * Structure to save HTTP authentication credential. */ typedef struct pj_http_auth_cred { /** * Specify specific authentication schemes to be responded. Valid values * are "basic" and "digest". If this field is not set, any authentication * schemes will be responded. * * Default is empty. */ pj_str_t scheme; /** * Specify specific authentication realm to be responded. If this field * is set, only 401/407 response with matching realm will be responded. * If this field is not set, any realms will be responded. * * Default is empty. */ pj_str_t realm; /** * Specify authentication username. * * Default is empty. */ pj_str_t username; /** * The type of password in \a data field. Currently only 0 is * supported, meaning the \a data contains plain-text password. * * Default is 0. */ unsigned data_type; /** * Specify authentication password. The encoding of the password depends * on the value of \a data_type field above. * * Default is empty. */ pj_str_t data; } pj_http_auth_cred; /** * Parameters that can be given during http request creation. Application * must initialize this structure with #pj_http_req_param_default(). */ typedef struct pj_http_req_param { /** * The address family of the URL. * Default is pj_AF_INET(). */ int addr_family; /** * The HTTP request method. * Default is GET. */ pj_str_t method; /** * The HTTP protocol version ("1.0" or "1.1"). * Default is "1.0". */ pj_str_t version; /** * HTTP request operation timeout. * Default is PJ_HTTP_DEFAULT_TIMEOUT. */ pj_time_val timeout; /** * User-defined data. * Default is NULL. */ void *user_data; /** * HTTP request headers. * Default is empty. */ pj_http_headers headers; /** * This structure describes the http request body. If application * specifies the data to send, the data must remain valid until * the HTTP request is sent. Alternatively, application can choose * to specify total_size as the total data size to send instead * while leaving the data NULL (and its size 0). In this case, * HTTP request will then call on_send_data() callback once it is * ready to send the request body. This will be useful if * application does not wish to load the data into the buffer at * once. * * Default is empty. */ struct pj_http_reqdata { void *data; /**< Request body data */ pj_size_t size; /**< Request body size */ pj_size_t total_size; /**< If total_size > 0, data */ /**< will be provided later */ } reqdata; /** * Authentication credential needed to respond to 401/407 response. */ pj_http_auth_cred auth_cred; /** * Optional source port range to use when binding the socket. * This can be used if the source port needs to be within a certain range * for instance due to strict firewall settings. The port used will be * randomized within the range. * * Note that if authentication is configured, the authentication response * will be a new transaction * * Default is 0 (The OS will select the source port automatically) */ pj_uint16_t source_port_range_start; /** * Optional source port range to use when binding. * The size of the port restriction range * * Default is 0 (The OS will select the source port automatically)) */ pj_uint16_t source_port_range_size; /** * Max number of retries if binding to a port fails. * Note that this does not adress the scenario where a request times out * or errors. This needs to be taken care of by the on_complete callback. * * Default is 3 */ pj_uint16_t max_retries; } pj_http_req_param; /** * HTTP authentication challenge, parsed from WWW-Authenticate header. */ typedef struct pj_http_auth_chal { pj_str_t scheme; /**< Auth scheme. */ pj_str_t realm; /**< Realm for the challenge. */ pj_str_t domain; /**< Domain. */ pj_str_t nonce; /**< Nonce challenge. */ pj_str_t opaque; /**< Opaque value. */ int stale; /**< Stale parameter. */ pj_str_t algorithm; /**< Algorithm parameter. */ pj_str_t qop; /**< Quality of protection. */ } pj_http_auth_chal; /** * This structure describes HTTP response. */ typedef struct pj_http_resp { pj_str_t version; /**< HTTP version of the server */ pj_uint16_t status_code; /**< Status code of the request */ pj_str_t reason; /**< Reason phrase */ pj_http_headers headers; /**< Response headers */ pj_http_auth_chal auth_chal; /**< Parsed WWW-Authenticate header, if any. */ pj_int32_t content_length; /**< The value of content-length header field. -1 if not specified. */ void *data; /**< Data received */ pj_size_t size; /**< Data size */ } pj_http_resp; /** * This structure describes HTTP URL. */ typedef struct pj_http_url { pj_str_t username; /**< Username part */ pj_str_t passwd; /**< Password part */ pj_str_t protocol; /**< Protocol used */ pj_str_t host; /**< Host name */ pj_uint16_t port; /**< Port number */ pj_str_t path; /**< Path */ } pj_http_url; /** * This structure describes the callbacks to be called by the HTTP request. */ typedef struct pj_http_req_callback { /** * This callback is called when a complete HTTP response header * is received. * * @param http_req The http request. * @param resp The response of the request. */ void (*on_response)(pj_http_req *http_req, const pj_http_resp *resp); /** * This callback is called when the HTTP request is ready to send * its request body. Application may wish to use this callback if * it wishes to load the data at a later time or if it does not * wish to load the whole data into memory. In order for this * callback to be called, application MUST set http_req_param.total_size * to a value greater than 0. * * @param http_req The http request. * @param data Pointer to the data that will be sent. Application * must set the pointer to the current data chunk/segment * to be sent. Data must remain valid until the next * on_send_data() callback or for the last segment, * until it is sent. * @param size Pointer to the data size that will be sent. */ void (*on_send_data)(pj_http_req *http_req, void **data, pj_size_t *size); /** * This callback is called when a segment of response body data * arrives. If this callback is specified (i.e. not NULL), the * on_complete() callback will be called with zero-length data * (within the response parameter), hence the application must * store and manage its own data buffer, otherwise the * on_complete() callback will be called with the response * parameter containing the complete data. * * @param http_req The http request. * @param data The buffer containing the data. * @param size The length of data in the buffer. */ void (*on_data_read)(pj_http_req *http_req, void *data, pj_size_t size); /** * This callback is called when the HTTP request is completed. * If the callback on_data_read() is specified, the variable * response->data will be set to NULL, otherwise it will * contain the complete data. Response data is allocated from * pj_http_req's internal memory pool so the data remain valid * as long as pj_http_req is not destroyed and application does * not start a new request. * * If no longer required, application may choose to destroy * pj_http_req immediately by calling #pj_http_req_destroy() inside * the callback. * * @param http_req The http request. * @param status The status of the request operation. PJ_SUCCESS * if the operation completed successfully * (connection-wise). To check the server's * status-code response to the HTTP request, * application should check resp->status_code instead. * @param resp The response of the corresponding request. If * the status argument is non-PJ_SUCCESS, this * argument will be set to NULL. */ void (*on_complete)(pj_http_req *http_req, pj_status_t status, const pj_http_resp *resp); } pj_http_req_callback; /** * Initialize the http request parameters with the default values. * * @param param The parameter to be initialized. */ PJ_DECL(void) pj_http_req_param_default(pj_http_req_param *param); /** * Add a header element/field. Application MUST make sure that * name and val pointer remains valid until the HTTP request is sent. * * @param headers The headers. * @param name The header field name. * @param value The header field value. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_http_headers_add_elmt(pj_http_headers *headers, pj_str_t *name, pj_str_t *val); /** * The same as #pj_http_headers_add_elmt() with char * as * its parameters. Application MUST make sure that name and val pointer * remains valid until the HTTP request is sent. * * @param headers The headers. * @param name The header field name. * @param value The header field value. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_http_headers_add_elmt2(pj_http_headers *headers, char *name, char *val); /** * Parse a http URL into its components. * * @param url The URL to be parsed. * @param hurl Pointer to receive the parsed result. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_http_req_parse_url(const pj_str_t *url, pj_http_url *hurl); /** * Create the HTTP request. * * @param pool Pool to use. HTTP request will use the pool's factory * to allocate its own memory pool. * @param url HTTP URL request. * @param timer The timer to use. * @param ioqueue The ioqueue to use. * @param param Optional parameters. When this parameter is not * specifed (NULL), the default values will be used. * @param hcb Pointer to structure containing application * callbacks. * @param http_req Pointer to receive the http request instance. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_http_req_create(pj_pool_t *pool, const pj_str_t *url, pj_timer_heap_t *timer, pj_ioqueue_t *ioqueue, const pj_http_req_param *param, const pj_http_req_callback *hcb, pj_http_req **http_req); /** * Set the timeout of the HTTP request operation. Note that if the * HTTP request is currently running, the timeout will only affect * subsequent request operations. * * @param http_req The http request. * @param timeout Timeout value for HTTP request operation. */ PJ_DECL(void) pj_http_req_set_timeout(pj_http_req *http_req, const pj_time_val* timeout); /** * Starts an asynchronous HTTP request to the URL specified. * * @param http_req The http request. * * @return * - PJ_SUCCESS if success * - non-zero which indicates the appropriate error code. */ PJ_DECL(pj_status_t) pj_http_req_start(pj_http_req *http_req); /** * Cancel the asynchronous HTTP request. * * @param http_req The http request. * @param notify If non-zero, the on_complete() callback will be * called with status PJ_ECANCELLED to notify that * the query has been cancelled. * * @return * - PJ_SUCCESS if success * - non-zero which indicates the appropriate error code. */ PJ_DECL(pj_status_t) pj_http_req_cancel(pj_http_req *http_req, pj_bool_t notify); /** * Destroy the http request. * * @param http_req The http request to be destroyed. * * @return PJ_SUCCESS if success. */ PJ_DECL(pj_status_t) pj_http_req_destroy(pj_http_req *http_req); /** * Find out whether the http request is running. * * @param http_req The http request. * * @return PJ_TRUE if a request is pending, or * PJ_FALSE if idle */ PJ_DECL(pj_bool_t) pj_http_req_is_running(const pj_http_req *http_req); /** * Retrieve the user data previously associated with this http * request. * * @param http_req The http request. * * @return The user data. */ PJ_DECL(void *) pj_http_req_get_user_data(pj_http_req *http_req); /** * @} */ PJ_END_DECL #endif /* __PJLIB_UTIL_HTTP_CLIENT_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/json.h ================================================ /* $Id$ */ /* * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_JSON_H__ #define __PJLIB_UTIL_JSON_H__ /** * @file json.h * @brief PJLIB JSON Implementation */ #include #include #include PJ_BEGIN_DECL /** * @defgroup PJ_JSON JSON Writer and Loader * @ingroup PJ_FILE_FMT * @{ * This API implements JSON file format according to RFC 4627. It can be used * to parse, write, and manipulate JSON documents. */ /** * Type of JSON value. */ typedef enum pj_json_val_type { PJ_JSON_VAL_NULL, /**< Null value (null) */ PJ_JSON_VAL_BOOL, /**< Boolean value (true, false) */ PJ_JSON_VAL_NUMBER, /**< Numeric (float or fixed point) */ PJ_JSON_VAL_STRING, /**< Literal string value. */ PJ_JSON_VAL_ARRAY, /**< Array */ PJ_JSON_VAL_OBJ /**< Object. */ } pj_json_val_type; /* Forward declaration for JSON element */ typedef struct pj_json_elem pj_json_elem; /** * JSON list to store child elements. */ typedef struct pj_json_list { PJ_DECL_LIST_MEMBER(pj_json_elem); } pj_json_list; /** * This represents JSON element. A JSON element is basically a name/value * pair, where the name is a string and the value can be one of null, boolean * (true and false constants), number, string, array (containing zero or more * elements), or object. An object can be viewed as C struct, that is a * compound element containing other elements, each having name/value pair. */ struct pj_json_elem { PJ_DECL_LIST_MEMBER(pj_json_elem); pj_str_t name; /**< ELement name. */ pj_json_val_type type; /**< Element type. */ union { pj_bool_t is_true; /**< Boolean value. */ float num; /**< Number value. */ pj_str_t str; /**< String value. */ pj_json_list children; /**< Object and array children */ } value; /**< Element value. */ }; /** * Structure to be specified to pj_json_parse() to be filled with additional * info when parsing failed. */ typedef struct pj_json_err_info { unsigned line; /**< Line location of the error */ unsigned col; /**< Column location of the error */ int err_char; /**< The offending character. */ } pj_json_err_info; /** * Type of function callback to write JSON document in pj_json_writef(). * * @param s The string to be written to the document. * @param size The length of the string * @param user_data User data that was specified to pj_json_writef() * * @return If the callback returns non-PJ_SUCCESS, it will * stop the pj_json_writef() function and this error * will be returned to caller. */ typedef pj_status_t (*pj_json_writer)(const char *s, unsigned size, void *user_data); /** * Initialize null element. * * @param el The element. * @param name Name to be given to the element, or NULL. */ PJ_DECL(void) pj_json_elem_null(pj_json_elem *el, pj_str_t *name); /** * Initialize boolean element with the specified value. * * @param el The element. * @param name Name to be given to the element, or NULL. * @param val The value. */ PJ_DECL(void) pj_json_elem_bool(pj_json_elem *el, pj_str_t *name, pj_bool_t val); /** * Initialize number element with the specified value. * * @param el The element. * @param name Name to be given to the element, or NULL. * @param val The value. */ PJ_DECL(void) pj_json_elem_number(pj_json_elem *el, pj_str_t *name, float val); /** * Initialize string element with the specified value. * * @param el The element. * @param name Name to be given to the element, or NULL. * @param val The value. */ PJ_DECL(void) pj_json_elem_string(pj_json_elem *el, pj_str_t *name, pj_str_t *val); /** * Initialize element as an empty array * * @param el The element. * @param name Name to be given to the element, or NULL. */ PJ_DECL(void) pj_json_elem_array(pj_json_elem *el, pj_str_t *name); /** * Initialize element as an empty object * * @param el The element. * @param name Name to be given to the element, or NULL. */ PJ_DECL(void) pj_json_elem_obj(pj_json_elem *el, pj_str_t *name); /** * Add an element to an object or array. * * @param el The object or array element. * @param child Element to be added to the object or array. */ PJ_DECL(void) pj_json_elem_add(pj_json_elem *el, pj_json_elem *child); /** * Parse a JSON document in the buffer. The buffer MUST be NULL terminated, * or if not then it must have enough size to put the NULL character. * * @param pool The pool to allocate memory for creating elements. * @param buffer String buffer containing JSON document. * @param size Size of the document. * @param err_info Optional structure to be filled with info when * parsing failed. * * @return The root element from the document. */ PJ_DECL(pj_json_elem*) pj_json_parse(pj_pool_t *pool, char *buffer, unsigned *size, pj_json_err_info *err_info); /** * Write the specified element to the string buffer. * * @param elem The element to be written. * @param buffer Output buffer. * @param size On input, it must be set to the size of the buffer. * Upon successful return, this will be set to * the length of the written string. * * @return PJ_SUCCESS on success or the appropriate error. */ PJ_DECL(pj_status_t) pj_json_write(const pj_json_elem *elem, char *buffer, unsigned *size); /** * Incrementally write the element to arbitrary medium using the specified * callback to write the document chunks. * * @param elem The element to be written. * @param writer Callback function which will be called to write * text chunks. * @param user_data Arbitrary user data which will be given back when * calling the callback. * * @return PJ_SUCCESS on success or the appropriate error. */ PJ_DECL(pj_status_t) pj_json_writef(const pj_json_elem *elem, pj_json_writer writer, void *user_data); /** * @} */ PJ_END_DECL #endif /* __PJLIB_UTIL_JSON_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/md5.h ================================================ /* $Id: md5.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_MD5_H__ #define __PJLIB_UTIL_MD5_H__ /** * @file md5.h * @brief MD5 Functions */ #include PJ_BEGIN_DECL /** * @defgroup PJLIB_UTIL_MD5 MD5 * @ingroup PJLIB_UTIL_ENCRYPTION * @{ */ /** MD5 context. */ typedef struct pj_md5_context { pj_uint32_t buf[4]; /**< buf */ pj_uint32_t bits[2]; /**< bits */ pj_uint8_t in[64]; /**< in */ } pj_md5_context; /** Initialize the algorithm. * @param pms MD5 context. */ PJ_DECL(void) pj_md5_init(pj_md5_context *pms); /** Append a string to the message. * @param pms MD5 context. * @param data Data. * @param nbytes Length of data. */ PJ_DECL(void) pj_md5_update( pj_md5_context *pms, const pj_uint8_t *data, unsigned nbytes); /** Finish the message and return the digest. * @param pms MD5 context. * @param digest 16 byte digest. */ PJ_DECL(void) pj_md5_final(pj_md5_context *pms, pj_uint8_t digest[16]); /** * @} */ PJ_END_DECL #endif /* __PJLIB_UTIL_MD5_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/pcap.h ================================================ /* $Id: pcap.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_PCAP_H__ #define __PJLIB_UTIL_PCAP_H__ /** * @file pcap.h * @brief Simple PCAP file reader */ #include PJ_BEGIN_DECL /** * @defgroup PJ_PCAP Simple PCAP file reader * @ingroup PJ_FILE_FMT * @{ * This module describes simple utility to read PCAP file. It is not intended * to support all PCAP features (that's what libpcap is for!), but it can * be useful for example to playback or stream PCAP contents. */ /** * Enumeration to describe supported data link types. */ typedef enum pj_pcap_link_type { /** Ethernet data link */ PJ_PCAP_LINK_TYPE_ETH = 1 } pj_pcap_link_type; /** * Enumeration to describe supported protocol types. */ typedef enum pj_pcap_proto_type { /** UDP protocol */ PJ_PCAP_PROTO_TYPE_UDP = 17 } pj_pcap_proto_type; /** * This describes UDP header, which may optionally be returned in * #pj_pcap_read_udp() function. All fields are in network byte order. */ typedef struct pj_pcap_udp_hdr { pj_uint16_t src_port; /**< Source port. */ pj_uint16_t dst_port; /**< Destination port */ pj_uint16_t len; /**< Length. */ pj_uint16_t csum; /**< Checksum. */ } pj_pcap_udp_hdr; /** * This structure describes the filter to be used when reading packets from * a PCAP file. When a filter is configured, only packets matching all the * filter specifications will be read from PCAP file. */ typedef struct pj_pcap_filter { /** * Select data link type, or zero to include any supported data links. */ pj_pcap_link_type link; /** * Select protocol, or zero to include all supported protocols. */ pj_pcap_proto_type proto; /** * Specify source IP address of the packets, or zero to include packets * from any IP addresses. Note that IP address here must be in * network byte order. */ pj_uint32_t ip_src; /** * Specify destination IP address of the packets, or zero to include packets * destined to any IP addresses. Note that IP address here must be in * network byte order. */ pj_uint32_t ip_dst; /** * Specify source port of the packets, or zero to include packets with * any source port number. Note that the port number must be in network * byte order. */ pj_uint16_t src_port; /** * Specify destination port of the packets, or zero to include packets with * any destination port number. Note that the port number must be in network * byte order. */ pj_uint16_t dst_port; } pj_pcap_filter; /** Opaque declaration for PCAP file */ typedef struct pj_pcap_file pj_pcap_file; /** * Initialize filter with default values. The default value is to allow * any packets. * * @param filter Filter to be initialized. */ PJ_DECL(void) pj_pcap_filter_default(pj_pcap_filter *filter); /** * Open PCAP file. * * @param pool Pool to allocate memory. * @param path File/path name. * @param p_file Pointer to receive PCAP file handle. * * @return PJ_SUCCESS if file can be opened successfully. */ PJ_DECL(pj_status_t) pj_pcap_open(pj_pool_t *pool, const char *path, pj_pcap_file **p_file); /** * Close PCAP file. * * @param file PCAP file handle. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_pcap_close(pj_pcap_file *file); /** * Configure filter for reading the file. When filter is configured, * only packets matching all the filter settings will be returned. * * @param file PCAP file handle. * @param filter The filter. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_pcap_set_filter(pj_pcap_file *file, const pj_pcap_filter *filter); /** * Read UDP payload from the next packet in the PCAP file. Optionally it * can return the UDP header, if caller supplies it. * * @param file PCAP file handle. * @param udp_hdr Optional buffer to receive UDP header. * @param udp_payload Buffer to receive the UDP payload. * @param udp_payload_size On input, specify the size of the buffer. * On output, it will be filled with the actual size * of the payload as read from the packet. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_pcap_read_udp(pj_pcap_file *file, pj_pcap_udp_hdr *udp_hdr, pj_uint8_t *udp_payload, pj_size_t *udp_payload_size); /** * @} */ PJ_END_DECL #endif /* __PJLIB_UTIL_PCAP_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/resolver.h ================================================ /* $Id: resolver.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_RESOLVER_H__ #define __PJLIB_UTIL_RESOLVER_H__ /** * @file resolver.h * @brief Asynchronous DNS resolver */ #include PJ_BEGIN_DECL /** * @defgroup PJ_DNS_RESOLVER DNS Asynchronous/Caching Resolution Engine * @ingroup PJ_DNS * @{ * * This module manages the host/server resolution by performing asynchronous * DNS queries and caching the results in the cache. It uses PJLIB-UTIL * low-level DNS parsing functions (see @ref PJ_DNS) and currently supports * several types of DNS resource records such as A record (typical query with * gethostbyname()) and SRV record. * * \section PJ_DNS_RESOLVER_FEATURES Features * * \subsection PJ_DNS_RESOLVER_FEATURES_ASYNC Asynchronous Query and Query Aggregation * * The DNS queries are performed asychronously, with timeout setting * configured on per resolver instance basis. Application can issue multiple * asynchronous queries simultaneously. Subsequent queries to the same resource * (name and DNS resource type) while existing query is still pending will be * merged into one query, so that only one DNS request packet is issued. * * \subsection PJ_DNS_RESOLVER_FEATURES_RETRANSMISSION Query Retransmission * * Asynchronous query will be retransmitted if no response is received * within the preconfigured time. Once maximum retransmission count is * exceeded and no response is received, the query will time out and the * callback will be called when error status. * * \subsection PJ_DNS_RESOLVER_FEATURES_CACHING Response Caching with TTL * * The resolver instance caches the results returned by nameservers, to * enhance the performance by minimizing the message round-trip to the server. * The TTL of the cached resposne is calculated from minimum TTL value found * across all resource record (RR) TTL in the response and further more it can * be limited to some preconfigured maximum TTL in the resolver. * * Response caching can be disabled by setting the maximum TTL value of the * resolver to zero. * * \subsection PJ_DNS_RESOLVER_FEATURES_PARALLEL Parallel and Backup Name Servers * * When the resolver is configured with multiple nameservers, initially the * queries will be issued to multiple name servers simultaneously to probe * which servers are not active. Once the probing stage is done, subsequent * queries will be directed to only one ACTIVE server which provides the best * response time. * * Name servers are probed periodically to see which nameservers are active * and which are down. This probing is done when a query is sent, thus no * timer is needed to maintain this. Also probing will be done in parallel * so that there would be no additional delay for the query. * * * \subsection PJ_DNS_RESOLVER_FEATURES_REC Supported Resource Records * * The low-level DNS parsing utility (see @ref PJ_DNS) supports parsing of * the following DNS resource records (RR): * - DNS A record * - DNS SRV record * - DNS PTR record * - DNS NS record * - DNS CNAME record * * For other types of record, application can parse the raw resource * record data (rdata) from the parsed DNS packet (#pj_dns_parsed_packet). * * * \section PJ_DNS_RESOLVER_USING Using the Resolver * * To use the resolver, application first creates the resolver instance by * calling #pj_dns_resolver_create(). If application already has its own * timer and ioqueue instances, it can instruct the resolver to use these * instances so that application does not need to poll the resolver * periodically to process events. If application does not specify the * timer and ioqueue instance for the resolver, an internal timer and * ioqueue will be created by the resolver. And since the resolver does not * create it's own thread, application MUST poll the resolver periodically * by calling #pj_dns_resolver_handle_events() to allow events (network and * timer) to be processed. * * Next, application MUST configure the nameservers to be used by the * resolver, by calling #pj_dns_resolver_set_ns(). * * Application performs asynchronous query by submitting the query with * #pj_dns_resolver_start_query(). Once the query completes (either * successfully or times out), the callback will be called. * * Application can cancel a pending query by calling #pj_dns_resolver_cancel_query(). * * Resolver must be destroyed by calling #pj_dns_resolver_destroy() to * release all resources back to the system. * * * \section PJ_DNS_RESOLVER_LIMITATIONS Resolver Limitations * * Current implementation mainly suffers from a growing memory problem, * which mainly is caused by the response caching. Although there is only * one cache entry per {query, name} combination, these cache entry will * never get deleted since there is no timer is created to invalidate these * entries. So the more unique names being queried by application, there more * enties will be created in the response cache. * * Note that a single response entry will occupy about 600-700 bytes of * pool memory (the PJ_DNS_RESOLVER_RES_BUF_SIZE value plus internal * structure). * * Application can work around this problem by doing one of these: * - disable caching by setting PJ_DNS_RESOLVER_MAX_TTL and * PJ_DNS_RESOLVER_INVALID_TTL to zero. * - periodically query #pj_dns_resolver_get_cached_count() and destroy- * recreate the resolver to recycle the memory used by the resolver. * * Note that future improvement may solve this problem by introducing * expiration timer to the cached entries. * * * \section PJ_DNS_RESOLVER_REFERENCE Reference * * The PJLIB-UTIL resolver was built from the information in the following * standards: * - * RFC 1035: "Domain names - implementation and specification" * - * RFC 2782: "A DNS RR for specifying the location of services (DNS SRV)" * */ /** * Opaque data type for DNS resolver object. */ typedef struct pj_dns_resolver pj_dns_resolver; /** * Opaque data type for asynchronous DNS query object. */ typedef struct pj_dns_async_query pj_dns_async_query; /** * Type of asynchronous callback which will be called when the asynchronous * query completes. * * @param user_data The user data set by application when creating the * asynchronous query. * @param status Status of the DNS resolution. * @param response The response packet received from the server. This * argument may be NULL when status is not PJ_SUCCESS. */ typedef void pj_dns_callback(void *user_data, pj_status_t status, pj_dns_parsed_packet *response); /** * This structure describes resolver settings. */ typedef struct pj_dns_settings { unsigned options; /**< Options flags. */ unsigned qretr_delay; /**< Query retransmit delay in msec. */ unsigned qretr_count; /**< Query maximum retransmission count. */ unsigned cache_max_ttl; /**< Maximum TTL for cached responses. If the value is zero, caching is disabled. */ unsigned good_ns_ttl; /**< See #PJ_DNS_RESOLVER_GOOD_NS_TTL */ unsigned bad_ns_ttl; /**< See #PJ_DNS_RESOLVER_BAD_NS_TTL */ } pj_dns_settings; /** * This structure represents DNS A record, as the result of parsing * DNS response packet using #pj_dns_parse_a_response(). */ typedef struct pj_dns_a_record { /** The target name being queried. */ pj_str_t name; /** If target name corresponds to a CNAME entry, the alias contains * the value of the CNAME entry, otherwise it will be empty. */ pj_str_t alias; /** Number of IP addresses. */ unsigned addr_count; /** IP addresses of the host found in the response */ pj_in_addr addr[PJ_DNS_MAX_IP_IN_A_REC]; /** Internal buffer for hostname and alias. */ char buf_[128]; } pj_dns_a_record; /** * Set default values to the DNS settings. * * @param s The DNS settings to be initialized. */ PJ_DECL(void) pj_dns_settings_default(pj_dns_settings *s); /** * Create DNS resolver instance. After the resolver is created, application * MUST configure the nameservers with #pj_dns_resolver_set_ns(). * * When creating the resolver, application may specify both timer heap * and ioqueue instance, so that it doesn't need to poll the resolver * periodically. * * @param pf Pool factory where the memory pool will be created from. * @param name Optional resolver name to identify the instance in * the log. * @param options Optional options, must be zero for now. * @param timer Optional timer heap instance to be used by the resolver. * If timer heap is not specified, an internal timer will be * created, and application would need to poll the resolver * periodically. * @param ioqueue Optional I/O Queue instance to be used by the resolver. * If ioqueue is not specified, an internal one will be * created, and application would need to poll the resolver * periodically. * @param p_resolver Pointer to receive the resolver instance. * * @return PJ_SUCCESS on success, or the appropriate error code, */ PJ_DECL(pj_status_t) pj_dns_resolver_create(pj_pool_factory *pf, const char *name, unsigned options, pj_timer_heap_t *timer, pj_ioqueue_t *ioqueue, pj_dns_resolver **p_resolver); /** * Update the name servers for the DNS resolver. The name servers MUST be * configured before any resolution can be done. The order of nameservers * specifies their priority; the first name server will be tried first * before the next in the list. * * @param resolver The resolver instance. * @param count Number of name servers in the array. * @param servers Array of name server IP addresses or hostnames. If * hostname is specified, the hostname must be resolvable * with pj_gethostbyname(). * @param ports Optional array of ports. If this argument is NULL, * the nameserver will use default port. * * @return PJ_SUCCESS on success, or the appropriate error code, */ PJ_DECL(pj_status_t) pj_dns_resolver_set_ns(pj_dns_resolver *resolver, unsigned count, const pj_str_t servers[], const pj_uint16_t ports[]); /** * Get the resolver current settings. * * @param resolver The resolver instance. * @param st Buffer to be filled up with resolver settings. * * @return The query timeout setting, in seconds. */ PJ_DECL(pj_status_t) pj_dns_resolver_get_settings(pj_dns_resolver *resolver, pj_dns_settings *st); /** * Modify the resolver settings. Application should initialize the settings * by retrieving current settings first before applying new settings, to * ensure that all fields are initialized properly. * * @param resolver The resolver instance. * @param st The resolver settings. * * @return PJ_SUCCESS on success, or the appropriate error code, */ PJ_DECL(pj_status_t) pj_dns_resolver_set_settings(pj_dns_resolver *resolver, const pj_dns_settings *st); /** * Poll for events from the resolver. This function MUST be called * periodically when the resolver is using it's own timer or ioqueue * (in other words, when NULL is specified as either \a timer or * \a ioqueue argument in #pj_dns_resolver_create()). * * @param resolver The resolver instance. * @param timeout Maximum time to wait for event occurence. If this * argument is NULL, this function will wait forever * until events occur. */ PJ_DECL(void) pj_dns_resolver_handle_events(pj_dns_resolver *resolver, const pj_time_val *timeout); /** * Destroy DNS resolver instance. * * @param resolver The resolver object to be destryed * @param notify If non-zero, all pending asynchronous queries will be * cancelled and its callback will be called. If FALSE, * then no callback will be called. * * @return PJ_SUCCESS on success, or the appropriate error code, */ PJ_DECL(pj_status_t) pj_dns_resolver_destroy(pj_dns_resolver *resolver, pj_bool_t notify); /** * Create and start asynchronous DNS query for a single resource. Depending * on whether response cache is available, this function will either start * an asynchronous DNS query or call the callback immediately. * * If response is not available in the cache, an asynchronous query will be * started, and callback will be called at some time later when the query * completes. If \a p_query argument is not NULL, it will be filled with * the asynchronous query object. * * If response is available in the cache, the callback will be called * immediately before this function returns. In this case, if \a p_query * argument is not NULL, the value will be set to NULL since no new query * is started. * * @param resolver The resolver object. * @param name The name to be resolved. * @param type The type of resource (see #pj_dns_type constants). * @param options Optional options, must be zero for now. * @param cb Callback to be called when the query completes, * either successfully or with failure. * @param user_data Arbitrary user data to be associated with the query, * and which will be given back in the callback. * @param p_query Optional pointer to receive the query object, if one * was started. If this pointer is specified, a NULL may * be returned if response cache is available immediately. * * @return PJ_SUCCESS if either an asynchronous query has been * started successfully or response cache is available and * the user callback has been called. */ PJ_DECL(pj_status_t) pj_dns_resolver_start_query(pj_dns_resolver *resolver, const pj_str_t *name, int type, unsigned options, pj_dns_callback *cb, void *user_data, pj_dns_async_query **p_query); /** * Cancel a pending query. * * @param query The pending asynchronous query to be cancelled. * @param notify If non-zero, the callback will be called with failure * status to notify that the query has been cancelled. * * @return PJ_SUCCESS on success, or the appropriate error code, */ PJ_DECL(pj_status_t) pj_dns_resolver_cancel_query(pj_dns_async_query *query, pj_bool_t notify); /** * A utility function to parse a DNS response containing A records into * DNS A record. * * @param pkt The DNS response packet. * @param rec The structure to be initialized with the parsed * DNS A record from the packet. * * @return PJ_SUCCESS if response can be parsed successfully. */ PJ_DECL(pj_status_t) pj_dns_parse_a_response(const pj_dns_parsed_packet *pkt, pj_dns_a_record *rec); /** * Put the specified DNS packet into DNS cache. This function is mainly used * for testing the resolver, however it can also be used to inject entries * into the resolver. * * The packet MUST contain either answer section or query section so that * it can be indexed. * * @param resolver The resolver instance. * @param pkt DNS packet to be added to the DNS cache. If the packet * matches existing entry, it will update the entry. * @param set_ttl If the value is PJ_FALSE, the entry will not expire * (so use with care). Otherwise cache expiration will be * calculated based on the TTL of the answeres. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_dns_resolver_add_entry(pj_dns_resolver *resolver, const pj_dns_parsed_packet *pkt, pj_bool_t set_ttl); /** * Get the total number of response in the response cache. * * @param resolver The resolver instance. * * @return Current number of entries being stored in the response * cache. */ PJ_DECL(unsigned) pj_dns_resolver_get_cached_count(pj_dns_resolver *resolver); /** * Dump resolver state to the log. * * @param resolver The resolver instance. * @param detail Will print detailed entries. */ PJ_DECL(void) pj_dns_resolver_dump(pj_dns_resolver *resolver, pj_bool_t detail); /** * @} */ PJ_END_DECL #endif /* __PJLIB_UTIL_RESOLVER_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/scanner.h ================================================ /* $Id: scanner.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_SCANNER_H__ #define __PJ_SCANNER_H__ /** * @file scanner.h * @brief Text Scanning. */ #include PJ_BEGIN_DECL /** * @defgroup PJ_SCAN Fast Text Scanning * @ingroup PJLIB_TEXT * @brief Text scanning utility. * * This module describes a fast text scanning functions. * * @{ */ #if defined(PJ_SCANNER_USE_BITWISE) && PJ_SCANNER_USE_BITWISE != 0 # include #else # include #endif /** * Initialize scanner input specification buffer. * * @param cs_buf The scanner character specification. */ PJ_DECL(void) pj_cis_buf_init(pj_cis_buf_t *cs_buf); /** * Create a new input specification. * * @param cs_buf Specification buffer. * @param cis Character input specification to be initialized. * * @return PJ_SUCCESS if new specification has been successfully * created, or PJ_ETOOMANY if there are already too many * specifications in the buffer. */ PJ_DECL(pj_status_t) pj_cis_init(pj_cis_buf_t *cs_buf, pj_cis_t *cis); /** * Create a new input specification based on an existing specification. * * @param new_cis The new specification to be initialized. * @param existing The existing specification, from which the input * bitmask will be copied to the new specification. * * @return PJ_SUCCESS if new specification has been successfully * created, or PJ_ETOOMANY if there are already too many * specifications in the buffer. */ PJ_DECL(pj_status_t) pj_cis_dup(pj_cis_t *new_cis, pj_cis_t *existing); /** * Add the characters in the specified range '[cstart, cend)' to the * specification (the last character itself ('cend') is not added). * * @param cis The scanner character specification. * @param cstart The first character in the range. * @param cend The next character after the last character in the range. */ PJ_DECL(void) pj_cis_add_range( pj_cis_t *cis, int cstart, int cend); /** * Add alphabetic characters to the specification. * * @param cis The scanner character specification. */ PJ_DECL(void) pj_cis_add_alpha( pj_cis_t *cis); /** * Add numeric characters to the specification. * * @param cis The scanner character specification. */ PJ_DECL(void) pj_cis_add_num( pj_cis_t *cis); /** * Add the characters in the string to the specification. * * @param cis The scanner character specification. * @param str The string. */ PJ_DECL(void) pj_cis_add_str( pj_cis_t *cis, const char *str); /** * Add specification from another specification. * * @param cis The specification is to be set. * @param rhs The specification to be copied. */ PJ_DECL(void) pj_cis_add_cis( pj_cis_t *cis, const pj_cis_t *rhs); /** * Delete characters in the specified range from the specification. * * @param cis The scanner character specification. * @param cstart The first character in the range. * @param cend The next character after the last character in the range. */ PJ_DECL(void) pj_cis_del_range( pj_cis_t *cis, int cstart, int cend); /** * Delete characters in the specified string from the specification. * * @param cis The scanner character specification. * @param str The string. */ PJ_DECL(void) pj_cis_del_str( pj_cis_t *cis, const char *str); /** * Invert specification. * * @param cis The scanner character specification. */ PJ_DECL(void) pj_cis_invert( pj_cis_t *cis ); /** * Check whether the specified character belongs to the specification. * * @param cis The scanner character specification. * @param c The character to check for matching. * * @return Non-zero if match (not necessarily one). */ PJ_INLINE(int) pj_cis_match( const pj_cis_t *cis, pj_uint8_t c ) { return PJ_CIS_ISSET(cis, c); } /** * Flags for scanner. */ enum { /** This flags specifies that the scanner should automatically skip whitespaces */ PJ_SCAN_AUTOSKIP_WS = 1, /** This flags specifies that the scanner should automatically skip SIP header continuation. This flag implies PJ_SCAN_AUTOSKIP_WS. */ PJ_SCAN_AUTOSKIP_WS_HEADER = 3, /** Auto-skip new lines. */ PJ_SCAN_AUTOSKIP_NEWLINE = 4 }; /* Forward decl. */ struct pj_scanner; /** * The callback function type to be called by the scanner when it encounters * syntax error. * * @param scanner The scanner instance that calls the callback . */ typedef void (*pj_syn_err_func_ptr)(struct pj_scanner *scanner); /** * The text scanner structure. */ typedef struct pj_scanner { char *begin; /**< Start of input buffer. */ char *end; /**< End of input buffer. */ char *curptr; /**< Current pointer. */ int line; /**< Current line. */ char *start_line; /**< Where current line starts. */ int skip_ws; /**< Skip whitespace flag. */ pj_syn_err_func_ptr callback; /**< Syntax error callback. */ } pj_scanner; /** * This structure can be used by application to store the state of the parser, * so that the scanner state can be rollback to this state when necessary. */ typedef struct pj_scan_state { char *curptr; /**< Current scanner's pointer. */ int line; /**< Current line. */ char *start_line; /**< Start of current line. */ } pj_scan_state; /** * Initialize the scanner. Note that the input string buffer must have * length at least buflen+1 because the scanner will NULL terminate the * string during initialization. * * @param scanner The scanner to be initialized. * @param bufstart The input buffer to scan. Note that buffer[buflen] will be * filled with NULL char until scanner is destroyed, so * the actual buffer length must be at least buflen+1. * @param buflen The length of the input buffer, which normally is * strlen(bufstart). * @param options Zero, or combination of PJ_SCAN_AUTOSKIP_WS or * PJ_SCAN_AUTOSKIP_WS_HEADER * @param callback Callback to be called when the scanner encounters syntax * error condition. */ PJ_DECL(void) pj_scan_init( pj_scanner *scanner, char *bufstart, pj_size_t buflen, unsigned options, pj_syn_err_func_ptr callback ); /** * Call this function when application has finished using the scanner. * * @param scanner The scanner. */ PJ_DECL(void) pj_scan_fini( pj_scanner *scanner ); /** * Determine whether the EOF condition for the scanner has been met. * * @param scanner The scanner. * * @return Non-zero if scanner is EOF. */ PJ_INLINE(int) pj_scan_is_eof( const pj_scanner *scanner) { return scanner->curptr >= scanner->end; } /** * Peek strings in current position according to parameter spec, and return * the strings in parameter out. The current scanner position will not be * moved. If the scanner is already in EOF state, syntax error callback will * be called thrown. * * @param scanner The scanner. * @param spec The spec to match input string. * @param out String to store the result. * * @return the character right after the peek-ed position or zero if there's * no more characters. */ PJ_DECL(int) pj_scan_peek( pj_scanner *scanner, const pj_cis_t *spec, pj_str_t *out); /** * Peek len characters in current position, and return them in out parameter. * Note that whitespaces or newlines will be returned as it is, regardless * of PJ_SCAN_AUTOSKIP_WS settings. If the character left is less than len, * syntax error callback will be called. * * @param scanner The scanner. * @param len Length to peek. * @param out String to store the result. * * @return the character right after the peek-ed position or zero if there's * no more characters. */ PJ_DECL(int) pj_scan_peek_n( pj_scanner *scanner, pj_size_t len, pj_str_t *out); /** * Peek strings in current position until spec is matched, and return * the strings in parameter out. The current scanner position will not be * moved. If the scanner is already in EOF state, syntax error callback will * be called. * * @param scanner The scanner. * @param spec The peeking will stop when the input match this spec. * @param out String to store the result. * * @return the character right after the peek-ed position. */ PJ_DECL(int) pj_scan_peek_until( pj_scanner *scanner, const pj_cis_t *spec, pj_str_t *out); /** * Get characters from the buffer according to the spec, and return them * in out parameter. The scanner will attempt to get as many characters as * possible as long as the spec matches. If the first character doesn't * match the spec, or scanner is already in EOF when this function is called, * an exception will be thrown. * * @param scanner The scanner. * @param spec The spec to match input string. * @param out String to store the result. */ PJ_DECL(void) pj_scan_get( pj_scanner *scanner, const pj_cis_t *spec, pj_str_t *out); /** * Just like #pj_scan_get(), but additionally performs unescaping when * escaped ('%') character is found. The input spec MUST NOT contain the * specification for '%' characted. * * @param scanner The scanner. * @param spec The spec to match input string. * @param out String to store the result. */ PJ_DECL(void) pj_scan_get_unescape( pj_scanner *scanner, const pj_cis_t *spec, pj_str_t *out); /** * Get characters between quotes. If current input doesn't match begin_quote, * syntax error will be thrown. Note that the resulting string will contain * the enclosing quote. * * @param scanner The scanner. * @param begin_quote The character to begin the quote. * @param end_quote The character to end the quote. * @param out String to store the result. */ PJ_DECL(void) pj_scan_get_quote( pj_scanner *scanner, int begin_quote, int end_quote, pj_str_t *out); /** * Get characters between quotes. If current input doesn't match begin_quote, * syntax error will be thrown. Note that the resulting string will contain * the enclosing quote. * * @param scanner The scanner. * @param begin_quotes The character array to begin the quotes. For example, * the two characters " and '. * @param end_quotes The character array to end the quotes. The position * found in the begin_quotes array will be used to match * the end quotes. So if the begin_quotes was the array * of "'< the end_quotes should be "'>. If begin_array * matched the ' then the end_quotes will look for ' to * match at the end. * @param qsize The size of the begin_quotes and end_quotes arrays. * @param out String to store the result. */ PJ_DECL(void) pj_scan_get_quotes(pj_scanner *scanner, const char *begin_quotes, const char *end_quotes, int qsize, pj_str_t *out); /** * Get N characters from the scanner. * * @param scanner The scanner. * @param N Number of characters to get. * @param out String to store the result. */ PJ_DECL(void) pj_scan_get_n( pj_scanner *scanner, unsigned N, pj_str_t *out); /** * Get one character from the scanner. * * @param scanner The scanner. * * @return The character. */ PJ_DECL(int) pj_scan_get_char( pj_scanner *scanner ); /** * Get characters from the scanner and move the scanner position until the * current character matches the spec. * * @param scanner The scanner. * @param spec Get until the input match this spec. * @param out String to store the result. */ PJ_DECL(void) pj_scan_get_until( pj_scanner *scanner, const pj_cis_t *spec, pj_str_t *out); /** * Get characters from the scanner and move the scanner position until the * current character matches until_char. * * @param scanner The scanner. * @param until_char Get until the input match this character. * @param out String to store the result. */ PJ_DECL(void) pj_scan_get_until_ch( pj_scanner *scanner, int until_char, pj_str_t *out); /** * Get characters from the scanner and move the scanner position until the * current character matches until_char. * * @param scanner The scanner. * @param until_spec Get until the input match any of these characters. * @param out String to store the result. */ PJ_DECL(void) pj_scan_get_until_chr( pj_scanner *scanner, const char *until_spec, pj_str_t *out); /** * Advance the scanner N characters, and skip whitespace * if necessary. * * @param scanner The scanner. * @param N Number of characters to skip. * @param skip Flag to specify whether whitespace should be skipped * after skipping the characters. */ PJ_DECL(void) pj_scan_advance_n( pj_scanner *scanner, unsigned N, pj_bool_t skip); /** * Compare string in current position with the specified string. * * @param scanner The scanner. * @param s The string to compare with. * @param len Length of the string to compare. * * @return zero, <0, or >0 (just like strcmp()). */ PJ_DECL(int) pj_scan_strcmp( pj_scanner *scanner, const char *s, int len); /** * Case-less string comparison of current position with the specified * string. * * @param scanner The scanner. * @param s The string to compare with. * @param len Length of the string to compare with. * * @return zero, <0, or >0 (just like strcmp()). */ PJ_DECL(int) pj_scan_stricmp( pj_scanner *scanner, const char *s, int len); /** * Perform case insensitive string comparison of string in current position, * knowing that the string to compare only consists of alphanumeric * characters. * * Note that unlike #pj_scan_stricmp, this function can only return zero or * -1. * * @param scanner The scanner. * @param s The string to compare with. * @param len Length of the string to compare with. * * @return zero if equal or -1. * * @see strnicmp_alnum, pj_stricmp_alnum */ PJ_DECL(int) pj_scan_stricmp_alnum( pj_scanner *scanner, const char *s, int len); /** * Get a newline from the scanner. A newline is defined as '\\n', or '\\r', or * "\\r\\n". If current input is not newline, syntax error will be thrown. * * @param scanner The scanner. */ PJ_DECL(void) pj_scan_get_newline( pj_scanner *scanner ); /** * Manually skip whitespaces according to flag that was specified when * the scanner was initialized. * * @param scanner The scanner. */ PJ_DECL(void) pj_scan_skip_whitespace( pj_scanner *scanner ); /** * Skip current line. * * @param scanner The scanner. */ PJ_DECL(void) pj_scan_skip_line( pj_scanner *scanner ); /** * Save the full scanner state. * * @param scanner The scanner. * @param state Variable to store scanner's state. */ PJ_DECL(void) pj_scan_save_state( const pj_scanner *scanner, pj_scan_state *state); /** * Restore the full scanner state. * Note that this would not restore the string if application has modified * it. This will only restore the scanner scanning position. * * @param scanner The scanner. * @param state State of the scanner. */ PJ_DECL(void) pj_scan_restore_state( pj_scanner *scanner, pj_scan_state *state); /** * Get current column position. * * @param scanner The scanner. * * @return The column position. */ PJ_INLINE(int) pj_scan_get_col( const pj_scanner *scanner ) { return (int)(scanner->curptr - scanner->start_line); } /** * @} */ PJ_END_DECL #endif ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/scanner_cis_bitwise.h ================================================ /* $Id: scanner_cis_bitwise.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_SCANNER_CIS_BIT_H__ #define __PJLIB_UTIL_SCANNER_CIS_BIT_H__ #include PJ_BEGIN_DECL /** * This describes the type of individual character specification in * #pj_cis_buf_t. Basicly the number of bits here */ #ifndef PJ_CIS_ELEM_TYPE # define PJ_CIS_ELEM_TYPE pj_uint32_t #endif /** * This describes the type of individual character specification in * #pj_cis_buf_t. */ typedef PJ_CIS_ELEM_TYPE pj_cis_elem_t; /** * Maximum number of input specification in a buffer. * Effectively this means the number of bits in pj_cis_elem_t. */ #define PJ_CIS_MAX_INDEX (sizeof(pj_cis_elem_t) << 3) /** * The scanner input specification buffer. */ typedef struct pj_cis_buf_t { pj_cis_elem_t cis_buf[256]; /**< Must be 256 (not 128)! */ pj_cis_elem_t use_mask; /**< To keep used indexes. */ } pj_cis_buf_t; /** * Character input specification. */ typedef struct pj_cis_t { pj_cis_elem_t *cis_buf; /**< Pointer to buffer. */ int cis_id; /**< Id. */ } pj_cis_t; /** * Set the membership of the specified character. * Note that this is a macro, and arguments may be evaluated more than once. * * @param cis Pointer to character input specification. * @param c The character. */ #define PJ_CIS_SET(cis,c) ((cis)->cis_buf[(int)(c)] |= (1 << (cis)->cis_id)) /** * Remove the membership of the specified character. * Note that this is a macro, and arguments may be evaluated more than once. * * @param cis Pointer to character input specification. * @param c The character to be removed from the membership. */ #define PJ_CIS_CLR(cis,c) ((cis)->cis_buf[(int)c] &= ~(1 << (cis)->cis_id)) /** * Check the membership of the specified character. * Note that this is a macro, and arguments may be evaluated more than once. * * @param cis Pointer to character input specification. * @param c The character. */ #define PJ_CIS_ISSET(cis,c) ((cis)->cis_buf[(int)c] & (1 << (cis)->cis_id)) PJ_END_DECL #endif /* __PJLIB_UTIL_SCANNER_CIS_BIT_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/scanner_cis_uint.h ================================================ /* $Id: scanner_cis_uint.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_SCANNER_CIS_BIT_H__ #define __PJLIB_UTIL_SCANNER_CIS_BIT_H__ #include PJ_BEGIN_DECL /** * This describes the type of individual character specification in * #pj_cis_buf_t. Basicly the number of bits here */ #ifndef PJ_CIS_ELEM_TYPE # define PJ_CIS_ELEM_TYPE int #endif /** * This describes the type of individual character specification in * #pj_cis_buf_t. */ typedef PJ_CIS_ELEM_TYPE pj_cis_elem_t; /** pj_cis_buf_t is not used when uint back-end is used. */ typedef int pj_cis_buf_t; /** * Character input specification. */ typedef struct pj_cis_t { PJ_CIS_ELEM_TYPE cis_buf[256]; /**< Internal buffer. */ } pj_cis_t; /** * Set the membership of the specified character. * Note that this is a macro, and arguments may be evaluated more than once. * * @param cis Pointer to character input specification. * @param c The character. */ #define PJ_CIS_SET(cis,c) ((cis)->cis_buf[(int)(c)] = 1) /** * Remove the membership of the specified character. * Note that this is a macro, and arguments may be evaluated more than once. * * @param cis Pointer to character input specification. * @param c The character to be removed from the membership. */ #define PJ_CIS_CLR(cis,c) ((cis)->cis_buf[(int)c] = 0) /** * Check the membership of the specified character. * Note that this is a macro, and arguments may be evaluated more than once. * * @param cis Pointer to character input specification. * @param c The character. */ #define PJ_CIS_ISSET(cis,c) ((cis)->cis_buf[(int)c]) PJ_END_DECL #endif /* __PJLIB_UTIL_SCANNER_CIS_BIT_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/sha1.h ================================================ /* $Id: sha1.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_SHA1_H__ #define __PJLIB_UTIL_SHA1_H__ /** * @file sha1.h * @brief SHA1 encryption implementation */ #include PJ_BEGIN_DECL /** * @defgroup PJLIB_UTIL_SHA1 SHA1 * @ingroup PJLIB_UTIL_ENCRYPTION * @{ */ /** SHA1 context */ typedef struct pj_sha1_context { pj_uint32_t state[5]; /**< State */ pj_uint32_t count[2]; /**< Count */ pj_uint8_t buffer[64]; /**< Buffer */ } pj_sha1_context; /** SHA1 digest size is 20 bytes */ #define PJ_SHA1_DIGEST_SIZE 20 /** Initialize the algorithm. * @param ctx SHA1 context. */ PJ_DECL(void) pj_sha1_init(pj_sha1_context *ctx); /** Append a stream to the message. * @param ctx SHA1 context. * @param data Data. * @param nbytes Length of data. */ PJ_DECL(void) pj_sha1_update(pj_sha1_context *ctx, const pj_uint8_t *data, const pj_size_t nbytes); /** Finish the message and return the digest. * @param ctx SHA1 context. * @param digest 16 byte digest. */ PJ_DECL(void) pj_sha1_final(pj_sha1_context *ctx, pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE]); /** * @} */ PJ_END_DECL #endif /* __PJLIB_UTIL_SHA1_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/srv_resolver.h ================================================ /* $Id: srv_resolver.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_SRV_RESOLVER_H__ #define __PJLIB_UTIL_SRV_RESOLVER_H__ /** * @file srv_resolver.h * @brief DNS SRV resolver */ #include PJ_BEGIN_DECL /** * @defgroup PJ_DNS_SRV_RESOLVER DNS SRV Resolution Helper * @ingroup PJ_DNS * @{ * * \section PJ_DNS_SRV_RESOLVER_INTRO DNS SRV Resolution Helper * * This module provides an even higher layer of abstraction for the DNS * resolution framework, to resolve DNS SRV names. * * The #pj_dns_srv_resolve() function will asynchronously resolve the server * name into IP address(es) with a single function call. If the SRV name * contains multiple names, then each will be resolved with individual * DNS A resolution to get the IP addresses. Upon successful completion, * application callback will be called with each IP address of the * target selected based on the load-balancing and fail-over criteria * below. * * When the resolver fails to resolve the name using DNS SRV resolution * (for example when the DNS SRV record is not present in the DNS server), * the resolver will fallback to using DNS A record resolution to resolve * the name. * * \subsection PJ_DNS_SRV_RESOLVER_FAILOVER_LOADBALANCE Load-Balancing and Fail-Over * * When multiple targets are returned in the DNS SRV response, server entries * are selected based on the following rule (which is described in RFC 2782): * - targets will be sorted based on the priority first. * - for targets with the same priority, #pj_dns_srv_resolve() will select * only one target according to its weight. To select this one target, * the function associates running-sum for all targets, and generates * a random number between zero and the total running-sum (inclusive). * The target selected is the first target with running-sum greater than * or equal to this random number. * * The above procedure will select one target for each priority, allowing * application to fail-over to the next target when the previous target fails. * These targets are returned in the #pj_dns_srv_record structure * argument of the callback. * * \section PJ_DNS_SRV_RESOLVER_REFERENCE Reference * * Reference: * - RFC 2782: * A DNS RR for specifying the location of services (DNS SRV) */ /** * Flags to be specified when starting the DNS SRV query. */ typedef enum pj_dns_srv_option { /** * Specify if the resolver should fallback with DNS A * resolution when the SRV resolution fails. This option may * be specified together with PJ_DNS_SRV_FALLBACK_AAAA to * make the resolver fallback to AAAA if SRV resolution fails, * and then to DNS A resolution if the AAAA resolution fails. */ PJ_DNS_SRV_FALLBACK_A = 1, /** * Specify if the resolver should fallback with DNS AAAA * resolution when the SRV resolution fails. This option may * be specified together with PJ_DNS_SRV_FALLBACK_A to * make the resolver fallback to AAAA if SRV resolution fails, * and then to DNS A resolution if the AAAA resolution fails. */ PJ_DNS_SRV_FALLBACK_AAAA = 2, /** * Specify if the resolver should try to resolve with DNS AAAA * resolution first of each targets in the DNS SRV record. If * this option is not specified, the SRV resolver will query * the DNS A record for the target instead. */ PJ_DNS_SRV_RESOLVE_AAAA = 4 } pj_dns_srv_option; /** * This structure represents DNS SRV records as the result of DNS SRV * resolution using #pj_dns_srv_resolve(). */ typedef struct pj_dns_srv_record { /** Number of address records. */ unsigned count; /** Address records. */ struct { /** Server priority (the lower the higher the priority). */ unsigned priority; /** Server weight (the higher the more load it can handle). */ unsigned weight; /** Port number. */ pj_uint16_t port; /** The host address. */ pj_dns_a_record server; } entry[PJ_DNS_SRV_MAX_ADDR]; } pj_dns_srv_record; /** Opaque declaration for DNS SRV query */ typedef struct pj_dns_srv_async_query pj_dns_srv_async_query; /** * Type of callback function to receive notification from the resolver * when the resolution process completes. */ typedef void pj_dns_srv_resolver_cb(void *user_data, pj_status_t status, const pj_dns_srv_record *rec); /** * Start DNS SRV resolution for the specified name. The full name of the * entry will be concatenated from \a res_name and \a domain_name fragments. * * @param domain_name The domain name part of the name. * @param res_name The full service name, including the transport name * and with all the leading underscore characters and * ending dot (e.g. "_sip._udp.", "_stun._udp."). * @param def_port The port number to be assigned to the resolved address * when the DNS SRV resolution fails and the name is * resolved with DNS A resolution. * @param pool Memory pool used to allocate memory for the query. * @param resolver The resolver instance. * @param option Option flags, which can be constructed from * #pj_dns_srv_option bitmask. Note that this argument * was called "fallback_a" in pjsip version 0.8.0 and * older, but the new option should be backward * compatible with existing applications. If application * specifies PJ_TRUE as "fallback_a" value, it will * correspond to PJ_DNS_SRV_FALLBACK_A option. * @param token Arbitrary data to be associated with this query when * the calback is called. * @param cb Pointer to callback function to receive the * notification when the resolution process completes. * @param p_query Optional pointer to receive the query object, if one * was started. If this pointer is specified, a NULL may * be returned if response cache is available immediately. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_dns_srv_resolve(const pj_str_t *domain_name, const pj_str_t *res_name, unsigned def_port, pj_pool_t *pool, pj_dns_resolver *resolver, unsigned option, void *token, pj_dns_srv_resolver_cb *cb, pj_dns_srv_async_query **p_query); /** * Cancel an outstanding DNS SRV query. * * @param query The pending asynchronous query to be cancelled. * @param notify If non-zero, the callback will be called with failure * status to notify that the query has been cancelled. * * @return PJ_SUCCESS on success, or the appropriate error code, */ PJ_DECL(pj_status_t) pj_dns_srv_cancel_query(pj_dns_srv_async_query *query, pj_bool_t notify); /** * @} */ PJ_END_DECL #endif /* __PJLIB_UTIL_SRV_RESOLVER_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/string.h ================================================ /* $Id: string.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_STRING_H__ #define __PJLIB_UTIL_STRING_H__ /** * @file string.h * @brief More string functions. */ #include #include /** * @defgroup PJLIB_UTIL_STRING String Escaping Utilities * @ingroup PJLIB_TEXT * @{ */ PJ_BEGIN_DECL /** * Unescape string. If source string does not contain any escaped * characters, the function would simply return the original string. * Otherwise a new string will be allocated. * * @param pool Pool to allocate the string. * @param src Source string to unescape. * * @return String with no escaped characters. */ PJ_DECL(pj_str_t) pj_str_unescape( pj_pool_t *pool, const pj_str_t *src); /** * Unescape string to destination. * * @param dst Target string. * @param src Source string. * * @return Target string. */ PJ_DECL(pj_str_t*) pj_strcpy_unescape(pj_str_t *dst, const pj_str_t *src); /** * Copy string to destination while escaping reserved characters, up to * the specified maximum length. * * @param dst Target string. * @param src Source string. * @param max Maximum length to copy to target string. * @param unres Unreserved characters, which are allowed to appear * unescaped. * * @return The target string if all characters have been copied * successfully, or NULL if there's not enough buffer to * escape the strings. */ PJ_DECL(pj_str_t*) pj_strncpy_escape(pj_str_t *dst, const pj_str_t *src, pj_ssize_t max, const pj_cis_t *unres); /** * Copy string to destination while escaping reserved characters, up to * the specified maximum length. * * @param dst Target string. * @param src Source string. * @param max Maximum length to copy to target string. * @param unres Unreserved characters, which are allowed to appear * unescaped. * * @return The length of the destination, or -1 if there's not * enough buffer. */ PJ_DECL(pj_ssize_t) pj_strncpy2_escape(char *dst, const pj_str_t *src, pj_ssize_t max, const pj_cis_t *unres); PJ_END_DECL /** * @} */ #endif /* __PJLIB_UTIL_STRING_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/stun_simple.h ================================================ /* $Id: stun_simple.h 4224 2012-08-09 05:21:25Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSTUN_H__ #define __PJSTUN_H__ /** * @file stun.h * @brief STUN client. */ #include #include PJ_BEGIN_DECL /* * This enumeration describes STUN message types. */ typedef enum pjstun_msg_type { PJSTUN_BINDING_REQUEST = 0x0001, PJSTUN_BINDING_RESPONSE = 0x0101, PJSTUN_BINDING_ERROR_RESPONSE = 0x0111, PJSTUN_SHARED_SECRET_REQUEST = 0x0002, PJSTUN_SHARED_SECRET_RESPONSE = 0x0102, PJSTUN_SHARED_SECRET_ERROR_RESPONSE = 0x0112 } pjstun_msg_type; /* * This enumeration describes STUN attribute types. */ typedef enum pjstun_attr_type { PJSTUN_ATTR_MAPPED_ADDR = 1, PJSTUN_ATTR_RESPONSE_ADDR, PJSTUN_ATTR_CHANGE_REQUEST, PJSTUN_ATTR_SOURCE_ADDR, PJSTUN_ATTR_CHANGED_ADDR, PJSTUN_ATTR_USERNAME, PJSTUN_ATTR_PASSWORD, PJSTUN_ATTR_MESSAGE_INTEGRITY, PJSTUN_ATTR_ERROR_CODE, PJSTUN_ATTR_UNKNOWN_ATTRIBUTES, PJSTUN_ATTR_REFLECTED_FROM, PJSTUN_ATTR_XOR_MAPPED_ADDR = 0x0020 } pjstun_attr_type; /* * This structre describes STUN message header. */ typedef struct pjstun_msg_hdr { pj_uint16_t type; pj_uint16_t length; pj_uint32_t tsx[4]; } pjstun_msg_hdr; /* * This structre describes STUN attribute header. */ typedef struct pjstun_attr_hdr { pj_uint16_t type; pj_uint16_t length; } pjstun_attr_hdr; /* * This structre describes STUN MAPPED-ADDR attribute. */ typedef struct pjstun_mapped_addr_attr { pjstun_attr_hdr hdr; pj_uint8_t ignored; pj_uint8_t family; pj_uint16_t port; pj_uint32_t addr; } pjstun_mapped_addr_attr; typedef pjstun_mapped_addr_attr pjstun_response_addr_attr; typedef pjstun_mapped_addr_attr pjstun_changed_addr_attr; typedef pjstun_mapped_addr_attr pjstun_src_addr_attr; typedef pjstun_mapped_addr_attr pjstun_reflected_form_attr; typedef struct pjstun_change_request_attr { pjstun_attr_hdr hdr; pj_uint32_t value; } pjstun_change_request_attr; typedef struct pjstun_username_attr { pjstun_attr_hdr hdr; pj_uint32_t value[1]; } pjstun_username_attr; typedef pjstun_username_attr pjstun_password_attr; typedef struct pjstun_error_code_attr { pjstun_attr_hdr hdr; pj_uint16_t ignored; pj_uint8_t err_class; pj_uint8_t number; char reason[4]; } pjstun_error_code_attr; typedef struct pjstun_msg { pjstun_msg_hdr *hdr; int attr_count; pjstun_attr_hdr *attr[PJSTUN_MAX_ATTR]; } pjstun_msg; /* STUN message API (stun.c). */ PJ_DECL(pj_status_t) pjstun_create_bind_req( pj_pool_t *pool, void **msg, pj_size_t *len, pj_uint32_t id_hi, pj_uint32_t id_lo); PJ_DECL(pj_status_t) pjstun_parse_msg( void *buf, pj_size_t len, pjstun_msg *msg); PJ_DECL(void*) pjstun_msg_find_attr( pjstun_msg *msg, pjstun_attr_type t); /** * @defgroup PJLIB_UTIL_STUN_CLIENT Simple STUN Helper * @ingroup PJ_PROTOCOLS * @brief A simple and small footprint STUN resolution helper * @{ * * This is the older implementation of STUN client, with only one function * provided (pjstun_get_mapped_addr()) to retrieve the public IP address * of multiple sockets. */ /** * This is the main function to request the mapped address of local sockets * to multiple STUN servers. This function is able to find the mapped * addresses of multiple sockets simultaneously, and for each socket, two * requests will be sent to two different STUN servers to see if both servers * get the same public address for the same socket. (Note that application can * specify the same address for the two servers, but still two requests will * be sent for each server). * * This function will perform necessary retransmissions of the requests if * response is not received within a predetermined period. When all responses * have been received, the function will compare the mapped addresses returned * by the servers, and when both are equal, the address will be returned in * \a mapped_addr argument. * * @param pf The pool factory where memory will be allocated from. * @param sock_cnt Number of sockets in the socket array. * @param sock Array of local UDP sockets which public addresses are * to be queried from the STUN servers. * @param srv1 Host name or IP address string of the first STUN * server. * @param port1 The port number of the first STUN server. * @param srv2 Host name or IP address string of the second STUN * server. * @param port2 The port number of the second STUN server. * @param mapped_addr Array to receive the mapped public address of the local * UDP sockets, when the function returns PJ_SUCCESS. * * @return This functions returns PJ_SUCCESS if responses are * received from all servers AND all servers returned the * same mapped public address. Otherwise this function may * return one of the following error codes: * - PJLIB_UTIL_ESTUNNOTRESPOND: no respons from servers. * - PJLIB_UTIL_ESTUNSYMMETRIC: different mapped addresses * are returned by servers. * - etc. * */ PJ_DECL(pj_status_t) pjstun_get_mapped_addr( pj_pool_factory *pf, int sock_cnt, pj_sock_t sock[], const pj_str_t *srv1, int port1, const pj_str_t *srv2, int port2, pj_sockaddr_in mapped_addr[]); /* * This structre describes configurable setting for requesting mapped address. */ typedef struct pjstun_setting { /** * Specifies whether STUN request generated by old STUN library should * insert magic cookie (specified in RFC 5389) in the transaction ID. */ pj_bool_t use_stun2; /** * Host name or IP address string of the first STUN server. */ pj_str_t srv1; /** * The port number of the first STUN server. */ int port1; /** * Host name or IP address string of the second STUN server. */ pj_str_t srv2; /** * The port number of the second STUN server. */ int port2; } pjstun_setting; /** * Another version of mapped address resolution of local sockets to multiple * STUN servers configured in #pjstun_setting. This function is able to find * the mapped addresses of multiple sockets simultaneously, and for each * socket, two requests will be sent to two different STUN servers to see if * both servers get the same public address for the same socket. (Note that * application can specify the same address for the two servers, but still * two requests will be sent for each server). * * This function will perform necessary retransmissions of the requests if * response is not received within a predetermined period. When all responses * have been received, the function will compare the mapped addresses returned * by the servers, and when both are equal, the address will be returned in * \a mapped_addr argument. * * @param pf The pool factory where memory will be allocated from. * @param opt The STUN settings. * @param sock_cnt Number of sockets in the socket array. * @param sock Array of local UDP sockets which public addresses are * to be queried from the STUN servers. * @param mapped_addr Array to receive the mapped public address of the local * UDP sockets, when the function returns PJ_SUCCESS. * * @return This functions returns PJ_SUCCESS if responses are * received from all servers AND all servers returned the * same mapped public address. Otherwise this function may * return one of the following error codes: * - PJLIB_UTIL_ESTUNNOTRESPOND: no respons from servers. * - PJLIB_UTIL_ESTUNSYMMETRIC: different mapped addresses * are returned by servers. * - etc. * */ PJ_DECL(pj_status_t) pjstun_get_mapped_addr2( pj_pool_factory *pf, const pjstun_setting *opt, int sock_cnt, pj_sock_t sock[], pj_sockaddr_in mapped_addr[]); PJ_END_DECL /** * @} */ #endif /* __PJSTUN_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/types.h ================================================ /* $Id: types.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_TYPES_H__ #define __PJLIB_UTIL_TYPES_H__ /** * @file types.h * @brief PJLIB-UTIL types. */ #include #include /** * @defgroup PJLIB_UTIL_BASE Base * @{ */ PJ_BEGIN_DECL /** * Initialize PJLIB UTIL (defined in errno.c) * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjlib_util_init(void); PJ_END_DECL /** * @} */ /** * @defgroup PJLIB_TEXT Text and String Manipulation */ /** * @defgroup PJ_PROTOCOLS Protocols */ /** * @defgroup PJ_FILE_FMT File Formats */ /** * @mainpage PJLIB-UTIL * * \n * \n * \n * This is the documentation of PJLIB-UTIL, an auxiliary library providing * adjunct functions to PJLIB. * * Please go to the Table of Contents page * for list of modules. * * * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n * \n */ #endif /* __PJLIB_UTIL_TYPES_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util/xml.h ================================================ /* $Id: xml.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_XML_H__ #define __PJ_XML_H__ /** * @file xml.h * @brief PJLIB XML Parser/Helper. */ #include #include PJ_BEGIN_DECL /** * @defgroup PJ_TINY_XML Mini/Tiny XML Parser/Helper * @ingroup PJ_FILE_FMT * @{ */ /** Typedef for XML attribute. */ typedef struct pj_xml_attr pj_xml_attr; /** Typedef for XML nodes. */ typedef struct pj_xml_node pj_xml_node; /** This structure declares XML attribute. */ struct pj_xml_attr { PJ_DECL_LIST_MEMBER(pj_xml_attr); /**< Standard list elements. */ pj_str_t name; /**< Attribute name. */ pj_str_t value; /**< Attribute value. */ }; /** This structure describes XML node head inside XML node structure. */ typedef struct pj_xml_node_head { PJ_DECL_LIST_MEMBER(pj_xml_node); /**< Standard list elements. */ } pj_xml_node_head; /** This structure describes XML node. */ struct pj_xml_node { PJ_DECL_LIST_MEMBER(pj_xml_node); /**< List @a prev and @a next member */ pj_str_t name; /**< Node name. */ pj_xml_attr attr_head; /**< Attribute list. */ pj_xml_node_head node_head; /**< Node list. */ pj_str_t content; /**< Node content. */ }; /** * Parse XML message into XML document with a single root node. The parser * is capable of parsing XML processing instruction construct ("next is the starting point. * @param name Node name to find. * * @return XML node found or NULL. */ PJ_DECL(pj_xml_node*) pj_xml_find_next_node(const pj_xml_node *parent, const pj_xml_node *node, const pj_str_t *name); /** * Recursively find the first node with the specified name in the child nodes * and their children. * * @param parent Parent node. * @param name Node name to find. * * @return XML node found or NULL. */ PJ_DECL(pj_xml_node*) pj_xml_find_node_rec(const pj_xml_node *parent, const pj_str_t *name); /** * Find first attribute within a node with the specified name and optional * value. * * @param node XML Node. * @param name Attribute name to find. * @param value Optional value to match. * * @return XML attribute found, or NULL. */ PJ_DECL(pj_xml_attr*) pj_xml_find_attr(const pj_xml_node *node, const pj_str_t *name, const pj_str_t *value); /** * Find a direct child node with the specified name and match the function. * * @param parent Parent node. * @param name Optional name. If this is NULL, the name will not be * matched. * @param data Data to be passed to matching function. * @param match Optional matching function. * * @return The first matched node, or NULL. */ PJ_DECL(pj_xml_node*) pj_xml_find( const pj_xml_node *parent, const pj_str_t *name, const void *data, pj_bool_t (*match)(const pj_xml_node *, const void*)); /** * Recursively find a child node with the specified name and match the * function. * * @param parent Parent node. * @param name Optional name. If this is NULL, the name will not be * matched. * @param data Data to be passed to matching function. * @param match Optional matching function. * * @return The first matched node, or NULL. */ PJ_DECL(pj_xml_node*) pj_xml_find_rec(const pj_xml_node *parent, const pj_str_t *name, const void *data, pj_bool_t (*match)(const pj_xml_node*, const void*)); /** * @} */ PJ_END_DECL #endif /* __PJ_XML_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/include/pjlib-util.h ================================================ /* $Id: pjlib-util.h 4476 2013-04-19 06:05:06Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJLIB_UTIL_H__ #define __PJLIB_UTIL_H__ /** * @file pjlib-util.h * @brief pjlib-util.h */ /* Base */ #include #include /* Getopt */ #include /* Crypto */ #include #include #include #include #include #include /* DNS and resolver */ #include #include #include /* Simple DNS server */ #include /* Text scanner and utilities */ #include #include /* XML */ #include /* JSON */ #include /* Old STUN */ #include /* PCAP */ #include /* HTTP */ #include #endif /* __PJLIB_UTIL_H__ */ ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/base64.c ================================================ /* $Id: base64.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #define INV -1 #define PADDING '=' static const char base64_char[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; static int base256_char(char c) { if (c >= 'A' && c <= 'Z') return (c - 'A'); else if (c >= 'a' && c <= 'z') return (c - 'a' + 26); else if (c >= '0' && c <= '9') return (c - '0' + 52); else if (c == '+') return (62); else if (c == '/') return (63); else { /* It *may* happen on bad input, so this is not a good idea. * pj_assert(!"Should not happen as '=' should have been filtered"); */ return INV; } } static void base256to64(pj_uint8_t c1, pj_uint8_t c2, pj_uint8_t c3, int padding, char *output) { *output++ = base64_char[c1>>2]; *output++ = base64_char[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)]; switch (padding) { case 0: *output++ = base64_char[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)]; *output = base64_char[c3 & 0x3F]; break; case 1: *output++ = base64_char[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)]; *output = PADDING; break; case 2: default: *output++ = PADDING; *output = PADDING; break; } } PJ_DEF(pj_status_t) pj_base64_encode(const pj_uint8_t *input, int in_len, char *output, int *out_len) { const pj_uint8_t *pi = input; pj_uint8_t c1, c2, c3; int i = 0; char *po = output; PJ_ASSERT_RETURN(input && output && out_len, PJ_EINVAL); PJ_ASSERT_RETURN(*out_len >= PJ_BASE256_TO_BASE64_LEN(in_len), PJ_ETOOSMALL); while (i < in_len) { c1 = *pi++; ++i; if (i == in_len) { base256to64(c1, 0, 0, 2, po); po += 4; break; } else { c2 = *pi++; ++i; if (i == in_len) { base256to64(c1, c2, 0, 1, po); po += 4; break; } else { c3 = *pi++; ++i; base256to64(c1, c2, c3, 0, po); } } po += 4; } *out_len = (int)(po - output); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_base64_decode(const pj_str_t *input, pj_uint8_t *out, int *out_len) { const char *buf = input->ptr; int len = (int)input->slen; int i, j, k; int c[4]; PJ_ASSERT_RETURN(input && out && out_len, PJ_EINVAL); while (buf[len-1] == '=' && len) --len; PJ_ASSERT_RETURN(*out_len >= PJ_BASE64_TO_BASE256_LEN(len), PJ_ETOOSMALL); for (i=0, j=0; i 1) { out[j++] = (pj_uint8_t)((c[0]<<2) | ((c[1] & 0x30)>>4)); if (k > 2) { out[j++] = (pj_uint8_t) (((c[1] & 0x0F)<<4) | ((c[2] & 0x3C)>>2)); } } break; } out[j++] = (pj_uint8_t)((c[0]<<2) | ((c[1] & 0x30)>>4)); out[j++] = (pj_uint8_t)(((c[1] & 0x0F)<<4) | ((c[2] & 0x3C)>>2)); out[j++] = (pj_uint8_t)(((c[2] & 0x03)<<6) | (c[3] & 0x3F)); } pj_assert(j < *out_len); *out_len = j; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/crc32.c ================================================ /* $Id: crc32.c 2511 2009-03-13 12:28:00Z bennylp $ */ /* * This is an implementation of CRC32. See ISO 3309 and ITU-T V.42 * for a formal specification * * This file is partly taken from Crypto++ library (http://www.cryptopp.com) * and http://www.di-mgt.com.au/crypto.html#CRC. * * Since the original version of the code is put in public domain, * this file is put on public domain as well. */ #include #define CRC32_NEGL 0xffffffffL #if defined(PJ_CRC32_HAS_TABLES) && PJ_CRC32_HAS_TABLES!=0 // crc.cpp - written and placed in the public domain by Wei Dai /* Table of CRC-32's of all single byte values (made by makecrc.c) */ #if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN != 0 #define CRC32_INDEX(c) (c & 0xff) #define CRC32_SHIFTED(c) (c >> 8) #define CRC32_SWAP(c) (c) static const pj_uint32_t crc_tab[] = { 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL }; #elif defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN != 0 #define CRC32_INDEX(c) (c >> 24) #define CRC32_SHIFTED(c) (c << 8) #define CRC32_SWAP(c) ((((c) & 0xff000000) >> 24) | \ (((c) & 0x00ff0000) >> 8) | \ (((c) & 0x0000ff00) << 8) | \ (((c) & 0x000000ff) << 24)) static const pj_uint32_t crc_tab[] = { 0x00000000L, 0x96300777L, 0x2c610eeeL, 0xba510999L, 0x19c46d07L, 0x8ff46a70L, 0x35a563e9L, 0xa395649eL, 0x3288db0eL, 0xa4b8dc79L, 0x1ee9d5e0L, 0x88d9d297L, 0x2b4cb609L, 0xbd7cb17eL, 0x072db8e7L, 0x911dbf90L, 0x6410b71dL, 0xf220b06aL, 0x4871b9f3L, 0xde41be84L, 0x7dd4da1aL, 0xebe4dd6dL, 0x51b5d4f4L, 0xc785d383L, 0x56986c13L, 0xc0a86b64L, 0x7af962fdL, 0xecc9658aL, 0x4f5c0114L, 0xd96c0663L, 0x633d0ffaL, 0xf50d088dL, 0xc8206e3bL, 0x5e10694cL, 0xe44160d5L, 0x727167a2L, 0xd1e4033cL, 0x47d4044bL, 0xfd850dd2L, 0x6bb50aa5L, 0xfaa8b535L, 0x6c98b242L, 0xd6c9bbdbL, 0x40f9bcacL, 0xe36cd832L, 0x755cdf45L, 0xcf0dd6dcL, 0x593dd1abL, 0xac30d926L, 0x3a00de51L, 0x8051d7c8L, 0x1661d0bfL, 0xb5f4b421L, 0x23c4b356L, 0x9995bacfL, 0x0fa5bdb8L, 0x9eb80228L, 0x0888055fL, 0xb2d90cc6L, 0x24e90bb1L, 0x877c6f2fL, 0x114c6858L, 0xab1d61c1L, 0x3d2d66b6L, 0x9041dc76L, 0x0671db01L, 0xbc20d298L, 0x2a10d5efL, 0x8985b171L, 0x1fb5b606L, 0xa5e4bf9fL, 0x33d4b8e8L, 0xa2c90778L, 0x34f9000fL, 0x8ea80996L, 0x18980ee1L, 0xbb0d6a7fL, 0x2d3d6d08L, 0x976c6491L, 0x015c63e6L, 0xf4516b6bL, 0x62616c1cL, 0xd8306585L, 0x4e0062f2L, 0xed95066cL, 0x7ba5011bL, 0xc1f40882L, 0x57c40ff5L, 0xc6d9b065L, 0x50e9b712L, 0xeab8be8bL, 0x7c88b9fcL, 0xdf1ddd62L, 0x492dda15L, 0xf37cd38cL, 0x654cd4fbL, 0x5861b24dL, 0xce51b53aL, 0x7400bca3L, 0xe230bbd4L, 0x41a5df4aL, 0xd795d83dL, 0x6dc4d1a4L, 0xfbf4d6d3L, 0x6ae96943L, 0xfcd96e34L, 0x468867adL, 0xd0b860daL, 0x732d0444L, 0xe51d0333L, 0x5f4c0aaaL, 0xc97c0dddL, 0x3c710550L, 0xaa410227L, 0x10100bbeL, 0x86200cc9L, 0x25b56857L, 0xb3856f20L, 0x09d466b9L, 0x9fe461ceL, 0x0ef9de5eL, 0x98c9d929L, 0x2298d0b0L, 0xb4a8d7c7L, 0x173db359L, 0x810db42eL, 0x3b5cbdb7L, 0xad6cbac0L, 0x2083b8edL, 0xb6b3bf9aL, 0x0ce2b603L, 0x9ad2b174L, 0x3947d5eaL, 0xaf77d29dL, 0x1526db04L, 0x8316dc73L, 0x120b63e3L, 0x843b6494L, 0x3e6a6d0dL, 0xa85a6a7aL, 0x0bcf0ee4L, 0x9dff0993L, 0x27ae000aL, 0xb19e077dL, 0x44930ff0L, 0xd2a30887L, 0x68f2011eL, 0xfec20669L, 0x5d5762f7L, 0xcb676580L, 0x71366c19L, 0xe7066b6eL, 0x761bd4feL, 0xe02bd389L, 0x5a7ada10L, 0xcc4add67L, 0x6fdfb9f9L, 0xf9efbe8eL, 0x43beb717L, 0xd58eb060L, 0xe8a3d6d6L, 0x7e93d1a1L, 0xc4c2d838L, 0x52f2df4fL, 0xf167bbd1L, 0x6757bca6L, 0xdd06b53fL, 0x4b36b248L, 0xda2b0dd8L, 0x4c1b0aafL, 0xf64a0336L, 0x607a0441L, 0xc3ef60dfL, 0x55df67a8L, 0xef8e6e31L, 0x79be6946L, 0x8cb361cbL, 0x1a8366bcL, 0xa0d26f25L, 0x36e26852L, 0x95770cccL, 0x03470bbbL, 0xb9160222L, 0x2f260555L, 0xbe3bbac5L, 0x280bbdb2L, 0x925ab42bL, 0x046ab35cL, 0xa7ffd7c2L, 0x31cfd0b5L, 0x8b9ed92cL, 0x1daede5bL, 0xb0c2649bL, 0x26f263ecL, 0x9ca36a75L, 0x0a936d02L, 0xa906099cL, 0x3f360eebL, 0x85670772L, 0x13570005L, 0x824abf95L, 0x147ab8e2L, 0xae2bb17bL, 0x381bb60cL, 0x9b8ed292L, 0x0dbed5e5L, 0xb7efdc7cL, 0x21dfdb0bL, 0xd4d2d386L, 0x42e2d4f1L, 0xf8b3dd68L, 0x6e83da1fL, 0xcd16be81L, 0x5b26b9f6L, 0xe177b06fL, 0x7747b718L, 0xe65a0888L, 0x706a0fffL, 0xca3b0666L, 0x5c0b0111L, 0xff9e658fL, 0x69ae62f8L, 0xd3ff6b61L, 0x45cf6c16L, 0x78e20aa0L, 0xeed20dd7L, 0x5483044eL, 0xc2b30339L, 0x612667a7L, 0xf71660d0L, 0x4d476949L, 0xdb776e3eL, 0x4a6ad1aeL, 0xdc5ad6d9L, 0x660bdf40L, 0xf03bd837L, 0x53aebca9L, 0xc59ebbdeL, 0x7fcfb247L, 0xe9ffb530L, 0x1cf2bdbdL, 0x8ac2bacaL, 0x3093b353L, 0xa6a3b424L, 0x0536d0baL, 0x9306d7cdL, 0x2957de54L, 0xbf67d923L, 0x2e7a66b3L, 0xb84a61c4L, 0x021b685dL, 0x942b6f2aL, 0x37be0bb4L, 0xa18e0cc3L, 0x1bdf055aL, 0x8def022dL }; #else # error "Endianness not defined" #endif PJ_DEF(void) pj_crc32_init(pj_crc32_context *ctx) { ctx->crc_state = 0; } PJ_DEF(pj_uint32_t) pj_crc32_update(pj_crc32_context *ctx, const pj_uint8_t *data, pj_size_t nbytes) { pj_uint32_t crc = ctx->crc_state ^ CRC32_NEGL; for( ; (((unsigned long)(pj_ssize_t)data) & 0x03) && nbytes > 0; --nbytes) { crc = crc_tab[CRC32_INDEX(crc) ^ *data++] ^ CRC32_SHIFTED(crc); } while (nbytes >= 4) { crc ^= *(const pj_uint32_t *)data; crc = crc_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); crc = crc_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); crc = crc_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); crc = crc_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); nbytes -= 4; data += 4; } while (nbytes--) { crc = crc_tab[CRC32_INDEX(crc) ^ *data++] ^ CRC32_SHIFTED(crc); } ctx->crc_state = crc ^ CRC32_NEGL; return ctx->crc_state; } PJ_DEF(pj_uint32_t) pj_crc32_final(pj_crc32_context *ctx) { return CRC32_SWAP(ctx->crc_state); } #else PJ_DEF(void) pj_crc32_init(pj_crc32_context *ctx) { ctx->crc_state = CRC32_NEGL; } PJ_DEF(pj_uint32_t) pj_crc32_update(pj_crc32_context *ctx, const pj_uint8_t *octets, pj_size_t len) { pj_uint32_t crc = ctx->crc_state; while (len--) { pj_uint32_t temp; int j; temp = (pj_uint32_t)((crc & 0xFF) ^ *octets++); for (j = 0; j < 8; j++) { if (temp & 0x1) temp = (temp >> 1) ^ 0xEDB88320; else temp >>= 1; } crc = (crc >> 8) ^ temp; } ctx->crc_state = crc; return crc ^ CRC32_NEGL; } PJ_DEF(pj_uint32_t) pj_crc32_final(pj_crc32_context *ctx) { ctx->crc_state ^= CRC32_NEGL; return ctx->crc_state; } #endif PJ_DEF(pj_uint32_t) pj_crc32_calc( const pj_uint8_t *data, pj_size_t nbytes) { pj_crc32_context ctx; pj_crc32_init(&ctx); pj_crc32_update(&ctx, data, nbytes); return pj_crc32_final(&ctx); } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/dns.c ================================================ /* $Id: dns.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include PJ_DEF(const char *) pj_dns_get_type_name(int type) { switch (type) { case PJ_DNS_TYPE_A: return "A"; case PJ_DNS_TYPE_AAAA: return "AAAA"; case PJ_DNS_TYPE_SRV: return "SRV"; case PJ_DNS_TYPE_NS: return "NS"; case PJ_DNS_TYPE_CNAME: return "CNAME"; case PJ_DNS_TYPE_PTR: return "PTR"; case PJ_DNS_TYPE_MX: return "MX"; case PJ_DNS_TYPE_TXT: return "TXT"; case PJ_DNS_TYPE_NAPTR: return "NAPTR"; } return "(Unknown)"; } static void write16(pj_uint8_t *p, pj_uint16_t val) { p[0] = (pj_uint8_t)(val >> 8); p[1] = (pj_uint8_t)(val & 0xFF); } /** * Initialize a DNS query transaction. */ PJ_DEF(pj_status_t) pj_dns_make_query( void *packet, unsigned *size, pj_uint16_t id, int qtype, const pj_str_t *name) { pj_uint8_t *p = (pj_uint8_t*)packet; const char *startlabel, *endlabel, *endname; pj_size_t d; /* Sanity check */ PJ_ASSERT_RETURN(packet && size && qtype && name, PJ_EINVAL); /* Calculate total number of bytes required. */ d = sizeof(pj_dns_hdr) + name->slen + 4; /* Check that size is sufficient. */ PJ_ASSERT_RETURN(*size >= d, PJLIB_UTIL_EDNSQRYTOOSMALL); /* Initialize header */ pj_assert(sizeof(pj_dns_hdr)==12); pj_bzero(p, sizeof(struct pj_dns_hdr)); write16(p+0, id); write16(p+2, (pj_uint16_t)PJ_DNS_SET_RD(1)); write16(p+4, (pj_uint16_t)1); /* Initialize query */ p = ((pj_uint8_t*)packet)+sizeof(pj_dns_hdr); /* Tokenize name */ startlabel = endlabel = name->ptr; endname = name->ptr + name->slen; while (endlabel != endname) { while (endlabel != endname && *endlabel != '.') ++endlabel; *p++ = (pj_uint8_t)(endlabel - startlabel); pj_memcpy(p, startlabel, endlabel-startlabel); p += (endlabel-startlabel); if (endlabel != endname && *endlabel == '.') ++endlabel; startlabel = endlabel; } *p++ = '\0'; /* Set type */ write16(p, (pj_uint16_t)qtype); p += 2; /* Set class (IN=1) */ write16(p, 1); p += 2; /* Done, calculate length */ *size = (unsigned)(p - (pj_uint8_t*)packet); return 0; } /* Get a name length (note: name consists of multiple labels and * it may contain pointers when name compression is applied) */ static pj_status_t get_name_len(int rec_counter, const pj_uint8_t *pkt, const pj_uint8_t *start, const pj_uint8_t *max, int *parsed_len, int *name_len) { const pj_uint8_t *p; pj_status_t status; /* Limit the number of recursion */ if (rec_counter > 10) { /* Too many name recursion */ return PJLIB_UTIL_EDNSINNAMEPTR; } *name_len = *parsed_len = 0; p = start; while (*p) { if ((*p & 0xc0) == 0xc0) { /* Compression is found! */ int ptr_len = 0; int dummy; pj_uint16_t offset; /* Get the 14bit offset */ pj_memcpy(&offset, p, 2); offset ^= pj_htons((pj_uint16_t)(0xc0 << 8)); offset = pj_ntohs(offset); /* Check that offset is valid */ if (offset >= max - pkt) return PJLIB_UTIL_EDNSINNAMEPTR; /* Get the name length from that offset. */ status = get_name_len(rec_counter+1, pkt, pkt + offset, max, &dummy, &ptr_len); if (status != PJ_SUCCESS) return status; *parsed_len += 2; *name_len += ptr_len; return PJ_SUCCESS; } else { unsigned label_len = *p; /* Check that label length is valid */ if (pkt+label_len > max) return PJLIB_UTIL_EDNSINNAMEPTR; p += (label_len + 1); *parsed_len += (label_len + 1); if (*p != 0) ++label_len; *name_len += label_len; if (p >= max) return PJLIB_UTIL_EDNSINSIZE; } } ++p; (*parsed_len)++; return PJ_SUCCESS; } /* Parse and copy name (note: name consists of multiple labels and * it may contain pointers when compression is applied). */ static pj_status_t get_name(int rec_counter, const pj_uint8_t *pkt, const pj_uint8_t *start, const pj_uint8_t *max, pj_str_t *name) { const pj_uint8_t *p; pj_status_t status; /* Limit the number of recursion */ if (rec_counter > 10) { /* Too many name recursion */ return PJLIB_UTIL_EDNSINNAMEPTR; } p = start; while (*p) { if ((*p & 0xc0) == 0xc0) { /* Compression is found! */ pj_uint16_t offset; /* Get the 14bit offset */ pj_memcpy(&offset, p, 2); offset ^= pj_htons((pj_uint16_t)(0xc0 << 8)); offset = pj_ntohs(offset); /* Check that offset is valid */ if (offset >= max - pkt) return PJLIB_UTIL_EDNSINNAMEPTR; /* Retrieve the name from that offset. */ status = get_name(rec_counter+1, pkt, pkt + offset, max, name); if (status != PJ_SUCCESS) return status; return PJ_SUCCESS; } else { unsigned label_len = *p; /* Check that label length is valid */ if (pkt+label_len > max) return PJLIB_UTIL_EDNSINNAMEPTR; pj_memcpy(name->ptr + name->slen, p+1, label_len); name->slen += label_len; p += label_len + 1; if (*p != 0) { *(name->ptr + name->slen) = '.'; ++name->slen; } if (p >= max) return PJLIB_UTIL_EDNSINSIZE; } } return PJ_SUCCESS; } /* Parse query records. */ static pj_status_t parse_query(pj_dns_parsed_query *q, pj_pool_t *pool, const pj_uint8_t *pkt, const pj_uint8_t *start, const pj_uint8_t *max, int *parsed_len) { const pj_uint8_t *p = start; int name_len, name_part_len; pj_status_t status; /* Get the length of the name */ status = get_name_len(0, pkt, start, max, &name_part_len, &name_len); if (status != PJ_SUCCESS) return status; /* Allocate memory for the name */ q->name.ptr = (char*) pj_pool_alloc(pool, name_len+4); q->name.slen = 0; /* Get the name */ status = get_name(0, pkt, start, max, &q->name); if (status != PJ_SUCCESS) return status; p = (start + name_part_len); /* Get the type */ pj_memcpy(&q->type, p, 2); q->type = pj_ntohs(q->type); p += 2; /* Get the class */ pj_memcpy(&q->dnsclass, p, 2); q->dnsclass = pj_ntohs(q->dnsclass); p += 2; *parsed_len = (int)(p - start); return PJ_SUCCESS; } /* Parse RR records */ static pj_status_t parse_rr(pj_dns_parsed_rr *rr, pj_pool_t *pool, const pj_uint8_t *pkt, const pj_uint8_t *start, const pj_uint8_t *max, int *parsed_len) { const pj_uint8_t *p = start; int name_len, name_part_len; pj_status_t status; /* Get the length of the name */ status = get_name_len(0, pkt, start, max, &name_part_len, &name_len); if (status != PJ_SUCCESS) return status; /* Allocate memory for the name */ rr->name.ptr = (char*) pj_pool_alloc(pool, name_len+4); rr->name.slen = 0; /* Get the name */ status = get_name(0, pkt, start, max, &rr->name); if (status != PJ_SUCCESS) return status; p = (start + name_part_len); /* Check the size can accomodate next few fields. */ if (p+10 > max) return PJLIB_UTIL_EDNSINSIZE; /* Get the type */ pj_memcpy(&rr->type, p, 2); rr->type = pj_ntohs(rr->type); p += 2; /* Get the class */ pj_memcpy(&rr->dnsclass, p, 2); rr->dnsclass = pj_ntohs(rr->dnsclass); p += 2; /* Class MUST be IN */ if (rr->dnsclass != 1) { /* Class is not IN, return error only if type is known (see #1889) */ if (rr->type == PJ_DNS_TYPE_A || rr->type == PJ_DNS_TYPE_AAAA || rr->type == PJ_DNS_TYPE_CNAME || rr->type == PJ_DNS_TYPE_NS || rr->type == PJ_DNS_TYPE_PTR || rr->type == PJ_DNS_TYPE_SRV) { return PJLIB_UTIL_EDNSINCLASS; } } /* Get TTL */ pj_memcpy(&rr->ttl, p, 4); rr->ttl = pj_ntohl(rr->ttl); p += 4; /* Get rdlength */ pj_memcpy(&rr->rdlength, p, 2); rr->rdlength = pj_ntohs(rr->rdlength); p += 2; /* Check that length is valid */ if (p + rr->rdlength > max) return PJLIB_UTIL_EDNSINSIZE; /* Parse some well known records */ if (rr->type == PJ_DNS_TYPE_A) { pj_memcpy(&rr->rdata.a.ip_addr, p, 4); p += 4; } else if (rr->type == PJ_DNS_TYPE_AAAA) { pj_memcpy(&rr->rdata.aaaa.ip_addr, p, 16); p += 16; } else if (rr->type == PJ_DNS_TYPE_CNAME || rr->type == PJ_DNS_TYPE_NS || rr->type == PJ_DNS_TYPE_PTR) { /* Get the length of the target name */ status = get_name_len(0, pkt, p, max, &name_part_len, &name_len); if (status != PJ_SUCCESS) return status; /* Allocate memory for the name */ rr->rdata.cname.name.ptr = (char*) pj_pool_alloc(pool, name_len); rr->rdata.cname.name.slen = 0; /* Get the name */ status = get_name(0, pkt, p, max, &rr->rdata.cname.name); if (status != PJ_SUCCESS) return status; p += name_part_len; } else if (rr->type == PJ_DNS_TYPE_SRV) { /* Priority */ pj_memcpy(&rr->rdata.srv.prio, p, 2); rr->rdata.srv.prio = pj_ntohs(rr->rdata.srv.prio); p += 2; /* Weight */ pj_memcpy(&rr->rdata.srv.weight, p, 2); rr->rdata.srv.weight = pj_ntohs(rr->rdata.srv.weight); p += 2; /* Port */ pj_memcpy(&rr->rdata.srv.port, p, 2); rr->rdata.srv.port = pj_ntohs(rr->rdata.srv.port); p += 2; /* Get the length of the target name */ status = get_name_len(0, pkt, p, max, &name_part_len, &name_len); if (status != PJ_SUCCESS) return status; /* Allocate memory for the name */ rr->rdata.srv.target.ptr = (char*) pj_pool_alloc(pool, name_len); rr->rdata.srv.target.slen = 0; /* Get the name */ status = get_name(0, pkt, p, max, &rr->rdata.srv.target); if (status != PJ_SUCCESS) return status; p += name_part_len; } else { /* Copy the raw data */ rr->data = pj_pool_alloc(pool, rr->rdlength); pj_memcpy(rr->data, p, rr->rdlength); p += rr->rdlength; } *parsed_len = (int)(p - start); return PJ_SUCCESS; } /* * Parse raw DNS packet into DNS packet structure. */ PJ_DEF(pj_status_t) pj_dns_parse_packet( pj_pool_t *pool, const void *packet, unsigned size, pj_dns_parsed_packet **p_res) { pj_dns_parsed_packet *res; const pj_uint8_t *start, *end; pj_status_t status; unsigned i; /* Sanity checks */ PJ_ASSERT_RETURN(pool && packet && size && p_res, PJ_EINVAL); /* Packet size must be at least as big as the header */ if (size < sizeof(pj_dns_hdr)) return PJLIB_UTIL_EDNSINSIZE; /* Create the structure */ res = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet); /* Copy the DNS header, and convert endianness to host byte order */ pj_memcpy(&res->hdr, packet, sizeof(pj_dns_hdr)); res->hdr.id = pj_ntohs(res->hdr.id); res->hdr.flags = pj_ntohs(res->hdr.flags); res->hdr.qdcount = pj_ntohs(res->hdr.qdcount); res->hdr.anscount = pj_ntohs(res->hdr.anscount); res->hdr.nscount = pj_ntohs(res->hdr.nscount); res->hdr.arcount = pj_ntohs(res->hdr.arcount); /* Mark start and end of payload */ start = ((const pj_uint8_t*)packet) + sizeof(pj_dns_hdr); end = ((const pj_uint8_t*)packet) + size; /* Parse query records (if any). */ if (res->hdr.qdcount) { res->q = (pj_dns_parsed_query*) pj_pool_zalloc(pool, res->hdr.qdcount * sizeof(pj_dns_parsed_query)); for (i=0; ihdr.qdcount; ++i) { int parsed_len = 0; status = parse_query(&res->q[i], pool, (const pj_uint8_t*)packet, start, end, &parsed_len); if (status != PJ_SUCCESS) return status; start += parsed_len; } } /* Parse answer, if any */ if (res->hdr.anscount) { res->ans = (pj_dns_parsed_rr*) pj_pool_zalloc(pool, res->hdr.anscount * sizeof(pj_dns_parsed_rr)); for (i=0; ihdr.anscount; ++i) { int parsed_len; status = parse_rr(&res->ans[i], pool, (const pj_uint8_t*)packet, start, end, &parsed_len); if (status != PJ_SUCCESS) return status; start += parsed_len; } } /* Parse authoritative NS records, if any */ if (res->hdr.nscount) { res->ns = (pj_dns_parsed_rr*) pj_pool_zalloc(pool, res->hdr.nscount * sizeof(pj_dns_parsed_rr)); for (i=0; ihdr.nscount; ++i) { int parsed_len; status = parse_rr(&res->ns[i], pool, (const pj_uint8_t*)packet, start, end, &parsed_len); if (status != PJ_SUCCESS) return status; start += parsed_len; } } /* Parse additional RR answer, if any */ if (res->hdr.arcount) { res->arr = (pj_dns_parsed_rr*) pj_pool_zalloc(pool, res->hdr.arcount * sizeof(pj_dns_parsed_rr)); for (i=0; ihdr.arcount; ++i) { int parsed_len; status = parse_rr(&res->arr[i], pool, (const pj_uint8_t*)packet, start, end, &parsed_len); if (status != PJ_SUCCESS) return status; start += parsed_len; } } /* Looks like everything is okay */ *p_res = res; return PJ_SUCCESS; } /* Perform name compression scheme. * If a name is already in the nametable, when no need to duplicate * the string with the pool, but rather just use the pointer there. */ static void apply_name_table( unsigned *count, pj_str_t nametable[], const pj_str_t *src, pj_pool_t *pool, pj_str_t *dst) { unsigned i; /* Scan strings in nametable */ for (i=0; i<*count; ++i) { if (pj_stricmp(&nametable[i], src) == 0) break; } /* If name is found in nametable, use the pointer in the nametable */ if (i != *count) { dst->ptr = nametable[i].ptr; dst->slen = nametable[i].slen; return; } /* Otherwise duplicate the string, and insert new name in nametable */ pj_strdup(pool, dst, src); if (*count < PJ_DNS_MAX_NAMES_IN_NAMETABLE) { nametable[*count].ptr = dst->ptr; nametable[*count].slen = dst->slen; ++(*count); } } static void copy_query(pj_pool_t *pool, pj_dns_parsed_query *dst, const pj_dns_parsed_query *src, unsigned *nametable_count, pj_str_t nametable[]) { pj_memcpy(dst, src, sizeof(*src)); apply_name_table(nametable_count, nametable, &src->name, pool, &dst->name); } static void copy_rr(pj_pool_t *pool, pj_dns_parsed_rr *dst, const pj_dns_parsed_rr *src, unsigned *nametable_count, pj_str_t nametable[]) { pj_memcpy(dst, src, sizeof(*src)); apply_name_table(nametable_count, nametable, &src->name, pool, &dst->name); if (src->data) { dst->data = pj_pool_alloc(pool, src->rdlength); pj_memcpy(dst->data, src->data, src->rdlength); } if (src->type == PJ_DNS_TYPE_SRV) { apply_name_table(nametable_count, nametable, &src->rdata.srv.target, pool, &dst->rdata.srv.target); } else if (src->type == PJ_DNS_TYPE_A) { dst->rdata.a.ip_addr.s_addr = src->rdata.a.ip_addr.s_addr; } else if (src->type == PJ_DNS_TYPE_AAAA) { pj_memcpy(&dst->rdata.aaaa.ip_addr, &src->rdata.aaaa.ip_addr, sizeof(pj_in6_addr)); } else if (src->type == PJ_DNS_TYPE_CNAME) { pj_strdup(pool, &dst->rdata.cname.name, &src->rdata.cname.name); } else if (src->type == PJ_DNS_TYPE_NS) { pj_strdup(pool, &dst->rdata.ns.name, &src->rdata.ns.name); } else if (src->type == PJ_DNS_TYPE_PTR) { pj_strdup(pool, &dst->rdata.ptr.name, &src->rdata.ptr.name); } } /* * Duplicate DNS packet. */ PJ_DEF(void) pj_dns_packet_dup(pj_pool_t *pool, const pj_dns_parsed_packet*p, unsigned options, pj_dns_parsed_packet **p_dst) { pj_dns_parsed_packet *dst; unsigned nametable_count = 0; #if PJ_DNS_MAX_NAMES_IN_NAMETABLE pj_str_t nametable[PJ_DNS_MAX_NAMES_IN_NAMETABLE]; #else pj_str_t *nametable = NULL; #endif unsigned i; PJ_ASSERT_ON_FAIL(pool && p && p_dst, return); /* Create packet and copy header */ *p_dst = dst = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet); pj_memcpy(&dst->hdr, &p->hdr, sizeof(p->hdr)); /* Initialize section counts in the target packet to zero. * If memory allocation fails during copying process, the target packet * should have a correct section counts. */ dst->hdr.qdcount = 0; dst->hdr.anscount = 0; dst->hdr.nscount = 0; dst->hdr.arcount = 0; /* Copy query section */ if (p->hdr.qdcount && (options & PJ_DNS_NO_QD)==0) { dst->q = (pj_dns_parsed_query*) pj_pool_alloc(pool, p->hdr.qdcount * sizeof(pj_dns_parsed_query)); for (i=0; ihdr.qdcount; ++i) { copy_query(pool, &dst->q[i], &p->q[i], &nametable_count, nametable); ++dst->hdr.qdcount; } } /* Copy answer section */ if (p->hdr.anscount && (options & PJ_DNS_NO_ANS)==0) { dst->ans = (pj_dns_parsed_rr*) pj_pool_alloc(pool, p->hdr.anscount * sizeof(pj_dns_parsed_rr)); for (i=0; ihdr.anscount; ++i) { copy_rr(pool, &dst->ans[i], &p->ans[i], &nametable_count, nametable); ++dst->hdr.anscount; } } /* Copy NS section */ if (p->hdr.nscount && (options & PJ_DNS_NO_NS)==0) { dst->ns = (pj_dns_parsed_rr*) pj_pool_alloc(pool, p->hdr.nscount * sizeof(pj_dns_parsed_rr)); for (i=0; ihdr.nscount; ++i) { copy_rr(pool, &dst->ns[i], &p->ns[i], &nametable_count, nametable); ++dst->hdr.nscount; } } /* Copy additional info section */ if (p->hdr.arcount && (options & PJ_DNS_NO_AR)==0) { dst->arr = (pj_dns_parsed_rr*) pj_pool_alloc(pool, p->hdr.arcount * sizeof(pj_dns_parsed_rr)); for (i=0; ihdr.arcount; ++i) { copy_rr(pool, &dst->arr[i], &p->arr[i], &nametable_count, nametable); ++dst->hdr.arcount; } } } PJ_DEF(void) pj_dns_init_srv_rr( pj_dns_parsed_rr *rec, const pj_str_t *res_name, unsigned dnsclass, unsigned ttl, unsigned prio, unsigned weight, unsigned port, const pj_str_t *target) { pj_bzero(rec, sizeof(*rec)); rec->name = *res_name; rec->type = PJ_DNS_TYPE_SRV; rec->dnsclass = (pj_uint16_t) dnsclass; rec->ttl = ttl; rec->rdata.srv.prio = (pj_uint16_t) prio; rec->rdata.srv.weight = (pj_uint16_t) weight; rec->rdata.srv.port = (pj_uint16_t) port; rec->rdata.srv.target = *target; } PJ_DEF(void) pj_dns_init_cname_rr( pj_dns_parsed_rr *rec, const pj_str_t *res_name, unsigned dnsclass, unsigned ttl, const pj_str_t *name) { pj_bzero(rec, sizeof(*rec)); rec->name = *res_name; rec->type = PJ_DNS_TYPE_CNAME; rec->dnsclass = (pj_uint16_t) dnsclass; rec->ttl = ttl; rec->rdata.cname.name = *name; } PJ_DEF(void) pj_dns_init_a_rr( pj_dns_parsed_rr *rec, const pj_str_t *res_name, unsigned dnsclass, unsigned ttl, const pj_in_addr *ip_addr) { pj_bzero(rec, sizeof(*rec)); rec->name = *res_name; rec->type = PJ_DNS_TYPE_A; rec->dnsclass = (pj_uint16_t) dnsclass; rec->ttl = ttl; rec->rdata.a.ip_addr = *ip_addr; } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/dns_dump.c ================================================ /* $Id: dns_dump.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #define THIS_FILE "dns_dump.c" #define LEVEL 3 static const char *spell_ttl(char *buf, int size, unsigned ttl) { #define DAY (3600*24) #define HOUR (3600) #define MINUTE (60) char *p = buf; int len; if (ttl > DAY) { len = pj_ansi_snprintf(p, size, "%dd ", ttl/DAY); if (len < 1 || len >= size) return "-err-"; size -= len; p += len; ttl %= DAY; } if (ttl > HOUR) { len = pj_ansi_snprintf(p, size, "%dh ", ttl/HOUR); if (len < 1 || len >= size) return "-err-"; size -= len; p += len; ttl %= HOUR; } if (ttl > MINUTE) { len = pj_ansi_snprintf(p, size, "%dm ", ttl/MINUTE); if (len < 1 || len >= size) return "-err-"; size -= len; p += len; ttl %= MINUTE; } if (ttl > 0) { len = pj_ansi_snprintf(p, size, "%ds ", ttl); if (len < 1 || len >= size) return "-err-"; size -= len; p += len; ttl = 0; } *p = '\0'; return buf; } static void dump_query(unsigned index, const pj_dns_parsed_query *q) { PJ_LOG(3,(THIS_FILE, " %d. Name: %.*s", index, (int)q->name.slen, q->name.ptr)); PJ_LOG(3,(THIS_FILE, " Type: %s (%d)", pj_dns_get_type_name(q->type), q->type)); PJ_LOG(3,(THIS_FILE, " Class: %s (%d)", (q->dnsclass==1 ? "IN" : ""), q->dnsclass)); } static void dump_answer(unsigned index, const pj_dns_parsed_rr *rr) { const pj_str_t root_name = { "", 6 }; const pj_str_t *name = &rr->name; char ttl_words[32]; if (name->slen == 0) name = &root_name; PJ_LOG(3,(THIS_FILE, " %d. %s record (type=%d)", index, pj_dns_get_type_name(rr->type), rr->type)); PJ_LOG(3,(THIS_FILE, " Name: %.*s", (int)name->slen, name->ptr)); PJ_LOG(3,(THIS_FILE, " TTL: %u (%s)", rr->ttl, spell_ttl(ttl_words, sizeof(ttl_words), rr->ttl))); PJ_LOG(3,(THIS_FILE, " Data length: %u", rr->rdlength)); if (rr->type == PJ_DNS_TYPE_SRV) { PJ_LOG(3,(THIS_FILE, " SRV: prio=%d, weight=%d %.*s:%d", rr->rdata.srv.prio, rr->rdata.srv.weight, (int)rr->rdata.srv.target.slen, rr->rdata.srv.target.ptr, rr->rdata.srv.port)); } else if (rr->type == PJ_DNS_TYPE_CNAME || rr->type == PJ_DNS_TYPE_NS || rr->type == PJ_DNS_TYPE_PTR) { PJ_LOG(3,(THIS_FILE, " Name: %.*s", (int)rr->rdata.cname.name.slen, rr->rdata.cname.name.ptr)); } else if (rr->type == PJ_DNS_TYPE_A) { PJ_LOG(3,(THIS_FILE, " IP address: %s", pj_inet_ntoa(rr->rdata.a.ip_addr))); } else if (rr->type == PJ_DNS_TYPE_AAAA) { char addr[PJ_INET6_ADDRSTRLEN]; PJ_LOG(3,(THIS_FILE, " IPv6 address: %s", pj_inet_ntop2(pj_AF_INET6(), &rr->rdata.aaaa.ip_addr, addr, sizeof(addr)))); } } PJ_DEF(void) pj_dns_dump_packet(const pj_dns_parsed_packet *res) { unsigned i; PJ_ASSERT_ON_FAIL(res != NULL, return); /* Header part */ PJ_LOG(3,(THIS_FILE, "Domain Name System packet (%s):", (PJ_DNS_GET_QR(res->hdr.flags) ? "response" : "query"))); PJ_LOG(3,(THIS_FILE, " Transaction ID: %d", res->hdr.id)); PJ_LOG(3,(THIS_FILE, " Flags: opcode=%d, authoritative=%d, truncated=%d, rcode=%d", PJ_DNS_GET_OPCODE(res->hdr.flags), PJ_DNS_GET_AA(res->hdr.flags), PJ_DNS_GET_TC(res->hdr.flags), PJ_DNS_GET_RCODE(res->hdr.flags))); PJ_LOG(3,(THIS_FILE, " Nb of queries: %d", res->hdr.qdcount)); PJ_LOG(3,(THIS_FILE, " Nb of answer RR: %d", res->hdr.anscount)); PJ_LOG(3,(THIS_FILE, " Nb of authority RR: %d", res->hdr.nscount)); PJ_LOG(3,(THIS_FILE, " Nb of additional RR: %d", res->hdr.arcount)); PJ_LOG(3,(THIS_FILE, "")); /* Dump queries */ if (res->hdr.qdcount) { PJ_LOG(3,(THIS_FILE, " Queries:")); for (i=0; ihdr.qdcount; ++i) { dump_query(i, &res->q[i]); } PJ_LOG(3,(THIS_FILE, "")); } /* Dump answers */ if (res->hdr.anscount) { PJ_LOG(3,(THIS_FILE, " Answers RR:")); for (i=0; ihdr.anscount; ++i) { dump_answer(i, &res->ans[i]); } PJ_LOG(3,(THIS_FILE, "")); } /* Dump NS sections */ if (res->hdr.nscount) { PJ_LOG(3,(THIS_FILE, " NS Authority RR:")); for (i=0; ihdr.nscount; ++i) { dump_answer(i, &res->ns[i]); } PJ_LOG(3,(THIS_FILE, "")); } /* Dump Additional info sections */ if (res->hdr.arcount) { PJ_LOG(3,(THIS_FILE, " Additional Info RR:")); for (i=0; ihdr.arcount; ++i) { dump_answer(i, &res->arr[i]); } PJ_LOG(3,(THIS_FILE, "")); } } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/dns_server.c ================================================ /* $Id: dns_server.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #define THIS_FILE "dns_server.c" #define MAX_ANS 16 #define MAX_PKT 1500 #define MAX_LABEL 32 struct label_tab { unsigned count; struct { unsigned pos; pj_str_t label; } a[MAX_LABEL]; }; struct rr { PJ_DECL_LIST_MEMBER(struct rr); pj_dns_parsed_rr rec; }; struct pj_dns_server { pj_pool_t *pool; pj_pool_factory *pf; pj_activesock_t *asock; pj_ioqueue_op_key_t send_key; struct rr rr_list; }; static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, void *data, pj_size_t size, const pj_sockaddr_t *src_addr, int addr_len, pj_status_t status); PJ_DEF(pj_status_t) pj_dns_server_create( pj_pool_factory *pf, pj_ioqueue_t *ioqueue, int af, unsigned port, unsigned flags, pj_dns_server **p_srv) { pj_pool_t *pool; pj_dns_server *srv; pj_sockaddr sock_addr; pj_activesock_cb sock_cb; pj_status_t status; PJ_ASSERT_RETURN(pf && ioqueue && p_srv && flags==0, PJ_EINVAL); PJ_ASSERT_RETURN(af==pj_AF_INET() || af==pj_AF_INET6(), PJ_EINVAL); pool = pj_pool_create(pf, "dnsserver", 256, 256, NULL); srv = (pj_dns_server*) PJ_POOL_ZALLOC_T(pool, pj_dns_server); srv->pool = pool; srv->pf = pf; pj_list_init(&srv->rr_list); pj_bzero(&sock_addr, sizeof(sock_addr)); sock_addr.addr.sa_family = (pj_uint16_t)af; pj_sockaddr_set_port(&sock_addr, (pj_uint16_t)port); pj_bzero(&sock_cb, sizeof(sock_cb)); sock_cb.on_data_recvfrom = &on_data_recvfrom; status = pj_activesock_create_udp(pool, &sock_addr, NULL, ioqueue, &sock_cb, srv, &srv->asock, NULL); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&srv->send_key, sizeof(srv->send_key)); status = pj_activesock_start_recvfrom(srv->asock, pool, MAX_PKT, 0); if (status != PJ_SUCCESS) goto on_error; *p_srv = srv; return PJ_SUCCESS; on_error: pj_dns_server_destroy(srv); return status; } PJ_DEF(pj_status_t) pj_dns_server_destroy(pj_dns_server *srv) { PJ_ASSERT_RETURN(srv, PJ_EINVAL); if (srv->asock) { pj_activesock_close(srv->asock); srv->asock = NULL; } if (srv->pool) { pj_pool_t *pool = srv->pool; srv->pool = NULL; pj_pool_release(pool); } return PJ_SUCCESS; } static struct rr* find_rr( pj_dns_server *srv, unsigned dns_class, unsigned type /* pj_dns_type */, const pj_str_t *name) { struct rr *r; r = srv->rr_list.next; while (r != &srv->rr_list) { if (r->rec.dnsclass == dns_class && r->rec.type == type && pj_stricmp(&r->rec.name, name)==0) { return r; } r = r->next; } return NULL; } PJ_DEF(pj_status_t) pj_dns_server_add_rec( pj_dns_server *srv, unsigned count, const pj_dns_parsed_rr rr_param[]) { unsigned i; PJ_ASSERT_RETURN(srv && count && rr_param, PJ_EINVAL); for (i=0; ipool, struct rr); pj_memcpy(&rr->rec, &rr_param[i], sizeof(pj_dns_parsed_rr)); pj_list_push_back(&srv->rr_list, rr); } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_dns_server_del_rec( pj_dns_server *srv, int dns_class, pj_dns_type type, const pj_str_t *name) { struct rr *rr; PJ_ASSERT_RETURN(srv && type && name, PJ_EINVAL); rr = find_rr(srv, dns_class, type, name); if (!rr) return PJ_ENOTFOUND; pj_list_erase(rr); return PJ_SUCCESS; } static void write16(pj_uint8_t *p, pj_uint16_t val) { p[0] = (pj_uint8_t)(val >> 8); p[1] = (pj_uint8_t)(val & 0xFF); } static void write32(pj_uint8_t *p, pj_uint32_t val) { val = pj_htonl(val); pj_memcpy(p, &val, 4); } static int print_name(pj_uint8_t *pkt, int size, pj_uint8_t *pos, const pj_str_t *name, struct label_tab *tab) { pj_uint8_t *p = pos; const char *endlabel, *endname; unsigned i; pj_str_t label; /* Check if name is in the table */ for (i=0; icount; ++i) { if (pj_strcmp(&tab->a[i].label, name)==0) break; } if (i != tab->count) { write16(p, (pj_uint16_t)(tab->a[i].pos | (0xc0 << 8))); return 2; } else { if (tab->count < MAX_LABEL) { tab->a[tab->count].pos = (unsigned)(p-pkt); tab->a[tab->count].label.ptr = (char*)(p+1); tab->a[tab->count].label.slen = name->slen; ++tab->count; } } endlabel = name->ptr; endname = name->ptr + name->slen; label.ptr = (char*)name->ptr; while (endlabel != endname) { while (endlabel != endname && *endlabel != '.') ++endlabel; label.slen = (endlabel - label.ptr); if (size < label.slen+1) return -1; *p = (pj_uint8_t)label.slen; pj_memcpy(p+1, label.ptr, label.slen); size -= (int)(label.slen+1); p += (label.slen+1); if (endlabel != endname && *endlabel == '.') ++endlabel; label.ptr = (char*)endlabel; } if (size == 0) return -1; *p++ = '\0'; return (int)(p-pos); } static int print_rr(pj_uint8_t *pkt, int size, pj_uint8_t *pos, const pj_dns_parsed_rr *rr, struct label_tab *tab) { pj_uint8_t *p = pos; int len; len = print_name(pkt, size, pos, &rr->name, tab); if (len < 0) return -1; p += len; size -= len; if (size < 8) return -1; pj_assert(rr->dnsclass == 1); write16(p+0, (pj_uint16_t)rr->type); /* type */ write16(p+2, (pj_uint16_t)rr->dnsclass); /* class */ write32(p+4, rr->ttl); /* TTL */ p += 8; size -= 8; if (rr->type == PJ_DNS_TYPE_A) { if (size < 6) return -1; /* RDLEN is 4 */ write16(p, 4); /* Address */ pj_memcpy(p+2, &rr->rdata.a.ip_addr, 4); p += 6; size -= 6; } else if (rr->type == PJ_DNS_TYPE_CNAME || rr->type == PJ_DNS_TYPE_NS || rr->type == PJ_DNS_TYPE_PTR) { if (size < 4) return -1; len = print_name(pkt, size-2, p+2, &rr->rdata.cname.name, tab); if (len < 0) return -1; write16(p, (pj_uint16_t)len); p += (len + 2); size -= (len + 2); } else if (rr->type == PJ_DNS_TYPE_SRV) { if (size < 10) return -1; write16(p+2, rr->rdata.srv.prio); /* Priority */ write16(p+4, rr->rdata.srv.weight); /* Weight */ write16(p+6, rr->rdata.srv.port); /* Port */ /* Target */ len = print_name(pkt, size-8, p+8, &rr->rdata.srv.target, tab); if (len < 0) return -1; /* RDLEN */ write16(p, (pj_uint16_t)(len + 6)); p += (len + 8); size -= (len + 8); } else { pj_assert(!"Not supported"); return -1; } return (int)(p-pos); } static int print_packet(const pj_dns_parsed_packet *rec, pj_uint8_t *pkt, int size) { pj_uint8_t *p = pkt; struct label_tab tab; int i, len; tab.count = 0; pj_assert(sizeof(pj_dns_hdr)==12); if (size < (int)sizeof(pj_dns_hdr)) return -1; /* Initialize header */ write16(p+0, rec->hdr.id); write16(p+2, rec->hdr.flags); write16(p+4, rec->hdr.qdcount); write16(p+6, rec->hdr.anscount); write16(p+8, rec->hdr.nscount); write16(p+10, rec->hdr.arcount); p = pkt + sizeof(pj_dns_hdr); size -= sizeof(pj_dns_hdr); /* Print queries */ for (i=0; ihdr.qdcount; ++i) { len = print_name(pkt, size, p, &rec->q[i].name, &tab); if (len < 0) return -1; p += len; size -= len; if (size < 4) return -1; /* Set type */ write16(p+0, (pj_uint16_t)rec->q[i].type); /* Set class (IN=1) */ pj_assert(rec->q[i].dnsclass == 1); write16(p+2, rec->q[i].dnsclass); p += 4; } /* Print answers */ for (i=0; ihdr.anscount; ++i) { len = print_rr(pkt, size, p, &rec->ans[i], &tab); if (len < 0) return -1; p += len; size -= len; } /* Print NS records */ for (i=0; ihdr.nscount; ++i) { len = print_rr(pkt, size, p, &rec->ns[i], &tab); if (len < 0) return -1; p += len; size -= len; } /* Print additional records */ for (i=0; ihdr.arcount; ++i) { len = print_rr(pkt, size, p, &rec->arr[i], &tab); if (len < 0) return -1; p += len; size -= len; } return (int)(p - pkt); } static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, void *data, pj_size_t size, const pj_sockaddr_t *src_addr, int addr_len, pj_status_t status) { pj_dns_server *srv; pj_pool_t *pool; pj_dns_parsed_packet *req; pj_dns_parsed_packet ans; struct rr *rr; pj_ssize_t pkt_len; unsigned i; if (status != PJ_SUCCESS) return PJ_TRUE; srv = (pj_dns_server*) pj_activesock_get_user_data(asock); pool = pj_pool_create(srv->pf, "dnssrvrx", 512, 256, NULL); status = pj_dns_parse_packet(pool, data, (unsigned)size, &req); if (status != PJ_SUCCESS) { char addrinfo[PJ_INET6_ADDRSTRLEN+10]; pj_sockaddr_print(src_addr, addrinfo, sizeof(addrinfo), 3); PJ_LOG(4,(THIS_FILE, "Error parsing query from %s", addrinfo)); goto on_return; } /* Init answer */ pj_bzero(&ans, sizeof(ans)); ans.hdr.id = req->hdr.id; ans.hdr.qdcount = 1; ans.q = (pj_dns_parsed_query*) PJ_POOL_ALLOC_T(pool, pj_dns_parsed_query); pj_memcpy(ans.q, req->q, sizeof(pj_dns_parsed_query)); if (req->hdr.qdcount != 1) { ans.hdr.flags = PJ_DNS_SET_RCODE(PJ_DNS_RCODE_FORMERR); goto send_pkt; } if (req->q[0].dnsclass != PJ_DNS_CLASS_IN) { ans.hdr.flags = PJ_DNS_SET_RCODE(PJ_DNS_RCODE_NOTIMPL); goto send_pkt; } /* Find the record */ rr = find_rr(srv, req->q->dnsclass, req->q->type, &req->q->name); if (rr == NULL) { ans.hdr.flags = PJ_DNS_SET_RCODE(PJ_DNS_RCODE_NXDOMAIN); goto send_pkt; } /* Init answer record */ ans.hdr.anscount = 0; ans.ans = (pj_dns_parsed_rr*) pj_pool_calloc(pool, MAX_ANS, sizeof(pj_dns_parsed_rr)); /* DNS SRV query needs special treatment since it returns multiple * records */ if (req->q->type == PJ_DNS_TYPE_SRV) { struct rr *r; r = srv->rr_list.next; while (r != &srv->rr_list) { if (r->rec.dnsclass == req->q->dnsclass && r->rec.type == PJ_DNS_TYPE_SRV && pj_stricmp(&r->rec.name, &req->q->name)==0 && ans.hdr.anscount < MAX_ANS) { pj_memcpy(&ans.ans[ans.hdr.anscount], &r->rec, sizeof(pj_dns_parsed_rr)); ++ans.hdr.anscount; } r = r->next; } } else { /* Otherwise just copy directly from the server record */ pj_memcpy(&ans.ans[ans.hdr.anscount], &rr->rec, sizeof(pj_dns_parsed_rr)); ++ans.hdr.anscount; } /* For each CNAME entry, add A entry */ for (i=0; irec, sizeof(pj_dns_parsed_rr)); ++ans.hdr.anscount; } } send_pkt: pkt_len = print_packet(&ans, (pj_uint8_t*)data, MAX_PKT); if (pkt_len < 1) { PJ_LOG(4,(THIS_FILE, "Error: answer too large")); goto on_return; } status = pj_activesock_sendto(srv->asock, &srv->send_key, data, &pkt_len, 0, src_addr, addr_len); if (status != PJ_SUCCESS && status != PJ_EPENDING) { PJ_LOG(4,(THIS_FILE, "Error sending answer, status=%d", status)); goto on_return; } on_return: pj_pool_release(pool); return PJ_TRUE; } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/errno.c ================================================ /* $Id: errno.c 4440 2013-03-14 07:18:13Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include /* PJLIB_UTIL's own error codes/messages * MUST KEEP THIS ARRAY SORTED!! * Message must be limited to 64 chars! */ #if defined(PJ_HAS_ERROR_STRING) && PJ_HAS_ERROR_STRING!=0 static const struct { int code; const char *msg; } err_str[] = { /* STUN errors */ PJ_BUILD_ERR( PJLIB_UTIL_ESTUNRESOLVE, "Unable to resolve STUN server" ), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNINMSGTYPE, "Unknown STUN message type" ), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNINMSGLEN, "Invalid STUN message length" ), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNINATTRLEN, "STUN attribute length error" ), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNINATTRTYPE, "Invalid STUN attribute type" ), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNININDEX, "Invalid STUN server/socket index" ), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOBINDRES, "No STUN binding response in the message" ), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNRECVERRATTR, "Received STUN error attribute" ), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOMAP, "No STUN mapped address attribute" ), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOTRESPOND, "Received no response from STUN server" ), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNSYMMETRIC, "Symetric NAT detected by STUN" ), /* XML errors */ PJ_BUILD_ERR( PJLIB_UTIL_EINXML, "Invalid XML message" ), /* JSON errors */ PJ_BUILD_ERR( PJLIB_UTIL_EINJSON, "Invalid JSON document" ), /* DNS errors */ PJ_BUILD_ERR( PJLIB_UTIL_EDNSQRYTOOSMALL, "DNS query packet buffer is too small"), PJ_BUILD_ERR( PJLIB_UTIL_EDNSINSIZE, "Invalid DNS packet length"), PJ_BUILD_ERR( PJLIB_UTIL_EDNSINCLASS, "Invalid DNS class"), PJ_BUILD_ERR( PJLIB_UTIL_EDNSINNAMEPTR, "Invalid DNS name pointer"), PJ_BUILD_ERR( PJLIB_UTIL_EDNSINNSADDR, "Invalid DNS nameserver address"), PJ_BUILD_ERR( PJLIB_UTIL_EDNSNONS, "No nameserver is in DNS resolver"), PJ_BUILD_ERR( PJLIB_UTIL_EDNSNOWORKINGNS, "No working DNS nameserver"), PJ_BUILD_ERR( PJLIB_UTIL_EDNSNOANSWERREC, "No answer record in the DNS response"), PJ_BUILD_ERR( PJLIB_UTIL_EDNSINANSWER, "Invalid DNS answer"), PJ_BUILD_ERR( PJLIB_UTIL_EDNS_FORMERR, "DNS \"Format error\""), PJ_BUILD_ERR( PJLIB_UTIL_EDNS_SERVFAIL, "DNS \"Server failure\""), PJ_BUILD_ERR( PJLIB_UTIL_EDNS_NXDOMAIN, "DNS \"Name Error\""), PJ_BUILD_ERR( PJLIB_UTIL_EDNS_NOTIMPL, "DNS \"Not Implemented\""), PJ_BUILD_ERR( PJLIB_UTIL_EDNS_REFUSED, "DNS \"Refused\""), PJ_BUILD_ERR( PJLIB_UTIL_EDNS_YXDOMAIN, "DNS \"The name exists\""), PJ_BUILD_ERR( PJLIB_UTIL_EDNS_YXRRSET, "DNS \"The RRset (name, type) exists\""), PJ_BUILD_ERR( PJLIB_UTIL_EDNS_NXRRSET, "DNS \"The RRset (name, type) does not exist\""), PJ_BUILD_ERR( PJLIB_UTIL_EDNS_NOTAUTH, "DNS \"Not authorized\""), PJ_BUILD_ERR( PJLIB_UTIL_EDNS_NOTZONE, "DNS \"The zone specified is not a zone\""), /* STUN */ PJ_BUILD_ERR( PJLIB_UTIL_ESTUNTOOMANYATTR, "Too many STUN attributes"), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNUNKNOWNATTR, "Unknown STUN attribute"), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNINADDRLEN, "Invalid STUN socket address length"), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNIPV6NOTSUPP, "STUN IPv6 attribute not supported"), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOTRESPONSE, "Expecting STUN response message"), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNINVALIDID, "STUN transaction ID mismatch"), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOHANDLER, "Unable to find STUN handler for the request"), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNMSGINTPOS, "Found non-FINGERPRINT attr. after MESSAGE-INTEGRITY"), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNFINGERPOS, "Found STUN attribute after FINGERPRINT"), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOUSERNAME, "Missing STUN USERNAME attribute"), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNMSGINT, "Missing/invalid STUN MESSAGE-INTEGRITY attribute"), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNDUPATTR, "Found duplicate STUN attribute"), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOREALM, "Missing STUN REALM attribute"), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNONCE, "Missing/stale STUN NONCE attribute value"), PJ_BUILD_ERR( PJLIB_UTIL_ESTUNTSXFAILED, "STUN transaction terminates with failure"), /* HTTP Client */ PJ_BUILD_ERR( PJLIB_UTIL_EHTTPINURL, "Invalid URL format"), PJ_BUILD_ERR( PJLIB_UTIL_EHTTPINPORT, "Invalid URL port number"), PJ_BUILD_ERR( PJLIB_UTIL_EHTTPINCHDR, "Incomplete response header received"), PJ_BUILD_ERR( PJLIB_UTIL_EHTTPINSBUF, "Insufficient buffer"), PJ_BUILD_ERR( PJLIB_UTIL_EHTTPLOST, "Connection lost"), /* CLI */ PJ_BUILD_ERR( PJ_CLI_EEXIT, "Exit current session"), PJ_BUILD_ERR( PJ_CLI_EMISSINGARG, "Missing argument"), PJ_BUILD_ERR( PJ_CLI_ETOOMANYARGS, "Too many arguments"), PJ_BUILD_ERR( PJ_CLI_EINVARG, "Invalid argument"), PJ_BUILD_ERR( PJ_CLI_EBADNAME, "Command name already exists"), PJ_BUILD_ERR( PJ_CLI_EBADID, "Command id already exists"), PJ_BUILD_ERR( PJ_CLI_EBADXML, "Invalid XML format"), PJ_BUILD_ERR( PJ_CLI_ETELNETLOST, "Connection lost"), }; #endif /* PJ_HAS_ERROR_STRING */ /* * pjlib_util_strerror() */ pj_str_t pjlib_util_strerror(pj_status_t statcode, char *buf, pj_size_t bufsize ) { pj_str_t errstr; #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) if (statcode >= PJLIB_UTIL_ERRNO_START && statcode < PJLIB_UTIL_ERRNO_START + PJ_ERRNO_SPACE_SIZE) { /* Find the error in the table. * Use binary search! */ int first = 0; int n = PJ_ARRAY_SIZE(err_str); while (n > 0) { int half = n/2; int mid = first + half; if (err_str[mid].code < statcode) { first = mid+1; n -= (half+1); } else if (err_str[mid].code > statcode) { n = half; } else { first = mid; break; } } if (PJ_ARRAY_SIZE(err_str) && err_str[first].code == statcode) { pj_str_t msg; msg.ptr = (char*)err_str[first].msg; msg.slen = pj_ansi_strlen(err_str[first].msg); errstr.ptr = buf; pj_strncpy_with_null(&errstr, &msg, bufsize); return errstr; } } #endif /* PJ_HAS_ERROR_STRING */ /* Error not found. */ errstr.ptr = buf; errstr.slen = pj_ansi_snprintf(buf, bufsize, "Unknown pjlib-util error %d", statcode); if (errstr.slen < 1 || errstr.slen >= (pj_ssize_t)bufsize) errstr.slen = bufsize - 1; return errstr; } PJ_DEF(pj_status_t) pjlib_util_init(void) { pj_status_t status; status = pj_register_strerror(PJLIB_UTIL_ERRNO_START, PJ_ERRNO_SPACE_SIZE, &pjlib_util_strerror); pj_assert(status == PJ_SUCCESS); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/getopt.c ================================================ /* $Id: getopt.c 3550 2011-05-05 05:33:27Z nanang $ */ /* * pj_getopt entry points * * modified by Mike Borella */ #include #include /* Internal only. Users should not call this directly. */ static int _getopt_internal (int argc, char *const *argv, const char *shortopts, const struct pj_getopt_option *longopts, int *longind, int long_only); /* pj_getopt_long and pj_getopt_long_only entry points for GNU pj_getopt. Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ # define GETOPT_INTERFACE_VERSION 2 int pj_getopt_long (int argc, char *const *argv, const char *options, const struct pj_getopt_option *long_options, int *opt_index) { return _getopt_internal (argc, argv, options, long_options, opt_index, 0); } /* Like pj_getopt_long, but '-' as well as '--' can indicate a long option. If an option that starts with '-' (not '--') doesn't match a long option, but does match a short option, it is parsed as a short option instead. */ int pj_getopt (int argc, char * const * argv, const char * optstring) { return _getopt_internal (argc, argv, optstring, (const struct pj_getopt_option *) 0, (int *) 0, 0); } #define _(msgid) (msgid) /* This version of `pj_getopt' appears to the caller like standard Unix `pj_getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `pj_getopt' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ /* For communication from `pj_getopt' to the caller. When `pj_getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *pj_optarg = NULL; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `pj_getopt'. On entry to `pj_getopt', zero means this is the first call; initialize. When `pj_getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `pj_optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* 1003.2 says this must be 1 before any call. */ int pj_optind = 1; /* Formerly, initialization of pj_getopt depended on pj_optind==0, which causes problems with re-calling pj_getopt as programs generally don't know that. */ static int __getopt_initialized = 0; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own pj_getopt implementation. */ int pj_optopt = '?'; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `pj_getopt' to return -1 with `pj_optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ static char *posixly_correct; static char * my_index (const char *str, int chr) { while (*str) { if (*str == chr) return (char *) str; str++; } return 0; } /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; # define SWAP_FLAGS(ch1, ch2) /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,pj_optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ static void exchange (char **argv) { int bottom = first_nonopt; int middle = last_nonopt; int top = pj_optind; char *tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { /* Bottom segment is the short one. */ int len = middle - bottom; register int i; /* Swap it with the top part of the top segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); } /* Exclude the moved bottom segment from further swapping. */ top -= len; } else { /* Top segment is the short one. */ int len = top - middle; register int i; /* Swap it with the bottom part of the bottom segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; SWAP_FLAGS (bottom + i, middle + i); } /* Exclude the moved top segment from further swapping. */ bottom += len; } } /* Update records for the slots the non-options now occupy. */ first_nonopt += (pj_optind - last_nonopt); last_nonopt = pj_optind; } /* Initialize the internal data when the first call is made. */ static const char *_getopt_initialize (int argc, char *const *argv, const char *optstring) { PJ_UNUSED_ARG(argc); PJ_UNUSED_ARG(argv); /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ first_nonopt = last_nonopt = pj_optind; nextchar = NULL; //posixly_correct = getenv ("POSIXLY_CORRECT"); posixly_correct = NULL; /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (posixly_correct != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; return optstring; } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `pj_getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `pj_getopt' finds another option character, it returns that character, updating `pj_optind' and `nextchar' so that the next call to `pj_getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `pj_getopt' returns -1. Then `pj_optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return '?' after printing an error message. If you set `pj_opterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `pj_optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `pj_optarg', otherwise `pj_optarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `pj_getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct pj_getopt_option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ static int _getopt_internal (int argc, char *const *argv, const char *optstring, const struct pj_getopt_option *longopts, int *longind, int long_only) { pj_optarg = NULL; if (pj_optind == 0 || !__getopt_initialized) { if (pj_optind == 0) pj_optind = 1; /* Don't scan ARGV[0], the program name. */ optstring = _getopt_initialize (argc, argv, optstring); __getopt_initialized = 1; } /* Test whether ARGV[pj_optind] points to a non-option argument. Either it does not have option syntax, or there is an environment flag from the shell indicating it is not an option. The later information is only used when the used in the GNU libc. */ #define NONOPTION_P (argv[pj_optind][0] != '-' || argv[pj_optind][1] == '\0') if (nextchar == NULL || *nextchar == '\0') { /* Advance to the next ARGV-element. */ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been moved back by the user (who may also have changed the arguments). */ if (last_nonopt > pj_optind) last_nonopt = pj_optind; if (first_nonopt > pj_optind) first_nonopt = pj_optind; if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != pj_optind) exchange ((char **) argv); else if (last_nonopt != pj_optind) first_nonopt = pj_optind; /* Skip any additional non-options and extend the range of non-options previously skipped. */ while (pj_optind < argc && NONOPTION_P) pj_optind++; last_nonopt = pj_optind; } /* The special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (pj_optind != argc && !pj_ansi_strcmp(argv[pj_optind], "--")) { pj_optind++; if (first_nonopt != last_nonopt && last_nonopt != pj_optind) exchange ((char **) argv); else if (first_nonopt == last_nonopt) first_nonopt = pj_optind; last_nonopt = argc; pj_optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (pj_optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) pj_optind = first_nonopt; return -1; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if (NONOPTION_P) { if (ordering == REQUIRE_ORDER) return -1; pj_optarg = argv[pj_optind++]; return 1; } /* We have found another option-ARGV-element. Skip the initial punctuation. */ nextchar = (argv[pj_optind] + 1 + (longopts != NULL && argv[pj_optind][1] == '-')); } /* Decode the current option-ARGV-element. */ /* Check whether the ARGV-element is a long option. If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't consider it an abbreviated form of a long option that starts with f. Otherwise there would be no way to give the -f short option. On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg "u". This distinction seems to be the most useful approach. */ if (longopts != NULL && (argv[pj_optind][1] == '-' || (long_only && (argv[pj_optind][2] || !my_index (optstring, argv[pj_optind][1]))))) { char *nameend; const struct pj_getopt_option *p; const struct pj_getopt_option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = -1; int option_index; for (nameend = nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == (unsigned int) strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { nextchar += strlen (nextchar); pj_optind++; pj_optopt = 0; return '?'; } if (pfound != NULL) { option_index = indfound; pj_optind++; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) pj_optarg = nameend + 1; else { nextchar += strlen (nextchar); pj_optopt = pfound->val; return '?'; } } else if (pfound->has_arg == 1) { if (pj_optind < argc) pj_optarg = argv[pj_optind++]; else { nextchar += strlen (nextchar); pj_optopt = pfound->val; return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not pj_getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[pj_optind][1] == '-' || my_index (optstring, *nextchar) == NULL) { nextchar = (char *) ""; pj_optind++; pj_optopt = 0; return '?'; } } /* Look at and handle the next short option-character. */ { char c = *nextchar++; char *temp = my_index (optstring, c); /* Increment `pj_optind' when we start to process its last character. */ if (*nextchar == '\0') ++pj_optind; if (temp == NULL || c == ':') { pj_optopt = c; return '?'; } /* Convenience. Treat POSIX -W foo same as long option --foo */ if (temp[0] == 'W' && temp[1] == ';') { char *nameend; const struct pj_getopt_option *p; const struct pj_getopt_option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; /* This is an option that requires an argument. */ if (*nextchar != '\0') { pj_optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ pj_optind++; } else if (pj_optind == argc) { pj_optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; return c; } else /* We already incremented `pj_optind' once; increment it again when taking next ARGV-elt as argument. */ pj_optarg = argv[pj_optind++]; /* pj_optarg is now the argument, see if it's in the table of longopts. */ for (nextchar = nameend = pj_optarg; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { nextchar += strlen (nextchar); pj_optind++; return '?'; } if (pfound != NULL) { option_index = indfound; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) pj_optarg = nameend + 1; else { nextchar += strlen (nextchar); return '?'; } } else if (pfound->has_arg == 1) { if (pj_optind < argc) pj_optarg = argv[pj_optind++]; else { nextchar += strlen (nextchar); return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } nextchar = NULL; return 'W'; /* Let the application handle it. */ } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { pj_optarg = nextchar; pj_optind++; } else pj_optarg = NULL; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { pj_optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ pj_optind++; } else if (pj_optind == argc) { pj_optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; } else /* We already incremented `pj_optind' once; increment it again when taking next ARGV-elt as argument. */ pj_optarg = argv[pj_optind++]; nextchar = NULL; } } return c; } } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/hmac_md5.c ================================================ /* $Id: hmac_md5.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include PJ_DEF(void) pj_hmac_md5_init(pj_hmac_md5_context *hctx, const pj_uint8_t *key, unsigned key_len) { pj_uint8_t k_ipad[64]; pj_uint8_t tk[16]; int i; /* if key is longer than 64 bytes reset it to key=MD5(key) */ if (key_len > 64) { pj_md5_context tctx; pj_md5_init(&tctx); pj_md5_update(&tctx, key, key_len); pj_md5_final(&tctx, tk); key = tk; key_len = 16; } /* * HMAC = H(K XOR opad, H(K XOR ipad, text)) */ /* start out by storing key in pads */ pj_bzero( k_ipad, sizeof(k_ipad)); pj_bzero( hctx->k_opad, sizeof(hctx->k_opad)); pj_memcpy( k_ipad, key, key_len); pj_memcpy( hctx->k_opad, key, key_len); /* XOR key with ipad and opad values */ for (i=0; i<64; i++) { k_ipad[i] ^= 0x36; hctx->k_opad[i] ^= 0x5c; } /* * perform inner MD5 */ pj_md5_init(&hctx->context); pj_md5_update(&hctx->context, k_ipad, 64); } PJ_DEF(void) pj_hmac_md5_update(pj_hmac_md5_context *hctx, const pj_uint8_t *input, unsigned input_len) { pj_md5_update(&hctx->context, input, input_len); } PJ_DEF(void) pj_hmac_md5_final(pj_hmac_md5_context *hctx, pj_uint8_t digest[16]) { pj_md5_final(&hctx->context, digest); /* * perform outer MD5 */ pj_md5_init(&hctx->context); pj_md5_update(&hctx->context, hctx->k_opad, 64); pj_md5_update(&hctx->context, digest, 16); pj_md5_final(&hctx->context, digest); } PJ_DEF(void) pj_hmac_md5( const pj_uint8_t *input, unsigned input_len, const pj_uint8_t *key, unsigned key_len, pj_uint8_t digest[16] ) { pj_hmac_md5_context ctx; pj_hmac_md5_init(&ctx, key, key_len); pj_hmac_md5_update(&ctx, input, input_len); pj_hmac_md5_final(&ctx, digest); } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/hmac_sha1.c ================================================ /* $Id: hmac_sha1.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include PJ_DEF(void) pj_hmac_sha1_init(pj_hmac_sha1_context *hctx, const pj_uint8_t *key, unsigned key_len) { pj_uint8_t k_ipad[64]; pj_uint8_t tk[20]; unsigned i; /* if key is longer than 64 bytes reset it to key=SHA1(key) */ if (key_len > 64) { pj_sha1_context tctx; pj_sha1_init(&tctx); pj_sha1_update(&tctx, key, key_len); pj_sha1_final(&tctx, tk); key = tk; key_len = 20; } /* * HMAC = H(K XOR opad, H(K XOR ipad, text)) */ /* start out by storing key in pads */ pj_bzero( k_ipad, sizeof(k_ipad)); pj_bzero( hctx->k_opad, sizeof(hctx->k_opad)); pj_memcpy( k_ipad, key, key_len); pj_memcpy( hctx->k_opad, key, key_len); /* XOR key with ipad and opad values */ for (i=0; i<64; i++) { k_ipad[i] ^= 0x36; hctx->k_opad[i] ^= 0x5c; } /* * perform inner SHA1 */ pj_sha1_init(&hctx->context); pj_sha1_update(&hctx->context, k_ipad, 64); } PJ_DEF(void) pj_hmac_sha1_update(pj_hmac_sha1_context *hctx, const pj_uint8_t *input, unsigned input_len) { pj_sha1_update(&hctx->context, input, input_len); } PJ_DEF(void) pj_hmac_sha1_final(pj_hmac_sha1_context *hctx, pj_uint8_t digest[20]) { pj_sha1_final(&hctx->context, digest); /* * perform outer SHA1 */ pj_sha1_init(&hctx->context); pj_sha1_update(&hctx->context, hctx->k_opad, 64); pj_sha1_update(&hctx->context, digest, 20); pj_sha1_final(&hctx->context, digest); } PJ_DEF(void) pj_hmac_sha1(const pj_uint8_t *input, unsigned input_len, const pj_uint8_t *key, unsigned key_len, pj_uint8_t digest[20] ) { pj_hmac_sha1_context ctx; pj_hmac_sha1_init(&ctx, key, key_len); pj_hmac_sha1_update(&ctx, input, input_len); pj_hmac_sha1_final(&ctx, digest); } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/http_client.c ================================================ /* $Id: http_client.c 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "http_client.c" #if 0 /* Enable some tracing */ #define TRACE_(arg) PJ_LOG(3,arg) #else #define TRACE_(arg) #endif #define NUM_PROTOCOL 2 #define HTTP_1_0 "1.0" #define HTTP_1_1 "1.1" #define CONTENT_LENGTH "Content-Length" /* Buffer size for sending/receiving messages. */ #define BUF_SIZE 2048 /* Initial data buffer size to store the data in case content- * length is not specified in the server's response. */ #define INITIAL_DATA_BUF_SIZE 2048 #define INITIAL_POOL_SIZE 1024 #define POOL_INCREMENT_SIZE 512 enum http_protocol { PROTOCOL_HTTP, PROTOCOL_HTTPS }; static const char *http_protocol_names[NUM_PROTOCOL] = { "HTTP", "HTTPS" }; static const unsigned int http_default_port[NUM_PROTOCOL] = { 80, 443 }; enum http_method { HTTP_GET, HTTP_PUT, HTTP_DELETE }; static const char *http_method_names[3] = { "GET", "PUT", "DELETE" }; enum http_state { IDLE, CONNECTING, SENDING_REQUEST, SENDING_REQUEST_BODY, REQUEST_SENT, READING_RESPONSE, READING_DATA, READING_COMPLETE, ABORTING, }; enum auth_state { AUTH_NONE, /* Not authenticating */ AUTH_RETRYING, /* New request with auth has been submitted */ AUTH_DONE /* Done retrying the request with auth. */ }; struct pj_http_req { pj_str_t url; /* Request URL */ pj_http_url hurl; /* Parsed request URL */ pj_sockaddr addr; /* The host's socket address */ pj_http_req_param param; /* HTTP request parameters */ pj_pool_t *pool; /* Pool to allocate memory from */ pj_timer_heap_t *timer; /* Timer for timeout management */ pj_ioqueue_t *ioqueue; /* Ioqueue to use */ pj_http_req_callback cb; /* Callbacks */ pj_activesock_t *asock; /* Active socket */ pj_status_t error; /* Error status */ pj_str_t buffer; /* Buffer to send/receive msgs */ enum http_state state; /* State of the HTTP request */ enum auth_state auth_state; /* Authentication state */ pj_timer_entry timer_entry;/* Timer entry */ pj_bool_t resolved; /* Whether URL's host is resolved */ pj_http_resp response; /* HTTP response */ pj_ioqueue_op_key_t op_key; struct tcp_state { /* Total data sent so far if the data is sent in segments (i.e. * if on_send_data() is not NULL and if param.reqdata.total_size > 0) */ pj_size_t tot_chunk_size; /* Size of data to be sent (in a single activesock operation).*/ pj_size_t send_size; /* Data size sent so far. */ pj_size_t current_send_size; /* Total data received so far. */ pj_size_t current_read_size; } tcp_state; }; /* Start sending the request */ static pj_status_t http_req_start_sending(pj_http_req *hreq); /* Start reading the response */ static pj_status_t http_req_start_reading(pj_http_req *hreq); /* End the request */ static pj_status_t http_req_end_request(pj_http_req *hreq); /* Parse the header data and populate the header fields with the result. */ static pj_status_t http_headers_parse(char *hdata, pj_size_t size, pj_http_headers *headers); /* Parse the response */ static pj_status_t http_response_parse(pj_pool_t *pool, pj_http_resp *response, void *data, pj_size_t size, pj_size_t *remainder); /* Restart the request with authentication */ static void restart_req_with_auth(pj_http_req *hreq); /* Parse authentication challenge */ static pj_status_t parse_auth_chal(pj_pool_t *pool, pj_str_t *input, pj_http_auth_chal *chal); static pj_uint16_t get_http_default_port(const pj_str_t *protocol) { int i; for (i = 0; i < NUM_PROTOCOL; i++) { if (!pj_stricmp2(protocol, http_protocol_names[i])) { return (pj_uint16_t)http_default_port[i]; } } return 0; } static const char * get_protocol(const pj_str_t *protocol) { int i; for (i = 0; i < NUM_PROTOCOL; i++) { if (!pj_stricmp2(protocol, http_protocol_names[i])) { return http_protocol_names[i]; } } /* Should not happen */ pj_assert(0); return NULL; } /* Syntax error handler for parser. */ static void on_syntax_error(pj_scanner *scanner) { PJ_UNUSED_ARG(scanner); PJ_THROW(PJ_EINVAL); // syntax error } /* Callback when connection is established to the server */ static pj_bool_t http_on_connect(pj_activesock_t *asock, pj_status_t status) { pj_http_req *hreq = (pj_http_req*) pj_activesock_get_user_data(asock); if (hreq->state == ABORTING || hreq->state == IDLE) return PJ_FALSE; if (status != PJ_SUCCESS) { hreq->error = status; pj_http_req_cancel(hreq, PJ_TRUE); return PJ_FALSE; } /* OK, we are connected. Start sending the request */ hreq->state = SENDING_REQUEST; http_req_start_sending(hreq); return PJ_TRUE; } static pj_bool_t http_on_data_sent(pj_activesock_t *asock, pj_ioqueue_op_key_t *op_key, pj_ssize_t sent) { pj_http_req *hreq = (pj_http_req*) pj_activesock_get_user_data(asock); PJ_UNUSED_ARG(op_key); if (hreq->state == ABORTING || hreq->state == IDLE) return PJ_FALSE; if (sent <= 0) { hreq->error = (sent < 0 ? (pj_status_t)-sent : PJLIB_UTIL_EHTTPLOST); pj_http_req_cancel(hreq, PJ_TRUE); return PJ_FALSE; } hreq->tcp_state.current_send_size += sent; TRACE_((THIS_FILE, "\nData sent: %d out of %d bytes", hreq->tcp_state.current_send_size, hreq->tcp_state.send_size)); if (hreq->tcp_state.current_send_size == hreq->tcp_state.send_size) { /* Find out whether there is a request body to send. */ if (hreq->param.reqdata.total_size > 0 || hreq->param.reqdata.size > 0) { if (hreq->state == SENDING_REQUEST) { /* Start sending the request body */ hreq->state = SENDING_REQUEST_BODY; hreq->tcp_state.tot_chunk_size = 0; pj_assert(hreq->param.reqdata.total_size == 0 || (hreq->param.reqdata.total_size > 0 && hreq->param.reqdata.size == 0)); } else { /* Continue sending the next chunk of the request body */ hreq->tcp_state.tot_chunk_size += hreq->tcp_state.send_size; if (hreq->tcp_state.tot_chunk_size == hreq->param.reqdata.total_size || hreq->param.reqdata.total_size == 0) { /* Finish sending all the chunks, start reading * the response. */ hreq->state = REQUEST_SENT; http_req_start_reading(hreq); return PJ_TRUE; } } if (hreq->param.reqdata.total_size > 0 && hreq->cb.on_send_data) { /* Call the callback for the application to provide * the next chunk of data to be sent. */ (*hreq->cb.on_send_data)(hreq, &hreq->param.reqdata.data, &hreq->param.reqdata.size); /* Make sure the total data size given by the user does not * exceed what the user originally said. */ pj_assert(hreq->tcp_state.tot_chunk_size + hreq->param.reqdata.size <= hreq->param.reqdata.total_size); } http_req_start_sending(hreq); } else { /* No request body, proceed to reading the server's response. */ hreq->state = REQUEST_SENT; http_req_start_reading(hreq); } } return PJ_TRUE; } static pj_bool_t http_on_data_read(pj_activesock_t *asock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder) { pj_http_req *hreq = (pj_http_req*) pj_activesock_get_user_data(asock); TRACE_((THIS_FILE, "\nData received: %d bytes", size)); if (hreq->state == ABORTING || hreq->state == IDLE) return PJ_FALSE; if (hreq->state == READING_RESPONSE) { pj_status_t st; pj_size_t rem; if (status != PJ_SUCCESS && status != PJ_EPENDING) { hreq->error = status; pj_http_req_cancel(hreq, PJ_TRUE); return PJ_FALSE; } /* Parse the response. */ st = http_response_parse(hreq->pool, &hreq->response, data, size, &rem); if (st == PJLIB_UTIL_EHTTPINCHDR) { /* If we already use up all our buffer and still * hasn't received the whole header, return error */ if (size == BUF_SIZE) { hreq->error = PJ_ETOOBIG; // response header size is too big pj_http_req_cancel(hreq, PJ_TRUE); return PJ_FALSE; } /* Keep the data if we do not get the whole response header */ *remainder = size; } else { hreq->state = READING_DATA; if (st != PJ_SUCCESS) { /* Server replied with an invalid (or unknown) response * format. We'll just pass the whole (unparsed) response * to the user. */ hreq->response.data = data; hreq->response.size = size - rem; } /* If code is 401 or 407, find and parse WWW-Authenticate or * Proxy-Authenticate header */ if (hreq->response.status_code == 401 || hreq->response.status_code == 407) { const pj_str_t STR_WWW_AUTH = { "WWW-Authenticate", 16 }; const pj_str_t STR_PROXY_AUTH = { "Proxy-Authenticate", 18 }; pj_http_resp *response = &hreq->response; pj_http_headers *hdrs = &response->headers; unsigned i; status = PJ_ENOTFOUND; for (i = 0; i < hdrs->count; i++) { if (!pj_stricmp(&hdrs->header[i].name, &STR_WWW_AUTH) || !pj_stricmp(&hdrs->header[i].name, &STR_PROXY_AUTH)) { status = parse_auth_chal(hreq->pool, &hdrs->header[i].value, &response->auth_chal); break; } } /* Check if we should perform authentication */ if (status == PJ_SUCCESS && hreq->auth_state == AUTH_NONE && hreq->response.auth_chal.scheme.slen && hreq->param.auth_cred.username.slen && (hreq->param.auth_cred.scheme.slen == 0 || !pj_stricmp(&hreq->response.auth_chal.scheme, &hreq->param.auth_cred.scheme)) && (hreq->param.auth_cred.realm.slen == 0 || !pj_stricmp(&hreq->response.auth_chal.realm, &hreq->param.auth_cred.realm)) ) { /* Yes, authentication is required and we have been * configured with credential. */ restart_req_with_auth(hreq); if (hreq->auth_state == AUTH_RETRYING) { /* We'll be resending the request with auth. This * connection has been closed. */ return PJ_FALSE; } } } /* We already received the response header, call the * appropriate callback. */ if (hreq->cb.on_response) (*hreq->cb.on_response)(hreq, &hreq->response); hreq->response.data = NULL; hreq->response.size = 0; if (rem > 0 || hreq->response.content_length == 0) return http_on_data_read(asock, (rem == 0 ? NULL: (char *)data + size - rem), rem, PJ_SUCCESS, NULL); } return PJ_TRUE; } if (hreq->state != READING_DATA) return PJ_FALSE; if (hreq->cb.on_data_read) { /* If application wishes to receive the data once available, call * its callback. */ if (size > 0) (*hreq->cb.on_data_read)(hreq, data, size); } else { if (hreq->response.size == 0) { /* If we know the content length, allocate the data based * on that, otherwise we'll use initial buffer size and grow * it later if necessary. */ hreq->response.size = (hreq->response.content_length == -1 ? INITIAL_DATA_BUF_SIZE : hreq->response.content_length); hreq->response.data = pj_pool_alloc(hreq->pool, hreq->response.size); } /* If the size of data received exceeds its current size, * grow the buffer by a factor of 2. */ if (hreq->tcp_state.current_read_size + size > hreq->response.size) { void *olddata = hreq->response.data; hreq->response.data = pj_pool_alloc(hreq->pool, hreq->response.size << 1); pj_memcpy(hreq->response.data, olddata, hreq->response.size); hreq->response.size <<= 1; } /* Append the response data. */ pj_memcpy((char *)hreq->response.data + hreq->tcp_state.current_read_size, data, size); } hreq->tcp_state.current_read_size += size; /* If the total data received so far is equal to the content length * or if it's already EOF. */ if ((hreq->response.content_length >=0 && (pj_ssize_t)hreq->tcp_state.current_read_size >= hreq->response.content_length) || (status == PJ_EEOF && hreq->response.content_length == -1)) { /* Finish reading */ http_req_end_request(hreq); hreq->response.size = hreq->tcp_state.current_read_size; /* HTTP request is completed, call the callback. */ if (hreq->cb.on_complete) { (*hreq->cb.on_complete)(hreq, PJ_SUCCESS, &hreq->response); } return PJ_FALSE; } /* Error status or premature EOF. */ if ((status != PJ_SUCCESS && status != PJ_EPENDING && status != PJ_EEOF) || (status == PJ_EEOF && hreq->response.content_length > -1)) { hreq->error = status; pj_http_req_cancel(hreq, PJ_TRUE); return PJ_FALSE; } return PJ_TRUE; } /* Callback to be called when query has timed out */ static void on_timeout( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { pj_http_req *hreq = (pj_http_req *) entry->user_data; PJ_UNUSED_ARG(timer_heap); /* Recheck that the request is still not completed, since there is a * slight possibility of race condition (timer elapsed while at the * same time response arrives). */ if (hreq->state == READING_COMPLETE) { /* Yeah, we finish on time */ return; } /* Invalidate id. */ hreq->timer_entry.id = 0; /* Request timed out. */ hreq->error = PJ_ETIMEDOUT; pj_http_req_cancel(hreq, PJ_TRUE); } /* Parse authentication challenge */ static pj_status_t parse_auth_chal(pj_pool_t *pool, pj_str_t *input, pj_http_auth_chal *chal) { pj_scanner scanner; const pj_str_t REALM_STR = { "realm", 5}, NONCE_STR = { "nonce", 5}, ALGORITHM_STR = { "algorithm", 9 }, STALE_STR = { "stale", 5}, QOP_STR = { "qop", 3}, OPAQUE_STR = { "opaque", 6}; pj_status_t status = PJ_SUCCESS; PJ_USE_EXCEPTION ; pj_scan_init(&scanner, input->ptr, input->slen, PJ_SCAN_AUTOSKIP_WS, &on_syntax_error); PJ_TRY { /* Get auth scheme */ if (*scanner.curptr == '"') { pj_scan_get_quote(&scanner, '"', '"', &chal->scheme); chal->scheme.ptr++; chal->scheme.slen -= 2; } else { pj_scan_get_until_chr(&scanner, " \t\r\n", &chal->scheme); } /* Loop parsing all parameters */ for (;;) { const char *end_param = ", \t\r\n;"; pj_str_t name, value; /* Get pair of parameter name and value */ value.ptr = NULL; value.slen = 0; pj_scan_get_until_chr(&scanner, "=, \t\r\n", &name); if (*scanner.curptr == '=') { pj_scan_get_char(&scanner); if (!pj_scan_is_eof(&scanner)) { if (*scanner.curptr == '"' || *scanner.curptr == '\'') { int quote_char = *scanner.curptr; pj_scan_get_quote(&scanner, quote_char, quote_char, &value); value.ptr++; value.slen -= 2; } else if (!strchr(end_param, *scanner.curptr)) { pj_scan_get_until_chr(&scanner, end_param, &value); } } value = pj_str_unescape(pool, &value); } if (!pj_stricmp(&name, &REALM_STR)) { chal->realm = value; } else if (!pj_stricmp(&name, &NONCE_STR)) { chal->nonce = value; } else if (!pj_stricmp(&name, &ALGORITHM_STR)) { chal->algorithm = value; } else if (!pj_stricmp(&name, &OPAQUE_STR)) { chal->opaque = value; } else if (!pj_stricmp(&name, &QOP_STR)) { chal->qop = value; } else if (!pj_stricmp(&name, &STALE_STR)) { chal->stale = value.slen && (*value.ptr != '0') && (*value.ptr != 'f') && (*value.ptr != 'F'); } /* Eat comma */ if (!pj_scan_is_eof(&scanner) && *scanner.curptr == ',') pj_scan_get_char(&scanner); else break; } } PJ_CATCH_ANY { status = PJ_GET_EXCEPTION(); pj_bzero(chal, sizeof(*chal)); TRACE_((THIS_FILE, "Error: parsing of auth header failed")); } PJ_END; pj_scan_fini(&scanner); return status; } /* The same as #pj_http_headers_add_elmt() with char * as * its parameters. */ PJ_DEF(pj_status_t) pj_http_headers_add_elmt2(pj_http_headers *headers, char *name, char *val) { pj_str_t f, v; pj_cstr(&f, name); pj_cstr(&v, val); return pj_http_headers_add_elmt(headers, &f, &v); } PJ_DEF(pj_status_t) pj_http_headers_add_elmt(pj_http_headers *headers, pj_str_t *name, pj_str_t *val) { PJ_ASSERT_RETURN(headers && name && val, PJ_FALSE); if (headers->count >= PJ_HTTP_HEADER_SIZE) return PJ_ETOOMANY; pj_strassign(&headers->header[headers->count].name, name); pj_strassign(&headers->header[headers->count++].value, val); return PJ_SUCCESS; } static pj_status_t http_response_parse(pj_pool_t *pool, pj_http_resp *response, void *data, pj_size_t size, pj_size_t *remainder) { pj_size_t i; char *cptr; char *end_status, *newdata; pj_scanner scanner; pj_str_t s; const pj_str_t STR_CONTENT_LENGTH = { CONTENT_LENGTH, 14 }; pj_status_t status; PJ_USE_EXCEPTION; PJ_ASSERT_RETURN(response, PJ_EINVAL); if (size < 2) return PJLIB_UTIL_EHTTPINCHDR; /* Detect whether we already receive the response's status-line * and its headers. We're looking for a pair of CRLFs. A pair of * LFs is also supported although it is not RFC standard. */ cptr = (char *)data; for (i = 1, cptr++; i < size; i++, cptr++) { if (*cptr == '\n') { if (*(cptr - 1) == '\n') break; if (*(cptr - 1) == '\r') { if (i >= 3 && *(cptr - 2) == '\n' && *(cptr - 3) == '\r') break; } } } if (i == size) return PJLIB_UTIL_EHTTPINCHDR; *remainder = size - 1 - i; pj_bzero(response, sizeof(*response)); response->content_length = -1; newdata = (char*) pj_pool_alloc(pool, i); pj_memcpy(newdata, data, i); /* Parse the status-line. */ pj_scan_init(&scanner, newdata, i, 0, &on_syntax_error); PJ_TRY { pj_scan_get_until_ch(&scanner, ' ', &response->version); pj_scan_advance_n(&scanner, 1, PJ_FALSE); pj_scan_get_until_ch(&scanner, ' ', &s); response->status_code = (pj_uint16_t)pj_strtoul(&s); pj_scan_advance_n(&scanner, 1, PJ_FALSE); pj_scan_get_until_ch(&scanner, '\n', &response->reason); if (response->reason.ptr[response->reason.slen-1] == '\r') response->reason.slen--; } PJ_CATCH_ANY { pj_scan_fini(&scanner); return PJ_GET_EXCEPTION(); } PJ_END; end_status = scanner.curptr; pj_scan_fini(&scanner); /* Parse the response headers. */ size = i - 2 - (end_status - newdata); if (size > 0) { status = http_headers_parse(end_status + 1, size, &response->headers); } else { status = PJ_SUCCESS; } /* Find content-length header field. */ for (i = 0; i < response->headers.count; i++) { if (!pj_stricmp(&response->headers.header[i].name, &STR_CONTENT_LENGTH)) { response->content_length = pj_strtoul(&response->headers.header[i].value); /* If content length is zero, make sure that it is because the * header value is really zero and not due to parsing error. */ if (response->content_length == 0) { if (pj_strcmp2(&response->headers.header[i].value, "0")) { response->content_length = -1; } } break; } } return status; } static pj_status_t http_headers_parse(char *hdata, pj_size_t size, pj_http_headers *headers) { pj_scanner scanner; pj_str_t s, s2; pj_status_t status; PJ_USE_EXCEPTION; PJ_ASSERT_RETURN(headers, PJ_EINVAL); pj_scan_init(&scanner, hdata, size, 0, &on_syntax_error); /* Parse each line of header field consisting of header field name and * value, separated by ":" and any number of white spaces. */ PJ_TRY { do { pj_scan_get_until_chr(&scanner, ":\n", &s); if (*scanner.curptr == ':') { pj_scan_advance_n(&scanner, 1, PJ_TRUE); pj_scan_get_until_ch(&scanner, '\n', &s2); if (s2.ptr[s2.slen-1] == '\r') s2.slen--; status = pj_http_headers_add_elmt(headers, &s, &s2); if (status != PJ_SUCCESS) PJ_THROW(status); } pj_scan_advance_n(&scanner, 1, PJ_TRUE); /* Finish parsing */ if (pj_scan_is_eof(&scanner)) break; } while (1); } PJ_CATCH_ANY { pj_scan_fini(&scanner); return PJ_GET_EXCEPTION(); } PJ_END; pj_scan_fini(&scanner); return PJ_SUCCESS; } PJ_DEF(void) pj_http_req_param_default(pj_http_req_param *param) { pj_assert(param); pj_bzero(param, sizeof(*param)); param->addr_family = pj_AF_INET(); pj_strset2(¶m->method, (char*)http_method_names[HTTP_GET]); pj_strset2(¶m->version, (char*)HTTP_1_0); param->timeout.msec = PJ_HTTP_DEFAULT_TIMEOUT; pj_time_val_normalize(¶m->timeout); param->max_retries = 3; } /* Get the location of '@' character to indicate the end of * user:passwd part of an URI. If user:passwd part is not * present, NULL will be returned. */ static char *get_url_at_pos(const char *str, pj_size_t len) { const char *end = str + len; const char *p = str; /* skip scheme: */ while (p!=end && *p!='/') ++p; if (p!=end && *p=='/') ++p; if (p!=end && *p=='/') ++p; if (p==end) return NULL; for (; p!=end; ++p) { switch (*p) { case '/': return NULL; case '@': return (char*)p; } } return NULL; } PJ_DEF(pj_status_t) pj_http_req_parse_url(const pj_str_t *url, pj_http_url *hurl) { pj_scanner scanner; pj_size_t len = url->slen; PJ_USE_EXCEPTION; if (!len) return -1; pj_bzero(hurl, sizeof(*hurl)); pj_scan_init(&scanner, url->ptr, url->slen, 0, &on_syntax_error); PJ_TRY { pj_str_t s; /* Exhaust any whitespaces. */ pj_scan_skip_whitespace(&scanner); /* Parse the protocol */ pj_scan_get_until_ch(&scanner, ':', &s); if (!pj_stricmp2(&s, http_protocol_names[PROTOCOL_HTTP])) { pj_strset2(&hurl->protocol, (char*)http_protocol_names[PROTOCOL_HTTP]); } else if (!pj_stricmp2(&s, http_protocol_names[PROTOCOL_HTTPS])) { pj_strset2(&hurl->protocol, (char*)http_protocol_names[PROTOCOL_HTTPS]); } else { PJ_THROW(PJ_ENOTSUP); // unsupported protocol } if (pj_scan_strcmp(&scanner, "://", 3)) { PJ_THROW(PJLIB_UTIL_EHTTPINURL); // no "://" after protocol name } pj_scan_advance_n(&scanner, 3, PJ_FALSE); if (get_url_at_pos(url->ptr, url->slen)) { /* Parse username and password */ pj_scan_get_until_chr(&scanner, ":@", &hurl->username); if (*scanner.curptr == ':') { pj_scan_get_char(&scanner); pj_scan_get_until_chr(&scanner, "@", &hurl->passwd); } else { hurl->passwd.slen = 0; } pj_scan_get_char(&scanner); } /* Parse the host and port number (if any) */ pj_scan_get_until_chr(&scanner, ":/", &s); pj_strassign(&hurl->host, &s); if (hurl->host.slen==0) PJ_THROW(PJ_EINVAL); if (pj_scan_is_eof(&scanner) || *scanner.curptr == '/') { /* No port number specified */ /* Assume default http/https port number */ hurl->port = get_http_default_port(&hurl->protocol); pj_assert(hurl->port > 0); } else { pj_scan_advance_n(&scanner, 1, PJ_FALSE); pj_scan_get_until_ch(&scanner, '/', &s); /* Parse the port number */ hurl->port = (pj_uint16_t)pj_strtoul(&s); if (!hurl->port) PJ_THROW(PJLIB_UTIL_EHTTPINPORT); // invalid port number } if (!pj_scan_is_eof(&scanner)) { hurl->path.ptr = scanner.curptr; hurl->path.slen = scanner.end - scanner.curptr; } else { /* no path, append '/' */ pj_cstr(&hurl->path, "/"); } } PJ_CATCH_ANY { pj_scan_fini(&scanner); return PJ_GET_EXCEPTION(); } PJ_END; pj_scan_fini(&scanner); return PJ_SUCCESS; } PJ_DEF(void) pj_http_req_set_timeout(pj_http_req *http_req, const pj_time_val* timeout) { pj_memcpy(&http_req->param.timeout, timeout, sizeof(*timeout)); } PJ_DEF(pj_status_t) pj_http_req_create(pj_pool_t *pool, const pj_str_t *url, pj_timer_heap_t *timer, pj_ioqueue_t *ioqueue, const pj_http_req_param *param, const pj_http_req_callback *hcb, pj_http_req **http_req) { pj_pool_t *own_pool; pj_http_req *hreq; char *at_pos; pj_status_t status; PJ_ASSERT_RETURN(pool && url && timer && ioqueue && hcb && http_req, PJ_EINVAL); *http_req = NULL; own_pool = pj_pool_create(pool->factory, NULL, INITIAL_POOL_SIZE, POOL_INCREMENT_SIZE, NULL); hreq = PJ_POOL_ZALLOC_T(own_pool, struct pj_http_req); if (!hreq) return PJ_ENOMEM; /* Initialization */ hreq->pool = own_pool; hreq->ioqueue = ioqueue; hreq->timer = timer; hreq->asock = NULL; pj_memcpy(&hreq->cb, hcb, sizeof(*hcb)); hreq->state = IDLE; hreq->resolved = PJ_FALSE; hreq->buffer.ptr = NULL; pj_timer_entry_init(&hreq->timer_entry, 0, hreq, &on_timeout); /* Initialize parameter */ if (param) { pj_memcpy(&hreq->param, param, sizeof(*param)); /* TODO: validate the param here * Should we validate the method as well? If yes, based on all HTTP * methods or based on supported methods only? For the later, one * drawback would be that you can't use this if the method is not * officially supported */ PJ_ASSERT_RETURN(hreq->param.addr_family==PJ_AF_UNSPEC || hreq->param.addr_family==PJ_AF_INET || hreq->param.addr_family==PJ_AF_INET6, PJ_EAFNOTSUP); PJ_ASSERT_RETURN(!pj_strcmp2(&hreq->param.version, HTTP_1_0) || !pj_strcmp2(&hreq->param.version, HTTP_1_1), PJ_ENOTSUP); pj_time_val_normalize(&hreq->param.timeout); } else { pj_http_req_param_default(&hreq->param); } /* Parse the URL */ if (!pj_strdup_with_null(hreq->pool, &hreq->url, url)) { pj_pool_release(hreq->pool); return PJ_ENOMEM; } status = pj_http_req_parse_url(&hreq->url, &hreq->hurl); if (status != PJ_SUCCESS) { pj_pool_release(hreq->pool); return status; // Invalid URL supplied } /* If URL contains username/password, move them to credential and * remove them from the URL. */ if ((at_pos=get_url_at_pos(hreq->url.ptr, hreq->url.slen)) != NULL) { pj_str_t tmp; char *user_pos = pj_strchr(&hreq->url, '/'); int removed_len; /* Save credential first, unescape the string */ tmp = pj_str_unescape(hreq->pool, &hreq->hurl.username);; pj_strdup(hreq->pool, &hreq->param.auth_cred.username, &tmp); tmp = pj_str_unescape(hreq->pool, &hreq->hurl.passwd); pj_strdup(hreq->pool, &hreq->param.auth_cred.data, &tmp); hreq->hurl.username.ptr = hreq->hurl.passwd.ptr = NULL; hreq->hurl.username.slen = hreq->hurl.passwd.slen = 0; /* Remove "username:password@" from the URL */ pj_assert(user_pos != 0 && user_pos < at_pos); user_pos += 2; removed_len = (int)(at_pos + 1 - user_pos); pj_memmove(user_pos, at_pos+1, hreq->url.ptr+hreq->url.slen-at_pos-1); hreq->url.slen -= removed_len; /* Need to adjust hostname and path pointers due to memmove*/ if (hreq->hurl.host.ptr > user_pos && hreq->hurl.host.ptr < user_pos + hreq->url.slen) { hreq->hurl.host.ptr -= removed_len; } /* path may come from a string constant, don't shift it if so */ if (hreq->hurl.path.ptr > user_pos && hreq->hurl.path.ptr < user_pos + hreq->url.slen) { hreq->hurl.path.ptr -= removed_len; } } *http_req = hreq; return PJ_SUCCESS; } PJ_DEF(pj_bool_t) pj_http_req_is_running(const pj_http_req *http_req) { PJ_ASSERT_RETURN(http_req, PJ_FALSE); return (http_req->state != IDLE); } PJ_DEF(void*) pj_http_req_get_user_data(pj_http_req *http_req) { PJ_ASSERT_RETURN(http_req, NULL); return http_req->param.user_data; } static pj_status_t start_http_req(pj_http_req *http_req, pj_bool_t notify_on_fail) { pj_sock_t sock = PJ_INVALID_SOCKET; pj_status_t status; pj_activesock_cb asock_cb; int retry = 0; PJ_ASSERT_RETURN(http_req, PJ_EINVAL); /* Http request is not idle, a request was initiated before and * is still in progress */ PJ_ASSERT_RETURN(http_req->state == IDLE, PJ_EBUSY); /* Reset few things to make sure restarting works */ http_req->error = 0; http_req->response.headers.count = 0; pj_bzero(&http_req->tcp_state, sizeof(http_req->tcp_state)); if (!http_req->resolved) { /* Resolve the Internet address of the host */ status = pj_sockaddr_init(http_req->param.addr_family, &http_req->addr, &http_req->hurl.host, http_req->hurl.port); if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&http_req->addr) || (http_req->param.addr_family==pj_AF_INET() && http_req->addr.ipv4.sin_addr.s_addr==PJ_INADDR_NONE)) { goto on_return; } http_req->resolved = PJ_TRUE; } status = pj_sock_socket(http_req->param.addr_family, pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) goto on_return; // error creating socket pj_bzero(&asock_cb, sizeof(asock_cb)); asock_cb.on_data_read = &http_on_data_read; asock_cb.on_data_sent = &http_on_data_sent; asock_cb.on_connect_complete = &http_on_connect; do { pj_sockaddr_in bound_addr; pj_uint16_t port = 0; /* If we are using port restriction. * Get a random port within the range */ if (http_req->param.source_port_range_start != 0) { port = (pj_uint16_t) (http_req->param.source_port_range_start + (pj_rand() % http_req->param.source_port_range_size)); } pj_sockaddr_in_init(&bound_addr, NULL, port); status = pj_sock_bind(sock, &bound_addr, sizeof(bound_addr)); } while (status != PJ_SUCCESS && (retry++ < http_req->param.max_retries)); if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "Unable to bind to the requested port")); pj_sock_close(sock); goto on_return; } // TODO: should we set whole data to 0 by default? // or add it in the param? status = pj_activesock_create(http_req->pool, sock, pj_SOCK_STREAM(), NULL, http_req->ioqueue, &asock_cb, http_req, &http_req->asock); if (status != PJ_SUCCESS) { pj_sock_close(sock); goto on_return; // error creating activesock } /* Schedule timeout timer for the request */ pj_assert(http_req->timer_entry.id == 0); http_req->timer_entry.id = 1; status = pj_timer_heap_schedule(http_req->timer, &http_req->timer_entry, &http_req->param.timeout); if (status != PJ_SUCCESS) { http_req->timer_entry.id = 0; goto on_return; // error scheduling timer } /* Connect to host */ http_req->state = CONNECTING; status = pj_activesock_start_connect(http_req->asock, http_req->pool, (pj_sock_t *)&(http_req->addr), pj_sockaddr_get_len(&http_req->addr)); if (status == PJ_SUCCESS) { http_req->state = SENDING_REQUEST; status = http_req_start_sending(http_req); if (status != PJ_SUCCESS) goto on_return; } else if (status != PJ_EPENDING) { goto on_return; // error connecting } return PJ_SUCCESS; on_return: http_req->error = status; if (notify_on_fail) pj_http_req_cancel(http_req, PJ_TRUE); else http_req_end_request(http_req); return status; } /* Starts an asynchronous HTTP request to the URL specified. */ PJ_DEF(pj_status_t) pj_http_req_start(pj_http_req *http_req) { return start_http_req(http_req, PJ_FALSE); } /* Respond to basic authentication challenge */ static pj_status_t auth_respond_basic(pj_http_req *hreq) { /* Basic authentication: * credentials = "Basic" basic-credentials * basic-credentials = base64-user-pass * base64-user-pass = * user-pass = userid ":" password * * Sample: * Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== */ pj_str_t user_pass; pj_http_header_elmt *phdr; int len; /* Use send buffer to store userid ":" password */ user_pass.ptr = hreq->buffer.ptr; pj_strcpy(&user_pass, &hreq->param.auth_cred.username); pj_strcat2(&user_pass, ":"); pj_strcat(&user_pass, &hreq->param.auth_cred.data); /* Create Authorization header */ phdr = &hreq->param.headers.header[hreq->param.headers.count++]; pj_bzero(phdr, sizeof(*phdr)); if (hreq->response.status_code == 401) phdr->name = pj_str("Authorization"); else phdr->name = pj_str("Proxy-Authorization"); len = (int)(PJ_BASE256_TO_BASE64_LEN(user_pass.slen) + 10); phdr->value.ptr = (char*)pj_pool_alloc(hreq->pool, len); phdr->value.slen = 0; pj_strcpy2(&phdr->value, "Basic "); len -= (int)phdr->value.slen; pj_base64_encode((pj_uint8_t*)user_pass.ptr, (int)user_pass.slen, phdr->value.ptr + phdr->value.slen, &len); phdr->value.slen += len; return PJ_SUCCESS; } /** Length of digest string. */ #define MD5_STRLEN 32 /* A macro just to get rid of type mismatch between char and unsigned char */ #define MD5_APPEND(pms,buf,len) pj_md5_update(pms, (const pj_uint8_t*)buf, \ (unsigned)len) /* Transform digest to string. * output must be at least PJSIP_MD5STRLEN+1 bytes. * * NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED! */ static void digest2str(const unsigned char digest[], char *output) { int i; for (i = 0; i<16; ++i) { pj_val_to_hex_digit(digest[i], output); output += 2; } } static void auth_create_digest_response(pj_str_t *result, const pj_http_auth_cred *cred, const pj_str_t *nonce, const pj_str_t *nc, const pj_str_t *cnonce, const pj_str_t *qop, const pj_str_t *uri, const pj_str_t *realm, const pj_str_t *method) { char ha1[MD5_STRLEN]; char ha2[MD5_STRLEN]; unsigned char digest[16]; pj_md5_context pms; pj_assert(result->slen >= MD5_STRLEN); TRACE_((THIS_FILE, "Begin creating digest")); if (cred->data_type == 0) { /*** *** ha1 = MD5(username ":" realm ":" password) ***/ pj_md5_init(&pms); MD5_APPEND( &pms, cred->username.ptr, cred->username.slen); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, realm->ptr, realm->slen); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, cred->data.ptr, cred->data.slen); pj_md5_final(&pms, digest); digest2str(digest, ha1); } else if (cred->data_type == 1) { pj_assert(cred->data.slen == 32); pj_memcpy( ha1, cred->data.ptr, cred->data.slen ); } else { pj_assert(!"Invalid data_type"); } TRACE_((THIS_FILE, " ha1=%.32s", ha1)); /*** *** ha2 = MD5(method ":" req_uri) ***/ pj_md5_init(&pms); MD5_APPEND( &pms, method->ptr, method->slen); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, uri->ptr, uri->slen); pj_md5_final(&pms, digest); digest2str(digest, ha2); TRACE_((THIS_FILE, " ha2=%.32s", ha2)); /*** *** When qop is not used: *** response = MD5(ha1 ":" nonce ":" ha2) *** *** When qop=auth is used: *** response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2) ***/ pj_md5_init(&pms); MD5_APPEND( &pms, ha1, MD5_STRLEN); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, nonce->ptr, nonce->slen); if (qop && qop->slen != 0) { MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, nc->ptr, nc->slen); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, cnonce->ptr, cnonce->slen); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, qop->ptr, qop->slen); } MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, ha2, MD5_STRLEN); /* This is the final response digest. */ pj_md5_final(&pms, digest); /* Convert digest to string and store in chal->response. */ result->slen = MD5_STRLEN; digest2str(digest, result->ptr); TRACE_((THIS_FILE, " digest=%.32s", result->ptr)); TRACE_((THIS_FILE, "Digest created")); } /* Find out if qop offer contains "auth" token */ static pj_bool_t auth_has_qop( pj_pool_t *pool, const pj_str_t *qop_offer) { pj_str_t qop; char *p; pj_strdup_with_null( pool, &qop, qop_offer); p = qop.ptr; while (*p) { *p = (char)pj_tolower(*p); ++p; } p = qop.ptr; while (*p) { if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') { int e = *(p+4); if (e=='"' || e==',' || e==0) return PJ_TRUE; else p += 4; } else { ++p; } } return PJ_FALSE; } #define STR_PREC(s) (int)(s).slen, (s).ptr /* Respond to digest authentication */ static pj_status_t auth_respond_digest(pj_http_req *hreq) { const pj_http_auth_chal *chal = &hreq->response.auth_chal; const pj_http_auth_cred *cred = &hreq->param.auth_cred; pj_http_header_elmt *phdr; char digest_response_buf[MD5_STRLEN]; int len; pj_str_t digest_response; /* Check algorithm is supported. We only support MD5 */ if (chal->algorithm.slen!=0 && pj_stricmp2(&chal->algorithm, "MD5")) { TRACE_((THIS_FILE, "Error: Unsupported digest algorithm \"%.*s\"", chal->algorithm.slen, chal->algorithm.ptr)); return PJ_ENOTSUP; } /* Add Authorization header */ phdr = &hreq->param.headers.header[hreq->param.headers.count++]; pj_bzero(phdr, sizeof(*phdr)); if (hreq->response.status_code == 401) phdr->name = pj_str("Authorization"); else phdr->name = pj_str("Proxy-Authorization"); /* Allocate space for the header */ len = (int)(8 + /* Digest */ 16 + hreq->param.auth_cred.username.slen + /* username= */ 12 + chal->realm.slen + /* realm= */ 12 + chal->nonce.slen + /* nonce= */ 8 + hreq->hurl.path.slen + /* uri= */ 16 + /* algorithm=MD5 */ 16 + MD5_STRLEN + /* response= */ 12 + /* qop=auth */ 8 + /* nc=.. */ 30 + /* cnonce= */ 12 + chal->opaque.slen + /* opaque=".." */ 0); phdr->value.ptr = (char*)pj_pool_alloc(hreq->pool, len); /* Configure buffer to temporarily store the digest */ digest_response.ptr = digest_response_buf; digest_response.slen = MD5_STRLEN; if (chal->qop.slen == 0) { const pj_str_t STR_MD5 = { "MD5", 3 }; int max_len; /* Server doesn't require quality of protection. */ auth_create_digest_response(&digest_response, cred, &chal->nonce, NULL, NULL, NULL, &hreq->hurl.path, &chal->realm, &hreq->param.method); max_len = len; len = pj_ansi_snprintf( phdr->value.ptr, max_len, "Digest username=\"%.*s\", " "realm=\"%.*s\", " "nonce=\"%.*s\", " "uri=\"%.*s\", " "algorithm=%.*s, " "response=\"%.*s\"", STR_PREC(cred->username), STR_PREC(chal->realm), STR_PREC(chal->nonce), STR_PREC(hreq->hurl.path), STR_PREC(STR_MD5), STR_PREC(digest_response)); if (len < 0 || len >= max_len) return PJ_ETOOSMALL; phdr->value.slen = len; } else if (auth_has_qop(hreq->pool, &chal->qop)) { /* Server requires quality of protection. * We respond with selecting "qop=auth" protection. */ const pj_str_t STR_MD5 = { "MD5", 3 }; const pj_str_t qop = pj_str("auth"); const pj_str_t nc = pj_str("00000001"); const pj_str_t cnonce = pj_str("b39971"); int max_len; auth_create_digest_response(&digest_response, cred, &chal->nonce, &nc, &cnonce, &qop, &hreq->hurl.path, &chal->realm, &hreq->param.method); max_len = len; len = pj_ansi_snprintf( phdr->value.ptr, max_len, "Digest username=\"%.*s\", " "realm=\"%.*s\", " "nonce=\"%.*s\", " "uri=\"%.*s\", " "algorithm=%.*s, " "response=\"%.*s\", " "qop=%.*s, " "nc=%.*s, " "cnonce=\"%.*s\"", STR_PREC(cred->username), STR_PREC(chal->realm), STR_PREC(chal->nonce), STR_PREC(hreq->hurl.path), STR_PREC(STR_MD5), STR_PREC(digest_response), STR_PREC(qop), STR_PREC(nc), STR_PREC(cnonce)); if (len < 0 || len >= max_len) return PJ_ETOOSMALL; phdr->value.slen = len; if (chal->opaque.slen) { pj_strcat2(&phdr->value, ", opaque=\""); pj_strcat(&phdr->value, &chal->opaque); pj_strcat2(&phdr->value, "\""); } } else { /* Server requires quality protection that we don't support. */ TRACE_((THIS_FILE, "Error: Unsupported qop offer %.*s", chal->qop.slen, chal->qop.ptr)); return PJ_ENOTSUP; } return PJ_SUCCESS; } static void restart_req_with_auth(pj_http_req *hreq) { pj_http_auth_chal *chal = &hreq->response.auth_chal; pj_http_auth_cred *cred = &hreq->param.auth_cred; pj_status_t status; if (hreq->param.headers.count >= PJ_HTTP_HEADER_SIZE) { TRACE_((THIS_FILE, "Error: no place to put Authorization header")); hreq->auth_state = AUTH_DONE; return; } /* If credential specifies specific scheme, make sure they match */ if (cred->scheme.slen && pj_stricmp(&chal->scheme, &cred->scheme)) { status = PJ_ENOTSUP; TRACE_((THIS_FILE, "Error: auth schemes mismatch")); goto on_error; } /* If credential specifies specific realm, make sure they match */ if (cred->realm.slen && pj_stricmp(&chal->realm, &cred->realm)) { status = PJ_ENOTSUP; TRACE_((THIS_FILE, "Error: auth realms mismatch")); goto on_error; } if (!pj_stricmp2(&chal->scheme, "basic")) { status = auth_respond_basic(hreq); } else if (!pj_stricmp2(&chal->scheme, "digest")) { status = auth_respond_digest(hreq); } else { TRACE_((THIS_FILE, "Error: unsupported HTTP auth scheme")); status = PJ_ENOTSUP; } if (status != PJ_SUCCESS) goto on_error; http_req_end_request(hreq); status = start_http_req(hreq, PJ_TRUE); if (status != PJ_SUCCESS) goto on_error; hreq->auth_state = AUTH_RETRYING; return; on_error: hreq->auth_state = AUTH_DONE; } /* snprintf() to a pj_str_t struct with an option to append the * result at the back of the string. */ static void str_snprintf(pj_str_t *s, size_t size, pj_bool_t append, const char *format, ...) { va_list arg; int retval; va_start(arg, format); if (!append) s->slen = 0; size -= s->slen; retval = pj_ansi_vsnprintf(s->ptr + s->slen, size, format, arg); s->slen += ((retval < (int)size) ? retval : size - 1); va_end(arg); } static pj_status_t http_req_start_sending(pj_http_req *hreq) { pj_status_t status; pj_str_t pkt; pj_ssize_t len; pj_size_t i; PJ_ASSERT_RETURN(hreq->state == SENDING_REQUEST || hreq->state == SENDING_REQUEST_BODY, PJ_EBUG); if (hreq->state == SENDING_REQUEST) { /* Prepare the request data */ if (!hreq->buffer.ptr) hreq->buffer.ptr = (char*)pj_pool_alloc(hreq->pool, BUF_SIZE); pj_strassign(&pkt, &hreq->buffer); pkt.slen = 0; /* Start-line */ str_snprintf(&pkt, BUF_SIZE, PJ_TRUE, "%.*s %.*s %s/%.*s\r\n", STR_PREC(hreq->param.method), STR_PREC(hreq->hurl.path), get_protocol(&hreq->hurl.protocol), STR_PREC(hreq->param.version)); /* Header field "Host" */ str_snprintf(&pkt, BUF_SIZE, PJ_TRUE, "Host: %.*s:%d\r\n", STR_PREC(hreq->hurl.host), hreq->hurl.port); if (!pj_strcmp2(&hreq->param.method, http_method_names[HTTP_PUT])) { char buf[16]; /* Header field "Content-Length" */ pj_utoa(hreq->param.reqdata.total_size ? (unsigned long)hreq->param.reqdata.total_size: (unsigned long)hreq->param.reqdata.size, buf); str_snprintf(&pkt, BUF_SIZE, PJ_TRUE, "%s: %s\r\n", CONTENT_LENGTH, buf); } /* Append user-specified headers */ for (i = 0; i < hreq->param.headers.count; i++) { str_snprintf(&pkt, BUF_SIZE, PJ_TRUE, "%.*s: %.*s\r\n", STR_PREC(hreq->param.headers.header[i].name), STR_PREC(hreq->param.headers.header[i].value)); } if (pkt.slen >= BUF_SIZE - 1) { status = PJLIB_UTIL_EHTTPINSBUF; goto on_return; } pj_strcat2(&pkt, "\r\n"); pkt.ptr[pkt.slen] = 0; TRACE_((THIS_FILE, "%s", pkt.ptr)); } else { pkt.ptr = (char*)hreq->param.reqdata.data; pkt.slen = hreq->param.reqdata.size; } /* Send the request */ len = pj_strlen(&pkt); pj_ioqueue_op_key_init(&hreq->op_key, sizeof(hreq->op_key)); hreq->tcp_state.send_size = len; hreq->tcp_state.current_send_size = 0; status = pj_activesock_send(hreq->asock, &hreq->op_key, pkt.ptr, &len, 0); if (status == PJ_SUCCESS) { http_on_data_sent(hreq->asock, &hreq->op_key, len); } else if (status != PJ_EPENDING) { goto on_return; // error sending data } return PJ_SUCCESS; on_return: http_req_end_request(hreq); return status; } static pj_status_t http_req_start_reading(pj_http_req *hreq) { pj_status_t status; PJ_ASSERT_RETURN(hreq->state == REQUEST_SENT, PJ_EBUG); /* Receive the response */ hreq->state = READING_RESPONSE; hreq->tcp_state.current_read_size = 0; pj_assert(hreq->buffer.ptr); status = pj_activesock_start_read2(hreq->asock, hreq->pool, BUF_SIZE, (void**)&hreq->buffer.ptr, 0); if (status != PJ_SUCCESS) { /* Error reading */ http_req_end_request(hreq); return status; } return PJ_SUCCESS; } static pj_status_t http_req_end_request(pj_http_req *hreq) { if (hreq->asock) { pj_activesock_close(hreq->asock); hreq->asock = NULL; } /* Cancel query timeout timer. */ if (hreq->timer_entry.id != 0) { pj_timer_heap_cancel(hreq->timer, &hreq->timer_entry); /* Invalidate id. */ hreq->timer_entry.id = 0; } hreq->state = IDLE; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_http_req_cancel(pj_http_req *http_req, pj_bool_t notify) { http_req->state = ABORTING; http_req_end_request(http_req); if (notify && http_req->cb.on_complete) { (*http_req->cb.on_complete)(http_req, (!http_req->error? PJ_ECANCELLED: http_req->error), NULL); } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_http_req_destroy(pj_http_req *http_req) { PJ_ASSERT_RETURN(http_req, PJ_EINVAL); /* If there is any pending request, cancel it */ if (http_req->state != IDLE) { pj_http_req_cancel(http_req, PJ_FALSE); } pj_pool_release(http_req->pool); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/json.c ================================================ /* $Id$ */ /* * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #define EL_INIT(p_el, nm, typ) do { \ if (nm) { \ p_el->name = *nm; \ } else { \ p_el->name.ptr = (char*)""; \ p_el->name.slen = 0; \ } \ p_el->type = typ; \ } while (0) struct write_state; struct parse_state; #define NO_NAME 1 static pj_status_t elem_write(const pj_json_elem *elem, struct write_state *st, unsigned flags); static pj_json_elem* parse_elem_throw(struct parse_state *st, pj_json_elem *elem); PJ_DEF(void) pj_json_elem_null(pj_json_elem *el, pj_str_t *name) { EL_INIT(el, name, PJ_JSON_VAL_NULL); } PJ_DEF(void) pj_json_elem_bool(pj_json_elem *el, pj_str_t *name, pj_bool_t val) { EL_INIT(el, name, PJ_JSON_VAL_BOOL); el->value.is_true = val; } PJ_DEF(void) pj_json_elem_number(pj_json_elem *el, pj_str_t *name, float val) { EL_INIT(el, name, PJ_JSON_VAL_NUMBER); el->value.num = val; } PJ_DEF(void) pj_json_elem_string( pj_json_elem *el, pj_str_t *name, pj_str_t *value) { EL_INIT(el, name, PJ_JSON_VAL_STRING); el->value.str = *value; } PJ_DEF(void) pj_json_elem_array(pj_json_elem *el, pj_str_t *name) { EL_INIT(el, name, PJ_JSON_VAL_ARRAY); pj_list_init(&el->value.children); } PJ_DEF(void) pj_json_elem_obj(pj_json_elem *el, pj_str_t *name) { EL_INIT(el, name, PJ_JSON_VAL_OBJ); pj_list_init(&el->value.children); } PJ_DEF(void) pj_json_elem_add(pj_json_elem *el, pj_json_elem *child) { pj_assert(el->type == PJ_JSON_VAL_OBJ || el->type == PJ_JSON_VAL_ARRAY); pj_list_push_back(&el->value.children, child); } struct parse_state { pj_pool_t *pool; pj_scanner scanner; pj_json_err_info *err_info; pj_cis_t float_spec; /* numbers with dot! */ }; static pj_status_t parse_children(struct parse_state *st, pj_json_elem *parent) { char end_quote = (parent->type == PJ_JSON_VAL_ARRAY)? ']' : '}'; pj_scan_get_char(&st->scanner); while (*st->scanner.curptr != end_quote) { pj_json_elem *child; while (*st->scanner.curptr == ',') pj_scan_get_char(&st->scanner); if (*st->scanner.curptr == end_quote) break; child = parse_elem_throw(st, NULL); if (!child) return PJLIB_UTIL_EINJSON; pj_json_elem_add(parent, child); } pj_scan_get_char(&st->scanner); return PJ_SUCCESS; } /* Return 0 if success or the index of the invalid char in the string */ static unsigned parse_quoted_string(struct parse_state *st, pj_str_t *output) { pj_str_t token; char *op, *ip, *iend; pj_scan_get_quote(&st->scanner, '"', '"', &token); /* Remove the quote characters */ token.ptr++; token.slen-=2; if (pj_strchr(&token, '\\') == NULL) { *output = token; return 0; } output->ptr = op = pj_pool_alloc(st->pool, token.slen); ip = token.ptr; iend = token.ptr + token.slen; while (ip != iend) { if (*ip == '\\') { ++ip; if (ip==iend) { goto on_error; } if (*ip == 'u') { ip++; if (iend - ip < 4) { ip = iend -1; goto on_error; } /* Only use the last two hext digits because we're on * ASCII */ *op++ = (char)(pj_hex_digit_to_val(ip[2]) * 16 + pj_hex_digit_to_val(ip[3])); ip += 4; } else if (*ip=='"' || *ip=='\\' || *ip=='/') { *op++ = *ip++; } else if (*ip=='b') { *op++ = '\b'; ip++; } else if (*ip=='f') { *op++ = '\f'; ip++; } else if (*ip=='n') { *op++ = '\n'; ip++; } else if (*ip=='r') { *op++ = '\r'; ip++; } else if (*ip=='t') { *op++ = '\t'; ip++; } else { goto on_error; } } else { *op++ = *ip++; } } output->slen = op - output->ptr; return 0; on_error: output->slen = op - output->ptr; return (unsigned)(ip - token.ptr); } static pj_json_elem* parse_elem_throw(struct parse_state *st, pj_json_elem *elem) { pj_str_t name = {NULL, 0}, value = {NULL, 0}; pj_str_t token; if (!elem) elem = pj_pool_alloc(st->pool, sizeof(*elem)); /* Parse name */ if (*st->scanner.curptr == '"') { pj_scan_get_char(&st->scanner); pj_scan_get_until_ch(&st->scanner, '"', &token); pj_scan_get_char(&st->scanner); if (*st->scanner.curptr == ':') { pj_scan_get_char(&st->scanner); name = token; } else { value = token; } } if (value.slen) { /* Element with string value and no name */ pj_json_elem_string(elem, &name, &value); return elem; } /* Parse value */ if (pj_cis_match(&st->float_spec, *st->scanner.curptr) || *st->scanner.curptr == '-') { float val; pj_bool_t neg = PJ_FALSE; if (*st->scanner.curptr == '-') { pj_scan_get_char(&st->scanner); neg = PJ_TRUE; } pj_scan_get(&st->scanner, &st->float_spec, &token); val = pj_strtof(&token); if (neg) val = -val; pj_json_elem_number(elem, &name, val); } else if (*st->scanner.curptr == '"') { unsigned err; char *start = st->scanner.curptr; err = parse_quoted_string(st, &token); if (err) { st->scanner.curptr = start + err; return NULL; } pj_json_elem_string(elem, &name, &token); } else if (pj_isalpha(*st->scanner.curptr)) { if (pj_scan_strcmp(&st->scanner, "false", 5)==0) { pj_json_elem_bool(elem, &name, PJ_FALSE); pj_scan_advance_n(&st->scanner, 5, PJ_TRUE); } else if (pj_scan_strcmp(&st->scanner, "true", 4)==0) { pj_json_elem_bool(elem, &name, PJ_TRUE); pj_scan_advance_n(&st->scanner, 4, PJ_TRUE); } else if (pj_scan_strcmp(&st->scanner, "null", 4)==0) { pj_json_elem_null(elem, &name); pj_scan_advance_n(&st->scanner, 4, PJ_TRUE); } else { return NULL; } } else if (*st->scanner.curptr == '[') { pj_json_elem_array(elem, &name); if (parse_children(st, elem) != PJ_SUCCESS) return NULL; } else if (*st->scanner.curptr == '{') { pj_json_elem_obj(elem, &name); if (parse_children(st, elem) != PJ_SUCCESS) return NULL; } else { return NULL; } return elem; } static void on_syntax_error(pj_scanner *scanner) { PJ_UNUSED_ARG(scanner); PJ_THROW(11); } PJ_DEF(pj_json_elem*) pj_json_parse(pj_pool_t *pool, char *buffer, unsigned *size, pj_json_err_info *err_info) { pj_cis_buf_t cis_buf; struct parse_state st; pj_json_elem *root; PJ_USE_EXCEPTION; PJ_ASSERT_RETURN(pool && buffer && size, NULL); if (!*size) return NULL; pj_bzero(&st, sizeof(st)); st.pool = pool; st.err_info = err_info; pj_scan_init(&st.scanner, buffer, *size, PJ_SCAN_AUTOSKIP_WS | PJ_SCAN_AUTOSKIP_NEWLINE, &on_syntax_error); pj_cis_buf_init(&cis_buf); pj_cis_init(&cis_buf, &st.float_spec); pj_cis_add_str(&st.float_spec, ".0123456789"); PJ_TRY { root = parse_elem_throw(&st, NULL); } PJ_CATCH_ANY { root = NULL; } PJ_END if (!root && err_info) { err_info->line = st.scanner.line; err_info->col = pj_scan_get_col(&st.scanner) + 1; err_info->err_char = *st.scanner.curptr; } *size = (unsigned)((buffer + *size) - st.scanner.curptr); pj_scan_fini(&st.scanner); return root; } struct buf_writer_data { char *pos; unsigned size; }; static pj_status_t buf_writer(const char *s, unsigned size, void *user_data) { struct buf_writer_data *buf_data = (struct buf_writer_data*)user_data; if (size+1 >= buf_data->size) return PJ_ETOOBIG; pj_memcpy(buf_data->pos, s, size); buf_data->pos += size; buf_data->size -= size; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_json_write(const pj_json_elem *elem, char *buffer, unsigned *size) { struct buf_writer_data buf_data; pj_status_t status; PJ_ASSERT_RETURN(elem && buffer && size, PJ_EINVAL); buf_data.pos = buffer; buf_data.size = *size; status = pj_json_writef(elem, &buf_writer, &buf_data); if (status != PJ_SUCCESS) return status; *buf_data.pos = '\0'; *size = (unsigned)(buf_data.pos - buffer); return PJ_SUCCESS; } #define MAX_INDENT 100 #ifndef PJ_JSON_NAME_MIN_LEN # define PJ_JSON_NAME_MIN_LEN 20 #endif #define ESC_BUF_LEN 64 #ifndef PJ_JSON_INDENT_SIZE # define PJ_JSON_INDENT_SIZE 3 #endif struct write_state { pj_json_writer writer; void *user_data; char indent_buf[MAX_INDENT]; int indent; char space[PJ_JSON_NAME_MIN_LEN]; }; #define CHECK(expr) do { \ status=expr; if (status!=PJ_SUCCESS) return status; } \ while (0) static pj_status_t write_string_escaped(const pj_str_t *value, struct write_state *st) { const char *ip = value->ptr; const char *iend = value->ptr + value->slen; char buf[ESC_BUF_LEN]; char *op = buf; char *oend = buf + ESC_BUF_LEN; pj_status_t status; while (ip != iend) { /* Write to buffer to speedup writing instead of calling * the callback one by one for each character. */ while (ip != iend && op != oend) { if (oend - op < 2) break; if (*ip == '"') { *op++ = '\\'; *op++ = '"'; ip++; } else if (*ip == '\\') { *op++ = '\\'; *op++ = '\\'; ip++; } else if (*ip == '/') { *op++ = '\\'; *op++ = '/'; ip++; } else if (*ip == '\b') { *op++ = '\\'; *op++ = 'b'; ip++; } else if (*ip == '\f') { *op++ = '\\'; *op++ = 'f'; ip++; } else if (*ip == '\n') { *op++ = '\\'; *op++ = 'n'; ip++; } else if (*ip == '\r') { *op++ = '\\'; *op++ = 'r'; ip++; } else if (*ip == '\t') { *op++ = '\\'; *op++ = 't'; ip++; } else if ((*ip >= 32 && *ip < 127)) { /* unescaped */ *op++ = *ip++; } else { /* escaped */ if (oend - op < 6) break; *op++ = '\\'; *op++ = 'u'; *op++ = '0'; *op++ = '0'; pj_val_to_hex_digit(*ip, op); op+=2; ip++; } } CHECK( st->writer( buf, (unsigned)(op-buf), st->user_data) ); op = buf; } return PJ_SUCCESS; } static pj_status_t write_children(const pj_json_list *list, const char quotes[2], struct write_state *st) { unsigned flags = (quotes[0]=='[') ? NO_NAME : 0; pj_status_t status; //CHECK( st->writer( st->indent_buf, st->indent, st->user_data) ); CHECK( st->writer( "es[0], 1, st->user_data) ); CHECK( st->writer( " ", 1, st->user_data) ); if (!pj_list_empty(list)) { pj_bool_t indent_added = PJ_FALSE; pj_json_elem *child = list->next; if (child->name.slen == 0) { /* Simple list */ while (child != (pj_json_elem*)list) { status = elem_write(child, st, flags); if (status != PJ_SUCCESS) return status; if (child->next != (pj_json_elem*)list) CHECK( st->writer( ", ", 2, st->user_data) ); child = child->next; } } else { if (st->indent < sizeof(st->indent_buf)) { st->indent += PJ_JSON_INDENT_SIZE; indent_added = PJ_TRUE; } CHECK( st->writer( "\n", 1, st->user_data) ); while (child != (pj_json_elem*)list) { status = elem_write(child, st, flags); if (status != PJ_SUCCESS) return status; if (child->next != (pj_json_elem*)list) CHECK( st->writer( ",\n", 2, st->user_data) ); else CHECK( st->writer( "\n", 1, st->user_data) ); child = child->next; } if (indent_added) { st->indent -= PJ_JSON_INDENT_SIZE; } CHECK( st->writer( st->indent_buf, st->indent, st->user_data) ); } } CHECK( st->writer( "es[1], 1, st->user_data) ); return PJ_SUCCESS; } static pj_status_t elem_write(const pj_json_elem *elem, struct write_state *st, unsigned flags) { pj_status_t status; if (elem->name.slen) { CHECK( st->writer( st->indent_buf, st->indent, st->user_data) ); if ((flags & NO_NAME)==0) { CHECK( st->writer( "\"", 1, st->user_data) ); CHECK( write_string_escaped(&elem->name, st) ); CHECK( st->writer( "\": ", 3, st->user_data) ); if (elem->name.slen < PJ_JSON_NAME_MIN_LEN /*&& elem->type != PJ_JSON_VAL_OBJ && elem->type != PJ_JSON_VAL_ARRAY*/) { CHECK( st->writer( st->space, (unsigned)(PJ_JSON_NAME_MIN_LEN - elem->name.slen), st->user_data) ); } } } switch (elem->type) { case PJ_JSON_VAL_NULL: CHECK( st->writer( "null", 4, st->user_data) ); break; case PJ_JSON_VAL_BOOL: if (elem->value.is_true) CHECK( st->writer( "true", 4, st->user_data) ); else CHECK( st->writer( "false", 5, st->user_data) ); break; case PJ_JSON_VAL_NUMBER: { char num_buf[65]; int len; if (elem->value.num == (int)elem->value.num) len = pj_ansi_snprintf(num_buf, sizeof(num_buf), "%d", (int)elem->value.num); else len = pj_ansi_snprintf(num_buf, sizeof(num_buf), "%f", elem->value.num); if (len < 0 || len >= sizeof(num_buf)) return PJ_ETOOBIG; CHECK( st->writer( num_buf, len, st->user_data) ); } break; case PJ_JSON_VAL_STRING: CHECK( st->writer( "\"", 1, st->user_data) ); CHECK( write_string_escaped( &elem->value.str, st) ); CHECK( st->writer( "\"", 1, st->user_data) ); break; case PJ_JSON_VAL_ARRAY: CHECK( write_children(&elem->value.children, "[]", st) ); break; case PJ_JSON_VAL_OBJ: CHECK( write_children(&elem->value.children, "{}", st) ); break; default: pj_assert(!"Unhandled value type"); } return PJ_SUCCESS; } #undef CHECK PJ_DEF(pj_status_t) pj_json_writef( const pj_json_elem *elem, pj_json_writer writer, void *user_data) { struct write_state st; PJ_ASSERT_RETURN(elem && writer, PJ_EINVAL); st.writer = writer; st.user_data = user_data, st.indent = 0; pj_memset(st.indent_buf, ' ', MAX_INDENT); pj_memset(st.space, ' ', PJ_JSON_NAME_MIN_LEN); return elem_write(elem, &st, 0); } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/md5.c ================================================ /* $Id: md5.c 1001 2007-02-25 15:38:32Z bennylp $ */ /* * This is the implementation of MD5 algorithm, based on the code * written by Colin Plumb. This file is put in public domain. */ #include #include /* pj_memcpy */ /* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. */ #if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN != 0 #define HIGHFIRST 1 #endif #ifndef HIGHFIRST #define byteReverse(buf, len) /* Nothing */ #else void byteReverse(unsigned char *buf, unsigned longs); #ifndef ASM_MD5 /* * Note: this code is harmless on little-endian machines. */ void byteReverse(unsigned char *buf, unsigned longs) { pj_uint32_t t; do { t = (pj_uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | ((unsigned) buf[1] << 8 | buf[0]); *(pj_uint32_t *) buf = t; buf += 4; } while (--longs); } #endif #endif static void MD5Transform(pj_uint32_t buf[4], pj_uint32_t const in[16]); /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ PJ_DEF(void) pj_md5_init(pj_md5_context *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; ctx->buf[2] = 0x98badcfe; ctx->buf[3] = 0x10325476; ctx->bits[0] = 0; ctx->bits[1] = 0; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ PJ_DEF(void) pj_md5_update( pj_md5_context *ctx, unsigned char const *buf, unsigned len) { pj_uint32_t t; /* Update bitcount */ t = ctx->bits[0]; if ((ctx->bits[0] = t + ((pj_uint32_t) len << 3)) < t) ctx->bits[1]++; /* Carry from low to high */ ctx->bits[1] += len >> 29; t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ /* Handle any leading odd-sized chunks */ if (t) { unsigned char *p = (unsigned char *) ctx->in + t; t = 64 - t; if (len < t) { pj_memcpy(p, buf, len); return; } pj_memcpy(p, buf, t); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (pj_uint32_t *) ctx->in); buf += t; len -= t; } /* Process data in 64-byte chunks */ while (len >= 64) { pj_memcpy(ctx->in, buf, 64); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (pj_uint32_t *) ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ pj_memcpy(ctx->in, buf, len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ PJ_DEF(void) pj_md5_final(pj_md5_context *ctx, unsigned char digest[16]) { unsigned count; unsigned char *p; /* Compute number of bytes mod 64 */ count = (ctx->bits[0] >> 3) & 0x3F; /* Set the first char of padding to 0x80. This is safe since there is always at least one byte free */ p = ctx->in + count; *p++ = 0x80; /* Bytes of padding needed to make 64 bytes */ count = 64 - 1 - count; /* Pad out to 56 mod 64 */ if (count < 8) { /* Two lots of padding: Pad the first block to 64 bytes */ pj_bzero(p, count); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (pj_uint32_t *) ctx->in); /* Now fill the next block with 56 bytes */ pj_bzero(ctx->in, 56); } else { /* Pad block to 56 bytes */ pj_bzero(p, count - 8); } byteReverse(ctx->in, 14); /* Append length in bits and transform */ ((pj_uint32_t *) ctx->in)[14] = ctx->bits[0]; ((pj_uint32_t *) ctx->in)[15] = ctx->bits[1]; MD5Transform(ctx->buf, (pj_uint32_t *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); pj_memcpy(digest, ctx->buf, 16); pj_bzero(ctx, sizeof(*ctx)); /* In case it's sensitive */ } #ifndef ASM_MD5 /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f, w, x, y, z, data, s) \ ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ static void MD5Transform(pj_uint32_t buf[4], pj_uint32_t const in[16]) { register pj_uint32_t a, b, c, d; a = buf[0]; b = buf[1]; c = buf[2]; d = buf[3]; MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } #endif ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/pcap.c ================================================ /* $Id: pcap.c 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #if 0 # define TRACE_(x) PJ_LOG(5,x) #else # define TRACE_(x) #endif #pragma pack(1) typedef struct pj_pcap_hdr { pj_uint32_t magic_number; /* magic number */ pj_uint16_t version_major; /* major version number */ pj_uint16_t version_minor; /* minor version number */ pj_int32_t thiszone; /* GMT to local correction */ pj_uint32_t sigfigs; /* accuracy of timestamps */ pj_uint32_t snaplen; /* max length of captured packets, in octets */ pj_uint32_t network; /* data link type */ } pj_pcap_hdr; typedef struct pj_pcap_rec_hdr { pj_uint32_t ts_sec; /* timestamp seconds */ pj_uint32_t ts_usec; /* timestamp microseconds */ pj_uint32_t incl_len; /* number of octets of packet saved in file */ pj_uint32_t orig_len; /* actual length of packet */ } pj_pcap_rec_hdr; #if 0 /* gcc insisted on aligning this struct to 32bit on ARM */ typedef struct pj_pcap_eth_hdr { pj_uint8_t dest[6]; pj_uint8_t src[6]; pj_uint8_t len[2]; } pj_pcap_eth_hdr; #else typedef pj_uint8_t pj_pcap_eth_hdr[14]; #endif typedef struct pj_pcap_ip_hdr { pj_uint8_t v_ihl; pj_uint8_t tos; pj_uint16_t len; pj_uint16_t id; pj_uint16_t flags_fragment; pj_uint8_t ttl; pj_uint8_t proto; pj_uint16_t csum; pj_uint32_t ip_src; pj_uint32_t ip_dst; } pj_pcap_ip_hdr; /* Implementation of pcap file */ struct pj_pcap_file { char obj_name[PJ_MAX_OBJ_NAME]; pj_oshandle_t fd; pj_bool_t swap; pj_pcap_hdr hdr; pj_pcap_filter filter; }; /* Init default filter */ PJ_DEF(void) pj_pcap_filter_default(pj_pcap_filter *filter) { pj_bzero(filter, sizeof(*filter)); } /* Open pcap file */ PJ_DEF(pj_status_t) pj_pcap_open(pj_pool_t *pool, const char *path, pj_pcap_file **p_file) { pj_pcap_file *file; pj_ssize_t sz; pj_status_t status; PJ_ASSERT_RETURN(pool && path && p_file, PJ_EINVAL); /* More sanity checks */ TRACE_(("pcap", "sizeof(pj_pcap_eth_hdr)=%d", sizeof(pj_pcap_eth_hdr))); PJ_ASSERT_RETURN(sizeof(pj_pcap_eth_hdr)==14, PJ_EBUG); TRACE_(("pcap", "sizeof(pj_pcap_ip_hdr)=%d", sizeof(pj_pcap_ip_hdr))); PJ_ASSERT_RETURN(sizeof(pj_pcap_ip_hdr)==20, PJ_EBUG); TRACE_(("pcap", "sizeof(pj_pcap_udp_hdr)=%d", sizeof(pj_pcap_udp_hdr))); PJ_ASSERT_RETURN(sizeof(pj_pcap_udp_hdr)==8, PJ_EBUG); file = PJ_POOL_ZALLOC_T(pool, pj_pcap_file); pj_ansi_strcpy(file->obj_name, "pcap"); status = pj_file_open(pool, path, PJ_O_RDONLY, &file->fd); if (status != PJ_SUCCESS) return status; /* Read file pcap header */ sz = sizeof(file->hdr); status = pj_file_read(file->fd, &file->hdr, &sz); if (status != PJ_SUCCESS) { pj_file_close(file->fd); return status; } /* Check magic number */ if (file->hdr.magic_number == 0xa1b2c3d4) { file->swap = PJ_FALSE; } else if (file->hdr.magic_number == 0xd4c3b2a1) { file->swap = PJ_TRUE; file->hdr.network = pj_ntohl(file->hdr.network); } else { /* Not PCAP file */ pj_file_close(file->fd); return PJ_EINVALIDOP; } TRACE_((file->obj_name, "PCAP file %s opened", path)); *p_file = file; return PJ_SUCCESS; } /* Close pcap file */ PJ_DEF(pj_status_t) pj_pcap_close(pj_pcap_file *file) { PJ_ASSERT_RETURN(file, PJ_EINVAL); TRACE_((file->obj_name, "PCAP file closed")); return pj_file_close(file->fd); } /* Setup filter */ PJ_DEF(pj_status_t) pj_pcap_set_filter(pj_pcap_file *file, const pj_pcap_filter *fil) { PJ_ASSERT_RETURN(file && fil, PJ_EINVAL); pj_memcpy(&file->filter, fil, sizeof(pj_pcap_filter)); return PJ_SUCCESS; } /* Read file */ static pj_status_t read_file(pj_pcap_file *file, void *buf, pj_ssize_t *sz) { pj_status_t status; status = pj_file_read(file->fd, buf, sz); if (status != PJ_SUCCESS) return status; if (*sz == 0) return PJ_EEOF; return PJ_SUCCESS; } static pj_status_t skip(pj_oshandle_t fd, pj_off_t bytes) { pj_status_t status; status = pj_file_setpos(fd, bytes, PJ_SEEK_CUR); if (status != PJ_SUCCESS) return status; return PJ_SUCCESS; } #define SKIP_PKT() \ if (rec_incl > sz_read) { \ status = skip(file->fd, rec_incl-sz_read);\ if (status != PJ_SUCCESS) \ return status; \ } /* Read UDP packet */ PJ_DEF(pj_status_t) pj_pcap_read_udp(pj_pcap_file *file, pj_pcap_udp_hdr *udp_hdr, pj_uint8_t *udp_payload, pj_size_t *udp_payload_size) { PJ_ASSERT_RETURN(file && udp_payload && udp_payload_size, PJ_EINVAL); PJ_ASSERT_RETURN(*udp_payload_size, PJ_EINVAL); /* Check data link type in PCAP file header */ if ((file->filter.link && file->hdr.network != (pj_uint32_t)file->filter.link) || file->hdr.network != PJ_PCAP_LINK_TYPE_ETH) { /* Link header other than Ethernet is not supported for now */ return PJ_ENOTSUP; } /* Loop until we have the packet */ for (;;) { union { pj_pcap_rec_hdr rec; pj_pcap_eth_hdr eth; pj_pcap_ip_hdr ip; pj_pcap_udp_hdr udp; } tmp; unsigned rec_incl; pj_ssize_t sz; pj_size_t sz_read = 0; pj_status_t status; TRACE_((file->obj_name, "Reading packet..")); /* Read PCAP packet header */ sz = sizeof(tmp.rec); status = read_file(file, &tmp.rec, &sz); if (status != PJ_SUCCESS) { TRACE_((file->obj_name, "read_file() error: %d", status)); return status; } rec_incl = tmp.rec.incl_len; /* Swap byte ordering */ if (file->swap) { tmp.rec.incl_len = pj_ntohl(tmp.rec.incl_len); tmp.rec.orig_len = pj_ntohl(tmp.rec.orig_len); tmp.rec.ts_sec = pj_ntohl(tmp.rec.ts_sec); tmp.rec.ts_usec = pj_ntohl(tmp.rec.ts_usec); } /* Read link layer header */ switch (file->hdr.network) { case PJ_PCAP_LINK_TYPE_ETH: sz = sizeof(tmp.eth); status = read_file(file, &tmp.eth, &sz); break; default: TRACE_((file->obj_name, "Error: link layer not Ethernet")); return PJ_ENOTSUP; } if (status != PJ_SUCCESS) { TRACE_((file->obj_name, "Error reading Eth header: %d", status)); return status; } sz_read += sz; /* Read IP header */ sz = sizeof(tmp.ip); status = read_file(file, &tmp.ip, &sz); if (status != PJ_SUCCESS) { TRACE_((file->obj_name, "Error reading IP header: %d", status)); return status; } sz_read += sz; /* Skip if IP source mismatch */ if (file->filter.ip_src && tmp.ip.ip_src != file->filter.ip_src) { TRACE_((file->obj_name, "IP source %s mismatch, skipping", pj_inet_ntoa(*(pj_in_addr*)&tmp.ip.ip_src))); SKIP_PKT(); continue; } /* Skip if IP destination mismatch */ if (file->filter.ip_dst && tmp.ip.ip_dst != file->filter.ip_dst) { TRACE_((file->obj_name, "IP detination %s mismatch, skipping", pj_inet_ntoa(*(pj_in_addr*)&tmp.ip.ip_dst))); SKIP_PKT(); continue; } /* Skip if proto mismatch */ if (file->filter.proto && tmp.ip.proto != file->filter.proto) { TRACE_((file->obj_name, "IP proto %d mismatch, skipping", tmp.ip.proto)); SKIP_PKT(); continue; } /* Read transport layer header */ switch (tmp.ip.proto) { case PJ_PCAP_PROTO_TYPE_UDP: sz = sizeof(tmp.udp); status = read_file(file, &tmp.udp, &sz); if (status != PJ_SUCCESS) { TRACE_((file->obj_name, "Error reading UDP header: %d",status)); return status; } sz_read += sz; /* Skip if source port mismatch */ if (file->filter.src_port && tmp.udp.src_port != file->filter.src_port) { TRACE_((file->obj_name, "UDP src port %d mismatch, skipping", pj_ntohs(tmp.udp.src_port))); SKIP_PKT(); continue; } /* Skip if destination port mismatch */ if (file->filter.dst_port && tmp.udp.dst_port != file->filter.dst_port) { TRACE_((file->obj_name, "UDP dst port %d mismatch, skipping", pj_ntohs(tmp.udp.dst_port))); SKIP_PKT(); continue; } /* Copy UDP header if caller wants it */ if (udp_hdr) { pj_memcpy(udp_hdr, &tmp.udp, sizeof(*udp_hdr)); } /* Calculate payload size */ sz = pj_ntohs(tmp.udp.len) - sizeof(tmp.udp); break; default: TRACE_((file->obj_name, "Not UDP, skipping")); SKIP_PKT(); continue; } /* Check if payload fits the buffer */ if (sz > (pj_ssize_t)*udp_payload_size) { TRACE_((file->obj_name, "Error: packet too large (%d bytes required)", sz)); SKIP_PKT(); return PJ_ETOOSMALL; } /* Read the payload */ status = read_file(file, udp_payload, &sz); if (status != PJ_SUCCESS) { TRACE_((file->obj_name, "Error reading payload: %d", status)); return status; } sz_read += sz; *udp_payload_size = sz; // Some layers may have trailer, e.g: link eth2. /* Check that we've read all the packets */ //PJ_ASSERT_RETURN(sz_read == rec_incl, PJ_EBUG); /* Skip trailer */ while (sz_read < rec_incl) { sz = rec_incl - sz_read; status = read_file(file, &tmp.eth, &sz); if (status != PJ_SUCCESS) { TRACE_((file->obj_name, "Error reading trailer: %d", status)); return status; } sz_read += sz; } return PJ_SUCCESS; } /* Does not reach here */ } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/resolver.c ================================================ /* $Id: resolver.c 4333 2013-01-23 09:53:39Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "resolver.c" /* Check that maximum DNS nameservers is not too large. * This has got todo with the datatype to index the nameserver in the query. */ #if PJ_DNS_RESOLVER_MAX_NS > 256 # error "PJ_DNS_RESOLVER_MAX_NS is too large (max=256)" #endif #define RES_HASH_TABLE_SIZE 127 /**< Hash table size (must be 2^n-1 */ #define PORT 53 /**< Default NS port. */ #define Q_HASH_TABLE_SIZE 127 /**< Query hash table size */ #define TIMER_SIZE 127 /**< Initial number of timers. */ #define MAX_FD 3 /**< Maximum internal sockets. */ #define RES_BUF_SZ PJ_DNS_RESOLVER_RES_BUF_SIZE #define UDPSZ PJ_DNS_RESOLVER_MAX_UDP_SIZE #define TMP_SZ PJ_DNS_RESOLVER_TMP_BUF_SIZE /* Nameserver state */ enum ns_state { STATE_PROBING, STATE_ACTIVE, STATE_BAD, }; static const char *state_names[3] = { "Probing", "Active", "Bad" }; /* * Each nameserver entry. * A name server is identified by its socket address (IP and port). * Each NS will have a flag to indicate whether it's properly functioning. */ struct nameserver { pj_sockaddr_in addr; /**< Server address. */ enum ns_state state; /**< Nameserver state. */ pj_time_val state_expiry; /**< Time set next state. */ pj_time_val rt_delay; /**< Response time. */ /* For calculating rt_delay: */ pj_uint16_t q_id; /**< Query ID. */ pj_time_val sent_time; /**< Time this query is sent. */ }; /* Child query list head * See comments on pj_dns_async_query below. */ struct query_head { PJ_DECL_LIST_MEMBER(pj_dns_async_query); }; /* Key to look for outstanding query and/or cached response */ struct res_key { pj_uint16_t qtype; /**< Query type. */ char name[PJ_MAX_HOSTNAME]; /**< Name being queried */ }; /* * This represents each asynchronous query entry. * This entry will be put in two hash tables, the first one keyed on the DNS * transaction ID to match response with the query, and the second one keyed * on "res_key" structure above to match a new request against outstanding * requests. * * An asynchronous entry may have child entries; child entries are subsequent * queries to the same resource while there is pending query on the same * DNS resource name and type. When a query has child entries, once the * response is received (or error occurs), the response will trigger callback * invocations for all childs entries. * * Note: when application cancels the query, the callback member will be * set to NULL, but for simplicity, the query will be let running. */ struct pj_dns_async_query { PJ_DECL_LIST_MEMBER(pj_dns_async_query); /**< List member. */ pj_dns_resolver *resolver; /**< The resolver instance. */ pj_uint16_t id; /**< Transaction ID. */ unsigned transmit_cnt; /**< Number of transmissions. */ struct res_key key; /**< Key to index this query. */ pj_hash_entry_buf hbufid; /**< Hash buffer 1 */ pj_hash_entry_buf hbufkey; /**< Hash buffer 2 */ pj_timer_entry timer_entry; /**< Timer to manage timeouts */ unsigned options; /**< Query options. */ void *user_data; /**< Application data. */ pj_dns_callback *cb; /**< Callback to be called. */ struct query_head child_head; /**< Child queries list head. */ }; /* This structure is used to keep cached response entry. * The cache is a hash table keyed on "res_key" structure above. */ struct cached_res { PJ_DECL_LIST_MEMBER(struct cached_res); pj_pool_t *pool; /**< Cache's pool. */ struct res_key key; /**< Resource key. */ pj_hash_entry_buf hbuf; /**< Hash buffer */ pj_time_val expiry_time; /**< Expiration time. */ pj_dns_parsed_packet *pkt; /**< The response packet. */ unsigned ref_cnt; /**< Reference counter. */ }; /* Resolver entry */ struct pj_dns_resolver { pj_str_t name; /**< Resolver instance name for id. */ /* Internals */ pj_pool_t *pool; /**< Internal pool. */ pj_mutex_t *mutex; /**< Mutex protection. */ pj_bool_t own_timer; /**< Do we own timer? */ pj_timer_heap_t *timer; /**< Timer instance. */ pj_bool_t own_ioqueue; /**< Do we own ioqueue? */ pj_ioqueue_t *ioqueue; /**< Ioqueue instance. */ char tmp_pool[TMP_SZ];/**< Temporary pool buffer. */ /* Socket */ pj_sock_t udp_sock; /**< UDP socket. */ pj_ioqueue_key_t *udp_key; /**< UDP socket ioqueue key. */ unsigned char udp_rx_pkt[UDPSZ];/**< UDP receive buffer. */ unsigned char udp_tx_pkt[UDPSZ];/**< UDP receive buffer. */ pj_ssize_t udp_len; /**< Length of received packet. */ pj_ioqueue_op_key_t udp_op_rx_key; /**< UDP read operation key. */ pj_ioqueue_op_key_t udp_op_tx_key; /**< UDP write operation key. */ pj_sockaddr_in udp_src_addr; /**< Source address of packet */ int udp_addr_len; /**< Source address length. */ /* Settings */ pj_dns_settings settings; /**< Resolver settings. */ /* Nameservers */ unsigned ns_count; /**< Number of name servers. */ struct nameserver ns[PJ_DNS_RESOLVER_MAX_NS]; /**< Array of NS. */ /* Last DNS transaction ID used. */ pj_uint16_t last_id; /* Hash table for cached response */ pj_hash_table_t *hrescache; /**< Cached response in hash table */ /* Pending asynchronous query, hashed by transaction ID. */ pj_hash_table_t *hquerybyid; /* Pending asynchronous query, hashed by "res_key" */ pj_hash_table_t *hquerybyres; /* Query entries free list */ struct query_head query_free_nodes; }; /* Callback from ioqueue when packet is received */ static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read); /* Callback to be called when query has timed out */ static void on_timeout( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry); /* Select which nameserver to use */ static pj_status_t select_nameservers(pj_dns_resolver *resolver, unsigned *count, unsigned servers[]); /* Close UDP socket */ static void close_sock(pj_dns_resolver *resv) { /* Close existing socket */ if (resv->udp_key != NULL) { pj_ioqueue_unregister(resv->udp_key); resv->udp_key = NULL; resv->udp_sock = PJ_INVALID_SOCKET; } else if (resv->udp_sock != PJ_INVALID_SOCKET) { pj_sock_close(resv->udp_sock); resv->udp_sock = PJ_INVALID_SOCKET; } } /* Initialize UDP socket */ static pj_status_t init_sock(pj_dns_resolver *resv) { pj_ioqueue_callback socket_cb; pj_status_t status; /* Create the UDP socket */ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &resv->udp_sock); if (status != PJ_SUCCESS) return status; /* Bind to any address/port */ status = pj_sock_bind_in(resv->udp_sock, 0, 0); if (status != PJ_SUCCESS) return status; /* Register to ioqueue */ pj_bzero(&socket_cb, sizeof(socket_cb)); socket_cb.on_read_complete = &on_read_complete; status = pj_ioqueue_register_sock(resv->pool, resv->ioqueue, resv->udp_sock, resv, &socket_cb, &resv->udp_key); if (status != PJ_SUCCESS) return status; pj_ioqueue_op_key_init(&resv->udp_op_rx_key, sizeof(resv->udp_op_rx_key)); pj_ioqueue_op_key_init(&resv->udp_op_tx_key, sizeof(resv->udp_op_tx_key)); /* Start asynchronous read to the UDP socket */ resv->udp_len = sizeof(resv->udp_rx_pkt); resv->udp_addr_len = sizeof(resv->udp_src_addr); status = pj_ioqueue_recvfrom(resv->udp_key, &resv->udp_op_rx_key, resv->udp_rx_pkt, &resv->udp_len, PJ_IOQUEUE_ALWAYS_ASYNC, &resv->udp_src_addr, &resv->udp_addr_len); if (status != PJ_EPENDING) return status; return PJ_SUCCESS; } /* Initialize DNS settings with default values */ PJ_DEF(void) pj_dns_settings_default(pj_dns_settings *s) { pj_bzero(s, sizeof(pj_dns_settings)); s->qretr_delay = PJ_DNS_RESOLVER_QUERY_RETRANSMIT_DELAY; s->qretr_count = PJ_DNS_RESOLVER_QUERY_RETRANSMIT_COUNT; s->cache_max_ttl = PJ_DNS_RESOLVER_MAX_TTL; s->good_ns_ttl = PJ_DNS_RESOLVER_GOOD_NS_TTL; s->bad_ns_ttl = PJ_DNS_RESOLVER_BAD_NS_TTL; } /* * Create the resolver. */ PJ_DEF(pj_status_t) pj_dns_resolver_create( pj_pool_factory *pf, const char *name, unsigned options, pj_timer_heap_t *timer, pj_ioqueue_t *ioqueue, pj_dns_resolver **p_resolver) { pj_pool_t *pool; pj_dns_resolver *resv; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(pf && p_resolver, PJ_EINVAL); if (name == NULL) name = THIS_FILE; /* Create and initialize resolver instance */ pool = pj_pool_create(pf, name, 4000, 4000, NULL); if (!pool) return PJ_ENOMEM; /* Create pool and name */ resv = PJ_POOL_ZALLOC_T(pool, struct pj_dns_resolver); resv->pool = pool; resv->udp_sock = PJ_INVALID_SOCKET; pj_strdup2_with_null(pool, &resv->name, name); /* Create the mutex */ status = pj_mutex_create_recursive(pool, name, &resv->mutex); if (status != PJ_SUCCESS) goto on_error; /* Timer, ioqueue, and settings */ resv->timer = timer; resv->ioqueue = ioqueue; resv->last_id = 1; pj_dns_settings_default(&resv->settings); resv->settings.options = options; /* Create the timer heap if one is not specified */ if (resv->timer == NULL) { status = pj_timer_heap_create(pool, TIMER_SIZE, &resv->timer); if (status != PJ_SUCCESS) goto on_error; } /* Create the ioqueue if one is not specified */ if (resv->ioqueue == NULL) { status = pj_ioqueue_create(pool, MAX_FD, &resv->ioqueue); if (status != PJ_SUCCESS) goto on_error; } /* Response cache hash table */ resv->hrescache = pj_hash_create(pool, RES_HASH_TABLE_SIZE); /* Query hash table and free list. */ resv->hquerybyid = pj_hash_create(pool, Q_HASH_TABLE_SIZE); resv->hquerybyres = pj_hash_create(pool, Q_HASH_TABLE_SIZE); pj_list_init(&resv->query_free_nodes); /* Initialize the UDP socket */ status = init_sock(resv); if (status != PJ_SUCCESS) goto on_error; /* Looks like everything is okay */ *p_resolver = resv; return PJ_SUCCESS; on_error: pj_dns_resolver_destroy(resv, PJ_FALSE); return status; } /* * Destroy DNS resolver instance. */ PJ_DEF(pj_status_t) pj_dns_resolver_destroy( pj_dns_resolver *resolver, pj_bool_t notify) { pj_hash_iterator_t it_buf, *it; PJ_ASSERT_RETURN(resolver, PJ_EINVAL); if (notify) { /* * Notify pending queries if requested. */ it = pj_hash_first(resolver->hquerybyid, &it_buf); while (it) { pj_dns_async_query *q = (pj_dns_async_query *) pj_hash_this(resolver->hquerybyid, it); pj_dns_async_query *cq; if (q->cb) (*q->cb)(q->user_data, PJ_ECANCELLED, NULL); cq = q->child_head.next; while (cq != (pj_dns_async_query*)&q->child_head) { if (cq->cb) (*cq->cb)(cq->user_data, PJ_ECANCELLED, NULL); cq = cq->next; } it = pj_hash_next(resolver->hquerybyid, it); } } /* Destroy cached entries */ it = pj_hash_first(resolver->hrescache, &it_buf); while (it) { struct cached_res *cache; cache = (struct cached_res*) pj_hash_this(resolver->hrescache, it); pj_hash_set(NULL, resolver->hrescache, &cache->key, sizeof(cache->key), 0, NULL); pj_pool_release(cache->pool); it = pj_hash_first(resolver->hrescache, &it_buf); } if (resolver->own_timer && resolver->timer) { pj_timer_heap_destroy(resolver->timer); resolver->timer = NULL; } close_sock(resolver); if (resolver->own_ioqueue && resolver->ioqueue) { pj_ioqueue_destroy(resolver->ioqueue); resolver->ioqueue = NULL; } if (resolver->mutex) { pj_mutex_destroy(resolver->mutex); resolver->mutex = NULL; } if (resolver->pool) { pj_pool_t *pool = resolver->pool; resolver->pool = NULL; pj_pool_release(pool); } return PJ_SUCCESS; } /* * Configure name servers for the DNS resolver. */ PJ_DEF(pj_status_t) pj_dns_resolver_set_ns( pj_dns_resolver *resolver, unsigned count, const pj_str_t servers[], const pj_uint16_t ports[]) { unsigned i; pj_time_val now; pj_status_t status; PJ_ASSERT_RETURN(resolver && count && servers, PJ_EINVAL); PJ_ASSERT_RETURN(count < PJ_DNS_RESOLVER_MAX_NS, PJ_EINVAL); pj_mutex_lock(resolver->mutex); if (count > PJ_DNS_RESOLVER_MAX_NS) count = PJ_DNS_RESOLVER_MAX_NS; resolver->ns_count = 0; pj_bzero(resolver->ns, sizeof(resolver->ns)); pj_gettimeofday(&now); for (i=0; ins[i]; status = pj_sockaddr_in_init(&ns->addr, &servers[i], (pj_uint16_t)(ports ? ports[i] : PORT)); if (status != PJ_SUCCESS) { pj_mutex_unlock(resolver->mutex); return PJLIB_UTIL_EDNSINNSADDR; } ns->state = STATE_ACTIVE; ns->state_expiry = now; ns->rt_delay.sec = 10; } resolver->ns_count = count; pj_mutex_unlock(resolver->mutex); return PJ_SUCCESS; } /* * Modify the resolver settings. */ PJ_DEF(pj_status_t) pj_dns_resolver_set_settings(pj_dns_resolver *resolver, const pj_dns_settings *st) { PJ_ASSERT_RETURN(resolver && st, PJ_EINVAL); pj_mutex_lock(resolver->mutex); pj_memcpy(&resolver->settings, st, sizeof(*st)); pj_mutex_unlock(resolver->mutex); return PJ_SUCCESS; } /* * Get the resolver current settings. */ PJ_DEF(pj_status_t) pj_dns_resolver_get_settings( pj_dns_resolver *resolver, pj_dns_settings *st) { PJ_ASSERT_RETURN(resolver && st, PJ_EINVAL); pj_mutex_lock(resolver->mutex); pj_memcpy(st, &resolver->settings, sizeof(*st)); pj_mutex_unlock(resolver->mutex); return PJ_SUCCESS; } /* * Poll for events from the resolver. */ PJ_DEF(void) pj_dns_resolver_handle_events(pj_dns_resolver *resolver, const pj_time_val *timeout) { PJ_ASSERT_ON_FAIL(resolver, return); pj_mutex_lock(resolver->mutex); pj_timer_heap_poll(resolver->timer, NULL); pj_mutex_unlock(resolver->mutex); pj_ioqueue_poll(resolver->ioqueue, timeout); } /* Get one query node from the free node, if any, or allocate * a new one. */ static pj_dns_async_query *alloc_qnode(pj_dns_resolver *resolver, unsigned options, void *user_data, pj_dns_callback *cb) { pj_dns_async_query *q; /* Merge query options with resolver options */ options |= resolver->settings.options; if (!pj_list_empty(&resolver->query_free_nodes)) { q = resolver->query_free_nodes.next; pj_list_erase(q); pj_bzero(q, sizeof(*q)); } else { q = PJ_POOL_ZALLOC_T(resolver->pool, pj_dns_async_query); } /* Init query */ q->resolver = resolver; q->options = options; q->user_data = user_data; q->cb = cb; pj_list_init(&q->child_head); return q; } /* * Transmit query. */ static pj_status_t transmit_query(pj_dns_resolver *resolver, pj_dns_async_query *q) { unsigned pkt_size; unsigned i, server_cnt; unsigned servers[PJ_DNS_RESOLVER_MAX_NS]; pj_time_val now; pj_str_t name; pj_time_val delay; pj_status_t status; /* Select which nameserver(s) to send requests to. */ server_cnt = PJ_ARRAY_SIZE(servers); status = select_nameservers(resolver, &server_cnt, servers); if (status != PJ_SUCCESS) { return status; } if (server_cnt == 0) { return PJLIB_UTIL_EDNSNOWORKINGNS; } /* Start retransmit/timeout timer for the query */ pj_assert(q->timer_entry.id == 0); q->timer_entry.id = 1; q->timer_entry.user_data = q; q->timer_entry.cb = &on_timeout; delay.sec = 0; delay.msec = resolver->settings.qretr_delay; pj_time_val_normalize(&delay); status = pj_timer_heap_schedule(resolver->timer, &q->timer_entry, &delay); if (status != PJ_SUCCESS) { return status; } /* Check if the socket is available for sending */ if (pj_ioqueue_is_pending(resolver->udp_key, &resolver->udp_op_tx_key)) { ++q->transmit_cnt; PJ_LOG(4,(resolver->name.ptr, "Socket busy in transmitting DNS %s query for %s%s", pj_dns_get_type_name(q->key.qtype), q->key.name, (q->transmit_cnt < resolver->settings.qretr_count? ", will try again later":""))); return PJ_SUCCESS; } /* Create DNS query packet */ pkt_size = sizeof(resolver->udp_tx_pkt); name = pj_str(q->key.name); status = pj_dns_make_query(resolver->udp_tx_pkt, &pkt_size, q->id, q->key.qtype, &name); if (status != PJ_SUCCESS) { pj_timer_heap_cancel(resolver->timer, &q->timer_entry); return status; } /* Get current time. */ pj_gettimeofday(&now); /* Send the packet to name servers */ for (i=0; ins[servers[i]]; status = pj_ioqueue_sendto(resolver->udp_key, &resolver->udp_op_tx_key, resolver->udp_tx_pkt, &sent, 0, &resolver->ns[servers[i]].addr, sizeof(pj_sockaddr_in)); PJ_PERROR(4,(resolver->name.ptr, status, "%s %d bytes to NS %d (%s:%d): DNS %s query for %s", (q->transmit_cnt==0? "Transmitting":"Re-transmitting"), (int)pkt_size, servers[i], pj_inet_ntoa(ns->addr.sin_addr), (int)pj_ntohs(ns->addr.sin_port), pj_dns_get_type_name(q->key.qtype), q->key.name)); if (ns->q_id == 0) { ns->q_id = q->id; ns->sent_time = now; } } ++q->transmit_cnt; return PJ_SUCCESS; } /* * Initialize resource key for hash table lookup. */ static void init_res_key(struct res_key *key, int type, const pj_str_t *name) { unsigned i; pj_size_t len; char *dst = key->name; const char *src = name->ptr; pj_bzero(key, sizeof(struct res_key)); key->qtype = (pj_uint16_t)type; len = name->slen; if (len > PJ_MAX_HOSTNAME) len = PJ_MAX_HOSTNAME; /* Copy key, in lowercase */ for (i=0; ipool->factory, "dnscache", RES_BUF_SZ, 256, NULL); cache = PJ_POOL_ZALLOC_T(pool, struct cached_res); cache->pool = pool; cache->ref_cnt = 1; return cache; } /* Re-allocate cache entry, to free cached packet */ static void reset_entry(struct cached_res **p_cached) { pj_pool_t *pool; struct cached_res *cache = *p_cached; unsigned ref_cnt; pool = cache->pool; ref_cnt = cache->ref_cnt; pj_pool_reset(pool); cache = PJ_POOL_ZALLOC_T(pool, struct cached_res); cache->pool = pool; cache->ref_cnt = ref_cnt; *p_cached = cache; } /* Put unused/expired cached entry to the free list */ static void free_entry(pj_dns_resolver *resolver, struct cached_res *cache) { PJ_UNUSED_ARG(resolver); pj_pool_release(cache->pool); } /* * Create and start asynchronous DNS query for a single resource. */ PJ_DEF(pj_status_t) pj_dns_resolver_start_query( pj_dns_resolver *resolver, const pj_str_t *name, int type, unsigned options, pj_dns_callback *cb, void *user_data, pj_dns_async_query **p_query) { pj_time_val now; struct res_key key; struct cached_res *cache; pj_dns_async_query *q; pj_uint32_t hval; pj_status_t status = PJ_SUCCESS; /* Validate arguments */ PJ_ASSERT_RETURN(resolver && name && type, PJ_EINVAL); /* Check name is not too long. */ PJ_ASSERT_RETURN(name->slen>0 && name->slen < PJ_MAX_HOSTNAME, PJ_ENAMETOOLONG); /* Check type */ PJ_ASSERT_RETURN(type > 0 && type < 0xFFFF, PJ_EINVAL); if (p_query) *p_query = NULL; /* Build resource key for looking up hash tables */ init_res_key(&key, type, name); /* Start working with the resolver */ pj_mutex_lock(resolver->mutex); /* Get current time. */ pj_gettimeofday(&now); /* First, check if we have cached response for the specified name/type, * and the cached entry has not expired. */ hval = 0; cache = (struct cached_res *) pj_hash_get(resolver->hrescache, &key, sizeof(key), &hval); if (cache) { /* We've found a cached entry. */ /* Check for expiration */ if (PJ_TIME_VAL_GT(cache->expiry_time, now)) { /* Log */ PJ_LOG(5,(resolver->name.ptr, "Picked up DNS %s record for %.*s from cache, ttl=%d", pj_dns_get_type_name(type), (int)name->slen, name->ptr, (int)(cache->expiry_time.sec - now.sec))); /* Map DNS Rcode in the response into PJLIB status name space */ status = PJ_DNS_GET_RCODE(cache->pkt->hdr.flags); status = PJ_STATUS_FROM_DNS_RCODE(status); /* Workaround for deadlock problem. Need to increment the cache's * ref counter first before releasing mutex, so the cache won't be * destroyed by other thread while in callback. */ cache->ref_cnt++; pj_mutex_unlock(resolver->mutex); /* This cached response is still valid. Just return this * response to caller. */ if (cb) { (*cb)(user_data, status, cache->pkt); } /* Done. No host resolution is necessary */ pj_mutex_lock(resolver->mutex); /* Decrement the ref counter. Also check if it is time to free * the cache (as it has been expired). */ cache->ref_cnt--; if (cache->ref_cnt <= 0) free_entry(resolver, cache); /* Must return PJ_SUCCESS */ status = PJ_SUCCESS; goto on_return; } /* At this point, we have a cached entry, but this entry has expired. * Remove this entry from the cached list. */ pj_hash_set(NULL, resolver->hrescache, &key, sizeof(key), 0, NULL); /* Also free the cache, if it is not being used (by callback). */ cache->ref_cnt--; if (cache->ref_cnt <= 0) free_entry(resolver, cache); /* Must continue with creating a query now */ } /* Next, check if we have pending query on the same resource */ q = (pj_dns_async_query *) pj_hash_get(resolver->hquerybyres, &key, sizeof(key), NULL); if (q) { /* Yes, there's another pending query to the same key. * Just create a new child query and add this query to * pending query's child queries. */ pj_dns_async_query *nq; nq = alloc_qnode(resolver, options, user_data, cb); pj_list_push_back(&q->child_head, nq); /* Done. This child query will be notified once the "parent" * query completes. */ status = PJ_SUCCESS; goto on_return; } /* There's no pending query to the same key, initiate a new one. */ q = alloc_qnode(resolver, options, user_data, cb); /* Save the ID and key */ /* TODO: dnsext-forgery-resilient: randomize id for security */ q->id = resolver->last_id++; if (resolver->last_id == 0) resolver->last_id = 1; pj_memcpy(&q->key, &key, sizeof(struct res_key)); /* Send the query */ status = transmit_query(resolver, q); if (status != PJ_SUCCESS) { pj_list_push_back(&resolver->query_free_nodes, q); goto on_return; } /* Add query entry to the hash tables */ pj_hash_set_np(resolver->hquerybyid, &q->id, sizeof(q->id), 0, q->hbufid, q); pj_hash_set_np(resolver->hquerybyres, &q->key, sizeof(q->key), 0, q->hbufkey, q); if (p_query) *p_query = q; on_return: pj_mutex_unlock(resolver->mutex); return status; } /* * Cancel a pending query. */ PJ_DEF(pj_status_t) pj_dns_resolver_cancel_query(pj_dns_async_query *query, pj_bool_t notify) { pj_dns_callback *cb; PJ_ASSERT_RETURN(query, PJ_EINVAL); pj_mutex_lock(query->resolver->mutex); cb = query->cb; query->cb = NULL; if (notify) (*cb)(query->user_data, PJ_ECANCELLED, NULL); pj_mutex_unlock(query->resolver->mutex); return PJ_SUCCESS; } /* * DNS response containing A packet. */ PJ_DEF(pj_status_t) pj_dns_parse_a_response(const pj_dns_parsed_packet *pkt, pj_dns_a_record *rec) { enum { MAX_SEARCH = 20 }; pj_str_t hostname, alias = {NULL, 0}, *resname; pj_size_t bufstart = 0; pj_size_t bufleft = sizeof(rec->buf_); unsigned i, ansidx, search_cnt=0; PJ_ASSERT_RETURN(pkt && rec, PJ_EINVAL); /* Init the record */ pj_bzero(rec, sizeof(pj_dns_a_record)); /* Return error if there's error in the packet. */ if (PJ_DNS_GET_RCODE(pkt->hdr.flags)) return PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_GET_RCODE(pkt->hdr.flags)); /* Return error if there's no query section */ if (pkt->hdr.qdcount == 0) return PJLIB_UTIL_EDNSINANSWER; /* Return error if there's no answer */ if (pkt->hdr.anscount == 0) return PJLIB_UTIL_EDNSNOANSWERREC; /* Get the hostname from the query. */ hostname = pkt->q[0].name; /* Copy hostname to the record */ if (hostname.slen > (int)bufleft) { return PJ_ENAMETOOLONG; } pj_memcpy(&rec->buf_[bufstart], hostname.ptr, hostname.slen); rec->name.ptr = &rec->buf_[bufstart]; rec->name.slen = hostname.slen; bufstart += hostname.slen; bufleft -= hostname.slen; /* Find the first RR which name matches the hostname */ for (ansidx=0; ansidx < pkt->hdr.anscount; ++ansidx) { if (pj_stricmp(&pkt->ans[ansidx].name, &hostname)==0) break; } if (ansidx == pkt->hdr.anscount) return PJLIB_UTIL_EDNSNOANSWERREC; resname = &hostname; /* Keep following CNAME records. */ while (pkt->ans[ansidx].type == PJ_DNS_TYPE_CNAME && search_cnt++ < MAX_SEARCH) { resname = &pkt->ans[ansidx].rdata.cname.name; if (!alias.slen) alias = *resname; for (i=0; i < pkt->hdr.anscount; ++i) { if (pj_stricmp(resname, &pkt->ans[i].name)==0) { break; } } if (i==pkt->hdr.anscount) return PJLIB_UTIL_EDNSNOANSWERREC; ansidx = i; } if (search_cnt >= MAX_SEARCH) return PJLIB_UTIL_EDNSINANSWER; if (pkt->ans[ansidx].type != PJ_DNS_TYPE_A) return PJLIB_UTIL_EDNSINANSWER; /* Copy alias to the record, if present. */ if (alias.slen) { if (alias.slen > (int)bufleft) return PJ_ENAMETOOLONG; pj_memcpy(&rec->buf_[bufstart], alias.ptr, alias.slen); rec->alias.ptr = &rec->buf_[bufstart]; rec->alias.slen = alias.slen; bufstart += alias.slen; bufleft -= alias.slen; } /* Get the IP addresses. */ for (i=0; i < pkt->hdr.anscount; ++i) { if (pkt->ans[i].type == PJ_DNS_TYPE_A && pj_stricmp(&pkt->ans[i].name, resname)==0 && rec->addr_count < PJ_DNS_MAX_IP_IN_A_REC) { rec->addr[rec->addr_count++].s_addr = pkt->ans[i].rdata.a.ip_addr.s_addr; } } if (rec->addr_count == 0) return PJLIB_UTIL_EDNSNOANSWERREC; return PJ_SUCCESS; } /* Set nameserver state */ static void set_nameserver_state(pj_dns_resolver *resolver, unsigned index, enum ns_state state, const pj_time_val *now) { struct nameserver *ns = &resolver->ns[index]; enum ns_state old_state = ns->state; ns->state = state; ns->state_expiry = *now; if (state == STATE_PROBING) ns->state_expiry.sec += ((resolver->settings.qretr_count + 2) * resolver->settings.qretr_delay) / 1000; else if (state == STATE_ACTIVE) ns->state_expiry.sec += resolver->settings.good_ns_ttl; else ns->state_expiry.sec += resolver->settings.bad_ns_ttl; PJ_LOG(5, (resolver->name.ptr, "Nameserver %s:%d state changed %s --> %s", pj_inet_ntoa(ns->addr.sin_addr), (int)pj_ntohs(ns->addr.sin_port), state_names[old_state], state_names[state])); } /* Select which nameserver(s) to use. Note this may return multiple * name servers. The algorithm to select which nameservers to be * sent the request to is as follows: * - select the first nameserver that is known to be good for the * last PJ_DNS_RESOLVER_GOOD_NS_TTL interval. * - for all NSes, if last_known_good >= PJ_DNS_RESOLVER_GOOD_NS_TTL, * include the NS to re-check again that the server is still good, * unless the NS is known to be bad in the last PJ_DNS_RESOLVER_BAD_NS_TTL * interval. * - for all NSes, if last_known_bad >= PJ_DNS_RESOLVER_BAD_NS_TTL, * also include the NS to re-check again that the server is still bad. */ static pj_status_t select_nameservers(pj_dns_resolver *resolver, unsigned *count, unsigned servers[]) { unsigned i, max_count=*count; int min; pj_time_val now; pj_assert(max_count > 0); *count = 0; servers[0] = 0xFFFF; /* Check that nameservers are configured. */ if (resolver->ns_count == 0) return PJLIB_UTIL_EDNSNONS; pj_gettimeofday(&now); /* Select one Active nameserver with best response time. */ for (min=-1, i=0; ins_count; ++i) { struct nameserver *ns = &resolver->ns[i]; if (ns->state != STATE_ACTIVE) continue; if (min == -1) min = i; else if (PJ_TIME_VAL_LT(ns->rt_delay, resolver->ns[min].rt_delay)) min = i; } if (min != -1) { servers[0] = min; ++(*count); } /* Scan nameservers. */ for (i=0; ins_count && *count < max_count; ++i) { struct nameserver *ns = &resolver->ns[i]; if (PJ_TIME_VAL_LTE(ns->state_expiry, now)) { if (ns->state == STATE_PROBING) { set_nameserver_state(resolver, i, STATE_BAD, &now); } else { set_nameserver_state(resolver, i, STATE_PROBING, &now); if ((int)i != min) { servers[*count] = i; ++(*count); } } } else if (ns->state == STATE_PROBING && (int)i != min) { servers[*count] = i; ++(*count); } } return PJ_SUCCESS; } /* Update name server status */ static void report_nameserver_status(pj_dns_resolver *resolver, const pj_sockaddr_in *ns_addr, const pj_dns_parsed_packet *pkt) { unsigned i; int rcode; pj_uint32_t q_id; pj_time_val now; pj_bool_t is_good; /* Only mark nameserver as "bad" if it returned non-parseable response or * it returned the following status codes */ if (pkt) { rcode = PJ_DNS_GET_RCODE(pkt->hdr.flags); q_id = pkt->hdr.id; } else { rcode = 0; q_id = (pj_uint32_t)-1; } if (!pkt || rcode == PJ_DNS_RCODE_SERVFAIL || rcode == PJ_DNS_RCODE_REFUSED || rcode == PJ_DNS_RCODE_NOTAUTH) { is_good = PJ_FALSE; } else { is_good = PJ_TRUE; } /* Mark time */ pj_gettimeofday(&now); /* Recheck all nameservers. */ for (i=0; ins_count; ++i) { struct nameserver *ns = &resolver->ns[i]; if (ns->addr.sin_addr.s_addr == ns_addr->sin_addr.s_addr && ns->addr.sin_port == ns_addr->sin_port && ns->addr.sin_family == ns_addr->sin_family) { if (q_id == ns->q_id) { /* Calculate response time */ pj_time_val rt = now; PJ_TIME_VAL_SUB(rt, ns->sent_time); ns->rt_delay = rt; ns->q_id = 0; } set_nameserver_state(resolver, i, (is_good ? STATE_ACTIVE : STATE_BAD), &now); break; } } } /* Update response cache */ static void update_res_cache(pj_dns_resolver *resolver, const struct res_key *key, pj_status_t status, pj_bool_t set_expiry, const pj_dns_parsed_packet *pkt) { struct cached_res *cache; pj_uint32_t hval=0, ttl; /* If status is unsuccessful, clear the same entry from the cache */ if (status != PJ_SUCCESS) { cache = (struct cached_res *) pj_hash_get(resolver->hrescache, key, sizeof(*key), &hval); /* Remove the entry before releasing its pool (see ticket #1710) */ pj_hash_set(NULL, resolver->hrescache, key, sizeof(*key), hval, NULL); /* Free the entry */ if (cache && --cache->ref_cnt <= 0) free_entry(resolver, cache); } /* Calculate expiration time. */ if (set_expiry) { if (pkt->hdr.anscount == 0 || status != PJ_SUCCESS) { /* If we don't have answers for the name, then give a different * ttl value (note: PJ_DNS_RESOLVER_INVALID_TTL may be zero, * which means that invalid names won't be kept in the cache) */ ttl = PJ_DNS_RESOLVER_INVALID_TTL; } else { /* Otherwise get the minimum TTL from the answers */ unsigned i; ttl = 0xFFFFFFFF; for (i=0; ihdr.anscount; ++i) { if (pkt->ans[i].ttl < ttl) ttl = pkt->ans[i].ttl; } } } else { ttl = 0xFFFFFFFF; } /* Apply maximum TTL */ if (ttl > resolver->settings.cache_max_ttl) ttl = resolver->settings.cache_max_ttl; /* If TTL is zero, clear the same entry in the hash table */ if (ttl == 0) { cache = (struct cached_res *) pj_hash_get(resolver->hrescache, key, sizeof(*key), &hval); /* Remove the entry before releasing its pool (see ticket #1710) */ pj_hash_set(NULL, resolver->hrescache, key, sizeof(*key), hval, NULL); /* Free the entry */ if (cache && --cache->ref_cnt <= 0) free_entry(resolver, cache); return; } /* Get a cache response entry */ cache = (struct cached_res *) pj_hash_get(resolver->hrescache, key, sizeof(*key), &hval); if (cache == NULL) { cache = alloc_entry(resolver); } else if (cache->ref_cnt > 1) { /* When cache entry is being used by callback (to app), just decrement * ref_cnt so it will be freed after the callback returns and allocate * new entry. */ cache->ref_cnt--; cache = alloc_entry(resolver); } else { /* Remove the entry before resetting its pool (see ticket #1710) */ pj_hash_set(NULL, resolver->hrescache, key, sizeof(*key), hval, NULL); /* Reset cache to avoid bloated cache pool */ reset_entry(&cache); } /* Duplicate the packet. * We don't need to keep the NS and AR sections from the packet, * so exclude from duplication. We do need to keep the Query * section since DNS A parser needs the query section to know * the name being requested. */ pj_dns_packet_dup(cache->pool, pkt, PJ_DNS_NO_NS | PJ_DNS_NO_AR, &cache->pkt); /* Calculate expiration time */ if (set_expiry) { pj_gettimeofday(&cache->expiry_time); cache->expiry_time.sec += ttl; } else { cache->expiry_time.sec = 0x7FFFFFFFL; cache->expiry_time.msec = 0; } /* Copy key to the cached response */ pj_memcpy(&cache->key, key, sizeof(*key)); /* Update the hash table */ pj_hash_set_np(resolver->hrescache, &cache->key, sizeof(*key), hval, cache->hbuf, cache); } /* Callback to be called when query has timed out */ static void on_timeout( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { pj_dns_resolver *resolver; pj_dns_async_query *q, *cq; pj_status_t status; PJ_UNUSED_ARG(timer_heap); q = (pj_dns_async_query *) entry->user_data; resolver = q->resolver; pj_mutex_lock(resolver->mutex); /* Recheck that this query is still pending, since there is a slight * possibility of race condition (timer elapsed while at the same time * response arrives) */ if (pj_hash_get(resolver->hquerybyid, &q->id, sizeof(q->id), NULL)==NULL) { /* Yeah, this query is done. */ pj_mutex_unlock(resolver->mutex); return; } /* Invalidate id. */ q->timer_entry.id = 0; /* Check to see if we should retransmit instead of time out */ if (q->transmit_cnt < resolver->settings.qretr_count) { status = transmit_query(resolver, q); if (status == PJ_SUCCESS) { pj_mutex_unlock(resolver->mutex); return; } else { /* Error occurs */ char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(resolver->name.ptr, "Error transmitting request: %s", errmsg)); /* Let it fallback to timeout section below */ } } /* Clear hash table entries */ pj_hash_set(NULL, resolver->hquerybyid, &q->id, sizeof(q->id), 0, NULL); pj_hash_set(NULL, resolver->hquerybyres, &q->key, sizeof(q->key), 0, NULL); /* Workaround for deadlock problem in #1565 (similar to #1108) */ pj_mutex_unlock(resolver->mutex); /* Call application callback, if any. */ if (q->cb) (*q->cb)(q->user_data, PJ_ETIMEDOUT, NULL); /* Call application callback for child queries. */ cq = q->child_head.next; while (cq != (void*)&q->child_head) { if (cq->cb) (*cq->cb)(cq->user_data, PJ_ETIMEDOUT, NULL); cq = cq->next; } /* Workaround for deadlock problem in #1565 (similar to #1108) */ pj_mutex_lock(resolver->mutex); /* Clear data */ q->timer_entry.id = 0; q->user_data = NULL; /* Put child entries into recycle list */ cq = q->child_head.next; while (cq != (void*)&q->child_head) { pj_dns_async_query *next = cq->next; pj_list_push_back(&resolver->query_free_nodes, cq); cq = next; } /* Put query entry into recycle list */ pj_list_push_back(&resolver->query_free_nodes, q); pj_mutex_unlock(resolver->mutex); } /* Callback from ioqueue when packet is received */ static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { pj_dns_resolver *resolver; pj_pool_t *pool = NULL; pj_dns_parsed_packet *dns_pkt; pj_dns_async_query *q; pj_status_t status; PJ_USE_EXCEPTION; resolver = (pj_dns_resolver *) pj_ioqueue_get_user_data(key); pj_mutex_lock(resolver->mutex); /* Check for errors */ if (bytes_read < 0) { char errmsg[PJ_ERR_MSG_SIZE]; status = (pj_status_t)-bytes_read; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(resolver->name.ptr, "DNS resolver read error from %s:%d: %s", pj_inet_ntoa(resolver->udp_src_addr.sin_addr), pj_ntohs(resolver->udp_src_addr.sin_port), errmsg)); goto read_next_packet; } PJ_LOG(5,(resolver->name.ptr, "Received %d bytes DNS response from %s:%d", (int)bytes_read, pj_inet_ntoa(resolver->udp_src_addr.sin_addr), pj_ntohs(resolver->udp_src_addr.sin_port))); /* Check for zero packet */ if (bytes_read == 0) goto read_next_packet; /* Create temporary pool from a fixed buffer */ pool = pj_pool_create_on_buf("restmp", resolver->tmp_pool, sizeof(resolver->tmp_pool)); /* Parse DNS response */ status = -1; dns_pkt = NULL; PJ_TRY { status = pj_dns_parse_packet(pool, resolver->udp_rx_pkt, (unsigned)bytes_read, &dns_pkt); } PJ_CATCH_ANY { status = PJ_ENOMEM; } PJ_END; /* Update nameserver status */ report_nameserver_status(resolver, &resolver->udp_src_addr, dns_pkt); /* Handle parse error */ if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(3,(resolver->name.ptr, "Error parsing DNS response from %s:%d: %s", pj_inet_ntoa(resolver->udp_src_addr.sin_addr), pj_ntohs(resolver->udp_src_addr.sin_port), errmsg)); goto read_next_packet; } /* Find the query based on the transaction ID */ q = (pj_dns_async_query*) pj_hash_get(resolver->hquerybyid, &dns_pkt->hdr.id, sizeof(dns_pkt->hdr.id), NULL); if (!q) { PJ_LOG(5,(resolver->name.ptr, "DNS response from %s:%d id=%d discarded", pj_inet_ntoa(resolver->udp_src_addr.sin_addr), pj_ntohs(resolver->udp_src_addr.sin_port), (unsigned)dns_pkt->hdr.id)); goto read_next_packet; } /* Map DNS Rcode in the response into PJLIB status name space */ status = PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_GET_RCODE(dns_pkt->hdr.flags)); /* Cancel query timeout timer. */ pj_assert(q->timer_entry.id != 0); pj_timer_heap_cancel(resolver->timer, &q->timer_entry); q->timer_entry.id = 0; /* Clear hash table entries */ pj_hash_set(NULL, resolver->hquerybyid, &q->id, sizeof(q->id), 0, NULL); pj_hash_set(NULL, resolver->hquerybyres, &q->key, sizeof(q->key), 0, NULL); /* Workaround for deadlock problem in #1108 */ pj_mutex_unlock(resolver->mutex); /* Notify applications first, to allow application to modify the * record before it is saved to the hash table. */ if (q->cb) (*q->cb)(q->user_data, status, dns_pkt); /* If query has subqueries, notify subqueries's application callback */ if (!pj_list_empty(&q->child_head)) { pj_dns_async_query *child_q; child_q = q->child_head.next; while (child_q != (pj_dns_async_query*)&q->child_head) { if (child_q->cb) (*child_q->cb)(child_q->user_data, status, dns_pkt); child_q = child_q->next; } } /* Workaround for deadlock problem in #1108 */ pj_mutex_lock(resolver->mutex); /* Save/update response cache. */ update_res_cache(resolver, &q->key, status, PJ_TRUE, dns_pkt); /* Recycle query objects, starting with the child queries */ if (!pj_list_empty(&q->child_head)) { pj_dns_async_query *child_q; child_q = q->child_head.next; while (child_q != (pj_dns_async_query*)&q->child_head) { pj_dns_async_query *next = child_q->next; pj_list_erase(child_q); pj_list_push_back(&resolver->query_free_nodes, child_q); child_q = next; } } pj_list_push_back(&resolver->query_free_nodes, q); read_next_packet: if (pool) { /* needed just in case PJ_HAS_POOL_ALT_API is set */ pj_pool_release(pool); } bytes_read = sizeof(resolver->udp_rx_pkt); resolver->udp_addr_len = sizeof(resolver->udp_src_addr); status = pj_ioqueue_recvfrom(resolver->udp_key, op_key, resolver->udp_rx_pkt, &bytes_read, PJ_IOQUEUE_ALWAYS_ASYNC, &resolver->udp_src_addr, &resolver->udp_addr_len); if (status != PJ_EPENDING) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(resolver->name.ptr, "DNS resolver ioqueue read error: %s", errmsg)); pj_assert(!"Unhandled error"); } pj_mutex_unlock(resolver->mutex); } /* * Put the specified DNS packet into DNS cache. This function is mainly used * for testing the resolver, however it can also be used to inject entries * into the resolver. */ PJ_DEF(pj_status_t) pj_dns_resolver_add_entry( pj_dns_resolver *resolver, const pj_dns_parsed_packet *pkt, pj_bool_t set_ttl) { struct res_key key; /* Sanity check */ PJ_ASSERT_RETURN(resolver && pkt, PJ_EINVAL); /* Packet must be a DNS response */ PJ_ASSERT_RETURN(PJ_DNS_GET_QR(pkt->hdr.flags) & 1, PJ_EINVAL); /* Make sure there are answers in the packet */ PJ_ASSERT_RETURN((pkt->hdr.anscount && pkt->ans) || (pkt->hdr.qdcount && pkt->q), PJLIB_UTIL_EDNSNOANSWERREC); pj_mutex_lock(resolver->mutex); /* Build resource key for looking up hash tables */ pj_bzero(&key, sizeof(struct res_key)); if (pkt->hdr.anscount) { /* Make sure name is not too long. */ PJ_ASSERT_RETURN(pkt->ans[0].name.slen < PJ_MAX_HOSTNAME, PJ_ENAMETOOLONG); init_res_key(&key, pkt->ans[0].type, &pkt->ans[0].name); } else { /* Make sure name is not too long. */ PJ_ASSERT_RETURN(pkt->q[0].name.slen < PJ_MAX_HOSTNAME, PJ_ENAMETOOLONG); init_res_key(&key, pkt->q[0].type, &pkt->q[0].name); } /* Insert entry. */ update_res_cache(resolver, &key, PJ_SUCCESS, set_ttl, pkt); pj_mutex_unlock(resolver->mutex); return PJ_SUCCESS; } /* * Get the total number of response in the response cache. */ PJ_DEF(unsigned) pj_dns_resolver_get_cached_count(pj_dns_resolver *resolver) { unsigned count; PJ_ASSERT_RETURN(resolver, 0); pj_mutex_lock(resolver->mutex); count = pj_hash_count(resolver->hrescache); pj_mutex_unlock(resolver->mutex); return count; } /* * Dump resolver state to the log. */ PJ_DEF(void) pj_dns_resolver_dump(pj_dns_resolver *resolver, pj_bool_t detail) { #if PJ_LOG_MAX_LEVEL >= 3 unsigned i; pj_time_val now; pj_mutex_lock(resolver->mutex); pj_gettimeofday(&now); PJ_LOG(3,(resolver->name.ptr, " Dumping resolver state:")); PJ_LOG(3,(resolver->name.ptr, " Name servers:")); for (i=0; ins_count; ++i) { struct nameserver *ns = &resolver->ns[i]; PJ_LOG(3,(resolver->name.ptr, " NS %d: %s:%d (state=%s until %ds, rtt=%d ms)", i, pj_inet_ntoa(ns->addr.sin_addr), pj_ntohs(ns->addr.sin_port), state_names[ns->state], ns->state_expiry.sec - now.sec, PJ_TIME_VAL_MSEC(ns->rt_delay))); } PJ_LOG(3,(resolver->name.ptr, " Nb. of cached responses: %u", pj_hash_count(resolver->hrescache))); if (detail) { pj_hash_iterator_t itbuf, *it; it = pj_hash_first(resolver->hrescache, &itbuf); while (it) { struct cached_res *cache; cache = (struct cached_res*)pj_hash_this(resolver->hrescache, it); PJ_LOG(3,(resolver->name.ptr, " Type %s: %s", pj_dns_get_type_name(cache->key.qtype), cache->key.name)); it = pj_hash_next(resolver->hrescache, it); } } PJ_LOG(3,(resolver->name.ptr, " Nb. of pending queries: %u (%u)", pj_hash_count(resolver->hquerybyid), pj_hash_count(resolver->hquerybyres))); if (detail) { pj_hash_iterator_t itbuf, *it; it = pj_hash_first(resolver->hquerybyid, &itbuf); while (it) { struct pj_dns_async_query *q; q = (pj_dns_async_query*) pj_hash_this(resolver->hquerybyid, it); PJ_LOG(3,(resolver->name.ptr, " Type %s: %s", pj_dns_get_type_name(q->key.qtype), q->key.name)); it = pj_hash_next(resolver->hquerybyid, it); } } PJ_LOG(3,(resolver->name.ptr, " Nb. of pending query free nodes: %u", pj_list_size(&resolver->query_free_nodes))); PJ_LOG(3,(resolver->name.ptr, " Nb. of timer entries: %u", pj_timer_heap_count(resolver->timer))); PJ_LOG(3,(resolver->name.ptr, " Pool capacity: %d, used size: %d", pj_pool_get_capacity(resolver->pool), pj_pool_get_used_size(resolver->pool))); pj_mutex_unlock(resolver->mutex); #endif } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/resolver_wrap.cpp ================================================ /* $Id: resolver_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This file is a C++ wrapper, see ticket #886 for details. */ #include "resolver.c" ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/scanner.c ================================================ /* $Id: scanner.c 4209 2012-07-18 10:21:00Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #define PJ_SCAN_IS_SPACE(c) ((c)==' ' || (c)=='\t') #define PJ_SCAN_IS_NEWLINE(c) ((c)=='\r' || (c)=='\n') #define PJ_SCAN_IS_PROBABLY_SPACE(c) ((c) <= 32) #define PJ_SCAN_CHECK_EOF(s) (s != scanner->end) #if defined(PJ_SCANNER_USE_BITWISE) && PJ_SCANNER_USE_BITWISE != 0 # include "scanner_cis_bitwise.c" #else # include "scanner_cis_uint.c" #endif static void pj_scan_syntax_err(pj_scanner *scanner) { (*scanner->callback)(scanner); } PJ_DEF(void) pj_cis_add_range(pj_cis_t *cis, int cstart, int cend) { /* Can not set zero. This is the requirement of the parser. */ pj_assert(cstart > 0); while (cstart != cend) { PJ_CIS_SET(cis, cstart); ++cstart; } } PJ_DEF(void) pj_cis_add_alpha(pj_cis_t *cis) { pj_cis_add_range( cis, 'a', 'z'+1); pj_cis_add_range( cis, 'A', 'Z'+1); } PJ_DEF(void) pj_cis_add_num(pj_cis_t *cis) { pj_cis_add_range( cis, '0', '9'+1); } PJ_DEF(void) pj_cis_add_str( pj_cis_t *cis, const char *str) { while (*str) { PJ_CIS_SET(cis, *str); ++str; } } PJ_DEF(void) pj_cis_add_cis( pj_cis_t *cis, const pj_cis_t *rhs) { int i; for (i=0; i<256; ++i) { if (PJ_CIS_ISSET(rhs, i)) PJ_CIS_SET(cis, i); } } PJ_DEF(void) pj_cis_del_range( pj_cis_t *cis, int cstart, int cend) { while (cstart != cend) { PJ_CIS_CLR(cis, cstart); cstart++; } } PJ_DEF(void) pj_cis_del_str( pj_cis_t *cis, const char *str) { while (*str) { PJ_CIS_CLR(cis, *str); ++str; } } PJ_DEF(void) pj_cis_invert( pj_cis_t *cis ) { unsigned i; /* Can not set zero. This is the requirement of the parser. */ for (i=1; i<256; ++i) { if (PJ_CIS_ISSET(cis,i)) PJ_CIS_CLR(cis,i); else PJ_CIS_SET(cis,i); } } PJ_DEF(void) pj_scan_init( pj_scanner *scanner, char *bufstart, pj_size_t buflen, unsigned options, pj_syn_err_func_ptr callback ) { PJ_CHECK_STACK(); scanner->begin = scanner->curptr = bufstart; scanner->end = bufstart + buflen; scanner->line = 1; scanner->start_line = scanner->begin; scanner->callback = callback; scanner->skip_ws = options; if (scanner->skip_ws) pj_scan_skip_whitespace(scanner); } PJ_DEF(void) pj_scan_fini( pj_scanner *scanner ) { PJ_CHECK_STACK(); PJ_UNUSED_ARG(scanner); } PJ_DEF(void) pj_scan_skip_whitespace( pj_scanner *scanner ) { register char *s = scanner->curptr; while (PJ_SCAN_IS_SPACE(*s)) { ++s; } if (PJ_SCAN_IS_NEWLINE(*s) && (scanner->skip_ws & PJ_SCAN_AUTOSKIP_NEWLINE)) { for (;;) { if (*s == '\r') { ++s; if (*s == '\n') ++s; ++scanner->line; scanner->curptr = scanner->start_line = s; } else if (*s == '\n') { ++s; ++scanner->line; scanner->curptr = scanner->start_line = s; } else if (PJ_SCAN_IS_SPACE(*s)) { do { ++s; } while (PJ_SCAN_IS_SPACE(*s)); } else { break; } } } if (PJ_SCAN_IS_NEWLINE(*s) && (scanner->skip_ws & PJ_SCAN_AUTOSKIP_WS_HEADER)==PJ_SCAN_AUTOSKIP_WS_HEADER) { /* Check for header continuation. */ scanner->curptr = s; if (*s == '\r') { ++s; } if (*s == '\n') { ++s; } scanner->start_line = s; if (PJ_SCAN_IS_SPACE(*s)) { register char *t = s; do { ++t; } while (PJ_SCAN_IS_SPACE(*t)); ++scanner->line; scanner->curptr = t; } } else { scanner->curptr = s; } } PJ_DEF(void) pj_scan_skip_line( pj_scanner *scanner ) { char *s = pj_ansi_strchr(scanner->curptr, '\n'); if (!s) { scanner->curptr = scanner->end; } else { scanner->curptr = scanner->start_line = s+1; scanner->line++; } } PJ_DEF(int) pj_scan_peek( pj_scanner *scanner, const pj_cis_t *spec, pj_str_t *out) { register char *s = scanner->curptr; if (s >= scanner->end) { pj_scan_syntax_err(scanner); return -1; } /* Don't need to check EOF with PJ_SCAN_CHECK_EOF(s) */ while (pj_cis_match(spec, *s)) ++s; pj_strset3(out, scanner->curptr, s); return *s; } PJ_DEF(int) pj_scan_peek_n( pj_scanner *scanner, pj_size_t len, pj_str_t *out) { char *endpos = scanner->curptr + len; if (endpos > scanner->end) { pj_scan_syntax_err(scanner); return -1; } pj_strset(out, scanner->curptr, len); return *endpos; } PJ_DEF(int) pj_scan_peek_until( pj_scanner *scanner, const pj_cis_t *spec, pj_str_t *out) { register char *s = scanner->curptr; if (s >= scanner->end) { pj_scan_syntax_err(scanner); return -1; } while (PJ_SCAN_CHECK_EOF(s) && !pj_cis_match( spec, *s)) ++s; pj_strset3(out, scanner->curptr, s); return *s; } PJ_DEF(void) pj_scan_get( pj_scanner *scanner, const pj_cis_t *spec, pj_str_t *out) { register char *s = scanner->curptr; pj_assert(pj_cis_match(spec,0)==0); /* EOF is detected implicitly */ if (!pj_cis_match(spec, *s)) { pj_scan_syntax_err(scanner); return; } do { ++s; } while (pj_cis_match(spec, *s)); /* No need to check EOF here (PJ_SCAN_CHECK_EOF(s)) because * buffer is NULL terminated and pj_cis_match(spec,0) should be * false. */ pj_strset3(out, scanner->curptr, s); scanner->curptr = s; if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) { pj_scan_skip_whitespace(scanner); } } PJ_DEF(void) pj_scan_get_unescape( pj_scanner *scanner, const pj_cis_t *spec, pj_str_t *out) { register char *s = scanner->curptr; char *dst = s; pj_assert(pj_cis_match(spec,0)==0); /* Must not match character '%' */ pj_assert(pj_cis_match(spec,'%')==0); /* EOF is detected implicitly */ if (!pj_cis_match(spec, *s) && *s != '%') { pj_scan_syntax_err(scanner); return; } out->ptr = s; do { if (*s == '%') { if (s+3 <= scanner->end && pj_isxdigit(*(s+1)) && pj_isxdigit(*(s+2))) { *dst = (pj_uint8_t) ((pj_hex_digit_to_val(*(s+1)) << 4) + pj_hex_digit_to_val(*(s+2))); ++dst; s += 3; } else { *dst++ = *s++; *dst++ = *s++; break; } } if (pj_cis_match(spec, *s)) { char *start = s; do { ++s; } while (pj_cis_match(spec, *s)); if (dst != start) pj_memmove(dst, start, s-start); dst += (s-start); } } while (*s == '%'); scanner->curptr = s; out->slen = (dst - out->ptr); if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) { pj_scan_skip_whitespace(scanner); } } PJ_DEF(void) pj_scan_get_quote( pj_scanner *scanner, int begin_quote, int end_quote, pj_str_t *out) { char beg = (char)begin_quote; char end = (char)end_quote; pj_scan_get_quotes(scanner, &beg, &end, 1, out); } PJ_DEF(void) pj_scan_get_quotes(pj_scanner *scanner, const char *begin_quote, const char *end_quote, int qsize, pj_str_t *out) { register char *s = scanner->curptr; int qpair = -1; int i; pj_assert(qsize > 0); /* Check and eat the begin_quote. */ for (i = 0; i < qsize; ++i) { if (*s == begin_quote[i]) { qpair = i; break; } } if (qpair == -1) { pj_scan_syntax_err(scanner); return; } ++s; /* Loop until end_quote is found. */ do { /* loop until end_quote is found. */ while (PJ_SCAN_CHECK_EOF(s) && *s != '\n' && *s != end_quote[qpair]) { ++s; } /* check that no backslash character precedes the end_quote. */ if (*s == end_quote[qpair]) { if (*(s-1) == '\\') { char *q = s-2; char *r = s-2; while (r != scanner->begin && *r == '\\') { --r; } /* break from main loop if we have odd number of backslashes */ if (((unsigned)(q-r) & 0x01) == 1) { break; } ++s; } else { /* end_quote is not preceeded by backslash. break now. */ break; } } else { /* loop ended by non-end_quote character. break now. */ break; } } while (1); /* Check and eat the end quote. */ if (*s != end_quote[qpair]) { pj_scan_syntax_err(scanner); return; } ++s; pj_strset3(out, scanner->curptr, s); scanner->curptr = s; if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) { pj_scan_skip_whitespace(scanner); } } PJ_DEF(void) pj_scan_get_n( pj_scanner *scanner, unsigned N, pj_str_t *out) { if (scanner->curptr + N > scanner->end) { pj_scan_syntax_err(scanner); return; } pj_strset(out, scanner->curptr, N); scanner->curptr += N; if (PJ_SCAN_IS_PROBABLY_SPACE(*scanner->curptr) && scanner->skip_ws) { pj_scan_skip_whitespace(scanner); } } PJ_DEF(int) pj_scan_get_char( pj_scanner *scanner ) { int chr = *scanner->curptr; if (!chr) { pj_scan_syntax_err(scanner); return 0; } ++scanner->curptr; if (PJ_SCAN_IS_PROBABLY_SPACE(*scanner->curptr) && scanner->skip_ws) { pj_scan_skip_whitespace(scanner); } return chr; } PJ_DEF(void) pj_scan_get_newline( pj_scanner *scanner ) { if (!PJ_SCAN_IS_NEWLINE(*scanner->curptr)) { pj_scan_syntax_err(scanner); return; } if (*scanner->curptr == '\r') { ++scanner->curptr; } if (*scanner->curptr == '\n') { ++scanner->curptr; } ++scanner->line; scanner->start_line = scanner->curptr; /** * This probably is a bug, see PROTOS test #2480. * This would cause scanner to incorrectly eat two new lines, e.g. * when parsing: * * Content-Length: 120\r\n * \r\n * ... * * When pj_scan_get_newline() is called to parse the first newline * in the Content-Length header, it will eat the second newline * too because it thinks that it's a header continuation. * * if (PJ_SCAN_IS_PROBABLY_SPACE(*scanner->curptr) && scanner->skip_ws) { * pj_scan_skip_whitespace(scanner); * } */ } PJ_DEF(void) pj_scan_get_until( pj_scanner *scanner, const pj_cis_t *spec, pj_str_t *out) { register char *s = scanner->curptr; if (s >= scanner->end) { pj_scan_syntax_err(scanner); return; } while (PJ_SCAN_CHECK_EOF(s) && !pj_cis_match(spec, *s)) { ++s; } pj_strset3(out, scanner->curptr, s); scanner->curptr = s; if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) { pj_scan_skip_whitespace(scanner); } } PJ_DEF(void) pj_scan_get_until_ch( pj_scanner *scanner, int until_char, pj_str_t *out) { register char *s = scanner->curptr; if (s >= scanner->end) { pj_scan_syntax_err(scanner); return; } while (PJ_SCAN_CHECK_EOF(s) && *s != until_char) { ++s; } pj_strset3(out, scanner->curptr, s); scanner->curptr = s; if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) { pj_scan_skip_whitespace(scanner); } } PJ_DEF(void) pj_scan_get_until_chr( pj_scanner *scanner, const char *until_spec, pj_str_t *out) { register char *s = scanner->curptr; pj_size_t speclen; if (s >= scanner->end) { pj_scan_syntax_err(scanner); return; } speclen = strlen(until_spec); while (PJ_SCAN_CHECK_EOF(s) && !memchr(until_spec, *s, speclen)) { ++s; } pj_strset3(out, scanner->curptr, s); scanner->curptr = s; if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) { pj_scan_skip_whitespace(scanner); } } PJ_DEF(void) pj_scan_advance_n( pj_scanner *scanner, unsigned N, pj_bool_t skip_ws) { if (scanner->curptr + N > scanner->end) { pj_scan_syntax_err(scanner); return; } scanner->curptr += N; if (PJ_SCAN_IS_PROBABLY_SPACE(*scanner->curptr) && skip_ws) { pj_scan_skip_whitespace(scanner); } } PJ_DEF(int) pj_scan_strcmp( pj_scanner *scanner, const char *s, int len) { if (scanner->curptr + len > scanner->end) { pj_scan_syntax_err(scanner); return -1; } return strncmp(scanner->curptr, s, len); } PJ_DEF(int) pj_scan_stricmp( pj_scanner *scanner, const char *s, int len) { if (scanner->curptr + len > scanner->end) { pj_scan_syntax_err(scanner); return -1; } return pj_ansi_strnicmp(scanner->curptr, s, len); } PJ_DEF(int) pj_scan_stricmp_alnum( pj_scanner *scanner, const char *s, int len) { if (scanner->curptr + len > scanner->end) { pj_scan_syntax_err(scanner); return -1; } return strnicmp_alnum(scanner->curptr, s, len); } PJ_DEF(void) pj_scan_save_state( const pj_scanner *scanner, pj_scan_state *state) { state->curptr = scanner->curptr; state->line = scanner->line; state->start_line = scanner->start_line; } PJ_DEF(void) pj_scan_restore_state( pj_scanner *scanner, pj_scan_state *state) { scanner->curptr = state->curptr; scanner->line = state->line; scanner->start_line = state->start_line; } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/scanner_cis_bitwise.c ================================================ /* $Id: scanner_cis_bitwise.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * THIS FILE IS INCLUDED BY scanner.c. * DO NOT COMPILE THIS FILE ALONE! */ PJ_DEF(void) pj_cis_buf_init( pj_cis_buf_t *cis_buf) { pj_bzero(cis_buf->cis_buf, sizeof(cis_buf->cis_buf)); cis_buf->use_mask = 0; } PJ_DEF(pj_status_t) pj_cis_init(pj_cis_buf_t *cis_buf, pj_cis_t *cis) { unsigned i; cis->cis_buf = cis_buf->cis_buf; for (i=0; iuse_mask & (1 << i)) == 0) { cis->cis_id = i; cis_buf->use_mask |= (1 << i); return PJ_SUCCESS; } } cis->cis_id = PJ_CIS_MAX_INDEX; return PJ_ETOOMANY; } PJ_DEF(pj_status_t) pj_cis_dup( pj_cis_t *new_cis, pj_cis_t *existing) { pj_status_t status; unsigned i; /* Warning: typecasting here! */ status = pj_cis_init((pj_cis_buf_t*)existing->cis_buf, new_cis); if (status != PJ_SUCCESS) return status; for (i=0; i<256; ++i) { if (PJ_CIS_ISSET(existing, i)) PJ_CIS_SET(new_cis, i); else PJ_CIS_CLR(new_cis, i); } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/scanner_cis_uint.c ================================================ /* $Id: scanner_cis_uint.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * THIS FILE IS INCLUDED BY scanner.c. * DO NOT COMPILE THIS FILE ALONE! */ PJ_DEF(void) pj_cis_buf_init( pj_cis_buf_t *cis_buf) { /* Do nothing. */ PJ_UNUSED_ARG(cis_buf); } PJ_DEF(pj_status_t) pj_cis_init(pj_cis_buf_t *cis_buf, pj_cis_t *cis) { PJ_UNUSED_ARG(cis_buf); pj_bzero(cis->cis_buf, sizeof(cis->cis_buf)); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_cis_dup( pj_cis_t *new_cis, pj_cis_t *existing) { pj_memcpy(new_cis, existing, sizeof(pj_cis_t)); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/sha1.c ================================================ /* $Id: sha1.c 3549 2011-05-05 05:10:06Z nanang $ */ /* * Modified 2/07 * By Benny Prijono * Still 100% Public Domain * * This is the implementation of SHA-1 encryption algorithm based on * Steve Reid work. Modified to work with PJLIB. */ /* SHA-1 in C By Steve Reid 100% Public Domain ----------------- Modified 7/98 By James H. Brown Still 100% Public Domain Corrected a problem which generated improper hash values on 16 bit machines Routine SHA1Update changed from void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len) to void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned long len) The 'len' parameter was declared an int which works fine on 32 bit machines. However, on 16 bit machines an int is too small for the shifts being done against it. This caused the hash function to generate incorrect values if len was greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). Since the file IO in main() reads 16K at a time, any file 8K or larger would be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million "a"s). I also changed the declaration of variables i & j in SHA1Update to unsigned long from unsigned int for the same reason. These changes should make no difference to any 32 bit implementations since an int and a long are the same size in those environments. -- I also corrected a few compiler warnings generated by Borland C. 1. Added #include for exit() prototype 2. Removed unused variable 'j' in SHA1Final 3. Changed exit(0) to return(0) at end of main. ALL changes I made can be located by searching for comments containing 'JHB' ----------------- Modified 8/98 By Steve Reid Still 100% public domain 1- Removed #include and used return() instead of exit() 2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) 3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net ----------------- Modified 4/01 By Saul Kravitz Still 100% PD Modified to run on Compaq Alpha hardware. ----------------- Modified 07/2002 By Ralph Giles Still 100% public domain modified for use with stdint types, autoconf code cleanup, removed attribution comments switched SHA1Final() argument order for consistency use SHA1_ prefix for public api move public api to sha1.h */ /* Test Vectors (from FIPS PUB 180-1) "abc" A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 A million repetitions of "a" 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ /* #define SHA1HANDSOFF */ /* blp: #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "os_types.h" #include "sha1.h" */ #include #include #undef SHA1HANDSOFF static void SHA1_Transform(pj_uint32_t state[5], pj_uint8_t buffer[64]); #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ /* FIXME: can we do this in an endian-proof way? */ /* #ifdef WORDS_BIGENDIAN */ #if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN != 0 #define blk0(i) block->l[i] #else #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ |(rol(block->l[i],8)&0x00FF00FF)) #endif #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); /* Hash a single 512-bit block. This is the core of the algorithm. */ static void SHA1_Transform(pj_uint32_t state[5], pj_uint8_t buffer[64]) { pj_uint32_t a, b, c, d, e; typedef union { pj_uint8_t c[64]; pj_uint32_t l[16]; } CHAR64LONG16; CHAR64LONG16* block; #ifdef SHA1HANDSOFF static pj_uint8_t workspace[64]; block = (CHAR64LONG16*)workspace; pj_memcpy(block, buffer, 64); #else block = (CHAR64LONG16*)buffer; #endif /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; } /* SHA1Init - Initialize new context */ PJ_DEF(void) pj_sha1_init(pj_sha1_context* context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* Run your data through this. */ PJ_DEF(void) pj_sha1_update(pj_sha1_context* context, const pj_uint8_t* data, const pj_size_t len) { pj_size_t i, j; j = (context->count[0] >> 3) & 63; if ((context->count[0] += (pj_uint32_t)len << 3) < (len << 3)) context->count[1]++; context->count[1] += ((pj_uint32_t)len >> 29); if ((j + len) > 63) { pj_memcpy(&context->buffer[j], data, (i = 64-j)); SHA1_Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) { pj_uint8_t tmp[64]; pj_memcpy(tmp, data + i, 64); SHA1_Transform(context->state, tmp); } j = 0; } else i = 0; pj_memcpy(&context->buffer[j], &data[i], len - i); } /* Add padding and return the message digest. */ PJ_DEF(void) pj_sha1_final(pj_sha1_context* context, pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE]) { pj_uint32_t i; pj_uint8_t finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } pj_sha1_update(context, (pj_uint8_t *)"\200", 1); while ((context->count[0] & 504) != 448) { pj_sha1_update(context, (pj_uint8_t *)"\0", 1); } pj_sha1_update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ for (i = 0; i < PJ_SHA1_DIGEST_SIZE; i++) { digest[i] = (pj_uint8_t) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } /* Wipe variables */ i = 0; pj_memset(context->buffer, 0, 64); pj_memset(context->state, 0, 20); pj_memset(context->count, 0, 8); pj_memset(finalcount, 0, 8); /* SWR */ #ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ SHA1_Transform(context->state, context->buffer); #endif } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/srv_resolver.c ================================================ /* $Id: srv_resolver.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #define THIS_FILE "srv_resolver.c" #define ADDR_MAX_COUNT PJ_DNS_MAX_IP_IN_A_REC struct common { pj_dns_type type; /**< Type of this structure.*/ }; struct srv_target { struct common common; pj_dns_srv_async_query *parent; pj_str_t target_name; pj_dns_async_query *q_a; char target_buf[PJ_MAX_HOSTNAME]; pj_str_t cname; char cname_buf[PJ_MAX_HOSTNAME]; unsigned port; unsigned priority; unsigned weight; unsigned sum; unsigned addr_cnt; pj_in_addr addr[ADDR_MAX_COUNT]; }; struct pj_dns_srv_async_query { struct common common; char *objname; pj_dns_type dns_state; /**< DNS type being resolved. */ pj_dns_resolver *resolver; /**< Resolver SIP instance. */ void *token; pj_dns_async_query *q_srv; pj_dns_srv_resolver_cb *cb; pj_status_t last_error; /* Original request: */ unsigned option; pj_str_t full_name; pj_str_t domain_part; pj_uint16_t def_port; /* SRV records and their resolved IP addresses: */ unsigned srv_cnt; struct srv_target srv[PJ_DNS_SRV_MAX_ADDR]; /* Number of hosts in SRV records that the IP address has been resolved */ unsigned host_resolved; }; /* Async resolver callback, forward decl. */ static void dns_callback(void *user_data, pj_status_t status, pj_dns_parsed_packet *pkt); /* * The public API to invoke DNS SRV resolution. */ PJ_DEF(pj_status_t) pj_dns_srv_resolve( const pj_str_t *domain_name, const pj_str_t *res_name, unsigned def_port, pj_pool_t *pool, pj_dns_resolver *resolver, unsigned option, void *token, pj_dns_srv_resolver_cb *cb, pj_dns_srv_async_query **p_query) { pj_size_t len; pj_str_t target_name; pj_dns_srv_async_query *query_job; pj_status_t status; PJ_ASSERT_RETURN(domain_name && domain_name->slen && res_name && res_name->slen && pool && resolver && cb, PJ_EINVAL); /* Build full name */ len = domain_name->slen + res_name->slen + 2; target_name.ptr = (char*) pj_pool_alloc(pool, len); pj_strcpy(&target_name, res_name); if (res_name->ptr[res_name->slen-1] != '.') pj_strcat2(&target_name, "."); len = target_name.slen; pj_strcat(&target_name, domain_name); target_name.ptr[target_name.slen] = '\0'; /* Build the query_job state */ query_job = PJ_POOL_ZALLOC_T(pool, pj_dns_srv_async_query); query_job->common.type = PJ_DNS_TYPE_SRV; query_job->objname = target_name.ptr; query_job->resolver = resolver; query_job->token = token; query_job->cb = cb; query_job->option = option; query_job->full_name = target_name; query_job->domain_part.ptr = target_name.ptr + len; query_job->domain_part.slen = target_name.slen - len; query_job->def_port = (pj_uint16_t)def_port; /* Start the asynchronous query_job */ query_job->dns_state = PJ_DNS_TYPE_SRV; PJ_LOG(5, (query_job->objname, "Starting async DNS %s query_job: target=%.*s:%d", pj_dns_get_type_name(query_job->dns_state), (int)target_name.slen, target_name.ptr, def_port)); status = pj_dns_resolver_start_query(resolver, &target_name, query_job->dns_state, 0, &dns_callback, query_job, &query_job->q_srv); if (status==PJ_SUCCESS && p_query) *p_query = query_job; return status; } /* * Cancel pending query. */ PJ_DEF(pj_status_t) pj_dns_srv_cancel_query(pj_dns_srv_async_query *query, pj_bool_t notify) { pj_bool_t has_pending = PJ_FALSE; unsigned i; if (query->q_srv) { pj_dns_resolver_cancel_query(query->q_srv, PJ_FALSE); query->q_srv = NULL; has_pending = PJ_TRUE; } for (i=0; isrv_cnt; ++i) { struct srv_target *srv = &query->srv[i]; if (srv->q_a) { pj_dns_resolver_cancel_query(srv->q_a, PJ_FALSE); srv->q_a = NULL; has_pending = PJ_TRUE; } } if (has_pending && notify && query->cb) { (*query->cb)(query->token, PJ_ECANCELLED, NULL); } return has_pending? PJ_SUCCESS : PJ_EINVALIDOP; } #define SWAP(type,ptr1,ptr2) if (ptr1 != ptr2) { \ type tmp; \ pj_memcpy(&tmp, ptr1, sizeof(type)); \ pj_memcpy(ptr1, ptr2, sizeof(type)); \ (ptr1)->target_name.ptr = (ptr1)->target_buf;\ pj_memcpy(ptr2, &tmp, sizeof(type)); \ (ptr2)->target_name.ptr = (ptr2)->target_buf;\ } else {} /* Build server entries in the query_job based on received SRV response */ static void build_server_entries(pj_dns_srv_async_query *query_job, pj_dns_parsed_packet *response) { unsigned i; /* Save the Resource Records in DNS answer into SRV targets. */ query_job->srv_cnt = 0; for (i=0; ihdr.anscount && query_job->srv_cnt < PJ_DNS_SRV_MAX_ADDR; ++i) { pj_dns_parsed_rr *rr = &response->ans[i]; struct srv_target *srv = &query_job->srv[query_job->srv_cnt]; if (rr->type != PJ_DNS_TYPE_SRV) { PJ_LOG(4,(query_job->objname, "Received non SRV answer for SRV query_job!")); continue; } if (rr->rdata.srv.target.slen > PJ_MAX_HOSTNAME) { PJ_LOG(4,(query_job->objname, "Hostname is too long!")); continue; } /* Build the SRV entry for RR */ pj_bzero(srv, sizeof(*srv)); srv->target_name.ptr = srv->target_buf; pj_strncpy(&srv->target_name, &rr->rdata.srv.target, sizeof(srv->target_buf)); srv->port = rr->rdata.srv.port; srv->priority = rr->rdata.srv.prio; srv->weight = rr->rdata.srv.weight; ++query_job->srv_cnt; } if (query_job->srv_cnt == 0) { PJ_LOG(4,(query_job->objname, "Could not find SRV record in DNS answer!")); return; } /* First pass: * order the entries based on priority. */ for (i=0; isrv_cnt-1; ++i) { unsigned min = i, j; for (j=i+1; jsrv_cnt; ++j) { if (query_job->srv[j].priority < query_job->srv[min].priority) min = j; } SWAP(struct srv_target, &query_job->srv[i], &query_job->srv[min]); } /* Second pass: * Order the entry in a list. * * The algorithm for selecting server among servers with the same * priority is described in RFC 2782. */ for (i=0; isrv_cnt; ++i) { unsigned j, count=1, sum; /* Calculate running sum for servers with the same priority */ sum = query_job->srv[i].sum = query_job->srv[i].weight; for (j=i+1; jsrv_cnt && query_job->srv[j].priority == query_job->srv[i].priority; ++j) { sum += query_job->srv[j].weight; query_job->srv[j].sum = sum; ++count; } if (count > 1) { unsigned r; /* Elect one random number between zero and the total sum of * weight (inclusive). */ r = pj_rand() % (sum + 1); /* Select the first server which running sum is greater than or * equal to the random number. */ for (j=i; jsrv[j].sum >= r) break; } /* Must have selected one! */ pj_assert(j != i+count); /* Put this entry in front (of entries with same priority) */ SWAP(struct srv_target, &query_job->srv[i], &query_job->srv[j]); /* Remove all other entries (of the same priority) */ /* Don't need to do this. * See https://trac.pjsip.org/repos/ticket/1719 while (count > 1) { pj_array_erase(query_job->srv, sizeof(struct srv_target), query_job->srv_cnt, i+1); --count; --query_job->srv_cnt; } */ } } /* Since we've been moving around SRV entries, update the pointers * in target_name. */ for (i=0; isrv_cnt; ++i) { query_job->srv[i].target_name.ptr = query_job->srv[i].target_buf; } /* Check for Additional Info section if A records are available, and * fill in the IP address (so that we won't need to resolve the A * record with another DNS query_job). */ for (i=0; ihdr.arcount; ++i) { pj_dns_parsed_rr *rr = &response->arr[i]; unsigned j; if (rr->type != PJ_DNS_TYPE_A) continue; /* Yippeaiyee!! There is an "A" record! * Update the IP address of the corresponding SRV record. */ for (j=0; jsrv_cnt; ++j) { if (pj_stricmp(&rr->name, &query_job->srv[j].target_name)==0 && query_job->srv[j].addr_cnt < ADDR_MAX_COUNT) { unsigned cnt = query_job->srv[j].addr_cnt; query_job->srv[j].addr[cnt].s_addr = rr->rdata.a.ip_addr.s_addr; /* Only increment host_resolved once per SRV record */ if (query_job->srv[j].addr_cnt == 0) ++query_job->host_resolved; ++query_job->srv[j].addr_cnt; break; } } /* Not valid message; SRV entry might have been deleted in * server selection process. */ /* if (j == query_job->srv_cnt) { PJ_LOG(4,(query_job->objname, "Received DNS SRV answer with A record, but " "couldn't find matching name (name=%.*s)", (int)rr->name.slen, rr->name.ptr)); } */ } /* Rescan again the name specified in the SRV record to see if IP * address is specified as the target name (unlikely, but well, who * knows..). */ for (i=0; isrv_cnt; ++i) { pj_in_addr addr; if (query_job->srv[i].addr_cnt != 0) { /* IP address already resolved */ continue; } if (pj_inet_aton(&query_job->srv[i].target_name, &addr) != 0) { query_job->srv[i].addr[query_job->srv[i].addr_cnt++] = addr; ++query_job->host_resolved; } } /* Print resolved entries to the log */ PJ_LOG(5,(query_job->objname, "SRV query_job for %.*s completed, " "%d of %d total entries selected%c", (int)query_job->full_name.slen, query_job->full_name.ptr, query_job->srv_cnt, response->hdr.anscount, (query_job->srv_cnt ? ':' : ' '))); for (i=0; isrv_cnt; ++i) { const char *addr; if (query_job->srv[i].addr_cnt != 0) addr = pj_inet_ntoa(query_job->srv[i].addr[0]); else addr = "-"; PJ_LOG(5,(query_job->objname, " %d: SRV %d %d %d %.*s (%s)", i, query_job->srv[i].priority, query_job->srv[i].weight, query_job->srv[i].port, (int)query_job->srv[i].target_name.slen, query_job->srv[i].target_name.ptr, addr)); } } /* Start DNS A record queries for all SRV records in the query_job structure */ static pj_status_t resolve_hostnames(pj_dns_srv_async_query *query_job) { unsigned i, err_cnt = 0; pj_status_t err=PJ_SUCCESS, status; query_job->dns_state = PJ_DNS_TYPE_A; for (i=0; isrv_cnt; ++i) { struct srv_target *srv = &query_job->srv[i]; PJ_LOG(5, (query_job->objname, "Starting async DNS A query_job for %.*s", (int)srv->target_name.slen, srv->target_name.ptr)); srv->common.type = PJ_DNS_TYPE_A; srv->parent = query_job; /* See also #1809: dns_callback() will be invoked synchronously when response * is available in the cache, and var 'query_job->host_resolved' will get * incremented within the dns_callback(), which will cause this function * returning false error, so don't use that variable for counting errors. */ status = pj_dns_resolver_start_query(query_job->resolver, &srv->target_name, PJ_DNS_TYPE_A, 0, &dns_callback, srv, &srv->q_a); if (status != PJ_SUCCESS) { query_job->host_resolved++; err_cnt++; err = status; } } return (err_cnt == query_job->srv_cnt) ? err : PJ_SUCCESS; } /* * This callback is called by PJLIB-UTIL DNS resolver when asynchronous * query_job has completed (successfully or with error). */ static void dns_callback(void *user_data, pj_status_t status, pj_dns_parsed_packet *pkt) { struct common *common = (struct common*) user_data; pj_dns_srv_async_query *query_job; struct srv_target *srv = NULL; unsigned i; if (common->type == PJ_DNS_TYPE_SRV) { query_job = (pj_dns_srv_async_query*) common; srv = NULL; } else if (common->type == PJ_DNS_TYPE_A) { srv = (struct srv_target*) common; query_job = srv->parent; } else { pj_assert(!"Unexpected user data!"); return; } /* Proceed to next stage */ if (query_job->dns_state == PJ_DNS_TYPE_SRV) { /* We are getting SRV response */ query_job->q_srv = NULL; if (status == PJ_SUCCESS && pkt->hdr.anscount != 0) { /* Got SRV response, build server entry. If A records are available * in additional records section of the DNS response, save them too. */ build_server_entries(query_job, pkt); } else if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; /* Update query_job last error */ query_job->last_error = status; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(query_job->objname, "DNS SRV resolution failed for %.*s: %s", (int)query_job->full_name.slen, query_job->full_name.ptr, errmsg)); /* Trigger error when fallback is disabled */ if ((query_job->option & (PJ_DNS_SRV_FALLBACK_A | PJ_DNS_SRV_FALLBACK_AAAA)) == 0) { goto on_error; } } /* If we can't build SRV record, assume the original target is * an A record and resolve with DNS A resolution. */ if (query_job->srv_cnt == 0) { /* Looks like we aren't getting any SRV responses. * Resolve the original target as A record by creating a * single "dummy" srv record and start the hostname resolution. */ PJ_LOG(4, (query_job->objname, "DNS SRV resolution failed for %.*s, trying " "resolving A record for %.*s", (int)query_job->full_name.slen, query_job->full_name.ptr, (int)query_job->domain_part.slen, query_job->domain_part.ptr)); /* Create a "dummy" srv record using the original target */ i = query_job->srv_cnt++; pj_bzero(&query_job->srv[i], sizeof(query_job->srv[i])); query_job->srv[i].target_name = query_job->domain_part; query_job->srv[i].priority = 0; query_job->srv[i].weight = 0; query_job->srv[i].port = query_job->def_port; } /* Resolve server hostnames (DNS A record) for hosts which don't have * A record yet. */ if (query_job->host_resolved != query_job->srv_cnt) { status = resolve_hostnames(query_job); if (status != PJ_SUCCESS) goto on_error; /* Must return now. Callback may have been called and query_job * may have been destroyed. */ return; } } else if (query_job->dns_state == PJ_DNS_TYPE_A) { /* Clear the outstanding job */ srv->q_a = NULL; /* Check that we really have answer */ if (status==PJ_SUCCESS && pkt->hdr.anscount != 0) { pj_dns_a_record rec; /* Parse response */ status = pj_dns_parse_a_response(pkt, &rec); if (status != PJ_SUCCESS) goto on_error; pj_assert(rec.addr_count != 0); /* Update CNAME alias, if present. */ if (rec.alias.slen) { pj_assert(rec.alias.slen <= (int)sizeof(srv->cname_buf)); srv->cname.ptr = srv->cname_buf; pj_strcpy(&srv->cname, &rec.alias); } else { srv->cname.slen = 0; } /* Update IP address of the corresponding hostname or CNAME */ if (srv->addr_cnt < ADDR_MAX_COUNT) { srv->addr[srv->addr_cnt++].s_addr = rec.addr[0].s_addr; PJ_LOG(5,(query_job->objname, "DNS A for %.*s: %s", (int)srv->target_name.slen, srv->target_name.ptr, pj_inet_ntoa(rec.addr[0]))); } /* Check for multiple IP addresses */ for (i=1; iaddr_cnt < ADDR_MAX_COUNT; ++i) { srv->addr[srv->addr_cnt++].s_addr = rec.addr[i].s_addr; PJ_LOG(5,(query_job->objname, "Additional DNS A for %.*s: %s", (int)srv->target_name.slen, srv->target_name.ptr, pj_inet_ntoa(rec.addr[i]))); } } else if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; /* Update last error */ query_job->last_error = status; /* Log error */ pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(query_job->objname, "DNS A record resolution failed: %s", errmsg)); } ++query_job->host_resolved; } else { pj_assert(!"Unexpected state!"); query_job->last_error = status = PJ_EINVALIDOP; goto on_error; } /* Check if all hosts have been resolved */ if (query_job->host_resolved == query_job->srv_cnt) { /* Got all answers, build server addresses */ pj_dns_srv_record srv_rec; srv_rec.count = 0; for (i=0; isrv_cnt; ++i) { unsigned j; struct srv_target *srv2 = &query_job->srv[i]; srv_rec.entry[srv_rec.count].priority = srv2->priority; srv_rec.entry[srv_rec.count].weight = srv2->weight; srv_rec.entry[srv_rec.count].port = (pj_uint16_t)srv2->port ; srv_rec.entry[srv_rec.count].server.name = srv2->target_name; srv_rec.entry[srv_rec.count].server.alias = srv2->cname; srv_rec.entry[srv_rec.count].server.addr_count = 0; pj_assert(srv2->addr_cnt <= PJ_DNS_MAX_IP_IN_A_REC); for (j=0; jaddr_cnt; ++j) { srv_rec.entry[srv_rec.count].server.addr[j].s_addr = srv2->addr[j].s_addr; ++srv_rec.entry[srv_rec.count].server.addr_count; } if (srv2->addr_cnt > 0) { ++srv_rec.count; if (srv_rec.count == PJ_DNS_SRV_MAX_ADDR) break; } } PJ_LOG(5,(query_job->objname, "Server resolution complete, %d server entry(s) found", srv_rec.count)); if (srv_rec.count > 0) status = PJ_SUCCESS; else { status = query_job->last_error; if (status == PJ_SUCCESS) status = PJLIB_UTIL_EDNSNOANSWERREC; } /* Call the callback */ (*query_job->cb)(query_job->token, status, &srv_rec); } return; on_error: /* Check for failure */ if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; PJ_UNUSED_ARG(errmsg); PJ_LOG(4,(query_job->objname, "DNS %s record resolution error for '%.*s'." " Err=%d (%s)", pj_dns_get_type_name(query_job->dns_state), (int)query_job->domain_part.slen, query_job->domain_part.ptr, status, pj_strerror(status,errmsg,sizeof(errmsg)).ptr)); (*query_job->cb)(query_job->token, status, NULL); return; } } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/string.c ================================================ /* $Id: string.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include PJ_DEF(pj_str_t) pj_str_unescape( pj_pool_t *pool, const pj_str_t *src_str) { char *src = src_str->ptr; char *end = src + src_str->slen; pj_str_t dst_str; char *dst; if (pj_strchr(src_str, '%')==NULL) return *src_str; dst = dst_str.ptr = (char*) pj_pool_alloc(pool, src_str->slen); while (src != end) { if (*src == '%' && src < end-2 && pj_isxdigit(*(src+1)) && pj_isxdigit(*(src+2))) { *dst = (pj_uint8_t) ((pj_hex_digit_to_val(*(src+1)) << 4) + pj_hex_digit_to_val(*(src+2))); ++dst; src += 3; } else { *dst++ = *src++; } } dst_str.slen = dst - dst_str.ptr; return dst_str; } PJ_DEF(pj_str_t*) pj_strcpy_unescape(pj_str_t *dst_str, const pj_str_t *src_str) { const char *src = src_str->ptr; const char *end = src + src_str->slen; char *dst = dst_str->ptr; while (src != end) { if (*src == '%' && src < end-2) { *dst = (pj_uint8_t) ((pj_hex_digit_to_val(*(src+1)) << 4) + pj_hex_digit_to_val(*(src+2))); ++dst; src += 3; } else { *dst++ = *src++; } } dst_str->slen = dst - dst_str->ptr; return dst_str; } PJ_DEF(pj_ssize_t) pj_strncpy2_escape( char *dst_str, const pj_str_t *src_str, pj_ssize_t max, const pj_cis_t *unres) { const char *src = src_str->ptr; const char *src_end = src + src_str->slen; char *dst = dst_str; char *dst_end = dst + max; if (max < src_str->slen) return -1; while (src != src_end && dst != dst_end) { if (pj_cis_match(unres, *src)) { *dst++ = *src++; } else { if (dst < dst_end-2) { *dst++ = '%'; pj_val_to_hex_digit(*src, dst); dst+=2; ++src; } else { break; } } } return src==src_end ? dst-dst_str : -1; } PJ_DEF(pj_str_t*) pj_strncpy_escape(pj_str_t *dst_str, const pj_str_t *src_str, pj_ssize_t max, const pj_cis_t *unres) { dst_str->slen = pj_strncpy2_escape(dst_str->ptr, src_str, max, unres); return dst_str->slen < 0 ? NULL : dst_str; } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/stun_simple.c ================================================ /* $Id: stun_simple.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #define THIS_FILE "stun_simple.c" PJ_DEF(pj_status_t) pjstun_create_bind_req( pj_pool_t *pool, void **msg, pj_size_t *len, pj_uint32_t id_hi, pj_uint32_t id_lo) { pjstun_msg_hdr *hdr; PJ_CHECK_STACK(); hdr = PJ_POOL_ZALLOC_T(pool, pjstun_msg_hdr); if (!hdr) return PJ_ENOMEM; hdr->type = pj_htons(PJSTUN_BINDING_REQUEST); hdr->tsx[2] = pj_htonl(id_hi); hdr->tsx[3] = pj_htonl(id_lo); *msg = hdr; *len = sizeof(pjstun_msg_hdr); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjstun_parse_msg( void *buf, pj_size_t buf_len, pjstun_msg *msg) { pj_uint16_t msg_type, msg_len; char *p_attr; PJ_CHECK_STACK(); msg->hdr = (pjstun_msg_hdr*)buf; msg_type = pj_ntohs(msg->hdr->type); switch (msg_type) { case PJSTUN_BINDING_REQUEST: case PJSTUN_BINDING_RESPONSE: case PJSTUN_BINDING_ERROR_RESPONSE: case PJSTUN_SHARED_SECRET_REQUEST: case PJSTUN_SHARED_SECRET_RESPONSE: case PJSTUN_SHARED_SECRET_ERROR_RESPONSE: break; default: PJ_LOG(4,(THIS_FILE, "Error: unknown msg type %d", msg_type)); return PJLIB_UTIL_ESTUNINMSGTYPE; } msg_len = pj_ntohs(msg->hdr->length); if (msg_len != buf_len - sizeof(pjstun_msg_hdr)) { PJ_LOG(4,(THIS_FILE, "Error: invalid msg_len %d (expecting %d)", msg_len, buf_len - sizeof(pjstun_msg_hdr))); return PJLIB_UTIL_ESTUNINMSGLEN; } msg->attr_count = 0; p_attr = (char*)buf + sizeof(pjstun_msg_hdr); while (msg_len > 0) { pjstun_attr_hdr **attr = &msg->attr[msg->attr_count]; pj_uint32_t len; pj_uint16_t attr_type; *attr = (pjstun_attr_hdr*)p_attr; len = pj_ntohs((pj_uint16_t) ((*attr)->length)) + sizeof(pjstun_attr_hdr); len = (len + 3) & ~3; if (msg_len < len) { PJ_LOG(4,(THIS_FILE, "Error: length mismatch in attr %d", msg->attr_count)); return PJLIB_UTIL_ESTUNINATTRLEN; } attr_type = pj_ntohs((*attr)->type); if (attr_type > PJSTUN_ATTR_REFLECTED_FROM && attr_type != PJSTUN_ATTR_XOR_MAPPED_ADDR) { PJ_LOG(5,(THIS_FILE, "Warning: unknown attr type %x in attr %d. " "Attribute was ignored.", attr_type, msg->attr_count)); } msg_len = (pj_uint16_t)(msg_len - len); p_attr += len; ++msg->attr_count; } return PJ_SUCCESS; } PJ_DEF(void*) pjstun_msg_find_attr( pjstun_msg *msg, pjstun_attr_type t) { int i; PJ_CHECK_STACK(); for (i=0; iattr_count; ++i) { pjstun_attr_hdr *attr = msg->attr[i]; if (pj_ntohs(attr->type) == t) return attr; } return 0; } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/stun_simple_client.c ================================================ /* $Id: stun_simple_client.c 4297 2012-11-13 08:46:42Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include enum { MAX_REQUEST = 4 }; static int stun_timer[] = {500, 500, 500, 500 }; #define STUN_MAGIC 0x2112A442 #define THIS_FILE "stun_client.c" #define LOG_ADDR(addr) pj_inet_ntoa(addr.sin_addr), pj_ntohs(addr.sin_port) #define TRACE_(x) PJ_LOG(6,x) PJ_DEF(pj_status_t) pjstun_get_mapped_addr( pj_pool_factory *pf, int sock_cnt, pj_sock_t sock[], const pj_str_t *srv1, int port1, const pj_str_t *srv2, int port2, pj_sockaddr_in mapped_addr[]) { pjstun_setting opt; pj_bzero(&opt, sizeof(opt)); opt.use_stun2 = PJ_FALSE; opt.srv1 = *srv1; opt.port1 = port1; opt.srv2 = *srv2; opt.port2 = port2; return pjstun_get_mapped_addr2(pf, &opt, sock_cnt, sock, mapped_addr); } PJ_DEF(pj_status_t) pjstun_get_mapped_addr2(pj_pool_factory *pf, const pjstun_setting *opt, int sock_cnt, pj_sock_t sock[], pj_sockaddr_in mapped_addr[]) { unsigned srv_cnt; const pj_str_t *srv1, *srv2; int port1, port2; pj_sockaddr_in srv_addr[2]; int i, send_cnt = 0, nfds; pj_pool_t *pool; struct query_rec { struct { pj_uint32_t mapped_addr; pj_uint32_t mapped_port; } srv[2]; } *rec; void *out_msg; pj_size_t out_msg_len; int wait_resp = 0; pj_status_t status; PJ_CHECK_STACK(); srv1 = &opt->srv1; port1 = opt->port1; srv2 = &opt->srv1; port2 = opt->port2; TRACE_((THIS_FILE, "Entering pjstun_get_mapped_addr()")); /* Create pool. */ pool = pj_pool_create(pf, "stun%p", 400, 400, NULL); if (!pool) return PJ_ENOMEM; /* Allocate client records */ rec = (struct query_rec*) pj_pool_calloc(pool, sock_cnt, sizeof(*rec)); if (!rec) { status = PJ_ENOMEM; goto on_error; } TRACE_((THIS_FILE, " Memory allocated.")); /* Create the outgoing BIND REQUEST message template */ status = pjstun_create_bind_req( pool, &out_msg, &out_msg_len, pj_rand(), pj_rand()); if (status != PJ_SUCCESS) goto on_error; /* Insert magic cookie (specified in RFC 5389) when requested to. */ if (opt->use_stun2) { pjstun_msg_hdr *hdr = (pjstun_msg_hdr*)out_msg; hdr->tsx[0] = pj_htonl(STUN_MAGIC); } TRACE_((THIS_FILE, " Binding request created.")); /* Resolve servers. */ status = pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1); if (status != PJ_SUCCESS) goto on_error; srv_cnt = 1; if (srv2 && port2) { status = pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2); if (status != PJ_SUCCESS) goto on_error; if (srv_addr[1].sin_addr.s_addr != srv_addr[0].sin_addr.s_addr && srv_addr[1].sin_port != srv_addr[0].sin_port) { srv_cnt++; } } TRACE_((THIS_FILE, " Server initialized, using %d server(s)", srv_cnt)); /* Init mapped addresses to zero */ pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in)); /* We need these many responses */ wait_resp = sock_cnt * srv_cnt; TRACE_((THIS_FILE, " Done initialization.")); #if defined(PJ_SELECT_NEEDS_NFDS) && PJ_SELECT_NEEDS_NFDS!=0 nfds = -1; for (i=0; i nfds) { nfds = sock[i]; } } #else nfds = FD_SETSIZE-1; #endif /* Main retransmission loop. */ for (send_cnt=0; send_cnttsx[2] = pj_htonl(i); msg_hdr->tsx[3] = pj_htonl(j); /* Send! */ sent_len = out_msg_len; status = pj_sock_sendto(sock[i], out_msg, &sent_len, 0, (pj_sockaddr_t*)&srv_addr[j], sizeof(pj_sockaddr_in)); } } /* All requests sent. * The loop below will wait for responses until all responses have * been received (i.e. wait_resp==0) or timeout occurs, which then * we'll go to the next retransmission iteration. */ TRACE_((THIS_FILE, " Request(s) sent, counter=%d", send_cnt)); /* Calculate time of next retransmission. */ pj_gettickcount(&next_tx); next_tx.sec += (stun_timer[send_cnt]/1000); next_tx.msec += (stun_timer[send_cnt]%1000); pj_time_val_normalize(&next_tx); for (pj_gettickcount(&now), select_rc=1; status==PJ_SUCCESS && select_rc>=1 && wait_resp>0 && PJ_TIME_VAL_LT(now, next_tx); pj_gettickcount(&now)) { pj_time_val timeout; timeout = next_tx; PJ_TIME_VAL_SUB(timeout, now); for (i=0; itsx[2]); srv_idx = pj_ntohl(msg.hdr->tsx[3]); if (sock_idx<0 || sock_idx>=sock_cnt || sock_idx!=i || srv_idx<0 || srv_idx>=2) { status = PJLIB_UTIL_ESTUNININDEX; continue; } if (pj_ntohs(msg.hdr->type) != PJSTUN_BINDING_RESPONSE) { status = PJLIB_UTIL_ESTUNNOBINDRES; continue; } if (rec[sock_idx].srv[srv_idx].mapped_port != 0) { /* Already got response */ continue; } /* From this part, we consider the packet as a valid STUN * response for our request. */ --wait_resp; if (pjstun_msg_find_attr(&msg, PJSTUN_ATTR_ERROR_CODE) != NULL) { status = PJLIB_UTIL_ESTUNRECVERRATTR; continue; } attr = (pjstun_mapped_addr_attr*) pjstun_msg_find_attr(&msg, PJSTUN_ATTR_MAPPED_ADDR); if (!attr) { attr = (pjstun_mapped_addr_attr*) pjstun_msg_find_attr(&msg, PJSTUN_ATTR_XOR_MAPPED_ADDR); if (!attr || attr->family != 1) { status = PJLIB_UTIL_ESTUNNOMAP; continue; } } rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr; rec[sock_idx].srv[srv_idx].mapped_port = attr->port; if (pj_ntohs(attr->hdr.type) == PJSTUN_ATTR_XOR_MAPPED_ADDR) { rec[sock_idx].srv[srv_idx].mapped_addr ^= pj_htonl(STUN_MAGIC); rec[sock_idx].srv[srv_idx].mapped_port ^= pj_htons(STUN_MAGIC >> 16); } } } /* The best scenario is if all requests have been replied. * Then we don't need to go to the next retransmission iteration. */ if (wait_resp <= 0) break; } TRACE_((THIS_FILE, " All responses received, calculating result..")); for (i=0; i * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include /* * md5.h */ PJ_EXPORT_SYMBOL(md5_init) PJ_EXPORT_SYMBOL(md5_append) PJ_EXPORT_SYMBOL(md5_finish) /* * scanner.h */ PJ_EXPORT_SYMBOL(pj_cs_init) PJ_EXPORT_SYMBOL(pj_cs_set) PJ_EXPORT_SYMBOL(pj_cs_add_range) PJ_EXPORT_SYMBOL(pj_cs_add_alpha) PJ_EXPORT_SYMBOL(pj_cs_add_num) PJ_EXPORT_SYMBOL(pj_cs_add_str) PJ_EXPORT_SYMBOL(pj_cs_del_range) PJ_EXPORT_SYMBOL(pj_cs_del_str) PJ_EXPORT_SYMBOL(pj_cs_invert) PJ_EXPORT_SYMBOL(pj_scan_init) PJ_EXPORT_SYMBOL(pj_scan_fini) PJ_EXPORT_SYMBOL(pj_scan_peek) PJ_EXPORT_SYMBOL(pj_scan_peek_n) PJ_EXPORT_SYMBOL(pj_scan_peek_until) PJ_EXPORT_SYMBOL(pj_scan_get) PJ_EXPORT_SYMBOL(pj_scan_get_quote) PJ_EXPORT_SYMBOL(pj_scan_get_n) PJ_EXPORT_SYMBOL(pj_scan_get_char) PJ_EXPORT_SYMBOL(pj_scan_get_newline) PJ_EXPORT_SYMBOL(pj_scan_get_until) PJ_EXPORT_SYMBOL(pj_scan_get_until_ch) PJ_EXPORT_SYMBOL(pj_scan_get_until_chr) PJ_EXPORT_SYMBOL(pj_scan_advance_n) PJ_EXPORT_SYMBOL(pj_scan_strcmp) PJ_EXPORT_SYMBOL(pj_scan_stricmp) PJ_EXPORT_SYMBOL(pj_scan_skip_whitespace) PJ_EXPORT_SYMBOL(pj_scan_save_state) PJ_EXPORT_SYMBOL(pj_scan_restore_state) /* * stun.h */ PJ_EXPORT_SYMBOL(pj_stun_create_bind_req) PJ_EXPORT_SYMBOL(pj_stun_parse_msg) PJ_EXPORT_SYMBOL(pj_stun_msg_find_attr) PJ_EXPORT_SYMBOL(pj_stun_get_mapped_addr) PJ_EXPORT_SYMBOL(pj_stun_get_err_msg) /* * xml.h */ PJ_EXPORT_SYMBOL(pj_xml_parse) PJ_EXPORT_SYMBOL(pj_xml_print) PJ_EXPORT_SYMBOL(pj_xml_add_node) PJ_EXPORT_SYMBOL(pj_xml_add_attr) PJ_EXPORT_SYMBOL(pj_xml_find_node) PJ_EXPORT_SYMBOL(pj_xml_find_next_node) PJ_EXPORT_SYMBOL(pj_xml_find_attr) PJ_EXPORT_SYMBOL(pj_xml_find) ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/xml.c ================================================ /* $Id: xml.c 3999 2012-03-30 07:10:13Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #define EX_SYNTAX_ERROR 12 #define THIS_FILE "xml.c" static void on_syntax_error(struct pj_scanner *scanner) { PJ_UNUSED_ARG(scanner); PJ_THROW(EX_SYNTAX_ERROR); } static pj_xml_node *alloc_node( pj_pool_t *pool ) { pj_xml_node *node; node = PJ_POOL_ZALLOC_T(pool, pj_xml_node); pj_list_init( &node->attr_head ); pj_list_init( &node->node_head ); return node; } static pj_xml_attr *alloc_attr( pj_pool_t *pool ) { return PJ_POOL_ZALLOC_T(pool, pj_xml_attr); } /* This is a recursive function! */ static pj_xml_node *xml_parse_node( pj_pool_t *pool, pj_scanner *scanner) { pj_xml_node *node; pj_str_t end_name; PJ_CHECK_STACK(); if (*scanner->curptr != '<') on_syntax_error(scanner); /* Handle Processing Instructino (PI) construct (i.e. "curptr == '<' && *(scanner->curptr+1) == '?') { pj_scan_advance_n(scanner, 2, PJ_FALSE); for (;;) { pj_str_t dummy; pj_scan_get_until_ch(scanner, '?', &dummy); if (*scanner->curptr=='?' && *(scanner->curptr+1)=='>') { pj_scan_advance_n(scanner, 2, PJ_TRUE); break; } else { pj_scan_advance_n(scanner, 1, PJ_FALSE); } } return xml_parse_node(pool, scanner); } /* Handle comments construct (i.e. "', &dummy); if (pj_scan_strcmp(scanner, ">", 1) == 0) { pj_scan_advance_n(scanner, 1, PJ_TRUE); break; } else { pj_scan_advance_n(scanner, 1, PJ_FALSE); } } return xml_parse_node(pool, scanner); } /* Alloc node. */ node = alloc_node(pool); /* Get '<' */ pj_scan_get_char(scanner); /* Get node name. */ pj_scan_get_until_chr( scanner, " />\t\r\n", &node->name); /* Get attributes. */ while (*scanner->curptr != '>' && *scanner->curptr != '/') { pj_xml_attr *attr = alloc_attr(pool); pj_scan_get_until_chr( scanner, "=> \t\r\n", &attr->name); if (*scanner->curptr == '=') { pj_scan_get_char( scanner ); pj_scan_get_quotes(scanner, "\"'", "\"'", 2, &attr->value); /* remove quote characters */ ++attr->value.ptr; attr->value.slen -= 2; } pj_list_push_back( &node->attr_head, attr ); } if (*scanner->curptr == '/') { pj_scan_get_char(scanner); if (pj_scan_get_char(scanner) != '>') on_syntax_error(scanner); return node; } /* Enclosing bracket. */ if (pj_scan_get_char(scanner) != '>') on_syntax_error(scanner); /* Sub nodes. */ while (*scanner->curptr == '<' && *(scanner->curptr+1) != '/' && *(scanner->curptr+1) != '!') { pj_xml_node *sub_node = xml_parse_node(pool, scanner); pj_list_push_back( &node->node_head, sub_node ); } /* Content. */ if (!pj_scan_is_eof(scanner) && *scanner->curptr != '<') { pj_scan_get_until_ch(scanner, '<', &node->content); } /* CDATA content. */ if (*scanner->curptr == '<' && *(scanner->curptr+1) == '!' && pj_scan_strcmp(scanner, "content); while (pj_scan_strcmp(scanner, "]]>", 3)) { pj_str_t dummy; pj_scan_get_until_ch(scanner, ']', &dummy); } node->content.slen = scanner->curptr - node->content.ptr; pj_scan_advance_n(scanner, 3, PJ_TRUE); } /* Enclosing node. */ if (pj_scan_get_char(scanner) != '<' || pj_scan_get_char(scanner) != '/') on_syntax_error(scanner); pj_scan_get_until_chr(scanner, " \t>", &end_name); /* Compare name. */ if (pj_stricmp(&node->name, &end_name) != 0) on_syntax_error(scanner); /* Enclosing '>' */ if (pj_scan_get_char(scanner) != '>') on_syntax_error(scanner); return node; } PJ_DEF(pj_xml_node*) pj_xml_parse( pj_pool_t *pool, char *msg, pj_size_t len) { pj_xml_node *node = NULL; pj_scanner scanner; PJ_USE_EXCEPTION; if (!msg || !len || !pool) return NULL; pj_scan_init( &scanner, msg, len, PJ_SCAN_AUTOSKIP_WS|PJ_SCAN_AUTOSKIP_NEWLINE, &on_syntax_error); PJ_TRY { node = xml_parse_node(pool, &scanner); } PJ_CATCH_ANY { PJ_LOG(4,(THIS_FILE, "Syntax error parsing XML in line %d column %d", scanner.line, pj_scan_get_col(&scanner))); } PJ_END; pj_scan_fini( &scanner ); return node; } /* This is a recursive function. */ static int xml_print_node( const pj_xml_node *node, int indent, char *buf, pj_size_t len ) { int i; char *p = buf; pj_xml_attr *attr; pj_xml_node *sub_node; #define SIZE_LEFT() ((int)(len - (p-buf))) PJ_CHECK_STACK(); /* Print name. */ if (SIZE_LEFT() < node->name.slen + indent + 5) return -1; for (i=0; iname.ptr, node->name.slen); p += node->name.slen; /* Print attributes. */ attr = node->attr_head.next; while (attr != &node->attr_head) { if (SIZE_LEFT() < attr->name.slen + attr->value.slen + 4) return -1; *p++ = ' '; /* Attribute name. */ pj_memcpy(p, attr->name.ptr, attr->name.slen); p += attr->name.slen; /* Attribute value. */ if (attr->value.slen) { *p++ = '='; *p++ = '"'; pj_memcpy(p, attr->value.ptr, attr->value.slen); p += attr->value.slen; *p++ = '"'; } attr = attr->next; } /* Check for empty node. */ if (node->content.slen==0 && node->node_head.next==(pj_xml_node*)&node->node_head) { *p++ = ' '; *p++ = '/'; *p++ = '>'; return (int)(p-buf); } /* Enclosing '>' */ if (SIZE_LEFT() < 1) return -1; *p++ = '>'; /* Print sub nodes. */ sub_node = node->node_head.next; while (sub_node != (pj_xml_node*)&node->node_head) { int printed; if (SIZE_LEFT() < indent + 3) return -1; //*p++ = '\r'; *p++ = '\n'; printed = xml_print_node(sub_node, indent + 1, p, SIZE_LEFT()); if (printed < 0) return -1; p += printed; sub_node = sub_node->next; } /* Content. */ if (node->content.slen) { if (SIZE_LEFT() < node->content.slen) return -1; pj_memcpy(p, node->content.ptr, node->content.slen); p += node->content.slen; } /* Enclosing node. */ if (node->node_head.next != (pj_xml_node*)&node->node_head) { if (SIZE_LEFT() < node->name.slen + 5 + indent) return -1; //*p++ = '\r'; *p++ = '\n'; for (i=0; iname.slen + 3) return -1; } *p++ = '<'; *p++ = '/'; pj_memcpy(p, node->name.ptr, node->name.slen); p += node->name.slen; *p++ = '>'; #undef SIZE_LEFT return (int)(p-buf); } PJ_DEF(int) pj_xml_print(const pj_xml_node *node, char *buf, pj_size_t len, pj_bool_t include_prolog) { int prolog_len = 0; int printed; if (!node || !buf || !len) return 0; if (include_prolog) { pj_str_t prolog = {"\n", 39}; if ((int)len < prolog.slen) return -1; pj_memcpy(buf, prolog.ptr, prolog.slen); prolog_len = (int)prolog.slen; } printed = xml_print_node(node, 0, buf+prolog_len, len-prolog_len) + prolog_len; if (printed > 0 && len-printed >= 1) { buf[printed++] = '\n'; } return printed; } PJ_DEF(pj_xml_node*) pj_xml_node_new(pj_pool_t *pool, const pj_str_t *name) { pj_xml_node *node = alloc_node(pool); pj_strdup(pool, &node->name, name); return node; } PJ_DEF(pj_xml_attr*) pj_xml_attr_new( pj_pool_t *pool, const pj_str_t *name, const pj_str_t *value) { pj_xml_attr *attr = alloc_attr(pool); pj_strdup( pool, &attr->name, name); pj_strdup( pool, &attr->value, value); return attr; } PJ_DEF(void) pj_xml_add_node( pj_xml_node *parent, pj_xml_node *node ) { pj_list_push_back(&parent->node_head, node); } PJ_DEF(void) pj_xml_add_attr( pj_xml_node *node, pj_xml_attr *attr ) { pj_list_push_back(&node->attr_head, attr); } PJ_DEF(pj_xml_node*) pj_xml_find_node(const pj_xml_node *parent, const pj_str_t *name) { const pj_xml_node *node = parent->node_head.next; PJ_CHECK_STACK(); while (node != (void*)&parent->node_head) { if (pj_stricmp(&node->name, name) == 0) return (pj_xml_node*)node; node = node->next; } return NULL; } PJ_DEF(pj_xml_node*) pj_xml_find_node_rec(const pj_xml_node *parent, const pj_str_t *name) { const pj_xml_node *node = parent->node_head.next; PJ_CHECK_STACK(); while (node != (void*)&parent->node_head) { pj_xml_node *found; if (pj_stricmp(&node->name, name) == 0) return (pj_xml_node*)node; found = pj_xml_find_node_rec(node, name); if (found) return (pj_xml_node*)found; node = node->next; } return NULL; } PJ_DEF(pj_xml_node*) pj_xml_find_next_node( const pj_xml_node *parent, const pj_xml_node *node, const pj_str_t *name) { PJ_CHECK_STACK(); node = node->next; while (node != (void*)&parent->node_head) { if (pj_stricmp(&node->name, name) == 0) return (pj_xml_node*)node; node = node->next; } return NULL; } PJ_DEF(pj_xml_attr*) pj_xml_find_attr( const pj_xml_node *node, const pj_str_t *name, const pj_str_t *value) { const pj_xml_attr *attr = node->attr_head.next; while (attr != (void*)&node->attr_head) { if (pj_stricmp(&attr->name, name)==0) { if (value) { if (pj_stricmp(&attr->value, value)==0) return (pj_xml_attr*)attr; } else { return (pj_xml_attr*)attr; } } attr = attr->next; } return NULL; } PJ_DEF(pj_xml_node*) pj_xml_find( const pj_xml_node *parent, const pj_str_t *name, const void *data, pj_bool_t (*match)(const pj_xml_node *, const void*)) { const pj_xml_node *node = (const pj_xml_node *)parent->node_head.next; if (!name && !match) return NULL; while (node != (const pj_xml_node*) &parent->node_head) { if (name) { if (pj_stricmp(&node->name, name)!=0) { node = node->next; continue; } } if (match) { if (match(node, data)) return (pj_xml_node*)node; } else { return (pj_xml_node*)node; } node = node->next; } return NULL; } PJ_DEF(pj_xml_node*) pj_xml_find_rec( const pj_xml_node *parent, const pj_str_t *name, const void *data, pj_bool_t (*match)(const pj_xml_node*, const void*)) { const pj_xml_node *node = (const pj_xml_node *)parent->node_head.next; if (!name && !match) return NULL; while (node != (const pj_xml_node*) &parent->node_head) { pj_xml_node *found; if (name) { if (pj_stricmp(&node->name, name)==0) { if (match) { if (match(node, data)) return (pj_xml_node*)node; } else { return (pj_xml_node*)node; } } } else if (match) { if (match(node, data)) return (pj_xml_node*)node; } found = pj_xml_find_rec(node, name, data, match); if (found) return found; node = node->next; } return NULL; } PJ_DEF(pj_xml_node*) pj_xml_clone( pj_pool_t *pool, const pj_xml_node *rhs) { pj_xml_node *node; const pj_xml_attr *r_attr; const pj_xml_node *child; node = alloc_node(pool); pj_strdup(pool, &node->name, &rhs->name); pj_strdup(pool, &node->content, &rhs->content); /* Clone all attributes */ r_attr = rhs->attr_head.next; while (r_attr != &rhs->attr_head) { pj_xml_attr *attr; attr = alloc_attr(pool); pj_strdup(pool, &attr->name, &r_attr->name); pj_strdup(pool, &attr->value, &r_attr->value); pj_list_push_back(&node->attr_head, attr); r_attr = r_attr->next; } /* Clone all child nodes. */ child = rhs->node_head.next; while (child != (pj_xml_node*) &rhs->node_head) { pj_xml_node *new_child; new_child = pj_xml_clone(pool, child); pj_list_push_back(&node->node_head, new_child); child = child->next; } return node; } ================================================ FILE: deps/pjsip/pjlib-util/src/pjlib-util/xml_wrap.cpp ================================================ /* $Id: xml_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This file is a C++ wrapper, see ticket #886 for details. */ #include "xml.c" ================================================ FILE: deps/pjsip/pjmedia/README.txt ================================================ [Last Update: 2006/03/04] This directory contains two static libraries: - pjmedia The multimedia framework. - pjmedia-codec Codec collections. pjmedia has G711 codecs (Alaw and ULaw). pjmedia-codec has: - GSM-FR implementation Copyright 1992 by Jutta Degener and Carsten Bormann, Technische Universitaet Berlin - Speex 1.1.12 http://www.speex.org ================================================ FILE: deps/pjsip/pjmedia/build/Makefile ================================================ include ../../build.mak include ../../version.mak THIRD_PARTY:=$(PJDIR)/third_party SRTP_INC=$(CC_INC)$(THIRD_PARTY)/build/srtp \ $(CC_INC)$(THIRD_PARTY)/srtp/crypto/include \ $(CC_INC)$(THIRD_PARTY)/srtp/include include $(PJDIR)/build/common.mak RULES_MAK := $(PJDIR)/build/rules.mak PJLIB_LIB:=$(PJDIR)/pjlib/lib/libpj-$(TARGET_NAME)$(LIBEXT) PJLIB_UTIL_LIB:=$(PJDIR)/pjlib-util/lib/libpjlib-util-$(TARGET_NAME)$(LIBEXT) PJNATH_LIB:=$(PJDIR)/pjnath/lib/libpjnath-$(TARGET_NAME)$(LIBEXT) export PJMEDIA_LIB:=../lib/libpjmedia-$(TARGET_NAME)$(LIBEXT) export PJMEDIA_CODEC_LIB:=../lib/libpjmedia-codec-$(TARGET_NAME)$(LIBEXT) export PJSDP_LIB:=../lib/libpjsdp-$(TARGET_NAME)$(LIBEXT) export PJMEDIA_AUDIODEV_LIB:=../lib/libpjmedia-audiodev-$(TARGET_NAME)$(LIBEXT) export PJMEDIA_VIDEODEV_LIB:=../lib/libpjmedia-videodev-$(TARGET_NAME)$(LIBEXT) ############################################################################### # Gather all flags. # export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ $(CFLAGS) $(CC_INC)../include \ $(CC_INC)../../pjlib/include \ $(CC_INC)../../pjlib-util/include \ $(CC_INC)../../pjmedia/include \ $(CC_INC)../../pjnath/include \ $(CC_INC)../.. \ $(SRTP_INC) export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ $(HOST_CXXFLAGS) $(CXXFLAGS) export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJMEDIA_VIDEODEV_LIB)) \ $(subst /,$(HOST_PSEP),$(PJMEDIA_CODEC_LIB)) \ $(subst /,$(HOST_PSEP),$(PJMEDIA_LIB)) \ $(subst /,$(HOST_PSEP),$(PJMEDIA_AUDIODEV_LIB)) \ $(subst /,$(HOST_PSEP),$(PJLIB_LIB)) \ $(subst /,$(HOST_PSEP),$(PJLIB_UTIL_LIB)) \ $(subst /,$(HOST_PSEP),$(PJNATH_LIB)) \ -L$(PJDIR)/third_party/lib \ $(APP_THIRD_PARTY_LIBS) \ $(APP_THIRD_PARTY_EXT) \ $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ $(LDFLAGS) ############################################################################### # Defines for building PJMEDIA library # export PJMEDIA_SRCDIR = ../src/pjmedia export PJMEDIA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ alaw_ulaw.o alaw_ulaw_table.o avi_player.o \ bidirectional.o clock_thread.o codec.o conference.o \ conf_switch.o converter.o converter_libswscale.o converter_libyuv.o \ delaybuf.o echo_common.o \ echo_port.o echo_suppress.o endpoint.o errno.o \ event.o format.o ffmpeg_util.o \ g711.o jbuf.o master_port.o mem_capture.o mem_player.o \ mixer_port.o null_port.o plc_common.o port.o splitcomb.o \ resample_resample.o resample_libsamplerate.o \ resample_port.o rtcp.o rtcp_xr.o rtp.o \ sdp.o sdp_cmp.o sdp_neg.o session.o silencedet.o \ sound_legacy.o sound_port.o stereo_port.o stream_common.o \ stream.o stream_info.o tonegen.o transport_adapter_sample.o \ transport_ice.o transport_loop.o transport_srtp.o transport_zrtp.o transport_udp.o \ types.o vid_codec.o vid_codec_util.o \ vid_port.o vid_stream.o vid_stream_info.o vid_tee.o \ wav_player.o wav_playlist.o wav_writer.o wave.o \ wsola.o export PJMEDIA_CFLAGS += $(_CFLAGS) ############################################################################### # Defines for building PJMEDIA-AUDIODEV library # export PJMEDIA_AUDIODEV_SRCDIR = ../src/pjmedia-audiodev export PJMEDIA_AUDIODEV_OBJS += audiodev.o errno.o null_dev.o wmme_dev.o alsa_dev.o export PJMEDIA_AUDIODEV_CFLAGS += $(_CFLAGS) ############################################################################### # Defines for building PJMEDIA-VIDEODEV library # export PJMEDIA_VIDEODEV_SRCDIR = ../src/pjmedia-videodev export PJMEDIA_VIDEODEV_OBJS += errno.o videodev.o avi_dev.o colorbar_dev.o v4l2_dev.o fb_dev.o null_dev.o util.o export PJMEDIA_VIDEODEV_CFLAGS += $(_CFLAGS) export PJMEDIA_VIDEODEV_CXXFLAGS += $(_CXXFLAGS) ############################################################################### # Defines for building PJSDP library # Note that SDP functionality is already INCLUDED in PJMEDIA. # The PJSDP library should only be used for applications that want SDP # but don't want to use the rest of the media framework. # export PJSDP_SRCDIR = ../src/pjmedia export PJSDP_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ errno.o sdp.o sdp_cmp.o sdp_neg.o export PJSDP_CFLAGS += $(_CFLAGS) ############################################################################### # Defines for building PJMEDIA-Codec library # export PJMEDIA_CODEC_SRCDIR = ../src/pjmedia-codec export PJMEDIA_CODEC_OBJS += audio_codecs.o ffmpeg_vid_codecs.o openh264.o \ h263_packetizer.o h264_packetizer.o vpx.o \ $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ $(CODEC_OBJS) \ g7221_sdp_match.o export PJMEDIA_CODEC_CFLAGS += $(_CFLAGS) $(GSM_CFLAGS) $(SPEEX_CFLAGS) \ $(ILBC_CFLAGS) $(IPP_CFLAGS) $(G7221_CFLAGS) export PJMEDIA_CODEC_CXXFLAGS += $(_CXXFLAGS) $(GSM_CFLAGS) $(SPEEX_CFLAGS) \ $(ILBC_CFLAGS) $(IPP_CFLAGS) $(G7221_CFLAGS) ############################################################################### # Defines for building test application # export PJMEDIA_TEST_SRCDIR = ../src/test export PJMEDIA_TEST_OBJS += codec_vectors.o jbuf_test.o main.o mips_test.o \ vid_codec_test.o vid_dev_test.o vid_port_test.o \ rtp_test.o test.o export PJMEDIA_TEST_OBJS += sdp_neg_test.o export PJMEDIA_TEST_CFLAGS += $(_CFLAGS) export PJMEDIA_TEST_LDFLAGS += $(_LDFLAGS) export PJMEDIA_TEST_EXE:=../bin/pjmedia-test-$(TARGET_NAME)$(HOST_EXE) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Main entry # # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. # TARGETS := pjmedia pjmedia-videodev pjmedia-audiodev pjmedia-codec pjsdp all: $(TARGETS) doc: cd .. && rm -rf docs/$(PJ_VERSION) && doxygen docs/doxygen.cfg @if [ -n "$(WWWDIR)" ] && ! [ -d "$(WWWDIR)/docs/$(PJ_VERSION)/pjmedia/docs/html" ] ; then \ echo "Creating docs/$(PJ_VERSION)/pjmedia/docs/html" ; \ mkdir -p $(WWWDIR)/docs/$(PJ_VERSION)/pjmedia/docs/html ; \ fi @if [ -n "$(WWWDIR)" ] && [ -d "$(WWWDIR)/docs/$(PJ_VERSION)/pjmedia/docs/html" ] ; then \ echo "Copying docs/$(PJ_VERSION) to $(WWWDIR)/docs/$(PJ_VERSION)/pjmedia/docs/html.." ; \ cp -v -a ../docs/$(PJ_VERSION)/html/* $(WWWDIR)/docs/$(PJ_VERSION)/pjmedia/docs/html/ ; \ fi dep: depend distclean: realclean .PHONY: dep depend pjmedia pjmedia-codec pjmedia-videodev pjmedia-audiodev pjmedia-test clean realclean distclean pjmedia: $(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $(PJMEDIA_LIB) pjmedia-codec: $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $(PJMEDIA_CODEC_LIB) pjmedia-videodev: $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_VIDEODEV app=pjmedia-videodev $(PJMEDIA_VIDEODEV_LIB) pjmedia-audiodev: $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_AUDIODEV app=pjmedia-audiodev $(PJMEDIA_AUDIODEV_LIB) pjsdp: $(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $(PJSDP_LIB) $(PJMEDIA_LIB): pjmedia pjmedia-test: $(PJMEDIA_LIB) pjmedia $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $(PJMEDIA_TEST_EXE) .PHONY: ../lib/pjmedia.ko ../lib/pjmedia.ko: echo Making $@ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $@ .PHONY: ../lib/pjmedia-codec.ko ../lib/pjmedia-codec.ko: echo Making $@ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $@ .PHONY: ../lib/pjmedia-test.ko ../lib/pjmedia-test.ko: $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $@ clean: $(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $@ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $@ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_VIDEODEV app=pjmedia-videodev $@ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_AUDIODEV app=pjmedia-audiodev $@ $(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $@ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $@ realclean: $(subst @@,$(subst /,$(HOST_PSEP),.pjmedia-$(TARGET_NAME).depend),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),.pjmedia-videodev-$(TARGET_NAME).depend),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),.pjmedia-audiodev-$(TARGET_NAME).depend),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),.pjmedia-codec-$(TARGET_NAME).depend),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),.pjmedia-test-$(TARGET_NAME).depend),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),.pjsdp-$(TARGET_NAME).depend),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $@ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_VIDEODEV app=pjmedia-videodev $@ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_AUDIODEV app=pjmedia-audiodev $@ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $@ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $@ $(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $@ depend: $(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $@ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_VIDEODEV app=pjmedia-videodev $@ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_AUDIODEV app=pjmedia-audiodev $@ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $@ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $@ $(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $@ echo '$(PJMEDIA_TEST_EXE): $(PJMEDIA_LIB) $(PJMEDIA_CODEC_LIB) $(PJNATH_LIB) $(PJLIB_UTIL_LIB) $(PJLIB_LIB)' >> .pjmedia-test-$(TARGET_NAME).depend ================================================ FILE: deps/pjsip/pjmedia/build/os-auto.mak.in ================================================ # @configure_input@ # Define the desired video device backend # Valid values are: # - mac_os # - iphone_os # - android_os AC_PJMEDIA_VIDEO = @ac_pjmedia_video@ # FFMPEG dlags FFMPEG_CFLAGS = @ac_ffmpeg_cflags@ FFMPEG_LDFLAGS = @ac_ffmpeg_ldflags@ # VPX flags VPX_CFLAGS = @ac_vpx_cflags@ VPX_LDFLAGS = @ac_vpx_ldflags@ # Video4Linux2 V4L2_CFLAGS = @ac_v4l2_cflags@ V4L2_LDFLAGS = @ac_v4l2_ldflags@ # AVF AC_PJMEDIA_VIDEO_HAS_AVF = @ac_pjmedia_video_has_avf@ AVF_CFLAGS = @ac_avf_cflags@ # iOS IOS_CFLAGS = @ac_ios_cflags@ # Dshow AC_PJMEDIA_VIDEO_HAS_DSHOW = @ac_pjmedia_video_has_dshow@ DSHOW_CFLAGS = @ac_dshow_cflags@ DSHOW_LDFLAGS = @ac_dshow_ldflags@ # libyuv LIBYUV_CFLAGS = @ac_libyuv_cflags@ LIBYUV_LDFLAGS = @ac_libyuv_ldflags@ # openh264 OPENH264_CFLAGS = @ac_openh264_cflags@ OPENH264_LDFLAGS = @ac_openh264_ldflags@ # PJMEDIA features exclusion export CFLAGS += @ac_no_small_filter@ @ac_no_large_filter@ @ac_no_speex_aec@ \ $(FFMPEG_CFLAGS) $(V4L2_CFLAGS) $(AVF_CFLAGS) \ $(IOS_CFLAGS) $(DSHOW_CFLAGS) $(LIBYUV_CFLAGS) $(OPENH264_CFLAGS) \ $(VPX_CFLAGS) export LDFLAGS += $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS) $(DSHOW_LDFLAGS) \ $(LIBYUV_LDFLAGS) $(OPENH264_LDFLAGS) $(VPX_LDFLAGS) # # Codecs # AC_NO_G7221_CODEC=@ac_no_g7221_codec@ export CODEC_OBJS= export PJMEDIA_AUDIODEV_OBJS += @ac_pjmedia_audiodev_objs@ export CODEC_OBJS += gsm.o export CFLAGS += -I$(THIRD_PARTY)/build/speex -I$(THIRD_PARTY)/speex/include export CODEC_OBJS += speex_codec.o ifneq (@ac_no_speex_aec@,1) export PJMEDIA_OBJS += echo_speex.o endif export CFLAGS += -I$(THIRD_PARTY)/webrtc/src export PJMEDIA_OBJS += echo_webrtc_aec.o export CODEC_OBJS += ilbc.o export CODEC_OBJS += g722.o g722/g722_enc.o g722/g722_dec.o ifeq ($(AC_NO_G7221_CODEC),1) export CFLAGS += -DPJMEDIA_HAS_G7221_CODEC=0 else export CODEC_OBJS += g7221.o export G7221_CFLAGS += -I$(THIRD_PARTY) endif export CODEC_OBJS += opus.o # # Dshow video device # ifeq ($(AC_PJMEDIA_VIDEO_HAS_DSHOW),yes) export PJMEDIA_VIDEODEV_OBJS += dshow_dev.o dshow_filter.o endif # # AVF video device # ifeq ($(AC_PJMEDIA_VIDEO_HAS_AVF),yes) export PJMEDIA_VIDEODEV_OBJS += avf_dev.o endif ================================================ FILE: deps/pjsip/pjmedia/docs/doxygen.cfg ================================================ # Doxyfile 1.3-rc3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # General configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "PJMEDIA Reference" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = $(PJ_VERSION) # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = docs/$(PJ_VERSION) # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, # Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en # (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, # Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian. OUTPUT_LANGUAGE = English # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these class will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = NO # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited # members of a class in the documentation of that class as if those members were # ordinary class members. Constructors, destructors and assignment operators of # the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. It is allowed to use relative paths in the argument list. STRIP_FROM_PATH = "/c/project/pjproject/pjmedia" # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower case letters. If set to YES upper case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # users are adviced to set this option to NO. CASE_SENSE_NAMES = YES # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explict @brief command for a brief description. JAVADOC_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = YES # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # reimplements. INHERIT_DOCS = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consist of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. # For instance some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources # only. Doxygen will then generate output that is more tailored for Java. # For instance namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES TYPEDEF_HIDES_STRUCT = YES #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = NO # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = include ../pjsip-apps/src/samples # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp # *.h++ *.idl *.odl FILE_PATTERNS = *.h *.c # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = *_i.h # The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories # that are symbolic links (a Unix filesystem feature) are excluded from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. EXCLUDE_PATTERNS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = ../pjsip-apps/src/samples # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = docs # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. INPUT_FILTER = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .htm # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = docs/header.html # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = docs/footer.html # The HTML_STYLESHEET tag can be used to specify a user defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet HTML_STYLESHEET = ../pjlib/docs/doxygen.css # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output dir. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non empty doxygen will try to run # the html help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the Html help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 1 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimised for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assigments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_XML = NO # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_PREDEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. PREDEFINED = PJ_DECL(x)=x PJ_DEF(x)=x PJ_IDECL(x)=x \ PJ_IDEF(x)=x PJ_INLINE(x)=x \ PJ_DECL_DATA(x)=x \ PJ_DECL_NO_RETURN(x)=x \ PJ_NO_RETURN=x \ PJ_HAS_HIGH_RES_TIMER=1 \ PJ_LOG_MAX_LEVEL=4 \ PJ_HAS_SEMAPHORE=1 \ PJ_HAS_EVENT_OBJ=1 \ PJ_HAS_TCP=1 \ PJMEDIA_HAS_SRTP=1 \ PJMEDIA_STREAM_ENABLE_KA=1 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse the # parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::addtions related to external references #--------------------------------------------------------------------------- # The TAGFILES tag can be used to specify one or more tagfiles. TAGFILES = ../pjlib/docs/pjlib.tag=../../../pjlib/docs/html ../pjlib-util/docs/pjlib-util.tag=../../../pjlib-util/docs/html ../pjnath/docs/pjnath.tag=../../../pjnath/docs/html ../pjsip/docs/pjmedia.tag=../../../pjmedia/docs/html # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = docs/pjmedia.tag # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = NO # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). #PERL_PATH = /usr/bin/perl PERL_PATH = /c/Perl/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or # super classes. Setting the tag to NO turns the diagrams off. Note that this # option is superceded by the HAVE_DOT option below. This is only a fallback. It is # recommended to install and use dot, since it yield more powerful graphs. CLASS_DIAGRAMS = NO # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found on the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermedate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::addtions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO ================================================ FILE: deps/pjsip/pjmedia/docs/footer.html ================================================

 


PJMEDIA small footprint Open Source media stack
Copyright (C) 2006-2008 Teluu Inc.
================================================ FILE: deps/pjsip/pjmedia/docs/header.html ================================================ $title ($projectnumber)

Home --> Documentations --> PJMEDIA Reference

================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/alaw_ulaw.h ================================================ /* $Id: alaw_ulaw.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_ALAW_ULAW_H__ #define __PJMEDIA_ALAW_ULAW_H__ #include PJ_BEGIN_DECL #if defined(PJMEDIA_HAS_ALAW_ULAW_TABLE) && PJMEDIA_HAS_ALAW_ULAW_TABLE!=0 extern const pj_uint8_t pjmedia_linear2ulaw_tab[16384]; extern const pj_uint8_t pjmedia_linear2alaw_tab[16384]; extern const pj_int16_t pjmedia_ulaw2linear_tab[256]; extern const pj_int16_t pjmedia_alaw2linear_tab[256]; /** * Convert 16-bit linear PCM value to 8-bit A-Law. * * @param pcm_val 16-bit linear PCM value. * @return 8-bit A-Law value. */ #define pjmedia_linear2alaw(pcm_val) \ pjmedia_linear2alaw_tab[(((pj_int16_t)pcm_val) >> 2) & 0x3fff] /** * Convert 8-bit A-Law value to 16-bit linear PCM value. * * @param chara_val 8-bit A-Law value. * @return 16-bit linear PCM value. */ #define pjmedia_alaw2linear(chara_val) \ pjmedia_alaw2linear_tab[chara_val] /** * Convert 16-bit linear PCM value to 8-bit U-Law. * * @param pcm_val 16-bit linear PCM value. * @return U-bit A-Law value. */ #define pjmedia_linear2ulaw(pcm_val) \ pjmedia_linear2ulaw_tab[(((pj_int16_t)pcm_val) >> 2) & 0x3fff] /** * Convert 8-bit U-Law value to 16-bit linear PCM value. * * @param u_val 8-bit U-Law value. * @return 16-bit linear PCM value. */ #define pjmedia_ulaw2linear(u_val) \ pjmedia_ulaw2linear_tab[u_val] /** * Convert 8-bit A-Law value to 8-bit U-Law value. * * @param aval 8-bit A-Law value. * @return 8-bit U-Law value. */ #define pjmedia_alaw2ulaw(aval) \ pjmedia_linear2ulaw(pjmedia_alaw2linear(aval)) /** * Convert 8-bit U-Law value to 8-bit A-Law value. * * @param uval 8-bit U-Law value. * @return 8-bit A-Law value. */ #define pjmedia_ulaw2alaw(uval) \ pjmedia_linear2alaw(pjmedia_ulaw2linear(uval)) #else /** * Convert 16-bit linear PCM value to 8-bit A-Law. * * @param pcm_val 16-bit linear PCM value. * @return 8-bit A-Law value. */ PJ_DECL(pj_uint8_t) pjmedia_linear2alaw(int pcm_val); /** * Convert 8-bit A-Law value to 16-bit linear PCM value. * * @param chara_val 8-bit A-Law value. * @return 16-bit linear PCM value. */ PJ_DECL(int) pjmedia_alaw2linear(unsigned chara_val); /** * Convert 16-bit linear PCM value to 8-bit U-Law. * * @param pcm_val 16-bit linear PCM value. * @return U-bit A-Law value. */ PJ_DECL(unsigned char) pjmedia_linear2ulaw(int pcm_val); /** * Convert 8-bit U-Law value to 16-bit linear PCM value. * * @param u_val 8-bit U-Law value. * @return 16-bit linear PCM value. */ PJ_DECL(int) pjmedia_ulaw2linear(unsigned char u_val); /** * Convert 8-bit A-Law value to 8-bit U-Law value. * * @param aval 8-bit A-Law value. * @return 8-bit U-Law value. */ PJ_DECL(unsigned char) pjmedia_alaw2ulaw(unsigned char aval); /** * Convert 8-bit U-Law value to 8-bit A-Law value. * * @param uval 8-bit U-Law value. * @return 8-bit A-Law value. */ PJ_DECL(unsigned char) pjmedia_ulaw2alaw(unsigned char uval); #endif /** * Encode 16-bit linear PCM data to 8-bit U-Law data. * * @param dst Destination buffer for 8-bit U-Law data. * @param src Source, 16-bit linear PCM data. * @param count Number of samples. */ PJ_INLINE(void) pjmedia_ulaw_encode(pj_uint8_t *dst, const pj_int16_t *src, pj_size_t count) { const pj_int16_t *end = src + count; while (src < end) { *dst++ = pjmedia_linear2ulaw(*src++); } } /** * Encode 16-bit linear PCM data to 8-bit A-Law data. * * @param dst Destination buffer for 8-bit A-Law data. * @param src Source, 16-bit linear PCM data. * @param count Number of samples. */ PJ_INLINE(void) pjmedia_alaw_encode(pj_uint8_t *dst, const pj_int16_t *src, pj_size_t count) { const pj_int16_t *end = src + count; while (src < end) { *dst++ = pjmedia_linear2alaw(*src++); } } /** * Decode 8-bit U-Law data to 16-bit linear PCM data. * * @param dst Destination buffer for 16-bit PCM data. * @param src Source, 8-bit U-Law data. * @param len Encoded frame/source length in bytes. */ PJ_INLINE(void) pjmedia_ulaw_decode(pj_int16_t *dst, const pj_uint8_t *src, pj_size_t len) { const pj_uint8_t *end = src + len; while (src < end) { *dst++ = pjmedia_ulaw2linear(*src++); } } /** * Decode 8-bit A-Law data to 16-bit linear PCM data. * * @param dst Destination buffer for 16-bit PCM data. * @param src Source, 8-bit A-Law data. * @param len Encoded frame/source length in bytes. */ PJ_INLINE(void) pjmedia_alaw_decode(pj_int16_t *dst, const pj_uint8_t *src, pj_size_t len) { const pj_uint8_t *end = src + len; while (src < end) { *dst++ = pjmedia_alaw2linear(*src++); } } PJ_END_DECL #endif /* __PJMEDIA_ALAW_ULAW_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/avi.h ================================================ /* $Id: avi.h 4058 2012-04-17 06:57:50Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_AVI_H__ #define __PJMEDIA_AVI_H__ /** * @file avi.h * @brief AVI file manipulation. */ /** * @defgroup PJMEDIA_FILE_FORMAT File Formats * @brief Supported file formats */ /** * @defgroup PJMEDIA_AVI AVI Header * @ingroup PJMEDIA_FILE_FORMAT * @brief Representation of RIFF/AVI file format * @{ * * This the the low level representation of RIFF/AVI file format. For * higher abstraction, please see \ref PJMEDIA_FILE_PLAY and * \ref PJMEDIA_FILE_REC. */ PJ_BEGIN_DECL #define PJMEDIA_AVI_MAX_NUM_STREAMS 4 static const char avi_tags[][4] = { { 'R', 'I', 'F', 'F' }, { 'A', 'V', 'I', ' ' }, { 'h', 'd', 'r', 'l' }, { 'a', 'v', 'i', 'h' }, { 's', 't', 'r', 'l' }, { 's', 't', 'r', 'h' }, { 'a', 'u', 'd', 's' }, { 'v', 'i', 'd', 's' }, { 's', 't', 'r', 'f' }, { 'm', 'o', 'v', 'i' }, { 'L', 'I', 'S', 'T' }, { 'J', 'U', 'N', 'K' }, }; typedef enum { PJMEDIA_AVI_RIFF_TAG = 0, PJMEDIA_AVI_AVI_TAG, PJMEDIA_AVI_HDRL_TAG, PJMEDIA_AVI_AVIH_TAG, PJMEDIA_AVI_STRL_TAG, PJMEDIA_AVI_STRH_TAG, PJMEDIA_AVI_AUDS_TAG, PJMEDIA_AVI_VIDS_TAG, PJMEDIA_AVI_STRF_TAG, PJMEDIA_AVI_MOVI_TAG, PJMEDIA_AVI_LIST_TAG, PJMEDIA_AVI_JUNK_TAG, } pjmedia_avi_tag; /** * These types describe the simpler/canonical version of an AVI file. * They do not support the full AVI RIFF format specification. */ #pragma pack(2) /** This structure describes RIFF AVI file header */ typedef struct riff_hdr_t { pj_uint32_t riff; /**< "RIFF" ASCII tag. */ pj_uint32_t file_len; /**< File length minus 8 bytes */ pj_uint32_t avi; /**< "AVI" ASCII tag. */ } riff_hdr_t; /** This structure describes avih header */ typedef struct avih_hdr_t { pj_uint32_t list_tag; pj_uint32_t list_sz; pj_uint32_t hdrl_tag; pj_uint32_t avih; pj_uint32_t size; pj_uint32_t usec_per_frame; /**< microsecs between frames */ pj_uint32_t max_Bps; pj_uint32_t pad; pj_uint32_t flags; pj_uint32_t tot_frames; pj_uint32_t init_frames; pj_uint32_t num_streams; pj_uint32_t buf_size; pj_uint32_t width; pj_uint32_t height; pj_uint32_t reserved[4]; } avih_hdr_t; /** This structure describes strl header */ typedef struct strl_hdr_t { pj_uint32_t list_tag; pj_uint32_t list_sz; pj_uint32_t strl_tag; pj_uint32_t strh; pj_uint32_t strh_size; pj_uint32_t data_type; pj_uint32_t codec; pj_uint32_t flags; pj_uint32_t bogus_priority_language; /**< Do not access this data */ pj_uint32_t init_frames; pj_uint32_t scale; pj_uint32_t rate; pj_uint32_t start; pj_uint32_t length; pj_uint32_t buf_size; pj_uint32_t quality; pj_uint32_t sample_size; pj_uint32_t bogus_frame[2]; /**< Do not access this data */ } strl_hdr_t; typedef struct { pj_uint32_t strf; pj_uint32_t strf_size; pj_uint16_t fmt_tag; /**< 1 for PCM */ pj_uint16_t nchannels; /**< Number of channels. */ pj_uint32_t sample_rate; /**< Sampling rate. */ pj_uint32_t bytes_per_sec; /**< Average bytes per second. */ pj_uint16_t block_align; /**< nchannels * bits / 8 */ pj_uint16_t bits_per_sample; /**< Bits per sample. */ pj_uint16_t extra_size; } strf_audio_hdr_t; /** * Sizes of strf_audio_hdr_t struct, started by the size (in bytes) of * 32-bits struct members, alternated with the size of 16-bits members. */ static const pj_uint8_t strf_audio_hdr_sizes [] = {8, 4, 8, 6}; typedef struct { pj_uint32_t strf; pj_uint32_t strf_size; pj_uint32_t biSize; pj_int32_t biWidth; pj_int32_t biHeight; pj_uint16_t biPlanes; pj_uint16_t biBitCount; pj_uint32_t biCompression; pj_uint32_t biSizeImage; pj_int32_t biXPelsPerMeter; pj_int32_t biYPelsPerMeter; pj_uint32_t biClrUsed; pj_uint32_t biClrImportant; } strf_video_hdr_t; static const pj_uint8_t strf_video_hdr_sizes [] = {20, 4, 24}; struct pjmedia_avi_hdr { riff_hdr_t riff_hdr; avih_hdr_t avih_hdr; strl_hdr_t strl_hdr[PJMEDIA_AVI_MAX_NUM_STREAMS]; union { strf_audio_hdr_t strf_audio_hdr; strf_video_hdr_t strf_video_hdr; } strf_hdr[PJMEDIA_AVI_MAX_NUM_STREAMS]; }; #pragma pack() /** * @see pjmedia_avi_hdr */ typedef struct pjmedia_avi_hdr pjmedia_avi_hdr; /** * This structure describes generic RIFF subchunk header. */ typedef struct pjmedia_avi_subchunk { pj_uint32_t id; /**< Subchunk ASCII tag. */ pj_uint32_t len; /**< Length following this field */ } pjmedia_avi_subchunk; PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_AVI_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/avi_stream.h ================================================ /* $Id: avi_stream.h 3715 2011-08-19 09:35:25Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_AVI_STREAM_H__ #define __PJMEDIA_AVI_STREAM_H__ /** * @file avi_stream.h * @brief AVI file player. */ #include PJ_BEGIN_DECL /** * @defgroup PJMEDIA_FILE_PLAY AVI File Player * @ingroup PJMEDIA_PORT * @brief Video and audio playback from AVI file * @{ */ /** * AVI file player options. */ enum pjmedia_avi_file_player_option { /** * Tell the file player to return NULL frame when the whole * file has been played. */ PJMEDIA_AVI_FILE_NO_LOOP = 1 }; /** * AVI stream data type. */ typedef pjmedia_port pjmedia_avi_stream; /** * Opaque data type for AVI streams. AVI streams is a collection of * zero or more AVI stream. */ typedef struct pjmedia_avi_streams pjmedia_avi_streams; /** * Create avi streams to play an AVI file. AVI player supports * reading AVI file with uncompressed video format and * 16 bit PCM or compressed G.711 A-law/U-law audio format. * * @param pool Pool to create the streams. * @param filename File name to open. * @param flags Avi streams creation flags. * @param p_streams Pointer to receive the avi streams instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_avi_player_create_streams(pj_pool_t *pool, const char *filename, unsigned flags, pjmedia_avi_streams **p_streams); /** * Get the number of AVI stream. * * @param streams The AVI streams. * * @return The number of AVI stream. */ PJ_DECL(unsigned) pjmedia_avi_streams_get_num_streams(pjmedia_avi_streams *streams); /** * Return the idx-th stream of the AVI streams. * * @param streams The AVI streams. * @param idx The stream index. * * @return The AVI stream or NULL if it does not exist. */ PJ_DECL(pjmedia_avi_stream *) pjmedia_avi_streams_get_stream(pjmedia_avi_streams *streams, unsigned idx); /** * Return an AVI stream with a certain media type from the AVI streams. * * @param streams The AVI streams. * @param start_idx The starting index. * @param media_type The media type of the stream. * * @return The AVI stream or NULL if it does not exist. */ PJ_DECL(pjmedia_avi_stream *) pjmedia_avi_streams_get_stream_by_media(pjmedia_avi_streams *streams, unsigned start_idx, pjmedia_type media_type); /** * Return the media port of an AVI stream. * * @param stream The AVI stream. * * @return The media port. */ PJ_INLINE(pjmedia_port *) pjmedia_avi_stream_get_port(pjmedia_avi_stream *stream) { return (pjmedia_port *)stream; } /** * Get the data length, in bytes. * * @param stream The AVI stream. * * @return The length of the data, in bytes. Upon error it will * return negative value. */ PJ_DECL(pj_ssize_t) pjmedia_avi_stream_get_len(pjmedia_avi_stream *stream); /** * Register a callback to be called when the file reading has reached the * end of file. If the file is set to play repeatedly, then the callback * will be called multiple times. Note that only one callback can be * registered for each AVI stream. * * @param stream The AVI stream. * @param user_data User data to be specified in the callback * @param cb Callback to be called. If the callback returns non- * PJ_SUCCESS, the playback will stop. Note that if * application destroys the file port in the callback, * it must return non-PJ_SUCCESS here. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_avi_stream_set_eof_cb(pjmedia_avi_stream *stream, void *user_data, pj_status_t (*cb)(pjmedia_avi_stream *stream, void *usr_data)); /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_AVI_STREAM_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/bidirectional.h ================================================ /* $Id: bidirectional.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_BIDIRECTIONAL_H__ #define __PJMEDIA_BIDIRECTIONAL_H__ /** * @file bidirectional.h * @brief Bidirectional media port. */ #include /** * @defgroup PJMEDIA_BIDIRECTIONAL_PORT Bidirectional Port * @ingroup PJMEDIA_PORT * @brief A bidirectional port combines two unidirectional ports into one * bidirectional port * @{ */ PJ_BEGIN_DECL /** * Create bidirectional port from two unidirectional ports * * @param pool Pool to allocate memory. * @param get_port Port where get_frame() will be directed to. * @param put_port Port where put_frame() will be directed to. * @param p_port Pointer to receive the port instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_bidirectional_port_create(pj_pool_t *pool, pjmedia_port *get_port, pjmedia_port *put_port, pjmedia_port **p_port ); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_BIDIRECTIONAL_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/circbuf.h ================================================ /* $Id: circbuf.h 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CIRC_BUF_H__ #define __PJMEDIA_CIRC_BUF_H__ /** * @file circbuf.h * @brief Circular Buffer. */ #include #include #include #include /** * @defgroup PJMED_CIRCBUF Circular Buffer * @ingroup PJMEDIA_FRAME_OP * @brief Circular buffer manages read and write contiguous audio samples in a * non-contiguous buffer as if the buffer were contiguous. This should give * better performance than keeping contiguous samples in a contiguous buffer, * since read/write operations will only update the pointers, instead of * shifting audio samples. * * @{ * * This section describes PJMEDIA's implementation of circular buffer. */ /* Algorithm checkings, for development purpose only */ #if 0 # define PJMEDIA_CIRC_BUF_CHECK(x) pj_assert(x) #else # define PJMEDIA_CIRC_BUF_CHECK(x) #endif PJ_BEGIN_DECL /** * Circular buffer structure */ typedef struct pjmedia_circ_buf { pj_int16_t *buf; /**< The buffer */ unsigned capacity; /**< Buffer capacity, in samples */ pj_int16_t *start; /**< Pointer to the first sample */ unsigned len; /**< Audio samples length, in samples */ } pjmedia_circ_buf; /** * Create the circular buffer. * * @param pool Pool where the circular buffer will be allocated * from. * @param capacity Capacity of the buffer, in samples. * @param p_cb Pointer to receive the circular buffer instance. * * @return PJ_SUCCESS if the circular buffer has been * created successfully, otherwise the appropriate * error will be returned. */ PJ_INLINE(pj_status_t) pjmedia_circ_buf_create(pj_pool_t *pool, unsigned capacity, pjmedia_circ_buf **p_cb) { pjmedia_circ_buf *cbuf; cbuf = PJ_POOL_ZALLOC_T(pool, pjmedia_circ_buf); cbuf->buf = (pj_int16_t*) pj_pool_calloc(pool, capacity, sizeof(pj_int16_t)); cbuf->capacity = capacity; cbuf->start = cbuf->buf; cbuf->len = 0; *p_cb = cbuf; return PJ_SUCCESS; } /** * Reset the circular buffer. * * @param circbuf The circular buffer. * * @return PJ_SUCCESS when successful. */ PJ_INLINE(pj_status_t) pjmedia_circ_buf_reset(pjmedia_circ_buf *circbuf) { circbuf->start = circbuf->buf; circbuf->len = 0; return PJ_SUCCESS; } /** * Get the circular buffer length, it is number of samples buffered in the * circular buffer. * * @param circbuf The circular buffer. * * @return The buffer length. */ PJ_INLINE(unsigned) pjmedia_circ_buf_get_len(pjmedia_circ_buf *circbuf) { return circbuf->len; } /** * Set circular buffer length. This is useful when audio buffer is manually * manipulated by the user, e.g: shrinked, expanded. * * @param circbuf The circular buffer. * @param len The new buffer length. */ PJ_INLINE(void) pjmedia_circ_buf_set_len(pjmedia_circ_buf *circbuf, unsigned len) { PJMEDIA_CIRC_BUF_CHECK(len <= circbuf->capacity); circbuf->len = len; } /** * Advance the read pointer of circular buffer. This function will discard * the skipped samples while advancing the read pointer, thus reducing * the buffer length. * * @param circbuf The circular buffer. * @param count Distance from current read pointer, can only be * possitive number, in samples. * * @return PJ_SUCCESS when successful, otherwise * the appropriate error will be returned. */ PJ_INLINE(pj_status_t) pjmedia_circ_buf_adv_read_ptr(pjmedia_circ_buf *circbuf, unsigned count) { if (count >= circbuf->len) return pjmedia_circ_buf_reset(circbuf); PJMEDIA_CIRC_BUF_CHECK(count <= circbuf->len); circbuf->start += count; if (circbuf->start >= circbuf->buf + circbuf->capacity) circbuf->start -= circbuf->capacity; circbuf->len -= count; return PJ_SUCCESS; } /** * Advance the write pointer of circular buffer. Since write pointer is always * pointing to a sample after the end of sample, so this function also means * increasing the buffer length. * * @param circbuf The circular buffer. * @param count Distance from current write pointer, can only be * possitive number, in samples. * * @return PJ_SUCCESS when successful, otherwise * the appropriate error will be returned. */ PJ_INLINE(pj_status_t) pjmedia_circ_buf_adv_write_ptr(pjmedia_circ_buf *circbuf, unsigned count) { if (count + circbuf->len > circbuf->capacity) return PJ_ETOOBIG; circbuf->len += count; return PJ_SUCCESS; } /** * Get the real buffer addresses containing the audio samples. * * @param circbuf The circular buffer. * @param reg1 Pointer to store the first buffer address. * @param reg1_len Pointer to store the length of the first buffer, * in samples. * @param reg2 Pointer to store the second buffer address. * @param reg2_len Pointer to store the length of the second buffer, * in samples. */ PJ_INLINE(void) pjmedia_circ_buf_get_read_regions(pjmedia_circ_buf *circbuf, pj_int16_t **reg1, unsigned *reg1_len, pj_int16_t **reg2, unsigned *reg2_len) { *reg1 = circbuf->start; *reg1_len = circbuf->len; if (*reg1 + *reg1_len > circbuf->buf + circbuf->capacity) { *reg1_len = (unsigned)(circbuf->buf + circbuf->capacity - circbuf->start); *reg2 = circbuf->buf; *reg2_len = circbuf->len - *reg1_len; } else { *reg2 = NULL; *reg2_len = 0; } PJMEDIA_CIRC_BUF_CHECK(*reg1_len != 0 || (*reg1_len == 0 && circbuf->len == 0)); PJMEDIA_CIRC_BUF_CHECK(*reg1_len + *reg2_len == circbuf->len); } /** * Get the real buffer addresses that is empty or writeable. * * @param circbuf The circular buffer. * @param reg1 Pointer to store the first buffer address. * @param reg1_len Pointer to store the length of the first buffer, * in samples. * @param reg2 Pointer to store the second buffer address. * @param reg2_len Pointer to store the length of the second buffer, * in samples. */ PJ_INLINE(void) pjmedia_circ_buf_get_write_regions(pjmedia_circ_buf *circbuf, pj_int16_t **reg1, unsigned *reg1_len, pj_int16_t **reg2, unsigned *reg2_len) { *reg1 = circbuf->start + circbuf->len; if (*reg1 >= circbuf->buf + circbuf->capacity) *reg1 -= circbuf->capacity; *reg1_len = circbuf->capacity - circbuf->len; if (*reg1 + *reg1_len > circbuf->buf + circbuf->capacity) { *reg1_len = (unsigned)(circbuf->buf + circbuf->capacity - *reg1); *reg2 = circbuf->buf; *reg2_len = (unsigned)(circbuf->start - circbuf->buf); } else { *reg2 = NULL; *reg2_len = 0; } PJMEDIA_CIRC_BUF_CHECK(*reg1_len != 0 || (*reg1_len == 0 && circbuf->len == 0)); PJMEDIA_CIRC_BUF_CHECK(*reg1_len + *reg2_len == circbuf->capacity - circbuf->len); } /** * Read audio samples from the circular buffer. * * @param circbuf The circular buffer. * @param data Buffer to store the read audio samples. * @param count Number of samples being read. * * @return PJ_SUCCESS when successful, otherwise * the appropriate error will be returned. */ PJ_INLINE(pj_status_t) pjmedia_circ_buf_read(pjmedia_circ_buf *circbuf, pj_int16_t *data, unsigned count) { pj_int16_t *reg1, *reg2; unsigned reg1cnt, reg2cnt; /* Data in the buffer is less than requested */ if (count > circbuf->len) return PJ_ETOOBIG; pjmedia_circ_buf_get_read_regions(circbuf, ®1, ®1cnt, ®2, ®2cnt); if (reg1cnt >= count) { pjmedia_copy_samples(data, reg1, count); } else { pjmedia_copy_samples(data, reg1, reg1cnt); pjmedia_copy_samples(data + reg1cnt, reg2, count - reg1cnt); } return pjmedia_circ_buf_adv_read_ptr(circbuf, count); } /** * Write audio samples to the circular buffer. * * @param circbuf The circular buffer. * @param data Audio samples to be written. * @param count Number of samples being written. * * @return PJ_SUCCESS when successful, otherwise * the appropriate error will be returned. */ PJ_INLINE(pj_status_t) pjmedia_circ_buf_write(pjmedia_circ_buf *circbuf, pj_int16_t *data, unsigned count) { pj_int16_t *reg1, *reg2; unsigned reg1cnt, reg2cnt; /* Data to write is larger than buffer can store */ if (count > circbuf->capacity - circbuf->len) return PJ_ETOOBIG; pjmedia_circ_buf_get_write_regions(circbuf, ®1, ®1cnt, ®2, ®2cnt); if (reg1cnt >= count) { pjmedia_copy_samples(reg1, data, count); } else { pjmedia_copy_samples(reg1, data, reg1cnt); pjmedia_copy_samples(reg2, data + reg1cnt, count - reg1cnt); } return pjmedia_circ_buf_adv_write_ptr(circbuf, count); } /** * Copy audio samples from the circular buffer without changing its state. * * @param circbuf The circular buffer. * @param start_idx Starting sample index to be copied. * @param data Buffer to store the read audio samples. * @param count Number of samples being read. * * @return PJ_SUCCESS when successful, otherwise * the appropriate error will be returned. */ PJ_INLINE(pj_status_t) pjmedia_circ_buf_copy(pjmedia_circ_buf *circbuf, unsigned start_idx, pj_int16_t *data, unsigned count) { pj_int16_t *reg1, *reg2; unsigned reg1cnt, reg2cnt; /* Data in the buffer is less than requested */ if (count + start_idx > circbuf->len) return PJ_ETOOBIG; pjmedia_circ_buf_get_read_regions(circbuf, ®1, ®1cnt, ®2, ®2cnt); if (reg1cnt > start_idx) { unsigned tmp_len; tmp_len = reg1cnt - start_idx; if (tmp_len > count) tmp_len = count; pjmedia_copy_samples(data, reg1 + start_idx, tmp_len); if (tmp_len < count) pjmedia_copy_samples(data + tmp_len, reg2, count - tmp_len); } else { pjmedia_copy_samples(data, reg2 + start_idx - reg1cnt, count); } return PJ_SUCCESS; } /** * Pack the buffer so the first sample will be in the beginning of the buffer. * This will also make the buffer contiguous. * * @param circbuf The circular buffer. * * @return PJ_SUCCESS when successful, otherwise * the appropriate error will be returned. */ PJ_INLINE(pj_status_t) pjmedia_circ_buf_pack_buffer(pjmedia_circ_buf *circbuf) { pj_int16_t *reg1, *reg2; unsigned reg1cnt, reg2cnt; unsigned gap; pjmedia_circ_buf_get_read_regions(circbuf, ®1, ®1cnt, ®2, ®2cnt); /* Check if not contigue */ if (reg2cnt != 0) { /* Check if no space left to roll the buffer * (or should this function provide temporary buffer?) */ gap = circbuf->capacity - pjmedia_circ_buf_get_len(circbuf); if (gap == 0) return PJ_ETOOBIG; /* Roll buffer left using the gap until reg2cnt == 0 */ do { if (gap > reg2cnt) gap = reg2cnt; pjmedia_move_samples(reg1 - gap, reg1, reg1cnt); pjmedia_copy_samples(reg1 + reg1cnt - gap, reg2, gap); if (gap < reg2cnt) pjmedia_move_samples(reg2, reg2 + gap, reg2cnt - gap); reg1 -= gap; reg1cnt += gap; reg2cnt -= gap; } while (reg2cnt > 0); } /* Finally, Shift samples to the left edge */ if (reg1 != circbuf->buf) pjmedia_move_samples(circbuf->buf, reg1, pjmedia_circ_buf_get_len(circbuf)); circbuf->start = circbuf->buf; return PJ_SUCCESS; } PJ_END_DECL /** * @} */ #endif ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/clock.h ================================================ /* $Id: clock.h 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CLOCK_H__ #define __PJMEDIA_CLOCK_H__ /** * @file clock.h * @brief Media clock. */ #include /** * @defgroup PJMEDIA_PORT_CLOCK Clock/Timing * @ingroup PJMEDIA_PORT * @brief Various types of classes that provide timing. * @{ The media clock/timing extends the media port concept that is explained in @ref PJMEDIA_PORT. When clock is present in the ports interconnection, media will flow automatically (and with correct timing too!) from one media port to another. There are few objects in PJMEDIA that are able to provide clock/timing to media ports interconnection: - @ref PJMED_SND_PORT\n The sound device makes a good candidate as the clock source, and PJMEDIA @ref PJMED_SND is designed so that it is able to invoke operations according to timing driven by the sound hardware clock (this may sound complicated, but actually it just means that the sound device abstraction provides callbacks to be called when it has/wants media frames).\n See @ref PJMED_SND_PORT for more details. - @ref PJMEDIA_MASTER_PORT\n The master port uses @ref PJMEDIA_CLOCK as the clock source. By using @ref PJMEDIA_MASTER_PORT, it is possible to interconnect passive media ports and let the frames flow automatically in timely manner.\n Please see @ref PJMEDIA_MASTER_PORT for more details. @} */ /** * @addtogroup PJMEDIA_CLOCK Clock Generator * @ingroup PJMEDIA_PORT_CLOCK * @brief Interface for generating clock. * @{ * * The clock generator provides the application with media timing, * and it is used by the @ref PJMEDIA_MASTER_PORT for its sound clock. * * The clock generator may be configured to run asynchronously * (the default behavior) or synchronously. When it is run * asynchronously, it will call the application's callback every time * the clock tick expires. When it is run synchronously, * application must continuously polls the clock generator to synchronize * the timing. */ PJ_BEGIN_DECL /** * Media clock source. */ typedef struct pjmedia_clock_src { pjmedia_type media_type; /**< Media type. */ unsigned clock_rate; /**< Clock rate. */ unsigned ptime_usec; /**< Frame interval (in usec). */ /** * The timestamp field holds an increasing value in samples and its * value is expected to be increased by clock_rate samples per second. */ pj_timestamp timestamp; /** * Timestamp's last update. The last_update field contains a value in * ticks, and it is expected to be increased by pj_get_timestamp_freq() * ticks per second. */ pj_timestamp last_update; } pjmedia_clock_src; /** * This is an auxiliary function to initialize the media clock source. * * @param clocksrc The clock source to be initialized. * @param media_type The media type. * @param clock_rate The clock rate. * @param ptime_usec Media frame interval (in usec). * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_clock_src_init( pjmedia_clock_src *clocksrc, pjmedia_type media_type, unsigned clock_rate, unsigned ptime_usec ); /** * This function updates the clock source's timestamp. Application should * use this function instead of updating the timestamp directly since this * function will also update the last_update field of the clock source. * * @param clocksrc The clock source to be updated. * @param timestamp The new timestamp, can be NULL if the current * timestamp does not change (in this case it * will only update the last_update field). * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_clock_src_update( pjmedia_clock_src *clocksrc, const pj_timestamp *timestamp ); /** * This function gets the clock source's current timestamp. Application * should use this function instead of accessing the timestamp directly * since this function will calculate the predicted timestamp for current * time, based on the values of timestamp, last_update, and clock_rate. * * @param clocksrc The clock source. * @param timestamp Argument to receive the current timestamp * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_clock_src_get_current_timestamp( const pjmedia_clock_src *clocksrc, pj_timestamp *timestamp); /** * This function gets the clock source's time in msec. * * @param clocksrc The clock source. * * @return The clock source's time (in msec). */ PJ_DECL(pj_uint32_t) pjmedia_clock_src_get_time_msec( const pjmedia_clock_src *clocksrc ); /** * Opaque declaration for media clock. */ typedef struct pjmedia_clock pjmedia_clock; /** * Options when creating the clock. */ enum pjmedia_clock_options { /** * Prevents the clock from running asynchronously. In this case, * application must poll the clock continuously by calling * #pjmedia_clock_wait() in order to synchronize timing. */ PJMEDIA_CLOCK_NO_ASYNC = 1, /** * Prevent the clock from setting it's thread to highest priority. */ PJMEDIA_CLOCK_NO_HIGHEST_PRIO = 2 }; typedef struct pjmedia_clock_param { /** * The frame interval, in microseconds. */ unsigned usec_interval; /** * The media clock rate, to determine timestamp * increment for each call. */ unsigned clock_rate; } pjmedia_clock_param; /** * Type of media clock callback. * * @param ts Current timestamp, in samples. * @param user_data Application data that is passed when * the clock was created. */ typedef void pjmedia_clock_callback(const pj_timestamp *ts, void *user_data); /** * Create media clock. This creates a media clock object that will run * periodically at an interval that is calculated from the audio parameters. * Once created, application must call #pjmedia_clock_start() to actually * start the clock. * * @see pjmedia_clock_create2() * * @param pool Pool to allocate memory. * @param clock_rate Number of samples per second. * @param channel_count Number of channel. * @param samples_per_frame Number of samples per frame. This argument * along with clock_rate and channel_count, specifies * the interval of each clock run (or clock ticks). * @param options Bitmask of pjmedia_clock_options. * @param cb Callback to be called for each clock tick. * @param user_data User data, which will be passed to the callback. * @param p_clock Pointer to receive the clock instance. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_clock_create( pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned options, pjmedia_clock_callback *cb, void *user_data, pjmedia_clock **p_clock); /** * Create media clock. This creates a media clock object that will run * periodically at the specified interval. Once created, application must * call #pjmedia_clock_start() to actually start the clock. * * @param pool Pool to allocate memory. * @param param The clock parameter. * @param options Bitmask of pjmedia_clock_options. * @param cb Callback to be called for each clock tick. * @param user_data User data, which will be passed to the callback. * @param p_clock Pointer to receive the clock instance. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_clock_create2(pj_pool_t *pool, const pjmedia_clock_param *param, unsigned options, pjmedia_clock_callback *cb, void *user_data, pjmedia_clock **p_clock); /** * Start the clock. For clock created with asynchronous flag set to TRUE, * this may start a worker thread for the clock (depending on the * backend clock implementation being used). * * @param clock The media clock. * * @return PJ_SUCCES on success. */ PJ_DECL(pj_status_t) pjmedia_clock_start(pjmedia_clock *clock); /** * Stop the clock. * * @param clock The media clock. * * @return PJ_SUCCES on success. */ PJ_DECL(pj_status_t) pjmedia_clock_stop(pjmedia_clock *clock); /** * Modify the clock's parameter. * * @param clock The media clock. * @param param The clock's new parameter. * @return PJ_SUCCES on success. */ PJ_DECL(pj_status_t) pjmedia_clock_modify(pjmedia_clock *clock, const pjmedia_clock_param *param); /** * Poll the media clock, and execute the callback when the clock tick has * elapsed. This operation is only valid if the clock is created with async * flag set to FALSE. * * @param clock The media clock. * @param wait If non-zero, then the function will block until * a clock tick elapsed and callback has been called. * @param ts Optional argument to receive the current * timestamp. * * @return Non-zero if clock tick has elapsed, or FALSE if * the function returns before a clock tick has * elapsed. */ PJ_DECL(pj_bool_t) pjmedia_clock_wait(pjmedia_clock *clock, pj_bool_t wait, pj_timestamp *ts); /** * Destroy the clock. * * @param clock The media clock. * * @return PJ_SUCCES on success. */ PJ_DECL(pj_status_t) pjmedia_clock_destroy(pjmedia_clock *clock); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_CLOCK_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/codec.h ================================================ /* $Id: codec.h 4278 2012-10-05 10:04:54Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CODEC_H__ #define __PJMEDIA_CODEC_H__ /** * @file codec.h * @brief Codec framework. */ #include #include #include #include PJ_BEGIN_DECL /** * @defgroup PJMEDIA_CODEC Codec Framework * @brief Media codec framework and management * @{ * * @section codec_mgmt_sec Codec Management * @subsection codec_fact_sec Codec Manager * * The codec manager is used to manage all codec capabilities in the endpoint. * When used with media endpoint (pjmedia_endpt), application can retrieve * the codec manager instance by calling #pjmedia_endpt_get_codec_mgr(). * * @subsection reg_new_codec Registering New Codec * * New codec types can be registered to PJMEDIA (or to be precise, to the * codec manager) during run-time. * To do this, application needs to initialize an instance of * codec factory (#pjmedia_codec_factory) and registers this codec factory * by calling #pjmedia_codec_mgr_register_factory(). * * For codecs implemented/supported by PJMEDIA, this process is normally * concealed in an easy to use function such as #pjmedia_codec_g711_init(). * * @subsection codec_factory Codec Factory * * A codec factory (#pjmedia_codec_factory) is registered to codec manager, * and it is used to create and release codec instance. * * The most important member of the codec factory is the "virtual" function * table #pjmedia_codec_factory_op, where it contains, among other thing, * pointer to functions to allocate and deallocate codec instance. * * @subsection codec_inst Codec Instance * * Application allocates codec instance by calling #pjmedia_codec_mgr_alloc_codec(). * One codec instance (#pjmedia_codec) can be used for simultaneous encoding * and decoding. * * The most important member of the codec instance is the "virtual" function * table #pjmedia_codec_op, where it holds pointer to functions to * encode/decode media frames. * * @subsection codec_ident Codec Identification * * A particular codec type in PJMEDIA can be uniquely identified by two * keys: by #pjmedia_codec_info, or by #pjmedia_codec_id string. A fully * qualified codec ID string consists of codec name, sampling rate, and * number of channels. However, application may use only first parts of * the tokens as long as it will make to codec ID unique. For example, "gsm" * is a fully qualified codec name, since it will always have 8000 clock * rate and 1 channel. Other examples of fully qualified codec ID strings * are "pcma", "speex/8000", "speex/16000", and "L16/16000/1". A codec * id "speex" (without clock rate) is not fully qualified, since it will * match the narrowband, wideband, and ultrawideband Speex codec. * * The two keys can be converted to one another, with * #pjmedia_codec_info_to_id() and #pjmedia_codec_mgr_find_codecs_by_id() * functions. * * Codec ID string is not case sensitive. * * * @section using_codec Using the Codec Framework * @subsection init_alloc_codec Allocating Codec * * Application needs to allocate one codec instance for encoding and decoding * media frames. One codec instance can be used to perform both encoding * and decoding. * * Application allocates codec by calling #pjmedia_codec_mgr_alloc_codec(). * This function takes #pjmedia_codec_info argument, which is used to locate * the particular codec factory to be used to allocate the codec. * * Application can build #pjmedia_codec_info structure manually for * the specific codec, or alternatively it may get the #pjmedia_codec_info * from the codec ID string, by using #pjmedia_codec_mgr_find_codecs_by_id() * function. * * The following snippet shows an example to allocate a codec: * \code pj_str_t codec_id; pjmedia_codec_info *codec_info; unsigned count = 1; pjmedia_codec *codec; codec_id = pj_str("pcma"); // Find codec info for the specified coded ID (i.e. "pcma"). status = pjmedia_codec_mgr_find_codecs_by_id( codec_mgr, &codec_id, &count, &codec_info, NULL); // Allocate the codec. status = pjmedia_codec_mgr_alloc_codec( codec_mgr, codec_info, &codec ); \endcode * * * @subsection opening_codec Initializing Codec * * Once codec is allocated, application needs to initialize the codec * by calling open member of the codec. This function * takes #pjmedia_codec_param as the argument, which contains the * settings for the codec. * * Application shoud use #pjmedia_codec_mgr_get_default_param() function * to initiaize #pjmedia_codec_param. The setting part of * #pjmedia_codec_param then can be tuned to suit the application's * requirements. * * The following snippet shows an example to initialize codec: * \code pjmedia_codec_param param; // Retrieve default codec param for the specified codec. pjmedia_codec_mgr_get_default_param(codec_mgr, codec_info ¶m); // Application may change the "settings" part of codec param, // for example, to disable VAD param.setting.vad = 0; // Open the codec using the specified settings. codec->op->open( codec, ¶m ); \endcode * * * @subsection enc_dec_codec Encoding and Decoding Media Frames * * Application encodes and decodes media frames by calling * encode and decode member of the codec's "virtual" * function table (#pjmedia_codec_op). * * @subsection plc_codec Concealing Lost Frames * * All codecs has Packet Lost Concealment (PLC) feature, and application * can activate the PLC to conceal lost frames by calling recover * member of the codec's "virtual" function table (#pjmedia_codec_op). * * If the codec's algorithm supports PLC, the recover function * will use the codec's PLC. Otherwise for codecs that don't have * intrinsic PLC, PJMEDIA will suply the PLC implementation from the * @ref PJMED_PLC implementation. * * @subsection close_codec Closing and Releasing the Codec * * The codec must be closed by calling close member of the codec's * operation. Then it must be released by calling * #pjmedia_codec_mgr_dealloc_codec(). */ /** * Standard RTP static payload types, as defined by RFC 3551. * The header file also declares dynamic payload * type numbers that are used by PJMEDIA when advertising the capability * for example in SDP message. */ enum pjmedia_rtp_pt { PJMEDIA_RTP_PT_PCMU = 0, /**< audio PCMU */ PJMEDIA_RTP_PT_G721 = 2, /**< audio G721 (old def for G726-32) */ PJMEDIA_RTP_PT_GSM = 3, /**< audio GSM */ PJMEDIA_RTP_PT_G723 = 4, /**< audio G723 */ PJMEDIA_RTP_PT_DVI4_8K = 5, /**< audio DVI4 8KHz */ PJMEDIA_RTP_PT_DVI4_16K = 6, /**< audio DVI4 16Khz */ PJMEDIA_RTP_PT_LPC = 7, /**< audio LPC */ PJMEDIA_RTP_PT_PCMA = 8, /**< audio PCMA */ PJMEDIA_RTP_PT_G722 = 9, /**< audio G722 */ PJMEDIA_RTP_PT_L16_2 = 10, /**< audio 16bit linear 44.1KHz stereo */ PJMEDIA_RTP_PT_L16_1 = 11, /**< audio 16bit linear 44.1KHz mono */ PJMEDIA_RTP_PT_QCELP = 12, /**< audio QCELP */ PJMEDIA_RTP_PT_CN = 13, /**< audio Comfort Noise */ PJMEDIA_RTP_PT_MPA = 14, /**< audio MPEG1/MPEG2 elemetr. streams */ PJMEDIA_RTP_PT_G728 = 15, /**< audio G728 */ PJMEDIA_RTP_PT_DVI4_11K = 16, /**< audio DVI4 11.025KHz mono */ PJMEDIA_RTP_PT_DVI4_22K = 17, /**< audio DVI4 22.050KHz mono */ PJMEDIA_RTP_PT_G729 = 18, /**< audio G729 */ PJMEDIA_RTP_PT_CELB = 25, /**< video/comb Cell-B by Sun (RFC2029) */ PJMEDIA_RTP_PT_JPEG = 26, /**< video JPEG */ PJMEDIA_RTP_PT_NV = 28, /**< video NV by nv program by Xerox */ PJMEDIA_RTP_PT_H261 = 31, /**< video H261 */ PJMEDIA_RTP_PT_MPV = 32, /**< video MPEG1 or MPEG2 elementary */ PJMEDIA_RTP_PT_MP2T = 33, /**< video MPEG2 transport */ PJMEDIA_RTP_PT_H263 = 34, /**< video H263 */ PJMEDIA_RTP_PT_DYNAMIC = 96 /**< start of dynamic RTP payload */ }; /** * Identification used to search for codec factory that supports specific * codec specification. */ typedef struct pjmedia_codec_info { pjmedia_type type; /**< Media type. */ unsigned pt; /**< Payload type (can be dynamic). */ pj_str_t encoding_name; /**< Encoding name. */ unsigned clock_rate; /**< Sampling rate. */ unsigned channel_cnt; /**< Channel count. */ } pjmedia_codec_info; /** * Structure of codec specific parameters which contains name=value pairs. * The codec specific parameters are to be used with SDP according to * the standards (e.g: RFC 3555) in SDP 'a=fmtp' attribute. */ typedef struct pjmedia_codec_fmtp { pj_uint8_t cnt; /**< Number of parameters. */ struct param { pj_str_t name; /**< Parameter name. */ pj_str_t val; /**< Parameter value. */ } param [PJMEDIA_CODEC_MAX_FMTP_CNT]; /**< The parameters. */ } pjmedia_codec_fmtp; /** * Detailed codec attributes used in configuring a codec and in querying * the capability of codec factories. Default attributes of any codecs could * be queried using #pjmedia_codec_mgr_get_default_param() and modified * using #pjmedia_codec_mgr_set_default_param(). * * Please note that codec parameter also contains SDP specific setting, * #dec_fmtp and #enc_fmtp, which may need to be set appropriately based on * the effective setting. See each codec documentation for more detail. */ typedef struct pjmedia_codec_param { /** * The "info" part of codec param describes the capability of the codec, * and the value should NOT be changed by application. */ struct { unsigned clock_rate; /**< Sampling rate in Hz */ unsigned channel_cnt; /**< Channel count. */ pj_uint32_t avg_bps; /**< Average bandwidth in bits/sec */ pj_uint32_t max_bps; /**< Maximum bandwidth in bits/sec */ unsigned max_rx_frame_size; /**< Maximum frame size */ pj_uint16_t frm_ptime; /**< Decoder frame ptime in msec. */ pj_uint16_t enc_ptime; /**< Encoder ptime, or zero if it's equal to decoder ptime. */ pj_uint8_t pcm_bits_per_sample; /**< Bits/sample in the PCM side */ pj_uint8_t pt; /**< Payload type. */ pjmedia_format_id fmt_id; /**< Source format, it's format of encoder input and decoder output. */ } info; /** * The "setting" part of codec param describes various settings to be * applied to the codec. When the codec param is retrieved from the codec * or codec factory, the values of these will be filled by the capability * of the codec. Any features that are supported by the codec (e.g. vad * or plc) will be turned on, so that application can query which * capabilities are supported by the codec. Application may change the * settings here before instantiating the codec/stream. */ struct { pj_uint8_t frm_per_pkt; /**< Number of frames per packet. */ unsigned vad:1; /**< Voice Activity Detector. */ unsigned cng:1; /**< Comfort Noise Generator. */ unsigned penh:1; /**< Perceptual Enhancement */ unsigned plc:1; /**< Packet loss concealment */ unsigned reserved:1; /**< Reserved, must be zero. */ pjmedia_codec_fmtp enc_fmtp;/**< Encoder's fmtp params. */ pjmedia_codec_fmtp dec_fmtp;/**< Decoder's fmtp params. */ } setting; } pjmedia_codec_param; /** * Duplicate codec parameter. * * @param pool The pool. * @param src The codec parameter to be duplicated. * * @return Duplicated codec parameter. */ PJ_DECL(pjmedia_codec_param*) pjmedia_codec_param_clone( pj_pool_t *pool, const pjmedia_codec_param *src); /* * Forward declaration for pjmedia_codec. */ typedef struct pjmedia_codec pjmedia_codec; /** * This structure describes codec operations. Each codec MUST implement * all of these functions. */ typedef struct pjmedia_codec_op { /** * Initialize codec using the specified attribute. * * Application should call #pjmedia_codec_init() instead of * calling this function directly. * * @param codec The codec instance. * @param pool Pool to use when the codec needs to allocate * some memory. * * @return PJ_SUCCESS on success. */ pj_status_t (*init)(pjmedia_codec *codec, pj_pool_t *pool ); /** * Open the codec and initialize with the specified parameter. * Upon successful initialization, the codec may modify the parameter * and fills in the unspecified values (such as enc_ptime, when * encoder ptime is different than decoder ptime). * * Application should call #pjmedia_codec_open() instead of * calling this function directly. * * @param codec The codec instance. * @param param Codec initialization parameter. * * @return PJ_SUCCESS on success. */ pj_status_t (*open)(pjmedia_codec *codec, pjmedia_codec_param *param ); /** * Close and shutdown codec, releasing all resources allocated by * this codec, if any. * * Application should call #pjmedia_codec_close() instead of * calling this function directly. * * @param codec The codec instance. * * @return PJ_SUCCESS on success. */ pj_status_t (*close)(pjmedia_codec *codec); /** * Modify the codec parameter after the codec is open. * Note that not all codec parameters can be modified during run-time. * When the parameter cannot be changed, this function will return * non-PJ_SUCCESS, and the original parameters will not be changed. * * Application can expect changing trivial codec settings such as * changing VAD setting to succeed. * * Application should call #pjmedia_codec_modify() instead of * calling this function directly. * * @param codec The codec instance. * @param param The new codec parameter. * * @return PJ_SUCCESS on success. */ pj_status_t (*modify)(pjmedia_codec *codec, const pjmedia_codec_param *param ); /** * Instruct the codec to inspect the specified payload/packet and * split the packet into individual base frames. Each output frames will * have ptime that is equal to basic frame ptime (i.e. the value of * info.frm_ptime in #pjmedia_codec_param). * * Application should call #pjmedia_codec_parse() instead of * calling this function directly. * * @param codec The codec instance * @param pkt The input packet. * @param pkt_size Size of the packet. * @param timestamp The timestamp of the first sample in the packet. * @param frame_cnt On input, specifies the maximum number of frames * in the array. On output, the codec must fill * with number of frames detected in the packet. * @param frames On output, specifies the frames that have been * detected in the packet. * * @return PJ_SUCCESS on success. */ pj_status_t (*parse)( pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *timestamp, unsigned *frame_cnt, pjmedia_frame frames[]); /** * Instruct the codec to encode the specified input frame. The input * PCM samples MUST have ptime that is multiplication of base frame * ptime (i.e. the value of info.frm_ptime in #pjmedia_codec_param). * * Application should call #pjmedia_codec_encode() instead of * calling this function directly. * * @param codec The codec instance. * @param input The input frame. * @param out_size The length of buffer in the output frame. * @param output The output frame. * * @return PJ_SUCCESS on success; */ pj_status_t (*encode)(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned out_size, struct pjmedia_frame *output); /** * Instruct the codec to decode the specified input frame. The input * frame MUST have ptime that is exactly equal to base frame * ptime (i.e. the value of info.frm_ptime in #pjmedia_codec_param). * Application can achieve this by parsing the packet into base * frames before decoding each frame. * * Application should call #pjmedia_codec_decode() instead of * calling this function directly. * * @param codec The codec instance. * @param input The input frame. * @param out_size The length of buffer in the output frame. * @param output The output frame. * * @return PJ_SUCCESS on success; */ pj_status_t (*decode)(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned out_size, struct pjmedia_frame *output); /** * Instruct the codec to recover a missing frame. * * Application should call #pjmedia_codec_recover() instead of * calling this function directly. * * @param codec The codec instance. * @param out_size The length of buffer in the output frame. * @param output The output frame where generated signal * will be placed. * * @return PJ_SUCCESS on success; */ pj_status_t (*recover)(pjmedia_codec *codec, unsigned out_size, struct pjmedia_frame *output); } pjmedia_codec_op; /* * Forward declaration for pjmedia_codec_factory. */ typedef struct pjmedia_codec_factory pjmedia_codec_factory; /** * This structure describes a codec instance. */ struct pjmedia_codec { /** Entries to put this codec instance in codec factory's list. */ PJ_DECL_LIST_MEMBER(struct pjmedia_codec); /** Codec's private data. */ void *codec_data; /** Codec factory where this codec was allocated. */ pjmedia_codec_factory *factory; /** Operations to codec. */ pjmedia_codec_op *op; }; /** * This structure describes operations that must be supported by codec * factories. */ typedef struct pjmedia_codec_factory_op { /** * Check whether the factory can create codec with the specified * codec info. * * @param factory The codec factory. * @param info The codec info. * * @return PJ_SUCCESS if this factory is able to create an * instance of codec with the specified info. */ pj_status_t (*test_alloc)(pjmedia_codec_factory *factory, const pjmedia_codec_info *info ); /** * Create default attributes for the specified codec ID. This function * can be called by application to get the capability of the codec. * * @param factory The codec factory. * @param info The codec info. * @param attr The attribute to be initialized. * * @return PJ_SUCCESS if success. */ pj_status_t (*default_attr)(pjmedia_codec_factory *factory, const pjmedia_codec_info *info, pjmedia_codec_param *attr ); /** * Enumerate supported codecs that can be created using this factory. * * @param factory The codec factory. * @param count On input, specifies the number of elements in * the array. On output, the value will be set to * the number of elements that have been initialized * by this function. * @param info The codec info array, which contents will be * initialized upon return. * * @return PJ_SUCCESS on success. */ pj_status_t (*enum_info)(pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]); /** * Create one instance of the codec with the specified codec info. * * @param factory The codec factory. * @param info The codec info. * @param p_codec Pointer to receive the codec instance. * * @return PJ_SUCCESS on success. */ pj_status_t (*alloc_codec)(pjmedia_codec_factory *factory, const pjmedia_codec_info *info, pjmedia_codec **p_codec); /** * This function is called by codec manager to return a particular * instance of codec back to the codec factory. * * @param factory The codec factory. * @param codec The codec instance to be returned. * * @return PJ_SUCCESS on success. */ pj_status_t (*dealloc_codec)(pjmedia_codec_factory *factory, pjmedia_codec *codec ); /** * This callback will be called to deinitialize and destroy this factory. */ pj_status_t (*destroy)(void); } pjmedia_codec_factory_op; /** * Codec factory describes a module that is able to create codec with specific * capabilities. These capabilities can be queried by codec manager to create * instances of codec. */ struct pjmedia_codec_factory { /** Entries to put this structure in the codec manager list. */ PJ_DECL_LIST_MEMBER(struct pjmedia_codec_factory); /** The factory's private data. */ void *factory_data; /** Operations to the factory. */ pjmedia_codec_factory_op *op; }; /** * Declare maximum codecs */ #define PJMEDIA_CODEC_MGR_MAX_CODECS 32 /** * Specify these values to set the codec priority, by calling * #pjmedia_codec_mgr_set_codec_priority(). */ typedef enum pjmedia_codec_priority { /** * This priority makes the codec the highest in the order. * The last codec specified with this priority will get the * highest place in the order, and will change the priority * of previously highest priority codec to NEXT_HIGHER. */ PJMEDIA_CODEC_PRIO_HIGHEST = 255, /** * This priority will put the codec as the next codec after * codecs with this same priority. */ PJMEDIA_CODEC_PRIO_NEXT_HIGHER = 254, /** * This is the initial codec priority when it is registered to * codec manager by codec factory. */ PJMEDIA_CODEC_PRIO_NORMAL = 128, /** * This priority makes the codec the lowest in the order. * The last codec specified with this priority will be put * in the last place in the order. */ PJMEDIA_CODEC_PRIO_LOWEST = 1, /** * This priority will prevent the codec from being listed in the * SDP created by media endpoint, thus should prevent the codec * from being used in the sessions. However, the codec will still * be listed by #pjmedia_codec_mgr_enum_codecs() and other codec * query functions. */ PJMEDIA_CODEC_PRIO_DISABLED = 0 } pjmedia_codec_priority; /** * Codec identification (e.g. "pcmu/8000/1"). * See @ref codec_ident for more info. */ typedef char pjmedia_codec_id[32]; /** * Opaque declaration of default codecs parameters. */ typedef struct pjmedia_codec_default_param pjmedia_codec_default_param; /** * Codec manager maintains array of these structs for each supported * codec. */ struct pjmedia_codec_desc { pjmedia_codec_info info; /**< Codec info. */ pjmedia_codec_id id; /**< Fully qualified name */ pjmedia_codec_priority prio; /**< Priority. */ pjmedia_codec_factory *factory; /**< The factory. */ pjmedia_codec_default_param *param; /**< Default codecs parameters. */ }; /** * The declaration for codec manager. Application doesn't normally need * to see this declaration, but nevertheless this declaration is needed * by media endpoint to instantiate the codec manager. */ typedef struct pjmedia_codec_mgr { /** Media endpoint instance. */ pj_pool_factory *pf; /** Codec manager pool. */ pj_pool_t *pool; /** Codec manager mutex. */ pj_mutex_t *mutex; /** List of codec factories registered to codec manager. */ pjmedia_codec_factory factory_list; /** Number of supported codecs. */ unsigned codec_cnt; /** Array of codec descriptor. */ struct pjmedia_codec_desc codec_desc[PJMEDIA_CODEC_MGR_MAX_CODECS]; } pjmedia_codec_mgr; /** * Initialize codec manager. Normally this function is called by pjmedia * endpoint's initialization code. * * @param mgr Codec manager instance. * @param pf Pool factory instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_mgr_init(pjmedia_codec_mgr *mgr, pj_pool_factory *pf); /** * Destroy codec manager. Normally this function is called by pjmedia * endpoint's deinitialization code. * * @param mgr Codec manager instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_mgr_destroy(pjmedia_codec_mgr *mgr); /** * Register codec factory to codec manager. This will also register * all supported codecs in the factory to the codec manager. * * @param mgr The codec manager instance. Application can get the * instance by calling #pjmedia_endpt_get_codec_mgr(). * @param factory The codec factory to be registered. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_mgr_register_factory( pjmedia_codec_mgr *mgr, pjmedia_codec_factory *factory); /** * Unregister codec factory from the codec manager. This will also * remove all the codecs registered by the codec factory from the * codec manager's list of supported codecs. This function should * only be called by the codec implementers and not by application. * * @param mgr The codec manager instance, use * #pjmedia_endpt_get_codec_mgr(). * @param factory The codec factory to be unregistered. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_mgr_unregister_factory( pjmedia_codec_mgr *mgr, pjmedia_codec_factory *factory); /** * Enumerate all supported codecs that have been registered to the * codec manager by codec factories. * * @param mgr The codec manager instance. Application can get the * instance by calling #pjmedia_endpt_get_codec_mgr(). * @param count On input, specifies the number of elements in * the array. On output, the value will be set to * the number of elements that have been initialized * by this function. * @param info The codec info array, which contents will be * initialized upon return. * @param prio Optional pointer to receive array of codec priorities. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_mgr_enum_codecs( pjmedia_codec_mgr *mgr, unsigned *count, pjmedia_codec_info info[], unsigned *prio); /** * Get codec info for the specified static payload type. Note that * this can only find codec with static payload types. This function can * be used to find codec info for a payload type inside SDP which doesn't * have the corresponding rtpmap attribute. * * @param mgr The codec manager instance. Application can get the * instance by calling #pjmedia_endpt_get_codec_mgr(). * @param pt Static payload type/number. * @param inf Pointer to receive codec info. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_mgr_get_codec_info( pjmedia_codec_mgr *mgr, unsigned pt, const pjmedia_codec_info **inf); /** * Convert codec info struct into a unique codec identifier. * A codec identifier looks something like "L16/44100/2". * * @param info The codec info * @param id Buffer to put the codec info string. * @param max_len The length of the buffer. * * @return The null terminated codec info string, or NULL if * the buffer is not long enough. */ PJ_DECL(char*) pjmedia_codec_info_to_id(const pjmedia_codec_info *info, char *id, unsigned max_len ); /** * Find codecs by the unique codec identifier. This function will find * all codecs that match the codec identifier prefix. For example, if * "L16" is specified, then it will find "L16/8000/1", "L16/16000/1", * and so on, up to the maximum count specified in the argument. * * @param mgr The codec manager instance. Application can get the * instance by calling #pjmedia_endpt_get_codec_mgr(). * @param codec_id The full codec ID or codec ID prefix. If an empty * string is given, it will match all codecs. * @param count Maximum number of codecs to find. On return, it * contains the actual number of codecs found. * @param p_info Array of pointer to codec info to be filled. This * argument may be NULL, which in this case, only * codec count will be returned. * @param prio Optional array of codec priorities. * * @return PJ_SUCCESS if at least one codec info is found. */ PJ_DECL(pj_status_t) pjmedia_codec_mgr_find_codecs_by_id( pjmedia_codec_mgr *mgr, const pj_str_t *codec_id, unsigned *count, const pjmedia_codec_info *p_info[], unsigned prio[]); /** * Set codec priority. The codec priority determines the order of * the codec in the SDP created by the endpoint. If more than one codecs * are found with the same codec_id prefix, then the function sets the * priorities of all those codecs. * * @param mgr The codec manager instance. Application can get the * instance by calling #pjmedia_endpt_get_codec_mgr(). * @param codec_id The full codec ID or codec ID prefix. If an empty * string is given, it will match all codecs. * @param prio Priority to be set. The priority can have any value * between 1 to 255. When the priority is set to zero, * the codec will be disabled. * * @return PJ_SUCCESS if at least one codec info is found. */ PJ_DECL(pj_status_t) pjmedia_codec_mgr_set_codec_priority(pjmedia_codec_mgr *mgr, const pj_str_t *codec_id, pj_uint8_t prio); /** * Get default codec param for the specified codec info. * * @param mgr The codec manager instance. Application can get the * instance by calling #pjmedia_endpt_get_codec_mgr(). * @param info The codec info, which default parameter's is being * queried. * @param param On return, will be filled with the default codec * parameter. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_mgr_get_default_param( pjmedia_codec_mgr *mgr, const pjmedia_codec_info *info, pjmedia_codec_param *param ); /** * Set default codec param for the specified codec info. * * @param mgr The codec manager instance. Application can get the * instance by calling #pjmedia_endpt_get_codec_mgr(). * @param info The codec info, which default parameter's is being * updated. * @param param The new default codec parameter. Set to NULL to reset * codec parameter to library default settings. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_mgr_set_default_param( pjmedia_codec_mgr *mgr, const pjmedia_codec_info *info, const pjmedia_codec_param *param ); /** * Request the codec manager to allocate one instance of codec with the * specified codec info. The codec will enumerate all codec factories * until it finds factory that is able to create the specified codec. * * @param mgr The codec manager instance. Application can get the * instance by calling #pjmedia_endpt_get_codec_mgr(). * @param info The information about the codec to be created. * @param p_codec Pointer to receive the codec instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_mgr_alloc_codec( pjmedia_codec_mgr *mgr, const pjmedia_codec_info *info, pjmedia_codec **p_codec); /** * Deallocate the specified codec instance. The codec manager will return * the instance of the codec back to its factory. * * @param mgr The codec manager instance. Application can get the * instance by calling #pjmedia_endpt_get_codec_mgr(). * @param codec The codec instance. * * @return PJ_SUCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_mgr_dealloc_codec(pjmedia_codec_mgr *mgr, pjmedia_codec *codec); /** * Initialize codec using the specified attribute. * * @param codec The codec instance. * @param pool Pool to use when the codec needs to allocate some memory. * * @return PJ_SUCCESS on success. */ PJ_INLINE(pj_status_t) pjmedia_codec_init( pjmedia_codec *codec, pj_pool_t *pool ) { return (*codec->op->init)(codec, pool); } /** * Open the codec and initialize with the specified parameter. * Upon successful initialization, the codec may modify the parameter * and fills in the unspecified values (such as enc_ptime, when * encoder ptime is different than decoder ptime). * * @param codec The codec instance. * @param param Codec initialization parameter. * * @return PJ_SUCCESS on success. */ PJ_INLINE(pj_status_t) pjmedia_codec_open( pjmedia_codec *codec, pjmedia_codec_param *param ) { return (*codec->op->open)(codec, param); } /** * Close and shutdown codec, releasing all resources allocated by * this codec, if any. * * @param codec The codec instance. * * @return PJ_SUCCESS on success. */ PJ_INLINE(pj_status_t) pjmedia_codec_close( pjmedia_codec *codec ) { return (*codec->op->close)(codec); } /** * Modify the codec parameter after the codec is open. * Note that not all codec parameters can be modified during run-time. * When the parameter cannot be changed, this function will return * non-PJ_SUCCESS, and the original parameters will not be changed. * * Application can expect changing trivial codec settings such as * changing VAD setting to succeed. * * @param codec The codec instance. * @param param The new codec parameter. * * @return PJ_SUCCESS on success. */ PJ_INLINE(pj_status_t) pjmedia_codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *param) { return (*codec->op->modify)(codec, param); } /** * Instruct the codec to inspect the specified payload/packet and * split the packet into individual base frames. Each output frames will * have ptime that is equal to basic frame ptime (i.e. the value of * info.frm_ptime in #pjmedia_codec_param). * * @param codec The codec instance * @param pkt The input packet. * @param pkt_size Size of the packet. * @param timestamp The timestamp of the first sample in the packet. * @param frame_cnt On input, specifies the maximum number of frames * in the array. On output, the codec must fill * with number of frames detected in the packet. * @param frames On output, specifies the frames that have been * detected in the packet. * * @return PJ_SUCCESS on success. */ PJ_INLINE(pj_status_t) pjmedia_codec_parse( pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *timestamp, unsigned *frame_cnt, pjmedia_frame frames[] ) { return (*codec->op->parse)(codec, pkt, pkt_size, timestamp, frame_cnt, frames); } /** * Instruct the codec to encode the specified input frame. The input * PCM samples MUST have ptime that is multiplication of base frame * ptime (i.e. the value of info.frm_ptime in #pjmedia_codec_param). * * @param codec The codec instance. * @param input The input frame. * @param out_size The length of buffer in the output frame. * @param output The output frame. * * @return PJ_SUCCESS on success; */ PJ_INLINE(pj_status_t) pjmedia_codec_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned out_size, struct pjmedia_frame *output ) { return (*codec->op->encode)(codec, input, out_size, output); } /** * Instruct the codec to decode the specified input frame. The input * frame MUST have ptime that is exactly equal to base frame * ptime (i.e. the value of info.frm_ptime in #pjmedia_codec_param). * Application can achieve this by parsing the packet into base * frames before decoding each frame. * * @param codec The codec instance. * @param input The input frame. * @param out_size The length of buffer in the output frame. * @param output The output frame. * * @return PJ_SUCCESS on success; */ PJ_INLINE(pj_status_t) pjmedia_codec_decode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned out_size, struct pjmedia_frame *output ) { return (*codec->op->decode)(codec, input, out_size, output); } /** * Instruct the codec to recover a missing frame. * * @param codec The codec instance. * @param out_size The length of buffer in the output frame. * @param output The output frame where generated signal * will be placed. * * @return PJ_SUCCESS on success; */ PJ_INLINE(pj_status_t) pjmedia_codec_recover( pjmedia_codec *codec, unsigned out_size, struct pjmedia_frame *output ) { if (codec->op && codec->op->recover) return (*codec->op->recover)(codec, out_size, output); else return PJ_ENOTSUP; } /** * @} */ /** * @defgroup PJMEDIA_CODEC_CODECS Supported codecs * @ingroup PJMEDIA_CODEC * @brief Documentation about individual codec supported by PJMEDIA * @{ * Please see the APIs provided by the individual codecs below. */ /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_CODEC_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/conference.h ================================================ /* $Id: conference.h 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CONF_H__ #define __PJMEDIA_CONF_H__ /** * @file conference.h * @brief Conference bridge. */ #include /** * @defgroup PJMEDIA_CONF Conference Bridge * @ingroup PJMEDIA_PORT * @brief Audio conference bridge implementation * @{ * * This describes the conference bridge implementation in PJMEDIA. The * conference bridge provides powerful and very efficient mechanism to * route the audio flow and mix the audio signal when required. * * Some more information about the media flow when conference bridge is * used is described in http://www.pjsip.org/trac/wiki/media-flow . */ PJ_BEGIN_DECL /** * The conference bridge signature in pjmedia_port_info. */ #define PJMEDIA_CONF_BRIDGE_SIGNATURE PJMEDIA_SIG_PORT_CONF /** * The audio switchboard signature in pjmedia_port_info. */ #define PJMEDIA_CONF_SWITCH_SIGNATURE PJMEDIA_SIG_PORT_CONF_SWITCH /** * Opaque type for conference bridge. */ typedef struct pjmedia_conf pjmedia_conf; /** * Conference port info. */ typedef struct pjmedia_conf_port_info { unsigned slot; /**< Slot number. */ pj_str_t name; /**< Port name. */ pjmedia_format format; /**< Format. */ pjmedia_port_op tx_setting; /**< Transmit settings. */ pjmedia_port_op rx_setting; /**< Receive settings. */ unsigned listener_cnt; /**< Number of listeners. */ unsigned *listener_slots; /**< Array of listeners. */ unsigned transmitter_cnt; /**< Number of transmitter. */ unsigned clock_rate; /**< Clock rate of the port. */ unsigned channel_count; /**< Number of channels. */ unsigned samples_per_frame; /**< Samples per frame */ unsigned bits_per_sample; /**< Bits per sample. */ int tx_adj_level; /**< Tx level adjustment. */ int rx_adj_level; /**< Rx level adjustment. */ } pjmedia_conf_port_info; /** * Conference port options. The values here can be combined in bitmask to * be specified when the conference bridge is created. */ enum pjmedia_conf_option { PJMEDIA_CONF_NO_MIC = 1, /**< Disable audio streams from the microphone device. */ PJMEDIA_CONF_NO_DEVICE = 2, /**< Do not create sound device. */ PJMEDIA_CONF_SMALL_FILTER=4,/**< Use small filter table when resampling */ PJMEDIA_CONF_USE_LINEAR=8 /**< Use linear resampling instead of filter based. */ }; /** * Create conference bridge with the specified parameters. The sampling rate, * samples per frame, and bits per sample will be used for the internal * operation of the bridge (e.g. when mixing audio frames). However, ports * with different configuration may be connected to the bridge. In this case, * the bridge is able to perform sampling rate conversion, and buffering in * case the samples per frame is different. * * For this version of PJMEDIA, only 16bits per sample is supported. * * For this version of PJMEDIA, the channel count of the ports MUST match * the channel count of the bridge. * * Under normal operation (i.e. when PJMEDIA_CONF_NO_DEVICE option is NOT * specified), the bridge internally create an instance of sound device * and connect the sound device to port zero of the bridge. * * If PJMEDIA_CONF_NO_DEVICE options is specified, no sound device will * be created in the conference bridge. Application MUST acquire the port * interface of the bridge by calling #pjmedia_conf_get_master_port(), and * connect this port interface to a sound device port by calling * #pjmedia_snd_port_connect(), or to a master port (pjmedia_master_port) * if application doesn't want to instantiate any sound devices. * * The sound device or master port are crucial for the bridge's operation, * because it provides the bridge with necessary clock to process the audio * frames periodically. Internally, the bridge runs when get_frame() to * port zero is called. * * @param pool Pool to use to allocate the bridge and * additional buffers for the sound device. * @param max_slots Maximum number of slots/ports to be created in * the bridge. Note that the bridge internally uses * one port for the sound device, so the actual * maximum number of ports will be less one than * this value. * @param sampling_rate Set the sampling rate of the bridge. This value * is also used to set the sampling rate of the * sound device. * @param channel_count Number of channels in the PCM stream. Normally * the value will be 1 for mono, but application may * specify a value of 2 for stereo. Note that all * ports that will be connected to the bridge MUST * have the same number of channels as the bridge. * @param samples_per_frame Set the number of samples per frame. This value * is also used to set the sound device. * @param bits_per_sample Set the number of bits per sample. This value * is also used to set the sound device. Currently * only 16bit per sample is supported. * @param options Bitmask options to be set for the bridge. The * options are constructed from #pjmedia_conf_option * enumeration. * @param p_conf Pointer to receive the conference bridge instance. * * @return PJ_SUCCESS if conference bridge can be created. */ PJ_DECL(pj_status_t) pjmedia_conf_create( pj_pool_t *pool, unsigned max_slots, unsigned sampling_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_conf **p_conf ); /** * Destroy conference bridge. * * @param conf The conference bridge. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf ); /** * Get the master port interface of the conference bridge. The master port * corresponds to the port zero of the bridge. This is only usefull when * application wants to manage the sound device by itself, instead of * allowing the bridge to automatically create a sound device implicitly. * * This function will only return a port interface if PJMEDIA_CONF_NO_DEVICE * option was specified when the bridge was created. * * Application can connect the port returned by this function to a * sound device by calling #pjmedia_snd_port_connect(). * * @param conf The conference bridge. * * @return The port interface of port zero of the bridge, * only when PJMEDIA_CONF_NO_DEVICE options was * specified when the bridge was created. */ PJ_DECL(pjmedia_port*) pjmedia_conf_get_master_port(pjmedia_conf *conf); /** * Set master port name. * * @param conf The conference bridge. * @param name Name to be assigned. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_conf_set_port0_name(pjmedia_conf *conf, const pj_str_t *name); /** * Add media port to the conference bridge. * * By default, the new conference port will have both TX and RX enabled, * but it is not connected to any other ports. Application SHOULD call * #pjmedia_conf_connect_port() to enable audio transmission and receipt * to/from this port. * * Once the media port is connected to other port(s) in the bridge, * the bridge will continuosly call get_frame() and put_frame() to the * port, allowing media to flow to/from the port. * * @param conf The conference bridge. * @param pool Pool to allocate buffers for this port. * @param strm_port Stream port interface. * @param name Optional name for the port. If this value is NULL, * the name will be taken from the name in the port * info. * @param p_slot Pointer to receive the slot index of the port in * the conference bridge. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf, pj_pool_t *pool, pjmedia_port *strm_port, const pj_str_t *name, unsigned *p_slot ); /** * Warning: This API has been deprecated since 1.3 and will be * removed in the future release, use @ref PJMEDIA_SPLITCOMB instead. * * Create and add a passive media port to the conference bridge. Unlike * "normal" media port that is added with #pjmedia_conf_add_port(), media * port created with this function will not have its get_frame() and * put_frame() called by the bridge; instead, application MUST continuosly * call these functions to the port, to allow media to flow from/to the * port. * * Upon return of this function, application will be given two objects: * the slot number of the port in the bridge, and pointer to the media * port where application MUST start calling get_frame() and put_frame() * to the port. * * @param conf The conference bridge. * @param pool Pool to allocate buffers etc for this port. * @param name Name to be assigned to the port. * @param clock_rate Clock rate/sampling rate. * @param channel_count Number of channels. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Number of bits per sample. * @param options Options (should be zero at the moment). * @param p_slot Pointer to receive the slot index of the port in * the conference bridge. * @param p_port Pointer to receive the port instance. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_conf_add_passive_port( pjmedia_conf *conf, pj_pool_t *pool, const pj_str_t *name, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, unsigned *p_slot, pjmedia_port **p_port ); /** * Change TX and RX settings for the port. * * @param conf The conference bridge. * @param slot Port number/slot in the conference bridge. * @param tx Settings for the transmission TO this port. * @param rx Settings for the receipt FROM this port. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_conf_configure_port( pjmedia_conf *conf, unsigned slot, pjmedia_port_op tx, pjmedia_port_op rx); /** * Enable unidirectional audio from the specified source slot to the * specified sink slot. * * @param conf The conference bridge. * @param src_slot Source slot. * @param sink_slot Sink slot. * @param level This argument is reserved for future improvements * where it is possible to adjust the level of signal * transmitted in a specific connection. For now, * this argument MUST be zero. * * @return PJ_SUCCES on success. */ PJ_DECL(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf, unsigned src_slot, unsigned sink_slot, int level ); /** * Disconnect unidirectional audio from the specified source to the specified * sink slot. * * @param conf The conference bridge. * @param src_slot Source slot. * @param sink_slot Sink slot. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf, unsigned src_slot, unsigned sink_slot ); /** * Get number of ports currently registered to the conference bridge. * * @param conf The conference bridge. * * @return Number of ports currently registered to the conference * bridge. */ PJ_DECL(unsigned) pjmedia_conf_get_port_count(pjmedia_conf *conf); /** * Get total number of ports connections currently set up in the bridge. * * @param conf The conference bridge. * * @return PJ_SUCCESS on success. */ PJ_DECL(unsigned) pjmedia_conf_get_connect_count(pjmedia_conf *conf); /** * Remove the specified port from the conference bridge. * * @param conf The conference bridge. * @param slot The port index to be removed. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf, unsigned slot ); /** * Enumerate occupied ports in the bridge. * * @param conf The conference bridge. * @param ports Array of port numbers to be filled in. * @param count On input, specifies the maximum number of ports * in the array. On return, it will be filled with * the actual number of ports. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_conf_enum_ports( pjmedia_conf *conf, unsigned ports[], unsigned *count ); /** * Get port info. * * @param conf The conference bridge. * @param slot Port index. * @param info Pointer to receive the info. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf, unsigned slot, pjmedia_conf_port_info *info); /** * Get occupied ports info. * * @param conf The conference bridge. * @param size On input, contains maximum number of infos * to be retrieved. On output, contains the actual * number of infos that have been copied. * @param info Array of info. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_conf_get_ports_info(pjmedia_conf *conf, unsigned *size, pjmedia_conf_port_info info[] ); /** * Get last signal level transmitted to or received from the specified port. * This will retrieve the "real-time" signal level of the audio as they are * transmitted or received by the specified port. Application may call this * function periodically to display the signal level to a VU meter. * * The signal level is an integer value in zero to 255, with zero indicates * no signal, and 255 indicates the loudest signal level. * * @param conf The conference bridge. * @param slot Slot number. * @param tx_level Optional argument to receive the level of signal * transmitted to the specified port (i.e. the direction * is from the bridge to the port). * @param rx_level Optional argument to receive the level of signal * received from the port (i.e. the direction is from the * port to the bridge). * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_conf_get_signal_level(pjmedia_conf *conf, unsigned slot, unsigned *tx_level, unsigned *rx_level); /** * Adjust the level of signal received from the specified port. * Application may adjust the level to make signal received from the port * either louder or more quiet. The level adjustment is calculated with this * formula: output = input * (adj_level+128) / 128. Using * this, zero indicates no adjustment, the value -128 will mute the signal, * and the value of +128 will make the signal 100% louder, +256 will make it * 200% louder, etc. * * The level adjustment value will stay with the port until the port is * removed from the bridge or new adjustment value is set. The current * level adjustment value is reported in the media port info when * the #pjmedia_conf_get_port_info() function is called. * * @param conf The conference bridge. * @param slot Slot number of the port. * @param adj_level Adjustment level, which must be greater than or equal * to -128. A value of zero means there is no level * adjustment to be made, the value -128 will mute the * signal, and the value of +128 will make the signal * 100% louder, +256 will make it 200% louder, etc. * See the function description for the formula. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_conf_adjust_rx_level( pjmedia_conf *conf, unsigned slot, int adj_level ); /** * Adjust the level of signal to be transmitted to the specified port. * Application may adjust the level to make signal transmitted to the port * either louder or more quiet. The level adjustment is calculated with this * formula: output = input * (adj_level+128) / 128. Using * this, zero indicates no adjustment, the value -128 will mute the signal, * and the value of +128 will make the signal 100% louder, +256 will make it * 200% louder, etc. * * The level adjustment value will stay with the port until the port is * removed from the bridge or new adjustment value is set. The current * level adjustment value is reported in the media port info when * the #pjmedia_conf_get_port_info() function is called. * * @param conf The conference bridge. * @param slot Slot number of the port. * @param adj_level Adjustment level, which must be greater than or equal * to -128. A value of zero means there is no level * adjustment to be made, the value -128 will mute the * signal, and the value of +128 will make the signal * 100% louder, +256 will make it 200% louder, etc. * See the function description for the formula. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_conf_adjust_tx_level( pjmedia_conf *conf, unsigned slot, int adj_level ); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_CONF_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/config.h ================================================ /* $Id: config.h 4443 2013-03-20 06:56:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CONFIG_H__ #define __PJMEDIA_CONFIG_H__ /** * @file pjmedia/config.h Compile time config * @brief Contains some compile time constants. */ #include /** * @defgroup PJMEDIA_BASE Base Types and Configurations */ /** * @defgroup PJMEDIA_CONFIG Compile time configuration * @ingroup PJMEDIA_BASE * @brief Some compile time configuration settings. * @{ */ /* * Include config_auto.h if autoconf is used (PJ_AUTOCONF is set) */ #if defined(PJ_AUTOCONF) # include #endif /** * Specify whether we prefer to use audio switch board rather than * conference bridge. * * Audio switch board is a kind of simplified version of conference * bridge, but not really the subset of conference bridge. It has * stricter rules on audio routing among the pjmedia ports and has * no audio mixing capability. The power of it is it could work with * encoded audio frames where conference brigde couldn't. * * Default: 0 */ #ifndef PJMEDIA_CONF_USE_SWITCH_BOARD # define PJMEDIA_CONF_USE_SWITCH_BOARD 0 #endif /** * Specify buffer size for audio switch board, in bytes. This buffer will * be used for transmitting/receiving audio frame data (and some overheads, * i.e: pjmedia_frame structure) among conference ports in the audio * switch board. For example, if a port uses PCM format @44100Hz mono * and frame time 20ms, the PCM audio data will require 1764 bytes, * so with overhead, a safe buffer size will be ~1900 bytes. * * Default: PJMEDIA_MAX_MTU */ #ifndef PJMEDIA_CONF_SWITCH_BOARD_BUF_SIZE # define PJMEDIA_CONF_SWITCH_BOARD_BUF_SIZE PJMEDIA_MAX_MTU #endif /* * Types of sound stream backends. */ /** * This macro has been deprecated in releasee 1.1. Please see * http://trac.pjsip.org/repos/wiki/Audio_Dev_API for more information. */ #if defined(PJMEDIA_SOUND_IMPLEMENTATION) # error PJMEDIA_SOUND_IMPLEMENTATION has been deprecated #endif /** * This macro has been deprecated in releasee 1.1. Please see * http://trac.pjsip.org/repos/wiki/Audio_Dev_API for more information. */ #if defined(PJMEDIA_PREFER_DIRECT_SOUND) # error PJMEDIA_PREFER_DIRECT_SOUND has been deprecated #endif /** * This macro controls whether the legacy sound device API is to be * implemented, for applications that still use the old sound device * API (sound.h). If this macro is set to non-zero, the sound_legacy.c * will be included in the compilation. The sound_legacy.c is an * implementation of old sound device (sound.h) using the new Audio * Device API. * * Please see http://trac.pjsip.org/repos/wiki/Audio_Dev_API for more * info. */ #ifndef PJMEDIA_HAS_LEGACY_SOUND_API # define PJMEDIA_HAS_LEGACY_SOUND_API 1 #endif /** * Specify default sound device latency, in milisecond. */ #ifndef PJMEDIA_SND_DEFAULT_REC_LATENCY # define PJMEDIA_SND_DEFAULT_REC_LATENCY 100 #endif /** * Specify default sound device latency, in milisecond. * * Default is 160ms for Windows Mobile and 140ms for other platforms. */ #ifndef PJMEDIA_SND_DEFAULT_PLAY_LATENCY # if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 # define PJMEDIA_SND_DEFAULT_PLAY_LATENCY 160 # else # define PJMEDIA_SND_DEFAULT_PLAY_LATENCY 140 # endif #endif /* * Types of WSOLA backend algorithm. */ /** * This denotes implementation of WSOLA using null algorithm. Expansion * will generate zero frames, and compression will just discard some * samples from the input. * * This type of implementation may be used as it requires the least * processing power. */ #define PJMEDIA_WSOLA_IMP_NULL 0 /** * This denotes implementation of WSOLA using fixed or floating point WSOLA * algorithm. This implementation provides the best quality of the result, * at the expense of one frame delay and intensive processing power * requirement. */ #define PJMEDIA_WSOLA_IMP_WSOLA 1 /** * This denotes implementation of WSOLA algorithm with faster waveform * similarity calculation. This implementation provides fair quality of * the result with the main advantage of low processing power requirement. */ #define PJMEDIA_WSOLA_IMP_WSOLA_LITE 2 /** * Specify type of Waveform based Similarity Overlap and Add (WSOLA) backend * implementation to be used. WSOLA is an algorithm to expand and/or compress * audio frames without changing the pitch, and used by the delaybuf and as PLC * backend algorithm. * * Default is PJMEDIA_WSOLA_IMP_WSOLA */ #ifndef PJMEDIA_WSOLA_IMP # define PJMEDIA_WSOLA_IMP PJMEDIA_WSOLA_IMP_WSOLA #endif /** * Specify the default maximum duration of synthetic audio that is generated * by WSOLA. This value should be long enough to cover burst of packet losses. * but not too long, because as the duration increases the quality would * degrade considerably. * * Note that this limit is only applied when fading is enabled in the WSOLA * session. * * Default: 80 */ #ifndef PJMEDIA_WSOLA_MAX_EXPAND_MSEC # define PJMEDIA_WSOLA_MAX_EXPAND_MSEC 80 #endif /** * Specify WSOLA template length, in milliseconds. The longer the template, * the smoother signal to be generated at the expense of more computation * needed, since the algorithm will have to compare more samples to find * the most similar pitch. * * Default: 5 */ #ifndef PJMEDIA_WSOLA_TEMPLATE_LENGTH_MSEC # define PJMEDIA_WSOLA_TEMPLATE_LENGTH_MSEC 5 #endif /** * Specify WSOLA algorithm delay, in milliseconds. The algorithm delay is * used to merge synthetic samples with real samples in the transition * between real to synthetic and vice versa. The longer the delay, the * smoother signal to be generated, at the expense of longer latency and * a slighty more computation. * * Default: 5 */ #ifndef PJMEDIA_WSOLA_DELAY_MSEC # define PJMEDIA_WSOLA_DELAY_MSEC 5 #endif /** * Set this to non-zero to disable fade-out/in effect in the PLC when it * instructs WSOLA to generate synthetic frames. The use of fading may * or may not improve the quality of audio, depending on the nature of * packet loss and the type of audio input (e.g. speech vs music). * Disabling fading also implicitly remove the maximum limit of synthetic * audio samples generated by WSOLA (see PJMEDIA_WSOLA_MAX_EXPAND_MSEC). * * Default: 0 */ #ifndef PJMEDIA_WSOLA_PLC_NO_FADING # define PJMEDIA_WSOLA_PLC_NO_FADING 0 #endif /** * Limit the number of calls by stream to the PLC to generate synthetic * frames to this duration. If packets are still lost after this maximum * duration, silence will be generated by the stream instead. Since the * PLC normally should have its own limit on the maximum duration of * synthetic frames to be generated (for PJMEDIA's PLC, the limit is * PJMEDIA_WSOLA_MAX_EXPAND_MSEC), we can set this value to a large number * to give additional flexibility should the PLC wants to do something * clever with the lost frames. * * Default: 240 ms */ #ifndef PJMEDIA_MAX_PLC_DURATION_MSEC # define PJMEDIA_MAX_PLC_DURATION_MSEC 240 #endif /** * Specify number of sound buffers. Larger number is better for sound * stability and to accommodate sound devices that are unable to send frames * in timely manner, however it would probably cause more audio delay (and * definitely will take more memory). One individual buffer is normally 10ms * or 20 ms long, depending on ptime settings (samples_per_frame value). * * The setting here currently is used by the conference bridge, the splitter * combiner port, and dsound.c. * * Default: (PJMEDIA_SND_DEFAULT_PLAY_LATENCY+20)/20 */ #ifndef PJMEDIA_SOUND_BUFFER_COUNT # define PJMEDIA_SOUND_BUFFER_COUNT ((PJMEDIA_SND_DEFAULT_PLAY_LATENCY+20)/20) #endif /** * Specify which A-law/U-law conversion algorithm to use. * By default the conversion algorithm uses A-law/U-law table which gives * the best performance, at the expense of 33 KBytes of static data. * If this option is disabled, a smaller but slower algorithm will be used. */ #ifndef PJMEDIA_HAS_ALAW_ULAW_TABLE # define PJMEDIA_HAS_ALAW_ULAW_TABLE 1 #endif /** * Unless specified otherwise, G711 codec is included by default. */ #ifndef PJMEDIA_HAS_G711_CODEC # define PJMEDIA_HAS_G711_CODEC 1 #endif /* * Warn about obsolete macros. * * PJMEDIA_HAS_SMALL_FILTER has been deprecated in 0.7. */ #if defined(PJMEDIA_HAS_SMALL_FILTER) # ifdef _MSC_VER # pragma message("Warning: PJMEDIA_HAS_SMALL_FILTER macro is deprecated"\ " and has no effect") # else # warning "PJMEDIA_HAS_SMALL_FILTER macro is deprecated and has no effect" # endif #endif /* * Warn about obsolete macros. * * PJMEDIA_HAS_LARGE_FILTER has been deprecated in 0.7. */ #if defined(PJMEDIA_HAS_LARGE_FILTER) # ifdef _MSC_VER # pragma message("Warning: PJMEDIA_HAS_LARGE_FILTER macro is deprecated"\ " and has no effect") # else # warning "PJMEDIA_HAS_LARGE_FILTER macro is deprecated" # endif #endif /* * These macros are obsolete in 0.7.1 so it will trigger compilation error. * Please use PJMEDIA_RESAMPLE_IMP to select the resample implementation * to use. */ #ifdef PJMEDIA_HAS_LIBRESAMPLE # error "PJMEDIA_HAS_LIBRESAMPLE macro is deprecated. Use '#define PJMEDIA_RESAMPLE_IMP PJMEDIA_RESAMPLE_LIBRESAMPLE'" #endif #ifdef PJMEDIA_HAS_SPEEX_RESAMPLE # error "PJMEDIA_HAS_SPEEX_RESAMPLE macro is deprecated. Use '#define PJMEDIA_RESAMPLE_IMP PJMEDIA_RESAMPLE_SPEEX'" #endif /* * Sample rate conversion backends. * Select one of these backends in PJMEDIA_RESAMPLE_IMP. */ #define PJMEDIA_RESAMPLE_NONE 1 /**< No resampling. */ #define PJMEDIA_RESAMPLE_LIBRESAMPLE 2 /**< Sample rate conversion using libresample. */ #define PJMEDIA_RESAMPLE_SPEEX 3 /**< Sample rate conversion using Speex. */ #define PJMEDIA_RESAMPLE_LIBSAMPLERATE 4 /**< Sample rate conversion using libsamplerate (a.k.a Secret Rabbit Code) */ /** * Select which resample implementation to use. Currently pjmedia supports: * - #PJMEDIA_RESAMPLE_LIBRESAMPLE, to use libresample-1.7, this is the default * implementation to be used. * - #PJMEDIA_RESAMPLE_LIBSAMPLERATE, to use libsamplerate implementation * (a.k.a. Secret Rabbit Code). * - #PJMEDIA_RESAMPLE_SPEEX, to use experimental sample rate conversion in * Speex library. * - #PJMEDIA_RESAMPLE_NONE, to disable sample rate conversion. Any calls to * resample function will return error. * * Default is PJMEDIA_RESAMPLE_LIBRESAMPLE */ #ifndef PJMEDIA_RESAMPLE_IMP # define PJMEDIA_RESAMPLE_IMP PJMEDIA_RESAMPLE_LIBRESAMPLE #endif /** * Specify whether libsamplerate, when used, should be linked statically * into the application. This option is only useful for Visual Studio * projects, and when this static linking is enabled */ /** * Default file player/writer buffer size. */ #ifndef PJMEDIA_FILE_PORT_BUFSIZE # define PJMEDIA_FILE_PORT_BUFSIZE 4000 #endif /** * Maximum frame duration (in msec) to be supported. * This (among other thing) will affect the size of buffers to be allocated * for outgoing packets. */ #ifndef PJMEDIA_MAX_FRAME_DURATION_MS # define PJMEDIA_MAX_FRAME_DURATION_MS 200 #endif /** * Max packet size for transmitting direction. */ #ifndef PJMEDIA_MAX_MTU # define PJMEDIA_MAX_MTU 1500 #endif /** * Max packet size for receiving direction. */ #ifndef PJMEDIA_MAX_MRU # define PJMEDIA_MAX_MRU 2000 #endif /** * DTMF/telephone-event duration, in timestamp. */ #ifndef PJMEDIA_DTMF_DURATION # define PJMEDIA_DTMF_DURATION 1600 /* in timestamp */ #endif /** * Number of RTP packets received from different source IP address from the * remote address required to make the stream switch transmission * to the source address. */ #ifndef PJMEDIA_RTP_NAT_PROBATION_CNT # define PJMEDIA_RTP_NAT_PROBATION_CNT 10 #endif /** * Number of RTCP packets received from different source IP address from the * remote address required to make the stream switch RTCP transmission * to the source address. */ #ifndef PJMEDIA_RTCP_NAT_PROBATION_CNT # define PJMEDIA_RTCP_NAT_PROBATION_CNT 3 #endif /** * Specify whether RTCP should be advertised in SDP. This setting would * affect whether RTCP candidate will be added in SDP when ICE is used. * Application might want to disable RTCP advertisement in SDP to * reduce the message size. * * Default: 1 (yes) */ #ifndef PJMEDIA_ADVERTISE_RTCP # define PJMEDIA_ADVERTISE_RTCP 1 #endif /** * Interval to send RTCP packets, in msec */ #ifndef PJMEDIA_RTCP_INTERVAL # define PJMEDIA_RTCP_INTERVAL 5000 /* msec*/ #endif /** * Tell RTCP to ignore the first N packets when calculating the * jitter statistics. From experimentation, the first few packets * (25 or so) have relatively big jitter, possibly because during * this time, the program is also busy setting up the signaling, * so they make the average jitter big. * * Default: 25. */ #ifndef PJMEDIA_RTCP_IGNORE_FIRST_PACKETS # define PJMEDIA_RTCP_IGNORE_FIRST_PACKETS 25 #endif /** * Specify whether RTCP statistics includes raw jitter statistics. * Raw jitter is defined as absolute value of network transit time * difference of two consecutive packets; refering to "difference D" * term in interarrival jitter calculation in RFC 3550 section 6.4.1. * * Default: 0 (no). */ #ifndef PJMEDIA_RTCP_STAT_HAS_RAW_JITTER # define PJMEDIA_RTCP_STAT_HAS_RAW_JITTER 0 #endif /** * Specify the factor with wich RTCP RTT statistics should be normalized * if exceptionally high. For e.g. mobile networks with potentially large * fluctuations, this might be unwanted. * * Use (0) to disable this feature. * * Default: 3. */ #ifndef PJMEDIA_RTCP_NORMALIZE_FACTOR # define PJMEDIA_RTCP_NORMALIZE_FACTOR 3 #endif /** * Specify whether RTCP statistics includes IP Delay Variation statistics. * IPDV is defined as network transit time difference of two consecutive * packets. The IPDV statistic can be useful to inspect clock skew existance * and level, e.g: when the IPDV mean values were stable in positive numbers, * then the remote clock (used in sending RTP packets) is faster than local * system clock. Ideally, the IPDV mean values are always equal to 0. * * Default: 0 (no). */ #ifndef PJMEDIA_RTCP_STAT_HAS_IPDV # define PJMEDIA_RTCP_STAT_HAS_IPDV 0 #endif /** * Specify whether RTCP XR support should be built into PJMEDIA. Disabling * this feature will reduce footprint slightly. Note that even when this * setting is enabled, RTCP XR processing will only be performed in stream * if it is enabled on run-time on per stream basis. See * PJMEDIA_STREAM_ENABLE_XR setting for more info. * * Default: 0 (no). */ #ifndef PJMEDIA_HAS_RTCP_XR # define PJMEDIA_HAS_RTCP_XR 0 #endif /** * The RTCP XR feature is activated and used by stream if \a enable_rtcp_xr * field of \a pjmedia_stream_info structure is non-zero. This setting * controls the default value of this field. * * Default: 0 (disabled) */ #ifndef PJMEDIA_STREAM_ENABLE_XR # define PJMEDIA_STREAM_ENABLE_XR 0 #endif /** * Specify the buffer length for storing any received RTCP SDES text * in a stream session. Usually RTCP contains only the mandatory SDES * field, i.e: CNAME. * * Default: 64 bytes. */ #ifndef PJMEDIA_RTCP_RX_SDES_BUF_LEN # define PJMEDIA_RTCP_RX_SDES_BUF_LEN 64 #endif /** * Specify how long (in miliseconds) the stream should suspend the * silence detector/voice activity detector (VAD) during the initial * period of the session. This feature is useful to open bindings in * all NAT routers between local and remote endpoint since most NATs * do not allow incoming packet to get in before local endpoint sends * outgoing packets. * * Specify zero to disable this feature. * * Default: 600 msec (which gives good probability that some RTP * packets will reach the destination, but without * filling up the jitter buffer on the remote end). */ #ifndef PJMEDIA_STREAM_VAD_SUSPEND_MSEC # define PJMEDIA_STREAM_VAD_SUSPEND_MSEC 600 #endif /** * Perform RTP payload type checking in the stream. Normally the peer * MUST send RTP with payload type as we specified in our SDP. Certain * agents may not be able to follow this hence the only way to have * communication is to disable this check. * * Default: 1 */ #ifndef PJMEDIA_STREAM_CHECK_RTP_PT # define PJMEDIA_STREAM_CHECK_RTP_PT 1 #endif /** * Reserve some space for application extra data, e.g: SRTP auth tag, * in RTP payload, so the total payload length will not exceed the MTU. */ #ifndef PJMEDIA_STREAM_RESV_PAYLOAD_LEN # define PJMEDIA_STREAM_RESV_PAYLOAD_LEN 20 #endif /** * Specify the maximum duration of silence period in the codec, in msec. * This is useful for example to keep NAT binding open in the firewall * and to prevent server from disconnecting the call because no * RTP packet is received. * * This only applies to codecs that use PJMEDIA's VAD (pretty much * everything including iLBC, except Speex, which has its own DTX * mechanism). * * Use (-1) to disable this feature. * * Default: 5000 ms * */ #ifndef PJMEDIA_CODEC_MAX_SILENCE_PERIOD # define PJMEDIA_CODEC_MAX_SILENCE_PERIOD 5000 #endif /** * Suggested or default threshold to be set for fixed silence detection * or as starting threshold for adaptive silence detection. The threshold * has the range from zero to 0xFFFF. */ #ifndef PJMEDIA_SILENCE_DET_THRESHOLD # define PJMEDIA_SILENCE_DET_THRESHOLD 4 #endif /** * Maximum silence threshold in the silence detector. The silence detector * will not cut the audio transmission if the audio level is above this * level. * * Use 0x10000 (or greater) to disable this feature. * * Default: 0x10000 (disabled) */ #ifndef PJMEDIA_SILENCE_DET_MAX_THRESHOLD # define PJMEDIA_SILENCE_DET_MAX_THRESHOLD 0x10000 #endif /** * Speex Accoustic Echo Cancellation (AEC). * By default is enabled. */ #ifndef PJMEDIA_HAS_SPEEX_AEC # define PJMEDIA_HAS_SPEEX_AEC 1 #endif /** * Specify whether Automatic Gain Control (AGC) should also be enabled in * Speex AEC. * * Default: 1 (yes) */ #ifndef PJMEDIA_SPEEX_AEC_USE_AGC # define PJMEDIA_SPEEX_AEC_USE_AGC 1 #endif /** * Specify whether denoise should also be enabled in Speex AEC. * * Default: 1 (yes) */ #ifndef PJMEDIA_SPEEX_AEC_USE_DENOISE # define PJMEDIA_SPEEX_AEC_USE_DENOISE 1 #endif /** * Maximum number of parameters in SDP fmtp attribute. * * Default: 16 */ #ifndef PJMEDIA_CODEC_MAX_FMTP_CNT # define PJMEDIA_CODEC_MAX_FMTP_CNT 16 #endif /** * This specifies the behavior of the SDP negotiator when responding to an * offer, whether it should rather use the codec preference as set by * remote, or should it rather use the codec preference as specified by * local endpoint. * * For example, suppose incoming call has codec order "8 0 3", while * local codec order is "3 0 8". If remote codec order is preferable, * the selected codec will be 8, while if local codec order is preferable, * the selected codec will be 3. * * If set to non-zero, the negotiator will use the codec order as specified * by remote in the offer. * * Note that this behavior can be changed during run-time by calling * pjmedia_sdp_neg_set_prefer_remote_codec_order(). * * Default is 1 (to maintain backward compatibility) */ #ifndef PJMEDIA_SDP_NEG_PREFER_REMOTE_CODEC_ORDER # define PJMEDIA_SDP_NEG_PREFER_REMOTE_CODEC_ORDER 1 #endif /** * This specifies the behavior of the SDP negotiator when responding to an * offer, whether it should answer with multiple formats or not. * * Note that this behavior can be changed during run-time by calling * pjmedia_sdp_neg_set_allow_multiple_codecs(). * * Default is 0 (to maintain backward compatibility) */ #ifndef PJMEDIA_SDP_NEG_ANSWER_MULTIPLE_CODECS # define PJMEDIA_SDP_NEG_ANSWER_MULTIPLE_CODECS 0 #endif /** * This specifies the maximum number of the customized SDP format * negotiation callbacks. */ #ifndef PJMEDIA_SDP_NEG_MAX_CUSTOM_FMT_NEG_CB # define PJMEDIA_SDP_NEG_MAX_CUSTOM_FMT_NEG_CB 8 #endif /** * This specifies if the SDP negotiator should rewrite answer payload * type numbers to use the same payload type numbers as the remote offer * for all matched codecs. * * Default is 1 (yes) */ #ifndef PJMEDIA_SDP_NEG_ANSWER_SYMMETRIC_PT # define PJMEDIA_SDP_NEG_ANSWER_SYMMETRIC_PT 1 #endif /** * Support for sending and decoding RTCP port in SDP (RFC 3605). * Default is equal to PJMEDIA_ADVERTISE_RTCP setting. */ #ifndef PJMEDIA_HAS_RTCP_IN_SDP # define PJMEDIA_HAS_RTCP_IN_SDP (PJMEDIA_ADVERTISE_RTCP) #endif /** * This macro controls whether pjmedia should include SDP * bandwidth modifier "TIAS" (RFC3890). * * Note that there is also a run-time variable to turn this setting * on or off, defined in endpoint.c. To access this variable, use * the following construct * \verbatim extern pj_bool_t pjmedia_add_bandwidth_tias_in_sdp; // Do not enable bandwidth information inclusion in sdp pjmedia_add_bandwidth_tias_in_sdp = PJ_FALSE; \endverbatim * * Default: 1 (yes) */ #ifndef PJMEDIA_ADD_BANDWIDTH_TIAS_IN_SDP # define PJMEDIA_ADD_BANDWIDTH_TIAS_IN_SDP 1 #endif /** * This macro controls whether pjmedia should include SDP rtpmap * attribute for static payload types. SDP rtpmap for static * payload types are optional, although they are normally included * for interoperability reason. * * Note that there is also a run-time variable to turn this setting * on or off, defined in endpoint.c. To access this variable, use * the following construct * \verbatim extern pj_bool_t pjmedia_add_rtpmap_for_static_pt; // Do not include rtpmap for static payload types (<96) pjmedia_add_rtpmap_for_static_pt = PJ_FALSE; \endverbatim * * Default: 1 (yes) */ #ifndef PJMEDIA_ADD_RTPMAP_FOR_STATIC_PT # define PJMEDIA_ADD_RTPMAP_FOR_STATIC_PT 1 #endif /** * This macro declares the payload type for telephone-event * that is advertised by PJMEDIA for outgoing SDP. If this macro * is set to zero, telephone events would not be advertised nor * supported. * * If this value is changed to other number, please update the * PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR too. */ #ifndef PJMEDIA_RTP_PT_TELEPHONE_EVENTS # define PJMEDIA_RTP_PT_TELEPHONE_EVENTS 96 #endif /** * Macro to get the string representation of the telephone-event * payload type. */ #ifndef PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR # define PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR "96" #endif /** * Maximum tones/digits that can be enqueued in the tone generator. */ #ifndef PJMEDIA_TONEGEN_MAX_DIGITS # define PJMEDIA_TONEGEN_MAX_DIGITS 32 #endif /* * Below specifies the various tone generator backend algorithm. */ /** * The math's sine(), floating point. This has very good precision * but it's the slowest and requires floating point support and * linking with the math library. */ #define PJMEDIA_TONEGEN_SINE 1 /** * Floating point approximation of sine(). This has relatively good * precision and much faster than plain sine(), but it requires floating- * point support and linking with the math library. */ #define PJMEDIA_TONEGEN_FLOATING_POINT 2 /** * Fixed point using sine signal generated by Cordic algorithm. This * algorithm can be tuned to provide balance between precision and * performance by tuning the PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP * setting, and may be suitable for platforms that lack floating-point * support. */ #define PJMEDIA_TONEGEN_FIXED_POINT_CORDIC 3 /** * Fast fixed point using some approximation to generate sine waves. * The tone generated by this algorithm is not very precise, however * the algorithm is very fast. */ #define PJMEDIA_TONEGEN_FAST_FIXED_POINT 4 /** * Specify the tone generator algorithm to be used. Please see * http://trac.pjsip.org/repos/wiki/Tone_Generator for the performance * analysis results of the various tone generator algorithms. * * Default value: * - PJMEDIA_TONEGEN_FLOATING_POINT when PJ_HAS_FLOATING_POINT is set * - PJMEDIA_TONEGEN_FIXED_POINT_CORDIC when PJ_HAS_FLOATING_POINT is not set */ #ifndef PJMEDIA_TONEGEN_ALG # if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT # define PJMEDIA_TONEGEN_ALG PJMEDIA_TONEGEN_FLOATING_POINT # else # define PJMEDIA_TONEGEN_ALG PJMEDIA_TONEGEN_FIXED_POINT_CORDIC # endif #endif /** * Specify the number of calculation loops to generate the tone, when * PJMEDIA_TONEGEN_FIXED_POINT_CORDIC algorithm is used. With more calculation * loops, the tone signal gets more precise, but this will add more * processing. * * Valid values are 1 to 28. * * Default value: 10 */ #ifndef PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP # define PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP 10 #endif /** * Enable high quality of tone generation, the better quality will cost * more CPU load. This is only applied to floating point enabled machines. * * By default it is enabled when PJ_HAS_FLOATING_POINT is set. * * This macro has been deprecated in version 1.0-rc3. */ #ifdef PJMEDIA_USE_HIGH_QUALITY_TONEGEN # error "The PJMEDIA_USE_HIGH_QUALITY_TONEGEN macro is obsolete" #endif /** * Fade-in duration for the tone, in milliseconds. Set to zero to disable * this feature. * * Default: 1 (msec) */ #ifndef PJMEDIA_TONEGEN_FADE_IN_TIME # define PJMEDIA_TONEGEN_FADE_IN_TIME 1 #endif /** * Fade-out duration for the tone, in milliseconds. Set to zero to disable * this feature. * * Default: 2 (msec) */ #ifndef PJMEDIA_TONEGEN_FADE_OUT_TIME # define PJMEDIA_TONEGEN_FADE_OUT_TIME 2 #endif /** * The default tone generator amplitude (1-32767). * * Default value: 12288 */ #ifndef PJMEDIA_TONEGEN_VOLUME # define PJMEDIA_TONEGEN_VOLUME 12288 #endif /** * Enable support for SRTP media transport. This will require linking * with libsrtp from the third_party directory. * * By default it is enabled. */ #ifndef PJMEDIA_HAS_SRTP # define PJMEDIA_HAS_SRTP 1 #endif /** * Let the library handle libsrtp initialization and deinitialization. * Application may want to disable this and manually perform libsrtp * initialization and deinitialization when it needs to use libsrtp * before the library is initialized or after the library is shutdown. * * By default it is enabled. */ #ifndef PJMEDIA_LIBSRTP_AUTO_INIT_DEINIT # define PJMEDIA_LIBSRTP_AUTO_INIT_DEINIT 1 #endif /** * Enable support to handle codecs with inconsistent clock rate * between clock rate in SDP/RTP & the clock rate that is actually used. * This happens for example with G.722 and MPEG audio codecs. * See: * - G.722 : RFC 3551 4.5.2 * - MPEG audio : RFC 3551 4.5.13 & RFC 3119 * * Also when this feature is enabled, some handling will be performed * to deal with clock rate incompatibilities of some phones. * * By default it is enabled. */ #ifndef PJMEDIA_HANDLE_G722_MPEG_BUG # define PJMEDIA_HANDLE_G722_MPEG_BUG 1 #endif /** * Transport info (pjmedia_transport_info) contains a socket info and list * of transport specific info, since transports can be chained together * (for example, SRTP transport uses UDP transport as the underlying * transport). This constant specifies maximum number of transport specific * infos that can be held in a transport info. */ #ifndef PJMEDIA_TRANSPORT_SPECIFIC_INFO_MAXCNT # define PJMEDIA_TRANSPORT_SPECIFIC_INFO_MAXCNT 4 #endif /** * Maximum size in bytes of storage buffer of a transport specific info. */ #ifndef PJMEDIA_TRANSPORT_SPECIFIC_INFO_MAXSIZE # define PJMEDIA_TRANSPORT_SPECIFIC_INFO_MAXSIZE (36*sizeof(long)) #endif /** * Value to be specified in PJMEDIA_STREAM_ENABLE_KA setting. * This indicates that an empty RTP packet should be used as * the keep-alive packet. */ #define PJMEDIA_STREAM_KA_EMPTY_RTP 1 /** * Value to be specified in PJMEDIA_STREAM_ENABLE_KA setting. * This indicates that a user defined packet should be used * as the keep-alive packet. The content of the user-defined * packet is specified by PJMEDIA_STREAM_KA_USER_PKT. Default * content is a CR-LF packet. */ #define PJMEDIA_STREAM_KA_USER 2 /** * The content of the user defined keep-alive packet. The format * of the packet is initializer to pj_str_t structure. Note that * the content may contain NULL character. */ #ifndef PJMEDIA_STREAM_KA_USER_PKT # define PJMEDIA_STREAM_KA_USER_PKT { "\r\n", 2 } #endif /** * Specify another type of keep-alive and NAT hole punching * mechanism (the other type is PJMEDIA_STREAM_VAD_SUSPEND_MSEC * and PJMEDIA_CODEC_MAX_SILENCE_PERIOD) to be used by stream. * When this feature is enabled, the stream will initially * transmit one packet to punch a hole in NAT, and periodically * transmit keep-alive packets. * * When this alternative keep-alive mechanism is used, application * may disable the other keep-alive mechanisms, i.e: by setting * PJMEDIA_STREAM_VAD_SUSPEND_MSEC to zero and * PJMEDIA_CODEC_MAX_SILENCE_PERIOD to -1. * * The value of this macro specifies the type of packet used * for the keep-alive mechanism. Valid values are * PJMEDIA_STREAM_KA_EMPTY_RTP and PJMEDIA_STREAM_KA_USER. * * The duration of the keep-alive interval further can be set * with PJMEDIA_STREAM_KA_INTERVAL setting. * * Default: 0 (disabled) */ #ifndef PJMEDIA_STREAM_ENABLE_KA # define PJMEDIA_STREAM_ENABLE_KA 0 #endif /** * Specify the keep-alive interval of PJMEDIA_STREAM_ENABLE_KA * mechanism, in seconds. * * Default: 5 seconds */ #ifndef PJMEDIA_STREAM_KA_INTERVAL # define PJMEDIA_STREAM_KA_INTERVAL 5 #endif /* * .... new stuffs ... */ /* * Video */ /** * Top level option to enable/disable video features. * * Default: 1 (enabled) */ #ifndef PJMEDIA_HAS_VIDEO # define PJMEDIA_HAS_VIDEO 1 #endif /** * Specify if FFMPEG is available. The value here will be used as the default * value for other FFMPEG settings below. * * Default: 0 */ #ifndef PJMEDIA_HAS_FFMPEG # define PJMEDIA_HAS_FFMPEG 0 #endif /** * Specify if FFMPEG libavformat is available. * * Default: PJMEDIA_HAS_FFMPEG (or detected by configure) */ #ifndef PJMEDIA_HAS_LIBAVFORMAT # define PJMEDIA_HAS_LIBAVFORMAT PJMEDIA_HAS_FFMPEG #endif /** * Specify if FFMPEG libavformat is available. * * Default: PJMEDIA_HAS_FFMPEG (or detected by configure) */ #ifndef PJMEDIA_HAS_LIBAVCODEC # define PJMEDIA_HAS_LIBAVCODEC PJMEDIA_HAS_FFMPEG #endif /** * Specify if FFMPEG libavutil is available. * * Default: PJMEDIA_HAS_FFMPEG (or detected by configure) */ #ifndef PJMEDIA_HAS_LIBAVUTIL # define PJMEDIA_HAS_LIBAVUTIL PJMEDIA_HAS_FFMPEG #endif /** * Specify if FFMPEG libswscale is available. * * Default: PJMEDIA_HAS_FFMPEG (or detected by configure) */ #ifndef PJMEDIA_HAS_LIBSWSCALE # define PJMEDIA_HAS_LIBSWSCALE PJMEDIA_HAS_FFMPEG #endif /** * Specify if FFMPEG libavcore is available. * * Default: PJMEDIA_HAS_FFMPEG (or detected by configure) */ #ifndef PJMEDIA_HAS_LIBAVCORE # define PJMEDIA_HAS_LIBAVCORE PJMEDIA_HAS_FFMPEG #endif /** * Specify if libvpx is available. * * Default: 0 (or detected by configure) */ #ifndef PJMEDIA_HAS_LIBVPX # define PJMEDIA_HAS_LIBVPX 0 #endif /** * Maximum video planes. * * Default: 4 */ #ifndef PJMEDIA_MAX_VIDEO_PLANES # define PJMEDIA_MAX_VIDEO_PLANES 4 #endif /** * Maximum number of video formats. * * Default: 32 */ #ifndef PJMEDIA_MAX_VIDEO_FORMATS # define PJMEDIA_MAX_VIDEO_FORMATS 32 #endif /** * Specify the maximum time difference (in ms) for synchronization between * two medias. If the synchronization media source is ahead of time * greater than this duration, it is considered to make a very large jump * and the synchronization will be reset. * * Default: 20000 */ #ifndef PJMEDIA_CLOCK_SYNC_MAX_SYNC_MSEC # define PJMEDIA_CLOCK_SYNC_MAX_SYNC_MSEC 20000 #endif /** * Maximum video frame size. * Default: 128kB */ #ifndef PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE # define PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE (1<<17) #endif /** * Specify the maximum duration (in ms) for resynchronization. When a media * is late to another media it is supposed to be synchronized to, it is * guaranteed to be synchronized again after this duration. While if the * media is ahead/early by t ms, it is guaranteed to be synchronized after * t + this duration. This timing only applies if there is no additional * resynchronization required during the specified duration. * * Default: 2000 */ #ifndef PJMEDIA_CLOCK_SYNC_MAX_RESYNC_DURATION # define PJMEDIA_CLOCK_SYNC_MAX_RESYNC_DURATION 2000 #endif /** * Minimum gap between two consecutive discards in jitter buffer, * in milliseconds. * * Default: 200 ms */ #ifndef PJMEDIA_JBUF_DISC_MIN_GAP # define PJMEDIA_JBUF_DISC_MIN_GAP 200 #endif /** * Minimum burst level reference used for calculating discard duration * in jitter buffer progressive discard algorithm, in frames. * * Default: 1 frame */ #ifndef PJMEDIA_JBUF_PRO_DISC_MIN_BURST # define PJMEDIA_JBUF_PRO_DISC_MIN_BURST 1 #endif /** * Maximum burst level reference used for calculating discard duration * in jitter buffer progressive discard algorithm, in frames. * * Default: 200 frames */ #ifndef PJMEDIA_JBUF_PRO_DISC_MAX_BURST # define PJMEDIA_JBUF_PRO_DISC_MAX_BURST 100 #endif /** * Duration for progressive discard algotithm in jitter buffer to discard * an excessive frame when burst is equal to or lower than * PJMEDIA_JBUF_PRO_DISC_MIN_BURST, in milliseconds. * * Default: 2000 ms */ #ifndef PJMEDIA_JBUF_PRO_DISC_T1 # define PJMEDIA_JBUF_PRO_DISC_T1 2000 #endif /** * Duration for progressive discard algotithm in jitter buffer to discard * an excessive frame when burst is equal to or greater than * PJMEDIA_JBUF_PRO_DISC_MAX_BURST, in milliseconds. * * Default: 10000 ms */ #ifndef PJMEDIA_JBUF_PRO_DISC_T2 # define PJMEDIA_JBUF_PRO_DISC_T2 10000 #endif /** * Video stream will discard old picture from the jitter buffer as soon as * new picture is received, to reduce latency. * * Default: 0 */ #ifndef PJMEDIA_VID_STREAM_SKIP_PACKETS_TO_REDUCE_LATENCY # define PJMEDIA_VID_STREAM_SKIP_PACKETS_TO_REDUCE_LATENCY 0 #endif /** * Maximum video payload size. Note that this must not be greater than * PJMEDIA_MAX_MTU. * * Default: (PJMEDIA_MAX_MTU - 100) */ #ifndef PJMEDIA_MAX_VID_PAYLOAD_SIZE # define PJMEDIA_MAX_VID_PAYLOAD_SIZE (PJMEDIA_MAX_MTU - 100) #endif /** * Specify target value for socket receive buffer size. It will be * applied to RTP socket of media transport using setsockopt(). When * transport failed to set the specified size, it will try with lower * value until the highest possible is successfully set. * * Setting this to zero will leave the socket receive buffer size to * OS default (e.g: usually 8 KB on desktop platforms). * * Default: 64 KB when video is enabled, otherwise zero (OS default) */ #ifndef PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE # if PJMEDIA_HAS_VIDEO # define PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE (64*1024) # else # define PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE 0 # endif #endif /** * Specify target value for socket send buffer size. It will be * applied to RTP socket of media transport using setsockopt(). When * transport failed to set the specified size, it will try with lower * value until the highest possible is successfully set. * * Setting this to zero will leave the socket send buffer size to * OS default (e.g: usually 8 KB on desktop platforms). * * Default: 64 KB when video is enabled, otherwise zero (OS default) */ #ifndef PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE # if PJMEDIA_HAS_VIDEO # define PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE (64*1024) # else # define PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE 0 # endif #endif /** * Specify if libyuv is available. * * Default: 0 (disable) */ #ifndef PJMEDIA_HAS_LIBYUV # define PJMEDIA_HAS_LIBYUV 0 #endif /** * Specify if dtmf flash in RFC 2833 is available. */ #ifndef PJMEDIA_HAS_DTMF_FLASH # define PJMEDIA_HAS_DTMF_FLASH 1 #endif /** * @} */ #endif /* __PJMEDIA_CONFIG_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/config_auto.h.in ================================================ /* $Id: config_auto.h.in 3295 2010-08-25 12:51:29Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CONFIG_AUTO_H_ #define __PJMEDIA_CONFIG_AUTO_H_ /** * @file config_auto.h * @brief PJMEDIA configuration as set by autoconf script */ /* * Note: * The configuration in config_site.h overrides any other settings, * including the setting as detected by autoconf. */ /* G711 codec */ #ifndef PJMEDIA_HAS_G711_CODEC #undef PJMEDIA_HAS_G711_CODEC #endif #endif /* __PJMEDIA_CONFIG_AUTO_H_ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/converter.h ================================================ /* $Id: converter.h 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CONVERTER_H__ #define __PJMEDIA_CONVERTER_H__ /** * @file pjmedia/converter.h Format conversion utilities * @brief Format conversion utilities */ #include #include #include #include /** * @defgroup PJMEDIA_CONVERTER Format converter * @ingroup PJMEDIA_FRAME_OP * @brief Audio and video converter utilities * @{ */ PJ_BEGIN_DECL /** * This describes conversion parameter. It specifies the source and * destination formats of the conversion. */ typedef struct pjmedia_conversion_param { pjmedia_format src; /**< Source format. */ pjmedia_format dst; /**< Destination format. */ } pjmedia_conversion_param; /** Forward declaration of factory operation structure */ typedef struct pjmedia_converter_factory_op pjmedia_converter_factory_op; /** * Converter priority guides. Converter priority determines which converter * instance to be used if more than one converters are able to perform the * requested conversion. Converter implementor can use this value to order * the preference based on attributes such as quality or performance. Higher * number indicates higher priority. */ typedef enum pjmedia_converter_priority_guide { /** Lowest priority. */ PJMEDIA_CONVERTER_PRIORITY_LOWEST = 0, /** Normal priority. */ PJMEDIA_CONVERTER_PRIORITY_NORMAL = 15000, /** Highest priority. */ PJMEDIA_CONVERTER_PRIORITY_HIGHEST = 32000 } pjmedia_converter_priority_guide; /** * Converter factory. The converter factory registers a callback function * to create converters. */ typedef struct pjmedia_converter_factory { /** * Standard list members. */ PJ_DECL_LIST_MEMBER(struct pjmedia_converter_factory); /** * Factory name. */ const char *name; /** * Converter priority determines which converter instance to be used if * more than one converters are able to perform the requested conversion. * Converter implementor can use this value to order the preference based * on attributes such as quality or performance. Higher number indicates * higher priority. The pjmedia_converter_priority_guide enumeration shall * be used as the base value to set the priority. */ int priority; /** * Pointer to factory operation. */ pjmedia_converter_factory_op *op; } pjmedia_converter_factory; /** Forward declaration for converter operation. */ typedef struct pjmedia_converter_op pjmedia_converter_op; /** * This structure describes a converter instance. */ typedef struct pjmedia_converter { /** * Pointer to converter operation. */ pjmedia_converter_op *op; } pjmedia_converter; /** * Converter factory operation. */ struct pjmedia_converter_factory_op { /** * This function creates a converter with the specified conversion format, * if such format is supported. * * @param cf The converter factory. * @param pool Pool to allocate memory from. * @param prm Conversion parameter. * @param p_cv Pointer to hold the created converter instance. * * @return PJ_SUCCESS if converter has been created successfully. */ pj_status_t (*create_converter)(pjmedia_converter_factory *cf, pj_pool_t *pool, const pjmedia_conversion_param *prm, pjmedia_converter **p_cv); /** * Destroy the factory. * * @param cf The converter factory. */ void (*destroy_factory)(pjmedia_converter_factory *cf); }; /** * Converter operation. */ struct pjmedia_converter_op { /** * Convert the buffer in the source frame and save the result in the * buffer of the destination frame, according to conversion format that * was specified when the converter was created. * * Note that application should use #pjmedia_converter_convert() instead * of calling this function directly. * * @param cv The converter instance. * @param src_frame The source frame. * @param dst_frame The destination frame. * * @return PJ_SUCCESS if conversion has been performed * successfully. */ pj_status_t (*convert)(pjmedia_converter *cv, pjmedia_frame *src_frame, pjmedia_frame *dst_frame); /** * Destroy the converter instance. * * Note that application should use #pjmedia_converter_destroy() instead * of calling this function directly. * * @param cv The converter. */ void (*destroy)(pjmedia_converter *cv); }; /** * Opaque data type for conversion manager. Typically, the conversion manager * is a singleton instance, although application may instantiate more than one * instances of this if required. */ typedef struct pjmedia_converter_mgr pjmedia_converter_mgr; /** * Create a new conversion manager instance. This will also set the pointer * to the singleton instance if the value is still NULL. * * @param pool Pool to allocate memory from. * @param mgr Pointer to hold the created instance of the * conversion manager. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_converter_mgr_create(pj_pool_t *pool, pjmedia_converter_mgr **mgr); /** * Get the singleton instance of the conversion manager. * * @return The instance. */ PJ_DECL(pjmedia_converter_mgr*) pjmedia_converter_mgr_instance(void); /** * Manually assign a specific video manager instance as the singleton * instance. Normally this is not needed if only one instance is ever * going to be created, as the library automatically assign the singleton * instance. * * @param mgr The instance to be used as the singleton instance. * Application may specify NULL to clear the singleton * singleton instance. */ PJ_DECL(void) pjmedia_converter_mgr_set_instance(pjmedia_converter_mgr *mgr); /** * Destroy a converter manager. If the manager happens to be the singleton * instance, the singleton instance will be set to NULL. * * @param mgr The converter manager. Specify NULL to use * the singleton instance. */ PJ_DECL(void) pjmedia_converter_mgr_destroy(pjmedia_converter_mgr *mgr); /** * Register a converter factory to the converter manager. * * @param mgr The converter manager. Specify NULL to use * the singleton instance. * @param f The converter factory to be registered. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_converter_mgr_register_factory(pjmedia_converter_mgr *mgr, pjmedia_converter_factory *f); /** * Unregister a previously registered converter factory from the converter * manager. * * @param mgr The converter manager. Specify NULL to use * the singleton instance. * @param f The converter factory to be unregistered. * @param call_destroy If this is set to non-zero, the \a destroy_factory() * callback of the factory will be called while * unregistering the factory from the manager. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_converter_mgr_unregister_factory(pjmedia_converter_mgr *mgr, pjmedia_converter_factory *f, pj_bool_t call_destroy); /** * Create a converter instance to perform the specified format conversion * as specified in \a param. * * @param mgr The converter manager. Specify NULL to use * the singleton instance. * @param pool Pool to allocate the memory from. * @param param Conversion parameter. * @param p_cv Pointer to hold the created converter. * * @return PJ_SUCCESS if a converter has been created successfully * or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_converter_create(pjmedia_converter_mgr *mgr, pj_pool_t *pool, pjmedia_conversion_param *param, pjmedia_converter **p_cv); /** * Convert the buffer in the source frame and save the result in the * buffer of the destination frame, according to conversion format that * was specified when the converter was created. * * @param cv The converter instance. * @param src_frame The source frame. * @param dst_frame The destination frame. * * @return PJ_SUCCESS if conversion has been performed * successfully. */ PJ_DECL(pj_status_t) pjmedia_converter_convert(pjmedia_converter *cv, pjmedia_frame *src_frame, pjmedia_frame *dst_frame); /** * Destroy the converter. * * @param cv The converter instance. */ PJ_DECL(void) pjmedia_converter_destroy(pjmedia_converter *cv); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_CONVERTER_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/delaybuf.h ================================================ /* $Id: delaybuf.h 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_DELAYBUF_H__ #define __PJMEDIA_DELAYBUF_H__ /** * @file delaybuf.h * @brief Delay Buffer. */ #include /** * @defgroup PJMED_DELAYBUF Adaptive Delay Buffer * @ingroup PJMEDIA_FRAME_OP * @brief Adaptive delay buffer with high-quality time-scale * modification * @{ * * This section describes PJMEDIA's implementation of delay buffer. * Delay buffer works quite similarly like a fixed jitter buffer, that * is it will delay the frame retrieval by some interval so that caller * will get continuous frame from the buffer. This can be useful when * the put() and get() operations are not evenly interleaved, for example * when caller performs burst of put() operations and then followed by * burst of get() operations. With using this delay buffer, the buffer * will put the burst frames into a buffer so that get() operations * will always get a frame from the buffer (assuming that the number of * get() and put() are matched). * * The buffer is adaptive, that is it continuously learns the optimal delay * to be applied to the audio flow at run-time. Once the optimal delay has * been learned, the delay buffer will apply this delay to the audio flow, * expanding or shrinking the audio samples as necessary when the actual * audio samples in the buffer are too low or too high. It does this without * distorting the audio quality of the audio, by using \a PJMED_WSOLA. * * The delay buffer is used in \ref PJMED_SND_PORT, \ref PJMEDIA_SPLITCOMB, * and \ref PJMEDIA_CONF. */ PJ_BEGIN_DECL /** Opaque declaration for delay buffer. */ typedef struct pjmedia_delay_buf pjmedia_delay_buf; /** * Delay buffer options. */ typedef enum pjmedia_delay_buf_flag { /** * Use simple FIFO mechanism for the delay buffer, i.e. * without WSOLA for expanding and shrinking audio samples. */ PJMEDIA_DELAY_BUF_SIMPLE_FIFO = 1 } pjmedia_delay_buf_flag; /** * Create the delay buffer. Once the delay buffer is created, it will * enter learning state unless the delay argument is specified, which * in this case it will directly enter the running state. * * @param pool Pool where the delay buffer will be allocated * from. * @param name Optional name for the buffer for log * identification. * @param clock_rate Number of samples processed per second. * @param samples_per_frame Number of samples per frame. * @param channel_count Number of channel per frame. * @param max_delay Maximum number of delay to be accommodated, * in ms, if this value is negative or less than * one frame time, default maximum delay used is * 400 ms. * @param options Options. If PJMEDIA_DELAY_BUF_SIMPLE_FIFO is * specified, then a simple FIFO mechanism * will be used instead of the adaptive * implementation (which uses WSOLA to expand * or shrink audio samples). * See #pjmedia_delay_buf_flag for other options. * @param p_b Pointer to receive the delay buffer instance. * * @return PJ_SUCCESS if the delay buffer has been * created successfully, otherwise the appropriate * error will be returned. */ PJ_DECL(pj_status_t) pjmedia_delay_buf_create(pj_pool_t *pool, const char *name, unsigned clock_rate, unsigned samples_per_frame, unsigned channel_count, unsigned max_delay, unsigned options, pjmedia_delay_buf **p_b); /** * Put one frame into the buffer. * * @param b The delay buffer. * @param frame Frame to be put into the buffer. This frame * must have samples_per_frame length. * * @return PJ_SUCCESS if frames can be put successfully. * PJ_EPENDING if the buffer is still at learning * state. PJ_ETOOMANY if the number of frames * will exceed maximum delay level, which in this * case the new frame will overwrite the oldest * frame in the buffer. */ PJ_DECL(pj_status_t) pjmedia_delay_buf_put(pjmedia_delay_buf *b, pj_int16_t frame[]); /** * Get one frame from the buffer. * * @param b The delay buffer. * @param frame Buffer to receive the frame from the delay * buffer. * * @return PJ_SUCCESS if frame has been copied successfully. * PJ_EPENDING if no frame is available, either * because the buffer is still at learning state or * no buffer is available during running state. * On non-successful return, the frame will be * filled with zeroes. */ PJ_DECL(pj_status_t) pjmedia_delay_buf_get(pjmedia_delay_buf *b, pj_int16_t frame[]); /** * Reset delay buffer. This will clear the buffer's content. But keep * the learning result. * * @param b The delay buffer. * * @return PJ_SUCCESS on success or the appropriate error. */ PJ_DECL(pj_status_t) pjmedia_delay_buf_reset(pjmedia_delay_buf *b); /** * Destroy delay buffer. * * @param b Delay buffer session. * * @return PJ_SUCCESS normally. */ PJ_DECL(pj_status_t) pjmedia_delay_buf_destroy(pjmedia_delay_buf *b); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_DELAYBUF_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/doxygen.h ================================================ /* $Id: doxygen.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_DOXYGEN_H__ #define __PJMEDIA_DOXYGEN_H__ /** * @file doxygen.h * @brief Doxygen's mainpage. */ /*////////////////////////////////////////////////////////////////////////// */ /* INTRODUCTION PAGE */ /** * @mainpage PJMEDIA * * \n * @section intro2 Introduction to PJMEDIA * * PJMEDIA is a fully featured media stack, distributed under Open Source/GPL * terms, and featuring small footprint and good extensibility and excellent * portability. Here are some brief overview of PJMEDIA benefits. * * @subsection benefit Benefits * @subsubsection full_feature Many Features * PJMEDIA has many features, and rather than to list them all here, please * see the Modules page for more info. * * Video is planned to arrive at version 2. * * @subsubsection portable Excellent Portability * It's been ported to all desktop systems and many mobile platforms including * Symbian, Windows Mobile, iPhone, and Android. Thanks to its zero thread * design, users have been able to run PJMEDIA on deeply embedded platforms, * even without operating systems (those typically found in DSP platforms). * Except the echo suppressor, all other PJMEDIA components have fixed point * implementation, which makes it ideal for embedded systems which lack FPU. * PJMEDIA also has tiny footprint, as explained below * * @subsubsection footprint Tiny Footprint * Lets not talk about less meaningful and potentially misleading term such as * core footprint, and instead here is the footprint of all components * typically used to build a full streaming media: * * \verbatim Category Component text data bss dec filename ------------------------------------------------------------------------------- Core Error subsystem 135 0 0 135 errno.o Core Endpoint 4210 4 4 4218 endpoint.o Core Port framework 652 0 0 652 port.o Core Codec framework 6257 0 0 6257 codec.o Codec Alaw/ulaw conv. 1060 16 0 1076 alaw_ulaw.o Codec G.711 3298 128 96 3522 g711.o Codec PLC 883 24 0 907 plc_common.o Codec PLC 7130 0 0 7130 wsola.o Session Stream 12222 0 1920 14142 stream.o Transport RTCP 3732 0 0 3732 rtcp.o Transport RTP 2568 0 0 2568 rtp.o Transport UDP 6612 96 0 6708 transport_udp.o Transport Jitter buffer 6473 0 0 6473 jbuf.o ------------------------------------------------------------------------------- TOTAL 55,232 268 2,020 57,520 \endverbatim * The 56KB are for media streaming components, complete with codec, RTP, and * RTCP. The footprint above was done for PJSIP version 1.8.2 on a Linux x86 * machine, using footprintopimization as explained in PJSIP FAQ. Numbers are * in bytes. * * @subsubsection quality Good Quality * PJMEDIA supports wideband, ultra-wideband, and beyond, as well as multiple * audio channels. The jitter buffer has been proven to work on lower * bandwidth links such as 3G, and to some extent, Edge and GPRS. We've grown * our own algorithm to compensate for packet losses and clock drifts in audio * transmission, as well as feature to use codec's built in PLC if available. * * @subsubsection hw Hardware Support * PJMEDIA supports hardware, firmware, or other built-in feature that comes * with the device. These are crucial for mobile devices to allow the best * use of the very limited CPU and battery power of the device. Among other * things, device's on-board codec and echo cancellation may be used if * available. * * @subsubsection extensible Extensible * Despite its tiny footprint, PJMEDIA uses a flexible port concept, which is * adapted from filter based concept found in other media framework. It is not * as flexible as those found in Direct Show or gstreamer (and that would be * unnecessary since it serves different purpose), but it's flexible enough * to allow components to be assembled one after another to achieve certain * task, and easy creation of such components by application and interconnect * them to the rest of the framework. * * @subsubsection doc (Fairly Okay) Documentation * We understand that any documentation can always be improved, but we put * a lot of efforts in creating and maintaining our documentation, because * we know it matters. * * \n * @subsection org1 Organization * * At the top-most level, PJMEDIA library suite contains the following * libraries. * * @subsubsection libpjmedia PJMEDIA * This contains all main media components. Please see the * Modules page for complete list of * components that PJMEDIA provides. * * @subsubsection libpjmediacodec PJMEDIA Codec * PJMEDIA-CODEC is a static library containing various codec implementations, * wrapped into PJMEDIA codec framework. The static library is designed as * such so that only codecs that are explicitly initialized are linked with * the application, therefore keeping the application size in control. * * Please see @ref PJMEDIA_CODEC for more info. * * @subsubsection libpjmediaaudiodev PJMEDIA Audio Device * PJMEDIA-Audiodev is audio device framework and abstraction library. Please * see @ref audio_device_api for more info. * * \n * @section pjmedia_concepts PJMEDIA Key Concepts * Below are some key concepts in PJMEDIA: * - @ref PJMEDIA_PORT * - @ref PJMEDIA_PORT_CLOCK * - @ref PJMEDIA_TRANSPORT * - @ref PJMEDIA_SESSION */ /** @page page_pjmedia_samples PJMEDIA and PJMEDIA-CODEC Examples @section pjmedia_samples_sec PJMEDIA and PJMEDIA-CODEC Examples Please find below some PJMEDIA related examples that may help in giving some more info: - @ref page_pjmedia_samples_level_c\n This is a good place to start learning about @ref PJMEDIA_PORT, as it shows that @ref PJMEDIA_PORT are only "passive" objects with get_frame() and put_frame() interface, and someone has to call these to retrieve/store media frames. - @ref page_pjmedia_samples_playfile_c\n This example shows that when application connects a media port (in this case a @ref PJMEDIA_FILE_PLAY) to @ref PJMED_SND_PORT, media will flow automatically since the @ref PJMED_SND_PORT provides @ref PJMEDIA_PORT_CLOCK. - @ref page_pjmedia_samples_recfile_c\n Demonstrates how to capture audio from microphone to WAV file. - @ref page_pjmedia_samples_playsine_c\n Demonstrates how to create a custom @ref PJMEDIA_PORT (in this case a sine wave generator) and integrate it to PJMEDIA. - @ref page_pjmedia_samples_confsample_c\n This demonstrates how to use the @ref PJMEDIA_CONF. The sample program can open multiple WAV files, and instruct the conference bridge to mix the signal before playing it to the sound device. - @ref page_pjmedia_samples_confbench_c\n I use this to benchmark/optimize the conference bridge algorithm, but readers may find the source useful. - @ref page_pjmedia_samples_resampleplay_c\n Demonstrates how to use @ref PJMEDIA_RESAMPLE_PORT to change the sampling rate of a media port (in this case, a @ref PJMEDIA_FILE_PLAY). - @ref page_pjmedia_samples_sndtest_c\n This program performs some tests to the sound device to get some quality parameters (such as sound jitter and clock drifts).\n Screenshots on WinXP: \image html sndtest.jpg "sndtest screenshot on WinXP" - @ref page_pjmedia_samples_streamutil_c\n This example mainly demonstrates how to stream media (in this case a @ref PJMEDIA_FILE_PLAY) to remote peer using RTP. - @ref page_pjmedia_samples_siprtp_c\n This is a useful program (integrated with PJSIP) to actively measure the network quality/impairment parameters by making one or more SIP calls (or receiving one or more SIP calls) and display the network impairment of each stream direction at the end of the call. The program is able to measure network quality parameters such as jitter, packet lost/reorder/duplicate, round trip time, etc.\n Note that the remote peer MUST support RTCP so that network quality of each direction can be calculated. Using siprtp for both endpoints is recommended.\n Screenshots on WinXP: \image html siprtp.jpg "siprtp screenshot on WinXP" - @ref page_pjmedia_samples_tonegen_c\n This is a simple program to generate a tone and write the samples to a raw PCM file. The main purpose of this file is to analyze the quality of the tones/sine wave generated by PJMEDIA tone/sine wave generator. - @ref page_pjmedia_samples_aectest_c\n Play a file to speaker, run AEC, and record the microphone input to see if echo is coming. */ /** * \page page_pjmedia_samples_siprtp_c Samples: Using SIP and Custom RTP/RTCP to Monitor Quality * * This source is an example to demonstrate using SIP and RTP/RTCP framework * to measure the network quality/impairment from the SIP call. This * program can be used to make calls or to receive calls from other * SIP endpoint (or other siprtp program), and to display the media * quality statistics at the end of the call. * * Note that the remote peer must support RTCP. * * The layout of the program has been designed so that custom reporting * can be generated instead of plain human readable text. * * The source code of the file is pjsip-apps/src/samples/siprtp.c * * Screenshots on WinXP: \image html siprtp.jpg * * \includelineno siprtp.c */ #endif /* __PJMEDIA_DOXYGEN_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/echo.h ================================================ /* $Id: echo.h 4082 2012-04-24 13:09:14Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_ECHO_H__ #define __PJMEDIA_ECHO_H__ /** * @file echo.h * @brief Echo Cancellation API. */ #include /** * @defgroup PJMEDIA_Echo_Cancel Accoustic Echo Cancellation API * @ingroup PJMEDIA_PORT * @brief Echo Cancellation API. * @{ * * This section describes API to perform echo cancellation to audio signal. * There may be multiple echo canceller implementation in PJMEDIA, ranging * from simple echo suppressor to a full Accoustic Echo Canceller/AEC. By * using this API, application should be able to use which EC backend to * use base on the requirement and capability of the platform. */ PJ_BEGIN_DECL /** * Opaque type for PJMEDIA Echo Canceller state. */ typedef struct pjmedia_echo_state pjmedia_echo_state; /** * Echo cancellation options. */ typedef enum pjmedia_echo_flag { /** * Use any available backend echo canceller algorithm. This is * the default settings. This setting is mutually exclusive with * PJMEDIA_ECHO_SIMPLE and PJMEDIA_ECHO_SPEEX. */ PJMEDIA_ECHO_DEFAULT= 0, /** * Force to use Speex AEC as the backend echo canceller algorithm. * This setting is mutually exclusive with PJMEDIA_ECHO_SIMPLE. */ PJMEDIA_ECHO_SPEEX = 1, /** * If PJMEDIA_ECHO_SIMPLE flag is specified during echo canceller * creation, then a simple echo suppressor will be used instead of * an accoustic echo cancellation. This setting is mutually exclusive * with PJMEDIA_ECHO_SPEEX. */ PJMEDIA_ECHO_SIMPLE = 2, /** * Force to use WebRTC AEC as the backend echo canceller algorithm. * This setting is mutually exclusive with PJMEDIA_ECHO_SIMPLE & PJMEDIA_ECHO_SPEEX. */ PJMEDIA_ECHO_WEBRTC = 3, /** * For internal use. */ PJMEDIA_ECHO_ALGO_MASK = 15, /** * If PJMEDIA_ECHO_NO_LOCK flag is specified, no mutex will be created * for the echo canceller, but application will guarantee that echo * canceller will not be called by different threads at the same time. */ PJMEDIA_ECHO_NO_LOCK = 16, /** * If PJMEDIA_ECHO_USE_SIMPLE_FIFO flag is specified, the delay buffer * created for the echo canceller will use simple FIFO mechanism, i.e. * without using WSOLA to expand and shrink audio samples. */ PJMEDIA_ECHO_USE_SIMPLE_FIFO = 32, /** * If PJMEDIA_ECHO_USE_SW_ECHO flag is specified, software echo canceller * will be used instead of device EC. */ PJMEDIA_ECHO_USE_SW_ECHO = 64 } pjmedia_echo_flag; /** * Create the echo canceller. * * @param pool Pool to allocate memory. * @param clock_rate Media clock rate/sampling rate. * @param samples_per_frame Number of samples per frame. * @param tail_ms Tail length, miliseconds. * @param latency_ms Total lacency introduced by playback and * recording device. Set to zero if the latency * is not known. * @param options Options. If PJMEDIA_ECHO_SIMPLE is specified, * then a simple echo suppressor implementation * will be used instead of an accoustic echo * cancellation. * See #pjmedia_echo_flag for other options. * @param p_echo Pointer to receive the Echo Canceller state. * * @return PJ_SUCCESS on success, or the appropriate status. */ PJ_DECL(pj_status_t) pjmedia_echo_create(pj_pool_t *pool, unsigned clock_rate, unsigned samples_per_frame, unsigned tail_ms, unsigned latency_ms, unsigned options, pjmedia_echo_state **p_echo ); /** * Create multi-channel the echo canceller. * * @param pool Pool to allocate memory. * @param clock_rate Media clock rate/sampling rate. * @param channel_count Number of channels. * @param samples_per_frame Number of samples per frame. * @param tail_ms Tail length, miliseconds. * @param latency_ms Total lacency introduced by playback and * recording device. Set to zero if the latency * is not known. * @param options Options. If PJMEDIA_ECHO_SIMPLE is specified, * then a simple echo suppressor implementation * will be used instead of an accoustic echo * cancellation. * See #pjmedia_echo_flag for other options. * @param p_echo Pointer to receive the Echo Canceller state. * * @return PJ_SUCCESS on success, or the appropriate status. */ PJ_DECL(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned tail_ms, unsigned latency_ms, unsigned options, pjmedia_echo_state **p_echo ); /** * Destroy the Echo Canceller. * * @param echo The Echo Canceller. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_echo_destroy(pjmedia_echo_state *echo ); /** * Reset the echo canceller. * * @param echo The Echo Canceller. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_echo_reset(pjmedia_echo_state *echo ); /** * Let the Echo Canceller know that a frame has been played to the speaker. * The Echo Canceller will keep the frame in its internal buffer, to be used * when cancelling the echo with #pjmedia_echo_capture(). * * @param echo The Echo Canceller. * @param play_frm Sample buffer containing frame to be played * (or has been played) to the playback device. * The frame must contain exactly samples_per_frame * number of samples. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_echo_playback(pjmedia_echo_state *echo, pj_int16_t *play_frm ); /** * Let the Echo Canceller know that a frame has been captured from the * microphone. The Echo Canceller will cancel the echo from the captured * signal, using the internal buffer (supplied by #pjmedia_echo_playback()) * as the FES (Far End Speech) reference. * * @param echo The Echo Canceller. * @param rec_frm On input, it contains the input signal (captured * from microphone) which echo is to be removed. * Upon returning this function, this buffer contain * the processed signal with the echo removed. * The frame must contain exactly samples_per_frame * number of samples. * @param options Echo cancellation options, reserved for future use. * Put zero for now. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_echo_capture(pjmedia_echo_state *echo, pj_int16_t *rec_frm, unsigned options ); /** * Perform echo cancellation. * * @param echo The Echo Canceller. * @param rec_frm On input, it contains the input signal (captured * from microphone) which echo is to be removed. * Upon returning this function, this buffer contain * the processed signal with the echo removed. * @param play_frm Sample buffer containing frame to be played * (or has been played) to the playback device. * The frame must contain exactly samples_per_frame * number of samples. * @param options Echo cancellation options, reserved for future use. * Put zero for now. * @param reserved Reserved for future use, put NULL for now. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_echo_cancel( pjmedia_echo_state *echo, pj_int16_t *rec_frm, const pj_int16_t *play_frm, unsigned options, void *reserved ); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_ECHO_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/echo_port.h ================================================ /* $Id: echo_port.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_AEC_PORT_H__ #define __PJMEDIA_AEC_PORT_H__ /** * @file echo_port.h * @brief AEC (Accoustic Echo Cancellation) media port. */ #include /** * @defgroup PJMEDIA_ECHO_PORT Echo Cancellation Port * @ingroup PJMEDIA_PORT * @brief Echo Cancellation * @{ * * Wrapper to \ref PJMEDIA_Echo_Cancel into media port interface. */ PJ_BEGIN_DECL /** * Create echo canceller port. * * @param pool Pool to allocate memory. * @param dn_port Downstream port. * @param tail_ms Tail length in miliseconds. * @param latency_ms Total lacency introduced by playback and * recording device. Set to zero if the latency * is not known. * @param options Options, as in #pjmedia_echo_create(). * @param p_port Pointer to receive the port instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool, pjmedia_port *dn_port, unsigned tail_ms, unsigned latency_ms, unsigned options, pjmedia_port **p_port ); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_AEC_PORT_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/endpoint.h ================================================ /* $Id: endpoint.h 4474 2013-04-16 09:12:59Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_MEDIAMGR_H__ #define __PJMEDIA_MEDIAMGR_H__ /** * @file endpoint.h * @brief Media endpoint. */ /** * @defgroup PJMED_ENDPT The Endpoint * @{ * * The media endpoint acts as placeholder for endpoint capabilities. Each * media endpoint will have a codec manager to manage list of codecs installed * in the endpoint and a sound device factory. * * A reference to media endpoint instance is required when application wants * to create a media session (#pjmedia_session_create()). */ #include #include #include PJ_BEGIN_DECL /** * This enumeration describes various flags that can be set or retrieved in * the media endpoint, by using pjmedia_endpt_set_flag() and * pjmedia_endpt_get_flag() respectively. */ typedef enum pjmedia_endpt_flag { /** * This flag controls whether telephony-event should be offered in SDP. * Value is boolean. */ PJMEDIA_ENDPT_HAS_TELEPHONE_EVENT_FLAG } pjmedia_endpt_flag; /** * Type of callback to register to pjmedia_endpt_atexit(). */ typedef void (*pjmedia_endpt_exit_callback)(pjmedia_endpt *endpt); /** * Create an instance of media endpoint. * * @param pf Pool factory, which will be used by the media endpoint * throughout its lifetime. * @param ioqueue Optional ioqueue instance to be registered to the * endpoint. The ioqueue instance is used to poll all RTP * and RTCP sockets. If this argument is NULL, the * endpoint will create an internal ioqueue instance. * @param worker_cnt Specify the number of worker threads to be created * to poll the ioqueue. * @param p_endpt Pointer to receive the endpoint instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_endpt_create( pj_pool_factory *pf, pj_ioqueue_t *ioqueue, unsigned worker_cnt, pjmedia_endpt **p_endpt); /** * Destroy media endpoint instance. * * @param endpt Media endpoint instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_endpt_destroy(pjmedia_endpt *endpt); /** * Change the value of a flag. * * @param endpt Media endpoint. * @param flag The flag. * @param value Pointer to the value to be set. * * @reurn PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_endpt_set_flag(pjmedia_endpt *endpt, pjmedia_endpt_flag flag, const void *value); /** * Retrieve the value of a flag. * * @param endpt Media endpoint. * @param flag The flag. * @param value Pointer to store the result. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_endpt_get_flag(pjmedia_endpt *endpt, pjmedia_endpt_flag flag, void *value); /** * Get the ioqueue instance of the media endpoint. * * @param endpt The media endpoint instance. * * @return The ioqueue instance of the media endpoint. */ PJ_DECL(pj_ioqueue_t*) pjmedia_endpt_get_ioqueue(pjmedia_endpt *endpt); /** * Get the number of worker threads on the media endpoint * * @param endpt The media endpoint instance. * @return The number of worker threads on the media endpoint */ PJ_DECL(unsigned) pjmedia_endpt_get_thread_count(pjmedia_endpt *endpt); /** * Get a reference to one of the worker threads of the media endpoint * * @param endpt The media endpoint instance. * @param index The index of the thread: 0<= index < thread_cnt * * @return pj_thread_t or NULL */ PJ_DECL(pj_thread_t*) pjmedia_endpt_get_thread(pjmedia_endpt *endpt, unsigned index); /** * Stop and destroy the worker threads of the media endpoint * * @param endpt The media endpoint instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_endpt_stop_threads(pjmedia_endpt *endpt); /** * Request the media endpoint to create pool. * * @param endpt The media endpoint instance. * @param name Name to be assigned to the pool. * @param initial Initial pool size, in bytes. * @param increment Increment size, in bytes. * * @return Memory pool. */ PJ_DECL(pj_pool_t*) pjmedia_endpt_create_pool( pjmedia_endpt *endpt, const char *name, pj_size_t initial, pj_size_t increment); /** * Get the codec manager instance of the media endpoint. * * @param endpt The media endpoint instance. * * @return The instance of codec manager belonging to * this media endpoint. */ PJ_DECL(pjmedia_codec_mgr*) pjmedia_endpt_get_codec_mgr(pjmedia_endpt *endpt); /** * Create a SDP session description that describes the endpoint * capability. * * @param endpt The media endpoint. * @param pool Pool to use to create the SDP descriptor. * @param stream_cnt Number of elements in the sock_info array. This * also denotes the maximum number of streams (i.e. * the "m=" lines) that will be created in the SDP. * By convention, if this value is greater than one, * the first media will be audio and the remaining * media is video. * @param sock_info Array of socket transport information. One * transport is needed for each media stream, and * each transport consists of an RTP and RTCP socket * pair. * @param p_sdp Pointer to receive SDP session descriptor. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt, pj_pool_t *pool, unsigned stream_cnt, const pjmedia_sock_info sock_info[], pjmedia_sdp_session **p_sdp ); /** * Create a "blank" SDP session description. The SDP will contain basic SDP * fields such as origin, time, and name, but without any media lines. * * @param endpt The media endpoint. * @param pool Pool to allocate memory from. * @param sess_name Optional SDP session name, or NULL to use default * value. * @param origin Address to put in the origin field. * @param p_sdp Pointer to receive the created SDP session. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_endpt_create_base_sdp(pjmedia_endpt *endpt, pj_pool_t *pool, const pj_str_t *sess_name, const pj_sockaddr *origin, pjmedia_sdp_session **p_sdp); /** * Create SDP media line for audio media. * * @param endpt The media endpoint. * @param pool Pool to allocate memory from. * @param si Socket information. * @param options Option flags, must be zero for now. * @param p_m Pointer to receive the created SDP media. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_endpt_create_audio_sdp(pjmedia_endpt *endpt, pj_pool_t *pool, const pjmedia_sock_info*si, unsigned options, pjmedia_sdp_media **p_m); /** * Create SDP media line for video media. * * @param endpt The media endpoint. * @param pool Pool to allocate memory from. * @param si Socket information. * @param options Option flags, must be zero for now. * @param p_m Pointer to receive the created SDP media. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_endpt_create_video_sdp(pjmedia_endpt *endpt, pj_pool_t *pool, const pjmedia_sock_info*si, unsigned options, pjmedia_sdp_media **p_m); /** * Dump media endpoint capabilities. * * @param endpt The media endpoint. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_endpt_dump(pjmedia_endpt *endpt); /** * Register cleanup function to be called by media endpoint when * #pjmedia_endpt_destroy() is called. Note that application should not * use or access any endpoint resource (such as pool, ioqueue) from within * the callback as such resource may have been released when the callback * function is invoked. * * @param endpt The media endpoint. * @param func The function to be registered. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_endpt_atexit(pjmedia_endpt *endpt, pjmedia_endpt_exit_callback func); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_MEDIAMGR_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/errno.h ================================================ /* $Id: errno.h 3945 2012-01-27 09:12:59Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_ERRNO_H__ #define __PJMEDIA_ERRNO_H__ /** * @file errno.h Error Codes * @brief PJMEDIA specific error codes. */ #include #include /** * @defgroup PJMEDIA_ERRNO Error Codes * @ingroup PJMEDIA_BASE * @brief PJMEDIA specific error codes. * @{ */ PJ_BEGIN_DECL /** * Start of error code relative to PJ_ERRNO_START_USER. */ #define PJMEDIA_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE) #define PJMEDIA_ERRNO_END (PJMEDIA_ERRNO_START + PJ_ERRNO_SPACE_SIZE - 1) #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) /** * Mapping from LibSRTP error codes to pjmedia error space. */ #define PJMEDIA_LIBSRTP_ERRNO_START (PJMEDIA_ERRNO_END-10200) #define PJMEDIA_LIBSRTP_ERRNO_END (PJMEDIA_LIBSRTP_ERRNO_START + 200 - 1) /** * Convert LibSRTP error code to PJMEDIA error code. * LibSRTP error code range: 0 <= err < 200 */ #define PJMEDIA_ERRNO_FROM_LIBSRTP(err) (PJMEDIA_LIBSRTP_ERRNO_START+err) #endif /************************************************************ * GENERIC/GENERAL PJMEDIA ERRORS ***********************************************************/ /** * @hideinitializer * General/unknown PJMEDIA error. */ #define PJMEDIA_ERROR (PJMEDIA_ERRNO_START+1) /* 220001 */ /************************************************************ * SDP ERRORS ***********************************************************/ /** * @hideinitializer * Generic invalid SDP descriptor. */ #define PJMEDIA_SDP_EINSDP (PJMEDIA_ERRNO_START+20) /* 220020 */ /** * @hideinitializer * Invalid SDP version. */ #define PJMEDIA_SDP_EINVER (PJMEDIA_ERRNO_START+21) /* 220021 */ /** * @hideinitializer * Invalid SDP origin (o=) line. */ #define PJMEDIA_SDP_EINORIGIN (PJMEDIA_ERRNO_START+22) /* 220022 */ /** * @hideinitializer * Invalid SDP time (t=) line. */ #define PJMEDIA_SDP_EINTIME (PJMEDIA_ERRNO_START+23) /* 220023 */ /** * @hideinitializer * Empty SDP subject/name (s=) line. */ #define PJMEDIA_SDP_EINNAME (PJMEDIA_ERRNO_START+24) /* 220024 */ /** * @hideinitializer * Invalid SDP connection info (c=) line. */ #define PJMEDIA_SDP_EINCONN (PJMEDIA_ERRNO_START+25) /* 220025 */ /** * @hideinitializer * Missing SDP connection info line. */ #define PJMEDIA_SDP_EMISSINGCONN (PJMEDIA_ERRNO_START+26) /* 220026 */ /** * @hideinitializer * Invalid attribute (a=) line. */ #define PJMEDIA_SDP_EINATTR (PJMEDIA_ERRNO_START+27) /* 220027 */ /** * @hideinitializer * Invalid rtpmap attribute. */ #define PJMEDIA_SDP_EINRTPMAP (PJMEDIA_ERRNO_START+28) /* 220028 */ /** * @hideinitializer * rtpmap attribute is too long. */ #define PJMEDIA_SDP_ERTPMAPTOOLONG (PJMEDIA_ERRNO_START+29) /* 220029 */ /** * @hideinitializer * rtpmap is missing for dynamic payload type. */ #define PJMEDIA_SDP_EMISSINGRTPMAP (PJMEDIA_ERRNO_START+30) /* 220030 */ /** * @hideinitializer * Invalid SDP media (m=) line. */ #define PJMEDIA_SDP_EINMEDIA (PJMEDIA_ERRNO_START+31) /* 220031 */ /** * @hideinitializer * No payload format in the media stream. */ #define PJMEDIA_SDP_ENOFMT (PJMEDIA_ERRNO_START+32) /* 220032 */ /** * @hideinitializer * Invalid payload type in media. */ #define PJMEDIA_SDP_EINPT (PJMEDIA_ERRNO_START+33) /* 220033 */ /** * @hideinitializer * Invalid SDP "fmtp" attribute. */ #define PJMEDIA_SDP_EINFMTP (PJMEDIA_ERRNO_START+34) /* 220034 */ /** * @hideinitializer * Invalid SDP "rtcp" attribute. */ #define PJMEDIA_SDP_EINRTCP (PJMEDIA_ERRNO_START+35) /* 220035 */ /** * @hideinitializer * Invalid SDP media transport protocol. */ #define PJMEDIA_SDP_EINPROTO (PJMEDIA_ERRNO_START+36) /* 220036 */ /** * @hideinitializer * Invalid SDP bandwidth info (b=) line. */ #define PJMEDIA_SDP_EINBANDW (PJMEDIA_ERRNO_START+37) /* 220037 */ /************************************************************ * SDP NEGOTIATOR ERRORS ***********************************************************/ /** * @hideinitializer * Invalid state to perform the specified operation. */ #define PJMEDIA_SDPNEG_EINSTATE (PJMEDIA_ERRNO_START+40) /* 220040 */ /** * @hideinitializer * No initial local SDP. */ #define PJMEDIA_SDPNEG_ENOINITIAL (PJMEDIA_ERRNO_START+41) /* 220041 */ /** * @hideinitializer * No currently active SDP. */ #define PJMEDIA_SDPNEG_ENOACTIVE (PJMEDIA_ERRNO_START+42) /* 220042 */ /** * @hideinitializer * No current offer or answer. */ #define PJMEDIA_SDPNEG_ENONEG (PJMEDIA_ERRNO_START+43) /* 220043 */ /** * @hideinitializer * Media count mismatch in offer and answer. */ #define PJMEDIA_SDPNEG_EMISMEDIA (PJMEDIA_ERRNO_START+44) /* 220044 */ /** * @hideinitializer * Media type is different in the remote answer. */ #define PJMEDIA_SDPNEG_EINVANSMEDIA (PJMEDIA_ERRNO_START+45) /* 220045 */ /** * @hideinitializer * Transport type is different in the remote answer. */ #define PJMEDIA_SDPNEG_EINVANSTP (PJMEDIA_ERRNO_START+46) /* 220046 */ /** * @hideinitializer * No common media payload is provided in the answer. */ #define PJMEDIA_SDPNEG_EANSNOMEDIA (PJMEDIA_ERRNO_START+47) /* 220047 */ /** * @hideinitializer * No media is active after negotiation. */ #define PJMEDIA_SDPNEG_ENOMEDIA (PJMEDIA_ERRNO_START+48) /* 220048 */ /** * @hideinitializer * No suitable codec for remote offer. */ #define PJMEDIA_SDPNEG_NOANSCODEC (PJMEDIA_ERRNO_START+49) /* 220049 */ /** * @hideinitializer * No suitable telephone-event for remote offer. */ #define PJMEDIA_SDPNEG_NOANSTELEVENT (PJMEDIA_ERRNO_START+50) /* 220050 */ /** * @hideinitializer * No suitable answer for unknown remote offer. */ #define PJMEDIA_SDPNEG_NOANSUNKNOWN (PJMEDIA_ERRNO_START+51) /* 220051 */ /************************************************************ * SDP COMPARISON STATUS ***********************************************************/ /** * @hideinitializer * SDP media stream not equal. */ #define PJMEDIA_SDP_EMEDIANOTEQUAL (PJMEDIA_ERRNO_START+60) /* 220060 */ /** * @hideinitializer * Port number in SDP media descriptor not equal. */ #define PJMEDIA_SDP_EPORTNOTEQUAL (PJMEDIA_ERRNO_START+61) /* 220061 */ /** * @hideinitializer * Transport in SDP media descriptor not equal. */ #define PJMEDIA_SDP_ETPORTNOTEQUAL (PJMEDIA_ERRNO_START+62) /* 220062 */ /** * @hideinitializer * Media format in SDP media descriptor not equal. */ #define PJMEDIA_SDP_EFORMATNOTEQUAL (PJMEDIA_ERRNO_START+63) /* 220063 */ /** * @hideinitializer * SDP connection description not equal. */ #define PJMEDIA_SDP_ECONNNOTEQUAL (PJMEDIA_ERRNO_START+64) /* 220064 */ /** * @hideinitializer * SDP attributes not equal. */ #define PJMEDIA_SDP_EATTRNOTEQUAL (PJMEDIA_ERRNO_START+65) /* 220065 */ /** * @hideinitializer * SDP media direction not equal. */ #define PJMEDIA_SDP_EDIRNOTEQUAL (PJMEDIA_ERRNO_START+66) /* 220066 */ /** * @hideinitializer * SDP fmtp attribute not equal. */ #define PJMEDIA_SDP_EFMTPNOTEQUAL (PJMEDIA_ERRNO_START+67) /* 220067 */ /** * @hideinitializer * SDP ftpmap attribute not equal. */ #define PJMEDIA_SDP_ERTPMAPNOTEQUAL (PJMEDIA_ERRNO_START+68) /* 220068 */ /** * @hideinitializer * SDP session descriptor not equal. */ #define PJMEDIA_SDP_ESESSNOTEQUAL (PJMEDIA_ERRNO_START+69) /* 220069 */ /** * @hideinitializer * SDP origin not equal. */ #define PJMEDIA_SDP_EORIGINNOTEQUAL (PJMEDIA_ERRNO_START+70) /* 220070 */ /** * @hideinitializer * SDP name/subject not equal. */ #define PJMEDIA_SDP_ENAMENOTEQUAL (PJMEDIA_ERRNO_START+71) /* 220071 */ /** * @hideinitializer * SDP time not equal. */ #define PJMEDIA_SDP_ETIMENOTEQUAL (PJMEDIA_ERRNO_START+72) /* 220072 */ /************************************************************ * CODEC ***********************************************************/ /** * @hideinitializer * Unsupported codec. */ #define PJMEDIA_CODEC_EUNSUP (PJMEDIA_ERRNO_START+80) /* 220080 */ /** * @hideinitializer * Codec internal creation error. */ #define PJMEDIA_CODEC_EFAILED (PJMEDIA_ERRNO_START+81) /* 220081 */ /** * @hideinitializer * Codec frame is too short. */ #define PJMEDIA_CODEC_EFRMTOOSHORT (PJMEDIA_ERRNO_START+82) /* 220082 */ /** * @hideinitializer * PCM buffer is too short. */ #define PJMEDIA_CODEC_EPCMTOOSHORT (PJMEDIA_ERRNO_START+83) /* 220083 */ /** * @hideinitializer * Invalid codec frame length. */ #define PJMEDIA_CODEC_EFRMINLEN (PJMEDIA_ERRNO_START+84) /* 220084 */ /** * @hideinitializer * Invalid PCM frame length. */ #define PJMEDIA_CODEC_EPCMFRMINLEN (PJMEDIA_ERRNO_START+85) /* 220085 */ /** * @hideinitializer * Invalid mode. */ #define PJMEDIA_CODEC_EINMODE (PJMEDIA_ERRNO_START+86) /* 220086 */ /** * @hideinitializer * Bad or corrupted bitstream. */ #define PJMEDIA_CODEC_EBADBITSTREAM (PJMEDIA_ERRNO_START+87) /* 220087 */ /************************************************************ * MEDIA ***********************************************************/ /** * @hideinitializer * Invalid remote IP address (in SDP). */ #define PJMEDIA_EINVALIDIP (PJMEDIA_ERRNO_START+100) /* 220100 */ /** * @hideinitializer * Asymetric codec is not supported. */ #define PJMEDIA_EASYMCODEC (PJMEDIA_ERRNO_START+101) /* 220101 */ /** * @hideinitializer * Invalid payload type. */ #define PJMEDIA_EINVALIDPT (PJMEDIA_ERRNO_START+102) /* 220102 */ /** * @hideinitializer * Missing rtpmap. */ #define PJMEDIA_EMISSINGRTPMAP (PJMEDIA_ERRNO_START+103) /* 220103 */ /** * @hideinitializer * Invalid media type. */ #define PJMEDIA_EINVALIMEDIATYPE (PJMEDIA_ERRNO_START+104) /* 220104 */ /** * @hideinitializer * Remote does not support DTMF. */ #define PJMEDIA_EREMOTENODTMF (PJMEDIA_ERRNO_START+105) /* 220105 */ /** * @hideinitializer * Invalid DTMF digit. */ #define PJMEDIA_RTP_EINDTMF (PJMEDIA_ERRNO_START+106) /* 220106 */ /** * @hideinitializer * Remote does not support RFC 2833 */ #define PJMEDIA_RTP_EREMNORFC2833 (PJMEDIA_ERRNO_START+107) /* 220107 */ /** * @hideinitializer * Invalid or bad format */ #define PJMEDIA_EBADFMT (PJMEDIA_ERRNO_START+108) /* 220108 */ /************************************************************ * RTP SESSION ERRORS ***********************************************************/ /** * @hideinitializer * General invalid RTP packet error. */ #define PJMEDIA_RTP_EINPKT (PJMEDIA_ERRNO_START+120) /* 220120 */ /** * @hideinitializer * Invalid RTP packet packing. */ #define PJMEDIA_RTP_EINPACK (PJMEDIA_ERRNO_START+121) /* 220121 */ /** * @hideinitializer * Invalid RTP packet version. */ #define PJMEDIA_RTP_EINVER (PJMEDIA_ERRNO_START+122) /* 220122 */ /** * @hideinitializer * RTP SSRC id mismatch. */ #define PJMEDIA_RTP_EINSSRC (PJMEDIA_ERRNO_START+123) /* 220123 */ /** * @hideinitializer * RTP payload type mismatch. */ #define PJMEDIA_RTP_EINPT (PJMEDIA_ERRNO_START+124) /* 220124 */ /** * @hideinitializer * Invalid RTP packet length. */ #define PJMEDIA_RTP_EINLEN (PJMEDIA_ERRNO_START+125) /* 220125 */ /** * @hideinitializer * RTP session restarted. */ #define PJMEDIA_RTP_ESESSRESTART (PJMEDIA_ERRNO_START+130) /* 220130 */ /** * @hideinitializer * RTP session in probation */ #define PJMEDIA_RTP_ESESSPROBATION (PJMEDIA_ERRNO_START+131) /* 220131 */ /** * @hideinitializer * Bad RTP sequence number */ #define PJMEDIA_RTP_EBADSEQ (PJMEDIA_ERRNO_START+132) /* 220132 */ /** * @hideinitializer * RTP media port destination is not configured */ #define PJMEDIA_RTP_EBADDEST (PJMEDIA_ERRNO_START+133) /* 220133 */ /** * @hideinitializer * RTP is not configured. */ #define PJMEDIA_RTP_ENOCONFIG (PJMEDIA_ERRNO_START+134) /* 220134 */ /************************************************************ * PORT ERRORS ***********************************************************/ /** * @hideinitializer * Generic incompatible port error. */ #define PJMEDIA_ENOTCOMPATIBLE (PJMEDIA_ERRNO_START+160) /* 220160 */ /** * @hideinitializer * Incompatible clock rate */ #define PJMEDIA_ENCCLOCKRATE (PJMEDIA_ERRNO_START+161) /* 220161 */ /** * @hideinitializer * Incompatible samples per frame */ #define PJMEDIA_ENCSAMPLESPFRAME (PJMEDIA_ERRNO_START+162) /* 220162 */ /** * @hideinitializer * Incompatible media type */ #define PJMEDIA_ENCTYPE (PJMEDIA_ERRNO_START+163) /* 220163 */ /** * @hideinitializer * Incompatible bits per sample */ #define PJMEDIA_ENCBITS (PJMEDIA_ERRNO_START+164) /* 220164 */ /** * @hideinitializer * Incompatible bytes per frame */ #define PJMEDIA_ENCBYTES (PJMEDIA_ERRNO_START+165) /* 220165 */ /** * @hideinitializer * Incompatible number of channels */ #define PJMEDIA_ENCCHANNEL (PJMEDIA_ERRNO_START+166) /* 220166 */ /************************************************************ * FILE ERRORS ***********************************************************/ /** * @hideinitializer * Not a valid WAVE file. */ #define PJMEDIA_ENOTVALIDWAVE (PJMEDIA_ERRNO_START+180) /* 220180 */ /** * @hideinitializer * Unsupported WAVE file. */ #define PJMEDIA_EWAVEUNSUPP (PJMEDIA_ERRNO_START+181) /* 220181 */ /** * @hideinitializer * Wave file too short. */ #define PJMEDIA_EWAVETOOSHORT (PJMEDIA_ERRNO_START+182) /* 220182 */ /** * @hideinitializer * Sound frame is too large for file buffer. */ #define PJMEDIA_EFRMFILETOOBIG (PJMEDIA_ERRNO_START+183) /* 220183 */ /** * @hideinitializer * Unsupported AVI file. */ #define PJMEDIA_EAVIUNSUPP (PJMEDIA_ERRNO_START+191) /* 220191 */ /************************************************************ * SOUND DEVICE ERRORS ***********************************************************/ /** * @hideinitializer * No suitable audio capture device. */ #define PJMEDIA_ENOSNDREC (PJMEDIA_ERRNO_START+200) /* 220200 */ /** * @hideinitializer * No suitable audio playback device. */ #define PJMEDIA_ENOSNDPLAY (PJMEDIA_ERRNO_START+201) /* 220201 */ /** * @hideinitializer * Invalid sound device ID. */ #define PJMEDIA_ESNDINDEVID (PJMEDIA_ERRNO_START+202) /* 220202 */ /** * @hideinitializer * Invalid sample format for sound device. */ #define PJMEDIA_ESNDINSAMPLEFMT (PJMEDIA_ERRNO_START+203) /* 220203 */ #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) /************************************************************ * SRTP TRANSPORT ERRORS ***********************************************************/ /** * @hideinitializer * SRTP crypto-suite name not match the offerer tag. */ #define PJMEDIA_SRTP_ECRYPTONOTMATCH (PJMEDIA_ERRNO_START+220) /* 220220 */ /** * @hideinitializer * Invalid SRTP key length for specific crypto. */ #define PJMEDIA_SRTP_EINKEYLEN (PJMEDIA_ERRNO_START+221) /* 220221 */ /** * @hideinitializer * Unsupported SRTP crypto-suite. */ #define PJMEDIA_SRTP_ENOTSUPCRYPTO (PJMEDIA_ERRNO_START+222) /* 220222 */ /** * @hideinitializer * SRTP SDP contains ambigue answer. */ #define PJMEDIA_SRTP_ESDPAMBIGUEANS (PJMEDIA_ERRNO_START+223) /* 220223 */ /** * @hideinitializer * Duplicated crypto tag. */ #define PJMEDIA_SRTP_ESDPDUPCRYPTOTAG (PJMEDIA_ERRNO_START+224) /* 220224 */ /** * @hideinitializer * Invalid crypto attribute. */ #define PJMEDIA_SRTP_ESDPINCRYPTO (PJMEDIA_ERRNO_START+225) /* 220225 */ /** * @hideinitializer * Invalid crypto tag. */ #define PJMEDIA_SRTP_ESDPINCRYPTOTAG (PJMEDIA_ERRNO_START+226) /* 220226 */ /** * @hideinitializer * Invalid SDP media transport for SRTP. */ #define PJMEDIA_SRTP_ESDPINTRANSPORT (PJMEDIA_ERRNO_START+227) /* 220227 */ /** * @hideinitializer * SRTP crypto attribute required in SDP. */ #define PJMEDIA_SRTP_ESDPREQCRYPTO (PJMEDIA_ERRNO_START+228) /* 220228 */ /** * @hideinitializer * Secure transport required in SDP media descriptor. */ #define PJMEDIA_SRTP_ESDPREQSECTP (PJMEDIA_ERRNO_START+229) /* 220229 */ #endif /* PJMEDIA_HAS_SRTP */ /** * Get error message for the specified error code. Note that this * function is only able to decode PJMEDIA specific error code. * Application should use pj_strerror(), which should be able to * decode all error codes belonging to all subsystems (e.g. pjlib, * pjmedia, pjsip, etc). * * @param status The error code. * @param buffer The buffer where to put the error message. * @param bufsize Size of the buffer. * * @return The error message as NULL terminated string, * wrapped with pj_str_t. */ PJ_DECL(pj_str_t) pjmedia_strerror( pj_status_t status, char *buffer, pj_size_t bufsize); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_ERRNO_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/event.h ================================================ /* $Id: event.h 3905 2011-12-09 05:15:39Z ming $ */ /* * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_EVENT_H__ #define __PJMEDIA_EVENT_H__ /** * @file pjmedia/event.h * @brief Event framework */ #include #include PJ_BEGIN_DECL /** * @defgroup PJMEDIA_EVENT Event Framework * @brief PJMEDIA event framework * @{ */ /** * This enumeration describes list of media events. */ typedef enum pjmedia_event_type { /** * No event. */ PJMEDIA_EVENT_NONE, /** * Media format has changed event. */ PJMEDIA_EVENT_FMT_CHANGED = PJMEDIA_FOURCC('F', 'M', 'C', 'H'), /** * Video window is being closed. */ PJMEDIA_EVENT_WND_CLOSING = PJMEDIA_FOURCC('W', 'N', 'C', 'L'), /** * Video window has been closed event. */ PJMEDIA_EVENT_WND_CLOSED = PJMEDIA_FOURCC('W', 'N', 'C', 'O'), /** * Video window has been resized event. */ PJMEDIA_EVENT_WND_RESIZED = PJMEDIA_FOURCC('W', 'N', 'R', 'Z'), /** * Mouse button has been pressed event. */ PJMEDIA_EVENT_MOUSE_BTN_DOWN = PJMEDIA_FOURCC('M', 'S', 'D', 'N'), /** * Video keyframe has just been decoded event. */ PJMEDIA_EVENT_KEYFRAME_FOUND = PJMEDIA_FOURCC('I', 'F', 'R', 'F'), /** * Video decoding error due to missing keyframe event. */ PJMEDIA_EVENT_KEYFRAME_MISSING = PJMEDIA_FOURCC('I', 'F', 'R', 'M'), /** * Remote video decoder asked for a keyframe. */ PJMEDIA_EVENT_KEYFRAME_REQUESTED = PJMEDIA_FOURCC('I', 'F', 'R', 'R'), /** * Video orientation has been changed event. */ PJMEDIA_EVENT_ORIENT_CHANGED = PJMEDIA_FOURCC('O', 'R', 'N', 'T') } pjmedia_event_type; /** * Additional data/parameters for media format changed event * (PJMEDIA_EVENT_FMT_CHANGED). */ typedef struct pjmedia_event_fmt_changed_data { /** The media flow direction */ pjmedia_dir dir; /** The new media format. */ pjmedia_format new_fmt; } pjmedia_event_fmt_changed_data; /** * Additional data/parameters are not needed. */ typedef struct pjmedia_event_dummy_data { /** Dummy data */ int dummy; } pjmedia_event_dummy_data; /** * Additional data/parameters for window resized event * (PJMEDIA_EVENT_WND_RESIZED). */ typedef struct pjmedia_event_wnd_resized_data { /** * The new window size. */ pjmedia_rect_size new_size; } pjmedia_event_wnd_resized_data; /** * Additional data/parameters for window closing event. */ typedef struct pjmedia_event_wnd_closing_data { /** Consumer may set this field to PJ_TRUE to cancel the closing */ pj_bool_t cancel; } pjmedia_event_wnd_closing_data; /** Additional parameters for window changed event. */ typedef pjmedia_event_dummy_data pjmedia_event_wnd_closed_data; /** Additional parameters for mouse button down event */ typedef pjmedia_event_dummy_data pjmedia_event_mouse_btn_down_data; /** Additional parameters for keyframe found event */ typedef pjmedia_event_dummy_data pjmedia_event_keyframe_found_data; /** Additional parameters for keyframe missing event */ typedef pjmedia_event_dummy_data pjmedia_event_keyframe_missing_data; /** * Maximum size of additional parameters section in pjmedia_event structure */ #define PJMEDIA_EVENT_DATA_MAX_SIZE sizeof(pjmedia_event_fmt_changed_data) /** Type of storage to hold user data in pjmedia_event structure */ typedef char pjmedia_event_user_data[PJMEDIA_EVENT_DATA_MAX_SIZE]; /** * This structure describes a media event. It consists mainly of the event * type and additional data/parameters for the event. Applications can * use #pjmedia_event_init() to initialize this event structure with * basic information about the event. */ typedef struct pjmedia_event { /** * The event type. */ pjmedia_event_type type; /** * The media timestamp when the event occurs. */ pj_timestamp timestamp; /** * Pointer information about the source of this event. This field * is provided mainly for comparison purpose so that event subscribers * can check which source the event originated from. Usage of this * pointer for other purpose may require special care such as mutex * locking or checking whether the object is already destroyed. */ const void *src; /** * Pointer information about the publisher of this event. This field * is provided mainly for comparison purpose so that event subscribers * can check which object published the event. Usage of this * pointer for other purpose may require special care such as mutex * locking or checking whether the object is already destroyed. */ const void *epub; /** * Additional data/parameters about the event. The type of data * will be specific to the event type being reported. */ union { /** Media format changed event data. */ pjmedia_event_fmt_changed_data fmt_changed; /** Window resized event data */ pjmedia_event_wnd_resized_data wnd_resized; /** Window closing event data. */ pjmedia_event_wnd_closing_data wnd_closing; /** Window closed event data */ pjmedia_event_wnd_closed_data wnd_closed; /** Mouse button down event data */ pjmedia_event_mouse_btn_down_data mouse_btn_down; /** Keyframe found event data */ pjmedia_event_keyframe_found_data keyframe_found; /** Keyframe missing event data */ pjmedia_event_keyframe_missing_data keyframe_missing; /** Storage for user event data */ pjmedia_event_user_data user; /** Pointer to storage to user event data, if it's outside * this struct */ void *ptr; } data; } pjmedia_event; /** * The callback to receive media events. * * @param event The media event. * @param user_data The user data associated with the callback. * * @return If the callback returns non-PJ_SUCCESS, this return * code may be propagated back to the caller. */ typedef pj_status_t pjmedia_event_cb(pjmedia_event *event, void *user_data); /** * This enumeration describes flags for event publication via * #pjmedia_event_publish(). */ typedef enum pjmedia_event_publish_flag { /** * Default flag. */ PJMEDIA_EVENT_PUBLISH_DEFAULT, /** * Publisher will only post the event to the event manager. It is the * event manager that will later notify all the publisher's subscribers. */ PJMEDIA_EVENT_PUBLISH_POST_EVENT = 1 } pjmedia_event_publish_flag; /** * Event manager flag. */ typedef enum pjmedia_event_mgr_flag { /** * Tell the event manager not to create any event worker thread. */ PJMEDIA_EVENT_MGR_NO_THREAD = 1 } pjmedia_event_mgr_flag; /** * Opaque data type for event manager. Typically, the event manager * is a singleton instance, although application may instantiate more than one * instances of this if required. */ typedef struct pjmedia_event_mgr pjmedia_event_mgr; /** * Create a new event manager instance. This will also set the pointer * to the singleton instance if the value is still NULL. * * @param pool Pool to allocate memory from. * @param options Options. Bitmask flags from #pjmedia_event_mgr_flag * @param mgr Pointer to hold the created instance of the * event manager. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_event_mgr_create(pj_pool_t *pool, unsigned options, pjmedia_event_mgr **mgr); /** * Get the singleton instance of the event manager. * * @return The instance. */ PJ_DECL(pjmedia_event_mgr*) pjmedia_event_mgr_instance(void); /** * Manually assign a specific event manager instance as the singleton * instance. Normally this is not needed if only one instance is ever * going to be created, as the library automatically assign the singleton * instance. * * @param mgr The instance to be used as the singleton instance. * Application may specify NULL to clear the singleton * singleton instance. */ PJ_DECL(void) pjmedia_event_mgr_set_instance(pjmedia_event_mgr *mgr); /** * Destroy an event manager. If the manager happens to be the singleton * instance, the singleton instance will be set to NULL. * * @param mgr The eventmanager. Specify NULL to use * the singleton instance. */ PJ_DECL(void) pjmedia_event_mgr_destroy(pjmedia_event_mgr *mgr); /** * Initialize event structure with basic data about the event. * * @param event The event to be initialized. * @param type The event type to be set for this event. * @param ts Event timestamp. May be set to NULL to set the event * timestamp to zero. * @param src Event source. */ PJ_DECL(void) pjmedia_event_init(pjmedia_event *event, pjmedia_event_type type, const pj_timestamp *ts, const void *src); /** * Subscribe a callback function to events published by the specified * publisher. Note that the subscriber may receive not only events emitted by * the specific publisher specified in the argument, but also from other * publishers contained by the publisher, if the publisher is republishing * events from other publishers. * * @param mgr The event manager. * @param cb The callback function to receive the event. * @param user_data The user data to be associated with the callback * function. * @param epub The event publisher. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_event_subscribe(pjmedia_event_mgr *mgr, pjmedia_event_cb *cb, void *user_data, void *epub); /** * Unsubscribe the callback associated with the user data from a publisher. * If the user data is not specified, this function will do the * unsubscription for all user data. If the publisher, epub, is not * specified, this function will do the unsubscription from all publishers. * * @param mgr The event manager. * @param cb The callback function. * @param user_data The user data associated with the callback * function, can be NULL. * @param epub The event publisher, can be NULL. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_event_unsubscribe(pjmedia_event_mgr *mgr, pjmedia_event_cb *cb, void *user_data, void *epub); /** * Publish the specified event to all subscribers of the specified event * publisher. By default, the function will call all the subcribers' * callbacks immediately. If the publisher uses the flag * PJMEDIA_EVENT_PUBLISH_POST_EVENT, publisher will only post the event * to the event manager and return immediately. It is the event manager * that will later notify all the publisher's subscribers. * * @param mgr The event manager. * @param epub The event publisher. * @param event The event to be published. * @param flag Publication flag. * * @return PJ_SUCCESS only if all subscription callbacks returned * PJ_SUCCESS. */ PJ_DECL(pj_status_t) pjmedia_event_publish(pjmedia_event_mgr *mgr, void *epub, pjmedia_event *event, pjmedia_event_publish_flag flag); /** * @} PJMEDIA_EVENT */ PJ_END_DECL #endif /* __PJMEDIA_EVENT_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/format.h ================================================ /* $Id: format.h 4470 2013-04-15 10:40:26Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_FORMAT_H__ #define __PJMEDIA_FORMAT_H__ /** * @file pjmedia/format.h Media format * @brief Media format */ #include /** * @defgroup PJMEDIA_FORMAT Media format * @ingroup PJMEDIA_TYPES * @brief Media format * @{ */ PJ_BEGIN_DECL /** * Macro for packing format from a four character code, similar to FOURCC. * This macro is used for building the constants in pjmedia_format_id * enumeration. */ #define PJMEDIA_FORMAT_PACK(C1, C2, C3, C4) PJMEDIA_FOURCC(C1, C2, C3, C4) /** * This enumeration uniquely identify audio sample and/or video pixel formats. * Some well known formats are listed here. The format ids are built by * combining four character codes, similar to FOURCC. The format id is * extensible, as application may define and use format ids not declared * on this enumeration. * * This format id along with other information will fully describe the media * in #pjmedia_format structure. */ typedef enum pjmedia_format_id { /* * Audio formats */ /** 16bit signed integer linear PCM audio */ PJMEDIA_FORMAT_L16 = 0, /** Alias for PJMEDIA_FORMAT_L16 */ PJMEDIA_FORMAT_PCM = PJMEDIA_FORMAT_L16, /** G.711 ALAW */ PJMEDIA_FORMAT_PCMA = PJMEDIA_FORMAT_PACK('A', 'L', 'A', 'W'), /** Alias for PJMEDIA_FORMAT_PCMA */ PJMEDIA_FORMAT_ALAW = PJMEDIA_FORMAT_PCMA, /** G.711 ULAW */ PJMEDIA_FORMAT_PCMU = PJMEDIA_FORMAT_PACK('u', 'L', 'A', 'W'), /** Aliaw for PJMEDIA_FORMAT_PCMU */ PJMEDIA_FORMAT_ULAW = PJMEDIA_FORMAT_PCMU, /** AMR narrowband */ PJMEDIA_FORMAT_AMR = PJMEDIA_FORMAT_PACK(' ', 'A', 'M', 'R'), /** ITU G.729 */ PJMEDIA_FORMAT_G729 = PJMEDIA_FORMAT_PACK('G', '7', '2', '9'), /** Internet Low Bit-Rate Codec (ILBC) */ PJMEDIA_FORMAT_ILBC = PJMEDIA_FORMAT_PACK('I', 'L', 'B', 'C'), /* * Video formats. */ /** * 24bit RGB */ PJMEDIA_FORMAT_RGB24 = PJMEDIA_FORMAT_PACK('R', 'G', 'B', '3'), /** * 32bit RGB with alpha channel */ PJMEDIA_FORMAT_ARGB = PJMEDIA_FORMAT_PACK('A', 'R', 'G', 'B'), PJMEDIA_FORMAT_RGBA = PJMEDIA_FORMAT_PACK('R', 'G', 'B', 'A'), PJMEDIA_FORMAT_BGRA = PJMEDIA_FORMAT_PACK('B', 'G', 'R', 'A'), /** * Alias for PJMEDIA_FORMAT_RGBA */ PJMEDIA_FORMAT_RGB32 = PJMEDIA_FORMAT_RGBA, /** * Device Independent Bitmap, alias for 24 bit RGB */ PJMEDIA_FORMAT_DIB = PJMEDIA_FORMAT_PACK('D', 'I', 'B', ' '), /** * This is planar 4:4:4/24bpp RGB format, the data can be treated as * three planes of color components, where the first plane contains * only the G samples, the second plane contains only the B samples, * and the third plane contains only the R samples. */ PJMEDIA_FORMAT_GBRP = PJMEDIA_FORMAT_PACK('G', 'B', 'R', 'P'), /** * This is a packed 4:4:4/32bpp format, where each pixel is encoded as * four consecutive bytes, arranged in the following sequence: V0, U0, * Y0, A0. Source: * http://msdn.microsoft.com/en-us/library/dd206750%28v=VS.85%29.aspx#ayuv */ PJMEDIA_FORMAT_AYUV = PJMEDIA_FORMAT_PACK('A', 'Y', 'U', 'V'), /** * This is packed 4:2:2/16bpp YUV format, the data can be treated as * an array of unsigned char values, where the first byte contains * the first Y sample, the second byte contains the first U (Cb) sample, * the third byte contains the second Y sample, and the fourth byte * contains the first V (Cr) sample, and so forth. Source: * http://msdn.microsoft.com/en-us/library/dd206750%28v=VS.85%29.aspx#yuy2 */ PJMEDIA_FORMAT_YUY2 = PJMEDIA_FORMAT_PACK('Y', 'U', 'Y', '2'), /** * This format is the same as the YUY2 format except the byte order is * reversed -- that is, the chroma and luma bytes are flipped. If the * image is addressed as an array of two little-endian WORD values, the * first WORD contains U in the LSBs and Y0 in the MSBs, and the second * WORD contains V in the LSBs and Y1 in the MSBs. Source: * http://msdn.microsoft.com/en-us/library/dd206750%28v=VS.85%29.aspx#uyvy */ PJMEDIA_FORMAT_UYVY = PJMEDIA_FORMAT_PACK('U', 'Y', 'V', 'Y'), /** * This format is the same as the YUY2 and UYVY format except the byte * order is reversed -- that is, the chroma and luma bytes are flipped. * If the image is addressed as an array of two little-endian WORD values, * the first WORD contains Y0 in the LSBs and V in the MSBs, and the second * WORD contains Y1 in the LSBs and U in the MSBs. */ PJMEDIA_FORMAT_YVYU = PJMEDIA_FORMAT_PACK('Y', 'V', 'Y', 'U'), /** * This is planar 4:2:0/12bpp YUV format, the data can be treated as * three planes of color components, where the first plane contains * only the Y samples, the second plane contains only the U (Cb) samples, * and the third plane contains only the V (Cr) sample. */ PJMEDIA_FORMAT_I420 = PJMEDIA_FORMAT_PACK('I', '4', '2', '0'), /** * IYUV is alias for I420. */ PJMEDIA_FORMAT_IYUV = PJMEDIA_FORMAT_I420, /** * This is planar 4:2:0/12bpp YUV format, similar to I420 or IYUV but * the U (Cb) and V (Cr) planes order is switched, i.e: the second plane * contains the V (Cb) samples and the third plane contains the V (Cr) * samples. */ PJMEDIA_FORMAT_YV12 = PJMEDIA_FORMAT_PACK('Y', 'V', '1', '2'), /** * This is planar 4:2:0/12bpp YUV format, the data can be treated as * two planes of color components, where the first plane contains * only the Y samples, the second plane contains interleaved * V (Cr) - U (Cb) samples. */ PJMEDIA_FORMAT_NV21 = PJMEDIA_FORMAT_PACK('N', 'V', '2', '1'), /** * This is planar 4:2:2/16bpp YUV format, the data can be treated as * three planes of color components, where the first plane contains * only the Y samples, the second plane contains only the U (Cb) samples, * and the third plane contains only the V (Cr) sample. */ PJMEDIA_FORMAT_I422 = PJMEDIA_FORMAT_PACK('I', '4', '2', '2'), /** * The JPEG version of planar 4:2:0/12bpp YUV format. */ PJMEDIA_FORMAT_I420JPEG = PJMEDIA_FORMAT_PACK('J', '4', '2', '0'), /** * The JPEG version of planar 4:2:2/16bpp YUV format. */ PJMEDIA_FORMAT_I422JPEG = PJMEDIA_FORMAT_PACK('J', '4', '2', '2'), /** * Encoded video formats */ PJMEDIA_FORMAT_H261 = PJMEDIA_FORMAT_PACK('H', '2', '6', '1'), PJMEDIA_FORMAT_H263 = PJMEDIA_FORMAT_PACK('H', '2', '6', '3'), PJMEDIA_FORMAT_H263P = PJMEDIA_FORMAT_PACK('P', '2', '6', '3'), PJMEDIA_FORMAT_H264 = PJMEDIA_FORMAT_PACK('H', '2', '6', '4'), PJMEDIA_FORMAT_MJPEG = PJMEDIA_FORMAT_PACK('M', 'J', 'P', 'G'), PJMEDIA_FORMAT_MPEG1VIDEO = PJMEDIA_FORMAT_PACK('M', 'P', '1', 'V'), PJMEDIA_FORMAT_MPEG2VIDEO = PJMEDIA_FORMAT_PACK('M', 'P', '2', 'V'), PJMEDIA_FORMAT_MPEG4 = PJMEDIA_FORMAT_PACK('M', 'P', 'G', '4'), PJMEDIA_FORMAT_VP8 = PJMEDIA_FORMAT_PACK('L', 'V', 'P', '8'), } pjmedia_format_id; /** * This enumeration specifies what type of detail is included in a * #pjmedia_format structure. */ typedef enum pjmedia_format_detail_type { /** Format detail is not specified. */ PJMEDIA_FORMAT_DETAIL_NONE, /** Audio format detail. */ PJMEDIA_FORMAT_DETAIL_AUDIO, /** Video format detail. */ PJMEDIA_FORMAT_DETAIL_VIDEO, /** Number of format detail type that has been defined. */ PJMEDIA_FORMAT_DETAIL_MAX } pjmedia_format_detail_type; /** * This structure is put in \a detail field of #pjmedia_format to describe * detail information about an audio media. */ typedef struct pjmedia_audio_format_detail { unsigned clock_rate; /**< Audio clock rate in samples or Hz. */ unsigned channel_count; /**< Number of channels. */ unsigned frame_time_usec;/**< Frame interval, in microseconds. */ unsigned bits_per_sample;/**< Number of bits per sample. */ pj_uint32_t avg_bps; /**< Average bitrate */ pj_uint32_t max_bps; /**< Maximum bitrate */ } pjmedia_audio_format_detail; /** * This structure is put in \a detail field of #pjmedia_format to describe * detail information about a video media. * * Additional information about a video format can also be retrieved by * calling #pjmedia_get_video_format_info(). */ typedef struct pjmedia_video_format_detail { pjmedia_rect_size size; /**< Video size (width, height) */ pjmedia_ratio fps; /**< Number of frames per second. */ pj_uint32_t avg_bps;/**< Average bitrate. */ pj_uint32_t max_bps;/**< Maximum bitrate. */ } pjmedia_video_format_detail; /** * This macro declares the size of the detail section in #pjmedia_format * to be reserved for user defined detail. */ #ifndef PJMEDIA_FORMAT_DETAIL_USER_SIZE # define PJMEDIA_FORMAT_DETAIL_USER_SIZE 1 #endif /** * This structure contains all the information needed to completely describe * a media. */ typedef struct pjmedia_format { /** * The format id that specifies the audio sample or video pixel format. * Some well known formats ids are declared in pjmedia_format_id * enumeration. * * @see pjmedia_format_id */ pj_uint32_t id; /** * The top-most type of the media, as an information. */ pjmedia_type type; /** * The type of detail structure in the \a detail pointer. */ pjmedia_format_detail_type detail_type; /** * Detail section to describe the media. */ union { /** * Detail section for audio format. */ pjmedia_audio_format_detail aud; /** * Detail section for video format. */ pjmedia_video_format_detail vid; /** * Reserved area for user-defined format detail. */ char user[PJMEDIA_FORMAT_DETAIL_USER_SIZE]; } det; } pjmedia_format; /** * This enumeration describes video color model. It mostly serves as * information only. */ typedef enum pjmedia_color_model { /** The color model is unknown or unspecified. */ PJMEDIA_COLOR_MODEL_NONE, /** RGB color model. */ PJMEDIA_COLOR_MODEL_RGB, /** YUV color model. */ PJMEDIA_COLOR_MODEL_YUV } pjmedia_color_model; /** * This structure holds information to apply a specific video format * against size and buffer information, and get additional information * from it. To do that, application fills up the input fields of this * structure, and give this structure to \a apply_fmt() function * of #pjmedia_video_format_info structure. */ typedef struct pjmedia_video_apply_fmt_param { /* input fields: */ /** * [IN] The image size. This field is mandatory, and has to be set * correctly prior to calling \a apply_fmt() function. */ pjmedia_rect_size size; /** * [IN] Pointer to the buffer that holds the frame. The \a apply_fmt() * function uses this pointer to calculate the pointer for each video * planes of the media. This field is optional -- however, the * \a apply_fmt() would still fill up the \a planes[] array with the * correct pointer even though the buffer is set to NULL. This could be * useful to calculate the size (in bytes) of each plane. */ pj_uint8_t *buffer; /* output fields: */ /** * [OUT] The size (in bytes) required of the buffer to hold the video * frame of the particular frame size (width, height). */ pj_size_t framebytes; /** * [OUT] Array of strides value (in bytes) for each video plane. */ int strides[PJMEDIA_MAX_VIDEO_PLANES]; /** * [OUT] Array of pointers to each of the video planes. The values are * calculated from the \a buffer field. */ pj_uint8_t *planes[PJMEDIA_MAX_VIDEO_PLANES]; /** * [OUT] Array of video plane sizes. */ pj_size_t plane_bytes[PJMEDIA_MAX_VIDEO_PLANES]; } pjmedia_video_apply_fmt_param; /** * This structure holds information to describe a video format. Application * can retrieve this structure by calling #pjmedia_get_video_format_info() * funcion. */ typedef struct pjmedia_video_format_info { /** * The unique format ID of the media. Well known format ids are declared * in pjmedia_format_id enumeration. */ pj_uint32_t id; /** * Null terminated string containing short identification about the * format. */ char name[8]; /** * Information about the color model of this video format. */ pjmedia_color_model color_model; /** * Number of bits needed to store one pixel of this video format. */ pj_uint8_t bpp; /** * Number of video planes that this format uses. Value 1 indicates * packed format, while value greater than 1 indicates planar format. */ pj_uint8_t plane_cnt; /** * Pointer to function to apply this format against size and buffer * information in pjmedia_video_apply_fmt_param argument. Application * uses this function to obtain various information such as the * memory size of a frame buffer, strides value of the image, the * location of the planes, and so on. See pjmedia_video_apply_fmt_param * for additional information. * * @param vfi The video format info. * @param vafp The parameters to investigate. * * @return PJ_SUCCESS if the function has calculated the * information in \a vafp successfully. */ pj_status_t (*apply_fmt)(const struct pjmedia_video_format_info *vfi, pjmedia_video_apply_fmt_param *vafp); } pjmedia_video_format_info; /***************************************************************************** * UTILITIES: */ /** * General utility routine to calculate samples per frame value from clock * rate, ptime (in usec), and channel count. Application should use this * macro whenever possible due to possible overflow in the math calculation. * * @param clock_rate Clock rate. * @param usec_ptime Frame interval, in microsecond. * @param channel_count Number of channels. * * @return The samples per frame value. */ PJ_INLINE(unsigned) PJMEDIA_SPF(unsigned clock_rate, unsigned usec_ptime, unsigned channel_count) { #if PJ_HAS_INT64 return ((unsigned)((pj_uint64_t)usec_ptime * \ clock_rate * channel_count / 1000000)); #elif PJ_HAS_FLOATING_POINT return ((unsigned)(1.0*usec_ptime * clock_rate * channel_count / 1000000)); #else return ((unsigned)(usec_ptime / 1000L * clock_rate * \ channel_count / 1000)); #endif } /** * Variant of #PJMEDIA_SPF() which takes frame rate instead of ptime. */ PJ_INLINE(unsigned) PJMEDIA_SPF2(unsigned clock_rate, const pjmedia_ratio *fr, unsigned channel_count) { #if PJ_HAS_INT64 return ((unsigned)((pj_uint64_t)clock_rate * fr->denum \ / fr->num / channel_count)); #elif PJ_HAS_FLOATING_POINT return ((unsigned)(1.0* clock_rate * fr->denum / fr->num /channel_count)); #else return ((unsigned)(1L * clock_rate * fr->denum / fr->num / channel_count)); #endif } /** * Utility routine to calculate frame size (in bytes) from bitrate and frame * interval values. Application should use this macro whenever possible due * to possible overflow in the math calculation. * * @param bps The bitrate of the stream. * @param usec_ptime Frame interval, in microsecond. * * @return Frame size in bytes. */ PJ_INLINE(unsigned) PJMEDIA_FSZ(unsigned bps, unsigned usec_ptime) { #if PJ_HAS_INT64 return ((unsigned)((pj_uint64_t)bps * usec_ptime / PJ_UINT64(8000000))); #elif PJ_HAS_FLOATING_POINT return ((unsigned)(1.0 * bps * usec_ptime / 8000000.0)); #else return ((unsigned)(bps / 8L * usec_ptime / 1000000)); #endif } /** * General utility routine to calculate ptime value from frame rate. * Application should use this macro whenever possible due to possible * overflow in the math calculation. * * @param frame_rate Frame rate * * @return The ptime value (in usec). */ PJ_INLINE(unsigned) PJMEDIA_PTIME(const pjmedia_ratio *frame_rate) { #if PJ_HAS_INT64 return ((unsigned)((pj_uint64_t)1000000 * \ frame_rate->denum / frame_rate->num)); #elif PJ_HAS_FLOATING_POINT return ((unsigned)(1000000.0 * frame_rate->denum / frame_rate->num)); #else return ((unsigned)((1000L * frame_rate->denum / frame_rate->num) * 1000)); #endif } /** * Utility to retrieve samples_per_frame value from * pjmedia_audio_format_detail. * * @param pafd Pointer to pjmedia_audio_format_detail * @return Samples per frame */ PJ_INLINE(unsigned) PJMEDIA_AFD_SPF(const pjmedia_audio_format_detail *pafd) { return PJMEDIA_SPF(pafd->clock_rate, pafd->frame_time_usec, pafd->channel_count); } /** * Utility to retrieve average frame size from pjmedia_audio_format_detail. * The average frame size is derived from the average bitrate of the audio * stream. * * @param afd Pointer to pjmedia_audio_format_detail * @return Average frame size. */ PJ_INLINE(unsigned) PJMEDIA_AFD_AVG_FSZ(const pjmedia_audio_format_detail *afd) { return PJMEDIA_FSZ(afd->avg_bps, afd->frame_time_usec); } /** * Utility to retrieve maximum frame size from pjmedia_audio_format_detail. * The maximum frame size is derived from the maximum bitrate of the audio * stream. * * @param afd Pointer to pjmedia_audio_format_detail * @return Average frame size. */ PJ_INLINE(unsigned) PJMEDIA_AFD_MAX_FSZ(const pjmedia_audio_format_detail *afd) { return PJMEDIA_FSZ(afd->max_bps, afd->frame_time_usec); } /** * Initialize the format as audio format with the specified parameters. * * @param fmt The format to be initialized. * @param fmt_id Format ID. See #pjmedia_format_id * @param clock_rate Audio clock rate. * @param channel_count Number of channels. * @param bits_per_sample Number of bits per sample. * @param frame_time_usec Frame interval, in microsecond. * @param avg_bps Average bitrate. * @param max_bps Maximum bitrate. */ PJ_INLINE(void) pjmedia_format_init_audio(pjmedia_format *fmt, pj_uint32_t fmt_id, unsigned clock_rate, unsigned channel_count, unsigned bits_per_sample, unsigned frame_time_usec, pj_uint32_t avg_bps, pj_uint32_t max_bps) { /* This function is inlined to avoid build problem due to circular * dependency, i.e: this function is part of pjmedia and is needed * by pjmedia-audiodev, while pjmedia depends on pjmedia-audiodev. */ fmt->id = fmt_id; fmt->type = PJMEDIA_TYPE_AUDIO; fmt->detail_type = PJMEDIA_FORMAT_DETAIL_AUDIO; fmt->det.aud.clock_rate = clock_rate; fmt->det.aud.channel_count = channel_count; fmt->det.aud.bits_per_sample = bits_per_sample; fmt->det.aud.frame_time_usec = frame_time_usec; fmt->det.aud.avg_bps = avg_bps; fmt->det.aud.max_bps = max_bps; } /** * Initialize the format as video format with the specified parameters. * A format manager should have been created, as this function will need * to consult to a format manager in order to fill in detailed * information about the format. * * @param fmt The format to be initialised. * @param fmt_id Format ID. See #pjmedia_format_id * @param width Image width. * @param height Image heigth. * @param fps_num FPS numerator. * @param fps_denum FPS denumerator. * @param avg_bps Average bitrate. * @param max_bps Maximum bitrate. */ PJ_DECL(void) pjmedia_format_init_video(pjmedia_format *fmt, pj_uint32_t fmt_id, unsigned width, unsigned height, unsigned fps_num, unsigned fps_denum); /** * Copy format to another. * * @param dst The destination format. * @param src The source format. * * @return Pointer to destination format. */ PJ_DECL(pjmedia_format*) pjmedia_format_copy(pjmedia_format *dst, const pjmedia_format *src); /** * Check if the format contains audio format, and retrieve the audio format * detail in the format. * * @param fmt The format structure. * @param assert_valid If this is set to non-zero, an assertion will be * raised if the detail type is not audio or if the * the detail is NULL. * * @return The instance of audio format detail in the format * structure, or NULL if the format doesn't contain * audio detail. */ PJ_DECL(pjmedia_audio_format_detail*) pjmedia_format_get_audio_format_detail(const pjmedia_format *fmt, pj_bool_t assert_valid); /** * Check if the format contains video format, and retrieve the video format * detail in the format. * * @param fmt The format structure. * @param assert_valid If this is set to non-zero, an assertion will be * raised if the detail type is not video or if the * the detail is NULL. * * @return The instance of video format detail in the format * structure, or NULL if the format doesn't contain * video detail. */ PJ_DECL(pjmedia_video_format_detail*) pjmedia_format_get_video_format_detail(const pjmedia_format *fmt, pj_bool_t assert_valid); /***************************************************************************** * FORMAT MANAGEMENT: */ /** * Opaque data type for video format manager. The video format manager manages * the repository of video formats that the framework recognises. Typically it * is a singleton instance, although application may instantiate more than one * instances of this if required. */ typedef struct pjmedia_video_format_mgr pjmedia_video_format_mgr; /** * Create a new video format manager instance. This will also set the pointer * to the singleton instance if the value is still NULL. * * @param pool The pool to allocate memory. * @param max_fmt Maximum number of formats to accommodate. * @param options Option flags. Must be zero for now. * @param p_mgr Pointer to hold the created instance. * * @return PJ_SUCCESS on success, or the appripriate error value. */ PJ_DECL(pj_status_t) pjmedia_video_format_mgr_create(pj_pool_t *pool, unsigned max_fmt, unsigned options, pjmedia_video_format_mgr **p_mgr); /** * Get the singleton instance of the video format manager. * * @return The instance. */ PJ_DECL(pjmedia_video_format_mgr*) pjmedia_video_format_mgr_instance(void); /** * Manually assign a specific video manager instance as the singleton * instance. Normally this is not needed if only one instance is ever * going to be created, as the library automatically assign the singleton * instance. * * @param mgr The instance to be used as the singleton instance. * Application may specify NULL to clear the singleton * singleton instance. */ PJ_DECL(void) pjmedia_video_format_mgr_set_instance(pjmedia_video_format_mgr *mgr); /** * Retrieve a video format info for the specified format id. * * @param mgr The video format manager. Specify NULL to use * the singleton instance (however, a video format * manager still must have been created prior to * calling this function). * @param id The format id which format info is to be * retrieved. * * @return The video format info. */ PJ_DECL(const pjmedia_video_format_info*) pjmedia_get_video_format_info(pjmedia_video_format_mgr *mgr, pj_uint32_t id); /** * Register a new video format to the framework. By default, built-in * formats will be registered automatically to the format manager when * it is created (note: built-in formats are ones which format id is * listed in pjmedia_format_id enumeration). This function allows * application to use user defined format id by registering that format * into the framework. * * @param mgr The video format manager. Specify NULL to use * the singleton instance (however, a video format * manager still must have been created prior to * calling this function). * @param vfi The video format info to be registered. This * structure must remain valid until the format * manager is destroyed. * * @return PJ_SUCCESS on success, or the appripriate error value. */ PJ_DECL(pj_status_t) pjmedia_register_video_format_info(pjmedia_video_format_mgr *mgr, pjmedia_video_format_info *vfi); /** * Destroy a video format manager. If the manager happens to be the singleton * instance, the singleton instance will be set to NULL. * * @param mgr The video format manager. Specify NULL to use * the singleton instance (however, a video format * manager still must have been created prior to * calling this function). */ PJ_DECL(void) pjmedia_video_format_mgr_destroy(pjmedia_video_format_mgr *mgr); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_FORMAT_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/frame.h ================================================ /* $Id: frame.h 3715 2011-08-19 09:35:25Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_FRAME_H__ #define __PJMEDIA_FRAME_H__ /** * @file pjmedia/frame.h Media frame * @brief Frame */ #include #include /** * @defgroup PJMEDIA_FRAME Media frame * @ingroup PJMEDIA_TYPES * @brief Frame * @{ */ PJ_BEGIN_DECL /** * Types of media frame. */ typedef enum pjmedia_frame_type { PJMEDIA_FRAME_TYPE_NONE, /**< No frame. */ PJMEDIA_FRAME_TYPE_AUDIO, /**< Normal audio frame. */ PJMEDIA_FRAME_TYPE_EXTENDED, /**< Extended audio frame. */ PJMEDIA_FRAME_TYPE_VIDEO /**< Video frame. */ } pjmedia_frame_type; /** * This structure describes a media frame. */ typedef struct pjmedia_frame { pjmedia_frame_type type; /**< Frame type. */ void *buf; /**< Pointer to buffer. */ pj_size_t size; /**< Frame size in bytes. */ pj_timestamp timestamp; /**< Frame timestamp. */ pj_uint32_t bit_info; /**< Bit info of the frame, sample case: a frame may not exactly start and end at the octet boundary, so this field may be used for specifying start & end bit offset. */ } pjmedia_frame; /** * The pjmedia_frame_ext is used to carry a more complex audio frames than * the typical PCM audio frames, and it is signaled by setting the "type" * field of a pjmedia_frame to PJMEDIA_FRAME_TYPE_EXTENDED. With this set, * application may typecast pjmedia_frame to pjmedia_frame_ext. * * This structure may contain more than one audio frames, which subsequently * will be called subframes in this structure. The subframes section * immediately follows the end of this structure, and each subframe is * represented by pjmedia_frame_ext_subframe structure. Every next * subframe immediately follows the previous subframe, and all subframes * are byte-aligned although its payload may not be byte-aligned. */ #pragma pack(1) typedef struct pjmedia_frame_ext { pjmedia_frame base; /**< Base frame info */ pj_uint16_t samples_cnt; /**< Number of samples in this frame */ pj_uint16_t subframe_cnt; /**< Number of (sub)frames in this frame */ /* Zero or more (sub)frames follows immediately after this, * each will be represented by pjmedia_frame_ext_subframe */ } pjmedia_frame_ext; #pragma pack() /** * This structure represents the individual subframes in the * pjmedia_frame_ext structure. */ #pragma pack(1) typedef struct pjmedia_frame_ext_subframe { pj_uint16_t bitlen; /**< Number of bits in the data */ pj_uint8_t data[1]; /**< Start of encoded data */ } pjmedia_frame_ext_subframe; #pragma pack() /** * Copy one frame to another. If the destination frame's size is smaller than * the source frame's, the destination buffer will be truncated. * * @param src Source frame. * @param dst Destination frame. */ PJ_INLINE(void) pjmedia_frame_copy(pjmedia_frame *dst, const pjmedia_frame *src) { dst->type = src->type; dst->timestamp = src->timestamp; dst->bit_info = src->bit_info; dst->size = (dst->size < src->size? dst->size: src->size); pj_memcpy(dst->buf, src->buf, dst->size); } /** * Append one subframe to #pjmedia_frame_ext. * * @param frm The #pjmedia_frame_ext. * @param src Subframe data. * @param bitlen Length of subframe, in bits. * @param samples_cnt Number of audio samples in subframe. */ PJ_INLINE(void) pjmedia_frame_ext_append_subframe(pjmedia_frame_ext *frm, const void *src, unsigned bitlen, unsigned samples_cnt) { pjmedia_frame_ext_subframe *fsub; pj_uint8_t *p; unsigned i; p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext); for (i = 0; i < frm->subframe_cnt; ++i) { fsub = (pjmedia_frame_ext_subframe*) p; p += sizeof(fsub->bitlen) + ((fsub->bitlen+7) >> 3); } fsub = (pjmedia_frame_ext_subframe*) p; fsub->bitlen = (pj_uint16_t)bitlen; if (bitlen) pj_memcpy(fsub->data, src, (bitlen+7) >> 3); frm->subframe_cnt++; frm->samples_cnt = (pj_uint16_t)(frm->samples_cnt + samples_cnt); } /** * Get a subframe from #pjmedia_frame_ext. * * @param frm The #pjmedia_frame_ext. * @param n Subframe index, zero based. * * @return The n-th subframe, or NULL if n is out-of-range. */ PJ_INLINE(pjmedia_frame_ext_subframe*) pjmedia_frame_ext_get_subframe(const pjmedia_frame_ext *frm, unsigned n) { pjmedia_frame_ext_subframe *sf = NULL; if (n < frm->subframe_cnt) { pj_uint8_t *p; unsigned i; p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext); for (i = 0; i < n; ++i) { sf = (pjmedia_frame_ext_subframe*) p; p += sizeof(sf->bitlen) + ((sf->bitlen+7) >> 3); } sf = (pjmedia_frame_ext_subframe*) p; } return sf; } /** * Extract all frame payload to the specified buffer. * * @param frm The frame. * @param dst Destination buffer. * @param maxlen Maximum size to copy (i.e. the size of the * destination buffer). * * @return Total size of payload copied. */ PJ_INLINE(unsigned) pjmedia_frame_ext_copy_payload(const pjmedia_frame_ext *frm, void *dst, unsigned maxlen) { unsigned i, copied=0; for (i=0; isubframe_cnt; ++i) { pjmedia_frame_ext_subframe *sf; unsigned sz; sf = pjmedia_frame_ext_get_subframe(frm, i); if (!sf) continue; sz = ((sf->bitlen + 7) >> 3); if (sz + copied > maxlen) break; pj_memcpy(((pj_uint8_t*)dst) + copied, sf->data, sz); copied += sz; } return copied; } /** * Pop out first n subframes from #pjmedia_frame_ext. * * @param frm The #pjmedia_frame_ext. * @param n Number of first subframes to be popped out. * * @return PJ_SUCCESS when successful. */ PJ_INLINE(pj_status_t) pjmedia_frame_ext_pop_subframes(pjmedia_frame_ext *frm, unsigned n) { pjmedia_frame_ext_subframe *sf; pj_uint8_t *move_src; pj_size_t move_len; if (frm->subframe_cnt <= n) { frm->subframe_cnt = 0; frm->samples_cnt = 0; return PJ_SUCCESS; } move_src = (pj_uint8_t*)pjmedia_frame_ext_get_subframe(frm, n); sf = pjmedia_frame_ext_get_subframe(frm, frm->subframe_cnt-1); move_len = ((pj_uint8_t*)sf - move_src + sizeof(sf->bitlen) + ((sf->bitlen+7) >> 3)); pj_memmove((pj_uint8_t*)frm+sizeof(pjmedia_frame_ext), move_src, move_len); frm->samples_cnt = (pj_uint16_t) (frm->samples_cnt - n*frm->samples_cnt/frm->subframe_cnt); frm->subframe_cnt = (pj_uint16_t) (frm->subframe_cnt - n); return PJ_SUCCESS; } /** * This is a general purpose function set PCM samples to zero. * Since this function is needed by many parts of the library, * by putting this functionality in one place, it enables some. * clever people to optimize this function. * * @param samples The 16bit PCM samples. * @param count Number of samples. */ PJ_INLINE(void) pjmedia_zero_samples(pj_int16_t *samples, unsigned count) { #if 1 pj_bzero(samples, (count<<1)); #elif 0 unsigned i; for (i=0; i>= 1; for (i=0; i>= 1; for (i=0; i>= 1; for (i=0; i * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_G711_H__ #define __PJMEDIA_G711_H__ /** * @file g711.h * @brief G711 Codec */ #include /** * @defgroup PJMED_G711 G.711 Codec * @ingroup PJMEDIA_CODEC_CODECS * @brief Standard G.711/PCMA and PCMU codec. * @{ * * This section describes functions to initialize and register G.711 codec * factory to the codec manager. After the codec factory has been registered, * application can use @ref PJMEDIA_CODEC API to manipulate the codec. * * The G.711 is an ultra low complexity codecs and in trade-off it results * in high bitrate, i.e: 64kbps for 16-bit PCM with sampling rate 8000Hz. * * The factory contains two main compression algorithms, PCMU/u-Law and * PCMA/A-Law. * * \section codec_setting Codec Settings * * \subsection general_setting General Settings * * General codec settings for this codec such as VAD and PLC can be * manipulated through the setting field in #pjmedia_codec_param. * Please see the documentation of #pjmedia_codec_param for more info. * * \subsection specific_setting Codec Specific Settings * * Currently none. */ PJ_BEGIN_DECL /** * Initialize and register G711 codec factory to pjmedia endpoint. * This will register PCMU and PCMA codec, in that order. * * @param endpt The pjmedia endpoint. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_g711_init(pjmedia_endpt *endpt); /** * Unregister G711 codec factory from pjmedia endpoint. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_g711_deinit(void); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_G711_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/jbuf.h ================================================ /* $Id: jbuf.h 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Based on implementation kindly contributed by Switchlab, Ltd. */ #ifndef __PJMEDIA_JBUF_H__ #define __PJMEDIA_JBUF_H__ /** * @file jbuf.h * @brief Adaptive jitter buffer implementation. */ #include /** * @defgroup PJMED_JBUF Adaptive jitter buffer * @ingroup PJMEDIA_FRAME_OP * @brief Adaptive de-jitter buffering implementation * @{ * * This section describes PJMEDIA's implementation of de-jitter buffer. * The de-jitter buffer may be set to operate in adaptive mode or fixed * delay mode. */ PJ_BEGIN_DECL /** * Types of frame returned by the jitter buffer. */ typedef enum pjmedia_jb_frame_type { PJMEDIA_JB_MISSING_FRAME = 0, /**< No frame because it's missing */ PJMEDIA_JB_NORMAL_FRAME = 1, /**< Normal frame is being returned */ PJMEDIA_JB_ZERO_PREFETCH_FRAME = 2, /**< Zero frame is being returned because JB is bufferring. */ PJMEDIA_JB_ZERO_EMPTY_FRAME = 3 /**< Zero frame is being returned because JB is empty. */ } pjmedia_jb_frame_type; /** * Enumeration of jitter buffer discard algorithm. The jitter buffer * continuously calculates the jitter level to get the optimum latency at * any time and in order to adjust the latency, the jitter buffer may need * to discard some frames. */ typedef enum pjmedia_jb_discard_algo { /** * Jitter buffer should not discard any frame, except when the jitter * buffer is full and a new frame arrives, one frame will be discarded * to make space for the new frame. */ PJMEDIA_JB_DISCARD_NONE = 0, /** * Only discard one frame in at least 200ms when the latency is considered * much higher than it should be. When the jitter buffer is full and a new * frame arrives, one frame will be discarded to make space for the new * frame. */ PJMEDIA_JB_DISCARD_STATIC, /** * The discard rate is dynamically calculated based on actual parameters * such as jitter level and latency. When the jitter buffer is full and * a new frame arrives, one frame will be discarded to make space for the * new frame. */ PJMEDIA_JB_DISCARD_PROGRESSIVE } pjmedia_jb_discard_algo; /** * This structure describes jitter buffer state. */ typedef struct pjmedia_jb_state { /* Setting */ unsigned frame_size; /**< Individual frame size, in bytes. */ unsigned min_prefetch; /**< Minimum allowed prefetch, in frms. */ unsigned max_prefetch; /**< Maximum allowed prefetch, in frms. */ /* Status */ unsigned burst; /**< Current burst level, in frames */ unsigned prefetch; /**< Current prefetch value, in frames */ unsigned size; /**< Current buffer size, in frames. */ /* Statistic */ unsigned avg_delay; /**< Average delay, in ms. */ unsigned min_delay; /**< Minimum delay, in ms. */ unsigned max_delay; /**< Maximum delay, in ms. */ unsigned dev_delay; /**< Standard deviation of delay, in ms.*/ unsigned avg_burst; /**< Average burst, in frames. */ unsigned lost; /**< Number of lost frames. */ unsigned discard; /**< Number of discarded frames. */ unsigned empty; /**< Number of empty on GET events. */ } pjmedia_jb_state; /** * The constant PJMEDIA_JB_DEFAULT_INIT_DELAY specifies default jitter * buffer prefetch count during jitter buffer creation. */ #define PJMEDIA_JB_DEFAULT_INIT_DELAY 15 /** * Opaque declaration for jitter buffer. */ typedef struct pjmedia_jbuf pjmedia_jbuf; /** * Create an adaptive jitter buffer according to the specification. If * application wants to have a fixed jitter buffer, it may call * #pjmedia_jbuf_set_fixed() after the jitter buffer is created. Also * if application wants to alter the discard algorithm, which the default * PJMEDIA_JB_DISCARD_PROGRESSIVE, it may call #pjmedia_jbuf_set_discard(). * * This function may allocate large chunk of memory to keep the frames in * the buffer. * * @param pool The pool to allocate memory. * @param name Name to identify the jitter buffer for logging * purpose. * @param frame_size The size of each frame that will be kept in the * jitter buffer, in bytes. This should correspond * to the minimum frame size supported by the codec. * For example, a 10ms frame (80 bytes) would be * recommended for G.711 codec. * @param max_count Maximum number of frames that can be kept in the * jitter buffer. This effectively means the maximum * delay that may be introduced by this jitter * buffer. * @param ptime Indication of frame duration, used to calculate * the interval between jitter recalculation. * @param p_jb Pointer to receive jitter buffer instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, const pj_str_t *name, unsigned frame_size, unsigned ptime, unsigned max_count, pjmedia_jbuf **p_jb); /** * Set the jitter buffer to fixed delay mode. The default behavior * is to adapt the delay with actual packet delay. * * @param jb The jitter buffer * @param prefetch The fixed delay value, in number of frames. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_jbuf_set_fixed( pjmedia_jbuf *jb, unsigned prefetch); /** * Set the jitter buffer to adaptive mode. * * @param jb The jitter buffer. * @param prefetch The initial prefetch value to be applied to the * jitter buffer. Setting this to other than 0 will * activate prefetch buffering, a jitter buffer feature * that each time it gets empty, it won't return a * normal frame until its size reaches the number * specified here. * @param min_prefetch The minimum delay that must be applied to each * incoming packets, in number of frames. * @param max_prefetch The maximum allowable value for prefetch delay, * in number of frames. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_jbuf_set_adaptive( pjmedia_jbuf *jb, unsigned prefetch, unsigned min_prefetch, unsigned max_prefetch); /** * Set the jitter buffer discard algorithm. The default discard algorithm, * set in jitter buffer creation, is PJMEDIA_JB_DISCARD_PROGRESSIVE. * * @param jb The jitter buffer. * @param algo The discard algorithm to be used. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_jbuf_set_discard(pjmedia_jbuf *jb, pjmedia_jb_discard_algo algo); /** * Destroy jitter buffer instance. * * @param jb The jitter buffer. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_jbuf_destroy(pjmedia_jbuf *jb); /** * Restart jitter. This function flushes all packets in the buffer and * reset the internal sequence number. * * @param jb The jitter buffer. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_jbuf_reset(pjmedia_jbuf *jb); /** * Put a frame to the jitter buffer. If the frame can be accepted (based * on the sequence number), the jitter buffer will copy the frame and put * it in the appropriate position in the buffer. * * Application MUST manage it's own synchronization when multiple threads * are accessing the jitter buffer at the same time. * * @param jb The jitter buffer. * @param frame Pointer to frame buffer to be stored in the jitter * buffer. * @param size The frame size. * @param frame_seq The frame sequence number. */ PJ_DECL(void) pjmedia_jbuf_put_frame( pjmedia_jbuf *jb, const void *frame, pj_size_t size, int frame_seq); /** * Put a frame to the jitter buffer. If the frame can be accepted (based * on the sequence number), the jitter buffer will copy the frame and put * it in the appropriate position in the buffer. * * Application MUST manage it's own synchronization when multiple threads * are accessing the jitter buffer at the same time. * * @param jb The jitter buffer. * @param frame Pointer to frame buffer to be stored in the jitter * buffer. * @param size The frame size. * @param bit_info Bit precise info of the frame, e.g: a frame may not * exactly start and end at the octet boundary, so this * field may be used for specifying start & end bit offset. * @param frame_seq The frame sequence number. * @param discarded Flag whether the frame is discarded by jitter buffer. */ PJ_DECL(void) pjmedia_jbuf_put_frame2( pjmedia_jbuf *jb, const void *frame, pj_size_t size, pj_uint32_t bit_info, int frame_seq, pj_bool_t *discarded); /** * Put a frame to the jitter buffer. If the frame can be accepted (based * on the sequence number), the jitter buffer will copy the frame and put * it in the appropriate position in the buffer. * * Application MUST manage it's own synchronization when multiple threads * are accessing the jitter buffer at the same time. * * @param jb The jitter buffer. * @param frame Pointer to frame buffer to be stored in the jitter * buffer. * @param size The frame size. * @param bit_info Bit precise info of the frame, e.g: a frame may not * exactly start and end at the octet boundary, so this * field may be used for specifying start & end bit offset. * @param frame_seq The frame sequence number. * @param frame_ts The frame timestamp. * @param discarded Flag whether the frame is discarded by jitter buffer. */ PJ_DECL(void) pjmedia_jbuf_put_frame3( pjmedia_jbuf *jb, const void *frame, pj_size_t size, pj_uint32_t bit_info, int frame_seq, pj_uint32_t frame_ts, pj_bool_t *discarded); /** * Get a frame from the jitter buffer. The jitter buffer will return the * oldest frame from it's buffer, when it is available. * * Application MUST manage it's own synchronization when multiple threads * are accessing the jitter buffer at the same time. * * @param jb The jitter buffer. * @param frame Buffer to receive the payload from the jitter buffer. * Application MUST make sure that the buffer has * appropriate size (i.e. not less than the frame size, * as specified when the jitter buffer was created). * The jitter buffer only copied a frame to this * buffer when the frame type returned by this function * is PJMEDIA_JB_NORMAL_FRAME. * @param p_frm_type Pointer to receive frame type. If jitter buffer is * currently empty or bufferring, the frame type will * be set to PJMEDIA_JB_ZERO_FRAME, and no frame will * be copied. If the jitter buffer detects that frame is * missing with current sequence number, the frame type * will be set to PJMEDIA_JB_MISSING_FRAME, and no * frame will be copied. If there is a frame, the jitter * buffer will copy the frame to the buffer, and frame * type will be set to PJMEDIA_JB_NORMAL_FRAME. */ PJ_DECL(void) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb, void *frame, char *p_frm_type); /** * Get a frame from the jitter buffer. The jitter buffer will return the * oldest frame from it's buffer, when it is available. * * @param jb The jitter buffer. * @param frame Buffer to receive the payload from the jitter buffer. * @see pjmedia_jbuf_get_frame(). * @param size Pointer to receive frame size. * @param p_frm_type Pointer to receive frame type. * @see pjmedia_jbuf_get_frame(). * @param bit_info Bit precise info of the frame, e.g: a frame may not * exactly start and end at the octet boundary, so this * field may be used for specifying start & end bit offset. */ PJ_DECL(void) pjmedia_jbuf_get_frame2(pjmedia_jbuf *jb, void *frame, pj_size_t *size, char *p_frm_type, pj_uint32_t *bit_info); /** * Get a frame from the jitter buffer. The jitter buffer will return the * oldest frame from it's buffer, when it is available. * * @param jb The jitter buffer. * @param frame Buffer to receive the payload from the jitter buffer. * @see pjmedia_jbuf_get_frame(). * @param size Pointer to receive frame size. * @param p_frm_type Pointer to receive frame type. * @see pjmedia_jbuf_get_frame(). * @param bit_info Bit precise info of the frame, e.g: a frame may not * exactly start and end at the octet boundary, so this * field may be used for specifying start & end bit offset. * @param ts Frame timestamp. * @param seq Frame sequence number. */ PJ_DECL(void) pjmedia_jbuf_get_frame3(pjmedia_jbuf *jb, void *frame, pj_size_t *size, char *p_frm_type, pj_uint32_t *bit_info, pj_uint32_t *ts, int *seq); /** * Peek a frame from the jitter buffer. The jitter buffer state will not be * modified. * * @param jb The jitter buffer. * @param offset Offset from the oldest frame to be peeked. * @param frame Buffer to receive the payload from the jitter buffer. * @see pjmedia_jbuf_get_frame(). * @param size Pointer to receive frame size. * @param p_frm_type Pointer to receive frame type. * @see pjmedia_jbuf_get_frame(). * @param bit_info Bit precise info of the frame, e.g: a frame may not * exactly start and end at the octet boundary, so this * field may be used for specifying start & end bit offset. * @param ts Frame timestamp. * @param seq Frame sequence number. */ PJ_DECL(void) pjmedia_jbuf_peek_frame(pjmedia_jbuf *jb, unsigned offset, const void **frame, pj_size_t *size, char *p_frm_type, pj_uint32_t *bit_info, pj_uint32_t *ts, int *seq); /** * Remove frames from the jitter buffer. * * @param jb The jitter buffer. * @param frame_cnt Number of frames to be removed. * * @return The number of frame successfully removed. */ PJ_DECL(unsigned) pjmedia_jbuf_remove_frame(pjmedia_jbuf *jb, unsigned frame_cnt); /** * Check if the jitter buffer is full. * * @param jb The jitter buffer. * * @return PJ_TRUE if it is full. */ PJ_DECL(pj_bool_t) pjmedia_jbuf_is_full(const pjmedia_jbuf *jb); /** * Get jitter buffer current state/settings. * * @param jb The jitter buffer. * @param state Buffer to receive jitter buffer state. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_jbuf_get_state( const pjmedia_jbuf *jb, pjmedia_jb_state *state ); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_JBUF_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/master_port.h ================================================ /* $Id: master_port.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_MASTER_PORT_H__ #define __PJMEDIA_MASTER_PORT_H__ /** * @file master_port.h * @brief Master port. */ #include /** * @defgroup PJMEDIA_MASTER_PORT Master Port * @ingroup PJMEDIA_PORT_CLOCK * @brief Thread based media clock provider * @{ * * A master port has two media ports connected to it, and by convention * thay are called downstream and upstream ports. The media stream flowing to * the downstream port is called encoding or send direction, and media stream * flowing to the upstream port is called decoding or receive direction * (imagine the downstream as stream to remote endpoint, and upstream as * local media port; media flowing to remote endpoint (downstream) will need * to be encoded before it is transmitted to remote endpoint). * * A master port internally has an instance of @ref PJMEDIA_CLOCK, which * provides the essensial timing for the master port. The @ref PJMEDIA_CLOCK * runs asynchronously, and whenever a clock tick expires, a callback * will be called, and the master port performs the following tasks: * - it calls get_frame() from the downstream port, * when give the frame to the upstream port by calling put_frame * to the upstream port, and * - performs the same task, but on the reverse direction (i.e. get the stream * from upstream port and give it to the downstream port). * * Because master port enables media stream to flow automatically, it is * said that the master port supplies @ref PJMEDIA_PORT_CLOCK to the * media ports interconnection. * */ PJ_BEGIN_DECL /** * Opaque declaration for master port. */ typedef struct pjmedia_master_port pjmedia_master_port; /** * Create a master port. * * @param pool Pool to allocate master port from. * @param u_port Upstream port. * @param d_port Downstream port. * @param options Options flags, from bitmask combinations from * pjmedia_clock_options. * @param p_m Pointer to receive the master port instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_master_port_create(pj_pool_t *pool, pjmedia_port *u_port, pjmedia_port *d_port, unsigned options, pjmedia_master_port **p_m); /** * Start the media flow. * * @param m The master port. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_master_port_start(pjmedia_master_port *m); /** * Stop the media flow. * * @param m The master port. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_master_port_stop(pjmedia_master_port *m); /** * Poll the master port clock and execute the callback when the clock tick has * elapsed. This operation is only valid if the master port is created with * #PJMEDIA_CLOCK_NO_ASYNC flag. * * @param m The master port. * @param wait If non-zero, then the function will block until * a clock tick elapsed and callback has been called. * @param ts Optional argument to receive the current * timestamp. * * @return Non-zero if clock tick has elapsed, or FALSE if * the function returns before a clock tick has * elapsed. */ PJ_DECL(pj_bool_t) pjmedia_master_port_wait(pjmedia_master_port *m, pj_bool_t wait, pj_timestamp *ts); /** * Change the upstream port. Note that application is responsible to destroy * current upstream port (the one that is going to be replaced with the * new port). * * @param m The master port. * @param port Port to be used for upstream port. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_master_port_set_uport(pjmedia_master_port *m, pjmedia_port *port); /** * Get the upstream port. * * @param m The master port. * * @return The upstream port. */ PJ_DECL(pjmedia_port*) pjmedia_master_port_get_uport(pjmedia_master_port*m); /** * Change the downstream port. Note that application is responsible to destroy * current downstream port (the one that is going to be replaced with the * new port). * * @param m The master port. * @param port Port to be used for downstream port. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_master_port_set_dport(pjmedia_master_port *m, pjmedia_port *port); /** * Get the downstream port. * * @param m The master port. * * @return The downstream port. */ PJ_DECL(pjmedia_port*) pjmedia_master_port_get_dport(pjmedia_master_port*m); /** * Destroy the master port, and optionally destroy the upstream and * downstream ports. * * @param m The master port. * @param destroy_ports If non-zero, the function will destroy both * upstream and downstream ports too. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_master_port_destroy(pjmedia_master_port *m, pj_bool_t destroy_ports); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_MASTER_PORT_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/mem_port.h ================================================ /* $Id: mem_port.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_MEM_PORT_H__ #define __PJMEDIA_MEM_PORT_H__ /** * @file mem_port.h * @brief Memory based media playback/capture port */ #include PJ_BEGIN_DECL /** * @defgroup PJMEDIA_MEM_PLAYER Memory/Buffer-based Playback Port * @ingroup PJMEDIA_PORT * @brief Media playback from a fixed size memory buffer * @{ * * A memory/buffer based playback port is used to play media from a fixed * size buffer. This is useful over @ref PJMEDIA_FILE_PLAY for * situation where filesystems are not available in the target system. */ /** * Memory player options. */ enum pjmedia_mem_player_option { /** * Tell the memory player to return NULL frame when the whole * buffer has been played instead of rewinding the buffer back * to start position. */ PJMEDIA_MEM_NO_LOOP = 1 }; /** * Create the buffer based playback to play the media from the specified * buffer. * * @param pool Pool to allocate memory for the port structure. * @param buffer The buffer to play the media from, which should * be available throughout the life time of the port. * The player plays the media directly from this * buffer (i.e. no copying is done). * @param size The size of the buffer, in bytes. * @param clock_rate Sampling rate. * @param channel_count Number of channels. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Number of bits per sample. * @param options Option flags, see #pjmedia_mem_player_option * @param p_port Pointer to receive the port instance. * * @return PJ_SUCCESS on success, or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_mem_player_create(pj_pool_t *pool, const void *buffer, pj_size_t size, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_port **p_port ); /** * Register a callback to be called when the buffer reading has reached the * end of buffer. If the player is set to play repeatedly, then the callback * will be called multiple times. Note that only one callback can be * registered for each player port. * * @param port The memory player port. * @param user_data User data to be specified in the callback * @param cb Callback to be called. If the callback returns non- * PJ_SUCCESS, the playback will stop. Note that if * application destroys the player port in the callback, * it must return non-PJ_SUCCESS here. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_mem_player_set_eof_cb( pjmedia_port *port, void *user_data, pj_status_t (*cb)(pjmedia_port *port, void *usr_data)); /** * @} */ /** * @defgroup PJMEDIA_MEM_CAPTURE Memory/Buffer-based Capture Port * @ingroup PJMEDIA_PORT * @brief Media capture to fixed size memory buffer * @{ * * A memory based capture is used to save media streams to a fixed size * buffer. This is useful over @ref PJMEDIA_FILE_REC for * situation where filesystems are not available in the target system. */ /** * Create media port to capture/record media into a fixed size buffer. * * @param pool Pool to allocate memory for the port structure. * @param buffer The buffer to record the media to, which should * be available throughout the life time of the port. * @param size The maximum size of the buffer, in bytes. * @param clock_rate Sampling rate. * @param channel_count Number of channels. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Number of bits per sample. * @param options Option flags. * @param p_port Pointer to receive the port instance. * * @return PJ_SUCCESS on success, or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_mem_capture_create(pj_pool_t *pool, void *buffer, pj_size_t size, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_port **p_port); /** * Register a callback to be called when no space left in the buffer. * Note that when a callback is registered, this callback will also be * called when application destroys the port and the callback has not * been called before. * * @param port The memory recorder port. * @param user_data User data to be specified in the callback * @param cb Callback to be called. If the callback returns non- * PJ_SUCCESS, the recording will stop. In other cases * recording will be restarted and the rest of the frame * will be stored starting from the beginning of the * buffer. Note that if application destroys the capture * port in the callback, it must return non-PJ_SUCCESS * here. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_mem_capture_set_eof_cb(pjmedia_port *port, void *user_data, pj_status_t (*cb)(pjmedia_port *port, void *usr_data)); /** * Return the current size of the recorded data in the buffer. * * @param port The memory recorder port. * @return The size of buffer data.. */ PJ_DECL(pj_size_t) pjmedia_mem_capture_get_size(pjmedia_port *port); /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_MEM_PORT_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/mixer_port.h ================================================ /* * Copyright (C) 2010 AG Projects * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_MIXER_PORT_H__ #define __PJMEDIA_MIXER_PORT_H__ /** * @file mixer_port.h * @brief Mixer media port. */ #include /** * @defgroup PJMEDIA_MIXER_PORT Mixer Port * @ingroup PJMEDIA_PORT * @brief The second simplest type of media port which forwards the frames it * gets unchanged. * @{ */ PJ_BEGIN_DECL /** * Create Mixer port. * * @param pool Pool to allocate memory. * @param sampling_rate Sampling rate of the port. * @param channel_count Number of channels. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Number of bits per sample. * @param p_port Pointer to receive the port instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_mixer_port_create(pj_pool_t *pool, unsigned sampling_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_port **p_port); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_MIXER_PORT_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/null_port.h ================================================ /* $Id: null_port.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_NULL_PORT_H__ #define __PJMEDIA_NULL_PORT_H__ /** * @file null_port.h * @brief Null media port. */ #include /** * @defgroup PJMEDIA_NULL_PORT Null Port * @ingroup PJMEDIA_PORT * @brief The simplest type of media port which does nothing. * @{ */ PJ_BEGIN_DECL /** * Create Null port. * * @param pool Pool to allocate memory. * @param sampling_rate Sampling rate of the port. * @param channel_count Number of channels. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Number of bits per sample. * @param p_port Pointer to receive the port instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_null_port_create( pj_pool_t *pool, unsigned sampling_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_port **p_port ); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_NULL_PORT_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/plc.h ================================================ /* $Id: plc.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_PLC_H__ #define __PJMEDIA_PLC_H__ /** * @file plc.h * @brief Packet Lost Concealment (PLC) API. */ #include /** * @defgroup PJMED_PLC Packet Lost Concealment (PLC) * @ingroup PJMEDIA_FRAME_OP * @brief Packet lost compensation algorithm * @{ * * This section describes PJMEDIA's implementation of Packet Lost * Concealment algorithm. This algorithm is used to implement PLC for * codecs that do not have built-in support for one (e.g. G.711 or GSM * codecs). * * The PLC algorithm (either built-in or external) is embedded in * PJMEDIA codec instance, and application can conceal lost frames * by calling recover() member of the codec's member * operation (#pjmedia_codec_op). * * See also @ref plc_codec for more info. */ PJ_BEGIN_DECL /** * Opaque declaration for PLC. */ typedef struct pjmedia_plc pjmedia_plc; /** * Create PLC session. This function will select the PLC algorithm to * use based on the arguments. * * @param pool Pool to allocate memory for the PLC. * @param clock_rate Media sampling rate. * @param samples_per_frame Number of samples per frame. * @param options Must be zero for now. * @param p_plc Pointer to receive the PLC instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_plc_create( pj_pool_t *pool, unsigned clock_rate, unsigned samples_per_frame, unsigned options, pjmedia_plc **p_plc); /** * Save a good frame to PLC. * * @param plc The PLC session. * @param frame The good frame to be stored to PLC. This frame * must have the same length as the configured * samples per frame. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_plc_save( pjmedia_plc *plc, pj_int16_t *frame ); /** * Generate a replacement for lost frame. * * @param plc The PLC session. * @param frame Buffer to receive the generated frame. This buffer * must be able to store the frame. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_plc_generate( pjmedia_plc *plc, pj_int16_t *frame ); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_PLC_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/port.h ================================================ /* $Id: port.h 3893 2011-12-01 10:49:07Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_PORT_H__ #define __PJMEDIA_PORT_H__ /** * @file port.h * @brief Port interface declaration */ #include #include #include #include #include #include #include /** @addtogroup PJMEDIA_PORT Media Ports Framework @{ @section media_port_intro Media Port Concepts @subsection The Media Port A media port (represented with pjmedia_port "class") provides a generic and extensible framework for implementing media elements. Media element itself could be a media source, sink, or processing element. A media port interface basically has the following properties: - media port information (pjmedia_port_info) to describe the media port properties (sampling rate, number of channels, etc.), - optional pointer to function to acquire frames from the port (the get_frame() interface), which will be called by #pjmedia_port_get_frame() public API, and - optional pointer to function to store frames to the port (the put_frame() interface) which will be called by #pjmedia_port_put_frame() public API. The get_frame() and put_frame() interface of course would only need to be implemented if the media port emits and/or takes media frames respectively. Media ports are passive "objects". By default, there is no worker thread to run the media flow. Applications (or other PJMEDIA components, as explained in @ref PJMEDIA_PORT_CLOCK) must actively call #pjmedia_port_get_frame() or #pjmedia_port_put_frame() from/to the media port in order to retrieve/store media frames. Some media ports (such as @ref PJMEDIA_CONF and @ref PJMEDIA_RESAMPLE_PORT) may be interconnected with (or encapsulate) other port, to perform the combined task of the ports, while some others represent the ultimate source/sink termination for the media. Interconnection means the upstream media port will call get_frame() and put_frame() to its downstream media port. For this to happen, the media ports need to have the same format, where format is defined as combination of sample format, clock rate, channel count, bits per sample, and samples per frame for audio media. @subsection port_clock_ex1 Example: Manual Resampling For example, suppose application wants to convert the sampling rate of one WAV file to another. In this case, application would create and arrange media ports connection as follows: \image html sample-manual-resampling.jpg Application would setup the media ports using the following pseudo- code: \code pjmedia_port *player, *resample, *writer; pj_status_t status; // Create the file player port. status = pjmedia_wav_player_port_create(pool, "Input.WAV", // file name 20, // ptime. PJMEDIA_FILE_NO_LOOP, // flags 0, // buffer size NULL, // user data. &player ); PJ_ASSERT_RETURN(status==PJ_SUCCESS, PJ_SUCCESS); // Create the resample port with specifying the target sampling rate, // and with the file port as the source. This will effectively // connect the resample port with the player port. status = pjmedia_resample_port_create( pool, player, 8000, 0, &resample); PJ_ASSERT_RETURN(status==PJ_SUCCESS, PJ_SUCCESS); // Create the file writer, specifying the resample port's configuration // as the WAV parameters. status pjmedia_wav_writer_port_create(pool, "Output.WAV", // file name. resample->info.clock_rate, resample->info.channel_count, resample->info.samples_per_frame, resample->info.bits_per_sample, 0, // flags 0, // buffer size NULL, // user data. &writer); \endcode After the ports have been set up, application can perform the conversion process by running this loop: \code pj_int16_t samplebuf[MAX_FRAME]; while (1) { pjmedia_frame frame; pj_status_t status; frame.buf = samplebuf; frame.size = sizeof(samplebuf); // Get the frame from resample port. status = pjmedia_port_get_frame(resample, &frame); if (status != PJ_SUCCESS || frame.type == PJMEDIA_FRAME_TYPE_NONE) { // End-of-file, end the conversion. break; } // Put the frame to write port. status = pjmedia_port_put_frame(writer, &frame); if (status != PJ_SUCCESS) { // Error in writing the file. break; } } \endcode For the sake of completeness, after the resampling process is done, application would need to destroy the ports: \code // Note: by default, destroying resample port will destroy the // the downstream port too. pjmedia_port_destroy(resample); pjmedia_port_destroy(writer); \endcode The above steps are okay for our simple purpose of changing file's sampling rate. But for other purposes, the process of reading and writing frames need to be done in timely manner (for example, sending RTP packets to remote stream). And more over, as the application's scope goes bigger, the same pattern of manually reading/writing frames comes up more and more often, thus perhaps it would be better if PJMEDIA provides mechanism to automate this process. And indeed PJMEDIA does provide such mechanism, which is described in @ref PJMEDIA_PORT_CLOCK section. @subsection media_port_autom Automating Media Flow PJMEDIA provides few mechanisms to make media flows automatically among media ports. This concept is described in @ref PJMEDIA_PORT_CLOCK section. */ PJ_BEGIN_DECL /** * Create 32bit port signature from ASCII characters. */ #define PJMEDIA_PORT_SIG(a,b,c,d) PJMEDIA_OBJ_SIG(a,b,c,d) /** * Port operation setting. */ typedef enum pjmedia_port_op { /** * No change to the port TX or RX settings. */ PJMEDIA_PORT_NO_CHANGE, /** * TX or RX is disabled from the port. It means get_frame() or * put_frame() WILL NOT be called for this port. */ PJMEDIA_PORT_DISABLE, /** * TX or RX is muted, which means that get_frame() or put_frame() * will still be called, but the audio frame is discarded. */ PJMEDIA_PORT_MUTE, /** * Enable TX and RX to/from this port. */ PJMEDIA_PORT_ENABLE } pjmedia_port_op; /** * Port info. */ typedef struct pjmedia_port_info { pj_str_t name; /**< Port name. */ pj_uint32_t signature; /**< Port signature. */ pjmedia_dir dir; /**< Port direction. */ pjmedia_format fmt; /**< Format. */ } pjmedia_port_info; /** * Utility to retrieve audio clock rate/sampling rate value from * pjmedia_port_info. * * @param pia Pointer to port info containing audio format. * @return Audio clock rate. */ PJ_INLINE(unsigned) PJMEDIA_PIA_SRATE(const pjmedia_port_info *pia) { pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); return pia->fmt.det.aud.clock_rate; } /** * Utility to retrieve audio channel count value from pjmedia_port_info. * * @param pia Pointer to port info containing audio format. * @return Audio channel count. */ PJ_INLINE(unsigned) PJMEDIA_PIA_CCNT(const pjmedia_port_info *pia) { pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); return pia->fmt.det.aud.channel_count; } /** * Utility to retrieve audio bits per sample value from pjmedia_port_info. * * @param pia Pointer to port info containing audio format. * @return Number of bits per sample. */ PJ_INLINE(unsigned) PJMEDIA_PIA_BITS(const pjmedia_port_info *pia) { pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); return pia->fmt.det.aud.bits_per_sample; } /** * Utility to retrieve audio frame interval (ptime) value from * pjmedia_port_info. * * @param pia Pointer to port info containing audio format. * @return Frame interval in msec. */ PJ_INLINE(unsigned) PJMEDIA_PIA_PTIME(const pjmedia_port_info *pia) { pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); return pia->fmt.det.aud.frame_time_usec / 1000; } /** * This is a utility routine to retrieve the audio samples_per_frame value * from port info. * * @param pia Pointer to port info containing audio format. * @return Samples per frame value. */ PJ_INLINE(unsigned) PJMEDIA_PIA_SPF(const pjmedia_port_info *pia) { pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); return PJMEDIA_AFD_SPF(&pia->fmt.det.aud); } /** * This is a utility routine to retrieve the average bitrate value * from port info. * * @param pia Pointer to port info containing audio format. * @return Bitrate, in bits per second. */ PJ_INLINE(unsigned) PJMEDIA_PIA_AVG_BPS(const pjmedia_port_info *pia) { pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); return pia->fmt.det.aud.avg_bps; } /** * This is a utility routine to retrieve the maximum bitrate value * from port info. * * @param pia Pointer to port info containing audio format. * @return Bitrate, in bits per second. */ PJ_INLINE(unsigned) PJMEDIA_PIA_MAX_BPS(const pjmedia_port_info *pia) { pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); return pia->fmt.det.aud.max_bps; } /** * This is a utility routine to retrieve the average audio frame size value * from pjmedia_port_info. * * @param pia Pointer to port info containing audio format. * @return Frame size in bytes. */ PJ_INLINE(unsigned) PJMEDIA_PIA_AVG_FSZ(const pjmedia_port_info *pia) { pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); return PJMEDIA_AFD_AVG_FSZ(&pia->fmt.det.aud); } /** * Utility to retrieve audio frame size from maximum bitrate from * pjmedia_port_info. * * @param pia Pointer to port info containing audio format. * @return Frame size in bytes. */ PJ_INLINE(unsigned) PJMEDIA_PIA_MAX_FSZ(const pjmedia_port_info *pia) { pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO && pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO); return PJMEDIA_AFD_MAX_FSZ(&pia->fmt.det.aud); } /** * Port interface. */ typedef struct pjmedia_port { pjmedia_port_info info; /**< Port information. */ /** Port data can be used by the port creator to attach arbitrary * value to be associated with the port. */ struct port_data { void *pdata; /**< Pointer data. */ long ldata; /**< Long data. */ } port_data; /** * Get clock source. * This should only be called by #pjmedia_port_get_clock_src(). */ pjmedia_clock_src* (*get_clock_src)(struct pjmedia_port *this_port, pjmedia_dir dir); /** * Sink interface. * This should only be called by #pjmedia_port_put_frame(). */ pj_status_t (*put_frame)(struct pjmedia_port *this_port, pjmedia_frame *frame); /** * Source interface. * This should only be called by #pjmedia_port_get_frame(). */ pj_status_t (*get_frame)(struct pjmedia_port *this_port, pjmedia_frame *frame); /** * Called to destroy this port. */ pj_status_t (*on_destroy)(struct pjmedia_port *this_port); } pjmedia_port; /** * This is an auxiliary function to initialize port info for * ports which deal with PCM audio. * * @param info The port info to be initialized. * @param name Port name. * @param signature Port signature. * @param clock_rate Port's clock rate. * @param channel_count Number of channels. * @param bits_per_sample Bits per sample. * @param samples_per_frame Number of samples per frame. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_port_info_init( pjmedia_port_info *info, const pj_str_t *name, unsigned signature, unsigned clock_rate, unsigned channel_count, unsigned bits_per_sample, unsigned samples_per_frame); /** * This is an auxiliary function to initialize port info for * ports which deal with PCM audio. * * @param info The port info to be initialized. * @param name Port name. * @param signature Port signature. * @param dir Port's direction. * @param fmt Port's media format. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_port_info_init2(pjmedia_port_info *info, const pj_str_t *name, unsigned signature, pjmedia_dir dir, const pjmedia_format *fmt); /** * Get a clock source from the port. * * @param port The media port. * @param dir Media port's direction. * * @return The clock source or NULL if clock source is not present * in the port. */ PJ_DECL(pjmedia_clock_src *) pjmedia_port_get_clock_src( pjmedia_port *port, pjmedia_dir dir ); /** * Get a frame from the port (and subsequent downstream ports). * * @param port The media port. * @param frame Frame to store samples. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_port_get_frame( pjmedia_port *port, pjmedia_frame *frame ); /** * Put a frame to the port (and subsequent downstream ports). * * @param port The media port. * @param frame Frame to the put to the port. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_port_put_frame( pjmedia_port *port, pjmedia_frame *frame ); /** * Destroy port (and subsequent downstream ports) * * @param port The media port. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_port_destroy( pjmedia_port *port ); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_PORT_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/resample.h ================================================ /* $Id: resample.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_RESAMPLE_H__ #define __PJMEDIA_RESAMPLE_H__ /** * @file resample.h * @brief Sample rate converter. */ #include #include /** * @defgroup PJMEDIA_RESAMPLE Resampling Algorithm * @ingroup PJMEDIA_FRAME_OP * @brief Sample rate conversion algorithm * @{ * * This section describes the base resampling functions. In addition to this, * application can use the @ref PJMEDIA_RESAMPLE_PORT which provides * media port abstraction for the base resampling algorithm. */ PJ_BEGIN_DECL /* * This file declares two types of API: * * Application can use #pjmedia_resample_create() and #pjmedia_resample_run() * to convert a frame from source rate to destination rate. The inpuit frame * must have a constant length. * * Alternatively, application can create a resampling port with * #pjmedia_resample_port_create() and connect the port to other ports to * change the sampling rate of the samples. */ /** * Opaque resample session. */ typedef struct pjmedia_resample pjmedia_resample; /** * Create a frame based resample session. * * @param pool Pool to allocate the structure and buffers. * @param high_quality If true, then high quality conversion will be * used, at the expense of more CPU and memory, * because temporary buffer needs to be created. * @param large_filter If true, large filter size will be used. * @param channel_count Number of channels. * @param rate_in Clock rate of the input samples. * @param rate_out Clock rate of the output samples. * @param samples_per_frame Number of samples per frame in the input. * @param p_resample Pointer to receive the resample session. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_resample_create(pj_pool_t *pool, pj_bool_t high_quality, pj_bool_t large_filter, unsigned channel_count, unsigned rate_in, unsigned rate_out, unsigned samples_per_frame, pjmedia_resample **p_resample); /** * Use the resample session to resample a frame. The frame must have the * same size and settings as the resample session, or otherwise the * behavior is undefined. * * @param resample The resample session. * @param input Buffer containing the input samples. * @param output Buffer to store the output samples. */ PJ_DECL(void) pjmedia_resample_run( pjmedia_resample *resample, const pj_int16_t *input, pj_int16_t *output ); /** * Get the input frame size of a resample session. * * @param resample The resample session. * * @return The frame size, in number of samples. */ PJ_DECL(unsigned) pjmedia_resample_get_input_size(pjmedia_resample *resample); /** * Destroy the resample. * * @param resample The resample session. */ PJ_DECL(void) pjmedia_resample_destroy(pjmedia_resample *resample); /** * @} */ /** * @defgroup PJMEDIA_RESAMPLE_PORT Resample Port * @ingroup PJMEDIA_PORT * @brief Audio sample rate conversion * @{ * * This section describes media port abstraction for @ref PJMEDIA_RESAMPLE. */ /** * Option flags that can be specified when creating resample port. */ enum pjmedia_resample_port_options { /** * Do not use high quality resampling algorithm, but use linear * algorithm instead. */ PJMEDIA_RESAMPLE_USE_LINEAR = 1, /** * Use small filter workspace when high quality resampling is * used. */ PJMEDIA_RESAMPLE_USE_SMALL_FILTER = 2, /** * Do not destroy downstream port when resample port is destroyed. */ PJMEDIA_RESAMPLE_DONT_DESTROY_DN = 4 }; /** * Create a resample port. This creates a bidirectional resample session, * which will resample frames when the port's get_frame() and put_frame() * is called. * * When the resample port's get_frame() is called, this port will get * a frame from the downstream port and resample the frame to the target * clock rate before returning it to the caller. * * When the resample port's put_frame() is called, this port will resample * the frame to the downstream port's clock rate before giving the frame * to the downstream port. * * @param pool Pool to allocate the structure and buffers. * @param dn_port The downstream port, which clock rate is to * be converted to the target clock rate. * @param clock_rate Target clock rate. * @param options Flags from #pjmedia_resample_port_options. * When this flag is zero, the default behavior * is to use high quality resampling with * large filter, and to destroy downstream port * when resample port is destroyed. * @param p_port Pointer to receive the resample port instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_resample_port_create( pj_pool_t *pool, pjmedia_port *dn_port, unsigned clock_rate, unsigned options, pjmedia_port **p_port ); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_RESAMPLE_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/rtcp.h ================================================ /* $Id: rtcp.h 3999 2012-03-30 07:10:13Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_RTCP_H__ #define __PJMEDIA_RTCP_H__ /** * @file rtcp.h * @brief RTCP implementation. */ #include #include #include PJ_BEGIN_DECL /** * @defgroup PJMED_RTCP RTCP Session and Encapsulation (RFC 3550) * @ingroup PJMEDIA_SESSION * @brief RTCP format and session management * @{ * * PJMEDIA implements subsets of RTCP specification (RFC 3550) to monitor * the quality of the real-time media (audio/video) transmission. In * addition to the standard quality monitoring and reporting with RTCP * SR and RR types, PJMEDIA's RTCP implementation is able to report * extended statistics for incoming streams, such as packet duplications, * reorder, discarded, and loss period (to distinguish between random * and burst loss). * * The bidirectional media quality statistic is represented with * #pjmedia_rtcp_stat structure. * * When application uses the stream interface (see @ref PJMED_STRM), * application may retrieve the RTCP statistic by calling * #pjmedia_stream_get_stat() function. */ #pragma pack(1) /** * RTCP sender report. */ typedef struct pjmedia_rtcp_sr { pj_uint32_t ntp_sec; /**< NTP time, seconds part. */ pj_uint32_t ntp_frac; /**< NTP time, fractions part. */ pj_uint32_t rtp_ts; /**< RTP timestamp. */ pj_uint32_t sender_pcount; /**< Sender packet cound. */ pj_uint32_t sender_bcount; /**< Sender octet/bytes count. */ } pjmedia_rtcp_sr; /** * RTCP receiver report. */ typedef struct pjmedia_rtcp_rr { pj_uint32_t ssrc; /**< SSRC identification. */ #if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 pj_uint32_t fract_lost:8; /**< Fraction lost. */ pj_uint32_t total_lost_2:8; /**< Total lost, bit 16-23. */ pj_uint32_t total_lost_1:8; /**< Total lost, bit 8-15. */ pj_uint32_t total_lost_0:8; /**< Total lost, bit 0-7. */ #else pj_uint32_t fract_lost:8; /**< Fraction lost. */ pj_uint32_t total_lost_2:8; /**< Total lost, bit 0-7. */ pj_uint32_t total_lost_1:8; /**< Total lost, bit 8-15. */ pj_uint32_t total_lost_0:8; /**< Total lost, bit 16-23. */ #endif pj_uint32_t last_seq; /**< Last sequence number. */ pj_uint32_t jitter; /**< Jitter. */ pj_uint32_t lsr; /**< Last SR. */ pj_uint32_t dlsr; /**< Delay since last SR. */ } pjmedia_rtcp_rr; /** * RTCP common header. */ typedef struct pjmedia_rtcp_common { #if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 unsigned version:2; /**< packet type */ unsigned p:1; /**< padding flag */ unsigned count:5; /**< varies by payload type */ unsigned pt:8; /**< payload type */ #else unsigned count:5; /**< varies by payload type */ unsigned p:1; /**< padding flag */ unsigned version:2; /**< packet type */ unsigned pt:8; /**< payload type */ #endif unsigned length:16; /**< packet length */ pj_uint32_t ssrc; /**< SSRC identification */ } pjmedia_rtcp_common; /** * This structure declares default RTCP packet (SR) that is sent by pjmedia. * Incoming RTCP packet may have different format, and must be parsed * manually by application. */ typedef struct pjmedia_rtcp_sr_pkt { pjmedia_rtcp_common common; /**< Common header. */ pjmedia_rtcp_sr sr; /**< Sender report. */ pjmedia_rtcp_rr rr; /**< variable-length list */ } pjmedia_rtcp_sr_pkt; /** * This structure declares RTCP RR (Receiver Report) packet. */ typedef struct pjmedia_rtcp_rr_pkt { pjmedia_rtcp_common common; /**< Common header. */ pjmedia_rtcp_rr rr; /**< variable-length list */ } pjmedia_rtcp_rr_pkt; #pragma pack() /** * RTCP SDES structure. */ typedef struct pjmedia_rtcp_sdes { pj_str_t cname; /**< RTCP SDES type CNAME. */ pj_str_t name; /**< RTCP SDES type NAME. */ pj_str_t email; /**< RTCP SDES type EMAIL. */ pj_str_t phone; /**< RTCP SDES type PHONE. */ pj_str_t loc; /**< RTCP SDES type LOC. */ pj_str_t tool; /**< RTCP SDES type TOOL. */ pj_str_t note; /**< RTCP SDES type NOTE. */ } pjmedia_rtcp_sdes; /** * NTP time representation. */ typedef struct pjmedia_rtcp_ntp_rec { pj_uint32_t hi; /**< High order 32-bit part. */ pj_uint32_t lo; /**< Lo order 32-bit part. */ } pjmedia_rtcp_ntp_rec; /** * Unidirectional RTP stream statistics. */ typedef struct pjmedia_rtcp_stream_stat { pj_time_val update; /**< Time of last update. */ unsigned update_cnt; /**< Number of updates (to calculate avg) */ pj_uint32_t pkt; /**< Total number of packets */ pj_uint32_t bytes; /**< Total number of payload/bytes */ unsigned discard; /**< Total number of discarded packets. */ unsigned loss; /**< Total number of packets lost */ unsigned reorder; /**< Total number of out of order packets */ unsigned dup; /**< Total number of duplicates packets */ pj_math_stat loss_period;/**< Loss period statistics (in usec) */ struct { unsigned burst:1; /**< Burst/sequential packet lost detected */ unsigned random:1; /**< Random packet lost detected. */ } loss_type; /**< Types of loss detected. */ pj_math_stat jitter; /**< Jitter statistics (in usec) */ } pjmedia_rtcp_stream_stat; /** * Bidirectional RTP stream statistics. */ typedef struct pjmedia_rtcp_stat { pj_time_val start; /**< Time when session was created */ pjmedia_rtcp_stream_stat tx; /**< Encoder stream statistics. */ pjmedia_rtcp_stream_stat rx; /**< Decoder stream statistics. */ pj_math_stat rtt; /**< Round trip delay statistic(in usec)*/ pj_uint32_t rtp_tx_last_ts; /**< Last TX RTP timestamp. */ pj_uint16_t rtp_tx_last_seq;/**< Last TX RTP sequence. */ #if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0 pj_math_stat rx_ipdv;/**< Statistics of IP packet delay variation in receiving direction (in usec). */ #endif #if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0 pj_math_stat rx_raw_jitter;/**< Statistic of raw jitter in receiving direction (in usec). */ #endif pjmedia_rtcp_sdes peer_sdes; /**< Peer SDES. */ char peer_sdes_buf_[PJMEDIA_RTCP_RX_SDES_BUF_LEN]; /**< Peer SDES buffer. */ } pjmedia_rtcp_stat; /** * RTCP session is used to monitor the RTP session of one endpoint. There * should only be one RTCP session for a bidirectional RTP streams. */ typedef struct pjmedia_rtcp_session { char *name; /**< Name identification. */ pjmedia_rtcp_sr_pkt rtcp_sr_pkt;/**< Cached RTCP SR packet. */ pjmedia_rtcp_rr_pkt rtcp_rr_pkt;/**< Cached RTCP RR packet. */ pjmedia_rtp_seq_session seq_ctrl; /**< RTCP sequence number control. */ unsigned rtp_last_ts;/**< Last timestamp in RX RTP pkt. */ unsigned clock_rate; /**< Clock rate of the stream */ unsigned pkt_size; /**< Avg pkt size, in samples. */ pj_uint32_t received; /**< # pkt received */ pj_uint32_t exp_prior; /**< # pkt expected at last interval*/ pj_uint32_t rx_prior; /**< # pkt received at last interval*/ pj_int32_t transit; /**< Rel transit time for prev pkt */ pj_uint32_t jitter; /**< Scaled jitter */ pj_time_val tv_base; /**< Base time, in seconds. */ pj_timestamp ts_base; /**< Base system timestamp. */ pj_timestamp ts_freq; /**< System timestamp frequency. */ pj_uint32_t rtp_ts_base;/**< Base RTP timestamp. */ pj_uint32_t rx_lsr; /**< NTP ts in last SR received */ pj_timestamp rx_lsr_time;/**< Time when last SR is received */ pj_uint32_t peer_ssrc; /**< Peer SSRC */ pjmedia_rtcp_stat stat; /**< Bidirectional stream stat. */ pj_bool_t keyframe_requested; /** Set to true when RTCP PLI is received */ #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) /** * Specify whether RTCP XR processing is enabled on this session. */ pj_bool_t xr_enabled; /** * RTCP XR session, only valid if RTCP XR processing is enabled * on this session. */ pjmedia_rtcp_xr_session xr_session; #endif } pjmedia_rtcp_session; /** * RTCP session settings. */ typedef struct pjmedia_rtcp_session_setting { char *name; /**< RTCP session name. */ unsigned clock_rate; /**< Sequence. */ unsigned samples_per_frame; /**< Timestamp. */ pj_uint32_t ssrc; /**< Sender SSRC. */ pj_uint32_t rtp_ts_base; /**< Base RTP timestamp. */ } pjmedia_rtcp_session_setting; /** * Initialize RTCP session setting. * * @param settings The RTCP session setting to be initialized. */ PJ_DECL(void) pjmedia_rtcp_session_setting_default( pjmedia_rtcp_session_setting *settings); /** * Initialize bidirectional RTCP statistics. * * @param stat The bidirectional RTCP statistics. */ PJ_DECL(void) pjmedia_rtcp_init_stat(pjmedia_rtcp_stat *stat); /** * Initialize RTCP session. * * @param session The session * @param name Optional name to identify the session (for * logging purpose). * @param clock_rate Codec clock rate in samples per second. * @param samples_per_frame Average number of samples per frame. * @param ssrc The SSRC used in to identify the session. */ PJ_DECL(void) pjmedia_rtcp_init( pjmedia_rtcp_session *session, char *name, unsigned clock_rate, unsigned samples_per_frame, pj_uint32_t ssrc ); /** * Initialize RTCP session. * * @param session The session * @param settings The RTCP session settings. */ PJ_DECL(void) pjmedia_rtcp_init2(pjmedia_rtcp_session *session, const pjmedia_rtcp_session_setting *settings); /** * Utility function to retrieve current NTP timestamp. * * @param sess RTCP session. * @param ntp NTP record. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_rtcp_get_ntp_time(const pjmedia_rtcp_session *sess, pjmedia_rtcp_ntp_rec *ntp); /** * Deinitialize RTCP session. * * @param session The session. */ PJ_DECL(void) pjmedia_rtcp_fini( pjmedia_rtcp_session *session); /** * Call this function everytime an RTP packet is received to let the RTCP * session do its internal calculations. * * @param session The session. * @param seq The RTP packet sequence number, in host byte order. * @param ts The RTP packet timestamp, in host byte order. * @param payload Size of the payload. */ PJ_DECL(void) pjmedia_rtcp_rx_rtp( pjmedia_rtcp_session *session, unsigned seq, unsigned ts, unsigned payload); /** * Call this function everytime an RTP packet is received to let the RTCP * session do its internal calculations. * * @param session The session. * @param seq The RTP packet sequence number, in host byte order. * @param ts The RTP packet timestamp, in host byte order. * @param payload Size of the payload. * @param discarded Flag to specify whether the packet is discarded. */ PJ_DECL(void) pjmedia_rtcp_rx_rtp2(pjmedia_rtcp_session *session, unsigned seq, unsigned ts, unsigned payload, pj_bool_t discarded); /** * Call this function everytime an RTP packet is sent to let the RTCP session * do its internal calculations. * * @param session The session. * @param ptsize The payload size of the RTP packet (ie packet minus * RTP header) in bytes. */ PJ_DECL(void) pjmedia_rtcp_tx_rtp( pjmedia_rtcp_session *session, unsigned ptsize ); /** * Call this function when an RTCP packet is received from remote peer. * This RTCP packet received from remote is used to calculate the end-to- * end delay of the network. * * @param session RTCP session. * @param rtcp_pkt The received RTCP packet. * @param size Size of the incoming packet. */ PJ_DECL(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *session, const void *rtcp_pkt, pj_size_t size); /** * Build a RTCP packet to be transmitted to remote RTP peer. This will * create RTCP Sender Report (SR) or Receiver Report (RR) depending on * whether the endpoint has been transmitting RTP since the last interval. * Note that this function will reset the interval counters (such as * the ones to calculate fraction lost) in the session. * * @param session The RTCP session. * @param rtcp_pkt Upon return, it will contain pointer to the * RTCP packet, which can be RTCP SR or RR. * @param len Upon return, it will indicate the size of * the RTCP packet. */ PJ_DECL(void) pjmedia_rtcp_build_rtcp( pjmedia_rtcp_session *session, void **rtcp_pkt, int *len); /** * Build an RTCP SDES (source description) packet. This packet can be * appended to other RTCP packets, e.g: RTCP RR/SR, to compose a compound * RTCP packet. * * @param session The RTCP session. * @param buf The buffer to receive RTCP SDES packet. * @param length On input, it will contain the buffer length. * On output, it will contain the generated RTCP SDES * packet length. * @param sdes The source description, see #pjmedia_rtcp_sdes. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_rtcp_build_rtcp_sdes( pjmedia_rtcp_session *session, void *buf, pj_size_t *length, const pjmedia_rtcp_sdes *sdes); /** * Build an RTCP BYE packet. This packet can be appended to other RTCP * packets, e.g: RTCP RR/SR, to compose a compound RTCP packet. * * @param session The RTCP session. * @param buf The buffer to receive RTCP BYE packet. * @param length On input, it will contain the buffer length. * On output, it will contain the generated RTCP BYE * packet length. * @param reason Optional, the BYE reason. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_rtcp_build_rtcp_bye( pjmedia_rtcp_session *session, void *buf, pj_size_t *length, const pj_str_t *reason); /** * Build an RTCP PLI packet. This packet can be appended to other RTCP * packets, e.g: RTCP RR/SR, to compose a compound RTCP packet. * * @param session The RTCP session. * @param buf The buffer to receive RTCP PLI packet. * @param length On input, it will contain the buffer length. * On output, it will contain the generated RTCP PLI * packet length. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_rtcp_build_rtcp_pli( pjmedia_rtcp_session *session, void *buf, pj_size_t *length); /** * Call this function if RTCP XR needs to be enabled/disabled in the * RTCP session. * * @param session The RTCP session. * @param enable Enable/disable RTCP XR. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_rtcp_enable_xr( pjmedia_rtcp_session *session, pj_bool_t enable); /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_RTCP_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/rtcp_xr.h ================================================ /* $Id: rtcp_xr.h 3999 2012-03-30 07:10:13Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_RTCP_XR_H__ #define __PJMEDIA_RTCP_XR_H__ /** * @file rtcp_xr.h * @brief RTCP XR implementation. */ #include #include PJ_BEGIN_DECL /** * @defgroup PJMED_RTCP_XR RTCP Extended Report (XR) - RFC 3611 * @ingroup PJMEDIA_SESSION * @brief RTCP XR extension to RTCP session * @{ * * PJMEDIA implements subsets of RTCP XR specification (RFC 3611) to monitor * the quality of the real-time media (audio/video) transmission. */ /** * Enumeration of report types of RTCP XR. Useful for user to enable varying * combinations of RTCP XR report blocks. */ typedef enum { PJMEDIA_RTCP_XR_LOSS_RLE = (1 << 0), PJMEDIA_RTCP_XR_DUP_RLE = (1 << 1), PJMEDIA_RTCP_XR_RCPT_TIMES = (1 << 2), PJMEDIA_RTCP_XR_RR_TIME = (1 << 3), PJMEDIA_RTCP_XR_DLRR = (1 << 4), PJMEDIA_RTCP_XR_STATS = (1 << 5), PJMEDIA_RTCP_XR_VOIP_METRICS = (1 << 6) } pjmedia_rtcp_xr_type; /** * Enumeration of info need to be updated manually to RTCP XR. Most info * could be updated automatically each time RTP received. */ typedef enum { PJMEDIA_RTCP_XR_INFO_SIGNAL_LVL = 1, PJMEDIA_RTCP_XR_INFO_NOISE_LVL = 2, PJMEDIA_RTCP_XR_INFO_RERL = 3, PJMEDIA_RTCP_XR_INFO_R_FACTOR = 4, PJMEDIA_RTCP_XR_INFO_MOS_LQ = 5, PJMEDIA_RTCP_XR_INFO_MOS_CQ = 6, PJMEDIA_RTCP_XR_INFO_CONF_PLC = 7, PJMEDIA_RTCP_XR_INFO_CONF_JBA = 8, PJMEDIA_RTCP_XR_INFO_CONF_JBR = 9, PJMEDIA_RTCP_XR_INFO_JB_NOM = 10, PJMEDIA_RTCP_XR_INFO_JB_MAX = 11, PJMEDIA_RTCP_XR_INFO_JB_ABS_MAX = 12 } pjmedia_rtcp_xr_info; /** * Enumeration of PLC types definitions for RTCP XR report. */ typedef enum { PJMEDIA_RTCP_XR_PLC_UNK = 0, PJMEDIA_RTCP_XR_PLC_DIS = 1, PJMEDIA_RTCP_XR_PLC_ENH = 2, PJMEDIA_RTCP_XR_PLC_STD = 3 } pjmedia_rtcp_xr_plc_type; /** * Enumeration of jitter buffer types definitions for RTCP XR report. */ typedef enum { PJMEDIA_RTCP_XR_JB_UNKNOWN = 0, PJMEDIA_RTCP_XR_JB_FIXED = 2, PJMEDIA_RTCP_XR_JB_ADAPTIVE = 3 } pjmedia_rtcp_xr_jb_type; #pragma pack(1) /** * This type declares RTCP XR Report Header. */ typedef struct pjmedia_rtcp_xr_rb_header { pj_uint8_t bt; /**< Block type. */ pj_uint8_t specific; /**< Block specific data. */ pj_uint16_t length; /**< Block length. */ } pjmedia_rtcp_xr_rb_header; /** * This type declares RTCP XR Receiver Reference Time Report Block. */ typedef struct pjmedia_rtcp_xr_rb_rr_time { pjmedia_rtcp_xr_rb_header header; /**< Block header. */ pj_uint32_t ntp_sec; /**< NTP time, seconds part. */ pj_uint32_t ntp_frac; /**< NTP time, fractions part. */ } pjmedia_rtcp_xr_rb_rr_time; /** * This type declares RTCP XR DLRR Report Sub-block */ typedef struct pjmedia_rtcp_xr_rb_dlrr_item { pj_uint32_t ssrc; /**< receiver SSRC */ pj_uint32_t lrr; /**< last receiver report */ pj_uint32_t dlrr; /**< delay since last receiver report */ } pjmedia_rtcp_xr_rb_dlrr_item; /** * This type declares RTCP XR DLRR Report Block */ typedef struct pjmedia_rtcp_xr_rb_dlrr { pjmedia_rtcp_xr_rb_header header; /**< Block header. */ pjmedia_rtcp_xr_rb_dlrr_item item; /**< Block contents, variable length list */ } pjmedia_rtcp_xr_rb_dlrr; /** * This type declares RTCP XR Statistics Summary Report Block */ typedef struct pjmedia_rtcp_xr_rb_stats { pjmedia_rtcp_xr_rb_header header; /**< Block header. */ pj_uint32_t ssrc; /**< Receiver SSRC */ pj_uint16_t begin_seq; /**< Begin RTP sequence reported */ pj_uint16_t end_seq; /**< End RTP sequence reported */ pj_uint32_t lost; /**< Number of packet lost in this interval */ pj_uint32_t dup; /**< Number of duplicated packet in this interval */ pj_uint32_t jitter_min; /**< Minimum jitter in this interval */ pj_uint32_t jitter_max; /**< Maximum jitter in this interval */ pj_uint32_t jitter_mean; /**< Average jitter in this interval */ pj_uint32_t jitter_dev; /**< Jitter deviation in this interval */ pj_uint32_t toh_min:8; /**< Minimum ToH in this interval */ pj_uint32_t toh_max:8; /**< Maximum ToH in this interval */ pj_uint32_t toh_mean:8; /**< Average ToH in this interval */ pj_uint32_t toh_dev:8; /**< ToH deviation in this interval */ } pjmedia_rtcp_xr_rb_stats; /** * This type declares RTCP XR VoIP Metrics Report Block */ typedef struct pjmedia_rtcp_xr_rb_voip_mtc { pjmedia_rtcp_xr_rb_header header; /**< Block header. */ pj_uint32_t ssrc; /**< Receiver SSRC */ pj_uint8_t loss_rate; /**< Packet loss rate */ pj_uint8_t discard_rate; /**< Packet discarded rate */ pj_uint8_t burst_den; /**< Burst density */ pj_uint8_t gap_den; /**< Gap density */ pj_uint16_t burst_dur; /**< Burst duration */ pj_uint16_t gap_dur; /**< Gap duration */ pj_uint16_t rnd_trip_delay;/**< Round trip delay */ pj_uint16_t end_sys_delay; /**< End system delay */ pj_uint8_t signal_lvl; /**< Signal level */ pj_uint8_t noise_lvl; /**< Noise level */ pj_uint8_t rerl; /**< Residual Echo Return Loss */ pj_uint8_t gmin; /**< The gap threshold */ pj_uint8_t r_factor; /**< Voice quality metric carried over this RTP session */ pj_uint8_t ext_r_factor; /**< Voice quality metric carried outside of this RTP session*/ pj_uint8_t mos_lq; /**< Mean Opinion Score for Listening Quality */ pj_uint8_t mos_cq; /**< Mean Opinion Score for Conversation Quality */ pj_uint8_t rx_config; /**< Receiver configuration */ pj_uint8_t reserved2; /**< Not used */ pj_uint16_t jb_nom; /**< Current delay by jitter buffer */ pj_uint16_t jb_max; /**< Maximum delay by jitter buffer */ pj_uint16_t jb_abs_max; /**< Maximum possible delay by jitter buffer */ } pjmedia_rtcp_xr_rb_voip_mtc; /** * Constant of RTCP-XR content size. */ #define PJMEDIA_RTCP_XR_BUF_SIZE \ sizeof(pjmedia_rtcp_xr_rb_rr_time) + \ sizeof(pjmedia_rtcp_xr_rb_dlrr) + \ sizeof(pjmedia_rtcp_xr_rb_stats) + \ sizeof(pjmedia_rtcp_xr_rb_voip_mtc) /** * This structure declares RTCP XR (Extended Report) packet. */ typedef struct pjmedia_rtcp_xr_pkt { struct { #if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 unsigned version:2; /**< packet type */ unsigned p:1; /**< padding flag */ unsigned count:5; /**< varies by payload type */ unsigned pt:8; /**< payload type */ #else unsigned count:5; /**< varies by payload type */ unsigned p:1; /**< padding flag */ unsigned version:2; /**< packet type */ unsigned pt:8; /**< payload type */ #endif unsigned length:16; /**< packet length */ pj_uint32_t ssrc; /**< SSRC identification */ } common; pj_int8_t buf[PJMEDIA_RTCP_XR_BUF_SIZE]; /**< Content buffer */ } pjmedia_rtcp_xr_pkt; #pragma pack() /** * This structure describes RTCP XR statitic. */ typedef struct pjmedia_rtcp_xr_stream_stat { struct { pj_time_val update; /**< Time of last update. */ pj_uint32_t begin_seq; /**< Begin # seq of this interval. */ pj_uint32_t end_seq; /**< End # seq of this interval. */ unsigned count; /**< Number of packets. */ /** * Flags represent whether the such report is valid/updated */ unsigned l:1; /**< Lost flag */ unsigned d:1; /**< Duplicated flag */ unsigned j:1; /**< Jitter flag */ unsigned t:2; /**< TTL or Hop Limit, 0=none, 1=TTL, 2=HL */ unsigned lost; /**< Number of packets lost */ unsigned dup; /**< Number of duplicated packets */ pj_math_stat jitter; /**< Jitter statistics (in usec) */ pj_math_stat toh; /**< TTL of hop limit statistics. */ } stat_sum; struct { pj_time_val update; /**< Time of last update. */ pj_uint8_t loss_rate; /**< Packet loss rate */ pj_uint8_t discard_rate; /**< Packet discarded rate */ pj_uint8_t burst_den; /**< Burst density */ pj_uint8_t gap_den; /**< Gap density */ pj_uint16_t burst_dur; /**< Burst duration */ pj_uint16_t gap_dur; /**< Gap duration */ pj_uint16_t rnd_trip_delay; /**< Round trip delay */ pj_uint16_t end_sys_delay; /**< End system delay */ pj_int8_t signal_lvl; /**< Signal level */ pj_int8_t noise_lvl; /**< Noise level */ pj_uint8_t rerl; /**< Residual Echo Return Loss */ pj_uint8_t gmin; /**< The gap threshold */ pj_uint8_t r_factor; /**< Voice quality metric carried over this RTP session */ pj_uint8_t ext_r_factor; /**< Voice quality metric carried outside of this RTP session*/ pj_uint8_t mos_lq; /**< Mean Opinion Score for Listening Quality */ pj_uint8_t mos_cq; /**< Mean Opinion Score for Conversation Quality */ pj_uint8_t rx_config; /**< Receiver configuration */ pj_uint16_t jb_nom; /**< Current delay by jitter buffer */ pj_uint16_t jb_max; /**< Maximum delay by jitter buffer */ pj_uint16_t jb_abs_max; /**< Maximum possible delay by jitter buffer */ } voip_mtc; } pjmedia_rtcp_xr_stream_stat; typedef struct pjmedia_rtcp_xr_stat { pjmedia_rtcp_xr_stream_stat rx; /**< Decoding direction statistics. */ pjmedia_rtcp_xr_stream_stat tx; /**< Encoding direction statistics. */ pj_math_stat rtt; /**< Round-trip delay stat (in usec) the value is calculated from receiver side. */ } pjmedia_rtcp_xr_stat; /** * Forward declaration of RTCP session */ struct pjmedia_rtcp_session; /** * RTCP session is used to monitor the RTP session of one endpoint. There * should only be one RTCP session for a bidirectional RTP streams. */ struct pjmedia_rtcp_xr_session { char *name; /**< Name identification. */ pjmedia_rtcp_xr_pkt pkt; /**< Cached RTCP XR packet. */ pj_uint32_t rx_lrr; /**< NTP ts in last RR received. */ pj_timestamp rx_lrr_time;/**< Time when last RR is received. */ pj_uint32_t rx_last_rr; /**< # pkt received since last sending RR time. */ pjmedia_rtcp_xr_stat stat; /**< RTCP XR statistics. */ /* The reference sequence number is an extended sequence number * that serves as the basis for determining whether a new 16 bit * sequence number comes earlier or later in the 32 bit sequence * space. */ pj_uint32_t src_ref_seq; pj_bool_t uninitialized_src_ref_seq; /* This structure contains variables needed for calculating * burst metrics. */ struct { pj_uint32_t pkt; pj_uint32_t lost; pj_uint32_t loss_count; pj_uint32_t discard_count; pj_uint32_t c11; pj_uint32_t c13; pj_uint32_t c14; pj_uint32_t c22; pj_uint32_t c23; pj_uint32_t c33; } voip_mtc_stat; unsigned ptime; /**< Packet time. */ unsigned frames_per_packet; /**< # frames per packet. */ struct pjmedia_rtcp_session *rtcp_session; /**< Parent/RTCP session. */ }; typedef struct pjmedia_rtcp_xr_session pjmedia_rtcp_xr_session; /** * Build an RTCP XR packet which contains one or more RTCP XR report blocks. * There are seven report types as defined in RFC 3611. * * @param session The RTCP XR session. * @param rpt_types Report types to be included in the packet, report types * are defined in pjmedia_rtcp_xr_type, set this to zero * will make this function build all reports appropriately. * @param rtcp_pkt Upon return, it will contain pointer to the RTCP XR packet. * @param len Upon return, it will indicate the size of the generated * RTCP XR packet. */ PJ_DECL(void) pjmedia_rtcp_build_rtcp_xr( pjmedia_rtcp_xr_session *session, unsigned rpt_types, void **rtcp_pkt, int *len); /** * Call this function to manually update some info needed by RTCP XR to * generate report which could not be populated directly when receiving * RTP. * * @param session The RTCP XR session. * @param info Info type to be updated, @see pjmedia_rtcp_xr_info. * @param val Value. */ PJ_DECL(pj_status_t) pjmedia_rtcp_xr_update_info( pjmedia_rtcp_xr_session *session, unsigned info, pj_int32_t val); /* * Private APIs: */ /** * This function is called internally by RTCP session when RTCP XR is enabled * to initialize the RTCP XR session. * * @param session RTCP XR session. * @param r_session RTCP session. * @param gmin Gmin value (defined in RFC 3611), set to 0 for default (16). * @param frames_per_packet Number of frames per packet. */ void pjmedia_rtcp_xr_init( pjmedia_rtcp_xr_session *session, struct pjmedia_rtcp_session *r_session, pj_uint8_t gmin, unsigned frames_per_packet); /** * This function is called internally by RTCP session to destroy * the RTCP XR session. * * @param session RTCP XR session. */ void pjmedia_rtcp_xr_fini( pjmedia_rtcp_xr_session *session ); /** * This function is called internally by RTCP session when it receives * incoming RTCP XR packets. * * @param session RTCP XR session. * @param rtcp_pkt The received RTCP XR packet. * @param size Size of the incoming packet. */ void pjmedia_rtcp_xr_rx_rtcp_xr( pjmedia_rtcp_xr_session *session, const void *rtcp_pkt, pj_size_t size); /** * This function is called internally by RTCP session whenever an RTP packet * is received or lost to let the RTCP XR session update its statistics. * Data passed to this function is a result of analyzation by RTCP and the * jitter buffer. Whenever some info is available, the value should be zero * or more (no negative info), otherwise if info is not available the info * should be -1 so no update will be done for this info in the RTCP XR session. * * @param session RTCP XR session. * @param seq Sequence number of RTP packet. * @param lost Info if this packet is lost. * @param dup Info if this packet is a duplication. * @param discarded Info if this packet is discarded * (not because of duplication). * @param jitter Info jitter of this packet. * @param toh Info Time To Live or Hops Limit of this packet. * @param toh_ipv4 Set PJ_TRUE if packet is transported over IPv4. */ void pjmedia_rtcp_xr_rx_rtp( pjmedia_rtcp_xr_session *session, unsigned seq, int lost, int dup, int discarded, int jitter, int toh, pj_bool_t toh_ipv4); /** * This function is called internally by RTCP session whenever an RTP * packet is sent to let the RTCP XR session do its internal calculations. * * @param session RTCP XR session. * @param ptsize Size of RTP payload being sent. */ void pjmedia_rtcp_xr_tx_rtp( pjmedia_rtcp_xr_session *session, unsigned ptsize ); /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_RTCP_XR_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/rtp.h ================================================ /* $Id: rtp.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_RTP_H__ #define __PJMEDIA_RTP_H__ /** * @file rtp.h * @brief RTP packet and RTP session declarations. */ #include PJ_BEGIN_DECL /** * @defgroup PJMED_RTP RTP Session and Encapsulation (RFC 3550) * @ingroup PJMEDIA_SESSION * @brief RTP format and session management * @{ * * The RTP module is designed to be dependent only to PJLIB, it does not depend * on any other parts of PJMEDIA library. The RTP module does not even depend * on any transports (sockets), to promote even more use, such as in DSP * development (where transport may be handled by different processor). * * An RTCP implementation is available, in separate module. Please see * @ref PJMED_RTCP. * * The functions that are provided by this module: * - creating RTP header for each outgoing packet. * - decoding RTP packet into RTP header and payload. * - provide simple RTP session management (sequence number, etc.) * * The RTP module does not use any dynamic memory at all. * * \section P1 How to Use the RTP Module * * First application must call #pjmedia_rtp_session_init() to initialize the RTP * session. * * When application wants to send RTP packet, it needs to call * #pjmedia_rtp_encode_rtp() to build the RTP header. Note that this WILL NOT build * the complete RTP packet, but instead only the header. Application can * then either concatenate the header with the payload, or send the two * fragments (the header and the payload) using scatter-gather transport API * (e.g. \a sendv()). * * When application receives an RTP packet, first it should call * #pjmedia_rtp_decode_rtp to decode RTP header and payload, then it should call * #pjmedia_rtp_session_update to check whether we can process the RTP payload, * and to let the RTP session updates its internal status. The decode function * is guaranteed to point the payload to the correct position regardless of * any options present in the RTP packet. * */ #ifdef _MSC_VER # pragma warning(disable:4214) // bit field types other than int #endif /** * RTP packet header. Note that all RTP functions here will work with this * header in network byte order. */ #pragma pack(1) struct pjmedia_rtp_hdr { #if defined(PJ_IS_BIG_ENDIAN) && (PJ_IS_BIG_ENDIAN!=0) pj_uint16_t v:2; /**< packet type/version */ pj_uint16_t p:1; /**< padding flag */ pj_uint16_t x:1; /**< extension flag */ pj_uint16_t cc:4; /**< CSRC count */ pj_uint16_t m:1; /**< marker bit */ pj_uint16_t pt:7; /**< payload type */ #else pj_uint16_t cc:4; /**< CSRC count */ pj_uint16_t x:1; /**< header extension flag */ pj_uint16_t p:1; /**< padding flag */ pj_uint16_t v:2; /**< packet type/version */ pj_uint16_t pt:7; /**< payload type */ pj_uint16_t m:1; /**< marker bit */ #endif pj_uint16_t seq; /**< sequence number */ pj_uint32_t ts; /**< timestamp */ pj_uint32_t ssrc; /**< synchronization source */ }; #pragma pack() /** * @see pjmedia_rtp_hdr */ typedef struct pjmedia_rtp_hdr pjmedia_rtp_hdr; /** * RTP extendsion header. */ struct pjmedia_rtp_ext_hdr { pj_uint16_t profile_data; /**< Profile data. */ pj_uint16_t length; /**< Length. */ }; /** * @see pjmedia_rtp_ext_hdr */ typedef struct pjmedia_rtp_ext_hdr pjmedia_rtp_ext_hdr; #pragma pack(1) /** * Declaration for DTMF telephony-events (RFC2833). */ struct pjmedia_rtp_dtmf_event { pj_uint8_t event; /**< Event type ID. */ pj_uint8_t e_vol; /**< Event volume. */ pj_uint16_t duration; /**< Event duration. */ }; /** * @see pjmedia_rtp_dtmf_event */ typedef struct pjmedia_rtp_dtmf_event pjmedia_rtp_dtmf_event; #pragma pack() /** * A generic sequence number management, used by both RTP and RTCP. */ struct pjmedia_rtp_seq_session { pj_uint16_t max_seq; /**< Highest sequence number heard */ pj_uint32_t cycles; /**< Shifted count of seq number cycles */ pj_uint32_t base_seq; /**< Base seq number */ pj_uint32_t bad_seq; /**< Last 'bad' seq number + 1 */ pj_uint32_t probation; /**< Sequ. packets till source is valid */ }; /** * @see pjmedia_rtp_seq_session */ typedef struct pjmedia_rtp_seq_session pjmedia_rtp_seq_session; /** * RTP session descriptor. */ struct pjmedia_rtp_session { pjmedia_rtp_hdr out_hdr; /**< Saved hdr for outgoing pkts. */ pjmedia_rtp_seq_session seq_ctrl; /**< Sequence number management. */ pj_uint16_t out_pt; /**< Default outgoing payload type. */ pj_uint32_t out_extseq; /**< Outgoing extended seq #. */ pj_uint32_t peer_ssrc; /**< Peer SSRC. */ pj_uint32_t received; /**< Number of received packets. */ }; /** * @see pjmedia_rtp_session */ typedef struct pjmedia_rtp_session pjmedia_rtp_session; /** * This structure is used to receive additional information about the * state of incoming RTP packet. */ struct pjmedia_rtp_status { union { struct flag { int bad:1; /**< General flag to indicate that sequence is bad, and application should not process this packet. More information will be given in other flags. */ int badpt:1; /**< Bad payload type. */ int badssrc:1; /**< Bad SSRC */ int dup:1; /**< Indicates duplicate packet */ int outorder:1; /**< Indicates out of order packet */ int probation:1;/**< Indicates that session is in probation until more packets are received. */ int restart:1; /**< Indicates that sequence number has made a large jump, and internal base sequence number has been adjusted. */ } flag; /**< Status flags. */ pj_uint16_t value; /**< Status value, to conveniently address all flags. */ } status; /**< Status information union. */ pj_uint16_t diff; /**< Sequence number difference from previous packet. Normally the value should be 1. Value greater than one may indicate packet loss. If packet with lower sequence is received, the value will be set to zero. If base sequence has been restarted, the value will be one. */ }; /** * RTP session settings. */ typedef struct pjmedia_rtp_session_setting { pj_uint8_t flags; /**< Bitmask flags to specify whether such field is set. Bitmask contents are: (bit #0 is LSB) bit #0: default payload type bit #1: sender SSRC bit #2: sequence bit #3: timestamp */ int default_pt; /**< Default payload type. */ pj_uint32_t sender_ssrc; /**< Sender SSRC. */ pj_uint16_t seq; /**< Sequence. */ pj_uint32_t ts; /**< Timestamp. */ } pjmedia_rtp_session_setting; /** * @see pjmedia_rtp_status */ typedef struct pjmedia_rtp_status pjmedia_rtp_status; /** * This function will initialize the RTP session according to given parameters. * * @param ses The session. * @param default_pt Default payload type. * @param sender_ssrc SSRC used for outgoing packets, in host byte order. * * @return PJ_SUCCESS if successfull. */ PJ_DECL(pj_status_t) pjmedia_rtp_session_init( pjmedia_rtp_session *ses, int default_pt, pj_uint32_t sender_ssrc ); /** * This function will initialize the RTP session according to given parameters * defined in RTP session settings. * * @param ses The session. * @param settings RTP session settings. * * @return PJ_SUCCESS if successfull. */ PJ_DECL(pj_status_t) pjmedia_rtp_session_init2( pjmedia_rtp_session *ses, pjmedia_rtp_session_setting settings); /** * Create the RTP header based on arguments and current state of the RTP * session. * * @param ses The session. * @param pt Payload type. * @param m Marker flag. * @param payload_len Payload length in bytes. * @param ts_len Timestamp length. * @param rtphdr Upon return will point to RTP packet header. * @param hdrlen Upon return will indicate the size of RTP packet header * * @return PJ_SUCCESS if successfull. */ PJ_DECL(pj_status_t) pjmedia_rtp_encode_rtp( pjmedia_rtp_session *ses, int pt, int m, int payload_len, int ts_len, const void **rtphdr, int *hdrlen ); /** * This function decodes incoming packet into RTP header and payload. * The decode function is guaranteed to point the payload to the correct * position regardless of any options present in the RTP packet. * * Note that this function does not modify the returned RTP header to * host byte order. * * @param ses The session. * @param pkt The received RTP packet. * @param pkt_len The length of the packet. * @param hdr Upon return will point to the location of the RTP * header inside the packet. Note that the RTP header * will be given back as is, meaning that the fields * will be in network byte order. * @param payload Upon return will point to the location of the * payload inside the packet. * @param payloadlen Upon return will indicate the size of the payload. * * @return PJ_SUCCESS if successfull. */ PJ_DECL(pj_status_t) pjmedia_rtp_decode_rtp( pjmedia_rtp_session *ses, const void *pkt, int pkt_len, const pjmedia_rtp_hdr **hdr, const void **payload, unsigned *payloadlen); /** * Call this function everytime an RTP packet is received to check whether * the packet can be received and to let the RTP session performs its internal * calculations. * * @param ses The session. * @param hdr The RTP header of the incoming packet. The header must * be given with fields in network byte order. * @param seq_st Optional structure to receive the status of the RTP packet * processing. */ PJ_DECL(void) pjmedia_rtp_session_update( pjmedia_rtp_session *ses, const pjmedia_rtp_hdr *hdr, pjmedia_rtp_status *seq_st); /** * Call this function everytime an RTP packet is received to check whether * the packet can be received and to let the RTP session performs its internal * calculations. * * @param ses The session. * @param hdr The RTP header of the incoming packet. The header must * be given with fields in network byte order. * @param seq_st Optional structure to receive the status of the RTP packet * processing. * @param check_pt Flag to indicate whether payload type needs to be validate. * * @see pjmedia_rtp_session_update() */ PJ_DECL(void) pjmedia_rtp_session_update2(pjmedia_rtp_session *ses, const pjmedia_rtp_hdr *hdr, pjmedia_rtp_status *seq_st, pj_bool_t check_pt); /* * INTERNAL: */ /** * Internal function for creating sequence number control, shared by RTCP * implementation. * * @param seq_ctrl The sequence control instance. * @param seq Sequence number to initialize. */ void pjmedia_rtp_seq_init(pjmedia_rtp_seq_session *seq_ctrl, pj_uint16_t seq); /** * Internal function update sequence control, shared by RTCP implementation. * * @param seq_ctrl The sequence control instance. * @param seq Sequence number to update. * @param seq_status Optional structure to receive additional information * about the packet. */ void pjmedia_rtp_seq_update( pjmedia_rtp_seq_session *seq_ctrl, pj_uint16_t seq, pjmedia_rtp_status *seq_status); /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_RTP_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/sdp.h ================================================ /* $Id: sdp.h 4367 2013-02-21 20:49:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_SDP_H__ #define __PJMEDIA_SDP_H__ /** * @file sdp.h * @brief SDP header file. */ #include #include /** * @defgroup PJMEDIA_SDP SDP Parsing and Data Structure * @ingroup PJMEDIA_SESSION * @brief SDP data structure representation and parsing * @{ * * The basic SDP session descriptor and elements are described in header * file . This file contains declaration for * SDP session descriptor and SDP media descriptor, along with their * attributes. This file also declares functions to parse SDP message. */ PJ_BEGIN_DECL /** * The PJMEDIA_MAX_SDP_FMT macro defines maximum format in a media line. */ #ifndef PJMEDIA_MAX_SDP_FMT # define PJMEDIA_MAX_SDP_FMT 32 #endif /** * The PJMEDIA_MAX_SDP_BANDW macro defines maximum bandwidth information * lines in a media line. */ #ifndef PJMEDIA_MAX_SDP_BANDW # define PJMEDIA_MAX_SDP_BANDW 4 #endif /** * The PJMEDIA_MAX_SDP_ATTR macro defines maximum SDP attributes in media and * session descriptor. */ #ifndef PJMEDIA_MAX_SDP_ATTR # define PJMEDIA_MAX_SDP_ATTR (PJMEDIA_MAX_SDP_FMT*2 + 4) #endif /** * The PJMEDIA_MAX_SDP_MEDIA macro defines maximum SDP media lines in a * SDP session descriptor. */ #ifndef PJMEDIA_MAX_SDP_MEDIA # define PJMEDIA_MAX_SDP_MEDIA 16 #endif /* ************************************************************************** * SDP ATTRIBUTES *************************************************************************** */ /** * Generic representation of attribute. */ struct pjmedia_sdp_attr { pj_str_t name; /**< Attribute name. */ pj_str_t value; /**< Attribute value. */ }; /** * @see pjmedia_sdp_attr */ typedef struct pjmedia_sdp_attr pjmedia_sdp_attr; /** * Create SDP attribute. * * @param pool Pool to create the attribute. * @param name Attribute name. * @param value Optional attribute value. * * @return The new SDP attribute. */ PJ_DECL(pjmedia_sdp_attr*) pjmedia_sdp_attr_create(pj_pool_t *pool, const char *name, const pj_str_t *value); /** * Clone attribute * * @param pool Pool to be used. * @param attr The attribute to clone. * * @return New attribute as cloned from the attribute. */ PJ_DECL(pjmedia_sdp_attr*) pjmedia_sdp_attr_clone(pj_pool_t *pool, const pjmedia_sdp_attr*attr); /** * Find the first attribute with the specified type. * * @param count Number of attributes in the array. * @param attr_array Array of attributes. * @param name Attribute name to find. * @param fmt Optional string to indicate which payload format * to find for \a rtpmap and \a fmt attributes. For other * types of attributes, the value should be NULL. * * @return The specified attribute, or NULL if it can't be found. * * @see pjmedia_sdp_attr_find2, pjmedia_sdp_media_find_attr, * pjmedia_sdp_media_find_attr2 */ PJ_DECL(pjmedia_sdp_attr*) pjmedia_sdp_attr_find(unsigned count, pjmedia_sdp_attr *const attr_array[], const pj_str_t *name, const pj_str_t *fmt); /** * Find the first attribute with the specified type. * * @param count Number of attributes in the array. * @param attr_array Array of attributes. * @param name Attribute name to find. * @param fmt Optional string to indicate which payload format * to find for \a rtpmap and \a fmt attributes. For other * types of attributes, the value should be NULL. * * @return The specified attribute, or NULL if it can't be found. * * @see pjmedia_sdp_attr_find, pjmedia_sdp_media_find_attr, * pjmedia_sdp_media_find_attr2 */ PJ_DECL(pjmedia_sdp_attr*) pjmedia_sdp_attr_find2(unsigned count, pjmedia_sdp_attr *const attr_array[], const char *name, const pj_str_t *fmt); /** * Add a new attribute to array of attributes. * * @param count Number of attributes in the array. * @param attr_array Array of attributes. * @param attr The attribute to add. * * @return PJ_SUCCESS or the error code. * * @see pjmedia_sdp_media_add_attr */ PJ_DECL(pj_status_t) pjmedia_sdp_attr_add(unsigned *count, pjmedia_sdp_attr *attr_array[], pjmedia_sdp_attr *attr); /** * Remove all attributes with the specified name in array of attributes. * * @param count Number of attributes in the array. * @param attr_array Array of attributes. * @param name Attribute name to find. * * @return Number of attributes removed. * * @see pjmedia_sdp_media_remove_all_attr */ PJ_DECL(unsigned) pjmedia_sdp_attr_remove_all(unsigned *count, pjmedia_sdp_attr *attr_array[], const char *name); /** * Remove the specified attribute from the attribute array. * * @param count Number of attributes in the array. * @param attr_array Array of attributes. * @param attr The attribute instance to remove. * * @return PJ_SUCCESS when attribute has been removed, or * PJ_ENOTFOUND when the attribute can not be found. * * @see pjmedia_sdp_media_remove_attr */ PJ_DECL(pj_status_t) pjmedia_sdp_attr_remove(unsigned *count, pjmedia_sdp_attr *attr_array[], pjmedia_sdp_attr *attr); /** * This structure declares SDP \a rtpmap attribute. */ struct pjmedia_sdp_rtpmap { pj_str_t pt; /**< Payload type. */ pj_str_t enc_name; /**< Encoding name. */ unsigned clock_rate; /**< Clock rate. */ pj_str_t param; /**< Parameter. */ }; /** * @see pjmedia_sdp_rtpmap */ typedef struct pjmedia_sdp_rtpmap pjmedia_sdp_rtpmap; /** * Convert generic attribute to SDP \a rtpmap. This function allocates * a new attribute and call #pjmedia_sdp_attr_get_rtpmap(). * * @param pool Pool used to create the rtpmap attribute. * @param attr Generic attribute to be converted to rtpmap, which * name must be "rtpmap". * @param p_rtpmap Pointer to receive SDP rtpmap attribute. * * @return PJ_SUCCESS if the attribute can be successfully * converted to \a rtpmap type. * * @see pjmedia_sdp_attr_get_rtpmap */ PJ_DECL(pj_status_t) pjmedia_sdp_attr_to_rtpmap(pj_pool_t *pool, const pjmedia_sdp_attr *attr, pjmedia_sdp_rtpmap **p_rtpmap); /** * Get the rtpmap representation of the same SDP attribute. * * @param attr Generic attribute to be converted to rtpmap, which * name must be "rtpmap". * @param rtpmap SDP \a rtpmap attribute to be initialized. * * @return PJ_SUCCESS if the attribute can be successfully * converted to \a rtpmap attribute. * * @see pjmedia_sdp_attr_to_rtpmap */ PJ_DECL(pj_status_t) pjmedia_sdp_attr_get_rtpmap(const pjmedia_sdp_attr *attr, pjmedia_sdp_rtpmap *rtpmap); /** * Convert \a rtpmap attribute to generic attribute. * * @param pool Pool to be used. * @param rtpmap The \a rtpmap attribute. * @param p_attr Pointer to receive the generic SDP attribute. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_sdp_rtpmap_to_attr( pj_pool_t *pool, const pjmedia_sdp_rtpmap *rtpmap, pjmedia_sdp_attr **p_attr); /** * This structure describes SDP \a fmtp attribute. */ typedef struct pjmedia_sdp_fmtp { pj_str_t fmt; /**< Format type. */ pj_str_t fmt_param; /**< Format specific parameter. */ } pjmedia_sdp_fmtp; /** * Get the fmtp representation of the same SDP attribute. * * @param attr Generic attribute to be converted to fmtp, which * name must be "fmtp". * @param fmtp SDP fmtp attribute to be initialized. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_sdp_attr_get_fmtp(const pjmedia_sdp_attr *attr, pjmedia_sdp_fmtp *fmtp); /** * This structure describes SDP \a rtcp attribute. */ typedef struct pjmedia_sdp_rtcp_attr { unsigned port; /**< RTCP port number. */ pj_str_t net_type; /**< Optional network type. */ pj_str_t addr_type; /**< Optional address type. */ pj_str_t addr; /**< Optional address. */ } pjmedia_sdp_rtcp_attr; /** * Parse a generic SDP attribute to get SDP rtcp attribute values. * * @param attr Generic attribute to be converted to rtcp, which * name must be "rtcp". * @param rtcp SDP rtcp attribute to be initialized. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_sdp_attr_get_rtcp(const pjmedia_sdp_attr *attr, pjmedia_sdp_rtcp_attr *rtcp); /** * Create a=rtcp attribute. * * @param pool Pool to create the attribute. * @param a Socket address. * * @return SDP RTCP attribute. */ PJ_DECL(pjmedia_sdp_attr*) pjmedia_sdp_attr_create_rtcp(pj_pool_t *pool, const pj_sockaddr *a); /* ************************************************************************** * SDP CONNECTION INFO **************************************************************************** */ /** * This structure describes SDP connection info ("c=" line). */ struct pjmedia_sdp_conn { pj_str_t net_type; /**< Network type ("IN"). */ pj_str_t addr_type; /**< Address type ("IP4", "IP6"). */ pj_str_t addr; /**< The address. */ }; /** * @see pjmedia_sdp_conn */ typedef struct pjmedia_sdp_conn pjmedia_sdp_conn; /** * Clone connection info. * * @param pool Pool to allocate memory for the new connection info. * @param rhs The connection into to clone. * * @return The new connection info. */ PJ_DECL(pjmedia_sdp_conn*) pjmedia_sdp_conn_clone(pj_pool_t *pool, const pjmedia_sdp_conn *rhs); /** * Compare connection info. * * @param conn1 The first connection info to compare. * @param conn1 The second connection info to compare. * @param option Comparison option, which should be zero for now. * * @return PJ_SUCCESS when both connection info are equal, otherwise * returns PJMEDIA_SDP_ECONNNOTEQUAL. */ PJ_DECL(pj_status_t) pjmedia_sdp_conn_cmp(const pjmedia_sdp_conn *conn1, const pjmedia_sdp_conn *conn2, unsigned option); /* ************************************************************************** * SDP BANDWIDTH INFO **************************************************************************** */ /** * This structure describes SDP bandwidth info ("b=" line). */ typedef struct pjmedia_sdp_bandw { pj_str_t modifier; /**< Bandwidth modifier. */ pj_uint32_t value; /**< Bandwidth value. */ } pjmedia_sdp_bandw; /** * Clone bandwidth info. * * @param pool Pool to allocate memory for the new bandwidth info. * @param rhs The bandwidth into to clone. * * @return The new bandwidth info. */ PJ_DECL(pjmedia_sdp_bandw*) pjmedia_sdp_bandw_clone(pj_pool_t *pool, const pjmedia_sdp_bandw *rhs); /* ************************************************************************** * SDP MEDIA INFO/LINE **************************************************************************** */ /** * This structure describes SDP media descriptor. A SDP media descriptor * starts with "m=" line and contains the media attributes and optional * connection line. */ struct pjmedia_sdp_media { /** Media descriptor line ("m=" line) */ struct { pj_str_t media; /**< Media type ("audio", "video") */ pj_uint16_t port; /**< Port number. */ unsigned port_count; /**< Port count, used only when >2 */ pj_str_t transport; /**< Transport ("RTP/AVP") */ unsigned fmt_count; /**< Number of formats. */ pj_str_t fmt[PJMEDIA_MAX_SDP_FMT]; /**< Media formats. */ } desc; pjmedia_sdp_conn *conn; /**< Optional connection info. */ unsigned bandw_count; /**< Number of bandwidth info. */ pjmedia_sdp_bandw *bandw[PJMEDIA_MAX_SDP_BANDW]; /**< Bandwidth info. */ unsigned attr_count; /**< Number of attributes. */ pjmedia_sdp_attr *attr[PJMEDIA_MAX_SDP_ATTR]; /**< Attributes. */ }; /** * @see pjmedia_sdp_media */ typedef struct pjmedia_sdp_media pjmedia_sdp_media; /** * Clone SDP media description. * * @param pool Pool to allocate memory for the new media description. * @param rhs The media descriptin to clone. * * @return New media description. */ PJ_DECL(pjmedia_sdp_media*) pjmedia_sdp_media_clone( pj_pool_t *pool, const pjmedia_sdp_media *rhs); /** * Find the first occurence of the specified attribute name in the media * descriptor. Optionally the format may be specified. * * @param m The SDP media description. * @param name Attribute name to find. * @param fmt Optional payload type to match in the * attribute list, when the attribute is \a rtpmap * or \a fmtp. For other types of SDP attributes, this * value should be NULL. * * @return The first instance of the specified attribute or NULL. */ PJ_DECL(pjmedia_sdp_attr*) pjmedia_sdp_media_find_attr(const pjmedia_sdp_media *m, const pj_str_t *name, const pj_str_t *fmt); /** * Find the first occurence of the specified attribute name in the SDP media * descriptor. Optionally the format may be specified. * * @param m The SDP media description. * @param name Attribute name to find. * @param fmt Optional payload type to match in the * attribute list, when the attribute is \a rtpmap * or \a fmtp. For other types of SDP attributes, this * value should be NULL. * * @return The first instance of the specified attribute or NULL. */ PJ_DECL(pjmedia_sdp_attr*) pjmedia_sdp_media_find_attr2(const pjmedia_sdp_media *m, const char *name, const pj_str_t *fmt); /** * Add new attribute to the media descriptor. * * @param m The SDP media description. * @param attr Attribute to add. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_sdp_media_add_attr(pjmedia_sdp_media *m, pjmedia_sdp_attr *attr); /** * Remove all attributes with the specified name from the SDP media * descriptor. * * @param m The SDP media description. * @param name Attribute name to remove. * * @return The number of attributes removed. */ PJ_DECL(unsigned) pjmedia_sdp_media_remove_all_attr(pjmedia_sdp_media *m, const char *name); /** * Remove the occurence of the specified attribute from the SDP media * descriptor. * * @param m The SDP media descriptor. * @param attr The attribute to find and remove. * * @return PJ_SUCCESS if the attribute can be found and has * been removed from the array. */ PJ_DECL(pj_status_t) pjmedia_sdp_media_remove_attr(pjmedia_sdp_media *m, pjmedia_sdp_attr *attr); /** * Compare two SDP media for equality. * * @param sd1 The first SDP media to compare. * @param sd2 The second SDP media to compare. * @param option Comparison option, which should be zero for now. * * @return PJ_SUCCESS when both SDP medias are equal, or the * appropriate status code describing which part of * the descriptors that are not equal. */ PJ_DECL(pj_status_t) pjmedia_sdp_media_cmp(const pjmedia_sdp_media *sd1, const pjmedia_sdp_media *sd2, unsigned option); /** * Compare two media transports for compatibility. * * @param t1 The first media transport to compare. * @param t2 The second media transport to compare. * * @return PJ_SUCCESS when both media transports are compatible, * otherwise returns PJMEDIA_SDP_ETPORTNOTEQUAL. */ PJ_DECL(pj_status_t) pjmedia_sdp_transport_cmp(const pj_str_t *t1, const pj_str_t *t2); /** * Deactivate SDP media. * * @param pool Memory pool to allocate memory from. * @param m The SDP media to deactivate. * * @return PJ_SUCCESS when SDP media successfully deactivated, * otherwise appropriate status code returned. */ PJ_DECL(pj_status_t) pjmedia_sdp_media_deactivate(pj_pool_t *pool, pjmedia_sdp_media *m); /** * Clone SDP media description and deactivate the new SDP media. * * @param pool Memory pool to allocate memory for the clone. * @param rhs The SDP media to clone. * * @return New media descrption with deactivated indication. */ PJ_DECL(pjmedia_sdp_media*) pjmedia_sdp_media_clone_deactivate( pj_pool_t *pool, const pjmedia_sdp_media *rhs); /* ************************************************************************** * SDP SESSION DESCRIPTION **************************************************************************** */ /** * This structure describes SDP session description. A SDP session descriptor * contains complete information about a session, and normally is exchanged * with remote media peer using signaling protocol such as SIP. */ struct pjmedia_sdp_session { /** Session origin (o= line) */ struct { pj_str_t user; /**< User */ pj_uint32_t id; /**< Session ID */ pj_uint32_t version; /**< Session version */ pj_str_t net_type; /**< Network type ("IN") */ pj_str_t addr_type; /**< Address type ("IP4", "IP6") */ pj_str_t addr; /**< The address. */ } origin; pj_str_t name; /**< Subject line (s=) */ pjmedia_sdp_conn *conn; /**< Connection line (c=) */ unsigned bandw_count; /**< Number of bandwidth info (b=) */ pjmedia_sdp_bandw *bandw[PJMEDIA_MAX_SDP_BANDW]; /**< Bandwidth info array (b=) */ /** Session time (t= line) */ struct { pj_uint32_t start; /**< Start time. */ pj_uint32_t stop; /**< Stop time. */ } time; unsigned attr_count; /**< Number of attributes. */ pjmedia_sdp_attr *attr[PJMEDIA_MAX_SDP_ATTR]; /**< Attributes array. */ unsigned media_count; /**< Number of media. */ pjmedia_sdp_media *media[PJMEDIA_MAX_SDP_MEDIA]; /**< Media array. */ }; /** * @see pjmedia_sdp_session */ typedef struct pjmedia_sdp_session pjmedia_sdp_session; /** * Parse SDP message. * * @param pool The pool to allocate SDP session description. * @param buf The message buffer. * @param len The length of the message. * @param p_sdp Pointer to receive the SDP session descriptor. * * @return PJ_SUCCESS if message was successfully parsed into * SDP session descriptor. */ PJ_DECL(pj_status_t) pjmedia_sdp_parse( pj_pool_t *pool, char *buf, pj_size_t len, pjmedia_sdp_session **p_sdp ); /** * Print SDP description to a buffer. * * @param sdp The SDP session description. * @param buf The buffer. * @param size The buffer length. * * @return the length printed, or -1 if the buffer is too * short. */ PJ_DECL(int) pjmedia_sdp_print( const pjmedia_sdp_session *sdp, char *buf, pj_size_t size); /** * Perform semantic validation for the specified SDP session descriptor. * This function perform validation beyond just syntactic verification, * such as to verify the value of network type and address type, check * the connection line, and verify that \a rtpmap attribute is present * when dynamic payload type is used. * * @param sdp The SDP session descriptor to validate. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp); /** * Perform semantic validation for the specified SDP session descriptor. * This function perform validation beyond just syntactic verification, * such as to verify the value of network type and address type, check * the connection line, and verify that \a rtpmap attribute is present * when dynamic payload type is used. * * @param sdp The SDP session descriptor to validate. * @param strict Flag whether the check should be strict, i.e: allow * media without connection line when port is zero. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_sdp_validate2(const pjmedia_sdp_session *sdp, pj_bool_t strict); /** * Clone SDP session descriptor. * * @param pool The pool used to clone the session. * @param sdp The SDP session to clone. * * @return New SDP session. */ PJ_DECL(pjmedia_sdp_session*) pjmedia_sdp_session_clone( pj_pool_t *pool, const pjmedia_sdp_session *sdp); /** * Compare two SDP session for equality. * * @param sd1 The first SDP session to compare. * @param sd2 The second SDP session to compare. * @param option Must be zero for now. * * @return PJ_SUCCESS when both SDPs are equal, or otherwise * the status code indicates which part of the session * descriptors are not equal. */ PJ_DECL(pj_status_t) pjmedia_sdp_session_cmp(const pjmedia_sdp_session *sd1, const pjmedia_sdp_session *sd2, unsigned option); /** * Add new attribute to the session descriptor. * * @param s The SDP session description. * @param attr Attribute to add. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_sdp_session_add_attr(pjmedia_sdp_session *s, pjmedia_sdp_attr *attr); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_SDP_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/sdp_neg.h ================================================ /* $Id: sdp_neg.h 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_SDP_NEG_H__ #define __PJMEDIA_SDP_NEG_H__ /** * @file sdp_neg.h * @brief SDP negotiator header file. */ /** * @defgroup PJMEDIA_SDP_NEG SDP Negotiation State Machine (Offer/Answer Model, RFC 3264) * @ingroup PJMEDIA_SESSION * @brief SDP Negotiation State Machine (Offer/Answer Model, RFC 3264) * @{ * * The header file contains the declaration * of SDP offer and answer negotiator. SDP offer and answer model is described * in RFC 3264 "An Offer/Answer Model with Session Description Protocol * (SDP)". * * The SDP negotiator is represented with opaque type \a pjmedia_sdp_neg. * This structure contains negotiation state and several SDP session * descriptors currently being used in the negotiation. * * * \section sdpneg_state_dia SDP Negotiator State Diagram * * The following diagram describes the state transition diagram of the * SDP negotiator. * *
 *                                              
 *                                              modify_local_offer()
 *     create_w_local_offer()  +-------------+  send_local_offer()
 *     ----------------------->| LOCAL_OFFER |<-----------------------
 *    |                        +-------------+______                  |
 *    |                               |             \_____________    |
 *    |           set_remote_answer() |           cancel_offer()  \   |
 *    |                               V                            v  |
 * +--+---+                     +-----------+     negotiate()     +-~----+
 * | NULL |                     | WAIT_NEGO |-------------------->| DONE |
 * +------+                     +-----------+                     +------+
 *    |                               A      ______________________^  |
 *    |            set_local_answer() |     /     cancel_offer()      |
 *    |                               |    /                          |
 *    |                        +--------------+   set_remote_offer()  |
 *     ----------------------->| REMOTE_OFFER |<----------------------
 *     create_w_remote_offer() +--------------+
 *
 * 
* * * * \section sdpneg_offer_answer SDP Offer/Answer Model with Negotiator * * \subsection sdpneg_create_offer Creating Initial Offer * * Application creates an offer by manualy building the SDP session descriptor * (pjmedia_sdp_session), or request PJMEDIA endpoint (pjmedia_endpt) to * create SDP session descriptor based on capabilities that present in the * endpoint by calling #pjmedia_endpt_create_sdp(). * * Application then creates SDP negotiator instance by calling * #pjmedia_sdp_neg_create_w_local_offer(), passing the SDP offer in the * function arguments. The SDP negotiator keeps a copy of current local offer, * and update its state to PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER. * * Application can then send the initial SDP offer that it creates to * remote peer using signaling protocol such as SIP. * * * \subsection sdpneg_subseq_offer Generating Subsequent Offer * * The negotiator can only create subsequent offer after it has finished * the negotiation process of previous offer/answer session (i.e. the * negotiator state is PJMEDIA_SDP_NEG_STATE_DONE). * * If any previous negotiation process was successfull (i.e. the return * value of #pjmedia_sdp_neg_negotiate() was PJ_SUCCESS), the negotiator * keeps both active local and active remote SDP. * * If application does not want send modified offer, it can just send * the active local SDP as the offer. In this case, application calls * #pjmedia_sdp_neg_send_local_offer() to get the active local SDP. * * If application wants to modify it's local offer, it MUST inform * the negotiator about the modified SDP by calling * #pjmedia_sdp_neg_modify_local_offer(). * * In both cases, the negotiator will internally create a copy of the offer, * and move it's state to PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, where it * waits until application passes the remote answer. * * * \subsection sdpneg_receive_offer Receiving Initial Offer * * Application receives an offer in the incoming request from remote to * establish multimedia session, such as incoming INVITE message with SDP * body. * * Initially, when the initial offer is received, application creates the * SDP negotiator by calling #pjmedia_sdp_neg_create_w_remote_offer(), * specifying the remote SDP offer in one of the argument. * * At this stage, application may or may not ready to create an answer. * For example, a SIP B2BUA needs to make outgoing call and receive SDP * from the outgoing call leg in order to create a SDP answer to the * incoming call leg. * * If application is not ready to create an answer, it passes NULL as * the local SDP when it calls #pjmedia_sdp_neg_create_w_remote_offer(). * * The section @ref sdpneg_create_answer describes the case when * application is ready to create a SDP answer. * * * \subsection sdpneg_subseq_offer Receiving Subsequent Offer * * Application passes subsequent SDP offer received from remote by * calling #pjmedia_sdp_neg_set_remote_offer(). * * The negotiator can only receive subsequent offer after it has finished * the negotiation process of previous offer/answer session (i.e. the * negotiator state is PJMEDIA_SDP_NEG_STATE_DONE). * * * \subsection sdpneg_recv_answer Receiving SDP Answer * * When application receives SDP answer from remote, it informs the * negotiator by calling #pjmedia_sdp_neg_set_remote_answer(). The * negotiator validates the answer (#pjmedia_sdp_validate()), and if * succeeds, it moves it's state to PJMEDIA_SDP_NEG_STATE_WAIT_NEGO. * * Application then instruct the negotiator to negotiate the remote * answer by calling #pjmedia_sdp_neg_negotiate(). The purpose of * this negotiation is to verify remote answer, and update the initial * offer according to the answer. For example, the initial offer may * specify that a stream is \a sendrecv, while the answer specifies * that remote stream is \a inactive. In this case, the negotiator * will update the stream in the local active media as \a inactive * too. * * If #pjmedia_sdp_neg_negotiate() returns PJ_SUCCESS, the negotiator will * keep the updated local answer and remote answer internally. These two * SDPs are called active local SDP and active remote SDP, as it describes * currently active session. * * Application can retrieve the active local SDP by calling * #pjmedia_sdp_neg_get_active_local(), and active remote SDP by calling * #pjmedia_sdp_neg_get_active_remote(). * * If #pjmedia_sdp_neg_negotiate() returns failure (i.e. not PJ_SUCCESS), * it WILL NOT update its active local and active remote SDP. * * Regardless of the return status of the #pjmedia_sdp_neg_negotiate(), * the negotiator state will move to PJMEDIA_SDP_NEG_STATE_DONE. * * * \subsection sdpneg_cancel_offer Cancelling an Offer * * In other case, after an offer is generated (negotiator state is in * PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER), the answer may not be received, and * application wants the negotiator to reset itself to its previous state. * Consider this example: * * - media has been established, and negotiator state is * PJMEDIA_SDP_NEG_STATE_DONE. * - application generates a new offer for re-INVITE, so in this case * it would either call #pjmedia_sdp_neg_send_local_offer() or * #pjmedia_sdp_neg_modify_local_offer() * - the negotiator state moves to PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER * - the re-INVITE was rejected with an error * * Since an answer is not received, it is necessary to reset the negotiator * state back to PJMEDIA_SDP_NEG_STATE_DONE so that the negotiator can * create or receive new offer. * * This can be accomplished by calling #pjmedia_sdp_neg_cancel_offer(), * to reset the negotiator state back to PJMEDIA_SDP_NEG_STATE_DONE. In * this case, both active local and active remote will not be modified. * * \subsection sdpneg_create_answer Generating SDP Answer * * After remote offer has been set in the negotiator, application can * request the SDP negotiator to generate appropriate answer based on local * capability. * * To do this, first the application MUST have an SDP describing its local * capabilities. This SDP can be built manually, or application can generate * SDP to describe local media endpoint capability by calling * #pjmedia_endpt_create_sdp(). When the application is a SIP B2BUA, * application can treat the SDP received from the outgoing call leg as if * it was it's local capability. * * The local SDP session descriptor DOES NOT have to match the SDP offer. * For example, it can have more or less media lines than the offer, or * their order may be different than the offer. The negotiator is capable * to match and reorder local SDP according to remote offer, and create * an answer that is suitable for the offer. * * After local SDP capability has been acquired, application can create * a SDP answer. * * If application does not already have the negotiator instance, it creates * one by calling #pjmedia_sdp_neg_create_w_remote_offer(), specifying * both remote SDP offer and local SDP as the arguments. The SDP negotiator * validates both remote and local SDP by calling #pjmedia_sdp_validate(), * and if both SDPs are valid, the negotiator state will move to * PJMEDIA_SDP_NEG_STATE_WAIT_NEGO where it is ready to negotiate the * offer and answer. * * If application already has the negotiator instance, it sets the local * SDP in the negotiator by calling #pjmedia_sdp_neg_set_local_answer(). * The SDP negotiator then validates local SDP (#pjmedia_sdp_validate() ), * and if it is valid, the negotiator state will move to * PJMEDIA_SDP_NEG_STATE_WAIT_NEGO where it is ready to negotiate the * offer and answer. * * After the SDP negotiator state has moved to PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, * application calls #pjmedia_sdp_neg_negotiate() to instruct the SDP * negotiator to negotiate both offer and answer. This function returns * PJ_SUCCESS if an answer can be generated AND at least one media stream * is active in the session. * * If #pjmedia_sdp_neg_negotiate() returns PJ_SUCCESS, the negotiator will * keep the remote offer and local answer internally. These two SDPs are * called active local SDP and active remote SDP, as it describes currently * active session. * * Application can retrieve the active local SDP by calling * #pjmedia_sdp_neg_get_active_local(), and send this SDP to remote as the * SDP answer. * * If #pjmedia_sdp_neg_negotiate() returns failure (i.e. not PJ_SUCCESS), * it WILL NOT update its active local and active remote SDP. * * Regardless of the return status of the #pjmedia_sdp_neg_negotiate(), * the negotiator state will move to PJMEDIA_SDP_NEG_STATE_DONE. * * */ #include PJ_BEGIN_DECL /** * This enumeration describes SDP negotiation state. */ enum pjmedia_sdp_neg_state { /** * This is the state of SDP negoator before it is initialized. */ PJMEDIA_SDP_NEG_STATE_NULL, /** * This state occurs when SDP negotiator has sent our offer to remote and * it is waiting for answer. */ PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, /** * This state occurs when SDP negotiator has received offer from remote * and currently waiting for local answer. */ PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER, /** * This state occurs when an offer (either local or remote) has been * provided with answer. The SDP negotiator is ready to negotiate both * session descriptors. Application can call #pjmedia_sdp_neg_negotiate() * immediately to begin negotiation process. */ PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, /** * This state occurs when SDP negotiation has completed, either * successfully or not. */ PJMEDIA_SDP_NEG_STATE_DONE }; /** * @see pjmedia_sdp_neg_state */ typedef enum pjmedia_sdp_neg_state pjmedia_sdp_neg_state; /** * Opaque declaration of SDP negotiator. */ typedef struct pjmedia_sdp_neg pjmedia_sdp_neg; /** * Flags to be given to pjmedia_sdp_neg_modify_local_offer2(). */ typedef enum pjmedia_mod_offer_flag { /** * Allow media type in the SDP to be changed. * When generating a new offer, in the case that a media line doesn't match * the active SDP, the new media line will be considered to replace the * existing media at the same position. */ PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE = 1 } pjmedia_mod_offer_flag; /** * Get the state string description of the specified state. * * @param state Negotiator state. * * @return String description of the state. */ PJ_DECL(const char*) pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_state state); /** * Create the SDP negotiator with local offer. The SDP negotiator then * will move to PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER state, where it waits * until it receives answer from remote. When SDP answer from remote is * received, application must call #pjmedia_sdp_neg_set_remote_answer(). * * After calling this function, application should send the local SDP offer * to remote party using signaling protocol such as SIP and wait for SDP * answer. * * @param pool Pool to allocate memory. The pool's lifetime needs * to be valid for the duration of the negotiator. * @param local The initial local capability. * @param p_neg Pointer to receive the negotiator instance. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_create_w_local_offer( pj_pool_t *pool, const pjmedia_sdp_session *local, pjmedia_sdp_neg **p_neg); /** * Initialize the SDP negotiator with remote offer, and optionally * specify the initial local capability, if known. Application normally * calls this function when it receives initial offer from remote. * * If local media capability is specified, this capability will be set as * initial local capability of the negotiator, and after this function is * called, the SDP negotiator state will move to state * PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, and the negotiation function can be * called. * * If local SDP is not specified, the negotiator will not have initial local * capability, and after this function is called the negotiator state will * move to PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER state. Application MUST supply * local answer later with #pjmedia_sdp_neg_set_local_answer(), before * calling the negotiation function. * * @param pool Pool to allocate memory. The pool's lifetime needs * to be valid for the duration of the negotiator. * @param initial Optional initial local capability. * @param remote The remote offer. * @param p_neg Pointer to receive the negotiator instance. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_create_w_remote_offer(pj_pool_t *pool, const pjmedia_sdp_session *initial, const pjmedia_sdp_session *remote, pjmedia_sdp_neg **p_neg); /** * This specifies the behavior of the SDP negotiator when responding to an * offer, whether it should rather use the codec preference as set by * remote, or should it rather use the codec preference as specified by * local endpoint. * * For example, suppose incoming call has codec order "8 0 3", while * local codec order is "3 0 8". If remote codec order is preferable, * the selected codec will be 8, while if local codec order is preferable, * the selected codec will be 3. * * By default, the value in PJMEDIA_SDP_NEG_PREFER_REMOTE_CODEC_ORDER will * be used. * * @param neg The SDP negotiator instance. * @param prefer_remote If non-zero, the negotiator will use the codec * order as specified in remote offer. If zero, it * will prefer to use the local codec order. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_set_prefer_remote_codec_order(pjmedia_sdp_neg *neg, pj_bool_t prefer_remote); /** * This specifies the behavior of the SDP negotiator when responding to an * offer, whether it should answer with multiple formats or not. * * By default, the value in PJMEDIA_SDP_NEG_ANSWER_MULTIPLE_CODECS will * be used. * * @param neg The SDP negotiator instance. * @param answer_multiple * If non-zero, the negotiator will respond with * multiple formats. If zero only a single format * will be returned. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_set_answer_multiple_codecs(pjmedia_sdp_neg *neg, pj_bool_t answer_multiple); /** * Get SDP negotiator state. * * @param neg The SDP negotiator instance. * * @return The negotiator state. */ PJ_DECL(pjmedia_sdp_neg_state) pjmedia_sdp_neg_get_state( pjmedia_sdp_neg *neg ); /** * Get the currently active local SDP. Application can only call this * function after negotiation has been done, or otherwise there won't be * active SDPs. Calling this function will not change the state of the * negotiator. * * @param neg The SDP negotiator instance. * @param local Pointer to receive the local active SDP. * * @return PJ_SUCCESS if local active SDP is present. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_get_active_local( pjmedia_sdp_neg *neg, const pjmedia_sdp_session **local); /** * Get the currently active remote SDP. Application can only call this * function after negotiation has been done, or otherwise there won't be * active SDPs. Calling this function will not change the state of the * negotiator. * * @param neg The SDP negotiator instance. * @param remote Pointer to receive the remote active SDP. * * @return PJ_SUCCESS if remote active SDP is present. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_get_active_remote( pjmedia_sdp_neg *neg, const pjmedia_sdp_session **remote); /** * Determine whether remote sent answer (as opposed to offer) on the * last negotiation. This function can only be called in state * PJMEDIA_SDP_NEG_STATE_DONE. * * @param neg The SDP negotiator instance. * * @return Non-zero if it was remote who sent answer, * otherwise zero if it was local who supplied * answer. */ PJ_DECL(pj_bool_t) pjmedia_sdp_neg_was_answer_remote(pjmedia_sdp_neg *neg); /** * Get the current remote SDP offer or answer. Application can only * call this function in state PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER or * PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, or otherwise there won't be remote * SDP offer/answer. Calling this function will not change the state * of the negotiator. * * @param neg The SDP negotiator instance. * @param remote Pointer to receive the current remote offer or * answer. * * @return PJ_SUCCESS if the negotiator currently has * remote offer or answer. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_get_neg_remote( pjmedia_sdp_neg *neg, const pjmedia_sdp_session **remote); /** * Get the current local SDP offer or answer. Application can only * call this function in state PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER or * PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, or otherwise there won't be local * SDP offer/answer. Calling this function will not change the state * of the negotiator. * * @param neg The SDP negotiator instance. * @param local Pointer to receive the current local offer or * answer. * * @return PJ_SUCCESS if the negotiator currently has * local offer or answer. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_get_neg_local( pjmedia_sdp_neg *neg, const pjmedia_sdp_session **local); /** * Modify local session with a new SDP and treat this as a new offer. * This function can only be called in state PJMEDIA_SDP_NEG_STATE_DONE. * After calling this function, application can send the SDP as offer * to remote party, using signaling protocol such as SIP. * The negotiator state will move to PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, * where it waits for SDP answer from remote. See also * #pjmedia_sdp_neg_modify_local_offer2() * * @param pool Pool to allocate memory. The pool's lifetime needs * to be valid for the duration of the negotiator. * @param neg The SDP negotiator instance. * @param local The new local SDP. * * @return PJ_SUCCESS on success, or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_modify_local_offer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session *local); /** * Modify local session with a new SDP and treat this as a new offer. * This function can only be called in state PJMEDIA_SDP_NEG_STATE_DONE. * After calling this function, application can send the SDP as offer * to remote party, using signaling protocol such as SIP. * The negotiator state will move to PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, * where it waits for SDP answer from remote. * * @param pool Pool to allocate memory. The pool's lifetime needs * to be valid for the duration of the negotiator. * @param neg The SDP negotiator instance. * @param flags Bitmask from pjmedia_mod_offer_flag. * @param local The new local SDP. * * @return PJ_SUCCESS on success, or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_modify_local_offer2( pj_pool_t *pool, pjmedia_sdp_neg *neg, unsigned flags, const pjmedia_sdp_session *local); /** * This function can only be called in PJMEDIA_SDP_NEG_STATE_DONE state. * Application calls this function to retrieve currently active * local SDP, and then send the SDP to remote as an offer. The negotiator * state will then move to PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, where it waits * for SDP answer from remote. * * When SDP answer has been received from remote, application must call * #pjmedia_sdp_neg_set_remote_answer(). * * @param pool Pool to allocate memory. The pool's lifetime needs * to be valid for the duration of the negotiator. * @param neg The SDP negotiator instance. * @param offer Pointer to receive active local SDP to be * offered to remote. * * @return PJ_SUCCESS if local offer can be created. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_send_local_offer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session **offer); /** * This function can only be called in PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER * state, i.e. after application calls #pjmedia_sdp_neg_send_local_offer() * function. Application calls this function when it receives SDP answer * from remote. After this function is called, the negotiator state will * move to PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, and application can call the * negotiation function #pjmedia_sdp_neg_negotiate(). * * @param pool Pool to allocate memory. The pool's lifetime needs * to be valid for the duration of the negotiator. * @param neg The SDP negotiator instance. * @param remote The remote answer. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_set_remote_answer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session *remote); /** * This function can only be called in PJMEDIA_SDP_NEG_STATE_DONE state. * Application calls this function when it receives SDP offer from remote. * After this function is called, the negotiator state will move to * PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER, and application MUST call the * #pjmedia_sdp_neg_set_local_answer() to set local answer before it can * call the negotiation function. * * @param pool Pool to allocate memory. The pool's lifetime needs * to be valid for the duration of the negotiator. * @param neg The SDP negotiator instance. * @param remote The remote offer. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_set_remote_offer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session *remote); /** * This function can only be called in PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER * state, i.e. after application calls #pjmedia_sdp_neg_set_remote_offer() * function. After this function is called, the negotiator state will * move to PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, and application can call the * negotiation function #pjmedia_sdp_neg_negotiate(). * * @param pool Pool to allocate memory. The pool's lifetime needs * to be valid for the duration of the negotiator. * @param neg The SDP negotiator instance. * @param local Optional local answer. If negotiator has initial * local capability, application can specify NULL on * this argument; in this case, the negotiator will * create answer by by negotiating remote offer with * initial local capability. If negotiator doesn't have * initial local capability, application MUST specify * local answer here. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_set_local_answer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session *local); /** * Call this function when the negotiator is in PJMEDIA_SDP_NEG_STATE_WAIT_NEGO * state to see if it was local who is answering the offer (instead of * remote). * * @param neg The negotiator. * * @return PJ_TRUE if it is local is answering an offer, PJ_FALSE * if remote has answered local offer. */ PJ_DECL(pj_bool_t) pjmedia_sdp_neg_has_local_answer(pjmedia_sdp_neg *neg); /** * Cancel any pending offer, whether the offer is initiated by local or * remote, and move negotiator state back to previous stable state * (PJMEDIA_SDP_NEG_STATE_DONE). The negotiator must be in * PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER or PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER * state. * * @param neg The negotiator. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_cancel_offer(pjmedia_sdp_neg *neg); /** * Negotiate local and remote answer. Before calling this function, the * SDP negotiator must be in PJMEDIA_SDP_NEG_STATE_WAIT_NEGO state. * After calling this function, the negotiator state will move to * PJMEDIA_SDP_NEG_STATE_DONE regardless whether the negotiation has * been successfull or not. * * If the negotiation succeeds (i.e. the return value is PJ_SUCCESS), * the active local and remote SDP will be replaced with the new SDP * from the negotiation process. * * If the negotiation fails, the active local and remote SDP will not * change. * * @param pool Pool to allocate memory. The pool's lifetime needs * to be valid for the duration of the negotiator. * @param neg The SDP negotiator instance. * @param allow_asym Should be zero. * * @return PJ_SUCCESS when there is at least one media * is actuve common in both offer and answer, or * failure code when negotiation has failed. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_negotiate( pj_pool_t *pool, pjmedia_sdp_neg *neg, pj_bool_t allow_asym); /** * Enumeration of customized SDP format matching option flags. See * #pjmedia_sdp_neg_register_fmt_match_cb() for more info. */ typedef enum pjmedia_sdp_neg_fmt_match_flag { /** * In generating answer, the SDP fmtp in the answer candidate may need * to be modified by the customized SDP format matching callback to * achieve flexible SDP negotiation, e.g: AMR fmtp 'octet-align' field * can be adjusted with the offer when the codec implementation support * both packetization modes octet-aligned and bandwidth-efficient. */ PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER = 1, } pjmedia_sdp_neg_fmt_match_flag; /** * The declaration of customized SDP format matching callback. See * #pjmedia_sdp_neg_register_fmt_match_cb() for more info. * * @param pool The memory pool. * @param offer The SDP media offer. * @param o_fmt_idx Index of the format in the SDP media offer. * @param answer The SDP media answer. * @param a_fmt_idx Index of the format in the SDP media answer. * @param option The format matching option, see * #pjmedia_sdp_neg_fmt_match_flag. * * @return PJ_SUCCESS when the formats in offer and answer match. */ typedef pj_status_t (*pjmedia_sdp_neg_fmt_match_cb)(pj_pool_t *pool, pjmedia_sdp_media *offer, unsigned o_fmt_idx, pjmedia_sdp_media *answer, unsigned a_fmt_idx, unsigned option); /** * Register customized SDP format matching callback function for the specified * format. The customized SDP format matching is needed when the format * identification in a media stream session cannot be simply determined by * encoding name and clock rate, but also involves one or more format specific * parameters, which are specified in SDP fmtp attribute. For example, * an H.264 video stream is also identified by profile, level, and * packetization-mode parameters. As those parameters are format specifics, * the negotiation must be done by the format or codec implementation. * * To unregister the callback of specific format, just call this function with * parameter #cb set to NULL. * * @param fmt_name The format name, e.g: "H.264", "AMR", "G7221". Note * that the string buffer must remain valid until the * callback is unregistered. * @param cb The customized SDP format negotiation callback or * NULL to unregister the specified format callback. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_register_fmt_match_cb( const pj_str_t *fmt_name, pjmedia_sdp_neg_fmt_match_cb cb); /** * Match format in the SDP media offer and answer. The matching mechanism * will be done by comparing the encoding name, clock rate, and encoding * parameters (if any), and if the custom format matching callback * for the specified format is registered, see * #pjmedia_sdp_neg_register_fmt_match_cb(), it will be called for * more detail verification, e.g: format parameters specified in SDP fmtp. * * @param pool The memory pool. * @param offer The SDP media offer. * @param o_fmt_idx Index of the format in the SDP media offer. * @param answer The SDP media answer. * @param a_fmt_idx Index of the format in the SDP media answer. * @param option The format matching option, see * #pjmedia_sdp_neg_fmt_match_flag. * * @return PJ_SUCCESS when the formats in offer and answer match. */ PJ_DECL(pj_status_t) pjmedia_sdp_neg_fmt_match( pj_pool_t *pool, pjmedia_sdp_media *offer, unsigned o_fmt_idx, pjmedia_sdp_media *answer, unsigned a_fmt_idx, unsigned option); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_SDP_NEG_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/session.h ================================================ /* $Id: session.h 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_SESSION_H__ #define __PJMEDIA_SESSION_H__ /** * @file session.h * @brief Media Session. */ #include #include #include PJ_BEGIN_DECL /** * @defgroup PJMEDIA_SESSION Media Sessions * @brief Management of media sessions * @{ * * A media session represents multimedia communication between two * parties. A media session represents the multimedia session that * is described by SDP session descriptor. A media session consists * of one or more media streams (pjmedia_stream), where each stream * represents one media line (m= line) in SDP. * * This module provides functions to create and manage multimedia * sessions. * * Application creates the media session by calling #pjmedia_session_create(), * normally after it has completed negotiating both SDP offer and answer. * The session creation function creates the media session (including * media streams) based on the content of local and remote SDP. */ /** * Session info, retrieved from a session by calling * #pjmedia_session_get_info(). */ struct pjmedia_session_info { /** Number of streams. */ unsigned stream_cnt; /** Individual stream info. */ pjmedia_stream_info stream_info[PJMEDIA_MAX_SDP_MEDIA]; }; /** * Opaque declaration of media session. */ typedef struct pjmedia_session pjmedia_session; /** * @see pjmedia_session_info. */ typedef struct pjmedia_session_info pjmedia_session_info; /** * This function will initialize the session info based on information * in both SDP session descriptors. The remaining information will be * taken from default codec parameters. If socket info array is specified, * the socket will be copied to the session info as well. * * @param pool Pool to allocate memory. * @param endpt Pjmedia endpoint. * @param max_streams Maximum number of stream infos to be created. * @param si Session info structure to be initialized. * @param local Local SDP session descriptor. * @param remote Remote SDP session descriptor. * * @return PJ_SUCCESS if stream info is successfully initialized. */ PJ_DECL(pj_status_t) pjmedia_session_info_from_sdp( pj_pool_t *pool, pjmedia_endpt *endpt, unsigned max_streams, pjmedia_session_info *si, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote); /** * This function will initialize the stream info based on information * in both SDP session descriptors for the specified stream index. * The remaining information will be taken from default codec parameters. * If socket info array is specified, the socket will be copied to the * session info as well. * * @param si Stream info structure to be initialized. * @param pool Pool to allocate memory. * @param endpt PJMEDIA endpoint instance. * @param local Local SDP session descriptor. * @param remote Remote SDP session descriptor. * @param stream_idx Media stream index in the session descriptor. * * @return PJ_SUCCESS if stream info is successfully initialized. */ PJ_DECL(pj_status_t) pjmedia_stream_info_from_sdp( pjmedia_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote, unsigned stream_idx); /** * Create media session based on the local and remote SDP. After the session * has been created, application normally would want to get the media port * interface of each streams, by calling #pjmedia_session_get_port(). The * media port interface exports put_frame() and get_frame() function, used * to transmit and receive media frames from the stream. * * Without application calling put_frame() and get_frame(), there will be * no media frames transmitted or received by the session. * * @param endpt The PJMEDIA endpoint instance. * @param si Session info containing stream count and array of * stream info. The stream count indicates how many * streams to be created in the session. * @param transports Array of media stream transports, with * sufficient number of elements (one for each stream). * @param user_data Arbitrary user data to be kept in the session. * @param p_session Pointer to receive the media session. * * @return PJ_SUCCESS if media session can be created * successfully. */ PJ_DECL(pj_status_t) pjmedia_session_create( pjmedia_endpt *endpt, const pjmedia_session_info *si, pjmedia_transport *transports[], void *user_data, pjmedia_session **p_session ); /** * Get media session info of the session. * * @param session The session which info is being queried. * @param info Pointer to receive session info. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_session_get_info( pjmedia_session *session, pjmedia_session_info *info ); /** * Get user data of the session. * * @param session The session being queried. * * @return User data of the session. */ PJ_DECL(void*) pjmedia_session_get_user_data( pjmedia_session *session); /** * Activate all streams in media session for the specified direction. * Application only needs to call this function if it previously paused * the session. * * @param session The media session. * @param dir The direction to activate. * * @return PJ_SUCCESS if success. */ PJ_DECL(pj_status_t) pjmedia_session_resume(pjmedia_session *session, pjmedia_dir dir); /** * Suspend receipt and transmission of all streams in media session * for the specified direction. * * @param session The media session. * @param dir The media direction to suspend. * * @return PJ_SUCCESS if success. */ PJ_DECL(pj_status_t) pjmedia_session_pause(pjmedia_session *session, pjmedia_dir dir); /** * Suspend receipt and transmission of individual stream in media session * for the specified direction. * * @param session The media session. * @param index The stream index. * @param dir The media direction to pause. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_session_pause_stream( pjmedia_session *session, unsigned index, pjmedia_dir dir); /** * Activate individual stream in media session for the specified direction. * * @param session The media session. * @param index The stream index. * @param dir The media direction to activate. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_session_resume_stream(pjmedia_session *session, unsigned index, pjmedia_dir dir); /** * Send RTCP SDES for the session. * * @param session The media session. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_session_send_rtcp_sdes( const pjmedia_session *session ); /** * Send RTCP BYE for the session. * * @param session The media session. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_session_send_rtcp_bye( const pjmedia_session *session ); /** * Enumerate media streams in the session. * * @param session The media session. * @param count On input, specifies the number of elements in * the array. On output, the number will be filled * with number of streams in the session. * @param strm_info Array of stream info. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_session_enum_streams( const pjmedia_session *session, unsigned *count, pjmedia_stream_info strm_info[]); /** * Get the media port interface of the specified stream. The media port * interface declares put_frame() and get_frame() function, which is the * only way for application to transmit and receive media frames from the * stream. * * @param session The media session. * @param index Stream index. * @param p_port Pointer to receive the media port interface for * the specified stream. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_session_get_port( pjmedia_session *session, unsigned index, pjmedia_port **p_port); /** * Get session statistics. The stream statistic shows various * indicators such as packet count, packet lost, jitter, delay, etc. * See also #pjmedia_session_get_stream_stat_jbuf() * * @param session The media session. * @param index Stream index. * @param stat Stream statistic. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_session_get_stream_stat(pjmedia_session *session, unsigned index, pjmedia_rtcp_stat *stat); /** * Reset session statistics. * * @param session The media session. * @param index Stream index. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_session_reset_stream_stat(pjmedia_session *session, unsigned index); #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) /** * Get extended session statistics. The extended statistic shows reports * from RTCP XR, such as per interval statistics summary (packet count, * packet lost, jitter, etc), VoIP metrics (delay, quality, etc) * * @param session The media session. * @param index Stream index. * @param stat_xr Stream extended statistics. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_session_get_stream_stat_xr( pjmedia_session *session, unsigned index, pjmedia_rtcp_xr_stat *stat_xr); #endif /** * Get current jitter buffer state for the specified stream. * See also #pjmedia_session_get_stream_stat() * * @param session The media session. * @param index Stream index. * @param state Jitter buffer state. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_session_get_stream_stat_jbuf( pjmedia_session *session, unsigned index, pjmedia_jb_state *state); /** * Dial DTMF digit to the stream, using RFC 2833 mechanism. * * @param session The media session. * @param index The stream index. * @param ascii_digits String of ASCII digits (i.e. 0-9*##A-B). * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_session_dial_dtmf( pjmedia_session *session, unsigned index, const pj_str_t *ascii_digits ); /** * Check if the specified stream has received DTMF digits. * * @param session The media session. * @param index The stream index. * * @return Non-zero (PJ_TRUE) if the stream has DTMF digits. */ PJ_DECL(pj_status_t) pjmedia_session_check_dtmf( pjmedia_session *session, unsigned index); /** * Retrieve DTMF digits from the specified stream. * * @param session The media session. * @param index The stream index. * @param ascii_digits Buffer to receive the digits. The length of this * buffer is indicated in the "size" argument. * @param size On input, contains the maximum digits to be copied * to the buffer. * On output, it contains the actual digits that has * been copied to the buffer. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_session_get_dtmf( pjmedia_session *session, unsigned index, char *ascii_digits, unsigned *size ); /** * Set callback to be called upon receiving DTMF digits. If callback is * registered, the stream will not buffer incoming DTMF but rather call * the callback as soon as DTMF digit is received completely. * * @param session The media session. * @param index The stream index. * @param cb Callback to be called upon receiving DTMF digits. * The DTMF digits will be given to the callback as * ASCII digits. * @param user_data User data to be returned back when the callback * is called. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_session_set_dtmf_callback(pjmedia_session *session, unsigned index, void (*cb)(pjmedia_stream*, void *user_data, int digit), void *user_data); /** * Destroy media session. * * @param session The media session. * * @return PJ_SUCCESS if success. */ PJ_DECL(pj_status_t) pjmedia_session_destroy(pjmedia_session *session); /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_SESSION_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/signatures.h ================================================ /* $Id: signatures.h 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_SIGNATURES_H__ #define __PJMEDIA_SIGNATURES_H__ /** * @file pjmedia/signatures.h * @brief Standard PJMEDIA object signatures */ #include PJ_BEGIN_DECL /** * @defgroup PJMEDIA_SIG Object Signatures * @ingroup PJMEDIA_BASE * @brief Standard PJMEDIA object signatures * @{ * * Object signature is a 32-bit integral value similar to FOURCC to help * identify PJMEDIA objects such as media ports, transports, codecs, etc. * There are several uses of this signature, for example a media port can * use the port object signature to verify that the given port instance * is the one that it created, and a receiver of \ref PJMEDIA_EVENT can * use the signature of the publisher to know which object emitted the * event. * * The 32-bit value of an object signature is generated by the following * macro: * * \verbatim #define PJMEDIA_SIGNATURE(a,b,c,d) (a<<24 | b<<16 | c<<8 | d) * \endverbatim * * The following convention is used to maintain order to the signature * values so that application can make use of it more effectively, and to * avoid conflict between the values themselves. For each object type or * class, a specific prefix will be assigned as signature, and a macro * is created to build a signature for such object: * * \verbatim Class Signature Signature creation and test macros --------------------------------------------------------------- Codec Cxxx PJMEDIA_SIG_CLASS_CODEC(b,c,d) PJMEDIA_SIG_IS_CLASS_CODEC(sig) Audio codec CAxx PJMEDIA_SIG_CLASS_AUD_CODEC(c,d) PJMEDIA_SIG_IS_CLASS_AUD_CODEC(sig) Video codec CVxx PJMEDIA_SIG_CLASS_VID_CODEC(c,d) PJMEDIA_SIG_IS_CLASS_VID_CODEC(sig) Media port Pxxx PJMEDIA_SIG_CLASS_PORT(b,c,d) PJMEDIA_SIG_IS_CLASS_PORT(sig) Audio media port PAxx PJMEDIA_SIG_CLASS_PORT_AUD(c,d) PJMEDIA_SIG_IS_CLASS_PORT_AUD(sig) Video media port PVxx PJMEDIA_SIG_CLASS_PORT_VID(c,d) PJMEDIA_SIG_IS_CLASS_PORT_VID(sig) Video device VDxx PJMEDIA_SIG_CLASS_VID_DEV(c,d) PJMEDIA_SIG_IS_CLASS_VID_DEV(sig) Video other VOxx PJMEDIA_SIG_CLASS_VID_OTHER(c,d) PJMEDIA_SIG_IS_CLASS_VID_OTHER(sig) Application object Axxx PJMEDIA_SIG_CLASS_APP(b,c,d) PJMEDIA_SIG_IS_CLASS_APP(sig) * \endverbatim * * In addition, signatures created in application code should have lowercase * letters to avoid conflict with built-in objects. */ /** * Type to store object signature. */ typedef pj_uint32_t pjmedia_obj_sig; /** * A utility function to convert signature to four letters string. * * @param sig The signature value. * @param buf Buffer to store the string, which MUST be at least * five bytes long. * * @return The string. */ PJ_INLINE(const char*) pjmedia_sig_name(pjmedia_obj_sig sig, char buf[]) { return pjmedia_fourcc_name(sig, buf); } /** * Macro to generate signature from four ASCII letters. */ #define PJMEDIA_SIGNATURE(a,b,c,d) PJMEDIA_FOURCC(a,b,c,d) /************************************************************************* * Codec signature ('Cxxx'). Please keep the constant names sorted. */ #define PJMEDIA_SIG_CLASS_CODEC(b,c,d) PJMEDIA_SIGNATURE('C',b,c,d) #define PJMEDIA_SIG_IS_CLASS_CODEC(sig) ((sig) >> 24 == 'C') /************************************************************************* * Audio codec signatures ('CAxx'). Please keep the constant names sorted. */ #define PJMEDIA_SIG_CLASS_AUD_CODEC(c,d) PJMEDIA_SIG_CLASS_CODEC('A',c,d) #define PJMEDIA_SIG_IS_CLASS_AUD_CODEC(s) ((s)>>24=='C' && (s)>>16=='A') /************************************************************************* * Video codec signatures ('CVxx'). Please keep the constant names sorted. */ #define PJMEDIA_SIG_CLASS_VID_CODEC(c,d) PJMEDIA_SIG_CLASS_CODEC('V',c,d) #define PJMEDIA_SIG_IS_CLASS_VID_CODEC(sig) ((s)>>24=='C' && (s)>>16=='V') #define PJMEDIA_SIG_VID_CODEC_FFMPEG PJMEDIA_SIG_CLASS_VID_CODEC('F','F') /************************************************************************* * Port signatures ('Pxxx'). Please keep the constant names sorted. */ #define PJMEDIA_SIG_CLASS_PORT(b,c,d) PJMEDIA_SIGNATURE('P',b,c,d) #define PJMEDIA_SIG_IS_CLASS_PORT(sig) ((sig) >> 24 == 'P') /************************************************************************* * Audio ports signatures ('PAxx'). Please keep the constant names sorted. */ #define PJMEDIA_SIG_CLASS_PORT_AUD(c,d) PJMEDIA_SIG_CLASS_PORT('A',c,d) #define PJMEDIA_SIG_IS_CLASS_PORT_AUD(s) ((s)>>24=='P' && (s)>>16=='A') #define PJMEDIA_SIG_PORT_BIDIR PJMEDIA_SIG_CLASS_PORT_AUD('B','D') #define PJMEDIA_SIG_PORT_CONF PJMEDIA_SIG_CLASS_PORT_AUD('C','F') #define PJMEDIA_SIG_PORT_CONF_PASV PJMEDIA_SIG_CLASS_PORT_AUD('C','P') #define PJMEDIA_SIG_PORT_CONF_SWITCH PJMEDIA_SIG_CLASS_PORT_AUD('C','S') #define PJMEDIA_SIG_PORT_ECHO PJMEDIA_SIG_CLASS_PORT_AUD('E','C') #define PJMEDIA_SIG_PORT_MEM_CAPTURE PJMEDIA_SIG_CLASS_PORT_AUD('M','C') #define PJMEDIA_SIG_PORT_MEM_PLAYER PJMEDIA_SIG_CLASS_PORT_AUD('M','P') #define PJMEDIA_SIG_PORT_MIXER PJMEDIA_SIG_CLASS_PORT_AUD('M','X') #define PJMEDIA_SIG_PORT_NULL PJMEDIA_SIG_CLASS_PORT_AUD('N','U') #define PJMEDIA_SIG_PORT_RESAMPLE PJMEDIA_SIG_CLASS_PORT_AUD('R','E') #define PJMEDIA_SIG_PORT_SPLIT_COMB PJMEDIA_SIG_CLASS_PORT_AUD('S','C') #define PJMEDIA_SIG_PORT_SPLIT_COMB_P PJMEDIA_SIG_CLASS_PORT_AUD('S','P') #define PJMEDIA_SIG_PORT_STEREO PJMEDIA_SIG_CLASS_PORT_AUD('S','R') #define PJMEDIA_SIG_PORT_STREAM PJMEDIA_SIG_CLASS_PORT_AUD('S','T') #define PJMEDIA_SIG_PORT_TONEGEN PJMEDIA_SIG_CLASS_PORT_AUD('T','O') #define PJMEDIA_SIG_PORT_WAV_PLAYER PJMEDIA_SIG_CLASS_PORT_AUD('W','P') #define PJMEDIA_SIG_PORT_WAV_PLAYLIST PJMEDIA_SIG_CLASS_PORT_AUD('W','Y') #define PJMEDIA_SIG_PORT_WAV_WRITER PJMEDIA_SIG_CLASS_PORT_AUD('W','W') /************************************************************************* * Video ports signatures ('PVxx'). Please keep the constant names sorted. */ #define PJMEDIA_SIG_CLASS_PORT_VID(c,d) PJMEDIA_SIG_CLASS_PORT('V',c,d) #define PJMEDIA_SIG_IS_CLASS_PORT_VID(s) ((s)>>24=='P' && (s)>>16=='V') /** AVI player signature. */ #define PJMEDIA_SIG_PORT_VID_AVI_PLAYER PJMEDIA_SIG_CLASS_PORT_VID('A','V') #define PJMEDIA_SIG_PORT_VID_STREAM PJMEDIA_SIG_CLASS_PORT_VID('S','T') #define PJMEDIA_SIG_PORT_VID_TEE PJMEDIA_SIG_CLASS_PORT_VID('T','E') /************************************************************************** * Video device signatures ('VDxx'). Please keep the constant names sorted. */ #define PJMEDIA_SIG_CLASS_VID_DEV(c,d) PJMEDIA_SIGNATURE('V','D',c,d) #define PJMEDIA_SIG_IS_CLASS_VID_DEV(s) ((s)>>24=='V' && (s)>>16=='D') #define PJMEDIA_SIG_VID_DEV_COLORBAR PJMEDIA_SIG_CLASS_VID_DEV('C','B') #define PJMEDIA_SIG_VID_DEV_SDL PJMEDIA_SIG_CLASS_VID_DEV('S','D') #define PJMEDIA_SIG_VID_DEV_V4L2 PJMEDIA_SIG_CLASS_VID_DEV('V','2') #define PJMEDIA_SIG_VID_DEV_DSHOW PJMEDIA_SIG_CLASS_VID_DEV('D','S') #define PJMEDIA_SIG_VID_DEV_QT PJMEDIA_SIG_CLASS_VID_DEV('Q','T') #define PJMEDIA_SIG_VID_DEV_IOS PJMEDIA_SIG_CLASS_VID_DEV('I','P') /********************************************************************* * Other video objects ('VOxx'). Please keep the constant names sorted. */ #define PJMEDIA_SIG_CLASS_VID_OTHER(c,d) PJMEDIA_SIGNATURE('V','O',c,d) #define PJMEDIA_SIG_IS_CLASS_VID_OTHER(s) ((s)>>24=='V' && (s)>>16=='O') #define PJMEDIA_SIG_VID_PORT PJMEDIA_SIG_CLASS_VID_OTHER('P','O') /********************************************************************* * Application class ('Axxx'). */ #define PJMEDIA_SIG_CLASS_APP(b,c,d) PJMEDIA_SIGNATURE('A',b,c,d) #define PJMEDIA_SIG_IS_CLASS_APP(s) ((s)>>24=='A') /** * @} PJSIP_MSG */ PJ_END_DECL #endif /* __PJMEDIA_SIGNATURES_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/silencedet.h ================================================ /* $Id: silencedet.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_SILENCE_DET_H__ #define __PJMEDIA_SILENCE_DET_H__ /** * @file silencedet.h * @brief Adaptive silence detector. */ #include /** * @defgroup PJMEDIA_SILENCEDET Adaptive Silence Detection * @ingroup PJMEDIA_FRAME_OP * @brief Adaptive Silence Detector * @{ */ PJ_BEGIN_DECL /** * Opaque declaration for silence detector. */ typedef struct pjmedia_silence_det pjmedia_silence_det; /** * Create voice activity detector with default settings. The default settings * are set to adaptive silence detection with the default threshold. * * @param pool Pool for allocating the structure. * @param clock_rate Clock rate. * @param samples_per_frame Number of samples per frame. The clock_rate and * samples_per_frame is only used to calculate the * frame time, from which some timing parameters * are calculated from. * @param p_sd Pointer to receive the silence detector instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_silence_det_create( pj_pool_t *pool, unsigned clock_rate, unsigned samples_per_frame, pjmedia_silence_det **p_sd ); /** * Set silence detector name to identify the particular silence detector * instance in the log. * * @param sd The silence detector. * @param name Name. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_silence_det_set_name(pjmedia_silence_det *sd, const char *name); /** * Set the sd to operate in fixed threshold mode. With fixed threshold mode, * the threshold will not be changed adaptively. * * @param sd The silence detector * @param threshold The silence threshold, or -1 to use default * threshold. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_silence_det_set_fixed( pjmedia_silence_det *sd, int threshold ); /** * Set the sd to operate in adaptive mode. This is the default mode * when the silence detector is created. * * @param sd The silence detector * @param threshold Initial threshold to be set, or -1 to use default * threshold. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_silence_det_set_adaptive(pjmedia_silence_det *sd, int threshold); /** * Set other silence detector parameters. * * @param sd The silence detector * @param before_silence Minimum duration of silence (in msec) before * silence is reported. If -1 is specified, then * the default value will be used. The default is * 400 msec. * @param recalc_time1 The interval (in msec) to recalculate threshold * in non-silence condition when adaptive silence * detection is set. If -1 is specified, then the * default value will be used. The default is 4000 * (msec). * @param recalc_time2 The interval (in msec) to recalculate threshold * in silence condition when adaptive silence detection * is set. If -1 is specified, then the default value * will be used. The default value is 2000 (msec). * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_silence_det_set_params( pjmedia_silence_det *sd, int before_silence, int recalc_time1, int recalc_time2); /** * Disable the silence detector. * * @param sd The silence detector * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_silence_det_disable( pjmedia_silence_det *sd ); /** * Perform voice activity detection on the given input samples. This * function uses #pjmedia_calc_avg_signal() and #pjmedia_silence_det_apply() * for its calculation. * * @param sd The silence detector instance. * @param samples Pointer to 16-bit PCM input samples. * @param count Number of samples in the input. * @param p_level Optional pointer to receive average signal level * of the input samples. * * @return Non zero if signal is silence. */ PJ_DECL(pj_bool_t) pjmedia_silence_det_detect( pjmedia_silence_det *sd, const pj_int16_t samples[], pj_size_t count, pj_int32_t *p_level); /** * Calculate average signal level for the given samples. * * @param samples Pointer to 16-bit PCM samples. * @param count Number of samples in the input. * * @return The average signal level, which simply is total level * divided by number of samples. */ PJ_DECL(pj_int32_t) pjmedia_calc_avg_signal( const pj_int16_t samples[], pj_size_t count ); /** * Perform voice activity detection, given the specified average signal * level. * * @param sd The silence detector instance. * @param level Signal level. * * @return Non zero if signal is silence. */ PJ_DECL(pj_bool_t) pjmedia_silence_det_apply( pjmedia_silence_det *sd, pj_uint32_t level); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_SILENCE_DET_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/sound.h ================================================ /* $Id: sound.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_SOUND_H__ #define __PJMEDIA_SOUND_H__ /** * @file sound.h * @brief Legacy sound device API */ #include #include PJ_BEGIN_DECL /** * @defgroup PJMED_SND Portable Sound Hardware Abstraction * @ingroup PJMED_SND_PORT * @brief PJMEDIA abstraction for sound device hardware * @{ * * Warning: this sound device API has been deprecated * and replaced by PJMEDIA Audio Device API. Please see * http://trac.pjsip.org/repos/wiki/Audio_Dev_API for more * information. * * This section describes lower level abstraction for sound device * hardware. Application normally uses the higher layer @ref * PJMED_SND_PORT abstraction since it works seamlessly with * @ref PJMEDIA_PORT. * * The sound hardware abstraction basically runs asychronously, * and application must register callbacks to be called to receive/ * supply audio frames from/to the sound hardware. * * A full duplex sound stream (created with #pjmedia_snd_open()) * requires application to supply two callbacks: * - rec_cb callback to be called when it has finished * capturing one media frame, and * - play_cb callback to be called when it needs media * frame to be played to the sound playback hardware. * * Half duplex sound stream (created with #pjmedia_snd_open_rec() or * #pjmedia_snd_open_player()) will only need one of the callback to * be specified. * * After sound stream is created, application need to call * #pjmedia_snd_stream_start() to start capturing/playing back media * frames from/to the sound device. */ /** Opaque declaration for pjmedia_snd_stream. */ typedef struct pjmedia_snd_stream pjmedia_snd_stream; /** * Device information structure returned by #pjmedia_snd_get_dev_info. */ typedef struct pjmedia_snd_dev_info { char name[PJMEDIA_AUD_DEV_INFO_NAME_LEN]; /**< Device name. */ unsigned input_count; /**< Max number of input channels. */ unsigned output_count; /**< Max number of output channels. */ unsigned default_samples_per_sec;/**< Default sampling rate. */ } pjmedia_snd_dev_info; /** * Stream information, can be retrieved from a live stream by calling * #pjmedia_snd_stream_get_info(). */ typedef struct pjmedia_snd_stream_info { pjmedia_dir dir; /**< Stream direction. */ int play_id; /**< Playback dev id, or -1 for rec only*/ int rec_id; /**< Capture dev id, or -1 for play only*/ unsigned clock_rate; /**< Actual clock rate. */ unsigned channel_count; /**< Number of channels. */ unsigned samples_per_frame; /**< Samples per frame. */ unsigned bits_per_sample; /**< Bits per sample. */ unsigned rec_latency; /**< Record latency, in samples. */ unsigned play_latency; /**< Playback latency, in samples. */ } pjmedia_snd_stream_info; /** * This callback is called by player stream when it needs additional data * to be played by the device. Application must fill in the whole of output * buffer with sound samples. * * @param user_data User data associated with the stream. * @param timestamp Timestamp, in samples. * @param output Buffer to be filled out by application. * @param size The size requested in bytes, which will be equal to * the size of one whole packet. * * @return Non-zero to stop the stream. */ typedef pj_status_t (*pjmedia_snd_play_cb)(/* in */ void *user_data, /* in */ pj_uint32_t timestamp, /* out */ void *output, /* out */ unsigned size); /** * This callback is called by recorder stream when it has captured the whole * packet worth of audio samples. * * @param user_data User data associated with the stream. * @param timestamp Timestamp, in samples. * @param output Buffer containing the captured audio samples. * @param size The size of the data in the buffer, in bytes. * * @return Non-zero to stop the stream. */ typedef pj_status_t (*pjmedia_snd_rec_cb)(/* in */ void *user_data, /* in */ pj_uint32_t timestamp, /* in */ void *input, /* in*/ unsigned size); /** * Init the sound library. * * @param factory The sound factory. * * @return Zero on success. */ PJ_DECL(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory); /** * Get the number of devices detected by the library. * * @return Number of devices. */ PJ_DECL(int) pjmedia_snd_get_dev_count(void); /** * Get device info. * * @param index The index of the device, which should be in the range * from zero to #pjmedia_snd_get_dev_count - 1. */ PJ_DECL(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index); /** * Set sound device latency, this function must be called before sound device * opened, or otherwise default latency setting will be used, @see * PJMEDIA_SND_DEFAULT_REC_LATENCY & PJMEDIA_SND_DEFAULT_PLAY_LATENCY. * * Choosing latency value is not straightforward, it should accomodate both * minimum latency and stability. Lower latency tends to cause sound device * less reliable (producing audio dropouts) on CPU load disturbance. Moreover, * the best latency setting may vary based on many aspects, e.g: sound card, * CPU, OS, kernel, etc. * * @param input_latency The latency of input device, in ms, set to 0 * for default PJMEDIA_SND_DEFAULT_REC_LATENCY. * @param output_latency The latency of output device, in ms, set to 0 * for default PJMEDIA_SND_DEFAULT_PLAY_LATENCY. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency, unsigned output_latency); /** * Create sound stream for both capturing audio and audio playback, from the * same device. This is the recommended way to create simultaneous recorder * and player streams (instead of creating separate capture and playback * streams), because it works on backends that does not allow * a device to be opened more than once. * * @param rec_id Device index for recorder/capture stream, or * -1 to use the first capable device. * @param play_id Device index for playback stream, or -1 to use * the first capable device. * @param clock_rate Sound device's clock rate to set. * @param channel_count Set number of channels, 1 for mono, or 2 for * stereo. The channel count determines the format * of the frame. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Set the number of bits per sample. The normal * value for this parameter is 16 bits per sample. * @param rec_cb Callback to handle captured audio samples. * @param play_cb Callback to be called when the sound player needs * more audio samples to play. * @param user_data User data to be associated with the stream. * @param p_snd_strm Pointer to receive the stream instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_snd_open(int rec_id, int play_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_snd_rec_cb rec_cb, pjmedia_snd_play_cb play_cb, void *user_data, pjmedia_snd_stream **p_snd_strm); /** * Create a unidirectional audio stream for capturing audio samples from * the sound device. * * @param index Device index, or -1 to let the library choose the * first available device. * @param clock_rate Sound device's clock rate to set. * @param channel_count Set number of channels, 1 for mono, or 2 for * stereo. The channel count determines the format * of the frame. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Set the number of bits per sample. The normal * value for this parameter is 16 bits per sample. * @param rec_cb Callback to handle captured audio samples. * @param user_data User data to be associated with the stream. * @param p_snd_strm Pointer to receive the stream instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_snd_open_rec( int index, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_snd_rec_cb rec_cb, void *user_data, pjmedia_snd_stream **p_snd_strm); /** * Create a unidirectional audio stream for playing audio samples to the * sound device. * * @param index Device index, or -1 to let the library choose the * first available device. * @param clock_rate Sound device's clock rate to set. * @param channel_count Set number of channels, 1 for mono, or 2 for * stereo. The channel count determines the format * of the frame. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Set the number of bits per sample. The normal * value for this parameter is 16 bits per sample. * @param play_cb Callback to be called when the sound player needs * more audio samples to play. * @param user_data User data to be associated with the stream. * @param p_snd_strm Pointer to receive the stream instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_snd_open_player( int index, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_snd_play_cb play_cb, void *user_data, pjmedia_snd_stream **p_snd_strm ); /** * Get information about live stream. * * @param strm The stream to be queried. * @param pi Pointer to stream information to be filled up with * information about the stream. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm, pjmedia_snd_stream_info *pi); /** * Start the stream. * * @param stream The recorder or player stream. * * @return Zero on success. */ PJ_DECL(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream); /** * Stop the stream. * * @param stream The recorder or player stream. * * @return Zero on success. */ PJ_DECL(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream); /** * Destroy the stream. * * @param stream The recorder of player stream. * * @return Zero on success. */ PJ_DECL(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream); /** * Deinitialize sound library. * * @return Zero on success. */ PJ_DECL(pj_status_t) pjmedia_snd_deinit(void); /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_SOUND_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/sound_port.h ================================================ /* $Id: sound_port.h 4082 2012-04-24 13:09:14Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_SOUND_PORT_H__ #define __PJMEDIA_SOUND_PORT_H__ /** * @file sound_port.h * @brief Media port connection abstraction to sound device. */ #include #include #include PJ_BEGIN_DECL /** * @defgroup PJMED_SND_PORT Sound Device Port * @ingroup PJMEDIA_PORT_CLOCK * @brief Media Port Connection Abstraction to the Sound Device @{ As explained in @ref PJMED_SND, the sound hardware abstraction provides some callbacks for its user: - it calls rec_cb callback when it has finished capturing one media frame, and - it calls play_cb when it needs media frame to be played to the sound playback hardware. The @ref PJMED_SND_PORT (the object being explained here) add a thin wrapper to the hardware abstraction: - it will call downstream port's put_frame() when rec_cb() is called (i.e. when the sound hardware has finished capturing frame), and - it will call downstream port's get_frame() when play_cb() is called (i.e. every time the sound hardware needs more frames to be played to the playback hardware). This simple abstraction enables media to flow automatically (and in timely manner) from the downstream media port to the sound device. In other words, the sound device port supplies media clock to the ports. The media clock concept is explained in @ref PJMEDIA_PORT_CLOCK section. Application registers downstream port to the sound device port by calling #pjmedia_snd_port_connect(); */ /** * Sound port options. */ enum pjmedia_snd_port_option { /** * Don't start the audio device when creating a sound port. */ PJMEDIA_SND_PORT_NO_AUTO_START = 1 }; /** * This structure specifies the parameters to create the sound port. * Use pjmedia_snd_port_param_default() to initialize this structure with * default values (mostly zeroes) */ typedef struct pjmedia_snd_port_param { /** * Base structure. */ pjmedia_aud_param base; /** * Sound port creation options. */ unsigned options; /** * Echo cancellation options/flags. */ unsigned ec_options; /** * Arbitrary user data for playback and record preview callbacks below. */ void *user_data; /** * Optional callback for audio frame preview right before queued to * the speaker. * Notes: * - application MUST NOT block or perform long operation in the callback * as the callback may be executed in sound device thread * - when using software echo cancellation, application MUST NOT modify * the audio data from within the callback, otherwise the echo canceller * will not work properly. * - the return value of the callback will be ignored */ pjmedia_aud_play_cb on_play_frame; /** * Optional callback for audio frame preview recorded from the microphone * before being processed by any media component such as software echo * canceller. * Notes: * - application MUST NOT block or perform long operation in the callback * as the callback may be executed in sound device thread * - when using software echo cancellation, application MUST NOT modify * the audio data from within the callback, otherwise the echo canceller * will not work properly. * - the return value of the callback will be ignored */ pjmedia_aud_rec_cb on_rec_frame; } pjmedia_snd_port_param; /** * Initialize pjmedia_snd_port_param with default values. * * @param prm The parameter. */ PJ_DECL(void) pjmedia_snd_port_param_default(pjmedia_snd_port_param *prm); /** * This opaque type describes sound device port connection. * Sound device port is not a media port, but it is used to connect media * port to the sound device. */ typedef struct pjmedia_snd_port pjmedia_snd_port; /** * Create bidirectional sound port for both capturing and playback of * audio samples. * * @param pool Pool to allocate sound port structure. * @param rec_id Device index for recorder/capture stream, or * -1 to use the first capable device. * @param play_id Device index for playback stream, or -1 to use * the first capable device. * @param clock_rate Sound device's clock rate to set. * @param channel_count Set number of channels, 1 for mono, or 2 for * stereo. The channel count determines the format * of the frame. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Set the number of bits per sample. The normal * value for this parameter is 16 bits per sample. * @param options Options flag. * @param p_port Pointer to receive the sound device port instance. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_snd_port_create( pj_pool_t *pool, int rec_id, int play_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_snd_port **p_port); /** * Create unidirectional sound device port for capturing audio streams from * the sound device with the specified parameters. * * @param pool Pool to allocate sound port structure. * @param index Device index, or -1 to let the library choose the * first available device. * @param clock_rate Sound device's clock rate to set. * @param channel_count Set number of channels, 1 for mono, or 2 for * stereo. The channel count determines the format * of the frame. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Set the number of bits per sample. The normal * value for this parameter is 16 bits per sample. * @param options Options flag. * @param p_port Pointer to receive the sound device port instance. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_snd_port_create_rec(pj_pool_t *pool, int index, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_snd_port **p_port); /** * Create unidirectional sound device port for playing audio streams with the * specified parameters. * * @param pool Pool to allocate sound port structure. * @param index Device index, or -1 to let the library choose the * first available device. * @param clock_rate Sound device's clock rate to set. * @param channel_count Set number of channels, 1 for mono, or 2 for * stereo. The channel count determines the format * of the frame. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Set the number of bits per sample. The normal * value for this parameter is 16 bits per sample. * @param options Options flag. * @param p_port Pointer to receive the sound device port instance. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_snd_port_create_player(pj_pool_t *pool, int index, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_snd_port **p_port); /** * Create sound device port according to the specified parameters. * * @param pool Pool to allocate sound port structure. * @param prm Sound port parameter. * @param p_port Pointer to receive the sound device port instance. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool, const pjmedia_snd_port_param *prm, pjmedia_snd_port **p_port); /** * Destroy sound device port. * * @param snd_port The sound device port. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_snd_port_destroy(pjmedia_snd_port *snd_port); /** * Retrieve the sound stream associated by this sound device port. * * @param snd_port The sound device port. * * @return The sound stream instance. */ PJ_DECL(pjmedia_aud_stream*) pjmedia_snd_port_get_snd_stream( pjmedia_snd_port *snd_port); /** * Change the echo cancellation settings. The echo cancellation settings * should have been specified when this sound port was created, by setting * the appropriate fields in the pjmedia_aud_param, because not all sound * device implementation supports changing the EC setting once the device * has been opened. * * The behavior of this function depends on whether device or software AEC * is being used. If the device supports AEC, this function will forward * the change request to the device and it will be up to the device whether * to support the request. If software AEC is being used (the software EC * will be used if the device does not support AEC), this function will * change the software EC settings. * * @param snd_port The sound device port. * @param pool Pool to re-create the echo canceller if necessary. * @param tail_ms Maximum echo tail length to be supported, in * miliseconds. If zero is specified, the EC would * be disabled. * @param options The options to be passed to #pjmedia_echo_create(). * This is only used if software EC is being used. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_snd_port_set_ec( pjmedia_snd_port *snd_port, pj_pool_t *pool, unsigned tail_ms, unsigned options); /** * Get current echo canceller tail length, in miliseconds. The tail length * will be zero if EC is not enabled. * * @param snd_port The sound device port. * @param p_length Pointer to receive the tail length. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_snd_port_get_ec_tail(pjmedia_snd_port *snd_port, unsigned *p_length); /** * Get a clock source from the sound port. * * @param snd_port The sound port. * @param dir Sound port's direction. * * @return The clock source. */ PJ_DECL(pjmedia_clock_src *) pjmedia_snd_port_get_clock_src( pjmedia_snd_port *snd_port, pjmedia_dir dir ); /** * Reset the EC state in the sound port. * * @param snd_port The sound device port. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_snd_port_reset_ec_state(pjmedia_snd_port *snd_port); /** * Connect a port to the sound device port. If the sound device port has a * sound recorder device, then this will start periodic function call to * the port's put_frame() function. If the sound device has a sound player * device, then this will start periodic function call to the port's * get_frame() function. * * For this version of PJMEDIA, the media port MUST have the same audio * settings as the sound device port, or otherwise the connection will * fail. This means the port MUST have the same clock_rate, channel count, * samples per frame, and bits per sample as the sound device port. * * @param snd_port The sound device port. * @param port The media port to be connected. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_snd_port_connect(pjmedia_snd_port *snd_port, pjmedia_port *port); /** * Retrieve the port instance currently attached to the sound port, if any. * * @param snd_port The sound device port. * * @return The port instance currently attached to the * sound device port, or NULL if there is no port * currently attached to the sound device port. */ PJ_DECL(pjmedia_port*) pjmedia_snd_port_get_port(pjmedia_snd_port *snd_port); /** * Disconnect currently attached port from the sound device port. * * @param snd_port The sound device port. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_snd_port_disconnect(pjmedia_snd_port *snd_port); /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_SOUND_PORT_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/splitcomb.h ================================================ /* $Id: splitcomb.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_SPLITCOMB_H__ #define __PJMEDIA_SPLITCOMB_H__ /** * @file splitcomb.h * @brief Media channel splitter/combiner port. */ #include /** * @addtogroup PJMEDIA_SPLITCOMB Media channel splitter/combiner * @ingroup PJMEDIA_PORT * @brief Split and combine multiple mono-channel media ports into * a single multiple-channels media port * @{ * * This section describes media port to split and combine media * channels in the stream. * * A splitter/combiner splits a single stereo/multichannels audio frame into * multiple audio frames to each channel when put_frame() is called, * and combines mono frames from each channel into a stereo/multichannel * frame when get_frame() is called. A common application for the splitter/ * combiner is to split frames from stereo to mono and vise versa. */ PJ_BEGIN_DECL /** * Create a media splitter/combiner with the specified parameters. * When the splitter/combiner is created, it creates an instance of * pjmedia_port. This media port represents the stereo/multichannel side * of the splitter/combiner. Application needs to supply the splitter/ * combiner with a media port for each audio channels. * * @param pool Pool to allocate memory to create the splitter/ * combiner. * @param clock_rate Audio clock rate/sampling rate. * @param channel_count Number of channels. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Bits per sample. * @param options Optional flags. * @param p_splitcomb Pointer to receive the splitter/combiner. * * @return PJ_SUCCESS on success, or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_splitcomb_create(pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_port **p_splitcomb); /** * Supply the splitter/combiner with media port for the specified channel * number. The media port will be called at the * same phase as the splitter/combiner; which means that when application * calls get_frame() of the splitter/combiner, it will call get_frame() * for all ports that have the same phase. And similarly for put_frame(). * * @param splitcomb The splitter/combiner. * @param ch_num Audio channel starting number (zero based). * @param options Must be zero at the moment. * @param port The media port. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_splitcomb_set_channel(pjmedia_port *splitcomb, unsigned ch_num, unsigned options, pjmedia_port *port); /** * Create a reverse phase media port for the specified channel number. * For channels with reversed phase, when application calls put_frame() to * the splitter/combiner, the splitter/combiner will only put the frame to * a buffer. Later on, when application calls get_frame() on the channel's * media port, it will return the frame that are available in the buffer. * The same process happens when application calls put_frame() to the * channel's media port, it will only put the frame to another buffer, which * will be returned when application calls get_frame() to the splitter's * media port. So this effectively reverse the phase of the media port. * * @param pool The pool to allocate memory for the port and * buffers. * @param splitcomb The splitter/combiner. * @param ch_num Audio channel starting number (zero based). * @param options Normally is zero, but the lower 8-bit of the * options can be used to specify the number of * buffers in the circular buffer. If zero, then * default number will be used (default: 8). * @param p_chport The media port created with reverse phase for * the specified audio channel. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, pjmedia_port *splitcomb, unsigned ch_num, unsigned options, pjmedia_port **p_chport); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_SPLITCOMB_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/stereo.h ================================================ /* $Id: stereo.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_STEREO_H__ #define __PJMEDIA_STEREO_H__ /** * @file stereo.h * @brief Monochannel and multichannel converter. */ #include #include #include #include /** * @defgroup PJMEDIA_STEREO Monochannel and multichannel audio frame converter * @ingroup PJMEDIA_FRAME_OP * @brief Mono - multi-channels audio conversion * @{ * */ PJ_BEGIN_DECL /** * Multichannel to monochannel conversion mixes samples from all channels * into the monochannel. */ #define PJMEDIA_STEREO_MIX PJ_TRUE /** * Multichannel to monochannel conversion, it has two operation mode specified * by param options, @see pjmedia_stereo_options. This function can work safely * using the same buffer (in place conversion). * * @param mono Output buffer to store the mono frame extracted * from the multichannels frame. * @param multi Input frame containing multichannels audio. * @param channel_count Number of channels in the input frame. * @param samples_per_frame Number of samples in the input frame. * @param mix If the value is PJ_TRUE then the input channels * will be mixed to produce output frame, otherwise * only frame from channel_src will be copied to the * output frame. * @param channel_src When mixing is disabled, the mono output frame * will be copied from this channel number. * * @return PJ_SUCCESS on success; */ PJ_INLINE(pj_status_t) pjmedia_convert_channel_nto1(pj_int16_t mono[], const pj_int16_t multi[], unsigned channel_count, unsigned samples_per_frame, pj_bool_t mix, unsigned channel_src) { unsigned i; PJ_ASSERT_RETURN(mono && multi && channel_count && samples_per_frame && channel_src < channel_count, PJ_EINVAL); if (mix==PJ_FALSE) { for (i = channel_src; i < samples_per_frame; i += channel_count) { *mono = multi[i]; ++mono; } } else { unsigned j; for (i = 0; i < samples_per_frame; i += channel_count) { int tmp = 0; for(j = 0; j < channel_count; ++j) tmp += multi[i+j]; if (tmp > 32767) tmp = 32767; else if (tmp < -32768) tmp = -32768; *mono = (pj_int16_t) tmp; ++mono; } } return PJ_SUCCESS; } /** * Monochannel to multichannel conversion, it will just duplicate the samples * from monochannel frame to all channels in the multichannel frame. * This function can work safely using the same buffer (in place conversion) * as long as the buffer is big enough for the multichannel samples. * * @param multi Output buffer to store the multichannels frame * mixed from the mono frame. * @param mono The input monochannel audio frame. * @param channel_count Desired number of channels in the output frame. * @param samples_per_frame Number of samples in the input frame. * @param options Options for conversion, currently must be zero. * * @return PJ_SUCCESS on success; */ PJ_INLINE(pj_status_t) pjmedia_convert_channel_1ton(pj_int16_t multi[], const pj_int16_t mono[], unsigned channel_count, unsigned samples_per_frame, unsigned options) { const pj_int16_t *src; PJ_ASSERT_RETURN(mono && multi && channel_count && samples_per_frame, PJ_EINVAL); PJ_ASSERT_RETURN(options == 0, PJ_EINVAL); PJ_UNUSED_ARG(options); src = mono + samples_per_frame - 1; samples_per_frame *= channel_count; while (samples_per_frame) { unsigned i; for (i=1; i<=channel_count; ++i) multi[samples_per_frame-i] = *src; samples_per_frame -= channel_count; --src; } return PJ_SUCCESS; } /** * Options for channel converter port. The #pjmedia_stereo_options is also * valid for this port options. */ typedef enum pjmedia_stereo_port_options { /** * Specifies whether this port should not destroy downstream port when * this port is destroyed. */ PJMEDIA_STEREO_DONT_DESTROY_DN = 4 } pjmedia_stereo_port_options; /** * Create a mono-multi channel converter port. This creates a converter session, * which will adjust the samples of audio frame to a different channel count * when the port's get_frame() and put_frame() is called. * * When the port's get_frame() is called, this port will get a frame from * the downstream port and convert the frame to the target channel count before * returning it to the caller. * * When the port's put_frame() is called, this port will convert the frame * to the downstream port's channel count before giving the frame to the * downstream port. * * @param pool Pool to allocate the structure and buffers. * @param dn_port The downstream port, which channel count is to * be converted to the target channel count. * @param channel_count This port channel count. * @param options Bitmask flags from #pjmedia_stereo_port_options * and also application may add PJMEDIA_STEREO_MIX * to mix channels. * When this flag is zero, the default behavior * is to use simple N-to-1 channel converter and * to destroy downstream port when this port is * destroyed. * @param p_port Pointer to receive the stereo port instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_stereo_port_create( pj_pool_t *pool, pjmedia_port *dn_port, unsigned channel_count, unsigned options, pjmedia_port **p_port ); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_STEREO_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/stream.h ================================================ /* $Id: stream.h 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_STREAM_H__ #define __PJMEDIA_STREAM_H__ /** * @file stream.h * @brief Media Stream. */ #include #include #include #include #include #include #include #include PJ_BEGIN_DECL /** * @defgroup PJMED_STRM Streams * @ingroup PJMEDIA_PORT * @brief Communicating with remote peer via the network * @{ * * A media stream is a bidirectional multimedia communication between two * endpoints. It corresponds to a media description (m= line) in SDP * session descriptor. * * A media stream consists of two unidirectional channels: * - encoding channel, which transmits unidirectional media to remote, and * - decoding channel, which receives unidirectional media from remote. * * A media stream exports media port interface (see @ref PJMEDIA_PORT) * and application normally uses this interface to interconnect the stream * to other PJMEDIA components. * * A media stream internally manages the following objects: * - an instance of media codec (see @ref PJMEDIA_CODEC), * - an @ref PJMED_JBUF, * - two instances of RTP sessions (#pjmedia_rtp_session, one for each * direction), * - one instance of RTCP session (#pjmedia_rtcp_session), * - and a reference to media transport to send and receive packets * to/from the network (see @ref PJMEDIA_TRANSPORT). * * Streams are created by calling #pjmedia_stream_create(), specifying * #pjmedia_stream_info structure in the parameter. Application can construct * the #pjmedia_stream_info structure manually, or use * #pjmedia_stream_info_from_sdp() or #pjmedia_session_info_from_sdp() * functions to construct the #pjmedia_stream_info from local and remote * SDP session descriptors. * * Application can also use @ref PJMEDIA_SESSION to indirectly create the * streams. */ /** * Opaque declaration for media channel. * Media channel is unidirectional flow of media from sender to * receiver. */ typedef struct pjmedia_channel pjmedia_channel; /** * This structure describes media stream information. Each media stream * corresponds to one "m=" line in SDP session descriptor, and it has * its own RTP/RTCP socket pair. */ typedef struct pjmedia_stream_info { pjmedia_type type; /**< Media type (audio, video) */ pjmedia_tp_proto proto; /**< Transport protocol (RTP/AVP, etc.) */ pjmedia_dir dir; /**< Media direction. */ pj_sockaddr rem_addr; /**< Remote RTP address */ pj_sockaddr rem_rtcp; /**< Optional remote RTCP address. If sin_family is zero, the RTP address will be calculated from RTP. */ #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) pj_bool_t rtcp_xr_enabled; /**< Specify whether RTCP XR is enabled.*/ pj_uint32_t rtcp_xr_interval; /**< RTCP XR interval. */ pj_sockaddr rtcp_xr_dest;/** #include PJ_BEGIN_DECL /** * This is internal function for parsing SDP format parameter of specific * format or payload type, used by stream in generating stream info from SDP. * * @param pool Pool to allocate memory, if pool is NULL, the fmtp * string pointers will point to the original string in * the SDP media descriptor. * @param m The SDP media containing the format parameter to * be parsed. * @param pt The format or payload type. * @param fmtp The format parameter to store the parsing result. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_stream_info_parse_fmtp(pj_pool_t *pool, const pjmedia_sdp_media *m, unsigned pt, pjmedia_codec_fmtp *fmtp); PJ_END_DECL #endif /* __PJMEDIA_STREAM_COMMON_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/symbian_sound_aps.h ================================================ /* $Id: symbian_sound_aps.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_SYMBIAN_SOUND_APS_H__ #define __PJMEDIA_SYMBIAN_SOUND_APS_H__ /** * @file symbian_sound_aps.h * @brief Sound device wrapper using Audio Proxy Server on * Symbian S60 3rd edition. */ #include PJ_BEGIN_DECL /** * Set audio routing for APS sound device. * * @param stream The sound device stream, the stream should be started * before calling this function. * @param route Audio routing to be set. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_snd_aps_set_route( pjmedia_snd_stream *stream, pjmedia_snd_route route); PJ_END_DECL #endif /* __PJMEDIA_SYMBIAN_SOUND_APS_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/tonegen.h ================================================ /* $Id: tonegen.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_TONEGEN_PORT_H__ #define __PJMEDIA_TONEGEN_PORT_H__ /** * @file tonegen.h * @brief Tone (sine, MF, DTMF) generator media port. */ #include /** * @defgroup PJMEDIA_MF_DTMF_TONE_GENERATOR Multi-frequency tone generator * @ingroup PJMEDIA_PORT * @brief Multi-frequency tone generator * @{ * * This page describes tone generator media port. A tone generator can be * used to generate a single frequency sine wave or dual frequency tones * such as DTMF. * * The tone generator media port provides two functions to generate tones. * The function #pjmedia_tonegen_play() can be used to generate arbitrary * single or dual frequency tone, and #pjmedia_tonegen_play_digits() is * used to play digits such as DTMF. Each tone specified in the playback * function has individual on and off signal duration that must be * specified by application. * * In order to play digits such as DTMF, the tone generator is equipped * with digit map, which contain information about the frequencies of * the digits. The default digit map is DTMF (0-9,a-d,*,#), but application * may specifiy different digit map to the tone generator by calling * #pjmedia_tonegen_set_digit_map() function. */ PJ_BEGIN_DECL /** * This structure describes individual MF digits to be played * with #pjmedia_tonegen_play(). */ typedef struct pjmedia_tone_desc { short freq1; /**< First frequency. */ short freq2; /**< Optional second frequency. */ short on_msec; /**< Playback ON duration, in miliseconds. */ short off_msec; /**< Playback OFF duration, ini miliseconds. */ short volume; /**< Volume (1-32767), or 0 for default, which PJMEDIA_TONEGEN_VOLUME will be used. */ short flags; /**< Currently internal flags, must be 0 */ } pjmedia_tone_desc; /** * This structure describes individual MF digits to be played * with #pjmedia_tonegen_play_digits(). */ typedef struct pjmedia_tone_digit { char digit; /**< The ASCI identification for the digit. */ short on_msec; /**< Playback ON duration, in miliseconds. */ short off_msec; /**< Playback OFF duration, ini miliseconds. */ short volume; /**< Volume (1-32767), or 0 for default, which PJMEDIA_TONEGEN_VOLUME will be used. */ } pjmedia_tone_digit; /** * This structure describes the digit map which is used by the tone generator * to produce tones from an ASCII digits. * Digit map used by a particular tone generator can be retrieved/set with * #pjmedia_tonegen_get_digit_map() and #pjmedia_tonegen_set_digit_map(). */ typedef struct pjmedia_tone_digit_map { unsigned count; /**< Number of digits in the map. */ struct { char digit; /**< The ASCI identification for the digit. */ short freq1; /**< First frequency. */ short freq2; /**< Optional second frequency. */ } digits[16]; /**< Array of digits in the digit map. */ } pjmedia_tone_digit_map; /** * Tone generator options. */ enum { /** * Play the tones in loop, restarting playing the first tone after * the last tone has been played. */ PJMEDIA_TONEGEN_LOOP = 1, /** * Disable mutex protection to the tone generator. */ PJMEDIA_TONEGEN_NO_LOCK = 2 }; /** * Create an instance of tone generator with the specified parameters. * When the tone generator is first created, it will be loaded with the * default digit map. * * @param pool Pool to allocate memory for the port structure. * @param clock_rate Sampling rate. * @param channel_count Number of channels. Currently only mono and stereo * are supported. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Number of bits per sample. This version of PJMEDIA * only supports 16bit per sample. * @param options Option flags. Application may specify * PJMEDIA_TONEGEN_LOOP to play the tone in a loop. * @param p_port Pointer to receive the port instance. * * @return PJ_SUCCESS on success, or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_tonegen_create(pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_port **p_port); /** * Create an instance of tone generator with the specified parameters. * When the tone generator is first created, it will be loaded with the * default digit map. * * @param pool Pool to allocate memory for the port structure. * @param name Optional name for the tone generator. * @param clock_rate Sampling rate. * @param channel_count Number of channels. Currently only mono and stereo * are supported. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Number of bits per sample. This version of PJMEDIA * only supports 16bit per sample. * @param options Option flags. Application may specify * PJMEDIA_TONEGEN_LOOP to play the tone in a loop. * @param p_port Pointer to receive the port instance. * * @return PJ_SUCCESS on success, or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_tonegen_create2(pj_pool_t *pool, const pj_str_t *name, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_port **p_port); /** * Check if the tone generator is still busy producing some tones. * * @param tonegen The tone generator instance. * * @return Non-zero if busy. */ PJ_DECL(pj_bool_t) pjmedia_tonegen_is_busy(pjmedia_port *tonegen); /** * Instruct the tone generator to stop current processing. * * @param tonegen The tone generator instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_tonegen_stop(pjmedia_port *tonegen); /** * Rewind the playback. This will start the playback to the first * tone in the playback list. * * @param tonegen The tone generator instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_tonegen_rewind(pjmedia_port *tonegen); /** * Instruct the tone generator to play single or dual frequency tones * with the specified duration. The new tones will be appended to currently * playing tones, unless #pjmedia_tonegen_stop() is called before calling * this function. The playback will begin as soon as the first get_frame() * is called to the generator. * * @param tonegen The tone generator instance. * @param count The number of tones in the array. * @param tones Array of tones to be played. * @param options Option flags. Application may specify * PJMEDIA_TONEGEN_LOOP to play the tone in a loop. * * @return PJ_SUCCESS on success, or PJ_ETOOMANY if * there are too many digits in the queue. */ PJ_DECL(pj_status_t) pjmedia_tonegen_play(pjmedia_port *tonegen, unsigned count, const pjmedia_tone_desc tones[], unsigned options); /** * Instruct the tone generator to play multiple MF digits with each of * the digits having individual ON/OFF duration. Each of the digit in the * digit array must have the corresponding descriptor in the digit map. * The new tones will be appended to currently playing tones, unless * #pjmedia_tonegen_stop() is called before calling this function. * The playback will begin as soon as the first get_frame() is called * to the generator. * * @param tonegen The tone generator instance. * @param count Number of digits in the array. * @param digits Array of MF digits. * @param options Option flags. Application may specify * PJMEDIA_TONEGEN_LOOP to play the tone in a loop. * * @return PJ_SUCCESS on success, or PJ_ETOOMANY if * there are too many digits in the queue, or * PJMEDIA_RTP_EINDTMF if invalid digit is * specified. */ PJ_DECL(pj_status_t) pjmedia_tonegen_play_digits(pjmedia_port *tonegen, unsigned count, const pjmedia_tone_digit digits[], unsigned options); /** * Get the digit-map currently used by this tone generator. * * @param tonegen The tone generator instance. * @param m On output, it will be filled with the pointer to * the digitmap currently used by the tone generator. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_tonegen_get_digit_map(pjmedia_port *tonegen, const pjmedia_tone_digit_map **m); /** * Set digit map to be used by the tone generator. * * @param tonegen The tone generator instance. * @param m Digitmap to be used by the tone generator. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_tonegen_set_digit_map(pjmedia_port *tonegen, pjmedia_tone_digit_map *m); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_TONEGEN_PORT_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/transport.h ================================================ /* $Id: transport.h 4345 2013-02-13 07:43:32Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_TRANSPORT_H__ #define __PJMEDIA_TRANSPORT_H__ /** * @file transport.h Media Transport Interface * @brief Transport interface. */ #include #include #include /** * @defgroup PJMEDIA_TRANSPORT Media Transport * @brief Transports. * @{ * The media transport (#pjmedia_transport) is the object to send and * receive media packets over the network. The media transport interface * allows the library to be extended to support different types of * transports to send and receive packets. * * The media transport is declared as #pjmedia_transport "class", which * declares "interfaces" to use the class in #pjmedia_transport_op * structure. For the user of the media transport (normally the user of * media transport is media stream, see \ref PJMED_STRM), these transport * "methods" are wrapped with API such as #pjmedia_transport_attach(), * so it should not need to call the function pointer inside * #pjmedia_transport_op directly. * * The connection between \ref PJMED_STRM and media transport is shown in * the diagram below: \image html media-transport.PNG * \section PJMEDIA_TRANSPORT_H_USING Basic Media Transport Usage * * The media transport's life-cycle normally follows the following stages. * * \subsection PJMEDIA_TRANSPORT_H_CREATE Creating the Media Transport * * Application creates the media transport when it needs to establish * media session to remote peer. The media transport is created using * specific function to create that particular transport; for example, * for UDP media transport, it is created with #pjmedia_transport_udp_create() * or #pjmedia_transport_udp_create2() functions. Different media * transports will provide different API to create those transports. * * Alternatively, application may create pool of media transports when * it is first started up. Using this approach probably is better, since * application has to specify the RTP port when sending the initial * session establishment request (e.g. SIP INVITE request), thus if * application only creates the media transport later when media is to be * established (normally when 200/OK is received, or when 18x is received * for early media), there is a possibility that the particular RTP * port might have been occupied by other programs. Also it is more * efficient since sockets don't need to be closed and re-opened between * calls. * * * \subsection PJMEDIA_TRANSPORT_H_ATTACH Attaching and Using the Media Transport. * * Application specifies the media transport instance when creating * the media session (#pjmedia_session_create()). Alternatively, it * may create the media stream directly with #pjmedia_stream_create() * and specify the transport instance in the argument. (Note: media * session is a high-level abstraction for media communications between * two endpoints, and it may contain more than one media streams, for * example, an audio stream and a video stream). * * When stream is created, it will "attach" itself to the media * transport by calling #pjmedia_transport_attach(), which is a thin * wrapper which calls "attach()" method of the media transport's * "virtual function pointer" (#pjmedia_transport_op). Among other things, * the stream specifies two callback functions to the transport: one * callback function will be called by transport when it receives RTP * packet, and another callback for incoming RTCP packet. The * #pjmedia_transport_attach() function also establish the destination * of the outgoing RTP and RTCP packets. * * When the stream needs to send outgoing RTP/RTCP packets, it will * call #pjmedia_transport_send_rtp() and #pjmedia_transport_send_rtcp() * of the media transport API, which is a thin wrapper to call send_rtp() * and send_rtcp() methods in the media transport's "virtual function * pointer" (#pjmedia_transport_op). * * When the stream is destroyed, it will "detach" itself from * the media transport by calling #pjmedia_transport_detach(), which is * a thin wrapper which calls "detach()" method of the media transport's * "virtual function pointer" (#pjmedia_transport_op). After the transport * is detached from its user (the stream), it will no longer report * incoming RTP/RTCP packets to the stream, and it will refuse to send * outgoing packets since the destination has been cleared. * * * \subsection PJMEDIA_TRANSPORT_H_REUSE Reusing the Media Transport. * * After transport has been detached, application may re-attach the * transport to another stream if it wants to. Detaching and re-attaching * media transport may be preferable than closing and re-opening the * transport, since it is more efficient (sockets don't need to be * closed and re-opened). However it is up to the application to choose * which method is most suitable for its uses. * * * \subsection PJMEDIA_TRANSPORT_H_DESTROY Destroying the Media Transport. * * Finally if application no longer needs the media transport, it will * call #pjmedia_transport_close() function, which is thin wrapper which * calls "destroy()" method of the media transport's "virtual function * pointer" (#pjmedia_transport_op). This function releases * all resources used by the transport, such as sockets and memory. * * * \section offer_answer Interaction with SDP Offer/Answer For basic UDP transport, the \ref PJMEDIA_TRANSPORT_H_USING above is sufficient to use the media transport. However, more complex media transports such as \ref PJMEDIA_TRANSPORT_SRTP and \ref PJMEDIA_TRANSPORT_ICE requires closer interactions with SDP offer and answer negotiation. The media transports can interact with the SDP offer/answer via these APIs: - #pjmedia_transport_media_create(), to initialize the media transport for new media session, - #pjmedia_transport_encode_sdp(), to encode SDP offer or answer, - #pjmedia_transport_media_start(), to activate the settings that have been negotiated by SDP offer answer, and - #pjmedia_transport_media_stop(), to deinitialize the media transport and reset the transport to its idle state. The usage of these API in the context of SDP offer answer will be described below. \subsection media_create Initializing Transport for New Session Application must call #pjmedia_transport_media_create() before using the transport for a new session. \subsection creat_oa Creating SDP Offer and Answer The #pjmedia_transport_encode_sdp() is used to put additional information from the transport to the local SDP, before the SDP is sent and negotiated with remote SDP. When creating an offer, call #pjmedia_transport_encode_sdp() with local SDP (and NULL as \a rem_sdp). The media transport will add the relevant attributes in the local SDP. Application then gives the local SDP to the invite session to be sent to remote agent. When creating an answer, also call #pjmedia_transport_encode_sdp(), but this time specify both local and remote SDP to the function. The media transport will once again modify the local SDP and add relevant attributes to the local SDP, if the appropriate attributes related to the transport functionality are present in remote offer. The remote SDP does not contain the relevant attributes, then the specific transport functionality will not be activated for the session. The #pjmedia_transport_encode_sdp() should also be called when application sends subsequent SDP offer or answer. The media transport will encode the appropriate attributes based on the state of the session. \subsection media_start Offer/Answer Completion Once both local and remote SDP have been negotiated by the \ref PJMEDIA_SDP_NEG (normally this is part of PJSIP invite session), application should give both local and remote SDP to #pjmedia_transport_media_start() so that the settings are activated for the session. This function should be called for both initial and subsequent SDP negotiation. \subsection media_stop Stopping Transport Once session is stop application must call #pjmedia_transport_media_stop() to deactivate the transport feature. Application may reuse the transport for subsequent media session by repeating the #pjmedia_transport_media_create(), #pjmedia_transport_encode_sdp(), #pjmedia_transport_media_start(), and #pjmedia_transport_media_stop() above. * \section PJMEDIA_TRANSPORT_H_IMPL Implementing Media Transport * * To implement a new type of media transport, one needs to "subclass" the * media transport "class" (#pjmedia_transport) by providing the "methods" * in the media transport "interface" (#pjmedia_transport_op), and provides * a function to create this new type of transport (similar to * #pjmedia_transport_udp_create() function). * * The media transport is expected to run indepently, that is there should * be no polling like function to poll the transport for incoming RTP/RTCP * packets. This normally can be done by registering the media sockets to * the media endpoint's IOQueue, which allows the transport to be notified * when incoming packet has arrived. * * Alternatively, media transport may utilize thread(s) internally to wait * for incoming packets. The thread then will call the appropriate RTP or * RTCP callback provided by its user (stream) whenever packet is received. * If the transport's user is a stream, then the callbacks provided by the * stream will be thread-safe, so the transport may call these callbacks * without having to serialize the access with some mutex protection. But * the media transport may still have to protect its internal data with * mutex protection, since it may be called by application's thread (for * example, to send RTP/RTCP packets). * */ #include PJ_BEGIN_DECL /** * Forward declaration for media transport. */ typedef struct pjmedia_transport pjmedia_transport; /** * Forward declaration for media transport info. */ typedef struct pjmedia_transport_info pjmedia_transport_info; /** * This enumeration specifies the general behaviour of media processing */ typedef enum pjmedia_tranport_media_option { /** * When this flag is specified, the transport will not perform media * transport validation, this is useful when transport is stacked with * other transport, for example when transport UDP is stacked under * transport SRTP, media transport validation only need to be done by * transport SRTP. */ PJMEDIA_TPMED_NO_TRANSPORT_CHECKING = 1 } pjmedia_tranport_media_option; /** * Media socket info is used to describe the underlying sockets * to be used as media transport. */ typedef struct pjmedia_sock_info { /** The RTP socket handle */ pj_sock_t rtp_sock; /** Address to be advertised as the local address for the RTP * socket, which does not need to be equal as the bound * address (for example, this address can be the address resolved * with STUN). */ pj_sockaddr rtp_addr_name; /** The RTCP socket handle. */ pj_sock_t rtcp_sock; /** Address to be advertised as the local address for the RTCP * socket, which does not need to be equal as the bound * address (for example, this address can be the address resolved * with STUN). */ pj_sockaddr rtcp_addr_name; } pjmedia_sock_info; /** * This structure describes the operations for the stream transport. */ struct pjmedia_transport_op { /** * Get media socket info from the specified transport. * * Application should call #pjmedia_transport_get_info() instead */ pj_status_t (*get_info)(pjmedia_transport *tp, pjmedia_transport_info *info); /** * This function is called by the stream when the transport is about * to be used by the stream for the first time, and it tells the transport * about remote RTP address to send the packet and some callbacks to be * called for incoming packets. * * Application should call #pjmedia_transport_attach() instead of * calling this function directly. */ pj_status_t (*attach)(pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb)(void *user_data, void *pkt, pj_ssize_t size), void (*rtcp_cb)(void *user_data, void *pkt, pj_ssize_t size)); /** * This function is called by the stream when the stream no longer * needs the transport (normally when the stream is about to be closed). * After the transport is detached, it will ignore incoming * RTP/RTCP packets, and will refuse to send outgoing RTP/RTCP packets. * Application may re-attach the media transport to another transport * user (e.g. stream) after the transport has been detached. * * Application should call #pjmedia_transport_detach() instead of * calling this function directly. */ void (*detach)(pjmedia_transport *tp, void *user_data); /** * This function is called by the stream to send RTP packet using the * transport. * * Application should call #pjmedia_transport_send_rtp() instead of * calling this function directly. */ pj_status_t (*send_rtp)(pjmedia_transport *tp, const void *pkt, pj_size_t size); /** * This function is called by the stream to send RTCP packet using the * transport. * * Application should call #pjmedia_transport_send_rtcp() instead of * calling this function directly. */ pj_status_t (*send_rtcp)(pjmedia_transport *tp, const void *pkt, pj_size_t size); /** * This function is called by the stream to send RTCP packet using the * transport with destination address other than default specified in * #pjmedia_transport_attach(). * * Application should call #pjmedia_transport_send_rtcp2() instead of * calling this function directly. */ pj_status_t (*send_rtcp2)(pjmedia_transport *tp, const pj_sockaddr_t *addr, unsigned addr_len, const void *pkt, pj_size_t size); /** * Prepare the transport for a new media session. * * Application should call #pjmedia_transport_media_create() instead of * calling this function directly. */ pj_status_t (*media_create)(pjmedia_transport *tp, pj_pool_t *sdp_pool, unsigned options, const pjmedia_sdp_session *remote_sdp, unsigned media_index); /** * This function is called by application to generate the SDP parts * related to transport type, e.g: ICE, SRTP. * * Application should call #pjmedia_transport_encode_sdp() instead of * calling this function directly. */ pj_status_t (*encode_sdp)(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *rem_sdp, unsigned media_index); /** * This function is called by application to start the transport * based on local and remote SDP. * * Application should call #pjmedia_transport_media_start() instead of * calling this function directly. */ pj_status_t (*media_start) (pjmedia_transport *tp, pj_pool_t *tmp_pool, const pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *sdp_remote, unsigned media_index); /** * This function is called by application to stop the transport. * * Application should call #pjmedia_transport_media_stop() instead of * calling this function directly. */ pj_status_t (*media_stop) (pjmedia_transport *tp); /** * This function can be called to simulate packet lost. * * Application should call #pjmedia_transport_simulate_lost() instead of * calling this function directly. */ pj_status_t (*simulate_lost)(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost); /** * This function can be called to destroy this transport. * * Application should call #pjmedia_transport_close() instead of * calling this function directly. */ pj_status_t (*destroy)(pjmedia_transport *tp); }; /** * @see pjmedia_transport_op. */ typedef struct pjmedia_transport_op pjmedia_transport_op; /** * Media transport type. */ typedef enum pjmedia_transport_type { /** Media transport using standard UDP */ PJMEDIA_TRANSPORT_TYPE_UDP, /** Media transport using ICE */ PJMEDIA_TRANSPORT_TYPE_ICE, /** * Media transport SRTP, this transport is actually security adapter to be * stacked with other transport to enable encryption on the underlying * transport. */ PJMEDIA_TRANSPORT_TYPE_SRTP, /** * Start of user defined transport. */ PJMEDIA_TRANSPORT_TYPE_USER } pjmedia_transport_type; /** * This structure declares media transport. A media transport is called * by the stream to transmit a packet, and will notify stream when * incoming packet is arrived. */ struct pjmedia_transport { /** Transport name (for logging purpose). */ char name[PJ_MAX_OBJ_NAME]; /** Transport type. */ pjmedia_transport_type type; /** Transport's "virtual" function table. */ pjmedia_transport_op *op; /** Application/user data */ void *user_data; }; /** * This structure describes storage buffer of transport specific info. * The actual transport specific info contents will be defined by transport * implementation. Note that some transport implementations do not need to * provide specific info, since the general socket info is enough. */ typedef struct pjmedia_transport_specific_info { /** * Specify media transport type. */ pjmedia_transport_type type; /** * Specify storage buffer size of transport specific info. */ int cbsize; /** * Storage buffer of transport specific info. */ char buffer[PJMEDIA_TRANSPORT_SPECIFIC_INFO_MAXSIZE]; } pjmedia_transport_specific_info; /** * This structure describes transport informations, including general * socket information and specific information of single transport or * stacked transports (e.g: SRTP stacked on top of UDP) */ struct pjmedia_transport_info { /** * General socket info. */ pjmedia_sock_info sock_info; /** * Remote address where RTP/RTCP originated from. In case this transport * hasn't ever received packet, the */ pj_sockaddr src_rtp_name; pj_sockaddr src_rtcp_name; /** * Specifies number of transport specific info included. */ unsigned specific_info_cnt; /** * Buffer storage of transport specific info. */ pjmedia_transport_specific_info spc_info[PJMEDIA_TRANSPORT_SPECIFIC_INFO_MAXCNT]; }; /** * Initialize transport info. * * @param info Transport info to be initialized. */ PJ_INLINE(void) pjmedia_transport_info_init(pjmedia_transport_info *info) { pj_bzero(info, sizeof(pjmedia_transport_info)); info->sock_info.rtp_sock = info->sock_info.rtcp_sock = PJ_INVALID_SOCKET; } /** * Get media transport info from the specified transport and all underlying * transports if any. The transport also contains information about socket info * which describes the local address of the transport, and would be needed * for example to fill in the "c=" and "m=" line of local SDP. * * @param tp The transport. * @param info Media transport info to be initialized. * * @return PJ_SUCCESS on success. */ PJ_INLINE(pj_status_t) pjmedia_transport_get_info(pjmedia_transport *tp, pjmedia_transport_info *info) { if (tp && tp->op && tp->op->get_info) return (*tp->op->get_info)(tp, info); return PJ_ENOTSUP; } /** * Utility API to get transport type specific info from the specified media * transport info. * * @param info Media transport info. * @param type Media transport type. * * @return Pointer to media transport specific info, or NULL if * specific info for the transport type is not found. */ PJ_INLINE(void*) pjmedia_transport_info_get_spc_info( pjmedia_transport_info *info, pjmedia_transport_type type) { unsigned i; for (i = 0; i < info->specific_info_cnt; ++i) { if (info->spc_info[i].type == type) return (void*)info->spc_info[i].buffer; } return NULL; } /** * Attach callbacks to be called on receipt of incoming RTP/RTCP packets. * This is just a simple wrapper which calls attach() member of * the transport. * * @param tp The media transport. * @param user_data Arbitrary user data to be set when the callbacks are * called. * @param rem_addr Remote RTP address to send RTP packet to. * @param rem_rtcp Optional remote RTCP address. If the argument is NULL * or if the address is zero, the RTCP address will be * calculated from the RTP address (which is RTP port * plus one). * @param addr_len Length of the remote address. * @param rtp_cb Callback to be called when RTP packet is received on * the transport. * @param rtcp_cb Callback to be called when RTCP packet is received on * the transport. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_INLINE(pj_status_t) pjmedia_transport_attach(pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb)(void *user_data, void *pkt, pj_ssize_t), void (*rtcp_cb)(void *usr_data, void*pkt, pj_ssize_t)) { return tp->op->attach(tp, user_data, rem_addr, rem_rtcp, addr_len, rtp_cb, rtcp_cb); } /** * Detach callbacks from the transport. * This is just a simple wrapper which calls detach() member of * the transport. After the transport is detached, it will ignore incoming * RTP/RTCP packets, and will refuse to send outgoing RTP/RTCP packets. * Application may re-attach the media transport to another transport user * (e.g. stream) after the transport has been detached. * * @param tp The media transport. * @param user_data User data which must match the previously set value * on attachment. */ PJ_INLINE(void) pjmedia_transport_detach(pjmedia_transport *tp, void *user_data) { tp->op->detach(tp, user_data); } /** * Send RTP packet with the specified media transport. This is just a simple * wrapper which calls send_rtp() member of the transport. The * RTP packet will be delivered to the destination address specified in * #pjmedia_transport_attach() function. * * @param tp The media transport. * @param pkt The packet to send. * @param size Size of the packet. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_INLINE(pj_status_t) pjmedia_transport_send_rtp(pjmedia_transport *tp, const void *pkt, pj_size_t size) { return (*tp->op->send_rtp)(tp, pkt, size); } /** * Send RTCP packet with the specified media transport. This is just a simple * wrapper which calls send_rtcp() member of the transport. The * RTCP packet will be delivered to the destination address specified in * #pjmedia_transport_attach() function. * * @param tp The media transport. * @param pkt The packet to send. * @param size Size of the packet. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_INLINE(pj_status_t) pjmedia_transport_send_rtcp(pjmedia_transport *tp, const void *pkt, pj_size_t size) { return (*tp->op->send_rtcp)(tp, pkt, size); } /** * Send RTCP packet with the specified media transport. This is just a simple * wrapper which calls send_rtcp2() member of the transport. The * RTCP packet will be delivered to the destination address specified in * param addr, if addr is NULL, RTCP packet will be delivered to destination * address specified in #pjmedia_transport_attach() function. * * @param tp The media transport. * @param addr The destination address. * @param addr_len Length of destination address. * @param pkt The packet to send. * @param size Size of the packet. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_INLINE(pj_status_t) pjmedia_transport_send_rtcp2(pjmedia_transport *tp, const pj_sockaddr_t *addr, unsigned addr_len, const void *pkt, pj_size_t size) { return (*tp->op->send_rtcp2)(tp, addr, addr_len, pkt, size); } /** * Prepare the media transport for a new media session, Application must * call this function before starting a new media session using this * transport. * * This is just a simple wrapper which calls media_create() member * of the transport. * * @param tp The media transport. * @param sdp_pool Pool object to allocate memory related to SDP * messaging components. * @param options Option flags, from #pjmedia_tranport_media_option * @param rem_sdp Remote SDP if local SDP is an answer, otherwise * specify NULL if SDP is an offer. * @param media_index Media index in SDP. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_INLINE(pj_status_t) pjmedia_transport_media_create(pjmedia_transport *tp, pj_pool_t *sdp_pool, unsigned options, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { return (*tp->op->media_create)(tp, sdp_pool, options, rem_sdp, media_index); } /** * Put transport specific information into the SDP. This function can be * called to put transport specific information in the initial or * subsequent SDP offer or answer. * * This is just a simple wrapper which calls encode_sdp() member * of the transport. * * @param tp The media transport. * @param sdp_pool Pool object to allocate memory related to SDP * messaging components. * @param sdp The local SDP to be filled in information from the * media transport. * @param rem_sdp Remote SDP if local SDP is an answer, otherwise * specify NULL if SDP is an offer. * @param media_index Media index in SDP. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_INLINE(pj_status_t) pjmedia_transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *sdp, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { return (*tp->op->encode_sdp)(tp, sdp_pool, sdp, rem_sdp, media_index); } /** * Start the transport session with the settings in both local and remote * SDP. The actual work that is done by this function depends on the * underlying transport type. For SRTP, this will activate the encryption * and decryption based on the keys found the SDPs. For ICE, this will * start ICE negotiation according to the information found in the SDPs. * * This is just a simple wrapper which calls media_start() member * of the transport. * * @param tp The media transport. * @param tmp_pool The memory pool for allocating temporary objects. * @param sdp_local Local SDP. * @param sdp_remote Remote SDP. * @param media_index Media index in the SDP. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_INLINE(pj_status_t) pjmedia_transport_media_start(pjmedia_transport *tp, pj_pool_t *tmp_pool, const pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *sdp_remote, unsigned media_index) { return (*tp->op->media_start)(tp, tmp_pool, sdp_local, sdp_remote, media_index); } /** * This API should be called when the session is stopped, to allow the media * transport to release its resources used for the session. * * This is just a simple wrapper which calls media_stop() member * of the transport. * * @param tp The media transport. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_INLINE(pj_status_t) pjmedia_transport_media_stop(pjmedia_transport *tp) { return (*tp->op->media_stop)(tp); } /** * Close media transport. This is just a simple wrapper which calls * destroy() member of the transport. This function will free * all resources created by this transport (such as sockets, memory, etc.). * * @param tp The media transport. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_INLINE(pj_status_t) pjmedia_transport_close(pjmedia_transport *tp) { if (tp->op->destroy) return (*tp->op->destroy)(tp); else return PJ_SUCCESS; } /** * Simulate packet lost in the specified direction (for testing purposes). * When enabled, the transport will randomly drop packets to the specified * direction. * * @param tp The media transport. * @param dir Media direction to which packets will be randomly dropped. * @param pct_lost Percent lost (0-100). Set to zero to disable packet * lost simulation. * * @return PJ_SUCCESS on success. */ PJ_INLINE(pj_status_t) pjmedia_transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost) { return (*tp->op->simulate_lost)(tp, dir, pct_lost); } PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_TRANSPORT_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/transport_adapter_sample.h ================================================ /* $Id: transport_adapter_sample.h 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_TRANSPORT_ADAPTER_SAMPLE_H__ #define __PJMEDIA_TRANSPORT_ADAPTER_SAMPLE_H__ /** * @file transport_adapter_sample.h * @brief Sample Media Transport Adapter */ #include /** * @defgroup PJMEDIA_TRANSPORT_ADAPTER_SAMPLE Sample Transport Adapter * @ingroup PJMEDIA_TRANSPORT * @brief Example on how to create transport adapter. * @{ * * This describes a sample implementation of transport adapter, similar to * the way the SRTP transport adapter works. */ PJ_BEGIN_DECL /** * Create the transport adapter, specifying the underlying transport to be * used to send and receive RTP/RTCP packets. * * @param endpt The media endpoint. * @param name Optional name to identify this media transport * for logging purposes. * @param base_tp The base/underlying media transport to send and * receive RTP/RTCP packets. * @param del_base Specify whether the base transport should also be * destroyed when destroy() is called upon us. * @param p_tp Pointer to receive the media transport instance. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_tp_adapter_create( pjmedia_endpt *endpt, const char *name, pjmedia_transport *base_tp, pj_bool_t del_base, pjmedia_transport **p_tp); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_TRANSPORT_ADAPTER_SAMPLE_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/transport_ice.h ================================================ /* $Id: transport_ice.h 4350 2013-02-15 03:57:31Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_TRANSPORT_ICE_H__ #define __PJMEDIA_TRANSPORT_ICE_H__ /** * @file transport_ice.h * @brief ICE capable media transport. */ #include #include /** * @defgroup PJMEDIA_TRANSPORT_ICE ICE Media Transport * @ingroup PJMEDIA_TRANSPORT * @brief Interactive Connectivity Establishment (ICE) transport * @{ * * This describes the implementation of media transport using * Interactive Connectivity Establishment (ICE) protocol. */ PJ_BEGIN_DECL /** * Structure containing callbacks to receive ICE notifications. */ typedef struct pjmedia_ice_cb { /** * This callback will be called when ICE negotiation completes. * * @param tp PJMEDIA ICE transport. * @param op The operation * @param status Operation status. */ void (*on_ice_complete)(pjmedia_transport *tp, pj_ice_strans_op op, pj_status_t status); /** * This callback will be called when ICE state changes. * * @param tp PJMEDIA ICE transport. * @param prev Previous state. * @param curr Current state. */ void (*on_ice_state)(pjmedia_transport *tp, pj_ice_strans_state prev, pj_ice_strans_state curr); /** * This callback will be called when ICE is stopped. * * @param tp PJMEDIA ICE transport. * @param reason Reason for stopping ICE. * @param err Error code */ void (*on_ice_stop)(pjmedia_transport *tp, char *reason, pj_status_t err); } pjmedia_ice_cb; /** * This structure specifies ICE transport specific info. This structure * will be filled in media transport specific info. */ typedef struct pjmedia_ice_transport_info { /** * Specifies whether ICE is used, i.e. SDP offer and answer indicates * that both parties support ICE and ICE should be used for the session. */ pj_bool_t active; /** * ICE sesion state. */ pj_ice_strans_state sess_state; /** * Session role. */ pj_ice_sess_role role; /** * Number of components in the component array. Before ICE negotiation * is complete, the number represents the number of components of the * local agent. After ICE negotiation has been completed successfully, * the number represents the number of common components between local * and remote agents. */ unsigned comp_cnt; /** * Array of ICE components. Typically the first element denotes RTP and * second element denotes RTCP. */ struct { /** * Local candidate type. */ pj_ice_cand_type lcand_type; /** * The local address. */ pj_sockaddr lcand_addr; /** * Remote candidate type. */ pj_ice_cand_type rcand_type; /** * Remote address. */ pj_sockaddr rcand_addr; } comp[2]; } pjmedia_ice_transport_info; /** * Options that can be specified when creating ICE transport. */ enum pjmedia_transport_ice_options { /** * Normally when remote doesn't use ICE, the ICE transport will * continuously check the source address of incoming packets to see * if it is different than the configured remote address, and switch * the remote address to the source address of the packet if they * are different after several packets are received. * Specifying this option will disable this feature. */ PJMEDIA_ICE_NO_SRC_ADDR_CHECKING = 1 }; /** * Create the Interactive Connectivity Establishment (ICE) media transport * using the specified configuration. When STUN or TURN (or both) is used, * the creation operation will complete asynchronously, when STUN resolution * and TURN allocation completes. When the initialization completes, the * \a on_ice_complete() complete will be called with \a op parameter equal * to PJ_ICE_STRANS_OP_INIT. * * In addition, this transport will also notify the application about the * result of ICE negotiation, also in \a on_ice_complete() callback. In this * case the callback will be called with \a op parameter equal to * PJ_ICE_STRANS_OP_NEGOTIATION. * * Other than this, application should use the \ref PJMEDIA_TRANSPORT API * to manipulate this media transport. * * @param endpt The media endpoint. * @param name Optional name to identify this ICE media transport * for logging purposes. * @param comp_cnt Number of components to be created. * @param cfg Pointer to configuration settings. * @param cb Optional structure containing ICE specific callbacks. * @param p_tp Pointer to receive the media transport instance. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt, const char *name, unsigned comp_cnt, const pj_ice_strans_cfg *cfg, const pjmedia_ice_cb *cb, pjmedia_transport **p_tp); /** * The same as #pjmedia_ice_create() with additional \a options param. * * @param endpt The media endpoint. * @param name Optional name to identify this ICE media transport * for logging purposes. * @param comp_cnt Number of components to be created. * @param cfg Pointer to configuration settings. * @param cb Optional structure containing ICE specific callbacks. * @param options Options, see #pjmedia_transport_ice_options. * @param p_tp Pointer to receive the media transport instance. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_ice_create2(pjmedia_endpt *endpt, const char *name, unsigned comp_cnt, const pj_ice_strans_cfg *cfg, const pjmedia_ice_cb *cb, unsigned options, pjmedia_transport **p_tp); /** * The same as #pjmedia_ice_create2() with additional \a user_data param. * * @param endpt The media endpoint. * @param name Optional name to identify this ICE media transport * for logging purposes. * @param comp_cnt Number of components to be created. * @param cfg Pointer to configuration settings. * @param cb Optional structure containing ICE specific callbacks. * @param options Options, see #pjmedia_transport_ice_options. * @param user_data User data to be attached to the transport. * @param p_tp Pointer to receive the media transport instance. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_ice_create3(pjmedia_endpt *endpt, const char *name, unsigned comp_cnt, const pj_ice_strans_cfg *cfg, const pjmedia_ice_cb *cb, unsigned options, void *user_data, pjmedia_transport **p_tp); /** * Return the ICE stream transport associated with this PJMEDIA transport * * @param tp Media transport instance. * * @return Pointer to the pj_ice_strans instance associated with this * media transport. */ PJ_DECL(pj_ice_strans*) pjmedia_ice_get_strans(pjmedia_transport *tp); /** * Get the group lock for the ICE media transport. * * @param tp The ICE media transport. * * @return The group lock. */ PJ_DECL(pj_grp_lock_t *) pjmedia_ice_get_grp_lock(pjmedia_transport *tp); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_TRANSPORT_ICE_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/transport_loop.h ================================================ /* $Id: transport_loop.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_TRANSPORT_LOOP_H__ #define __PJMEDIA_TRANSPORT_LOOP_H__ /** * @file transport_loop.h * @brief Loopback transport */ #include /** * @defgroup PJMEDIA_TRANSPORT_LOOP Loopback Media Transport * @ingroup PJMEDIA_TRANSPORT * @brief Loopback transport for testing. * @{ * * This is the loopback media transport, where packets sent to this transport * will be sent back to the streams attached to this transport. Unlike the * other PJMEDIA transports, the loop transport may be attached to multiple * streams (in other words, application should specify the same loop transport * instance when calling #pjmedia_stream_create()). Any RTP or RTCP packets * sent by one stream to this transport by default will be sent back to all * streams that are attached to this transport, including to the stream that * sends the packet. Application may individually select which stream to * receive packets by calling #pjmedia_transport_loop_disable_rx(). */ PJ_BEGIN_DECL /** * Create the loopback transport. * * @param endpt The media endpoint instance. * @param p_tp Pointer to receive the transport instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_transport_loop_create(pjmedia_endpt *endpt, pjmedia_transport **p_tp); /** * Set this stream as the receiver of incoming packets. */ PJ_DECL(pj_status_t) pjmedia_transport_loop_disable_rx(pjmedia_transport *tp, void *user, pj_bool_t disabled); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_TRANSPORT_LOOP_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/transport_srtp.h ================================================ /* $Id: transport_srtp.h 3999 2012-03-30 07:10:13Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_TRANSPORT_SRTP_H__ #define __PJMEDIA_TRANSPORT_SRTP_H__ /** * @file transport_srtp.h * @brief Secure RTP (SRTP) transport. */ #include /** * @defgroup PJMEDIA_TRANSPORT_SRTP Secure RTP (SRTP) Media Transport * @ingroup PJMEDIA_TRANSPORT * @brief Media transport adapter to add SRTP feature to existing transports * @{ * * This module implements SRTP as described by RFC 3711, using RFC 4568 as * key exchange method. It implements \ref PJMEDIA_TRANSPORT to integrate * with the rest of PJMEDIA framework. * * As we know, media transport is separated from the stream object (which * does the encoding/decoding of PCM frames, (de)packetization of RTP/RTCP * packets, and de-jitter buffering). The connection between stream and media * transport is established when the stream is created (we need to specify * media transport during stream creation), and the interconnection can be * depicted from the diagram below: * \image html media-transport.PNG * I think the diagram above is self-explanatory. * * SRTP functionality is implemented as some kind of "adapter", which is * plugged between the stream and the actual media transport that does * sending/receiving RTP/RTCP packets. When SRTP is used, the interconnection * between stream and transport is like the diagram below: * \image html media-srtp-transport.PNG * So to stream, the SRTP transport behaves as if it is a media transport * (because it is a media transport), and to the media transport it behaves * as if it is a stream. The SRTP object then forwards RTP packets back and * forth between stream and the actual transport, encrypting/decrypting * the RTP/RTCP packets as necessary. * * The neat thing about this design is the SRTP "adapter" then can be used * to encrypt any kind of media transports. We currently have UDP and ICE * media transports that can benefit SRTP, and we could add SRTP to any * media transports that will be added in the future. */ PJ_BEGIN_DECL /** * Crypto option. */ typedef enum pjmedia_srtp_crypto_option { /** When this flag is specified, encryption will be disabled. */ PJMEDIA_SRTP_NO_ENCRYPTION = 1, /** When this flag is specified, authentication will be disabled. */ PJMEDIA_SRTP_NO_AUTHENTICATION = 2 } pjmedia_srtp_crypto_option; /** * This structure describes an individual crypto setting. */ typedef struct pjmedia_srtp_crypto { /** Optional key. If empty, a random key will be autogenerated. */ pj_str_t key; /** Crypto name. */ pj_str_t name; /** Flags, bitmask from #pjmedia_srtp_crypto_option */ unsigned flags; } pjmedia_srtp_crypto; /** * This enumeration specifies the behavior of the SRTP transport regarding * media security offer and answer. */ typedef enum pjmedia_srtp_use { /** * When this flag is specified, SRTP will be disabled, and the transport * will reject RTP/SAVP offer. */ PJMEDIA_SRTP_DISABLED, /** * When this flag is specified, SRTP will be advertised as optional and * incoming SRTP offer will be accepted. */ PJMEDIA_SRTP_OPTIONAL, /** * When this flag is specified, the transport will require that RTP/SAVP * media shall be used. */ PJMEDIA_SRTP_MANDATORY } pjmedia_srtp_use; /** * Settings to be given when creating SRTP transport. Application should call * #pjmedia_srtp_setting_default() to initialize this structure with its * default values. */ typedef struct pjmedia_srtp_setting { /** * Specify the usage policy. Default is PJMEDIA_SRTP_OPTIONAL. */ pjmedia_srtp_use use; /** * Specify whether the SRTP transport should close the member transport * when it is destroyed. Default: PJ_TRUE. */ pj_bool_t close_member_tp; /** * Specify the number of crypto suite settings. */ unsigned crypto_count; /** * Specify individual crypto suite setting. */ pjmedia_srtp_crypto crypto[8]; } pjmedia_srtp_setting; /** * This structure specifies SRTP transport specific info. This will fit * into \a buffer field of pjmedia_transport_specific_info. */ typedef struct pjmedia_srtp_info { /** * Specify whether the SRTP transport is active for SRTP session. */ pj_bool_t active; /** * Specify the policy used by the SRTP session for receive direction. */ pjmedia_srtp_crypto rx_policy; /** * Specify the policy used by the SRTP session for transmit direction. */ pjmedia_srtp_crypto tx_policy; /** * Specify the usage policy. */ pjmedia_srtp_use use; /** * Specify the peer's usage policy. */ pjmedia_srtp_use peer_use; } pjmedia_srtp_info; /** * Initialize SRTP library. This function should be called before * any SRTP functions, however calling #pjmedia_transport_srtp_create() * will also invoke this function. This function will also register SRTP * library deinitialization to #pj_atexit(), so the deinitialization * of SRTP library will be performed automatically by PJLIB destructor. * * @param endpt The media endpoint instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_srtp_init_lib(pjmedia_endpt *endpt); /** * Initialize SRTP setting with its default values. * * @param opt SRTP setting to be initialized. */ PJ_DECL(void) pjmedia_srtp_setting_default(pjmedia_srtp_setting *opt); /** * Create an SRTP media transport. * * @param endpt The media endpoint instance. * @param tp The actual media transport to send and receive * RTP/RTCP packets. This media transport will be * kept as member transport of this SRTP instance. * @param opt Optional settings. If NULL is given, default * settings will be used. * @param p_tp Pointer to receive the transport SRTP instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_transport_srtp_create( pjmedia_endpt *endpt, pjmedia_transport *tp, const pjmedia_srtp_setting *opt, pjmedia_transport **p_tp); /** * Manually start SRTP session with the given parameters. Application only * needs to call this function when the SRTP transport is used without SDP * offer/answer. When SDP offer/answer framework is used, the SRTP transport * will be started/stopped by #pjmedia_transport_media_start() and * #pjmedia_transport_media_stop() respectively. * * Please note that even if an RTP stream is only one direction, application * will still need to provide both crypto suites, because it is needed by * RTCP. * If application specifies the crypto keys, the keys for transmit and receive * direction MUST be different. * * @param srtp The SRTP transport. * @param tx Crypto suite setting for transmit direction. * @param rx Crypto suite setting for receive direction. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_transport_srtp_start( pjmedia_transport *srtp, const pjmedia_srtp_crypto *tx, const pjmedia_srtp_crypto *rx); /** * Stop SRTP session. * * @param srtp The SRTP media transport. * * @return PJ_SUCCESS on success. * * @see #pjmedia_transport_srtp_start() */ PJ_DECL(pj_status_t) pjmedia_transport_srtp_stop(pjmedia_transport *srtp); /** * This is a utility function to decrypt SRTP packet using SRTP transport. * This function is not part of SRTP transport's API, but it can be used * to decrypt SRTP packets from non-network (for example, from a saved file) * without having to use the transport framework. See pcaputil.c in the * samples collection on how to use this function. * * @param tp The SRTP transport. * @param is_rtp Set to non-zero if the packet is SRTP, otherwise set * to zero if the packet is SRTCP. * @param pkt On input, it contains SRTP or SRTCP packet. On * output, it contains the decrypted RTP/RTCP packet. * @param pkt_len On input, specify the length of the buffer. On * output, it will be filled with the actual length * of decrypted packet. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_transport_srtp_decrypt_pkt(pjmedia_transport *tp, pj_bool_t is_rtp, void *pkt, int *pkt_len); /** * Query member transport of SRTP. * * @param srtp The SRTP media transport. * * @return member media transport. */ PJ_DECL(pjmedia_transport*) pjmedia_transport_srtp_get_member( pjmedia_transport *srtp); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_TRANSPORT_SRTP_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/transport_udp.h ================================================ /* $Id: transport_udp.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_TRANSPORT_UDP_H__ #define __PJMEDIA_TRANSPORT_UDP_H__ /** * @file transport_udp.h * @brief Stream transport with UDP. */ #include /** * @defgroup PJMEDIA_TRANSPORT_UDP UDP Media Transport * @ingroup PJMEDIA_TRANSPORT * @brief Implementation of media transport with UDP sockets. * @{ * * The UDP media transport is the standard based media transport * as described by RFC 3550/3551. It can be used to facilitate RTP/RTCP * unicast or multicast communication. */ PJ_BEGIN_DECL /** * Options that can be specified when creating UDP transport. */ enum pjmedia_transport_udp_options { /** * Normally the UDP transport will continuously check the source address * of incoming packets to see if it is different than the configured * remote address, and switch the remote address to the source address * of the packet if they are different after several packets are * received. * Specifying this option will disable this feature. */ PJMEDIA_UDP_NO_SRC_ADDR_CHECKING = 1 }; /** * Create an RTP and RTCP sockets and bind the sockets to the specified * port to create media transport. * * @param endpt The media endpoint instance. * @param name Optional name to be assigned to the transport. * @param port UDP port number for the RTP socket. The RTCP port number * will be set to one above RTP port. * @param options Options, bitmask of #pjmedia_transport_udp_options. * @param p_tp Pointer to receive the transport instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_transport_udp_create(pjmedia_endpt *endpt, const char *name, int port, unsigned options, pjmedia_transport **p_tp); /** * Create an RTP and RTCP sockets and bind the sockets to the specified * address and port to create media transport. * * @param endpt The media endpoint instance. * @param name Optional name to be assigned to the transport. * @param addr Optional local address to bind the sockets to. If this * argument is NULL or empty, the sockets will be bound * to all interface. * @param port UDP port number for the RTP socket. The RTCP port number * will be set to one above RTP port. * @param options Options, bitmask of #pjmedia_transport_udp_options. * @param p_tp Pointer to receive the transport instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_transport_udp_create2(pjmedia_endpt *endpt, const char *name, const pj_str_t *addr, int port, unsigned options, pjmedia_transport **p_tp); /** * Another variant of #pjmedia_transport_udp_create() which allows * the creation of IPv6 transport. * * @param endpt The media endpoint instance. * @param af Address family, which can be pj_AF_INET() for IPv4 or * pj_AF_INET6() for IPv6. * @param name Optional name to be assigned to the transport. * @param addr Optional local address to bind the sockets to. If this * argument is NULL or empty, the sockets will be bound * to all interface. * @param port UDP port number for the RTP socket. The RTCP port number * will be set to one above RTP port. * @param options Options, bitmask of #pjmedia_transport_udp_options. * @param p_tp Pointer to receive the transport instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_transport_udp_create3(pjmedia_endpt *endpt, int af, const char *name, const pj_str_t *addr, int port, unsigned options, pjmedia_transport **p_tp); /** * Create UDP stream transport from existing sockets. Use this function when * the sockets have previously been created. * * @param endpt The media endpoint instance. * @param name Optional name to be assigned to the transport. * @param si Media socket info containing the RTP and RTCP sockets. * @param options Options, bitmask of #pjmedia_transport_udp_options. * @param p_tp Pointer to receive the transport instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_transport_udp_attach(pjmedia_endpt *endpt, const char *name, const pjmedia_sock_info *si, unsigned options, pjmedia_transport **p_tp); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_TRANSPORT_UDP_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/transport_zrtp.h ================================================ /* $Id$ */ /* Copyright (C) 2010 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef __PJMEDIA_TRANSPORT_ZRTP_H__ #define __PJMEDIA_TRANSPORT_ZRTP_H__ /** * @file transport_zrtp.h * @brief ZRTP Media Transport Adapter */ /* transport.h includes types.h -> config.h -> config_auto.h */ #include #include "../../third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpCWrapper.h" /** * @defgroup PJMEDIA_TRANSPORT_ZRTP ZRTP Transport Adapter * @brief This the ZRTP transport adapter. * @{ * * PJMEDIA extension to support GNU ZRTP. * * ZRTP was developed by Phil Zimmermann and provides functions to * negotiate keys and other necessary data (crypto data) to set-up * the Secure RTP (SRTP) crypto context. Refer to Phil's ZRTP * specification at his Zfone * project site to get more detailed information about the * capabilities of ZRTP. * * Short overview of the ZRTP implementation * * ZRTP is a specific protocol to negotiate encryption algorithms * and the required key material. ZRTP uses a RTP session to * exchange its protocol messages. Thus ZRTP is independent of any * signaling protocol like SIP, XMPP and alike. * * A complete GNU ZRTP implementation consists of two parts, the * GNU ZRTP core and some specific code that binds the GNU ZRTP core to * the underlying RTP/SRTP stack and the operating system: *
    *
  • * The GNU ZRTP core is independent of a specific RTP/SRTP * stack and the operationg system and consists of the ZRTP * protocol state engine, the ZRTP protocol messages, and the * GNU ZRTP engine. The GNU ZRTP engine provides methods to * setup ZRTP message and to analyze received ZRTP messages, * to compute the crypto data required for SRTP, and to * maintain the required hashes and HMAC. *
  • *
  • * The second part of an implementation is specific * glue code the binds the GNU ZRTP core to the * actual RTP/SRTP implementation and other operating system * specific services such as timers, mutexes. *
  • *
* * The GNU ZRTP core uses callback methods (refer to * zrtp_Callback) to access RTP/SRTP or operating specific methods, * for example to send data via the RTP stack, to access * timers, provide mutex handling, and to report events to the * application. * * The PJMEDIA ZRTP transport * * ZRTP transport implements code that is specific to the pjmedia * implementation. ZRTP transport also implements the specific code to * provide the mutex and timeout handling to the GNU ZRTP * core. Both, the mutex and the timeout handling, use the pjlib * library to stay independent of the operating * seystem. * * To perform its tasks ZRTP transport *
    *
  • implements the pjmedia transport functions and callbacks. *
  • *
  • implements the zrtp_Callbacks methods to provide * access and other specific services (timer, mutex) to GNU * ZRTP *
  • *
  • provides ZRTP specific methods that applications may use * to control and setup GNU ZRTP *
  • *
  • can register and use an application specific callback * class (refer to zrtp_UserCallbacks) *
  • *
* * After instantiating a GNU ZRTP session (see below for a short * example) applications may use the methods of * ZRTP transport and the ZRTP engine to control and setup GNU ZRTP, * for example enable or disable ZRTP processing or getting ZRTP status * information. * * GNU ZRTP defines zrtp_UserCallback methods structure that an application * may use and register with ZRTP transport. GNU ZRTP and ZRTP transport * use the zrtp_UserCallback methods to report ZRTP events to the * application. The application may display this information to * the user or act otherwise. * * The following figure depicts the relationships between * ZRTP transport, pjmedia RTP implementation, the GNU ZRTP core, * SRTP and an application that provides zrtp_UserCallback methods. * @verbatim +-----------+ | | | SRTP-ZRTP | | | +-----------+ |C Wrapper | +-----+-----+ | | uses | +-----------------+ +-------+--------+ +-+-----------------+ | App (pjsua) | | | |C| | | creates a | uses | transport_zrtp | uses | | GNU ZRTP | | ZRTP transport +------+ implements +------+W| core | | and implements | | zrtp_Callback | |r| implementation | |zrtp_UserCallback| | | |a| (ZRtp et al) | +-----------------+ +----------------+ |p| | +-+-----------------+ @endverbatim * * The following short code snippet shows how to use ZRTP transport * * @code * * #include * ... * // Create media transport * status = pjmedia_transport_udp_create(med_endpt, NULL, local_port, * 0, &transport); * if (status != PJ_SUCCESS) * return status; * * status = pjmedia_transport_zrtp_create(med_endpt, NULL, transport, * &zrtp_tp); * app_perror(THIS_FILE, "Error creating zrtp", status); * transport = zrtp_tp; * if (dir == PJMEDIA_DIR_ENCODING) * pjmedia_transport_zrtp_initialize(transport, "testenc.zid", 1, NULL); * else * pjmedia_transport_zrtp_initialize(transport, "testdec.zid", 1, NULL); * ... * @endcode * */ #define PJMEDIA_TRANSPORT_TYPE_ZRTP PJMEDIA_TRANSPORT_TYPE_USER+2 PJ_BEGIN_DECL /** * ZRTP option. */ typedef enum pjmedia_zrtp_use { /** When this flag is specified, ZRTP will be disabled. */ PJMEDIA_NO_ZRTP = 1, /** When this flag is specified, PJSUA-LIB creates a ZRTP transport * call calls back the applicaion for further process if callback is * set. */ PJMEDIA_CREATE_ZRTP = 2 } pjmedia_zrtp_use; /** * This structure specifies ZRTP transport specific info. This will fit * into \a buffer field of pjmedia_transport_specific_info. */ typedef struct pjmedia_zrtp_info { /** * Specify whether the ZRTP transport is active for this session. */ pj_bool_t active; /** * Specify the cipher being used. */ char cipher[128]; } pjmedia_zrtp_info; /** * Application callback methods. * * The RTP stack specific part of GNU ZRTP uses these callback methods * to report ZRTP events to the application. Thus the application that * instantiates the RTP stack shall implement these methods and show these * inforemation to the user. * * CAVEAT
* All user callback methods run in the context of the RTP thread. Thus * it is of paramount importance to keep the execution time of the methods * as short as possible. * * @author Werner Dittmann */ typedef struct pjmedia_zrtp_cb { /** * Inform user interface that security is active now. * * ZRTP calls this method if the sender and the receiver are * in secure mode now. * * @param cipher * Name and mode of cipher used to encrypt the SRTP stream */ void (*secure_on)(pjmedia_transport *tp, char* cipher); /** * Inform user interface that security is not active any more. * * ZRTP calls this method if either the sender or the receiver * left secure mode. * */ void (*secure_off)(pjmedia_transport *tp); /** * Show the Short Authentication String (SAS) on user interface. * * ZRTP calls this method to display the SAS and inform about the SAS * verification status. The user interface shall enable a SAS verfication * button (or similar UI element). The user shall click on this UI * element after he/she confirmed the SAS code with the partner. * * @param sas * The string containing the SAS. * @param verified * If verified is true then SAS was verified by both * parties during a previous call, otherwise it is set to false. */ void (*show_sas)(pjmedia_transport *tp, char* sas, int32_t verified); /** * Inform the user that ZRTP received "go clear" message from its peer. * * On receipt of a go clear message the user is requested to confirm * a switch to unsecure (clear) modus. Until the user confirms ZRTP * (and the underlying RTP) does not send any data. * */ void (*confirm_go_clear)(pjmedia_transport *tp); /** * Show some information to user. * * ZRTP calls this method to display some information to the user. * Along with the message ZRTP provides a severity indicator that * defines: Info, Warning, Error, and Alert. Refer to the * MessageSeverity enum in ZrtpCodes.h. The * UI may use this indicator to highlight messages or alike. * * @param sev * Severity of the message. * @param subCode * The subcode identifying the reason. */ void (*show_message)(pjmedia_transport *tp, int32_t sev, int32_t subCode); /** * ZRTP transport calls this if the negotiation failed. * * ZRTPQueue calls this method in case ZRTP negotiation failed. The * parameters show the severity as well as some explanatory text. * Refer to the MessageSeverity enum above. * * @param severity * This defines the message's severity * @param subCode * The subcode identifying the reason. */ void (*negotiation_failed)(pjmedia_transport *tp, int32_t severity, int32_t subCode); /** * ZRTP transport calls this method if the other side does not support ZRTP. * * If the other side does not answer the ZRTP Hello packets then * ZRTP calls this method. * */ void (*not_supported_by_other)(pjmedia_transport *tp); /** * ZRTP transport calls this method to inform about a PBX enrollment request. * * Please refer to chapter 8.3 ff to get more details about PBX enrollment * and SAS relay. * * @param info * Give some information to the user about the PBX requesting an * enrollment. */ void (*ask_enrollment)(pjmedia_transport *tp, int32_t info); /** * ZRTP transport calls this method to inform about PBX enrollment result. * * Informs the use about the acceptance or denial of an PBX enrollment * request * * @param info * Give some information to the user about the result of an * enrollment. */ void (*inform_enrollment)(pjmedia_transport *tp, int32_t info); /** * ZRTP transport calls this method to request a SAS signature. * * After ZRTP core was able to compute the Short Authentication String * (SAS) it calls this method. The client may now use an approriate * method to sign the SAS. The client may use * setSignatureData() of ZrtpQueue to store the signature * data an enable signature transmission to the other peer. Refer * to chapter 8.2 of ZRTP specification. * * @param sas * The SAS string to sign. * @see ZrtpQueue#setSignatureData * */ void (*sign_sas)(pjmedia_transport *tp, uint8_t* sas); /** * ZRTP transport calls this method to request a SAS signature check. * * After ZRTP received a SAS signature in one of the Confirm packets it * call this method. The client may use getSignatureLength() * and getSignatureData()of ZrtpQueue to get the signature * data and perform the signature check. Refer to chapter 8.2 of ZRTP * specification. * * If the signature check fails the client may return false to ZRTP. In * this case ZRTP signals an error to the other peer and terminates * the ZRTP handshake. * * @param sas * The SAS string that was signed by the other peer. * @return * true if the signature was ok, false otherwise. * */ int32_t (*check_sas_signature)(pjmedia_transport *tp, uint8_t* sas); } pjmedia_zrtp_cb; /** * Create the transport adapter, specifying the underlying transport to be * used to send and receive RTP/RTCP packets. * * @param endpt The media endpoint. * @param timer_heap The heap where timers will be scheduled. * @param transport The underlying media transport to send and receive * RTP/RTCP packets. * @param p_tp Pointer to receive the media transport instance. * * @param close_slave * Close the slave transport on transport_destroy. PJSUA-LIB * sets this to PJ_FALSE because it takes care of this. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_transport_zrtp_create( pjmedia_endpt *endpt, pj_timer_heap_t *timer_heap, pjmedia_transport *transport, pjmedia_transport **p_tp, pj_bool_t close_slave); /* * Implement the specific ZRTP transport functions */ /** * Initialize the ZRTP transport. * * Before an application can use ZRTP it has to initialize the * ZRTP implementation. This method opens a file that contains ZRTP specific * information such as the applications ZID (ZRTP id) and its * retained shared secrets. * * Before an application initializes the ZRTP it may use ZRTP functions * to set specific configuration data. See the relevant documentation * in @c ZrtpCWrapper.h . The application can peform this after * it created transport_zrtp. * * If one application requires several ZRTP sessions all * sessions use the same timeout thread and use the same ZID * file. Therefore an application does not need to do any * synchronisation regading ZID files or timeouts. This is * managed by the ZRTP implementation. * * The current implementation of ZRTP transport does not support * different ZID files for one application instance. This * restriction may be removed in later versions. * * The application may specify its own ZID file name. If no * ZID file name is specified it defaults to * $HOME/.GNUccRTP.zid if the HOME * environment variable is set. If it is not set the current * directory is used. * * If the method could set up the timeout thread and open the ZID * file then it enables ZRTP processing and returns. * * @param tp * Pointer to the ZRTP transport data as returned by * @c pjmedia_transport_zrtp_create. * * @param zidFilename * The name of the ZID file, can be a relative or absolut * filename. * * @param autoEnable * if set to true the method automatically sets enableZrtp to * true. This enables the ZRTP auto-sense mode. * * @param zrtp_cb * Pointer the application's ZRTP callbacks structure. Setting * a NULL switches off the user callbacks * @return * PJ_SUCCESS on success, ZRTP processing enabled, other codes * leave ZRTP processing disabled. * */ PJ_DECL(pj_status_t) pjmedia_transport_zrtp_initialize(pjmedia_transport *tp, const char *zidFilename, pj_bool_t autoEnable, pjmedia_zrtp_cb *zrtp_cb); /** * Enable or disable ZRTP processing. * * Call this method to enable or disable ZRTP processing after * calling pjmedia_transport_zrtp_initialize with the * parameter @c autoEnable set to false. This can be done before * using a RTP session or at any time during a RTP session. * * Existing SRTP sessions or currently active ZRTP processing will * not be stopped or disconnected. * * If the application enables ZRTP then: *
    *
  • ZRTP transport starts to send ZRTP Hello packets after at least * one RTP packet was sent and received on the associated RTP * session. Thus if an application enables ZRTP and ZRTP transport * detects traffic on the RTP session then ZRTP transport automatically * starts the ZRTP protocol. This automatic start is convenient * for applications that negotiate RTP parameters and set up RTP * sessions but the actual RTP traffic starts some time later. *
  • *
  • ZRTP transport analyses incoming packets to detect ZRTP * messages. If ZRTP was started, either via automatic start (see * above) or explicitly via @c zrtp_startZrtp, then ZrtpQueue * forwards ZRTP packets to the GNU ZRTP core. *
* * @param tp * Pointer to the ZRTP transport data as returned by * @c pjmedia_transport_zrtp_create. * * @param onOff * @c 1 to enable ZRTP, @c 0 to disable ZRTP */ PJ_DECL(void) pjmedia_transport_zrtp_setEnableZrtp(pjmedia_transport *tp, pj_bool_t onOff); /** * Return the state of ZRTP enable state. * * @param tp * Pointer to the ZRTP transport data as returned by * @c pjmedia_transport_zrtp_create. * * @return @c true if ZRTP processing is enabled, @c false * otherwise. */ PJ_DECL(pj_bool_t) pjmedia_transport_zrtp_isEnableZrtp(pjmedia_transport *tp); /** * Starts the ZRTP protocol engine. * * Applications may call this method to immediatly start the ZRTP protocol * engine any time after initializing ZRTP and setting optinal parameters, * for example client id or multi-stream parameters. * * If the application does not call this method but sucessfully initialized * the ZRTP engine using @c pjmedia_transport_zrtp_initialize then ZRTP may * also start, depending on the autoEnable parameter. * * @param tp * Pointer to the ZRTP transport data as returned by * @c pjmedia_transport_zrtp_create. * * @see pjmedia_transport_zrtp_initialize */ PJ_DECL(void) pjmedia_transport_zrtp_startZrtp(pjmedia_transport *tp); /** * Stops the ZRTP protocol engine. * * Applications call this method to stop the ZRTP protocol * engine. The ZRTP transport can not start or process any ZRTP * negotiations. * * This call does not deactivate SRTP processing of ZRTP transport, thus * the ZRTP transport still encrypts/decrypts data via SRTP. * * @param tp * Pointer to the ZRTP transport data as returned by * @c pjmedia_transport_zrtp_create. * */ PJ_DECL(void) pjmedia_transport_zrtp_stopZrtp(pjmedia_transport *tp); /** * Set the local SSRC in case of receive-only sessions. * * Receiver-only RTP sessions never send RTP packets, thus ZRTP cannot learn * the local (sender) SSRC. ZRTP requires the SSRC to bind the RTP session * to the SRTP and its handshake. In this case the application shall generate * a SSRC value and set it. * * Usually an application knows if a specific RTP session is receive-only, for * example by inspecting and parsing the SDP data. * * If the application later decides to switch this RTP session to full-duplex * mode (send and receive) it shall use the generated SSRC to intialize the * RTP session. Then the outgoing packets are encrypted by SRTP. * * @param tp * Pointer to the ZRTP transport data as returned by * @c pjmedia_transport_zrtp_create. * * @param ssrc * The local ssrc value in host order. */ PJ_DECL(void) pjmedia_transport_zrtp_setLocalSSRC(pjmedia_transport *tp, uint32_t ssrc); /** * Check the state of the MitM mode flag. * * If true then this ZRTP session acts as MitM, usually enabled by a PBX * client (user agent) * * @return state of mitmMode */ PJ_DECL(pj_bool_t) pjmedia_transport_zrtp_isMitmMode(pjmedia_transport *tp); /** * Set the state of the MitM mode flag. * * If MitM mode is set to true this ZRTP session acts as MitM, usually * enabled by a PBX client (user agent). * * @param mitmMode defines the new state of the mitmMode flag */ PJ_DECL(void) pjmedia_transport_zrtp_setMitmMode(pjmedia_transport *tp, pj_bool_t mitmMode); /** * Set / reset the SAS verification flag. * */ PJ_DECL(void) pjmedia_transport_zrtp_setSASVerified(pjmedia_transport *tp, pj_bool_t verified); /** * Get the peer's ZID. * */ PJ_DECL(int) pjmedia_transport_zrtp_getPeerZid(pjmedia_transport *tp, unsigned char* data); /** * Get the peer's name. * */ PJ_DECL(char*) pjmedia_transport_zrtp_getPeerName(pjmedia_transport *tp); /** * Set the peer's name. * */ PJ_DECL(void) pjmedia_transport_zrtp_putPeerName(pjmedia_transport *tp, const char *name); PJ_DECL(char*) pjmedia_transport_zrtp_getMultiStreamParameters(pjmedia_transport *tp, pj_int32_t *length); PJ_DECL(void) pjmedia_transport_zrtp_setMultiStreamParameters(pjmedia_transport *tp, const char *parameters, pj_int32_t length, pjmedia_transport *master_tp); /** * Get the ZRTP context pointer. * * Appplications need the ZRTP context pointer if they call ZRTP specific * methods. The ZRTP specific include file @c ZrtpCWrapper contains the * descriptions of the ZRTP methods. * * @return Pointer to ZRTP context * * @see zrtp_setAuxSecret() * @see zrtp_setPbxSecret() * @see zrtp_inState() * @see zrtp_SASVerified() * @see zrtp_resetSASVerified() * @see zrtp_getHelloHash() * @see zrtp_getMultiStrParams() * @see zrtp_setMultiStrParams() * @see zrtp_isMultiStream() * @see zrtp_isMultiStreamAvailable() * @see zrtp_acceptEnrollment() * @see zrtp_setSignatureData() * @see zrtp_getSignatureData() * @see zrtp_getSignatureLength() * @see zrtp_getZid(); */ PJ_DECL(ZrtpContext*) pjmedia_transport_zrtp_getZrtpContext(pjmedia_transport *tp); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_TRANSPORT_ADAPTER_SAMPLE_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/types.h ================================================ /* $Id: types.h 3774 2011-09-27 05:24:06Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_TYPES_H__ #define __PJMEDIA_TYPES_H__ /** * @file pjmedia/types.h Basic Types * @brief Basic PJMEDIA types. */ #include #include #include /** * @defgroup PJMEDIA_PORT Media Ports Framework * @brief Extensible framework for media terminations */ /** * @defgroup PJMEDIA_FRAME_OP Audio Manipulation Algorithms * @brief Algorithms to manipulate audio frames */ /** * @defgroup PJMEDIA_TYPES Basic Types * @ingroup PJMEDIA_BASE * @brief Basic PJMEDIA types and operations. * @{ */ /** * Top most media type. See also #pjmedia_type_name(). */ typedef enum pjmedia_type { /** Type is not specified. */ PJMEDIA_TYPE_NONE, /** The media is audio */ PJMEDIA_TYPE_AUDIO, /** The media is video. */ PJMEDIA_TYPE_VIDEO, /** The media is application. */ PJMEDIA_TYPE_APPLICATION, /** The media type is unknown or unsupported. */ PJMEDIA_TYPE_UNKNOWN } pjmedia_type; /** * Media transport protocol. */ typedef enum pjmedia_tp_proto { /** No transport type */ PJMEDIA_TP_PROTO_NONE = 0, /** RTP using A/V profile */ PJMEDIA_TP_PROTO_RTP_AVP, /** Secure RTP */ PJMEDIA_TP_PROTO_RTP_SAVP, /** Unknown */ PJMEDIA_TP_PROTO_UNKNOWN } pjmedia_tp_proto; /** * Media direction. */ typedef enum pjmedia_dir { /** None */ PJMEDIA_DIR_NONE = 0, /** Encoding (outgoing to network) stream, also known as capture */ PJMEDIA_DIR_ENCODING = 1, /** Same as encoding direction. */ PJMEDIA_DIR_CAPTURE = PJMEDIA_DIR_ENCODING, /** Decoding (incoming from network) stream, also known as playback. */ PJMEDIA_DIR_DECODING = 2, /** Same as decoding. */ PJMEDIA_DIR_PLAYBACK = PJMEDIA_DIR_DECODING, /** Same as decoding. */ PJMEDIA_DIR_RENDER = PJMEDIA_DIR_DECODING, /** Incoming and outgoing stream, same as PJMEDIA_DIR_CAPTURE_PLAYBACK */ PJMEDIA_DIR_ENCODING_DECODING = 3, /** Same as ENCODING_DECODING */ PJMEDIA_DIR_CAPTURE_PLAYBACK = PJMEDIA_DIR_ENCODING_DECODING, /** Same as ENCODING_DECODING */ PJMEDIA_DIR_CAPTURE_RENDER = PJMEDIA_DIR_ENCODING_DECODING } pjmedia_dir; /** * Opaque declaration of media endpoint. */ typedef struct pjmedia_endpt pjmedia_endpt; /* * Forward declaration for stream (needed by transport). */ typedef struct pjmedia_stream pjmedia_stream; /** * Enumeration for picture coordinate base. */ typedef enum pjmedia_coord_base { /** * This specifies that the pixel [0, 0] location is at the left-top * position. */ PJMEDIA_COORD_BASE_LEFT_TOP, /** * This specifies that the pixel [0, 0] location is at the left-bottom * position. */ PJMEDIA_COORD_BASE_LEFT_BOTTOM } pjmedia_coord_base; /** * This structure is used to represent rational numbers. */ typedef struct pjmedia_ratio { int num; /** < Numerator. */ int denum; /** < Denumerator. */ } pjmedia_ratio; /** * This structure represent a coordinate. */ typedef struct pjmedia_coord { int x; /**< X position of the coordinate */ int y; /**< Y position of the coordinate */ } pjmedia_coord; /** * This structure represents rectangle size. */ typedef struct pjmedia_rect_size { unsigned w; /**< The width. */ unsigned h; /**< The height. */ } pjmedia_rect_size; /** * This structure describes a rectangle. */ typedef struct pjmedia_rect { pjmedia_coord coord; /**< The position. */ pjmedia_rect_size size; /**< The size. */ } pjmedia_rect; /** * Enumeration for video/picture orientation. */ typedef enum pjmedia_orient { /** * Unknown orientation. */ PJMEDIA_ORIENT_UNKNOWN, /** * Natural orientation, i.e. the original orientation video will be * displayed/captured without rotation. */ PJMEDIA_ORIENT_NATURAL, /** * Specifies that the video/picture needs to be rotated 90 degrees * from its natural orientation in clockwise direction from the user's * perspective. * Note that for devices with back cameras (which faces away * from the user), the video will actually need to be rotated * 270 degrees clockwise instead. */ PJMEDIA_ORIENT_ROTATE_90DEG, /** * Specifies that the video/picture needs to be rotated 180 degrees * from its natural orientation. */ PJMEDIA_ORIENT_ROTATE_180DEG, /** * Specifies that the video/picture needs to be rotated 270 degrees * from its natural orientation in clockwise direction from the user's * perspective. * Note that for devices with back cameras (which faces away * from the user), the video will actually need to be rotated * 90 degrees clockwise instead. */ PJMEDIA_ORIENT_ROTATE_270DEG } pjmedia_orient; /** * Macro for packing format from a four character code, similar to FOURCC. */ #define PJMEDIA_FOURCC(C1, C2, C3, C4) ( C4<<24 | C3<<16 | C2<<8 | C1 ) /** * Utility function to return the string name for a pjmedia_type. * * @param t The media type. * * @return String. */ PJ_DECL(const char*) pjmedia_type_name(pjmedia_type t); /** * A utility function to convert fourcc type of value to four letters string. * * @param sig The fourcc value. * @param buf Buffer to store the string, which MUST be at least * five bytes long. * * @return The string. */ PJ_INLINE(const char*) pjmedia_fourcc_name(pj_uint32_t sig, char buf[]) { buf[3] = (char)((sig >> 24) & 0xFF); buf[2] = (char)((sig >> 16) & 0xFF); buf[1] = (char)((sig >> 8) & 0xFF); buf[0] = (char)((sig >> 0) & 0xFF); buf[4] = '\0'; return buf; } /** * @} */ #endif /* __PJMEDIA_TYPES_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/vid_codec.h ================================================ /* $Id: vid_codec.h 3956 2012-02-21 08:31:26Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_VID_CODEC_H__ #define __PJMEDIA_VID_CODEC_H__ /** * @file vid_codec.h * @brief Video codec framework. */ #include #include #include #include #include #include PJ_BEGIN_DECL /** * @defgroup PJMEDIA_VID_CODEC Video Codecs * @ingroup PJMEDIA_CODEC * @{ */ #define PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT 8 #define PJMEDIA_VID_CODEC_MAX_FPS_CNT 16 /** * This enumeration specifies the packetization property of video encoding * process. The value is bitmask, and smaller value will have higher priority * to be used. */ typedef enum pjmedia_vid_packing { /** * This specifies that the packetization is unknown, or if nothing * is supported. */ PJMEDIA_VID_PACKING_UNKNOWN, /** * This specifies that the result of video encoding process will be * segmented into packets, which is suitable for RTP transmission. * The maximum size of the packets is set in \a enc_mtu field of * pjmedia_vid_codec_param. */ PJMEDIA_VID_PACKING_PACKETS = 1, /** * This specifies that video encoding function will produce a whole * or full frame from the source frame. This is normally used for * encoding video for offline storage such as to an AVI file. The * maximum size of the packets is set in \a enc_mtu field of * pjmedia_vid_codec_param. */ PJMEDIA_VID_PACKING_WHOLE = 2 } pjmedia_vid_packing; /** * Enumeration of video frame info flag for the bit_info field in the * pjmedia_frame. */ typedef enum pjmedia_vid_frm_bit_info { /** * The video frame is keyframe. */ PJMEDIA_VID_FRM_KEYFRAME = 1 } pjmedia_vid_frm_bit_info; /** * Encoding option. */ typedef struct pjmedia_vid_encode_opt { /** * Flag to force the encoder to generate keyframe for the specified input * frame. When this flag is set, application can verify the result by * examining PJMEDIA_VID_FRM_KEYFRAME flag in the bit_info field of the * output frame. */ pj_bool_t force_keyframe; } pjmedia_vid_encode_opt; /** * Identification used to search for codec factory that supports specific * codec specification. */ typedef struct pjmedia_vid_codec_info { pjmedia_format_id fmt_id; /**< Encoded format ID */ unsigned pt; /**< Payload type */ pj_str_t encoding_name; /**< Encoding name */ pj_str_t encoding_desc; /**< Encoding desc */ unsigned clock_rate; /**< Clock rate */ pjmedia_dir dir; /**< Direction */ unsigned dec_fmt_id_cnt; /**< # of supported encoding source format IDs */ pjmedia_format_id dec_fmt_id[PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT]; /**< Supported encoding source format IDs */ unsigned packings; /**< Supported or requested packings, strategies, bitmask from pjmedia_vid_packing */ unsigned fps_cnt; /**< # of supported frame-rates, can be zero (support any frame-rate) */ pjmedia_ratio fps[PJMEDIA_VID_CODEC_MAX_FPS_CNT]; /**< Supported frame-rates */ } pjmedia_vid_codec_info; /** * Detailed codec attributes used in configuring a codec and in querying * the capability of codec factories. Default attributes of any codecs could * be queried using #pjmedia_vid_codec_mgr_get_default_param() and modified * using #pjmedia_vid_codec_mgr_set_default_param(). * * Please note that codec parameter also contains SDP specific setting, * #dec_fmtp and #enc_fmtp, which may need to be set appropriately based on * the effective setting. See each codec documentation for more detail. */ typedef struct pjmedia_vid_codec_param { pjmedia_dir dir; /**< Direction */ pjmedia_vid_packing packing; /**< Packetization strategy. */ pjmedia_format enc_fmt; /**< Encoded format */ pjmedia_codec_fmtp enc_fmtp; /**< Encoder fmtp params */ unsigned enc_mtu; /**< MTU or max payload size setting*/ pjmedia_format dec_fmt; /**< Decoded format */ pjmedia_codec_fmtp dec_fmtp; /**< Decoder fmtp params */ pj_bool_t ignore_fmtp; /**< Ignore fmtp params. If set to PJ_TRUE, the codec will apply format settings specified in enc_fmt and dec_fmt only. */ } pjmedia_vid_codec_param; /** * Duplicate video codec parameter. * * @param pool The pool. * @param src The video codec parameter to be duplicated. * * @return Duplicated codec parameter. */ PJ_DECL(pjmedia_vid_codec_param*) pjmedia_vid_codec_param_clone( pj_pool_t *pool, const pjmedia_vid_codec_param *src); /** * Forward declaration for video codec. */ typedef struct pjmedia_vid_codec pjmedia_vid_codec; /** * This structure describes codec operations. Each codec MUST implement * all of these functions. */ typedef struct pjmedia_vid_codec_op { /** * See #pjmedia_vid_codec_init(). */ pj_status_t (*init)(pjmedia_vid_codec *codec, pj_pool_t *pool ); /** * See #pjmedia_vid_codec_open(). */ pj_status_t (*open)(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *param ); /** * See #pjmedia_vid_codec_close(). */ pj_status_t (*close)(pjmedia_vid_codec *codec); /** * See #pjmedia_vid_codec_modify(). */ pj_status_t (*modify)(pjmedia_vid_codec *codec, const pjmedia_vid_codec_param *param); /** * See #pjmedia_vid_codec_get_param(). */ pj_status_t (*get_param)(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *param); /** * See #pjmedia_vid_codec_encode_begin(). */ pj_status_t (*encode_begin)(pjmedia_vid_codec *codec, const pjmedia_vid_encode_opt *opt, const pjmedia_frame *input, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more); /** * See #pjmedia_vid_codec_encode_more() */ pj_status_t (*encode_more)(pjmedia_vid_codec *codec, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more); /* * See #pjmedia_vid_codec_decode(). */ pj_status_t (*decode)(pjmedia_vid_codec *codec, pj_size_t count, pjmedia_frame packets[], unsigned out_size, pjmedia_frame *output); /** * See #pjmedia_vid_codec_recover() */ pj_status_t (*recover)(pjmedia_vid_codec *codec, unsigned out_size, pjmedia_frame *output); } pjmedia_vid_codec_op; /* * Forward declaration for pjmedia_vid_codec_factory. */ typedef struct pjmedia_vid_codec_factory pjmedia_vid_codec_factory; /** * This structure describes a video codec instance. Codec implementers * should use #pjmedia_vid_codec_init() to initialize this structure with * default values. */ struct pjmedia_vid_codec { /** Entries to put this codec instance in codec factory's list. */ PJ_DECL_LIST_MEMBER(struct pjmedia_vid_codec); /** Codec's private data. */ void *codec_data; /** Codec factory where this codec was allocated. */ pjmedia_vid_codec_factory *factory; /** Operations to codec. */ pjmedia_vid_codec_op *op; }; /** * This structure describes operations that must be supported by codec * factories. */ typedef struct pjmedia_vid_codec_factory_op { /** * Check whether the factory can create codec with the specified * codec info. * * @param factory The codec factory. * @param info The codec info. * * @return PJ_SUCCESS if this factory is able to create an * instance of codec with the specified info. */ pj_status_t (*test_alloc)(pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info ); /** * Create default attributes for the specified codec ID. This function * can be called by application to get the capability of the codec. * * @param factory The codec factory. * @param info The codec info. * @param attr The attribute to be initialized. * * @return PJ_SUCCESS if success. */ pj_status_t (*default_attr)(pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info, pjmedia_vid_codec_param *attr ); /** * Enumerate supported codecs that can be created using this factory. * * @param factory The codec factory. * @param count On input, specifies the number of elements in * the array. On output, the value will be set to * the number of elements that have been initialized * by this function. * @param info The codec info array, which contents will be * initialized upon return. * * @return PJ_SUCCESS on success. */ pj_status_t (*enum_info)(pjmedia_vid_codec_factory *factory, unsigned *count, pjmedia_vid_codec_info codecs[]); /** * Create one instance of the codec with the specified codec info. * * @param factory The codec factory. * @param info The codec info. * @param p_codec Pointer to receive the codec instance. * * @return PJ_SUCCESS on success. */ pj_status_t (*alloc_codec)(pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info, pjmedia_vid_codec **p_codec); /** * This function is called by codec manager to return a particular * instance of codec back to the codec factory. * * @param factory The codec factory. * @param codec The codec instance to be returned. * * @return PJ_SUCCESS on success. */ pj_status_t (*dealloc_codec)(pjmedia_vid_codec_factory *factory, pjmedia_vid_codec *codec ); } pjmedia_vid_codec_factory_op; /** * Codec factory describes a module that is able to create codec with specific * capabilities. These capabilities can be queried by codec manager to create * instances of codec. */ struct pjmedia_vid_codec_factory { /** Entries to put this structure in the codec manager list. */ PJ_DECL_LIST_MEMBER(struct pjmedia_vid_codec_factory); /** The factory's private data. */ void *factory_data; /** Operations to the factory. */ pjmedia_vid_codec_factory_op *op; }; /** * Opaque declaration for codec manager. */ typedef struct pjmedia_vid_codec_mgr pjmedia_vid_codec_mgr; /** * Declare maximum codecs */ #define PJMEDIA_VID_CODEC_MGR_MAX_CODECS 32 /** * Initialize codec manager. If there is no the default video codec manager, * this function will automatically set the default video codec manager to * the new codec manager instance. Normally this function is called by pjmedia * endpoint's initialization code. * * @param pool The pool instance. * @param mgr The pointer to the new codec manager instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_create(pj_pool_t *pool, pjmedia_vid_codec_mgr **mgr); /** * Destroy codec manager. Normally this function is called by pjmedia * endpoint's deinitialization code. * * @param mgr Codec manager instance. If NULL, it is the default codec * manager instance will be destroyed. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_destroy(pjmedia_vid_codec_mgr *mgr); /** * Get the default codec manager instance. * * @return The default codec manager instance or NULL if none. */ PJ_DECL(pjmedia_vid_codec_mgr*) pjmedia_vid_codec_mgr_instance(void); /** * Set the default codec manager instance. * * @param mgr The codec manager instance. */ PJ_DECL(void) pjmedia_vid_codec_mgr_set_instance(pjmedia_vid_codec_mgr* mgr); /** * Register codec factory to codec manager. This will also register * all supported codecs in the factory to the codec manager. * * @param mgr The codec manager instance. If NULL, the default codec * manager instance will be used. * @param factory The codec factory to be registered. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_register_factory( pjmedia_vid_codec_mgr *mgr, pjmedia_vid_codec_factory *factory); /** * Unregister codec factory from the codec manager. This will also * remove all the codecs registered by the codec factory from the * codec manager's list of supported codecs. * * @param mgr The codec manager instance. If NULL, the default codec * manager instance will be used. * @param factory The codec factory to be unregistered. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_unregister_factory( pjmedia_vid_codec_mgr *mgr, pjmedia_vid_codec_factory *factory); /** * Enumerate all supported codecs that have been registered to the * codec manager by codec factories. * * @param mgr The codec manager instance. If NULL, the default codec * manager instance will be used. * @param count On input, specifies the number of elements in * the array. On output, the value will be set to * the number of elements that have been initialized * by this function. * @param info The codec info array, which contents will be * initialized upon return. * @param prio Optional pointer to receive array of codec priorities. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_enum_codecs(pjmedia_vid_codec_mgr *mgr, unsigned *count, pjmedia_vid_codec_info info[], unsigned *prio); /** * Get codec info for the specified payload type. The payload type must be * static or locally defined in #pjmedia_video_pt. * * @param mgr The codec manager instance. If NULL, the default codec * manager instance will be used. * @param pt The payload type/number. * @param info Pointer to receive codec info. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_get_codec_info( pjmedia_vid_codec_mgr *mgr, unsigned pt, const pjmedia_vid_codec_info **info); /** * Get codec info for the specified format ID. * * @param mgr The codec manager instance. If NULL, the default codec * manager instance will be used. * @param fmt_id Format ID. See #pjmedia_format_id * @param info Pointer to receive codec info. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_get_codec_info2(pjmedia_vid_codec_mgr *mgr, pjmedia_format_id fmt_id, const pjmedia_vid_codec_info **info); /** * Convert codec info struct into a unique codec identifier. * A codec identifier looks something like "H263/90000". * * @param info The codec info * @param id Buffer to put the codec info string. * @param max_len The length of the buffer. * * @return The null terminated codec info string, or NULL if * the buffer is not long enough. */ PJ_DECL(char*) pjmedia_vid_codec_info_to_id(const pjmedia_vid_codec_info *info, char *id, unsigned max_len ); /** * Find codecs by the unique codec identifier. This function will find * all codecs that match the codec identifier prefix. For example, if * "H26" is specified, then it will find "H263/90000", "H264/90000", * and so on, up to the maximum count specified in the argument. * * @param mgr The codec manager instance. If NULL, the default codec * manager instance will be used. * @param codec_id The full codec ID or codec ID prefix. If an empty * string is given, it will match all codecs. * @param count Maximum number of codecs to find. On return, it * contains the actual number of codecs found. * @param p_info Array of pointer to codec info to be filled. This * argument may be NULL, which in this case, only * codec count will be returned. * @param prio Optional array of codec priorities. * * @return PJ_SUCCESS if at least one codec info is found. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_find_codecs_by_id(pjmedia_vid_codec_mgr *mgr, const pj_str_t *codec_id, unsigned *count, const pjmedia_vid_codec_info *p_info[], unsigned prio[]); /** * Set codec priority. The codec priority determines the order of * the codec in the SDP created by the endpoint. If more than one codecs * are found with the same codec_id prefix, then the function sets the * priorities of all those codecs. * * @param mgr The codec manager instance. If NULL, the default codec * manager instance will be used. * @param codec_id The full codec ID or codec ID prefix. If an empty * string is given, it will match all codecs. * @param prio Priority to be set. The priority can have any value * between 1 to 255. When the priority is set to zero, * the codec will be disabled. * * @return PJ_SUCCESS if at least one codec info is found. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_set_codec_priority(pjmedia_vid_codec_mgr *mgr, const pj_str_t *codec_id, pj_uint8_t prio); /** * Get default codec param for the specified codec info. * * @param mgr The codec manager instance. If NULL, the default codec * manager instance will be used. * @param info The codec info, which default parameter's is being * queried. * @param param On return, will be filled with the default codec * parameter. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_get_default_param(pjmedia_vid_codec_mgr *mgr, const pjmedia_vid_codec_info *info, pjmedia_vid_codec_param *param); /** * Set default codec param for the specified codec info. * * @param mgr The codec manager instance. If NULL, the default codec * manager instance will be used. * @param pool The pool instance. * @param info The codec info, which default parameter's is being * updated. * @param param The new default codec parameter. Set to NULL to reset * codec parameter to library default settings. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_set_default_param(pjmedia_vid_codec_mgr *mgr, const pjmedia_vid_codec_info *info, const pjmedia_vid_codec_param *param); /** * Request the codec manager to allocate one instance of codec with the * specified codec info. The codec will enumerate all codec factories * until it finds factory that is able to create the specified codec. * * @param mgr The codec manager instance. If NULL, the default codec * manager instance will be used. * @param info The information about the codec to be created. * @param p_codec Pointer to receive the codec instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_alloc_codec( pjmedia_vid_codec_mgr *mgr, const pjmedia_vid_codec_info *info, pjmedia_vid_codec **p_codec); /** * Deallocate the specified codec instance. The codec manager will return * the instance of the codec back to its factory. * * @param mgr The codec manager instance. If NULL, the default codec * manager instance will be used. * @param codec The codec instance. * * @return PJ_SUCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_dealloc_codec( pjmedia_vid_codec_mgr *mgr, pjmedia_vid_codec *codec); /** * Initialize codec using the specified attribute. * * @param codec The codec instance. * @param pool Pool to use when the codec needs to allocate * some memory. * * @return PJ_SUCCESS on success. */ PJ_INLINE(pj_status_t) pjmedia_vid_codec_init( pjmedia_vid_codec *codec, pj_pool_t *pool ) { return (*codec->op->init)(codec, pool); } /** * Open the codec and initialize with the specified parameter. * Upon successful initialization, the codec may modify the parameter * and fills in the unspecified values (such as size or frame rate of * the encoder format, as it may need to be negotiated with remote * preferences via SDP fmtp). * * @param codec The codec instance. * @param param Codec initialization parameter. * * @return PJ_SUCCESS on success. */ PJ_INLINE(pj_status_t) pjmedia_vid_codec_open(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *param) { return (*codec->op->open)(codec, param); } /** * Close and shutdown codec, releasing all resources allocated by * this codec, if any. * * @param codec The codec instance. * * @return PJ_SUCCESS on success. */ PJ_INLINE(pj_status_t) pjmedia_vid_codec_close( pjmedia_vid_codec *codec ) { return (*codec->op->close)(codec); } /** * Modify the codec parameter after the codec is open. * Note that not all codec parameters can be modified during run-time. * When the parameter cannot be changed, this function will return * non-PJ_SUCCESS, and the original parameters will not be changed. * * @param codec The codec instance. * @param param The new codec parameter. * * @return PJ_SUCCESS on success. */ PJ_INLINE(pj_status_t) pjmedia_vid_codec_modify(pjmedia_vid_codec *codec, const pjmedia_vid_codec_param *param) { return (*codec->op->modify)(codec, param); } /** * Get the codec parameter after the codec is opened. * * @param codec The codec instance. * @param param The codec parameter. * * @return PJ_SUCCESS on success. */ PJ_INLINE(pj_status_t) pjmedia_vid_codec_get_param(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *param) { return (*codec->op->get_param)(codec, param); } /** * Encode the specified input frame. The input MUST contain only one picture * with the appropriate format as specified when opening the codec. Depending * on the packing or packetization set in the \a packing param, the process * may produce multiple encoded packets or payloads to represent the picture. * This is true for example for PJMEDIA_VID_PACKING_PACKETS packing. In this * case, the \a has_more field will be set to PJ_TRUE, and application should * call pjmedia_vid_codec_encode_more() to get the remaining results from the * codec. * * @param codec The codec instance. * @param opt Optional encoding options. * @param input The input frame. * @param out_size The length of buffer in the output frame. This * should be at least the same as the configured * encoding MTU of the codec. * @param output The output frame. * @param has_more PJ_TRUE if more payloads are available; application * should then call pjmedia_vid_codec_encode_more() * to retrieve the remaining results. * * @return PJ_SUCCESS on success; */ PJ_INLINE(pj_status_t) pjmedia_vid_codec_encode_begin( pjmedia_vid_codec *codec, const pjmedia_vid_encode_opt *opt, const pjmedia_frame *input, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more) { return (*codec->op->encode_begin)(codec, opt, input, out_size, output, has_more); } /** * Retrieve more encoded packets/payloads from the codec. Application * should call this function repeatedly until \a has_more flag is set * to PJ_FALSE. * * @param codec The codec instance. * @param out_size The length of buffer in the output frame. This * should be at least the same as as the configured * encoding MTU of the codec. * @param output The output frame. * @param has_more PJ_TRUE if more payloads are available, which in * this case application should call \a encode_more() * to retrieve them. * * @return PJ_SUCCESS on success; */ PJ_INLINE(pj_status_t) pjmedia_vid_codec_encode_more( pjmedia_vid_codec *codec, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more) { return (*codec->op->encode_more)(codec, out_size, output, has_more); } /** * Decode the input packets into one picture. If the packing is set to * PJMEDIA_VID_PACKING_PACKETS when opening the codec, the codec is set * to decode multiple encoded packets into one picture. These encoded * packets are typically retrieved from the jitter buffer. If the packing * is set to PJMEDIA_VID_PACKING_WHOLE, then this decode function can only * accept one frame as the input. * * Note that the decoded picture format may different to the configured * setting (i.e. the format specified in the #pjmedia_vid_codec_param when * opening the codec), in this case the PJMEDIA_EVENT_FMT_CHANGED event will * be emitted by the codec to notify the event. The codec parameter will * also be updated, and application can query the format by using * pjmedia_vid_codec_get_param(). * * @param codec The codec instance. * @param pkt_count Number of packets in the input. * @param packets Array of input packets, each containing an encoded * frame. * @param out_size The length of buffer in the output frame. * @param output The output frame. * * @return PJ_SUCCESS on success; */ PJ_INLINE(pj_status_t) pjmedia_vid_codec_decode(pjmedia_vid_codec *codec, pj_size_t pkt_count, pjmedia_frame packets[], unsigned out_size, pjmedia_frame *output) { return (*codec->op->decode)(codec, pkt_count, packets, out_size, output); } /** * Recover a missing frame. * * @param codec The codec instance. * @param out_size The length of buffer in the output frame. * @param output The output frame where generated signal * will be placed. * * @return PJ_SUCCESS on success; */ PJ_INLINE(pj_status_t) pjmedia_vid_codec_recover(pjmedia_vid_codec *codec, unsigned out_size, pjmedia_frame *output) { if (codec->op && codec->op->recover) return (*codec->op->recover)(codec, out_size, output); else return PJ_ENOTSUP; } /** * @} */ /** * @defgroup PJMEDIA_CODEC_VID_CODECS Supported video codecs * @ingroup PJMEDIA_VID_CODEC */ PJ_END_DECL #endif /* __PJMEDIA_VID_CODEC_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/vid_codec_util.h ================================================ /* $Id: vid_codec_util.h 3715 2011-08-19 09:35:25Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_VID_CODEC_UTIL_H__ #define __PJMEDIA_VID_CODEC_UTIL_H__ /** * @file vid_codec_util.h * @brief Video codec utilities. */ #include #include PJ_BEGIN_DECL /** * Definition of H.263 parameters. */ typedef struct pjmedia_vid_codec_h263_fmtp { unsigned mpi_cnt; /**< # of parsed MPI param */ struct mpi { pjmedia_rect_size size; /**< Picture size/resolution */ unsigned val; /**< MPI value */ } mpi[32]; /**< Minimum Picture Interval parameter */ } pjmedia_vid_codec_h263_fmtp; /** * Parse SDP fmtp of H.263. * * @param fmtp The H.263 SDP fmtp to be parsed. * @param h263_fmtp The parsing result. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_h263_parse_fmtp( const pjmedia_codec_fmtp *fmtp, pjmedia_vid_codec_h263_fmtp *h263_fmtp); /** * Parse, negotiate, and apply the encoding and decoding SDP fmtp of H.263 * in the specified codec parameter. * * @param param The codec parameter. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_h263_apply_fmtp( pjmedia_vid_codec_param *param); /** * Definition of H.264 parameters. */ typedef struct pjmedia_vid_codec_h264_fmtp { /* profile-level-id */ pj_uint8_t profile_idc; /**< Profile ID */ pj_uint8_t profile_iop; /**< Profile constraints bits */ pj_uint8_t level; /**< Level */ /* packetization-mode */ pj_uint8_t packetization_mode; /**< Packetization mode */ /* max-mbps, max-fs, max-cpb, max-dpb, and max-br */ unsigned max_mbps; /**< Max macroblock processing rate */ unsigned max_fs; /**< Max frame size (in macroblocks) */ unsigned max_cpb; /**< Max coded picture buffer size */ unsigned max_dpb; /**< Max decoded picture buffer size */ unsigned max_br; /**< Max video bit rate */ /* sprop-parameter-sets, in NAL units */ pj_size_t sprop_param_sets_len; /**< Parameter set length */ pj_uint8_t sprop_param_sets[256]; /**< Parameter set (SPS & PPS), in NAL unit bitstream */ } pjmedia_vid_codec_h264_fmtp; /** * Parse SDP fmtp of H.264. * * @param fmtp The H.264 SDP fmtp to be parsed. * @param h264_fmtp The parsing result. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_h264_parse_fmtp( const pjmedia_codec_fmtp *fmtp, pjmedia_vid_codec_h264_fmtp *h264_fmtp); /** * Match H.264 format in the SDP media offer and answer. This will compare * H.264 identifier parameters in SDP fmtp, i.e: "profile-level-id" and * "packetization-mode" fields. For better interoperability, when the option * #PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER is set, this function * may update the answer so the parameters in the answer match to ones * in the offer. * * @param pool The memory pool. * @param offer The SDP media offer. * @param o_fmt_idx Index of the H.264 format in the SDP media offer. * @param answer The SDP media answer. * @param a_fmt_idx Index of the H.264 format in the SDP media answer. * @param option The format matching option, see * #pjmedia_sdp_neg_fmt_match_flag. * * @return PJ_SUCCESS when the formats in offer and answer match. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_h264_match_sdp( pj_pool_t *pool, pjmedia_sdp_media *offer, unsigned o_fmt_idx, pjmedia_sdp_media *answer, unsigned a_fmt_idx, unsigned option); /** * Parse and apply the encoding and decoding SDP fmtp of H.264 in the * specified codec parameter. This will validate size and fps to conform * to H.264 level specified in SDP fmtp "profile-level-id". * * @param param The codec parameter. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_codec_h264_apply_fmtp( pjmedia_vid_codec_param *param); PJ_END_DECL #endif /* __PJMEDIA_VID_CODEC_UTIL_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/vid_port.h ================================================ /* $Id: vid_port.h 4168 2012-06-18 05:59:08Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_VIDPORT_H__ #define __PJMEDIA_VIDPORT_H__ /** * @file pjmedia/videoport.h Video media port * @brief Video media port */ #include #include /** * @defgroup PJMEDIA_VIDEO_PORT Video media port * @ingroup PJMEDIA_PORT_CLOCK * @brief Video media port * @{ */ PJ_BEGIN_DECL /** * This structure describes the parameters to create a video port */ typedef struct pjmedia_vid_port_param { /** * Video stream parameter. */ pjmedia_vid_dev_param vidparam; /** * Specify whether the video port should use active or passive interface. * If active interface is selected, the video port will perform as * a media clock, automatically calls pjmedia_port_get_frame() and * pjmedia_port_put_frame() of its slave port (depending on the direction * that is specified when opening the video stream). If passive interface * is selected, application can retrieve the media port of this video * port by calling pjmedia_vid_port_get_passive_port(), and subsequently * calls pjmedia_port_put_frame() or pjmedia_port_get_frame() to that * media port. * * Default: PJ_TRUE */ pj_bool_t active; } pjmedia_vid_port_param; /** * Opaque data type for video port. */ typedef struct pjmedia_vid_port pjmedia_vid_port; /** * Initialize the parameter with the default values. Note that this typically * would only fill the structure to zeroes unless they have different default * values. * * @param prm The parameter. */ PJ_DECL(void) pjmedia_vid_port_param_default(pjmedia_vid_port_param *prm); /** * Create a video port with the specified parameter. When video port opens * the video stream with different parameter than the requested values in * the \a prm.vidparam argument, it will automatically do the necessary * conversion. * * @param pool Pool to allocate memory from. * @param prm The video port parameter. * @param p_vp Pointer to receive the result. * * @return PJ_SUCCESS if video port has been created * successfully, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_vid_port_create(pj_pool_t *pool, const pjmedia_vid_port_param *prm, pjmedia_vid_port **p_vp); /** * Set the callbacks of the video port's underlying video stream. * * @param vid_port The video port. * @param cb Pointer to structure containing video stream * callbacks. * @param user_data Arbitrary user data, which will be given back in the * callbacks. */ PJ_DECL(void) pjmedia_vid_port_set_cb(pjmedia_vid_port *vid_port, const pjmedia_vid_dev_cb *cb, void *user_data); /** * Return the underlying video stream of the video port. * * @param vid_port The video port. * * @return The video stream. */ PJ_DECL(pjmedia_vid_dev_stream*) pjmedia_vid_port_get_stream(pjmedia_vid_port *vid_port); /** * Return the (passive) media port of the video port. This operation * is only valid for video ports created with passive interface selected. * Retrieving the media port for active video ports may raise an * assertion. * * @param vid_port The video port. * * @return The media port instance, or NULL. */ PJ_DECL(pjmedia_port*) pjmedia_vid_port_get_passive_port(pjmedia_vid_port *vid_port); /** * Get a clock source from the video port. * * @param vid_port The video port. * * @return The clock source. */ PJ_DECL(pjmedia_clock_src *) pjmedia_vid_port_get_clock_src( pjmedia_vid_port *vid_port ); /** * Set a clock source for the video port. * * @param vid_port The video port. * @param clocksrc The clock source. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_vid_port_set_clock_src( pjmedia_vid_port *vid_port, pjmedia_clock_src *clocksrc ); /** * Connect the video port to a downstream (slave) media port. This operation * is only valid for video ports created with active interface selected. * Connecting a passive video port may raise an assertion. * * @param vid_port The video port. * @param port A downstream media port to be connected to * this video port. * @param destroy Specify if the downstream media port should also be * destroyed by this video port when the video port * is destroyed. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_vid_port_connect(pjmedia_vid_port *vid_port, pjmedia_port *port, pj_bool_t destroy); /** * Disconnect the video port from its downstream (slave) media port, if any. * This operation is only valid for video ports created with active interface * selected, and assertion may be triggered if this is invoked on a passive * video port. * * @param vid_port The video port. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_vid_port_disconnect(pjmedia_vid_port *vid_port); /** * Retrieve the media port currently connected as downstream media port of the * specified video port. This operation is only valid for video ports created * with active interface selected, and assertion may be triggered if this is * invoked on a passive video port. * * @param vid_port The video port. * * @return Media port currently connected to the video port, * if any. */ PJ_DECL(pjmedia_port*) pjmedia_vid_port_get_connected_port(pjmedia_vid_port *vid_port); /** * Start the video port. * * @param vid_port The video port. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_vid_port_start(pjmedia_vid_port *vid_port); /** * Query whether the video port has been started. * * @param vid_port The video port. * * @return PJ_TRUE if the video port has been started. */ PJ_DECL(pj_bool_t) pjmedia_vid_port_is_running(pjmedia_vid_port *vid_port); /** * Stop the video port. * * @param vid_port The video port. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_vid_port_stop(pjmedia_vid_port *vid_port); /** * Destroy the video port, along with its video stream. If the video port is * an active one, this may also destroy the downstream media port, if the * destroy flag is set when the media port is connected. * * @param vid_port The video port. */ PJ_DECL(void) pjmedia_vid_port_destroy(pjmedia_vid_port *vid_port); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_VIDPORT_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/vid_stream.h ================================================ /* $Id: vid_stream.h 4043 2012-04-12 13:41:50Z nanang $ */ /* * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_VID_STREAM_H__ #define __PJMEDIA_VID_STREAM_H__ /** * @file vid_stream.h * @brief Video Stream. */ #include #include #include #include #include #include #include PJ_BEGIN_DECL /** * @defgroup PJMED_VID_STRM Video streams * @ingroup PJMEDIA_PORT * @brief Video communication via the network * @{ * * A video stream is a bidirectional video communication between two * endpoints. It corresponds to a video media description ("m=video" line) * in SDP session descriptor. * * A video stream consists of two unidirectional channels: * - encoding channel, which transmits unidirectional video to remote, and * - decoding channel, which receives unidirectional media from remote. * * A video stream exports two media port interface (see @ref PJMEDIA_PORT), * one for each direction, and application normally uses this interface to * interconnect the stream to other PJMEDIA components, e.g: the video * capture port supplies frames to the encoding port and video renderer * consumes frames from the decoding port. * * A video stream internally manages the following objects: * - an instance of video codec (see @ref PJMEDIA_VID_CODEC), * - an @ref PJMED_JBUF, * - two instances of RTP sessions (#pjmedia_rtp_session, one for each * direction), * - one instance of RTCP session (#pjmedia_rtcp_session), * - and a reference to video transport to send and receive packets * to/from the network (see @ref PJMEDIA_TRANSPORT). * * Video streams are created by calling #pjmedia_vid_stream_create(), * specifying #pjmedia_stream_info structure in the parameter. Application * can construct the #pjmedia_vid_stream_info structure manually, or use * #pjmedia_vid_stream_info_from_sdp() function to construct the * #pjmedia_vid stream_info from local and remote SDP session descriptors. */ /** * Enumeration of video stream sending rate control. */ typedef enum pjmedia_vid_stream_rc_method { /** * No sending rate control. All outgoing RTP packets will be transmitted * immediately right after encoding process is done. */ PJMEDIA_VID_STREAM_RC_NONE = 0, /** * Simple blocking. Each outgoing RTP packet transmission may be delayed * to avoid peak bandwidth that is much higher than specified. The thread * invoking the video stream put_frame(), e.g: video capture device thread, * will be blocked whenever transmission delay takes place. */ PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING = 1 } pjmedia_vid_stream_rc_method; /** * Structure of configuration settings for video stream sending rate control. */ typedef struct pjmedia_vid_stream_rc_config { /** * Rate control method. * * Default: PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING. */ pjmedia_vid_stream_rc_method method; /** * Upstream/outgoing bandwidth. If this is set to zero, the video stream * will use codec maximum bitrate setting. * * Default: 0 (follow codec maximum bitrate). */ unsigned bandwidth; } pjmedia_vid_stream_rc_config; /** * This structure describes video stream information. Each video stream * corresponds to one "m=" line in SDP session descriptor, and it has * its own RTP/RTCP socket pair. */ typedef struct pjmedia_vid_stream_info { pjmedia_type type; /**< Media type (audio, video) */ pjmedia_tp_proto proto; /**< Transport protocol (RTP/AVP, etc.) */ pjmedia_dir dir; /**< Media direction. */ pj_sockaddr rem_addr; /**< Remote RTP address */ pj_sockaddr rem_rtcp; /**< Optional remote RTCP address. If sin_family is zero, the RTP address will be calculated from RTP. */ unsigned tx_pt; /**< Outgoing codec paylaod type. */ unsigned rx_pt; /**< Incoming codec paylaod type. */ pj_uint32_t ssrc; /**< RTP SSRC. */ pj_uint32_t rtp_ts; /**< Initial RTP timestamp. */ pj_uint16_t rtp_seq; /**< Initial RTP sequence number. */ pj_uint8_t rtp_seq_ts_set; /**< Bitmask flags if initial RTP sequence and/or timestamp for sender are set. bit 0/LSB : sequence flag bit 1 : timestamp flag */ int jb_init; /**< Jitter buffer init delay in msec. (-1 for default). */ int jb_min_pre; /**< Jitter buffer minimum prefetch delay in msec (-1 for default). */ int jb_max_pre; /**< Jitter buffer maximum prefetch delay in msec (-1 for default). */ int jb_max; /**< Jitter buffer max delay in msec. */ #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 pj_bool_t use_ka; /**< Stream keep-alive and NAT hole punch (see #PJMEDIA_STREAM_ENABLE_KA) is enabled? */ #endif pjmedia_vid_codec_info codec_info; /**< Incoming codec format info. */ pjmedia_vid_codec_param *codec_param; /**< Optional codec param. */ pj_bool_t rtcp_sdes_bye_disabled; /**< Disable automatic sending of RTCP SDES and BYE. */ pjmedia_vid_stream_rc_config rc_cfg; /**< Stream send rate control settings. */ } pjmedia_vid_stream_info; /** * This function will initialize the video stream info based on information * in both SDP session descriptors for the specified stream index. * The remaining information will be taken from default codec parameters. * If socket info array is specified, the socket will be copied to the * session info as well. * * @param si Stream info structure to be initialized. * @param pool Pool to allocate memory. * @param endpt PJMEDIA endpoint instance. * @param local Local SDP session descriptor. * @param remote Remote SDP session descriptor. * @param stream_idx Media stream index in the session descriptor. * * @return PJ_SUCCESS if stream info is successfully initialized. */ PJ_DECL(pj_status_t) pjmedia_vid_stream_info_from_sdp(pjmedia_vid_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote, unsigned stream_idx); /** * Initialize the video stream rate control with default settings. * * @param cfg Video stream rate control structure to be initialized. */ PJ_DECL(void) pjmedia_vid_stream_rc_config_default(pjmedia_vid_stream_rc_config *cfg); /* * Opaque declaration for video stream. */ typedef struct pjmedia_vid_stream pjmedia_vid_stream; /** * Create a video stream based on the specified parameter. After the video * stream has been created, application normally would want to get the media * port interface of the stream, by calling pjmedia_vid_stream_get_port(). * The media port interface exports put_frame() and get_frame() function, * used to transmit and receive media frames from the stream. * * Without application calling put_frame() and get_frame(), there will be * no media frames transmitted or received by the stream. * * @param endpt Media endpoint. * @param pool Optional pool to allocate memory for the stream. If * this is not specified, one will be created internally. * A large number of memory may be needed because jitter * buffer needs to preallocate some storage. * @param info Stream information to create the stream. Upon return, * this info will be updated with the information from * the instantiated codec. Note that if the "pool" * argument is NULL, some fields in this "info" parameter * will be allocated from the internal pool of the * stream, which means that they will only remain valid * as long as the stream is not destroyed. * @param tp Media transport instance used to transmit and receive * RTP/RTCP packets to/from the underlying network. * @param user_data Arbitrary user data (for future callback feature). * @param p_stream Pointer to receive the video stream. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_stream_create( pjmedia_endpt *endpt, pj_pool_t *pool, pjmedia_vid_stream_info *info, pjmedia_transport *tp, void *user_data, pjmedia_vid_stream **p_stream); /** * Destroy the video stream. * * @param stream The video stream. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_stream_destroy(pjmedia_vid_stream *stream); /** * Get the media port interface of the stream. The media port interface * declares put_frame() and get_frame() function, which is the only * way for application to transmit and receive media frames from the * stream. As bidirectional video streaming may have different video * formats in the encoding and decoding direction, there are two media * ports exported by the video stream, one for each direction. * * @param stream The video stream. * @param dir The video direction. * @param p_port Pointer to receive the port interface. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_stream_get_port( pjmedia_vid_stream *stream, pjmedia_dir dir, pjmedia_port **p_port); /** * Get the media transport object associated with this stream. * * @param st The video stream. * * @return The transport object being used by the stream. */ PJ_DECL(pjmedia_transport*) pjmedia_vid_stream_get_transport( pjmedia_vid_stream *st); /** * Get the stream statistics. See also #pjmedia_stream_get_stat_jbuf() * * @param stream The video stream. * @param stat Media stream statistics. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_stream_get_stat( const pjmedia_vid_stream *stream, pjmedia_rtcp_stat *stat); /** * Reset the video stream statistics. * * @param stream The video stream. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_stream_reset_stat(pjmedia_vid_stream *stream); /** * Get current jitter buffer state. See also #pjmedia_stream_get_stat() * * @param stream The video stream. * @param state Jitter buffer state. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_stream_get_stat_jbuf( const pjmedia_vid_stream *stream, pjmedia_jb_state *state); /** * Get the stream info. * * @param stream The video stream. * @param info Video stream info. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_stream_get_info( const pjmedia_vid_stream *stream, pjmedia_vid_stream_info *info); /** * Start the video stream. This will start the appropriate channels * in the video stream, depending on the video direction that was set * when the stream was created. * * @param stream The video stream. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream); /** * Query if the stream is started on the specified direction. * * @param stream The video stream. * @param dir The direction to be checked. * * @return PJ_TRUE if stream is started. */ PJ_DECL(pj_bool_t) pjmedia_vid_stream_is_running(pjmedia_vid_stream *stream, pjmedia_dir dir); /** * Pause stream channels. * * @param stream The video stream. * @param dir Which channel direction to pause. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream, pjmedia_dir dir); /** * Resume stream channels. * * @param stream The video stream. * @param dir Which channel direction to resume. * * @return PJ_SUCCESS on success; */ PJ_DECL(pj_status_t) pjmedia_vid_stream_resume(pjmedia_vid_stream *stream, pjmedia_dir dir); /** * Force stream to send video keyframe on the next transmission. * * @param stream The video stream. * * @return PJ_SUCCESS on success; */ PJ_DECL(pj_status_t) pjmedia_vid_stream_send_keyframe( pjmedia_vid_stream *stream); /** * Send RTCP SDES for the media stream. * * @param stream The media stream. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_stream_send_rtcp_sdes( pjmedia_vid_stream *stream); /** * Send RTCP BYE for the media stream. * * @param stream The media stream. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_stream_send_rtcp_bye( pjmedia_vid_stream *stream); /** * Send RTCP PLI for the media stream. * * @param stream The media stream. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_stream_send_rtcp_pli( pjmedia_vid_stream *stream); /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_VID_STREAM_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/vid_tee.h ================================================ /* $Id: vid_tee.h 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_VID_TEE_H__ #define __PJMEDIA_VID_TEE_H__ /** * @file vid_tee.h * @brief Video tee (source duplicator). */ #include /** * @addtogroup PJMEDIA_VID_TEE Video source duplicator * @ingroup PJMEDIA_PORT * @brief Duplicate video data from a media port into multiple media port * destinations * @{ * * This section describes media port to duplicate video data in the stream. * * A video tee branches video stream flow from one source port to multiple * destination ports by simply duplicating the video data supplied by the * source port and delivering the copy to all registered destinations. * * The video tee is a unidirectional port, i.e: data flows from source port * to destination ports only. Also, the video source port MUST actively call * pjmedia_port_put_frame() to the video tee and the video destination ports * MUST NEVER call pjmedia_port_get_frame() to the video tee. Please note that * there is no specific order of which destination port will receive a frame * from the video tee. * * The video tee is not thread-safe, so it is application responsibility * to synchronize video tee operations, e.g: make sure the source port is * paused during adding or removing a destination port. */ PJ_BEGIN_DECL /** * Enumeration of video tee flags. */ typedef enum pjmedia_vid_tee_flag { /** * Tell the video tee that the destination port will do in-place * processing, so the delivered data may be modified by this port. * If this flag is used, buffer will be copied before being given to * the destination port. */ PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC = 4, } pjmedia_vid_tee_flag; /** * Create a video tee port with the specified source media port. Application * should destroy the tee with pjmedia_port_destroy() as usual. Note that * destroying the tee does not destroy its destination ports. * * @param pool The pool. * @param fmt The source media port's format. * @param max_dst_cnt The maximum number of destination ports supported. * @param p_vid_tee Pointer to receive the video tee port. * * @return PJ_SUCCESS on success, or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_tee_create(pj_pool_t *pool, const pjmedia_format *fmt, unsigned max_dst_cnt, pjmedia_port **p_vid_tee); /** * Add a destination media port to the video tee. For this function, the * destination port's media format must match the source format. * * @param vid_tee The video tee. * @param option Video tee option, see @pjmedia_vid_tee_flag. * @param port The destination media port. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_vid_tee_add_dst_port(pjmedia_port *vid_tee, unsigned option, pjmedia_port *port); /** * Add a destination media port to the video tee. This function will also * create a converter if the destination port's media format does not match * the source format. * * @param vid_tee The video tee. * @param option Video tee option, see @pjmedia_vid_tee_flag. * @param port The destination media port. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_vid_tee_add_dst_port2(pjmedia_port *vid_tee, unsigned option, pjmedia_port *port); /** * Remove a destination media port from the video tee. * * @param vid_tee The video tee. * @param port The destination media port to be removed. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjmedia_vid_tee_remove_dst_port(pjmedia_port *vid_tee, pjmedia_port *port); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_VID_TEE_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/wav_playlist.h ================================================ /* $Id: wav_playlist.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_WAV_PLAYLIST_H__ #define __PJMEDIA_WAV_PLAYLIST_H__ /** * @file wav_playlist.h * @brief WAV file playlist. */ #include PJ_BEGIN_DECL /** * @defgroup PJMEDIA_WAV_PLAYLIST WAV File Play List * @ingroup PJMEDIA_PORT * @brief Audio playback of multiple WAV files * @{ * * The WAV play list port enables application to play back multiple * WAV files in a playlist. */ /** * Create a WAV playlist from the array of WAV file names. The WAV * files must have the same clock rate, number of channels, and bits * per sample, or otherwise this function will return error. * * @param pool Pool to create memory buffers for this port. * @param port_label Optional label to set as the port name. * @param file_list Array of WAV file names. * @param file_count Number of files in the array. * @param ptime The duration (in miliseconds) of each frame read * from this port. If the value is zero, the default * duration (20ms) will be used. * @param options Optional options. Application may specify * PJMEDIA_FILE_NO_LOOP to prevent play back loop. * @param buff_size Buffer size to be allocated. If the value is zero or * negative, the port will use default buffer size (which * is about 4KB). * @param p_port Pointer to receive the file port instance. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool, const pj_str_t *port_label, const pj_str_t file_list[], int file_count, unsigned ptime, unsigned options, pj_ssize_t buff_size, pjmedia_port **p_port); /** * Register a callback to be called when the file reading has reached the * end of file of the last file. If the file is set to play repeatedly, * then the callback will be called multiple times. Note that only one * callback can be registered for each file port. * * @param port The WAV play list port. * @param user_data User data to be specified in the callback * @param cb Callback to be called. If the callback returns non- * PJ_SUCCESS, the playback will stop. Note that if * application destroys the file port in the callback, * it must return non-PJ_SUCCESS here. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_wav_playlist_set_eof_cb(pjmedia_port *port, void *user_data, pj_status_t (*cb)(pjmedia_port *port, void *usr_data)); /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_WAV_PLAYLIST_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/wav_port.h ================================================ /* $Id: wav_port.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_WAV_PORT_H__ #define __PJMEDIA_WAV_PORT_H__ /** * @file wav_port.h * @brief WAV file player and writer. */ #include PJ_BEGIN_DECL /** * @defgroup PJMEDIA_FILE_PLAY WAV File Player * @ingroup PJMEDIA_PORT * @brief Audio playback from WAV file * @{ */ /** * WAV file player options. */ enum pjmedia_file_player_option { /** * Tell the file player to return NULL frame when the whole * file has been played. */ PJMEDIA_FILE_NO_LOOP = 1 }; /** * Additional information about the WAV player. */ typedef struct pjmedia_wav_player_info { /** * Format ID of the payload. */ pjmedia_format_id fmt_id; /** * The number of bits per sample of the file payload. For example, * the value is 16 for PCM WAV and 8 for Alaw/Ulas WAV files. */ unsigned payload_bits_per_sample; /** * The WAV payload size in bytes. */ pj_uint32_t size_bytes; /** * The WAV payload size in samples. */ pj_uint32_t size_samples; } pjmedia_wav_player_info; /** * Create a media port to play streams from a WAV file. WAV player port * supports for reading WAV file with uncompressed 16 bit PCM format or * compressed G.711 A-law/U-law format. * * @param pool Pool to create memory buffers for this port. * @param filename File name to open. * @param ptime The duration (in miliseconds) of each frame read * from this port. If the value is zero, the default * duration (20ms) will be used. * @param flags Port creation flags. * @param buff_size Buffer size to be allocated. If the value is zero or * negative, the port will use default buffer size (which * is about 4KB). * @param p_port Pointer to receive the file port instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, const char *filename, unsigned ptime, unsigned flags, pj_ssize_t buff_size, pjmedia_port **p_port ); /** * Get additional info about the file player. * * @param port The file port. * @param i The info. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_wav_player_get_info(pjmedia_port *port, pjmedia_wav_player_info *i); /** * Get the data length, in bytes. * * @param port The file player port. * * @return The length of the data, in bytes. On error, the * error code is given as negative value. */ PJ_DECL(pj_ssize_t) pjmedia_wav_player_get_len(pjmedia_port *port); /** * Set the file play position of WAV player. * * @param port The file player port. * @param offset Playback position in bytes, relative to the start of * the payload. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_wav_player_port_set_pos( pjmedia_port *port, pj_uint32_t offset ); /** * Get the file play position of WAV player, in bytes. * * @param port The file player port. * * @return The current play position, in bytes. On error, the * error code is given as negative value. */ PJ_DECL(pj_ssize_t) pjmedia_wav_player_port_get_pos( pjmedia_port *port ); /** * Register a callback to be called when the file reading has reached the * end of file. If the file is set to play repeatedly, then the callback * will be called multiple times. Note that only one callback can be * registered for each file port. * * @param port The file player port. * @param user_data User data to be specified in the callback * @param cb Callback to be called. If the callback returns non- * PJ_SUCCESS, the playback will stop. Note that if * application destroys the file port in the callback, * it must return non-PJ_SUCCESS here. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_wav_player_set_eof_cb( pjmedia_port *port, void *user_data, pj_status_t (*cb)(pjmedia_port *port, void *usr_data)); /** * @} */ /** * @defgroup PJMEDIA_FILE_REC File Writer (Recorder) * @ingroup PJMEDIA_PORT * @brief Audio capture/recording to WAV file * @{ */ /** * WAV file writer options. */ enum pjmedia_file_writer_option { /** * Tell the file writer to save the audio in PCM format. */ PJMEDIA_FILE_WRITE_PCM = 0, /** * Tell the file writer to save the audio in G711 Alaw format. */ PJMEDIA_FILE_WRITE_ALAW = 1, /** * Tell the file writer to save the audio in G711 Alaw format. */ PJMEDIA_FILE_WRITE_ULAW = 2, }; /** * Create a media port to record streams to a WAV file. Note that the port * must be closed properly (with #pjmedia_port_destroy()) so that the WAV * header can be filled with correct values (such as the file length). * WAV writer port supports for writing audio in uncompressed 16 bit PCM format * or compressed G.711 U-law/A-law format, this needs to be specified in * \a flags param. * * @param pool Pool to create memory buffers for this port. * @param filename File name. * @param clock_rate The sampling rate. * @param channel_count Number of channels. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Number of bits per sample (eg 16). * @param flags Port creation flags, see * #pjmedia_file_writer_option. * @param buff_size Buffer size to be allocated. If the value is * zero or negative, the port will use default buffer * size (which is about 4KB). * @param p_port Pointer to receive the file port instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_wav_writer_port_create(pj_pool_t *pool, const char *filename, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned flags, pj_ssize_t buff_size, pjmedia_port **p_port ); /** * Get current writing position. Note that this does not necessarily match * the size written to the file, since the WAV writer employs some internal * buffering. Also the value reported here only indicates the payload size * (it does not include the size of the WAV header), * * @param port The file writer port. * * @return Positive value to indicate the position (in bytes), * or negative value containing the error code. */ PJ_DECL(pj_ssize_t) pjmedia_wav_writer_port_get_pos( pjmedia_port *port ); /** * Register the callback to be called when the file writing has reached * certain size. Application can use this callback, for example, to limit * the size of the output file. * * @param port The file writer port. * @param pos The file position on which the callback will be called. * @param user_data User data to be specified in the callback, and will be * given on the callback. * @param cb Callback to be called. If the callback returns non- * PJ_SUCCESS, the writing will stop. Note that if * application destroys the port in the callback, it must * return non-PJ_SUCCESS here. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_wav_writer_port_set_cb( pjmedia_port *port, pj_size_t pos, void *user_data, pj_status_t (*cb)(pjmedia_port *port, void *usr_data)); /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_WAV_PORT_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/wave.h ================================================ /* $Id: wave.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_WAVE_H__ #define __PJMEDIA_WAVE_H__ /** * @file wave.h * @brief WAVE file manipulation. */ #include /** * @defgroup PJMEDIA_FILE_FORMAT File Formats * @brief Supported file formats */ /** * @defgroup PJMEDIA_WAVE WAVE Header * @ingroup PJMEDIA_FILE_FORMAT * @brief Representation of RIFF/WAVE file format * @{ * * This the the low level representation of RIFF/WAVE file format. For * higher abstraction, please see \ref PJMEDIA_FILE_PLAY and * \ref PJMEDIA_FILE_REC. */ PJ_BEGIN_DECL /** * Standard RIFF tag to identify RIFF file format in the WAVE header. */ #define PJMEDIA_RIFF_TAG ('F'<<24|'F'<<16|'I'<<8|'R') /** * Standard WAVE tag to identify WAVE header. */ #define PJMEDIA_WAVE_TAG ('E'<<24|'V'<<16|'A'<<8|'W') /** * Standard FMT tag to identify format chunks. */ #define PJMEDIA_FMT_TAG (' '<<24|'t'<<16|'m'<<8|'f') /** * Standard DATA tag to identify data chunks. */ #define PJMEDIA_DATA_TAG ('a'<<24|'t'<<16|'a'<<8|'d') /** * Standard FACT tag to identify fact chunks. */ #define PJMEDIA_FACT_TAG ('t'<<24|'c'<<16|'a'<<8|'f') /** * Enumeration of format compression tag. */ typedef enum { PJMEDIA_WAVE_FMT_TAG_PCM = 1, PJMEDIA_WAVE_FMT_TAG_ALAW = 6, PJMEDIA_WAVE_FMT_TAG_ULAW = 7 } pjmedia_wave_fmt_tag; /** * This file describes the simpler/canonical version of a WAVE file. * It does not support the full RIFF format specification. */ #pragma pack(2) struct pjmedia_wave_hdr { /** This structure describes RIFF WAVE file header */ struct { pj_uint32_t riff; /**< "RIFF" ASCII tag. */ pj_uint32_t file_len; /**< File length minus 8 bytes */ pj_uint32_t wave; /**< "WAVE" ASCII tag. */ } riff_hdr; /** This structure describes format chunks/header */ struct { pj_uint32_t fmt; /**< "fmt " ASCII tag. */ pj_uint32_t len; /**< 16 for PCM. */ pj_uint16_t fmt_tag; /**< 1 for PCM */ pj_uint16_t nchan; /**< Number of channels. */ pj_uint32_t sample_rate; /**< Sampling rate. */ pj_uint32_t bytes_per_sec; /**< Average bytes per second. */ pj_uint16_t block_align; /**< nchannels * bits / 8 */ pj_uint16_t bits_per_sample; /**< Bits per sample. */ } fmt_hdr; /** The data header preceeds the actual data in the file. */ struct { pj_uint32_t data; /**< "data" ASCII tag. */ pj_uint32_t len; /**< Data length. */ } data_hdr; }; #pragma pack() /** * @see pjmedia_wave_hdr */ typedef struct pjmedia_wave_hdr pjmedia_wave_hdr; /** * This structure describes generic RIFF subchunk header. */ typedef struct pjmedia_wave_subchunk { pj_uint32_t id; /**< Subchunk ASCII tag. */ pj_uint32_t len; /**< Length following this field */ } pjmedia_wave_subchunk; /** * Normalize subchunk header from little endian (the representation of * RIFF file) into host's endian. */ #if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 # define PJMEDIA_WAVE_NORMALIZE_SUBCHUNK(ch) \ do { \ (ch)->id = pj_swap32((ch)->id); \ (ch)->len = pj_swap32((ch)->len); \ } while (0) #else # define PJMEDIA_WAVE_NORMALIZE_SUBCHUNK(ch) #endif /** * On big-endian hosts, this function swaps the byte order of the values * in the WAVE header fields. On little-endian hosts, this function does * nothing. * * Application SHOULD call this function after reading the WAVE header * chunks from a file. * * @param hdr The WAVE header. */ PJ_DECL(void) pjmedia_wave_hdr_file_to_host( pjmedia_wave_hdr *hdr ); /** * On big-endian hosts, this function swaps the byte order of the values * in the WAVE header fields. On little-endian hosts, this function does * nothing. * * Application SHOULD call this function before writing the WAVE header * to a file. * * @param hdr The WAVE header. */ PJ_DECL(void) pjmedia_wave_hdr_host_to_file( pjmedia_wave_hdr *hdr ); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_WAVE_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia/wsola.h ================================================ /* $Id: wsola.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_WSOLA_H__ #define __PJMEDIA_WSOLA_H__ /** * @file wsola.h * @brief Waveform Similarity Based Overlap-Add (WSOLA) */ #include /** * @defgroup PJMED_WSOLA Waveform Similarity Based Overlap-Add (WSOLA) * @ingroup PJMEDIA_FRAME_OP * @brief Time-scale modification to audio without affecting the pitch * @{ * * This section describes Waveform Similarity Based Overlap-Add (WSOLA) * implementation in PJMEDIA. The WSOLA API here can be used both to * compress (speed-up) and stretch (expand, slow down) audio playback * without altering the pitch, or as a mean for performing packet loss * concealment (WSOLA). * * The WSOLA implementation is used by \ref PJMED_DELAYBUF and \ref PJMED_PLC. */ PJ_BEGIN_DECL /** * Opaque declaration for WSOLA structure. */ typedef struct pjmedia_wsola pjmedia_wsola; /** * WSOLA options, can be combined with bitmask operation. */ enum pjmedia_wsola_option { /** * Disable Hanning window to conserve memory. */ PJMEDIA_WSOLA_NO_HANNING = 1, /** * Specify that the WSOLA will not be used for PLC. */ PJMEDIA_WSOLA_NO_PLC = 2, /** * Specify that the WSOLA will not be used to discard frames in * non-contiguous buffer. */ PJMEDIA_WSOLA_NO_DISCARD = 4, /** * Disable fade-in and fade-out feature in the transition between * actual and synthetic frames in WSOLA. With fade feature enabled, * WSOLA will only generate a limited number of synthetic frames * (configurable with #pjmedia_wsola_set_max_expand()), fading out * the volume on every more samples it generates, and when it reaches * the limit it will only generate silence. */ PJMEDIA_WSOLA_NO_FADING = 8 }; /** * Create and initialize WSOLA. * * @param pool Pool to allocate memory for WSOLA. * @param clock_rate Sampling rate of audio playback. * @param samples_per_frame Number of samples per frame. * @param channel_count Number of channels. * @param options Option flags, bitmask combination of * #pjmedia_wsola_option. * @param p_wsola Pointer to receive WSOLA structure. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_wsola_create(pj_pool_t *pool, unsigned clock_rate, unsigned samples_per_frame, unsigned channel_count, unsigned options, pjmedia_wsola **p_wsola); /** * Specify maximum number of continuous synthetic frames that can be * generated by WSOLA, in milliseconds. This option will only take * effect if fading is not disabled via the option when the WSOLA * session was created. Default value is PJMEDIA_WSOLA_MAX_EXPAND_MSEC * (see also the documentation of PJMEDIA_WSOLA_MAX_EXPAND_MSEC for * more information). * * @param wsola The WSOLA session * @param msec The duration. * * @return PJ_SUCCESS normally. */ PJ_DECL(pj_status_t) pjmedia_wsola_set_max_expand(pjmedia_wsola *wsola, unsigned msec); /** * Destroy WSOLA. * * @param wsola WSOLA session. * * @return PJ_SUCCESS normally. */ PJ_DECL(pj_status_t) pjmedia_wsola_destroy(pjmedia_wsola *wsola); /** * Reset the buffer contents of WSOLA. * * @param wsola WSOLA session. * @param options Reset options, must be zero for now. * * @return PJ_SUCCESS normally. */ PJ_DECL(pj_status_t) pjmedia_wsola_reset(pjmedia_wsola *wsola, unsigned options); /** * Give one good frame to WSOLA to be kept as reference. Application * must continuously give WSOLA good frames to keep its session up to * date with current playback. Depending on the WSOLA implementation, * this function may modify the content of the frame. * * @param wsola WSOLA session. * @param frm The frame, which length must match the samples per * frame setting of the WSOLA session. * @param prev_lost If application previously generated a synthetic * frame with #pjmedia_wsola_generate() before calling * this function, specify whether that was because of * packet lost. If so, set this parameter to PJ_TRUE * to make WSOLA interpolate this frame with its buffer. * Otherwise if this value is PJ_FALSE, WSOLA will * just append this frame to the end of its buffer. * * @return PJ_SUCCESS normally. */ PJ_DECL(pj_status_t) pjmedia_wsola_save(pjmedia_wsola *wsola, pj_int16_t frm[], pj_bool_t prev_lost); /** * Generate one synthetic frame from WSOLA. * * @param wsola WSOLA session. * @param frm Buffer to receive the frame. * * @return PJ_SUCCESS normally. */ PJ_DECL(pj_status_t) pjmedia_wsola_generate(pjmedia_wsola *wsola, pj_int16_t frm[]); /** * Compress or compact the specified buffer by removing some audio samples * from the buffer, without altering the pitch. For this function to work, * total length of the buffer must be more than twice \a erase_cnt. * * @param wsola WSOLA session. * @param buf1 Pointer to buffer. * @param buf1_cnt Number of samples in the buffer. * @param buf2 Pointer to second buffer, if the buffer is not * contiguous. Otherwise this parameter must be NULL. * @param buf2_cnt Number of samples in the second buffer, if the buffer * is not contiguous. Otherwise this parameter should be * zero. * @param erase_cnt On input, specify the number of samples to be erased. * This function may erase more or less than the requested * number, and the actual number of samples erased will be * given on this argument upon returning from the function. * * @return PJ_SUCCESS if some samples have been erased, PJ_ETOOSMALL * if buffer is too small to be reduced, PJ_EINVAL if any * of the parameters are not valid. */ PJ_DECL(pj_status_t) pjmedia_wsola_discard(pjmedia_wsola *wsola, pj_int16_t buf1[], unsigned buf1_cnt, pj_int16_t buf2[], unsigned buf2_cnt, unsigned *erase_cnt); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_WSOLA_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-audiodev/audiodev.h ================================================ /* $Id: audiodev.h 4243 2012-08-31 11:42:17Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_AUDIODEV_AUDIODEV_H__ #define __PJMEDIA_AUDIODEV_AUDIODEV_H__ /** * @file audiodev.h * @brief Audio device API. */ #include #include #include #include #include #include #include PJ_BEGIN_DECL /** * @defgroup s2_audio_device_reference Audio Device API Reference * @ingroup audio_device_api * @brief API Reference * @{ */ /** * Type for device index. */ typedef pj_int32_t pjmedia_aud_dev_index; /** * Device index constants. */ enum { /** * Constant to denote default capture device */ PJMEDIA_AUD_DEFAULT_CAPTURE_DEV = -1, /** * Constant to denote default playback device */ PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV = -2, /** * Constant to denote invalid device index. */ PJMEDIA_AUD_INVALID_DEV = -3 }; /** * This enumeration identifies various audio device capabilities. These audio * capabilities indicates what features are supported by the underlying * audio device implementation. * * Applications get these capabilities in the #pjmedia_aud_dev_info structure. * * Application can also set the specific features/capabilities when opening * the audio stream by setting the \a flags member of #pjmedia_aud_param * structure. * * Once audio stream is running, application can also retrieve or set some * specific audio capability, by using #pjmedia_aud_stream_get_cap() and * #pjmedia_aud_stream_set_cap() and specifying the desired capability. The * value of the capability is specified as pointer, and application needs to * supply the pointer with the correct value, according to the documentation * of each of the capability. */ typedef enum pjmedia_aud_dev_cap { /** * Support for audio formats other than PCM. The value of this capability * is represented by #pjmedia_format structure. */ PJMEDIA_AUD_DEV_CAP_EXT_FORMAT = 1, /** * Support for audio input latency control or query. The value of this * capability is an unsigned integer containing milliseconds value of * the latency. */ PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY = 2, /** * Support for audio output latency control or query. The value of this * capability is an unsigned integer containing milliseconds value of * the latency. */ PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY = 4, /** * Support for setting/retrieving the audio input device volume level. * The value of this capability is an unsigned integer representing * the input audio volume setting in percent. */ PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING = 8, /** * Support for setting/retrieving the audio output device volume level. * The value of this capability is an unsigned integer representing * the output audio volume setting in percent. */ PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING = 16, /** * Support for monitoring the current audio input signal volume. * The value of this capability is an unsigned integer representing * the audio volume in percent. */ PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER = 32, /** * Support for monitoring the current audio output signal volume. * The value of this capability is an unsigned integer representing * the audio volume in percent. */ PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER = 64, /** * Support for audio input routing. The value of this capability is an * integer containing #pjmedia_aud_dev_route enumeration. */ PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE = 128, /** * Support for audio output routing (e.g. loudspeaker vs earpiece). The * value of this capability is an integer containing #pjmedia_aud_dev_route * enumeration. */ PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE = 256, /** * The audio device has echo cancellation feature. The value of this * capability is a pj_bool_t containing boolean PJ_TRUE or PJ_FALSE. */ PJMEDIA_AUD_DEV_CAP_EC = 512, /** * The audio device supports setting echo cancellation fail length. The * value of this capability is an unsigned integer representing the * echo tail in milliseconds. */ PJMEDIA_AUD_DEV_CAP_EC_TAIL = 1024, /** * The audio device has voice activity detection feature. The value * of this capability is a pj_bool_t containing boolean PJ_TRUE or * PJ_FALSE. */ PJMEDIA_AUD_DEV_CAP_VAD = 2048, /** * The audio device has comfort noise generation feature. The value * of this capability is a pj_bool_t containing boolean PJ_TRUE or * PJ_FALSE. */ PJMEDIA_AUD_DEV_CAP_CNG = 4096, /** * The audio device has packet loss concealment feature. The value * of this capability is a pj_bool_t containing boolean PJ_TRUE or * PJ_FALSE. */ PJMEDIA_AUD_DEV_CAP_PLC = 8192, /** * End of capability */ PJMEDIA_AUD_DEV_CAP_MAX = 16384 } pjmedia_aud_dev_cap; /** * This enumeration describes audio routing setting. */ typedef enum pjmedia_aud_dev_route { /** * Default route, it is the default audio route of the audio framework * backend, as in opening audio device without specifying any route * setting or with specifying neutral route setting. */ PJMEDIA_AUD_DEV_ROUTE_DEFAULT = 0, /** Route to loudspeaker */ PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER = 1, /** Route to earpiece */ PJMEDIA_AUD_DEV_ROUTE_EARPIECE = 2, /** Route to paired Bluetooth device */ PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH = 4 } pjmedia_aud_dev_route; /** * Device information structure returned by #pjmedia_aud_dev_get_info(). */ typedef struct pjmedia_aud_dev_info { /** * The device name */ char name[PJMEDIA_AUD_DEV_INFO_NAME_LEN]; /** * Maximum number of input channels supported by this device. If the * value is zero, the device does not support input operation (i.e. * it is a playback only device). */ unsigned input_count; /** * Maximum number of output channels supported by this device. If the * value is zero, the device does not support output operation (i.e. * it is an input only device). */ unsigned output_count; /** * Default sampling rate. */ unsigned default_samples_per_sec; /** * The underlying driver name */ char driver[32]; /** * Device capabilities, as bitmask combination of #pjmedia_aud_dev_cap. */ unsigned caps; /** * Supported audio device routes, as bitmask combination of * #pjmedia_aud_dev_route. The value may be zero if the device * does not support audio routing. */ unsigned routes; /** * Number of audio formats supported by this device. The value may be * zero if the device does not support non-PCM format. */ unsigned ext_fmt_cnt; /** * Array of supported extended audio formats */ pjmedia_format ext_fmt[8]; } pjmedia_aud_dev_info; /** * This callback is called by player stream when it needs additional data * to be played by the device. Application must fill in the whole of output * buffer with audio samples. * * The frame argument contains the following values: * - timestamp Playback timestamp, in samples. * - buf Buffer to be filled out by application. * - size The size requested in bytes, which will be equal to * the size of one whole packet. * * @param user_data User data associated with the stream. * @param frame Audio frame, which buffer is to be filled in by * the application. * * @return Returning non-PJ_SUCCESS will cause the audio stream * to stop */ typedef pj_status_t (*pjmedia_aud_play_cb)(void *user_data, pjmedia_frame *frame); /** * This callback is called by recorder stream when it has captured the whole * packet worth of audio samples. * * @param user_data User data associated with the stream. * @param frame Captured frame. * * @return Returning non-PJ_SUCCESS will cause the audio stream * to stop */ typedef pj_status_t (*pjmedia_aud_rec_cb)(void *user_data, pjmedia_frame *frame); /** * This structure specifies the parameters to open the audio stream. */ typedef struct pjmedia_aud_param { /** * The audio direction. This setting is mandatory. */ pjmedia_dir dir; /** * The audio recorder device ID. This setting is mandatory if the audio * direction includes input/capture direction. */ pjmedia_aud_dev_index rec_id; /** * The audio playback device ID. This setting is mandatory if the audio * direction includes output/playback direction. */ pjmedia_aud_dev_index play_id; /** * Clock rate/sampling rate. This setting is mandatory. */ unsigned clock_rate; /** * Number of channels. This setting is mandatory. */ unsigned channel_count; /** * Number of samples per frame. This setting is mandatory. */ unsigned samples_per_frame; /** * Number of bits per sample. This setting is mandatory. */ unsigned bits_per_sample; /** * This flags specifies which of the optional settings are valid in this * structure. The flags is bitmask combination of pjmedia_aud_dev_cap. */ unsigned flags; /** * Set the audio format. This setting is optional, and will only be used * if PJMEDIA_AUD_DEV_CAP_EXT_FORMAT is set in the flags. */ pjmedia_format ext_fmt; /** * Input latency, in milliseconds. This setting is optional, and will * only be used if PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY is set in the flags. */ unsigned input_latency_ms; /** * Input latency, in milliseconds. This setting is optional, and will * only be used if PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY is set in the flags. */ unsigned output_latency_ms; /** * Input volume setting, in percent. This setting is optional, and will * only be used if PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING is set in * the flags. */ unsigned input_vol; /** * Output volume setting, in percent. This setting is optional, and will * only be used if PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING is set in * the flags. */ unsigned output_vol; /** * Set the audio input route. This setting is optional, and will only be * used if PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE is set in the flags. */ pjmedia_aud_dev_route input_route; /** * Set the audio output route. This setting is optional, and will only be * used if PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE is set in the flags. */ pjmedia_aud_dev_route output_route; /** * Enable/disable echo canceller, if the device supports it. This setting * is optional, and will only be used if PJMEDIA_AUD_DEV_CAP_EC is set in * the flags. */ pj_bool_t ec_enabled; /** * Set echo canceller tail length in milliseconds, if the device supports * it. This setting is optional, and will only be used if * PJMEDIA_AUD_DEV_CAP_EC_TAIL is set in the flags. */ unsigned ec_tail_ms; /** * Enable/disable PLC. This setting is optional, and will only be used * if PJMEDIA_AUD_DEV_CAP_PLC is set in the flags. */ pj_bool_t plc_enabled; /** * Enable/disable CNG. This setting is optional, and will only be used * if PJMEDIA_AUD_DEV_CAP_CNG is set in the flags. */ pj_bool_t cng_enabled; /** * Enable/disable VAD. This setting is optional, and will only be used * if PJMEDIA_AUD_DEV_CAP_VAD is set in the flags. */ pj_bool_t vad_enabled; } pjmedia_aud_param; typedef enum pjmedia_aud_dev_event { PJMEDIA_AUD_DEV_DEFAULT_INPUT_CHANGED, PJMEDIA_AUD_DEV_DEFAULT_OUTPUT_CHANGED, PJMEDIA_AUD_DEV_LIST_WILL_REFRESH, PJMEDIA_AUD_DEV_LIST_DID_REFRESH } pjmedia_aud_dev_event; typedef void (*pjmedia_aud_dev_observer_callback)(pjmedia_aud_dev_event event); /** * This structure specifies the parameters to set an audio device observer */ typedef struct pjmedia_aud_dev_observer { pjmedia_aud_dev_observer_callback cb; pj_pool_t *pool; pj_mutex_t *lock; pj_thread_t *thread; pj_thread_desc thread_desc; } pjmedia_aud_dev_observer; /** Forward declaration for pjmedia_aud_stream */ typedef struct pjmedia_aud_stream pjmedia_aud_stream; /** Forward declaration for audio device factory */ typedef struct pjmedia_aud_dev_factory pjmedia_aud_dev_factory; /* typedef for factory creation function */ typedef pjmedia_aud_dev_factory* (*pjmedia_aud_dev_factory_create_func_ptr)(pj_pool_factory*); /** * Get string info for the specified capability. * * @param cap The capability ID. * @param p_desc Optional pointer which will be filled with longer * description about the capability. * * @return Capability name. */ PJ_DECL(const char*) pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap, const char **p_desc); /** * Set a capability field value in #pjmedia_aud_param structure. This will * also set the flags field for the specified capability in the structure. * * @param param The structure. * @param cap The audio capability which value is to be set. * @param pval Pointer to value. Please see the type of value to * be supplied in the pjmedia_aud_dev_cap documentation. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_param_set_cap(pjmedia_aud_param *param, pjmedia_aud_dev_cap cap, const void *pval); /** * Get a capability field value from #pjmedia_aud_param structure. This * function will return PJMEDIA_EAUD_INVCAP error if the flag for that * capability is not set in the flags field in the structure. * * @param param The structure. * @param cap The audio capability which value is to be retrieved. * @param pval Pointer to value. Please see the type of value to * be supplied in the pjmedia_aud_dev_cap documentation. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_param_get_cap(const pjmedia_aud_param *param, pjmedia_aud_dev_cap cap, void *pval); /** * Initialize the audio subsystem. This will register all supported audio * device factories to the audio subsystem. This function may be called * more than once, but each call to this function must have the * corresponding #pjmedia_aud_subsys_shutdown() call. * * @param pf The pool factory. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_subsys_init(pj_pool_factory *pf); /** * Get the pool factory registered to the audio subsystem. * * @return The pool factory. */ PJ_DECL(pj_pool_factory*) pjmedia_aud_subsys_get_pool_factory(void); /** * Shutdown the audio subsystem. This will destroy all audio device factories * registered in the audio subsystem. Note that currently opened audio streams * may or may not be closed, depending on the implementation of the audio * device factories. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_subsys_shutdown(void); /** * Register a supported audio device factory to the audio subsystem. This * function can only be called after calling #pjmedia_aud_subsys_init(). * * @param adf The audio device factory. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_register_factory(pjmedia_aud_dev_factory_create_func_ptr adf); /** * Unregister an audio device factory from the audio subsystem. This * function can only be called after calling #pjmedia_aud_subsys_init(). * Devices from this factory will be unlisted. If a device from this factory * is currently in use, then the behavior is undefined. * * @param adf The audio device factory. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_unregister_factory(pjmedia_aud_dev_factory_create_func_ptr adf); /** * Refresh the list of sound devices installed in the system. This function * will only refresh the list of audio device so all active audio streams will * be unaffected. After refreshing the device list, application MUST make sure * to update all index references to audio devices (i.e. all variables of type * pjmedia_aud_dev_index) before calling any function that accepts audio device * index as its parameter. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_dev_refresh(void); /** * Get the number of sound devices installed in the system. * * @return The number of sound devices installed in the system. */ PJ_DECL(unsigned) pjmedia_aud_dev_count(void); /** * Get device information. * * @param id The audio device ID. * @param info The device information which will be filled in by this * function once it returns successfully. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id, pjmedia_aud_dev_info *info); /** * Lookup device index based on the driver and device name. * * @param drv_name The driver name. * @param dev_name The device name. * @param id Pointer to store the returned device ID. * * @return PJ_SUCCESS if the device can be found. */ PJ_DECL(pj_status_t) pjmedia_aud_dev_lookup(const char *drv_name, const char *dev_name, pjmedia_aud_dev_index *id); /** * Initialize the audio device parameters with default values for the * specified device. * * @param id The audio device ID. * @param param The audio device parameters which will be initialized * by this function once it returns successfully. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_index id, pjmedia_aud_param *param); /** * Open audio stream object using the specified parameters. * * @param param Sound device parameters to be used for the stream. * @param rec_cb Callback to be called on every input frame captured. * @param play_cb Callback to be called everytime the sound device needs * audio frames to be played back. * @param user_data Arbitrary user data, which will be given back in the * callbacks. * @param p_strm Pointer to receive the audio stream. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_param *param, pjmedia_aud_rec_cb rec_cb, pjmedia_aud_play_cb play_cb, void *user_data, pjmedia_aud_stream **p_strm); /** * Get the running parameters for the specified audio stream. * * @param strm The audio stream. * @param param Audio stream parameters to be filled in by this * function once it returns successfully. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm, pjmedia_aud_param *param); /** * Get the value of a specific capability of the audio stream. * * @param strm The audio stream. * @param cap The audio capability which value is to be retrieved. * @param value Pointer to value to be filled in by this function * once it returns successfully. Please see the type * of value to be supplied in the pjmedia_aud_dev_cap * documentation. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_stream_get_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, void *value); /** * Set the value of a specific capability of the audio stream. * * @param strm The audio stream. * @param cap The audio capability which value is to be set. * @param value Pointer to value. Please see the type of value to * be supplied in the pjmedia_aud_dev_cap documentation. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_stream_set_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, const void *value); /** * Start the stream. * * @param strm The audio stream. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_stream_start(pjmedia_aud_stream *strm); /** * Stop the stream. * * @param strm The audio stream. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_stream_stop(pjmedia_aud_stream *strm); /** * Destroy the stream. * * @param strm The audio stream. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_stream_destroy(pjmedia_aud_stream *strm); /** * Set an audio device observer callback. * * @param cb The callback that needs to be registred, or NULL in * in case it needs to be unregistered. Only one callback * can be registered. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_aud_dev_set_observer_cb(pjmedia_aud_dev_observer_callback cb); /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_AUDIODEV_AUDIODEV_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-audiodev/audiodev_imp.h ================================================ /* $Id: audiodev_imp.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __AUDIODEV_IMP_H__ #define __AUDIODEV_IMP_H__ #include /** * @defgroup s8_audio_device_implementors_api Audio Device Implementors API * @ingroup audio_device_api * @brief API for audio device implementors * @{ */ typedef enum pjmedia_aud_dev_change_event { DEFAULT_INPUT_CHANGED = 1, DEFAULT_OUTPUT_CHANGED, DEVICE_LIST_CHANGED } pjmedia_aud_dev_change_event; typedef void (*pjmedia_aud_dev_change_callback)(pjmedia_aud_dev_change_event event); /** * Sound device factory operations. */ typedef struct pjmedia_aud_dev_factory_op { /** * Initialize the audio device factory. * * @param f The audio device factory. */ pj_status_t (*init)(pjmedia_aud_dev_factory *f); /** * Close this audio device factory and release all resources back to the * operating system. * * @param f The audio device factory. */ pj_status_t (*destroy)(pjmedia_aud_dev_factory *f); /** * Get the number of audio devices installed in the system. * * @param f The audio device factory. */ unsigned (*get_dev_count)(pjmedia_aud_dev_factory *f); /** * Get the audio device information and capabilities. * * @param f The audio device factory. * @param index Device index. * @param info The audio device information structure which will be * initialized by this function once it returns * successfully. */ pj_status_t (*get_dev_info)(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_dev_info *info); /** * Initialize the specified audio device parameter with the default * values for the specified device. * * @param f The audio device factory. * @param index Device index. * @param param The audio device parameter. */ pj_status_t (*default_param)(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_param *param); /** * Open the audio device and create audio stream. See * #pjmedia_aud_stream_create() */ pj_status_t (*create_stream)(pjmedia_aud_dev_factory *f, const pjmedia_aud_param *param, pjmedia_aud_rec_cb rec_cb, pjmedia_aud_play_cb play_cb, void *user_data, pjmedia_aud_stream **p_aud_strm); /** * Refresh the list of audio devices installed in the system. * * @param f The audio device factory. */ pj_status_t (*refresh)(pjmedia_aud_dev_factory *f); /** * Set audio device change callback * * @param f The audio device factory. * @param cb The audio device change callback. */ void (*set_dev_change_cb)(pjmedia_aud_dev_factory *f, pjmedia_aud_dev_change_callback cb); /** * Get default recording device index * * @param f The audio device factory. */ int (*get_default_rec_dev)(pjmedia_aud_dev_factory *f); /** * Get default playback device index * * @param f The audio device factory. */ int (*get_default_play_dev)(pjmedia_aud_dev_factory *f); } pjmedia_aud_dev_factory_op; /** * This structure describes an audio device factory. */ struct pjmedia_aud_dev_factory { /** Internal data to be initialized by audio subsystem. */ struct { /** Driver index */ unsigned drv_idx; } sys; /** Operations */ pjmedia_aud_dev_factory_op *op; }; /** * Sound stream operations. */ typedef struct pjmedia_aud_stream_op { /** * See #pjmedia_aud_stream_get_param() */ pj_status_t (*get_param)(pjmedia_aud_stream *strm, pjmedia_aud_param *param); /** * See #pjmedia_aud_stream_get_cap() */ pj_status_t (*get_cap)(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, void *value); /** * See #pjmedia_aud_stream_set_cap() */ pj_status_t (*set_cap)(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, const void *value); /** * See #pjmedia_aud_stream_start() */ pj_status_t (*start)(pjmedia_aud_stream *strm); /** * See #pjmedia_aud_stream_stop(). */ pj_status_t (*stop)(pjmedia_aud_stream *strm); /** * See #pjmedia_aud_stream_destroy(). */ pj_status_t (*destroy)(pjmedia_aud_stream *strm); } pjmedia_aud_stream_op; /** * This structure describes the audio device stream. */ struct pjmedia_aud_stream { /** Internal data to be initialized by audio subsystem */ struct { /** Driver index */ unsigned drv_idx; } sys; /** Operations */ pjmedia_aud_stream_op *op; }; /** * @} */ #endif /* __AUDIODEV_IMP_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-audiodev/config.h ================================================ /* $Id: config.h 4435 2013-03-11 06:32:58Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_AUDIODEV_CONFIG_H__ #define __PJMEDIA_AUDIODEV_CONFIG_H__ /** * @file config.h * @brief Audio config. */ #include #include PJ_BEGIN_DECL /** * @defgroup audio_device_api Audio Device API * @brief PJMEDIA audio device abstraction API. */ /** * @defgroup s1_audio_device_config Compile time configurations * @ingroup audio_device_api * @brief Compile time configurations * @{ */ /** * This setting controls whether native ALSA support should be included. */ #ifndef PJMEDIA_AUDIO_DEV_HAS_ALSA # define PJMEDIA_AUDIO_DEV_HAS_ALSA 0 #endif /** * This setting controls whether null audio support should be included. */ #ifndef PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO # define PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO 0 #endif /** * This setting controls whether coreaudio support should be included. */ #ifndef PJMEDIA_AUDIO_DEV_HAS_COREAUDIO # define PJMEDIA_AUDIO_DEV_HAS_COREAUDIO 0 #endif /** * This setting controls whether WMME support should be included. */ #ifndef PJMEDIA_AUDIO_DEV_HAS_WMME # define PJMEDIA_AUDIO_DEV_HAS_WMME 0 #endif /** * This setting controls the buffer length of audio device name. * * Default: 128 for Windows platforms, 64 for others */ #ifndef PJMEDIA_AUD_DEV_INFO_NAME_LEN # if (defined(PJ_WIN32) && PJ_WIN32!=0) || \ (defined(PJ_WIN64) && PJ_WIN64!=0) # define PJMEDIA_AUD_DEV_INFO_NAME_LEN 128 # else # define PJMEDIA_AUD_DEV_INFO_NAME_LEN 64 # endif #endif /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_AUDIODEV_CONFIG_H__ */ /* --------------------- DOCUMENTATION FOLLOWS --------------------------- */ /** * @addtogroup audio_device_api Audio Device API * @{ PJMEDIA Audio Device API is a cross-platform audio API appropriate for use with VoIP applications and many other types of audio streaming applications. The API abstracts many different audio API's on various platforms, such as: - PortAudio back-end for Win32, Windows Mobile, Linux, Unix, dan MacOS X. - native WMME audio for Win32 and Windows Mobile devices - native Symbian audio streaming/multimedia framework (MMF) implementation - native Nokia Audio Proxy Server (APS) implementation - null-audio implementation - and more to be implemented in the future The Audio Device API/library is an evolution from PJMEDIA @ref PJMED_SND and contains many enhancements: - Forward compatibility: \n The new API has been designed to be extensible, it will support new API's as well as new features that may be introduced in the future without breaking compatibility with applications that use this API as well as compatibility with existing device implementations. - Device capabilities: \n At the heart of the API is device capabilities management, where all possible audio capabilities of audio devices should be able to be handled in a generic manner. With this framework, new capabilities that may be discovered in the future can be handled in manner without breaking existing applications. - Built-in features: \n The device capabilities framework enables applications to use and control audio features built-in in the device, such as: - echo cancellation, - built-in codecs, - audio routing (e.g. to earpiece or loudspeaker), - volume control, - etc. - Codec support: \n Some audio devices such as Nokia/Symbian Audio Proxy Server (APS) and Nokia VoIP Audio Services (VAS) support built-in hardware audio codecs (e.g. G.729, iLBC, and AMR), and application can use the sound device in encoded mode to make use of these hardware codecs. - Multiple backends: \n The new API supports multiple audio backends (called factories or drivers in the code) to be active simultaneously, and audio backends may be added or removed during run-time. @section using Overview on using the API @subsection getting_started Getting started -# Configure the application's project settings.\n Add the following include: \code #include \endcode\n And add pjmedia-audiodev library to your application link specifications.\n -# Compile time settings.\n Use the compile time settings to enable or disable specific audio drivers. For more information, please see \ref s1_audio_device_config. -# API initialization and cleaning up.\n Before anything else, application must initialize the API by calling: \code pjmedia_aud_subsys_init(pf);\endcode\n And add this in the application cleanup sequence \code pjmedia_aud_subsys_shutdown();\endcode @subsection devices Working with devices -# The following code prints the list of audio devices detected in the system. \code int dev_count; pjmedia_aud_dev_index dev_idx; pj_status_t status; dev_count = pjmedia_aud_dev_count(); printf("Got %d audio devices\n", dev_count); for (dev_idx=0; dev_idx #include /** * @defgroup error_codes Error Codes * @ingroup audio_device_api * @brief Audio devive library specific error codes. * @{ */ PJ_BEGIN_DECL /** * Start of error code relative to PJ_ERRNO_START_USER. * This value is 420000. */ #define PJMEDIA_AUDIODEV_ERRNO_START \ (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*5) #define PJMEDIA_AUDIODEV_ERRNO_END \ (PJMEDIA_AUDIODEV_ERRNO_START + PJ_ERRNO_SPACE_SIZE - 1) /** * Mapping from Windows multimedia WaveIn error codes. */ #define PJMEDIA_AUDIODEV_WMME_IN_ERROR_START \ (PJMEDIA_AUDIODEV_ERRNO_START + 30000) #define PJMEDIA_AUDIODEV_WMME_IN_ERROR_END \ (PJMEDIA_AUDIODEV_WMME_IN_ERROR_START + 1000 - 1) /** * Convert WaveIn operation error codes to PJLIB error space. */ #define PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(err) \ ((int)PJMEDIA_AUDIODEV_WMME_IN_ERROR_START+err) /** * Mapping from Windows multimedia WaveOut error codes. */ #define PJMEDIA_AUDIODEV_WMME_OUT_ERROR_START \ (PJMEDIA_AUDIODEV_WMME_IN_ERROR_END + 1000) #define PJMEDIA_AUDIODEV_WMME_OUT_ERROR_END \ (PJMEDIA_AUDIODEV_WMME_OUT_ERROR_START + 1000) /** * Convert WaveOut operation error codes to PJLIB error space. */ #define PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(err) \ ((int)PJMEDIA_AUDIODEV_WMME_OUT_ERROR_START+err) /** * Mapping from CoreAudio error codes to pjmedia error space. */ #define PJMEDIA_AUDIODEV_COREAUDIO_ERRNO_START \ (PJMEDIA_AUDIODEV_ERRNO_START+20000) #define PJMEDIA_AUDIODEV_COREAUDIO_ERRNO_END \ (PJMEDIA_AUDIODEV_COREAUDIO_ERRNO_START + 20000 -1) /** * Convert CoreAudio error code to PJLIB error code. * CoreAudio error code range: 0 >= err >= -10000 */ #define PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(err) \ ((int)PJMEDIA_AUDIODEV_COREAUDIO_ERRNO_START-err) /** * Mapping from BDIMAD error codes to pjmedia error space. */ #define PJMEDIA_AUDIODEV_BDIMAD_ERROR_START \ (PJMEDIA_AUDIODEV_ERRNO_START + 40000) #define PJMEDIA_AUDIODEV_BDIMAD_ERROR_END \ (PJMEDIA_AUDIODEV_BDIMAD_ERROR_START + 2000 - 1) /** * Convert BDIMAD error codes to PJLIB error space. */ #define PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(err) \ ((int)PJMEDIA_AUDIODEV_BDIMAD_ERROR_START+err) /************************************************************ * Audio Device API error codes ***********************************************************/ /** * @hideinitializer * General/unknown error. */ #define PJMEDIA_EAUD_ERR (PJMEDIA_AUDIODEV_ERRNO_START+1) /* 420001 */ /** * @hideinitializer * Unknown error from audio driver */ #define PJMEDIA_EAUD_SYSERR (PJMEDIA_AUDIODEV_ERRNO_START+2) /* 420002 */ /** * @hideinitializer * Audio subsystem not initialized */ #define PJMEDIA_EAUD_INIT (PJMEDIA_AUDIODEV_ERRNO_START+3) /* 420003 */ /** * @hideinitializer * Invalid audio device */ #define PJMEDIA_EAUD_INVDEV (PJMEDIA_AUDIODEV_ERRNO_START+4) /* 420004 */ /** * @hideinitializer * Found no devices */ #define PJMEDIA_EAUD_NODEV (PJMEDIA_AUDIODEV_ERRNO_START+5) /* 420005 */ /** * @hideinitializer * Unable to find default device */ #define PJMEDIA_EAUD_NODEFDEV (PJMEDIA_AUDIODEV_ERRNO_START+6) /* 420006 */ /** * @hideinitializer * Device not ready */ #define PJMEDIA_EAUD_NOTREADY (PJMEDIA_AUDIODEV_ERRNO_START+7) /* 420007 */ /** * @hideinitializer * The audio capability is invalid or not supported */ #define PJMEDIA_EAUD_INVCAP (PJMEDIA_AUDIODEV_ERRNO_START+8) /* 420008 */ /** * @hideinitializer * The operation is invalid or not supported */ #define PJMEDIA_EAUD_INVOP (PJMEDIA_AUDIODEV_ERRNO_START+9) /* 420009 */ /** * @hideinitializer * Bad or invalid audio device format */ #define PJMEDIA_EAUD_BADFORMAT (PJMEDIA_AUDIODEV_ERRNO_START+10) /* 4200010 */ /** * @hideinitializer * Invalid audio device sample format */ #define PJMEDIA_EAUD_SAMPFORMAT (PJMEDIA_AUDIODEV_ERRNO_START+11) /* 4200011 */ /** * @hideinitializer * Bad latency setting */ #define PJMEDIA_EAUD_BADLATENCY (PJMEDIA_AUDIODEV_ERRNO_START+12) /* 4200012 */ /** * Get error message for the specified error code. Note that this * function is only able to decode PJMEDIA Audiodev specific error code. * Application should use pj_strerror(), which should be able to * decode all error codes belonging to all subsystems (e.g. pjlib, * pjmedia, pjsip, etc). * * @param status The error code. * @param buffer The buffer where to put the error message. * @param bufsize Size of the buffer. * * @return The error message as NULL terminated string, * wrapped with pj_str_t. */ PJ_DECL(pj_str_t) pjmedia_audiodev_strerror(pj_status_t status, char *buffer, pj_size_t bufsize); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_AUDIODEV_AUDIODEV_ERRNO_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/audio_codecs.h ================================================ /* $Id: audio_codecs.h 3666 2011-07-19 08:40:20Z nanang $ */ /* * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CODEC_ALL_CODECS_H__ #define __PJMEDIA_CODEC_ALL_CODECS_H__ /** * @file pjmedia-codec/all_codecs.h * @brief Helper function to register all codecs */ #include #include PJ_BEGIN_DECL /** * @defgroup PJMEDIA_CODEC_REGISTER_ALL Codec registration helper * @ingroup PJMEDIA_CODEC_CODECS * @brief Helper function to register all codecs * @{ * * Helper function to register all codecs that are implemented in * PJMEDIA-CODEC library. */ /** * Codec configuration. Call #pjmedia_audio_codec_config_default() to initialize * this structure with the default values. */ typedef struct pjmedia_audio_codec_config { /** Speex codec settings. See #pjmedia_codec_speex_init() for more info */ struct { unsigned option; /**< Bitmask of options. */ int quality; /**< Codec quality. */ int complexity; /**< Codec complexity. */ } speex; /** iLBC settings */ struct { unsigned mode; /**< iLBC mode. */ } ilbc; /** Passthrough */ struct { pjmedia_codec_passthrough_setting setting; /**< Passthrough */ } passthrough; } pjmedia_audio_codec_config; /** * Initialize pjmedia_audio_codec_config structure with default values. * * @param cfg The codec config to be initialized. */ PJ_DECL(void) pjmedia_audio_codec_config_default(pjmedia_audio_codec_config *cfg); /** * Register all known audio codecs implemented in PJMEDA-CODEC library to the * specified media endpoint. * * @param endpt The media endpoint. * @param c Optional codec configuration, or NULL to use default * values. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_codec_register_audio_codecs(pjmedia_endpt *endpt, const pjmedia_audio_codec_config *c); /** * @} PJMEDIA_CODEC_REGISTER_ALL */ PJ_END_DECL #endif /* __PJMEDIA_CODEC_ALL_CODECS_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/config.h ================================================ /* $Id: config.h 4331 2013-01-23 06:18:18Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CODEC_CONFIG_H__ #define __PJMEDIA_CODEC_CONFIG_H__ /** * @file config.h * @brief PJMEDIA-CODEC compile time settings */ /** * @defgroup pjmedia_codec_config PJMEDIA-CODEC Compile Time Settings * @ingroup PJMEDIA_CODEC * @brief Various compile time settings such as to enable/disable codecs * @{ */ #include /* * Include config_auto.h if autoconf is used (PJ_AUTOCONF is set) */ #if defined(PJ_AUTOCONF) # include #endif /** * Unless specified otherwise, L16 codec is included by default. */ #ifndef PJMEDIA_HAS_L16_CODEC # define PJMEDIA_HAS_L16_CODEC 1 #endif /** * Unless specified otherwise, GSM codec is included by default. */ #ifndef PJMEDIA_HAS_GSM_CODEC # define PJMEDIA_HAS_GSM_CODEC 1 #endif /** * Unless specified otherwise, Speex codec is included by default. */ #ifndef PJMEDIA_HAS_SPEEX_CODEC # define PJMEDIA_HAS_SPEEX_CODEC 1 #endif /** * Speex codec default complexity setting. */ #ifndef PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY # define PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY 2 #endif /** * Speex codec default quality setting. Please note that pjsua-lib may override * this setting via its codec quality setting (i.e PJSUA_DEFAULT_CODEC_QUALITY). */ #ifndef PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY # define PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY 8 #endif /** * Unless specified otherwise, iLBC codec is included by default. */ #ifndef PJMEDIA_HAS_ILBC_CODEC # define PJMEDIA_HAS_ILBC_CODEC 1 #endif /** * Unless specified otherwise, G.722 codec is included by default. */ #ifndef PJMEDIA_HAS_G722_CODEC # define PJMEDIA_HAS_G722_CODEC 1 #endif /** * Default G.722 codec encoder and decoder level adjustment. The G.722 * specifies that it uses 14 bit PCM for input and output, while PJMEDIA * normally uses 16 bit PCM, so the conversion is done by applying * level adjustment. If the value is non-zero, then PCM input samples to * the encoder will be shifted right by this value, and similarly PCM * output samples from the decoder will be shifted left by this value. * * This can be changed at run-time after initialization by calling * #pjmedia_codec_g722_set_pcm_shift(). * * Default: 2. */ #ifndef PJMEDIA_G722_DEFAULT_PCM_SHIFT # define PJMEDIA_G722_DEFAULT_PCM_SHIFT 2 #endif /** * Specifies whether G.722 PCM shifting should be stopped when clipping * detected in the decoder. Enabling this feature can be useful when * talking to G.722 implementation that uses 16 bit PCM for G.722 input/ * output (for any reason it seems to work) and the PCM shifting causes * audio clipping. * * See also #PJMEDIA_G722_DEFAULT_PCM_SHIFT. * * Default: enabled. */ #ifndef PJMEDIA_G722_STOP_PCM_SHIFT_ON_CLIPPING # define PJMEDIA_G722_STOP_PCM_SHIFT_ON_CLIPPING 1 #endif /** * Unless specified otherwise, opus codec is included by default. */ #ifndef PJMEDIA_HAS_OPUS_CODEC # define PJMEDIA_HAS_OPUS_CODEC 1 #endif /** * Enable Passthrough codecs. * * Default: 0 */ #ifndef PJMEDIA_HAS_PASSTHROUGH_CODECS # define PJMEDIA_HAS_PASSTHROUGH_CODECS 0 #endif /** * G.722.1 codec is disabled by default. */ #ifndef PJMEDIA_HAS_G7221_CODEC # define PJMEDIA_HAS_G7221_CODEC 0 #endif /** * Default G.722.1 codec encoder and decoder level adjustment. * If the value is non-zero, then PCM input samples to the encoder will * be shifted right by this value, and similarly PCM output samples from * the decoder will be shifted left by this value. * * This can be changed at run-time after initialization by calling * #pjmedia_codec_g7221_set_pcm_shift(). */ #ifndef PJMEDIA_G7221_DEFAULT_PCM_SHIFT # define PJMEDIA_G7221_DEFAULT_PCM_SHIFT 1 #endif /** * Enabling both G.722.1 codec implementations, internal PJMEDIA and IPP, * may cause problem in SDP, i.e: payload types duplications. So, let's * just trap such case here at compile time. * * Application can control which implementation to be used by manipulating * PJMEDIA_HAS_G7221_CODEC and PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1 in * config_site.h. */ #if (PJMEDIA_HAS_G7221_CODEC != 0) && (PJMEDIA_HAS_INTEL_IPP != 0) && \ (PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1 != 0) # error Only one G.722.1 implementation can be enabled at the same time. \ Please use PJMEDIA_HAS_G7221_CODEC and \ PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1 in your config_site.h \ to control which implementation to be used. #endif /** * Specify if FFMPEG codecs are available. * * Default: PJMEDIA_HAS_LIBAVCODEC */ #ifndef PJMEDIA_HAS_FFMPEG_CODEC # define PJMEDIA_HAS_FFMPEG_CODEC PJMEDIA_HAS_LIBAVCODEC #endif /** * Specify if FFMPEG video codecs are available. * * Default: PJMEDIA_HAS_FFMPEG_CODEC */ #ifndef PJMEDIA_HAS_FFMPEG_VID_CODEC # define PJMEDIA_HAS_FFMPEG_VID_CODEC PJMEDIA_HAS_FFMPEG_CODEC #endif /** * Enable FFMPEG H263+/H263-1998 codec. * * Default: 1 */ #ifndef PJMEDIA_HAS_FFMPEG_CODEC_H263P # define PJMEDIA_HAS_FFMPEG_CODEC_H263P PJMEDIA_HAS_FFMPEG_VID_CODEC #endif /** * Enable FFMPEG H264 codec (requires libx264). * * Default: 0 */ #ifndef PJMEDIA_HAS_FFMPEG_CODEC_H264 # define PJMEDIA_HAS_FFMPEG_CODEC_H264 PJMEDIA_HAS_FFMPEG_VID_CODEC #endif /** * Compile VPX support, unless explicitly disabled */ #ifndef PJMEDIA_HAS_VPX_CODEC # define PJMEDIA_HAS_VPX_CODEC PJMEDIA_HAS_LIBVPX #endif /** * @} */ #endif /* __PJMEDIA_CODEC_CONFIG_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/config_auto.h.in ================================================ /* $Id: config_auto.h.in 4331 2013-01-23 06:18:18Z ming $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CODEC_CONFIG_AUTO_H_ #define __PJMEDIA_CODEC_CONFIG_AUTO_H_ /** * @file config_auto.h * @brief PJMEDIA-CODEC configuration as set by autoconf script */ /* * Note: * The configuration in config_site.h overrides any other settings, * including the setting as detected by autoconf. */ /* GSM codec */ #ifndef PJMEDIA_HAS_GSM_CODEC #undef PJMEDIA_HAS_GSM_CODEC #endif /* Speex codec */ #ifndef PJMEDIA_HAS_SPEEX_CODEC #undef PJMEDIA_HAS_SPEEX_CODEC #endif /* iLBC codec */ #ifndef PJMEDIA_HAS_ILBC_CODEC #undef PJMEDIA_HAS_ILBC_CODEC #endif /* G722 codec */ #ifndef PJMEDIA_HAS_G722_CODEC #undef PJMEDIA_HAS_G722_CODEC #endif /* G7221 codec */ #ifndef PJMEDIA_HAS_G7221_CODEC #undef PJMEDIA_HAS_G7221_CODEC #endif #endif /* __PJMEDIA_CODEC_CONFIG_AUTO_H_ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/ffmpeg_vid_codecs.h ================================================ /* $Id: ffmpeg_vid_codecs.h 4049 2012-04-13 06:24:23Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CODECS_FFMPEG_VID_H__ #define __PJMEDIA_CODECS_FFMPEG_VID_H__ #include #include PJ_BEGIN_DECL /** * @defgroup PJMEDIA_CODEC_VID_FFMPEG FFmpeg Codecs * @ingroup PJMEDIA_CODEC_VID_CODECS * @{ */ /** * Initialize and register FFMPEG video codecs factory to pjmedia endpoint. * * @param mgr The video codec manager instance where this codec will * be registered to. Specify NULL to use default instance * (in that case, an instance of video codec manager must * have been created beforehand). * @param pf Pool factory. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_ffmpeg_vid_init(pjmedia_vid_codec_mgr *mgr, pj_pool_factory *pf); /** * Unregister FFMPEG video codecs factory from the video codec manager and * deinitialize the codecs library. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_ffmpeg_vid_deinit(void); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_CODECS_FFMPEG_VID_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/g722.h ================================================ /* $Id: g722.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CODEC_G722_H__ #define __PJMEDIA_CODEC_G722_H__ /** * @file pjmedia-codec/g722.h * @brief G.722 codec. */ #include /** * @defgroup PJMED_G722 G.722 Codec * @ingroup PJMEDIA_CODEC_CODECS * @brief Implementation of G.722 Codec * @{ * * This section describes functions to initialize and register G.722 codec * factory to the codec manager. After the codec factory has been registered, * application can use @ref PJMEDIA_CODEC API to manipulate the codec. * * The G.722 implementation uses 16-bit PCM with sampling rate 16000Hz and * 20ms frame length resulting in 64kbps bitrate. * * The G.722 codec implementation is provided as part of pjmedia-codec * library, and does not depend on external G.722 codec implementation. * * \section codec_setting Codec Settings * * \subsection general_setting General Settings * * General codec settings for this codec such as VAD and PLC can be * manipulated through the setting field in #pjmedia_codec_param. * Please see the documentation of #pjmedia_codec_param for more info. * * \subsection specific_setting Codec Specific Settings * * Currently none. */ PJ_BEGIN_DECL /** * Initialize and register G.722 codec factory to pjmedia endpoint. * * @param endpt The pjmedia endpoint. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_g722_init(pjmedia_endpt *endpt); /** * Unregister G.722 codec factory from pjmedia endpoint and cleanup * resources allocated by the factory. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_g722_deinit(void); /** * Set the G.722 codec encoder and decoder level adjustment. * If the value is non-zero, then PCM input samples to the encoder will * be shifted right by this value, and similarly PCM output samples from * the decoder will be shifted left by this value. * * Default value is PJMEDIA_G722_DEFAULT_PCM_SHIFT. * * @param val The value * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_g722_set_pcm_shift(unsigned val); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_CODEC_G722_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/g7221.h ================================================ /* $Id: g7221.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CODECS_G7221_H__ #define __PJMEDIA_CODECS_G7221_H__ /** * @file pjmedia-codec/g7221.h * @brief G722.1 codec. */ #include /** * @defgroup PJMED_G7221_CODEC G.722.1 Codec (Siren7/Siren14) * @ingroup PJMEDIA_CODEC_CODECS * @brief Implementation of G.722.1 codec * @{ * * G.722.1 licensed from Polycom
* G.722.1 Annex C licensed from Polycom * * This section describes functions to initialize and register G.722.1 codec * factory to the codec manager. After the codec factory has been registered, * application can use @ref PJMEDIA_CODEC API to manipulate the codec. * * PJMEDIA G722.1 codec implementation is based on ITU-T Recommendation * G.722.1 (05/2005) C fixed point implementation including its Annex C. * * G.722.1 is a low complexity codec that supports 7kHz and 14kHz audio * bandwidth working at bitrates ranging from 16kbps to 48kbps. It may be * used with speech or music inputs. * * * \section codec_setting Codec Settings * * \subsection general_setting General Settings * * General codec settings for this codec such as VAD and PLC can be * manipulated through the setting field in #pjmedia_codec_param. * Please see the documentation of #pjmedia_codec_param for more info. * * \subsection specific_setting Codec Specific Settings * * The following settings are applicable for this codec. * * \subsubsection bitrate Bitrate * * The codec implementation supports standard and non-standard bitrates. * Use #pjmedia_codec_g7221_set_mode() to enable or disable the bitrates. * * By default, only standard bitrates are enabled upon initialization: * - for 7kHz audio bandwidth (16kHz sampling rate): 24kbps and 32kbps, * - for 14kHz audio bandwidth (32kHz sampling rate): 24kbps, 32kbps, and * 48kbps. * * The usage of non-standard bitrates must follow these requirements: * - for 7kHz audio bandwidth (16kHz sampling rate): 16000 to 32000 bps, * multiplication of 400 * - for 14kHz audio bandwidth (32kHz sampling rate): 24000 to 48000 bps, * multiplication of 400 * * The bitrate is set via param.setting.dec_fmtp, if it does not * contain bitrate info, the codec will check param.info.avg_bps. * * \note * Currently only up to two non-standard modes can be enabled. * * \remark * There is a flaw in the codec manager as currently it could not * differentiate G.722.1 codecs by bitrates, hence invoking * #pjmedia_codec_mgr_set_default_param() may only affect a G.722.1 codec * with the highest priority (or first index found in codec enumeration * when they have same priority) and invoking * #pjmedia_codec_mgr_set_codec_priority() will set priority of all G.722.1 * codecs with sampling rate as specified. */ PJ_BEGIN_DECL /** * Initialize and register G.722.1 codec factory to pjmedia endpoint. * * @param endpt The pjmedia endpoint. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_g7221_init( pjmedia_endpt *endpt ); /** * Enable and disable G.722.1 mode. By default, the standard modes are * enabled upon initialization, i.e.: * - sampling rate 16kHz, bitrate 24kbps and 32kbps. * - sampling rate 32kHz, bitrate 24kbps, 32kbps, and 48kbps. * This function can also be used for enabling non-standard modes. * Note that currently only up to two non-standard modes can be enabled * at one time. * * @param sample_rate PCM sampling rate, in Hz, valid values are only * 16000 and 32000. * @param bitrate G722.1 bitrate, in bps, the valid values are * standard and non-standard bitrates as described * above. * @param enabled PJ_TRUE for enabling specified mode. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_g7221_set_mode(unsigned sample_rate, unsigned bitrate, pj_bool_t enabled); /** * Set the G.722.1 codec encoder and decoder level adjustment. * If the value is non-zero, then PCM input samples to the encoder will * be shifted right by this value, and similarly PCM output samples from * the decoder will be shifted left by this value. * * \note * This function is also applicable for G722.1 implementation with IPP * back-end. * * Default value is PJMEDIA_G7221_DEFAULT_PCM_SHIFT. * * @param val The value * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_g7221_set_pcm_shift(int val); /** * Unregister G.722.1 codecs factory from pjmedia endpoint. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_g7221_deinit(void); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_CODECS_G7221_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/g7221_sdp_match.h ================================================ /* $Id: g7221_sdp_match.h 3911 2011-12-15 06:45:23Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CODEC_G7221_SDP_MATCH_H__ #define __PJMEDIA_CODEC_G7221_SDP_MATCH_H__ /** * @file g7221_sdp_match.h * @brief Special SDP format match for G722.1. */ #include PJ_BEGIN_DECL /** * Match G.722.1 format in the SDP media offer and answer. This function * will match G.722.1 bitrate setting in the SDP format parameter of * offer and answer. * * @param pool The memory pool. * @param offer The SDP media offer. * @param o_fmt_idx Index of the G.722.1 format in the SDP media offer. * @param answer The SDP media answer. * @param a_fmt_idx Index of the G.722.1 format in the SDP media answer. * @param option The format matching option, see * #pjmedia_sdp_neg_fmt_match_flag. * * @return PJ_SUCCESS when the formats in offer and answer match. */ PJ_DECL(pj_status_t) pjmedia_codec_g7221_match_sdp( pj_pool_t *pool, pjmedia_sdp_media *offer, unsigned o_fmt_idx, pjmedia_sdp_media *answer, unsigned a_fmt_idx, unsigned option); PJ_END_DECL #endif /* __PJMEDIA_CODEC_G7221_SDP_MATCH_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/gsm.h ================================================ /* $Id: gsm.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CODEC_GSM_H__ #define __PJMEDIA_CODEC_GSM_H__ /** * @file pjmedia-codec/gsm.h * @brief GSM 06.10 codec. */ #include /** * @defgroup PJMED_GSM GSM 06.10 Codec * @ingroup PJMEDIA_CODEC_CODECS * @brief Implementation of GSM FR based on GSM 06.10 library * @{ * * This section describes functions to initialize and register GSM codec * factory to the codec manager. After the codec factory has been registered, * application can use @ref PJMEDIA_CODEC API to manipulate the codec. * * The GSM codec supports 16-bit PCM with sampling rate of 8000Hz resulting * in 13.2kbps bitrate. * * \section codec_setting Codec Settings * * \subsection general_setting General Settings * * General codec settings for this codec such as VAD and PLC can be * manipulated through the setting field in #pjmedia_codec_param. * Please see the documentation of #pjmedia_codec_param for more info. * * \subsection specific_setting Codec Specific Settings * * Currently none. */ PJ_BEGIN_DECL /** * Initialize and register GSM codec factory to pjmedia endpoint. * * @param endpt The pjmedia endpoint. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_gsm_init( pjmedia_endpt *endpt ); /** * Unregister GSM codec factory from pjmedia endpoint and deinitialize * the GSM codec library. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_gsm_deinit(void); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_CODEC_GSM_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/h263_packetizer.h ================================================ /* $Id: h263_packetizer.h 3715 2011-08-19 09:35:25Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_H263_PACKETIZER_H__ #define __PJMEDIA_H263_PACKETIZER_H__ /** * @file h263_packetizer.h * @brief Packetizes/unpacketizes H.263 bitstream into RTP payload. */ #include #include PJ_BEGIN_DECL /** * Opaque declaration for H.263 packetizer. */ typedef struct pjmedia_h263_packetizer pjmedia_h263_packetizer; /** * Enumeration of H.263 packetization modes. */ typedef enum { /** * H.263 RTP packetization using RFC 4629. */ PJMEDIA_H263_PACKETIZER_MODE_RFC4629, /** * H.263 RTP packetization using legacy RFC 2190. * This is currently not supported. */ PJMEDIA_H263_PACKETIZER_MODE_RFC2190, } pjmedia_h263_packetizer_mode; /** * H.263 packetizer configuration. */ typedef struct pjmedia_h263_packetizer_cfg { /** * Maximum payload length. * Default: PJMEDIA_MAX_MTU */ int mtu; /** * Packetization mode. * Default: PJMEDIA_H263_PACKETIZER_MODE_RFC4629 */ pjmedia_h263_packetizer_mode mode; } pjmedia_h263_packetizer_cfg; /** * Create H.263 packetizer. * * @param pool The memory pool. * @param cfg Packetizer settings, if NULL, default setting * will be used. * @param p_pktz Pointer to receive the packetizer. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_h263_packetizer_create( pj_pool_t *pool, const pjmedia_h263_packetizer_cfg *cfg, pjmedia_h263_packetizer **p_pktz); /** * Generate an RTP payload from a H.263 picture bitstream. Note that this * function will apply in-place processing, so the bitstream may be modified * during the packetization. * * @param pktz The packetizer. * @param bits The picture bitstream to be packetized. * @param bits_len The length of the bitstream. * @param bits_pos The bitstream offset to be packetized. * @param payload The output payload. * @param payload_len The output payload length. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_h263_packetize(pjmedia_h263_packetizer *pktz, pj_uint8_t *bits, pj_size_t bits_len, unsigned *bits_pos, const pj_uint8_t **payload, pj_size_t *payload_len); /** * Append an RTP payload to an H.263 picture bitstream. Note that in case of * noticing packet lost, application should keep calling this function with * payload pointer set to NULL, as the packetizer need to update its internal * state. * * @param pktz The packetizer. * @param payload The payload to be unpacketized. * @param payload_len The payload length. * @param bits The bitstream buffer. * @param bits_size The bitstream buffer size. * @param bits_pos The bitstream offset to put the unpacketized payload * in the bitstream, upon return, this will be updated * to the latest offset as a result of the unpacketized * payload. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_h263_unpacketize(pjmedia_h263_packetizer *pktz, const pj_uint8_t *payload, pj_size_t payload_len, pj_uint8_t *bits, pj_size_t bits_size, unsigned *bits_pos); PJ_END_DECL #endif /* __PJMEDIA_H263_PACKETIZER_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/h264_packetizer.h ================================================ /* $Id: h264_packetizer.h 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_H264_PACKETIZER_H__ #define __PJMEDIA_H264_PACKETIZER_H__ /** * @file h264_packetizer.h * @brief Packetizes H.264 bitstream into RTP payload and vice versa. */ #include PJ_BEGIN_DECL /** * Opaque declaration for H.264 packetizer. */ typedef struct pjmedia_h264_packetizer pjmedia_h264_packetizer; /** * Enumeration of H.264 packetization modes. */ typedef enum { /** * Single NAL unit packetization mode will only generate payloads * containing a complete single NAL unit packet. As H.264 NAL unit * size can be very large, this mode is usually not applicable for * network environments with MTU size limitation. */ PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL, /** * Non-interleaved packetization mode will generate payloads with the * following possible formats: * - single NAL unit packets, * - NAL units aggregation STAP-A packets, * - fragmented NAL unit FU-A packets. */ PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED, /** * Interleaved packetization mode will generate payloads with the * following possible formats: * - single NAL unit packets, * - NAL units aggregation STAP-A & STAP-B packets, * - fragmented NAL unit FU-A & FU-B packets. * This packetization mode is currently unsupported. */ PJMEDIA_H264_PACKETIZER_MODE_INTERLEAVED, } pjmedia_h264_packetizer_mode; /** * H.264 packetizer setting. */ typedef struct pjmedia_h264_packetizer_cfg { /** * Maximum payload length. * Default: PJMEDIA_MAX_MTU */ int mtu; /** * Packetization mode. * Default: PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED */ pjmedia_h264_packetizer_mode mode; } pjmedia_h264_packetizer_cfg; /** * Create H.264 packetizer. * * @param pool The memory pool. * @param cfg Packetizer settings, if NULL, default setting * will be used. * @param p_pktz Pointer to receive the packetizer. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_h264_packetizer_create( pj_pool_t *pool, const pjmedia_h264_packetizer_cfg *cfg, pjmedia_h264_packetizer **p_pktz); /** * Generate an RTP payload from a H.264 picture bitstream. Note that this * function will apply in-place processing, so the bitstream may be modified * during the packetization. * * @param pktz The packetizer. * @param bits The picture bitstream to be packetized. * @param bits_len The length of the bitstream. * @param bits_pos The bitstream offset to be packetized. * @param payload The output payload. * @param payload_len The output payload length. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_h264_packetize(pjmedia_h264_packetizer *pktz, pj_uint8_t *bits, pj_size_t bits_len, unsigned *bits_pos, const pj_uint8_t **payload, pj_size_t *payload_len); /** * Append an RTP payload to an H.264 picture bitstream. Note that in case of * noticing packet lost, application should keep calling this function with * payload pointer set to NULL, as the packetizer need to update its internal * state. * * @param pktz The packetizer. * @param payload The payload to be unpacketized. * @param payload_len The payload length. * @param bits The bitstream buffer. * @param bits_size The bitstream buffer size. * @param bits_pos The bitstream offset to put the unpacketized payload * in the bitstream, upon return, this will be updated * to the latest offset as a result of the unpacketized * payload. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_h264_unpacketize(pjmedia_h264_packetizer *pktz, const pj_uint8_t *payload, pj_size_t payload_len, pj_uint8_t *bits, pj_size_t bits_len, unsigned *bits_pos); PJ_END_DECL #endif /* __PJMEDIA_H264_PACKETIZER_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/ilbc.h ================================================ /* $Id: ilbc.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CODEC_ILBC_H__ #define __PJMEDIA_CODEC_ILBC_H__ /** * @file pjmedia-codec/ilbc.h * @brief iLBC codec. */ #include /** * @defgroup PJMED_ILBC iLBC Codec * @ingroup PJMEDIA_CODEC_CODECS * @brief Implementation of iLBC Codec * @{ * * This section describes functions to initialize and register iLBC codec * factory to the codec manager. After the codec factory has been registered, * application can use @ref PJMEDIA_CODEC API to manipulate the codec. * * The iLBC codec is developed by Global IP Solutions (GIPS), formerly * Global IP Sound. The iLBC offers low bitrate and graceful audio quality * degradation on frame losses. * * The iLBC codec supports 16-bit PCM audio signal with sampling rate of * 8000Hz operating at two modes: 20ms and 30ms frame length modes, resulting * in bitrates of 15.2kbps for 20ms mode and 13.33kbps for 30ms mode. * * * \section codec_setting Codec Settings * * \subsection general_setting General Settings * * General codec settings for this codec such as VAD and PLC can be * manipulated through the setting field in #pjmedia_codec_param. * Please see the documentation of #pjmedia_codec_param for more info. * * \subsection specific_setting Codec Specific Settings * * The following settings are applicable for this codec. * * \subsubsection mode Mode * * The default mode should be set upon initialization, see * #pjmedia_codec_ilbc_init(). After the codec is initialized, the default * mode can be modified using #pjmedia_codec_mgr_set_default_param(). * * In #pjmedia_codec_param, iLBC mode can be set by specifying SDP * format parameter "mode" in the SDP "a=fmtp" attribute for decoding * direction. Valid values are "20" and "30" (for 20ms and 30ms mode * respectively). * * Here is an example to set up #pjmedia_codec_param to use mode 20ms: * \code pjmedia_codec_param param; ... // setting iLBC mode in SDP param.setting.dec_fmtp.cnt = 1; param.setting.dec_fmtp.param[0].name = pj_str("mode"); param.setting.dec_fmtp.param[0].val = pj_str("20"); ... \endcode */ PJ_BEGIN_DECL /** * Initialize and register iLBC codec factory to pjmedia endpoint. * * @param endpt The pjmedia endpoint. * @param mode Default decoder mode to be used. Valid values are * 20 and 30 ms. Note that encoder mode follows the * setting advertised in the remote's SDP. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_ilbc_init( pjmedia_endpt *endpt, int mode ); /** * Unregister iLBC codec factory from pjmedia endpoint and deinitialize * the iLBC codec library. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_ilbc_deinit(void); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_CODEC_ILBC_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/openh264.h ================================================ /* $Id$ */ /* * Copyright (C) 2014 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CODEC_OPENH264_H__ #define __PJMEDIA_CODEC_OPENH264_H__ #include #include /** * @file pjmedia-codec/openh264.h * @brief Open H.264 codec */ PJ_BEGIN_DECL /** * @defgroup PJMEDIA_CODEC_OPENH264 Open H.264 Codec * @ingroup PJMEDIA_CODEC_VID_CODECS * @{ */ /** * Initialize and register OpenH264 codec factory. * * @param mgr The video codec manager instance where this codec will * be registered to. Specify NULL to use default instance * (in that case, an instance of video codec manager must * have been created beforehand). * @param pf Pool factory. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_openh264_vid_init(pjmedia_vid_codec_mgr *mgr, pj_pool_factory *pf); /** * Unregister OpenH264 video codecs factory from the video codec manager and * deinitialize the codec library. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_openh264_vid_deinit(void); /** * @} PJMEDIA_CODEC_OPENH264 */ PJ_END_DECL #endif /* __PJMEDIA_CODEC_OPENH264_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/opus.h ================================================ #ifndef __PJMEDIA_CODEC_OPUS_CODEC_H__ #define __PJMEDIA_CODEC_OPUS_CODEC_H__ /** * @file pj_opus.h * @brief OPUS codec. */ #include PJ_BEGIN_DECL PJ_DECL(pj_status_t) pjmedia_codec_opus_init( pjmedia_endpt *endpt); PJ_DECL(pj_status_t) pjmedia_codec_opus_deinit(void); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_CODEC_OPUS_CODEC_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/passthrough.h ================================================ /* $Id: passthrough.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CODECS_PASSTHROUGH_H__ #define __PJMEDIA_CODECS_PASSTHROUGH_H__ /** * @file pjmedia-codec/passthrough.h * @brief Passthrough codecs. */ #include /** * @defgroup PJMED_PASSTHROUGH_CODEC Passthrough Codecs * @ingroup PJMEDIA_CODEC_CODECS * @brief Implementation of passthrough codecs * @{ * * This section describes functions to initialize and register passthrough * codecs factory to the codec manager. After the codec factory has been * registered, application can use @ref PJMEDIA_CODEC API to manipulate * the codec. * * Passthrough codecs are codecs wrapper that does NOT perform encoding * or decoding, it just PACK and PARSE encoded audio data from/into RTP * payload. This will accomodate pjmedia ports which work with encoded * audio data, e.g: encoded audio files, sound device with capability * of playing/recording encoded audio data. * * This codec factory contains various codecs, i.e: G.729, iLBC, * AMR, and G.711. * * * \section pjmedia_codec_passthrough_g729 Passthrough G.729 * * G.729 supports 16-bit PCM audio signal with sampling rate 8000Hz, * frame length 10ms, and resulting in bitrate 8000bps. * * \subsection codec_setting Codec Settings * * General codec settings for this codec such as VAD and PLC can be * manipulated through the setting field in #pjmedia_codec_param. * Please see the documentation of #pjmedia_codec_param for more info. * * Note that G.729 VAD status should be signalled in SDP, see more * description below. * * \subsubsection annexb Annex B * * The capability of VAD/DTX is specified in Annex B. * * By default, Annex B is enabled. This default setting of Annex B can * be modified using #pjmedia_codec_mgr_set_default_param(). * * In #pjmedia_codec_param, Annex B is configured via VAD setting and * format parameter "annexb" in the SDP "a=fmtp" attribute in * decoding fmtp field. Valid values are "yes" and "no", * the implementation default is "yes". When this parameter is omitted * in the SDP, the value will be "yes" (RFC 4856 Section 2.1.9). * * Here is an example of modifying default setting of Annex B to * be disabled using #pjmedia_codec_mgr_set_default_param(): \code pjmedia_codec_param param; pjmedia_codec_mgr_get_default_param(.., ¶m); ... // Set VAD param.setting.vad = 0; // Set SDP format parameter param.setting.dec_fmtp.cnt = 1; param.setting.dec_fmtp.param[0].name = pj_str("annexb"); param.setting.dec_fmtp.param[0].val = pj_str("no"); ... pjmedia_codec_mgr_set_default_param(.., ¶m); \endcode * * \note * The difference of Annex B status in SDP offer/answer may be considered as * incompatible codec in SDP negotiation. * * * \section pjmedia_codec_passthrough_ilbc Passthrough iLBC * * The iLBC codec is developed by Global IP Solutions (GIPS), formerly * Global IP Sound. The iLBC offers low bitrate and graceful audio quality * degradation on frame losses. * * The iLBC codec supports 16-bit PCM audio signal with sampling rate of * 8000Hz operating at two modes: 20ms and 30ms frame length modes, resulting * in bitrates of 15.2kbps for 20ms mode and 13.33kbps for 30ms mode. * * \subsection codec_setting Codec Settings * * General codec settings for this codec such as VAD and PLC can be * manipulated through the setting field in #pjmedia_codec_param. * Please see the documentation of #pjmedia_codec_param for more info. * * \subsubsection mode Mode * * The default mode should be set upon initialization, see * #pjmedia_codec_passthrough_init2(). After the codec is initialized, the * default mode can be modified using #pjmedia_codec_mgr_set_default_param(). * * In #pjmedia_codec_param, iLBC mode can be set by specifying SDP * format parameter "mode" in the SDP "a=fmtp" attribute for decoding * direction. Valid values are "20" and "30" (for 20ms and 30ms mode * respectively). * * Here is an example to set up #pjmedia_codec_param to use mode 20ms: * \code pjmedia_codec_param param; ... // setting iLBC mode in SDP param.setting.dec_fmtp.cnt = 1; param.setting.dec_fmtp.param[0].name = pj_str("mode"); param.setting.dec_fmtp.param[0].val = pj_str("20"); ... \endcode * * * \section pjmedia_codec_passthrough_amr Passthrough AMR * * IPP AMR supports 16-bit PCM audio signal with sampling rate 8000Hz, * 20ms frame length and producing various bitrates that ranges from 4.75kbps * to 12.2kbps. * * \subsection codec_setting Codec Settings * * General codec settings for this codec such as VAD and PLC can be * manipulated through the setting field in #pjmedia_codec_param. * Please see the documentation of #pjmedia_codec_param for more info. * * \subsubsection bitrate Bitrate * * By default, encoding bitrate is 7400bps. This default setting can be * modified using #pjmedia_codec_mgr_set_default_param() by specifying * prefered AMR bitrate in field info::avg_bps of * #pjmedia_codec_param. Valid bitrates could be seen in * #pjmedia_codec_amrnb_bitrates. * * \subsubsection payload_format Payload Format * * There are two AMR payload format types, bandwidth-efficient and * octet-aligned. Default setting is using octet-aligned. This default payload * format can be modified using #pjmedia_codec_mgr_set_default_param(). * * In #pjmedia_codec_param, payload format can be set by specifying SDP * format parameters "octet-align" in the SDP "a=fmtp" attribute for * decoding direction. Valid values are "0" (for bandwidth efficient mode) * and "1" (for octet-aligned mode). * * \subsubsection mode_set Mode-Set * * Mode-set is used for restricting AMR modes in decoding direction. * * By default, no mode-set restriction applied. This default setting can be * be modified using #pjmedia_codec_mgr_set_default_param(). * * In #pjmedia_codec_param, mode-set could be specified via format parameters * "mode-set" in the SDP "a=fmtp" attribute for decoding direction. Valid * value is a comma separated list of modes from the set 0 - 7, e.g: * "4,5,6,7". When this parameter is omitted, no mode-set restrictions applied. * * Here is an example of modifying AMR default codec param: \code pjmedia_codec_param param; pjmedia_codec_mgr_get_default_param(.., ¶m); ... // set default encoding bitrate to the highest 12.2kbps param.info.avg_bps = 12200; // restrict decoding bitrate to 10.2kbps and 12.2kbps only param.setting.dec_fmtp.param[0].name = pj_str("mode-set"); param.setting.dec_fmtp.param[0].val = pj_str("6,7"); // also set to use bandwidth-efficient payload format param.setting.dec_fmtp.param[1].name = pj_str("octet-align"); param.setting.dec_fmtp.param[1].val = pj_str("0"); param.setting.dec_fmtp.cnt = 2; ... pjmedia_codec_mgr_set_default_param(.., ¶m); \endcode * * * \section pjmedia_codec_passthrough_g711 Passthrough G.711 * * The G.711 is an ultra low complexity codecs and in trade-off it results * in high bitrate, i.e: 64kbps for 16-bit PCM with sampling rate 8000Hz. * * The factory contains two main compression algorithms, PCMU/u-Law and * PCMA/A-Law. * * \subsection codec_setting Codec Settings * * General codec settings for this codec such as VAD and PLC can be * manipulated through the setting field in #pjmedia_codec_param. * Please see the documentation of #pjmedia_codec_param for more info. */ PJ_BEGIN_DECL /** * Codec passthrough configuration settings. */ typedef struct pjmedia_codec_passthrough_setting { unsigned fmt_cnt; /**< Number of encoding formats to be enabled. */ pjmedia_format *fmts; /**< Encoding formats to be enabled. */ unsigned ilbc_mode; /**< iLBC default mode. */ } pjmedia_codec_passthrough_setting; /** * Initialize and register passthrough codecs factory to pjmedia endpoint, * all supported encoding formats will be enabled. * * @param endpt The pjmedia endpoint. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_passthrough_init( pjmedia_endpt *endpt ); /** * Initialize and register passthrough codecs factory to pjmedia endpoint * with only specified encoding formats enabled. * * @param endpt The pjmedia endpoint. * @param setting The settings. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_passthrough_init2( pjmedia_endpt *endpt, const pjmedia_codec_passthrough_setting *setting); /** * Unregister passthrough codecs factory from pjmedia endpoint. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_passthrough_deinit(void); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_CODECS_PASSTHROUGH_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/speex.h ================================================ /* $Id: speex.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CODEC_SPEEX_H__ #define __PJMEDIA_CODEC_SPEEX_H__ /** * @file speex.h * @brief Speex codec header. */ #include /** * @defgroup PJMED_SPEEX Speex Codec Family * @ingroup PJMEDIA_CODEC_CODECS * @brief Implementation of Speex codecs (narrow/wide/ultrawide-band). * @{ * * This section describes functions to initialize and register speex codec * factory to the codec manager. After the codec factory has been registered, * application can use @ref PJMEDIA_CODEC API to manipulate the codec. * * The Speex codec uses multiple bit rates, and supports ultra-wideband * (32 kHz sampling rate), wideband (16 kHz sampling rate) and narrowband * (telephone quality, 8 kHz sampling rate) * * By default, the speex codec factory registers three Speex codecs: * "speex/8000" narrowband codec, "speex/16000" wideband codec, and * "speex/32000" ultra-wideband codec. This behavior can be changed by * specifying #pjmedia_speex_options flags during initialization. * * * \section codec_setting Codec Settings * * \subsection general_setting General Settings * * General codec settings for this codec such as VAD and PLC can be * manipulated through the setting field in #pjmedia_codec_param. * Please see the documentation of #pjmedia_codec_param for more info. * * \subsection specific_setting Codec Specific Settings * * The following settings are applicable for this codec. * * \subsubsection quality_vs_complexity Quality vs Complexity * * The Speex codec quality versus computational complexity and bandwidth * requirement can be adjusted by modifying the quality and complexity * setting, by calling #pjmedia_codec_speex_set_param(). The RFC 5574 * Section 5 shows the relationship between quality setting and the * resulting bitrate. * * The default setting of quality is specified in * #PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY. And the default setting of * complexity is specified in #PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY. */ PJ_BEGIN_DECL /** * Bitmask options to be passed during Speex codec factory initialization. */ enum pjmedia_speex_options { PJMEDIA_SPEEX_NO_NB = 1, /**< Disable narrowband mode. */ PJMEDIA_SPEEX_NO_WB = 2, /**< Disable wideband mode. */ PJMEDIA_SPEEX_NO_UWB = 4, /**< Disable ultra-wideband mode. */ }; /** * Initialize and register Speex codec factory to pjmedia endpoint. * * @param endpt The pjmedia endpoint. * @param options Bitmask of pjmedia_speex_options (default=0). * @param quality Specify encoding quality, or use -1 for default * (@see PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY). * @param complexity Specify encoding complexity , or use -1 for default * (@see PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY). * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_speex_init( pjmedia_endpt *endpt, unsigned options, int quality, int complexity ); /** * Initialize Speex codec factory using default settings and register to * pjmedia endpoint. * * @param endpt The pjmedia endpoint. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_speex_init_default(pjmedia_endpt *endpt); /** * Change the settings of Speex codec. * * @param clock_rate Clock rate of Speex mode to be set. * @param quality Specify encoding quality, or use -1 for default * (@see PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY). * @param complexity Specify encoding complexity , or use -1 for default * (@see PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY). * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_speex_set_param(unsigned clock_rate, int quality, int complexity); /** * Unregister Speex codec factory from pjmedia endpoint and deinitialize * the Speex codec library. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_speex_deinit(void); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_CODEC_SPEEX_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/types.h ================================================ /* $Id: types.h 4264 2012-09-24 06:58:16Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CODEC_TYPES_H__ #define __PJMEDIA_CODEC_TYPES_H__ /** * @file types.h * @brief PJMEDIA-CODEC types and constants */ #include #include /** * @defgroup pjmedia_codec_types PJMEDIA-CODEC Types and Constants * @ingroup PJMEDIA_CODEC * @brief Constants used by PJMEDIA-CODEC * @{ */ /** * These are the dynamic payload types that are used by audio codecs in * this library. Also see the header file for list * of static payload types. */ enum pjmedia_audio_pt { /* According to IANA specifications, dynamic payload types are to be in * the range 96-127 (inclusive). This enum is structured to place the * values of the payload types specified below into that range. * * PJMEDIA_RTP_PT_DYNAMIC is defined in . It is defined * to be 96. * * PJMEDIA_RTP_PT_TELEPHONE_EVENTS is defined in . * The default value is 96. */ #if PJMEDIA_RTP_PT_TELEPHONE_EVENTS PJMEDIA_RTP_PT_START = PJMEDIA_RTP_PT_TELEPHONE_EVENTS, #else PJMEDIA_RTP_PT_START = (PJMEDIA_RTP_PT_DYNAMIC-1), #endif PJMEDIA_RTP_PT_SPEEX_NB, /**< Speex narrowband/8KHz */ PJMEDIA_RTP_PT_SPEEX_WB, /**< Speex wideband/16KHz */ PJMEDIA_RTP_PT_SPEEX_UWB, /**< Speex 32KHz */ PJMEDIA_RTP_PT_SILK_NB, /**< SILK narrowband/8KHz */ PJMEDIA_RTP_PT_SILK_MB, /**< SILK mediumband/12KHz */ PJMEDIA_RTP_PT_SILK_WB, /**< SILK wideband/16KHz */ PJMEDIA_RTP_PT_SILK_SWB, /**< SILK 24KHz */ PJMEDIA_RTP_PT_ILBC, /**< iLBC (13.3/15.2Kbps) */ PJMEDIA_RTP_PT_AMR, /**< AMR (4.75 - 12.2Kbps) */ PJMEDIA_RTP_PT_AMRWB, /**< AMRWB (6.6 - 23.85Kbps)*/ PJMEDIA_RTP_PT_AMRWBE, /**< AMRWBE */ PJMEDIA_RTP_PT_OPUS, /**< OPUS */ PJMEDIA_RTP_PT_G726_16, /**< G726 @ 16Kbps */ PJMEDIA_RTP_PT_G726_24, /**< G726 @ 24Kbps */ PJMEDIA_RTP_PT_G726_32, /**< G726 @ 32Kbps */ PJMEDIA_RTP_PT_G726_40, /**< G726 @ 40Kbps */ PJMEDIA_RTP_PT_G722_1_16, /**< G722.1 (16Kbps) */ PJMEDIA_RTP_PT_G722_1_24, /**< G722.1 (24Kbps) */ PJMEDIA_RTP_PT_G722_1_32, /**< G722.1 (32Kbps) */ PJMEDIA_RTP_PT_G7221C_24, /**< G722.1 Annex C (24Kbps)*/ PJMEDIA_RTP_PT_G7221C_32, /**< G722.1 Annex C (32Kbps)*/ PJMEDIA_RTP_PT_G7221C_48, /**< G722.1 Annex C (48Kbps)*/ PJMEDIA_RTP_PT_G7221_RSV1, /**< G722.1 reserve */ PJMEDIA_RTP_PT_G7221_RSV2, /**< G722.1 reserve */ PJMEDIA_RTP_PT_L16_8KHZ_MONO, /**< L16 @ 8KHz, mono */ PJMEDIA_RTP_PT_L16_8KHZ_STEREO, /**< L16 @ 8KHz, stereo */ //PJMEDIA_RTP_PT_L16_11KHZ_MONO, /**< L16 @ 11KHz, mono */ //PJMEDIA_RTP_PT_L16_11KHZ_STEREO, /**< L16 @ 11KHz, stereo */ PJMEDIA_RTP_PT_L16_16KHZ_MONO, /**< L16 @ 16KHz, mono */ PJMEDIA_RTP_PT_L16_16KHZ_STEREO, /**< L16 @ 16KHz, stereo */ //PJMEDIA_RTP_PT_L16_22KHZ_MONO, /**< L16 @ 22KHz, mono */ //PJMEDIA_RTP_PT_L16_22KHZ_STEREO, /**< L16 @ 22KHz, stereo */ //PJMEDIA_RTP_PT_L16_32KHZ_MONO, /**< L16 @ 32KHz, mono */ //PJMEDIA_RTP_PT_L16_32KHZ_STEREO, /**< L16 @ 32KHz, stereo */ //PJMEDIA_RTP_PT_L16_48KHZ_MONO, /**< L16 @ 48KHz, mono */ //PJMEDIA_RTP_PT_L16_48KHZ_STEREO, /**< L16 @ 48KHz, stereo */ /* Caution! * Ensure the value of the last pt above is <= 127. */ }; /** * These are the dynamic payload types that are used by video codecs in * this library. */ enum pjmedia_video_pt { /* Video payload types */ PJMEDIA_RTP_PT_VID_START = (PJMEDIA_RTP_PT_DYNAMIC-1), PJMEDIA_RTP_PT_H263P, PJMEDIA_RTP_PT_H264, PJMEDIA_RTP_PT_H264_RSV1, PJMEDIA_RTP_PT_H264_RSV2, PJMEDIA_RTP_PT_H264_RSV3, PJMEDIA_RTP_PT_H264_RSV4, PJMEDIA_RTP_PT_VP8, /* Caution! * Ensure the value of the last pt above is <= 127. */ }; /** * @} */ #endif /* __PJMEDIA_CODEC_TYPES_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec/vpx.h ================================================ /** * Copyright (C) 2010 Regis Montoya (aka r3gis - www.r3gis.fr) * This file is part of pjsip_android. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __PJMEDIA_CODECS_VPX_H__ #define __PJMEDIA_CODECS_VPX_H__ #include #include PJ_BEGIN_DECL /** * @defgroup PJMEDIA_CODEC_VPX libvpx Codecs * @ingroup PJMEDIA_CODEC_VID_CODECS * @{ */ /** * Initialize and register libvpx video codecs factory to pjmedia endpoint. * * @param mgr The video codec manager instance where this codec will * be registered to. Specify NULL to use default instance * (in that case, an instance of video codec manager must * have been created beforehand). * @param pf Pool factory. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_vpx_init(pjmedia_vid_codec_mgr *mgr, pj_pool_factory *pf); /** * Unregister libvpx video codecs factory from the video codec manager and * deinitialize the codecs library. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_codec_vpx_deinit(void); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_CODECS_VPX_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-codec.h ================================================ /* $Id: pjmedia-codec.h 4331 2013-01-23 06:18:18Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_CODEC_PJMEDIA_CODEC_H__ #define __PJMEDIA_CODEC_PJMEDIA_CODEC_H__ /** * @file pjmedia-codec.h * @brief Include all codecs API in PJMEDIA-CODEC */ #include #include #include #include #include #include #include #include #include #include #include #endif /* __PJMEDIA_CODEC_PJMEDIA_CODEC_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-videodev/avi_dev.h ================================================ /* $Id: avi_dev.h 4016 2012-04-04 05:05:50Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_VIDEODEV_AVI_DEV_H__ #define __PJMEDIA_VIDEODEV_AVI_DEV_H__ /** * @file avi_dev.h * @brief AVI player virtual device */ #include #include PJ_BEGIN_DECL /** * @defgroup avi_dev AVI Player Virtual Device * @ingroup video_device_api * @brief AVI player virtual device * @{ * This describes a virtual capture device which takes its input from an AVI * file. */ /** * Settings for the AVI player virtual device. This param corresponds to * PJMEDIA_VID_DEV_CAP_AVI_PLAY capability of the video device/stream. */ typedef struct pjmedia_avi_dev_param { /** * Specifies the full path of the AVI file to be played. */ pj_str_t path; /** * If this setting is specified when setting the device, this specifies * the title to be assigned as the device name. If this setting not * specified, the filename part of the path will be used. */ pj_str_t title; /** * The underlying AVI streams created by the device. If the value is NULL, * that means the device has not been configured yet. Application can use * this field to retrieve the audio stream of the AVI. This setting is * "get"-only and will be ignored in "set capability" operation. */ pjmedia_avi_streams *avi_streams; } pjmedia_avi_dev_param; /** * Reset pjmedia_avi_dev_param with the default settings. This mostly will * reset all values to NULL or zero. * * @param p The parameter to be initialized. */ PJ_DECL(void) pjmedia_avi_dev_param_default(pjmedia_avi_dev_param *p); /** * Create a AVI device factory, and register it to the video device * subsystem. At least one factory needs to be created before an AVI * device can be allocated and used, and normally only one factory is * needed per application. * * @param pf Pool factory to be used. * @param max_dev Number of devices to be reserved. * @param p_ret Pointer to return the factory instance, to be * used when allocating a virtual device. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_avi_dev_create_factory( pj_pool_factory *pf, unsigned max_dev, pjmedia_vid_dev_factory **p_ret); /** * Allocate one device ID to be used to play the specified AVI file in * the parameter. * * @param param The parameter, with at least the AVI file path * set. * @param p_id Optional pointer to receive device ID to play * the file. * * @return PJ_SUCCESS or the appropriate error code. * */ PJ_DECL(pj_status_t) pjmedia_avi_dev_alloc(pjmedia_vid_dev_factory *f, pjmedia_avi_dev_param *param, pjmedia_vid_dev_index *p_id); /** * Retrieve the parameters set for the virtual device. * * @param id Device ID. * @param prm Structure to receive the settings. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_avi_dev_get_param(pjmedia_vid_dev_index id, pjmedia_avi_dev_param *param); /** * Free the resources associated with the virtual device. * * @param id The device ID. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pjmedia_avi_dev_free(pjmedia_vid_dev_index id); /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_VIDEODEV_AVI_DEV_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-videodev/config.h ================================================ /* $Id: config.h 4414 2013-03-05 08:21:02Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_VIDEODEV_CONFIG_H__ #define __PJMEDIA_VIDEODEV_CONFIG_H__ /** * @file config.h * @brief Video config. */ #include #include PJ_BEGIN_DECL /** * @defgroup video_device_api Video Device API * @brief PJMEDIA video device abstraction API. */ /** * @defgroup s1_video_device_config Compile time configurations * @ingroup video_device_api * @brief Compile time configurations * @{ */ /** * This setting controls the maximum number of formats that can be * supported by a video device. * * Default: 64 */ #ifndef PJMEDIA_VID_DEV_INFO_FMT_CNT # define PJMEDIA_VID_DEV_INFO_FMT_CNT 64 #endif #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) /** * This setting controls the maximum number of supported video device drivers. * * Default: 8 */ #ifndef PJMEDIA_VID_DEV_MAX_DRIVERS # define PJMEDIA_VID_DEV_MAX_DRIVERS 8 #endif /** * This setting controls the maximum number of supported video devices. * * Default: 16 */ #ifndef PJMEDIA_VID_DEV_MAX_DEVS # define PJMEDIA_VID_DEV_MAX_DEVS 16 #endif /** * This setting controls whether AVFoundation support should be included. * * Default: 0 (or detected by configure) */ #ifndef PJMEDIA_VIDEO_DEV_HAS_AVF # define PJMEDIA_VIDEO_DEV_HAS_AVF 0 #endif /** * This setting controls whether Direct Show support should be included. * * Default: 0 (unfinished) */ #ifndef PJMEDIA_VIDEO_DEV_HAS_DSHOW # define PJMEDIA_VIDEO_DEV_HAS_DSHOW 0 //PJ_WIN32 #endif /** * This setting controls whether colorbar source support should be included. * * Default: 1 */ #ifndef PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC # define PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC 1 #endif /** * Video4Linux2 * * Default: 0 (or detected by configure) */ #ifndef PJMEDIA_VIDEO_DEV_HAS_V4L2 # define PJMEDIA_VIDEO_DEV_HAS_V4L2 0 #endif /** * Enable support for AVI player virtual capture device. * * Default: 1 */ #ifndef PJMEDIA_VIDEO_DEV_HAS_AVI # define PJMEDIA_VIDEO_DEV_HAS_AVI 1 #endif /** * Enable support for frame buffer render device. * * Default: 1 */ #ifndef PJMEDIA_VIDEO_DEV_HAS_FB # define PJMEDIA_VIDEO_DEV_HAS_FB 1 #endif #endif /* defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) */ /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_VIDEODEV_CONFIG_H__ */ /* --------------------- DOCUMENTATION FOLLOWS --------------------------- */ /** * @addtogroup video_device_api Video Device API * @{ PJMEDIA Video Device API is a cross-platform video API appropriate for use with VoIP applications and many other types of video streaming applications. The API abstracts many different video API's on various platforms, such as: - native Direct Show video for Win32 and Windows Mobile devices - null-video implementation - and more to be implemented in the future The Video Device API/library is an evolution from PJMEDIA @ref PJMED_SND and contains many enhancements: - Forward compatibility: \n The new API has been designed to be extensible, it will support new API's as well as new features that may be introduced in the future without breaking compatibility with applications that use this API as well as compatibility with existing device implementations. - Device capabilities: \n At the heart of the API is device capabilities management, where all possible video capabilities of video devices should be able to be handled in a generic manner. With this framework, new capabilities that may be discovered in the future can be handled in manner without breaking existing applications. - Built-in features: \n The device capabilities framework enables applications to use and control video features built-in in the device, such as: - built-in formats, - etc. - Codec support: \n Some video devices support built-in hardware video codecs, and application can use the video device in encoded mode to make use of these hardware codecs. - Multiple backends: \n The new API supports multiple video backends (called factories or drivers in the code) to be active simultaneously, and video backends may be added or removed during run-time. */ /** * @} */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-videodev/errno.h ================================================ /* $Id: errno.h 3715 2011-08-19 09:35:25Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_VIDEODEV_VIDEODEV_ERRNO_H__ #define __PJMEDIA_VIDEODEV_VIDEODEV_ERRNO_H__ /** * @file errno.h Error Codes * @brief Videodev specific error codes. */ #include #include /** * @defgroup error_codes Error Codes * @ingroup video_device_api * @brief Video device library specific error codes. * @{ */ PJ_BEGIN_DECL /** * Start of error code relative to PJ_ERRNO_START_USER. * This value is 520000. */ #define PJMEDIA_VIDEODEV_ERRNO_START \ (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*7) #define PJMEDIA_VIDEODEV_ERRNO_END \ (PJMEDIA_VIDEODEV_ERRNO_START + PJ_ERRNO_SPACE_SIZE - 1) /************************************************************ * Video Device API error codes ***********************************************************/ /** * @hideinitializer * General/unknown error. */ #define PJMEDIA_EVID_ERR (PJMEDIA_VIDEODEV_ERRNO_START+1) /* 520001 */ /** * @hideinitializer * Unknown error from video driver */ #define PJMEDIA_EVID_SYSERR (PJMEDIA_VIDEODEV_ERRNO_START+2) /* 520002 */ /** * @hideinitializer * Video subsystem not initialized */ #define PJMEDIA_EVID_INIT (PJMEDIA_VIDEODEV_ERRNO_START+3) /* 520003 */ /** * @hideinitializer * Invalid video device */ #define PJMEDIA_EVID_INVDEV (PJMEDIA_VIDEODEV_ERRNO_START+4) /* 520004 */ /** * @hideinitializer * Found no devices */ #define PJMEDIA_EVID_NODEV (PJMEDIA_VIDEODEV_ERRNO_START+5) /* 520005 */ /** * @hideinitializer * Unable to find default device */ #define PJMEDIA_EVID_NODEFDEV (PJMEDIA_VIDEODEV_ERRNO_START+6) /* 520006 */ /** * @hideinitializer * Device not ready */ #define PJMEDIA_EVID_NOTREADY (PJMEDIA_VIDEODEV_ERRNO_START+7) /* 520007 */ /** * @hideinitializer * The video capability is invalid or not supported */ #define PJMEDIA_EVID_INVCAP (PJMEDIA_VIDEODEV_ERRNO_START+8) /* 520008 */ /** * @hideinitializer * The operation is invalid or not supported */ #define PJMEDIA_EVID_INVOP (PJMEDIA_VIDEODEV_ERRNO_START+9) /* 520009 */ /** * @hideinitializer * Bad or invalid video device format */ #define PJMEDIA_EVID_BADFORMAT (PJMEDIA_VIDEODEV_ERRNO_START+10) /* 520010 */ /** * @hideinitializer * Invalid video device sample format */ #define PJMEDIA_EVID_SAMPFORMAT (PJMEDIA_VIDEODEV_ERRNO_START+11) /* 520011 */ /** * @hideinitializer * Bad latency setting */ #define PJMEDIA_EVID_BADLATENCY (PJMEDIA_VIDEODEV_ERRNO_START+12) /* 520012 */ /** * @hideinitializer * Bad/unsupported video size */ #define PJMEDIA_EVID_BADSIZE (PJMEDIA_VIDEODEV_ERRNO_START+13) /* 520013 */ /** * Get error message for the specified error code. Note that this * function is only able to decode PJMEDIA Videodev specific error code. * Application should use pj_strerror(), which should be able to * decode all error codes belonging to all subsystems (e.g. pjlib, * pjmedia, pjsip, etc). * * @param status The error code. * @param buffer The buffer where to put the error message. * @param bufsize Size of the buffer. * * @return The error message as NULL terminated string, * wrapped with pj_str_t. */ PJ_DECL(pj_str_t) pjmedia_videodev_strerror(pj_status_t status, char *buffer, pj_size_t bufsize); PJ_END_DECL /** * @} */ #endif /* __PJMEDIA_VIDEODEV_VIDEODEV_ERRNO_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-videodev/fb_dev.h ================================================ /* $Id$ */ /* * Copyright (C) 2014-present AG Projects * Copyright (C) 2013-2014 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef PJMEDIA_VIDEODEV_FB_DEV_H__ #define PJMEDIA_VIDEODEV_FB_DEV_H__ #include typedef void (*pjmedia_vid_dev_fb_frame_cb)(const pjmedia_frame *frame, const pjmedia_rect_size size, void *user_data); pj_status_t pjmedia_vid_dev_fb_set_callback(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_fb_frame_cb cb, void *user_data); #endif /* PJMEDIA_VIDEODEV_FB_DEV_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-videodev/videodev.h ================================================ /* $Id: videodev.h 4167 2012-06-15 08:13:43Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_VIDEODEV_VIDEODEV_H__ #define __PJMEDIA_VIDEODEV_VIDEODEV_H__ /** * @file videodev.h * @brief Video device API. */ #include #include #include #include #include #include PJ_BEGIN_DECL /** * @defgroup video_device_reference Video Device API Reference * @ingroup video_device_api * @brief API Reference * @{ */ /** * Type for device index. */ typedef pj_int32_t pjmedia_vid_dev_index; /** * Enumeration of window handle type. */ typedef enum pjmedia_vid_dev_hwnd_type { /** * Type none. */ PJMEDIA_VID_DEV_HWND_TYPE_NONE, /** * Native window handle on Windows. */ PJMEDIA_VID_DEV_HWND_TYPE_WINDOWS, /** * Native view on iOS. */ PJMEDIA_VID_DEV_HWND_TYPE_IOS, /** * Native window handle on Android. */ PJMEDIA_VID_DEV_HWND_TYPE_ANDROID } pjmedia_vid_dev_hwnd_type; /** * Type for window handle. */ typedef struct pjmedia_vid_dev_hwnd { /** * The window handle type. */ pjmedia_vid_dev_hwnd_type type; /** * The window handle. */ union { struct { void *hwnd; /**< HWND */ } win; struct { void *window; /**< Window */ void *display; /**< Display */ } x11; struct { void *window; /**< Window */ } cocoa; struct { void *window; /**< Window */ } ios; struct { void *window; /**< Native window */ } android; void *window; } info; } pjmedia_vid_dev_hwnd; /** * Parameter for switching device with PJMEDIA_VID_DEV_CAP_SWITCH capability. * Initialize this with pjmedia_vid_dev_switch_param_default() */ typedef struct pjmedia_vid_dev_switch_param { /** * Target device ID to switch to. Once the switching is successful, the * video stream will use this device and the old device will be closed. */ pjmedia_vid_dev_index target_id; } pjmedia_vid_dev_switch_param; /** * Enumeration of window flags. */ typedef enum pjmedia_vid_dev_wnd_flag { /** * Window with border. */ PJMEDIA_VID_DEV_WND_BORDER = 1, /** * Window can be resized. */ PJMEDIA_VID_DEV_WND_RESIZABLE = 2 } pjmedia_vid_dev_wnd_flag; /** * Device index constants. */ enum pjmedia_vid_dev_std_index { /** * Constant to denote default capture device */ PJMEDIA_VID_DEFAULT_CAPTURE_DEV = -1, /** * Constant to denote default render device */ PJMEDIA_VID_DEFAULT_RENDER_DEV = -2, /** * Constant to denote invalid device index. */ PJMEDIA_VID_INVALID_DEV = -3 }; /** * This enumeration identifies various video device capabilities. These video * capabilities indicates what features are supported by the underlying * video device implementation. * * Applications get these capabilities in the #pjmedia_vid_dev_info structure. * * Application can also set the specific features/capabilities when opening * the video stream by setting the \a flags member of #pjmedia_vid_dev_param * structure. * * Once video stream is running, application can also retrieve or set some * specific video capability, by using #pjmedia_vid_dev_stream_get_cap() and * #pjmedia_vid_dev_stream_set_cap() and specifying the desired capability. The * value of the capability is specified as pointer, and application needs to * supply the pointer with the correct value, according to the documentation * of each of the capability. */ typedef enum pjmedia_vid_dev_cap { /** * Support for video formats. The value of this capability * is represented by #pjmedia_format structure. */ PJMEDIA_VID_DEV_CAP_FORMAT = 1, /** * Support for video input scaling */ PJMEDIA_VID_DEV_CAP_INPUT_SCALE = 2, /** * Support for returning the native window handle of the video window. * For renderer, this means the window handle of the renderer window, * while for capture, this means the window handle of the native preview, * only if the device supports PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW * capability. * * The value of this capability is pointer to pjmedia_vid_dev_hwnd * structure. */ PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW = 4, /** * Support for resizing video output. This capability SHOULD be * implemented by renderer, to alter the video output dimension on the fly. * Value is pjmedia_rect_size. */ PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE = 8, /** * Support for setting the video window's position. * Value is pjmedia_coord specifying the window's new coordinate. */ PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION = 16, /** * Support for setting the video output's visibility. * The value of this capability is a pj_bool_t containing boolean * PJ_TRUE or PJ_FALSE. */ PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE = 32, /** * Support for native preview capability in capture devices. Value is * pj_bool_t. With native preview, capture device can be instructed to * show or hide a preview window showing video directly from the camera * by setting this capability to PJ_TRUE or PJ_FALSE. Once the preview * is started, application may use PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW * capability to query the video window. * * The value of this capability is a pj_bool_t containing boolean * PJ_TRUE or PJ_FALSE. */ PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW = 64, /** * Support for changing video orientation. For a renderer device, * changing video orientation in will potentially affect the size of * render window, i.e: width and height swap. For a capture device, * the video will be rotated but the size of the video frame * will stay the same, so the video may be resized or stretched. * * The value of this capability is pjmedia_orient. */ PJMEDIA_VID_DEV_CAP_ORIENTATION = 128, /** * Support for fast switching to another device. A video stream with this * capability allows replacing of its underlying device with another * device, saving the user from opening a new video stream and gets a much * faster and smoother switching action. * * Note that even when this capability is supported by a device, it may * not be able to switch to arbitrary device. Application must always * check the return value of the operation to verify that switching has * occurred. * * This capability is currently write-only (i.e. set-only). * * The value of this capability is pointer to pjmedia_vid_dev_switch_param * structure. */ PJMEDIA_VID_DEV_CAP_SWITCH = 256, /** * Support for setting the output video window's flags. * The value of this capability is a bitmask combination of * #pjmedia_vid_dev_wnd_flag. */ PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS = 512, /** * End of standard capability */ PJMEDIA_VID_DEV_CAP_MAX = 16384 } pjmedia_vid_dev_cap; /** * Device information structure returned by #pjmedia_vid_dev_get_info(). */ typedef struct pjmedia_vid_dev_info { /** The device ID */ pjmedia_vid_dev_index id; /** The device name */ char name[64]; /** The underlying driver name */ char driver[32]; /** * The supported direction of the video device, i.e. whether it supports * capture only, render only, or both. */ pjmedia_dir dir; /** * Specify whether the device supports callback. Devices that implement * "active interface" will actively call the callbacks to give or ask for * video frames. If the device doesn't support callback, application * must actively request or give video frames from/to the device by using * pjmedia_vid_dev_stream_get_frame()/pjmedia_vid_dev_stream_put_frame(). */ pj_bool_t has_callback; /** Device capabilities, as bitmask combination of #pjmedia_vid_dev_cap */ unsigned caps; /** Number of video formats supported by this device */ unsigned fmt_cnt; /** * Array of supported video formats. Some fields in each supported video * format may be set to zero or of "unknown" value, to indicate that the * value is unknown or should be ignored. When these value are not set * to zero, it indicates that the exact format combination is being used. */ pjmedia_format fmt[PJMEDIA_VID_DEV_INFO_FMT_CNT]; } pjmedia_vid_dev_info; /** Forward declaration for pjmedia_vid_dev_stream */ typedef struct pjmedia_vid_dev_stream pjmedia_vid_dev_stream; typedef struct pjmedia_vid_dev_cb { /** * This callback is called by capturer stream when it has captured the * whole packet worth of video samples. * * @param stream The video stream. * @param user_data User data associated with the stream. * @param frame Captured frame. * * @return Returning non-PJ_SUCCESS will cause the video * stream to stop */ pj_status_t (*capture_cb)(pjmedia_vid_dev_stream *stream, void *user_data, pjmedia_frame *frame); /** * This callback is called by renderer stream when it needs additional * data to be rendered by the device. Application must fill in the whole * of output buffer with video samples. * * The frame argument contains the following values: * - timestamp Rendering timestamp, in samples. * - buf Buffer to be filled out by application. * - size The size requested in bytes, which will be equal * to the size of one whole packet. * * @param stream The video stream. * @param user_data User data associated with the stream. * @param frame Video frame, which buffer is to be filled in by * the application. * * @return Returning non-PJ_SUCCESS will cause the video * stream to stop */ pj_status_t (*render_cb)(pjmedia_vid_dev_stream *stream, void *user_data, pjmedia_frame *frame); } pjmedia_vid_dev_cb; /** * This structure specifies the parameters to open the video stream. */ typedef struct pjmedia_vid_dev_param { /** * The video direction. This setting is mandatory. */ pjmedia_dir dir; /** * The video capture device ID. This setting is mandatory if the video * direction includes input/capture direction. */ pjmedia_vid_dev_index cap_id; /** * The video render device ID. This setting is mandatory if the video * direction includes output/render direction. */ pjmedia_vid_dev_index rend_id; /** * Video clock rate. This setting is mandatory if the video * direction includes input/capture direction */ unsigned clock_rate; /** * Video frame rate. This setting is mandatory if the video * direction includes input/capture direction */ // pjmedia_ratio frame_rate; /** * This flags specifies which of the optional settings are valid in this * structure. The flags is bitmask combination of pjmedia_vid_dev_cap. */ unsigned flags; /** * Set the video format. This setting is mandatory. */ pjmedia_format fmt; /** * Window for the renderer to display the video. This setting is optional, * and will only be used if PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW is set in * the flags. */ pjmedia_vid_dev_hwnd window; /** * Video display size. This setting is optional, and will only be used * if PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE is set in the flags. */ pjmedia_rect_size disp_size; /** * Video window position. This setting is optional, and will only be used * if PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION is set in the flags. */ pjmedia_coord window_pos; /** * Video window's visibility. This setting is optional, and will only be * used if PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE is set in the flags. */ pj_bool_t window_hide; /** * Enable built-in preview. This setting is optional and is only used * if PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW capability is supported and * set in the flags. */ pj_bool_t native_preview; /** * Video orientation. This setting is optional and is only used if * PJMEDIA_VID_DEV_CAP_ORIENTATION capability is supported and is * set in the flags. */ pjmedia_orient orient; /** * Video window flags. This setting is optional, and will only be used * if PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS is set in the flags. */ unsigned window_flags; } pjmedia_vid_dev_param; /** Forward declaration for video device factory */ typedef struct pjmedia_vid_dev_factory pjmedia_vid_dev_factory; /* typedef for factory creation function */ typedef pjmedia_vid_dev_factory* (*pjmedia_vid_dev_factory_create_func_ptr)(pj_pool_factory*); /** * Initialize pjmedia_vid_dev_switch_param. * * @param p Parameter to be initialized. */ PJ_INLINE(void) pjmedia_vid_dev_switch_param_default(pjmedia_vid_dev_switch_param *p) { pj_bzero(p, sizeof(*p)); p->target_id = PJMEDIA_VID_INVALID_DEV; } /** * Get string info for the specified capability. * * @param cap The capability ID. * @param p_desc Optional pointer which will be filled with longer * description about the capability. * * @return Capability name. */ PJ_DECL(const char*) pjmedia_vid_dev_cap_name(pjmedia_vid_dev_cap cap, const char **p_desc); /** * Set a capability field value in #pjmedia_vid_dev_param structure. This will * also set the flags field for the specified capability in the structure. * * @param param The structure. * @param cap The video capability which value is to be set. * @param pval Pointer to value. Please see the type of value to * be supplied in the pjmedia_vid_dev_cap documentation. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_param_set_cap(pjmedia_vid_dev_param *param, pjmedia_vid_dev_cap cap, const void *pval); /** * Get a capability field value from #pjmedia_vid_dev_param structure. This * function will return PJMEDIA_EVID_INVCAP error if the flag for that * capability is not set in the flags field in the structure. * * @param param The structure. * @param cap The video capability which value is to be retrieved. * @param pval Pointer to value. Please see the type of value to * be supplied in the pjmedia_vid_dev_cap documentation. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_param_get_cap(const pjmedia_vid_dev_param *param, pjmedia_vid_dev_cap cap, void *pval); /** * Initialize the video device subsystem. This will register all supported * video device factories to the video device subsystem. This function may be * called more than once, but each call to this function must have the * corresponding #pjmedia_vid_dev_subsys_shutdown() call. * * @param pf The pool factory. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_subsys_init(pj_pool_factory *pf); /** * Get the pool factory registered to the video device subsystem. * * @return The pool factory. */ PJ_DECL(pj_pool_factory*) pjmedia_vid_dev_subsys_get_pool_factory(void); /** * Shutdown the video device subsystem. This will destroy all video device * factories registered in the video device subsystem. Note that currently * opened video streams may or may not be closed, depending on the * implementation of the video device factories. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_subsys_shutdown(void); /** * Register a supported video device factory to the video device subsystem. * Application can either register a function to create the factory, or * an instance of an already created factory. * * This function can only be called after calling * #pjmedia_vid_dev_subsys_init(). * * @param vdf The factory creation function. Either vdf or factory * argument must be specified. * @param factory Factory instance. Either vdf or factory * argument must be specified. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_register_factory(pjmedia_vid_dev_factory_create_func_ptr vdf, pjmedia_vid_dev_factory *factory); /** * Unregister a video device factory from the video device subsystem. This * function can only be called after calling #pjmedia_vid_dev_subsys_init(). * Devices from this factory will be unlisted. If a device from this factory * is currently in use, then the behavior is undefined. * * @param vdf The video device factory. Either vdf or factory argument * must be specified. * @param factory The factory instance. Either vdf or factory argument * must be specified. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_unregister_factory(pjmedia_vid_dev_factory_create_func_ptr vdf, pjmedia_vid_dev_factory *factory); /** * Refresh the list of video devices installed in the system. This function * will only refresh the list of videoo device so all active video streams will * be unaffected. After refreshing the device list, application MUST make sure * to update all index references to video devices (i.e. all variables of type * pjmedia_vid_dev_index) before calling any function that accepts video device * index as its parameter. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_refresh(void); /** * Get the number of video devices installed in the system. * * @return The number of video devices installed in the system. */ PJ_DECL(unsigned) pjmedia_vid_dev_count(void); /** * Get device information. * * @param id The video device ID. * @param info The device information which will be filled in by this * function once it returns successfully. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_get_info(pjmedia_vid_dev_index id, pjmedia_vid_dev_info *info); /** * Lookup device index based on the driver and device name. * * @param drv_name The driver name. * @param dev_name The device name. * @param id Pointer to store the returned device ID. * * @return PJ_SUCCESS if the device can be found. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_lookup(const char *drv_name, const char *dev_name, pjmedia_vid_dev_index *id); /** * Initialize the video device parameters with default values for the * specified device. * * @param id The video device ID. * @param param The video device parameters which will be initialized * by this function once it returns successfully. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_default_param(pj_pool_t *pool, pjmedia_vid_dev_index id, pjmedia_vid_dev_param *param); /** * Open video stream object using the specified parameters. If stream is * created successfully, this function will return PJ_SUCCESS and the * stream pointer will be returned in the p_strm argument. * * The opened stream may have been opened with different size and fps * than the requested values in the \a param argument. Application should * check the actual size and fps that the stream was opened with by inspecting * the values in the \a param argument and see if they have changed. Also * if the device ID in the \a param specifies default device, it may be * replaced with the actual device ID upon return. * * @param param On input, it specifies the video device parameters * to be used for the stream. On output, this will be * set to the actual video device parameters used to * open the stream. * @param cb Pointer to structure containing video stream * callbacks. * @param user_data Arbitrary user data, which will be given back in the * callbacks. * @param p_strm Pointer to receive the video stream. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_create( pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_strm); /** * Get the running parameters for the specified video stream. * * @param strm The video stream. * @param param Video stream parameters to be filled in by this * function once it returns successfully. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_get_param( pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_param *param); /** * Get the value of a specific capability of the video stream. * * @param strm The video stream. * @param cap The video capability which value is to be retrieved. * @param value Pointer to value to be filled in by this function * once it returns successfully. Please see the type * of value to be supplied in the pjmedia_vid_dev_cap * documentation. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_get_cap( pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, void *value); /** * Set the value of a specific capability of the video stream. * * @param strm The video stream. * @param cap The video capability which value is to be set. * @param value Pointer to value. Please see the type of value to * be supplied in the pjmedia_vid_dev_cap documentation. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_set_cap( pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, const void *value); /** * Start the stream. * * @param strm The video stream. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_start( pjmedia_vid_dev_stream *strm); /** * Query whether the stream has been started. * * @param strm The video stream * * @return PJ_TRUE if the video stream has been started. */ PJ_DECL(pj_bool_t) pjmedia_vid_dev_stream_is_running(pjmedia_vid_dev_stream *strm); /** * Request one frame from the stream. Application needs to call this function * periodically only if the stream doesn't support "active interface", i.e. * the pjmedia_vid_dev_info.has_callback member is PJ_FALSE. * * @param strm The video stream. * @param frame The video frame to be filled by the device. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_get_frame( pjmedia_vid_dev_stream *strm, pjmedia_frame *frame); /** * Put one frame to the stream. Application needs to call this function * periodically only if the stream doesn't support "active interface", i.e. * the pjmedia_vid_dev_info.has_callback member is PJ_FALSE. * * @param strm The video stream. * @param frame The video frame to put to the device. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_put_frame( pjmedia_vid_dev_stream *strm, const pjmedia_frame *frame); /** * Stop the stream. * * @param strm The video stream. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_stop( pjmedia_vid_dev_stream *strm); /** * Destroy the stream. * * @param strm The video stream. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_destroy( pjmedia_vid_dev_stream *strm); /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_VIDEODEV_VIDEODEV_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia-videodev/videodev_imp.h ================================================ /* $Id: videodev_imp.h 4016 2012-04-04 05:05:50Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __VIDEODEV_IMP_H__ #define __VIDEODEV_IMP_H__ #include /** * @defgroup s8_video_device_implementors_api Video Device Implementors API * @ingroup video_device_api * @brief API for video device implementors * @{ */ /** * Video device factory operations. */ typedef struct pjmedia_vid_dev_factory_op { /** * Initialize the video device factory. * * @param f The video device factory. */ pj_status_t (*init)(pjmedia_vid_dev_factory *f); /** * Close this video device factory and release all resources back to the * operating system. * * @param f The video device factory. */ pj_status_t (*destroy)(pjmedia_vid_dev_factory *f); /** * Get the number of video devices installed in the system. * * @param f The video device factory. */ unsigned (*get_dev_count)(pjmedia_vid_dev_factory *f); /** * Get the video device information and capabilities. * * @param f The video device factory. * @param index Device index. * @param info The video device information structure which will be * initialized by this function once it returns * successfully. */ pj_status_t (*get_dev_info)(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info); /** * Initialize the specified video device parameter with the default * values for the specified device. * * @param f The video device factory. * @param index Device index. * @param param The video device parameter. */ pj_status_t (*default_param)(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param); /** * Open the video device and create video stream. See * #pjmedia_vid_dev_stream_create() */ pj_status_t (*create_stream)(pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm); /** * Refresh the list of video devices installed in the system. * * @param f The video device factory. */ pj_status_t (*refresh)(pjmedia_vid_dev_factory *f); } pjmedia_vid_dev_factory_op; /** * This structure describes a video device factory. */ struct pjmedia_vid_dev_factory { /** Internal data to be initialized by video subsystem. */ struct { /** Driver index */ unsigned drv_idx; } sys; /** Operations */ pjmedia_vid_dev_factory_op *op; }; /** * Video stream operations. */ typedef struct pjmedia_vid_dev_stream_op { /** * See #pjmedia_vid_dev_stream_get_param() */ pj_status_t (*get_param)(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_param *param); /** * See #pjmedia_vid_dev_stream_get_cap() */ pj_status_t (*get_cap)(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, void *value); /** * See #pjmedia_vid_dev_stream_set_cap() */ pj_status_t (*set_cap)(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, const void *value); /** * See #pjmedia_vid_dev_stream_start() */ pj_status_t (*start)(pjmedia_vid_dev_stream *strm); /** * See #pjmedia_vid_dev_stream_get_frame() */ pj_status_t (*get_frame)(pjmedia_vid_dev_stream *strm, pjmedia_frame *frame); /** * See #pjmedia_vid_dev_stream_put_frame() */ pj_status_t (*put_frame)(pjmedia_vid_dev_stream *strm, const pjmedia_frame *frame); /** * See #pjmedia_vid_dev_stream_stop(). */ pj_status_t (*stop)(pjmedia_vid_dev_stream *strm); /** * See #pjmedia_vid_dev_stream_destroy(). */ pj_status_t (*destroy)(pjmedia_vid_dev_stream *strm); } pjmedia_vid_dev_stream_op; /** * This structure describes the video device stream. */ struct pjmedia_vid_dev_stream { /** Internal data to be initialized by video subsystem */ struct { /** Driver index */ unsigned drv_idx; /** Has it been started? */ pj_bool_t is_running; } sys; /** Operations */ pjmedia_vid_dev_stream_op *op; }; /** * Internal API: return the factory instance and device index that's local * to the factory for a given device ID. * * @param id Device id. * @param p_f Out: factory instance * @param p_local_index Out: device index within the factory * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_vid_dev_get_local_index(pjmedia_vid_dev_index id, pjmedia_vid_dev_factory **p_f, unsigned *p_local_index); /** * Internal API: return the global device index given a factory instance and * a local device index. * * @param f Factory. * @param local_idx Local index. * @param pid Returned global index. * * @return PJ_SUCCESS on success. */ PJ_DEF(pj_status_t) pjmedia_vid_dev_get_global_index(const pjmedia_vid_dev_factory *f, unsigned local_idx, pjmedia_vid_dev_index *pid); /** * @} */ #endif /* __VIDEODEV_IMP_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia.h ================================================ /* $Id: pjmedia.h 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_H__ #define __PJMEDIA_H__ /** * @file pjmedia.h * @brief PJMEDIA main header file. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* __PJMEDIA_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia_audiodev.h ================================================ /* $Id: pjmedia_audiodev.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_AUDIODEV_H__ #define __PJMEDIA_AUDIODEV_H__ /** * @file pjmedia_audiodev.h * @brief PJMEDIA main header file. */ #include #include #endif /* __PJMEDIA_AUDIODEV_H__ */ ================================================ FILE: deps/pjsip/pjmedia/include/pjmedia_videodev.h ================================================ /* $Id: pjmedia_videodev.h 4016 2012-04-04 05:05:50Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_VIDEODEV_H__ #define __PJMEDIA_VIDEODEV_H__ /** * @file pjmedia_videodev.h * @brief PJMEDIA main header file. */ #include #include #include #include #endif /* __PJMEDIA_VIDEODEV_H__ */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/alaw_ulaw.c ================================================ /* * This source code is a product of Sun Microsystems, Inc. and is provided * for unrestricted use. Users may copy or modify this source code without * charge. * * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun source code is provided with no support and without any obligation on * the part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ #include #if !defined(PJMEDIA_HAS_ALAW_ULAW_TABLE) || PJMEDIA_HAS_ALAW_ULAW_TABLE==0 #ifdef _MSC_VER # pragma warning ( disable: 4244 ) /* Conversion from int to char etc */ #endif /* * g711.c * * u-law, A-law and linear PCM conversions. */ #define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ #define QUANT_MASK (0xf) /* Quantization field mask. */ #define NSEGS (8) /* Number of A-law segments. */ #define SEG_SHIFT (4) /* Left shift for segment number. */ #define SEG_MASK (0x70) /* Segment field mask. */ static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF}; /* copy from CCITT G.711 specifications */ static unsigned char _u2a[128] = { /* u- to A-law conversions */ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 29, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 46, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128}; static unsigned char _a2u[128] = { /* A- to u-law conversions */ 1, 3, 5, 7, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 48, 49, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}; static int search( int val, short *table, int size) { int i; for (i = 0; i < size; i++) { if (val <= *table++) return (i); } return (size); } /* * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law * * linear2alaw() accepts an 16-bit integer and encodes it as A-law data. * * Linear Input Code Compressed Code * ------------------------ --------------- * 0000000wxyza 000wxyz * 0000001wxyza 001wxyz * 000001wxyzab 010wxyz * 00001wxyzabc 011wxyz * 0001wxyzabcd 100wxyz * 001wxyzabcde 101wxyz * 01wxyzabcdef 110wxyz * 1wxyzabcdefg 111wxyz * * For further information see John C. Bellamy's Digital Telephony, 1982, * John Wiley & Sons, pps 98-111 and 472-476. */ PJ_DEF(pj_uint8_t) pjmedia_linear2alaw( int pcm_val) /* 2's complement (16-bit range) */ { int mask; int seg; unsigned char aval; if (pcm_val >= 0) { mask = 0xD5; /* sign (7th) bit = 1 */ } else { mask = 0x55; /* sign bit = 0 */ pcm_val = -pcm_val - 8; /* https://trac.pjsip.org/repos/ticket/1301 * Thank you K Johnson - Zetron - 27 May 2011 */ if (pcm_val < 0) pcm_val = 0; } /* Convert the scaled magnitude to segment number. */ seg = search(pcm_val, seg_end, 8); /* Combine the sign, segment, and quantization bits. */ if (seg >= 8) /* out of range, return maximum value. */ return (0x7F ^ mask); else { aval = seg << SEG_SHIFT; if (seg < 2) aval |= (pcm_val >> 4) & QUANT_MASK; else aval |= (pcm_val >> (seg + 3)) & QUANT_MASK; return (aval ^ mask); } } /* * alaw2linear() - Convert an A-law value to 16-bit linear PCM * */ PJ_DEF(int) pjmedia_alaw2linear( unsigned a_val) { int t; int seg; a_val ^= 0x55; t = (a_val & QUANT_MASK) << 4; seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; switch (seg) { case 0: t += 8; break; case 1: t += 0x108; break; default: t += 0x108; t <<= seg - 1; } return ((a_val & SIGN_BIT) ? t : -t); } #define BIAS (0x84) /* Bias for linear code. */ /* * linear2ulaw() - Convert a linear PCM value to u-law * * In order to simplify the encoding process, the original linear magnitude * is biased by adding 33 which shifts the encoding range from (0 - 8158) to * (33 - 8191). The result can be seen in the following encoding table: * * Biased Linear Input Code Compressed Code * ------------------------ --------------- * 00000001wxyza 000wxyz * 0000001wxyzab 001wxyz * 000001wxyzabc 010wxyz * 00001wxyzabcd 011wxyz * 0001wxyzabcde 100wxyz * 001wxyzabcdef 101wxyz * 01wxyzabcdefg 110wxyz * 1wxyzabcdefgh 111wxyz * * Each biased linear code has a leading 1 which identifies the segment * number. The value of the segment number is equal to 7 minus the number * of leading 0's. The quantization interval is directly available as the * four bits wxyz. * The trailing bits (a - h) are ignored. * * Ordinarily the complement of the resulting code word is used for * transmission, and so the code word is complemented before it is returned. * * For further information see John C. Bellamy's Digital Telephony, 1982, * John Wiley & Sons, pps 98-111 and 472-476. */ PJ_DEF(unsigned char) pjmedia_linear2ulaw( int pcm_val) /* 2's complement (16-bit range) */ { int mask; int seg; unsigned char uval; /* Get the sign and the magnitude of the value. */ if (pcm_val < 0) { pcm_val = BIAS - pcm_val; mask = 0x7F; } else { pcm_val += BIAS; mask = 0xFF; } /* Convert the scaled magnitude to segment number. */ seg = search(pcm_val, seg_end, 8); /* * Combine the sign, segment, quantization bits; * and complement the code word. */ if (seg >= 8) /* out of range, return maximum value. */ return (0x7F ^ mask); else { uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF); return (uval ^ mask); } } /* * ulaw2linear() - Convert a u-law value to 16-bit linear PCM * * First, a biased linear code is derived from the code word. An unbiased * output can then be obtained by subtracting 33 from the biased code. * * Note that this function expects to be passed the complement of the * original code word. This is in keeping with ISDN conventions. */ PJ_DEF(int) pjmedia_ulaw2linear( unsigned char u_val) { int t; /* Shortcut: when input is zero, output is zero * This will also make the VAD works harder. * -bennylp */ if (u_val == 0) return 0; /* Complement to obtain normal u-law value. */ u_val = ~u_val; /* * Extract and bias the quantization bits. Then * shift up by the segment number and subtract out the bias. */ t = ((u_val & QUANT_MASK) << 3) + BIAS; t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); } /* A-law to u-law conversion */ PJ_DEF(unsigned char) pjmedia_alaw2ulaw( unsigned char aval) { aval &= 0xff; return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) : (0x7F ^ _a2u[aval ^ 0x55])); } /* u-law to A-law conversion */ PJ_DEF(unsigned char) pjmedia_ulaw2alaw( unsigned char uval) { uval &= 0xff; return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) : (0x55 ^ (_u2a[0x7F ^ uval] - 1))); } #endif /* PJMEDIA_HAS_ALAW_ULAW_TABLE */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/alaw_ulaw_table.c ================================================ /* $Id: alaw_ulaw_table.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * The tables here and also the conversion ideas are contributed by * Toni Rutar . Many thanks! */ #include #if defined(PJMEDIA_HAS_ALAW_ULAW_TABLE) && PJMEDIA_HAS_ALAW_ULAW_TABLE!=0 const pj_uint8_t pjmedia_linear2ulaw_tab[16384] = { 0xff,0xfe,0xfe,0xfd,0xfd,0xfc,0xfc,0xfb, 0xfb,0xfa,0xfa,0xf9,0xf9,0xf8,0xf8,0xf7, 0xf7,0xf6,0xf6,0xf5,0xf5,0xf4,0xf4,0xf3, 0xf3,0xf2,0xf2,0xf1,0xf1,0xf0,0xf0,0xef, 0xef,0xef,0xef,0xee,0xee,0xee,0xee,0xed, 0xed,0xed,0xed,0xec,0xec,0xec,0xec,0xeb, 0xeb,0xeb,0xeb,0xea,0xea,0xea,0xea,0xe9, 0xe9,0xe9,0xe9,0xe8,0xe8,0xe8,0xe8,0xe7, 0xe7,0xe7,0xe7,0xe6,0xe6,0xe6,0xe6,0xe5, 0xe5,0xe5,0xe5,0xe4,0xe4,0xe4,0xe4,0xe3, 0xe3,0xe3,0xe3,0xe2,0xe2,0xe2,0xe2,0xe1, 0xe1,0xe1,0xe1,0xe0,0xe0,0xe0,0xe0,0xdf, 0xdf,0xdf,0xdf,0xdf,0xdf,0xdf,0xdf,0xde, 0xde,0xde,0xde,0xde,0xde,0xde,0xde,0xdd, 0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdc, 0xdc,0xdc,0xdc,0xdc,0xdc,0xdc,0xdc,0xdb, 0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xda, 0xda,0xda,0xda,0xda,0xda,0xda,0xda,0xd9, 0xd9,0xd9,0xd9,0xd9,0xd9,0xd9,0xd9,0xd8, 0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd8,0xd7, 0xd7,0xd7,0xd7,0xd7,0xd7,0xd7,0xd7,0xd6, 0xd6,0xd6,0xd6,0xd6,0xd6,0xd6,0xd6,0xd5, 0xd5,0xd5,0xd5,0xd5,0xd5,0xd5,0xd5,0xd4, 0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd4,0xd3, 0xd3,0xd3,0xd3,0xd3,0xd3,0xd3,0xd3,0xd2, 0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0xd1, 0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd0, 0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0xcf, 0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf, 0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xce, 0xce,0xce,0xce,0xce,0xce,0xce,0xce,0xce, 0xce,0xce,0xce,0xce,0xce,0xce,0xce,0xcd, 0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd, 0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcc, 0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc, 0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xcb, 0xcb,0xcb,0xcb,0xcb,0xcb,0xcb,0xcb,0xcb, 0xcb,0xcb,0xcb,0xcb,0xcb,0xcb,0xcb,0xca, 0xca,0xca,0xca,0xca,0xca,0xca,0xca,0xca, 0xca,0xca,0xca,0xca,0xca,0xca,0xca,0xc9, 0xc9,0xc9,0xc9,0xc9,0xc9,0xc9,0xc9,0xc9, 0xc9,0xc9,0xc9,0xc9,0xc9,0xc9,0xc9,0xc8, 0xc8,0xc8,0xc8,0xc8,0xc8,0xc8,0xc8,0xc8, 0xc8,0xc8,0xc8,0xc8,0xc8,0xc8,0xc8,0xc7, 0xc7,0xc7,0xc7,0xc7,0xc7,0xc7,0xc7,0xc7, 0xc7,0xc7,0xc7,0xc7,0xc7,0xc7,0xc7,0xc6, 0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6, 0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc5, 0xc5,0xc5,0xc5,0xc5,0xc5,0xc5,0xc5,0xc5, 0xc5,0xc5,0xc5,0xc5,0xc5,0xc5,0xc5,0xc4, 0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4, 0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc4,0xc3, 0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3, 0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc2, 0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2, 0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc1, 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1, 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc0, 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0, 0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xbf, 0xbf,0xbf,0xbf,0xbf,0xbf,0xbf,0xbf,0xbf, 0xbf,0xbf,0xbf,0xbf,0xbf,0xbf,0xbf,0xbf, 0xbf,0xbf,0xbf,0xbf,0xbf,0xbf,0xbf,0xbf, 0xbf,0xbf,0xbf,0xbf,0xbf,0xbf,0xbf,0xbe, 0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe, 0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe, 0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe, 0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0xbd, 0xbd,0xbd,0xbd,0xbd,0xbd,0xbd,0xbd,0xbd, 0xbd,0xbd,0xbd,0xbd,0xbd,0xbd,0xbd,0xbd, 0xbd,0xbd,0xbd,0xbd,0xbd,0xbd,0xbd,0xbd, 0xbd,0xbd,0xbd,0xbd,0xbd,0xbd,0xbd,0xbc, 0xbc,0xbc,0xbc,0xbc,0xbc,0xbc,0xbc,0xbc, 0xbc,0xbc,0xbc,0xbc,0xbc,0xbc,0xbc,0xbc, 0xbc,0xbc,0xbc,0xbc,0xbc,0xbc,0xbc,0xbc, 0xbc,0xbc,0xbc,0xbc,0xbc,0xbc,0xbc,0xbb, 0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb, 0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb, 0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb, 0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xba, 0xba,0xba,0xba,0xba,0xba,0xba,0xba,0xba, 0xba,0xba,0xba,0xba,0xba,0xba,0xba,0xba, 0xba,0xba,0xba,0xba,0xba,0xba,0xba,0xba, 0xba,0xba,0xba,0xba,0xba,0xba,0xba,0xb9, 0xb9,0xb9,0xb9,0xb9,0xb9,0xb9,0xb9,0xb9, 0xb9,0xb9,0xb9,0xb9,0xb9,0xb9,0xb9,0xb9, 0xb9,0xb9,0xb9,0xb9,0xb9,0xb9,0xb9,0xb9, 0xb9,0xb9,0xb9,0xb9,0xb9,0xb9,0xb9,0xb8, 0xb8,0xb8,0xb8,0xb8,0xb8,0xb8,0xb8,0xb8, 0xb8,0xb8,0xb8,0xb8,0xb8,0xb8,0xb8,0xb8, 0xb8,0xb8,0xb8,0xb8,0xb8,0xb8,0xb8,0xb8, 0xb8,0xb8,0xb8,0xb8,0xb8,0xb8,0xb8,0xb7, 0xb7,0xb7,0xb7,0xb7,0xb7,0xb7,0xb7,0xb7, 0xb7,0xb7,0xb7,0xb7,0xb7,0xb7,0xb7,0xb7, 0xb7,0xb7,0xb7,0xb7,0xb7,0xb7,0xb7,0xb7, 0xb7,0xb7,0xb7,0xb7,0xb7,0xb7,0xb7,0xb6, 0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb6, 0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb6, 0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb6, 0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb5, 0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5, 0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5, 0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5, 0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb4, 0xb4,0xb4,0xb4,0xb4,0xb4,0xb4,0xb4,0xb4, 0xb4,0xb4,0xb4,0xb4,0xb4,0xb4,0xb4,0xb4, 0xb4,0xb4,0xb4,0xb4,0xb4,0xb4,0xb4,0xb4, 0xb4,0xb4,0xb4,0xb4,0xb4,0xb4,0xb4,0xb3, 0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3, 0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3, 0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3, 0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb2, 0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2, 0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2, 0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2, 0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb1, 0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, 0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, 0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, 0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb0, 0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0, 0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0, 0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0, 0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xb0,0xaf, 0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf, 0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf, 0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf, 0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf, 0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf, 0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf, 0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf, 0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xae, 0xae,0xae,0xae,0xae,0xae,0xae,0xae,0xae, 0xae,0xae,0xae,0xae,0xae,0xae,0xae,0xae, 0xae,0xae,0xae,0xae,0xae,0xae,0xae,0xae, 0xae,0xae,0xae,0xae,0xae,0xae,0xae,0xae, 0xae,0xae,0xae,0xae,0xae,0xae,0xae,0xae, 0xae,0xae,0xae,0xae,0xae,0xae,0xae,0xae, 0xae,0xae,0xae,0xae,0xae,0xae,0xae,0xae, 0xae,0xae,0xae,0xae,0xae,0xae,0xae,0xad, 0xad,0xad,0xad,0xad,0xad,0xad,0xad,0xad, 0xad,0xad,0xad,0xad,0xad,0xad,0xad,0xad, 0xad,0xad,0xad,0xad,0xad,0xad,0xad,0xad, 0xad,0xad,0xad,0xad,0xad,0xad,0xad,0xad, 0xad,0xad,0xad,0xad,0xad,0xad,0xad,0xad, 0xad,0xad,0xad,0xad,0xad,0xad,0xad,0xad, 0xad,0xad,0xad,0xad,0xad,0xad,0xad,0xad, 0xad,0xad,0xad,0xad,0xad,0xad,0xad,0xac, 0xac,0xac,0xac,0xac,0xac,0xac,0xac,0xac, 0xac,0xac,0xac,0xac,0xac,0xac,0xac,0xac, 0xac,0xac,0xac,0xac,0xac,0xac,0xac,0xac, 0xac,0xac,0xac,0xac,0xac,0xac,0xac,0xac, 0xac,0xac,0xac,0xac,0xac,0xac,0xac,0xac, 0xac,0xac,0xac,0xac,0xac,0xac,0xac,0xac, 0xac,0xac,0xac,0xac,0xac,0xac,0xac,0xac, 0xac,0xac,0xac,0xac,0xac,0xac,0xac,0xab, 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab, 0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xaa, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xa9, 0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa9, 0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa9, 0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa9, 0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa9, 0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa9, 0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa9, 0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa9, 0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa9,0xa8, 0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8, 0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8, 0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8, 0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8, 0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8, 0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8, 0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8, 0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa7, 0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa7, 0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa7, 0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa7, 0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa7, 0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa7, 0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa7, 0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa7, 0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa7,0xa6, 0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa6, 0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa6, 0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa6, 0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa6, 0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa6, 0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa6, 0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa6, 0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa6,0xa5, 0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa5, 0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa5, 0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa5, 0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa5, 0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa5, 0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa5, 0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa5, 0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa5,0xa4, 0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4, 0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4, 0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4, 0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4, 0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4, 0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4, 0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4, 0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xa3, 0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa3, 0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa3, 0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa3, 0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa3, 0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa3, 0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa3, 0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa3, 0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa3,0xa2, 0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2, 0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2, 0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2, 0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2, 0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2, 0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2, 0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2, 0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa1, 0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa1, 0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa1, 0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa1, 0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa1, 0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa1, 0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa1, 0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa1, 0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa1,0xa0, 0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0, 0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0, 0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0, 0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0, 0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0, 0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0, 0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0, 0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f, 0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9f,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e, 0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9e,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d, 0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9d,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c, 0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9c,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b, 0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9b,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a, 0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x9a,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f, 0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8f,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e, 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, 0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c, 0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8c,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b, 0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8b,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a, 0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b, 0x1b,0x1b,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d, 0x1d,0x1d,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, 0x1f,0x1f,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b, 0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b, 0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b, 0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b, 0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b, 0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b, 0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b, 0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b,0x2b, 0x2b,0x2b,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c, 0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c, 0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c, 0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c, 0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c, 0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c, 0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c, 0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c,0x2c, 0x2c,0x2c,0x2d,0x2d,0x2d,0x2d,0x2d,0x2d, 0x2d,0x2d,0x2d,0x2d,0x2d,0x2d,0x2d,0x2d, 0x2d,0x2d,0x2d,0x2d,0x2d,0x2d,0x2d,0x2d, 0x2d,0x2d,0x2d,0x2d,0x2d,0x2d,0x2d,0x2d, 0x2d,0x2d,0x2d,0x2d,0x2d,0x2d,0x2d,0x2d, 0x2d,0x2d,0x2d,0x2d,0x2d,0x2d,0x2d,0x2d, 0x2d,0x2d,0x2d,0x2d,0x2d,0x2d,0x2d,0x2d, 0x2d,0x2d,0x2d,0x2d,0x2d,0x2d,0x2d,0x2d, 0x2d,0x2d,0x2e,0x2e,0x2e,0x2e,0x2e,0x2e, 0x2e,0x2e,0x2e,0x2e,0x2e,0x2e,0x2e,0x2e, 0x2e,0x2e,0x2e,0x2e,0x2e,0x2e,0x2e,0x2e, 0x2e,0x2e,0x2e,0x2e,0x2e,0x2e,0x2e,0x2e, 0x2e,0x2e,0x2e,0x2e,0x2e,0x2e,0x2e,0x2e, 0x2e,0x2e,0x2e,0x2e,0x2e,0x2e,0x2e,0x2e, 0x2e,0x2e,0x2e,0x2e,0x2e,0x2e,0x2e,0x2e, 0x2e,0x2e,0x2e,0x2e,0x2e,0x2e,0x2e,0x2e, 0x2e,0x2e,0x2f,0x2f,0x2f,0x2f,0x2f,0x2f, 0x2f,0x2f,0x2f,0x2f,0x2f,0x2f,0x2f,0x2f, 0x2f,0x2f,0x2f,0x2f,0x2f,0x2f,0x2f,0x2f, 0x2f,0x2f,0x2f,0x2f,0x2f,0x2f,0x2f,0x2f, 0x2f,0x2f,0x2f,0x2f,0x2f,0x2f,0x2f,0x2f, 0x2f,0x2f,0x2f,0x2f,0x2f,0x2f,0x2f,0x2f, 0x2f,0x2f,0x2f,0x2f,0x2f,0x2f,0x2f,0x2f, 0x2f,0x2f,0x2f,0x2f,0x2f,0x2f,0x2f,0x2f, 0x2f,0x2f,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a, 0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a, 0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a, 0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a, 0x3a,0x3a,0x3b,0x3b,0x3b,0x3b,0x3b,0x3b, 0x3b,0x3b,0x3b,0x3b,0x3b,0x3b,0x3b,0x3b, 0x3b,0x3b,0x3b,0x3b,0x3b,0x3b,0x3b,0x3b, 0x3b,0x3b,0x3b,0x3b,0x3b,0x3b,0x3b,0x3b, 0x3b,0x3b,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c, 0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c, 0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c, 0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c, 0x3c,0x3c,0x3d,0x3d,0x3d,0x3d,0x3d,0x3d, 0x3d,0x3d,0x3d,0x3d,0x3d,0x3d,0x3d,0x3d, 0x3d,0x3d,0x3d,0x3d,0x3d,0x3d,0x3d,0x3d, 0x3d,0x3d,0x3d,0x3d,0x3d,0x3d,0x3d,0x3d, 0x3d,0x3d,0x3e,0x3e,0x3e,0x3e,0x3e,0x3e, 0x3e,0x3e,0x3e,0x3e,0x3e,0x3e,0x3e,0x3e, 0x3e,0x3e,0x3e,0x3e,0x3e,0x3e,0x3e,0x3e, 0x3e,0x3e,0x3e,0x3e,0x3e,0x3e,0x3e,0x3e, 0x3e,0x3e,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f, 0x3f,0x3f,0x40,0x40,0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, 0x40,0x40,0x41,0x41,0x41,0x41,0x41,0x41, 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, 0x41,0x41,0x42,0x42,0x42,0x42,0x42,0x42, 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42, 0x42,0x42,0x43,0x43,0x43,0x43,0x43,0x43, 0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43, 0x43,0x43,0x44,0x44,0x44,0x44,0x44,0x44, 0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44, 0x44,0x44,0x45,0x45,0x45,0x45,0x45,0x45, 0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45, 0x45,0x45,0x46,0x46,0x46,0x46,0x46,0x46, 0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46, 0x46,0x46,0x47,0x47,0x47,0x47,0x47,0x47, 0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47, 0x47,0x47,0x48,0x48,0x48,0x48,0x48,0x48, 0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x48, 0x48,0x48,0x49,0x49,0x49,0x49,0x49,0x49, 0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49, 0x49,0x49,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a, 0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a, 0x4a,0x4a,0x4b,0x4b,0x4b,0x4b,0x4b,0x4b, 0x4b,0x4b,0x4b,0x4b,0x4b,0x4b,0x4b,0x4b, 0x4b,0x4b,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c, 0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c, 0x4c,0x4c,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d, 0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d, 0x4d,0x4d,0x4e,0x4e,0x4e,0x4e,0x4e,0x4e, 0x4e,0x4e,0x4e,0x4e,0x4e,0x4e,0x4e,0x4e, 0x4e,0x4e,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f, 0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f, 0x4f,0x4f,0x50,0x50,0x50,0x50,0x50,0x50, 0x50,0x50,0x51,0x51,0x51,0x51,0x51,0x51, 0x51,0x51,0x52,0x52,0x52,0x52,0x52,0x52, 0x52,0x52,0x53,0x53,0x53,0x53,0x53,0x53, 0x53,0x53,0x54,0x54,0x54,0x54,0x54,0x54, 0x54,0x54,0x55,0x55,0x55,0x55,0x55,0x55, 0x55,0x55,0x56,0x56,0x56,0x56,0x56,0x56, 0x56,0x56,0x57,0x57,0x57,0x57,0x57,0x57, 0x57,0x57,0x58,0x58,0x58,0x58,0x58,0x58, 0x58,0x58,0x59,0x59,0x59,0x59,0x59,0x59, 0x59,0x59,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a, 0x5a,0x5a,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b, 0x5b,0x5b,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c, 0x5c,0x5c,0x5d,0x5d,0x5d,0x5d,0x5d,0x5d, 0x5d,0x5d,0x5e,0x5e,0x5e,0x5e,0x5e,0x5e, 0x5e,0x5e,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f, 0x5f,0x5f,0x60,0x60,0x60,0x60,0x61,0x61, 0x61,0x61,0x62,0x62,0x62,0x62,0x63,0x63, 0x63,0x63,0x64,0x64,0x64,0x64,0x65,0x65, 0x65,0x65,0x66,0x66,0x66,0x66,0x67,0x67, 0x67,0x67,0x68,0x68,0x68,0x68,0x69,0x69, 0x69,0x69,0x6a,0x6a,0x6a,0x6a,0x6b,0x6b, 0x6b,0x6b,0x6c,0x6c,0x6c,0x6c,0x6d,0x6d, 0x6d,0x6d,0x6e,0x6e,0x6e,0x6e,0x6f,0x6f, 0x6f,0x6f,0x70,0x70,0x71,0x71,0x72,0x72, 0x73,0x73,0x74,0x74,0x75,0x75,0x76,0x76, 0x77,0x77,0x78,0x78,0x79,0x79,0x7a,0x7a, 0x7b,0x7b,0x7c,0x7c,0x7d,0x7d,0x7e,0x7e }; const pj_uint8_t pjmedia_linear2alaw_tab[16384] = { 0xD5,0xD5,0xD5,0xD5,0xD4,0xD4,0xD4,0xD4, 0xD7,0xD7,0xD7,0xD7,0xD6,0xD6,0xD6,0xD6, 0xD1,0xD1,0xD1,0xD1,0xD0,0xD0,0xD0,0xD0, 0xD3,0xD3,0xD3,0xD3,0xD2,0xD2,0xD2,0xD2, 0xDD,0xDD,0xDD,0xDD,0xDC,0xDC,0xDC,0xDC, 0xDF,0xDF,0xDF,0xDF,0xDE,0xDE,0xDE,0xDE, 0xD9,0xD9,0xD9,0xD9,0xD8,0xD8,0xD8,0xD8, 0xDB,0xDB,0xDB,0xDB,0xDA,0xDA,0xDA,0xDA, 0xC5,0xC5,0xC5,0xC5,0xC4,0xC4,0xC4,0xC4, 0xC7,0xC7,0xC7,0xC7,0xC6,0xC6,0xC6,0xC6, 0xC1,0xC1,0xC1,0xC1,0xC0,0xC0,0xC0,0xC0, 0xC3,0xC3,0xC3,0xC3,0xC2,0xC2,0xC2,0xC2, 0xCD,0xCD,0xCD,0xCD,0xCC,0xCC,0xCC,0xCC, 0xCF,0xCF,0xCF,0xCF,0xCE,0xCE,0xCE,0xCE, 0xC9,0xC9,0xC9,0xC9,0xC8,0xC8,0xC8,0xC8, 0xCB,0xCB,0xCB,0xCB,0xCA,0xCA,0xCA,0xCA, 0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5, 0xF4,0xF4,0xF4,0xF4,0xF4,0xF4,0xF4,0xF4, 0xF7,0xF7,0xF7,0xF7,0xF7,0xF7,0xF7,0xF7, 0xF6,0xF6,0xF6,0xF6,0xF6,0xF6,0xF6,0xF6, 0xF1,0xF1,0xF1,0xF1,0xF1,0xF1,0xF1,0xF1, 0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0, 0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3,0xF3, 0xF2,0xF2,0xF2,0xF2,0xF2,0xF2,0xF2,0xF2, 0xFD,0xFD,0xFD,0xFD,0xFD,0xFD,0xFD,0xFD, 0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE, 0xF9,0xF9,0xF9,0xF9,0xF9,0xF9,0xF9,0xF9, 0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0xF8, 0xFB,0xFB,0xFB,0xFB,0xFB,0xFB,0xFB,0xFB, 0xFA,0xFA,0xFA,0xFA,0xFA,0xFA,0xFA,0xFA, 0xE5,0xE5,0xE5,0xE5,0xE5,0xE5,0xE5,0xE5, 0xE5,0xE5,0xE5,0xE5,0xE5,0xE5,0xE5,0xE5, 0xE4,0xE4,0xE4,0xE4,0xE4,0xE4,0xE4,0xE4, 0xE4,0xE4,0xE4,0xE4,0xE4,0xE4,0xE4,0xE4, 0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7, 0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7, 0xE6,0xE6,0xE6,0xE6,0xE6,0xE6,0xE6,0xE6, 0xE6,0xE6,0xE6,0xE6,0xE6,0xE6,0xE6,0xE6, 0xE1,0xE1,0xE1,0xE1,0xE1,0xE1,0xE1,0xE1, 0xE1,0xE1,0xE1,0xE1,0xE1,0xE1,0xE1,0xE1, 0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0, 0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0, 0xE3,0xE3,0xE3,0xE3,0xE3,0xE3,0xE3,0xE3, 0xE3,0xE3,0xE3,0xE3,0xE3,0xE3,0xE3,0xE3, 0xE2,0xE2,0xE2,0xE2,0xE2,0xE2,0xE2,0xE2, 0xE2,0xE2,0xE2,0xE2,0xE2,0xE2,0xE2,0xE2, 0xED,0xED,0xED,0xED,0xED,0xED,0xED,0xED, 0xED,0xED,0xED,0xED,0xED,0xED,0xED,0xED, 0xEC,0xEC,0xEC,0xEC,0xEC,0xEC,0xEC,0xEC, 0xEC,0xEC,0xEC,0xEC,0xEC,0xEC,0xEC,0xEC, 0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF, 0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF,0xEF, 0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE, 0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE, 0xE9,0xE9,0xE9,0xE9,0xE9,0xE9,0xE9,0xE9, 0xE9,0xE9,0xE9,0xE9,0xE9,0xE9,0xE9,0xE9, 0xE8,0xE8,0xE8,0xE8,0xE8,0xE8,0xE8,0xE8, 0xE8,0xE8,0xE8,0xE8,0xE8,0xE8,0xE8,0xE8, 0xEB,0xEB,0xEB,0xEB,0xEB,0xEB,0xEB,0xEB, 0xEB,0xEB,0xEB,0xEB,0xEB,0xEB,0xEB,0xEB, 0xEA,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA, 0xEA,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x95,0x95,0x95,0x95,0x95,0x95,0x95,0x95, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x94,0x94,0x94,0x94,0x94,0x94,0x94,0x94, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x97, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x96,0x96,0x96,0x96,0x96,0x96,0x96,0x96, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x91,0x91,0x91,0x91,0x91,0x91,0x91,0x91, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x93,0x93,0x93,0x93,0x93,0x93,0x93,0x93, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92, 0x9D,0x9D,0x9D,0x9D,0x9D,0x9D,0x9D,0x9D, 0x9D,0x9D,0x9D,0x9D,0x9D,0x9D,0x9D,0x9D, 0x9D,0x9D,0x9D,0x9D,0x9D,0x9D,0x9D,0x9D, 0x9D,0x9D,0x9D,0x9D,0x9D,0x9D,0x9D,0x9D, 0x9C,0x9C,0x9C,0x9C,0x9C,0x9C,0x9C,0x9C, 0x9C,0x9C,0x9C,0x9C,0x9C,0x9C,0x9C,0x9C, 0x9C,0x9C,0x9C,0x9C,0x9C,0x9C,0x9C,0x9C, 0x9C,0x9C,0x9C,0x9C,0x9C,0x9C,0x9C,0x9C, 0x9F,0x9F,0x9F,0x9F,0x9F,0x9F,0x9F,0x9F, 0x9F,0x9F,0x9F,0x9F,0x9F,0x9F,0x9F,0x9F, 0x9F,0x9F,0x9F,0x9F,0x9F,0x9F,0x9F,0x9F, 0x9F,0x9F,0x9F,0x9F,0x9F,0x9F,0x9F,0x9F, 0x9E,0x9E,0x9E,0x9E,0x9E,0x9E,0x9E,0x9E, 0x9E,0x9E,0x9E,0x9E,0x9E,0x9E,0x9E,0x9E, 0x9E,0x9E,0x9E,0x9E,0x9E,0x9E,0x9E,0x9E, 0x9E,0x9E,0x9E,0x9E,0x9E,0x9E,0x9E,0x9E, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99,0x99,0x99,0x99,0x99, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x98,0x98,0x98,0x98,0x98,0x98,0x98,0x98, 0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B, 0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B, 0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B, 0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B,0x9B, 0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A, 0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A, 0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A, 0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A,0x9A, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x85,0x85,0x85,0x85,0x85,0x85,0x85,0x85, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x87,0x87,0x87,0x87,0x87,0x87,0x87,0x87, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82, 0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D, 0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D, 0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D, 0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D, 0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D, 0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D, 0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D, 0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D,0x8D, 0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C, 0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C, 0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C, 0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C, 0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C, 0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C, 0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C, 0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C,0x8C, 0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F, 0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F, 0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F, 0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F, 0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F, 0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F, 0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F, 0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F,0x8F, 0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E, 0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E, 0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E, 0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E, 0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E, 0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E, 0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E, 0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E,0x8E, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x89, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88, 0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B, 0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B, 0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B, 0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B, 0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B, 0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B, 0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B, 0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B,0x8B, 0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A, 0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A, 0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A, 0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A, 0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A, 0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A, 0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A, 0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A,0x8A, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5,0xB5, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4,0xB4, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7,0xB7, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6,0xB6, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1,0xB1, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0,0xB0, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3,0xB3, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2,0xB2, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD,0xBD, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC,0xBC, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF,0xBF, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE,0xBE, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9,0xB9, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8,0xB8, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA,0xBA, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4,0xA4, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7,0xA7, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6,0xA6, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1,0xA1, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0,0xA0, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3,0xA3, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2,0xA2, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD,0xAD, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF,0xAF, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE,0xAE, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9,0xA9, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xA8, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B,0x2B, 0x2B,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28, 0x28,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x29,0x29,0x29,0x29,0x29,0x29,0x29, 0x29,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E,0x2E, 0x2E,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F,0x2F, 0x2F,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C,0x2C, 0x2C,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, 0x2D,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23, 0x23,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x21,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26, 0x26,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x27, 0x27,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x25,0x25,0x25,0x25,0x25,0x25,0x25, 0x25,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A,0x3A, 0x3A,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, 0x3B,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38, 0x38,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x39, 0x39,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E, 0x3E,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, 0x3F,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, 0x3C,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, 0x3D,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x32, 0x32,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, 0x33,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, 0x30,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x31,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x36,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, 0x37,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x34,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35, 0x35,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A, 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A, 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A, 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A, 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A, 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A, 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A, 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A, 0x0A,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B, 0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B, 0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B, 0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B, 0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B, 0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B, 0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B, 0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B, 0x0B,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, 0x09,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E, 0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E, 0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E, 0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E, 0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E, 0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E, 0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E, 0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E, 0x0E,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, 0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, 0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, 0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, 0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, 0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, 0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, 0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, 0x0F,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, 0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, 0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, 0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, 0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, 0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, 0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, 0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, 0x0C,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D, 0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D, 0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D, 0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D, 0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D, 0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D, 0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D, 0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D, 0x0D,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, 0x02,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, 0x06,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, 0x07,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, 0x05,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A, 0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A, 0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A, 0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A, 0x1A,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B, 0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B, 0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B, 0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B, 0x1B,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E, 0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E, 0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E, 0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E, 0x1E,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F, 0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F, 0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F, 0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F, 0x1F,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C, 0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C, 0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C, 0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C, 0x1C,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D, 0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D, 0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D, 0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D, 0x1D,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, 0x12,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13, 0x13,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x10,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, 0x16,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17, 0x17,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14, 0x14,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15, 0x15,0x6A,0x6A,0x6A,0x6A,0x6A,0x6A,0x6A, 0x6A,0x6A,0x6A,0x6A,0x6A,0x6A,0x6A,0x6A, 0x6A,0x6B,0x6B,0x6B,0x6B,0x6B,0x6B,0x6B, 0x6B,0x6B,0x6B,0x6B,0x6B,0x6B,0x6B,0x6B, 0x6B,0x68,0x68,0x68,0x68,0x68,0x68,0x68, 0x68,0x68,0x68,0x68,0x68,0x68,0x68,0x68, 0x68,0x69,0x69,0x69,0x69,0x69,0x69,0x69, 0x69,0x69,0x69,0x69,0x69,0x69,0x69,0x69, 0x69,0x6E,0x6E,0x6E,0x6E,0x6E,0x6E,0x6E, 0x6E,0x6E,0x6E,0x6E,0x6E,0x6E,0x6E,0x6E, 0x6E,0x6F,0x6F,0x6F,0x6F,0x6F,0x6F,0x6F, 0x6F,0x6F,0x6F,0x6F,0x6F,0x6F,0x6F,0x6F, 0x6F,0x6C,0x6C,0x6C,0x6C,0x6C,0x6C,0x6C, 0x6C,0x6C,0x6C,0x6C,0x6C,0x6C,0x6C,0x6C, 0x6C,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D, 0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D,0x6D, 0x6D,0x62,0x62,0x62,0x62,0x62,0x62,0x62, 0x62,0x62,0x62,0x62,0x62,0x62,0x62,0x62, 0x62,0x63,0x63,0x63,0x63,0x63,0x63,0x63, 0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63, 0x63,0x60,0x60,0x60,0x60,0x60,0x60,0x60, 0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60, 0x60,0x61,0x61,0x61,0x61,0x61,0x61,0x61, 0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61, 0x61,0x66,0x66,0x66,0x66,0x66,0x66,0x66, 0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66, 0x66,0x67,0x67,0x67,0x67,0x67,0x67,0x67, 0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67, 0x67,0x64,0x64,0x64,0x64,0x64,0x64,0x64, 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64, 0x64,0x65,0x65,0x65,0x65,0x65,0x65,0x65, 0x65,0x65,0x65,0x65,0x65,0x65,0x65,0x65, 0x65,0x7A,0x7A,0x7A,0x7A,0x7A,0x7A,0x7A, 0x7A,0x7B,0x7B,0x7B,0x7B,0x7B,0x7B,0x7B, 0x7B,0x78,0x78,0x78,0x78,0x78,0x78,0x78, 0x78,0x79,0x79,0x79,0x79,0x79,0x79,0x79, 0x79,0x7E,0x7E,0x7E,0x7E,0x7E,0x7E,0x7E, 0x7E,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F, 0x7F,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C, 0x7C,0x7D,0x7D,0x7D,0x7D,0x7D,0x7D,0x7D, 0x7D,0x72,0x72,0x72,0x72,0x72,0x72,0x72, 0x72,0x73,0x73,0x73,0x73,0x73,0x73,0x73, 0x73,0x70,0x70,0x70,0x70,0x70,0x70,0x70, 0x70,0x71,0x71,0x71,0x71,0x71,0x71,0x71, 0x71,0x76,0x76,0x76,0x76,0x76,0x76,0x76, 0x76,0x77,0x77,0x77,0x77,0x77,0x77,0x77, 0x77,0x74,0x74,0x74,0x74,0x74,0x74,0x74, 0x74,0x75,0x75,0x75,0x75,0x75,0x75,0x75, 0x75,0x4A,0x4A,0x4A,0x4A,0x4B,0x4B,0x4B, 0x4B,0x48,0x48,0x48,0x48,0x49,0x49,0x49, 0x49,0x4E,0x4E,0x4E,0x4E,0x4F,0x4F,0x4F, 0x4F,0x4C,0x4C,0x4C,0x4C,0x4D,0x4D,0x4D, 0x4D,0x42,0x42,0x42,0x42,0x43,0x43,0x43, 0x43,0x40,0x40,0x40,0x40,0x41,0x41,0x41, 0x41,0x46,0x46,0x46,0x46,0x47,0x47,0x47, 0x47,0x44,0x44,0x44,0x44,0x45,0x45,0x45, 0x45,0x5A,0x5A,0x5A,0x5A,0x5B,0x5B,0x5B, 0x5B,0x58,0x58,0x58,0x58,0x59,0x59,0x59, 0x59,0x5E,0x5E,0x5E,0x5E,0x5F,0x5F,0x5F, 0x5F,0x5C,0x5C,0x5C,0x5C,0x5D,0x5D,0x5D, 0x5D,0x52,0x52,0x52,0x52,0x53,0x53,0x53, 0x53,0x50,0x50,0x50,0x50,0x51,0x51,0x51, 0x51,0x56,0x56,0x56,0x56,0x57,0x57,0x57, 0x57,0x54,0x54,0x54,0x54,0x55,0x55,0x55 }; const pj_int16_t pjmedia_ulaw2linear_tab[256] = { -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, -876, -844, -812, -780, -748, -716, -684, -652, -620, -588, -556, -524, -492, -460, -428, -396, -372, -356, -340, -324, -308, -292, -276, -260, -244, -228, -212, -196, -180, -164, -148, -132, -120, -112, -104, -96, -88, -80, -72, -64, -56, -48, -40, -32, -24, -16, -8, 0, 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, 876, 844, 812, 780, 748, 716, 684, 652, 620, 588, 556, 524, 492, 460, 428, 396, 372, 356, 340, 324, 308, 292, 276, 260, 244, 228, 212, 196, 180, 164, 148, 132, 120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0 }; const pj_int16_t pjmedia_alaw2linear_tab[256] = { -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472, -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, -344, -328, -376, -360, -280, -264, -312, -296, -472, -456, -504, -488, -408, -392, -440, -424, -88, -72, -120, -104, -24, -8, -56, -40, -216, -200, -248, -232, -152, -136, -184, -168, -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, -688, -656, -752, -720, -560, -528, -624, -592, -944, -912, -1008, -976, -816, -784, -880, -848, 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, 344, 328, 376, 360, 280, 264, 312, 296, 472, 456, 504, 488, 408, 392, 440, 424, 88, 72, 120, 104, 24, 8, 56, 40, 216, 200, 248, 232, 152, 136, 184, 168, 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, 688, 656, 752, 720, 560, 528, 624, 592, 944, 912, 1008, 976, 816, 784, 880, 848 }; #endif ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/avi_player.c ================================================ /* $Id: avi_player.c 4057 2012-04-17 06:54:50Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * Default file player/writer buffer size. */ #include #include #include #include #include #include #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #define THIS_FILE "avi_player.c" #define AVIF_MUSTUSEINDEX 0x00000020 #define AVIF_ISINTERLEAVED 0x00000100 #define AVISF_DISABLED 0x00000001 #define AVISF_VIDEO_PALCHANGES 0x00010000 #define AVI_EOF 0xFFEEFFEE #define COMPARE_TAG(doc_tag, tag) (doc_tag == *((pj_uint32_t *)avi_tags[tag])) #define SIGNATURE PJMEDIA_SIG_PORT_VID_AVI_PLAYER #define VIDEO_CLOCK_RATE 90000 #if 0 # define TRACE_(x) PJ_LOG(4,x) #else # define TRACE_(x) #endif #if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 static void data_to_host(void *data, pj_uint8_t bits, unsigned count) { unsigned i; count /= (bits == 32? 4 : 2); if (bits == 32) { pj_int32_t *data32 = (pj_int32_t *)data; for (i=0; i= nsizes) break; data_to_host(datap, 16, sizes[i]); datap += sizes[i]; } } #else # define data_to_host(data, bits, count) # define data_to_host2(data, nsizes, sizes) #endif typedef struct avi_fmt_info { pjmedia_format_id fmt_id; pjmedia_format_id eff_fmt_id; } avi_fmt_info; static avi_fmt_info avi_fmts[] = { {PJMEDIA_FORMAT_MJPEG}, {PJMEDIA_FORMAT_H264}, {PJMEDIA_FORMAT_UYVY}, {PJMEDIA_FORMAT_YUY2}, {PJMEDIA_FORMAT_IYUV}, {PJMEDIA_FORMAT_I420}, {PJMEDIA_FORMAT_DIB}, {PJMEDIA_FORMAT_RGB24}, {PJMEDIA_FORMAT_RGB32}, {PJMEDIA_FORMAT_PACK('X','V','I','D'), PJMEDIA_FORMAT_MPEG4}, {PJMEDIA_FORMAT_PACK('x','v','i','d'), PJMEDIA_FORMAT_MPEG4}, {PJMEDIA_FORMAT_PACK('D','I','V','X'), PJMEDIA_FORMAT_MPEG4}, {PJMEDIA_FORMAT_PACK('F','M','P','4'), PJMEDIA_FORMAT_MPEG4}, {PJMEDIA_FORMAT_PACK('D','X','5','0'), PJMEDIA_FORMAT_MPEG4} }; struct pjmedia_avi_streams { unsigned num_streams; pjmedia_port **streams; }; struct avi_reader_port { pjmedia_port base; unsigned stream_id; unsigned options; pjmedia_format_id fmt_id; unsigned usec_per_frame; pj_uint16_t bits_per_sample; pj_bool_t eof; pj_off_t fsize; pj_off_t start_data; pj_uint8_t pad; pj_oshandle_t fd; pj_ssize_t size_left; pj_timestamp next_ts; pj_status_t (*cb)(pjmedia_port*, void*); }; static pj_status_t avi_get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t avi_on_destroy(pjmedia_port *this_port); static struct avi_reader_port *create_avi_port(pj_pool_t *pool) { const pj_str_t name = pj_str("file"); struct avi_reader_port *port; port = PJ_POOL_ZALLOC_T(pool, struct avi_reader_port); if (!port) return NULL; /* Put in default values. * These will be overriden once the file is read. */ pjmedia_port_info_init(&port->base.info, &name, SIGNATURE, 8000, 1, 16, 80); port->fd = (pj_oshandle_t)(pj_ssize_t)-1; port->base.get_frame = &avi_get_frame; port->base.on_destroy = &avi_on_destroy; return port; } #define file_read(fd, data, size) file_read2(fd, data, size, 32) #define file_read2(fd, data, size, bits) file_read3(fd, data, size, bits, NULL) static pj_status_t file_read3(pj_oshandle_t fd, void *data, pj_ssize_t size, pj_uint16_t bits, pj_ssize_t *psz_read) { pj_ssize_t size_read = size, size_to_read = size; pj_status_t status = pj_file_read(fd, data, &size_read); if (status != PJ_SUCCESS) return status; /* Normalize AVI header fields values from little-endian to host * byte order. */ if (bits > 0) data_to_host(data, bits, size_read); if (size_read != size_to_read) { if (psz_read) *psz_read = size_read; return AVI_EOF; } return status; } /* * Create AVI player port. */ PJ_DEF(pj_status_t) pjmedia_avi_player_create_streams(pj_pool_t *pool, const char *filename, unsigned options, pjmedia_avi_streams **p_streams) { pjmedia_avi_hdr avi_hdr; struct avi_reader_port *fport[PJMEDIA_AVI_MAX_NUM_STREAMS]; pj_off_t pos; unsigned i, nstr = 0; pj_status_t status = PJ_SUCCESS; /* Check arguments. */ PJ_ASSERT_RETURN(pool && filename && p_streams, PJ_EINVAL); /* Check the file really exists. */ if (!pj_file_exists(filename)) { return PJ_ENOTFOUND; } /* Create fport instance. */ fport[0] = create_avi_port(pool); if (!fport[0]) { return PJ_ENOMEM; } /* Get the file size. */ fport[0]->fsize = pj_file_size(filename); /* Size must be more than AVI header size */ if (fport[0]->fsize <= sizeof(riff_hdr_t) + sizeof(avih_hdr_t) + sizeof(strl_hdr_t)) { return PJMEDIA_EINVALIMEDIATYPE; } /* Open file. */ status = pj_file_open(pool, filename, PJ_O_RDONLY, &fport[0]->fd); if (status != PJ_SUCCESS) return status; /* Read the RIFF + AVIH header. */ status = file_read(fport[0]->fd, &avi_hdr, sizeof(riff_hdr_t) + sizeof(avih_hdr_t)); if (status != PJ_SUCCESS) goto on_error; /* Validate AVI file. */ if (!COMPARE_TAG(avi_hdr.riff_hdr.riff, PJMEDIA_AVI_RIFF_TAG) || !COMPARE_TAG(avi_hdr.riff_hdr.avi, PJMEDIA_AVI_AVI_TAG) || !COMPARE_TAG(avi_hdr.avih_hdr.list_tag, PJMEDIA_AVI_LIST_TAG) || !COMPARE_TAG(avi_hdr.avih_hdr.hdrl_tag, PJMEDIA_AVI_HDRL_TAG) || !COMPARE_TAG(avi_hdr.avih_hdr.avih, PJMEDIA_AVI_AVIH_TAG)) { status = PJMEDIA_EINVALIMEDIATYPE; goto on_error; } PJ_LOG(5, (THIS_FILE, "The AVI file has %d streams.", avi_hdr.avih_hdr.num_streams)); /* Unsupported AVI format. */ if (avi_hdr.avih_hdr.num_streams > PJMEDIA_AVI_MAX_NUM_STREAMS) { status = PJMEDIA_EAVIUNSUPP; goto on_error; } /** * TODO: Possibly unsupported AVI format. * If you encounter this warning, verify whether the avi player * is working properly. */ if (avi_hdr.avih_hdr.flags & AVIF_MUSTUSEINDEX || avi_hdr.avih_hdr.pad > 1) { PJ_LOG(3, (THIS_FILE, "Warning!!! Possibly unsupported AVI format: " "flags:%d, pad:%d", avi_hdr.avih_hdr.flags, avi_hdr.avih_hdr.pad)); } /* Read the headers of each stream. */ for (i = 0; i < avi_hdr.avih_hdr.num_streams; i++) { pj_size_t elem = 0; pj_ssize_t size_to_read; /* Read strl header */ status = file_read(fport[0]->fd, &avi_hdr.strl_hdr[i], sizeof(strl_hdr_t)); if (status != PJ_SUCCESS) goto on_error; elem = COMPARE_TAG(avi_hdr.strl_hdr[i].data_type, PJMEDIA_AVI_VIDS_TAG) ? sizeof(strf_video_hdr_t) : COMPARE_TAG(avi_hdr.strl_hdr[i].data_type, PJMEDIA_AVI_AUDS_TAG) ? sizeof(strf_audio_hdr_t) : 0; /* Read strf header */ status = file_read2(fport[0]->fd, &avi_hdr.strf_hdr[i], elem, 0); if (status != PJ_SUCCESS) goto on_error; /* Normalize the endian */ if (elem == sizeof(strf_video_hdr_t)) data_to_host2(&avi_hdr.strf_hdr[i], sizeof(strf_video_hdr_sizes)/ sizeof(strf_video_hdr_sizes[0]), strf_video_hdr_sizes); else if (elem == sizeof(strf_audio_hdr_t)) data_to_host2(&avi_hdr.strf_hdr[i], sizeof(strf_audio_hdr_sizes)/ sizeof(strf_audio_hdr_sizes[0]), strf_audio_hdr_sizes); /* Skip the remainder of the header */ size_to_read = avi_hdr.strl_hdr[i].list_sz - (sizeof(strl_hdr_t) - 8) - elem; status = pj_file_setpos(fport[0]->fd, size_to_read, PJ_SEEK_CUR); if (status != PJ_SUCCESS) { goto on_error; } } /* Finish reading the AVIH header */ status = pj_file_setpos(fport[0]->fd, avi_hdr.avih_hdr.list_sz + sizeof(riff_hdr_t) + 8, PJ_SEEK_SET); if (status != PJ_SUCCESS) { goto on_error; } /* Skip any JUNK or LIST INFO until we get MOVI tag */ do { pjmedia_avi_subchunk ch; int read = 0; status = file_read(fport[0]->fd, &ch, sizeof(pjmedia_avi_subchunk)); if (status != PJ_SUCCESS) { goto on_error; } if (COMPARE_TAG(ch.id, PJMEDIA_AVI_LIST_TAG)) { read = 4; status = file_read(fport[0]->fd, &ch, read); if (COMPARE_TAG(ch.id, PJMEDIA_AVI_MOVI_TAG)) break; } status = pj_file_setpos(fport[0]->fd, ch.len-read, PJ_SEEK_CUR); if (status != PJ_SUCCESS) { goto on_error; } } while(1); status = pj_file_getpos(fport[0]->fd, &pos); if (status != PJ_SUCCESS) goto on_error; for (i = 0, nstr = 0; i < avi_hdr.avih_hdr.num_streams; i++) { pjmedia_format_id fmt_id; /* Skip non-audio, non-video, or disabled streams) */ if ((!COMPARE_TAG(avi_hdr.strl_hdr[i].data_type, PJMEDIA_AVI_VIDS_TAG) && !COMPARE_TAG(avi_hdr.strl_hdr[i].data_type, PJMEDIA_AVI_AUDS_TAG)) || avi_hdr.strl_hdr[i].flags & AVISF_DISABLED) { continue; } if (COMPARE_TAG(avi_hdr.strl_hdr[i].data_type, PJMEDIA_AVI_VIDS_TAG)) { int j; if (avi_hdr.strl_hdr[i].flags & AVISF_VIDEO_PALCHANGES) { PJ_LOG(4, (THIS_FILE, "Unsupported video stream")); continue; } fmt_id = avi_hdr.strl_hdr[i].codec; for (j = sizeof(avi_fmts)/sizeof(avi_fmts[0])-1; j >= 0; j--) { /* Check supported video formats here */ if (fmt_id == avi_fmts[j].fmt_id) { if (avi_fmts[j].eff_fmt_id) fmt_id = avi_fmts[j].eff_fmt_id; break; } } if (j < 0) { PJ_LOG(4, (THIS_FILE, "Unsupported video stream")); continue; } } else { /* Check supported audio formats here */ if ((avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_PCM && avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_ALAW && avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_ULAW && avi_hdr.strl_hdr[i].codec != PJMEDIA_WAVE_FMT_TAG_PCM) || avi_hdr.strf_hdr[i].strf_audio_hdr.bits_per_sample != 16) { PJ_LOG(4, (THIS_FILE, "Unsupported audio stream")); continue; } /* Normalize format ID */ fmt_id = avi_hdr.strl_hdr[i].codec; if (avi_hdr.strl_hdr[i].codec == PJMEDIA_WAVE_FMT_TAG_PCM) fmt_id = PJMEDIA_FORMAT_PCM; } if (nstr > 0) { /* Create fport instance. */ fport[nstr] = create_avi_port(pool); if (!fport[nstr]) { status = PJ_ENOMEM; goto on_error; } /* Open file. */ status = pj_file_open(pool, filename, PJ_O_RDONLY, &fport[nstr]->fd); if (status != PJ_SUCCESS) goto on_error; /* Set the file position */ status = pj_file_setpos(fport[nstr]->fd, pos, PJ_SEEK_SET); if (status != PJ_SUCCESS) { goto on_error; } } fport[nstr]->stream_id = i; fport[nstr]->fmt_id = fmt_id; nstr++; } if (nstr == 0) { status = PJMEDIA_EAVIUNSUPP; goto on_error; } for (i = 0; i < nstr; i++) { strl_hdr_t *strl_hdr = &avi_hdr.strl_hdr[fport[i]->stream_id]; /* Initialize */ fport[i]->options = options; fport[i]->fsize = fport[0]->fsize; /* Current file position now points to start of data */ fport[i]->start_data = pos; if (COMPARE_TAG(strl_hdr->data_type, PJMEDIA_AVI_VIDS_TAG)) { strf_video_hdr_t *strf_hdr = &avi_hdr.strf_hdr[fport[i]->stream_id].strf_video_hdr; const pjmedia_video_format_info *vfi; vfi = pjmedia_get_video_format_info( pjmedia_video_format_mgr_instance(), strl_hdr->codec); fport[i]->bits_per_sample = (vfi ? vfi->bpp : 0); fport[i]->usec_per_frame = avi_hdr.avih_hdr.usec_per_frame; pjmedia_format_init_video(&fport[i]->base.info.fmt, fport[i]->fmt_id, strf_hdr->biWidth, strf_hdr->biHeight, strl_hdr->rate, strl_hdr->scale); #if 0 /* The calculation below is wrong. strf_hdr->biSizeImage shows * uncompressed size. Looks like we need to go the ugly way to * get the bitrage: * http://www.virtualdub.org/blog/pivot/entry.php?id=159 */ bps = strf_hdr->biSizeImage * 8 * strl_hdr->rate / strl_hdr->scale; if (bps==0) { /* strf_hdr->biSizeImage may be zero for uncompressed RGB */ bps = strf_hdr->biWidth * strf_hdr->biHeight * strf_hdr->biBitCount * strl_hdr->rate / strl_hdr->scale; } fport[i]->base.info.fmt.det.vid.avg_bps = bps; fport[i]->base.info.fmt.det.vid.max_bps = bps; #endif } else { strf_audio_hdr_t *strf_hdr = &avi_hdr.strf_hdr[fport[i]->stream_id].strf_audio_hdr; fport[i]->bits_per_sample = strf_hdr->bits_per_sample; fport[i]->usec_per_frame = avi_hdr.avih_hdr.usec_per_frame; pjmedia_format_init_audio(&fport[i]->base.info.fmt, fport[i]->fmt_id, strf_hdr->sample_rate, strf_hdr->nchannels, strf_hdr->bits_per_sample, 20000 /* fport[i]->usec_per_frame */, strf_hdr->bytes_per_sec * 8, strf_hdr->bytes_per_sec * 8); } pj_strdup2(pool, &fport[i]->base.info.name, filename); } /* Done. */ *p_streams = pj_pool_alloc(pool, sizeof(pjmedia_avi_streams)); (*p_streams)->num_streams = nstr; (*p_streams)->streams = pj_pool_calloc(pool, (*p_streams)->num_streams, sizeof(pjmedia_port *)); for (i = 0; i < nstr; i++) (*p_streams)->streams[i] = &fport[i]->base; PJ_LOG(4,(THIS_FILE, "AVI file player '%.*s' created with " "%d media ports", (int)fport[0]->base.info.name.slen, fport[0]->base.info.name.ptr, (*p_streams)->num_streams)); return PJ_SUCCESS; on_error: fport[0]->base.on_destroy(&fport[0]->base); for (i = 1; i < nstr; i++) fport[i]->base.on_destroy(&fport[i]->base); if (status == AVI_EOF) return PJMEDIA_EINVALIMEDIATYPE; return status; } PJ_DEF(unsigned) pjmedia_avi_streams_get_num_streams(pjmedia_avi_streams *streams) { pj_assert(streams); return streams->num_streams; } PJ_DEF(pjmedia_avi_stream *) pjmedia_avi_streams_get_stream(pjmedia_avi_streams *streams, unsigned idx) { pj_assert(streams); return (idx < streams->num_streams ? streams->streams[idx] : NULL); } PJ_DEF(pjmedia_avi_stream *) pjmedia_avi_streams_get_stream_by_media(pjmedia_avi_streams *streams, unsigned start_idx, pjmedia_type media_type) { unsigned i; pj_assert(streams); for (i = start_idx; i < streams->num_streams; i++) if (streams->streams[i]->info.fmt.type == media_type) return streams->streams[i]; return NULL; } /* * Get the data length, in bytes. */ PJ_DEF(pj_ssize_t) pjmedia_avi_stream_get_len(pjmedia_avi_stream *stream) { struct avi_reader_port *fport; /* Sanity check */ PJ_ASSERT_RETURN(stream, -PJ_EINVAL); /* Check that this is really a player port */ PJ_ASSERT_RETURN(stream->info.signature == SIGNATURE, -PJ_EINVALIDOP); fport = (struct avi_reader_port*) stream; return (pj_ssize_t)(fport->fsize - fport->start_data); } /* * Register a callback to be called when the file reading has reached the * end of file. */ PJ_DEF(pj_status_t) pjmedia_avi_stream_set_eof_cb( pjmedia_avi_stream *stream, void *user_data, pj_status_t (*cb)(pjmedia_avi_stream *stream, void *usr_data)) { struct avi_reader_port *fport; /* Sanity check */ PJ_ASSERT_RETURN(stream, -PJ_EINVAL); /* Check that this is really a player port */ PJ_ASSERT_RETURN(stream->info.signature == SIGNATURE, -PJ_EINVALIDOP); fport = (struct avi_reader_port*) stream; fport->base.port_data.pdata = user_data; fport->cb = cb; return PJ_SUCCESS; } /* * Get frame from file. */ static pj_status_t avi_get_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct avi_reader_port *fport = (struct avi_reader_port*)this_port; pj_status_t status; pj_ssize_t size_read = 0, size_to_read = 0; pj_assert(fport->base.info.signature == SIGNATURE); /* We encountered end of file */ if (fport->eof) { pj_status_t status = PJ_SUCCESS; PJ_LOG(5,(THIS_FILE, "File port %.*s EOF", (int)fport->base.info.name.slen, fport->base.info.name.ptr)); /* Call callback, if any */ if (fport->cb) status = (*fport->cb)(this_port, fport->base.port_data.pdata); /* If callback returns non PJ_SUCCESS or 'no loop' is specified, * return immediately (and don't try to access player port since * it might have been destroyed by the callback). */ if ((status != PJ_SUCCESS) || (fport->options & PJMEDIA_AVI_FILE_NO_LOOP)) { frame->type = PJMEDIA_FRAME_TYPE_NONE; frame->size = 0; return PJ_EEOF; } /* Rewind file */ PJ_LOG(5,(THIS_FILE, "File port %.*s rewinding..", (int)fport->base.info.name.slen, fport->base.info.name.ptr)); fport->eof = PJ_FALSE; pj_file_setpos(fport->fd, fport->start_data, PJ_SEEK_SET); } /* Fill frame buffer. */ size_to_read = frame->size; do { pjmedia_avi_subchunk ch = {0, 0}; char *cid; unsigned stream_id; /* We need to read data from the file past the chunk boundary */ if (fport->size_left > 0 && fport->size_left < size_to_read) { status = file_read3(fport->fd, frame->buf, fport->size_left, fport->bits_per_sample, &size_read); if (status != PJ_SUCCESS) goto on_error2; size_to_read -= fport->size_left; fport->size_left = 0; } /* Read new chunk data */ if (fport->size_left == 0) { pj_off_t pos; pj_file_getpos(fport->fd, &pos); /* Data is padded to the nearest WORD boundary */ if (fport->pad) { status = pj_file_setpos(fport->fd, fport->pad, PJ_SEEK_CUR); fport->pad = 0; } status = file_read(fport->fd, &ch, sizeof(pjmedia_avi_subchunk)); if (status != PJ_SUCCESS) { size_read = 0; goto on_error2; } cid = (char *)&ch.id; if (cid[0] >= '0' && cid[0] <= '9' && cid[1] >= '0' && cid[1] <= '9') { stream_id = (cid[0] - '0') * 10 + (cid[1] - '0'); } else stream_id = 100; fport->pad = (pj_uint8_t)ch.len & 1; TRACE_((THIS_FILE, "Reading movi data at pos %u (%x), id: %.*s, " "length: %u", (unsigned long)pos, (unsigned long)pos, 4, cid, ch.len)); /* We are only interested in data with our stream id */ if (stream_id != fport->stream_id) { if (COMPARE_TAG(ch.id, PJMEDIA_AVI_LIST_TAG)) PJ_LOG(5, (THIS_FILE, "Unsupported LIST tag found in " "the movi data.")); else if (COMPARE_TAG(ch.id, PJMEDIA_AVI_RIFF_TAG)) { PJ_LOG(3, (THIS_FILE, "Unsupported format: multiple " "AVIs in a single file.")); status = AVI_EOF; goto on_error2; } status = pj_file_setpos(fport->fd, ch.len, PJ_SEEK_CUR); continue; } fport->size_left = ch.len; } frame->type = (fport->base.info.fmt.type == PJMEDIA_TYPE_VIDEO ? PJMEDIA_FRAME_TYPE_VIDEO : PJMEDIA_FRAME_TYPE_AUDIO); if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) { if (size_to_read > fport->size_left) size_to_read = fport->size_left; status = file_read3(fport->fd, (char *)frame->buf + frame->size - size_to_read, size_to_read, fport->bits_per_sample, &size_read); if (status != PJ_SUCCESS) goto on_error2; fport->size_left -= size_to_read; } else { pj_assert(frame->size >= ch.len); status = file_read3(fport->fd, frame->buf, ch.len, 0, &size_read); if (status != PJ_SUCCESS) goto on_error2; frame->size = ch.len; fport->size_left = 0; } break; } while(1); frame->timestamp.u64 = fport->next_ts.u64; if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) { if (fport->usec_per_frame) { fport->next_ts.u64 += (fport->usec_per_frame * fport->base.info.fmt.det.aud.clock_rate / 1000000); } else { fport->next_ts.u64 += (frame->size * fport->base.info.fmt.det.aud.clock_rate / (fport->base.info.fmt.det.aud.avg_bps / 8)); } } else { if (fport->usec_per_frame) { fport->next_ts.u64 += (fport->usec_per_frame * VIDEO_CLOCK_RATE / 1000000); } else { fport->next_ts.u64 += (frame->size * VIDEO_CLOCK_RATE / (fport->base.info.fmt.det.vid.avg_bps / 8)); } } return PJ_SUCCESS; on_error2: if (status == AVI_EOF) { size_to_read -= size_read; pj_bzero((char *)frame->buf + frame->size - size_to_read, size_to_read); fport->eof = PJ_TRUE; return PJ_SUCCESS; } return status; } /* * Destroy port. */ static pj_status_t avi_on_destroy(pjmedia_port *this_port) { struct avi_reader_port *fport = (struct avi_reader_port*) this_port; pj_assert(this_port->info.signature == SIGNATURE); if (fport->fd != (pj_oshandle_t) (pj_ssize_t)-1) pj_file_close(fport->fd); return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_VIDEO */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/bidirectional.c ================================================ /* $Id: bidirectional.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #define THIS_FILE "bidirectional.c" #define SIGNATURE PJMEDIA_SIG_PORT_BIDIR struct bidir_port { pjmedia_port base; pjmedia_port *get_port; pjmedia_port *put_port; }; static pj_status_t put_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct bidir_port *p = (struct bidir_port*)this_port; return pjmedia_port_put_frame(p->put_port, frame); } static pj_status_t get_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct bidir_port *p = (struct bidir_port*)this_port; return pjmedia_port_get_frame(p->get_port, frame); } PJ_DEF(pj_status_t) pjmedia_bidirectional_port_create( pj_pool_t *pool, pjmedia_port *get_port, pjmedia_port *put_port, pjmedia_port **p_port ) { struct bidir_port *port; const pjmedia_audio_format_detail *gafd; port = PJ_POOL_ZALLOC_T(pool, struct bidir_port); gafd = pjmedia_format_get_audio_format_detail(&get_port->info.fmt, 1); pjmedia_port_info_init(&port->base.info, &get_port->info.name, SIGNATURE, gafd->clock_rate, gafd->channel_count, gafd->bits_per_sample, PJMEDIA_AFD_SPF(gafd)); port->get_port = get_port; port->put_port = put_port; port->base.get_frame = &get_frame; port->base.put_frame = &put_frame; *p_port = &port->base; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/clock_thread.c ================================================ /* $Id: clock_thread.c 4160 2012-06-07 04:10:22Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include /* API: Init clock source */ PJ_DEF(pj_status_t) pjmedia_clock_src_init( pjmedia_clock_src *clocksrc, pjmedia_type media_type, unsigned clock_rate, unsigned ptime_usec ) { PJ_ASSERT_RETURN(clocksrc, PJ_EINVAL); clocksrc->media_type = media_type; clocksrc->clock_rate = clock_rate; clocksrc->ptime_usec = ptime_usec; pj_set_timestamp32(&clocksrc->timestamp, 0, 0); pj_get_timestamp(&clocksrc->last_update); return PJ_SUCCESS; } /* API: Update clock source */ PJ_DECL(pj_status_t) pjmedia_clock_src_update( pjmedia_clock_src *clocksrc, const pj_timestamp *timestamp ) { PJ_ASSERT_RETURN(clocksrc, PJ_EINVAL); if (timestamp) pj_memcpy(&clocksrc->timestamp, timestamp, sizeof(pj_timestamp)); pj_get_timestamp(&clocksrc->last_update); return PJ_SUCCESS; } /* API: Get clock source's current timestamp */ PJ_DEF(pj_status_t) pjmedia_clock_src_get_current_timestamp( const pjmedia_clock_src *clocksrc, pj_timestamp *timestamp) { pj_timestamp now; unsigned elapsed_ms; PJ_ASSERT_RETURN(clocksrc && timestamp, PJ_EINVAL); pj_get_timestamp(&now); elapsed_ms = pj_elapsed_msec(&clocksrc->last_update, &now); pj_memcpy(timestamp, &clocksrc->timestamp, sizeof(pj_timestamp)); pj_add_timestamp32(timestamp, elapsed_ms * clocksrc->clock_rate / 1000); return PJ_SUCCESS; } /* API: Get clock source's time (in ms) */ PJ_DEF(pj_uint32_t) pjmedia_clock_src_get_time_msec( const pjmedia_clock_src *clocksrc ) { pj_timestamp ts; pjmedia_clock_src_get_current_timestamp(clocksrc, &ts); #if PJ_HAS_INT64 if (ts.u64 > PJ_UINT64(0x3FFFFFFFFFFFFF)) return (pj_uint32_t)(ts.u64 / clocksrc->clock_rate * 1000); else return (pj_uint32_t)(ts.u64 * 1000 / clocksrc->clock_rate); #elif PJ_HAS_FLOATING_POINT return (pj_uint32_t)((1.0 * ts.u32.hi * 0xFFFFFFFFUL + ts.u32.lo) * 1000.0 / clocksrc->clock_rate); #else if (ts.u32.lo > 0x3FFFFFUL) return (pj_uint32_t)(0xFFFFFFFFUL / clocksrc->clock_rate * ts.u32.hi * 1000UL + ts.u32.lo / clocksrc->clock_rate * 1000UL); else return (pj_uint32_t)(0xFFFFFFFFUL / clocksrc->clock_rate * ts.u32.hi * 1000UL + ts.u32.lo * 1000UL / clocksrc->clock_rate); #endif } /* * Implementation of media clock with OS thread. */ struct pjmedia_clock { pj_pool_t *pool; pj_timestamp freq; pj_timestamp interval; pj_timestamp next_tick; pj_timestamp timestamp; unsigned timestamp_inc; unsigned options; pj_uint64_t max_jump; pjmedia_clock_callback *cb; void *user_data; pj_thread_t *thread; pj_bool_t running; pj_bool_t quitting; pj_lock_t *lock; }; static int clock_thread(void *arg); #define MAX_JUMP_MSEC 500 #define USEC_IN_SEC (pj_uint64_t)1000000 /* * Create media clock. */ PJ_DEF(pj_status_t) pjmedia_clock_create( pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned options, pjmedia_clock_callback *cb, void *user_data, pjmedia_clock **p_clock) { pjmedia_clock_param param; param.usec_interval = (unsigned)(samples_per_frame * USEC_IN_SEC / channel_count / clock_rate); param.clock_rate = clock_rate; return pjmedia_clock_create2(pool, ¶m, options, cb, user_data, p_clock); } PJ_DEF(pj_status_t) pjmedia_clock_create2(pj_pool_t *pool, const pjmedia_clock_param *param, unsigned options, pjmedia_clock_callback *cb, void *user_data, pjmedia_clock **p_clock) { pjmedia_clock *clock; pj_status_t status; PJ_ASSERT_RETURN(pool && param->usec_interval && param->clock_rate && p_clock, PJ_EINVAL); clock = PJ_POOL_ALLOC_T(pool, pjmedia_clock); clock->pool = pj_pool_create(pool->factory, "clock%p", 512, 512, NULL); status = pj_get_timestamp_freq(&clock->freq); if (status != PJ_SUCCESS) return status; clock->interval.u64 = param->usec_interval * clock->freq.u64 / USEC_IN_SEC; clock->next_tick.u64 = 0; clock->timestamp.u64 = 0; clock->max_jump = MAX_JUMP_MSEC * clock->freq.u64 / 1000; clock->timestamp_inc = (unsigned)(param->usec_interval * param->clock_rate / USEC_IN_SEC); clock->options = options; clock->cb = cb; clock->user_data = user_data; clock->thread = NULL; clock->running = PJ_FALSE; clock->quitting = PJ_FALSE; /* I don't think we need a mutex, so we'll use null. */ status = pj_lock_create_null_mutex(pool, "clock", &clock->lock); if (status != PJ_SUCCESS) return status; *p_clock = clock; return PJ_SUCCESS; } /* * Start the clock. */ PJ_DEF(pj_status_t) pjmedia_clock_start(pjmedia_clock *clock) { pj_timestamp now; pj_status_t status; PJ_ASSERT_RETURN(clock != NULL, PJ_EINVAL); if (clock->running) return PJ_SUCCESS; status = pj_get_timestamp(&now); if (status != PJ_SUCCESS) return status; clock->next_tick.u64 = now.u64 + clock->interval.u64; clock->running = PJ_TRUE; clock->quitting = PJ_FALSE; if ((clock->options & PJMEDIA_CLOCK_NO_ASYNC) == 0 && !clock->thread) { status = pj_thread_create(clock->pool, "clock", &clock_thread, clock, 0, 0, &clock->thread); if (status != PJ_SUCCESS) { clock->running = PJ_FALSE; return status; } } return PJ_SUCCESS; } /* * Stop the clock. */ PJ_DEF(pj_status_t) pjmedia_clock_stop(pjmedia_clock *clock) { PJ_ASSERT_RETURN(clock != NULL, PJ_EINVAL); clock->running = PJ_FALSE; clock->quitting = PJ_TRUE; if (clock->thread) { if (pj_thread_join(clock->thread) == PJ_SUCCESS) { pj_thread_destroy(clock->thread); clock->thread = NULL; pj_pool_reset(clock->pool); } else { clock->quitting = PJ_FALSE; } } return PJ_SUCCESS; } /* * Update the clock. */ PJ_DEF(pj_status_t) pjmedia_clock_modify(pjmedia_clock *clock, const pjmedia_clock_param *param) { clock->interval.u64 = param->usec_interval * clock->freq.u64 / USEC_IN_SEC; clock->timestamp_inc = (unsigned)(param->usec_interval * param->clock_rate / USEC_IN_SEC); return PJ_SUCCESS; } /* Calculate next tick */ PJ_INLINE(void) clock_calc_next_tick(pjmedia_clock *clock, pj_timestamp *now) { if (clock->next_tick.u64+clock->max_jump < now->u64) { /* Timestamp has made large jump, adjust next_tick */ clock->next_tick.u64 = now->u64; } clock->next_tick.u64 += clock->interval.u64; } /* * Poll the clock. */ PJ_DEF(pj_bool_t) pjmedia_clock_wait( pjmedia_clock *clock, pj_bool_t wait, pj_timestamp *ts) { pj_timestamp now; pj_status_t status; PJ_ASSERT_RETURN(clock != NULL, PJ_FALSE); PJ_ASSERT_RETURN((clock->options & PJMEDIA_CLOCK_NO_ASYNC) != 0, PJ_FALSE); PJ_ASSERT_RETURN(clock->running, PJ_FALSE); status = pj_get_timestamp(&now); if (status != PJ_SUCCESS) return PJ_FALSE; /* Wait for the next tick to happen */ if (now.u64 < clock->next_tick.u64) { unsigned msec; if (!wait) return PJ_FALSE; msec = pj_elapsed_msec(&now, &clock->next_tick); pj_thread_sleep(msec); } /* Call callback, if any */ if (clock->cb) (*clock->cb)(&clock->timestamp, clock->user_data); /* Report timestamp to caller */ if (ts) ts->u64 = clock->timestamp.u64; /* Increment timestamp */ clock->timestamp.u64 += clock->timestamp_inc; /* Calculate next tick */ clock_calc_next_tick(clock, &now); /* Done */ return PJ_TRUE; } /* * Clock thread */ static int clock_thread(void *arg) { pj_timestamp now; pjmedia_clock *clock = (pjmedia_clock*) arg; /* Set thread priority to maximum unless not wanted. */ if ((clock->options & PJMEDIA_CLOCK_NO_HIGHEST_PRIO) == 0) { int max = pj_thread_get_prio_max(pj_thread_this()); if (max > 0) pj_thread_set_prio(pj_thread_this(), max); } /* Get the first tick */ pj_get_timestamp(&clock->next_tick); clock->next_tick.u64 += clock->interval.u64; while (!clock->quitting) { pj_get_timestamp(&now); /* Wait for the next tick to happen */ if (now.u64 < clock->next_tick.u64) { unsigned msec; msec = pj_elapsed_msec(&now, &clock->next_tick); pj_thread_sleep(msec); } /* Skip if not running */ if (!clock->running) { /* Calculate next tick */ clock_calc_next_tick(clock, &now); continue; } pj_lock_acquire(clock->lock); /* Call callback, if any */ if (clock->cb) (*clock->cb)(&clock->timestamp, clock->user_data); /* Best effort way to detect if we've been destroyed in the callback */ if (clock->quitting) break; /* Increment timestamp */ clock->timestamp.u64 += clock->timestamp_inc; /* Calculate next tick */ clock_calc_next_tick(clock, &now); pj_lock_release(clock->lock); } return 0; } /* * Destroy the clock. */ PJ_DEF(pj_status_t) pjmedia_clock_destroy(pjmedia_clock *clock) { PJ_ASSERT_RETURN(clock != NULL, PJ_EINVAL); clock->running = PJ_FALSE; clock->quitting = PJ_TRUE; if (clock->thread) { pj_thread_join(clock->thread); pj_thread_destroy(clock->thread); clock->thread = NULL; } if (clock->lock) { pj_lock_destroy(clock->lock); clock->lock = NULL; } if (clock->pool) { pj_pool_t *pool = clock->pool; clock->pool = NULL; pj_pool_release(pool); } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/codec.c ================================================ /* $Id: codec.c 4254 2012-09-14 04:06:29Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #define THIS_FILE "codec.c" /* Definition of default codecs parameters */ struct pjmedia_codec_default_param { pj_pool_t *pool; pjmedia_codec_param *param; }; /* Sort codecs in codec manager based on priorities */ static void sort_codecs(pjmedia_codec_mgr *mgr); /* * Duplicate codec parameter. */ PJ_DEF(pjmedia_codec_param*) pjmedia_codec_param_clone( pj_pool_t *pool, const pjmedia_codec_param *src) { pjmedia_codec_param *p; unsigned i; PJ_ASSERT_RETURN(pool && src, NULL); p = PJ_POOL_ZALLOC_T(pool, pjmedia_codec_param); /* Update codec param */ pj_memcpy(p, src, sizeof(pjmedia_codec_param)); for (i = 0; i < src->setting.dec_fmtp.cnt; ++i) { pj_strdup(pool, &p->setting.dec_fmtp.param[i].name, &src->setting.dec_fmtp.param[i].name); pj_strdup(pool, &p->setting.dec_fmtp.param[i].val, &src->setting.dec_fmtp.param[i].val); } for (i = 0; i < src->setting.enc_fmtp.cnt; ++i) { pj_strdup(pool, &p->setting.enc_fmtp.param[i].name, &src->setting.enc_fmtp.param[i].name); pj_strdup(pool, &p->setting.enc_fmtp.param[i].val, &src->setting.enc_fmtp.param[i].val); } return p; } /* * Initialize codec manager. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_init (pjmedia_codec_mgr *mgr, pj_pool_factory *pf) { pj_status_t status; PJ_ASSERT_RETURN(mgr && pf, PJ_EINVAL); /* Init codec manager */ pj_bzero(mgr, sizeof(pjmedia_codec_mgr)); mgr->pf = pf; pj_list_init (&mgr->factory_list); mgr->codec_cnt = 0; /* Create pool */ mgr->pool = pj_pool_create(mgr->pf, "codec-mgr", 256, 256, NULL); /* Create mutex */ status = pj_mutex_create_recursive(mgr->pool, "codec-mgr", &mgr->mutex); if (status != PJ_SUCCESS) return status; return PJ_SUCCESS; } /* * Initialize codec manager. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_destroy (pjmedia_codec_mgr *mgr) { pjmedia_codec_factory *factory; unsigned i; PJ_ASSERT_RETURN(mgr, PJ_EINVAL); /* Destroy all factories in the list */ factory = mgr->factory_list.next; while (factory != &mgr->factory_list) { pjmedia_codec_factory *next = factory->next; (*factory->op->destroy)(); factory = next; } /* Cleanup all pools of all codec default params */ for (i=0; icodec_cnt; ++i) { if (mgr->codec_desc[i].param) { pj_assert(mgr->codec_desc[i].param->pool); pj_pool_release(mgr->codec_desc[i].param->pool); } } /* Destroy mutex */ if (mgr->mutex) pj_mutex_destroy(mgr->mutex); /* Release pool */ if (mgr->pool) pj_pool_release(mgr->pool); /* Just for safety, set codec manager states to zero */ pj_bzero(mgr, sizeof(pjmedia_codec_mgr)); return PJ_SUCCESS; } /* * Register a codec factory. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_register_factory( pjmedia_codec_mgr *mgr, pjmedia_codec_factory *factory) { pjmedia_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS]; unsigned i, count; pj_status_t status; PJ_ASSERT_RETURN(mgr && factory, PJ_EINVAL); /* Since 2.0 we require codec factory to implement "destroy" op. Please * see: https://trac.pjsip.org/repos/ticket/1294 * * Really! Please do see it. */ PJ_ASSERT_RETURN(factory->op->destroy != NULL, PJ_ENOTSUP); /* Enum codecs */ count = PJ_ARRAY_SIZE(info); status = factory->op->enum_info(factory, &count, info); if (status != PJ_SUCCESS) return status; pj_mutex_lock(mgr->mutex); /* Check codec count */ if (count + mgr->codec_cnt > PJ_ARRAY_SIZE(mgr->codec_desc)) { pj_mutex_unlock(mgr->mutex); return PJ_ETOOMANY; } /* Save the codecs */ for (i=0; icodec_desc[mgr->codec_cnt+i], &info[i], sizeof(pjmedia_codec_info)); mgr->codec_desc[mgr->codec_cnt+i].prio = PJMEDIA_CODEC_PRIO_NORMAL; mgr->codec_desc[mgr->codec_cnt+i].factory = factory; pjmedia_codec_info_to_id( &info[i], mgr->codec_desc[mgr->codec_cnt+i].id, sizeof(pjmedia_codec_id)); } /* Update count */ mgr->codec_cnt += count; /* Re-sort codec based on priorities */ sort_codecs(mgr); /* Add factory to the list */ pj_list_push_back(&mgr->factory_list, factory); pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } /* * Unregister a codec factory. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_unregister_factory( pjmedia_codec_mgr *mgr, pjmedia_codec_factory *factory) { unsigned i; PJ_ASSERT_RETURN(mgr && factory, PJ_EINVAL); pj_mutex_lock(mgr->mutex); /* Factory must be registered. */ if (pj_list_find_node(&mgr->factory_list, factory) != factory) { pj_mutex_unlock(mgr->mutex); return PJ_ENOTFOUND; } /* Erase factory from the factory list */ pj_list_erase(factory); /* Remove all supported codecs from the codec manager that were created * by the specified factory. */ for (i=0; icodec_cnt; ) { if (mgr->codec_desc[i].factory == factory) { /* Release pool of codec default param */ if (mgr->codec_desc[i].param) { pj_assert(mgr->codec_desc[i].param->pool); pj_pool_release(mgr->codec_desc[i].param->pool); } /* Remove the codec from array of codec descriptions */ pj_array_erase(mgr->codec_desc, sizeof(mgr->codec_desc[0]), mgr->codec_cnt, i); --mgr->codec_cnt; } else { ++i; } } pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } /* * Enum all codecs. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_enum_codecs(pjmedia_codec_mgr *mgr, unsigned *count, pjmedia_codec_info codecs[], unsigned *prio) { unsigned i; PJ_ASSERT_RETURN(mgr && count && codecs, PJ_EINVAL); pj_mutex_lock(mgr->mutex); if (*count > mgr->codec_cnt) *count = mgr->codec_cnt; for (i=0; i<*count; ++i) { pj_memcpy(&codecs[i], &mgr->codec_desc[i].info, sizeof(pjmedia_codec_info)); } if (prio) { for (i=0; i < *count; ++i) prio[i] = mgr->codec_desc[i].prio; } pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } /* * Get codec info for static payload type. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_get_codec_info( pjmedia_codec_mgr *mgr, unsigned pt, const pjmedia_codec_info **p_info) { unsigned i; PJ_ASSERT_RETURN(mgr && p_info && pt>=0 && pt < 96, PJ_EINVAL); pj_mutex_lock(mgr->mutex); for (i=0; icodec_cnt; ++i) { if (mgr->codec_desc[i].info.pt == pt) { *p_info = &mgr->codec_desc[i].info; pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } } pj_mutex_unlock(mgr->mutex); return PJMEDIA_CODEC_EUNSUP; } /* * Convert codec info struct into a unique codec identifier. * A codec identifier looks something like "L16/44100/2". */ PJ_DEF(char*) pjmedia_codec_info_to_id( const pjmedia_codec_info *info, char *id, unsigned max_len ) { int len; PJ_ASSERT_RETURN(info && id && max_len, NULL); len = pj_ansi_snprintf(id, max_len, "%.*s/%u/%u", (int)info->encoding_name.slen, info->encoding_name.ptr, info->clock_rate, info->channel_cnt); if (len < 1 || len >= (int)max_len) { id[0] = '\0'; return NULL; } return id; } /* * Find codecs by the unique codec identifier. This function will find * all codecs that match the codec identifier prefix. For example, if * "L16" is specified, then it will find "L16/8000/1", "L16/16000/1", * and so on, up to the maximum count specified in the argument. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_find_codecs_by_id( pjmedia_codec_mgr *mgr, const pj_str_t *codec_id, unsigned *count, const pjmedia_codec_info *p_info[], unsigned prio[]) { unsigned i, found = 0; PJ_ASSERT_RETURN(mgr && codec_id && count && *count, PJ_EINVAL); pj_mutex_lock(mgr->mutex); for (i=0; icodec_cnt; ++i) { if (codec_id->slen == 0 || pj_strnicmp2(codec_id, mgr->codec_desc[i].id, codec_id->slen) == 0) { if (p_info) p_info[found] = &mgr->codec_desc[i].info; if (prio) prio[found] = mgr->codec_desc[i].prio; ++found; if (found >= *count) break; } } pj_mutex_unlock(mgr->mutex); *count = found; return found ? PJ_SUCCESS : PJ_ENOTFOUND; } /* Swap two codecs positions in codec manager */ static void swap_codec(pjmedia_codec_mgr *mgr, unsigned i, unsigned j) { struct pjmedia_codec_desc tmp; pj_memcpy(&tmp, &mgr->codec_desc[i], sizeof(struct pjmedia_codec_desc)); pj_memcpy(&mgr->codec_desc[i], &mgr->codec_desc[j], sizeof(struct pjmedia_codec_desc)); pj_memcpy(&mgr->codec_desc[j], &tmp, sizeof(struct pjmedia_codec_desc)); } /* Sort codecs in codec manager based on priorities */ static void sort_codecs(pjmedia_codec_mgr *mgr) { unsigned i; /* Re-sort */ for (i=0; icodec_cnt; ++i) { unsigned j, max; for (max=i, j=i+1; jcodec_cnt; ++j) { if (mgr->codec_desc[j].prio > mgr->codec_desc[max].prio) max = j; } if (max != i) swap_codec(mgr, i, max); } /* Change PJMEDIA_CODEC_PRIO_HIGHEST codecs to NEXT_HIGHER */ for (i=0; icodec_cnt; ++i) { if (mgr->codec_desc[i].prio == PJMEDIA_CODEC_PRIO_HIGHEST) mgr->codec_desc[i].prio = PJMEDIA_CODEC_PRIO_NEXT_HIGHER; else break; } } /** * Set codec priority. The codec priority determines the order of * the codec in the SDP created by the endpoint. If more than one codecs * are found with the same codec_id prefix, then the function sets the * priorities of all those codecs. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_set_codec_priority( pjmedia_codec_mgr *mgr, const pj_str_t *codec_id, pj_uint8_t prio) { unsigned i, found = 0; PJ_ASSERT_RETURN(mgr && codec_id, PJ_EINVAL); pj_mutex_lock(mgr->mutex); /* Update the priorities of affected codecs */ for (i=0; icodec_cnt; ++i) { if (codec_id->slen == 0 || pj_strnicmp2(codec_id, mgr->codec_desc[i].id, codec_id->slen) == 0) { mgr->codec_desc[i].prio = (pjmedia_codec_priority) prio; ++found; } } if (!found) { pj_mutex_unlock(mgr->mutex); return PJ_ENOTFOUND; } /* Re-sort codecs */ sort_codecs(mgr); pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } /* * Allocate one codec. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_alloc_codec(pjmedia_codec_mgr *mgr, const pjmedia_codec_info *info, pjmedia_codec **p_codec) { pjmedia_codec_factory *factory; pj_status_t status; PJ_ASSERT_RETURN(mgr && info && p_codec, PJ_EINVAL); *p_codec = NULL; pj_mutex_lock(mgr->mutex); factory = mgr->factory_list.next; while (factory != &mgr->factory_list) { if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) { status = (*factory->op->alloc_codec)(factory, info, p_codec); if (status == PJ_SUCCESS) { pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } } factory = factory->next; } pj_mutex_unlock(mgr->mutex); return PJMEDIA_CODEC_EUNSUP; } /* * Get default codec parameter. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_get_default_param( pjmedia_codec_mgr *mgr, const pjmedia_codec_info *info, pjmedia_codec_param *param ) { pjmedia_codec_factory *factory; pj_status_t status; pjmedia_codec_id codec_id; struct pjmedia_codec_desc *codec_desc = NULL; unsigned i; PJ_ASSERT_RETURN(mgr && info && param, PJ_EINVAL); if (!pjmedia_codec_info_to_id(info, (char*)&codec_id, sizeof(codec_id))) return PJ_EINVAL; pj_mutex_lock(mgr->mutex); /* First, lookup default param in codec desc */ for (i=0; i < mgr->codec_cnt; ++i) { if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) { codec_desc = &mgr->codec_desc[i]; break; } } /* If we found the codec and its default param is set, return it */ if (codec_desc && codec_desc->param) { pj_assert(codec_desc->param->param); pj_memcpy(param, codec_desc->param->param, sizeof(pjmedia_codec_param)); pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } /* Otherwise query the default param from codec factory */ factory = mgr->factory_list.next; while (factory != &mgr->factory_list) { if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) { status = (*factory->op->default_attr)(factory, info, param); if (status == PJ_SUCCESS) { /* Check for invalid max_bps. */ if (param->info.max_bps < param->info.avg_bps) param->info.max_bps = param->info.avg_bps; pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } } factory = factory->next; } pj_mutex_unlock(mgr->mutex); return PJMEDIA_CODEC_EUNSUP; } /* * Set default codec parameter. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_set_default_param( pjmedia_codec_mgr *mgr, const pjmedia_codec_info *info, const pjmedia_codec_param *param ) { unsigned i; pjmedia_codec_id codec_id; pj_pool_t *pool, *old_pool = NULL; struct pjmedia_codec_desc *codec_desc = NULL; pjmedia_codec_default_param *p; PJ_ASSERT_RETURN(mgr && info, PJ_EINVAL); if (!pjmedia_codec_info_to_id(info, (char*)&codec_id, sizeof(codec_id))) return PJ_EINVAL; pj_mutex_lock(mgr->mutex); /* Lookup codec desc */ for (i=0; i < mgr->codec_cnt; ++i) { if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) { codec_desc = &mgr->codec_desc[i]; break; } } /* Codec not found */ if (!codec_desc) { pj_mutex_unlock(mgr->mutex); return PJMEDIA_CODEC_EUNSUP; } /* If codec param is previously set, reset the codec param but release * the codec param pool later after the new param is set (ticket #1171). */ if (codec_desc->param) { pj_assert(codec_desc->param->pool); old_pool = codec_desc->param->pool; codec_desc->param = NULL; } /* When param is set to NULL, i.e: setting default codec param to library * default setting, just return PJ_SUCCESS. */ if (NULL == param) { pj_mutex_unlock(mgr->mutex); if (old_pool) pj_pool_release(old_pool); return PJ_SUCCESS; } /* Instantiate and initialize codec param */ pool = pj_pool_create(mgr->pf, (char*)codec_id, 256, 256, NULL); codec_desc->param = PJ_POOL_ZALLOC_T(pool, pjmedia_codec_default_param); p = codec_desc->param; p->pool = pool; /* Update codec param */ p->param = pjmedia_codec_param_clone(pool, param); if (!p->param) { pj_mutex_unlock(mgr->mutex); return PJ_EINVAL; } pj_mutex_unlock(mgr->mutex); if (old_pool) pj_pool_release(old_pool); return PJ_SUCCESS; } /* * Dealloc codec. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_dealloc_codec(pjmedia_codec_mgr *mgr, pjmedia_codec *codec) { PJ_ASSERT_RETURN(mgr && codec, PJ_EINVAL); return (*codec->factory->op->dealloc_codec)(codec->factory, codec); } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/conf_switch.c ================================================ /* $Id: conf_switch.c 4443 2013-03-20 06:56:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #if defined(PJMEDIA_CONF_USE_SWITCH_BOARD) && PJMEDIA_CONF_USE_SWITCH_BOARD!=0 /* CONF_DEBUG enables detailed operation of the conference bridge. * Beware that it prints large amounts of logs (several lines per frame). */ //#define CONF_DEBUG #ifdef CONF_DEBUG # include # define TRACE_(x) PJ_LOG(5,x) #else # define TRACE_(x) #endif #define THIS_FILE "conf_switch.c" #define SIGNATURE PJMEDIA_CONF_SWITCH_SIGNATURE #define SIGNATURE_PORT PJMEDIA_PORT_SIGNATURE('S', 'W', 'T', 'P') #define NORMAL_LEVEL 128 #define SLOT_TYPE unsigned #define INVALID_SLOT ((SLOT_TYPE)-1) #define BUFFER_SIZE PJMEDIA_CONF_SWITCH_BOARD_BUF_SIZE #define MAX_LEVEL (32767) #define MIN_LEVEL (-32768) /* * DON'T GET CONFUSED WITH TX/RX!! * * TX and RX directions are always viewed from the conference bridge's point * of view, and NOT from the port's point of view. So TX means the bridge * is transmitting to the port, RX means the bridge is receiving from the * port. */ /** * This is a port connected to conference bridge. */ struct conf_port { SLOT_TYPE slot; /**< Array of listeners. */ pj_str_t name; /**< Port name. */ pjmedia_port *port; /**< get_frame() and put_frame() */ pjmedia_port_op rx_setting; /**< Can we receive from this port */ pjmedia_port_op tx_setting; /**< Can we transmit to this port */ unsigned listener_cnt; /**< Number of listeners. */ SLOT_TYPE *listener_slots;/**< Array of listeners. */ unsigned transmitter_cnt;/**info.fmt.id == PJMEDIA_FORMAT_PCM && PJMEDIA_PIA_SPF(&port->info)*2 > BUFFER_SIZE - sizeof(pjmedia_frame)) { pj_assert(!"Too small buffer size for audio switchboard. " "Try increase PJMEDIA_CONF_SWITCH_BOARD_BUF_SIZE"); return PJ_ETOOSMALL; } /* Create port. */ conf_port = PJ_POOL_ZALLOC_T(pool, struct conf_port); /* Set name */ pj_strdup_with_null(pool, &conf_port->name, name); /* Default has tx and rx enabled. */ conf_port->rx_setting = PJMEDIA_PORT_ENABLE; conf_port->tx_setting = PJMEDIA_PORT_ENABLE; /* Default level adjustment is 128 (which means no adjustment) */ conf_port->tx_adj_level = NORMAL_LEVEL; conf_port->rx_adj_level = NORMAL_LEVEL; /* Create transmit flag array */ conf_port->listener_slots = (SLOT_TYPE*) pj_pool_zalloc(pool, conf->max_ports * sizeof(SLOT_TYPE)); PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM); /* Save some port's infos, for convenience. */ conf_port->port = port; conf_port->info = &port->info; conf_port->samples_per_frame = PJMEDIA_PIA_SPF(&port->info); /* Init pjmedia_frame structure in the TX buffer. */ f = (pjmedia_frame*)conf_port->tx_buf; f->buf = conf_port->tx_buf + sizeof(pjmedia_frame); /* Done */ *p_conf_port = conf_port; return PJ_SUCCESS; } /* * Create port zero for the sound device. */ static pj_status_t create_sound_port( pj_pool_t *pool, pjmedia_conf *conf ) { struct conf_port *conf_port; pj_str_t name = { "Master/sound", 12 }; pj_status_t status; status = create_conf_port(pool, conf, conf->master_port, &name, &conf_port); if (status != PJ_SUCCESS) return status; /* Add the port to the bridge */ conf_port->slot = 0; conf->ports[0] = conf_port; conf->port_cnt++; PJ_LOG(5,(THIS_FILE, "Sound device successfully created for port 0")); return PJ_SUCCESS; } /* * Create conference bridge. */ PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool, unsigned max_ports, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_conf **p_conf ) { pjmedia_conf *conf; const pj_str_t name = { "Conf", 4 }; pj_status_t status; /* Can only accept 16bits per sample, for now.. */ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); PJ_LOG(5,(THIS_FILE, "Creating conference bridge with %d ports", max_ports)); /* Create and init conf structure. */ conf = PJ_POOL_ZALLOC_T(pool, pjmedia_conf); PJ_ASSERT_RETURN(conf, PJ_ENOMEM); conf->ports = (struct conf_port**) pj_pool_zalloc(pool, max_ports*sizeof(void*)); PJ_ASSERT_RETURN(conf->ports, PJ_ENOMEM); conf->options = options; conf->max_ports = max_ports; /* Create and initialize the master port interface. */ conf->master_port = PJ_POOL_ZALLOC_T(pool, pjmedia_port); PJ_ASSERT_RETURN(conf->master_port, PJ_ENOMEM); pjmedia_port_info_init(&conf->master_port->info, &name, SIGNATURE, clock_rate, channel_count, bits_per_sample, samples_per_frame); conf->master_port->port_data.pdata = conf; conf->master_port->port_data.ldata = 0; conf->master_port->get_frame = &get_frame; conf->master_port->put_frame = &put_frame; conf->master_port->on_destroy = &destroy_port; /* Create port zero for sound device. */ status = create_sound_port(pool, conf); if (status != PJ_SUCCESS) return status; /* Create mutex. */ status = pj_mutex_create_recursive(pool, "conf", &conf->mutex); if (status != PJ_SUCCESS) return status; /* Done */ *p_conf = conf; return PJ_SUCCESS; } /* * Pause sound device. */ static pj_status_t pause_sound( pjmedia_conf *conf ) { /* Do nothing. */ PJ_UNUSED_ARG(conf); return PJ_SUCCESS; } /* * Resume sound device. */ static pj_status_t resume_sound( pjmedia_conf *conf ) { /* Do nothing. */ PJ_UNUSED_ARG(conf); return PJ_SUCCESS; } /** * Destroy conference bridge. */ PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf ) { PJ_ASSERT_RETURN(conf != NULL, PJ_EINVAL); /* Destroy mutex */ pj_mutex_destroy(conf->mutex); return PJ_SUCCESS; } /* * Destroy the master port (will destroy the conference) */ static pj_status_t destroy_port(pjmedia_port *this_port) { pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata; return pjmedia_conf_destroy(conf); } /* * Get port zero interface. */ PJ_DEF(pjmedia_port*) pjmedia_conf_get_master_port(pjmedia_conf *conf) { /* Sanity check. */ PJ_ASSERT_RETURN(conf != NULL, NULL); /* Can only return port interface when PJMEDIA_CONF_NO_DEVICE was * present in the option. */ PJ_ASSERT_RETURN((conf->options & PJMEDIA_CONF_NO_DEVICE) != 0, NULL); return conf->master_port; } /* * Set master port name. */ PJ_DEF(pj_status_t) pjmedia_conf_set_port0_name(pjmedia_conf *conf, const pj_str_t *name) { unsigned len; /* Sanity check. */ PJ_ASSERT_RETURN(conf != NULL && name != NULL, PJ_EINVAL); len = name->slen; if (len > sizeof(conf->master_name_buf)) len = sizeof(conf->master_name_buf); if (len > 0) pj_memcpy(conf->master_name_buf, name->ptr, len); conf->ports[0]->name.ptr = conf->master_name_buf; conf->ports[0]->name.slen = len; conf->master_port->info.name = conf->ports[0]->name; return PJ_SUCCESS; } /* * Add stream port to the conference bridge. */ PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf, pj_pool_t *pool, pjmedia_port *strm_port, const pj_str_t *port_name, unsigned *p_port ) { struct conf_port *conf_port; unsigned index; pj_status_t status; PJ_ASSERT_RETURN(conf && pool && strm_port, PJ_EINVAL); /* PJ_ASSERT_RETURN(conf->clock_rate == strm_port->info.clock_rate, PJMEDIA_ENCCLOCKRATE); PJ_ASSERT_RETURN(conf->channel_count == strm_port->info.channel_count, PJMEDIA_ENCCHANNEL); PJ_ASSERT_RETURN(conf->bits_per_sample == strm_port->info.bits_per_sample, PJMEDIA_ENCBITS); */ /* Port's samples per frame should be equal to or multiplication of * conference's samples per frame. */ /* Not sure if this is needed! PJ_ASSERT_RETURN((conf->samples_per_frame % strm_port->info.samples_per_frame==0) || (strm_port->info.samples_per_frame % conf->samples_per_frame==0), PJMEDIA_ENCSAMPLESPFRAME); */ /* If port_name is not specified, use the port's name */ if (!port_name) port_name = &strm_port->info.name; pj_mutex_lock(conf->mutex); if (conf->port_cnt >= conf->max_ports) { pj_assert(!"Too many ports"); pj_mutex_unlock(conf->mutex); return PJ_ETOOMANY; } /* Find empty port in the conference bridge. */ for (index=0; index < conf->max_ports; ++index) { if (conf->ports[index] == NULL) break; } pj_assert(index != conf->max_ports); /* Create conf port structure. */ status = create_conf_port(pool, conf, strm_port, port_name, &conf_port); if (status != PJ_SUCCESS) { pj_mutex_unlock(conf->mutex); return status; } /* Put the port. */ conf_port->slot = index; conf->ports[index] = conf_port; conf->port_cnt++; /* Done. */ if (p_port) { *p_port = index; } pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } /* * Add passive port. */ PJ_DEF(pj_status_t) pjmedia_conf_add_passive_port( pjmedia_conf *conf, pj_pool_t *pool, const pj_str_t *name, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, unsigned *p_slot, pjmedia_port **p_port ) { PJ_UNUSED_ARG(conf); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(name); PJ_UNUSED_ARG(clock_rate); PJ_UNUSED_ARG(channel_count); PJ_UNUSED_ARG(samples_per_frame); PJ_UNUSED_ARG(bits_per_sample); PJ_UNUSED_ARG(options); PJ_UNUSED_ARG(p_slot); PJ_UNUSED_ARG(p_port); return PJ_ENOTSUP; } /* * Change TX and RX settings for the port. */ PJ_DEF(pj_status_t) pjmedia_conf_configure_port( pjmedia_conf *conf, unsigned slot, pjmedia_port_op tx, pjmedia_port_op rx) { struct conf_port *conf_port; /* Check arguments */ PJ_ASSERT_RETURN(conf && slotmax_ports, PJ_EINVAL); pj_mutex_lock(conf->mutex); /* Port must be valid. */ conf_port = conf->ports[slot]; if (conf_port == NULL) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } if (tx != PJMEDIA_PORT_NO_CHANGE) conf_port->tx_setting = tx; if (rx != PJMEDIA_PORT_NO_CHANGE) conf_port->rx_setting = rx; pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } /* * Connect port. */ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf, unsigned src_slot, unsigned sink_slot, int level ) { struct conf_port *src_port, *dst_port; pj_bool_t start_sound = PJ_FALSE; pjmedia_audio_format_detail *src_afd, *dst_afd; unsigned i; /* Check arguments */ PJ_ASSERT_RETURN(conf && src_slotmax_ports && sink_slotmax_ports, PJ_EINVAL); /* For now, level MUST be zero. */ PJ_ASSERT_RETURN(level == 0, PJ_EINVAL); pj_mutex_lock(conf->mutex); /* Ports must be valid. */ src_port = conf->ports[src_slot]; dst_port = conf->ports[sink_slot]; if (!src_port || !dst_port) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } src_afd = pjmedia_format_get_audio_format_detail(&src_port->info->fmt, 1); dst_afd = pjmedia_format_get_audio_format_detail(&dst_port->info->fmt, 1); /* Format must match. */ if (src_port->info->fmt.id != dst_port->info->fmt.id || src_afd->avg_bps != dst_afd->avg_bps) { pj_mutex_unlock(conf->mutex); return PJMEDIA_ENOTCOMPATIBLE; } /* Clock rate must match. */ if (src_afd->clock_rate != dst_afd->clock_rate) { pj_mutex_unlock(conf->mutex); return PJMEDIA_ENCCLOCKRATE; } /* Channel count must match. */ if (src_afd->channel_count != dst_afd->channel_count) { pj_mutex_unlock(conf->mutex); return PJMEDIA_ENCCHANNEL; } /* Source and sink ptime must be equal or a multiplication factor. */ if ((src_afd->frame_time_usec % dst_afd->frame_time_usec != 0) && (dst_afd->frame_time_usec % src_afd->frame_time_usec != 0)) { pj_mutex_unlock(conf->mutex); return PJMEDIA_ENCSAMPLESPFRAME; } /* If sink is currently listening to other ports, it needs to be released * first before the new connection made. */ if (dst_port->transmitter_cnt > 0) { unsigned j; pj_bool_t transmitter_found = PJ_FALSE; pj_assert(dst_port->transmitter_cnt == 1); for (j=0; jmax_ports && !transmitter_found; ++j) { if (conf->ports[j]) { unsigned k; for (k=0; k < conf->ports[j]->listener_cnt; ++k) { if (conf->ports[j]->listener_slots[k] == sink_slot) { PJ_LOG(2,(THIS_FILE, "Connection [%d->%d] is " "disconnected for new connection [%d->%d]", j, sink_slot, src_slot, sink_slot)); pjmedia_conf_disconnect_port(conf, j, sink_slot); transmitter_found = PJ_TRUE; break; } } } } pj_assert(dst_port->transmitter_cnt == 0); } /* Check if connection has been made */ for (i=0; ilistener_cnt; ++i) { if (src_port->listener_slots[i] == sink_slot) break; } /* Update master port info shortcut, note that application may update * the master port info when the audio device needs to be reopened with * a new format to match to ports connection format. */ conf->ports[0]->samples_per_frame = PJMEDIA_PIA_SPF(conf->ports[0]->info); if (i == src_port->listener_cnt) { src_port->listener_slots[src_port->listener_cnt] = sink_slot; ++conf->connect_cnt; ++src_port->listener_cnt; ++dst_port->transmitter_cnt; if (conf->connect_cnt == 1) start_sound = 1; PJ_LOG(4,(THIS_FILE,"Port %d (%.*s) transmitting to port %d (%.*s)", src_slot, (int)src_port->name.slen, src_port->name.ptr, sink_slot, (int)dst_port->name.slen, dst_port->name.ptr)); } pj_mutex_unlock(conf->mutex); /* Sound device must be started without mutex, otherwise the * sound thread will deadlock (?) */ if (start_sound) resume_sound(conf); return PJ_SUCCESS; } /* * Disconnect port */ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf, unsigned src_slot, unsigned sink_slot ) { struct conf_port *src_port, *dst_port; unsigned i; /* Check arguments */ PJ_ASSERT_RETURN(conf && src_slotmax_ports && sink_slotmax_ports, PJ_EINVAL); pj_mutex_lock(conf->mutex); /* Ports must be valid. */ src_port = conf->ports[src_slot]; dst_port = conf->ports[sink_slot]; if (!src_port || !dst_port) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } /* Check if connection has been made */ for (i=0; ilistener_cnt; ++i) { if (src_port->listener_slots[i] == sink_slot) break; } if (i != src_port->listener_cnt) { pjmedia_frame_ext *f; pj_assert(src_port->listener_cnt > 0 && src_port->listener_cnt < conf->max_ports); pj_assert(dst_port->transmitter_cnt > 0 && dst_port->transmitter_cnt < conf->max_ports); pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE), src_port->listener_cnt, i); --conf->connect_cnt; --src_port->listener_cnt; --dst_port->transmitter_cnt; /* Cleanup listener TX buffer. */ f = (pjmedia_frame_ext*)dst_port->tx_buf; f->base.type = PJMEDIA_FRAME_TYPE_NONE; f->base.size = 0; f->samples_cnt = 0; f->subframe_cnt = 0; PJ_LOG(4,(THIS_FILE, "Port %d (%.*s) stop transmitting to port %d (%.*s)", src_slot, (int)src_port->name.slen, src_port->name.ptr, sink_slot, (int)dst_port->name.slen, dst_port->name.ptr)); } pj_mutex_unlock(conf->mutex); if (conf->connect_cnt == 0) { pause_sound(conf); } return PJ_SUCCESS; } /* * Get number of ports currently registered to the conference bridge. */ PJ_DEF(unsigned) pjmedia_conf_get_port_count(pjmedia_conf *conf) { return conf->port_cnt; } /* * Get total number of ports connections currently set up in the bridge. */ PJ_DEF(unsigned) pjmedia_conf_get_connect_count(pjmedia_conf *conf) { return conf->connect_cnt; } /* * Remove the specified port. */ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf, unsigned port ) { struct conf_port *conf_port; unsigned i; /* Check arguments */ PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL); /* Suspend the sound devices. * Don't want to remove port while port is being accessed by sound * device's threads! */ pj_mutex_lock(conf->mutex); /* Port must be valid. */ conf_port = conf->ports[port]; if (conf_port == NULL) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } conf_port->tx_setting = PJMEDIA_PORT_DISABLE; conf_port->rx_setting = PJMEDIA_PORT_DISABLE; /* Remove this port from transmit array of other ports. */ for (i=0; imax_ports; ++i) { unsigned j; struct conf_port *src_port; src_port = conf->ports[i]; if (!src_port) continue; if (src_port->listener_cnt == 0) continue; for (j=0; jlistener_cnt; ++j) { if (src_port->listener_slots[j] == port) { pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE), src_port->listener_cnt, j); pj_assert(conf->connect_cnt > 0); --conf->connect_cnt; --src_port->listener_cnt; break; } } } /* Update transmitter_cnt of ports we're transmitting to */ while (conf_port->listener_cnt) { unsigned dst_slot; struct conf_port *dst_port; pjmedia_frame_ext *f; dst_slot = conf_port->listener_slots[conf_port->listener_cnt-1]; dst_port = conf->ports[dst_slot]; --dst_port->transmitter_cnt; --conf_port->listener_cnt; pj_assert(conf->connect_cnt > 0); --conf->connect_cnt; /* Cleanup & reinit listener TX buffer. */ f = (pjmedia_frame_ext*)dst_port->tx_buf; f->base.type = PJMEDIA_FRAME_TYPE_NONE; f->base.size = 0; f->samples_cnt = 0; f->subframe_cnt = 0; } /* Remove the port. */ conf->ports[port] = NULL; --conf->port_cnt; pj_mutex_unlock(conf->mutex); /* Stop sound if there's no connection. */ if (conf->connect_cnt == 0) { pause_sound(conf); } return PJ_SUCCESS; } /* * Enum ports. */ PJ_DEF(pj_status_t) pjmedia_conf_enum_ports( pjmedia_conf *conf, unsigned ports[], unsigned *p_count ) { unsigned i, count=0; PJ_ASSERT_RETURN(conf && p_count && ports, PJ_EINVAL); /* Lock mutex */ pj_mutex_lock(conf->mutex); for (i=0; imax_ports && count<*p_count; ++i) { if (!conf->ports[i]) continue; ports[count++] = i; } /* Unlock mutex */ pj_mutex_unlock(conf->mutex); *p_count = count; return PJ_SUCCESS; } /* * Get port info */ PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf, unsigned slot, pjmedia_conf_port_info *info) { struct conf_port *conf_port; const pjmedia_audio_format_detail *afd; /* Check arguments */ PJ_ASSERT_RETURN(conf && slotmax_ports, PJ_EINVAL); /* Lock mutex */ pj_mutex_lock(conf->mutex); /* Port must be valid. */ conf_port = conf->ports[slot]; if (conf_port == NULL) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } afd = pjmedia_format_get_audio_format_detail(&conf_port->info->fmt, 1); pj_bzero(info, sizeof(pjmedia_conf_port_info)); info->slot = slot; info->name = conf_port->name; info->tx_setting = conf_port->tx_setting; info->rx_setting = conf_port->rx_setting; info->listener_cnt = conf_port->listener_cnt; info->listener_slots = conf_port->listener_slots; info->transmitter_cnt = conf_port->transmitter_cnt; info->clock_rate = afd->clock_rate; info->channel_count = afd->channel_count; info->samples_per_frame = conf_port->samples_per_frame; info->bits_per_sample = afd->bits_per_sample; info->format = conf_port->port->info.fmt; info->tx_adj_level = conf_port->tx_adj_level - NORMAL_LEVEL; info->rx_adj_level = conf_port->rx_adj_level - NORMAL_LEVEL; /* Unlock mutex */ pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_conf_get_ports_info(pjmedia_conf *conf, unsigned *size, pjmedia_conf_port_info info[]) { unsigned i, count=0; PJ_ASSERT_RETURN(conf && size && info, PJ_EINVAL); /* Lock mutex */ pj_mutex_lock(conf->mutex); for (i=0; imax_ports && count<*size; ++i) { if (!conf->ports[i]) continue; pjmedia_conf_get_port_info(conf, i, &info[count]); ++count; } /* Unlock mutex */ pj_mutex_unlock(conf->mutex); *size = count; return PJ_SUCCESS; } /* * Get signal level. */ PJ_DEF(pj_status_t) pjmedia_conf_get_signal_level( pjmedia_conf *conf, unsigned slot, unsigned *tx_level, unsigned *rx_level) { struct conf_port *conf_port; /* Check arguments */ PJ_ASSERT_RETURN(conf && slotmax_ports, PJ_EINVAL); /* Lock mutex */ pj_mutex_lock(conf->mutex); /* Port must be valid. */ conf_port = conf->ports[slot]; if (conf_port == NULL) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } if (tx_level != NULL) { *tx_level = conf_port->tx_level; } if (rx_level != NULL) *rx_level = conf_port->rx_level; /* Unlock mutex */ pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } /* * Adjust RX level of individual port. */ PJ_DEF(pj_status_t) pjmedia_conf_adjust_rx_level( pjmedia_conf *conf, unsigned slot, int adj_level ) { struct conf_port *conf_port; /* Check arguments */ PJ_ASSERT_RETURN(conf && slotmax_ports, PJ_EINVAL); /* Value must be from -128 to +127 */ /* Disabled, you can put more than +127, at your own risk: PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL); */ PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL); /* Lock mutex */ pj_mutex_lock(conf->mutex); /* Port must be valid. */ conf_port = conf->ports[slot]; if (conf_port == NULL) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } /* Level adjustment is applicable only for ports that work with raw PCM. */ PJ_ASSERT_RETURN(conf_port->info->fmt.id == PJMEDIA_FORMAT_L16, PJ_EIGNORED); /* Set normalized adjustment level. */ conf_port->rx_adj_level = adj_level + NORMAL_LEVEL; /* Unlock mutex */ pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } /* * Adjust TX level of individual port. */ PJ_DEF(pj_status_t) pjmedia_conf_adjust_tx_level( pjmedia_conf *conf, unsigned slot, int adj_level ) { struct conf_port *conf_port; /* Check arguments */ PJ_ASSERT_RETURN(conf && slotmax_ports, PJ_EINVAL); /* Value must be from -128 to +127 */ /* Disabled, you can put more than +127,, at your own risk: PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL); */ PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL); /* Lock mutex */ pj_mutex_lock(conf->mutex); /* Port must be valid. */ conf_port = conf->ports[slot]; if (conf_port == NULL) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } /* Level adjustment is applicable only for ports that work with raw PCM. */ PJ_ASSERT_RETURN(conf_port->info->fmt.id == PJMEDIA_FORMAT_L16, PJ_EIGNORED); /* Set normalized adjustment level. */ conf_port->tx_adj_level = adj_level + NORMAL_LEVEL; /* Unlock mutex */ pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } /* Deliver frm_src to a listener port, eventually call port's put_frame() * when samples count in the frm_dst are equal to port's samples_per_frame. */ static pj_status_t write_frame(struct conf_port *cport_dst, const pjmedia_frame *frm_src) { pjmedia_frame *frm_dst = (pjmedia_frame*)cport_dst->tx_buf; PJ_TODO(MAKE_SURE_DEST_FRAME_HAS_ENOUGH_SPACE); frm_dst->type = frm_src->type; frm_dst->timestamp = cport_dst->ts_tx; if (frm_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) { pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frm_src; pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst; unsigned i; for (i = 0; i < f_src->subframe_cnt; ++i) { pjmedia_frame_ext_subframe *sf; /* Copy frame to listener's TX buffer. */ sf = pjmedia_frame_ext_get_subframe(f_src, i); pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen, f_src->samples_cnt / f_src->subframe_cnt); /* Check if it's time to deliver the TX buffer to listener, * i.e: samples count in TX buffer equal to listener's * samples per frame. */ if (f_dst->samples_cnt >= cport_dst->samples_per_frame) { if (cport_dst->slot) { pjmedia_port_put_frame(cport_dst->port, (pjmedia_frame*)f_dst); /* Reset TX buffer. */ f_dst->subframe_cnt = 0; f_dst->samples_cnt = 0; } /* Update TX timestamp. */ pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->samples_per_frame); } } } else if (frm_src->type == PJMEDIA_FRAME_TYPE_AUDIO) { pj_int16_t *f_start, *f_end; f_start = (pj_int16_t*)frm_src->buf; f_end = f_start + (frm_src->size >> 1); while (f_start < f_end) { unsigned nsamples_to_copy, nsamples_req; /* Copy frame to listener's TX buffer. * Note that if the destination is port 0, just copy the whole * available samples. */ nsamples_to_copy = f_end - f_start; nsamples_req = cport_dst->samples_per_frame - (frm_dst->size>>1); if (cport_dst->slot && nsamples_to_copy > nsamples_req) nsamples_to_copy = nsamples_req; /* Adjust TX level. */ if (cport_dst->tx_adj_level != NORMAL_LEVEL) { pj_int16_t *p, *p_end; p = f_start; p_end = p + nsamples_to_copy; while (p < p_end) { pj_int32_t itemp = *p; /* Adjust the level */ itemp = (itemp * cport_dst->tx_adj_level) >> 7; /* Clip the signal if it's too loud */ if (itemp > MAX_LEVEL) itemp = MAX_LEVEL; else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL; /* Put back in the buffer. */ *p = (pj_int16_t)itemp; ++p; } } pjmedia_copy_samples((pj_int16_t*)frm_dst->buf + (frm_dst->size>>1), f_start, nsamples_to_copy); frm_dst->size += nsamples_to_copy << 1; f_start += nsamples_to_copy; /* Check if it's time to deliver the TX buffer to listener, * i.e: samples count in TX buffer equal to listener's * samples per frame. Note that for destination port 0 this * function will just populate all samples in the TX buffer. */ if (cport_dst->slot == 0) { /* Update TX timestamp. */ pj_add_timestamp32(&cport_dst->ts_tx, nsamples_to_copy); } else if ((frm_dst->size >> 1) == cport_dst->samples_per_frame) { pjmedia_port_put_frame(cport_dst->port, frm_dst); /* Reset TX buffer. */ frm_dst->size = 0; /* Update TX timestamp. */ pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->samples_per_frame); } } } else if (frm_src->type == PJMEDIA_FRAME_TYPE_NONE) { /* Check port format. */ if (cport_dst->port && cport_dst->port->info.fmt.id == PJMEDIA_FORMAT_L16) { /* When there is already some samples in listener's TX buffer, * pad the buffer with "zero samples". */ if (frm_dst->size != 0) { pjmedia_zero_samples((pj_int16_t*)frm_dst->buf, cport_dst->samples_per_frame - (frm_dst->size>>1)); frm_dst->type = PJMEDIA_FRAME_TYPE_AUDIO; frm_dst->size = cport_dst->samples_per_frame << 1; if (cport_dst->slot) { pjmedia_port_put_frame(cport_dst->port, frm_dst); /* Reset TX buffer. */ frm_dst->size = 0; } /* Update TX timestamp. */ pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->samples_per_frame); } } else { pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst; if (f_dst->samples_cnt != 0) { frm_dst->type = PJMEDIA_FRAME_TYPE_EXTENDED; pjmedia_frame_ext_append_subframe(f_dst, NULL, 0, (pj_uint16_t) (cport_dst->samples_per_frame - f_dst->samples_cnt)); if (cport_dst->slot) { pjmedia_port_put_frame(cport_dst->port, frm_dst); /* Reset TX buffer. */ f_dst->subframe_cnt = 0; f_dst->samples_cnt = 0; } /* Update TX timestamp. */ pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->samples_per_frame); } } /* Synchronize clock. */ while (pj_cmp_timestamp(&cport_dst->ts_clock, &cport_dst->ts_tx) > 0) { frm_dst->type = PJMEDIA_FRAME_TYPE_NONE; frm_dst->timestamp = cport_dst->ts_tx; if (cport_dst->slot) pjmedia_port_put_frame(cport_dst->port, frm_dst); /* Update TX timestamp. */ pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->samples_per_frame); } } return PJ_SUCCESS; } /* * Player callback. */ static pj_status_t get_frame(pjmedia_port *this_port, pjmedia_frame *frame) { pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata; unsigned ci, i; /* Lock mutex */ pj_mutex_lock(conf->mutex); /* Call get_frame() from all ports (except port 0) that has * receiver and distribute the frame (put the frame to the destination * port's buffer to accommodate different ptime, and ultimately call * put_frame() of that port) to ports that are receiving from this port. */ for (i=1, ci=1; imax_ports && ciport_cnt; ++i) { struct conf_port *cport = conf->ports[i]; unsigned master_samples_per_frame; /* Skip empty port. */ if (!cport) continue; /* Var "ci" is to count how many ports have been visited so far. */ ++ci; master_samples_per_frame = PJMEDIA_PIA_SPF(&conf->master_port->info); /* Update clock of the port. */ pj_add_timestamp32(&cport->ts_clock, master_samples_per_frame); /* Skip if we're not allowed to receive from this port or * the port doesn't have listeners. */ if (cport->rx_setting == PJMEDIA_PORT_DISABLE || cport->listener_cnt == 0) { cport->rx_level = 0; pj_add_timestamp32(&cport->ts_rx, master_samples_per_frame); continue; } /* Get frame from each port, put it to the listener TX buffer, * and eventually call put_frame() of the listener. This loop * will also make sure the ptime between conf & port synchronized. */ while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_rx) > 0) { pjmedia_frame *f = (pjmedia_frame*)conf->buf; pj_status_t status; unsigned j; pj_int32_t level = 0; pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame); f->buf = &conf->buf[sizeof(pjmedia_frame)]; f->size = cport->samples_per_frame<<1; /* Get frame from port. */ status = pjmedia_port_get_frame(cport->port, f); if (status != PJ_SUCCESS) continue; /* Calculate & adjust RX level. */ if (f->type == PJMEDIA_FRAME_TYPE_AUDIO) { if (cport->rx_adj_level != NORMAL_LEVEL) { pj_int16_t *p = (pj_int16_t*)f->buf; pj_int16_t *end; end = p + (f->size >> 1); while (p < end) { pj_int32_t itemp = *p; /* Adjust the level */ itemp = (itemp * cport->rx_adj_level) >> 7; /* Clip the signal if it's too loud */ if (itemp > MAX_LEVEL) itemp = MAX_LEVEL; else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL; level += PJ_ABS(itemp); /* Put back in the buffer. */ *p = (pj_int16_t)itemp; ++p; } level /= (f->size >> 1); } else { level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf, f->size >> 1); } } else if (f->type == PJMEDIA_FRAME_TYPE_EXTENDED) { /* For extended frame, level is unknown, so we just set * it to NORMAL_LEVEL. */ level = NORMAL_LEVEL; } cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff; /* Put the frame to all listeners. */ for (j=0; j < cport->listener_cnt; ++j) { struct conf_port *listener; listener = conf->ports[cport->listener_slots[j]]; /* Skip if this listener doesn't want to receive audio */ if (listener->tx_setting == PJMEDIA_PORT_DISABLE) { pj_add_timestamp32(&listener->ts_tx, listener->samples_per_frame); listener->tx_level = 0; continue; } status = write_frame(listener, f); if (status != PJ_SUCCESS) { listener->tx_level = 0; continue; } /* Set listener TX level based on transmitter RX level & * listener TX level. */ listener->tx_level = (cport->rx_level * listener->tx_adj_level) >> 8; } } } /* Keep alive. Update TX timestamp and send frame type NONE to all * underflow ports at their own clock. */ for (i=1, ci=1; imax_ports && ciport_cnt; ++i) { struct conf_port *cport = conf->ports[i]; /* Skip empty port. */ if (!cport) continue; /* Var "ci" is to count how many ports have been visited so far. */ ++ci; if (cport->tx_setting==PJMEDIA_PORT_MUTE || cport->transmitter_cnt==0) { pjmedia_frame_ext *f; /* Clear left-over samples in tx_buffer, if any, so that it won't * be transmitted next time we have audio signal. */ f = (pjmedia_frame_ext*)cport->tx_buf; f->base.type = PJMEDIA_FRAME_TYPE_NONE; f->base.size = 0; f->samples_cnt = 0; f->subframe_cnt = 0; cport->tx_level = 0; while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_tx) > 0) { if (cport->tx_setting == PJMEDIA_PORT_ENABLE) { pjmedia_frame tmp_f; tmp_f.timestamp = cport->ts_tx; tmp_f.type = PJMEDIA_FRAME_TYPE_NONE; tmp_f.buf = NULL; tmp_f.size = 0; pjmedia_port_put_frame(cport->port, &tmp_f); pj_add_timestamp32(&cport->ts_tx, cport->samples_per_frame); } } } } /* Return sound playback frame. */ do { struct conf_port *this_cport = conf->ports[this_port->port_data.ldata]; pjmedia_frame *f_src = (pjmedia_frame*) this_cport->tx_buf; frame->type = f_src->type; if (f_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) { pjmedia_frame_ext *f_src_ = (pjmedia_frame_ext*)f_src; pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frame; pjmedia_frame_ext_subframe *sf; unsigned samples_per_subframe; if (f_src_->samples_cnt < this_cport->samples_per_frame) { f_dst->base.type = PJMEDIA_FRAME_TYPE_NONE; f_dst->samples_cnt = 0; f_dst->subframe_cnt = 0; break; } f_dst->samples_cnt = 0; f_dst->subframe_cnt = 0; i = 0; samples_per_subframe = f_src_->samples_cnt / f_src_->subframe_cnt; while (f_dst->samples_cnt < this_cport->samples_per_frame) { sf = pjmedia_frame_ext_get_subframe(f_src_, i++); pj_assert(sf); pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen, samples_per_subframe); } /* Shift left TX buffer. */ pjmedia_frame_ext_pop_subframes(f_src_, i); } else if (f_src->type == PJMEDIA_FRAME_TYPE_AUDIO) { if ((f_src->size>>1) < this_cport->samples_per_frame) { frame->type = PJMEDIA_FRAME_TYPE_NONE; frame->size = 0; break; } pjmedia_copy_samples((pj_int16_t*)frame->buf, (pj_int16_t*)f_src->buf, this_cport->samples_per_frame); frame->size = this_cport->samples_per_frame << 1; /* Shift left TX buffer. */ f_src->size -= frame->size; if (f_src->size) pjmedia_move_samples((pj_int16_t*)f_src->buf, (pj_int16_t*)f_src->buf + this_cport->samples_per_frame, f_src->size >> 1); } else { /* PJMEDIA_FRAME_TYPE_NONE */ pjmedia_frame_ext *f_src_ = (pjmedia_frame_ext*)f_src; /* Reset source/TX buffer */ f_src_->base.size = 0; f_src_->samples_cnt = 0; f_src_->subframe_cnt = 0; } } while (0); /* Unlock mutex */ pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } /* * Recorder callback. */ static pj_status_t put_frame(pjmedia_port *this_port, pjmedia_frame *f) { pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata; struct conf_port *cport; unsigned j; pj_int32_t level = 0; /* Lock mutex */ pj_mutex_lock(conf->mutex); /* Get conf port of this port */ cport = conf->ports[this_port->port_data.ldata]; if (cport == NULL) { /* Unlock mutex */ pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame); /* Skip if this port is muted/disabled. */ if (cport->rx_setting == PJMEDIA_PORT_DISABLE) { cport->rx_level = 0; /* Unlock mutex */ pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } /* Skip if no port is listening to the microphone */ if (cport->listener_cnt == 0) { cport->rx_level = 0; /* Unlock mutex */ pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } /* Calculate & adjust RX level. */ if (f->type == PJMEDIA_FRAME_TYPE_AUDIO) { if (cport->rx_adj_level != NORMAL_LEVEL) { pj_int16_t *p = (pj_int16_t*)f->buf; pj_int16_t *end; end = p + (f->size >> 1); while (p < end) { pj_int32_t itemp = *p; /* Adjust the level */ itemp = (itemp * cport->rx_adj_level) >> 7; /* Clip the signal if it's too loud */ if (itemp > MAX_LEVEL) itemp = MAX_LEVEL; else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL; level += PJ_ABS(itemp); /* Put back in the buffer. */ *p = (pj_int16_t)itemp; ++p; } level /= (f->size >> 1); } else { level = pjmedia_calc_avg_signal((const pj_int16_t*)f->buf, f->size >> 1); } } else if (f->type == PJMEDIA_FRAME_TYPE_EXTENDED) { /* For extended frame, level is unknown, so we just set * it to NORMAL_LEVEL. */ level = NORMAL_LEVEL; } cport->rx_level = pjmedia_linear2ulaw(level) ^ 0xff; /* Put the frame to all listeners. */ for (j=0; j < cport->listener_cnt; ++j) { struct conf_port *listener; pj_status_t status; listener = conf->ports[cport->listener_slots[j]]; /* Skip if this listener doesn't want to receive audio */ if (listener->tx_setting == PJMEDIA_PORT_DISABLE) { pj_add_timestamp32(&listener->ts_tx, listener->samples_per_frame); listener->tx_level = 0; continue; } /* Skip loopback for now. */ if (listener == cport) { pj_add_timestamp32(&listener->ts_tx, listener->samples_per_frame); listener->tx_level = 0; continue; } status = write_frame(listener, f); if (status != PJ_SUCCESS) { listener->tx_level = 0; continue; } /* Set listener TX level based on transmitter RX level & listener * TX level. */ listener->tx_level = (cport->rx_level * listener->tx_adj_level) >> 8; } /* Unlock mutex */ pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } #endif ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/conference.c ================================================ /* $Id: conference.c 4198 2012-07-05 10:25:46Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(PJMEDIA_CONF_USE_SWITCH_BOARD) || PJMEDIA_CONF_USE_SWITCH_BOARD==0 /* CONF_DEBUG enables detailed operation of the conference bridge. * Beware that it prints large amounts of logs (several lines per frame). */ //#define CONF_DEBUG #ifdef CONF_DEBUG # include # define TRACE_(x) PJ_LOG(5,x) #else # define TRACE_(x) #endif /* REC_FILE macro enables recording of the samples written to the sound * device. The file contains RAW PCM data with no header, and has the * same settings (clock rate etc) as the conference bridge. * This should only be enabled when debugging audio quality *only*. */ //#define REC_FILE "confrec.pcm" #ifdef REC_FILE static FILE *fhnd_rec; #endif #define THIS_FILE "conference.c" #define RX_BUF_COUNT PJMEDIA_SOUND_BUFFER_COUNT #define BYTES_PER_SAMPLE 2 #define SIGNATURE PJMEDIA_CONF_BRIDGE_SIGNATURE #define SIGNATURE_PORT PJMEDIA_SIG_PORT_CONF_PASV /* Normal level is hardcodec to 128 in all over places */ #define NORMAL_LEVEL 128 #define SLOT_TYPE unsigned #define INVALID_SLOT ((SLOT_TYPE)-1) /* These are settings to control the adaptivity of changes in the * signal level of the ports, so that sudden change in signal level * in the port does not cause misaligned signal (which causes noise). */ #define ATTACK_A (conf->clock_rate / conf->samples_per_frame) #define ATTACK_B 1 #define DECAY_A 0 #define DECAY_B 1 #define SIMPLE_AGC(last, target) \ if (target >= last) \ target = (ATTACK_A*(last+1)+ATTACK_B*target)/(ATTACK_A+ATTACK_B); \ else \ target = (DECAY_A*last+DECAY_B*target)/(DECAY_A+DECAY_B) #define MAX_LEVEL (32767) #define MIN_LEVEL (-32768) #define IS_OVERFLOW(s) ((s > MAX_LEVEL) || (s < MIN_LEVEL)) /* * DON'T GET CONFUSED WITH TX/RX!! * * TX and RX directions are always viewed from the conference bridge's point * of view, and NOT from the port's point of view. So TX means the bridge * is transmitting to the port, RX means the bridge is receiving from the * port. */ /** * This is a port connected to conference bridge. */ struct conf_port { pj_str_t name; /**< Port name. */ pjmedia_port *port; /**< get_frame() and put_frame() */ pjmedia_port_op rx_setting; /**< Can we receive from this port */ pjmedia_port_op tx_setting; /**< Can we transmit to this port */ unsigned listener_cnt; /**< Number of listeners. */ SLOT_TYPE *listener_slots;/**< Array of listeners. */ unsigned transmitter_cnt;/**name, name); /* Default has tx and rx enabled. */ conf_port->rx_setting = PJMEDIA_PORT_ENABLE; conf_port->tx_setting = PJMEDIA_PORT_ENABLE; /* Default level adjustment is 128 (which means no adjustment) */ conf_port->tx_adj_level = NORMAL_LEVEL; conf_port->rx_adj_level = NORMAL_LEVEL; /* Create transmit flag array */ conf_port->listener_slots = (SLOT_TYPE*) pj_pool_zalloc(pool, conf->max_ports * sizeof(SLOT_TYPE)); PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM); /* Save some port's infos, for convenience. */ if (port) { pjmedia_audio_format_detail *afd; afd = pjmedia_format_get_audio_format_detail(&port->info.fmt, 1); conf_port->port = port; conf_port->clock_rate = afd->clock_rate; conf_port->samples_per_frame = PJMEDIA_AFD_SPF(afd); conf_port->channel_count = afd->channel_count; } else { conf_port->port = NULL; conf_port->clock_rate = conf->clock_rate; conf_port->samples_per_frame = conf->samples_per_frame; conf_port->channel_count = conf->channel_count; } /* If port's clock rate is different than conference's clock rate, * create a resample sessions. */ if (conf_port->clock_rate != conf->clock_rate) { pj_bool_t high_quality; pj_bool_t large_filter; high_quality = ((conf->options & PJMEDIA_CONF_USE_LINEAR)==0); large_filter = ((conf->options & PJMEDIA_CONF_SMALL_FILTER)==0); /* Create resample for rx buffer. */ status = pjmedia_resample_create( pool, high_quality, large_filter, conf->channel_count, conf_port->clock_rate,/* Rate in */ conf->clock_rate, /* Rate out */ conf->samples_per_frame * conf_port->clock_rate / conf->clock_rate, &conf_port->rx_resample); if (status != PJ_SUCCESS) return status; /* Create resample for tx buffer. */ status = pjmedia_resample_create(pool, high_quality, large_filter, conf->channel_count, conf->clock_rate, /* Rate in */ conf_port->clock_rate, /* Rate out */ conf->samples_per_frame, &conf_port->tx_resample); if (status != PJ_SUCCESS) return status; } /* * Initialize rx and tx buffer, only when port's samples per frame or * port's clock rate or channel number is different then the conference * bridge settings. */ if (conf_port->clock_rate != conf->clock_rate || conf_port->channel_count != conf->channel_count || conf_port->samples_per_frame != conf->samples_per_frame) { unsigned port_ptime, conf_ptime, buff_ptime; port_ptime = conf_port->samples_per_frame / conf_port->channel_count * 1000 / conf_port->clock_rate; conf_ptime = conf->samples_per_frame / conf->channel_count * 1000 / conf->clock_rate; /* Calculate the size (in ptime) for the port buffer according to * this formula: * - if either ptime is an exact multiple of the other, then use * the larger ptime (e.g. 20ms and 40ms, use 40ms). * - if not, then the ptime is sum of both ptimes (e.g. 20ms * and 30ms, use 50ms) */ if (port_ptime > conf_ptime) { buff_ptime = port_ptime; if (port_ptime % conf_ptime) buff_ptime += conf_ptime; } else { buff_ptime = conf_ptime; if (conf_ptime % port_ptime) buff_ptime += port_ptime; } /* Create RX buffer. */ //conf_port->rx_buf_cap = (unsigned)(conf_port->samples_per_frame + // conf->samples_per_frame * // conf_port->clock_rate * 1.0 / // conf->clock_rate + 0.5); conf_port->rx_buf_cap = conf_port->clock_rate * buff_ptime / 1000; if (conf_port->channel_count > conf->channel_count) conf_port->rx_buf_cap *= conf_port->channel_count; else conf_port->rx_buf_cap *= conf->channel_count; conf_port->rx_buf_count = 0; conf_port->rx_buf = (pj_int16_t*) pj_pool_alloc(pool, conf_port->rx_buf_cap * sizeof(conf_port->rx_buf[0])); PJ_ASSERT_RETURN(conf_port->rx_buf, PJ_ENOMEM); /* Create TX buffer. */ conf_port->tx_buf_cap = conf_port->rx_buf_cap; conf_port->tx_buf_count = 0; conf_port->tx_buf = (pj_int16_t*) pj_pool_alloc(pool, conf_port->tx_buf_cap * sizeof(conf_port->tx_buf[0])); PJ_ASSERT_RETURN(conf_port->tx_buf, PJ_ENOMEM); } /* Create mix buffer. */ conf_port->mix_buf = (pj_int32_t*) pj_pool_zalloc(pool, conf->samples_per_frame * sizeof(conf_port->mix_buf[0])); PJ_ASSERT_RETURN(conf_port->mix_buf, PJ_ENOMEM); conf_port->last_mix_adj = NORMAL_LEVEL; /* Done */ *p_conf_port = conf_port; return PJ_SUCCESS; } /* * Add passive port. */ static pj_status_t create_pasv_port( pjmedia_conf *conf, pj_pool_t *pool, const pj_str_t *name, pjmedia_port *port, struct conf_port **p_conf_port) { struct conf_port *conf_port; pj_status_t status; unsigned ptime; /* Create port */ status = create_conf_port(pool, conf, port, name, &conf_port); if (status != PJ_SUCCESS) return status; /* Passive port has delay buf. */ ptime = conf->samples_per_frame * 1000 / conf->clock_rate / conf->channel_count; status = pjmedia_delay_buf_create(pool, name->ptr, conf->clock_rate, conf->samples_per_frame, conf->channel_count, RX_BUF_COUNT * ptime, /* max delay */ 0, /* options */ &conf_port->delay_buf); if (status != PJ_SUCCESS) return status; *p_conf_port = conf_port; return PJ_SUCCESS; } /* * Create port zero for the sound device. */ static pj_status_t create_sound_port( pj_pool_t *pool, pjmedia_conf *conf ) { struct conf_port *conf_port; pj_str_t name = { "Master/sound", 12 }; pj_status_t status; status = create_pasv_port(conf, pool, &name, NULL, &conf_port); if (status != PJ_SUCCESS) return status; /* Create sound device port: */ if ((conf->options & PJMEDIA_CONF_NO_DEVICE) == 0) { pjmedia_aud_stream *strm; pjmedia_aud_param param; /* * If capture is disabled then create player only port. * Otherwise create bidirectional sound device port. */ if (conf->options & PJMEDIA_CONF_NO_MIC) { status = pjmedia_snd_port_create_player(pool, -1, conf->clock_rate, conf->channel_count, conf->samples_per_frame, conf->bits_per_sample, 0, /* options */ &conf->snd_dev_port); } else { status = pjmedia_snd_port_create( pool, -1, -1, conf->clock_rate, conf->channel_count, conf->samples_per_frame, conf->bits_per_sample, 0, /* Options */ &conf->snd_dev_port); } if (status != PJ_SUCCESS) return status; strm = pjmedia_snd_port_get_snd_stream(conf->snd_dev_port); status = pjmedia_aud_stream_get_param(strm, ¶m); if (status == PJ_SUCCESS) { pjmedia_aud_dev_info snd_dev_info; if (conf->options & PJMEDIA_CONF_NO_MIC) pjmedia_aud_dev_get_info(param.play_id, &snd_dev_info); else pjmedia_aud_dev_get_info(param.rec_id, &snd_dev_info); pj_strdup2_with_null(pool, &conf_port->name, snd_dev_info.name); } PJ_LOG(5,(THIS_FILE, "Sound device successfully created for port 0")); } /* Add the port to the bridge */ conf->ports[0] = conf_port; conf->port_cnt++; return PJ_SUCCESS; } /* * Create conference bridge. */ PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool, unsigned max_ports, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_conf **p_conf ) { pjmedia_conf *conf; const pj_str_t name = { "Conf", 4 }; pj_status_t status; /* Can only accept 16bits per sample, for now.. */ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); PJ_LOG(5,(THIS_FILE, "Creating conference bridge with %d ports", max_ports)); /* Create and init conf structure. */ conf = PJ_POOL_ZALLOC_T(pool, pjmedia_conf); PJ_ASSERT_RETURN(conf, PJ_ENOMEM); conf->ports = (struct conf_port**) pj_pool_zalloc(pool, max_ports*sizeof(void*)); PJ_ASSERT_RETURN(conf->ports, PJ_ENOMEM); conf->options = options; conf->max_ports = max_ports; conf->clock_rate = clock_rate; conf->channel_count = channel_count; conf->samples_per_frame = samples_per_frame; conf->bits_per_sample = bits_per_sample; /* Create and initialize the master port interface. */ conf->master_port = PJ_POOL_ZALLOC_T(pool, pjmedia_port); PJ_ASSERT_RETURN(conf->master_port, PJ_ENOMEM); pjmedia_port_info_init(&conf->master_port->info, &name, SIGNATURE, clock_rate, channel_count, bits_per_sample, samples_per_frame); conf->master_port->port_data.pdata = conf; conf->master_port->port_data.ldata = 0; conf->master_port->get_frame = &get_frame; conf->master_port->put_frame = &put_frame; conf->master_port->on_destroy = &destroy_port; /* Create port zero for sound device. */ status = create_sound_port(pool, conf); if (status != PJ_SUCCESS) { pjmedia_conf_destroy(conf); return status; } /* Create mutex. */ status = pj_mutex_create_recursive(pool, "conf", &conf->mutex); if (status != PJ_SUCCESS) { pjmedia_conf_destroy(conf); return status; } /* If sound device was created, connect sound device to the * master port. */ if (conf->snd_dev_port) { status = pjmedia_snd_port_connect( conf->snd_dev_port, conf->master_port ); if (status != PJ_SUCCESS) { pjmedia_conf_destroy(conf); return status; } } /* Done */ *p_conf = conf; return PJ_SUCCESS; } /* * Pause sound device. */ static pj_status_t pause_sound( pjmedia_conf *conf ) { /* Do nothing. */ PJ_UNUSED_ARG(conf); return PJ_SUCCESS; } /* * Resume sound device. */ static pj_status_t resume_sound( pjmedia_conf *conf ) { /* Do nothing. */ PJ_UNUSED_ARG(conf); return PJ_SUCCESS; } /** * Destroy conference bridge. */ PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf ) { unsigned i, ci; PJ_ASSERT_RETURN(conf != NULL, PJ_EINVAL); /* Destroy sound device port. */ if (conf->snd_dev_port) { pjmedia_snd_port_destroy(conf->snd_dev_port); conf->snd_dev_port = NULL; } /* Destroy delay buf of all (passive) ports. */ for (i=0, ci=0; imax_ports && ciport_cnt; ++i) { struct conf_port *cport; cport = conf->ports[i]; if (!cport) continue; ++ci; if (cport->delay_buf) { pjmedia_delay_buf_destroy(cport->delay_buf); cport->delay_buf = NULL; } } /* Destroy mutex */ if (conf->mutex) pj_mutex_destroy(conf->mutex); return PJ_SUCCESS; } /* * Destroy the master port (will destroy the conference) */ static pj_status_t destroy_port(pjmedia_port *this_port) { pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata; return pjmedia_conf_destroy(conf); } static pj_status_t destroy_port_pasv(pjmedia_port *this_port) { pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata; struct conf_port *port = conf->ports[this_port->port_data.ldata]; pj_status_t status; status = pjmedia_delay_buf_destroy(port->delay_buf); if (status == PJ_SUCCESS) port->delay_buf = NULL; return status; } /* * Get port zero interface. */ PJ_DEF(pjmedia_port*) pjmedia_conf_get_master_port(pjmedia_conf *conf) { /* Sanity check. */ PJ_ASSERT_RETURN(conf != NULL, NULL); /* Can only return port interface when PJMEDIA_CONF_NO_DEVICE was * present in the option. */ PJ_ASSERT_RETURN((conf->options & PJMEDIA_CONF_NO_DEVICE) != 0, NULL); return conf->master_port; } /* * Set master port name. */ PJ_DEF(pj_status_t) pjmedia_conf_set_port0_name(pjmedia_conf *conf, const pj_str_t *name) { pj_size_t len; /* Sanity check. */ PJ_ASSERT_RETURN(conf != NULL && name != NULL, PJ_EINVAL); len = name->slen; if (len > sizeof(conf->master_name_buf)) len = sizeof(conf->master_name_buf); if (len > 0) pj_memcpy(conf->master_name_buf, name->ptr, len); conf->ports[0]->name.ptr = conf->master_name_buf; conf->ports[0]->name.slen = len; if (conf->master_port) conf->master_port->info.name = conf->ports[0]->name; return PJ_SUCCESS; } /* * Add stream port to the conference bridge. */ PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf, pj_pool_t *pool, pjmedia_port *strm_port, const pj_str_t *port_name, unsigned *p_port ) { struct conf_port *conf_port; unsigned index; pj_status_t status; PJ_ASSERT_RETURN(conf && pool && strm_port, PJ_EINVAL); /* If port_name is not specified, use the port's name */ if (!port_name) port_name = &strm_port->info.name; /* For this version of PJMEDIA, channel(s) number MUST be: * - same between port & conference bridge. * - monochannel on port or conference bridge. */ if (PJMEDIA_PIA_CCNT(&strm_port->info) != conf->channel_count && (PJMEDIA_PIA_CCNT(&strm_port->info) != 1 && conf->channel_count != 1)) { pj_assert(!"Number of channels mismatch"); return PJMEDIA_ENCCHANNEL; } pj_mutex_lock(conf->mutex); if (conf->port_cnt >= conf->max_ports) { pj_assert(!"Too many ports"); pj_mutex_unlock(conf->mutex); return PJ_ETOOMANY; } /* Find empty port in the conference bridge. */ for (index=0; index < conf->max_ports; ++index) { if (conf->ports[index] == NULL) break; } pj_assert(index != conf->max_ports); /* Create conf port structure. */ status = create_conf_port(pool, conf, strm_port, port_name, &conf_port); if (status != PJ_SUCCESS) { pj_mutex_unlock(conf->mutex); return status; } /* Put the port. */ conf->ports[index] = conf_port; conf->port_cnt++; /* Done. */ if (p_port) { *p_port = index; } pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } /* * Add passive port. */ PJ_DEF(pj_status_t) pjmedia_conf_add_passive_port( pjmedia_conf *conf, pj_pool_t *pool, const pj_str_t *name, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, unsigned *p_slot, pjmedia_port **p_port ) { struct conf_port *conf_port; pjmedia_port *port; unsigned index; pj_str_t tmp; pj_status_t status; PJ_LOG(1, (THIS_FILE, "This API has been deprecated since 1.3 and will " "be removed in the future release!")); PJ_ASSERT_RETURN(conf && pool, PJ_EINVAL); /* For this version of PJMEDIA, channel(s) number MUST be: * - same between port & conference bridge. * - monochannel on port or conference bridge. */ if (channel_count != conf->channel_count && (channel_count != 1 && conf->channel_count != 1)) { pj_assert(!"Number of channels mismatch"); return PJMEDIA_ENCCHANNEL; } /* For this version, options must be zero */ PJ_ASSERT_RETURN(options == 0, PJ_EINVAL); PJ_UNUSED_ARG(options); pj_mutex_lock(conf->mutex); if (conf->port_cnt >= conf->max_ports) { pj_assert(!"Too many ports"); pj_mutex_unlock(conf->mutex); return PJ_ETOOMANY; } /* Find empty port in the conference bridge. */ for (index=0; index < conf->max_ports; ++index) { if (conf->ports[index] == NULL) break; } pj_assert(index != conf->max_ports); if (name == NULL) { name = &tmp; tmp.ptr = (char*) pj_pool_alloc(pool, 32); tmp.slen = pj_ansi_snprintf(tmp.ptr, 32, "ConfPort#%d", index); } /* Create and initialize the media port structure. */ port = PJ_POOL_ZALLOC_T(pool, pjmedia_port); PJ_ASSERT_RETURN(port, PJ_ENOMEM); pjmedia_port_info_init(&port->info, name, SIGNATURE_PORT, clock_rate, channel_count, bits_per_sample, samples_per_frame); port->port_data.pdata = conf; port->port_data.ldata = index; port->get_frame = &get_frame_pasv; port->put_frame = &put_frame; port->on_destroy = &destroy_port_pasv; /* Create conf port structure. */ status = create_pasv_port(conf, pool, name, port, &conf_port); if (status != PJ_SUCCESS) { pj_mutex_unlock(conf->mutex); return status; } /* Put the port. */ conf->ports[index] = conf_port; conf->port_cnt++; /* Done. */ if (p_slot) *p_slot = index; if (p_port) *p_port = port; pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } /* * Change TX and RX settings for the port. */ PJ_DEF(pj_status_t) pjmedia_conf_configure_port( pjmedia_conf *conf, unsigned slot, pjmedia_port_op tx, pjmedia_port_op rx) { struct conf_port *conf_port; /* Check arguments */ PJ_ASSERT_RETURN(conf && slotmax_ports, PJ_EINVAL); pj_mutex_lock(conf->mutex); /* Port must be valid. */ conf_port = conf->ports[slot]; if (conf_port == NULL) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } conf_port = conf->ports[slot]; if (tx != PJMEDIA_PORT_NO_CHANGE) conf_port->tx_setting = tx; if (rx != PJMEDIA_PORT_NO_CHANGE) conf_port->rx_setting = rx; pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } /* * Connect port. */ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf, unsigned src_slot, unsigned sink_slot, int level ) { struct conf_port *src_port, *dst_port; pj_bool_t start_sound = PJ_FALSE; unsigned i; /* Check arguments */ PJ_ASSERT_RETURN(conf && src_slotmax_ports && sink_slotmax_ports, PJ_EINVAL); /* For now, level MUST be zero. */ PJ_ASSERT_RETURN(level == 0, PJ_EINVAL); pj_mutex_lock(conf->mutex); /* Ports must be valid. */ src_port = conf->ports[src_slot]; dst_port = conf->ports[sink_slot]; if (!src_port || !dst_port) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } /* Check if connection has been made */ for (i=0; ilistener_cnt; ++i) { if (src_port->listener_slots[i] == sink_slot) break; } if (i == src_port->listener_cnt) { src_port->listener_slots[src_port->listener_cnt] = sink_slot; ++conf->connect_cnt; ++src_port->listener_cnt; ++dst_port->transmitter_cnt; if (conf->connect_cnt == 1) start_sound = 1; PJ_LOG(4,(THIS_FILE,"Port %d (%.*s) transmitting to port %d (%.*s)", src_slot, (int)src_port->name.slen, src_port->name.ptr, sink_slot, (int)dst_port->name.slen, dst_port->name.ptr)); } pj_mutex_unlock(conf->mutex); /* Sound device must be started without mutex, otherwise the * sound thread will deadlock (?) */ if (start_sound) resume_sound(conf); return PJ_SUCCESS; } /* * Disconnect port */ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf, unsigned src_slot, unsigned sink_slot ) { struct conf_port *src_port, *dst_port; unsigned i; /* Check arguments */ PJ_ASSERT_RETURN(conf && src_slotmax_ports && sink_slotmax_ports, PJ_EINVAL); pj_mutex_lock(conf->mutex); /* Ports must be valid. */ src_port = conf->ports[src_slot]; dst_port = conf->ports[sink_slot]; if (!src_port || !dst_port) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } /* Check if connection has been made */ for (i=0; ilistener_cnt; ++i) { if (src_port->listener_slots[i] == sink_slot) break; } if (i != src_port->listener_cnt) { pj_assert(src_port->listener_cnt > 0 && src_port->listener_cnt < conf->max_ports); pj_assert(dst_port->transmitter_cnt > 0 && dst_port->transmitter_cnt < conf->max_ports); pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE), src_port->listener_cnt, i); --conf->connect_cnt; --src_port->listener_cnt; --dst_port->transmitter_cnt; PJ_LOG(4,(THIS_FILE, "Port %d (%.*s) stop transmitting to port %d (%.*s)", src_slot, (int)src_port->name.slen, src_port->name.ptr, sink_slot, (int)dst_port->name.slen, dst_port->name.ptr)); /* if source port is passive port and has no listener, reset delaybuf */ if (src_port->delay_buf && src_port->listener_cnt == 0) pjmedia_delay_buf_reset(src_port->delay_buf); } pj_mutex_unlock(conf->mutex); if (conf->connect_cnt == 0) { pause_sound(conf); } return PJ_SUCCESS; } /* * Get number of ports currently registered to the conference bridge. */ PJ_DEF(unsigned) pjmedia_conf_get_port_count(pjmedia_conf *conf) { return conf->port_cnt; } /* * Get total number of ports connections currently set up in the bridge. */ PJ_DEF(unsigned) pjmedia_conf_get_connect_count(pjmedia_conf *conf) { return conf->connect_cnt; } /* * Remove the specified port. */ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf, unsigned port ) { struct conf_port *conf_port; unsigned i; /* Check arguments */ PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL); /* Suspend the sound devices. * Don't want to remove port while port is being accessed by sound * device's threads! */ pj_mutex_lock(conf->mutex); /* Port must be valid. */ conf_port = conf->ports[port]; if (conf_port == NULL) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } conf_port->tx_setting = PJMEDIA_PORT_DISABLE; conf_port->rx_setting = PJMEDIA_PORT_DISABLE; /* Remove this port from transmit array of other ports. */ for (i=0; imax_ports; ++i) { unsigned j; struct conf_port *src_port; src_port = conf->ports[i]; if (!src_port) continue; if (src_port->listener_cnt == 0) continue; for (j=0; jlistener_cnt; ++j) { if (src_port->listener_slots[j] == port) { pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE), src_port->listener_cnt, j); pj_assert(conf->connect_cnt > 0); --conf->connect_cnt; --src_port->listener_cnt; break; } } } /* Update transmitter_cnt of ports we're transmitting to */ while (conf_port->listener_cnt) { unsigned dst_slot; struct conf_port *dst_port; dst_slot = conf_port->listener_slots[conf_port->listener_cnt-1]; dst_port = conf->ports[dst_slot]; --dst_port->transmitter_cnt; --conf_port->listener_cnt; pj_assert(conf->connect_cnt > 0); --conf->connect_cnt; } /* Destroy pjmedia port if this conf port is passive port, * i.e: has delay buf. */ if (conf_port->delay_buf) { pjmedia_port_destroy(conf_port->port); conf_port->port = NULL; } /* Remove the port. */ conf->ports[port] = NULL; --conf->port_cnt; pj_mutex_unlock(conf->mutex); /* Stop sound if there's no connection. */ if (conf->connect_cnt == 0) { pause_sound(conf); } return PJ_SUCCESS; } /* * Enum ports. */ PJ_DEF(pj_status_t) pjmedia_conf_enum_ports( pjmedia_conf *conf, unsigned ports[], unsigned *p_count ) { unsigned i, count=0; PJ_ASSERT_RETURN(conf && p_count && ports, PJ_EINVAL); /* Lock mutex */ pj_mutex_lock(conf->mutex); for (i=0; imax_ports && count<*p_count; ++i) { if (!conf->ports[i]) continue; ports[count++] = i; } /* Unlock mutex */ pj_mutex_unlock(conf->mutex); *p_count = count; return PJ_SUCCESS; } /* * Get port info */ PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf, unsigned slot, pjmedia_conf_port_info *info) { struct conf_port *conf_port; /* Check arguments */ PJ_ASSERT_RETURN(conf && slotmax_ports, PJ_EINVAL); /* Lock mutex */ pj_mutex_lock(conf->mutex); /* Port must be valid. */ conf_port = conf->ports[slot]; if (conf_port == NULL) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } info->slot = slot; info->name = conf_port->name; info->tx_setting = conf_port->tx_setting; info->rx_setting = conf_port->rx_setting; info->listener_cnt = conf_port->listener_cnt; info->listener_slots = conf_port->listener_slots; info->transmitter_cnt = conf_port->transmitter_cnt; info->clock_rate = conf_port->clock_rate; info->channel_count = conf_port->channel_count; info->samples_per_frame = conf_port->samples_per_frame; info->bits_per_sample = conf->bits_per_sample; info->tx_adj_level = conf_port->tx_adj_level - NORMAL_LEVEL; info->rx_adj_level = conf_port->rx_adj_level - NORMAL_LEVEL; /* Unlock mutex */ pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_conf_get_ports_info(pjmedia_conf *conf, unsigned *size, pjmedia_conf_port_info info[]) { unsigned i, count=0; PJ_ASSERT_RETURN(conf && size && info, PJ_EINVAL); /* Lock mutex */ pj_mutex_lock(conf->mutex); for (i=0; imax_ports && count<*size; ++i) { if (!conf->ports[i]) continue; pjmedia_conf_get_port_info(conf, i, &info[count]); ++count; } /* Unlock mutex */ pj_mutex_unlock(conf->mutex); *size = count; return PJ_SUCCESS; } /* * Get signal level. */ PJ_DEF(pj_status_t) pjmedia_conf_get_signal_level( pjmedia_conf *conf, unsigned slot, unsigned *tx_level, unsigned *rx_level) { struct conf_port *conf_port; /* Check arguments */ PJ_ASSERT_RETURN(conf && slotmax_ports, PJ_EINVAL); /* Lock mutex */ pj_mutex_lock(conf->mutex); /* Port must be valid. */ conf_port = conf->ports[slot]; if (conf_port == NULL) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } if (tx_level != NULL) { *tx_level = conf_port->tx_level; } if (rx_level != NULL) *rx_level = conf_port->rx_level; /* Unlock mutex */ pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } /* * Adjust RX level of individual port. */ PJ_DEF(pj_status_t) pjmedia_conf_adjust_rx_level( pjmedia_conf *conf, unsigned slot, int adj_level ) { struct conf_port *conf_port; /* Check arguments */ PJ_ASSERT_RETURN(conf && slotmax_ports, PJ_EINVAL); /* Value must be from -128 to +127 */ /* Disabled, you can put more than +127, at your own risk: PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL); */ PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL); /* Lock mutex */ pj_mutex_lock(conf->mutex); /* Port must be valid. */ conf_port = conf->ports[slot]; if (conf_port == NULL) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } /* Set normalized adjustment level. */ conf_port->rx_adj_level = adj_level + NORMAL_LEVEL; /* Unlock mutex */ pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } /* * Adjust TX level of individual port. */ PJ_DEF(pj_status_t) pjmedia_conf_adjust_tx_level( pjmedia_conf *conf, unsigned slot, int adj_level ) { struct conf_port *conf_port; /* Check arguments */ PJ_ASSERT_RETURN(conf && slotmax_ports, PJ_EINVAL); /* Value must be from -128 to +127 */ /* Disabled, you can put more than +127,, at your own risk: PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL); */ PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL); /* Lock mutex */ pj_mutex_lock(conf->mutex); /* Port must be valid. */ conf_port = conf->ports[slot]; if (conf_port == NULL) { pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } /* Set normalized adjustment level. */ conf_port->tx_adj_level = adj_level + NORMAL_LEVEL; /* Unlock mutex */ pj_mutex_unlock(conf->mutex); return PJ_SUCCESS; } /* * Read from port. */ static pj_status_t read_port( pjmedia_conf *conf, struct conf_port *cport, pj_int16_t *frame, pj_size_t count, pjmedia_frame_type *type ) { pj_assert(count == conf->samples_per_frame); TRACE_((THIS_FILE, "read_port %.*s: count=%d", (int)cport->name.slen, cport->name.ptr, count)); /* * If port's samples per frame and sampling rate and channel count * matche conference bridge's settings, get the frame directly from * the port. */ if (cport->rx_buf_cap == 0) { pjmedia_frame f; pj_status_t status; f.buf = frame; f.size = count * BYTES_PER_SAMPLE; TRACE_((THIS_FILE, " get_frame %.*s: count=%d", (int)cport->name.slen, cport->name.ptr, count)); status = pjmedia_port_get_frame(cport->port, &f); *type = f.type; return status; } else { unsigned samples_req; /* Initialize frame type */ if (cport->rx_buf_count == 0) { *type = PJMEDIA_FRAME_TYPE_NONE; } else { /* we got some samples in the buffer */ *type = PJMEDIA_FRAME_TYPE_AUDIO; } /* * If we don't have enough samples in rx_buf, read from the port * first. Remember that rx_buf may be in different clock rate and * channel count! */ samples_req = (unsigned) (count * 1.0 * cport->clock_rate / conf->clock_rate + 0.5); while (cport->rx_buf_count < samples_req) { pjmedia_frame f; pj_status_t status; f.buf = cport->rx_buf + cport->rx_buf_count; f.size = cport->samples_per_frame * BYTES_PER_SAMPLE; TRACE_((THIS_FILE, " get_frame, count=%d", cport->samples_per_frame)); status = pjmedia_port_get_frame(cport->port, &f); if (status != PJ_SUCCESS) { /* Fatal error! */ return status; } if (f.type != PJMEDIA_FRAME_TYPE_AUDIO) { TRACE_((THIS_FILE, " get_frame returned non-audio")); pjmedia_zero_samples( cport->rx_buf + cport->rx_buf_count, cport->samples_per_frame); } else { /* We've got at least one frame */ *type = PJMEDIA_FRAME_TYPE_AUDIO; } /* Adjust channels */ if (cport->channel_count != conf->channel_count) { if (cport->channel_count == 1) { pjmedia_convert_channel_1ton((pj_int16_t*)f.buf, (const pj_int16_t*)f.buf, conf->channel_count, cport->samples_per_frame, 0); cport->rx_buf_count += (cport->samples_per_frame * conf->channel_count); } else { /* conf->channel_count == 1 */ pjmedia_convert_channel_nto1((pj_int16_t*)f.buf, (const pj_int16_t*)f.buf, cport->channel_count, cport->samples_per_frame, PJMEDIA_STEREO_MIX, 0); cport->rx_buf_count += (cport->samples_per_frame / cport->channel_count); } } else { cport->rx_buf_count += cport->samples_per_frame; } TRACE_((THIS_FILE, " rx buffer size is now %d", cport->rx_buf_count)); pj_assert(cport->rx_buf_count <= cport->rx_buf_cap); } /* * If port's clock_rate is different, resample. * Otherwise just copy. */ if (cport->clock_rate != conf->clock_rate) { unsigned src_count; TRACE_((THIS_FILE, " resample, input count=%d", pjmedia_resample_get_input_size(cport->rx_resample))); pjmedia_resample_run( cport->rx_resample,cport->rx_buf, frame); src_count = (unsigned)(count * 1.0 * cport->clock_rate / conf->clock_rate + 0.5); cport->rx_buf_count -= src_count; if (cport->rx_buf_count) { pjmedia_move_samples(cport->rx_buf, cport->rx_buf+src_count, cport->rx_buf_count); } TRACE_((THIS_FILE, " rx buffer size is now %d", cport->rx_buf_count)); } else { pjmedia_copy_samples(frame, cport->rx_buf, (unsigned)count); cport->rx_buf_count -= (unsigned)count; if (cport->rx_buf_count) { pjmedia_move_samples(cport->rx_buf, cport->rx_buf+count, cport->rx_buf_count); } } } return PJ_SUCCESS; } /* * Write the mixed signal to the port. */ static pj_status_t write_port(pjmedia_conf *conf, struct conf_port *cport, const pj_timestamp *timestamp, pjmedia_frame_type *frm_type) { pj_int16_t *buf; unsigned j, ts; pj_status_t status; pj_int32_t adj_level; pj_int32_t tx_level; unsigned dst_count; *frm_type = PJMEDIA_FRAME_TYPE_AUDIO; /* If port is muted or nobody is transmitting to this port, * transmit NULL frame. */ if (cport->tx_setting == PJMEDIA_PORT_MUTE || cport->transmitter_cnt==0) { pjmedia_frame frame; /* Clear left-over samples in tx_buffer, if any, so that it won't * be transmitted next time we have audio signal. */ cport->tx_buf_count = 0; /* Add sample counts to heart-beat samples */ cport->tx_heart_beat += conf->samples_per_frame * cport->clock_rate / conf->clock_rate * cport->channel_count / conf->channel_count; /* Set frame timestamp */ frame.timestamp.u64 = timestamp->u64 * cport->clock_rate / conf->clock_rate; frame.type = PJMEDIA_FRAME_TYPE_NONE; frame.buf = NULL; frame.size = 0; /* Transmit heart-beat frames (may transmit more than one NULL frame * if port's ptime is less than bridge's ptime. */ if (cport->port && cport->port->put_frame) { while (cport->tx_heart_beat >= cport->samples_per_frame) { pjmedia_port_put_frame(cport->port, &frame); cport->tx_heart_beat -= cport->samples_per_frame; frame.timestamp.u64 += cport->samples_per_frame; } } cport->tx_level = 0; *frm_type = PJMEDIA_FRAME_TYPE_NONE; return PJ_SUCCESS; } else if (cport->tx_setting != PJMEDIA_PORT_ENABLE) { cport->tx_level = 0; *frm_type = PJMEDIA_FRAME_TYPE_NONE; return PJ_SUCCESS; } /* Reset heart-beat sample count */ cport->tx_heart_beat = 0; buf = (pj_int16_t*) cport->mix_buf; /* If there are sources in the mix buffer, convert the mixed samples * from 32bit to 16bit in the mixed samples itself. This is possible * because mixed sample is 32bit. * * In addition to this process, if we need to change the level of * TX signal, we adjust is here too. */ /* Calculate signal level and adjust the signal when needed. * Two adjustments performed at once: * 1. user setting adjustment (tx_adj_level). * 2. automatic adjustment of overflowed mixed buffer (mix_adj). */ /* Apply simple AGC to the mix_adj, the automatic adjust, to avoid * dramatic change in the level thus causing noise because the signal * is now not aligned with the signal from the previous frame. */ SIMPLE_AGC(cport->last_mix_adj, cport->mix_adj); cport->last_mix_adj = cport->mix_adj; /* adj_level = cport->tx_adj_level * cport->mix_adj / NORMAL_LEVEL;*/ adj_level = cport->tx_adj_level * cport->mix_adj; adj_level >>= 7; tx_level = 0; if (adj_level != NORMAL_LEVEL) { for (j=0; jsamples_per_frame; ++j) { pj_int32_t itemp = cport->mix_buf[j]; /* Adjust the level */ /*itemp = itemp * adj_level / NORMAL_LEVEL;*/ itemp = (itemp * adj_level) >> 7; /* Clip the signal if it's too loud */ if (itemp > MAX_LEVEL) itemp = MAX_LEVEL; else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL; /* Put back in the buffer. */ buf[j] = (pj_int16_t) itemp; tx_level += (buf[j]>=0? buf[j] : -buf[j]); } } else { for (j=0; jsamples_per_frame; ++j) { buf[j] = (pj_int16_t) cport->mix_buf[j]; tx_level += (buf[j]>=0? buf[j] : -buf[j]); } } tx_level /= conf->samples_per_frame; /* Convert level to 8bit complement ulaw */ tx_level = pjmedia_linear2ulaw(tx_level) ^ 0xff; cport->tx_level = tx_level; /* If port has the same clock_rate and samples_per_frame and * number of channels as the conference bridge, transmit the * frame as is. */ if (cport->clock_rate == conf->clock_rate && cport->samples_per_frame == conf->samples_per_frame && cport->channel_count == conf->channel_count) { if (cport->port != NULL) { pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = buf; frame.size = conf->samples_per_frame * BYTES_PER_SAMPLE; /* No need to adjust timestamp, port has the same * clock rate as conference bridge */ frame.timestamp = *timestamp; TRACE_((THIS_FILE, "put_frame %.*s, count=%d", (int)cport->name.slen, cport->name.ptr, frame.size / BYTES_PER_SAMPLE)); return pjmedia_port_put_frame(cport->port, &frame); } else return PJ_SUCCESS; } /* If it has different clock_rate, must resample. */ if (cport->clock_rate != conf->clock_rate) { pjmedia_resample_run( cport->tx_resample, buf, cport->tx_buf + cport->tx_buf_count ); dst_count = (unsigned)(conf->samples_per_frame * 1.0 * cport->clock_rate / conf->clock_rate + 0.5); } else { /* Same clock rate. * Just copy the samples to tx_buffer. */ pjmedia_copy_samples( cport->tx_buf + cport->tx_buf_count, buf, conf->samples_per_frame ); dst_count = conf->samples_per_frame; } /* Adjust channels */ if (cport->channel_count != conf->channel_count) { pj_int16_t *tx_buf = cport->tx_buf + cport->tx_buf_count; if (conf->channel_count == 1) { pjmedia_convert_channel_1ton(tx_buf, tx_buf, cport->channel_count, dst_count, 0); dst_count *= cport->channel_count; } else { /* cport->channel_count == 1 */ pjmedia_convert_channel_nto1(tx_buf, tx_buf, conf->channel_count, dst_count, PJMEDIA_STEREO_MIX, 0); dst_count /= conf->channel_count; } } cport->tx_buf_count += dst_count; pj_assert(cport->tx_buf_count <= cport->tx_buf_cap); /* Transmit while we have enough frame in the tx_buf. */ status = PJ_SUCCESS; ts = 0; while (cport->tx_buf_count >= cport->samples_per_frame && status == PJ_SUCCESS) { TRACE_((THIS_FILE, "write_port %.*s: count=%d", (int)cport->name.slen, cport->name.ptr, cport->samples_per_frame)); if (cport->port) { pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = cport->tx_buf; frame.size = cport->samples_per_frame * BYTES_PER_SAMPLE; /* Adjust timestamp as port may have different clock rate * than the bridge. */ frame.timestamp.u64 = timestamp->u64 * cport->clock_rate / conf->clock_rate; /* Add timestamp for individual frame */ frame.timestamp.u64 += ts; ts += cport->samples_per_frame; TRACE_((THIS_FILE, "put_frame %.*s, count=%d", (int)cport->name.slen, cport->name.ptr, frame.size / BYTES_PER_SAMPLE)); status = pjmedia_port_put_frame(cport->port, &frame); } else status = PJ_SUCCESS; cport->tx_buf_count -= cport->samples_per_frame; if (cport->tx_buf_count) { pjmedia_move_samples(cport->tx_buf, cport->tx_buf + cport->samples_per_frame, cport->tx_buf_count); } TRACE_((THIS_FILE, " tx_buf count now is %d", cport->tx_buf_count)); } return status; } /* * Player callback. */ static pj_status_t get_frame(pjmedia_port *this_port, pjmedia_frame *frame) { pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata; pjmedia_frame_type speaker_frame_type = PJMEDIA_FRAME_TYPE_NONE; unsigned ci, cj, i, j; pj_int16_t *p_in; TRACE_((THIS_FILE, "- clock -")); /* Check that correct size is specified. */ pj_assert(frame->size == conf->samples_per_frame * conf->bits_per_sample / 8); /* Must lock mutex */ pj_mutex_lock(conf->mutex); /* Reset port source count. We will only reset port's mix * buffer when we have someone transmitting to it. */ for (i=0, ci=0; imax_ports && ci < conf->port_cnt; ++i) { struct conf_port *conf_port = conf->ports[i]; /* Skip empty port. */ if (!conf_port) continue; /* Var "ci" is to count how many ports have been visited so far. */ ++ci; /* Reset buffer (only necessary if the port has transmitter) and * reset auto adjustment level for mixed signal. */ conf_port->mix_adj = NORMAL_LEVEL; if (conf_port->transmitter_cnt) { pj_bzero(conf_port->mix_buf, conf->samples_per_frame*sizeof(conf_port->mix_buf[0])); } } /* Get frames from all ports, and "mix" the signal * to mix_buf of all listeners of the port. */ for (i=0, ci=0; i < conf->max_ports && ci < conf->port_cnt; ++i) { struct conf_port *conf_port = conf->ports[i]; pj_int32_t level = 0; /* Skip empty port. */ if (!conf_port) continue; /* Var "ci" is to count how many ports have been visited so far. */ ++ci; /* Skip if we're not allowed to receive from this port. */ if (conf_port->rx_setting == PJMEDIA_PORT_DISABLE) { conf_port->rx_level = 0; continue; } /* Also skip if this port doesn't have listeners. */ if (conf_port->listener_cnt == 0) { conf_port->rx_level = 0; continue; } /* Get frame from this port. * For passive ports, get the frame from the delay_buf. * For other ports, get the frame from the port. */ if (conf_port->delay_buf != NULL) { pj_status_t status; status = pjmedia_delay_buf_get(conf_port->delay_buf, (pj_int16_t*)frame->buf); if (status != PJ_SUCCESS) continue; } else { pj_status_t status; pjmedia_frame_type frame_type; status = read_port(conf, conf_port, (pj_int16_t*)frame->buf, conf->samples_per_frame, &frame_type); if (status != PJ_SUCCESS) { /* bennylp: why do we need this???? * Also see comments on similar issue with write_port(). PJ_LOG(4,(THIS_FILE, "Port %.*s get_frame() returned %d. " "Port is now disabled", (int)conf_port->name.slen, conf_port->name.ptr, status)); conf_port->rx_setting = PJMEDIA_PORT_DISABLE; */ continue; } /* Check that the port is not removed when we call get_frame() */ if (conf->ports[i] == NULL) continue; /* Ignore if we didn't get any frame */ if (frame_type != PJMEDIA_FRAME_TYPE_AUDIO) continue; } p_in = (pj_int16_t*) frame->buf; /* Adjust the RX level from this port * and calculate the average level at the same time. */ if (conf_port->rx_adj_level != NORMAL_LEVEL) { for (j=0; jsamples_per_frame; ++j) { /* For the level adjustment, we need to store the sample to * a temporary 32bit integer value to avoid overflowing the * 16bit sample storage. */ pj_int32_t itemp; itemp = p_in[j]; /*itemp = itemp * adj / NORMAL_LEVEL;*/ /* bad code (signed/unsigned badness): * itemp = (itemp * conf_port->rx_adj_level) >> 7; */ itemp *= conf_port->rx_adj_level; itemp >>= 7; /* Clip the signal if it's too loud */ if (itemp > MAX_LEVEL) itemp = MAX_LEVEL; else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL; p_in[j] = (pj_int16_t) itemp; level += (p_in[j]>=0? p_in[j] : -p_in[j]); } } else { for (j=0; jsamples_per_frame; ++j) { level += (p_in[j]>=0? p_in[j] : -p_in[j]); } } level /= conf->samples_per_frame; /* Convert level to 8bit complement ulaw */ level = pjmedia_linear2ulaw(level) ^ 0xff; /* Put this level to port's last RX level. */ conf_port->rx_level = level; // Ticket #671: Skipping very low audio signal may cause noise // to be generated in the remote end by some hardphones. /* Skip processing frame if level is zero */ //if (level == 0) // continue; /* Add the signal to all listeners. */ for (cj=0; cj < conf_port->listener_cnt; ++cj) { struct conf_port *listener; pj_int32_t *mix_buf; unsigned k; listener = conf->ports[conf_port->listener_slots[cj]]; /* Skip if this listener doesn't want to receive audio */ if (listener->tx_setting != PJMEDIA_PORT_ENABLE) continue; mix_buf = listener->mix_buf; if (listener->transmitter_cnt > 1) { /* Mixing signals, * and calculate appropriate level adjustment if there is * any overflowed level in the mixed signal. */ for (k=0; k < conf->samples_per_frame; ++k) { mix_buf[k] += p_in[k]; /* Check if normalization adjustment needed. */ if (IS_OVERFLOW(mix_buf[k])) { /* NORMAL_LEVEL * MAX_LEVEL / mix_buf[k]; */ int tmp_adj = (MAX_LEVEL<<7) / mix_buf[k]; if (tmp_adj<0) tmp_adj = -tmp_adj; if (tmp_adjmix_adj) listener->mix_adj = tmp_adj; } /* if any overflow in the mixed signals */ } /* loop mixing signals */ } else { /* Only 1 transmitter: * just copy the samples to the mix buffer * no mixing and level adjustment needed */ for (k=0; ksamples_per_frame; ++k) { mix_buf[k] = p_in[k]; } } } /* loop the listeners of conf port */ } /* loop of all conf ports */ /* Time for all ports to transmit whetever they have in their * buffer. */ for (i=0, ci=0; imax_ports && ciport_cnt; ++i) { struct conf_port *conf_port = conf->ports[i]; pjmedia_frame_type frm_type; pj_status_t status; if (!conf_port) continue; /* Var "ci" is to count how many ports have been visited. */ ++ci; status = write_port( conf, conf_port, &frame->timestamp, &frm_type); if (status != PJ_SUCCESS) { /* bennylp: why do we need this???? One thing for sure, put_frame()/write_port() may return non-successfull status on Win32 if there's temporary glitch on network interface, so disabling the port here does not sound like a good idea. PJ_LOG(4,(THIS_FILE, "Port %.*s put_frame() returned %d. " "Port is now disabled", (int)conf_port->name.slen, conf_port->name.ptr, status)); conf_port->tx_setting = PJMEDIA_PORT_DISABLE; */ continue; } /* Set the type of frame to be returned to sound playback * device. */ if (i == 0) speaker_frame_type = frm_type; } /* Return sound playback frame. */ if (conf->ports[0]->tx_level) { TRACE_((THIS_FILE, "write to audio, count=%d", conf->samples_per_frame)); pjmedia_copy_samples( (pj_int16_t*)frame->buf, (const pj_int16_t*)conf->ports[0]->mix_buf, conf->samples_per_frame); } else { /* Force frame type NONE */ speaker_frame_type = PJMEDIA_FRAME_TYPE_NONE; } /* MUST set frame type */ frame->type = speaker_frame_type; pj_mutex_unlock(conf->mutex); #ifdef REC_FILE if (fhnd_rec == NULL) fhnd_rec = fopen(REC_FILE, "wb"); if (fhnd_rec) fwrite(frame->buf, frame->size, 1, fhnd_rec); #endif return PJ_SUCCESS; } /* * get_frame() for passive port */ static pj_status_t get_frame_pasv(pjmedia_port *this_port, pjmedia_frame *frame) { pj_assert(0); PJ_UNUSED_ARG(this_port); PJ_UNUSED_ARG(frame); return -1; } /* * Recorder (or passive port) callback. */ static pj_status_t put_frame(pjmedia_port *this_port, pjmedia_frame *frame) { pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata; struct conf_port *port = conf->ports[this_port->port_data.ldata]; pj_status_t status; /* Check for correct size. */ PJ_ASSERT_RETURN( frame->size == conf->samples_per_frame * conf->bits_per_sample / 8, PJMEDIA_ENCSAMPLESPFRAME); /* Check existance of delay_buf instance */ PJ_ASSERT_RETURN( port->delay_buf, PJ_EBUG ); /* Skip if this port is muted/disabled. */ if (port->rx_setting != PJMEDIA_PORT_ENABLE) { return PJ_SUCCESS; } /* Skip if no port is listening to the microphone */ if (port->listener_cnt == 0) { return PJ_SUCCESS; } status = pjmedia_delay_buf_put(port->delay_buf, (pj_int16_t*)frame->buf); return status; } #endif ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/converter.c ================================================ /* $Id: converter.c 4412 2013-03-05 03:12:32Z riza $ */ /* * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #define THIS_FILE "converter.c" struct pjmedia_converter_mgr { pjmedia_converter_factory factory_list; }; static pjmedia_converter_mgr *converter_manager_instance; #if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL PJ_DECL(pj_status_t) pjmedia_libswscale_converter_init(pjmedia_converter_mgr *mgr); #endif #if defined(PJMEDIA_HAS_LIBYUV) && PJMEDIA_HAS_LIBYUV != 0 PJ_DECL(pj_status_t) pjmedia_libyuv_converter_init(pjmedia_converter_mgr *mgr); #endif PJ_DEF(pj_status_t) pjmedia_converter_mgr_create(pj_pool_t *pool, pjmedia_converter_mgr **p_mgr) { pjmedia_converter_mgr *mgr; pj_status_t status = PJ_SUCCESS; mgr = PJ_POOL_ALLOC_T(pool, pjmedia_converter_mgr); pj_list_init(&mgr->factory_list); if (!converter_manager_instance) converter_manager_instance = mgr; #if defined(PJMEDIA_HAS_LIBYUV) && PJMEDIA_HAS_LIBYUV != 0 status = pjmedia_libyuv_converter_init(mgr); if (status != PJ_SUCCESS) { PJ_PERROR(4,(THIS_FILE, status, "Error initializing libyuv converter")); } #endif #if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL status = pjmedia_libswscale_converter_init(mgr); if (status != PJ_SUCCESS) { PJ_PERROR(4,(THIS_FILE, status, "Error initializing libswscale converter")); } #endif if (p_mgr) *p_mgr = mgr; return status; } PJ_DEF(pjmedia_converter_mgr*) pjmedia_converter_mgr_instance(void) { pj_assert(converter_manager_instance != NULL); return converter_manager_instance; } PJ_DEF(void) pjmedia_converter_mgr_set_instance(pjmedia_converter_mgr *mgr) { converter_manager_instance = mgr; } PJ_DEF(void) pjmedia_converter_mgr_destroy(pjmedia_converter_mgr *mgr) { pjmedia_converter_factory *f; if (!mgr) mgr = pjmedia_converter_mgr_instance(); PJ_ASSERT_ON_FAIL(mgr != NULL, return); f = mgr->factory_list.next; while (f != &mgr->factory_list) { pjmedia_converter_factory *next = f->next; pj_list_erase(f); (*f->op->destroy_factory)(f); f = next; } if (converter_manager_instance == mgr) converter_manager_instance = NULL; } PJ_DEF(pj_status_t) pjmedia_converter_mgr_register_factory(pjmedia_converter_mgr *mgr, pjmedia_converter_factory *factory) { pjmedia_converter_factory *pf; if (!mgr) mgr = pjmedia_converter_mgr_instance(); PJ_ASSERT_RETURN(mgr != NULL, PJ_EINVAL); PJ_ASSERT_RETURN(!pj_list_find_node(&mgr->factory_list, factory), PJ_EEXISTS); pf = mgr->factory_list.next; while (pf != &mgr->factory_list) { if (pf->priority < factory->priority) break; pf = pf->next; } pj_list_insert_before(pf, factory); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_converter_mgr_unregister_factory(pjmedia_converter_mgr *mgr, pjmedia_converter_factory *f, pj_bool_t destroy) { if (!mgr) mgr = pjmedia_converter_mgr_instance(); PJ_ASSERT_RETURN(mgr != NULL, PJ_EINVAL); PJ_ASSERT_RETURN(pj_list_find_node(&mgr->factory_list, f), PJ_ENOTFOUND); pj_list_erase(f); if (destroy) (*f->op->destroy_factory)(f); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_converter_create(pjmedia_converter_mgr *mgr, pj_pool_t *pool, pjmedia_conversion_param *param, pjmedia_converter **p_cv) { pjmedia_converter_factory *f; pjmedia_converter *cv = NULL; pj_status_t status = PJ_ENOTFOUND; if (!mgr) mgr = pjmedia_converter_mgr_instance(); PJ_ASSERT_RETURN(mgr != NULL, PJ_EINVAL); *p_cv = NULL; f = mgr->factory_list.next; while (f != &mgr->factory_list) { status = (*f->op->create_converter)(f, pool, param, &cv); if (status == PJ_SUCCESS) break; f = f->next; } if (status != PJ_SUCCESS) return status; if (param->src.type == PJMEDIA_TYPE_VIDEO) { char src_fourcc_name[5]; char dst_fourcc_name[5]; PJ_LOG(4, (THIS_FILE, "Converter %p (%s) created for video: %dx%d %s -> %dx%d %s", cv, f->name, param->src.det.vid.size.w, param->src.det.vid.size.h, pjmedia_fourcc_name(param->src.id, src_fourcc_name), param->dst.det.vid.size.w, param->dst.det.vid.size.h, pjmedia_fourcc_name(param->dst.id, dst_fourcc_name))); } else if (param->src.type == PJMEDIA_TYPE_AUDIO) { PJ_LOG(4, (THIS_FILE, "Converter %p created for audio", cv)); } else { PJ_LOG(4, (THIS_FILE, "Converter %p created for unknown", cv)); } *p_cv = cv; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_converter_convert(pjmedia_converter *cv, pjmedia_frame *src_frame, pjmedia_frame *dst_frame) { return (*cv->op->convert)(cv, src_frame, dst_frame); } PJ_DEF(void) pjmedia_converter_destroy(pjmedia_converter *cv) { PJ_LOG(4, (THIS_FILE, "Converter %p destroyed", cv)); (*cv->op->destroy)(cv); } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/converter_libswscale.c ================================================ /* $Id: converter_libswscale.c 4076 2012-04-24 09:40:35Z bennylp $ */ /* * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL #include "ffmpeg_util.h" #include static pj_status_t factory_create_converter(pjmedia_converter_factory *cf, pj_pool_t *pool, const pjmedia_conversion_param*prm, pjmedia_converter **p_cv); static void factory_destroy_factory(pjmedia_converter_factory *cf); static pj_status_t libswscale_conv_convert(pjmedia_converter *converter, pjmedia_frame *src_frame, pjmedia_frame *dst_frame); static void libswscale_conv_destroy(pjmedia_converter *converter); struct fmt_info { const pjmedia_video_format_info *fmt_info; pjmedia_video_apply_fmt_param apply_param; }; struct ffmpeg_converter { pjmedia_converter base; struct SwsContext *sws_ctx; struct fmt_info src, dst; }; static pjmedia_converter_factory_op libswscale_factory_op = { &factory_create_converter, &factory_destroy_factory }; static pjmedia_converter_op liswscale_converter_op = { &libswscale_conv_convert, &libswscale_conv_destroy }; static pj_status_t factory_create_converter(pjmedia_converter_factory *cf, pj_pool_t *pool, const pjmedia_conversion_param *prm, pjmedia_converter **p_cv) { enum AVPixelFormat srcFormat, dstFormat; const pjmedia_video_format_detail *src_detail, *dst_detail; const pjmedia_video_format_info *src_fmt_info, *dst_fmt_info; struct SwsContext *sws_ctx; struct ffmpeg_converter *fcv; pj_status_t status; PJ_UNUSED_ARG(cf); /* Only supports video */ if (prm->src.type != PJMEDIA_TYPE_VIDEO || prm->dst.type != prm->src.type || prm->src.detail_type != PJMEDIA_FORMAT_DETAIL_VIDEO || prm->dst.detail_type != prm->src.detail_type) { return PJ_ENOTSUP; } /* lookup source format info */ src_fmt_info = pjmedia_get_video_format_info( pjmedia_video_format_mgr_instance(), prm->src.id); if (!src_fmt_info) return PJ_ENOTSUP; /* lookup destination format info */ dst_fmt_info = pjmedia_get_video_format_info( pjmedia_video_format_mgr_instance(), prm->dst.id); if (!dst_fmt_info) return PJ_ENOTSUP; src_detail = pjmedia_format_get_video_format_detail(&prm->src, PJ_TRUE); dst_detail = pjmedia_format_get_video_format_detail(&prm->dst, PJ_TRUE); status = pjmedia_format_id_to_PixelFormat(prm->src.id, &srcFormat); if (status != PJ_SUCCESS) return PJ_ENOTSUP; status = pjmedia_format_id_to_PixelFormat(prm->dst.id, &dstFormat); if (status != PJ_SUCCESS) return PJ_ENOTSUP; sws_ctx = sws_getContext(src_detail->size.w, src_detail->size.h, srcFormat, dst_detail->size.w, dst_detail->size.h, dstFormat, SWS_BICUBIC, NULL, NULL, NULL); if (sws_ctx == NULL) return PJ_ENOTSUP; fcv = PJ_POOL_ZALLOC_T(pool, struct ffmpeg_converter); fcv->base.op = &liswscale_converter_op; fcv->sws_ctx = sws_ctx; fcv->src.apply_param.size = src_detail->size; fcv->src.fmt_info = src_fmt_info; fcv->dst.apply_param.size = dst_detail->size; fcv->dst.fmt_info = dst_fmt_info; *p_cv = &fcv->base; return PJ_SUCCESS; } static void factory_destroy_factory(pjmedia_converter_factory *cf) { PJ_UNUSED_ARG(cf); } static pj_status_t libswscale_conv_convert(pjmedia_converter *converter, pjmedia_frame *src_frame, pjmedia_frame *dst_frame) { struct ffmpeg_converter *fcv = (struct ffmpeg_converter*)converter; struct fmt_info *src = &fcv->src, *dst = &fcv->dst; int h; src->apply_param.buffer = src_frame->buf; (*src->fmt_info->apply_fmt)(src->fmt_info, &src->apply_param); dst->apply_param.buffer = dst_frame->buf; (*dst->fmt_info->apply_fmt)(dst->fmt_info, &dst->apply_param); h = sws_scale(fcv->sws_ctx, (const uint8_t* const *)src->apply_param.planes, src->apply_param.strides, 0, src->apply_param.size.h, dst->apply_param.planes, dst->apply_param.strides); //sws_scale() return value can't be trusted? There are cases when //sws_scale() returns zero but conversion seems to work okay. //return h==(int)dst->apply_param.size.h ? PJ_SUCCESS : PJ_EUNKNOWN; PJ_UNUSED_ARG(h); return PJ_SUCCESS; } static void libswscale_conv_destroy(pjmedia_converter *converter) { struct ffmpeg_converter *fcv = (struct ffmpeg_converter*)converter; if (fcv->sws_ctx) { struct SwsContext *tmp = fcv->sws_ctx; fcv->sws_ctx = NULL; sws_freeContext(tmp); } } static pjmedia_converter_factory libswscale_factory = { NULL, NULL, /* list */ "libswscale", /* name */ PJMEDIA_CONVERTER_PRIORITY_NORMAL+1, /* priority */ NULL /* op will be init-ed later */ }; PJ_DEF(pj_status_t) pjmedia_libswscale_converter_init(pjmedia_converter_mgr *mgr) { libswscale_factory.op = &libswscale_factory_op; pjmedia_ffmpeg_add_ref(); return pjmedia_converter_mgr_register_factory(mgr, &libswscale_factory); } PJ_DEF(pj_status_t) pjmedia_libswscale_converter_shutdown(pjmedia_converter_mgr *mgr, pj_pool_t *pool) { PJ_UNUSED_ARG(pool); pjmedia_ffmpeg_dec_ref(); return pjmedia_converter_mgr_unregister_factory(mgr, &libswscale_factory, PJ_TRUE); } #ifdef _MSC_VER # pragma comment( lib, "avutil.lib") # pragma comment( lib, "swscale.lib") #endif #endif /* #if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/converter_libyuv.c ================================================ /* $Id$ */ /* * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #if defined(PJMEDIA_HAS_LIBYUV) && PJMEDIA_HAS_LIBYUV != 0 #include static pj_status_t factory_create_converter(pjmedia_converter_factory *cf, pj_pool_t *pool, const pjmedia_conversion_param*prm, pjmedia_converter **p_cv); static void factory_destroy_factory(pjmedia_converter_factory *cf); static pj_status_t libyuv_conv_convert(pjmedia_converter *converter, pjmedia_frame *src_frame, pjmedia_frame *dst_frame); static void libyuv_conv_destroy(pjmedia_converter *converter); static pjmedia_converter_factory_op libyuv_factory_op = { &factory_create_converter, &factory_destroy_factory }; static pjmedia_converter_op libyuv_converter_op = { &libyuv_conv_convert, &libyuv_conv_destroy }; typedef struct fmt_info { const pjmedia_video_format_info *vid_fmt_info; pjmedia_video_apply_fmt_param apply_param; } fmt_info; typedef enum conv_func_type { CONV_PACK_TO_PACK, CONV_PACK_TO_PLANAR, CONV_PLANAR_TO_PACK, CONV_PLANAR_TO_PLANAR, SCALE_PACK, SCALE_PLANAR } conv_func_type; typedef void (*gen_conv_func)(); typedef int (*conv_pack_to_pack_method)(const uint8* src, int src_stride, uint8* dst, int dst_stride, int width, int height); typedef int (*conv_pack_to_planar_method)(const uint8* src, int src_stride, uint8* dst1, int dst_stride1, uint8* dst2, int dst_stride2, uint8* dst3, int dst_stride3, int width, int height); typedef int (*conv_planar_to_pack_method)(const uint8* src1, int src_stride1, const uint8* src2, int src_stride2, const uint8* src3, int src_stride3, uint8* dst, int dst_stride, int width, int height); typedef int (*conv_planar_to_planar_method)(const uint8* src1, int src_stride1, const uint8* src2, int src_stride2, const uint8* src3, int src_stride3, uint8* dst1, int dst_stride1, uint8* dst2, int dst_stride2, uint8* dst3, int dst_stride3, int width, int height); typedef int (*scale_pack_method) (const uint8* src_argb, int src_stride_argb, int src_width, int src_height, uint8* dst_argb, int dst_stride_argb, int dst_width, int dst_height, enum FilterMode filtering); typedef int (*scale_planar_method) (const uint8* src_y, int src_stride_y, const uint8* src_u, int src_stride_u, const uint8* src_v, int src_stride_v, int src_width, int src_height, uint8* dst_y, int dst_stride_y, uint8* dst_u, int dst_stride_u, uint8* dst_v, int dst_stride_v, int dst_width, int dst_height, enum FilterMode filtering); typedef union act_method { conv_pack_to_pack_method conv_pack_to_pack; conv_pack_to_planar_method conv_pack_to_planar; conv_planar_to_pack_method conv_planar_to_pack; conv_planar_to_planar_method conv_planar_to_planar; scale_pack_method scale_pack; scale_planar_method scale_planar; } act_method; typedef struct fmt_convert_map { pj_uint32_t src_id; pj_uint32_t dst_id; conv_func_type func_type; gen_conv_func conv_func; } fmt_convert_map; /* Maximum number of steps/act needed for the conversion/scale process. */ #define MAXIMUM_ACT 3 /* Define the filter mode for libyuv: * 0 : None (fastest) * 1 : Linear * 2 : Biinear * 3 : Filter Box (best quality) */ #if !defined(LIBYUV_FILTER_MODE) # define LIBYUV_FILTER_MODE 3 #endif #define METHOD_IS_SCALE(mtd) mtd>CONV_PLANAR_TO_PLANAR /* Macro to help define format conversion table. */ #define GET_PJ_FORMAT(fmt) PJMEDIA_FORMAT_##fmt #define MAP_CONV_PACK_TO_PACK(src,dst,method) GET_PJ_FORMAT(src),\ GET_PJ_FORMAT(dst),CONV_PACK_TO_PACK,(gen_conv_func)&method #define MAP_CONV_PACK_TO_PLANAR(src,dst,method) GET_PJ_FORMAT(src),\ GET_PJ_FORMAT(dst),CONV_PACK_TO_PLANAR,(gen_conv_func)&method #define MAP_CONV_PLANAR_TO_PACK(src,dst,method) GET_PJ_FORMAT(src),\ GET_PJ_FORMAT(dst),CONV_PLANAR_TO_PACK,(gen_conv_func)&method #define MAP_CONV_PLANAR_TO_PLANAR(src,dst,method) GET_PJ_FORMAT(src),\ GET_PJ_FORMAT(dst),CONV_PLANAR_TO_PLANAR,(gen_conv_func)&method #define MAP_SCALE_PACK(fmt,method) GET_PJ_FORMAT(fmt),\ GET_PJ_FORMAT(fmt),SCALE_PACK,(gen_conv_func)&method #define MAP_SCALE_PLANAR(fmt,method) GET_PJ_FORMAT(fmt),\ GET_PJ_FORMAT(fmt),SCALE_PLANAR,(gen_conv_func)&method static fmt_convert_map conv_to_i420[] = { {MAP_CONV_PACK_TO_PLANAR(RGB24,I420,RGB24ToI420)}, {MAP_CONV_PACK_TO_PLANAR(RGBA,I420,ABGRToI420)}, {MAP_CONV_PACK_TO_PLANAR(BGRA,I420,ARGBToI420)}, {MAP_CONV_PACK_TO_PLANAR(YUY2,I420,YUY2ToI420)}, {MAP_CONV_PACK_TO_PLANAR(UYVY,I420,UYVYToI420)}, {MAP_CONV_PLANAR_TO_PLANAR(I422,I420,I422ToI420)} }; static fmt_convert_map conv_from_i420[] = { {MAP_CONV_PLANAR_TO_PACK(I420,RGB24,I420ToRGB24)}, {MAP_CONV_PLANAR_TO_PACK(I420,RGBA,I420ToABGR)}, {MAP_CONV_PLANAR_TO_PACK(I420,BGRA,I420ToARGB)}, {MAP_CONV_PLANAR_TO_PACK(I420,YUY2,I420ToYUY2)}, {MAP_CONV_PLANAR_TO_PACK(I420,UYVY,I420ToUYVY)}, {MAP_CONV_PLANAR_TO_PLANAR(I420,I422,I420ToI422)}, {MAP_SCALE_PLANAR(I420,I420Scale)} }; static fmt_convert_map conv_to_bgra[] = { {MAP_CONV_PACK_TO_PACK(RGB24,BGRA,RGB24ToARGB)}, {MAP_CONV_PACK_TO_PACK(RGBA,BGRA,ABGRToARGB)}, {MAP_CONV_PACK_TO_PACK(YUY2,BGRA,YUY2ToARGB)}, {MAP_CONV_PACK_TO_PACK(UYVY,BGRA,UYVYToARGB)}, {MAP_CONV_PLANAR_TO_PACK(I422,BGRA,I422ToARGB)}, {MAP_CONV_PLANAR_TO_PACK(I420,BGRA,I420ToARGB)} }; static fmt_convert_map conv_from_bgra[] = { {MAP_CONV_PACK_TO_PACK(BGRA,RGB24,ARGBToRGB24)}, {MAP_CONV_PACK_TO_PACK(BGRA,RGBA,ARGBToABGR)}, {MAP_CONV_PACK_TO_PACK(BGRA,YUY2,ARGBToYUY2)}, {MAP_CONV_PACK_TO_PACK(BGRA,UYVY,ARGBToUYVY)}, {MAP_CONV_PACK_TO_PLANAR(BGRA,I422,ARGBToI422)}, {MAP_CONV_PACK_TO_PLANAR(BGRA,I420,ARGBToI420)}, {MAP_SCALE_PACK(BGRA,ARGBScale)} }; typedef struct converter_act { conv_func_type act_type; struct fmt_info src_fmt_info; struct fmt_info dst_fmt_info; act_method method; } converter_act; struct libyuv_converter { pjmedia_converter base; int act_num; converter_act act[MAXIMUM_ACT]; }; /* Find the matched format conversion map. */ static pj_status_t get_converter_map(pj_uint32_t src_id, pj_uint32_t dst_id, const pjmedia_rect_size *src_size, const pjmedia_rect_size *dst_size, int act_num, converter_act *act) { fmt_convert_map *map = NULL; unsigned cnt = 0, i = 0; unsigned act_idx = act_num - 1; # define GET_MAP(src) \ do { \ map=src; \ cnt=PJ_ARRAY_SIZE(src); \ }while(0) if (src_id == PJMEDIA_FORMAT_I420) { GET_MAP(conv_from_i420); } else if (src_id == PJMEDIA_FORMAT_BGRA) { GET_MAP(conv_from_bgra); } if (!map) { if (dst_id == PJMEDIA_FORMAT_I420) { GET_MAP(conv_to_i420); } else if (dst_id == PJMEDIA_FORMAT_BGRA) { GET_MAP(conv_to_bgra); } } if (!map) return PJ_ENOTSUP; for (;icolor_model == PJMEDIA_COLOR_MODEL_YUV) { return PJMEDIA_FORMAT_I420; } else { return PJMEDIA_FORMAT_BGRA; } } /* This method will find and set all the steps needed for conversion/scale. * More than one step might be needed, since not all format is provided with * a direct conversion/scale method. * e.g : Scale YUY2 (240*320) to YUY2 (320*720) * - Step 1: Convert YUY2(240*320) to I420 (240*320) * - Step 2: Scale I420 (320*760) * - Step 3: Convert I420 (320*760) to YUY2 (320*760) */ static int set_converter_act(pj_uint32_t src_id, pj_uint32_t dst_id, const pjmedia_rect_size *src_size, const pjmedia_rect_size *dst_size, converter_act *act) { unsigned act_num = 0; pj_uint32_t current_id = src_id; pj_bool_t need_scale = PJ_FALSE; /* Convert to I420 or BGRA if needed. */ if ((src_id != PJMEDIA_FORMAT_I420) || (src_id != PJMEDIA_FORMAT_BGRA)) { pj_uint32_t next_id = get_next_conv_fmt(src_id); if (get_converter_map(src_id, next_id, src_size, dst_size, ++act_num, act) != PJ_SUCCESS) { return 0; } current_id = next_id; } /* Scale if needed */ need_scale = ((src_size->w != dst_size->w) || (src_size->h != dst_size->h)); if (need_scale) { if (get_converter_map(current_id, current_id, src_size, dst_size, ++act_num, act) != PJ_SUCCESS) { return 0; } } /* Convert if needed */ if (current_id != dst_id) { if (get_converter_map(current_id, dst_id, src_size, dst_size, ++act_num, act) != PJ_SUCCESS) { return 0; } } return act_num; } /* Additional buffer might be needed for formats without direct conversion/scale * method. This method will allocate and set the destination buffer needed by * the conversion/scaling process. */ static pj_status_t set_destination_buffer(pj_pool_t *pool, struct libyuv_converter *lconv) { int i = 0; for (;iact_num-1;++i) { pj_size_t buffer_size = 0; fmt_info *info = &lconv->act[i].dst_fmt_info; /* Get destination buffer size. */ (*info->vid_fmt_info->apply_fmt)(info->vid_fmt_info, &info->apply_param); buffer_size = info->apply_param.framebytes; /* Allocate buffer. */ lconv->act[i].dst_fmt_info.apply_param.buffer = (pj_uint8_t*)pj_pool_alloc(pool, buffer_size); if (!lconv->act[i].dst_fmt_info.apply_param.buffer) return PJ_ENOMEM; } return PJ_SUCCESS; } /* Check the act input/output format matched the conversion/scale format. */ static pj_bool_t check_converter_act(const converter_act *act, int act_num, pj_uint32_t src_id, const pjmedia_rect_size *src_size, pj_uint32_t dst_id, const pjmedia_rect_size *dst_size) { if (act_num) { const struct fmt_info *first_fmt = &act[0].src_fmt_info; const struct fmt_info *last_fmt = &act[act_num-1].dst_fmt_info; if ((first_fmt->vid_fmt_info->id == src_id) && (first_fmt->apply_param.size.h == src_size->h) && (first_fmt->apply_param.size.w == src_size->w) && (last_fmt->vid_fmt_info->id == dst_id) && (last_fmt->apply_param.size.h == dst_size->h) && (last_fmt->apply_param.size.w == dst_size->w)) { return PJ_TRUE; } } return PJ_FALSE; } static pj_status_t factory_create_converter(pjmedia_converter_factory *cf, pj_pool_t *pool, const pjmedia_conversion_param *prm, pjmedia_converter **p_cv) { const pjmedia_video_format_detail *src_detail, *dst_detail; const pjmedia_video_format_info *src_fmt_info, *dst_fmt_info; struct libyuv_converter *lconv = NULL; pj_status_t status = PJ_ENOTSUP; PJ_UNUSED_ARG(cf); /* Only supports video */ if (prm->src.type != PJMEDIA_TYPE_VIDEO || prm->dst.type != prm->src.type || prm->src.detail_type != PJMEDIA_FORMAT_DETAIL_VIDEO || prm->dst.detail_type != prm->src.detail_type) { return status; } /* lookup source format info */ src_fmt_info = pjmedia_get_video_format_info( pjmedia_video_format_mgr_instance(), prm->src.id); if (!src_fmt_info) return status; /* lookup destination format info */ dst_fmt_info = pjmedia_get_video_format_info( pjmedia_video_format_mgr_instance(), prm->dst.id); if (!dst_fmt_info) return status; src_detail = pjmedia_format_get_video_format_detail(&prm->src, PJ_TRUE); dst_detail = pjmedia_format_get_video_format_detail(&prm->dst, PJ_TRUE); lconv = PJ_POOL_ZALLOC_T(pool, struct libyuv_converter); lconv->base.op = &libyuv_converter_op; lconv->act_num = set_converter_act(src_fmt_info->id, dst_fmt_info->id, &src_detail->size, &dst_detail->size, lconv->act); if (!lconv->act_num) { return status; } if (!check_converter_act(lconv->act, lconv->act_num, src_fmt_info->id, &src_detail->size, dst_fmt_info->id, &dst_detail->size)) { return status; } status = set_destination_buffer(pool, lconv); *p_cv = &lconv->base; return status; } static void factory_destroy_factory(pjmedia_converter_factory *cf) { PJ_UNUSED_ARG(cf); } static pj_status_t libyuv_conv_convert(pjmedia_converter *converter, pjmedia_frame *src_frame, pjmedia_frame *dst_frame) { struct libyuv_converter *lconv = (struct libyuv_converter*)converter; int i = 0; /* Set the first act buffer from src frame. */ lconv->act[0].src_fmt_info.apply_param.buffer = src_frame->buf; /* Set the last act buffer from dst frame. */ lconv->act[lconv->act_num-1].dst_fmt_info.apply_param.buffer = dst_frame->buf; for (;iact_num;++i) { /* Use destination info as the source info for the next act. */ struct fmt_info *src_fmt_info = (i==0)?&lconv->act[i].src_fmt_info: &lconv->act[i-1].dst_fmt_info; struct fmt_info *dst_fmt_info = &lconv->act[i].dst_fmt_info; (*src_fmt_info->vid_fmt_info->apply_fmt)(src_fmt_info->vid_fmt_info, &src_fmt_info->apply_param); (*dst_fmt_info->vid_fmt_info->apply_fmt)(dst_fmt_info->vid_fmt_info, &dst_fmt_info->apply_param); switch (lconv->act[i].act_type) { case CONV_PACK_TO_PACK: (*lconv->act[i].method.conv_pack_to_pack)( (const uint8*)src_fmt_info->apply_param.planes[0], src_fmt_info->apply_param.strides[0], dst_fmt_info->apply_param.planes[0], dst_fmt_info->apply_param.strides[0], dst_fmt_info->apply_param.size.w, dst_fmt_info->apply_param.size.h); break; case CONV_PACK_TO_PLANAR: (*lconv->act[i].method.conv_pack_to_planar)( (const uint8*)src_fmt_info->apply_param.planes[0], src_fmt_info->apply_param.strides[0], dst_fmt_info->apply_param.planes[0], dst_fmt_info->apply_param.strides[0], dst_fmt_info->apply_param.planes[1], dst_fmt_info->apply_param.strides[1], dst_fmt_info->apply_param.planes[2], dst_fmt_info->apply_param.strides[2], dst_fmt_info->apply_param.size.w, dst_fmt_info->apply_param.size.h); break; case CONV_PLANAR_TO_PACK: (*lconv->act[i].method.conv_planar_to_pack)( (const uint8*)src_fmt_info->apply_param.planes[0], src_fmt_info->apply_param.strides[0], (const uint8*)src_fmt_info->apply_param.planes[1], src_fmt_info->apply_param.strides[1], (const uint8*)src_fmt_info->apply_param.planes[2], src_fmt_info->apply_param.strides[2], dst_fmt_info->apply_param.planes[0], dst_fmt_info->apply_param.strides[0], dst_fmt_info->apply_param.size.w, dst_fmt_info->apply_param.size.h); break; case CONV_PLANAR_TO_PLANAR: (*lconv->act[i].method.conv_planar_to_planar)( (const uint8*)src_fmt_info->apply_param.planes[0], src_fmt_info->apply_param.strides[0], (const uint8*)src_fmt_info->apply_param.planes[1], src_fmt_info->apply_param.strides[1], (const uint8*)src_fmt_info->apply_param.planes[2], src_fmt_info->apply_param.strides[2], dst_fmt_info->apply_param.planes[0], dst_fmt_info->apply_param.strides[0], dst_fmt_info->apply_param.planes[1], dst_fmt_info->apply_param.strides[1], dst_fmt_info->apply_param.planes[2], dst_fmt_info->apply_param.strides[2], dst_fmt_info->apply_param.size.w, dst_fmt_info->apply_param.size.h); break; case SCALE_PACK: (*lconv->act[i].method.scale_pack)( (const uint8*)src_fmt_info->apply_param.planes[0], src_fmt_info->apply_param.strides[0], src_fmt_info->apply_param.size.w, src_fmt_info->apply_param.size.h, (uint8*)dst_fmt_info->apply_param.planes[0], dst_fmt_info->apply_param.strides[0], dst_fmt_info->apply_param.size.w, dst_fmt_info->apply_param.size.h, LIBYUV_FILTER_MODE); break; case SCALE_PLANAR: (*lconv->act[i].method.scale_planar)( (const uint8*)src_fmt_info->apply_param.planes[0], src_fmt_info->apply_param.strides[0], (const uint8*)src_fmt_info->apply_param.planes[1], src_fmt_info->apply_param.strides[1], (const uint8*)src_fmt_info->apply_param.planes[2], src_fmt_info->apply_param.strides[2], src_fmt_info->apply_param.size.w, src_fmt_info->apply_param.size.h, (uint8*)dst_fmt_info->apply_param.planes[0], dst_fmt_info->apply_param.strides[0], (uint8*)dst_fmt_info->apply_param.planes[1], dst_fmt_info->apply_param.strides[1], (uint8*)dst_fmt_info->apply_param.planes[2], dst_fmt_info->apply_param.strides[2], dst_fmt_info->apply_param.size.w, dst_fmt_info->apply_param.size.h, LIBYUV_FILTER_MODE); break; }; } return PJ_SUCCESS; } static void libyuv_conv_destroy(pjmedia_converter *converter) { PJ_UNUSED_ARG(converter); } static pjmedia_converter_factory libyuv_factory = { NULL, NULL, /* list */ "libyuv", /* name */ PJMEDIA_CONVERTER_PRIORITY_NORMAL, /* priority */ NULL /* op will be init-ed later */ }; PJ_DEF(pj_status_t) pjmedia_libyuv_converter_init(pjmedia_converter_mgr *mgr) { libyuv_factory.op = &libyuv_factory_op; return pjmedia_converter_mgr_register_factory(mgr, &libyuv_factory); } PJ_DEF(pj_status_t) pjmedia_libyuv_converter_shutdown(pjmedia_converter_mgr *mgr, pj_pool_t *pool) { PJ_UNUSED_ARG(pool); return pjmedia_converter_mgr_unregister_factory(mgr, &libyuv_factory, PJ_TRUE); } #ifdef _MSC_VER # pragma comment(lib, "libyuv.lib") #endif #endif //#if defined(PJMEDIA_HAS_LIBYUV) && PJMEDIA_HAS_LIBYUV != 0 ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/delaybuf.c ================================================ /* $Id: delaybuf.c 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #if 0 # define TRACE__(x) PJ_LOG(3,x) #else # define TRACE__(x) #endif /* Operation types of delay buffer */ enum OP { OP_PUT, OP_GET }; /* Specify time for delaybuf to recalculate effective delay, in ms. */ #define RECALC_TIME 2000 /* Default value of maximum delay, in ms, this value is used when * maximum delay requested is less than ptime (one frame length). */ #define DEFAULT_MAX_DELAY 400 /* Number of frames to add to learnt level for additional stability. */ #define SAFE_MARGIN 0 /* This structure describes internal delaybuf settings and states. */ struct pjmedia_delay_buf { /* Properties and configuration */ char obj_name[PJ_MAX_OBJ_NAME]; pj_lock_t *lock; /**< Lock object. */ unsigned samples_per_frame; /**< Number of samples in one frame */ unsigned ptime; /**< Frame time, in ms */ unsigned channel_count; /**< Channel count, in ms */ pjmedia_circ_buf *circ_buf; /**< Circular buffer to store audio samples */ unsigned max_cnt; /**< Maximum samples to be buffered */ unsigned eff_cnt; /**< Effective count of buffered samples to keep the optimum balance between delay and stability. This is calculated based on burst level. */ /* Learning vars */ unsigned level; /**< Burst level counter */ enum OP last_op; /**< Last op (GET or PUT) of learning*/ int recalc_timer; /**< Timer for recalculating max_level*/ unsigned max_level; /**< Current max burst level */ /* Drift handler */ pjmedia_wsola *wsola; /**< Drift handler */ }; PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool, const char *name, unsigned clock_rate, unsigned samples_per_frame, unsigned channel_count, unsigned max_delay, unsigned options, pjmedia_delay_buf **p_b) { pjmedia_delay_buf *b; pj_status_t status; PJ_ASSERT_RETURN(pool && samples_per_frame && clock_rate && channel_count && p_b, PJ_EINVAL); if (!name) { name = "delaybuf"; } b = PJ_POOL_ZALLOC_T(pool, pjmedia_delay_buf); pj_ansi_strncpy(b->obj_name, name, PJ_MAX_OBJ_NAME-1); b->samples_per_frame = samples_per_frame; b->channel_count = channel_count; b->ptime = samples_per_frame * 1000 / clock_rate / channel_count; if (max_delay < b->ptime) max_delay = PJ_MAX(DEFAULT_MAX_DELAY, b->ptime); b->max_cnt = samples_per_frame * max_delay / b->ptime; b->eff_cnt = b->max_cnt >> 1; b->recalc_timer = RECALC_TIME; /* Create circular buffer */ status = pjmedia_circ_buf_create(pool, b->max_cnt, &b->circ_buf); if (status != PJ_SUCCESS) return status; if (!(options & PJMEDIA_DELAY_BUF_SIMPLE_FIFO)) { /* Create WSOLA */ status = pjmedia_wsola_create(pool, clock_rate, samples_per_frame, 1, PJMEDIA_WSOLA_NO_FADING, &b->wsola); if (status != PJ_SUCCESS) return status; PJ_LOG(5, (b->obj_name, "Using delay buffer with WSOLA.")); } else { PJ_LOG(5, (b->obj_name, "Using simple FIFO delay buffer.")); } /* Finally, create mutex */ status = pj_lock_create_recursive_mutex(pool, b->obj_name, &b->lock); if (status != PJ_SUCCESS) return status; *p_b = b; TRACE__((b->obj_name,"Delay buffer created")); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_delay_buf_destroy(pjmedia_delay_buf *b) { pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(b, PJ_EINVAL); pj_lock_acquire(b->lock); if (b->wsola) { status = pjmedia_wsola_destroy(b->wsola); if (status == PJ_SUCCESS) b->wsola = NULL; } pj_lock_release(b->lock); pj_lock_destroy(b->lock); b->lock = NULL; return status; } /* This function will erase samples from delay buffer. * The number of erased samples is guaranteed to be >= erase_cnt. */ static void shrink_buffer(pjmedia_delay_buf *b, unsigned erase_cnt) { pj_int16_t *buf1, *buf2; unsigned buf1len; unsigned buf2len; pj_status_t status; pj_assert(b && erase_cnt && pjmedia_circ_buf_get_len(b->circ_buf)); pjmedia_circ_buf_get_read_regions(b->circ_buf, &buf1, &buf1len, &buf2, &buf2len); status = pjmedia_wsola_discard(b->wsola, buf1, buf1len, buf2, buf2len, &erase_cnt); if ((status == PJ_SUCCESS) && (erase_cnt > 0)) { /* WSOLA discard will manage the first buffer to be full, unless * erase_cnt is greater than second buffer length. So it is safe * to just set the circular buffer length. */ pjmedia_circ_buf_set_len(b->circ_buf, pjmedia_circ_buf_get_len(b->circ_buf) - erase_cnt); PJ_LOG(5,(b->obj_name,"%d samples reduced, buf_cnt=%d", erase_cnt, pjmedia_circ_buf_get_len(b->circ_buf))); } } /* Fast increase, slow decrease */ #define AGC_UP(cur, target) cur = (cur + target*3) >> 2 #define AGC_DOWN(cur, target) cur = (cur*3 + target) >> 2 #define AGC(cur, target) \ if (cur < target) AGC_UP(cur, target); \ else AGC_DOWN(cur, target) static void update(pjmedia_delay_buf *b, enum OP op) { /* Sequential operation */ if (op == b->last_op) { ++b->level; return; } /* Switching operation */ if (b->level > b->max_level) b->max_level = b->level; b->recalc_timer -= (b->level * b->ptime) >> 1; b->last_op = op; b->level = 1; /* Recalculate effective count based on max_level */ if (b->recalc_timer <= 0) { unsigned new_eff_cnt = (b->max_level+SAFE_MARGIN)*b->samples_per_frame; /* Smoothening effective count transition */ AGC(b->eff_cnt, new_eff_cnt); /* Make sure the new effective count is multiplication of * channel_count, so let's round it up. */ if (b->eff_cnt % b->channel_count) b->eff_cnt += b->channel_count - (b->eff_cnt % b->channel_count); TRACE__((b->obj_name,"Cur eff_cnt=%d", b->eff_cnt)); b->max_level = 0; b->recalc_timer = RECALC_TIME; } /* See if we need to shrink the buffer to reduce delay */ if (op == OP_PUT && pjmedia_circ_buf_get_len(b->circ_buf) > b->samples_per_frame + b->eff_cnt) { unsigned erase_cnt = b->samples_per_frame >> 1; unsigned old_buf_cnt = pjmedia_circ_buf_get_len(b->circ_buf); shrink_buffer(b, erase_cnt); PJ_LOG(4,(b->obj_name,"Buffer size adjusted from %d to %d (eff_cnt=%d)", old_buf_cnt, pjmedia_circ_buf_get_len(b->circ_buf), b->eff_cnt)); } } PJ_DEF(pj_status_t) pjmedia_delay_buf_put(pjmedia_delay_buf *b, pj_int16_t frame[]) { pj_status_t status; PJ_ASSERT_RETURN(b && frame, PJ_EINVAL); pj_lock_acquire(b->lock); if (b->wsola) { update(b, OP_PUT); status = pjmedia_wsola_save(b->wsola, frame, PJ_FALSE); if (status != PJ_SUCCESS) { pj_lock_release(b->lock); return status; } } /* Overflow checking */ if (pjmedia_circ_buf_get_len(b->circ_buf) + b->samples_per_frame > b->max_cnt) { unsigned erase_cnt; if (b->wsola) { /* shrink one frame or just the diff? */ //erase_cnt = b->samples_per_frame; erase_cnt = pjmedia_circ_buf_get_len(b->circ_buf) + b->samples_per_frame - b->max_cnt; shrink_buffer(b, erase_cnt); } /* Check if shrinking failed or erased count is less than requested, * delaybuf needs to drop eldest samples, this is bad since the voice * samples get rough transition which may produce tick noise. */ if (pjmedia_circ_buf_get_len(b->circ_buf) + b->samples_per_frame > b->max_cnt) { erase_cnt = pjmedia_circ_buf_get_len(b->circ_buf) + b->samples_per_frame - b->max_cnt; pjmedia_circ_buf_adv_read_ptr(b->circ_buf, erase_cnt); PJ_LOG(4,(b->obj_name,"%sDropping %d eldest samples, buf_cnt=%d", (b->wsola? "Shrinking failed or insufficient. ": ""), erase_cnt, pjmedia_circ_buf_get_len(b->circ_buf))); } } pjmedia_circ_buf_write(b->circ_buf, frame, b->samples_per_frame); pj_lock_release(b->lock); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_delay_buf_get( pjmedia_delay_buf *b, pj_int16_t frame[]) { pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(b && frame, PJ_EINVAL); pj_lock_acquire(b->lock); if (b->wsola) update(b, OP_GET); /* Starvation checking */ if (pjmedia_circ_buf_get_len(b->circ_buf) < b->samples_per_frame) { PJ_LOG(4,(b->obj_name,"Underflow, buf_cnt=%d, will generate 1 frame", pjmedia_circ_buf_get_len(b->circ_buf))); if (b->wsola) { status = pjmedia_wsola_generate(b->wsola, frame); if (status == PJ_SUCCESS) { TRACE__((b->obj_name,"Successfully generate 1 frame")); if (pjmedia_circ_buf_get_len(b->circ_buf) == 0) { pj_lock_release(b->lock); return PJ_SUCCESS; } /* Put generated frame into buffer */ pjmedia_circ_buf_write(b->circ_buf, frame, b->samples_per_frame); } } if (!b->wsola || status != PJ_SUCCESS) { unsigned buf_len = pjmedia_circ_buf_get_len(b->circ_buf); /* Give all what delay buffer has, then pad with zeroes */ if (b->wsola) PJ_LOG(4,(b->obj_name,"Error generating frame, status=%d", status)); pjmedia_circ_buf_read(b->circ_buf, frame, buf_len); pjmedia_zero_samples(&frame[buf_len], b->samples_per_frame - buf_len); /* The buffer is empty now, reset it */ pjmedia_circ_buf_reset(b->circ_buf); pj_lock_release(b->lock); return PJ_SUCCESS; } } pjmedia_circ_buf_read(b->circ_buf, frame, b->samples_per_frame); pj_lock_release(b->lock); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_delay_buf_reset(pjmedia_delay_buf *b) { PJ_ASSERT_RETURN(b, PJ_EINVAL); pj_lock_acquire(b->lock); b->recalc_timer = RECALC_TIME; /* Reset buffer */ pjmedia_circ_buf_reset(b->circ_buf); /* Reset WSOLA */ if (b->wsola) pjmedia_wsola_reset(b->wsola, 0); pj_lock_release(b->lock); PJ_LOG(5,(b->obj_name,"Delay buffer is reset")); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/dummy.c ================================================ /* $Id: dummy.c 3715 2011-08-19 09:35:25Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/echo_common.c ================================================ /* $Id: echo_common.c 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include "echo_internal.h" #define THIS_FILE "echo_common.c" typedef struct ec_operations ec_operations; struct frame { PJ_DECL_LIST_MEMBER(struct frame); short buf[1]; }; struct pjmedia_echo_state { pj_pool_t *pool; char *obj_name; unsigned samples_per_frame; void *state; ec_operations *op; pj_bool_t lat_ready; /* lat_buf has been filled in. */ struct frame lat_buf; /* Frame queue for delayed playback */ struct frame lat_free; /* Free frame list. */ pjmedia_delay_buf *delay_buf; pj_int16_t *frm_buf; }; struct ec_operations { const char *name; pj_status_t (*ec_create)(pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned tail_ms, unsigned options, void **p_state ); pj_status_t (*ec_destroy)(void *state ); void (*ec_reset)(void *state ); pj_status_t (*ec_cancel)(void *state, pj_int16_t *rec_frm, const pj_int16_t *play_frm, unsigned options, void *reserved ); pj_status_t (*ec_playback)(void *state, pj_int16_t *play_frm ); pj_status_t (*ec_capture)(void *state, pj_int16_t *rec_frm, unsigned options ); }; static struct ec_operations echo_supp_op = { "Echo suppressor", &echo_supp_create, &echo_supp_destroy, &echo_supp_reset, &echo_supp_cancel_echo }; /* * Speex AEC prototypes */ #if defined(PJMEDIA_HAS_SPEEX_AEC) && PJMEDIA_HAS_SPEEX_AEC!=0 static struct ec_operations speex_aec_op = { "AEC", &speex_aec_create, &speex_aec_destroy, &speex_aec_reset, &speex_aec_cancel_echo, &speex_aec_playback, &speex_aec_capture }; #endif /* * IPP AEC prototypes */ #if defined(PJMEDIA_HAS_INTEL_IPP_AEC) && PJMEDIA_HAS_INTEL_IPP_AEC!=0 static struct ec_operations ipp_aec_op = { "IPP AEC", &ipp_aec_create, &ipp_aec_destroy, &ipp_aec_reset, &ipp_aec_cancel_echo }; #endif /* * WebRTC prototypes */ #if defined(PJMEDIA_HAS_WEBRTC_AEC) && PJMEDIA_HAS_WEBRTC_AEC!=0 static struct ec_operations webrtc_aec_op = { "WEBRTC AEC", &webrtc_aec_create, &webrtc_aec_destroy, &webrtc_aec_reset, &webrtc_aec_cancel_echo }; #endif /* * Create the echo canceller. */ PJ_DEF(pj_status_t) pjmedia_echo_create( pj_pool_t *pool, unsigned clock_rate, unsigned samples_per_frame, unsigned tail_ms, unsigned latency_ms, unsigned options, pjmedia_echo_state **p_echo ) { return pjmedia_echo_create2(pool, clock_rate, 1, samples_per_frame, tail_ms, latency_ms, options, p_echo); } /* * Create the echo canceller. */ PJ_DEF(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned tail_ms, unsigned latency_ms, unsigned options, pjmedia_echo_state **p_echo ) { unsigned ptime, lat_cnt; unsigned delay_buf_opt = 0; pjmedia_echo_state *ec; pj_status_t status; /* Create new pool and instantiate and init the EC */ pool = pj_pool_create(pool->factory, "ec%p", 256, 256, NULL); ec = PJ_POOL_ZALLOC_T(pool, struct pjmedia_echo_state); ec->pool = pool; ec->obj_name = pool->obj_name; ec->samples_per_frame = samples_per_frame; ec->frm_buf = (pj_int16_t*)pj_pool_alloc(pool, samples_per_frame<<1); pj_list_init(&ec->lat_buf); pj_list_init(&ec->lat_free); /* Select the backend algorithm */ if (0) { /* Dummy */ ; #if defined(PJMEDIA_HAS_SPEEX_AEC) && PJMEDIA_HAS_SPEEX_AEC!=0 } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_SPEEX || (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT) { ec->op = &speex_aec_op; #endif #if defined(PJMEDIA_HAS_WEBRTC_AEC) && PJMEDIA_HAS_WEBRTC_AEC!=0 } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_WEBRTC || (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT) { ec->op = &webrtc_aec_op; #endif #if defined(PJMEDIA_HAS_INTEL_IPP_AEC) && PJMEDIA_HAS_INTEL_IPP_AEC!=0 } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_IPP || (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT) { ec->op = &ipp_aec_op; #endif } else { ec->op = &echo_supp_op; } /* Completeness check for EC operation playback and capture, they must * be implemented both or none. */ pj_assert(!ec->op->ec_capture == !ec->op->ec_playback); PJ_LOG(5,(ec->obj_name, "Creating %s", ec->op->name)); /* Instantiate EC object */ status = (*ec->op->ec_create)(pool, clock_rate, channel_count, samples_per_frame, tail_ms, options, &ec->state); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } /* If EC algo does not have playback and capture callbakcs, * create latency buffer and delay buffer to handle drift. */ if (ec->op->ec_playback && ec->op->ec_capture) { latency_ms = 0; } else { /* Create latency buffers */ ptime = samples_per_frame * 1000 / clock_rate; if (latency_ms > ptime) { /* Normalize latency with delaybuf/WSOLA latency */ latency_ms -= PJ_MIN(ptime, PJMEDIA_WSOLA_DELAY_MSEC); } if (latency_ms < ptime) { /* Give at least one frame delay to simplify programming */ latency_ms = ptime; } lat_cnt = latency_ms / ptime; while (lat_cnt--) { struct frame *frm; frm = (struct frame*) pj_pool_alloc(pool, (samples_per_frame<<1) + sizeof(struct frame)); pj_list_push_back(&ec->lat_free, frm); } /* Create delay buffer to compensate drifts */ if (options & PJMEDIA_ECHO_USE_SIMPLE_FIFO) delay_buf_opt |= PJMEDIA_DELAY_BUF_SIMPLE_FIFO; status = pjmedia_delay_buf_create(ec->pool, ec->obj_name, clock_rate, samples_per_frame, channel_count, (PJMEDIA_SOUND_BUFFER_COUNT+1) * ptime, delay_buf_opt, &ec->delay_buf); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } } PJ_LOG(4,(ec->obj_name, "%s created, clock_rate=%d, channel=%d, " "samples per frame=%d, tail length=%d ms, " "latency=%d ms", ec->op->name, clock_rate, channel_count, samples_per_frame, tail_ms, latency_ms)); /* Done */ *p_echo = ec; return PJ_SUCCESS; } /* * Destroy the Echo Canceller. */ PJ_DEF(pj_status_t) pjmedia_echo_destroy(pjmedia_echo_state *echo ) { (*echo->op->ec_destroy)(echo->state); if (echo->delay_buf) { pjmedia_delay_buf_destroy(echo->delay_buf); echo->delay_buf = NULL; } pj_pool_release(echo->pool); return PJ_SUCCESS; } /* * Reset the echo canceller. */ PJ_DEF(pj_status_t) pjmedia_echo_reset(pjmedia_echo_state *echo ) { while (!pj_list_empty(&echo->lat_buf)) { struct frame *frm; frm = echo->lat_buf.next; pj_list_erase(frm); pj_list_push_back(&echo->lat_free, frm); } echo->lat_ready = PJ_FALSE; if (echo->delay_buf) pjmedia_delay_buf_reset(echo->delay_buf); echo->op->ec_reset(echo->state); return PJ_SUCCESS; } /* * Let the Echo Canceller know that a frame has been played to the speaker. */ PJ_DEF(pj_status_t) pjmedia_echo_playback( pjmedia_echo_state *echo, pj_int16_t *play_frm ) { /* If EC algo has playback handler, just pass the frame. */ if (echo->op->ec_playback) { return (*echo->op->ec_playback)(echo->state, play_frm); } /* Playing frame should be stored, as it will be used by echo_capture() * as reference frame, delay buffer is used for storing the playing frames * as in case there was clock drift between mic & speaker. * * Ticket #830: * Note that pjmedia_delay_buf_put() may modify the input frame and those * modified frames may not be smooth, i.e: if there were two or more * consecutive pjmedia_delay_buf_get() before next pjmedia_delay_buf_put(), * so we'll just feed the delay buffer with the copy of playing frame, * instead of the original playing frame. However this will cause the EC * uses slight 'different' frames (for reference) than actually played * by the speaker. */ pjmedia_copy_samples(echo->frm_buf, play_frm, echo->samples_per_frame); pjmedia_delay_buf_put(echo->delay_buf, echo->frm_buf); if (!echo->lat_ready) { /* We've not built enough latency in the buffer, so put this frame * in the latency buffer list. */ struct frame *frm; if (pj_list_empty(&echo->lat_free)) { echo->lat_ready = PJ_TRUE; PJ_LOG(5,(echo->obj_name, "Latency bufferring complete")); return PJ_SUCCESS; } frm = echo->lat_free.prev; pj_list_erase(frm); /* Move one frame from delay buffer to the latency buffer. */ pjmedia_delay_buf_get(echo->delay_buf, echo->frm_buf); pjmedia_copy_samples(frm->buf, echo->frm_buf, echo->samples_per_frame); pj_list_push_back(&echo->lat_buf, frm); } return PJ_SUCCESS; } /* * Let the Echo Canceller knows that a frame has been captured from * the microphone. */ PJ_DEF(pj_status_t) pjmedia_echo_capture( pjmedia_echo_state *echo, pj_int16_t *rec_frm, unsigned options ) { struct frame *oldest_frm; pj_status_t status, rc; /* If EC algo has capture handler, just pass the frame. */ if (echo->op->ec_capture) { return (*echo->op->ec_capture)(echo->state, rec_frm, options); } if (!echo->lat_ready) { /* Prefetching to fill in the desired latency */ PJ_LOG(5,(echo->obj_name, "Prefetching..")); return PJ_SUCCESS; } /* Retrieve oldest frame from the latency buffer */ oldest_frm = echo->lat_buf.next; pj_list_erase(oldest_frm); /* Cancel echo using this reference frame */ status = pjmedia_echo_cancel(echo, rec_frm, oldest_frm->buf, options, NULL); /* Move one frame from delay buffer to the latency buffer. */ rc = pjmedia_delay_buf_get(echo->delay_buf, oldest_frm->buf); if (rc != PJ_SUCCESS) { /* Ooops.. no frame! */ PJ_LOG(5,(echo->obj_name, "No frame from delay buffer. This will upset EC later")); pjmedia_zero_samples(oldest_frm->buf, echo->samples_per_frame); } pj_list_push_back(&echo->lat_buf, oldest_frm); return status; } /* * Perform echo cancellation. */ PJ_DEF(pj_status_t) pjmedia_echo_cancel( pjmedia_echo_state *echo, pj_int16_t *rec_frm, const pj_int16_t *play_frm, unsigned options, void *reserved ) { return (*echo->op->ec_cancel)( echo->state, rec_frm, play_frm, options, reserved); } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/echo_internal.h ================================================ /* $Id: echo_internal.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_ECHO_INTERNAL_H__ #define __PJMEDIA_ECHO_INTERNAL_H__ #include PJ_BEGIN_DECL /* * Simple echo suppressor */ PJ_DECL(pj_status_t) echo_supp_create(pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned tail_ms, unsigned options, void **p_state ); PJ_DECL(pj_status_t) echo_supp_destroy(void *state); PJ_DECL(void) echo_supp_reset(void *state); PJ_DECL(pj_status_t) echo_supp_cancel_echo(void *state, pj_int16_t *rec_frm, const pj_int16_t *play_frm, unsigned options, void *reserved ); PJ_DECL(pj_status_t) speex_aec_create(pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned tail_ms, unsigned options, void **p_state ); PJ_DECL(pj_status_t) speex_aec_destroy(void *state ); PJ_DECL(void) speex_aec_reset(void *state ); PJ_DECL(pj_status_t) speex_aec_cancel_echo(void *state, pj_int16_t *rec_frm, const pj_int16_t *play_frm, unsigned options, void *reserved ); PJ_DECL(pj_status_t) speex_aec_playback(void *state, pj_int16_t *play_frm ); PJ_DECL(pj_status_t) speex_aec_capture(void *state, pj_int16_t *rec_frm, unsigned options ); PJ_DECL(pj_status_t) ipp_aec_create(pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned tail_ms, unsigned options, void **p_echo ); PJ_DECL(pj_status_t) ipp_aec_destroy(void *state ); PJ_DECL(void) ipp_aec_reset(void *state ); PJ_DECL(pj_status_t) ipp_aec_cancel_echo(void *state, pj_int16_t *rec_frm, const pj_int16_t *play_frm, unsigned options, void *reserved ); PJ_DECL(pj_status_t) webrtc_aec_create(pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned tail_ms, unsigned options, void **p_echo ); PJ_DECL(pj_status_t) webrtc_aec_destroy(void *state ); PJ_DECL(void) webrtc_aec_reset(void *state ); PJ_DECL(pj_status_t) webrtc_aec_cancel_echo(void *state, pj_int16_t *rec_frm, const pj_int16_t *play_frm, unsigned options, void *reserved ); PJ_END_DECL #endif ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/echo_port.c ================================================ /* $Id: echo_port.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #define THIS_FILE "ec_port.c" #define SIGNATURE PJMEDIA_SIG_PORT_ECHO #define BUF_COUNT 32 struct ec { pjmedia_port base; pjmedia_port *dn_port; pjmedia_echo_state *ec; }; static pj_status_t ec_put_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t ec_get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t ec_on_destroy(pjmedia_port *this_port); PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool, pjmedia_port *dn_port, unsigned tail_ms, unsigned latency_ms, unsigned options, pjmedia_port **p_port ) { const pj_str_t AEC = { "EC", 2 }; pjmedia_audio_format_detail *afd; struct ec *ec; pj_status_t status; PJ_ASSERT_RETURN(pool && dn_port && p_port, PJ_EINVAL); afd = pjmedia_format_get_audio_format_detail(&dn_port->info.fmt, PJ_TRUE); PJ_ASSERT_RETURN(afd->bits_per_sample==16 && tail_ms, PJ_EINVAL); /* Create the port and the AEC itself */ ec = PJ_POOL_ZALLOC_T(pool, struct ec); pjmedia_port_info_init(&ec->base.info, &AEC, SIGNATURE, afd->clock_rate, afd->channel_count, afd->bits_per_sample, PJMEDIA_AFD_SPF(afd)); status = pjmedia_echo_create2(pool, afd->clock_rate, afd->channel_count, PJMEDIA_AFD_SPF(afd), tail_ms, latency_ms, options, &ec->ec); if (status != PJ_SUCCESS) return status; /* More init */ ec->dn_port = dn_port; ec->base.get_frame = &ec_get_frame; ec->base.put_frame = &ec_put_frame; ec->base.on_destroy = &ec_on_destroy; /* Done */ *p_port = &ec->base; return PJ_SUCCESS; } static pj_status_t ec_put_frame( pjmedia_port *this_port, pjmedia_frame *frame) { struct ec *ec = (struct ec*)this_port; PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVAL); if (frame->type == PJMEDIA_FRAME_TYPE_NONE ) { return pjmedia_port_put_frame(ec->dn_port, frame); } PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info), PJ_EINVAL); pjmedia_echo_capture(ec->ec, (pj_int16_t*)frame->buf, 0); return pjmedia_port_put_frame(ec->dn_port, frame); } static pj_status_t ec_get_frame( pjmedia_port *this_port, pjmedia_frame *frame) { struct ec *ec = (struct ec*)this_port; pj_status_t status; PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVAL); status = pjmedia_port_get_frame(ec->dn_port, frame); if (status!=PJ_SUCCESS || frame->type!=PJMEDIA_FRAME_TYPE_AUDIO) { pjmedia_zero_samples((pj_int16_t*)frame->buf, PJMEDIA_PIA_SPF(&this_port->info)); } pjmedia_echo_playback(ec->ec, (pj_int16_t*)frame->buf); return status; } static pj_status_t ec_on_destroy(pjmedia_port *this_port) { struct ec *ec = (struct ec*)this_port; PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVAL); pjmedia_echo_destroy(ec->ec); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/echo_speex.c ================================================ /* $Id: echo_speex.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #if defined(PJMEDIA_HAS_SPEEX_AEC) && PJMEDIA_HAS_SPEEX_AEC != 0 #include #include #include "echo_internal.h" typedef struct speex_ec { SpeexEchoState *state; SpeexPreprocessState *preprocess; unsigned samples_per_frame; unsigned prefetch; unsigned options; pj_int16_t *tmp_frame; } speex_ec; /* * Create the AEC. */ PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned tail_ms, unsigned options, void **p_echo ) { speex_ec *echo; int sampling_rate; *p_echo = NULL; echo = PJ_POOL_ZALLOC_T(pool, speex_ec); PJ_ASSERT_RETURN(echo != NULL, PJ_ENOMEM); echo->samples_per_frame = samples_per_frame; echo->options = options; #if 0 echo->state = speex_echo_state_init_mc(echo->samples_per_frame, clock_rate * tail_ms / 1000, channel_count, channel_count); #else if (channel_count != 1) { PJ_LOG(2,("echo_speex.c", "Multichannel EC is not supported by this " "echo canceller. It may not work.")); } echo->state = speex_echo_state_init(echo->samples_per_frame, clock_rate * tail_ms / 1000); #endif if (echo->state == NULL) { return PJ_ENOMEM; } /* Set sampling rate */ sampling_rate = clock_rate; speex_echo_ctl(echo->state, SPEEX_ECHO_SET_SAMPLING_RATE, &sampling_rate); echo->preprocess = speex_preprocess_state_init(echo->samples_per_frame, clock_rate); if (echo->preprocess == NULL) { speex_echo_state_destroy(echo->state); return PJ_ENOMEM; } /* Disable all preprocessing, we only want echo cancellation */ #if 0 disabled = 0; enabled = 1; speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DENOISE, &enabled); speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_AGC, &disabled); speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_VAD, &disabled); speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DEREVERB, &enabled); #endif /* Enable/disable AGC & denoise */ { spx_int32_t enabled; enabled = PJMEDIA_SPEEX_AEC_USE_AGC; speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_AGC, &enabled); enabled = PJMEDIA_SPEEX_AEC_USE_DENOISE; speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DENOISE, &enabled); } /* Control echo cancellation in the preprocessor */ speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_ECHO_STATE, echo->state); /* Create temporary frame for echo cancellation */ echo->tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, 2*samples_per_frame); PJ_ASSERT_RETURN(echo->tmp_frame != NULL, PJ_ENOMEM); /* Done */ *p_echo = echo; return PJ_SUCCESS; } /* * Destroy AEC */ PJ_DEF(pj_status_t) speex_aec_destroy(void *state ) { speex_ec *echo = (speex_ec*) state; PJ_ASSERT_RETURN(echo && echo->state, PJ_EINVAL); if (echo->state) { speex_echo_state_destroy(echo->state); echo->state = NULL; } if (echo->preprocess) { speex_preprocess_state_destroy(echo->preprocess); echo->preprocess = NULL; } return PJ_SUCCESS; } /* * Reset AEC */ PJ_DEF(void) speex_aec_reset(void *state ) { speex_ec *echo = (speex_ec*) state; speex_echo_state_reset(echo->state); } /* * Perform echo cancellation. */ PJ_DEF(pj_status_t) speex_aec_cancel_echo( void *state, pj_int16_t *rec_frm, const pj_int16_t *play_frm, unsigned options, void *reserved ) { speex_ec *echo = (speex_ec*) state; /* Sanity checks */ PJ_ASSERT_RETURN(echo && rec_frm && play_frm && options==0 && reserved==NULL, PJ_EINVAL); /* Cancel echo, put output in temporary buffer */ speex_echo_cancellation(echo->state, (const spx_int16_t*)rec_frm, (const spx_int16_t*)play_frm, (spx_int16_t*)echo->tmp_frame); /* Preprocess output */ speex_preprocess_run(echo->preprocess, (spx_int16_t*)echo->tmp_frame); /* Copy temporary buffer back to original rec_frm */ pjmedia_copy_samples(rec_frm, echo->tmp_frame, echo->samples_per_frame); return PJ_SUCCESS; } /* * Let AEC know that a frame was queued to be played. */ PJ_DEF(pj_status_t) speex_aec_playback( void *state, pj_int16_t *play_frm ) { speex_ec *echo = (speex_ec*) state; /* Sanity checks */ PJ_ASSERT_RETURN(echo && play_frm, PJ_EINVAL); speex_echo_playback(echo->state, (spx_int16_t*)play_frm); return PJ_SUCCESS; } /* * Perform echo cancellation to captured frame. */ PJ_DEF(pj_status_t) speex_aec_capture( void *state, pj_int16_t *rec_frm, unsigned options ) { speex_ec *echo = (speex_ec*) state; /* Sanity checks */ PJ_ASSERT_RETURN(echo && rec_frm, PJ_EINVAL); PJ_UNUSED_ARG(options); /* Cancel echo */ pjmedia_copy_samples(echo->tmp_frame, rec_frm, echo->samples_per_frame); speex_echo_capture(echo->state, (spx_int16_t*)echo->tmp_frame, (spx_int16_t*)rec_frm); /* Apply preprocessing */ speex_preprocess_run(echo->preprocess, (spx_int16_t*)rec_frm); return PJ_SUCCESS; } #endif ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/echo_suppress.c ================================================ /* $Id: echo_suppress.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include "echo_internal.h" #define THIS_FILE "echo_suppress.c" /* Maximum float constant */ #define MAX_FLOAT (float)1.701411e38 /* The effective learn duration (in seconds) before we declare that learning * is complete. The actual learning duration itself may be longer depending * on the conversation pattern (e.g. we can't detect echo if speaker is only * playing silence). */ #define MAX_CALC_DURATION_SEC 3 /* The internal audio segment length, in milliseconds. 10ms shold be good * and no need to change it. */ #define SEGMENT_PTIME 10 /* The length of the template signal in milliseconds. The longer the template, * the better correlation will be found, at the expense of more processing * and longer learning time. */ #define TEMPLATE_PTIME 200 /* How long to look back in the past to see if either mic or speaker is * active. */ #define SIGNAL_LOOKUP_MSEC 200 /* The minimum level value to be considered as talking, in uLaw complement * (0-255). */ #define MIN_SIGNAL_ULAW 35 /* The period (in seconds) on which the ES will analize it's effectiveness, * and it may trigger soft-reset to force recalculation. */ #define CHECK_PERIOD 30 /* Maximum signal level of average echo residue (in uLaw complement). When * the residue value exceeds this value, we force the ES to re-learn. */ #define MAX_RESIDUE 2.5 #if 0 # define TRACE_(expr) PJ_LOG(5,expr) static const char *state_names[] = { "Null", "local talking", "remote silent", "doubletalk", "remote talking" }; #else # define TRACE_(expr) #endif PJ_INLINE(float) FABS(float val) { if (val < 0) return -val; else return val; } #if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0 typedef float pj_ufloat_t; # define pj_ufloat_from_float(f) (f) # define pj_ufloat_mul_u(val1, f) ((val1) * (f)) # define pj_ufloat_mul_i(val1, f) ((val1) * (f)) #else typedef pj_uint32_t pj_ufloat_t; pj_ufloat_t pj_ufloat_from_float(float f) { return (pj_ufloat_t)(f * 65536); } unsigned pj_ufloat_mul_u(unsigned val1, pj_ufloat_t val2) { return (val1 * val2) >> 16; } int pj_ufloat_mul_i(int val1, pj_ufloat_t val2) { return (val1 * (pj_int32_t)val2) >> 16; } #endif /* Conversation state */ typedef enum talk_state { ST_NULL, ST_LOCAL_TALK, ST_REM_SILENT, ST_DOUBLETALK, ST_REM_TALK } talk_state_t; /* Description: The echo suppressor tries to find the position of echoed signal by looking at the correlation between signal played to the speaker (played signal) and the signal captured from the microphone (recorded signal). To do this, it first divides the frames (from mic and speaker) into segments, calculate the audio level of the segment, and save the level information in the playback and record history (play_hist and rec_hist respectively). In the history, the newest element (depicted as "t0" in the diagram belo) is put in the last position of the array. The record history size is as large as the template size (tmpl_cnt), since we will use the record history as the template to find the best matching position in the playback history. Here is the record history buffer: <--templ_cnt--> +-------------+ | rec_hist | +-------------+ t-templ_cnt......t0 As you can see, the newest frame ("t0") is put as the last element in the array. The playback history size is larger than record history, since we need to find the matching pattern in the past. The playback history size is "templ_cnt + tail_cnt", where "tail_cnt" is the number of segments equal to the maximum tail length. The maximum tail length is set when the ES is created. Here is the playback history buffer: <-----tail_cnt-----> <--templ_cnt--> +-------------------+--------------+ | play_hist | +-------------------+--------------+ t-play_hist_cnt...t-templ_cnt.......t0 Learning: During the processing, the ES calculates the following values: - the correlation value, that is how similar the playback signal compared to the mic signal. The lower the correlation value the better (i.e. more similar) the signal is. The correlation value is done over the template duration. - the gain scaling factor, that is the ratio between mic signal and speaker signal. The ES calculates both the minimum and average ratios. The ES calculates both the values above for every tail position in the playback history. The values are saved in arrays below: <-----tail_cnt-----> +-------------------+ | corr_sum | +-------------------+ | min_factor | +-------------------+ | avg_factor | +-------------------+ At the end of processing, the ES iterates through the correlation array and picks the tail index with the lowest corr_sum value. This is the position where echo is most likely to be found. Processing: Once learning is done, the ES will change the level of the mic signal depending on the state of the conversation and according to the ratio that has been found in the learning phase above. */ /* * The simple echo suppresor state */ typedef struct echo_supp { unsigned clock_rate; /* Clock rate. */ pj_uint16_t samples_per_frame; /* Frame length in samples */ pj_uint16_t samples_per_segment;/* Segment length in samples */ pj_uint16_t tail_ms; /* Tail length in milliseconds */ pj_uint16_t tail_samples; /* Tail length in samples. */ pj_bool_t learning; /* Are we still learning yet? */ talk_state_t talk_state; /* Current talking state */ int tail_index; /* Echo location, -1 if not found */ unsigned max_calc; /* # of calc before learning complete. (see MAX_CALC_DURATION_SEC) */ unsigned calc_cnt; /* Number of calculations so far */ unsigned update_cnt; /* # of updates */ unsigned templ_cnt; /* Template length, in # of segments */ unsigned tail_cnt; /* Tail length, in # of segments */ unsigned play_hist_cnt; /* # of segments in play_hist */ pj_uint16_t *play_hist; /* Array of playback levels */ pj_uint16_t *rec_hist; /* Array of rec levels */ float *corr_sum; /* Array of corr for each tail pos. */ float *tmp_corr; /* Temporary corr array calculation */ float best_corr; /* Best correlation so far. */ unsigned sum_rec_level; /* Running sum of level in rec_hist */ float rec_corr; /* Running corr in rec_hist. */ unsigned sum_play_level0; /* Running sum of level for first pos */ float play_corr0; /* Running corr for first pos . */ float *min_factor; /* Array of minimum scaling factor */ float *avg_factor; /* Array of average scaling factor */ float *tmp_factor; /* Array to store provisional result */ unsigned running_cnt; /* Running duration in # of frames */ float residue; /* Accummulated echo residue. */ float last_factor; /* Last factor applied to mic signal */ } echo_supp; /* * Create. */ PJ_DEF(pj_status_t) echo_supp_create( pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned tail_ms, unsigned options, void **p_state ) { echo_supp *ec; PJ_UNUSED_ARG(channel_count); PJ_UNUSED_ARG(options); PJ_ASSERT_RETURN(samples_per_frame >= SEGMENT_PTIME * clock_rate / 1000, PJ_ENOTSUP); ec = PJ_POOL_ZALLOC_T(pool, struct echo_supp); ec->clock_rate = clock_rate; ec->samples_per_frame = (pj_uint16_t)samples_per_frame; ec->samples_per_segment = (pj_uint16_t)(SEGMENT_PTIME * clock_rate / 1000); ec->tail_ms = (pj_uint16_t)tail_ms; ec->tail_samples = (pj_uint16_t)(tail_ms * clock_rate / 1000); ec->templ_cnt = TEMPLATE_PTIME / SEGMENT_PTIME; ec->tail_cnt = (pj_uint16_t)(tail_ms / SEGMENT_PTIME); ec->play_hist_cnt = (pj_uint16_t)(ec->tail_cnt+ec->templ_cnt); ec->max_calc = (pj_uint16_t)(MAX_CALC_DURATION_SEC * clock_rate / ec->samples_per_segment); ec->rec_hist = (pj_uint16_t*) pj_pool_alloc(pool, ec->templ_cnt * sizeof(ec->rec_hist[0])); /* Note: play history has twice number of elements */ ec->play_hist = (pj_uint16_t*) pj_pool_alloc(pool, ec->play_hist_cnt * sizeof(ec->play_hist[0])); ec->corr_sum = (float*) pj_pool_alloc(pool, ec->tail_cnt * sizeof(ec->corr_sum[0])); ec->tmp_corr = (float*) pj_pool_alloc(pool, ec->tail_cnt * sizeof(ec->tmp_corr[0])); ec->min_factor = (float*) pj_pool_alloc(pool, ec->tail_cnt * sizeof(ec->min_factor[0])); ec->avg_factor = (float*) pj_pool_alloc(pool, ec->tail_cnt * sizeof(ec->avg_factor[0])); ec->tmp_factor = (float*) pj_pool_alloc(pool, ec->tail_cnt * sizeof(ec->tmp_factor[0])); echo_supp_reset(ec); *p_state = ec; return PJ_SUCCESS; } /* * Destroy. */ PJ_DEF(pj_status_t) echo_supp_destroy(void *state) { PJ_UNUSED_ARG(state); return PJ_SUCCESS; } /* * Hard reset */ PJ_DEF(void) echo_supp_reset(void *state) { unsigned i; echo_supp *ec = (echo_supp*) state; pj_bzero(ec->rec_hist, ec->templ_cnt * sizeof(ec->rec_hist[0])); pj_bzero(ec->play_hist, ec->play_hist_cnt * sizeof(ec->play_hist[0])); for (i=0; itail_cnt; ++i) { ec->corr_sum[i] = ec->avg_factor[i] = 0; ec->min_factor[i] = MAX_FLOAT; } ec->update_cnt = 0; ec->calc_cnt = 0; ec->learning = PJ_TRUE; ec->tail_index = -1; ec->best_corr = MAX_FLOAT; ec->talk_state = ST_NULL; ec->last_factor = 1.0; ec->residue = 0; ec->running_cnt = 0; ec->sum_rec_level = ec->sum_play_level0 = 0; ec->rec_corr = ec->play_corr0 = 0; } /* * Soft reset to force the EC to re-learn without having to discard all * rec and playback history. */ PJ_DEF(void) echo_supp_soft_reset(void *state) { unsigned i; echo_supp *ec = (echo_supp*) state; for (i=0; itail_cnt; ++i) { ec->corr_sum[i] = 0; } ec->update_cnt = 0; ec->calc_cnt = 0; ec->learning = PJ_TRUE; ec->best_corr = MAX_FLOAT; ec->residue = 0; ec->running_cnt = 0; ec->sum_rec_level = ec->sum_play_level0 = 0; ec->rec_corr = ec->play_corr0 = 0; PJ_LOG(4,(THIS_FILE, "Echo suppressor soft reset. Re-learning..")); } /* Set state */ static void echo_supp_set_state(echo_supp *ec, talk_state_t state, unsigned level) { PJ_UNUSED_ARG(level); if (state != ec->talk_state) { TRACE_((THIS_FILE, "[%03d.%03d] %s --> %s, level=%u", (ec->update_cnt * SEGMENT_PTIME / 1000), ((ec->update_cnt * SEGMENT_PTIME) % 1000), state_names[ec->talk_state], state_names[state], level)); ec->talk_state = state; } } /* * Update EC state */ static void echo_supp_update(echo_supp *ec, pj_int16_t *rec_frm, const pj_int16_t *play_frm) { int prev_index; unsigned i, j, frm_level, sum_play_level, ulaw; pj_uint16_t old_rec_frm_level, old_play_frm_level; float play_corr; ++ec->update_cnt; if (ec->update_cnt > 0x7FFFFFFF) ec->update_cnt = 0x7FFFFFFF; /* Detect overflow */ /* Calculate current play frame level */ frm_level = pjmedia_calc_avg_signal(play_frm, ec->samples_per_segment); ++frm_level; /* to avoid division by zero */ /* Save the oldest frame level for later */ old_play_frm_level = ec->play_hist[0]; /* Push current frame level to the back of the play history */ pj_array_erase(ec->play_hist, sizeof(pj_uint16_t), ec->play_hist_cnt, 0); ec->play_hist[ec->play_hist_cnt-1] = (pj_uint16_t) frm_level; /* Calculate level of current mic frame */ frm_level = pjmedia_calc_avg_signal(rec_frm, ec->samples_per_segment); ++frm_level; /* to avoid division by zero */ /* Save the oldest frame level for later */ old_rec_frm_level = ec->rec_hist[0]; /* Push to the back of the rec history */ pj_array_erase(ec->rec_hist, sizeof(pj_uint16_t), ec->templ_cnt, 0); ec->rec_hist[ec->templ_cnt-1] = (pj_uint16_t) frm_level; /* Can't do the calc until the play history is full. */ if (ec->update_cnt < ec->play_hist_cnt) return; /* Skip if learning is done */ if (!ec->learning) return; /* Calculate rec signal pattern */ if (ec->sum_rec_level == 0) { /* Buffer has just been filled up, do full calculation */ ec->rec_corr = 0; ec->sum_rec_level = 0; for (i=0; i < ec->templ_cnt-1; ++i) { float corr; corr = (float)ec->rec_hist[i+1] / ec->rec_hist[i]; ec->rec_corr += corr; ec->sum_rec_level += ec->rec_hist[i]; } ec->sum_rec_level += ec->rec_hist[i]; } else { /* Update from previous calculation */ ec->sum_rec_level = ec->sum_rec_level - old_rec_frm_level + ec->rec_hist[ec->templ_cnt-1]; ec->rec_corr = ec->rec_corr - ((float)ec->rec_hist[0] / old_rec_frm_level) + ((float)ec->rec_hist[ec->templ_cnt-1] / ec->rec_hist[ec->templ_cnt-2]); } /* Iterate through the play history and calculate the signal correlation * for every tail position in the play_hist. Save the result in temporary * array since we may bail out early if the conversation state is not good * to detect echo. */ /* * First phase: do full calculation for the first position */ if (ec->sum_play_level0 == 0) { /* Buffer has just been filled up, do full calculation */ sum_play_level = 0; play_corr = 0; for (j=0; jtempl_cnt-1; ++j) { float corr; corr = (float)ec->play_hist[j+1] / ec->play_hist[j]; play_corr += corr; sum_play_level += ec->play_hist[j]; } sum_play_level += ec->play_hist[j]; ec->sum_play_level0 = sum_play_level; ec->play_corr0 = play_corr; } else { /* Update from previous calculation */ ec->sum_play_level0 = ec->sum_play_level0 - old_play_frm_level + ec->play_hist[ec->templ_cnt-1]; ec->play_corr0 = ec->play_corr0 - ((float)ec->play_hist[0] / old_play_frm_level) + ((float)ec->play_hist[ec->templ_cnt-1] / ec->play_hist[ec->templ_cnt-2]); sum_play_level = ec->sum_play_level0; play_corr = ec->play_corr0; } ec->tmp_corr[0] = FABS(play_corr - ec->rec_corr); ec->tmp_factor[0] = (float)ec->sum_rec_level / sum_play_level; /* Bail out if remote isn't talking */ ulaw = pjmedia_linear2ulaw(sum_play_level/ec->templ_cnt) ^ 0xFF; if (ulaw < MIN_SIGNAL_ULAW) { echo_supp_set_state(ec, ST_REM_SILENT, ulaw); return; } /* Bail out if local user is talking */ if (ec->sum_rec_level >= sum_play_level) { echo_supp_set_state(ec, ST_LOCAL_TALK, ulaw); return; } /* * Second phase: do incremental calculation for the rest of positions */ for (i=1; i < ec->tail_cnt; ++i) { unsigned end; end = i + ec->templ_cnt; sum_play_level = sum_play_level - ec->play_hist[i-1] + ec->play_hist[end-1]; play_corr = play_corr - ((float)ec->play_hist[i]/ec->play_hist[i-1]) + ((float)ec->play_hist[end-1]/ec->play_hist[end-2]); /* Bail out if remote isn't talking */ ulaw = pjmedia_linear2ulaw(sum_play_level/ec->templ_cnt) ^ 0xFF; if (ulaw < MIN_SIGNAL_ULAW) { echo_supp_set_state(ec, ST_REM_SILENT, ulaw); return; } /* Bail out if local user is talking */ if (ec->sum_rec_level >= sum_play_level) { echo_supp_set_state(ec, ST_LOCAL_TALK, ulaw); return; } #if 0 // disabled: not a good idea if mic throws out loud echo /* Also bail out if we suspect there's a doubletalk */ ulaw = pjmedia_linear2ulaw(ec->sum_rec_level/ec->templ_cnt) ^ 0xFF; if (ulaw > MIN_SIGNAL_ULAW) { echo_supp_set_state(ec, ST_DOUBLETALK, ulaw); return; } #endif /* Calculate correlation and save to temporary array */ ec->tmp_corr[i] = FABS(play_corr - ec->rec_corr); /* Also calculate the gain factor between mic and speaker level */ ec->tmp_factor[i] = (float)ec->sum_rec_level / sum_play_level; pj_assert(ec->tmp_factor[i] < 1); } /* We seem to have good signal, we can update the EC state */ echo_supp_set_state(ec, ST_REM_TALK, MIN_SIGNAL_ULAW); /* Accummulate the correlation value to the history and at the same * time find the tail index of the best correlation. */ prev_index = ec->tail_index; for (i=1; itail_cnt-1; ++i) { float *p = &ec->corr_sum[i], sum; /* Accummulate correlation value for this tail position */ ec->corr_sum[i] += ec->tmp_corr[i]; /* Update the min and avg gain factor for this tail position */ if (ec->tmp_factor[i] < ec->min_factor[i]) ec->min_factor[i] = ec->tmp_factor[i]; ec->avg_factor[i] = ((ec->avg_factor[i] * ec->tail_cnt) + ec->tmp_factor[i]) / (ec->tail_cnt + 1); /* To get the best correlation, also include the correlation * value of the neighbouring tail locations. */ sum = *(p-1) + (*p)*2 + *(p+1); //sum = *p; /* See if we have better correlation value */ if (sum < ec->best_corr) { ec->tail_index = i; ec->best_corr = sum; } } if (ec->tail_index != prev_index) { unsigned duration; int imin, iavg; duration = ec->update_cnt * SEGMENT_PTIME; imin = (int)(ec->min_factor[ec->tail_index] * 1000); iavg = (int)(ec->avg_factor[ec->tail_index] * 1000); PJ_LOG(4,(THIS_FILE, "Echo suppressor updated at t=%03d.%03ds, echo tail=%d msec" ", factor min/avg=%d.%03d/%d.%03d", (duration/1000), (duration%1000), (ec->tail_cnt-ec->tail_index) * SEGMENT_PTIME, imin/1000, imin%1000, iavg/1000, iavg%1000)); } ++ec->calc_cnt; if (ec->calc_cnt > ec->max_calc) { unsigned duration; int imin, iavg; ec->learning = PJ_FALSE; ec->running_cnt = 0; duration = ec->update_cnt * SEGMENT_PTIME; imin = (int)(ec->min_factor[ec->tail_index] * 1000); iavg = (int)(ec->avg_factor[ec->tail_index] * 1000); PJ_LOG(4,(THIS_FILE, "Echo suppressor learning done at t=%03d.%03ds, tail=%d ms" ", factor min/avg=%d.%03d/%d.%03d", (duration/1000), (duration%1000), (ec->tail_cnt-ec->tail_index) * SEGMENT_PTIME, imin/1000, imin%1000, iavg/1000, iavg%1000)); } } /* Amplify frame */ static void amplify_frame(pj_int16_t *frm, unsigned length, pj_ufloat_t factor) { unsigned i; for (i=0; isamples_per_frame / ec->samples_per_segment; pj_assert(N>0); for (i=0; isamples_per_segment; echo_supp_update(ec, rec_frm+pos, play_frm+pos); } if (ec->tail_index < 0) { /* Not ready */ } else { unsigned lookup_cnt, rec_level=0, play_level=0; unsigned tail_cnt; float factor; /* How many previous segments to lookup */ lookup_cnt = SIGNAL_LOOKUP_MSEC / SEGMENT_PTIME; if (lookup_cnt > ec->templ_cnt) lookup_cnt = ec->templ_cnt; /* Lookup in recording history to get maximum mic level, to see * if local user is currently talking */ for (i=ec->templ_cnt - lookup_cnt; i < ec->templ_cnt; ++i) { if (ec->rec_hist[i] > rec_level) rec_level = ec->rec_hist[i]; } rec_level = pjmedia_linear2ulaw(rec_level) ^ 0xFF; /* Calculate the detected tail length, in # of segments */ tail_cnt = (ec->tail_cnt - ec->tail_index); /* Lookup in playback history to get max speaker level, to see * if remote user is currently talking */ for (i=ec->play_hist_cnt -lookup_cnt -tail_cnt; iplay_hist_cnt-tail_cnt; ++i) { if (ec->play_hist[i] > play_level) play_level = ec->play_hist[i]; } play_level = pjmedia_linear2ulaw(play_level) ^ 0xFF; if (rec_level >= MIN_SIGNAL_ULAW) { if (play_level < MIN_SIGNAL_ULAW) { /* Mic is talking, speaker is idle. Let mic signal pass as is. */ factor = 1.0; echo_supp_set_state(ec, ST_LOCAL_TALK, rec_level); } else if (rec_level > play_level) { /* Seems that both are talking. Scale the mic signal * down a little bit to reduce echo, while allowing both * parties to talk at the same time. */ factor = (float)(ec->avg_factor[ec->tail_index] * 2); echo_supp_set_state(ec, ST_DOUBLETALK, rec_level); } else { /* Speaker is active, but we've picked up large signal in * the microphone. Assume that this is an echo, so bring * the level down to minimum too. */ factor = ec->min_factor[ec->tail_index] / 2; echo_supp_set_state(ec, ST_REM_TALK, play_level); } } else { if (play_level < MIN_SIGNAL_ULAW) { /* Both mic and speaker seems to be idle. Also scale the * mic signal down with average factor to reduce low power * echo. */ factor = ec->avg_factor[ec->tail_index] * 3 / 2; echo_supp_set_state(ec, ST_REM_SILENT, rec_level); } else { /* Mic is idle, but there's something playing in speaker. * Scale the mic down to minimum */ factor = ec->min_factor[ec->tail_index] / 2; echo_supp_set_state(ec, ST_REM_TALK, play_level); } } /* Smoothen the transition */ if (factor >= ec->last_factor) factor = (factor + ec->last_factor) / 2; else factor = (factor + ec->last_factor*19) / 20; /* Amplify frame */ amplify_frame(rec_frm, ec->samples_per_frame, pj_ufloat_from_float(factor)); ec->last_factor = factor; if (ec->talk_state == ST_REM_TALK) { unsigned level, recalc_cnt; /* Get the adjusted frame signal level */ level = pjmedia_calc_avg_signal(rec_frm, ec->samples_per_frame); level = pjmedia_linear2ulaw(level) ^ 0xFF; /* Accumulate average echo residue to see the ES effectiveness */ ec->residue = ((ec->residue * ec->running_cnt) + level) / (ec->running_cnt + 1); ++ec->running_cnt; /* Check if we need to re-learn */ recalc_cnt = CHECK_PERIOD * ec->clock_rate / ec->samples_per_frame; if (ec->running_cnt > recalc_cnt) { int iresidue; iresidue = (int)(ec->residue*1000); PJ_LOG(5,(THIS_FILE, "Echo suppressor residue = %d.%03d", iresidue/1000, iresidue%1000)); if (ec->residue > MAX_RESIDUE && !ec->learning) { echo_supp_soft_reset(ec); ec->residue = 0; } else { ec->running_cnt = 0; ec->residue = 0; } } } } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/echo_webrtc_aec.c ================================================ /** * Copyright (C) 2011-2013 AG Projects * Copyright (C) 2010 Regis Montoya (aka r3gis - www.r3gis.fr) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #if defined(PJMEDIA_HAS_WEBRTC_AEC) && PJMEDIA_HAS_WEBRTC_AEC != 0 /* 0: conservative, 1: moderate, 2: aggresive */ #ifndef PJMEDIA_WEBRTC_AEC_AGGRESSIVENESS #define PJMEDIA_WEBRTC_AEC_AGGRESSIVENESS 2 #endif /* 0: mild, 1: mediumn, 2: aggressive */ #ifndef PJMEDIA_WEBRTC_NS_POLICY #define PJMEDIA_WEBRTC_NS_POLICY 0 #endif #define THIS_FILE "echo_webrtc_aec.c" #include #include #include #include #include "echo_internal.h" /* * This file contains the implementation of an echo canceller and noise suppressor for PJSIP which uses components * from the WebRTC project. Things to take into account: * * - The WebRTC engine works with 10ms frames, while in PJSIP we use 20ms frames mostly, all data fed to WebRTC elements needs * to be chunked in 10ms chunks. * - When a 32kHz sampling rate is used, the WebRTC engine needs frames to be passed split into low and high frequencies. PJSIP * will give us a frame with all frequencies, so the signal processing library in WebRTC must be used to split frames into low * and high frequencies, and combine them later. */ typedef struct AudioBuffer { int samples_per_channel; pj_bool_t is_split; WebRtc_Word16* data; WebRtc_Word16 low_pass_data[160]; WebRtc_Word16 high_pass_data[160]; WebRtc_Word32 analysis_filter_state1[6]; WebRtc_Word32 analysis_filter_state2[6]; WebRtc_Word32 synthesis_filter_state1[6]; WebRtc_Word32 synthesis_filter_state2[6]; } AudioBuffer; static WebRtc_Word16* AudioBuffer_GetData(AudioBuffer *ab); static WebRtc_Word16* AudioBuffer_GetLowPassData(AudioBuffer *ab); static WebRtc_Word16* AudioBuffer_GetHighPassData(AudioBuffer *ab); static void AudioBuffer_SetData(AudioBuffer *ab, WebRtc_Word16 *data); static void AudioBuffer_Initialize(AudioBuffer *ab, int sample_rate); static int AudioBuffer_SamplesPerChannel(AudioBuffer *ab); static WebRtc_Word16* AudioBuffer_GetData(AudioBuffer *ab) { pj_assert(ab->data); if (ab->is_split) { WebRtcSpl_SynthesisQMF(ab->low_pass_data, ab->high_pass_data, ab->data, ab->synthesis_filter_state1, ab->synthesis_filter_state2); } return ab->data; } static WebRtc_Word16* AudioBuffer_GetLowPassData(AudioBuffer *ab) { if (!ab->is_split) { return ab->data; } else { return ab->low_pass_data; } } static WebRtc_Word16* AudioBuffer_GetHighPassData(AudioBuffer *ab) { if (!ab->is_split) { return ab->data; } else { return ab->high_pass_data; } } static void AudioBuffer_Initialize(AudioBuffer *ab, int sample_rate) { pj_bzero(ab, sizeof(AudioBuffer)); if (sample_rate == 32000) { ab->is_split = PJ_TRUE; ab->samples_per_channel = 160; } else { ab->is_split = PJ_FALSE; ab->samples_per_channel = sample_rate / 100; } } static void AudioBuffer_SetData(AudioBuffer *ab, WebRtc_Word16 *data) { ab->data = data; if (ab->is_split) { /* split data into low and high bands */ WebRtcSpl_AnalysisQMF(ab->data, /* input data */ ab->low_pass_data, /* pointer to low pass data storage*/ ab->high_pass_data, /* pointer to high pass data storage*/ ab->analysis_filter_state1, ab->analysis_filter_state2); } } static int AudioBuffer_SamplesPerChannel(AudioBuffer *ab) { return ab->samples_per_channel; } const WebRtc_Word16 kFilterCoefficients8kHz[5] = {3798, -7596, 3798, 7807, -3733}; const WebRtc_Word16 kFilterCoefficients[5] = {4012, -8024, 4012, 8002, -3913}; typedef struct { WebRtc_Word16 y[4]; WebRtc_Word16 x[2]; const WebRtc_Word16* ba; } HighPassFilterState; static int HighPassFilter_Initialize(HighPassFilterState* hpf, int sample_rate) { assert(hpf != NULL); if (sample_rate == 8000) { hpf->ba = kFilterCoefficients8kHz; } else { hpf->ba = kFilterCoefficients; } WebRtcSpl_MemSetW16(hpf->x, 0, 2); WebRtcSpl_MemSetW16(hpf->y, 0, 4); return 0; } static int HighPassFilter_Process(HighPassFilterState* hpf, WebRtc_Word16* data, int length) { assert(hpf != NULL); int i; WebRtc_Word32 tmp_int32 = 0; WebRtc_Word16* y = hpf->y; WebRtc_Word16* x = hpf->x; const WebRtc_Word16* ba = hpf->ba; for (i = 0; i < length; i++) { // y[i] = b[0] * x[i] + b[1] * x[i-1] + b[2] * x[i-2] // + -a[1] * y[i-1] + -a[2] * y[i-2]; tmp_int32 = WEBRTC_SPL_MUL_16_16(y[1], ba[3]); // -a[1] * y[i-1] (low part) tmp_int32 += WEBRTC_SPL_MUL_16_16(y[3], ba[4]); // -a[2] * y[i-2] (low part) tmp_int32 = (tmp_int32 >> 15); tmp_int32 += WEBRTC_SPL_MUL_16_16(y[0], ba[3]); // -a[1] * y[i-1] (high part) tmp_int32 += WEBRTC_SPL_MUL_16_16(y[2], ba[4]); // -a[2] * y[i-2] (high part) tmp_int32 = (tmp_int32 << 1); tmp_int32 += WEBRTC_SPL_MUL_16_16(data[i], ba[0]); // b[0]*x[0] tmp_int32 += WEBRTC_SPL_MUL_16_16(x[0], ba[1]); // b[1]*x[i-1] tmp_int32 += WEBRTC_SPL_MUL_16_16(x[1], ba[2]); // b[2]*x[i-2] // Update state (input part) x[1] = x[0]; x[0] = data[i]; // Update state (filtered part) y[2] = y[0]; y[3] = y[1]; y[0] = (WebRtc_Word16)(tmp_int32 >> 13); y[1] = (WebRtc_Word16)((tmp_int32 - WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)(y[0]), 13)) << 2); // Rounding in Q12, i.e. add 2^11 tmp_int32 += 2048; // Saturate (to 2^27) so that the HP filtered signal does not overflow tmp_int32 = WEBRTC_SPL_SAT((WebRtc_Word32)(134217727), tmp_int32, (WebRtc_Word32)(-134217728)); // Convert back to Q0 and use rounding data[i] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp_int32, 12); } return 0; } typedef struct webrtc_ec { void *AEC_inst; void *AGC_inst; NsHandle *NS_inst; pj_bool_t needs_reset; unsigned skip_frames; unsigned silence_frames; unsigned clock_rate; unsigned echo_tail; unsigned samples_per_frame; unsigned samples_per_10ms_frame; WebRtc_Word32 mic_capture_level; WebRtc_Word16 has_echo; WebRtc_UWord8 is_saturated; HighPassFilterState hpf; AudioBuffer capture_audio_buffer; AudioBuffer playback_audio_buffer; pj_int16_t *tmp_frame; pj_int16_t *empty_frame; } webrtc_ec; #define WEBRTC_AEC_ERROR(aec_inst, tag) \ do { \ unsigned status = WebRtcAec_get_error_code(aec_inst); \ PJ_LOG(4, (THIS_FILE, "WebRTC AEC ERROR (%s) %d", tag, status)); \ } while (0) \ #define WEBRTC_AGC_ERROR(ns_inst, text) \ do { \ PJ_LOG(4, (THIS_FILE, "WebRTC AGC ERROR (%s)", text)); \ } while (0) \ #define WEBRTC_NS_ERROR(ns_inst, text) \ do { \ PJ_LOG(4, (THIS_FILE, "WebRTC NS ERROR (%s)", text)); \ } while (0) \ PJ_DEF(pj_status_t) webrtc_aec_create(pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned tail_ms, unsigned options, void **p_echo ) { webrtc_ec *echo; int status; *p_echo = NULL; if (clock_rate != 16000 && clock_rate != 32000) { PJ_LOG(4, (THIS_FILE, "Unsupported sample rate: %d", clock_rate)); return PJ_EINVAL; } echo = PJ_POOL_ZALLOC_T(pool, webrtc_ec); PJ_ASSERT_RETURN(echo != NULL, PJ_ENOMEM); status = WebRtcAec_Create(&echo->AEC_inst); if(status != 0) { PJ_LOG(4, (THIS_FILE, "Couldn't allocate memory for WebRTC AEC")); goto error; } status = WebRtcAec_Init(echo->AEC_inst, clock_rate, clock_rate); if(status != 0) { WEBRTC_AEC_ERROR(echo->AEC_inst, "initialization"); goto error; } AecConfig aec_config; aec_config.nlpMode = PJMEDIA_WEBRTC_AEC_AGGRESSIVENESS; aec_config.skewMode = kAecFalse; aec_config.metricsMode = kAecFalse; status = WebRtcAec_set_config(echo->AEC_inst, aec_config); if(status != 0) { WEBRTC_AEC_ERROR(echo->AEC_inst, "config initialization"); goto error; } status = WebRtcAgc_Create(&echo->AGC_inst); if(status != 0) { PJ_LOG(4, (THIS_FILE, "Couldn't allocate memory for WebRTC AGC")); goto error; } status = WebRtcAgc_Init(echo->AGC_inst, 0, 255, kAgcModeAdaptiveAnalog, clock_rate); if(status != 0) { WEBRTC_AGC_ERROR(echo->AGC_inst, "initialization"); goto error; } WebRtcAgc_config_t agc_config; agc_config.targetLevelDbfs = 7; agc_config.compressionGaindB = 0; agc_config.limiterEnable = kAgcFalse; status = WebRtcAgc_set_config(echo->AGC_inst, agc_config); if(status != 0) { WEBRTC_AGC_ERROR(echo->AGC_inst, "config initialization"); goto error; } status = WebRtcNs_Create(&echo->NS_inst); if(status != 0) { PJ_LOG(4, (THIS_FILE, "Couldn't allocate memory for WebRTC NS")); goto error; } status = WebRtcNs_Init(echo->NS_inst, clock_rate); if(status != 0) { WEBRTC_NS_ERROR(echo->NS_inst, "initialization"); goto error; } status = WebRtcNs_set_policy(echo->NS_inst, PJMEDIA_WEBRTC_NS_POLICY); if (status != 0) { WEBRTC_NS_ERROR(echo->NS_inst, "failed to set policy"); } echo->clock_rate = clock_rate; echo->samples_per_frame = samples_per_frame; echo->samples_per_10ms_frame = clock_rate / 100; /* the WebRTC engine works with 10ms frames */ echo->echo_tail = tail_ms; echo->needs_reset = PJ_TRUE; echo->skip_frames = 0; echo->silence_frames = 0; echo->mic_capture_level = 255; /* initial mic capture level, maximum */ /* Allocate temporary frames for echo cancellation */ echo->tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, sizeof(pj_int16_t)*samples_per_frame); PJ_ASSERT_RETURN(echo->tmp_frame, PJ_ENOMEM); echo->empty_frame = (pj_int16_t*) pj_pool_zalloc(pool, sizeof(pj_int16_t)*samples_per_frame); PJ_ASSERT_RETURN(echo->empty_frame, PJ_ENOMEM); /* Initialize audio buffers */ AudioBuffer_Initialize(&echo->capture_audio_buffer, clock_rate); AudioBuffer_Initialize(&echo->playback_audio_buffer, clock_rate); /* Initialize high pass filter */ HighPassFilter_Initialize(&echo->hpf, clock_rate); PJ_LOG(4, (THIS_FILE, "WebRTC AEC and NS initialized")); *p_echo = echo; return PJ_SUCCESS; error: if (echo->AEC_inst) WebRtcAec_Free(echo->AEC_inst); if (echo->AGC_inst) WebRtcAgc_Free(echo->AGC_inst); if (echo->NS_inst) WebRtcNs_Free(echo->NS_inst); return PJ_EBUG; } PJ_DEF(pj_status_t) webrtc_aec_destroy(void *state ) { webrtc_ec *echo = (webrtc_ec*) state; PJ_ASSERT_RETURN(echo, PJ_EINVAL); if (echo->AEC_inst) { WebRtcAec_Free(echo->AEC_inst); echo->AEC_inst = NULL; } if (echo->AGC_inst) { WebRtcAgc_Free(echo->AGC_inst); echo->AGC_inst = NULL; } if (echo->NS_inst) { WebRtcNs_Free(echo->NS_inst); echo->NS_inst = NULL; } return PJ_SUCCESS; } PJ_DEF(void) webrtc_aec_reset(void *state) { /* Synchronously reset later, before processing the next frame, to avoid race conditions */ ((webrtc_ec*)state)->needs_reset = PJ_TRUE; } static void aec_reset(webrtc_ec *echo) { PJ_ASSERT_ON_FAIL(echo && echo->AEC_inst && echo->AGC_inst && echo->NS_inst, {return;}); int status = 0; /* re-initialize the AEC */ status = WebRtcAec_Init(echo->AEC_inst, echo->clock_rate, echo->clock_rate); if(status != 0) { WEBRTC_AEC_ERROR(echo->AEC_inst, "re-initialization"); return; } AecConfig aec_config; aec_config.nlpMode = PJMEDIA_WEBRTC_AEC_AGGRESSIVENESS; aec_config.skewMode = kAecFalse; aec_config.metricsMode = kAecFalse; status = WebRtcAec_set_config(echo->AEC_inst, aec_config); if(status != 0) { WEBRTC_AEC_ERROR(echo->AEC_inst, "configuration re-initialization"); return; } /* re-initialize the AGC */ status = WebRtcAgc_Init(echo->AGC_inst, 0, 255, kAgcModeAdaptiveAnalog, echo->clock_rate); if(status != 0) { WEBRTC_AGC_ERROR(echo->AGC_inst, "initialization"); return; } WebRtcAgc_config_t agc_config; agc_config.targetLevelDbfs = 7; agc_config.compressionGaindB = 0; agc_config.limiterEnable = kAgcFalse; status = WebRtcAgc_set_config(echo->AGC_inst, agc_config); if(status != 0) { WEBRTC_AGC_ERROR(echo->AGC_inst, "config initialization"); return; } /* re-initialize the NS */ status = WebRtcNs_Init(echo->NS_inst, echo->clock_rate); if(status != 0) { WEBRTC_NS_ERROR(echo->NS_inst, "re-initialization"); return; } status = WebRtcNs_set_policy(echo->NS_inst, PJMEDIA_WEBRTC_NS_POLICY); if (status != 0) { WEBRTC_NS_ERROR(echo->NS_inst, "configuration re-initialization"); return; } /* re-initialize audio buffers */ AudioBuffer_Initialize(&echo->capture_audio_buffer, echo->clock_rate); AudioBuffer_Initialize(&echo->playback_audio_buffer, echo->clock_rate); /* re-initialize high pass filter state */ HighPassFilter_Initialize(&echo->hpf, echo->clock_rate); /* re-initialize mic level */ echo->mic_capture_level = 255; PJ_LOG(4, (THIS_FILE, "WebRTC AEC reset succeeded")); } /* * Perform echo cancellation. */ PJ_DEF(pj_status_t) webrtc_aec_cancel_echo(void *state, pj_int16_t *rec_frm, const pj_int16_t *play_frm, unsigned options, void *reserved) { webrtc_ec *echo = (webrtc_ec*) state; pj_int16_t *capture_frame, *result_frame; int i, status; /* Sanity checks */ PJ_ASSERT_RETURN(echo && echo->AEC_inst && echo->AGC_inst && echo->NS_inst, PJ_EINVAL); PJ_ASSERT_RETURN(rec_frm && play_frm && options==0 && reserved==NULL, PJ_EINVAL); /* Check if a reset is needed */ if (echo->needs_reset) { aec_reset(echo); echo->needs_reset = PJ_FALSE; echo->skip_frames = 15; echo->silence_frames = 10; } if (echo->skip_frames) { echo->skip_frames--; capture_frame = echo->empty_frame; result_frame = echo->empty_frame; } else if (echo->silence_frames) { echo->silence_frames--; capture_frame = rec_frm; result_frame = echo->empty_frame; } else { capture_frame = rec_frm; result_frame = echo->tmp_frame; } /* Copy record frame to a temporary buffer, in case things go wrong audio will be returned unchanged */ pjmedia_copy_samples(echo->tmp_frame, capture_frame, echo->samples_per_frame); for(i=0; i < echo->samples_per_frame; i+= echo->samples_per_10ms_frame) { /* feed a 10ms frame into the audio buffers */ AudioBuffer_SetData(&echo->capture_audio_buffer, (WebRtc_Word16 *) (&echo->tmp_frame[i])); AudioBuffer_SetData(&echo->playback_audio_buffer, (WebRtc_Word16 *) (&play_frm[i])); /* Apply high pass filer */ HighPassFilter_Process(&echo->hpf, AudioBuffer_GetLowPassData(&echo->capture_audio_buffer), AudioBuffer_SamplesPerChannel(&echo->capture_audio_buffer)); /* Analyze capture data gain * NOTE: if we used kAgcModeAdaptiveDigital we'd use WebRtcAgc_VirtualMic instead */ status = WebRtcAgc_AddMic(echo->AGC_inst, AudioBuffer_GetLowPassData(&echo->capture_audio_buffer), AudioBuffer_GetHighPassData(&echo->capture_audio_buffer), AudioBuffer_SamplesPerChannel(&echo->capture_audio_buffer)); if(status != 0) { WEBRTC_AGC_ERROR(echo->AGC_inst, "gain analysis"); return PJ_EBUG; } /* Feed farend buffer to AGC */ status = WebRtcAgc_AddFarend(echo->AGC_inst, AudioBuffer_GetLowPassData(&echo->playback_audio_buffer), AudioBuffer_SamplesPerChannel(&echo->playback_audio_buffer)); if(status != 0) { WEBRTC_AGC_ERROR(echo->AGC_inst, "farend buffering"); return PJ_EBUG; } /* Feed farend buffer to AEC */ status = WebRtcAec_BufferFarend(echo->AEC_inst, AudioBuffer_GetLowPassData(&echo->playback_audio_buffer), AudioBuffer_SamplesPerChannel(&echo->playback_audio_buffer)); if(status != 0) { WEBRTC_AEC_ERROR(echo->AEC_inst, "farend buffering"); return PJ_EBUG; } /* Noise suppression */ status = WebRtcNs_Process(echo->NS_inst, AudioBuffer_GetLowPassData(&echo->capture_audio_buffer), AudioBuffer_GetHighPassData(&echo->capture_audio_buffer), AudioBuffer_GetLowPassData(&echo->capture_audio_buffer), AudioBuffer_GetHighPassData(&echo->capture_audio_buffer)); if (status != 0) { WEBRTC_NS_ERROR(echo->NS_inst, "ns processing"); return PJ_EBUG; } /* Process echo cancellation */ status = WebRtcAec_Process(echo->AEC_inst, AudioBuffer_GetLowPassData(&echo->capture_audio_buffer), AudioBuffer_GetHighPassData(&echo->capture_audio_buffer), AudioBuffer_GetLowPassData(&echo->capture_audio_buffer), AudioBuffer_GetHighPassData(&echo->capture_audio_buffer), AudioBuffer_SamplesPerChannel(&echo->capture_audio_buffer), echo->echo_tail, 0); if(status != 0) { WEBRTC_AEC_ERROR(echo->AEC_inst, "echo processing"); return PJ_EBUG; } WebRtcAec_get_echo_status(echo->AEC_inst, &echo->has_echo); #if 0 if (echo->has_echo) { PJ_LOG(4, (THIS_FILE, "Sound might have echo")); } #endif /* Process gain control */ status = WebRtcAgc_Process(echo->AGC_inst, AudioBuffer_GetLowPassData(&echo->capture_audio_buffer), AudioBuffer_GetHighPassData(&echo->capture_audio_buffer), AudioBuffer_SamplesPerChannel(&echo->capture_audio_buffer), AudioBuffer_GetLowPassData(&echo->capture_audio_buffer), AudioBuffer_GetHighPassData(&echo->capture_audio_buffer), echo->mic_capture_level, &echo->mic_capture_level, echo->has_echo, &echo->is_saturated); if (status != 0) { WEBRTC_AGC_ERROR(echo->AGC_inst, "agc processing"); return PJ_EBUG; } #if 0 if (echo->is_saturated) { PJ_LOG(4, (THIS_FILE, "Sound might be saturated")); } #endif /* finish frame processing, in case we are working at 32kHz low and high bands will be combined */ AudioBuffer_GetData(&echo->capture_audio_buffer); } /* Copy temporary buffer back to original rec_frm */ pjmedia_copy_samples(rec_frm, result_frame, echo->samples_per_frame); return PJ_SUCCESS; } #endif ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/endpoint.c ================================================ /* $Id: endpoint.c 4474 2013-04-16 09:12:59Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "endpoint.c" static const pj_str_t STR_IN = { "IN", 2 }; static const pj_str_t STR_IP4 = { "IP4", 3}; static const pj_str_t STR_IP6 = { "IP6", 3}; static const pj_str_t STR_RTP_AVP = { "RTP/AVP", 7 }; static const pj_str_t STR_SDP_NAME = { "pjmedia", 7 }; static const pj_str_t STR_SENDRECV = { "sendrecv", 8 }; /* Config to control rtpmap inclusion for static payload types */ pj_bool_t pjmedia_add_rtpmap_for_static_pt = PJMEDIA_ADD_RTPMAP_FOR_STATIC_PT; /* Config to control use of RFC3890 TIAS */ pj_bool_t pjmedia_add_bandwidth_tias_in_sdp = PJMEDIA_ADD_BANDWIDTH_TIAS_IN_SDP; /* Worker thread proc. */ static int PJ_THREAD_FUNC worker_proc(void*); #define MAX_THREADS 16 /* List of media endpoint exit callback. */ typedef struct exit_cb { PJ_DECL_LIST_MEMBER (struct exit_cb); pjmedia_endpt_exit_callback func; } exit_cb; /** Concrete declaration of media endpoint. */ struct pjmedia_endpt { /** Pool. */ pj_pool_t *pool; /** Pool factory. */ pj_pool_factory *pf; /** Codec manager. */ pjmedia_codec_mgr codec_mgr; /** IOqueue instance. */ pj_ioqueue_t *ioqueue; /** Do we own the ioqueue? */ pj_bool_t own_ioqueue; /** Number of threads. */ unsigned thread_cnt; /** IOqueue polling thread, if any. */ pj_thread_t *thread[MAX_THREADS]; /** To signal polling thread to quit. */ pj_bool_t quit_flag; /** Is telephone-event enable */ pj_bool_t has_telephone_event; /** List of exit callback. */ exit_cb exit_cb_list; }; /** * Initialize and get the instance of media endpoint. */ PJ_DEF(pj_status_t) pjmedia_endpt_create(pj_pool_factory *pf, pj_ioqueue_t *ioqueue, unsigned worker_cnt, pjmedia_endpt **p_endpt) { pj_pool_t *pool; pjmedia_endpt *endpt; unsigned i; pj_status_t status; status = pj_register_strerror(PJMEDIA_ERRNO_START, PJ_ERRNO_SPACE_SIZE, &pjmedia_strerror); pj_assert(status == PJ_SUCCESS); PJ_ASSERT_RETURN(pf && p_endpt, PJ_EINVAL); PJ_ASSERT_RETURN(worker_cnt <= MAX_THREADS, PJ_EINVAL); pool = pj_pool_create(pf, "med-ept", 512, 512, NULL); if (!pool) return PJ_ENOMEM; endpt = PJ_POOL_ZALLOC_T(pool, struct pjmedia_endpt); endpt->pool = pool; endpt->pf = pf; endpt->ioqueue = ioqueue; endpt->thread_cnt = worker_cnt; endpt->has_telephone_event = PJ_TRUE; /* Sound */ status = pjmedia_aud_subsys_init(pf); if (status != PJ_SUCCESS) goto on_error; /* Init codec manager. */ status = pjmedia_codec_mgr_init(&endpt->codec_mgr, endpt->pf); if (status != PJ_SUCCESS) goto on_error; /* Initialize exit callback list. */ pj_list_init(&endpt->exit_cb_list); /* Create ioqueue if none is specified. */ if (endpt->ioqueue == NULL) { endpt->own_ioqueue = PJ_TRUE; status = pj_ioqueue_create( endpt->pool, PJ_IOQUEUE_MAX_HANDLES, &endpt->ioqueue); if (status != PJ_SUCCESS) goto on_error; if (worker_cnt == 0) { PJ_LOG(4,(THIS_FILE, "Warning: no worker thread is created in" "media endpoint for internal ioqueue")); } } /* Create worker threads if asked. */ for (i=0; ipool, "media", &worker_proc, endpt, 0, 0, &endpt->thread[i]); if (status != PJ_SUCCESS) goto on_error; } *p_endpt = endpt; return PJ_SUCCESS; on_error: /* Destroy threads */ for (i=0; ithread_cnt; ++i) { if (endpt->thread[i]) { pj_thread_destroy(endpt->thread[i]); } } /* Destroy internal ioqueue */ if (endpt->ioqueue && endpt->own_ioqueue) pj_ioqueue_destroy(endpt->ioqueue); pjmedia_codec_mgr_destroy(&endpt->codec_mgr); pjmedia_aud_subsys_shutdown(); pj_pool_release(pool); return status; } /** * Get the codec manager instance. */ PJ_DEF(pjmedia_codec_mgr*) pjmedia_endpt_get_codec_mgr(pjmedia_endpt *endpt) { return &endpt->codec_mgr; } /** * Deinitialize media endpoint. */ PJ_DEF(pj_status_t) pjmedia_endpt_destroy (pjmedia_endpt *endpt) { exit_cb *ecb; pjmedia_endpt_stop_threads(endpt); /* Destroy internal ioqueue */ if (endpt->ioqueue && endpt->own_ioqueue) { pj_ioqueue_destroy(endpt->ioqueue); endpt->ioqueue = NULL; } endpt->pf = NULL; pjmedia_codec_mgr_destroy(&endpt->codec_mgr); pjmedia_aud_subsys_shutdown(); /* Call all registered exit callbacks */ ecb = endpt->exit_cb_list.next; while (ecb != &endpt->exit_cb_list) { (*ecb->func)(endpt); ecb = ecb->next; } pj_pool_release (endpt->pool); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_endpt_set_flag( pjmedia_endpt *endpt, pjmedia_endpt_flag flag, const void *value) { PJ_ASSERT_RETURN(endpt, PJ_EINVAL); switch (flag) { case PJMEDIA_ENDPT_HAS_TELEPHONE_EVENT_FLAG: endpt->has_telephone_event = *(pj_bool_t*)value; break; default: return PJ_EINVAL; } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_endpt_get_flag( pjmedia_endpt *endpt, pjmedia_endpt_flag flag, void *value) { PJ_ASSERT_RETURN(endpt, PJ_EINVAL); switch (flag) { case PJMEDIA_ENDPT_HAS_TELEPHONE_EVENT_FLAG: *(pj_bool_t*)value = endpt->has_telephone_event; break; default: return PJ_EINVAL; } return PJ_SUCCESS; } /** * Get the ioqueue instance of the media endpoint. */ PJ_DEF(pj_ioqueue_t*) pjmedia_endpt_get_ioqueue(pjmedia_endpt *endpt) { PJ_ASSERT_RETURN(endpt, NULL); return endpt->ioqueue; } /** * Get the number of worker threads in media endpoint. */ PJ_DEF(unsigned) pjmedia_endpt_get_thread_count(pjmedia_endpt *endpt) { PJ_ASSERT_RETURN(endpt, 0); return endpt->thread_cnt; } /** * Get a reference to one of the worker threads of the media endpoint */ PJ_DEF(pj_thread_t*) pjmedia_endpt_get_thread(pjmedia_endpt *endpt, unsigned index) { PJ_ASSERT_RETURN(endpt, NULL); PJ_ASSERT_RETURN(index < endpt->thread_cnt, NULL); /* here should be an assert on index >= 0 < endpt->thread_cnt */ return endpt->thread[index]; } /** * Stop and destroy the worker threads of the media endpoint */ PJ_DEF(pj_status_t) pjmedia_endpt_stop_threads(pjmedia_endpt *endpt) { unsigned i; PJ_ASSERT_RETURN(endpt, PJ_EINVAL); endpt->quit_flag = 1; /* Destroy threads */ for (i=0; ithread_cnt; ++i) { if (endpt->thread[i]) { pj_thread_join(endpt->thread[i]); pj_thread_destroy(endpt->thread[i]); endpt->thread[i] = NULL; } } return PJ_SUCCESS; } /** * Worker thread proc. */ static int PJ_THREAD_FUNC worker_proc(void *arg) { pjmedia_endpt *endpt = (pjmedia_endpt*) arg; while (!endpt->quit_flag) { pj_time_val timeout = { 0, 500 }; pj_ioqueue_poll(endpt->ioqueue, &timeout); } return 0; } /** * Create pool. */ PJ_DEF(pj_pool_t*) pjmedia_endpt_create_pool( pjmedia_endpt *endpt, const char *name, pj_size_t initial, pj_size_t increment) { pj_assert(endpt != NULL); return pj_pool_create(endpt->pf, name, initial, increment, NULL); } /* Common initialization for both audio and video SDP media line */ static pj_status_t init_sdp_media(pjmedia_sdp_media *m, pj_pool_t *pool, const pj_str_t *media_type, const pjmedia_sock_info *sock_info) { char tmp_addr[PJ_INET6_ADDRSTRLEN]; pjmedia_sdp_attr *attr; const pj_sockaddr *addr; pj_strdup(pool, &m->desc.media, media_type); addr = &sock_info->rtp_addr_name; /* Validate address family */ PJ_ASSERT_RETURN(addr->addr.sa_family == pj_AF_INET() || addr->addr.sa_family == pj_AF_INET6(), PJ_EAFNOTSUP); /* SDP connection line */ m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn); m->conn->net_type = STR_IN; m->conn->addr_type = (addr->addr.sa_family==pj_AF_INET())? STR_IP4:STR_IP6; pj_sockaddr_print(addr, tmp_addr, sizeof(tmp_addr), 0); pj_strdup2(pool, &m->conn->addr, tmp_addr); /* Port and transport in media description */ m->desc.port = pj_sockaddr_get_port(addr); m->desc.port_count = 1; pj_strdup (pool, &m->desc.transport, &STR_RTP_AVP); /* Add "rtcp" attribute */ #if defined(PJMEDIA_HAS_RTCP_IN_SDP) && PJMEDIA_HAS_RTCP_IN_SDP!=0 if (sock_info->rtcp_addr_name.addr.sa_family != 0) { attr = pjmedia_sdp_attr_create_rtcp(pool, &sock_info->rtcp_addr_name); if (attr) pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); } #endif /* Add sendrecv attribute. */ attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr); attr->name = STR_SENDRECV; m->attr[m->attr_count++] = attr; return PJ_SUCCESS; } /* Create m=audio SDP media line */ PJ_DEF(pj_status_t) pjmedia_endpt_create_audio_sdp(pjmedia_endpt *endpt, pj_pool_t *pool, const pjmedia_sock_info *si, unsigned options, pjmedia_sdp_media **p_m) { const pj_str_t STR_AUDIO = { "audio", 5 }; pjmedia_sdp_media *m; pjmedia_sdp_attr *attr; unsigned i; unsigned max_bitrate = 0; pj_status_t status; PJ_UNUSED_ARG(options); /* Check that there are not too many codecs */ PJ_ASSERT_RETURN(endpt->codec_mgr.codec_cnt <= PJMEDIA_MAX_SDP_FMT, PJ_ETOOMANY); /* Create and init basic SDP media */ m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media); status = init_sdp_media(m, pool, &STR_AUDIO, si); if (status != PJ_SUCCESS) return status; /* Add format, rtpmap, and fmtp (when applicable) for each codec */ for (i=0; icodec_mgr.codec_cnt; ++i) { pjmedia_codec_info *codec_info; pjmedia_sdp_rtpmap rtpmap; char tmp_param[3]; pjmedia_codec_param codec_param; pj_str_t *fmt; if (endpt->codec_mgr.codec_desc[i].prio == PJMEDIA_CODEC_PRIO_DISABLED) break; codec_info = &endpt->codec_mgr.codec_desc[i].info; pjmedia_codec_mgr_get_default_param(&endpt->codec_mgr, codec_info, &codec_param); fmt = &m->desc.fmt[m->desc.fmt_count++]; fmt->ptr = (char*) pj_pool_alloc(pool, 8); fmt->slen = pj_utoa(codec_info->pt, fmt->ptr); rtpmap.pt = *fmt; rtpmap.enc_name = codec_info->encoding_name; #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0) if (codec_info->pt == PJMEDIA_RTP_PT_G722) rtpmap.clock_rate = 8000; else rtpmap.clock_rate = codec_info->clock_rate; #else rtpmap.clock_rate = codec_info->clock_rate; #endif /* For audio codecs, rtpmap parameters denotes the number * of channels, which can be omited if the value is 1. */ if (codec_info->type == PJMEDIA_TYPE_AUDIO && codec_info->channel_cnt > 1) { /* Can only support one digit channel count */ pj_assert(codec_info->channel_cnt < 10); tmp_param[0] = (char)('0' + codec_info->channel_cnt); rtpmap.param.ptr = tmp_param; rtpmap.param.slen = 1; } else { rtpmap.param.ptr = ""; rtpmap.param.slen = 0; } if (codec_info->pt >= 96 || pjmedia_add_rtpmap_for_static_pt) { pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr); m->attr[m->attr_count++] = attr; } /* Add fmtp params */ if (codec_param.setting.dec_fmtp.cnt > 0) { enum { MAX_FMTP_STR_LEN = 160 }; char buf[MAX_FMTP_STR_LEN]; unsigned buf_len = 0, ii; pjmedia_codec_fmtp *dec_fmtp = &codec_param.setting.dec_fmtp; /* Print codec PT */ buf_len += pj_ansi_snprintf(buf, MAX_FMTP_STR_LEN - buf_len, "%d", codec_info->pt); for (ii = 0; ii < dec_fmtp->cnt; ++ii) { pj_size_t test_len = 2; /* Check if buf still available */ test_len = dec_fmtp->param[ii].val.slen + dec_fmtp->param[ii].name.slen + 2; if (test_len + buf_len >= MAX_FMTP_STR_LEN) return PJ_ETOOBIG; /* Print delimiter */ buf_len += pj_ansi_snprintf(&buf[buf_len], MAX_FMTP_STR_LEN - buf_len, (ii == 0?" ":";")); /* Print an fmtp param */ if (dec_fmtp->param[ii].name.slen) buf_len += pj_ansi_snprintf( &buf[buf_len], MAX_FMTP_STR_LEN - buf_len, "%.*s=%.*s", (int)dec_fmtp->param[ii].name.slen, dec_fmtp->param[ii].name.ptr, (int)dec_fmtp->param[ii].val.slen, dec_fmtp->param[ii].val.ptr); else buf_len += pj_ansi_snprintf(&buf[buf_len], MAX_FMTP_STR_LEN - buf_len, "%.*s", (int)dec_fmtp->param[ii].val.slen, dec_fmtp->param[ii].val.ptr); } attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr); attr->name = pj_str("fmtp"); attr->value = pj_strdup3(pool, buf); m->attr[m->attr_count++] = attr; } /* Find maximum bitrate in this media */ if (max_bitrate < codec_param.info.max_bps) max_bitrate = codec_param.info.max_bps; } #if defined(PJMEDIA_RTP_PT_TELEPHONE_EVENTS) && \ PJMEDIA_RTP_PT_TELEPHONE_EVENTS != 0 /* * Add support telephony event */ if (endpt->has_telephone_event) { m->desc.fmt[m->desc.fmt_count++] = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR); /* Add rtpmap. */ attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr); attr->name = pj_str("rtpmap"); attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR " telephone-event/8000"); m->attr[m->attr_count++] = attr; /* Add fmtp */ attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr); attr->name = pj_str("fmtp"); #if defined(PJMEDIA_HAS_DTMF_FLASH) && PJMEDIA_HAS_DTMF_FLASH!= 0 attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR " 0-16"); #else attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR " 0-15"); #endif m->attr[m->attr_count++] = attr; } #endif /* Put bandwidth info in media level using bandwidth modifier "TIAS" * (RFC3890). */ #if 0 if (max_bitrate && pjmedia_add_bandwidth_tias_in_sdp) { const pj_str_t STR_BANDW_MODIFIER = { "TIAS", 4 }; pjmedia_sdp_bandw *b; b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw); b->modifier = STR_BANDW_MODIFIER; b->value = max_bitrate; m->bandw[m->bandw_count++] = b; } #endif *p_m = m; return PJ_SUCCESS; } #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) /* Create m=video SDP media line */ PJ_DEF(pj_status_t) pjmedia_endpt_create_video_sdp(pjmedia_endpt *endpt, pj_pool_t *pool, const pjmedia_sock_info *si, unsigned options, pjmedia_sdp_media **p_m) { const pj_str_t STR_VIDEO = { "video", 5 }; pjmedia_sdp_media *m; pjmedia_vid_codec_info codec_info[PJMEDIA_VID_CODEC_MGR_MAX_CODECS]; unsigned codec_prio[PJMEDIA_VID_CODEC_MGR_MAX_CODECS]; pjmedia_sdp_attr *attr; unsigned cnt, i; unsigned max_bitrate = 0; pj_status_t status; PJ_UNUSED_ARG(options); /* Make sure video codec manager is instantiated */ if (!pjmedia_vid_codec_mgr_instance()) pjmedia_vid_codec_mgr_create(endpt->pool, NULL); /* Create and init basic SDP media */ m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media); status = init_sdp_media(m, pool, &STR_VIDEO, si); if (status != PJ_SUCCESS) return status; cnt = PJ_ARRAY_SIZE(codec_info); status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &cnt, codec_info, codec_prio); /* Check that there are not too many codecs */ PJ_ASSERT_RETURN(0 <= PJMEDIA_MAX_SDP_FMT, PJ_ETOOMANY); /* Add format, rtpmap, and fmtp (when applicable) for each codec */ for (i=0; i PJMEDIA_MAX_SDP_FMT) { /* Too many codecs, perhaps it is better to tell application by * returning appropriate status code. */ PJ_PERROR(3,(THIS_FILE, PJ_ETOOMANY, "Skipping some video codecs")); break; } /* Must support RTP packetization and bidirectional */ if ((codec_info[i].packings & PJMEDIA_VID_PACKING_PACKETS) == 0 || codec_info[i].dir != PJMEDIA_DIR_ENCODING_DECODING) { continue; } pjmedia_vid_codec_mgr_get_default_param(NULL, &codec_info[i], &codec_param); fmt = &m->desc.fmt[m->desc.fmt_count++]; fmt->ptr = (char*) pj_pool_alloc(pool, 8); fmt->slen = pj_utoa(codec_info[i].pt, fmt->ptr); rtpmap.pt = *fmt; /* Encoding name */ rtpmap.enc_name = codec_info[i].encoding_name; /* Clock rate */ rtpmap.clock_rate = codec_info[i].clock_rate; if (codec_info[i].pt >= 96 || pjmedia_add_rtpmap_for_static_pt) { pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr); m->attr[m->attr_count++] = attr; } /* Add fmtp params */ if (codec_param.dec_fmtp.cnt > 0) { enum { MAX_FMTP_STR_LEN = 160 }; char buf[MAX_FMTP_STR_LEN]; unsigned buf_len = 0, j; pjmedia_codec_fmtp *dec_fmtp = &codec_param.dec_fmtp; /* Print codec PT */ buf_len += pj_ansi_snprintf(buf, MAX_FMTP_STR_LEN - buf_len, "%d", codec_info[i].pt); for (j = 0; j < dec_fmtp->cnt; ++j) { pj_size_t test_len = 2; /* Check if buf still available */ test_len = dec_fmtp->param[j].val.slen + dec_fmtp->param[j].name.slen + 2; if (test_len + buf_len >= MAX_FMTP_STR_LEN) return PJ_ETOOBIG; /* Print delimiter */ buf_len += pj_ansi_snprintf(&buf[buf_len], MAX_FMTP_STR_LEN - buf_len, (j == 0?" ":";")); /* Print an fmtp param */ if (dec_fmtp->param[j].name.slen) buf_len += pj_ansi_snprintf( &buf[buf_len], MAX_FMTP_STR_LEN - buf_len, "%.*s=%.*s", (int)dec_fmtp->param[j].name.slen, dec_fmtp->param[j].name.ptr, (int)dec_fmtp->param[j].val.slen, dec_fmtp->param[j].val.ptr); else buf_len += pj_ansi_snprintf(&buf[buf_len], MAX_FMTP_STR_LEN - buf_len, "%.*s", (int)dec_fmtp->param[j].val.slen, dec_fmtp->param[j].val.ptr); } attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr); attr->name = pj_str("fmtp"); attr->value = pj_strdup3(pool, buf); m->attr[m->attr_count++] = attr; } /* Find maximum bitrate in this media */ vfd = pjmedia_format_get_video_format_detail(&codec_param.enc_fmt, PJ_TRUE); if (vfd && max_bitrate < vfd->max_bps) max_bitrate = vfd->max_bps; } /* Put bandwidth info in media level using bandwidth modifier "TIAS" * (RFC3890). */ if (max_bitrate && pjmedia_add_bandwidth_tias_in_sdp) { const pj_str_t STR_BANDW_MODIFIER = { "TIAS", 4 }; pjmedia_sdp_bandw *b; b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw); b->modifier = STR_BANDW_MODIFIER; b->value = max_bitrate; m->bandw[m->bandw_count++] = b; } *p_m = m; return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_VIDEO */ /** * Create a "blank" SDP session description. The SDP will contain basic SDP * fields such as origin, time, and name, but without any media lines. */ PJ_DEF(pj_status_t) pjmedia_endpt_create_base_sdp( pjmedia_endpt *endpt, pj_pool_t *pool, const pj_str_t *sess_name, const pj_sockaddr *origin, pjmedia_sdp_session **p_sdp) { pj_time_val tv; pjmedia_sdp_session *sdp; /* Sanity check arguments */ PJ_ASSERT_RETURN(endpt && pool && p_sdp, PJ_EINVAL); sdp = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session); pj_gettimeofday(&tv); sdp->origin.user = pj_str("-"); sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL; sdp->origin.net_type = STR_IN; if (origin->addr.sa_family == pj_AF_INET()) { sdp->origin.addr_type = STR_IP4; pj_strdup2(pool, &sdp->origin.addr, pj_inet_ntoa(origin->ipv4.sin_addr)); } else if (origin->addr.sa_family == pj_AF_INET6()) { char tmp_addr[PJ_INET6_ADDRSTRLEN]; sdp->origin.addr_type = STR_IP6; pj_strdup2(pool, &sdp->origin.addr, pj_sockaddr_print(origin, tmp_addr, sizeof(tmp_addr), 0)); } else { pj_assert(!"Invalid address family"); return PJ_EAFNOTSUP; } if (sess_name) pj_strdup(pool, &sdp->name, sess_name); else sdp->name = STR_SDP_NAME; /* SDP time and attributes. */ sdp->time.start = sdp->time.stop = 0; sdp->attr_count = 0; /* Done */ *p_sdp = sdp; return PJ_SUCCESS; } /** * Create a SDP session description that describes the endpoint * capability. */ PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt, pj_pool_t *pool, unsigned stream_cnt, const pjmedia_sock_info sock_info[], pjmedia_sdp_session **p_sdp ) { const pj_sockaddr *addr0; pjmedia_sdp_session *sdp; pjmedia_sdp_media *m; pj_status_t status; /* Sanity check arguments */ PJ_ASSERT_RETURN(endpt && pool && p_sdp && stream_cnt, PJ_EINVAL); PJ_ASSERT_RETURN(stream_cnt < PJMEDIA_MAX_SDP_MEDIA, PJ_ETOOMANY); addr0 = &sock_info[0].rtp_addr_name; /* Create and initialize basic SDP session */ status = pjmedia_endpt_create_base_sdp(endpt, pool, NULL, addr0, &sdp); if (status != PJ_SUCCESS) return status; /* Audio is first, by convention */ status = pjmedia_endpt_create_audio_sdp(endpt, pool, &sock_info[0], 0, &m); if (status != PJ_SUCCESS) return status; sdp->media[sdp->media_count++] = m; #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) { unsigned i; /* The remaining stream, if any, are videos (by convention as well) */ for (i=1; imedia[sdp->media_count++] = m; } } #endif /* Done */ *p_sdp = sdp; return PJ_SUCCESS; } #if PJ_LOG_MAX_LEVEL >= 3 static const char *good_number(char *buf, pj_int32_t val) { if (val < 1000) { pj_ansi_sprintf(buf, "%d", val); } else if (val < 1000000) { pj_ansi_sprintf(buf, "%d.%dK", val / 1000, (val % 1000) / 100); } else { pj_ansi_sprintf(buf, "%d.%02dM", val / 1000000, (val % 1000000) / 10000); } return buf; } #endif PJ_DEF(pj_status_t) pjmedia_endpt_dump(pjmedia_endpt *endpt) { #if PJ_LOG_MAX_LEVEL >= 3 unsigned i, count; pjmedia_codec_info codec_info[32]; unsigned prio[32]; PJ_LOG(3,(THIS_FILE, "Dumping PJMEDIA capabilities:")); count = PJ_ARRAY_SIZE(codec_info); if (pjmedia_codec_mgr_enum_codecs(&endpt->codec_mgr, &count, codec_info, prio) != PJ_SUCCESS) { PJ_LOG(3,(THIS_FILE, " -error: failed to enum codecs")); return PJ_SUCCESS; } PJ_LOG(3,(THIS_FILE, " Total number of installed codecs: %d", count)); for (i=0; icodec_mgr, &codec_info[i], ¶m) != PJ_SUCCESS) { pj_bzero(¶m, sizeof(pjmedia_codec_param)); } PJ_LOG(3,(THIS_FILE, " %s codec #%2d: pt=%d (%.*s @%dKHz/%d, %sbps, %dms%s%s%s%s%s)", type, i, codec_info[i].pt, (int)codec_info[i].encoding_name.slen, codec_info[i].encoding_name.ptr, codec_info[i].clock_rate/1000, codec_info[i].channel_cnt, good_number(bps, param.info.avg_bps), param.info.frm_ptime * param.setting.frm_per_pkt, (param.setting.vad ? " vad" : ""), (param.setting.cng ? " cng" : ""), (param.setting.plc ? " plc" : ""), (param.setting.penh ? " penh" : ""), (prio[i]==PJMEDIA_CODEC_PRIO_DISABLED?" disabled":""))); } #endif return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_endpt_atexit( pjmedia_endpt *endpt, pjmedia_endpt_exit_callback func) { exit_cb *new_cb; PJ_ASSERT_RETURN(endpt && func, PJ_EINVAL); if (endpt->quit_flag) return PJ_EINVALIDOP; new_cb = PJ_POOL_ZALLOC_T(endpt->pool, exit_cb); new_cb->func = func; pj_enter_critical_section(); pj_list_push_back(&endpt->exit_cb_list, new_cb); pj_leave_critical_section(); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/errno.c ================================================ /* $Id: errno.c 3945 2012-01-27 09:12:59Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) PJ_BEGIN_DECL const char* get_libsrtp_errstr(int err); PJ_END_DECL #endif /* PJMEDIA's own error codes/messages * MUST KEEP THIS ARRAY SORTED!! * Message must be limited to 64 chars! */ #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) static const struct { int code; const char *msg; } err_str[] = { /* Generic PJMEDIA errors, shouldn't be used! */ PJ_BUILD_ERR( PJMEDIA_ERROR, "Unspecified PJMEDIA error" ), /* SDP error. */ PJ_BUILD_ERR( PJMEDIA_SDP_EINSDP, "Invalid SDP descriptor" ), PJ_BUILD_ERR( PJMEDIA_SDP_EINVER, "Invalid SDP version line" ), PJ_BUILD_ERR( PJMEDIA_SDP_EINORIGIN, "Invalid SDP origin line" ), PJ_BUILD_ERR( PJMEDIA_SDP_EINTIME, "Invalid SDP time line"), PJ_BUILD_ERR( PJMEDIA_SDP_EINNAME, "SDP name/subject line is empty"), PJ_BUILD_ERR( PJMEDIA_SDP_EINCONN, "Invalid SDP connection line"), PJ_BUILD_ERR( PJMEDIA_SDP_EMISSINGCONN, "Missing SDP connection info line"), PJ_BUILD_ERR( PJMEDIA_SDP_EINATTR, "Invalid SDP attributes"), PJ_BUILD_ERR( PJMEDIA_SDP_EINRTPMAP, "Invalid SDP rtpmap attribute"), PJ_BUILD_ERR( PJMEDIA_SDP_ERTPMAPTOOLONG,"SDP rtpmap attribute too long"), PJ_BUILD_ERR( PJMEDIA_SDP_EMISSINGRTPMAP,"Missing SDP rtpmap for dynamic payload type"), PJ_BUILD_ERR( PJMEDIA_SDP_EINMEDIA, "Invalid SDP media line" ), PJ_BUILD_ERR( PJMEDIA_SDP_ENOFMT, "No SDP payload format in the media line" ), PJ_BUILD_ERR( PJMEDIA_SDP_EINPT, "Invalid SDP payload type in media line" ), PJ_BUILD_ERR( PJMEDIA_SDP_EINFMTP, "Invalid SDP fmtp attribute" ), PJ_BUILD_ERR( PJMEDIA_SDP_EINRTCP, "Invalid SDP rtcp attribyte" ), PJ_BUILD_ERR( PJMEDIA_SDP_EINPROTO, "Invalid SDP media transport protocol" ), PJ_BUILD_ERR( PJMEDIA_SDP_EINBANDW, "Invalid SDP bandwidth info line" ), /* SDP negotiator errors. */ PJ_BUILD_ERR( PJMEDIA_SDPNEG_EINSTATE, "Invalid SDP negotiator state for operation" ), PJ_BUILD_ERR( PJMEDIA_SDPNEG_ENOINITIAL, "No initial local SDP in SDP negotiator" ), PJ_BUILD_ERR( PJMEDIA_SDPNEG_ENOACTIVE, "No active SDP in SDP negotiator" ), PJ_BUILD_ERR( PJMEDIA_SDPNEG_ENONEG, "No current local/remote offer/answer" ), PJ_BUILD_ERR( PJMEDIA_SDPNEG_EMISMEDIA, "SDP media count mismatch in offer/answer" ), PJ_BUILD_ERR( PJMEDIA_SDPNEG_EINVANSMEDIA, "SDP media type mismatch in offer/answer" ), PJ_BUILD_ERR( PJMEDIA_SDPNEG_EINVANSTP, "SDP media transport type mismatch in offer/answer" ), PJ_BUILD_ERR( PJMEDIA_SDPNEG_EANSNOMEDIA, "No common SDP media payload in answer" ), PJ_BUILD_ERR( PJMEDIA_SDPNEG_ENOMEDIA, "No active media stream after negotiation" ), PJ_BUILD_ERR( PJMEDIA_SDPNEG_NOANSCODEC, "No suitable codec for remote offer"), PJ_BUILD_ERR( PJMEDIA_SDPNEG_NOANSTELEVENT, "No suitable telephone-event for remote offer"), PJ_BUILD_ERR( PJMEDIA_SDPNEG_NOANSUNKNOWN, "No suitable answer for unknown remote offer"), /* SDP comparison results */ PJ_BUILD_ERR( PJMEDIA_SDP_EMEDIANOTEQUAL, "SDP media descriptor not equal" ), PJ_BUILD_ERR( PJMEDIA_SDP_EPORTNOTEQUAL, "Port in SDP media descriptor not equal" ), PJ_BUILD_ERR( PJMEDIA_SDP_ETPORTNOTEQUAL, "Transport in SDP media descriptor not equal" ), PJ_BUILD_ERR( PJMEDIA_SDP_EFORMATNOTEQUAL, "Format in SDP media descriptor not equal" ), PJ_BUILD_ERR( PJMEDIA_SDP_ECONNNOTEQUAL, "SDP connection line not equal" ), PJ_BUILD_ERR( PJMEDIA_SDP_EATTRNOTEQUAL, "SDP attributes not equal" ), PJ_BUILD_ERR( PJMEDIA_SDP_EDIRNOTEQUAL, "SDP media direction not equal" ), PJ_BUILD_ERR( PJMEDIA_SDP_EFMTPNOTEQUAL, "SDP fmtp attribute not equal" ), PJ_BUILD_ERR( PJMEDIA_SDP_ERTPMAPNOTEQUAL, "SDP rtpmap attribute not equal" ), PJ_BUILD_ERR( PJMEDIA_SDP_ESESSNOTEQUAL, "SDP session descriptor not equal" ), PJ_BUILD_ERR( PJMEDIA_SDP_EORIGINNOTEQUAL, "SDP origin line not equal" ), PJ_BUILD_ERR( PJMEDIA_SDP_ENAMENOTEQUAL, "SDP name/subject line not equal" ), PJ_BUILD_ERR( PJMEDIA_SDP_ETIMENOTEQUAL, "SDP time line not equal" ), /* Codec errors. */ PJ_BUILD_ERR( PJMEDIA_CODEC_EUNSUP, "Unsupported media codec" ), PJ_BUILD_ERR( PJMEDIA_CODEC_EFAILED, "Codec internal creation error" ), PJ_BUILD_ERR( PJMEDIA_CODEC_EFRMTOOSHORT, "Codec frame is too short" ), PJ_BUILD_ERR( PJMEDIA_CODEC_EPCMTOOSHORT, "PCM frame is too short" ), PJ_BUILD_ERR( PJMEDIA_CODEC_EFRMINLEN, "Invalid codec frame length" ), PJ_BUILD_ERR( PJMEDIA_CODEC_EPCMFRMINLEN, "Invalid PCM frame length" ), PJ_BUILD_ERR( PJMEDIA_CODEC_EINMODE, "Invalid codec mode (no fmtp?)" ), PJ_BUILD_ERR( PJMEDIA_CODEC_EBADBITSTREAM, "Bad or corrupted bitstream" ), /* Media errors. */ PJ_BUILD_ERR( PJMEDIA_EINVALIDIP, "Invalid remote media (IP) address" ), PJ_BUILD_ERR( PJMEDIA_EASYMCODEC, "Asymetric media codec is not supported" ), PJ_BUILD_ERR( PJMEDIA_EINVALIDPT, "Invalid media payload type" ), PJ_BUILD_ERR( PJMEDIA_EMISSINGRTPMAP, "Missing rtpmap in media description" ), PJ_BUILD_ERR( PJMEDIA_EINVALIMEDIATYPE, "Invalid media type" ), PJ_BUILD_ERR( PJMEDIA_EREMOTENODTMF, "Remote does not support DTMF" ), PJ_BUILD_ERR( PJMEDIA_RTP_EINDTMF, "Invalid DTMF digit" ), PJ_BUILD_ERR( PJMEDIA_RTP_EREMNORFC2833,"Remote does not support RFC 2833" ), PJ_BUILD_ERR( PJMEDIA_EBADFMT, "Bad format"), /* RTP session errors. */ PJ_BUILD_ERR( PJMEDIA_RTP_EINPKT, "Invalid RTP packet" ), PJ_BUILD_ERR( PJMEDIA_RTP_EINPACK, "Invalid RTP packing (internal error)" ), PJ_BUILD_ERR( PJMEDIA_RTP_EINVER, "Invalid RTP version" ), PJ_BUILD_ERR( PJMEDIA_RTP_EINSSRC, "RTP packet SSRC id mismatch" ), PJ_BUILD_ERR( PJMEDIA_RTP_EINPT, "RTP packet payload type mismatch" ), PJ_BUILD_ERR( PJMEDIA_RTP_EINLEN, "Invalid RTP packet length" ), PJ_BUILD_ERR( PJMEDIA_RTP_ESESSRESTART, "RTP session restarted" ), PJ_BUILD_ERR( PJMEDIA_RTP_ESESSPROBATION, "RTP session in probation" ), PJ_BUILD_ERR( PJMEDIA_RTP_EBADSEQ, "Bad sequence number in RTP packet" ), PJ_BUILD_ERR( PJMEDIA_RTP_EBADDEST, "RTP media port destination is not configured" ), PJ_BUILD_ERR( PJMEDIA_RTP_ENOCONFIG, "RTP is not configured" ), /* Media port errors: */ PJ_BUILD_ERR( PJMEDIA_ENOTCOMPATIBLE, "Media ports are not compatible" ), PJ_BUILD_ERR( PJMEDIA_ENCCLOCKRATE, "Media ports have incompatible clock rate" ), PJ_BUILD_ERR( PJMEDIA_ENCSAMPLESPFRAME, "Media ports have incompatible samples per frame" ), PJ_BUILD_ERR( PJMEDIA_ENCTYPE, "Media ports have incompatible media type" ), PJ_BUILD_ERR( PJMEDIA_ENCBITS, "Media ports have incompatible bits per sample" ), PJ_BUILD_ERR( PJMEDIA_ENCBYTES, "Media ports have incompatible bytes per frame" ), PJ_BUILD_ERR( PJMEDIA_ENCCHANNEL, "Media ports have incompatible number of channels" ), /* Media file errors: */ PJ_BUILD_ERR( PJMEDIA_ENOTVALIDWAVE, "Not a valid WAVE file" ), PJ_BUILD_ERR( PJMEDIA_EWAVEUNSUPP, "Unsupported WAVE file format" ), PJ_BUILD_ERR( PJMEDIA_EWAVETOOSHORT, "WAVE file too short" ), PJ_BUILD_ERR( PJMEDIA_EFRMFILETOOBIG, "Sound frame too large for file buffer"), PJ_BUILD_ERR( PJMEDIA_EAVIUNSUPP, "Unsupported AVI file"), /* Sound device errors: */ PJ_BUILD_ERR( PJMEDIA_ENOSNDREC, "No suitable sound capture device" ), PJ_BUILD_ERR( PJMEDIA_ENOSNDPLAY, "No suitable sound playback device" ), PJ_BUILD_ERR( PJMEDIA_ESNDINDEVID, "Invalid sound device ID" ), PJ_BUILD_ERR( PJMEDIA_ESNDINSAMPLEFMT, "Invalid sample format for sound device" ), #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) /* SRTP transport errors: */ PJ_BUILD_ERR( PJMEDIA_SRTP_ECRYPTONOTMATCH, "SRTP crypto-suite name not match the offerer tag" ), PJ_BUILD_ERR( PJMEDIA_SRTP_EINKEYLEN, "Invalid SRTP key length for specific crypto" ), PJ_BUILD_ERR( PJMEDIA_SRTP_ENOTSUPCRYPTO, "Unsupported SRTP crypto-suite" ), PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPAMBIGUEANS, "SRTP SDP contains ambigue answer" ), PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPDUPCRYPTOTAG,"Duplicated SRTP crypto tag" ), PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPINCRYPTO, "Invalid SRTP crypto attribute" ), PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPINCRYPTOTAG, "Invalid SRTP crypto tag" ), PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPINTRANSPORT, "Invalid SDP media transport for SRTP" ), PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPREQCRYPTO, "SRTP crypto attribute required" ), PJ_BUILD_ERR( PJMEDIA_SRTP_ESDPREQSECTP, "Secure transport required in SDP media descriptor" ) #endif }; #endif /* PJ_HAS_ERROR_STRING */ /* * pjmedia_strerror() */ PJ_DEF(pj_str_t) pjmedia_strerror( pj_status_t statcode, char *buf, pj_size_t bufsize ) { pj_str_t errstr; #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) /* LIBSRTP error */ if (statcode >= PJMEDIA_LIBSRTP_ERRNO_START && statcode < PJMEDIA_LIBSRTP_ERRNO_END) { int err = statcode - PJMEDIA_LIBSRTP_ERRNO_START; pj_str_t msg; msg = pj_str((char*)get_libsrtp_errstr(err)); errstr.ptr = buf; pj_strncpy_with_null(&errstr, &msg, bufsize); return errstr; } else #endif /* PJMEDIA error */ if (statcode >= PJMEDIA_ERRNO_START && statcode < PJMEDIA_ERRNO_END) { /* Find the error in the table. * Use binary search! */ int first = 0; int n = PJ_ARRAY_SIZE(err_str); while (n > 0) { int half = n/2; int mid = first + half; if (err_str[mid].code < statcode) { first = mid+1; n -= (half+1); } else if (err_str[mid].code > statcode) { n = half; } else { first = mid; break; } } if (PJ_ARRAY_SIZE(err_str) && err_str[first].code == statcode) { pj_str_t msg; msg.ptr = (char*)err_str[first].msg; msg.slen = pj_ansi_strlen(err_str[first].msg); errstr.ptr = buf; pj_strncpy_with_null(&errstr, &msg, bufsize); return errstr; } } #endif /* PJ_HAS_ERROR_STRING */ /* Error not found. */ errstr.ptr = buf; errstr.slen = pj_ansi_snprintf(buf, bufsize, "Unknown pjmedia error %d", statcode); if (errstr.slen < 1 || errstr.slen >= (pj_ssize_t)bufsize) errstr.slen = bufsize - 1; return errstr; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/event.c ================================================ /* $Id: event.c 3905 2011-12-09 05:15:39Z ming $ */ /* * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #define THIS_FILE "event.c" #define MAX_EVENTS 16 typedef struct esub esub; struct esub { PJ_DECL_LIST_MEMBER(esub); pjmedia_event_cb *cb; void *user_data; void *epub; }; typedef struct event_queue { pjmedia_event events[MAX_EVENTS]; /**< array of events. */ int head, tail; pj_bool_t is_full; } event_queue; struct pjmedia_event_mgr { pj_pool_t *pool; pj_thread_t *thread; /**< worker thread. */ pj_bool_t is_quitting; pj_sem_t *sem; pj_mutex_t *mutex; event_queue ev_queue; event_queue *pub_ev_queue; /**< publish() event queue. */ esub esub_list; /**< list of subscribers. */ esub free_esub_list; /**< list of subscribers. */ esub *th_next_sub, /**< worker thread's next sub. */ *pub_next_sub; /**< publish() next sub. */ }; static pjmedia_event_mgr *event_manager_instance; static pj_status_t event_queue_add_event(event_queue* ev_queue, pjmedia_event *event) { if (ev_queue->is_full) { char ev_name[5]; /* This event will be ignored. */ PJ_LOG(4, (THIS_FILE, "Lost event %s from publisher [0x%p] " "due to full queue.", pjmedia_fourcc_name(event->type, ev_name), event->epub)); return PJ_ETOOMANY; } pj_memcpy(&ev_queue->events[ev_queue->tail], event, sizeof(*event)); ev_queue->tail = (ev_queue->tail + 1) % MAX_EVENTS; if (ev_queue->tail == ev_queue->head) ev_queue->is_full = PJ_TRUE; return PJ_SUCCESS; } static pj_status_t event_mgr_distribute_events(pjmedia_event_mgr *mgr, event_queue *ev_queue, esub **next_sub, pj_bool_t rls_lock) { pj_status_t err = PJ_SUCCESS; esub * sub = mgr->esub_list.next; pjmedia_event *ev = &ev_queue->events[ev_queue->head]; while (sub != &mgr->esub_list) { *next_sub = sub->next; /* Check if the subscriber is interested in * receiving the event from the publisher. */ if (sub->epub == ev->epub || !sub->epub) { pjmedia_event_cb *cb = sub->cb; void *user_data = sub->user_data; pj_status_t status; if (rls_lock) pj_mutex_unlock(mgr->mutex); status = (*cb)(ev, user_data); if (status != PJ_SUCCESS && err == PJ_SUCCESS) err = status; if (rls_lock) pj_mutex_lock(mgr->mutex); } sub = *next_sub; } *next_sub = NULL; ev_queue->head = (ev_queue->head + 1) % MAX_EVENTS; ev_queue->is_full = PJ_FALSE; return err; } /* Event worker thread function. */ static int event_worker_thread(void *arg) { pjmedia_event_mgr *mgr = (pjmedia_event_mgr *)arg; while (1) { /* Wait until there is an event. */ pj_sem_wait(mgr->sem); if (mgr->is_quitting) break; pj_mutex_lock(mgr->mutex); event_mgr_distribute_events(mgr, &mgr->ev_queue, &mgr->th_next_sub, PJ_TRUE); pj_mutex_unlock(mgr->mutex); } return 0; } PJ_DEF(pj_status_t) pjmedia_event_mgr_create(pj_pool_t *pool, unsigned options, pjmedia_event_mgr **p_mgr) { pjmedia_event_mgr *mgr; pj_status_t status; mgr = PJ_POOL_ZALLOC_T(pool, pjmedia_event_mgr); mgr->pool = pj_pool_create(pool->factory, "evt mgr", 500, 500, NULL); pj_list_init(&mgr->esub_list); pj_list_init(&mgr->free_esub_list); if (!(options & PJMEDIA_EVENT_MGR_NO_THREAD)) { status = pj_sem_create(mgr->pool, "ev_sem", 0, MAX_EVENTS + 1, &mgr->sem); if (status != PJ_SUCCESS) return status; status = pj_thread_create(mgr->pool, "ev_thread", &event_worker_thread, mgr, 0, 0, &mgr->thread); if (status != PJ_SUCCESS) { pjmedia_event_mgr_destroy(mgr); return status; } } status = pj_mutex_create_recursive(mgr->pool, "ev_mutex", &mgr->mutex); if (status != PJ_SUCCESS) { pjmedia_event_mgr_destroy(mgr); return status; } if (!event_manager_instance) event_manager_instance = mgr; if (p_mgr) *p_mgr = mgr; return PJ_SUCCESS; } PJ_DEF(pjmedia_event_mgr*) pjmedia_event_mgr_instance(void) { return event_manager_instance; } PJ_DEF(void) pjmedia_event_mgr_set_instance(pjmedia_event_mgr *mgr) { event_manager_instance = mgr; } PJ_DEF(void) pjmedia_event_mgr_destroy(pjmedia_event_mgr *mgr) { if (!mgr) mgr = pjmedia_event_mgr_instance(); PJ_ASSERT_ON_FAIL(mgr != NULL, return); if (mgr->thread) { mgr->is_quitting = PJ_TRUE; pj_sem_post(mgr->sem); pj_thread_join(mgr->thread); } if (mgr->sem) { pj_sem_destroy(mgr->sem); mgr->sem = NULL; } if (mgr->mutex) { pj_mutex_destroy(mgr->mutex); mgr->mutex = NULL; } if (mgr->pool) pj_pool_release(mgr->pool); if (event_manager_instance == mgr) event_manager_instance = NULL; } PJ_DEF(void) pjmedia_event_init( pjmedia_event *event, pjmedia_event_type type, const pj_timestamp *ts, const void *src) { pj_bzero(event, sizeof(*event)); event->type = type; if (ts) event->timestamp.u64 = ts->u64; event->epub = event->src = src; } PJ_DEF(pj_status_t) pjmedia_event_subscribe( pjmedia_event_mgr *mgr, pjmedia_event_cb *cb, void *user_data, void *epub) { esub *sub; PJ_ASSERT_RETURN(cb, PJ_EINVAL); if (!mgr) mgr = pjmedia_event_mgr_instance(); PJ_ASSERT_RETURN(mgr, PJ_EINVAL); pj_mutex_lock(mgr->mutex); /* Check whether callback function with the same user data is already * subscribed to the publisher. This is to prevent the callback function * receiving the same event from the same publisher more than once. */ sub = mgr->esub_list.next; while (sub != &mgr->esub_list) { esub *next = sub->next; if (sub->cb == cb && sub->user_data == user_data && sub->epub == epub) { pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } sub = next; } if (mgr->free_esub_list.next != &mgr->free_esub_list) { sub = mgr->free_esub_list.next; pj_list_erase(sub); } else sub = PJ_POOL_ZALLOC_T(mgr->pool, esub); sub->cb = cb; sub->user_data = user_data; sub->epub = epub; pj_list_push_back(&mgr->esub_list, sub); pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_event_unsubscribe(pjmedia_event_mgr *mgr, pjmedia_event_cb *cb, void *user_data, void *epub) { esub *sub; PJ_ASSERT_RETURN(cb, PJ_EINVAL); if (!mgr) mgr = pjmedia_event_mgr_instance(); PJ_ASSERT_RETURN(mgr, PJ_EINVAL); pj_mutex_lock(mgr->mutex); sub = mgr->esub_list.next; while (sub != &mgr->esub_list) { esub *next = sub->next; if (sub->cb == cb && (sub->user_data == user_data || !user_data) && (sub->epub == epub || !epub)) { /* If the worker thread or pjmedia_event_publish() API is * in the process of distributing events, make sure that * its pointer to the next subscriber stays valid. */ if (mgr->th_next_sub == sub) mgr->th_next_sub = sub->next; if (mgr->pub_next_sub == sub) mgr->pub_next_sub = sub->next; pj_list_erase(sub); pj_list_push_back(&mgr->free_esub_list, sub); if (user_data && epub) break; } sub = next; } pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_event_publish( pjmedia_event_mgr *mgr, void *epub, pjmedia_event *event, pjmedia_event_publish_flag flag) { pj_status_t err = PJ_SUCCESS; PJ_ASSERT_RETURN(epub && event, PJ_EINVAL); if (!mgr) mgr = pjmedia_event_mgr_instance(); PJ_ASSERT_RETURN(mgr, PJ_EINVAL); event->epub = epub; pj_mutex_lock(mgr->mutex); if (flag & PJMEDIA_EVENT_PUBLISH_POST_EVENT) { if (event_queue_add_event(&mgr->ev_queue, event) == PJ_SUCCESS) pj_sem_post(mgr->sem); } else { /* For nested pjmedia_event_publish() calls, i.e. calling publish() * inside the subscriber's callback, the function will only add * the event to the event queue of the first publish() call. It * is the first publish() call that will be responsible to * distribute the events. */ if (mgr->pub_ev_queue) { event_queue_add_event(mgr->pub_ev_queue, event); } else { static event_queue ev_queue; pj_status_t status; ev_queue.head = ev_queue.tail = 0; ev_queue.is_full = PJ_FALSE; mgr->pub_ev_queue = &ev_queue; event_queue_add_event(mgr->pub_ev_queue, event); do { status = event_mgr_distribute_events(mgr, mgr->pub_ev_queue, &mgr->pub_next_sub, PJ_FALSE); if (status != PJ_SUCCESS && err == PJ_SUCCESS) err = status; } while(ev_queue.head != ev_queue.tail || ev_queue.is_full); mgr->pub_ev_queue = NULL; } } pj_mutex_unlock(mgr->mutex); return err; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/ffmpeg_util.c ================================================ /* $Id: ffmpeg_util.c 4158 2012-06-06 09:56:14Z nanang $ */ /* * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #if PJMEDIA_HAS_LIBAVFORMAT && PJMEDIA_HAS_LIBAVUTIL #include "ffmpeg_util.h" #include /* Conversion table between pjmedia_format_id and AVPixelFormat */ static const struct ffmpeg_fmt_table_t { pjmedia_format_id id; enum AVPixelFormat pf; } ffmpeg_fmt_table[] = { { PJMEDIA_FORMAT_ARGB, AV_PIX_FMT_ARGB}, { PJMEDIA_FORMAT_RGBA, AV_PIX_FMT_RGBA}, { PJMEDIA_FORMAT_RGB24,AV_PIX_FMT_BGR24}, { PJMEDIA_FORMAT_BGRA, AV_PIX_FMT_BGRA}, { PJMEDIA_FORMAT_GBRP, AV_PIX_FMT_GBR24P}, { PJMEDIA_FORMAT_AYUV, AV_PIX_FMT_NONE}, { PJMEDIA_FORMAT_YUY2, AV_PIX_FMT_YUYV422}, { PJMEDIA_FORMAT_UYVY, AV_PIX_FMT_UYVY422}, { PJMEDIA_FORMAT_I420, AV_PIX_FMT_YUV420P}, //{ PJMEDIA_FORMAT_YV12, AV_PIX_FMT_YUV420P}, { PJMEDIA_FORMAT_I422, AV_PIX_FMT_YUV422P}, { PJMEDIA_FORMAT_I420JPEG, AV_PIX_FMT_YUVJ420P}, { PJMEDIA_FORMAT_I422JPEG, AV_PIX_FMT_YUVJ422P}, }; /* Conversion table between pjmedia_format_id and CodecID */ static const struct ffmpeg_codec_table_t { pjmedia_format_id id; unsigned codec_id; } ffmpeg_codec_table[] = { {PJMEDIA_FORMAT_H261, AV_CODEC_ID_H261}, {PJMEDIA_FORMAT_H263, AV_CODEC_ID_H263}, {PJMEDIA_FORMAT_H263P, AV_CODEC_ID_H263P}, {PJMEDIA_FORMAT_H264, AV_CODEC_ID_H264}, {PJMEDIA_FORMAT_MPEG1VIDEO, AV_CODEC_ID_MPEG1VIDEO}, {PJMEDIA_FORMAT_MPEG2VIDEO, AV_CODEC_ID_MPEG2VIDEO}, {PJMEDIA_FORMAT_MPEG4, AV_CODEC_ID_MPEG4}, {PJMEDIA_FORMAT_MJPEG, AV_CODEC_ID_MJPEG} }; static int pjmedia_ffmpeg_ref_cnt; static void ffmpeg_log_cb(void* ptr, int level, const char* fmt, va_list vl); void pjmedia_ffmpeg_add_ref() { if (pjmedia_ffmpeg_ref_cnt++ == 0) { av_log_set_level(AV_LOG_ERROR); av_log_set_callback(&ffmpeg_log_cb); av_register_all(); } } void pjmedia_ffmpeg_dec_ref() { if (pjmedia_ffmpeg_ref_cnt-- == 1) { /* How to shutdown ffmpeg? */ } if (pjmedia_ffmpeg_ref_cnt < 0) pjmedia_ffmpeg_ref_cnt = 0; } static void ffmpeg_log_cb(void* ptr, int level, const char* fmt, va_list vl) { const char *LOG_SENDER = "ffmpeg"; enum { LOG_LEVEL = 5 }; char buf[100]; pj_size_t bufsize = sizeof(buf), len; pj_str_t fmt_st; /* Custom callback needs to filter log level by itself */ if (level > av_log_get_level()) return; /* Add original ffmpeg sender to log format */ if (ptr) { AVClass* avc = *(AVClass**)ptr; len = pj_ansi_snprintf(buf, bufsize, "%s: ", avc->item_name(ptr)); if (len < 1 || len >= bufsize) len = bufsize - 1; bufsize -= len; } /* Copy original log format */ len = pj_ansi_strlen(fmt); if (len > bufsize-1) len = bufsize-1; pj_memcpy(buf+sizeof(buf)-bufsize, fmt, len); bufsize -= len; /* Trim log format */ pj_strset(&fmt_st, buf, sizeof(buf)-bufsize); pj_strrtrim(&fmt_st); buf[fmt_st.slen] = '\0'; pj_log(LOG_SENDER, LOG_LEVEL, buf, vl); } pj_status_t pjmedia_format_id_to_PixelFormat(pjmedia_format_id fmt_id, enum AVPixelFormat *pixel_format) { unsigned i; for (i=0; iid==fmt_id && t->pf != AV_PIX_FMT_NONE) { *pixel_format = t->pf; return PJ_SUCCESS; } } *pixel_format = AV_PIX_FMT_NONE; return PJ_ENOTFOUND; } pj_status_t PixelFormat_to_pjmedia_format_id(enum AVPixelFormat pf, pjmedia_format_id *fmt_id) { unsigned i; for (i=0; ipf == pf) { if (fmt_id) *fmt_id = t->id; return PJ_SUCCESS; } } return PJ_ENOTFOUND; } pj_status_t pjmedia_format_id_to_CodecID(pjmedia_format_id fmt_id, unsigned *codec_id) { unsigned i; for (i=0; iid==fmt_id && t->codec_id != AV_PIX_FMT_NONE) { *codec_id = t->codec_id; return PJ_SUCCESS; } } *codec_id = (unsigned)AV_PIX_FMT_NONE; return PJ_ENOTFOUND; } pj_status_t CodecID_to_pjmedia_format_id(unsigned codec_id, pjmedia_format_id *fmt_id) { unsigned i; for (i=0; icodec_id == codec_id) { if (fmt_id) *fmt_id = t->id; return PJ_SUCCESS; } } return PJ_ENOTFOUND; } #ifdef _MSC_VER # pragma comment( lib, "avformat.lib") # pragma comment( lib, "avutil.lib") #endif #endif /* #if PJMEDIA_HAS_LIBAVFORMAT && PJMEDIA_HAS_LIBAVUTIL */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/ffmpeg_util.h ================================================ /* $Id: ffmpeg_util.h 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This file contains common utilities that are useful for pjmedia components * that use ffmpeg. This is not a public API. */ #ifndef __PJMEDIA_FFMPEG_UTIL_H__ #define __PJMEDIA_FFMPEG_UTIL_H__ #include #ifdef _MSC_VER # ifndef __cplusplus # define inline _inline # endif # pragma warning(disable:4244) /* possible loss of data */ #endif #include #include void pjmedia_ffmpeg_add_ref(); void pjmedia_ffmpeg_dec_ref(); pj_status_t pjmedia_format_id_to_PixelFormat(pjmedia_format_id fmt_id, enum AVPixelFormat *pixel_format); pj_status_t PixelFormat_to_pjmedia_format_id(enum AVPixelFormat pf, pjmedia_format_id *fmt_id); pj_status_t pjmedia_format_id_to_CodecID(pjmedia_format_id fmt_id, unsigned *codec_id); pj_status_t CodecID_to_pjmedia_format_id(unsigned codec_id, pjmedia_format_id *fmt_id); #endif /* __PJMEDIA_FFMPEG_UTIL_H__ */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/format.c ================================================ /* $Id: format.c 4158 2012-06-06 09:56:14Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include PJ_DEF(pjmedia_audio_format_detail*) pjmedia_format_get_audio_format_detail(const pjmedia_format *fmt, pj_bool_t assert_valid) { if (fmt->detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO) { return (pjmedia_audio_format_detail*) &fmt->det.aud; } else { /* Get rid of unused var compiler warning if pj_assert() * macro does not do anything */ PJ_UNUSED_ARG(assert_valid); pj_assert(!assert_valid || !"Invalid audio format detail"); return NULL; } } PJ_DEF(pjmedia_format*) pjmedia_format_copy(pjmedia_format *dst, const pjmedia_format *src) { return (pjmedia_format*)pj_memcpy(dst, src, sizeof(*src)); } #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) static pj_status_t apply_packed_fmt(const pjmedia_video_format_info *fi, pjmedia_video_apply_fmt_param *aparam); static pj_status_t apply_planar_420(const pjmedia_video_format_info *fi, pjmedia_video_apply_fmt_param *aparam); static pj_status_t apply_planar_422(const pjmedia_video_format_info *fi, pjmedia_video_apply_fmt_param *aparam); static pj_status_t apply_planar_444(const pjmedia_video_format_info *fi, pjmedia_video_apply_fmt_param *aparam); struct pjmedia_video_format_mgr { unsigned max_info; unsigned info_cnt; pjmedia_video_format_info **infos; }; static pjmedia_video_format_mgr *video_format_mgr_instance; static pjmedia_video_format_info built_in_vid_fmt_info[] = { {PJMEDIA_FORMAT_RGB24, "RGB24", PJMEDIA_COLOR_MODEL_RGB, 24, 1, &apply_packed_fmt}, {PJMEDIA_FORMAT_ARGB, "ARGB", PJMEDIA_COLOR_MODEL_RGB, 32, 1, &apply_packed_fmt}, {PJMEDIA_FORMAT_RGBA, "RGBA", PJMEDIA_COLOR_MODEL_RGB, 32, 1, &apply_packed_fmt}, {PJMEDIA_FORMAT_BGRA, "BGRA", PJMEDIA_COLOR_MODEL_RGB, 32, 1, &apply_packed_fmt}, {PJMEDIA_FORMAT_DIB , "DIB ", PJMEDIA_COLOR_MODEL_RGB, 24, 1, &apply_packed_fmt}, {PJMEDIA_FORMAT_GBRP, "GBRP", PJMEDIA_COLOR_MODEL_RGB, 24, 3, &apply_planar_444}, {PJMEDIA_FORMAT_AYUV, "AYUV", PJMEDIA_COLOR_MODEL_YUV, 32, 1, &apply_packed_fmt}, {PJMEDIA_FORMAT_YUY2, "YUY2", PJMEDIA_COLOR_MODEL_YUV, 16, 1, &apply_packed_fmt}, {PJMEDIA_FORMAT_UYVY, "UYVY", PJMEDIA_COLOR_MODEL_YUV, 16, 1, &apply_packed_fmt}, {PJMEDIA_FORMAT_YVYU, "YVYU", PJMEDIA_COLOR_MODEL_YUV, 16, 1, &apply_packed_fmt}, {PJMEDIA_FORMAT_I420, "I420", PJMEDIA_COLOR_MODEL_YUV, 12, 3, &apply_planar_420}, {PJMEDIA_FORMAT_YV12, "YV12", PJMEDIA_COLOR_MODEL_YUV, 12, 3, &apply_planar_420}, {PJMEDIA_FORMAT_I422, "I422", PJMEDIA_COLOR_MODEL_YUV, 16, 3, &apply_planar_422}, {PJMEDIA_FORMAT_I420JPEG, "I420JPG", PJMEDIA_COLOR_MODEL_YUV, 12, 3, &apply_planar_420}, {PJMEDIA_FORMAT_I422JPEG, "I422JPG", PJMEDIA_COLOR_MODEL_YUV, 16, 3, &apply_planar_422}, }; PJ_DEF(void) pjmedia_format_init_video( pjmedia_format *fmt, pj_uint32_t fmt_id, unsigned width, unsigned height, unsigned fps_num, unsigned fps_denum) { pj_assert(fps_denum); fmt->id = fmt_id; fmt->type = PJMEDIA_TYPE_VIDEO; fmt->detail_type = PJMEDIA_FORMAT_DETAIL_VIDEO; fmt->det.vid.size.w = width; fmt->det.vid.size.h = height; fmt->det.vid.fps.num = fps_num; fmt->det.vid.fps.denum = fps_denum; fmt->det.vid.avg_bps = fmt->det.vid.max_bps = 0; if (pjmedia_video_format_mgr_instance()) { const pjmedia_video_format_info *vfi; pjmedia_video_apply_fmt_param vafp; pj_uint32_t bps; vfi = pjmedia_get_video_format_info(NULL, fmt->id); if (vfi) { pj_bzero(&vafp, sizeof(vafp)); vafp.size = fmt->det.vid.size; vfi->apply_fmt(vfi, &vafp); bps = (pj_uint32_t)vafp.framebytes * fps_num * (pj_size_t)8 / fps_denum; fmt->det.vid.avg_bps = fmt->det.vid.max_bps = bps; } } } PJ_DEF(pjmedia_video_format_detail*) pjmedia_format_get_video_format_detail(const pjmedia_format *fmt, pj_bool_t assert_valid) { if (fmt->detail_type==PJMEDIA_FORMAT_DETAIL_VIDEO) { return (pjmedia_video_format_detail*)&fmt->det.vid; } else { pj_assert(!assert_valid || !"Invalid video format detail"); return NULL; } } static pj_status_t apply_packed_fmt(const pjmedia_video_format_info *fi, pjmedia_video_apply_fmt_param *aparam) { unsigned i; pj_size_t stride; stride = (pj_size_t)((aparam->size.w*fi->bpp) >> 3); /* Calculate memsize */ aparam->framebytes = stride * aparam->size.h; /* Packed formats only use 1 plane */ aparam->planes[0] = aparam->buffer; aparam->strides[0] = (int)stride; aparam->plane_bytes[0] = aparam->framebytes; /* Zero unused planes */ for (i=1; istrides[i] = 0; aparam->planes[i] = NULL; } return PJ_SUCCESS; } static pj_status_t apply_planar_420(const pjmedia_video_format_info *fi, pjmedia_video_apply_fmt_param *aparam) { unsigned i; pj_size_t Y_bytes; PJ_UNUSED_ARG(fi); /* Calculate memsize */ Y_bytes = (pj_size_t)(aparam->size.w * aparam->size.h); aparam->framebytes = Y_bytes + (Y_bytes>>1); /* Planar formats use 3 plane */ aparam->strides[0] = aparam->size.w; aparam->strides[1] = aparam->strides[2] = (aparam->size.w>>1); aparam->planes[0] = aparam->buffer; aparam->planes[1] = aparam->planes[0] + Y_bytes; aparam->planes[2] = aparam->planes[1] + (Y_bytes>>2); aparam->plane_bytes[0] = Y_bytes; aparam->plane_bytes[1] = aparam->plane_bytes[2] = (Y_bytes>>2); /* Zero unused planes */ for (i=3; istrides[i] = 0; aparam->planes[i] = NULL; aparam->plane_bytes[i] = 0; } return PJ_SUCCESS; } static pj_status_t apply_planar_422(const pjmedia_video_format_info *fi, pjmedia_video_apply_fmt_param *aparam) { unsigned i; pj_size_t Y_bytes; PJ_UNUSED_ARG(fi); /* Calculate memsize */ Y_bytes = (pj_size_t)(aparam->size.w * aparam->size.h); aparam->framebytes = (Y_bytes << 1); /* Planar formats use 3 plane */ aparam->strides[0] = aparam->size.w; aparam->strides[1] = aparam->strides[2] = (aparam->size.w>>1); aparam->planes[0] = aparam->buffer; aparam->planes[1] = aparam->planes[0] + Y_bytes; aparam->planes[2] = aparam->planes[1] + (Y_bytes>>1); aparam->plane_bytes[0] = Y_bytes; aparam->plane_bytes[1] = aparam->plane_bytes[2] = (Y_bytes>>1); /* Zero unused planes */ for (i=3; istrides[i] = 0; aparam->planes[i] = NULL; aparam->plane_bytes[i] = 0; } return PJ_SUCCESS; } static pj_status_t apply_planar_444(const pjmedia_video_format_info *fi, pjmedia_video_apply_fmt_param *aparam) { unsigned i; pj_size_t Y_bytes; PJ_UNUSED_ARG(fi); /* Calculate memsize */ Y_bytes = (pj_size_t)(aparam->size.w * aparam->size.h); aparam->framebytes = (Y_bytes * 3); /* Planar formats use 3 plane */ aparam->strides[0] = aparam->strides[1] = aparam->strides[2] = aparam->size.w; aparam->planes[0] = aparam->buffer; aparam->planes[1] = aparam->planes[0] + Y_bytes; aparam->planes[2] = aparam->planes[1] + Y_bytes; aparam->plane_bytes[0] = aparam->plane_bytes[1] = aparam->plane_bytes[2] = Y_bytes; /* Zero unused planes */ for (i=3; istrides[i] = 0; aparam->planes[i] = NULL; aparam->plane_bytes[i] = 0; } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_video_format_mgr_create(pj_pool_t *pool, unsigned max_fmt, unsigned options, pjmedia_video_format_mgr **p_mgr) { pjmedia_video_format_mgr *mgr; unsigned i; PJ_ASSERT_RETURN(pool && options==0, PJ_EINVAL); PJ_UNUSED_ARG(options); mgr = PJ_POOL_ALLOC_T(pool, pjmedia_video_format_mgr); mgr->max_info = max_fmt; mgr->info_cnt = 0; mgr->infos = pj_pool_calloc(pool, max_fmt, sizeof(pjmedia_video_format_info *)); if (video_format_mgr_instance == NULL) video_format_mgr_instance = mgr; for (i=0; iinfos[0]; n = mgr->info_cnt; for (; n > 0; ) { unsigned half = n / 2; pjmedia_video_format_info **mid = first + half; if ((*mid)->id < id) { first = ++mid; n -= half + 1; } else if ((*mid)->id==id) { return *mid; } else { n = half; } } return NULL; } PJ_DEF(pj_status_t) pjmedia_register_video_format_info(pjmedia_video_format_mgr *mgr, pjmedia_video_format_info *info) { unsigned i; if (!mgr) mgr = pjmedia_video_format_mgr_instance(); PJ_ASSERT_RETURN(mgr != NULL, PJ_EINVALIDOP); if (mgr->info_cnt >= mgr->max_info) return PJ_ETOOMANY; /* Insert to the array, sorted */ for (i=0; iinfo_cnt; ++i) { if (mgr->infos[i]->id >= info->id) break; } if (i < mgr->info_cnt) { if (mgr->infos[i]->id == info->id) { /* just overwrite */ mgr->infos[i] = info; return PJ_SUCCESS; } pj_memmove(&mgr->infos[i+1], &mgr->infos[i], (mgr->info_cnt - i) * sizeof(pjmedia_video_format_info*)); } mgr->infos[i] = info; mgr->info_cnt++; return PJ_SUCCESS; } PJ_DEF(pjmedia_video_format_mgr*) pjmedia_video_format_mgr_instance(void) { pj_assert(video_format_mgr_instance != NULL); return video_format_mgr_instance; } PJ_DEF(void) pjmedia_video_format_mgr_set_instance(pjmedia_video_format_mgr *mgr) { video_format_mgr_instance = mgr; } PJ_DEF(void) pjmedia_video_format_mgr_destroy(pjmedia_video_format_mgr *mgr) { if (!mgr) mgr = pjmedia_video_format_mgr_instance(); PJ_ASSERT_ON_FAIL(mgr != NULL, return); mgr->info_cnt = 0; if (video_format_mgr_instance == mgr) video_format_mgr_instance = NULL; } #endif /* PJMEDIA_HAS_VIDEO */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/g711.c ================================================ /* $Id: g711.c 4266 2012-09-26 05:55:18Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* This file contains file from Sun Microsystems, Inc, with the complete * notice in the second half of this file. */ #include #include #include #include #include #include #include #include #include #include #include #if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0 /* We removed PLC in 0.6 (and re-enabled it again in 0.9!) */ #define PLC_DISABLED 0 #define G711_BPS 64000 #define G711_CODEC_CNT 0 /* number of codec to preallocate in memory */ #define PTIME 10 /* basic frame size is 10 msec */ #define FRAME_SIZE (8000 * PTIME / 1000) /* 80 bytes */ #define SAMPLES_PER_FRAME (8000 * PTIME / 1000) /* 80 samples */ /* Prototypes for G711 factory */ static pj_status_t g711_test_alloc( pjmedia_codec_factory *factory, const pjmedia_codec_info *id ); static pj_status_t g711_default_attr( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ); static pj_status_t g711_enum_codecs (pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]); static pj_status_t g711_alloc_codec( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec); static pj_status_t g711_dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ); /* Prototypes for G711 implementation. */ static pj_status_t g711_init( pjmedia_codec *codec, pj_pool_t *pool ); static pj_status_t g711_open( pjmedia_codec *codec, pjmedia_codec_param *attr ); static pj_status_t g711_close( pjmedia_codec *codec ); static pj_status_t g711_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr ); static pj_status_t g711_parse(pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *timestamp, unsigned *frame_cnt, pjmedia_frame frames[]); static pj_status_t g711_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); static pj_status_t g711_decode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); #if !PLC_DISABLED static pj_status_t g711_recover( pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output); #endif /* Definition for G711 codec operations. */ static pjmedia_codec_op g711_op = { &g711_init, &g711_open, &g711_close, &g711_modify, &g711_parse, &g711_encode, &g711_decode, #if !PLC_DISABLED &g711_recover #else NULL #endif }; /* Definition for G711 codec factory operations. */ static pjmedia_codec_factory_op g711_factory_op = { &g711_test_alloc, &g711_default_attr, &g711_enum_codecs, &g711_alloc_codec, &g711_dealloc_codec, &pjmedia_codec_g711_deinit }; /* G711 factory private data */ static struct g711_factory { pjmedia_codec_factory base; pjmedia_endpt *endpt; pj_pool_t *pool; pj_mutex_t *mutex; pjmedia_codec codec_list; } g711_factory; /* G711 codec private data. */ struct g711_private { unsigned pt; #if !PLC_DISABLED pj_bool_t plc_enabled; pjmedia_plc *plc; #endif pj_bool_t vad_enabled; pjmedia_silence_det *vad; pj_timestamp last_tx; }; PJ_DEF(pj_status_t) pjmedia_codec_g711_init(pjmedia_endpt *endpt) { pjmedia_codec_mgr *codec_mgr; pj_status_t status; if (g711_factory.endpt != NULL) { /* Already initialized. */ return PJ_SUCCESS; } /* Init factory */ g711_factory.base.op = &g711_factory_op; g711_factory.base.factory_data = NULL; g711_factory.endpt = endpt; pj_list_init(&g711_factory.codec_list); /* Create pool */ g711_factory.pool = pjmedia_endpt_create_pool(endpt, "g711", 4000, 4000); if (!g711_factory.pool) return PJ_ENOMEM; /* Create mutex. */ status = pj_mutex_create_simple(g711_factory.pool, "g611", &g711_factory.mutex); if (status != PJ_SUCCESS) goto on_error; /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); if (!codec_mgr) { return PJ_EINVALIDOP; } /* Register codec factory to endpoint. */ status = pjmedia_codec_mgr_register_factory(codec_mgr, &g711_factory.base); if (status != PJ_SUCCESS) return status; return PJ_SUCCESS; on_error: if (g711_factory.mutex) { pj_mutex_destroy(g711_factory.mutex); g711_factory.mutex = NULL; } if (g711_factory.pool) { pj_pool_release(g711_factory.pool); g711_factory.pool = NULL; } return status; } PJ_DEF(pj_status_t) pjmedia_codec_g711_deinit(void) { pjmedia_codec_mgr *codec_mgr; pj_status_t status; if (g711_factory.endpt == NULL) { /* Not registered. */ return PJ_SUCCESS; } /* Lock mutex. */ pj_mutex_lock(g711_factory.mutex); /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(g711_factory.endpt); if (!codec_mgr) { g711_factory.endpt = NULL; pj_mutex_unlock(g711_factory.mutex); return PJ_EINVALIDOP; } /* Unregister G711 codec factory. */ status = pjmedia_codec_mgr_unregister_factory(codec_mgr, &g711_factory.base); g711_factory.endpt = NULL; /* Destroy mutex. */ pj_mutex_unlock(g711_factory.mutex); pj_mutex_destroy(g711_factory.mutex); g711_factory.mutex = NULL; /* Release pool. */ pj_pool_release(g711_factory.pool); g711_factory.pool = NULL; return status; } static pj_status_t g711_test_alloc(pjmedia_codec_factory *factory, const pjmedia_codec_info *id ) { PJ_UNUSED_ARG(factory); /* It's sufficient to check payload type only. */ return (id->pt==PJMEDIA_RTP_PT_PCMU || id->pt==PJMEDIA_RTP_PT_PCMA)? 0:-1; } static pj_status_t g711_default_attr (pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ) { PJ_UNUSED_ARG(factory); pj_bzero(attr, sizeof(pjmedia_codec_param)); attr->info.clock_rate = 8000; attr->info.channel_cnt = 1; attr->info.avg_bps = G711_BPS; attr->info.max_bps = G711_BPS; attr->info.pcm_bits_per_sample = 16; attr->info.frm_ptime = PTIME; attr->info.pt = (pj_uint8_t)id->pt; /* Set default frames per packet to 2 (or 20ms) */ attr->setting.frm_per_pkt = 2; #if !PLC_DISABLED /* Enable plc by default. */ attr->setting.plc = 1; #endif /* Enable VAD by default. */ attr->setting.vad = 1; /* Default all other flag bits disabled. */ return PJ_SUCCESS; } static pj_status_t g711_enum_codecs(pjmedia_codec_factory *factory, unsigned *max_count, pjmedia_codec_info codecs[]) { unsigned count = 0; PJ_UNUSED_ARG(factory); if (count < *max_count) { codecs[count].type = PJMEDIA_TYPE_AUDIO; codecs[count].pt = PJMEDIA_RTP_PT_PCMU; codecs[count].encoding_name = pj_str("PCMU"); codecs[count].clock_rate = 8000; codecs[count].channel_cnt = 1; ++count; } if (count < *max_count) { codecs[count].type = PJMEDIA_TYPE_AUDIO; codecs[count].pt = PJMEDIA_RTP_PT_PCMA; codecs[count].encoding_name = pj_str("PCMA"); codecs[count].clock_rate = 8000; codecs[count].channel_cnt = 1; ++count; } *max_count = count; return PJ_SUCCESS; } static pj_status_t g711_alloc_codec( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec) { pjmedia_codec *codec = NULL; pj_status_t status; PJ_ASSERT_RETURN(factory==&g711_factory.base, PJ_EINVAL); /* Lock mutex. */ pj_mutex_lock(g711_factory.mutex); /* Allocate new codec if no more is available */ if (pj_list_empty(&g711_factory.codec_list)) { struct g711_private *codec_priv; codec = PJ_POOL_ALLOC_T(g711_factory.pool, pjmedia_codec); codec_priv = PJ_POOL_ZALLOC_T(g711_factory.pool, struct g711_private); if (!codec || !codec_priv) { pj_mutex_unlock(g711_factory.mutex); return PJ_ENOMEM; } /* Set the payload type */ codec_priv->pt = id->pt; #if !PLC_DISABLED /* Create PLC, always with 10ms ptime */ status = pjmedia_plc_create(g711_factory.pool, 8000, SAMPLES_PER_FRAME, 0, &codec_priv->plc); if (status != PJ_SUCCESS) { pj_mutex_unlock(g711_factory.mutex); return status; } #endif /* Create VAD */ status = pjmedia_silence_det_create(g711_factory.pool, 8000, SAMPLES_PER_FRAME, &codec_priv->vad); if (status != PJ_SUCCESS) { pj_mutex_unlock(g711_factory.mutex); return status; } codec->factory = factory; codec->op = &g711_op; codec->codec_data = codec_priv; } else { codec = g711_factory.codec_list.next; pj_list_erase(codec); } /* Zero the list, for error detection in g711_dealloc_codec */ codec->next = codec->prev = NULL; *p_codec = codec; /* Unlock mutex. */ pj_mutex_unlock(g711_factory.mutex); return PJ_SUCCESS; } static pj_status_t g711_dealloc_codec(pjmedia_codec_factory *factory, pjmedia_codec *codec ) { struct g711_private *priv = (struct g711_private*) codec->codec_data; int i = 0; PJ_ASSERT_RETURN(factory==&g711_factory.base, PJ_EINVAL); /* Check that this node has not been deallocated before */ pj_assert (codec->next==NULL && codec->prev==NULL); if (codec->next!=NULL || codec->prev!=NULL) { return PJ_EINVALIDOP; } #if !PLC_DISABLED /* Clear left samples in the PLC, since codec+plc will be reused * next time. */ for (i=0; i<2; ++i) { pj_int16_t frame[SAMPLES_PER_FRAME]; pjmedia_zero_samples(frame, PJ_ARRAY_SIZE(frame)); pjmedia_plc_save(priv->plc, frame); } #else PJ_UNUSED_ARG(i); PJ_UNUSED_ARG(priv); #endif /* Lock mutex. */ pj_mutex_lock(g711_factory.mutex); /* Insert at the back of the list */ pj_list_insert_before(&g711_factory.codec_list, codec); /* Unlock mutex. */ pj_mutex_unlock(g711_factory.mutex); return PJ_SUCCESS; } static pj_status_t g711_init( pjmedia_codec *codec, pj_pool_t *pool ) { /* There's nothing to do here really */ PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(pool); return PJ_SUCCESS; } static pj_status_t g711_open(pjmedia_codec *codec, pjmedia_codec_param *attr ) { struct g711_private *priv = (struct g711_private*) codec->codec_data; priv->pt = attr->info.pt; #if !PLC_DISABLED priv->plc_enabled = (attr->setting.plc != 0); #endif priv->vad_enabled = (attr->setting.vad != 0); return PJ_SUCCESS; } static pj_status_t g711_close( pjmedia_codec *codec ) { PJ_UNUSED_ARG(codec); /* Nothing to do */ return PJ_SUCCESS; } static pj_status_t g711_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr ) { struct g711_private *priv = (struct g711_private*) codec->codec_data; if (attr->info.pt != priv->pt) return PJMEDIA_EINVALIDPT; #if !PLC_DISABLED priv->plc_enabled = (attr->setting.plc != 0); #endif priv->vad_enabled = (attr->setting.vad != 0); return PJ_SUCCESS; } static pj_status_t g711_parse( pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]) { unsigned count = 0; PJ_UNUSED_ARG(codec); PJ_ASSERT_RETURN(ts && frame_cnt && frames, PJ_EINVAL); while (pkt_size >= FRAME_SIZE && count < *frame_cnt) { frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO; frames[count].buf = pkt; frames[count].size = FRAME_SIZE; frames[count].timestamp.u64 = ts->u64 + SAMPLES_PER_FRAME * count; pkt = ((char*)pkt) + FRAME_SIZE; pkt_size -= FRAME_SIZE; ++count; } *frame_cnt = count; return PJ_SUCCESS; } static pj_status_t g711_encode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { pj_int16_t *samples = (pj_int16_t*) input->buf; struct g711_private *priv = (struct g711_private*) codec->codec_data; /* Check output buffer length */ if (output_buf_len < (input->size >> 1)) return PJMEDIA_CODEC_EFRMTOOSHORT; /* Detect silence if VAD is enabled */ if (priv->vad_enabled) { pj_bool_t is_silence; pj_int32_t silence_period; silence_period = pj_timestamp_diff32(&priv->last_tx, &input->timestamp); is_silence = pjmedia_silence_det_detect(priv->vad, (const pj_int16_t*) input->buf, (input->size >> 1), NULL); if (is_silence && (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 || silence_period < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*8000/1000)) { output->type = PJMEDIA_FRAME_TYPE_NONE; output->buf = NULL; output->size = 0; output->timestamp = input->timestamp; return PJ_SUCCESS; } else { priv->last_tx = input->timestamp; } } /* Encode */ if (priv->pt == PJMEDIA_RTP_PT_PCMA) { unsigned i, n; pj_uint8_t *dst = (pj_uint8_t*) output->buf; n = ((unsigned)input->size >> 1); for (i=0; i!=n; ++i, ++dst) { *dst = pjmedia_linear2alaw(samples[i]); } } else if (priv->pt == PJMEDIA_RTP_PT_PCMU) { unsigned i, n; pj_uint8_t *dst = (pj_uint8_t*) output->buf; n = ((unsigned)input->size >> 1); for (i=0; i!=n; ++i, ++dst) { *dst = pjmedia_linear2ulaw(samples[i]); } } else { return PJMEDIA_EINVALIDPT; } output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->size = (input->size >> 1); output->timestamp = input->timestamp; return PJ_SUCCESS; } static pj_status_t g711_decode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { struct g711_private *priv = (struct g711_private*) codec->codec_data; /* Check output buffer length */ PJ_ASSERT_RETURN(output_buf_len >= (input->size << 1), PJMEDIA_CODEC_EPCMTOOSHORT); /* Input buffer MUST have exactly 80 bytes long */ PJ_ASSERT_RETURN(input->size == FRAME_SIZE, PJMEDIA_CODEC_EFRMINLEN); /* Decode */ if (priv->pt == PJMEDIA_RTP_PT_PCMA) { unsigned i; pj_uint8_t *src = (pj_uint8_t*) input->buf; pj_uint16_t *dst = (pj_uint16_t*) output->buf; for (i=0; i!=input->size; ++i) { *dst++ = (pj_uint16_t) pjmedia_alaw2linear(*src++); } } else if (priv->pt == PJMEDIA_RTP_PT_PCMU) { unsigned i; pj_uint8_t *src = (pj_uint8_t*) input->buf; pj_uint16_t *dst = (pj_uint16_t*) output->buf; for (i=0; i!=input->size; ++i) { *dst++ = (pj_uint16_t) pjmedia_ulaw2linear(*src++); } } else { return PJMEDIA_EINVALIDPT; } output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->size = (input->size << 1); output->timestamp = input->timestamp; #if !PLC_DISABLED if (priv->plc_enabled) pjmedia_plc_save( priv->plc, (pj_int16_t*)output->buf); #endif return PJ_SUCCESS; } #if !PLC_DISABLED static pj_status_t g711_recover( pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output) { struct g711_private *priv = (struct g711_private*) codec->codec_data; if (!priv->plc_enabled) return PJ_EINVALIDOP; PJ_ASSERT_RETURN(output_buf_len >= SAMPLES_PER_FRAME * 2, PJMEDIA_CODEC_EPCMTOOSHORT); pjmedia_plc_generate(priv->plc, (pj_int16_t*)output->buf); output->size = SAMPLES_PER_FRAME * 2; return PJ_SUCCESS; } #endif #endif /* PJMEDIA_HAS_G711_CODEC */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/jbuf.c ================================================ /* $Id: jbuf.c 4369 2013-02-21 21:55:54Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Based on implementation kindly contributed by Switchlab, Ltd. */ #include #include #include #include #include #include #include #define THIS_FILE "jbuf.c" /* Invalid sequence number, used as the initial value. */ #define INVALID_OFFSET -9999 /* Maximum burst length, whenever an operation is bursting longer than * this value, JB will assume that the opposite operation was idle. */ #define MAX_BURST_MSEC 1000 /* Number of OP switches to be performed in JB_STATUS_INITIALIZING, before * JB can switch its states to JB_STATUS_PROCESSING. */ #define INIT_CYCLE 10 /* Minimal difference between JB size and 2*burst-level to perform * JB shrinking in static discard algorithm. */ #define STA_DISC_SAFE_SHRINKING_DIFF 1 /* Struct of JB internal buffer, represented in a circular buffer containing * frame content, frame type, frame length, and frame bit info. */ typedef struct jb_framelist_t { /* Settings */ unsigned frame_size; /**< maximum size of frame */ unsigned max_count; /**< maximum number of frames */ /* Buffers */ char *content; /**< frame content array */ int *frame_type; /**< frame type array */ pj_size_t *content_len; /**< frame length array */ pj_uint32_t *bit_info; /**< frame bit info array */ pj_uint32_t *ts; /**< timestamp array */ /* States */ unsigned head; /**< index of head, pointed frame will be returned by next GET */ unsigned size; /**< current size of framelist, including discarded frames. */ unsigned discarded_num; /**< current number of discarded frames. */ int origin; /**< original index of flist_head */ } jb_framelist_t; typedef void (*discard_algo)(pjmedia_jbuf *jb); static void jbuf_discard_static(pjmedia_jbuf *jb); static void jbuf_discard_progressive(pjmedia_jbuf *jb); struct pjmedia_jbuf { /* Settings (consts) */ pj_str_t jb_name; /**< jitter buffer name */ pj_size_t jb_frame_size; /**< frame size */ unsigned jb_frame_ptime; /**< frame duration. */ pj_size_t jb_max_count; /**< capacity of jitter buffer, in frames */ int jb_init_prefetch; /**< Initial prefetch */ int jb_min_prefetch; /**< Minimum allowable prefetch */ int jb_max_prefetch; /**< Maximum allowable prefetch */ int jb_max_burst; /**< maximum possible burst, whenever burst exceeds this value, it won't be included in level calculation */ int jb_min_shrink_gap; /**< How often can we shrink */ discard_algo jb_discard_algo; /**< Discard algorithm */ /* Buffer */ jb_framelist_t jb_framelist; /**< the buffer */ /* States */ int jb_level; /**< delay between source & destination (calculated according of the number of burst get/put operations) */ int jb_max_hist_level; /**< max level during the last level calculations */ int jb_stable_hist; /**< num of times the delay has been lower then the prefetch num */ int jb_last_op; /**< last operation executed (put/get) */ int jb_eff_level; /**< effective burst level */ int jb_prefetch; /**< no. of frame to insert before removing some (at the beginning of the framelist->content operation), the value may be continuously updated based on current frame burst level. */ pj_bool_t jb_prefetching; /**< flag if jbuf is prefetching. */ int jb_status; /**< status is 'init' until the first 'put' operation */ int jb_init_cycle_cnt; /**< status is 'init' until the first 'put' operation */ int jb_discard_ref; /**< Seq # of last frame deleted or discarded */ unsigned jb_discard_dist; /**< Distance from jb_discard_ref to perform discard (in frm) */ /* Statistics */ pj_math_stat jb_delay; /**< Delay statistics of jitter buffer (in ms) */ pj_math_stat jb_burst; /**< Burst statistics (in frames) */ unsigned jb_lost; /**< Number of lost frames. */ unsigned jb_discard; /**< Number of discarded frames. */ unsigned jb_empty; /**< Number of empty/prefetching frame returned by GET. */ }; #define JB_STATUS_INITIALIZING 0 #define JB_STATUS_PROCESSING 1 /* Progressive discard algorithm introduced to reduce JB latency * by discarding incoming frames with adaptive aggressiveness based on * actual burst level. */ #define PROGRESSIVE_DISCARD 1 /* Internal JB frame flag, discarded frame will not be returned by JB to * application, it's just simply discarded. */ #define PJMEDIA_JB_DISCARDED_FRAME 1024 /* Enabling this would log the jitter buffer state about once per * second. */ #if 0 # define TRACE__(args) PJ_LOG(5,args) #else # define TRACE__(args) #endif static pj_status_t jb_framelist_reset(jb_framelist_t *framelist); static unsigned jb_framelist_remove_head(jb_framelist_t *framelist, unsigned count); static pj_status_t jb_framelist_init( pj_pool_t *pool, jb_framelist_t *framelist, unsigned frame_size, unsigned max_count) { PJ_ASSERT_RETURN(pool && framelist, PJ_EINVAL); pj_bzero(framelist, sizeof(jb_framelist_t)); framelist->frame_size = frame_size; framelist->max_count = max_count; framelist->content = (char*) pj_pool_alloc(pool, framelist->frame_size* framelist->max_count); framelist->frame_type = (int*) pj_pool_alloc(pool, sizeof(framelist->frame_type[0])* framelist->max_count); framelist->content_len = (pj_size_t*) pj_pool_alloc(pool, sizeof(framelist->content_len[0])* framelist->max_count); framelist->bit_info = (pj_uint32_t*) pj_pool_alloc(pool, sizeof(framelist->bit_info[0])* framelist->max_count); framelist->ts = (pj_uint32_t*) pj_pool_alloc(pool, sizeof(framelist->ts[0])* framelist->max_count); return jb_framelist_reset(framelist); } static pj_status_t jb_framelist_destroy(jb_framelist_t *framelist) { PJ_UNUSED_ARG(framelist); return PJ_SUCCESS; } static pj_status_t jb_framelist_reset(jb_framelist_t *framelist) { framelist->head = 0; framelist->origin = INVALID_OFFSET; framelist->size = 0; framelist->discarded_num = 0; //pj_bzero(framelist->content, // framelist->frame_size * // framelist->max_count); pj_memset(framelist->frame_type, PJMEDIA_JB_MISSING_FRAME, sizeof(framelist->frame_type[0]) * framelist->max_count); pj_bzero(framelist->content_len, sizeof(framelist->content_len[0]) * framelist->max_count); //pj_bzero(framelist->bit_info, // sizeof(framelist->bit_info[0]) * // framelist->max_count); return PJ_SUCCESS; } static unsigned jb_framelist_size(const jb_framelist_t *framelist) { return framelist->size; } static unsigned jb_framelist_eff_size(const jb_framelist_t *framelist) { return (framelist->size - framelist->discarded_num); } static int jb_framelist_origin(const jb_framelist_t *framelist) { return framelist->origin; } static pj_bool_t jb_framelist_get(jb_framelist_t *framelist, void *frame, pj_size_t *size, pjmedia_jb_frame_type *p_type, pj_uint32_t *bit_info, pj_uint32_t *ts, int *seq) { if (framelist->size) { pj_bool_t prev_discarded = PJ_FALSE; /* Skip discarded frames */ while (framelist->frame_type[framelist->head] == PJMEDIA_JB_DISCARDED_FRAME) { jb_framelist_remove_head(framelist, 1); prev_discarded = PJ_TRUE; } /* Return the head frame if any */ if (framelist->size) { if (prev_discarded) { /* Ticket #1188: when previous frame(s) was discarded, return * 'missing' frame to trigger PLC to get smoother signal. */ *p_type = PJMEDIA_JB_MISSING_FRAME; if (size) *size = 0; if (bit_info) *bit_info = 0; } else { pj_memcpy(frame, framelist->content + framelist->head * framelist->frame_size, framelist->frame_size); *p_type = (pjmedia_jb_frame_type) framelist->frame_type[framelist->head]; if (size) *size = framelist->content_len[framelist->head]; if (bit_info) *bit_info = framelist->bit_info[framelist->head]; } if (ts) *ts = framelist->ts[framelist->head]; if (seq) *seq = framelist->origin; //pj_bzero(framelist->content + // framelist->head * framelist->frame_size, // framelist->frame_size); framelist->frame_type[framelist->head] = PJMEDIA_JB_MISSING_FRAME; framelist->content_len[framelist->head] = 0; framelist->bit_info[framelist->head] = 0; framelist->ts[framelist->head] = 0; framelist->origin++; framelist->head = (framelist->head + 1) % framelist->max_count; framelist->size--; return PJ_TRUE; } } /* No frame available */ pj_bzero(frame, framelist->frame_size); return PJ_FALSE; } static pj_bool_t jb_framelist_peek(jb_framelist_t *framelist, unsigned offset, const void **frame, pj_size_t *size, pjmedia_jb_frame_type *type, pj_uint32_t *bit_info, pj_uint32_t *ts, int *seq) { unsigned pos, idx; if (offset >= jb_framelist_eff_size(framelist)) return PJ_FALSE; pos = framelist->head; idx = offset; /* Find actual peek position, note there may be discarded frames */ while (1) { if (framelist->frame_type[pos] != PJMEDIA_JB_DISCARDED_FRAME) { if (idx == 0) break; else --idx; } pos = (pos + 1) % framelist->max_count; } /* Return the frame pointer */ if (frame) *frame = framelist->content + pos*framelist->frame_size; if (type) *type = (pjmedia_jb_frame_type) framelist->frame_type[pos]; if (size) *size = framelist->content_len[pos]; if (bit_info) *bit_info = framelist->bit_info[pos]; if (ts) *ts = framelist->ts[pos]; if (seq) *seq = framelist->origin + offset; return PJ_TRUE; } /* Remove oldest frames as many as param 'count' */ static unsigned jb_framelist_remove_head(jb_framelist_t *framelist, unsigned count) { if (count > framelist->size) count = framelist->size; if (count) { /* may be done in two steps if overlapping */ unsigned step1,step2; unsigned tmp = framelist->head+count; unsigned i; if (tmp > framelist->max_count) { step1 = framelist->max_count - framelist->head; step2 = count-step1; } else { step1 = count; step2 = 0; } for (i = framelist->head; i < (framelist->head + step1); ++i) { if (framelist->frame_type[i] == PJMEDIA_JB_DISCARDED_FRAME) { pj_assert(framelist->discarded_num > 0); framelist->discarded_num--; } } //pj_bzero(framelist->content + // framelist->head * framelist->frame_size, // step1*framelist->frame_size); pj_memset(framelist->frame_type+framelist->head, PJMEDIA_JB_MISSING_FRAME, step1*sizeof(framelist->frame_type[0])); pj_bzero(framelist->content_len+framelist->head, step1*sizeof(framelist->content_len[0])); if (step2) { for (i = 0; i < step2; ++i) { if (framelist->frame_type[i] == PJMEDIA_JB_DISCARDED_FRAME) { pj_assert(framelist->discarded_num > 0); framelist->discarded_num--; } } //pj_bzero( framelist->content, // step2*framelist->frame_size); pj_memset(framelist->frame_type, PJMEDIA_JB_MISSING_FRAME, step2*sizeof(framelist->frame_type[0])); pj_bzero (framelist->content_len, step2*sizeof(framelist->content_len[0])); } /* update states */ framelist->origin += count; framelist->head = (framelist->head + count) % framelist->max_count; framelist->size -= count; } return count; } static pj_status_t jb_framelist_put_at(jb_framelist_t *framelist, int index, const void *frame, unsigned frame_size, pj_uint32_t bit_info, pj_uint32_t ts, unsigned frame_type) { int distance; unsigned pos; enum { MAX_MISORDER = 100 }; enum { MAX_DROPOUT = 3000 }; PJ_ASSERT_RETURN(frame_size <= framelist->frame_size, PJ_EINVAL); /* too late or sequence restart */ if (index < framelist->origin) { if (framelist->origin - index < MAX_MISORDER) { /* too late */ return PJ_ETOOSMALL; } else { /* sequence restart */ framelist->origin = index - framelist->size; } } /* if jbuf is empty, just reset the origin */ if (framelist->size == 0) { pj_assert(framelist->discarded_num == 0); framelist->origin = index; } /* get distance of this frame to the first frame in the buffer */ distance = index - framelist->origin; /* far jump, the distance is greater than buffer capacity */ if (distance >= (int)framelist->max_count) { if (distance > MAX_DROPOUT) { /* jump too far, reset the buffer */ jb_framelist_reset(framelist); framelist->origin = index; distance = 0; } else { /* otherwise, reject the frame */ return PJ_ETOOMANY; } } /* get the slot position */ pos = (framelist->head + distance) % framelist->max_count; /* if the slot is occupied, it must be duplicated frame, ignore it. */ if (framelist->frame_type[pos] != PJMEDIA_JB_MISSING_FRAME) return PJ_EEXISTS; /* put the frame into the slot */ framelist->frame_type[pos] = frame_type; framelist->content_len[pos] = frame_size; framelist->bit_info[pos] = bit_info; framelist->ts[pos] = ts; /* update framelist size */ if (framelist->origin + (int)framelist->size <= index) framelist->size = distance + 1; if(PJMEDIA_JB_NORMAL_FRAME == frame_type) { /* copy frame content */ pj_memcpy(framelist->content + pos * framelist->frame_size, frame, frame_size); } return PJ_SUCCESS; } static pj_status_t jb_framelist_discard(jb_framelist_t *framelist, int index) { unsigned pos; PJ_ASSERT_RETURN(index >= framelist->origin && index < framelist->origin + (int)framelist->size, PJ_EINVAL); /* Get the slot position */ pos = (framelist->head + (index - framelist->origin)) % framelist->max_count; /* Discard the frame */ framelist->frame_type[pos] = PJMEDIA_JB_DISCARDED_FRAME; framelist->discarded_num++; return PJ_SUCCESS; } enum pjmedia_jb_op { JB_OP_INIT = -1, JB_OP_PUT = 1, JB_OP_GET = 2 }; PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, const pj_str_t *name, unsigned frame_size, unsigned ptime, unsigned max_count, pjmedia_jbuf **p_jb) { pjmedia_jbuf *jb; pj_status_t status; jb = PJ_POOL_ZALLOC_T(pool, pjmedia_jbuf); status = jb_framelist_init(pool, &jb->jb_framelist, frame_size, max_count); if (status != PJ_SUCCESS) return status; pj_strdup_with_null(pool, &jb->jb_name, name); jb->jb_frame_size = frame_size; jb->jb_frame_ptime = ptime; jb->jb_prefetch = PJ_MIN(PJMEDIA_JB_DEFAULT_INIT_DELAY,max_count*4/5); jb->jb_min_prefetch = 0; jb->jb_max_prefetch = max_count*4/5; jb->jb_max_count = max_count; jb->jb_min_shrink_gap= PJMEDIA_JBUF_DISC_MIN_GAP / ptime; jb->jb_max_burst = PJ_MAX(MAX_BURST_MSEC / ptime, max_count*3/4); pj_math_stat_init(&jb->jb_delay); pj_math_stat_init(&jb->jb_burst); pjmedia_jbuf_set_discard(jb, PJMEDIA_JB_DISCARD_PROGRESSIVE); pjmedia_jbuf_reset(jb); *p_jb = jb; return PJ_SUCCESS; } /* * Set the jitter buffer to fixed delay mode. The default behavior * is to adapt the delay with actual packet delay. * */ PJ_DEF(pj_status_t) pjmedia_jbuf_set_fixed( pjmedia_jbuf *jb, unsigned prefetch) { PJ_ASSERT_RETURN(jb, PJ_EINVAL); PJ_ASSERT_RETURN(prefetch <= jb->jb_max_count, PJ_EINVAL); jb->jb_min_prefetch = jb->jb_max_prefetch = jb->jb_prefetch = jb->jb_init_prefetch = prefetch; pjmedia_jbuf_set_discard(jb, PJMEDIA_JB_DISCARD_NONE); return PJ_SUCCESS; } /* * Set the jitter buffer to adaptive mode. */ PJ_DEF(pj_status_t) pjmedia_jbuf_set_adaptive( pjmedia_jbuf *jb, unsigned prefetch, unsigned min_prefetch, unsigned max_prefetch) { PJ_ASSERT_RETURN(jb, PJ_EINVAL); PJ_ASSERT_RETURN(min_prefetch <= max_prefetch && prefetch <= max_prefetch && max_prefetch <= jb->jb_max_count, PJ_EINVAL); jb->jb_prefetch = jb->jb_init_prefetch = prefetch; jb->jb_min_prefetch = min_prefetch; jb->jb_max_prefetch = max_prefetch; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_jbuf_set_discard( pjmedia_jbuf *jb, pjmedia_jb_discard_algo algo) { PJ_ASSERT_RETURN(jb, PJ_EINVAL); PJ_ASSERT_RETURN(algo >= PJMEDIA_JB_DISCARD_NONE && algo <= PJMEDIA_JB_DISCARD_PROGRESSIVE, PJ_EINVAL); switch(algo) { case PJMEDIA_JB_DISCARD_PROGRESSIVE: jb->jb_discard_algo = &jbuf_discard_progressive; break; case PJMEDIA_JB_DISCARD_STATIC: jb->jb_discard_algo = &jbuf_discard_static; break; default: jb->jb_discard_algo = NULL; break; } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_jbuf_reset(pjmedia_jbuf *jb) { jb->jb_level = 0; jb->jb_last_op = JB_OP_INIT; jb->jb_stable_hist = 0; jb->jb_status = JB_STATUS_INITIALIZING; jb->jb_init_cycle_cnt= 0; jb->jb_max_hist_level= 0; jb->jb_prefetching = (jb->jb_prefetch != 0); jb->jb_discard_dist = 0; jb_framelist_reset(&jb->jb_framelist); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_jbuf_destroy(pjmedia_jbuf *jb) { PJ_LOG(5, (jb->jb_name.ptr, "" "JB summary:\n" " size=%d/eff=%d prefetch=%d level=%d\n" " delay (min/max/avg/dev)=%d/%d/%d/%d ms\n" " burst (min/max/avg/dev)=%d/%d/%d/%d frames\n" " lost=%d discard=%d empty=%d", jb_framelist_size(&jb->jb_framelist), jb_framelist_eff_size(&jb->jb_framelist), jb->jb_prefetch, jb->jb_eff_level, jb->jb_delay.min, jb->jb_delay.max, jb->jb_delay.mean, pj_math_stat_get_stddev(&jb->jb_delay), jb->jb_burst.min, jb->jb_burst.max, jb->jb_burst.mean, pj_math_stat_get_stddev(&jb->jb_burst), jb->jb_lost, jb->jb_discard, jb->jb_empty)); return jb_framelist_destroy(&jb->jb_framelist); } PJ_DEF(pj_bool_t) pjmedia_jbuf_is_full(const pjmedia_jbuf *jb) { return jb->jb_framelist.size == jb->jb_framelist.max_count; } static void jbuf_calculate_jitter(pjmedia_jbuf *jb) { int diff, cur_size; cur_size = jb_framelist_eff_size(&jb->jb_framelist); pj_math_stat_update(&jb->jb_burst, jb->jb_level); jb->jb_max_hist_level = PJ_MAX(jb->jb_max_hist_level, jb->jb_level); /* Burst level is decreasing */ if (jb->jb_level < jb->jb_eff_level) { enum { STABLE_HISTORY_LIMIT = 20 }; jb->jb_stable_hist++; /* Only update the effective level (and prefetch) if 'stable' * condition is reached (not just short time impulse) */ if (jb->jb_stable_hist > STABLE_HISTORY_LIMIT) { diff = (jb->jb_eff_level - jb->jb_max_hist_level) / 3; if (diff < 1) diff = 1; /* Update effective burst level */ jb->jb_eff_level -= diff; /* Update prefetch based on level */ if (jb->jb_init_prefetch) { jb->jb_prefetch = jb->jb_eff_level; if (jb->jb_prefetch < jb->jb_min_prefetch) jb->jb_prefetch = jb->jb_min_prefetch; if (jb->jb_prefetch > jb->jb_max_prefetch) jb->jb_prefetch = jb->jb_max_prefetch; } /* Reset history */ jb->jb_max_hist_level = 0; jb->jb_stable_hist = 0; TRACE__((jb->jb_name.ptr,"jb updated(1), lvl=%d pre=%d, size=%d", jb->jb_eff_level, jb->jb_prefetch, cur_size)); PJ_UNUSED_ARG(cur_size); /* Warning about unused var */ } } /* Burst level is increasing */ else if (jb->jb_level > jb->jb_eff_level) { /* Instaneous set effective burst level to recent maximum level */ jb->jb_eff_level = PJ_MIN(jb->jb_max_hist_level, (int)(jb->jb_max_count*4/5)); /* Update prefetch based on level */ if (jb->jb_init_prefetch) { jb->jb_prefetch = jb->jb_eff_level; if (jb->jb_prefetch > jb->jb_max_prefetch) jb->jb_prefetch = jb->jb_max_prefetch; if (jb->jb_prefetch < jb->jb_min_prefetch) jb->jb_prefetch = jb->jb_min_prefetch; } jb->jb_stable_hist = 0; /* Do not reset max_hist_level. */ //jb->jb_max_hist_level = 0; TRACE__((jb->jb_name.ptr,"jb updated(2), lvl=%d pre=%d, size=%d", jb->jb_eff_level, jb->jb_prefetch, cur_size)); } /* Level is unchanged */ else { jb->jb_stable_hist = 0; } } static void jbuf_discard_static(pjmedia_jbuf *jb) { /* These code is used for shortening the delay in the jitter buffer. * It needs shrink only when there is possibility of drift. Drift * detection is performed by inspecting the jitter buffer size, if * its size is twice of current burst level, there can be drift. * * Moreover, normally drift level is quite low, so JB shouldn't need * to shrink aggresively, it will shrink maximum one frame per * PJMEDIA_JBUF_DISC_MIN_GAP ms. Theoritically, JB may handle drift level * as much as = FRAME_PTIME/PJMEDIA_JBUF_DISC_MIN_GAP * 100% * * Whenever there is drift, where PUT > GET, this method will keep * the latency (JB size) as much as twice of burst level. */ /* Shrinking due of drift will be implicitly done by progressive discard, * so just disable it when progressive discard is active. */ int diff, burst_level; burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level); diff = jb_framelist_eff_size(&jb->jb_framelist) - burst_level*2; if (diff >= STA_DISC_SAFE_SHRINKING_DIFF) { int seq_origin; /* Check and adjust jb_discard_ref, in case there was * seq restart */ seq_origin = jb_framelist_origin(&jb->jb_framelist); if (seq_origin < jb->jb_discard_ref) jb->jb_discard_ref = seq_origin; if (seq_origin - jb->jb_discard_ref >= jb->jb_min_shrink_gap) { /* Shrink slowly, one frame per cycle */ diff = 1; /* Drop frame(s)! */ diff = jb_framelist_remove_head(&jb->jb_framelist, diff); jb->jb_discard_ref = jb_framelist_origin(&jb->jb_framelist); jb->jb_discard += diff; TRACE__((jb->jb_name.ptr, "JB shrinking %d frame(s), cur size=%d", diff, jb_framelist_eff_size(&jb->jb_framelist))); } } } static void jbuf_discard_progressive(pjmedia_jbuf *jb) { unsigned cur_size, burst_level, overflow, T, discard_dist; int last_seq; /* Should be done in PUT operation */ if (jb->jb_last_op != JB_OP_PUT) return; /* Check if latency is longer than burst */ cur_size = jb_framelist_eff_size(&jb->jb_framelist); burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level); if (cur_size <= burst_level) { /* Reset any scheduled discard */ jb->jb_discard_dist = 0; return; } /* Estimate discard duration needed for adjusting latency */ if (burst_level <= PJMEDIA_JBUF_PRO_DISC_MIN_BURST) T = PJMEDIA_JBUF_PRO_DISC_T1; else if (burst_level >= PJMEDIA_JBUF_PRO_DISC_MAX_BURST) T = PJMEDIA_JBUF_PRO_DISC_T2; else T = PJMEDIA_JBUF_PRO_DISC_T1 + (PJMEDIA_JBUF_PRO_DISC_T2 - PJMEDIA_JBUF_PRO_DISC_T1) * (burst_level - PJMEDIA_JBUF_PRO_DISC_MIN_BURST) / (PJMEDIA_JBUF_PRO_DISC_MAX_BURST-PJMEDIA_JBUF_PRO_DISC_MIN_BURST); /* Calculate current discard distance */ overflow = cur_size - burst_level; discard_dist = T / overflow / jb->jb_frame_ptime; /* Get last seq number in the JB */ last_seq = jb_framelist_origin(&jb->jb_framelist) + jb_framelist_size(&jb->jb_framelist) - 1; /* Setup new discard schedule if none, otherwise, update the existing * discard schedule (can be delayed or accelerated). */ if (jb->jb_discard_dist == 0) { /* Setup new discard schedule */ jb->jb_discard_ref = last_seq; } else if (last_seq < jb->jb_discard_ref) { /* Seq restarted, update discard reference */ jb->jb_discard_ref = last_seq; } jb->jb_discard_dist = PJ_MAX(jb->jb_min_shrink_gap, (int)discard_dist); /* Check if we need to discard now */ if (last_seq >= (jb->jb_discard_ref + (int)jb->jb_discard_dist)) { int discard_seq; discard_seq = jb->jb_discard_ref + jb->jb_discard_dist; if (discard_seq < jb_framelist_origin(&jb->jb_framelist)) discard_seq = jb_framelist_origin(&jb->jb_framelist); jb_framelist_discard(&jb->jb_framelist, discard_seq); TRACE__((jb->jb_name.ptr, "Discard #%d: ref=#%d dist=%d orig=%d size=%d/%d " "burst=%d/%d", discard_seq, jb->jb_discard_ref, jb->jb_discard_dist, jb_framelist_origin(&jb->jb_framelist), cur_size, jb_framelist_size(&jb->jb_framelist), jb->jb_eff_level, burst_level)); /* Update discard reference */ jb->jb_discard_ref = discard_seq; } } PJ_INLINE(void) jbuf_update(pjmedia_jbuf *jb, int oper) { if(jb->jb_last_op != oper) { jb->jb_last_op = oper; if (jb->jb_status == JB_STATUS_INITIALIZING) { /* Switch status 'initializing' -> 'processing' after some OP * switch cycles and current OP is GET (burst level is calculated * based on PUT burst), so burst calculation is guaranted to be * performed right after the status switching. */ if (++jb->jb_init_cycle_cnt >= INIT_CYCLE && oper == JB_OP_GET) { jb->jb_status = JB_STATUS_PROCESSING; /* To make sure the burst calculation will be done right after * this, adjust burst level if it exceeds max burst level. */ jb->jb_level = PJ_MIN(jb->jb_level, jb->jb_max_burst); } else { jb->jb_level = 0; return; } } /* Perform jitter calculation based on PUT burst-level only, since * GET burst-level may not be accurate, e.g: when VAD is active. * Note that when burst-level is too big, i.e: exceeds jb_max_burst, * the GET op may be idle, in this case, we better skip the jitter * calculation. */ if (oper == JB_OP_GET && jb->jb_level <= jb->jb_max_burst) jbuf_calculate_jitter(jb); jb->jb_level = 0; } /* Call discard algorithm */ if (jb->jb_status == JB_STATUS_PROCESSING && jb->jb_discard_algo) { (*jb->jb_discard_algo)(jb); } } PJ_DEF(void) pjmedia_jbuf_put_frame( pjmedia_jbuf *jb, const void *frame, pj_size_t frame_size, int frame_seq) { pjmedia_jbuf_put_frame3(jb, frame, frame_size, 0, frame_seq, 0, NULL); } PJ_DEF(void) pjmedia_jbuf_put_frame2(pjmedia_jbuf *jb, const void *frame, pj_size_t frame_size, pj_uint32_t bit_info, int frame_seq, pj_bool_t *discarded) { pjmedia_jbuf_put_frame3(jb, frame, frame_size, bit_info, frame_seq, 0, discarded); } PJ_DEF(void) pjmedia_jbuf_put_frame3(pjmedia_jbuf *jb, const void *frame, pj_size_t frame_size, pj_uint32_t bit_info, int frame_seq, pj_uint32_t ts, pj_bool_t *discarded) { pj_size_t min_frame_size; int new_size, cur_size; pj_status_t status; cur_size = jb_framelist_eff_size(&jb->jb_framelist); /* Attempt to store the frame */ min_frame_size = PJ_MIN(frame_size, jb->jb_frame_size); status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame, (unsigned)min_frame_size, bit_info, ts, PJMEDIA_JB_NORMAL_FRAME); /* Jitter buffer is full, remove some older frames */ while (status == PJ_ETOOMANY) { int distance; unsigned removed; /* Remove as few as possible just to make this frame in. Note that * the cases of seq-jump, out-of-order, and seq restart should have * been handled/normalized by previous call of jb_framelist_put_at(). * So we're confident about 'distance' value here. */ distance = (frame_seq - jb_framelist_origin(&jb->jb_framelist)) - (int)jb->jb_max_count + 1; pj_assert(distance > 0); removed = jb_framelist_remove_head(&jb->jb_framelist, distance); status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame, (unsigned)min_frame_size, bit_info, ts, PJMEDIA_JB_NORMAL_FRAME); jb->jb_discard += removed; } /* Get new JB size after PUT */ new_size = jb_framelist_eff_size(&jb->jb_framelist); /* Return the flag if this frame is discarded */ if (discarded) *discarded = (status != PJ_SUCCESS); if (status == PJ_SUCCESS) { if (jb->jb_prefetching) { TRACE__((jb->jb_name.ptr, "PUT prefetch_cnt=%d/%d", new_size, jb->jb_prefetch)); if (new_size >= jb->jb_prefetch) jb->jb_prefetching = PJ_FALSE; } jb->jb_level += (new_size > cur_size ? new_size-cur_size : 1); jbuf_update(jb, JB_OP_PUT); } else jb->jb_discard++; } /* * Get frame from jitter buffer. */ PJ_DEF(void) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb, void *frame, char *p_frame_type) { pjmedia_jbuf_get_frame3(jb, frame, NULL, p_frame_type, NULL, NULL, NULL); } /* * Get frame from jitter buffer. */ PJ_DEF(void) pjmedia_jbuf_get_frame2(pjmedia_jbuf *jb, void *frame, pj_size_t *size, char *p_frame_type, pj_uint32_t *bit_info) { pjmedia_jbuf_get_frame3(jb, frame, size, p_frame_type, bit_info, NULL, NULL); } /* * Get frame from jitter buffer. */ PJ_DEF(void) pjmedia_jbuf_get_frame3(pjmedia_jbuf *jb, void *frame, pj_size_t *size, char *p_frame_type, pj_uint32_t *bit_info, pj_uint32_t *ts, int *seq) { if (jb->jb_prefetching) { /* Can't return frame because jitter buffer is filling up * minimum prefetch. */ //pj_bzero(frame, jb->jb_frame_size); *p_frame_type = PJMEDIA_JB_ZERO_PREFETCH_FRAME; if (size) *size = 0; TRACE__((jb->jb_name.ptr, "GET prefetch_cnt=%d/%d", jb_framelist_eff_size(&jb->jb_framelist), jb->jb_prefetch)); jb->jb_empty++; } else { pjmedia_jb_frame_type ftype = PJMEDIA_JB_NORMAL_FRAME; pj_bool_t res; /* Try to retrieve a frame from frame list */ res = jb_framelist_get(&jb->jb_framelist, frame, size, &ftype, bit_info, ts, seq); if (res) { /* We've successfully retrieved a frame from the frame list, but * the frame could be a blank frame! */ if (ftype == PJMEDIA_JB_NORMAL_FRAME) { *p_frame_type = PJMEDIA_JB_NORMAL_FRAME; } else { *p_frame_type = PJMEDIA_JB_MISSING_FRAME; jb->jb_lost++; } /* Store delay history at the first GET */ if (jb->jb_last_op == JB_OP_PUT) { unsigned cur_size; /* We've just retrieved one frame, so add one to cur_size */ cur_size = jb_framelist_eff_size(&jb->jb_framelist) + 1; pj_math_stat_update(&jb->jb_delay, cur_size*jb->jb_frame_ptime); } } else { /* Jitter buffer is empty */ if (jb->jb_prefetch) jb->jb_prefetching = PJ_TRUE; //pj_bzero(frame, jb->jb_frame_size); *p_frame_type = PJMEDIA_JB_ZERO_EMPTY_FRAME; if (size) *size = 0; jb->jb_empty++; } } jb->jb_level++; jbuf_update(jb, JB_OP_GET); } /* * Get jitter buffer state. */ PJ_DEF(pj_status_t) pjmedia_jbuf_get_state( const pjmedia_jbuf *jb, pjmedia_jb_state *state ) { PJ_ASSERT_RETURN(jb && state, PJ_EINVAL); state->frame_size = (unsigned)jb->jb_frame_size; state->min_prefetch = jb->jb_min_prefetch; state->max_prefetch = jb->jb_max_prefetch; state->burst = jb->jb_eff_level; state->prefetch = jb->jb_prefetch; state->size = jb_framelist_eff_size(&jb->jb_framelist); state->avg_delay = jb->jb_delay.mean; state->min_delay = jb->jb_delay.min; state->max_delay = jb->jb_delay.max; state->dev_delay = pj_math_stat_get_stddev(&jb->jb_delay); state->avg_burst = jb->jb_burst.mean; state->empty = jb->jb_empty; state->discard = jb->jb_discard; state->lost = jb->jb_lost; return PJ_SUCCESS; } PJ_DEF(void) pjmedia_jbuf_peek_frame( pjmedia_jbuf *jb, unsigned offset, const void **frame, pj_size_t *size, char *p_frm_type, pj_uint32_t *bit_info, pj_uint32_t *ts, int *seq) { pjmedia_jb_frame_type ftype; pj_bool_t res; res = jb_framelist_peek(&jb->jb_framelist, offset, frame, size, &ftype, bit_info, ts, seq); if (!res) *p_frm_type = PJMEDIA_JB_ZERO_EMPTY_FRAME; else if (ftype == PJMEDIA_JB_NORMAL_FRAME) *p_frm_type = PJMEDIA_JB_NORMAL_FRAME; else *p_frm_type = PJMEDIA_JB_MISSING_FRAME; } PJ_DEF(unsigned) pjmedia_jbuf_remove_frame(pjmedia_jbuf *jb, unsigned frame_cnt) { unsigned count, last_discard_num; last_discard_num = jb->jb_framelist.discarded_num; count = jb_framelist_remove_head(&jb->jb_framelist, frame_cnt); /* Remove some more when there were discarded frames included */ while (jb->jb_framelist.discarded_num < last_discard_num) { /* Calculate frames count to be removed next */ frame_cnt = last_discard_num - jb->jb_framelist.discarded_num; /* Normalize non-discarded frames count just been removed */ count -= frame_cnt; /* Remove more frames */ last_discard_num = jb->jb_framelist.discarded_num; count += jb_framelist_remove_head(&jb->jb_framelist, frame_cnt); } return count; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/master_port.c ================================================ /* $Id: master_port.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include struct pjmedia_master_port { unsigned options; pjmedia_clock *clock; pjmedia_port *u_port; pjmedia_port *d_port; unsigned buff_size; void *buff; pj_lock_t *lock; }; static void clock_callback(const pj_timestamp *ts, void *user_data); /* * Create a master port. * */ PJ_DEF(pj_status_t) pjmedia_master_port_create( pj_pool_t *pool, pjmedia_port *u_port, pjmedia_port *d_port, unsigned options, pjmedia_master_port **p_m) { pjmedia_master_port *m; unsigned clock_rate; unsigned channel_count; unsigned samples_per_frame; unsigned bytes_per_frame; pjmedia_audio_format_detail *u_afd, *d_afd; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(pool && u_port && d_port && p_m, PJ_EINVAL); u_afd = pjmedia_format_get_audio_format_detail(&u_port->info.fmt, PJ_TRUE); d_afd = pjmedia_format_get_audio_format_detail(&d_port->info.fmt, PJ_TRUE); /* Both ports MUST have equal clock rate */ PJ_ASSERT_RETURN(u_afd->clock_rate == d_afd->clock_rate, PJMEDIA_ENCCLOCKRATE); /* Both ports MUST have equal samples per frame */ PJ_ASSERT_RETURN(PJMEDIA_PIA_SPF(&u_port->info)== PJMEDIA_PIA_SPF(&d_port->info), PJMEDIA_ENCSAMPLESPFRAME); /* Both ports MUST have equal channel count */ PJ_ASSERT_RETURN(u_afd->channel_count == d_afd->channel_count, PJMEDIA_ENCCHANNEL); /* Get clock_rate and samples_per_frame from one of the port. */ clock_rate = u_afd->clock_rate; samples_per_frame = PJMEDIA_PIA_SPF(&u_port->info); channel_count = u_afd->channel_count; /* Get the bytes_per_frame value, to determine the size of the * buffer. We take the larger size of the two ports. */ bytes_per_frame = PJMEDIA_AFD_AVG_FSZ(u_afd); if (PJMEDIA_AFD_AVG_FSZ(d_afd) > bytes_per_frame) bytes_per_frame = PJMEDIA_AFD_AVG_FSZ(d_afd); /* Create the master port instance */ m = PJ_POOL_ZALLOC_T(pool, pjmedia_master_port); m->options = options; m->u_port = u_port; m->d_port = d_port; /* Create buffer */ m->buff_size = bytes_per_frame; m->buff = pj_pool_alloc(pool, m->buff_size); if (!m->buff) return PJ_ENOMEM; /* Create lock object */ status = pj_lock_create_simple_mutex(pool, "mport", &m->lock); if (status != PJ_SUCCESS) return status; /* Create media clock */ status = pjmedia_clock_create(pool, clock_rate, channel_count, samples_per_frame, options, &clock_callback, m, &m->clock); if (status != PJ_SUCCESS) { pj_lock_destroy(m->lock); return status; } /* Done */ *p_m = m; return PJ_SUCCESS; } /* * Start the media flow. */ PJ_DEF(pj_status_t) pjmedia_master_port_start(pjmedia_master_port *m) { PJ_ASSERT_RETURN(m && m->clock, PJ_EINVAL); PJ_ASSERT_RETURN(m->u_port && m->d_port, PJ_EINVALIDOP); return pjmedia_clock_start(m->clock); } /* * Stop the media flow. */ PJ_DEF(pj_status_t) pjmedia_master_port_stop(pjmedia_master_port *m) { PJ_ASSERT_RETURN(m && m->clock, PJ_EINVAL); return pjmedia_clock_stop(m->clock); } /* Poll the master port clock */ PJ_DEF(pj_bool_t) pjmedia_master_port_wait( pjmedia_master_port *m, pj_bool_t wait, pj_timestamp *ts) { PJ_ASSERT_RETURN(m && m->clock, PJ_FALSE); return pjmedia_clock_wait(m->clock, wait, ts); } /* * Callback to be called for each clock ticks. */ static void clock_callback(const pj_timestamp *ts, void *user_data) { pjmedia_master_port *m = (pjmedia_master_port*) user_data; pjmedia_frame frame; pj_status_t status; /* Lock access to ports. */ pj_lock_acquire(m->lock); /* Get frame from upstream port and pass it to downstream port */ pj_bzero(&frame, sizeof(frame)); frame.buf = m->buff; frame.size = m->buff_size; frame.timestamp.u64 = ts->u64; status = pjmedia_port_get_frame(m->u_port, &frame); if (status != PJ_SUCCESS) frame.type = PJMEDIA_FRAME_TYPE_NONE; status = pjmedia_port_put_frame(m->d_port, &frame); /* Get frame from downstream port and pass it to upstream port */ pj_bzero(&frame, sizeof(frame)); frame.buf = m->buff; frame.size = m->buff_size; frame.timestamp.u64 = ts->u64; status = pjmedia_port_get_frame(m->d_port, &frame); if (status != PJ_SUCCESS) frame.type = PJMEDIA_FRAME_TYPE_NONE; status = pjmedia_port_put_frame(m->u_port, &frame); /* Release lock */ pj_lock_release(m->lock); } /* * Change the upstream port. */ PJ_DEF(pj_status_t) pjmedia_master_port_set_uport(pjmedia_master_port *m, pjmedia_port *port) { PJ_ASSERT_RETURN(m && port, PJ_EINVAL); /* Only supports audio for now */ PJ_ASSERT_RETURN(port->info.fmt.type==PJMEDIA_TYPE_AUDIO, PJ_ENOTSUP); /* If we have downstream port, make sure they have matching samples per * frame. */ if (m->d_port) { PJ_ASSERT_RETURN( PJMEDIA_PIA_PTIME(&port->info) == PJMEDIA_PIA_PTIME(&m->d_port->info), PJMEDIA_ENCSAMPLESPFRAME ); } pj_lock_acquire(m->lock); m->u_port = port; pj_lock_release(m->lock); return PJ_SUCCESS; } /* * Get the upstream port. */ PJ_DEF(pjmedia_port*) pjmedia_master_port_get_uport(pjmedia_master_port*m) { PJ_ASSERT_RETURN(m, NULL); return m->u_port; } /* * Change the downstream port. */ PJ_DEF(pj_status_t) pjmedia_master_port_set_dport(pjmedia_master_port *m, pjmedia_port *port) { PJ_ASSERT_RETURN(m && port, PJ_EINVAL); /* Only supports audio for now */ PJ_ASSERT_RETURN(port->info.fmt.type==PJMEDIA_TYPE_AUDIO, PJ_ENOTSUP); /* If we have upstream port, make sure they have matching samples per * frame. */ if (m->u_port) { PJ_ASSERT_RETURN( PJMEDIA_PIA_PTIME(&port->info) == PJMEDIA_PIA_PTIME(&m->u_port->info), PJMEDIA_ENCSAMPLESPFRAME ); } pj_lock_acquire(m->lock); m->d_port = port; pj_lock_release(m->lock); return PJ_SUCCESS; } /* * Get the downstream port. */ PJ_DEF(pjmedia_port*) pjmedia_master_port_get_dport(pjmedia_master_port*m) { PJ_ASSERT_RETURN(m, NULL); return m->d_port; } /* * Destroy the master port, and optionally destroy the u_port and * d_port ports. */ PJ_DEF(pj_status_t) pjmedia_master_port_destroy(pjmedia_master_port *m, pj_bool_t destroy_ports) { PJ_ASSERT_RETURN(m, PJ_EINVAL); if (m->clock) { pjmedia_clock_destroy(m->clock); m->clock = NULL; } if (m->u_port && destroy_ports) { pjmedia_port_destroy(m->u_port); m->u_port = NULL; } if (m->d_port && destroy_ports) { pjmedia_port_destroy(m->d_port); m->d_port = NULL; } if (m->lock) { pj_lock_destroy(m->lock); m->lock = NULL; } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/mem_capture.c ================================================ /* $Id: mem_capture.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #define THIS_FILE "mem_capture.c" #define SIGNATURE PJMEDIA_SIG_PORT_MEM_CAPTURE #define BYTES_PER_SAMPLE 2 struct mem_rec { pjmedia_port base; unsigned options; char *buffer; pj_size_t buf_size; char *write_pos; pj_bool_t eof; void *user_data; pj_status_t (*cb)(pjmedia_port *port, void *user_data); }; static pj_status_t rec_put_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t rec_get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t rec_on_destroy(pjmedia_port *this_port); PJ_DEF(pj_status_t) pjmedia_mem_capture_create( pj_pool_t *pool, void *buffer, pj_size_t size, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_port **p_port) { struct mem_rec *rec; const pj_str_t name = { "memrec", 6 }; /* Sanity check */ PJ_ASSERT_RETURN(pool && buffer && size && clock_rate && channel_count && samples_per_frame && bits_per_sample && p_port, PJ_EINVAL); /* Can only support 16bit PCM */ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); rec = PJ_POOL_ZALLOC_T(pool, struct mem_rec); PJ_ASSERT_RETURN(rec != NULL, PJ_ENOMEM); /* Create the rec */ pjmedia_port_info_init(&rec->base.info, &name, SIGNATURE, clock_rate, channel_count, bits_per_sample, samples_per_frame); rec->base.put_frame = &rec_put_frame; rec->base.get_frame = &rec_get_frame; rec->base.on_destroy = &rec_on_destroy; /* Save the buffer */ rec->buffer = rec->write_pos = (char*)buffer; rec->buf_size = size; /* Options */ rec->options = options; *p_port = &rec->base; return PJ_SUCCESS; } /* * Register a callback to be called when the file reading has reached the * end of buffer. */ PJ_DEF(pj_status_t) pjmedia_mem_capture_set_eof_cb( pjmedia_port *port, void *user_data, pj_status_t (*cb)(pjmedia_port *port, void *usr_data)) { struct mem_rec *rec; PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP); rec = (struct mem_rec*) port; rec->user_data = user_data; rec->cb = cb; return PJ_SUCCESS; } /* Get current buffer size */ PJ_DEF(pj_size_t) pjmedia_mem_capture_get_size(pjmedia_port *port) { struct mem_rec *rec; PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, 0); rec = (struct mem_rec*) port; if (rec->eof){ return rec->buf_size; } return rec->write_pos - rec->buffer; } static pj_status_t rec_put_frame( pjmedia_port *this_port, pjmedia_frame *frame) { struct mem_rec *rec; char *endpos; pj_size_t size_written; PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVALIDOP); rec = (struct mem_rec*) this_port; if (rec->eof) { return PJ_EEOF; } size_written = 0; endpos = rec->buffer + rec->buf_size; while (size_written < frame->size) { pj_size_t max; max = frame->size - size_written; if ((endpos - rec->write_pos) < (int)max) max = endpos - rec->write_pos; pj_memcpy(rec->write_pos, ((char*)frame->buf)+size_written, max); size_written += max; rec->write_pos += max; pj_assert(rec->write_pos <= endpos); if (rec->write_pos == endpos) { /* Rewind */ rec->write_pos = rec->buffer; /* Call callback, if any */ if (rec->cb) { pj_status_t status; rec->eof = PJ_TRUE; status = (*rec->cb)(this_port, rec->user_data); if (status != PJ_SUCCESS) { /* Must not access recorder from here on. It may be * destroyed by application. */ return status; } rec->eof = PJ_FALSE; } } } return PJ_SUCCESS; } static pj_status_t rec_get_frame( pjmedia_port *this_port, pjmedia_frame *frame) { PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVALIDOP); PJ_UNUSED_ARG(this_port); frame->size = 0; frame->type = PJMEDIA_FRAME_TYPE_NONE; return PJ_SUCCESS; } static pj_status_t rec_on_destroy(pjmedia_port *this_port) { /* Call callback if data was captured * and we're not in the callback already. */ struct mem_rec *rec; PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVALIDOP); rec = (struct mem_rec*) this_port; if(rec->cb && PJ_FALSE == rec->eof) { rec->eof = PJ_TRUE; (*rec->cb)(this_port, rec->user_data); } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/mem_player.c ================================================ /* $Id: mem_player.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #define THIS_FILE "mem_player.c" #define SIGNATURE PJMEDIA_SIG_PORT_MEM_PLAYER #define BYTES_PER_SAMPLE 2 struct mem_player { pjmedia_port base; unsigned options; pj_timestamp timestamp; char *buffer; pj_size_t buf_size; char *read_pos; pj_bool_t eof; void *user_data; pj_status_t (*cb)(pjmedia_port *port, void *user_data); }; static pj_status_t mem_put_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t mem_get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t mem_on_destroy(pjmedia_port *this_port); PJ_DEF(pj_status_t) pjmedia_mem_player_create( pj_pool_t *pool, const void *buffer, pj_size_t size, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_port **p_port ) { struct mem_player *port; pj_str_t name = pj_str("memplayer"); /* Sanity check */ PJ_ASSERT_RETURN(pool && buffer && size && clock_rate && channel_count && samples_per_frame && bits_per_sample && p_port, PJ_EINVAL); /* Can only support 16bit PCM */ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); port = PJ_POOL_ZALLOC_T(pool, struct mem_player); PJ_ASSERT_RETURN(port != NULL, PJ_ENOMEM); /* Create the port */ pjmedia_port_info_init(&port->base.info, &name, SIGNATURE, clock_rate, channel_count, bits_per_sample, samples_per_frame); port->base.put_frame = &mem_put_frame; port->base.get_frame = &mem_get_frame; port->base.on_destroy = &mem_on_destroy; /* Save the buffer */ port->buffer = port->read_pos = (char*)buffer; port->buf_size = size; /* Options */ port->options = options; *p_port = &port->base; return PJ_SUCCESS; } /* * Register a callback to be called when the file reading has reached the * end of buffer. */ PJ_DEF(pj_status_t) pjmedia_mem_player_set_eof_cb( pjmedia_port *port, void *user_data, pj_status_t (*cb)(pjmedia_port *port, void *usr_data)) { struct mem_player *player; PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP); player = (struct mem_player*) port; player->user_data = user_data; player->cb = cb; return PJ_SUCCESS; } static pj_status_t mem_put_frame( pjmedia_port *this_port, pjmedia_frame *frame) { PJ_UNUSED_ARG(this_port); PJ_UNUSED_ARG(frame); return PJ_SUCCESS; } static pj_status_t mem_get_frame( pjmedia_port *this_port, pjmedia_frame *frame) { struct mem_player *player; char *endpos; pj_size_t size_needed, size_written; PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVALIDOP); player = (struct mem_player*) this_port; if (player->eof) { pj_status_t status = PJ_SUCCESS; /* Call callback, if any */ if (player->cb) status = (*player->cb)(this_port, player->user_data); /* If callback returns non PJ_SUCCESS or 'no loop' is specified * return immediately (and don't try to access player port since * it might have been destroyed by the callback). */ if ((status != PJ_SUCCESS) || (player->options & PJMEDIA_MEM_NO_LOOP)) { frame->type = PJMEDIA_FRAME_TYPE_NONE; return PJ_EEOF; } player->eof = PJ_FALSE; } size_needed = PJMEDIA_PIA_AVG_FSZ(&this_port->info); size_written = 0; endpos = player->buffer + player->buf_size; while (size_written < size_needed) { char *dst = ((char*)frame->buf) + size_written; pj_size_t max; max = size_needed - size_written; if (endpos - player->read_pos < (int)max) max = endpos - player->read_pos; pj_memcpy(dst, player->read_pos, max); size_written += max; player->read_pos += max; pj_assert(player->read_pos <= endpos); if (player->read_pos == endpos) { /* Set EOF flag */ player->eof = PJ_TRUE; /* Reset read pointer */ player->read_pos = player->buffer; /* Pad with zeroes then return for no looped play */ if (player->options & PJMEDIA_MEM_NO_LOOP) { pj_size_t null_len; null_len = size_needed - size_written; pj_bzero(dst + max, null_len); break; } } } frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info); frame->timestamp.u64 = player->timestamp.u64; frame->type = PJMEDIA_FRAME_TYPE_AUDIO; player->timestamp.u64 += PJMEDIA_PIA_SPF(&this_port->info); return PJ_SUCCESS; } static pj_status_t mem_on_destroy(pjmedia_port *this_port) { PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVALIDOP); /* Destroy signature */ this_port->info.signature = 0; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/mixer_port.c ================================================ /* * Copyright (C) 2010 AG Projects * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #define SIGNATURE PJMEDIA_SIG_PORT_MIXER #define MIN(a, b) ((a)>(b)?(b):(a)) struct mixer_port { pjmedia_port base; pjmedia_frame_type last_frame_type; pj_size_t last_frame_size; pj_timestamp last_frame_timestamp; pj_int16_t* buffer; pj_size_t buffer_size; }; static pj_status_t mixer_get_frame(pjmedia_port *port, pjmedia_frame *frame); static pj_status_t mixer_put_frame(pjmedia_port *port, pjmedia_frame *frame); static pj_status_t mixer_on_destroy(pjmedia_port *port); PJ_DEF(pj_status_t) pjmedia_mixer_port_create(pj_pool_t *pool, unsigned sampling_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_port **p_port) { struct mixer_port *port; const pj_str_t name = pj_str("mixer-port"); PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL); port = PJ_POOL_ZALLOC_T(pool, struct mixer_port); PJ_ASSERT_RETURN(port != NULL, PJ_ENOMEM); pjmedia_port_info_init(&port->base.info, &name, SIGNATURE, sampling_rate, channel_count, bits_per_sample, samples_per_frame); port->base.get_frame = &mixer_get_frame; port->base.put_frame = &mixer_put_frame; port->base.on_destroy = &mixer_on_destroy; port->last_frame_type = PJMEDIA_FRAME_TYPE_NONE; port->last_frame_size = 0; port->last_frame_timestamp.u64 = 0; port->buffer = (pj_int16_t*) pj_pool_calloc(pool, samples_per_frame, sizeof(pj_int16_t)); port->buffer_size = sizeof(pj_int16_t) * samples_per_frame; *p_port = &port->base; return PJ_SUCCESS; } /* * Put frame to file. */ static pj_status_t mixer_put_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct mixer_port* port = (struct mixer_port*) this_port; if (!frame->size || frame->type != PJMEDIA_FRAME_TYPE_AUDIO) { port->last_frame_type = PJMEDIA_FRAME_TYPE_NONE; port->last_frame_size = 0; port->last_frame_timestamp.u64 = 0; return PJ_SUCCESS; } PJ_ASSERT_RETURN(frame->size <= port->buffer_size, PJ_EINVAL); port->last_frame_type = frame->type; pj_get_timestamp(&port->last_frame_timestamp); port->last_frame_size = MIN(port->buffer_size, frame->size); memcpy(port->buffer, frame->buf, port->last_frame_size); return PJ_SUCCESS; } /* * Get frame from file. */ static pj_status_t mixer_get_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct mixer_port* port = (struct mixer_port*) this_port; pj_timestamp now; pj_uint32_t frame_age; pj_get_timestamp(&now); frame_age = pj_elapsed_usec(&port->last_frame_timestamp, &now); if (port->last_frame_timestamp.u64 != 0 && frame_age <= 100000) { frame->type = port->last_frame_type; frame->size = port->last_frame_size; frame->timestamp.u64 = 0; if (port->last_frame_size > 0) { memcpy(frame->buf, port->buffer, port->last_frame_size); } } else { frame->type = PJMEDIA_FRAME_TYPE_NONE; frame->size = 0; frame->timestamp.u64 = 0; } return PJ_SUCCESS; } /* * Destroy port. */ static pj_status_t mixer_on_destroy(pjmedia_port *this_port) { return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/null_port.c ================================================ /* $Id: null_port.c 4423 2013-03-06 06:06:17Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #define SIGNATURE PJMEDIA_SIG_PORT_NULL static pj_status_t null_get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t null_put_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t null_on_destroy(pjmedia_port *this_port); PJ_DEF(pj_status_t) pjmedia_null_port_create( pj_pool_t *pool, unsigned sampling_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_port **p_port ) { pjmedia_port *port; const pj_str_t name = pj_str("null-port"); PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL); port = PJ_POOL_ZALLOC_T(pool, pjmedia_port); PJ_ASSERT_RETURN(port != NULL, PJ_ENOMEM); pjmedia_port_info_init(&port->info, &name, SIGNATURE, sampling_rate, channel_count, bits_per_sample, samples_per_frame); port->get_frame = &null_get_frame; port->put_frame = &null_put_frame; port->on_destroy = &null_on_destroy; *p_port = port; return PJ_SUCCESS; } /* * Put frame to file. */ static pj_status_t null_put_frame(pjmedia_port *this_port, pjmedia_frame *frame) { PJ_UNUSED_ARG(this_port); PJ_UNUSED_ARG(frame); return PJ_SUCCESS; } /* * Get frame from file. */ static pj_status_t null_get_frame(pjmedia_port *this_port, pjmedia_frame *frame) { frame->type = PJMEDIA_FRAME_TYPE_AUDIO; frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info); frame->timestamp.u32.lo += PJMEDIA_PIA_SPF(&this_port->info); pjmedia_zero_samples((pj_int16_t*)frame->buf, PJMEDIA_PIA_SPF(&this_port->info)); return PJ_SUCCESS; } /* * Destroy port. */ static pj_status_t null_on_destroy(pjmedia_port *this_port) { PJ_UNUSED_ARG(this_port); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/plc_common.c ================================================ /* $Id: plc_common.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include static void* plc_wsola_create(pj_pool_t*, unsigned c, unsigned f); static void plc_wsola_save(void*, pj_int16_t*); static void plc_wsola_generate(void*, pj_int16_t*); /** * This struct is used internally to represent a PLC backend. */ struct plc_alg { void* (*plc_create)(pj_pool_t*, unsigned c, unsigned f); void (*plc_save)(void*, pj_int16_t*); void (*plc_generate)(void*, pj_int16_t*); }; static struct plc_alg plc_wsola = { &plc_wsola_create, &plc_wsola_save, &plc_wsola_generate }; struct pjmedia_plc { void *obj; struct plc_alg *op; }; /* * Create PLC session. This function will select the PLC algorithm to * use based on the arguments. */ PJ_DEF(pj_status_t) pjmedia_plc_create( pj_pool_t *pool, unsigned clock_rate, unsigned samples_per_frame, unsigned options, pjmedia_plc **p_plc) { pjmedia_plc *plc; PJ_ASSERT_RETURN(pool && clock_rate && samples_per_frame && p_plc, PJ_EINVAL); PJ_ASSERT_RETURN(options == 0, PJ_EINVAL); PJ_UNUSED_ARG(options); plc = PJ_POOL_ZALLOC_T(pool, pjmedia_plc); plc->op = &plc_wsola; plc->obj = plc->op->plc_create(pool, clock_rate, samples_per_frame); *p_plc = plc; return PJ_SUCCESS; } /* * Save a good frame to PLC. */ PJ_DEF(pj_status_t) pjmedia_plc_save( pjmedia_plc *plc, pj_int16_t *frame ) { PJ_ASSERT_RETURN(plc && frame, PJ_EINVAL); plc->op->plc_save(plc->obj, frame); return PJ_SUCCESS; } /* * Generate a replacement for lost frame. */ PJ_DEF(pj_status_t) pjmedia_plc_generate( pjmedia_plc *plc, pj_int16_t *frame ) { PJ_ASSERT_RETURN(plc && frame, PJ_EINVAL); plc->op->plc_generate(plc->obj, frame); return PJ_SUCCESS; } ////////////////////////////////////////////////////////////////////////////// /* * Packet loss concealment based on WSOLA */ struct wsola_plc { pjmedia_wsola *wsola; pj_bool_t prev_lost; }; static void* plc_wsola_create(pj_pool_t *pool, unsigned clock_rate, unsigned samples_per_frame) { struct wsola_plc *o; unsigned flag; pj_status_t status; PJ_UNUSED_ARG(clock_rate); o = PJ_POOL_ZALLOC_T(pool, struct wsola_plc); o->prev_lost = PJ_FALSE; flag = PJMEDIA_WSOLA_NO_DISCARD; if (PJMEDIA_WSOLA_PLC_NO_FADING) flag |= PJMEDIA_WSOLA_NO_FADING; status = pjmedia_wsola_create(pool, clock_rate, samples_per_frame, 1, flag, &o->wsola); if (status != PJ_SUCCESS) return NULL; return o; } static void plc_wsola_save(void *plc, pj_int16_t *frame) { struct wsola_plc *o = (struct wsola_plc*) plc; pjmedia_wsola_save(o->wsola, frame, o->prev_lost); o->prev_lost = PJ_FALSE; } static void plc_wsola_generate(void *plc, pj_int16_t *frame) { struct wsola_plc *o = (struct wsola_plc*) plc; pjmedia_wsola_generate(o->wsola, frame); o->prev_lost = PJ_TRUE; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/port.c ================================================ /* $Id: port.c 3893 2011-12-01 10:49:07Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #define THIS_FILE "port.c" /** * This is an auxiliary function to initialize port info for * ports which deal with PCM audio. */ PJ_DEF(pj_status_t) pjmedia_port_info_init( pjmedia_port_info *info, const pj_str_t *name, unsigned signature, unsigned clock_rate, unsigned channel_count, unsigned bits_per_sample, unsigned samples_per_frame) { #define USEC_IN_SEC (pj_uint64_t)1000000 unsigned frame_time_usec, avg_bps; pj_bzero(info, sizeof(*info)); info->signature = signature; info->dir = PJMEDIA_DIR_ENCODING_DECODING; info->name = *name; frame_time_usec = (unsigned)(samples_per_frame * USEC_IN_SEC / channel_count / clock_rate); avg_bps = clock_rate * channel_count * bits_per_sample; pjmedia_format_init_audio(&info->fmt, PJMEDIA_FORMAT_L16, clock_rate, channel_count, bits_per_sample, frame_time_usec, avg_bps, avg_bps); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_port_info_init2( pjmedia_port_info *info, const pj_str_t *name, unsigned signature, pjmedia_dir dir, const pjmedia_format *fmt) { pj_bzero(info, sizeof(*info)); info->signature = signature; info->dir = dir; info->name = *name; pjmedia_format_copy(&info->fmt, fmt); return PJ_SUCCESS; } /** * Get a clock source from the port. */ PJ_DEF(pjmedia_clock_src *) pjmedia_port_get_clock_src( pjmedia_port *port, pjmedia_dir dir ) { if (port && port->get_clock_src) return port->get_clock_src(port, dir); else return NULL; } /** * Get a frame from the port (and subsequent downstream ports). */ PJ_DEF(pj_status_t) pjmedia_port_get_frame( pjmedia_port *port, pjmedia_frame *frame ) { PJ_ASSERT_RETURN(port && frame, PJ_EINVAL); if (port->get_frame) return port->get_frame(port, frame); else { frame->type = PJMEDIA_FRAME_TYPE_NONE; return PJ_EINVALIDOP; } } /** * Put a frame to the port (and subsequent downstream ports). */ PJ_DEF(pj_status_t) pjmedia_port_put_frame( pjmedia_port *port, pjmedia_frame *frame ) { PJ_ASSERT_RETURN(port && frame, PJ_EINVAL); if (port->put_frame) return port->put_frame(port, frame); else return PJ_EINVALIDOP; } /** * Destroy port (and subsequent downstream ports) */ PJ_DEF(pj_status_t) pjmedia_port_destroy( pjmedia_port *port ) { pj_status_t status; PJ_ASSERT_RETURN(port, PJ_EINVAL); if (port->on_destroy) status = port->on_destroy(port); else status = PJ_SUCCESS; return status; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/resample_libsamplerate.c ================================================ /* $Id: resample_libsamplerate.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include /* * HOW TO ACTIVATE LIBSAMPLERATE (a.k.a SRC/Secret Rabbit Code) AS * PJMEDIA'S SAMPLE RATE CONVERSION BACKEND * * See README.txt in third_party/samplerate directory. */ #if PJMEDIA_RESAMPLE_IMP==PJMEDIA_RESAMPLE_LIBSAMPLERATE #include "../../third_party/libsamplerate/src/samplerate.h" #define THIS_FILE "resample_libsamplerate.c" #if defined(_MSC_VER) # ifdef _DEBUG # pragma comment( lib, "../../third_party/lib/libsamplerate-i386-win32-vc-debug.lib") # else # pragma comment( lib, "../../third_party/lib/libsamplerate-i386-win32-vc-release.lib") # endif #endif struct pjmedia_resample { SRC_STATE *state; unsigned in_samples; unsigned out_samples; float *frame_in, *frame_out; unsigned in_extra, out_extra; double ratio; }; PJ_DEF(pj_status_t) pjmedia_resample_create( pj_pool_t *pool, pj_bool_t high_quality, pj_bool_t large_filter, unsigned channel_count, unsigned rate_in, unsigned rate_out, unsigned samples_per_frame, pjmedia_resample **p_resample) { pjmedia_resample *resample; int type, err; PJ_ASSERT_RETURN(pool && p_resample && rate_in && rate_out && samples_per_frame, PJ_EINVAL); resample = PJ_POOL_ZALLOC_T(pool, pjmedia_resample); PJ_ASSERT_RETURN(resample, PJ_ENOMEM); /* Select conversion type */ if (high_quality) { type = large_filter ? SRC_SINC_BEST_QUALITY : SRC_SINC_MEDIUM_QUALITY; } else { type = large_filter ? SRC_SINC_FASTEST : SRC_LINEAR; } /* Create converter */ resample->state = src_new(type, channel_count, &err); if (resample->state == NULL) { PJ_LOG(4,(THIS_FILE, "Error creating resample: %s", src_strerror(err))); return PJMEDIA_ERROR; } /* Calculate ratio */ resample->ratio = rate_out * 1.0 / rate_in; /* Calculate number of samples for input and output */ resample->in_samples = samples_per_frame; resample->out_samples = rate_out / (rate_in / samples_per_frame); resample->frame_in = (float*) pj_pool_calloc(pool, resample->in_samples + 8, sizeof(float)); resample->frame_out = (float*) pj_pool_calloc(pool, resample->out_samples + 8, sizeof(float)); /* Set the converter ratio */ err = src_set_ratio(resample->state, resample->ratio); if (err != 0) { PJ_LOG(4,(THIS_FILE, "Error creating resample: %s", src_strerror(err))); return PJMEDIA_ERROR; } /* Done */ PJ_LOG(5,(THIS_FILE, "Resample using libsamplerate %s, type=%s (%s), " "ch=%d, in/out rate=%d/%d", src_get_version(), src_get_name(type), src_get_description(type), channel_count, rate_in, rate_out)); *p_resample = resample; return PJ_SUCCESS; } PJ_DEF(void) pjmedia_resample_run( pjmedia_resample *resample, const pj_int16_t *input, pj_int16_t *output ) { SRC_DATA src_data; /* Convert samples to float */ src_short_to_float_array(input, resample->frame_in, resample->in_samples); if (resample->in_extra) { unsigned i; for (i=0; iin_extra; ++i) resample->frame_in[resample->in_samples+i] = resample->frame_in[resample->in_samples-1]; } /* Prepare SRC_DATA */ pj_bzero(&src_data, sizeof(src_data)); src_data.data_in = resample->frame_in; src_data.data_out = resample->frame_out; src_data.input_frames = resample->in_samples + resample->in_extra; src_data.output_frames = resample->out_samples + resample->out_extra; src_data.src_ratio = resample->ratio; /* Process! */ src_process(resample->state, &src_data); /* Convert output back to short */ src_float_to_short_array(resample->frame_out, output, src_data.output_frames_gen); /* Replay last sample if conversion couldn't fill up the whole * frame. This could happen for example with 22050 to 16000 conversion. */ if (src_data.output_frames_gen < (int)resample->out_samples) { unsigned i; if (resample->in_extra < 4) resample->in_extra++; for (i=src_data.output_frames_gen; iout_samples; ++i) { output[i] = output[src_data.output_frames_gen-1]; } } } PJ_DEF(unsigned) pjmedia_resample_get_input_size(pjmedia_resample *resample) { PJ_ASSERT_RETURN(resample != NULL, 0); return resample->in_samples; } PJ_DEF(void) pjmedia_resample_destroy(pjmedia_resample *resample) { PJ_ASSERT_ON_FAIL(resample, return); if (resample->state) { src_delete(resample->state); resample->state = NULL; PJ_LOG(5,(THIS_FILE, "Resample destroyed")); } } #else /* PJMEDIA_RESAMPLE_IMP==PJMEDIA_RESAMPLE_LIBSAMPLERATE */ int pjmedia_libsamplerate_excluded; #endif /* PJMEDIA_RESAMPLE_IMP==PJMEDIA_RESAMPLE_LIBSAMPLERATE */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/resample_port.c ================================================ /* $Id: resample_port.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #define BYTES_PER_SAMPLE 2 #define SIGNATURE PJMEDIA_SIG_PORT_RESAMPLE struct resample_port { pjmedia_port base; pjmedia_port *dn_port; unsigned options; pjmedia_resample *resample_get; pjmedia_resample *resample_put; pj_int16_t *get_buf; pj_int16_t *put_buf; }; static pj_status_t resample_put_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t resample_get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t resample_destroy(pjmedia_port *this_port); PJ_DEF(pj_status_t) pjmedia_resample_port_create( pj_pool_t *pool, pjmedia_port *dn_port, unsigned clock_rate, unsigned opt, pjmedia_port **p_port ) { const pj_str_t name = pj_str("resample"); struct resample_port *rport; pjmedia_audio_format_detail *d_afd, *r_afd; pj_status_t status; /* Validate arguments. */ PJ_ASSERT_RETURN(pool && dn_port && clock_rate && p_port, PJ_EINVAL); /* Only supports 16bit samples per frame */ PJ_ASSERT_RETURN(PJMEDIA_PIA_BITS(&dn_port->info) == 16, PJMEDIA_ENCBITS); d_afd = pjmedia_format_get_audio_format_detail(&dn_port->info.fmt, 1); /* Create and initialize port. */ rport = PJ_POOL_ZALLOC_T(pool, struct resample_port); PJ_ASSERT_RETURN(rport != NULL, PJ_ENOMEM); pjmedia_port_info_init(&rport->base.info, &name, SIGNATURE, clock_rate, d_afd->channel_count, BYTES_PER_SAMPLE * 8, clock_rate * d_afd->frame_time_usec / 1000000); rport->dn_port = dn_port; rport->options = opt; r_afd = pjmedia_format_get_audio_format_detail(&rport->base.info.fmt, 1); /* Create buffers. * We need separate buffer for get_frame() and put_frame() since * both functions may run simultaneously. */ rport->get_buf = (pj_int16_t*) pj_pool_alloc(pool, PJMEDIA_PIA_AVG_FSZ(&dn_port->info)); PJ_ASSERT_RETURN(rport->get_buf != NULL, PJ_ENOMEM); rport->put_buf = (pj_int16_t*) pj_pool_alloc(pool, PJMEDIA_PIA_AVG_FSZ(&dn_port->info)); PJ_ASSERT_RETURN(rport->put_buf != NULL, PJ_ENOMEM); /* Create "get_frame" resample */ status = pjmedia_resample_create(pool, (opt&PJMEDIA_RESAMPLE_USE_LINEAR)==0, (opt&PJMEDIA_RESAMPLE_USE_SMALL_FILTER)==0, d_afd->channel_count, d_afd->clock_rate, r_afd->clock_rate, PJMEDIA_PIA_SPF(&dn_port->info), &rport->resample_get); if (status != PJ_SUCCESS) return status; /* Create "put_frame" resample */ status = pjmedia_resample_create(pool, (opt&PJMEDIA_RESAMPLE_USE_LINEAR)==0, (opt&PJMEDIA_RESAMPLE_USE_SMALL_FILTER)==0, d_afd->channel_count, r_afd->clock_rate, d_afd->clock_rate, PJMEDIA_PIA_SPF(&rport->base.info), &rport->resample_put); /* Media port interface */ rport->base.get_frame = &resample_get_frame; rport->base.put_frame = &resample_put_frame; rport->base.on_destroy = &resample_destroy; /* Done */ *p_port = &rport->base; return PJ_SUCCESS; } static pj_status_t resample_put_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct resample_port *rport = (struct resample_port*) this_port; pjmedia_frame downstream_frame; /* Return if we don't have downstream port. */ if (rport->dn_port == NULL) { return PJ_SUCCESS; } if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) { pjmedia_resample_run( rport->resample_put, (const pj_int16_t*) frame->buf, rport->put_buf); downstream_frame.buf = rport->put_buf; downstream_frame.size = PJMEDIA_PIA_AVG_FSZ(&rport->dn_port->info); } else { downstream_frame.buf = frame->buf; downstream_frame.size = frame->size; } downstream_frame.type = frame->type; downstream_frame.timestamp.u64 = frame->timestamp.u64; return pjmedia_port_put_frame( rport->dn_port, &downstream_frame ); } static pj_status_t resample_get_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct resample_port *rport = (struct resample_port*) this_port; pjmedia_frame tmp_frame; pj_status_t status; /* Return silence if we don't have downstream port */ if (rport->dn_port == NULL) { pj_bzero(frame->buf, frame->size); return PJ_SUCCESS; } tmp_frame.buf = rport->get_buf; tmp_frame.size = PJMEDIA_PIA_AVG_FSZ(&rport->dn_port->info); tmp_frame.timestamp.u64 = frame->timestamp.u64; tmp_frame.type = PJMEDIA_FRAME_TYPE_AUDIO; status = pjmedia_port_get_frame( rport->dn_port, &tmp_frame); if (status != PJ_SUCCESS) return status; if (tmp_frame.type != PJMEDIA_FRAME_TYPE_AUDIO) { frame->type = tmp_frame.type; frame->timestamp = tmp_frame.timestamp; /* Copy whatever returned as long as the buffer size is enough */ frame->size = tmp_frame.size < PJMEDIA_PIA_AVG_FSZ(&rport->base.info) ? tmp_frame.size : PJMEDIA_PIA_AVG_FSZ(&rport->base.info); if (tmp_frame.size) { pjmedia_copy_samples((pj_int16_t*)frame->buf, (const pj_int16_t*)tmp_frame.buf, (unsigned)frame->size >> 1); } return PJ_SUCCESS; } pjmedia_resample_run( rport->resample_get, (const pj_int16_t*) tmp_frame.buf, (pj_int16_t*) frame->buf); frame->size = PJMEDIA_PIA_AVG_FSZ(&rport->base.info); frame->type = PJMEDIA_FRAME_TYPE_AUDIO; return PJ_SUCCESS; } static pj_status_t resample_destroy(pjmedia_port *this_port) { struct resample_port *rport = (struct resample_port*) this_port; if ((rport->options & PJMEDIA_RESAMPLE_DONT_DESTROY_DN)==0) { pjmedia_port_destroy(rport->dn_port); rport->dn_port = NULL; } if (rport->resample_get) { pjmedia_resample_destroy(rport->resample_get); rport->resample_get = NULL; } if (rport->resample_put) { pjmedia_resample_destroy(rport->resample_put); rport->resample_put = NULL; } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/resample_resample.c ================================================ /* $Id: resample_resample.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #if PJMEDIA_RESAMPLE_IMP==PJMEDIA_RESAMPLE_LIBRESAMPLE #include #define THIS_FILE "resample.c" struct pjmedia_resample { double factor; /* Conversion factor = rate_out / rate_in. */ pj_bool_t large_filter; /* Large filter? */ pj_bool_t high_quality; /* Not fast? */ unsigned xoff; /* History and lookahead size, in samples */ unsigned frame_size; /* Samples per frame. */ unsigned channel_cnt; /* Channel count. */ /* Buffer for monochannel */ pj_int16_t *buffer; /* Input buffer. */ /* Buffer for multichannel */ pj_int16_t **in_buffer; /* Array of input buffer for each channel. */ pj_int16_t *tmp_buffer; /* Temporary output buffer for processing. */ }; PJ_DEF(pj_status_t) pjmedia_resample_create( pj_pool_t *pool, pj_bool_t high_quality, pj_bool_t large_filter, unsigned channel_count, unsigned rate_in, unsigned rate_out, unsigned samples_per_frame, pjmedia_resample **p_resample) { pjmedia_resample *resample; PJ_ASSERT_RETURN(pool && p_resample && rate_in && rate_out && samples_per_frame, PJ_EINVAL); resample = PJ_POOL_ZALLOC_T(pool, pjmedia_resample); PJ_ASSERT_RETURN(resample, PJ_ENOMEM); /* * If we're downsampling, always use the fast algorithm since it seems * to yield the same quality. */ if (rate_out < rate_in) { //no this is not a good idea. It sounds pretty good with speech, //but very poor with background noise etc. //high_quality = 0; } resample->factor = rate_out * 1.0 / rate_in; resample->large_filter = large_filter; resample->high_quality = high_quality; resample->channel_cnt = channel_count; resample->frame_size = samples_per_frame; if (high_quality) { /* This is a bug in xoff calculation, thanks Stephane Lussier * of Macadamian dot com. * resample->xoff = large_filter ? 32 : 6; */ resample->xoff = res_GetXOFF(resample->factor, (char)large_filter); } else { resample->xoff = 1; } if (channel_count == 1) { unsigned size; /* Allocate input buffer */ size = (samples_per_frame + 2*resample->xoff) * sizeof(pj_int16_t); resample->buffer = (pj_int16_t*) pj_pool_alloc(pool, size); PJ_ASSERT_RETURN(resample->buffer, PJ_ENOMEM); pjmedia_zero_samples(resample->buffer, resample->xoff*2); } else if (channel_count > 1) { unsigned i, size; /* Allocate input buffer table */ size = channel_count * sizeof(pj_int16_t*); resample->in_buffer = (pj_int16_t**)pj_pool_alloc(pool, size); /* Allocate input buffer */ size = (samples_per_frame/channel_count + 2*resample->xoff) * sizeof(pj_int16_t); for (i = 0; i < channel_count; ++i) { resample->in_buffer[i] = (pj_int16_t*)pj_pool_alloc(pool, size); PJ_ASSERT_RETURN(resample->in_buffer, PJ_ENOMEM); pjmedia_zero_samples(resample->in_buffer[i], resample->xoff*2); } /* Allocate temporary output buffer */ size = (unsigned) (resample->frame_size * sizeof(pj_int16_t) * resample->factor / channel_count + 0.5); resample->tmp_buffer = (pj_int16_t*) pj_pool_alloc(pool, size); PJ_ASSERT_RETURN(resample->tmp_buffer, PJ_ENOMEM); } *p_resample = resample; PJ_LOG(5,(THIS_FILE, "resample created: %s qualiy, %s filter, in/out " "rate=%d/%d", (high_quality?"high":"low"), (large_filter?"large":"small"), rate_in, rate_out)); return PJ_SUCCESS; } PJ_DEF(void) pjmedia_resample_run( pjmedia_resample *resample, const pj_int16_t *input, pj_int16_t *output ) { PJ_ASSERT_ON_FAIL(resample, return); /* Okay chaps, here's how we do resampling. * * The original resample algorithm requires xoff samples *before* the * input buffer as history, and another xoff samples *after* the * end of the input buffer as lookahead. Since application can only * supply framesize buffer on each run, PJMEDIA needs to arrange the * buffer to meet these requirements. * * So here comes the trick. * * First of all, because of the history and lookahead requirement, * resample->buffer need to accomodate framesize+2*xoff samples in its * buffer. This is done when the buffer is created. * * On the first run, the input frame (supplied by application) is * copied to resample->buffer at 2*xoff position. The first 2*xoff * samples are initially zeroed (in the initialization). The resample * algorithm then invoked at resample->buffer+xoff ONLY, thus giving * it one xoff at the beginning as zero, and one xoff at the end * as the end of the original input. The resample algorithm will see * that the first xoff samples in the input as zero. * * So here's the layout of resample->buffer on the first run. * * run 0 * +------+------+--------------+ * | 0000 | 0000 | frame0... | * +------+------+--------------+ * ^ ^ ^ ^ * 0 xoff 2*xoff size+2*xoff * * (Note again: resample algorithm is called at resample->buffer+xoff) * * At the end of the run, 2*xoff samples from the end of * resample->buffer are copied to the beginning of resample->buffer. * The first xoff part of this will be used as history for the next * run, and the second xoff part of this is actually the start of * resampling for the next run. * * And the first run completes, the function returns. * * * On the next run, the input frame supplied by application is again * copied at 2*xoff position in the resample->buffer, and the * resample algorithm is again invoked at resample->buffer+xoff * position. So effectively, the resample algorithm will start its * operation on the last xoff from the previous frame, and gets the * history from the last 2*xoff of the previous frame, and the look- * ahead from the last xoff of current frame. * * So on this run, the buffer layout is: * * run 1 * +------+------+--------------+ * | frm0 | frm0 | frame1... | * +------+------+--------------+ * ^ ^ ^ ^ * 0 xoff 2*xoff size+2*xoff * * As you can see from above diagram, the resampling algorithm is * actually called from the last xoff part of previous frame (frm0). * * And so on the process continues for the next frame, and the next, * and the next, ... * */ if (resample->channel_cnt == 1) { pj_int16_t *dst_buf; const pj_int16_t *src_buf; /* Prepare input frame */ dst_buf = resample->buffer + resample->xoff*2; pjmedia_copy_samples(dst_buf, input, resample->frame_size); /* Resample */ if (resample->high_quality) { res_Resample(resample->buffer + resample->xoff, output, resample->factor, (pj_uint16_t)resample->frame_size, (char)resample->large_filter, (char)PJ_TRUE); } else { res_SrcLinear(resample->buffer + resample->xoff, output, resample->factor, (pj_uint16_t)resample->frame_size); } /* Update history */ dst_buf = resample->buffer; src_buf = input + resample->frame_size - resample->xoff*2; pjmedia_copy_samples(dst_buf, src_buf, resample->xoff * 2); } else { /* Multichannel */ unsigned i, j; for (i = 0; i < resample->channel_cnt; ++i) { pj_int16_t *dst_buf; const pj_int16_t *src_buf; unsigned mono_frm_sz_in; unsigned mono_frm_sz_out; mono_frm_sz_in = resample->frame_size / resample->channel_cnt; mono_frm_sz_out = (unsigned)(mono_frm_sz_in * resample->factor + 0.5); /* Deinterleave input */ dst_buf = resample->in_buffer[i] + resample->xoff*2; src_buf = input + i; for (j = 0; j < mono_frm_sz_in; ++j) { *dst_buf++ = *src_buf; src_buf += resample->channel_cnt; } /* Resample this channel */ if (resample->high_quality) { res_Resample(resample->in_buffer[i] + resample->xoff, resample->tmp_buffer, resample->factor, (pj_uint16_t)mono_frm_sz_in, (char)resample->large_filter, (char)PJ_TRUE); } else { res_SrcLinear( resample->in_buffer[i], resample->tmp_buffer, resample->factor, (pj_uint16_t)mono_frm_sz_in); } /* Update history */ dst_buf = resample->in_buffer[i]; src_buf = resample->in_buffer[i] + mono_frm_sz_in; pjmedia_copy_samples(dst_buf, src_buf, resample->xoff * 2); /* Reinterleave output */ dst_buf = output + i; src_buf = resample->tmp_buffer; for (j = 0; j < mono_frm_sz_out; ++j) { *dst_buf = *src_buf++; dst_buf += resample->channel_cnt; } } } } PJ_DEF(unsigned) pjmedia_resample_get_input_size(pjmedia_resample *resample) { PJ_ASSERT_RETURN(resample != NULL, 0); return resample->frame_size; } PJ_DEF(void) pjmedia_resample_destroy(pjmedia_resample *resample) { PJ_UNUSED_ARG(resample); } #elif PJMEDIA_RESAMPLE_IMP==PJMEDIA_RESAMPLE_NONE /* * This is the configuration when sample rate conversion is disabled. */ PJ_DEF(pj_status_t) pjmedia_resample_create( pj_pool_t *pool, pj_bool_t high_quality, pj_bool_t large_filter, unsigned channel_count, unsigned rate_in, unsigned rate_out, unsigned samples_per_frame, pjmedia_resample **p_resample) { PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(high_quality); PJ_UNUSED_ARG(large_filter); PJ_UNUSED_ARG(channel_count); PJ_UNUSED_ARG(rate_in); PJ_UNUSED_ARG(rate_out); PJ_UNUSED_ARG(samples_per_frame); PJ_UNUSED_ARG(p_resample); return PJ_EINVALIDOP; } PJ_DEF(void) pjmedia_resample_run( pjmedia_resample *resample, const pj_int16_t *input, pj_int16_t *output ) { PJ_UNUSED_ARG(resample); PJ_UNUSED_ARG(input); PJ_UNUSED_ARG(output); } PJ_DEF(unsigned) pjmedia_resample_get_input_size(pjmedia_resample *resample) { PJ_UNUSED_ARG(resample); return 0; } PJ_DEF(void) pjmedia_resample_destroy(pjmedia_resample *resample) { PJ_UNUSED_ARG(resample); } #endif /* PJMEDIA_RESAMPLE_IMP */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/resample_speex.c ================================================ /* $Id: resample_speex.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #if PJMEDIA_RESAMPLE_IMP==PJMEDIA_RESAMPLE_SPEEX #include #define THIS_FILE "resample_speex.c" struct pjmedia_resample { SpeexResamplerState *state; unsigned in_samples_per_frame; unsigned out_samples_per_frame; }; PJ_DEF(pj_status_t) pjmedia_resample_create( pj_pool_t *pool, pj_bool_t high_quality, pj_bool_t large_filter, unsigned channel_count, unsigned rate_in, unsigned rate_out, unsigned samples_per_frame, pjmedia_resample **p_resample) { pjmedia_resample *resample; int quality; int err; PJ_ASSERT_RETURN(pool && p_resample && rate_in && rate_out && samples_per_frame, PJ_EINVAL); resample = PJ_POOL_ZALLOC_T(pool, pjmedia_resample); PJ_ASSERT_RETURN(resample, PJ_ENOMEM); if (high_quality) { if (large_filter) quality = 10; else quality = 7; } else { quality = 3; } resample->in_samples_per_frame = samples_per_frame; resample->out_samples_per_frame = rate_out / (rate_in / samples_per_frame); resample->state = speex_resampler_init(channel_count, rate_in, rate_out, quality, &err); if (resample->state == NULL || err != RESAMPLER_ERR_SUCCESS) return PJ_ENOMEM; *p_resample = resample; PJ_LOG(5,(THIS_FILE, "resample created: quality=%d, ch=%d, in/out rate=%d/%d", quality, channel_count, rate_in, rate_out)); return PJ_SUCCESS; } PJ_DEF(void) pjmedia_resample_run( pjmedia_resample *resample, const pj_int16_t *input, pj_int16_t *output ) { spx_uint32_t in_length, out_length; PJ_ASSERT_ON_FAIL(resample, return); in_length = resample->in_samples_per_frame; out_length = resample->out_samples_per_frame; speex_resampler_process_interleaved_int(resample->state, (const __int16 *)input, &in_length, (__int16 *)output, &out_length); pj_assert(in_length == resample->in_samples_per_frame); pj_assert(out_length == resample->out_samples_per_frame); } PJ_DEF(unsigned) pjmedia_resample_get_input_size(pjmedia_resample *resample) { PJ_ASSERT_RETURN(resample != NULL, 0); return resample->in_samples_per_frame; } PJ_DEF(void) pjmedia_resample_destroy(pjmedia_resample *resample) { PJ_ASSERT_ON_FAIL(resample, return); if (resample->state) { speex_resampler_destroy(resample->state); resample->state = NULL; } } #else /* PJMEDIA_RESAMPLE_IMP==PJMEDIA_RESAMPLE_SPEEX */ int pjmedia_resample_speex_excluded; #endif /* PJMEDIA_RESAMPLE_IMP==PJMEDIA_RESAMPLE_SPEEX */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/rtcp.c ================================================ /* $Id: rtcp.c 4283 2012-10-12 06:19:32Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #define THIS_FILE "rtcp.c" #define RTCP_SR 200 #define RTCP_RR 201 #define RTCP_SDES 202 #define RTCP_BYE 203 #define RTCP_PSFB 206 /* Payload-specific FB message (RFC 4585) */ #define RTCP_XR 207 enum { RTCP_SDES_NULL = 0, RTCP_SDES_CNAME = 1, RTCP_SDES_NAME = 2, RTCP_SDES_EMAIL = 3, RTCP_SDES_PHONE = 4, RTCP_SDES_LOC = 5, RTCP_SDES_TOOL = 6, RTCP_SDES_NOTE = 7 }; #if PJ_HAS_HIGH_RES_TIMER==0 # error "High resolution timer needs to be enabled" #endif #if 0 # define TRACE_(x) PJ_LOG(3,x) #else # define TRACE_(x) ; #endif /* * Get NTP time. */ PJ_DEF(pj_status_t) pjmedia_rtcp_get_ntp_time(const pjmedia_rtcp_session *sess, pjmedia_rtcp_ntp_rec *ntp) { /* Seconds between 1900-01-01 to 1970-01-01 */ #define JAN_1970 (2208988800UL) pj_timestamp ts; pj_status_t status; status = pj_get_timestamp(&ts); /* Fill up the high 32bit part */ ntp->hi = (pj_uint32_t)((ts.u64 - sess->ts_base.u64) / sess->ts_freq.u64) + sess->tv_base.sec + JAN_1970; /* Calculate seconds fractions */ ts.u64 = (ts.u64 - sess->ts_base.u64) % sess->ts_freq.u64; pj_assert(ts.u64 < sess->ts_freq.u64); ts.u64 = (ts.u64 << 32) / sess->ts_freq.u64; /* Fill up the low 32bit part */ ntp->lo = ts.u32.lo; #if (defined(PJ_WIN32) && PJ_WIN32!=0) || \ (defined(PJ_WIN64) && PJ_WIN64!=0) || \ (defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0) /* On Win32, since we use QueryPerformanceCounter() as the backend * timestamp API, we need to protect against this bug: * Performance counter value may unexpectedly leap forward * http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323 */ { /* * Compare elapsed time reported by timestamp with actual elapsed * time. If the difference is too excessive, then we use system * time instead. */ /* MIN_DIFF needs to be large enough so that "normal" diff caused * by system activity or context switch doesn't trigger the time * correction. */ enum { MIN_DIFF = 400 }; pj_time_val ts_time, elapsed, diff; pj_gettimeofday(&elapsed); ts_time.sec = ntp->hi - sess->tv_base.sec - JAN_1970; ts_time.msec = (long)(ntp->lo * 1000.0 / 0xFFFFFFFF); PJ_TIME_VAL_SUB(elapsed, sess->tv_base); if (PJ_TIME_VAL_LT(ts_time, elapsed)) { diff = elapsed; PJ_TIME_VAL_SUB(diff, ts_time); } else { diff = ts_time; PJ_TIME_VAL_SUB(diff, elapsed); } if (PJ_TIME_VAL_MSEC(diff) >= MIN_DIFF) { TRACE_((sess->name, "RTCP NTP timestamp corrected by %d ms", PJ_TIME_VAL_MSEC(diff))); ntp->hi = elapsed.sec + sess->tv_base.sec + JAN_1970; ntp->lo = (elapsed.msec * 65536 / 1000) << 16; } } #endif return status; } /* * Initialize RTCP session setting. */ PJ_DEF(void) pjmedia_rtcp_session_setting_default( pjmedia_rtcp_session_setting *settings) { pj_bzero(settings, sizeof(*settings)); } /* * Initialize bidirectional RTCP statistics. * */ PJ_DEF(void) pjmedia_rtcp_init_stat(pjmedia_rtcp_stat *stat) { pj_time_val now; pj_assert(stat); pj_bzero(stat, sizeof(pjmedia_rtcp_stat)); pj_math_stat_init(&stat->rtt); pj_math_stat_init(&stat->rx.loss_period); pj_math_stat_init(&stat->rx.jitter); pj_math_stat_init(&stat->tx.loss_period); pj_math_stat_init(&stat->tx.jitter); #if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0 pj_math_stat_init(&stat->rx_ipdv); #endif #if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0 pj_math_stat_init(&stat->rx_raw_jitter); #endif pj_gettimeofday(&now); stat->start = now; } /* * Initialize RTCP session. */ PJ_DEF(void) pjmedia_rtcp_init(pjmedia_rtcp_session *sess, char *name, unsigned clock_rate, unsigned samples_per_frame, pj_uint32_t ssrc) { pjmedia_rtcp_session_setting settings; pjmedia_rtcp_session_setting_default(&settings); settings.name = name; settings.clock_rate = clock_rate; settings.samples_per_frame = samples_per_frame; settings.ssrc = ssrc; pjmedia_rtcp_init2(sess, &settings); } /* * Initialize RTCP session. */ PJ_DEF(void) pjmedia_rtcp_init2( pjmedia_rtcp_session *sess, const pjmedia_rtcp_session_setting *settings) { pjmedia_rtcp_sr_pkt *sr_pkt = &sess->rtcp_sr_pkt; pj_time_val now; /* Memset everything */ pj_bzero(sess, sizeof(pjmedia_rtcp_session)); /* Last RX timestamp in RTP packet */ sess->rtp_last_ts = (unsigned)-1; /* Name */ sess->name = settings->name ? settings->name : (char*)THIS_FILE; /* Set clock rate */ sess->clock_rate = settings->clock_rate; sess->pkt_size = settings->samples_per_frame; /* Init common RTCP SR header */ sr_pkt->common.version = 2; sr_pkt->common.count = 1; sr_pkt->common.pt = RTCP_SR; sr_pkt->common.length = pj_htons(12); sr_pkt->common.ssrc = pj_htonl(settings->ssrc); /* Copy to RTCP RR header */ pj_memcpy(&sess->rtcp_rr_pkt.common, &sr_pkt->common, sizeof(pjmedia_rtcp_common)); sess->rtcp_rr_pkt.common.pt = RTCP_RR; sess->rtcp_rr_pkt.common.length = pj_htons(7); /* Get time and timestamp base and frequency */ pj_gettimeofday(&now); sess->tv_base = now; pj_get_timestamp(&sess->ts_base); pj_get_timestamp_freq(&sess->ts_freq); sess->rtp_ts_base = settings->rtp_ts_base; /* Initialize statistics states */ pjmedia_rtcp_init_stat(&sess->stat); /* RR will be initialized on receipt of the first RTP packet. */ } PJ_DEF(void) pjmedia_rtcp_fini(pjmedia_rtcp_session *sess) { #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) pjmedia_rtcp_xr_fini(&sess->xr_session); #else /* Nothing to do. */ PJ_UNUSED_ARG(sess); #endif } static void rtcp_init_seq(pjmedia_rtcp_session *sess) { sess->received = 0; sess->exp_prior = 0; sess->rx_prior = 0; sess->transit = 0; sess->jitter = 0; } PJ_DEF(void) pjmedia_rtcp_rx_rtp( pjmedia_rtcp_session *sess, unsigned seq, unsigned rtp_ts, unsigned payload) { pjmedia_rtcp_rx_rtp2(sess, seq, rtp_ts, payload, PJ_FALSE); } PJ_DEF(void) pjmedia_rtcp_rx_rtp2(pjmedia_rtcp_session *sess, unsigned seq, unsigned rtp_ts, unsigned payload, pj_bool_t discarded) { pj_timestamp ts; pj_uint32_t arrival; pj_int32_t transit; pjmedia_rtp_status seq_st; #if !defined(PJMEDIA_HAS_RTCP_XR) || (PJMEDIA_HAS_RTCP_XR == 0) PJ_UNUSED_ARG(discarded); #endif if (sess->stat.rx.pkt == 0) { /* Init sequence for the first time. */ pjmedia_rtp_seq_init(&sess->seq_ctrl, (pj_uint16_t)seq); } sess->stat.rx.pkt++; sess->stat.rx.bytes += payload; /* Process the RTP packet. */ pjmedia_rtp_seq_update(&sess->seq_ctrl, (pj_uint16_t)seq, &seq_st); if (seq_st.status.flag.restart) { rtcp_init_seq(sess); } if (seq_st.status.flag.dup) { sess->stat.rx.dup++; TRACE_((sess->name, "Duplicate packet detected")); } if (seq_st.status.flag.outorder && !seq_st.status.flag.probation) { sess->stat.rx.reorder++; TRACE_((sess->name, "Out-of-order packet detected")); } if (seq_st.status.flag.bad) { sess->stat.rx.discard++; #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq, -1, /* lost */ (seq_st.status.flag.dup? 1:0), /* dup */ (!seq_st.status.flag.dup? 1:-1), /* discard */ -1, /* jitter */ -1, 0); /* toh */ #endif TRACE_((sess->name, "Bad packet discarded")); return; } /* Only mark "good" packets */ ++sess->received; /* Calculate loss periods. */ if (seq_st.diff > 1) { unsigned count = seq_st.diff - 1; unsigned period; period = count * sess->pkt_size * 1000 / sess->clock_rate; period *= 1000; /* Update packet lost. * The packet lost number will also be updated when we're sending * outbound RTCP RR. */ sess->stat.rx.loss += (seq_st.diff - 1); TRACE_((sess->name, "%d packet(s) lost", seq_st.diff - 1)); /* Update loss period stat */ pj_math_stat_update(&sess->stat.rx.loss_period, period); } /* * Calculate jitter only when sequence is good (see RFC 3550 section A.8), * AND only when the timestamp is different than the last packet * (see RTP FAQ). */ if (seq_st.diff == 1 && rtp_ts != sess->rtp_last_ts) { /* Get arrival time and convert timestamp to samples */ pj_get_timestamp(&ts); ts.u64 = ts.u64 * sess->clock_rate / sess->ts_freq.u64; arrival = ts.u32.lo; transit = arrival - rtp_ts; /* Ignore the first N packets as they normally have bad jitter * due to other threads working to establish the call */ if (sess->transit == 0 || sess->received < PJMEDIA_RTCP_IGNORE_FIRST_PACKETS) { sess->transit = transit; sess->stat.rx.jitter.min = (unsigned)-1; } else { pj_int32_t d; pj_uint32_t jitter; d = transit - sess->transit; if (d < 0) d = -d; sess->jitter += d - ((sess->jitter + 8) >> 4); /* Update jitter stat */ jitter = sess->jitter >> 4; /* Convert jitter unit from samples to usec */ if (jitter < 4294) jitter = jitter * 1000000 / sess->clock_rate; else { jitter = jitter * 1000 / sess->clock_rate; jitter *= 1000; } pj_math_stat_update(&sess->stat.rx.jitter, jitter); #if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0 { pj_uint32_t raw_jitter; /* Convert raw jitter unit from samples to usec */ if (d < 4294) raw_jitter = d * 1000000 / sess->clock_rate; else { raw_jitter = d * 1000 / sess->clock_rate; raw_jitter *= 1000; } /* Update jitter stat */ pj_math_stat_update(&sess->stat.rx_raw_jitter, raw_jitter); } #endif #if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0 { pj_int32_t ipdv; ipdv = transit - sess->transit; /* Convert IPDV unit from samples to usec */ if (ipdv > -2147 && ipdv < 2147) ipdv = ipdv * 1000000 / (int)sess->clock_rate; else { ipdv = ipdv * 1000 / (int)sess->clock_rate; ipdv *= 1000; } /* Update jitter stat */ pj_math_stat_update(&sess->stat.rx_ipdv, ipdv); } #endif #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq, 0, /* lost */ 0, /* dup */ discarded, /* discard */ (sess->jitter >> 4), /* jitter */ -1, 0); /* toh */ #endif /* Update session transit */ sess->transit = transit; } #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) } else if (seq_st.diff > 1) { int i; /* Report RTCP XR about packet losses */ for (i=seq_st.diff-1; i>0; --i) { pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq - i, 1, /* lost */ 0, /* dup */ 0, /* discard */ -1, /* jitter */ -1, 0); /* toh */ } /* Report RTCP XR this packet */ pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq, 0, /* lost */ 0, /* dup */ discarded, /* discard */ -1, /* jitter */ -1, 0); /* toh */ #endif } /* Update timestamp of last RX RTP packet */ sess->rtp_last_ts = rtp_ts; } PJ_DEF(void) pjmedia_rtcp_tx_rtp(pjmedia_rtcp_session *sess, unsigned bytes_payload_size) { /* Update statistics */ sess->stat.tx.pkt++; sess->stat.tx.bytes += bytes_payload_size; } static void parse_rtcp_report( pjmedia_rtcp_session *sess, const void *pkt, pj_size_t size) { pjmedia_rtcp_common *common = (pjmedia_rtcp_common*) pkt; const pjmedia_rtcp_rr *rr = NULL; const pjmedia_rtcp_sr *sr = NULL; pj_uint32_t last_loss, jitter_samp, jitter; /* Parse RTCP */ if (common->pt == RTCP_SR) { sr = (pjmedia_rtcp_sr*) (((char*)pkt) + sizeof(pjmedia_rtcp_common)); if (common->count > 0 && size >= (sizeof(pjmedia_rtcp_sr_pkt))) { rr = (pjmedia_rtcp_rr*)(((char*)pkt) + (sizeof(pjmedia_rtcp_common) + sizeof(pjmedia_rtcp_sr))); } } else if (common->pt == RTCP_RR && common->count > 0) { rr = (pjmedia_rtcp_rr*)(((char*)pkt) + sizeof(pjmedia_rtcp_common)); #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) } else if (common->pt == RTCP_XR) { if (sess->xr_enabled) pjmedia_rtcp_xr_rx_rtcp_xr(&sess->xr_session, pkt, size); return; #endif } if (sr) { /* Save LSR from NTP timestamp of RTCP packet */ sess->rx_lsr = ((pj_ntohl(sr->ntp_sec) & 0x0000FFFF) << 16) | ((pj_ntohl(sr->ntp_frac) >> 16) & 0xFFFF); /* Calculate SR arrival time for DLSR */ pj_get_timestamp(&sess->rx_lsr_time); TRACE_((sess->name, "Rx RTCP SR: ntp_ts=%p", sess->rx_lsr, (pj_uint32_t)(sess->rx_lsr_time.u64*65536/sess->ts_freq.u64))); } /* Nothing more to do if there's no RR packet */ if (rr == NULL) return; last_loss = sess->stat.tx.loss; /* Get packet loss */ sess->stat.tx.loss = (rr->total_lost_2 << 16) + (rr->total_lost_1 << 8) + rr->total_lost_0; TRACE_((sess->name, "Rx RTCP RR: total_lost_2=%x, 1=%x, 0=%x, lost=%d", (int)rr->total_lost_2, (int)rr->total_lost_1, (int)rr->total_lost_0, sess->stat.tx.loss)); /* We can't calculate the exact loss period for TX, so just give the * best estimation. */ if (sess->stat.tx.loss > last_loss) { unsigned period; /* Loss period in msec */ period = (sess->stat.tx.loss - last_loss) * sess->pkt_size * 1000 / sess->clock_rate; /* Loss period in usec */ period *= 1000; /* Update loss period stat */ pj_math_stat_update(&sess->stat.tx.loss_period, period); } /* Get jitter value in usec */ jitter_samp = pj_ntohl(rr->jitter); /* Calculate jitter in usec, avoiding overflows */ if (jitter_samp <= 4294) jitter = jitter_samp * 1000000 / sess->clock_rate; else { jitter = jitter_samp * 1000 / sess->clock_rate; jitter *= 1000; } /* Update jitter statistics */ pj_math_stat_update(&sess->stat.tx.jitter, jitter); /* Can only calculate if LSR and DLSR is present in RR */ if (rr->lsr && rr->dlsr) { pj_uint32_t lsr, now, dlsr; pj_uint64_t eedelay; pjmedia_rtcp_ntp_rec ntp; /* LSR is the middle 32bit of NTP. It has 1/65536 second * resolution */ lsr = pj_ntohl(rr->lsr); /* DLSR is delay since LSR, also in 1/65536 resolution */ dlsr = pj_ntohl(rr->dlsr); /* Get current time, and convert to 1/65536 resolution */ pjmedia_rtcp_get_ntp_time(sess, &ntp); now = ((ntp.hi & 0xFFFF) << 16) + (ntp.lo >> 16); /* End-to-end delay is (now-lsr-dlsr) */ eedelay = now - lsr - dlsr; /* Convert end to end delay to usec (keeping the calculation in * 64bit space):: * sess->ee_delay = (eedelay * 1000) / 65536; */ if (eedelay < 4294) { eedelay = (eedelay * 1000000) >> 16; } else { eedelay = (eedelay * 1000) >> 16; eedelay *= 1000; } TRACE_((sess->name, "Rx RTCP RR: lsr=%p, dlsr=%p (%d:%03dms), " "now=%p, rtt=%p", lsr, dlsr, dlsr/65536, (dlsr%65536)*1000/65536, now, (pj_uint32_t)eedelay)); /* Only save calculation if "now" is greater than lsr, or * otherwise rtt will be invalid */ if (now-dlsr >= lsr) { unsigned rtt = (pj_uint32_t)eedelay; /* Check that eedelay value really makes sense. * We allow up to 30 seconds RTT! */ if (eedelay > 30 * 1000 * 1000UL) { TRACE_((sess->name, "RTT not making any sense, ignored..")); goto end_rtt_calc; } #if defined(PJMEDIA_RTCP_NORMALIZE_FACTOR) && PJMEDIA_RTCP_NORMALIZE_FACTOR!=0 /* "Normalize" rtt value that is exceptionally high. For such * values, "normalize" the rtt to be PJMEDIA_RTCP_NORMALIZE_FACTOR * times the average value. */ if (rtt > ((unsigned)sess->stat.rtt.mean * PJMEDIA_RTCP_NORMALIZE_FACTOR) && sess->stat.rtt.n!=0) { unsigned orig_rtt = rtt; rtt = sess->stat.rtt.mean * PJMEDIA_RTCP_NORMALIZE_FACTOR; PJ_LOG(5,(sess->name, "RTT value %d usec is normalized to %d usec", orig_rtt, rtt)); } #endif TRACE_((sess->name, "RTCP RTT is set to %d usec", rtt)); /* Update RTT stat */ pj_math_stat_update(&sess->stat.rtt, rtt); } else { PJ_LOG(5, (sess->name, "Internal RTCP NTP clock skew detected: " "lsr=%p, now=%p, dlsr=%p (%d:%03dms), " "diff=%d", lsr, now, dlsr, dlsr/65536, (dlsr%65536)*1000/65536, dlsr-(now-lsr))); } } end_rtt_calc: pj_gettimeofday(&sess->stat.tx.update); sess->stat.tx.update_cnt++; } static void parse_rtcp_sdes(pjmedia_rtcp_session *sess, const void *pkt, pj_size_t size) { pjmedia_rtcp_sdes *sdes = &sess->stat.peer_sdes; char *p, *p_end; char *b, *b_end; p = (char*)pkt + 8; p_end = (char*)pkt + size; pj_bzero(sdes, sizeof(*sdes)); b = sess->stat.peer_sdes_buf_; b_end = b + sizeof(sess->stat.peer_sdes_buf_); while (p < p_end) { pj_uint8_t sdes_type, sdes_len; pj_str_t sdes_value = {NULL, 0}; sdes_type = *p++; /* Check for end of SDES item list */ if (sdes_type == RTCP_SDES_NULL || p == p_end) break; sdes_len = *p++; /* Check for corrupted SDES packet */ if (p + sdes_len > p_end) break; /* Get SDES item */ if (b + sdes_len < b_end) { pj_memcpy(b, p, sdes_len); sdes_value.ptr = b; sdes_value.slen = sdes_len; b += sdes_len; } else { /* Insufficient SDES buffer */ PJ_LOG(5, (sess->name, "Unsufficient buffer to save RTCP SDES type %d:%.*s", sdes_type, sdes_len, p)); p += sdes_len; continue; } switch (sdes_type) { case RTCP_SDES_CNAME: sdes->cname = sdes_value; break; case RTCP_SDES_NAME: sdes->name = sdes_value; break; case RTCP_SDES_EMAIL: sdes->email = sdes_value; break; case RTCP_SDES_PHONE: sdes->phone = sdes_value; break; case RTCP_SDES_LOC: sdes->loc = sdes_value; break; case RTCP_SDES_TOOL: sdes->tool = sdes_value; break; case RTCP_SDES_NOTE: sdes->note = sdes_value; break; default: TRACE_((sess->name, "Received unknown RTCP SDES type %d:%.*s", sdes_type, sdes_value.slen, sdes_value.ptr)); break; } p += sdes_len; } } static void parse_rtcp_bye(pjmedia_rtcp_session *sess, const void *pkt, pj_size_t size) { pj_str_t reason = {"-", 1}; /* Check and get BYE reason */ if (size > 8) { reason.slen = PJ_MIN(sizeof(sess->stat.peer_sdes_buf_), *((pj_uint8_t*)pkt+8)); pj_memcpy(sess->stat.peer_sdes_buf_, ((pj_uint8_t*)pkt+9), reason.slen); reason.ptr = sess->stat.peer_sdes_buf_; } /* Just print RTCP BYE log */ PJ_LOG(5, (sess->name, "Received RTCP BYE, reason: %.*s", reason.slen, reason.ptr)); } static void parse_rtcp_psfb(pjmedia_rtcp_session *sess, const void *pkt, pj_size_t size) { pjmedia_rtcp_common *common = (pjmedia_rtcp_common*)pkt; pj_assert(common->pt == RTCP_PSFB); if (common->count == 1) { /* It's a PLI */ PJ_LOG(5, (sess->name, "Received RTCP PLI")); sess->keyframe_requested = PJ_TRUE; } } PJ_DEF(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *sess, const void *pkt, pj_size_t size) { pj_uint8_t *p, *p_end; sess->keyframe_requested = PJ_FALSE; p = (pj_uint8_t*)pkt; p_end = p + size; while (p < p_end) { pjmedia_rtcp_common *common = (pjmedia_rtcp_common*)p; unsigned len; len = (pj_ntohs((pj_uint16_t)common->length)+1) * 4; switch(common->pt) { case RTCP_SR: case RTCP_RR: case RTCP_XR: parse_rtcp_report(sess, p, len); break; case RTCP_SDES: parse_rtcp_sdes(sess, p, len); break; case RTCP_BYE: parse_rtcp_bye(sess, p, len); break; case RTCP_PSFB: parse_rtcp_psfb(sess, p, len); break; default: /* Ignore unknown RTCP */ TRACE_((sess->name, "Received unknown RTCP packet type=%d", common->pt)); break; } p += len; } } PJ_DEF(void) pjmedia_rtcp_build_rtcp(pjmedia_rtcp_session *sess, void **ret_p_pkt, int *len) { pj_uint32_t expected, expected_interval, received_interval, lost_interval; pjmedia_rtcp_sr *sr; pjmedia_rtcp_rr *rr; pj_timestamp ts_now; pjmedia_rtcp_ntp_rec ntp; /* Get current NTP time. */ pj_get_timestamp(&ts_now); pjmedia_rtcp_get_ntp_time(sess, &ntp); /* See if we have transmitted RTP packets since last time we * sent RTCP SR. */ if (sess->stat.tx.pkt != pj_ntohl(sess->rtcp_sr_pkt.sr.sender_pcount)) { pj_time_val ts_time; pj_uint32_t rtp_ts; /* So we should send RTCP SR */ *ret_p_pkt = (void*) &sess->rtcp_sr_pkt; *len = sizeof(pjmedia_rtcp_sr_pkt); rr = &sess->rtcp_sr_pkt.rr; sr = &sess->rtcp_sr_pkt.sr; /* Update packet count */ sr->sender_pcount = pj_htonl(sess->stat.tx.pkt); /* Update octets count */ sr->sender_bcount = pj_htonl(sess->stat.tx.bytes); /* Fill in NTP timestamp in SR. */ sr->ntp_sec = pj_htonl(ntp.hi); sr->ntp_frac = pj_htonl(ntp.lo); /* Fill in RTP timestamp (corresponds to NTP timestamp) in SR. */ ts_time.sec = ntp.hi - sess->tv_base.sec - JAN_1970; ts_time.msec = (long)(ntp.lo * 1000.0 / 0xFFFFFFFF); rtp_ts = sess->rtp_ts_base + (pj_uint32_t)(sess->clock_rate*ts_time.sec) + (pj_uint32_t)(sess->clock_rate*ts_time.msec/1000); sr->rtp_ts = pj_htonl(rtp_ts); TRACE_((sess->name, "TX RTCP SR: ntp_ts=%p", ((ntp.hi & 0xFFFF) << 16) + ((ntp.lo & 0xFFFF0000) >> 16))); } else { /* We should send RTCP RR then */ *ret_p_pkt = (void*) &sess->rtcp_rr_pkt; *len = sizeof(pjmedia_rtcp_rr_pkt); rr = &sess->rtcp_rr_pkt.rr; sr = NULL; } /* SSRC and last_seq */ rr->ssrc = pj_htonl(sess->peer_ssrc); rr->last_seq = (sess->seq_ctrl.cycles & 0xFFFF0000L); /* Since this is an "+=" operation, make sure we update last_seq on * both RR and SR. */ sess->rtcp_sr_pkt.rr.last_seq += sess->seq_ctrl.max_seq; sess->rtcp_rr_pkt.rr.last_seq += sess->seq_ctrl.max_seq; rr->last_seq = pj_htonl(rr->last_seq); /* Jitter */ rr->jitter = pj_htonl(sess->jitter >> 4); /* Total lost. */ expected = pj_ntohl(rr->last_seq) - sess->seq_ctrl.base_seq; /* This is bug: total lost already calculated on each incoming RTP! if (expected >= sess->received) sess->stat.rx.loss = expected - sess->received; else sess->stat.rx.loss = 0; */ rr->total_lost_2 = (sess->stat.rx.loss >> 16) & 0xFF; rr->total_lost_1 = (sess->stat.rx.loss >> 8) & 0xFF; rr->total_lost_0 = (sess->stat.rx.loss & 0xFF); /* Fraction lost calculation */ expected_interval = expected - sess->exp_prior; sess->exp_prior = expected; received_interval = sess->received - sess->rx_prior; sess->rx_prior = sess->received; if (expected_interval >= received_interval) lost_interval = expected_interval - received_interval; else lost_interval = 0; if (expected_interval==0 || lost_interval == 0) { rr->fract_lost = 0; } else { rr->fract_lost = (lost_interval << 8) / expected_interval; } if (sess->rx_lsr_time.u64 == 0 || sess->rx_lsr == 0) { rr->lsr = 0; rr->dlsr = 0; } else { pj_timestamp ts; pj_uint32_t lsr = sess->rx_lsr; pj_uint64_t lsr_time = sess->rx_lsr_time.u64; pj_uint32_t dlsr; /* Convert LSR time to 1/65536 seconds resolution */ lsr_time = (lsr_time << 16) / sess->ts_freq.u64; /* Fill in LSR. LSR is the middle 32bit of the last SR NTP time received. */ rr->lsr = pj_htonl(lsr); /* Fill in DLSR. DLSR is Delay since Last SR, in 1/65536 seconds. */ ts.u64 = ts_now.u64; /* Convert interval to 1/65536 seconds value */ ts.u64 = (ts.u64 << 16) / sess->ts_freq.u64; /* Get DLSR */ dlsr = (pj_uint32_t)(ts.u64 - lsr_time); rr->dlsr = pj_htonl(dlsr); TRACE_((sess->name,"Tx RTCP RR: lsr=%p, lsr_time=%p, now=%p, dlsr=%p" "(%ds:%03dms)", lsr, (pj_uint32_t)lsr_time, (pj_uint32_t)ts.u64, dlsr, dlsr/65536, (dlsr%65536)*1000/65536 )); } /* Update counter */ pj_gettimeofday(&sess->stat.rx.update); sess->stat.rx.update_cnt++; } PJ_DEF(pj_status_t) pjmedia_rtcp_build_rtcp_sdes( pjmedia_rtcp_session *session, void *buf, pj_size_t *length, const pjmedia_rtcp_sdes *sdes) { pjmedia_rtcp_common *hdr; pj_uint8_t *p; pj_size_t len; PJ_ASSERT_RETURN(session && buf && length && sdes, PJ_EINVAL); /* Verify SDES item length */ if (sdes->cname.slen > 255 || sdes->name.slen > 255 || sdes->email.slen > 255 || sdes->phone.slen > 255 || sdes->loc.slen > 255 || sdes->tool.slen > 255 || sdes->note.slen > 255) { return PJ_EINVAL; } /* Verify buffer length */ len = sizeof(*hdr); if (sdes->cname.slen) len += sdes->cname.slen + 2; if (sdes->name.slen) len += sdes->name.slen + 2; if (sdes->email.slen) len += sdes->email.slen + 2; if (sdes->phone.slen) len += sdes->phone.slen + 2; if (sdes->loc.slen) len += sdes->loc.slen + 2; if (sdes->tool.slen) len += sdes->tool.slen + 2; if (sdes->note.slen) len += sdes->note.slen + 2; len++; /* null termination */ len = ((len+3)/4) * 4; if (len > *length) return PJ_ETOOSMALL; /* Build RTCP SDES header */ hdr = (pjmedia_rtcp_common*)buf; pj_memcpy(hdr, &session->rtcp_sr_pkt.common, sizeof(*hdr)); hdr->pt = RTCP_SDES; hdr->length = pj_htons((pj_uint16_t)(len/4 - 1)); /* Build RTCP SDES items */ p = (pj_uint8_t*)hdr + sizeof(*hdr); #define BUILD_SDES_ITEM(SDES_NAME, SDES_TYPE) \ if (sdes->SDES_NAME.slen) { \ *p++ = SDES_TYPE; \ *p++ = (pj_uint8_t)sdes->SDES_NAME.slen; \ pj_memcpy(p, sdes->SDES_NAME.ptr, sdes->SDES_NAME.slen); \ p += sdes->SDES_NAME.slen; \ } BUILD_SDES_ITEM(cname, RTCP_SDES_CNAME); BUILD_SDES_ITEM(name, RTCP_SDES_NAME); BUILD_SDES_ITEM(email, RTCP_SDES_EMAIL); BUILD_SDES_ITEM(phone, RTCP_SDES_PHONE); BUILD_SDES_ITEM(loc, RTCP_SDES_LOC); BUILD_SDES_ITEM(tool, RTCP_SDES_TOOL); BUILD_SDES_ITEM(note, RTCP_SDES_NOTE); #undef BUILD_SDES_ITEM /* Null termination */ *p++ = 0; /* Pad to 32bit */ while ((p-(pj_uint8_t*)buf) % 4) *p++ = 0; /* Finally */ pj_assert((int)len == p-(pj_uint8_t*)buf); *length = len; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_rtcp_build_rtcp_bye(pjmedia_rtcp_session *session, void *buf, pj_size_t *length, const pj_str_t *reason) { pjmedia_rtcp_common *hdr; pj_uint8_t *p; pj_size_t len; PJ_ASSERT_RETURN(session && buf && length, PJ_EINVAL); /* Verify BYE reason length */ if (reason && reason->slen > 255) return PJ_EINVAL; /* Verify buffer length */ len = sizeof(*hdr); if (reason && reason->slen) len += reason->slen + 1; len = ((len+3)/4) * 4; if (len > *length) return PJ_ETOOSMALL; /* Build RTCP BYE header */ hdr = (pjmedia_rtcp_common*)buf; pj_memcpy(hdr, &session->rtcp_sr_pkt.common, sizeof(*hdr)); hdr->pt = RTCP_BYE; hdr->length = pj_htons((pj_uint16_t)(len/4 - 1)); /* Write RTCP BYE reason */ p = (pj_uint8_t*)hdr + sizeof(*hdr); if (reason && reason->slen) { *p++ = (pj_uint8_t)reason->slen; pj_memcpy(p, reason->ptr, reason->slen); p += reason->slen; } /* Pad to 32bit */ while ((p-(pj_uint8_t*)buf) % 4) *p++ = 0; pj_assert((int)len == p-(pj_uint8_t*)buf); *length = len; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_rtcp_build_rtcp_pli(pjmedia_rtcp_session *session, void *buf, pj_size_t *length) { pjmedia_rtcp_common *hdr; pj_uint8_t *p; pj_size_t len = 12; /* pjmedia_rtcp_common + media SSRC (uint32_t) */ PJ_ASSERT_RETURN(session && buf && length, PJ_EINVAL); /* Verify buffer length */ if (len > *length) return PJ_ETOOSMALL; /* Build RTCP PLI */ hdr = (pjmedia_rtcp_common*)buf; pj_memcpy(hdr, &session->rtcp_sr_pkt.common, sizeof(*hdr)); hdr->pt = RTCP_PSFB; hdr->count = 1; /* FMT: 1 == Picture Loss Indication (PLI) */ hdr->length = pj_htons((pj_uint16_t)(len/4 - 1)); p = (pj_uint8_t*)hdr + sizeof(*hdr); pj_memset(p, 0, (pj_uint8_t*)hdr + len - p); *length = len; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_rtcp_enable_xr( pjmedia_rtcp_session *sess, pj_bool_t enable) { #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) /* Check if request won't change anything */ if (!(enable ^ sess->xr_enabled)) return PJ_SUCCESS; if (!enable) { sess->xr_enabled = PJ_FALSE; return PJ_SUCCESS; } pjmedia_rtcp_xr_init(&sess->xr_session, sess, 0, 1); sess->xr_enabled = PJ_TRUE; return PJ_SUCCESS; #else PJ_UNUSED_ARG(sess); PJ_UNUSED_ARG(enable); return PJ_ENOTSUP; #endif } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/rtcp_xr.c ================================================ /* $Id: rtcp_xr.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) #define THIS_FILE "rtcp_xr.c" #if PJ_HAS_HIGH_RES_TIMER==0 # error "High resolution timer needs to be enabled" #endif /* RTCP XR payload type */ #define RTCP_XR 207 /* RTCP XR block types */ #define BT_LOSS_RLE 1 #define BT_DUP_RLE 2 #define BT_RCPT_TIMES 3 #define BT_RR_TIME 4 #define BT_DLRR 5 #define BT_STATS 6 #define BT_VOIP_METRICS 7 #define DEFAULT_GMIN 16 #if 0 # define TRACE_(x) PJ_LOG(3,x) #else # define TRACE_(x) ; #endif void pjmedia_rtcp_xr_init( pjmedia_rtcp_xr_session *session, struct pjmedia_rtcp_session *parent_session, pj_uint8_t gmin, unsigned frames_per_packet) { pj_bzero(session, sizeof(pjmedia_rtcp_xr_session)); session->name = parent_session->name; session->rtcp_session = parent_session; pj_memcpy(&session->pkt.common, &session->rtcp_session->rtcp_sr_pkt.common, sizeof(pjmedia_rtcp_common)); session->pkt.common.pt = RTCP_XR; /* Init config */ session->stat.rx.voip_mtc.gmin = (pj_uint8_t)(gmin? gmin : DEFAULT_GMIN); session->ptime = session->rtcp_session->pkt_size * 1000 / session->rtcp_session->clock_rate; session->frames_per_packet = frames_per_packet; /* Init Statistics Summary fields which have non-zero default */ session->stat.rx.stat_sum.jitter.min = (unsigned) -1; session->stat.rx.stat_sum.toh.min = (unsigned) -1; /* Init VoIP Metrics fields which have non-zero default */ session->stat.rx.voip_mtc.signal_lvl = 127; session->stat.rx.voip_mtc.noise_lvl = 127; session->stat.rx.voip_mtc.rerl = 127; session->stat.rx.voip_mtc.r_factor = 127; session->stat.rx.voip_mtc.ext_r_factor = 127; session->stat.rx.voip_mtc.mos_lq = 127; session->stat.rx.voip_mtc.mos_cq = 127; session->stat.tx.voip_mtc.signal_lvl = 127; session->stat.tx.voip_mtc.noise_lvl = 127; session->stat.tx.voip_mtc.rerl = 127; session->stat.tx.voip_mtc.r_factor = 127; session->stat.tx.voip_mtc.ext_r_factor = 127; session->stat.tx.voip_mtc.mos_lq = 127; session->stat.tx.voip_mtc.mos_cq = 127; } void pjmedia_rtcp_xr_fini(pjmedia_rtcp_xr_session *session) { PJ_UNUSED_ARG(session); } PJ_DEF(void) pjmedia_rtcp_build_rtcp_xr( pjmedia_rtcp_xr_session *sess, unsigned rpt_types, void **rtcp_pkt, int *len) { pj_uint16_t size = 0; /* Receiver Reference Time Report Block */ /* Build this block if we have received packets since last build */ if ((rpt_types == 0 || (rpt_types & PJMEDIA_RTCP_XR_RR_TIME)) && sess->rx_last_rr != sess->rtcp_session->stat.rx.pkt) { pjmedia_rtcp_xr_rb_rr_time *r; pjmedia_rtcp_ntp_rec ntp; r = (pjmedia_rtcp_xr_rb_rr_time*) &sess->pkt.buf[size]; pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_rr_time)); /* Init block header */ r->header.bt = BT_RR_TIME; r->header.specific = 0; r->header.length = pj_htons(2); /* Generate block contents */ pjmedia_rtcp_get_ntp_time(sess->rtcp_session, &ntp); r->ntp_sec = pj_htonl(ntp.hi); r->ntp_frac = pj_htonl(ntp.lo); /* Finally */ size += sizeof(pjmedia_rtcp_xr_rb_rr_time); sess->rx_last_rr = sess->rtcp_session->stat.rx.pkt; } /* DLRR Report Block */ /* Build this block if we have received RR NTP (rx_lrr) before */ if ((rpt_types == 0 || (rpt_types & PJMEDIA_RTCP_XR_DLRR)) && sess->rx_lrr) { pjmedia_rtcp_xr_rb_dlrr *r; pjmedia_rtcp_xr_rb_dlrr_item *dlrr_item; pj_timestamp ts; r = (pjmedia_rtcp_xr_rb_dlrr*) &sess->pkt.buf[size]; pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_dlrr)); /* Init block header */ r->header.bt = BT_DLRR; r->header.specific = 0; r->header.length = pj_htons(sizeof(pjmedia_rtcp_xr_rb_dlrr)/4 - 1); /* Generate block contents */ dlrr_item = &r->item; dlrr_item->ssrc = pj_htonl(sess->rtcp_session->peer_ssrc); dlrr_item->lrr = pj_htonl(sess->rx_lrr); /* Calculate DLRR */ if (sess->rx_lrr != 0) { pj_get_timestamp(&ts); ts.u64 -= sess->rx_lrr_time.u64; /* Convert DLRR time to 1/65536 seconds resolution */ ts.u64 = (ts.u64 << 16) / sess->rtcp_session->ts_freq.u64; dlrr_item->dlrr = pj_htonl(ts.u32.lo); } else { dlrr_item->dlrr = 0; } /* Finally */ size += sizeof(pjmedia_rtcp_xr_rb_dlrr); } /* Statistics Summary Block */ /* Build this block if we have received packets since last build */ if ((rpt_types == 0 || (rpt_types & PJMEDIA_RTCP_XR_STATS)) && sess->stat.rx.stat_sum.count > 0) { pjmedia_rtcp_xr_rb_stats *r; pj_uint8_t specific = 0; r = (pjmedia_rtcp_xr_rb_stats*) &sess->pkt.buf[size]; pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_stats)); /* Init block header */ specific |= sess->stat.rx.stat_sum.l ? (1 << 7) : 0; specific |= sess->stat.rx.stat_sum.d ? (1 << 6) : 0; specific |= sess->stat.rx.stat_sum.j ? (1 << 5) : 0; specific |= (sess->stat.rx.stat_sum.t & 3) << 3; r->header.bt = BT_STATS; r->header.specific = specific; r->header.length = pj_htons(9); /* Generate block contents */ r->ssrc = pj_htonl(sess->rtcp_session->peer_ssrc); r->begin_seq = pj_htons((pj_uint16_t) (sess->stat.rx.stat_sum.begin_seq & 0xFFFF)); r->end_seq = pj_htons((pj_uint16_t) (sess->stat.rx.stat_sum.end_seq & 0xFFFF)); if (sess->stat.rx.stat_sum.l) { r->lost = pj_htonl(sess->stat.rx.stat_sum.lost); } if (sess->stat.rx.stat_sum.d) { r->dup = pj_htonl(sess->stat.rx.stat_sum.dup); } if (sess->stat.rx.stat_sum.j) { r->jitter_min = pj_htonl(sess->stat.rx.stat_sum.jitter.min); r->jitter_max = pj_htonl(sess->stat.rx.stat_sum.jitter.max); r->jitter_mean = pj_htonl((unsigned)sess->stat.rx.stat_sum.jitter.mean); r->jitter_dev = pj_htonl(pj_math_stat_get_stddev(&sess->stat.rx.stat_sum.jitter)); } if (sess->stat.rx.stat_sum.t) { r->toh_min = sess->stat.rx.stat_sum.toh.min; r->toh_max = sess->stat.rx.stat_sum.toh.max; r->toh_mean = (unsigned) sess->stat.rx.stat_sum.toh.mean; r->toh_dev = pj_math_stat_get_stddev(&sess->stat.rx.stat_sum.toh); } /* Reset TX statistics summary each time built */ pj_bzero(&sess->stat.rx.stat_sum, sizeof(sess->stat.rx.stat_sum)); sess->stat.rx.stat_sum.jitter.min = (unsigned) -1; sess->stat.rx.stat_sum.toh.min = (unsigned) -1; /* Finally */ size += sizeof(pjmedia_rtcp_xr_rb_stats); pj_gettimeofday(&sess->stat.rx.stat_sum.update); } /* Voip Metrics Block */ /* Build this block if we have received packets */ if ((rpt_types == 0 || (rpt_types & PJMEDIA_RTCP_XR_VOIP_METRICS)) && sess->rtcp_session->stat.rx.pkt) { pjmedia_rtcp_xr_rb_voip_mtc *r; pj_uint32_t c11; pj_uint32_t c13; pj_uint32_t c14; pj_uint32_t c22; pj_uint32_t c23; pj_uint32_t c31; pj_uint32_t c32; pj_uint32_t c33; pj_uint32_t ctotal, m; unsigned est_extra_delay; r = (pjmedia_rtcp_xr_rb_voip_mtc*) &sess->pkt.buf[size]; pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_voip_mtc)); /* Init block header */ r->header.bt = BT_VOIP_METRICS; r->header.specific = 0; r->header.length = pj_htons(8); /* Use temp vars for easiness. */ c11 = sess->voip_mtc_stat.c11; c13 = sess->voip_mtc_stat.c13; c14 = sess->voip_mtc_stat.c14; c22 = sess->voip_mtc_stat.c22; c23 = sess->voip_mtc_stat.c23; c33 = sess->voip_mtc_stat.c33; m = sess->ptime * sess->frames_per_packet; /* Calculate additional transition counts. */ c31 = c13; c32 = c23; ctotal = c11 + c14 + c13 + c22 + c23 + c31 + c32 + c33; if (ctotal) { pj_uint32_t p32, p23; //original version: //p32 = c32 / (c31 + c32 + c33); if (c31 + c32 + c33 == 0) p32 = 0; else p32 = (c32 << 16) / (c31 + c32 + c33); //original version: //if ((c22 + c23) < 1) { // p23 = 1; //} else { // p23 = 1 - c22 / (c22 + c23); //} if (c23 == 0) { p23 = 0; } else { p23 = (c23 << 16) / (c22 + c23); } /* Calculate loss/discard densities, scaled of 0-256 */ if (c11 == 0) sess->stat.rx.voip_mtc.gap_den = 0; else sess->stat.rx.voip_mtc.gap_den = (pj_uint8_t) ((c14 << 8) / (c11 + c14)); if (p23 == 0) sess->stat.rx.voip_mtc.burst_den = 0; else sess->stat.rx.voip_mtc.burst_den = (pj_uint8_t) ((p23 << 8) / (p23 + p32)); /* Calculate (average) durations, in ms */ if (c13 == 0) { c13 = 1; ctotal += 1; } sess->stat.rx.voip_mtc.gap_dur = (pj_uint16_t) ((c11+c14+c13) * m / c13); sess->stat.rx.voip_mtc.burst_dur = (pj_uint16_t) ((ctotal - (c11+c14+c13)) * m / c13); /* Callculate loss/discard rates, scaled 0-256 */ sess->stat.rx.voip_mtc.loss_rate = (pj_uint8_t) ((sess->voip_mtc_stat.loss_count << 8) / ctotal); sess->stat.rx.voip_mtc.discard_rate = (pj_uint8_t) ((sess->voip_mtc_stat.discard_count << 8) / ctotal); } else { /* No lost/discarded packet yet. */ sess->stat.rx.voip_mtc.gap_den = 0; sess->stat.rx.voip_mtc.burst_den = 0; sess->stat.rx.voip_mtc.gap_dur = 0; sess->stat.rx.voip_mtc.burst_dur = 0; sess->stat.rx.voip_mtc.loss_rate = 0; sess->stat.rx.voip_mtc.discard_rate = 0; } /* Set round trip delay (in ms) to RTT calculated after receiving * DLRR or DLSR. */ if (sess->stat.rtt.last) sess->stat.rx.voip_mtc.rnd_trip_delay = (pj_uint16_t) (sess->stat.rtt.last / 1000); else if (sess->rtcp_session->stat.rtt.last) sess->stat.rx.voip_mtc.rnd_trip_delay = (pj_uint16_t) (sess->rtcp_session->stat.rtt.last / 1000); /* End system delay = RTT/2 + current jitter buffer size + * EXTRA (estimated extra delay) * EXTRA will cover additional delay introduced by other components of * audio engine, e.g: sound device, codec, AEC, PLC, WSOLA. * Since it is difficult to get the exact value of EXTRA, estimation * is taken to be totally around 30ms + sound device latency. */ est_extra_delay = 30; #if PJMEDIA_SOUND_IMPLEMENTATION!=PJMEDIA_SOUND_NULL_SOUND est_extra_delay += PJMEDIA_SND_DEFAULT_REC_LATENCY + PJMEDIA_SND_DEFAULT_PLAY_LATENCY; #endif sess->stat.rx.voip_mtc.end_sys_delay = (pj_uint16_t) (sess->stat.rx.voip_mtc.rnd_trip_delay / 2 + sess->stat.rx.voip_mtc.jb_nom + est_extra_delay); /* Generate block contents */ r->ssrc = pj_htonl(sess->rtcp_session->peer_ssrc); r->loss_rate = sess->stat.rx.voip_mtc.loss_rate; r->discard_rate = sess->stat.rx.voip_mtc.discard_rate; r->burst_den = sess->stat.rx.voip_mtc.burst_den; r->gap_den = sess->stat.rx.voip_mtc.gap_den; r->burst_dur = pj_htons(sess->stat.rx.voip_mtc.burst_dur); r->gap_dur = pj_htons(sess->stat.rx.voip_mtc.gap_dur); r->rnd_trip_delay = pj_htons(sess->stat.rx.voip_mtc.rnd_trip_delay); r->end_sys_delay = pj_htons(sess->stat.rx.voip_mtc.end_sys_delay); /* signal & noise level encoded in two's complement form */ r->signal_lvl = (pj_uint8_t) ((sess->stat.rx.voip_mtc.signal_lvl >= 0)? sess->stat.rx.voip_mtc.signal_lvl : (sess->stat.rx.voip_mtc.signal_lvl + 256)); r->noise_lvl = (pj_uint8_t) ((sess->stat.rx.voip_mtc.noise_lvl >= 0)? sess->stat.rx.voip_mtc.noise_lvl : (sess->stat.rx.voip_mtc.noise_lvl + 256)); r->rerl = sess->stat.rx.voip_mtc.rerl; r->gmin = sess->stat.rx.voip_mtc.gmin; r->r_factor = sess->stat.rx.voip_mtc.r_factor; r->ext_r_factor = sess->stat.rx.voip_mtc.ext_r_factor; r->mos_lq = sess->stat.rx.voip_mtc.mos_lq; r->mos_cq = sess->stat.rx.voip_mtc.mos_cq; r->rx_config = sess->stat.rx.voip_mtc.rx_config; r->jb_nom = pj_htons(sess->stat.rx.voip_mtc.jb_nom); r->jb_max = pj_htons(sess->stat.rx.voip_mtc.jb_max); r->jb_abs_max = pj_htons(sess->stat.rx.voip_mtc.jb_abs_max); /* Finally */ size += sizeof(pjmedia_rtcp_xr_rb_voip_mtc); pj_gettimeofday(&sess->stat.rx.voip_mtc.update); } /* Add RTCP XR header size */ size += sizeof(sess->pkt.common); /* Set RTCP XR header 'length' to packet size in 32-bit unit minus one */ sess->pkt.common.length = pj_htons((pj_uint16_t)(size/4 - 1)); /* Set the return values */ *rtcp_pkt = (void*) &sess->pkt; *len = size; } void pjmedia_rtcp_xr_rx_rtcp_xr( pjmedia_rtcp_xr_session *sess, const void *pkt, pj_size_t size) { const pjmedia_rtcp_xr_pkt *rtcp_xr = (pjmedia_rtcp_xr_pkt*) pkt; const pjmedia_rtcp_xr_rb_rr_time *rb_rr_time = NULL; const pjmedia_rtcp_xr_rb_dlrr *rb_dlrr = NULL; const pjmedia_rtcp_xr_rb_stats *rb_stats = NULL; const pjmedia_rtcp_xr_rb_voip_mtc *rb_voip_mtc = NULL; const pjmedia_rtcp_xr_rb_header *rb_hdr = (pjmedia_rtcp_xr_rb_header*) rtcp_xr->buf; unsigned pkt_len, rb_len; if (rtcp_xr->common.pt != RTCP_XR) return; pkt_len = pj_ntohs((pj_uint16_t)rtcp_xr->common.length); if ((pkt_len + 1) > (size / 4)) return; /* Parse report rpt_types */ while ((pj_int32_t*)rb_hdr < (pj_int32_t*)pkt + pkt_len) { rb_len = pj_ntohs((pj_uint16_t)rb_hdr->length); /* Just skip any block with length == 0 (no report content) */ if (rb_len) { switch (rb_hdr->bt) { case BT_RR_TIME: rb_rr_time = (pjmedia_rtcp_xr_rb_rr_time*) rb_hdr; break; case BT_DLRR: rb_dlrr = (pjmedia_rtcp_xr_rb_dlrr*) rb_hdr; break; case BT_STATS: rb_stats = (pjmedia_rtcp_xr_rb_stats*) rb_hdr; break; case BT_VOIP_METRICS: rb_voip_mtc = (pjmedia_rtcp_xr_rb_voip_mtc*) rb_hdr; break; default: break; } } rb_hdr = (pjmedia_rtcp_xr_rb_header*) ((pj_int32_t*)rb_hdr + rb_len + 1); } /* Receiving RR Time */ if (rb_rr_time) { /* Save LRR from NTP timestamp of the RR time block report */ sess->rx_lrr = ((pj_ntohl(rb_rr_time->ntp_sec) & 0x0000FFFF) << 16) | ((pj_ntohl(rb_rr_time->ntp_frac) >> 16) & 0xFFFF); /* Calculate RR arrival time for DLRR */ pj_get_timestamp(&sess->rx_lrr_time); TRACE_((sess->name, "Rx RTCP SR: ntp_ts=%p", sess->rx_lrr, (pj_uint32_t)(sess->rx_lrr_time.u64*65536/ sess->rtcp_session->ts_freq.u64))); } /* Receiving DLRR */ if (rb_dlrr) { pj_uint32_t lrr, now, dlrr; pj_uint64_t eedelay; pjmedia_rtcp_ntp_rec ntp; /* LRR is the middle 32bit of NTP. It has 1/65536 second * resolution */ lrr = pj_ntohl(rb_dlrr->item.lrr); /* DLRR is delay since LRR, also in 1/65536 resolution */ dlrr = pj_ntohl(rb_dlrr->item.dlrr); /* Get current time, and convert to 1/65536 resolution */ pjmedia_rtcp_get_ntp_time(sess->rtcp_session, &ntp); now = ((ntp.hi & 0xFFFF) << 16) + (ntp.lo >> 16); /* End-to-end delay is (now-lrr-dlrr) */ eedelay = now - lrr - dlrr; /* Convert end to end delay to usec (keeping the calculation in * 64bit space):: * sess->ee_delay = (eedelay * 1000) / 65536; */ if (eedelay < 4294) { eedelay = (eedelay * 1000000) >> 16; } else { eedelay = (eedelay * 1000) >> 16; eedelay *= 1000; } TRACE_((sess->name, "Rx RTCP XR DLRR: lrr=%p, dlrr=%p (%d:%03dms), " "now=%p, rtt=%p", lrr, dlrr, dlrr/65536, (dlrr%65536)*1000/65536, now, (pj_uint32_t)eedelay)); /* Only save calculation if "now" is greater than lrr, or * otherwise rtt will be invalid */ if (now-dlrr >= lrr) { unsigned rtt = (pj_uint32_t)eedelay; /* Check that eedelay value really makes sense. * We allow up to 30 seconds RTT! */ if (eedelay <= 30 * 1000 * 1000UL) { /* "Normalize" rtt value that is exceptionally high. * For such values, "normalize" the rtt to be three times * the average value. */ if (rtt>((unsigned)sess->stat.rtt.mean*3) && sess->stat.rtt.n!=0) { unsigned orig_rtt = rtt; rtt = (unsigned)sess->stat.rtt.mean*3; PJ_LOG(5,(sess->name, "RTT value %d usec is normalized to %d usec", orig_rtt, rtt)); } TRACE_((sess->name, "RTCP RTT is set to %d usec", rtt)); pj_math_stat_update(&sess->stat.rtt, rtt); } } else { PJ_LOG(5, (sess->name, "Internal RTCP NTP clock skew detected: " "lrr=%p, now=%p, dlrr=%p (%d:%03dms), " "diff=%d", lrr, now, dlrr, dlrr/65536, (dlrr%65536)*1000/65536, dlrr-(now-lrr))); } } /* Receiving Statistics Summary */ if (rb_stats) { pj_uint8_t flags = rb_stats->header.specific; pj_bzero(&sess->stat.tx.stat_sum, sizeof(sess->stat.tx.stat_sum)); /* Range of packets sequence reported in this blocks */ sess->stat.tx.stat_sum.begin_seq = pj_ntohs(rb_stats->begin_seq); sess->stat.tx.stat_sum.end_seq = pj_ntohs(rb_stats->end_seq); /* Get flags of valid fields */ sess->stat.tx.stat_sum.l = (flags & (1 << 7)) != 0; sess->stat.tx.stat_sum.d = (flags & (1 << 6)) != 0; sess->stat.tx.stat_sum.j = (flags & (1 << 5)) != 0; sess->stat.tx.stat_sum.t = (flags & (3 << 3)) != 0; /* Fetch the reports info */ if (sess->stat.tx.stat_sum.l) { sess->stat.tx.stat_sum.lost = pj_ntohl(rb_stats->lost); } if (sess->stat.tx.stat_sum.d) { sess->stat.tx.stat_sum.dup = pj_ntohl(rb_stats->dup); } if (sess->stat.tx.stat_sum.j) { sess->stat.tx.stat_sum.jitter.min = pj_ntohl(rb_stats->jitter_min); sess->stat.tx.stat_sum.jitter.max = pj_ntohl(rb_stats->jitter_max); sess->stat.tx.stat_sum.jitter.mean= pj_ntohl(rb_stats->jitter_mean); pj_math_stat_set_stddev(&sess->stat.tx.stat_sum.jitter, pj_ntohl(rb_stats->jitter_dev)); } if (sess->stat.tx.stat_sum.t) { sess->stat.tx.stat_sum.toh.min = rb_stats->toh_min; sess->stat.tx.stat_sum.toh.max = rb_stats->toh_max; sess->stat.tx.stat_sum.toh.mean= rb_stats->toh_mean; pj_math_stat_set_stddev(&sess->stat.tx.stat_sum.toh, pj_ntohl(rb_stats->toh_dev)); } pj_gettimeofday(&sess->stat.tx.stat_sum.update); } /* Receiving VoIP Metrics */ if (rb_voip_mtc) { sess->stat.tx.voip_mtc.loss_rate = rb_voip_mtc->loss_rate; sess->stat.tx.voip_mtc.discard_rate = rb_voip_mtc->discard_rate; sess->stat.tx.voip_mtc.burst_den = rb_voip_mtc->burst_den; sess->stat.tx.voip_mtc.gap_den = rb_voip_mtc->gap_den; sess->stat.tx.voip_mtc.burst_dur = pj_ntohs(rb_voip_mtc->burst_dur); sess->stat.tx.voip_mtc.gap_dur = pj_ntohs(rb_voip_mtc->gap_dur); sess->stat.tx.voip_mtc.rnd_trip_delay = pj_ntohs(rb_voip_mtc->rnd_trip_delay); sess->stat.tx.voip_mtc.end_sys_delay = pj_ntohs(rb_voip_mtc->end_sys_delay); /* signal & noise level encoded in two's complement form */ sess->stat.tx.voip_mtc.signal_lvl = (pj_int8_t) ((rb_voip_mtc->signal_lvl > 127)? ((int)rb_voip_mtc->signal_lvl - 256) : rb_voip_mtc->signal_lvl); sess->stat.tx.voip_mtc.noise_lvl = (pj_int8_t) ((rb_voip_mtc->noise_lvl > 127)? ((int)rb_voip_mtc->noise_lvl - 256) : rb_voip_mtc->noise_lvl); sess->stat.tx.voip_mtc.rerl = rb_voip_mtc->rerl; sess->stat.tx.voip_mtc.gmin = rb_voip_mtc->gmin; sess->stat.tx.voip_mtc.r_factor = rb_voip_mtc->r_factor; sess->stat.tx.voip_mtc.ext_r_factor = rb_voip_mtc->ext_r_factor; sess->stat.tx.voip_mtc.mos_lq = rb_voip_mtc->mos_lq; sess->stat.tx.voip_mtc.mos_cq = rb_voip_mtc->mos_cq; sess->stat.tx.voip_mtc.rx_config = rb_voip_mtc->rx_config; sess->stat.tx.voip_mtc.jb_nom = pj_ntohs(rb_voip_mtc->jb_nom); sess->stat.tx.voip_mtc.jb_max = pj_ntohs(rb_voip_mtc->jb_max); sess->stat.tx.voip_mtc.jb_abs_max = pj_ntohs(rb_voip_mtc->jb_abs_max); pj_gettimeofday(&sess->stat.tx.voip_mtc.update); } } /* Place seq into a 32-bit sequence number space based upon a * heuristic for its most likely location. */ static pj_uint32_t extend_seq(pjmedia_rtcp_xr_session *sess, const pj_uint16_t seq) { pj_uint32_t extended_seq, seq_a, seq_b, diff_a, diff_b; if(sess->uninitialized_src_ref_seq) { /* This is the first sequence number received. Place * it in the middle of the extended sequence number * space. */ sess->src_ref_seq = seq | 0x80000000u; sess->uninitialized_src_ref_seq = PJ_FALSE; extended_seq = sess->src_ref_seq; } else { /* Prior sequence numbers have been received. * Propose two candidates for the extended sequence * number: seq_a is without wraparound, seq_b with * wraparound. */ seq_a = seq | (sess->src_ref_seq & 0xFFFF0000u); if(sess->src_ref_seq < seq_a) { seq_b = seq_a - 0x00010000u; diff_a = seq_a - sess->src_ref_seq; diff_b = sess->src_ref_seq - seq_b; } else { seq_b = seq_a + 0x00010000u; diff_a = sess->src_ref_seq - seq_a; diff_b = seq_b - sess->src_ref_seq; } /* Choose the closer candidate. If they are equally * close, the choice is somewhat arbitrary: we choose * the candidate for which no rollover is necessary. */ if(diff_a < diff_b) { extended_seq = seq_a; } else { extended_seq = seq_b; } /* Set the reference sequence number to be this most * recently-received sequence number. */ sess->src_ref_seq = extended_seq; } /* Return our best guess for a 32-bit sequence number that * corresponds to the 16-bit number we were given. */ return extended_seq; } void pjmedia_rtcp_xr_rx_rtp( pjmedia_rtcp_xr_session *sess, unsigned seq, int lost, int dup, int discarded, int jitter, int toh, pj_bool_t toh_ipv4) { pj_uint32_t ext_seq; /* Get 32 bit version of sequence */ ext_seq = extend_seq(sess, (pj_uint16_t)seq); /* Update statistics summary */ sess->stat.rx.stat_sum.count++; if (sess->stat.rx.stat_sum.begin_seq == 0 || sess->stat.rx.stat_sum.begin_seq > ext_seq) { sess->stat.rx.stat_sum.begin_seq = ext_seq; } if (sess->stat.rx.stat_sum.end_seq == 0 || sess->stat.rx.stat_sum.end_seq < ext_seq) { sess->stat.rx.stat_sum.end_seq = ext_seq; } if (lost >= 0) { sess->stat.rx.stat_sum.l = PJ_TRUE; if (lost > 0) sess->stat.rx.stat_sum.lost++; } if (dup >= 0) { sess->stat.rx.stat_sum.d = PJ_TRUE; if (dup > 0) sess->stat.rx.stat_sum.dup++; } if (jitter >= 0) { sess->stat.rx.stat_sum.j = PJ_TRUE; pj_math_stat_update(&sess->stat.rx.stat_sum.jitter, jitter); } if (toh >= 0) { sess->stat.rx.stat_sum.t = toh_ipv4? 1 : 2; pj_math_stat_update(&sess->stat.rx.stat_sum.toh, toh); } /* Update burst metrics. * There are two terms introduced in the RFC 3611: gap & burst. * Gap represents good stream condition, lost+discard rate <= 1/Gmin. * Burst represents the opposite, lost+discard rate > 1/Gmin. */ if (lost >= 0 && discarded >= 0) { if(lost > 0) { sess->voip_mtc_stat.loss_count++; } if(discarded > 0) { sess->voip_mtc_stat.discard_count++; } if(!lost && !discarded) { /* Number of good packets since last lost/discarded */ sess->voip_mtc_stat.pkt++; } else { if(sess->voip_mtc_stat.pkt >= sess->stat.rx.voip_mtc.gmin) { /* Gap condition */ if(sess->voip_mtc_stat.lost == 1) { /* Gap -> Gap */ sess->voip_mtc_stat.c14++; } else { /* Burst -> Gap */ sess->voip_mtc_stat.c13++; } sess->voip_mtc_stat.lost = 1; sess->voip_mtc_stat.c11 += sess->voip_mtc_stat.pkt; } else { /* Burst condition */ sess->voip_mtc_stat.lost++; if(sess->voip_mtc_stat.pkt == 0) { /* Consecutive losts */ sess->voip_mtc_stat.c33++; } else { /* Any good packets, but still bursting */ sess->voip_mtc_stat.c23++; sess->voip_mtc_stat.c22 += (sess->voip_mtc_stat.pkt - 1); } } sess->voip_mtc_stat.pkt = 0; } } } void pjmedia_rtcp_xr_tx_rtp( pjmedia_rtcp_xr_session *session, unsigned ptsize ) { PJ_UNUSED_ARG(session); PJ_UNUSED_ARG(ptsize); } PJ_DEF(pj_status_t) pjmedia_rtcp_xr_update_info( pjmedia_rtcp_xr_session *sess, unsigned info, pj_int32_t val) { int v = val; switch(info) { case PJMEDIA_RTCP_XR_INFO_SIGNAL_LVL: sess->stat.rx.voip_mtc.signal_lvl = (pj_int8_t) v; break; case PJMEDIA_RTCP_XR_INFO_NOISE_LVL: sess->stat.rx.voip_mtc.noise_lvl = (pj_int8_t) v; break; case PJMEDIA_RTCP_XR_INFO_RERL: sess->stat.rx.voip_mtc.rerl = (pj_uint8_t) v; break; case PJMEDIA_RTCP_XR_INFO_R_FACTOR: sess->stat.rx.voip_mtc.ext_r_factor = (pj_uint8_t) v; break; case PJMEDIA_RTCP_XR_INFO_MOS_LQ: sess->stat.rx.voip_mtc.mos_lq = (pj_uint8_t) v; break; case PJMEDIA_RTCP_XR_INFO_MOS_CQ: sess->stat.rx.voip_mtc.mos_cq = (pj_uint8_t) v; break; case PJMEDIA_RTCP_XR_INFO_CONF_PLC: if (v >= 0 && v <= 3) { sess->stat.rx.voip_mtc.rx_config &= 0x3F; sess->stat.rx.voip_mtc.rx_config |= (pj_uint8_t) (v << 6); } break; case PJMEDIA_RTCP_XR_INFO_CONF_JBA: if (v >= 0 && v <= 3) { sess->stat.rx.voip_mtc.rx_config &= 0xCF; sess->stat.rx.voip_mtc.rx_config |= (pj_uint8_t) (v << 4); } break; case PJMEDIA_RTCP_XR_INFO_CONF_JBR: if (v >= 0 && v <= 15) { sess->stat.rx.voip_mtc.rx_config &= 0xF0; sess->stat.rx.voip_mtc.rx_config |= (pj_uint8_t) v; } break; case PJMEDIA_RTCP_XR_INFO_JB_NOM: sess->stat.rx.voip_mtc.jb_nom = (pj_uint16_t) v; break; case PJMEDIA_RTCP_XR_INFO_JB_MAX: sess->stat.rx.voip_mtc.jb_max = (pj_uint16_t) v; break; case PJMEDIA_RTCP_XR_INFO_JB_ABS_MAX: sess->stat.rx.voip_mtc.jb_abs_max = (pj_uint16_t) v; break; default: return PJ_EINVAL; } return PJ_SUCCESS; } #endif ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/rtp.c ================================================ /* $Id: rtp.c 4235 2012-08-24 03:15:42Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include /* pj_htonx, pj_htonx */ #include #include #include #define THIS_FILE "rtp.c" #define RTP_VERSION 2 #define RTP_SEQ_MOD (1 << 16) #define MAX_DROPOUT ((pj_int16_t)3000) #define MAX_MISORDER ((pj_int16_t)100) #define MIN_SEQUENTIAL ((pj_int16_t)2) static void pjmedia_rtp_seq_restart(pjmedia_rtp_seq_session *seq_ctrl, pj_uint16_t seq); PJ_DEF(pj_status_t) pjmedia_rtp_session_init( pjmedia_rtp_session *ses, int default_pt, pj_uint32_t sender_ssrc ) { PJ_LOG(5, (THIS_FILE, "pjmedia_rtp_session_init: ses=%p, default_pt=%d, ssrc=0x%x", ses, default_pt, sender_ssrc)); /* Check RTP header packing. */ if (sizeof(struct pjmedia_rtp_hdr) != 12) { pj_assert(!"Wrong RTP header packing!"); return PJMEDIA_RTP_EINPACK; } /* If sender_ssrc is not specified, create from random value. */ if (sender_ssrc == 0 || sender_ssrc == (pj_uint32_t)-1) { sender_ssrc = pj_htonl(pj_rand()); } else { sender_ssrc = pj_htonl(sender_ssrc); } /* Initialize session. */ pj_bzero(ses, sizeof(*ses)); /* Initial sequence number SHOULD be random, according to RFC 3550. */ /* According to RFC 3711, it should be random within 2^15 bit */ ses->out_extseq = pj_rand() & 0x7FFF; ses->peer_ssrc = 0; /* Build default header for outgoing RTP packet. */ ses->out_hdr.v = RTP_VERSION; ses->out_hdr.p = 0; ses->out_hdr.x = 0; ses->out_hdr.cc = 0; ses->out_hdr.m = 0; ses->out_hdr.pt = (pj_uint8_t) default_pt; ses->out_hdr.seq = (pj_uint16_t) pj_htons( (pj_uint16_t)ses->out_extseq ); ses->out_hdr.ts = 0; ses->out_hdr.ssrc = sender_ssrc; /* Keep some arguments as session defaults. */ ses->out_pt = (pj_uint16_t) default_pt; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_rtp_session_init2( pjmedia_rtp_session *ses, pjmedia_rtp_session_setting settings) { pj_status_t status; int pt = 0; pj_uint32_t sender_ssrc = 0; if (settings.flags & 1) pt = settings.default_pt; if (settings.flags & 2) sender_ssrc = settings.sender_ssrc; status = pjmedia_rtp_session_init(ses, pt, sender_ssrc); if (status != PJ_SUCCESS) return status; if (settings.flags & 4) { ses->out_extseq = settings.seq; ses->out_hdr.seq = pj_htons((pj_uint16_t)ses->out_extseq); } if (settings.flags & 8) ses->out_hdr.ts = pj_htonl(settings.ts); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_rtp_encode_rtp( pjmedia_rtp_session *ses, int pt, int m, int payload_len, int ts_len, const void **rtphdr, int *hdrlen ) { /* Update timestamp */ ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len); /* If payload_len is zero, bail out. * This is a clock frame; we're not really transmitting anything. */ if (payload_len == 0) return PJ_SUCCESS; /* Update session. */ ses->out_extseq++; /* Create outgoing header. */ ses->out_hdr.pt = (pj_uint8_t) ((pt == -1) ? ses->out_pt : pt); ses->out_hdr.m = (pj_uint16_t) m; ses->out_hdr.seq = pj_htons( (pj_uint16_t) ses->out_extseq); /* Return values */ *rtphdr = &ses->out_hdr; *hdrlen = sizeof(pjmedia_rtp_hdr); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_rtp_decode_rtp( pjmedia_rtp_session *ses, const void *pkt, int pkt_len, const pjmedia_rtp_hdr **hdr, const void **payload, unsigned *payloadlen) { int offset; PJ_UNUSED_ARG(ses); /* Assume RTP header at the start of packet. We'll verify this later. */ *hdr = (pjmedia_rtp_hdr*)pkt; /* Check RTP header sanity. */ if ((*hdr)->v != RTP_VERSION) { return PJMEDIA_RTP_EINVER; } /* Payload is located right after header plus CSRC */ offset = sizeof(pjmedia_rtp_hdr) + ((*hdr)->cc * sizeof(pj_uint32_t)); /* Adjust offset if RTP extension is used. */ if ((*hdr)->x) { pjmedia_rtp_ext_hdr *ext = (pjmedia_rtp_ext_hdr*) (((pj_uint8_t*)pkt) + offset); offset += ((pj_ntohs(ext->length)+1) * sizeof(pj_uint32_t)); } /* Check that offset is less than packet size */ if (offset > pkt_len) return PJMEDIA_RTP_EINLEN; /* Find and set payload. */ *payload = ((pj_uint8_t*)pkt) + offset; *payloadlen = pkt_len - offset; /* Remove payload padding if any */ if ((*hdr)->p && *payloadlen > 0) { pj_uint8_t pad_len; pad_len = ((pj_uint8_t*)(*payload))[*payloadlen - 1]; if (pad_len <= *payloadlen) *payloadlen -= pad_len; } return PJ_SUCCESS; } PJ_DEF(void) pjmedia_rtp_session_update( pjmedia_rtp_session *ses, const pjmedia_rtp_hdr *hdr, pjmedia_rtp_status *p_seq_st) { pjmedia_rtp_session_update2(ses, hdr, p_seq_st, PJ_TRUE); } PJ_DEF(void) pjmedia_rtp_session_update2( pjmedia_rtp_session *ses, const pjmedia_rtp_hdr *hdr, pjmedia_rtp_status *p_seq_st, pj_bool_t check_pt) { pjmedia_rtp_status seq_st; /* for now check_pt MUST be either PJ_TRUE or PJ_FALSE. * In the future we might change check_pt from boolean to * unsigned integer to accommodate more flags. */ pj_assert(check_pt==PJ_TRUE || check_pt==PJ_FALSE); /* Init status */ seq_st.status.value = 0; seq_st.diff = 0; /* Check SSRC. */ if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc); if (pj_ntohl(hdr->ssrc) != ses->peer_ssrc) { seq_st.status.flag.badssrc = 1; ses->peer_ssrc = pj_ntohl(hdr->ssrc); } /* Check payload type. */ if (check_pt && hdr->pt != ses->out_pt) { if (p_seq_st) { p_seq_st->status.value = seq_st.status.value; p_seq_st->status.flag.bad = 1; p_seq_st->status.flag.badpt = 1; } return; } /* Initialize sequence number on first packet received. */ if (ses->received == 0) pjmedia_rtp_seq_init( &ses->seq_ctrl, pj_ntohs(hdr->seq) ); /* Check sequence number to see if remote session has been restarted. */ pjmedia_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq), &seq_st); if (seq_st.status.flag.restart) { ++ses->received; } else if (!seq_st.status.flag.bad) { ++ses->received; } if (p_seq_st) { p_seq_st->status.value = seq_st.status.value; p_seq_st->diff = seq_st.diff; } } void pjmedia_rtp_seq_restart(pjmedia_rtp_seq_session *sess, pj_uint16_t seq) { sess->base_seq = seq; sess->max_seq = seq; sess->bad_seq = RTP_SEQ_MOD + 1; sess->cycles = 0; } void pjmedia_rtp_seq_init(pjmedia_rtp_seq_session *sess, pj_uint16_t seq) { pjmedia_rtp_seq_restart(sess, seq); sess->max_seq = (pj_uint16_t) (seq - 1); sess->probation = MIN_SEQUENTIAL; } void pjmedia_rtp_seq_update( pjmedia_rtp_seq_session *sess, pj_uint16_t seq, pjmedia_rtp_status *seq_status) { pj_uint16_t udelta = (pj_uint16_t) (seq - sess->max_seq); pjmedia_rtp_status st; /* Init status */ st.status.value = 0; st.diff = 0; /* * Source is not valid until MIN_SEQUENTIAL packets with * sequential sequence numbers have been received. */ if (sess->probation) { st.status.flag.probation = 1; if (seq == sess->max_seq+ 1) { /* packet is in sequence */ st.diff = 1; sess->probation--; sess->max_seq = seq; if (sess->probation == 0) { st.status.flag.probation = 0; } } else { st.diff = 0; st.status.flag.bad = 1; if (seq == sess->max_seq) st.status.flag.dup = 1; else st.status.flag.outorder = 1; sess->probation = MIN_SEQUENTIAL - 1; sess->max_seq = seq; } } else if (udelta == 0) { st.status.flag.dup = 1; } else if (udelta < MAX_DROPOUT) { /* in order, with permissible gap */ if (seq < sess->max_seq) { /* Sequence number wrapped - count another 64K cycle. */ sess->cycles += RTP_SEQ_MOD; } sess->max_seq = seq; st.diff = udelta; } else if (udelta <= (RTP_SEQ_MOD - MAX_MISORDER)) { /* the sequence number made a very large jump */ if (seq == sess->bad_seq) { /* * Two sequential packets -- assume that the other side * restarted without telling us so just re-sync * (i.e., pretend this was the first packet). */ pjmedia_rtp_seq_restart(sess, seq); st.status.flag.restart = 1; st.status.flag.probation = 1; st.diff = 1; } else { sess->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1); st.status.flag.bad = 1; st.status.flag.outorder = 1; } } else { /* old duplicate or reordered packet. * Not necessarily bad packet (?) */ st.status.flag.outorder = 1; } if (seq_status) { seq_status->diff = st.diff; seq_status->status.value = st.status.value; } } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/sdp.c ================================================ /* $Id: sdp.c 4367 2013-02-21 20:49:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include enum { SKIP_WS = 0, SYNTAX_ERROR = 1, }; // New token definition from RFC 4566 (SDP) #define TOKEN "!#$%&'*+-.^_`{|}~" //#define TOKEN "-.!%*_=`'~" //#define TOKEN "'`-./:?\"#$&*;=@[]^_`{|}+~!" #define NTP_OFFSET ((pj_uint32_t)2208988800) #define THIS_FILE "sdp.c" typedef struct parse_context { pj_status_t last_error; } parse_context; /* * Prototypes for line parser. */ static void parse_version(pj_scanner *scanner, parse_context *ctx); static void parse_origin(pj_scanner *scanner, pjmedia_sdp_session *ses, parse_context *ctx); static void parse_time(pj_scanner *scanner, pjmedia_sdp_session *ses, parse_context *ctx); static void parse_generic_line(pj_scanner *scanner, pj_str_t *str, parse_context *ctx); static void parse_connection_info(pj_scanner *scanner, pjmedia_sdp_conn *conn, parse_context *ctx); static void parse_bandwidth_info(pj_scanner *scanner, pjmedia_sdp_bandw *bandw, parse_context *ctx); static pjmedia_sdp_attr *parse_attr(pj_pool_t *pool, pj_scanner *scanner, parse_context *ctx); static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med, parse_context *ctx); static void on_scanner_error(pj_scanner *scanner); /* * Scanner character specification. */ static int is_initialized; static pj_cis_buf_t cis_buf; static pj_cis_t cs_digit, cs_token; static void init_sdp_parser(void) { if (is_initialized != 0) return; pj_enter_critical_section(); if (is_initialized != 0) { pj_leave_critical_section(); return; } pj_cis_buf_init(&cis_buf); pj_cis_init(&cis_buf, &cs_token); pj_cis_add_alpha(&cs_token); pj_cis_add_num(&cs_token); pj_cis_add_str(&cs_token, TOKEN); pj_cis_init(&cis_buf, &cs_digit); pj_cis_add_num(&cs_digit); is_initialized = 1; pj_leave_critical_section(); } PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_create( pj_pool_t *pool, const char *name, const pj_str_t *value) { pjmedia_sdp_attr *attr; PJ_ASSERT_RETURN(pool && name, NULL); attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr); pj_strdup2(pool, &attr->name, name); if (value) pj_strdup_with_null(pool, &attr->value, value); else { attr->value.ptr = NULL; attr->value.slen = 0; } return attr; } PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_clone(pj_pool_t *pool, const pjmedia_sdp_attr *rhs) { pjmedia_sdp_attr *attr; PJ_ASSERT_RETURN(pool && rhs, NULL); attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr); pj_strdup(pool, &attr->name, &rhs->name); pj_strdup_with_null(pool, &attr->value, &rhs->value); return attr; } PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_find (unsigned count, pjmedia_sdp_attr *const attr_array[], const pj_str_t *name, const pj_str_t *c_fmt) { unsigned i; unsigned c_pt = 0xFFFF; PJ_ASSERT_RETURN(count <= PJMEDIA_MAX_SDP_ATTR, NULL); if (c_fmt) c_pt = pj_strtoul(c_fmt); for (i=0; iname, name) == 0) { const pjmedia_sdp_attr *a = attr_array[i]; if (c_fmt) { unsigned pt = (unsigned) pj_strtoul2(&a->value, NULL, 10); if (pt == c_pt) { return (pjmedia_sdp_attr*)a; } } else return (pjmedia_sdp_attr*)a; } } return NULL; } PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_find2(unsigned count, pjmedia_sdp_attr *const attr_array[], const char *c_name, const pj_str_t *c_fmt) { pj_str_t name; name.ptr = (char*)c_name; name.slen = pj_ansi_strlen(c_name); return pjmedia_sdp_attr_find(count, attr_array, &name, c_fmt); } PJ_DEF(pj_status_t) pjmedia_sdp_attr_add(unsigned *count, pjmedia_sdp_attr *attr_array[], pjmedia_sdp_attr *attr) { PJ_ASSERT_RETURN(count && attr_array && attr, PJ_EINVAL); PJ_ASSERT_RETURN(*count < PJMEDIA_MAX_SDP_ATTR, PJ_ETOOMANY); attr_array[*count] = attr; (*count)++; return PJ_SUCCESS; } PJ_DEF(unsigned) pjmedia_sdp_attr_remove_all(unsigned *count, pjmedia_sdp_attr *attr_array[], const char *name) { unsigned i, removed = 0; pj_str_t attr_name; PJ_ASSERT_RETURN(count && attr_array && name, PJ_EINVAL); PJ_ASSERT_RETURN(*count <= PJMEDIA_MAX_SDP_ATTR, PJ_ETOOMANY); attr_name.ptr = (char*)name; attr_name.slen = pj_ansi_strlen(name); for (i=0; i<*count; ) { if (pj_strcmp(&attr_array[i]->name, &attr_name)==0) { pj_array_erase(attr_array, sizeof(pjmedia_sdp_attr*), *count, i); --(*count); ++removed; } else { ++i; } } return removed; } PJ_DEF(pj_status_t) pjmedia_sdp_attr_remove( unsigned *count, pjmedia_sdp_attr *attr_array[], pjmedia_sdp_attr *attr ) { unsigned i, removed=0; PJ_ASSERT_RETURN(count && attr_array && attr, PJ_EINVAL); PJ_ASSERT_RETURN(*count <= PJMEDIA_MAX_SDP_ATTR, PJ_ETOOMANY); for (i=0; i<*count; ) { if (attr_array[i] == attr) { pj_array_erase(attr_array, sizeof(pjmedia_sdp_attr*), *count, i); --(*count); ++removed; } else { ++i; } } return removed ? PJ_SUCCESS : PJ_ENOTFOUND; } PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_rtpmap( const pjmedia_sdp_attr *attr, pjmedia_sdp_rtpmap *rtpmap) { pj_scanner scanner; pj_str_t token; pj_status_t status = -1; char term = 0; PJ_USE_EXCEPTION; PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "rtpmap")==0, PJ_EINVALIDOP); PJ_ASSERT_RETURN(attr->value.slen != 0, PJMEDIA_SDP_EINATTR); init_sdp_parser(); /* Check if input is null terminated, and null terminate if * necessary. Unfortunately this may crash the application if * attribute was allocated from a read-only memory location. * But this shouldn't happen as attribute's value normally is * null terminated. */ if (attr->value.ptr[attr->value.slen] != 0 && attr->value.ptr[attr->value.slen] != '\r' && attr->value.ptr[attr->value.slen] != '\n') { pj_assert(!"Shouldn't happen"); term = attr->value.ptr[attr->value.slen]; attr->value.ptr[attr->value.slen] = '\0'; } pj_scan_init(&scanner, (char*)attr->value.ptr, attr->value.slen, PJ_SCAN_AUTOSKIP_WS, &on_scanner_error); /* rtpmap sample: * a=rtpmap:98 L16/16000/2. */ /* Init */ rtpmap->pt.slen = rtpmap->param.slen = rtpmap->enc_name.slen = 0; rtpmap->clock_rate = 0; /* Parse */ PJ_TRY { /* Get payload type. */ pj_scan_get(&scanner, &cs_token, &rtpmap->pt); /* Get encoding name. */ pj_scan_get(&scanner, &cs_token, &rtpmap->enc_name); /* Expecting '/' after encoding name. */ if (pj_scan_get_char(&scanner) != '/') { status = PJMEDIA_SDP_EINRTPMAP; goto on_return; } /* Get the clock rate. */ pj_scan_get(&scanner, &cs_digit, &token); rtpmap->clock_rate = pj_strtoul(&token); /* Expecting either '/' or EOF */ if (*scanner.curptr == '/') { pj_scan_get_char(&scanner); rtpmap->param.ptr = scanner.curptr; rtpmap->param.slen = scanner.end - scanner.curptr; } else { rtpmap->param.slen = 0; } status = PJ_SUCCESS; } PJ_CATCH_ANY { status = PJMEDIA_SDP_EINRTPMAP; } PJ_END; on_return: pj_scan_fini(&scanner); if (term) { attr->value.ptr[attr->value.slen] = term; } return status; } PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_fmtp( const pjmedia_sdp_attr *attr, pjmedia_sdp_fmtp *fmtp) { const char *p = attr->value.ptr; const char *end = attr->value.ptr + attr->value.slen; pj_str_t token; PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "fmtp")==0, PJ_EINVALIDOP); /* fmtp BNF: * a=fmtp: */ /* Get format. */ token.ptr = (char*)p; while (pj_isdigit(*p) && p!=end) ++p; token.slen = p - token.ptr; if (token.slen == 0) return PJMEDIA_SDP_EINFMTP; fmtp->fmt = token; /* Expecting space after format. */ if (*p != ' ') return PJMEDIA_SDP_EINFMTP; /* Get space. */ ++p; /* Set the remaining string as fmtp format parameter. */ fmtp->fmt_param.ptr = (char*)p; fmtp->fmt_param.slen = end - p; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_rtcp(const pjmedia_sdp_attr *attr, pjmedia_sdp_rtcp_attr *rtcp) { pj_scanner scanner; pj_str_t token; pj_status_t status = -1; PJ_USE_EXCEPTION; PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "rtcp")==0, PJ_EINVALIDOP); init_sdp_parser(); /* fmtp BNF: * a=rtcp: [nettype addrtype address] */ pj_scan_init(&scanner, (char*)attr->value.ptr, attr->value.slen, PJ_SCAN_AUTOSKIP_WS, &on_scanner_error); /* Init */ rtcp->net_type.slen = rtcp->addr_type.slen = rtcp->addr.slen = 0; /* Parse */ PJ_TRY { /* Get the port */ pj_scan_get(&scanner, &cs_token, &token); rtcp->port = pj_strtoul(&token); /* Have address? */ if (!pj_scan_is_eof(&scanner)) { /* Get network type */ pj_scan_get(&scanner, &cs_token, &rtcp->net_type); /* Get address type */ pj_scan_get(&scanner, &cs_token, &rtcp->addr_type); /* Get the address */ pj_scan_get(&scanner, &cs_token, &rtcp->addr); } status = PJ_SUCCESS; } PJ_CATCH_ANY { status = PJMEDIA_SDP_EINRTCP; } PJ_END; pj_scan_fini(&scanner); return status; } PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_create_rtcp(pj_pool_t *pool, const pj_sockaddr *a) { enum { ATTR_LEN = PJ_INET6_ADDRSTRLEN+16 }; pjmedia_sdp_attr *attr; attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr); attr->name = pj_str("rtcp"); attr->value.ptr = (char*) pj_pool_alloc(pool, ATTR_LEN); if (a->addr.sa_family == pj_AF_INET()) { attr->value.slen = pj_ansi_snprintf(attr->value.ptr, ATTR_LEN, "%u IN IP4 %s", pj_ntohs(a->ipv4.sin_port), pj_inet_ntoa(a->ipv4.sin_addr)); } else if (a->addr.sa_family == pj_AF_INET6()) { char tmp_addr[PJ_INET6_ADDRSTRLEN]; attr->value.slen = pj_ansi_snprintf(attr->value.ptr, ATTR_LEN, "%u IN IP6 %s", pj_sockaddr_get_port(a), pj_sockaddr_print(a, tmp_addr, sizeof(tmp_addr), 0)); } else { pj_assert(!"Unsupported address family"); return NULL; } return attr; } PJ_DEF(pj_status_t) pjmedia_sdp_attr_to_rtpmap(pj_pool_t *pool, const pjmedia_sdp_attr *attr, pjmedia_sdp_rtpmap **p_rtpmap) { PJ_ASSERT_RETURN(pool && attr && p_rtpmap, PJ_EINVAL); *p_rtpmap = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_rtpmap); PJ_ASSERT_RETURN(*p_rtpmap, PJ_ENOMEM); return pjmedia_sdp_attr_get_rtpmap(attr, *p_rtpmap); } PJ_DEF(pj_status_t) pjmedia_sdp_rtpmap_to_attr(pj_pool_t *pool, const pjmedia_sdp_rtpmap *rtpmap, pjmedia_sdp_attr **p_attr) { pjmedia_sdp_attr *attr; char tempbuf[128]; int len; /* Check arguments. */ PJ_ASSERT_RETURN(pool && rtpmap && p_attr, PJ_EINVAL); /* Check that mandatory attributes are specified. */ PJ_ASSERT_RETURN(rtpmap->enc_name.slen && rtpmap->clock_rate, PJMEDIA_SDP_EINRTPMAP); attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr); PJ_ASSERT_RETURN(attr != NULL, PJ_ENOMEM); attr->name.ptr = "rtpmap"; attr->name.slen = 6; /* Format: ":pt enc_name/clock_rate[/param]" */ len = pj_ansi_snprintf(tempbuf, sizeof(tempbuf), "%.*s %.*s/%u%s%.*s", (int)rtpmap->pt.slen, rtpmap->pt.ptr, (int)rtpmap->enc_name.slen, rtpmap->enc_name.ptr, rtpmap->clock_rate, (rtpmap->param.slen ? "/" : ""), (int)rtpmap->param.slen, rtpmap->param.ptr); if (len < 1 || len >= (int)sizeof(tempbuf)) return PJMEDIA_SDP_ERTPMAPTOOLONG; attr->value.slen = len; attr->value.ptr = (char*) pj_pool_alloc(pool, attr->value.slen+1); pj_memcpy(attr->value.ptr, tempbuf, attr->value.slen+1); *p_attr = attr; return PJ_SUCCESS; } static int print_connection_info( pjmedia_sdp_conn *c, char *buf, int len) { int printed; printed = pj_ansi_snprintf(buf, len, "c=%.*s %.*s %.*s\r\n", (int)c->net_type.slen, c->net_type.ptr, (int)c->addr_type.slen, c->addr_type.ptr, (int)c->addr.slen, c->addr.ptr); if (printed < 1 || printed >= len) return -1; return printed; } PJ_DEF(pjmedia_sdp_conn*) pjmedia_sdp_conn_clone (pj_pool_t *pool, const pjmedia_sdp_conn *rhs) { pjmedia_sdp_conn *c = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_conn); if (!c) return NULL; if (!pj_strdup (pool, &c->net_type, &rhs->net_type)) return NULL; if (!pj_strdup (pool, &c->addr_type, &rhs->addr_type)) return NULL; if (!pj_strdup (pool, &c->addr, &rhs->addr)) return NULL; return c; } PJ_DEF(pjmedia_sdp_bandw*) pjmedia_sdp_bandw_clone (pj_pool_t *pool, const pjmedia_sdp_bandw *rhs) { pjmedia_sdp_bandw *b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw); if (!b) return NULL; if (!pj_strdup (pool, &b->modifier, &rhs->modifier)) return NULL; b->value = rhs->value; return b; } static pj_ssize_t print_bandw(const pjmedia_sdp_bandw *bandw, char *buf, pj_size_t len) { char *p = buf; if ((int)len < bandw->modifier.slen + 10 + 5) return -1; *p++ = 'b'; *p++ = '='; pj_memcpy(p, bandw->modifier.ptr, bandw->modifier.slen); p += bandw->modifier.slen; *p++ = ':'; p += pj_utoa(bandw->value, p); *p++ = '\r'; *p++ = '\n'; return p-buf; } static pj_ssize_t print_attr(const pjmedia_sdp_attr *attr, char *buf, pj_size_t len) { char *p = buf; if ((int)len < attr->name.slen + attr->value.slen + 10) return -1; *p++ = 'a'; *p++ = '='; pj_memcpy(p, attr->name.ptr, attr->name.slen); p += attr->name.slen; if (attr->value.slen) { *p++ = ':'; pj_memcpy(p, attr->value.ptr, attr->value.slen); p += attr->value.slen; } *p++ = '\r'; *p++ = '\n'; return p-buf; } static int print_media_desc( pjmedia_sdp_media *m, char *buf, int len) { char *p = buf; char *end = buf+len; unsigned i; int printed; /* check length for the "m=" line. */ if (len < m->desc.media.slen+m->desc.transport.slen+12+24) { return -1; } *p++ = 'm'; /* m= */ *p++ = '='; pj_memcpy(p, m->desc.media.ptr, m->desc.media.slen); p += m->desc.media.slen; *p++ = ' '; printed = pj_utoa(m->desc.port, p); p += printed; if (m->desc.port_count > 1) { *p++ = '/'; printed = pj_utoa(m->desc.port_count, p); p += printed; } *p++ = ' '; pj_memcpy(p, m->desc.transport.ptr, m->desc.transport.slen); p += m->desc.transport.slen; for (i=0; idesc.fmt_count; ++i) { *p++ = ' '; pj_memcpy(p, m->desc.fmt[i].ptr, m->desc.fmt[i].slen); p += m->desc.fmt[i].slen; } *p++ = '\r'; *p++ = '\n'; /* print connection info, if present. */ if (m->conn) { printed = print_connection_info(m->conn, p, (int)(end-p)); if (printed < 0) { return -1; } p += printed; } /* print optional bandwidth info. */ for (i=0; ibandw_count; ++i) { printed = (int)print_bandw(m->bandw[i], p, end-p); if (printed < 0) { return -1; } p += printed; } /* print attributes. */ for (i=0; iattr_count; ++i) { printed = (int)print_attr(m->attr[i], p, end-p); if (printed < 0) { return -1; } p += printed; } return (int)(p-buf); } PJ_DEF(pjmedia_sdp_media*) pjmedia_sdp_media_clone( pj_pool_t *pool, const pjmedia_sdp_media *rhs) { unsigned int i; pjmedia_sdp_media *m = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_media); PJ_ASSERT_RETURN(m != NULL, NULL); pj_strdup (pool, &m->desc.media, &rhs->desc.media); m->desc.port = rhs->desc.port; m->desc.port_count = rhs->desc.port_count; pj_strdup (pool, &m->desc.transport, &rhs->desc.transport); m->desc.fmt_count = rhs->desc.fmt_count; for (i=0; idesc.fmt_count; ++i) pj_strdup(pool, &m->desc.fmt[i], &rhs->desc.fmt[i]); if (rhs->conn) { m->conn = pjmedia_sdp_conn_clone (pool, rhs->conn); PJ_ASSERT_RETURN(m->conn != NULL, NULL); } else { m->conn = NULL; } m->bandw_count = rhs->bandw_count; for (i=0; i < rhs->bandw_count; ++i) { m->bandw[i] = pjmedia_sdp_bandw_clone (pool, rhs->bandw[i]); PJ_ASSERT_RETURN(m->bandw[i] != NULL, NULL); } m->attr_count = rhs->attr_count; for (i=0; i < rhs->attr_count; ++i) { m->attr[i] = pjmedia_sdp_attr_clone (pool, rhs->attr[i]); PJ_ASSERT_RETURN(m->attr[i] != NULL, NULL); } return m; } PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_media_find_attr( const pjmedia_sdp_media *m, const pj_str_t *name, const pj_str_t *fmt) { PJ_ASSERT_RETURN(m && name, NULL); return pjmedia_sdp_attr_find(m->attr_count, m->attr, name, fmt); } PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_media_find_attr2( const pjmedia_sdp_media *m, const char *name, const pj_str_t *fmt) { PJ_ASSERT_RETURN(m && name, NULL); return pjmedia_sdp_attr_find2(m->attr_count, m->attr, name, fmt); } PJ_DEF(pj_status_t) pjmedia_sdp_media_add_attr( pjmedia_sdp_media *m, pjmedia_sdp_attr *attr) { return pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); } PJ_DEF(pj_status_t) pjmedia_sdp_session_add_attr(pjmedia_sdp_session *s, pjmedia_sdp_attr *attr) { return pjmedia_sdp_attr_add(&s->attr_count, s->attr, attr); } PJ_DEF(unsigned) pjmedia_sdp_media_remove_all_attr(pjmedia_sdp_media *m, const char *name) { return pjmedia_sdp_attr_remove_all(&m->attr_count, m->attr, name); } PJ_DEF(pj_status_t) pjmedia_sdp_media_remove_attr(pjmedia_sdp_media *m, pjmedia_sdp_attr *attr) { return pjmedia_sdp_attr_remove(&m->attr_count, m->attr, attr); } static int print_session(const pjmedia_sdp_session *ses, char *buf, pj_ssize_t len) { char *p = buf; char *end = buf+len; unsigned i; int printed; /* Check length for v= and o= lines. */ if (len < 5+ 2+ses->origin.user.slen+18+ ses->origin.net_type.slen+ses->origin.addr.slen + 2) { return -1; } /* SDP version (v= line) */ pj_memcpy(p, "v=0\r\n", 5); p += 5; /* Owner (o=) line. */ *p++ = 'o'; *p++ = '='; pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen); p += ses->origin.user.slen; *p++ = ' '; printed = pj_utoa(ses->origin.id, p); p += printed; *p++ = ' '; printed = pj_utoa(ses->origin.version, p); p += printed; *p++ = ' '; pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen); p += ses->origin.net_type.slen; *p++ = ' '; pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen); p += ses->origin.addr_type.slen; *p++ = ' '; pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen); p += ses->origin.addr.slen; *p++ = '\r'; *p++ = '\n'; /* Session name (s=) line. */ if ((end-p) < 8+ses->name.slen) { return -1; } *p++ = 's'; *p++ = '='; pj_memcpy(p, ses->name.ptr, ses->name.slen); p += ses->name.slen; *p++ = '\r'; *p++ = '\n'; /* Connection line (c=) if exist. */ if (ses->conn) { printed = print_connection_info(ses->conn, p, (int)(end-p)); if (printed < 1) { return -1; } p += printed; } /* print optional bandwidth info. */ for (i=0; ibandw_count; ++i) { printed = (int)print_bandw(ses->bandw[i], p, end-p); if (printed < 1) { return -1; } p += printed; } /* Time */ if ((end-p) < 24) { return -1; } *p++ = 't'; *p++ = '='; printed = pj_utoa(ses->time.start, p); p += printed; *p++ = ' '; printed = pj_utoa(ses->time.stop, p); p += printed; *p++ = '\r'; *p++ = '\n'; /* Print all attribute (a=) lines. */ for (i=0; iattr_count; ++i) { printed = (int)print_attr(ses->attr[i], p, end-p); if (printed < 0) { return -1; } p += printed; } /* Print media (m=) lines. */ for (i=0; imedia_count; ++i) { printed = print_media_desc(ses->media[i], p, (int)(end-p)); if (printed < 0) { return -1; } p += printed; } return (int)(p-buf); } /****************************************************************************** * PARSERS */ static void parse_version(pj_scanner *scanner, parse_context *ctx) { ctx->last_error = PJMEDIA_SDP_EINVER; /* check equal sign */ if (*(scanner->curptr+1) != '=') { on_scanner_error(scanner); return; } /* check version is 0 */ if (*(scanner->curptr+2) != '0') { on_scanner_error(scanner); return; } /* We've got what we're looking for, skip anything until newline */ pj_scan_skip_line(scanner); } static void parse_origin(pj_scanner *scanner, pjmedia_sdp_session *ses, parse_context *ctx) { pj_str_t str; ctx->last_error = PJMEDIA_SDP_EINORIGIN; /* check equal sign */ if (*(scanner->curptr+1) != '=') { on_scanner_error(scanner); return; } /* o= */ pj_scan_advance_n(scanner, 2, SKIP_WS); /* username. */ pj_scan_get_until_ch(scanner, ' ', &ses->origin.user); pj_scan_get_char(scanner); /* id */ pj_scan_get_until_ch(scanner, ' ', &str); ses->origin.id = pj_strtoul(&str); pj_scan_get_char(scanner); /* version */ pj_scan_get_until_ch(scanner, ' ', &str); ses->origin.version = pj_strtoul(&str); pj_scan_get_char(scanner); /* network-type */ pj_scan_get_until_ch(scanner, ' ', &ses->origin.net_type); pj_scan_get_char(scanner); /* addr-type */ pj_scan_get_until_ch(scanner, ' ', &ses->origin.addr_type); pj_scan_get_char(scanner); /* address */ pj_scan_get_until_chr(scanner, " \t\r\n", &ses->origin.addr); /* We've got what we're looking for, skip anything until newline */ pj_scan_skip_line(scanner); } static void parse_time(pj_scanner *scanner, pjmedia_sdp_session *ses, parse_context *ctx) { pj_str_t str; ctx->last_error = PJMEDIA_SDP_EINTIME; /* check equal sign */ if (*(scanner->curptr+1) != '=') { on_scanner_error(scanner); return; } /* t= */ pj_scan_advance_n(scanner, 2, SKIP_WS); /* start time */ pj_scan_get_until_ch(scanner, ' ', &str); ses->time.start = pj_strtoul(&str); pj_scan_get_char(scanner); /* stop time */ pj_scan_get_until_chr(scanner, " \t\r\n", &str); ses->time.stop = pj_strtoul(&str); /* We've got what we're looking for, skip anything until newline */ pj_scan_skip_line(scanner); } static void parse_generic_line(pj_scanner *scanner, pj_str_t *str, parse_context *ctx) { ctx->last_error = PJMEDIA_SDP_EINSDP; /* check equal sign */ if (*(scanner->curptr+1) != '=') { on_scanner_error(scanner); return; } /* x= */ pj_scan_advance_n(scanner, 2, SKIP_WS); /* get anything until newline (including whitespaces). */ pj_scan_get_until_chr(scanner, "\r\n", str); /* newline. */ pj_scan_get_newline(scanner); } static void parse_connection_info(pj_scanner *scanner, pjmedia_sdp_conn *conn, parse_context *ctx) { ctx->last_error = PJMEDIA_SDP_EINCONN; /* c= */ pj_scan_advance_n(scanner, 2, SKIP_WS); /* network-type */ pj_scan_get_until_ch(scanner, ' ', &conn->net_type); pj_scan_get_char(scanner); /* addr-type */ pj_scan_get_until_ch(scanner, ' ', &conn->addr_type); pj_scan_get_char(scanner); /* address. */ pj_scan_get_until_chr(scanner, "/ \t\r\n", &conn->addr); PJ_TODO(PARSE_SDP_CONN_ADDRESS_SUBFIELDS); /* We've got what we're looking for, skip anything until newline */ pj_scan_skip_line(scanner); } static void parse_bandwidth_info(pj_scanner *scanner, pjmedia_sdp_bandw *bandw, parse_context *ctx) { pj_str_t str; ctx->last_error = PJMEDIA_SDP_EINBANDW; /* b= */ pj_scan_advance_n(scanner, 2, SKIP_WS); /* modifier */ pj_scan_get_until_ch(scanner, ':', &bandw->modifier); pj_scan_get_char(scanner); /* value */ pj_scan_get_until_chr(scanner, " \t\r\n", &str); bandw->value = pj_strtoul(&str); /* We've got what we're looking for, skip anything until newline */ pj_scan_skip_line(scanner); } static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med, parse_context *ctx) { pj_str_t str; ctx->last_error = PJMEDIA_SDP_EINMEDIA; /* check the equal sign */ if (*(scanner->curptr+1) != '=') { on_scanner_error(scanner); return; } /* m= */ pj_scan_advance_n(scanner, 2, SKIP_WS); /* type */ pj_scan_get_until_ch(scanner, ' ', &med->desc.media); pj_scan_get_char(scanner); /* port */ pj_scan_get(scanner, &cs_token, &str); med->desc.port = (unsigned short)pj_strtoul(&str); if (*scanner->curptr == '/') { /* port count */ pj_scan_get_char(scanner); pj_scan_get(scanner, &cs_token, &str); med->desc.port_count = pj_strtoul(&str); } else { med->desc.port_count = 0; } if (pj_scan_get_char(scanner) != ' ') { PJ_THROW(SYNTAX_ERROR); } /* transport */ pj_scan_get_until_chr(scanner, " \t\r\n", &med->desc.transport); /* format list */ med->desc.fmt_count = 0; while (*scanner->curptr == ' ') { pj_str_t fmt; pj_scan_get_char(scanner); /* Check again for the end of the line */ if ((*scanner->curptr == '\r') || (*scanner->curptr == '\n')) break; pj_scan_get(scanner, &cs_token, &fmt); if (med->desc.fmt_count < PJMEDIA_MAX_SDP_FMT) med->desc.fmt[med->desc.fmt_count++] = fmt; else PJ_PERROR(2,(THIS_FILE, PJ_ETOOMANY, "Error adding SDP media format %.*s, " "format is ignored", (int)fmt.slen, fmt.ptr)); } /* We've got what we're looking for, skip anything until newline */ pj_scan_skip_line(scanner); } static void on_scanner_error(pj_scanner *scanner) { PJ_UNUSED_ARG(scanner); PJ_THROW(SYNTAX_ERROR); } static pjmedia_sdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner, parse_context *ctx) { pjmedia_sdp_attr *attr; ctx->last_error = PJMEDIA_SDP_EINATTR; attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr); /* check equal sign */ if (*(scanner->curptr+1) != '=') { on_scanner_error(scanner); return NULL; } /* skip a= */ pj_scan_advance_n(scanner, 2, SKIP_WS); /* get attr name. */ pj_scan_get(scanner, &cs_token, &attr->name); if (*scanner->curptr && *scanner->curptr != '\r' && *scanner->curptr != '\n') { /* skip ':' if present. */ if (*scanner->curptr == ':') pj_scan_get_char(scanner); /* get value */ if (*scanner->curptr != '\r' && *scanner->curptr != '\n') { pj_scan_get_until_chr(scanner, "\r\n", &attr->value); } else { attr->value.ptr = NULL; attr->value.slen = 0; } } else { attr->value.ptr = NULL; attr->value.slen = 0; } /* We've got what we're looking for, skip anything until newline */ pj_scan_skip_line(scanner); return attr; } /* * Apply direction attribute in session to all media. */ static void apply_media_direction(pjmedia_sdp_session *sdp) { pjmedia_sdp_attr *dir_attr = NULL; unsigned i; const pj_str_t inactive = { "inactive", 8 }; const pj_str_t sendonly = { "sendonly", 8 }; const pj_str_t recvonly = { "recvonly", 8 }; const pj_str_t sendrecv = { "sendrecv", 8 }; /* Find direction attribute in session, don't need to find default * direction "sendrecv". */ for (i = 0; i < sdp->attr_count && !dir_attr; ++i) { if (!pj_strcmp(&sdp->attr[i]->name, &sendonly) || !pj_strcmp(&sdp->attr[i]->name, &recvonly) || !pj_strcmp(&sdp->attr[i]->name, &inactive)) { dir_attr = sdp->attr[i]; } } /* Found the direction attribute */ if (dir_attr) { /* Remove the direction attribute in session */ pjmedia_sdp_attr_remove(&sdp->attr_count, sdp->attr, dir_attr); /* Apply the direction attribute to all media, but not overriding it * if media already has direction attribute. */ for (i = 0; i < sdp->media_count; ++i) { pjmedia_sdp_media *m; unsigned j; /* Find direction attribute in this media */ m = sdp->media[i]; for (j = 0; j < m->attr_count; ++j) { if (!pj_strcmp(&m->attr[j]->name, &sendrecv) || !pj_strcmp(&m->attr[j]->name, &sendonly) || !pj_strcmp(&m->attr[j]->name, &recvonly) || !pj_strcmp(&m->attr[j]->name, &inactive)) { break; } } /* Not found, apply direction attribute from session */ if (j == m->attr_count) pjmedia_sdp_media_add_attr(m, dir_attr); } } } /* * Parse SDP message. */ PJ_DEF(pj_status_t) pjmedia_sdp_parse( pj_pool_t *pool, char *buf, pj_size_t len, pjmedia_sdp_session **p_sdp) { pj_scanner scanner; pjmedia_sdp_session *session; pjmedia_sdp_media *media = NULL; pjmedia_sdp_attr *attr; pjmedia_sdp_conn *conn; pjmedia_sdp_bandw *bandw; pj_str_t dummy; int cur_name = 254; parse_context ctx; PJ_USE_EXCEPTION; ctx.last_error = PJ_SUCCESS; init_sdp_parser(); pj_scan_init(&scanner, buf, len, 0, &on_scanner_error); session = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session); PJ_ASSERT_RETURN(session != NULL, PJ_ENOMEM); /* Ignore leading newlines */ while (*scanner.curptr=='\r' || *scanner.curptr=='\n') pj_scan_get_char(&scanner); PJ_TRY { while (!pj_scan_is_eof(&scanner)) { cur_name = *scanner.curptr; switch (cur_name) { case 'a': attr = parse_attr(pool, &scanner, &ctx); if (attr) { if (media) { if (media->attr_count < PJMEDIA_MAX_SDP_ATTR) pjmedia_sdp_media_add_attr(media, attr); else PJ_PERROR(2, (THIS_FILE, PJ_ETOOMANY, "Error adding media attribute, " "attribute is ignored")); } else { if (session->attr_count < PJMEDIA_MAX_SDP_ATTR) pjmedia_sdp_session_add_attr(session, attr); else PJ_PERROR(2, (THIS_FILE, PJ_ETOOMANY, "Error adding session attribute" ", attribute is ignored")); } } break; case 'o': parse_origin(&scanner, session, &ctx); break; case 's': parse_generic_line(&scanner, &session->name, &ctx); break; case 'c': conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn); parse_connection_info(&scanner, conn, &ctx); if (media) { media->conn = conn; } else { session->conn = conn; } break; case 't': parse_time(&scanner, session, &ctx); break; case 'm': media = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media); parse_media(&scanner, media, &ctx); if (session->media_count < PJMEDIA_MAX_SDP_MEDIA) session->media[ session->media_count++ ] = media; else PJ_PERROR(2,(THIS_FILE, PJ_ETOOMANY, "Error adding media, media is ignored")); break; case 'v': parse_version(&scanner, &ctx); break; case 13: case 10: pj_scan_get_char(&scanner); /* Allow empty newlines at the end of the message */ while (!pj_scan_is_eof(&scanner)) { if (*scanner.curptr != 13 && *scanner.curptr != 10) { ctx.last_error = PJMEDIA_SDP_EINSDP; on_scanner_error(&scanner); } pj_scan_get_char(&scanner); } break; case 'b': bandw = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_bandw); parse_bandwidth_info(&scanner, bandw, &ctx); if (media) { if (media->bandw_count < PJMEDIA_MAX_SDP_BANDW) media->bandw[media->bandw_count++] = bandw; else PJ_PERROR(2, (THIS_FILE, PJ_ETOOMANY, "Error adding media bandwidth " "info, info is ignored")); } else { if (session->bandw_count < PJMEDIA_MAX_SDP_BANDW) session->bandw[session->bandw_count++] = bandw; else PJ_PERROR(2, (THIS_FILE, PJ_ETOOMANY, "Error adding session bandwidth " "info, info is ignored")); } break; default: if (cur_name >= 'a' && cur_name <= 'z') parse_generic_line(&scanner, &dummy, &ctx); else { ctx.last_error = PJMEDIA_SDP_EINSDP; on_scanner_error(&scanner); } break; } } ctx.last_error = PJ_SUCCESS; } PJ_CATCH_ANY { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(ctx.last_error, errmsg, sizeof(errmsg)); PJ_LOG(4, (THIS_FILE, "Error parsing SDP in line %d col %d: %s", scanner.line, pj_scan_get_col(&scanner), errmsg)); session = NULL; pj_assert(ctx.last_error != PJ_SUCCESS); } PJ_END; pj_scan_fini(&scanner); if (session) apply_media_direction(session); *p_sdp = session; return ctx.last_error; } /* * Print SDP description. */ PJ_DEF(int) pjmedia_sdp_print( const pjmedia_sdp_session *desc, char *buf, pj_size_t size) { return print_session(desc, buf, size); } /* * Clone session */ PJ_DEF(pjmedia_sdp_session*) pjmedia_sdp_session_clone( pj_pool_t *pool, const pjmedia_sdp_session *rhs) { pjmedia_sdp_session *sess; unsigned i; PJ_ASSERT_RETURN(pool && rhs, NULL); sess = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session); PJ_ASSERT_RETURN(sess != NULL, NULL); /* Clone origin line. */ pj_strdup(pool, &sess->origin.user, &rhs->origin.user); sess->origin.id = rhs->origin.id; sess->origin.version = rhs->origin.version; pj_strdup(pool, &sess->origin.net_type, &rhs->origin.net_type); pj_strdup(pool, &sess->origin.addr_type, &rhs->origin.addr_type); pj_strdup(pool, &sess->origin.addr, &rhs->origin.addr); /* Clone subject line. */ pj_strdup(pool, &sess->name, &rhs->name); /* Clone connection line */ if (rhs->conn) { sess->conn = pjmedia_sdp_conn_clone(pool, rhs->conn); PJ_ASSERT_RETURN(sess->conn != NULL, NULL); } /* Duplicate bandwidth info */ sess->bandw_count = rhs->bandw_count; for (i=0; ibandw_count; ++i) { sess->bandw[i] = pjmedia_sdp_bandw_clone(pool, rhs->bandw[i]); } /* Clone time line. */ sess->time.start = rhs->time.start; sess->time.stop = rhs->time.stop; /* Duplicate session attributes. */ sess->attr_count = rhs->attr_count; for (i=0; iattr_count; ++i) { sess->attr[i] = pjmedia_sdp_attr_clone(pool, rhs->attr[i]); } /* Duplicate media descriptors. */ sess->media_count = rhs->media_count; for (i=0; imedia_count; ++i) { sess->media[i] = pjmedia_sdp_media_clone(pool, rhs->media[i]); } return sess; } #define CHECK(exp,ret) do { \ /*pj_assert(exp);*/ \ if (!(exp)) \ return ret; \ } while (0) /* Validate SDP connetion info. */ static pj_status_t validate_sdp_conn(const pjmedia_sdp_conn *c) { CHECK( c, PJ_EINVAL); CHECK( pj_strcmp2(&c->net_type, "IN")==0, PJMEDIA_SDP_EINCONN); CHECK( pj_strcmp2(&c->addr_type, "IP4")==0 || pj_strcmp2(&c->addr_type, "IP6")==0, PJMEDIA_SDP_EINCONN); CHECK( c->addr.slen != 0, PJMEDIA_SDP_EINCONN); return PJ_SUCCESS; } /* Validate SDP session descriptor. */ PJ_DEF(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp) { return pjmedia_sdp_validate2(sdp, PJ_TRUE); } /* Validate SDP session descriptor. */ PJ_DEF(pj_status_t) pjmedia_sdp_validate2(const pjmedia_sdp_session *sdp, pj_bool_t strict) { unsigned i; const pj_str_t STR_RTPMAP = { "rtpmap", 6 }; CHECK( sdp != NULL, PJ_EINVAL); /* Validate origin line. */ CHECK( sdp->origin.user.slen != 0, PJMEDIA_SDP_EINORIGIN); CHECK( pj_strcmp2(&sdp->origin.net_type, "IN")==0, PJMEDIA_SDP_EINORIGIN); CHECK( pj_strcmp2(&sdp->origin.addr_type, "IP4")==0 || pj_strcmp2(&sdp->origin.addr_type, "IP6")==0, PJMEDIA_SDP_EINORIGIN); CHECK( sdp->origin.addr.slen != 0, PJMEDIA_SDP_EINORIGIN); /* Validate subject line. */ CHECK( sdp->name.slen != 0, PJMEDIA_SDP_EINNAME); /* Ignore start and stop time. */ /* If session level connection info is present, validate it. */ if (sdp->conn) { pj_status_t status = validate_sdp_conn(sdp->conn); if (status != PJ_SUCCESS) return status; } /* Validate each media. */ for (i=0; imedia_count; ++i) { const pjmedia_sdp_media *m = sdp->media[i]; unsigned j; /* Validate the m= line. */ CHECK( m->desc.media.slen != 0, PJMEDIA_SDP_EINMEDIA); CHECK( m->desc.transport.slen != 0, PJMEDIA_SDP_EINMEDIA); CHECK( m->desc.fmt_count != 0 || m->desc.port==0, PJMEDIA_SDP_ENOFMT); /* If media level connection info is present, validate it. */ if (m->conn) { pj_status_t status = validate_sdp_conn(m->conn); if (status != PJ_SUCCESS) return status; } /* If media doesn't have connection info, then connection info * must be present in the session. */ if (m->conn == NULL) { if (sdp->conn == NULL) if (strict || m->desc.port != 0) return PJMEDIA_SDP_EMISSINGCONN; } /* Verify payload type. */ for (j=0; jdesc.fmt_count; ++j) { /* Arrgh noo!! Payload type can be non-numeric!! * RTC based programs sends "null" for instant messaging! */ if (pj_isdigit(*m->desc.fmt[j].ptr)) { unsigned pt = pj_strtoul(&m->desc.fmt[j]); /* Payload type is between 0 and 127. */ CHECK( pt <= 127, PJMEDIA_SDP_EINPT); /* If port is not zero, then for each dynamic payload type, an * rtpmap attribute must be specified. */ if (m->desc.port != 0 && pt >= 96) { const pjmedia_sdp_attr *a; a = pjmedia_sdp_media_find_attr(m, &STR_RTPMAP, &m->desc.fmt[j]); CHECK( a != NULL, PJMEDIA_SDP_EMISSINGRTPMAP); } } } } /* Looks good. */ return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_sdp_transport_cmp( const pj_str_t *t1, const pj_str_t *t2) { static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 }; /* Exactly equal? */ if (pj_stricmp(t1, t2) == 0) return PJ_SUCCESS; /* Compatible? */ if ((!pj_stricmp(t1, &ID_RTP_AVP) || !pj_stricmp(t1, &ID_RTP_SAVP)) && (!pj_stricmp(t2, &ID_RTP_AVP) || !pj_stricmp(t2, &ID_RTP_SAVP))) return PJ_SUCCESS; return PJMEDIA_SDP_ETPORTNOTEQUAL; } PJ_DEF(pj_status_t) pjmedia_sdp_media_deactivate(pj_pool_t *pool, pjmedia_sdp_media *m) { PJ_ASSERT_RETURN(m, PJ_EINVAL); PJ_UNUSED_ARG(pool); /* Set port to zero */ m->desc.port = 0; /* And remove attributes */ m->attr_count = 0; return PJ_SUCCESS; } PJ_DEF(pjmedia_sdp_media*) pjmedia_sdp_media_clone_deactivate( pj_pool_t *pool, const pjmedia_sdp_media *rhs) { unsigned int i; pjmedia_sdp_media *m; PJ_ASSERT_RETURN(pool && rhs, NULL); m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media); pj_memcpy(m, rhs, sizeof(*m)); /* Clone the media line only */ pj_strdup (pool, &m->desc.media, &rhs->desc.media); pj_strdup (pool, &m->desc.transport, &rhs->desc.transport); for (i=0; idesc.fmt_count; ++i) pj_strdup(pool, &m->desc.fmt[i], &rhs->desc.fmt[i]); if (rhs->conn) { m->conn = pjmedia_sdp_conn_clone (pool, rhs->conn); PJ_ASSERT_RETURN(m->conn != NULL, NULL); } m->bandw_count = rhs->bandw_count; for (i=0; i < rhs->bandw_count; ++i) { m->bandw[i] = pjmedia_sdp_bandw_clone (pool, rhs->bandw[i]); PJ_ASSERT_RETURN(m->bandw[i] != NULL, NULL); } /* And deactivate it */ pjmedia_sdp_media_deactivate(pool, m); return m; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/sdp_cmp.c ================================================ /* $Id: sdp_cmp.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include /* Compare connection line. */ static pj_status_t compare_conn(const pjmedia_sdp_conn *c1, const pjmedia_sdp_conn *c2) { /* Compare network type. */ if (pj_strcmp(&c1->net_type, &c2->net_type) != 0) return PJMEDIA_SDP_ECONNNOTEQUAL; /* Compare address type. */ if (pj_strcmp(&c1->addr_type, &c2->addr_type) != 0) return PJMEDIA_SDP_ECONNNOTEQUAL; /* Compare address. */ if (pj_strcmp(&c1->addr, &c2->addr) != 0) return PJMEDIA_SDP_ECONNNOTEQUAL; return PJ_SUCCESS; } /* Compare attributes array. */ static pj_status_t compare_attr_imp(unsigned count1, pjmedia_sdp_attr *const attr1[], unsigned count2, pjmedia_sdp_attr *const attr2[]) { pj_status_t status; unsigned i; const pj_str_t inactive = { "inactive", 8 }; const pj_str_t sendrecv = { "sendrecv", 8 }; const pj_str_t sendonly = { "sendonly", 8 }; const pj_str_t recvonly = { "recvonly", 8 }; const pj_str_t fmtp = { "fmtp", 4 }; const pj_str_t rtpmap = { "rtpmap", 6 }; /* For simplicity, we only compare the following attributes, and ignore * the others: * - direction, eg. inactive, sendonly, recvonly, sendrecv * - fmtp for each payload. * - rtpmap for each payload. */ for (i=0; iname, &inactive) == 0 || pj_strcmp(&a1->name, &sendrecv) == 0 || pj_strcmp(&a1->name, &sendonly) == 0 || pj_strcmp(&a1->name, &recvonly) == 0) { /* For inactive, sendrecv, sendonly, and recvonly attributes, * the same attribute must be present on the other SDP. */ const pjmedia_sdp_attr *a2; a2 = pjmedia_sdp_attr_find(count2, attr2, &a1->name, NULL); if (!a2) return PJMEDIA_SDP_EDIRNOTEQUAL; } else if (pj_strcmp(&a1->name, &fmtp) == 0) { /* For fmtp attribute, find the fmtp attribute in the other SDP * for the same payload type, and compare the fmtp param/value. */ pjmedia_sdp_fmtp fmtp1, fmtp2; const pjmedia_sdp_attr *a2; status = pjmedia_sdp_attr_get_fmtp(a1, &fmtp1); if (status != PJ_SUCCESS) return PJMEDIA_SDP_EFMTPNOTEQUAL; a2 = pjmedia_sdp_attr_find(count2, attr2, &a1->name, &fmtp1.fmt); if (!a2) return PJMEDIA_SDP_EFMTPNOTEQUAL; status = pjmedia_sdp_attr_get_fmtp(a2, &fmtp2); if (status != PJ_SUCCESS) return PJMEDIA_SDP_EFMTPNOTEQUAL; if (pj_strcmp(&fmtp1.fmt_param, &fmtp2.fmt_param) != 0) return PJMEDIA_SDP_EFMTPNOTEQUAL; } else if (pj_strcmp(&a1->name, &rtpmap) == 0) { /* For rtpmap attribute, find rtpmap attribute on the other SDP * for the same payload type, and compare both rtpmap atribute * values. */ pjmedia_sdp_rtpmap r1, r2; const pjmedia_sdp_attr *a2; status = pjmedia_sdp_attr_get_rtpmap(a1, &r1); if (status != PJ_SUCCESS) return PJMEDIA_SDP_ERTPMAPNOTEQUAL; a2 = pjmedia_sdp_attr_find(count2, attr2, &a1->name, &r1.pt); if (!a2) return PJMEDIA_SDP_ERTPMAPNOTEQUAL; status = pjmedia_sdp_attr_get_rtpmap(a2, &r2); if (status != PJ_SUCCESS) return PJMEDIA_SDP_ERTPMAPNOTEQUAL; if (pj_strcmp(&r1.pt, &r2.pt) != 0) return PJMEDIA_SDP_ERTPMAPNOTEQUAL; if (pj_strcmp(&r1.enc_name, &r2.enc_name) != 0) return PJMEDIA_SDP_ERTPMAPNOTEQUAL; if (r1.clock_rate != r2.clock_rate) return PJMEDIA_SDP_ERTPMAPNOTEQUAL; if (pj_strcmp(&r1.param, &r2.param) != 0) return PJMEDIA_SDP_ERTPMAPNOTEQUAL; } } return PJ_SUCCESS; } /* Compare attributes array. */ static pj_status_t compare_attr(unsigned count1, pjmedia_sdp_attr *const attr1[], unsigned count2, pjmedia_sdp_attr *const attr2[]) { pj_status_t status; status = compare_attr_imp(count1, attr1, count2, attr2); if (status != PJ_SUCCESS) return status; status = compare_attr_imp(count2, attr2, count1, attr1); if (status != PJ_SUCCESS) return status; return PJ_SUCCESS; } /* Compare media descriptor */ PJ_DEF(pj_status_t) pjmedia_sdp_media_cmp( const pjmedia_sdp_media *sd1, const pjmedia_sdp_media *sd2, unsigned option) { unsigned i; pj_status_t status; PJ_ASSERT_RETURN(sd1 && sd2 && option==0, PJ_EINVAL); PJ_UNUSED_ARG(option); /* Compare media type. */ if (pj_strcmp(&sd1->desc.media, &sd2->desc.media) != 0) return PJMEDIA_SDP_EMEDIANOTEQUAL; /* Compare port number. */ if (sd1->desc.port != sd2->desc.port) return PJMEDIA_SDP_EPORTNOTEQUAL; /* Compare port count. */ if (sd1->desc.port_count != sd2->desc.port_count) return PJMEDIA_SDP_EPORTNOTEQUAL; /* Compare transports. */ if (pj_strcmp(&sd1->desc.transport, &sd2->desc.transport) != 0) return PJMEDIA_SDP_ETPORTNOTEQUAL; /* For zeroed port media, stop comparing here */ if (sd1->desc.port == 0) return PJ_SUCCESS; /* Compare number of formats. */ if (sd1->desc.fmt_count != sd2->desc.fmt_count) return PJMEDIA_SDP_EFORMATNOTEQUAL; /* Compare formats, in order. */ for (i=0; idesc.fmt_count; ++i) { if (pj_strcmp(&sd1->desc.fmt[i], &sd2->desc.fmt[i]) != 0) return PJMEDIA_SDP_EFORMATNOTEQUAL; } /* Compare connection line, if they exist. */ if (sd1->conn) { if (!sd2->conn) return PJMEDIA_SDP_EMEDIANOTEQUAL; status = compare_conn(sd1->conn, sd2->conn); } else { if (sd2->conn) return PJMEDIA_SDP_EMEDIANOTEQUAL; } /* Compare attributes. */ status = compare_attr(sd1->attr_count, sd1->attr, sd2->attr_count, sd2->attr); if (status != PJ_SUCCESS) return status; /* Looks equal */ return PJ_SUCCESS; } /* * Compare two SDP session for equality. */ PJ_DEF(pj_status_t) pjmedia_sdp_session_cmp( const pjmedia_sdp_session *sd1, const pjmedia_sdp_session *sd2, unsigned option) { unsigned i; pj_status_t status; PJ_ASSERT_RETURN(sd1 && sd2 && option==0, PJ_EINVAL); PJ_UNUSED_ARG(option); /* Compare the origin line. */ if (pj_strcmp(&sd1->origin.user, &sd2->origin.user) != 0) return PJMEDIA_SDP_EORIGINNOTEQUAL; if (sd1->origin.id != sd2->origin.id) return PJMEDIA_SDP_EORIGINNOTEQUAL; if (sd1->origin.version != sd2->origin.version) return PJMEDIA_SDP_EORIGINNOTEQUAL; if (pj_strcmp(&sd1->origin.net_type, &sd2->origin.net_type) != 0) return PJMEDIA_SDP_EORIGINNOTEQUAL; if (pj_strcmp(&sd1->origin.addr_type, &sd2->origin.addr_type) != 0) return PJMEDIA_SDP_EORIGINNOTEQUAL; if (pj_strcmp(&sd1->origin.addr, &sd2->origin.addr) != 0) return PJMEDIA_SDP_EORIGINNOTEQUAL; /* Compare the subject line. */ if (pj_strcmp(&sd1->name, &sd2->name) != 0) return PJMEDIA_SDP_ENAMENOTEQUAL; /* Compare connection line, when they exist */ if (sd1->conn) { if (!sd2->conn) return PJMEDIA_SDP_ECONNNOTEQUAL; status = compare_conn(sd1->conn, sd2->conn); if (status != PJ_SUCCESS) return status; } else { if (sd2->conn) return PJMEDIA_SDP_ECONNNOTEQUAL; } /* Compare time line. */ if (sd1->time.start != sd2->time.start) return PJMEDIA_SDP_ETIMENOTEQUAL; if (sd1->time.stop != sd2->time.stop) return PJMEDIA_SDP_ETIMENOTEQUAL; /* Compare attributes. */ status = compare_attr(sd1->attr_count, sd1->attr, sd2->attr_count, sd2->attr); if (status != PJ_SUCCESS) return status; /* Compare media lines. */ if (sd1->media_count != sd2->media_count) return PJMEDIA_SDP_EMEDIANOTEQUAL; for (i=0; imedia_count; ++i) { status = pjmedia_sdp_media_cmp(sd1->media[i], sd2->media[i], 0); if (status != PJ_SUCCESS) return status; } /* Looks equal. */ return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_sdp_conn_cmp(const pjmedia_sdp_conn *conn1, const pjmedia_sdp_conn *conn2, unsigned option) { PJ_UNUSED_ARG(option); return compare_conn(conn1, conn2); } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/sdp_neg.c ================================================ /* $Id: sdp_neg.c 4498 2013-04-24 09:52:25Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include /** * This structure describes SDP media negotiator. */ struct pjmedia_sdp_neg { pjmedia_sdp_neg_state state; /**< Negotiator state. */ pj_bool_t prefer_remote_codec_order; pj_bool_t answer_with_multiple_codecs; pj_bool_t has_remote_answer; pj_bool_t answer_was_remote; pjmedia_sdp_session *initial_sdp, /**< Initial local SDP */ *initial_sdp_tmp, /**< Temporary initial local SDP */ *active_local_sdp, /**< Currently active local SDP. */ *active_remote_sdp, /**< Currently active remote's. */ *neg_local_sdp, /**< Temporary local SDP. */ *neg_remote_sdp; /**< Temporary remote SDP. */ }; static const char *state_str[] = { "STATE_NULL", "STATE_LOCAL_OFFER", "STATE_REMOTE_OFFER", "STATE_WAIT_NEGO", "STATE_DONE", }; /* Definition of customized SDP format negotiation callback */ struct fmt_match_cb_t { pj_str_t fmt_name; pjmedia_sdp_neg_fmt_match_cb cb; }; /* Number of registered customized SDP format negotiation callbacks */ static unsigned fmt_match_cb_cnt; /* The registered customized SDP format negotiation callbacks */ static struct fmt_match_cb_t fmt_match_cb[PJMEDIA_SDP_NEG_MAX_CUSTOM_FMT_NEG_CB]; /* Redefining a very long identifier name, just for convenience */ #define ALLOW_MODIFY_ANSWER PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER static pj_status_t custom_fmt_match( pj_pool_t *pool, const pj_str_t *fmt_name, pjmedia_sdp_media *offer, unsigned o_fmt_idx, pjmedia_sdp_media *answer, unsigned a_fmt_idx, unsigned option); /* * Get string representation of negotiator state. */ PJ_DEF(const char*) pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_state state) { if ((int)state >=0 && state < (pjmedia_sdp_neg_state)PJ_ARRAY_SIZE(state_str)) return state_str[state]; return ""; } /* * Create with local offer. */ PJ_DEF(pj_status_t) pjmedia_sdp_neg_create_w_local_offer( pj_pool_t *pool, const pjmedia_sdp_session *local, pjmedia_sdp_neg **p_neg) { pjmedia_sdp_neg *neg; pj_status_t status; /* Check arguments are valid. */ PJ_ASSERT_RETURN(pool && local && p_neg, PJ_EINVAL); *p_neg = NULL; /* Validate local offer. */ PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(local))==PJ_SUCCESS, status); /* Create and initialize negotiator. */ neg = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_neg); PJ_ASSERT_RETURN(neg != NULL, PJ_ENOMEM); neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER; neg->prefer_remote_codec_order = PJMEDIA_SDP_NEG_PREFER_REMOTE_CODEC_ORDER; neg->answer_with_multiple_codecs = PJMEDIA_SDP_NEG_ANSWER_MULTIPLE_CODECS; neg->initial_sdp = pjmedia_sdp_session_clone(pool, local); neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, local); *p_neg = neg; return PJ_SUCCESS; } /* * Create with remote offer and initial local offer/answer. */ PJ_DEF(pj_status_t) pjmedia_sdp_neg_create_w_remote_offer(pj_pool_t *pool, const pjmedia_sdp_session *initial, const pjmedia_sdp_session *remote, pjmedia_sdp_neg **p_neg) { pjmedia_sdp_neg *neg; pj_status_t status; /* Check arguments are valid. */ PJ_ASSERT_RETURN(pool && remote && p_neg, PJ_EINVAL); *p_neg = NULL; /* Validate remote offer and initial answer */ status = pjmedia_sdp_validate2(remote, PJ_FALSE); if (status != PJ_SUCCESS) return status; /* Create and initialize negotiator. */ neg = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_neg); PJ_ASSERT_RETURN(neg != NULL, PJ_ENOMEM); neg->prefer_remote_codec_order = PJMEDIA_SDP_NEG_PREFER_REMOTE_CODEC_ORDER; neg->neg_remote_sdp = pjmedia_sdp_session_clone(pool, remote); if (initial) { PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(initial))==PJ_SUCCESS, status); neg->initial_sdp = pjmedia_sdp_session_clone(pool, initial); neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, initial); neg->state = PJMEDIA_SDP_NEG_STATE_WAIT_NEGO; } else { neg->state = PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER; } *p_neg = neg; return PJ_SUCCESS; } /* * Set codec order preference. */ PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_prefer_remote_codec_order( pjmedia_sdp_neg *neg, pj_bool_t prefer_remote) { PJ_ASSERT_RETURN(neg, PJ_EINVAL); neg->prefer_remote_codec_order = prefer_remote; return PJ_SUCCESS; } /* * Set multiple codec answering. */ PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_answer_multiple_codecs( pjmedia_sdp_neg *neg, pj_bool_t answer_multiple) { PJ_ASSERT_RETURN(neg, PJ_EINVAL); neg->answer_with_multiple_codecs = answer_multiple; return PJ_SUCCESS; } /* * Get SDP negotiator state. */ PJ_DEF(pjmedia_sdp_neg_state) pjmedia_sdp_neg_get_state( pjmedia_sdp_neg *neg ) { /* Check arguments are valid. */ PJ_ASSERT_RETURN(neg != NULL, PJMEDIA_SDP_NEG_STATE_NULL); return neg->state; } PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_active_local( pjmedia_sdp_neg *neg, const pjmedia_sdp_session **local) { PJ_ASSERT_RETURN(neg && local, PJ_EINVAL); PJ_ASSERT_RETURN(neg->active_local_sdp, PJMEDIA_SDPNEG_ENOACTIVE); *local = neg->active_local_sdp; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_active_remote( pjmedia_sdp_neg *neg, const pjmedia_sdp_session **remote) { PJ_ASSERT_RETURN(neg && remote, PJ_EINVAL); PJ_ASSERT_RETURN(neg->active_remote_sdp, PJMEDIA_SDPNEG_ENOACTIVE); *remote = neg->active_remote_sdp; return PJ_SUCCESS; } PJ_DEF(pj_bool_t) pjmedia_sdp_neg_was_answer_remote(pjmedia_sdp_neg *neg) { PJ_ASSERT_RETURN(neg, PJ_FALSE); return neg->answer_was_remote; } PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_neg_remote( pjmedia_sdp_neg *neg, const pjmedia_sdp_session **remote) { PJ_ASSERT_RETURN(neg && remote, PJ_EINVAL); PJ_ASSERT_RETURN(neg->neg_remote_sdp, PJMEDIA_SDPNEG_ENONEG); *remote = neg->neg_remote_sdp; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_neg_local( pjmedia_sdp_neg *neg, const pjmedia_sdp_session **local) { PJ_ASSERT_RETURN(neg && local, PJ_EINVAL); PJ_ASSERT_RETURN(neg->neg_local_sdp, PJMEDIA_SDPNEG_ENONEG); *local = neg->neg_local_sdp; return PJ_SUCCESS; } static pjmedia_sdp_media *sdp_media_clone_deactivate( pj_pool_t *pool, const pjmedia_sdp_media *rem_med, const pjmedia_sdp_media *local_med, const pjmedia_sdp_session *local_sess) { pjmedia_sdp_media *res; res = pjmedia_sdp_media_clone_deactivate(pool, rem_med); if (!res) return NULL; if (!res->conn && (!local_sess || !local_sess->conn)) { if (local_med && local_med->conn) res->conn = pjmedia_sdp_conn_clone(pool, local_med->conn); else { res->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn); res->conn->net_type = pj_str("IN"); res->conn->addr_type = pj_str("IP4"); res->conn->addr = pj_str("127.0.0.1"); } } return res; } /* * Modify local SDP and wait for remote answer. */ PJ_DEF(pj_status_t) pjmedia_sdp_neg_modify_local_offer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session *local) { return pjmedia_sdp_neg_modify_local_offer2(pool, neg, 0, local); } PJ_DEF(pj_status_t) pjmedia_sdp_neg_modify_local_offer2( pj_pool_t *pool, pjmedia_sdp_neg *neg, unsigned flags, const pjmedia_sdp_session *local) { pjmedia_sdp_session *new_offer; pjmedia_sdp_session *old_offer; char media_used[PJMEDIA_MAX_SDP_MEDIA]; unsigned oi; /* old offer media index */ pj_status_t status; /* Check arguments are valid. */ PJ_ASSERT_RETURN(pool && neg && local, PJ_EINVAL); /* Can only do this in STATE_DONE. */ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE, PJMEDIA_SDPNEG_EINSTATE); /* Validate the new offer */ status = pjmedia_sdp_validate(local); if (status != PJ_SUCCESS) return status; /* Change state to STATE_LOCAL_OFFER */ neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER; /* Init vars */ pj_bzero(media_used, sizeof(media_used)); old_offer = neg->active_local_sdp; new_offer = pjmedia_sdp_session_clone(pool, local); /* RFC 3264 Section 8: When issuing an offer that modifies the session, * the "o=" line of the new SDP MUST be identical to that in the * previous SDP, except that the version in the origin field MUST * increment by one from the previous SDP. */ pj_strdup(pool, &new_offer->origin.user, &old_offer->origin.user); new_offer->origin.id = old_offer->origin.id; new_offer->origin.version = old_offer->origin.version + 1; pj_strdup(pool, &new_offer->origin.net_type, &old_offer->origin.net_type); pj_strdup(pool, &new_offer->origin.addr_type,&old_offer->origin.addr_type); pj_strdup(pool, &new_offer->origin.addr, &old_offer->origin.addr); if ((flags & PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE) == 0) { /* Generating the new offer, in the case media lines doesn't match the * active SDP (e.g. current/active SDP's have m=audio and m=video lines, * and the new offer only has m=audio line), the negotiator will fix * the new offer by reordering and adding the missing media line with * port number set to zero. */ for (oi = 0; oi < old_offer->media_count; ++oi) { pjmedia_sdp_media *om; pjmedia_sdp_media *nm; unsigned ni; /* new offer media index */ pj_bool_t found = PJ_FALSE; om = old_offer->media[oi]; for (ni = oi; ni < new_offer->media_count; ++ni) { nm = new_offer->media[ni]; if (pj_strcmp(&nm->desc.media, &om->desc.media) == 0) { if (ni != oi) { /* The same media found but the position unmatched to * the old offer, so let's put this media in the right * place, and keep the order of the rest. */ pj_array_insert( new_offer->media, /* array */ sizeof(new_offer->media[0]), /* elmt size*/ ni, /* count */ oi, /* pos */ &nm); /* new elmt */ } found = PJ_TRUE; break; } } if (!found) { pjmedia_sdp_media *m; m = sdp_media_clone_deactivate(pool, om, om, local); pj_array_insert(new_offer->media, sizeof(new_offer->media[0]), new_offer->media_count++, oi, &m); } } } else { /* If media type change is allowed, the negotiator only needs to fix * the new offer by adding the missing media line(s) with port number * set to zero. */ for (oi = new_offer->media_count; oi < old_offer->media_count; ++oi) { pjmedia_sdp_media *m; m = sdp_media_clone_deactivate(pool, old_offer->media[oi], old_offer->media[oi], local); pj_array_insert(new_offer->media, sizeof(new_offer->media[0]), new_offer->media_count++, oi, &m); } } /* New_offer fixed */ neg->initial_sdp_tmp = neg->initial_sdp; neg->initial_sdp = new_offer; neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, new_offer); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_sdp_neg_send_local_offer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session **offer) { /* Check arguments are valid. */ PJ_ASSERT_RETURN(neg && offer, PJ_EINVAL); *offer = NULL; /* Can only do this in STATE_DONE or STATE_LOCAL_OFFER. */ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE || neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, PJMEDIA_SDPNEG_EINSTATE); if (neg->state == PJMEDIA_SDP_NEG_STATE_DONE) { /* If in STATE_DONE, set the active SDP as the offer. */ PJ_ASSERT_RETURN(neg->active_local_sdp, PJMEDIA_SDPNEG_ENOACTIVE); /* Retain initial SDP */ if (neg->initial_sdp) { neg->initial_sdp_tmp = neg->initial_sdp; neg->initial_sdp = pjmedia_sdp_session_clone(pool, neg->initial_sdp); } neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER; neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, neg->active_local_sdp); *offer = neg->active_local_sdp; } else { /* We assume that we're in STATE_LOCAL_OFFER. * In this case set the neg_local_sdp as the offer. */ *offer = neg->neg_local_sdp; } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_remote_answer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session *remote) { /* Check arguments are valid. */ PJ_ASSERT_RETURN(pool && neg && remote, PJ_EINVAL); /* Can only do this in STATE_LOCAL_OFFER. * If we haven't provided local offer, then rx_remote_offer() should * be called instead of this function. */ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, PJMEDIA_SDPNEG_EINSTATE); /* We're ready to negotiate. */ neg->state = PJMEDIA_SDP_NEG_STATE_WAIT_NEGO; neg->has_remote_answer = PJ_TRUE; neg->neg_remote_sdp = pjmedia_sdp_session_clone(pool, remote); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_remote_offer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session *remote) { /* Check arguments are valid. */ PJ_ASSERT_RETURN(pool && neg && remote, PJ_EINVAL); /* Can only do this in STATE_DONE. * If we already provide local offer, then rx_remote_answer() should * be called instead of this function. */ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE, PJMEDIA_SDPNEG_EINSTATE); /* State now is STATE_REMOTE_OFFER. */ neg->state = PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER; neg->neg_remote_sdp = pjmedia_sdp_session_clone(pool, remote); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_local_answer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session *local) { /* Check arguments are valid. */ PJ_ASSERT_RETURN(pool && neg && local, PJ_EINVAL); /* Can only do this in STATE_REMOTE_OFFER. * If we already provide local offer, then rx_remote_answer() should * be called instead of this function. */ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER, PJMEDIA_SDPNEG_EINSTATE); /* State now is STATE_WAIT_NEGO. */ neg->state = PJMEDIA_SDP_NEG_STATE_WAIT_NEGO; if (local) { neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, local); if (neg->initial_sdp) { /* Retain initial_sdp value. */ neg->initial_sdp_tmp = neg->initial_sdp; neg->initial_sdp = pjmedia_sdp_session_clone(pool, neg->initial_sdp); /* I don't think there is anything in RFC 3264 that mandates * answerer to place the same origin (and increment version) * in the answer, but probably it won't hurt either. * Note that the version will be incremented in * pjmedia_sdp_neg_negotiate() */ neg->neg_local_sdp->origin.id = neg->initial_sdp->origin.id; } else { neg->initial_sdp = pjmedia_sdp_session_clone(pool, local); } } else { PJ_ASSERT_RETURN(neg->initial_sdp, PJMEDIA_SDPNEG_ENOINITIAL); neg->initial_sdp_tmp = neg->initial_sdp; neg->initial_sdp = pjmedia_sdp_session_clone(pool, neg->initial_sdp); neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, neg->initial_sdp); } return PJ_SUCCESS; } PJ_DEF(pj_bool_t) pjmedia_sdp_neg_has_local_answer(pjmedia_sdp_neg *neg) { pj_assert(neg && neg->state==PJMEDIA_SDP_NEG_STATE_WAIT_NEGO); return !neg->has_remote_answer; } /* Swap string. */ static void str_swap(pj_str_t *str1, pj_str_t *str2) { pj_str_t tmp = *str1; *str1 = *str2; *str2 = tmp; } static void remove_all_media_directions(pjmedia_sdp_media *m) { pjmedia_sdp_media_remove_all_attr(m, "inactive"); pjmedia_sdp_media_remove_all_attr(m, "sendrecv"); pjmedia_sdp_media_remove_all_attr(m, "sendonly"); pjmedia_sdp_media_remove_all_attr(m, "recvonly"); } /* Update media direction based on peer's media direction */ static void update_media_direction(pj_pool_t *pool, const pjmedia_sdp_media *remote, pjmedia_sdp_media *local) { pjmedia_dir old_dir = PJMEDIA_DIR_ENCODING_DECODING, new_dir; /* Get the media direction of local SDP */ if (pjmedia_sdp_media_find_attr2(local, "sendonly", NULL)) old_dir = PJMEDIA_DIR_ENCODING; else if (pjmedia_sdp_media_find_attr2(local, "recvonly", NULL)) old_dir = PJMEDIA_DIR_DECODING; else if (pjmedia_sdp_media_find_attr2(local, "inactive", NULL)) old_dir = PJMEDIA_DIR_NONE; new_dir = old_dir; /* Adjust local media direction based on remote media direction */ if (pjmedia_sdp_media_find_attr2(remote, "inactive", NULL) != NULL) { /* If remote has "a=inactive", then local is inactive too */ new_dir = PJMEDIA_DIR_NONE; } else if(pjmedia_sdp_media_find_attr2(remote, "sendonly", NULL) != NULL) { /* If remote has "a=sendonly", then set local to "recvonly" if * it is currently "sendrecv". Otherwise if local is NOT "recvonly", * then set local direction to "inactive". */ switch (old_dir) { case PJMEDIA_DIR_ENCODING_DECODING: new_dir = PJMEDIA_DIR_DECODING; break; case PJMEDIA_DIR_DECODING: /* No change */ break; default: new_dir = PJMEDIA_DIR_NONE; break; } } else if(pjmedia_sdp_media_find_attr2(remote, "recvonly", NULL) != NULL) { /* If remote has "a=recvonly", then set local to "sendonly" if * it is currently "sendrecv". Otherwise if local is NOT "sendonly", * then set local direction to "inactive" */ switch (old_dir) { case PJMEDIA_DIR_ENCODING_DECODING: new_dir = PJMEDIA_DIR_ENCODING; break; case PJMEDIA_DIR_ENCODING: /* No change */ break; default: new_dir = PJMEDIA_DIR_NONE; break; } } else { /* Remote indicates "sendrecv" capability. No change to local * direction */ } if (new_dir != old_dir) { pjmedia_sdp_attr *a = NULL; remove_all_media_directions(local); switch (new_dir) { case PJMEDIA_DIR_NONE: a = pjmedia_sdp_attr_create(pool, "inactive", NULL); break; case PJMEDIA_DIR_ENCODING: a = pjmedia_sdp_attr_create(pool, "sendonly", NULL); break; case PJMEDIA_DIR_DECODING: a = pjmedia_sdp_attr_create(pool, "recvonly", NULL); break; default: /* sendrecv */ break; } if (a) { pjmedia_sdp_media_add_attr(local, a); } } } /* Update single local media description to after receiving answer * from remote. */ static pj_status_t process_m_answer( pj_pool_t *pool, pjmedia_sdp_media *offer, pjmedia_sdp_media *answer, pj_bool_t allow_asym) { unsigned i; /* Check that the media type match our offer. */ if (pj_strcmp(&answer->desc.media, &offer->desc.media)!=0) { /* The media type in the answer is different than the offer! */ return PJMEDIA_SDPNEG_EINVANSMEDIA; } /* Check that transport in the answer match our offer. */ /* At this point, transport type must be compatible, * the transport instance will do more validation later. */ if (pjmedia_sdp_transport_cmp(&answer->desc.transport, &offer->desc.transport) != PJ_SUCCESS) { return PJMEDIA_SDPNEG_EINVANSTP; } /* Check if remote has rejected our offer */ if (answer->desc.port == 0) { /* Remote has rejected our offer. * Deactivate our media too. */ pjmedia_sdp_media_deactivate(pool, offer); /* Don't need to proceed */ return PJ_SUCCESS; } /* Ticket #1148: check if remote answer does not set port to zero when * offered with port zero. Let's just tolerate it. */ if (offer->desc.port == 0) { /* Don't need to proceed */ return PJ_SUCCESS; } /* No need to update the direction when processing an answer */ /* If asymetric media is allowed, then just check that remote answer has * codecs that are within the offer. * * Otherwise if asymetric media is not allowed, then we will choose only * one codec in our initial offer to match the answer. */ if (allow_asym) { for (i=0; idesc.fmt_count; ++i) { unsigned j; pj_str_t *rem_fmt = &answer->desc.fmt[i]; for (j=0; jdesc.fmt_count; ++j) { if (pj_strcmp(rem_fmt, &answer->desc.fmt[j])==0) break; } if (j != offer->desc.fmt_count) { /* Found at least one common codec. */ break; } } if (i == answer->desc.fmt_count) { /* No common codec in the answer! */ return PJMEDIA_SDPNEG_EANSNOMEDIA; } PJ_TODO(CHECK_SDP_NEGOTIATION_WHEN_ASYMETRIC_MEDIA_IS_ALLOWED); } else { /* Offer format priority based on answer format index/priority */ unsigned offer_fmt_prior[PJMEDIA_MAX_SDP_FMT]; /* Remove all format in the offer that has no matching answer */ for (i=0; idesc.fmt_count;) { unsigned pt; pj_uint32_t j; pj_str_t *fmt = &offer->desc.fmt[i]; /* Find matching answer */ pt = pj_strtoul(fmt); if (pt < 96) { for (j=0; jdesc.fmt_count; ++j) { if (pj_strcmp(fmt, &answer->desc.fmt[j])==0) break; } } else { /* This is dynamic payload type. * For dynamic payload type, we must look the rtpmap and * compare the encoding name. */ const pjmedia_sdp_attr *a; pjmedia_sdp_rtpmap or_; /* Get the rtpmap for the payload type in the offer. */ a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", fmt); if (!a) { pj_assert(!"Bug! Offer should have been validated"); return PJ_EBUG; } pjmedia_sdp_attr_get_rtpmap(a, &or_); /* Find paylaod in answer SDP with matching * encoding name and clock rate. */ for (j=0; jdesc.fmt_count; ++j) { a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", &answer->desc.fmt[j]); if (a) { pjmedia_sdp_rtpmap ar; pjmedia_sdp_attr_get_rtpmap(a, &ar); /* See if encoding name, clock rate, and channel * count match */ if (!pj_stricmp(&or_.enc_name, &ar.enc_name) && or_.clock_rate == ar.clock_rate && (pj_stricmp(&or_.param, &ar.param)==0 || (ar.param.slen==1 && *ar.param.ptr=='1'))) { /* Call custom format matching callbacks */ if (custom_fmt_match(pool, &or_.enc_name, offer, i, answer, j, 0) == PJ_SUCCESS) { /* Match! */ break; } } } } } if (j == answer->desc.fmt_count) { /* This format has no matching answer. * Remove it from our offer. */ pjmedia_sdp_attr *a; /* Remove rtpmap associated with this format */ a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", fmt); if (a) pjmedia_sdp_media_remove_attr(offer, a); /* Remove fmtp associated with this format */ a = pjmedia_sdp_media_find_attr2(offer, "fmtp", fmt); if (a) pjmedia_sdp_media_remove_attr(offer, a); /* Remove this format from offer's array */ pj_array_erase(offer->desc.fmt, sizeof(offer->desc.fmt[0]), offer->desc.fmt_count, i); --offer->desc.fmt_count; } else { offer_fmt_prior[i] = j; ++i; } } if (0 == offer->desc.fmt_count) { /* No common codec in the answer! */ return PJMEDIA_SDPNEG_EANSNOMEDIA; } /* Post process: * - Resort offer formats so the order match to the answer. * - Remove answer formats that unmatches to the offer. */ /* Resort offer formats */ for (i=0; idesc.fmt_count; ++i) { unsigned j; for (j=i+1; jdesc.fmt_count; ++j) { if (offer_fmt_prior[i] > offer_fmt_prior[j]) { unsigned tmp = offer_fmt_prior[i]; offer_fmt_prior[i] = offer_fmt_prior[j]; offer_fmt_prior[j] = tmp; str_swap(&offer->desc.fmt[i], &offer->desc.fmt[j]); } } } /* Remove unmatched answer formats */ { unsigned del_cnt = 0; for (i=0; idesc.fmt_count;) { /* The offer is ordered now, also the offer_fmt_prior */ if (i >= offer->desc.fmt_count || offer_fmt_prior[i]-del_cnt != i) { pj_str_t *fmt = &answer->desc.fmt[i]; pjmedia_sdp_attr *a; /* Remove rtpmap associated with this format */ a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", fmt); if (a) pjmedia_sdp_media_remove_attr(answer, a); /* Remove fmtp associated with this format */ a = pjmedia_sdp_media_find_attr2(answer, "fmtp", fmt); if (a) pjmedia_sdp_media_remove_attr(answer, a); /* Remove this format from answer's array */ pj_array_erase(answer->desc.fmt, sizeof(answer->desc.fmt[0]), answer->desc.fmt_count, i); --answer->desc.fmt_count; ++del_cnt; } else { ++i; } } } } /* Looks okay */ return PJ_SUCCESS; } /* Update local media session (offer) to create active local session * after receiving remote answer. */ static pj_status_t process_answer(pj_pool_t *pool, pjmedia_sdp_session *offer, pjmedia_sdp_session *answer, pj_bool_t allow_asym, pjmedia_sdp_session **p_active) { unsigned omi = 0; /* Offer media index */ unsigned ami = 0; /* Answer media index */ pj_bool_t has_active = PJ_FALSE; pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(pool && offer && answer && p_active, PJ_EINVAL); /* Check that media count match between offer and answer */ // Ticket #527, different media count is allowed for more interoperability, // however, the media order must be same between offer and answer. // if (offer->media_count != answer->media_count) // return PJMEDIA_SDPNEG_EMISMEDIA; /* Now update each media line in the offer with the answer. */ for (; omimedia_count; ++omi) { if (ami == answer->media_count) { /* The answer has less media than the offer */ pjmedia_sdp_media *am; /* Generate matching-but-disabled-media for the answer */ am = sdp_media_clone_deactivate(pool, offer->media[omi], offer->media[omi], offer); answer->media[answer->media_count++] = am; ++ami; /* Deactivate our media offer too */ pjmedia_sdp_media_deactivate(pool, offer->media[omi]); /* No answer media to be negotiated */ continue; } status = process_m_answer(pool, offer->media[omi], answer->media[ami], allow_asym); /* If media type is mismatched, just disable the media. */ if (status == PJMEDIA_SDPNEG_EINVANSMEDIA) { pjmedia_sdp_media_deactivate(pool, offer->media[omi]); continue; } /* No common format in the answer media. */ else if (status == PJMEDIA_SDPNEG_EANSNOMEDIA) { pjmedia_sdp_media_deactivate(pool, offer->media[omi]); pjmedia_sdp_media_deactivate(pool, answer->media[ami]); } /* Return the error code, for other errors. */ else if (status != PJ_SUCCESS) { return status; } if (offer->media[omi]->desc.port != 0) has_active = PJ_TRUE; ++ami; } *p_active = offer; return has_active ? PJ_SUCCESS : PJMEDIA_SDPNEG_ENOMEDIA; } /* Internal function to rewrite the format string in SDP attribute rtpmap * and fmtp. */ PJ_INLINE(void) rewrite_pt(pj_pool_t *pool, pj_str_t *attr_val, const pj_str_t *old_pt, const pj_str_t *new_pt) { int len_diff = (int)(new_pt->slen - old_pt->slen); /* Note that attribute value should be null-terminated. */ if (len_diff > 0) { pj_str_t new_val; new_val.ptr = (char*)pj_pool_alloc(pool, attr_val->slen+len_diff+1); new_val.slen = attr_val->slen + len_diff; pj_memcpy(new_val.ptr + len_diff, attr_val->ptr, attr_val->slen + 1); *attr_val = new_val; } else if (len_diff < 0) { attr_val->slen += len_diff; pj_memmove(attr_val->ptr, attr_val->ptr - len_diff, attr_val->slen + 1); } pj_memcpy(attr_val->ptr, new_pt->ptr, new_pt->slen); } /* Internal function to apply symmetric PT for the local answer. */ static void apply_answer_symmetric_pt(pj_pool_t *pool, pjmedia_sdp_media *answer, unsigned pt_cnt, const pj_str_t pt_offer[], const pj_str_t pt_answer[]) { pjmedia_sdp_attr *a_tmp[PJMEDIA_MAX_SDP_ATTR]; unsigned i, a_tmp_cnt = 0; /* Rewrite the payload types in the answer if different to * the ones in the offer. */ for (i = 0; i < pt_cnt; ++i) { pjmedia_sdp_attr *a; /* Skip if the PTs are the same already, e.g: static PT. */ if (pj_strcmp(&pt_answer[i], &pt_offer[i]) == 0) continue; /* Rewrite payload type in the answer to match to the offer */ pj_strdup(pool, &answer->desc.fmt[i], &pt_offer[i]); /* Also update payload type in rtpmap */ a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", &pt_answer[i]); if (a) { rewrite_pt(pool, &a->value, &pt_answer[i], &pt_offer[i]); /* Temporarily remove the attribute in case the new payload * type is being used by another format in the media. */ pjmedia_sdp_media_remove_attr(answer, a); a_tmp[a_tmp_cnt++] = a; } /* Also update payload type in fmtp */ a = pjmedia_sdp_media_find_attr2(answer, "fmtp", &pt_answer[i]); if (a) { rewrite_pt(pool, &a->value, &pt_answer[i], &pt_offer[i]); /* Temporarily remove the attribute in case the new payload * type is being used by another format in the media. */ pjmedia_sdp_media_remove_attr(answer, a); a_tmp[a_tmp_cnt++] = a; } } /* Return back 'rtpmap' and 'fmtp' attributes */ for (i = 0; i < a_tmp_cnt; ++i) pjmedia_sdp_media_add_attr(answer, a_tmp[i]); } /* Try to match offer with answer. */ static pj_status_t match_offer(pj_pool_t *pool, pj_bool_t prefer_remote_codec_order, pj_bool_t answer_with_multiple_codecs, const pjmedia_sdp_media *offer, const pjmedia_sdp_media *preanswer, const pjmedia_sdp_session *preanswer_sdp, pjmedia_sdp_media **p_answer) { unsigned i; pj_bool_t master_has_codec = 0, master_has_other = 0, found_matching_codec = 0, found_matching_telephone_event = 0, found_matching_other = 0; unsigned pt_answer_count = 0; pj_str_t pt_answer[PJMEDIA_MAX_SDP_FMT]; pj_str_t pt_offer[PJMEDIA_MAX_SDP_FMT]; pjmedia_sdp_media *answer; const pjmedia_sdp_media *master, *slave; /* If offer has zero port, just clone the offer */ if (offer->desc.port == 0) { answer = sdp_media_clone_deactivate(pool, offer, preanswer, preanswer_sdp); *p_answer = answer; return PJ_SUCCESS; } /* If the preanswer define zero port, this media is being rejected, * just clone the preanswer. */ if (preanswer->desc.port == 0) { answer = pjmedia_sdp_media_clone(pool, preanswer); *p_answer = answer; return PJ_SUCCESS; } /* Set master/slave negotiator based on prefer_remote_codec_order. */ if (prefer_remote_codec_order) { master = offer; slave = preanswer; } else { master = preanswer; slave = offer; } /* With the addition of telephone-event and dodgy MS RTC SDP, * the answer generation algorithm looks really shitty... */ for (i=0; idesc.fmt_count; ++i) { unsigned j; if (pj_isdigit(*master->desc.fmt[i].ptr)) { /* This is normal/standard payload type, where it's identified * by payload number. */ unsigned pt; pt = pj_strtoul(&master->desc.fmt[i]); if (pt < 96) { /* For static payload type, it's enough to compare just * the payload number. */ master_has_codec = 1; /* We just need to select one codec if not allowing multiple. * Continue if we have selected matching codec for previous * payload. */ if (!answer_with_multiple_codecs && found_matching_codec) continue; /* Find matching codec in local descriptor. */ for (j=0; jdesc.fmt_count; ++j) { unsigned p; p = pj_strtoul(&slave->desc.fmt[j]); if (p == pt && pj_isdigit(*slave->desc.fmt[j].ptr)) { found_matching_codec = 1; pt_offer[pt_answer_count] = slave->desc.fmt[j]; pt_answer[pt_answer_count++] = slave->desc.fmt[j]; break; } } } else { /* This is dynamic payload type. * For dynamic payload type, we must look the rtpmap and * compare the encoding name. */ const pjmedia_sdp_attr *a; pjmedia_sdp_rtpmap or_; pj_bool_t is_codec; /* Get the rtpmap for the payload type in the master. */ a = pjmedia_sdp_media_find_attr2(master, "rtpmap", &master->desc.fmt[i]); if (!a) { pj_assert(!"Bug! Offer should have been validated"); return PJMEDIA_SDP_EMISSINGRTPMAP; } pjmedia_sdp_attr_get_rtpmap(a, &or_); if (!pj_stricmp2(&or_.enc_name, "telephone-event")) { if (found_matching_telephone_event) continue; is_codec = 0; } else { master_has_codec = 1; if (!answer_with_multiple_codecs && found_matching_codec) continue; is_codec = 1; } /* Find paylaod in our initial SDP with matching * encoding name and clock rate. */ for (j=0; jdesc.fmt_count; ++j) { a = pjmedia_sdp_media_find_attr2(slave, "rtpmap", &slave->desc.fmt[j]); if (a) { pjmedia_sdp_rtpmap lr; pjmedia_sdp_attr_get_rtpmap(a, &lr); /* See if encoding name, clock rate, and * channel count match */ if (!pj_stricmp(&or_.enc_name, &lr.enc_name) && or_.clock_rate == lr.clock_rate && (pj_stricmp(&or_.param, &lr.param)==0 || (lr.param.slen==0 && or_.param.slen==1 && *or_.param.ptr=='1') || (or_.param.slen==0 && lr.param.slen==1 && *lr.param.ptr=='1'))) { /* Match! */ if (is_codec) { pjmedia_sdp_media *o_med, *a_med; unsigned o_fmt_idx, a_fmt_idx; o_med = (pjmedia_sdp_media*)offer; a_med = (pjmedia_sdp_media*)preanswer; o_fmt_idx = prefer_remote_codec_order? i:j; a_fmt_idx = prefer_remote_codec_order? j:i; /* Call custom format matching callbacks */ if (custom_fmt_match(pool, &or_.enc_name, o_med, o_fmt_idx, a_med, a_fmt_idx, ALLOW_MODIFY_ANSWER) != PJ_SUCCESS) { continue; } found_matching_codec = 1; } else { found_matching_telephone_event = 1; } pt_offer[pt_answer_count] = prefer_remote_codec_order? offer->desc.fmt[i]: offer->desc.fmt[j]; pt_answer[pt_answer_count++] = prefer_remote_codec_order? preanswer->desc.fmt[j]: preanswer->desc.fmt[i]; break; } } } } } else { /* This is a non-standard, brain damaged SDP where the payload * type is non-numeric. It exists e.g. in Microsoft RTC based * UA, to indicate instant messaging capability. * Example: * - m=x-ms-message 5060 sip null */ master_has_other = 1; if (found_matching_other) continue; for (j=0; jdesc.fmt_count; ++j) { if (!pj_strcmp(&master->desc.fmt[i], &slave->desc.fmt[j])) { /* Match */ found_matching_other = 1; pt_offer[pt_answer_count] = prefer_remote_codec_order? offer->desc.fmt[i]: offer->desc.fmt[j]; pt_answer[pt_answer_count++] = prefer_remote_codec_order? preanswer->desc.fmt[j]: preanswer->desc.fmt[i]; break; } } } } /* See if all types of master can be matched. */ if (master_has_codec && !found_matching_codec) { return PJMEDIA_SDPNEG_NOANSCODEC; } /* If this comment is removed, negotiation will fail if remote has offered telephone-event and local is not configured with telephone-event if (offer_has_telephone_event && !found_matching_telephone_event) { return PJMEDIA_SDPNEG_NOANSTELEVENT; } */ if (master_has_other && !found_matching_other) { return PJMEDIA_SDPNEG_NOANSUNKNOWN; } /* Seems like everything is in order. * Build the answer by cloning from preanswer, but rearrange the payload * to suit the offer. */ answer = pjmedia_sdp_media_clone(pool, preanswer); for (i=0; idesc.fmt_count; ++j) { if (!pj_strcmp(&answer->desc.fmt[j], &pt_answer[i])) break; } pj_assert(j != answer->desc.fmt_count); str_swap(&answer->desc.fmt[i], &answer->desc.fmt[j]); } /* Remove unwanted local formats. */ for (i=pt_answer_count; idesc.fmt_count; ++i) { pjmedia_sdp_attr *a; /* Remove rtpmap for this format */ a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", &answer->desc.fmt[i]); if (a) { pjmedia_sdp_media_remove_attr(answer, a); } /* Remove fmtp for this format */ a = pjmedia_sdp_media_find_attr2(answer, "fmtp", &answer->desc.fmt[i]); if (a) { pjmedia_sdp_media_remove_attr(answer, a); } } answer->desc.fmt_count = pt_answer_count; #if PJMEDIA_SDP_NEG_ANSWER_SYMMETRIC_PT apply_answer_symmetric_pt(pool, answer, pt_answer_count, pt_offer, pt_answer); #endif /* Update media direction. */ update_media_direction(pool, offer, answer); *p_answer = answer; return PJ_SUCCESS; } /* Create complete answer for remote's offer. */ static pj_status_t create_answer( pj_pool_t *pool, pj_bool_t prefer_remote_codec_order, pj_bool_t answer_with_multiple_codecs, const pjmedia_sdp_session *initial, const pjmedia_sdp_session *offer, pjmedia_sdp_session **p_answer) { pj_status_t status = PJMEDIA_SDPNEG_ENOMEDIA; pj_bool_t has_active = PJ_FALSE; pjmedia_sdp_session *answer; char media_used[PJMEDIA_MAX_SDP_MEDIA]; unsigned i; /* Validate remote offer. * This should have been validated before. */ PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(offer))==PJ_SUCCESS, status); /* Create initial answer by duplicating initial SDP, * but clear all media lines. The media lines will be filled up later. */ answer = pjmedia_sdp_session_clone(pool, initial); PJ_ASSERT_RETURN(answer != NULL, PJ_ENOMEM); answer->media_count = 0; pj_bzero(media_used, sizeof(media_used)); /* For each media line, create our answer based on our initial * capability. */ for (i=0; imedia_count; ++i) { const pjmedia_sdp_media *om; /* offer */ const pjmedia_sdp_media *im; /* initial media */ pjmedia_sdp_media *am = NULL; /* answer/result */ unsigned j; om = offer->media[i]; /* Find media description in our initial capability that matches * the media type and transport type of offer's media, has * matching codec, and has not been used to answer other offer. */ for (im=NULL, j=0; jmedia_count; ++j) { im = initial->media[j]; if (pj_strcmp(&om->desc.media, &im->desc.media)==0 && pj_strcmp(&om->desc.transport, &im->desc.transport)==0 && media_used[j] == 0) { pj_status_t status2; /* See if it has matching codec. */ status2 = match_offer(pool, prefer_remote_codec_order, answer_with_multiple_codecs, om, im, initial, &am); if (status2 == PJ_SUCCESS) { /* Mark media as used. */ media_used[j] = 1; break; } else { status = status2; } } } if (j==initial->media_count) { /* No matching media. * Reject the offer by setting the port to zero in the answer. */ /* For simplicity in the construction of the answer, we'll * just clone the media from the offer. Anyway receiver will * ignore anything in the media once it sees that the port * number is zero. */ am = sdp_media_clone_deactivate(pool, om, om, answer); } else { /* The answer is in am */ pj_assert(am != NULL); } /* Add the media answer */ answer->media[answer->media_count++] = am; /* Check if this media is active.*/ if (am->desc.port != 0) has_active = PJ_TRUE; } *p_answer = answer; return has_active ? PJ_SUCCESS : status; } /* Cancel offer */ PJ_DEF(pj_status_t) pjmedia_sdp_neg_cancel_offer(pjmedia_sdp_neg *neg) { PJ_ASSERT_RETURN(neg, PJ_EINVAL); /* Must be in LOCAL_OFFER state. */ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER || neg->state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER, PJMEDIA_SDPNEG_EINSTATE); if (neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER && neg->active_local_sdp) { /* Increment next version number. This happens if for example * the reinvite offer is rejected by 488. If we don't increment * the version here, the next offer will have the same version. */ neg->active_local_sdp->origin.version++; } /* Revert back initial SDP */ if (neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) neg->initial_sdp = neg->initial_sdp_tmp; /* Clear temporary SDP */ neg->initial_sdp_tmp = NULL; neg->neg_local_sdp = neg->neg_remote_sdp = NULL; neg->has_remote_answer = PJ_FALSE; /* Reset state to done */ neg->state = PJMEDIA_SDP_NEG_STATE_DONE; return PJ_SUCCESS; } /* The best bit: SDP negotiation function! */ PJ_DEF(pj_status_t) pjmedia_sdp_neg_negotiate( pj_pool_t *pool, pjmedia_sdp_neg *neg, pj_bool_t allow_asym) { pj_status_t status; /* Check arguments are valid. */ PJ_ASSERT_RETURN(pool && neg, PJ_EINVAL); /* Must be in STATE_WAIT_NEGO state. */ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, PJMEDIA_SDPNEG_EINSTATE); /* Must have remote offer. */ PJ_ASSERT_RETURN(neg->neg_remote_sdp, PJ_EBUG); if (neg->has_remote_answer) { pjmedia_sdp_session *active; status = process_answer(pool, neg->neg_local_sdp, neg->neg_remote_sdp, allow_asym, &active); if (status == PJ_SUCCESS) { /* Only update active SDPs when negotiation is successfull */ neg->active_local_sdp = active; neg->active_remote_sdp = neg->neg_remote_sdp; } } else { pjmedia_sdp_session *answer = NULL; status = create_answer(pool, neg->prefer_remote_codec_order, neg->answer_with_multiple_codecs, neg->neg_local_sdp, neg->neg_remote_sdp, &answer); if (status == PJ_SUCCESS) { pj_uint32_t active_ver; if (neg->active_local_sdp) active_ver = neg->active_local_sdp->origin.version; else active_ver = neg->initial_sdp->origin.version; /* Only update active SDPs when negotiation is successfull */ neg->active_local_sdp = answer; neg->active_remote_sdp = neg->neg_remote_sdp; /* Increment SDP version */ neg->active_local_sdp->origin.version = ++active_ver; } } /* State is DONE regardless */ neg->state = PJMEDIA_SDP_NEG_STATE_DONE; /* Save state */ neg->answer_was_remote = neg->has_remote_answer; /* Revert back initial SDP if nego fails */ if (status != PJ_SUCCESS) neg->initial_sdp = neg->initial_sdp_tmp; /* Clear temporary SDP */ neg->initial_sdp_tmp = NULL; neg->neg_local_sdp = neg->neg_remote_sdp = NULL; neg->has_remote_answer = PJ_FALSE; return status; } static pj_status_t custom_fmt_match(pj_pool_t *pool, const pj_str_t *fmt_name, pjmedia_sdp_media *offer, unsigned o_fmt_idx, pjmedia_sdp_media *answer, unsigned a_fmt_idx, unsigned option) { unsigned i; for (i = 0; i < fmt_match_cb_cnt; ++i) { if (pj_stricmp(fmt_name, &fmt_match_cb[i].fmt_name) == 0) { pj_assert(fmt_match_cb[i].cb); return (*fmt_match_cb[i].cb)(pool, offer, o_fmt_idx, answer, a_fmt_idx, option); } } /* Not customized format matching found, should be matched */ return PJ_SUCCESS; } /* Register customized SDP format negotiation callback function. */ PJ_DEF(pj_status_t) pjmedia_sdp_neg_register_fmt_match_cb( const pj_str_t *fmt_name, pjmedia_sdp_neg_fmt_match_cb cb) { struct fmt_match_cb_t *f = NULL; unsigned i; PJ_ASSERT_RETURN(fmt_name, PJ_EINVAL); /* Check if the callback for the format name has been registered */ for (i = 0; i < fmt_match_cb_cnt; ++i) { if (pj_stricmp(fmt_name, &fmt_match_cb[i].fmt_name) == 0) break; } /* Unregistration */ if (cb == NULL) { if (i == fmt_match_cb_cnt) return PJ_ENOTFOUND; pj_array_erase(fmt_match_cb, sizeof(fmt_match_cb[0]), fmt_match_cb_cnt, i); fmt_match_cb_cnt--; return PJ_SUCCESS; } /* Registration */ if (i < fmt_match_cb_cnt) { /* The same format name has been registered before */ if (cb != fmt_match_cb[i].cb) return PJ_EEXISTS; else return PJ_SUCCESS; } if (fmt_match_cb_cnt >= PJ_ARRAY_SIZE(fmt_match_cb)) return PJ_ETOOMANY; f = &fmt_match_cb[fmt_match_cb_cnt++]; f->fmt_name = *fmt_name; f->cb = cb; return PJ_SUCCESS; } /* Match format in the SDP media offer and answer. */ PJ_DEF(pj_status_t) pjmedia_sdp_neg_fmt_match(pj_pool_t *pool, pjmedia_sdp_media *offer, unsigned o_fmt_idx, pjmedia_sdp_media *answer, unsigned a_fmt_idx, unsigned option) { const pjmedia_sdp_attr *attr; pjmedia_sdp_rtpmap o_rtpmap, a_rtpmap; unsigned o_pt; unsigned a_pt; o_pt = pj_strtoul(&offer->desc.fmt[o_fmt_idx]); a_pt = pj_strtoul(&answer->desc.fmt[a_fmt_idx]); if (o_pt < 96 || a_pt < 96) { if (o_pt == a_pt) return PJ_SUCCESS; else return PJMEDIA_SDP_EFORMATNOTEQUAL; } /* Get the format rtpmap from the offer. */ attr = pjmedia_sdp_media_find_attr2(offer, "rtpmap", &offer->desc.fmt[o_fmt_idx]); if (!attr) { pj_assert(!"Bug! Offer haven't been validated"); return PJ_EBUG; } pjmedia_sdp_attr_get_rtpmap(attr, &o_rtpmap); /* Get the format rtpmap from the answer. */ attr = pjmedia_sdp_media_find_attr2(answer, "rtpmap", &answer->desc.fmt[a_fmt_idx]); if (!attr) { pj_assert(!"Bug! Answer haven't been validated"); return PJ_EBUG; } pjmedia_sdp_attr_get_rtpmap(attr, &a_rtpmap); if (pj_stricmp(&o_rtpmap.enc_name, &a_rtpmap.enc_name) != 0 || (o_rtpmap.clock_rate != a_rtpmap.clock_rate) || (!(pj_stricmp(&o_rtpmap.param, &a_rtpmap.param) == 0 || (a_rtpmap.param.slen == 0 && o_rtpmap.param.slen == 1 && *o_rtpmap.param.ptr == '1') || (o_rtpmap.param.slen == 0 && a_rtpmap.param.slen == 1 && *a_rtpmap.param.ptr=='1')))) { return PJMEDIA_SDP_EFORMATNOTEQUAL; } return custom_fmt_match(pool, &o_rtpmap.enc_name, offer, o_fmt_idx, answer, a_fmt_idx, option); } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/sdp_wrap.cpp ================================================ /* $Id: sdp_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This file is a C++ wrapper, see ticket #886 for details. */ #include "sdp.c" ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/session.c ================================================ /* $Id: session.c 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include struct pjmedia_session { pj_pool_t *pool; pjmedia_endpt *endpt; unsigned stream_cnt; pjmedia_stream_info stream_info[PJMEDIA_MAX_SDP_MEDIA]; pjmedia_stream *stream[PJMEDIA_MAX_SDP_MEDIA]; void *user_data; }; #define THIS_FILE "session.c" #ifndef PJMEDIA_SESSION_SIZE # define PJMEDIA_SESSION_SIZE (10*1024) #endif #ifndef PJMEDIA_SESSION_INC # define PJMEDIA_SESSION_INC 1024 #endif /* * Initialize session info from SDP session descriptors. */ PJ_DEF(pj_status_t) pjmedia_session_info_from_sdp( pj_pool_t *pool, pjmedia_endpt *endpt, unsigned max_streams, pjmedia_session_info *si, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote) { unsigned i; PJ_ASSERT_RETURN(pool && endpt && si && local && remote, PJ_EINVAL); si->stream_cnt = max_streams; if (si->stream_cnt > local->media_count) si->stream_cnt = local->media_count; for (i=0; istream_cnt; ++i) { pj_status_t status; status = pjmedia_stream_info_from_sdp( &si->stream_info[i], pool, endpt, local, remote, i); if (status != PJ_SUCCESS) return status; } return PJ_SUCCESS; } /** * Create new session. */ PJ_DEF(pj_status_t) pjmedia_session_create( pjmedia_endpt *endpt, const pjmedia_session_info *si, pjmedia_transport *transports[], void *user_data, pjmedia_session **p_session ) { pj_pool_t *pool; pjmedia_session *session; int i; /* Must be signed */ pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(endpt && si && p_session, PJ_EINVAL); /* Create pool for the session. */ pool = pjmedia_endpt_create_pool( endpt, "session", PJMEDIA_SESSION_SIZE, PJMEDIA_SESSION_INC); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); session = PJ_POOL_ZALLOC_T(pool, pjmedia_session); session->pool = pool; session->endpt = endpt; session->stream_cnt = si->stream_cnt; session->user_data = user_data; /* Copy stream info (this simple memcpy may break sometime) */ pj_memcpy(session->stream_info, si->stream_info, si->stream_cnt * sizeof(pjmedia_stream_info)); /* * Now create and start the stream! */ for (i=0; i<(int)si->stream_cnt; ++i) { /* Create the stream */ status = pjmedia_stream_create(endpt, session->pool, &session->stream_info[i], (transports?transports[i]:NULL), session, &session->stream[i]); if (status == PJ_SUCCESS) status = pjmedia_stream_start(session->stream[i]); if (status != PJ_SUCCESS) { for ( --i; i>=0; --i) { pjmedia_stream_destroy(session->stream[i]); } pj_pool_release(session->pool); return status; } } /* Done. */ *p_session = session; return PJ_SUCCESS; } /* * Get session info. */ PJ_DEF(pj_status_t) pjmedia_session_get_info( pjmedia_session *session, pjmedia_session_info *info ) { PJ_ASSERT_RETURN(session && info, PJ_EINVAL); info->stream_cnt = session->stream_cnt; pj_memcpy(info->stream_info, session->stream_info, session->stream_cnt * sizeof(pjmedia_stream_info)); return PJ_SUCCESS; } /* * Get user data. */ PJ_DEF(void*) pjmedia_session_get_user_data( pjmedia_session *session) { return (session? session->user_data : NULL); } /** * Destroy media session. */ PJ_DEF(pj_status_t) pjmedia_session_destroy (pjmedia_session *session) { unsigned i; PJ_ASSERT_RETURN(session, PJ_EINVAL); for (i=0; istream_cnt; ++i) { pjmedia_stream_destroy(session->stream[i]); } pj_pool_release (session->pool); return PJ_SUCCESS; } /** * Activate all stream in media session. * */ PJ_DEF(pj_status_t) pjmedia_session_resume(pjmedia_session *session, pjmedia_dir dir) { unsigned i; PJ_ASSERT_RETURN(session, PJ_EINVAL); for (i=0; istream_cnt; ++i) { pjmedia_session_resume_stream(session, i, dir); } return PJ_SUCCESS; } /** * Suspend receipt and transmission of all stream in media session. * */ PJ_DEF(pj_status_t) pjmedia_session_pause(pjmedia_session *session, pjmedia_dir dir) { unsigned i; PJ_ASSERT_RETURN(session, PJ_EINVAL); for (i=0; istream_cnt; ++i) { pjmedia_session_pause_stream(session, i, dir); } return PJ_SUCCESS; } /** * Suspend receipt and transmission of individual stream in media session. */ PJ_DEF(pj_status_t) pjmedia_session_pause_stream( pjmedia_session *session, unsigned index, pjmedia_dir dir) { PJ_ASSERT_RETURN(session && index < session->stream_cnt, PJ_EINVAL); return pjmedia_stream_pause(session->stream[index], dir); } /** * Activate individual stream in media session. * */ PJ_DEF(pj_status_t) pjmedia_session_resume_stream( pjmedia_session *session, unsigned index, pjmedia_dir dir) { PJ_ASSERT_RETURN(session && index < session->stream_cnt, PJ_EINVAL); return pjmedia_stream_resume(session->stream[index], dir); } /** * Send RTCP SDES for the session. */ PJ_DEF(pj_status_t) pjmedia_session_send_rtcp_sdes( const pjmedia_session *session ) { unsigned i; PJ_ASSERT_RETURN(session, PJ_EINVAL); for (i=0; istream_cnt; ++i) { pjmedia_stream_send_rtcp_sdes(session->stream[i]); } return PJ_SUCCESS; } /** * Send RTCP BYE for the session. */ PJ_DEF(pj_status_t) pjmedia_session_send_rtcp_bye( const pjmedia_session *session ) { unsigned i; PJ_ASSERT_RETURN(session, PJ_EINVAL); for (i=0; istream_cnt; ++i) { pjmedia_stream_send_rtcp_bye(session->stream[i]); } return PJ_SUCCESS; } /** * Enumerate media stream in the session. */ PJ_DEF(pj_status_t) pjmedia_session_enum_streams(const pjmedia_session *session, unsigned *count, pjmedia_stream_info info[]) { unsigned i; PJ_ASSERT_RETURN(session && count && *count && info, PJ_EINVAL); if (*count > session->stream_cnt) *count = session->stream_cnt; for (i=0; i<*count; ++i) { pj_memcpy(&info[i], &session->stream_info[i], sizeof(pjmedia_stream_info)); } return PJ_SUCCESS; } /* * Get the port interface. */ PJ_DEF(pj_status_t) pjmedia_session_get_port( pjmedia_session *session, unsigned index, pjmedia_port **p_port) { return pjmedia_stream_get_port( session->stream[index], p_port); } /* * Get statistics */ PJ_DEF(pj_status_t) pjmedia_session_get_stream_stat( pjmedia_session *session, unsigned index, pjmedia_rtcp_stat *stat) { PJ_ASSERT_RETURN(session && stat && index < session->stream_cnt, PJ_EINVAL); return pjmedia_stream_get_stat(session->stream[index], stat); } /** * Reset session statistics. */ PJ_DEF(pj_status_t) pjmedia_session_reset_stream_stat( pjmedia_session *session, unsigned index) { PJ_ASSERT_RETURN(session && index < session->stream_cnt, PJ_EINVAL); return pjmedia_stream_reset_stat(session->stream[index]); } #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) /* * Get extended statistics */ PJ_DEF(pj_status_t) pjmedia_session_get_stream_stat_xr( pjmedia_session *session, unsigned index, pjmedia_rtcp_xr_stat *stat_xr) { PJ_ASSERT_RETURN(session && stat_xr && index < session->stream_cnt, PJ_EINVAL); return pjmedia_stream_get_stat_xr(session->stream[index], stat_xr); } #endif PJ_DEF(pj_status_t) pjmedia_session_get_stream_stat_jbuf( pjmedia_session *session, unsigned index, pjmedia_jb_state *state) { PJ_ASSERT_RETURN(session && state && index < session->stream_cnt, PJ_EINVAL); return pjmedia_stream_get_stat_jbuf(session->stream[index], state); } /* * Dial DTMF digit to the stream, using RFC 2833 mechanism. */ PJ_DEF(pj_status_t) pjmedia_session_dial_dtmf( pjmedia_session *session, unsigned index, const pj_str_t *ascii_digits ) { PJ_ASSERT_RETURN(session && ascii_digits, PJ_EINVAL); return pjmedia_stream_dial_dtmf(session->stream[index], ascii_digits); } /* * Check if the specified stream has received DTMF digits. */ PJ_DEF(pj_status_t) pjmedia_session_check_dtmf( pjmedia_session *session, unsigned index ) { PJ_ASSERT_RETURN(session, PJ_EINVAL); return pjmedia_stream_check_dtmf(session->stream[index]); } /* * Retrieve DTMF digits from the specified stream. */ PJ_DEF(pj_status_t) pjmedia_session_get_dtmf( pjmedia_session *session, unsigned index, char *ascii_digits, unsigned *size ) { PJ_ASSERT_RETURN(session && ascii_digits && size, PJ_EINVAL); return pjmedia_stream_get_dtmf(session->stream[index], ascii_digits, size); } /* * Install DTMF callback. */ PJ_DEF(pj_status_t) pjmedia_session_set_dtmf_callback(pjmedia_session *session, unsigned index, void (*cb)(pjmedia_stream*, void *user_data, int digit), void *user_data) { PJ_ASSERT_RETURN(session && index < session->stream_cnt, PJ_EINVAL); return pjmedia_stream_set_dtmf_callback(session->stream[index], cb, user_data); } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/silencedet.c ================================================ /* $Id: silencedet.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #define THIS_FILE "silencedet.c" #if 1 # define TRACE_(x) PJ_LOG(5,x) #else # define TRACE_(x) #endif /** * This enumeration specifies operation mode of silence detector */ typedef enum pjmedia_silence_det_mode { VAD_MODE_NONE, VAD_MODE_FIXED, VAD_MODE_ADAPTIVE } pjmedia_silence_det_mode; /** * Default settings */ #define DEF_RECALC_ON_VOICED 4000 /* Time to recalculate threshold in voiced condition, in ms */ #define DEF_RECALC_ON_SILENCE 2000 /* Time to recalculate threshold in silence condition, in ms. */ #define DEF_BEFORE_SILENCE 400 /* Silence time before really changing state into SILENCE, in ms. */ #define DEF_THRESHOLD 1000 /* Default threshold. */ /** * This enumeration specifies the states of the silence detector. */ enum pjmedia_silence_det_state { STATE_SILENCE, STATE_START_SILENCE, STATE_VOICED }; /** * This structure holds the silence detector state. */ struct pjmedia_silence_det { char objname[PJ_MAX_OBJ_NAME]; /**< VAD name. */ int mode; /**< VAD mode. */ unsigned ptime; /**< Frame time, in msec. */ unsigned threshold; /**< Current threshold level. */ unsigned sum_level; /**< Total sum of recent level. */ unsigned sum_cnt; /**< Number of level summed. */ unsigned silence_timer; /**< Silence condition timer. */ unsigned voiced_timer; /**< Voiced condition timer. */ enum pjmedia_silence_det_state state;/**< Silence detector state. */ unsigned recalc_on_voiced; /**< Setting of time to recalc threshold in voiced condition. */ unsigned recalc_on_silence; /**< Setting of time to recalc threshold in silence condition.*/ unsigned before_silence; /**< Setting of silence time before really changing state into SILENCE, in ms. */ }; PJ_DEF(pj_status_t) pjmedia_silence_det_create( pj_pool_t *pool, unsigned clock_rate, unsigned samples_per_frame, pjmedia_silence_det **p_sd) { pjmedia_silence_det *sd; PJ_ASSERT_RETURN(pool && p_sd, PJ_EINVAL); sd = PJ_POOL_ZALLOC_T(pool, pjmedia_silence_det); pj_ansi_snprintf(sd->objname, PJ_MAX_OBJ_NAME, "sd%p", sd); sd->objname[PJ_MAX_OBJ_NAME-1] = '\0'; sd->ptime = samples_per_frame * 1000 / clock_rate; /* Default settings */ pjmedia_silence_det_set_params(sd, -1, -1, -1); /* Restart in adaptive, silent mode */ pjmedia_silence_det_set_adaptive( sd, -1 ); *p_sd = sd; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_silence_det_set_name( pjmedia_silence_det *sd, const char *name) { PJ_ASSERT_RETURN(sd && name, PJ_EINVAL); pj_ansi_snprintf(sd->objname, PJ_MAX_OBJ_NAME, name, sd); sd->objname[PJ_MAX_OBJ_NAME-1] = '\0'; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_silence_det_set_adaptive(pjmedia_silence_det *sd, int threshold) { PJ_ASSERT_RETURN(sd, PJ_EINVAL); if (threshold < 0) threshold = DEF_THRESHOLD; sd->mode = VAD_MODE_ADAPTIVE; sd->threshold = threshold; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_silence_det_set_fixed( pjmedia_silence_det *sd, int threshold ) { PJ_ASSERT_RETURN(sd, PJ_EINVAL); if (threshold < 0) threshold = DEF_THRESHOLD; sd->mode = VAD_MODE_FIXED; sd->threshold = threshold; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_silence_det_set_params( pjmedia_silence_det *sd, int before_silence, int recalc_time1, int recalc_time2) { PJ_ASSERT_RETURN(sd, PJ_EINVAL); if (recalc_time1 < 0) recalc_time1 = DEF_RECALC_ON_VOICED; if (recalc_time2 < 0) recalc_time2 = DEF_RECALC_ON_SILENCE; if (before_silence < 0) before_silence = DEF_BEFORE_SILENCE; sd->recalc_on_voiced = recalc_time1; sd->recalc_on_silence = recalc_time2; sd->before_silence = before_silence; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_silence_det_disable( pjmedia_silence_det *sd ) { PJ_ASSERT_RETURN(sd, PJ_EINVAL); sd->mode = VAD_MODE_NONE; return PJ_SUCCESS; } PJ_DEF(pj_int32_t) pjmedia_calc_avg_signal( const pj_int16_t samples[], pj_size_t count) { pj_uint32_t sum = 0; const pj_int16_t * pcm = samples; const pj_int16_t * end = samples + count; if (count==0) return 0; while (pcm != end) { if (*pcm < 0) sum -= *pcm++; else sum += *pcm++; } return (pj_int32_t)(sum / count); } PJ_DEF(pj_bool_t) pjmedia_silence_det_apply( pjmedia_silence_det *sd, pj_uint32_t level) { int avg_recent_level; if (sd->mode == VAD_MODE_NONE) return PJ_FALSE; if (sd->mode == VAD_MODE_FIXED) return (level < sd->threshold); /* Calculating recent level */ sd->sum_level += level; ++sd->sum_cnt; avg_recent_level = (sd->sum_level / sd->sum_cnt); if (level > sd->threshold || level >= PJMEDIA_SILENCE_DET_MAX_THRESHOLD) { sd->silence_timer = 0; sd->voiced_timer += sd->ptime; switch(sd->state) { case STATE_VOICED: if (sd->voiced_timer > sd->recalc_on_voiced) { /* Voiced for long time (>recalc_on_voiced), current * threshold seems to be too low. */ sd->threshold = (avg_recent_level + sd->threshold) >> 1; TRACE_((THIS_FILE,"Re-adjust threshold (in talk burst)" "to %d", sd->threshold)); sd->voiced_timer = 0; /* Reset sig_level */ sd->sum_level = avg_recent_level; sd->sum_cnt = 1; } break; case STATE_SILENCE: TRACE_((THIS_FILE,"Starting talk burst (level=%d threshold=%d)", level, sd->threshold)); case STATE_START_SILENCE: sd->state = STATE_VOICED; /* Reset sig_level */ sd->sum_level = level; sd->sum_cnt = 1; break; default: pj_assert(0); break; } } else { sd->voiced_timer = 0; sd->silence_timer += sd->ptime; switch(sd->state) { case STATE_SILENCE: if (sd->silence_timer >= sd->recalc_on_silence) { sd->threshold = avg_recent_level << 1; TRACE_((THIS_FILE,"Re-adjust threshold (in silence)" "to %d", sd->threshold)); sd->silence_timer = 0; /* Reset sig_level */ sd->sum_level = avg_recent_level; sd->sum_cnt = 1; } break; case STATE_VOICED: sd->state = STATE_START_SILENCE; /* Reset sig_level */ sd->sum_level = level; sd->sum_cnt = 1; case STATE_START_SILENCE: if (sd->silence_timer >= sd->before_silence) { sd->state = STATE_SILENCE; sd->threshold = avg_recent_level << 1; TRACE_((THIS_FILE,"Starting silence (level=%d " "threshold=%d)", level, sd->threshold)); /* Reset sig_level */ sd->sum_level = avg_recent_level; sd->sum_cnt = 1; } break; default: pj_assert(0); break; } } return (sd->state == STATE_SILENCE); } PJ_DEF(pj_bool_t) pjmedia_silence_det_detect( pjmedia_silence_det *sd, const pj_int16_t samples[], pj_size_t count, pj_int32_t *p_level) { pj_uint32_t level; /* Calculate average signal level. */ level = pjmedia_calc_avg_signal(samples, count); /* Report to caller, if required. */ if (p_level) *p_level = level; return pjmedia_silence_det_apply(sd, level); } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/sound_legacy.c ================================================ /* $Id: sound_legacy.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This is implementation of legacy sound device API, for applications * that still use the old/deprecated sound device API. This implementation * uses the new Audio Device API. * * Please see http://trac.pjsip.org/repos/wiki/Audio_Dev_API for more * information. */ #include #include #include #if PJMEDIA_HAS_LEGACY_SOUND_API static struct legacy_subsys { pjmedia_snd_dev_info info[4]; unsigned info_counter; unsigned user_rec_latency; unsigned user_play_latency; } g_sys; struct pjmedia_snd_stream { pj_pool_t *pool; pjmedia_aud_stream *aud_strm; pjmedia_snd_rec_cb user_rec_cb; pjmedia_snd_play_cb user_play_cb; void *user_user_data; }; PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory) { return pjmedia_aud_subsys_init(factory); } PJ_DEF(pj_status_t) pjmedia_snd_deinit(void) { return pjmedia_aud_subsys_shutdown(); } PJ_DEF(int) pjmedia_snd_get_dev_count(void) { return pjmedia_aud_dev_count(); } PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index) { pjmedia_snd_dev_info *oi = &g_sys.info[g_sys.info_counter]; pjmedia_aud_dev_info di; g_sys.info_counter = (g_sys.info_counter+1) % PJ_ARRAY_SIZE(g_sys.info); if (pjmedia_aud_dev_get_info(index, &di) != PJ_SUCCESS) return NULL; pj_bzero(oi, sizeof(*oi)); pj_ansi_strncpy(oi->name, di.name, sizeof(oi->name)); oi->name[sizeof(oi->name)-1] = '\0'; oi->input_count = di.input_count; oi->output_count = di.output_count; oi->default_samples_per_sec = di.default_samples_per_sec; return oi; } static pj_status_t snd_play_cb(void *user_data, pjmedia_frame *frame) { pjmedia_snd_stream *strm = (pjmedia_snd_stream*)user_data; frame->type = PJMEDIA_FRAME_TYPE_AUDIO; return strm->user_play_cb(strm->user_user_data, frame->timestamp.u32.lo, frame->buf, (unsigned)frame->size); } static pj_status_t snd_rec_cb(void *user_data, pjmedia_frame *frame) { pjmedia_snd_stream *strm = (pjmedia_snd_stream*)user_data; return strm->user_rec_cb(strm->user_user_data, frame->timestamp.u32.lo, frame->buf, (unsigned)frame->size); } static pj_status_t open_stream( pjmedia_dir dir, int rec_id, int play_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_snd_rec_cb rec_cb, pjmedia_snd_play_cb play_cb, void *user_data, pjmedia_snd_stream **p_snd_strm) { pj_pool_t *pool; pjmedia_snd_stream *snd_strm; pjmedia_aud_param param; pj_status_t status; /* Initialize parameters */ if (dir & PJMEDIA_DIR_CAPTURE) { status = pjmedia_aud_dev_default_param(rec_id, ¶m); } else { status = pjmedia_aud_dev_default_param(play_id, ¶m); } if (status != PJ_SUCCESS) return status; param.dir = dir; param.rec_id = rec_id; param.play_id = play_id; param.clock_rate = clock_rate; param.channel_count = channel_count; param.samples_per_frame = samples_per_frame; param.bits_per_sample = bits_per_sample; /* Latencies setting */ if ((dir & PJMEDIA_DIR_CAPTURE) && g_sys.user_rec_latency) { param.flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY; param.input_latency_ms = g_sys.user_rec_latency; } if ((dir & PJMEDIA_DIR_PLAYBACK) && g_sys.user_play_latency) { param.flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; param.output_latency_ms = g_sys.user_play_latency; } /* Create sound wrapper */ pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "legacy-snd", 512, 512, NULL); snd_strm = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_stream); snd_strm->pool = pool; snd_strm->user_rec_cb = rec_cb; snd_strm->user_play_cb = play_cb; snd_strm->user_user_data = user_data; /* Create the stream */ status = pjmedia_aud_stream_create(¶m, &snd_rec_cb, &snd_play_cb, snd_strm, &snd_strm->aud_strm); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } *p_snd_strm = snd_strm; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_snd_rec_cb rec_cb, void *user_data, pjmedia_snd_stream **p_snd_strm) { return open_stream(PJMEDIA_DIR_CAPTURE, index, PJMEDIA_AUD_INVALID_DEV, clock_rate, channel_count, samples_per_frame, bits_per_sample, rec_cb, NULL, user_data, p_snd_strm); } PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_snd_play_cb play_cb, void *user_data, pjmedia_snd_stream **p_snd_strm ) { return open_stream(PJMEDIA_DIR_PLAYBACK, PJMEDIA_AUD_INVALID_DEV, index, clock_rate, channel_count, samples_per_frame, bits_per_sample, NULL, play_cb, user_data, p_snd_strm); } PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id, int play_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_snd_rec_cb rec_cb, pjmedia_snd_play_cb play_cb, void *user_data, pjmedia_snd_stream **p_snd_strm) { return open_stream(PJMEDIA_DIR_CAPTURE_PLAYBACK, rec_id, play_id, clock_rate, channel_count, samples_per_frame, bits_per_sample, rec_cb, play_cb, user_data, p_snd_strm); } PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream) { return pjmedia_aud_stream_start(stream->aud_strm); } PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream) { return pjmedia_aud_stream_stop(stream->aud_strm); } PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm, pjmedia_snd_stream_info *pi) { pjmedia_aud_param param; pj_status_t status; status = pjmedia_aud_stream_get_param(strm->aud_strm, ¶m); if (status != PJ_SUCCESS) return status; pj_bzero(pi, sizeof(*pi)); pi->dir = param.dir; pi->play_id = param.play_id; pi->rec_id = param.rec_id; pi->clock_rate = param.clock_rate; pi->channel_count = param.channel_count; pi->samples_per_frame = param.samples_per_frame; pi->bits_per_sample = param.bits_per_sample; if (param.flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY) { pi->rec_latency = param.input_latency_ms; } if (param.flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY) { pi->play_latency = param.output_latency_ms; } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream) { pj_status_t status; status = pjmedia_aud_stream_destroy(stream->aud_strm); if (status != PJ_SUCCESS) return status; pj_pool_release(stream->pool); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency, unsigned output_latency) { g_sys.user_rec_latency = input_latency; g_sys.user_play_latency = output_latency; return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_LEGACY_SOUND_API */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/sound_port.c ================================================ /* $Id: sound_port.c 4487 2013-04-23 05:37:41Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include /* pj_memset() */ #define AEC_TAIL 128 /* default AEC length in ms */ #define AEC_SUSPEND_LIMIT 5 /* seconds of no activity */ #define THIS_FILE "sound_port.c" //#define TEST_OVERFLOW_UNDERFLOW struct pjmedia_snd_port { int rec_id; int play_id; pj_uint32_t aud_caps; pjmedia_aud_param aud_param; pjmedia_aud_stream *aud_stream; pjmedia_dir dir; pjmedia_port *port; pjmedia_clock_src cap_clocksrc, play_clocksrc; unsigned clock_rate; unsigned channel_count; unsigned samples_per_frame; unsigned bits_per_sample; unsigned options; unsigned prm_ec_options; /* software ec */ pjmedia_echo_state *ec_state; unsigned ec_options; unsigned ec_tail_len; pj_bool_t ec_suspended; unsigned ec_suspend_count; unsigned ec_suspend_limit; /* audio frame preview callbacks */ void *user_data; pjmedia_aud_play_cb on_play_frame; pjmedia_aud_rec_cb on_rec_frame; }; /* * The callback called by sound player when it needs more samples to be * played. */ static pj_status_t play_cb(void *user_data, pjmedia_frame *frame) { pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; pjmedia_port *port; const unsigned required_size = (unsigned)frame->size; pj_status_t status; pjmedia_clock_src_update(&snd_port->play_clocksrc, &frame->timestamp); port = snd_port->port; if (port == NULL) goto no_frame; status = pjmedia_port_get_frame(port, frame); if (status != PJ_SUCCESS) goto no_frame; if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) goto no_frame; /* Must supply the required samples */ pj_assert(frame->size == required_size); if (snd_port->ec_state) { if (snd_port->ec_suspended) { snd_port->ec_suspended = PJ_FALSE; pjmedia_echo_reset(snd_port->ec_state); PJ_LOG(4,(THIS_FILE, "EC activated")); } snd_port->ec_suspend_count = 0; pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf); } /* Invoke preview callback */ if (snd_port->on_play_frame) (*snd_port->on_play_frame)(snd_port->user_data, frame); return PJ_SUCCESS; no_frame: frame->type = PJMEDIA_FRAME_TYPE_AUDIO; frame->size = required_size; pj_bzero(frame->buf, frame->size); if (snd_port->ec_state && !snd_port->ec_suspended) { ++snd_port->ec_suspend_count; if (snd_port->ec_suspend_count > snd_port->ec_suspend_limit) { snd_port->ec_suspended = PJ_TRUE; PJ_LOG(4,(THIS_FILE, "EC suspended because of inactivity")); } if (snd_port->ec_state) { /* To maintain correct delay in EC */ pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf); } } /* Invoke preview callback */ if (snd_port->on_play_frame) (*snd_port->on_play_frame)(snd_port->user_data, frame); return PJ_SUCCESS; } /* * The callback called by sound recorder when it has finished capturing a * frame. */ static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame) { pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; pjmedia_port *port; pjmedia_clock_src_update(&snd_port->cap_clocksrc, &frame->timestamp); /* Invoke preview callback */ if (snd_port->on_rec_frame) (*snd_port->on_rec_frame)(snd_port->user_data, frame); port = snd_port->port; if (port == NULL) return PJ_SUCCESS; /* Cancel echo */ if (snd_port->ec_state && !snd_port->ec_suspended) { pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) frame->buf, 0); } pjmedia_port_put_frame(port, frame); return PJ_SUCCESS; } /* * The callback called by sound player when it needs more samples to be * played. This version is for non-PCM data. */ static pj_status_t play_cb_ext(void *user_data, pjmedia_frame *frame) { pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; pjmedia_port *port = snd_port->port; if (port == NULL) { frame->type = PJMEDIA_FRAME_TYPE_NONE; return PJ_SUCCESS; } pjmedia_port_get_frame(port, frame); /* Invoke preview callback */ if (snd_port->on_play_frame) (*snd_port->on_play_frame)(snd_port->user_data, frame); return PJ_SUCCESS; } /* * The callback called by sound recorder when it has finished capturing a * frame. This version is for non-PCM data. */ static pj_status_t rec_cb_ext(void *user_data, pjmedia_frame *frame) { pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; pjmedia_port *port; /* Invoke preview callback */ if (snd_port->on_rec_frame) (*snd_port->on_rec_frame)(snd_port->user_data, frame); port = snd_port->port; if (port == NULL) return PJ_SUCCESS; pjmedia_port_put_frame(port, frame); return PJ_SUCCESS; } /* Initialize with default values (zero) */ PJ_DEF(void) pjmedia_snd_port_param_default(pjmedia_snd_port_param *prm) { pj_bzero(prm, sizeof(*prm)); } /* * Start the sound stream. * This may be called even when the sound stream has already been started. */ static pj_status_t start_sound_device( pj_pool_t *pool, pjmedia_snd_port *snd_port ) { pjmedia_aud_rec_cb snd_rec_cb; pjmedia_aud_play_cb snd_play_cb; pjmedia_aud_param param_copy; pj_status_t status; /* Check if sound has been started. */ if (snd_port->aud_stream != NULL) return PJ_SUCCESS; PJ_ASSERT_RETURN(snd_port->dir == PJMEDIA_DIR_CAPTURE || snd_port->dir == PJMEDIA_DIR_PLAYBACK || snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK, PJ_EBUG); /* Get device caps */ if (snd_port->aud_param.dir & PJMEDIA_DIR_CAPTURE) { pjmedia_aud_dev_info dev_info; status = pjmedia_aud_dev_get_info(snd_port->aud_param.rec_id, &dev_info); if (status != PJ_SUCCESS) return status; snd_port->aud_caps = dev_info.caps; } else { snd_port->aud_caps = 0; } /* Process EC settings */ pj_memcpy(¶m_copy, &snd_port->aud_param, sizeof(param_copy)); if (param_copy.flags & PJMEDIA_AUD_DEV_CAP_EC) { /* EC is wanted */ if ((snd_port->prm_ec_options & PJMEDIA_ECHO_USE_SW_ECHO) == 0 && (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC)) { /* Device supports EC */ /* Nothing to do */ } else { /* Application wants to use software EC or device * doesn't support EC, remove EC settings from * device parameters */ param_copy.flags &= ~(PJMEDIA_AUD_DEV_CAP_EC | PJMEDIA_AUD_DEV_CAP_EC_TAIL); } } /* Use different callback if format is not PCM */ if (snd_port->aud_param.ext_fmt.id == PJMEDIA_FORMAT_L16) { snd_rec_cb = &rec_cb; snd_play_cb = &play_cb; } else { snd_rec_cb = &rec_cb_ext; snd_play_cb = &play_cb_ext; } /* Open the device */ status = pjmedia_aud_stream_create(¶m_copy, snd_rec_cb, snd_play_cb, snd_port, &snd_port->aud_stream); if (status != PJ_SUCCESS) return status; /* Inactivity limit before EC is suspended. */ snd_port->ec_suspend_limit = AEC_SUSPEND_LIMIT * (snd_port->clock_rate / snd_port->samples_per_frame); /* Create software EC if parameter specifies EC and * (app specifically requests software EC or device * doesn't support EC). Only do this if the format is PCM! */ if ((snd_port->aud_param.flags & PJMEDIA_AUD_DEV_CAP_EC) && ((snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC)==0 || (snd_port->prm_ec_options & PJMEDIA_ECHO_USE_SW_ECHO) != 0) && param_copy.ext_fmt.id == PJMEDIA_FORMAT_PCM) { if ((snd_port->aud_param.flags & PJMEDIA_AUD_DEV_CAP_EC_TAIL)==0) { snd_port->aud_param.flags |= PJMEDIA_AUD_DEV_CAP_EC_TAIL; snd_port->aud_param.ec_tail_ms = AEC_TAIL; PJ_LOG(4,(THIS_FILE, "AEC tail is set to default %u ms", snd_port->aud_param.ec_tail_ms)); } pjmedia_snd_port_set_ec(snd_port, pool, snd_port->aud_param.ec_tail_ms, snd_port->prm_ec_options); } /* Start sound stream. */ if (!(snd_port->options & PJMEDIA_SND_PORT_NO_AUTO_START)) { status = pjmedia_aud_stream_start(snd_port->aud_stream); } if (status != PJ_SUCCESS) { pjmedia_aud_stream_destroy(snd_port->aud_stream); snd_port->aud_stream = NULL; return status; } return PJ_SUCCESS; } /* * Stop the sound device. * This may be called even when there's no sound device in the port. */ static pj_status_t stop_sound_device( pjmedia_snd_port *snd_port ) { /* Check if we have sound stream device. */ if (snd_port->aud_stream) { pjmedia_aud_stream_stop(snd_port->aud_stream); pjmedia_aud_stream_destroy(snd_port->aud_stream); snd_port->aud_stream = NULL; } /* Destroy AEC */ if (snd_port->ec_state) { pjmedia_echo_destroy(snd_port->ec_state); snd_port->ec_state = NULL; } return PJ_SUCCESS; } /* * Create bidirectional port. */ PJ_DEF(pj_status_t) pjmedia_snd_port_create( pj_pool_t *pool, int rec_id, int play_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_snd_port **p_port) { pjmedia_snd_port_param param; pj_status_t status; pjmedia_snd_port_param_default(¶m); /* Normalize rec_id & play_id */ if (rec_id < 0) rec_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV; if (play_id < 0) play_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV; status = pjmedia_aud_dev_default_param(rec_id, ¶m.base); if (status != PJ_SUCCESS) return status; param.base.dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; param.base.rec_id = rec_id; param.base.play_id = play_id; param.base.clock_rate = clock_rate; param.base.channel_count = channel_count; param.base.samples_per_frame = samples_per_frame; param.base.bits_per_sample = bits_per_sample; param.options = options; param.ec_options = 0; return pjmedia_snd_port_create2(pool, ¶m, p_port); } /* * Create sound recorder AEC. */ PJ_DEF(pj_status_t) pjmedia_snd_port_create_rec( pj_pool_t *pool, int dev_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_snd_port **p_port) { pjmedia_snd_port_param param; pj_status_t status; pjmedia_snd_port_param_default(¶m); /* Normalize dev_id */ if (dev_id < 0) dev_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV; status = pjmedia_aud_dev_default_param(dev_id, ¶m.base); if (status != PJ_SUCCESS) return status; param.base.dir = PJMEDIA_DIR_CAPTURE; param.base.rec_id = dev_id; param.base.clock_rate = clock_rate; param.base.channel_count = channel_count; param.base.samples_per_frame = samples_per_frame; param.base.bits_per_sample = bits_per_sample; param.options = options; param.ec_options = 0; return pjmedia_snd_port_create2(pool, ¶m, p_port); } /* * Create sound player port. */ PJ_DEF(pj_status_t) pjmedia_snd_port_create_player( pj_pool_t *pool, int dev_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_snd_port **p_port) { pjmedia_snd_port_param param; pj_status_t status; pjmedia_snd_port_param_default(¶m); /* Normalize dev_id */ if (dev_id < 0) dev_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV; status = pjmedia_aud_dev_default_param(dev_id, ¶m.base); if (status != PJ_SUCCESS) return status; param.base.dir = PJMEDIA_DIR_PLAYBACK; param.base.play_id = dev_id; param.base.clock_rate = clock_rate; param.base.channel_count = channel_count; param.base.samples_per_frame = samples_per_frame; param.base.bits_per_sample = bits_per_sample; param.options = options; param.ec_options = 0; return pjmedia_snd_port_create2(pool, ¶m, p_port); } /* * Create sound port. */ PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool, const pjmedia_snd_port_param *prm, pjmedia_snd_port **p_port) { pjmedia_snd_port *snd_port; pj_status_t status; unsigned ptime_usec; PJ_ASSERT_RETURN(pool && prm && p_port, PJ_EINVAL); snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port); PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM); snd_port->dir = prm->base.dir; snd_port->rec_id = prm->base.rec_id; snd_port->play_id = prm->base.play_id; snd_port->clock_rate = prm->base.clock_rate; snd_port->channel_count = prm->base.channel_count; snd_port->samples_per_frame = prm->base.samples_per_frame; snd_port->bits_per_sample = prm->base.bits_per_sample; pj_memcpy(&snd_port->aud_param, &prm->base, sizeof(snd_port->aud_param)); snd_port->options = prm->options; snd_port->prm_ec_options = prm->ec_options; snd_port->user_data = prm->user_data; snd_port->on_play_frame = prm->on_play_frame; snd_port->on_rec_frame = prm->on_rec_frame; ptime_usec = prm->base.samples_per_frame * 1000 / prm->base.channel_count / prm->base.clock_rate * 1000; pjmedia_clock_src_init(&snd_port->cap_clocksrc, PJMEDIA_TYPE_AUDIO, snd_port->clock_rate, ptime_usec); pjmedia_clock_src_init(&snd_port->play_clocksrc, PJMEDIA_TYPE_AUDIO, snd_port->clock_rate, ptime_usec); /* Start sound device immediately. * If there's no port connected, the sound callback will return * empty signal. */ status = start_sound_device( pool, snd_port ); if (status != PJ_SUCCESS) { pjmedia_snd_port_destroy(snd_port); return status; } *p_port = snd_port; return PJ_SUCCESS; } /* * Destroy port (also destroys the sound device). */ PJ_DEF(pj_status_t) pjmedia_snd_port_destroy(pjmedia_snd_port *snd_port) { PJ_ASSERT_RETURN(snd_port, PJ_EINVAL); return stop_sound_device(snd_port); } /* * Retrieve the sound stream associated by this sound device port. */ PJ_DEF(pjmedia_aud_stream*) pjmedia_snd_port_get_snd_stream( pjmedia_snd_port *snd_port) { PJ_ASSERT_RETURN(snd_port, NULL); return snd_port->aud_stream; } /* Reset EC state */ PJ_DEF(pj_status_t) pjmedia_snd_port_reset_ec_state( pjmedia_snd_port *snd_port ) { PJ_ASSERT_RETURN(snd_port, PJ_EINVAL); if (snd_port->ec_state) { pjmedia_echo_reset(snd_port->ec_state); PJ_LOG(4,(THIS_FILE, "EC reset")); } return PJ_SUCCESS; } /* * Change EC settings. */ PJ_DEF(pj_status_t) pjmedia_snd_port_set_ec( pjmedia_snd_port *snd_port, pj_pool_t *pool, unsigned tail_ms, unsigned options) { pjmedia_aud_param prm; pj_status_t status; /* Sound must be opened in full-duplex mode */ PJ_ASSERT_RETURN(snd_port && snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK, PJ_EINVALIDOP); /* Determine whether we use device or software EC */ if ((snd_port->prm_ec_options & PJMEDIA_ECHO_USE_SW_ECHO) == 0 && (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC)) { /* We use device EC */ pj_bool_t ec_enabled; /* Query EC status */ status = pjmedia_aud_stream_get_cap(snd_port->aud_stream, PJMEDIA_AUD_DEV_CAP_EC, &ec_enabled); if (status != PJ_SUCCESS) return status; if (tail_ms != 0) { /* Change EC setting */ if (!ec_enabled) { /* Enable EC first */ pj_bool_t value = PJ_TRUE; status = pjmedia_aud_stream_set_cap(snd_port->aud_stream, PJMEDIA_AUD_DEV_CAP_EC, &value); if (status != PJ_SUCCESS) return status; } if ((snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC_TAIL)==0) { /* Device does not support setting EC tail */ return PJMEDIA_EAUD_INVCAP; } return pjmedia_aud_stream_set_cap(snd_port->aud_stream, PJMEDIA_AUD_DEV_CAP_EC_TAIL, &tail_ms); } else if (ec_enabled) { /* Disable EC */ pj_bool_t value = PJ_FALSE; return pjmedia_aud_stream_set_cap(snd_port->aud_stream, PJMEDIA_AUD_DEV_CAP_EC, &value); } else { /* Request to disable EC but EC has been disabled */ /* Do nothing */ return PJ_SUCCESS; } } else { /* We use software EC */ /* Check if there is change in parameters */ if (tail_ms==snd_port->ec_tail_len && options==snd_port->ec_options) { PJ_LOG(5,(THIS_FILE, "pjmedia_snd_port_set_ec() ignored, no " "change in settings")); return PJ_SUCCESS; } status = pjmedia_aud_stream_get_param(snd_port->aud_stream, &prm); if (status != PJ_SUCCESS) return status; /* Audio stream must be in PCM format */ PJ_ASSERT_RETURN(prm.ext_fmt.id == PJMEDIA_FORMAT_PCM, PJ_EINVALIDOP); /* Destroy AEC */ if (snd_port->ec_state) { pjmedia_echo_destroy(snd_port->ec_state); snd_port->ec_state = NULL; } if (tail_ms != 0) { unsigned delay_ms; //No need to add input latency in the latency calculation, //since actual input latency should be zero. //delay_ms = (si.rec_latency + si.play_latency) * 1000 / // snd_port->clock_rate; /* Set EC latency to 3/4 of output latency to reduce the * possibility of missing/late reference frame. */ delay_ms = prm.output_latency_ms * 3/4; status = pjmedia_echo_create2(pool, snd_port->clock_rate, snd_port->channel_count, snd_port->samples_per_frame, tail_ms, delay_ms, options, &snd_port->ec_state); if (status != PJ_SUCCESS) snd_port->ec_state = NULL; else snd_port->ec_suspended = PJ_FALSE; } else { PJ_LOG(4,(THIS_FILE, "Echo canceller is now disabled in the " "sound port")); status = PJ_SUCCESS; } snd_port->ec_options = options; snd_port->ec_tail_len = tail_ms; } return status; } /* Get AEC tail length */ PJ_DEF(pj_status_t) pjmedia_snd_port_get_ec_tail( pjmedia_snd_port *snd_port, unsigned *p_length) { PJ_ASSERT_RETURN(snd_port && p_length, PJ_EINVAL); /* Determine whether we use device or software EC */ if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC) { /* We use device EC */ pj_bool_t ec_enabled; pj_status_t status; /* Query EC status */ status = pjmedia_aud_stream_get_cap(snd_port->aud_stream, PJMEDIA_AUD_DEV_CAP_EC, &ec_enabled); if (status != PJ_SUCCESS) return status; if (!ec_enabled) { *p_length = 0; } else if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC_TAIL) { /* Get device EC tail */ status = pjmedia_aud_stream_get_cap(snd_port->aud_stream, PJMEDIA_AUD_DEV_CAP_EC_TAIL, p_length); if (status != PJ_SUCCESS) return status; } else { /* Just use default */ *p_length = AEC_TAIL; } } else { /* We use software EC */ *p_length = snd_port->ec_state ? snd_port->ec_tail_len : 0; } return PJ_SUCCESS; } /* * Get clock source. */ PJ_DEF(pjmedia_clock_src *) pjmedia_snd_port_get_clock_src( pjmedia_snd_port *snd_port, pjmedia_dir dir ) { return (dir == PJMEDIA_DIR_CAPTURE? &snd_port->cap_clocksrc: &snd_port->play_clocksrc); } /* * Connect a port. */ PJ_DEF(pj_status_t) pjmedia_snd_port_connect( pjmedia_snd_port *snd_port, pjmedia_port *port) { pjmedia_audio_format_detail *afd; PJ_ASSERT_RETURN(snd_port && port, PJ_EINVAL); afd = pjmedia_format_get_audio_format_detail(&port->info.fmt, PJ_TRUE); /* Check that port has the same configuration as the sound device * port. */ if (afd->clock_rate != snd_port->clock_rate) return PJMEDIA_ENCCLOCKRATE; if (PJMEDIA_AFD_SPF(afd) != snd_port->samples_per_frame) return PJMEDIA_ENCSAMPLESPFRAME; if (afd->channel_count != snd_port->channel_count) return PJMEDIA_ENCCHANNEL; if (afd->bits_per_sample != snd_port->bits_per_sample) return PJMEDIA_ENCBITS; /* Port is okay. */ snd_port->port = port; return PJ_SUCCESS; } /* * Get the connected port. */ PJ_DEF(pjmedia_port*) pjmedia_snd_port_get_port(pjmedia_snd_port *snd_port) { PJ_ASSERT_RETURN(snd_port, NULL); return snd_port->port; } /* * Disconnect port. */ PJ_DEF(pj_status_t) pjmedia_snd_port_disconnect(pjmedia_snd_port *snd_port) { PJ_ASSERT_RETURN(snd_port, PJ_EINVAL); snd_port->port = NULL; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/splitcomb.c ================================================ /* $Id: splitcomb.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #define SIGNATURE PJMEDIA_SIG_PORT_SPLIT_COMB #define SIGNATURE_PORT PJMEDIA_SIG_PORT_SPLIT_COMB_P #define THIS_FILE "splitcomb.c" #define TMP_SAMP_TYPE pj_int16_t /* Maximum number of channels. */ #define MAX_CHANNELS 16 /* Maximum number of buffers to be accommodated by delaybuf */ #define MAX_BUF_CNT PJMEDIA_SOUND_BUFFER_COUNT /* Maximum number of burst before we pause the media flow */ #define MAX_BURST (buf_cnt + 6) /* Maximum number of NULL frames received before we pause the * media flow. */ #define MAX_NULL_FRAMES (rport->max_burst) /* Operations */ #define OP_PUT (1) #define OP_GET (-1) /* * Media flow directions: * * put_frame() +-----+ * UPSTREAM ------------>|split|<--> DOWNSTREAM * <------------|comb | * get_frame() +-----+ * */ enum sc_dir { /* This is the media direction from the splitcomb to the * downstream port(s), which happens when: * - put_frame() is called to the splitcomb * - get_frame() is called to the reverse channel port. */ DIR_DOWNSTREAM, /* This is the media direction from the downstream port to * the splitcomb, which happens when: * - get_frame() is called to the splitcomb * - put_frame() is called to the reverse channel port. */ DIR_UPSTREAM }; /* * This structure describes the splitter/combiner. */ struct splitcomb { pjmedia_port base; unsigned options; /* Array of ports, one for each channel */ struct { pjmedia_port *port; pj_bool_t reversed; } port_desc[MAX_CHANNELS]; /* Temporary buffers needed to extract mono frame from * multichannel frame. We could use stack for this, but this * way it should be safer for devices with small stack size. */ TMP_SAMP_TYPE *get_buf; TMP_SAMP_TYPE *put_buf; }; /* * This structure describes reverse port. */ struct reverse_port { pjmedia_port base; struct splitcomb*parent; unsigned ch_num; /* Maximum burst before media flow is suspended. * With reverse port, it's possible that either end of the * port doesn't actually process the media flow (meaning, it * stops calling get_frame()/put_frame()). When this happens, * the other end will encounter excessive underflow or overflow, * depending on which direction is not actively processed by * the stopping end. * * To avoid excessive underflow/overflow, the media flow will * be suspended once underflow/overflow goes over this max_burst * limit. */ int max_burst; /* When the media interface port of the splitcomb or the reverse * channel port is registered to conference bridge, the bridge * will transmit NULL frames to the media port when the media * port is not receiving any audio from other slots (for example, * when no other slots are connected to the media port). * * When this happens, we will generate zero frame to our buffer, * to avoid underflow/overflow. But after too many NULL frames * are received, we will pause the media flow instead, to save * some processing. * * This value controls how many NULL frames can be received * before we suspend media flow for a particular direction. */ unsigned max_null_frames; /* A reverse port need a temporary buffer to store frames * (because of the different phase, see splitcomb.h for details). * Since we can not expect get_frame() and put_frame() to be * called evenly one after another, we use delay buffers to * accomodate the burst. * * We maintain state for each direction, hence the array. The * array is indexed by direction (sc_dir). */ struct { /* The delay buffer where frames will be stored */ pjmedia_delay_buf *dbuf; /* Flag to indicate that audio flow on this direction * is currently being suspended (perhaps because nothing * is processing the frame on the other end). */ pj_bool_t paused; /* Operation level. When the level exceeds a maximum value, * the media flow on this direction will be paused. */ int level; /* Timestamp. */ pj_timestamp ts; /* Number of NULL frames transmitted to this port so far. * NULL frame indicate that nothing is transmitted, and * once we get too many of this, we should pause the media * flow to reduce processing. */ unsigned null_cnt; } buf[2]; /* Must have temporary put buffer for the delay buf, * unfortunately. */ pj_int16_t *tmp_up_buf; }; /* * Prototypes. */ static pj_status_t put_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t on_destroy(pjmedia_port *this_port); static pj_status_t rport_put_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t rport_get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t rport_on_destroy(pjmedia_port *this_port); /* * Create the splitter/combiner. */ PJ_DEF(pj_status_t) pjmedia_splitcomb_create( pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_port **p_splitcomb) { const pj_str_t name = pj_str("splitcomb"); struct splitcomb *sc; /* Sanity check */ PJ_ASSERT_RETURN(pool && clock_rate && channel_count && samples_per_frame && bits_per_sample && p_splitcomb, PJ_EINVAL); /* Only supports 16 bits per sample */ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); *p_splitcomb = NULL; /* Create the splitter/combiner structure */ sc = PJ_POOL_ZALLOC_T(pool, struct splitcomb); PJ_ASSERT_RETURN(sc != NULL, PJ_ENOMEM); /* Create temporary buffers */ sc->get_buf = (TMP_SAMP_TYPE*) pj_pool_alloc(pool, samples_per_frame * sizeof(TMP_SAMP_TYPE) / channel_count); PJ_ASSERT_RETURN(sc->get_buf, PJ_ENOMEM); sc->put_buf = (TMP_SAMP_TYPE*) pj_pool_alloc(pool, samples_per_frame * sizeof(TMP_SAMP_TYPE) / channel_count); PJ_ASSERT_RETURN(sc->put_buf, PJ_ENOMEM); /* Save options */ sc->options = options; /* Initialize port */ pjmedia_port_info_init(&sc->base.info, &name, SIGNATURE, clock_rate, channel_count, bits_per_sample, samples_per_frame); sc->base.put_frame = &put_frame; sc->base.get_frame = &get_frame; sc->base.on_destroy = &on_destroy; /* Init ports array */ /* sc->port_desc = pj_pool_zalloc(pool, channel_count*sizeof(*sc->port_desc)); */ pj_bzero(sc->port_desc, sizeof(sc->port_desc)); /* Done for now */ *p_splitcomb = &sc->base; return PJ_SUCCESS; } /* * Attach media port with the same phase as the splitter/combiner. */ PJ_DEF(pj_status_t) pjmedia_splitcomb_set_channel( pjmedia_port *splitcomb, unsigned ch_num, unsigned options, pjmedia_port *port) { struct splitcomb *sc = (struct splitcomb*) splitcomb; /* Sanity check */ PJ_ASSERT_RETURN(splitcomb && port, PJ_EINVAL); /* Make sure this is really a splitcomb port */ PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL); /* Check the channel number */ PJ_ASSERT_RETURN(ch_num < PJMEDIA_PIA_CCNT(&sc->base.info), PJ_EINVAL); /* options is unused for now */ PJ_UNUSED_ARG(options); sc->port_desc[ch_num].port = port; sc->port_desc[ch_num].reversed = PJ_FALSE; return PJ_SUCCESS; } /* * Create reverse phase port for the specified channel. */ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, pjmedia_port *splitcomb, unsigned ch_num, unsigned options, pjmedia_port **p_chport) { const pj_str_t name = pj_str("scomb-rev"); struct splitcomb *sc = (struct splitcomb*) splitcomb; struct reverse_port *rport; unsigned buf_cnt; const pjmedia_audio_format_detail *sc_afd, *p_afd; pjmedia_port *port; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(pool && splitcomb, PJ_EINVAL); /* Make sure this is really a splitcomb port */ PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL); /* Check the channel number */ PJ_ASSERT_RETURN(ch_num < PJMEDIA_PIA_CCNT(&sc->base.info), PJ_EINVAL); /* options is unused for now */ PJ_UNUSED_ARG(options); sc_afd = pjmedia_format_get_audio_format_detail(&splitcomb->info.fmt, 1); /* Create the port */ rport = PJ_POOL_ZALLOC_T(pool, struct reverse_port); rport->parent = sc; rport->ch_num = ch_num; /* Initialize port info... */ port = &rport->base; pjmedia_port_info_init(&port->info, &name, SIGNATURE_PORT, sc_afd->clock_rate, 1, sc_afd->bits_per_sample, PJMEDIA_PIA_SPF(&splitcomb->info) / sc_afd->channel_count); p_afd = pjmedia_format_get_audio_format_detail(&port->info.fmt, 1); /* ... and the callbacks */ port->put_frame = &rport_put_frame; port->get_frame = &rport_get_frame; port->on_destroy = &rport_on_destroy; /* Buffer settings */ buf_cnt = options & 0xFF; if (buf_cnt == 0) buf_cnt = MAX_BUF_CNT; rport->max_burst = MAX_BURST; rport->max_null_frames = MAX_NULL_FRAMES; /* Create downstream/put buffers */ status = pjmedia_delay_buf_create(pool, "scombdb-dn", p_afd->clock_rate, PJMEDIA_PIA_SPF(&port->info), p_afd->channel_count, buf_cnt * p_afd->frame_time_usec / 1000, 0, &rport->buf[DIR_DOWNSTREAM].dbuf); if (status != PJ_SUCCESS) { return status; } /* Create upstream/get buffers */ status = pjmedia_delay_buf_create(pool, "scombdb-up", p_afd->clock_rate, PJMEDIA_PIA_SPF(&port->info), p_afd->channel_count, buf_cnt * p_afd->frame_time_usec / 1000, 0, &rport->buf[DIR_UPSTREAM].dbuf); if (status != PJ_SUCCESS) { pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf); return status; } /* And temporary upstream/get buffer */ rport->tmp_up_buf = (pj_int16_t*) pj_pool_alloc(pool, PJMEDIA_PIA_AVG_FSZ(&port->info)); /* Save port in the splitcomb */ sc->port_desc[ch_num].port = &rport->base; sc->port_desc[ch_num].reversed = PJ_TRUE; /* Done */ *p_chport = port; return status; } /* * Extract one mono frame from a multichannel frame. */ static void extract_mono_frame( const pj_int16_t *in, pj_int16_t *out, unsigned ch, unsigned ch_cnt, unsigned samples_count) { unsigned i; in += ch; for (i=0; ibuf[dir].level += op; if (op == OP_PUT) { rport->buf[dir].ts.u64 += PJMEDIA_PIA_SPF(&rport->base.info); } if (rport->buf[dir].paused) { if (rport->buf[dir].level < -rport->max_burst) { /* Prevent the level from overflowing and resets back to zero */ rport->buf[dir].level = -rport->max_burst; } else if (rport->buf[dir].level > rport->max_burst) { /* Prevent the level from overflowing and resets back to zero */ rport->buf[dir].level = rport->max_burst; } else { /* Level has fallen below max level, we can resume * media flow. */ PJ_LOG(5,(rport->base.info.name.ptr, "Resuming media flow on %s direction (level=%d)", dir_name[dir], rport->buf[dir].level)); rport->buf[dir].level = 0; rport->buf[dir].paused = PJ_FALSE; //This will cause disruption in audio, and it seems to be //working fine without this anyway, so we disable it for now. //pjmedia_delay_buf_learn(rport->buf[dir].dbuf); } } else { if (rport->buf[dir].level >= rport->max_burst || rport->buf[dir].level <= -rport->max_burst) { /* Level has reached maximum level, the other side of * rport is not sending/retrieving frames. Pause the * rport on this direction. */ PJ_LOG(5,(rport->base.info.name.ptr, "Pausing media flow on %s direction (level=%d)", dir_name[dir], rport->buf[dir].level)); rport->buf[dir].paused = PJ_TRUE; } } } /* * "Write" a multichannel frame downstream. This would split * the multichannel frame into individual mono channel, and write * it to the appropriate port. */ static pj_status_t put_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct splitcomb *sc = (struct splitcomb*) this_port; unsigned ch; /* Handle null frame */ if (frame->type == PJMEDIA_FRAME_TYPE_NONE) { for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) { pjmedia_port *port = sc->port_desc[ch].port; if (!port) continue; if (!sc->port_desc[ch].reversed) { pjmedia_port_put_frame(port, frame); } else { struct reverse_port *rport = (struct reverse_port*)port; /* Update the number of NULL frames received. Once we have too * many of this, we'll stop calling op_update() to let the * media be suspended. */ if (++rport->buf[DIR_DOWNSTREAM].null_cnt > rport->max_null_frames) { /* Prevent the counter from overflowing and resetting * back to zero */ rport->buf[DIR_DOWNSTREAM].null_cnt = rport->max_null_frames + 1; continue; } /* Write zero port to delaybuf so that it doesn't underflow. * If we don't do this, get_frame() on this direction will * cause delaybuf to generate missing frame and the last * frame transmitted to delaybuf will be replayed multiple * times, which doesn't sound good. */ /* Update rport state. */ op_update(rport, DIR_DOWNSTREAM, OP_PUT); /* Discard frame if rport is paused on this direction */ if (rport->buf[DIR_DOWNSTREAM].paused) continue; /* Generate zero frame. */ pjmedia_zero_samples(sc->put_buf, PJMEDIA_PIA_SPF(&port->info)); /* Put frame to delay buffer */ pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf, sc->put_buf); } } return PJ_SUCCESS; } /* Not sure how we would handle partial frame, so better reject * it for now. */ PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info), PJ_EINVAL); /* * Write mono frame into each channels */ for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) { pjmedia_port *port = sc->port_desc[ch].port; if (!port) continue; /* Extract the mono frame to temporary buffer */ extract_mono_frame((const pj_int16_t*)frame->buf, sc->put_buf, ch, PJMEDIA_PIA_CCNT(&this_port->info), (unsigned)frame->size * 8 / PJMEDIA_PIA_BITS(&this_port->info) / PJMEDIA_PIA_CCNT(&this_port->info)); if (!sc->port_desc[ch].reversed) { /* Write to normal port */ pjmedia_frame mono_frame; mono_frame.buf = sc->put_buf; mono_frame.size = frame->size / PJMEDIA_PIA_CCNT(&this_port->info); mono_frame.type = frame->type; mono_frame.timestamp.u64 = frame->timestamp.u64; /* Write */ pjmedia_port_put_frame(port, &mono_frame); } else { /* Write to reversed phase port */ struct reverse_port *rport = (struct reverse_port*)port; /* Reset NULL frame counter */ rport->buf[DIR_DOWNSTREAM].null_cnt = 0; /* Update rport state. */ op_update(rport, DIR_DOWNSTREAM, OP_PUT); if (!rport->buf[DIR_DOWNSTREAM].paused) { pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf, sc->put_buf); } } } return PJ_SUCCESS; } /* * Get a multichannel frame upstream. * This will get mono channel frame from each port and put the * mono frame into the multichannel frame. */ static pj_status_t get_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct splitcomb *sc = (struct splitcomb*) this_port; unsigned ch; pj_bool_t has_frame = PJ_FALSE; /* Read frame from each port */ for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) { pjmedia_port *port = sc->port_desc[ch].port; pjmedia_frame mono_frame; pj_status_t status; if (!port) { pjmedia_zero_samples(sc->get_buf, PJMEDIA_PIA_SPF(&this_port->info) / PJMEDIA_PIA_CCNT(&this_port->info)); } else if (sc->port_desc[ch].reversed == PJ_FALSE) { /* Read from normal port */ mono_frame.buf = sc->get_buf; mono_frame.size = PJMEDIA_PIA_AVG_FSZ(&port->info); mono_frame.timestamp.u64 = frame->timestamp.u64; status = pjmedia_port_get_frame(port, &mono_frame); if (status != PJ_SUCCESS || mono_frame.type != PJMEDIA_FRAME_TYPE_AUDIO) { pjmedia_zero_samples(sc->get_buf, PJMEDIA_PIA_SPF(&port->info)); } frame->timestamp.u64 = mono_frame.timestamp.u64; } else { /* Read from temporary buffer for reverse port */ struct reverse_port *rport = (struct reverse_port*)port; /* Update rport state. */ op_update(rport, DIR_UPSTREAM, OP_GET); if (!rport->buf[DIR_UPSTREAM].paused) { pjmedia_delay_buf_get(rport->buf[DIR_UPSTREAM].dbuf, sc->get_buf); } else { pjmedia_zero_samples(sc->get_buf, PJMEDIA_PIA_SPF(&port->info)); } frame->timestamp.u64 = rport->buf[DIR_UPSTREAM].ts.u64; } /* Combine the mono frame into multichannel frame */ store_mono_frame(sc->get_buf, (pj_int16_t*)frame->buf, ch, PJMEDIA_PIA_CCNT(&this_port->info), PJMEDIA_PIA_SPF(&this_port->info) / PJMEDIA_PIA_CCNT(&this_port->info)); has_frame = PJ_TRUE; } /* Return NO_FRAME is we don't get any frames from downstream ports */ if (has_frame) { frame->type = PJMEDIA_FRAME_TYPE_AUDIO; frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info); } else frame->type = PJMEDIA_FRAME_TYPE_NONE; return PJ_SUCCESS; } static pj_status_t on_destroy(pjmedia_port *this_port) { /* Nothing to do for the splitcomb * Reverse ports must be destroyed separately. */ PJ_UNUSED_ARG(this_port); return PJ_SUCCESS; } /* * Put a frame in the reverse port (upstream direction). This frame * will be picked up by get_frame() above. */ static pj_status_t rport_put_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct reverse_port *rport = (struct reverse_port*) this_port; pj_assert(frame->size <= PJMEDIA_PIA_AVG_FSZ(&rport->base.info)); /* Handle NULL frame */ if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) { /* Update the number of NULL frames received. Once we have too * many of this, we'll stop calling op_update() to let the * media be suspended. */ if (++rport->buf[DIR_UPSTREAM].null_cnt > rport->max_null_frames) { /* Prevent the counter from overflowing and resetting back * to zero */ rport->buf[DIR_UPSTREAM].null_cnt = rport->max_null_frames + 1; return PJ_SUCCESS; } /* Write zero port to delaybuf so that it doesn't underflow. * If we don't do this, get_frame() on this direction will * cause delaybuf to generate missing frame and the last * frame transmitted to delaybuf will be replayed multiple * times, which doesn't sound good. */ /* Update rport state. */ op_update(rport, DIR_UPSTREAM, OP_PUT); /* Discard frame if rport is paused on this direction */ if (rport->buf[DIR_UPSTREAM].paused) return PJ_SUCCESS; /* Generate zero frame. */ pjmedia_zero_samples(rport->tmp_up_buf, PJMEDIA_PIA_SPF(&this_port->info)); /* Put frame to delay buffer */ return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf, rport->tmp_up_buf); } /* Not sure how to handle partial frame, so better reject for now */ PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info), PJ_EINVAL); /* Reset NULL frame counter */ rport->buf[DIR_UPSTREAM].null_cnt = 0; /* Update rport state. */ op_update(rport, DIR_UPSTREAM, OP_PUT); /* Discard frame if rport is paused on this direction */ if (rport->buf[DIR_UPSTREAM].paused) return PJ_SUCCESS; /* Unfortunately must copy to temporary buffer since delay buf * modifies the frame content. */ pjmedia_copy_samples(rport->tmp_up_buf, (const pj_int16_t*)frame->buf, PJMEDIA_PIA_SPF(&this_port->info)); /* Put frame to delay buffer */ return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf, rport->tmp_up_buf); } /* Get a mono frame from a reversed phase channel (downstream direction). * The frame is put by put_frame() call to the splitcomb. */ static pj_status_t rport_get_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct reverse_port *rport = (struct reverse_port*) this_port; /* Update state */ op_update(rport, DIR_DOWNSTREAM, OP_GET); /* Return no frame if media flow on this direction is being * paused. */ if (rport->buf[DIR_DOWNSTREAM].paused) { frame->type = PJMEDIA_FRAME_TYPE_NONE; return PJ_SUCCESS; } /* Get frame from delay buffer */ frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info); frame->type = PJMEDIA_FRAME_TYPE_AUDIO; frame->timestamp.u64 = rport->buf[DIR_DOWNSTREAM].ts.u64; return pjmedia_delay_buf_get(rport->buf[DIR_DOWNSTREAM].dbuf, (short*)frame->buf); } static pj_status_t rport_on_destroy(pjmedia_port *this_port) { struct reverse_port *rport = (struct reverse_port*) this_port; pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf); pjmedia_delay_buf_destroy(rport->buf[DIR_UPSTREAM].dbuf); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/stereo_port.c ================================================ /* $Id: stereo_port.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #define SIGNATURE PJMEDIA_SIG_PORT_STEREO struct stereo_port { pjmedia_port base; pjmedia_port *dn_port; unsigned options; pj_int16_t *put_buf; pj_int16_t *get_buf; }; static pj_status_t stereo_put_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t stereo_get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t stereo_destroy(pjmedia_port *this_port); PJ_DEF(pj_status_t) pjmedia_stereo_port_create( pj_pool_t *pool, pjmedia_port *dn_port, unsigned channel_count, unsigned options, pjmedia_port **p_port ) { const pj_str_t name = pj_str("stereo"); struct stereo_port *sport; unsigned samples_per_frame; /* Validate arguments. */ PJ_ASSERT_RETURN(pool && dn_port && channel_count && p_port, PJ_EINVAL); /* Only supports 16bit samples per frame */ PJ_ASSERT_RETURN(PJMEDIA_PIA_BITS(&dn_port->info) == 16, PJMEDIA_ENCBITS); /* Validate channel counts */ PJ_ASSERT_RETURN(((PJMEDIA_PIA_CCNT(&dn_port->info)>1 && channel_count==1) || (PJMEDIA_PIA_CCNT(&dn_port->info)==1 && channel_count>1)), PJ_EINVAL); /* Create and initialize port. */ sport = PJ_POOL_ZALLOC_T(pool, struct stereo_port); PJ_ASSERT_RETURN(sport != NULL, PJ_ENOMEM); samples_per_frame = PJMEDIA_PIA_SPF(&dn_port->info) * channel_count / PJMEDIA_PIA_CCNT(&dn_port->info); pjmedia_port_info_init(&sport->base.info, &name, SIGNATURE, PJMEDIA_PIA_SRATE(&dn_port->info), channel_count, PJMEDIA_PIA_BITS(&dn_port->info), samples_per_frame); sport->dn_port = dn_port; sport->options = options; /* We always need buffer for put_frame */ sport->put_buf = (pj_int16_t*) pj_pool_alloc(pool, PJMEDIA_PIA_AVG_FSZ(&dn_port->info)); /* See if we need buffer for get_frame */ if (PJMEDIA_PIA_CCNT(&dn_port->info) > channel_count) { sport->get_buf = (pj_int16_t*) pj_pool_alloc(pool, PJMEDIA_PIA_AVG_FSZ(&dn_port->info)); } /* Media port interface */ sport->base.get_frame = &stereo_get_frame; sport->base.put_frame = &stereo_put_frame; sport->base.on_destroy = &stereo_destroy; /* Done */ *p_port = &sport->base; return PJ_SUCCESS; } static pj_status_t stereo_put_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct stereo_port *sport = (struct stereo_port*) this_port; const pjmedia_audio_format_detail *s_afd, *dn_afd; pjmedia_frame tmp_frame; /* Return if we don't have downstream port. */ if (sport->dn_port == NULL) { return PJ_SUCCESS; } s_afd = pjmedia_format_get_audio_format_detail(&this_port->info.fmt, 1); dn_afd = pjmedia_format_get_audio_format_detail(&sport->dn_port->info.fmt, 1); if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) { tmp_frame.buf = sport->put_buf; if (dn_afd->channel_count == 1) { pjmedia_convert_channel_nto1((pj_int16_t*)tmp_frame.buf, (const pj_int16_t*)frame->buf, s_afd->channel_count, PJMEDIA_AFD_SPF(s_afd), (sport->options & PJMEDIA_STEREO_MIX), 0); } else { pjmedia_convert_channel_1ton((pj_int16_t*)tmp_frame.buf, (const pj_int16_t*)frame->buf, dn_afd->channel_count, PJMEDIA_AFD_SPF(s_afd), sport->options); } tmp_frame.size = PJMEDIA_AFD_AVG_FSZ(dn_afd); } else { tmp_frame.buf = frame->buf; tmp_frame.size = frame->size; } tmp_frame.type = frame->type; tmp_frame.timestamp.u64 = frame->timestamp.u64; return pjmedia_port_put_frame( sport->dn_port, &tmp_frame ); } static pj_status_t stereo_get_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct stereo_port *sport = (struct stereo_port*) this_port; const pjmedia_audio_format_detail *s_afd, *dn_afd; pjmedia_frame tmp_frame; pj_status_t status; /* Return silence if we don't have downstream port */ if (sport->dn_port == NULL) { pj_bzero(frame->buf, frame->size); return PJ_SUCCESS; } s_afd = pjmedia_format_get_audio_format_detail(&this_port->info.fmt, 1); dn_afd = pjmedia_format_get_audio_format_detail(&sport->dn_port->info.fmt, 1); tmp_frame.buf = sport->get_buf? sport->get_buf : frame->buf; tmp_frame.size = PJMEDIA_PIA_AVG_FSZ(&sport->dn_port->info); tmp_frame.timestamp.u64 = frame->timestamp.u64; tmp_frame.type = PJMEDIA_FRAME_TYPE_AUDIO; status = pjmedia_port_get_frame( sport->dn_port, &tmp_frame); if (status != PJ_SUCCESS) return status; if (tmp_frame.type != PJMEDIA_FRAME_TYPE_AUDIO) { frame->type = tmp_frame.type; frame->timestamp = tmp_frame.timestamp; frame->size = tmp_frame.size; if (tmp_frame.size && tmp_frame.buf == sport->get_buf) pj_memcpy(frame->buf, tmp_frame.buf, tmp_frame.size); return PJ_SUCCESS; } if (s_afd->channel_count == 1) { pjmedia_convert_channel_nto1((pj_int16_t*)frame->buf, (const pj_int16_t*)tmp_frame.buf, dn_afd->channel_count, PJMEDIA_AFD_SPF(s_afd), (sport->options & PJMEDIA_STEREO_MIX), 0); } else { pjmedia_convert_channel_1ton((pj_int16_t*)frame->buf, (const pj_int16_t*)tmp_frame.buf, s_afd->channel_count, PJMEDIA_AFD_SPF(dn_afd), sport->options); } frame->size = PJMEDIA_AFD_AVG_FSZ(s_afd); frame->type = PJMEDIA_FRAME_TYPE_AUDIO; return PJ_SUCCESS; } static pj_status_t stereo_destroy(pjmedia_port *this_port) { struct stereo_port *sport = (struct stereo_port*) this_port; if ((sport->options & PJMEDIA_STEREO_DONT_DESTROY_DN)==0) { pjmedia_port_destroy(sport->dn_port); sport->dn_port = NULL; } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/stream.c ================================================ /* $Id: stream.c 4336 2013-01-29 08:15:02Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* memcpy() */ #define THIS_FILE "stream.c" #define ERRLEVEL 1 #define LOGERR_(expr) stream_perror expr #define TRC_(expr) PJ_LOG(5,expr) #define BYTES_PER_SAMPLE 2 /* Limit the number of synthetic audio samples that are generated by PLC. * Normally PLC should have it's own means to limit the number of * synthetic frames, so we need to set this to a reasonably large value * just as precaution */ #define MAX_PLC_MSEC PJMEDIA_MAX_PLC_DURATION_MSEC /* Tracing jitter buffer operations in a stream session to a CSV file. * The trace will contain JB operation timestamp, frame info, RTP info, and * the JB state right after the operation. */ #define TRACE_JB 0 /* Enable/disable trace. */ #define TRACE_JB_PATH_PREFIX "" /* Optional path/prefix for the CSV filename. */ #if TRACE_JB # include # define TRACE_JB_INVALID_FD ((pj_oshandle_t)-1) # define TRACE_JB_OPENED(s) (s->trace_jb_fd != TRACE_JB_INVALID_FD) #endif #ifndef PJMEDIA_STREAM_SIZE # define PJMEDIA_STREAM_SIZE 1000 #endif #ifndef PJMEDIA_STREAM_INC # define PJMEDIA_STREAM_INC 1000 #endif /* Number of DTMF E bit transmissions */ #define DTMF_EBIT_RETRANSMIT_CNT 3 /** * Media channel. */ struct pjmedia_channel { pjmedia_stream *stream; /**< Parent stream. */ pjmedia_dir dir; /**< Channel direction. */ unsigned pt; /**< Payload type. */ pj_bool_t paused; /**< Paused?. */ unsigned out_pkt_size; /**< Size of output buffer. */ void *out_pkt; /**< Output buffer. */ unsigned out_pkt_len; /**< Length of data in buffer. */ pjmedia_rtp_session rtp; /**< RTP session. */ }; struct dtmf { int event; pj_uint32_t duration; int ebit_cnt; /**< # of E bit transmissions */ }; /** * This structure describes media stream. * A media stream is bidirectional media transmission between two endpoints. * It consists of two channels, i.e. encoding and decoding channels. * A media stream corresponds to a single "m=" line in a SDP session * description. */ struct pjmedia_stream { pjmedia_endpt *endpt; /**< Media endpoint. */ pjmedia_codec_mgr *codec_mgr; /**< Codec manager instance. */ pjmedia_stream_info si; /**< Creation parameter. */ pjmedia_port port; /**< Port interface. */ pjmedia_channel *enc; /**< Encoding channel. */ pjmedia_channel *dec; /**< Decoding channel. */ pj_pool_t *own_pool; /**< Only created if not given */ pjmedia_dir dir; /**< Stream direction. */ void *user_data; /**< User data. */ pj_str_t cname; /**< SDES CNAME */ pjmedia_transport *transport; /**< Stream transport. */ pjmedia_codec *codec; /**< Codec instance being used. */ pjmedia_codec_param codec_param; /**< Codec param. */ pj_int16_t *enc_buf; /**< Encoding buffer, when enc's ptime is different than dec. Otherwise it's NULL. */ unsigned enc_samples_per_pkt; unsigned enc_buf_size; /**< Encoding buffer size, in samples. */ unsigned enc_buf_pos; /**< First position in buf. */ unsigned enc_buf_count; /**< Number of samples in the encoding buffer. */ unsigned plc_cnt; /**< # of consecutive PLC frames*/ unsigned max_plc_cnt; /**< Max # of PLC frames */ unsigned vad_enabled; /**< VAD enabled in param. */ unsigned frame_size; /**< Size of encoded base frame.*/ pj_bool_t is_streaming; /**< Currently streaming?. This is used to put RTP marker bit. */ pj_uint32_t ts_vad_disabled;/**< TS when VAD was disabled. */ pj_uint32_t tx_duration; /**< TX duration in timestamp. */ pj_mutex_t *jb_mutex; pjmedia_jbuf *jb; /**< Jitter buffer. */ char jb_last_frm; /**< Last frame type from jb */ unsigned jb_last_frm_cnt;/**< Last JB frame type counter*/ pjmedia_rtcp_session rtcp; /**< RTCP for incoming RTP. */ pj_uint32_t rtcp_last_tx; /**< RTCP tx time in timestamp */ pj_uint32_t rtcp_interval; /**< Interval, in timestamp. */ pj_bool_t initial_rr; /**< Initial RTCP RR sent */ pj_bool_t rtcp_sdes_bye_disabled;/**< Send RTCP SDES/BYE?*/ void *out_rtcp_pkt; /**< Outgoing RTCP packet. */ unsigned out_rtcp_pkt_size; /**< Outgoing RTCP packet size. */ /* RFC 2833 DTMF transmission queue: */ int tx_event_pt; /**< Outgoing pt for dtmf. */ int tx_dtmf_count; /**< # of digits in tx dtmf buf.*/ struct dtmf tx_dtmf_buf[32];/**< Outgoing dtmf queue. */ /* Incoming DTMF: */ int rx_event_pt; /**< Incoming pt for dtmf. */ int last_dtmf; /**< Current digit, or -1. */ pj_uint32_t last_dtmf_dur; /**< Start ts for cur digit. */ unsigned rx_dtmf_count; /**< # of digits in dtmf rx buf.*/ char rx_dtmf_buf[32];/**< Incoming DTMF buffer. */ /* DTMF callback */ void (*dtmf_cb)(pjmedia_stream*, void*, int); void *dtmf_cb_user_data; #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0) /* Enable support to handle codecs with inconsistent clock rate * between clock rate in SDP/RTP & the clock rate that is actually used. * This happens for example with G.722 and MPEG audio codecs. */ pj_bool_t has_g722_mpeg_bug; /**< Flag to specify whether normalization process is needed */ unsigned rtp_tx_ts_len_per_pkt; /**< Normalized ts length per packet transmitted according to 'erroneous' definition */ unsigned rtp_rx_ts_len_per_frame; /**< Normalized ts length per frame received according to 'erroneous' definition */ unsigned rtp_rx_last_cnt;/**< Nb of frames in last pkt */ unsigned rtp_rx_check_cnt; /**< Counter of remote timestamp checking */ #endif #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) pj_uint32_t rtcp_xr_last_tx; /**< RTCP XR tx time in timestamp. */ pj_uint32_t rtcp_xr_interval; /**< Interval, in timestamp. */ pj_sockaddr rtcp_xr_dest; /**< Additional remote RTCP XR dest. If sin_family is zero, it will be ignored*/ unsigned rtcp_xr_dest_len; /**< Length of RTCP XR dest address */ #endif #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 pj_bool_t use_ka; /**< Stream keep-alive with non- codec-VAD mechanism is enabled? */ pj_timestamp last_frm_ts_sent; /**< Timestamp of last sending packet */ #endif #if TRACE_JB pj_oshandle_t trace_jb_fd; /**< Jitter tracing file handle.*/ char *trace_jb_buf; /**< Jitter tracing buffer. */ #endif pj_uint32_t rtp_rx_last_ts; /**< Last received RTP timestamp*/ pj_status_t rtp_rx_last_err; /**< Last RTP recv() error */ }; /* RFC 2833 digit */ static const char digitmap[17] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '#', 'A', 'B', 'C', 'D', 'R'}; /* Zero audio frame samples */ static pj_int16_t zero_frame[2 * 30 * 16000 / 1000]; /* * Print error. */ static void stream_perror(const char *sender, const char *title, pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(sender, "%s: %s [err:%d]", title, errmsg, status)); } static pj_status_t send_rtcp(pjmedia_stream *stream, pj_bool_t with_sdes, pj_bool_t with_bye, pj_bool_t with_xr); #if TRACE_JB PJ_INLINE(int) trace_jb_print_timestamp(char **buf, pj_ssize_t len) { pj_time_val now; pj_parsed_time ptime; char *p = *buf; if (len < 14) return -1; pj_gettimeofday(&now); pj_time_decode(&now, &ptime); p += pj_utoa_pad(ptime.hour, p, 2, '0'); *p++ = ':'; p += pj_utoa_pad(ptime.min, p, 2, '0'); *p++ = ':'; p += pj_utoa_pad(ptime.sec, p, 2, '0'); *p++ = '.'; p += pj_utoa_pad(ptime.msec, p, 3, '0'); *p++ = ','; *buf = p; return 0; } PJ_INLINE(int) trace_jb_print_state(pjmedia_stream *stream, char **buf, pj_ssize_t len) { char *p = *buf; char *endp = *buf + len; pjmedia_jb_state state; pjmedia_jbuf_get_state(stream->jb, &state); len = pj_ansi_snprintf(p, endp-p, "%d, %d, %d", state.size, state.burst, state.prefetch); if ((len < 0) || (len >= endp-p)) return -1; p += len; *buf = p; return 0; } static void trace_jb_get(pjmedia_stream *stream, pjmedia_jb_frame_type ft, pj_size_t fsize) { char *p = stream->trace_jb_buf; char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE; pj_ssize_t len = 0; const char* ft_st; if (!TRACE_JB_OPENED(stream)) return; /* Print timestamp. */ if (trace_jb_print_timestamp(&p, endp-p)) goto on_insuff_buffer; /* Print frame type and size */ switch(ft) { case PJMEDIA_JB_MISSING_FRAME: ft_st = "missing"; break; case PJMEDIA_JB_NORMAL_FRAME: ft_st = "normal"; break; case PJMEDIA_JB_ZERO_PREFETCH_FRAME: ft_st = "prefetch"; break; case PJMEDIA_JB_ZERO_EMPTY_FRAME: ft_st = "empty"; break; default: ft_st = "unknown"; break; } /* Print operation, size, frame count, frame type */ len = pj_ansi_snprintf(p, endp-p, "GET,%d,1,%s,,,,", fsize, ft_st); if ((len < 0) || (len >= endp-p)) goto on_insuff_buffer; p += len; /* Print JB state */ if (trace_jb_print_state(stream, &p, endp-p)) goto on_insuff_buffer; /* Print end of line */ if (endp-p < 2) goto on_insuff_buffer; *p++ = '\n'; /* Write and flush */ len = p - stream->trace_jb_buf; pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); pj_file_flush(stream->trace_jb_fd); return; on_insuff_buffer: pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); } static void trace_jb_put(pjmedia_stream *stream, const pjmedia_rtp_hdr *hdr, unsigned payloadlen, unsigned frame_cnt) { char *p = stream->trace_jb_buf; char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE; pj_ssize_t len = 0; if (!TRACE_JB_OPENED(stream)) return; /* Print timestamp. */ if (trace_jb_print_timestamp(&p, endp-p)) goto on_insuff_buffer; /* Print operation, size, frame count, RTP info */ len = pj_ansi_snprintf(p, endp-p, "PUT,%d,%d,,%d,%d,%d,", payloadlen, frame_cnt, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), hdr->m); if ((len < 0) || (len >= endp-p)) goto on_insuff_buffer; p += len; /* Print JB state */ if (trace_jb_print_state(stream, &p, endp-p)) goto on_insuff_buffer; /* Print end of line */ if (endp-p < 2) goto on_insuff_buffer; *p++ = '\n'; /* Write and flush */ len = p - stream->trace_jb_buf; pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); pj_file_flush(stream->trace_jb_fd); return; on_insuff_buffer: pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); } #endif /* TRACE_JB */ #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0 /* * Send keep-alive packet using non-codec frame. */ static void send_keep_alive_packet(pjmedia_stream *stream) { #if PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_EMPTY_RTP /* Keep-alive packet is empty RTP */ pj_status_t status; void *pkt; int pkt_len; TRC_((stream->port.info.name.ptr, "Sending keep-alive (RTCP and empty RTP)")); /* Send RTP */ status = pjmedia_rtp_encode_rtp( &stream->enc->rtp, stream->enc->pt, 0, 1, 0, (const void**)&pkt, &pkt_len); pj_assert(status == PJ_SUCCESS); pj_memcpy(stream->enc->out_pkt, pkt, pkt_len); pjmedia_transport_send_rtp(stream->transport, stream->enc->out_pkt, pkt_len); /* Send RTCP */ send_rtcp(stream, PJ_TRUE, PJ_FALSE, PJ_FALSE); #elif PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER /* Keep-alive packet is defined in PJMEDIA_STREAM_KA_USER_PKT */ int pkt_len; const pj_str_t str_ka = PJMEDIA_STREAM_KA_USER_PKT; TRC_((stream->port.info.name.ptr, "Sending keep-alive (custom RTP/RTCP packets)")); /* Send to RTP port */ pj_memcpy(stream->enc->out_pkt, str_ka.ptr, str_ka.slen); pkt_len = str_ka.slen; pjmedia_transport_send_rtp(stream->transport, stream->enc->out_pkt, pkt_len); /* Send to RTCP port */ pjmedia_transport_send_rtcp(stream->transport, stream->enc->out_pkt, pkt_len); #else PJ_UNUSED_ARG(stream); #endif } #endif /* defined(PJMEDIA_STREAM_ENABLE_KA) */ /* * play_callback() * * This callback is called by sound device's player thread when it * needs to feed the player with some frames. */ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) { pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata; pjmedia_channel *channel = stream->dec; unsigned samples_count, samples_per_frame, samples_required; pj_int16_t *p_out_samp; pj_status_t status; /* Return no frame is channel is paused */ if (channel->paused) { frame->type = PJMEDIA_FRAME_TYPE_NONE; return PJ_SUCCESS; } /* Repeat get frame from the jitter buffer and decode the frame * until we have enough frames according to codec's ptime. */ /* Lock jitter buffer mutex first */ pj_mutex_lock( stream->jb_mutex ); samples_required = PJMEDIA_PIA_SPF(&stream->port.info); samples_per_frame = stream->codec_param.info.frm_ptime * stream->codec_param.info.clock_rate * stream->codec_param.info.channel_cnt / 1000; p_out_samp = (pj_int16_t*) frame->buf; for (samples_count=0; samples_count < samples_required; samples_count += samples_per_frame) { char frame_type; pj_size_t frame_size; pj_uint32_t bit_info; /* Get frame from jitter buffer. */ pjmedia_jbuf_get_frame2(stream->jb, channel->out_pkt, &frame_size, &frame_type, &bit_info); #if TRACE_JB trace_jb_get(stream, frame_type, frame_size); #endif if (frame_type == PJMEDIA_JB_MISSING_FRAME) { /* Activate PLC */ if (stream->codec->op->recover && stream->codec_param.setting.plc && stream->plc_cnt < stream->max_plc_cnt) { pjmedia_frame frame_out; frame_out.buf = p_out_samp + samples_count; frame_out.size = frame->size - samples_count*2; status = pjmedia_codec_recover(stream->codec, (unsigned)frame_out.size, &frame_out); ++stream->plc_cnt; } else { status = -1; } if (status != PJ_SUCCESS) { /* Either PLC failed or PLC not supported/enabled */ pjmedia_zero_samples(p_out_samp + samples_count, samples_required - samples_count); } if (frame_type != stream->jb_last_frm) { /* Report changing frame type event */ PJ_LOG(5,(stream->port.info.name.ptr, "Frame lost%s!", (status == PJ_SUCCESS? ", recovered":""))); stream->jb_last_frm = frame_type; stream->jb_last_frm_cnt = 1; } else { stream->jb_last_frm_cnt++; } } else if (frame_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) { const char *with_plc = ""; /* Jitter buffer is empty. If this is the first "empty" state, * activate PLC to smoothen the fade-out, otherwise zero * the frame. */ //Using this "if" will only invoke PLC for the first packet //lost and not the subsequent ones. //if (frame_type != stream->jb_last_frm) { if (1) { /* Activate PLC to smoothen the missing frame */ if (stream->codec->op->recover && stream->codec_param.setting.plc && stream->plc_cnt < stream->max_plc_cnt) { pjmedia_frame frame_out; do { frame_out.buf = p_out_samp + samples_count; frame_out.size = frame->size - samples_count*2; status = pjmedia_codec_recover(stream->codec, (unsigned)frame_out.size, &frame_out); if (status != PJ_SUCCESS) break; samples_count += samples_per_frame; ++stream->plc_cnt; } while (samples_count < samples_required && stream->plc_cnt < stream->max_plc_cnt); with_plc = ", plc invoked"; } } if (samples_count < samples_required) { pjmedia_zero_samples(p_out_samp + samples_count, samples_required - samples_count); samples_count = samples_required; } if (stream->jb_last_frm != frame_type) { pjmedia_jb_state jb_state; /* Report changing frame type event */ pjmedia_jbuf_get_state(stream->jb, &jb_state); PJ_LOG(5,(stream->port.info.name.ptr, "Jitter buffer empty (prefetch=%d)%s", jb_state.prefetch, with_plc)); stream->jb_last_frm = frame_type; stream->jb_last_frm_cnt = 1; } else { stream->jb_last_frm_cnt++; } break; } else if (frame_type != PJMEDIA_JB_NORMAL_FRAME) { const char *with_plc = ""; /* It can only be PJMEDIA_JB_ZERO_PREFETCH frame */ pj_assert(frame_type == PJMEDIA_JB_ZERO_PREFETCH_FRAME); /* Always activate PLC when it's available.. */ if (stream->codec->op->recover && stream->codec_param.setting.plc && stream->plc_cnt < stream->max_plc_cnt) { pjmedia_frame frame_out; do { frame_out.buf = p_out_samp + samples_count; frame_out.size = frame->size - samples_count*2; status = pjmedia_codec_recover(stream->codec, (unsigned)frame_out.size, &frame_out); if (status != PJ_SUCCESS) break; samples_count += samples_per_frame; ++stream->plc_cnt; } while (samples_count < samples_required && stream->plc_cnt < stream->max_plc_cnt); with_plc = ", plc invoked"; } if (samples_count < samples_required) { pjmedia_zero_samples(p_out_samp + samples_count, samples_required - samples_count); samples_count = samples_required; } if (stream->jb_last_frm != frame_type) { pjmedia_jb_state jb_state; /* Report changing frame type event */ pjmedia_jbuf_get_state(stream->jb, &jb_state); PJ_LOG(5,(stream->port.info.name.ptr, "Jitter buffer is bufferring (prefetch=%d)%s", jb_state.prefetch, with_plc)); stream->jb_last_frm = frame_type; stream->jb_last_frm_cnt = 1; } else { stream->jb_last_frm_cnt++; } break; } else { /* Got "NORMAL" frame from jitter buffer */ pjmedia_frame frame_in, frame_out; stream->plc_cnt = 0; /* Decode */ frame_in.buf = channel->out_pkt; frame_in.size = frame_size; frame_in.bit_info = bit_info; frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO; /* ignored */ frame_out.buf = p_out_samp + samples_count; frame_out.size = frame->size - samples_count*BYTES_PER_SAMPLE; status = pjmedia_codec_decode( stream->codec, &frame_in, (unsigned)frame_out.size, &frame_out); if (status != 0) { LOGERR_((port->info.name.ptr, "codec decode() error", status)); pjmedia_zero_samples(p_out_samp + samples_count, samples_per_frame); } if (stream->jb_last_frm != frame_type) { /* Report changing frame type event */ PJ_LOG(5,(stream->port.info.name.ptr, "Jitter buffer starts returning normal frames " "(after %d empty/lost)", stream->jb_last_frm_cnt, stream->jb_last_frm)); stream->jb_last_frm = frame_type; stream->jb_last_frm_cnt = 1; } else { stream->jb_last_frm_cnt++; } } } /* Unlock jitter buffer mutex. */ pj_mutex_unlock( stream->jb_mutex ); /* Return PJMEDIA_FRAME_TYPE_NONE if we have no frames at all * (it can happen when jitter buffer returns PJMEDIA_JB_ZERO_EMPTY_FRAME). */ if (samples_count == 0) { frame->type = PJMEDIA_FRAME_TYPE_NONE; frame->size = 0; } else { frame->type = PJMEDIA_FRAME_TYPE_AUDIO; frame->size = samples_count * BYTES_PER_SAMPLE; frame->timestamp.u64 = 0; } return PJ_SUCCESS; } /* The other version of get_frame callback used when stream port format * is non linear PCM. */ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) { pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata; pjmedia_channel *channel = stream->dec; pjmedia_frame_ext *f = (pjmedia_frame_ext*)frame; unsigned samples_per_frame, samples_required; pj_status_t status; /* Return no frame if channel is paused */ if (channel->paused) { frame->type = PJMEDIA_FRAME_TYPE_NONE; return PJ_SUCCESS; } /* Repeat get frame from the jitter buffer and decode the frame * until we have enough frames according to codec's ptime. */ samples_required = PJMEDIA_PIA_SPF(&stream->port.info); samples_per_frame = stream->codec_param.info.frm_ptime * stream->codec_param.info.clock_rate * stream->codec_param.info.channel_cnt / 1000; pj_bzero(f, sizeof(pjmedia_frame_ext)); f->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; while (f->samples_cnt < samples_required) { char frame_type; pj_size_t frame_size; pj_uint32_t bit_info; /* Lock jitter buffer mutex first */ pj_mutex_lock( stream->jb_mutex ); /* Get frame from jitter buffer. */ pjmedia_jbuf_get_frame2(stream->jb, channel->out_pkt, &frame_size, &frame_type, &bit_info); #if TRACE_JB trace_jb_get(stream, frame_type, frame_size); #endif /* Unlock jitter buffer mutex. */ pj_mutex_unlock( stream->jb_mutex ); if (frame_type == PJMEDIA_JB_NORMAL_FRAME) { /* Got "NORMAL" frame from jitter buffer */ pjmedia_frame frame_in; /* Decode */ frame_in.buf = channel->out_pkt; frame_in.size = frame_size; frame_in.bit_info = bit_info; frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO; status = pjmedia_codec_decode( stream->codec, &frame_in, 0, frame); if (status != PJ_SUCCESS) { LOGERR_((port->info.name.ptr, "codec decode() error", status)); pjmedia_frame_ext_append_subframe(f, NULL, 0, (pj_uint16_t)samples_per_frame); } if (stream->jb_last_frm != frame_type) { /* Report changing frame type event */ PJ_LOG(5,(stream->port.info.name.ptr, "Jitter buffer starts returning normal frames " "(after %d empty/lost)", stream->jb_last_frm_cnt, stream->jb_last_frm)); stream->jb_last_frm = frame_type; stream->jb_last_frm_cnt = 1; } else { stream->jb_last_frm_cnt++; } } else { /* Try to generate frame by invoking PLC (when any) */ status = PJ_SUCCESS; if (stream->codec->op->recover) { status = pjmedia_codec_recover(stream->codec, 0, frame); } /* No PLC or PLC failed */ if (!stream->codec->op->recover || status != PJ_SUCCESS) { pjmedia_frame_ext_append_subframe(f, NULL, 0, (pj_uint16_t)samples_per_frame); } if (frame_type == PJMEDIA_JB_MISSING_FRAME) { if (frame_type != stream->jb_last_frm) { /* Report changing frame type event */ PJ_LOG(5,(stream->port.info.name.ptr, "Frame lost!")); stream->jb_last_frm = frame_type; stream->jb_last_frm_cnt = 1; } else { stream->jb_last_frm_cnt++; } } else if (frame_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) { if (frame_type != stream->jb_last_frm) { pjmedia_jb_state jb_state; /* Report changing frame type event */ pjmedia_jbuf_get_state(stream->jb, &jb_state); PJ_LOG(5,(stream->port.info.name.ptr, "Jitter buffer empty (prefetch=%d)", jb_state.prefetch)); stream->jb_last_frm = frame_type; stream->jb_last_frm_cnt = 1; } else { stream->jb_last_frm_cnt++; } } else { /* It can only be PJMEDIA_JB_ZERO_PREFETCH frame */ pj_assert(frame_type == PJMEDIA_JB_ZERO_PREFETCH_FRAME); if (stream->jb_last_frm != frame_type) { pjmedia_jb_state jb_state; /* Report changing frame type event */ pjmedia_jbuf_get_state(stream->jb, &jb_state); PJ_LOG(5,(stream->port.info.name.ptr, "Jitter buffer is bufferring (prefetch=%d)", jb_state.prefetch)); stream->jb_last_frm = frame_type; stream->jb_last_frm_cnt = 1; } else { stream->jb_last_frm_cnt++; } } } } return PJ_SUCCESS; } /* * Transmit DTMF */ static void create_dtmf_payload(pjmedia_stream *stream, struct pjmedia_frame *frame_out, int forced_last, int *first, int *last) { pjmedia_rtp_dtmf_event *event; struct dtmf *digit = &stream->tx_dtmf_buf[0]; pj_assert(sizeof(pjmedia_rtp_dtmf_event) == 4); *first = *last = 0; event = (pjmedia_rtp_dtmf_event*) frame_out->buf; if (digit->duration == 0) { PJ_LOG(5,(stream->port.info.name.ptr, "Sending DTMF digit id %c", digitmap[digit->event])); *first = 1; } digit->duration += PJMEDIA_PIA_SPF(&stream->port.info); if (digit->duration >= PJMEDIA_DTMF_DURATION) digit->duration = PJMEDIA_DTMF_DURATION; event->event = (pj_uint8_t)digit->event; event->e_vol = 10; event->duration = pj_htons((pj_uint16_t)digit->duration); if (forced_last) { digit->duration = PJMEDIA_DTMF_DURATION; } if (digit->duration >= PJMEDIA_DTMF_DURATION) { event->e_vol |= 0x80; if (++digit->ebit_cnt >= DTMF_EBIT_RETRANSMIT_CNT) { *last = 1; /* Prepare next digit. */ pj_mutex_lock(stream->jb_mutex); pj_array_erase(stream->tx_dtmf_buf, sizeof(stream->tx_dtmf_buf[0]), stream->tx_dtmf_count, 0); --stream->tx_dtmf_count; pj_mutex_unlock(stream->jb_mutex); } } frame_out->size = 4; } static pj_status_t send_rtcp(pjmedia_stream *stream, pj_bool_t with_sdes, pj_bool_t with_bye, pj_bool_t with_xr) { void *sr_rr_pkt; pj_uint8_t *pkt; int len, max_len; pj_status_t status; /* Build RTCP RR/SR packet */ pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len); #if !defined(PJMEDIA_HAS_RTCP_XR) || (PJMEDIA_HAS_RTCP_XR == 0) with_xr = PJ_FALSE; #endif if (with_sdes || with_bye || with_xr) { pkt = (pj_uint8_t*) stream->out_rtcp_pkt; pj_memcpy(pkt, sr_rr_pkt, len); max_len = stream->out_rtcp_pkt_size; } else { pkt = (pj_uint8_t*)sr_rr_pkt; max_len = len; } /* Build RTCP SDES packet */ if (with_sdes) { pjmedia_rtcp_sdes sdes; pj_size_t sdes_len; pj_bzero(&sdes, sizeof(sdes)); sdes.cname = stream->cname; sdes_len = max_len - len; status = pjmedia_rtcp_build_rtcp_sdes(&stream->rtcp, pkt+len, &sdes_len, &sdes); if (status != PJ_SUCCESS) { PJ_PERROR(4,(stream->port.info.name.ptr, status, "Error generating RTCP SDES")); } else { len += (int)sdes_len; } } /* Build RTCP XR packet */ #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) if (with_xr) { int i; pjmedia_jb_state jb_state; void *xr_pkt; int xr_len; /* Update RTCP XR with current JB states */ pjmedia_jbuf_get_state(stream->jb, &jb_state); i = jb_state.avg_delay; status = pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, PJMEDIA_RTCP_XR_INFO_JB_NOM, i); pj_assert(status == PJ_SUCCESS); i = jb_state.max_delay; status = pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, PJMEDIA_RTCP_XR_INFO_JB_MAX, i); pj_assert(status == PJ_SUCCESS); pjmedia_rtcp_build_rtcp_xr(&stream->rtcp.xr_session, 0, &xr_pkt, &xr_len); if (xr_len + len <= max_len) { pj_memcpy(pkt+len, xr_pkt, xr_len); len += xr_len; /* Send the RTCP XR to third-party destination if specified */ if (stream->rtcp_xr_dest_len) { pjmedia_transport_send_rtcp2(stream->transport, &stream->rtcp_xr_dest, stream->rtcp_xr_dest_len, xr_pkt, xr_len); } } else { PJ_PERROR(4,(stream->port.info.name.ptr, PJ_ETOOBIG, "Error generating RTCP-XR")); } } #endif /* Build RTCP BYE packet */ if (with_bye) { pj_size_t bye_len; bye_len = max_len - len; status = pjmedia_rtcp_build_rtcp_bye(&stream->rtcp, pkt+len, &bye_len, NULL); if (status != PJ_SUCCESS) { PJ_PERROR(4,(stream->port.info.name.ptr, status, "Error generating RTCP BYE")); } else { len += (int)bye_len; } } /* Send! */ status = pjmedia_transport_send_rtcp(stream->transport, pkt, len); return status; } /** * check_tx_rtcp() * * This function is can be called by either put_frame() or get_frame(), * to transmit periodic RTCP SR/RR report. */ static void check_tx_rtcp(pjmedia_stream *stream, pj_uint32_t timestamp) { /* Note that timestamp may represent local or remote timestamp, * depending on whether this function is called from put_frame() * or get_frame(). */ if (stream->rtcp_last_tx == 0) { stream->rtcp_last_tx = timestamp; } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) { pj_bool_t with_xr = PJ_FALSE; pj_status_t status; #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) if (stream->rtcp.xr_enabled) { if (stream->rtcp_xr_last_tx == 0) { stream->rtcp_xr_last_tx = timestamp; } else if (timestamp - stream->rtcp_xr_last_tx >= stream->rtcp_xr_interval) { with_xr = PJ_TRUE; /* Update last tx RTCP XR */ stream->rtcp_xr_last_tx = timestamp; } } #endif status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, PJ_FALSE, with_xr); if (status != PJ_SUCCESS) { PJ_PERROR(4,(stream->port.info.name.ptr, status, "Error sending RTCP")); } stream->rtcp_last_tx = timestamp; } } /** * Rebuffer the frame when encoder and decoder has different ptime * (such as when different iLBC modes are used by local and remote) */ static void rebuffer(pjmedia_stream *stream, pjmedia_frame *frame) { /* How many samples are needed */ unsigned count; /* Normalize frame */ if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) frame->size = 0; /* Remove used frame from the buffer. */ if (stream->enc_buf_pos) { if (stream->enc_buf_count) { pj_memmove(stream->enc_buf, stream->enc_buf + stream->enc_buf_pos, (stream->enc_buf_count << 1)); } stream->enc_buf_pos = 0; } /* Make sure we have space to store the new frame */ pj_assert(stream->enc_buf_count + (frame->size >> 1) < stream->enc_buf_size); /* Append new frame to the buffer */ if (frame->size) { /* Handle case when there is no port transmitting to this port */ if (frame->buf) { pj_memcpy(stream->enc_buf + stream->enc_buf_count, frame->buf, frame->size); } else { pj_bzero(stream->enc_buf + stream->enc_buf_count, frame->size); } stream->enc_buf_count += ((unsigned)frame->size >> 1); } /* How many samples are needed */ count = stream->codec_param.info.enc_ptime * PJMEDIA_PIA_SRATE(&stream->port.info) / 1000; /* See if we have enough samples */ if (stream->enc_buf_count >= count) { frame->type = PJMEDIA_FRAME_TYPE_AUDIO; frame->buf = stream->enc_buf; frame->size = (count << 1); stream->enc_buf_pos = count; stream->enc_buf_count -= count; } else { /* We don't have enough samples */ frame->type = PJMEDIA_FRAME_TYPE_NONE; } } /** * put_frame_imp() */ static pj_status_t put_frame_imp( pjmedia_port *port, pjmedia_frame *frame ) { pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata; pjmedia_channel *channel = stream->enc; pj_status_t status = 0; pjmedia_frame frame_out; unsigned ts_len, rtp_ts_len, samples_per_frame; void *rtphdr; int rtphdrlen; int inc_timestamp = 0; #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0 /* If the interval since last sending packet is greater than * PJMEDIA_STREAM_KA_INTERVAL, send keep-alive packet. */ if (stream->use_ka) { pj_uint32_t dtx_duration; dtx_duration = pj_timestamp_diff32(&stream->last_frm_ts_sent, &frame->timestamp); if (dtx_duration > PJMEDIA_STREAM_KA_INTERVAL * PJMEDIA_PIA_SRATE(&stream->port.info)) { send_keep_alive_packet(stream); stream->last_frm_ts_sent = frame->timestamp; } } #endif /* Don't do anything if stream is paused */ if (channel->paused) { stream->enc_buf_pos = stream->enc_buf_count = 0; return PJ_SUCCESS; } /* Number of samples in the frame */ if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) ts_len = ((unsigned)frame->size >> 1) / stream->codec_param.info.channel_cnt; else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) ts_len = PJMEDIA_PIA_SPF(&stream->port.info) / PJMEDIA_PIA_CCNT(&stream->port.info); else ts_len = 0; /* Increment transmit duration */ stream->tx_duration += ts_len; #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0) /* Handle special case for audio codec with RTP timestamp inconsistence * e.g: G722, MPEG audio. */ if (stream->has_g722_mpeg_bug) rtp_ts_len = stream->rtp_tx_ts_len_per_pkt; else rtp_ts_len = ts_len; #else rtp_ts_len = ts_len; #endif /* Init frame_out buffer. */ frame_out.buf = ((char*)channel->out_pkt) + sizeof(pjmedia_rtp_hdr); frame_out.size = 0; /* Calculate number of samples per frame */ samples_per_frame = stream->enc_samples_per_pkt; /* If we have DTMF digits in the queue, transmit the digits. * Otherwise encode the PCM buffer. */ if (stream->tx_dtmf_count) { int first=0, last=0; create_dtmf_payload(stream, &frame_out, 0, &first, &last); /* Encapsulate into RTP packet. Note that: * - RTP marker should be set on the beginning of a new event * - RTP timestamp is constant for the same packet. */ status = pjmedia_rtp_encode_rtp( &channel->rtp, stream->tx_event_pt, first, (int)frame_out.size, (first ? rtp_ts_len : 0), (const void**)&rtphdr, &rtphdrlen); if (last) { /* This is the last packet for the event. * Increment the RTP timestamp of the RTP session, for next * RTP packets. */ inc_timestamp = PJMEDIA_DTMF_DURATION + ((DTMF_EBIT_RETRANSMIT_CNT-1) * samples_per_frame) - rtp_ts_len; } /* * Special treatment for FRAME_TYPE_AUDIO but with frame->buf==NULL. * This happens when stream input is disconnected from the bridge. * In this case we periodically transmit RTP frame to keep NAT binding * open, by giving zero PCM frame to the codec. * * This was originally done in http://trac.pjsip.org/repos/ticket/56, * but then disabled in http://trac.pjsip.org/repos/ticket/439, but * now it's enabled again. */ } else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO && frame->buf == NULL && stream->port.info.fmt.id == PJMEDIA_FORMAT_L16 && (stream->dir & PJMEDIA_DIR_ENCODING) && stream->enc_samples_per_pkt < PJ_ARRAY_SIZE(zero_frame)) { pjmedia_frame silence_frame; pj_bzero(&silence_frame, sizeof(silence_frame)); silence_frame.buf = zero_frame; silence_frame.size = stream->enc_samples_per_pkt * 2; silence_frame.type = PJMEDIA_FRAME_TYPE_AUDIO; silence_frame.timestamp.u32.lo = pj_ntohl(stream->enc->rtp.out_hdr.ts); /* Encode! */ status = pjmedia_codec_encode( stream->codec, &silence_frame, channel->out_pkt_size - sizeof(pjmedia_rtp_hdr), &frame_out); if (status != PJ_SUCCESS) { LOGERR_((stream->port.info.name.ptr, "Codec encode() error", status)); return status; } /* Encapsulate. */ status = pjmedia_rtp_encode_rtp( &channel->rtp, channel->pt, 0, (int)frame_out.size, rtp_ts_len, (const void**)&rtphdr, &rtphdrlen); /* Encode audio frame */ } else if ((frame->type == PJMEDIA_FRAME_TYPE_AUDIO && frame->buf != NULL) || (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED)) { /* Encode! */ status = pjmedia_codec_encode( stream->codec, frame, channel->out_pkt_size - sizeof(pjmedia_rtp_hdr), &frame_out); if (status != PJ_SUCCESS) { LOGERR_((stream->port.info.name.ptr, "Codec encode() error", status)); return status; } /* Encapsulate. */ status = pjmedia_rtp_encode_rtp( &channel->rtp, channel->pt, 0, (int)frame_out.size, rtp_ts_len, (const void**)&rtphdr, &rtphdrlen); } else { /* Just update RTP session's timestamp. */ status = pjmedia_rtp_encode_rtp( &channel->rtp, 0, 0, 0, rtp_ts_len, (const void**)&rtphdr, &rtphdrlen); } if (status != PJ_SUCCESS) { LOGERR_((stream->port.info.name.ptr, "RTP encode_rtp() error", status)); return status; } /* Check if now is the time to transmit RTCP SR/RR report. * We only do this when stream direction is not "decoding only", because * when it is, check_tx_rtcp() will be handled by get_frame(). */ if (stream->dir != PJMEDIA_DIR_DECODING) { check_tx_rtcp(stream, pj_ntohl(channel->rtp.out_hdr.ts)); } /* Do nothing if we have nothing to transmit */ if (frame_out.size == 0) { if (stream->is_streaming) { PJ_LOG(5,(stream->port.info.name.ptr,"Starting silence")); stream->is_streaming = PJ_FALSE; } return PJ_SUCCESS; } /* Copy RTP header to the beginning of packet */ pj_memcpy(channel->out_pkt, rtphdr, sizeof(pjmedia_rtp_hdr)); /* Special case for DTMF: timestamp remains constant for * the same event, and is only updated after a complete event * has been transmitted. */ if (inc_timestamp) { pjmedia_rtp_encode_rtp( &channel->rtp, stream->tx_event_pt, 0, 0, inc_timestamp, NULL, NULL); } /* Set RTP marker bit if currently not streaming */ if (stream->is_streaming == PJ_FALSE) { pjmedia_rtp_hdr *rtp = (pjmedia_rtp_hdr*) channel->out_pkt; rtp->m = 1; PJ_LOG(5,(stream->port.info.name.ptr,"Start talksprut..")); } stream->is_streaming = PJ_TRUE; /* Send the RTP packet to the transport. */ status = pjmedia_transport_send_rtp(stream->transport, channel->out_pkt, frame_out.size + sizeof(pjmedia_rtp_hdr)); if (status != PJ_SUCCESS) { PJ_PERROR(4,(stream->port.info.name.ptr, status, "Error sending RTP")); return PJ_SUCCESS; } /* Update stat */ pjmedia_rtcp_tx_rtp(&stream->rtcp, (unsigned)frame_out.size); stream->rtcp.stat.rtp_tx_last_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts); stream->rtcp.stat.rtp_tx_last_seq = pj_ntohs(stream->enc->rtp.out_hdr.seq); #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /* Update timestamp of last sending packet. */ stream->last_frm_ts_sent = frame->timestamp; #endif return PJ_SUCCESS; } /** * put_frame() * * This callback is called by upstream component when it has PCM frame * to transmit. This function encodes the PCM frame, pack it into * RTP packet, and transmit to peer. */ static pj_status_t put_frame( pjmedia_port *port, pjmedia_frame *frame ) { pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata; pjmedia_frame tmp_zero_frame; unsigned samples_per_frame; samples_per_frame = stream->enc_samples_per_pkt; /* http://www.pjsip.org/trac/ticket/56: * when input is PJMEDIA_FRAME_TYPE_NONE, feed zero PCM frame * instead so that encoder can decide whether or not to transmit * silence frame. */ if (frame->type == PJMEDIA_FRAME_TYPE_NONE) { pj_memcpy(&tmp_zero_frame, frame, sizeof(pjmedia_frame)); frame = &tmp_zero_frame; tmp_zero_frame.buf = NULL; tmp_zero_frame.size = samples_per_frame * 2; tmp_zero_frame.type = PJMEDIA_FRAME_TYPE_AUDIO; } #if 0 // This is no longer needed because each TYPE_NONE frame will // be converted into zero frame above /* If VAD is temporarily disabled during creation, feed zero PCM frame * to the codec. */ if (stream->vad_enabled != stream->codec_param.setting.vad && stream->vad_enabled != 0 && frame->type == PJMEDIA_FRAME_TYPE_NONE && samples_per_frame <= ZERO_PCM_MAX_SIZE) { pj_memcpy(&tmp_in_frame, frame, sizeof(pjmedia_frame)); frame = &tmp_in_frame; tmp_in_frame.buf = NULL; tmp_in_frame.size = samples_per_frame * 2; tmp_in_frame.type = PJMEDIA_FRAME_TYPE_AUDIO; } #endif /* If VAD is temporarily disabled during creation, enable it * after transmitting for VAD_SUSPEND_SEC seconds. */ if (stream->vad_enabled != stream->codec_param.setting.vad && (stream->tx_duration - stream->ts_vad_disabled) > PJMEDIA_PIA_SRATE(&stream->port.info) * PJMEDIA_STREAM_VAD_SUSPEND_MSEC / 1000) { stream->codec_param.setting.vad = stream->vad_enabled; pjmedia_codec_modify(stream->codec, &stream->codec_param); PJ_LOG(4,(stream->port.info.name.ptr,"VAD re-enabled")); } /* If encoder has different ptime than decoder, then the frame must * be passed through the encoding buffer via rebuffer() function. */ if (stream->enc_buf != NULL) { pjmedia_frame tmp_rebuffer_frame; pj_status_t status = PJ_SUCCESS; /* Copy original frame to temporary frame since we need * to modify it. */ pj_memcpy(&tmp_rebuffer_frame, frame, sizeof(pjmedia_frame)); /* Loop while we have full frame in enc_buffer */ for (;;) { pj_status_t st; /* Run rebuffer() */ rebuffer(stream, &tmp_rebuffer_frame); /* Process this frame */ st = put_frame_imp(port, &tmp_rebuffer_frame); if (st != PJ_SUCCESS) status = st; /* If we still have full frame in the buffer, re-run * rebuffer() with NULL frame. */ if (stream->enc_buf_count >= stream->enc_samples_per_pkt) { tmp_rebuffer_frame.type = PJMEDIA_FRAME_TYPE_NONE; } else { /* Otherwise break */ break; } } return status; } else { return put_frame_imp(port, frame); } } #if 0 static void dump_bin(const char *buf, unsigned len) { unsigned i; PJ_LOG(3,(THIS_FILE, "begin dump")); for (i=0; ilast_dtmf != -1 && event->event == stream->last_dtmf && pj_ntohs(event->duration) >= stream->last_dtmf_dur) { /* Yes, this is the same event. */ stream->last_dtmf_dur = pj_ntohs(event->duration); return; } /* Ignore unknown event. */ #if defined(PJMEDIA_HAS_DTMF_FLASH) && PJMEDIA_HAS_DTMF_FLASH!= 0 if (event->event > 16) { #else if (event->event > 15) { #endif PJ_LOG(5,(stream->port.info.name.ptr, "Ignored RTP pkt with bad DTMF event %d", event->event)); return; } /* New event! */ PJ_LOG(5,(stream->port.info.name.ptr, "Received DTMF digit %c, vol=%d", digitmap[event->event], (event->e_vol & 0x3F))); stream->last_dtmf = event->event; stream->last_dtmf_dur = pj_ntohs(event->duration); /* If DTMF callback is installed, call the callback, otherwise keep * the DTMF digits in the buffer. */ if (stream->dtmf_cb) { stream->dtmf_cb(stream, stream->dtmf_cb_user_data, digitmap[event->event]); } else { /* By convention, we use jitter buffer's mutex to access shared * DTMF variables. */ pj_mutex_lock(stream->jb_mutex); if (stream->rx_dtmf_count >= PJ_ARRAY_SIZE(stream->rx_dtmf_buf)) { /* DTMF digits overflow. Discard the oldest digit. */ pj_array_erase(stream->rx_dtmf_buf, sizeof(stream->rx_dtmf_buf[0]), stream->rx_dtmf_count, 0); --stream->rx_dtmf_count; } stream->rx_dtmf_buf[stream->rx_dtmf_count++] = digitmap[event->event]; pj_mutex_unlock(stream->jb_mutex); } } /* * This callback is called by stream transport on receipt of packets * in the RTP socket. */ static void on_rx_rtp( void *data, void *pkt, pj_ssize_t bytes_read) { pjmedia_stream *stream = (pjmedia_stream*) data; pjmedia_channel *channel = stream->dec; const pjmedia_rtp_hdr *hdr; const void *payload; unsigned payloadlen; pjmedia_rtp_status seq_st; pj_status_t status; pj_bool_t pkt_discarded = PJ_FALSE; /* Check for errors */ if (bytes_read < 0) { status = (pj_status_t)-bytes_read; if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { return; } if (stream->rtp_rx_last_err != status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(stream->port.info.name.ptr, "Unable to receive RTP packet, recv() returned %d: %s", status, errmsg)); stream->rtp_rx_last_err = status; } return; } else { stream->rtp_rx_last_err = PJ_SUCCESS; } /* Ignore keep-alive packets */ if (bytes_read < (pj_ssize_t) sizeof(pjmedia_rtp_hdr)) return; /* Update RTP and RTCP session. */ status = pjmedia_rtp_decode_rtp(&channel->rtp, pkt, (int)bytes_read, &hdr, &payload, &payloadlen); if (status != PJ_SUCCESS) { LOGERR_((stream->port.info.name.ptr, "RTP decode error", status)); stream->rtcp.stat.rx.discard++; return; } /* Ignore the packet if decoder is paused */ if (channel->paused) goto on_return; /* Update RTP session (also checks if RTP session can accept * the incoming packet. */ pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, hdr->pt != stream->rx_event_pt); if (seq_st.status.value) { TRC_ ((stream->port.info.name.ptr, "RTP status: badpt=%d, badssrc=%d, dup=%d, " "outorder=%d, probation=%d, restart=%d", seq_st.status.flag.badpt, seq_st.status.flag.badssrc, seq_st.status.flag.dup, seq_st.status.flag.outorder, seq_st.status.flag.probation, seq_st.status.flag.restart)); if (seq_st.status.flag.badpt) { PJ_LOG(4,(stream->port.info.name.ptr, "Bad RTP pt %d (expecting %d)", hdr->pt, channel->rtp.out_pt)); } if (seq_st.status.flag.badssrc) { PJ_LOG(4,(stream->port.info.name.ptr, "Changed RTP peer SSRC %d (previously %d)", channel->rtp.peer_ssrc, stream->rtcp.peer_ssrc)); stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc; } } /* Skip bad RTP packet */ if (seq_st.status.flag.bad) { pkt_discarded = PJ_TRUE; goto on_return; } /* Ignore if payloadlen is zero */ if (payloadlen == 0) { pkt_discarded = PJ_TRUE; goto on_return; } /* Handle incoming DTMF. */ if (hdr->pt == stream->rx_event_pt) { /* Ignore out-of-order packet as it will be detected as new * digit. Also ignore duplicate packet as it serves no use. */ if (seq_st.status.flag.outorder || seq_st.status.flag.dup) { goto on_return; } handle_incoming_dtmf(stream, payload, payloadlen); goto on_return; } /* Put "good" packet to jitter buffer, or reset the jitter buffer * when RTP session is restarted. */ pj_mutex_lock( stream->jb_mutex ); if (seq_st.status.flag.restart) { status = pjmedia_jbuf_reset(stream->jb); PJ_LOG(4,(stream->port.info.name.ptr, "Jitter buffer reset")); } else { /* * Packets may contain more than one frames, while the jitter * buffer can only take one frame per "put" operation. So we need * to ask the codec to "parse" the payload into multiple frames. */ enum { MAX = 16 }; pj_timestamp ts; unsigned i, count = MAX; unsigned ts_span; pjmedia_frame frames[MAX]; /* Get the timestamp of the first sample */ ts.u64 = pj_ntohl(hdr->ts); /* Parse the payload. */ status = pjmedia_codec_parse(stream->codec, (void*)payload, payloadlen, &ts, &count, frames); if (status != PJ_SUCCESS) { LOGERR_((stream->port.info.name.ptr, "Codec parse() error", status)); count = 0; } #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0) /* This code is used to learn the samples per frame value that is put * by remote endpoint, for codecs with inconsistent clock rate such * as G.722 or MPEG audio. We need to learn the samples per frame * value as it is used as divider when inserting frames into the * jitter buffer. */ if (stream->has_g722_mpeg_bug) { if (stream->rtp_rx_check_cnt) { /* Make sure the detection performed only on two consecutive * packets with valid RTP sequence and no wrapped timestamp. */ if (seq_st.diff == 1 && stream->rtp_rx_last_ts && ts.u64 > stream->rtp_rx_last_ts && stream->rtp_rx_last_cnt > 0) { unsigned peer_frm_ts_diff; unsigned frm_ts_span; /* Calculate actual frame timestamp span */ frm_ts_span = PJMEDIA_PIA_SPF(&stream->port.info) / stream->codec_param.setting.frm_per_pkt/ PJMEDIA_PIA_CCNT(&stream->port.info); /* Get remote frame timestamp span */ peer_frm_ts_diff = ((pj_uint32_t)ts.u64-stream->rtp_rx_last_ts) / stream->rtp_rx_last_cnt; /* Possibilities remote's samples per frame for G.722 * are only (frm_ts_span) and (frm_ts_span/2), this * validation is needed to avoid wrong decision because * of silence frames. */ if (stream->codec_param.info.pt == PJMEDIA_RTP_PT_G722 && (peer_frm_ts_diff == frm_ts_span || peer_frm_ts_diff == (frm_ts_span>>1))) { if (peer_frm_ts_diff < stream->rtp_rx_ts_len_per_frame) { stream->rtp_rx_ts_len_per_frame = peer_frm_ts_diff; /* Done, stop the check immediately */ stream->rtp_rx_check_cnt = 1; } if (--stream->rtp_rx_check_cnt == 0) { PJ_LOG(4, (THIS_FILE, "G722 codec used, remote" " samples per frame detected = %d", stream->rtp_rx_ts_len_per_frame)); /* Reset jitter buffer once detection done */ pjmedia_jbuf_reset(stream->jb); } } } stream->rtp_rx_last_ts = (pj_uint32_t)ts.u64; stream->rtp_rx_last_cnt = count; } ts_span = stream->rtp_rx_ts_len_per_frame; /* Adjust the timestamp of the parsed frames */ for (i=0; icodec_param.info.frm_ptime * stream->codec_param.info.clock_rate / 1000; } #else ts_span = stream->codec_param.info.frm_ptime * stream->codec_param.info.clock_rate / 1000; #endif /* Put each frame to jitter buffer. */ for (i=0; ijb, frames[i].buf, frames[i].size, frames[i].bit_info, ext_seq, &discarded); if (discarded) pkt_discarded = PJ_TRUE; } #if TRACE_JB trace_jb_put(stream, hdr, payloadlen, count); #endif } pj_mutex_unlock( stream->jb_mutex ); /* Check if now is the time to transmit RTCP SR/RR report. * We only do this when stream direction is "decoding only", * because otherwise check_tx_rtcp() will be handled by put_frame() */ if (stream->dir == PJMEDIA_DIR_DECODING) { check_tx_rtcp(stream, pj_ntohl(hdr->ts)); } if (status != 0) { LOGERR_((stream->port.info.name.ptr, "Jitter buffer put() error", status)); pkt_discarded = PJ_TRUE; goto on_return; } on_return: /* Update RTCP session */ if (stream->rtcp.peer_ssrc == 0) stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc; pjmedia_rtcp_rx_rtp2(&stream->rtcp, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), payloadlen, pkt_discarded); /* Send RTCP RR and SDES after we receive some RTP packets */ if (stream->rtcp.received >= 10 && !stream->initial_rr) { status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, PJ_FALSE, PJ_FALSE); if (status != PJ_SUCCESS) { PJ_PERROR(4,(stream->port.info.name.ptr, status, "Error sending initial RTCP RR")); } else { stream->initial_rr = PJ_TRUE; } } } /* * This callback is called by stream transport on receipt of packets * in the RTCP socket. */ static void on_rx_rtcp( void *data, void *pkt, pj_ssize_t bytes_read) { pjmedia_stream *stream = (pjmedia_stream*) data; /* Check for errors */ if (bytes_read < 0) { if (bytes_read != -PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { LOGERR_((stream->port.info.name.ptr, "RTCP recv() error", (pj_status_t)-bytes_read)); } return; } pjmedia_rtcp_rx_rtcp(&stream->rtcp, pkt, bytes_read); } /* * Create media channel. */ static pj_status_t create_channel( pj_pool_t *pool, pjmedia_stream *stream, pjmedia_dir dir, unsigned pt, const pjmedia_stream_info *param, pjmedia_channel **p_channel) { pjmedia_channel *channel; pj_status_t status; /* Allocate memory for channel descriptor */ channel = PJ_POOL_ZALLOC_T(pool, pjmedia_channel); PJ_ASSERT_RETURN(channel != NULL, PJ_ENOMEM); /* Init channel info. */ channel->stream = stream; channel->dir = dir; channel->paused = 1; channel->pt = pt; /* Allocate buffer for outgoing packet. */ if (param->type == PJMEDIA_TYPE_AUDIO) { channel->out_pkt_size = sizeof(pjmedia_rtp_hdr) + stream->codec_param.info.max_bps * PJMEDIA_MAX_FRAME_DURATION_MS / 8 / 1000; if (channel->out_pkt_size > PJMEDIA_MAX_MTU - PJMEDIA_STREAM_RESV_PAYLOAD_LEN) { channel->out_pkt_size = PJMEDIA_MAX_MTU - PJMEDIA_STREAM_RESV_PAYLOAD_LEN; } } else { return PJ_ENOTSUP; } channel->out_pkt = pj_pool_alloc(pool, channel->out_pkt_size); PJ_ASSERT_RETURN(channel->out_pkt != NULL, PJ_ENOMEM); /* Create RTP and RTCP sessions: */ if (param->rtp_seq_ts_set == 0) { status = pjmedia_rtp_session_init(&channel->rtp, pt, param->ssrc); } else { pjmedia_rtp_session_setting settings; settings.flags = (pj_uint8_t)((param->rtp_seq_ts_set << 2) | 3); settings.default_pt = pt; settings.sender_ssrc = param->ssrc; settings.seq = param->rtp_seq; settings.ts = param->rtp_ts; status = pjmedia_rtp_session_init2(&channel->rtp, settings); } if (status != PJ_SUCCESS) return status; /* Done. */ *p_channel = channel; return PJ_SUCCESS; } /* * Create media stream. */ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, pj_pool_t *pool, const pjmedia_stream_info *info, pjmedia_transport *tp, void *user_data, pjmedia_stream **p_stream) { enum { M = 32 }; pjmedia_stream *stream; pj_str_t name; unsigned jb_init, jb_max, jb_min_pre, jb_max_pre; pjmedia_audio_format_detail *afd; pj_pool_t *own_pool = NULL; char *p; pj_status_t status; PJ_ASSERT_RETURN(endpt && info && p_stream, PJ_EINVAL); if (pool == NULL) { own_pool = pjmedia_endpt_create_pool( endpt, "strm%p", PJMEDIA_STREAM_SIZE, PJMEDIA_STREAM_INC); PJ_ASSERT_RETURN(own_pool != NULL, PJ_ENOMEM); pool = own_pool; } /* Allocate the media stream: */ stream = PJ_POOL_ZALLOC_T(pool, pjmedia_stream); PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM); stream->own_pool = own_pool; pj_memcpy(&stream->si, info, sizeof(*info)); pj_strdup(pool, &stream->si.fmt.encoding_name, &info->fmt.encoding_name); if (info->param) stream->si.param = pjmedia_codec_param_clone(pool, info->param); /* Init stream/port name */ name.ptr = (char*) pj_pool_alloc(pool, M); name.slen = pj_ansi_snprintf(name.ptr, M, "strm%p", stream); /* Init some port-info. Some parts of the info will be set later * once we have more info about the codec. */ pjmedia_port_info_init(&stream->port.info, &name, PJMEDIA_SIG_PORT_STREAM, info->fmt.clock_rate, info->fmt.channel_cnt, 16, 80); afd = pjmedia_format_get_audio_format_detail(&stream->port.info.fmt, 1); /* Init port. */ //No longer there in 2.0 //pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name); afd->clock_rate = info->fmt.clock_rate; afd->channel_count = info->fmt.channel_cnt; stream->port.port_data.pdata = stream; /* Init stream: */ stream->endpt = endpt; stream->codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); stream->dir = info->dir; stream->user_data = user_data; stream->rtcp_interval = (PJMEDIA_RTCP_INTERVAL-500 + (pj_rand()%1000)) * info->fmt.clock_rate / 1000; stream->rtcp_sdes_bye_disabled = info->rtcp_sdes_bye_disabled; stream->tx_event_pt = info->tx_event_pt ? info->tx_event_pt : -1; stream->rx_event_pt = info->rx_event_pt ? info->rx_event_pt : -1; stream->last_dtmf = -1; stream->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME; #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 stream->use_ka = info->use_ka; #endif /* Build random RTCP CNAME. CNAME has user@host format */ stream->cname.ptr = p = (char*) pj_pool_alloc(pool, 20); pj_create_random_string(p, 5); p += 5; *p++ = '@'; *p++ = 'p'; *p++ = 'j'; pj_create_random_string(p, 6); p += 6; *p++ = '.'; *p++ = 'o'; *p++ = 'r'; *p++ = 'g'; stream->cname.slen = p - stream->cname.ptr; /* Create mutex to protect jitter buffer: */ status = pj_mutex_create_simple(pool, NULL, &stream->jb_mutex); if (status != PJ_SUCCESS) goto err_cleanup; /* Create and initialize codec: */ status = pjmedia_codec_mgr_alloc_codec( stream->codec_mgr, &info->fmt, &stream->codec); if (status != PJ_SUCCESS) goto err_cleanup; /* Get codec param: */ if (info->param) stream->codec_param = *stream->si.param; else { status = pjmedia_codec_mgr_get_default_param(stream->codec_mgr, &info->fmt, &stream->codec_param); if (status != PJ_SUCCESS) goto err_cleanup; } /* Check for invalid max_bps. */ if (stream->codec_param.info.max_bps < stream->codec_param.info.avg_bps) stream->codec_param.info.max_bps = stream->codec_param.info.avg_bps; /* Check for invalid frame per packet. */ if (stream->codec_param.setting.frm_per_pkt < 1) stream->codec_param.setting.frm_per_pkt = 1; /* Init the codec. */ status = pjmedia_codec_init(stream->codec, pool); if (status != PJ_SUCCESS) goto err_cleanup; /* Open the codec. */ /* The clock rate for Opus codec is not static, * it's negotiated in the SDP. */ if (!pj_stricmp2(&info->fmt.encoding_name, "opus")) { stream->codec_param.info.clock_rate = info->fmt.clock_rate; stream->codec_param.info.channel_cnt = info->fmt.channel_cnt; } status = pjmedia_codec_open(stream->codec, &stream->codec_param); if (status != PJ_SUCCESS) goto err_cleanup; /* Set additional info and callbacks. */ afd->bits_per_sample = 16; afd->frame_time_usec = stream->codec_param.info.frm_ptime * stream->codec_param.setting.frm_per_pkt * 1000; stream->port.info.fmt.id = stream->codec_param.info.fmt_id; if (stream->codec_param.info.fmt_id == PJMEDIA_FORMAT_L16) { /* Raw format */ afd->avg_bps = afd->max_bps = afd->clock_rate * afd->channel_count * afd->bits_per_sample; stream->port.put_frame = &put_frame; stream->port.get_frame = &get_frame; } else { /* Encoded format */ afd->avg_bps = stream->codec_param.info.avg_bps; afd->max_bps = stream->codec_param.info.max_bps; /* Not applicable for 2.0 if ((stream->codec_param.info.max_bps * stream->codec_param.info.frm_ptime * stream->codec_param.setting.frm_per_pkt) % 8000 != 0) { ++stream->port.info.bytes_per_frame; } stream->port.info.format.bitrate = stream->codec_param.info.avg_bps; stream->port.info.format.vad = (stream->codec_param.setting.vad != 0); */ stream->port.put_frame = &put_frame; stream->port.get_frame = &get_frame_ext; } /* If encoder and decoder's ptime are asymmetric, then we need to * create buffer on the encoder side. This could happen for example * with iLBC */ if (stream->codec_param.info.enc_ptime!=0 && stream->codec_param.info.enc_ptime!=stream->codec_param.info.frm_ptime) { unsigned ptime; stream->enc_samples_per_pkt = stream->codec_param.info.enc_ptime * stream->codec_param.info.channel_cnt * afd->clock_rate / 1000; /* Set buffer size as twice the largest ptime value between * stream's ptime, encoder ptime, or decoder ptime. */ ptime = afd->frame_time_usec / 1000; if (stream->codec_param.info.enc_ptime > ptime) ptime = stream->codec_param.info.enc_ptime; if (stream->codec_param.info.frm_ptime > ptime) ptime = stream->codec_param.info.frm_ptime; ptime <<= 1; /* Allocate buffer */ stream->enc_buf_size = afd->clock_rate * ptime / 1000; stream->enc_buf = (pj_int16_t*) pj_pool_alloc(pool, stream->enc_buf_size * 2); } else { stream->enc_samples_per_pkt = PJMEDIA_AFD_SPF(afd); } /* Initially disable the VAD in the stream, to help traverse NAT better */ stream->vad_enabled = stream->codec_param.setting.vad; if (PJMEDIA_STREAM_VAD_SUSPEND_MSEC > 0 && stream->vad_enabled) { stream->codec_param.setting.vad = 0; stream->ts_vad_disabled = 0; pjmedia_codec_modify(stream->codec, &stream->codec_param); PJ_LOG(4,(stream->port.info.name.ptr,"VAD temporarily disabled")); } /* Get the frame size */ if (stream->codec_param.info.max_rx_frame_size > 0) { stream->frame_size = stream->codec_param.info.max_rx_frame_size; } else { stream->frame_size = stream->codec_param.info.max_bps * stream->codec_param.info.frm_ptime / 8 / 1000; if ((stream->codec_param.info.max_bps * stream->codec_param.info.frm_ptime) % 8000 != 0) { ++stream->frame_size; } } /* How many consecutive PLC frames can be generated */ stream->max_plc_cnt = (MAX_PLC_MSEC+stream->codec_param.info.frm_ptime-1)/ stream->codec_param.info.frm_ptime; #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0) stream->rtp_rx_check_cnt = 50; stream->has_g722_mpeg_bug = PJ_FALSE; stream->rtp_rx_last_ts = 0; stream->rtp_rx_last_cnt = 0; stream->rtp_tx_ts_len_per_pkt = stream->enc_samples_per_pkt / stream->codec_param.info.channel_cnt; stream->rtp_rx_ts_len_per_frame = PJMEDIA_AFD_SPF(afd) / stream->codec_param.setting.frm_per_pkt / stream->codec_param.info.channel_cnt; if (info->fmt.pt == PJMEDIA_RTP_PT_G722) { stream->has_g722_mpeg_bug = PJ_TRUE; /* RTP clock rate = 1/2 real clock rate */ stream->rtp_tx_ts_len_per_pkt >>= 1; } else if (!pj_stricmp2(&info->fmt.encoding_name, "opus")) { unsigned opus_ts_modifier = 48000 / afd->clock_rate; stream->rtp_rx_check_cnt = 0; stream->has_g722_mpeg_bug = PJ_TRUE; stream->rtp_tx_ts_len_per_pkt *= opus_ts_modifier; stream->rtp_rx_ts_len_per_frame *= opus_ts_modifier; } #endif /* Init jitter buffer parameters: */ if (info->jb_max >= stream->codec_param.info.frm_ptime) jb_max = (info->jb_max + stream->codec_param.info.frm_ptime - 1) / stream->codec_param.info.frm_ptime; else jb_max = 500 / stream->codec_param.info.frm_ptime; if (info->jb_min_pre >= stream->codec_param.info.frm_ptime) jb_min_pre = info->jb_min_pre / stream->codec_param.info.frm_ptime; else //jb_min_pre = 60 / stream->codec_param.info.frm_ptime; jb_min_pre = 1; if (info->jb_max_pre >= stream->codec_param.info.frm_ptime) jb_max_pre = info->jb_max_pre / stream->codec_param.info.frm_ptime; else //jb_max_pre = 240 / stream->codec_param.info.frm_ptime; jb_max_pre = jb_max * 4 / 5; if (info->jb_init >= stream->codec_param.info.frm_ptime) jb_init = info->jb_init / stream->codec_param.info.frm_ptime; else //jb_init = (jb_min_pre + jb_max_pre) / 2; jb_init = 0; /* Create jitter buffer */ status = pjmedia_jbuf_create(pool, &stream->port.info.name, stream->frame_size, stream->codec_param.info.frm_ptime, jb_max, &stream->jb); if (status != PJ_SUCCESS) goto err_cleanup; /* Set up jitter buffer */ pjmedia_jbuf_set_adaptive( stream->jb, jb_init, jb_min_pre, jb_max_pre); /* Create decoder channel: */ status = create_channel( pool, stream, PJMEDIA_DIR_DECODING, info->rx_pt, info, &stream->dec); if (status != PJ_SUCCESS) goto err_cleanup; /* Create encoder channel: */ status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING, info->tx_pt, info, &stream->enc); if (status != PJ_SUCCESS) goto err_cleanup; /* Init RTCP session: */ { pjmedia_rtcp_session_setting rtcp_setting; pjmedia_rtcp_session_setting_default(&rtcp_setting); rtcp_setting.name = stream->port.info.name.ptr; rtcp_setting.ssrc = info->ssrc; rtcp_setting.rtp_ts_base = pj_ntohl(stream->enc->rtp.out_hdr.ts); rtcp_setting.clock_rate = info->fmt.clock_rate; rtcp_setting.samples_per_frame = PJMEDIA_AFD_SPF(afd); #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0) /* Special case for G.722 */ if (info->fmt.pt == PJMEDIA_RTP_PT_G722) { rtcp_setting.clock_rate = 8000; rtcp_setting.samples_per_frame = 160; } #endif pjmedia_rtcp_init2(&stream->rtcp, &rtcp_setting); if (info->rtp_seq_ts_set) { stream->rtcp.stat.rtp_tx_last_seq = info->rtp_seq; stream->rtcp.stat.rtp_tx_last_ts = info->rtp_ts; } } /* Allocate outgoing RTCP buffer, should be enough to hold SR/RR, SDES, * BYE, and XR. */ stream->out_rtcp_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) + sizeof(pjmedia_rtcp_common) + (4 + (unsigned)stream->cname.slen) + 32; #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) if (info->rtcp_xr_enabled) { stream->out_rtcp_pkt_size += sizeof(pjmedia_rtcp_xr_pkt); } #endif if (stream->out_rtcp_pkt_size > PJMEDIA_MAX_MTU) stream->out_rtcp_pkt_size = PJMEDIA_MAX_MTU; stream->out_rtcp_pkt = pj_pool_alloc(pool, stream->out_rtcp_pkt_size); /* Only attach transport when stream is ready. */ status = pjmedia_transport_attach(tp, stream, &info->rem_addr, &info->rem_rtcp, pj_sockaddr_get_len(&info->rem_addr), &on_rx_rtp, &on_rx_rtcp); if (status != PJ_SUCCESS) goto err_cleanup; stream->transport = tp; #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) /* Enable RTCP XR and update stream info/config to RTCP XR */ if (info->rtcp_xr_enabled) { int i; pjmedia_rtcp_enable_xr(&stream->rtcp, PJ_TRUE); /* Set RTCP XR TX interval */ if (info->rtcp_xr_interval != 0) stream->rtcp_xr_interval = info->rtcp_xr_interval; else stream->rtcp_xr_interval = (PJMEDIA_RTCP_INTERVAL + (pj_rand() % 8000)) * info->fmt.clock_rate / 1000; /* Additional third-party RTCP XR destination */ if (info->rtcp_xr_dest.addr.sa_family != 0) { stream->rtcp_xr_dest_len = pj_sockaddr_get_len(&info->rtcp_xr_dest); pj_memcpy(&stream->rtcp_xr_dest, &info->rtcp_xr_dest, stream->rtcp_xr_dest_len); } /* jitter buffer adaptive info */ i = PJMEDIA_RTCP_XR_JB_ADAPTIVE; pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, PJMEDIA_RTCP_XR_INFO_CONF_JBA, i); /* Jitter buffer aggressiveness info (estimated) */ i = 7; pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, PJMEDIA_RTCP_XR_INFO_CONF_JBR, i); /* Jitter buffer absolute maximum delay */ i = jb_max * stream->codec_param.info.frm_ptime; pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, PJMEDIA_RTCP_XR_INFO_JB_ABS_MAX, i); /* PLC info */ if (stream->codec_param.setting.plc == 0) i = PJMEDIA_RTCP_XR_PLC_DIS; else #if PJMEDIA_WSOLA_IMP==PJMEDIA_WSOLA_IMP_WSOLA i = PJMEDIA_RTCP_XR_PLC_ENH; #else i = PJMEDIA_RTCP_XR_PLC_DIS; #endif pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, PJMEDIA_RTCP_XR_INFO_CONF_PLC, i); } #endif /* Update the stream info's codec param */ stream->si.param = &stream->codec_param; /* Send RTCP SDES */ if (!stream->rtcp_sdes_bye_disabled) { pjmedia_stream_send_rtcp_sdes(stream); } #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /* NAT hole punching by sending KA packet via RTP transport. */ if (stream->use_ka) send_keep_alive_packet(stream); #endif #if TRACE_JB { char trace_name[PJ_MAXPATH]; pj_ssize_t len; pj_ansi_snprintf(trace_name, sizeof(trace_name), TRACE_JB_PATH_PREFIX "%s.csv", stream->port.info.name.ptr); status = pj_file_open(pool, trace_name, PJ_O_WRONLY, &stream->trace_jb_fd); if (status != PJ_SUCCESS) { stream->trace_jb_fd = TRACE_JB_INVALID_FD; PJ_LOG(3,(THIS_FILE, "Failed creating RTP trace file '%s'", trace_name)); } else { stream->trace_jb_buf = (char*)pj_pool_alloc(pool, PJ_LOG_MAX_SIZE); /* Print column header */ len = pj_ansi_snprintf(stream->trace_jb_buf, PJ_LOG_MAX_SIZE, "Time, Operation, Size, Frame Count, " "Frame type, RTP Seq, RTP TS, RTP M, " "JB size, JB burst level, JB prefetch\n"); if (len < 1 || len >= PJ_LOG_MAX_SIZE) len = PJ_LOG_MAX_SIZE-1; pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); pj_file_flush(stream->trace_jb_fd); } } #endif /* Success! */ *p_stream = stream; PJ_LOG(5,(THIS_FILE, "Stream %s created", stream->port.info.name.ptr)); return PJ_SUCCESS; err_cleanup: pjmedia_stream_destroy(stream); return status; } /* * Destroy stream. */ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) { pj_status_t status; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); /* Send RTCP BYE (also SDES & XR) */ if (!stream->rtcp_sdes_bye_disabled) { send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_TRUE); } /* If we're in the middle of transmitting DTMF digit, send one last * RFC 2833 RTP packet with 'End' flag set. */ if (stream->tx_dtmf_count && stream->tx_dtmf_buf[0].duration != 0) { pjmedia_frame frame_out; pjmedia_channel *channel = stream->enc; int first=0, last=0; void *rtphdr; int rtphdrlen; pj_bzero(&frame_out, sizeof(frame_out)); frame_out.buf = ((char*)channel->out_pkt) + sizeof(pjmedia_rtp_hdr); frame_out.size = 0; create_dtmf_payload(stream, &frame_out, 1, &first, &last); /* Encapsulate into RTP packet. Note that: * - RTP marker should be set on the beginning of a new event * - RTP timestamp is constant for the same packet. */ status = pjmedia_rtp_encode_rtp( &channel->rtp, stream->tx_event_pt, first, (int)frame_out.size, 0, (const void**)&rtphdr, &rtphdrlen); if (status == PJ_SUCCESS) { /* Copy RTP header to the beginning of packet */ pj_memcpy(channel->out_pkt, rtphdr, sizeof(pjmedia_rtp_hdr)); /* Send the RTP packet to the transport. */ status = pjmedia_transport_send_rtp(stream->transport, channel->out_pkt, frame_out.size + sizeof(pjmedia_rtp_hdr)); } if (status != PJ_SUCCESS) { PJ_PERROR(4,(stream->port.info.name.ptr, status, "Error sending RTP/DTMF end packet")); } } /* Detach from transport * MUST NOT hold stream mutex while detaching from transport, as * it may cause deadlock. See ticket #460 for the details. */ if (stream->transport) { pjmedia_transport_detach(stream->transport, stream); stream->transport = NULL; } /* This function may be called when stream is partly initialized. */ if (stream->jb_mutex) pj_mutex_lock(stream->jb_mutex); /* Free codec. */ if (stream->codec) { pjmedia_codec_close(stream->codec); pjmedia_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec); stream->codec = NULL; } /* Free mutex */ if (stream->jb_mutex) { pj_mutex_unlock(stream->jb_mutex); pj_mutex_destroy(stream->jb_mutex); stream->jb_mutex = NULL; } /* Destroy jitter buffer */ if (stream->jb) pjmedia_jbuf_destroy(stream->jb); #if TRACE_JB if (TRACE_JB_OPENED(stream)) { pj_file_close(stream->trace_jb_fd); stream->trace_jb_fd = TRACE_JB_INVALID_FD; } #endif if (stream->own_pool) { pj_pool_t *pool = stream->own_pool; stream->own_pool = NULL; pj_pool_release(pool); } return PJ_SUCCESS; } /* * Get the last frame frame type retreived from the jitter buffer. */ PJ_DEF(char) pjmedia_stream_get_last_jb_frame_type(pjmedia_stream *stream) { return stream->jb_last_frm; } /* * Get the port interface. */ PJ_DEF(pj_status_t) pjmedia_stream_get_port( pjmedia_stream *stream, pjmedia_port **p_port ) { *p_port = &stream->port; return PJ_SUCCESS; } /* * Get the transport object */ PJ_DEF(pjmedia_transport*) pjmedia_stream_get_transport(pjmedia_stream *st) { return st->transport; } /* * Start stream. */ PJ_DEF(pj_status_t) pjmedia_stream_start(pjmedia_stream *stream) { PJ_ASSERT_RETURN(stream && stream->enc && stream->dec, PJ_EINVALIDOP); if (stream->enc && (stream->dir & PJMEDIA_DIR_ENCODING)) { stream->enc->paused = 0; //pjmedia_snd_stream_start(stream->enc->snd_stream); PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream started")); } else { PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream paused")); } if (stream->dec && (stream->dir & PJMEDIA_DIR_DECODING)) { stream->dec->paused = 0; //pjmedia_snd_stream_start(stream->dec->snd_stream); PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream started")); } else { PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream paused")); } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_stream_get_info( const pjmedia_stream *stream, pjmedia_stream_info *info) { PJ_ASSERT_RETURN(stream && info, PJ_EINVAL); pj_memcpy(info, &stream->si, sizeof(pjmedia_stream_info)); return PJ_SUCCESS; } /* * Get stream statistics. */ PJ_DEF(pj_status_t) pjmedia_stream_get_stat( const pjmedia_stream *stream, pjmedia_rtcp_stat *stat) { PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL); pj_memcpy(stat, &stream->rtcp.stat, sizeof(pjmedia_rtcp_stat)); return PJ_SUCCESS; } /* * Reset the stream statistics in the middle of a stream session. */ PJ_DEF(pj_status_t) pjmedia_stream_reset_stat(pjmedia_stream *stream) { PJ_ASSERT_RETURN(stream, PJ_EINVAL); pjmedia_rtcp_init_stat(&stream->rtcp.stat); return PJ_SUCCESS; } #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) /* * Get stream extended statistics. */ PJ_DEF(pj_status_t) pjmedia_stream_get_stat_xr( const pjmedia_stream *stream, pjmedia_rtcp_xr_stat *stat) { PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL); if (stream->rtcp.xr_enabled) { pj_memcpy(stat, &stream->rtcp.xr_session.stat, sizeof(pjmedia_rtcp_xr_stat)); return PJ_SUCCESS; } return PJ_ENOTFOUND; } #endif /* * Get jitter buffer state. */ PJ_DEF(pj_status_t) pjmedia_stream_get_stat_jbuf(const pjmedia_stream *stream, pjmedia_jb_state *state) { PJ_ASSERT_RETURN(stream && state, PJ_EINVAL); return pjmedia_jbuf_get_state(stream->jb, state); } /* * Pause stream. */ PJ_DEF(pj_status_t) pjmedia_stream_pause( pjmedia_stream *stream, pjmedia_dir dir) { PJ_ASSERT_RETURN(stream, PJ_EINVAL); if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) { stream->enc->paused = 1; PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream paused")); } if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) { stream->dec->paused = 1; /* Also reset jitter buffer */ pj_mutex_lock( stream->jb_mutex ); pjmedia_jbuf_reset(stream->jb); pj_mutex_unlock( stream->jb_mutex ); PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream paused")); } return PJ_SUCCESS; } /* * Resume stream */ PJ_DEF(pj_status_t) pjmedia_stream_resume( pjmedia_stream *stream, pjmedia_dir dir) { PJ_ASSERT_RETURN(stream, PJ_EINVAL); if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) { stream->enc->paused = 0; PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream resumed")); } if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) { stream->dec->paused = 0; PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream resumed")); } return PJ_SUCCESS; } /* * Dial DTMF */ PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf( pjmedia_stream *stream, const pj_str_t *digit_char) { pj_status_t status = PJ_SUCCESS; /* By convention we use jitter buffer mutex to access DTMF * queue. */ PJ_ASSERT_RETURN(stream && digit_char, PJ_EINVAL); /* Check that remote can receive DTMF events. */ if (stream->tx_event_pt < 0) { return PJMEDIA_RTP_EREMNORFC2833; } pj_mutex_lock(stream->jb_mutex); if (stream->tx_dtmf_count+digit_char->slen >= (long)PJ_ARRAY_SIZE(stream->tx_dtmf_buf)) { status = PJ_ETOOMANY; } else { int i; /* convert ASCII digits into payload type first, to make sure * that all digits are valid. */ for (i=0; islen; ++i) { unsigned pt; int dig = pj_tolower(digit_char->ptr[i]); if (dig >= '0' && dig <= '9') { pt = dig - '0'; } else if (dig >= 'a' && dig <= 'd') { pt = dig - 'a' + 12; } else if (dig == '*') { pt = 10; } else if (dig == '#') { pt = 11; } #if defined(PJMEDIA_HAS_DTMF_FLASH) && PJMEDIA_HAS_DTMF_FLASH!= 0 else if (dig == 'r') { pt = 16; } #endif else { status = PJMEDIA_RTP_EINDTMF; break; } stream->tx_dtmf_buf[stream->tx_dtmf_count+i].event = pt; stream->tx_dtmf_buf[stream->tx_dtmf_count+i].duration = 0; stream->tx_dtmf_buf[stream->tx_dtmf_count+i].ebit_cnt = 0; } if (status != PJ_SUCCESS) goto on_return; /* Increment digit count only if all digits are valid. */ stream->tx_dtmf_count += (int)digit_char->slen; } on_return: pj_mutex_unlock(stream->jb_mutex); return status; } /* * See if we have DTMF digits in the rx buffer. */ PJ_DEF(pj_bool_t) pjmedia_stream_check_dtmf(pjmedia_stream *stream) { return stream->rx_dtmf_count != 0; } /* * Retrieve incoming DTMF digits from the stream's DTMF buffer. */ PJ_DEF(pj_status_t) pjmedia_stream_get_dtmf( pjmedia_stream *stream, char *digits, unsigned *size) { PJ_ASSERT_RETURN(stream && digits && size, PJ_EINVAL); pj_assert(sizeof(stream->rx_dtmf_buf[0]) == 0); /* By convention, we use jitter buffer's mutex to access DTMF * digits resources. */ pj_mutex_lock(stream->jb_mutex); if (stream->rx_dtmf_count < *size) *size = stream->rx_dtmf_count; if (*size) { pj_memcpy(digits, stream->rx_dtmf_buf, *size); stream->rx_dtmf_count -= *size; if (stream->rx_dtmf_count) { pj_memmove(stream->rx_dtmf_buf, &stream->rx_dtmf_buf[*size], stream->rx_dtmf_count); } } pj_mutex_unlock(stream->jb_mutex); return PJ_SUCCESS; } /* * Set callback to be called upon receiving DTMF digits. */ PJ_DEF(pj_status_t) pjmedia_stream_set_dtmf_callback(pjmedia_stream *stream, void (*cb)(pjmedia_stream*, void *user_data, int digit), void *user_data) { PJ_ASSERT_RETURN(stream, PJ_EINVAL); /* By convention, we use jitter buffer's mutex to access DTMF * digits resources. */ pj_mutex_lock(stream->jb_mutex); stream->dtmf_cb = cb; stream->dtmf_cb_user_data = user_data; pj_mutex_unlock(stream->jb_mutex); return PJ_SUCCESS; } /* * Send RTCP SDES. */ PJ_DEF(pj_status_t) pjmedia_stream_send_rtcp_sdes( pjmedia_stream *stream ) { PJ_ASSERT_RETURN(stream, PJ_EINVAL); return send_rtcp(stream, PJ_TRUE, PJ_FALSE, PJ_FALSE); } /* * Send RTCP BYE. */ PJ_DEF(pj_status_t) pjmedia_stream_send_rtcp_bye( pjmedia_stream *stream ) { PJ_ASSERT_RETURN(stream, PJ_EINVAL); if (stream->enc && stream->transport) { return send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_FALSE); } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/stream_common.c ================================================ /* $Id: stream_common.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #define THIS_FILE "stream_common.c" /* * Parse fmtp for specified format/payload type. */ PJ_DEF(pj_status_t) pjmedia_stream_info_parse_fmtp( pj_pool_t *pool, const pjmedia_sdp_media *m, unsigned pt, pjmedia_codec_fmtp *fmtp) { const pjmedia_sdp_attr *attr; pjmedia_sdp_fmtp sdp_fmtp; char *p, *p_end, fmt_buf[8]; pj_str_t fmt; pj_status_t status; pj_assert(m && fmtp); pj_bzero(fmtp, sizeof(pjmedia_codec_fmtp)); /* Get "fmtp" attribute for the format */ pj_ansi_sprintf(fmt_buf, "%d", pt); fmt = pj_str(fmt_buf); attr = pjmedia_sdp_media_find_attr2(m, "fmtp", &fmt); if (attr == NULL) return PJ_SUCCESS; /* Parse "fmtp" attribute */ status = pjmedia_sdp_attr_get_fmtp(attr, &sdp_fmtp); if (status != PJ_SUCCESS) return status; /* Prepare parsing */ p = sdp_fmtp.fmt_param.ptr; p_end = p + sdp_fmtp.fmt_param.slen; /* Parse */ while (p < p_end) { char *token, *start, *end; if (fmtp->cnt >= PJMEDIA_CODEC_MAX_FMTP_CNT) { PJ_LOG(4,(THIS_FILE, "Warning: fmtp parameter count exceeds " "PJMEDIA_CODEC_MAX_FMTP_CNT")); return PJ_SUCCESS; } /* Skip whitespaces */ while (p < p_end && (*p == ' ' || *p == '\t')) ++p; if (p == p_end) break; /* Get token */ start = p; while (p < p_end && *p != ';' && *p != '=') ++p; end = p - 1; /* Right trim */ while (end >= start && (*end == ' ' || *end == '\t' || *end == '\r' || *end == '\n' )) --end; /* Forward a char after trimming */ ++end; /* Store token */ if (end > start) { if (pool) { token = (char*)pj_pool_alloc(pool, end - start); pj_ansi_strncpy(token, start, end - start); } else { token = start; } if (*p == '=') /* Got param name */ pj_strset(&fmtp->param[fmtp->cnt].name, token, end - start); else /* Got param value */ pj_strset(&fmtp->param[fmtp->cnt++].val, token, end - start); } else if (*p != '=') { ++fmtp->cnt; } /* Next */ ++p; } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/stream_info.c ================================================ /* $Id: stream_info.c 3982 2012-03-22 09:56:52Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include static const pj_str_t ID_AUDIO = { "audio", 5}; static const pj_str_t ID_IN = { "IN", 2 }; static const pj_str_t ID_IP4 = { "IP4", 3}; static const pj_str_t ID_IP6 = { "IP6", 3}; static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 }; //static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 }; static const pj_str_t ID_RTPMAP = { "rtpmap", 6 }; static const pj_str_t ID_TELEPHONE_EVENT = { "telephone-event", 15 }; /* * Internal function for collecting codec info and param from the SDP media. */ static pj_status_t get_audio_codec_info_param(pjmedia_stream_info *si, pj_pool_t *pool, pjmedia_codec_mgr *mgr, const pjmedia_sdp_media *local_m, const pjmedia_sdp_media *rem_m) { const pjmedia_sdp_attr *attr; pjmedia_sdp_rtpmap *rtpmap; unsigned i, fmti, pt = 0; pj_status_t status; /* Find the first codec which is not telephone-event */ for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) { pjmedia_sdp_rtpmap r; if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) ) return PJMEDIA_EINVALIDPT; pt = pj_strtoul(&local_m->desc.fmt[fmti]); if (pt < 96) { /* This is known static PT. Skip rtpmap checking because it is * optional. */ break; } attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) continue; status = pjmedia_sdp_attr_get_rtpmap(attr, &r); if (status != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) != 0) break; } if ( fmti >= local_m->desc.fmt_count ) return PJMEDIA_EINVALIDPT; /* Get payload type for receiving direction */ si->rx_pt = pt; /* Get codec info. * For static payload types, get the info from codec manager. * For dynamic payload types, MUST get the rtpmap. */ if (pt < 96) { pj_bool_t has_rtpmap; rtpmap = NULL; has_rtpmap = PJ_TRUE; attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) { has_rtpmap = PJ_FALSE; } if (attr != NULL) { status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); if (status != PJ_SUCCESS) has_rtpmap = PJ_FALSE; } /* Build codec format info: */ if (has_rtpmap) { si->fmt.type = si->type; si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); si->fmt.clock_rate = rtpmap->clock_rate; #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0) /* The session info should have the actual clock rate, because * this info is used for calculationg buffer size, etc in stream */ if (si->fmt.pt == PJMEDIA_RTP_PT_G722) si->fmt.clock_rate = 16000; #endif /* For audio codecs, rtpmap parameters denotes the number of * channels. */ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param); } else { si->fmt.channel_cnt = 1; } } else { const pjmedia_codec_info *p_info; status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info); if (status != PJ_SUCCESS) return status; pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info)); } /* For static payload type, pt's are symetric */ si->tx_pt = pt; } else { pjmedia_codec_id codec_id; pj_str_t codec_id_st; const pjmedia_codec_info *p_info; attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[fmti]); if (attr == NULL) return PJMEDIA_EMISSINGRTPMAP; status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); if (status != PJ_SUCCESS) return status; /* Build codec format info: */ si->fmt.type = si->type; si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); si->fmt.encoding_name = rtpmap->enc_name; si->fmt.clock_rate = rtpmap->clock_rate; /* For audio codecs, rtpmap parameters denotes the number of * channels. */ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param); } else { si->fmt.channel_cnt = 1; } /* Normalize the codec info from codec manager. Note that the * payload type will be resetted to its default (it might have * been rewritten by the SDP negotiator to match to the remote * offer), this is intentional as currently some components may * prefer (or even require) the default PT in codec info. */ pjmedia_codec_info_to_id(&si->fmt, codec_id, sizeof(codec_id)); i = 1; codec_id_st = pj_str(codec_id); status = pjmedia_codec_mgr_find_codecs_by_id(mgr, &codec_id_st, &i, &p_info, NULL); if (status != PJ_SUCCESS) return status; pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info)); /* Determine payload type for outgoing channel, by finding * dynamic payload type in remote SDP that matches the answer. */ si->tx_pt = 0xFFFF; for (i=0; idesc.fmt_count; ++i) { if (pjmedia_sdp_neg_fmt_match(pool, (pjmedia_sdp_media*)local_m, fmti, (pjmedia_sdp_media*)rem_m, i, 0) == PJ_SUCCESS) { /* Found matched codec. */ si->tx_pt = pj_strtoul(&rem_m->desc.fmt[i]); break; } } if (si->tx_pt == 0xFFFF) return PJMEDIA_EMISSINGRTPMAP; } /* Now that we have codec info, get the codec param. */ si->param = PJ_POOL_ALLOC_T(pool, pjmedia_codec_param); status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt, si->param); /* Get remote fmtp for our encoder. */ pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt, &si->param->setting.enc_fmtp); /* Get local fmtp for our decoder. */ pjmedia_stream_info_parse_fmtp(pool, local_m, si->rx_pt, &si->param->setting.dec_fmtp); /* Get the remote ptime for our encoder. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "ptime", NULL); if (attr) { pj_str_t tmp_val = attr->value; unsigned frm_per_pkt; pj_strltrim(&tmp_val); /* Round up ptime when the specified is not multiple of frm_ptime */ frm_per_pkt = (pj_strtoul(&tmp_val) + si->param->info.frm_ptime/2) / si->param->info.frm_ptime; if (frm_per_pkt != 0) { si->param->setting.frm_per_pkt = (pj_uint8_t)frm_per_pkt; } } /* Get remote maxptime for our encoder. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "maxptime", NULL); if (attr) { pj_str_t tmp_val = attr->value; pj_strltrim(&tmp_val); si->tx_maxptime = pj_strtoul(&tmp_val); } /* When direction is NONE (it means SDP negotiation has failed) we don't * need to return a failure here, as returning failure will cause * the whole SDP to be rejected. See ticket #: * http:// * * Thanks Alain Totouom */ if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE) return status; /* Get incomming payload type for telephone-events */ si->rx_event_pt = -1; for (i=0; iattr_count; ++i) { pjmedia_sdp_rtpmap r; attr = local_m->attr[i]; if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) continue; if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { si->rx_event_pt = pj_strtoul(&r.pt); break; } } /* Get outgoing payload type for telephone-events */ si->tx_event_pt = -1; for (i=0; iattr_count; ++i) { pjmedia_sdp_rtpmap r; attr = rem_m->attr[i]; if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) continue; if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) continue; if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { si->tx_event_pt = pj_strtoul(&r.pt); break; } } return PJ_SUCCESS; } /* * Create stream info from SDP media line. */ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( pjmedia_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote, unsigned stream_idx) { const pj_str_t STR_INACTIVE = { "inactive", 8 }; const pj_str_t STR_SENDONLY = { "sendonly", 8 }; const pj_str_t STR_RECVONLY = { "recvonly", 8 }; pjmedia_codec_mgr *mgr; const pjmedia_sdp_attr *attr; const pjmedia_sdp_media *local_m; const pjmedia_sdp_media *rem_m; const pjmedia_sdp_conn *local_conn; const pjmedia_sdp_conn *rem_conn; int rem_af, local_af; pj_sockaddr local_addr; pj_status_t status; /* Validate arguments: */ PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); /* Keep SDP shortcuts */ local_m = local->media[stream_idx]; rem_m = remote->media[stream_idx]; local_conn = local_m->conn ? local_m->conn : local->conn; if (local_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; rem_conn = rem_m->conn ? rem_m->conn : remote->conn; if (rem_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; /* Media type must be audio */ if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) != 0) return PJMEDIA_EINVALIMEDIATYPE; /* Get codec manager. */ mgr = pjmedia_endpt_get_codec_mgr(endpt); /* Reset: */ pj_bzero(si, sizeof(*si)); #if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR /* Set default RTCP XR enabled/disabled */ si->rtcp_xr_enabled = PJ_TRUE; #endif /* Media type: */ si->type = PJMEDIA_TYPE_AUDIO; /* Transport protocol */ /* At this point, transport type must be compatible, * the transport instance will do more validation later. */ status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, &local_m->desc.transport); if (status != PJ_SUCCESS) return PJMEDIA_SDPNEG_EINVANSTP; if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) { si->proto = PJMEDIA_TP_PROTO_RTP_AVP; } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) { si->proto = PJMEDIA_TP_PROTO_RTP_SAVP; } else { si->proto = PJMEDIA_TP_PROTO_UNKNOWN; return PJ_SUCCESS; } /* Check address family in remote SDP */ rem_af = pj_AF_UNSPEC(); if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { rem_af = pj_AF_INET(); } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { rem_af = pj_AF_INET6(); } } if (rem_af==pj_AF_UNSPEC()) { /* Unsupported address family */ return PJ_EAFNOTSUP; } /* Set remote address: */ status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, rem_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* Check address family of local info */ local_af = pj_AF_UNSPEC(); if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { local_af = pj_AF_INET(); } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { local_af = pj_AF_INET6(); } } if (local_af==pj_AF_UNSPEC()) { /* Unsupported address family */ return PJ_SUCCESS; } /* Set remote address: */ status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr, local_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* Local and remote address family must match */ if (local_af != rem_af) return PJ_EAFNOTSUP; /* Media direction: */ if (local_m->desc.port == 0 || pj_sockaddr_has_addr(&local_addr)==PJ_FALSE || pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) { /* Inactive stream. */ si->dir = PJMEDIA_DIR_NONE; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { /* Send only stream. */ si->dir = PJMEDIA_DIR_ENCODING; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { /* Recv only stream. */ si->dir = PJMEDIA_DIR_DECODING; } else { /* Send and receive stream. */ si->dir = PJMEDIA_DIR_ENCODING_DECODING; } /* No need to do anything else if stream is rejected */ if (local_m->desc.port == 0) { return PJ_SUCCESS; } /* If "rtcp" attribute is present in the SDP, set the RTCP address * from that attribute. Otherwise, calculate from RTP address. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "rtcp", NULL); if (attr) { pjmedia_sdp_rtcp_attr rtcp; status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); if (status == PJ_SUCCESS) { if (rtcp.addr.slen) { status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, (pj_uint16_t)rtcp.port); } else { pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, (pj_uint16_t)rtcp.port); pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), pj_sockaddr_get_addr(&si->rem_addr), pj_sockaddr_get_addr_len(&si->rem_addr)); } } } if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { int rtcp_port; pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); } /* Get the payload number for receive channel. */ /* Previously we used to rely on fmt[0] being the selected codec, but some UA sends telephone-event as fmt[0] and this would cause assert failure below. Thanks Chris Hamilton for this patch. // And codec must be numeric! if (!pj_isdigit(*local_m->desc.fmt[0].ptr) || !pj_isdigit(*rem_m->desc.fmt[0].ptr)) { return PJMEDIA_EINVALIDPT; } pt = pj_strtoul(&local_m->desc.fmt[0]); pj_assert(PJMEDIA_RTP_PT_TELEPHONE_EVENTS==0 || pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS); */ /* Get codec info and param */ status = get_audio_codec_info_param(si, pool, mgr, local_m, rem_m); /* Leave SSRC to random. */ si->ssrc = pj_rand(); /* Set default jitter buffer parameter. */ si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; return status; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/tonegen.c ================================================ /* $Id: tonegen.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include /* amplitude */ #define AMP PJMEDIA_TONEGEN_VOLUME #ifndef M_PI # define M_PI ((DATA)3.141592653589793238462643383279) #endif #if PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_SINE #include #define DATA double /* * This is the good old tone generator using sin(). * Speed = 1347 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4). * approx. 10.91 MIPS * * 506,535 usec/100.29 MIPS on ARM926EJ-S. */ struct gen { DATA add; DATA c; DATA vol; }; #define GEN_INIT(var,R,F,A) var.add = ((DATA)F)/R, var.c=0, var.vol=A #define GEN_SAMP(val,var) val = (short)(sin(var.c * 2 * M_PI) * \ var.vol); \ var.c += var.add #elif PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_FLOATING_POINT #include #define DATA float /* * Default floating-point based tone generation using sine wave * generation from: * http://www.musicdsp.org/showone.php?id=10. * This produces good quality tone in relatively faster time than * the normal sin() generator. * Speed = 350 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4). * approx. 2.84 MIPS * * 18,037 usec/3.57 MIPS on ARM926EJ-S. */ struct gen { DATA a, s0, s1; }; #define GEN_INIT(var,R,F,A) var.a = (DATA) (2.0 * sin(M_PI * F / R)); \ var.s0 = 0; \ var.s1 = (DATA)(0 - (int)A) #define GEN_SAMP(val,var) var.s0 = var.s0 - var.a * var.s1; \ var.s1 = var.s1 + var.a * var.s0; \ val = (short) var.s0 #elif PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_FIXED_POINT_CORDIC /* Cordic algorithm with 28 bit size, from: * http://www.dcs.gla.ac.uk/~jhw/cordic/ * Speed = 742 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4). * (PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP=7) * approx. 6.01 MIPS * * ARM926EJ-S results: * loop=7: 8,943 usec/1.77 MIPS * loop=8: 9,872 usec/1.95 MIPS * loop=10: 11,662 usec/2.31 MIPS * loop=12: 13,561 usec/2.69 MIPS */ #define CORDIC_1K 0x026DD3B6 #define CORDIC_HALF_PI 0x06487ED5 #define CORDIC_PI (CORDIC_HALF_PI * 2) #define CORDIC_MUL_BITS 26 #define CORDIC_MUL (1 << CORDIC_MUL_BITS) #define CORDIC_NTAB 28 #define CORDIC_LOOP PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP static int cordic_ctab [] = { 0x03243F6A, 0x01DAC670, 0x00FADBAF, 0x007F56EA, 0x003FEAB7, 0x001FFD55, 0x000FFFAA, 0x0007FFF5, 0x0003FFFE, 0x0001FFFF, 0x0000FFFF, 0x00007FFF, 0x00003FFF, 0x00001FFF, 0x00000FFF, 0x000007FF, 0x000003FF, 0x000001FF, 0x000000FF, 0x0000007F, 0x0000003F, 0x0000001F, 0x0000000F, 0x00000007, 0x00000003, 0x00000001, 0x00000000, 0x00000000 }; static pj_int32_t cordic(pj_int32_t theta, unsigned n) { unsigned k; int d; pj_int32_t tx; pj_int32_t x = CORDIC_1K, y = 0, z = theta; for (k=0; k=0) ? 0 : -1; #else /* Only slightly (~2.5%) faster, but not portable? */ d = z>>27; #endif tx = x - (((y>>k) ^ d) - d); y = y + (((x>>k) ^ d) - d); z = z - ((cordic_ctab[k] ^ d) - d); x = tx; } return y; } /* Note: theta must be uint32 here */ static pj_int32_t cordic_sin(pj_uint32_t theta, unsigned n) { if (theta < CORDIC_HALF_PI) return cordic(theta, n); else if (theta < CORDIC_PI) return cordic(CORDIC_HALF_PI-(theta-CORDIC_HALF_PI), n); else if (theta < CORDIC_PI + CORDIC_HALF_PI) return -cordic(theta - CORDIC_PI, n); else if (theta < 2 * CORDIC_PI) return -cordic(CORDIC_HALF_PI-(theta-3*CORDIC_HALF_PI), n); else { pj_assert(!"Invalid cordic_sin() value"); return 0; } } struct gen { unsigned add; pj_uint32_t c; unsigned vol; }; #define VOL(var,v) (((v) * var.vol) >> 15) #define GEN_INIT(var,R,F,A) gen_init(&var, R, F, A) #define GEN_SAMP(val,var) val = gen_samp(&var) static void gen_init(struct gen *var, unsigned R, unsigned F, unsigned A) { var->add = 2*CORDIC_PI/R * F; var->c = 0; var->vol = A; } PJ_INLINE(short) gen_samp(struct gen *var) { pj_int32_t val; val = cordic_sin(var->c, CORDIC_LOOP); /*val = (val * 32767) / CORDIC_MUL; *val = VOL((*var), val); */ val = ((val >> 10) * var->vol) >> 16; var->c += var->add; if (var->c > 2*CORDIC_PI) var->c -= (2 * CORDIC_PI); return (short) val; } #elif PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_FAST_FIXED_POINT /* * Fallback algorithm when floating point is disabled. * This is a very fast fixed point tone generation using sine wave * approximation from * http://www.audiomulch.com/~rossb/code/sinusoids/ * Quality wise not so good, but it's blazing fast! * Speed = 117 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4). * approx. 0.95 MIPS * * 1,449 usec/0.29 MIPS on ARM926EJ-S. */ PJ_INLINE(int) approximate_sin3(unsigned x) { unsigned s=-(int)(x>>31); x+=x; x=x>>16; x*=x^0xffff; // x=x*(2-x) x+=x; // optional return x^s; } struct gen { unsigned add; unsigned c; unsigned vol; }; #define MAXI ((unsigned)0xFFFFFFFF) #define SIN approximate_sin3 #define VOL(var,v) (((v) * var.vol) >> 15) #define GEN_INIT(var,R,F,A) var.add = MAXI/R * F, var.c=0, var.vol=A #define GEN_SAMP(val,var) val = (short) VOL(var,SIN(var.c)>>16); \ var.c += var.add #else #error "PJMEDIA_TONEGEN_ALG is not set correctly" #endif struct gen_state { struct gen tone1; struct gen tone2; pj_bool_t has_tone2; }; static void init_generate_single_tone(struct gen_state *state, unsigned clock_rate, unsigned freq, unsigned vol) { GEN_INIT(state->tone1,clock_rate,freq,vol); state->has_tone2 = PJ_FALSE; } static void generate_single_tone(struct gen_state *state, unsigned channel_count, unsigned samples, short buf[]) { short *end = buf + samples; if (channel_count==1) { while (buf < end) { GEN_SAMP(*buf++, state->tone1); } } else if (channel_count == 2) { while (buf < end) { GEN_SAMP(*buf, state->tone1); *(buf+1) = *buf; buf += 2; } } } static void init_generate_dual_tone(struct gen_state *state, unsigned clock_rate, unsigned freq1, unsigned freq2, unsigned vol) { GEN_INIT(state->tone1,clock_rate,freq1,vol); GEN_INIT(state->tone2,clock_rate,freq2,vol); state->has_tone2 = PJ_TRUE; } static void generate_dual_tone(struct gen_state *state, unsigned channel_count, unsigned samples, short buf[]) { short *end = buf + samples; if (channel_count==1) { int val, val2; while (buf < end) { GEN_SAMP(val, state->tone1); GEN_SAMP(val2, state->tone2); *buf++ = (short)((val+val2) >> 1); } } else if (channel_count == 2) { int val, val2; while (buf < end) { GEN_SAMP(val, state->tone1); GEN_SAMP(val2, state->tone2); val = (val + val2) >> 1; *buf++ = (short)val; *buf++ = (short)val; } } } static void init_generate_tone(struct gen_state *state, unsigned clock_rate, unsigned freq1, unsigned freq2, unsigned vol) { if (freq2) init_generate_dual_tone(state, clock_rate, freq1, freq2 ,vol); else init_generate_single_tone(state, clock_rate, freq1,vol); } static void generate_tone(struct gen_state *state, unsigned channel_count, unsigned samples, short buf[]) { if (!state->has_tone2) generate_single_tone(state, channel_count, samples, buf); else generate_dual_tone(state, channel_count, samples, buf); } /****************************************************************************/ #define SIGNATURE PJMEDIA_SIG_PORT_TONEGEN #define THIS_FILE "tonegen.c" #if 0 # define TRACE_(expr) PJ_LOG(4,expr) #else # define TRACE_(expr) #endif enum flags { PJMEDIA_TONE_INITIALIZED = 1, PJMEDIA_TONE_ENABLE_FADE = 2 }; struct tonegen { pjmedia_port base; /* options */ unsigned options; unsigned playback_options; unsigned fade_in_len; /* fade in for this # of samples */ unsigned fade_out_len; /* fade out for this # of samples*/ /* lock */ pj_lock_t *lock; /* Digit map */ pjmedia_tone_digit_map *digit_map; /* Tone generation state */ struct gen_state state; /* Currently played digits: */ unsigned count; /* # of digits */ unsigned cur_digit; /* currently played */ unsigned dig_samples; /* sample pos in cur digit */ pjmedia_tone_desc digits[PJMEDIA_TONEGEN_MAX_DIGITS];/* array of digits*/ }; /* Default digit map is DTMF */ static pjmedia_tone_digit_map digit_map = { 16, { { '0', 941, 1336 }, { '1', 697, 1209 }, { '2', 697, 1336 }, { '3', 697, 1477 }, { '4', 770, 1209 }, { '5', 770, 1336 }, { '6', 770, 1477 }, { '7', 852, 1209 }, { '8', 852, 1336 }, { '9', 852, 1477 }, { 'a', 697, 1633 }, { 'b', 770, 1633 }, { 'c', 852, 1633 }, { 'd', 941, 1633 }, { '*', 941, 1209 }, { '#', 941, 1477 }, } }; static pj_status_t tonegen_get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t tonegen_destroy(pjmedia_port *this_port); /* * Create an instance of tone generator with the specified parameters. * When the tone generator is first created, it will be loaded with the * default digit map. */ PJ_DEF(pj_status_t) pjmedia_tonegen_create2(pj_pool_t *pool, const pj_str_t *name, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_port **p_port) { const pj_str_t STR_TONE_GEN = pj_str("tonegen"); struct tonegen *tonegen; pj_status_t status; PJ_ASSERT_RETURN(pool && clock_rate && channel_count && samples_per_frame && bits_per_sample == 16 && p_port != NULL, PJ_EINVAL); /* Only support mono and stereo */ PJ_ASSERT_RETURN(channel_count==1 || channel_count==2, PJ_EINVAL); /* Create and initialize port */ tonegen = PJ_POOL_ZALLOC_T(pool, struct tonegen); if (name == NULL || name->slen == 0) name = &STR_TONE_GEN; status = pjmedia_port_info_init(&tonegen->base.info, name, SIGNATURE, clock_rate, channel_count, bits_per_sample, samples_per_frame); if (status != PJ_SUCCESS) return status; tonegen->options = options; tonegen->base.get_frame = &tonegen_get_frame; tonegen->base.on_destroy = &tonegen_destroy; tonegen->digit_map = &digit_map; tonegen->fade_in_len = PJMEDIA_TONEGEN_FADE_IN_TIME * clock_rate / 1000; tonegen->fade_out_len = PJMEDIA_TONEGEN_FADE_OUT_TIME * clock_rate / 1000; /* Lock */ if (options & PJMEDIA_TONEGEN_NO_LOCK) { status = pj_lock_create_null_mutex(pool, "tonegen", &tonegen->lock); } else { status = pj_lock_create_simple_mutex(pool, "tonegen", &tonegen->lock); } if (status != PJ_SUCCESS) { return status; } TRACE_((THIS_FILE, "Tonegen created: %u/%u/%u/%u", clock_rate, channel_count, samples_per_frame, bits_per_sample)); /* Done */ *p_port = &tonegen->base; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_tonegen_create( pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_port **p_port) { return pjmedia_tonegen_create2(pool, NULL, clock_rate, channel_count, samples_per_frame, bits_per_sample, options, p_port); } /* * Check if the tone generator is still busy producing some tones. */ PJ_DEF(pj_bool_t) pjmedia_tonegen_is_busy(pjmedia_port *port) { struct tonegen *tonegen = (struct tonegen*) port; PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_TRUE); return tonegen->count != 0; } /* * Instruct the tone generator to stop current processing. */ PJ_DEF(pj_status_t) pjmedia_tonegen_stop(pjmedia_port *port) { struct tonegen *tonegen = (struct tonegen*) port; PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL); TRACE_((THIS_FILE, "tonegen_stop()")); pj_lock_acquire(tonegen->lock); tonegen->count = 0; tonegen->cur_digit = 0; tonegen->dig_samples = 0; pj_lock_release(tonegen->lock); return PJ_SUCCESS; } /* * Instruct the tone generator to stop current processing. */ PJ_DEF(pj_status_t) pjmedia_tonegen_rewind(pjmedia_port *port) { struct tonegen *tonegen = (struct tonegen*) port; PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL); TRACE_((THIS_FILE, "tonegen_rewind()")); /* Reset back to the first tone */ pj_lock_acquire(tonegen->lock); tonegen->cur_digit = 0; tonegen->dig_samples = 0; pj_lock_release(tonegen->lock); return PJ_SUCCESS; } /* * Callback to destroy tonegen */ static pj_status_t tonegen_destroy(pjmedia_port *port) { struct tonegen *tonegen = (struct tonegen*) port; PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL); TRACE_((THIS_FILE, "tonegen_destroy()")); pj_lock_acquire(tonegen->lock); pj_lock_release(tonegen->lock); pj_lock_destroy(tonegen->lock); return PJ_SUCCESS; } /* * Fill a frame with tones. */ static pj_status_t tonegen_get_frame(pjmedia_port *port, pjmedia_frame *frame) { struct tonegen *tonegen = (struct tonegen*) port; short *dst, *end; unsigned clock_rate = PJMEDIA_PIA_SRATE(&tonegen->base.info); PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL); pj_lock_acquire(tonegen->lock); if (tonegen->count == 0) { /* We don't have digits to play */ frame->type = PJMEDIA_FRAME_TYPE_NONE; goto on_return; } if (tonegen->cur_digit > tonegen->count) { /* We have played all the digits */ if ((tonegen->options|tonegen->playback_options)&PJMEDIA_TONEGEN_LOOP) { /* Reset back to the first tone */ tonegen->cur_digit = 0; tonegen->dig_samples = 0; TRACE_((THIS_FILE, "tonegen_get_frame(): rewind")); } else { tonegen->count = 0; tonegen->cur_digit = 0; frame->type = PJMEDIA_FRAME_TYPE_NONE; TRACE_((THIS_FILE, "tonegen_get_frame(): no more digit")); goto on_return; } } if (tonegen->dig_samples>=(tonegen->digits[tonegen->cur_digit].on_msec+ tonegen->digits[tonegen->cur_digit].off_msec)* clock_rate / 1000) { /* We have finished with current digit */ tonegen->cur_digit++; tonegen->dig_samples = 0; TRACE_((THIS_FILE, "tonegen_get_frame(): next digit")); } if (tonegen->cur_digit >= tonegen->count) { /* After we're finished with the last digit, we have played all * the digits */ if ((tonegen->options|tonegen->playback_options)&PJMEDIA_TONEGEN_LOOP) { /* Reset back to the first tone */ tonegen->cur_digit = 0; tonegen->dig_samples = 0; TRACE_((THIS_FILE, "tonegen_get_frame(): rewind")); } else { tonegen->count = 0; tonegen->cur_digit = 0; frame->type = PJMEDIA_FRAME_TYPE_NONE; TRACE_((THIS_FILE, "tonegen_get_frame(): no more digit")); goto on_return; } } dst = (short*) frame->buf; end = dst + PJMEDIA_PIA_SPF(&port->info); while (dst < end) { pjmedia_tone_desc *dig = &tonegen->digits[tonegen->cur_digit]; unsigned required, cnt, on_samp, off_samp; required = (unsigned)(end - dst); on_samp = dig->on_msec * clock_rate / 1000; off_samp = dig->off_msec * clock_rate / 1000; /* Init tonegen */ if (tonegen->dig_samples == 0 && (tonegen->count!=1 || !(dig->flags & PJMEDIA_TONE_INITIALIZED))) { init_generate_tone(&tonegen->state, PJMEDIA_PIA_SRATE(&port->info), dig->freq1, dig->freq2, dig->volume); dig->flags |= PJMEDIA_TONE_INITIALIZED; if (tonegen->cur_digit > 0) { /* Clear initialized flag of previous digit */ tonegen->digits[tonegen->cur_digit-1].flags &= (~PJMEDIA_TONE_INITIALIZED); } } /* Add tone signal */ if (tonegen->dig_samples < on_samp) { cnt = on_samp - tonegen->dig_samples; if (cnt > required) cnt = required; generate_tone(&tonegen->state, PJMEDIA_PIA_CCNT(&port->info), cnt, dst); dst += cnt; tonegen->dig_samples += cnt; required -= cnt; if ((dig->flags & PJMEDIA_TONE_ENABLE_FADE) && tonegen->dig_samples == cnt) { /* Fade in */ short *samp = (dst - cnt); short *samp_end; if (cnt > tonegen->fade_in_len) cnt = tonegen->fade_in_len; samp_end = samp + cnt; if (cnt) { const unsigned step = 0xFFFF / cnt; unsigned scale = 0; for (; samp < samp_end; ++samp) { (*samp) = (short)(((*samp) * scale) >> 16); scale += step; } } } else if ((dig->flags & PJMEDIA_TONE_ENABLE_FADE) && tonegen->dig_samples==on_samp) { /* Fade out */ if (cnt > tonegen->fade_out_len) cnt = tonegen->fade_out_len; if (cnt) { short *samp = (dst - cnt); const unsigned step = 0xFFFF / cnt; unsigned scale = 0xFFFF - step; for (; samp < dst; ++samp) { (*samp) = (short)(((*samp) * scale) >> 16); scale -= step; } } } if (dst == end) break; } /* Add silence signal */ cnt = off_samp + on_samp - tonegen->dig_samples; if (cnt > required) cnt = required; pjmedia_zero_samples(dst, cnt); dst += cnt; tonegen->dig_samples += cnt; /* Move to next digit if we're finished with this tone */ if (tonegen->dig_samples >= on_samp + off_samp) { tonegen->cur_digit++; tonegen->dig_samples = 0; if (tonegen->cur_digit >= tonegen->count) { /* All digits have been played */ if ((tonegen->options & PJMEDIA_TONEGEN_LOOP) || (tonegen->playback_options & PJMEDIA_TONEGEN_LOOP)) { tonegen->cur_digit = 0; } else { break; } } } } if (dst < end) pjmedia_zero_samples(dst, (unsigned)(end-dst)); frame->type = PJMEDIA_FRAME_TYPE_AUDIO; frame->size = PJMEDIA_PIA_AVG_FSZ(&port->info); TRACE_((THIS_FILE, "tonegen_get_frame(): frame created, level=%u", pjmedia_calc_avg_signal((pj_int16_t*)frame->buf, frame->size/2))); if (tonegen->cur_digit >= tonegen->count) { if ((tonegen->options|tonegen->playback_options)&PJMEDIA_TONEGEN_LOOP) { /* Reset back to the first tone */ tonegen->cur_digit = 0; tonegen->dig_samples = 0; TRACE_((THIS_FILE, "tonegen_get_frame(): rewind")); } else { tonegen->count = 0; tonegen->cur_digit = 0; TRACE_((THIS_FILE, "tonegen_get_frame(): no more digit")); } } on_return: pj_lock_release(tonegen->lock); return PJ_SUCCESS; } /* * Play tones. */ PJ_DEF(pj_status_t) pjmedia_tonegen_play( pjmedia_port *port, unsigned count, const pjmedia_tone_desc tones[], unsigned options) { struct tonegen *tonegen = (struct tonegen*) port; unsigned i; PJ_ASSERT_RETURN(port && port->info.signature == SIGNATURE && count && tones, PJ_EINVAL); /* Don't put more than available buffer */ PJ_ASSERT_RETURN(count+tonegen->count <= PJMEDIA_TONEGEN_MAX_DIGITS, PJ_ETOOMANY); pj_lock_acquire(tonegen->lock); /* Set playback options */ tonegen->playback_options = options; /* Copy digits */ pj_memcpy(tonegen->digits + tonegen->count, tones, count * sizeof(pjmedia_tone_desc)); /* Normalize volume, and check if we need to disable fading. * Disable fading if tone off time is zero. Application probably * wants to play this tone continuously (e.g. dial tone). */ for (i=0; idigits[i+tonegen->count]; if (t->volume == 0) t->volume = AMP; else if (t->volume < 0) t->volume = (short) -t->volume; /* Reset flags */ t->flags = 0; if (t->off_msec != 0) t->flags |= PJMEDIA_TONE_ENABLE_FADE; } tonegen->count += count; pj_lock_release(tonegen->lock); return PJ_SUCCESS; } /* * Play digits. */ PJ_DEF(pj_status_t) pjmedia_tonegen_play_digits( pjmedia_port *port, unsigned count, const pjmedia_tone_digit digits[], unsigned options) { struct tonegen *tonegen = (struct tonegen*) port; pjmedia_tone_desc tones[PJMEDIA_TONEGEN_MAX_DIGITS]; const pjmedia_tone_digit_map *map; unsigned i; PJ_ASSERT_RETURN(port && port->info.signature == SIGNATURE && count && digits, PJ_EINVAL); PJ_ASSERT_RETURN(count < PJMEDIA_TONEGEN_MAX_DIGITS, PJ_ETOOMANY); pj_lock_acquire(tonegen->lock); map = tonegen->digit_map; for (i=0; icount; ++j) { if (d == map->digits[j].digit) break; } if (j == map->count) { pj_lock_release(tonegen->lock); return PJMEDIA_RTP_EINDTMF; } tones[i].freq1 = map->digits[j].freq1; tones[i].freq2 = map->digits[j].freq2; tones[i].on_msec = digits[i].on_msec; tones[i].off_msec = digits[i].off_msec; tones[i].volume = digits[i].volume; } pj_lock_release(tonegen->lock); return pjmedia_tonegen_play(port, count, tones, options); } /* * Get the digit-map currently used by this tone generator. */ PJ_DEF(pj_status_t) pjmedia_tonegen_get_digit_map(pjmedia_port *port, const pjmedia_tone_digit_map **m) { struct tonegen *tonegen = (struct tonegen*) port; PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL); PJ_ASSERT_RETURN(m != NULL, PJ_EINVAL); *m = tonegen->digit_map; return PJ_SUCCESS; } /* * Set digit map to be used by the tone generator. */ PJ_DEF(pj_status_t) pjmedia_tonegen_set_digit_map(pjmedia_port *port, pjmedia_tone_digit_map *m) { struct tonegen *tonegen = (struct tonegen*) port; PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL); PJ_ASSERT_RETURN(m != NULL, PJ_EINVAL); pj_lock_acquire(tonegen->lock); tonegen->digit_map = m; pj_lock_release(tonegen->lock); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/transport_adapter_sample.c ================================================ /* $Id: transport_adapter_sample.c 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include /* Transport functions prototypes */ static pj_status_t transport_get_info (pjmedia_transport *tp, pjmedia_transport_info *info); static pj_status_t transport_attach (pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb)(void*, void*, pj_ssize_t), void (*rtcp_cb)(void*, void*, pj_ssize_t)); static void transport_detach (pjmedia_transport *tp, void *strm); static pj_status_t transport_send_rtp( pjmedia_transport *tp, const void *pkt, pj_size_t size); static pj_status_t transport_send_rtcp(pjmedia_transport *tp, const void *pkt, pj_size_t size); static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, const pj_sockaddr_t *addr, unsigned addr_len, const void *pkt, pj_size_t size); static pj_status_t transport_media_create(pjmedia_transport *tp, pj_pool_t *sdp_pool, unsigned options, const pjmedia_sdp_session *rem_sdp, unsigned media_index); static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *rem_sdp, unsigned media_index); static pj_status_t transport_media_start (pjmedia_transport *tp, pj_pool_t *pool, const pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *rem_sdp, unsigned media_index); static pj_status_t transport_media_stop(pjmedia_transport *tp); static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost); static pj_status_t transport_destroy (pjmedia_transport *tp); /* The transport operations */ static struct pjmedia_transport_op tp_adapter_op = { &transport_get_info, &transport_attach, &transport_detach, &transport_send_rtp, &transport_send_rtcp, &transport_send_rtcp2, &transport_media_create, &transport_encode_sdp, &transport_media_start, &transport_media_stop, &transport_simulate_lost, &transport_destroy }; /* The transport adapter instance */ struct tp_adapter { pjmedia_transport base; pj_bool_t del_base; pj_pool_t *pool; /* Stream information. */ void *stream_user_data; void (*stream_rtp_cb)(void *user_data, void *pkt, pj_ssize_t); void (*stream_rtcp_cb)(void *user_data, void *pkt, pj_ssize_t); /* Add your own member here.. */ pjmedia_transport *slave_tp; }; /* * Create the adapter. */ PJ_DEF(pj_status_t) pjmedia_tp_adapter_create( pjmedia_endpt *endpt, const char *name, pjmedia_transport *transport, pj_bool_t del_base, pjmedia_transport **p_tp) { pj_pool_t *pool; struct tp_adapter *adapter; if (name == NULL) name = "tpad%p"; /* Create the pool and initialize the adapter structure */ pool = pjmedia_endpt_create_pool(endpt, name, 512, 512); adapter = PJ_POOL_ZALLOC_T(pool, struct tp_adapter); adapter->pool = pool; pj_ansi_strncpy(adapter->base.name, pool->obj_name, sizeof(adapter->base.name)); adapter->base.type = (pjmedia_transport_type) (PJMEDIA_TRANSPORT_TYPE_USER + 1); adapter->base.op = &tp_adapter_op; /* Save the transport as the slave transport */ adapter->slave_tp = transport; adapter->del_base = del_base; /* Done */ *p_tp = &adapter->base; return PJ_SUCCESS; } /* * get_info() is called to get the transport addresses to be put * in SDP c= line and a=rtcp line. */ static pj_status_t transport_get_info(pjmedia_transport *tp, pjmedia_transport_info *info) { struct tp_adapter *adapter = (struct tp_adapter*)tp; /* Since we don't have our own connection here, we just pass * this function to the slave transport. */ return pjmedia_transport_get_info(adapter->slave_tp, info); } /* This is our RTP callback, that is called by the slave transport when it * receives RTP packet. */ static void transport_rtp_cb(void *user_data, void *pkt, pj_ssize_t size) { struct tp_adapter *adapter = (struct tp_adapter*)user_data; pj_assert(adapter->stream_rtp_cb != NULL); /* Call stream's callback */ adapter->stream_rtp_cb(adapter->stream_user_data, pkt, size); } /* This is our RTCP callback, that is called by the slave transport when it * receives RTCP packet. */ static void transport_rtcp_cb(void *user_data, void *pkt, pj_ssize_t size) { struct tp_adapter *adapter = (struct tp_adapter*)user_data; pj_assert(adapter->stream_rtcp_cb != NULL); /* Call stream's callback */ adapter->stream_rtcp_cb(adapter->stream_user_data, pkt, size); } /* * attach() is called by stream to register callbacks that we should * call on receipt of RTP and RTCP packets. */ static pj_status_t transport_attach(pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb)(void*, void*, pj_ssize_t), void (*rtcp_cb)(void*, void*, pj_ssize_t)) { struct tp_adapter *adapter = (struct tp_adapter*)tp; pj_status_t status; /* In this example, we will save the stream information and callbacks * to our structure, and we will register different RTP/RTCP callbacks * instead. */ pj_assert(adapter->stream_user_data == NULL); adapter->stream_user_data = user_data; adapter->stream_rtp_cb = rtp_cb; adapter->stream_rtcp_cb = rtcp_cb; status = pjmedia_transport_attach(adapter->slave_tp, adapter, rem_addr, rem_rtcp, addr_len, &transport_rtp_cb, &transport_rtcp_cb); if (status != PJ_SUCCESS) { adapter->stream_user_data = NULL; adapter->stream_rtp_cb = NULL; adapter->stream_rtcp_cb = NULL; return status; } return PJ_SUCCESS; } /* * detach() is called when the media is terminated, and the stream is * to be disconnected from us. */ static void transport_detach(pjmedia_transport *tp, void *strm) { struct tp_adapter *adapter = (struct tp_adapter*)tp; PJ_UNUSED_ARG(strm); if (adapter->stream_user_data != NULL) { pjmedia_transport_detach(adapter->slave_tp, adapter); adapter->stream_user_data = NULL; adapter->stream_rtp_cb = NULL; adapter->stream_rtcp_cb = NULL; } } /* * send_rtp() is called to send RTP packet. The "pkt" and "size" argument * contain both the RTP header and the payload. */ static pj_status_t transport_send_rtp( pjmedia_transport *tp, const void *pkt, pj_size_t size) { struct tp_adapter *adapter = (struct tp_adapter*)tp; /* You may do some processing to the RTP packet here if you want. */ /* Send the packet using the slave transport */ return pjmedia_transport_send_rtp(adapter->slave_tp, pkt, size); } /* * send_rtcp() is called to send RTCP packet. The "pkt" and "size" argument * contain the RTCP packet. */ static pj_status_t transport_send_rtcp(pjmedia_transport *tp, const void *pkt, pj_size_t size) { struct tp_adapter *adapter = (struct tp_adapter*)tp; /* You may do some processing to the RTCP packet here if you want. */ /* Send the packet using the slave transport */ return pjmedia_transport_send_rtcp(adapter->slave_tp, pkt, size); } /* * This is another variant of send_rtcp(), with the alternate destination * address in the argument. */ static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, const pj_sockaddr_t *addr, unsigned addr_len, const void *pkt, pj_size_t size) { struct tp_adapter *adapter = (struct tp_adapter*)tp; return pjmedia_transport_send_rtcp2(adapter->slave_tp, addr, addr_len, pkt, size); } /* * The media_create() is called when the transport is about to be used for * a new call. */ static pj_status_t transport_media_create(pjmedia_transport *tp, pj_pool_t *sdp_pool, unsigned options, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { struct tp_adapter *adapter = (struct tp_adapter*)tp; /* if "rem_sdp" is not NULL, it means we are UAS. You may do some * inspections on the incoming SDP to verify that the SDP is acceptable * for us. If the SDP is not acceptable, we can reject the SDP by * returning non-PJ_SUCCESS. */ if (rem_sdp) { /* Do your stuff.. */ } /* Once we're done with our initialization, pass the call to the * slave transports to let it do it's own initialization too. */ return pjmedia_transport_media_create(adapter->slave_tp, sdp_pool, options, rem_sdp, media_index); } /* * The encode_sdp() is called when we're about to send SDP to remote party, * either as SDP offer or as SDP answer. */ static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { struct tp_adapter *adapter = (struct tp_adapter*)tp; /* If "rem_sdp" is not NULL, it means we're encoding SDP answer. You may * do some more checking on the SDP's once again to make sure that * everything is okay before we send SDP. */ if (rem_sdp) { /* Do checking stuffs here.. */ } /* You may do anything to the local_sdp, e.g. adding new attributes, or * even modifying the SDP if you want. */ if (1) { /* Say we add a proprietary attribute here.. */ pjmedia_sdp_attr *my_attr; my_attr = PJ_POOL_ALLOC_T(sdp_pool, pjmedia_sdp_attr); pj_strdup2(sdp_pool, &my_attr->name, "X-adapter"); pj_strdup2(sdp_pool, &my_attr->value, "some value"); pjmedia_sdp_attr_add(&local_sdp->media[media_index]->attr_count, local_sdp->media[media_index]->attr, my_attr); } /* And then pass the call to slave transport to let it encode its * information in the SDP. You may choose to call encode_sdp() to slave * first before adding your custom attributes if you want. */ return pjmedia_transport_encode_sdp(adapter->slave_tp, sdp_pool, local_sdp, rem_sdp, media_index); } /* * The media_start() is called once both local and remote SDP have been * negotiated successfully, and the media is ready to start. Here we can start * committing our processing. */ static pj_status_t transport_media_start(pjmedia_transport *tp, pj_pool_t *pool, const pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { struct tp_adapter *adapter = (struct tp_adapter*)tp; /* Do something.. */ /* And pass the call to the slave transport */ return pjmedia_transport_media_start(adapter->slave_tp, pool, local_sdp, rem_sdp, media_index); } /* * The media_stop() is called when media has been stopped. */ static pj_status_t transport_media_stop(pjmedia_transport *tp) { struct tp_adapter *adapter = (struct tp_adapter*)tp; /* Do something.. */ /* And pass the call to the slave transport */ return pjmedia_transport_media_stop(adapter->slave_tp); } /* * simulate_lost() is called to simulate packet lost */ static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost) { struct tp_adapter *adapter = (struct tp_adapter*)tp; return pjmedia_transport_simulate_lost(adapter->slave_tp, dir, pct_lost); } /* * destroy() is called when the transport is no longer needed. */ static pj_status_t transport_destroy (pjmedia_transport *tp) { struct tp_adapter *adapter = (struct tp_adapter*)tp; /* Close the slave transport */ if (adapter->del_base) { pjmedia_transport_close(adapter->slave_tp); } /* Self destruct.. */ pj_pool_release(adapter->pool); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/transport_ice.c ================================================ /* $Id: transport_ice.c 4350 2013-02-15 03:57:31Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #define THIS_FILE "transport_ice.c" #if 0 # define TRACE__(expr) PJ_LOG(5,expr) #else # define TRACE__(expr) #endif enum oa_role { ROLE_NONE, ROLE_OFFERER, ROLE_ANSWERER }; struct sdp_state { unsigned match_comp_cnt; /* Matching number of components */ pj_bool_t ice_mismatch; /* Address doesn't match candidates */ pj_bool_t ice_restart; /* Offer to restart ICE */ pj_ice_sess_role local_role; /* Our role */ }; struct transport_ice { pjmedia_transport base; pj_pool_t *pool; int af; unsigned options; /**< Transport options. */ unsigned comp_cnt; pj_ice_strans *ice_st; pjmedia_ice_cb cb; unsigned media_option; pj_bool_t initial_sdp; enum oa_role oa_role; /**< Last role in SDP offer/answer */ struct sdp_state rem_offer_state;/**< Describes the remote offer */ void *stream; pj_sockaddr remote_rtp; pj_sockaddr remote_rtcp; unsigned addr_len; /**< Length of addresses. */ pj_bool_t use_ice; pj_sockaddr rtp_src_addr; /**< Actual source RTP address. */ pj_sockaddr rtcp_src_addr; /**< Actual source RTCP address. */ unsigned rtp_src_cnt; /**< How many pkt from this addr. */ unsigned rtcp_src_cnt; /**< How many pkt from this addr. */ unsigned tx_drop_pct; /**< Percent of tx pkts to drop. */ unsigned rx_drop_pct; /**< Percent of rx pkts to drop. */ void (*rtp_cb)(void*, void*, pj_ssize_t); void (*rtcp_cb)(void*, void*, pj_ssize_t); }; /* * These are media transport operations. */ static pj_status_t transport_get_info (pjmedia_transport *tp, pjmedia_transport_info *info); static pj_status_t transport_attach (pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb)(void*, void*, pj_ssize_t), void (*rtcp_cb)(void*, void*, pj_ssize_t)); static void transport_detach (pjmedia_transport *tp, void *strm); static pj_status_t transport_send_rtp( pjmedia_transport *tp, const void *pkt, pj_size_t size); static pj_status_t transport_send_rtcp(pjmedia_transport *tp, const void *pkt, pj_size_t size); static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, const pj_sockaddr_t *addr, unsigned addr_len, const void *pkt, pj_size_t size); static pj_status_t transport_media_create(pjmedia_transport *tp, pj_pool_t *pool, unsigned options, const pjmedia_sdp_session *rem_sdp, unsigned media_index); static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *tmp_pool, pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *rem_sdp, unsigned media_index); static pj_status_t transport_media_start(pjmedia_transport *tp, pj_pool_t *pool, const pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *rem_sdp, unsigned media_index); static pj_status_t transport_media_stop(pjmedia_transport *tp); static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost); static pj_status_t transport_destroy (pjmedia_transport *tp); /* * And these are ICE callbacks. */ static void ice_on_rx_data(pj_ice_strans *ice_st, unsigned comp_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len); static void ice_on_ice_complete(pj_ice_strans *ice_st, pj_ice_strans_op op, pj_status_t status); static void ice_on_ice_state(pj_ice_strans *ice_st, pj_ice_strans_state prev, pj_ice_strans_state curr); static pjmedia_transport_op transport_ice_op = { &transport_get_info, &transport_attach, &transport_detach, &transport_send_rtp, &transport_send_rtcp, &transport_send_rtcp2, &transport_media_create, &transport_encode_sdp, &transport_media_start, &transport_media_stop, &transport_simulate_lost, &transport_destroy }; static const pj_str_t STR_RTP_AVP = { "RTP/AVP", 7 }; static const pj_str_t STR_CANDIDATE = { "candidate", 9}; static const pj_str_t STR_REM_CAND = { "remote-candidates", 17 }; static const pj_str_t STR_ICE_LITE = { "ice-lite", 8}; static const pj_str_t STR_ICE_MISMATCH = { "ice-mismatch", 12}; static const pj_str_t STR_ICE_UFRAG = { "ice-ufrag", 9 }; static const pj_str_t STR_ICE_PWD = { "ice-pwd", 7 }; static const pj_str_t STR_IP4 = { "IP4", 3 }; static const pj_str_t STR_IP6 = { "IP6", 3 }; static const pj_str_t STR_RTCP = { "rtcp", 4 }; static const pj_str_t STR_BANDW_RR = { "RR", 2 }; static const pj_str_t STR_BANDW_RS = { "RS", 2 }; enum { COMP_RTP = 1, COMP_RTCP = 2 }; /* * Create ICE media transport. */ PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt, const char *name, unsigned comp_cnt, const pj_ice_strans_cfg *cfg, const pjmedia_ice_cb *cb, pjmedia_transport **p_tp) { return pjmedia_ice_create2(endpt, name, comp_cnt, cfg, cb, 0, p_tp); } /* * Create ICE media transport. */ PJ_DEF(pj_status_t) pjmedia_ice_create2(pjmedia_endpt *endpt, const char *name, unsigned comp_cnt, const pj_ice_strans_cfg *cfg, const pjmedia_ice_cb *cb, unsigned options, pjmedia_transport **p_tp) { return pjmedia_ice_create3(endpt, name, comp_cnt, cfg, cb, options, NULL, p_tp); } /* * Create ICE media transport. */ PJ_DEF(pj_status_t) pjmedia_ice_create3(pjmedia_endpt *endpt, const char *name, unsigned comp_cnt, const pj_ice_strans_cfg *cfg, const pjmedia_ice_cb *cb, unsigned options, void *user_data, pjmedia_transport **p_tp) { pj_pool_t *pool; pj_ice_strans_cb ice_st_cb; pj_ice_strans_cfg ice_st_cfg; struct transport_ice *tp_ice; pj_status_t status; PJ_ASSERT_RETURN(endpt && comp_cnt && cfg && p_tp, PJ_EINVAL); /* Create transport instance */ pool = pjmedia_endpt_create_pool(endpt, name, 512, 512); tp_ice = PJ_POOL_ZALLOC_T(pool, struct transport_ice); tp_ice->pool = pool; tp_ice->af = cfg->af; tp_ice->options = options; tp_ice->comp_cnt = comp_cnt; pj_ansi_strcpy(tp_ice->base.name, pool->obj_name); tp_ice->base.op = &transport_ice_op; tp_ice->base.type = PJMEDIA_TRANSPORT_TYPE_ICE; tp_ice->base.user_data = user_data; tp_ice->initial_sdp = PJ_TRUE; tp_ice->oa_role = ROLE_NONE; tp_ice->use_ice = PJ_FALSE; pj_memcpy(&ice_st_cfg, cfg, sizeof(pj_ice_strans_cfg)); if (cb) pj_memcpy(&tp_ice->cb, cb, sizeof(pjmedia_ice_cb)); /* Assign return value first because ICE might call callback * in create() */ *p_tp = &tp_ice->base; /* Configure ICE callbacks */ pj_bzero(&ice_st_cb, sizeof(ice_st_cb)); ice_st_cb.on_ice_complete = &ice_on_ice_complete; ice_st_cb.on_ice_state = &ice_on_ice_state; ice_st_cb.on_rx_data = &ice_on_rx_data; /* Configure RTP socket buffer settings, if not set */ if (ice_st_cfg.comp[COMP_RTP-1].so_rcvbuf_size == 0) { ice_st_cfg.comp[COMP_RTP-1].so_rcvbuf_size = PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE; } if (ice_st_cfg.comp[COMP_RTP-1].so_sndbuf_size == 0) { ice_st_cfg.comp[COMP_RTP-1].so_sndbuf_size = PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE; } /* Create ICE */ status = pj_ice_strans_create(name, &ice_st_cfg, comp_cnt, tp_ice, &ice_st_cb, &tp_ice->ice_st); if (status != PJ_SUCCESS) { pj_pool_release(pool); *p_tp = NULL; return status; } /* Done */ return PJ_SUCCESS; } /* * Get the ICE stream transport associated with this media transport. */ PJ_DEF(pj_ice_strans*) pjmedia_ice_get_strans(pjmedia_transport *tp) { struct transport_ice *tp_ice; tp_ice = (struct transport_ice*) tp; return tp_ice->ice_st; } PJ_DEF(pj_grp_lock_t *) pjmedia_ice_get_grp_lock(pjmedia_transport *tp) { PJ_ASSERT_RETURN(tp, NULL); return pj_ice_strans_get_grp_lock(((struct transport_ice *)tp)->ice_st); } /* Disable ICE when SDP from remote doesn't contain a=candidate line */ static void set_no_ice(struct transport_ice *tp_ice, const char *reason, pj_status_t err) { if (err != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(err, errmsg, sizeof(errmsg)); PJ_LOG(4,(tp_ice->base.name, "Stopping ICE, reason=%s:%s", reason, errmsg)); } else { PJ_LOG(4,(tp_ice->base.name, "Stopping ICE, reason=%s", reason)); } /* Notify application about ICE stop */ if (tp_ice->cb.on_ice_stop) (*tp_ice->cb.on_ice_stop)(&tp_ice->base, (char *)reason, err); if (tp_ice->ice_st) { pj_ice_strans_stop_ice(tp_ice->ice_st); } tp_ice->use_ice = PJ_FALSE; } /* Create SDP candidate attribute */ static int print_sdp_cand_attr(char *buffer, int max_len, const pj_ice_sess_cand *cand) { char ipaddr[PJ_INET6_ADDRSTRLEN+2]; int len, len2; len = pj_ansi_snprintf( buffer, max_len, "%.*s %u UDP %u %s %u typ ", (int)cand->foundation.slen, cand->foundation.ptr, (unsigned)cand->comp_id, cand->prio, pj_sockaddr_print(&cand->addr, ipaddr, sizeof(ipaddr), 0), (unsigned)pj_sockaddr_get_port(&cand->addr)); if (len < 1 || len >= max_len) return -1; switch (cand->type) { case PJ_ICE_CAND_TYPE_HOST: len2 = pj_ansi_snprintf(buffer+len, max_len-len, "host"); break; case PJ_ICE_CAND_TYPE_SRFLX: case PJ_ICE_CAND_TYPE_RELAYED: case PJ_ICE_CAND_TYPE_PRFLX: len2 = pj_ansi_snprintf(buffer+len, max_len-len, "%s raddr %s rport %d", pj_ice_get_cand_type_name(cand->type), pj_sockaddr_print(&cand->rel_addr, ipaddr, sizeof(ipaddr), 0), (int)pj_sockaddr_get_port(&cand->rel_addr)); break; default: pj_assert(!"Invalid candidate type"); len2 = -1; break; } if (len2 < 1 || len2 >= max_len-len) return -1; return len+len2; } /* Get ice-ufrag and ice-pwd attribute */ static void get_ice_attr(const pjmedia_sdp_session *rem_sdp, const pjmedia_sdp_media *rem_m, const pjmedia_sdp_attr **p_ice_ufrag, const pjmedia_sdp_attr **p_ice_pwd) { pjmedia_sdp_attr *attr; /* Find ice-ufrag attribute in media descriptor */ attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, &STR_ICE_UFRAG, NULL); if (attr == NULL) { /* Find ice-ufrag attribute in session descriptor */ attr = pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr, &STR_ICE_UFRAG, NULL); } *p_ice_ufrag = attr; /* Find ice-pwd attribute in media descriptor */ attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, &STR_ICE_PWD, NULL); if (attr == NULL) { /* Find ice-pwd attribute in session descriptor */ attr = pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr, &STR_ICE_PWD, NULL); } *p_ice_pwd = attr; } /* Encode and add "a=ice-mismatch" attribute in the SDP */ static void encode_ice_mismatch(pj_pool_t *sdp_pool, pjmedia_sdp_session *sdp_local, unsigned media_index) { pjmedia_sdp_attr *attr; pjmedia_sdp_media *m = sdp_local->media[media_index]; attr = PJ_POOL_ALLOC_T(sdp_pool, pjmedia_sdp_attr); attr->name = STR_ICE_MISMATCH; attr->value.slen = 0; pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); } /* Encode ICE information in SDP */ static pj_status_t encode_session_in_sdp(struct transport_ice *tp_ice, pj_pool_t *sdp_pool, pjmedia_sdp_session *sdp_local, unsigned media_index, unsigned comp_cnt, pj_bool_t restart_session) { enum { ATTR_BUF_LEN = 160, /* Max len of a=candidate attr */ RATTR_BUF_LEN= 160 /* Max len of a=remote-candidates attr */ }; pjmedia_sdp_media *m = sdp_local->media[media_index]; pj_str_t local_ufrag, local_pwd; pjmedia_sdp_attr *attr; pj_status_t status; /* Must have a session */ PJ_ASSERT_RETURN(pj_ice_strans_has_sess(tp_ice->ice_st), PJ_EBUG); /* Get ufrag and pwd from current session */ pj_ice_strans_get_ufrag_pwd(tp_ice->ice_st, &local_ufrag, &local_pwd, NULL, NULL); /* The listing of candidates depends on whether ICE has completed * or not. When ICE has completed: * * 9.1.2.2: Existing Media Streams with ICE Completed * The agent MUST include a candidate attributes for candidates * matching the default destination for each component of the * media stream, and MUST NOT include any other candidates. * * When ICE has not completed, we shall include all candidates. * * Except when we have detected that remote is offering to restart * the session, in this case we will answer with full ICE SDP and * new ufrag/pwd pair. */ if (!restart_session && pj_ice_strans_sess_is_complete(tp_ice->ice_st) && pj_ice_strans_get_state(tp_ice->ice_st) != PJ_ICE_STRANS_STATE_FAILED) { const pj_ice_sess_check *check; char *attr_buf; pjmedia_sdp_conn *conn; pjmedia_sdp_attr *a_rtcp; pj_str_t rem_cand; unsigned comp; /* Encode ice-ufrag attribute */ attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr, &local_ufrag); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); /* Encode ice-pwd attribute */ attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr, &local_pwd); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); /* Prepare buffer */ attr_buf = (char*) pj_pool_alloc(sdp_pool, ATTR_BUF_LEN); rem_cand.ptr = (char*) pj_pool_alloc(sdp_pool, RATTR_BUF_LEN); rem_cand.slen = 0; /* 9.1.2.2: Existing Media Streams with ICE Completed * The default destination for media (i.e., the values of * the IP addresses and ports in the m and c line used for * that media stream) MUST be the local candidate from the * highest priority nominated pair in the valid list for each * component. */ check = pj_ice_strans_get_valid_pair(tp_ice->ice_st, 1); if (check == NULL) { pj_assert(!"Shouldn't happen"); return PJ_EBUG; } /* Override connection line address and media port number */ conn = m->conn; if (conn == NULL) conn = sdp_local->conn; conn->addr.ptr = (char*) pj_pool_alloc(sdp_pool, PJ_INET6_ADDRSTRLEN); pj_sockaddr_print(&check->lcand->addr, conn->addr.ptr, PJ_INET6_ADDRSTRLEN, 0); conn->addr.slen = pj_ansi_strlen(conn->addr.ptr); m->desc.port = pj_sockaddr_get_port(&check->lcand->addr); /* Override address RTCP attribute if it's present */ if (comp_cnt == 2 && (check = pj_ice_strans_get_valid_pair(tp_ice->ice_st, COMP_RTCP)) != NULL && (a_rtcp = pjmedia_sdp_attr_find(m->attr_count, m->attr, &STR_RTCP, 0)) != NULL) { pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a_rtcp); a_rtcp = pjmedia_sdp_attr_create_rtcp(sdp_pool, &check->lcand->addr); if (a_rtcp) pjmedia_sdp_attr_add(&m->attr_count, m->attr, a_rtcp); } /* Encode only candidates matching the default destination * for each component */ for (comp=0; comp < comp_cnt; ++comp) { int len; pj_str_t value; /* Get valid pair for this component */ check = pj_ice_strans_get_valid_pair(tp_ice->ice_st, comp+1); if (check == NULL) continue; /* Print and add local candidate in the pair */ value.ptr = attr_buf; value.slen = print_sdp_cand_attr(attr_buf, ATTR_BUF_LEN, check->lcand); if (value.slen < 0) { pj_assert(!"Not enough attr_buf to print candidate"); return PJ_EBUG; } attr = pjmedia_sdp_attr_create(sdp_pool, STR_CANDIDATE.ptr, &value); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); /* Append to a=remote-candidates attribute */ if (pj_ice_strans_get_role(tp_ice->ice_st) == PJ_ICE_SESS_ROLE_CONTROLLING) { char rem_addr[PJ_INET6_ADDRSTRLEN]; pj_sockaddr_print(&check->rcand->addr, rem_addr, sizeof(rem_addr), 0); len = pj_ansi_snprintf( rem_cand.ptr + rem_cand.slen, RATTR_BUF_LEN - rem_cand.slen, "%s%u %s %u", (rem_cand.slen==0? "" : " "), comp+1, rem_addr, pj_sockaddr_get_port(&check->rcand->addr) ); if (len < 1 || len >= RATTR_BUF_LEN - rem_cand.slen) { pj_assert(!"Not enough buffer to print " "remote-candidates"); return PJ_EBUG; } rem_cand.slen += len; } } /* 9.1.2.2: Existing Media Streams with ICE Completed * In addition, if the agent is controlling, it MUST include * the a=remote-candidates attribute for each media stream * whose check list is in the Completed state. The attribute * contains the remote candidates from the highest priority * nominated pair in the valid list for each component of that * media stream. */ if (pj_ice_strans_get_role(tp_ice->ice_st) == PJ_ICE_SESS_ROLE_CONTROLLING) { attr = pjmedia_sdp_attr_create(sdp_pool, STR_REM_CAND.ptr, &rem_cand); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); } } else if (pj_ice_strans_has_sess(tp_ice->ice_st) && (restart_session || pj_ice_strans_get_state(tp_ice->ice_st) != PJ_ICE_STRANS_STATE_FAILED)) { /* Encode all candidates to SDP media */ char *attr_buf; unsigned comp; /* If ICE is not restarted, encode current ICE ufrag/pwd. * Otherwise generate new one. */ if (!restart_session) { attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr, &local_ufrag); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr, &local_pwd); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); } else { pj_str_t str; str.slen = PJ_ICE_UFRAG_LEN; str.ptr = (char*) pj_pool_alloc(sdp_pool, str.slen); pj_create_random_string(str.ptr, str.slen); attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr, &str); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); str.ptr = (char*) pj_pool_alloc(sdp_pool, str.slen); pj_create_random_string(str.ptr, str.slen); attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr, &str); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); } /* Create buffer to encode candidates as SDP attribute */ attr_buf = (char*) pj_pool_alloc(sdp_pool, ATTR_BUF_LEN); for (comp=0; comp < comp_cnt; ++comp) { unsigned cand_cnt; pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND]; unsigned i; cand_cnt = PJ_ARRAY_SIZE(cand); status = pj_ice_strans_enum_cands(tp_ice->ice_st, comp+1, &cand_cnt, cand); if (status != PJ_SUCCESS) return status; for (i=0; iattr_count, m->attr, attr); } } } else { /* ICE has failed, application should have terminated this call */ } /* Removing a=rtcp line when there is only one component. */ if (comp_cnt == 1) { attr = pjmedia_sdp_attr_find(m->attr_count, m->attr, &STR_RTCP, NULL); if (attr) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, attr); /* If RTCP is not in use, we MUST send b=RS:0 and b=RR:0. */ pj_assert(m->bandw_count + 2 <= PJ_ARRAY_SIZE(m->bandw)); if (m->bandw_count + 2 <= PJ_ARRAY_SIZE(m->bandw)) { m->bandw[m->bandw_count] = PJ_POOL_ZALLOC_T(sdp_pool, pjmedia_sdp_bandw); pj_memcpy(&m->bandw[m->bandw_count]->modifier, &STR_BANDW_RS, sizeof(pj_str_t)); m->bandw_count++; m->bandw[m->bandw_count] = PJ_POOL_ZALLOC_T(sdp_pool, pjmedia_sdp_bandw); pj_memcpy(&m->bandw[m->bandw_count]->modifier, &STR_BANDW_RR, sizeof(pj_str_t)); m->bandw_count++; } } return PJ_SUCCESS; } /* Parse a=candidate line */ static pj_status_t parse_cand(const char *obj_name, pj_pool_t *pool, const pj_str_t *orig_input, pj_ice_sess_cand *cand) { pj_str_t input; char *token, *host; int af; pj_str_t s; pj_status_t status = PJNATH_EICEINCANDSDP; pj_bzero(cand, sizeof(*cand)); pj_strdup_with_null(pool, &input, orig_input); PJ_UNUSED_ARG(obj_name); /* Foundation */ token = strtok(input.ptr, " "); if (!token) { TRACE__((obj_name, "Expecting ICE foundation in candidate")); goto on_return; } pj_strdup2(pool, &cand->foundation, token); /* Component ID */ token = strtok(NULL, " "); if (!token) { TRACE__((obj_name, "Expecting ICE component ID in candidate")); goto on_return; } cand->comp_id = (pj_uint8_t) atoi(token); /* Transport */ token = strtok(NULL, " "); if (!token) { TRACE__((obj_name, "Expecting ICE transport in candidate")); goto on_return; } if (pj_ansi_stricmp(token, "UDP") != 0) { TRACE__((obj_name, "Expecting ICE UDP transport only in candidate")); goto on_return; } /* Priority */ token = strtok(NULL, " "); if (!token) { TRACE__((obj_name, "Expecting ICE priority in candidate")); goto on_return; } cand->prio = atoi(token); /* Host */ host = strtok(NULL, " "); if (!host) { TRACE__((obj_name, "Expecting ICE host in candidate")); goto on_return; } /* Detect address family */ if (pj_ansi_strchr(host, ':')) af = pj_AF_INET6(); else af = pj_AF_INET(); /* Assign address */ if (pj_sockaddr_init(af, &cand->addr, pj_cstr(&s, host), 0)) { TRACE__((obj_name, "Invalid ICE candidate address")); goto on_return; } /* Port */ token = strtok(NULL, " "); if (!token) { TRACE__((obj_name, "Expecting ICE port number in candidate")); goto on_return; } pj_sockaddr_set_port(&cand->addr, (pj_uint16_t)atoi(token)); /* typ */ token = strtok(NULL, " "); if (!token) { TRACE__((obj_name, "Expecting ICE \"typ\" in candidate")); goto on_return; } if (pj_ansi_stricmp(token, "typ") != 0) { TRACE__((obj_name, "Expecting ICE \"typ\" in candidate")); goto on_return; } /* candidate type */ token = strtok(NULL, " "); if (!token) { TRACE__((obj_name, "Expecting ICE candidate type in candidate")); goto on_return; } if (pj_ansi_stricmp(token, "host") == 0) { cand->type = PJ_ICE_CAND_TYPE_HOST; } else if (pj_ansi_stricmp(token, "srflx") == 0) { cand->type = PJ_ICE_CAND_TYPE_SRFLX; } else if (pj_ansi_stricmp(token, "relay") == 0) { cand->type = PJ_ICE_CAND_TYPE_RELAYED; } else if (pj_ansi_stricmp(token, "prflx") == 0) { cand->type = PJ_ICE_CAND_TYPE_PRFLX; } else { PJ_LOG(5,(obj_name, "Invalid ICE candidate type %s in candidate", token)); goto on_return; } status = PJ_SUCCESS; on_return: return status; } /* Create initial SDP offer */ static pj_status_t create_initial_offer(struct transport_ice *tp_ice, pj_pool_t *sdp_pool, pjmedia_sdp_session *loc_sdp, unsigned media_index) { pj_status_t status; /* Encode ICE in SDP */ status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, tp_ice->comp_cnt, PJ_FALSE); if (status != PJ_SUCCESS) { set_no_ice(tp_ice, "Error encoding SDP answer", status); return status; } return PJ_SUCCESS; } /* Verify incoming offer */ static pj_status_t verify_ice_sdp(struct transport_ice *tp_ice, pj_pool_t *tmp_pool, const pjmedia_sdp_session *rem_sdp, unsigned media_index, pj_ice_sess_role current_ice_role, struct sdp_state *sdp_state) { const pjmedia_sdp_media *rem_m; const pjmedia_sdp_attr *ufrag_attr, *pwd_attr; const pjmedia_sdp_conn *rem_conn; pj_bool_t comp1_found=PJ_FALSE, comp2_found=PJ_FALSE, has_rtcp=PJ_FALSE; pj_sockaddr rem_conn_addr, rtcp_addr; unsigned i; pj_status_t status; rem_m = rem_sdp->media[media_index]; /* Get the "ice-ufrag" and "ice-pwd" attributes */ get_ice_attr(rem_sdp, rem_m, &ufrag_attr, &pwd_attr); /* If "ice-ufrag" or "ice-pwd" are not found, disable ICE */ if (ufrag_attr==NULL || pwd_attr==NULL) { sdp_state->match_comp_cnt = 0; return PJ_SUCCESS; } /* Verify that default target for each component matches one of the * candidate for the component. Otherwise stop ICE with ICE ice_mismatch * error. */ /* Component 1 is the c= line */ rem_conn = rem_m->conn; if (rem_conn == NULL) rem_conn = rem_sdp->conn; if (!rem_conn) return PJMEDIA_SDP_EMISSINGCONN; /* Verify address family matches */ if ((tp_ice->af==pj_AF_INET() && pj_strcmp(&rem_conn->addr_type, &STR_IP4)!=0) || (tp_ice->af==pj_AF_INET6() && pj_strcmp(&rem_conn->addr_type, &STR_IP6)!=0)) { return PJMEDIA_SDP_ETPORTNOTEQUAL; } /* Assign remote connection address */ status = pj_sockaddr_init(tp_ice->af, &rem_conn_addr, &rem_conn->addr, (pj_uint16_t)rem_m->desc.port); if (status != PJ_SUCCESS) return status; if (tp_ice->comp_cnt > 1) { const pjmedia_sdp_attr *attr; /* Get default RTCP candidate from a=rtcp line, if present, otherwise * calculate default RTCP candidate from default RTP target. */ attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, &STR_RTCP, NULL); has_rtcp = (attr != NULL); if (attr) { pjmedia_sdp_rtcp_attr rtcp_attr; status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp_attr); if (status != PJ_SUCCESS) { /* Error parsing a=rtcp attribute */ return status; } if (rtcp_attr.addr.slen) { /* Verify address family matches */ if ((tp_ice->af==pj_AF_INET() && pj_strcmp(&rtcp_attr.addr_type, &STR_IP4)!=0) || (tp_ice->af==pj_AF_INET6() && pj_strcmp(&rtcp_attr.addr_type, &STR_IP6)!=0)) { return PJMEDIA_SDP_ETPORTNOTEQUAL; } /* Assign RTCP address */ status = pj_sockaddr_init(tp_ice->af, &rtcp_addr, &rtcp_attr.addr, (pj_uint16_t)rtcp_attr.port); if (status != PJ_SUCCESS) { return PJMEDIA_SDP_EINRTCP; } } else { /* Assign RTCP address */ status = pj_sockaddr_init(tp_ice->af, &rtcp_addr, NULL, (pj_uint16_t)rtcp_attr.port); if (status != PJ_SUCCESS) { return PJMEDIA_SDP_EINRTCP; } pj_sockaddr_copy_addr(&rtcp_addr, &rem_conn_addr); } } else { unsigned rtcp_port; rtcp_port = pj_sockaddr_get_port(&rem_conn_addr) + 1; pj_sockaddr_cp(&rtcp_addr, &rem_conn_addr); pj_sockaddr_set_port(&rtcp_addr, (pj_uint16_t)rtcp_port); } } /* Find the default addresses in a=candidate attributes. */ for (i=0; iattr_count; ++i) { pj_ice_sess_cand cand; if (pj_strcmp(&rem_m->attr[i]->name, &STR_CANDIDATE)!=0) continue; status = parse_cand(tp_ice->base.name, tmp_pool, &rem_m->attr[i]->value, &cand); if (status != PJ_SUCCESS) { PJ_LOG(4,(tp_ice->base.name, "Error in parsing SDP candidate attribute '%.*s', " "candidate is ignored", (int)rem_m->attr[i]->value.slen, rem_m->attr[i]->value.ptr)); continue; } if (!comp1_found && cand.comp_id==COMP_RTP && pj_sockaddr_cmp(&rem_conn_addr, &cand.addr)==0) { comp1_found = PJ_TRUE; } else if (!comp2_found && cand.comp_id==COMP_RTCP && pj_sockaddr_cmp(&rtcp_addr, &cand.addr)==0) { comp2_found = PJ_TRUE; } if (cand.comp_id == COMP_RTCP) has_rtcp = PJ_TRUE; if (comp1_found && (comp2_found || tp_ice->comp_cnt==1)) break; } /* Check matched component count and ice_mismatch */ if (comp1_found && (tp_ice->comp_cnt==1 || !has_rtcp)) { sdp_state->match_comp_cnt = 1; sdp_state->ice_mismatch = PJ_FALSE; } else if (comp1_found && comp2_found) { sdp_state->match_comp_cnt = 2; sdp_state->ice_mismatch = PJ_FALSE; } else { sdp_state->match_comp_cnt = (tp_ice->comp_cnt > 1 && has_rtcp)? 2 : 1; sdp_state->ice_mismatch = PJ_TRUE; } /* Detect remote restarting session */ if (pj_ice_strans_has_sess(tp_ice->ice_st) && (pj_ice_strans_sess_is_running(tp_ice->ice_st) || pj_ice_strans_sess_is_complete(tp_ice->ice_st))) { pj_str_t rem_run_ufrag, rem_run_pwd; pj_ice_strans_get_ufrag_pwd(tp_ice->ice_st, NULL, NULL, &rem_run_ufrag, &rem_run_pwd); if (pj_strcmp(&ufrag_attr->value, &rem_run_ufrag) || pj_strcmp(&pwd_attr->value, &rem_run_pwd)) { /* Remote offers to restart ICE */ sdp_state->ice_restart = PJ_TRUE; } else { sdp_state->ice_restart = PJ_FALSE; } } else { sdp_state->ice_restart = PJ_FALSE; } /* Detect our role */ if (current_ice_role==PJ_ICE_SESS_ROLE_CONTROLLING) { sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLING; } else { if (pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr, &STR_ICE_LITE, NULL) != NULL) { /* Remote is ICE Lite */ sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLING; } else { sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLED; } } PJ_LOG(4,(tp_ice->base.name, "Processing SDP: support ICE=%u, common comp_cnt=%u, " "ice_mismatch=%u, ice_restart=%u, local_role=%s", (sdp_state->match_comp_cnt != 0), sdp_state->match_comp_cnt, sdp_state->ice_mismatch, sdp_state->ice_restart, pj_ice_sess_role_name(sdp_state->local_role))); return PJ_SUCCESS; } /* Verify incoming offer and create initial answer */ static pj_status_t create_initial_answer(struct transport_ice *tp_ice, pj_pool_t *sdp_pool, pjmedia_sdp_session *loc_sdp, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { const pjmedia_sdp_media *rem_m = rem_sdp->media[media_index]; pj_status_t status; /* Check if media is removed (just in case) */ if (rem_m->desc.port == 0) { return PJ_SUCCESS; } /* Verify the offer */ status = verify_ice_sdp(tp_ice, sdp_pool, rem_sdp, media_index, PJ_ICE_SESS_ROLE_CONTROLLED, &tp_ice->rem_offer_state); if (status != PJ_SUCCESS) { set_no_ice(tp_ice, "Invalid SDP offer", status); return status; } /* Does remote support ICE? */ if (tp_ice->rem_offer_state.match_comp_cnt==0) { set_no_ice(tp_ice, "No ICE found in SDP offer", PJ_SUCCESS); return PJ_SUCCESS; } /* ICE ice_mismatch? */ if (tp_ice->rem_offer_state.ice_mismatch) { set_no_ice(tp_ice, "ICE ice_mismatch in remote offer", PJ_SUCCESS); encode_ice_mismatch(sdp_pool, loc_sdp, media_index); return PJ_SUCCESS; } /* Encode ICE in SDP */ status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, tp_ice->rem_offer_state.match_comp_cnt, PJ_FALSE); if (status != PJ_SUCCESS) { set_no_ice(tp_ice, "Error encoding SDP answer", status); return status; } return PJ_SUCCESS; } /* Create subsequent SDP offer */ static pj_status_t create_subsequent_offer(struct transport_ice *tp_ice, pj_pool_t *sdp_pool, pjmedia_sdp_session *loc_sdp, unsigned media_index) { unsigned comp_cnt; if (pj_ice_strans_has_sess(tp_ice->ice_st) == PJ_FALSE) { /* We don't have ICE */ return PJ_SUCCESS; } comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st); return encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, comp_cnt, PJ_FALSE); } /* Create subsequent SDP answer */ static pj_status_t create_subsequent_answer(struct transport_ice *tp_ice, pj_pool_t *sdp_pool, pjmedia_sdp_session *loc_sdp, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { pj_status_t status; /* We have a session */ status = verify_ice_sdp(tp_ice, sdp_pool, rem_sdp, media_index, PJ_ICE_SESS_ROLE_CONTROLLED, &tp_ice->rem_offer_state); if (status != PJ_SUCCESS) { /* Something wrong with the offer */ return status; } if (pj_ice_strans_has_sess(tp_ice->ice_st)) { /* * Received subsequent offer while we have ICE active. */ if (tp_ice->rem_offer_state.match_comp_cnt == 0) { /* Remote no longer offers ICE */ return PJ_SUCCESS; } if (tp_ice->rem_offer_state.ice_mismatch) { encode_ice_mismatch(sdp_pool, loc_sdp, media_index); return PJ_SUCCESS; } status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, tp_ice->rem_offer_state.match_comp_cnt, tp_ice->rem_offer_state.ice_restart); if (status != PJ_SUCCESS) return status; /* Done */ } else { /* * Received subsequent offer while we DON'T have ICE active. */ if (tp_ice->rem_offer_state.match_comp_cnt == 0) { /* Remote does not support ICE */ return PJ_SUCCESS; } if (tp_ice->rem_offer_state.ice_mismatch) { encode_ice_mismatch(sdp_pool, loc_sdp, media_index); return PJ_SUCCESS; } /* Looks like now remote is offering ICE, so we need to create * ICE session now. */ status = pj_ice_strans_init_ice(tp_ice->ice_st, PJ_ICE_SESS_ROLE_CONTROLLED, NULL, NULL); if (status != PJ_SUCCESS) { /* Fail to create new ICE session */ return status; } status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, tp_ice->rem_offer_state.match_comp_cnt, tp_ice->rem_offer_state.ice_restart); if (status != PJ_SUCCESS) return status; /* Done */ } return PJ_SUCCESS; } /* * For both UAC and UAS, pass in the SDP before sending it to remote. * This will add ICE attributes to the SDP. */ static pj_status_t transport_media_create(pjmedia_transport *tp, pj_pool_t *sdp_pool, unsigned options, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { struct transport_ice *tp_ice = (struct transport_ice*)tp; pj_ice_sess_role ice_role; pj_status_t status; PJ_UNUSED_ARG(media_index); PJ_UNUSED_ARG(sdp_pool); tp_ice->media_option = options; tp_ice->oa_role = ROLE_NONE; tp_ice->initial_sdp = PJ_TRUE; /* Init ICE, the initial role is set now based on availability of * rem_sdp, but it will be checked again later. */ ice_role = (rem_sdp==NULL ? PJ_ICE_SESS_ROLE_CONTROLLING : PJ_ICE_SESS_ROLE_CONTROLLED); status = pj_ice_strans_init_ice(tp_ice->ice_st, ice_role, NULL, NULL); /* Done */ return status; } static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { struct transport_ice *tp_ice = (struct transport_ice*)tp; pj_status_t status; /* Validate media transport */ /* This transport only support RTP/AVP transport, unless if * transport checking is disabled */ if ((tp_ice->media_option & PJMEDIA_TPMED_NO_TRANSPORT_CHECKING) == 0) { pjmedia_sdp_media *loc_m, *rem_m; rem_m = rem_sdp? rem_sdp->media[media_index] : NULL; loc_m = sdp_local->media[media_index]; if (pj_stricmp(&loc_m->desc.transport, &STR_RTP_AVP) || (rem_m && pj_stricmp(&rem_m->desc.transport, &STR_RTP_AVP))) { pjmedia_sdp_media_deactivate(sdp_pool, loc_m); return PJMEDIA_SDP_EINPROTO; } } if (tp_ice->initial_sdp) { if (rem_sdp) { status = create_initial_answer(tp_ice, sdp_pool, sdp_local, rem_sdp, media_index); } else { status = create_initial_offer(tp_ice, sdp_pool, sdp_local, media_index); } } else { if (rem_sdp) { status = create_subsequent_answer(tp_ice, sdp_pool, sdp_local, rem_sdp, media_index); } else { status = create_subsequent_offer(tp_ice, sdp_pool, sdp_local, media_index); } } if (status==PJ_SUCCESS) { if (rem_sdp) tp_ice->oa_role = ROLE_ANSWERER; else tp_ice->oa_role = ROLE_OFFERER; } return status; } /* Start ICE session with the specified remote SDP */ static pj_status_t start_ice(struct transport_ice *tp_ice, pj_pool_t *tmp_pool, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { pjmedia_sdp_media *rem_m = rem_sdp->media[media_index]; const pjmedia_sdp_attr *ufrag_attr, *pwd_attr; pj_ice_sess_cand *cand; unsigned i, cand_cnt; pj_status_t status; get_ice_attr(rem_sdp, rem_m, &ufrag_attr, &pwd_attr); /* Allocate candidate array */ cand = (pj_ice_sess_cand*) pj_pool_calloc(tmp_pool, PJ_ICE_MAX_CAND, sizeof(pj_ice_sess_cand)); /* Get all candidates in the media */ cand_cnt = 0; for (i=0; iattr_count && cand_cnt < PJ_ICE_MAX_CAND; ++i) { pjmedia_sdp_attr *attr; attr = rem_m->attr[i]; if (pj_strcmp(&attr->name, &STR_CANDIDATE)!=0) continue; /* Parse candidate */ status = parse_cand(tp_ice->base.name, tmp_pool, &attr->value, &cand[cand_cnt]); if (status != PJ_SUCCESS) { PJ_LOG(4,(tp_ice->base.name, "Error in parsing SDP candidate attribute '%.*s', " "candidate is ignored", (int)attr->value.slen, attr->value.ptr)); continue; } cand_cnt++; } /* Start ICE */ return pj_ice_strans_start_ice(tp_ice->ice_st, &ufrag_attr->value, &pwd_attr->value, cand_cnt, cand); } /* * Start ICE checks when both offer and answer have been negotiated * by SDP negotiator. */ static pj_status_t transport_media_start(pjmedia_transport *tp, pj_pool_t *tmp_pool, const pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { struct transport_ice *tp_ice = (struct transport_ice*)tp; pjmedia_sdp_media *rem_m; enum oa_role current_oa_role; pj_bool_t initial_oa; pj_status_t status; PJ_ASSERT_RETURN(tp && tmp_pool && rem_sdp, PJ_EINVAL); PJ_ASSERT_RETURN(media_index < rem_sdp->media_count, PJ_EINVAL); rem_m = rem_sdp->media[media_index]; initial_oa = tp_ice->initial_sdp; current_oa_role = tp_ice->oa_role; /* SDP has been negotiated */ tp_ice->initial_sdp = PJ_FALSE; tp_ice->oa_role = ROLE_NONE; /* Nothing to do if we don't have ICE session */ if (pj_ice_strans_has_sess(tp_ice->ice_st) == PJ_FALSE) { return PJ_SUCCESS; } /* Special case for Session Timer. The re-INVITE for session refresh * doesn't call transport_encode_sdp(), causing current_oa_role to * be set to ROLE_NONE. This is a workaround. */ if (current_oa_role == ROLE_NONE) { current_oa_role = ROLE_OFFERER; } /* Processing depends on the offer/answer role */ if (current_oa_role == ROLE_OFFERER) { /* * We are offerer. So this will be the first time we see the * remote's SDP. */ struct sdp_state answer_state; /* Verify the answer */ status = verify_ice_sdp(tp_ice, tmp_pool, rem_sdp, media_index, PJ_ICE_SESS_ROLE_CONTROLLING, &answer_state); if (status != PJ_SUCCESS) { /* Something wrong in the SDP answer */ set_no_ice(tp_ice, "Invalid remote SDP answer", status); return status; } /* Does it have ICE? */ if (answer_state.match_comp_cnt == 0) { /* Remote doesn't support ICE */ set_no_ice(tp_ice, "Remote answer doesn't support ICE", PJ_SUCCESS); return PJ_SUCCESS; } /* Check if remote has reported ice-mismatch */ if (pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, &STR_ICE_MISMATCH, NULL) != NULL) { /* Remote has reported ice-mismatch */ set_no_ice(tp_ice, "Remote answer contains 'ice-mismatch' attribute", PJ_SUCCESS); return PJ_SUCCESS; } /* Check if remote has indicated a restart */ if (answer_state.ice_restart) { PJ_LOG(2,(tp_ice->base.name, "Warning: remote has signalled ICE restart in SDP " "answer which is disallowed. Remote ICE negotiation" " may fail.")); } /* Check if the answer itself is mismatched */ if (answer_state.ice_mismatch) { /* This happens either when a B2BUA modified remote answer but * strangely didn't modify our offer, or remote is not capable * of detecting mismatch in our offer (it didn't put * 'ice-mismatch' attribute in the answer). */ PJ_LOG(2,(tp_ice->base.name, "Warning: remote answer mismatch, but it does not " "reject our offer with 'ice-mismatch'. ICE negotiation " "may fail")); } /* Do nothing if ICE is complete or running */ if (pj_ice_strans_sess_is_running(tp_ice->ice_st)) { PJ_LOG(4,(tp_ice->base.name, "Ignored offer/answer because ICE is running")); return PJ_SUCCESS; } if (pj_ice_strans_sess_is_complete(tp_ice->ice_st)) { PJ_LOG(4,(tp_ice->base.name, "ICE session unchanged")); return PJ_SUCCESS; } /* Start ICE */ } else { /* * We are answerer. We've seen and negotiated remote's SDP * before, and the result is in "rem_offer_state". */ const pjmedia_sdp_attr *ufrag_attr, *pwd_attr; /* Check for ICE in remote offer */ if (tp_ice->rem_offer_state.match_comp_cnt == 0) { /* No ICE attribute present */ set_no_ice(tp_ice, "Remote no longer offers ICE", PJ_SUCCESS); return PJ_SUCCESS; } /* Check for ICE ice_mismatch condition in the offer */ if (tp_ice->rem_offer_state.ice_mismatch) { set_no_ice(tp_ice, "Remote offer mismatch: ", PJNATH_EICEMISMATCH); return PJ_SUCCESS; } /* If ICE is complete and remote doesn't request restart, * then leave the session as is. */ if (!initial_oa && tp_ice->rem_offer_state.ice_restart == PJ_FALSE) { /* Remote has not requested ICE restart, so session is * unchanged. */ PJ_LOG(4,(tp_ice->base.name, "ICE session unchanged")); return PJ_SUCCESS; } /* Either remote has requested ICE restart or this is our * first answer. */ /* Stop ICE */ if (!initial_oa) { set_no_ice(tp_ice, "restarting by remote request..", PJ_SUCCESS); /* We have put new ICE ufrag and pwd in the answer. Now * create a new ICE session with that ufrag/pwd pair. */ get_ice_attr(sdp_local, sdp_local->media[media_index], &ufrag_attr, &pwd_attr); status = pj_ice_strans_init_ice(tp_ice->ice_st, tp_ice->rem_offer_state.local_role, &ufrag_attr->value, &pwd_attr->value); if (status != PJ_SUCCESS) { PJ_LOG(1,(tp_ice->base.name, "ICE re-initialization failed (status=%d)!", status)); return status; } } /* Ticket #977: Update role if turns out we're supposed to be the * Controlling agent (e.g. when talking to ice-lite peer). */ if (tp_ice->rem_offer_state.local_role==PJ_ICE_SESS_ROLE_CONTROLLING && pj_ice_strans_has_sess(tp_ice->ice_st)) { pj_ice_strans_change_role(tp_ice->ice_st, PJ_ICE_SESS_ROLE_CONTROLLING); } /* start ICE */ } /* Now start ICE */ status = start_ice(tp_ice, tmp_pool, rem_sdp, media_index); if (status != PJ_SUCCESS) { PJ_LOG(1,(tp_ice->base.name, "ICE restart failed (status=%d)!", status)); return status; } /* Done */ tp_ice->use_ice = PJ_TRUE; return PJ_SUCCESS; } static pj_status_t transport_media_stop(pjmedia_transport *tp) { struct transport_ice *tp_ice = (struct transport_ice*)tp; set_no_ice(tp_ice, "media stop requested", PJ_SUCCESS); return PJ_SUCCESS; } static pj_status_t transport_get_info(pjmedia_transport *tp, pjmedia_transport_info *info) { struct transport_ice *tp_ice = (struct transport_ice*)tp; pj_ice_sess_cand cand; pj_status_t status; pj_bzero(&info->sock_info, sizeof(info->sock_info)); info->sock_info.rtp_sock = info->sock_info.rtcp_sock = PJ_INVALID_SOCKET; /* Get RTP default address */ status = pj_ice_strans_get_def_cand(tp_ice->ice_st, 1, &cand); if (status != PJ_SUCCESS) return status; pj_sockaddr_cp(&info->sock_info.rtp_addr_name, &cand.base_addr); /* Get RTCP default address */ if (tp_ice->comp_cnt > 1) { status = pj_ice_strans_get_def_cand(tp_ice->ice_st, 2, &cand); if (status != PJ_SUCCESS) return status; pj_sockaddr_cp(&info->sock_info.rtcp_addr_name, &cand.base_addr); } /* Set remote address originating RTP & RTCP if this transport has * ICE activated or received any packets. */ if (tp_ice->use_ice || tp_ice->rtp_src_cnt) { info->src_rtp_name = tp_ice->rtp_src_addr; } if (tp_ice->use_ice || tp_ice->rtcp_src_cnt) { info->src_rtcp_name = tp_ice->rtcp_src_addr; } /* Fill up transport specific info */ if (info->specific_info_cnt < PJ_ARRAY_SIZE(info->spc_info)) { pjmedia_transport_specific_info *tsi; pjmedia_ice_transport_info *ii; unsigned i; pj_assert(sizeof(*ii) <= sizeof(tsi->buffer)); tsi = &info->spc_info[info->specific_info_cnt++]; tsi->type = PJMEDIA_TRANSPORT_TYPE_ICE; tsi->cbsize = sizeof(*ii); ii = (pjmedia_ice_transport_info*) tsi->buffer; pj_bzero(ii, sizeof(*ii)); ii->active = tp_ice->use_ice; if (pj_ice_strans_has_sess(tp_ice->ice_st)) ii->role = pj_ice_strans_get_role(tp_ice->ice_st); else ii->role = PJ_ICE_SESS_ROLE_UNKNOWN; ii->sess_state = pj_ice_strans_get_state(tp_ice->ice_st); ii->comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st); for (i=1; i<=ii->comp_cnt && i<=PJ_ARRAY_SIZE(ii->comp); ++i) { const pj_ice_sess_check *chk; chk = pj_ice_strans_get_valid_pair(tp_ice->ice_st, i); if (chk) { ii->comp[i-1].lcand_type = chk->lcand->type; pj_sockaddr_cp(&ii->comp[i-1].lcand_addr, &chk->lcand->addr); ii->comp[i-1].rcand_type = chk->rcand->type; pj_sockaddr_cp(&ii->comp[i-1].rcand_addr, &chk->rcand->addr); } } } return PJ_SUCCESS; } static pj_status_t transport_attach (pjmedia_transport *tp, void *stream, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb)(void*, void*, pj_ssize_t), void (*rtcp_cb)(void*, void*, pj_ssize_t)) { struct transport_ice *tp_ice = (struct transport_ice*)tp; tp_ice->stream = stream; tp_ice->rtp_cb = rtp_cb; tp_ice->rtcp_cb = rtcp_cb; pj_memcpy(&tp_ice->remote_rtp, rem_addr, addr_len); pj_memcpy(&tp_ice->remote_rtcp, rem_rtcp, addr_len); tp_ice->addr_len = addr_len; /* Init source RTP & RTCP addresses and counter */ tp_ice->rtp_src_addr = tp_ice->remote_rtp; tp_ice->rtcp_src_addr = tp_ice->remote_rtcp; tp_ice->rtp_src_cnt = 0; tp_ice->rtcp_src_cnt = 0; return PJ_SUCCESS; } static void transport_detach(pjmedia_transport *tp, void *strm) { struct transport_ice *tp_ice = (struct transport_ice*)tp; /* TODO: need to solve ticket #460 here */ tp_ice->rtp_cb = NULL; tp_ice->rtcp_cb = NULL; tp_ice->stream = NULL; PJ_UNUSED_ARG(strm); } static pj_status_t transport_send_rtp(pjmedia_transport *tp, const void *pkt, pj_size_t size) { struct transport_ice *tp_ice = (struct transport_ice*)tp; /* Simulate packet lost on TX direction */ if (tp_ice->tx_drop_pct) { if ((pj_rand() % 100) <= (int)tp_ice->tx_drop_pct) { PJ_LOG(5,(tp_ice->base.name, "TX RTP packet dropped because of pkt lost " "simulation")); return PJ_SUCCESS; } } return pj_ice_strans_sendto(tp_ice->ice_st, 1, pkt, size, &tp_ice->remote_rtp, tp_ice->addr_len); } static pj_status_t transport_send_rtcp(pjmedia_transport *tp, const void *pkt, pj_size_t size) { return transport_send_rtcp2(tp, NULL, 0, pkt, size); } static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, const pj_sockaddr_t *addr, unsigned addr_len, const void *pkt, pj_size_t size) { struct transport_ice *tp_ice = (struct transport_ice*)tp; if (tp_ice->comp_cnt > 1) { if (addr == NULL) { addr = &tp_ice->remote_rtcp; addr_len = pj_sockaddr_get_len(addr); } return pj_ice_strans_sendto(tp_ice->ice_st, 2, pkt, size, addr, addr_len); } else { return PJ_SUCCESS; } } static void ice_on_rx_data(pj_ice_strans *ice_st, unsigned comp_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { struct transport_ice *tp_ice; pj_bool_t discard = PJ_FALSE; tp_ice = (struct transport_ice*) pj_ice_strans_get_user_data(ice_st); if (comp_id==1 && tp_ice->rtp_cb) { /* Simulate packet lost on RX direction */ if (tp_ice->rx_drop_pct) { if ((pj_rand() % 100) <= (int)tp_ice->rx_drop_pct) { PJ_LOG(5,(tp_ice->base.name, "RX RTP packet dropped because of pkt lost " "simulation")); return; } } /* See if source address of RTP packet is different than the * configured address, and switch RTP remote address to * source packet address after several consecutive packets * have been received. */ if (!tp_ice->use_ice) { pj_bool_t enable_switch = ((tp_ice->options & PJMEDIA_ICE_NO_SRC_ADDR_CHECKING)==0); if (!enable_switch || pj_sockaddr_cmp(&tp_ice->remote_rtp, src_addr) == 0) { /* Don't switch while we're receiving from remote_rtp */ tp_ice->rtp_src_cnt = 0; } else { ++tp_ice->rtp_src_cnt; /* Check if the source address is recognized. */ if (pj_sockaddr_cmp(src_addr, &tp_ice->rtp_src_addr) != 0) { /* Remember the new source address. */ pj_sockaddr_cp(&tp_ice->rtp_src_addr, src_addr); /* Reset counter */ tp_ice->rtp_src_cnt = 0; discard = PJ_TRUE; } if (tp_ice->rtp_src_cnt < PJMEDIA_RTP_NAT_PROBATION_CNT) { discard = PJ_TRUE; } else { char addr_text[80]; /* Set remote RTP address to source address */ pj_sockaddr_cp(&tp_ice->remote_rtp, &tp_ice->rtp_src_addr); tp_ice->addr_len = pj_sockaddr_get_len(&tp_ice->remote_rtp); /* Reset counter */ tp_ice->rtp_src_cnt = 0; PJ_LOG(4,(tp_ice->base.name, "Remote RTP address switched to %s", pj_sockaddr_print(&tp_ice->remote_rtp, addr_text, sizeof(addr_text), 3))); /* Also update remote RTCP address if actual RTCP source * address is not heard yet. */ if (!pj_sockaddr_has_addr(&tp_ice->rtcp_src_addr)) { pj_uint16_t port; pj_sockaddr_cp(&tp_ice->remote_rtcp, &tp_ice->remote_rtp); port = (pj_uint16_t) (pj_sockaddr_get_port(&tp_ice->remote_rtp)+1); pj_sockaddr_set_port(&tp_ice->remote_rtcp, port); PJ_LOG(4,(tp_ice->base.name, "Remote RTCP address switched to predicted " "address %s", pj_sockaddr_print(&tp_ice->remote_rtcp, addr_text, sizeof(addr_text), 3))); } } } } if (!discard) (*tp_ice->rtp_cb)(tp_ice->stream, pkt, size); } else if (comp_id==2 && tp_ice->rtcp_cb) { /* Check if RTCP source address is the same as the configured * remote address, and switch the address when they are * different. */ if (!tp_ice->use_ice && (tp_ice->options & PJMEDIA_ICE_NO_SRC_ADDR_CHECKING)==0) { if (pj_sockaddr_cmp(&tp_ice->remote_rtcp, src_addr) == 0) { tp_ice->rtcp_src_cnt = 0; } else { char addr_text[80]; ++tp_ice->rtcp_src_cnt; if (tp_ice->rtcp_src_cnt < PJMEDIA_RTCP_NAT_PROBATION_CNT) { discard = PJ_TRUE; } else { tp_ice->rtcp_src_cnt = 0; pj_sockaddr_cp(&tp_ice->rtcp_src_addr, src_addr); pj_sockaddr_cp(&tp_ice->remote_rtcp, src_addr); pj_assert(tp_ice->addr_len==pj_sockaddr_get_len(src_addr)); PJ_LOG(4,(tp_ice->base.name, "Remote RTCP address switched to %s", pj_sockaddr_print(&tp_ice->remote_rtcp, addr_text, sizeof(addr_text), 3))); } } } if (!discard) (*tp_ice->rtcp_cb)(tp_ice->stream, pkt, size); } PJ_UNUSED_ARG(src_addr_len); } static void ice_on_ice_complete(pj_ice_strans *ice_st, pj_ice_strans_op op, pj_status_t result) { struct transport_ice *tp_ice; tp_ice = (struct transport_ice*) pj_ice_strans_get_user_data(ice_st); /* Notify application */ if (tp_ice->cb.on_ice_complete) (*tp_ice->cb.on_ice_complete)(&tp_ice->base, op, result); } static void ice_on_ice_state(pj_ice_strans *ice_st, pj_ice_strans_state prev, pj_ice_strans_state curr) { struct transport_ice *tp_ice; tp_ice = (struct transport_ice*) pj_ice_strans_get_user_data(ice_st); /* Notify application */ if (tp_ice->cb.on_ice_state) (*tp_ice->cb.on_ice_state)(&tp_ice->base, prev, curr); } /* Simulate lost */ static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost) { struct transport_ice *ice = (struct transport_ice*) tp; PJ_ASSERT_RETURN(tp && pct_lost <= 100, PJ_EINVAL); if (dir & PJMEDIA_DIR_ENCODING) ice->tx_drop_pct = pct_lost; if (dir & PJMEDIA_DIR_DECODING) ice->rx_drop_pct = pct_lost; return PJ_SUCCESS; } /* * Destroy ICE media transport. */ static pj_status_t transport_destroy(pjmedia_transport *tp) { struct transport_ice *tp_ice = (struct transport_ice*)tp; if (tp_ice->ice_st) { pj_ice_strans_destroy(tp_ice->ice_st); tp_ice->ice_st = NULL; } if (tp_ice->pool) { pj_pool_t *pool = tp_ice->pool; tp_ice->pool = NULL; pj_pool_release(pool); } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/transport_loop.c ================================================ /* $Id: transport_loop.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include struct user { pj_bool_t rx_disabled; /**< Doesn't want to receive pkt? */ void *user_data; /**< Only valid when attached */ void (*rtp_cb)( void*, /**< To report incoming RTP. */ void*, pj_ssize_t); void (*rtcp_cb)( void*, /**< To report incoming RTCP. */ void*, pj_ssize_t); }; struct transport_loop { pjmedia_transport base; /**< Base transport. */ pj_pool_t *pool; /**< Memory pool */ unsigned user_cnt; /**< Number of attachments */ struct user users[4]; /**< Array of users. */ unsigned tx_drop_pct; /**< Percent of tx pkts to drop. */ unsigned rx_drop_pct; /**< Percent of rx pkts to drop. */ }; /* * These are media transport operations. */ static pj_status_t transport_get_info (pjmedia_transport *tp, pjmedia_transport_info *info); static pj_status_t transport_attach (pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb)(void*, void*, pj_ssize_t), void (*rtcp_cb)(void*, void*, pj_ssize_t)); static void transport_detach (pjmedia_transport *tp, void *strm); static pj_status_t transport_send_rtp( pjmedia_transport *tp, const void *pkt, pj_size_t size); static pj_status_t transport_send_rtcp(pjmedia_transport *tp, const void *pkt, pj_size_t size); static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, const pj_sockaddr_t *addr, unsigned addr_len, const void *pkt, pj_size_t size); static pj_status_t transport_media_create(pjmedia_transport *tp, pj_pool_t *pool, unsigned options, const pjmedia_sdp_session *sdp_remote, unsigned media_index); static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *pool, pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *rem_sdp, unsigned media_index); static pj_status_t transport_media_start (pjmedia_transport *tp, pj_pool_t *pool, const pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *sdp_remote, unsigned media_index); static pj_status_t transport_media_stop(pjmedia_transport *tp); static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost); static pj_status_t transport_destroy (pjmedia_transport *tp); static pjmedia_transport_op transport_udp_op = { &transport_get_info, &transport_attach, &transport_detach, &transport_send_rtp, &transport_send_rtcp, &transport_send_rtcp2, &transport_media_create, &transport_encode_sdp, &transport_media_start, &transport_media_stop, &transport_simulate_lost, &transport_destroy }; /** * Create loopback transport. */ PJ_DEF(pj_status_t) pjmedia_transport_loop_create(pjmedia_endpt *endpt, pjmedia_transport **p_tp) { struct transport_loop *tp; pj_pool_t *pool; /* Sanity check */ PJ_ASSERT_RETURN(endpt && p_tp, PJ_EINVAL); /* Create transport structure */ pool = pjmedia_endpt_create_pool(endpt, "tploop", 512, 512); if (!pool) return PJ_ENOMEM; tp = PJ_POOL_ZALLOC_T(pool, struct transport_loop); tp->pool = pool; pj_ansi_strncpy(tp->base.name, tp->pool->obj_name, PJ_MAX_OBJ_NAME-1); tp->base.op = &transport_udp_op; tp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP; /* Done */ *p_tp = &tp->base; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_transport_loop_disable_rx( pjmedia_transport *tp, void *user, pj_bool_t disabled) { struct transport_loop *loop = (struct transport_loop*) tp; unsigned i; for (i=0; iuser_cnt; ++i) { if (loop->users[i].user_data == user) { loop->users[i].rx_disabled = disabled; return PJ_SUCCESS; } } pj_assert(!"Invalid stream user"); return PJ_ENOTFOUND; } /** * Close loopback transport. */ static pj_status_t transport_destroy(pjmedia_transport *tp) { struct transport_loop *loop = (struct transport_loop*) tp; /* Sanity check */ PJ_ASSERT_RETURN(tp, PJ_EINVAL); pj_pool_release(loop->pool); return PJ_SUCCESS; } /* Called to get the transport info */ static pj_status_t transport_get_info(pjmedia_transport *tp, pjmedia_transport_info *info) { PJ_ASSERT_RETURN(tp && info, PJ_EINVAL); info->sock_info.rtp_sock = 1; pj_sockaddr_in_init(&info->sock_info.rtp_addr_name.ipv4, 0, 0); info->sock_info.rtcp_sock = 2; pj_sockaddr_in_init(&info->sock_info.rtcp_addr_name.ipv4, 0, 0); return PJ_SUCCESS; } /* Called by application to initialize the transport */ static pj_status_t transport_attach( pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb)(void*, void*, pj_ssize_t), void (*rtcp_cb)(void*, void*, pj_ssize_t)) { struct transport_loop *loop = (struct transport_loop*) tp; unsigned i; const pj_sockaddr *rtcp_addr; /* Validate arguments */ PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL); /* Must not be "attached" to same user */ for (i=0; iuser_cnt; ++i) { PJ_ASSERT_RETURN(loop->users[i].user_data != user_data, PJ_EINVALIDOP); } PJ_ASSERT_RETURN(loop->user_cnt != PJ_ARRAY_SIZE(loop->users), PJ_ETOOMANY); PJ_UNUSED_ARG(rem_rtcp); PJ_UNUSED_ARG(rtcp_addr); /* "Attach" the application: */ /* Save the new user */ loop->users[loop->user_cnt].rtp_cb = rtp_cb; loop->users[loop->user_cnt].rtcp_cb = rtcp_cb; loop->users[loop->user_cnt].user_data = user_data; ++loop->user_cnt; return PJ_SUCCESS; } /* Called by application when it no longer needs the transport */ static void transport_detach( pjmedia_transport *tp, void *user_data) { struct transport_loop *loop = (struct transport_loop*) tp; unsigned i; pj_assert(tp); for (i=0; iuser_cnt; ++i) { if (loop->users[i].user_data == user_data) break; } /* Remove this user */ if (i != loop->user_cnt) { pj_array_erase(loop->users, sizeof(loop->users[0]), loop->user_cnt, i); --loop->user_cnt; } } /* Called by application to send RTP packet */ static pj_status_t transport_send_rtp( pjmedia_transport *tp, const void *pkt, pj_size_t size) { struct transport_loop *loop = (struct transport_loop*)tp; unsigned i; /* Simulate packet lost on TX direction */ if (loop->tx_drop_pct) { if ((pj_rand() % 100) <= (int)loop->tx_drop_pct) { PJ_LOG(5,(loop->base.name, "TX RTP packet dropped because of pkt lost " "simulation")); return PJ_SUCCESS; } } /* Simulate packet lost on RX direction */ if (loop->rx_drop_pct) { if ((pj_rand() % 100) <= (int)loop->rx_drop_pct) { PJ_LOG(5,(loop->base.name, "RX RTP packet dropped because of pkt lost " "simulation")); return PJ_SUCCESS; } } /* Distribute to users */ for (i=0; iuser_cnt; ++i) { if (!loop->users[i].rx_disabled && loop->users[i].rtp_cb) (*loop->users[i].rtp_cb)(loop->users[i].user_data, (void*)pkt, size); } return PJ_SUCCESS; } /* Called by application to send RTCP packet */ static pj_status_t transport_send_rtcp(pjmedia_transport *tp, const void *pkt, pj_size_t size) { return transport_send_rtcp2(tp, NULL, 0, pkt, size); } /* Called by application to send RTCP packet */ static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, const pj_sockaddr_t *addr, unsigned addr_len, const void *pkt, pj_size_t size) { struct transport_loop *loop = (struct transport_loop*)tp; unsigned i; PJ_UNUSED_ARG(addr_len); PJ_UNUSED_ARG(addr); /* Distribute to users */ for (i=0; iuser_cnt; ++i) { if (!loop->users[i].rx_disabled && loop->users[i].rtcp_cb) (*loop->users[i].rtcp_cb)(loop->users[i].user_data, (void*)pkt, size); } return PJ_SUCCESS; } static pj_status_t transport_media_create(pjmedia_transport *tp, pj_pool_t *pool, unsigned options, const pjmedia_sdp_session *sdp_remote, unsigned media_index) { PJ_UNUSED_ARG(tp); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(options); PJ_UNUSED_ARG(sdp_remote); PJ_UNUSED_ARG(media_index); return PJ_SUCCESS; } static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *pool, pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { PJ_UNUSED_ARG(tp); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(sdp_local); PJ_UNUSED_ARG(rem_sdp); PJ_UNUSED_ARG(media_index); return PJ_SUCCESS; } static pj_status_t transport_media_start(pjmedia_transport *tp, pj_pool_t *pool, const pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *sdp_remote, unsigned media_index) { PJ_UNUSED_ARG(tp); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(sdp_local); PJ_UNUSED_ARG(sdp_remote); PJ_UNUSED_ARG(media_index); return PJ_SUCCESS; } static pj_status_t transport_media_stop(pjmedia_transport *tp) { PJ_UNUSED_ARG(tp); return PJ_SUCCESS; } static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost) { struct transport_loop *loop = (struct transport_loop*)tp; PJ_ASSERT_RETURN(tp && pct_lost <= 100, PJ_EINVAL); if (dir & PJMEDIA_DIR_ENCODING) loop->tx_drop_pct = pct_lost; if (dir & PJMEDIA_DIR_DECODING) loop->rx_drop_pct = pct_lost; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/transport_srtp.c ================================================ /* $Id: transport_srtp.c 4366 2013-02-21 20:41:31Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) #if defined(PJ_HAS_SSL_SOCK) && (PJ_HAS_SSL_SOCK != 0) # include /* Suppress compile warning of OpenSSL deprecation (OpenSSL is deprecated * since MacOSX 10.7). */ #if defined(PJ_DARWINOS) && PJ_DARWINOS==1 # pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #endif #if defined(PJMEDIA_EXTERNAL_SRTP) && (PJMEDIA_EXTERNAL_SRTP != 0) # include # include #else # include #endif #define THIS_FILE "transport_srtp.c" /* Maximum size of outgoing packet */ #define MAX_RTP_BUFFER_LEN PJMEDIA_MAX_MTU #define MAX_RTCP_BUFFER_LEN PJMEDIA_MAX_MTU /* Maximum SRTP crypto key length */ #define MAX_KEY_LEN 128 /* Initial value of probation counter. When probation counter > 0, * it means SRTP is in probation state, and it may restart when * srtp_unprotect() returns err_status_replay_* */ #define PROBATION_CNT_INIT 100 #define DEACTIVATE_MEDIA(pool, m) pjmedia_sdp_media_deactivate(pool, m) static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 }; static const pj_str_t ID_INACTIVE = { "inactive", 8 }; static const pj_str_t ID_CRYPTO = { "crypto", 6 }; typedef struct crypto_suite { char *name; cipher_type_id_t cipher_type; unsigned cipher_key_len; auth_type_id_t auth_type; unsigned auth_key_len; unsigned srtp_auth_tag_len; unsigned srtcp_auth_tag_len; sec_serv_t service; } crypto_suite; /* Crypto suites as defined on RFC 4568 */ static crypto_suite crypto_suites[] = { /* plain RTP/RTCP (no cipher & no auth) */ {"NULL", NULL_CIPHER, 0, NULL_AUTH, 0, 0, 0, sec_serv_none}, /* cipher AES_CM, auth HMAC_SHA1, auth tag len = 10 octets */ {"AES_CM_128_HMAC_SHA1_80", AES_128_ICM, 30, HMAC_SHA1, 20, 10, 10, sec_serv_conf_and_auth}, /* cipher AES_CM, auth HMAC_SHA1, auth tag len = 4 octets */ {"AES_CM_128_HMAC_SHA1_32", AES_128_ICM, 30, HMAC_SHA1, 20, 4, 10, sec_serv_conf_and_auth}, /* * F8_128_HMAC_SHA1_8 not supported by libsrtp? * {"F8_128_HMAC_SHA1_8", NULL_CIPHER, 0, NULL_AUTH, 0, 0, 0, sec_serv_none} */ }; typedef struct transport_srtp { pjmedia_transport base; /**< Base transport interface. */ pj_pool_t *pool; /**< Pool for transport SRTP. */ pj_lock_t *mutex; /**< Mutex for libsrtp contexts.*/ char rtp_tx_buffer[MAX_RTP_BUFFER_LEN]; char rtcp_tx_buffer[MAX_RTCP_BUFFER_LEN]; pjmedia_srtp_setting setting; unsigned media_option; /* SRTP policy */ pj_bool_t session_inited; pj_bool_t offerer_side; pj_bool_t bypass_srtp; char tx_key[MAX_KEY_LEN]; char rx_key[MAX_KEY_LEN]; pjmedia_srtp_crypto tx_policy; pjmedia_srtp_crypto rx_policy; /* Temporary policy for negotiation */ pjmedia_srtp_crypto tx_policy_neg; pjmedia_srtp_crypto rx_policy_neg; /* libSRTP contexts */ srtp_t srtp_tx_ctx; srtp_t srtp_rx_ctx; /* Stream information */ void *user_data; void (*rtp_cb)( void *user_data, void *pkt, pj_ssize_t size); void (*rtcp_cb)(void *user_data, void *pkt, pj_ssize_t size); /* Transport information */ pjmedia_transport *member_tp; /**< Underlying transport. */ /* SRTP usage policy of peer. This field is updated when media is starting. * This is useful when SRTP is in optional mode and peer is using mandatory * mode, so when local is about to reinvite/update, it should offer * RTP/SAVP instead of offering RTP/AVP. */ pjmedia_srtp_use peer_use; /* When probation counter > 0, it means SRTP is in probation state, * and it may restart when srtp_unprotect() returns err_status_replay_* */ unsigned probation_cnt; } transport_srtp; /* * This callback is called by transport when incoming rtp is received */ static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size); /* * This callback is called by transport when incoming rtcp is received */ static void srtp_rtcp_cb( void *user_data, void *pkt, pj_ssize_t size); /* * These are media transport operations. */ static pj_status_t transport_get_info (pjmedia_transport *tp, pjmedia_transport_info *info); static pj_status_t transport_attach (pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb)(void*, void*, pj_ssize_t), void (*rtcp_cb)(void*, void*, pj_ssize_t)); static void transport_detach (pjmedia_transport *tp, void *strm); static pj_status_t transport_send_rtp( pjmedia_transport *tp, const void *pkt, pj_size_t size); static pj_status_t transport_send_rtcp(pjmedia_transport *tp, const void *pkt, pj_size_t size); static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, const pj_sockaddr_t *addr, unsigned addr_len, const void *pkt, pj_size_t size); static pj_status_t transport_media_create(pjmedia_transport *tp, pj_pool_t *sdp_pool, unsigned options, const pjmedia_sdp_session *sdp_remote, unsigned media_index); static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *sdp_remote, unsigned media_index); static pj_status_t transport_media_start (pjmedia_transport *tp, pj_pool_t *pool, const pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *sdp_remote, unsigned media_index); static pj_status_t transport_media_stop(pjmedia_transport *tp); static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost); static pj_status_t transport_destroy (pjmedia_transport *tp); static pjmedia_transport_op transport_srtp_op = { &transport_get_info, &transport_attach, &transport_detach, &transport_send_rtp, &transport_send_rtcp, &transport_send_rtcp2, &transport_media_create, &transport_encode_sdp, &transport_media_start, &transport_media_stop, &transport_simulate_lost, &transport_destroy }; /* This function may also be used by other module, e.g: pjmedia/errno.c, * it should have C compatible declaration. */ PJ_BEGIN_DECL const char* get_libsrtp_errstr(int err); PJ_END_DECL const char* get_libsrtp_errstr(int err) { #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) static char *liberr[] = { "ok", /* err_status_ok = 0 */ "unspecified failure", /* err_status_fail = 1 */ "unsupported parameter", /* err_status_bad_param = 2 */ "couldn't allocate memory", /* err_status_alloc_fail = 3 */ "couldn't deallocate properly", /* err_status_dealloc_fail = 4 */ "couldn't initialize", /* err_status_init_fail = 5 */ "can't process as much data as requested", /* err_status_terminus = 6 */ "authentication failure", /* err_status_auth_fail = 7 */ "cipher failure", /* err_status_cipher_fail = 8 */ "replay check failed (bad index)", /* err_status_replay_fail = 9 */ "replay check failed (index too old)", /* err_status_replay_old = 10 */ "algorithm failed test routine", /* err_status_algo_fail = 11 */ "unsupported operation", /* err_status_no_such_op = 12 */ "no appropriate context found", /* err_status_no_ctx = 13 */ "unable to perform desired validation", /* err_status_cant_check = 14 */ "can't use key any more", /* err_status_key_expired = 15 */ "error in use of socket", /* err_status_socket_err = 16 */ "error in use POSIX signals", /* err_status_signal_err = 17 */ "nonce check failed", /* err_status_nonce_bad = 18 */ "couldn't read data", /* err_status_read_fail = 19 */ "couldn't write data", /* err_status_write_fail = 20 */ "error pasring data", /* err_status_parse_err = 21 */ "error encoding data", /* err_status_encode_err = 22 */ "error while using semaphores", /* err_status_semaphore_err = 23 */ "error while using pfkey" /* err_status_pfkey_err = 24 */ }; if (err >= 0 && err < (int)PJ_ARRAY_SIZE(liberr)) { return liberr[err]; } else { static char msg[32]; pj_ansi_snprintf(msg, sizeof(msg), "Unknown libsrtp error %d", err); return msg; } #else static char msg[32]; pj_ansi_snprintf(msg, sizeof(msg), "libsrtp error %d", err); return msg; #endif } static pj_bool_t libsrtp_initialized; static void pjmedia_srtp_deinit_lib(pjmedia_endpt *endpt); PJ_DEF(pj_status_t) pjmedia_srtp_init_lib(pjmedia_endpt *endpt) { #if PJMEDIA_LIBSRTP_AUTO_INIT_DEINIT if (libsrtp_initialized == PJ_FALSE) { err_status_t err; err = srtp_init(); if (err != err_status_ok) { PJ_LOG(4, (THIS_FILE, "Failed to initialize libsrtp: %s", get_libsrtp_errstr(err))); return PJMEDIA_ERRNO_FROM_LIBSRTP(err); } if (pjmedia_endpt_atexit(endpt, pjmedia_srtp_deinit_lib) != PJ_SUCCESS) { /* There will be memory leak when it fails to schedule libsrtp * deinitialization, however the memory leak could be harmless, * since in modern OS's memory used by an application is released * when the application terminates. */ PJ_LOG(4, (THIS_FILE, "Failed to register libsrtp deinit.")); } libsrtp_initialized = PJ_TRUE; } #else PJ_UNUSED_ARG(endpt); #endif return PJ_SUCCESS; } static void pjmedia_srtp_deinit_lib(pjmedia_endpt *endpt) { err_status_t err; /* Note that currently this SRTP init/deinit is not equipped with * reference counter, it should be safe as normally there is only * one single instance of media endpoint and even if it isn't, the * pjmedia_transport_srtp_create() will invoke SRTP init (the only * drawback should be the delay described by #788). */ PJ_UNUSED_ARG(endpt); #if defined(PJMEDIA_EXTERNAL_SRTP) && (PJMEDIA_EXTERNAL_SRTP != 0) # if defined(PJMEDIA_SRTP_HAS_DEINIT) && PJMEDIA_SRTP_HAS_DEINIT!=0 err = srtp_deinit(); # elif defined(PJMEDIA_SRTP_HAS_SHUTDOWN) && PJMEDIA_SRTP_HAS_SHUTDOWN!=0 err = srtp_shutdown(); # else err = err_status_ok; # endif #else err = srtp_deinit(); #endif if (err != err_status_ok) { PJ_LOG(4, (THIS_FILE, "Failed to deinitialize libsrtp: %s", get_libsrtp_errstr(err))); } libsrtp_initialized = PJ_FALSE; } static int get_crypto_idx(const pj_str_t* crypto_name) { int i; int cs_cnt = sizeof(crypto_suites)/sizeof(crypto_suites[0]); /* treat unspecified crypto_name as crypto 'NULL' */ if (crypto_name->slen == 0) return 0; for (i=0; ikey, &c2->key); if (r != 0) return r; r = pj_stricmp(&c1->name, &c2->name); if (r != 0) return r; return (c1->flags != c2->flags); } static pj_bool_t srtp_crypto_empty(const pjmedia_srtp_crypto* c) { return (c->name.slen==0 || c->key.slen==0); } PJ_DEF(void) pjmedia_srtp_setting_default(pjmedia_srtp_setting *opt) { unsigned i; pj_assert(opt); pj_bzero(opt, sizeof(pjmedia_srtp_setting)); opt->close_member_tp = PJ_TRUE; opt->use = PJMEDIA_SRTP_OPTIONAL; /* Copy default crypto-suites, but skip crypto 'NULL' */ opt->crypto_count = sizeof(crypto_suites)/sizeof(crypto_suites[0]) - 1; for (i=0; icrypto_count; ++i) opt->crypto[i].name = pj_str(crypto_suites[i+1].name); } /* * Create an SRTP media transport. */ PJ_DEF(pj_status_t) pjmedia_transport_srtp_create( pjmedia_endpt *endpt, pjmedia_transport *tp, const pjmedia_srtp_setting *opt, pjmedia_transport **p_tp) { pj_pool_t *pool; transport_srtp *srtp; pj_status_t status; unsigned i; PJ_ASSERT_RETURN(endpt && tp && p_tp, PJ_EINVAL); /* Check crypto availability */ if (opt && opt->crypto_count == 0 && opt->use == PJMEDIA_SRTP_MANDATORY) return PJMEDIA_SRTP_ESDPREQCRYPTO; /* Check crypto */ if (opt && opt->use != PJMEDIA_SRTP_DISABLED) { for (i=0; i < opt->crypto_count; ++i) { int cs_idx = get_crypto_idx(&opt->crypto[i].name); /* check crypto name */ if (cs_idx == -1) return PJMEDIA_SRTP_ENOTSUPCRYPTO; /* check key length */ if (opt->crypto[i].key.slen && opt->crypto[i].key.slen < (pj_ssize_t)crypto_suites[cs_idx].cipher_key_len) return PJMEDIA_SRTP_EINKEYLEN; } } /* Init libsrtp. */ status = pjmedia_srtp_init_lib(endpt); if (status != PJ_SUCCESS) return status; pool = pjmedia_endpt_create_pool(endpt, "srtp%p", 1000, 1000); srtp = PJ_POOL_ZALLOC_T(pool, transport_srtp); srtp->pool = pool; srtp->session_inited = PJ_FALSE; srtp->bypass_srtp = PJ_FALSE; srtp->probation_cnt = PROBATION_CNT_INIT; if (opt) { srtp->setting = *opt; if (opt->use == PJMEDIA_SRTP_DISABLED) srtp->setting.crypto_count = 0; for (i=0; i < srtp->setting.crypto_count; ++i) { int cs_idx = get_crypto_idx(&opt->crypto[i].name); pj_str_t tmp_key = opt->crypto[i].key; /* re-set crypto */ srtp->setting.crypto[i].name = pj_str(crypto_suites[cs_idx].name); /* cut key length */ if (tmp_key.slen) tmp_key.slen = crypto_suites[cs_idx].cipher_key_len; pj_strdup(pool, &srtp->setting.crypto[i].key, &tmp_key); } } else { pjmedia_srtp_setting_default(&srtp->setting); } status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &srtp->mutex); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } /* Initialize base pjmedia_transport */ pj_memcpy(srtp->base.name, pool->obj_name, PJ_MAX_OBJ_NAME); if (tp) srtp->base.type = tp->type; else srtp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP; srtp->base.op = &transport_srtp_op; /* Set underlying transport */ srtp->member_tp = tp; /* Initialize peer's SRTP usage mode. */ srtp->peer_use = srtp->setting.use; /* Done */ *p_tp = &srtp->base; return PJ_SUCCESS; } /* * Initialize and start SRTP session with the given parameters. */ PJ_DEF(pj_status_t) pjmedia_transport_srtp_start( pjmedia_transport *tp, const pjmedia_srtp_crypto *tx, const pjmedia_srtp_crypto *rx) { transport_srtp *srtp = (transport_srtp*) tp; srtp_policy_t tx_; srtp_policy_t rx_; err_status_t err; int cr_tx_idx = 0; int au_tx_idx = 0; int cr_rx_idx = 0; int au_rx_idx = 0; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(tp && tx && rx, PJ_EINVAL); pj_lock_acquire(srtp->mutex); if (srtp->session_inited) { pjmedia_transport_srtp_stop(tp); } /* Get encryption and authentication method */ cr_tx_idx = au_tx_idx = get_crypto_idx(&tx->name); if (tx->flags & PJMEDIA_SRTP_NO_ENCRYPTION) cr_tx_idx = 0; if (tx->flags & PJMEDIA_SRTP_NO_AUTHENTICATION) au_tx_idx = 0; cr_rx_idx = au_rx_idx = get_crypto_idx(&rx->name); if (rx->flags & PJMEDIA_SRTP_NO_ENCRYPTION) cr_rx_idx = 0; if (rx->flags & PJMEDIA_SRTP_NO_AUTHENTICATION) au_rx_idx = 0; /* Check whether the crypto-suite requested is supported */ if (cr_tx_idx == -1 || cr_rx_idx == -1 || au_tx_idx == -1 || au_rx_idx == -1) { status = PJMEDIA_SRTP_ENOTSUPCRYPTO; goto on_return; } /* If all options points to 'NULL' method, just bypass SRTP */ if (cr_tx_idx == 0 && cr_rx_idx == 0 && au_tx_idx == 0 && au_rx_idx == 0) { srtp->bypass_srtp = PJ_TRUE; goto on_return; } /* Check key length */ if (tx->key.slen != (pj_ssize_t)crypto_suites[cr_tx_idx].cipher_key_len || rx->key.slen != (pj_ssize_t)crypto_suites[cr_rx_idx].cipher_key_len) { status = PJMEDIA_SRTP_EINKEYLEN; goto on_return; } /* Init transmit direction */ pj_bzero(&tx_, sizeof(srtp_policy_t)); pj_memmove(srtp->tx_key, tx->key.ptr, tx->key.slen); if (cr_tx_idx && au_tx_idx) tx_.rtp.sec_serv = sec_serv_conf_and_auth; else if (cr_tx_idx) tx_.rtp.sec_serv = sec_serv_conf; else if (au_tx_idx) tx_.rtp.sec_serv = sec_serv_auth; else tx_.rtp.sec_serv = sec_serv_none; tx_.key = (uint8_t*)srtp->tx_key; tx_.ssrc.type = ssrc_any_outbound; tx_.ssrc.value = 0; tx_.rtp.cipher_type = crypto_suites[cr_tx_idx].cipher_type; tx_.rtp.cipher_key_len = crypto_suites[cr_tx_idx].cipher_key_len; tx_.rtp.auth_type = crypto_suites[au_tx_idx].auth_type; tx_.rtp.auth_key_len = crypto_suites[au_tx_idx].auth_key_len; tx_.rtp.auth_tag_len = crypto_suites[au_tx_idx].srtp_auth_tag_len; tx_.rtcp = tx_.rtp; tx_.rtcp.auth_tag_len = crypto_suites[au_tx_idx].srtcp_auth_tag_len; tx_.next = NULL; err = srtp_create(&srtp->srtp_tx_ctx, &tx_); if (err != err_status_ok) { status = PJMEDIA_ERRNO_FROM_LIBSRTP(err); goto on_return; } srtp->tx_policy = *tx; pj_strset(&srtp->tx_policy.key, srtp->tx_key, tx->key.slen); srtp->tx_policy.name=pj_str(crypto_suites[get_crypto_idx(&tx->name)].name); /* Init receive direction */ pj_bzero(&rx_, sizeof(srtp_policy_t)); pj_memmove(srtp->rx_key, rx->key.ptr, rx->key.slen); if (cr_rx_idx && au_rx_idx) rx_.rtp.sec_serv = sec_serv_conf_and_auth; else if (cr_rx_idx) rx_.rtp.sec_serv = sec_serv_conf; else if (au_rx_idx) rx_.rtp.sec_serv = sec_serv_auth; else rx_.rtp.sec_serv = sec_serv_none; rx_.key = (uint8_t*)srtp->rx_key; rx_.ssrc.type = ssrc_any_inbound; rx_.ssrc.value = 0; rx_.rtp.sec_serv = crypto_suites[cr_rx_idx].service; rx_.rtp.cipher_type = crypto_suites[cr_rx_idx].cipher_type; rx_.rtp.cipher_key_len = crypto_suites[cr_rx_idx].cipher_key_len; rx_.rtp.auth_type = crypto_suites[au_rx_idx].auth_type; rx_.rtp.auth_key_len = crypto_suites[au_rx_idx].auth_key_len; rx_.rtp.auth_tag_len = crypto_suites[au_rx_idx].srtp_auth_tag_len; rx_.rtcp = rx_.rtp; rx_.rtcp.auth_tag_len = crypto_suites[au_rx_idx].srtcp_auth_tag_len; rx_.next = NULL; err = srtp_create(&srtp->srtp_rx_ctx, &rx_); if (err != err_status_ok) { srtp_dealloc(srtp->srtp_tx_ctx); status = PJMEDIA_ERRNO_FROM_LIBSRTP(err); goto on_return; } srtp->rx_policy = *rx; pj_strset(&srtp->rx_policy.key, srtp->rx_key, rx->key.slen); srtp->rx_policy.name=pj_str(crypto_suites[get_crypto_idx(&rx->name)].name); /* Declare SRTP session initialized */ srtp->session_inited = PJ_TRUE; /* Logging stuffs */ #if PJ_LOG_MAX_LEVEL >= 5 { char b64[PJ_BASE256_TO_BASE64_LEN(MAX_KEY_LEN)]; int b64_len; /* TX crypto and key */ b64_len = sizeof(b64); status = pj_base64_encode((pj_uint8_t*)tx->key.ptr, tx->key.slen, b64, &b64_len); if (status != PJ_SUCCESS) b64_len = pj_ansi_sprintf(b64, "--key too long--"); else b64[b64_len] = '\0'; PJ_LOG(5, (srtp->pool->obj_name, "TX: %s key=%s", srtp->tx_policy.name.ptr, b64)); if (srtp->tx_policy.flags) { PJ_LOG(5,(srtp->pool->obj_name, "TX: disable%s%s", (cr_tx_idx?"":" enc"), (au_tx_idx?"":" auth"))); } /* RX crypto and key */ b64_len = sizeof(b64); status = pj_base64_encode((pj_uint8_t*)rx->key.ptr, rx->key.slen, b64, &b64_len); if (status != PJ_SUCCESS) b64_len = pj_ansi_sprintf(b64, "--key too long--"); else b64[b64_len] = '\0'; PJ_LOG(5, (srtp->pool->obj_name, "RX: %s key=%s", srtp->rx_policy.name.ptr, b64)); if (srtp->rx_policy.flags) { PJ_LOG(5,(srtp->pool->obj_name,"RX: disable%s%s", (cr_rx_idx?"":" enc"), (au_rx_idx?"":" auth"))); } } #endif on_return: pj_lock_release(srtp->mutex); return status; } /* * Stop SRTP session. */ PJ_DEF(pj_status_t) pjmedia_transport_srtp_stop(pjmedia_transport *srtp) { transport_srtp *p_srtp = (transport_srtp*) srtp; err_status_t err; PJ_ASSERT_RETURN(srtp, PJ_EINVAL); pj_lock_acquire(p_srtp->mutex); if (!p_srtp->session_inited) { pj_lock_release(p_srtp->mutex); return PJ_SUCCESS; } err = srtp_dealloc(p_srtp->srtp_rx_ctx); if (err != err_status_ok) { PJ_LOG(4, (p_srtp->pool->obj_name, "Failed to dealloc RX SRTP context: %s", get_libsrtp_errstr(err))); } err = srtp_dealloc(p_srtp->srtp_tx_ctx); if (err != err_status_ok) { PJ_LOG(4, (p_srtp->pool->obj_name, "Failed to dealloc TX SRTP context: %s", get_libsrtp_errstr(err))); } p_srtp->session_inited = PJ_FALSE; pj_bzero(&p_srtp->rx_policy, sizeof(p_srtp->rx_policy)); pj_bzero(&p_srtp->tx_policy, sizeof(p_srtp->tx_policy)); pj_lock_release(p_srtp->mutex); return PJ_SUCCESS; } PJ_DEF(pjmedia_transport *) pjmedia_transport_srtp_get_member( pjmedia_transport *tp) { transport_srtp *srtp = (transport_srtp*) tp; PJ_ASSERT_RETURN(tp, NULL); return srtp->member_tp; } static pj_status_t transport_get_info(pjmedia_transport *tp, pjmedia_transport_info *info) { transport_srtp *srtp = (transport_srtp*) tp; pjmedia_srtp_info srtp_info; int spc_info_idx; PJ_ASSERT_RETURN(tp && info, PJ_EINVAL); PJ_ASSERT_RETURN(info->specific_info_cnt < PJMEDIA_TRANSPORT_SPECIFIC_INFO_MAXCNT, PJ_ETOOMANY); PJ_ASSERT_RETURN(sizeof(pjmedia_srtp_info) <= PJMEDIA_TRANSPORT_SPECIFIC_INFO_MAXSIZE, PJ_ENOMEM); srtp_info.active = srtp->session_inited; srtp_info.rx_policy = srtp->rx_policy; srtp_info.tx_policy = srtp->tx_policy; srtp_info.use = srtp->setting.use; srtp_info.peer_use = srtp->peer_use; spc_info_idx = info->specific_info_cnt++; info->spc_info[spc_info_idx].type = PJMEDIA_TRANSPORT_TYPE_SRTP; info->spc_info[spc_info_idx].cbsize = sizeof(srtp_info); pj_memcpy(&info->spc_info[spc_info_idx].buffer, &srtp_info, sizeof(srtp_info)); return pjmedia_transport_get_info(srtp->member_tp, info); } static pj_status_t transport_attach(pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb) (void*, void*, pj_ssize_t), void (*rtcp_cb)(void*, void*, pj_ssize_t)) { transport_srtp *srtp = (transport_srtp*) tp; pj_status_t status; PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL); /* Save the callbacks */ pj_lock_acquire(srtp->mutex); srtp->rtp_cb = rtp_cb; srtp->rtcp_cb = rtcp_cb; srtp->user_data = user_data; pj_lock_release(srtp->mutex); /* Attach itself to transport */ status = pjmedia_transport_attach(srtp->member_tp, srtp, rem_addr, rem_rtcp, addr_len, &srtp_rtp_cb, &srtp_rtcp_cb); if (status != PJ_SUCCESS) { pj_lock_acquire(srtp->mutex); srtp->rtp_cb = NULL; srtp->rtcp_cb = NULL; srtp->user_data = NULL; pj_lock_release(srtp->mutex); return status; } return PJ_SUCCESS; } static void transport_detach(pjmedia_transport *tp, void *strm) { transport_srtp *srtp = (transport_srtp*) tp; PJ_UNUSED_ARG(strm); PJ_ASSERT_ON_FAIL(tp, return); if (srtp->member_tp) { pjmedia_transport_detach(srtp->member_tp, srtp); } /* Clear up application infos from transport */ pj_lock_acquire(srtp->mutex); srtp->rtp_cb = NULL; srtp->rtcp_cb = NULL; srtp->user_data = NULL; pj_lock_release(srtp->mutex); } static pj_status_t transport_send_rtp( pjmedia_transport *tp, const void *pkt, pj_size_t size) { pj_status_t status; transport_srtp *srtp = (transport_srtp*) tp; int len = (int)size; err_status_t err; if (srtp->bypass_srtp) return pjmedia_transport_send_rtp(srtp->member_tp, pkt, size); if (size > sizeof(srtp->rtp_tx_buffer) - 10) return PJ_ETOOBIG; pj_memcpy(srtp->rtp_tx_buffer, pkt, size); pj_lock_acquire(srtp->mutex); if (!srtp->session_inited) { pj_lock_release(srtp->mutex); return PJ_EINVALIDOP; } err = srtp_protect(srtp->srtp_tx_ctx, srtp->rtp_tx_buffer, &len); pj_lock_release(srtp->mutex); if (err == err_status_ok) { status = pjmedia_transport_send_rtp(srtp->member_tp, srtp->rtp_tx_buffer, len); } else { status = PJMEDIA_ERRNO_FROM_LIBSRTP(err); } return status; } static pj_status_t transport_send_rtcp(pjmedia_transport *tp, const void *pkt, pj_size_t size) { return transport_send_rtcp2(tp, NULL, 0, pkt, size); } static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, const pj_sockaddr_t *addr, unsigned addr_len, const void *pkt, pj_size_t size) { pj_status_t status; transport_srtp *srtp = (transport_srtp*) tp; int len = (int)size; err_status_t err; if (srtp->bypass_srtp) { return pjmedia_transport_send_rtcp2(srtp->member_tp, addr, addr_len, pkt, size); } if (size > sizeof(srtp->rtcp_tx_buffer) - 10) return PJ_ETOOBIG; pj_memcpy(srtp->rtcp_tx_buffer, pkt, size); pj_lock_acquire(srtp->mutex); if (!srtp->session_inited) { pj_lock_release(srtp->mutex); return PJ_EINVALIDOP; } err = srtp_protect_rtcp(srtp->srtp_tx_ctx, srtp->rtcp_tx_buffer, &len); pj_lock_release(srtp->mutex); if (err == err_status_ok) { status = pjmedia_transport_send_rtcp2(srtp->member_tp, addr, addr_len, srtp->rtcp_tx_buffer, len); } else { status = PJMEDIA_ERRNO_FROM_LIBSRTP(err); } return status; } static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost) { transport_srtp *srtp = (transport_srtp *) tp; PJ_ASSERT_RETURN(tp, PJ_EINVAL); return pjmedia_transport_simulate_lost(srtp->member_tp, dir, pct_lost); } static pj_status_t transport_destroy (pjmedia_transport *tp) { transport_srtp *srtp = (transport_srtp *) tp; pj_status_t status; PJ_ASSERT_RETURN(tp, PJ_EINVAL); if (srtp->setting.close_member_tp && srtp->member_tp) { pjmedia_transport_close(srtp->member_tp); } status = pjmedia_transport_srtp_stop(tp); /* In case mutex is being acquired by other thread */ pj_lock_acquire(srtp->mutex); pj_lock_release(srtp->mutex); pj_lock_destroy(srtp->mutex); pj_pool_release(srtp->pool); return status; } /* * This callback is called by transport when incoming rtp is received */ static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size) { transport_srtp *srtp = (transport_srtp *) user_data; int len = size; err_status_t err; void (*cb)(void*, void*, pj_ssize_t) = NULL; void *cb_data = NULL; if (srtp->bypass_srtp) { srtp->rtp_cb(srtp->user_data, pkt, size); return; } if (size < 0) { return; } /* Make sure buffer is 32bit aligned */ PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return ); if (srtp->probation_cnt > 0) --srtp->probation_cnt; pj_lock_acquire(srtp->mutex); if (!srtp->session_inited) { pj_lock_release(srtp->mutex); return; } err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); if (srtp->probation_cnt > 0 && (err == err_status_replay_old || err == err_status_replay_fail)) { /* Handle such condition that stream is updated (RTP seq is reinited * & SRTP is restarted), but some old packets are still coming * so SRTP is learning wrong RTP seq. While the newly inited RTP seq * comes, SRTP thinks the RTP seq is replayed, so srtp_unprotect() * will return err_status_replay_*. Restarting SRTP can resolve this. */ pjmedia_srtp_crypto tx, rx; pj_status_t status; tx = srtp->tx_policy; rx = srtp->rx_policy; status = pjmedia_transport_srtp_start((pjmedia_transport*)srtp, &tx, &rx); if (status != PJ_SUCCESS) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to restart SRTP, err=%s", get_libsrtp_errstr(err))); } else if (!srtp->bypass_srtp) { err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); } } if (err != err_status_ok) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to unprotect SRTP, pkt size=%d, err=%s", size, get_libsrtp_errstr(err))); } else { cb = srtp->rtp_cb; cb_data = srtp->user_data; } pj_lock_release(srtp->mutex); if (cb) { (*cb)(cb_data, pkt, len); } } /* * This callback is called by transport when incoming rtcp is received */ static void srtp_rtcp_cb( void *user_data, void *pkt, pj_ssize_t size) { transport_srtp *srtp = (transport_srtp *) user_data; int len = size; err_status_t err; void (*cb)(void*, void*, pj_ssize_t) = NULL; void *cb_data = NULL; if (srtp->bypass_srtp) { srtp->rtcp_cb(srtp->user_data, pkt, size); return; } if (size < 0) { return; } /* Make sure buffer is 32bit aligned */ PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return ); pj_lock_acquire(srtp->mutex); if (!srtp->session_inited) { pj_lock_release(srtp->mutex); return; } err = srtp_unprotect_rtcp(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); if (err != err_status_ok) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to unprotect SRTCP, pkt size=%d, err=%s", size, get_libsrtp_errstr(err))); } else { cb = srtp->rtcp_cb; cb_data = srtp->user_data; } pj_lock_release(srtp->mutex); if (cb) { (*cb)(cb_data, pkt, len); } } /* Generate crypto attribute, including crypto key. * If crypto-suite chosen is crypto NULL, just return PJ_SUCCESS, * and set buffer_len = 0. */ static pj_status_t generate_crypto_attr_value(pj_pool_t *pool, char *buffer, int *buffer_len, pjmedia_srtp_crypto *crypto, int tag) { pj_status_t status; int cs_idx = get_crypto_idx(&crypto->name); char b64_key[PJ_BASE256_TO_BASE64_LEN(MAX_KEY_LEN)+1]; int b64_key_len = sizeof(b64_key); int print_len; if (cs_idx == -1) return PJMEDIA_SRTP_ENOTSUPCRYPTO; /* Crypto-suite NULL. */ if (cs_idx == 0) { *buffer_len = 0; return PJ_SUCCESS; } /* Generate key if not specified. */ if (crypto->key.slen == 0) { pj_bool_t key_ok; char key[MAX_KEY_LEN]; err_status_t err; unsigned i; PJ_ASSERT_RETURN(MAX_KEY_LEN >= crypto_suites[cs_idx].cipher_key_len, PJ_ETOOSMALL); do { key_ok = PJ_TRUE; #if defined(PJ_HAS_SSL_SOCK) && (PJ_HAS_SSL_SOCK != 0) /* Include OpenSSL libraries for MSVC */ # ifdef _MSC_VER # pragma comment( lib, "libeay32") # pragma comment( lib, "ssleay32") # endif err = RAND_bytes((unsigned char*)key, crypto_suites[cs_idx].cipher_key_len); if (err != 1) { PJ_LOG(5,(THIS_FILE, "Failed generating random key")); return PJMEDIA_ERRNO_FROM_LIBSRTP(1); } #else err = crypto_get_random((unsigned char*)key, crypto_suites[cs_idx].cipher_key_len); if (err != err_status_ok) { PJ_LOG(5,(THIS_FILE, "Failed generating random key: %s", get_libsrtp_errstr(err))); return PJMEDIA_ERRNO_FROM_LIBSRTP(err); } #endif for (i=0; ikey.ptr = (char*) pj_pool_zalloc(pool, crypto_suites[cs_idx].cipher_key_len); pj_memcpy(crypto->key.ptr, key, crypto_suites[cs_idx].cipher_key_len); crypto->key.slen = crypto_suites[cs_idx].cipher_key_len; } if (crypto->key.slen != (pj_ssize_t)crypto_suites[cs_idx].cipher_key_len) return PJMEDIA_SRTP_EINKEYLEN; /* Key transmitted via SDP should be base64 encoded. */ status = pj_base64_encode((pj_uint8_t*)crypto->key.ptr, crypto->key.slen, b64_key, &b64_key_len); if (status != PJ_SUCCESS) { PJ_LOG(5,(THIS_FILE, "Failed encoding plain key to base64")); return status; } b64_key[b64_key_len] = '\0'; PJ_ASSERT_RETURN(*buffer_len >= (crypto->name.slen + \ b64_key_len + 16), PJ_ETOOSMALL); /* Print the crypto attribute value. */ print_len = pj_ansi_snprintf(buffer, *buffer_len, "%d %s inline:%s", tag, crypto_suites[cs_idx].name, b64_key); if (print_len < 1 || print_len >= *buffer_len) return PJ_ETOOSMALL; *buffer_len = print_len; return PJ_SUCCESS; } /* Parse crypto attribute line */ static pj_status_t parse_attr_crypto(pj_pool_t *pool, const pjmedia_sdp_attr *attr, pjmedia_srtp_crypto *crypto, int *tag) { pj_str_t input; char *token; pj_size_t token_len; pj_str_t tmp; pj_status_t status; int itmp; pj_bzero(crypto, sizeof(*crypto)); pj_strdup_with_null(pool, &input, &attr->value); /* Tag */ token = strtok(input.ptr, " "); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting tag")); return PJMEDIA_SDP_EINATTR; } token_len = pj_ansi_strlen(token); /* Tag must not use leading zeroes. */ if (token_len > 1 && *token == '0') return PJMEDIA_SDP_EINATTR; /* Tag must be decimal, i.e: contains only digit '0'-'9'. */ for (itmp = 0; itmp < token_len; ++itmp) if (!pj_isdigit(token[itmp])) return PJMEDIA_SDP_EINATTR; /* Get tag value. */ *tag = atoi(token); /* Crypto-suite */ token = strtok(NULL, " "); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting crypto suite")); return PJMEDIA_SDP_EINATTR; } crypto->name = pj_str(token); /* Key method */ token = strtok(NULL, ":"); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key method")); return PJMEDIA_SDP_EINATTR; } if (pj_ansi_stricmp(token, "inline")) { PJ_LOG(4,(THIS_FILE, "Attribute crypto key method '%s' not supported!", token)); return PJMEDIA_SDP_EINATTR; } /* Key */ token = strtok(NULL, "| "); if (!token) { PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key")); return PJMEDIA_SDP_EINATTR; } tmp = pj_str(token); if (PJ_BASE64_TO_BASE256_LEN(tmp.slen) > MAX_KEY_LEN) { PJ_LOG(4,(THIS_FILE, "Key too long")); return PJMEDIA_SRTP_EINKEYLEN; } /* Decode key */ crypto->key.ptr = (char*) pj_pool_zalloc(pool, MAX_KEY_LEN); itmp = MAX_KEY_LEN; status = pj_base64_decode(&tmp, (pj_uint8_t*)crypto->key.ptr, &itmp); if (status != PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "Failed decoding crypto key from base64")); return status; } crypto->key.slen = itmp; return PJ_SUCCESS; } static pj_status_t transport_media_create(pjmedia_transport *tp, pj_pool_t *sdp_pool, unsigned options, const pjmedia_sdp_session *sdp_remote, unsigned media_index) { struct transport_srtp *srtp = (struct transport_srtp*) tp; unsigned member_tp_option; PJ_ASSERT_RETURN(tp, PJ_EINVAL); pj_bzero(&srtp->rx_policy_neg, sizeof(srtp->rx_policy_neg)); pj_bzero(&srtp->tx_policy_neg, sizeof(srtp->tx_policy_neg)); srtp->media_option = options; member_tp_option = options | PJMEDIA_TPMED_NO_TRANSPORT_CHECKING; srtp->offerer_side = sdp_remote == NULL; /* Validations */ if (srtp->offerer_side) { if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) goto BYPASS_SRTP; } else { pjmedia_sdp_media *m_rem; m_rem = sdp_remote->media[media_index]; /* Nothing to do on inactive media stream */ if (pjmedia_sdp_media_find_attr(m_rem, &ID_INACTIVE, NULL)) goto BYPASS_SRTP; /* Validate remote media transport based on SRTP usage option. */ switch (srtp->setting.use) { case PJMEDIA_SRTP_DISABLED: if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0) return PJMEDIA_SRTP_ESDPINTRANSPORT; goto BYPASS_SRTP; case PJMEDIA_SRTP_OPTIONAL: break; case PJMEDIA_SRTP_MANDATORY: if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) != 0) return PJMEDIA_SRTP_ESDPINTRANSPORT; break; } } goto PROPAGATE_MEDIA_CREATE; BYPASS_SRTP: srtp->bypass_srtp = PJ_TRUE; member_tp_option &= ~PJMEDIA_TPMED_NO_TRANSPORT_CHECKING; PROPAGATE_MEDIA_CREATE: return pjmedia_transport_media_create(srtp->member_tp, sdp_pool, member_tp_option, sdp_remote, media_index); } static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *sdp_remote, unsigned media_index) { struct transport_srtp *srtp = (struct transport_srtp*) tp; pjmedia_sdp_media *m_rem, *m_loc; enum { MAXLEN = 512 }; char buffer[MAXLEN]; int buffer_len; pj_status_t status; pjmedia_sdp_attr *attr; pj_str_t attr_value; unsigned i, j; PJ_ASSERT_RETURN(tp && sdp_pool && sdp_local, PJ_EINVAL); pj_bzero(&srtp->rx_policy_neg, sizeof(srtp->rx_policy_neg)); pj_bzero(&srtp->tx_policy_neg, sizeof(srtp->tx_policy_neg)); srtp->offerer_side = sdp_remote == NULL; m_rem = sdp_remote ? sdp_remote->media[media_index] : NULL; m_loc = sdp_local->media[media_index]; /* Bypass if media transport is not RTP/AVP or RTP/SAVP */ if (pj_stricmp(&m_loc->desc.transport, &ID_RTP_AVP) != 0 && pj_stricmp(&m_loc->desc.transport, &ID_RTP_SAVP) != 0) goto BYPASS_SRTP; /* Do nothing if we are in bypass */ if (srtp->bypass_srtp) goto BYPASS_SRTP; /* If the media is inactive, do nothing. */ /* No, we still need to process SRTP offer/answer even if the media is * marked as inactive, because the transport is still alive in this * case (e.g. for keep-alive). See: * http://trac.pjsip.org/repos/ticket/1079 */ /* if (pjmedia_sdp_media_find_attr(m_loc, &ID_INACTIVE, NULL) || (m_rem && pjmedia_sdp_media_find_attr(m_rem, &ID_INACTIVE, NULL))) goto BYPASS_SRTP; */ /* Check remote media transport & set local media transport * based on SRTP usage option. */ if (srtp->offerer_side) { /* Generate transport */ switch (srtp->setting.use) { case PJMEDIA_SRTP_DISABLED: goto BYPASS_SRTP; case PJMEDIA_SRTP_OPTIONAL: m_loc->desc.transport = (srtp->peer_use == PJMEDIA_SRTP_MANDATORY)? ID_RTP_SAVP : ID_RTP_AVP; break; case PJMEDIA_SRTP_MANDATORY: m_loc->desc.transport = ID_RTP_SAVP; break; } /* Generate crypto attribute if not yet */ if (pjmedia_sdp_media_find_attr(m_loc, &ID_CRYPTO, NULL) == NULL) { /* Offer only current active crypto if any, otherwise offer all * crypto-suites in the setting. */ for (i=0; isetting.crypto_count; ++i) { if (srtp->tx_policy.name.slen && pj_stricmp(&srtp->tx_policy.name, &srtp->setting.crypto[i].name) != 0) { continue; } buffer_len = MAXLEN; status = generate_crypto_attr_value(srtp->pool, buffer, &buffer_len, &srtp->setting.crypto[i], i+1); if (status != PJ_SUCCESS) return status; /* If buffer_len==0, just skip the crypto attribute. */ if (buffer_len) { pj_strset(&attr_value, buffer, buffer_len); attr = pjmedia_sdp_attr_create(srtp->pool, ID_CRYPTO.ptr, &attr_value); m_loc->attr[m_loc->attr_count++] = attr; } } } } else { /* Answerer side */ pj_assert(sdp_remote && m_rem); /* Generate transport */ switch (srtp->setting.use) { case PJMEDIA_SRTP_DISABLED: if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0) return PJMEDIA_SRTP_ESDPINTRANSPORT; goto BYPASS_SRTP; case PJMEDIA_SRTP_OPTIONAL: m_loc->desc.transport = m_rem->desc.transport; break; case PJMEDIA_SRTP_MANDATORY: if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) != 0) return PJMEDIA_SRTP_ESDPINTRANSPORT; m_loc->desc.transport = ID_RTP_SAVP; break; } /* Generate crypto attribute if not yet */ if (pjmedia_sdp_media_find_attr(m_loc, &ID_CRYPTO, NULL) == NULL) { pjmedia_srtp_crypto tmp_rx_crypto; pj_bool_t has_crypto_attr = PJ_FALSE; int matched_idx = -1; int chosen_tag = 0; int tags[64]; /* assume no more than 64 crypto attrs in a media */ unsigned cr_attr_count = 0; /* Find supported crypto-suite, get the tag, and assign policy_local */ for (i=0; iattr_count; ++i) { if (pj_stricmp(&m_rem->attr[i]->name, &ID_CRYPTO) != 0) continue; has_crypto_attr = PJ_TRUE; status = parse_attr_crypto(srtp->pool, m_rem->attr[i], &tmp_rx_crypto, &tags[cr_attr_count]); if (status != PJ_SUCCESS) return status; /* Check duplicated tag */ for (j=0; jsetting.crypto_count; ++j) if (pj_stricmp(&tmp_rx_crypto.name, &srtp->setting.crypto[j].name) == 0) { int cs_idx = get_crypto_idx(&tmp_rx_crypto.name); if (cs_idx == -1) return PJMEDIA_SRTP_ENOTSUPCRYPTO; /* Force to use test key */ /* bad keys for snom: */ //char *hex_test_key = "58b29c5c8f42308120ce857e439f2d" // "7810a8b10ad0b1446be5470faea496"; //char *hex_test_key = "20a26aac7ba062d356ff52b61e3993" // "ccb78078f12c64db94b9c294927fd0"; //pj_str_t *test_key = &srtp->setting.crypto[j].key; //char *raw_test_key = pj_pool_zalloc(srtp->pool, 64); //hex_string_to_octet_string( // raw_test_key, // hex_test_key, // strlen(hex_test_key)); //pj_strset(test_key, raw_test_key, // crypto_suites[cs_idx].cipher_key_len); /* EO Force to use test key */ if (tmp_rx_crypto.key.slen != (int)crypto_suites[cs_idx].cipher_key_len) return PJMEDIA_SRTP_EINKEYLEN; srtp->rx_policy_neg = tmp_rx_crypto; chosen_tag = tags[cr_attr_count]; matched_idx = j; break; } } cr_attr_count++; } /* Check crypto negotiation result */ switch (srtp->setting.use) { case PJMEDIA_SRTP_DISABLED: pj_assert(!"Should never reach here"); break; case PJMEDIA_SRTP_OPTIONAL: /* bypass SRTP when no crypto-attr and remote uses RTP/AVP */ if (!has_crypto_attr && pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP) == 0) goto BYPASS_SRTP; /* bypass SRTP when nothing match and remote uses RTP/AVP */ else if (matched_idx == -1 && pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP) == 0) goto BYPASS_SRTP; break; case PJMEDIA_SRTP_MANDATORY: /* Do nothing, intentional */ break; } /* No crypto attr */ if (!has_crypto_attr) { DEACTIVATE_MEDIA(sdp_pool, m_loc); return PJMEDIA_SRTP_ESDPREQCRYPTO; } /* No crypto match */ if (matched_idx == -1) { DEACTIVATE_MEDIA(sdp_pool, m_loc); return PJMEDIA_SRTP_ENOTSUPCRYPTO; } /* we have to generate crypto answer, * with srtp->tx_policy_neg matched the offer * and rem_tag contains matched offer tag. */ buffer_len = MAXLEN; status = generate_crypto_attr_value(srtp->pool, buffer, &buffer_len, &srtp->setting.crypto[matched_idx], chosen_tag); if (status != PJ_SUCCESS) return status; srtp->tx_policy_neg = srtp->setting.crypto[matched_idx]; /* If buffer_len==0, just skip the crypto attribute. */ if (buffer_len) { pj_strset(&attr_value, buffer, buffer_len); attr = pjmedia_sdp_attr_create(sdp_pool, ID_CRYPTO.ptr, &attr_value); m_loc->attr[m_loc->attr_count++] = attr; } /* At this point, we get valid rx_policy_neg & tx_policy_neg. */ } } goto PROPAGATE_MEDIA_CREATE; BYPASS_SRTP: /* Do not update this flag here as actually the media session hasn't been * updated. */ //srtp->bypass_srtp = PJ_TRUE; PROPAGATE_MEDIA_CREATE: return pjmedia_transport_encode_sdp(srtp->member_tp, sdp_pool, sdp_local, sdp_remote, media_index); } static pj_status_t transport_media_start(pjmedia_transport *tp, pj_pool_t *pool, const pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *sdp_remote, unsigned media_index) { struct transport_srtp *srtp = (struct transport_srtp*) tp; pjmedia_sdp_media *m_rem, *m_loc; pj_status_t status; unsigned i; PJ_ASSERT_RETURN(tp && pool && sdp_local && sdp_remote, PJ_EINVAL); m_rem = sdp_remote->media[media_index]; m_loc = sdp_local->media[media_index]; if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0) srtp->peer_use = PJMEDIA_SRTP_MANDATORY; else srtp->peer_use = PJMEDIA_SRTP_OPTIONAL; /* For answerer side, this function will just have to start SRTP */ /* Check remote media transport & set local media transport * based on SRTP usage option. */ if (srtp->offerer_side) { if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) { if (pjmedia_sdp_media_find_attr(m_rem, &ID_CRYPTO, NULL)) { DEACTIVATE_MEDIA(pool, m_loc); return PJMEDIA_SRTP_ESDPINCRYPTO; } goto BYPASS_SRTP; } else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) { // Regardless the answer's transport type (RTP/AVP or RTP/SAVP), // the answer must be processed through in optional mode. // Please note that at this point transport type is ensured to be // RTP/AVP or RTP/SAVP, see transport_media_create() //if (pj_stricmp(&m_rem->desc.transport, &m_loc->desc.transport)) { //DEACTIVATE_MEDIA(pool, m_loc); //return PJMEDIA_SDP_EINPROTO; //} } else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) { if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP)) { DEACTIVATE_MEDIA(pool, m_loc); return PJMEDIA_SDP_EINPROTO; } } } if (srtp->offerer_side) { /* find supported crypto-suite, get the tag, and assign policy_local */ pjmedia_srtp_crypto tmp_tx_crypto; pj_bool_t has_crypto_attr = PJ_FALSE; int rem_tag; for (i=0; iattr_count; ++i) { if (pj_stricmp(&m_rem->attr[i]->name, &ID_CRYPTO) != 0) continue; /* more than one crypto attribute in media answer */ if (has_crypto_attr) { DEACTIVATE_MEDIA(pool, m_loc); return PJMEDIA_SRTP_ESDPAMBIGUEANS; } has_crypto_attr = PJ_TRUE; status = parse_attr_crypto(srtp->pool, m_rem->attr[i], &tmp_tx_crypto, &rem_tag); if (status != PJ_SUCCESS) return status; /* our offer tag is always ordered by setting */ if (rem_tag < 1 || rem_tag > (int)srtp->setting.crypto_count) { DEACTIVATE_MEDIA(pool, m_loc); return PJMEDIA_SRTP_ESDPINCRYPTOTAG; } /* match the crypto name */ if (pj_stricmp(&tmp_tx_crypto.name, &srtp->setting.crypto[rem_tag-1].name) != 0) { DEACTIVATE_MEDIA(pool, m_loc); return PJMEDIA_SRTP_ECRYPTONOTMATCH; } srtp->tx_policy_neg = srtp->setting.crypto[rem_tag-1]; srtp->rx_policy_neg = tmp_tx_crypto; } if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) { /* should never reach here */ goto BYPASS_SRTP; } else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) { if (!has_crypto_attr) goto BYPASS_SRTP; } else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) { if (!has_crypto_attr) { DEACTIVATE_MEDIA(pool, m_loc); return PJMEDIA_SRTP_ESDPREQCRYPTO; } } /* At this point, we get valid rx_policy_neg & tx_policy_neg. */ } /* Make sure we have the SRTP policies */ if (srtp_crypto_empty(&srtp->tx_policy_neg) || srtp_crypto_empty(&srtp->rx_policy_neg)) { goto BYPASS_SRTP; } /* Reset probation counts */ srtp->probation_cnt = PROBATION_CNT_INIT; /* Got policy_local & policy_remote, let's initalize the SRTP */ /* Ticket #1075: media_start() is called whenever media description * gets updated, e.g: call hold, however we should restart SRTP only * when the SRTP policy settings are updated. */ if (srtp_crypto_cmp(&srtp->tx_policy_neg, &srtp->tx_policy) || srtp_crypto_cmp(&srtp->rx_policy_neg, &srtp->rx_policy)) { status = pjmedia_transport_srtp_start(tp, &srtp->tx_policy_neg, &srtp->rx_policy_neg); if (status != PJ_SUCCESS) return status; } srtp->bypass_srtp = PJ_FALSE; goto PROPAGATE_MEDIA_START; BYPASS_SRTP: srtp->bypass_srtp = PJ_TRUE; srtp->peer_use = PJMEDIA_SRTP_DISABLED; if (srtp->session_inited) { pjmedia_transport_srtp_stop(tp); } PROPAGATE_MEDIA_START: return pjmedia_transport_media_start(srtp->member_tp, pool, sdp_local, sdp_remote, media_index); } static pj_status_t transport_media_stop(pjmedia_transport *tp) { struct transport_srtp *srtp = (struct transport_srtp*) tp; pj_status_t status; PJ_ASSERT_RETURN(tp, PJ_EINVAL); status = pjmedia_transport_media_stop(srtp->member_tp); if (status != PJ_SUCCESS) PJ_LOG(4, (srtp->pool->obj_name, "SRTP failed stop underlying media transport.")); return pjmedia_transport_srtp_stop(tp); } /* Utility */ PJ_DEF(pj_status_t) pjmedia_transport_srtp_decrypt_pkt(pjmedia_transport *tp, pj_bool_t is_rtp, void *pkt, int *pkt_len) { transport_srtp *srtp = (transport_srtp *)tp; err_status_t err; if (srtp->bypass_srtp) return PJ_SUCCESS; PJ_ASSERT_RETURN(tp && pkt && (*pkt_len>0), PJ_EINVAL); PJ_ASSERT_RETURN(srtp->session_inited, PJ_EINVALIDOP); /* Make sure buffer is 32bit aligned */ PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return PJ_EINVAL); pj_lock_acquire(srtp->mutex); if (!srtp->session_inited) { pj_lock_release(srtp->mutex); return PJ_EINVALIDOP; } if (is_rtp) err = srtp_unprotect(srtp->srtp_rx_ctx, pkt, pkt_len); else err = srtp_unprotect_rtcp(srtp->srtp_rx_ctx, pkt, pkt_len); if (err != err_status_ok) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to unprotect SRTP, pkt size=%d, err=%s", *pkt_len, get_libsrtp_errstr(err))); } pj_lock_release(srtp->mutex); return (err==err_status_ok) ? PJ_SUCCESS : PJMEDIA_ERRNO_FROM_LIBSRTP(err); } #endif ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/transport_udp.c ================================================ /* $Id: transport_udp.c 4197 2012-07-05 07:26:29Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include /* Maximum size of incoming RTP packet */ #define RTP_LEN PJMEDIA_MAX_MRU /* Maximum size of incoming RTCP packet */ #define RTCP_LEN 600 /* Maximum pending write operations */ #define MAX_PENDING 4 static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; /* Pending write buffer */ typedef struct pending_write { char buffer[PJMEDIA_MAX_MTU]; pj_ioqueue_op_key_t op_key; } pending_write; struct transport_udp { pjmedia_transport base; /**< Base transport. */ pj_pool_t *pool; /**< Memory pool */ unsigned options; /**< Transport options. */ unsigned media_options; /**< Transport media options. */ void *user_data; /**< Only valid when attached */ pj_bool_t attached; /**< Has attachment? */ pj_sockaddr rem_rtp_addr; /**< Remote RTP address */ pj_sockaddr rem_rtcp_addr; /**< Remote RTCP address */ int addr_len; /**< Length of addresses. */ void (*rtp_cb)( void*, /**< To report incoming RTP. */ void*, pj_ssize_t); void (*rtcp_cb)( void*, /**< To report incoming RTCP. */ void*, pj_ssize_t); unsigned tx_drop_pct; /**< Percent of tx pkts to drop. */ unsigned rx_drop_pct; /**< Percent of rx pkts to drop. */ pj_sock_t rtp_sock; /**< RTP socket */ pj_sockaddr rtp_addr_name; /**< Published RTP address. */ pj_ioqueue_key_t *rtp_key; /**< RTP socket key in ioqueue */ pj_ioqueue_op_key_t rtp_read_op; /**< Pending read operation */ unsigned rtp_write_op_id;/**< Next write_op to use */ pending_write rtp_pending_write[MAX_PENDING]; /**< Pending write */ pj_sockaddr rtp_src_addr; /**< Actual packet src addr. */ unsigned rtp_src_cnt; /**< How many pkt from this addr. */ int rtp_addrlen; /**< Address length. */ char rtp_pkt[RTP_LEN];/**< Incoming RTP packet buffer */ pj_sock_t rtcp_sock; /**< RTCP socket */ pj_sockaddr rtcp_addr_name; /**< Published RTCP address. */ pj_sockaddr rtcp_src_addr; /**< Actual source RTCP address. */ unsigned rtcp_src_cnt; /**< How many pkt from this addr. */ int rtcp_addr_len; /**< Length of RTCP src address. */ pj_ioqueue_key_t *rtcp_key; /**< RTCP socket key in ioqueue */ pj_ioqueue_op_key_t rtcp_read_op; /**< Pending read operation */ pj_ioqueue_op_key_t rtcp_write_op; /**< Pending write operation */ char rtcp_pkt[RTCP_LEN];/**< Incoming RTCP packet buffer */ }; static void on_rx_rtp( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read); static void on_rx_rtcp(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read); /* * These are media transport operations. */ static pj_status_t transport_get_info (pjmedia_transport *tp, pjmedia_transport_info *info); static pj_status_t transport_attach (pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb)(void*, void*, pj_ssize_t), void (*rtcp_cb)(void*, void*, pj_ssize_t)); static void transport_detach (pjmedia_transport *tp, void *strm); static pj_status_t transport_send_rtp( pjmedia_transport *tp, const void *pkt, pj_size_t size); static pj_status_t transport_send_rtcp(pjmedia_transport *tp, const void *pkt, pj_size_t size); static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, const pj_sockaddr_t *addr, unsigned addr_len, const void *pkt, pj_size_t size); static pj_status_t transport_media_create(pjmedia_transport *tp, pj_pool_t *pool, unsigned options, const pjmedia_sdp_session *sdp_remote, unsigned media_index); static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *pool, pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *rem_sdp, unsigned media_index); static pj_status_t transport_media_start (pjmedia_transport *tp, pj_pool_t *pool, const pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *sdp_remote, unsigned media_index); static pj_status_t transport_media_stop(pjmedia_transport *tp); static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost); static pj_status_t transport_destroy (pjmedia_transport *tp); static pjmedia_transport_op transport_udp_op = { &transport_get_info, &transport_attach, &transport_detach, &transport_send_rtp, &transport_send_rtcp, &transport_send_rtcp2, &transport_media_create, &transport_encode_sdp, &transport_media_start, &transport_media_stop, &transport_simulate_lost, &transport_destroy }; /** * Create UDP stream transport. */ PJ_DEF(pj_status_t) pjmedia_transport_udp_create( pjmedia_endpt *endpt, const char *name, int port, unsigned options, pjmedia_transport **p_tp) { return pjmedia_transport_udp_create2(endpt, name, NULL, port, options, p_tp); } /** * Create UDP stream transport. */ PJ_DEF(pj_status_t) pjmedia_transport_udp_create2(pjmedia_endpt *endpt, const char *name, const pj_str_t *addr, int port, unsigned options, pjmedia_transport **p_tp) { return pjmedia_transport_udp_create3(endpt, pj_AF_INET(), name, addr, port, options, p_tp); } /** * Create UDP stream transport. */ PJ_DEF(pj_status_t) pjmedia_transport_udp_create3(pjmedia_endpt *endpt, int af, const char *name, const pj_str_t *addr, int port, unsigned options, pjmedia_transport **p_tp) { pjmedia_sock_info si; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && port && p_tp, PJ_EINVAL); pj_bzero(&si, sizeof(pjmedia_sock_info)); si.rtp_sock = si.rtcp_sock = PJ_INVALID_SOCKET; /* Create RTP socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &si.rtp_sock); if (status != PJ_SUCCESS) goto on_error; /* Bind RTP socket */ status = pj_sockaddr_init(af, &si.rtp_addr_name, addr, (pj_uint16_t)port); if (status != PJ_SUCCESS) goto on_error; status = pj_sock_bind(si.rtp_sock, &si.rtp_addr_name, pj_sockaddr_get_len(&si.rtp_addr_name)); if (status != PJ_SUCCESS) goto on_error; /* Create RTCP socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &si.rtcp_sock); if (status != PJ_SUCCESS) goto on_error; /* Bind RTCP socket */ status = pj_sockaddr_init(af, &si.rtcp_addr_name, addr, (pj_uint16_t)(port+1)); if (status != PJ_SUCCESS) goto on_error; status = pj_sock_bind(si.rtcp_sock, &si.rtcp_addr_name, pj_sockaddr_get_len(&si.rtcp_addr_name)); if (status != PJ_SUCCESS) goto on_error; /* Create UDP transport by attaching socket info */ return pjmedia_transport_udp_attach( endpt, name, &si, options, p_tp); on_error: if (si.rtp_sock != PJ_INVALID_SOCKET) pj_sock_close(si.rtp_sock); if (si.rtcp_sock != PJ_INVALID_SOCKET) pj_sock_close(si.rtcp_sock); return status; } /** * Create UDP stream transport from existing socket info. */ PJ_DEF(pj_status_t) pjmedia_transport_udp_attach( pjmedia_endpt *endpt, const char *name, const pjmedia_sock_info *si, unsigned options, pjmedia_transport **p_tp) { struct transport_udp *tp; pj_pool_t *pool; pj_ioqueue_t *ioqueue; pj_ioqueue_callback rtp_cb, rtcp_cb; pj_ssize_t size; unsigned i; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && si && p_tp, PJ_EINVAL); /* Get ioqueue instance */ ioqueue = pjmedia_endpt_get_ioqueue(endpt); if (name==NULL) name = "udp%p"; /* Create transport structure */ pool = pjmedia_endpt_create_pool(endpt, name, 512, 512); if (!pool) return PJ_ENOMEM; tp = PJ_POOL_ZALLOC_T(pool, struct transport_udp); tp->pool = pool; tp->options = options; pj_memcpy(tp->base.name, pool->obj_name, PJ_MAX_OBJ_NAME); tp->base.op = &transport_udp_op; tp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP; /* Copy socket infos */ tp->rtp_sock = si->rtp_sock; tp->rtp_addr_name = si->rtp_addr_name; tp->rtcp_sock = si->rtcp_sock; tp->rtcp_addr_name = si->rtcp_addr_name; /* If address is 0.0.0.0, use host's IP address */ if (!pj_sockaddr_has_addr(&tp->rtp_addr_name)) { pj_sockaddr hostip; status = pj_gethostip(tp->rtp_addr_name.addr.sa_family, &hostip); if (status != PJ_SUCCESS) goto on_error; pj_memcpy(pj_sockaddr_get_addr(&tp->rtp_addr_name), pj_sockaddr_get_addr(&hostip), pj_sockaddr_get_addr_len(&hostip)); } /* Same with RTCP */ if (!pj_sockaddr_has_addr(&tp->rtcp_addr_name)) { pj_memcpy(pj_sockaddr_get_addr(&tp->rtcp_addr_name), pj_sockaddr_get_addr(&tp->rtp_addr_name), pj_sockaddr_get_addr_len(&tp->rtp_addr_name)); } /* Setup RTP socket with the ioqueue */ pj_bzero(&rtp_cb, sizeof(rtp_cb)); rtp_cb.on_read_complete = &on_rx_rtp; status = pj_ioqueue_register_sock(pool, ioqueue, tp->rtp_sock, tp, &rtp_cb, &tp->rtp_key); if (status != PJ_SUCCESS) goto on_error; /* Disallow concurrency so that detach() and destroy() are * synchronized with the callback. */ status = pj_ioqueue_set_concurrency(tp->rtp_key, PJ_FALSE); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&tp->rtp_read_op, sizeof(tp->rtp_read_op)); for (i=0; irtp_pending_write); ++i) pj_ioqueue_op_key_init(&tp->rtp_pending_write[i].op_key, sizeof(tp->rtp_pending_write[i].op_key)); /* Kick of pending RTP read from the ioqueue */ tp->rtp_addrlen = sizeof(tp->rtp_src_addr); size = sizeof(tp->rtp_pkt); status = pj_ioqueue_recvfrom(tp->rtp_key, &tp->rtp_read_op, tp->rtp_pkt, &size, PJ_IOQUEUE_ALWAYS_ASYNC, &tp->rtp_src_addr, &tp->rtp_addrlen); if (status != PJ_EPENDING) goto on_error; /* Setup RTCP socket with ioqueue */ pj_bzero(&rtcp_cb, sizeof(rtcp_cb)); rtcp_cb.on_read_complete = &on_rx_rtcp; status = pj_ioqueue_register_sock(pool, ioqueue, tp->rtcp_sock, tp, &rtcp_cb, &tp->rtcp_key); if (status != PJ_SUCCESS) goto on_error; status = pj_ioqueue_set_concurrency(tp->rtcp_key, PJ_FALSE); if (status != PJ_SUCCESS) goto on_error; pj_ioqueue_op_key_init(&tp->rtcp_read_op, sizeof(tp->rtcp_read_op)); pj_ioqueue_op_key_init(&tp->rtcp_write_op, sizeof(tp->rtcp_write_op)); /* Kick of pending RTCP read from the ioqueue */ size = sizeof(tp->rtcp_pkt); tp->rtcp_addr_len = sizeof(tp->rtcp_src_addr); status = pj_ioqueue_recvfrom( tp->rtcp_key, &tp->rtcp_read_op, tp->rtcp_pkt, &size, PJ_IOQUEUE_ALWAYS_ASYNC, &tp->rtcp_src_addr, &tp->rtcp_addr_len); if (status != PJ_EPENDING) goto on_error; /* Done */ *p_tp = &tp->base; return PJ_SUCCESS; on_error: transport_destroy(&tp->base); return status; } /** * Close UDP transport. */ static pj_status_t transport_destroy(pjmedia_transport *tp) { struct transport_udp *udp = (struct transport_udp*) tp; /* Sanity check */ PJ_ASSERT_RETURN(tp, PJ_EINVAL); /* Must not close while application is using this */ //PJ_ASSERT_RETURN(!udp->attached, PJ_EINVALIDOP); if (udp->rtp_key) { /* This will block the execution if callback is still * being called. */ pj_ioqueue_unregister(udp->rtp_key); udp->rtp_key = NULL; udp->rtp_sock = PJ_INVALID_SOCKET; } else if (udp->rtp_sock != PJ_INVALID_SOCKET) { pj_sock_close(udp->rtp_sock); udp->rtp_sock = PJ_INVALID_SOCKET; } if (udp->rtcp_key) { pj_ioqueue_unregister(udp->rtcp_key); udp->rtcp_key = NULL; udp->rtcp_sock = PJ_INVALID_SOCKET; } else if (udp->rtcp_sock != PJ_INVALID_SOCKET) { pj_sock_close(udp->rtcp_sock); udp->rtcp_sock = PJ_INVALID_SOCKET; } pj_pool_release(udp->pool); return PJ_SUCCESS; } /* Notification from ioqueue about incoming RTP packet */ static void on_rx_rtp( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { struct transport_udp *udp; pj_status_t status; PJ_UNUSED_ARG(op_key); udp = (struct transport_udp*) pj_ioqueue_get_user_data(key); do { void (*cb)(void*,void*,pj_ssize_t); void *user_data; pj_bool_t discard = PJ_FALSE; cb = udp->rtp_cb; user_data = udp->user_data; /* Simulate packet lost on RX direction */ if (udp->rx_drop_pct) { if ((pj_rand() % 100) <= (int)udp->rx_drop_pct) { PJ_LOG(5,(udp->base.name, "RX RTP packet dropped because of pkt lost " "simulation")); discard = PJ_TRUE; } } /* See if source address of RTP packet is different than the * configured address, and switch RTP remote address to * source packet address after several consecutive packets * have been received. */ if (bytes_read>0 && (udp->options & PJMEDIA_UDP_NO_SRC_ADDR_CHECKING)==0) { if (pj_sockaddr_cmp(&udp->rem_rtp_addr, &udp->rtp_src_addr) == 0) { /* We're still receiving from rem_rtp_addr. Don't switch. */ udp->rtp_src_cnt = 0; } else { udp->rtp_src_cnt++; if (udp->rtp_src_cnt < PJMEDIA_RTP_NAT_PROBATION_CNT) { discard = PJ_TRUE; } else { char addr_text[80]; /* Set remote RTP address to source address */ pj_memcpy(&udp->rem_rtp_addr, &udp->rtp_src_addr, sizeof(pj_sockaddr)); /* Reset counter */ udp->rtp_src_cnt = 0; PJ_LOG(4,(udp->base.name, "Remote RTP address switched to %s", pj_sockaddr_print(&udp->rtp_src_addr, addr_text, sizeof(addr_text), 3))); /* Also update remote RTCP address if actual RTCP source * address is not heard yet. */ if (!pj_sockaddr_has_addr(&udp->rtcp_src_addr)) { pj_uint16_t port; pj_memcpy(&udp->rem_rtcp_addr, &udp->rem_rtp_addr, sizeof(pj_sockaddr)); pj_sockaddr_copy_addr(&udp->rem_rtcp_addr, &udp->rem_rtp_addr); port = (pj_uint16_t) (pj_sockaddr_get_port(&udp->rem_rtp_addr)+1); pj_sockaddr_set_port(&udp->rem_rtcp_addr, port); pj_memcpy(&udp->rtcp_src_addr, &udp->rem_rtcp_addr, sizeof(pj_sockaddr)); PJ_LOG(4,(udp->base.name, "Remote RTCP address switched to predicted" " address %s", pj_sockaddr_print(&udp->rtcp_src_addr, addr_text, sizeof(addr_text), 3))); } } } } if (!discard && udp->attached && cb) (*cb)(user_data, udp->rtp_pkt, bytes_read); bytes_read = sizeof(udp->rtp_pkt); udp->rtp_addrlen = sizeof(udp->rtp_src_addr); status = pj_ioqueue_recvfrom(udp->rtp_key, &udp->rtp_read_op, udp->rtp_pkt, &bytes_read, 0, &udp->rtp_src_addr, &udp->rtp_addrlen); if (status != PJ_EPENDING && status != PJ_SUCCESS) bytes_read = -status; } while (status != PJ_EPENDING && status != PJ_ECANCELLED); } /* Notification from ioqueue about incoming RTCP packet */ static void on_rx_rtcp(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { struct transport_udp *udp; pj_status_t status; PJ_UNUSED_ARG(op_key); udp = (struct transport_udp*) pj_ioqueue_get_user_data(key); do { void (*cb)(void*,void*,pj_ssize_t); void *user_data; cb = udp->rtcp_cb; user_data = udp->user_data; if (udp->attached && cb) (*cb)(user_data, udp->rtcp_pkt, bytes_read); /* Check if RTCP source address is the same as the configured * remote address, and switch the address when they are * different. */ if (bytes_read>0 && (udp->options & PJMEDIA_UDP_NO_SRC_ADDR_CHECKING)==0) { if (pj_sockaddr_cmp(&udp->rem_rtcp_addr, &udp->rtcp_src_addr) == 0) { /* Still receiving from rem_rtcp_addr, don't switch */ udp->rtcp_src_cnt = 0; } else { ++udp->rtcp_src_cnt; if (udp->rtcp_src_cnt >= PJMEDIA_RTCP_NAT_PROBATION_CNT ) { char addr_text[80]; udp->rtcp_src_cnt = 0; pj_memcpy(&udp->rem_rtcp_addr, &udp->rtcp_src_addr, sizeof(pj_sockaddr)); PJ_LOG(4,(udp->base.name, "Remote RTCP address switched to %s", pj_sockaddr_print(&udp->rtcp_src_addr, addr_text, sizeof(addr_text), 3))); } } } bytes_read = sizeof(udp->rtcp_pkt); udp->rtcp_addr_len = sizeof(udp->rtcp_src_addr); status = pj_ioqueue_recvfrom(udp->rtcp_key, &udp->rtcp_read_op, udp->rtcp_pkt, &bytes_read, 0, &udp->rtcp_src_addr, &udp->rtcp_addr_len); if (status != PJ_EPENDING && status != PJ_SUCCESS) bytes_read = -status; } while (status != PJ_EPENDING && status != PJ_ECANCELLED); } /* Called to get the transport info */ static pj_status_t transport_get_info(pjmedia_transport *tp, pjmedia_transport_info *info) { struct transport_udp *udp = (struct transport_udp*)tp; PJ_ASSERT_RETURN(tp && info, PJ_EINVAL); info->sock_info.rtp_sock = udp->rtp_sock; info->sock_info.rtp_addr_name = udp->rtp_addr_name; info->sock_info.rtcp_sock = udp->rtcp_sock; info->sock_info.rtcp_addr_name = udp->rtcp_addr_name; /* Get remote address originating RTP & RTCP. */ info->src_rtp_name = udp->rtp_src_addr; info->src_rtcp_name = udp->rtcp_src_addr; return PJ_SUCCESS; } /* Called by application to initialize the transport */ static pj_status_t transport_attach( pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb)(void*, void*, pj_ssize_t), void (*rtcp_cb)(void*, void*, pj_ssize_t)) { struct transport_udp *udp = (struct transport_udp*) tp; const pj_sockaddr *rtcp_addr; /* Validate arguments */ PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL); /* Must not be "attached" to existing application */ PJ_ASSERT_RETURN(!udp->attached, PJ_EINVALIDOP); /* Lock the ioqueue keys to make sure that callbacks are * not executed. See ticket #844 for details. */ pj_ioqueue_lock_key(udp->rtp_key); pj_ioqueue_lock_key(udp->rtcp_key); /* "Attach" the application: */ /* Copy remote RTP address */ pj_memcpy(&udp->rem_rtp_addr, rem_addr, addr_len); /* Copy remote RTP address, if one is specified. */ rtcp_addr = (const pj_sockaddr*) rem_rtcp; if (rtcp_addr && pj_sockaddr_has_addr(rtcp_addr)) { pj_memcpy(&udp->rem_rtcp_addr, rem_rtcp, addr_len); } else { unsigned rtcp_port; /* Otherwise guess the RTCP address from the RTP address */ pj_memcpy(&udp->rem_rtcp_addr, rem_addr, addr_len); rtcp_port = pj_sockaddr_get_port(&udp->rem_rtp_addr) + 1; pj_sockaddr_set_port(&udp->rem_rtcp_addr, (pj_uint16_t)rtcp_port); } /* Save the callbacks */ udp->rtp_cb = rtp_cb; udp->rtcp_cb = rtcp_cb; udp->user_data = user_data; /* Save address length */ udp->addr_len = addr_len; /* Last, mark transport as attached */ udp->attached = PJ_TRUE; /* Reset source RTP & RTCP addresses and counter */ pj_bzero(&udp->rtp_src_addr, sizeof(udp->rtp_src_addr)); pj_bzero(&udp->rtcp_src_addr, sizeof(udp->rtcp_src_addr)); udp->rtp_src_cnt = 0; udp->rtcp_src_cnt = 0; /* Set buffer size for RTP socket */ #if PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE { unsigned sobuf_size = PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE; pj_status_t status; status = pj_sock_setsockopt_sobuf(udp->rtp_sock, pj_SO_RCVBUF(), PJ_TRUE, &sobuf_size); if (status != PJ_SUCCESS) { pj_perror(3, tp->name, status, "Failed setting SO_RCVBUF"); } else { if (sobuf_size < PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE) { PJ_LOG(4, (tp->name, "Warning! Cannot set SO_RCVBUF as configured, " "now=%d, configured=%d", sobuf_size, PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE)); } else { PJ_LOG(5, (tp->name, "SO_RCVBUF set to %d", sobuf_size)); } } } #endif #if PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE { unsigned sobuf_size = PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE; pj_status_t status; status = pj_sock_setsockopt_sobuf(udp->rtp_sock, pj_SO_SNDBUF(), PJ_TRUE, &sobuf_size); if (status != PJ_SUCCESS) { pj_perror(3, tp->name, status, "Failed setting SO_SNDBUF"); } else { if (sobuf_size < PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE) { PJ_LOG(4, (tp->name, "Warning! Cannot set SO_SNDBUF as configured, " "now=%d, configured=%d", sobuf_size, PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE)); } else { PJ_LOG(5, (tp->name, "SO_SNDBUF set to %d", sobuf_size)); } } } #endif /* Unlock keys */ pj_ioqueue_unlock_key(udp->rtcp_key); pj_ioqueue_unlock_key(udp->rtp_key); return PJ_SUCCESS; } /* Called by application when it no longer needs the transport */ static void transport_detach( pjmedia_transport *tp, void *user_data) { struct transport_udp *udp = (struct transport_udp*) tp; pj_assert(tp); if (udp->attached) { /* Lock the ioqueue keys to make sure that callbacks are * not executed. See ticket #460 for details. */ pj_ioqueue_lock_key(udp->rtp_key); pj_ioqueue_lock_key(udp->rtcp_key); /* User data is unreferenced on Release build */ PJ_UNUSED_ARG(user_data); /* As additional checking, check if the same user data is specified */ pj_assert(user_data == udp->user_data); /* First, mark transport as unattached */ udp->attached = PJ_FALSE; /* Clear up application infos from transport */ udp->rtp_cb = NULL; udp->rtcp_cb = NULL; udp->user_data = NULL; /* Unlock keys */ pj_ioqueue_unlock_key(udp->rtcp_key); pj_ioqueue_unlock_key(udp->rtp_key); } } /* Called by application to send RTP packet */ static pj_status_t transport_send_rtp( pjmedia_transport *tp, const void *pkt, pj_size_t size) { struct transport_udp *udp = (struct transport_udp*)tp; pj_ssize_t sent; unsigned id; struct pending_write *pw; pj_status_t status; /* Must be attached */ PJ_ASSERT_RETURN(udp->attached, PJ_EINVALIDOP); /* Check that the size is supported */ PJ_ASSERT_RETURN(size <= PJMEDIA_MAX_MTU, PJ_ETOOBIG); /* Simulate packet lost on TX direction */ if (udp->tx_drop_pct) { if ((pj_rand() % 100) <= (int)udp->tx_drop_pct) { PJ_LOG(5,(udp->base.name, "TX RTP packet dropped because of pkt lost " "simulation")); return PJ_SUCCESS; } } id = udp->rtp_write_op_id; pw = &udp->rtp_pending_write[id]; /* We need to copy packet to our buffer because when the * operation is pending, caller might write something else * to the original buffer. */ pj_memcpy(pw->buffer, pkt, size); sent = size; status = pj_ioqueue_sendto( udp->rtp_key, &udp->rtp_pending_write[id].op_key, pw->buffer, &sent, 0, &udp->rem_rtp_addr, udp->addr_len); udp->rtp_write_op_id = (udp->rtp_write_op_id + 1) % PJ_ARRAY_SIZE(udp->rtp_pending_write); if (status==PJ_SUCCESS || status==PJ_EPENDING) return PJ_SUCCESS; return status; } /* Called by application to send RTCP packet */ static pj_status_t transport_send_rtcp(pjmedia_transport *tp, const void *pkt, pj_size_t size) { return transport_send_rtcp2(tp, NULL, 0, pkt, size); } /* Called by application to send RTCP packet */ static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, const pj_sockaddr_t *addr, unsigned addr_len, const void *pkt, pj_size_t size) { struct transport_udp *udp = (struct transport_udp*)tp; pj_ssize_t sent; pj_status_t status; PJ_ASSERT_RETURN(udp->attached, PJ_EINVALIDOP); if (addr == NULL) { addr = &udp->rem_rtcp_addr; addr_len = udp->addr_len; } sent = size; status = pj_ioqueue_sendto( udp->rtcp_key, &udp->rtcp_write_op, pkt, &sent, 0, addr, addr_len); if (status==PJ_SUCCESS || status==PJ_EPENDING) return PJ_SUCCESS; return status; } static pj_status_t transport_media_create(pjmedia_transport *tp, pj_pool_t *pool, unsigned options, const pjmedia_sdp_session *sdp_remote, unsigned media_index) { struct transport_udp *udp = (struct transport_udp*)tp; PJ_ASSERT_RETURN(tp && pool, PJ_EINVAL); udp->media_options = options; PJ_UNUSED_ARG(sdp_remote); PJ_UNUSED_ARG(media_index); return PJ_SUCCESS; } static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *pool, pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { struct transport_udp *udp = (struct transport_udp*)tp; /* Validate media transport */ /* By now, this transport only support RTP/AVP transport */ if ((udp->media_options & PJMEDIA_TPMED_NO_TRANSPORT_CHECKING) == 0) { pjmedia_sdp_media *m_rem, *m_loc; m_rem = rem_sdp? rem_sdp->media[media_index] : NULL; m_loc = sdp_local->media[media_index]; if (pj_stricmp(&m_loc->desc.transport, &ID_RTP_AVP) || (m_rem && pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP))) { pjmedia_sdp_media_deactivate(pool, m_loc); return PJMEDIA_SDP_EINPROTO; } } return PJ_SUCCESS; } static pj_status_t transport_media_start(pjmedia_transport *tp, pj_pool_t *pool, const pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *sdp_remote, unsigned media_index) { PJ_ASSERT_RETURN(tp && pool && sdp_local, PJ_EINVAL); PJ_UNUSED_ARG(tp); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(sdp_local); PJ_UNUSED_ARG(sdp_remote); PJ_UNUSED_ARG(media_index); return PJ_SUCCESS; } static pj_status_t transport_media_stop(pjmedia_transport *tp) { PJ_UNUSED_ARG(tp); return PJ_SUCCESS; } static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost) { struct transport_udp *udp = (struct transport_udp*)tp; PJ_ASSERT_RETURN(tp && pct_lost <= 100, PJ_EINVAL); if (dir & PJMEDIA_DIR_ENCODING) udp->tx_drop_pct = pct_lost; if (dir & PJMEDIA_DIR_DECODING) udp->rx_drop_pct = pct_lost; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/transport_zrtp.c ================================================ /* $Id$ */ /* * Copyright (C) 2010 Werner Dittmann * This is the pjmedia ZRTP transport module. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "../../third_party/zsrtp/include/ZsrtpCWrapper.h" #define THIS_FILE "transport_zrtp.c" #define MAX_RTP_BUFFER_LEN PJMEDIA_MAX_MTU #define MAX_RTCP_BUFFER_LEN PJMEDIA_MAX_MTU /* Transport functions prototypes */ static pj_status_t transport_get_info(pjmedia_transport *tp, pjmedia_transport_info *info); static pj_status_t transport_attach(pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb)(void*, void*, pj_ssize_t), void (*rtcp_cb)(void*, void*, pj_ssize_t)); static void transport_detach(pjmedia_transport *tp, void *strm); static pj_status_t transport_send_rtp(pjmedia_transport *tp, const void *pkt, pj_size_t size); static pj_status_t transport_send_rtcp(pjmedia_transport *tp, const void *pkt, pj_size_t size); static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, const pj_sockaddr_t *addr, unsigned addr_len, const void *pkt, pj_size_t size); static pj_status_t transport_media_create(pjmedia_transport *tp, pj_pool_t *sdp_pool, unsigned options, const pjmedia_sdp_session *rem_sdp, unsigned media_index); static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *rem_sdp, unsigned media_index); static pj_status_t transport_media_start(pjmedia_transport *tp, pj_pool_t *pool, const pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *rem_sdp, unsigned media_index); static pj_status_t transport_media_stop(pjmedia_transport *tp); static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost); static pj_status_t transport_destroy(pjmedia_transport *tp); /* The transport operations */ static struct pjmedia_transport_op tp_zrtp_op = { &transport_get_info, &transport_attach, &transport_detach, &transport_send_rtp, &transport_send_rtcp, &transport_send_rtcp2, &transport_media_create, &transport_encode_sdp, &transport_media_start, &transport_media_stop, &transport_simulate_lost, &transport_destroy }; /* The transport zrtp instance */ struct tp_zrtp { pjmedia_transport base; pj_pool_t *pool; /* Stream information. */ void *stream_user_data; void (*stream_rtp_cb)(void *user_data, void *pkt, pj_ssize_t); void (*stream_rtcp_cb)(void *user_data, void *pkt, pj_ssize_t); /* Add your own member here.. */ uint64_t protect; uint64_t unprotect; int32_t unprotect_err; int32_t refcount; pj_timer_heap_t* timer_heap; pj_timer_entry timeoutEntry; pj_mutex_t* zrtpMutex; ZsrtpContext* srtpReceive; ZsrtpContext* srtpSend; ZsrtpContextCtrl* srtcpReceive; ZsrtpContextCtrl* srtcpSend; void* sendBuffer; void* sendBufferCtrl; pj_uint8_t* zrtpBuffer; // pj_int32_t sendBufferLen; pj_uint32_t peerSSRC; /* stored in host order */ pj_uint32_t localSSRC; /* stored in host order */ char* clientIdString; pjmedia_transport *slave_tp; pjmedia_zrtp_cb cb; ZrtpContext* zrtpCtx; pj_uint16_t zrtpSeq; pj_bool_t enableZrtp; pj_bool_t started; pj_bool_t close_slave; pj_bool_t mitmMode; char cipher[128]; }; /* Forward declaration of thethe ZRTP specific callback functions that this adapter must implement */ static int32_t zrtp_sendDataZRTP(ZrtpContext* ctx, const uint8_t* data, int32_t length) ; static int32_t zrtp_activateTimer(ZrtpContext* ctx, int32_t time) ; static int32_t zrtp_cancelTimer(ZrtpContext* ctx) ; static void zrtp_sendInfo(ZrtpContext* ctx, int32_t severity, int32_t subCode) ; static int32_t zrtp_srtpSecretsReady(ZrtpContext* ctx, C_SrtpSecret_t* secrets, int32_t part) ; static void zrtp_srtpSecretsOff(ZrtpContext* ctx, int32_t part) ; static void zrtp_srtpSecretsOn(ZrtpContext* ctx, char* c, char* s, int32_t verified) ; static void zrtp_handleGoClear(ZrtpContext* ctx) ; static void zrtp_zrtpNegotiationFailed(ZrtpContext* ctx, int32_t severity, int32_t subCode) ; static void zrtp_zrtpNotSuppOther(ZrtpContext* ctx) ; static void zrtp_synchEnter(ZrtpContext* ctx) ; static void zrtp_synchLeave(ZrtpContext* ctx) ; static void zrtp_zrtpAskEnrollment(ZrtpContext* ctx, int32_t info) ; static void zrtp_zrtpInformEnrollment(ZrtpContext* ctx, int32_t info) ; static void zrtp_signSAS(ZrtpContext* ctx, uint8_t* sasHash) ; static int32_t zrtp_checkSASSignature(ZrtpContext* ctx, uint8_t* sasHash) ; /* The callback function structure for ZRTP */ static zrtp_Callbacks c_callbacks = { &zrtp_sendDataZRTP, &zrtp_activateTimer, &zrtp_cancelTimer, &zrtp_sendInfo, &zrtp_srtpSecretsReady, &zrtp_srtpSecretsOff, &zrtp_srtpSecretsOn, &zrtp_handleGoClear, &zrtp_zrtpNegotiationFailed, &zrtp_zrtpNotSuppOther, &zrtp_synchEnter, &zrtp_synchLeave, &zrtp_zrtpAskEnrollment, &zrtp_zrtpInformEnrollment, &zrtp_signSAS, &zrtp_checkSASSignature }; static void timer_callback(pj_timer_heap_t *ht, pj_timer_entry *e); static char clientId[] = "SIP SIMPLE Client SDK"; /* * Create the ZRTP transport. */ PJ_DEF(pj_status_t) pjmedia_transport_zrtp_create(pjmedia_endpt *endpt, pj_timer_heap_t *timer_heap, pjmedia_transport *tp, pjmedia_transport **p_tp, pj_bool_t close_slave) { pj_pool_t *pool; struct tp_zrtp *zrtp; PJ_ASSERT_RETURN(endpt && tp && p_tp, PJ_EINVAL); /* Create the pool and initialize the adapter structure */ pool = pjmedia_endpt_create_pool(endpt, "zrtp%p", 5*1024, 512); zrtp = PJ_POOL_ZALLOC_T(pool, struct tp_zrtp); zrtp->pool = pool; /* Initialize base pjmedia_transport */ pj_memcpy(zrtp->base.name, pool->obj_name, PJ_MAX_OBJ_NAME); zrtp->base.type = tp->type; zrtp->base.op = &tp_zrtp_op; /* Set the timer heap to be used for timers */ zrtp->timer_heap = timer_heap; /* Create the empty wrapper */ zrtp->zrtpCtx = zrtp_CreateWrapper(); /* Initialize standard values */ zrtp->clientIdString = clientId; /* Set standard name */ zrtp->zrtpSeq = 1; /* TODO: randomize */ pj_mutex_create_simple(zrtp->pool, "zrtp", &zrtp->zrtpMutex); zrtp->zrtpBuffer = pj_pool_zalloc(pool, MAX_ZRTP_SIZE); zrtp->sendBuffer = pj_pool_zalloc(pool, MAX_RTP_BUFFER_LEN); zrtp->sendBufferCtrl = pj_pool_zalloc(pool, MAX_RTCP_BUFFER_LEN); zrtp->slave_tp = tp; zrtp->close_slave = close_slave; zrtp->mitmMode = PJ_FALSE; /* Done */ zrtp->refcount++; *p_tp = &zrtp->base; return PJ_SUCCESS; } PJ_DECL(pj_status_t) pjmedia_transport_zrtp_initialize(pjmedia_transport *tp, const char *zidFilename, pj_bool_t autoEnable, pjmedia_zrtp_cb *cb) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; PJ_ASSERT_RETURN(tp, PJ_EINVAL); zrtp_initializeZrtpEngine(zrtp->zrtpCtx, &c_callbacks, zrtp->clientIdString, zidFilename, zrtp, zrtp->mitmMode); zrtp->enableZrtp = autoEnable; if (cb) pj_memcpy(&zrtp->cb, cb, sizeof(pjmedia_zrtp_cb)); return PJ_SUCCESS; } static void timer_callback(pj_timer_heap_t *ht, pj_timer_entry *e) { struct tp_zrtp *zrtp = (struct tp_zrtp*)e->user_data; zrtp_processTimeout(zrtp->zrtpCtx); PJ_UNUSED_ARG(ht); } /* * Here start with callback functions that support the ZRTP core */ static int32_t zrtp_sendDataZRTP(ZrtpContext* ctx, const uint8_t* data, int32_t length) { struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData; pj_uint16_t totalLen = length + 12; /* Fixed number of bytes of ZRTP header */ pj_uint32_t crc; pj_uint8_t* buffer = zrtp->zrtpBuffer; pj_uint16_t* pus; pj_uint32_t* pui; if ((totalLen) > MAX_ZRTP_SIZE) return 0; /* Get some handy pointers */ pus = (pj_uint16_t*)buffer; pui = (pj_uint32_t*)buffer; /* set up fixed ZRTP header */ *buffer = 0x10; /* invalid RTP version - refer to ZRTP spec chap 5 */ *(buffer + 1) = 0; pus[1] = pj_htons(zrtp->zrtpSeq++); pui[1] = pj_htonl(ZRTP_MAGIC); pui[2] = pj_htonl(zrtp->localSSRC); /* stored in host order */ /* Copy ZRTP message data behind the header data */ pj_memcpy(buffer+12, data, length); /* Setup and compute ZRTP CRC */ crc = zrtp_GenerateCksum(buffer, totalLen-CRC_SIZE); /* convert and store CRC in ZRTP packet.*/ crc = zrtp_EndCksum(crc); *(uint32_t*)(buffer+totalLen-CRC_SIZE) = pj_htonl(crc); /* Send the ZRTP packet using the slave transport */ return (pjmedia_transport_send_rtp(zrtp->slave_tp, buffer, totalLen) == PJ_SUCCESS) ? 1 : 0; } static int32_t zrtp_activateTimer(ZrtpContext* ctx, int32_t time) { pj_time_val timeout; pj_status_t status; struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData; timeout.sec = time / 1000; timeout.msec = time % 1000; pj_timer_entry_init(&zrtp->timeoutEntry, 0, zrtp, &timer_callback); status = pj_timer_heap_schedule(zrtp->timer_heap, &zrtp->timeoutEntry, &timeout); if (status == PJ_SUCCESS) return 1; else return 0; } static int32_t zrtp_cancelTimer(ZrtpContext* ctx) { pj_status_t status; struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData; status = pj_timer_heap_cancel(zrtp->timer_heap, &zrtp->timeoutEntry); if (status == PJ_SUCCESS) return 1; else return 0; } static void zrtp_sendInfo(ZrtpContext* ctx, int32_t severity, int32_t subCode) { struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData; if (zrtp->cb.show_message) zrtp->cb.show_message(&zrtp->base, severity, subCode); } static int32_t zrtp_srtpSecretsReady(ZrtpContext* ctx, C_SrtpSecret_t* secrets, int32_t part) { struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData; ZsrtpContext* recvCrypto; ZsrtpContext* senderCrypto; ZsrtpContextCtrl* recvCryptoCtrl; ZsrtpContextCtrl* senderCryptoCtrl; int cipher; int authn; int authKeyLen; // int srtcpAuthTagLen; if (secrets->authAlgorithm == zrtp_Sha1) { authn = SrtpAuthenticationSha1Hmac; authKeyLen = 20; // srtcpAuthTagLen = 80; // Always 80 bit for SRTCP / SHA1 } if (secrets->authAlgorithm == zrtp_Skein) { authn = SrtpAuthenticationSkeinHmac; authKeyLen = 32; // srtcpAuthTagLen = 64; // Always 64 bit for SRTCP / Skein } if (secrets->symEncAlgorithm == zrtp_Aes) cipher = SrtpEncryptionAESCM; if (secrets->symEncAlgorithm == zrtp_TwoFish) cipher = SrtpEncryptionTWOCM; if (part == ForSender) { // To encrypt packets: intiator uses initiator keys, // responder uses responder keys // Create a "half baked" crypto context first and store it. This is // the main crypto context for the sending part of the connection. if (secrets->role == Initiator) { senderCrypto = zsrtp_CreateWrapper(zrtp->localSSRC, 0, 0L, // keyderivation << 48, cipher, // encryption algo authn, // authtentication algo (unsigned char*)secrets->keyInitiator, // Master Key secrets->initKeyLen / 8, // Master Key length (unsigned char*)secrets->saltInitiator, // Master Salt secrets->initSaltLen / 8, // Master Salt length secrets->initKeyLen / 8, // encryption keyl authKeyLen, // authentication key len secrets->initSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag lenA senderCryptoCtrl = zsrtp_CreateWrapperCtrl(zrtp->localSSRC, cipher, // encryption algo authn, // authtication algo (unsigned char*)secrets->keyInitiator, // Master Key secrets->initKeyLen / 8, // Master Key length (unsigned char*)secrets->saltInitiator, // Master Salt secrets->initSaltLen / 8, // Master Salt length secrets->initKeyLen / 8, // encryption keyl authKeyLen, // authentication key len secrets->initSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag len // srtcpAuthTagLen / 8); // authentication tag len } else { senderCrypto = zsrtp_CreateWrapper(zrtp->localSSRC, 0, 0L, // keyderivation << 48, cipher, // encryption algo authn, // authtentication algo (unsigned char*)secrets->keyResponder, // Master Key secrets->respKeyLen / 8, // Master Key length (unsigned char*)secrets->saltResponder, // Master Salt secrets->respSaltLen / 8, // Master Salt length secrets->respKeyLen / 8, // encryption keyl authKeyLen, // authentication key len secrets->respSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag len senderCryptoCtrl = zsrtp_CreateWrapperCtrl(zrtp->localSSRC, cipher, // encryption algo authn, // authtication algo (unsigned char*)secrets->keyResponder, // Master Key secrets->respKeyLen / 8, // Master Key length (unsigned char*)secrets->saltResponder, // Master Salt secrets->respSaltLen / 8, // Master Salt length secrets->respKeyLen / 8, // encryption keyl authKeyLen, // authentication key len secrets->respSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag len // srtcpAuthTagLen / 8); // authentication tag len } if (senderCrypto == NULL) { return 0; } // Create a SRTP crypto context for real SSRC sender stream. // Note: key derivation can be done at this time only if the // key derivation rate is 0 (disabled). For ZRTP this is the // case: the key derivation is defined as 2^48 // which is effectively 0. zsrtp_deriveSrtpKeys(senderCrypto, 0L); zrtp->srtpSend = senderCrypto; zsrtp_deriveSrtpKeysCtrl(senderCryptoCtrl); zrtp->srtcpSend = senderCryptoCtrl; } if (part == ForReceiver) { // To decrypt packets: intiator uses responder keys, // responder initiator keys // See comment above. if (secrets->role == Initiator) { recvCrypto = zsrtp_CreateWrapper(zrtp->peerSSRC, 0, 0L, // keyderivation << 48, cipher, // encryption algo authn, // authtentication algo (unsigned char*)secrets->keyResponder, // Master Key secrets->respKeyLen / 8, // Master Key length (unsigned char*)secrets->saltResponder, // Master Salt secrets->respSaltLen / 8, // Master Salt length secrets->respKeyLen / 8, // encryption keyl authKeyLen, // authentication key len secrets->respSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag len recvCryptoCtrl = zsrtp_CreateWrapperCtrl(zrtp->peerSSRC, cipher, // encryption algo authn, // authtication algo (unsigned char*)secrets->keyResponder, // Master Key secrets->respKeyLen / 8, // Master Key length (unsigned char*)secrets->saltResponder, // Master Salt secrets->respSaltLen / 8, // Master Salt length secrets->respKeyLen / 8, // encryption keyl authKeyLen, // authentication key len secrets->respSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag len // srtcpAuthTagLen / 8); // authentication tag len } else { recvCrypto = zsrtp_CreateWrapper(zrtp->peerSSRC, 0, 0L, // keyderivation << 48, cipher, // encryption algo authn, // authtentication algo (unsigned char*)secrets->keyInitiator, // Master Key secrets->initKeyLen / 8, // Master Key length (unsigned char*)secrets->saltInitiator, // Master Salt secrets->initSaltLen / 8, // Master Salt length secrets->initKeyLen / 8, // encryption keyl authKeyLen, // authentication key len secrets->initSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag len recvCryptoCtrl = zsrtp_CreateWrapperCtrl(zrtp->peerSSRC, cipher, // encryption algo authn, // authtication algo (unsigned char*)secrets->keyInitiator, // Master Key secrets->initKeyLen / 8, // Master Key length (unsigned char*)secrets->saltInitiator, // Master Salt secrets->initSaltLen / 8, // Master Salt length secrets->initKeyLen / 8, // encryption keyl authKeyLen, // authentication key len secrets->initSaltLen / 8, // session salt len secrets->srtpAuthTagLen / 8); // authentication tag len // srtcpAuthTagLen / 8); // authentication tag len } if (recvCrypto == NULL) { return 0; } // Create a SRTP crypto context for real SSRC input stream. // If the sender didn't provide a SSRC just insert the template // into the queue. After we received the first packet the real // crypto context will be created. // // Note: key derivation can be done at this time only if the // key derivation rate is 0 (disabled). For ZRTP this is the // case: the key derivation is defined as 2^48 // which is effectively 0. zsrtp_deriveSrtpKeys(recvCrypto, 0L); zrtp->srtpReceive = recvCrypto; zsrtp_deriveSrtpKeysCtrl(recvCryptoCtrl); zrtp->srtcpReceive = recvCryptoCtrl; } return 1; } static void zrtp_srtpSecretsOff(ZrtpContext* ctx, int32_t part) { struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData; if (part == ForSender) { zsrtp_DestroyWrapper(zrtp->srtpSend); zsrtp_DestroyWrapperCtrl(zrtp->srtcpSend); zrtp->srtpSend = NULL; zrtp->srtcpSend = NULL; } if (part == ForReceiver) { zsrtp_DestroyWrapper(zrtp->srtpReceive); zsrtp_DestroyWrapperCtrl(zrtp->srtcpReceive); zrtp->srtpReceive = NULL; zrtp->srtcpReceive = NULL; } if (zrtp->cb.secure_off) zrtp->cb.secure_off(&zrtp->base); } static void zrtp_srtpSecretsOn(ZrtpContext* ctx, char* c, char* s, int32_t verified) { struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData; int len; len = strlen(c); if (len > sizeof(zrtp->cipher) - 1) len = sizeof(zrtp->cipher) - 1; memcpy(zrtp->cipher, c, len); zrtp->cipher[len] = '\0'; if (zrtp->cb.secure_on) zrtp->cb.secure_on(&zrtp->base, c); if (s && strlen(s) > 0 && zrtp->cb.show_sas) zrtp->cb.show_sas(&zrtp->base, s, verified); } static void zrtp_handleGoClear(ZrtpContext* ctx) { /* TODO: implement */ } static void zrtp_zrtpNegotiationFailed(ZrtpContext* ctx, int32_t severity, int32_t subCode) { struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData; if (zrtp->cb.negotiation_failed) zrtp->cb.negotiation_failed(&zrtp->base, severity, subCode); } static void zrtp_zrtpNotSuppOther(ZrtpContext* ctx) { struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData; if (zrtp->cb.not_supported_by_other) zrtp->cb.not_supported_by_other(&zrtp->base); } static void zrtp_synchEnter(ZrtpContext* ctx) { struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData; pj_mutex_lock(zrtp->zrtpMutex); } static void zrtp_synchLeave(ZrtpContext* ctx) { struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData; pj_mutex_unlock(zrtp->zrtpMutex); } static void zrtp_zrtpAskEnrollment(ZrtpContext* ctx, int32_t info) { struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData; if (zrtp->cb.ask_enrollment) zrtp->cb.ask_enrollment(&zrtp->base, info); } static void zrtp_zrtpInformEnrollment(ZrtpContext* ctx, int32_t info) { struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData; if (zrtp->cb.inform_enrollment) zrtp->cb.inform_enrollment(&zrtp->base, info); } static void zrtp_signSAS(ZrtpContext* ctx, uint8_t* sasHash) { struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData; if (zrtp->cb.sign_sas) zrtp->cb.sign_sas(&zrtp->base, sasHash); } static int32_t zrtp_checkSASSignature(ZrtpContext* ctx, uint8_t* sasHash) { struct tp_zrtp *zrtp = (struct tp_zrtp*)ctx->userData; if (zrtp->cb.check_sas_signature) return zrtp->cb.check_sas_signature(&zrtp->base, sasHash); return 0; } /* * Implement the specific ZRTP transport functions */ PJ_DEF(void) pjmedia_transport_zrtp_setEnableZrtp(pjmedia_transport *tp, pj_bool_t onOff) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; pj_assert(tp); zrtp->enableZrtp = onOff; } PJ_DEF(pj_bool_t) pjmedia_transport_zrtp_isEnableZrtp(pjmedia_transport *tp) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; PJ_ASSERT_RETURN(tp, PJ_FALSE); return zrtp->enableZrtp; } PJ_DEF(void) pjmedia_transport_zrtp_startZrtp(pjmedia_transport *tp) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; pj_assert(tp && zrtp->zrtpCtx); zrtp_startZrtpEngine(zrtp->zrtpCtx); zrtp->started = 1; } PJ_DEF(void) pjmedia_transport_zrtp_stopZrtp(pjmedia_transport *tp) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; pj_assert(tp && zrtp->zrtpCtx); zrtp_stopZrtpEngine(zrtp->zrtpCtx); zrtp->started = 0; } PJ_DEF(void) pjmedia_transport_zrtp_setLocalSSRC(pjmedia_transport *tp, uint32_t ssrc) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; pj_assert(tp); zrtp->localSSRC = ssrc; } PJ_DEF(pj_bool_t) pjmedia_transport_zrtp_isMitmMode(pjmedia_transport *tp) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; pj_assert(tp); return zrtp->mitmMode; } PJ_DEF(void) pjmedia_transport_zrtp_setMitmMode(pjmedia_transport *tp, pj_bool_t mitmMode) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; pj_assert(tp); zrtp->mitmMode = mitmMode; } PJ_DEF(ZrtpContext*) pjmedia_transport_zrtp_getZrtpContext(pjmedia_transport *tp) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; PJ_ASSERT_RETURN(tp, NULL); return zrtp->zrtpCtx; } PJ_DEF(void) pjmedia_transport_zrtp_setSASVerified(pjmedia_transport *tp, pj_bool_t verified) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; pj_assert(tp); if (verified) zrtp_SASVerified(zrtp->zrtpCtx); else zrtp_resetSASVerified(zrtp->zrtpCtx); } PJ_DEF(int) pjmedia_transport_zrtp_getPeerZid(pjmedia_transport *tp, unsigned char* data) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; pj_assert(tp); return zrtp_getPeerZid(zrtp->zrtpCtx, data); } PJ_DEF(char*) pjmedia_transport_zrtp_getPeerName(pjmedia_transport *tp) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; pj_assert(tp); return zrtp_getPeerName(zrtp->zrtpCtx); } PJ_DEF(void) pjmedia_transport_zrtp_putPeerName(pjmedia_transport *tp, const char *name) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; pj_assert(tp); zrtp_putPeerName(zrtp->zrtpCtx, name); } PJ_DEF(char*) pjmedia_transport_zrtp_getMultiStreamParameters(pjmedia_transport *tp, pj_int32_t *length) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; pj_assert(tp); return zrtp_getMultiStrParams(zrtp->zrtpCtx, length); } PJ_DEF(void) pjmedia_transport_zrtp_setMultiStreamParameters(pjmedia_transport *tp, const char *parameters, pj_int32_t length, pjmedia_transport *master_tp) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; struct tp_zrtp *master_zrtp = (struct tp_zrtp*)master_tp; pj_assert(tp); pj_assert(master_tp); zrtp_setMultiStrParams(zrtp->zrtpCtx, (char*) parameters, length, master_zrtp->zrtpCtx); } /* * get_info() is called to get the transport addresses to be put * in SDP c= line and a=rtcp line. */ static pj_status_t transport_get_info(pjmedia_transport *tp, pjmedia_transport_info *info) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; pjmedia_zrtp_info zrtp_info; int spc_info_idx; PJ_ASSERT_RETURN(tp && info, PJ_EINVAL); PJ_ASSERT_RETURN(info->specific_info_cnt < PJMEDIA_TRANSPORT_SPECIFIC_INFO_MAXCNT, PJ_ETOOMANY); zrtp_info.active = zrtp_inState(zrtp->zrtpCtx, SecureState) ? PJ_TRUE : PJ_FALSE; if (zrtp_info.active) memcpy(zrtp_info.cipher, zrtp->cipher, sizeof(zrtp->cipher)); else zrtp_info.cipher[0] = '\0'; spc_info_idx = info->specific_info_cnt++; info->spc_info[spc_info_idx].type = PJMEDIA_TRANSPORT_TYPE_ZRTP; pj_memcpy(&info->spc_info[spc_info_idx].buffer, &zrtp_info, sizeof(zrtp_info)); return pjmedia_transport_get_info(zrtp->slave_tp, info); } /* This is our RTP callback, that is called by the slave transport when it * receives RTP packet. */ static void transport_rtp_cb(void *user_data, void *pkt, pj_ssize_t size) { struct tp_zrtp *zrtp = (struct tp_zrtp*)user_data; pj_uint8_t* buffer = (pj_uint8_t*)pkt; int32_t newLen = 0; pj_status_t rc = PJ_SUCCESS; pj_assert(zrtp && zrtp->stream_rtcp_cb && pkt); // check if this could be a real RTP/SRTP packet. if ((*buffer & 0xf0) != 0x10) { // Could be real RTP, check if we are in secure mode if (zrtp->srtpReceive == NULL || size < 0) { zrtp->stream_rtp_cb(zrtp->stream_user_data, pkt, size); } else { rc = zsrtp_unprotect(zrtp->srtpReceive, pkt, size, &newLen); if (rc == 1) { zrtp->unprotect++; zrtp->stream_rtp_cb(zrtp->stream_user_data, pkt, newLen); zrtp->unprotect_err = 0; } else { if (zrtp->cb.show_message) { if (rc == -1) zrtp->cb.show_message(&zrtp->base, zrtp_Warning, zrtp_WarningSRTPauthError); else zrtp->cb.show_message(&zrtp->base, zrtp_Warning, zrtp_WarningSRTPreplayError); } zrtp->unprotect_err = rc; /* We failed to decrypt the packet, but forward it regardless to the slave * transport, it might not have been encrypted after all */ zrtp->stream_rtp_cb(zrtp->stream_user_data, pkt, size); } } if (!zrtp->started && zrtp->enableZrtp) pjmedia_transport_zrtp_startZrtp((pjmedia_transport *)zrtp); return; } // We assume all other packets are ZRTP packets here. Process // if ZRTP processing is enabled. Because valid RTP packets are // already handled we delete any packets here after processing. if (zrtp->enableZrtp && zrtp->zrtpCtx != NULL) { // Get CRC value into crc (see above how to compute the offset) pj_uint16_t temp = size - CRC_SIZE; pj_uint32_t crc = *(uint32_t*)(buffer + temp); crc = pj_ntohl(crc); if (!zrtp_CheckCksum(buffer, temp, crc)) { if (zrtp->cb.show_message) zrtp->cb.show_message(&zrtp->base, zrtp_Warning, zrtp_WarningCRCmismatch); return; } pj_uint32_t magic = *(pj_uint32_t*)(buffer + 4); magic = pj_ntohl(magic); // Check if it is really a ZRTP packet, return, no further processing if (magic != ZRTP_MAGIC) return; // cover the case if the other party sends _only_ ZRTP packets at the // beginning of a session. Start ZRTP in this case as well. if (!zrtp->started) { pjmedia_transport_zrtp_startZrtp((pjmedia_transport *)zrtp); } // this now points beyond the undefined and length field. // We need them, thus adjust unsigned char* zrtpMsg = (buffer + 12); // store peer's SSRC in host order, used when creating the CryptoContext zrtp->peerSSRC = *(pj_uint32_t*)(buffer + 8); zrtp->peerSSRC = pj_ntohl(zrtp->peerSSRC); zrtp_processZrtpMessage(zrtp->zrtpCtx, zrtpMsg, zrtp->peerSSRC, size); } } /* This is our RTCP callback, that is called by the slave transport when it * receives RTCP packet. */ static void transport_rtcp_cb(void *user_data, void *pkt, pj_ssize_t size) { struct tp_zrtp *zrtp = (struct tp_zrtp*)user_data; int32_t newLen = 0; pj_status_t rc = PJ_SUCCESS; pj_assert(zrtp && zrtp->stream_rtcp_cb); if (zrtp->srtcpReceive == NULL || size < 0) { zrtp->stream_rtcp_cb(zrtp->stream_user_data, pkt, size); } else { rc = zsrtp_unprotectCtrl(zrtp->srtcpReceive, pkt, size, &newLen); if (rc == 1) { /* Call stream's callback */ zrtp->stream_rtcp_cb(zrtp->stream_user_data, pkt, newLen); } else { // Testing: print some error output } } } /* * attach() is called by stream to register callbacks that we should * call on receipt of RTP and RTCP packets. */ static pj_status_t transport_attach(pjmedia_transport *tp, void *user_data, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, void (*rtp_cb)(void*, void*, pj_ssize_t), void (*rtcp_cb)(void*, void*, pj_ssize_t)) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; pj_status_t status; PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL); /* In this example, we will save the stream information and callbacks * to our structure, and we will register different RTP/RTCP callbacks * instead. */ pj_assert(zrtp->stream_user_data == NULL); zrtp->stream_user_data = user_data; zrtp->stream_rtp_cb = rtp_cb; zrtp->stream_rtcp_cb = rtcp_cb; status = pjmedia_transport_attach(zrtp->slave_tp, zrtp, rem_addr, rem_rtcp, addr_len, &transport_rtp_cb, &transport_rtcp_cb); if (status != PJ_SUCCESS) { zrtp->stream_user_data = NULL; zrtp->stream_rtp_cb = NULL; zrtp->stream_rtcp_cb = NULL; return status; } return PJ_SUCCESS; } /* * detach() is called when the media is terminated, and the stream is * to be disconnected from us. */ static void transport_detach(pjmedia_transport *tp, void *strm) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; PJ_UNUSED_ARG(strm); PJ_ASSERT_ON_FAIL(tp, return); if (zrtp->stream_user_data != NULL) { pjmedia_transport_detach(zrtp->slave_tp, zrtp); zrtp->stream_user_data = NULL; zrtp->stream_rtp_cb = NULL; zrtp->stream_rtcp_cb = NULL; } } /* * send_rtp() is called to send RTP packet. The "pkt" and "size" argument * contain both the RTP header and the payload. */ static pj_status_t transport_send_rtp(pjmedia_transport *tp, const void *pkt, pj_size_t size) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; pj_uint32_t* pui = (pj_uint32_t*)pkt; int32_t newLen = 0; pj_status_t rc = PJ_SUCCESS; PJ_ASSERT_RETURN(tp && pkt, PJ_EINVAL); if (!zrtp->started && zrtp->enableZrtp) { if (zrtp->localSSRC == 0) zrtp->localSSRC = pj_ntohl(pui[2]); /* Learn own SSRC before starting ZRTP */ pjmedia_transport_zrtp_startZrtp((pjmedia_transport *)zrtp); } if (zrtp->srtpSend == NULL) { return pjmedia_transport_send_rtp(zrtp->slave_tp, pkt, size); } else { if (size+80 > MAX_RTP_BUFFER_LEN) return PJ_ETOOBIG; pj_memcpy(zrtp->sendBuffer, pkt, size); rc = zsrtp_protect(zrtp->srtpSend, zrtp->sendBuffer, size, &newLen); zrtp->protect++; if (rc == 1) return pjmedia_transport_send_rtp(zrtp->slave_tp, zrtp->sendBuffer, newLen); else return PJ_EIGNORED; } } /* * send_rtcp() is called to send RTCP packet. The "pkt" and "size" argument * contain the RTCP packet. */ static pj_status_t transport_send_rtcp(pjmedia_transport *tp, const void *pkt, pj_size_t size) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; pj_status_t rc = PJ_SUCCESS; int32_t newLen = 0; PJ_ASSERT_RETURN(tp, PJ_EINVAL); /* You may do some processing to the RTCP packet here if you want. */ if (zrtp->srtcpSend == NULL) { return pjmedia_transport_send_rtcp(zrtp->slave_tp, pkt, size); } else { if (size+80 > MAX_RTCP_BUFFER_LEN) return PJ_ETOOBIG; pj_memcpy(zrtp->sendBufferCtrl, pkt, size); rc = zsrtp_protectCtrl(zrtp->srtcpSend, zrtp->sendBufferCtrl, size, &newLen); if (rc == 1) return pjmedia_transport_send_rtcp(zrtp->slave_tp, zrtp->sendBufferCtrl, newLen); else return PJ_EIGNORED; } /* Send the packet using the slave transport */ // return pjmedia_transport_send_rtcp(zrtp->slave_tp, pkt, size); } /* * This is another variant of send_rtcp(), with the alternate destination * address in the argument. */ static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, const pj_sockaddr_t *addr, unsigned addr_len, const void *pkt, pj_size_t size) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; PJ_ASSERT_RETURN(tp, PJ_EINVAL); return pjmedia_transport_send_rtcp2(zrtp->slave_tp, addr, addr_len, pkt, size); } /* * The media_create() is called when the transport is about to be used for * a new call. */ static pj_status_t transport_media_create(pjmedia_transport *tp, pj_pool_t *sdp_pool, unsigned options, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; PJ_ASSERT_RETURN(tp, PJ_EINVAL); /* if "rem_sdp" is not NULL, it means we are UAS. You may do some * inspections on the incoming SDP to verify that the SDP is acceptable * for us. If the SDP is not acceptable, we can reject the SDP by * returning non-PJ_SUCCESS. */ if (rem_sdp) { /* Do your stuff.. */ } /* Once we're done with our initialization, pass the call to the * slave transports to let it do it's own initialization too. */ return pjmedia_transport_media_create(zrtp->slave_tp, sdp_pool, options, rem_sdp, media_index); } /* * The encode_sdp() is called when we're about to send SDP to remote party, * either as SDP offer or as SDP answer. */ static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; int32_t numVersions, i; PJ_ASSERT_RETURN(tp, PJ_EINVAL); /* If "rem_sdp" is not NULL, it means we're encoding SDP answer. You may * do some more checking on the SDP's once again to make sure that * everything is okay before we send SDP. */ if (rem_sdp) { /* Do checking stuffs here.. */ } /* Add zrtp-hash attributes to both INVITE and 200 OK. */ numVersions = zrtp_getNumberSupportedVersions(zrtp->zrtpCtx); for (i = 0; i < numVersions; i++) { char *zrtp_hello_hash = zrtp_getHelloHash(zrtp->zrtpCtx, i); if (zrtp_hello_hash && *zrtp_hello_hash) { int zrtp_hello_hash_len = strlen(zrtp_hello_hash); pj_str_t *zrtp_hash_str = PJ_POOL_ALLOC_T(sdp_pool, pj_str_t); pjmedia_sdp_attr *zrtp_hash = NULL; zrtp_hash_str->ptr = zrtp_hello_hash; zrtp_hash_str->slen = zrtp_hello_hash_len; zrtp_hash = pjmedia_sdp_attr_create(sdp_pool, "zrtp-hash", zrtp_hash_str); if (zrtp_hash && pjmedia_sdp_attr_add(&local_sdp->media[media_index]->attr_count, local_sdp->media[media_index]->attr, zrtp_hash) == PJ_SUCCESS) { PJ_LOG(4, (THIS_FILE, "attribute added: a=zrtp-hash:%s", zrtp_hello_hash)); } else { PJ_LOG(4, (THIS_FILE, "error adding attribute: a=zrtp-hash:%s", zrtp_hello_hash)); } } } /* You may do anything to the local_sdp, e.g. adding new attributes, or * even modifying the SDP if you want. */ if (0) { /* Say we add a proprietary attribute here.. */ pjmedia_sdp_attr *my_attr; my_attr = PJ_POOL_ALLOC_T(sdp_pool, pjmedia_sdp_attr); pj_strdup2(sdp_pool, &my_attr->name, "X-zrtp"); pj_strdup2(sdp_pool, &my_attr->value, "some value"); pjmedia_sdp_attr_add(&local_sdp->media[media_index]->attr_count, local_sdp->media[media_index]->attr, my_attr); } /* And then pass the call to slave transport to let it encode its * information in the SDP. You may choose to call encode_sdp() to slave * first before adding your custom attributes if you want. */ return pjmedia_transport_encode_sdp(zrtp->slave_tp, sdp_pool, local_sdp, rem_sdp, media_index); } /* * The media_start() is called once both local and remote SDP have been * negotiated successfully, and the media is ready to start. Here we can start * committing our processing. */ static pj_status_t transport_media_start(pjmedia_transport *tp, pj_pool_t *pool, const pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; PJ_ASSERT_RETURN(tp, PJ_EINVAL); /* Do something.. */ /* And pass the call to the slave transport */ return pjmedia_transport_media_start(zrtp->slave_tp, pool, local_sdp, rem_sdp, media_index); } /* * The media_stop() is called when media has been stopped. */ static pj_status_t transport_media_stop(pjmedia_transport *tp) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; PJ_ASSERT_RETURN(tp, PJ_EINVAL); /* Do something.. */ PJ_LOG(4, (THIS_FILE, "Media stop - encrypted packets: %ld, decrypted packets: %ld", zrtp->protect, zrtp->unprotect)); /* And pass the call to the slave transport */ return pjmedia_transport_media_stop(zrtp->slave_tp); } /* * simulate_lost() is called to simulate packet lost */ static pj_status_t transport_simulate_lost(pjmedia_transport *tp, pjmedia_dir dir, unsigned pct_lost) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; PJ_ASSERT_RETURN(tp, PJ_EINVAL); return pjmedia_transport_simulate_lost(zrtp->slave_tp, dir, pct_lost); } /* * destroy() is called when the transport is no longer needed. */ static pj_status_t transport_destroy(pjmedia_transport *tp) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; PJ_ASSERT_RETURN(tp, PJ_EINVAL); PJ_LOG(4, (THIS_FILE, "Destroy - encrypted packets: %ld, decrypted packets: %ld", zrtp->protect, zrtp->unprotect)); /* close the slave transport in case */ if (zrtp->close_slave && zrtp->slave_tp) pjmedia_transport_close(zrtp->slave_tp); /* Self destruct.. */ zrtp_stopZrtpEngine(zrtp->zrtpCtx); zrtp_DestroyWrapper(zrtp->zrtpCtx); zrtp->zrtpCtx = NULL; /* In case mutex is being acquired by other thread */ pj_mutex_lock(zrtp->zrtpMutex); pj_mutex_unlock(zrtp->zrtpMutex); pj_mutex_destroy(zrtp->zrtpMutex); pj_pool_release(zrtp->pool); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/types.c ================================================ /* $Id: types.c 4411 2013-03-04 04:34:38Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include /** * Utility function to return the string name for a pjmedia_type. * * @param t The media type. * * @return String. */ PJ_DEF(const char*) pjmedia_type_name(pjmedia_type t) { const char *type_names[] = { "none", "audio", "video", "application", "unknown" }; pj_assert(t < (int)PJ_ARRAY_SIZE(type_names)); pj_assert(PJMEDIA_TYPE_UNKNOWN == 4); if (t < (int)PJ_ARRAY_SIZE(type_names)) return type_names[t]; else return "??"; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/vid_codec.c ================================================ /* $Id: vid_codec.c 4008 2012-04-03 04:03:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #define THIS_FILE "vid_codec.c" static pjmedia_vid_codec_mgr *def_vid_codec_mgr; /* Definition of default codecs parameters */ typedef struct pjmedia_vid_codec_default_param { pj_pool_t *pool; pjmedia_vid_codec_param *param; } pjmedia_vid_codec_default_param; /* * Codec manager maintains array of these structs for each supported * codec. */ typedef struct pjmedia_vid_codec_desc { pjmedia_vid_codec_info info; /**< Codec info. */ pjmedia_codec_id id; /**< Fully qualified name */ pjmedia_codec_priority prio; /**< Priority. */ pjmedia_vid_codec_factory *factory; /**< The factory. */ pjmedia_vid_codec_default_param *def_param; /**< Default codecs parameters. */ } pjmedia_vid_codec_desc; /* The declaration of video codec manager */ struct pjmedia_vid_codec_mgr { /** Pool factory instance. */ pj_pool_factory *pf; /** Codec manager mutex. */ pj_mutex_t *mutex; /** List of codec factories registered to codec manager. */ pjmedia_vid_codec_factory factory_list; /** Number of supported codecs. */ unsigned codec_cnt; /** Array of codec descriptor. */ pjmedia_vid_codec_desc codec_desc[PJMEDIA_CODEC_MGR_MAX_CODECS]; }; /* Sort codecs in codec manager based on priorities */ static void sort_codecs(pjmedia_vid_codec_mgr *mgr); /* * Duplicate video codec parameter. */ PJ_DEF(pjmedia_vid_codec_param*) pjmedia_vid_codec_param_clone( pj_pool_t *pool, const pjmedia_vid_codec_param *src) { pjmedia_vid_codec_param *p; unsigned i; PJ_ASSERT_RETURN(pool && src, NULL); p = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec_param); /* Update codec param */ pj_memcpy(p, src, sizeof(pjmedia_vid_codec_param)); for (i = 0; i < src->dec_fmtp.cnt; ++i) { pj_strdup(pool, &p->dec_fmtp.param[i].name, &src->dec_fmtp.param[i].name); pj_strdup(pool, &p->dec_fmtp.param[i].val, &src->dec_fmtp.param[i].val); } for (i = 0; i < src->enc_fmtp.cnt; ++i) { pj_strdup(pool, &p->enc_fmtp.param[i].name, &src->enc_fmtp.param[i].name); pj_strdup(pool, &p->enc_fmtp.param[i].val, &src->enc_fmtp.param[i].val); } return p; } /* * Initialize codec manager. */ PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_create( pj_pool_t *pool, pjmedia_vid_codec_mgr **p_mgr) { pjmedia_vid_codec_mgr *mgr; pj_status_t status; PJ_ASSERT_RETURN(pool, PJ_EINVAL); mgr = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec_mgr); mgr->pf = pool->factory; pj_list_init (&mgr->factory_list); mgr->codec_cnt = 0; /* Create mutex */ status = pj_mutex_create_recursive(pool, "vid-codec-mgr", &mgr->mutex); if (status != PJ_SUCCESS) return status; if (!def_vid_codec_mgr) def_vid_codec_mgr = mgr; if (p_mgr) *p_mgr = mgr; return PJ_SUCCESS; } /* * Initialize codec manager. */ PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_destroy (pjmedia_vid_codec_mgr *mgr) { if (!mgr) mgr = def_vid_codec_mgr; PJ_ASSERT_RETURN(mgr, PJ_EINVAL); /* Destroy mutex */ if (mgr->mutex) pj_mutex_destroy(mgr->mutex); /* Just for safety, set codec manager states to zero */ pj_bzero(mgr, sizeof(pjmedia_vid_codec_mgr)); if (mgr == def_vid_codec_mgr) def_vid_codec_mgr = NULL; return PJ_SUCCESS; } PJ_DEF(pjmedia_vid_codec_mgr*) pjmedia_vid_codec_mgr_instance(void) { //pj_assert(def_vid_codec_mgr); return def_vid_codec_mgr; } PJ_DEF(void) pjmedia_vid_codec_mgr_set_instance(pjmedia_vid_codec_mgr* mgr) { def_vid_codec_mgr = mgr; } /* * Register a codec factory. */ PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_register_factory( pjmedia_vid_codec_mgr *mgr, pjmedia_vid_codec_factory *factory) { pjmedia_vid_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS]; unsigned i, count; pj_status_t status; PJ_ASSERT_RETURN(factory, PJ_EINVAL); if (!mgr) mgr = def_vid_codec_mgr; PJ_ASSERT_RETURN(mgr, PJ_EINVAL); /* Enum codecs */ count = PJ_ARRAY_SIZE(info); status = factory->op->enum_info(factory, &count, info); if (status != PJ_SUCCESS) return status; pj_mutex_lock(mgr->mutex); /* Check codec count */ if (count + mgr->codec_cnt > PJ_ARRAY_SIZE(mgr->codec_desc)) { pj_mutex_unlock(mgr->mutex); return PJ_ETOOMANY; } /* Save the codecs */ for (i=0; icodec_desc[mgr->codec_cnt+i], &info[i], sizeof(pjmedia_vid_codec_info)); mgr->codec_desc[mgr->codec_cnt+i].prio = PJMEDIA_CODEC_PRIO_NORMAL; mgr->codec_desc[mgr->codec_cnt+i].factory = factory; pjmedia_vid_codec_info_to_id( &info[i], mgr->codec_desc[mgr->codec_cnt+i].id, sizeof(pjmedia_codec_id)); } /* Update count */ mgr->codec_cnt += count; /* Re-sort codec based on priorities */ sort_codecs(mgr); /* Add factory to the list */ pj_list_push_back(&mgr->factory_list, factory); pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } /* * Unregister a codec factory. */ PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_unregister_factory( pjmedia_vid_codec_mgr *mgr, pjmedia_vid_codec_factory *factory) { unsigned i; PJ_ASSERT_RETURN(factory, PJ_EINVAL); if (!mgr) mgr = def_vid_codec_mgr; PJ_ASSERT_RETURN(mgr, PJ_EINVAL); pj_mutex_lock(mgr->mutex); /* Factory must be registered. */ if (pj_list_find_node(&mgr->factory_list, factory) != factory) { pj_mutex_unlock(mgr->mutex); return PJ_ENOTFOUND; } /* Erase factory from the factory list */ pj_list_erase(factory); /* Remove all supported codecs from the codec manager that were created * by the specified factory. */ for (i=0; icodec_cnt; ) { if (mgr->codec_desc[i].factory == factory) { /* Remove the codec from array of codec descriptions */ pj_array_erase(mgr->codec_desc, sizeof(mgr->codec_desc[0]), mgr->codec_cnt, i); --mgr->codec_cnt; } else { ++i; } } pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } /* * Enum all codecs. */ PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_enum_codecs( pjmedia_vid_codec_mgr *mgr, unsigned *count, pjmedia_vid_codec_info codecs[], unsigned *prio) { unsigned i; PJ_ASSERT_RETURN(count && codecs, PJ_EINVAL); if (!mgr) mgr = def_vid_codec_mgr; PJ_ASSERT_RETURN(mgr, PJ_EINVAL); pj_mutex_lock(mgr->mutex); if (*count > mgr->codec_cnt) *count = mgr->codec_cnt; for (i=0; i<*count; ++i) { pj_memcpy(&codecs[i], &mgr->codec_desc[i].info, sizeof(pjmedia_vid_codec_info)); } if (prio) { for (i=0; i < *count; ++i) prio[i] = mgr->codec_desc[i].prio; } pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } /* * Get codec info for the specified payload type. */ PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_codec_info( pjmedia_vid_codec_mgr *mgr, unsigned pt, const pjmedia_vid_codec_info **p_info) { unsigned i; PJ_ASSERT_RETURN(p_info, PJ_EINVAL); if (!mgr) mgr = def_vid_codec_mgr; PJ_ASSERT_RETURN(mgr, PJ_EINVAL); pj_mutex_lock(mgr->mutex); for (i=0; icodec_cnt; ++i) { if (mgr->codec_desc[i].info.pt == pt) { *p_info = &mgr->codec_desc[i].info; pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } } pj_mutex_unlock(mgr->mutex); return PJMEDIA_CODEC_EUNSUP; } PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_codec_info2( pjmedia_vid_codec_mgr *mgr, pjmedia_format_id fmt_id, const pjmedia_vid_codec_info **p_info) { unsigned i; PJ_ASSERT_RETURN(p_info, PJ_EINVAL); if (!mgr) mgr = def_vid_codec_mgr; PJ_ASSERT_RETURN(mgr, PJ_EINVAL); pj_mutex_lock(mgr->mutex); for (i=0; icodec_cnt; ++i) { if (mgr->codec_desc[i].info.fmt_id == fmt_id) { *p_info = &mgr->codec_desc[i].info; pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } } pj_mutex_unlock(mgr->mutex); return PJMEDIA_CODEC_EUNSUP; } /* * Convert codec info struct into a unique codec identifier. * A codec identifier looks something like "H263/34". */ PJ_DEF(char*) pjmedia_vid_codec_info_to_id( const pjmedia_vid_codec_info *info, char *id, unsigned max_len ) { int len; PJ_ASSERT_RETURN(info && id && max_len, NULL); len = pj_ansi_snprintf(id, max_len, "%.*s/%u", (int)info->encoding_name.slen, info->encoding_name.ptr, info->pt); if (len < 1 || len >= (int)max_len) { id[0] = '\0'; return NULL; } return id; } /* * Find codecs by the unique codec identifier. This function will find * all codecs that match the codec identifier prefix. For example, if * "L16" is specified, then it will find "L16/8000/1", "L16/16000/1", * and so on, up to the maximum count specified in the argument. */ PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_find_codecs_by_id( pjmedia_vid_codec_mgr *mgr, const pj_str_t *codec_id, unsigned *count, const pjmedia_vid_codec_info *p_info[], unsigned prio[]) { unsigned i, found = 0; PJ_ASSERT_RETURN(codec_id && count && *count, PJ_EINVAL); if (!mgr) mgr = def_vid_codec_mgr; PJ_ASSERT_RETURN(mgr, PJ_EINVAL); pj_mutex_lock(mgr->mutex); for (i=0; icodec_cnt; ++i) { if (codec_id->slen == 0 || pj_strnicmp2(codec_id, mgr->codec_desc[i].id, codec_id->slen) == 0) { if (p_info) p_info[found] = &mgr->codec_desc[i].info; if (prio) prio[found] = mgr->codec_desc[i].prio; ++found; if (found >= *count) break; } } pj_mutex_unlock(mgr->mutex); *count = found; return found ? PJ_SUCCESS : PJ_ENOTFOUND; } /* Swap two codecs positions in codec manager */ static void swap_codec(pjmedia_vid_codec_mgr *mgr, unsigned i, unsigned j) { pjmedia_vid_codec_desc tmp; pj_memcpy(&tmp, &mgr->codec_desc[i], sizeof(pjmedia_vid_codec_desc)); pj_memcpy(&mgr->codec_desc[i], &mgr->codec_desc[j], sizeof(pjmedia_vid_codec_desc)); pj_memcpy(&mgr->codec_desc[j], &tmp, sizeof(pjmedia_vid_codec_desc)); } /* Sort codecs in codec manager based on priorities */ static void sort_codecs(pjmedia_vid_codec_mgr *mgr) { unsigned i; /* Re-sort */ for (i=0; icodec_cnt; ++i) { unsigned j, max; for (max=i, j=i+1; jcodec_cnt; ++j) { if (mgr->codec_desc[j].prio > mgr->codec_desc[max].prio) max = j; } if (max != i) swap_codec(mgr, i, max); } /* Change PJMEDIA_CODEC_PRIO_HIGHEST codecs to NEXT_HIGHER */ for (i=0; icodec_cnt; ++i) { if (mgr->codec_desc[i].prio == PJMEDIA_CODEC_PRIO_HIGHEST) mgr->codec_desc[i].prio = PJMEDIA_CODEC_PRIO_NEXT_HIGHER; else break; } } /** * Set codec priority. The codec priority determines the order of * the codec in the SDP created by the endpoint. If more than one codecs * are found with the same codec_id prefix, then the function sets the * priorities of all those codecs. */ PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_set_codec_priority( pjmedia_vid_codec_mgr *mgr, const pj_str_t *codec_id, pj_uint8_t prio) { unsigned i, found = 0; PJ_ASSERT_RETURN(codec_id, PJ_EINVAL); if (!mgr) mgr = def_vid_codec_mgr; PJ_ASSERT_RETURN(mgr, PJ_EINVAL); pj_mutex_lock(mgr->mutex); /* Update the priorities of affected codecs */ for (i=0; icodec_cnt; ++i) { if (codec_id->slen == 0 || pj_strnicmp2(codec_id, mgr->codec_desc[i].id, codec_id->slen) == 0) { mgr->codec_desc[i].prio = (pjmedia_codec_priority) prio; ++found; } } if (!found) { pj_mutex_unlock(mgr->mutex); return PJ_ENOTFOUND; } /* Re-sort codecs */ sort_codecs(mgr); pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } /* * Allocate one codec. */ PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_alloc_codec( pjmedia_vid_codec_mgr *mgr, const pjmedia_vid_codec_info *info, pjmedia_vid_codec **p_codec) { pjmedia_vid_codec_factory *factory; pj_status_t status; PJ_ASSERT_RETURN(info && p_codec, PJ_EINVAL); if (!mgr) mgr = def_vid_codec_mgr; PJ_ASSERT_RETURN(mgr, PJ_EINVAL); *p_codec = NULL; pj_mutex_lock(mgr->mutex); factory = mgr->factory_list.next; while (factory != &mgr->factory_list) { if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) { status = (*factory->op->alloc_codec)(factory, info, p_codec); if (status == PJ_SUCCESS) { pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } } factory = factory->next; } pj_mutex_unlock(mgr->mutex); return PJMEDIA_CODEC_EUNSUP; } /* * Get default codec parameter. */ PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_default_param( pjmedia_vid_codec_mgr *mgr, const pjmedia_vid_codec_info *info, pjmedia_vid_codec_param *param ) { pjmedia_vid_codec_factory *factory; pj_status_t status; pjmedia_codec_id codec_id; pjmedia_vid_codec_desc *codec_desc = NULL; unsigned i; PJ_ASSERT_RETURN(info && param, PJ_EINVAL); if (!mgr) mgr = def_vid_codec_mgr; PJ_ASSERT_RETURN(mgr, PJ_EINVAL); if (!pjmedia_vid_codec_info_to_id(info, (char*)&codec_id, sizeof(codec_id))) return PJ_EINVAL; pj_mutex_lock(mgr->mutex); /* First, lookup default param in codec desc */ for (i=0; i < mgr->codec_cnt; ++i) { if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) { codec_desc = &mgr->codec_desc[i]; break; } } /* If we found the codec and its default param is set, return it */ if (codec_desc && codec_desc->def_param) { pj_memcpy(param, codec_desc->def_param->param, sizeof(pjmedia_vid_codec_param)); pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } /* Otherwise query the default param from codec factory */ factory = mgr->factory_list.next; while (factory != &mgr->factory_list) { if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) { status = (*factory->op->default_attr)(factory, info, param); if (status == PJ_SUCCESS) { /* Check for invalid max_bps. */ //if (param->info.max_bps < param->info.avg_bps) // param->info.max_bps = param->info.avg_bps; pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } } factory = factory->next; } pj_mutex_unlock(mgr->mutex); return PJMEDIA_CODEC_EUNSUP; } /* * Set default codec parameter. */ PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_set_default_param( pjmedia_vid_codec_mgr *mgr, const pjmedia_vid_codec_info *info, const pjmedia_vid_codec_param *param ) { unsigned i; pjmedia_codec_id codec_id; pjmedia_vid_codec_desc *codec_desc = NULL; pj_pool_t *pool, *old_pool = NULL; pjmedia_vid_codec_default_param *p; PJ_ASSERT_RETURN(info, PJ_EINVAL); if (!mgr) mgr = def_vid_codec_mgr; PJ_ASSERT_RETURN(mgr, PJ_EINVAL); if (!pjmedia_vid_codec_info_to_id(info, (char*)&codec_id, sizeof(codec_id))) return PJ_EINVAL; pj_mutex_lock(mgr->mutex); /* Lookup codec desc */ for (i=0; i < mgr->codec_cnt; ++i) { if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) { codec_desc = &mgr->codec_desc[i]; break; } } /* Codec not found */ if (!codec_desc) { pj_mutex_unlock(mgr->mutex); return PJMEDIA_CODEC_EUNSUP; } /* If codec param is previously set */ if (codec_desc->def_param) { pj_assert(codec_desc->def_param->pool); old_pool = codec_desc->def_param->pool; codec_desc->def_param = NULL; } /* When param is set to NULL, i.e: setting default codec param to library * default setting, just return PJ_SUCCESS. */ if (NULL == param) { pj_mutex_unlock(mgr->mutex); if (old_pool) pj_pool_release(old_pool); return PJ_SUCCESS; } /* Create new default codec param instance */ pool = pj_pool_create(mgr->pf, (char*)codec_id, 256, 256, NULL); codec_desc->def_param = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec_default_param); p = codec_desc->def_param; p->pool = pool; /* Update codec default param */ p->param = pjmedia_vid_codec_param_clone(pool, param); if (!p->param) { pj_mutex_unlock(mgr->mutex); return PJ_EINVAL; } codec_desc->def_param = p; pj_mutex_unlock(mgr->mutex); /* Release old pool at the very end, as application tends to apply changes * to the existing/old codec param fetched using * pjmedia_vid_codec_mgr_get_default_param() which doesn't do deep clone. */ if (old_pool) pj_pool_release(old_pool); return PJ_SUCCESS; } /* * Dealloc codec. */ PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_dealloc_codec(pjmedia_vid_codec_mgr *mgr, pjmedia_vid_codec *codec) { PJ_ASSERT_RETURN(codec, PJ_EINVAL); if (!mgr) mgr = def_vid_codec_mgr; PJ_ASSERT_RETURN(mgr, PJ_EINVAL); return (*codec->factory->op->dealloc_codec)(codec->factory, codec); } #endif /* PJMEDIA_HAS_VIDEO */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/vid_codec_util.c ================================================ /* $Id: vid_codec_util.c 4362 2013-02-21 14:51:56Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #define THIS_FILE "vid_codec_util.c" /* If this is set to non-zero, H.264 custom negotiation will require * "profile-level-id" and "packetization-mode" to be exact match to * get a successful negotiation. Note that flexible answer (updating * SDP answer to match remote offer) is always active regardless the * value of this macro. */ #define H264_STRICT_SDP_NEGO 0 /* Default frame rate, if not specified */ #define DEFAULT_H264_FPS_NUM 10 #define DEFAULT_H264_FPS_DENUM 1 /* Default aspect ratio, if not specified */ #define DEFAULT_H264_RATIO_NUM 4 #define DEFAULT_H264_RATIO_DENUM 3 /* ITU resolution definition */ struct mpi_resolution_t { pj_str_t name; pjmedia_rect_size size; } mpi_resolutions [] = { {{"CIF",3}, {352,288}}, {{"QCIF",4}, {176,144}}, {{"SQCIF",5}, {88,72}}, {{"CIF4",4}, {704,576}}, {{"CIF16",5}, {1408,1142}}, }; #define CALC_H264_MB_NUM(size) (((size.w+15)/16)*((size.h+15)/16)) #define CALC_H264_MBPS(size,fps) CALC_H264_MB_NUM(size)*fps.num/fps.denum /* Parse fmtp value for custom resolution, e.g: "CUSTOM=800,600,2" */ static pj_status_t parse_custom_res_fmtp(const pj_str_t *fmtp_val, pjmedia_rect_size *size, unsigned *mpi) { const char *p, *p_end; pj_str_t token; unsigned long val[3] = {0}; unsigned i = 0; p = token.ptr = fmtp_val->ptr; p_end = p + fmtp_val->slen; while (p<=p_end && i32) return PJ_EINVAL; size->w = val[0]; size->h = val[1]; *mpi = val[2]; return PJ_SUCCESS; } /* H263 fmtp parser */ PJ_DEF(pj_status_t) pjmedia_vid_codec_parse_h263_fmtp( const pjmedia_codec_fmtp *fmtp, pjmedia_vid_codec_h263_fmtp *h263_fmtp) { const pj_str_t CUSTOM = {"CUSTOM", 6}; unsigned i; pj_bzero(h263_fmtp, sizeof(*h263_fmtp)); for (i=0; icnt; ++i) { unsigned j; pj_bool_t parsed = PJ_FALSE; if (h263_fmtp->mpi_cnt >= PJ_ARRAY_SIZE(h263_fmtp->mpi)) { pj_assert(!"Too small MPI array in H263 fmtp"); continue; } /* Standard size MPIs */ for (j=0; jparam[i].name, &mpi_resolutions[j].name)==0) { unsigned mpi; mpi = pj_strtoul(&fmtp->param[i].val); if (mpi<1 || mpi>32) return PJMEDIA_SDP_EINFMTP; h263_fmtp->mpi[h263_fmtp->mpi_cnt].size = mpi_resolutions[j].size; h263_fmtp->mpi[h263_fmtp->mpi_cnt].val = mpi; ++h263_fmtp->mpi_cnt; parsed = PJ_TRUE; } } if (parsed) continue; /* Custom size MPIs */ if (pj_stricmp(&fmtp->param[i].name, &CUSTOM)==0) { pjmedia_rect_size size; unsigned mpi; pj_status_t status; status = parse_custom_res_fmtp(&fmtp->param[i].val, &size, &mpi); if (status != PJ_SUCCESS) return PJMEDIA_SDP_EINFMTP; h263_fmtp->mpi[h263_fmtp->mpi_cnt].size = size; h263_fmtp->mpi[h263_fmtp->mpi_cnt].val = mpi; ++h263_fmtp->mpi_cnt; } } return PJ_SUCCESS; } static unsigned fps_to_mpi(const pjmedia_ratio *fps) { unsigned mpi; /* Original formula = (fps->denum * 30000) / (fps->num * 1001) */ mpi = (fps->denum*30000 + fps->num*1001/2) / (fps->num*1001); /* Normalize, should be in the range of 1-32 */ if (mpi > 32) mpi = 32; if (mpi < 1) mpi = 1; return mpi; }; PJ_DEF(pj_status_t) pjmedia_vid_codec_h263_apply_fmtp( pjmedia_vid_codec_param *param) { if (param->dir & PJMEDIA_DIR_ENCODING) { pjmedia_vid_codec_h263_fmtp fmtp_loc, fmtp_rem; pjmedia_rect_size size = {0}; unsigned mpi = 0; pjmedia_video_format_detail *vfd; pj_status_t status; vfd = pjmedia_format_get_video_format_detail(¶m->enc_fmt, PJ_TRUE); /* Get local param */ // Local param should be fetched from "param->enc_fmt" instead of // "param->dec_fmtp". //status = pjmedia_vid_codec_parse_h263_fmtp(¶m->dec_fmtp, // &fmtp_loc); //if (status != PJ_SUCCESS) // return status; fmtp_loc.mpi_cnt = 1; fmtp_loc.mpi[0].size = vfd->size; fmtp_loc.mpi[0].val = fps_to_mpi(&vfd->fps); /* Get remote param */ status = pjmedia_vid_codec_parse_h263_fmtp(¶m->enc_fmtp, &fmtp_rem); if (status != PJ_SUCCESS) return status; /* Negotiate size & MPI setting */ if (fmtp_rem.mpi_cnt == 0) { /* Remote doesn't specify MPI setting, send QCIF=1 */ size.w = 176; size.h = 144; mpi = 1; //} else if (fmtp_loc.mpi_cnt == 0) { // /* Local MPI setting not set, just use remote preference. */ // size = fmtp_rem.mpi[0].size; // mpi = fmtp_rem.mpi[0].val; } else { /* Both have preferences, let's try to match them */ unsigned i, j; pj_bool_t matched = PJ_FALSE; pj_uint32_t min_diff = 0xFFFFFFFF; pj_uint32_t loc_sq, rem_sq, diff; /* Find the exact size match or the closest size, then choose * the highest MPI among the match/closest pair. */ for (i = 0; i < fmtp_rem.mpi_cnt && !matched; ++i) { rem_sq = fmtp_rem.mpi[i].size.w * fmtp_rem.mpi[i].size.h; for (j = 0; j < fmtp_loc.mpi_cnt; ++j) { /* See if we got exact match */ if (fmtp_rem.mpi[i].size.w == fmtp_loc.mpi[j].size.w && fmtp_rem.mpi[i].size.h == fmtp_loc.mpi[j].size.h) { size = fmtp_rem.mpi[i].size; mpi = PJ_MAX(fmtp_rem.mpi[i].val, fmtp_loc.mpi[j].val); matched = PJ_TRUE; break; } /* Otherwise keep looking for the closest match */ loc_sq = fmtp_loc.mpi[j].size.w * fmtp_loc.mpi[j].size.h; diff = loc_sq>rem_sq? (loc_sq-rem_sq):(rem_sq-loc_sq); if (diff < min_diff) { size = rem_sqsize = size; vfd->fps.num = 30000; vfd->fps.denum = 1001 * mpi; } if (param->dir & PJMEDIA_DIR_DECODING) { /* Here we just want to find the highest resolution and the lowest MPI * we support and set it as the decoder param. */ pjmedia_vid_codec_h263_fmtp fmtp; pjmedia_video_format_detail *vfd; pj_status_t status; status = pjmedia_vid_codec_parse_h263_fmtp(¶m->dec_fmtp, &fmtp); if (status != PJ_SUCCESS) return status; vfd = pjmedia_format_get_video_format_detail(¶m->dec_fmt, PJ_TRUE); if (fmtp.mpi_cnt == 0) { /* No resolution specified, lets just assume 4CIF=1! */ vfd->size.w = 704; vfd->size.h = 576; vfd->fps.num = 30000; vfd->fps.denum = 1001; } else { unsigned i, max_size = 0, max_size_idx = 0, min_mpi = 32; /* Get the largest size and the lowest MPI */ for (i = 0; i < fmtp.mpi_cnt; ++i) { if (fmtp.mpi[i].size.w * fmtp.mpi[i].size.h > max_size) { max_size = fmtp.mpi[i].size.w * fmtp.mpi[i].size.h; max_size_idx = i; } if (fmtp.mpi[i].val < min_mpi) min_mpi = fmtp.mpi[i].val; } vfd->size = fmtp.mpi[max_size_idx].size; vfd->fps.num = 30000; vfd->fps.denum = 1001 * min_mpi; } } return PJ_SUCCESS; } /* Declaration of H.264 level info */ typedef struct h264_level_info_t { unsigned id; /* Level id. */ unsigned max_mbps; /* Max macroblocks per second. */ unsigned max_mb; /* Max macroblocks. */ unsigned max_br; /* Max bitrate (kbps). */ } h264_level_info_t; /* Init H264 parameters based on profile-level-id */ static pj_status_t init_h264_profile(const pj_str_t *profile, pjmedia_vid_codec_h264_fmtp *fmtp) { const h264_level_info_t level_info[] = { { 10, 1485, 99, 64 }, { 9, 1485, 99, 128 }, /*< level 1b */ { 11, 3000, 396, 192 }, { 12, 6000, 396, 384 }, { 13, 11880, 396, 768 }, { 20, 11880, 396, 2000 }, { 21, 19800, 792, 4000 }, { 22, 20250, 1620, 4000 }, { 30, 40500, 1620, 10000 }, { 31, 108000, 3600, 14000 }, { 32, 216000, 5120, 20000 }, { 40, 245760, 8192, 20000 }, { 41, 245760, 8192, 50000 }, { 42, 522240, 8704, 50000 }, { 50, 589824, 22080, 135000 }, { 51, 983040, 36864, 240000 }, }; unsigned i, tmp; pj_str_t endst; const h264_level_info_t *li = NULL; if (profile->slen != 6) return PJMEDIA_SDP_EINFMTP; tmp = pj_strtoul2(profile, &endst, 16); if (endst.slen) return PJMEDIA_SDP_EINFMTP; fmtp->profile_idc = (pj_uint8_t)((tmp >> 16) & 0xFF); fmtp->profile_iop = (pj_uint8_t)((tmp >> 8) & 0xFF); fmtp->level = (pj_uint8_t)(tmp & 0xFF); for (i = 0; i < PJ_ARRAY_SIZE(level_info); ++i) { if (level_info[i].id == fmtp->level) { li = &level_info[i]; break; } } if (li == NULL) return PJMEDIA_SDP_EINFMTP; /* Init profile level spec */ if (fmtp->max_br == 0) fmtp->max_br = li->max_br; if (fmtp->max_mbps == 0) fmtp->max_mbps = li->max_mbps; if (fmtp->max_fs == 0) fmtp->max_fs = li->max_mb; return PJ_SUCCESS; } /* H264 fmtp parser */ PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_parse_fmtp( const pjmedia_codec_fmtp *fmtp, pjmedia_vid_codec_h264_fmtp *h264_fmtp) { const pj_str_t PROFILE_LEVEL_ID = {"profile-level-id", 16}; const pj_str_t MAX_MBPS = {"max-mbps", 8}; const pj_str_t MAX_FS = {"max-fs", 6}; const pj_str_t MAX_CPB = {"max-cpb", 7}; const pj_str_t MAX_DPB = {"max-dpb", 7}; const pj_str_t MAX_BR = {"max-br", 6}; const pj_str_t PACKETIZATION_MODE = {"packetization-mode", 18}; const pj_str_t SPROP_PARAMETER_SETS = {"sprop-parameter-sets", 20}; unsigned i; pj_status_t status; pj_bzero(h264_fmtp, sizeof(*h264_fmtp)); for (i=0; icnt; ++i) { unsigned tmp; if (pj_stricmp(&fmtp->param[i].name, &PROFILE_LEVEL_ID)==0) { /* Init H264 parameters based on level, if not set yet */ status = init_h264_profile(&fmtp->param[i].val, h264_fmtp); if (status != PJ_SUCCESS) return status; } else if (pj_stricmp(&fmtp->param[i].name, &PACKETIZATION_MODE)==0) { tmp = pj_strtoul(&fmtp->param[i].val); if (tmp <= 2) h264_fmtp->packetization_mode = (pj_uint8_t)tmp; else return PJMEDIA_SDP_EINFMTP; } else if (pj_stricmp(&fmtp->param[i].name, &MAX_MBPS)==0) { tmp = pj_strtoul(&fmtp->param[i].val); h264_fmtp->max_mbps = PJ_MAX(tmp, h264_fmtp->max_mbps); } else if (pj_stricmp(&fmtp->param[i].name, &MAX_FS)==0) { tmp = pj_strtoul(&fmtp->param[i].val); h264_fmtp->max_fs = PJ_MAX(tmp, h264_fmtp->max_fs); } else if (pj_stricmp(&fmtp->param[i].name, &MAX_CPB)==0) { tmp = pj_strtoul(&fmtp->param[i].val); h264_fmtp->max_cpb = PJ_MAX(tmp, h264_fmtp->max_cpb); } else if (pj_stricmp(&fmtp->param[i].name, &MAX_DPB)==0) { tmp = pj_strtoul(&fmtp->param[i].val); h264_fmtp->max_dpb = PJ_MAX(tmp, h264_fmtp->max_dpb); } else if (pj_stricmp(&fmtp->param[i].name, &MAX_BR)==0) { tmp = pj_strtoul(&fmtp->param[i].val); h264_fmtp->max_br = PJ_MAX(tmp, h264_fmtp->max_br); } else if (pj_stricmp(&fmtp->param[i].name, &SPROP_PARAMETER_SETS)==0) { pj_str_t sps_st; sps_st = fmtp->param[i].val; while (sps_st.slen) { pj_str_t tmp_st; int tmp_len; const pj_uint8_t start_code[3] = {0, 0, 1}; char *p; pj_uint8_t *nal; pj_status_t status; /* Find field separator ',' */ tmp_st = sps_st; p = pj_strchr(&sps_st, ','); if (p) { tmp_st.slen = p - sps_st.ptr; sps_st.ptr = p+1; sps_st.slen -= (tmp_st.slen+1); } else { sps_st.slen = 0; } /* Decode field and build NAL unit for this param */ nal = &h264_fmtp->sprop_param_sets[ h264_fmtp->sprop_param_sets_len]; tmp_len = PJ_ARRAY_SIZE(h264_fmtp->sprop_param_sets) - (int)h264_fmtp->sprop_param_sets_len - PJ_ARRAY_SIZE(start_code); status = pj_base64_decode(&tmp_st, nal + PJ_ARRAY_SIZE(start_code), &tmp_len); if (status != PJ_SUCCESS) return PJMEDIA_SDP_EINFMTP; tmp_len += PJ_ARRAY_SIZE(start_code); pj_memcpy(nal, start_code, PJ_ARRAY_SIZE(start_code)); h264_fmtp->sprop_param_sets_len += tmp_len; } } } /* When profile-level-id is not specified, use default value "42000A" */ if (h264_fmtp->profile_idc == 0) { const pj_str_t DEF_PROFILE = {"42000A", 6}; status = init_h264_profile(&DEF_PROFILE, h264_fmtp); if (status != PJ_SUCCESS) return status; } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_match_sdp(pj_pool_t *pool, pjmedia_sdp_media *offer, unsigned o_fmt_idx, pjmedia_sdp_media *answer, unsigned a_fmt_idx, unsigned option) { const pj_str_t PROFILE_LEVEL_ID = {"profile-level-id", 16}; const pj_str_t PACKETIZATION_MODE = {"packetization-mode", 18}; pjmedia_codec_fmtp o_fmtp_raw, a_fmtp_raw; pjmedia_vid_codec_h264_fmtp o_fmtp, a_fmtp; pj_status_t status; PJ_UNUSED_ARG(pool); /* Parse offer */ status = pjmedia_stream_info_parse_fmtp( NULL, offer, pj_strtoul(&offer->desc.fmt[o_fmt_idx]), &o_fmtp_raw); if (status != PJ_SUCCESS) return status; status = pjmedia_vid_codec_h264_parse_fmtp(&o_fmtp_raw, &o_fmtp); if (status != PJ_SUCCESS) return status; /* Parse answer */ status = pjmedia_stream_info_parse_fmtp( NULL, answer, pj_strtoul(&answer->desc.fmt[a_fmt_idx]), &a_fmtp_raw); if (status != PJ_SUCCESS) return status; status = pjmedia_vid_codec_h264_parse_fmtp(&a_fmtp_raw, &a_fmtp); if (status != PJ_SUCCESS) return status; if (option & PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER) { unsigned i; /* Flexible negotiation, adjust our answer to the offer. * Apply Postel's Principle (TM) in it's full glory. */ if (a_fmtp.profile_idc != o_fmtp.profile_idc) a_fmtp.profile_idc = o_fmtp.profile_idc; if (a_fmtp.profile_iop != o_fmtp.profile_iop) a_fmtp.profile_iop = o_fmtp.profile_iop; if (a_fmtp.packetization_mode >= o_fmtp.packetization_mode) a_fmtp.packetization_mode = o_fmtp.packetization_mode; /* Match them now */ #if H264_STRICT_SDP_NEGO if (a_fmtp.profile_idc != o_fmtp.profile_idc || a_fmtp.profile_iop != o_fmtp.profile_iop || a_fmtp.packetization_mode != o_fmtp.packetization_mode) { return PJMEDIA_SDP_EFORMATNOTEQUAL; } #else if (a_fmtp.profile_idc != o_fmtp.profile_idc) { return PJMEDIA_SDP_EFORMATNOTEQUAL; } #endif /* Update the answer */ for (i = 0; i < a_fmtp_raw.cnt; ++i) { if (pj_stricmp(&a_fmtp_raw.param[i].name, &PROFILE_LEVEL_ID) == 0) { char *p = a_fmtp_raw.param[i].val.ptr; pj_val_to_hex_digit(a_fmtp.profile_idc, p); p += 2; pj_val_to_hex_digit(a_fmtp.profile_iop, p); } else if (pj_stricmp(&a_fmtp_raw.param[i].name, &PACKETIZATION_MODE) == 0) { char *p = a_fmtp_raw.param[i].val.ptr; *p = '0' + a_fmtp.packetization_mode; } } } else { #if H264_STRICT_SDP_NEGO /* Strict negotiation */ if (a_fmtp.profile_idc != o_fmtp.profile_idc || a_fmtp.profile_iop != o_fmtp.profile_iop || a_fmtp.packetization_mode != o_fmtp.packetization_mode) { return PJMEDIA_SDP_EFORMATNOTEQUAL; } #else /* Permissive negotiation */ if (a_fmtp.profile_idc != o_fmtp.profile_idc) { return PJMEDIA_SDP_EFORMATNOTEQUAL; } #endif } return PJ_SUCCESS; } /* Find greatest common divisor (GCD) */ static unsigned gcd (unsigned a, unsigned b) { unsigned c; while (b) { c = a % b; a = b; b = c; } return a; } /* Find highest resolution possible for the specified H264 fmtp and * aspect ratio. */ static pj_status_t find_highest_res(pjmedia_vid_codec_h264_fmtp *fmtp, const pjmedia_ratio *fps, const pjmedia_ratio *ratio, pjmedia_rect_size *size, pj_bool_t is_decoding) { pjmedia_ratio def_ratio = { DEFAULT_H264_RATIO_NUM, DEFAULT_H264_RATIO_DENUM }; pjmedia_ratio def_fps = { DEFAULT_H264_FPS_NUM, DEFAULT_H264_FPS_DENUM }; pjmedia_ratio asp_ratio, the_fps; unsigned max_fs, g, scale; pj_assert(size); /* Get the ratio, or just use default if not provided. */ if (ratio && ratio->num && ratio->denum) { asp_ratio = *ratio; } else { asp_ratio = def_ratio; } /* Normalize the aspect ratio */ g = gcd(asp_ratio.num, asp_ratio.denum); asp_ratio.num /= g; asp_ratio.denum /= g; /* Get the frame rate, or just use default if not provided. */ if (fps && fps->num && fps->denum) { the_fps = *fps; } else { the_fps = def_fps; } /* Calculate maximum size (in macroblocks) */ max_fs = fmtp->max_mbps * the_fps.denum / the_fps.num; max_fs = PJ_MIN(max_fs, fmtp->max_fs); /* Check if the specified ratio is using big numbers * (not normalizable), override it! */ if ((int)max_fs < asp_ratio.num * asp_ratio.denum) { if ((int)max_fs >= def_ratio.num * def_ratio.denum) asp_ratio = def_ratio; else asp_ratio.num = asp_ratio.denum = 1; } /* Calculate the scale factor for size */ scale = pj_isqrt(max_fs / asp_ratio.denum / asp_ratio.num); /* Calculate the size, note that the frame size is in macroblock units */ size->w = asp_ratio.num * scale * 16; size->h = asp_ratio.denum * scale * 16; /* #1769: for decoding, size is usually used for allocating buffer, * so we need to make sure that frame size is not less than max_fs. */ if (is_decoding && ((size->w * size->h) >> 8) < max_fs) { /* Size is less than max_fs, recalculate using ratio 1:1 and * round up the scale. */ scale = pj_isqrt(max_fs) + 1; size->w = size->h = scale * 16; } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_apply_fmtp( pjmedia_vid_codec_param *param) { if (param->dir & PJMEDIA_DIR_ENCODING) { pjmedia_vid_codec_h264_fmtp fmtp; pjmedia_video_format_detail *vfd; pj_status_t status; /* Get remote param */ status = pjmedia_vid_codec_h264_parse_fmtp(¶m->enc_fmtp, &fmtp); if (status != PJ_SUCCESS) return status; /* Adjust fps, size, and bitrate to conform to H.264 level * specified by remote SDP fmtp. */ vfd = pjmedia_format_get_video_format_detail(¶m->enc_fmt, PJ_TRUE); if (vfd->fps.num == 0 || vfd->fps.denum == 0) { vfd->fps.num = DEFAULT_H264_FPS_NUM; vfd->fps.denum = DEFAULT_H264_FPS_DENUM; } if (vfd->size.w && vfd->size.h) { unsigned mb, mbps; /* Scale down the resolution if it exceeds profile spec */ mb = CALC_H264_MB_NUM(vfd->size); mbps = CALC_H264_MBPS(vfd->size, vfd->fps); if (mb > fmtp.max_fs || mbps > fmtp.max_mbps) { pjmedia_ratio r; r.num = vfd->size.w; r.denum = vfd->size.h; find_highest_res(&fmtp, &vfd->fps, &r, &vfd->size, PJ_FALSE); } } else { /* When not specified, just use the highest res possible*/ pjmedia_ratio r; r.num = vfd->size.w; r.denum = vfd->size.h; find_highest_res(&fmtp, &vfd->fps, &r, &vfd->size, PJ_FALSE); } /* Encoding bitrate must not be higher than H264 level spec */ if (vfd->avg_bps > fmtp.max_br * 1000) vfd->avg_bps = fmtp.max_br * 1000; if (vfd->max_bps > fmtp.max_br * 1000) vfd->max_bps = fmtp.max_br * 1000; } if (param->dir & PJMEDIA_DIR_DECODING) { /* Here we just want to find the highest resolution possible from the * fmtp and set it as the decoder param. */ pjmedia_vid_codec_h264_fmtp fmtp; pjmedia_video_format_detail *vfd; pjmedia_ratio r; pjmedia_rect_size highest_size; pj_status_t status; status = pjmedia_vid_codec_h264_parse_fmtp(¶m->dec_fmtp, &fmtp); if (status != PJ_SUCCESS) return status; vfd = pjmedia_format_get_video_format_detail(¶m->dec_fmt, PJ_TRUE); if (vfd->fps.num == 0 || vfd->fps.denum == 0) { vfd->fps.num = DEFAULT_H264_FPS_NUM; vfd->fps.denum = DEFAULT_H264_FPS_DENUM; } /* Normalize decoding resolution, i.e: it must not be lower than * the H264 profile level setting used, as this may be used by * app to allocate buffer. */ r.num = vfd->size.w; r.denum = vfd->size.h; find_highest_res(&fmtp, &vfd->fps, &r, &highest_size, PJ_TRUE); if (vfd->size.w * vfd->size.h < highest_size.w * highest_size.h) vfd->size = highest_size; /* Normalize decoding bitrate based on H264 level spec */ if (vfd->avg_bps < fmtp.max_br * 1000) vfd->avg_bps = fmtp.max_br * 1000; if (vfd->max_bps < fmtp.max_br * 1000) vfd->max_bps = fmtp.max_br * 1000; } return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_VIDEO */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/vid_port.c ================================================ /* $Id: vid_port.c 4290 2012-11-01 03:06:33Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #define SIGNATURE PJMEDIA_SIG_VID_PORT #define THIS_FILE "vid_port.c" /* Enable/disable test of finding closest format algo */ #define ENABLE_TEST_FIND_FMT 0 /** * Enable this to trace the format matching process. */ #if 0 # define TRACE_FIND_FMT(args) PJ_LOG(5,args) #else # define TRACE_FIND_FMT(args) #endif /** * We use nearest width and aspect ratio to find match between the requested * format and the supported format. Specify this to determine the array size * of the supported formats with the nearest width. From this array, we will * find the one with lowest diff_ratio. Setting this to 1 will thus skip * the aspect ratio calculation. */ #ifndef PJMEDIA_VID_PORT_MATCH_WIDTH_ARRAY_SIZE # define PJMEDIA_VID_PORT_MATCH_WIDTH_ARRAY_SIZE 3 #endif typedef struct vid_pasv_port vid_pasv_port; enum role { ROLE_NONE, ROLE_ACTIVE, ROLE_PASSIVE }; enum fmt_match { FMT_MATCH, FMT_SAME_COLOR_SPACE, FMT_DIFF_COLOR_SPACE }; struct pjmedia_vid_port { pj_pool_t *pool; pj_str_t dev_name; pjmedia_dir dir; // pjmedia_rect_size cap_size; pjmedia_vid_dev_stream *strm; pjmedia_vid_dev_cb strm_cb; void *strm_cb_data; enum role role, stream_role; vid_pasv_port *pasv_port; pjmedia_port *client_port; pj_bool_t destroy_client_port; struct { pjmedia_converter *conv; void *conv_buf; pj_size_t conv_buf_size; pjmedia_conversion_param conv_param; unsigned usec_ctr; unsigned usec_src, usec_dst; } conv; pjmedia_clock *clock; pjmedia_clock_src clocksrc; struct sync_clock_src_t { pjmedia_clock_src *sync_clocksrc; pj_int32_t sync_delta; unsigned max_sync_ticks; unsigned nsync_frame; unsigned nsync_progress; } sync_clocksrc; pjmedia_frame *frm_buf; pj_size_t frm_buf_size; pj_mutex_t *frm_mutex; }; struct vid_pasv_port { pjmedia_port base; pjmedia_vid_port *vp; }; struct fmt_prop { pj_uint32_t id; pjmedia_rect_size size; pjmedia_ratio fps; }; static pj_status_t vidstream_cap_cb(pjmedia_vid_dev_stream *stream, void *user_data, pjmedia_frame *frame); static pj_status_t vidstream_render_cb(pjmedia_vid_dev_stream *stream, void *user_data, pjmedia_frame *frame); static pj_status_t vidstream_event_cb(pjmedia_event *event, void *user_data); static pj_status_t client_port_event_cb(pjmedia_event *event, void *user_data); static void enc_clock_cb(const pj_timestamp *ts, void *user_data); static void dec_clock_cb(const pj_timestamp *ts, void *user_data); static pj_status_t vid_pasv_port_put_frame(struct pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t vid_pasv_port_get_frame(struct pjmedia_port *this_port, pjmedia_frame *frame); PJ_DEF(void) pjmedia_vid_port_param_default(pjmedia_vid_port_param *prm) { pj_bzero(prm, sizeof(*prm)); prm->active = PJ_TRUE; } static const char *vid_dir_name(pjmedia_dir dir) { switch (dir) { case PJMEDIA_DIR_CAPTURE: return "capture"; case PJMEDIA_DIR_RENDER: return "render"; default: return "??"; } } static pj_status_t create_converter(pjmedia_vid_port *vp) { if (vp->conv.conv) { pjmedia_converter_destroy(vp->conv.conv); vp->conv.conv = NULL; } /* Instantiate converter if necessary */ if (vp->conv.conv_param.src.id != vp->conv.conv_param.dst.id || (vp->conv.conv_param.src.det.vid.size.w != vp->conv.conv_param.dst.det.vid.size.w) || (vp->conv.conv_param.src.det.vid.size.h != vp->conv.conv_param.dst.det.vid.size.h)) { pj_status_t status; /* Yes, we need converter */ status = pjmedia_converter_create(NULL, vp->pool, &vp->conv.conv_param, &vp->conv.conv); if (status != PJ_SUCCESS) { PJ_PERROR(4,(THIS_FILE, status, "Error creating converter")); return status; } } if (vp->conv.conv || (vp->role==ROLE_ACTIVE && (vp->dir & PJMEDIA_DIR_ENCODING))) { pj_status_t status; const pjmedia_video_format_info *vfi; pjmedia_video_apply_fmt_param vafp; /* Allocate buffer for conversion */ vfi = pjmedia_get_video_format_info(NULL, vp->conv.conv_param.dst.id); if (!vfi) return PJMEDIA_EBADFMT; pj_bzero(&vafp, sizeof(vafp)); vafp.size = vp->conv.conv_param.dst.det.vid.size; status = vfi->apply_fmt(vfi, &vafp); if (status != PJ_SUCCESS) return PJMEDIA_EBADFMT; if (vafp.framebytes > vp->conv.conv_buf_size) { vp->conv.conv_buf = pj_pool_alloc(vp->pool, vafp.framebytes); vp->conv.conv_buf_size = vafp.framebytes; } } vp->conv.usec_ctr = 0; vp->conv.usec_src = PJMEDIA_PTIME(&vp->conv.conv_param.src.det.vid.fps); vp->conv.usec_dst = PJMEDIA_PTIME(&vp->conv.conv_param.dst.det.vid.fps); return PJ_SUCCESS; } static pj_uint32_t match_format_id(pj_uint32_t req_id, pj_uint32_t sup_id) { const pjmedia_video_format_info *req_fmt_info, *sup_fmt_info; if (req_id == sup_id) return FMT_MATCH; req_fmt_info = pjmedia_get_video_format_info( pjmedia_video_format_mgr_instance(), req_id); sup_fmt_info = pjmedia_get_video_format_info( pjmedia_video_format_mgr_instance(), sup_id); if ((req_fmt_info == NULL) || (sup_fmt_info == NULL)) { return FMT_DIFF_COLOR_SPACE; } if (req_fmt_info->color_model == sup_fmt_info->color_model) { return FMT_SAME_COLOR_SPACE; } return FMT_DIFF_COLOR_SPACE; } static pj_uint32_t get_match_format_id(pj_uint32_t req_fmt_id, pjmedia_vid_dev_info *di) { unsigned i, match_idx = 0, match_fmt = FMT_DIFF_COLOR_SPACE+1; /* Find the matching format. If no exact match is found, find * the supported format with the same color space. If no match is found, * use the first supported format on the list. */ for (i = 0; i < di->fmt_cnt; ++i) { unsigned tmp_fmt = match_format_id(req_fmt_id, di->fmt[i].id); if (match_fmt == FMT_MATCH) return req_fmt_id; if (tmp_fmt < match_fmt) { match_idx = i; match_fmt = tmp_fmt; } } return di->fmt[match_idx].id; } /** * Find the closest supported format from the specific requested format. * The algo is to find a supported size with the matching format id, width and * lowest diff_ratio. * --- * For format id matching, the priority is: * 1. Find exact match * 2. Find format with the same color space * 3. Use the first supported format. * --- * For ratio matching: * Find the lowest difference of the aspect ratio between the requested and * the supported format. */ static struct fmt_prop find_closest_fmt(pj_uint32_t req_fmt_id, pjmedia_rect_size *req_fmt_size, pjmedia_ratio *req_fmt_fps, pjmedia_vid_dev_info *di) { unsigned i, match_idx = 0; pj_uint32_t match_fmt_id; float req_ratio, min_diff_ratio = 0.0; struct fmt_prop ret_prop; pj_bool_t found_exact_match = PJ_FALSE; #define GET_DIFF(x, y) ((x) > (y)? (x-y) : (y-x)) /* This will contain the supported format with lowest width difference */ pjmedia_rect_size nearest_width[PJMEDIA_VID_PORT_MATCH_WIDTH_ARRAY_SIZE]; /* Initialize the list. */ for (i=0;ifmt_cnt;++i) { pjmedia_video_format_detail *vfd; unsigned diff_width1, diff_width2; /* Ignore supported format with different format id. */ if (di->fmt[i].id != match_fmt_id) continue; vfd = pjmedia_format_get_video_format_detail(&di->fmt[i], PJ_TRUE); /* Exact match found. */ if ((vfd->size.w == req_fmt_size->w) && (vfd->size.h == req_fmt_size->h)) { nearest_width[0] = vfd->size; found_exact_match = PJ_TRUE; break; } diff_width1 = GET_DIFF(vfd->size.w, req_fmt_size->w); diff_width2 = GET_DIFF(nearest_width[0].w, req_fmt_size->w); /* Fill the nearest width list. */ if (diff_width1 <= diff_width2) { int k = 1; pjmedia_rect_size tmp_size = vfd->size; while(((GET_DIFF(tmp_size.w, req_fmt_size->w) < (GET_DIFF(nearest_width[k].w, req_fmt_size->w))) && (k < PJ_ARRAY_SIZE(nearest_width)))) { nearest_width[k-1] = nearest_width[k]; ++k; } nearest_width[k-1] = tmp_size; } } /* No need to calculate ratio if exact match is found. */ if (!found_exact_match) { pj_bool_t found_match = PJ_FALSE; /* We have the list of supported format with nearest width. Now get the * best ratio. */ req_ratio = (float)req_fmt_size->w / (float)req_fmt_size->h; for (i=0;isize.w, vid_fd->size.h, vid_fd->fps.num, vid_fd->fps.denum)); } for (i = 0; i < PJ_ARRAY_SIZE(find_id); i++) { for (j = 0; j < PJ_ARRAY_SIZE(find_fps); j++) { for (k = 0; k < PJ_ARRAY_SIZE(find_size); k++) { struct fmt_prop match_prop; pjmedia_fourcc_name(find_id[i], fmt_name); TRACE_FIND_FMT((THIS_FILE, "Trying to find closest match " "id:%s size:%dx%d fps:%d/%d", fmt_name, find_size[k].w, find_size[k].h, find_fps[j].num, find_fps[j].denum)); match_prop = find_closest_fmt(find_id[i], &find_size[k], &find_fps[j], di); if ((match_prop.id == find_id[i]) && (match_prop.size.w == find_size[k].w) && (match_prop.size.h == find_size[k].h) && (match_prop.fps.num / match_prop.fps.denum == find_fps[j].num * find_fps[j].denum)) { TRACE_FIND_FMT((THIS_FILE, "Exact Match found!!")); } else { pjmedia_fourcc_name(match_prop.id, fmt_name); TRACE_FIND_FMT((THIS_FILE, "Closest format = "\ "id:%s size:%dx%d fps:%d/%d", fmt_name, match_prop.size.w, match_prop.size.h, match_prop.fps.num, match_prop.fps.denum)); } } } } } #endif PJ_DEF(pj_status_t) pjmedia_vid_port_create( pj_pool_t *pool, const pjmedia_vid_port_param *prm, pjmedia_vid_port **p_vid_port) { pjmedia_vid_port *vp; pjmedia_video_format_detail *vfd; char dev_name[64]; char fmt_name[5]; pjmedia_vid_dev_cb vid_cb; pj_bool_t need_frame_buf = PJ_FALSE; pj_status_t status; unsigned ptime_usec; pjmedia_vid_dev_param vparam; pjmedia_vid_dev_info di; PJ_ASSERT_RETURN(pool && prm && p_vid_port, PJ_EINVAL); PJ_ASSERT_RETURN(prm->vidparam.fmt.type == PJMEDIA_TYPE_VIDEO && prm->vidparam.dir != PJMEDIA_DIR_NONE && prm->vidparam.dir != PJMEDIA_DIR_CAPTURE_RENDER, PJ_EINVAL); /* Retrieve the video format detail */ vfd = pjmedia_format_get_video_format_detail(&prm->vidparam.fmt, PJ_TRUE); if (!vfd) return PJ_EINVAL; PJ_ASSERT_RETURN(vfd->fps.num, PJ_EINVAL); /* Allocate videoport */ vp = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_port); vp->pool = pj_pool_create(pool->factory, "video port", 500, 500, NULL); vp->role = prm->active ? ROLE_ACTIVE : ROLE_PASSIVE; vp->dir = prm->vidparam.dir; // vp->cap_size = vfd->size; vparam = prm->vidparam; dev_name[0] = '\0'; /* Get device info */ if (vp->dir & PJMEDIA_DIR_CAPTURE) status = pjmedia_vid_dev_get_info(prm->vidparam.cap_id, &di); else status = pjmedia_vid_dev_get_info(prm->vidparam.rend_id, &di); if (status != PJ_SUCCESS) return status; pj_ansi_snprintf(dev_name, sizeof(dev_name), "%s [%s]", di.name, di.driver); pjmedia_fourcc_name(vparam.fmt.id, fmt_name); PJ_LOG(4,(THIS_FILE, "Opening device %s for %s: format=%s, size=%dx%d @%d:%d fps", dev_name, vid_dir_name(prm->vidparam.dir), fmt_name, vfd->size.w, vfd->size.h, vfd->fps.num, vfd->fps.denum)); if (di.dir == PJMEDIA_DIR_RENDER) { /* Find the matching format. If no exact match is found, find * the supported format with the same color space. If no match is found, * use the first supported format on the list. */ pj_assert(di.fmt_cnt != 0); vparam.fmt.id = get_match_format_id(prm->vidparam.fmt.id, &di); } else { struct fmt_prop match_prop; if (di.fmt_cnt == 0) { status = PJMEDIA_EVID_SYSERR; PJ_PERROR(4,(THIS_FILE, status, "Device has no supported format")); return status; } #if ENABLE_TEST_FIND_FMT test_find_closest_fmt(&di); #endif match_prop = find_closest_fmt(prm->vidparam.fmt.id, &vfd->size, &vfd->fps, &di); if ((match_prop.id != prm->vidparam.fmt.id) || (match_prop.size.w != vfd->size.w) || (match_prop.size.h != vfd->size.h)) { vparam.fmt.id = match_prop.id; vparam.fmt.det.vid.size = match_prop.size; } } pj_strdup2_with_null(pool, &vp->dev_name, di.name); vp->stream_role = di.has_callback ? ROLE_ACTIVE : ROLE_PASSIVE; ptime_usec = PJMEDIA_PTIME(&vfd->fps); pjmedia_clock_src_init(&vp->clocksrc, PJMEDIA_TYPE_VIDEO, prm->vidparam.clock_rate, ptime_usec); vp->sync_clocksrc.max_sync_ticks = PJMEDIA_CLOCK_SYNC_MAX_RESYNC_DURATION * 1000 / vp->clocksrc.ptime_usec; /* Create the video stream */ pj_bzero(&vid_cb, sizeof(vid_cb)); vid_cb.capture_cb = &vidstream_cap_cb; vid_cb.render_cb = &vidstream_render_cb; status = pjmedia_vid_dev_stream_create(&vparam, &vid_cb, vp, &vp->strm); if (status != PJ_SUCCESS) goto on_error; PJ_LOG(4,(THIS_FILE, "Device %s opened: format=%s, size=%dx%d @%d:%d fps", dev_name, fmt_name, vparam.fmt.det.vid.size.w, vparam.fmt.det.vid.size.h, vparam.fmt.det.vid.fps.num, vparam.fmt.det.vid.fps.denum)); /* Subscribe to device's events */ pjmedia_event_subscribe(NULL, &vidstream_event_cb, vp, vp->strm); if (vp->dir & PJMEDIA_DIR_CAPTURE) { pjmedia_format_copy(&vp->conv.conv_param.src, &vparam.fmt); pjmedia_format_copy(&vp->conv.conv_param.dst, &prm->vidparam.fmt); } else { pjmedia_format_copy(&vp->conv.conv_param.src, &prm->vidparam.fmt); pjmedia_format_copy(&vp->conv.conv_param.dst, &vparam.fmt); } status = create_converter(vp); if (status != PJ_SUCCESS) goto on_error; if (vp->role==ROLE_ACTIVE && ((vp->dir & PJMEDIA_DIR_ENCODING) || vp->stream_role==ROLE_PASSIVE)) { pjmedia_clock_param param; /* Active role is wanted, but our device is passive, so create * master clocks to run the media flow. For encoding direction, * we also want to create our own clock since the device's clock * may run at a different rate. */ need_frame_buf = PJ_TRUE; param.usec_interval = PJMEDIA_PTIME(&vfd->fps); param.clock_rate = prm->vidparam.clock_rate; status = pjmedia_clock_create2(pool, ¶m, PJMEDIA_CLOCK_NO_HIGHEST_PRIO, (vp->dir & PJMEDIA_DIR_ENCODING) ? &enc_clock_cb: &dec_clock_cb, vp, &vp->clock); if (status != PJ_SUCCESS) goto on_error; } else if (vp->role==ROLE_PASSIVE) { vid_pasv_port *pp; /* Always need to create media port for passive role */ vp->pasv_port = pp = PJ_POOL_ZALLOC_T(pool, vid_pasv_port); pp->vp = vp; pp->base.get_frame = &vid_pasv_port_get_frame; pp->base.put_frame = &vid_pasv_port_put_frame; pjmedia_port_info_init2(&pp->base.info, &vp->dev_name, PJMEDIA_SIG_VID_PORT, prm->vidparam.dir, &prm->vidparam.fmt); need_frame_buf = PJ_TRUE; } if (need_frame_buf) { const pjmedia_video_format_info *vfi; pjmedia_video_apply_fmt_param vafp; vfi = pjmedia_get_video_format_info(NULL, vparam.fmt.id); if (!vfi) { status = PJ_ENOTFOUND; goto on_error; } pj_bzero(&vafp, sizeof(vafp)); vafp.size = vparam.fmt.det.vid.size; status = vfi->apply_fmt(vfi, &vafp); if (status != PJ_SUCCESS) goto on_error; vp->frm_buf = PJ_POOL_ZALLOC_T(pool, pjmedia_frame); vp->frm_buf_size = vafp.framebytes; vp->frm_buf->buf = pj_pool_alloc(pool, vafp.framebytes); vp->frm_buf->size = vp->frm_buf_size; vp->frm_buf->type = PJMEDIA_FRAME_TYPE_NONE; status = pj_mutex_create_simple(pool, vp->dev_name.ptr, &vp->frm_mutex); if (status != PJ_SUCCESS) goto on_error; } *p_vid_port = vp; return PJ_SUCCESS; on_error: pjmedia_vid_port_destroy(vp); return status; } PJ_DEF(void) pjmedia_vid_port_set_cb(pjmedia_vid_port *vid_port, const pjmedia_vid_dev_cb *cb, void *user_data) { pj_assert(vid_port && cb); pj_memcpy(&vid_port->strm_cb, cb, sizeof(*cb)); vid_port->strm_cb_data = user_data; } PJ_DEF(pjmedia_vid_dev_stream*) pjmedia_vid_port_get_stream(pjmedia_vid_port *vp) { PJ_ASSERT_RETURN(vp, NULL); return vp->strm; } PJ_DEF(pjmedia_port*) pjmedia_vid_port_get_passive_port(pjmedia_vid_port *vp) { PJ_ASSERT_RETURN(vp && vp->role==ROLE_PASSIVE, NULL); return &vp->pasv_port->base; } PJ_DEF(pjmedia_clock_src *) pjmedia_vid_port_get_clock_src( pjmedia_vid_port *vid_port ) { PJ_ASSERT_RETURN(vid_port, NULL); return &vid_port->clocksrc; } PJ_DECL(pj_status_t) pjmedia_vid_port_set_clock_src( pjmedia_vid_port *vid_port, pjmedia_clock_src *clocksrc) { PJ_ASSERT_RETURN(vid_port && clocksrc, PJ_EINVAL); vid_port->sync_clocksrc.sync_clocksrc = clocksrc; vid_port->sync_clocksrc.sync_delta = pjmedia_clock_src_get_time_msec(&vid_port->clocksrc) - pjmedia_clock_src_get_time_msec(clocksrc); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_vid_port_connect(pjmedia_vid_port *vp, pjmedia_port *port, pj_bool_t destroy) { PJ_ASSERT_RETURN(vp && vp->role==ROLE_ACTIVE, PJ_EINVAL); vp->destroy_client_port = destroy; vp->client_port = port; /* Subscribe to client port's events */ pjmedia_event_subscribe(NULL, &client_port_event_cb, vp, vp->client_port); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_vid_port_disconnect(pjmedia_vid_port *vp) { PJ_ASSERT_RETURN(vp && vp->role==ROLE_ACTIVE, PJ_EINVAL); pjmedia_event_unsubscribe(NULL, &client_port_event_cb, vp, vp->client_port); vp->client_port = NULL; return PJ_SUCCESS; } PJ_DEF(pjmedia_port*) pjmedia_vid_port_get_connected_port(pjmedia_vid_port *vp) { PJ_ASSERT_RETURN(vp && vp->role==ROLE_ACTIVE, NULL); return vp->client_port; } PJ_DEF(pj_status_t) pjmedia_vid_port_start(pjmedia_vid_port *vp) { pj_status_t status; PJ_ASSERT_RETURN(vp, PJ_EINVAL); status = pjmedia_vid_dev_stream_start(vp->strm); if (status != PJ_SUCCESS) goto on_error; if (vp->clock) { status = pjmedia_clock_start(vp->clock); if (status != PJ_SUCCESS) goto on_error; } return PJ_SUCCESS; on_error: pjmedia_vid_port_stop(vp); return status; } PJ_DEF(pj_bool_t) pjmedia_vid_port_is_running(pjmedia_vid_port *vp) { return pjmedia_vid_dev_stream_is_running(vp->strm); } PJ_DEF(pj_status_t) pjmedia_vid_port_stop(pjmedia_vid_port *vp) { pj_status_t status; PJ_ASSERT_RETURN(vp, PJ_EINVAL); if (vp->clock) { status = pjmedia_clock_stop(vp->clock); } status = pjmedia_vid_dev_stream_stop(vp->strm); return status; } PJ_DEF(void) pjmedia_vid_port_destroy(pjmedia_vid_port *vp) { PJ_ASSERT_ON_FAIL(vp, return); PJ_LOG(4,(THIS_FILE, "Closing %s..", vp->dev_name.ptr)); if (vp->clock) { pjmedia_clock_destroy(vp->clock); vp->clock = NULL; } if (vp->strm) { pjmedia_event_unsubscribe(NULL, &vidstream_event_cb, vp, vp->strm); pjmedia_vid_dev_stream_destroy(vp->strm); vp->strm = NULL; } if (vp->client_port) { pjmedia_event_unsubscribe(NULL, &client_port_event_cb, vp, vp->client_port); if (vp->destroy_client_port) pjmedia_port_destroy(vp->client_port); vp->client_port = NULL; } if (vp->frm_mutex) { pj_mutex_destroy(vp->frm_mutex); vp->frm_mutex = NULL; } if (vp->conv.conv) { pjmedia_converter_destroy(vp->conv.conv); vp->conv.conv = NULL; } pj_pool_release(vp->pool); } /* static void save_rgb_frame(int width, int height, const pjmedia_frame *frm) { static int counter; FILE *pFile; char szFilename[32]; const pj_uint8_t *pFrame = (const pj_uint8_t*)frm->buf; int y; if (counter > 10) return; // Open file sprintf(szFilename, "frame%02d.ppm", counter++); pFile=fopen(szFilename, "wb"); if(pFile==NULL) return; // Write header fprintf(pFile, "P6\n%d %d\n255\n", width, height); // Write pixel data for(y=0; ytype == PJMEDIA_EVENT_FMT_CHANGED) { const pjmedia_video_format_detail *vfd; const pjmedia_video_format_detail *vfd_cur; pjmedia_vid_dev_param vid_param; pj_status_t status; /* Retrieve the current video format detail */ pjmedia_vid_dev_stream_get_param(vp->strm, &vid_param); vfd_cur = pjmedia_format_get_video_format_detail( &vid_param.fmt, PJ_TRUE); if (!vfd_cur) return PJMEDIA_EVID_BADFORMAT; /* Retrieve the new video format detail */ vfd = pjmedia_format_get_video_format_detail( &event->data.fmt_changed.new_fmt, PJ_TRUE); if (!vfd || !vfd->fps.num || !vfd->fps.denum) return PJMEDIA_EVID_BADFORMAT; /* Ticket #1876: if this is a passive renderer and only frame rate is * changing, simply modify the clock. */ if (vp->dir == PJMEDIA_DIR_RENDER && vp->stream_role == ROLE_PASSIVE && vp->role == ROLE_ACTIVE) { pj_bool_t fps_only; pjmedia_video_format_detail tmp_vfd; tmp_vfd = *vfd_cur; tmp_vfd.fps = vfd->fps; fps_only = pj_memcmp(vfd, &tmp_vfd, sizeof(*vfd)) == 0; if (fps_only) { pjmedia_clock_param clock_param; clock_param.usec_interval = PJMEDIA_PTIME(&vfd->fps); clock_param.clock_rate = vid_param.clock_rate; pjmedia_clock_modify(vp->clock, &clock_param); return pjmedia_event_publish(NULL, vp, event, PJMEDIA_EVENT_PUBLISH_POST_EVENT); } } /* Ticket #1827: * Stopping video port should not be necessary here because * it will also try to stop the clock, from inside the clock's * own thread, so it may get stuck. We just stop the video device * stream instead. * pjmedia_vid_port_stop(vp); */ pjmedia_vid_dev_stream_stop(vp->strm); /* Change the destination format to the new format */ pjmedia_format_copy(&vp->conv.conv_param.src, &event->data.fmt_changed.new_fmt); /* Only copy the size here */ vp->conv.conv_param.dst.det.vid.size = event->data.fmt_changed.new_fmt.det.vid.size; status = create_converter(vp); if (status != PJ_SUCCESS) { PJ_PERROR(4,(THIS_FILE, status, "Error recreating converter")); return status; } if (vid_param.fmt.id != vp->conv.conv_param.dst.id || (vid_param.fmt.det.vid.size.h != vp->conv.conv_param.dst.det.vid.size.h) || (vid_param.fmt.det.vid.size.w != vp->conv.conv_param.dst.det.vid.size.w)) { status = pjmedia_vid_dev_stream_set_cap(vp->strm, PJMEDIA_VID_DEV_CAP_FORMAT, &vp->conv.conv_param.dst); if (status != PJ_SUCCESS) { PJ_LOG(3, (THIS_FILE, "failure in changing the format of the " "video device")); PJ_LOG(3, (THIS_FILE, "reverting to its original format: %s", status != PJMEDIA_EVID_ERR ? "success" : "failure")); pjmedia_vid_port_start(vp); return status; } } if (vp->stream_role == ROLE_PASSIVE) { pjmedia_clock_param clock_param; /** * Initially, frm_buf was allocated the biggest * supported size, so we do not need to re-allocate * the buffer here. */ /* Adjust the clock */ clock_param.usec_interval = PJMEDIA_PTIME(&vfd->fps); clock_param.clock_rate = vid_param.clock_rate; pjmedia_clock_modify(vp->clock, &clock_param); } /* pjmedia_vid_port_start(vp); */ pjmedia_vid_dev_stream_start(vp->strm); } /* Republish the event, post the event to the event manager * to avoid deadlock if vidport is trying to stop the clock. */ return pjmedia_event_publish(NULL, vp, event, PJMEDIA_EVENT_PUBLISH_POST_EVENT); } static pj_status_t convert_frame(pjmedia_vid_port *vp, pjmedia_frame *src_frame, pjmedia_frame *dst_frame) { pj_status_t status = PJ_SUCCESS; if (vp->conv.conv) { if (!dst_frame->buf || dst_frame->size < vp->conv.conv_buf_size) { dst_frame->buf = vp->conv.conv_buf; dst_frame->size = vp->conv.conv_buf_size; } status = pjmedia_converter_convert(vp->conv.conv, src_frame, dst_frame); } return status; } /* Copy frame to buffer. */ static void copy_frame_to_buffer(pjmedia_vid_port *vp, pjmedia_frame *frame) { pj_mutex_lock(vp->frm_mutex); pjmedia_frame_copy(vp->frm_buf, frame); pj_mutex_unlock(vp->frm_mutex); } /* Get frame from buffer and convert it if necessary. */ static pj_status_t get_frame_from_buffer(pjmedia_vid_port *vp, pjmedia_frame *frame) { pj_status_t status = PJ_SUCCESS; pj_mutex_lock(vp->frm_mutex); if (vp->conv.conv) status = convert_frame(vp, vp->frm_buf, frame); else pjmedia_frame_copy(frame, vp->frm_buf); pj_mutex_unlock(vp->frm_mutex); return status; } static void enc_clock_cb(const pj_timestamp *ts, void *user_data) { /* We are here because user wants us to be active but the stream is * passive. So get a frame from the stream and push it to user. */ pjmedia_vid_port *vp = (pjmedia_vid_port*)user_data; pjmedia_frame frame_; pj_status_t status = PJ_SUCCESS; pj_assert(vp->role==ROLE_ACTIVE); PJ_UNUSED_ARG(ts); if (!vp->client_port) return; if (vp->stream_role == ROLE_PASSIVE) { while (vp->conv.usec_ctr < vp->conv.usec_dst) { vp->frm_buf->size = vp->frm_buf_size; status = pjmedia_vid_dev_stream_get_frame(vp->strm, vp->frm_buf); vp->conv.usec_ctr += vp->conv.usec_src; } vp->conv.usec_ctr -= vp->conv.usec_dst; if (status != PJ_SUCCESS) return; } //save_rgb_frame(vp->cap_size.w, vp->cap_size.h, vp->frm_buf); frame_.buf = vp->conv.conv_buf; frame_.size = vp->conv.conv_buf_size; status = get_frame_from_buffer(vp, &frame_); if (status != PJ_SUCCESS) return; status = pjmedia_port_put_frame(vp->client_port, &frame_); if (status != PJ_SUCCESS) return; } static void dec_clock_cb(const pj_timestamp *ts, void *user_data) { /* We are here because user wants us to be active but the stream is * passive. So get a frame from the stream and push it to user. */ pjmedia_vid_port *vp = (pjmedia_vid_port*)user_data; pj_status_t status; pjmedia_frame frame; pj_assert(vp->role==ROLE_ACTIVE && vp->stream_role==ROLE_PASSIVE); PJ_UNUSED_ARG(ts); if (!vp->client_port) return; status = vidstream_render_cb(vp->strm, vp, &frame); if (status != PJ_SUCCESS) return; if (frame.size > 0) status = pjmedia_vid_dev_stream_put_frame(vp->strm, &frame); } static pj_status_t vidstream_cap_cb(pjmedia_vid_dev_stream *stream, void *user_data, pjmedia_frame *frame) { pjmedia_vid_port *vp = (pjmedia_vid_port*)user_data; /* We just store the frame in the buffer. For active role, we let * video port's clock to push the frame buffer to the user. * The decoding counterpart for passive role and active stream is * located in vid_pasv_port_put_frame() */ copy_frame_to_buffer(vp, frame); /* This is tricky since the frame is still in its original unconverted * format, which may not be what the application expects. */ if (vp->strm_cb.capture_cb) return (*vp->strm_cb.capture_cb)(stream, vp->strm_cb_data, frame); return PJ_SUCCESS; } static pj_status_t vidstream_render_cb(pjmedia_vid_dev_stream *stream, void *user_data, pjmedia_frame *frame) { pjmedia_vid_port *vp = (pjmedia_vid_port*)user_data; pj_status_t status = PJ_SUCCESS; pj_bzero(frame, sizeof(pjmedia_frame)); if (vp->role==ROLE_ACTIVE) { unsigned frame_ts = vp->clocksrc.clock_rate / 1000 * vp->clocksrc.ptime_usec / 1000; if (!vp->client_port) return status; if (vp->sync_clocksrc.sync_clocksrc) { pjmedia_clock_src *src = vp->sync_clocksrc.sync_clocksrc; pj_int32_t diff; unsigned nsync_frame; /* Synchronization */ /* Calculate the time difference (in ms) with the sync source */ diff = pjmedia_clock_src_get_time_msec(&vp->clocksrc) - pjmedia_clock_src_get_time_msec(src) - vp->sync_clocksrc.sync_delta; /* Check whether sync source made a large jump */ if (diff < 0 && -diff > PJMEDIA_CLOCK_SYNC_MAX_SYNC_MSEC) { pjmedia_clock_src_update(&vp->clocksrc, NULL); vp->sync_clocksrc.sync_delta = pjmedia_clock_src_get_time_msec(src) - pjmedia_clock_src_get_time_msec(&vp->clocksrc); vp->sync_clocksrc.nsync_frame = 0; return status; } /* Calculate the difference (in frames) with the sync source */ nsync_frame = abs(diff) * 1000 / vp->clocksrc.ptime_usec; if (nsync_frame == 0) { /* Nothing to sync */ vp->sync_clocksrc.nsync_frame = 0; } else { pj_int32_t init_sync_frame = nsync_frame; /* Check whether it's a new sync or whether we need to reset * the sync */ if (vp->sync_clocksrc.nsync_frame == 0 || (vp->sync_clocksrc.nsync_frame > 0 && nsync_frame > vp->sync_clocksrc.nsync_frame)) { vp->sync_clocksrc.nsync_frame = nsync_frame; vp->sync_clocksrc.nsync_progress = 0; } else { init_sync_frame = vp->sync_clocksrc.nsync_frame; } if (diff >= 0) { unsigned skip_mod; /* We are too fast */ if (vp->sync_clocksrc.max_sync_ticks > 0) { skip_mod = init_sync_frame / vp->sync_clocksrc.max_sync_ticks + 2; } else skip_mod = init_sync_frame + 2; PJ_LOG(5, (THIS_FILE, "synchronization: early by %d ms", diff)); /* We'll play a frame every skip_mod-th tick instead of * a complete pause */ if (++vp->sync_clocksrc.nsync_progress % skip_mod > 0) { pjmedia_clock_src_update(&vp->clocksrc, NULL); return status; } } else { unsigned i, ndrop = init_sync_frame; /* We are too late, drop the frame */ if (vp->sync_clocksrc.max_sync_ticks > 0) { ndrop /= vp->sync_clocksrc.max_sync_ticks; ndrop++; } PJ_LOG(5, (THIS_FILE, "synchronization: late, " "dropping %d frame(s)", ndrop)); if (ndrop >= nsync_frame) { vp->sync_clocksrc.nsync_frame = 0; ndrop = nsync_frame; } else vp->sync_clocksrc.nsync_progress += ndrop; for (i = 0; i < ndrop; i++) { vp->frm_buf->size = vp->frm_buf_size; status = pjmedia_port_get_frame(vp->client_port, vp->frm_buf); if (status != PJ_SUCCESS) { pjmedia_clock_src_update(&vp->clocksrc, NULL); return status; } pj_add_timestamp32(&vp->clocksrc.timestamp, frame_ts); } } } } vp->frm_buf->size = vp->frm_buf_size; status = pjmedia_port_get_frame(vp->client_port, vp->frm_buf); if (status != PJ_SUCCESS) { pjmedia_clock_src_update(&vp->clocksrc, NULL); return status; } pj_add_timestamp32(&vp->clocksrc.timestamp, frame_ts); pjmedia_clock_src_update(&vp->clocksrc, NULL); status = convert_frame(vp, vp->frm_buf, frame); if (status != PJ_SUCCESS) return status; if (!vp->conv.conv) pj_memcpy(frame, vp->frm_buf, sizeof(*frame)); } else { /* The stream is active while we are passive so we need to get the * frame from the buffer. * The encoding counterpart is located in vid_pasv_port_get_frame() */ get_frame_from_buffer(vp, frame); } if (vp->strm_cb.render_cb) return (*vp->strm_cb.render_cb)(stream, vp->strm_cb_data, frame); return PJ_SUCCESS; } static pj_status_t vid_pasv_port_put_frame(struct pjmedia_port *this_port, pjmedia_frame *frame) { struct vid_pasv_port *vpp = (struct vid_pasv_port*)this_port; pjmedia_vid_port *vp = vpp->vp; if (vp->stream_role==ROLE_PASSIVE) { /* We are passive and the stream is passive. * The encoding counterpart is in vid_pasv_port_get_frame(). */ pj_status_t status; pjmedia_frame frame_; pj_bzero(&frame_, sizeof(frame_)); status = convert_frame(vp, frame, &frame_); if (status != PJ_SUCCESS) return status; return pjmedia_vid_dev_stream_put_frame(vp->strm, (vp->conv.conv? &frame_: frame)); } else { /* We are passive while the stream is active so we just store the * frame in the buffer. * The encoding counterpart is located in vidstream_cap_cb() */ copy_frame_to_buffer(vp, frame); } return PJ_SUCCESS; } static pj_status_t vid_pasv_port_get_frame(struct pjmedia_port *this_port, pjmedia_frame *frame) { struct vid_pasv_port *vpp = (struct vid_pasv_port*)this_port; pjmedia_vid_port *vp = vpp->vp; pj_status_t status = PJ_SUCCESS; if (vp->stream_role==ROLE_PASSIVE) { /* We are passive and the stream is passive. * The decoding counterpart is in vid_pasv_port_put_frame(). */ status = pjmedia_vid_dev_stream_get_frame(vp->strm, (vp->conv.conv? vp->frm_buf: frame)); if (status != PJ_SUCCESS) return status; status = convert_frame(vp, vp->frm_buf, frame); } else { /* The stream is active while we are passive so we need to get the * frame from the buffer. * The decoding counterpart is located in vidstream_rend_cb() */ get_frame_from_buffer(vp, frame); } return status; } #endif /* PJMEDIA_HAS_VIDEO */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/vid_stream.c ================================================ /* $Id: vid_stream.c 4197 2012-07-05 07:26:29Z nanang $ */ /* * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* memcpy() */ #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #define THIS_FILE "vid_stream.c" #define ERRLEVEL 1 #define LOGERR_(expr) stream_perror expr #define TRC_(expr) PJ_LOG(5,expr) #define SIGNATURE PJMEDIA_SIG_PORT_VID_STREAM #define TRACE_RC 0 /* Tracing jitter buffer operations in a stream session to a CSV file. * The trace will contain JB operation timestamp, frame info, RTP info, and * the JB state right after the operation. */ #define TRACE_JB 0 /* Enable/disable trace. */ #define TRACE_JB_PATH_PREFIX "" /* Optional path/prefix for the CSV filename. */ #if TRACE_JB # include # define TRACE_JB_INVALID_FD ((pj_oshandle_t)-1) # define TRACE_JB_OPENED(s) (s->trace_jb_fd != TRACE_JB_INVALID_FD) #endif #ifndef PJMEDIA_VSTREAM_SIZE # define PJMEDIA_VSTREAM_SIZE 1000 #endif #ifndef PJMEDIA_VSTREAM_INC # define PJMEDIA_VSTREAM_INC 1000 #endif /* Due to network MTU limitation, a picture bitstream may be splitted into * several chunks for RTP delivery. The chunk number may vary depend on the * picture resolution and MTU. This constant specifies the minimum chunk * number to be allocated to store a picture bitstream in decoding direction. */ #define MIN_CHUNKS_PER_FRM 30 /* Video stream keep-alive feature is currently disabled. */ #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0 # undef PJMEDIA_STREAM_ENABLE_KA # define PJMEDIA_STREAM_ENABLE_KA 0 #endif /** * Media channel. */ typedef struct pjmedia_vid_channel { pjmedia_vid_stream *stream; /**< Parent stream. */ pjmedia_dir dir; /**< Channel direction. */ pjmedia_port port; /**< Port interface. */ unsigned pt; /**< Payload type. */ pj_bool_t paused; /**< Paused?. */ void *buf; /**< Output buffer. */ unsigned buf_size; /**< Size of output buffer. */ pjmedia_rtp_session rtp; /**< RTP session. */ } pjmedia_vid_channel; /** * This structure describes media stream. * A media stream is bidirectional media transmission between two endpoints. * It consists of two channels, i.e. encoding and decoding channels. * A media stream corresponds to a single "m=" line in a SDP session * description. */ struct pjmedia_vid_stream { pj_pool_t *own_pool; /**< Internal pool. */ pjmedia_endpt *endpt; /**< Media endpoint. */ pjmedia_vid_codec_mgr *codec_mgr; /**< Codec manager. */ pjmedia_vid_stream_info info; /**< Stream info. */ pjmedia_vid_channel *enc; /**< Encoding channel. */ pjmedia_vid_channel *dec; /**< Decoding channel. */ pjmedia_dir dir; /**< Stream direction. */ void *user_data; /**< User data. */ pj_str_t name; /**< Stream name */ pj_str_t cname; /**< SDES CNAME */ pjmedia_transport *transport; /**< Stream transport. */ unsigned send_err_cnt; /**< Send error count. */ pj_mutex_t *jb_mutex; pjmedia_jbuf *jb; /**< Jitter buffer. */ char jb_last_frm; /**< Last frame type from jb */ unsigned jb_last_frm_cnt;/**< Last JB frame type counter*/ pjmedia_rtcp_session rtcp; /**< RTCP for incoming RTP. */ pj_uint32_t rtcp_last_tx; /**< RTCP tx time in timestamp */ pj_uint32_t rtcp_interval; /**< Interval, in timestamp. */ pj_bool_t initial_rr; /**< Initial RTCP RR sent */ pj_bool_t rtcp_sdes_bye_disabled;/**< Send RTCP SDES/BYE?*/ void *out_rtcp_pkt; /**< Outgoing RTCP packet. */ unsigned out_rtcp_pkt_size; /**< Outgoing RTCP packet size. */ unsigned dec_max_size; /**< Size of decoded/raw picture*/ pjmedia_ratio dec_max_fps; /**< Max fps of decoding dir. */ pjmedia_frame dec_frame; /**< Current decoded frame. */ pjmedia_event fmt_event; /**< Buffered fmt_changed event to avoid deadlock */ pjmedia_event found_keyframe_event; /**< Buffered found keyframe event for delayed republish*/ pjmedia_event miss_keyframe_event; /**< Buffered missing keyframe event for delayed republish*/ pjmedia_event keyframe_req_event; /**< Buffered keyframe request event for delayed republish*/ unsigned frame_size; /**< Size of encoded base frame.*/ unsigned frame_ts_len; /**< Frame length in timestamp. */ unsigned rx_frame_cnt; /**< # of array in rx_frames */ pjmedia_frame *rx_frames; /**< Temp. buffer for incoming frame assembly. */ pj_bool_t force_keyframe;/**< Forced to encode keyframe? */ #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 pj_bool_t use_ka; /**< Stream keep-alive with non- codec-VAD mechanism is enabled? */ pj_timestamp last_frm_ts_sent; /**< Timestamp of last sending packet */ #endif #if TRACE_JB pj_oshandle_t trace_jb_fd; /**< Jitter tracing file handle.*/ char *trace_jb_buf; /**< Jitter tracing buffer. */ #endif pjmedia_vid_codec *codec; /**< Codec instance being used. */ pj_uint32_t last_dec_ts; /**< Last decoded timestamp. */ int last_dec_seq; /**< Last decoded sequence. */ pj_status_t rtp_rx_last_err; /**< Last RTP recv() error. */ pj_timestamp ts_freq; /**< Timestamp frequency. */ #if TRACE_RC unsigned rc_total_sleep; unsigned rc_total_pkt; unsigned rc_total_img; pj_timestamp tx_start; pj_timestamp tx_end; #endif }; /* Prototypes */ static pj_status_t decode_frame(pjmedia_vid_stream *stream, pjmedia_frame *frame); /* * Print error. */ static void stream_perror(const char *sender, const char *title, pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(sender, "%s: %s [err:%d]", title, errmsg, status)); } static pj_status_t send_rtcp(pjmedia_vid_stream *stream, pj_bool_t with_sdes, pj_bool_t with_bye); #if TRACE_JB PJ_INLINE(int) trace_jb_print_timestamp(char **buf, pj_ssize_t len) { pj_time_val now; pj_parsed_time ptime; char *p = *buf; if (len < 14) return -1; pj_gettimeofday(&now); pj_time_decode(&now, &ptime); p += pj_utoa_pad(ptime.hour, p, 2, '0'); *p++ = ':'; p += pj_utoa_pad(ptime.min, p, 2, '0'); *p++ = ':'; p += pj_utoa_pad(ptime.sec, p, 2, '0'); *p++ = '.'; p += pj_utoa_pad(ptime.msec, p, 3, '0'); *p++ = ','; *buf = p; return 0; } PJ_INLINE(int) trace_jb_print_state(pjmedia_vid_stream *stream, char **buf, pj_ssize_t len) { char *p = *buf; char *endp = *buf + len; pjmedia_jb_state state; pjmedia_jbuf_get_state(stream->jb, &state); len = pj_ansi_snprintf(p, endp-p, "%d, %d, %d", state.size, state.burst, state.prefetch); if ((len < 0) || (len >= endp-p)) return -1; p += len; *buf = p; return 0; } static void trace_jb_get(pjmedia_vid_stream *stream, pjmedia_jb_frame_type ft, pj_size_t fsize) { char *p = stream->trace_jb_buf; char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE; pj_ssize_t len = 0; const char* ft_st; if (!TRACE_JB_OPENED(stream)) return; /* Print timestamp. */ if (trace_jb_print_timestamp(&p, endp-p)) goto on_insuff_buffer; /* Print frame type and size */ switch(ft) { case PJMEDIA_JB_MISSING_FRAME: ft_st = "missing"; break; case PJMEDIA_JB_NORMAL_FRAME: ft_st = "normal"; break; case PJMEDIA_JB_ZERO_PREFETCH_FRAME: ft_st = "prefetch"; break; case PJMEDIA_JB_ZERO_EMPTY_FRAME: ft_st = "empty"; break; default: ft_st = "unknown"; break; } /* Print operation, size, frame count, frame type */ len = pj_ansi_snprintf(p, endp-p, "GET,%d,1,%s,,,,", fsize, ft_st); if ((len < 0) || (len >= endp-p)) goto on_insuff_buffer; p += len; /* Print JB state */ if (trace_jb_print_state(stream, &p, endp-p)) goto on_insuff_buffer; /* Print end of line */ if (endp-p < 2) goto on_insuff_buffer; *p++ = '\n'; /* Write and flush */ len = p - stream->trace_jb_buf; pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); pj_file_flush(stream->trace_jb_fd); return; on_insuff_buffer: pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); } static void trace_jb_put(pjmedia_vid_stream *stream, const pjmedia_rtp_hdr *hdr, unsigned payloadlen, unsigned frame_cnt) { char *p = stream->trace_jb_buf; char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE; pj_ssize_t len = 0; if (!TRACE_JB_OPENED(stream)) return; /* Print timestamp. */ if (trace_jb_print_timestamp(&p, endp-p)) goto on_insuff_buffer; /* Print operation, size, frame count, RTP info */ len = pj_ansi_snprintf(p, endp-p, "PUT,%d,%d,,%d,%d,%d,", payloadlen, frame_cnt, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), hdr->m); if ((len < 0) || (len >= endp-p)) goto on_insuff_buffer; p += len; /* Print JB state */ if (trace_jb_print_state(stream, &p, endp-p)) goto on_insuff_buffer; /* Print end of line */ if (endp-p < 2) goto on_insuff_buffer; *p++ = '\n'; /* Write and flush */ len = p - stream->trace_jb_buf; pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); pj_file_flush(stream->trace_jb_fd); return; on_insuff_buffer: pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); } #endif /* TRACE_JB */ static void dump_port_info(const pjmedia_vid_channel *chan, const char *event_name) { const pjmedia_port_info *pi = &chan->port.info; char fourcc_name[5]; PJ_LOG(4, (pi->name.ptr, " %s format %s: %dx%d %s%s %d/%d(~%d)fps", (chan->dir==PJMEDIA_DIR_DECODING? "Decoding":"Encoding"), event_name, pi->fmt.det.vid.size.w, pi->fmt.det.vid.size.h, pjmedia_fourcc_name(pi->fmt.id, fourcc_name), (chan->dir==PJMEDIA_DIR_ENCODING?"->":"<-"), pi->fmt.det.vid.fps.num, pi->fmt.det.vid.fps.denum, pi->fmt.det.vid.fps.num/pi->fmt.det.vid.fps.denum)); } /* * Handle events from stream components. */ static pj_status_t stream_event_cb(pjmedia_event *event, void *user_data) { pjmedia_vid_stream *stream = (pjmedia_vid_stream*)user_data; if (event->epub == stream->codec) { /* This is codec event */ switch (event->type) { case PJMEDIA_EVENT_FMT_CHANGED: /* Copy the event to avoid deadlock if we publish the event * now. This happens because fmt_event may trigger restart * while we're still holding the jb_mutex. */ pj_memcpy(&stream->fmt_event, event, sizeof(*event)); return PJ_SUCCESS; case PJMEDIA_EVENT_KEYFRAME_FOUND: /* Republish this event later from get_frame(). */ pj_memcpy(&stream->found_keyframe_event, event, sizeof(*event)); return PJ_SUCCESS; case PJMEDIA_EVENT_KEYFRAME_MISSING: /* Republish this event later from get_frame(). */ pj_memcpy(&stream->miss_keyframe_event, event, sizeof(*event)); return PJ_SUCCESS; default: break; } } return pjmedia_event_publish(NULL, stream, event, 0); } #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0 /* * Send keep-alive packet using non-codec frame. */ static void send_keep_alive_packet(pjmedia_vid_stream *stream) { #if PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_EMPTY_RTP /* Keep-alive packet is empty RTP */ pjmedia_vid_channel *channel = stream->enc; pj_status_t status; void *pkt; int pkt_len; TRC_((channel->port.info.name.ptr, "Sending keep-alive (RTCP and empty RTP)")); /* Send RTP */ status = pjmedia_rtp_encode_rtp( &stream->enc->rtp, stream->enc->pt, 0, 1, 0, (const void**)&pkt, &pkt_len); pj_assert(status == PJ_SUCCESS); pj_memcpy(stream->enc->buf, pkt, pkt_len); pjmedia_transport_send_rtp(stream->transport, stream->enc->buf, pkt_len); /* Send RTCP */ send_rtcp(stream, PJ_TRUE, PJ_FALSE); #elif PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER /* Keep-alive packet is defined in PJMEDIA_STREAM_KA_USER_PKT */ pjmedia_vid_channel *channel = stream->enc; int pkt_len; const pj_str_t str_ka = PJMEDIA_STREAM_KA_USER_PKT; TRC_((channel->port.info.name.ptr, "Sending keep-alive (custom RTP/RTCP packets)")); /* Send to RTP port */ pj_memcpy(stream->enc->buf, str_ka.ptr, str_ka.slen); pkt_len = str_ka.slen; pjmedia_transport_send_rtp(stream->transport, stream->enc->buf, pkt_len); /* Send to RTCP port */ pjmedia_transport_send_rtcp(stream->transport, stream->enc->buf, pkt_len); #else PJ_UNUSED_ARG(stream); #endif } #endif /* defined(PJMEDIA_STREAM_ENABLE_KA) */ static pj_status_t send_rtcp(pjmedia_vid_stream *stream, pj_bool_t with_sdes, pj_bool_t with_bye) { void *sr_rr_pkt; pj_uint8_t *pkt; int len, max_len; pj_status_t status; /* Build RTCP RR/SR packet */ pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len); if (with_sdes || with_bye) { pkt = (pj_uint8_t*) stream->out_rtcp_pkt; pj_memcpy(pkt, sr_rr_pkt, len); max_len = stream->out_rtcp_pkt_size; } else { pkt = (pj_uint8_t*)sr_rr_pkt; max_len = len; } /* Build RTCP SDES packet */ if (with_sdes) { pjmedia_rtcp_sdes sdes; pj_size_t sdes_len; pj_bzero(&sdes, sizeof(sdes)); sdes.cname = stream->cname; sdes_len = max_len - len; status = pjmedia_rtcp_build_rtcp_sdes(&stream->rtcp, pkt+len, &sdes_len, &sdes); if (status != PJ_SUCCESS) { PJ_PERROR(4,(stream->name.ptr, status, "Error generating RTCP SDES")); } else { len += (int)sdes_len; } } /* Build RTCP BYE packet */ if (with_bye) { pj_size_t bye_len; bye_len = max_len - len; status = pjmedia_rtcp_build_rtcp_bye(&stream->rtcp, pkt+len, &bye_len, NULL); if (status != PJ_SUCCESS) { PJ_PERROR(4,(stream->name.ptr, status, "Error generating RTCP BYE")); } else { len += (int)bye_len; } } /* Send! */ status = pjmedia_transport_send_rtcp(stream->transport, pkt, len); return status; } /** * check_tx_rtcp() * * This function is can be called by either put_frame() or get_frame(), * to transmit periodic RTCP SR/RR report. */ static void check_tx_rtcp(pjmedia_vid_stream *stream, pj_uint32_t timestamp) { /* Note that timestamp may represent local or remote timestamp, * depending on whether this function is called from put_frame() * or get_frame(). */ if (stream->rtcp_last_tx == 0) { stream->rtcp_last_tx = timestamp; } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) { pj_status_t status; status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, PJ_FALSE); if (status != PJ_SUCCESS) { PJ_PERROR(4,(stream->name.ptr, status, "Error sending RTCP")); } stream->rtcp_last_tx = timestamp; } } #if 0 static void dump_bin(const char *buf, unsigned len) { unsigned i; PJ_LOG(3,(THIS_FILE, "begin dump")); for (i=0; idec; const pjmedia_rtp_hdr *hdr; const void *payload; unsigned payloadlen; pjmedia_rtp_status seq_st; pj_status_t status; pj_bool_t pkt_discarded = PJ_FALSE; /* Check for errors */ if (bytes_read < 0) { status = (pj_status_t)-bytes_read; if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { return; } if (stream->rtp_rx_last_err != status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(channel->port.info.name.ptr, "Unable to receive RTP packet, recv() returned %d: %s", status, errmsg)); stream->rtp_rx_last_err = status; } return; } else { stream->rtp_rx_last_err = PJ_SUCCESS; } /* Ignore keep-alive packets */ if (bytes_read < (pj_ssize_t) sizeof(pjmedia_rtp_hdr)) return; /* Update RTP and RTCP session. */ status = pjmedia_rtp_decode_rtp(&channel->rtp, pkt, (int)bytes_read, &hdr, &payload, &payloadlen); if (status != PJ_SUCCESS) { LOGERR_((channel->port.info.name.ptr, "RTP decode error", status)); stream->rtcp.stat.rx.discard++; return; } /* Ignore the packet if decoder is paused */ if (channel->paused) goto on_return; /* Update RTP session (also checks if RTP session can accept * the incoming packet. */ pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, PJ_TRUE); if (seq_st.status.value) { TRC_ ((channel->port.info.name.ptr, "RTP status: badpt=%d, badssrc=%d, dup=%d, " "outorder=%d, probation=%d, restart=%d", seq_st.status.flag.badpt, seq_st.status.flag.badssrc, seq_st.status.flag.dup, seq_st.status.flag.outorder, seq_st.status.flag.probation, seq_st.status.flag.restart)); if (seq_st.status.flag.badpt) { PJ_LOG(4,(channel->port.info.name.ptr, "Bad RTP pt %d (expecting %d)", hdr->pt, channel->rtp.out_pt)); } if (seq_st.status.flag.badssrc) { PJ_LOG(4,(channel->port.info.name.ptr, "Changed RTP peer SSRC %d (previously %d)", channel->rtp.peer_ssrc, stream->rtcp.peer_ssrc)); stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc; } } /* Skip bad RTP packet */ if (seq_st.status.flag.bad) { pkt_discarded = PJ_TRUE; goto on_return; } /* Ignore if payloadlen is zero */ if (payloadlen == 0) { pkt_discarded = PJ_TRUE; goto on_return; } pj_mutex_lock( stream->jb_mutex ); /* Quickly see if there may be a full picture in the jitter buffer, and * decode them if so. More thorough check will be done in decode_frame(). */ if ((pj_ntohl(hdr->ts) != stream->dec_frame.timestamp.u32.lo) || hdr->m) { if (PJMEDIA_VID_STREAM_SKIP_PACKETS_TO_REDUCE_LATENCY) { /* Always decode whenever we have picture in jb and * overwrite already decoded picture if necessary */ pj_size_t old_size = stream->dec_frame.size; stream->dec_frame.size = stream->dec_max_size; if (decode_frame(stream, &stream->dec_frame) != PJ_SUCCESS) { stream->dec_frame.size = old_size; } } else { /* Only decode if we don't already have decoded one, * unless the jb is full. */ pj_bool_t can_decode = PJ_FALSE; if (pjmedia_jbuf_is_full(stream->jb)) { can_decode = PJ_TRUE; } else if (stream->dec_frame.size == 0) { can_decode = PJ_TRUE; } if (can_decode) { stream->dec_frame.size = stream->dec_max_size; if (decode_frame(stream, &stream->dec_frame) != PJ_SUCCESS) { stream->dec_frame.size = 0; } } } } /* Put "good" packet to jitter buffer, or reset the jitter buffer * when RTP session is restarted. */ if (seq_st.status.flag.restart) { status = pjmedia_jbuf_reset(stream->jb); PJ_LOG(4,(channel->port.info.name.ptr, "Jitter buffer reset")); } else { /* Just put the payload into jitter buffer */ pjmedia_jbuf_put_frame3(stream->jb, payload, payloadlen, 0, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), NULL); #if TRACE_JB trace_jb_put(stream, hdr, payloadlen, count); #endif } pj_mutex_unlock( stream->jb_mutex ); /* Check if now is the time to transmit RTCP SR/RR report. * We only do this when stream direction is "decoding only", * because otherwise check_tx_rtcp() will be handled by put_frame() */ if (stream->dir == PJMEDIA_DIR_DECODING) { check_tx_rtcp(stream, pj_ntohl(hdr->ts)); } if (status != 0) { LOGERR_((channel->port.info.name.ptr, "Jitter buffer put() error", status)); pkt_discarded = PJ_TRUE; goto on_return; } on_return: /* Update RTCP session */ if (stream->rtcp.peer_ssrc == 0) stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc; pjmedia_rtcp_rx_rtp2(&stream->rtcp, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), payloadlen, pkt_discarded); /* Send RTCP RR and SDES after we receive some RTP packets */ if (stream->rtcp.received >= 10 && !stream->initial_rr) { status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, PJ_FALSE); if (status != PJ_SUCCESS) { PJ_PERROR(4,(stream->name.ptr, status, "Error sending initial RTCP RR")); } else { stream->initial_rr = PJ_TRUE; } } } /* * This callback is called by stream transport on receipt of packets * in the RTCP socket. */ static void on_rx_rtcp( void *data, void *pkt, pj_ssize_t bytes_read) { pjmedia_vid_stream *stream = (pjmedia_vid_stream*) data; /* Check for errors */ if (bytes_read < 0) { if (bytes_read != -PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { LOGERR_((stream->cname.ptr, "RTCP recv() error", (pj_status_t)-bytes_read)); } return; } pjmedia_rtcp_rx_rtcp(&stream->rtcp, pkt, bytes_read); /* XXX: posting some event from the RTCP session might be a better option */ if (stream->rtcp.keyframe_requested) { pjmedia_event event; pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_REQUESTED, NULL, stream); pj_memcpy(&stream->keyframe_req_event, &event, sizeof(event)); } } static pj_status_t put_frame(pjmedia_port *port, pjmedia_frame *frame) { pjmedia_vid_stream *stream = (pjmedia_vid_stream*) port->port_data.pdata; pjmedia_vid_channel *channel = stream->enc; pj_status_t status = 0; pjmedia_frame frame_out; unsigned rtp_ts_len; void *rtphdr; int rtphdrlen; pj_bool_t has_more_data = PJ_FALSE; pj_size_t total_sent = 0; pjmedia_vid_encode_opt enc_opt; unsigned pkt_cnt = 0; pj_timestamp initial_time; #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0 /* If the interval since last sending packet is greater than * PJMEDIA_STREAM_KA_INTERVAL, send keep-alive packet. */ if (stream->use_ka) { pj_uint32_t dtx_duration; dtx_duration = pj_timestamp_diff32(&stream->last_frm_ts_sent, &frame->timestamp); /* Video stream keep-alive feature is currently disabled. */ /* if (dtx_duration > PJMEDIA_STREAM_KA_INTERVAL * PJMEDIA_PIA_SRATE(&channel->port.info)) { send_keep_alive_packet(stream); stream->last_frm_ts_sent = frame->timestamp; } */ } #endif /* Don't do anything if stream is paused */ if (channel->paused) { return PJ_SUCCESS; } /* Get frame length in timestamp unit */ rtp_ts_len = stream->frame_ts_len; /* Init frame_out buffer. */ frame_out.buf = ((char*)channel->buf) + sizeof(pjmedia_rtp_hdr); frame_out.size = 0; /* Init encoding option */ pj_bzero(&enc_opt, sizeof(enc_opt)); if (stream->force_keyframe) { /* Force encoder to generate keyframe */ enc_opt.force_keyframe = PJ_TRUE; stream->force_keyframe = PJ_FALSE; TRC_((channel->port.info.name.ptr, "Forcing encoder to generate keyframe")); } /* Encode! */ status = pjmedia_vid_codec_encode_begin(stream->codec, &enc_opt, frame, channel->buf_size - sizeof(pjmedia_rtp_hdr), &frame_out, &has_more_data); if (status != PJ_SUCCESS) { LOGERR_((channel->port.info.name.ptr, "Codec encode_begin() error", status)); /* Update RTP timestamp */ pjmedia_rtp_encode_rtp(&channel->rtp, channel->pt, 1, 0, rtp_ts_len, (const void**)&rtphdr, &rtphdrlen); return status; } pj_get_timestamp(&initial_time); /* Loop while we have frame to send */ for (;;) { status = pjmedia_rtp_encode_rtp(&channel->rtp, channel->pt, (has_more_data == PJ_FALSE ? 1 : 0), (int)frame_out.size, rtp_ts_len, (const void**)&rtphdr, &rtphdrlen); if (status != PJ_SUCCESS) { LOGERR_((channel->port.info.name.ptr, "RTP encode_rtp() error", status)); return status; } /* When the payload length is zero, we should not send anything, * but proceed the rest normally. */ if (frame_out.size != 0) { /* Copy RTP header to the beginning of packet */ pj_memcpy(channel->buf, rtphdr, sizeof(pjmedia_rtp_hdr)); /* Send the RTP packet to the transport. */ status = pjmedia_transport_send_rtp(stream->transport, (char*)channel->buf, frame_out.size + sizeof(pjmedia_rtp_hdr)); if (status != PJ_SUCCESS) { enum { COUNT_TO_REPORT = 20 }; if (stream->send_err_cnt++ == 0) { LOGERR_((channel->port.info.name.ptr, "Transport send_rtp() error", status)); } if (stream->send_err_cnt > COUNT_TO_REPORT) stream->send_err_cnt = 0; /* Ignore this error */ } pjmedia_rtcp_tx_rtp(&stream->rtcp, (unsigned)frame_out.size); total_sent += frame_out.size; pkt_cnt++; } if (!has_more_data) break; /* Next packets use same timestamp */ rtp_ts_len = 0; frame_out.size = 0; /* Encode more! */ status = pjmedia_vid_codec_encode_more(stream->codec, channel->buf_size - sizeof(pjmedia_rtp_hdr), &frame_out, &has_more_data); if (status != PJ_SUCCESS) { LOGERR_((channel->port.info.name.ptr, "Codec encode_more() error", status)); /* Ignore this error (?) */ break; } /* Send rate control */ if (stream->info.rc_cfg.method==PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING) { pj_timestamp now, next_send_ts, total_send_ts; total_send_ts.u64 = total_sent * stream->ts_freq.u64 * 8 / stream->info.rc_cfg.bandwidth; next_send_ts = initial_time; pj_add_timestamp(&next_send_ts, &total_send_ts); pj_get_timestamp(&now); if (pj_cmp_timestamp(&now, &next_send_ts) < 0) { unsigned ms_sleep; ms_sleep = pj_elapsed_msec(&now, &next_send_ts); if (ms_sleep > 10) ms_sleep = 10; pj_thread_sleep(ms_sleep); } } } #if TRACE_RC /* Trace log for rate control */ { pj_timestamp end_time; unsigned total_sleep; pj_get_timestamp(&end_time); total_sleep = pj_elapsed_msec(&initial_time, &end_time); PJ_LOG(5, (stream->name.ptr, "total pkt=%d size=%d sleep=%d", pkt_cnt, total_sent, total_sleep)); if (stream->tx_start.u64 == 0) stream->tx_start = initial_time; stream->tx_end = end_time; stream->rc_total_pkt += pkt_cnt; stream->rc_total_sleep += total_sleep; stream->rc_total_img++; } #endif /* Check if now is the time to transmit RTCP SR/RR report. * We only do this when stream direction is not "decoding only", because * when it is, check_tx_rtcp() will be handled by get_frame(). */ if (stream->dir != PJMEDIA_DIR_DECODING) { check_tx_rtcp(stream, pj_ntohl(channel->rtp.out_hdr.ts)); } /* Do nothing if we have nothing to transmit */ if (total_sent == 0) { return PJ_SUCCESS; } /* Update stat */ if (pkt_cnt) { stream->rtcp.stat.rtp_tx_last_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts); stream->rtcp.stat.rtp_tx_last_seq = pj_ntohs(stream->enc->rtp.out_hdr.seq); } #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /* Update timestamp of last sending packet. */ stream->last_frm_ts_sent = frame->timestamp; #endif return PJ_SUCCESS; } /* Decode one image from jitter buffer */ static pj_status_t decode_frame(pjmedia_vid_stream *stream, pjmedia_frame *frame) { pjmedia_vid_channel *channel = stream->dec; pj_uint32_t last_ts = 0; int frm_first_seq = 0, frm_last_seq = 0; pj_bool_t got_frame = PJ_FALSE; unsigned cnt; pj_status_t status; /* Repeat get payload from the jitter buffer until all payloads with same * timestamp are collected. */ /* Check if we got a decodable frame */ for (cnt=0; ; ++cnt) { char ptype; pj_uint32_t ts; int seq; /* Peek frame from jitter buffer. */ pjmedia_jbuf_peek_frame(stream->jb, cnt, NULL, NULL, &ptype, NULL, &ts, &seq); if (ptype == PJMEDIA_JB_NORMAL_FRAME) { if (last_ts == 0) { last_ts = ts; frm_first_seq = seq; } if (ts != last_ts) { got_frame = PJ_TRUE; break; } frm_last_seq = seq; } else if (ptype == PJMEDIA_JB_ZERO_EMPTY_FRAME) { /* No more packet in the jitter buffer */ break; } } if (got_frame) { unsigned i; /* Generate frame bitstream from the payload */ if (cnt > stream->rx_frame_cnt) { PJ_LOG(1,(channel->port.info.name.ptr, "Discarding %u frames because array is full!", cnt - stream->rx_frame_cnt)); pjmedia_jbuf_remove_frame(stream->jb, cnt - stream->rx_frame_cnt); cnt = stream->rx_frame_cnt; } for (i = 0; i < cnt; ++i) { char ptype; stream->rx_frames[i].type = PJMEDIA_FRAME_TYPE_VIDEO; stream->rx_frames[i].timestamp.u64 = last_ts; stream->rx_frames[i].bit_info = 0; /* We use jbuf_peek_frame() as it will returns the pointer of * the payload (no buffer and memcpy needed), just as we need. */ pjmedia_jbuf_peek_frame(stream->jb, i, (const void**)&stream->rx_frames[i].buf, &stream->rx_frames[i].size, &ptype, NULL, NULL, NULL); if (ptype != PJMEDIA_JB_NORMAL_FRAME) { /* Packet lost, must set payload to NULL and keep going */ stream->rx_frames[i].buf = NULL; stream->rx_frames[i].size = 0; stream->rx_frames[i].type = PJMEDIA_FRAME_TYPE_NONE; continue; } } /* Decode */ status = pjmedia_vid_codec_decode(stream->codec, cnt, stream->rx_frames, (unsigned)frame->size, frame); if (status != PJ_SUCCESS) { LOGERR_((channel->port.info.name.ptr, "codec decode() error", status)); frame->type = PJMEDIA_FRAME_TYPE_NONE; frame->size = 0; } pjmedia_jbuf_remove_frame(stream->jb, cnt); } /* Learn remote frame rate after successful decoding */ if (frame->type == PJMEDIA_FRAME_TYPE_VIDEO && frame->size) { /* Only check remote frame rate when timestamp is not wrapping and * sequence is increased by 1. */ if (last_ts > stream->last_dec_ts && frm_first_seq - stream->last_dec_seq == 1) { pj_uint32_t ts_diff; pjmedia_video_format_detail *vfd; ts_diff = last_ts - stream->last_dec_ts; vfd = pjmedia_format_get_video_format_detail( &channel->port.info.fmt, PJ_TRUE); if (stream->info.codec_info.clock_rate * vfd->fps.denum != vfd->fps.num * ts_diff) { /* Frame rate changed, update decoding port info */ if (stream->info.codec_info.clock_rate % ts_diff == 0) { vfd->fps.num = stream->info.codec_info.clock_rate/ts_diff; vfd->fps.denum = 1; } else { vfd->fps.num = stream->info.codec_info.clock_rate; vfd->fps.denum = ts_diff; } /* Update stream info */ stream->info.codec_param->dec_fmt.det.vid.fps = vfd->fps; /* Publish PJMEDIA_EVENT_FMT_CHANGED event if frame rate * increased and not exceeding 60fps. */ if (vfd->fps.num/vfd->fps.denum <= 60.0 && vfd->fps.num * stream->dec_max_fps.denum > stream->dec_max_fps.num * vfd->fps.denum) { /*printf("FPS CHANGED: %d/%d -> %d/%d\n", stream->dec_max_fps.num, stream->dec_max_fps.denum, vfd->fps.num, vfd->fps.denum);*/ pjmedia_event *event = &stream->fmt_event; /* Update max fps of decoding dir */ stream->dec_max_fps = vfd->fps; /* Use the buffered format changed event: * - just update the framerate if there is pending event, * - otherwise, init the whole event. */ if (stream->fmt_event.type != PJMEDIA_EVENT_NONE) { event->data.fmt_changed.new_fmt.det.vid.fps = vfd->fps; } else { pjmedia_event_init(event, PJMEDIA_EVENT_FMT_CHANGED, &frame->timestamp, stream); event->data.fmt_changed.dir = PJMEDIA_DIR_DECODING; pj_memcpy(&event->data.fmt_changed.new_fmt, &stream->info.codec_param->dec_fmt, sizeof(pjmedia_format)); } } } } /* Update last frame seq and timestamp */ stream->last_dec_seq = frm_last_seq; stream->last_dec_ts = last_ts; } return got_frame ? PJ_SUCCESS : PJ_ENOTFOUND; } static pj_status_t get_frame(pjmedia_port *port, pjmedia_frame *frame) { pjmedia_vid_stream *stream = (pjmedia_vid_stream*) port->port_data.pdata; pjmedia_vid_channel *channel = stream->dec; /* Return no frame is channel is paused */ if (channel->paused) { frame->type = PJMEDIA_FRAME_TYPE_NONE; frame->size = 0; return PJ_SUCCESS; } /* Report pending events. Do not publish the event while holding the * jb_mutex as that would lead to deadlock. It should be safe to * operate on fmt_event without the mutex because format change normally * would only occur once during the start of the media. */ if (stream->fmt_event.type != PJMEDIA_EVENT_NONE) { pjmedia_event_fmt_changed_data *fmt_chg_data; fmt_chg_data = &stream->fmt_event.data.fmt_changed; /* Update stream info and decoding channel port info */ if (fmt_chg_data->dir == PJMEDIA_DIR_DECODING) { pjmedia_format_copy(&stream->info.codec_param->dec_fmt, &fmt_chg_data->new_fmt); pjmedia_format_copy(&stream->dec->port.info.fmt, &fmt_chg_data->new_fmt); /* Override the framerate to be 1.5x higher in the event * for the renderer. */ #if 0 fmt_chg_data->new_fmt.det.vid.fps.num *= 3; fmt_chg_data->new_fmt.det.vid.fps.num /= 2; #endif } else { pjmedia_format_copy(&stream->info.codec_param->enc_fmt, &fmt_chg_data->new_fmt); pjmedia_format_copy(&stream->enc->port.info.fmt, &fmt_chg_data->new_fmt); } dump_port_info(fmt_chg_data->dir==PJMEDIA_DIR_DECODING ? stream->dec : stream->enc, "changed"); pjmedia_event_publish(NULL, port, &stream->fmt_event, 0); stream->fmt_event.type = PJMEDIA_EVENT_NONE; } if (stream->found_keyframe_event.type != PJMEDIA_EVENT_NONE) { pjmedia_event_publish(NULL, port, &stream->found_keyframe_event, PJMEDIA_EVENT_PUBLISH_POST_EVENT); stream->found_keyframe_event.type = PJMEDIA_EVENT_NONE; } if (stream->miss_keyframe_event.type != PJMEDIA_EVENT_NONE) { pjmedia_event_publish(NULL, port, &stream->miss_keyframe_event, PJMEDIA_EVENT_PUBLISH_POST_EVENT); stream->miss_keyframe_event.type = PJMEDIA_EVENT_NONE; } if (stream->keyframe_req_event.type != PJMEDIA_EVENT_NONE) { pjmedia_event_publish(NULL, port, &stream->keyframe_req_event, PJMEDIA_EVENT_PUBLISH_POST_EVENT); stream->keyframe_req_event.type = PJMEDIA_EVENT_NONE; } pj_mutex_lock( stream->jb_mutex ); if (stream->dec_frame.size == 0) { /* Don't have frame in buffer, try to decode one */ if (decode_frame(stream, frame) != PJ_SUCCESS) { frame->type = PJMEDIA_FRAME_TYPE_NONE; frame->size = 0; } } else { if (frame->size < stream->dec_frame.size) { PJ_LOG(4,(stream->dec->port.info.name.ptr, "Error: not enough buffer for decoded frame " "(supplied=%d, required=%d)", (int)frame->size, (int)stream->dec_frame.size)); frame->type = PJMEDIA_FRAME_TYPE_NONE; frame->size = 0; } else { frame->type = stream->dec_frame.type; frame->timestamp = stream->dec_frame.timestamp; frame->size = stream->dec_frame.size; pj_memcpy(frame->buf, stream->dec_frame.buf, frame->size); } stream->dec_frame.size = 0; } pj_mutex_unlock( stream->jb_mutex ); return PJ_SUCCESS; } /* * Create media channel. */ static pj_status_t create_channel( pj_pool_t *pool, pjmedia_vid_stream *stream, pjmedia_dir dir, unsigned pt, const pjmedia_vid_stream_info *info, pjmedia_vid_channel **p_channel) { enum { M = 32 }; pjmedia_vid_channel *channel; pj_status_t status; unsigned min_out_pkt_size; pj_str_t name; const char *type_name; pjmedia_format *fmt; char fourcc_name[5]; pjmedia_port_info *pi; pj_assert(info->type == PJMEDIA_TYPE_VIDEO); pj_assert(dir == PJMEDIA_DIR_DECODING || dir == PJMEDIA_DIR_ENCODING); /* Allocate memory for channel descriptor */ channel = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_channel); PJ_ASSERT_RETURN(channel != NULL, PJ_ENOMEM); /* Init vars */ if (dir==PJMEDIA_DIR_DECODING) { type_name = "vstdec"; fmt = &info->codec_param->dec_fmt; } else { type_name = "vstenc"; fmt = &info->codec_param->enc_fmt; } name.ptr = (char*) pj_pool_alloc(pool, M); name.slen = pj_ansi_snprintf(name.ptr, M, "%s%p", type_name, stream); pi = &channel->port.info; /* Init channel info. */ channel->stream = stream; channel->dir = dir; channel->paused = 1; channel->pt = pt; /* Allocate buffer for outgoing packet. */ if (dir == PJMEDIA_DIR_ENCODING) { channel->buf_size = sizeof(pjmedia_rtp_hdr) + stream->frame_size; /* It should big enough to hold (minimally) RTCP SR with an SDES. */ min_out_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) + sizeof(pjmedia_rtcp_common) + (4 + (unsigned)stream->cname.slen) + 32; if (channel->buf_size < min_out_pkt_size) channel->buf_size = min_out_pkt_size; channel->buf = pj_pool_alloc(pool, channel->buf_size); PJ_ASSERT_RETURN(channel->buf != NULL, PJ_ENOMEM); } /* Create RTP and RTCP sessions: */ if (info->rtp_seq_ts_set == 0) { status = pjmedia_rtp_session_init(&channel->rtp, pt, info->ssrc); } else { pjmedia_rtp_session_setting settings; settings.flags = (pj_uint8_t)((info->rtp_seq_ts_set << 2) | 3); settings.default_pt = pt; settings.sender_ssrc = info->ssrc; settings.seq = info->rtp_seq; settings.ts = info->rtp_ts; status = pjmedia_rtp_session_init2(&channel->rtp, settings); } if (status != PJ_SUCCESS) return status; /* Init port. */ pjmedia_port_info_init2(pi, &name, SIGNATURE, dir, fmt); if (dir == PJMEDIA_DIR_DECODING) { channel->port.get_frame = &get_frame; } else { pi->fmt.id = info->codec_param->dec_fmt.id; channel->port.put_frame = &put_frame; } /* Init port. */ channel->port.port_data.pdata = stream; PJ_LOG(5, (name.ptr, "%s channel created %dx%d %s%s%.*s %d/%d(~%d)fps", (dir==PJMEDIA_DIR_ENCODING?"Encoding":"Decoding"), pi->fmt.det.vid.size.w, pi->fmt.det.vid.size.h, pjmedia_fourcc_name(pi->fmt.id, fourcc_name), (dir==PJMEDIA_DIR_ENCODING?"->":"<-"), info->codec_info.encoding_name.slen, info->codec_info.encoding_name.ptr, pi->fmt.det.vid.fps.num, pi->fmt.det.vid.fps.denum, pi->fmt.det.vid.fps.num/pi->fmt.det.vid.fps.denum)); /* Done. */ *p_channel = channel; return PJ_SUCCESS; } /* * Create stream. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( pjmedia_endpt *endpt, pj_pool_t *pool, pjmedia_vid_stream_info *info, pjmedia_transport *tp, void *user_data, pjmedia_vid_stream **p_stream) { enum { M = 32 }; pj_pool_t *own_pool = NULL; pjmedia_vid_stream *stream; unsigned jb_init, jb_max, jb_min_pre, jb_max_pre; int frm_ptime, chunks_per_frm; pjmedia_video_format_detail *vfd_enc, *vfd_dec; char *p; pj_status_t status; if (!pool) { own_pool = pjmedia_endpt_create_pool( endpt, "vstrm%p", PJMEDIA_VSTREAM_SIZE, PJMEDIA_VSTREAM_INC); PJ_ASSERT_RETURN(own_pool != NULL, PJ_ENOMEM); pool = own_pool; } /* Allocate stream */ stream = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_stream); PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM); stream->own_pool = own_pool; /* Get codec manager */ stream->codec_mgr = pjmedia_vid_codec_mgr_instance(); PJ_ASSERT_RETURN(stream->codec_mgr, PJMEDIA_CODEC_EFAILED); /* Init stream/port name */ stream->name.ptr = (char*) pj_pool_alloc(pool, M); stream->name.slen = pj_ansi_snprintf(stream->name.ptr, M, "vstrm%p", stream); /* Create and initialize codec: */ status = pjmedia_vid_codec_mgr_alloc_codec(stream->codec_mgr, &info->codec_info, &stream->codec); if (status != PJ_SUCCESS) return status; /* Get codec param: */ if (!info->codec_param) { pjmedia_vid_codec_param def_param; status = pjmedia_vid_codec_mgr_get_default_param(stream->codec_mgr, &info->codec_info, &def_param); if (status != PJ_SUCCESS) return status; info->codec_param = pjmedia_vid_codec_param_clone(pool, &def_param); pj_assert(info->codec_param); } /* Init codec param and adjust MTU */ info->codec_param->dir = info->dir; info->codec_param->enc_mtu -= (sizeof(pjmedia_rtp_hdr) + PJMEDIA_STREAM_RESV_PAYLOAD_LEN); if (info->codec_param->enc_mtu > PJMEDIA_MAX_MTU) info->codec_param->enc_mtu = PJMEDIA_MAX_MTU; /* Packet size estimation for decoding direction */ vfd_enc = pjmedia_format_get_video_format_detail( &info->codec_param->enc_fmt, PJ_TRUE); vfd_dec = pjmedia_format_get_video_format_detail( &info->codec_param->dec_fmt, PJ_TRUE); /* Init stream: */ stream->endpt = endpt; stream->dir = info->dir; stream->user_data = user_data; stream->rtcp_interval = (PJMEDIA_RTCP_INTERVAL-500 + (pj_rand()%1000)) * info->codec_info.clock_rate / 1000; stream->rtcp_sdes_bye_disabled = info->rtcp_sdes_bye_disabled; stream->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME; #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 stream->use_ka = info->use_ka; #endif /* Build random RTCP CNAME. CNAME has user@host format */ stream->cname.ptr = p = (char*) pj_pool_alloc(pool, 20); pj_create_random_string(p, 5); p += 5; *p++ = '@'; *p++ = 'p'; *p++ = 'j'; pj_create_random_string(p, 6); p += 6; *p++ = '.'; *p++ = 'o'; *p++ = 'r'; *p++ = 'g'; stream->cname.slen = p - stream->cname.ptr; /* Create mutex to protect jitter buffer: */ status = pj_mutex_create_simple(pool, NULL, &stream->jb_mutex); if (status != PJ_SUCCESS) return status; /* Init and open the codec. */ status = pjmedia_vid_codec_init(stream->codec, pool); if (status != PJ_SUCCESS) return status; status = pjmedia_vid_codec_open(stream->codec, info->codec_param); if (status != PJ_SUCCESS) return status; /* Subscribe to codec events */ pjmedia_event_subscribe(NULL, &stream_event_cb, stream, stream->codec); /* Estimate the maximum frame size */ stream->frame_size = vfd_enc->size.w * vfd_enc->size.h * 4; #if 0 stream->frame_size = vfd_enc->max_bps/8 * vfd_enc->fps.denum / vfd_enc->fps.num; /* As the maximum frame_size is not represented directly by maximum bps * (which includes intra and predicted frames), let's increase the * frame size value for safety. */ stream->frame_size <<= 4; #endif /* Validate the frame size */ if (stream->frame_size == 0 || stream->frame_size > PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE) { stream->frame_size = PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE; } /* Get frame length in timestamp unit */ stream->frame_ts_len = info->codec_info.clock_rate * vfd_enc->fps.denum / vfd_enc->fps.num; /* Initialize send rate states */ pj_get_timestamp_freq(&stream->ts_freq); if (info->rc_cfg.bandwidth == 0) info->rc_cfg.bandwidth = vfd_enc->max_bps; /* For simple blocking, need to have bandwidth large enough, otherwise * we can slow down the transmission too much */ if (info->rc_cfg.method==PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING && info->rc_cfg.bandwidth < vfd_enc->avg_bps * 3) { info->rc_cfg.bandwidth = vfd_enc->avg_bps * 3; } /* Override the initial framerate in the decoding direction. This initial * value will be used by the renderer to configure its clock, and setting * it to a bit higher value can avoid the possibility of high latency * caused by clock drift (remote encoder clock runs slightly faster than * local renderer clock) or video setup lag. Note that the actual framerate * will be continuously calculated based on the incoming RTP timestamps. */ #if 0 vfd_dec->fps.num = vfd_dec->fps.num * 3 / 2; #endif stream->dec_max_fps = vfd_dec->fps; /* Create decoder channel */ status = create_channel( pool, stream, PJMEDIA_DIR_DECODING, info->rx_pt, info, &stream->dec); if (status != PJ_SUCCESS) return status; /* Create encoder channel */ status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING, info->tx_pt, info, &stream->enc); if (status != PJ_SUCCESS) return status; /* Create temporary buffer for immediate decoding */ stream->dec_max_size = vfd_dec->size.w * vfd_dec->size.h * 4; stream->dec_frame.buf = pj_pool_alloc(pool, stream->dec_max_size); /* Init jitter buffer parameters: */ frm_ptime = 1000 * vfd_enc->fps.denum / vfd_enc->fps.num; chunks_per_frm = stream->frame_size / PJMEDIA_MAX_MRU; if (chunks_per_frm < MIN_CHUNKS_PER_FRM) chunks_per_frm = MIN_CHUNKS_PER_FRM; /* JB max count, default 500ms */ if (info->jb_max >= frm_ptime) jb_max = info->jb_max * chunks_per_frm / frm_ptime; else jb_max = 500 * chunks_per_frm / frm_ptime; /* JB min prefetch, default 1 frame */ if (info->jb_min_pre >= frm_ptime) jb_min_pre = info->jb_min_pre * chunks_per_frm / frm_ptime; else jb_min_pre = 1; /* JB max prefetch, default 4/5 JB max count */ if (info->jb_max_pre >= frm_ptime) jb_max_pre = info->jb_max_pre * chunks_per_frm / frm_ptime; else jb_max_pre = jb_max * 4 / 5; /* JB init prefetch, default 0 */ if (info->jb_init >= frm_ptime) jb_init = info->jb_init * chunks_per_frm / frm_ptime; else jb_init = 0; /* Allocate array for temporary storage for assembly of incoming * frames. Add more just in case. */ stream->rx_frame_cnt = chunks_per_frm * 2; stream->rx_frames = pj_pool_calloc(pool, stream->rx_frame_cnt, sizeof(stream->rx_frames[0])); /* Create jitter buffer */ status = pjmedia_jbuf_create(pool, &stream->dec->port.info.name, PJMEDIA_MAX_MRU, 1000 * vfd_enc->fps.denum / vfd_enc->fps.num, jb_max, &stream->jb); if (status != PJ_SUCCESS) return status; /* Set up jitter buffer */ pjmedia_jbuf_set_adaptive(stream->jb, jb_init, jb_min_pre, jb_max_pre); pjmedia_jbuf_set_discard(stream->jb, PJMEDIA_JB_DISCARD_NONE); /* Init RTCP session: */ { pjmedia_rtcp_session_setting rtcp_setting; pjmedia_rtcp_session_setting_default(&rtcp_setting); rtcp_setting.name = stream->name.ptr; rtcp_setting.ssrc = info->ssrc; rtcp_setting.rtp_ts_base = pj_ntohl(stream->enc->rtp.out_hdr.ts); rtcp_setting.clock_rate = info->codec_info.clock_rate; rtcp_setting.samples_per_frame = 1; pjmedia_rtcp_init2(&stream->rtcp, &rtcp_setting); } /* Allocate outgoing RTCP buffer, should be enough to hold SR/RR, SDES, * BYE, and XR. */ stream->out_rtcp_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) + sizeof(pjmedia_rtcp_common) + (4 + (unsigned)stream->cname.slen) + 32; if (stream->out_rtcp_pkt_size > PJMEDIA_MAX_MTU) stream->out_rtcp_pkt_size = PJMEDIA_MAX_MTU; stream->out_rtcp_pkt = pj_pool_alloc(pool, stream->out_rtcp_pkt_size); /* Only attach transport when stream is ready. */ status = pjmedia_transport_attach(tp, stream, &info->rem_addr, &info->rem_rtcp, pj_sockaddr_get_len(&info->rem_addr), &on_rx_rtp, &on_rx_rtcp); if (status != PJ_SUCCESS) return status; stream->transport = tp; /* Send RTCP SDES */ if (!stream->rtcp_sdes_bye_disabled) { pjmedia_vid_stream_send_rtcp_sdes(stream); } #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /* NAT hole punching by sending KA packet via RTP transport. */ if (stream->use_ka) send_keep_alive_packet(stream); #endif #if TRACE_JB { char trace_name[PJ_MAXPATH]; pj_ssize_t len; pj_ansi_snprintf(trace_name, sizeof(trace_name), TRACE_JB_PATH_PREFIX "%s.csv", channel->port.info.name.ptr); status = pj_file_open(pool, trace_name, PJ_O_RDWR, &stream->trace_jb_fd); if (status != PJ_SUCCESS) { stream->trace_jb_fd = TRACE_JB_INVALID_FD; PJ_LOG(3,(THIS_FILE, "Failed creating RTP trace file '%s'", trace_name)); } else { stream->trace_jb_buf = (char*)pj_pool_alloc(pool, PJ_LOG_MAX_SIZE); /* Print column header */ len = pj_ansi_snprintf(stream->trace_jb_buf, PJ_LOG_MAX_SIZE, "Time, Operation, Size, Frame Count, " "Frame type, RTP Seq, RTP TS, RTP M, " "JB size, JB burst level, JB prefetch\n"); if (len < 1 || len >= PJ_LOG_MAX_SIZE) len = PJ_LOG_MAX_SIZE - 1; pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); pj_file_flush(stream->trace_jb_fd); } } #endif /* Save the stream info */ pj_memcpy(&stream->info, info, sizeof(*info)); stream->info.codec_param = pjmedia_vid_codec_param_clone( pool, info->codec_param); /* Success! */ *p_stream = stream; PJ_LOG(5,(THIS_FILE, "Video stream %s created", stream->name.ptr)); return PJ_SUCCESS; } /* * Destroy stream. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy( pjmedia_vid_stream *stream ) { PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); #if TRACE_RC { unsigned total_time; total_time = pj_elapsed_msec(&stream->tx_start, &stream->tx_end); PJ_LOG(5, (stream->name.ptr, "RC stat: pkt_cnt=%.2f/image, sleep=%.2fms/s, fps=%.2f", stream->rc_total_pkt*1.0/stream->rc_total_img, stream->rc_total_sleep*1000.0/total_time, stream->rc_total_img*1000.0/total_time)); } #endif /* Send RTCP BYE (also SDES) */ if (!stream->rtcp_sdes_bye_disabled) { send_rtcp(stream, PJ_TRUE, PJ_TRUE); } /* Detach from transport * MUST NOT hold stream mutex while detaching from transport, as * it may cause deadlock. See ticket #460 for the details. */ if (stream->transport) { pjmedia_transport_detach(stream->transport, stream); stream->transport = NULL; } /* This function may be called when stream is partly initialized. */ if (stream->jb_mutex) pj_mutex_lock(stream->jb_mutex); /* Free codec. */ if (stream->codec) { pjmedia_event_unsubscribe(NULL, &stream_event_cb, stream, stream->codec); pjmedia_vid_codec_close(stream->codec); pjmedia_vid_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec); stream->codec = NULL; } /* Free mutex */ if (stream->jb_mutex) { pj_mutex_destroy(stream->jb_mutex); stream->jb_mutex = NULL; } /* Destroy jitter buffer */ if (stream->jb) { pjmedia_jbuf_destroy(stream->jb); stream->jb = NULL; } #if TRACE_JB if (TRACE_JB_OPENED(stream)) { pj_file_close(stream->trace_jb_fd); stream->trace_jb_fd = TRACE_JB_INVALID_FD; } #endif if (stream->own_pool) { pj_pool_t *pool = stream->own_pool; stream->own_pool = NULL; pj_pool_release(pool); } return PJ_SUCCESS; } /* * Get the port interface. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_port(pjmedia_vid_stream *stream, pjmedia_dir dir, pjmedia_port **p_port ) { PJ_ASSERT_RETURN(dir==PJMEDIA_DIR_ENCODING || dir==PJMEDIA_DIR_DECODING, PJ_EINVAL); if (dir == PJMEDIA_DIR_ENCODING) *p_port = &stream->enc->port; else *p_port = &stream->dec->port; return PJ_SUCCESS; } /* * Get the transport object */ PJ_DEF(pjmedia_transport*) pjmedia_vid_stream_get_transport( pjmedia_vid_stream *st) { return st->transport; } /* * Get stream statistics. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat( const pjmedia_vid_stream *stream, pjmedia_rtcp_stat *stat) { PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL); pj_memcpy(stat, &stream->rtcp.stat, sizeof(pjmedia_rtcp_stat)); return PJ_SUCCESS; } /* * Reset the stream statistics in the middle of a stream session. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_reset_stat(pjmedia_vid_stream *stream) { PJ_ASSERT_RETURN(stream, PJ_EINVAL); pjmedia_rtcp_init_stat(&stream->rtcp.stat); return PJ_SUCCESS; } /* * Get jitter buffer state. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat_jbuf( const pjmedia_vid_stream *stream, pjmedia_jb_state *state) { PJ_ASSERT_RETURN(stream && state, PJ_EINVAL); return pjmedia_jbuf_get_state(stream->jb, state); } /* * Get the stream info. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_info( const pjmedia_vid_stream *stream, pjmedia_vid_stream_info *info) { PJ_ASSERT_RETURN(stream && info, PJ_EINVAL); pj_memcpy(info, &stream->info, sizeof(*info)); return PJ_SUCCESS; } /* * Start stream. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream) { PJ_ASSERT_RETURN(stream && stream->enc && stream->dec, PJ_EINVALIDOP); if (stream->enc && (stream->dir & PJMEDIA_DIR_ENCODING)) { stream->enc->paused = 0; //pjmedia_snd_stream_start(stream->enc->snd_stream); PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream started")); } else { PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused")); } if (stream->dec && (stream->dir & PJMEDIA_DIR_DECODING)) { stream->dec->paused = 0; //pjmedia_snd_stream_start(stream->dec->snd_stream); PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream started")); } else { PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused")); } return PJ_SUCCESS; } /* * Check status. */ PJ_DEF(pj_bool_t) pjmedia_vid_stream_is_running(pjmedia_vid_stream *stream, pjmedia_dir dir) { pj_bool_t is_running = PJ_TRUE; PJ_ASSERT_RETURN(stream, PJ_FALSE); if (dir & PJMEDIA_DIR_ENCODING) { is_running &= (stream->enc && !stream->enc->paused); } if (dir & PJMEDIA_DIR_DECODING) { is_running &= (stream->dec && !stream->dec->paused); } return is_running; } /* * Pause stream. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream, pjmedia_dir dir) { PJ_ASSERT_RETURN(stream, PJ_EINVAL); if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) { stream->enc->paused = 1; PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused")); } if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) { stream->dec->paused = 1; /* Also reset jitter buffer */ pj_mutex_lock( stream->jb_mutex ); pjmedia_jbuf_reset(stream->jb); pj_mutex_unlock( stream->jb_mutex ); PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused")); } return PJ_SUCCESS; } /* * Resume stream */ PJ_DEF(pj_status_t) pjmedia_vid_stream_resume(pjmedia_vid_stream *stream, pjmedia_dir dir) { PJ_ASSERT_RETURN(stream, PJ_EINVAL); if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) { stream->enc->paused = 0; PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream resumed")); } if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) { stream->dec->paused = 0; PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream resumed")); } return PJ_SUCCESS; } /* * Force stream to send video keyframe. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_send_keyframe( pjmedia_vid_stream *stream) { PJ_ASSERT_RETURN(stream, PJ_EINVAL); if (!pjmedia_vid_stream_is_running(stream, PJMEDIA_DIR_ENCODING)) return PJ_EINVALIDOP; stream->force_keyframe = PJ_TRUE; return PJ_SUCCESS; } /* * Send RTCP SDES. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_sdes( pjmedia_vid_stream *stream) { PJ_ASSERT_RETURN(stream, PJ_EINVAL); return send_rtcp(stream, PJ_TRUE, PJ_FALSE); } /* * Send RTCP BYE. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_bye( pjmedia_vid_stream *stream) { PJ_ASSERT_RETURN(stream, PJ_EINVAL); if (stream->enc && stream->transport) { return send_rtcp(stream, PJ_TRUE, PJ_TRUE); } return PJ_SUCCESS; } /* * Send RTCP PLI. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_pli( pjmedia_vid_stream *stream) { PJ_ASSERT_RETURN(stream, PJ_EINVAL); if (stream->enc && stream->transport) { void *sr_rr_pkt; pj_uint8_t *pkt; int len, max_len; pj_status_t status; pj_size_t pli_len; /* Build RTCP RR/SR packet */ pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len); pkt = (pj_uint8_t*) stream->out_rtcp_pkt; pj_memcpy(pkt, sr_rr_pkt, len); max_len = stream->out_rtcp_pkt_size; /* Build RTCP PLI packet */ pli_len = max_len - len; status = pjmedia_rtcp_build_rtcp_pli(&stream->rtcp, pkt+len, &pli_len); if (status != PJ_SUCCESS) { PJ_PERROR(4,(stream->name.ptr, status, "Error generating RTCP PLI")); } else { len += (int)pli_len; } /* Send! */ status = pjmedia_transport_send_rtcp(stream->transport, pkt, len); return status; } return PJ_SUCCESS; } /* * Initialize the video stream rate control with default settings. */ PJ_DEF(void) pjmedia_vid_stream_rc_config_default(pjmedia_vid_stream_rc_config *cfg) { pj_bzero(cfg, sizeof(*cfg)); cfg->method = PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING; } #endif /* PJMEDIA_HAS_VIDEO */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/vid_stream_info.c ================================================ /* $Id: vid_stream_info.c 4257 2012-09-17 03:11:44Z ming $ */ /* * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) static const pj_str_t ID_VIDEO = { "video", 5}; static const pj_str_t ID_IN = { "IN", 2 }; static const pj_str_t ID_IP4 = { "IP4", 3}; static const pj_str_t ID_IP6 = { "IP6", 3}; static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 }; //static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 }; static const pj_str_t ID_RTPMAP = { "rtpmap", 6 }; /* * Internal function for collecting codec info and param from the SDP media. */ static pj_status_t get_video_codec_info_param(pjmedia_vid_stream_info *si, pj_pool_t *pool, pjmedia_vid_codec_mgr *mgr, const pjmedia_sdp_media *local_m, const pjmedia_sdp_media *rem_m) { unsigned pt = 0; const pjmedia_vid_codec_info *p_info; pj_status_t status; pt = pj_strtoul(&local_m->desc.fmt[0]); /* Get payload type for receiving direction */ si->rx_pt = pt; /* Get codec info and payload type for transmitting direction. */ if (pt < 96) { /* For static payload types, get the codec info from codec manager. */ status = pjmedia_vid_codec_mgr_get_codec_info(mgr, pt, &p_info); if (status != PJ_SUCCESS) return status; si->codec_info = *p_info; /* Get payload type for transmitting direction. * For static payload type, pt's are symetric. */ si->tx_pt = pt; } else { const pjmedia_sdp_attr *attr; pjmedia_sdp_rtpmap *rtpmap; pjmedia_codec_id codec_id; pj_str_t codec_id_st; unsigned i; /* Determine payload type for outgoing channel, by finding * dynamic payload type in remote SDP that matches the answer. */ si->tx_pt = 0xFFFF; for (i=0; idesc.fmt_count; ++i) { if (pjmedia_sdp_neg_fmt_match(NULL, (pjmedia_sdp_media*)local_m, 0, (pjmedia_sdp_media*)rem_m, i, 0) == PJ_SUCCESS) { /* Found matched codec. */ si->tx_pt = pj_strtoul(&rem_m->desc.fmt[i]); break; } } if (si->tx_pt == 0xFFFF) return PJMEDIA_EMISSINGRTPMAP; /* For dynamic payload types, get codec name from the rtpmap */ attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, &local_m->desc.fmt[0]); if (attr == NULL) return PJMEDIA_EMISSINGRTPMAP; status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); if (status != PJ_SUCCESS) return status; /* Then get the codec info from the codec manager */ pj_ansi_snprintf(codec_id, sizeof(codec_id), "%.*s/", (int)rtpmap->enc_name.slen, rtpmap->enc_name.ptr); codec_id_st = pj_str(codec_id); i = 1; status = pjmedia_vid_codec_mgr_find_codecs_by_id(mgr, &codec_id_st, &i, &p_info, NULL); if (status != PJ_SUCCESS) return status; si->codec_info = *p_info; } /* Request for codec with the correct packing for streaming */ si->codec_info.packings = PJMEDIA_VID_PACKING_PACKETS; /* Now that we have codec info, get the codec param. */ si->codec_param = PJ_POOL_ALLOC_T(pool, pjmedia_vid_codec_param); status = pjmedia_vid_codec_mgr_get_default_param(mgr, &si->codec_info, si->codec_param); /* Adjust encoding bitrate, if higher than remote preference. The remote * bitrate preference is read from SDP "b=TIAS" line in media level. */ if ((si->dir & PJMEDIA_DIR_ENCODING) && rem_m->bandw_count) { unsigned i, bandw = 0; for (i = 0; i < rem_m->bandw_count; ++i) { const pj_str_t STR_BANDW_MODIFIER_TIAS = { "TIAS", 4 }; if (!pj_stricmp(&rem_m->bandw[i]->modifier, &STR_BANDW_MODIFIER_TIAS)) { bandw = rem_m->bandw[i]->value; break; } } if (bandw) { pjmedia_video_format_detail *enc_vfd; enc_vfd = pjmedia_format_get_video_format_detail( &si->codec_param->enc_fmt, PJ_TRUE); if (!enc_vfd->avg_bps || enc_vfd->avg_bps > bandw) enc_vfd->avg_bps = bandw * 3 / 4; if (!enc_vfd->max_bps || enc_vfd->max_bps > bandw) enc_vfd->max_bps = bandw; } } /* Get remote fmtp for our encoder. */ pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt, &si->codec_param->enc_fmtp); /* Get local fmtp for our decoder. */ pjmedia_stream_info_parse_fmtp(pool, local_m, si->rx_pt, &si->codec_param->dec_fmtp); /* When direction is NONE (it means SDP negotiation has failed) we don't * need to return a failure here, as returning failure will cause * the whole SDP to be rejected. See ticket #: * http:// * * Thanks Alain Totouom */ if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE) return status; return PJ_SUCCESS; } /* * Create stream info from SDP media line. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_info_from_sdp( pjmedia_vid_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote, unsigned stream_idx) { const pj_str_t STR_INACTIVE = { "inactive", 8 }; const pj_str_t STR_SENDONLY = { "sendonly", 8 }; const pj_str_t STR_RECVONLY = { "recvonly", 8 }; const pjmedia_sdp_attr *attr; const pjmedia_sdp_media *local_m; const pjmedia_sdp_media *rem_m; const pjmedia_sdp_conn *local_conn; const pjmedia_sdp_conn *rem_conn; int rem_af, local_af; pj_sockaddr local_addr; pj_status_t status; PJ_UNUSED_ARG(endpt); /* Validate arguments: */ PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); /* Keep SDP shortcuts */ local_m = local->media[stream_idx]; rem_m = remote->media[stream_idx]; local_conn = local_m->conn ? local_m->conn : local->conn; if (local_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; rem_conn = rem_m->conn ? rem_m->conn : remote->conn; if (rem_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; /* Media type must be video */ if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) != 0) return PJMEDIA_EINVALIMEDIATYPE; /* Reset: */ pj_bzero(si, sizeof(*si)); /* Media type: */ si->type = PJMEDIA_TYPE_VIDEO; /* Transport protocol */ /* At this point, transport type must be compatible, * the transport instance will do more validation later. */ status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, &local_m->desc.transport); if (status != PJ_SUCCESS) return PJMEDIA_SDPNEG_EINVANSTP; if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) { si->proto = PJMEDIA_TP_PROTO_RTP_AVP; } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) { si->proto = PJMEDIA_TP_PROTO_RTP_SAVP; } else { si->proto = PJMEDIA_TP_PROTO_UNKNOWN; return PJ_SUCCESS; } /* Check address family in remote SDP */ rem_af = pj_AF_UNSPEC(); if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { rem_af = pj_AF_INET(); } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { rem_af = pj_AF_INET6(); } } if (rem_af==pj_AF_UNSPEC()) { /* Unsupported address family */ return PJ_EAFNOTSUP; } /* Set remote address: */ status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, rem_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* Check address family of local info */ local_af = pj_AF_UNSPEC(); if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { local_af = pj_AF_INET(); } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { local_af = pj_AF_INET6(); } } if (local_af==pj_AF_UNSPEC()) { /* Unsupported address family */ return PJ_SUCCESS; } /* Set remote address: */ status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr, local_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* Local and remote address family must match */ if (local_af != rem_af) return PJ_EAFNOTSUP; /* Media direction: */ if (local_m->desc.port == 0 || pj_sockaddr_has_addr(&local_addr)==PJ_FALSE || pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) { /* Inactive stream. */ si->dir = PJMEDIA_DIR_NONE; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { /* Send only stream. */ si->dir = PJMEDIA_DIR_ENCODING; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { /* Recv only stream. */ si->dir = PJMEDIA_DIR_DECODING; } else { /* Send and receive stream. */ si->dir = PJMEDIA_DIR_ENCODING_DECODING; } /* No need to do anything else if stream is rejected */ if (local_m->desc.port == 0) { return PJ_SUCCESS; } /* If "rtcp" attribute is present in the SDP, set the RTCP address * from that attribute. Otherwise, calculate from RTP address. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "rtcp", NULL); if (attr) { pjmedia_sdp_rtcp_attr rtcp; status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); if (status == PJ_SUCCESS) { if (rtcp.addr.slen) { status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, (pj_uint16_t)rtcp.port); } else { pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, (pj_uint16_t)rtcp.port); pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), pj_sockaddr_get_addr(&si->rem_addr), pj_sockaddr_get_addr_len(&si->rem_addr)); } } } if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { int rtcp_port; pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); } /* Get codec info and param */ status = get_video_codec_info_param(si, pool, NULL, local_m, rem_m); /* Leave SSRC to random. */ si->ssrc = pj_rand(); /* Set default jitter buffer parameter. */ si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; return status; } #endif /* PJMEDIA_HAS_VIDEO */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/vid_tee.c ================================================ /* $Id: vid_tee.c 3773 2011-09-23 04:06:01Z nanang $ */ /* * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #define TEE_PORT_NAME "vid_tee" #define TEE_PORT_SIGN PJMEDIA_SIG_PORT_VID_TEE #define THIS_FILE "vid_tee.c" typedef struct vid_tee_dst_port { pjmedia_port *dst; unsigned option; } vid_tee_dst_port; typedef struct vid_tee_port { pjmedia_port base; pj_pool_t *pool; pj_pool_factory *pf; pj_pool_t *buf_pool; void *buf[2]; unsigned buf_cnt; pj_size_t buf_size; unsigned dst_port_maxcnt; unsigned dst_port_cnt; vid_tee_dst_port *dst_ports; pj_uint8_t *put_frm_flag; pj_mutex_t *lock; struct vid_tee_conv_t { pjmedia_converter *conv; pj_size_t conv_buf_size; } *tee_conv; } vid_tee_port; static pj_status_t tee_put_frame(pjmedia_port *port, pjmedia_frame *frame); static pj_status_t tee_get_frame(pjmedia_port *port, pjmedia_frame *frame); static pj_status_t tee_destroy(pjmedia_port *port); /* * Create a video tee port with the specified source media port. */ PJ_DEF(pj_status_t) pjmedia_vid_tee_create( pj_pool_t *pool, const pjmedia_format *fmt, unsigned max_dst_cnt, pjmedia_port **p_vid_tee) { vid_tee_port *tee; pj_str_t name_st; const pjmedia_video_format_info *vfi; pjmedia_video_apply_fmt_param vafp; pj_status_t status; PJ_ASSERT_RETURN(pool && fmt && p_vid_tee, PJ_EINVAL); PJ_ASSERT_RETURN(fmt->type == PJMEDIA_TYPE_VIDEO, PJ_EINVAL); /* Allocate video tee structure */ tee = PJ_POOL_ZALLOC_T(pool, vid_tee_port); tee->pf = pool->factory; tee->pool = pj_pool_create(tee->pf, "video tee", 500, 500, NULL); /* Create lock */ status = pj_mutex_create_simple(pool, "vid-tee-mutex", &tee->lock); if (status != PJ_SUCCESS) return status; /* Initialize video tee structure */ tee->dst_port_maxcnt = max_dst_cnt; tee->dst_ports = (vid_tee_dst_port*) pj_pool_calloc(pool, max_dst_cnt, sizeof(vid_tee_dst_port)); tee->tee_conv = (struct vid_tee_conv_t *) pj_pool_calloc(pool, max_dst_cnt, sizeof(struct vid_tee_conv_t)); tee->put_frm_flag = (pj_uint8_t*) pj_pool_calloc(pool, max_dst_cnt, sizeof(tee->put_frm_flag[0])); /* Initialize video tee buffer, its size is one frame */ vfi = pjmedia_get_video_format_info(NULL, fmt->id); if (vfi == NULL) { status = PJMEDIA_EBADFMT; goto on_error; } pj_bzero(&vafp, sizeof(vafp)); vafp.size = fmt->det.vid.size; status = vfi->apply_fmt(vfi, &vafp); if (status != PJ_SUCCESS) goto on_error; tee->buf_size = vafp.framebytes; /* Initialize video tee port */ status = pjmedia_port_info_init2(&tee->base.info, pj_strset2(&name_st, (char*)TEE_PORT_NAME), TEE_PORT_SIGN, PJMEDIA_DIR_ENCODING, fmt); if (status != PJ_SUCCESS) goto on_error; tee->base.get_frame = &tee_get_frame; tee->base.put_frame = &tee_put_frame; tee->base.on_destroy = &tee_destroy; /* Done */ *p_vid_tee = &tee->base; return PJ_SUCCESS; on_error: pj_mutex_destroy(tee->lock); tee->lock = NULL; return status; } static void realloc_buf(vid_tee_port *vid_tee, unsigned buf_cnt, pj_size_t buf_size) { unsigned i; if (buf_cnt > vid_tee->buf_cnt) vid_tee->buf_cnt = buf_cnt; if (buf_size > vid_tee->buf_size) { /* We need a larger buffer here. */ vid_tee->buf_size = buf_size; if (vid_tee->buf_pool) { pj_pool_release(vid_tee->buf_pool); vid_tee->buf_pool = NULL; } vid_tee->buf[0] = vid_tee->buf[1] = NULL; } if (!vid_tee->buf_pool) { vid_tee->buf_pool = pj_pool_create(vid_tee->pf, "video tee buffer", 1000, 1000, NULL); } for (i = 0; i < vid_tee->buf_cnt; i++) { if (!vid_tee->buf[i]) vid_tee->buf[i] = pj_pool_alloc(vid_tee->buf_pool, vid_tee->buf_size); } } /* * Add a destination media port to the video tee. */ PJ_DEF(pj_status_t) pjmedia_vid_tee_add_dst_port(pjmedia_port *vid_tee, unsigned option, pjmedia_port *port) { vid_tee_port *tee = (vid_tee_port*)vid_tee; pjmedia_video_format_detail *vfd; pj_status_t status; PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN, PJ_EINVAL); pj_mutex_lock(tee->lock); if (tee->dst_port_cnt >= tee->dst_port_maxcnt) { status = PJ_ETOOMANY; goto end; } if (vid_tee->info.fmt.id != port->info.fmt.id) { status = PJMEDIA_EBADFMT; goto end; } vfd = pjmedia_format_get_video_format_detail(&port->info.fmt, PJ_TRUE); if (vfd->size.w != vid_tee->info.fmt.det.vid.size.w || vfd->size.h != vid_tee->info.fmt.det.vid.size.h) { status = PJMEDIA_EBADFMT; goto end; } realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)? 1: 0, tee->buf_size); pj_bzero(&tee->tee_conv[tee->dst_port_cnt], sizeof(tee->tee_conv[0])); tee->dst_ports[tee->dst_port_cnt].dst = port; tee->dst_ports[tee->dst_port_cnt].option = option; ++tee->dst_port_cnt; status = PJ_SUCCESS; end: pj_mutex_unlock(tee->lock); return status; } /* * Add a destination media port to the video tee. Create a converter if * necessary. */ PJ_DEF(pj_status_t) pjmedia_vid_tee_add_dst_port2(pjmedia_port *vid_tee, unsigned option, pjmedia_port *port) { vid_tee_port *tee = (vid_tee_port*)vid_tee; pjmedia_video_format_detail *vfd; pj_status_t status; PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN, PJ_EINVAL); pj_mutex_lock(tee->lock); if (tee->dst_port_cnt >= tee->dst_port_maxcnt) { status = PJ_ETOOMANY; goto end; } pj_bzero(&tee->tee_conv[tee->dst_port_cnt], sizeof(tee->tee_conv[0])); /* Check if we need to create a converter. */ vfd = pjmedia_format_get_video_format_detail(&port->info.fmt, PJ_TRUE); if (vid_tee->info.fmt.id != port->info.fmt.id || vfd->size.w != vid_tee->info.fmt.det.vid.size.w || vfd->size.h != vid_tee->info.fmt.det.vid.size.h) { const pjmedia_video_format_info *vfi; pjmedia_video_apply_fmt_param vafp; pjmedia_conversion_param conv_param; vfi = pjmedia_get_video_format_info(NULL, port->info.fmt.id); if (vfi == NULL) { status = PJMEDIA_EBADFMT; goto end; } pj_bzero(&vafp, sizeof(vafp)); vafp.size = port->info.fmt.det.vid.size; status = vfi->apply_fmt(vfi, &vafp); if (status != PJ_SUCCESS) goto end; realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)? 2: 1, vafp.framebytes); pjmedia_format_copy(&conv_param.src, &vid_tee->info.fmt); pjmedia_format_copy(&conv_param.dst, &port->info.fmt); status = pjmedia_converter_create( NULL, tee->pool, &conv_param, &tee->tee_conv[tee->dst_port_cnt].conv); if (status != PJ_SUCCESS) goto end; tee->tee_conv[tee->dst_port_cnt].conv_buf_size = vafp.framebytes; } else { realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)? 1: 0, tee->buf_size); } tee->dst_ports[tee->dst_port_cnt].dst = port; tee->dst_ports[tee->dst_port_cnt].option = option; ++tee->dst_port_cnt; status = PJ_SUCCESS; end: pj_mutex_unlock(tee->lock); return status; } /* * Remove a destination media port from the video tee. */ PJ_DEF(pj_status_t) pjmedia_vid_tee_remove_dst_port(pjmedia_port *vid_tee, pjmedia_port *port) { vid_tee_port *tee = (vid_tee_port*)vid_tee; unsigned i; PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN, PJ_EINVAL); pj_mutex_lock(tee->lock); for (i = 0; i < tee->dst_port_cnt; ++i) { if (tee->dst_ports[i].dst == port) { if (tee->tee_conv[i].conv) pjmedia_converter_destroy(tee->tee_conv[i].conv); pj_array_erase(tee->dst_ports, sizeof(tee->dst_ports[0]), tee->dst_port_cnt, i); pj_array_erase(tee->tee_conv, sizeof(tee->tee_conv[0]), tee->dst_port_cnt, i); --tee->dst_port_cnt; pj_mutex_unlock(tee->lock); return PJ_SUCCESS; } } pj_mutex_unlock(tee->lock); return PJ_ENOTFOUND; } static pj_status_t tee_put_frame(pjmedia_port *port, pjmedia_frame *frame) { vid_tee_port *tee = (vid_tee_port*)port; unsigned i, j; const pj_uint8_t PUT_FRM_DONE = 1; if (pj_mutex_trylock(tee->lock) != PJ_SUCCESS) { /* we are busy adding / removing consumers */ return PJ_SUCCESS; } pj_bzero(tee->put_frm_flag, tee->dst_port_cnt * sizeof(tee->put_frm_flag[0])); for (i = 0; i < tee->dst_port_cnt; ++i) { pjmedia_frame frame_ = *frame; if (tee->put_frm_flag[i]) continue; if (tee->tee_conv[i].conv) { pj_status_t status; frame_.buf = tee->buf[0]; frame_.size = tee->tee_conv[i].conv_buf_size; status = pjmedia_converter_convert(tee->tee_conv[i].conv, frame, &frame_); if (status != PJ_SUCCESS) { PJ_LOG(3, (THIS_FILE, "Failed to convert frame for destination" " port %d (%.*s)", i, tee->dst_ports[i].dst->info.name.slen, tee->dst_ports[i].dst->info.name.ptr)); continue; } } /* Find other destination ports which has the same format so * we don't need to do the same conversion twice. */ for (j = i; j < tee->dst_port_cnt; ++j) { pjmedia_frame framep; if (tee->put_frm_flag[j] || (tee->dst_ports[j].dst->info.fmt.id != tee->dst_ports[i].dst->info.fmt.id) || (tee->dst_ports[j].dst->info.fmt.det.vid.size.w != tee->dst_ports[i].dst->info.fmt.det.vid.size.w) || (tee->dst_ports[j].dst->info.fmt.det.vid.size.h != tee->dst_ports[i].dst->info.fmt.det.vid.size.h)) { continue; } framep = frame_; /* For dst_ports that do in-place processing, we need to duplicate * the data source first. */ if (tee->dst_ports[j].option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC) { PJ_ASSERT_RETURN(tee->buf_size <= frame_.size, PJ_ETOOBIG); framep.buf = tee->buf[tee->buf_cnt-1]; framep.size = frame_.size; pj_memcpy(framep.buf, frame_.buf, frame_.size); } /* Deliver the data */ pjmedia_port_put_frame(tee->dst_ports[j].dst, &framep); tee->put_frm_flag[j] = PUT_FRM_DONE; if (!tee->tee_conv[i].conv) break; } } pj_mutex_unlock(tee->lock); return PJ_SUCCESS; } static pj_status_t tee_get_frame(pjmedia_port *port, pjmedia_frame *frame) { PJ_UNUSED_ARG(port); PJ_UNUSED_ARG(frame); pj_assert(!"Bug! Tee port get_frame() shouldn't be called."); return PJ_EBUG; } static pj_status_t tee_destroy(pjmedia_port *port) { vid_tee_port *tee = (vid_tee_port*)port; PJ_ASSERT_RETURN(port && port->info.signature==TEE_PORT_SIGN, PJ_EINVAL); if (tee->lock) { pj_mutex_destroy(tee->lock); tee->lock = NULL; } pj_pool_release(tee->pool); if (tee->buf_pool) pj_pool_release(tee->buf_pool); pj_bzero(tee, sizeof(*tee)); return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_VIDEO */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/wav_player.c ================================================ /* $Id: wav_player.c 4122 2012-05-14 11:04:46Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "wav_player.c" #define SIGNATURE PJMEDIA_SIG_PORT_WAV_PLAYER #define BITS_PER_SAMPLE 16 #if 1 # define TRACE_(x) PJ_LOG(4,x) #else # define TRACE_(x) #endif #if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 static void samples_to_host(pj_int16_t *samples, unsigned count) { unsigned i; for (i=0; ibase.info, &name, SIGNATURE, 8000, 1, 16, 80); port->base.get_frame = &file_get_frame; port->base.on_destroy = &file_on_destroy; return port; } /* * Fill buffer. */ static pj_status_t fill_buffer(struct file_reader_port *fport) { pj_uint32_t size_left = fport->bufsize; unsigned size_to_read; pj_ssize_t size; pj_status_t status; fport->eofpos = NULL; while (size_left > 0) { /* Calculate how many bytes to read in this run. */ size = size_to_read = size_left; status = pj_file_read(fport->fd, &fport->buf[fport->bufsize-size_left], &size); if (status != PJ_SUCCESS) return status; if (size < 0) { /* Should return more appropriate error code here.. */ return PJ_ECANCELLED; } if (size > (pj_ssize_t)fport->data_left) { /* We passed the end of the data chunk, * only count the portion read from the data chunk. */ size = (pj_ssize_t)fport->data_left; } size_left -= (pj_uint32_t)size; fport->data_left -= (pj_uint32_t)size; fport->fpos += size; /* If size is less than size_to_read, it indicates that we've * encountered EOF. Rewind the file. */ if (size < (pj_ssize_t)size_to_read) { fport->eof = PJ_TRUE; fport->eofpos = fport->buf + fport->bufsize - size_left; if (fport->options & PJMEDIA_FILE_NO_LOOP) { /* Zero remaining buffer */ if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_PCM) { pj_bzero(fport->eofpos, size_left); } else if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW) { int val = pjmedia_linear2ulaw(0); pj_memset(fport->eofpos, val, size_left); } else if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ALAW) { int val = pjmedia_linear2alaw(0); pj_memset(fport->eofpos, val, size_left); } size_left = 0; } /* Rewind file */ fport->fpos = fport->start_data; pj_file_setpos( fport->fd, fport->fpos, PJ_SEEK_SET); fport->data_left = fport->data_len; } } /* Convert samples to host rep */ samples_to_host((pj_int16_t*)fport->buf, fport->bufsize/fport->bytes_per_sample); return PJ_SUCCESS; } /* * Create WAVE player port. */ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, const char *filename, unsigned ptime, unsigned options, pj_ssize_t buff_size, pjmedia_port **p_port ) { pjmedia_wave_hdr wave_hdr; pj_ssize_t size_to_read, size_read; struct file_reader_port *fport; pjmedia_audio_format_detail *ad; pj_off_t pos; pj_str_t name; unsigned samples_per_frame; pj_status_t status = PJ_SUCCESS; /* Check arguments. */ PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL); /* Check the file really exists. */ if (!pj_file_exists(filename)) { return PJ_ENOTFOUND; } /* Normalize ptime */ if (ptime == 0) ptime = 20; /* Normalize buff_size */ if (buff_size < 1) buff_size = PJMEDIA_FILE_PORT_BUFSIZE; /* Create fport instance. */ fport = create_file_port(pool); if (!fport) { return PJ_ENOMEM; } /* Get the file size. */ fport->fsize = pj_file_size(filename); /* Size must be more than WAVE header size */ if (fport->fsize <= sizeof(pjmedia_wave_hdr)) { return PJMEDIA_ENOTVALIDWAVE; } /* Open file. */ status = pj_file_open( pool, filename, PJ_O_RDONLY, &fport->fd); if (status != PJ_SUCCESS) return status; /* Read the file header plus fmt header only. */ size_read = size_to_read = sizeof(wave_hdr) - 8; status = pj_file_read( fport->fd, &wave_hdr, &size_read); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } if (size_read != size_to_read) { pj_file_close(fport->fd); return PJMEDIA_ENOTVALIDWAVE; } /* Normalize WAVE header fields values from little-endian to host * byte order. */ pjmedia_wave_hdr_file_to_host(&wave_hdr); /* Validate WAVE file. */ if (wave_hdr.riff_hdr.riff != PJMEDIA_RIFF_TAG || wave_hdr.riff_hdr.wave != PJMEDIA_WAVE_TAG || wave_hdr.fmt_hdr.fmt != PJMEDIA_FMT_TAG) { pj_file_close(fport->fd); TRACE_((THIS_FILE, "actual value|expected riff=%x|%x, wave=%x|%x fmt=%x|%x", wave_hdr.riff_hdr.riff, PJMEDIA_RIFF_TAG, wave_hdr.riff_hdr.wave, PJMEDIA_WAVE_TAG, wave_hdr.fmt_hdr.fmt, PJMEDIA_FMT_TAG)); return PJMEDIA_ENOTVALIDWAVE; } /* Validate format and its attributes (i.e: bits per sample, block align) */ switch (wave_hdr.fmt_hdr.fmt_tag) { case PJMEDIA_WAVE_FMT_TAG_PCM: if (wave_hdr.fmt_hdr.bits_per_sample != 16 || wave_hdr.fmt_hdr.block_align != 2 * wave_hdr.fmt_hdr.nchan) status = PJMEDIA_EWAVEUNSUPP; break; case PJMEDIA_WAVE_FMT_TAG_ALAW: case PJMEDIA_WAVE_FMT_TAG_ULAW: if (wave_hdr.fmt_hdr.bits_per_sample != 8 || wave_hdr.fmt_hdr.block_align != wave_hdr.fmt_hdr.nchan) status = PJMEDIA_ENOTVALIDWAVE; break; default: status = PJMEDIA_EWAVEUNSUPP; break; } if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } fport->fmt_tag = (pjmedia_wave_fmt_tag)wave_hdr.fmt_hdr.fmt_tag; fport->bytes_per_sample = (pj_uint16_t) (wave_hdr.fmt_hdr.bits_per_sample / 8); /* If length of fmt_header is greater than 16, skip the remaining * fmt header data. */ if (wave_hdr.fmt_hdr.len > 16) { size_to_read = wave_hdr.fmt_hdr.len - 16; status = pj_file_setpos(fport->fd, size_to_read, PJ_SEEK_CUR); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } } /* Repeat reading the WAVE file until we have 'data' chunk */ for (;;) { pjmedia_wave_subchunk subchunk; size_read = 8; status = pj_file_read(fport->fd, &subchunk, &size_read); if (status != PJ_SUCCESS || size_read != 8) { pj_file_close(fport->fd); return PJMEDIA_EWAVETOOSHORT; } /* Normalize endianness */ PJMEDIA_WAVE_NORMALIZE_SUBCHUNK(&subchunk); /* Break if this is "data" chunk */ if (subchunk.id == PJMEDIA_DATA_TAG) { wave_hdr.data_hdr.data = PJMEDIA_DATA_TAG; wave_hdr.data_hdr.len = subchunk.len; break; } /* Otherwise skip the chunk contents */ size_to_read = subchunk.len; status = pj_file_setpos(fport->fd, size_to_read, PJ_SEEK_CUR); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } } /* Current file position now points to start of data */ status = pj_file_getpos(fport->fd, &pos); fport->start_data = (unsigned)pos; fport->data_len = wave_hdr.data_hdr.len; fport->data_left = wave_hdr.data_hdr.len; /* Validate length. */ if (wave_hdr.data_hdr.len > fport->fsize - fport->start_data) { pj_file_close(fport->fd); return PJMEDIA_EWAVEUNSUPP; } if (wave_hdr.data_hdr.len < ptime * wave_hdr.fmt_hdr.sample_rate * wave_hdr.fmt_hdr.nchan / 1000) { pj_file_close(fport->fd); return PJMEDIA_EWAVETOOSHORT; } /* It seems like we have a valid WAVE file. */ /* Initialize */ fport->options = options; /* Update port info. */ ad = pjmedia_format_get_audio_format_detail(&fport->base.info.fmt, 1); pj_strdup2(pool, &name, filename); samples_per_frame = ptime * wave_hdr.fmt_hdr.sample_rate * wave_hdr.fmt_hdr.nchan / 1000; pjmedia_port_info_init(&fport->base.info, &name, SIGNATURE, wave_hdr.fmt_hdr.sample_rate, wave_hdr.fmt_hdr.nchan, BITS_PER_SAMPLE, samples_per_frame); /* If file is shorter than buffer size, adjust buffer size to file * size. Otherwise EOF callback will be called multiple times when * fill_buffer() is called. */ if (wave_hdr.data_hdr.len < (unsigned)buff_size) buff_size = wave_hdr.data_hdr.len; /* Create file buffer. */ fport->bufsize = (pj_uint32_t)buff_size; /* samples_per_frame must be smaller than bufsize (because get_frame() * doesn't handle this case). */ if (samples_per_frame * fport->bytes_per_sample >= fport->bufsize) { pj_file_close(fport->fd); return PJ_EINVAL; } /* Create buffer. */ fport->buf = (char*) pj_pool_alloc(pool, fport->bufsize); if (!fport->buf) { pj_file_close(fport->fd); return PJ_ENOMEM; } fport->readpos = fport->buf; /* Set initial position of the file. */ fport->fpos = fport->start_data; /* Fill up the buffer. */ status = fill_buffer(fport); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } /* Done. */ *p_port = &fport->base; PJ_LOG(4,(THIS_FILE, "File player '%.*s' created: samp.rate=%d, ch=%d, bufsize=%uKB, " "filesize=%luKB", (int)fport->base.info.name.slen, fport->base.info.name.ptr, ad->clock_rate, ad->channel_count, fport->bufsize / 1000, (unsigned long)(fport->fsize / 1000))); return PJ_SUCCESS; } /* * Get additional info about the file player. */ PJ_DEF(pj_status_t) pjmedia_wav_player_get_info( pjmedia_port *port, pjmedia_wav_player_info *info) { struct file_reader_port *fport; PJ_ASSERT_RETURN(port && info, PJ_EINVAL); pj_bzero(info, sizeof(*info)); /* Check that this is really a player port */ PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP); fport = (struct file_reader_port*) port; if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_PCM) { info->fmt_id = PJMEDIA_FORMAT_PCM; info->payload_bits_per_sample = 16; } else if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW) { info->fmt_id = PJMEDIA_FORMAT_ULAW; info->payload_bits_per_sample = 8; } else if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ALAW) { info->fmt_id = PJMEDIA_FORMAT_ALAW; info->payload_bits_per_sample = 8; } else { pj_assert(!"Unsupported format"); return PJ_ENOTSUP; } info->size_bytes = pjmedia_wav_player_get_len(port); info->size_samples = info->size_bytes / (info->payload_bits_per_sample / 8); return PJ_SUCCESS; } /* * Get the data length, in bytes. */ PJ_DEF(pj_ssize_t) pjmedia_wav_player_get_len(pjmedia_port *port) { struct file_reader_port *fport; pj_ssize_t size; /* Sanity check */ PJ_ASSERT_RETURN(port, -PJ_EINVAL); /* Check that this is really a player port */ PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, -PJ_EINVALIDOP); fport = (struct file_reader_port*) port; size = (pj_ssize_t) fport->fsize; return size - fport->start_data; } /* * Set position. */ PJ_DEF(pj_status_t) pjmedia_wav_player_port_set_pos(pjmedia_port *port, pj_uint32_t bytes ) { struct file_reader_port *fport; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(port, PJ_EINVAL); /* Check that this is really a player port */ PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP); fport = (struct file_reader_port*) port; /* Check that this offset does not pass the audio-data (in case of * extra chunk after audio data chunk */ PJ_ASSERT_RETURN(bytes < fport->data_len, PJ_EINVAL); fport->fpos = fport->start_data + bytes; fport->data_left = fport->data_len - bytes; pj_file_setpos( fport->fd, fport->fpos, PJ_SEEK_SET); fport->eof = PJ_FALSE; status = fill_buffer(fport); if (status != PJ_SUCCESS) return status; fport->readpos = fport->buf; return PJ_SUCCESS; } /* * Get the file play position of WAV player (in bytes). */ PJ_DEF(pj_ssize_t) pjmedia_wav_player_port_get_pos( pjmedia_port *port ) { struct file_reader_port *fport; pj_size_t payload_pos; /* Sanity check */ PJ_ASSERT_RETURN(port, -PJ_EINVAL); /* Check that this is really a player port */ PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, -PJ_EINVALIDOP); fport = (struct file_reader_port*) port; payload_pos = (pj_size_t)(fport->fpos - fport->start_data); if (payload_pos >= fport->bufsize) return payload_pos - fport->bufsize + (fport->readpos - fport->buf); else return (fport->readpos - fport->buf) % payload_pos; } /* * Register a callback to be called when the file reading has reached the * end of file. */ PJ_DEF(pj_status_t) pjmedia_wav_player_set_eof_cb( pjmedia_port *port, void *user_data, pj_status_t (*cb)(pjmedia_port *port, void *usr_data)) { struct file_reader_port *fport; /* Sanity check */ PJ_ASSERT_RETURN(port, -PJ_EINVAL); /* Check that this is really a player port */ PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, -PJ_EINVALIDOP); fport = (struct file_reader_port*) port; fport->base.port_data.pdata = user_data; fport->cb = cb; return PJ_SUCCESS; } /* * Get frame from file. */ static pj_status_t file_get_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct file_reader_port *fport = (struct file_reader_port*)this_port; pj_size_t frame_size; pj_status_t status = PJ_SUCCESS; pj_assert(fport->base.info.signature == SIGNATURE); pj_assert(frame->size <= fport->bufsize); /* EOF is set and readpos already passed the eofpos */ if (fport->eof && fport->readpos >= fport->eofpos) { PJ_LOG(5,(THIS_FILE, "File port %.*s EOF", (int)fport->base.info.name.slen, fport->base.info.name.ptr)); /* Call callback, if any */ if (fport->cb) status = (*fport->cb)(this_port, fport->base.port_data.pdata); /* If callback returns non PJ_SUCCESS or 'no loop' is specified, * return immediately (and don't try to access player port since * it might have been destroyed by the callback). */ if ((status != PJ_SUCCESS) || (fport->options & PJMEDIA_FILE_NO_LOOP)) { frame->type = PJMEDIA_FRAME_TYPE_NONE; frame->size = 0; return PJ_EEOF; } PJ_LOG(5,(THIS_FILE, "File port %.*s rewinding..", (int)fport->base.info.name.slen, fport->base.info.name.ptr)); fport->eof = PJ_FALSE; } //pj_assert(frame->size == fport->base.info.bytes_per_frame); if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_PCM) { frame_size = frame->size; //frame->size = frame_size; } else { /* Must be ULAW or ALAW */ pj_assert(fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW || fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ALAW); frame_size = frame->size >> 1; frame->size = frame_size << 1; } /* Copy frame from buffer. */ frame->type = PJMEDIA_FRAME_TYPE_AUDIO; frame->timestamp.u64 = 0; if ((fport->readpos + frame_size) <= (fport->buf + fport->bufsize)) { /* Read contiguous buffer. */ pj_memcpy(frame->buf, fport->readpos, frame_size); /* Fill up the buffer if all has been read. */ fport->readpos += frame_size; if (fport->readpos == fport->buf + fport->bufsize) { fport->readpos = fport->buf; status = fill_buffer(fport); if (status != PJ_SUCCESS) { frame->type = PJMEDIA_FRAME_TYPE_NONE; frame->size = 0; fport->readpos = fport->buf + fport->bufsize; return status; } } } else { unsigned endread; /* Split read. * First stage: read until end of buffer. */ endread = (unsigned)((fport->buf+fport->bufsize) - fport->readpos); pj_memcpy(frame->buf, fport->readpos, endread); /* End Of Buffer and EOF and NO LOOP */ if (fport->eof && (fport->options & PJMEDIA_FILE_NO_LOOP)) { fport->readpos += endread; if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_PCM) { pj_bzero((char*)frame->buf + endread, frame_size - endread); } else if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW) { int val = pjmedia_linear2ulaw(0); pj_memset((char*)frame->buf + endread, val, frame_size - endread); } else if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ALAW) { int val = pjmedia_linear2alaw(0); pj_memset((char*)frame->buf + endread, val, frame_size - endread); } return PJ_SUCCESS; } /* Second stage: fill up buffer, and read from the start of buffer. */ status = fill_buffer(fport); if (status != PJ_SUCCESS) { frame->type = PJMEDIA_FRAME_TYPE_NONE; frame->size = 0; fport->readpos = fport->buf + fport->bufsize; return status; } pj_memcpy(((char*)frame->buf)+endread, fport->buf, frame_size-endread); fport->readpos = fport->buf + (frame_size - endread); } if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW || fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ALAW) { unsigned i; pj_uint16_t *dst; pj_uint8_t *src; dst = (pj_uint16_t*)frame->buf + frame_size - 1; src = (pj_uint8_t*)frame->buf + frame_size - 1; if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW) { for (i = 0; i < frame_size; ++i) { *dst-- = (pj_uint16_t) pjmedia_ulaw2linear(*src--); } } else { for (i = 0; i < frame_size; ++i) { *dst-- = (pj_uint16_t) pjmedia_alaw2linear(*src--); } } } return PJ_SUCCESS; } /* * Destroy port. */ static pj_status_t file_on_destroy(pjmedia_port *this_port) { struct file_reader_port *fport = (struct file_reader_port*) this_port; pj_assert(this_port->info.signature == SIGNATURE); pj_file_close(fport->fd); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/wav_playlist.c ================================================ /* $Id: wav_playlist.c 3917 2011-12-20 10:01:35Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * Original author: * David Clark * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #define THIS_FILE "wav_playlist.c" #define SIGNATURE PJMEDIA_SIG_PORT_WAV_PLAYLIST #define BYTES_PER_SAMPLE 2 #if 1 # define TRACE_(x) PJ_LOG(4,x) #else # define TRACE_(x) #endif #if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 static void samples_to_host(pj_int16_t *samples, unsigned count) { unsigned i; for (i=0; ibase.info, name, SIGNATURE, 8000, 1, 16, 80); port->base.get_frame = &file_list_get_frame; port->base.on_destroy = &file_list_on_destroy; return port; } /* * Fill buffer for file_list operations. */ static pj_status_t file_fill_buffer(struct playlist_port *fport) { pj_uint32_t size_left = fport->bufsize; pj_uint32_t size_to_read; pj_ssize_t size; pj_status_t status; int current_file = fport->current_file; /* Can't read file if EOF and loop flag is disabled */ if (fport->eof) return PJ_EEOF; while (size_left > 0) { /* Calculate how many bytes to read in this run. */ size = size_to_read = size_left; status = pj_file_read(fport->fd_list[current_file], &fport->buf[fport->bufsize-size_left], &size); if (status != PJ_SUCCESS) return status; if (size < 0) { /* Should return more appropriate error code here.. */ return PJ_ECANCELLED; } size_left -= (pj_uint32_t)size; fport->fpos_list[current_file] += size; /* If size is less than size_to_read, it indicates that we've * encountered EOF. Rewind the file. */ if (size < (pj_ssize_t)size_to_read) { /* Rewind the file for the next iteration */ fport->fpos_list[current_file] = fport->start_data_list[current_file]; pj_file_setpos(fport->fd_list[current_file], fport->fpos_list[current_file], PJ_SEEK_SET); /* Move to next file */ current_file++; fport->current_file = current_file; if (fport->current_file == fport->max_file) { /* Clear the remaining part of the buffer first, to prevent * old samples from being played. If the playback restarts, * this will be overwritten by new reading. */ if (size_left > 0) { pj_bzero(&fport->buf[fport->bufsize-size_left], size_left); } /* All files have been played. Call callback, if any. */ if (fport->cb) { PJ_LOG(5,(THIS_FILE, "File port %.*s EOF, calling callback", (int)fport->base.info.name.slen, fport->base.info.name.ptr)); fport->eof = PJ_TRUE; status = (*fport->cb)(&fport->base, fport->base.port_data.pdata); if (status != PJ_SUCCESS) { /* This will crash if file port is destroyed in the * callback, that's why we set the eof flag before * calling the callback: fport->eof = PJ_TRUE; */ return status; } fport->eof = PJ_FALSE; } if (fport->options & PJMEDIA_FILE_NO_LOOP) { PJ_LOG(5,(THIS_FILE, "File port %.*s EOF, stopping..", (int)fport->base.info.name.slen, fport->base.info.name.ptr)); fport->eof = PJ_TRUE; return PJ_EEOF; } else { PJ_LOG(5,(THIS_FILE, "File port %.*s EOF, rewinding..", (int)fport->base.info.name.slen, fport->base.info.name.ptr)); /* start with first file again. */ fport->current_file = current_file = 0; fport->fpos_list[0] = fport->start_data_list[0]; pj_file_setpos(fport->fd_list[0], fport->fpos_list[0], PJ_SEEK_SET); } } /* if current_file == max_file */ } /* size < size_to_read */ } /* while () */ /* Convert samples to host rep */ samples_to_host((pj_int16_t*)fport->buf, fport->bufsize/BYTES_PER_SAMPLE); return PJ_SUCCESS; } /* * Create wave list player. */ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool, const pj_str_t *port_label, const pj_str_t file_list[], int file_count, unsigned ptime, unsigned options, pj_ssize_t buff_size, pjmedia_port **p_port) { struct playlist_port *fport; pjmedia_audio_format_detail *afd; pj_off_t pos; pj_status_t status; int index; pj_bool_t has_wave_info = PJ_FALSE; pj_str_t tmp_port_label; char filename[PJ_MAXPATH]; /* filename for open operations. */ /* Check arguments. */ PJ_ASSERT_RETURN(pool && file_list && file_count && p_port, PJ_EINVAL); /* Normalize port_label */ if (port_label == NULL || port_label->slen == 0) { tmp_port_label = pj_str("WAV playlist"); port_label = &tmp_port_label; } /* Be sure all files exist */ for (index=0; indexbase.info.fmt, 1); /* start with the first file. */ fport->current_file = 0; fport->max_file = file_count; /* Create file descriptor list */ fport->fd_list = (pj_oshandle_t*) pj_pool_zalloc(pool, sizeof(pj_oshandle_t)*file_count); if (!fport->fd_list) { return PJ_ENOMEM; } /* Create file size list */ fport->fsize_list = (pj_off_t*) pj_pool_alloc(pool, sizeof(pj_off_t)*file_count); if (!fport->fsize_list) { return PJ_ENOMEM; } /* Create start of WAVE data list */ fport->start_data_list = (unsigned*) pj_pool_alloc(pool, sizeof(unsigned)*file_count); if (!fport->start_data_list) { return PJ_ENOMEM; } /* Create file position list */ fport->fpos_list = (pj_off_t*) pj_pool_alloc(pool, sizeof(pj_off_t)*file_count); if (!fport->fpos_list) { return PJ_ENOMEM; } /* Create file buffer once for this operation. */ if (buff_size < 1) buff_size = PJMEDIA_FILE_PORT_BUFSIZE; fport->bufsize = (pj_uint32_t)buff_size; /* Create buffer. */ fport->buf = (char*) pj_pool_alloc(pool, fport->bufsize); if (!fport->buf) { return PJ_ENOMEM; } /* Initialize port */ fport->options = options; fport->readpos = fport->buf; /* ok run this for all files to be sure all are good for playback. */ for (index=file_count-1; index>=0; index--) { pjmedia_wave_hdr wavehdr; pj_ssize_t size_to_read, size_read; /* we end with the last one so we are good to go if still in function*/ pj_memcpy(filename, file_list[index].ptr, file_list[index].slen); filename[file_list[index].slen] = '\0'; /* Get the file size. */ fport->current_file = index; fport->fsize_list[index] = pj_file_size(filename); /* Size must be more than WAVE header size */ if (fport->fsize_list[index] <= sizeof(pjmedia_wave_hdr)) { status = PJMEDIA_ENOTVALIDWAVE; goto on_error; } /* Open file. */ status = pj_file_open( pool, filename, PJ_O_RDONLY, &fport->fd_list[index]); if (status != PJ_SUCCESS) goto on_error; /* Read the file header plus fmt header only. */ size_read = size_to_read = sizeof(wavehdr) - 8; status = pj_file_read( fport->fd_list[index], &wavehdr, &size_read); if (status != PJ_SUCCESS) { goto on_error; } if (size_read != size_to_read) { status = PJMEDIA_ENOTVALIDWAVE; goto on_error; } /* Normalize WAVE header fields values from little-endian to host * byte order. */ pjmedia_wave_hdr_file_to_host(&wavehdr); /* Validate WAVE file. */ if (wavehdr.riff_hdr.riff != PJMEDIA_RIFF_TAG || wavehdr.riff_hdr.wave != PJMEDIA_WAVE_TAG || wavehdr.fmt_hdr.fmt != PJMEDIA_FMT_TAG) { TRACE_((THIS_FILE, "actual value|expected riff=%x|%x, wave=%x|%x fmt=%x|%x", wavehdr.riff_hdr.riff, PJMEDIA_RIFF_TAG, wavehdr.riff_hdr.wave, PJMEDIA_WAVE_TAG, wavehdr.fmt_hdr.fmt, PJMEDIA_FMT_TAG)); status = PJMEDIA_ENOTVALIDWAVE; goto on_error; } /* Must be PCM with 16bits per sample */ if (wavehdr.fmt_hdr.fmt_tag != 1 || wavehdr.fmt_hdr.bits_per_sample != 16) { status = PJMEDIA_EWAVEUNSUPP; goto on_error; } /* Block align must be 2*nchannels */ if (wavehdr.fmt_hdr.block_align != wavehdr.fmt_hdr.nchan * BYTES_PER_SAMPLE) { status = PJMEDIA_EWAVEUNSUPP; goto on_error; } /* If length of fmt_header is greater than 16, skip the remaining * fmt header data. */ if (wavehdr.fmt_hdr.len > 16) { size_to_read = wavehdr.fmt_hdr.len - 16; status = pj_file_setpos(fport->fd_list[index], size_to_read, PJ_SEEK_CUR); if (status != PJ_SUCCESS) { goto on_error; } } /* Repeat reading the WAVE file until we have 'data' chunk */ for (;;) { pjmedia_wave_subchunk subchunk; size_read = 8; status = pj_file_read(fport->fd_list[index], &subchunk, &size_read); if (status != PJ_SUCCESS || size_read != 8) { status = PJMEDIA_EWAVETOOSHORT; goto on_error; } /* Normalize endianness */ PJMEDIA_WAVE_NORMALIZE_SUBCHUNK(&subchunk); /* Break if this is "data" chunk */ if (subchunk.id == PJMEDIA_DATA_TAG) { wavehdr.data_hdr.data = PJMEDIA_DATA_TAG; wavehdr.data_hdr.len = subchunk.len; break; } /* Otherwise skip the chunk contents */ size_to_read = subchunk.len; status = pj_file_setpos(fport->fd_list[index], size_to_read, PJ_SEEK_CUR); if (status != PJ_SUCCESS) { goto on_error; } } /* Current file position now points to start of data */ status = pj_file_getpos(fport->fd_list[index], &pos); fport->start_data_list[index] = (unsigned)pos; /* Validate length. */ if (wavehdr.data_hdr.len != fport->fsize_list[index] - fport->start_data_list[index]) { status = PJMEDIA_EWAVEUNSUPP; goto on_error; } if (wavehdr.data_hdr.len < 400) { status = PJMEDIA_EWAVETOOSHORT; goto on_error; } /* It seems like we have a valid WAVE file. */ /* Update port info if we don't have one, otherwise check * that the WAV file has the same attributes as previous files. */ if (!has_wave_info) { afd->channel_count = wavehdr.fmt_hdr.nchan; afd->clock_rate = wavehdr.fmt_hdr.sample_rate; afd->bits_per_sample = wavehdr.fmt_hdr.bits_per_sample; afd->frame_time_usec = ptime * 1000; afd->avg_bps = afd->max_bps = afd->clock_rate * afd->channel_count * afd->bits_per_sample; has_wave_info = PJ_TRUE; } else { /* Check that this file has the same characteristics as the other * files. */ if (wavehdr.fmt_hdr.nchan != afd->channel_count || wavehdr.fmt_hdr.sample_rate != afd->clock_rate || wavehdr.fmt_hdr.bits_per_sample != afd->bits_per_sample) { /* This file has different characteristics than the other * files. */ PJ_LOG(4,(THIS_FILE, "WAV playlist error: file '%s' has differrent number" " of channels, sample rate, or bits per sample", filename)); status = PJMEDIA_EWAVEUNSUPP; goto on_error; } } /* Set initial position of the file. */ fport->fpos_list[index] = fport->start_data_list[index]; } /* Fill up the buffer. */ status = file_fill_buffer(fport); if (status != PJ_SUCCESS) { goto on_error; } /* Done. */ *p_port = &fport->base; PJ_LOG(4,(THIS_FILE, "WAV playlist '%.*s' created: samp.rate=%d, ch=%d, bufsize=%uKB", (int)port_label->slen, port_label->ptr, afd->clock_rate, afd->channel_count, fport->bufsize / 1000)); return PJ_SUCCESS; on_error: for (index=0; indexfd_list[index] != 0) pj_file_close(fport->fd_list[index]); } return status; } /* * Register a callback to be called when the file reading has reached the * end of the last file. */ PJ_DEF(pj_status_t) pjmedia_wav_playlist_set_eof_cb(pjmedia_port *port, void *user_data, pj_status_t (*cb)(pjmedia_port *port, void *usr_data)) { struct playlist_port *fport; /* Sanity check */ PJ_ASSERT_RETURN(port, PJ_EINVAL); /* Check that this is really a playlist port */ PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP); fport = (struct playlist_port*) port; fport->base.port_data.pdata = user_data; fport->cb = cb; return PJ_SUCCESS; } /* * Get frame from file for file_list operation */ static pj_status_t file_list_get_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct playlist_port *fport = (struct playlist_port*)this_port; pj_size_t frame_size; pj_status_t status; pj_assert(fport->base.info.signature == SIGNATURE); //frame_size = fport->base.info.bytes_per_frame; //pj_assert(frame->size == frame_size); frame_size = frame->size; /* Copy frame from buffer. */ frame->type = PJMEDIA_FRAME_TYPE_AUDIO; frame->size = frame_size; frame->timestamp.u64 = 0; if (fport->readpos + frame_size <= fport->buf + fport->bufsize) { /* Read contiguous buffer. */ pj_memcpy(frame->buf, fport->readpos, frame_size); /* Fill up the buffer if all has been read. */ fport->readpos += frame_size; if (fport->readpos == fport->buf + fport->bufsize) { fport->readpos = fport->buf; status = file_fill_buffer(fport); if (status != PJ_SUCCESS) { frame->type = PJMEDIA_FRAME_TYPE_NONE; frame->size = 0; return status; } } } else { unsigned endread; /* Split read. * First stage: read until end of buffer. */ endread = (unsigned)((fport->buf+fport->bufsize) - fport->readpos); pj_memcpy(frame->buf, fport->readpos, endread); /* Second stage: fill up buffer, and read from the start of buffer. */ status = file_fill_buffer(fport); if (status != PJ_SUCCESS) { pj_bzero(((char*)frame->buf)+endread, frame_size-endread); return status; } pj_memcpy(((char*)frame->buf)+endread, fport->buf, frame_size-endread); fport->readpos = fport->buf + (frame_size - endread); } return PJ_SUCCESS; } /* * Destroy port. */ static pj_status_t file_list_on_destroy(pjmedia_port *this_port) { struct playlist_port *fport = (struct playlist_port*) this_port; int index; pj_assert(this_port->info.signature == SIGNATURE); for (index=0; indexmax_file; index++) pj_file_close(fport->fd_list[index]); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/wav_writer.c ================================================ /* $Id: wav_writer.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "wav_writer.c" #define SIGNATURE PJMEDIA_SIG_PORT_WAV_WRITER struct file_port { pjmedia_port base; pjmedia_wave_fmt_tag fmt_tag; pj_uint16_t bytes_per_sample; pj_size_t bufsize; char *buf; char *writepos; pj_size_t total; pj_oshandle_t fd; pj_size_t cb_size; pj_status_t (*cb)(pjmedia_port*, void*); }; static pj_status_t file_put_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t file_get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t file_on_destroy(pjmedia_port *this_port); /* * Create file writer port. */ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, const char *filename, unsigned sampling_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned flags, pj_ssize_t buff_size, pjmedia_port **p_port ) { struct file_port *fport; pjmedia_wave_hdr wave_hdr; pj_ssize_t size; pj_str_t name; pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL); /* Only supports 16bits per sample for now. * See flush_buffer(). */ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); /* Create file port instance. */ fport = PJ_POOL_ZALLOC_T(pool, struct file_port); PJ_ASSERT_RETURN(fport != NULL, PJ_ENOMEM); /* Initialize port info. */ pj_strdup2(pool, &name, filename); pjmedia_port_info_init(&fport->base.info, &name, SIGNATURE, sampling_rate, channel_count, bits_per_sample, samples_per_frame); fport->base.get_frame = &file_get_frame; fport->base.put_frame = &file_put_frame; fport->base.on_destroy = &file_on_destroy; if (flags == PJMEDIA_FILE_WRITE_ALAW) { fport->fmt_tag = PJMEDIA_WAVE_FMT_TAG_ALAW; fport->bytes_per_sample = 1; } else if (flags == PJMEDIA_FILE_WRITE_ULAW) { fport->fmt_tag = PJMEDIA_WAVE_FMT_TAG_ULAW; fport->bytes_per_sample = 1; } else { fport->fmt_tag = PJMEDIA_WAVE_FMT_TAG_PCM; fport->bytes_per_sample = 2; } /* Open file in write and read mode. * We need the read mode because we'll modify the WAVE header once * the recording has completed. */ status = pj_file_open(pool, filename, PJ_O_WRONLY, &fport->fd); if (status != PJ_SUCCESS) return status; /* Initialize WAVE header */ pj_bzero(&wave_hdr, sizeof(pjmedia_wave_hdr)); wave_hdr.riff_hdr.riff = PJMEDIA_RIFF_TAG; wave_hdr.riff_hdr.file_len = 0; /* will be filled later */ wave_hdr.riff_hdr.wave = PJMEDIA_WAVE_TAG; wave_hdr.fmt_hdr.fmt = PJMEDIA_FMT_TAG; wave_hdr.fmt_hdr.len = 16; wave_hdr.fmt_hdr.fmt_tag = (pj_uint16_t)fport->fmt_tag; wave_hdr.fmt_hdr.nchan = (pj_int16_t)channel_count; wave_hdr.fmt_hdr.sample_rate = sampling_rate; wave_hdr.fmt_hdr.bytes_per_sec = sampling_rate * channel_count * fport->bytes_per_sample; wave_hdr.fmt_hdr.block_align = (pj_uint16_t) (fport->bytes_per_sample * channel_count); wave_hdr.fmt_hdr.bits_per_sample = (pj_uint16_t) (fport->bytes_per_sample * 8); wave_hdr.data_hdr.data = PJMEDIA_DATA_TAG; wave_hdr.data_hdr.len = 0; /* will be filled later */ /* Convert WAVE header from host byte order to little endian * before writing the header. */ pjmedia_wave_hdr_host_to_file(&wave_hdr); /* Write WAVE header */ if (fport->fmt_tag != PJMEDIA_WAVE_FMT_TAG_PCM) { pjmedia_wave_subchunk fact_chunk; pj_uint32_t tmp = 0; fact_chunk.id = PJMEDIA_FACT_TAG; fact_chunk.len = 4; PJMEDIA_WAVE_NORMALIZE_SUBCHUNK(&fact_chunk); /* Write WAVE header without DATA chunk header */ size = sizeof(pjmedia_wave_hdr) - sizeof(wave_hdr.data_hdr); status = pj_file_write(fport->fd, &wave_hdr, &size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } /* Write FACT chunk if it stores compressed data */ size = sizeof(fact_chunk); status = pj_file_write(fport->fd, &fact_chunk, &size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } size = 4; status = pj_file_write(fport->fd, &tmp, &size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } /* Write DATA chunk header */ size = sizeof(wave_hdr.data_hdr); status = pj_file_write(fport->fd, &wave_hdr.data_hdr, &size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } } else { size = sizeof(pjmedia_wave_hdr); status = pj_file_write(fport->fd, &wave_hdr, &size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } } /* Set buffer size. */ if (buff_size < 1) buff_size = PJMEDIA_FILE_PORT_BUFSIZE; fport->bufsize = buff_size; /* Check that buffer size is greater than bytes per frame */ pj_assert(fport->bufsize >= PJMEDIA_PIA_AVG_FSZ(&fport->base.info)); /* Allocate buffer and set initial write position */ fport->buf = (char*) pj_pool_alloc(pool, fport->bufsize); if (fport->buf == NULL) { pj_file_close(fport->fd); return PJ_ENOMEM; } fport->writepos = fport->buf; /* Done. */ *p_port = &fport->base; PJ_LOG(4,(THIS_FILE, "File writer '%.*s' created: samp.rate=%d, bufsize=%uKB", (int)fport->base.info.name.slen, fport->base.info.name.ptr, PJMEDIA_PIA_SRATE(&fport->base.info), fport->bufsize / 1000)); return PJ_SUCCESS; } /* * Get current writing position. */ PJ_DEF(pj_ssize_t) pjmedia_wav_writer_port_get_pos( pjmedia_port *port ) { struct file_port *fport; /* Sanity check */ PJ_ASSERT_RETURN(port, -PJ_EINVAL); /* Check that this is really a writer port */ PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, -PJ_EINVALIDOP); fport = (struct file_port*) port; return fport->total; } /* * Register callback. */ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_set_cb( pjmedia_port *port, pj_size_t pos, void *user_data, pj_status_t (*cb)(pjmedia_port *port, void *usr_data)) { struct file_port *fport; /* Sanity check */ PJ_ASSERT_RETURN(port && cb, PJ_EINVAL); /* Check that this is really a writer port */ PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP); fport = (struct file_port*) port; fport->cb_size = pos; fport->base.port_data.pdata = user_data; fport->cb = cb; return PJ_SUCCESS; } #if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 static void swap_samples(pj_int16_t *samples, unsigned count) { unsigned i; for (i=0; iwritepos - fport->buf; pj_status_t status; /* Convert samples to little endian */ swap_samples((pj_int16_t*)fport->buf, bytes/fport->bytes_per_sample); /* Write to file. */ status = pj_file_write(fport->fd, fport->buf, &bytes); /* Reset writepos */ fport->writepos = fport->buf; return status; } /* * Put a frame into the buffer. When the buffer is full, flush the buffer * to the file. */ static pj_status_t file_put_frame(pjmedia_port *this_port, pjmedia_frame *frame) { struct file_port *fport = (struct file_port *)this_port; pj_size_t frame_size; if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_PCM) frame_size = frame->size; else frame_size = frame->size >> 1; /* Flush buffer if we don't have enough room for the frame. */ if (fport->writepos + frame_size > fport->buf + fport->bufsize) { pj_status_t status; status = flush_buffer(fport); if (status != PJ_SUCCESS) return status; } /* Check if frame is not too large. */ PJ_ASSERT_RETURN(fport->writepos+frame_size <= fport->buf+fport->bufsize, PJMEDIA_EFRMFILETOOBIG); /* Copy frame to buffer. */ if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_PCM) { pj_memcpy(fport->writepos, frame->buf, frame->size); } else { unsigned i; pj_int16_t *src = (pj_int16_t*)frame->buf; pj_uint8_t *dst = (pj_uint8_t*)fport->writepos; if (fport->fmt_tag == PJMEDIA_WAVE_FMT_TAG_ULAW) { for (i = 0; i < frame_size; ++i) { *dst++ = pjmedia_linear2ulaw(*src++); } } else { for (i = 0; i < frame_size; ++i) { *dst++ = pjmedia_linear2alaw(*src++); } } } fport->writepos += frame_size; /* Increment total written, and check if we need to call callback */ fport->total += frame_size; if (fport->cb && fport->total >= fport->cb_size) { pj_status_t (*cb)(pjmedia_port*, void*); pj_status_t status; cb = fport->cb; fport->cb = NULL; status = (*cb)(this_port, this_port->port_data.pdata); return status; } return PJ_SUCCESS; } /* * Get frame, basicy is a no-op operation. */ static pj_status_t file_get_frame(pjmedia_port *this_port, pjmedia_frame *frame) { PJ_UNUSED_ARG(this_port); PJ_UNUSED_ARG(frame); return PJ_EINVALIDOP; } /* * Close the port, modify file header with updated file length. */ static pj_status_t file_on_destroy(pjmedia_port *this_port) { enum { FILE_LEN_POS = 4, DATA_LEN_POS = 40 }; struct file_port *fport = (struct file_port *)this_port; pj_off_t file_size; pj_ssize_t bytes; pj_uint32_t wave_file_len; pj_uint32_t wave_data_len; pj_status_t status; pj_uint32_t data_len_pos = DATA_LEN_POS; /* Flush remaining buffers. */ if (fport->writepos != fport->buf) flush_buffer(fport); /* Get file size. */ status = pj_file_getpos(fport->fd, &file_size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } /* Calculate wave fields */ wave_file_len = (pj_uint32_t)(file_size - 8); wave_data_len = (pj_uint32_t)(file_size - sizeof(pjmedia_wave_hdr)); #if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 wave_file_len = pj_swap32(wave_file_len); wave_data_len = pj_swap32(wave_data_len); #endif /* Seek to the file_len field. */ status = pj_file_setpos(fport->fd, FILE_LEN_POS, PJ_SEEK_SET); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } /* Write file_len */ bytes = sizeof(wave_file_len); status = pj_file_write(fport->fd, &wave_file_len, &bytes); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } /* Write samples_len in FACT chunk */ if (fport->fmt_tag != PJMEDIA_WAVE_FMT_TAG_PCM) { enum { SAMPLES_LEN_POS = 44}; pj_uint32_t wav_samples_len; /* Adjust wave_data_len & data_len_pos since there is FACT chunk */ wave_data_len -= 12; data_len_pos += 12; wav_samples_len = wave_data_len; /* Seek to samples_len field. */ status = pj_file_setpos(fport->fd, SAMPLES_LEN_POS, PJ_SEEK_SET); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } /* Write samples_len */ bytes = sizeof(wav_samples_len); status = pj_file_write(fport->fd, &wav_samples_len, &bytes); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } } /* Seek to data_len field. */ status = pj_file_setpos(fport->fd, data_len_pos, PJ_SEEK_SET); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } /* Write file_len */ bytes = sizeof(wave_data_len); status = pj_file_write(fport->fd, &wave_data_len, &bytes); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); return status; } /* Close file */ status = pj_file_close(fport->fd); if (status != PJ_SUCCESS) return status; /* Done. */ return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/wave.c ================================================ /* $Id: wave.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include /* * Change the endianness of WAVE header fields. */ static void wave_hdr_swap_bytes( pjmedia_wave_hdr *hdr ) { #if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 hdr->riff_hdr.riff = pj_swap32(hdr->riff_hdr.riff); hdr->riff_hdr.file_len = pj_swap32(hdr->riff_hdr.file_len); hdr->riff_hdr.wave = pj_swap32(hdr->riff_hdr.wave); hdr->fmt_hdr.fmt = pj_swap32(hdr->fmt_hdr.fmt); hdr->fmt_hdr.len = pj_swap32(hdr->fmt_hdr.len); hdr->fmt_hdr.fmt_tag = pj_swap16(hdr->fmt_hdr.fmt_tag); hdr->fmt_hdr.nchan = pj_swap16(hdr->fmt_hdr.nchan); hdr->fmt_hdr.sample_rate = pj_swap32(hdr->fmt_hdr.sample_rate); hdr->fmt_hdr.bytes_per_sec = pj_swap32(hdr->fmt_hdr.bytes_per_sec); hdr->fmt_hdr.block_align = pj_swap16(hdr->fmt_hdr.block_align); hdr->fmt_hdr.bits_per_sample = pj_swap16(hdr->fmt_hdr.bits_per_sample); hdr->data_hdr.data = pj_swap32(hdr->data_hdr.data); hdr->data_hdr.len = pj_swap32(hdr->data_hdr.len); #else PJ_UNUSED_ARG(hdr); #endif } PJ_DEF(void) pjmedia_wave_hdr_file_to_host( pjmedia_wave_hdr *hdr ) { wave_hdr_swap_bytes(hdr); } PJ_DEF(void) pjmedia_wave_hdr_host_to_file( pjmedia_wave_hdr *hdr ) { wave_hdr_swap_bytes(hdr); } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia/wsola.c ================================================ /* $Id: wsola.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include /* * This file contains implementation of WSOLA using PJMEDIA_WSOLA_IMP_WSOLA * or PJMEDIA_WSOLA_IMP_NULL */ #define THIS_FILE "wsola.c" /* * http://trac.pjsip.org/repos/ticket/683: * Workaround for segfault problem in the fixed point version of create_win() * on ARM9 platform, possibly due to gcc optimization bug. * * For now, we will use linear window when floating point is disabled. */ #ifndef PJMEDIA_WSOLA_LINEAR_WIN # define PJMEDIA_WSOLA_LINEAR_WIN (!PJ_HAS_FLOATING_POINT) #endif #if 0 # define TRACE_(x) PJ_LOG(4,x) #else # define TRACE_(x) #endif #if 0 # define CHECK_(x) pj_assert(x) #else # define CHECK_(x) #endif #if (PJMEDIA_WSOLA_IMP==PJMEDIA_WSOLA_IMP_WSOLA) || \ (PJMEDIA_WSOLA_IMP==PJMEDIA_WSOLA_IMP_WSOLA_LITE) /* * WSOLA implementation using WSOLA */ /* Buffer size including history, in frames */ #define FRAME_CNT 6 /* Number of history frames in buffer */ #define HIST_CNT 1.5 /* Template size, in msec */ #define TEMPLATE_PTIME PJMEDIA_WSOLA_TEMPLATE_LENGTH_MSEC /* Hanning window size, in msec */ #define HANNING_PTIME PJMEDIA_WSOLA_DELAY_MSEC /* Number of frames in erase buffer */ #define ERASE_CNT ((unsigned)3) /* Minimum distance from template for find_pitch() of expansion, in frames */ #define EXP_MIN_DIST 0.5 /* Maximum distance from template for find_pitch() of expansion, in frames */ #define EXP_MAX_DIST HIST_CNT /* Duration of a continuous synthetic frames after which the volume * of the synthetic frame will be set to zero with fading-out effect. */ #define MAX_EXPAND_MSEC PJMEDIA_WSOLA_MAX_EXPAND_MSEC /* Buffer content: * * +---------+-----------+--------------------+ * | history | min_extra | more extra / empty | * +---------+-----------+--------------------+ * ^ ^ ^ ^ * buf hist_size min_extra buf_size * * History size (hist_size) is a constant value, initialized upon creation. * * min_extra size is equal to HANNING_PTIME, this samples is useful for * smoothening samples transition between generated frame & history * (when PLC is invoked), or between generated samples & normal frame * (after lost/PLC). Since min_extra samples need to be available at * any time, this will introduce delay of HANNING_PTIME ms. * * More extra is excess samples produced by PLC (PLC frame generation may * produce more than exact one frame). * * At any particular time, the buffer will contain at least (hist_size + * min_extra) samples. * * A "save" operation will append the new frame to the end of the buffer, * return the frame from samples right after history and shift the buffer * by one frame. * */ /* WSOLA structure */ struct pjmedia_wsola { unsigned clock_rate; /* Sampling rate. */ pj_uint16_t samples_per_frame; /* Samples per frame (const) */ pj_uint16_t channel_count; /* Channel countt (const) */ pj_uint16_t options; /* Options. */ pjmedia_circ_buf *buf; /* The buffer. */ pj_int16_t *erase_buf; /* Temporary erase buffer. */ pj_int16_t *merge_buf; /* Temporary merge buffer. */ pj_uint16_t buf_size; /* Total buffer size (const) */ pj_uint16_t hanning_size; /* Hanning window size (const) */ pj_uint16_t templ_size; /* Template size (const) */ pj_uint16_t hist_size; /* History size (const) */ pj_uint16_t min_extra; /* Minimum extra (const) */ unsigned max_expand_cnt; /* Max # of synthetic samples */ unsigned fade_out_pos; /* Last fade-out position */ pj_uint16_t expand_sr_min_dist;/* Minimum distance from template for find_pitch() on expansion (const) */ pj_uint16_t expand_sr_max_dist;/* Maximum distance from template for find_pitch() on expansion (const) */ #if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0 float *hanning; /* Hanning window. */ #else pj_uint16_t *hanning; /* Hanning window. */ #endif pj_timestamp ts; /* Running timestamp. */ }; #if (PJMEDIA_WSOLA_IMP==PJMEDIA_WSOLA_IMP_WSOLA_LITE) /* In this implementation, waveform similarity comparison is done by calculating * the difference of total level between template frame and the target buffer * for each template_cnt samples. The smallest difference value assumed to be * the most similar block. This seems to be naive, however some tests show * acceptable results and the processing speed is amazing. * * diff level = (template[1]+..+template[n]) - (target[1]+..+target[n]) */ static pj_int16_t *find_pitch(pj_int16_t *frm, pj_int16_t *beg, pj_int16_t *end, unsigned template_cnt, int first) { pj_int16_t *sr, *best=beg; int best_corr = 0x7FFFFFFF; int frm_sum = 0; unsigned i; for (i = 0; i 0? corr : -corr; if (first) { if (abs_corr < best_corr) { best_corr = abs_corr; best = sr; } } else { if (abs_corr <= best_corr) { best_corr = abs_corr; best = sr; } } } /*TRACE_((THIS_FILE, "found pitch at %u", best-beg));*/ return best; } #endif #if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0 /* * Floating point version. */ #if (PJMEDIA_WSOLA_IMP==PJMEDIA_WSOLA_IMP_WSOLA) static pj_int16_t *find_pitch(pj_int16_t *frm, pj_int16_t *beg, pj_int16_t *end, unsigned template_cnt, int first) { pj_int16_t *sr, *best=beg; double best_corr = 0; for (sr=beg; sr!=end; ++sr) { double corr = 0; unsigned i; /* Do calculation on 8 samples at once */ for (i=0; i best_corr) { best_corr = corr; best = sr; } } else { if (corr >= best_corr) { best_corr = corr; best = sr; } } } /*TRACE_((THIS_FILE, "found pitch at %u", best-beg));*/ return best; } #endif static void overlapp_add(pj_int16_t dst[], unsigned count, pj_int16_t l[], pj_int16_t r[], float w[]) { unsigned i; for (i=0; i best_corr) { best_corr = corr; best = sr; } } else { if (corr >= best_corr) { best_corr = corr; best = sr; } } } /*TRACE_((THIS_FILE, "found pitch at %u", best-beg));*/ return best; } #endif static void overlapp_add(pj_int16_t dst[], unsigned count, pj_int16_t l[], pj_int16_t r[], pj_uint16_t w[]) { unsigned i; for (i=0; i> WINDOW_BITS); } } static void overlapp_add_simple(pj_int16_t dst[], unsigned count, pj_int16_t l[], pj_int16_t r[]) { int step = ((WINDOW_MAX_VAL+1) / count), stepdown = WINDOW_MAX_VAL; unsigned i; for (i=0; i> WINDOW_BITS); stepdown -= step; } } #if PJ_HAS_INT64 && !PJMEDIA_WSOLA_LINEAR_WIN /* approx_cos(): * see: http://www.audiomulch.com/~rossb/code/sinusoids/ */ static pj_uint32_t approx_cos( pj_uint32_t x ) { pj_uint32_t i,j,k; if( x == 0 ) return 0xFFFFFFFF; i = x << 1; k = ((x + 0xBFFFFFFD) & 0x80000000) >> 30; j = i - i * ((i & 0x80000000)>>30); j = j >> 15; j = (j * j + j) >> 1; j = j - j * k; return j; } #endif /* PJ_HAS_INT64 && .. */ static void create_win(pj_pool_t *pool, pj_uint16_t **pw, unsigned count) { unsigned i; pj_uint16_t *w = (pj_uint16_t*)pj_pool_calloc(pool, count, sizeof(pj_uint16_t)); *pw = w; for (i=0; imax_expand_cnt; #if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0 float fade_pos = (float)wsola->fade_out_pos; #else int fade_pos = wsola->fade_out_pos; #endif if (wsola->fade_out_pos == 0) { pjmedia_zero_samples(buf, count); } else if (fade_pos < count) { for (; fade_pos; --fade_pos, ++buf) { *buf = (pj_int16_t)(*buf * fade_pos / fade_cnt); } if (buf != end) pjmedia_zero_samples(buf, (unsigned)(end - buf)); wsola->fade_out_pos = 0; } else { for (; buf != end; --fade_pos, ++buf) { *buf = (pj_int16_t)(*buf * fade_pos / fade_cnt); } wsola->fade_out_pos -= count; } } PJ_DEF(pj_status_t) pjmedia_wsola_create( pj_pool_t *pool, unsigned clock_rate, unsigned samples_per_frame, unsigned channel_count, unsigned options, pjmedia_wsola **p_wsola) { pjmedia_wsola *wsola; pj_status_t status; PJ_ASSERT_RETURN(pool && clock_rate && samples_per_frame && p_wsola, PJ_EINVAL); PJ_ASSERT_RETURN(clock_rate <= 65535, PJ_EINVAL); PJ_ASSERT_RETURN(samples_per_frame < clock_rate, PJ_EINVAL); PJ_ASSERT_RETURN(channel_count > 0, PJ_EINVAL); /* Allocate wsola and initialize vars */ wsola = PJ_POOL_ZALLOC_T(pool, pjmedia_wsola); wsola->clock_rate= (pj_uint16_t) clock_rate; wsola->samples_per_frame = (pj_uint16_t) samples_per_frame; wsola->channel_count = (pj_uint16_t) channel_count; wsola->options = (pj_uint16_t) options; wsola->max_expand_cnt = clock_rate * MAX_EXPAND_MSEC / 1000; wsola->fade_out_pos = wsola->max_expand_cnt; /* Create circular buffer */ wsola->buf_size = (pj_uint16_t) (samples_per_frame * FRAME_CNT); status = pjmedia_circ_buf_create(pool, wsola->buf_size, &wsola->buf); if (status != PJ_SUCCESS) { PJ_LOG(3, (THIS_FILE, "Failed to create circular buf")); return status; } /* Calculate history size */ wsola->hist_size = (pj_uint16_t)(HIST_CNT * samples_per_frame); /* Calculate template size */ wsola->templ_size = (pj_uint16_t)(TEMPLATE_PTIME * clock_rate * channel_count / 1000); if (wsola->templ_size > samples_per_frame) wsola->templ_size = wsola->samples_per_frame; /* Calculate hanning window size */ wsola->hanning_size = (pj_uint16_t)(HANNING_PTIME * clock_rate * channel_count / 1000); if (wsola->hanning_size > wsola->samples_per_frame) wsola->hanning_size = wsola->samples_per_frame; pj_assert(wsola->templ_size <= wsola->hanning_size); /* Create merge buffer */ wsola->merge_buf = (pj_int16_t*) pj_pool_calloc(pool, wsola->hanning_size, sizeof(pj_int16_t)); /* Setup with PLC */ if ((options & PJMEDIA_WSOLA_NO_PLC) == 0) { wsola->min_extra = wsola->hanning_size; wsola->expand_sr_min_dist = (pj_uint16_t) (EXP_MIN_DIST * wsola->samples_per_frame); wsola->expand_sr_max_dist = (pj_uint16_t) (EXP_MAX_DIST * wsola->samples_per_frame); } /* Setup with hanning */ if ((options & PJMEDIA_WSOLA_NO_HANNING) == 0) { create_win(pool, &wsola->hanning, wsola->hanning_size); } /* Setup with discard */ if ((options & PJMEDIA_WSOLA_NO_DISCARD) == 0) { wsola->erase_buf = (pj_int16_t*)pj_pool_calloc(pool, samples_per_frame * ERASE_CNT, sizeof(pj_int16_t)); } /* Generate dummy extra */ pjmedia_circ_buf_set_len(wsola->buf, wsola->hist_size + wsola->min_extra); *p_wsola = wsola; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_wsola_destroy(pjmedia_wsola *wsola) { /* Nothing to do */ PJ_UNUSED_ARG(wsola); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_wsola_set_max_expand(pjmedia_wsola *wsola, unsigned msec) { PJ_ASSERT_RETURN(wsola, PJ_EINVAL); wsola->max_expand_cnt = msec * wsola->clock_rate / 1000; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_wsola_reset( pjmedia_wsola *wsola, unsigned options) { PJ_ASSERT_RETURN(wsola && options==0, PJ_EINVAL); PJ_UNUSED_ARG(options); pjmedia_circ_buf_reset(wsola->buf); pjmedia_circ_buf_set_len(wsola->buf, wsola->hist_size + wsola->min_extra); pjmedia_zero_samples(wsola->buf->start, wsola->buf->len); wsola->fade_out_pos = wsola->max_expand_cnt; return PJ_SUCCESS; } static void expand(pjmedia_wsola *wsola, unsigned needed) { unsigned generated = 0; unsigned rep; pj_int16_t *reg1, *reg2; unsigned reg1_len, reg2_len; pjmedia_circ_buf_pack_buffer(wsola->buf); pjmedia_circ_buf_get_read_regions(wsola->buf, ®1, ®1_len, ®2, ®2_len); CHECK_(reg2_len == 0); for (rep=1;; ++rep) { pj_int16_t *start, *templ; unsigned dist; templ = reg1 + reg1_len - wsola->hanning_size; CHECK_(templ - reg1 >= wsola->hist_size); start = find_pitch(templ, templ - wsola->expand_sr_max_dist, templ - wsola->expand_sr_min_dist, wsola->templ_size, 1); /* Should we make sure that "start" is really aligned to * channel #0, in case of stereo? Probably not necessary, as * find_pitch() should have found the best match anyway. */ if (wsola->options & PJMEDIA_WSOLA_NO_HANNING) { overlapp_add_simple(wsola->merge_buf, wsola->hanning_size, templ, start); } else { /* Check if pointers are in the valid range */ CHECK_(templ >= wsola->buf->buf && templ + wsola->hanning_size <= wsola->buf->buf + wsola->buf->capacity); CHECK_(start >= wsola->buf->buf && start + wsola->hanning_size <= wsola->buf->buf + wsola->buf->capacity); overlapp_add(wsola->merge_buf, wsola->hanning_size, templ, start, wsola->hanning); } /* How many new samples do we have */ dist = (unsigned)(templ - start); /* Not enough buffer to hold the result */ if (reg1_len + dist > wsola->buf_size) { pj_assert(!"WSOLA buffer size may be to small!"); break; } /* Copy the "tail" (excess frame) to the end */ pjmedia_move_samples(templ + wsola->hanning_size, start + wsola->hanning_size, dist); /* Copy the merged frame */ pjmedia_copy_samples(templ, wsola->merge_buf, wsola->hanning_size); /* We have new samples */ reg1_len += dist; pjmedia_circ_buf_set_len(wsola->buf, reg1_len); generated += dist; if (generated >= needed) { TRACE_((THIS_FILE, "WSOLA frame expanded after %d iterations", rep)); break; } } } static unsigned compress(pjmedia_wsola *wsola, pj_int16_t *buf, unsigned count, unsigned del_cnt) { unsigned samples_del = 0, rep; for (rep=1; ; ++rep) { pj_int16_t *start, *end; unsigned dist; if (count <= wsola->hanning_size + del_cnt) { TRACE_((THIS_FILE, "Not enough samples to compress!")); return samples_del; } // Make start distance to del_cnt, so discard will be performed in // only one iteration. //start = buf + (frmsz >> 1); start = buf + del_cnt - samples_del; end = start + wsola->samples_per_frame; if (end + wsola->hanning_size > buf + count) { end = buf+count-wsola->hanning_size; } CHECK_(start < end); start = find_pitch(buf, start, end, wsola->templ_size, 0); dist = (unsigned)(start - buf); if (wsola->options & PJMEDIA_WSOLA_NO_HANNING) { overlapp_add_simple(buf, wsola->hanning_size, buf, start); } else { overlapp_add(buf, wsola->hanning_size, buf, start, wsola->hanning); } pjmedia_move_samples(buf + wsola->hanning_size, buf + wsola->hanning_size + dist, count - wsola->hanning_size - dist); count -= dist; samples_del += dist; if (samples_del >= del_cnt) { TRACE_((THIS_FILE, "Erased %d of %d requested after %d iteration(s)", samples_del, del_cnt, rep)); break; } } return samples_del; } PJ_DEF(pj_status_t) pjmedia_wsola_save( pjmedia_wsola *wsola, pj_int16_t frm[], pj_bool_t prev_lost) { unsigned buf_len; pj_status_t status; buf_len = pjmedia_circ_buf_get_len(wsola->buf); /* Update vars */ wsola->ts.u64 += wsola->samples_per_frame; /* If previous frame was lost, smoothen this frame with the generated one */ if (prev_lost) { pj_int16_t *reg1, *reg2; unsigned reg1_len, reg2_len; pj_int16_t *ola_left; /* Trim excessive len */ if ((int)buf_len > wsola->hist_size + (wsola->min_extra<<1)) { buf_len = wsola->hist_size + (wsola->min_extra<<1); pjmedia_circ_buf_set_len(wsola->buf, buf_len); } pjmedia_circ_buf_get_read_regions(wsola->buf, ®1, ®1_len, ®2, ®2_len); CHECK_(pjmedia_circ_buf_get_len(wsola->buf) >= (unsigned)(wsola->hist_size + (wsola->min_extra<<1))); /* Continue applying fade out to the extra samples */ if ((wsola->options & PJMEDIA_WSOLA_NO_FADING)==0) { if (reg2_len == 0) { wsola_fade_out(wsola, reg1 + reg1_len - (wsola->min_extra<<1), (wsola->min_extra<<1)); } else if ((int)reg2_len >= (wsola->min_extra<<1)) { wsola_fade_out(wsola, reg2 + reg2_len - (wsola->min_extra<<1), (wsola->min_extra<<1)); } else { unsigned tmp = (wsola->min_extra<<1) - reg2_len; wsola_fade_out(wsola, reg1 + reg1_len - tmp, tmp); wsola_fade_out(wsola, reg2, reg2_len); } } /* Get the region in buffer to be merged with the frame */ if (reg2_len == 0) { ola_left = reg1 + reg1_len - wsola->min_extra; } else if (reg2_len >= wsola->min_extra) { ola_left = reg2 + reg2_len - wsola->min_extra; } else { unsigned tmp; tmp = wsola->min_extra - reg2_len; pjmedia_copy_samples(wsola->merge_buf, reg1 + reg1_len - tmp, tmp); pjmedia_copy_samples(wsola->merge_buf + tmp, reg2, reg2_len); ola_left = wsola->merge_buf; } /* Apply fade-in to the frame before merging */ if ((wsola->options & PJMEDIA_WSOLA_NO_FADING)==0) { unsigned count = wsola->min_extra; int fade_in_pos; /* Scale fade_in position based on last fade-out */ fade_in_pos = wsola->fade_out_pos * count / wsola->max_expand_cnt; /* Fade-in it */ fade_in(frm, wsola->samples_per_frame, fade_in_pos, count); } /* Merge it */ overlapp_add_simple(frm, wsola->min_extra, ola_left, frm); /* Trim len */ buf_len -= wsola->min_extra; pjmedia_circ_buf_set_len(wsola->buf, buf_len); } else if ((wsola->options & PJMEDIA_WSOLA_NO_FADING)==0 && wsola->fade_out_pos != wsola->max_expand_cnt) { unsigned count = wsola->min_extra; int fade_in_pos; /* Fade out the remaining synthetic samples */ if (buf_len > wsola->hist_size) { pj_int16_t *reg1, *reg2; unsigned reg1_len, reg2_len; /* Number of samples to fade out */ count = buf_len - wsola->hist_size; pjmedia_circ_buf_get_read_regions(wsola->buf, ®1, ®1_len, ®2, ®2_len); CHECK_(pjmedia_circ_buf_get_len(wsola->buf) >= (unsigned)(wsola->hist_size + (wsola->min_extra<<1))); /* Continue applying fade out to the extra samples */ if (reg2_len == 0) { wsola_fade_out(wsola, reg1 + reg1_len - count, count); } else if (reg2_len >= count) { wsola_fade_out(wsola, reg2 + reg2_len - count, count); } else { unsigned tmp = count - reg2_len; wsola_fade_out(wsola, reg1 + reg1_len - tmp, tmp); wsola_fade_out(wsola, reg2, reg2_len); } } /* Apply fade-in to the frame */ count = wsola->min_extra; /* Scale fade_in position based on last fade-out */ fade_in_pos = wsola->fade_out_pos * count / wsola->max_expand_cnt; /* Fade it in */ fade_in(frm, wsola->samples_per_frame, fade_in_pos, count); } wsola->fade_out_pos = wsola->max_expand_cnt; status = pjmedia_circ_buf_write(wsola->buf, frm, wsola->samples_per_frame); if (status != PJ_SUCCESS) { TRACE_((THIS_FILE, "Failed writing to circbuf [err=%d]", status)); return status; } status = pjmedia_circ_buf_copy(wsola->buf, wsola->hist_size, frm, wsola->samples_per_frame); if (status != PJ_SUCCESS) { TRACE_((THIS_FILE, "Failed copying from circbuf [err=%d]", status)); return status; } return pjmedia_circ_buf_adv_read_ptr(wsola->buf, wsola->samples_per_frame); } PJ_DEF(pj_status_t) pjmedia_wsola_generate( pjmedia_wsola *wsola, pj_int16_t frm[]) { unsigned samples_len, samples_req; pj_status_t status = PJ_SUCCESS; CHECK_(pjmedia_circ_buf_get_len(wsola->buf) >= wsola->hist_size + wsola->min_extra); /* Calculate how many samples in the buffer */ samples_len = pjmedia_circ_buf_get_len(wsola->buf) - wsola->hist_size; /* Calculate how many samples are required to be available in the buffer */ samples_req = wsola->samples_per_frame + (wsola->min_extra << 1); wsola->ts.u64 += wsola->samples_per_frame; if (samples_len < samples_req) { /* Expand buffer */ expand(wsola, samples_req - samples_len); TRACE_((THIS_FILE, "Buf size after expanded = %d", pjmedia_circ_buf_get_len(wsola->buf))); } status = pjmedia_circ_buf_copy(wsola->buf, wsola->hist_size, frm, wsola->samples_per_frame); if (status != PJ_SUCCESS) { TRACE_((THIS_FILE, "Failed copying from circbuf [err=%d]", status)); return status; } pjmedia_circ_buf_adv_read_ptr(wsola->buf, wsola->samples_per_frame); /* Apply fade-out to the frame */ if ((wsola->options & PJMEDIA_WSOLA_NO_FADING)==0) { wsola_fade_out(wsola, frm, wsola->samples_per_frame); } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_wsola_discard( pjmedia_wsola *wsola, pj_int16_t buf1[], unsigned buf1_cnt, pj_int16_t buf2[], unsigned buf2_cnt, unsigned *del_cnt) { PJ_ASSERT_RETURN(wsola && buf1 && buf1_cnt && del_cnt, PJ_EINVAL); PJ_ASSERT_RETURN(*del_cnt, PJ_EINVAL); if (buf2_cnt == 0) { /* The whole buffer is contiguous space, straight away. */ *del_cnt = compress(wsola, buf1, buf1_cnt, *del_cnt); } else { PJ_ASSERT_RETURN(buf2, PJ_EINVAL); if (buf1_cnt < ERASE_CNT * wsola->samples_per_frame && buf2_cnt < ERASE_CNT * wsola->samples_per_frame && wsola->erase_buf == NULL) { /* We need erase_buf but WSOLA was created with * PJMEDIA_WSOLA_NO_DISCARD flag. */ pj_assert(!"WSOLA need erase buffer!"); return PJ_EINVALIDOP; } if (buf2_cnt >= ERASE_CNT * wsola->samples_per_frame) { /* Enough space to perform compress in the second buffer. */ *del_cnt = compress(wsola, buf2, buf2_cnt, *del_cnt); } else if (buf1_cnt >= ERASE_CNT * wsola->samples_per_frame) { /* Enough space to perform compress in the first buffer, but then * we need to re-arrange the buffers so there is no gap between * buffers. */ unsigned max; *del_cnt = compress(wsola, buf1, buf1_cnt, *del_cnt); max = *del_cnt; if (max > buf2_cnt) max = buf2_cnt; pjmedia_move_samples(buf1 + buf1_cnt - (*del_cnt), buf2, max); if (max < buf2_cnt) { pjmedia_move_samples(buf2, buf2+(*del_cnt), buf2_cnt-max); } } else { /* Not enough samples in either buffers to perform compress. * Need to combine the buffers in a contiguous space, the erase_buf. */ unsigned buf_size = buf1_cnt + buf2_cnt; pj_int16_t *rem; /* remainder */ unsigned rem_cnt; if (buf_size > ERASE_CNT * wsola->samples_per_frame) { buf_size = ERASE_CNT * wsola->samples_per_frame; rem_cnt = buf1_cnt + buf2_cnt - buf_size; rem = buf2 + buf2_cnt - rem_cnt; } else { rem = NULL; rem_cnt = 0; } pjmedia_copy_samples(wsola->erase_buf, buf1, buf1_cnt); pjmedia_copy_samples(wsola->erase_buf+buf1_cnt, buf2, buf_size-buf1_cnt); *del_cnt = compress(wsola, wsola->erase_buf, buf_size, *del_cnt); buf_size -= (*del_cnt); /* Copy back to buffers */ if (buf_size == buf1_cnt) { pjmedia_copy_samples(buf1, wsola->erase_buf, buf_size); if (rem_cnt) { pjmedia_move_samples(buf2, rem, rem_cnt); } } else if (buf_size < buf1_cnt) { pjmedia_copy_samples(buf1, wsola->erase_buf, buf_size); if (rem_cnt) { unsigned c = rem_cnt; if (c > buf1_cnt-buf_size) { c = buf1_cnt-buf_size; } pjmedia_copy_samples(buf1+buf_size, rem, c); rem += c; rem_cnt -= c; if (rem_cnt) pjmedia_move_samples(buf2, rem, rem_cnt); } } else { pjmedia_copy_samples(buf1, wsola->erase_buf, buf1_cnt); pjmedia_copy_samples(buf2, wsola->erase_buf+buf1_cnt, buf_size-buf1_cnt); if (rem_cnt) { pjmedia_move_samples(buf2+buf_size-buf1_cnt, rem, rem_cnt); } } } } return (*del_cnt) > 0 ? PJ_SUCCESS : PJ_ETOOSMALL; } #elif PJMEDIA_WSOLA_IMP==PJMEDIA_WSOLA_IMP_NULL /* * WSOLA implementation using NULL */ struct pjmedia_wsola { unsigned samples_per_frame; }; PJ_DEF(pj_status_t) pjmedia_wsola_create( pj_pool_t *pool, unsigned clock_rate, unsigned samples_per_frame, unsigned channel_count, unsigned options, pjmedia_wsola **p_wsola) { pjmedia_wsola *wsola; wsola = PJ_POOL_ZALLOC_T(pool, struct pjmedia_wsola); wsola->samples_per_frame = samples_per_frame; PJ_UNUSED_ARG(clock_rate); PJ_UNUSED_ARG(channel_count); PJ_UNUSED_ARG(options); *p_wsola = wsola; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_wsola_destroy(pjmedia_wsola *wsola) { PJ_UNUSED_ARG(wsola); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_wsola_reset( pjmedia_wsola *wsola, unsigned options) { PJ_UNUSED_ARG(wsola); PJ_UNUSED_ARG(options); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_wsola_save( pjmedia_wsola *wsola, pj_int16_t frm[], pj_bool_t prev_lost) { PJ_UNUSED_ARG(wsola); PJ_UNUSED_ARG(frm); PJ_UNUSED_ARG(prev_lost); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_wsola_generate( pjmedia_wsola *wsola, pj_int16_t frm[]) { pjmedia_zero_samples(frm, wsola->samples_per_frame); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_wsola_discard( pjmedia_wsola *wsola, pj_int16_t buf1[], unsigned buf1_cnt, pj_int16_t buf2[], unsigned buf2_cnt, unsigned *del_cnt) { CHECK_(buf1_cnt + buf2_cnt >= wsola->samples_per_frame); PJ_UNUSED_ARG(buf1); PJ_UNUSED_ARG(buf1_cnt); PJ_UNUSED_ARG(buf2); PJ_UNUSED_ARG(buf2_cnt); *del_cnt = wsola->samples_per_frame; return PJ_SUCCESS; } #endif /* #if PJMEDIA_WSOLA_IMP.. */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-audiodev/alsa_dev.c ================================================ /* $Id: alsa_dev.c 4283 2012-10-12 06:19:32Z ming $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2007-2009 Keystream AB and Konftel AB, All rights reserved. * Author: * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #if defined(PJMEDIA_AUDIO_DEV_HAS_ALSA) && PJMEDIA_AUDIO_DEV_HAS_ALSA #include #include #include #include #include #include #include #include #define THIS_FILE "alsa_dev.c" #define MAX_DEVICES 128 /* Set to 1 to enable tracing */ #if 0 # define TRACE_(expr) PJ_LOG(5,expr) #else # define TRACE_(expr) #endif /* * Factory prototypes */ static pj_status_t alsa_factory_init(pjmedia_aud_dev_factory *f); static pj_status_t alsa_factory_destroy(pjmedia_aud_dev_factory *f); static pj_status_t alsa_factory_refresh(pjmedia_aud_dev_factory *f); static unsigned alsa_factory_get_dev_count(pjmedia_aud_dev_factory *f); static pj_status_t alsa_factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_dev_info *info); static pj_status_t alsa_factory_default_param(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_param *param); static pj_status_t alsa_factory_create_stream(pjmedia_aud_dev_factory *f, const pjmedia_aud_param *param, pjmedia_aud_rec_cb rec_cb, pjmedia_aud_play_cb play_cb, void *user_data, pjmedia_aud_stream **p_strm); static void alsa_factory_set_observer(pjmedia_aud_dev_factory *f, pjmedia_aud_dev_change_callback cb); static int alsa_factory_get_default_rec_dev(pjmedia_aud_dev_factory *f); static int alsa_factory_get_default_play_dev(pjmedia_aud_dev_factory *f); /* * Stream prototypes */ static pj_status_t alsa_stream_get_param(pjmedia_aud_stream *strm, pjmedia_aud_param *param); static pj_status_t alsa_stream_get_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, void *value); static pj_status_t alsa_stream_set_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, const void *value); static pj_status_t alsa_stream_start(pjmedia_aud_stream *strm); static pj_status_t alsa_stream_stop(pjmedia_aud_stream *strm); static pj_status_t alsa_stream_destroy(pjmedia_aud_stream *strm); /* alsa device info */ struct alsa_dev_info { pjmedia_aud_dev_info info; char alsa_name[64]; }; struct alsa_factory { pjmedia_aud_dev_factory base; pj_pool_factory *pf; pj_pool_t *pool; pj_pool_t *base_pool; unsigned dev_cnt; struct alsa_dev_info devs[MAX_DEVICES]; }; struct alsa_stream { pjmedia_aud_stream base; /* Common */ pj_pool_t *pool; struct alsa_factory *af; void *user_data; pjmedia_aud_param param; /* Running parameter */ int rec_id; /* Capture device id */ int quit; /* Playback */ snd_pcm_t *pb_pcm; snd_pcm_uframes_t pb_frames; /* samples_per_frame */ pjmedia_aud_play_cb pb_cb; unsigned pb_buf_size; char *pb_buf; pj_thread_t *pb_thread; /* Capture */ snd_pcm_t *ca_pcm; snd_pcm_uframes_t ca_frames; /* samples_per_frame */ pjmedia_aud_rec_cb ca_cb; unsigned ca_buf_size; char *ca_buf; pj_thread_t *ca_thread; }; static pjmedia_aud_dev_factory_op alsa_factory_op = { &alsa_factory_init, &alsa_factory_destroy, &alsa_factory_get_dev_count, &alsa_factory_get_dev_info, &alsa_factory_default_param, &alsa_factory_create_stream, &alsa_factory_refresh, &alsa_factory_set_observer, &alsa_factory_get_default_rec_dev, &alsa_factory_get_default_play_dev }; static pjmedia_aud_stream_op alsa_stream_op = { &alsa_stream_get_param, &alsa_stream_get_cap, &alsa_stream_set_cap, &alsa_stream_start, &alsa_stream_stop, &alsa_stream_destroy }; static void null_alsa_error_handler (const char *file, int line, const char *function, int err, const char *fmt, ...) { PJ_UNUSED_ARG(file); PJ_UNUSED_ARG(line); PJ_UNUSED_ARG(function); PJ_UNUSED_ARG(err); PJ_UNUSED_ARG(fmt); } static void alsa_error_handler (const char *file, int line, const char *function, int err, const char *fmt, ...) { char err_msg[128]; int index, len; va_list arg; #ifndef NDEBUG index = snprintf (err_msg, sizeof(err_msg), "ALSA lib %s:%i:(%s) ", file, line, function); #else index = snprintf (err_msg, sizeof(err_msg), "ALSA lib: "); #endif if (index < 1 || index >= (int)sizeof(err_msg)) { index = sizeof(err_msg)-1; err_msg[index] = '\0'; goto print_msg; } va_start (arg, fmt); if (index < sizeof(err_msg)-1) { len = vsnprintf( err_msg+index, sizeof(err_msg)-index, fmt, arg); if (len < 1 || len >= (int)sizeof(err_msg)-index) len = sizeof(err_msg)-index-1; index += len; err_msg[index] = '\0'; } va_end(arg); if (err && index < sizeof(err_msg)-1) { len = snprintf( err_msg+index, sizeof(err_msg)-index, ": %s", snd_strerror(err)); if (len < 1 || len >= (int)sizeof(err_msg)-index) len = sizeof(err_msg)-index-1; index += len; err_msg[index] = '\0'; } print_msg: PJ_LOG (4,(THIS_FILE, "%s", err_msg)); } static pj_status_t add_dev (struct alsa_factory *af, const char *dev_name, const char *dev_desc) { struct alsa_dev_info *adi; snd_pcm_t* pcm; int pb_result, ca_result; if (af->dev_cnt >= PJ_ARRAY_SIZE(af->devs)) return PJ_ETOOMANY; adi = &af->devs[af->dev_cnt]; TRACE_((THIS_FILE, "add_dev (%s): Enter", dev_name)); /* Try to open the device in playback mode */ pb_result = snd_pcm_open (&pcm, dev_name, SND_PCM_STREAM_PLAYBACK, 0); if (pb_result >= 0) { TRACE_((THIS_FILE, "Try to open the device for playback - success")); snd_pcm_close (pcm); } else { TRACE_((THIS_FILE, "Try to open the device for playback - failure")); } /* Try to open the device in capture mode */ ca_result = snd_pcm_open (&pcm, dev_name, SND_PCM_STREAM_CAPTURE, 0); if (ca_result >= 0) { TRACE_((THIS_FILE, "Try to open the device for capture - success")); snd_pcm_close (pcm); } else { TRACE_((THIS_FILE, "Try to open the device for capture - failure")); } /* Check if the device could be opened in playback or capture mode */ if (pb_result<0 && ca_result<0) { TRACE_((THIS_FILE, "Unable to open sound device %s", dev_name)); return PJMEDIA_EAUD_NODEV; } /* Reset device info */ pj_bzero(adi, sizeof(*adi)); /* Set device name */ strncpy(adi->alsa_name, dev_name, sizeof(adi->alsa_name)); /* Set comprehensive device name */ int name_size = sizeof(adi->info.name); if (dev_desc) { pj_bool_t name_set = PJ_FALSE; if (strncmp("sysdefault", dev_name, 10) == 0) { /* Only use first line for default device*/ char *ptr = strstr(dev_desc, "\n"); if (ptr) { int len = ptr - dev_desc; strncpy(adi->info.name, dev_desc, (len >= name_size-1)?name_size:len); name_set = PJ_TRUE; } } else if (strncmp("iec958", dev_name, 6) == 0) { /* Mangle name for SPDIF devices*/ char *ptr = strstr(dev_desc, ","); if (ptr) { int len = ptr - dev_desc; if (len + 18 < name_size) { strncpy(adi->info.name, dev_desc, len); strncpy(adi->info.name+len, ", Digital (S/PDIF)", 18); name_set = PJ_TRUE; } } } if (!name_set) { /* Use the entire description for other device names */ int i = 0; while (i < name_size-1 && dev_desc[i] != '\0') { if (dev_desc[i] == '\n' || dev_desc[i] == '\r') adi->info.name[i] = ' '; else adi->info.name[i] = dev_desc[i]; i++; } } } else { strncpy(adi->info.name, dev_name, name_size); } /* Check the number of playback channels */ adi->info.output_count = (pb_result>=0) ? 1 : 0; /* Check the number of capture channels */ adi->info.input_count = (ca_result>=0) ? 1 : 0; /* Set the default sample rate */ adi->info.default_samples_per_sec = 8000; /* Driver name */ strcpy(adi->info.driver, "ALSA"); ++af->dev_cnt; PJ_LOG (5,(THIS_FILE, "Added sound device %s", adi->alsa_name)); return PJ_SUCCESS; } /* Create ALSA audio driver. */ pjmedia_aud_dev_factory* pjmedia_alsa_factory(pj_pool_factory *pf) { struct alsa_factory *af; pj_pool_t *pool; pool = pj_pool_create(pf, "alsa_aud_base", 256, 256, NULL); af = PJ_POOL_ZALLOC_T(pool, struct alsa_factory); af->pf = pf; af->base_pool = pool; af->base.op = &alsa_factory_op; return &af->base; } /* API: init factory */ static pj_status_t alsa_factory_init(pjmedia_aud_dev_factory *f) { pj_status_t status = alsa_factory_refresh(f); if (PJ_SUCCESS != status) return status; PJ_LOG(4,(THIS_FILE, "ALSA initialized")); return PJ_SUCCESS; } /* API: destroy factory */ static pj_status_t alsa_factory_destroy(pjmedia_aud_dev_factory *f) { struct alsa_factory *af = (struct alsa_factory*)f; if (af->pool) pj_pool_release(af->pool); if (af->base_pool) { pj_pool_t *pool = af->base_pool; af->base_pool = NULL; pj_pool_release(pool); } /* Restore handler */ snd_lib_error_set_handler(NULL); return PJ_SUCCESS; } /* API: refresh the device list */ static pj_status_t alsa_factory_refresh(pjmedia_aud_dev_factory *f) { struct alsa_factory *af = (struct alsa_factory*)f; char **hints, **n; int err; TRACE_((THIS_FILE, "pjmedia_snd_init: Enumerate sound devices")); if (af->pool != NULL) { pj_pool_release(af->pool); af->pool = NULL; } af->pool = pj_pool_create(af->pf, "alsa_aud", 256, 256, NULL); af->dev_cnt = 0; /* Enumerate sound devices */ err = snd_device_name_hint(-1, "pcm", (void***)&hints); if (err != 0) return PJMEDIA_EAUD_SYSERR; /* Set a null error handler prior to enumeration to suppress errors */ snd_lib_error_set_handler(null_alsa_error_handler); n = hints; while (*n != NULL) { char *name = snd_device_name_get_hint(*n, "NAME"); char *desc = snd_device_name_get_hint(*n, "DESC"); if (name != NULL) { if (strncmp("null", name, 4) == 0 || strncmp("front", name, 5) == 0 || strncmp("rear", name, 4) == 0 || strncmp("side", name, 4) == 0 || strncmp("dmix", name, 4) == 0 || strncmp("dsnoop", name, 6) == 0 || strncmp("hw", name, 2) == 0 || strncmp("plughw", name, 6) == 0 || strncmp("center_lfe", name, 10) == 0 || strncmp("surround", name, 8) == 0 || (strncmp("default", name, 7) == 0 && strstr(name, ":CARD=") != NULL)) { /* skip these devices, 'sysdefault' always contains the relevant information */ ; } else { add_dev(af, name, desc); } free(name); free(desc); } n++; } /* Install error handler after enumeration, otherwise we'll get many * error messages about invalid card/device ID. */ snd_lib_error_set_handler(alsa_error_handler); err = snd_device_name_free_hint((void**)hints); PJ_LOG(4,(THIS_FILE, "ALSA driver found %d devices", af->dev_cnt)); return PJ_SUCCESS; } /* API: get device count */ static unsigned alsa_factory_get_dev_count(pjmedia_aud_dev_factory *f) { struct alsa_factory *af = (struct alsa_factory*)f; return af->dev_cnt; } /* API: get device info */ static pj_status_t alsa_factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_dev_info *info) { struct alsa_factory *af = (struct alsa_factory*)f; PJ_ASSERT_RETURN(index>=0 && indexdev_cnt, PJ_EINVAL); pj_memcpy(info, &af->devs[index].info, sizeof(*info)); info->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; return PJ_SUCCESS; } /* API: create default parameter */ static pj_status_t alsa_factory_default_param(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_param *param) { struct alsa_factory *af = (struct alsa_factory*)f; struct alsa_dev_info *adi; PJ_ASSERT_RETURN(index>=0 && indexdev_cnt, PJ_EINVAL); adi = &af->devs[index]; pj_bzero(param, sizeof(*param)); if (adi->info.input_count && adi->info.output_count) { param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; param->rec_id = index; param->play_id = index; } else if (adi->info.input_count) { param->dir = PJMEDIA_DIR_CAPTURE; param->rec_id = index; param->play_id = PJMEDIA_AUD_INVALID_DEV; } else if (adi->info.output_count) { param->dir = PJMEDIA_DIR_PLAYBACK; param->play_id = index; param->rec_id = PJMEDIA_AUD_INVALID_DEV; } else { return PJMEDIA_EAUD_INVDEV; } param->clock_rate = adi->info.default_samples_per_sec; param->channel_count = 1; param->samples_per_frame = adi->info.default_samples_per_sec * 20 / 1000; param->bits_per_sample = 16; param->flags = adi->info.caps; param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY; param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY; return PJ_SUCCESS; } static int pb_thread_func (void *arg) { struct alsa_stream* stream = (struct alsa_stream*) arg; snd_pcm_t* pcm = stream->pb_pcm; int size = stream->pb_buf_size; snd_pcm_uframes_t nframes = stream->pb_frames; void* user_data = stream->user_data; char* buf = stream->pb_buf; pj_timestamp tstamp; int result; pj_bzero (buf, size); tstamp.u64 = 0; TRACE_((THIS_FILE, "pb_thread_func(%u): Started", (unsigned)syscall(SYS_gettid))); snd_pcm_prepare (pcm); while (!stream->quit) { pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = buf; frame.size = size; frame.timestamp.u64 = tstamp.u64; frame.bit_info = 0; result = stream->pb_cb (user_data, &frame); if (result != PJ_SUCCESS || stream->quit) break; if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero (buf, size); result = snd_pcm_writei (pcm, buf, nframes); if (result == -EPIPE) { PJ_LOG (4,(THIS_FILE, "pb_thread_func: underrun!")); snd_pcm_prepare (pcm); } else if (result < 0) { PJ_LOG (4,(THIS_FILE, "pb_thread_func: error writing data!")); } tstamp.u64 += nframes; } snd_pcm_drain (pcm); TRACE_((THIS_FILE, "pb_thread_func: Stopped")); return PJ_SUCCESS; } static int ca_thread_func (void *arg) { struct alsa_stream* stream = (struct alsa_stream*) arg; snd_pcm_t* pcm = stream->ca_pcm; int size = stream->ca_buf_size; snd_pcm_uframes_t nframes = stream->ca_frames; void* user_data = stream->user_data; char* buf = stream->ca_buf; pj_timestamp tstamp; int result; struct sched_param param; pthread_t* thid; thid = (pthread_t*) pj_thread_get_os_handle (pj_thread_this()); param.sched_priority = sched_get_priority_max (SCHED_RR); PJ_LOG (5,(THIS_FILE, "ca_thread_func(%u): Set thread priority " "for audio capture thread.", (unsigned)syscall(SYS_gettid))); result = pthread_setschedparam (*thid, SCHED_RR, ¶m); if (result) { if (result == EPERM) PJ_LOG (5,(THIS_FILE, "Unable to increase thread priority, " "root access needed.")); else PJ_LOG (5,(THIS_FILE, "Unable to increase thread priority, " "error: %d", result)); } pj_bzero (buf, size); tstamp.u64 = 0; TRACE_((THIS_FILE, "ca_thread_func(%u): Started", (unsigned)syscall(SYS_gettid))); snd_pcm_prepare (pcm); while (!stream->quit) { pjmedia_frame frame; pj_bzero (buf, size); result = snd_pcm_readi (pcm, buf, nframes); if (result == -EPIPE) { PJ_LOG (4,(THIS_FILE, "ca_thread_func: overrun!")); snd_pcm_prepare (pcm); continue; } else if (result < 0) { PJ_LOG (4,(THIS_FILE, "ca_thread_func: error reading data!")); } if (stream->quit) break; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = (void*) buf; frame.size = size; frame.timestamp.u64 = tstamp.u64; frame.bit_info = 0; result = stream->ca_cb (user_data, &frame); if (result != PJ_SUCCESS || stream->quit) break; tstamp.u64 += nframes; } snd_pcm_drop (pcm); TRACE_((THIS_FILE, "ca_thread_func: Stopped")); return PJ_SUCCESS; } static pj_status_t open_playback (struct alsa_stream* stream, const pjmedia_aud_param *param) { snd_pcm_hw_params_t* params; snd_pcm_format_t format; int result; unsigned int rate; snd_pcm_uframes_t tmp_buf_size; snd_pcm_uframes_t tmp_period_size; if (param->play_id < 0 || param->play_id >= stream->af->dev_cnt) return PJMEDIA_EAUD_INVDEV; /* Open PCM for playback */ PJ_LOG (5,(THIS_FILE, "open_playback: Open playback device '%s'", stream->af->devs[param->play_id].alsa_name)); result = snd_pcm_open (&stream->pb_pcm, stream->af->devs[param->play_id].alsa_name, SND_PCM_STREAM_PLAYBACK, 0); if (result < 0) return PJMEDIA_EAUD_SYSERR; /* Allocate a hardware parameters object. */ snd_pcm_hw_params_alloca (¶ms); /* Fill it in with default values. */ snd_pcm_hw_params_any (stream->pb_pcm, params); /* Set interleaved mode */ snd_pcm_hw_params_set_access (stream->pb_pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED); /* Set format */ switch (param->bits_per_sample) { case 8: TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S8")); format = SND_PCM_FORMAT_S8; break; case 16: TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S16_LE")); format = SND_PCM_FORMAT_S16_LE; break; case 24: TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S24_LE")); format = SND_PCM_FORMAT_S24_LE; break; case 32: TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S32_LE")); format = SND_PCM_FORMAT_S32_LE; break; default: TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S16_LE")); format = SND_PCM_FORMAT_S16_LE; break; } snd_pcm_hw_params_set_format (stream->pb_pcm, params, format); /* Set number of channels */ TRACE_((THIS_FILE, "open_playback: set channels: %d", param->channel_count)); snd_pcm_hw_params_set_channels (stream->pb_pcm, params, param->channel_count); /* Set clock rate */ rate = param->clock_rate; TRACE_((THIS_FILE, "open_playback: set clock rate: %d", rate)); snd_pcm_hw_params_set_rate_near (stream->pb_pcm, params, &rate, NULL); TRACE_((THIS_FILE, "open_playback: clock rate set to: %d", rate)); /* Set period size to samples_per_frame frames. */ stream->pb_frames = (snd_pcm_uframes_t) param->samples_per_frame / param->channel_count; TRACE_((THIS_FILE, "open_playback: set period size: %d", stream->pb_frames)); tmp_period_size = stream->pb_frames; snd_pcm_hw_params_set_period_size_near (stream->pb_pcm, params, &tmp_period_size, NULL); TRACE_((THIS_FILE, "open_playback: period size set to: %d", tmp_period_size)); /* Set the sound device buffer size and latency */ if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY) tmp_buf_size = (rate / 1000) * param->output_latency_ms; else tmp_buf_size = (rate / 1000) * PJMEDIA_SND_DEFAULT_PLAY_LATENCY; snd_pcm_hw_params_set_buffer_size_near (stream->pb_pcm, params, &tmp_buf_size); stream->param.output_latency_ms = tmp_buf_size / (rate / 1000); /* Set our buffer */ stream->pb_buf_size = stream->pb_frames * param->channel_count * (param->bits_per_sample/8); stream->pb_buf = (char*) pj_pool_alloc(stream->pool, stream->pb_buf_size); TRACE_((THIS_FILE, "open_playback: buffer size set to: %d", (int)tmp_buf_size)); TRACE_((THIS_FILE, "open_playback: playback_latency set to: %d ms", (int)stream->param.output_latency_ms)); /* Activate the parameters */ result = snd_pcm_hw_params (stream->pb_pcm, params); if (result < 0) { snd_pcm_close (stream->pb_pcm); return PJMEDIA_EAUD_SYSERR; } PJ_LOG (5,(THIS_FILE, "Opened device alsa(%s) for playing, sample rate=%d" ", ch=%d, bits=%d, period size=%d frames, latency=%d ms", stream->af->devs[param->play_id].alsa_name, rate, param->channel_count, param->bits_per_sample, stream->pb_frames, (int)stream->param.output_latency_ms)); return PJ_SUCCESS; } static pj_status_t open_capture (struct alsa_stream* stream, const pjmedia_aud_param *param) { snd_pcm_hw_params_t* params; snd_pcm_format_t format; int result; unsigned int rate; snd_pcm_uframes_t tmp_buf_size; snd_pcm_uframes_t tmp_period_size; if (param->rec_id < 0 || param->rec_id >= stream->af->dev_cnt) return PJMEDIA_EAUD_INVDEV; /* Open PCM for capture */ PJ_LOG (5,(THIS_FILE, "open_capture: Open capture device '%s'", stream->af->devs[param->rec_id].alsa_name)); result = snd_pcm_open (&stream->ca_pcm, stream->af->devs[param->rec_id].alsa_name, SND_PCM_STREAM_CAPTURE, 0); if (result < 0) return PJMEDIA_EAUD_SYSERR; /* Allocate a hardware parameters object. */ snd_pcm_hw_params_alloca (¶ms); /* Fill it in with default values. */ snd_pcm_hw_params_any (stream->ca_pcm, params); /* Set interleaved mode */ snd_pcm_hw_params_set_access (stream->ca_pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED); /* Set format */ switch (param->bits_per_sample) { case 8: TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S8")); format = SND_PCM_FORMAT_S8; break; case 16: TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S16_LE")); format = SND_PCM_FORMAT_S16_LE; break; case 24: TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S24_LE")); format = SND_PCM_FORMAT_S24_LE; break; case 32: TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S32_LE")); format = SND_PCM_FORMAT_S32_LE; break; default: TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S16_LE")); format = SND_PCM_FORMAT_S16_LE; break; } snd_pcm_hw_params_set_format (stream->ca_pcm, params, format); /* Set number of channels */ TRACE_((THIS_FILE, "open_capture: set channels: %d", param->channel_count)); snd_pcm_hw_params_set_channels (stream->ca_pcm, params, param->channel_count); /* Set clock rate */ rate = param->clock_rate; TRACE_((THIS_FILE, "open_capture: set clock rate: %d", rate)); snd_pcm_hw_params_set_rate_near (stream->ca_pcm, params, &rate, NULL); TRACE_((THIS_FILE, "open_capture: clock rate set to: %d", rate)); /* Set period size to samples_per_frame frames. */ stream->ca_frames = (snd_pcm_uframes_t) param->samples_per_frame / param->channel_count; TRACE_((THIS_FILE, "open_capture: set period size: %d", stream->ca_frames)); tmp_period_size = stream->ca_frames; snd_pcm_hw_params_set_period_size_near (stream->ca_pcm, params, &tmp_period_size, NULL); TRACE_((THIS_FILE, "open_capture: period size set to: %d", tmp_period_size)); /* Set the sound device buffer size and latency */ if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY) tmp_buf_size = (rate / 1000) * param->input_latency_ms; else tmp_buf_size = (rate / 1000) * PJMEDIA_SND_DEFAULT_REC_LATENCY; snd_pcm_hw_params_set_buffer_size_near (stream->ca_pcm, params, &tmp_buf_size); stream->param.input_latency_ms = tmp_buf_size / (rate / 1000); /* Set our buffer */ stream->ca_buf_size = stream->ca_frames * param->channel_count * (param->bits_per_sample/8); stream->ca_buf = (char*) pj_pool_alloc (stream->pool, stream->ca_buf_size); TRACE_((THIS_FILE, "open_capture: buffer size set to: %d", (int)tmp_buf_size)); TRACE_((THIS_FILE, "open_capture: capture_latency set to: %d ms", (int)stream->param.input_latency_ms)); /* Activate the parameters */ result = snd_pcm_hw_params (stream->ca_pcm, params); if (result < 0) { snd_pcm_close (stream->ca_pcm); return PJMEDIA_EAUD_SYSERR; } PJ_LOG (5,(THIS_FILE, "Opened device alsa(%s) for capture, sample rate=%d" ", ch=%d, bits=%d, period size=%d frames, latency=%d ms", stream->af->devs[param->rec_id].alsa_name, rate, param->channel_count, param->bits_per_sample, stream->ca_frames, (int)stream->param.input_latency_ms)); return PJ_SUCCESS; } /* API: create stream */ static pj_status_t alsa_factory_create_stream(pjmedia_aud_dev_factory *f, const pjmedia_aud_param *param, pjmedia_aud_rec_cb rec_cb, pjmedia_aud_play_cb play_cb, void *user_data, pjmedia_aud_stream **p_strm) { struct alsa_factory *af = (struct alsa_factory*)f; pj_status_t status; pj_pool_t* pool; struct alsa_stream* stream; pool = pj_pool_create (af->pf, "alsa%p", 1024, 1024, NULL); if (!pool) return PJ_ENOMEM; /* Allocate and initialize comon stream data */ stream = PJ_POOL_ZALLOC_T (pool, struct alsa_stream); stream->base.op = &alsa_stream_op; stream->pool = pool; stream->af = af; stream->user_data = user_data; stream->pb_cb = play_cb; stream->ca_cb = rec_cb; stream->quit = 0; pj_memcpy(&stream->param, param, sizeof(*param)); /* Init playback */ if (param->dir & PJMEDIA_DIR_PLAYBACK) { status = open_playback (stream, param); if (status != PJ_SUCCESS) { pj_pool_release (pool); return status; } } /* Init capture */ if (param->dir & PJMEDIA_DIR_CAPTURE) { status = open_capture (stream, param); if (status != PJ_SUCCESS) { if (param->dir & PJMEDIA_DIR_PLAYBACK) snd_pcm_close (stream->pb_pcm); pj_pool_release (pool); return status; } } *p_strm = &stream->base; return PJ_SUCCESS; } /* API: set audio device change observer */ static void alsa_factory_set_observer(pjmedia_aud_dev_factory *f, pjmedia_aud_dev_change_callback cb) { PJ_UNUSED_ARG(f); PJ_UNUSED_ARG(cb); } /* API: get default recording device */ static int alsa_factory_get_default_rec_dev(pjmedia_aud_dev_factory *f) { PJ_UNUSED_ARG(f); return -1; } /* API: get default playback device */ static int alsa_factory_get_default_play_dev(pjmedia_aud_dev_factory *f) { PJ_UNUSED_ARG(f); return -1; } /* API: get running parameter */ static pj_status_t alsa_stream_get_param(pjmedia_aud_stream *s, pjmedia_aud_param *pi) { struct alsa_stream *stream = (struct alsa_stream*)s; PJ_ASSERT_RETURN(s && pi, PJ_EINVAL); pj_memcpy(pi, &stream->param, sizeof(*pi)); return PJ_SUCCESS; } /* API: get capability */ static pj_status_t alsa_stream_get_cap(pjmedia_aud_stream *s, pjmedia_aud_dev_cap cap, void *pval) { struct alsa_stream *stream = (struct alsa_stream*)s; PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY && (stream->param.dir & PJMEDIA_DIR_CAPTURE)) { /* Recording latency */ *(unsigned*)pval = stream->param.input_latency_ms; return PJ_SUCCESS; } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && (stream->param.dir & PJMEDIA_DIR_PLAYBACK)) { /* Playback latency */ *(unsigned*)pval = stream->param.output_latency_ms; return PJ_SUCCESS; } else { return PJMEDIA_EAUD_INVCAP; } } /* API: set capability */ static pj_status_t alsa_stream_set_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, const void *value) { PJ_UNUSED_ARG(strm); PJ_UNUSED_ARG(cap); PJ_UNUSED_ARG(value); return PJMEDIA_EAUD_INVCAP; } /* API: start stream */ static pj_status_t alsa_stream_start (pjmedia_aud_stream *s) { struct alsa_stream *stream = (struct alsa_stream*)s; pj_status_t status = PJ_SUCCESS; stream->quit = 0; if (stream->param.dir & PJMEDIA_DIR_PLAYBACK) { status = pj_thread_create (stream->pool, "alsasound_playback", pb_thread_func, stream, 0, //ZERO, 0, &stream->pb_thread); if (status != PJ_SUCCESS) return status; } if (stream->param.dir & PJMEDIA_DIR_CAPTURE) { status = pj_thread_create (stream->pool, "alsasound_playback", ca_thread_func, stream, 0, //ZERO, 0, &stream->ca_thread); if (status != PJ_SUCCESS) { stream->quit = PJ_TRUE; pj_thread_join(stream->pb_thread); pj_thread_destroy(stream->pb_thread); stream->pb_thread = NULL; } } return status; } /* API: stop stream */ static pj_status_t alsa_stream_stop (pjmedia_aud_stream *s) { struct alsa_stream *stream = (struct alsa_stream*)s; stream->quit = 1; if (stream->pb_thread) { TRACE_((THIS_FILE, "alsa_stream_stop(%u): Waiting for playback to stop.", (unsigned)syscall(SYS_gettid))); pj_thread_join (stream->pb_thread); TRACE_((THIS_FILE, "alsa_stream_stop(%u): playback stopped.", (unsigned)syscall(SYS_gettid))); pj_thread_destroy(stream->pb_thread); stream->pb_thread = NULL; } if (stream->ca_thread) { TRACE_((THIS_FILE, "alsa_stream_stop(%u): Waiting for capture to stop.", (unsigned)syscall(SYS_gettid))); pj_thread_join (stream->ca_thread); TRACE_((THIS_FILE, "alsa_stream_stop(%u): capture stopped.", (unsigned)syscall(SYS_gettid))); pj_thread_destroy(stream->ca_thread); stream->ca_thread = NULL; } return PJ_SUCCESS; } static pj_status_t alsa_stream_destroy (pjmedia_aud_stream *s) { struct alsa_stream *stream = (struct alsa_stream*)s; alsa_stream_stop (s); if (stream->param.dir & PJMEDIA_DIR_PLAYBACK) { snd_pcm_close (stream->pb_pcm); stream->pb_pcm = NULL; } if (stream->param.dir & PJMEDIA_DIR_CAPTURE) { snd_pcm_close (stream->ca_pcm); stream->ca_pcm = NULL; } pj_pool_release (stream->pool); return PJ_SUCCESS; } #endif /* PJMEDIA_AUDIO_DEV_HAS_ALSA */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-audiodev/audiodev.c ================================================ /* $Id: audiodev.c 4435 2013-03-11 06:32:58Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #define THIS_FILE "audiodev.c" #define DEFINE_CAP(name, info) {name, info} /* Capability names */ static struct cap_info { const char *name; const char *info; } cap_infos[] = { DEFINE_CAP("ext-fmt", "Extended/non-PCM format"), DEFINE_CAP("latency-in", "Input latency/buffer size setting"), DEFINE_CAP("latency-out", "Output latency/buffer size setting"), DEFINE_CAP("vol-in", "Input volume setting"), DEFINE_CAP("vol-out", "Output volume setting"), DEFINE_CAP("meter-in", "Input meter"), DEFINE_CAP("meter-out", "Output meter"), DEFINE_CAP("route-in", "Input routing"), DEFINE_CAP("route-out", "Output routing"), DEFINE_CAP("aec", "Accoustic echo cancellation"), DEFINE_CAP("aec-tail", "Tail length setting for AEC"), DEFINE_CAP("vad", "Voice activity detection"), DEFINE_CAP("cng", "Comfort noise generation"), DEFINE_CAP("plg", "Packet loss concealment") }; /* * The device index seen by application and driver is different. * * At application level, device index is index to global list of device. * At driver level, device index is index to device list on that particular * factory only. */ #define MAKE_DEV_ID(f_id, index) (((f_id & 0xFFFF) << 16) | (index & 0xFFFF)) #define GET_INDEX(dev_id) ((dev_id) & 0xFFFF) #define GET_FID(dev_id) ((dev_id) >> 16) #define DEFAULT_DEV_ID 0 #if PJMEDIA_AUDIO_DEV_HAS_COREAUDIO pjmedia_aud_dev_factory* pjmedia_coreaudio_factory(pj_pool_factory *pf); #endif #if PJMEDIA_AUDIO_DEV_HAS_ALSA pjmedia_aud_dev_factory* pjmedia_alsa_factory(pj_pool_factory *pf); #endif #if PJMEDIA_AUDIO_DEV_HAS_OPENSL pjmedia_aud_dev_factory* pjmedia_opensl_factory(pj_pool_factory *pf); #endif #if PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI pjmedia_aud_dev_factory* pjmedia_android_factory(pj_pool_factory *pf); #endif #if PJMEDIA_AUDIO_DEV_HAS_BB10 pjmedia_aud_dev_factory* pjmedia_bb10_factory(pj_pool_factory *pf); #endif #if PJMEDIA_AUDIO_DEV_HAS_WMME pjmedia_aud_dev_factory* pjmedia_wmme_factory(pj_pool_factory *pf); #endif #if PJMEDIA_AUDIO_DEV_HAS_BDIMAD pjmedia_aud_dev_factory* pjmedia_bdimad_factory(pj_pool_factory *pf); #endif #if PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS pjmedia_aud_dev_factory* pjmedia_symb_vas_factory(pj_pool_factory *pf); #endif #if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS pjmedia_aud_dev_factory* pjmedia_aps_factory(pj_pool_factory *pf); #endif #if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA pjmedia_aud_dev_factory* pjmedia_symb_mda_factory(pj_pool_factory *pf); #endif #if PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO pjmedia_aud_dev_factory* pjmedia_null_audio_factory(pj_pool_factory *pf); #endif #define MAX_DRIVERS 16 #define MAX_DEVS 64 /* driver structure */ struct driver { /* Creation function */ pjmedia_aud_dev_factory_create_func_ptr create; /* Factory instance */ pjmedia_aud_dev_factory *f; char name[32]; /* Driver name */ unsigned dev_cnt; /* Number of devices */ unsigned start_idx; /* Start index in global list */ int rec_dev_idx;/* Default capture device. */ int play_dev_idx;/* Default playback device */ }; /* The audio subsystem */ static struct aud_subsys { unsigned init_count; /* How many times init() is called */ pj_pool_factory *pf; /* The pool factory. */ unsigned drv_cnt; /* Number of drivers. */ struct driver drv[MAX_DRIVERS]; /* Array of drivers. */ unsigned dev_cnt; /* Total number of devices. */ pj_uint32_t dev_list[MAX_DEVS];/* Array of device IDs. */ pjmedia_aud_dev_observer dev_observer; } aud_subsys; /* callback for device change operations */ static void process_aud_dev_change_event(pjmedia_aud_dev_change_event event) { pj_status_t status; if (!pj_thread_is_registered()) { status = pj_thread_register("aud_dev_observer", aud_subsys.dev_observer.thread_desc, &aud_subsys.dev_observer.thread); if (status != PJ_SUCCESS) { return; } PJ_LOG(5, (THIS_FILE, "Audio device change thread registered")); } status = pj_mutex_lock(aud_subsys.dev_observer.lock); if (status != PJ_SUCCESS) { PJ_LOG(5, (THIS_FILE, "Could not acquire audio device change lock")); return; } if (!aud_subsys.dev_observer.cb) { /* there is no registered callback to call */ goto end; } switch(event) { case DEFAULT_INPUT_CHANGED: PJ_LOG(5, (THIS_FILE, "Default input device changed")); pjmedia_aud_dev_refresh(); (*aud_subsys.dev_observer.cb)(PJMEDIA_AUD_DEV_DEFAULT_INPUT_CHANGED); break; case DEFAULT_OUTPUT_CHANGED: PJ_LOG(5, (THIS_FILE, "Default output device changed")); pjmedia_aud_dev_refresh(); (*aud_subsys.dev_observer.cb)(PJMEDIA_AUD_DEV_DEFAULT_OUTPUT_CHANGED); break; case DEVICE_LIST_CHANGED: PJ_LOG(5, (THIS_FILE, "Device list changed")); (*aud_subsys.dev_observer.cb)(PJMEDIA_AUD_DEV_LIST_WILL_REFRESH); pjmedia_aud_dev_refresh(); (*aud_subsys.dev_observer.cb)(PJMEDIA_AUD_DEV_LIST_DID_REFRESH); break; default: PJ_LOG(5, (THIS_FILE, "Unknown event: %d", event)); break; } end: status = pj_mutex_unlock(aud_subsys.dev_observer.lock); if (status != PJ_SUCCESS) { PJ_LOG(5, (THIS_FILE, "Could not release audio device change lock")); } } /* API: get capability name/info */ PJ_DEF(const char*) pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap, const char **p_desc) { const char *desc; unsigned i; if (p_desc==NULL) p_desc = &desc; for (i=0; iname; \ *size = sizeof(param->name) switch (cap) { case PJMEDIA_AUD_DEV_CAP_EXT_FORMAT: FIELD_INFO(ext_fmt); break; case PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY: FIELD_INFO(input_latency_ms); break; case PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY: FIELD_INFO(output_latency_ms); break; case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING: FIELD_INFO(input_vol); break; case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING: FIELD_INFO(output_vol); break; case PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE: FIELD_INFO(input_route); break; case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE: FIELD_INFO(output_route); break; case PJMEDIA_AUD_DEV_CAP_EC: FIELD_INFO(ec_enabled); break; case PJMEDIA_AUD_DEV_CAP_EC_TAIL: FIELD_INFO(ec_tail_ms); break; /* vad is no longer in "fmt" in 2.0. case PJMEDIA_AUD_DEV_CAP_VAD: FIELD_INFO(ext_fmt.vad); break; */ case PJMEDIA_AUD_DEV_CAP_CNG: FIELD_INFO(cng_enabled); break; case PJMEDIA_AUD_DEV_CAP_PLC: FIELD_INFO(plc_enabled); break; default: return PJMEDIA_EAUD_INVCAP; } #undef FIELD_INFO return PJ_SUCCESS; } /* API: set cap value to param */ PJ_DEF(pj_status_t) pjmedia_aud_param_set_cap( pjmedia_aud_param *param, pjmedia_aud_dev_cap cap, const void *pval) { void *cap_ptr; unsigned cap_size; pj_status_t status; status = get_cap_pointer(param, cap, &cap_ptr, &cap_size); if (status != PJ_SUCCESS) return status; pj_memcpy(cap_ptr, pval, cap_size); param->flags |= cap; return PJ_SUCCESS; } /* API: get cap value from param */ PJ_DEF(pj_status_t) pjmedia_aud_param_get_cap( const pjmedia_aud_param *param, pjmedia_aud_dev_cap cap, void *pval) { void *cap_ptr; unsigned cap_size; pj_status_t status; status = get_cap_pointer(param, cap, &cap_ptr, &cap_size); if (status != PJ_SUCCESS) return status; if ((param->flags & cap) == 0) { pj_bzero(cap_ptr, cap_size); return PJMEDIA_EAUD_INVCAP; } pj_memcpy(pval, cap_ptr, cap_size); return PJ_SUCCESS; } /* Internal: init driver */ static pj_status_t init_driver(unsigned drv_idx, pj_bool_t refresh) { struct driver *drv = &aud_subsys.drv[drv_idx]; pjmedia_aud_dev_factory *f; unsigned i, dev_cnt; pj_status_t status; if (!refresh && drv->create) { /* Create the factory */ f = (*drv->create)(aud_subsys.pf); if (!f) return PJ_EUNKNOWN; /* Call factory->init() */ status = f->op->init(f); if (status != PJ_SUCCESS) { f->op->destroy(f); return status; } } else { f = drv->f; } /* Register device change observer */ if (!refresh) { f->op->set_dev_change_cb(f, &process_aud_dev_change_event); } if (!f) return PJ_EUNKNOWN; /* Get number of devices */ dev_cnt = f->op->get_dev_count(f); if (dev_cnt + aud_subsys.dev_cnt > MAX_DEVS) { PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because" " there are too many devices", aud_subsys.dev_cnt + dev_cnt - MAX_DEVS)); dev_cnt = MAX_DEVS - aud_subsys.dev_cnt; } /* enabling this will cause pjsua-lib initialization to fail when there * is no sound device installed in the system, even when pjsua has been * run with --null-audio * if (dev_cnt == 0) { f->op->destroy(f); return PJMEDIA_EAUD_NODEV; } */ /* Fill in default devices */ drv->rec_dev_idx = f->op->get_default_rec_dev(f); drv->play_dev_idx = f->op->get_default_play_dev(f); for (i=0; iop->get_dev_info(f, i, &info); if (status != PJ_SUCCESS) { f->op->destroy(f); return status; } if (drv->name[0]=='\0') { /* Set driver name */ pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name)); drv->name[sizeof(drv->name)-1] = '\0'; } if (drv->play_dev_idx < 0 && info.output_count) { /* Set default playback device */ drv->play_dev_idx = i; } if (drv->rec_dev_idx < 0 && info.input_count) { /* Set default capture device */ drv->rec_dev_idx = i; } if (drv->play_dev_idx >= 0 && drv->rec_dev_idx >= 0) { /* Done. */ break; } } /* Register the factory */ drv->f = f; drv->f->sys.drv_idx = drv_idx; drv->start_idx = aud_subsys.dev_cnt; drv->dev_cnt = dev_cnt; /* Register devices to global list */ for (i=0; if) { drv->f->op->set_dev_change_cb(drv->f, NULL); drv->f->op->destroy(drv->f); drv->f = NULL; } pj_bzero(drv, sizeof(*drv)); drv->play_dev_idx = drv->rec_dev_idx = PJMEDIA_AUD_INVALID_DEV; } /* API: Initialize the audio subsystem. */ PJ_DEF(pj_status_t) pjmedia_aud_subsys_init(pj_pool_factory *pf) { unsigned i; pj_status_t status; /* Allow init() to be called multiple times as long as there is matching * number of shutdown(). */ if (aud_subsys.init_count++ != 0) { return PJ_SUCCESS; } /* Register error subsystem */ status = pj_register_strerror(PJMEDIA_AUDIODEV_ERRNO_START, PJ_ERRNO_SPACE_SIZE, &pjmedia_audiodev_strerror); pj_assert(status == PJ_SUCCESS); /* Init */ aud_subsys.pf = pf; aud_subsys.drv_cnt = 0; aud_subsys.dev_cnt = 0; /* Register creation functions */ #if PJMEDIA_AUDIO_DEV_HAS_OPENSL aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_opensl_factory; #endif #if PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_android_factory; #endif #if PJMEDIA_AUDIO_DEV_HAS_BB10 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_bb10_factory; #endif #if PJMEDIA_AUDIO_DEV_HAS_ALSA aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_alsa_factory; #endif #if PJMEDIA_AUDIO_DEV_HAS_COREAUDIO aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_coreaudio_factory; #endif #if PJMEDIA_AUDIO_DEV_HAS_WMME aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_wmme_factory; #endif #if PJMEDIA_AUDIO_DEV_HAS_BDIMAD aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_bdimad_factory; #endif #if PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_symb_vas_factory; #endif #if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_aps_factory; #endif #if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_symb_mda_factory; #endif #if PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_null_audio_factory; #endif /* Initialize audio device observer objects */ pj_status_t st; aud_subsys.dev_observer.pool = pj_pool_create(pf, "aud_dev_observer_pool", 512, 512, NULL); if (!aud_subsys.dev_observer.pool) { return PJ_ENOMEM; } st = pj_mutex_create_simple(aud_subsys.dev_observer.pool, "aud_dev_observer_lock", &aud_subsys.dev_observer.lock); if (st != PJ_SUCCESS) { return st; } aud_subsys.dev_observer.cb = NULL; /* Initialize each factory and build the device ID list */ for (i=0; icreate == adf) { for (j = drv->start_idx; j < drv->start_idx + drv->dev_cnt; j++) { aud_subsys.dev_list[j] = (pj_uint32_t)PJMEDIA_AUD_INVALID_DEV; } deinit_driver(i); return PJ_SUCCESS; } } return PJMEDIA_EAUD_ERR; } /* API: get the pool factory registered to the audio subsystem. */ PJ_DEF(pj_pool_factory*) pjmedia_aud_subsys_get_pool_factory(void) { return aud_subsys.pf; } /* API: Shutdown the audio subsystem. */ PJ_DEF(pj_status_t) pjmedia_aud_subsys_shutdown(void) { unsigned i; /* Allow shutdown() to be called multiple times as long as there is matching * number of init(). */ if (aud_subsys.init_count == 0) { return PJ_SUCCESS; } --aud_subsys.init_count; if (aud_subsys.init_count == 0) { for (i=0; if && drv->f->op->refresh) { pj_status_t status = drv->f->op->refresh(drv->f); if (status != PJ_SUCCESS) { PJ_PERROR(4, (THIS_FILE, status, "Unable to refresh device " "list for %s", drv->name)); } } init_driver(i, PJ_TRUE); } return PJ_SUCCESS; } /* API: Get the number of sound devices installed in the system. */ PJ_DEF(unsigned) pjmedia_aud_dev_count(void) { return aud_subsys.dev_cnt; } /* Internal: convert local index to global device index */ static pj_status_t make_global_index(unsigned drv_idx, pjmedia_aud_dev_index *id) { if (*id < 0) { return PJ_SUCCESS; } /* Check that factory still exists */ PJ_ASSERT_RETURN(aud_subsys.drv[drv_idx].f, PJ_EBUG); /* Check that device index is valid */ PJ_ASSERT_RETURN(*id>=0 && *id<(int)aud_subsys.drv[drv_idx].dev_cnt, PJ_EBUG); *id += aud_subsys.drv[drv_idx].start_idx; return PJ_SUCCESS; } /* Internal: lookup device id */ static pj_status_t lookup_dev(pjmedia_aud_dev_index id, pjmedia_aud_dev_factory **p_f, unsigned *p_local_index) { int f_id, index; if (id < 0) { unsigned i; if (id == PJMEDIA_AUD_INVALID_DEV) return PJMEDIA_EAUD_INVDEV; for (i=0; irec_dev_idx >= 0) { id = drv->rec_dev_idx; make_global_index(i, &id); break; } else if (id==PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV && drv->play_dev_idx >= 0) { id = drv->play_dev_idx; make_global_index(i, &id); break; } } if (id < 0) { return PJMEDIA_EAUD_NODEFDEV; } } f_id = GET_FID(aud_subsys.dev_list[id]); index = GET_INDEX(aud_subsys.dev_list[id]); if (f_id < 0 || f_id >= (int)aud_subsys.drv_cnt) return PJMEDIA_EAUD_INVDEV; if (index < 0 || index >= (int)aud_subsys.drv[f_id].dev_cnt) return PJMEDIA_EAUD_INVDEV; *p_f = aud_subsys.drv[f_id].f; *p_local_index = (unsigned)index; return PJ_SUCCESS; } /* API: Get device information. */ PJ_DEF(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id, pjmedia_aud_dev_info *info) { pjmedia_aud_dev_factory *f; unsigned index; pj_status_t status; PJ_ASSERT_RETURN(info && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL); PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT); status = lookup_dev(id, &f, &index); if (status != PJ_SUCCESS) return status; return f->op->get_dev_info(f, index, info); } /* API: find device */ PJ_DEF(pj_status_t) pjmedia_aud_dev_lookup( const char *drv_name, const char *dev_name, pjmedia_aud_dev_index *id) { pjmedia_aud_dev_factory *f = NULL; unsigned drv_idx, dev_idx; PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL); PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT); for (drv_idx=0; drv_idxop->get_dev_info(f, dev_idx, &info); if (status != PJ_SUCCESS) return status; if (!pj_ansi_stricmp(dev_name, info.name)) break; } if (dev_idx==aud_subsys.drv[drv_idx].dev_cnt) return PJ_ENOTFOUND; *id = dev_idx; make_global_index(drv_idx, id); return PJ_SUCCESS; } /* API: Initialize the audio device parameters with default values for the * specified device. */ PJ_DEF(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_index id, pjmedia_aud_param *param) { pjmedia_aud_dev_factory *f; unsigned index; pj_status_t status; PJ_ASSERT_RETURN(param && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL); PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT); status = lookup_dev(id, &f, &index); if (status != PJ_SUCCESS) return status; status = f->op->default_param(f, index, param); if (status != PJ_SUCCESS) return status; /* Normalize device IDs */ make_global_index(f->sys.drv_idx, ¶m->rec_id); make_global_index(f->sys.drv_idx, ¶m->play_id); return PJ_SUCCESS; } /* API: Open audio stream object using the specified parameters. */ PJ_DEF(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_param *prm, pjmedia_aud_rec_cb rec_cb, pjmedia_aud_play_cb play_cb, void *user_data, pjmedia_aud_stream **p_aud_strm) { pjmedia_aud_dev_factory *rec_f=NULL, *play_f=NULL, *f=NULL; pjmedia_aud_param param; pj_status_t status; PJ_ASSERT_RETURN(prm && prm->dir && p_aud_strm, PJ_EINVAL); PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT); PJ_ASSERT_RETURN(prm->dir==PJMEDIA_DIR_CAPTURE || prm->dir==PJMEDIA_DIR_PLAYBACK || prm->dir==PJMEDIA_DIR_CAPTURE_PLAYBACK, PJ_EINVAL); /* Must make copy of param because we're changing device ID */ pj_memcpy(¶m, prm, sizeof(param)); /* Normalize rec_id */ if (param.dir & PJMEDIA_DIR_CAPTURE) { unsigned index; if (param.rec_id < 0) param.rec_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV; status = lookup_dev(param.rec_id, &rec_f, &index); if (status != PJ_SUCCESS) return status; param.rec_id = index; f = rec_f; } /* Normalize play_id */ if (param.dir & PJMEDIA_DIR_PLAYBACK) { unsigned index; if (param.play_id < 0) param.play_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV; status = lookup_dev(param.play_id, &play_f, &index); if (status != PJ_SUCCESS) return status; param.play_id = index; f = play_f; } PJ_ASSERT_RETURN(f != NULL, PJ_EBUG); /* For now, rec_id and play_id must belong to the same factory */ PJ_ASSERT_RETURN((param.dir != PJMEDIA_DIR_CAPTURE_PLAYBACK) || (rec_f == play_f), PJMEDIA_EAUD_INVDEV); /* Create the stream */ status = f->op->create_stream(f, ¶m, rec_cb, play_cb, user_data, p_aud_strm); if (status != PJ_SUCCESS) return status; /* Assign factory id to the stream */ (*p_aud_strm)->sys.drv_idx = f->sys.drv_idx; return PJ_SUCCESS; } /* API: Get the running parameters for the specified audio stream. */ PJ_DEF(pj_status_t) pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm, pjmedia_aud_param *param) { pj_status_t status; PJ_ASSERT_RETURN(strm && param, PJ_EINVAL); PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT); status = strm->op->get_param(strm, param); if (status != PJ_SUCCESS) return status; /* Normalize device id's */ make_global_index(strm->sys.drv_idx, ¶m->rec_id); make_global_index(strm->sys.drv_idx, ¶m->play_id); return PJ_SUCCESS; } /* API: Get the value of a specific capability of the audio stream. */ PJ_DEF(pj_status_t) pjmedia_aud_stream_get_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, void *value) { return strm->op->get_cap(strm, cap, value); } /* API: Set the value of a specific capability of the audio stream. */ PJ_DEF(pj_status_t) pjmedia_aud_stream_set_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, const void *value) { return strm->op->set_cap(strm, cap, value); } /* API: Start the stream. */ PJ_DEF(pj_status_t) pjmedia_aud_stream_start(pjmedia_aud_stream *strm) { return strm->op->start(strm); } /* API: Stop the stream. */ PJ_DEF(pj_status_t) pjmedia_aud_stream_stop(pjmedia_aud_stream *strm) { return strm->op->stop(strm); } /* API: Destroy the stream. */ PJ_DEF(pj_status_t) pjmedia_aud_stream_destroy(pjmedia_aud_stream *strm) { return strm->op->destroy(strm); } /* API: Register device change observer. */ PJ_DEF(pj_status_t) pjmedia_aud_dev_set_observer_cb(pjmedia_aud_dev_observer_callback cb) { pj_status_t status; status = pj_mutex_lock(aud_subsys.dev_observer.lock); if (status != PJ_SUCCESS) { PJ_LOG(5, (THIS_FILE, "Could not acquire audio device change lock")); return status; } aud_subsys.dev_observer.cb = cb; status = pj_mutex_unlock(aud_subsys.dev_observer.lock); if (status != PJ_SUCCESS) { PJ_LOG(5, (THIS_FILE, "Could not release audio device change lock")); } return status; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-audiodev/coreaudio_dev.m ================================================ /* $Id$ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #if PJMEDIA_AUDIO_DEV_HAS_COREAUDIO #include "TargetConditionals.h" #if TARGET_OS_IPHONE #define COREAUDIO_MAC 0 #else #define COREAUDIO_MAC 1 #endif #include #include #if COREAUDIO_MAC #include #else #include #define AudioDeviceID unsigned /** * As in iOS SDK 4 or later, audio route change property listener is * no longer necessary. Just make surethat your application can receive * remote control events by adding the code: * [[UIApplication sharedApplication] * beginReceivingRemoteControlEvents]; * Otherwise audio route change (such as headset plug/unplug) will not be * processed while your application is in the background mode. */ #define USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER 0 /* Starting iOS SDK 7, Audio Session API is deprecated. */ #define USE_AUDIO_SESSION_API 0 #endif /* For Mac OS 10.5.x and earlier */ #if AUDIO_UNIT_VERSION < 1060 #define AudioComponent Component #define AudioComponentDescription ComponentDescription #define AudioComponentInstance ComponentInstance #define AudioComponentFindNext FindNextComponent #define AudioComponentInstanceNew OpenAComponent #define AudioComponentInstanceDispose CloseComponent #endif #define THIS_FILE "coreaudio_dev.c" /* coreaudio device info */ struct coreaudio_dev_info { pjmedia_aud_dev_info info; AudioDeviceID dev_id; }; /* linked list of streams */ struct stream_list { PJ_DECL_LIST_MEMBER(struct stream_list); struct coreaudio_stream *stream; }; /* coreaudio factory */ struct coreaudio_factory { pjmedia_aud_dev_factory base; pj_pool_t *base_pool; pj_pool_t *pool; pj_pool_factory *pf; pj_mutex_t *mutex; unsigned dev_count; struct coreaudio_dev_info *dev_info; AudioComponent io_comp; struct stream_list streams; }; /* Sound stream. */ struct coreaudio_stream { pjmedia_aud_stream base; /**< Base stream */ pjmedia_aud_param param; /**< Settings */ pj_pool_t *pool; /**< Memory pool. */ struct coreaudio_factory *cf; struct stream_list list_entry; pjmedia_aud_rec_cb rec_cb; /**< Capture callback. */ pjmedia_aud_play_cb play_cb; /**< Playback callback. */ void *user_data; /**< Application data. */ pj_timestamp play_timestamp; pj_timestamp rec_timestamp; pj_int16_t *rec_buf; unsigned rec_buf_count; pj_int16_t *play_buf; unsigned play_buf_count; pj_bool_t interrupted; pj_bool_t quit_flag; pj_bool_t running; pj_bool_t rec_thread_initialized; pj_thread_desc rec_thread_desc; pj_thread_t *rec_thread; pj_bool_t play_thread_initialized; pj_thread_desc play_thread_desc; pj_thread_t *play_thread; AudioUnit io_units[2]; AudioStreamBasicDescription streamFormat; AudioBufferList *audio_buf; AudioConverterRef resample; pj_int16_t *resample_buf; void *resample_buf_ptr; unsigned resample_buf_count; unsigned resample_buf_size; #if !COREAUDIO_MAC AVAudioSession *sess; #endif }; /* Static variable */ static struct coreaudio_factory *cf_instance = NULL; /* Prototypes */ static pj_status_t ca_factory_init(pjmedia_aud_dev_factory *f); static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f); static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f); static unsigned ca_factory_get_dev_count(pjmedia_aud_dev_factory *f); static pj_status_t ca_factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_dev_info *info); static pj_status_t ca_factory_default_param(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_param *param); static pj_status_t ca_factory_create_stream(pjmedia_aud_dev_factory *f, const pjmedia_aud_param *param, pjmedia_aud_rec_cb rec_cb, pjmedia_aud_play_cb play_cb, void *user_data, pjmedia_aud_stream **p_aud_strm); static void ca_factory_set_observer(pjmedia_aud_dev_factory *f, pjmedia_aud_dev_change_callback cb); static int ca_factory_get_default_rec_dev(pjmedia_aud_dev_factory *f); static int ca_factory_get_default_play_dev(pjmedia_aud_dev_factory *f); static pj_status_t ca_stream_get_param(pjmedia_aud_stream *strm, pjmedia_aud_param *param); static pj_status_t ca_stream_get_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, void *value); static pj_status_t ca_stream_set_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, const void *value); static pj_status_t ca_stream_start(pjmedia_aud_stream *strm); static pj_status_t ca_stream_stop(pjmedia_aud_stream *strm); static pj_status_t ca_stream_destroy(pjmedia_aud_stream *strm); static pj_status_t create_audio_unit(AudioComponent io_comp, AudioDeviceID dev_id, pjmedia_dir dir, struct coreaudio_stream *strm, AudioUnit *io_unit); #if !COREAUDIO_MAC && USE_AUDIO_SESSION_API != 0 static void interruptionListener(void *inClientData, UInt32 inInterruption); static void propListener(void * inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void * inData); #endif /* Operations */ static pjmedia_aud_dev_factory_op factory_op = { &ca_factory_init, &ca_factory_destroy, &ca_factory_get_dev_count, &ca_factory_get_dev_info, &ca_factory_default_param, &ca_factory_create_stream, &ca_factory_refresh, &ca_factory_set_observer, &ca_factory_get_default_rec_dev, &ca_factory_get_default_play_dev }; static pjmedia_aud_stream_op stream_op = { &ca_stream_get_param, &ca_stream_get_cap, &ca_stream_set_cap, &ca_stream_start, &ca_stream_stop, &ca_stream_destroy }; /**************************************************************************** * Factory operations */ /* * Init coreaudio audio driver. */ pjmedia_aud_dev_factory* pjmedia_coreaudio_factory(pj_pool_factory *pf) { struct coreaudio_factory *f; pj_pool_t *pool; pool = pj_pool_create(pf, "core audio base", 1000, 1000, NULL); f = PJ_POOL_ZALLOC_T(pool, struct coreaudio_factory); f->pf = pf; f->base_pool = pool; f->base.op = &factory_op; return &f->base; } /* API: init factory */ static pj_status_t ca_factory_init(pjmedia_aud_dev_factory *f) { struct coreaudio_factory *cf = (struct coreaudio_factory*)f; AudioComponentDescription desc; pj_status_t status; #if !COREAUDIO_MAC unsigned i; #endif pj_list_init(&cf->streams); status = pj_mutex_create_recursive(cf->base_pool, "coreaudio", &cf->mutex); if (status != PJ_SUCCESS) return status; desc.componentType = kAudioUnitType_Output; #if COREAUDIO_MAC desc.componentSubType = kAudioUnitSubType_HALOutput; #else desc.componentSubType = kAudioUnitSubType_RemoteIO; #endif desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; cf->io_comp = AudioComponentFindNext(NULL, &desc); if (cf->io_comp == NULL) return PJMEDIA_EAUD_INIT; // cannot find IO unit; status = ca_factory_refresh(f); if (status != PJ_SUCCESS) return status; #if !COREAUDIO_MAC cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL); cf->dev_count = 1; cf->dev_info = (struct coreaudio_dev_info*) pj_pool_calloc(cf->pool, cf->dev_count, sizeof(struct coreaudio_dev_info)); for (i = 0; i < cf->dev_count; i++) { struct coreaudio_dev_info *cdi; cdi = &cf->dev_info[i]; pj_bzero(cdi, sizeof(*cdi)); cdi->dev_id = 0; strcpy(cdi->info.name, "iPhone IO device"); strcpy(cdi->info.driver, "apple"); cdi->info.input_count = 1; cdi->info.output_count = 1; cdi->info.default_samples_per_sec = 8000; /* Set the device capabilities here */ cdi->info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY | PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING | #if USE_AUDIO_SESSION_API != 0 PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE | PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE | #endif PJMEDIA_AUD_DEV_CAP_EC; cdi->info.routes = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER | PJMEDIA_AUD_DEV_ROUTE_EARPIECE | PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH; PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d) %dHz", i, cdi->info.name, cdi->info.input_count, cdi->info.output_count, cdi->info.default_samples_per_sec)); } #if USE_AUDIO_SESSION_API != 0 { OSStatus ostatus; /* Initialize the Audio Session */ ostatus = AudioSessionInitialize(NULL, NULL, interruptionListener, NULL); if (ostatus != kAudioSessionNoError) { PJ_LOG(4, (THIS_FILE, "Warning: cannot initialize audio session services (%i)", ostatus)); } /* Listen for audio routing change notifications. */ #if USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER != 0 ostatus = AudioSessionAddPropertyListener( kAudioSessionProperty_AudioRouteChange, propListener, cf); if (ostatus != kAudioSessionNoError) { PJ_LOG(4, (THIS_FILE, "Warning: cannot listen for audio route change " "notifications (%i)", ostatus)); } #endif } #endif /* Initialize audio session category and mode */ { AVAudioSession *sess = [AVAudioSession sharedInstance]; pj_bool_t err; if ([sess respondsToSelector:@selector(setCategory:withOptions:error:)]) { err = [sess setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil] != YES; } else { err = [sess setCategory:AVAudioSessionCategoryPlayAndRecord error:nil] != YES; } if (err) { PJ_LOG(3, (THIS_FILE, "Warning: failed settting audio session category")); } if ([sess respondsToSelector:@selector(setMode:error:)] && [sess setMode:AVAudioSessionModeVoiceChat error:nil] != YES) { PJ_LOG(3, (THIS_FILE, "Warning: failed settting audio mode")); } } cf_instance = cf; #endif PJ_LOG(4, (THIS_FILE, "core audio initialized")); return PJ_SUCCESS; } /* API: destroy factory */ static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f) { struct coreaudio_factory *cf = (struct coreaudio_factory*)f; pj_pool_t *pool; pj_assert(cf); pj_assert(cf->base_pool); pj_assert(pj_list_empty(&cf->streams)); #if !COREAUDIO_MAC #if USE_AUDIO_SESSION_API != 0 && USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER != 0 AudioSessionRemovePropertyListenerWithUserData( kAudioSessionProperty_AudioRouteChange, propListener, cf); #endif #endif if (cf->pool) { pj_pool_release(cf->pool); cf->pool = NULL; } if (cf->mutex) { pj_mutex_lock(cf->mutex); cf_instance = NULL; pj_mutex_unlock(cf->mutex); pj_mutex_destroy(cf->mutex); cf->mutex = NULL; } pool = cf->base_pool; cf->base_pool = NULL; pj_pool_release(pool); return PJ_SUCCESS; } /* API: refresh the device list */ static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f) { #if !COREAUDIO_MAC /* iPhone doesn't support refreshing the device list */ PJ_UNUSED_ARG(f); return PJ_SUCCESS; #else struct coreaudio_factory *cf = (struct coreaudio_factory*)f; unsigned i; unsigned dev_count; AudioObjectPropertyAddress addr; AudioDeviceID *dev_ids; UInt32 buf_size, dev_size, size = sizeof(AudioDeviceID); AudioBufferList *buf = NULL; OSStatus ostatus; if (cf->pool != NULL) { pj_pool_release(cf->pool); cf->pool = NULL; } cf->dev_count = 0; cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL); /* Find out how many audio devices there are */ addr.mSelector = kAudioHardwarePropertyDevices; addr.mScope = kAudioObjectPropertyScopeGlobal; addr.mElement = kAudioObjectPropertyElementMaster; ostatus = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, NULL, &dev_size); if (ostatus != noErr) { dev_size = 0; } /* Calculate the number of audio devices available */ dev_count = dev_size / size; if (dev_count==0) { PJ_LOG(4,(THIS_FILE, "core audio found no sound devices")); /* Enabling this will cause pjsua-lib initialization to fail when * there is no sound device installed in the system, even when pjsua * has been run with --null-audio. Moreover, it might be better to * think that the core audio backend initialization is successful, * regardless there is no audio device installed, as later application * can check it using get_dev_count(). return PJMEDIA_EAUD_NODEV; */ return PJ_SUCCESS; } PJ_LOG(4, (THIS_FILE, "core audio detected %d devices", dev_count)); /* Get all the audio device IDs */ dev_ids = (AudioDeviceID *)pj_pool_calloc(cf->pool, dev_count, size); if (!dev_ids) return PJ_ENOMEM; ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &dev_size, (void *)dev_ids); if (ostatus != noErr ) { /* This should not happen since we have successfully retrieved * the property data size before */ return PJMEDIA_EAUD_INIT; } if (dev_size > 1) { AudioDeviceID dev_id = kAudioObjectUnknown; unsigned idx = 0; /* Find default audio input device */ addr.mSelector = kAudioHardwarePropertyDefaultInputDevice; addr.mScope = kAudioObjectPropertyScopeGlobal; addr.mElement = kAudioObjectPropertyElementMaster; size = sizeof(dev_id); ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, (void *)&dev_id); if (ostatus == noErr && dev_id != dev_ids[idx]) { AudioDeviceID temp_id = dev_ids[idx]; for (i = idx + 1; i < dev_count; i++) { if (dev_ids[i] == dev_id) { dev_ids[idx++] = dev_id; dev_ids[i] = temp_id; break; } } } /* Find default audio output device */ addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, (void *)&dev_id); if (ostatus == noErr && dev_id != dev_ids[idx]) { AudioDeviceID temp_id = dev_ids[idx]; for (i = idx + 1; i < dev_count; i++) { if (dev_ids[i] == dev_id) { dev_ids[idx] = dev_id; dev_ids[i] = temp_id; break; } } } } /* Build the devices' info */ cf->dev_info = (struct coreaudio_dev_info*) pj_pool_calloc(cf->pool, dev_count, sizeof(struct coreaudio_dev_info)); buf_size = 0; for (i = 0; i < dev_count; i++) { struct coreaudio_dev_info *cdi; Float64 sampleRate; cdi = &cf->dev_info[i]; pj_bzero(cdi, sizeof(*cdi)); cdi->dev_id = dev_ids[i]; /* Get device name */ addr.mSelector = kAudioDevicePropertyDeviceName; addr.mScope = kAudioObjectPropertyScopeGlobal; addr.mElement = kAudioObjectPropertyElementMaster; size = sizeof(cdi->info.name); AudioObjectGetPropertyData(cdi->dev_id, &addr, 0, NULL, &size, (void *)cdi->info.name); strcpy(cdi->info.driver, "core audio"); /* Get the number of input channels */ addr.mSelector = kAudioDevicePropertyStreamConfiguration; addr.mScope = kAudioDevicePropertyScopeInput; size = 0; ostatus = AudioObjectGetPropertyDataSize(cdi->dev_id, &addr, 0, NULL, &size); if (ostatus == noErr && size > 0) { if (size > buf_size) { buf = pj_pool_alloc(cf->pool, size); buf_size = size; } if (buf) { UInt32 idx; /* Get the input stream configuration */ ostatus = AudioObjectGetPropertyData(cdi->dev_id, &addr, 0, NULL, &size, buf); if (ostatus == noErr) { /* Count the total number of input channels in * the stream */ for (idx = 0; idx < buf->mNumberBuffers; idx++) { cdi->info.input_count += buf->mBuffers[idx].mNumberChannels; } } } } /* Get the number of output channels */ addr.mScope = kAudioDevicePropertyScopeOutput; size = 0; ostatus = AudioObjectGetPropertyDataSize(cdi->dev_id, &addr, 0, NULL, &size); if (ostatus == noErr && size > 0) { if (size > buf_size) { buf = pj_pool_alloc(cf->pool, size); buf_size = size; } if (buf) { UInt32 idx; /* Get the output stream configuration */ ostatus = AudioObjectGetPropertyData(cdi->dev_id, &addr, 0, NULL, &size, buf); if (ostatus == noErr) { /* Count the total number of output channels in * the stream */ for (idx = 0; idx < buf->mNumberBuffers; idx++) { cdi->info.output_count += buf->mBuffers[idx].mNumberChannels; } } } } /* Get default sample rate */ addr.mSelector = kAudioDevicePropertyNominalSampleRate; addr.mScope = kAudioObjectPropertyScopeGlobal; size = sizeof(Float64); ostatus = AudioObjectGetPropertyData (cdi->dev_id, &addr, 0, NULL, &size, &sampleRate); cdi->info.default_samples_per_sec = (ostatus == noErr ? sampleRate: 16000); /* Set device capabilities here */ if (cdi->info.input_count > 0) { cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY; } if (cdi->info.output_count > 0) { cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; addr.mSelector = kAudioDevicePropertyVolumeScalar; addr.mScope = kAudioDevicePropertyScopeOutput; if (AudioObjectHasProperty(cdi->dev_id, &addr)) { cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING; } } cf->dev_count++; PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d) %dHz", i, cdi->info.name, cdi->info.input_count, cdi->info.output_count, cdi->info.default_samples_per_sec)); } return PJ_SUCCESS; #endif } /* API: get number of devices */ static unsigned ca_factory_get_dev_count(pjmedia_aud_dev_factory *f) { struct coreaudio_factory *cf = (struct coreaudio_factory*)f; return cf->dev_count; } /* API: get device info */ static pj_status_t ca_factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_dev_info *info) { struct coreaudio_factory *cf = (struct coreaudio_factory*)f; PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EAUD_INVDEV); pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info)); return PJ_SUCCESS; } /* API: create default device parameter */ static pj_status_t ca_factory_default_param(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_param *param) { struct coreaudio_factory *cf = (struct coreaudio_factory*)f; struct coreaudio_dev_info *di = &cf->dev_info[index]; PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EAUD_INVDEV); pj_bzero(param, sizeof(*param)); if (di->info.input_count && di->info.output_count) { param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; param->rec_id = index; param->play_id = index; } else if (di->info.input_count) { param->dir = PJMEDIA_DIR_CAPTURE; param->rec_id = index; param->play_id = PJMEDIA_AUD_INVALID_DEV; } else if (di->info.output_count) { param->dir = PJMEDIA_DIR_PLAYBACK; param->play_id = index; param->rec_id = PJMEDIA_AUD_INVALID_DEV; } else { return PJMEDIA_EAUD_INVDEV; } /* Set the mandatory settings here */ param->clock_rate = di->info.default_samples_per_sec; param->channel_count = 1; param->samples_per_frame = di->info.default_samples_per_sec * 20 / 1000; param->bits_per_sample = 16; /* Set the param for device capabilities here */ param->flags = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY; param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY; return PJ_SUCCESS; } static OSStatus property_listener_proc(AudioObjectID objectID, UInt32 numberAddresses, const AudioObjectPropertyAddress inAddresses[], void *clientData) { pjmedia_aud_dev_change_callback cb = (pjmedia_aud_dev_change_callback)clientData; pjmedia_aud_dev_change_event event; UInt32 i; for(i = 0; i < numberAddresses; i++) { event = 0; switch (inAddresses[i].mSelector) { case kAudioHardwarePropertyDefaultInputDevice: event = DEFAULT_INPUT_CHANGED; break; case kAudioHardwarePropertyDefaultOutputDevice: event = DEFAULT_OUTPUT_CHANGED; break; case kAudioHardwarePropertyDevices: event = DEVICE_LIST_CHANGED; break; default: break; } if (event > 0) { (cb)(event); } } return noErr; } /* API: set audio device change observer */ static void ca_factory_set_observer(pjmedia_aud_dev_factory *f, pjmedia_aud_dev_change_callback cb) { AudioObjectPropertyAddress addr; OSStatus ostatus; /* observer for devices list */ addr.mSelector = kAudioHardwarePropertyDevices; addr.mScope = kAudioObjectPropertyScopeGlobal; addr.mElement = kAudioObjectPropertyElementMaster; if (cb) { ostatus = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &addr, property_listener_proc, cb); } else { ostatus = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &addr, property_listener_proc, cb); } if (ostatus != noErr) { PJ_LOG(5,(THIS_FILE, "Error %sregistering devices list observer", cb==NULL ? "un-" : "")); } /* observer for default input device */ addr.mSelector = kAudioHardwarePropertyDefaultInputDevice; if (cb) { ostatus = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &addr, property_listener_proc, cb); } else { ostatus = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &addr, property_listener_proc, cb); } if (ostatus != noErr) { PJ_LOG(5,(THIS_FILE, "Error %sregistering default input device observer", cb==NULL ? "un-" : "")); } /* observer for default output device */ addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; if (cb) { ostatus = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &addr, property_listener_proc, cb); } else { ostatus = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &addr, property_listener_proc, cb); } if (ostatus != noErr) { PJ_LOG(5,(THIS_FILE, "Error %sregistering default output device observer", cb==NULL ? "un-" : "")); } } /* API: get default recording device */ static int ca_factory_get_default_rec_dev(pjmedia_aud_dev_factory *f) { AudioDeviceID dev_id = kAudioObjectUnknown; AudioObjectPropertyAddress addr; UInt32 size; OSStatus ostatus; int i; int idx = -1; struct coreaudio_factory *cf = (struct coreaudio_factory*)f; /* Find default audio input device */ addr.mSelector = kAudioHardwarePropertyDefaultInputDevice; addr.mScope = kAudioObjectPropertyScopeGlobal; addr.mElement = kAudioObjectPropertyElementMaster; size = sizeof(dev_id); ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, (void *)&dev_id); if (ostatus == noErr) { for (i = 0; i < cf->dev_count; i++) { struct coreaudio_dev_info *cdi; cdi = &cf->dev_info[i]; if (cdi->dev_id == dev_id) { idx = i; break; } } } return idx; } /* API: get default playback device */ static int ca_factory_get_default_play_dev(pjmedia_aud_dev_factory *f) { AudioDeviceID dev_id = kAudioObjectUnknown; AudioObjectPropertyAddress addr; UInt32 size; OSStatus ostatus; int i; int idx = -1; struct coreaudio_factory *cf = (struct coreaudio_factory*)f; /* Find default audio output device */ addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; addr.mScope = kAudioObjectPropertyScopeGlobal; addr.mElement = kAudioObjectPropertyElementMaster; size = sizeof(dev_id); ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, (void *)&dev_id); if (ostatus == noErr) { for (i = 0; i < cf->dev_count; i++) { struct coreaudio_dev_info *cdi; cdi = &cf->dev_info[i]; if (cdi->dev_id == dev_id) { idx = i; break; } } } return idx; } OSStatus resampleProc(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData) { struct coreaudio_stream *strm = (struct coreaudio_stream*)inUserData; if (*ioNumberDataPackets > strm->resample_buf_size) *ioNumberDataPackets = strm->resample_buf_size; ioData->mNumberBuffers = 1; ioData->mBuffers[0].mNumberChannels = strm->streamFormat.mChannelsPerFrame; ioData->mBuffers[0].mData = strm->resample_buf_ptr; ioData->mBuffers[0].mDataByteSize = *ioNumberDataPackets * strm->streamFormat.mChannelsPerFrame * strm->param.bits_per_sample >> 3; return noErr; } static OSStatus resample_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon; OSStatus ostatus; pj_status_t status = 0; unsigned nsamples; AudioBufferList *buf = strm->audio_buf; pj_int16_t *input; UInt32 resampleSize; pj_assert(!strm->quit_flag); /* Known cases of callback's thread: * - The thread may be changed in the middle of a session * it happens when plugging/unplugging headphone. * - The same thread may be reused in consecutive sessions. The first * session will leave TLS set, but release the TLS data address, * so the second session must re-register the callback's thread. */ if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered()) { pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc)); status = pj_thread_register("ca_rec", strm->rec_thread_desc, &strm->rec_thread); strm->rec_thread_initialized = 1; PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)", inNumberFrames)); } buf->mBuffers[0].mData = NULL; buf->mBuffers[0].mDataByteSize = inNumberFrames * strm->streamFormat.mChannelsPerFrame; /* Render the unit to get input data */ ostatus = AudioUnitRender(strm->io_units[0], ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, buf); if (ostatus != noErr) { PJ_LOG(5, (THIS_FILE, "Core audio unit render error %i", ostatus)); goto on_break; } input = (pj_int16_t *)buf->mBuffers[0].mData; resampleSize = strm->resample_buf_size; nsamples = inNumberFrames * strm->param.channel_count + strm->resample_buf_count; if (nsamples >= resampleSize) { pjmedia_frame frame; UInt32 resampleOutput = strm->param.samples_per_frame / strm->streamFormat.mChannelsPerFrame; AudioBufferList ab; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = (void*) strm->rec_buf; frame.size = strm->param.samples_per_frame * strm->param.bits_per_sample >> 3; frame.bit_info = 0; ab.mNumberBuffers = 1; ab.mBuffers[0].mNumberChannels = strm->streamFormat.mChannelsPerFrame; ab.mBuffers[0].mData = strm->rec_buf; ab.mBuffers[0].mDataByteSize = frame.size; /* If buffer is not empty, combine the buffer with the just incoming * samples, then call put_frame. */ if (strm->resample_buf_count) { unsigned chunk_count = resampleSize - strm->resample_buf_count; pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count, input, chunk_count); /* Do the resample */ strm->resample_buf_ptr = strm->resample_buf; ostatus = AudioConverterFillComplexBuffer(strm->resample, resampleProc, strm, &resampleOutput, &ab, NULL); if (ostatus != noErr) { goto on_break; } frame.timestamp.u64 = strm->rec_timestamp.u64; status = (*strm->rec_cb)(strm->user_data, &frame); input = input + chunk_count; nsamples -= resampleSize; strm->resample_buf_count = 0; strm->rec_timestamp.u64 += strm->param.samples_per_frame / strm->param.channel_count; } /* Give all frames we have */ while (nsamples >= resampleSize && status == 0) { frame.timestamp.u64 = strm->rec_timestamp.u64; /* Do the resample */ strm->resample_buf_ptr = input; ab.mBuffers[0].mDataByteSize = frame.size; resampleOutput = strm->param.samples_per_frame / strm->streamFormat.mChannelsPerFrame; ostatus = AudioConverterFillComplexBuffer(strm->resample, resampleProc, strm, &resampleOutput, &ab, NULL); if (ostatus != noErr) { goto on_break; } status = (*strm->rec_cb)(strm->user_data, &frame); input = (pj_int16_t*) input + resampleSize; nsamples -= resampleSize; strm->rec_timestamp.u64 += strm->param.samples_per_frame / strm->param.channel_count; } /* Store the remaining samples into the buffer */ if (nsamples && status == 0) { strm->resample_buf_count = nsamples; pjmedia_copy_samples(strm->resample_buf, input, nsamples); } } else { /* Not enough samples, let's just store them in the buffer */ pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count, input, inNumberFrames * strm->param.channel_count); strm->resample_buf_count += inNumberFrames * strm->param.channel_count; } return noErr; on_break: return -1; } static OSStatus input_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon; OSStatus ostatus; pj_status_t status = 0; unsigned nsamples; AudioBufferList *buf = strm->audio_buf; pj_int16_t *input; pj_assert(!strm->quit_flag); /* Known cases of callback's thread: * - The thread may be changed in the middle of a session * it happens when plugging/unplugging headphone. * - The same thread may be reused in consecutive sessions. The first * session will leave TLS set, but release the TLS data address, * so the second session must re-register the callback's thread. */ if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered()) { pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc)); status = pj_thread_register("ca_rec", strm->rec_thread_desc, &strm->rec_thread); strm->rec_thread_initialized = 1; PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)", inNumberFrames)); } buf->mBuffers[0].mData = NULL; buf->mBuffers[0].mDataByteSize = inNumberFrames * strm->streamFormat.mChannelsPerFrame; /* Render the unit to get input data */ ostatus = AudioUnitRender(strm->io_units[0], ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, buf); if (ostatus != noErr) { PJ_LOG(5, (THIS_FILE, "Core audio unit render error %i", ostatus)); goto on_break; } input = (pj_int16_t *)buf->mBuffers[0].mData; /* Calculate number of samples we've got */ nsamples = inNumberFrames * strm->param.channel_count + strm->rec_buf_count; if (nsamples >= strm->param.samples_per_frame) { pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.size = strm->param.samples_per_frame * strm->param.bits_per_sample >> 3; frame.bit_info = 0; /* If buffer is not empty, combine the buffer with the just incoming * samples, then call put_frame. */ if (strm->rec_buf_count) { unsigned chunk_count = 0; chunk_count = strm->param.samples_per_frame - strm->rec_buf_count; pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count, input, chunk_count); frame.buf = (void*) strm->rec_buf; frame.timestamp.u64 = strm->rec_timestamp.u64; status = (*strm->rec_cb)(strm->user_data, &frame); input = input + chunk_count; nsamples -= strm->param.samples_per_frame; strm->rec_buf_count = 0; strm->rec_timestamp.u64 += strm->param.samples_per_frame / strm->param.channel_count; } /* Give all frames we have */ while (nsamples >= strm->param.samples_per_frame && status == 0) { frame.buf = (void*) input; frame.timestamp.u64 = strm->rec_timestamp.u64; status = (*strm->rec_cb)(strm->user_data, &frame); input = (pj_int16_t*) input + strm->param.samples_per_frame; nsamples -= strm->param.samples_per_frame; strm->rec_timestamp.u64 += strm->param.samples_per_frame / strm->param.channel_count; } /* Store the remaining samples into the buffer */ if (nsamples && status == 0) { strm->rec_buf_count = nsamples; pjmedia_copy_samples(strm->rec_buf, input, nsamples); } } else { /* Not enough samples, let's just store them in the buffer */ pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count, input, inNumberFrames * strm->param.channel_count); strm->rec_buf_count += inNumberFrames * strm->param.channel_count; } return noErr; on_break: return -1; } static OSStatus output_renderer(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { struct coreaudio_stream *stream = (struct coreaudio_stream*)inRefCon; pj_status_t status = 0; unsigned nsamples_req = inNumberFrames * stream->param.channel_count; pj_int16_t *output = ioData->mBuffers[0].mData; pj_assert(!stream->quit_flag); /* Known cases of callback's thread: * - The thread may be changed in the middle of a session * it happens when plugging/unplugging headphone. * - The same thread may be reused in consecutive sessions. The first * session will leave TLS set, but release the TLS data address, * so the second session must re-register the callback's thread. */ if (stream->play_thread_initialized == 0 || !pj_thread_is_registered()) { pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc)); status = pj_thread_register("coreaudio", stream->play_thread_desc, &stream->play_thread); stream->play_thread_initialized = 1; PJ_LOG(5,(THIS_FILE, "Player thread started, (%i frames)", inNumberFrames)); } /* Check if any buffered samples */ if (stream->play_buf_count) { /* samples buffered >= requested by sound device */ if (stream->play_buf_count >= nsamples_req) { pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf, nsamples_req); stream->play_buf_count -= nsamples_req; pjmedia_move_samples(stream->play_buf, stream->play_buf + nsamples_req, stream->play_buf_count); nsamples_req = 0; return noErr; } /* samples buffered < requested by sound device */ pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf, stream->play_buf_count); nsamples_req -= stream->play_buf_count; output = (pj_int16_t*)output + stream->play_buf_count; stream->play_buf_count = 0; } /* Fill output buffer as requested */ while (nsamples_req && status == 0) { pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.size = stream->param.samples_per_frame * stream->param.bits_per_sample >> 3; frame.timestamp.u64 = stream->play_timestamp.u64; frame.bit_info = 0; if (nsamples_req >= stream->param.samples_per_frame) { frame.buf = output; status = (*stream->play_cb)(stream->user_data, &frame); if (status != PJ_SUCCESS) goto on_break; if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero(frame.buf, frame.size); nsamples_req -= stream->param.samples_per_frame; output = (pj_int16_t*)output + stream->param.samples_per_frame; } else { frame.buf = stream->play_buf; status = (*stream->play_cb)(stream->user_data, &frame); if (status != PJ_SUCCESS) goto on_break; if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero(frame.buf, frame.size); pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf, nsamples_req); stream->play_buf_count = stream->param.samples_per_frame - nsamples_req; pjmedia_move_samples(stream->play_buf, stream->play_buf+nsamples_req, stream->play_buf_count); nsamples_req = 0; } stream->play_timestamp.u64 += stream->param.samples_per_frame / stream->param.channel_count; } return noErr; on_break: return -1; } #if !COREAUDIO_MAC && USE_AUDIO_SESSION_API != 0 static void propListener(void *inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void * inData) { struct coreaudio_factory *cf = (struct coreaudio_factory*)inClientData; struct stream_list *it, *itBegin; CFDictionaryRef routeDictionary; CFNumberRef reason; SInt32 reasonVal; pj_assert(cf); if (inID != kAudioSessionProperty_AudioRouteChange) return; routeDictionary = (CFDictionaryRef)inData; reason = (CFNumberRef) CFDictionaryGetValue( routeDictionary, CFSTR(kAudioSession_AudioRouteChangeKey_Reason)); CFNumberGetValue(reason, kCFNumberSInt32Type, &reasonVal); if (reasonVal != kAudioSessionRouteChangeReason_OldDeviceUnavailable) { PJ_LOG(3, (THIS_FILE, "ignoring audio route change...")); return; } PJ_LOG(3, (THIS_FILE, "audio route changed")); pj_mutex_lock(cf->mutex); itBegin = &cf->streams; for (it = itBegin->next; it != itBegin; it = it->next) { if (it->stream->interrupted) continue; /* status = ca_stream_stop((pjmedia_aud_stream *)it->stream); status = ca_stream_start((pjmedia_aud_stream *)it->stream); if (status != PJ_SUCCESS) { PJ_LOG(3, (THIS_FILE, "Error: failed to restart the audio unit (%i)", status)); continue; } PJ_LOG(3, (THIS_FILE, "core audio unit successfully restarted")); */ } pj_mutex_unlock(cf->mutex); } static void interruptionListener(void *inClientData, UInt32 inInterruption) { struct stream_list *it, *itBegin; pj_status_t status; static pj_thread_desc thread_desc; pj_thread_t *thread; /* Register the thread with PJLIB, this is must for any external threads * which need to use the PJLIB framework. */ if (!pj_thread_is_registered()) { pj_bzero(thread_desc, sizeof(pj_thread_desc)); status = pj_thread_register("intListener", thread_desc, &thread); } PJ_LOG(3, (THIS_FILE, "Session interrupted! --- %s ---", inInterruption == kAudioSessionBeginInterruption ? "Begin Interruption" : "End Interruption")); if (!cf_instance) return; pj_mutex_lock(cf_instance->mutex); itBegin = &cf_instance->streams; for (it = itBegin->next; it != itBegin; it = it->next) { if (inInterruption == kAudioSessionEndInterruption && it->stream->interrupted == PJ_TRUE) { UInt32 audioCategory; OSStatus ostatus; /* Make sure that your application can receive remote control * events by adding the code: * [[UIApplication sharedApplication] * beginReceivingRemoteControlEvents]; * Otherwise audio unit will fail to restart while your * application is in the background mode. */ /* Make sure we set the correct audio category before restarting */ audioCategory = kAudioSessionCategory_PlayAndRecord; ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory); if (ostatus != kAudioSessionNoError) { PJ_LOG(4, (THIS_FILE, "Warning: cannot set the audio session category (%i)", ostatus)); } /* Restart the stream */ status = ca_stream_start((pjmedia_aud_stream*)it->stream); if (status != PJ_SUCCESS) { PJ_LOG(3, (THIS_FILE, "Error: failed to restart the audio unit (%i)", status)); continue; } PJ_LOG(3, (THIS_FILE, "core audio unit successfully resumed" " after interruption")); } else if (inInterruption == kAudioSessionBeginInterruption && it->stream->running == PJ_TRUE) { status = ca_stream_stop((pjmedia_aud_stream*)it->stream); it->stream->interrupted = PJ_TRUE; } } pj_mutex_unlock(cf_instance->mutex); } #endif #if COREAUDIO_MAC /* Internal: create audio converter for resampling the recorder device */ static pj_status_t create_audio_resample(struct coreaudio_stream *strm, AudioStreamBasicDescription *desc) { OSStatus ostatus; pj_assert(strm->streamFormat.mSampleRate != desc->mSampleRate); pj_assert(NULL == strm->resample); pj_assert(NULL == strm->resample_buf); /* Create the audio converter */ ostatus = AudioConverterNew(desc, &strm->streamFormat, &strm->resample); if (ostatus != noErr) { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } /* * Allocate the buffer required to hold enough input data */ strm->resample_buf_size = (unsigned)(desc->mSampleRate * strm->param.samples_per_frame / strm->param.clock_rate); strm->resample_buf = (pj_int16_t*) pj_pool_alloc(strm->pool, strm->resample_buf_size * strm->param.bits_per_sample >> 3); if (!strm->resample_buf) return PJ_ENOMEM; strm->resample_buf_count = 0; return PJ_SUCCESS; } #endif /* Internal: create audio unit for recorder/playback device */ static pj_status_t create_audio_unit(AudioComponent io_comp, AudioDeviceID dev_id, pjmedia_dir dir, struct coreaudio_stream *strm, AudioUnit *io_unit) { OSStatus ostatus; #if !COREAUDIO_MAC strm->sess = [AVAudioSession sharedInstance]; #endif /* Create an audio unit to interface with the device */ ostatus = AudioComponentInstanceNew(io_comp, io_unit); if (ostatus != noErr) { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } /* Set audio unit's properties for capture device */ if (dir & PJMEDIA_DIR_CAPTURE) { UInt32 enable = 1; /* Enable input */ ostatus = AudioUnitSetProperty(*io_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enable, sizeof(enable)); if (ostatus != noErr) { PJ_LOG(4, (THIS_FILE, "Warning: cannot enable IO of capture device %d", dev_id)); } /* Disable output */ if (!(dir & PJMEDIA_DIR_PLAYBACK)) { enable = 0; ostatus = AudioUnitSetProperty(*io_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enable, sizeof(enable)); if (ostatus != noErr) { PJ_LOG(4, (THIS_FILE, "Warning: cannot disable IO of capture device %d", dev_id)); } } } /* Set audio unit's properties for playback device */ if (dir & PJMEDIA_DIR_PLAYBACK) { UInt32 enable = 1; /* Enable output */ ostatus = AudioUnitSetProperty(*io_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enable, sizeof(enable)); if (ostatus != noErr) { PJ_LOG(4, (THIS_FILE, "Warning: cannot enable IO of playback device %d", dev_id)); } } #if COREAUDIO_MAC PJ_LOG(5, (THIS_FILE, "Opening device %d", dev_id)); ostatus = AudioUnitSetProperty(*io_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &dev_id, sizeof(dev_id)); if (ostatus != noErr) { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } #endif if (dir & PJMEDIA_DIR_CAPTURE) { #if COREAUDIO_MAC AudioStreamBasicDescription deviceFormat; UInt32 size; /* * Keep the sample rate from the device, otherwise we will confuse * AUHAL */ size = sizeof(AudioStreamBasicDescription); ostatus = AudioUnitGetProperty(*io_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &deviceFormat, &size); if (ostatus != noErr) { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } strm->streamFormat.mSampleRate = deviceFormat.mSampleRate; #endif /* When setting the stream format, we have to make sure the sample * rate is supported. Setting an unsupported sample rate will cause * AudioUnitRender() to fail later. */ ostatus = AudioUnitSetProperty(*io_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &strm->streamFormat, sizeof(strm->streamFormat)); if (ostatus != noErr) { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } #if COREAUDIO_MAC strm->streamFormat.mSampleRate = strm->param.clock_rate; size = sizeof(AudioStreamBasicDescription); ostatus = AudioUnitGetProperty (*io_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &deviceFormat, &size); if (ostatus == noErr) { if (strm->streamFormat.mSampleRate != deviceFormat.mSampleRate) { pj_status_t rc = create_audio_resample(strm, &deviceFormat); if (PJ_SUCCESS != rc) return rc; } } else { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } #endif } if (dir & PJMEDIA_DIR_PLAYBACK) { AURenderCallbackStruct output_cb; /* Set the stream format */ ostatus = AudioUnitSetProperty(*io_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &strm->streamFormat, sizeof(strm->streamFormat)); if (ostatus != noErr) { PJ_LOG(4, (THIS_FILE, "Warning: cannot set playback stream format of dev %d", dev_id)); } /* Set render callback */ output_cb.inputProc = output_renderer; output_cb.inputProcRefCon = strm; ostatus = AudioUnitSetProperty(*io_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &output_cb, sizeof(output_cb)); if (ostatus != noErr) { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } /* Allocate playback buffer */ strm->play_buf = (pj_int16_t*)pj_pool_alloc(strm->pool, strm->param.samples_per_frame * strm->param.bits_per_sample >> 3); if (!strm->play_buf) return PJ_ENOMEM; strm->play_buf_count = 0; } if (dir & PJMEDIA_DIR_CAPTURE) { AURenderCallbackStruct input_cb; #if COREAUDIO_MAC AudioBuffer *ab; UInt32 size, buf_size; #endif /* Set input callback */ input_cb.inputProc = strm->resample ? resample_callback : input_callback; input_cb.inputProcRefCon = strm; ostatus = AudioUnitSetProperty( *io_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input_cb, sizeof(input_cb)); if (ostatus != noErr) { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } #if COREAUDIO_MAC /* Get device's buffer frame size */ size = sizeof(UInt32); ostatus = AudioUnitGetProperty(*io_unit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0, &buf_size, &size); if (ostatus != noErr) { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } /* Allocate audio buffer */ strm->audio_buf = (AudioBufferList*)pj_pool_alloc(strm->pool, sizeof(AudioBufferList) + sizeof(AudioBuffer)); if (!strm->audio_buf) return PJ_ENOMEM; strm->audio_buf->mNumberBuffers = 1; ab = &strm->audio_buf->mBuffers[0]; ab->mNumberChannels = strm->streamFormat.mChannelsPerFrame; ab->mDataByteSize = buf_size * ab->mNumberChannels * strm->param.bits_per_sample >> 3; ab->mData = pj_pool_alloc(strm->pool, ab->mDataByteSize); if (!ab->mData) return PJ_ENOMEM; #else /* We will let AudioUnitRender() to allocate the buffer * for us later */ strm->audio_buf = (AudioBufferList*)pj_pool_alloc(strm->pool, sizeof(AudioBufferList) + sizeof(AudioBuffer)); if (!strm->audio_buf) return PJ_ENOMEM; strm->audio_buf->mNumberBuffers = 1; strm->audio_buf->mBuffers[0].mNumberChannels = strm->streamFormat.mChannelsPerFrame; #endif /* Allocate recording buffer */ strm->rec_buf = (pj_int16_t*)pj_pool_alloc(strm->pool, strm->param.samples_per_frame * strm->param.bits_per_sample >> 3); if (!strm->rec_buf) return PJ_ENOMEM; strm->rec_buf_count = 0; } /* Initialize the audio unit */ ostatus = AudioUnitInitialize(*io_unit); if (ostatus != noErr) { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } return PJ_SUCCESS; } /* API: create stream */ static pj_status_t ca_factory_create_stream(pjmedia_aud_dev_factory *f, const pjmedia_aud_param *param, pjmedia_aud_rec_cb rec_cb, pjmedia_aud_play_cb play_cb, void *user_data, pjmedia_aud_stream **p_aud_strm) { struct coreaudio_factory *cf = (struct coreaudio_factory*)f; pj_pool_t *pool; struct coreaudio_stream *strm; pj_status_t status; /* Create and Initialize stream descriptor */ pool = pj_pool_create(cf->pf, "coreaudio-dev", 1000, 1000, NULL); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); strm = PJ_POOL_ZALLOC_T(pool, struct coreaudio_stream); pj_list_init(&strm->list_entry); strm->list_entry.stream = strm; strm->cf = cf; pj_memcpy(&strm->param, param, sizeof(*param)); strm->pool = pool; strm->rec_cb = rec_cb; strm->play_cb = play_cb; strm->user_data = user_data; /* Set the stream format */ strm->streamFormat.mSampleRate = param->clock_rate; strm->streamFormat.mFormatID = kAudioFormatLinearPCM; strm->streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; strm->streamFormat.mBitsPerChannel = strm->param.bits_per_sample; strm->streamFormat.mChannelsPerFrame = param->channel_count; strm->streamFormat.mBytesPerFrame = strm->streamFormat.mChannelsPerFrame * strm->param.bits_per_sample >> 3; strm->streamFormat.mFramesPerPacket = 1; strm->streamFormat.mBytesPerPacket = strm->streamFormat.mBytesPerFrame * strm->streamFormat.mFramesPerPacket; /* Apply input/output routes settings before we create the audio units */ if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE) { ca_stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE, ¶m->input_route); } if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE) { ca_stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE, ¶m->output_route); } if (param->flags & PJMEDIA_AUD_DEV_CAP_EC) { ca_stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_EC, ¶m->ec_enabled); } else { pj_bool_t ec = PJ_FALSE; ca_stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_EC, &ec); } strm->io_units[0] = strm->io_units[1] = NULL; if (param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK && param->rec_id == param->play_id) { /* If both input and output are on the same device, only create * one audio unit to interface with the device. */ status = create_audio_unit(cf->io_comp, cf->dev_info[param->rec_id].dev_id, param->dir, strm, &strm->io_units[0]); if (status != PJ_SUCCESS) goto on_error; } else { unsigned nunits = 0; if (param->dir & PJMEDIA_DIR_CAPTURE) { status = create_audio_unit(cf->io_comp, cf->dev_info[param->rec_id].dev_id, PJMEDIA_DIR_CAPTURE, strm, &strm->io_units[nunits++]); if (status != PJ_SUCCESS) goto on_error; } if (param->dir & PJMEDIA_DIR_PLAYBACK) { status = create_audio_unit(cf->io_comp, cf->dev_info[param->play_id].dev_id, PJMEDIA_DIR_PLAYBACK, strm, &strm->io_units[nunits++]); if (status != PJ_SUCCESS) goto on_error; } } /* Apply the remaining settings */ if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY) { ca_stream_get_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY, &strm->param.input_latency_ms); } if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY) { ca_stream_get_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY, &strm->param.output_latency_ms); } if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) { ca_stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, ¶m->output_vol); } pj_mutex_lock(strm->cf->mutex); pj_assert(pj_list_empty(&strm->list_entry)); pj_list_insert_after(&strm->cf->streams, &strm->list_entry); pj_mutex_unlock(strm->cf->mutex); /* Done */ strm->base.op = &stream_op; *p_aud_strm = &strm->base; return PJ_SUCCESS; on_error: ca_stream_destroy((pjmedia_aud_stream *)strm); return status; } /* API: Get stream info. */ static pj_status_t ca_stream_get_param(pjmedia_aud_stream *s, pjmedia_aud_param *pi) { struct coreaudio_stream *strm = (struct coreaudio_stream*)s; PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); pj_memcpy(pi, &strm->param, sizeof(*pi)); /* Update the device capabilities' values */ if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY, &pi->input_latency_ms) == PJ_SUCCESS) { pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY; } if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY, &pi->output_latency_ms) == PJ_SUCCESS) { pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; } if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, &pi->output_vol) == PJ_SUCCESS) { pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING; } if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE, &pi->input_route) == PJ_SUCCESS) { pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE; } if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE, &pi->output_route) == PJ_SUCCESS) { pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE; } if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_EC, &pi->ec_enabled) == PJ_SUCCESS) { pi->flags |= PJMEDIA_AUD_DEV_CAP_EC; } return PJ_SUCCESS; } /* API: get capability */ static pj_status_t ca_stream_get_cap(pjmedia_aud_stream *s, pjmedia_aud_dev_cap cap, void *pval) { struct coreaudio_stream *strm = (struct coreaudio_stream*)s; PJ_UNUSED_ARG(strm); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY && (strm->param.dir & PJMEDIA_DIR_CAPTURE)) { #if COREAUDIO_MAC UInt32 latency, size = sizeof(UInt32); /* Recording latency */ if (AudioUnitGetProperty (strm->io_units[0], kAudioDevicePropertyLatency, kAudioUnitScope_Input, 1, &latency, &size) == noErr) { UInt32 latency2; if (AudioUnitGetProperty (strm->io_units[0], kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Input, 1, &latency2, &size) == noErr) { strm->param.input_latency_ms = (latency + latency2) * 1000 / strm->param.clock_rate; } } #else if ([strm->sess respondsToSelector:@selector(inputLatency)]) { strm->param.input_latency_ms = (unsigned)(([strm->sess inputLatency] + [strm->sess IOBufferDuration]) * 1000); } else return PJMEDIA_EAUD_INVCAP; #endif *(unsigned*)pval = strm->param.input_latency_ms; return PJ_SUCCESS; } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && (strm->param.dir & PJMEDIA_DIR_PLAYBACK)) { #if COREAUDIO_MAC UInt32 latency, size = sizeof(UInt32); AudioUnit *io_unit = strm->io_units[1] ? &strm->io_units[1] : &strm->io_units[0]; /* Playback latency */ if (AudioUnitGetProperty (*io_unit, kAudioDevicePropertyLatency, kAudioUnitScope_Output, 0, &latency, &size) == noErr) { UInt32 latency2; if (AudioUnitGetProperty (*io_unit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Output, 0, &latency2, &size) == noErr) { strm->param.output_latency_ms = (latency + latency2) * 1000 / strm->param.clock_rate; } } #else if ([strm->sess respondsToSelector:@selector(outputLatency)]) { strm->param.output_latency_ms = (unsigned)(([strm->sess outputLatency] + [strm->sess IOBufferDuration]) * 1000); } else return PJMEDIA_EAUD_INVCAP; #endif *(unsigned*)pval = strm->param.output_latency_ms; return PJ_SUCCESS; } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING && (strm->param.dir & PJMEDIA_DIR_PLAYBACK)) { #if COREAUDIO_MAC OSStatus ostatus; Float32 volume; UInt32 size = sizeof(Float32); /* Output volume setting */ ostatus = AudioUnitGetProperty (strm->io_units[1] ? strm->io_units[1] : strm->io_units[0], kAudioDevicePropertyVolumeScalar, kAudioUnitScope_Output, 0, &volume, &size); if (ostatus != noErr) return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); *(unsigned*)pval = (unsigned)(volume * 100); return PJ_SUCCESS; #else if ([strm->sess respondsToSelector:@selector(outputVolume)]) { *(unsigned*)pval = (unsigned)([strm->sess outputVolume] * 100); return PJ_SUCCESS; } else return PJMEDIA_EAUD_INVCAP; #endif #if !COREAUDIO_MAC #if USE_AUDIO_SESSION_API != 0 } else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE && (strm->param.dir & PJMEDIA_DIR_CAPTURE)) { UInt32 btooth, size = sizeof(UInt32); OSStatus ostatus; ostatus = AudioSessionGetProperty ( kAudioSessionProperty_OverrideCategoryEnableBluetoothInput, &size, &btooth); if (ostatus != kAudioSessionNoError) { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } *(pjmedia_aud_dev_route*)pval = btooth? PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH: PJMEDIA_AUD_DEV_ROUTE_DEFAULT; return PJ_SUCCESS; } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE && (strm->param.dir & PJMEDIA_DIR_PLAYBACK)) { CFStringRef route; UInt32 size = sizeof(CFStringRef); OSStatus ostatus; ostatus = AudioSessionGetProperty (kAudioSessionProperty_AudioRoute, &size, &route); if (ostatus != kAudioSessionNoError) { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } if (!route) { *(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_DEFAULT; } else if (CFStringHasPrefix(route, CFSTR("Headset"))) { *(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_EARPIECE; } else { *(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_DEFAULT; } CFRelease(route); return PJ_SUCCESS; #endif } else if (cap==PJMEDIA_AUD_DEV_CAP_EC) { AudioComponentDescription desc; OSStatus ostatus; ostatus = AudioComponentGetDescription(strm->cf->io_comp, &desc); if (ostatus != noErr) { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } *(pj_bool_t*)pval = (desc.componentSubType == kAudioUnitSubType_VoiceProcessingIO); return PJ_SUCCESS; #endif } else { return PJMEDIA_EAUD_INVCAP; } } /* API: set capability */ static pj_status_t ca_stream_set_cap(pjmedia_aud_stream *s, pjmedia_aud_dev_cap cap, const void *pval) { struct coreaudio_stream *strm = (struct coreaudio_stream*)s; PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); #if COREAUDIO_MAC if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING && (strm->param.dir & PJMEDIA_DIR_PLAYBACK)) { OSStatus ostatus; Float32 volume = *(unsigned*)pval; /* Output volume setting */ volume /= 100.0; ostatus = AudioUnitSetProperty (strm->io_units[1] ? strm->io_units[1] : strm->io_units[0], kAudioDevicePropertyVolumeScalar, kAudioUnitScope_Output, 0, &volume, sizeof(Float32)); if (ostatus != noErr) { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } strm->param.output_vol = *(unsigned*)pval; return PJ_SUCCESS; } #else if (cap==PJMEDIA_AUD_DEV_CAP_EC) { AudioComponentDescription desc; AudioComponent io_comp; desc.componentType = kAudioUnitType_Output; desc.componentSubType = (*(pj_bool_t*)pval)? kAudioUnitSubType_VoiceProcessingIO : kAudioUnitSubType_RemoteIO; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; io_comp = AudioComponentFindNext(NULL, &desc); if (io_comp == NULL) return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(-1); strm->cf->io_comp = io_comp; strm->param.ec_enabled = *(pj_bool_t*)pval; PJ_LOG(4, (THIS_FILE, "Using %s audio unit", (desc.componentSubType == kAudioUnitSubType_RemoteIO? "RemoteIO": "VoiceProcessingIO"))); return PJ_SUCCESS; } else if ((cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY && (strm->param.dir & PJMEDIA_DIR_CAPTURE)) || (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && (strm->param.dir & PJMEDIA_DIR_PLAYBACK))) { NSTimeInterval duration = *(unsigned *)pval; unsigned latency; /* For low-latency audio streaming, you can set this value to * as low as 5 ms (the default is 23ms). However, lowering the * latency may cause a decrease in audio quality. */ duration /= 1000; if ([strm->sess setPreferredIOBufferDuration:duration error:nil] != YES) { PJ_LOG(4, (THIS_FILE, "Error: cannot set the preferred buffer duration")); return PJMEDIA_EAUD_INVOP; } ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY, &latency); ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY, &latency); return PJ_SUCCESS; } #if USE_AUDIO_SESSION_API != 0 else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE && (strm->param.dir & PJMEDIA_DIR_CAPTURE)) { UInt32 btooth = *(pjmedia_aud_dev_route*)pval == PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH ? 1 : 0; OSStatus ostatus; ostatus = AudioSessionSetProperty ( kAudioSessionProperty_OverrideCategoryEnableBluetoothInput, sizeof(btooth), &btooth); if (ostatus != kAudioSessionNoError) { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } strm->param.input_route = *(pjmedia_aud_dev_route*)pval; return PJ_SUCCESS; } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE && (strm->param.dir & PJMEDIA_DIR_PLAYBACK)) { OSStatus ostatus; UInt32 route = *(pjmedia_aud_dev_route*)pval == PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER ? kAudioSessionOverrideAudioRoute_Speaker : kAudioSessionOverrideAudioRoute_None; ostatus = AudioSessionSetProperty ( kAudioSessionProperty_OverrideAudioRoute, sizeof(route), &route); if (ostatus != kAudioSessionNoError) { return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } strm->param.output_route = *(pjmedia_aud_dev_route*)pval; return PJ_SUCCESS; } #endif #endif return PJMEDIA_EAUD_INVCAP; } /* API: Start stream. */ static pj_status_t ca_stream_start(pjmedia_aud_stream *strm) { struct coreaudio_stream *stream = (struct coreaudio_stream*)strm; OSStatus ostatus; UInt32 i; if (stream->running) return PJ_SUCCESS; stream->quit_flag = 0; stream->interrupted = PJ_FALSE; stream->rec_buf_count = 0; stream->play_buf_count = 0; stream->resample_buf_count = 0; if (stream->resample) { ostatus = AudioConverterReset(stream->resample); if (ostatus != noErr) return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } #if !COREAUDIO_MAC if ([stream->sess setActive:true error:nil] != YES) { PJ_LOG(4, (THIS_FILE, "Warning: cannot activate audio session")); } #endif for (i = 0; i < 2; i++) { if (stream->io_units[i] == NULL) break; ostatus = AudioOutputUnitStart(stream->io_units[i]); if (ostatus != noErr) { if (i == 1) AudioOutputUnitStop(stream->io_units[0]); return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } } stream->running = PJ_TRUE; PJ_LOG(4, (THIS_FILE, "core audio stream started")); return PJ_SUCCESS; } /* API: Stop stream. */ static pj_status_t ca_stream_stop(pjmedia_aud_stream *strm) { struct coreaudio_stream *stream = (struct coreaudio_stream*)strm; OSStatus ostatus; unsigned i; int should_deactivate; struct stream_list *it, *itBegin; if (!stream->running) return PJ_SUCCESS; for (i = 0; i < 2; i++) { if (stream->io_units[i] == NULL) break; ostatus = AudioOutputUnitStop(stream->io_units[i]); if (ostatus != noErr) { if (i == 0 && stream->io_units[1]) AudioOutputUnitStop(stream->io_units[1]); return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); } } /* Check whether we need to deactivate the audio session. */ pj_mutex_lock(stream->cf->mutex); pj_assert(!pj_list_empty(&stream->cf->streams)); pj_assert(!pj_list_empty(&stream->list_entry)); stream->running = PJ_FALSE; should_deactivate = PJ_TRUE; itBegin = &stream->cf->streams; for (it = itBegin->next; it != itBegin; it = it->next) { if (it->stream->running) { should_deactivate = PJ_FALSE; break; } } pj_mutex_unlock(stream->cf->mutex); #if !COREAUDIO_MAC if (should_deactivate) { if ([stream->sess respondsToSelector:@selector(setActive:withOptions:error:)]) { [stream->sess setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil]; } else { if ([stream->sess setActive:NO error:nil] != YES) { PJ_LOG(4, (THIS_FILE, "Warning: cannot deactivate " "audio session")); } } } #endif stream->quit_flag = 1; stream->play_thread_initialized = 0; stream->rec_thread_initialized = 0; pj_bzero(stream->rec_thread_desc, sizeof(pj_thread_desc)); pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc)); PJ_LOG(4, (THIS_FILE, "core audio stream stopped")); return PJ_SUCCESS; } /* API: Destroy stream. */ static pj_status_t ca_stream_destroy(pjmedia_aud_stream *strm) { struct coreaudio_stream *stream = (struct coreaudio_stream*)strm; unsigned i; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); ca_stream_stop(strm); for (i = 0; i < 2; i++) { if (stream->io_units[i]) { AudioUnitUninitialize(stream->io_units[i]); AudioComponentInstanceDispose(stream->io_units[i]); stream->io_units[i] = NULL; } } if (stream->resample) AudioConverterDispose(stream->resample); pj_mutex_lock(stream->cf->mutex); if (!pj_list_empty(&stream->list_entry)) pj_list_erase(&stream->list_entry); pj_mutex_unlock(stream->cf->mutex); pj_pool_release(stream->pool); return PJ_SUCCESS; } #endif /* PJMEDIA_AUDIO_DEV_HAS_COREAUDIO */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-audiodev/errno.c ================================================ /* $Id: errno.c 4432 2013-03-08 08:02:48Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #if PJMEDIA_AUDIO_DEV_HAS_WMME # ifdef _MSC_VER # pragma warning(push, 3) # endif # include # include # ifdef _MSC_VER # pragma warning(pop) # endif #endif /* PJMEDIA-Audiodev's own error codes/messages * MUST KEEP THIS ARRAY SORTED!! * Message must be limited to 64 chars! */ #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) static const struct { int code; const char *msg; } err_str[] = { PJ_BUILD_ERR( PJMEDIA_EAUD_ERR, "Unspecified audio device error" ), PJ_BUILD_ERR( PJMEDIA_EAUD_SYSERR, "Unknown error from audio driver" ), PJ_BUILD_ERR( PJMEDIA_EAUD_INIT, "Audio subsystem not initialized" ), PJ_BUILD_ERR( PJMEDIA_EAUD_INVDEV, "Invalid audio device" ), PJ_BUILD_ERR( PJMEDIA_EAUD_NODEV, "Found no audio devices" ), PJ_BUILD_ERR( PJMEDIA_EAUD_NODEFDEV, "Unable to find default audio device" ), PJ_BUILD_ERR( PJMEDIA_EAUD_NOTREADY, "Audio device not ready" ), PJ_BUILD_ERR( PJMEDIA_EAUD_INVCAP, "Invalid or unsupported audio capability" ), PJ_BUILD_ERR( PJMEDIA_EAUD_INVOP, "Invalid or unsupported audio device operation" ), PJ_BUILD_ERR( PJMEDIA_EAUD_BADFORMAT, "Bad or invalid audio device format" ), PJ_BUILD_ERR( PJMEDIA_EAUD_SAMPFORMAT, "Invalid audio device sample format"), PJ_BUILD_ERR( PJMEDIA_EAUD_BADLATENCY, "Bad audio latency setting") }; #endif /* PJ_HAS_ERROR_STRING */ /* * pjmedia_audiodev_strerror() */ PJ_DEF(pj_str_t) pjmedia_audiodev_strerror(pj_status_t statcode, char *buf, pj_size_t bufsize ) { pj_str_t errstr; #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) /* See if the error comes from Core Audio. */ #if PJMEDIA_AUDIO_DEV_HAS_COREAUDIO if (statcode >= PJMEDIA_AUDIODEV_COREAUDIO_ERRNO_START && statcode <= PJMEDIA_AUDIODEV_COREAUDIO_ERRNO_END) { int ca_err = PJMEDIA_AUDIODEV_COREAUDIO_ERRNO_START - statcode; PJ_UNUSED_ARG(ca_err); // TODO: create more helpful error messages errstr.ptr = buf; pj_strcpy2(&errstr, "Core audio error"); return errstr; } else #endif /* See if the error comes from WMME */ #if PJMEDIA_AUDIO_DEV_HAS_WMME if ((statcode >= PJMEDIA_AUDIODEV_WMME_IN_ERROR_START && statcode < PJMEDIA_AUDIODEV_WMME_IN_ERROR_END) || (statcode >= PJMEDIA_AUDIODEV_WMME_OUT_ERROR_START && statcode < PJMEDIA_AUDIODEV_WMME_OUT_ERROR_END)) { MMRESULT native_err, mr; MMRESULT (WINAPI *waveGetErrText)(UINT mmrError, LPTSTR pszText, UINT cchText); PJ_DECL_UNICODE_TEMP_BUF(wbuf, 80) if (statcode >= PJMEDIA_AUDIODEV_WMME_IN_ERROR_START && statcode <= PJMEDIA_AUDIODEV_WMME_IN_ERROR_END) { native_err = statcode - PJMEDIA_AUDIODEV_WMME_IN_ERROR_START; waveGetErrText = &waveInGetErrorText; } else { native_err = statcode - PJMEDIA_AUDIODEV_WMME_OUT_ERROR_START; waveGetErrText = &waveOutGetErrorText; } #if PJ_NATIVE_STRING_IS_UNICODE mr = (*waveGetErrText)(native_err, wbuf, PJ_ARRAY_SIZE(wbuf)); if (mr == MMSYSERR_NOERROR) { int len = wcslen(wbuf); pj_unicode_to_ansi(wbuf, len, buf, bufsize); } #else mr = (*waveGetErrText)(native_err, buf, (UINT)bufsize); #endif if (mr==MMSYSERR_NOERROR) { errstr.ptr = buf; errstr.slen = pj_ansi_strlen(buf); return errstr; } else { pj_ansi_snprintf(buf, bufsize, "MMSYSTEM native error %d", native_err); return pj_str(buf); } } else #endif /* See if the error comes from BDIMAD */ #if PJMEDIA_AUDIO_DEV_HAS_BDIMAD if (statcode >= PJMEDIA_AUDIODEV_BDIMAD_ERROR_START && statcode < PJMEDIA_AUDIODEV_BDIMAD_ERROR_END) { pj_status_t native_err; native_err = statcode - PJMEDIA_AUDIODEV_BDIMAD_ERROR_START; pj_ansi_snprintf(buf, bufsize, "BDIMAD native error %d", native_err); return pj_str(buf); } else #endif /* Audiodev error */ if (statcode >= PJMEDIA_AUDIODEV_ERRNO_START && statcode < PJMEDIA_AUDIODEV_ERRNO_END) { /* Find the error in the table. * Use binary search! */ int first = 0; int n = PJ_ARRAY_SIZE(err_str); while (n > 0) { int half = n/2; int mid = first + half; if (err_str[mid].code < statcode) { first = mid+1; n -= (half+1); } else if (err_str[mid].code > statcode) { n = half; } else { first = mid; break; } } if (PJ_ARRAY_SIZE(err_str) && err_str[first].code == statcode) { pj_str_t msg; msg.ptr = (char*)err_str[first].msg; msg.slen = pj_ansi_strlen(err_str[first].msg); errstr.ptr = buf; pj_strncpy_with_null(&errstr, &msg, bufsize); return errstr; } } #endif /* PJ_HAS_ERROR_STRING */ /* Error not found. */ errstr.ptr = buf; errstr.slen = pj_ansi_snprintf(buf, bufsize, "Unknown pjmedia-audiodev error %d", statcode); if (errstr.slen < 1 || errstr.slen >= (pj_ssize_t)bufsize) errstr.slen = bufsize - 1; return errstr; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-audiodev/null_dev.c ================================================ /* $Id: null_dev.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #if PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO #define THIS_FILE "null_dev.c" /* null_audio device info */ struct null_audio_dev_info { pjmedia_aud_dev_info info; unsigned dev_id; }; /* null_audio factory */ struct null_audio_factory { pjmedia_aud_dev_factory base; pj_pool_t *pool; pj_pool_factory *pf; unsigned dev_count; struct null_audio_dev_info *dev_info; }; /* Sound stream. */ struct null_audio_stream { pjmedia_aud_stream base; /**< Base stream */ pjmedia_aud_param param; /**< Settings */ pj_pool_t *pool; /**< Memory pool. */ pjmedia_aud_rec_cb rec_cb; /**< Capture callback. */ pjmedia_aud_play_cb play_cb; /**< Playback callback. */ void *user_data; /**< Application data. */ }; /* Prototypes */ static pj_status_t null_factory_init(pjmedia_aud_dev_factory *f); static pj_status_t null_factory_destroy(pjmedia_aud_dev_factory *f); static pj_status_t null_factory_refresh(pjmedia_aud_dev_factory *f); static unsigned null_factory_get_dev_count(pjmedia_aud_dev_factory *f); static pj_status_t null_factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_dev_info *info); static pj_status_t null_factory_default_param(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_param *param); static pj_status_t null_factory_create_stream(pjmedia_aud_dev_factory *f, const pjmedia_aud_param *param, pjmedia_aud_rec_cb rec_cb, pjmedia_aud_play_cb play_cb, void *user_data, pjmedia_aud_stream **p_aud_strm); static pj_status_t null_stream_get_param(pjmedia_aud_stream *strm, pjmedia_aud_param *param); static pj_status_t null_stream_get_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, void *value); static pj_status_t null_stream_set_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, const void *value); static pj_status_t null_stream_start(pjmedia_aud_stream *strm); static pj_status_t null_stream_stop(pjmedia_aud_stream *strm); static pj_status_t null_stream_destroy(pjmedia_aud_stream *strm); /* Operations */ static pjmedia_aud_dev_factory_op factory_op = { &null_factory_init, &null_factory_destroy, &null_factory_get_dev_count, &null_factory_get_dev_info, &null_factory_default_param, &null_factory_create_stream, &null_factory_refresh }; static pjmedia_aud_stream_op stream_op = { &null_stream_get_param, &null_stream_get_cap, &null_stream_set_cap, &null_stream_start, &null_stream_stop, &null_stream_destroy }; /**************************************************************************** * Factory operations */ /* * Init null_audio audio driver. */ pjmedia_aud_dev_factory* pjmedia_null_audio_factory(pj_pool_factory *pf) { struct null_audio_factory *f; pj_pool_t *pool; pool = pj_pool_create(pf, "null audio", 1000, 1000, NULL); f = PJ_POOL_ZALLOC_T(pool, struct null_audio_factory); f->pf = pf; f->pool = pool; f->base.op = &factory_op; return &f->base; } /* API: init factory */ static pj_status_t null_factory_init(pjmedia_aud_dev_factory *f) { struct null_audio_factory *nf = (struct null_audio_factory*)f; struct null_audio_dev_info *ndi; /* Initialize input and output devices here */ nf->dev_count = 1; nf->dev_info = (struct null_audio_dev_info*) pj_pool_calloc(nf->pool, nf->dev_count, sizeof(struct null_audio_dev_info)); ndi = &nf->dev_info[0]; pj_bzero(ndi, sizeof(*ndi)); strcpy(ndi->info.name, "null device"); strcpy(ndi->info.driver, "null"); ndi->info.input_count = 1; ndi->info.output_count = 1; ndi->info.default_samples_per_sec = 16000; /* Set the device capabilities here */ ndi->info.caps = 0; PJ_LOG(4, (THIS_FILE, "null audio initialized")); return PJ_SUCCESS; } /* API: destroy factory */ static pj_status_t null_factory_destroy(pjmedia_aud_dev_factory *f) { struct null_audio_factory *nf = (struct null_audio_factory*)f; pj_pool_t *pool = nf->pool; nf->pool = NULL; pj_pool_release(pool); return PJ_SUCCESS; } /* API: refresh the list of devices */ static pj_status_t null_factory_refresh(pjmedia_aud_dev_factory *f) { PJ_UNUSED_ARG(f); return PJ_SUCCESS; } /* API: get number of devices */ static unsigned null_factory_get_dev_count(pjmedia_aud_dev_factory *f) { struct null_audio_factory *nf = (struct null_audio_factory*)f; return nf->dev_count; } /* API: get device info */ static pj_status_t null_factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_dev_info *info) { struct null_audio_factory *nf = (struct null_audio_factory*)f; PJ_ASSERT_RETURN(index < nf->dev_count, PJMEDIA_EAUD_INVDEV); pj_memcpy(info, &nf->dev_info[index].info, sizeof(*info)); return PJ_SUCCESS; } /* API: create default device parameter */ static pj_status_t null_factory_default_param(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_param *param) { struct null_audio_factory *nf = (struct null_audio_factory*)f; struct null_audio_dev_info *di = &nf->dev_info[index]; PJ_ASSERT_RETURN(index < nf->dev_count, PJMEDIA_EAUD_INVDEV); pj_bzero(param, sizeof(*param)); if (di->info.input_count && di->info.output_count) { param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; param->rec_id = index; param->play_id = index; } else if (di->info.input_count) { param->dir = PJMEDIA_DIR_CAPTURE; param->rec_id = index; param->play_id = PJMEDIA_AUD_INVALID_DEV; } else if (di->info.output_count) { param->dir = PJMEDIA_DIR_PLAYBACK; param->play_id = index; param->rec_id = PJMEDIA_AUD_INVALID_DEV; } else { return PJMEDIA_EAUD_INVDEV; } /* Set the mandatory settings here */ /* The values here are just some examples */ param->clock_rate = di->info.default_samples_per_sec; param->channel_count = 1; param->samples_per_frame = di->info.default_samples_per_sec * 20 / 1000; param->bits_per_sample = 16; /* Set the device capabilities here */ param->flags = 0; return PJ_SUCCESS; } /* API: create stream */ static pj_status_t null_factory_create_stream(pjmedia_aud_dev_factory *f, const pjmedia_aud_param *param, pjmedia_aud_rec_cb rec_cb, pjmedia_aud_play_cb play_cb, void *user_data, pjmedia_aud_stream **p_aud_strm) { struct null_audio_factory *nf = (struct null_audio_factory*)f; pj_pool_t *pool; struct null_audio_stream *strm; /* Create and Initialize stream descriptor */ pool = pj_pool_create(nf->pf, "null_audio-dev", 1000, 1000, NULL); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); strm = PJ_POOL_ZALLOC_T(pool, struct null_audio_stream); pj_memcpy(&strm->param, param, sizeof(*param)); strm->pool = pool; strm->rec_cb = rec_cb; strm->play_cb = play_cb; strm->user_data = user_data; /* Create player stream here */ if (param->dir & PJMEDIA_DIR_PLAYBACK) { } /* Create capture stream here */ if (param->dir & PJMEDIA_DIR_CAPTURE) { } /* Apply the remaining settings */ /* Below is an example if you want to set the output volume */ if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) { null_stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, ¶m->output_vol); } /* Done */ strm->base.op = &stream_op; *p_aud_strm = &strm->base; return PJ_SUCCESS; } /* API: Get stream info. */ static pj_status_t null_stream_get_param(pjmedia_aud_stream *s, pjmedia_aud_param *pi) { struct null_audio_stream *strm = (struct null_audio_stream*)s; PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); pj_memcpy(pi, &strm->param, sizeof(*pi)); /* Example: Update the volume setting */ if (null_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, &pi->output_vol) == PJ_SUCCESS) { pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING; } return PJ_SUCCESS; } /* API: get capability */ static pj_status_t null_stream_get_cap(pjmedia_aud_stream *s, pjmedia_aud_dev_cap cap, void *pval) { struct null_audio_stream *strm = (struct null_audio_stream*)s; PJ_UNUSED_ARG(strm); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); /* Example: Get the output's volume setting */ if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) { /* Output volume setting */ *(unsigned*)pval = 0; // retrieve output device's volume here return PJ_SUCCESS; } else { return PJMEDIA_EAUD_INVCAP; } } /* API: set capability */ static pj_status_t null_stream_set_cap(pjmedia_aud_stream *s, pjmedia_aud_dev_cap cap, const void *pval) { struct null_audio_stream *strm = (struct null_audio_stream*)s; PJ_UNUSED_ARG(strm); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); /* Example */ if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) { /* Output volume setting */ // set output's volume level here return PJ_SUCCESS; } return PJMEDIA_EAUD_INVCAP; } /* API: Start stream. */ static pj_status_t null_stream_start(pjmedia_aud_stream *strm) { struct null_audio_stream *stream = (struct null_audio_stream*)strm; PJ_UNUSED_ARG(stream); PJ_LOG(4, (THIS_FILE, "Starting null audio stream")); return PJ_SUCCESS; } /* API: Stop stream. */ static pj_status_t null_stream_stop(pjmedia_aud_stream *strm) { struct null_audio_stream *stream = (struct null_audio_stream*)strm; PJ_UNUSED_ARG(stream); PJ_LOG(4, (THIS_FILE, "Stopping null audio stream")); return PJ_SUCCESS; } /* API: Destroy stream. */ static pj_status_t null_stream_destroy(pjmedia_aud_stream *strm) { struct null_audio_stream *stream = (struct null_audio_stream*)strm; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); null_stream_stop(strm); pj_pool_release(stream->pool); return PJ_SUCCESS; } #endif /* PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-audiodev/wmme_dev.c ================================================ /* $Id: wmme_dev.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #if PJMEDIA_AUDIO_DEV_HAS_WMME #ifdef _MSC_VER # pragma warning(push, 3) #endif #include #include #include #include #ifdef _MSC_VER # pragma warning(pop) #endif #ifndef PJMEDIA_WMME_DEV_USE_MMDEVICE_API # if defined(_WIN32_WINNT) && (_WIN32_WINNT>=0x0600) # define PJMEDIA_WMME_DEV_USE_MMDEVICE_API 1 # else # define PJMEDIA_WMME_DEV_USE_MMDEVICE_API 0 # endif #endif #if PJMEDIA_WMME_DEV_USE_MMDEVICE_API != 0 # define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17) # define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18) #endif /* mingw lacks WAVE_FORMAT_ALAW/MULAW */ #ifndef WAVE_FORMAT_ALAW # define WAVE_FORMAT_ALAW 0x0006 #endif #ifndef WAVE_FORMAT_MULAW # define WAVE_FORMAT_MULAW 0x0007 #endif #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 # pragma comment(lib, "Coredll.lib") #elif defined(_MSC_VER) # pragma comment(lib, "winmm.lib") #endif #define THIS_FILE "wmme_dev.c" /* WMME device change observer */ struct wmme_dev_observer { pj_thread_t *thread; pj_pool_t *pool; pjmedia_aud_dev_change_callback cb; HWND hWnd; }; /* WMME device info */ struct wmme_dev_info { pjmedia_aud_dev_info info; unsigned deviceId; const wchar_t *endpointId; }; /* WMME factory */ struct wmme_factory { pjmedia_aud_dev_factory base; pj_pool_t *base_pool; pj_pool_t *pool; pj_pool_factory *pf; unsigned dev_count; struct wmme_dev_info *dev_info; struct wmme_dev_observer dev_observer; }; /* Individual WMME capture/playback stream descriptor */ struct wmme_channel { union { HWAVEIN In; HWAVEOUT Out; } hWave; WAVEHDR *WaveHdr; HANDLE hEvent; DWORD dwBufIdx; DWORD dwMaxBufIdx; pj_timestamp timestamp; }; /* Sound stream. */ struct wmme_stream { pjmedia_aud_stream base; /**< Base stream */ pjmedia_aud_param param; /**< Settings */ pj_pool_t *pool; /**< Memory pool. */ pjmedia_aud_rec_cb rec_cb; /**< Capture callback. */ pjmedia_aud_play_cb play_cb; /**< Playback callback. */ void *user_data; /**< Application data. */ struct wmme_channel play_strm; /**< Playback stream. */ struct wmme_channel rec_strm; /**< Capture stream. */ void *buffer; /**< Temp. frame buffer. */ pjmedia_format_id fmt_id; /**< Frame format */ pj_uint8_t silence_char; /**< Silence pattern */ unsigned bytes_per_frame; /**< Bytes per frame */ pjmedia_frame_ext *xfrm; /**< Extended frame buffer */ unsigned xfrm_size; /**< Total ext frm size */ pj_thread_t *thread; /**< Thread handle. */ HANDLE thread_quit_event; /**< Quit signal to thread */ }; /* Prototypes */ static pj_status_t factory_init(pjmedia_aud_dev_factory *f); static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f); static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f); static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f); static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_dev_info *info); static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_param *param); static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f, const pjmedia_aud_param *param, pjmedia_aud_rec_cb rec_cb, pjmedia_aud_play_cb play_cb, void *user_data, pjmedia_aud_stream **p_aud_strm); static void factory_set_observer(pjmedia_aud_dev_factory *f, pjmedia_aud_dev_change_callback cb); static int factory_get_default_rec_dev(pjmedia_aud_dev_factory *f); static int factory_get_default_play_dev(pjmedia_aud_dev_factory *f); static pj_status_t stream_get_param(pjmedia_aud_stream *strm, pjmedia_aud_param *param); static pj_status_t stream_get_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, void *value); static pj_status_t stream_set_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, const void *value); static pj_status_t stream_start(pjmedia_aud_stream *strm); static pj_status_t stream_stop(pjmedia_aud_stream *strm); static pj_status_t stream_destroy(pjmedia_aud_stream *strm); /* Operations */ static pjmedia_aud_dev_factory_op factory_op = { &factory_init, &factory_destroy, &factory_get_dev_count, &factory_get_dev_info, &factory_default_param, &factory_create_stream, &factory_refresh, &factory_set_observer, &factory_get_default_rec_dev, &factory_get_default_play_dev }; static pjmedia_aud_stream_op stream_op = { &stream_get_param, &stream_get_cap, &stream_set_cap, &stream_start, &stream_stop, &stream_destroy }; /**************************************************************************** * Factory operations */ /* * Init WMME audio driver. */ pjmedia_aud_dev_factory* pjmedia_wmme_factory(pj_pool_factory *pf) { struct wmme_factory *f; pj_pool_t *pool; pool = pj_pool_create(pf, "WMME base", 1000, 1000, NULL); f = PJ_POOL_ZALLOC_T(pool, struct wmme_factory); f->pf = pf; f->base_pool = pool; f->base.op = &factory_op; return &f->base; } /* Internal: Windows Vista and Windows 7 have their device * names truncated when using the waveXXX api. The names * should be acquired from the MMDevice APIs */ #if PJMEDIA_WMME_DEV_USE_MMDEVICE_API != 0 #define COBJMACROS #include #define INITGUID #include #include DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E); DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6); static void get_dev_names(pjmedia_aud_dev_factory *f) { struct wmme_factory *wf = (struct wmme_factory*)f; HRESULT coinit = S_OK; HRESULT hr = S_OK; IMMDeviceEnumerator *pEnumerator = NULL; IMMDeviceCollection *pDevices = NULL; UINT cDevices = 0; UINT nDevice = 0; coinit = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (coinit == RPC_E_CHANGED_MODE) coinit = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (FAILED(coinit)) goto on_error; hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&pEnumerator); if (FAILED(hr)) goto on_error; hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eAll, DEVICE_STATE_ACTIVE, &pDevices); if (FAILED(hr)) goto on_error; hr = IMMDeviceCollection_GetCount(pDevices, &cDevices); if (FAILED(hr)) goto on_error; for (nDevice = 0; nDevice < cDevices; ++nDevice) { IMMDevice *pDevice = NULL; IPropertyStore *pProps = NULL; LPWSTR pwszID = NULL; PROPVARIANT varName; unsigned i; PropVariantInit(&varName); hr = IMMDeviceCollection_Item(pDevices, nDevice, &pDevice); if (FAILED(hr)) goto cleanup; hr = IMMDevice_GetId(pDevice, &pwszID); if (FAILED(hr)) goto cleanup; hr = IMMDevice_OpenPropertyStore(pDevice, STGM_READ, &pProps); if (FAILED(hr)) goto cleanup; hr = IPropertyStore_GetValue(pProps, &PKEY_Device_FriendlyName, &varName); if (FAILED(hr)) goto cleanup; for (i = 0; i < wf->dev_count; ++i) { if (0 == wcscmp(wf->dev_info[i].endpointId, pwszID)) { pj_unicode_to_ansi(varName.pwszVal, wcslen(varName.pwszVal), wf->dev_info[i].info.name, sizeof(wf->dev_info[i].info.name)); break; } } PropVariantClear(&varName); cleanup: if (pProps) IPropertyStore_Release(pProps); if (pwszID) CoTaskMemFree(pwszID); if (pDevice) hr = IMMDevice_Release(pDevice); } on_error: if (pDevices) hr = IMMDeviceCollection_Release(pDevices); if (pEnumerator) hr = IMMDeviceEnumerator_Release(pEnumerator); if (SUCCEEDED(coinit)) CoUninitialize(); } #else static void get_dev_names(pjmedia_aud_dev_factory *f) { PJ_UNUSED_ARG(f); } #endif /* Internal: build device info from WAVEINCAPS/WAVEOUTCAPS */ static void build_dev_info(UINT deviceId, struct wmme_dev_info *wdi, const WAVEINCAPS *wic, const WAVEOUTCAPS *woc) { #define WIC_WOC(wic,woc,field) (wic? wic->field : woc->field) pj_bzero(wdi, sizeof(*wdi)); wdi->deviceId = deviceId; /* Device Name */ if (deviceId==WAVE_MAPPER) { strncpy(wdi->info.name, "Wave mapper", sizeof(wdi->info.name)); wdi->info.name[sizeof(wdi->info.name)-1] = '\0'; } else { const pj_char_t *szPname = WIC_WOC(wic, woc, szPname); PJ_DECL_ANSI_TEMP_BUF(wTmp, sizeof(wdi->info.name)); strncpy(wdi->info.name, PJ_NATIVE_TO_STRING(szPname, wTmp, PJ_ARRAY_SIZE(wTmp)), sizeof(wdi->info.name)); wdi->info.name[sizeof(wdi->info.name)-1] = '\0'; } wdi->info.default_samples_per_sec = 16000; strcpy(wdi->info.driver, "WMME"); if (wic) { wdi->info.input_count = wic->wChannels; wdi->info.caps |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY; /* Sometimes a device can return a rediculously large number of * channels. This happened with an SBLive card on a Windows ME box. * It also happens on Win XP! */ if (wdi->info.input_count<1 || wdi->info.input_count>256) { wdi->info.input_count = 2; } } if (woc) { wdi->info.output_count = woc->wChannels; wdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; if (woc->dwSupport & WAVECAPS_VOLUME) { wdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING; } /* Sometimes a device can return a rediculously large number of * channels. This happened with an SBLive card on a Windows ME box. * It also happens on Win XP! */ if (wdi->info.output_count<1 || wdi->info.output_count>256) { wdi->info.output_count = 2; } } /* Extended formats */ wdi->info.caps |= PJMEDIA_AUD_DEV_CAP_EXT_FORMAT; wdi->info.ext_fmt_cnt = 2; pjmedia_format_init_audio(&wdi->info.ext_fmt[0], PJMEDIA_FORMAT_PCMU, 8000, 1, 8, 20000, 64000, 64000); pjmedia_format_init_audio(&wdi->info.ext_fmt[0], PJMEDIA_FORMAT_PCMA, 8000, 1, 8, 20000, 64000, 64000); } /* API: init factory */ static pj_status_t factory_init(pjmedia_aud_dev_factory *f) { pj_status_t ret = factory_refresh(f); if (ret != PJ_SUCCESS) return ret; PJ_LOG(4, (THIS_FILE, "WMME initialized")); return PJ_SUCCESS; } /* API: refresh the device list */ static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f) { struct wmme_factory *wf = (struct wmme_factory*)f; unsigned c; int i; int inputDeviceCount, outputDeviceCount, devCount=0; pj_bool_t waveMapperAdded = PJ_FALSE; if (wf->pool != NULL) { pj_pool_release(wf->pool); wf->pool = NULL; } /* Enumerate sound devices */ wf->dev_count = 0; wf->pool = pj_pool_create(wf->pf, "WMME", 1000, 1000, NULL); inputDeviceCount = waveInGetNumDevs(); devCount += inputDeviceCount; outputDeviceCount = waveOutGetNumDevs(); devCount += outputDeviceCount; if (devCount) { /* Assume there is WAVE_MAPPER */ devCount += 2; } if (devCount==0) { PJ_LOG(4,(THIS_FILE, "WMME found no sound devices")); /* Enabling this will cause pjsua-lib initialization to fail when there * is no sound device installed in the system, even when pjsua has been * run with --null-audio. Moreover, it might be better to think that * the WMME backend initialization is successfull, regardless there is * no audio device installed, as later application can check it using * get_dev_count(). return PJMEDIA_EAUD_NODEV; */ return PJ_SUCCESS; } wf->dev_info = (struct wmme_dev_info*) pj_pool_calloc(wf->pool, devCount, sizeof(struct wmme_dev_info)); if (inputDeviceCount && outputDeviceCount) { /* Attempt to add WAVE_MAPPER as input and output device */ WAVEINCAPS wic; MMRESULT mr; pj_bzero(&wic, sizeof(WAVEINCAPS)); mr = waveInGetDevCaps(WAVE_MAPPER, &wic, sizeof(WAVEINCAPS)); if (mr == MMSYSERR_NOERROR) { WAVEOUTCAPS woc; pj_bzero(&woc, sizeof(WAVEOUTCAPS)); mr = waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(WAVEOUTCAPS)); if (mr == MMSYSERR_NOERROR) { build_dev_info(WAVE_MAPPER, &wf->dev_info[wf->dev_count], &wic, &woc); wf->dev_info[wf->dev_count].endpointId = L""; ++wf->dev_count; waveMapperAdded = PJ_TRUE; } } } if (inputDeviceCount > 0) { /* -1 is the WAVE_MAPPER */ for (i = (waveMapperAdded? 0 : -1); i < inputDeviceCount; ++i) { UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i); WAVEINCAPS wic; MMRESULT mr; DWORD cbEndpointId; pj_bzero(&wic, sizeof(WAVEINCAPS)); mr = waveInGetDevCaps(uDeviceID, &wic, sizeof(WAVEINCAPS)); if (mr == MMSYSERR_NOMEM) return PJ_ENOMEM; if (mr != MMSYSERR_NOERROR) continue; build_dev_info(uDeviceID, &wf->dev_info[wf->dev_count], &wic, NULL); #if PJMEDIA_WMME_DEV_USE_MMDEVICE_API != 0 /* Try to get the endpoint id of the audio device */ wf->dev_info[wf->dev_count].endpointId = L""; mr = waveInMessage((HWAVEIN)IntToPtr(uDeviceID), DRV_QUERYFUNCTIONINSTANCEIDSIZE, (DWORD_PTR)&cbEndpointId, (DWORD_PTR)NULL); if (mr == MMSYSERR_NOERROR) { const wchar_t **epid = &wf->dev_info[wf->dev_count].endpointId; *epid = (const wchar_t*) pj_pool_calloc(wf->pool, cbEndpointId, 1); mr = waveInMessage((HWAVEIN)IntToPtr(uDeviceID), DRV_QUERYFUNCTIONINSTANCEID, (DWORD_PTR)*epid, cbEndpointId); } #else PJ_UNUSED_ARG(cbEndpointId); #endif ++wf->dev_count; } } if( outputDeviceCount > 0 ) { /* -1 is the WAVE_MAPPER */ for (i = (waveMapperAdded? 0 : -1); i < outputDeviceCount; ++i) { UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i); WAVEOUTCAPS woc; MMRESULT mr; DWORD cbEndpointId; pj_bzero(&woc, sizeof(WAVEOUTCAPS)); mr = waveOutGetDevCaps(uDeviceID, &woc, sizeof(WAVEOUTCAPS)); if (mr == MMSYSERR_NOMEM) return PJ_ENOMEM; if (mr != MMSYSERR_NOERROR) continue; build_dev_info(uDeviceID, &wf->dev_info[wf->dev_count], NULL, &woc); #if PJMEDIA_WMME_DEV_USE_MMDEVICE_API != 0 /* Try to get the endpoint id of the audio device */ wf->dev_info[wf->dev_count].endpointId = L""; mr = waveOutMessage((HWAVEOUT)IntToPtr(uDeviceID), DRV_QUERYFUNCTIONINSTANCEIDSIZE, (DWORD_PTR)&cbEndpointId, (DWORD_PTR)NULL); if (mr == MMSYSERR_NOERROR) { const wchar_t **epid = &wf->dev_info[wf->dev_count].endpointId; *epid = (const wchar_t*)pj_pool_calloc(wf->pool, cbEndpointId, 1); mr = waveOutMessage((HWAVEOUT)IntToPtr(uDeviceID), DRV_QUERYFUNCTIONINSTANCEID, (DWORD_PTR)*epid, cbEndpointId); } #else PJ_UNUSED_ARG(cbEndpointId); #endif ++wf->dev_count; } } /* On Windows Vista and Windows 7 get the full device names */ get_dev_names(f); PJ_LOG(4, (THIS_FILE, "WMME found %d devices:", wf->dev_count)); for (c = 0; c < wf->dev_count; ++c) { PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d)", c, wf->dev_info[c].info.name, wf->dev_info[c].info.input_count, wf->dev_info[c].info.output_count)); } return PJ_SUCCESS; } /* API: destroy factory */ static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f) { struct wmme_factory *wf = (struct wmme_factory*)f; pj_pool_t *pool = wf->base_pool; pj_pool_release(wf->pool); wf->base_pool = NULL; pj_pool_release(pool); return PJ_SUCCESS; } /* API: get number of devices */ static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f) { struct wmme_factory *wf = (struct wmme_factory*)f; return wf->dev_count; } /* API: get device info */ static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_dev_info *info) { struct wmme_factory *wf = (struct wmme_factory*)f; PJ_ASSERT_RETURN(index < wf->dev_count, PJMEDIA_EAUD_INVDEV); pj_memcpy(info, &wf->dev_info[index].info, sizeof(*info)); return PJ_SUCCESS; } /* API: create default device parameter */ static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_param *param) { struct wmme_factory *wf = (struct wmme_factory*)f; struct wmme_dev_info *di = &wf->dev_info[index]; PJ_ASSERT_RETURN(index < wf->dev_count, PJMEDIA_EAUD_INVDEV); pj_bzero(param, sizeof(*param)); if (di->info.input_count && di->info.output_count) { param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; param->rec_id = index; param->play_id = index; } else if (di->info.input_count) { param->dir = PJMEDIA_DIR_CAPTURE; param->rec_id = index; param->play_id = PJMEDIA_AUD_INVALID_DEV; } else if (di->info.output_count) { param->dir = PJMEDIA_DIR_PLAYBACK; param->play_id = index; param->rec_id = PJMEDIA_AUD_INVALID_DEV; } else { return PJMEDIA_EAUD_INVDEV; } param->clock_rate = di->info.default_samples_per_sec; param->channel_count = 1; param->samples_per_frame = di->info.default_samples_per_sec * 20 / 1000; param->bits_per_sample = 16; param->flags = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY; param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY; return PJ_SUCCESS; } /* Internal: init WAVEFORMATEX */ static pj_status_t init_waveformatex(LPWAVEFORMATEX wfx, const pjmedia_aud_param *prm) { pj_bzero(wfx, sizeof(WAVEFORMATEX)); if (prm->ext_fmt.id == PJMEDIA_FORMAT_L16) { enum { BYTES_PER_SAMPLE = 2 }; wfx->wFormatTag = WAVE_FORMAT_PCM; wfx->nChannels = (pj_uint16_t)prm->channel_count; wfx->nSamplesPerSec = prm->clock_rate; wfx->nBlockAlign = (pj_uint16_t)(prm->channel_count * BYTES_PER_SAMPLE); wfx->nAvgBytesPerSec = prm->clock_rate * prm->channel_count * BYTES_PER_SAMPLE; wfx->wBitsPerSample = 16; return PJ_SUCCESS; } else if ((prm->flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) && (prm->ext_fmt.id == PJMEDIA_FORMAT_PCMA || prm->ext_fmt.id == PJMEDIA_FORMAT_PCMU)) { unsigned ptime; ptime = prm->samples_per_frame * 1000 / (prm->clock_rate * prm->channel_count); wfx->wFormatTag = (pj_uint16_t) ((prm->ext_fmt.id==PJMEDIA_FORMAT_PCMA) ? WAVE_FORMAT_ALAW : WAVE_FORMAT_MULAW); wfx->nChannels = (pj_uint16_t)prm->channel_count; wfx->nSamplesPerSec = prm->clock_rate; wfx->nAvgBytesPerSec = prm->clock_rate * prm->channel_count; wfx->nBlockAlign = (pj_uint16_t)(wfx->nAvgBytesPerSec * ptime / 1000); wfx->wBitsPerSample = 8; wfx->cbSize = 0; return PJ_SUCCESS; } else { return PJMEDIA_EAUD_BADFORMAT; } } /* Get format name */ static const char *get_fmt_name(pj_uint32_t id) { static char name[8]; if (id == PJMEDIA_FORMAT_L16) return "PCM"; pj_memcpy(name, &id, 4); name[4] = '\0'; return name; } /* Internal: create WMME player device. */ static pj_status_t init_player_stream( struct wmme_factory *wf, pj_pool_t *pool, struct wmme_stream *parent, struct wmme_channel *wmme_strm, const pjmedia_aud_param *prm, unsigned buffer_count) { MMRESULT mr; WAVEFORMATEX wfx; unsigned i, ptime; DWORD flag; pj_status_t status; PJ_ASSERT_RETURN(prm->play_id < (int)wf->dev_count, PJ_EINVAL); /* * Create a wait event. */ wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (NULL == wmme_strm->hEvent) return pj_get_os_error(); /* * Set up wave format structure for opening the device. */ status = init_waveformatex(&wfx, prm); if (status != PJ_SUCCESS) return status; ptime = prm->samples_per_frame * 1000 / (prm->clock_rate * prm->channel_count); parent->bytes_per_frame = wfx.nAvgBytesPerSec * ptime / 1000; flag = CALLBACK_EVENT; if (prm->ext_fmt.id == PJMEDIA_FORMAT_L16) flag |= WAVE_FORMAT_DIRECT; /* * Open wave device. */ mr = waveOutOpen(&wmme_strm->hWave.Out, wf->dev_info[prm->play_id].deviceId, &wfx, (DWORD_PTR)wmme_strm->hEvent, 0, flag); if (mr != MMSYSERR_NOERROR) { return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr); } /* Pause the wave out device */ mr = waveOutPause(wmme_strm->hWave.Out); if (mr != MMSYSERR_NOERROR) { return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr); } /* * Create the buffers. */ wmme_strm->WaveHdr = (WAVEHDR*) pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count); for (i = 0; i < buffer_count; ++i) { wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool, parent->bytes_per_frame); wmme_strm->WaveHdr[i].dwBufferLength = parent->bytes_per_frame; mr = waveOutPrepareHeader(wmme_strm->hWave.Out, &(wmme_strm->WaveHdr[i]), sizeof(WAVEHDR)); if (mr != MMSYSERR_NOERROR) { return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr); } mr = waveOutWrite(wmme_strm->hWave.Out, &(wmme_strm->WaveHdr[i]), sizeof(WAVEHDR)); if (mr != MMSYSERR_NOERROR) { return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr); } } wmme_strm->dwBufIdx = 0; wmme_strm->dwMaxBufIdx = buffer_count; wmme_strm->timestamp.u64 = 0; /* Done setting up play device. */ PJ_LOG(4, (THIS_FILE, " WaveAPI Sound player \"%s\" initialized (" "format=%s, clock_rate=%d, " "channel_count=%d, samples_per_frame=%d (%dms))", wf->dev_info[prm->play_id].info.name, get_fmt_name(prm->ext_fmt.id), prm->clock_rate, prm->channel_count, prm->samples_per_frame, prm->samples_per_frame * 1000 / prm->clock_rate)); return PJ_SUCCESS; } /* Internal: create Windows Multimedia recorder device */ static pj_status_t init_capture_stream( struct wmme_factory *wf, pj_pool_t *pool, struct wmme_stream *parent, struct wmme_channel *wmme_strm, const pjmedia_aud_param *prm, unsigned buffer_count) { MMRESULT mr; WAVEFORMATEX wfx; DWORD flag; unsigned i, ptime; PJ_ASSERT_RETURN(prm->rec_id < (int)wf->dev_count, PJ_EINVAL); /* * Create a wait event. */ wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (NULL == wmme_strm->hEvent) { return pj_get_os_error(); } /* * Set up wave format structure for opening the device. */ init_waveformatex(&wfx, prm); ptime = prm->samples_per_frame * 1000 / (prm->clock_rate * prm->channel_count); parent->bytes_per_frame = wfx.nAvgBytesPerSec * ptime / 1000; flag = CALLBACK_EVENT; if (prm->ext_fmt.id == PJMEDIA_FORMAT_L16) flag |= WAVE_FORMAT_DIRECT; /* * Open wave device. */ mr = waveInOpen(&wmme_strm->hWave.In, wf->dev_info[prm->rec_id].deviceId, &wfx, (DWORD_PTR)wmme_strm->hEvent, 0, flag); if (mr != MMSYSERR_NOERROR) { return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr); } /* * Create the buffers. */ wmme_strm->WaveHdr = (WAVEHDR*) pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count); for (i = 0; i < buffer_count; ++i) { wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool, parent->bytes_per_frame); wmme_strm->WaveHdr[i].dwBufferLength = parent->bytes_per_frame; mr = waveInPrepareHeader(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[i]), sizeof(WAVEHDR)); if (mr != MMSYSERR_NOERROR) { return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr); } mr = waveInAddBuffer(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[i]), sizeof(WAVEHDR)); if (mr != MMSYSERR_NOERROR) { return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr); } } wmme_strm->dwBufIdx = 0; wmme_strm->dwMaxBufIdx = buffer_count; wmme_strm->timestamp.u64 = 0; /* Done setting up play device. */ PJ_LOG(4,(THIS_FILE, " WaveAPI Sound recorder \"%s\" initialized " "(format=%s, clock_rate=%d, " "channel_count=%d, samples_per_frame=%d (%dms))", wf->dev_info[prm->rec_id].info.name, get_fmt_name(prm->ext_fmt.id), prm->clock_rate, prm->channel_count, prm->samples_per_frame, prm->samples_per_frame * 1000 / prm->clock_rate)); return PJ_SUCCESS; } /* WMME capture and playback thread. */ static int PJ_THREAD_FUNC wmme_dev_thread(void *arg) { struct wmme_stream *strm = (struct wmme_stream*)arg; HANDLE events[3]; unsigned eventCount; pj_status_t status = PJ_SUCCESS; static unsigned rec_cnt, play_cnt; enum { MAX_BURST = 1000 }; /* Suppress compile warning for unused debugging vars */ PJ_UNUSED_ARG(rec_cnt); PJ_UNUSED_ARG(play_cnt); rec_cnt = play_cnt = 0; eventCount = 0; events[eventCount++] = strm->thread_quit_event; if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) events[eventCount++] = strm->play_strm.hEvent; if (strm->param.dir & PJMEDIA_DIR_CAPTURE) events[eventCount++] = strm->rec_strm.hEvent; /* Raise self priority. We don't want the audio to be distorted by * system activity. */ #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) CeSetThreadPriority(GetCurrentThread(), 153); else CeSetThreadPriority(GetCurrentThread(), 247); #else SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); #endif /* * Loop while not signalled to quit, wait for event objects to be * signalled by WMME capture and play buffer. */ while (status == PJ_SUCCESS) { DWORD rc; pjmedia_dir signalled_dir; /* Swap hWaveIn and hWaveOut to get equal opportunity for both */ if (eventCount==3) { HANDLE hTemp = events[2]; events[2] = events[1]; events[1] = hTemp; } rc = WaitForMultipleObjects(eventCount, events, FALSE, INFINITE); if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0 + eventCount) continue; if (rc == WAIT_OBJECT_0) break; if (rc == (WAIT_OBJECT_0 + 1)) { if (events[1] == strm->play_strm.hEvent) signalled_dir = PJMEDIA_DIR_PLAYBACK; else signalled_dir = PJMEDIA_DIR_CAPTURE; } else { if (events[2] == strm->play_strm.hEvent) signalled_dir = PJMEDIA_DIR_PLAYBACK; else signalled_dir = PJMEDIA_DIR_CAPTURE; } if (signalled_dir == PJMEDIA_DIR_PLAYBACK) { struct wmme_channel *wmme_strm = &strm->play_strm; unsigned burst; status = PJ_SUCCESS; /* * Windows Multimedia has requested us to feed some frames to * playback buffer. */ for (burst=0; burstWaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE); ++burst) { void *buffer = wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData; pjmedia_frame pcm_frame, *frame; MMRESULT mr = MMSYSERR_NOERROR; //PJ_LOG(5,(THIS_FILE, "Finished writing buffer %d", // wmme_strm->dwBufIdx)); if (strm->fmt_id == PJMEDIA_FORMAT_L16) { /* PCM mode */ frame = &pcm_frame; frame->type = PJMEDIA_FRAME_TYPE_AUDIO; frame->size = strm->bytes_per_frame; frame->buf = buffer; frame->timestamp.u64 = wmme_strm->timestamp.u64; frame->bit_info = 0; } else { /* Codec mode */ frame = &strm->xfrm->base; strm->xfrm->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; strm->xfrm->base.size = strm->bytes_per_frame; strm->xfrm->base.buf = NULL; strm->xfrm->base.timestamp.u64 = wmme_strm->timestamp.u64; strm->xfrm->base.bit_info = 0; } /* Get frame from application. */ //PJ_LOG(5,(THIS_FILE, "xxx %u play_cb", play_cnt++)); status = (*strm->play_cb)(strm->user_data, frame); if (status != PJ_SUCCESS) break; if (strm->fmt_id == PJMEDIA_FORMAT_L16) { /* PCM mode */ if (frame->type == PJMEDIA_FRAME_TYPE_NONE) { pj_bzero(buffer, strm->bytes_per_frame); } else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) { pj_assert(!"Frame type not supported"); } else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) { /* Nothing to do */ } else { pj_assert(!"Frame type not supported"); } } else { /* Codec mode */ if (frame->type == PJMEDIA_FRAME_TYPE_NONE) { pj_memset(buffer, strm->silence_char, strm->bytes_per_frame); } else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) { unsigned sz; sz = pjmedia_frame_ext_copy_payload(strm->xfrm, buffer, strm->bytes_per_frame); if (sz < strm->bytes_per_frame) { pj_memset((char*)buffer+sz, strm->silence_char, strm->bytes_per_frame - sz); } } else { pj_assert(!"Frame type not supported"); } } /* Write to the device. */ mr = waveOutWrite(wmme_strm->hWave.Out, &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]), sizeof(WAVEHDR)); if (mr != MMSYSERR_NOERROR) { status = PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr); break; } /* Increment position. */ if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx) wmme_strm->dwBufIdx = 0; wmme_strm->timestamp.u64 += strm->param.samples_per_frame / strm->param.channel_count; } /* for */ } else { struct wmme_channel *wmme_strm = &strm->rec_strm; unsigned burst; MMRESULT mr = MMSYSERR_NOERROR; status = PJ_SUCCESS; /* * Windows Multimedia has indicated that it has some frames ready * in the capture buffer. Get as much frames as possible to * prevent overflows. */ #if 0 { static DWORD tc = 0; DWORD now = GetTickCount(); DWORD i = 0; DWORD bits = 0; if (tc == 0) tc = now; for (i = 0; i < wmme_strm->dwMaxBufIdx; ++i) { bits = bits << 4; bits |= wmme_strm->WaveHdr[i].dwFlags & WHDR_DONE; } PJ_LOG(5,(THIS_FILE, "Record Signal> Index: %d, Delta: %4.4d, " "Flags: %6.6x\n", wmme_strm->dwBufIdx, now - tc, bits)); tc = now; } #endif for (burst=0; burstWaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE); ++burst) { char* buffer = (char*) wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData; unsigned cap_len = wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwBytesRecorded; pjmedia_frame pcm_frame, *frame; /* PJ_LOG(5,(THIS_FILE, "Read %d bytes from buffer %d", cap_len, wmme_strm->dwBufIdx)); */ if (strm->fmt_id == PJMEDIA_FORMAT_L16) { /* PCM mode */ if (cap_len < strm->bytes_per_frame) pj_bzero(buffer + cap_len, strm->bytes_per_frame - cap_len); /* Copy the audio data out of the wave buffer. */ pj_memcpy(strm->buffer, buffer, strm->bytes_per_frame); /* Prepare frame */ frame = &pcm_frame; frame->type = PJMEDIA_FRAME_TYPE_AUDIO; frame->buf = strm->buffer; frame->size = strm->bytes_per_frame; frame->timestamp.u64 = wmme_strm->timestamp.u64; frame->bit_info = 0; } else { /* Codec mode */ frame = &strm->xfrm->base; frame->type = PJMEDIA_FRAME_TYPE_EXTENDED; frame->buf = NULL; frame->size = strm->bytes_per_frame; frame->timestamp.u64 = wmme_strm->timestamp.u64; frame->bit_info = 0; strm->xfrm->samples_cnt = 0; strm->xfrm->subframe_cnt = 0; pjmedia_frame_ext_append_subframe( strm->xfrm, buffer, strm->bytes_per_frame *8, strm->param.samples_per_frame ); } /* Re-add the buffer to the device. */ mr = waveInAddBuffer(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]), sizeof(WAVEHDR)); if (mr != MMSYSERR_NOERROR) { status = PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr); break; } /* Call callback */ //PJ_LOG(5,(THIS_FILE, "xxx %u rec_cb", rec_cnt++)); status = (*strm->rec_cb)(strm->user_data, frame); if (status != PJ_SUCCESS) break; /* Increment position. */ if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx) wmme_strm->dwBufIdx = 0; wmme_strm->timestamp.u64 += strm->param.samples_per_frame / strm->param.channel_count; } /* for */ } } PJ_LOG(5,(THIS_FILE, "WMME: thread stopping..")); return 0; } /* API: create stream */ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f, const pjmedia_aud_param *param, pjmedia_aud_rec_cb rec_cb, pjmedia_aud_play_cb play_cb, void *user_data, pjmedia_aud_stream **p_aud_strm) { struct wmme_factory *wf = (struct wmme_factory*)f; pj_pool_t *pool; struct wmme_stream *strm; pj_uint8_t silence_char; pj_status_t status; switch (param->ext_fmt.id) { case PJMEDIA_FORMAT_L16: silence_char = '\0'; break; case PJMEDIA_FORMAT_ALAW: silence_char = (pj_uint8_t)'\xd5'; break; case PJMEDIA_FORMAT_ULAW: silence_char = (pj_uint8_t)'\xff'; break; default: return PJMEDIA_EAUD_BADFORMAT; } /* Create and Initialize stream descriptor */ pool = pj_pool_create(wf->pf, "wmme-dev", 1000, 1000, NULL); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); strm = PJ_POOL_ZALLOC_T(pool, struct wmme_stream); pj_memcpy(&strm->param, param, sizeof(*param)); strm->pool = pool; strm->rec_cb = rec_cb; strm->play_cb = play_cb; strm->user_data = user_data; strm->fmt_id = param->ext_fmt.id; strm->silence_char = silence_char; /* Create player stream */ if (param->dir & PJMEDIA_DIR_PLAYBACK) { unsigned buf_count; if ((param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY)==0) { strm->param.flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; strm->param.output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY; } buf_count = strm->param.output_latency_ms * param->clock_rate * param->channel_count / param->samples_per_frame / 1000; status = init_player_stream(wf, strm->pool, strm, &strm->play_strm, param, buf_count); if (status != PJ_SUCCESS) { stream_destroy(&strm->base); return status; } } /* Create capture stream */ if (param->dir & PJMEDIA_DIR_CAPTURE) { unsigned buf_count; if ((param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY)==0) { strm->param.flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY; strm->param.input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY; } buf_count = strm->param.input_latency_ms * param->clock_rate * param->channel_count / param->samples_per_frame / 1000; status = init_capture_stream(wf, strm->pool, strm, &strm->rec_strm, param, buf_count); if (status != PJ_SUCCESS) { stream_destroy(&strm->base); return status; } } strm->buffer = pj_pool_alloc(pool, strm->bytes_per_frame); if (!strm->buffer) { pj_pool_release(pool); return PJ_ENOMEM; } /* If format is extended, must create buffer for the extended frame. */ if (strm->fmt_id != PJMEDIA_FORMAT_L16) { strm->xfrm_size = sizeof(pjmedia_frame_ext) + 32 * sizeof(pjmedia_frame_ext_subframe) + strm->bytes_per_frame + 4; strm->xfrm = (pjmedia_frame_ext*) pj_pool_alloc(pool, strm->xfrm_size); } /* Create the stop event */ strm->thread_quit_event = CreateEvent(NULL, FALSE, FALSE, NULL); if (strm->thread_quit_event == NULL) { status = pj_get_os_error(); stream_destroy(&strm->base); return status; } /* Create and start the thread */ status = pj_thread_create(pool, "wmme", &wmme_dev_thread, strm, 0, 0, &strm->thread); if (status != PJ_SUCCESS) { stream_destroy(&strm->base); return status; } /* Apply the remaining settings */ if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) { stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, ¶m->output_vol); } /* Done */ strm->base.op = &stream_op; *p_aud_strm = &strm->base; return PJ_SUCCESS; } /* Processes OS messages arriving at the hWnd window */ INT_PTR WINAPI ProcessOSMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { /* wf is used in order to query the number of audio devices currently handled */ static struct wmme_factory *wf = NULL; switch( message ) { case WM_CREATE: /* Initialize wf pointer on the first run */ if (wf == NULL) { CREATESTRUCT *CrtStrPtr = (CREATESTRUCT *) lParam; wf = (struct wmme_factory *)(CrtStrPtr->lpCreateParams); } break; case WM_DEVICECHANGE: /* Possible insertion or removal of device. There's some issues: - Some devices/drivers does not trigger arrival nor removecomplete events, but only devnodes_changed events. Therefore, we process all of those type of events. - Some hardware can send many devnodes_changed events at the same time (up to ~15 of such events). These batches are detected using temporal locality, using constMaxBatchPeriod_. Once the device is detected, the rest of redundant events are discarded. In order to know if there's a new device or not, actual audio devices count is compared to stored audio devices count (via wf->dev_count). - Hardware takes some time to settle and be recognized by drivers. A small window of time is given in order to account for this (constMaxSettleTime_); Settle time should be slightly lower than batch period. */ if (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE || wParam == DBT_DEVNODES_CHANGED) { const int constMaxBatchPeriod_ = 3; /* seconds */ const int constMaxSettleTime_ = (constMaxBatchPeriod_ * 1000) - 500; /* milliseconds */ /* Loop that allows hardware to settle */ int settleTimeLeft = constMaxSettleTime_; while (settleTimeLeft > 0) { /* Check if actual devices lists (I/O) sizes have actually changed before notifying upper levels. Consider input devices, output devices and a WAVE MAPPER device for each. */ if(waveInGetNumDevs() + waveOutGetNumDevs() + 2 != wf->dev_count) { /* Hardware changed */ if (wf->dev_observer.cb) { wf->dev_observer.cb(DEVICE_LIST_CHANGED); } break; } else { /* Hardware is settling... */ Sleep(250); settleTimeLeft -= 250; } } } break; case WM_CLOSE: if (!DestroyWindow(hWnd)) { PJ_LOG(4,(THIS_FILE, "Couldn't destroy message window")); } break; case WM_DESTROY: PostQuitMessage(0); break; default: break; } return 1; } static pj_status_t create_os_messages_window(struct wmme_factory *wf) { pj_status_t status = PJ_EBUG; WNDCLASSEX wndClass; HWND hWnd; /* Set up and register window class */ ZeroMemory(&wndClass, sizeof(WNDCLASSEX)); wndClass.cbSize = sizeof(WNDCLASSEX); wndClass.style = CS_OWNDC; wndClass.lpfnWndProc = (WNDPROC)(ProcessOSMessage); wndClass.hInstance = (HINSTANCE)(GetModuleHandle(0)); wndClass.lpszClassName = "DeviceChangeMessageWindow"; if (RegisterClassEx(&wndClass)) { /* Create the window that will receive OS messages */ hWnd = CreateWindowEx( 0, "DeviceChangeMessageWindow", NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, (LPVOID)(wf)); if (hWnd != NULL) { wf->dev_observer.hWnd = hWnd; if (UpdateWindow(hWnd) != 0) { status = PJ_SUCCESS; } } else { PJ_LOG(4,(THIS_FILE, "Error creating window to receive device change events")); } } return status; } static pj_status_t dispatch_os_messages(void) { pj_status_t status = PJ_SUCCESS; MSG msg; int ret; /* Process OS messages with low cpu-usage wait loop */ while((ret = GetMessage(&msg, NULL, 0, 0)) != 0) { if (ret == -1) { PJ_LOG(4,(THIS_FILE, "Couldn't process OS message")); status = PJ_EBUG; break; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } return status; } /* WMME device observer thread thread. */ static int PJ_THREAD_FUNC wmme_dev_observer_thread(void *arg) { struct wmme_factory *wf = (struct wmme_factory*)arg; pj_status_t status; status = create_os_messages_window(wf); if (status == PJ_SUCCESS) { status = dispatch_os_messages(); if (status != PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "Error dispatching device detection window events")); } } else { PJ_LOG(4,(THIS_FILE, "Failed to create window for receiving device detection events")); } return status; } /* API: set audio device change observer */ static void factory_set_observer(pjmedia_aud_dev_factory *f, pjmedia_aud_dev_change_callback cb) { struct wmme_factory *wf = (struct wmme_factory*)f; pj_pool_t *pool; pj_status_t status; if (cb) { pool = pj_pool_create(wf->pf, "wmme-dev-observer", 1000, 1000, NULL); PJ_ASSERT_ON_FAIL(pool != NULL, {return;}); status = pj_thread_create(pool, "wmme_observer", &wmme_dev_observer_thread, wf, 0, 0, &wf->dev_observer.thread); if (status != PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "Failed to create WMME device detection thread")); wf->dev_observer.thread = NULL; return; } wf->dev_observer.cb = cb; } else { wf->dev_observer.cb = NULL; if (wf->dev_observer.hWnd) { CloseWindow(wf->dev_observer.hWnd); wf->dev_observer.hWnd = NULL; } pj_thread_join(wf->dev_observer.thread); pj_thread_destroy(wf->dev_observer.thread); wf->dev_observer.thread = NULL; } } /* API: get default recording device */ static int factory_get_default_rec_dev(pjmedia_aud_dev_factory *f) { PJ_UNUSED_ARG(f); /* Let PJMEDIA pick the first one available */ return -1; } /* API: get default playback device */ static int factory_get_default_play_dev(pjmedia_aud_dev_factory *f) { PJ_UNUSED_ARG(f); /* Let PJMEDIA pick the first one available */ return -1; } /* API: Get stream info. */ static pj_status_t stream_get_param(pjmedia_aud_stream *s, pjmedia_aud_param *pi) { struct wmme_stream *strm = (struct wmme_stream*)s; PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); pj_memcpy(pi, &strm->param, sizeof(*pi)); /* Update the volume setting */ if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, &pi->output_vol) == PJ_SUCCESS) { pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING; } return PJ_SUCCESS; } /* API: get capability */ static pj_status_t stream_get_cap(pjmedia_aud_stream *s, pjmedia_aud_dev_cap cap, void *pval) { struct wmme_stream *strm = (struct wmme_stream*)s; PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY && (strm->param.dir & PJMEDIA_DIR_CAPTURE)) { /* Recording latency */ *(unsigned*)pval = strm->param.input_latency_ms; return PJ_SUCCESS; } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && (strm->param.dir & PJMEDIA_DIR_PLAYBACK)) { /* Playback latency */ *(unsigned*)pval = strm->param.output_latency_ms; return PJ_SUCCESS; } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING && strm->play_strm.hWave.Out) { /* Output volume setting */ DWORD waveVol; MMRESULT mr; mr = waveOutGetVolume(strm->play_strm.hWave.Out, &waveVol); if (mr != MMSYSERR_NOERROR) { return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr); } waveVol &= 0xFFFF; *(unsigned*)pval = (waveVol * 100) / 0xFFFF; return PJ_SUCCESS; } else { return PJMEDIA_EAUD_INVCAP; } } /* API: set capability */ static pj_status_t stream_set_cap(pjmedia_aud_stream *s, pjmedia_aud_dev_cap cap, const void *pval) { struct wmme_stream *strm = (struct wmme_stream*)s; PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING && strm->play_strm.hWave.Out) { /* Output volume setting */ unsigned vol = *(unsigned*)pval; DWORD waveVol; MMRESULT mr; pj_status_t status; if (vol > 100) vol = 100; waveVol = (vol * 0xFFFF) / 100; waveVol |= (waveVol << 16); mr = waveOutSetVolume(strm->play_strm.hWave.Out, waveVol); status = (mr==MMSYSERR_NOERROR)? PJ_SUCCESS : PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr); if (status == PJ_SUCCESS) { strm->param.output_vol = *(unsigned*)pval; } return status; } return PJMEDIA_EAUD_INVCAP; } /* API: Start stream. */ static pj_status_t stream_start(pjmedia_aud_stream *strm) { struct wmme_stream *stream = (struct wmme_stream*)strm; MMRESULT mr; if (stream->play_strm.hWave.Out != NULL) { mr = waveOutRestart(stream->play_strm.hWave.Out); if (mr != MMSYSERR_NOERROR) { return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr); } PJ_LOG(4,(THIS_FILE, "WMME playback stream started")); } if (stream->rec_strm.hWave.In != NULL) { mr = waveInStart(stream->rec_strm.hWave.In); if (mr != MMSYSERR_NOERROR) { return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr); } PJ_LOG(4,(THIS_FILE, "WMME capture stream started")); } return PJ_SUCCESS; } /* API: Stop stream. */ static pj_status_t stream_stop(pjmedia_aud_stream *strm) { struct wmme_stream *stream = (struct wmme_stream*)strm; MMRESULT mr; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); if (stream->play_strm.hWave.Out != NULL) { mr = waveOutPause(stream->play_strm.hWave.Out); if (mr != MMSYSERR_NOERROR) { return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr); } PJ_LOG(4,(THIS_FILE, "Stopped WMME playback stream")); } if (stream->rec_strm.hWave.In != NULL) { mr = waveInStop(stream->rec_strm.hWave.In); if (mr != MMSYSERR_NOERROR) { return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr); } PJ_LOG(4,(THIS_FILE, "Stopped WMME capture stream")); } return PJ_SUCCESS; } /* API: Destroy stream. */ static pj_status_t stream_destroy(pjmedia_aud_stream *strm) { struct wmme_stream *stream = (struct wmme_stream*)strm; unsigned i; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); stream_stop(strm); /* Stop the stream thread */ if (stream->thread) { SetEvent(stream->thread_quit_event); pj_thread_join(stream->thread); pj_thread_destroy(stream->thread); stream->thread = NULL; } /* Close the thread quit event */ if (stream->thread_quit_event) { CloseHandle(stream->thread_quit_event); stream->thread_quit_event = NULL; } /* Unprepare the headers and close the play device */ if (stream->play_strm.hWave.Out) { waveOutReset(stream->play_strm.hWave.Out); for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i) waveOutUnprepareHeader(stream->play_strm.hWave.Out, &(stream->play_strm.WaveHdr[i]), sizeof(WAVEHDR)); waveOutClose(stream->play_strm.hWave.Out); stream->play_strm.hWave.Out = NULL; } /* Close the play event */ if (stream->play_strm.hEvent) { CloseHandle(stream->play_strm.hEvent); stream->play_strm.hEvent = NULL; } /* Unprepare the headers and close the record device */ if (stream->rec_strm.hWave.In) { waveInReset(stream->rec_strm.hWave.In); for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i) waveInUnprepareHeader(stream->rec_strm.hWave.In, &(stream->rec_strm.WaveHdr[i]), sizeof(WAVEHDR)); waveInClose(stream->rec_strm.hWave.In); stream->rec_strm.hWave.In = NULL; } /* Close the record event */ if (stream->rec_strm.hEvent) { CloseHandle(stream->rec_strm.hEvent); stream->rec_strm.hEvent = NULL; } pj_pool_release(stream->pool); return PJ_SUCCESS; } #endif /* PJMEDIA_AUDIO_DEV_HAS_WMME */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/audio_codecs.c ================================================ /* $Id: audio_codecs.c 4335 2013-01-29 08:09:15Z ming $ */ /* * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include PJ_DEF(void) pjmedia_audio_codec_config_default(pjmedia_audio_codec_config*cfg) { pj_bzero(cfg, sizeof(*cfg)); cfg->speex.option = 0; cfg->speex.quality = PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY; cfg->speex.complexity = PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY; cfg->ilbc.mode = 30; cfg->passthrough.setting.ilbc_mode = cfg->ilbc.mode; } PJ_DEF(pj_status_t) pjmedia_codec_register_audio_codecs(pjmedia_endpt *endpt, const pjmedia_audio_codec_config *c) { pjmedia_audio_codec_config default_cfg; pj_status_t status; PJ_ASSERT_RETURN(endpt, PJ_EINVAL); if (!c) { pjmedia_audio_codec_config_default(&default_cfg); c = &default_cfg; } PJ_ASSERT_RETURN(c->ilbc.mode==20 || c->ilbc.mode==30, PJ_EINVAL); #if PJMEDIA_HAS_PASSTHROUGH_CODECS status = pjmedia_codec_passthrough_init2(endpt, &c->passthrough.setting); if (status != PJ_SUCCESS) return status; #endif #if PJMEDIA_HAS_SPEEX_CODEC /* Register speex. */ status = pjmedia_codec_speex_init(endpt, c->speex.option, c->speex.quality, c->speex.complexity); if (status != PJ_SUCCESS) return status; #endif #if PJMEDIA_HAS_ILBC_CODEC /* Register iLBC. */ status = pjmedia_codec_ilbc_init( endpt, c->ilbc.mode); if (status != PJ_SUCCESS) return status; #endif /* PJMEDIA_HAS_ILBC_CODEC */ #if PJMEDIA_HAS_GSM_CODEC /* Register GSM */ status = pjmedia_codec_gsm_init(endpt); if (status != PJ_SUCCESS) return status; #endif /* PJMEDIA_HAS_GSM_CODEC */ #if PJMEDIA_HAS_G711_CODEC /* Register PCMA and PCMU */ status = pjmedia_codec_g711_init(endpt); if (status != PJ_SUCCESS) return status; #endif /* PJMEDIA_HAS_G711_CODEC */ #if PJMEDIA_HAS_G722_CODEC status = pjmedia_codec_g722_init(endpt ); if (status != PJ_SUCCESS) return status; #endif /* PJMEDIA_HAS_G722_CODEC */ #if PJMEDIA_HAS_G7221_CODEC /* Register G722.1 codecs */ status = pjmedia_codec_g7221_init(endpt); if (status != PJ_SUCCESS) return status; #endif /* PJMEDIA_HAS_G7221_CODEC */ #if PJMEDIA_HAS_OPUS_CODEC /* Register opus codecs */ status = pjmedia_codec_opus_init(endpt); if (status != PJ_SUCCESS) return status; #endif /* PJMEDIA_HAS_OPUS_CODEC */ return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c ================================================ /* $Id: ffmpeg_vid_codecs.c 4311 2012-12-20 06:45:09Z nanang $ */ /* * Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include /* * Only build this file if PJMEDIA_HAS_FFMPEG_VID_CODEC != 0 and * PJMEDIA_HAS_VIDEO != 0 */ #if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && \ PJMEDIA_HAS_FFMPEG_VID_CODEC != 0 && \ defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #define THIS_FILE "ffmpeg_vid_codecs.c" #define LIBAVCODEC_VER_AT_LEAST(major,minor) (LIBAVCODEC_VERSION_MAJOR > major || \ (LIBAVCODEC_VERSION_MAJOR == major && \ LIBAVCODEC_VERSION_MINOR >= minor)) #include "../pjmedia/ffmpeg_util.h" #include #include #if LIBAVCODEC_VER_AT_LEAST(53,20) /* Needed by 264 so far, on libavcodec 53.20 */ # include #endif /* Various compatibility */ #if LIBAVCODEC_VER_AT_LEAST(53,20) # define AVCODEC_OPEN(ctx,c) avcodec_open2(ctx,c,NULL) #else # define AVCODEC_OPEN(ctx,c) avcodec_open(ctx,c) #endif #if LIBAVCODEC_VER_AT_LEAST(53,61) # define AVCODEC_HAS_ENCODE(c) (c->encode2) # define AV_OPT_SET(obj,name,val,opt) (av_opt_set(obj,name,val,opt)==0) # define AV_OPT_SET_INT(obj,name,val) (av_opt_set_int(obj,name,val,0)==0) #else # define AVCODEC_HAS_ENCODE(c) (c->encode) # define AV_OPT_SET(obj,name,val,opt) (av_set_string3(obj,name,val,opt,NULL)==0) # define AV_OPT_SET_INT(obj,name,val) (av_set_int(obj,name,val)!=NULL) #endif #define AVCODEC_HAS_DECODE(c) (c->decode) /* Prototypes for FFMPEG codecs factory */ static pj_status_t ffmpeg_test_alloc( pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *id ); static pj_status_t ffmpeg_default_attr( pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info, pjmedia_vid_codec_param *attr ); static pj_status_t ffmpeg_enum_codecs( pjmedia_vid_codec_factory *factory, unsigned *count, pjmedia_vid_codec_info codecs[]); static pj_status_t ffmpeg_alloc_codec( pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info, pjmedia_vid_codec **p_codec); static pj_status_t ffmpeg_dealloc_codec( pjmedia_vid_codec_factory *factory, pjmedia_vid_codec *codec ); /* Prototypes for FFMPEG codecs implementation. */ static pj_status_t ffmpeg_codec_init( pjmedia_vid_codec *codec, pj_pool_t *pool ); static pj_status_t ffmpeg_codec_open( pjmedia_vid_codec *codec, pjmedia_vid_codec_param *attr ); static pj_status_t ffmpeg_codec_close( pjmedia_vid_codec *codec ); static pj_status_t ffmpeg_codec_modify(pjmedia_vid_codec *codec, const pjmedia_vid_codec_param *attr ); static pj_status_t ffmpeg_codec_get_param(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *param); static pj_status_t ffmpeg_codec_encode_begin(pjmedia_vid_codec *codec, const pjmedia_vid_encode_opt *opt, const pjmedia_frame *input, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more); static pj_status_t ffmpeg_codec_encode_more(pjmedia_vid_codec *codec, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more); static pj_status_t ffmpeg_codec_decode( pjmedia_vid_codec *codec, pj_size_t pkt_count, pjmedia_frame packets[], unsigned out_size, pjmedia_frame *output); /* Definition for FFMPEG codecs operations. */ static pjmedia_vid_codec_op ffmpeg_op = { &ffmpeg_codec_init, &ffmpeg_codec_open, &ffmpeg_codec_close, &ffmpeg_codec_modify, &ffmpeg_codec_get_param, &ffmpeg_codec_encode_begin, &ffmpeg_codec_encode_more, &ffmpeg_codec_decode, NULL }; /* Definition for FFMPEG codecs factory operations. */ static pjmedia_vid_codec_factory_op ffmpeg_factory_op = { &ffmpeg_test_alloc, &ffmpeg_default_attr, &ffmpeg_enum_codecs, &ffmpeg_alloc_codec, &ffmpeg_dealloc_codec }; /* FFMPEG codecs factory */ static struct ffmpeg_factory { pjmedia_vid_codec_factory base; pjmedia_vid_codec_mgr *mgr; pj_pool_factory *pf; pj_pool_t *pool; pj_mutex_t *mutex; } ffmpeg_factory; typedef struct ffmpeg_codec_desc ffmpeg_codec_desc; /* FFMPEG codecs private data. */ typedef struct ffmpeg_private { const ffmpeg_codec_desc *desc; pjmedia_vid_codec_param param; /**< Codec param */ pj_pool_t *pool; /**< Pool for each instance */ /* Format info and apply format param */ const pjmedia_video_format_info *enc_vfi; pjmedia_video_apply_fmt_param enc_vafp; const pjmedia_video_format_info *dec_vfi; pjmedia_video_apply_fmt_param dec_vafp; /* Buffers, only needed for multi-packets */ pj_bool_t whole; void *enc_buf; unsigned enc_buf_size; pj_bool_t enc_buf_is_keyframe; unsigned enc_frame_len; unsigned enc_processed; void *dec_buf; unsigned dec_buf_size; pj_timestamp last_dec_keyframe_ts; /* The ffmpeg codec states. */ AVCodec *enc; AVCodec *dec; AVCodecContext *enc_ctx; AVCodecContext *dec_ctx; /* The ffmpeg decoder cannot set the output format, so format conversion * may be needed for post-decoding. */ enum AVPixelFormat expected_dec_fmt; /**< Expected output format of ffmpeg decoder */ void *data; /**< Codec specific data */ } ffmpeg_private; /* Shortcuts for packetize & unpacketize function declaration, * as it has long params and is reused many times! */ #define FUNC_PACKETIZE(name) \ pj_status_t(name)(ffmpeg_private *ff, pj_uint8_t *bits, \ pj_size_t bits_len, unsigned *bits_pos, \ const pj_uint8_t **payload, pj_size_t *payload_len) #define FUNC_UNPACKETIZE(name) \ pj_status_t(name)(ffmpeg_private *ff, const pj_uint8_t *payload, \ pj_size_t payload_len, pj_uint8_t *bits, \ pj_size_t bits_len, unsigned *bits_pos) #define FUNC_FMT_MATCH(name) \ pj_status_t(name)(pj_pool_t *pool, \ pjmedia_sdp_media *offer, unsigned o_fmt_idx, \ pjmedia_sdp_media *answer, unsigned a_fmt_idx, \ unsigned option) /* Type definition of codec specific functions */ typedef FUNC_PACKETIZE(*func_packetize); typedef FUNC_UNPACKETIZE(*func_unpacketize); typedef pj_status_t (*func_preopen) (ffmpeg_private *ff); typedef pj_status_t (*func_postopen) (ffmpeg_private *ff); typedef FUNC_FMT_MATCH(*func_sdp_fmt_match); /* FFMPEG codec info */ struct ffmpeg_codec_desc { /* Predefined info */ pjmedia_vid_codec_info info; pjmedia_format_id base_fmt_id; /**< Some codecs may be exactly same or compatible with another codec, base format will tell the initializer to copy this codec desc from its base format */ pjmedia_rect_size size; pjmedia_ratio fps; pj_uint32_t avg_bps; pj_uint32_t max_bps; func_packetize packetize; func_unpacketize unpacketize; func_preopen preopen; func_preopen postopen; func_sdp_fmt_match sdp_fmt_match; pjmedia_codec_fmtp dec_fmtp; /* Init time defined info */ pj_bool_t enabled; AVCodec *enc; AVCodec *dec; }; #if PJMEDIA_HAS_FFMPEG_CODEC_H264 && !LIBAVCODEC_VER_AT_LEAST(53,20) # error "Must use libavcodec version 53.20 or later to enable FFMPEG H264" #endif /* H264 constants */ #define PROFILE_H264_BASELINE 66 #define PROFILE_H264_MAIN 77 #define PROFILE_H264_HIGH 100 /* Codec specific functions */ #if PJMEDIA_HAS_FFMPEG_CODEC_H264 static pj_status_t h264_preopen(ffmpeg_private *ff); static pj_status_t h264_postopen(ffmpeg_private *ff); static FUNC_PACKETIZE(h264_packetize); static FUNC_UNPACKETIZE(h264_unpacketize); #endif static pj_status_t h263_preopen(ffmpeg_private *ff); static FUNC_PACKETIZE(h263_packetize); static FUNC_UNPACKETIZE(h263_unpacketize); /* Internal codec info */ static ffmpeg_codec_desc codec_desc[] = { #if PJMEDIA_HAS_FFMPEG_CODEC_H264 { {PJMEDIA_FORMAT_H264, PJMEDIA_RTP_PT_H264, {"H264",4}, {"Constrained Baseline (level=30, pack=1)", 39}}, 0, {720, 480}, {15, 1}, 256000, 256000, &h264_packetize, &h264_unpacketize, &h264_preopen, &h264_postopen, &pjmedia_vid_codec_h264_match_sdp, /* Leading space for better compatibility (strange indeed!) */ {2, { {{"profile-level-id",16}, {"42e01e",6}}, {{" packetization-mode",19}, {"1",1}}, } }, }, #endif #if PJMEDIA_HAS_FFMPEG_CODEC_H263P { {PJMEDIA_FORMAT_H263P, PJMEDIA_RTP_PT_H263P, {"H263-1998",9}}, PJMEDIA_FORMAT_H263, {352, 288}, {15, 1}, 256000, 256000, &h263_packetize, &h263_unpacketize, &h263_preopen, NULL, NULL, {2, { {{"CIF",3}, {"1",1}}, {{"QCIF",4}, {"1",1}}, } }, }, #endif { {PJMEDIA_FORMAT_H263, PJMEDIA_RTP_PT_H263, {"H263",4}}, }, { {PJMEDIA_FORMAT_H261, PJMEDIA_RTP_PT_H261, {"H261",4}}, }, { {PJMEDIA_FORMAT_MJPEG, PJMEDIA_RTP_PT_JPEG, {"JPEG",4}}, PJMEDIA_FORMAT_MJPEG, {640, 480}, {25, 1}, }, { {PJMEDIA_FORMAT_MPEG4, 0, {"MP4V",4}}, PJMEDIA_FORMAT_MPEG4, {640, 480}, {25, 1}, }, }; #if PJMEDIA_HAS_FFMPEG_CODEC_H264 typedef struct h264_data { pjmedia_vid_codec_h264_fmtp fmtp; pjmedia_h264_packetizer *pktz; } h264_data; static pj_status_t h264_preopen(ffmpeg_private *ff) { h264_data *data; pjmedia_h264_packetizer_cfg pktz_cfg; pj_status_t status; data = PJ_POOL_ZALLOC_T(ff->pool, h264_data); ff->data = data; /* Parse remote fmtp */ status = pjmedia_vid_codec_h264_parse_fmtp(&ff->param.enc_fmtp, &data->fmtp); if (status != PJ_SUCCESS) return status; /* Create packetizer */ pktz_cfg.mtu = ff->param.enc_mtu; #if 0 if (data->fmtp.packetization_mode == 0) pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL; else if (data->fmtp.packetization_mode == 1) pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED; else return PJ_ENOTSUP; #else if (data->fmtp.packetization_mode!= PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL && data->fmtp.packetization_mode!= PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED) { return PJ_ENOTSUP; } /* Better always send in single NAL mode for better compatibility */ pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL; #endif status = pjmedia_h264_packetizer_create(ff->pool, &pktz_cfg, &data->pktz); if (status != PJ_SUCCESS) return status; /* Apply SDP fmtp to format in codec param */ if (!ff->param.ignore_fmtp) { status = pjmedia_vid_codec_h264_apply_fmtp(&ff->param); if (status != PJ_SUCCESS) return status; } if (ff->param.dir & PJMEDIA_DIR_ENCODING) { pjmedia_video_format_detail *vfd; AVCodecContext *ctx = ff->enc_ctx; const char *profile = NULL; vfd = pjmedia_format_get_video_format_detail(&ff->param.enc_fmt, PJ_TRUE); /* Override generic params after applying SDP fmtp */ ctx->width = vfd->size.w; ctx->height = vfd->size.h; ctx->time_base.num = vfd->fps.denum; ctx->time_base.den = vfd->fps.num; /* Apply profile. */ ctx->profile = data->fmtp.profile_idc; switch (ctx->profile) { case PROFILE_H264_BASELINE: profile = "baseline"; break; case PROFILE_H264_MAIN: profile = "main"; break; case PROFILE_H264_HIGH: profile = "high"; break; default: break; } if (profile && !AV_OPT_SET(ctx->priv_data, "profile", profile, 0)) { PJ_LOG(3, (THIS_FILE, "Failed to set H264 profile to '%s'", profile)); } /* Apply profile constraint bits. */ //PJ_TODO(set_h264_constraint_bits_properly_in_ffmpeg); if (data->fmtp.profile_iop) { #if defined(FF_PROFILE_H264_CONSTRAINED) ctx->profile |= FF_PROFILE_H264_CONSTRAINED; #endif } /* Apply profile level. */ ctx->level = data->fmtp.level; /* Limit NAL unit size as we prefer single NAL unit packetization */ if (!AV_OPT_SET_INT(ctx->priv_data, "slice-max-size", ff->param.enc_mtu)) { PJ_LOG(3, (THIS_FILE, "Failed to set H264 max NAL size to %d", ff->param.enc_mtu)); } /* Apply intra-refresh */ if (!AV_OPT_SET_INT(ctx->priv_data, "intra-refresh", 1)) { PJ_LOG(3, (THIS_FILE, "Failed to set x264 intra-refresh")); } /* Misc x264 settings (performance, quality, latency, etc). * Let's just use the x264 predefined preset & tune. */ if (!AV_OPT_SET(ctx->priv_data, "preset", "ultrafast", 0)) { PJ_LOG(3, (THIS_FILE, "Failed to set x264 preset 'veryfast'")); } if (!AV_OPT_SET(ctx->priv_data, "tune", "fastdecode+zerolatency", 0)) { PJ_LOG(3, (THIS_FILE, "Failed to set x264 tune 'fastdecode+zerolatency'")); } } if (ff->param.dir & PJMEDIA_DIR_DECODING) { AVCodecContext *ctx = ff->dec_ctx; /* Apply the "sprop-parameter-sets" fmtp from remote SDP to * extradata of ffmpeg codec context. */ if (data->fmtp.sprop_param_sets_len) { ctx->extradata_size = (int)data->fmtp.sprop_param_sets_len; ctx->extradata = data->fmtp.sprop_param_sets; } } return PJ_SUCCESS; } static pj_status_t h264_postopen(ffmpeg_private *ff) { h264_data *data = (h264_data*)ff->data; PJ_UNUSED_ARG(data); return PJ_SUCCESS; } static FUNC_PACKETIZE(h264_packetize) { h264_data *data = (h264_data*)ff->data; return pjmedia_h264_packetize(data->pktz, bits, bits_len, bits_pos, payload, payload_len); } static FUNC_UNPACKETIZE(h264_unpacketize) { h264_data *data = (h264_data*)ff->data; return pjmedia_h264_unpacketize(data->pktz, payload, payload_len, bits, bits_len, bits_pos); } #endif /* PJMEDIA_HAS_FFMPEG_CODEC_H264 */ #if PJMEDIA_HAS_FFMPEG_CODEC_H263P typedef struct h263_data { pjmedia_h263_packetizer *pktz; } h263_data; /* H263 pre-open */ static pj_status_t h263_preopen(ffmpeg_private *ff) { h263_data *data; pjmedia_h263_packetizer_cfg pktz_cfg; pj_status_t status; data = PJ_POOL_ZALLOC_T(ff->pool, h263_data); ff->data = data; /* Create packetizer */ pktz_cfg.mtu = ff->param.enc_mtu; pktz_cfg.mode = PJMEDIA_H263_PACKETIZER_MODE_RFC4629; status = pjmedia_h263_packetizer_create(ff->pool, &pktz_cfg, &data->pktz); if (status != PJ_SUCCESS) return status; /* Apply fmtp settings to codec param */ if (!ff->param.ignore_fmtp) { status = pjmedia_vid_codec_h263_apply_fmtp(&ff->param); } /* Override generic params after applying SDP fmtp */ if (ff->param.dir & PJMEDIA_DIR_ENCODING) { pjmedia_video_format_detail *vfd; AVCodecContext *ctx = ff->enc_ctx; vfd = pjmedia_format_get_video_format_detail(&ff->param.enc_fmt, PJ_TRUE); /* Override generic params after applying SDP fmtp */ ctx->width = vfd->size.w; ctx->height = vfd->size.h; ctx->time_base.num = vfd->fps.denum; ctx->time_base.den = vfd->fps.num; } return status; } static FUNC_PACKETIZE(h263_packetize) { h263_data *data = (h263_data*)ff->data; return pjmedia_h263_packetize(data->pktz, bits, bits_len, bits_pos, payload, payload_len); } static FUNC_UNPACKETIZE(h263_unpacketize) { h263_data *data = (h263_data*)ff->data; return pjmedia_h263_unpacketize(data->pktz, payload, payload_len, bits, bits_len, bits_pos); } #endif /* PJMEDIA_HAS_FFMPEG_CODEC_H263P */ static const ffmpeg_codec_desc* find_codec_desc_by_info( const pjmedia_vid_codec_info *info) { int i; for (i=0; ienabled && (desc->info.fmt_id == info->fmt_id) && ((desc->info.dir & info->dir) == info->dir) && (desc->info.pt == info->pt) && (desc->info.packings & info->packings)) { return desc; } } return NULL; } static int find_codec_idx_by_fmt_id(pjmedia_format_id fmt_id) { int i; for (i=0; itype != AVMEDIA_TYPE_VIDEO) continue; /* Video encoder and decoder are usually implemented in separate * AVCodec instances. While the codec attributes (e.g: raw formats, * supported fps) are in the encoder. */ //PJ_LOG(3, (THIS_FILE, "%s", c->name)); status = CodecID_to_pjmedia_format_id(c->id, &fmt_id); /* Skip if format ID is unknown */ if (status != PJ_SUCCESS) continue; codec_info_idx = find_codec_idx_by_fmt_id(fmt_id); /* Skip if codec is unwanted by this wrapper (not listed in * the codec info array) */ if (codec_info_idx < 0) continue; desc = &codec_desc[codec_info_idx]; /* Skip duplicated codec implementation */ if ((AVCODEC_HAS_ENCODE(c) && (desc->info.dir & PJMEDIA_DIR_ENCODING)) || (AVCODEC_HAS_DECODE(c) && (desc->info.dir & PJMEDIA_DIR_DECODING))) { continue; } /* Get raw/decoded format ids in the encoder */ if (c->pix_fmts && AVCODEC_HAS_ENCODE(c)) { pjmedia_format_id raw_fmt[PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT]; unsigned raw_fmt_cnt = 0; unsigned raw_fmt_cnt_should_be = 0; const enum AVPixelFormat *p = c->pix_fmts; for(;(p && *p != -1) && (raw_fmt_cnt < PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT); ++p) { pjmedia_format_id fmt_id; raw_fmt_cnt_should_be++; status = PixelFormat_to_pjmedia_format_id(*p, &fmt_id); if (status != PJ_SUCCESS) { PJ_LOG(6, (THIS_FILE, "Unrecognized ffmpeg pixel " "format %d", *p)); continue; } //raw_fmt[raw_fmt_cnt++] = fmt_id; /* Disable some formats due to H.264 error: * x264 [error]: baseline profile doesn't support 4:4:4 */ if (desc->info.pt != PJMEDIA_RTP_PT_H264 || fmt_id != PJMEDIA_FORMAT_RGB24) { raw_fmt[raw_fmt_cnt++] = fmt_id; } } if (raw_fmt_cnt == 0) { PJ_LOG(5, (THIS_FILE, "No recognized raw format " "for codec [%s/%s], codec ignored", c->name, c->long_name)); /* Skip this encoder */ continue; } if (raw_fmt_cnt < raw_fmt_cnt_should_be) { PJ_LOG(6, (THIS_FILE, "Codec [%s/%s] have %d raw formats, " "recognized only %d raw formats", c->name, c->long_name, raw_fmt_cnt_should_be, raw_fmt_cnt)); } desc->info.dec_fmt_id_cnt = raw_fmt_cnt; pj_memcpy(desc->info.dec_fmt_id, raw_fmt, sizeof(raw_fmt[0])*raw_fmt_cnt); } /* Get supported framerates */ if (c->supported_framerates) { const AVRational *fr = c->supported_framerates; while ((fr->num != 0 || fr->den != 0) && desc->info.fps_cnt < PJMEDIA_VID_CODEC_MAX_FPS_CNT) { desc->info.fps[desc->info.fps_cnt].num = fr->num; desc->info.fps[desc->info.fps_cnt].denum = fr->den; ++desc->info.fps_cnt; ++fr; } } /* Get ffmpeg encoder instance */ if (AVCODEC_HAS_ENCODE(c) && !desc->enc) { desc->info.dir |= PJMEDIA_DIR_ENCODING; desc->enc = c; } /* Get ffmpeg decoder instance */ if (AVCODEC_HAS_DECODE(c) && !desc->dec) { desc->info.dir |= PJMEDIA_DIR_DECODING; desc->dec = c; } /* Enable this codec when any ffmpeg codec instance are recognized * and the supported raw formats info has been collected. */ if ((desc->dec || desc->enc) && desc->info.dec_fmt_id_cnt) { desc->enabled = PJ_TRUE; } /* Normalize default value of clock rate */ if (desc->info.clock_rate == 0) desc->info.clock_rate = 90000; /* Set supported packings */ desc->info.packings |= PJMEDIA_VID_PACKING_WHOLE; if (desc->packetize && desc->unpacketize) desc->info.packings |= PJMEDIA_VID_PACKING_PACKETS; } /* Review all codecs for applying base format, registering format match for * SDP negotiation, etc. */ for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) { ffmpeg_codec_desc *desc = &codec_desc[i]; /* Init encoder/decoder description from base format */ if (desc->base_fmt_id && (!desc->dec || !desc->enc)) { ffmpeg_codec_desc *base_desc = NULL; int base_desc_idx; pjmedia_dir copied_dir = PJMEDIA_DIR_NONE; base_desc_idx = find_codec_idx_by_fmt_id(desc->base_fmt_id); if (base_desc_idx != -1) base_desc = &codec_desc[base_desc_idx]; if (!base_desc || !base_desc->enabled) continue; /* Copy description from base codec */ if (!desc->info.dec_fmt_id_cnt) { desc->info.dec_fmt_id_cnt = base_desc->info.dec_fmt_id_cnt; pj_memcpy(desc->info.dec_fmt_id, base_desc->info.dec_fmt_id, sizeof(pjmedia_format_id)*desc->info.dec_fmt_id_cnt); } if (!desc->info.fps_cnt) { desc->info.fps_cnt = base_desc->info.fps_cnt; pj_memcpy(desc->info.fps, base_desc->info.fps, sizeof(desc->info.fps[0])*desc->info.fps_cnt); } if (!desc->info.clock_rate) { desc->info.clock_rate = base_desc->info.clock_rate; } if (!desc->dec && base_desc->dec) { copied_dir |= PJMEDIA_DIR_DECODING; desc->dec = base_desc->dec; } if (!desc->enc && base_desc->enc) { copied_dir |= PJMEDIA_DIR_ENCODING; desc->enc = base_desc->enc; } desc->info.dir |= copied_dir; desc->enabled = (desc->info.dir != PJMEDIA_DIR_NONE); /* Set supported packings */ desc->info.packings |= PJMEDIA_VID_PACKING_WHOLE; if (desc->packetize && desc->unpacketize) desc->info.packings |= PJMEDIA_VID_PACKING_PACKETS; if (copied_dir != PJMEDIA_DIR_NONE) { const char *dir_name[] = {NULL, "encoder", "decoder", "codec"}; PJ_LOG(5, (THIS_FILE, "The %.*s %s is using base codec (%.*s)", desc->info.encoding_name.slen, desc->info.encoding_name.ptr, dir_name[copied_dir], base_desc->info.encoding_name.slen, base_desc->info.encoding_name.ptr)); } } /* Registering format match for SDP negotiation */ if (desc->sdp_fmt_match) { status = pjmedia_sdp_neg_register_fmt_match_cb( &desc->info.encoding_name, desc->sdp_fmt_match); pj_assert(status == PJ_SUCCESS); } /* Print warning about missing encoder/decoder */ if (!desc->enc) { PJ_LOG(4, (THIS_FILE, "Cannot find %.*s encoder in ffmpeg library", desc->info.encoding_name.slen, desc->info.encoding_name.ptr)); } if (!desc->dec) { PJ_LOG(4, (THIS_FILE, "Cannot find %.*s decoder in ffmpeg library", desc->info.encoding_name.slen, desc->info.encoding_name.ptr)); } } /* Register codec factory to codec manager. */ status = pjmedia_vid_codec_mgr_register_factory(mgr, &ffmpeg_factory.base); if (status != PJ_SUCCESS) goto on_error; ffmpeg_factory.pool = pool; /* Done. */ return PJ_SUCCESS; on_error: pj_pool_release(pool); return status; } /* * Unregister FFMPEG codecs factory from pjmedia endpoint. */ PJ_DEF(pj_status_t) pjmedia_codec_ffmpeg_vid_deinit(void) { pj_status_t status = PJ_SUCCESS; if (ffmpeg_factory.pool == NULL) { /* Already deinitialized */ return PJ_SUCCESS; } pj_mutex_lock(ffmpeg_factory.mutex); /* Unregister FFMPEG codecs factory. */ status = pjmedia_vid_codec_mgr_unregister_factory(ffmpeg_factory.mgr, &ffmpeg_factory.base); /* Destroy mutex. */ pj_mutex_unlock(ffmpeg_factory.mutex); pj_mutex_destroy(ffmpeg_factory.mutex); ffmpeg_factory.mutex = NULL; /* Destroy pool. */ pj_pool_release(ffmpeg_factory.pool); ffmpeg_factory.pool = NULL; pjmedia_ffmpeg_dec_ref(); return status; } /* * Check if factory can allocate the specified codec. */ static pj_status_t ffmpeg_test_alloc( pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info ) { const ffmpeg_codec_desc *desc; PJ_ASSERT_RETURN(factory==&ffmpeg_factory.base, PJ_EINVAL); PJ_ASSERT_RETURN(info, PJ_EINVAL); desc = find_codec_desc_by_info(info); if (!desc) { return PJMEDIA_CODEC_EUNSUP; } return PJ_SUCCESS; } /* * Generate default attribute. */ static pj_status_t ffmpeg_default_attr( pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info, pjmedia_vid_codec_param *attr ) { const ffmpeg_codec_desc *desc; unsigned i; PJ_ASSERT_RETURN(factory==&ffmpeg_factory.base, PJ_EINVAL); PJ_ASSERT_RETURN(info && attr, PJ_EINVAL); desc = find_codec_desc_by_info(info); if (!desc) { return PJMEDIA_CODEC_EUNSUP; } pj_bzero(attr, sizeof(pjmedia_vid_codec_param)); /* Scan the requested packings and use the lowest number */ attr->packing = 0; for (i=0; i<15; ++i) { unsigned packing = (1 << i); if ((desc->info.packings & info->packings) & packing) { attr->packing = (pjmedia_vid_packing)packing; break; } } if (attr->packing == 0) { /* No supported packing in info */ return PJMEDIA_CODEC_EUNSUP; } /* Direction */ attr->dir = desc->info.dir; /* Encoded format */ pjmedia_format_init_video(&attr->enc_fmt, desc->info.fmt_id, desc->size.w, desc->size.h, desc->fps.num, desc->fps.denum); /* Decoded format */ pjmedia_format_init_video(&attr->dec_fmt, desc->info.dec_fmt_id[0], desc->size.w, desc->size.h, desc->fps.num, desc->fps.denum); /* Decoding fmtp */ attr->dec_fmtp = desc->dec_fmtp; /* Bitrate */ attr->enc_fmt.det.vid.avg_bps = desc->avg_bps; attr->enc_fmt.det.vid.max_bps = desc->max_bps; /* Encoding MTU */ attr->enc_mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE; return PJ_SUCCESS; } /* * Enum codecs supported by this factory. */ static pj_status_t ffmpeg_enum_codecs( pjmedia_vid_codec_factory *factory, unsigned *count, pjmedia_vid_codec_info codecs[]) { unsigned i, max_cnt; PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL); max_cnt = PJ_MIN(*count, PJ_ARRAY_SIZE(codec_desc)); *count = 0; for (i=0; iop = &ffmpeg_op; codec->factory = factory; ff = PJ_POOL_ZALLOC_T(pool, ffmpeg_private); if (!ff) { status = PJ_ENOMEM; goto on_error; } codec->codec_data = ff; ff->pool = pool; ff->enc = desc->enc; ff->dec = desc->dec; ff->desc = desc; *p_codec = codec; return PJ_SUCCESS; on_error: if (pool) pj_pool_release(pool); return status; } /* * Free codec. */ static pj_status_t ffmpeg_dealloc_codec( pjmedia_vid_codec_factory *factory, pjmedia_vid_codec *codec ) { ffmpeg_private *ff; pj_pool_t *pool; PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL); /* Close codec, if it's not closed. */ ff = (ffmpeg_private*) codec->codec_data; pool = ff->pool; codec->codec_data = NULL; pj_pool_release(pool); return PJ_SUCCESS; } /* * Init codec. */ static pj_status_t ffmpeg_codec_init( pjmedia_vid_codec *codec, pj_pool_t *pool ) { PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(pool); return PJ_SUCCESS; } static void print_ffmpeg_err(int err) { #if LIBAVCODEC_VER_AT_LEAST(52,72) char errbuf[512]; if (av_strerror(err, errbuf, sizeof(errbuf)) >= 0) PJ_LOG(5, (THIS_FILE, "ffmpeg err %d: %s", err, errbuf)); #else PJ_LOG(5, (THIS_FILE, "ffmpeg err %d", err)); #endif } static pj_status_t open_ffmpeg_codec(ffmpeg_private *ff, pj_mutex_t *ff_mutex) { enum AVPixelFormat pix_fmt; pjmedia_video_format_detail *vfd; pj_bool_t enc_opened = PJ_FALSE, dec_opened = PJ_FALSE; pj_status_t status; /* Get decoded pixel format */ status = pjmedia_format_id_to_PixelFormat(ff->param.dec_fmt.id, &pix_fmt); if (status != PJ_SUCCESS) return status; ff->expected_dec_fmt = pix_fmt; /* Get video format detail for shortcut access to encoded format */ vfd = pjmedia_format_get_video_format_detail(&ff->param.enc_fmt, PJ_TRUE); /* Allocate ffmpeg codec context */ if (ff->param.dir & PJMEDIA_DIR_ENCODING) { #if LIBAVCODEC_VER_AT_LEAST(53,20) ff->enc_ctx = avcodec_alloc_context3(ff->enc); #else ff->enc_ctx = avcodec_alloc_context(); #endif if (ff->enc_ctx == NULL) goto on_error; } if (ff->param.dir & PJMEDIA_DIR_DECODING) { #if LIBAVCODEC_VER_AT_LEAST(53,20) ff->dec_ctx = avcodec_alloc_context3(ff->dec); #else ff->dec_ctx = avcodec_alloc_context(); #endif if (ff->dec_ctx == NULL) goto on_error; } /* Init generic encoder params */ if (ff->param.dir & PJMEDIA_DIR_ENCODING) { AVCodecContext *ctx = ff->enc_ctx; ctx->pix_fmt = pix_fmt; ctx->width = vfd->size.w; ctx->height = vfd->size.h; ctx->time_base.num = vfd->fps.denum; ctx->time_base.den = vfd->fps.num; if (vfd->avg_bps) { ctx->bit_rate = vfd->avg_bps; if (vfd->max_bps > vfd->avg_bps) ctx->bit_rate_tolerance = vfd->max_bps - vfd->avg_bps; } ctx->strict_std_compliance = FF_COMPLIANCE_STRICT; ctx->workaround_bugs = FF_BUG_AUTODETECT; ctx->opaque = ff; /* Set no delay, note that this may cause some codec functionals * not working (e.g: rate control). */ #if LIBAVCODEC_VER_AT_LEAST(52,113) && !LIBAVCODEC_VER_AT_LEAST(53,20) ctx->rc_lookahead = 0; #endif } /* Init generic decoder params */ if (ff->param.dir & PJMEDIA_DIR_DECODING) { AVCodecContext *ctx = ff->dec_ctx; /* Width/height may be overriden by ffmpeg after first decoding. */ ctx->width = ctx->coded_width = ff->param.dec_fmt.det.vid.size.w; ctx->height = ctx->coded_height = ff->param.dec_fmt.det.vid.size.h; ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; ctx->workaround_bugs = FF_BUG_AUTODETECT; ctx->opaque = ff; } /* Override generic params or apply specific params before opening * the codec. */ if (ff->desc->preopen) { status = (*ff->desc->preopen)(ff); if (status != PJ_SUCCESS) goto on_error; } /* Open encoder */ if (ff->param.dir & PJMEDIA_DIR_ENCODING) { int err; pj_mutex_lock(ff_mutex); err = AVCODEC_OPEN(ff->enc_ctx, ff->enc); pj_mutex_unlock(ff_mutex); if (err < 0) { print_ffmpeg_err(err); status = PJMEDIA_CODEC_EFAILED; goto on_error; } enc_opened = PJ_TRUE; } /* Open decoder */ if (ff->param.dir & PJMEDIA_DIR_DECODING) { int err; pj_mutex_lock(ff_mutex); err = AVCODEC_OPEN(ff->dec_ctx, ff->dec); pj_mutex_unlock(ff_mutex); if (err < 0) { print_ffmpeg_err(err); status = PJMEDIA_CODEC_EFAILED; goto on_error; } dec_opened = PJ_TRUE; } /* Let the codec apply specific params after the codec opened */ if (ff->desc->postopen) { status = (*ff->desc->postopen)(ff); if (status != PJ_SUCCESS) goto on_error; } return PJ_SUCCESS; on_error: if (ff->enc_ctx) { if (enc_opened) avcodec_close(ff->enc_ctx); av_free(ff->enc_ctx); ff->enc_ctx = NULL; } if (ff->dec_ctx) { if (dec_opened) avcodec_close(ff->dec_ctx); av_free(ff->dec_ctx); ff->dec_ctx = NULL; } return status; } /* * Open codec. */ static pj_status_t ffmpeg_codec_open( pjmedia_vid_codec *codec, pjmedia_vid_codec_param *attr ) { ffmpeg_private *ff; pj_status_t status; pj_mutex_t *ff_mutex; PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL); ff = (ffmpeg_private*)codec->codec_data; pj_memcpy(&ff->param, attr, sizeof(*attr)); /* Normalize encoding MTU in codec param */ if (attr->enc_mtu > PJMEDIA_MAX_VID_PAYLOAD_SIZE) attr->enc_mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE; /* Open the codec */ ff_mutex = ((struct ffmpeg_factory*)codec->factory)->mutex; status = open_ffmpeg_codec(ff, ff_mutex); if (status != PJ_SUCCESS) goto on_error; /* Init format info and apply-param of decoder */ ff->dec_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id); if (!ff->dec_vfi) { status = PJ_EINVAL; goto on_error; } pj_bzero(&ff->dec_vafp, sizeof(ff->dec_vafp)); ff->dec_vafp.size = ff->param.dec_fmt.det.vid.size; ff->dec_vafp.buffer = NULL; status = (*ff->dec_vfi->apply_fmt)(ff->dec_vfi, &ff->dec_vafp); if (status != PJ_SUCCESS) { goto on_error; } /* Init format info and apply-param of encoder */ ff->enc_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id); if (!ff->enc_vfi) { status = PJ_EINVAL; goto on_error; } pj_bzero(&ff->enc_vafp, sizeof(ff->enc_vafp)); ff->enc_vafp.size = ff->param.enc_fmt.det.vid.size; ff->enc_vafp.buffer = NULL; status = (*ff->enc_vfi->apply_fmt)(ff->enc_vfi, &ff->enc_vafp); if (status != PJ_SUCCESS) { goto on_error; } /* Alloc buffers if needed */ ff->whole = (ff->param.packing == PJMEDIA_VID_PACKING_WHOLE); if (!ff->whole) { ff->enc_buf_size = (unsigned)ff->enc_vafp.framebytes; ff->enc_buf = pj_pool_alloc(ff->pool, ff->enc_buf_size); ff->dec_buf_size = (unsigned)ff->dec_vafp.framebytes; ff->dec_buf = pj_pool_alloc(ff->pool, ff->dec_buf_size); } /* Update codec attributes, e.g: encoding format may be changed by * SDP fmtp negotiation. */ pj_memcpy(attr, &ff->param, sizeof(*attr)); return PJ_SUCCESS; on_error: ffmpeg_codec_close(codec); return status; } /* * Close codec. */ static pj_status_t ffmpeg_codec_close( pjmedia_vid_codec *codec ) { ffmpeg_private *ff; pj_mutex_t *ff_mutex; PJ_ASSERT_RETURN(codec, PJ_EINVAL); ff = (ffmpeg_private*)codec->codec_data; ff_mutex = ((struct ffmpeg_factory*)codec->factory)->mutex; pj_mutex_lock(ff_mutex); if (ff->enc_ctx) { avcodec_close(ff->enc_ctx); av_free(ff->enc_ctx); } if (ff->dec_ctx && ff->dec_ctx!=ff->enc_ctx) { avcodec_close(ff->dec_ctx); av_free(ff->dec_ctx); } ff->enc_ctx = NULL; ff->dec_ctx = NULL; pj_mutex_unlock(ff_mutex); return PJ_SUCCESS; } /* * Modify codec settings. */ static pj_status_t ffmpeg_codec_modify( pjmedia_vid_codec *codec, const pjmedia_vid_codec_param *attr) { ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data; PJ_UNUSED_ARG(attr); PJ_UNUSED_ARG(ff); return PJ_ENOTSUP; } static pj_status_t ffmpeg_codec_get_param(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *param) { ffmpeg_private *ff; PJ_ASSERT_RETURN(codec && param, PJ_EINVAL); ff = (ffmpeg_private*)codec->codec_data; pj_memcpy(param, &ff->param, sizeof(*param)); return PJ_SUCCESS; } static pj_status_t ffmpeg_packetize ( pjmedia_vid_codec *codec, pj_uint8_t *bits, pj_size_t bits_len, unsigned *bits_pos, const pj_uint8_t **payload, pj_size_t *payload_len) { ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data; if (ff->desc->packetize) { return (*ff->desc->packetize)(ff, bits, bits_len, bits_pos, payload, payload_len); } return PJ_ENOTSUP; } static pj_status_t ffmpeg_unpacketize(pjmedia_vid_codec *codec, const pj_uint8_t *payload, pj_size_t payload_len, pj_uint8_t *bits, pj_size_t bits_len, unsigned *bits_pos) { ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data; if (ff->desc->unpacketize) { return (*ff->desc->unpacketize)(ff, payload, payload_len, bits, bits_len, bits_pos); } return PJ_ENOTSUP; } /* * Encode frames. */ static pj_status_t ffmpeg_codec_encode_whole(pjmedia_vid_codec *codec, const pjmedia_vid_encode_opt *opt, const pjmedia_frame *input, unsigned output_buf_len, pjmedia_frame *output) { ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data; pj_uint8_t *p = (pj_uint8_t*)input->buf; AVFrame avframe; AVPacket avpacket; int err, got_packet; //AVRational src_timebase; /* For some reasons (e.g: SSE/MMX usage), the avcodec_encode_video() must * have stack aligned to 16 bytes. Let's try to be safe by preparing the * 16-bytes aligned stack here, in case it's not managed by the ffmpeg. */ PJ_ALIGN_DATA(pj_uint32_t i[4], 16); if ((long)(pj_ssize_t)i & 0xF) { PJ_LOG(2,(THIS_FILE, "Stack alignment fails")); } /* Check if encoder has been opened */ PJ_ASSERT_RETURN(ff->enc_ctx, PJ_EINVALIDOP); pj_bzero(&avframe, sizeof(avframe)); av_frame_unref(&avframe); // Let ffmpeg manage the timestamps /* src_timebase.num = 1; src_timebase.den = ff->desc->info.clock_rate; avframe.pts = av_rescale_q(input->timestamp.u64, src_timebase, ff->enc_ctx->time_base); */ for (i[0] = 0; i[0] < ff->enc_vfi->plane_cnt; ++i[0]) { avframe.data[i[0]] = p; avframe.linesize[i[0]] = ff->enc_vafp.strides[i[0]]; p += ff->enc_vafp.plane_bytes[i[0]]; } /* Force keyframe */ if (opt && opt->force_keyframe) { #if LIBAVCODEC_VER_AT_LEAST(53,20) avframe.pict_type = AV_PICTURE_TYPE_I; #else avframe.pict_type = FF_I_TYPE; #endif } av_init_packet(&avpacket); avpacket.data = (pj_uint8_t*)output->buf; avpacket.size = output_buf_len; #if LIBAVCODEC_VER_AT_LEAST(54,15) err = avcodec_encode_video2(ff->enc_ctx, &avpacket, &avframe, &got_packet); if (!err && got_packet) err = avpacket.size; #else PJ_UNUSED_ARG(got_packet); err = avcodec_encode_video(ff->enc_ctx, avpacket.data, avpacket.size, &avframe); #endif if (err < 0) { print_ffmpeg_err(err); return PJMEDIA_CODEC_EFAILED; } else { output->size = err; output->bit_info = 0; if (avpacket.flags & AV_PKT_FLAG_KEY) output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; } return PJ_SUCCESS; } static pj_status_t ffmpeg_codec_encode_begin(pjmedia_vid_codec *codec, const pjmedia_vid_encode_opt *opt, const pjmedia_frame *input, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more) { ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data; pj_status_t status; *has_more = PJ_FALSE; if (ff->whole) { status = ffmpeg_codec_encode_whole(codec, opt, input, out_size, output); } else { pjmedia_frame whole_frm; const pj_uint8_t *payload; pj_size_t payload_len; pj_bzero(&whole_frm, sizeof(whole_frm)); whole_frm.buf = ff->enc_buf; whole_frm.size = ff->enc_buf_size; status = ffmpeg_codec_encode_whole(codec, opt, input, (unsigned)whole_frm.size, &whole_frm); if (status != PJ_SUCCESS) return status; ff->enc_buf_is_keyframe = (whole_frm.bit_info & PJMEDIA_VID_FRM_KEYFRAME); ff->enc_frame_len = (unsigned)whole_frm.size; ff->enc_processed = 0; status = ffmpeg_packetize(codec, (pj_uint8_t*)whole_frm.buf, whole_frm.size, &ff->enc_processed, &payload, &payload_len); if (status != PJ_SUCCESS) return status; if (out_size < payload_len) return PJMEDIA_CODEC_EFRMTOOSHORT; output->type = PJMEDIA_FRAME_TYPE_VIDEO; pj_memcpy(output->buf, payload, payload_len); output->size = payload_len; if (ff->enc_buf_is_keyframe) output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; *has_more = (ff->enc_processed < ff->enc_frame_len); } return status; } static pj_status_t ffmpeg_codec_encode_more(pjmedia_vid_codec *codec, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more) { ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data; const pj_uint8_t *payload; pj_size_t payload_len; pj_status_t status; *has_more = PJ_FALSE; if (ff->enc_processed >= ff->enc_frame_len) { /* No more frame */ return PJ_EEOF; } status = ffmpeg_packetize(codec, (pj_uint8_t*)ff->enc_buf, ff->enc_frame_len, &ff->enc_processed, &payload, &payload_len); if (status != PJ_SUCCESS) return status; if (out_size < payload_len) return PJMEDIA_CODEC_EFRMTOOSHORT; output->type = PJMEDIA_FRAME_TYPE_VIDEO; pj_memcpy(output->buf, payload, payload_len); output->size = payload_len; if (ff->enc_buf_is_keyframe) output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; *has_more = (ff->enc_processed < ff->enc_frame_len); return PJ_SUCCESS; } static pj_status_t check_decode_result(pjmedia_vid_codec *codec, const pj_timestamp *ts, pj_bool_t got_keyframe) { ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data; pjmedia_video_apply_fmt_param *vafp = &ff->dec_vafp; pjmedia_event event; /* Check for format change. * Decoder output format is set by libavcodec, in case it is different * to the configured param. */ if (ff->dec_ctx->pix_fmt != ff->expected_dec_fmt || ff->dec_ctx->width != (int)vafp->size.w || ff->dec_ctx->height != (int)vafp->size.h) { pjmedia_format_id new_fmt_id; pj_status_t status; /* Get current raw format id from ffmpeg decoder context */ status = PixelFormat_to_pjmedia_format_id(ff->dec_ctx->pix_fmt, &new_fmt_id); if (status != PJ_SUCCESS) return status; /* Update decoder format in param */ ff->param.dec_fmt.id = new_fmt_id; ff->param.dec_fmt.det.vid.size.w = ff->dec_ctx->width; ff->param.dec_fmt.det.vid.size.h = ff->dec_ctx->height; ff->expected_dec_fmt = ff->dec_ctx->pix_fmt; /* Re-init format info and apply-param of decoder */ ff->dec_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id); if (!ff->dec_vfi) return PJ_ENOTSUP; pj_bzero(&ff->dec_vafp, sizeof(ff->dec_vafp)); ff->dec_vafp.size = ff->param.dec_fmt.det.vid.size; ff->dec_vafp.buffer = NULL; status = (*ff->dec_vfi->apply_fmt)(ff->dec_vfi, &ff->dec_vafp); if (status != PJ_SUCCESS) return status; /* Realloc buffer if necessary */ if (ff->dec_vafp.framebytes > ff->dec_buf_size) { PJ_LOG(5,(THIS_FILE, "Reallocating decoding buffer %u --> %u", (unsigned)ff->dec_buf_size, (unsigned)ff->dec_vafp.framebytes)); ff->dec_buf_size = (unsigned)ff->dec_vafp.framebytes; ff->dec_buf = pj_pool_alloc(ff->pool, ff->dec_buf_size); } /* Broadcast format changed event */ pjmedia_event_init(&event, PJMEDIA_EVENT_FMT_CHANGED, ts, codec); event.data.fmt_changed.dir = PJMEDIA_DIR_DECODING; pj_memcpy(&event.data.fmt_changed.new_fmt, &ff->param.dec_fmt, sizeof(ff->param.dec_fmt)); pjmedia_event_publish(NULL, codec, &event, 0); } /* Check for missing/found keyframe */ if (got_keyframe) { pj_get_timestamp(&ff->last_dec_keyframe_ts); /* Broadcast keyframe event */ pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_FOUND, ts, codec); pjmedia_event_publish(NULL, codec, &event, 0); } else if (ff->last_dec_keyframe_ts.u64 == 0) { /* Broadcast missing keyframe event */ pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING, ts, codec); pjmedia_event_publish(NULL, codec, &event, 0); } return PJ_SUCCESS; } /* * Decode frame. */ static pj_status_t ffmpeg_codec_decode_whole(pjmedia_vid_codec *codec, const pjmedia_frame *input, unsigned output_buf_len, pjmedia_frame *output) { ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data; AVFrame avframe; AVPacket avpacket; int err, got_picture; /* Check if decoder has been opened */ PJ_ASSERT_RETURN(ff->dec_ctx, PJ_EINVALIDOP); /* Reset output frame bit info */ output->bit_info = 0; /* Validate output buffer size */ // Do this validation later after getting decoding result, where the real // decoded size will be assured. //if (ff->dec_vafp.framebytes > output_buf_len) //return PJ_ETOOSMALL; /* Init frame to receive the decoded data, the ffmpeg codec context will * automatically provide the decoded buffer (single buffer used for the * whole decoding session, and seems to be freed when the codec context * closed). */ pj_bzero(&avframe, sizeof(avframe)); av_frame_unref(&avframe); /* Init packet, the container of the encoded data */ av_init_packet(&avpacket); avpacket.data = (pj_uint8_t*)input->buf; avpacket.size = (int)input->size; /* ffmpeg warns: * - input buffer padding, at least AV_INPUT_BUFFER_PADDING_SIZE * - null terminated * Normally, encoded buffer is allocated more than needed, so lets just * bzero the input buffer end/pad, hope it will be just fine. */ pj_bzero(avpacket.data+avpacket.size, AV_INPUT_BUFFER_PADDING_SIZE); output->bit_info = 0; output->timestamp = input->timestamp; #if LIBAVCODEC_VER_AT_LEAST(52,72) //avpacket.flags = AV_PKT_FLAG_KEY; #else avpacket.flags = 0; #endif #if LIBAVCODEC_VER_AT_LEAST(52,72) err = avcodec_decode_video2(ff->dec_ctx, &avframe, &got_picture, &avpacket); #else err = avcodec_decode_video(ff->dec_ctx, &avframe, &got_picture, avpacket.data, avpacket.size); #endif if (err < 0) { pjmedia_event event; output->type = PJMEDIA_FRAME_TYPE_NONE; output->size = 0; print_ffmpeg_err(err); /* Broadcast missing keyframe event */ pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING, &input->timestamp, codec); pjmedia_event_publish(NULL, codec, &event, 0); return PJMEDIA_CODEC_EBADBITSTREAM; } else if (got_picture) { pjmedia_video_apply_fmt_param *vafp = &ff->dec_vafp; pj_uint8_t *q = (pj_uint8_t*)output->buf; unsigned i; pj_status_t status; /* Check decoding result, e.g: see if the format got changed, * keyframe found/missing. */ status = check_decode_result(codec, &input->timestamp, avframe.key_frame); if (status != PJ_SUCCESS) return status; /* Check provided buffer size */ if (vafp->framebytes > output_buf_len) return PJ_ETOOSMALL; /* Get the decoded data */ for (i = 0; i < ff->dec_vfi->plane_cnt; ++i) { pj_uint8_t *p = avframe.data[i]; /* The decoded data may contain padding */ if (avframe.linesize[i]!=vafp->strides[i]) { /* Padding exists, copy line by line */ pj_uint8_t *q_end; q_end = q+vafp->plane_bytes[i]; while(q < q_end) { pj_memcpy(q, p, vafp->strides[i]); q += vafp->strides[i]; p += avframe.linesize[i]; } } else { /* No padding, copy the whole plane */ pj_memcpy(q, p, vafp->plane_bytes[i]); q += vafp->plane_bytes[i]; } } output->type = PJMEDIA_FRAME_TYPE_VIDEO; output->size = vafp->framebytes; } else { output->type = PJMEDIA_FRAME_TYPE_NONE; output->size = 0; } return PJ_SUCCESS; } static pj_status_t ffmpeg_codec_decode( pjmedia_vid_codec *codec, pj_size_t pkt_count, pjmedia_frame packets[], unsigned out_size, pjmedia_frame *output) { ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data; pj_status_t status; PJ_ASSERT_RETURN(codec && pkt_count > 0 && packets && output, PJ_EINVAL); if (ff->whole) { pj_assert(pkt_count==1); return ffmpeg_codec_decode_whole(codec, &packets[0], out_size, output); } else { pjmedia_frame whole_frm; unsigned whole_len = 0; unsigned i; for (i=0; i ff->dec_buf_size) { PJ_LOG(5,(THIS_FILE, "Decoding buffer overflow")); break; } status = ffmpeg_unpacketize(codec, packets[i].buf, packets[i].size, ff->dec_buf, ff->dec_buf_size, &whole_len); if (status != PJ_SUCCESS) { PJ_PERROR(5,(THIS_FILE, status, "Unpacketize error")); continue; } } whole_frm.buf = ff->dec_buf; whole_frm.size = whole_len; whole_frm.timestamp = output->timestamp = packets[i].timestamp; whole_frm.bit_info = 0; return ffmpeg_codec_decode_whole(codec, &whole_frm, out_size, output); } } #ifdef _MSC_VER # pragma comment( lib, "avcodec.lib") #endif #endif /* PJMEDIA_HAS_FFMPEG_VID_CODEC */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/g722/g722_dec.c ================================================ /* $Id: g722_dec.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Based on implementation found in Carnegie Mellon Speech Group Software * depository (ftp://ftp.cs.cmu.edu/project/fgdata/index.html). No copyright * was claimed in the original source codes. */ #include #include #include #include "g722_dec.h" #if defined(PJMEDIA_HAS_G722_CODEC) && (PJMEDIA_HAS_G722_CODEC != 0) #define MODE 1 #define SATURATE(v, max, min) \ if (v>max) v = max; \ else if (v> 2 ; wd2 = qm4[ril] ; dlt = (detl * wd2) >> 15 ; return (dlt) ; } static int block3l (g722_dec_t *dec, int il) { int detl ; int ril, il4, wd, wd1, wd2, wd3, nbpl, depl ; static const int wl[8] = { -60, -30, 58, 172, 334, 538, 1198, 3042 }; static const int rl42[16] = { 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0 }; /* LOGSCL */ ril = il >> 2 ; il4 = rl42[ril] ; wd = (dec->nbl * 32512) >> 15 ; nbpl = wd + wl[il4] ; if (nbpl < 0) nbpl = 0 ; if (nbpl > 18432) nbpl = 18432 ; /* SCALEL */ wd1 = (nbpl >> 6) & 31 ; wd2 = nbpl >> 11 ; if ((8 - wd2) < 0) wd3 = ilb[wd1] << (wd2 - 8) ; else wd3 = ilb[wd1] >> (8 - wd2) ; depl = wd3 << 2 ; /* DELAYA */ dec->nbl = nbpl ; /* DELAYL */ detl = depl ; return (detl) ; } static int block4l (g722_dec_t *dec, int dl) { int sl = dec->slow ; int i ; int wd, wd1, wd2, wd3, wd4, wd5/*, wd6 */; dec->dlt[0] = dl; /* RECONS */ dec->rlt[0] = sl + dec->dlt[0] ; SATURATE(dec->rlt[0], 32767, -32768); /* PARREC */ dec->plt[0] = dec->dlt[0] + dec->szl ; SATURATE(dec->plt[0], 32767, -32768); /* UPPOL2 */ dec->sgl[0] = dec->plt[0] >> 15 ; dec->sgl[1] = dec->plt[1] >> 15 ; dec->sgl[2] = dec->plt[2] >> 15 ; wd1 = dec->al[1] << 2; SATURATE(wd1, 32767, -32768); if ( dec->sgl[0] == dec->sgl[1] ) wd2 = - wd1 ; else wd2 = wd1 ; if (wd2 > 32767) wd2 = 32767; wd2 = wd2 >> 7 ; if ( dec->sgl[0] == dec->sgl[2] ) wd3 = 128 ; else wd3 = - 128 ; wd4 = wd2 + wd3 ; wd5 = (dec->al[2] * 32512) >> 15 ; dec->apl[2] = wd4 + wd5 ; SATURATE(dec->apl[2], 12288, -12288); /* UPPOL1 */ dec->sgl[0] = dec->plt[0] >> 15 ; dec->sgl[1] = dec->plt[1] >> 15 ; if ( dec->sgl[0] == dec->sgl[1] ) wd1 = 192 ; else wd1 = - 192 ; wd2 = (dec->al[1] * 32640) >> 15 ; dec->apl[1] = wd1 + wd2 ; SATURATE(dec->apl[1], 32767, -32768); wd3 = (15360 - dec->apl[2]) ; SATURATE(wd3, 32767, -32768); if ( dec->apl[1] > wd3) dec->apl[1] = wd3 ; if ( dec->apl[1] < -wd3) dec->apl[1] = -wd3 ; /* UPZERO */ if ( dec->dlt[0] == 0 ) wd1 = 0 ; else wd1 = 128 ; dec->sgl[0] = dec->dlt[0] >> 15 ; for ( i = 1; i < 7; i++ ) { dec->sgl[i] = dec->dlt[i] >> 15 ; if ( dec->sgl[i] == dec->sgl[0] ) wd2 = wd1 ; else wd2 = - wd1 ; wd3 = (dec->bl[i] * 32640) >> 15 ; dec->bpl[i] = wd2 + wd3 ; SATURATE(dec->bpl[i], 32767, -32768); } /* DELAYA */ for ( i = 6; i > 0; i-- ) { dec->dlt[i] = dec->dlt[i-1] ; dec->bl[i] = dec->bpl[i] ; } for ( i = 2; i > 0; i-- ) { dec->rlt[i] = dec->rlt[i-1] ; dec->plt[i] = dec->plt[i-1] ; dec->al[i] = dec->apl[i] ; } /* FILTEP */ wd1 = dec->rlt[1] << 1; SATURATE(wd1, 32767, -32768); wd1 = ( dec->al[1] * wd1 ) >> 15 ; wd2 = dec->rlt[2] << 1; SATURATE(wd2, 32767, -32768); wd2 = ( dec->al[2] * wd2 ) >> 15 ; dec->spl = wd1 + wd2 ; SATURATE(dec->spl, 32767, -32768); /* FILTEZ */ dec->szl = 0 ; for (i=6; i>0; i--) { wd = dec->dlt[i] << 1; SATURATE(wd, 32767, -32768); dec->szl += (dec->bl[i] * wd) >> 15 ; SATURATE(dec->szl, 32767, -32768); } /* PREDIC */ sl = dec->spl + dec->szl ; SATURATE(sl, 32767, -32768); return (sl) ; } static int block5l (int ilr, int sl, int detl, int mode) { int yl ; int ril, dl, wd2 = 0; static const int qm5[32] = { -280, -280, -23352, -17560, -14120, -11664, -9752, -8184, -6864, -5712, -4696, -3784, -2960, -2208, -1520, -880, 23352, 17560, 14120, 11664, 9752, 8184, 6864, 5712, 4696, 3784, 2960, 2208, 1520, 880, 280, -280 }; static const int qm6[64] = { -136, -136, -136, -136, -24808, -21904, -19008, -16704, -14984, -13512, -12280, -11192, -10232, -9360, -8576, -7856, -7192, -6576, -6000, -5456, -4944, -4464, -4008, -3576, -3168, -2776, -2400, -2032, -1688, -1360, -1040, -728, 24808, 21904, 19008, 16704, 14984, 13512, 12280, 11192, 10232, 9360, 8576, 7856, 7192, 6576, 6000, 5456, 4944, 4464, 4008, 3576, 3168, 2776, 2400, 2032, 1688, 1360, 1040, 728, 432, 136, -432, -136 }; /* INVQBL */ if (mode == 1) { ril = ilr ; wd2 = qm6[ril] ; } if (mode == 2) { ril = ilr >> 1 ; wd2 = qm5[ril] ; } if (mode == 3) { ril = ilr >> 2 ; wd2 = qm4[ril] ; } dl = (detl * wd2 ) >> 15 ; /* RECONS */ yl = sl + dl ; SATURATE(yl, 32767, -32768); return (yl) ; } static int block6l (int yl) { int rl ; rl = yl ; SATURATE(rl, 16383, -16384); return (rl) ; } static int block2h (int ih, int deth) { int dh ; int wd2 ; static const int qm2[4] = {-7408, -1616, 7408, 1616} ; /* INVQAH */ wd2 = qm2[ih] ; dh = (deth * wd2) >> 15 ; return (dh) ; } static int block3h (g722_dec_t *dec, int ih) { int deth ; int ih2, wd, wd1, wd2, wd3, nbph, deph ; static const int wh[3] = {0, -214, 798} ; static const int rh2[4] = {2, 1, 2, 1} ; /* LOGSCH */ ih2 = rh2[ih] ; wd = (dec->nbh * 32512) >> 15 ; nbph = wd + wh[ih2] ; if (nbph < 0) nbph = 0 ; if (nbph > 22528) nbph = 22528 ; /* SCALEH */ wd1 = (nbph >> 6) & 31 ; wd2 = nbph >> 11 ; if ((10 - wd2) < 0) wd3 = ilb[wd1] << (wd2 - 10) ; else wd3 = ilb[wd1] >> (10 - wd2) ; deph = wd3 << 2 ; /* DELAYA */ dec->nbh = nbph ; /* DELAYH */ deth = deph ; return (deth) ; } static int block4h (g722_dec_t *dec, int d) { int sh = dec->shigh; int i ; int wd, wd1, wd2, wd3, wd4, wd5/*, wd6 */; dec->dh[0] = d; /* RECONS */ dec->rh[0] = sh + dec->dh[0] ; SATURATE(dec->rh[0], 32767, -32768); /* PARREC */ dec->ph[0] = dec->dh[0] + dec->szh ; SATURATE(dec->ph[0], 32767, -32768); /* UPPOL2 */ dec->sgh[0] = dec->ph[0] >> 15 ; dec->sgh[1] = dec->ph[1] >> 15 ; dec->sgh[2] = dec->ph[2] >> 15 ; wd1 = dec->ah[1] << 2; SATURATE(wd1, 32767, -32768); if ( dec->sgh[0] == dec->sgh[1] ) wd2 = - wd1 ; else wd2 = wd1 ; if (wd2 > 32767) wd2 = 32767; wd2 = wd2 >> 7 ; if ( dec->sgh[0] == dec->sgh[2] ) wd3 = 128 ; else wd3 = - 128 ; wd4 = wd2 + wd3 ; wd5 = (dec->ah[2] * 32512) >> 15 ; dec->aph[2] = wd4 + wd5 ; SATURATE(dec->aph[2], 12288, -12288); /* UPPOL1 */ dec->sgh[0] = dec->ph[0] >> 15 ; dec->sgh[1] = dec->ph[1] >> 15 ; if ( dec->sgh[0] == dec->sgh[1] ) wd1 = 192 ; else wd1 = - 192 ; wd2 = (dec->ah[1] * 32640) >> 15 ; dec->aph[1] = wd1 + wd2 ; SATURATE(dec->aph[1], 32767, -32768); //dec->aph[2]? //if (aph[2] > 32767) aph[2] = 32767; //if (aph[2] < -32768) aph[2] = -32768; wd3 = (15360 - dec->aph[2]) ; SATURATE(wd3, 32767, -32768); if ( dec->aph[1] > wd3) dec->aph[1] = wd3 ; if ( dec->aph[1] < -wd3) dec->aph[1] = -wd3 ; /* UPZERO */ if ( dec->dh[0] == 0 ) wd1 = 0 ; if ( dec->dh[0] != 0 ) wd1 = 128 ; dec->sgh[0] = dec->dh[0] >> 15 ; for ( i = 1; i < 7; i++ ) { dec->sgh[i] = dec->dh[i] >> 15 ; if ( dec->sgh[i] == dec->sgh[0] ) wd2 = wd1 ; else wd2 = - wd1 ; wd3 = (dec->bh[i] * 32640) >> 15 ; dec->bph[i] = wd2 + wd3 ; } /* DELAYA */ for ( i = 6; i > 0; i-- ) { dec->dh[i] = dec->dh[i-1] ; dec->bh[i] = dec->bph[i] ; } for ( i = 2; i > 0; i-- ) { dec->rh[i] = dec->rh[i-1] ; dec->ph[i] = dec->ph[i-1] ; dec->ah[i] = dec->aph[i] ; } /* FILTEP */ wd1 = dec->rh[1] << 1 ; SATURATE(wd1, 32767, -32768); wd1 = ( dec->ah[1] * wd1 ) >> 15 ; wd2 = dec->rh[2] << 1; SATURATE(wd2, 32767, -32768); wd2 = ( dec->ah[2] * wd2 ) >> 15 ; dec->sph = wd1 + wd2 ; SATURATE(dec->sph, 32767, -32768); /* FILTEZ */ dec->szh = 0 ; for (i=6; i>0; i--) { wd = dec->dh[i] << 1; SATURATE(wd, 32767, -32768); dec->szh += (dec->bh[i] * wd) >> 15 ; SATURATE(dec->szh, 32767, -32768); } /* PREDIC */ sh = dec->sph + dec->szh ; SATURATE(sh, 32767, -32768); return (sh) ; } static int block5h (int dh, int sh) { int rh ; rh = dh + sh; SATURATE(rh, 16383, -16384); return (rh) ; } static void rx_qmf(g722_dec_t *dec, int rl, int rh, int *xout1, int *xout2) { int i; pj_memmove(&dec->xd[1], dec->xd, 11*sizeof(dec->xd[0])); pj_memmove(&dec->xs[1], dec->xs, 11*sizeof(dec->xs[0])); /* RECA */ dec->xd[0] = rl - rh ; if (dec->xd[0] > 16383) dec->xd[0] = 16383; else if (dec->xd[0] < -16384) dec->xd[0] = -16384; /* RECB */ dec->xs[0] = rl + rh ; if (dec->xs[0] > 16383) dec->xs[0] = 16383; else if (dec->xs[0] < -16384) dec->xs[0] = -16384; /* ACCUMC */ *xout1 = 0; for (i=0; i<12; ++i) *xout1 += dec->xd[i] * g722_qmf_coeff[2*i]; *xout1 = *xout1 >> 12 ; if (*xout1 > 16383) *xout1 = 16383 ; else if (*xout1 < -16384) *xout1 = -16384 ; /* ACCUMD */ *xout2 = 0; for (i=0; i<12; ++i) *xout2 += dec->xs[i] * g722_qmf_coeff[2*i+1]; *xout2 = *xout2 >> 12 ; if (*xout2 > 16383) *xout2 = 16383 ; else if (*xout2 < -16384) *xout2 = -16384 ; } PJ_DEF(pj_status_t) g722_dec_init(g722_dec_t *dec) { PJ_ASSERT_RETURN(dec, PJ_EINVAL); pj_bzero(dec, sizeof(g722_dec_t)); dec->detlow = 32; dec->dethigh = 8; return PJ_SUCCESS; } PJ_DEF(pj_status_t) g722_dec_decode( g722_dec_t *dec, void *in, pj_size_t in_size, pj_int16_t out[], pj_size_t *nsamples) { unsigned i; int ilowr, ylow, rlow, dlowt; int ihigh, rhigh, dhigh; int pcm1, pcm2; pj_uint8_t *in_ = (pj_uint8_t*) in; PJ_ASSERT_RETURN(dec && in && in_size && out && nsamples, PJ_EINVAL); PJ_ASSERT_RETURN(*nsamples >= (in_size << 1), PJ_ETOOSMALL); for(i = 0; i < in_size; ++i) { ilowr = in_[i] & 63; ihigh = (in_[i] >> 6) & 3; /* low band decoder */ ylow = block5l (ilowr, dec->slow, dec->detlow, MODE) ; rlow = block6l (ylow) ; dlowt = block2l (ilowr, dec->detlow) ; dec->detlow = block3l (dec, ilowr) ; dec->slow = block4l (dec, dlowt) ; /* rlow <= output low band pcm */ /* high band decoder */ dhigh = block2h (ihigh, dec->dethigh) ; rhigh = block5h (dhigh, dec->shigh) ; dec->dethigh = block3h (dec, ihigh) ; dec->shigh = block4h (dec, dhigh) ; /* rhigh <= output high band pcm */ rx_qmf(dec, rlow, rhigh, &pcm1, &pcm2); out[i*2] = (pj_int16_t)pcm1; out[i*2+1] = (pj_int16_t)pcm2; } *nsamples = in_size << 1; return PJ_SUCCESS; } PJ_DEF(pj_status_t) g722_dec_deinit(g722_dec_t *dec) { pj_bzero(dec, sizeof(g722_dec_t)); return PJ_SUCCESS; } #endif ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/g722/g722_dec.h ================================================ /* $Id: g722_dec.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Based on implementation found in Carnegie Mellon Speech Group Software * depository (ftp://ftp.cs.cmu.edu/project/fgdata/index.html). No copyright * was claimed in the original source codes. */ #ifndef __PJMEDIA_CODEC_G722_DEC_H__ #define __PJMEDIA_CODEC_G722_DEC_H__ #include /* Decoder state */ typedef struct g722_dec_t { /* PCM low band */ int slow; int detlow; int spl; int szl; int rlt [3]; int al [3]; int apl [3]; int plt [3]; int dlt [7]; int bl [7]; int bpl [7]; int sgl [7]; int nbl; /* PCM high band*/ int shigh; int dethigh; int sph; int szh; int rh [3]; int ah [3]; int aph [3]; int ph [3]; int dh [7]; int bh [7]; int bph [7]; int sgh [7]; int nbh; /* QMF signal history */ int xd[12]; int xs[12]; } g722_dec_t; PJ_DECL(pj_status_t) g722_dec_init(g722_dec_t *dec); PJ_DECL(pj_status_t) g722_dec_decode(g722_dec_t *dec, void *in, pj_size_t in_size, pj_int16_t out[], pj_size_t *nsamples); PJ_DECL(pj_status_t) g722_dec_deinit(g722_dec_t *dec); #endif /* __PJMEDIA_CODEC_G722_DEC_H__ */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/g722/g722_enc.c ================================================ /* $Id: g722_enc.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Based on implementation found in Carnegie Mellon Speech Group Software * depository (ftp://ftp.cs.cmu.edu/project/fgdata/index.html). No copyright * was claimed in the original source codes. */ #include #include #include #include "g722_enc.h" #if defined(PJMEDIA_HAS_G722_CODEC) && (PJMEDIA_HAS_G722_CODEC != 0) #define SATURATE(v, max, min) \ if (v>max) v = max; \ else if (v> 15 ; if (sil == 0 ) wd = el ; else wd = (32767 - el) & 32767 ; mil = 1 ; for (i = 1; i < 30; i++) { hdu = (q6[i] << 3) * detl; wd1 = (hdu >> 15) ; if (wd >= wd1) mil = (i + 1) ; else break ; } if (sil == -1 ) il = iln[mil] ; else il = ilp[mil] ; return (il) ; } static int block2l (int il, int detl) { int dlt; int ril, wd2 ; static const int qm4[16] = { 0, -20456, -12896, -8968, -6288, -4240, -2584, -1200, 20456, 12896, 8968, 6288, 4240, 2584, 1200, 0 }; /* INVQAL */ ril = il >> 2 ; wd2 = qm4[ril] ; dlt = (detl * wd2) >> 15 ; return (dlt) ; } static int block3l (g722_enc_t *enc, int il) { int detl; int ril, il4, wd, wd1, wd2, wd3, nbpl, depl ; static int const wl[8] = { -60, -30, 58, 172, 334, 538, 1198, 3042 } ; static int const rl42[16] = { 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0 }; static const int ilb[32] = { 2048, 2093, 2139, 2186, 2233, 2282, 2332, 2383, 2435, 2489, 2543, 2599, 2656, 2714, 2774, 2834, 2896, 2960, 3025, 3091, 3158, 3228, 3298, 3371, 3444, 3520, 3597, 3676, 3756, 3838, 3922, 4008 }; /* LOGSCL */ ril = il >> 2 ; il4 = rl42[ril] ; wd = (enc->nbl * 32512) >> 15 ; nbpl = wd + wl[il4] ; if (nbpl < 0) nbpl = 0 ; if (nbpl > 18432) nbpl = 18432 ; /* SCALEL */ wd1 = (nbpl >> 6) & 31 ; wd2 = nbpl >> 11 ; if ((8 - wd2) < 0) wd3 = ilb[wd1] << (wd2 - 8) ; else wd3 = ilb[wd1] >> (8 - wd2) ; depl = wd3 << 2 ; /* DELAYA */ enc->nbl = nbpl ; /* DELAYL */ detl = depl ; #ifdef DEBUG_VERBOSE printf ("BLOCK3L il=%4d, ril=%4d, il4=%4d, nbl=%4d, wd=%4d, nbpl=%4d\n", il, ril, il4, enc->nbl, wd, nbpl) ; printf ("wd1=%4d, wd2=%4d, wd3=%4d, depl=%4d, detl=%4d\n", wd1, wd2, wd3, depl, detl) ; #endif return (detl) ; } static int block4l (g722_enc_t *enc, int dl) { int sl = enc->slow; int i ; int wd, wd1, wd2, wd3, wd4, wd5 /*, wd6 */; enc->dlt[0] = dl; /* RECONS */ enc->rlt[0] = sl + enc->dlt[0] ; SATURATE(enc->rlt[0], 32767, -32768); /* PARREC */ enc->plt[0] = enc->dlt[0] + enc->szl ; SATURATE(enc->plt[0], 32767, -32768); /* UPPOL2 */ enc->sgl[0] = enc->plt[0] >> 15 ; enc->sgl[1] = enc->plt[1] >> 15 ; enc->sgl[2] = enc->plt[2] >> 15 ; wd1 = enc->al[1] << 2; SATURATE(wd1, 32767, -32768); if ( enc->sgl[0] == enc->sgl[1] ) wd2 = - wd1 ; else wd2 = wd1 ; if ( wd2 > 32767 ) wd2 = 32767; wd2 = wd2 >> 7 ; if ( enc->sgl[0] == enc->sgl[2] ) wd3 = 128 ; else wd3 = - 128 ; wd4 = wd2 + wd3 ; wd5 = (enc->al[2] * 32512) >> 15 ; enc->apl[2] = wd4 + wd5 ; SATURATE(enc->apl[2], 12288, -12288); /* UPPOL1 */ enc->sgl[0] = enc->plt[0] >> 15 ; enc->sgl[1] = enc->plt[1] >> 15 ; if ( enc->sgl[0] == enc->sgl[1] ) wd1 = 192 ; else wd1 = - 192 ; wd2 = (enc->al[1] * 32640) >> 15 ; enc->apl[1] = wd1 + wd2 ; SATURATE(enc->apl[1], 32767, -32768); wd3 = (15360 - enc->apl[2]) ; SATURATE(wd3, 32767, -32768); if ( enc->apl[1] > wd3) enc->apl[1] = wd3 ; if ( enc->apl[1] < -wd3) enc->apl[1] = -wd3 ; /* UPZERO */ if ( enc->dlt[0] == 0 ) wd1 = 0 ; else wd1 = 128 ; enc->sgl[0] = enc->dlt[0] >> 15 ; for ( i = 1; i < 7; i++ ) { enc->sgl[i] = enc->dlt[i] >> 15 ; if ( enc->sgl[i] == enc->sgl[0] ) wd2 = wd1 ; else wd2 = - wd1 ; wd3 = (enc->bl[i] * 32640) >> 15 ; enc->bpl[i] = wd2 + wd3 ; SATURATE(enc->bpl[i], 32767, -32768); } /* DELAYA */ for ( i = 6; i > 0; i-- ) { enc->dlt[i] = enc->dlt[i-1] ; enc->bl[i] = enc->bpl[i] ; } for ( i = 2; i > 0; i-- ) { enc->rlt[i] = enc->rlt[i-1] ; enc->plt[i] = enc->plt[i-1] ; enc->al[i] = enc->apl[i] ; } /* FILTEP */ wd1 = enc->rlt[1] + enc->rlt[1]; SATURATE(wd1, 32767, -32768); wd1 = ( enc->al[1] * wd1 ) >> 15 ; wd2 = enc->rlt[2] + enc->rlt[2]; SATURATE(wd2, 32767, -32768); wd2 = ( enc->al[2] * wd2 ) >> 15 ; enc->spl = wd1 + wd2 ; SATURATE(enc->spl, 32767, -32768); /* FILTEZ */ enc->szl = 0 ; for (i=6; i>0; i--) { wd = enc->dlt[i] + enc->dlt[i]; SATURATE(wd, 32767, -32768); enc->szl += (enc->bl[i] * wd) >> 15 ; SATURATE(enc->szl, 32767, -32768); } /* PREDIC */ sl = enc->spl + enc->szl ; SATURATE(sl, 32767, -32768); return (sl) ; } static int block1h (int xh, int sh, int deth) { int ih ; int eh, sih, mih, wd, wd1, hdu ; static const int ihn[3] = { 0, 1, 0 } ; static const int ihp[3] = { 0, 3, 2 } ; /* SUBTRA */ eh = xh - sh ; SATURATE(eh, 32767, -32768); /* QUANTH */ sih = eh >> 15 ; if (sih == 0 ) wd = eh ; else wd = (32767 - eh) & 32767 ; hdu = (564 << 3) * deth; wd1 = (hdu >> 15) ; if (wd >= wd1) mih = 2 ; else mih = 1 ; if (sih == -1 ) ih = ihn[mih] ; else ih = ihp[mih] ; return (ih) ; } static int block2h (int ih, int deth) { int dh ; int wd2 ; static const int qm2[4] = {-7408, -1616, 7408, 1616}; /* INVQAH */ wd2 = qm2[ih] ; dh = (deth * wd2) >> 15 ; return (dh) ; } static int block3h (g722_enc_t *enc, int ih) { int deth ; int ih2, wd, wd1, wd2, wd3, nbph, deph ; static const int wh[3] = {0, -214, 798} ; static const int rh2[4] = {2, 1, 2, 1} ; static const int ilb[32] = { 2048, 2093, 2139, 2186, 2233, 2282, 2332, 2383, 2435, 2489, 2543, 2599, 2656, 2714, 2774, 2834, 2896, 2960, 3025, 3091, 3158, 3228, 3298, 3371, 3444, 3520, 3597, 3676, 3756, 3838, 3922, 4008 }; /* LOGSCH */ ih2 = rh2[ih] ; wd = (enc->nbh * 32512) >> 15 ; nbph = wd + wh[ih2] ; if (nbph < 0) nbph = 0 ; if (nbph > 22528) nbph = 22528 ; /* SCALEH */ wd1 = (nbph >> 6) & 31 ; wd2 = nbph >> 11 ; if ((10-wd2) < 0) wd3 = ilb[wd1] << (wd2-10) ; else wd3 = ilb[wd1] >> (10-wd2) ; deph = wd3 << 2 ; /* DELAYA */ enc->nbh = nbph ; /* DELAYH */ deth = deph ; return (deth) ; } static int block4h (g722_enc_t *enc, int d) { int sh = enc->shigh; int i ; int wd, wd1, wd2, wd3, wd4, wd5 /*, wd6 */; enc->dh[0] = d; /* RECONS */ enc->rh[0] = sh + enc->dh[0] ; SATURATE(enc->rh[0], 32767, -32768); /* PARREC */ enc->ph[0] = enc->dh[0] + enc->szh ; SATURATE(enc->ph[0], 32767, -32768); /* UPPOL2 */ enc->sgh[0] = enc->ph[0] >> 15 ; enc->sgh[1] = enc->ph[1] >> 15 ; enc->sgh[2] = enc->ph[2] >> 15 ; wd1 = enc->ah[1] << 2; SATURATE(wd1, 32767, -32768); if ( enc->sgh[0] == enc->sgh[1] ) wd2 = - wd1 ; else wd2 = wd1 ; if ( wd2 > 32767 ) wd2 = 32767; wd2 = wd2 >> 7 ; if ( enc->sgh[0] == enc->sgh[2] ) wd3 = 128 ; else wd3 = - 128 ; wd4 = wd2 + wd3 ; wd5 = (enc->ah[2] * 32512) >> 15 ; enc->aph[2] = wd4 + wd5 ; SATURATE(enc->aph[2], 12288, -12288); /* UPPOL1 */ enc->sgh[0] = enc->ph[0] >> 15 ; enc->sgh[1] = enc->ph[1] >> 15 ; if ( enc->sgh[0] == enc->sgh[1] ) wd1 = 192 ; else wd1 = - 192 ; wd2 = (enc->ah[1] * 32640) >> 15 ; enc->aph[1] = wd1 + wd2 ; SATURATE(enc->aph[1], 32767, -32768); wd3 = (15360 - enc->aph[2]) ; SATURATE(wd3, 32767, -32768); if ( enc->aph[1] > wd3) enc->aph[1] = wd3 ; else if ( enc->aph[1] < -wd3) enc->aph[1] = -wd3 ; /* UPZERO */ if ( enc->dh[0] == 0 ) wd1 = 0 ; else wd1 = 128 ; enc->sgh[0] = enc->dh[0] >> 15 ; for ( i = 1; i < 7; i++ ) { enc->sgh[i] = enc->dh[i] >> 15 ; if ( enc->sgh[i] == enc->sgh[0] ) wd2 = wd1 ; else wd2 = - wd1 ; wd3 = (enc->bh[i] * 32640) >> 15 ; enc->bph[i] = wd2 + wd3 ; SATURATE(enc->bph[i], 32767, -32768); } /* DELAYA */ for ( i = 6; i > 0; i-- ) { enc->dh[i] = enc->dh[i-1] ; enc->bh[i] = enc->bph[i] ; } for ( i = 2; i > 0; i-- ) { enc->rh[i] = enc->rh[i-1] ; enc->ph[i] = enc->ph[i-1] ; enc->ah[i] = enc->aph[i] ; } /* FILTEP */ wd1 = enc->rh[1] + enc->rh[1]; SATURATE(wd1, 32767, -32768); wd1 = ( enc->ah[1] * wd1 ) >> 15 ; wd2 = enc->rh[2] + enc->rh[2]; SATURATE(wd2, 32767, -32768); wd2 = ( enc->ah[2] * wd2 ) >> 15 ; enc->sph = wd1 + wd2 ; SATURATE(enc->sph, 32767, -32768); /* FILTEZ */ enc->szh = 0 ; for (i=6; i>0; i--) { wd = enc->dh[i] + enc->dh[i]; SATURATE(wd, 32767, -32768); enc->szh += (enc->bh[i] * wd) >> 15 ; SATURATE(enc->szh, 32767, -32768); } /* PREDIC */ sh = enc->sph + enc->szh ; SATURATE(sh, 32767, -32768); return (sh) ; } /* PROCESS PCM THROUGH THE QMF FILTER */ static void tx_qmf(g722_enc_t *enc, int pcm1, int pcm2, int *lo, int *hi) { int sumodd, sumeven; int i; pj_memmove(&enc->x[2], enc->x, 22 * sizeof(enc->x[0])); enc->x[1] = pcm1; enc->x[0] = pcm2; sumodd = 0; for (i=1; i<24; i+=2) sumodd += enc->x[i] * g722_qmf_coeff[i]; sumeven = 0; for (i=0; i<24; i+=2) sumeven += enc->x[i] * g722_qmf_coeff[i]; *lo = (sumeven + sumodd) >> 13 ; *hi = (sumeven - sumodd) >> 13 ; SATURATE(*lo, 16383, -16384); SATURATE(*hi, 16383, -16383); } PJ_DEF(pj_status_t) g722_enc_init(g722_enc_t *enc) { PJ_ASSERT_RETURN(enc, PJ_EINVAL); pj_bzero(enc, sizeof(g722_enc_t)); enc->detlow = 32; enc->dethigh = 8; return PJ_SUCCESS; } PJ_DEF(pj_status_t) g722_enc_encode( g722_enc_t *enc, pj_int16_t in[], pj_size_t nsamples, void *out, pj_size_t *out_size) { unsigned i; int xlow, ilow, dlowt; int xhigh, ihigh, dhigh; pj_uint8_t *out_ = (pj_uint8_t*) out; PJ_ASSERT_RETURN(enc && in && nsamples && out && out_size, PJ_EINVAL); PJ_ASSERT_RETURN(nsamples % 2 == 0, PJ_EINVAL); PJ_ASSERT_RETURN(*out_size >= (nsamples >> 1), PJ_ETOOSMALL); for(i = 0; i < nsamples; i += 2) { tx_qmf(enc, in[i], in[i+1], &xlow, &xhigh); /* low band encoder */ ilow = block1l (xlow, enc->slow, enc->detlow) ; dlowt = block2l (ilow, enc->detlow) ; enc->detlow = block3l (enc, ilow) ; enc->slow = block4l (enc, dlowt) ; /* high band encoder */ ihigh = block1h (xhigh, enc->shigh, enc->dethigh) ; dhigh = block2h (ihigh, enc->dethigh) ; enc->dethigh = block3h (enc, ihigh) ; enc->shigh = block4h (enc, dhigh) ; /* bits mix low & high adpcm */ out_[i/2] = (pj_uint8_t)((ihigh << 6) | ilow); } *out_size = nsamples >> 1; return PJ_SUCCESS; } PJ_DEF(pj_status_t) g722_enc_deinit(g722_enc_t *enc) { pj_bzero(enc, sizeof(g722_enc_t)); return PJ_SUCCESS; } #endif ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/g722/g722_enc.h ================================================ /* $Id: g722_enc.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Based on implementation found in Carnegie Mellon Speech Group Software * depository (ftp://ftp.cs.cmu.edu/project/fgdata/index.html). No copyright * was claimed in the original source codes. */ #ifndef __PJMEDIA_CODEC_G722_ENC_H__ #define __PJMEDIA_CODEC_G722_ENC_H__ #include /* Encoder state */ typedef struct g722_enc_t { /* PCM low band */ int slow; int detlow; int spl; int szl; int rlt [3]; int al [3]; int apl [3]; int plt [3]; int dlt [7]; int bl [7]; int bpl [7]; int sgl [7]; int nbl; /* PCM high band*/ int shigh; int dethigh; int sph; int szh; int rh [3]; int ah [3]; int aph [3]; int ph [3]; int dh [7]; int bh [7]; int bph [7]; int sgh [7]; int nbh; /* QMF signal history */ int x[24]; } g722_enc_t; PJ_DECL(pj_status_t) g722_enc_init(g722_enc_t *enc); PJ_DECL(pj_status_t) g722_enc_encode(g722_enc_t *enc, pj_int16_t in[], pj_size_t nsamples, void *out, pj_size_t *out_size); PJ_DECL(pj_status_t) g722_enc_deinit(g722_enc_t *enc); #endif /* __PJMEDIA_CODEC_G722_ENC_H__ */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/g722.c ================================================ /* $Id: g722.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #if defined(PJMEDIA_HAS_G722_CODEC) && (PJMEDIA_HAS_G722_CODEC != 0) #include "g722/g722_enc.h" #include "g722/g722_dec.h" #define THIS_FILE "g722.c" /* Defines */ #define PTIME (10) #define SAMPLES_PER_FRAME (16000 * PTIME /1000) #define FRAME_LEN (80) #define PLC_DISABLED 0 /* Tracing */ #ifndef PJ_TRACE # define PJ_TRACE 0 #endif #if PJ_TRACE # define TRACE_(expr) PJ_LOG(4,expr) #else # define TRACE_(expr) #endif /* Prototypes for G722 factory */ static pj_status_t g722_test_alloc(pjmedia_codec_factory *factory, const pjmedia_codec_info *id ); static pj_status_t g722_default_attr(pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ); static pj_status_t g722_enum_codecs(pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]); static pj_status_t g722_alloc_codec(pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec); static pj_status_t g722_dealloc_codec(pjmedia_codec_factory *factory, pjmedia_codec *codec ); /* Prototypes for G722 implementation. */ static pj_status_t g722_codec_init(pjmedia_codec *codec, pj_pool_t *pool ); static pj_status_t g722_codec_open(pjmedia_codec *codec, pjmedia_codec_param *attr ); static pj_status_t g722_codec_close(pjmedia_codec *codec ); static pj_status_t g722_codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr ); static pj_status_t g722_codec_parse(pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]); static pj_status_t g722_codec_encode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); static pj_status_t g722_codec_decode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); #if !PLC_DISABLED static pj_status_t g722_codec_recover(pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output); #endif /* Definition for G722 codec operations. */ static pjmedia_codec_op g722_op = { &g722_codec_init, &g722_codec_open, &g722_codec_close, &g722_codec_modify, &g722_codec_parse, &g722_codec_encode, &g722_codec_decode, #if !PLC_DISABLED &g722_codec_recover #else NULL #endif }; /* Definition for G722 codec factory operations. */ static pjmedia_codec_factory_op g722_factory_op = { &g722_test_alloc, &g722_default_attr, &g722_enum_codecs, &g722_alloc_codec, &g722_dealloc_codec, &pjmedia_codec_g722_deinit }; /* G722 factory */ static struct g722_codec_factory { pjmedia_codec_factory base; pjmedia_endpt *endpt; pj_pool_t *pool; pj_mutex_t *mutex; pjmedia_codec codec_list; unsigned pcm_shift; } g722_codec_factory; /* G722 codec private data. */ struct g722_data { g722_enc_t encoder; g722_dec_t decoder; unsigned pcm_shift; pj_int16_t pcm_clip_mask; pj_bool_t plc_enabled; pj_bool_t vad_enabled; pjmedia_silence_det *vad; pj_timestamp last_tx; #if !PLC_DISABLED pjmedia_plc *plc; #endif }; /* * Initialize and register G722 codec factory to pjmedia endpoint. */ PJ_DEF(pj_status_t) pjmedia_codec_g722_init( pjmedia_endpt *endpt ) { pjmedia_codec_mgr *codec_mgr; pj_status_t status; if (g722_codec_factory.pool != NULL) return PJ_SUCCESS; /* Create G722 codec factory. */ g722_codec_factory.base.op = &g722_factory_op; g722_codec_factory.base.factory_data = NULL; g722_codec_factory.endpt = endpt; g722_codec_factory.pcm_shift = PJMEDIA_G722_DEFAULT_PCM_SHIFT; g722_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "g722", 1000, 1000); if (!g722_codec_factory.pool) return PJ_ENOMEM; pj_list_init(&g722_codec_factory.codec_list); /* Create mutex. */ status = pj_mutex_create_simple(g722_codec_factory.pool, "g722", &g722_codec_factory.mutex); if (status != PJ_SUCCESS) goto on_error; /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); if (!codec_mgr) { status = PJ_EINVALIDOP; goto on_error; } /* Register codec factory to endpoint. */ status = pjmedia_codec_mgr_register_factory(codec_mgr, &g722_codec_factory.base); if (status != PJ_SUCCESS) goto on_error; TRACE_((THIS_FILE, "G722 codec factory initialized")); /* Done. */ return PJ_SUCCESS; on_error: pj_pool_release(g722_codec_factory.pool); g722_codec_factory.pool = NULL; return status; } /* * Unregister G722 codec factory from pjmedia endpoint and deinitialize * the G722 codec library. */ PJ_DEF(pj_status_t) pjmedia_codec_g722_deinit(void) { pjmedia_codec_mgr *codec_mgr; pj_status_t status; if (g722_codec_factory.pool == NULL) return PJ_SUCCESS; /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(g722_codec_factory.endpt); if (!codec_mgr) { pj_pool_release(g722_codec_factory.pool); g722_codec_factory.pool = NULL; return PJ_EINVALIDOP; } /* Unregister G722 codec factory. */ status = pjmedia_codec_mgr_unregister_factory(codec_mgr, &g722_codec_factory.base); /* Destroy mutex. */ pj_mutex_destroy(g722_codec_factory.mutex); /* Destroy pool. */ pj_pool_release(g722_codec_factory.pool); g722_codec_factory.pool = NULL; TRACE_((THIS_FILE, "G722 codec factory shutdown")); return status; } /* * Set level adjustment. */ PJ_DEF(pj_status_t) pjmedia_codec_g722_set_pcm_shift(unsigned val) { g722_codec_factory.pcm_shift = val; return PJ_SUCCESS; } /* * Check if factory can allocate the specified codec. */ static pj_status_t g722_test_alloc(pjmedia_codec_factory *factory, const pjmedia_codec_info *info ) { PJ_UNUSED_ARG(factory); /* Check payload type. */ if (info->pt != PJMEDIA_RTP_PT_G722) return PJMEDIA_CODEC_EUNSUP; /* Ignore the rest, since it's static payload type. */ return PJ_SUCCESS; } /* * Generate default attribute. */ static pj_status_t g722_default_attr( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ) { PJ_UNUSED_ARG(factory); PJ_UNUSED_ARG(id); pj_bzero(attr, sizeof(pjmedia_codec_param)); attr->info.clock_rate = 16000; attr->info.channel_cnt = 1; attr->info.avg_bps = 64000; attr->info.max_bps = 64000; attr->info.pcm_bits_per_sample = 16; attr->info.frm_ptime = PTIME; attr->info.pt = PJMEDIA_RTP_PT_G722; attr->setting.frm_per_pkt = 2; attr->setting.vad = 1; attr->setting.plc = 1; /* Default all other flag bits disabled. */ return PJ_SUCCESS; } /* * Enum codecs supported by this factory (i.e. only G722!). */ static pj_status_t g722_enum_codecs(pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]) { PJ_UNUSED_ARG(factory); PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); pj_bzero(&codecs[0], sizeof(pjmedia_codec_info)); codecs[0].encoding_name = pj_str("G722"); codecs[0].pt = PJMEDIA_RTP_PT_G722; codecs[0].type = PJMEDIA_TYPE_AUDIO; codecs[0].clock_rate = 16000; codecs[0].channel_cnt = 1; *count = 1; return PJ_SUCCESS; } /* * Allocate a new G722 codec instance. */ static pj_status_t g722_alloc_codec(pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec) { pjmedia_codec *codec; struct g722_data *g722_data; PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &g722_codec_factory.base, PJ_EINVAL); pj_mutex_lock(g722_codec_factory.mutex); /* Get free nodes, if any. */ if (!pj_list_empty(&g722_codec_factory.codec_list)) { codec = g722_codec_factory.codec_list.next; pj_list_erase(codec); } else { pj_status_t status; codec = PJ_POOL_ZALLOC_T(g722_codec_factory.pool, pjmedia_codec); PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM); codec->op = &g722_op; codec->factory = factory; g722_data = PJ_POOL_ZALLOC_T(g722_codec_factory.pool, struct g722_data); codec->codec_data = g722_data; #if !PLC_DISABLED /* Create PLC */ status = pjmedia_plc_create(g722_codec_factory.pool, 16000, SAMPLES_PER_FRAME, 0, &g722_data->plc); if (status != PJ_SUCCESS) { pj_mutex_unlock(g722_codec_factory.mutex); return status; } #endif /* Create silence detector */ status = pjmedia_silence_det_create(g722_codec_factory.pool, 16000, SAMPLES_PER_FRAME, &g722_data->vad); if (status != PJ_SUCCESS) { pj_mutex_unlock(g722_codec_factory.mutex); TRACE_((THIS_FILE, "Create silence detector failed (status = %d)", status)); return status; } } pj_mutex_unlock(g722_codec_factory.mutex); *p_codec = codec; return PJ_SUCCESS; } /* * Free codec. */ static pj_status_t g722_dealloc_codec(pjmedia_codec_factory *factory, pjmedia_codec *codec ) { struct g722_data *g722_data; int i; PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &g722_codec_factory.base, PJ_EINVAL); g722_data = (struct g722_data*) codec->codec_data; /* Close codec, if it's not closed. */ g722_codec_close(codec); #if !PLC_DISABLED /* Clear left samples in the PLC, since codec+plc will be reused * next time. */ for (i=0; i<2; ++i) { pj_int16_t frame[SAMPLES_PER_FRAME]; pjmedia_zero_samples(frame, PJ_ARRAY_SIZE(frame)); pjmedia_plc_save(g722_data->plc, frame); } #else PJ_UNUSED_ARG(i); #endif /* Re-init silence_period */ pj_set_timestamp32(&g722_data->last_tx, 0, 0); /* Put in the free list. */ pj_mutex_lock(g722_codec_factory.mutex); pj_list_push_front(&g722_codec_factory.codec_list, codec); pj_mutex_unlock(g722_codec_factory.mutex); return PJ_SUCCESS; } /* * Init codec. */ static pj_status_t g722_codec_init(pjmedia_codec *codec, pj_pool_t *pool ) { PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(pool); return PJ_SUCCESS; } /* * Open codec. */ static pj_status_t g722_codec_open(pjmedia_codec *codec, pjmedia_codec_param *attr ) { struct g722_data *g722_data = (struct g722_data*) codec->codec_data; pj_status_t status; PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL); PJ_ASSERT_RETURN(g722_data != NULL, PJ_EINVALIDOP); status = g722_enc_init(&g722_data->encoder); if (status != PJ_SUCCESS) { TRACE_((THIS_FILE, "g722_enc_init() failed, status=%d", status)); pj_mutex_unlock(g722_codec_factory.mutex); return PJMEDIA_CODEC_EFAILED; } status = g722_dec_init(&g722_data->decoder); if (status != PJ_SUCCESS) { TRACE_((THIS_FILE, "g722_dec_init() failed, status=%d", status)); pj_mutex_unlock(g722_codec_factory.mutex); return PJMEDIA_CODEC_EFAILED; } g722_data->vad_enabled = (attr->setting.vad != 0); g722_data->plc_enabled = (attr->setting.plc != 0); g722_data->pcm_shift = g722_codec_factory.pcm_shift; g722_data->pcm_clip_mask = (pj_int16_t)(1<pcm_clip_mask <<= (16-g722_codec_factory.pcm_shift); TRACE_((THIS_FILE, "G722 codec opened: vad=%d, plc=%d", g722_data->vad_enabled, g722_data->plc_enabled)); return PJ_SUCCESS; } /* * Close codec. */ static pj_status_t g722_codec_close( pjmedia_codec *codec ) { /* The codec, encoder, and decoder will be reused, so there's * nothing to do here */ PJ_UNUSED_ARG(codec); TRACE_((THIS_FILE, "G722 codec closed")); return PJ_SUCCESS; } /* * Modify codec settings. */ static pj_status_t g722_codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr ) { struct g722_data *g722_data = (struct g722_data*) codec->codec_data; pj_assert(g722_data != NULL); g722_data->vad_enabled = (attr->setting.vad != 0); g722_data->plc_enabled = (attr->setting.plc != 0); TRACE_((THIS_FILE, "G722 codec modified: vad=%d, plc=%d", g722_data->vad_enabled, g722_data->plc_enabled)); return PJ_SUCCESS; } /* * Get frames in the packet. */ static pj_status_t g722_codec_parse(pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]) { unsigned count = 0; PJ_UNUSED_ARG(codec); PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL); TRACE_((THIS_FILE, "G722 parse(): input len=%d", pkt_size)); while (pkt_size >= FRAME_LEN && count < *frame_cnt) { frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO; frames[count].buf = pkt; frames[count].size = FRAME_LEN; frames[count].timestamp.u64 = ts->u64 + count * SAMPLES_PER_FRAME; pkt = ((char*)pkt) + FRAME_LEN; pkt_size -= FRAME_LEN; ++count; } TRACE_((THIS_FILE, "G722 parse(): got %d frames", count)); *frame_cnt = count; return PJ_SUCCESS; } /* * Encode frame. */ static pj_status_t g722_codec_encode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { struct g722_data *g722_data = (struct g722_data*) codec->codec_data; pj_status_t status; pj_assert(g722_data && input && output); PJ_ASSERT_RETURN((input->size >> 2) <= output_buf_len, PJMEDIA_CODEC_EFRMTOOSHORT); /* Detect silence */ if (g722_data->vad_enabled) { pj_bool_t is_silence; pj_int32_t silence_duration; silence_duration = pj_timestamp_diff32(&g722_data->last_tx, &input->timestamp); is_silence = pjmedia_silence_det_detect(g722_data->vad, (const pj_int16_t*) input->buf, (input->size >> 1), NULL); if (is_silence && (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 || silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*16000/1000)) { output->type = PJMEDIA_FRAME_TYPE_NONE; output->buf = NULL; output->size = 0; output->timestamp = input->timestamp; return PJ_SUCCESS; } else { g722_data->last_tx = input->timestamp; } } /* Adjust input signal level from 16-bit to 14-bit */ if (g722_data->pcm_shift) { pj_int16_t *p, *end; p = (pj_int16_t*)input->buf; end = p + input->size/2; while (p < end) { *p++ >>= g722_data->pcm_shift; } } /* Encode to temporary buffer */ output->size = output_buf_len; status = g722_enc_encode(&g722_data->encoder, (pj_int16_t*)input->buf, (input->size >> 1), output->buf, &output->size); if (status != PJ_SUCCESS) { output->size = 0; output->buf = NULL; output->type = PJMEDIA_FRAME_TYPE_NONE; TRACE_((THIS_FILE, "G722 encode() status: %d", status)); return PJMEDIA_CODEC_EFAILED; } output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->timestamp = input->timestamp; TRACE_((THIS_FILE, "G722 encode(): size=%d", output->size)); return PJ_SUCCESS; } /* * Decode frame. */ static pj_status_t g722_codec_decode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { struct g722_data *g722_data = (struct g722_data*) codec->codec_data; pj_status_t status; pj_assert(g722_data != NULL); PJ_ASSERT_RETURN(input && output, PJ_EINVAL); TRACE_((THIS_FILE, "G722 decode(): inbuf=%p, insize=%d, outbuf=%p," "outsize=%d", input->buf, input->size, output->buf, output_buf_len)); if (output_buf_len < SAMPLES_PER_FRAME * 2) { TRACE_((THIS_FILE, "G722 decode() ERROR: PJMEDIA_CODEC_EPCMTOOSHORT")); return PJMEDIA_CODEC_EPCMTOOSHORT; } if (input->size != FRAME_LEN) { TRACE_((THIS_FILE, "G722 decode() ERROR: PJMEDIA_CODEC_EFRMTOOSHORT")); return PJMEDIA_CODEC_EFRMTOOSHORT; } /* Decode */ output->size = SAMPLES_PER_FRAME; status = g722_dec_decode(&g722_data->decoder, input->buf, input->size, (pj_int16_t*)output->buf, &output->size); if (status != PJ_SUCCESS) { TRACE_((THIS_FILE, "G722 decode() status: %d", status)); return PJMEDIA_CODEC_EFAILED; } pj_assert(output->size == SAMPLES_PER_FRAME); /* Adjust input signal level from 14-bit to 16-bit */ if (g722_data->pcm_shift) { pj_int16_t *p, *end; p = (pj_int16_t*)output->buf; end = p + output->size; while (p < end) { #if PJMEDIA_G722_STOP_PCM_SHIFT_ON_CLIPPING /* If there is clipping, stop the PCM shifting */ if (*p & g722_data->pcm_clip_mask) { g722_data->pcm_shift = 0; break; } #endif *p++ <<= g722_data->pcm_shift; } } output->size = SAMPLES_PER_FRAME * 2; output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->timestamp = input->timestamp; #if !PLC_DISABLED if (g722_data->plc_enabled) pjmedia_plc_save(g722_data->plc, (pj_int16_t*)output->buf); #endif TRACE_((THIS_FILE, "G722 decode done")); return PJ_SUCCESS; } #if !PLC_DISABLED /* * Recover lost frame. */ static pj_status_t g722_codec_recover(pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output) { struct g722_data *g722_data = (struct g722_data*)codec->codec_data; PJ_ASSERT_RETURN(g722_data->plc_enabled, PJ_EINVALIDOP); PJ_ASSERT_RETURN(output_buf_len >= SAMPLES_PER_FRAME * 2, PJMEDIA_CODEC_EPCMTOOSHORT); pjmedia_plc_generate(g722_data->plc, (pj_int16_t*)output->buf); output->size = SAMPLES_PER_FRAME * 2; output->type = PJMEDIA_FRAME_TYPE_AUDIO; return PJ_SUCCESS; } #endif #endif // PJMEDIA_HAS_G722_CODEC ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/g7221.c ================================================ /* $Id: g7221.c 4001 2012-03-30 07:53:36Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Only build this file if PJMEDIA_HAS_G7221_CODEC != 0 */ #if defined(PJMEDIA_HAS_G7221_CODEC) && PJMEDIA_HAS_G7221_CODEC!=0 #include "../../../third_party/g7221/common/defs.h" #define THIS_FILE "g7221.c" /* Codec tag, it is the SDP encoding name and also MIME subtype name */ #define CODEC_TAG "G7221" /* Sampling rates definition */ #define WB_SAMPLE_RATE 16000 #define UWB_SAMPLE_RATE 32000 /* Maximum number of samples per frame. */ #define MAX_SAMPLES_PER_FRAME (UWB_SAMPLE_RATE * 20 / 1000) /* Maximum number of codec params. */ #define MAX_CODEC_MODES 8 #define START_RSV_MODES_IDX 6 /* Prototypes for G722.1 codec factory */ static pj_status_t test_alloc( pjmedia_codec_factory *factory, const pjmedia_codec_info *id ); static pj_status_t default_attr( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ); static pj_status_t enum_codecs( pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]); static pj_status_t alloc_codec( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec); static pj_status_t dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ); /* Prototypes for G722.1 codec implementation. */ static pj_status_t codec_init( pjmedia_codec *codec, pj_pool_t *pool ); static pj_status_t codec_open( pjmedia_codec *codec, pjmedia_codec_param *attr ); static pj_status_t codec_close( pjmedia_codec *codec ); static pj_status_t codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr ); static pj_status_t codec_parse( pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]); static pj_status_t codec_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); static pj_status_t codec_decode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); static pj_status_t codec_recover( pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output); /* Definition for G722.1 codec operations. */ static pjmedia_codec_op codec_op = { &codec_init, &codec_open, &codec_close, &codec_modify, &codec_parse, &codec_encode, &codec_decode, &codec_recover }; /* Definition for G722.1 codec factory operations. */ static pjmedia_codec_factory_op codec_factory_op = { &test_alloc, &default_attr, &enum_codecs, &alloc_codec, &dealloc_codec, &pjmedia_codec_g7221_deinit }; /* Structure of G722.1 mode */ typedef struct codec_mode { pj_bool_t enabled; /* Is this mode enabled? */ pj_uint8_t pt; /* Payload type. */ unsigned sample_rate; /* Default sampling rate to be used.*/ unsigned bitrate; /* Bitrate. */ char bitrate_str[8]; /* Bitrate in string. */ } codec_mode; /* G722.1 codec factory */ static struct codec_factory { pjmedia_codec_factory base; /**< Base class. */ pjmedia_endpt *endpt; /**< PJMEDIA endpoint instance. */ pj_pool_t *pool; /**< Codec factory pool. */ pj_mutex_t *mutex; /**< Codec factory mutex. */ int pcm_shift; /**< Level adjustment */ unsigned mode_count; /**< Number of G722.1 modes. */ codec_mode modes[MAX_CODEC_MODES]; /**< The G722.1 modes. */ unsigned mode_rsv_start;/**< Start index of G722.1 non- standard modes, currently there can only be up to two non-standard modes enabled at the same time. */ } codec_factory; /* G722.1 codec private data. */ typedef struct codec_private { pj_pool_t *pool; /**< Pool for each instance. */ pj_bool_t plc_enabled; /**< PLC enabled? */ pj_bool_t vad_enabled; /**< VAD enabled? */ pjmedia_silence_det *vad; /**< PJMEDIA VAD instance. */ pj_timestamp last_tx; /**< Timestamp of last transmit.*/ /* ITU ref implementation seems to leave the codec engine states to be * managed by the application, so here we go. */ /* Common engine state */ pj_uint16_t samples_per_frame; /**< Samples per frame. */ pj_uint16_t bitrate; /**< Coded stream bitrate. */ pj_uint16_t frame_size; /**< Coded frame size. */ pj_uint16_t frame_size_bits; /**< Coded frame size in bits. */ pj_uint16_t number_of_regions; /**< Number of regions. */ int pcm_shift; /**< Adjustment for PCM in/out */ /* Encoder specific state */ Word16 *enc_frame; /**< 16bit to 14bit buffer */ Word16 *enc_old_frame; /* Decoder specific state */ Word16 *dec_old_frame; Rand_Obj dec_randobj; Word16 dec_old_mag_shift; Word16 *dec_old_mlt_coefs; } codec_private_t; /* * Helper function for looking up mode based on payload type. */ static codec_mode* lookup_mode(unsigned pt) { codec_mode* mode = NULL; unsigned i; for (i = 0; i < codec_factory.mode_count; ++i) { mode = &codec_factory.modes[i]; if (mode->pt == pt) break; } return mode; } /* * Helper function to validate G722.1 mode. Valid modes are defined as: * 1. sample rate must be 16kHz or 32kHz, * 2. bitrate: * - for sampling rate 16kHz: 16000 to 32000 bps, it must be a multiple * of 400 (to keep RTP payload octed-aligned) * - for sampling rate 32kHz: 24000 to 48000 bps, it must be a multiple * of 400 (to keep RTP payload octed-aligned) */ static pj_bool_t validate_mode(unsigned sample_rate, unsigned bitrate) { if (sample_rate == WB_SAMPLE_RATE) { if (bitrate < 16000 || bitrate > 32000 || ((bitrate-16000) % 400 != 0)) { return PJ_FALSE; } } else if (sample_rate == UWB_SAMPLE_RATE) { if (bitrate < 24000 || bitrate > 48000 || ((bitrate-24000) % 400 != 0)) { return PJ_FALSE; } } else { return PJ_FALSE; } return PJ_TRUE; } #if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0 PJ_INLINE(void) swap_bytes(pj_uint16_t *buf, unsigned count) { pj_uint16_t *end = buf + count; while (buf != end) { *buf = (pj_uint16_t)((*buf << 8) | (*buf >> 8)); ++buf; } } #else #define swap_bytes(buf, count) #endif /* * Initialize and register G722.1 codec factory to pjmedia endpoint. */ PJ_DEF(pj_status_t) pjmedia_codec_g7221_init( pjmedia_endpt *endpt ) { pjmedia_codec_mgr *codec_mgr; codec_mode *mode; pj_str_t codec_name; pj_status_t status; if (codec_factory.pool != NULL) { /* Already initialized. */ return PJ_SUCCESS; } /* Initialize codec modes, by default all standard bitrates are enabled */ codec_factory.mode_count = 0; codec_factory.pcm_shift = PJMEDIA_G7221_DEFAULT_PCM_SHIFT; mode = &codec_factory.modes[codec_factory.mode_count++]; mode->enabled = PJ_TRUE; mode->pt = PJMEDIA_RTP_PT_G722_1_24; mode->sample_rate = WB_SAMPLE_RATE; mode->bitrate = 24000; pj_utoa(mode->bitrate, mode->bitrate_str); mode = &codec_factory.modes[codec_factory.mode_count++]; mode->enabled = PJ_TRUE; mode->pt = PJMEDIA_RTP_PT_G722_1_32; mode->sample_rate = WB_SAMPLE_RATE; mode->bitrate = 32000; pj_utoa(mode->bitrate, mode->bitrate_str); mode = &codec_factory.modes[codec_factory.mode_count++]; mode->enabled = PJ_TRUE; mode->pt = PJMEDIA_RTP_PT_G7221C_24; mode->sample_rate = UWB_SAMPLE_RATE; mode->bitrate = 24000; pj_utoa(mode->bitrate, mode->bitrate_str); mode = &codec_factory.modes[codec_factory.mode_count++]; mode->enabled = PJ_TRUE; mode->pt = PJMEDIA_RTP_PT_G7221C_32; mode->sample_rate = UWB_SAMPLE_RATE; mode->bitrate = 32000; pj_utoa(mode->bitrate, mode->bitrate_str); mode = &codec_factory.modes[codec_factory.mode_count++]; mode->enabled = PJ_TRUE; mode->pt = PJMEDIA_RTP_PT_G7221C_48; mode->sample_rate = UWB_SAMPLE_RATE; mode->bitrate = 48000; pj_utoa(mode->bitrate, mode->bitrate_str); /* Non-standard bitrates */ /* Bitrate 16kbps is non-standard but rather commonly used. */ mode = &codec_factory.modes[codec_factory.mode_count++]; mode->enabled = PJ_FALSE; mode->pt = PJMEDIA_RTP_PT_G722_1_16; mode->sample_rate = WB_SAMPLE_RATE; mode->bitrate = 16000; pj_utoa(mode->bitrate, mode->bitrate_str); /* Reserved two modes for non-standard bitrates */ codec_factory.mode_rsv_start = codec_factory.mode_count; mode = &codec_factory.modes[codec_factory.mode_count++]; mode->enabled = PJ_FALSE; mode->pt = PJMEDIA_RTP_PT_G7221_RSV1; mode = &codec_factory.modes[codec_factory.mode_count++]; mode->enabled = PJ_FALSE; mode->pt = PJMEDIA_RTP_PT_G7221_RSV2; pj_assert(codec_factory.mode_count <= MAX_CODEC_MODES); /* Create G722.1 codec factory. */ codec_factory.base.op = &codec_factory_op; codec_factory.base.factory_data = NULL; codec_factory.endpt = endpt; codec_factory.pool = pjmedia_endpt_create_pool(endpt, "G722.1 codec", 4000, 4000); if (!codec_factory.pool) return PJ_ENOMEM; /* Create mutex. */ status = pj_mutex_create_simple(codec_factory.pool, "G722.1 codec", &codec_factory.mutex); if (status != PJ_SUCCESS) goto on_error; /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); if (!codec_mgr) { status = PJ_EINVALIDOP; goto on_error; } /* Register format match callback. */ pj_cstr(&codec_name, CODEC_TAG); status = pjmedia_sdp_neg_register_fmt_match_cb( &codec_name, &pjmedia_codec_g7221_match_sdp); if (status != PJ_SUCCESS) goto on_error; /* Register codec factory to endpoint. */ status = pjmedia_codec_mgr_register_factory(codec_mgr, &codec_factory.base); if (status != PJ_SUCCESS) goto on_error; /* Done. */ return PJ_SUCCESS; on_error: if (codec_factory.mutex) { pj_mutex_destroy(codec_factory.mutex); codec_factory.mutex = NULL; } pj_pool_release(codec_factory.pool); codec_factory.pool = NULL; return status; } /** * Enable and disable G722.1 modes, including non-standard modes. */ PJ_DEF(pj_status_t) pjmedia_codec_g7221_set_mode(unsigned sample_rate, unsigned bitrate, pj_bool_t enabled) { pjmedia_codec_mgr *codec_mgr; unsigned i; /* Validate mode */ if (!validate_mode(sample_rate, bitrate)) return PJMEDIA_CODEC_EINMODE; /* Get codec manager */ codec_mgr = pjmedia_endpt_get_codec_mgr(codec_factory.endpt); if (!codec_mgr) return PJMEDIA_CODEC_EFAILED; /* Look up in factory modes table */ for (i = 0; i < codec_factory.mode_count; ++i) { if (codec_factory.modes[i].sample_rate == sample_rate && codec_factory.modes[i].bitrate == bitrate) { codec_factory.modes[i].enabled = enabled; /* Re-register G722.1 codec factory to update codec list */ pjmedia_codec_mgr_unregister_factory(codec_mgr, &codec_factory.base); pjmedia_codec_mgr_register_factory(codec_mgr, &codec_factory.base); return PJ_SUCCESS; } } /* Mode not found in modes table, this may be a request to enable * a non-standard G722.1 mode. */ /* Non-standard mode need to be initialized first before user * can disable it. */ if (!enabled) return PJ_ENOTFOUND; /* Initialize a non-standard mode, look for available space. */ for (i = codec_factory.mode_rsv_start; i < codec_factory.mode_count; ++i) { if (!codec_factory.modes[i].enabled) { codec_mode *mode = &codec_factory.modes[i]; mode->enabled = PJ_TRUE; mode->sample_rate = sample_rate; mode->bitrate = bitrate; pj_utoa(mode->bitrate, mode->bitrate_str); /* Re-register G722.1 codec factory to update codec list */ pjmedia_codec_mgr_unregister_factory(codec_mgr, &codec_factory.base); pjmedia_codec_mgr_register_factory(codec_mgr, &codec_factory.base); return PJ_SUCCESS; } } /* No space for non-standard mode. */ return PJ_ETOOMANY; } /* * Set level adjustment. */ PJ_DEF(pj_status_t) pjmedia_codec_g7221_set_pcm_shift(int val) { codec_factory.pcm_shift = val; return PJ_SUCCESS; } /* * Unregister G722.1 codec factory from pjmedia endpoint. */ PJ_DEF(pj_status_t) pjmedia_codec_g7221_deinit(void) { pjmedia_codec_mgr *codec_mgr; pj_status_t status; if (codec_factory.pool == NULL) { /* Already deinitialized */ return PJ_SUCCESS; } pj_mutex_lock(codec_factory.mutex); /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(codec_factory.endpt); if (!codec_mgr) { pj_pool_release(codec_factory.pool); codec_factory.pool = NULL; pj_mutex_unlock(codec_factory.mutex); return PJ_EINVALIDOP; } /* Unregister G722.1 codec factory. */ status = pjmedia_codec_mgr_unregister_factory(codec_mgr, &codec_factory.base); /* Destroy mutex. */ pj_mutex_unlock(codec_factory.mutex); pj_mutex_destroy(codec_factory.mutex); codec_factory.mutex = NULL; /* Destroy pool. */ pj_pool_release(codec_factory.pool); codec_factory.pool = NULL; return status; } /* * Check if factory can allocate the specified codec. */ static pj_status_t test_alloc( pjmedia_codec_factory *factory, const pjmedia_codec_info *info ) { PJ_UNUSED_ARG(factory); /* Type MUST be audio. */ if (info->type != PJMEDIA_TYPE_AUDIO) return PJMEDIA_CODEC_EUNSUP; /* Check encoding name. */ if (pj_stricmp2(&info->encoding_name, CODEC_TAG) != 0) return PJMEDIA_CODEC_EUNSUP; /* Check clock-rate */ if (info->clock_rate != WB_SAMPLE_RATE && info->clock_rate != UWB_SAMPLE_RATE) { return PJMEDIA_CODEC_EUNSUP; } return PJ_SUCCESS; } /* * Generate default attribute. */ static pj_status_t default_attr ( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ) { codec_mode *mode; PJ_ASSERT_RETURN(factory==&codec_factory.base, PJ_EINVAL); pj_bzero(attr, sizeof(pjmedia_codec_param)); mode = lookup_mode(id->pt); if (mode == NULL || !mode->enabled) return PJMEDIA_CODEC_EUNSUP; attr->info.pt = (pj_uint8_t)id->pt; attr->info.channel_cnt = 1; attr->info.clock_rate = mode->sample_rate; attr->info.max_bps = mode->bitrate; attr->info.avg_bps = mode->bitrate; attr->info.pcm_bits_per_sample = 16; attr->info.frm_ptime = 20; /* Default flags. */ attr->setting.plc = 1; attr->setting.vad = 0; attr->setting.frm_per_pkt = 1; /* Default FMTP setting */ attr->setting.dec_fmtp.cnt = 1; attr->setting.dec_fmtp.param[0].name = pj_str("bitrate"); attr->setting.dec_fmtp.param[0].val = pj_str(mode->bitrate_str); return PJ_SUCCESS; } /* * Enum codecs supported by this factory. */ static pj_status_t enum_codecs( pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]) { unsigned i, max_cnt; PJ_ASSERT_RETURN(factory==&codec_factory.base, PJ_EINVAL); PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); max_cnt = *count; *count = 0; for (i=0; (i < codec_factory.mode_count) && (*count < max_cnt); ++i) { if (!codec_factory.modes[i].enabled) continue; pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info)); codecs[*count].encoding_name = pj_str((char*)CODEC_TAG); codecs[*count].pt = codec_factory.modes[i].pt; codecs[*count].type = PJMEDIA_TYPE_AUDIO; codecs[*count].clock_rate = codec_factory.modes[i].sample_rate; codecs[*count].channel_cnt = 1; ++ *count; } return PJ_SUCCESS; } /* * Allocate a new codec instance. */ static pj_status_t alloc_codec( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec) { codec_private_t *codec_data; pjmedia_codec *codec; pj_pool_t *pool; pj_status_t status; PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &codec_factory.base, PJ_EINVAL); pj_mutex_lock(codec_factory.mutex); /* Create pool for codec instance */ pool = pjmedia_endpt_create_pool(codec_factory.endpt, "G7221", 512, 512); codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec); codec->op = &codec_op; codec->factory = factory; codec->codec_data = PJ_POOL_ZALLOC_T(pool, codec_private_t); codec_data = (codec_private_t*) codec->codec_data; codec_data->pool = pool; /* Create silence detector */ status = pjmedia_silence_det_create(pool, id->clock_rate, id->clock_rate * 20 / 1000, &codec_data->vad); if (status != PJ_SUCCESS) { pj_mutex_unlock(codec_factory.mutex); return status; } pj_mutex_unlock(codec_factory.mutex); *p_codec = codec; return PJ_SUCCESS; } /* * Free codec. */ static pj_status_t dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ) { codec_private_t *codec_data; pj_pool_t *pool; PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &codec_factory.base, PJ_EINVAL); /* Close codec, if it's not closed. */ codec_data = (codec_private_t*) codec->codec_data; pool = codec_data->pool; codec_close(codec); /* Release codec pool */ pj_pool_release(pool); return PJ_SUCCESS; } /* * Init codec. */ static pj_status_t codec_init( pjmedia_codec *codec, pj_pool_t *pool ) { PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(pool); return PJ_SUCCESS; } /* * Open codec. */ static pj_status_t codec_open( pjmedia_codec *codec, pjmedia_codec_param *attr ) { codec_private_t *codec_data = (codec_private_t*) codec->codec_data; pj_pool_t *pool; pjmedia_codec_fmtp *fmtp = &attr->setting.dec_fmtp; pj_uint16_t fmtp_bitrate = 0; unsigned tmp; for (tmp = 0; tmp < fmtp->cnt && !fmtp_bitrate; ++tmp) { if (!pj_strcmp2(&fmtp->param[tmp].name, "bitrate")) fmtp_bitrate = (pj_uint16_t)pj_strtoul(&fmtp->param[tmp].val); } if (fmtp_bitrate == 0) fmtp_bitrate = (pj_uint16_t)attr->info.avg_bps; /* Validate bitrate */ if (!fmtp_bitrate || !validate_mode(attr->info.clock_rate, fmtp_bitrate)) return PJMEDIA_CODEC_EINMODE; pool = codec_data->pool; /* Initialize common state */ codec_data->vad_enabled = (attr->setting.vad != 0); codec_data->plc_enabled = (attr->setting.plc != 0); codec_data->bitrate = fmtp_bitrate; codec_data->frame_size_bits = fmtp_bitrate*20/1000; codec_data->frame_size = (pj_uint16_t)(codec_data->frame_size_bits>>3); codec_data->samples_per_frame = (pj_uint16_t) (attr->info.clock_rate*20/1000); codec_data->number_of_regions = (pj_uint16_t) (attr->info.clock_rate <= WB_SAMPLE_RATE? NUMBER_OF_REGIONS:MAX_NUMBER_OF_REGIONS); codec_data->pcm_shift = codec_factory.pcm_shift; /* Initialize encoder state */ tmp = codec_data->samples_per_frame << 1; codec_data->enc_old_frame = (Word16*)pj_pool_zalloc(pool, tmp); codec_data->enc_frame = (Word16*)pj_pool_alloc(pool, tmp); /* Initialize decoder state */ tmp = codec_data->samples_per_frame; codec_data->dec_old_frame = (Word16*)pj_pool_zalloc(pool, tmp); tmp = codec_data->samples_per_frame << 1; codec_data->dec_old_mlt_coefs = (Word16*)pj_pool_zalloc(pool, tmp); codec_data->dec_randobj.seed0 = 1; codec_data->dec_randobj.seed1 = 1; codec_data->dec_randobj.seed2 = 1; codec_data->dec_randobj.seed3 = 1; /* Update codec param */ attr->info.avg_bps = attr->info.max_bps = fmtp_bitrate; return PJ_SUCCESS; } /* * Close codec. */ static pj_status_t codec_close( pjmedia_codec *codec ) { PJ_UNUSED_ARG(codec); return PJ_SUCCESS; } /* * Modify codec settings. */ static pj_status_t codec_modify( pjmedia_codec *codec, const pjmedia_codec_param *attr ) { codec_private_t *codec_data = (codec_private_t*) codec->codec_data; codec_data->vad_enabled = (attr->setting.vad != 0); codec_data->plc_enabled = (attr->setting.plc != 0); return PJ_SUCCESS; } /* * Get frames in the packet. */ static pj_status_t codec_parse( pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]) { codec_private_t *codec_data = (codec_private_t*) codec->codec_data; unsigned count = 0; PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL); /* Parse based on fixed frame size. */ while (pkt_size >= codec_data->frame_size && count < *frame_cnt) { frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO; frames[count].buf = pkt; frames[count].size = codec_data->frame_size; frames[count].timestamp.u64 = ts->u64 + count * codec_data->samples_per_frame; pkt = (pj_uint8_t*)pkt + codec_data->frame_size; pkt_size -= codec_data->frame_size; ++count; } pj_assert(pkt_size == 0); *frame_cnt = count; return PJ_SUCCESS; } /* * Encode frames. */ static pj_status_t codec_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { codec_private_t *codec_data = (codec_private_t*) codec->codec_data; unsigned nsamples, processed; /* Check frame in & out size */ nsamples = input->size >> 1; PJ_ASSERT_RETURN(nsamples % codec_data->samples_per_frame == 0, PJMEDIA_CODEC_EPCMFRMINLEN); PJ_ASSERT_RETURN(output_buf_len >= codec_data->frame_size * nsamples / codec_data->samples_per_frame, PJMEDIA_CODEC_EFRMTOOSHORT); /* Apply silence detection if VAD is enabled */ if (codec_data->vad_enabled) { pj_bool_t is_silence; pj_int32_t silence_duration; pj_assert(codec_data->vad); silence_duration = pj_timestamp_diff32(&codec_data->last_tx, &input->timestamp); is_silence = pjmedia_silence_det_detect(codec_data->vad, (const pj_int16_t*) input->buf, (input->size >> 1), NULL); if (is_silence && (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 || silence_duration < (PJMEDIA_CODEC_MAX_SILENCE_PERIOD * (int)codec_data->samples_per_frame / 20))) { output->type = PJMEDIA_FRAME_TYPE_NONE; output->buf = NULL; output->size = 0; output->timestamp = input->timestamp; return PJ_SUCCESS; } else { codec_data->last_tx = input->timestamp; } } processed = 0; output->size = 0; while (processed < nsamples) { Word16 mlt_coefs[MAX_SAMPLES_PER_FRAME]; Word16 mag_shift; const Word16 *pcm_input; pj_int8_t *out_bits; pcm_input = (const Word16*)input->buf + processed; out_bits = (pj_int8_t*)output->buf + output->size; /* Encoder adjust the input signal level */ if (codec_data->pcm_shift) { unsigned i; for (i=0; isamples_per_frame; ++i) { codec_data->enc_frame[i] = (Word16)(pcm_input[i] >> codec_data->pcm_shift); } pcm_input = codec_data->enc_frame; } /* Convert input samples to rmlt coefs */ mag_shift = samples_to_rmlt_coefs(pcm_input, codec_data->enc_old_frame, mlt_coefs, codec_data->samples_per_frame); /* Encode the mlt coefs. Note that encoder output stream is * 16 bit array, so we need to take care about endianness. */ encoder(codec_data->frame_size_bits, codec_data->number_of_regions, mlt_coefs, mag_shift, (Word16*)out_bits); /* Encoder output are in native host byte order, while ITU says * it must be in network byte order (MSB first). */ swap_bytes((pj_uint16_t*)out_bits, codec_data->frame_size/2); processed += codec_data->samples_per_frame; output->size += codec_data->frame_size; } output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->timestamp = input->timestamp; return PJ_SUCCESS; } /* * Decode frame. */ static pj_status_t codec_decode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { codec_private_t *codec_data = (codec_private_t*) codec->codec_data; Word16 mlt_coefs[MAX_SAMPLES_PER_FRAME]; Word16 mag_shift; Bit_Obj bitobj; Word16 frame_error_flag = 0; /* Check frame out length size */ PJ_ASSERT_RETURN(output_buf_len >= (unsigned)(codec_data->samples_per_frame<<1), PJMEDIA_CODEC_EPCMTOOSHORT); /* If input is NULL, perform PLC by settting frame_error_flag to 1 */ if (input) { /* Check frame in length size */ PJ_ASSERT_RETURN((pj_uint16_t)input->size == codec_data->frame_size, PJMEDIA_CODEC_EFRMINLEN); /* Decoder requires input of 16-bits array in native host byte * order, while the frame received from the network are in * network byte order (MSB first). */ swap_bytes((pj_uint16_t*)input->buf, codec_data->frame_size/2); bitobj.code_word_ptr = (Word16*)input->buf; bitobj.current_word = *bitobj.code_word_ptr; bitobj.code_bit_count = 0; bitobj.number_of_bits_left = codec_data->frame_size_bits; output->timestamp = input->timestamp; } else { pj_bzero(&bitobj, sizeof(bitobj)); frame_error_flag = 1; } /* Process the input frame to get mlt coefs */ decoder(&bitobj, &codec_data->dec_randobj, codec_data->number_of_regions, mlt_coefs, &mag_shift, &codec_data->dec_old_mag_shift, codec_data->dec_old_mlt_coefs, frame_error_flag); /* Convert the mlt_coefs to PCM samples */ rmlt_coefs_to_samples(mlt_coefs, codec_data->dec_old_frame, (Word16*)output->buf, codec_data->samples_per_frame, mag_shift); /* Decoder adjust PCM signal */ if (codec_data->pcm_shift) { unsigned i; pj_int16_t *buf = (Word16*)output->buf; for (i=0; isamples_per_frame; ++i) { buf[i] <<= codec_data->pcm_shift; } } output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->size = codec_data->samples_per_frame << 1; return PJ_SUCCESS; } /* * Recover lost frame. */ static pj_status_t codec_recover( pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output) { codec_private_t *codec_data = (codec_private_t*) codec->codec_data; /* Use native PLC when PLC is enabled. */ if (codec_data->plc_enabled) return codec_decode(codec, NULL, output_buf_len, output); /* Otherwise just return zero-fill frame. */ output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->size = codec_data->samples_per_frame << 1; pjmedia_zero_samples((pj_int16_t*)output->buf, codec_data->samples_per_frame); return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_G7221_CODEC */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/g7221_sdp_match.c ================================================ /* $Id: g7221_sdp_match.c 3911 2011-12-15 06:45:23Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #define GET_FMTP_IVAL_BASE(ival, base, fmtp, param, default_val) \ do { \ pj_str_t s; \ char *p; \ p = pj_stristr(&fmtp.fmt_param, ¶m); \ if (!p) { \ ival = default_val; \ break; \ } \ pj_strset(&s, p + param.slen, fmtp.fmt_param.slen - \ (p - fmtp.fmt_param.ptr) - param.slen); \ ival = pj_strtoul2(&s, NULL, base); \ } while (0) #define GET_FMTP_IVAL(ival, fmtp, param, default_val) \ GET_FMTP_IVAL_BASE(ival, 10, fmtp, param, default_val) PJ_DEF(pj_status_t) pjmedia_codec_g7221_match_sdp(pj_pool_t *pool, pjmedia_sdp_media *offer, unsigned o_fmt_idx, pjmedia_sdp_media *answer, unsigned a_fmt_idx, unsigned option) { const pjmedia_sdp_attr *attr_ans; const pjmedia_sdp_attr *attr_ofr; pjmedia_sdp_fmtp fmtp; unsigned a_bitrate, o_bitrate; const pj_str_t bitrate = {"bitrate=", 8}; pj_status_t status; PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(option); /* Parse offer */ attr_ofr = pjmedia_sdp_media_find_attr2(offer, "fmtp", &offer->desc.fmt[o_fmt_idx]); if (!attr_ofr) return PJMEDIA_SDP_EINFMTP; status = pjmedia_sdp_attr_get_fmtp(attr_ofr, &fmtp); if (status != PJ_SUCCESS) return status; GET_FMTP_IVAL(o_bitrate, fmtp, bitrate, 0); /* Parse answer */ attr_ans = pjmedia_sdp_media_find_attr2(answer, "fmtp", &answer->desc.fmt[a_fmt_idx]); if (!attr_ans) return PJMEDIA_SDP_EINFMTP; status = pjmedia_sdp_attr_get_fmtp(attr_ans, &fmtp); if (status != PJ_SUCCESS) return status; GET_FMTP_IVAL(a_bitrate, fmtp, bitrate, 0); /* Compare bitrate in answer and offer. */ if (a_bitrate != o_bitrate) return PJMEDIA_SDP_EFORMATNOTEQUAL; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/gsm.c ================================================ /* $Id: gsm.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include /* * Only build this file if PJMEDIA_HAS_GSM_CODEC != 0 */ #if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC != 0 #if defined(PJMEDIA_EXTERNAL_GSM_CODEC) && PJMEDIA_EXTERNAL_GSM_CODEC # if PJMEDIA_EXTERNAL_GSM_GSM_H # include # elif PJMEDIA_EXTERNAL_GSM_H # include # else # error Please set the location of gsm.h # endif #else # include "../../third_party/gsm/inc/gsm.h" #endif /* We removed PLC in 0.6 (and re-enabled it again in 0.9!) */ #define PLC_DISABLED 0 /* Prototypes for GSM factory */ static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory, const pjmedia_codec_info *id ); static pj_status_t gsm_default_attr( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ); static pj_status_t gsm_enum_codecs( pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]); static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec); static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ); /* Prototypes for GSM implementation. */ static pj_status_t gsm_codec_init( pjmedia_codec *codec, pj_pool_t *pool ); static pj_status_t gsm_codec_open( pjmedia_codec *codec, pjmedia_codec_param *attr ); static pj_status_t gsm_codec_close( pjmedia_codec *codec ); static pj_status_t gsm_codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr ); static pj_status_t gsm_codec_parse( pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]); static pj_status_t gsm_codec_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); static pj_status_t gsm_codec_decode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); #if !PLC_DISABLED static pj_status_t gsm_codec_recover(pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output); #endif /* Definition for GSM codec operations. */ static pjmedia_codec_op gsm_op = { &gsm_codec_init, &gsm_codec_open, &gsm_codec_close, &gsm_codec_modify, &gsm_codec_parse, &gsm_codec_encode, &gsm_codec_decode, #if !PLC_DISABLED &gsm_codec_recover #else NULL #endif }; /* Definition for GSM codec factory operations. */ static pjmedia_codec_factory_op gsm_factory_op = { &gsm_test_alloc, &gsm_default_attr, &gsm_enum_codecs, &gsm_alloc_codec, &gsm_dealloc_codec, &pjmedia_codec_gsm_deinit }; /* GSM factory */ static struct gsm_codec_factory { pjmedia_codec_factory base; pjmedia_endpt *endpt; pj_pool_t *pool; pj_mutex_t *mutex; pjmedia_codec codec_list; } gsm_codec_factory; /* GSM codec private data. */ struct gsm_data { struct gsm_state *encoder; struct gsm_state *decoder; pj_bool_t plc_enabled; #if !PLC_DISABLED pjmedia_plc *plc; #endif pj_bool_t vad_enabled; pjmedia_silence_det *vad; pj_timestamp last_tx; }; /* * Initialize and register GSM codec factory to pjmedia endpoint. */ PJ_DEF(pj_status_t) pjmedia_codec_gsm_init( pjmedia_endpt *endpt ) { pjmedia_codec_mgr *codec_mgr; pj_status_t status; if (gsm_codec_factory.pool != NULL) return PJ_SUCCESS; /* Create GSM codec factory. */ gsm_codec_factory.base.op = &gsm_factory_op; gsm_codec_factory.base.factory_data = NULL; gsm_codec_factory.endpt = endpt; gsm_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "gsm", 4000, 4000); if (!gsm_codec_factory.pool) return PJ_ENOMEM; pj_list_init(&gsm_codec_factory.codec_list); /* Create mutex. */ status = pj_mutex_create_simple(gsm_codec_factory.pool, "gsm", &gsm_codec_factory.mutex); if (status != PJ_SUCCESS) goto on_error; /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); if (!codec_mgr) { status = PJ_EINVALIDOP; goto on_error; } /* Register codec factory to endpoint. */ status = pjmedia_codec_mgr_register_factory(codec_mgr, &gsm_codec_factory.base); if (status != PJ_SUCCESS) goto on_error; /* Done. */ return PJ_SUCCESS; on_error: pj_pool_release(gsm_codec_factory.pool); gsm_codec_factory.pool = NULL; return status; } /* * Unregister GSM codec factory from pjmedia endpoint and deinitialize * the GSM codec library. */ PJ_DEF(pj_status_t) pjmedia_codec_gsm_deinit(void) { pjmedia_codec_mgr *codec_mgr; pj_status_t status; if (gsm_codec_factory.pool == NULL) return PJ_SUCCESS; /* We don't want to deinit if there's outstanding codec. */ /* This is silly, as we'll always have codec in the list if we ever allocate a codec! A better behavior maybe is to deallocate all codecs in the list. pj_mutex_lock(gsm_codec_factory.mutex); if (!pj_list_empty(&gsm_codec_factory.codec_list)) { pj_mutex_unlock(gsm_codec_factory.mutex); return PJ_EBUSY; } */ /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(gsm_codec_factory.endpt); if (!codec_mgr) { pj_pool_release(gsm_codec_factory.pool); gsm_codec_factory.pool = NULL; return PJ_EINVALIDOP; } /* Unregister GSM codec factory. */ status = pjmedia_codec_mgr_unregister_factory(codec_mgr, &gsm_codec_factory.base); /* Destroy mutex. */ pj_mutex_destroy(gsm_codec_factory.mutex); /* Destroy pool. */ pj_pool_release(gsm_codec_factory.pool); gsm_codec_factory.pool = NULL; return status; } /* * Check if factory can allocate the specified codec. */ static pj_status_t gsm_test_alloc( pjmedia_codec_factory *factory, const pjmedia_codec_info *info ) { PJ_UNUSED_ARG(factory); /* Check payload type. */ if (info->pt != PJMEDIA_RTP_PT_GSM) return PJMEDIA_CODEC_EUNSUP; /* Ignore the rest, since it's static payload type. */ return PJ_SUCCESS; } /* * Generate default attribute. */ static pj_status_t gsm_default_attr (pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ) { PJ_UNUSED_ARG(factory); PJ_UNUSED_ARG(id); pj_bzero(attr, sizeof(pjmedia_codec_param)); attr->info.clock_rate = 8000; attr->info.channel_cnt = 1; attr->info.avg_bps = 13200; attr->info.max_bps = 13200; attr->info.pcm_bits_per_sample = 16; attr->info.frm_ptime = 20; attr->info.pt = PJMEDIA_RTP_PT_GSM; attr->setting.frm_per_pkt = 1; attr->setting.vad = 1; #if !PLC_DISABLED attr->setting.plc = 1; #endif /* Default all other flag bits disabled. */ return PJ_SUCCESS; } /* * Enum codecs supported by this factory (i.e. only GSM!). */ static pj_status_t gsm_enum_codecs(pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]) { PJ_UNUSED_ARG(factory); PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); pj_bzero(&codecs[0], sizeof(pjmedia_codec_info)); codecs[0].encoding_name = pj_str("GSM"); codecs[0].pt = PJMEDIA_RTP_PT_GSM; codecs[0].type = PJMEDIA_TYPE_AUDIO; codecs[0].clock_rate = 8000; codecs[0].channel_cnt = 1; *count = 1; return PJ_SUCCESS; } /* * Allocate a new GSM codec instance. */ static pj_status_t gsm_alloc_codec( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec) { pjmedia_codec *codec; struct gsm_data *gsm_data; pj_status_t status; PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL); pj_mutex_lock(gsm_codec_factory.mutex); /* Get free nodes, if any. */ if (!pj_list_empty(&gsm_codec_factory.codec_list)) { codec = gsm_codec_factory.codec_list.next; pj_list_erase(codec); } else { codec = PJ_POOL_ZALLOC_T(gsm_codec_factory.pool, pjmedia_codec); PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM); codec->op = &gsm_op; codec->factory = factory; gsm_data = PJ_POOL_ZALLOC_T(gsm_codec_factory.pool, struct gsm_data); codec->codec_data = gsm_data; #if !PLC_DISABLED /* Create PLC */ status = pjmedia_plc_create(gsm_codec_factory.pool, 8000, 160, 0, &gsm_data->plc); if (status != PJ_SUCCESS) { pj_mutex_unlock(gsm_codec_factory.mutex); return status; } #endif /* Create silence detector */ status = pjmedia_silence_det_create(gsm_codec_factory.pool, 8000, 160, &gsm_data->vad); if (status != PJ_SUCCESS) { pj_mutex_unlock(gsm_codec_factory.mutex); return status; } } pj_mutex_unlock(gsm_codec_factory.mutex); *p_codec = codec; return PJ_SUCCESS; } /* * Free codec. */ static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ) { struct gsm_data *gsm_data; int i; PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL); gsm_data = (struct gsm_data*) codec->codec_data; /* Close codec, if it's not closed. */ gsm_codec_close(codec); #if !PLC_DISABLED /* Clear left samples in the PLC, since codec+plc will be reused * next time. */ for (i=0; i<2; ++i) { pj_int16_t frame[160]; pjmedia_zero_samples(frame, PJ_ARRAY_SIZE(frame)); pjmedia_plc_save(gsm_data->plc, frame); } #else PJ_UNUSED_ARG(i); #endif /* Re-init silence_period */ pj_set_timestamp32(&gsm_data->last_tx, 0, 0); /* Put in the free list. */ pj_mutex_lock(gsm_codec_factory.mutex); pj_list_push_front(&gsm_codec_factory.codec_list, codec); pj_mutex_unlock(gsm_codec_factory.mutex); return PJ_SUCCESS; } /* * Init codec. */ static pj_status_t gsm_codec_init( pjmedia_codec *codec, pj_pool_t *pool ) { PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(pool); return PJ_SUCCESS; } /* * Open codec. */ static pj_status_t gsm_codec_open( pjmedia_codec *codec, pjmedia_codec_param *attr ) { struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data; pj_assert(gsm_data != NULL); pj_assert(gsm_data->encoder == NULL && gsm_data->decoder == NULL); gsm_data->encoder = gsm_create(); if (!gsm_data->encoder) return PJMEDIA_CODEC_EFAILED; gsm_data->decoder = gsm_create(); if (!gsm_data->decoder) return PJMEDIA_CODEC_EFAILED; gsm_data->vad_enabled = (attr->setting.vad != 0); gsm_data->plc_enabled = (attr->setting.plc != 0); return PJ_SUCCESS; } /* * Close codec. */ static pj_status_t gsm_codec_close( pjmedia_codec *codec ) { struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data; pj_assert(gsm_data != NULL); if (gsm_data->encoder) { gsm_destroy(gsm_data->encoder); gsm_data->encoder = NULL; } if (gsm_data->decoder) { gsm_destroy(gsm_data->decoder); gsm_data->decoder = NULL; } return PJ_SUCCESS; } /* * Modify codec settings. */ static pj_status_t gsm_codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr ) { struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data; pj_assert(gsm_data != NULL); pj_assert(gsm_data->encoder != NULL && gsm_data->decoder != NULL); gsm_data->vad_enabled = (attr->setting.vad != 0); gsm_data->plc_enabled = (attr->setting.plc != 0); return PJ_SUCCESS; } /* * Get frames in the packet. */ static pj_status_t gsm_codec_parse( pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]) { unsigned count = 0; PJ_UNUSED_ARG(codec); PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL); while (pkt_size >= 33 && count < *frame_cnt) { frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO; frames[count].buf = pkt; frames[count].size = 33; frames[count].timestamp.u64 = ts->u64 + count * 160; pkt = ((char*)pkt) + 33; pkt_size -= 33; ++count; } *frame_cnt = count; return PJ_SUCCESS; } /* * Encode frame. */ static pj_status_t gsm_codec_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data; pj_int16_t *pcm_in; pj_size_t in_size; pj_assert(gsm_data && input && output); pcm_in = (pj_int16_t*)input->buf; in_size = input->size; PJ_ASSERT_RETURN(in_size % 320 == 0, PJMEDIA_CODEC_EPCMFRMINLEN); PJ_ASSERT_RETURN(output_buf_len >= 33 * in_size/320, PJMEDIA_CODEC_EFRMTOOSHORT); /* Detect silence */ if (gsm_data->vad_enabled) { pj_bool_t is_silence; pj_int32_t silence_duration; silence_duration = pj_timestamp_diff32(&gsm_data->last_tx, &input->timestamp); is_silence = pjmedia_silence_det_detect(gsm_data->vad, (const pj_int16_t*) input->buf, (input->size >> 1), NULL); if (is_silence && (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 || silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*8000/1000)) { output->type = PJMEDIA_FRAME_TYPE_NONE; output->buf = NULL; output->size = 0; output->timestamp = input->timestamp; return PJ_SUCCESS; } else { gsm_data->last_tx = input->timestamp; } } /* Encode */ output->size = 0; while (in_size >= 320) { gsm_encode(gsm_data->encoder, pcm_in, (unsigned char*)output->buf + output->size); pcm_in += 160; output->size += 33; in_size -= 320; } output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->timestamp = input->timestamp; return PJ_SUCCESS; } /* * Decode frame. */ static pj_status_t gsm_codec_decode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data; pj_assert(gsm_data != NULL); PJ_ASSERT_RETURN(input && output, PJ_EINVAL); if (output_buf_len < 320) return PJMEDIA_CODEC_EPCMTOOSHORT; if (input->size < 33) return PJMEDIA_CODEC_EFRMTOOSHORT; gsm_decode(gsm_data->decoder, (unsigned char*)input->buf, (short*)output->buf); output->size = 320; output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->timestamp = input->timestamp; #if !PLC_DISABLED if (gsm_data->plc_enabled) pjmedia_plc_save( gsm_data->plc, (pj_int16_t*)output->buf); #endif return PJ_SUCCESS; } #if !PLC_DISABLED /* * Recover lost frame. */ static pj_status_t gsm_codec_recover(pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output) { struct gsm_data *gsm_data = (struct gsm_data*) codec->codec_data; PJ_ASSERT_RETURN(gsm_data->plc_enabled, PJ_EINVALIDOP); PJ_ASSERT_RETURN(output_buf_len >= 320, PJMEDIA_CODEC_EPCMTOOSHORT); pjmedia_plc_generate(gsm_data->plc, (pj_int16_t*)output->buf); output->size = 320; return PJ_SUCCESS; } #endif #endif /* PJMEDIA_HAS_GSM_CODEC */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/h263_packetizer.c ================================================ /* $Id: h263_packetizer.c 4006 2012-04-02 08:40:54Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #define THIS_FILE "h263_packetizer.c" /* H.263 packetizer definition */ struct pjmedia_h263_packetizer { /* Current settings */ pjmedia_h263_packetizer_cfg cfg; /* Unpacketizer state */ unsigned unpack_last_sync_pos; pj_bool_t unpack_prev_lost; }; /* * Find synchronization point (PSC, slice, GSBC, EOS, EOSBS) in H.263 * bitstream. */ static pj_uint8_t* find_sync_point(pj_uint8_t *data, pj_size_t data_len) { pj_uint8_t *p = data, *end = data+data_len-1; while (p < end && (*p || *(p+1))) ++p; if (p == end) return NULL; return p; } /* * Find synchronization point (PSC, slice, GSBC, EOS, EOSBS) in H.263 * bitstream, in reversed manner. */ static pj_uint8_t* find_sync_point_rev(pj_uint8_t *data, pj_size_t data_len) { pj_uint8_t *p = data+data_len-2; while (p >= data && (*p || *(p+1))) --p; if (p < data) return (data + data_len); return p; } /* * Create H263 packetizer. */ PJ_DEF(pj_status_t) pjmedia_h263_packetizer_create( pj_pool_t *pool, const pjmedia_h263_packetizer_cfg *cfg, pjmedia_h263_packetizer **p) { pjmedia_h263_packetizer *p_; PJ_ASSERT_RETURN(pool && p, PJ_EINVAL); if (cfg && cfg->mode != PJMEDIA_H263_PACKETIZER_MODE_RFC4629) return PJ_ENOTSUP; p_ = PJ_POOL_ZALLOC_T(pool, pjmedia_h263_packetizer); if (cfg) { pj_memcpy(&p_->cfg, cfg, sizeof(*cfg)); } else { p_->cfg.mode = PJMEDIA_H263_PACKETIZER_MODE_RFC4629; p_->cfg.mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE; } *p = p_; return PJ_SUCCESS; } /* * Generate an RTP payload from H.263 frame bitstream, in-place processing. */ PJ_DEF(pj_status_t) pjmedia_h263_packetize(pjmedia_h263_packetizer *pktz, pj_uint8_t *bits, pj_size_t bits_len, unsigned *pos, const pj_uint8_t **payload, pj_size_t *payload_len) { pj_uint8_t *p, *end; pj_assert(pktz && bits && pos && payload && payload_len); pj_assert(*pos <= bits_len); p = bits + *pos; end = bits + bits_len; /* Put two octets payload header */ if ((end-p > 2) && *p==0 && *(p+1)==0) { /* The bitstream starts with synchronization point, just override * the two zero octets (sync point mark) for payload header. */ *p = 0x04; } else { /* Not started in synchronization point, we will use two octets * preceeding the bitstream for payload header! */ if (*pos < 2) { /* Invalid H263 bitstream, it's not started with PSC */ return PJ_EINVAL; } p -= 2; *p = 0; } *(p+1) = 0; /* When bitstream truncation needed because of payload length/MTU * limitation, try to use sync point for the payload boundary. */ if (end-p > pktz->cfg.mtu) { end = find_sync_point_rev(p+2, pktz->cfg.mtu-2); } *payload = p; *payload_len = end-p; *pos = (unsigned)(end - bits); return PJ_SUCCESS; } /* * Append an RTP payload to a H.263 picture bitstream. */ PJ_DEF(pj_status_t) pjmedia_h263_unpacketize (pjmedia_h263_packetizer *pktz, const pj_uint8_t *payload, pj_size_t payload_len, pj_uint8_t *bits, pj_size_t bits_size, unsigned *pos) { pj_uint8_t P, V, PLEN; const pj_uint8_t *p = payload; pj_uint8_t *q; q = bits + *pos; /* Check if this is a missing/lost packet */ if (payload == NULL) { pktz->unpack_prev_lost = PJ_TRUE; return PJ_SUCCESS; } /* H263 payload header size is two octets */ if (payload_len < 2) { /* Invalid bitstream, discard this payload */ pktz->unpack_prev_lost = PJ_TRUE; return PJ_EINVAL; } /* Reset last sync point for every new picture bitstream */ if (*pos == 0) pktz->unpack_last_sync_pos = 0; /* Get payload header info */ P = *p & 0x04; V = *p & 0x02; PLEN = ((*p & 0x01) << 5) + ((*(p+1) & 0xF8)>>3); /* Get start bitstream pointer */ p += 2; /* Skip payload header */ if (V) p += 1; /* Skip VRC data */ if (PLEN) p += PLEN; /* Skip extra picture header data */ /* Get bitstream length */ if (payload_len > (pj_size_t)(p - payload)) { payload_len -= (p - payload); } else { /* Invalid bitstream, discard this payload */ pktz->unpack_prev_lost = PJ_TRUE; return PJ_EINVAL; } /* Validate bitstream length */ if (bits_size < *pos + payload_len + 2) { /* Insufficient bistream buffer, discard this payload */ pj_assert(!"Insufficient H.263 bitstream buffer"); pktz->unpack_prev_lost = PJ_TRUE; return PJ_ETOOSMALL; } /* Start writing bitstream */ /* No sync point flag */ if (!P) { if (*pos == 0) { /* Previous packet must be lost */ pktz->unpack_prev_lost = PJ_TRUE; /* If there is extra picture header, let's use it. */ if (PLEN) { /* Write two zero octets for PSC */ *q++ = 0; *q++ = 0; /* Copy the picture header */ p -= PLEN; pj_memcpy(q, p, PLEN); p += PLEN; q += PLEN; } } else if (pktz->unpack_prev_lost) { /* If prev packet was lost, revert the bitstream pointer to * the last sync point. */ pj_assert(pktz->unpack_last_sync_pos <= *pos); q = bits + pktz->unpack_last_sync_pos; } /* There was packet lost, see if this payload contain sync point * (usable data). */ if (pktz->unpack_prev_lost) { pj_uint8_t *sync; sync = find_sync_point((pj_uint8_t*)p, payload_len); if (sync) { /* Got sync point, update P/sync-point flag */ P = 1; /* Skip the two zero octets */ sync += 2; /* Update payload length and start bitstream pointer */ payload_len -= (sync - p); p = sync; } else { /* No sync point in it, just discard this payload */ return PJ_EIGNORED; } } } /* Write two zero octets when payload flagged with sync point */ if (P) { pktz->unpack_last_sync_pos = (unsigned)(q - bits); *q++ = 0; *q++ = 0; } /* Write the payload to the bitstream */ pj_memcpy(q, p, payload_len); q += payload_len; /* Update the bitstream writing offset */ *pos = (unsigned)(q - bits); pktz->unpack_prev_lost = PJ_FALSE; return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_VIDEO */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/h264_packetizer.c ================================================ /* $Id: h264_packetizer.c 4006 2012-04-02 08:40:54Z nanang $ */ /* * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #define THIS_FILE "h264_packetizer.c" #define DBG_PACKETIZE 0 #define DBG_UNPACKETIZE 0 /* H.264 packetizer definition */ struct pjmedia_h264_packetizer { /* Current settings */ pjmedia_h264_packetizer_cfg cfg; /* Unpacketizer state */ unsigned unpack_last_sync_pos; pj_bool_t unpack_prev_lost; }; /* Enumeration of H.264 NAL unit types */ enum { NAL_TYPE_SINGLE_NAL_MIN = 1, NAL_TYPE_SINGLE_NAL_MAX = 23, NAL_TYPE_STAP_A = 24, NAL_TYPE_FU_A = 28, }; /* * Find next NAL unit from the specified H.264 bitstream data. */ static pj_uint8_t* find_next_nal_unit(pj_uint8_t *start, pj_uint8_t *end) { pj_uint8_t *p = start; /* Simply lookup "0x000001" pattern */ while (p <= end-3 && (p[0] || p[1] || p[2]!=1)) ++p; if (p > end-3) /* No more NAL unit in this bitstream */ return NULL; /* Include 8 bits leading zero */ if (p>start && *(p-1)==0) return (p-1); return p; } /* * Create H264 packetizer. */ PJ_DEF(pj_status_t) pjmedia_h264_packetizer_create( pj_pool_t *pool, const pjmedia_h264_packetizer_cfg *cfg, pjmedia_h264_packetizer **p) { pjmedia_h264_packetizer *p_; PJ_ASSERT_RETURN(pool && p, PJ_EINVAL); if (cfg && cfg->mode != PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED && cfg->mode != PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL) { return PJ_ENOTSUP; } p_ = PJ_POOL_ZALLOC_T(pool, pjmedia_h264_packetizer); if (cfg) { pj_memcpy(&p_->cfg, cfg, sizeof(*cfg)); } else { p_->cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED; p_->cfg.mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE; } *p = p_; return PJ_SUCCESS; } /* * Generate an RTP payload from H.264 frame bitstream, in-place processing. */ PJ_DEF(pj_status_t) pjmedia_h264_packetize(pjmedia_h264_packetizer *pktz, pj_uint8_t *buf, pj_size_t buf_len, unsigned *pos, const pj_uint8_t **payload, pj_size_t *payload_len) { pj_uint8_t *nal_start = NULL, *nal_end = NULL, *nal_octet = NULL; pj_uint8_t *p, *end; enum { HEADER_SIZE_FU_A = 2, HEADER_SIZE_STAP_A = 3, }; enum { MAX_NALS_IN_AGGR = 32 }; #if DBG_PACKETIZE if (*pos == 0 && buf_len) { PJ_LOG(3, ("h264pack", "<< Start packing new frame >>")); } #endif p = buf + *pos; end = buf + buf_len; /* Find NAL unit startcode */ if (end-p >= 4) nal_start = find_next_nal_unit(p, p+4); if (nal_start) { /* Get NAL unit octet pointer */ while (*nal_start++ == 0); nal_octet = nal_start; } else { /* This NAL unit is being fragmented */ nal_start = p; } /* Get end of NAL unit */ p = nal_start+pktz->cfg.mtu+1; if (p > end || pktz->cfg.mode==PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL) p = end; nal_end = find_next_nal_unit(nal_start, p); if (!nal_end) nal_end = p; /* Validate MTU vs NAL length on single NAL unit packetization */ if ((pktz->cfg.mode==PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL) && nal_end - nal_start > pktz->cfg.mtu) { //pj_assert(!"MTU too small for H.264 single NAL packetization mode"); PJ_LOG(2,("h264_packetizer.c", "MTU too small for H.264 (required=%u, MTU=%u)", nal_end - nal_start, pktz->cfg.mtu)); return PJ_ETOOSMALL; } /* Evaluate the proper payload format structure */ /* Fragmentation (FU-A) packet */ if ((pktz->cfg.mode != PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL) && (!nal_octet || nal_end-nal_start > pktz->cfg.mtu)) { pj_uint8_t NRI, TYPE; if (nal_octet) { /* We have NAL unit octet, so this is the first fragment */ NRI = (*nal_octet & 0x60) >> 5; TYPE = *nal_octet & 0x1F; /* Skip nal_octet in nal_start to be overriden by FU header */ ++nal_start; } else { /* Not the first fragment, get NRI and NAL unit type * from the previous fragment. */ p = nal_start - pktz->cfg.mtu; NRI = (*p & 0x60) >> 5; TYPE = *(p+1) & 0x1F; } /* Init FU indicator (one octet: F+NRI+TYPE) */ p = nal_start - HEADER_SIZE_FU_A; *p = (NRI << 5) | NAL_TYPE_FU_A; ++p; /* Init FU header (one octed: S+E+R+TYPE) */ *p = TYPE; if (nal_octet) *p |= (1 << 7); /* S bit flag = start of fragmentation */ if (nal_end-nal_start+HEADER_SIZE_FU_A <= pktz->cfg.mtu) *p |= (1 << 6); /* E bit flag = end of fragmentation */ /* Set payload, payload length */ *payload = nal_start - HEADER_SIZE_FU_A; if (nal_end-nal_start+HEADER_SIZE_FU_A > pktz->cfg.mtu) *payload_len = pktz->cfg.mtu; else *payload_len = nal_end - nal_start + HEADER_SIZE_FU_A; *pos = (unsigned)(*payload + *payload_len - buf); #if DBG_PACKETIZE PJ_LOG(3, ("h264pack", "Packetized fragmented H264 NAL unit " "(pos=%d, type=%d, NRI=%d, S=%d, E=%d, len=%d/%d)", *payload-buf, TYPE, NRI, *p>>7, (*p>>6)&1, *payload_len, buf_len)); #endif return PJ_SUCCESS; } /* Aggregation (STAP-A) packet */ if ((pktz->cfg.mode != PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL) && (nal_end != end) && (nal_end - nal_start + HEADER_SIZE_STAP_A) < pktz->cfg.mtu) { int total_size; unsigned nal_cnt = 1; pj_uint8_t *nal[MAX_NALS_IN_AGGR]; pj_size_t nal_size[MAX_NALS_IN_AGGR]; pj_uint8_t NRI; pj_assert(nal_octet); /* Init the first NAL unit in the packet */ nal[0] = nal_start; nal_size[0] = nal_end - nal_start; total_size = (int)nal_size[0] + HEADER_SIZE_STAP_A; NRI = (*nal_octet & 0x60) >> 5; /* Populate next NAL units */ while (nal_cnt < MAX_NALS_IN_AGGR) { pj_uint8_t *tmp_end; /* Find start address of the next NAL unit */ p = nal[nal_cnt-1] + nal_size[nal_cnt-1]; while (*p++ == 0); nal[nal_cnt] = p; /* Find end address of the next NAL unit */ tmp_end = p + (pktz->cfg.mtu - total_size); if (tmp_end > end) tmp_end = end; p = find_next_nal_unit(p+1, tmp_end); if (p) { nal_size[nal_cnt] = p - nal[nal_cnt]; } else { break; } /* Update total payload size (2 octet NAL size + NAL) */ total_size += (2 + (int)nal_size[nal_cnt]); if (total_size <= pktz->cfg.mtu) { pj_uint8_t tmp_nri; /* Get maximum NRI of the aggregated NAL units */ tmp_nri = (*(nal[nal_cnt]-1) & 0x60) >> 5; if (tmp_nri > NRI) NRI = tmp_nri; } else { break; } ++nal_cnt; } /* Only use STAP-A when we found more than one NAL units */ if (nal_cnt > 1) { unsigned i; /* Init STAP-A NAL header (F+NRI+TYPE) */ p = nal[0] - HEADER_SIZE_STAP_A; *p++ = (NRI << 5) | NAL_TYPE_STAP_A; /* Append all populated NAL units into payload (SIZE+NAL) */ for (i = 0; i < nal_cnt; ++i) { /* Put size (2 octets in network order) */ pj_assert(nal_size[i] <= 0xFFFF); *p++ = (pj_uint8_t)(nal_size[i] >> 8); *p++ = (pj_uint8_t)(nal_size[i] & 0xFF); /* Append NAL unit, watchout memmove()-ing bitstream! */ if (p != nal[i]) pj_memmove(p, nal[i], nal_size[i]); p += nal_size[i]; } /* Set payload, payload length, and pos */ *payload = nal[0] - HEADER_SIZE_STAP_A; pj_assert(*payload >= buf+*pos); *payload_len = p - *payload; *pos = (unsigned)(nal[nal_cnt-1] + nal_size[nal_cnt-1] - buf); #if DBG_PACKETIZE PJ_LOG(3, ("h264pack", "Packetized aggregation of " "%d H264 NAL units (pos=%d, NRI=%d len=%d/%d)", nal_cnt, *payload-buf, NRI, *payload_len, buf_len)); #endif return PJ_SUCCESS; } } /* Single NAL unit packet */ *payload = nal_start; *payload_len = nal_end - nal_start; *pos = (unsigned)(nal_end - buf); #if DBG_PACKETIZE PJ_LOG(3, ("h264pack", "Packetized single H264 NAL unit " "(pos=%d, type=%d, NRI=%d, len=%d/%d)", nal_start-buf, *nal_octet&0x1F, (*nal_octet&0x60)>>5, *payload_len, buf_len)); #endif return PJ_SUCCESS; } /* * Append RTP payload to a H.264 picture bitstream. Note that the only * payload format that cares about packet lost is the NAL unit * fragmentation format (FU-A/B), so we will only manage the "prev_lost" * state for the FU-A/B packets. */ PJ_DEF(pj_status_t) pjmedia_h264_unpacketize(pjmedia_h264_packetizer *pktz, const pj_uint8_t *payload, pj_size_t payload_len, pj_uint8_t *bits, pj_size_t bits_len, unsigned *bits_pos) { const pj_uint8_t nal_start_code[3] = {0, 0, 1}; enum { MIN_PAYLOAD_SIZE = 2 }; pj_uint8_t nal_type; PJ_UNUSED_ARG(pktz); #if DBG_UNPACKETIZE if (*bits_pos == 0 && payload_len) { PJ_LOG(3, ("h264unpack", ">> Start unpacking new frame <<")); } #endif /* Check if this is a missing/lost packet */ if (payload == NULL) { pktz->unpack_prev_lost = PJ_TRUE; return PJ_SUCCESS; } /* H264 payload size */ if (payload_len < MIN_PAYLOAD_SIZE) { /* Invalid bitstream, discard this payload */ pktz->unpack_prev_lost = PJ_TRUE; return PJ_EINVAL; } /* Reset last sync point for every new picture bitstream */ if (*bits_pos == 0) pktz->unpack_last_sync_pos = 0; nal_type = *payload & 0x1F; if (nal_type >= NAL_TYPE_SINGLE_NAL_MIN && nal_type <= NAL_TYPE_SINGLE_NAL_MAX) { /* Single NAL unit packet */ pj_uint8_t *p = bits + *bits_pos; /* Validate bitstream length */ if (bits_len-*bits_pos < payload_len+PJ_ARRAY_SIZE(nal_start_code)) { /* Insufficient bistream buffer, discard this payload */ pj_assert(!"Insufficient H.263 bitstream buffer"); return PJ_ETOOSMALL; } /* Write NAL unit start code */ pj_memcpy(p, &nal_start_code, PJ_ARRAY_SIZE(nal_start_code)); p += PJ_ARRAY_SIZE(nal_start_code); /* Write NAL unit */ pj_memcpy(p, payload, payload_len); p += payload_len; /* Update the bitstream writing offset */ *bits_pos = (unsigned)(p - bits); pktz->unpack_last_sync_pos = *bits_pos; #if DBG_UNPACKETIZE PJ_LOG(3, ("h264unpack", "Unpacked single H264 NAL unit " "(type=%d, NRI=%d, len=%d)", nal_type, (*payload&0x60)>>5, payload_len)); #endif } else if (nal_type == NAL_TYPE_STAP_A) { /* Aggregation packet */ pj_uint8_t *p, *p_end; const pj_uint8_t *q, *q_end; unsigned cnt = 0; /* Validate bitstream length */ if (bits_len - *bits_pos < payload_len + 32) { /* Insufficient bistream buffer, discard this payload */ pj_assert(!"Insufficient H.263 bitstream buffer"); return PJ_ETOOSMALL; } /* Fill bitstream */ p = bits + *bits_pos; p_end = bits + bits_len; q = payload + 1; q_end = payload + payload_len; while (q < q_end && p < p_end) { pj_uint16_t tmp_nal_size; /* Write NAL unit start code */ pj_memcpy(p, &nal_start_code, PJ_ARRAY_SIZE(nal_start_code)); p += PJ_ARRAY_SIZE(nal_start_code); /* Get NAL unit size */ tmp_nal_size = (*q << 8) | *(q+1); q += 2; if (q + tmp_nal_size > q_end) { /* Invalid bitstream, discard the rest of the payload */ return PJ_EINVAL; } /* Write NAL unit */ pj_memcpy(p, q, tmp_nal_size); p += tmp_nal_size; q += tmp_nal_size; ++cnt; /* Update the bitstream writing offset */ *bits_pos = (unsigned)(p - bits); pktz->unpack_last_sync_pos = *bits_pos; } #if DBG_UNPACKETIZE PJ_LOG(3, ("h264unpack", "Unpacked %d H264 NAL units (len=%d)", cnt, payload_len)); #endif } else if (nal_type == NAL_TYPE_FU_A) { /* Fragmentation packet */ pj_uint8_t *p; const pj_uint8_t *q = payload; pj_uint8_t NRI, TYPE, S, E; p = bits + *bits_pos; /* Validate bitstream length */ if (bits_len-*bits_pos < payload_len+PJ_ARRAY_SIZE(nal_start_code)) { /* Insufficient bistream buffer, drop this packet */ pj_assert(!"Insufficient H.263 bitstream buffer"); pktz->unpack_prev_lost = PJ_TRUE; return PJ_ETOOSMALL; } /* Get info */ S = *(q+1) & 0x80; /* Start bit flag */ E = *(q+1) & 0x40; /* End bit flag */ TYPE = *(q+1) & 0x1f; NRI = (*q & 0x60) >> 5; /* Fill bitstream */ if (S) { /* This is the first part, write NAL unit start code */ pj_memcpy(p, &nal_start_code, PJ_ARRAY_SIZE(nal_start_code)); p += PJ_ARRAY_SIZE(nal_start_code); /* Write NAL unit octet */ *p++ = (NRI << 5) | TYPE; } else if (pktz->unpack_prev_lost) { /* If prev packet was lost, revert the bitstream pointer to * the last sync point. */ pj_assert(pktz->unpack_last_sync_pos <= *bits_pos); *bits_pos = pktz->unpack_last_sync_pos; /* And discard this payload (and the following fragmentation * payloads carrying this same NAL unit. */ return PJ_EIGNORED; } q += 2; /* Write NAL unit */ pj_memcpy(p, q, payload_len - 2); p += (payload_len - 2); /* Update the bitstream writing offset */ *bits_pos = (unsigned)(p - bits); if (E) { /* Update the sync pos only if the end bit flag is set */ pktz->unpack_last_sync_pos = *bits_pos; } #if DBG_UNPACKETIZE PJ_LOG(3, ("h264unpack", "Unpacked fragmented H264 NAL unit " "(type=%d, NRI=%d, len=%d)", TYPE, NRI, payload_len)); #endif } else { *bits_pos = 0; return PJ_ENOTSUP; } pktz->unpack_prev_lost = PJ_FALSE; return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_VIDEO */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/ilbc.c ================================================ /* $Id: ilbc.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO #include #define iLBC_Enc_Inst_t AudioConverterRef #define iLBC_Dec_Inst_t AudioConverterRef #define BLOCKL_MAX 1 #else #include "../../third_party/ilbc/iLBC_encode.h" #include "../../third_party/ilbc/iLBC_decode.h" #endif /* * Only build this file if PJMEDIA_HAS_ILBC_CODEC != 0 */ #if defined(PJMEDIA_HAS_ILBC_CODEC) && PJMEDIA_HAS_ILBC_CODEC != 0 #define THIS_FILE "ilbc.c" #define CLOCK_RATE 8000 #define DEFAULT_MODE 30 /* Prototypes for iLBC factory */ static pj_status_t ilbc_test_alloc(pjmedia_codec_factory *factory, const pjmedia_codec_info *id ); static pj_status_t ilbc_default_attr(pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ); static pj_status_t ilbc_enum_codecs(pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]); static pj_status_t ilbc_alloc_codec(pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec); static pj_status_t ilbc_dealloc_codec(pjmedia_codec_factory *factory, pjmedia_codec *codec ); /* Prototypes for iLBC implementation. */ static pj_status_t ilbc_codec_init(pjmedia_codec *codec, pj_pool_t *pool ); static pj_status_t ilbc_codec_open(pjmedia_codec *codec, pjmedia_codec_param *attr ); static pj_status_t ilbc_codec_close(pjmedia_codec *codec ); static pj_status_t ilbc_codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr ); static pj_status_t ilbc_codec_parse(pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]); static pj_status_t ilbc_codec_encode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); static pj_status_t ilbc_codec_decode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); static pj_status_t ilbc_codec_recover(pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output); /* Definition for iLBC codec operations. */ static pjmedia_codec_op ilbc_op = { &ilbc_codec_init, &ilbc_codec_open, &ilbc_codec_close, &ilbc_codec_modify, &ilbc_codec_parse, &ilbc_codec_encode, &ilbc_codec_decode, &ilbc_codec_recover }; /* Definition for iLBC codec factory operations. */ static pjmedia_codec_factory_op ilbc_factory_op = { &ilbc_test_alloc, &ilbc_default_attr, &ilbc_enum_codecs, &ilbc_alloc_codec, &ilbc_dealloc_codec, &pjmedia_codec_ilbc_deinit }; /* iLBC factory */ static struct ilbc_factory { pjmedia_codec_factory base; pjmedia_endpt *endpt; int mode; int bps; } ilbc_factory; /* iLBC codec private data. */ struct ilbc_codec { pjmedia_codec base; pj_pool_t *pool; char obj_name[PJ_MAX_OBJ_NAME]; pjmedia_silence_det *vad; pj_bool_t vad_enabled; pj_bool_t plc_enabled; pj_timestamp last_tx; pj_bool_t enc_ready; iLBC_Enc_Inst_t enc; unsigned enc_frame_size; unsigned enc_samples_per_frame; float enc_block[BLOCKL_MAX]; pj_bool_t dec_ready; iLBC_Dec_Inst_t dec; unsigned dec_frame_size; unsigned dec_samples_per_frame; float dec_block[BLOCKL_MAX]; #if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO unsigned enc_total_packets; char *enc_buffer; unsigned enc_buffer_offset; unsigned dec_total_packets; char *dec_buffer; unsigned dec_buffer_offset; #endif }; static pj_str_t STR_MODE = {"mode", 4}; /* * Initialize and register iLBC codec factory to pjmedia endpoint. */ PJ_DEF(pj_status_t) pjmedia_codec_ilbc_init( pjmedia_endpt *endpt, int mode ) { pjmedia_codec_mgr *codec_mgr; pj_status_t status; PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL); PJ_ASSERT_RETURN(mode==0 || mode==20 || mode==30, PJ_EINVAL); /* Create iLBC codec factory. */ ilbc_factory.base.op = &ilbc_factory_op; ilbc_factory.base.factory_data = NULL; ilbc_factory.endpt = endpt; if (mode == 0) mode = DEFAULT_MODE; ilbc_factory.mode = mode; if (mode == 20) { ilbc_factory.bps = 15200; } else { ilbc_factory.bps = 13333; } /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); if (!codec_mgr) return PJ_EINVALIDOP; /* Register codec factory to endpoint. */ status = pjmedia_codec_mgr_register_factory(codec_mgr, &ilbc_factory.base); if (status != PJ_SUCCESS) return status; /* Done. */ return PJ_SUCCESS; } /* * Unregister iLBC codec factory from pjmedia endpoint and deinitialize * the iLBC codec library. */ PJ_DEF(pj_status_t) pjmedia_codec_ilbc_deinit(void) { pjmedia_codec_mgr *codec_mgr; pj_status_t status; /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(ilbc_factory.endpt); if (!codec_mgr) return PJ_EINVALIDOP; /* Unregister iLBC codec factory. */ status = pjmedia_codec_mgr_unregister_factory(codec_mgr, &ilbc_factory.base); return status; } /* * Check if factory can allocate the specified codec. */ static pj_status_t ilbc_test_alloc( pjmedia_codec_factory *factory, const pjmedia_codec_info *info ) { const pj_str_t ilbc_tag = { "iLBC", 4}; PJ_UNUSED_ARG(factory); PJ_ASSERT_RETURN(factory==&ilbc_factory.base, PJ_EINVAL); /* Type MUST be audio. */ if (info->type != PJMEDIA_TYPE_AUDIO) return PJMEDIA_CODEC_EUNSUP; /* Check encoding name. */ if (pj_stricmp(&info->encoding_name, &ilbc_tag) != 0) return PJMEDIA_CODEC_EUNSUP; /* Check clock-rate */ if (info->clock_rate != CLOCK_RATE) return PJMEDIA_CODEC_EUNSUP; /* Channel count must be one */ if (info->channel_cnt != 1) return PJMEDIA_CODEC_EUNSUP; /* Yes, this should be iLBC! */ return PJ_SUCCESS; } /* * Generate default attribute. */ static pj_status_t ilbc_default_attr (pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ) { PJ_UNUSED_ARG(factory); PJ_ASSERT_RETURN(factory==&ilbc_factory.base, PJ_EINVAL); PJ_UNUSED_ARG(id); PJ_ASSERT_RETURN(pj_stricmp2(&id->encoding_name, "iLBC")==0, PJ_EINVAL); pj_bzero(attr, sizeof(pjmedia_codec_param)); attr->info.clock_rate = CLOCK_RATE; attr->info.channel_cnt = 1; attr->info.avg_bps = ilbc_factory.bps; attr->info.max_bps = 15200; attr->info.pcm_bits_per_sample = 16; attr->info.frm_ptime = (short)ilbc_factory.mode; attr->info.pt = PJMEDIA_RTP_PT_ILBC; attr->setting.frm_per_pkt = 1; attr->setting.vad = 1; attr->setting.plc = 1; attr->setting.penh = 1; attr->setting.dec_fmtp.cnt = 1; attr->setting.dec_fmtp.param[0].name = STR_MODE; if (ilbc_factory.mode == 30) attr->setting.dec_fmtp.param[0].val = pj_str("30"); else attr->setting.dec_fmtp.param[0].val = pj_str("20"); return PJ_SUCCESS; } /* * Enum codecs supported by this factory (i.e. only iLBC!). */ static pj_status_t ilbc_enum_codecs(pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]) { PJ_UNUSED_ARG(factory); PJ_ASSERT_RETURN(factory==&ilbc_factory.base, PJ_EINVAL); PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); pj_bzero(&codecs[0], sizeof(pjmedia_codec_info)); codecs[0].encoding_name = pj_str("iLBC"); codecs[0].pt = PJMEDIA_RTP_PT_ILBC; codecs[0].type = PJMEDIA_TYPE_AUDIO; codecs[0].clock_rate = 8000; codecs[0].channel_cnt = 1; *count = 1; return PJ_SUCCESS; } /* * Allocate a new iLBC codec instance. */ static pj_status_t ilbc_alloc_codec(pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec) { pj_pool_t *pool; struct ilbc_codec *codec; PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &ilbc_factory.base, PJ_EINVAL); pool = pjmedia_endpt_create_pool(ilbc_factory.endpt, "iLBC%p", 2000, 2000); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); codec = PJ_POOL_ZALLOC_T(pool, struct ilbc_codec); codec->base.op = &ilbc_op; codec->base.factory = factory; codec->pool = pool; pj_ansi_snprintf(codec->obj_name, sizeof(codec->obj_name), "ilbc%p", codec); *p_codec = &codec->base; return PJ_SUCCESS; } /* * Free codec. */ static pj_status_t ilbc_dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ) { struct ilbc_codec *ilbc_codec; PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); PJ_UNUSED_ARG(factory); PJ_ASSERT_RETURN(factory == &ilbc_factory.base, PJ_EINVAL); ilbc_codec = (struct ilbc_codec*) codec; #if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO if (ilbc_codec->enc) { AudioConverterDispose(ilbc_codec->enc); ilbc_codec->enc = NULL; } if (ilbc_codec->dec) { AudioConverterDispose(ilbc_codec->dec); ilbc_codec->dec = NULL; } #endif pj_pool_release(ilbc_codec->pool); return PJ_SUCCESS; } /* * Init codec. */ static pj_status_t ilbc_codec_init(pjmedia_codec *codec, pj_pool_t *pool ) { PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(pool); return PJ_SUCCESS; } /* * Open codec. */ static pj_status_t ilbc_codec_open(pjmedia_codec *codec, pjmedia_codec_param *attr ) { struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec; pj_status_t status; unsigned i; pj_uint16_t dec_fmtp_mode = DEFAULT_MODE, enc_fmtp_mode = DEFAULT_MODE; #if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO AudioStreamBasicDescription srcFormat, dstFormat; UInt32 size; srcFormat.mSampleRate = attr->info.clock_rate; srcFormat.mFormatID = kAudioFormatLinearPCM; srcFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; srcFormat.mBitsPerChannel = attr->info.pcm_bits_per_sample; srcFormat.mChannelsPerFrame = attr->info.channel_cnt; srcFormat.mBytesPerFrame = srcFormat.mChannelsPerFrame * srcFormat.mBitsPerChannel >> 3; srcFormat.mFramesPerPacket = 1; srcFormat.mBytesPerPacket = srcFormat.mBytesPerFrame * srcFormat.mFramesPerPacket; memset(&dstFormat, 0, sizeof(dstFormat)); dstFormat.mSampleRate = attr->info.clock_rate; dstFormat.mFormatID = kAudioFormatiLBC; dstFormat.mChannelsPerFrame = attr->info.channel_cnt; #endif pj_assert(ilbc_codec != NULL); pj_assert(ilbc_codec->enc_ready == PJ_FALSE && ilbc_codec->dec_ready == PJ_FALSE); /* Get decoder mode */ for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) { if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name, &STR_MODE) == 0) { dec_fmtp_mode = (pj_uint16_t) pj_strtoul(&attr->setting.dec_fmtp.param[i].val); break; } } /* Decoder mode must be set */ PJ_ASSERT_RETURN(dec_fmtp_mode == 20 || dec_fmtp_mode == 30, PJMEDIA_CODEC_EINMODE); /* Get encoder mode */ for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) { if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_MODE) == 0) { enc_fmtp_mode = (pj_uint16_t) pj_strtoul(&attr->setting.enc_fmtp.param[i].val); break; } } PJ_ASSERT_RETURN(enc_fmtp_mode==20 || enc_fmtp_mode==30, PJMEDIA_CODEC_EINMODE); /* Both sides of a bi-directional session MUST use the same "mode" value. * In this point, possible values are only 20 or 30, so when encoder and * decoder modes are not same, just use the default mode, it is 30. */ if (enc_fmtp_mode != dec_fmtp_mode) { enc_fmtp_mode = dec_fmtp_mode = DEFAULT_MODE; PJ_LOG(4,(ilbc_codec->obj_name, "Normalized iLBC encoder and decoder modes to %d", DEFAULT_MODE)); } /* Update some attributes based on negotiated mode. */ attr->info.avg_bps = (dec_fmtp_mode == 30? 13333 : 15200); attr->info.frm_ptime = dec_fmtp_mode; /* Create encoder */ #if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO dstFormat.mFramesPerPacket = CLOCK_RATE * enc_fmtp_mode / 1000; dstFormat.mBytesPerPacket = (enc_fmtp_mode == 20? 38 : 50); /* Use AudioFormat API to fill out the rest of the description */ size = sizeof(dstFormat); AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &dstFormat); if (AudioConverterNew(&srcFormat, &dstFormat, &ilbc_codec->enc) != noErr) return PJMEDIA_CODEC_EFAILED; ilbc_codec->enc_frame_size = (enc_fmtp_mode == 20? 38 : 50); #else ilbc_codec->enc_frame_size = initEncode(&ilbc_codec->enc, enc_fmtp_mode); #endif ilbc_codec->enc_samples_per_frame = CLOCK_RATE * enc_fmtp_mode / 1000; ilbc_codec->enc_ready = PJ_TRUE; /* Create decoder */ #if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO if (AudioConverterNew(&dstFormat, &srcFormat, &ilbc_codec->dec) != noErr) return PJMEDIA_CODEC_EFAILED; ilbc_codec->dec_samples_per_frame = CLOCK_RATE * dec_fmtp_mode / 1000; #else ilbc_codec->dec_samples_per_frame = initDecode(&ilbc_codec->dec, dec_fmtp_mode, attr->setting.penh); #endif ilbc_codec->dec_frame_size = (dec_fmtp_mode == 20? 38 : 50); ilbc_codec->dec_ready = PJ_TRUE; /* Save plc flags */ ilbc_codec->plc_enabled = (attr->setting.plc != 0); /* Create silence detector. */ ilbc_codec->vad_enabled = (attr->setting.vad != 0); status = pjmedia_silence_det_create(ilbc_codec->pool, CLOCK_RATE, ilbc_codec->enc_samples_per_frame, &ilbc_codec->vad); if (status != PJ_SUCCESS) return status; /* Init last_tx (not necessary because of zalloc, but better * be safe in case someone remove zalloc later. */ pj_set_timestamp32(&ilbc_codec->last_tx, 0, 0); PJ_LOG(4,(ilbc_codec->obj_name, "iLBC codec opened, mode=%d", dec_fmtp_mode)); return PJ_SUCCESS; } /* * Close codec. */ static pj_status_t ilbc_codec_close( pjmedia_codec *codec ) { struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec; PJ_UNUSED_ARG(codec); PJ_LOG(5,(ilbc_codec->obj_name, "iLBC codec closed")); return PJ_SUCCESS; } /* * Modify codec settings. */ static pj_status_t ilbc_codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr ) { struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec; ilbc_codec->plc_enabled = (attr->setting.plc != 0); ilbc_codec->vad_enabled = (attr->setting.vad != 0); return PJ_SUCCESS; } /* * Get frames in the packet. */ static pj_status_t ilbc_codec_parse( pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]) { struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec; unsigned count; PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL); count = 0; while (pkt_size >= ilbc_codec->dec_frame_size && count < *frame_cnt) { frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO; frames[count].buf = pkt; frames[count].size = ilbc_codec->dec_frame_size; frames[count].timestamp.u64 = ts->u64 + count * ilbc_codec->dec_samples_per_frame; pkt = ((char*)pkt) + ilbc_codec->dec_frame_size; pkt_size -= ilbc_codec->dec_frame_size; ++count; } *frame_cnt = count; return PJ_SUCCESS; } #if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO static OSStatus encodeDataProc ( AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData ) { struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)inUserData; /* Initialize in case of failure */ ioData->mBuffers[0].mData = NULL; ioData->mBuffers[0].mDataByteSize = 0; if (ilbc_codec->enc_total_packets < *ioNumberDataPackets) { *ioNumberDataPackets = ilbc_codec->enc_total_packets; } if (*ioNumberDataPackets) { ioData->mBuffers[0].mData = ilbc_codec->enc_buffer + ilbc_codec->enc_buffer_offset; ioData->mBuffers[0].mDataByteSize = *ioNumberDataPackets * ilbc_codec->enc_samples_per_frame << 1; ilbc_codec->enc_buffer_offset += ioData->mBuffers[0].mDataByteSize; } ilbc_codec->enc_total_packets -= *ioNumberDataPackets; return noErr; } static OSStatus decodeDataProc ( AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData ) { struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)inUserData; /* Initialize in case of failure */ ioData->mBuffers[0].mData = NULL; ioData->mBuffers[0].mDataByteSize = 0; if (ilbc_codec->dec_total_packets < *ioNumberDataPackets) { *ioNumberDataPackets = ilbc_codec->dec_total_packets; } if (*ioNumberDataPackets) { ioData->mBuffers[0].mData = ilbc_codec->dec_buffer + ilbc_codec->dec_buffer_offset; ioData->mBuffers[0].mDataByteSize = *ioNumberDataPackets * ilbc_codec->dec_frame_size; ilbc_codec->dec_buffer_offset += ioData->mBuffers[0].mDataByteSize; } ilbc_codec->dec_total_packets -= *ioNumberDataPackets; return noErr; } #endif /* * Encode frame. */ static pj_status_t ilbc_codec_encode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec; pj_int16_t *pcm_in; pj_size_t nsamples; pj_assert(ilbc_codec && input && output); pcm_in = (pj_int16_t*)input->buf; nsamples = input->size >> 1; PJ_ASSERT_RETURN(nsamples % ilbc_codec->enc_samples_per_frame == 0, PJMEDIA_CODEC_EPCMFRMINLEN); PJ_ASSERT_RETURN(output_buf_len >= ilbc_codec->enc_frame_size * nsamples / ilbc_codec->enc_samples_per_frame, PJMEDIA_CODEC_EFRMTOOSHORT); /* Detect silence */ if (ilbc_codec->vad_enabled) { pj_bool_t is_silence; pj_int32_t silence_period; silence_period = pj_timestamp_diff32(&ilbc_codec->last_tx, &input->timestamp); is_silence = pjmedia_silence_det_detect(ilbc_codec->vad, (const pj_int16_t*)input->buf, (input->size >> 1), NULL); if (is_silence && (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 || silence_period < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*8000/1000)) { output->type = PJMEDIA_FRAME_TYPE_NONE; output->buf = NULL; output->size = 0; output->timestamp = input->timestamp; return PJ_SUCCESS; } else { ilbc_codec->last_tx = input->timestamp; } } /* Encode */ output->size = 0; while (nsamples >= ilbc_codec->enc_samples_per_frame) { #if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO OSStatus err; AudioBufferList theABL; UInt32 npackets = 1; theABL.mNumberBuffers = 1; theABL.mBuffers[0].mNumberChannels = 1; theABL.mBuffers[0].mDataByteSize = output_buf_len; theABL.mBuffers[0].mData = output->buf + output->size; ilbc_codec->enc_total_packets = 1; ilbc_codec->enc_buffer = (char *)input->buf; ilbc_codec->enc_buffer_offset = input->size - (nsamples << 1); err = AudioConverterFillComplexBuffer(ilbc_codec->enc, encodeDataProc, ilbc_codec, &npackets, &theABL, NULL); if (err == noErr && npackets) { output->size += npackets * ilbc_codec->enc_frame_size; } #else unsigned i; /* Convert to float */ for (i=0; ienc_samples_per_frame; ++i) { ilbc_codec->enc_block[i] = (float) (*pcm_in++); } iLBC_encode((unsigned char *)output->buf + output->size, ilbc_codec->enc_block, &ilbc_codec->enc); output->size += ilbc_codec->enc.no_of_bytes; #endif nsamples -= ilbc_codec->enc_samples_per_frame; } output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->timestamp = input->timestamp; return PJ_SUCCESS; } /* * Decode frame. */ static pj_status_t ilbc_codec_decode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec; #if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO UInt32 npackets; OSStatus err; AudioBufferList theABL; #else unsigned i; #endif pj_assert(ilbc_codec != NULL); PJ_ASSERT_RETURN(input && output, PJ_EINVAL); if (output_buf_len < (ilbc_codec->dec_samples_per_frame << 1)) return PJMEDIA_CODEC_EPCMTOOSHORT; if (input->size != ilbc_codec->dec_frame_size) return PJMEDIA_CODEC_EFRMINLEN; /* Decode to temporary buffer */ #if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO npackets = input->size / ilbc_codec->dec_frame_size * ilbc_codec->dec_samples_per_frame; theABL.mNumberBuffers = 1; theABL.mBuffers[0].mNumberChannels = 1; theABL.mBuffers[0].mDataByteSize = output_buf_len; theABL.mBuffers[0].mData = output->buf; ilbc_codec->dec_total_packets = npackets; ilbc_codec->dec_buffer = (char *)input->buf; ilbc_codec->dec_buffer_offset = 0; err = AudioConverterFillComplexBuffer(ilbc_codec->dec, decodeDataProc, ilbc_codec, &npackets, &theABL, NULL); if (err == noErr) { output->size = npackets * (ilbc_codec->dec_samples_per_frame << 1); } #else iLBC_decode(ilbc_codec->dec_block, (unsigned char*) input->buf, &ilbc_codec->dec, 1); /* Convert decodec samples from float to short */ for (i=0; idec_samples_per_frame; ++i) { ((short*)output->buf)[i] = (short)ilbc_codec->dec_block[i]; } output->size = (ilbc_codec->dec_samples_per_frame << 1); #endif output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->timestamp = input->timestamp; return PJ_SUCCESS; } /* * Recover lost frame. */ static pj_status_t ilbc_codec_recover(pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output) { struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec; #if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO UInt32 npackets; OSStatus err; AudioBufferList theABL; #else unsigned i; #endif pj_assert(ilbc_codec != NULL); PJ_ASSERT_RETURN(output, PJ_EINVAL); if (output_buf_len < (ilbc_codec->dec_samples_per_frame << 1)) return PJMEDIA_CODEC_EPCMTOOSHORT; /* Decode to temporary buffer */ #if defined(PJMEDIA_ILBC_CODEC_USE_COREAUDIO)&& PJMEDIA_ILBC_CODEC_USE_COREAUDIO npackets = 1; theABL.mNumberBuffers = 1; theABL.mBuffers[0].mNumberChannels = 1; theABL.mBuffers[0].mDataByteSize = output_buf_len; theABL.mBuffers[0].mData = output->buf; ilbc_codec->dec_total_packets = npackets; ilbc_codec->dec_buffer_offset = 0; if (ilbc_codec->dec_buffer) { err = AudioConverterFillComplexBuffer(ilbc_codec->dec, decodeDataProc, ilbc_codec, &npackets, &theABL, NULL); if (err == noErr) { output->size = npackets * (ilbc_codec->dec_samples_per_frame << 1); } } else { output->size = npackets * (ilbc_codec->dec_samples_per_frame << 1); pj_bzero(output->buf, output->size); } #else iLBC_decode(ilbc_codec->dec_block, NULL, &ilbc_codec->dec, 0); /* Convert decodec samples from float to short */ for (i=0; idec_samples_per_frame; ++i) { ((short*)output->buf)[i] = (short)ilbc_codec->dec_block[i]; } output->size = (ilbc_codec->dec_samples_per_frame << 1); #endif output->type = PJMEDIA_FRAME_TYPE_AUDIO; return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_ILBC_CODEC */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/openh264.cpp ================================================ /* $Id$ */ /* * Copyright (C)2014 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #if defined(PJMEDIA_HAS_OPENH264_CODEC) && \ PJMEDIA_HAS_OPENH264_CODEC != 0 && \ defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #ifdef _MSC_VER # include # pragma comment( lib, "openh264.lib") #endif /* OpenH264: */ #include #include /* * Constants */ #define THIS_FILE "openh264.cpp" #if (defined(PJ_DARWINOS) && PJ_DARWINOS != 0 && TARGET_OS_IPHONE) || \ defined(__ANDROID__) # define DEFAULT_WIDTH 352 # define DEFAULT_HEIGHT 288 #else # define DEFAULT_WIDTH 720 # define DEFAULT_HEIGHT 480 #endif #define DEFAULT_FPS 15 #define DEFAULT_AVG_BITRATE 256000 #define DEFAULT_MAX_BITRATE 256000 #define MAX_RX_WIDTH 1200 #define MAX_RX_HEIGHT 800 /* * Factory operations. */ static pj_status_t oh264_test_alloc(pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info ); static pj_status_t oh264_default_attr(pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info, pjmedia_vid_codec_param *attr ); static pj_status_t oh264_enum_info(pjmedia_vid_codec_factory *factory, unsigned *count, pjmedia_vid_codec_info codecs[]); static pj_status_t oh264_alloc_codec(pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info, pjmedia_vid_codec **p_codec); static pj_status_t oh264_dealloc_codec(pjmedia_vid_codec_factory *factory, pjmedia_vid_codec *codec ); /* * Codec operations */ static pj_status_t oh264_codec_init(pjmedia_vid_codec *codec, pj_pool_t *pool ); static pj_status_t oh264_codec_open(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *param ); static pj_status_t oh264_codec_close(pjmedia_vid_codec *codec); static pj_status_t oh264_codec_modify(pjmedia_vid_codec *codec, const pjmedia_vid_codec_param *param); static pj_status_t oh264_codec_get_param(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *param); static pj_status_t oh264_codec_encode_begin(pjmedia_vid_codec *codec, const pjmedia_vid_encode_opt *opt, const pjmedia_frame *input, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more); static pj_status_t oh264_codec_encode_more(pjmedia_vid_codec *codec, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more); static pj_status_t oh264_codec_decode(pjmedia_vid_codec *codec, pj_size_t count, pjmedia_frame packets[], unsigned out_size, pjmedia_frame *output); /* Definition for OpenH264 codecs operations. */ static pjmedia_vid_codec_op oh264_codec_op = { &oh264_codec_init, &oh264_codec_open, &oh264_codec_close, &oh264_codec_modify, &oh264_codec_get_param, &oh264_codec_encode_begin, &oh264_codec_encode_more, &oh264_codec_decode, NULL }; /* Definition for OpenH264 codecs factory operations. */ static pjmedia_vid_codec_factory_op oh264_factory_op = { &oh264_test_alloc, &oh264_default_attr, &oh264_enum_info, &oh264_alloc_codec, &oh264_dealloc_codec }; static struct oh264_factory { pjmedia_vid_codec_factory base; pjmedia_vid_codec_mgr *mgr; pj_pool_factory *pf; pj_pool_t *pool; } oh264_factory; typedef struct oh264_codec_data { pj_pool_t *pool; pjmedia_vid_codec_param *prm; pj_bool_t whole; pjmedia_h264_packetizer *pktz; /* Encoder state */ ISVCEncoder *enc; SSourcePicture *esrc_pic; unsigned enc_input_size; pj_uint8_t *enc_frame_whole; unsigned enc_frame_size; unsigned enc_processed; pj_timestamp ets; SFrameBSInfo bsi; int ilayer; /* Decoder state */ ISVCDecoder *dec; pj_uint8_t *dec_buf; unsigned dec_buf_size; } oh264_codec_data; struct SLayerPEncCtx { pj_int32_t iDLayerQp; SSliceArgument sSliceArgument; }; PJ_DEF(pj_status_t) pjmedia_codec_openh264_vid_init(pjmedia_vid_codec_mgr *mgr, pj_pool_factory *pf) { const pj_str_t h264_name = { (char*)"H264", 4}; pj_status_t status; if (oh264_factory.pool != NULL) { /* Already initialized. */ return PJ_SUCCESS; } if (!mgr) mgr = pjmedia_vid_codec_mgr_instance(); PJ_ASSERT_RETURN(mgr, PJ_EINVAL); /* Create OpenH264 codec factory. */ oh264_factory.base.op = &oh264_factory_op; oh264_factory.base.factory_data = NULL; oh264_factory.mgr = mgr; oh264_factory.pf = pf; oh264_factory.pool = pj_pool_create(pf, "oh264factory", 256, 256, NULL); if (!oh264_factory.pool) return PJ_ENOMEM; /* Registering format match for SDP negotiation */ status = pjmedia_sdp_neg_register_fmt_match_cb( &h264_name, &pjmedia_vid_codec_h264_match_sdp); if (status != PJ_SUCCESS) goto on_error; /* Register codec factory to codec manager. */ status = pjmedia_vid_codec_mgr_register_factory(mgr, &oh264_factory.base); if (status != PJ_SUCCESS) goto on_error; PJ_LOG(4,(THIS_FILE, "OpenH264 codec initialized")); /* Done. */ return PJ_SUCCESS; on_error: pj_pool_release(oh264_factory.pool); oh264_factory.pool = NULL; return status; } /* * Unregister OpenH264 codecs factory from pjmedia endpoint. */ PJ_DEF(pj_status_t) pjmedia_codec_openh264_vid_deinit(void) { pj_status_t status = PJ_SUCCESS; if (oh264_factory.pool == NULL) { /* Already deinitialized */ return PJ_SUCCESS; } /* Unregister OpenH264 codecs factory. */ status = pjmedia_vid_codec_mgr_unregister_factory(oh264_factory.mgr, &oh264_factory.base); /* Destroy pool. */ pj_pool_release(oh264_factory.pool); oh264_factory.pool = NULL; return status; } static pj_status_t oh264_test_alloc(pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info ) { PJ_ASSERT_RETURN(factory == &oh264_factory.base, PJ_EINVAL); if (info->fmt_id == PJMEDIA_FORMAT_H264 && info->pt != 0) { return PJ_SUCCESS; } return PJMEDIA_CODEC_EUNSUP; } static pj_status_t oh264_default_attr(pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info, pjmedia_vid_codec_param *attr ) { PJ_ASSERT_RETURN(factory == &oh264_factory.base, PJ_EINVAL); PJ_ASSERT_RETURN(info && attr, PJ_EINVAL); pj_bzero(attr, sizeof(pjmedia_vid_codec_param)); attr->dir = PJMEDIA_DIR_ENCODING_DECODING; attr->packing = PJMEDIA_VID_PACKING_PACKETS; /* Encoded format */ pjmedia_format_init_video(&attr->enc_fmt, PJMEDIA_FORMAT_H264, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FPS, 1); /* Decoded format */ pjmedia_format_init_video(&attr->dec_fmt, PJMEDIA_FORMAT_I420, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FPS, 1); /* Decoding fmtp */ attr->dec_fmtp.cnt = 2; attr->dec_fmtp.param[0].name = pj_str((char*)"profile-level-id"); attr->dec_fmtp.param[0].val = pj_str((char*)"42e01e"); attr->dec_fmtp.param[1].name = pj_str((char*)" packetization-mode"); attr->dec_fmtp.param[1].val = pj_str((char*)"1"); /* Bitrate */ attr->enc_fmt.det.vid.avg_bps = DEFAULT_AVG_BITRATE; attr->enc_fmt.det.vid.max_bps = DEFAULT_MAX_BITRATE; /* Encoding MTU */ attr->enc_mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE; return PJ_SUCCESS; } static pj_status_t oh264_enum_info(pjmedia_vid_codec_factory *factory, unsigned *count, pjmedia_vid_codec_info info[]) { PJ_ASSERT_RETURN(info && *count > 0, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &oh264_factory.base, PJ_EINVAL); *count = 1; info->fmt_id = PJMEDIA_FORMAT_H264; info->pt = PJMEDIA_RTP_PT_H264; info->encoding_name = pj_str((char*)"H264"); info->encoding_desc = pj_str((char*)"OpenH264 codec"); info->clock_rate = 90000; info->dir = PJMEDIA_DIR_ENCODING_DECODING; info->dec_fmt_id_cnt = 1; info->dec_fmt_id[0] = PJMEDIA_FORMAT_I420; info->packings = PJMEDIA_VID_PACKING_PACKETS | PJMEDIA_VID_PACKING_WHOLE; info->fps_cnt = 3; info->fps[0].num = 15; info->fps[0].denum = 1; info->fps[1].num = 25; info->fps[1].denum = 1; info->fps[2].num = 30; info->fps[2].denum = 1; return PJ_SUCCESS; } static pj_status_t oh264_alloc_codec(pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info, pjmedia_vid_codec **p_codec) { pj_pool_t *pool; pjmedia_vid_codec *codec; oh264_codec_data *oh264_data; int rc; PJ_ASSERT_RETURN(factory == &oh264_factory.base && info && p_codec, PJ_EINVAL); *p_codec = NULL; pool = pj_pool_create(oh264_factory.pf, "oh264%p", 512, 512, NULL); if (!pool) return PJ_ENOMEM; /* codec instance */ codec = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec); codec->factory = factory; codec->op = &oh264_codec_op; /* codec data */ oh264_data = PJ_POOL_ZALLOC_T(pool, oh264_codec_data); oh264_data->pool = pool; codec->codec_data = oh264_data; /* encoder allocation */ rc = WelsCreateSVCEncoder(&oh264_data->enc); if (rc != 0) goto on_error; oh264_data->esrc_pic = PJ_POOL_ZALLOC_T(pool, SSourcePicture); /* decoder allocation */ rc = WelsCreateDecoder(&oh264_data->dec); if (rc != 0) goto on_error; *p_codec = codec; return PJ_SUCCESS; on_error: oh264_dealloc_codec(factory, codec); return PJMEDIA_CODEC_EFAILED; } static pj_status_t oh264_dealloc_codec(pjmedia_vid_codec_factory *factory, pjmedia_vid_codec *codec ) { oh264_codec_data *oh264_data; PJ_ASSERT_RETURN(codec, PJ_EINVAL); PJ_UNUSED_ARG(factory); oh264_data = (oh264_codec_data*) codec->codec_data; if (oh264_data->enc) { WelsDestroySVCEncoder(oh264_data->enc); oh264_data->enc = NULL; } if (oh264_data->dec) { oh264_data->dec->Uninitialize(); WelsDestroyDecoder(oh264_data->dec); oh264_data->dec = NULL; } pj_pool_release(oh264_data->pool); return PJ_SUCCESS; } static pj_status_t oh264_codec_init(pjmedia_vid_codec *codec, pj_pool_t *pool ) { PJ_ASSERT_RETURN(codec && pool, PJ_EINVAL); PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(pool); return PJ_SUCCESS; } static pj_status_t oh264_codec_open(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *codec_param ) { oh264_codec_data *oh264_data; pjmedia_vid_codec_param *param; pjmedia_h264_packetizer_cfg pktz_cfg; pjmedia_vid_codec_h264_fmtp h264_fmtp; SEncParamExt eprm; SSpatialLayerConfig *elayer = &eprm.sSpatialLayers[0]; SLayerPEncCtx elayer_ctx; SDecodingParam sDecParam = {0}; int rc; pj_status_t status; PJ_ASSERT_RETURN(codec && codec_param, PJ_EINVAL); PJ_LOG(5,(THIS_FILE, "Opening codec..")); oh264_data = (oh264_codec_data*) codec->codec_data; oh264_data->prm = pjmedia_vid_codec_param_clone( oh264_data->pool, codec_param); param = oh264_data->prm; /* Parse remote fmtp */ pj_bzero(&h264_fmtp, sizeof(h264_fmtp)); status = pjmedia_vid_codec_h264_parse_fmtp(¶m->enc_fmtp, &h264_fmtp); if (status != PJ_SUCCESS) return status; /* Apply SDP fmtp to format in codec param */ if (!param->ignore_fmtp) { status = pjmedia_vid_codec_h264_apply_fmtp(param); if (status != PJ_SUCCESS) return status; } pj_bzero(&pktz_cfg, sizeof(pktz_cfg)); pktz_cfg.mtu = param->enc_mtu; /* Packetization mode */ #if 0 if (h264_fmtp.packetization_mode == 0) pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL; else if (h264_fmtp.packetization_mode == 1) pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED; else return PJ_ENOTSUP; #else if (h264_fmtp.packetization_mode!= PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL && h264_fmtp.packetization_mode!= PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED) { return PJ_ENOTSUP; } /* Better always send in single NAL mode for better compatibility */ pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL; #endif status = pjmedia_h264_packetizer_create(oh264_data->pool, &pktz_cfg, &oh264_data->pktz); if (status != PJ_SUCCESS) return status; oh264_data->whole = (param->packing == PJMEDIA_VID_PACKING_WHOLE); /* * Encoder */ /* Init encoder parameters */ oh264_data->enc->GetDefaultParams (&eprm); eprm.iComplexityMode = MEDIUM_COMPLEXITY; eprm.sSpatialLayers[0].uiProfileIdc = PRO_BASELINE; eprm.iPicWidth = param->enc_fmt.det.vid.size.w; eprm.iUsageType = CAMERA_VIDEO_REAL_TIME; eprm.iPicHeight = param->enc_fmt.det.vid.size.h; eprm.fMaxFrameRate = (param->enc_fmt.det.vid.fps.num * 1.0f / param->enc_fmt.det.vid.fps.denum); eprm.iTemporalLayerNum = 1; eprm.uiIntraPeriod = 0; /* I-Frame interval in frames */ eprm.eSpsPpsIdStrategy = (oh264_data->whole ? CONSTANT_ID : INCREASING_ID); eprm.bEnableFrameCroppingFlag = true; eprm.iLoopFilterDisableIdc = 0; eprm.iLoopFilterAlphaC0Offset = 0; eprm.iLoopFilterBetaOffset = 0; eprm.iMultipleThreadIdc = 1; //eprm.bEnableRc = 1; eprm.iTargetBitrate = param->enc_fmt.det.vid.avg_bps; eprm.bEnableFrameSkip = 1; eprm.bEnableDenoise = 0; eprm.bEnableSceneChangeDetect = 1; eprm.bEnableBackgroundDetection = 1; eprm.bEnableAdaptiveQuant = 1; eprm.bEnableLongTermReference = 0; eprm.iLtrMarkPeriod = 30; eprm.bPrefixNalAddingCtrl = false; eprm.iSpatialLayerNum = 1; if (!oh264_data->whole) { eprm.uiMaxNalSize = param->enc_mtu; } pj_bzero(&elayer_ctx, sizeof (SLayerPEncCtx)); elayer_ctx.iDLayerQp = 24; elayer_ctx.sSliceArgument.uiSliceMode = (oh264_data->whole ? SM_SINGLE_SLICE : SM_SIZELIMITED_SLICE); elayer_ctx.sSliceArgument.uiSliceSizeConstraint = param->enc_mtu; elayer_ctx.sSliceArgument.uiSliceNum = 1; elayer_ctx.sSliceArgument.uiSliceMbNum[0] = 960; elayer_ctx.sSliceArgument.uiSliceMbNum[1] = 0; elayer_ctx.sSliceArgument.uiSliceMbNum[2] = 0; elayer_ctx.sSliceArgument.uiSliceMbNum[3] = 0; elayer_ctx.sSliceArgument.uiSliceMbNum[4] = 0; elayer_ctx.sSliceArgument.uiSliceMbNum[5] = 0; elayer_ctx.sSliceArgument.uiSliceMbNum[6] = 0; elayer_ctx.sSliceArgument.uiSliceMbNum[7] = 0; elayer->iVideoWidth = eprm.iPicWidth; elayer->iVideoHeight = eprm.iPicHeight; elayer->fFrameRate = eprm.fMaxFrameRate; elayer->uiProfileIdc = eprm.sSpatialLayers[0].uiProfileIdc; elayer->iSpatialBitrate = eprm.iTargetBitrate; elayer->iDLayerQp = elayer_ctx.iDLayerQp; elayer->sSliceArgument.uiSliceMode = elayer_ctx.sSliceArgument.uiSliceMode; memcpy ( &elayer->sSliceArgument, &elayer_ctx.sSliceArgument, sizeof (SSliceArgument)); memcpy ( &elayer->sSliceArgument.uiSliceMbNum[0], &elayer_ctx.sSliceArgument.uiSliceMbNum[0], sizeof (elayer_ctx.sSliceArgument.uiSliceMbNum)); /* Init input picture */ oh264_data->esrc_pic->iColorFormat = videoFormatI420; oh264_data->esrc_pic->uiTimeStamp = 0; oh264_data->esrc_pic->iPicWidth = eprm.iPicWidth; oh264_data->esrc_pic->iPicHeight = eprm.iPicHeight; oh264_data->esrc_pic->iStride[0] = oh264_data->esrc_pic->iPicWidth; oh264_data->esrc_pic->iStride[1] = oh264_data->esrc_pic->iStride[2] = oh264_data->esrc_pic->iStride[0]>>1; oh264_data->enc_input_size = oh264_data->esrc_pic->iPicWidth * oh264_data->esrc_pic->iPicHeight * 3 >> 1; /* Initialize encoder */ rc = oh264_data->enc->InitializeExt (&eprm); if (rc != cmResultSuccess) { PJ_LOG(4,(THIS_FILE, "SVC encoder Initialize failed, rc=%d", rc)); return PJMEDIA_CODEC_EFAILED; } int videoFormat = videoFormatI420; rc = oh264_data->enc->SetOption (ENCODER_OPTION_DATAFORMAT, &videoFormat); if (rc != cmResultSuccess) { PJ_LOG(4,(THIS_FILE, "SVC encoder SetOption videoFormatI420 failed, " "rc=%d", rc)); return PJMEDIA_CODEC_EFAILED; } /* * Decoder */ sDecParam.sVideoProperty.size = sizeof (sDecParam.sVideoProperty); sDecParam.uiTargetDqLayer = (pj_uint8_t) - 1; sDecParam.eEcActiveIdc = ERROR_CON_SLICE_COPY; sDecParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT; //TODO: // Apply "sprop-parameter-sets" here rc = WelsCreateDecoder(&oh264_data->dec); if (rc) { PJ_LOG(4,(THIS_FILE, "Unable to create OpenH264 decoder")); return PJMEDIA_CODEC_EFAILED; } rc = oh264_data->dec->Initialize (&sDecParam); if (rc) { PJ_LOG(4,(THIS_FILE, "Decoder initialization failed, rc=%d", rc)); return PJMEDIA_CODEC_EFAILED; } oh264_data->dec_buf_size = (MAX_RX_WIDTH * MAX_RX_HEIGHT * 3 >> 1) + (MAX_RX_WIDTH); oh264_data->dec_buf = (pj_uint8_t*)pj_pool_alloc(oh264_data->pool, oh264_data->dec_buf_size); /* Need to update param back after values are negotiated */ pj_memcpy(codec_param, param, sizeof(*codec_param)); return PJ_SUCCESS; } static pj_status_t oh264_codec_close(pjmedia_vid_codec *codec) { PJ_ASSERT_RETURN(codec, PJ_EINVAL); PJ_UNUSED_ARG(codec); return PJ_SUCCESS; } static pj_status_t oh264_codec_modify(pjmedia_vid_codec *codec, const pjmedia_vid_codec_param *param) { PJ_ASSERT_RETURN(codec && param, PJ_EINVAL); PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(param); return PJ_EINVALIDOP; } static pj_status_t oh264_codec_get_param(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *param) { struct oh264_codec_data *oh264_data; PJ_ASSERT_RETURN(codec && param, PJ_EINVAL); oh264_data = (oh264_codec_data*) codec->codec_data; pj_memcpy(param, oh264_data->prm, sizeof(*param)); return PJ_SUCCESS; } static pj_status_t oh264_codec_encode_begin(pjmedia_vid_codec *codec, const pjmedia_vid_encode_opt *opt, const pjmedia_frame *input, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more) { struct oh264_codec_data *oh264_data; int rc; PJ_ASSERT_RETURN(codec && input && out_size && output && has_more, PJ_EINVAL); oh264_data = (oh264_codec_data*) codec->codec_data; PJ_ASSERT_RETURN(input->size == oh264_data->enc_input_size, PJMEDIA_CODEC_EFRMINLEN); if (opt && opt->force_keyframe) { oh264_data->enc->ForceIntraFrame(true); } oh264_data->esrc_pic->pData[0] = (pj_uint8_t*)input->buf; oh264_data->esrc_pic->pData[1] = oh264_data->esrc_pic->pData[0] + (oh264_data->esrc_pic->iPicWidth * oh264_data->esrc_pic->iPicHeight); oh264_data->esrc_pic->pData[2] = oh264_data->esrc_pic->pData[1] + (oh264_data->esrc_pic->iPicWidth * oh264_data->esrc_pic->iPicHeight >>2); pj_memset (&oh264_data->bsi, 0, sizeof (SFrameBSInfo)); rc = oh264_data->enc->EncodeFrame( oh264_data->esrc_pic, &oh264_data->bsi); if (rc != cmResultSuccess) { PJ_LOG(5,(THIS_FILE, "EncodeFrame() error, ret: %d", rc)); return PJMEDIA_CODEC_EFAILED; } if (oh264_data->bsi.eFrameType == videoFrameTypeSkip) { output->size = 0; output->type = PJMEDIA_FRAME_TYPE_NONE; output->timestamp = input->timestamp; return PJ_SUCCESS; } oh264_data->ets = input->timestamp; oh264_data->ilayer = 0; oh264_data->enc_frame_size = oh264_data->enc_processed = 0; if (oh264_data->whole) { SLayerBSInfo* pLayerBsInfo; pj_uint8_t *payload; unsigned i, payload_size = 0; *has_more = PJ_FALSE; /* Find which layer with biggest payload */ oh264_data->ilayer = 0; payload_size = oh264_data->bsi.sLayerInfo[0].pNalLengthInByte[0]; for (i=0; i < (unsigned)oh264_data->bsi.iLayerNum; ++i) { unsigned j; pLayerBsInfo = &oh264_data->bsi.sLayerInfo[i]; for (j=0; j < (unsigned)pLayerBsInfo->iNalCount; ++j) { if (pLayerBsInfo->pNalLengthInByte[j] > (int)payload_size) { payload_size = pLayerBsInfo->pNalLengthInByte[j]; oh264_data->ilayer = i; } } } pLayerBsInfo = &oh264_data->bsi.sLayerInfo[oh264_data->ilayer]; if (pLayerBsInfo == NULL) { output->size = 0; output->type = PJMEDIA_FRAME_TYPE_NONE; return PJ_SUCCESS; } payload = pLayerBsInfo->pBsBuf; payload_size = 0; for (int inal = pLayerBsInfo->iNalCount - 1; inal >= 0; --inal) { payload_size += pLayerBsInfo->pNalLengthInByte[inal]; } if (payload_size > out_size) return PJMEDIA_CODEC_EFRMTOOSHORT; output->type = PJMEDIA_FRAME_TYPE_VIDEO; output->size = payload_size; output->timestamp = input->timestamp; pj_memcpy(output->buf, payload, payload_size); return PJ_SUCCESS; } return oh264_codec_encode_more(codec, out_size, output, has_more); } static pj_status_t oh264_codec_encode_more(pjmedia_vid_codec *codec, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more) { struct oh264_codec_data *oh264_data; const pj_uint8_t *payload; pj_size_t payload_len; pj_status_t status; PJ_ASSERT_RETURN(codec && out_size && output && has_more, PJ_EINVAL); oh264_data = (oh264_codec_data*) codec->codec_data; if (oh264_data->enc_processed < oh264_data->enc_frame_size) { /* We have outstanding frame in packetizer */ status = pjmedia_h264_packetize(oh264_data->pktz, oh264_data->enc_frame_whole, oh264_data->enc_frame_size, &oh264_data->enc_processed, &payload, &payload_len); if (status != PJ_SUCCESS) { /* Reset */ oh264_data->enc_frame_size = oh264_data->enc_processed = 0; *has_more = (oh264_data->enc_processed < oh264_data->enc_frame_size) || (oh264_data->ilayer < oh264_data->bsi.iLayerNum); PJ_PERROR(3,(THIS_FILE, status, "pjmedia_h264_packetize() error")); return status; } PJ_ASSERT_RETURN(payload_len <= out_size, PJMEDIA_CODEC_EFRMTOOSHORT); output->type = PJMEDIA_FRAME_TYPE_VIDEO; pj_memcpy(output->buf, payload, payload_len); output->size = payload_len; if (oh264_data->bsi.eFrameType == videoFrameTypeIDR) { output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; } *has_more = (oh264_data->enc_processed < oh264_data->enc_frame_size) || (oh264_data->ilayer < oh264_data->bsi.iLayerNum); return PJ_SUCCESS; } if (oh264_data->ilayer >= oh264_data->bsi.iLayerNum) { /* No more unretrieved frame */ goto no_frame; } SLayerBSInfo* pLayerBsInfo; pLayerBsInfo = &oh264_data->bsi.sLayerInfo[oh264_data->ilayer++]; if (pLayerBsInfo == NULL) { goto no_frame; } oh264_data->enc_frame_size = 0; for (int inal = pLayerBsInfo->iNalCount - 1; inal >= 0; --inal) { oh264_data->enc_frame_size += pLayerBsInfo->pNalLengthInByte[inal]; } oh264_data->enc_frame_whole = pLayerBsInfo->pBsBuf; oh264_data->enc_processed = 0; status = pjmedia_h264_packetize(oh264_data->pktz, oh264_data->enc_frame_whole, oh264_data->enc_frame_size, &oh264_data->enc_processed, &payload, &payload_len); if (status != PJ_SUCCESS) { /* Reset */ oh264_data->enc_frame_size = oh264_data->enc_processed = 0; *has_more = (oh264_data->ilayer < oh264_data->bsi.iLayerNum); PJ_PERROR(3,(THIS_FILE, status, "pjmedia_h264_packetize() error [2]")); return status; } PJ_ASSERT_RETURN(payload_len <= out_size, PJMEDIA_CODEC_EFRMTOOSHORT); output->type = PJMEDIA_FRAME_TYPE_VIDEO; pj_memcpy(output->buf, payload, payload_len); output->size = payload_len; if (oh264_data->bsi.eFrameType == videoFrameTypeIDR) { output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; } *has_more = (oh264_data->enc_processed < oh264_data->enc_frame_size) || (oh264_data->ilayer < oh264_data->bsi.iLayerNum); return PJ_SUCCESS; no_frame: *has_more = PJ_FALSE; output->size = 0; output->type = PJMEDIA_FRAME_TYPE_NONE; return PJ_SUCCESS; } static int write_yuv(pj_uint8_t *buf, unsigned dst_len, unsigned char* pData[3], int iStride[2], int iWidth, int iHeight) { unsigned req_size; pj_uint8_t *dst = buf; pj_uint8_t *max = dst + dst_len; int i; unsigned char* pPtr = NULL; req_size = (iWidth * iHeight) + (iWidth / 2 * iHeight / 2) + (iWidth / 2 * iHeight / 2); if (dst_len < req_size) return -1; pPtr = pData[0]; for (i = 0; i < iHeight && (dst + iWidth < max); i++) { pj_memcpy(dst, pPtr, iWidth); pPtr += iStride[0]; dst += iWidth; } if (i < iHeight) return -1; iHeight = iHeight / 2; iWidth = iWidth / 2; pPtr = pData[1]; for (i = 0; i < iHeight && (dst + iWidth <= max); i++) { pj_memcpy(dst, pPtr, iWidth); pPtr += iStride[1]; dst += iWidth; } if (i < iHeight) return -1; pPtr = pData[2]; for (i = 0; i < iHeight && (dst + iWidth <= max); i++) { pj_memcpy(dst, pPtr, iWidth); pPtr += iStride[1]; dst += iWidth; } if (i < iHeight) return -1; return dst - buf; } static pj_status_t oh264_got_decoded_frame(pjmedia_vid_codec *codec, struct oh264_codec_data *oh264_data, unsigned char *pData[3], SBufferInfo *sDstBufInfo, pj_timestamp *timestamp, unsigned out_size, pjmedia_frame *output) { pj_uint8_t* pDst[3] = {NULL}; pDst[0] = (pj_uint8_t*)pData[0]; pDst[1] = (pj_uint8_t*)pData[1]; pDst[2] = (pj_uint8_t*)pData[2]; /* Do not reset size as it may already contain frame output->size = 0; */ if (!pDst[0] || !pDst[1] || !pDst[2]) { return PJ_SUCCESS; } int iStride[2]; int iWidth = sDstBufInfo->UsrData.sSystemBuffer.iWidth; int iHeight = sDstBufInfo->UsrData.sSystemBuffer.iHeight; iStride[0] = sDstBufInfo->UsrData.sSystemBuffer.iStride[0]; iStride[1] = sDstBufInfo->UsrData.sSystemBuffer.iStride[1]; int len = write_yuv((pj_uint8_t *)output->buf, out_size, pDst, iStride, iWidth, iHeight); if (len > 0) { output->timestamp = *timestamp; output->size = len; output->type = PJMEDIA_FRAME_TYPE_VIDEO; } else { /* buffer is damaged, reset size */ output->size = 0; return PJMEDIA_CODEC_EFRMTOOSHORT; } /* Detect format change */ if (iWidth != (int)oh264_data->prm->dec_fmt.det.vid.size.w || iHeight != (int)oh264_data->prm->dec_fmt.det.vid.size.h) { pjmedia_event event; PJ_LOG(4,(THIS_FILE, "Frame size changed: %dx%d --> %dx%d", oh264_data->prm->dec_fmt.det.vid.size.w, oh264_data->prm->dec_fmt.det.vid.size.h, iWidth, iHeight)); oh264_data->prm->dec_fmt.det.vid.size.w = iWidth; oh264_data->prm->dec_fmt.det.vid.size.h = iHeight; /* Broadcast format changed event */ pjmedia_event_init(&event, PJMEDIA_EVENT_FMT_CHANGED, timestamp, codec); event.data.fmt_changed.dir = PJMEDIA_DIR_DECODING; pjmedia_format_copy(&event.data.fmt_changed.new_fmt, &oh264_data->prm->dec_fmt); pjmedia_event_publish(NULL, codec, &event, PJMEDIA_EVENT_PUBLISH_DEFAULT); } return PJ_SUCCESS; } static pj_status_t oh264_codec_decode(pjmedia_vid_codec *codec, pj_size_t count, pjmedia_frame packets[], unsigned out_size, pjmedia_frame *output) { struct oh264_codec_data *oh264_data; unsigned char* pData[3] = {NULL}; const pj_uint8_t nal_start[] = { 0, 0, 1 }; SBufferInfo sDstBufInfo; pj_bool_t has_frame = PJ_FALSE; unsigned buf_pos, whole_len = 0; unsigned i, frm_cnt; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(codec && count && packets && out_size && output, PJ_EINVAL); PJ_ASSERT_RETURN(output->buf, PJ_EINVAL); oh264_data = (oh264_codec_data*) codec->codec_data; /* * Step 1: unpacketize the packets/frames */ whole_len = 0; if (oh264_data->whole) { for (i=0; i oh264_data->dec_buf_size) { PJ_LOG(4,(THIS_FILE, "Decoding buffer overflow [1]")); return PJMEDIA_CODEC_EFRMTOOSHORT; } pj_memcpy( oh264_data->dec_buf + whole_len, (pj_uint8_t*)packets[i].buf, packets[i].size); whole_len += packets[i].size; } } else { for (i=0; i oh264_data->dec_buf_size) { PJ_LOG(4,(THIS_FILE, "Decoding buffer overflow [1]")); return PJMEDIA_CODEC_EFRMTOOSHORT; } status = pjmedia_h264_unpacketize( oh264_data->pktz, (pj_uint8_t*)packets[i].buf, packets[i].size, oh264_data->dec_buf, oh264_data->dec_buf_size, &whole_len); if (status != PJ_SUCCESS) { PJ_PERROR(4,(THIS_FILE, status, "Unpacketize error")); continue; } } } if (whole_len + sizeof(nal_start) > oh264_data->dec_buf_size) { PJ_LOG(4,(THIS_FILE, "Decoding buffer overflow [2]")); return PJMEDIA_CODEC_EFRMTOOSHORT; } /* Dummy NAL sentinel */ pj_memcpy( oh264_data->dec_buf + whole_len, nal_start, sizeof(nal_start)); /* * Step 2: parse the individual NAL and give to decoder */ buf_pos = 0; for ( frm_cnt=0; ; ++frm_cnt) { unsigned frm_size; unsigned char *start; for (i = 0; buf_pos + i < whole_len; i++) { if (oh264_data->dec_buf[buf_pos + i] == 0 && oh264_data->dec_buf[buf_pos + i + 1] == 0 && oh264_data->dec_buf[buf_pos + i + 2] == 1 && i > 1) { break; } } frm_size = i; pj_bzero( pData, sizeof(pData)); pj_bzero( &sDstBufInfo, sizeof (SBufferInfo)); start = oh264_data->dec_buf + buf_pos; /* Decode */ oh264_data->dec->DecodeFrame2( start, frm_size, pData, &sDstBufInfo); if (sDstBufInfo.iBufferStatus == 1) { /* May overwrite existing frame but that's ok. */ status = oh264_got_decoded_frame(codec, oh264_data, pData, &sDstBufInfo, &packets[0].timestamp, out_size, output); has_frame = (status==PJ_SUCCESS && output->size != 0); } if (buf_pos + frm_size >= whole_len) break; buf_pos += frm_size; } /* Signal that we have no more frames */ pj_int32_t iEndOfStreamFlag = true; oh264_data->dec->SetOption( DECODER_OPTION_END_OF_STREAM, (void*)&iEndOfStreamFlag); /* Retrieve the decoded frame */ pj_bzero(pData, sizeof(pData)); pj_bzero(&sDstBufInfo, sizeof (SBufferInfo)); oh264_data->dec->DecodeFrame2 (NULL, 0, pData, &sDstBufInfo); if (sDstBufInfo.iBufferStatus == 1) { /* Overwrite existing output frame and that's ok, because we assume * newer frame have better quality because it has more NALs */ status = oh264_got_decoded_frame(codec, oh264_data, pData, &sDstBufInfo, &packets[0].timestamp, out_size, output); has_frame = (status==PJ_SUCCESS && output->size != 0); } if (!has_frame) { pjmedia_event event; /* Broadcast missing keyframe event */ pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING, &packets[0].timestamp, codec); pjmedia_event_publish(NULL, codec, &event, PJMEDIA_EVENT_PUBLISH_DEFAULT); PJ_LOG(5,(THIS_FILE, "Decode couldn't produce picture, " "input nframes=%d, concatenated size=%d bytes", count, whole_len)); output->type = PJMEDIA_FRAME_TYPE_NONE; output->size = 0; output->timestamp = packets[0].timestamp; } return status; } #endif /* PJMEDIA_HAS_OPENH264_CODEC */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/opus.c ================================================ /** * Copyright (C) 2010 Regis Montoya (aka r3gis - www.r3gis.fr) * This file is part of pjsip_android. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #if defined(PJMEDIA_HAS_OPUS_CODEC) && (PJMEDIA_HAS_OPUS_CODEC!=0) #include "../../third_party/opus/include/opus.h" /* Opus can encode frames of 2.5, 5, 10, 20, 40, or 60 ms. */ #define FRAME_LENGTH_MS 20 /* It can also combine multiple frames into packets of up to 120 ms */ /* So at maximum 2.5ms * 48frames = 120ms*/ #define OPUS_MAX_FRAMES_PER_PACKET 48 #define OPUS_CLOCK_RATE 48000 #define _TRACE_OPUS 0 #define THIS_FILE "opus.c" /* Prototypes for OPUS factory */ static pj_status_t opus_test_alloc(pjmedia_codec_factory *factory, const pjmedia_codec_info *id); static pj_status_t opus_default_attr(pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr); static pj_status_t opus_enum_codecs(pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]); static pj_status_t opus_alloc_codec(pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec); static pj_status_t opus_dealloc_codec(pjmedia_codec_factory *factory, pjmedia_codec *codec); /* Prototypes for OPUS implementation. */ static pj_status_t opus_codec_init(pjmedia_codec *codec, pj_pool_t *pool); static pj_status_t opus_codec_open(pjmedia_codec *codec, pjmedia_codec_param *attr); static pj_status_t opus_codec_close(pjmedia_codec *codec); static pj_status_t opus_codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr); static pj_status_t opus_codec_parse(pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *timestamp, unsigned *frame_cnt, pjmedia_frame frames[]); static pj_status_t opus_codec_encode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); static pj_status_t opus_codec_decode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); static pj_status_t opus_codec_recover(pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output); /* Definition for OPUS codec operations. */ static pjmedia_codec_op opus_op = { &opus_codec_init, &opus_codec_open, &opus_codec_close, &opus_codec_modify, &opus_codec_parse, &opus_codec_encode, &opus_codec_decode, &opus_codec_recover }; /* Definition for OPUS codec factory operations. */ static pjmedia_codec_factory_op opus_factory_op = { &opus_test_alloc, &opus_default_attr, &opus_enum_codecs, &opus_alloc_codec, &opus_dealloc_codec, &pjmedia_codec_opus_deinit }; /* OPUS factory private data */ static struct opus_factory { pjmedia_codec_factory base; pjmedia_endpt *endpt; pj_pool_t *pool; pj_mutex_t *mutex; pjmedia_codec codec_list; } opus_factory; /* OPUS codec private data. */ struct opus_private { pj_pool_t *pool; /* Pool for each instance. */ pj_uint8_t pcm_bytes_per_sample; int externalFs; /* Clock rate we would like to limit from outside */ pj_bool_t enc_ready; OpusEncoder* psEnc; pj_bool_t dec_ready; OpusDecoder* psDec; /* Buffer of 120ms to hold decoded frames. */ void *dec_buf; pj_size_t dec_buf_size; pj_size_t dec_buf_max_size; int dec_buf_sample_per_frame; pj_uint32_t pkt_info; /* Packet info for buffered frames. */ }; int opus_to_pjsip_error_code(int opus_error) { switch (opus_error) { case OPUS_BAD_ARG: /* One or more invalid/out of range arguments */ return PJ_EINVAL; case OPUS_BUFFER_TOO_SMALL: /* The mode struct passed is invalid */ return PJMEDIA_CODEC_EPCMTOOSHORT; case OPUS_INTERNAL_ERROR: /* An internal error was detected */ return PJMEDIA_CODEC_EFAILED; case OPUS_INVALID_PACKET: /* The compressed data passed is corrupted */ return PJMEDIA_CODEC_EBADBITSTREAM; case OPUS_UNIMPLEMENTED: /* Invalid/unsupported request number */ return PJ_ENOTSUP; case OPUS_INVALID_STATE: /* An encoder or decoder structure is invalid or already freed */ return PJ_EINVALIDOP; case OPUS_ALLOC_FAIL: /* Memory allocation has failed */ return PJMEDIA_CODEC_EFAILED; } return PJMEDIA_ERROR; } /* * Apply opus settings to dec_fmtp parameters */ void apply_opus_codec_params(pj_pool_t* pool, pjmedia_codec_param *attr) { attr->setting.dec_fmtp.cnt = 0; attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].name = pj_str("useinbandfec"); if (attr->setting.plc == 0) { attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].val = pj_str("0"); } else { attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].val = pj_str("1"); } attr->setting.dec_fmtp.cnt++; if (attr->setting.vad == 1) { attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].name = pj_str("usedtx"); attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].val = pj_str("1"); attr->setting.dec_fmtp.cnt++; } if (attr->info.channel_cnt == 2) { attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].name = pj_str("stereo"); attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].val = pj_str("1"); attr->setting.dec_fmtp.cnt++; } if (attr->info.clock_rate < 48000) { attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].name = pj_str("maxcodedaudiobandwidth"); char clock_rate_char[8]; pj_utoa(attr->info.clock_rate, clock_rate_char); pj_strdup2(pool, &attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].val, clock_rate_char); attr->setting.dec_fmtp.cnt++; } } PJ_DEF(pj_status_t) pjmedia_codec_opus_init(pjmedia_endpt *endpt) { pjmedia_codec_mgr *codec_mgr; pj_status_t status; if (opus_factory.endpt != NULL) { /* Already initialized. */ return PJ_SUCCESS; } /* Init factory */ opus_factory.base.op = &opus_factory_op; opus_factory.base.factory_data = NULL; opus_factory.endpt = endpt; /* Create pool */ opus_factory.pool = pjmedia_endpt_create_pool(endpt, "opus codecs", 4000, 4000); if (!opus_factory.pool) return PJ_ENOMEM; /* Init list */ pj_list_init(&opus_factory.codec_list); /* Create mutex. */ status = pj_mutex_create_simple(opus_factory.pool, "opus codecs", &opus_factory.mutex); if (status != PJ_SUCCESS) goto on_error; PJ_LOG(5, (THIS_FILE, "Init opus")); /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); if (!codec_mgr) return PJ_EINVALIDOP; PJ_LOG(5, (THIS_FILE, "Init opus > DONE")); /* Register codec factory to endpoint. */ status = pjmedia_codec_mgr_register_factory(codec_mgr, &opus_factory.base); if (status != PJ_SUCCESS) return status; return PJ_SUCCESS; on_error: if (opus_factory.mutex) { pj_mutex_destroy(opus_factory.mutex); opus_factory.mutex = NULL; } if (opus_factory.pool) { pj_pool_release(opus_factory.pool); opus_factory.pool = NULL; } return status; } /* * Unregister OPUS codec factory from pjmedia endpoint and deinitialize * the OPUS codec library. */ PJ_DEF(pj_status_t) pjmedia_codec_opus_deinit(void) { pjmedia_codec_mgr *codec_mgr; pj_status_t status; if (opus_factory.endpt == NULL) { /* Not registered. */ return PJ_SUCCESS; } /* Lock mutex. */ pj_mutex_lock(opus_factory.mutex); /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(opus_factory.endpt); if (!codec_mgr) { opus_factory.endpt = NULL; pj_mutex_unlock(opus_factory.mutex); return PJ_EINVALIDOP; } /* Unregister opus codec factory. */ status = pjmedia_codec_mgr_unregister_factory(codec_mgr, &opus_factory.base); opus_factory.endpt = NULL; /* Destroy mutex. */ pj_mutex_unlock(opus_factory.mutex); pj_mutex_destroy(opus_factory.mutex); opus_factory.mutex = NULL; /* Release pool. */ pj_pool_release(opus_factory.pool); opus_factory.pool = NULL; return status; } /* * Check if factory can allocate the specified codec. */ static pj_status_t opus_test_alloc(pjmedia_codec_factory *factory, const pjmedia_codec_info *info) { const pj_str_t opus_tag = { "opus", 4 }; PJ_UNUSED_ARG(factory); PJ_ASSERT_RETURN(factory==&opus_factory.base, PJ_EINVAL); /* Type MUST be audio. */ if (info->type != PJMEDIA_TYPE_AUDIO) return PJMEDIA_CODEC_EUNSUP; /* Check encoding name. */ if (pj_stricmp(&info->encoding_name, &opus_tag) != 0) return PJMEDIA_CODEC_EUNSUP; /* Check clock-rate */ if (info->clock_rate == 8000 || info->clock_rate == 12000 || info->clock_rate == 16000 || info->clock_rate == 24000 || info->clock_rate == 48000) { return PJ_SUCCESS; } /* Clock rate not supported */ return PJMEDIA_CODEC_EUNSUP; } /* * Generate default attribute. */ static pj_status_t opus_default_attr(pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr) { PJ_ASSERT_RETURN(factory == &opus_factory.base, PJ_EINVAL); pj_bzero(attr, sizeof(pjmedia_codec_param)); /* Table from opus rfc +-------+---------+-----------+ | Mode | fs (Hz) | BR (kbps) | +-------+---------+-----------+ | voice | 8000 | 6 - 20 | | voice | 12000 | 7 - 25 | | voice | 16000 | 8 - 30 | | voice | 24000 | 18 - 28 | | voice | 48000 | 24 - 32 | +-------+---------+-----------+ */ attr->info.channel_cnt = 1; /* SAGH: set to 2? */ /* * TODO : would like to use 16kHz as internal clock rate in our case * pjmedia seems to have no support of different clock rate for RTP * and for associated port. Keeping 48kHz for RTP is needed (we just have * to transform timestamps) but to feed codec with 16kHz frames seems requires * some extra work in pjmedia. * For now we are obliged to use pjmedia resampler while would be * more efficient to use the Opus feature instead. * Using g722 hack was tried but seems useless. */ attr->info.clock_rate = 48000; attr->info.avg_bps = 20000; attr->info.max_bps = 32000; attr->info.frm_ptime = FRAME_LENGTH_MS; attr->info.pcm_bits_per_sample = 16; attr->info.pt = (pj_uint8_t) id->pt; /* Inform the stream to prepare a larger buffer since we cannot parse * OPUS packets and split it into individual frames. * Max packet size of opus is 120ms audio */ attr->info.max_rx_frame_size = attr->info.max_bps * 120 / 8 / 1000; if ((attr->info.max_bps * attr->info.frm_ptime) % 8000 != 0) ++attr->info.max_rx_frame_size; attr->setting.frm_per_pkt = 1; /* Default usedtx is 0 in opus */ attr->setting.vad = 0; /* Default useinbandfec is 1 in opus */ attr->setting.plc = 1; /* Apply these settings to relevant fmtp parameters */ apply_opus_codec_params(opus_factory.pool, attr); return PJ_SUCCESS; } /* * Enum codecs supported by this factory. */ static pj_status_t opus_enum_codecs(pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]) { PJ_UNUSED_ARG(factory); PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); pj_bzero(&codecs[0], sizeof(pjmedia_codec_info)); codecs[0].encoding_name = pj_str("opus"); codecs[0].pt = PJMEDIA_RTP_PT_OPUS; codecs[0].type = PJMEDIA_TYPE_AUDIO; codecs[0].clock_rate = 48000; codecs[0].channel_cnt = 1; /* SAGHUL: set to 2? */ *count = 1; return PJ_SUCCESS; } /* * Allocate a new OPUS codec instance. */ static pj_status_t opus_alloc_codec(pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec) { pjmedia_codec *codec; struct opus_private *opus; PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &opus_factory.base, PJ_EINVAL); pj_mutex_lock(opus_factory.mutex); /* Get free nodes, if any. */ if (!pj_list_empty(&opus_factory.codec_list)) { codec = opus_factory.codec_list.next; pj_list_erase(codec); } else { codec = PJ_POOL_ZALLOC_T(opus_factory.pool, pjmedia_codec); PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM); codec->op = &opus_op; codec->factory = factory; codec->codec_data = pj_pool_alloc(opus_factory.pool, sizeof(struct opus_private)); } pj_mutex_unlock(opus_factory.mutex); opus = (struct opus_private*) codec->codec_data; opus->enc_ready = PJ_FALSE; opus->dec_ready = PJ_FALSE; /* Create pool for codec instance */ opus->pool = pjmedia_endpt_create_pool(opus_factory.endpt, "opuscodec", 512, 512); *p_codec = codec; return PJ_SUCCESS; } /* * Free codec. */ static pj_status_t opus_dealloc_codec(pjmedia_codec_factory *factory, pjmedia_codec *codec) { struct opus_private *opus; PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); PJ_UNUSED_ARG(factory); PJ_ASSERT_RETURN(factory == &opus_factory.base, PJ_EINVAL); opus = (struct opus_private*) codec->codec_data; /* Close codec, if it's not closed. */ if (opus->enc_ready || opus->dec_ready) opus_codec_close(codec); /* Put in the free list. */ pj_mutex_lock(opus_factory.mutex); pj_list_push_front(&opus_factory.codec_list, codec); pj_mutex_unlock(opus_factory.mutex); pj_pool_release(opus->pool); opus->pool = NULL; return PJ_SUCCESS; } /* * Init codec. */ static pj_status_t opus_codec_init(pjmedia_codec *codec, pj_pool_t *pool) { PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(pool); return PJ_SUCCESS; } /* * Open codec. */ static pj_status_t opus_codec_open(pjmedia_codec *codec, pjmedia_codec_param *attr) { const pj_str_t STR_FMTP_USE_INBAND_FEC = { "useinbandfec", 12 }; const pj_str_t STR_FMTP_MAX_AVERAGE_BITRATE = { "maxaveragebitrate", 17 }; const pj_str_t STR_FMTP_MAX_CODED_AUDIO_BANDWIDTH = { "maxcodedaudiobandwidth", 22 }; const pj_str_t STR_FMTP_USE_DTX = { "usedtx", 6 }; struct opus_private *opus; int ret, tmpFmtpVal; unsigned i, structSizeBytes, max_nsamples; opus = (struct opus_private*) codec->codec_data; PJ_ASSERT_RETURN(opus && !opus->enc_ready && !opus->dec_ready, PJ_EINVAL); PJ_LOG(4, (THIS_FILE, "Clock rate is %d ", attr->info.clock_rate)); opus->externalFs = attr->info.clock_rate; /* Create Encoder */ structSizeBytes = opus_encoder_get_size(attr->info.channel_cnt); opus->psEnc = pj_pool_zalloc(opus->pool, structSizeBytes); ret = opus_encoder_init(opus->psEnc, opus->externalFs, attr->info.channel_cnt, OPUS_APPLICATION_AUDIO); if (ret) { PJ_LOG(1, (THIS_FILE, "Unable to init encoder : %d", ret)); return PJ_EINVAL; } /* * Set Encoder parameters * TODO : have it configurable */ opus_encoder_ctl(opus->psEnc, OPUS_SET_COMPLEXITY(10)); opus_encoder_ctl(opus->psEnc, OPUS_SET_INBAND_FEC(1)); /* on by default */ opus_encoder_ctl(opus->psEnc, OPUS_SET_PACKET_LOSS_PERC(5)); opus_encoder_ctl(opus->psEnc, OPUS_SET_SIGNAL(OPUS_AUTO)); /* Apply fmtp params to Encoder */ for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) { if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_FMTP_USE_INBAND_FEC) == 0) { tmpFmtpVal = (int)(pj_strtoul(&attr->setting.enc_fmtp.param[i].val)); opus_encoder_ctl(opus->psEnc, OPUS_SET_INBAND_FEC(tmpFmtpVal)); break; } else if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_FMTP_MAX_AVERAGE_BITRATE) == 0) { tmpFmtpVal = (int)(pj_strtoul(&attr->setting.enc_fmtp.param[i].val)); if (tmpFmtpVal >= 6000 && tmpFmtpVal <= 510000) { opus_encoder_ctl(opus->psEnc, OPUS_SET_BITRATE(tmpFmtpVal)); } } else if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_FMTP_MAX_CODED_AUDIO_BANDWIDTH) == 0) { tmpFmtpVal = (int)(pj_strtoul(&attr->setting.enc_fmtp.param[i].val)); if (tmpFmtpVal <= 8000) { opus_encoder_ctl(opus->psEnc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND)); } else if (tmpFmtpVal <= 12000) { opus_encoder_ctl(opus->psEnc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_MEDIUMBAND)); } else if (tmpFmtpVal <= 16000) { opus_encoder_ctl(opus->psEnc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND)); } else if (tmpFmtpVal <= 24000) { opus_encoder_ctl(opus->psEnc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND)); } else if (tmpFmtpVal <= 48000) { opus_encoder_ctl(opus->psEnc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)); } } else if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_FMTP_USE_DTX) == 0) { tmpFmtpVal = (int)(pj_strtoul(&attr->setting.enc_fmtp.param[i].val)); opus_encoder_ctl(opus->psEnc, OPUS_SET_DTX(tmpFmtpVal)); } } opus->enc_ready = PJ_TRUE; /* Decoder buffer */ opus->pcm_bytes_per_sample = attr->info.pcm_bits_per_sample / 8; max_nsamples = 120 * OPUS_CLOCK_RATE / 1000; /* 120ms is max frame time */ opus->dec_buf_max_size = max_nsamples * opus->pcm_bytes_per_sample; opus->dec_buf = pj_pool_alloc(opus->pool, opus->dec_buf_max_size); /* Create decoder */ structSizeBytes = opus_decoder_get_size(attr->info.channel_cnt); opus->psDec = pj_pool_zalloc(opus->pool, structSizeBytes); ret = opus_decoder_init(opus->psDec, opus->externalFs, attr->info.channel_cnt); if (ret) { PJ_LOG(1, (THIS_FILE, "Unable to init decoder : %d", ret)); return PJ_EINVAL; } opus->dec_ready = PJ_TRUE; return PJ_SUCCESS; } /* * Close codec. */ static pj_status_t opus_codec_close(pjmedia_codec *codec) { struct opus_private *opus; opus = (struct opus_private*) codec->codec_data; opus->enc_ready = PJ_FALSE; opus->dec_ready = PJ_FALSE; PJ_LOG(5, (THIS_FILE, "OPUS codec closed")); return PJ_SUCCESS; } /* * Modify codec settings. */ static pj_status_t opus_codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr) { PJ_TODO(implement_opus_codec_modify); PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(attr); return PJ_SUCCESS; } /* * Encode frame. */ static pj_status_t opus_codec_encode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { struct opus_private *opus; opus_int32 ret; unsigned nsamples; PJ_ASSERT_RETURN(codec && input && output, PJ_EINVAL); opus = (struct opus_private*) codec->codec_data; /* Check frame in size */ nsamples = input->size / opus->pcm_bytes_per_sample; /* TODO: validate? */ /* Encode */ output->size = 0; ret = opus_encode(opus->psEnc, (opus_int16*) input->buf, nsamples, (unsigned char *) output->buf, output_buf_len); if (ret < 0) { PJ_LOG(1, (THIS_FILE, "Impossible to encode packet %d", ret)); return opus_to_pjsip_error_code(ret); } else { output->size = (pj_size_t) ret; } output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->timestamp = input->timestamp; #if _TRACE_OPUS PJ_LOG(4, (THIS_FILE, "Encoder packet size %d for input %d ouput max len %d @ %d", output->size, input->size, output_buf_len, (unsigned) output->timestamp.u64)); #endif return PJ_SUCCESS; } /* * Get frames in the packet. */ static pj_status_t opus_codec_parse(pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]) { struct opus_private *opus; unsigned char toc; const unsigned char *raw_frames[48]; short size[48]; int err, payload_offset, samples_per_frame; unsigned i; PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL); opus = (struct opus_private*) codec->codec_data; err = opus_packet_parse(pkt, pkt_size, &toc, raw_frames, size, &payload_offset); if (err <= 0) { PJ_LOG(4, (THIS_FILE, "Error parsing Opus packet: %s", opus_strerror(err))); *frame_cnt = 0; return opus_to_pjsip_error_code(err); } *frame_cnt = (unsigned)err; samples_per_frame = opus_packet_get_samples_per_frame(pkt, opus->externalFs); #if _TRACE_OPUS PJ_LOG(4, (THIS_FILE, "Pkt info : bw -> %d , spf -> %d", opus_packet_get_bandwidth(pkt), samples_per_frame)); #endif for (i = 0; i < *frame_cnt; i++) { frames[i].type = PJMEDIA_FRAME_TYPE_AUDIO; frames[i].bit_info = (((unsigned)ts->u64 & 0xFFFF) << 16) | (((unsigned)pkt & 0xFF) << 8) | i; frames[i].buf = pkt; frames[i].size = pkt_size; frames[i].timestamp.u64 = ts->u64 + i * samples_per_frame; #if _TRACE_OPUS PJ_LOG(4, (THIS_FILE, "parsed %d of %d",frames[i].size, *frame_cnt)); #endif } return PJ_SUCCESS; } static pj_status_t opus_codec_decode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { struct opus_private *opus; unsigned pkt_info, frm_info, frm_size; PJ_ASSERT_RETURN(codec && input && output_buf_len && output, PJ_EINVAL); opus = (struct opus_private*) codec->codec_data; pkt_info = input->bit_info & 0xFFFFFF00; frm_info = input->bit_info & 0xF; if (opus->pkt_info != pkt_info || input->bit_info == 0) { opus->pkt_info = pkt_info; opus->dec_buf_sample_per_frame = opus_packet_get_samples_per_frame(input->buf, opus->externalFs); /* We need to decode all the frames in the packet. */ opus->dec_buf_size = opus_decode(opus->psDec, (const unsigned char *) input->buf, (opus_int32) input->size, opus->dec_buf, opus->dec_buf_max_size, 0 /* decode FEC */); if(opus->dec_buf_size <= 0){ PJ_LOG(2, (THIS_FILE, "Failed to decode frame (err=%d)", opus->dec_buf_size)); opus->dec_buf_size = 0; } else { opus->dec_buf_size = opus->dec_buf_size * opus->pcm_bytes_per_sample; } } /* We have this packet decoded now (either was previously in the buffer or was just added to buffer). */ if (opus->dec_buf_size == 0) { /* The decoding was a failure. */ output->size = 0; } else { frm_size = opus->dec_buf_sample_per_frame * opus->pcm_bytes_per_sample; #if _TRACE_OPUS PJ_LOG(4, (THIS_FILE, "Decode : copy from big buffer %d to %d", output_buf_len, frm_size)); #endif if(output_buf_len < frm_size){ return PJ_ETOOSMALL; } /* Copy the decoded frame from the buffer. */ pj_memcpy(output->buf, ((opus_int16*)opus->dec_buf) + (frm_info * frm_size), frm_size); output->size = frm_size; } if (output->size == 0) { output->type = PJMEDIA_FRAME_TYPE_NONE; output->buf = NULL; return PJMEDIA_CODEC_EFAILED; } output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->timestamp = input->timestamp; #if _TRACE_OPUS PJ_LOG(4, (THIS_FILE, "Decoded %d to %d with max %d", input->size, output->size, output_buf_len)); #endif return PJ_SUCCESS; } /* * Recover lost frame. */ static pj_status_t opus_codec_recover(pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output) { struct opus_private *opus; int ret = 0; int frame_size; PJ_ASSERT_RETURN(output, PJ_EINVAL); opus = (struct opus_private*) codec->codec_data; frame_size = output_buf_len / opus->pcm_bytes_per_sample; /* Decode */ ret = opus_decode(opus->psDec, (const unsigned char *) NULL, 0, output->buf, frame_size, 0); if (ret < 0) { PJ_LOG(1, (THIS_FILE, "Failed to recover opus frame %d", ret)); return PJ_EINVAL; } else if (ret == 0) { #if _TRACE_OPUS PJ_LOG(4, (THIS_FILE, "Empty frame recovered %d", ret)); #endif output->type = PJMEDIA_FRAME_TYPE_NONE; output->buf = NULL; output->size = 0; } else { #if _TRACE_OPUS PJ_LOG(4, (THIS_FILE, "Frame recovered %d", ret)); #endif output->size = ret * opus->pcm_bytes_per_sample; output->type = PJMEDIA_FRAME_TYPE_AUDIO; } return PJ_SUCCESS; } #endif ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/passthrough.c ================================================ /* $Id: passthrough.c 4082 2012-04-24 13:09:14Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include /* * Only build this file if PJMEDIA_HAS_PASSTHROUGH_CODECS != 0 */ #if defined(PJMEDIA_HAS_PASSTHROUGH_CODECS) && PJMEDIA_HAS_PASSTHROUGH_CODECS!=0 #define THIS_FILE "passthrough.c" /* Prototypes for passthrough codecs factory */ static pj_status_t test_alloc( pjmedia_codec_factory *factory, const pjmedia_codec_info *id ); static pj_status_t default_attr( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ); static pj_status_t enum_codecs( pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]); static pj_status_t alloc_codec( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec); static pj_status_t dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ); /* Prototypes for passthrough codecs implementation. */ static pj_status_t codec_init( pjmedia_codec *codec, pj_pool_t *pool ); static pj_status_t codec_open( pjmedia_codec *codec, pjmedia_codec_param *attr ); static pj_status_t codec_close( pjmedia_codec *codec ); static pj_status_t codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr ); static pj_status_t codec_parse( pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]); static pj_status_t codec_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); static pj_status_t codec_decode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); static pj_status_t codec_recover( pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output); /* Definition for passthrough codecs operations. */ static pjmedia_codec_op codec_op = { &codec_init, &codec_open, &codec_close, &codec_modify, &codec_parse, &codec_encode, &codec_decode, &codec_recover }; /* Definition for passthrough codecs factory operations. */ static pjmedia_codec_factory_op codec_factory_op = { &test_alloc, &default_attr, &enum_codecs, &alloc_codec, &dealloc_codec, &pjmedia_codec_passthrough_deinit }; /* Passthrough codecs factory */ static struct codec_factory { pjmedia_codec_factory base; pjmedia_endpt *endpt; pj_pool_t *pool; pj_mutex_t *mutex; } codec_factory; /* Passthrough codecs private data. */ typedef struct codec_private { pj_pool_t *pool; /**< Pool for each instance. */ int codec_idx; /**< Codec index. */ void *codec_setting; /**< Specific codec setting. */ pj_uint16_t avg_frame_size; /**< Average of frame size. */ unsigned samples_per_frame; /**< Samples per frame, for iLBC this can be 240 or 160, can only be known after codec is opened. */ } codec_private_t; /* CUSTOM CALLBACKS */ /* Parse frames from a packet. Default behaviour of frame parsing is * just separating frames based on calculating frame length derived * from bitrate. Implement this callback when the default behaviour is * unapplicable. */ typedef pj_status_t (*parse_cb)(codec_private_t *codec_data, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]); /* Pack frames into a packet. Default behaviour of packing frames is * just stacking the frames with octet aligned without adding any * payload header. Implement this callback when the default behaviour is * unapplicable. */ typedef pj_status_t (*pack_cb)(codec_private_t *codec_data, const struct pjmedia_frame_ext *input, unsigned output_buf_len, struct pjmedia_frame *output); /* Custom callback implementations. */ static pj_status_t parse_amr( codec_private_t *codec_data, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]); static pj_status_t pack_amr ( codec_private_t *codec_data, const struct pjmedia_frame_ext *input, unsigned output_buf_len, struct pjmedia_frame *output); /* Passthrough codec implementation descriptions. */ static struct codec_desc { int enabled; /* Is this codec enabled? */ const char *name; /* Codec name. */ pj_uint8_t pt; /* Payload type. */ pjmedia_format_id fmt_id; /* Source format. */ unsigned clock_rate; /* Codec's clock rate. */ unsigned channel_count; /* Codec's channel count. */ unsigned samples_per_frame; /* Codec's samples count. */ unsigned def_bitrate; /* Default bitrate of this codec. */ unsigned max_bitrate; /* Maximum bitrate of this codec. */ pj_uint8_t frm_per_pkt; /* Default num of frames per packet.*/ pj_uint8_t vad; /* VAD enabled/disabled. */ pj_uint8_t plc; /* PLC enabled/disabled. */ parse_cb parse; /* Callback to parse bitstream. */ pack_cb pack; /* Callback to pack bitstream. */ pjmedia_codec_fmtp dec_fmtp; /* Decoder's fmtp params. */ } codec_desc[] = { # if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR {1, "AMR", PJMEDIA_RTP_PT_AMR, PJMEDIA_FORMAT_AMR, 8000, 1, 160, 7400, 12200, 2, 1, 1, &parse_amr, &pack_amr /*, {1, {{{"octet-align", 11}, {"1", 1}}} } */ }, # endif # if PJMEDIA_HAS_PASSTHROUGH_CODEC_G729 {1, "G729", PJMEDIA_RTP_PT_G729, PJMEDIA_FORMAT_G729, 8000, 1, 80, 8000, 8000, 2, 1, 1 }, # endif # if PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC {1, "iLBC", PJMEDIA_RTP_PT_ILBC, PJMEDIA_FORMAT_ILBC, 8000, 1, 240, 13333, 15200, 1, 1, 1, NULL, NULL, {1, {{{"mode", 4}, {"30", 2}}} } }, # endif # if PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU {1, "PCMU", PJMEDIA_RTP_PT_PCMU, PJMEDIA_FORMAT_PCMU, 8000, 1, 80, 64000, 64000, 2, 1, 1 }, # endif # if PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA {1, "PCMA", PJMEDIA_RTP_PT_PCMA, PJMEDIA_FORMAT_PCMA, 8000, 1, 80, 64000, 64000, 2, 1, 1 }, # endif }; #if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR #include typedef struct amr_settings_t { pjmedia_codec_amr_pack_setting enc_setting; pjmedia_codec_amr_pack_setting dec_setting; pj_int8_t enc_mode; } amr_settings_t; /* Pack AMR payload */ static pj_status_t pack_amr ( codec_private_t *codec_data, const struct pjmedia_frame_ext *input, unsigned output_buf_len, struct pjmedia_frame *output) { enum {MAX_FRAMES_PER_PACKET = PJMEDIA_MAX_FRAME_DURATION_MS / 20}; pjmedia_frame frames[MAX_FRAMES_PER_PACKET]; amr_settings_t* setting = (amr_settings_t*)codec_data->codec_setting; pjmedia_codec_amr_pack_setting *enc_setting = &setting->enc_setting; pj_uint8_t SID_FT; unsigned i; pj_assert(input->subframe_cnt <= MAX_FRAMES_PER_PACKET); SID_FT = (pj_uint8_t)(enc_setting->amr_nb? 8 : 9); /* Get frames */ for (i = 0; i < input->subframe_cnt; ++i) { pjmedia_frame_ext_subframe *sf; pjmedia_codec_amr_bit_info *info; unsigned len; sf = pjmedia_frame_ext_get_subframe(input, i); len = (sf->bitlen + 7) >> 3; info = (pjmedia_codec_amr_bit_info*) &frames[i].bit_info; pj_bzero(info, sizeof(*info)); if (len == 0) { /* DTX */ info->frame_type = 15; } else { info->frame_type = pjmedia_codec_amr_get_mode2(enc_setting->amr_nb, len); } info->good_quality = 1; info->mode = setting->enc_mode; if (info->frame_type == SID_FT) info->STI = (sf->data[4] >> 4) & 1; frames[i].buf = sf->data; frames[i].size = len; } output->size = output_buf_len; return pjmedia_codec_amr_pack(frames, input->subframe_cnt, enc_setting, output->buf, &output->size); } /* Parse AMR payload into frames. */ static pj_status_t parse_amr(codec_private_t *codec_data, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]) { amr_settings_t* s = (amr_settings_t*)codec_data->codec_setting; pjmedia_codec_amr_pack_setting *setting; pj_status_t status; pj_uint8_t cmr; setting = &s->dec_setting; status = pjmedia_codec_amr_parse(pkt, pkt_size, ts, setting, frames, frame_cnt, &cmr); if (status != PJ_SUCCESS) return status; // CMR is not supported for now. /* Check Change Mode Request. */ //if ((setting->amr_nb && cmr <= 7) || (!setting->amr_nb && cmr <= 8)) { // s->enc_mode = cmr; //} return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR */ /* * Initialize and register passthrough codec factory to pjmedia endpoint. */ PJ_DEF(pj_status_t) pjmedia_codec_passthrough_init( pjmedia_endpt *endpt ) { pjmedia_codec_mgr *codec_mgr; pj_str_t codec_name; pj_status_t status; if (codec_factory.pool != NULL) { /* Already initialized. */ return PJ_EEXISTS; } /* Create passthrough codec factory. */ codec_factory.base.op = &codec_factory_op; codec_factory.base.factory_data = NULL; codec_factory.endpt = endpt; codec_factory.pool = pjmedia_endpt_create_pool(endpt, "Passthrough codecs", 4000, 4000); if (!codec_factory.pool) return PJ_ENOMEM; /* Create mutex. */ status = pj_mutex_create_simple(codec_factory.pool, "Passthrough codecs", &codec_factory.mutex); if (status != PJ_SUCCESS) goto on_error; /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); if (!codec_mgr) { status = PJ_EINVALIDOP; goto on_error; } /* Register format match callback. */ #if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR pj_cstr(&codec_name, "AMR"); status = pjmedia_sdp_neg_register_fmt_match_cb( &codec_name, &pjmedia_codec_amr_match_sdp); if (status != PJ_SUCCESS) goto on_error; #endif /* Suppress compile warning */ PJ_UNUSED_ARG(codec_name); /* Register codec factory to endpoint. */ status = pjmedia_codec_mgr_register_factory(codec_mgr, &codec_factory.base); if (status != PJ_SUCCESS) goto on_error; /* Done. */ return PJ_SUCCESS; on_error: pj_pool_release(codec_factory.pool); codec_factory.pool = NULL; return status; } /* * Initialize and register passthrough codec factory to pjmedia endpoint. */ PJ_DEF(pj_status_t) pjmedia_codec_passthrough_init2( pjmedia_endpt *endpt, const pjmedia_codec_passthrough_setting *setting) { if (codec_factory.pool != NULL) { /* Already initialized. */ return PJ_EEXISTS; } if (setting != NULL) { unsigned i; /* Enable/disable codecs based on the specified encoding formats */ for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) { pj_bool_t enabled = PJ_FALSE; unsigned j; for (j = 0; j < setting->fmt_cnt && !enabled; ++j) { if ((pj_uint32_t)codec_desc[i].fmt_id == setting->fmts[j].id) enabled = PJ_TRUE; } codec_desc[i].enabled = enabled; } #if PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC /* Update iLBC codec description based on default mode setting. */ for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) { if (codec_desc[i].enabled && codec_desc[i].fmt_id == PJMEDIA_FORMAT_ILBC) { codec_desc[i].samples_per_frame = (setting->ilbc_mode == 20? 160 : 240); codec_desc[i].def_bitrate = (setting->ilbc_mode == 20? 15200 : 13333); pj_strset2(&codec_desc[i].dec_fmtp.param[0].val, (setting->ilbc_mode == 20? "20" : "30")); break; } } #endif } return pjmedia_codec_passthrough_init(endpt); } /* * Unregister passthrough codecs factory from pjmedia endpoint. */ PJ_DEF(pj_status_t) pjmedia_codec_passthrough_deinit(void) { pjmedia_codec_mgr *codec_mgr; unsigned i; pj_status_t status; if (codec_factory.pool == NULL) { /* Already deinitialized */ return PJ_SUCCESS; } pj_mutex_lock(codec_factory.mutex); /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(codec_factory.endpt); if (!codec_mgr) { pj_pool_release(codec_factory.pool); codec_factory.pool = NULL; pj_mutex_unlock(codec_factory.mutex); return PJ_EINVALIDOP; } /* Unregister passthrough codecs factory. */ status = pjmedia_codec_mgr_unregister_factory(codec_mgr, &codec_factory.base); /* Destroy mutex. */ pj_mutex_unlock(codec_factory.mutex); pj_mutex_destroy(codec_factory.mutex); codec_factory.mutex = NULL; /* Destroy pool. */ pj_pool_release(codec_factory.pool); codec_factory.pool = NULL; /* Re-enable all codecs in the codec_desc. */ for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) { codec_desc[i].enabled = PJ_TRUE; } return status; } /* * Check if factory can allocate the specified codec. */ static pj_status_t test_alloc( pjmedia_codec_factory *factory, const pjmedia_codec_info *info ) { unsigned i; PJ_UNUSED_ARG(factory); /* Type MUST be audio. */ if (info->type != PJMEDIA_TYPE_AUDIO) return PJMEDIA_CODEC_EUNSUP; for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) { pj_str_t name = pj_str((char*)codec_desc[i].name); if ((pj_stricmp(&info->encoding_name, &name) == 0) && (info->clock_rate == (unsigned)codec_desc[i].clock_rate) && (info->channel_cnt == (unsigned)codec_desc[i].channel_count) && (codec_desc[i].enabled)) { return PJ_SUCCESS; } } /* Unsupported, or mode is disabled. */ return PJMEDIA_CODEC_EUNSUP; } /* * Generate default attribute. */ static pj_status_t default_attr ( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ) { unsigned i; PJ_ASSERT_RETURN(factory==&codec_factory.base, PJ_EINVAL); pj_bzero(attr, sizeof(pjmedia_codec_param)); for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) { pj_str_t name = pj_str((char*)codec_desc[i].name); if ((pj_stricmp(&id->encoding_name, &name) == 0) && (id->clock_rate == (unsigned)codec_desc[i].clock_rate) && (id->channel_cnt == (unsigned)codec_desc[i].channel_count) && (id->pt == (unsigned)codec_desc[i].pt)) { attr->info.pt = (pj_uint8_t)id->pt; attr->info.channel_cnt = codec_desc[i].channel_count; attr->info.clock_rate = codec_desc[i].clock_rate; attr->info.avg_bps = codec_desc[i].def_bitrate; attr->info.max_bps = codec_desc[i].max_bitrate; attr->info.pcm_bits_per_sample = 16; attr->info.frm_ptime = (pj_uint16_t) (codec_desc[i].samples_per_frame * 1000 / codec_desc[i].channel_count / codec_desc[i].clock_rate); attr->info.fmt_id = codec_desc[i].fmt_id; /* Default flags. */ attr->setting.frm_per_pkt = codec_desc[i].frm_per_pkt; attr->setting.plc = codec_desc[i].plc; attr->setting.penh= 0; attr->setting.vad = codec_desc[i].vad; attr->setting.cng = attr->setting.vad; attr->setting.dec_fmtp = codec_desc[i].dec_fmtp; if (attr->setting.vad == 0) { #if PJMEDIA_HAS_PASSTHROUGH_CODEC_G729 if (id->pt == PJMEDIA_RTP_PT_G729) { /* Signal G729 Annex B is being disabled */ attr->setting.dec_fmtp.cnt = 1; pj_strset2(&attr->setting.dec_fmtp.param[0].name, "annexb"); pj_strset2(&attr->setting.dec_fmtp.param[0].val, "no"); } #endif } return PJ_SUCCESS; } } return PJMEDIA_CODEC_EUNSUP; } /* * Enum codecs supported by this factory. */ static pj_status_t enum_codecs( pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]) { unsigned max; unsigned i; PJ_UNUSED_ARG(factory); PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); max = *count; for (i = 0, *count = 0; i < PJ_ARRAY_SIZE(codec_desc) && *count < max; ++i) { if (!codec_desc[i].enabled) continue; pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info)); codecs[*count].encoding_name = pj_str((char*)codec_desc[i].name); codecs[*count].pt = codec_desc[i].pt; codecs[*count].type = PJMEDIA_TYPE_AUDIO; codecs[*count].clock_rate = codec_desc[i].clock_rate; codecs[*count].channel_cnt = codec_desc[i].channel_count; ++*count; } return PJ_SUCCESS; } /* * Allocate a new codec instance. */ static pj_status_t alloc_codec( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec) { codec_private_t *codec_data; pjmedia_codec *codec; int idx; pj_pool_t *pool; unsigned i; PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &codec_factory.base, PJ_EINVAL); pj_mutex_lock(codec_factory.mutex); /* Find codec's index */ idx = -1; for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) { pj_str_t name = pj_str((char*)codec_desc[i].name); if ((pj_stricmp(&id->encoding_name, &name) == 0) && (id->clock_rate == (unsigned)codec_desc[i].clock_rate) && (id->channel_cnt == (unsigned)codec_desc[i].channel_count) && (codec_desc[i].enabled)) { idx = i; break; } } if (idx == -1) { *p_codec = NULL; return PJMEDIA_CODEC_EUNSUP; } /* Create pool for codec instance */ pool = pjmedia_endpt_create_pool(codec_factory.endpt, "passthroughcodec", 512, 512); codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec); codec->op = &codec_op; codec->factory = factory; codec->codec_data = PJ_POOL_ZALLOC_T(pool, codec_private_t); codec_data = (codec_private_t*) codec->codec_data; codec_data->pool = pool; codec_data->codec_idx = idx; pj_mutex_unlock(codec_factory.mutex); *p_codec = codec; return PJ_SUCCESS; } /* * Free codec. */ static pj_status_t dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ) { codec_private_t *codec_data; PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &codec_factory.base, PJ_EINVAL); /* Close codec, if it's not closed. */ codec_data = (codec_private_t*) codec->codec_data; codec_close(codec); pj_pool_release(codec_data->pool); return PJ_SUCCESS; } /* * Init codec. */ static pj_status_t codec_init( pjmedia_codec *codec, pj_pool_t *pool ) { PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(pool); return PJ_SUCCESS; } /* * Open codec. */ static pj_status_t codec_open( pjmedia_codec *codec, pjmedia_codec_param *attr ) { codec_private_t *codec_data = (codec_private_t*) codec->codec_data; struct codec_desc *desc = &codec_desc[codec_data->codec_idx]; pj_pool_t *pool; int i, j; pool = codec_data->pool; /* Cache samples per frame value */ codec_data->samples_per_frame = desc->samples_per_frame; /* Calculate bitstream size */ i = attr->info.avg_bps * codec_data->samples_per_frame; j = desc->clock_rate << 3; codec_data->avg_frame_size = (pj_uint16_t)(i / j); if (i % j) ++codec_data->avg_frame_size; #if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR /* Init AMR settings */ if (desc->pt == PJMEDIA_RTP_PT_AMR || desc->pt == PJMEDIA_RTP_PT_AMRWB) { amr_settings_t *s; pj_uint8_t octet_align = 0; pj_int8_t enc_mode; enc_mode = pjmedia_codec_amr_get_mode(attr->info.avg_bps); pj_assert(enc_mode >= 0 && enc_mode <= 8); for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) { const pj_str_t STR_FMTP_OCTET_ALIGN = {"octet-align", 11}; /* Fetch octet-align setting. It should be fine to fetch only * the decoder, since encoder & decoder must use the same setting * (RFC 4867 section 8.3.1). */ if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name, &STR_FMTP_OCTET_ALIGN) == 0) { octet_align=(pj_uint8_t) (pj_strtoul(&attr->setting.dec_fmtp.param[i].val)); break; } } for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) { const pj_str_t STR_FMTP_MODE_SET = {"mode-set", 8}; /* mode-set, encoding mode is chosen based on local default mode * setting: * - if local default mode is included in the mode-set, use it * - otherwise, find the closest mode to local default mode; * if there are two closest modes, prefer to use the higher * one, e.g: local default mode is 4, the mode-set param * contains '2,3,5,6', then 5 will be chosen. */ if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_FMTP_MODE_SET) == 0) { const char *p; pj_size_t l; pj_int8_t diff = 99; p = pj_strbuf(&attr->setting.enc_fmtp.param[i].val); l = pj_strlen(&attr->setting.enc_fmtp.param[i].val); while (l--) { if ((desc->pt==PJMEDIA_RTP_PT_AMR && *p>='0' && *p<='7') || (desc->pt==PJMEDIA_RTP_PT_AMRWB && *p>='0' && *p<='8')) { pj_int8_t tmp = (pj_int8_t)(*p - '0' - enc_mode); if (PJ_ABS(diff) > PJ_ABS(tmp) || (PJ_ABS(diff) == PJ_ABS(tmp) && tmp > diff)) { diff = tmp; if (diff == 0) break; } } ++p; } if (diff == 99) return PJMEDIA_CODEC_EFAILED; enc_mode = (pj_int8_t)(enc_mode + diff); break; } } s = PJ_POOL_ZALLOC_T(pool, amr_settings_t); codec_data->codec_setting = s; s->enc_mode = enc_mode; if (s->enc_mode < 0) return PJMEDIA_CODEC_EINMODE; s->enc_setting.amr_nb = (pj_uint8_t)(desc->pt == PJMEDIA_RTP_PT_AMR); s->enc_setting.octet_aligned = octet_align; s->enc_setting.reorder = PJ_FALSE; /* Note this! passthrough codec doesn't do sensitivity bits reordering */ s->enc_setting.cmr = 15; s->dec_setting.amr_nb = (pj_uint8_t)(desc->pt == PJMEDIA_RTP_PT_AMR); s->dec_setting.octet_aligned = octet_align; s->dec_setting.reorder = PJ_FALSE; /* Note this! passthrough codec doesn't do sensitivity bits reordering */ /* Return back bitrate info to application */ attr->info.avg_bps = s->enc_setting.amr_nb? pjmedia_codec_amrnb_bitrates[s->enc_mode]: pjmedia_codec_amrwb_bitrates[s->enc_mode]; } #endif #if PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC /* Init iLBC settings */ if (desc->pt == PJMEDIA_RTP_PT_ILBC) { enum { DEFAULT_MODE = 30 }; static pj_str_t STR_MODE = {"mode", 4}; pj_uint16_t dec_fmtp_mode = DEFAULT_MODE, enc_fmtp_mode = DEFAULT_MODE; /* Get decoder mode */ for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) { if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name, &STR_MODE) == 0) { dec_fmtp_mode = (pj_uint16_t) pj_strtoul(&attr->setting.dec_fmtp.param[i].val); break; } } /* Decoder mode must be set */ PJ_ASSERT_RETURN(dec_fmtp_mode == 20 || dec_fmtp_mode == 30, PJMEDIA_CODEC_EINMODE); /* Get encoder mode */ for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) { if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_MODE) == 0) { enc_fmtp_mode = (pj_uint16_t) pj_strtoul(&attr->setting.enc_fmtp.param[i].val); break; } } PJ_ASSERT_RETURN(enc_fmtp_mode==20 || enc_fmtp_mode==30, PJMEDIA_CODEC_EINMODE); /* Both sides of a bi-directional session MUST use the same "mode" value. * In this point, possible values are only 20 or 30, so when encoder and * decoder modes are not same, just use the default mode, it is 30. */ if (enc_fmtp_mode != dec_fmtp_mode) { enc_fmtp_mode = dec_fmtp_mode = DEFAULT_MODE; PJ_LOG(4,(pool->obj_name, "Normalized iLBC encoder and decoder modes to %d", DEFAULT_MODE)); } /* Update some attributes based on negotiated mode. */ attr->info.avg_bps = (dec_fmtp_mode == 30? 13333 : 15200); attr->info.frm_ptime = dec_fmtp_mode; /* Override average frame size */ codec_data->avg_frame_size = (dec_fmtp_mode == 30? 50 : 38); /* Override samples per frame */ codec_data->samples_per_frame = (dec_fmtp_mode == 30? 240 : 160); } #endif return PJ_SUCCESS; } /* * Close codec. */ static pj_status_t codec_close( pjmedia_codec *codec ) { PJ_UNUSED_ARG(codec); return PJ_SUCCESS; } /* * Modify codec settings. */ static pj_status_t codec_modify( pjmedia_codec *codec, const pjmedia_codec_param *attr ) { /* Not supported yet. */ PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(attr); return PJ_ENOTSUP; } /* * Get frames in the packet. */ static pj_status_t codec_parse( pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]) { codec_private_t *codec_data = (codec_private_t*) codec->codec_data; struct codec_desc *desc = &codec_desc[codec_data->codec_idx]; unsigned count = 0; PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL); if (desc->parse != NULL) { return desc->parse(codec_data, pkt, pkt_size, ts, frame_cnt, frames); } while (pkt_size >= codec_data->avg_frame_size && count < *frame_cnt) { frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO; frames[count].buf = pkt; frames[count].size = codec_data->avg_frame_size; frames[count].timestamp.u64 = ts->u64 + count * codec_data->samples_per_frame; pkt = (pj_uint8_t*)pkt + codec_data->avg_frame_size; pkt_size -= codec_data->avg_frame_size; ++count; } if (pkt_size && count < *frame_cnt) { frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO; frames[count].buf = pkt; frames[count].size = pkt_size; frames[count].timestamp.u64 = ts->u64 + count * codec_data->samples_per_frame; ++count; } *frame_cnt = count; return PJ_SUCCESS; } /* * Encode frames. */ static pj_status_t codec_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { codec_private_t *codec_data = (codec_private_t*) codec->codec_data; struct codec_desc *desc = &codec_desc[codec_data->codec_idx]; const pjmedia_frame_ext *input_ = (const pjmedia_frame_ext*) input; pj_assert(input && input->type == PJMEDIA_FRAME_TYPE_EXTENDED); if (desc->pack != NULL) { desc->pack(codec_data, input_, output_buf_len, output); } else { if (input_->subframe_cnt == 0) { /* DTX */ output->buf = NULL; output->size = 0; output->type = PJMEDIA_FRAME_TYPE_NONE; } else { unsigned i; pj_uint8_t *p = output->buf; output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->size = 0; for (i = 0; i < input_->subframe_cnt; ++i) { pjmedia_frame_ext_subframe *sf; unsigned sf_len; sf = pjmedia_frame_ext_get_subframe(input_, i); pj_assert(sf); sf_len = (sf->bitlen + 7) >> 3; pj_memcpy(p, sf->data, sf_len); p += sf_len; output->size += sf_len; /* If there is SID or DTX frame, break the loop. */ if (desc->pt == PJMEDIA_RTP_PT_G729 && sf_len < codec_data->avg_frame_size) { break; } } } } output->timestamp = input->timestamp; return PJ_SUCCESS; } /* * Decode frame. */ static pj_status_t codec_decode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { codec_private_t *codec_data = (codec_private_t*) codec->codec_data; #if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR struct codec_desc *desc = &codec_desc[codec_data->codec_idx]; #endif pjmedia_frame_ext *output_ = (pjmedia_frame_ext*) output; pj_assert(input); PJ_UNUSED_ARG(output_buf_len); #if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR /* Need to rearrange the AMR bitstream, since the bitstream may not be * started from bit 0 or may need to be reordered from sensitivity order * into encoder bits order. */ if (desc->pt == PJMEDIA_RTP_PT_AMR || desc->pt == PJMEDIA_RTP_PT_AMRWB) { pjmedia_frame input_; pjmedia_codec_amr_pack_setting *setting; setting = &((amr_settings_t*)codec_data->codec_setting)->dec_setting; input_ = *input; pjmedia_codec_amr_predecode(input, setting, &input_); pjmedia_frame_ext_append_subframe(output_, input_.buf, (pj_uint16_t)(input_.size << 3), (pj_uint16_t)codec_data->samples_per_frame); output->timestamp = input->timestamp; return PJ_SUCCESS; } #endif pjmedia_frame_ext_append_subframe(output_, input->buf, (pj_uint16_t)(input->size << 3), (pj_uint16_t)codec_data->samples_per_frame); output->timestamp = input->timestamp; return PJ_SUCCESS; } /* * Recover lost frame. */ static pj_status_t codec_recover( pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output) { codec_private_t *codec_data = (codec_private_t*) codec->codec_data; pjmedia_frame_ext *output_ = (pjmedia_frame_ext*) output; PJ_UNUSED_ARG(output_buf_len); pjmedia_frame_ext_append_subframe(output_, NULL, 0, (pj_uint16_t)codec_data->samples_per_frame); return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_PASSTHROUGH_CODECS */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/speex_codec.c ================================================ /* $Id: speex_codec.c 3664 2011-07-19 03:42:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include /* * Only build this file if PJMEDIA_HAS_SPEEX_CODEC != 0 */ #if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0 #define THIS_FILE "speex_codec.c" /* Prototypes for Speex factory */ static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory, const pjmedia_codec_info *id ); static pj_status_t spx_default_attr( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ); static pj_status_t spx_enum_codecs( pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]); static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec); static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ); /* Prototypes for Speex implementation. */ static pj_status_t spx_codec_init( pjmedia_codec *codec, pj_pool_t *pool ); static pj_status_t spx_codec_open( pjmedia_codec *codec, pjmedia_codec_param *attr ); static pj_status_t spx_codec_close( pjmedia_codec *codec ); static pj_status_t spx_codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr ); static pj_status_t spx_codec_parse( pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]); static pj_status_t spx_codec_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); static pj_status_t spx_codec_decode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); static pj_status_t spx_codec_recover(pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output); /* Definition for Speex codec operations. */ static pjmedia_codec_op spx_op = { &spx_codec_init, &spx_codec_open, &spx_codec_close, &spx_codec_modify, &spx_codec_parse, &spx_codec_encode, &spx_codec_decode, &spx_codec_recover }; /* Definition for Speex codec factory operations. */ static pjmedia_codec_factory_op spx_factory_op = { &spx_test_alloc, &spx_default_attr, &spx_enum_codecs, &spx_alloc_codec, &spx_dealloc_codec, &pjmedia_codec_speex_deinit }; /* Index to Speex parameter. */ enum { PARAM_NB, /* Index for narrowband parameter. */ PARAM_WB, /* Index for wideband parameter. */ PARAM_UWB, /* Index for ultra-wideband parameter */ }; /* Speex default parameter */ struct speex_param { int enabled; /* Is this mode enabled? */ const SpeexMode *mode; /* Speex mode. */ int pt; /* Payload type. */ unsigned clock_rate; /* Default sampling rate to be used.*/ int quality; /* Default encoder quality. */ int complexity; /* Default encoder complexity. */ int samples_per_frame; /* Samples per frame. */ int framesize; /* Frame size for current mode. */ int bitrate; /* Bit rate for current mode. */ int max_bitrate; /* Max bit rate for current mode. */ }; /* Speex factory */ static struct spx_factory { pjmedia_codec_factory base; pjmedia_endpt *endpt; pj_pool_t *pool; pj_mutex_t *mutex; pjmedia_codec codec_list; struct speex_param speex_param[3]; } spx_factory; /* Speex codec private data. */ struct spx_private { int param_id; /**< Index to speex param. */ void *enc; /**< Encoder state. */ SpeexBits enc_bits; /**< Encoder bits. */ void *dec; /**< Decoder state. */ SpeexBits dec_bits; /**< Decoder bits. */ }; /* * Get codec bitrate and frame size. */ static pj_status_t get_speex_info( struct speex_param *p ) { void *state; int tmp; /* Create temporary encoder */ state = speex_encoder_init(p->mode); if (!state) return PJMEDIA_CODEC_EFAILED; /* Set the quality */ if (p->quality != -1) speex_encoder_ctl(state, SPEEX_SET_QUALITY, &p->quality); /* Sampling rate. */ speex_encoder_ctl(state, SPEEX_SET_SAMPLING_RATE, &p->clock_rate); /* VAD off to have max bitrate */ tmp = 0; speex_encoder_ctl(state, SPEEX_SET_VAD, &tmp); /* Complexity. */ if (p->complexity != -1) speex_encoder_ctl(state, SPEEX_SET_COMPLEXITY, &p->complexity); /* Now get the frame size */ speex_encoder_ctl(state, SPEEX_GET_FRAME_SIZE, &p->samples_per_frame); /* Now get the average bitrate */ speex_encoder_ctl(state, SPEEX_GET_BITRATE, &p->bitrate); /* Calculate framesize. */ p->framesize = p->bitrate * 20 / 1000; /* Now get the maximum bitrate by using maximum quality (=10) */ tmp = 10; speex_encoder_ctl(state, SPEEX_SET_QUALITY, &tmp); speex_encoder_ctl(state, SPEEX_GET_BITRATE, &p->max_bitrate); /* Destroy encoder. */ speex_encoder_destroy(state); return PJ_SUCCESS; } /* * Initialize and register Speex codec factory to pjmedia endpoint. */ PJ_DEF(pj_status_t) pjmedia_codec_speex_init( pjmedia_endpt *endpt, unsigned options, int quality, int complexity ) { pjmedia_codec_mgr *codec_mgr; unsigned i; pj_status_t status; if (spx_factory.pool != NULL) { /* Already initialized. */ return PJ_SUCCESS; } /* Get defaults */ if (quality < 0) quality = PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY; if (complexity < 0) complexity = PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY; /* Validate quality & complexity */ PJ_ASSERT_RETURN(quality >= 0 && quality <= 10, PJ_EINVAL); PJ_ASSERT_RETURN(complexity >= 1 && complexity <= 10, PJ_EINVAL); /* Create Speex codec factory. */ spx_factory.base.op = &spx_factory_op; spx_factory.base.factory_data = NULL; spx_factory.endpt = endpt; spx_factory.pool = pjmedia_endpt_create_pool(endpt, "speex", 4000, 4000); if (!spx_factory.pool) return PJ_ENOMEM; pj_list_init(&spx_factory.codec_list); /* Create mutex. */ status = pj_mutex_create_simple(spx_factory.pool, "speex", &spx_factory.mutex); if (status != PJ_SUCCESS) goto on_error; /* Initialize default Speex parameter. */ spx_factory.speex_param[PARAM_NB].enabled = ((options & PJMEDIA_SPEEX_NO_NB) == 0); spx_factory.speex_param[PARAM_NB].pt = PJMEDIA_RTP_PT_SPEEX_NB; spx_factory.speex_param[PARAM_NB].mode = speex_lib_get_mode(SPEEX_MODEID_NB); spx_factory.speex_param[PARAM_NB].clock_rate = 8000; spx_factory.speex_param[PARAM_NB].quality = quality; spx_factory.speex_param[PARAM_NB].complexity = complexity; spx_factory.speex_param[PARAM_WB].enabled = ((options & PJMEDIA_SPEEX_NO_WB) == 0); spx_factory.speex_param[PARAM_WB].pt = PJMEDIA_RTP_PT_SPEEX_WB; spx_factory.speex_param[PARAM_WB].mode = speex_lib_get_mode(SPEEX_MODEID_WB); spx_factory.speex_param[PARAM_WB].clock_rate = 16000; spx_factory.speex_param[PARAM_WB].quality = quality; spx_factory.speex_param[PARAM_WB].complexity = complexity; spx_factory.speex_param[PARAM_UWB].enabled = ((options & PJMEDIA_SPEEX_NO_UWB) == 0); spx_factory.speex_param[PARAM_UWB].pt = PJMEDIA_RTP_PT_SPEEX_UWB; spx_factory.speex_param[PARAM_UWB].mode = speex_lib_get_mode(SPEEX_MODEID_UWB); spx_factory.speex_param[PARAM_UWB].clock_rate = 32000; spx_factory.speex_param[PARAM_UWB].quality = quality; spx_factory.speex_param[PARAM_UWB].complexity = complexity; /* Somehow quality <=4 is broken in linux. */ if (quality <= 4 && quality >= 0) { PJ_LOG(5,(THIS_FILE, "Adjusting quality to 5 for uwb")); spx_factory.speex_param[PARAM_UWB].quality = 5; } /* Get codec framesize and avg bitrate for each mode. */ for (i=0; i= 0 && quality <= 10, PJ_EINVAL); PJ_ASSERT_RETURN(complexity >= 1 && complexity <= 10, PJ_EINVAL); /* Apply the settings */ for (i=0; i= 0) { PJ_LOG(5,(THIS_FILE, "Adjusting quality to 5 for uwb")); spx_factory.speex_param[PARAM_UWB].quality = 5; } status = get_speex_info(&spx_factory.speex_param[i]); return status; } } return PJ_EINVAL; } /* * Unregister Speex codec factory from pjmedia endpoint and deinitialize * the Speex codec library. */ PJ_DEF(pj_status_t) pjmedia_codec_speex_deinit(void) { pjmedia_codec_mgr *codec_mgr; pj_status_t status; if (spx_factory.pool == NULL) { /* Already deinitialized */ return PJ_SUCCESS; } pj_mutex_lock(spx_factory.mutex); /* We don't want to deinit if there's outstanding codec. */ /* This is silly, as we'll always have codec in the list if we ever allocate a codec! A better behavior maybe is to deallocate all codecs in the list. if (!pj_list_empty(&spx_factory.codec_list)) { pj_mutex_unlock(spx_factory.mutex); return PJ_EBUSY; } */ /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(spx_factory.endpt); if (!codec_mgr) { pj_pool_release(spx_factory.pool); spx_factory.pool = NULL; return PJ_EINVALIDOP; } /* Unregister Speex codec factory. */ status = pjmedia_codec_mgr_unregister_factory(codec_mgr, &spx_factory.base); /* Destroy mutex. */ pj_mutex_unlock(spx_factory.mutex); pj_mutex_destroy(spx_factory.mutex); spx_factory.mutex = NULL; /* Destroy pool. */ pj_pool_release(spx_factory.pool); spx_factory.pool = NULL; return status; } /* * Check if factory can allocate the specified codec. */ static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory, const pjmedia_codec_info *info ) { const pj_str_t speex_tag = { "speex", 5}; unsigned i; PJ_UNUSED_ARG(factory); /* Type MUST be audio. */ if (info->type != PJMEDIA_TYPE_AUDIO) return PJMEDIA_CODEC_EUNSUP; /* Check encoding name. */ if (pj_stricmp(&info->encoding_name, &speex_tag) != 0) return PJMEDIA_CODEC_EUNSUP; /* Check clock-rate */ for (i=0; iclock_rate == spx_factory.speex_param[i].clock_rate) { /* Okay, let's Speex! */ return PJ_SUCCESS; } } /* Unsupported, or mode is disabled. */ return PJMEDIA_CODEC_EUNSUP; } /* * Generate default attribute. */ static pj_status_t spx_default_attr (pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ) { PJ_ASSERT_RETURN(factory==&spx_factory.base, PJ_EINVAL); pj_bzero(attr, sizeof(pjmedia_codec_param)); attr->info.pt = (pj_uint8_t)id->pt; attr->info.channel_cnt = 1; if (id->clock_rate <= 8000) { attr->info.clock_rate = spx_factory.speex_param[PARAM_NB].clock_rate; attr->info.avg_bps = spx_factory.speex_param[PARAM_NB].bitrate; attr->info.max_bps = spx_factory.speex_param[PARAM_NB].max_bitrate; } else if (id->clock_rate <= 16000) { attr->info.clock_rate = spx_factory.speex_param[PARAM_WB].clock_rate; attr->info.avg_bps = spx_factory.speex_param[PARAM_WB].bitrate; attr->info.max_bps = spx_factory.speex_param[PARAM_WB].max_bitrate; } else { /* Wow.. somebody is doing ultra-wideband. Cool...! */ attr->info.clock_rate = spx_factory.speex_param[PARAM_UWB].clock_rate; attr->info.avg_bps = spx_factory.speex_param[PARAM_UWB].bitrate; attr->info.max_bps = spx_factory.speex_param[PARAM_UWB].max_bitrate; } attr->info.pcm_bits_per_sample = 16; attr->info.frm_ptime = 20; attr->info.pt = (pj_uint8_t)id->pt; attr->setting.frm_per_pkt = 1; /* Default flags. */ attr->setting.cng = 1; attr->setting.plc = 1; attr->setting.penh =1 ; attr->setting.vad = 1; return PJ_SUCCESS; } /* * Enum codecs supported by this factory (i.e. only Speex!). */ static pj_status_t spx_enum_codecs(pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]) { unsigned max; int i; /* Must be signed */ PJ_UNUSED_ARG(factory); PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); max = *count; *count = 0; /* * We return three codecs here, and in this order: * - ultra-wideband, wideband, and narrowband. */ for (i=PJ_ARRAY_SIZE(spx_factory.speex_param)-1; i>=0 && *countop = &spx_op; codec->factory = factory; codec->codec_data = pj_pool_alloc(spx_factory.pool, sizeof(struct spx_private)); } pj_mutex_unlock(spx_factory.mutex); spx = (struct spx_private*) codec->codec_data; spx->enc = NULL; spx->dec = NULL; if (id->clock_rate <= 8000) spx->param_id = PARAM_NB; else if (id->clock_rate <= 16000) spx->param_id = PARAM_WB; else spx->param_id = PARAM_UWB; *p_codec = codec; return PJ_SUCCESS; } /* * Free codec. */ static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ) { struct spx_private *spx; PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL); /* Close codec, if it's not closed. */ spx = (struct spx_private*) codec->codec_data; if (spx->enc != NULL || spx->dec != NULL) { spx_codec_close(codec); } /* Put in the free list. */ pj_mutex_lock(spx_factory.mutex); pj_list_push_front(&spx_factory.codec_list, codec); pj_mutex_unlock(spx_factory.mutex); return PJ_SUCCESS; } /* * Init codec. */ static pj_status_t spx_codec_init( pjmedia_codec *codec, pj_pool_t *pool ) { PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(pool); return PJ_SUCCESS; } /* * Open codec. */ static pj_status_t spx_codec_open( pjmedia_codec *codec, pjmedia_codec_param *attr ) { struct spx_private *spx; int id, tmp; spx = (struct spx_private*) codec->codec_data; id = spx->param_id; /* * Create and initialize encoder. */ spx->enc = speex_encoder_init(spx_factory.speex_param[id].mode); if (!spx->enc) return PJMEDIA_CODEC_EFAILED; speex_bits_init(&spx->enc_bits); /* Set the quality*/ if (spx_factory.speex_param[id].quality != -1) { speex_encoder_ctl(spx->enc, SPEEX_SET_QUALITY, &spx_factory.speex_param[id].quality); } /* Sampling rate. */ tmp = attr->info.clock_rate; speex_encoder_ctl(spx->enc, SPEEX_SET_SAMPLING_RATE, &spx_factory.speex_param[id].clock_rate); /* VAD */ tmp = (attr->setting.vad != 0); speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp); speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp); /* Complexity */ if (spx_factory.speex_param[id].complexity != -1) { speex_encoder_ctl(spx->enc, SPEEX_SET_COMPLEXITY, &spx_factory.speex_param[id].complexity); } /* * Create and initialize decoder. */ spx->dec = speex_decoder_init(spx_factory.speex_param[id].mode); if (!spx->dec) { spx_codec_close(codec); return PJMEDIA_CODEC_EFAILED; } speex_bits_init(&spx->dec_bits); /* Sampling rate. */ speex_decoder_ctl(spx->dec, SPEEX_SET_SAMPLING_RATE, &spx_factory.speex_param[id].clock_rate); /* PENH */ tmp = attr->setting.penh; speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp); return PJ_SUCCESS; } /* * Close codec. */ static pj_status_t spx_codec_close( pjmedia_codec *codec ) { struct spx_private *spx; spx = (struct spx_private*) codec->codec_data; /* Destroy encoder*/ if (spx->enc) { speex_encoder_destroy( spx->enc ); spx->enc = NULL; speex_bits_destroy( &spx->enc_bits ); } /* Destroy decoder */ if (spx->dec) { speex_decoder_destroy( spx->dec); spx->dec = NULL; speex_bits_destroy( &spx->dec_bits ); } return PJ_SUCCESS; } /* * Modify codec settings. */ static pj_status_t spx_codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr ) { struct spx_private *spx; int tmp; spx = (struct spx_private*) codec->codec_data; /* VAD */ tmp = (attr->setting.vad != 0); speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp); speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp); /* PENH */ tmp = attr->setting.penh; speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp); return PJ_SUCCESS; } #if 0 # define TRACE__(args) PJ_LOG(5,args) #else # define TRACE__(args) #endif #undef THIS_FUNC #define THIS_FUNC "speex_get_next_frame" #define NB_SUBMODES 16 #define NB_SUBMODE_BITS 4 #define SB_SUBMODES 8 #define SB_SUBMODE_BITS 3 /* This function will iterate frames & submodes in the Speex bits. * Returns 0 if a frame found, otherwise returns -1. */ static int speex_get_next_frame(SpeexBits *bits) { static const int inband_skip_table[NB_SUBMODES] = {1, 1, 4, 4, 4, 4, 4, 4, 8, 8, 16, 16, 32, 32, 64, 64 }; static const int wb_skip_table[SB_SUBMODES] = {SB_SUBMODE_BITS+1, 36, 112, 192, 352, -1, -1, -1}; unsigned submode; unsigned nb_count = 0; while (speex_bits_remaining(bits) >= 5) { unsigned wb_count = 0; unsigned bit_ptr = bits->bitPtr; unsigned char_ptr = bits->charPtr; /* WB frame */ while ((speex_bits_remaining(bits) >= 4) && speex_bits_unpack_unsigned(bits, 1)) { int advance; submode = speex_bits_unpack_unsigned(bits, 3); advance = wb_skip_table[submode]; if (advance < 0) { TRACE__((THIS_FUNC, "Invalid mode encountered. " "The stream is corrupted.")); return -1; } TRACE__((THIS_FUNC, "WB layer skipped: %d bits", advance)); advance -= (SB_SUBMODE_BITS+1); speex_bits_advance(bits, advance); bit_ptr = bits->bitPtr; char_ptr = bits->charPtr; /* Consecutive subband frames may not exceed 2 frames */ if (++wb_count > 2) return -1; } /* End of bits, return the frame */ if (speex_bits_remaining(bits) < 4) { TRACE__((THIS_FUNC, "End of stream")); return 0; } /* Stop iteration, return the frame */ if (nb_count > 0) { bits->bitPtr = bit_ptr; bits->charPtr = char_ptr; return 0; } /* Get control bits */ submode = speex_bits_unpack_unsigned(bits, 4); TRACE__((THIS_FUNC, "Control bits: %d at %d", submode, bits->charPtr*8+bits->bitPtr)); if (submode == 15) { TRACE__((THIS_FUNC, "Found submode: terminator")); return -1; } else if (submode == 14) { /* in-band signal; next 4 bits contain signal id */ submode = speex_bits_unpack_unsigned(bits, 4); TRACE__((THIS_FUNC, "Found submode: in-band %d bits", inband_skip_table[submode])); speex_bits_advance(bits, inband_skip_table[submode]); } else if (submode == 13) { /* user in-band; next 5 bits contain msg len */ submode = speex_bits_unpack_unsigned(bits, 5); TRACE__((THIS_FUNC, "Found submode: user-band %d bytes", submode)); speex_bits_advance(bits, submode * 8); } else if (submode > 8) { TRACE__((THIS_FUNC, "Unknown sub-mode %d", submode)); return -1; } else { /* NB frame */ int advance = submode; speex_mode_query(&speex_nb_mode, SPEEX_SUBMODE_BITS_PER_FRAME, &advance); if (advance < 0) { TRACE__((THIS_FUNC, "Invalid mode encountered. " "The stream is corrupted.")); return -1; } TRACE__((THIS_FUNC, "Submode %d: %d bits", submode, advance)); advance -= (NB_SUBMODE_BITS+1); speex_bits_advance(bits, advance); ++nb_count; } } return 0; } /* * Get frames in the packet. */ static pj_status_t spx_codec_parse( pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]) { struct spx_private *spx = (struct spx_private*) codec->codec_data; unsigned samples_per_frame; unsigned count = 0; int char_ptr = 0; int bit_ptr = 0; samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame; /* Copy the data into the speex bit-stream */ speex_bits_read_from(&spx->dec_bits, (char*)pkt, (int)pkt_size); while (speex_get_next_frame(&spx->dec_bits) == 0 && spx->dec_bits.charPtr != char_ptr) { frames[count].buf = (char*)pkt + char_ptr; /* Bit info contains start bit offset of the frame */ frames[count].bit_info = bit_ptr; frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO; frames[count].timestamp.u64 = ts->u64 + count * samples_per_frame; frames[count].size = spx->dec_bits.charPtr - char_ptr; if (spx->dec_bits.bitPtr) ++frames[count].size; bit_ptr = spx->dec_bits.bitPtr; char_ptr = spx->dec_bits.charPtr; ++count; } *frame_cnt = count; return PJ_SUCCESS; } /* * Encode frames. */ static pj_status_t spx_codec_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { struct spx_private *spx; unsigned samples_per_frame; int tx = 0; spx_int16_t *pcm_in = (spx_int16_t*)input->buf; pj_size_t nsamples; spx = (struct spx_private*) codec->codec_data; if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) { output->size = 0; output->buf = NULL; output->timestamp = input->timestamp; output->type = input->type; return PJ_SUCCESS; } nsamples = input->size >> 1; samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame; PJ_ASSERT_RETURN(nsamples % samples_per_frame == 0, PJMEDIA_CODEC_EPCMFRMINLEN); /* Flush all the bits in the struct so we can encode a new frame */ speex_bits_reset(&spx->enc_bits); /* Encode the frames */ while (nsamples >= samples_per_frame) { tx += speex_encode_int(spx->enc, pcm_in, &spx->enc_bits); pcm_in += samples_per_frame; nsamples -= samples_per_frame; } /* Check if we need not to transmit the frame (DTX) */ if (tx == 0) { output->buf = NULL; output->size = 0; output->timestamp.u64 = input->timestamp.u64; output->type = PJMEDIA_FRAME_TYPE_NONE; return PJ_SUCCESS; } /* Check size. */ pj_assert(speex_bits_nbytes(&spx->enc_bits) <= (int)output_buf_len); /* Copy the bits to an array of char that can be written */ output->size = speex_bits_write(&spx->enc_bits, (char*)output->buf, output_buf_len); output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->timestamp = input->timestamp; return PJ_SUCCESS; } /* * Decode frame. */ static pj_status_t spx_codec_decode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { struct spx_private *spx; unsigned samples_per_frame; spx = (struct spx_private*) codec->codec_data; samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame; PJ_ASSERT_RETURN(output_buf_len >= samples_per_frame << 1, PJMEDIA_CODEC_EPCMTOOSHORT); if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) { pjmedia_zero_samples((pj_int16_t*)output->buf, samples_per_frame); output->size = samples_per_frame << 1; output->timestamp.u64 = input->timestamp.u64; output->type = PJMEDIA_FRAME_TYPE_AUDIO; return PJ_SUCCESS; } /* Copy the data into the bit-stream struct */ speex_bits_read_from(&spx->dec_bits, (char*)input->buf, (int)input->size); /* Set Speex dec_bits pointer to the start bit of the frame */ speex_bits_advance(&spx->dec_bits, input->bit_info); /* Decode the data */ speex_decode_int(spx->dec, &spx->dec_bits, (spx_int16_t*)output->buf); output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->size = samples_per_frame << 1; output->timestamp.u64 = input->timestamp.u64; return PJ_SUCCESS; } /* * Recover lost frame. */ static pj_status_t spx_codec_recover(pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output) { struct spx_private *spx; unsigned count; /* output_buf_len is unreferenced when building in Release mode */ PJ_UNUSED_ARG(output_buf_len); spx = (struct spx_private*) codec->codec_data; count = spx_factory.speex_param[spx->param_id].clock_rate * 20 / 1000; pj_assert(count <= output_buf_len/2); /* Recover packet loss */ speex_decode_int(spx->dec, NULL, (spx_int16_t*) output->buf); output->size = count * 2; return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_SPEEX_CODEC */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-codec/vpx.c ================================================ /** * Copyright (C) 2015 AG Projects * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr) * This file is part of pjsip_android. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #if defined(PJ_WIN32) && PJ_WIN32!=0 # include #elif defined(PJ_DARWINOS) && PJ_DARWINOS!=0 # include # include #elif defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H!=0 # include #else # warning "Cannot find a way to guess the number of cores on this platform." #endif #if defined(PJMEDIA_HAS_VPX_CODEC) && \ PJMEDIA_HAS_VPX_CODEC != 0 && \ defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #define THIS_FILE "vpx.c" #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 #define DEFAULT_FPS 15 #define DEFAULT_AVG_BITRATE 256000 #define DEFAULT_MAX_BITRATE 256000 #define VPX_CODEC_DISABLE_COMPAT 1 #include #include #include #include #if 1 # define TRACE_(x) PJ_LOG(4, x) #else # define TRACE_(x) #endif /* Prototypes for LibVPX codecs factory */ static pj_status_t pj_vpx_test_alloc(pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *id); static pj_status_t pj_vpx_default_attr(pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info, pjmedia_vid_codec_param *attr); static pj_status_t pj_vpx_enum_codecs(pjmedia_vid_codec_factory *factory, unsigned *count, pjmedia_vid_codec_info codecs[]); static pj_status_t pj_vpx_alloc_codec(pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info, pjmedia_vid_codec **p_codec); static pj_status_t pj_vpx_dealloc_codec(pjmedia_vid_codec_factory *factory, pjmedia_vid_codec *codec); /* Prototypes for VPX codec implementation. */ static pj_status_t pj_vpx_codec_init(pjmedia_vid_codec *codec, pj_pool_t *pool); static pj_status_t pj_vpx_codec_open(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *attr); static pj_status_t pj_vpx_codec_close(pjmedia_vid_codec *codec); static pj_status_t pj_vpx_codec_modify(pjmedia_vid_codec *codec, const pjmedia_vid_codec_param *attr); static pj_status_t pj_vpx_codec_get_param(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *param); static pj_status_t pj_vpx_codec_encode_begin(pjmedia_vid_codec *codec, const pjmedia_vid_encode_opt *opt, const pjmedia_frame *input, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more); static pj_status_t pj_vpx_codec_encode_more(pjmedia_vid_codec *codec, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more); static pj_status_t pj_vpx_codec_decode(pjmedia_vid_codec *codec, pj_size_t pkt_count, pjmedia_frame packets[], unsigned out_size, pjmedia_frame *output); /* Definition for VPX codec operations. */ static pjmedia_vid_codec_op vpx_op = { &pj_vpx_codec_init, &pj_vpx_codec_open, &pj_vpx_codec_close, &pj_vpx_codec_modify, &pj_vpx_codec_get_param, &pj_vpx_codec_encode_begin, &pj_vpx_codec_encode_more, &pj_vpx_codec_decode, NULL }; /* Definition for VPX factory operations. */ static pjmedia_vid_codec_factory_op vpx_factory_op = { &pj_vpx_test_alloc, &pj_vpx_default_attr, &pj_vpx_enum_codecs, &pj_vpx_alloc_codec, &pj_vpx_dealloc_codec }; /* VPX codec factory */ static struct vpx_factory { pjmedia_vid_codec_factory base; pjmedia_vid_codec_mgr *mgr; pj_pool_factory *pf; pj_pool_t *pool; pj_mutex_t *mutex; } vpx_factory; typedef struct vpx_codec_desc vpx_codec_desc; /* VPX codec private data. */ typedef struct vpx_private { const vpx_codec_desc *desc; pjmedia_vid_codec_param param; /**< Codec param */ pj_pool_t *pool; /**< Pool for each instance */ /* Format info and apply format param */ const pjmedia_video_format_info *enc_vfi; pjmedia_video_apply_fmt_param enc_vafp; const pjmedia_video_format_info *dec_vfi; pjmedia_video_apply_fmt_param dec_vafp; /* The vpx encoder. */ struct vpx_codec_ctx encoder; struct vpx_image rawimg; void *enc_buf; unsigned enc_buf_size; pj_bool_t enc_buf_is_keyframe; unsigned enc_frame_len; unsigned enc_processed; vpx_codec_iter_t enc_iter; int rc_max_intra_target; /* The vpx decoder */ struct vpx_codec_ctx decoder; void *dec_buf; unsigned dec_buf_size; unsigned dec_frame_len; pj_bool_t dec_stream_info_init; pj_timestamp last_dec_keyframe_ts; } vpx_private; /* Number of threads to use, depending on resolution and number of CPUS. * Borrowed from WebRTC. */ unsigned int number_of_threads(int width, int height, int cpus) { #if 1 int c = width * height; if (c >= 1920 * 1080 && cpus > 8) return 8; else if (c > 1280 * 960 && cpus >=6) return 3; else if (c > 640 * 480 && cpus >= 3) return 2; else return 1; #else return cpus - 1; #endif } unsigned int number_of_cores(void) { static unsigned int ncores; if (!ncores) { #if defined(PJ_WIN32) && PJ_WIN32!=0 SYSTEM_INFO si; GetSystemInfo(&si); ncores = si.dwNumberOfProcessors; #elif defined(PJ_DARWINOS) && PJ_DARWINOS!=0 int name[] = {CTL_HW, HW_AVAILCPU}; int ncpu; size_t size = sizeof(ncpu); if (0 == sysctl(name, 2, &ncpu, &size, NULL, 0)) { ncores = ncpu; } else { PJ_LOG(4, (THIS_FILE, "Failed to detect number of CPU cores")); ncores = 1; } #elif defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H!=0 ncores = sysconf(_SC_NPROCESSORS_ONLN); #else ncores = 1; #endif } return ncores; } /* * Initialize and register VPX codec factory to pjmedia endpoint. */ PJ_DEF(pj_status_t) pjmedia_codec_vpx_init(pjmedia_vid_codec_mgr *mgr, pj_pool_factory *pf) { pj_pool_t *pool; pj_status_t status; TRACE_((THIS_FILE, "Init vpx codec")); if (vpx_factory.pool != NULL ) { /* Already initialized. */ return PJ_SUCCESS; } if (!mgr) mgr = pjmedia_vid_codec_mgr_instance(); PJ_ASSERT_RETURN(mgr, PJ_EINVAL); /* Create VPX codec factory. */ vpx_factory.base.op = &vpx_factory_op; vpx_factory.base.factory_data = NULL; vpx_factory.mgr = mgr; vpx_factory.pf = pf; pool = pj_pool_create(pf, "vpx codec factory", 256, 256, NULL ); if (!pool) return PJ_ENOMEM; /* Create mutex. */ status = pj_mutex_create_simple(pool, "vpx codec factory", &vpx_factory.mutex); if (status != PJ_SUCCESS) goto on_error; /* Register codec factory to codec manager. */ status = pjmedia_vid_codec_mgr_register_factory(mgr, &vpx_factory.base); if (status != PJ_SUCCESS) goto on_error; vpx_factory.pool = pool; /* Done. */ return PJ_SUCCESS; on_error: pj_pool_release(pool); return status; } /* * Unregister VPX factory from pjmedia endpoint. */ PJ_DEF(pj_status_t) pjmedia_codec_vpx_deinit(void) { pj_status_t status = PJ_SUCCESS; TRACE_((THIS_FILE, "Deinit vpx codec")); if (vpx_factory.pool == NULL ) { /* Already deinitialized */ return PJ_SUCCESS; } pj_mutex_lock(vpx_factory.mutex); /* Unregister VPX codecs factory. */ status = pjmedia_vid_codec_mgr_unregister_factory(vpx_factory.mgr, &vpx_factory.base); /* Destroy mutex. */ pj_mutex_destroy(vpx_factory.mutex); /* Destroy pool. */ pj_pool_release(vpx_factory.pool); vpx_factory.pool = NULL; return status; } /* * Check if factory can allocate the specified codec. */ static pj_status_t pj_vpx_test_alloc(pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info) { const pj_str_t vpx_tag = { "VP8", 3}; PJ_ASSERT_RETURN(factory==&vpx_factory.base, PJ_EINVAL); PJ_ASSERT_RETURN(info, PJ_EINVAL); /* Check encoding name. */ if (pj_stricmp(&info->encoding_name, &vpx_tag) != 0) return PJMEDIA_CODEC_EUNSUP; return PJ_SUCCESS; } /* * Generate default attribute. */ static pj_status_t pj_vpx_default_attr(pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info, pjmedia_vid_codec_param *attr) { PJ_ASSERT_RETURN(factory==&vpx_factory.base, PJ_EINVAL); PJ_ASSERT_RETURN(info && attr, PJ_EINVAL); TRACE_((THIS_FILE, "vpx default attr")); pj_bzero(attr, sizeof(pjmedia_vid_codec_param)); /* Scan the requested packings and use the lowest number */ attr->packing = 1; /* Direction */ attr->dir = PJMEDIA_DIR_ENCODING_DECODING; /* Encoded format */ pjmedia_format_init_video(&attr->enc_fmt, PJMEDIA_FORMAT_VP8, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FPS, 1); /* Decoded format */ pjmedia_format_init_video(&attr->dec_fmt, PJMEDIA_FORMAT_I420, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FPS, 1); /* Decoding fmtp */ attr->dec_fmtp.cnt = 0; /* Bitrate */ attr->enc_fmt.det.vid.avg_bps = DEFAULT_AVG_BITRATE; attr->enc_fmt.det.vid.max_bps = DEFAULT_MAX_BITRATE; attr->enc_fmtp.cnt = 0; /* Encoding MTU */ attr->enc_mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE; return PJ_SUCCESS; } /* * Enum codecs supported by this factory. */ static pj_status_t pj_vpx_enum_codecs(pjmedia_vid_codec_factory *factory, unsigned *count, pjmedia_vid_codec_info info[]) { PJ_ASSERT_RETURN(info && *count > 0, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &vpx_factory.base, PJ_EINVAL); TRACE_((THIS_FILE, "Enum codecs...")); *count = 1; info->fmt_id = PJMEDIA_FORMAT_VP8; info->pt = PJMEDIA_RTP_PT_VP8; info->encoding_name = pj_str("VP8"); info->encoding_desc = pj_str("WebM Project VP8 Encoder"); info->dir = PJMEDIA_DIR_ENCODING_DECODING; info->clock_rate = 90000; info->dec_fmt_id_cnt = 1; info->dec_fmt_id[0] = PJMEDIA_FORMAT_I420; info->packings = PJMEDIA_VID_PACKING_PACKETS; info->fps_cnt = 4; info->fps[0].num = 15; info->fps[0].denum = 1; info->fps[1].num = 20; info->fps[1].denum = 1; info->fps[2].num = 25; info->fps[2].denum = 1; info->fps[3].num = 30; info->fps[3].denum = 1; return PJ_SUCCESS; } /* * Allocate a new codec instance. */ static pj_status_t pj_vpx_alloc_codec(pjmedia_vid_codec_factory *factory, const pjmedia_vid_codec_info *info, pjmedia_vid_codec **p_codec) { vpx_private *vpx; pjmedia_vid_codec *codec; pj_pool_t *pool = NULL; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(factory && info && p_codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &vpx_factory.base, PJ_EINVAL); TRACE_((THIS_FILE, "vpx pj_vpx_alloc_codec")); if (info->pt != PJMEDIA_RTP_PT_VP8) { return PJMEDIA_CODEC_EUNSUP; } /* Create pool for codec instance */ pool = pj_pool_create(vpx_factory.pf, "vp8 codec", 512, 512, NULL ); codec = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec); if (!codec) { status = PJ_ENOMEM; goto on_error; } codec->op = &vpx_op; codec->factory = factory; vpx = PJ_POOL_ZALLOC_T(pool, vpx_private); if (!vpx) { status = PJ_ENOMEM; goto on_error; } codec->codec_data = vpx; vpx->pool = pool; *p_codec = codec; return PJ_SUCCESS; on_error: if (pool) pj_pool_release(pool); return status; } /* * Free codec. */ static pj_status_t pj_vpx_dealloc_codec(pjmedia_vid_codec_factory *factory, pjmedia_vid_codec *codec) { vpx_private *vpx; pj_pool_t *pool; PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &vpx_factory.base, PJ_EINVAL); TRACE_((THIS_FILE, "vpx pj_vpx_dealloc_codec")); /* Close codec, if it's not closed. */ vpx = (vpx_private*) codec->codec_data; pool = vpx->pool; codec->codec_data = NULL; pj_pool_release(pool); return PJ_SUCCESS; } /* * Init codec. */ static pj_status_t pj_vpx_codec_init(pjmedia_vid_codec *codec, pj_pool_t *pool) { PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(pool); TRACE_((THIS_FILE, "vpx pj_vpx_codec_init")); return PJ_SUCCESS; } static pj_status_t pj_vpx_encoder_open(vpx_private *vpx) { vpx_codec_flags_t flags = 0; /* XXX: use VPX_CODEC_USE_OUTPUT_PARTITION ? */ const struct vpx_codec_iface *iface = &vpx_codec_vp8_cx_algo; struct vpx_codec_enc_cfg enccfg; int res; TRACE_((THIS_FILE, "vpx pj_vpx_encoder_open")); res = vpx_codec_enc_config_default(iface, &enccfg, 0); if (res != VPX_CODEC_OK) { PJ_LOG(1, (THIS_FILE, "Failed to get vpx default config : %s", vpx_codec_err_to_string(res))); return PJMEDIA_CODEC_EFAILED; } enccfg.g_w = vpx->param.enc_fmt.det.vid.size.w; enccfg.g_h = vpx->param.enc_fmt.det.vid.size.h; enccfg.g_timebase.num = vpx->param.enc_fmt.det.vid.fps.num; enccfg.g_timebase.den = vpx->param.enc_fmt.det.vid.fps.denum; //provide dummy value to initialize wrapper, values will be updated each _encode() vpx_img_wrap(&vpx->rawimg, VPX_IMG_FMT_I420, vpx->param.enc_fmt.det.vid.size.w, vpx->param.enc_fmt.det.vid.size.h, 1, NULL); enccfg.g_threads = number_of_threads(enccfg.g_w, enccfg.g_h, number_of_cores()); PJ_LOG(4, (THIS_FILE, "Using %d threads for VPX encoding", enccfg.g_threads)); enccfg.g_lag_in_frames = 0; enccfg.g_pass = VPX_RC_ONE_PASS; enccfg.rc_end_usage = VPX_CBR; enccfg.rc_target_bitrate = vpx->param.enc_fmt.det.vid.avg_bps / 1000; // in kbit/s enccfg.g_timebase.num = 1; enccfg.g_timebase.den = 90000; enccfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT; enccfg.rc_resize_allowed = 1; enccfg.rc_min_quantizer = 2; enccfg.rc_max_quantizer = 56; enccfg.rc_undershoot_pct = 100; enccfg.rc_overshoot_pct = 15; enccfg.rc_buf_initial_sz = 500; enccfg.rc_buf_optimal_sz = 600; enccfg.rc_buf_sz = 1000; enccfg.kf_mode = VPX_KF_AUTO; enccfg.kf_max_dist = 3000; vpx->rc_max_intra_target = PJ_MAX(300, enccfg.rc_buf_sz * 0.5 * enccfg.g_timebase.num / 10); res = vpx_codec_enc_init(&vpx->encoder, vpx_codec_vp8_cx(), &enccfg, flags); if (res != VPX_CODEC_OK) { PJ_LOG(1, (THIS_FILE, "Failed to init vpx encoder : %s", vpx_codec_err_to_string(res))); return PJMEDIA_CODEC_EFAILED; } vpx_codec_control(&vpx->encoder, VP8E_SET_STATIC_THRESHOLD, 1); vpx_codec_control(&vpx->encoder, VP8E_SET_CPUUSED, -6); // XXX: test vpx_codec_control(&vpx->encoder, VP8E_SET_TOKEN_PARTITIONS, VP8_ONE_TOKENPARTITION); vpx_codec_control(&vpx->encoder, VP8E_SET_MAX_INTRA_BITRATE_PCT, vpx->rc_max_intra_target); #ifdef VP8E_SET_SCREEN_CONTENT_MODE vpx_codec_control(&vpx->encoder, VP8E_SET_SCREEN_CONTENT_MODE, 0); #endif vpx->enc_iter = NULL; vpx->enc_buf_size = vpx->enc_vafp.framebytes; vpx->enc_buf = pj_pool_alloc(vpx->pool, vpx->enc_buf_size); vpx->dec_buf_size = vpx->dec_vafp.framebytes; vpx->dec_buf = pj_pool_alloc(vpx->pool, vpx->dec_buf_size); return PJ_SUCCESS; } static pj_status_t pj_vpx_decoder_open(vpx_private *vpx) { vpx_codec_flags_t flags = 0; vpx_codec_dec_cfg_t cfg; int res; cfg.threads = 1; cfg.h = 0; cfg.w = 0; res = vpx_codec_dec_init(&vpx->decoder, vpx_codec_vp8_dx(), &cfg, flags); if (res != VPX_CODEC_OK) { PJ_LOG(1, (THIS_FILE, "Failed to init vpx decoder : %s", vpx_codec_err_to_string(res))); return PJ_ENOMEM; } return PJ_SUCCESS; } /* * Open codec. */ static pj_status_t pj_vpx_codec_open(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *attr) { vpx_private *vpx; pj_status_t status; PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL); vpx = (vpx_private*) codec->codec_data; pj_memcpy(&vpx->param, attr, sizeof(*attr)); /* Normalize encoding MTU in codec param */ if (attr->enc_mtu > PJMEDIA_MAX_VID_PAYLOAD_SIZE) { attr->enc_mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE; } /* Init format info and apply-param of decoder */ vpx->dec_vfi = pjmedia_get_video_format_info(NULL, vpx->param.dec_fmt.id); if (!vpx->dec_vfi) { status = PJ_EINVAL; goto on_error; } pj_bzero(&vpx->dec_vafp, sizeof(vpx->dec_vafp)); vpx->dec_vafp.size = vpx->param.dec_fmt.det.vid.size; vpx->dec_vafp.buffer = NULL; status = (*vpx->dec_vfi->apply_fmt)(vpx->dec_vfi, &vpx->dec_vafp); if (status != PJ_SUCCESS) { goto on_error; } /* Init format info and apply-param of encoder */ vpx->enc_vfi = pjmedia_get_video_format_info(NULL, vpx->param.dec_fmt.id); if (!vpx->enc_vfi) { status = PJ_EINVAL; goto on_error; } pj_bzero(&vpx->enc_vafp, sizeof(vpx->enc_vafp)); vpx->enc_vafp.size = vpx->param.enc_fmt.det.vid.size; vpx->enc_vafp.buffer = NULL; status = (*vpx->enc_vfi->apply_fmt)(vpx->enc_vfi, &vpx->enc_vafp); if (status != PJ_SUCCESS) { goto on_error; } /* Open the encoder */ TRACE_((THIS_FILE, "Open vpx version : %s build : %s", vpx_codec_version_str(), vpx_codec_build_config())); if (vpx->param.dir & PJMEDIA_DIR_ENCODING) { status = pj_vpx_encoder_open(vpx); if (status != PJ_SUCCESS) { goto on_error; } } if (vpx->param.dir & PJMEDIA_DIR_DECODING) { status = pj_vpx_decoder_open(vpx); if (status != PJ_SUCCESS) { goto on_error; } } /* Update codec attributes, e.g: encoding format may be changed by * SDP fmtp negotiation. */ pj_memcpy(attr, &vpx->param, sizeof(*attr)); return PJ_SUCCESS; on_error: pj_vpx_codec_close(codec); return status; } /* * Close codec. */ static pj_status_t pj_vpx_codec_close(pjmedia_vid_codec *codec) { vpx_private *vpx; PJ_ASSERT_RETURN(codec, PJ_EINVAL); vpx = (vpx_private*) codec->codec_data; vpx_codec_destroy(&vpx->decoder); vpx_codec_destroy(&vpx->encoder); vpx_img_free(&vpx->rawimg); return PJ_SUCCESS; } /* * Modify codec settings. */ static pj_status_t pj_vpx_codec_modify(pjmedia_vid_codec *codec, const pjmedia_vid_codec_param *attr) { vpx_private *vpx = (vpx_private*) codec->codec_data; // TODO : add bitrate change support here PJ_UNUSED_ARG(attr); PJ_UNUSED_ARG(vpx); return PJ_ENOTSUP; } static pj_status_t pj_vpx_codec_get_param(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *param) { vpx_private *vpx; PJ_ASSERT_RETURN(codec && param, PJ_EINVAL); vpx = (vpx_private*) codec->codec_data; pj_memcpy(param, &vpx->param, sizeof(*param)); return PJ_SUCCESS; } /* * Encode frames. */ static pj_status_t pj_vpx_codec_encode_begin(pjmedia_vid_codec *codec, const pjmedia_vid_encode_opt *opt, const pjmedia_frame *input, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more) { vpx_private *vpx = (vpx_private*) codec->codec_data; vpx_image_t *rawimg; vpx_enc_frame_flags_t flags = 0; pj_uint8_t *p; int i, res; PJ_ASSERT_RETURN(codec && input, PJ_EINVAL); p = (pj_uint8_t*) input->buf; *has_more = PJ_FALSE; rawimg = &vpx->rawimg; if(input->size < vpx->enc_vafp.framebytes){ PJ_LOG(1, (THIS_FILE, "Frame provided is too small !")); return PJ_ETOOSMALL; } for (i = 0; i < vpx->enc_vfi->plane_cnt; ++i) { rawimg->planes[i] = p; rawimg->stride[i] = vpx->enc_vafp.strides[i]; p += vpx->enc_vafp.plane_bytes[i]; } if (opt && opt->force_keyframe) { flags |= VPX_EFLAG_FORCE_KF; vpx_codec_control(&vpx->encoder, VP8E_SET_MAX_INTRA_BITRATE_PCT, vpx->rc_max_intra_target); } res = vpx_codec_encode(&vpx->encoder, rawimg, input->timestamp.u64, 1, flags, VPX_DL_REALTIME); if (res != VPX_CODEC_OK) { PJ_LOG(1, (THIS_FILE, "Failed to encode : %s %s", vpx_codec_err_to_string(res), vpx->encoder.err_detail)); return PJMEDIA_CODEC_EFAILED; } vpx->enc_iter = NULL; vpx->enc_frame_len = 0; vpx->enc_processed = 0; return pj_vpx_codec_encode_more(codec, out_size, output, has_more); } static pj_status_t pj_vpx_codec_encode_more(pjmedia_vid_codec *codec, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more) { vpx_private *vpx = (vpx_private*) codec->codec_data; const vpx_codec_cx_pkt_t *pkt; /* Default return */ *has_more = PJ_FALSE; output->size = 0; output->type = PJMEDIA_FRAME_TYPE_NONE; if (vpx->enc_frame_len == 0) { /* * For now we assume that we have only one cx data here * Which is probably fine as we do not ask encoder to bufferize */ //PJ_LOG(4, (THIS_FILE, "Encode one frame at %p", vpx->enc_iter)); pkt = vpx_codec_get_cx_data(&vpx->encoder, &vpx->enc_iter); if (pkt == NULL ) { if (!vpx->encoder.err) { PJ_LOG(3, (THIS_FILE, "Encoder packet dropped")); return PJ_SUCCESS; } else { PJ_LOG(1, (THIS_FILE, "Failed to get cx datas : %s", vpx_codec_err_to_string(vpx->encoder.err))); return PJMEDIA_CODEC_EFAILED; } } else if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { pj_memcpy(vpx->enc_buf, pkt->data.frame.buf, pkt->data.frame.sz); vpx->enc_frame_len = pkt->data.frame.sz; vpx->enc_processed = 0; vpx->enc_buf_is_keyframe = !!(pkt->data.frame.flags & VPX_FRAME_IS_KEY) ? PJ_TRUE : PJ_FALSE; //PJ_LOG(4, (THIS_FILE, "Encoded with 0 byte : %d", ((pj_uint8_t*)(vpx->enc_buf))[0])); } else { PJ_LOG(6, (THIS_FILE, "Vpx packet kind %d not taken into account", pkt->kind)); return PJ_SUCCESS; } } // TODO we should support if iter not over too if(vpx->enc_frame_len > 0) { //PJ_LOG(4, (THIS_FILE, "We have an enc_frame : %d; max : %d", vpx->enc_frame_len, vpx->param.enc_mtu)); /* Reserve 1 octet for vp8 packetization info */ unsigned max_size = vpx->param.enc_mtu - 1; unsigned remaining_size = vpx->enc_frame_len - vpx->enc_processed; /* TODO : we could equally distributed packets sizes */ unsigned payload_len = PJ_MIN(remaining_size, max_size); pj_uint8_t* p = (pj_uint8_t*) output->buf; pj_uint8_t* s = (pj_uint8_t*) vpx->enc_buf; //PJ_LOG(4, (THIS_FILE, "Payload : %d", payload_len)); output->type = PJMEDIA_FRAME_TYPE_VIDEO; output->bit_info = 0; if (vpx->enc_buf_is_keyframe) { output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; } /* Set vp8 packetization info */ p[0] = 0; if(vpx->enc_processed == 0) p[0] |= 0x10; if(!vpx->enc_buf_is_keyframe) p[0] |= 0x20; pj_memcpy( (p + 1), (s + vpx->enc_processed), payload_len); output->size = payload_len + 1; vpx->enc_processed += payload_len; *has_more = !(vpx->enc_processed == vpx->enc_frame_len); } //PJ_LOG(4, (THIS_FILE, "Encoded size %d", output->size)); return PJ_SUCCESS; } /* * Decode frame. */ static pj_status_t check_decode_result(pjmedia_vid_codec *codec, const vpx_image_t *img, const pj_timestamp *ts) { vpx_private *vpx = (vpx_private*) codec->codec_data; pjmedia_video_apply_fmt_param *vafp = &vpx->dec_vafp; pjmedia_event event; int res, reference_updates = 0; /* Check for format change. */ if (img->d_w != (int) vafp->size.w || img->d_h != (int) vafp->size.h) { pj_status_t status; /* Update decoder format in param */ vpx->param.dec_fmt.det.vid.size.w = img->d_w; vpx->param.dec_fmt.det.vid.size.h = img->d_h; /* Re-init format info and apply-param of decoder */ vpx->dec_vfi = pjmedia_get_video_format_info(NULL, vpx->param.dec_fmt.id); if (!vpx->dec_vfi) return PJ_ENOTSUP; pj_bzero(&vpx->dec_vafp, sizeof(vpx->dec_vafp)); vpx->dec_vafp.size = vpx->param.dec_fmt.det.vid.size; vpx->dec_vafp.buffer = NULL; status = (*vpx->dec_vfi->apply_fmt)(vpx->dec_vfi, &vpx->dec_vafp); if (status != PJ_SUCCESS) return status; /* Realloc buffer if necessary */ if (vpx->dec_vafp.framebytes > vpx->dec_buf_size) { PJ_LOG(5,(THIS_FILE, "Reallocating decoding buffer %u --> %u", (unsigned)vpx->dec_buf_size, (unsigned)vpx->dec_vafp.framebytes)); vpx->dec_buf_size = (unsigned)vpx->dec_vafp.framebytes; vpx->dec_buf = pj_pool_alloc(vpx->pool, vpx->dec_buf_size); } /* Broadcast format changed event */ pjmedia_event_init(&event, PJMEDIA_EVENT_FMT_CHANGED, ts, codec); event.data.fmt_changed.dir = PJMEDIA_DIR_DECODING; pj_memcpy(&event.data.fmt_changed.new_fmt, &vpx->param.dec_fmt, sizeof(vpx->param.dec_fmt)); pjmedia_event_publish(NULL, codec, &event, 0); } /* Check for found keyframe */ res = vpx_codec_control(&vpx->decoder, VP8D_GET_LAST_REF_UPDATES, &reference_updates); if (res == VPX_CODEC_OK) { pj_bool_t got_keyframe = (reference_updates & VP8_GOLD_FRAME); if (got_keyframe) { pj_get_timestamp(&vpx->last_dec_keyframe_ts); /* Broadcast keyframe event */ pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_FOUND, ts, codec); pjmedia_event_publish(NULL, codec, &event, 0); } } return PJ_SUCCESS; } static pj_status_t pj_vpx_codec_decode_whole(pjmedia_vid_codec *codec, const vpx_image_t *img, const pj_timestamp *ts, unsigned output_buf_len, pjmedia_frame *output) { pj_status_t status; int half_width = (img->d_w + 1) >> 1; int half_height = (img->d_h + 1) >> 1; uint8_t* buf; uint32_t pos = 0; uint32_t plane, y; uint8_t* buffer = output->buf; int buffer_size = img->d_w * img->d_h + half_width * half_height * 2; output->type = PJMEDIA_FRAME_TYPE_NONE; output->size = 0; /* Check decoding result, e.g: see if the format got changed, * keyframe found/missing. */ status = check_decode_result(codec, img, ts); if (status != PJ_SUCCESS) return status; /* Reset output frame bit info */ output->bit_info = 0; output->timestamp = *ts; if (buffer_size <= output_buf_len) { for (plane = 0; plane < 3; plane++) { unsigned int width = (plane ? half_width : img->d_w); unsigned int height = (plane ? half_height : img->d_h); buf = img->planes[plane]; for (y = 0; y < height; y++) { pj_memcpy(&buffer[pos], buf, width); pos += width; buf += img->stride[plane]; } } output->size = buffer_size; output->type = PJMEDIA_FRAME_TYPE_VIDEO; return PJ_SUCCESS; } else { PJ_LOG(1, (THIS_FILE, "Frame ignored because of too small buffer")); return PJ_ETOOSMALL; } } static pj_status_t pj_vpx_codec_decode(pjmedia_vid_codec *codec, pj_size_t pkt_count, pjmedia_frame packets[], unsigned out_size, pjmedia_frame *output) { vpx_private *vpx = (vpx_private*) codec->codec_data; vpx_image_t *img; vpx_codec_iter_t iter; int i, res; PJ_ASSERT_RETURN(codec && pkt_count > 0 && packets && output, PJ_EINVAL); vpx->dec_frame_len = 0; /* TODO : packet parsing is absolutely incomplete here !!!! * We should manage extensions, partitions etc * */ for (i = 0; i < pkt_count; ++i) { pj_uint8_t *data; pj_uint8_t extended_bit, s_bit, partition_id; unsigned extension_len = 0; unsigned payload_size = packets[i].size; if(payload_size == 0) { continue; } data = packets[i].buf; extended_bit = (*data) & 0x80; s_bit = (*data) & 0x20; partition_id = (*data) & 0x1F; PJ_UNUSED_ARG(s_bit); PJ_UNUSED_ARG(partition_id); /* First octet is for */ /* |X|R|N|S|PartID | */ if(extended_bit) { pj_uint8_t i_bit, l_bit, t_bit, k_bit; (data)++; extension_len++; i_bit = (*data) & 0x80; l_bit = (*data) & 0x40; t_bit = (*data) & 0x20; k_bit = (*data) & 0x10; if(payload_size <= 1) { PJ_LOG(4, (THIS_FILE, "Error decoding VP8 extended attributes")); continue; } /* We have extension in octet 2 */ /* |I|L|T|K| RSV | */ if (i_bit) { data++; if(payload_size <= 2){ PJ_LOG(4, (THIS_FILE, "Error decoding VP8 extended picture ID attribute")); continue; } // I present check M flag for long picture ID if ((*data) & 0x80) { data++; } } if (l_bit) { data++; } if (t_bit || k_bit) { data++; } } data++; payload_size = packets[i].size - (data - (pj_uint8_t*)packets[i].buf); //PJ_LOG(4, (THIS_FILE, "Unpack RTP %d size %d, start %d", i, packets[i].size, s[0] & 0x10)); if((vpx->dec_frame_len + payload_size) < vpx->dec_buf_size) { pj_memcpy(vpx->dec_buf + vpx->dec_frame_len, data, payload_size); vpx->dec_frame_len += payload_size; } else { PJ_LOG(1, (THIS_FILE, "Buffer is too small")); } } if(vpx->dec_frame_len == 0){ PJ_LOG(1, (THIS_FILE, "No content for these packets")); return PJ_SUCCESS; } res = vpx_codec_decode(&vpx->decoder, vpx->dec_buf, vpx->dec_frame_len, NULL, VPX_DL_REALTIME); switch (res) { case VPX_CODEC_UNSUP_BITSTREAM: case VPX_CODEC_UNSUP_FEATURE: case VPX_CODEC_CORRUPT_FRAME: /* Fatal errors to the stream, request a keyframe to see if we can recover */ PJ_LOG(4, (THIS_FILE, "Fatal error decoding stream: (%d) %s", res, vpx_codec_err_to_string(res))); pjmedia_event event; pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING, NULL, codec); pjmedia_event_publish(NULL, codec, &event, 0); return PJMEDIA_CODEC_EBADBITSTREAM; case VPX_CODEC_OK: break; default: PJ_LOG(4, (THIS_FILE, "Failed to decode packets: (%d) %s", res, vpx_codec_err_to_string(res))); return PJMEDIA_ERROR; } iter = NULL; for (;;) { pj_status_t status; img = vpx_codec_get_frame(&vpx->decoder, &iter); if (img == NULL) break; status = pj_vpx_codec_decode_whole(codec, img, &packets[0].timestamp, out_size, output); if (status != PJ_SUCCESS) { PJ_LOG(4, (THIS_FILE, "Failed to decode frame")); /* XXX stop processing and request keyframe? */ } } return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_VPX_VID_CODEC */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-videodev/avf_dev.m ================================================ /* * Copyright (C) 2014-present AG Projects (http://ag-projects.com) * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && PJMEDIA_HAS_VIDEO != 0 && \ defined(PJMEDIA_VIDEO_DEV_HAS_AVF) && PJMEDIA_VIDEO_DEV_HAS_AVF != 0 #include #include #include #include #define THIS_FILE "avf_dev.c" #define DEFAULT_CLOCK_RATE 90000 #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 #define DEFAULT_FPS 15 typedef struct avf_fmt_info { pjmedia_format_id pjmedia_format; unsigned avf_format; } avf_fmt_info; static avf_fmt_info avf_fmts[] = { {PJMEDIA_FORMAT_BGRA, kCVPixelFormatType_32BGRA}, {PJMEDIA_FORMAT_YUY2, kCVPixelFormatType_422YpCbCr8_yuvs}, {PJMEDIA_FORMAT_UYVY, kCVPixelFormatType_422YpCbCr8}, }; /* avf device info */ struct avf_dev_info { pjmedia_vid_dev_info info; AVCaptureDevice *dev; }; /* avf factory */ struct avf_factory { pjmedia_vid_dev_factory base; pj_pool_t *pool; pj_pool_t *dev_pool; pj_pool_factory *pf; unsigned dev_count; struct avf_dev_info *dev_info; }; struct avf_stream; /* forward declaration */ typedef void (*func_ptr)(struct avf_stream *strm); @interface AVFDelegate: NSObject { @public struct avf_stream *stream; } @end /* Video stream. */ struct avf_stream { pjmedia_vid_dev_stream base; /**< Base stream */ pjmedia_vid_dev_param param; /**< Settings */ pj_pool_t *pool; /**< Memory pool. */ pj_timestamp cap_frame_ts; /**< Captured frame tstamp */ unsigned cap_ts_inc; /**< Increment */ pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */ void *user_data; /**< Application data. */ pjmedia_rect_size size; pj_bool_t cap_thread_initialized; pj_thread_desc cap_thread_desc; pj_thread_t *cap_thread; pj_bool_t cap_exited; struct avf_factory *af; pj_status_t status; pj_bool_t is_running; dispatch_queue_t video_ops_queue; AVCaptureSession *cap_session; AVCaptureDeviceInput *dev_input; AVCaptureVideoDataOutput *video_output; AVFDelegate *delegate; }; /* Prototypes */ static pj_status_t avf_factory_init(pjmedia_vid_dev_factory *f); static pj_status_t avf_factory_destroy(pjmedia_vid_dev_factory *f); static pj_status_t avf_factory_refresh(pjmedia_vid_dev_factory *f); static unsigned avf_factory_get_dev_count(pjmedia_vid_dev_factory *f); static pj_status_t avf_factory_get_dev_info(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info); static pj_status_t avf_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param); static pj_status_t avf_factory_create_stream(pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm); static pj_status_t avf_stream_get_param(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_param *param); static pj_status_t avf_stream_get_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, void *value); static pj_status_t avf_stream_set_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, const void *value); static pj_status_t avf_stream_start(pjmedia_vid_dev_stream *strm); static pj_status_t avf_stream_stop(pjmedia_vid_dev_stream *strm); static pj_status_t avf_stream_destroy(pjmedia_vid_dev_stream *strm); /* Operations */ static pjmedia_vid_dev_factory_op factory_op = { &avf_factory_init, &avf_factory_destroy, &avf_factory_get_dev_count, &avf_factory_get_dev_info, &avf_factory_default_param, &avf_factory_create_stream, &avf_factory_refresh }; static pjmedia_vid_dev_stream_op stream_op = { &avf_stream_get_param, &avf_stream_get_cap, &avf_stream_set_cap, &avf_stream_start, NULL, NULL, &avf_stream_stop, &avf_stream_destroy }; /**************************************************************************** * Factory operations */ /* * Init avf video driver. */ pjmedia_vid_dev_factory* pjmedia_avf_factory(pj_pool_factory *pf) { struct avf_factory *f; pj_pool_t *pool; pool = pj_pool_create(pf, "avf video", 4000, 4000, NULL); f = PJ_POOL_ZALLOC_T(pool, struct avf_factory); f->pf = pf; f->pool = pool; f->base.op = &factory_op; return &f->base; } /* API: init factory */ static pj_status_t avf_factory_init(pjmedia_vid_dev_factory *f) { return avf_factory_refresh(f); } /* API: destroy factory */ static pj_status_t avf_factory_destroy(pjmedia_vid_dev_factory *f) { struct avf_factory *af = (struct avf_factory*)f; pj_pool_t *pool = af->pool; if (af->dev_pool) pj_pool_release(af->dev_pool); af->pool = NULL; if (pool) pj_pool_release(pool); return PJ_SUCCESS; } /* API: refresh the list of devices */ static pj_status_t avf_factory_refresh(pjmedia_vid_dev_factory *f) { struct avf_factory *af = (struct avf_factory*)f; struct avf_dev_info *di; unsigned dev_count = 0; NSAutoreleasePool *apool = [[NSAutoreleasePool alloc]init]; NSArray *dev_array; if (af->dev_pool) { pj_pool_release(af->dev_pool); af->dev_pool = NULL; } dev_array = [AVCaptureDevice devices]; for (AVCaptureDevice *device in dev_array) { if ([device hasMediaType:AVMediaTypeVideo] && ![device isSuspended]) { dev_count++; } } /* Initialize input and output devices here */ af->dev_count = 0; af->dev_pool = pj_pool_create(af->pf, "avf video", 500, 500, NULL); af->dev_info = (struct avf_dev_info*) pj_pool_calloc(af->dev_pool, dev_count, sizeof(struct avf_dev_info)); for (AVCaptureDevice *device in dev_array) { if (![device hasMediaType:AVMediaTypeVideo] || [device isSuspended]) { continue; } di = &af->dev_info[af->dev_count++]; pj_bzero(di, sizeof(*di)); di->dev = device; pj_ansi_strncpy(di->info.name, [device.localizedName UTF8String], sizeof(di->info.name)); pj_ansi_strncpy(di->info.driver, "AVF", sizeof(di->info.driver)); di->info.dir = PJMEDIA_DIR_CAPTURE; di->info.has_callback = PJ_TRUE; di->info.fmt_cnt = 0; di->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT; PJ_LOG(4, (THIS_FILE, " dev: %s", di->info.name)); for (AVCaptureDeviceFormat* f in [device formats]) { unsigned i; CMFormatDescriptionRef desc = [f formatDescription]; for (i = 0; i < PJ_ARRAY_SIZE(avf_fmts); i++) { if (CMFormatDescriptionGetMediaSubType(desc) == avf_fmts[i].avf_format) { char fmt_name[5]; CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(desc); if (dim.width < 640) continue; pjmedia_fourcc_name(avf_fmts[i].pjmedia_format, fmt_name); PJ_LOG(4, (THIS_FILE, " detected resolution %dx%d (%s)", dim.width, dim.height, fmt_name)); pjmedia_format *fmt = &di->info.fmt[di->info.fmt_cnt++]; pjmedia_format_init_video(fmt, avf_fmts[i].pjmedia_format, dim.width, dim.height, DEFAULT_FPS, 1); } } } if (di->info.fmt_cnt == 0) { PJ_LOG(4, (THIS_FILE, " there are no compatible formats, using default")); pjmedia_format *fmt = &di->info.fmt[di->info.fmt_cnt++]; pjmedia_format_init_video(fmt, avf_fmts[0].pjmedia_format, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FPS, 1); } } [apool release]; PJ_LOG(4, (THIS_FILE, "avf video has %d devices", af->dev_count)); return PJ_SUCCESS; } /* API: get number of devices */ static unsigned avf_factory_get_dev_count(pjmedia_vid_dev_factory *f) { struct avf_factory *af = (struct avf_factory*)f; return af->dev_count; } /* API: get device info */ static pj_status_t avf_factory_get_dev_info(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info) { struct avf_factory *af = (struct avf_factory*)f; PJ_ASSERT_RETURN(index < af->dev_count, PJMEDIA_EVID_INVDEV); pj_memcpy(info, &af->dev_info[index].info, sizeof(*info)); return PJ_SUCCESS; } /* API: create default device parameter */ static pj_status_t avf_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param) { struct avf_factory *af = (struct avf_factory*)f; struct avf_dev_info *di = &af->dev_info[index]; PJ_ASSERT_RETURN(index < af->dev_count, PJMEDIA_EVID_INVDEV); PJ_UNUSED_ARG(pool); pj_bzero(param, sizeof(*param)); param->dir = PJMEDIA_DIR_CAPTURE; param->cap_id = index; param->rend_id = PJMEDIA_VID_INVALID_DEV; param->flags = PJMEDIA_VID_DEV_CAP_FORMAT; param->clock_rate = DEFAULT_CLOCK_RATE; pj_memcpy(¶m->fmt, &di->info.fmt[0], sizeof(param->fmt)); return PJ_SUCCESS; } static avf_fmt_info* get_avf_format_info(pjmedia_format_id id) { unsigned i; for (i = 0; i < PJ_ARRAY_SIZE(avf_fmts); i++) { if (avf_fmts[i].pjmedia_format == id) return &avf_fmts[i]; } return NULL; } @implementation AVFDelegate - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { pjmedia_frame frame = {0}; CVImageBufferRef img; CVReturn ret; OSType type; size_t width, height; /* Register thread if needed */ if (stream->cap_thread_initialized == 0 || !pj_thread_is_registered()) { pj_bzero(stream->cap_thread_desc, sizeof(pj_thread_desc)); pj_thread_register("avf_cap", stream->cap_thread_desc, &stream->cap_thread); stream->cap_thread_initialized = 1; } if (!sampleBuffer) return; /* Get a CMSampleBuffer's Core Video image buffer for the media data */ img = CMSampleBufferGetImageBuffer(sampleBuffer); if (!img) return; /* Check for supported formats */ type = CVPixelBufferGetPixelFormatType(img); switch(type) { case kCVPixelFormatType_32BGRA: case kCVPixelFormatType_422YpCbCr8_yuvs: case kCVPixelFormatType_422YpCbCr8: break; default: PJ_LOG(4, (THIS_FILE, "Unsupported image format! %c%c%c%c", type>>24, type>>16, type>>8, type>>0)); return; } /* Lock the base address of the pixel buffer */ ret = CVPixelBufferLockBaseAddress(img, kCVPixelBufferLock_ReadOnly); if (ret != kCVReturnSuccess) return; width = CVPixelBufferGetWidth(img); height = CVPixelBufferGetHeight(img); /* Prepare frame */ frame.type = PJMEDIA_FRAME_TYPE_VIDEO; frame.timestamp.u64 = stream->cap_frame_ts.u64; frame.buf = CVPixelBufferGetBaseAddress(img); frame.size = CVPixelBufferGetBytesPerRow(img) * height; if (stream->size.w != width || stream->size.h != height) { PJ_LOG(4, (THIS_FILE, "AVF image size changed, before: %dx%d, after: %dx%d", stream->size.w, stream->size.h, width, height)); } if (stream->vid_cb.capture_cb) { (*stream->vid_cb.capture_cb)(&stream->base, stream->user_data, &frame); } stream->cap_frame_ts.u64 += stream->cap_ts_inc; /* Unlock the pixel buffer */ CVPixelBufferUnlockBaseAddress(img, kCVPixelBufferLock_ReadOnly); } @end static void init_avf_stream(struct avf_stream *strm) { const pjmedia_video_format_info *vfi; pjmedia_video_format_detail *vfd; avf_fmt_info *fi = get_avf_format_info(strm->param.fmt.id); NSError *error; pj_status_t status; if (!fi) { strm->status = PJMEDIA_EVID_BADFORMAT; return; } strm->cap_session = [[AVCaptureSession alloc] init]; if (!strm->cap_session) { strm->status = PJ_ENOMEM; return; } strm->cap_session.sessionPreset = AVCaptureSessionPresetHigh; vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE); pj_assert(vfd); vfi = pjmedia_get_video_format_info(NULL, strm->param.fmt.id); pj_assert(vfi); vfd->size = strm->size; PJ_LOG(4, (THIS_FILE, "Opening video device at %dx%d resolution", vfd->size.w, vfd->size.h)); /* Add the video device to the session as a device input */ AVCaptureDevice *videoDevice = strm->af->dev_info[strm->param.cap_id].dev; strm->dev_input = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error: &error]; if (!strm->dev_input) { status = PJMEDIA_EVID_SYSERR; return; } [strm->cap_session addInput:strm->dev_input]; strm->video_output = [[AVCaptureVideoDataOutput alloc] init]; if (!strm->video_output) { status = PJMEDIA_EVID_SYSERR; return; } [strm->cap_session addOutput:strm->video_output]; /* Configure the video output */ strm->video_output.alwaysDiscardsLateVideoFrames = YES; /* The Apple provided documentation says the only supported key is kCVPixelBufferPixelFormatTypeKey, * but it turns out kCVPixelBufferWidthKey and kCVPixelBufferHeightKey are also required. Thanks * Chromium, for figuring it out.*/ strm->video_output.videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: @(fi->avf_format), kCVPixelBufferPixelFormatTypeKey, @(vfd->size.w), kCVPixelBufferWidthKey, @(vfd->size.h), kCVPixelBufferHeightKey, nil]; strm->delegate = [[AVFDelegate alloc] init]; strm->delegate->stream = strm; dispatch_queue_t queue = dispatch_queue_create("AVFQueue", NULL); [strm->video_output setSampleBufferDelegate:strm->delegate queue:queue]; dispatch_release(queue); } static void run_func_on_video_queue(struct avf_stream *strm, func_ptr func) { dispatch_sync(strm->video_ops_queue, ^{ (*func)(strm); }); } /* API: create stream */ static pj_status_t avf_factory_create_stream(pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm) { struct avf_factory *af = (struct avf_factory*)f; pj_pool_t *pool; struct avf_stream *strm; const pjmedia_video_format_info *vfi; pjmedia_video_format_detail *vfd; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL); PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO && param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO && param->dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL); vfi = pjmedia_get_video_format_info(NULL, param->fmt.id); if (!vfi) return PJMEDIA_EVID_BADFORMAT; /* Create and Initialize stream descriptor */ pool = pj_pool_create(af->pf, "avf-dev", 4000, 4000, NULL); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); strm = PJ_POOL_ZALLOC_T(pool, struct avf_stream); pj_memcpy(&strm->param, param, sizeof(*param)); strm->pool = pool; pj_memcpy(&strm->vid_cb, cb, sizeof(*cb)); strm->user_data = user_data; strm->af = af; vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE); pj_memcpy(&strm->size, &vfd->size, sizeof(vfd->size)); pj_assert(vfd->fps.num); strm->cap_ts_inc = PJMEDIA_SPF2(strm->param.clock_rate, &vfd->fps, 1); /* Create dispatch queue */ strm->video_ops_queue = dispatch_queue_create("AVF Video Ops", DISPATCH_QUEUE_SERIAL); /* Create capture stream here */ strm->status = PJ_SUCCESS; run_func_on_video_queue(strm, init_avf_stream); status = strm->status; if (status != PJ_SUCCESS) { dispatch_release(strm->video_ops_queue); avf_stream_destroy((pjmedia_vid_dev_stream *)strm); return status; } /* Update param as output */ param->fmt = strm->param.fmt; /* Done */ strm->base.op = &stream_op; *p_vid_strm = &strm->base; return PJ_SUCCESS; } /* API: Get stream info. */ static pj_status_t avf_stream_get_param(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_param *pi) { struct avf_stream *strm = (struct avf_stream*)s; PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); pj_memcpy(pi, &strm->param, sizeof(*pi)); return PJ_SUCCESS; } /* API: get capability */ static pj_status_t avf_stream_get_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, void *pval) { struct avf_stream *strm = (struct avf_stream*)s; PJ_UNUSED_ARG(strm); PJ_UNUSED_ARG(cap); PJ_UNUSED_ARG(pval); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); return PJMEDIA_EVID_INVCAP; } /* API: set capability */ static pj_status_t avf_stream_set_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, const void *pval) { struct avf_stream *strm = (struct avf_stream*)s; PJ_UNUSED_ARG(strm); PJ_UNUSED_ARG(cap); PJ_UNUSED_ARG(pval); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); return PJMEDIA_EVID_INVCAP; } static void start_avf(struct avf_stream *strm) { [strm->cap_session startRunning]; } static void stop_avf(struct avf_stream *strm) { [strm->cap_session stopRunning]; } /* API: Start stream. */ static pj_status_t avf_stream_start(pjmedia_vid_dev_stream *strm) { struct avf_stream *stream = (struct avf_stream*)strm; PJ_LOG(4, (THIS_FILE, "Starting avf video stream")); if (stream->cap_session) { run_func_on_video_queue(stream, start_avf); if (![stream->cap_session isRunning]) return PJMEDIA_EVID_NOTREADY; stream->is_running = PJ_TRUE; } return PJ_SUCCESS; } /* API: Stop stream. */ static pj_status_t avf_stream_stop(pjmedia_vid_dev_stream *strm) { struct avf_stream *stream = (struct avf_stream*)strm; PJ_LOG(4, (THIS_FILE, "Stopping avf video stream")); if (stream->cap_session && [stream->cap_session isRunning]) { int i; stream->cap_exited = PJ_FALSE; run_func_on_video_queue(stream, stop_avf); stream->is_running = PJ_FALSE; for (i = 50; i >= 0 && !stream->cap_exited; i--) { pj_thread_sleep(10); } } return PJ_SUCCESS; } static void destroy_avf(struct avf_stream *strm) { if (strm->cap_session) { [strm->cap_session removeInput:strm->dev_input]; [strm->cap_session removeOutput:strm->video_output]; [strm->cap_session release]; strm->cap_session = NULL; } if (strm->delegate) { [strm->delegate release]; strm->delegate = NULL; } if (strm->dev_input) { strm->dev_input = NULL; } if (strm->video_output) { strm->video_output = NULL; } } /* API: Destroy stream. */ static pj_status_t avf_stream_destroy(pjmedia_vid_dev_stream *strm) { struct avf_stream *stream = (struct avf_stream*)strm; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); avf_stream_stop(strm); run_func_on_video_queue(stream, destroy_avf); dispatch_release(stream->video_ops_queue); pj_pool_release(stream->pool); return PJ_SUCCESS; } #endif /* PJMEDIA_VIDEO_DEV_HAS_AVF */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-videodev/avi_dev.c ================================================ /* $Id: avi_dev.c 4086 2012-04-26 02:44:41Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #if defined(PJMEDIA_VIDEO_DEV_HAS_AVI) && PJMEDIA_VIDEO_DEV_HAS_AVI != 0 && \ defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #define THIS_FILE "avi_dev.c" #define DRIVER_NAME "AVIDev" #define DEFAULT_CLOCK_RATE 90000 #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 #define DEFAULT_FPS 25 typedef struct avi_dev_strm avi_dev_strm; /* avi_ device info */ struct avi_dev_info { pjmedia_vid_dev_info info; pj_pool_t *pool; pj_str_t fpath; pj_str_t title; pjmedia_avi_streams *avi; pjmedia_port *vid; avi_dev_strm *strm; pjmedia_vid_codec *codec; pj_uint8_t *enc_buf; pj_size_t enc_buf_size; }; /* avi_ factory */ struct avi_factory { pjmedia_vid_dev_factory base; pj_pool_t *pool; pj_pool_factory *pf; unsigned dev_count; struct avi_dev_info *dev_info; }; /* Video stream. */ struct avi_dev_strm { pjmedia_vid_dev_stream base; /**< Base stream */ pjmedia_vid_dev_param param; /**< Settings */ pj_pool_t *pool; /**< Memory pool. */ struct avi_dev_info *adi; pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */ void *user_data; /**< Application data. */ }; /* Prototypes */ static pj_status_t avi_factory_init(pjmedia_vid_dev_factory *f); static pj_status_t avi_factory_destroy(pjmedia_vid_dev_factory *f); static pj_status_t avi_factory_refresh(pjmedia_vid_dev_factory *f); static unsigned avi_factory_get_dev_count(pjmedia_vid_dev_factory *f); static pj_status_t avi_factory_get_dev_info(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info); static pj_status_t avi_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param); static pj_status_t avi_factory_create_stream( pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm); static pj_status_t avi_dev_strm_get_param(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_param *param); static pj_status_t avi_dev_strm_get_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, void *value); static pj_status_t avi_dev_strm_set_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, const void *value); static pj_status_t avi_dev_strm_get_frame(pjmedia_vid_dev_stream *strm, pjmedia_frame *frame); static pj_status_t avi_dev_strm_start(pjmedia_vid_dev_stream *strm); static pj_status_t avi_dev_strm_stop(pjmedia_vid_dev_stream *strm); static pj_status_t avi_dev_strm_destroy(pjmedia_vid_dev_stream *strm); static void reset_dev_info(struct avi_dev_info *adi); /* Operations */ static pjmedia_vid_dev_factory_op factory_op = { &avi_factory_init, &avi_factory_destroy, &avi_factory_get_dev_count, &avi_factory_get_dev_info, &avi_factory_default_param, &avi_factory_create_stream, &avi_factory_refresh }; static pjmedia_vid_dev_stream_op stream_op = { &avi_dev_strm_get_param, &avi_dev_strm_get_cap, &avi_dev_strm_set_cap, &avi_dev_strm_start, &avi_dev_strm_get_frame, NULL, &avi_dev_strm_stop, &avi_dev_strm_destroy }; /**************************************************************************** * Factory operations */ /* API */ PJ_DEF(pj_status_t) pjmedia_avi_dev_create_factory( pj_pool_factory *pf, unsigned max_dev, pjmedia_vid_dev_factory **p_ret) { struct avi_factory *cf; pj_pool_t *pool; pj_status_t status; pool = pj_pool_create(pf, "avidevfc%p", 512, 512, NULL); cf = PJ_POOL_ZALLOC_T(pool, struct avi_factory); cf->pf = pf; cf->pool = pool; cf->dev_count = max_dev; cf->base.op = &factory_op; cf->dev_info = (struct avi_dev_info*) pj_pool_calloc(cf->pool, cf->dev_count, sizeof(struct avi_dev_info)); if (p_ret) { *p_ret = &cf->base; } status = pjmedia_vid_register_factory(NULL, &cf->base); if (status != PJ_SUCCESS) return status; PJ_LOG(4, (THIS_FILE, "AVI dev factory created with %d virtual device(s)", cf->dev_count)); return PJ_SUCCESS; } /* API: init factory */ static pj_status_t avi_factory_init(pjmedia_vid_dev_factory *f) { struct avi_factory *cf = (struct avi_factory*)f; unsigned i; for (i=0; idev_count; ++i) { reset_dev_info(&cf->dev_info[i]); } return PJ_SUCCESS; } /* API: destroy factory */ static pj_status_t avi_factory_destroy(pjmedia_vid_dev_factory *f) { struct avi_factory *cf = (struct avi_factory*)f; pj_pool_t *pool = cf->pool; cf->pool = NULL; pj_pool_release(pool); return PJ_SUCCESS; } /* API: refresh the list of devices */ static pj_status_t avi_factory_refresh(pjmedia_vid_dev_factory *f) { PJ_UNUSED_ARG(f); return PJ_SUCCESS; } /* API: get number of devices */ static unsigned avi_factory_get_dev_count(pjmedia_vid_dev_factory *f) { struct avi_factory *cf = (struct avi_factory*)f; return cf->dev_count; } /* API: get device info */ static pj_status_t avi_factory_get_dev_info(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info) { struct avi_factory *cf = (struct avi_factory*)f; PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV); pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info)); return PJ_SUCCESS; } /* API: create default device parameter */ static pj_status_t avi_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param) { struct avi_factory *cf = (struct avi_factory*)f; struct avi_dev_info *di = &cf->dev_info[index]; PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV); PJ_UNUSED_ARG(pool); pj_bzero(param, sizeof(*param)); param->dir = PJMEDIA_DIR_CAPTURE; param->cap_id = index; param->rend_id = PJMEDIA_VID_INVALID_DEV; param->flags = PJMEDIA_VID_DEV_CAP_FORMAT; param->clock_rate = DEFAULT_CLOCK_RATE; pj_memcpy(¶m->fmt, &di->info.fmt[0], sizeof(param->fmt)); return PJ_SUCCESS; } /* reset dev info */ static void reset_dev_info(struct avi_dev_info *adi) { /* Close avi streams */ if (adi->avi) { unsigned i, cnt; cnt = pjmedia_avi_streams_get_num_streams(adi->avi); for (i=0; iavi, i); if (as) { pjmedia_port *port; port = pjmedia_avi_stream_get_port(as); pjmedia_port_destroy(port); } } adi->avi = NULL; } if (adi->codec) { pjmedia_vid_codec_close(adi->codec); adi->codec = NULL; } if (adi->pool) pj_pool_release(adi->pool); pj_bzero(adi, sizeof(*adi)); /* Fill up with *dummy" device info */ pj_ansi_strncpy(adi->info.name, "AVI Player", sizeof(adi->info.name)-1); pj_ansi_strncpy(adi->info.driver, DRIVER_NAME, sizeof(adi->info.driver)-1); adi->info.dir = PJMEDIA_DIR_CAPTURE; adi->info.has_callback = PJ_FALSE; } /* API: release resources */ PJ_DEF(pj_status_t) pjmedia_avi_dev_free(pjmedia_vid_dev_index id) { pjmedia_vid_dev_factory *f; struct avi_factory *cf; unsigned local_idx; struct avi_dev_info *adi; pj_status_t status; /* Lookup the factory and local device index */ status = pjmedia_vid_dev_get_local_index(id, &f, &local_idx); if (status != PJ_SUCCESS) return status; /* The factory must be AVI factory */ PJ_ASSERT_RETURN(f->op->init == &avi_factory_init, PJMEDIA_EVID_INVDEV); cf = (struct avi_factory*)f; /* Device index should be valid */ PJ_ASSERT_RETURN(local_idx <= cf->dev_count, PJ_EBUG); adi = &cf->dev_info[local_idx]; /* Cannot configure if stream is running */ if (adi->strm) return PJ_EBUSY; /* Reset */ reset_dev_info(adi); return PJ_SUCCESS; } /* API: get param */ PJ_DEF(pj_status_t) pjmedia_avi_dev_get_param(pjmedia_vid_dev_index id, pjmedia_avi_dev_param *prm) { pjmedia_vid_dev_factory *f; struct avi_factory *cf; unsigned local_idx; struct avi_dev_info *adi; pj_status_t status; /* Lookup the factory and local device index */ status = pjmedia_vid_dev_get_local_index(id, &f, &local_idx); if (status != PJ_SUCCESS) return status; /* The factory must be factory */ PJ_ASSERT_RETURN(f->op->init == &avi_factory_init, PJMEDIA_EVID_INVDEV); cf = (struct avi_factory*)f; /* Device index should be valid */ PJ_ASSERT_RETURN(local_idx <= cf->dev_count, PJ_EBUG); adi = &cf->dev_info[local_idx]; pj_bzero(prm, sizeof(*prm)); prm->path = adi->fpath; prm->title = adi->title; prm->avi_streams = adi->avi; return PJ_SUCCESS; } PJ_DEF(void) pjmedia_avi_dev_param_default(pjmedia_avi_dev_param *p) { pj_bzero(p, sizeof(*p)); } /* API: configure the AVI */ PJ_DEF(pj_status_t) pjmedia_avi_dev_alloc( pjmedia_vid_dev_factory *f, pjmedia_avi_dev_param *p, pjmedia_vid_dev_index *p_id) { pjmedia_vid_dev_index id; struct avi_factory *cf = (struct avi_factory*)f; unsigned local_idx; struct avi_dev_info *adi = NULL; pjmedia_format avi_fmt; const pjmedia_video_format_info *vfi; pj_status_t status; PJ_ASSERT_RETURN(f && p && p_id, PJ_EINVAL); if (p_id) *p_id = PJMEDIA_VID_INVALID_DEV; /* Get a free dev */ for (local_idx=0; local_idxdev_count; ++local_idx) { if (cf->dev_info[local_idx].avi == NULL) { adi = &cf->dev_info[local_idx]; break; } } if (!adi) return PJ_ETOOMANY; /* Convert local ID to global id */ status = pjmedia_vid_dev_get_global_index(&cf->base, local_idx, &id); if (status != PJ_SUCCESS) return status; /* Reset */ if (adi->pool) { pj_pool_release(adi->pool); } pj_bzero(adi, sizeof(*adi)); /* Reinit */ PJ_ASSERT_RETURN(p->path.slen, PJ_EINVAL); adi->pool = pj_pool_create(cf->pf, "avidi%p", 512, 512, NULL); /* Open the AVI */ pj_strdup_with_null(adi->pool, &adi->fpath, &p->path); status = pjmedia_avi_player_create_streams(adi->pool, adi->fpath.ptr, 0, &adi->avi); if (status != PJ_SUCCESS) { goto on_error; } adi->vid = pjmedia_avi_streams_get_stream_by_media(adi->avi, 0, PJMEDIA_TYPE_VIDEO); if (!adi->vid) { status = PJMEDIA_EVID_BADFORMAT; PJ_LOG(4,(THIS_FILE, "Error: cannot find video in AVI %s", adi->fpath.ptr)); goto on_error; } pjmedia_format_copy(&avi_fmt, &adi->vid->info.fmt); vfi = pjmedia_get_video_format_info(NULL, avi_fmt.id); /* Check whether the frame is encoded. */ if (!vfi || vfi->bpp == 0) { /* Yes, prepare codec */ const pjmedia_vid_codec_info *codec_info; pjmedia_vid_codec_param codec_param; pjmedia_video_apply_fmt_param vafp; /* Lookup codec */ status = pjmedia_vid_codec_mgr_get_codec_info2(NULL, avi_fmt.id, &codec_info); if (status != PJ_SUCCESS || !codec_info) goto on_error; status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info, &codec_param); if (status != PJ_SUCCESS) goto on_error; /* Open codec */ status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info, &adi->codec); if (status != PJ_SUCCESS) goto on_error; status = pjmedia_vid_codec_init(adi->codec, adi->pool); if (status != PJ_SUCCESS) goto on_error; codec_param.dir = PJMEDIA_DIR_DECODING; codec_param.packing = PJMEDIA_VID_PACKING_WHOLE; status = pjmedia_vid_codec_open(adi->codec, &codec_param); if (status != PJ_SUCCESS) goto on_error; /* Allocate buffer */ avi_fmt.id = codec_info->dec_fmt_id[0]; vfi = pjmedia_get_video_format_info(NULL, avi_fmt.id); pj_bzero(&vafp, sizeof(vafp)); vafp.size = avi_fmt.det.vid.size; status = vfi->apply_fmt(vfi, &vafp); if (status != PJ_SUCCESS) goto on_error; adi->enc_buf = pj_pool_alloc(adi->pool, vafp.framebytes); adi->enc_buf_size = vafp.framebytes; } /* Calculate title */ if (p->title.slen) { pj_strdup_with_null(adi->pool, &adi->title, &p->title); } else { char *start = p->path.ptr + p->path.slen; pj_str_t tmp; while (start >= p->path.ptr) { if (*start == '/' || *start == '\\') break; --start; } tmp.ptr = start + 1; tmp.slen = p->path.ptr + p->path.slen - tmp.ptr; pj_strdup_with_null(adi->pool, &adi->title, &tmp); } /* Init device info */ pj_ansi_strncpy(adi->info.name, adi->title.ptr, sizeof(adi->info.name)-1); pj_ansi_strncpy(adi->info.driver, DRIVER_NAME, sizeof(adi->info.driver)-1); adi->info.dir = PJMEDIA_DIR_CAPTURE; adi->info.has_callback = PJ_FALSE; adi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT; adi->info.fmt_cnt = 1; pjmedia_format_copy(&adi->info.fmt[0], &avi_fmt); /* Set out vars */ if (p_id) *p_id = id; p->avi_streams = adi->avi; if (p->title.slen == 0) p->title = adi->title; return PJ_SUCCESS; on_error: if (adi->codec) { pjmedia_vid_codec_close(adi->codec); adi->codec = NULL; } if (adi->pool) { pj_pool_release(adi->pool); adi->pool = NULL; } pjmedia_avi_dev_free(id); return status; } /* API: create stream */ static pj_status_t avi_factory_create_stream( pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm) { struct avi_factory *cf = (struct avi_factory*)f; pj_pool_t *pool = NULL; struct avi_dev_info *adi; struct avi_dev_strm *strm; PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL); PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO && param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO && param->dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL); /* Device must have been configured with pjmedia_avi_dev_set_param() */ adi = &cf->dev_info[param->cap_id]; PJ_ASSERT_RETURN(adi->avi != NULL, PJ_EINVALIDOP); /* Cannot create while stream is already active */ PJ_ASSERT_RETURN(adi->strm==NULL, PJ_EINVALIDOP); /* Create and initialize basic stream descriptor */ pool = pj_pool_create(cf->pf, "avidev%p", 512, 512, NULL); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); strm = PJ_POOL_ZALLOC_T(pool, struct avi_dev_strm); pj_memcpy(&strm->param, param, sizeof(*param)); strm->pool = pool; pj_memcpy(&strm->vid_cb, cb, sizeof(*cb)); strm->user_data = user_data; strm->adi = adi; pjmedia_format_copy(¶m->fmt, &adi->info.fmt[0]); /* Done */ strm->base.op = &stream_op; adi->strm = strm; *p_vid_strm = &strm->base; return PJ_SUCCESS; } /* API: Get stream info. */ static pj_status_t avi_dev_strm_get_param(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_param *pi) { struct avi_dev_strm *strm = (struct avi_dev_strm*)s; PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); pj_memcpy(pi, &strm->param, sizeof(*pi)); return PJ_SUCCESS; } /* API: get capability */ static pj_status_t avi_dev_strm_get_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, void *pval) { struct avi_dev_strm *strm = (struct avi_dev_strm*)s; PJ_UNUSED_ARG(strm); PJ_UNUSED_ARG(cap); PJ_UNUSED_ARG(pval); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); return PJMEDIA_EVID_INVCAP; } /* API: set capability */ static pj_status_t avi_dev_strm_set_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, const void *pval) { struct avi_dev_strm *strm = (struct avi_dev_strm*)s; PJ_UNUSED_ARG(strm); PJ_UNUSED_ARG(cap); PJ_UNUSED_ARG(pval); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); return PJMEDIA_EVID_INVCAP; } /* API: Get frame from stream */ static pj_status_t avi_dev_strm_get_frame(pjmedia_vid_dev_stream *strm, pjmedia_frame *frame) { struct avi_dev_strm *stream = (struct avi_dev_strm*)strm; if (stream->adi->codec) { pjmedia_frame enc_frame; pj_status_t status; enc_frame.buf = stream->adi->enc_buf; enc_frame.size = stream->adi->enc_buf_size; status = pjmedia_port_get_frame(stream->adi->vid, &enc_frame); if (status != PJ_SUCCESS) return status; return pjmedia_vid_codec_decode(stream->adi->codec, 1, &enc_frame, (unsigned)frame->size, frame); } else { return pjmedia_port_get_frame(stream->adi->vid, frame); } } /* API: Start stream. */ static pj_status_t avi_dev_strm_start(pjmedia_vid_dev_stream *strm) { struct avi_dev_strm *stream = (struct avi_dev_strm*)strm; PJ_UNUSED_ARG(stream); PJ_LOG(4, (THIS_FILE, "Starting avi video stream")); return PJ_SUCCESS; } /* API: Stop stream. */ static pj_status_t avi_dev_strm_stop(pjmedia_vid_dev_stream *strm) { struct avi_dev_strm *stream = (struct avi_dev_strm*)strm; PJ_UNUSED_ARG(stream); PJ_LOG(4, (THIS_FILE, "Stopping avi video stream")); return PJ_SUCCESS; } /* API: Destroy stream. */ static pj_status_t avi_dev_strm_destroy(pjmedia_vid_dev_stream *strm) { struct avi_dev_strm *stream = (struct avi_dev_strm*)strm; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); avi_dev_strm_stop(strm); stream->adi->strm = NULL; stream->adi = NULL; pj_pool_release(stream->pool); return PJ_SUCCESS; } #endif /* PJMEDIA_VIDEO_DEV_HAS_AVI */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-videodev/colorbar_dev.c ================================================ /* $Id: colorbar_dev.c 4158 2012-06-06 09:56:14Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && PJMEDIA_HAS_VIDEO != 0 && \ defined(PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC) && \ PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC != 0 #define THIS_FILE "colorbar_dev.c" #define DEFAULT_CLOCK_RATE 90000 #define DEFAULT_WIDTH 352 //640 #define DEFAULT_HEIGHT 288 //480 #define DEFAULT_FPS 25 /* cbar_ device info */ struct cbar_dev_info { pjmedia_vid_dev_info info; }; /* cbar_ factory */ struct cbar_factory { pjmedia_vid_dev_factory base; pj_pool_t *pool; pj_pool_factory *pf; unsigned dev_count; struct cbar_dev_info *dev_info; }; struct cbar_fmt_info { pjmedia_format_id fmt_id; /* Format ID */ /* Info for packed formats. */ unsigned c_offset[3]; /* Color component offset, in bytes */ unsigned c_stride[3]; /* Color component stride, or distance between two consecutive same color components, in bytes */ }; /* Colorbar video source supports */ static struct cbar_fmt_info cbar_fmts[] = { /* Packed formats */ { PJMEDIA_FORMAT_YUY2, {0, 1, 3}, {2, 4, 4} }, { PJMEDIA_FORMAT_UYVY, {1, 0, 2}, {2, 4, 4} }, { PJMEDIA_FORMAT_YVYU, {0, 3, 1}, {2, 4, 4} }, { PJMEDIA_FORMAT_RGBA, {0, 1, 2}, {4, 4, 4} }, { PJMEDIA_FORMAT_RGB24, {0, 1, 2}, {3, 3, 3} }, { PJMEDIA_FORMAT_BGRA, {2, 1, 0}, {4, 4, 4} }, /* Planar formats */ { PJMEDIA_FORMAT_YV12 }, { PJMEDIA_FORMAT_I420 }, { PJMEDIA_FORMAT_I422 }, { PJMEDIA_FORMAT_I420JPEG }, { PJMEDIA_FORMAT_I422JPEG }, }; /* Video stream. */ struct cbar_stream { pjmedia_vid_dev_stream base; /**< Base stream */ pjmedia_vid_dev_param param; /**< Settings */ pj_pool_t *pool; /**< Memory pool. */ pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */ void *user_data; /**< Application data. */ const struct cbar_fmt_info *cbfi; const pjmedia_video_format_info *vfi; pjmedia_video_apply_fmt_param vafp; pj_uint8_t *first_line[PJMEDIA_MAX_VIDEO_PLANES]; pj_timestamp ts; unsigned ts_inc; /* For active capturer only */ pjmedia_clock *clock; pj_uint8_t *clock_buf; }; /* Prototypes */ static pj_status_t cbar_factory_init(pjmedia_vid_dev_factory *f); static pj_status_t cbar_factory_destroy(pjmedia_vid_dev_factory *f); static pj_status_t cbar_factory_refresh(pjmedia_vid_dev_factory *f); static unsigned cbar_factory_get_dev_count(pjmedia_vid_dev_factory *f); static pj_status_t cbar_factory_get_dev_info(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info); static pj_status_t cbar_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param); static pj_status_t cbar_factory_create_stream( pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm); static pj_status_t cbar_stream_get_param(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_param *param); static pj_status_t cbar_stream_get_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, void *value); static pj_status_t cbar_stream_set_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, const void *value); static pj_status_t cbar_stream_get_frame(pjmedia_vid_dev_stream *strm, pjmedia_frame *frame); static pj_status_t cbar_stream_start(pjmedia_vid_dev_stream *strm); static pj_status_t cbar_stream_stop(pjmedia_vid_dev_stream *strm); static pj_status_t cbar_stream_destroy(pjmedia_vid_dev_stream *strm); /* Operations */ static pjmedia_vid_dev_factory_op factory_op = { &cbar_factory_init, &cbar_factory_destroy, &cbar_factory_get_dev_count, &cbar_factory_get_dev_info, &cbar_factory_default_param, &cbar_factory_create_stream, &cbar_factory_refresh }; static pjmedia_vid_dev_stream_op stream_op = { &cbar_stream_get_param, &cbar_stream_get_cap, &cbar_stream_set_cap, &cbar_stream_start, &cbar_stream_get_frame, NULL, &cbar_stream_stop, &cbar_stream_destroy }; /**************************************************************************** * Factory operations */ /* * Init cbar_ video driver. */ pjmedia_vid_dev_factory* pjmedia_cbar_factory(pj_pool_factory *pf) { struct cbar_factory *f; pj_pool_t *pool; pool = pj_pool_create(pf, "cbar video", 512, 512, NULL); f = PJ_POOL_ZALLOC_T(pool, struct cbar_factory); f->pf = pf; f->pool = pool; f->base.op = &factory_op; return &f->base; } /* API: init factory */ static pj_status_t cbar_factory_init(pjmedia_vid_dev_factory *f) { struct cbar_factory *cf = (struct cbar_factory*)f; struct cbar_dev_info *ddi; unsigned i; cf->dev_count = 2; cf->dev_info = (struct cbar_dev_info*) pj_pool_calloc(cf->pool, cf->dev_count, sizeof(struct cbar_dev_info)); /* Passive capturer */ ddi = &cf->dev_info[0]; pj_bzero(ddi, sizeof(*ddi)); pj_ansi_strncpy(ddi->info.name, "Colorbar generator", sizeof(ddi->info.name)); ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0'; pj_ansi_strncpy(ddi->info.driver, "Colorbar", sizeof(ddi->info.driver)); ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0'; ddi->info.dir = PJMEDIA_DIR_CAPTURE; ddi->info.has_callback = PJ_FALSE; ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT; ddi->info.fmt_cnt = sizeof(cbar_fmts)/sizeof(cbar_fmts[0]); for (i = 0; i < ddi->info.fmt_cnt; i++) { pjmedia_format *fmt = &ddi->info.fmt[i]; pjmedia_format_init_video(fmt, cbar_fmts[i].fmt_id, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FPS, 1); } /* Active capturer */ ddi = &cf->dev_info[1]; pj_bzero(ddi, sizeof(*ddi)); pj_ansi_strncpy(ddi->info.name, "Colorbar-active", sizeof(ddi->info.name)); ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0'; pj_ansi_strncpy(ddi->info.driver, "Colorbar", sizeof(ddi->info.driver)); ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0'; ddi->info.dir = PJMEDIA_DIR_CAPTURE; ddi->info.has_callback = PJ_TRUE; ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT; ddi->info.fmt_cnt = sizeof(cbar_fmts)/sizeof(cbar_fmts[0]); for (i = 0; i < ddi->info.fmt_cnt; i++) { pjmedia_format *fmt = &ddi->info.fmt[i]; pjmedia_format_init_video(fmt, cbar_fmts[i].fmt_id, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FPS, 1); } PJ_LOG(4, (THIS_FILE, "Colorbar video src initialized with %d device(s):", cf->dev_count)); for (i = 0; i < cf->dev_count; i++) { PJ_LOG(4, (THIS_FILE, "%2d: %s", i, cf->dev_info[i].info.name)); } return PJ_SUCCESS; } /* API: destroy factory */ static pj_status_t cbar_factory_destroy(pjmedia_vid_dev_factory *f) { struct cbar_factory *cf = (struct cbar_factory*)f; pj_pool_t *pool = cf->pool; cf->pool = NULL; pj_pool_release(pool); return PJ_SUCCESS; } /* API: refresh the list of devices */ static pj_status_t cbar_factory_refresh(pjmedia_vid_dev_factory *f) { PJ_UNUSED_ARG(f); return PJ_SUCCESS; } /* API: get number of devices */ static unsigned cbar_factory_get_dev_count(pjmedia_vid_dev_factory *f) { struct cbar_factory *cf = (struct cbar_factory*)f; return cf->dev_count; } /* API: get device info */ static pj_status_t cbar_factory_get_dev_info(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info) { struct cbar_factory *cf = (struct cbar_factory*)f; PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV); pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info)); return PJ_SUCCESS; } /* API: create default device parameter */ static pj_status_t cbar_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param) { struct cbar_factory *cf = (struct cbar_factory*)f; struct cbar_dev_info *di = &cf->dev_info[index]; PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV); PJ_UNUSED_ARG(pool); pj_bzero(param, sizeof(*param)); param->dir = PJMEDIA_DIR_CAPTURE; param->cap_id = index; param->rend_id = PJMEDIA_VID_INVALID_DEV; param->flags = PJMEDIA_VID_DEV_CAP_FORMAT; param->clock_rate = DEFAULT_CLOCK_RATE; pj_memcpy(¶m->fmt, &di->info.fmt[0], sizeof(param->fmt)); return PJ_SUCCESS; } static const struct cbar_fmt_info* get_cbar_fmt_info(pjmedia_format_id id) { unsigned i; for (i = 0; i < sizeof(cbar_fmts)/sizeof(cbar_fmts[0]); i++) { if (cbar_fmts[i].fmt_id == id) return &cbar_fmts[i]; } return NULL; } static void fill_first_line(pj_uint8_t *first_lines[], const struct cbar_fmt_info *cbfi, const pjmedia_video_format_info *vfi, const pjmedia_video_apply_fmt_param *vafp) { typedef pj_uint8_t color_comp_t[3]; color_comp_t rgb_colors[] = { {255,255,255}, {255,255,0}, {0,255,255}, {0,255,0}, {255,0,255}, {255,0,0}, {0,0,255}, {0,0,0} }; color_comp_t yuv_colors[] = { //{235,128,128}, {162,44,142}, {131,156,44}, {112,72,58}, //{84,184,198}, {65,100,212}, {35,212,114}, {16,128,128} {235,128,128}, {210,16,146}, {170,166,16}, {145,54,34}, {106,202,222}, {81,90,240}, {41,240,110}, {16,128,128} }; unsigned i, j, k; if (vfi->plane_cnt == 1) { /* Packed */ for (i = 0; i < 8; ++i) { /* iterate bars */ for (j = 0; j < 3; ++j) { /* iterate color components */ pj_uint8_t *p = NULL, c; unsigned bar_width, inc_p; if (vfi->color_model == PJMEDIA_COLOR_MODEL_RGB) c = rgb_colors[i][j]; else c = yuv_colors[i][j]; bar_width = vafp->size.w/8; bar_width /= (cbfi->c_stride[j] * 8 / vfi->bpp); inc_p = cbfi->c_stride[j]; p = first_lines[0] + bar_width*i*inc_p + cbfi->c_offset[j]; /* draw this color */ for (k = 0; k < bar_width; ++k) { *p = c; p += inc_p; } } } } else if (vfi->plane_cnt == 3) { for (i = 0; i < 8; ++i) { /* iterate bars */ for (j = 0; j < 3; ++j) { /* iterate planes/color components */ pj_uint8_t *p = NULL, c; unsigned bar_width; if (vfi->color_model == PJMEDIA_COLOR_MODEL_RGB) c = rgb_colors[i][j]; else { if (vfi->id == PJMEDIA_FORMAT_YV12 && j > 0) c = yuv_colors[i][3-j]; else c = yuv_colors[i][j]; } bar_width = vafp->strides[j]/8; p = first_lines[j] + bar_width*i; /* draw this plane/color */ for (k = 0; k < bar_width; ++k) *p++ = c; } } } } static void clock_cb(const pj_timestamp *ts, void *user_data) { struct cbar_stream *stream = (struct cbar_stream*)user_data; pjmedia_frame f; pj_status_t status; PJ_UNUSED_ARG(ts); pj_bzero(&f, sizeof(f)); f.buf = stream->clock_buf; f.size = stream->vafp.framebytes; status = cbar_stream_get_frame(&stream->base, &f); if (status == PJ_SUCCESS) { (*stream->vid_cb.capture_cb)(&stream->base, stream->user_data, &f); } } /* API: create stream */ static pj_status_t cbar_factory_create_stream( pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm) { struct cbar_factory *cf = (struct cbar_factory*)f; pj_pool_t *pool; struct cbar_stream *strm; const pjmedia_video_format_detail *vfd; const pjmedia_video_format_info *vfi; pjmedia_video_apply_fmt_param vafp; const struct cbar_fmt_info *cbfi; unsigned i; PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL); PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO && param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO && param->dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL); pj_bzero(&vafp, sizeof(vafp)); vfd = pjmedia_format_get_video_format_detail(¶m->fmt, PJ_TRUE); vfi = pjmedia_get_video_format_info(NULL, param->fmt.id); cbfi = get_cbar_fmt_info(param->fmt.id); if (!vfi || !cbfi) return PJMEDIA_EVID_BADFORMAT; vafp.size = param->fmt.det.vid.size; if (vfi->apply_fmt(vfi, &vafp) != PJ_SUCCESS) return PJMEDIA_EVID_BADFORMAT; /* Create and Initialize stream descriptor */ pool = pj_pool_create(cf->pf, "cbar-dev", 512, 512, NULL); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); strm = PJ_POOL_ZALLOC_T(pool, struct cbar_stream); pj_memcpy(&strm->param, param, sizeof(*param)); strm->pool = pool; pj_memcpy(&strm->vid_cb, cb, sizeof(*cb)); strm->user_data = user_data; strm->vfi = vfi; strm->cbfi = cbfi; pj_memcpy(&strm->vafp, &vafp, sizeof(vafp)); strm->ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1); for (i = 0; i < vfi->plane_cnt; ++i) { strm->first_line[i] = pj_pool_alloc(pool, vafp.strides[i]); pj_memset(strm->first_line[i], 255, vafp.strides[i]); } fill_first_line(strm->first_line, strm->cbfi, vfi, &strm->vafp); /* Apply the remaining settings */ /* if (param->flags & PJMEDIA_VID_DEV_CAP_INPUT_SCALE) { cbar_stream_set_cap(&strm->base, PJMEDIA_VID_DEV_CAP_INPUT_SCALE, ¶m->fmt); } */ /* Active role? */ if (param->cap_id == 1 && cb && cb->capture_cb) { pjmedia_clock_param clock_param; pj_status_t status; /* Allocate buffer */ strm->clock_buf = pj_pool_alloc(pool, strm->vafp.framebytes); /* Create clock */ pj_bzero(&clock_param, sizeof(clock_param)); clock_param.usec_interval = PJMEDIA_PTIME(&vfd->fps); clock_param.clock_rate = param->clock_rate; status = pjmedia_clock_create2(pool, &clock_param, PJMEDIA_CLOCK_NO_HIGHEST_PRIO, &clock_cb, strm, &strm->clock); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } } /* Done */ strm->base.op = &stream_op; *p_vid_strm = &strm->base; return PJ_SUCCESS; } /* API: Get stream info. */ static pj_status_t cbar_stream_get_param(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_param *pi) { struct cbar_stream *strm = (struct cbar_stream*)s; PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); pj_memcpy(pi, &strm->param, sizeof(*pi)); /* if (cbar_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_INPUT_SCALE, &pi->fmt.info_size) == PJ_SUCCESS) { pi->flags |= PJMEDIA_VID_DEV_CAP_INPUT_SCALE; } */ return PJ_SUCCESS; } /* API: get capability */ static pj_status_t cbar_stream_get_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, void *pval) { struct cbar_stream *strm = (struct cbar_stream*)s; PJ_UNUSED_ARG(strm); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE) { return PJMEDIA_EVID_INVCAP; // return PJ_SUCCESS; } else { return PJMEDIA_EVID_INVCAP; } } /* API: set capability */ static pj_status_t cbar_stream_set_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, const void *pval) { struct cbar_stream *strm = (struct cbar_stream*)s; PJ_UNUSED_ARG(strm); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE) { return PJ_SUCCESS; } return PJMEDIA_EVID_INVCAP; } static pj_status_t spectrum_run(struct cbar_stream *d, pj_uint8_t *p, pj_size_t size) { unsigned i; pj_uint8_t *ptr = p; pj_time_val tv; PJ_UNUSED_ARG(size); /* Subsequent lines */ for (i=0; ivfi->plane_cnt; ++i) { pj_uint8_t *plane_end; plane_end = ptr + d->vafp.plane_bytes[i]; while (ptr < plane_end) { pj_memcpy(ptr, d->first_line[i], d->vafp.strides[i]); ptr += d->vafp.strides[i]; } } /* blinking dot */ pj_gettimeofday(&tv); if (tv.msec < 660) { enum { DOT_SIZE = 8 }; pj_uint8_t dot_clr_rgb[3] = {255, 255, 255}; pj_uint8_t dot_clr_yuv[3] = {235, 128, 128}; if (d->vfi->plane_cnt == 1) { for (i = 0; i < 3; ++i) { pj_uint8_t *ptr; unsigned j, k, inc_ptr; pj_size_t dot_size = DOT_SIZE; dot_size /= (d->cbfi->c_stride[i] * 8 / d->vfi->bpp); inc_ptr = d->cbfi->c_stride[i]; for (j = 0; j < dot_size; ++j) { ptr = p + d->vafp.strides[0]*(dot_size+j+1) - 2*dot_size*inc_ptr + d->cbfi->c_offset[i]; for (k = 0; k < dot_size; ++k) { if (d->vfi->color_model == PJMEDIA_COLOR_MODEL_RGB) *ptr = dot_clr_rgb[i]; else *ptr = dot_clr_yuv[i]; ptr += inc_ptr; } } } } else { pj_size_t offset_p = 0; for (i = 0; i < 3; ++i) { pj_uint8_t *ptr, c; unsigned j; pj_size_t dot_size = DOT_SIZE; if (d->vfi->color_model == PJMEDIA_COLOR_MODEL_RGB) c = dot_clr_rgb[i]; else c = dot_clr_yuv[i]; dot_size /= (d->vafp.size.w / d->vafp.strides[i]); ptr = p + offset_p + d->vafp.strides[i]*(dot_size+1) - 2*dot_size; for (j = 0; j < dot_size; ++j) { pj_memset(ptr, c, dot_size); ptr += d->vafp.strides[i]; } offset_p += d->vafp.plane_bytes[i]; } } } return PJ_SUCCESS; } /* API: Get frame from stream */ static pj_status_t cbar_stream_get_frame(pjmedia_vid_dev_stream *strm, pjmedia_frame *frame) { struct cbar_stream *stream = (struct cbar_stream*)strm; frame->type = PJMEDIA_FRAME_TYPE_VIDEO; frame->bit_info = 0; frame->timestamp = stream->ts; stream->ts.u64 += stream->ts_inc; return spectrum_run(stream, frame->buf, frame->size); } /* API: Start stream. */ static pj_status_t cbar_stream_start(pjmedia_vid_dev_stream *strm) { struct cbar_stream *stream = (struct cbar_stream*)strm; PJ_LOG(4, (THIS_FILE, "Starting cbar video stream")); if (stream->clock) return pjmedia_clock_start(stream->clock); return PJ_SUCCESS; } /* API: Stop stream. */ static pj_status_t cbar_stream_stop(pjmedia_vid_dev_stream *strm) { struct cbar_stream *stream = (struct cbar_stream*)strm; PJ_LOG(4, (THIS_FILE, "Stopping cbar video stream")); if (stream->clock) return pjmedia_clock_stop(stream->clock); return PJ_SUCCESS; } /* API: Destroy stream. */ static pj_status_t cbar_stream_destroy(pjmedia_vid_dev_stream *strm) { struct cbar_stream *stream = (struct cbar_stream*)strm; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); cbar_stream_stop(strm); if (stream->clock) pjmedia_clock_destroy(stream->clock); stream->clock = NULL; pj_pool_release(stream->pool); return PJ_SUCCESS; } #endif /* PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-videodev/dshow_dev.c ================================================ /* $Id: dshow_dev.c 4253 2012-09-13 08:35:24Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && PJMEDIA_HAS_VIDEO != 0 && \ defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0 #include #define COBJMACROS #include #include #include #ifndef DIBSIZE # define WIDTHBYTES(BTIS) ((DWORD)(((BTIS)+31) & (~31)) / 8) # define DIBWIDTHBYTES(BI) (DWORD)(BI).biBitCount) * (DWORD)WIDTHBYTES((DWORD)(BI).biWidth # define _DIBSIZE(BI) (DIBWIDTHBYTES(BI) * (DWORD)(BI).biHeight) # define DIBSIZE(BI) ((BI).biHeight < 0 ? (-1)*(_DIBSIZE(BI)) : _DIBSIZE(BI)) #endif #define THIS_FILE "dshow_dev.c" #define DEFAULT_CLOCK_RATE 90000 #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 #define DEFAULT_FPS 25 /* Temporarily disable DirectShow renderer (VMR) */ #define HAS_VMR 0 typedef void (*input_callback)(void *user_data, IMediaSample *pMediaSample); typedef struct NullRenderer NullRenderer; IBaseFilter* NullRenderer_Create(input_callback input_cb, void *user_data); typedef struct dshow_fmt_info { pjmedia_format_id pjmedia_format; const GUID *dshow_format; pj_bool_t enabled; } dshow_fmt_info; static dshow_fmt_info dshow_fmts[] = { {PJMEDIA_FORMAT_YUY2, &MEDIASUBTYPE_YUY2, PJ_FALSE} , {PJMEDIA_FORMAT_RGB24, &MEDIASUBTYPE_RGB24, PJ_FALSE} , {PJMEDIA_FORMAT_RGB32, &MEDIASUBTYPE_RGB32, PJ_FALSE} , {PJMEDIA_FORMAT_IYUV, &MEDIASUBTYPE_IYUV, PJ_FALSE} , {PJMEDIA_FORMAT_I420, &WMMEDIASUBTYPE_I420, PJ_FALSE} }; /* dshow_ device info */ struct dshow_dev_info { pjmedia_vid_dev_info info; unsigned dev_id; WCHAR display_name[192]; }; /* dshow_ factory */ struct dshow_factory { pjmedia_vid_dev_factory base; pj_pool_t *pool; pj_pool_t *dev_pool; pj_pool_factory *pf; unsigned dev_count; struct dshow_dev_info *dev_info; }; /* Video stream. */ struct dshow_stream { pjmedia_vid_dev_stream base; /**< Base stream */ pjmedia_vid_dev_param param; /**< Settings */ pj_pool_t *pool; /**< Memory pool. */ pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */ void *user_data; /**< Application data. */ pj_bool_t quit_flag; pj_bool_t rend_thread_exited; pj_bool_t cap_thread_exited; pj_bool_t cap_thread_initialized; pj_thread_desc cap_thread_desc; pj_thread_t *cap_thread; void *frm_buf; unsigned frm_buf_size; struct dshow_graph { IFilterGraph *filter_graph; IMediaFilter *media_filter; IBaseFilter *source_filter; IBaseFilter *rend_filter; AM_MEDIA_TYPE *mediatype; } dgraph; pj_timestamp cap_ts; unsigned cap_ts_inc; }; /* Prototypes */ static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f); static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f); static pj_status_t dshow_factory_refresh(pjmedia_vid_dev_factory *f); static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f); static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info); static pj_status_t dshow_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param); static pj_status_t dshow_factory_create_stream( pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm); static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_param *param); static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, void *value); static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, const void *value); static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm); static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm); static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm); /* Operations */ static pjmedia_vid_dev_factory_op factory_op = { &dshow_factory_init, &dshow_factory_destroy, &dshow_factory_get_dev_count, &dshow_factory_get_dev_info, &dshow_factory_default_param, &dshow_factory_create_stream, &dshow_factory_refresh }; static pjmedia_vid_dev_stream_op stream_op = { &dshow_stream_get_param, &dshow_stream_get_cap, &dshow_stream_set_cap, &dshow_stream_start, NULL, NULL, &dshow_stream_stop, &dshow_stream_destroy }; /**************************************************************************** * Factory operations */ /* * Init dshow_ video driver. */ pjmedia_vid_dev_factory* pjmedia_dshow_factory(pj_pool_factory *pf) { struct dshow_factory *f; pj_pool_t *pool; pool = pj_pool_create(pf, "dshow video", 1000, 1000, NULL); f = PJ_POOL_ZALLOC_T(pool, struct dshow_factory); f->pf = pf; f->pool = pool; f->base.op = &factory_op; return &f->base; } /* API: init factory */ static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f) { HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (hr == RPC_E_CHANGED_MODE) { PJ_LOG(4,(THIS_FILE, "Failed initializing DShow: " "COM library already initialized with " "incompatible concurrency model")); return PJMEDIA_EVID_INIT; } return dshow_factory_refresh(f); } /* API: destroy factory */ static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f) { struct dshow_factory *df = (struct dshow_factory*)f; pj_pool_t *pool = df->pool; df->pool = NULL; if (df->dev_pool) pj_pool_release(df->dev_pool); if (pool) pj_pool_release(pool); CoUninitialize(); return PJ_SUCCESS; } static HRESULT get_cap_device(struct dshow_factory *df, unsigned id, IBaseFilter **filter) { IBindCtx *pbc; HRESULT hr; hr = CreateBindCtx(0, &pbc); if (SUCCEEDED (hr)) { IMoniker *moniker; DWORD pchEaten; hr = MkParseDisplayName(pbc, df->dev_info[id].display_name, &pchEaten, &moniker); if (SUCCEEDED(hr)) { hr = IMoniker_BindToObject(moniker, pbc, NULL, &IID_IBaseFilter, (LPVOID *)filter); IMoniker_Release(moniker); } IBindCtx_Release(pbc); } return hr; } static void enum_dev_cap(IBaseFilter *filter, pjmedia_dir dir, const GUID *dshow_fmt, AM_MEDIA_TYPE **pMediatype, int width, int height, IPin **pSrcpin, pjmedia_vid_dev_info *vdi) { IEnumPins *pEnum; AM_MEDIA_TYPE *mediatype = NULL; pj_bool_t match_wh = PJ_FALSE; HRESULT hr; if (pSrcpin) *pSrcpin = NULL; hr = IBaseFilter_EnumPins(filter, &pEnum); if (SUCCEEDED(hr)) { /* Loop through all the pins. */ IPin *pPin = NULL; while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) { PIN_DIRECTION pindirtmp; hr = IPin_QueryDirection(pPin, &pindirtmp); if (hr != S_OK || pindirtmp != PINDIR_OUTPUT) { if (SUCCEEDED(hr)) IPin_Release(pPin); continue; } if (dir == PJMEDIA_DIR_CAPTURE) { IAMStreamConfig *streamcaps; hr = IPin_QueryInterface(pPin, &IID_IAMStreamConfig, (LPVOID *)&streamcaps); if (SUCCEEDED(hr)) { VIDEO_STREAM_CONFIG_CAPS vscc; int i, isize, icount; IAMStreamConfig_GetNumberOfCapabilities(streamcaps, &icount, &isize); for (i = 0; i < icount; i++) { unsigned j, nformat; RPC_STATUS rpcstatus, rpcstatus2; hr = IAMStreamConfig_GetStreamCaps(streamcaps, i, &mediatype, (BYTE *)&vscc); if (FAILED (hr)) continue; nformat = (dshow_fmt? 1: sizeof(dshow_fmts)/sizeof(dshow_fmts[0])); for (j = 0; j < nformat; j++) { const GUID *dshow_format = dshow_fmt; if (!dshow_format) dshow_format = dshow_fmts[j].dshow_format; if (UuidCompare(&mediatype->subtype, (UUID*)dshow_format, &rpcstatus) == 0 && rpcstatus == RPC_S_OK && UuidCompare(&mediatype->formattype, (UUID*)&FORMAT_VideoInfo, &rpcstatus2) == 0 && rpcstatus2 == RPC_S_OK) { VIDEOINFOHEADER *vi; vi = (VIDEOINFOHEADER *)mediatype->pbFormat; if (!dshow_fmt) dshow_fmts[j].enabled = PJ_TRUE; if (vdi && vdi->fmt_cnt < PJMEDIA_VID_DEV_INFO_FMT_CNT) { unsigned fps_num=DEFAULT_FPS, fps_denum=1; if (vi->AvgTimePerFrame != 0) { fps_num = 10000000; fps_denum=(unsigned)vi->AvgTimePerFrame; } pjmedia_format_init_video( &vdi->fmt[vdi->fmt_cnt++], dshow_fmts[j].pjmedia_format, vi->bmiHeader.biWidth, vi->bmiHeader.biHeight, fps_num, fps_denum); } if (pSrcpin) { if ((width == 0 && height == 0 ) || (vi->bmiHeader.biWidth == width && vi->bmiHeader.biHeight == height)) { match_wh = PJ_TRUE; } *pSrcpin = pPin; *pMediatype = mediatype; } } } if (pSrcpin && *pSrcpin && match_wh) break; } IAMStreamConfig_Release(streamcaps); } } else { *pSrcpin = pPin; } if (pSrcpin && *pSrcpin) break; IPin_Release(pPin); } IEnumPins_Release(pEnum); } } /* API: refresh the list of devices */ static pj_status_t dshow_factory_refresh(pjmedia_vid_dev_factory *f) { struct dshow_factory *df = (struct dshow_factory*)f; struct dshow_dev_info *ddi; int dev_count = 0; unsigned c; ICreateDevEnum *dev_enum = NULL; IEnumMoniker *enum_cat = NULL; IMoniker *moniker = NULL; HRESULT hr; ULONG fetched; if (df->dev_pool) { pj_pool_release(df->dev_pool); df->dev_pool = NULL; } for (c = 0; c < sizeof(dshow_fmts) / sizeof(dshow_fmts[0]); c++) { dshow_fmts[c].enabled = PJ_FALSE; } df->dev_count = 0; df->dev_pool = pj_pool_create(df->pf, "dshow video", 500, 500, NULL); hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum, (void**)&dev_enum); if (FAILED(hr) || ICreateDevEnum_CreateClassEnumerator(dev_enum, &CLSID_VideoInputDeviceCategory, &enum_cat, 0) != S_OK) { PJ_LOG(4,(THIS_FILE, "Windows found no video input devices")); if (dev_enum) ICreateDevEnum_Release(dev_enum); dev_count = 0; } else { while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) { dev_count++; } } /* Add renderer device */ dev_count += 1; df->dev_info = (struct dshow_dev_info*) pj_pool_calloc(df->dev_pool, dev_count, sizeof(struct dshow_dev_info)); if (dev_count > 1) { IEnumMoniker_Reset(enum_cat); while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) { IPropertyBag *prop_bag; hr = IMoniker_BindToStorage(moniker, 0, 0, &IID_IPropertyBag, (void**)&prop_bag); if (SUCCEEDED(hr)) { VARIANT var_name; VariantInit(&var_name); hr = IPropertyBag_Read(prop_bag, L"FriendlyName", &var_name, NULL); if (SUCCEEDED(hr) && var_name.bstrVal) { WCHAR *wszDisplayName = NULL; IBaseFilter *filter; pj_ssize_t len; ddi = &df->dev_info[df->dev_count++]; pj_bzero(ddi, sizeof(*ddi)); len = wcslen(var_name.bstrVal), len = WideCharToMultiByte(CP_ACP, 0, var_name.bstrVal, (int)len, ddi->info.name, sizeof(ddi->info.name), NULL, NULL); ddi->info.name[len] = '\0'; hr = IMoniker_GetDisplayName(moniker, NULL, NULL, &wszDisplayName); if (hr == S_OK && wszDisplayName) { pj_memcpy(ddi->display_name, wszDisplayName, (wcslen(wszDisplayName)+1) * sizeof(WCHAR)); CoTaskMemFree(wszDisplayName); } strncpy(ddi->info.driver, "dshow", sizeof(ddi->info.driver)); ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0'; ddi->info.dir = PJMEDIA_DIR_CAPTURE; ddi->info.has_callback = PJ_TRUE; /* Set the device capabilities here */ ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT; hr = get_cap_device(df, df->dev_count-1, &filter); if (SUCCEEDED(hr)) { ddi->info.fmt_cnt = 0; enum_dev_cap(filter, ddi->info.dir, NULL, NULL, 0, 0, NULL, &ddi->info); } } VariantClear(&var_name); IPropertyBag_Release(prop_bag); } IMoniker_Release(moniker); } IEnumMoniker_Release(enum_cat); ICreateDevEnum_Release(dev_enum); } PJ_LOG(4, (THIS_FILE, "DShow has %d devices:", df->dev_count)); for (c = 0; c < df->dev_count; ++c) { PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (%s)", c, df->dev_info[c].info.name, df->dev_info[c].info.dir & PJMEDIA_DIR_CAPTURE ? "capture" : "render")); } return PJ_SUCCESS; } /* API: get number of devices */ static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f) { struct dshow_factory *df = (struct dshow_factory*)f; return df->dev_count; } /* API: get device info */ static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info) { struct dshow_factory *df = (struct dshow_factory*)f; PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV); pj_memcpy(info, &df->dev_info[index].info, sizeof(*info)); return PJ_SUCCESS; } /* API: create default device parameter */ static pj_status_t dshow_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param) { struct dshow_factory *df = (struct dshow_factory*)f; struct dshow_dev_info *di = &df->dev_info[index]; PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV); PJ_UNUSED_ARG(pool); pj_bzero(param, sizeof(*param)); if (di->info.dir & PJMEDIA_DIR_CAPTURE) { param->dir = PJMEDIA_DIR_CAPTURE; param->cap_id = index; param->rend_id = PJMEDIA_VID_INVALID_DEV; } else { return PJMEDIA_EVID_INVDEV; } /* Set the device capabilities here */ param->clock_rate = DEFAULT_CLOCK_RATE; param->flags = PJMEDIA_VID_DEV_CAP_FORMAT; pjmedia_format_copy(¶m->fmt, &di->info.fmt[0]); return PJ_SUCCESS; } static void input_cb(void *user_data, IMediaSample *pMediaSample) { struct dshow_stream *strm = (struct dshow_stream*)user_data; pjmedia_frame frame = {0}; if (strm->quit_flag) { strm->cap_thread_exited = PJ_TRUE; return; } if (strm->cap_thread_initialized == 0 || !pj_thread_is_registered()) { pj_status_t status; status = pj_thread_register("ds_cap", strm->cap_thread_desc, &strm->cap_thread); if (status != PJ_SUCCESS) return; strm->cap_thread_initialized = 1; PJ_LOG(5,(THIS_FILE, "Capture thread started")); } frame.type = PJMEDIA_FRAME_TYPE_VIDEO; IMediaSample_GetPointer(pMediaSample, (BYTE **)&frame.buf); frame.size = IMediaSample_GetActualDataLength(pMediaSample); frame.bit_info = 0; frame.timestamp = strm->cap_ts; strm->cap_ts.u64 += strm->cap_ts_inc; if (strm->frm_buf_size) { unsigned i, stride; BYTE *src_buf, *dst_buf; pjmedia_video_format_detail *vfd; /* Image is bottom-up, convert it to top-down. */ src_buf = dst_buf = (BYTE *)frame.buf; stride = strm->frm_buf_size; vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE); src_buf += (vfd->size.h - 1) * stride; for (i = vfd->size.h / 2; i > 0; i--) { memcpy(strm->frm_buf, dst_buf, stride); memcpy(dst_buf, src_buf, stride); memcpy(src_buf, strm->frm_buf, stride); dst_buf += stride; src_buf -= stride; } } if (strm->vid_cb.capture_cb) (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &frame); } static dshow_fmt_info* get_dshow_format_info(pjmedia_format_id id) { unsigned i; for (i = 0; i < sizeof(dshow_fmts)/sizeof(dshow_fmts[0]); i++) { if (dshow_fmts[i].pjmedia_format == id && dshow_fmts[i].enabled) return &dshow_fmts[i]; } return NULL; } static pj_status_t create_filter_graph(pjmedia_dir dir, unsigned id, pj_bool_t use_def_size, pj_bool_t use_def_fps, struct dshow_factory *df, struct dshow_stream *strm, struct dshow_graph *graph) { HRESULT hr; IEnumPins *pEnum; IPin *srcpin = NULL; IPin *sinkpin = NULL; AM_MEDIA_TYPE *mediatype = NULL; VIDEOINFOHEADER *video_info, *vi = NULL; pjmedia_video_format_detail *vfd; const pjmedia_video_format_info *vfi; PJ_ASSERT_RETURN(dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL); vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(), strm->param.fmt.id); if (!vfi) return PJMEDIA_EVID_BADFORMAT; hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (FAILED(hr)) { PJ_LOG(4,(THIS_FILE, "Error: CoInitializeEx")); goto on_error; } hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC, &IID_IFilterGraph, (LPVOID *)&graph->filter_graph); if (FAILED(hr)) { goto on_error; } hr = IFilterGraph_QueryInterface(graph->filter_graph, &IID_IMediaFilter, (LPVOID *)&graph->media_filter); if (FAILED(hr)) { goto on_error; } hr = get_cap_device(df, id, &graph->source_filter); if (FAILED(hr)) { goto on_error; } hr = IFilterGraph_AddFilter(graph->filter_graph, graph->source_filter, L"capture"); if (FAILED(hr)) { goto on_error; } graph->rend_filter = NullRenderer_Create(input_cb, strm); IBaseFilter_EnumPins(graph->rend_filter, &pEnum); if (SUCCEEDED(hr)) { // Loop through all the pins IPin *pPin = NULL; while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) { PIN_DIRECTION pindirtmp; hr = IPin_QueryDirection(pPin, &pindirtmp); if (hr == S_OK && pindirtmp == PINDIR_INPUT) { sinkpin = pPin; break; } IPin_Release(pPin); } IEnumPins_Release(pEnum); } vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE); enum_dev_cap(graph->source_filter, dir, get_dshow_format_info(strm->param.fmt.id)->dshow_format, &mediatype, (use_def_size? 0: vfd->size.w), (use_def_size? 0: vfd->size.h), &srcpin, NULL); graph->mediatype = mediatype; if (!srcpin || !sinkpin || !mediatype) { hr = VFW_E_TYPE_NOT_ACCEPTED; goto on_error; } video_info = (VIDEOINFOHEADER *) mediatype->pbFormat; if (!use_def_size) { video_info->bmiHeader.biWidth = vfd->size.w; video_info->bmiHeader.biHeight = vfd->size.h; } if (video_info->AvgTimePerFrame == 0 || (!use_def_fps && vfd->fps.num != 0)) { video_info->AvgTimePerFrame = (LONGLONG) (10000000 * (double)vfd->fps.denum / vfd->fps.num); } video_info->bmiHeader.biSizeImage = DIBSIZE(video_info->bmiHeader); mediatype->lSampleSize = DIBSIZE(video_info->bmiHeader); hr = IFilterGraph_AddFilter(graph->filter_graph, (IBaseFilter *)graph->rend_filter, L"renderer"); if (FAILED(hr)) goto on_error; hr = IFilterGraph_ConnectDirect(graph->filter_graph, srcpin, sinkpin, mediatype); if (SUCCEEDED(hr)) { if (use_def_size || use_def_fps) { pjmedia_format_init_video(&strm->param.fmt, strm->param.fmt.id, video_info->bmiHeader.biWidth, video_info->bmiHeader.biHeight, 10000000, (unsigned)video_info->AvgTimePerFrame); } strm->frm_buf_size = 0; if (dir == PJMEDIA_DIR_CAPTURE && video_info->bmiHeader.biCompression == BI_RGB && video_info->bmiHeader.biHeight > 0) { /* Allocate buffer to flip the captured image. */ strm->frm_buf_size = (video_info->bmiHeader.biBitCount >> 3) * video_info->bmiHeader.biWidth; strm->frm_buf = pj_pool_alloc(strm->pool, strm->frm_buf_size); } } on_error: if (srcpin) IPin_Release(srcpin); if (sinkpin) IPin_Release(sinkpin); if (vi) CoTaskMemFree(vi); if (FAILED(hr)) { char msg[80]; if (AMGetErrorText(hr, msg, sizeof(msg))) { PJ_LOG(4,(THIS_FILE, "Error creating filter graph: %s (hr=0x%x)", msg, hr)); } return PJ_EUNKNOWN; } return PJ_SUCCESS; } static void destroy_filter_graph(struct dshow_stream * stream) { if (stream->dgraph.source_filter) { IBaseFilter_Release(stream->dgraph.source_filter); stream->dgraph.source_filter = NULL; } if (stream->dgraph.rend_filter) { IBaseFilter_Release(stream->dgraph.rend_filter); stream->dgraph.rend_filter = NULL; } if (stream->dgraph.media_filter) { IMediaFilter_Release(stream->dgraph.media_filter); stream->dgraph.media_filter = NULL; } if (stream->dgraph.filter_graph) { IFilterGraph_Release(stream->dgraph.filter_graph); stream->dgraph.filter_graph = NULL; } } /* API: create stream */ static pj_status_t dshow_factory_create_stream( pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm) { struct dshow_factory *df = (struct dshow_factory*)f; pj_pool_t *pool; struct dshow_stream *strm; pj_status_t status; const pjmedia_video_format_detail *vfd; PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL); if (!get_dshow_format_info(param->fmt.id)) return PJMEDIA_EVID_BADFORMAT; /* Create and Initialize stream descriptor */ pool = pj_pool_create(df->pf, "dshow-dev", 1000, 1000, NULL); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); strm = PJ_POOL_ZALLOC_T(pool, struct dshow_stream); pj_memcpy(&strm->param, param, sizeof(*param)); strm->pool = pool; pj_memcpy(&strm->vid_cb, cb, sizeof(*cb)); strm->user_data = user_data; /* Create capture stream here */ status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id, PJ_FALSE, PJ_FALSE, df, strm, &strm->dgraph); if (status != PJ_SUCCESS) { destroy_filter_graph(strm); /* Try to use default fps */ PJ_LOG(4,(THIS_FILE, "Trying to open dshow dev with default fps")); status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id, PJ_FALSE, PJ_TRUE, df, strm, &strm->dgraph); if (status != PJ_SUCCESS) { /* Still failed, now try to use default fps and size */ destroy_filter_graph(strm); /* Try to use default fps */ PJ_LOG(4,(THIS_FILE, "Trying to open dshow dev with default " "size & fps")); status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id, PJ_TRUE, PJ_TRUE, df, strm, &strm->dgraph); } if (status != PJ_SUCCESS) goto on_error; pj_memcpy(param, &strm->param, sizeof(*param)); } vfd = pjmedia_format_get_video_format_detail(¶m->fmt, PJ_TRUE); strm->cap_ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1); /* Done */ strm->base.op = &stream_op; *p_vid_strm = &strm->base; return PJ_SUCCESS; on_error: dshow_stream_destroy((pjmedia_vid_dev_stream *)strm); return status; } /* API: Get stream info. */ static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_param *pi) { struct dshow_stream *strm = (struct dshow_stream*)s; PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); pj_memcpy(pi, &strm->param, sizeof(*pi)); if (dshow_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW, &pi->window) == PJ_SUCCESS) { pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW; } return PJ_SUCCESS; } /* API: get capability */ static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, void *pval) { struct dshow_stream *strm = (struct dshow_stream*)s; PJ_UNUSED_ARG(strm); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) { *(unsigned*)pval = 0; return PJ_SUCCESS; } else { return PJMEDIA_EVID_INVCAP; } } /* API: set capability */ static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, const void *pval) { struct dshow_stream *strm = (struct dshow_stream*)s; PJ_UNUSED_ARG(strm); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) { // set renderer's window here return PJ_SUCCESS; } return PJMEDIA_EVID_INVCAP; } /* API: Start stream. */ static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm) { struct dshow_stream *stream = (struct dshow_stream*)strm; HRESULT hr; stream->quit_flag = PJ_FALSE; stream->cap_thread_exited = PJ_FALSE; stream->rend_thread_exited = PJ_FALSE; hr = IMediaFilter_Run(stream->dgraph.media_filter, 0); if (FAILED(hr)) { char msg[80]; if (AMGetErrorText(hr, msg, sizeof(msg))) { PJ_LOG(4,(THIS_FILE, "Error starting media: %s", msg)); } return PJ_EUNKNOWN; } PJ_LOG(4, (THIS_FILE, "Starting dshow video stream")); return PJ_SUCCESS; } /* API: Stop stream. */ static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm) { struct dshow_stream *stream = (struct dshow_stream*)strm; unsigned i; stream->quit_flag = PJ_TRUE; if (stream->cap_thread) { for (i=0; !stream->cap_thread_exited && i<100; ++i) pj_thread_sleep(10); } for (i=0; !stream->rend_thread_exited && i<100; ++i) pj_thread_sleep(10); if (stream->dgraph.media_filter) IMediaFilter_Stop(stream->dgraph.media_filter); PJ_LOG(4, (THIS_FILE, "Stopping dshow video stream")); return PJ_SUCCESS; } /* API: Destroy stream. */ static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm) { struct dshow_stream *stream = (struct dshow_stream*)strm; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); dshow_stream_stop(strm); destroy_filter_graph(stream); pj_pool_release(stream->pool); return PJ_SUCCESS; } #endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-videodev/dshow_filter.cpp ================================================ /* $Id: dshowclasses.cpp 4062 2012-04-19 06:36:57Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0 #include #include #include typedef void (*input_callback)(void *user_data, IMediaSample *pMediaSample); const GUID CLSID_NullRenderer = {0xF9168C5E, 0xCEB2, 0x4FAA, {0xB6, 0xBF, 0x32, 0x9B, 0xF3, 0x9F, 0xA1, 0xE4}}; class NullRenderer: public CBaseRenderer { public: NullRenderer(HRESULT *pHr); virtual ~NullRenderer(); virtual HRESULT CheckMediaType(const CMediaType *pmt); virtual HRESULT DoRenderSample(IMediaSample *pMediaSample); input_callback input_cb; void *user_data; }; NullRenderer::NullRenderer(HRESULT *pHr): CBaseRenderer(CLSID_NullRenderer, "NullRenderer", NULL, pHr) { input_cb = NULL; } NullRenderer::~NullRenderer() { } HRESULT NullRenderer::CheckMediaType(const CMediaType *pmt) { return S_OK; } HRESULT NullRenderer::DoRenderSample(IMediaSample *pMediaSample) { if (input_cb) input_cb(user_data, pMediaSample); return S_OK; } extern "C" IBaseFilter* NullRenderer_Create(input_callback input_cb, void *user_data) { HRESULT hr; NullRenderer *renderer = new NullRenderer(&hr); renderer->AddRef(); renderer->input_cb = input_cb; renderer->user_data = user_data; return (CBaseFilter *)renderer; } #endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-videodev/errno.c ================================================ /* $Id: errno.c 3715 2011-08-19 09:35:25Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include /* PJMEDIA-videodev's own error codes/messages * MUST KEEP THIS ARRAY SORTED!! * Message must be limited to 64 chars! */ #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) static const struct { int code; const char *msg; } err_str[] = { PJ_BUILD_ERR( PJMEDIA_EVID_ERR, "Unspecified video device error" ), PJ_BUILD_ERR( PJMEDIA_EVID_SYSERR, "Unknown error from video driver" ), PJ_BUILD_ERR( PJMEDIA_EVID_INIT, "video subsystem not initialized" ), PJ_BUILD_ERR( PJMEDIA_EVID_INVDEV, "Invalid video device" ), PJ_BUILD_ERR( PJMEDIA_EVID_NODEV, "Found no video devices" ), PJ_BUILD_ERR( PJMEDIA_EVID_NODEFDEV, "Unable to find default video device" ), PJ_BUILD_ERR( PJMEDIA_EVID_NOTREADY, "video device not ready" ), PJ_BUILD_ERR( PJMEDIA_EVID_INVCAP, "Invalid or unsupported video capability" ), PJ_BUILD_ERR( PJMEDIA_EVID_INVOP, "Invalid or unsupported video device operation" ), PJ_BUILD_ERR( PJMEDIA_EVID_BADFORMAT, "Bad or invalid video device format" ), PJ_BUILD_ERR( PJMEDIA_EVID_SAMPFORMAT, "Invalid video device sample format"), PJ_BUILD_ERR( PJMEDIA_EVID_BADLATENCY, "Bad video latency setting") }; #endif /* PJ_HAS_ERROR_STRING */ /* * pjmedia_videodev_strerror() */ PJ_DEF(pj_str_t) pjmedia_videodev_strerror(pj_status_t statcode, char *buf, pj_size_t bufsize ) { pj_str_t errstr; #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) /* videodev error */ if (statcode >= PJMEDIA_VIDEODEV_ERRNO_START && statcode < PJMEDIA_VIDEODEV_ERRNO_END) { /* Find the error in the table. * Use binary search! */ int first = 0; int n = PJ_ARRAY_SIZE(err_str); while (n > 0) { int half = n/2; int mid = first + half; if (err_str[mid].code < statcode) { first = mid+1; n -= (half+1); } else if (err_str[mid].code > statcode) { n = half; } else { first = mid; break; } } if (PJ_ARRAY_SIZE(err_str) && err_str[first].code == statcode) { pj_str_t msg; msg.ptr = (char*)err_str[first].msg; msg.slen = pj_ansi_strlen(err_str[first].msg); errstr.ptr = buf; pj_strncpy_with_null(&errstr, &msg, bufsize); return errstr; } } #endif /* PJ_HAS_ERROR_STRING */ /* Error not found. */ errstr.ptr = buf; errstr.slen = pj_ansi_snprintf(buf, bufsize, "Unknown pjmedia-videodev error %d", statcode); if (errstr.slen < 1 || errstr.slen >= (pj_ssize_t)bufsize) errstr.slen = bufsize - 1; return errstr; } #endif /* PJMEDIA_HAS_VIDEO */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-videodev/fb_dev.c ================================================ /* * Copyright (C) 2014-present AG Projects * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && PJMEDIA_HAS_VIDEO != 0 && \ defined(PJMEDIA_VIDEO_DEV_HAS_FB) && PJMEDIA_VIDEO_DEV_HAS_FB != 0 #include #define THIS_FILE "fb_dev.c" #define DEFAULT_CLOCK_RATE 90000 #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 #define DEFAULT_FPS 25 /* Supported formats */ #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 static pjmedia_format_id fb_fmts[] = {PJMEDIA_FORMAT_ARGB}; #else static pjmedia_format_id fb_fmts[] = {PJMEDIA_FORMAT_BGRA}; #endif /* fb device info */ struct fb_dev_info { pjmedia_vid_dev_info info; }; /* factory */ struct fb_factory { pjmedia_vid_dev_factory base; pj_pool_t *pool; pj_pool_factory *pf; unsigned dev_count; struct fb_dev_info *dev_info; }; /* Video stream. */ struct fb_stream { pjmedia_vid_dev_stream base; /**< Base stream */ pjmedia_vid_dev_param param; /**< Settings */ pj_pool_t *pool; /**< Memory pool. */ pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */ void *user_data; /**< Application data. */ struct fb_factory *ff; pj_bool_t is_running; pjmedia_rect_size vid_size; struct { pjmedia_vid_dev_fb_frame_cb cb; void *user_data; } frame_handler; }; /* Prototypes */ static pj_status_t fb_factory_init(pjmedia_vid_dev_factory *f); static pj_status_t fb_factory_destroy(pjmedia_vid_dev_factory *f); static pj_status_t fb_factory_refresh(pjmedia_vid_dev_factory *f); static unsigned fb_factory_get_dev_count(pjmedia_vid_dev_factory *f); static pj_status_t fb_factory_get_dev_info(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info); static pj_status_t fb_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param); static pj_status_t fb_factory_create_stream(pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm); static pj_status_t fb_stream_get_param(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_param *param); static pj_status_t fb_stream_get_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, void *value); static pj_status_t fb_stream_set_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, const void *value); static pj_status_t fb_stream_put_frame(pjmedia_vid_dev_stream *strm, const pjmedia_frame *frame); static pj_status_t fb_stream_start(pjmedia_vid_dev_stream *strm); static pj_status_t fb_stream_stop(pjmedia_vid_dev_stream *strm); static pj_status_t fb_stream_destroy(pjmedia_vid_dev_stream *strm); /* Operations */ static pjmedia_vid_dev_factory_op factory_op = { &fb_factory_init, &fb_factory_destroy, &fb_factory_get_dev_count, &fb_factory_get_dev_info, &fb_factory_default_param, &fb_factory_create_stream, &fb_factory_refresh }; static pjmedia_vid_dev_stream_op stream_op = { &fb_stream_get_param, &fb_stream_get_cap, &fb_stream_set_cap, &fb_stream_start, NULL, &fb_stream_put_frame, &fb_stream_stop, &fb_stream_destroy }; /**************************************************************************** * Factory operations */ /* * Init FB video driver. */ pjmedia_vid_dev_factory* pjmedia_fb_factory(pj_pool_factory *pf) { struct fb_factory *f; pj_pool_t *pool; pool = pj_pool_create(pf, "fb video", 1000, 1000, NULL); f = PJ_POOL_ZALLOC_T(pool, struct fb_factory); f->pf = pf; f->pool = pool; f->base.op = &factory_op; return &f->base; } /* API: init factory */ static pj_status_t fb_factory_init(pjmedia_vid_dev_factory *f) { struct fb_factory *ff = (struct fb_factory*)f; struct fb_dev_info *di; unsigned i, l; /* Initialize input and output devices here */ ff->dev_info = (struct fb_dev_info*) pj_pool_calloc(ff->pool, 1, sizeof(struct fb_dev_info)); ff->dev_count = 0; di = &ff->dev_info[ff->dev_count++]; pj_bzero(di, sizeof(*di)); strcpy(di->info.name, "FrameBuffer renderer"); strcpy(di->info.driver, "FrameBuffer"); di->info.dir = PJMEDIA_DIR_RENDER; di->info.has_callback = PJ_FALSE; di->info.caps = 0; for (i = 0; i < ff->dev_count; i++) { di = &ff->dev_info[i]; di->info.fmt_cnt = PJ_ARRAY_SIZE(fb_fmts); di->info.caps |= PJMEDIA_VID_DEV_CAP_FORMAT; for (l = 0; l < PJ_ARRAY_SIZE(fb_fmts); l++) { pjmedia_format *fmt = &di->info.fmt[l]; pjmedia_format_init_video(fmt, fb_fmts[l], DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FPS, 1); } } PJ_LOG(4, (THIS_FILE, "FrameBuffer initialized")); return PJ_SUCCESS; } /* API: destroy factory */ static pj_status_t fb_factory_destroy(pjmedia_vid_dev_factory *f) { struct fb_factory *ff = (struct fb_factory*)f; pj_pool_t *pool = ff->pool; ff->pool = NULL; pj_pool_release(pool); return PJ_SUCCESS; } /* API: refresh the list of devices */ static pj_status_t fb_factory_refresh(pjmedia_vid_dev_factory *f) { PJ_UNUSED_ARG(f); return PJ_SUCCESS; } /* API: get number of devices */ static unsigned fb_factory_get_dev_count(pjmedia_vid_dev_factory *f) { struct fb_factory *ff = (struct fb_factory*)f; return ff->dev_count; } /* API: get device info */ static pj_status_t fb_factory_get_dev_info(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info) { struct fb_factory *ff = (struct fb_factory*)f; PJ_ASSERT_RETURN(index < ff->dev_count, PJMEDIA_EVID_INVDEV); pj_memcpy(info, &ff->dev_info[index].info, sizeof(*info)); return PJ_SUCCESS; } /* API: create default device parameter */ static pj_status_t fb_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param) { struct fb_factory *ff = (struct fb_factory*)f; struct fb_dev_info *di = &ff->dev_info[index]; PJ_ASSERT_RETURN(index < ff->dev_count, PJMEDIA_EVID_INVDEV); PJ_UNUSED_ARG(pool); pj_bzero(param, sizeof(*param)); param->dir = PJMEDIA_DIR_RENDER; param->rend_id = index; param->cap_id = PJMEDIA_VID_INVALID_DEV; /* Set the device capabilities here */ param->flags = PJMEDIA_VID_DEV_CAP_FORMAT; param->clock_rate = DEFAULT_CLOCK_RATE; pj_memcpy(¶m->fmt, &di->info.fmt[0], sizeof(param->fmt)); return PJ_SUCCESS; } /* API: Put frame from stream */ static pj_status_t fb_stream_put_frame(pjmedia_vid_dev_stream *strm, const pjmedia_frame *frame) { struct fb_stream *stream = (struct fb_stream*)strm; if (!stream->is_running) return PJ_EINVALIDOP; if (frame->size==0 || frame->buf==NULL) return PJ_SUCCESS; if (stream->frame_handler.cb) stream->frame_handler.cb(frame, stream->vid_size, stream->frame_handler.user_data); return PJ_SUCCESS; } /* API: create stream */ static pj_status_t fb_factory_create_stream(pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm) { struct fb_factory *ff = (struct fb_factory*)f; pj_pool_t *pool; pj_status_t status; struct fb_stream *strm; const pjmedia_video_format_info *vfi; PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL); PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_RENDER, PJ_EINVAL); PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO && param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO && param->dir == PJMEDIA_DIR_RENDER, PJ_EINVAL); vfi = pjmedia_get_video_format_info(NULL, param->fmt.id); if (!vfi) return PJMEDIA_EVID_BADFORMAT; /* Create and Initialize stream descriptor */ pool = pj_pool_create(ff->pf, "fb-dev", 1000, 1000, NULL); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); strm = PJ_POOL_ZALLOC_T(pool, struct fb_stream); pj_memcpy(&strm->param, param, sizeof(*param)); strm->pool = pool; strm->ff = ff; pj_memcpy(&strm->vid_cb, cb, sizeof(*cb)); strm->user_data = user_data; status = fb_stream_set_cap(&strm->base, PJMEDIA_VID_DEV_CAP_FORMAT, ¶m->fmt); if (status != PJ_SUCCESS) { fb_stream_destroy((pjmedia_vid_dev_stream *)strm); return status; } /* Done */ strm->base.op = &stream_op; *p_vid_strm = &strm->base; return PJ_SUCCESS; } /* API: Get stream info. */ static pj_status_t fb_stream_get_param(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_param *pi) { struct fb_stream *strm = (struct fb_stream*)s; PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); pj_memcpy(pi, &strm->param, sizeof(*pi)); return PJ_SUCCESS; } /* API: get capability */ static pj_status_t fb_stream_get_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, void *pval) { struct fb_stream *strm = (struct fb_stream*)s; PJ_UNUSED_ARG(strm); PJ_UNUSED_ARG(cap); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); return PJMEDIA_EVID_INVCAP; } /* API: set capability */ static pj_status_t fb_stream_set_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, const void *pval) { struct fb_stream *strm = (struct fb_stream*)s; PJ_UNUSED_ARG(strm); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); if (cap == PJMEDIA_VID_DEV_CAP_FORMAT) { const pjmedia_video_format_info *vfi; pjmedia_video_format_detail *vfd; pjmedia_format *fmt = (pjmedia_format *)pval; vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(), fmt->id); if (!vfi) return PJMEDIA_EVID_BADFORMAT; pjmedia_format_copy(&strm->param.fmt, fmt); vfd = pjmedia_format_get_video_format_detail(fmt, PJ_TRUE); pj_memcpy(&strm->vid_size, &vfd->size, sizeof(vfd->size)); if (strm->param.disp_size.w == 0 || strm->param.disp_size.h == 0) pj_memcpy(&strm->param.disp_size, &vfd->size, sizeof(vfd->size)); return PJ_SUCCESS; } return PJMEDIA_EVID_INVCAP; } /* API: Start stream. */ static pj_status_t fb_stream_start(pjmedia_vid_dev_stream *strm) { struct fb_stream *stream = (struct fb_stream*)strm; PJ_UNUSED_ARG(strm); PJ_LOG(4, (THIS_FILE, "Starting FB video stream")); stream->is_running = PJ_TRUE; return PJ_SUCCESS; } /* API: Stop stream. */ static pj_status_t fb_stream_stop(pjmedia_vid_dev_stream *strm) { struct fb_stream *stream = (struct fb_stream*)strm; PJ_UNUSED_ARG(strm); PJ_LOG(4, (THIS_FILE, "Stopping FB video stream")); stream->is_running = PJ_FALSE; return PJ_SUCCESS; } /* API: Destroy stream. */ static pj_status_t fb_stream_destroy(pjmedia_vid_dev_stream *strm) { struct fb_stream *stream = (struct fb_stream*)strm; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); fb_stream_stop(strm); pj_pool_release(stream->pool); return PJ_SUCCESS; } /* API: set callback for handling frames */ pj_status_t pjmedia_vid_dev_fb_set_callback(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_fb_frame_cb cb, void *user_data) { struct fb_stream *stream = (struct fb_stream*)strm; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); if (stream->is_running) return PJ_EBUSY; stream->frame_handler.cb = cb; stream->frame_handler.user_data = user_data; return PJ_SUCCESS; } #endif /* PJMEDIA_VIDEO_DEV_HAS_FB */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-videodev/null_dev.c ================================================ /* $Id: colorbar_dev.c 4158 2012-06-06 09:56:14Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && PJMEDIA_HAS_VIDEO != 0 && \ defined(PJMEDIA_VIDEO_DEV_HAS_NULL) && \ PJMEDIA_VIDEO_DEV_HAS_NULL != 0 #define THIS_FILE "null_dev.c" #define DEFAULT_CLOCK_RATE 90000 #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 #define DEFAULT_FPS 5 /* null_ device info */ struct null_dev_info { pjmedia_vid_dev_info info; }; /* null_ factory */ struct null_factory { pjmedia_vid_dev_factory base; pj_pool_t *pool; pj_pool_factory *pf; unsigned dev_count; struct null_dev_info *dev_info; }; struct null_fmt_info { pjmedia_format_id fmt_id; /* Format ID */ }; /* Null video source supports */ static struct null_fmt_info null_fmts[] = { { PJMEDIA_FORMAT_BGRA }, }; /* Video stream. */ struct null_stream { pjmedia_vid_dev_stream base; /**< Base stream */ pjmedia_vid_dev_param param; /**< Settings */ pj_pool_t *pool; /**< Memory pool. */ pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */ void *user_data; /**< Application data. */ const struct null_fmt_info *cbfi; const pjmedia_video_format_info *vfi; pjmedia_video_apply_fmt_param vafp; pj_uint8_t *first_line[PJMEDIA_MAX_VIDEO_PLANES]; pj_timestamp ts; unsigned ts_inc; }; /* Prototypes */ static pj_status_t null_factory_init(pjmedia_vid_dev_factory *f); static pj_status_t null_factory_destroy(pjmedia_vid_dev_factory *f); static pj_status_t null_factory_refresh(pjmedia_vid_dev_factory *f); static unsigned null_factory_get_dev_count(pjmedia_vid_dev_factory *f); static pj_status_t null_factory_get_dev_info(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info); static pj_status_t null_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param); static pj_status_t null_factory_create_stream( pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm); static pj_status_t null_stream_get_param(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_param *param); static pj_status_t null_stream_get_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, void *value); static pj_status_t null_stream_set_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, const void *value); static pj_status_t null_stream_get_frame(pjmedia_vid_dev_stream *strm, pjmedia_frame *frame); static pj_status_t null_stream_start(pjmedia_vid_dev_stream *strm); static pj_status_t null_stream_stop(pjmedia_vid_dev_stream *strm); static pj_status_t null_stream_destroy(pjmedia_vid_dev_stream *strm); /* Operations */ static pjmedia_vid_dev_factory_op factory_op = { &null_factory_init, &null_factory_destroy, &null_factory_get_dev_count, &null_factory_get_dev_info, &null_factory_default_param, &null_factory_create_stream, &null_factory_refresh }; static pjmedia_vid_dev_stream_op stream_op = { &null_stream_get_param, &null_stream_get_cap, &null_stream_set_cap, &null_stream_start, &null_stream_get_frame, NULL, &null_stream_stop, &null_stream_destroy }; /**************************************************************************** * Factory operations */ /* * Init null_ video driver. */ pjmedia_vid_dev_factory* pjmedia_null_factory(pj_pool_factory *pf) { struct null_factory *f; pj_pool_t *pool; pool = pj_pool_create(pf, "null video", 512, 512, NULL); f = PJ_POOL_ZALLOC_T(pool, struct null_factory); f->pf = pf; f->pool = pool; f->base.op = &factory_op; return &f->base; } /* API: init factory */ static pj_status_t null_factory_init(pjmedia_vid_dev_factory *f) { struct null_factory *cf = (struct null_factory*)f; struct null_dev_info *ddi; unsigned i; cf->dev_count = 1; cf->dev_info = (struct null_dev_info*) pj_pool_calloc(cf->pool, cf->dev_count, sizeof(struct null_dev_info)); ddi = &cf->dev_info[0]; pj_bzero(ddi, sizeof(*ddi)); pj_ansi_strncpy(ddi->info.name, "Null video device", sizeof(ddi->info.name)); ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0'; pj_ansi_strncpy(ddi->info.driver, "Null", sizeof(ddi->info.driver)); ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0'; ddi->info.dir = PJMEDIA_DIR_CAPTURE; ddi->info.has_callback = PJ_FALSE; ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT; ddi->info.fmt_cnt = sizeof(null_fmts)/sizeof(null_fmts[0]); for (i = 0; i < ddi->info.fmt_cnt; i++) { pjmedia_format *fmt = &ddi->info.fmt[i]; pjmedia_format_init_video(fmt, null_fmts[i].fmt_id, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FPS, 1); } PJ_LOG(4, (THIS_FILE, "Null video src initialized with %d device(s):", cf->dev_count)); for (i = 0; i < cf->dev_count; i++) { PJ_LOG(4, (THIS_FILE, "%2d: %s", i, cf->dev_info[i].info.name)); } return PJ_SUCCESS; } /* API: destroy factory */ static pj_status_t null_factory_destroy(pjmedia_vid_dev_factory *f) { struct null_factory *cf = (struct null_factory*)f; pj_pool_t *pool = cf->pool; cf->pool = NULL; pj_pool_release(pool); return PJ_SUCCESS; } /* API: refresh the list of devices */ static pj_status_t null_factory_refresh(pjmedia_vid_dev_factory *f) { PJ_UNUSED_ARG(f); return PJ_SUCCESS; } /* API: get number of devices */ static unsigned null_factory_get_dev_count(pjmedia_vid_dev_factory *f) { struct null_factory *cf = (struct null_factory*)f; return cf->dev_count; } /* API: get device info */ static pj_status_t null_factory_get_dev_info(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info) { struct null_factory *cf = (struct null_factory*)f; PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV); pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info)); return PJ_SUCCESS; } /* API: create default device parameter */ static pj_status_t null_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param) { struct null_factory *cf = (struct null_factory*)f; struct null_dev_info *di = &cf->dev_info[index]; PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV); PJ_UNUSED_ARG(pool); pj_bzero(param, sizeof(*param)); param->dir = PJMEDIA_DIR_CAPTURE; param->cap_id = index; param->rend_id = PJMEDIA_VID_INVALID_DEV; param->flags = PJMEDIA_VID_DEV_CAP_FORMAT; param->clock_rate = DEFAULT_CLOCK_RATE; pj_memcpy(¶m->fmt, &di->info.fmt[0], sizeof(param->fmt)); return PJ_SUCCESS; } static const struct null_fmt_info* get_null_fmt_info(pjmedia_format_id id) { unsigned i; for (i = 0; i < sizeof(null_fmts)/sizeof(null_fmts[0]); i++) { if (null_fmts[i].fmt_id == id) return &null_fmts[i]; } return NULL; } /* API: create stream */ static pj_status_t null_factory_create_stream( pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm) { struct null_factory *cf = (struct null_factory*)f; pj_pool_t *pool; struct null_stream *strm; const pjmedia_video_format_detail *vfd; const pjmedia_video_format_info *vfi; pjmedia_video_apply_fmt_param vafp; const struct null_fmt_info *cbfi; unsigned i; PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL); PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO && param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO && param->dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL); pj_bzero(&vafp, sizeof(vafp)); vfd = pjmedia_format_get_video_format_detail(¶m->fmt, PJ_TRUE); vfi = pjmedia_get_video_format_info(NULL, param->fmt.id); cbfi = get_null_fmt_info(param->fmt.id); if (!vfi || !cbfi) return PJMEDIA_EVID_BADFORMAT; vafp.size = param->fmt.det.vid.size; if (vfi->apply_fmt(vfi, &vafp) != PJ_SUCCESS) return PJMEDIA_EVID_BADFORMAT; /* Create and Initialize stream descriptor */ pool = pj_pool_create(cf->pf, "null-dev", 512, 512, NULL); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); strm = PJ_POOL_ZALLOC_T(pool, struct null_stream); pj_memcpy(&strm->param, param, sizeof(*param)); strm->pool = pool; pj_memcpy(&strm->vid_cb, cb, sizeof(*cb)); strm->user_data = user_data; strm->vfi = vfi; strm->cbfi = cbfi; pj_memcpy(&strm->vafp, &vafp, sizeof(vafp)); strm->ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1); for (i = 0; i < vfi->plane_cnt; ++i) { strm->first_line[i] = pj_pool_alloc(pool, vafp.strides[i]); pj_memset(strm->first_line[i], 0, vafp.strides[i]); } /* Done */ strm->base.op = &stream_op; *p_vid_strm = &strm->base; return PJ_SUCCESS; } /* API: Get stream info. */ static pj_status_t null_stream_get_param(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_param *pi) { struct null_stream *strm = (struct null_stream*)s; PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); pj_memcpy(pi, &strm->param, sizeof(*pi)); return PJ_SUCCESS; } /* API: get capability */ static pj_status_t null_stream_get_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, void *pval) { struct null_stream *strm = (struct null_stream*)s; PJ_UNUSED_ARG(strm); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); return PJMEDIA_EVID_INVCAP; } /* API: set capability */ static pj_status_t null_stream_set_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, const void *pval) { struct null_stream *strm = (struct null_stream*)s; PJ_UNUSED_ARG(strm); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); return PJMEDIA_EVID_INVCAP; } /* API: Get frame from stream */ static pj_status_t null_stream_get_frame(pjmedia_vid_dev_stream *strm, pjmedia_frame *frame) { struct null_stream *stream = (struct null_stream*)strm; unsigned i; pj_uint8_t *ptr = frame->buf; frame->type = PJMEDIA_FRAME_TYPE_VIDEO; frame->bit_info = 0; frame->timestamp = stream->ts; stream->ts.u64 += stream->ts_inc; /* paint subsequent lines */ for (i=0; ivfi->plane_cnt; ++i) { pj_uint8_t *plane_end; plane_end = ptr + stream->vafp.plane_bytes[i]; while (ptr < plane_end) { pj_memcpy(ptr, stream->first_line[i], stream->vafp.strides[i]); ptr += stream->vafp.strides[i]; } } return PJ_SUCCESS; } /* API: Start stream. */ static pj_status_t null_stream_start(pjmedia_vid_dev_stream *strm) { struct null_stream *stream = (struct null_stream*)strm; PJ_UNUSED_ARG(stream); PJ_LOG(4, (THIS_FILE, "Starting null video stream")); return PJ_SUCCESS; } /* API: Stop stream. */ static pj_status_t null_stream_stop(pjmedia_vid_dev_stream *strm) { struct null_stream *stream = (struct null_stream*)strm; PJ_UNUSED_ARG(stream); PJ_LOG(4, (THIS_FILE, "Stopping null video stream")); return PJ_SUCCESS; } /* API: Destroy stream. */ static pj_status_t null_stream_destroy(pjmedia_vid_dev_stream *strm) { struct null_stream *stream = (struct null_stream*)strm; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); null_stream_stop(strm); pj_pool_release(stream->pool); return PJ_SUCCESS; } #endif /* PJMEDIA_VIDEO_DEV_HAS_NULL */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-videodev/util.c ================================================ /* $Id$ */ /* * Copyright (C) 2014-2015 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "util.h" #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #if defined(PJMEDIA_HAS_LIBYUV) && PJMEDIA_HAS_LIBYUV != 0 #include #define HAS_ROTATION 1 #else #define HAS_ROTATION 0 #endif #define THIS_FILE "vid_util.c" pj_status_t pjmedia_vid_dev_conv_create_converter(pjmedia_vid_dev_conv *conv, pj_pool_t *pool, pjmedia_format *fmt, pjmedia_rect_size src_size, pjmedia_rect_size dst_size, pj_bool_t handle_rotation, pj_bool_t maintain_aspect_ratio) { pj_status_t status; pjmedia_conversion_param conv_param; const pjmedia_video_format_info *vfi; pj_assert((src_size.w == dst_size.w || src_size.h == dst_size.h) || (src_size.w == dst_size.h || src_size.h == dst_size.w)); if (conv->conv) return PJ_SUCCESS; if (fmt->id != PJMEDIA_FORMAT_I420 && fmt->id != PJMEDIA_FORMAT_BGRA) return PJ_EINVAL; /* Currently, for BGRA format, device must handle the rotation. */ if (fmt->id == PJMEDIA_FORMAT_BGRA && handle_rotation) return PJ_ENOTSUP; if (handle_rotation) { #if !HAS_ROTATION return PJ_ENOTSUP; #endif } conv->src_size = src_size; conv->dst_size = dst_size; conv->handle_rotation = handle_rotation; pjmedia_format_copy(&conv->fmt, fmt); pjmedia_format_copy(&conv_param.src, fmt); pjmedia_format_copy(&conv_param.dst, fmt); /* If we do the rotation, the conversion's source size must be the same * as the device's original size. Otherwise, frames that require conversion * are the ones of which orientation differ by 90 or 270 degrees from the * destination size. */ if (handle_rotation) { conv_param.src.det.vid.size = src_size; } else { conv_param.src.det.vid.size.w = dst_size.h; conv_param.src.det.vid.size.h = dst_size.w; } /* Maintaining aspect ratio requires filling the left&right / * top&bottom area with black color. * Currently it is only supported for I420. * TODO: support BGRA as well */ if (fmt->id != PJMEDIA_FORMAT_I420) maintain_aspect_ratio = PJ_FALSE; /* Calculate the size after rotation. * If aspect ratio doesn't need to be maintained, rot_size is simply equal * to the destination size. Otherwise, we need to fit the rotated frame * to height or to width. */ conv->maintain_aspect_ratio = maintain_aspect_ratio; if (maintain_aspect_ratio) { conv->fit_to_h = (dst_size.w >= dst_size.h? PJ_TRUE: PJ_FALSE); if (conv->fit_to_h) { /* Fit to height */ conv->rot_size.h = dst_size.h; conv->rot_size.w = dst_size.h * dst_size.h / dst_size.w; /* Make sure the width difference is divisible by four * so we can have equal padding left and right. */ conv->rot_size.w += (dst_size.w - conv->rot_size.w) % 4; conv->pad = (conv->dst_size.w - conv->rot_size.w) / 2; } else { /* Fit to width */ conv->rot_size.w = dst_size.w; conv->rot_size.h = dst_size.w * dst_size.w / dst_size.h; conv->rot_size.h += (dst_size.h - conv->rot_size.h) % 4; conv->pad = (conv->dst_size.h - conv->rot_size.h) / 2; } } else { conv->rot_size = dst_size; } /* Calculate the size after resizing. */ if (handle_rotation) { /* If we do the rotation, conversion is done before rotation. */ if (maintain_aspect_ratio) { /* Since aspect ratio is maintained, the long side after * conversion must be the same as before conversion. * For example: 352x288 will be converted to 288x236 */ pj_size_t long_s = (conv->rot_size.h > conv->rot_size.w? conv->rot_size.h: conv->rot_size.w); pj_size_t short_s = (conv->rot_size.h > conv->rot_size.w? conv->rot_size.w: conv->rot_size.h); if (src_size.w > src_size.h) { conv->res_size.w = long_s; conv->res_size.h = short_s; } else { conv->res_size.w = short_s; conv->res_size.h = long_s; } } else { /* We don't need to maintain aspect ratio, * so just swap the width and height. * For example: 352x288 will be resized to 288x352 */ conv->res_size.w = src_size.h; conv->res_size.h = src_size.w; } conv_param.dst.det.vid.size = conv->res_size; } else { conv->res_size = conv->rot_size; conv_param.dst.det.vid.size = conv->rot_size; } status = pjmedia_converter_create(NULL, pool, &conv_param, &conv->conv); if (status != PJ_SUCCESS) { PJ_LOG(3, (THIS_FILE, "Error creating converter")); return status; } vfi = pjmedia_get_video_format_info(NULL, fmt->id); pj_assert(vfi); conv->wxh = conv->dst_size.w * conv->dst_size.h; conv->src_frame_size = dst_size.w * dst_size.h * vfi->bpp / 8; conv->conv_frame_size = conv->rot_size.w * conv->rot_size.h; conv->conv_frame_size *= vfi->bpp / 8; conv->conv_buf = pj_pool_alloc(pool, conv->src_frame_size); pjmedia_vid_dev_conv_set_rotation(conv, PJMEDIA_ORIENT_NATURAL); PJ_LOG(4, (THIS_FILE, "Orientation converter created: %dx%d to %dx%d, " "maintain aspect ratio=%s", conv_param.src.det.vid.size.w, conv_param.src.det.vid.size.h, conv_param.dst.det.vid.size.w, conv_param.dst.det.vid.size.h, maintain_aspect_ratio? "yes": "no")); return PJ_SUCCESS; } void pjmedia_vid_dev_conv_set_rotation(pjmedia_vid_dev_conv *conv, pjmedia_orient rotation) { pjmedia_rect_size new_size = conv->src_size; conv->rotation = rotation; if (rotation == PJMEDIA_ORIENT_ROTATE_90DEG || rotation == PJMEDIA_ORIENT_ROTATE_270DEG) { new_size.w = conv->src_size.h; new_size.h = conv->src_size.w; } /* Check whether new size (size after rotation) and destination * are both portrait or both landscape. If yes, resize will not * be required in pjmedia_vid_dev_conv_resize_and_rotate() below. * For example, 352x288 frame rotated 270 degrees will fit into * a destination frame of 288x352 (no resize needed). */ if ((new_size.w > new_size.h && conv->dst_size.w > conv->dst_size.h) || (new_size.h > new_size.w && conv->dst_size.h > conv->dst_size.w)) { conv->match_src_dst = PJ_TRUE; } else { conv->match_src_dst = PJ_FALSE; } } pj_status_t pjmedia_vid_dev_conv_resize_and_rotate(pjmedia_vid_dev_conv *conv, void *src_buf, void **result) { #define swap(a, b) {pj_uint8_t *c = a; a = b; b = c;} pj_status_t status; pjmedia_frame src_frame, dst_frame; pjmedia_rect_size src_size = conv->src_size; pj_uint8_t *src = src_buf; pj_uint8_t *dst = conv->conv_buf; pj_assert(src_buf); if (!conv->conv) return PJ_EINVALIDOP; if (!conv->match_src_dst) { /* We need to resize. */ src_frame.buf = src; dst_frame.buf = dst; src_frame.size = conv->src_frame_size; dst_frame.size = conv->conv_frame_size; status = pjmedia_converter_convert(conv->conv, &src_frame, &dst_frame); if (status != PJ_SUCCESS) { PJ_LOG(3, (THIS_FILE, "Failed to convert frame")); return status; } src_size = conv->res_size; swap(src, dst); } if (conv->handle_rotation && conv->rotation != PJMEDIA_ORIENT_NATURAL) { /* We need to do rotation. */ if (conv->fmt.id == PJMEDIA_FORMAT_I420) { pjmedia_rect_size dst_size = src_size; pj_size_t p_len = src_size.w * src_size.h; if (conv->rotation == PJMEDIA_ORIENT_ROTATE_90DEG || conv->rotation == PJMEDIA_ORIENT_ROTATE_270DEG) { dst_size.w = src_size.h; dst_size.h = src_size.w; } #if defined(PJMEDIA_HAS_LIBYUV) && PJMEDIA_HAS_LIBYUV != 0 enum RotationMode mode; switch (conv->rotation) { case PJMEDIA_ORIENT_ROTATE_90DEG: mode = kRotate90; break; case PJMEDIA_ORIENT_ROTATE_180DEG: mode = kRotate180; break; case PJMEDIA_ORIENT_ROTATE_270DEG: mode = kRotate270; break; default: mode = kRotate0; } I420Rotate(src, src_size.w, src+p_len, src_size.w/2, src+p_len+p_len/4, src_size.w/2, dst, dst_size.w, dst+p_len, dst_size.w/2, dst+p_len+p_len/4, dst_size.w/2, src_size.w, src_size.h, mode); swap(src, dst); #else PJ_UNUSED_ARG(p_len); PJ_UNUSED_ARG(dst_size); #endif } } if (!conv->match_src_dst && conv->maintain_aspect_ratio) { /* Center the frame and fill the area with black color */ if (conv->fmt.id == PJMEDIA_FORMAT_I420) { unsigned i = 0; pj_uint8_t *pdst = dst; pj_uint8_t *psrc = src; pj_size_t p_len_src = 0, p_len_dst = conv->wxh; int pad = conv->pad; pj_bzero(pdst, p_len_dst); if (conv->fit_to_h) { /* Fill the left and right with black */ for (; i < conv->dst_size.h; ++i) { pdst += pad; pj_memcpy(pdst, psrc, conv->rot_size.w); pdst += conv->rot_size.w; psrc += conv->rot_size.w; pdst += pad; } } else { /* Fill the top and bottom with black */ p_len_src = conv->rot_size.w * conv->rot_size.h; pj_memcpy(pdst + conv->rot_size.w * pad, psrc, p_len_src); psrc += p_len_src; pdst += p_len_dst; } /* Fill the U&V components with 0x80 to make it black. * Bzero-ing will make the area look green instead. */ pj_memset(pdst, 0x80, p_len_dst/2); pad /= 2; if (conv->fit_to_h) { p_len_src = conv->rot_size.w / 2; for (i = conv->dst_size.h; i > 0; --i) { pdst += pad; pj_memcpy(pdst, psrc, p_len_src); pdst += p_len_src; psrc += p_len_src; pdst += pad; } } else { pj_uint8_t *U, *V; pj_size_t gap = conv->rot_size.w * pad / 2; p_len_src /= 4; U = pdst; V = U + p_len_dst/4; pj_memcpy(U + gap, psrc, p_len_src); psrc += p_len_src; pj_memcpy(V + gap, psrc, p_len_src); } swap(src, dst); } } *result = src; return PJ_SUCCESS; } void pjmedia_vid_dev_conv_destroy_converter(pjmedia_vid_dev_conv *conv) { if (conv->conv) { pjmedia_converter_destroy(conv->conv); conv->conv = NULL; } } #endif /* PJMEDIA_HAS_VIDEO */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-videodev/util.h ================================================ /* $Id$ */ /* * Copyright (C) 2014-2015 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJMEDIA_VIDEODEV_UTIL_H__ #define __PJMEDIA_VIDEODEV_UTIL_H__ #include #include #include /* * Video device utility functions to resize and rotate video frames. */ typedef struct pjmedia_vid_dev_conv { pjmedia_converter *conv; pjmedia_format fmt; pjmedia_rect_size src_size; pjmedia_rect_size dst_size; pjmedia_rect_size res_size; /* Size after resizing */ pjmedia_orient rotation; pjmedia_rect_size rot_size; /* Size after rotation */ void *conv_buf; pj_size_t src_frame_size; pj_size_t conv_frame_size; pj_bool_t fit_to_h; pj_bool_t handle_rotation; pj_bool_t maintain_aspect_ratio; pj_bool_t match_src_dst; pj_int32_t pad; pj_size_t wxh; } pjmedia_vid_dev_conv; /** * Create converter. * The process: * frame --> resize --> rotate --> center * (if handle_rotation (if maintain_aspect_ratio * == PJ_TRUE) == PJ_TRUE) * * handle_rotation will specify whether the converter will need to do the * rotation as well. If PJ_FALSE, the video device will handle the rotation * and pass the already-rotated frame. * * maintain_aspect_ratio defines whether aspect ratio should be maintained * when rotating the image. * If PJ_TRUE, a frame of size w x h will be resized and rotated to * a new frame of size new_w x new_h (new_h and new_h have the same * aspect ratio as w x h). Then the new frame will be centered-fit into * the original frame with black area inserted to fill the gaps. * Disabling this setting will only resize the frame of size w x h to h x w, * and then rotate it to fit the original size of w x h. It will achieve * a slightly faster performance but the resulting image will be stretched. * The feature to maintain aspect ratio is only supported for certain formats * (currently, only if fmt.id equals to I420). */ pj_status_t pjmedia_vid_dev_conv_create_converter(pjmedia_vid_dev_conv *conv, pj_pool_t *pool, pjmedia_format *fmt, pjmedia_rect_size src_size, pjmedia_rect_size dst_size, pj_bool_t handle_rotation, pj_bool_t maintain_aspect_ratio); /* Set rotation */ void pjmedia_vid_dev_conv_set_rotation(pjmedia_vid_dev_conv *conv, pjmedia_orient rotation); /* Resize the buffer and rotate it, if necessary */ pj_status_t pjmedia_vid_dev_conv_resize_and_rotate(pjmedia_vid_dev_conv *conv, void *src_buf, void **result); /* Destroy converter */ void pjmedia_vid_dev_conv_destroy_converter(pjmedia_vid_dev_conv *conv); #endif /* __PJMEDIA_VIDEODEV_UTIL_H__ */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-videodev/v4l2_dev.c ================================================ /* $Id: v4l2_dev.c 4310 2012-12-19 05:38:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && PJMEDIA_HAS_VIDEO != 0 && \ defined(PJMEDIA_VIDEO_DEV_HAS_V4L2) && PJMEDIA_VIDEO_DEV_HAS_V4L2 != 0 #include #include #include #include #include #define THIS_FILE "v4l2_dev.c" #define DRIVER_NAME "v4l2" #define V4L2_MAX_DEVS 4 #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 #define DEFAULT_FPS 25 #define DEFAULT_CLOCK_RATE 90000 #define INVALID_FD -1 #define BUFFER_CNT 2 #define MAX_IOCTL_RETRY 20 /* mapping between pjmedia_fmt_id and v4l2 pixel format */ typedef struct vid4lin_fmt_map { pj_uint32_t pjmedia_fmt_id; pj_uint32_t v4l2_fmt_id; } vid4lin_fmt_map; /* I/O type being used */ enum vid4lin_io_type { IO_TYPE_NONE, IO_TYPE_READ, IO_TYPE_MMAP, IO_TYPE_MMAP_USER }; /* descriptor for each mmap-ed buffer */ typedef struct vid4lin_buffer { void *start; size_t length; } vid4lin_buffer; /* v4l2 device info */ typedef struct vid4lin_dev_info { pjmedia_vid_dev_info info; char dev_name[32]; struct v4l2_capability v4l2_cap; } vid4lin_dev_info; /* v4l2 factory */ typedef struct vid4lin_factory { pjmedia_vid_dev_factory base; pj_pool_t *pool; pj_pool_t *dev_pool; pj_pool_factory *pf; unsigned dev_count; vid4lin_dev_info *dev_info; } vid4lin_factory; /* Video stream. */ typedef struct vid4lin_stream { pjmedia_vid_dev_stream base; /**< Base stream */ pjmedia_vid_dev_param param; /**< Settings */ pj_pool_t *pool; /**< Memory pool. */ int fd; /**< Video fd. */ char name[64]; /**< Name for log */ enum vid4lin_io_type io_type; /**< I/O method. */ unsigned buf_cnt; /**< MMap buf cnt. */ vid4lin_buffer *buffers; /**< MMap buffers. */ pj_time_val start_time; /**< Time when started */ pjmedia_vid_dev_cb vid_cb; /**< Stream callback */ void *user_data; /**< Application data */ } vid4lin_stream; /* Use this to convert between pjmedia_format_id and V4L2 fourcc */ static vid4lin_fmt_map v4l2_fmt_maps[] = { { PJMEDIA_FORMAT_RGB24, V4L2_PIX_FMT_BGR24 }, { PJMEDIA_FORMAT_RGBA, V4L2_PIX_FMT_BGR32 }, { PJMEDIA_FORMAT_RGB32, V4L2_PIX_FMT_BGR32 }, { PJMEDIA_FORMAT_AYUV, V4L2_PIX_FMT_YUV32 }, { PJMEDIA_FORMAT_YUY2, V4L2_PIX_FMT_YUYV }, { PJMEDIA_FORMAT_UYVY, V4L2_PIX_FMT_UYVY } }; /* Prototypes */ static pj_status_t vid4lin_factory_init(pjmedia_vid_dev_factory *f); static pj_status_t vid4lin_factory_destroy(pjmedia_vid_dev_factory *f); static pj_status_t vid4lin_factory_refresh(pjmedia_vid_dev_factory *f); static unsigned vid4lin_factory_get_dev_count(pjmedia_vid_dev_factory *f); static pj_status_t vid4lin_factory_get_dev_info(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info); static pj_status_t vid4lin_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param); static pj_status_t vid4lin_factory_create_stream(pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *prm, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p); static pj_status_t vid4lin_stream_get_param(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_param *param); static pj_status_t vid4lin_stream_get_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, void *value); static pj_status_t vid4lin_stream_set_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, const void *value); static pj_status_t vid4lin_stream_get_frame(pjmedia_vid_dev_stream *strm, pjmedia_frame *frame); static pj_status_t vid4lin_stream_start(pjmedia_vid_dev_stream *strm); static pj_status_t vid4lin_stream_stop(pjmedia_vid_dev_stream *strm); static pj_status_t vid4lin_stream_destroy(pjmedia_vid_dev_stream *strm); /* Operations */ static pjmedia_vid_dev_factory_op factory_op = { &vid4lin_factory_init, &vid4lin_factory_destroy, &vid4lin_factory_get_dev_count, &vid4lin_factory_get_dev_info, &vid4lin_factory_default_param, &vid4lin_factory_create_stream, &vid4lin_factory_refresh }; static pjmedia_vid_dev_stream_op stream_op = { &vid4lin_stream_get_param, &vid4lin_stream_get_cap, &vid4lin_stream_set_cap, &vid4lin_stream_start, &vid4lin_stream_get_frame, NULL, &vid4lin_stream_stop, &vid4lin_stream_destroy }; /**************************************************************************** * Factory operations */ /* * Factory creation function. */ pjmedia_vid_dev_factory* pjmedia_v4l2_factory(pj_pool_factory *pf) { vid4lin_factory *f; pj_pool_t *pool; pool = pj_pool_create(pf, DRIVER_NAME, 512, 512, NULL); f = PJ_POOL_ZALLOC_T(pool, vid4lin_factory); f->pf = pf; f->pool = pool; f->base.op = &factory_op; return &f->base; } /* util: ioctl that tries harder. */ static pj_status_t xioctl(int fh, int request, void *arg) { enum { RETRY = MAX_IOCTL_RETRY }; int r, c=0; do { r = v4l2_ioctl(fh, request, arg); } while (r==-1 && c++fmt_cntv4l2_fmt_id; v4l2_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; status = xioctl(fd, VIDIOC_TRY_FMT, &v4l2_fmt); if (status != PJ_SUCCESS || v4l2_fmt.fmt.pix.pixelformat != fmt_map->v4l2_fmt_id || v4l2_fmt.fmt.pix.width != v4l_sizes[i].w || v4l2_fmt.fmt.pix.height != v4l_sizes[i].h) { continue; } pjmedia_format_init_video(&info->fmt[info->fmt_cnt++], fmt_id, v4l_sizes[i].w, v4l_sizes[i].h, DEFAULT_FPS, 1); } } /* Scan V4L2 devices */ static pj_status_t v4l2_scan_devs(vid4lin_factory *f) { vid4lin_dev_info vdi[V4L2_MAX_DEVS]; char dev_name[32]; unsigned i, old_count; pj_status_t status; if (f->dev_pool) { pj_pool_release(f->dev_pool); f->dev_pool = NULL; } pj_bzero(vdi, sizeof(vdi)); old_count = f->dev_count; f->dev_count = 0; f->dev_pool = pj_pool_create(f->pf, DRIVER_NAME, 500, 500, NULL); for (i=0; idev_count < V4L2_MAX_DEVS; ++i) { int fd; vid4lin_dev_info *pdi; pj_uint32_t fmt_cap[8]; int j, fmt_cnt=0; pdi = &vdi[f->dev_count]; snprintf(dev_name, sizeof(dev_name), "/dev/video%d", i); if (!pj_file_exists(dev_name)) continue; fd = v4l2_open(dev_name, O_RDWR, 0); if (fd == -1) continue; status = xioctl(fd, VIDIOC_QUERYCAP, &pdi->v4l2_cap); if (status != PJ_SUCCESS) { PJ_PERROR(4,(THIS_FILE, status, "Error querying %s", dev_name)); v4l2_close(fd); continue; } if ((pdi->v4l2_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) { v4l2_close(fd); continue; } PJ_LOG(5,(THIS_FILE, "Found capture device %s", pdi->v4l2_cap.card)); PJ_LOG(5,(THIS_FILE, " Enumerating formats:")); for (j=0; fmt_cntdev_name, dev_name, sizeof(pdi->dev_name)); pdi->dev_name[sizeof(pdi->dev_name)-1] = '\0'; strncpy(pdi->info.name, (char*)pdi->v4l2_cap.card, sizeof(pdi->info.name)); pdi->info.name[sizeof(pdi->info.name)-1] = '\0'; strncpy(pdi->info.driver, DRIVER_NAME, sizeof(pdi->info.driver)); pdi->info.driver[sizeof(pdi->info.driver)-1] = '\0'; pdi->info.dir = PJMEDIA_DIR_CAPTURE; pdi->info.has_callback = PJ_FALSE; pdi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT; for (j=0; jinfo); } v4l2_close(fd); if (j < fmt_cnt) continue; f->dev_count++; } if (f->dev_count == 0) return PJ_SUCCESS; if (f->dev_count > old_count || f->dev_info == NULL) { f->dev_info = (vid4lin_dev_info*) pj_pool_calloc(f->dev_pool, f->dev_count, sizeof(vid4lin_dev_info)); } pj_memcpy(f->dev_info, vdi, f->dev_count * sizeof(vid4lin_dev_info)); return PJ_SUCCESS; } /* API: init factory */ static pj_status_t vid4lin_factory_init(pjmedia_vid_dev_factory *f) { return vid4lin_factory_refresh(f); } /* API: destroy factory */ static pj_status_t vid4lin_factory_destroy(pjmedia_vid_dev_factory *f) { vid4lin_factory *cf = (vid4lin_factory*)f; pj_pool_t *pool = cf->pool; if (cf->dev_pool) pj_pool_release(cf->dev_pool); if (cf->pool) { cf->pool = NULL; pj_pool_release(pool); } return PJ_SUCCESS; } /* API: refresh the list of devices */ static pj_status_t vid4lin_factory_refresh(pjmedia_vid_dev_factory *f) { vid4lin_factory *cf = (vid4lin_factory*)f; pj_status_t status; status = v4l2_scan_devs(cf); if (status != PJ_SUCCESS) return status; PJ_LOG(4, (THIS_FILE, "Video4Linux2 has %d devices", cf->dev_count)); return PJ_SUCCESS; } /* API: get number of devices */ static unsigned vid4lin_factory_get_dev_count(pjmedia_vid_dev_factory *f) { vid4lin_factory *cf = (vid4lin_factory*)f; return cf->dev_count; } /* API: get device info */ static pj_status_t vid4lin_factory_get_dev_info(pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_info *info) { vid4lin_factory *cf = (vid4lin_factory*)f; PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV); pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info)); return PJ_SUCCESS; } /* API: create default device parameter */ static pj_status_t vid4lin_factory_default_param(pj_pool_t *pool, pjmedia_vid_dev_factory *f, unsigned index, pjmedia_vid_dev_param *param) { vid4lin_factory *cf = (vid4lin_factory*)f; PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV); pj_bzero(param, sizeof(*param)); param->dir = PJMEDIA_DIR_CAPTURE; param->cap_id = index; param->rend_id = PJMEDIA_VID_INVALID_DEV; param->flags = PJMEDIA_VID_DEV_CAP_FORMAT; param->clock_rate = DEFAULT_CLOCK_RATE; pjmedia_format_copy(¶m->fmt, &cf->dev_info[index].info.fmt[0]); return PJ_SUCCESS; } /* util: setup format */ static pj_status_t vid4lin_stream_init_fmt(vid4lin_stream *stream, const pjmedia_vid_dev_param *param, pj_uint32_t pix_fmt) { pjmedia_video_format_detail *vfd; struct v4l2_format v4l2_fmt; pj_status_t status; vfd = pjmedia_format_get_video_format_detail(¶m->fmt, PJ_TRUE); if (vfd == NULL) return PJMEDIA_EVID_BADFORMAT; pj_bzero(&v4l2_fmt, sizeof(v4l2_fmt)); v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; v4l2_fmt.fmt.pix.width = vfd->size.w; v4l2_fmt.fmt.pix.height = vfd->size.h; v4l2_fmt.fmt.pix.pixelformat = pix_fmt; v4l2_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; status = xioctl(stream->fd, VIDIOC_S_FMT, &v4l2_fmt); if (status != PJ_SUCCESS) return status; if (v4l2_fmt.fmt.pix.pixelformat != pix_fmt) { status = PJMEDIA_EVID_BADFORMAT; return status; } if ((v4l2_fmt.fmt.pix.width != vfd->size.w) || (v4l2_fmt.fmt.pix.height != vfd->size.h)) { /* Size has changed */ vfd->size.w = v4l2_fmt.fmt.pix.width; vfd->size.h = v4l2_fmt.fmt.pix.height; } return PJ_SUCCESS; } /* Util: initiate v4l2 streaming via mmap */ static pj_status_t vid4lin_stream_init_streaming(vid4lin_stream *stream) { struct v4l2_requestbuffers req; unsigned i; pj_status_t status; pj_bzero(&req, sizeof(req)); req.count = BUFFER_CNT; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; status = xioctl(stream->fd, VIDIOC_REQBUFS, &req); if (status != PJ_SUCCESS) return status; stream->buffers = pj_pool_calloc(stream->pool, req.count, sizeof(*stream->buffers)); stream->buf_cnt = 0; for (i = 0; i < req.count; ++i) { struct v4l2_buffer buf; pj_bzero(&buf, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; status = xioctl(stream->fd, VIDIOC_QUERYBUF, &buf); if (status != PJ_SUCCESS) goto on_error; stream->buffers[i].length = buf.length; stream->buffers[i].start = v4l2_mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, stream->fd, buf.m.offset); if (MAP_FAILED == stream->buffers[i].start) { status = pj_get_os_error(); goto on_error; } stream->buf_cnt++; } PJ_LOG(5,(THIS_FILE, " mmap streaming initialized")); stream->io_type = IO_TYPE_MMAP; return PJ_SUCCESS; on_error: return status; } /* init streaming with user pointer */ static pj_status_t vid4lin_stream_init_streaming_user(vid4lin_stream *stream) { return PJ_ENOTSUP; } /* init streaming with read() */ static pj_status_t vid4lin_stream_init_read_write(vid4lin_stream *stream) { return PJ_ENOTSUP; } /* API: create stream */ static pj_status_t vid4lin_factory_create_stream(pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm) { vid4lin_factory *cf = (vid4lin_factory*)f; pj_pool_t *pool; vid4lin_stream *stream; vid4lin_dev_info *vdi; const vid4lin_fmt_map *fmt_map; const pjmedia_video_format_info *fmt_info; pjmedia_video_format_detail *vfd; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL); PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO && param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO && param->dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL); PJ_ASSERT_RETURN(param->cap_id >= 0 && param->cap_id < cf->dev_count, PJMEDIA_EVID_INVDEV); fmt_info = pjmedia_get_video_format_info(NULL, param->fmt.id); if (!fmt_info || (fmt_map=get_v4l2_format_info(param->fmt.id))==NULL) return PJMEDIA_EVID_BADFORMAT; vdi = &cf->dev_info[param->cap_id]; vfd = pjmedia_format_get_video_format_detail(¶m->fmt, PJ_TRUE); PJ_UNUSED_ARG(vfd); /* Unused for now */ /* Create and Initialize stream descriptor */ pool = pj_pool_create(cf->pf, vdi->info.name, 512, 512, NULL); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); stream = PJ_POOL_ZALLOC_T(pool, vid4lin_stream); pj_memcpy(&stream->param, param, sizeof(*param)); stream->pool = pool; pj_memcpy(&stream->vid_cb, cb, sizeof(*cb)); strncpy(stream->name, vdi->info.name, sizeof(stream->name)); stream->name[sizeof(stream->name)-1] = '\0'; stream->user_data = user_data; stream->fd = INVALID_FD; stream->fd = v4l2_open(vdi->dev_name, O_RDWR, 0); if (stream->fd < 0) goto on_error; status = vid4lin_stream_init_fmt(stream, param, fmt_map->v4l2_fmt_id); if (status != PJ_SUCCESS) goto on_error; if (vdi->v4l2_cap.capabilities & V4L2_CAP_STREAMING) status = vid4lin_stream_init_streaming(stream); if (status!=PJ_SUCCESS && vdi->v4l2_cap.capabilities & V4L2_CAP_STREAMING) status = vid4lin_stream_init_streaming_user(stream); if (status!=PJ_SUCCESS && vdi->v4l2_cap.capabilities & V4L2_CAP_READWRITE) status = vid4lin_stream_init_read_write(stream); if (status != PJ_SUCCESS) { PJ_LOG(1,(THIS_FILE, "Error: unable to initiate I/O on %s", stream->name)); goto on_error; } /* Done */ stream->base.op = &stream_op; *p_vid_strm = &stream->base; return PJ_SUCCESS; on_error: if (status == PJ_SUCCESS) status = PJ_RETURN_OS_ERROR(errno); vid4lin_stream_destroy(&stream->base); return status; } /* API: Get stream info. */ static pj_status_t vid4lin_stream_get_param(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_param *pi) { vid4lin_stream *strm = (vid4lin_stream*)s; PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); pj_memcpy(pi, &strm->param, sizeof(*pi)); return PJ_SUCCESS; } /* API: get capability */ static pj_status_t vid4lin_stream_get_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, void *pval) { vid4lin_stream *strm = (vid4lin_stream*)s; PJ_UNUSED_ARG(strm); PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE) { return PJMEDIA_EVID_INVCAP; // return PJ_SUCCESS; } else { return PJMEDIA_EVID_INVCAP; } } /* API: set capability */ static pj_status_t vid4lin_stream_set_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, const void *pval) { vid4lin_stream *strm = (vid4lin_stream*)s; PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); /* if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE) { return PJ_SUCCESS; } */ PJ_UNUSED_ARG(strm); PJ_UNUSED_ARG(cap); PJ_UNUSED_ARG(pval); return PJMEDIA_EVID_INVCAP; } /* get frame from mmap */ static pj_status_t vid4lin_stream_get_frame_mmap(vid4lin_stream *stream, pjmedia_frame *frame) { struct v4l2_buffer buf; pj_time_val time; pj_status_t status = PJ_SUCCESS; unsigned tmp_idx; pj_bzero(&buf, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; status = xioctl(stream->fd, VIDIOC_DQBUF, &buf); if (status != PJ_SUCCESS) return status; if (frame->size < buf.bytesused) { /* supplied buffer is too small */ pj_assert(!"frame buffer is too small for v4l2"); status = PJ_ETOOSMALL; goto on_return; } time.sec = buf.timestamp.tv_sec; time.msec = buf.timestamp.tv_usec / 1000; PJ_TIME_VAL_SUB(time, stream->start_time); frame->type = PJMEDIA_FRAME_TYPE_VIDEO; frame->bit_info = 0; frame->size = buf.bytesused; frame->timestamp.u64 = PJ_UINT64(1) * PJ_TIME_VAL_MSEC(time) * stream->param.clock_rate / PJ_UINT64(1000); pj_memcpy(frame->buf, stream->buffers[buf.index].start, buf.bytesused); on_return: tmp_idx = buf.index; pj_bzero(&buf, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = tmp_idx; xioctl(stream->fd, VIDIOC_QBUF, &buf); return status; } /* API: Get frame from stream */ static pj_status_t vid4lin_stream_get_frame(pjmedia_vid_dev_stream *strm, pjmedia_frame *frame) { vid4lin_stream *stream = (vid4lin_stream*)strm; if (stream->io_type == IO_TYPE_MMAP) return vid4lin_stream_get_frame_mmap(stream, frame); else { pj_assert(!"Unsupported i/o type"); return PJ_EINVALIDOP; } } /* API: Start stream. */ static pj_status_t vid4lin_stream_start(pjmedia_vid_dev_stream *strm) { vid4lin_stream *stream = (vid4lin_stream*)strm; struct v4l2_buffer buf; enum v4l2_buf_type type; unsigned i; pj_status_t status; PJ_ASSERT_RETURN(stream->fd != -1, PJ_EINVALIDOP); PJ_LOG(4, (THIS_FILE, "Starting v4l2 video stream %s", stream->name)); pj_gettimeofday(&stream->start_time); for (i = 0; i < stream->buf_cnt; ++i) { pj_bzero(&buf, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; status = xioctl(stream->fd, VIDIOC_QBUF, &buf); if (status != PJ_SUCCESS) goto on_error; } type = V4L2_BUF_TYPE_VIDEO_CAPTURE; status = xioctl(stream->fd, VIDIOC_STREAMON, &type); if (status != PJ_SUCCESS) goto on_error; return PJ_SUCCESS; on_error: if (i > 0) { /* Dequeue already enqueued buffers. Can we do this while streaming * is not started? */ unsigned n = i; for (i=0; ifd, VIDIOC_DQBUF, &buf); } } return status; } /* API: Stop stream. */ static pj_status_t vid4lin_stream_stop(pjmedia_vid_dev_stream *strm) { vid4lin_stream *stream = (vid4lin_stream*)strm; enum v4l2_buf_type type; pj_status_t status; if (stream->fd < 0) return PJ_SUCCESS; PJ_LOG(4, (THIS_FILE, "Stopping v4l2 video stream %s", stream->name)); type = V4L2_BUF_TYPE_VIDEO_CAPTURE; status = xioctl(stream->fd, VIDIOC_STREAMOFF, &type); if (status != PJ_SUCCESS) return status; return PJ_SUCCESS; } /* API: Destroy stream. */ static pj_status_t vid4lin_stream_destroy(pjmedia_vid_dev_stream *strm) { vid4lin_stream *stream = (vid4lin_stream*)strm; unsigned i; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); vid4lin_stream_stop(strm); PJ_LOG(4, (THIS_FILE, "Destroying v4l2 video stream %s", stream->name)); for (i=0; ibuf_cnt; ++i) { if (stream->buffers[i].start != MAP_FAILED) { v4l2_munmap(stream->buffers[i].start, stream->buffers[i].length); stream->buffers[i].start = MAP_FAILED; } } if (stream->fd >= 0) { v4l2_close(stream->fd); stream->fd = -1; } pj_pool_release(stream->pool); return PJ_SUCCESS; } #endif /* PJMEDIA_VIDEO_DEV_HAS_V4L2 */ ================================================ FILE: deps/pjsip/pjmedia/src/pjmedia-videodev/videodev.c ================================================ /* $Id: videodev.c 4016 2012-04-04 05:05:50Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #define THIS_FILE "videodev.c" #define DEFINE_CAP(name, info) {name, info} /* Capability names */ static struct cap_info { const char *name; const char *info; } cap_infos[] = { DEFINE_CAP("format", "Video format"), DEFINE_CAP("scale", "Input dimension"), DEFINE_CAP("window", "Window handle"), DEFINE_CAP("resize", "Renderer resize"), DEFINE_CAP("position", "Renderer position"), DEFINE_CAP("hide", "Renderer hide"), DEFINE_CAP("preview", "Input preview"), DEFINE_CAP("orientation", "Video orientation"), DEFINE_CAP("switch", "Switch device"), DEFINE_CAP("wndflags", "Window flags") }; /* * The device index seen by application and driver is different. * * At application level, device index is index to global list of device. * At driver level, device index is index to device list on that particular * factory only. */ #define MAKE_DEV_ID(f_id, index) (((f_id & 0xFFFF) << 16) | (index & 0xFFFF)) #define GET_INDEX(dev_id) ((dev_id) & 0xFFFF) #define GET_FID(dev_id) ((dev_id) >> 16) #define DEFAULT_DEV_ID 0 /* extern functions to create factories */ #if PJMEDIA_VIDEO_DEV_HAS_NULL_VIDEO pjmedia_vid_dev_factory* pjmedia_null_video_factory(pj_pool_factory *pf); #endif #if PJMEDIA_VIDEO_DEV_HAS_DSHOW pjmedia_vid_dev_factory* pjmedia_dshow_factory(pj_pool_factory *pf); #endif #if PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC pjmedia_vid_dev_factory* pjmedia_cbar_factory(pj_pool_factory *pf); #endif #if PJMEDIA_VIDEO_DEV_HAS_V4L2 pjmedia_vid_dev_factory* pjmedia_v4l2_factory(pj_pool_factory *pf); #endif #if PJMEDIA_VIDEO_DEV_HAS_AVF pjmedia_vid_dev_factory* pjmedia_avf_factory(pj_pool_factory *pf); #endif #if PJMEDIA_VIDEO_DEV_HAS_IOS pjmedia_vid_dev_factory* pjmedia_ios_factory(pj_pool_factory *pf); #endif #if PJMEDIA_VIDEO_DEV_HAS_OPENGL pjmedia_vid_dev_factory* pjmedia_opengl_factory(pj_pool_factory *pf); #endif #if PJMEDIA_VIDEO_DEV_HAS_FB pjmedia_vid_dev_factory* pjmedia_fb_factory(pj_pool_factory *pf); #endif #if PJMEDIA_VIDEO_DEV_HAS_NULL pjmedia_vid_dev_factory* pjmedia_null_factory(pj_pool_factory *pf); #endif #if PJMEDIA_VIDEO_DEV_HAS_ANDROID pjmedia_vid_dev_factory* pjmedia_and_factory(pj_pool_factory *pf); #endif #define MAX_DRIVERS PJMEDIA_VID_DEV_MAX_DRIVERS #define MAX_DEVS PJMEDIA_VID_DEV_MAX_DEVS /* driver structure */ struct driver { /* Creation function */ pjmedia_vid_dev_factory_create_func_ptr create; /* Factory instance */ pjmedia_vid_dev_factory *f; char name[32]; /* Driver name */ unsigned dev_cnt; /* Number of devices */ unsigned start_idx; /* Start index in global list */ int cap_dev_idx; /* Default capture device. */ int rend_dev_idx; /* Default render device */ }; /* The video device subsystem */ static struct vid_subsys { unsigned init_count; /* How many times init() is called */ pj_pool_factory *pf; /* The pool factory. */ unsigned drv_cnt; /* Number of drivers. */ struct driver drv[MAX_DRIVERS]; /* Array of drivers. */ unsigned dev_cnt; /* Total number of devices. */ pj_uint32_t dev_list[MAX_DEVS];/* Array of device IDs. */ } vid_subsys; /* API: get capability name/info */ PJ_DEF(const char*) pjmedia_vid_dev_cap_name(pjmedia_vid_dev_cap cap, const char **p_desc) { const char *desc; unsigned i; if (p_desc==NULL) p_desc = &desc; for (i=0; iname; \ *size = sizeof(param->name) switch (cap) { case PJMEDIA_VID_DEV_CAP_FORMAT: FIELD_INFO(fmt); break; case PJMEDIA_VID_DEV_CAP_INPUT_SCALE: FIELD_INFO(disp_size); break; case PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW: FIELD_INFO(window); break; case PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE: FIELD_INFO(disp_size); break; case PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION: FIELD_INFO(window_pos); break; case PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE: FIELD_INFO(window_hide); break; case PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW: FIELD_INFO(native_preview); break; case PJMEDIA_VID_DEV_CAP_ORIENTATION: FIELD_INFO(orient); break; /* The PJMEDIA_VID_DEV_CAP_SWITCH does not have an entry in the * param (it doesn't make sense to open a stream and tell it * to switch immediately). */ case PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS: FIELD_INFO(window_flags); break; default: return PJMEDIA_EVID_INVCAP; } #undef FIELD_INFO return PJ_SUCCESS; } /* API: set cap value to param */ PJ_DEF(pj_status_t) pjmedia_vid_dev_param_set_cap( pjmedia_vid_dev_param *param, pjmedia_vid_dev_cap cap, const void *pval) { void *cap_ptr; unsigned cap_size; pj_status_t status; status = get_cap_pointer(param, cap, &cap_ptr, &cap_size); if (status != PJ_SUCCESS) return status; pj_memcpy(cap_ptr, pval, cap_size); param->flags |= cap; return PJ_SUCCESS; } /* API: get cap value from param */ PJ_DEF(pj_status_t) pjmedia_vid_dev_param_get_cap( const pjmedia_vid_dev_param *param, pjmedia_vid_dev_cap cap, void *pval) { void *cap_ptr; unsigned cap_size; pj_status_t status; status = get_cap_pointer(param, cap, &cap_ptr, &cap_size); if (status != PJ_SUCCESS) return status; if ((param->flags & cap) == 0) { pj_bzero(cap_ptr, cap_size); return PJMEDIA_EVID_INVCAP; } pj_memcpy(pval, cap_ptr, cap_size); return PJ_SUCCESS; } /* Internal: init driver */ static pj_status_t init_driver(unsigned drv_idx, pj_bool_t refresh) { struct driver *drv = &vid_subsys.drv[drv_idx]; pjmedia_vid_dev_factory *f; unsigned i, dev_cnt; pj_status_t status; if (!refresh) { /* Create the factory */ f = (*drv->create)(vid_subsys.pf); if (!f) return PJ_EUNKNOWN; /* Call factory->init() */ status = f->op->init(f); if (status != PJ_SUCCESS) { f->op->destroy(f); return status; } } else { f = drv->f; } /* Get number of devices */ dev_cnt = f->op->get_dev_count(f); if (dev_cnt + vid_subsys.dev_cnt > MAX_DEVS) { PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because" " there are too many devices", vid_subsys.dev_cnt + dev_cnt - MAX_DEVS)); dev_cnt = MAX_DEVS - vid_subsys.dev_cnt; } /* enabling this will cause pjsua-lib initialization to fail when there * is no video device installed in the system, even when pjsua has been * run with --null-video * if (dev_cnt == 0) { f->op->destroy(f); return PJMEDIA_EVID_NODEV; } */ /* Fill in default devices */ drv->rend_dev_idx = drv->cap_dev_idx = -1; for (i=0; iop->get_dev_info(f, i, &info); if (status != PJ_SUCCESS) { f->op->destroy(f); return status; } if (drv->name[0]=='\0') { /* Set driver name */ pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name)); drv->name[sizeof(drv->name)-1] = '\0'; } if (drv->rend_dev_idx < 0 && (info.dir & PJMEDIA_DIR_RENDER)) { /* Set default render device */ drv->rend_dev_idx = i; } if (drv->cap_dev_idx < 0 && (info.dir & PJMEDIA_DIR_CAPTURE)) { /* Set default capture device */ drv->cap_dev_idx = i; } if (drv->rend_dev_idx >= 0 && drv->cap_dev_idx >= 0) { /* Done. */ break; } } /* Register the factory */ drv->f = f; drv->f->sys.drv_idx = drv_idx; drv->start_idx = vid_subsys.dev_cnt; drv->dev_cnt = dev_cnt; /* Register devices to global list */ for (i=0; if) { drv->f->op->destroy(drv->f); drv->f = NULL; } drv->dev_cnt = 0; drv->rend_dev_idx = drv->cap_dev_idx = -1; } /* API: Initialize the video device subsystem. */ PJ_DEF(pj_status_t) pjmedia_vid_dev_subsys_init(pj_pool_factory *pf) { unsigned i; pj_status_t status = PJ_SUCCESS; /* Allow init() to be called multiple times as long as there is matching * number of shutdown(). */ if (vid_subsys.init_count++ != 0) { return PJ_SUCCESS; } /* Register error subsystem */ pj_register_strerror(PJMEDIA_VIDEODEV_ERRNO_START, PJ_ERRNO_SPACE_SIZE, &pjmedia_videodev_strerror); /* Init */ vid_subsys.pf = pf; vid_subsys.drv_cnt = 0; vid_subsys.dev_cnt = 0; /* Register creation functions */ #if PJMEDIA_VIDEO_DEV_HAS_V4L2 vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_v4l2_factory; #endif #if PJMEDIA_VIDEO_DEV_HAS_AVF vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_avf_factory; #endif #if PJMEDIA_VIDEO_DEV_HAS_OPENGL vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_opengl_factory; #endif #if PJMEDIA_VIDEO_DEV_HAS_IOS vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_ios_factory; #endif #if PJMEDIA_VIDEO_DEV_HAS_DSHOW vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_dshow_factory; #endif #if PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_cbar_factory; #endif #if PJMEDIA_VIDEO_DEV_HAS_FB vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_fb_factory; #endif #if PJMEDIA_VIDEO_DEV_HAS_NULL vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_null_factory; #endif /* Initialize each factory and build the device ID list */ for (i=0; iinit() */ status = factory->op->init(factory); if (status != PJ_SUCCESS) { factory->op->destroy(factory); return status; } refresh = PJ_TRUE; } status = init_driver(vid_subsys.drv_cnt, refresh); if (status == PJ_SUCCESS) { vid_subsys.drv_cnt++; } else { deinit_driver(vid_subsys.drv_cnt); } return status; } /* API: unregister a video device factory from the video device subsystem. */ PJ_DEF(pj_status_t) pjmedia_vid_unregister_factory(pjmedia_vid_dev_factory_create_func_ptr adf, pjmedia_vid_dev_factory *factory) { unsigned i, j; if (vid_subsys.init_count == 0) return PJMEDIA_EVID_INIT; for (i=0; if==factory) || (adf && drv->create == adf)) { for (j = drv->start_idx; j < drv->start_idx + drv->dev_cnt; j++) { vid_subsys.dev_list[j] = (pj_uint32_t)PJMEDIA_VID_INVALID_DEV; } deinit_driver(i); pj_bzero(drv, sizeof(*drv)); return PJ_SUCCESS; } } return PJMEDIA_EVID_ERR; } /* API: get the pool factory registered to the video device subsystem. */ PJ_DEF(pj_pool_factory*) pjmedia_vid_dev_subsys_get_pool_factory(void) { return vid_subsys.pf; } /* API: Shutdown the video device subsystem. */ PJ_DEF(pj_status_t) pjmedia_vid_dev_subsys_shutdown(void) { unsigned i; /* Allow shutdown() to be called multiple times as long as there is * matching number of init(). */ if (vid_subsys.init_count == 0) { return PJ_SUCCESS; } --vid_subsys.init_count; if (vid_subsys.init_count == 0) { for (i=0; if && drv->f->op->refresh) { pj_status_t status = drv->f->op->refresh(drv->f); if (status != PJ_SUCCESS) { PJ_PERROR(4, (THIS_FILE, status, "Unable to refresh device " "list for %s", drv->name)); } } init_driver(i, PJ_TRUE); } return PJ_SUCCESS; } /* API: Get the number of video devices installed in the system. */ PJ_DEF(unsigned) pjmedia_vid_dev_count(void) { return vid_subsys.dev_cnt; } /* Internal: convert local index to global device index */ static pj_status_t make_global_index(unsigned drv_idx, pjmedia_vid_dev_index *id) { if (*id < 0) { return PJ_SUCCESS; } /* Check that factory still exists */ PJ_ASSERT_RETURN(vid_subsys.drv[drv_idx].f, PJ_EBUG); /* Check that device index is valid */ PJ_ASSERT_RETURN(*id>=0 && *id<(int)vid_subsys.drv[drv_idx].dev_cnt, PJ_EBUG); *id += vid_subsys.drv[drv_idx].start_idx; return PJ_SUCCESS; } /* Internal: lookup device id */ static pj_status_t lookup_dev(pjmedia_vid_dev_index id, pjmedia_vid_dev_factory **p_f, unsigned *p_local_index) { int f_id, index; if (id < 0) { unsigned i; if (id <= PJMEDIA_VID_INVALID_DEV) return PJMEDIA_EVID_INVDEV; for (i=0; icap_dev_idx >= 0) { id = drv->cap_dev_idx; make_global_index(i, &id); break; } else if (id==PJMEDIA_VID_DEFAULT_RENDER_DEV && drv->rend_dev_idx >= 0) { id = drv->rend_dev_idx; make_global_index(i, &id); break; } } if (id < 0) { return PJMEDIA_EVID_NODEFDEV; } } f_id = GET_FID(vid_subsys.dev_list[id]); index = GET_INDEX(vid_subsys.dev_list[id]); if (f_id < 0 || f_id >= (int)vid_subsys.drv_cnt) return PJMEDIA_EVID_INVDEV; if (index < 0 || index >= (int)vid_subsys.drv[f_id].dev_cnt) return PJMEDIA_EVID_INVDEV; *p_f = vid_subsys.drv[f_id].f; *p_local_index = (unsigned)index; return PJ_SUCCESS; } /* API: lookup device id */ PJ_DEF(pj_status_t) pjmedia_vid_dev_get_local_index(pjmedia_vid_dev_index id, pjmedia_vid_dev_factory **p_f, unsigned *p_local_index) { return lookup_dev(id, p_f, p_local_index); } /* API: from factory and local index, get global index */ PJ_DEF(pj_status_t) pjmedia_vid_dev_get_global_index(const pjmedia_vid_dev_factory *f, unsigned local_idx, pjmedia_vid_dev_index *pid) { PJ_ASSERT_RETURN(f->sys.drv_idx >= 0 && f->sys.drv_idx < MAX_DRIVERS, PJ_EINVALIDOP); *pid = local_idx; return make_global_index(f->sys.drv_idx, pid); } /* API: Get device information. */ PJ_DEF(pj_status_t) pjmedia_vid_dev_get_info(pjmedia_vid_dev_index id, pjmedia_vid_dev_info *info) { pjmedia_vid_dev_factory *f; unsigned index; pj_status_t status; PJ_ASSERT_RETURN(info, PJ_EINVAL); PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT); if (id <= PJMEDIA_VID_INVALID_DEV) return PJMEDIA_EVID_INVDEV; status = lookup_dev(id, &f, &index); if (status != PJ_SUCCESS) return status; status = f->op->get_dev_info(f, index, info); /* Make sure device ID is the real ID (not PJMEDIA_VID_DEFAULT_*_DEV) */ info->id = index; make_global_index(f->sys.drv_idx, &info->id); return status; } /* API: find device */ PJ_DEF(pj_status_t) pjmedia_vid_dev_lookup( const char *drv_name, const char *dev_name, pjmedia_vid_dev_index *id) { pjmedia_vid_dev_factory *f = NULL; unsigned drv_idx, dev_idx; PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL); PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT); for (drv_idx=0; drv_idxop->get_dev_info(f, dev_idx, &info); if (status != PJ_SUCCESS) return status; if (!pj_ansi_stricmp(dev_name, info.name)) break; } if (dev_idx==vid_subsys.drv[drv_idx].dev_cnt) return PJ_ENOTFOUND; *id = dev_idx; make_global_index(drv_idx, id); return PJ_SUCCESS; } /* API: Initialize the video device parameters with default values for the * specified device. */ PJ_DEF(pj_status_t) pjmedia_vid_dev_default_param(pj_pool_t *pool, pjmedia_vid_dev_index id, pjmedia_vid_dev_param *param) { pjmedia_vid_dev_factory *f; unsigned index; pj_status_t status; PJ_ASSERT_RETURN(param, PJ_EINVAL); PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT); if (id <= PJMEDIA_VID_INVALID_DEV) return PJMEDIA_EVID_INVDEV; status = lookup_dev(id, &f, &index); if (status != PJ_SUCCESS) return status; status = f->op->default_param(pool, f, index, param); if (status != PJ_SUCCESS) return status; /* Normalize device IDs */ make_global_index(f->sys.drv_idx, ¶m->cap_id); make_global_index(f->sys.drv_idx, ¶m->rend_id); return PJ_SUCCESS; } /* API: Open video stream object using the specified parameters. */ PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_create( pjmedia_vid_dev_param *prm, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm) { pjmedia_vid_dev_factory *cap_f=NULL, *rend_f=NULL, *f=NULL; pj_status_t status; PJ_ASSERT_RETURN(prm && prm->dir && p_vid_strm, PJ_EINVAL); PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT); PJ_ASSERT_RETURN(prm->dir==PJMEDIA_DIR_CAPTURE || prm->dir==PJMEDIA_DIR_RENDER || prm->dir==PJMEDIA_DIR_CAPTURE_RENDER, PJ_EINVAL); /* Normalize cap_id */ if (prm->dir & PJMEDIA_DIR_CAPTURE) { unsigned index; if (prm->cap_id < 0) prm->cap_id = PJMEDIA_VID_DEFAULT_CAPTURE_DEV; status = lookup_dev(prm->cap_id, &cap_f, &index); if (status != PJ_SUCCESS) return status; prm->cap_id = index; f = cap_f; } /* Normalize rend_id */ if (prm->dir & PJMEDIA_DIR_RENDER) { unsigned index; if (prm->rend_id < 0) prm->rend_id = PJMEDIA_VID_DEFAULT_RENDER_DEV; status = lookup_dev(prm->rend_id, &rend_f, &index); if (status != PJ_SUCCESS) return status; prm->rend_id = index; f = rend_f; } PJ_ASSERT_RETURN(f != NULL, PJ_EBUG); /* For now, cap_id and rend_id must belong to the same factory */ PJ_ASSERT_RETURN((prm->dir != PJMEDIA_DIR_CAPTURE_RENDER) || (cap_f == rend_f), PJMEDIA_EVID_INVDEV); /* Create the stream */ status = f->op->create_stream(f, prm, cb, user_data, p_vid_strm); if (status != PJ_SUCCESS) return status; /* Assign factory id to the stream */ (*p_vid_strm)->sys.drv_idx = f->sys.drv_idx; return PJ_SUCCESS; } /* API: Get the running parameters for the specified video stream. */ PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_param( pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_param *param) { pj_status_t status; PJ_ASSERT_RETURN(strm && param, PJ_EINVAL); PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT); status = strm->op->get_param(strm, param); if (status != PJ_SUCCESS) return status; /* Normalize device id's */ make_global_index(strm->sys.drv_idx, ¶m->cap_id); make_global_index(strm->sys.drv_idx, ¶m->rend_id); return PJ_SUCCESS; } /* API: Get the value of a specific capability of the video stream. */ PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_cap( pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, void *value) { return strm->op->get_cap(strm, cap, value); } /* API: Set the value of a specific capability of the video stream. */ PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_set_cap( pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, const void *value) { /* For fast switching, device global index needs to be converted to * local index before forwarding the request to the device stream. */ if (cap == PJMEDIA_VID_DEV_CAP_SWITCH) { pjmedia_vid_dev_factory *f; unsigned local_idx; pj_status_t status; pjmedia_vid_dev_switch_param p = *(pjmedia_vid_dev_switch_param*)value; status = lookup_dev(p.target_id, &f, &local_idx); if (status != PJ_SUCCESS) return status; /* Make sure that current & target devices share the same factory */ if (f->sys.drv_idx != strm->sys.drv_idx) return PJMEDIA_EVID_INVDEV; p.target_id = local_idx; return strm->op->set_cap(strm, cap, &p); } return strm->op->set_cap(strm, cap, value); } /* API: Start the stream. */ PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_start(pjmedia_vid_dev_stream *strm) { pj_status_t status; if (pjmedia_vid_dev_stream_is_running(strm)) return PJ_SUCCESS; status = strm->op->start(strm); if (status == PJ_SUCCESS) strm->sys.is_running = PJ_TRUE; return status; } /* API: has it been started? */ PJ_DEF(pj_bool_t) pjmedia_vid_dev_stream_is_running(pjmedia_vid_dev_stream *strm) { return strm->sys.is_running; } PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_frame( pjmedia_vid_dev_stream *strm, pjmedia_frame *frame) { pj_assert(strm->op->get_frame); return strm->op->get_frame(strm, frame); } PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_put_frame( pjmedia_vid_dev_stream *strm, const pjmedia_frame *frame) { pj_assert(strm->op->put_frame); return strm->op->put_frame(strm, frame); } /* API: Stop the stream. */ PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_stop(pjmedia_vid_dev_stream *strm) { strm->sys.is_running = PJ_FALSE; return strm->op->stop(strm); } /* API: Destroy the stream. */ PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_destroy( pjmedia_vid_dev_stream *strm) { strm->sys.is_running = PJ_FALSE; return strm->op->destroy(strm); } #endif /* PJMEDIA_HAS_VIDEO */ ================================================ FILE: deps/pjsip/pjnath/build/Makefile ================================================ # By default, the test application includes main.o. # OS make file may override this with os-specific files export PJNATH_TEST_OBJS = main.o include ../../build.mak include ../../version.mak include $(PJDIR)/build/common.mak RULES_MAK := $(PJDIR)/build/rules.mak PJLIB_LIB:=../../pjlib/lib/libpj-$(TARGET_NAME)$(LIBEXT) PJLIB_UTIL_LIB:=../../pjlib-util/lib/libpjlib-util-$(TARGET_NAME)$(LIBEXT) export PJNATH_LIB:=../lib/libpjnath-$(TARGET_NAME)$(LIBEXT) ############################################################################### # Gather all flags. # export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ $(CFLAGS) $(CC_INC)../include $(CC_INC)../../pjlib/include \ $(CC_INC)../../pjlib-util/include export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ $(HOST_CXXFLAGS) $(CXXFLAGS) export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJNATH_LIB)) \ $(subst /,$(HOST_PSEP),$(PJLIB_UTIL_LIB)) \ $(subst /,$(HOST_PSEP),$(PJLIB_LIB)) \ $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ $(LDFLAGS) ############################################################################### # Defines for building PJNATH library # export PJNATH_SRCDIR = ../src/pjnath export PJNATH_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ errno.o ice_session.o ice_strans.o nat_detect.o stun_auth.o \ stun_msg.o stun_msg_dump.o stun_session.o stun_sock.o \ stun_transaction.o turn_session.o turn_sock.o export PJNATH_CFLAGS += $(_CFLAGS) ############################################################################### # Defines for building test application # export PJNATH_TEST_SRCDIR = ../src/pjnath-test export PJNATH_TEST_OBJS += ice_test.o stun.o sess_auth.o server.o concur_test.o \ stun_sock_test.o turn_sock_test.o test.o export PJNATH_TEST_CFLAGS += $(_CFLAGS) export PJNATH_TEST_LDFLAGS += $(_LDFLAGS) export PJNATH_TEST_EXE:=../bin/pjnath-test-$(TARGET_NAME)$(HOST_EXE) ############################################################################### # Defines for building TURN client application # export PJTURN_CLIENT_SRCDIR = ../src/pjturn-client export PJTURN_CLIENT_OBJS += client_main.o export PJTURN_CLIENT_CFLAGS += $(_CFLAGS) export PJTURN_CLIENT_LDFLAGS += $(_LDFLAGS) export PJTURN_CLIENT_EXE:=../bin/pjturn-client-$(TARGET_NAME)$(HOST_EXE) ############################################################################### # Defines for building TURN server application # export PJTURN_SRV_SRCDIR = ../src/pjturn-srv export PJTURN_SRV_OBJS += allocation.o auth.o listener_udp.o \ listener_tcp.o server.o main.o export PJTURN_SRV_CFLAGS += $(_CFLAGS) export PJTURN_SRV_LDFLAGS += $(_LDFLAGS) export PJTURN_SRV_EXE:=../bin/pjturn-srv-$(TARGET_NAME)$(HOST_EXE) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Main entry # # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. # TARGETS := pjnath pjturn-client pjturn-srv all: $(TARGETS) doc: cd .. && rm -rf docs/$(PJ_VERSION) && doxygen docs/doxygen.cfg @if [ -n "$(WWWDIR)" ] && ! [ -d "$(WWWDIR)/docs/$(PJ_VERSION)/pjnath/docs/html" ] ; then \ echo "Creating docs/$(PJ_VERSION)/pjnath/docs/html" ; \ mkdir -p $(WWWDIR)/docs/$(PJ_VERSION)/pjnath/docs/html ; \ fi @if [ -n "$(WWWDIR)" ] && [ -d "$(WWWDIR)/docs/$(PJ_VERSION)/pjnath/docs/html" ] ; then \ echo "Copying docs/$(PJ_VERSION) to $(WWWDIR)/docs/$(PJ_VERSION)/pjnath/docs/html.." ; \ cp -v -a ../docs/$(PJ_VERSION)/html/* $(WWWDIR)/docs/$(PJ_VERSION)/pjnath/docs/html/ ; \ fi dep: depend distclean: realclean .PHONY: dep depend pjlib pjlib-test clean realclean distclean pjnath: $(MAKE) -f $(RULES_MAK) APP=PJNATH app=pjnath $(PJNATH_LIB) $$(PJNATH_LIB): pjnath pjnath-test: $(PJLIB_LIB) $(PJLIB_UTIL_LIB) $(PJNATH_LIB) $(MAKE) -f $(RULES_MAK) APP=PJNATH_TEST app=pjnath-test $(PJNATH_TEST_EXE) pjturn-client: $(PJLIB_LIB) $(PJLIB_UTIL_LIB) $(PJNATH_LIB) $(MAKE) -f $(RULES_MAK) APP=PJTURN_CLIENT app=pjturn-client $(PJTURN_CLIENT_EXE) pjturn-srv: $(PJLIB_LIB) $(PJLIB_UTIL_LIB) $(PJNATH_LIB) $(MAKE) -f $(RULES_MAK) APP=PJTURN_SRV app=pjturn-srv $(PJTURN_SRV_EXE) .PHONY: ../lib/pjnath.ko ../lib/pjnath.ko: echo Making $@ $(MAKE) -f $(RULES_MAK) APP=PJNATH app=pjnath $@ .PHONY: ../lib/pjnath-test.ko ../lib/pjnath-test.ko: $(MAKE) -f $(RULES_MAK) APP=PJNATH_TEST app=pjnath-test $@ clean: $(MAKE) -f $(RULES_MAK) APP=PJNATH app=pjnath $@ $(MAKE) -f $(RULES_MAK) APP=PJNATH_TEST app=pjnath-test $@ $(MAKE) -f $(RULES_MAK) APP=PJTURN_CLIENT app=pjturn-client $@ $(MAKE) -f $(RULES_MAK) APP=PJTURN_SRV app=pjturn-srv $@ realclean: $(subst @@,$(subst /,$(HOST_PSEP),.pjnath-$(TARGET_NAME).depend),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),.pjnath-test-$(TARGET_NAME).depend),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),.pjturn-client-$(TARGET_NAME).depend),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),.pjturn-srv-$(TARGET_NAME).depend),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=PJNATH app=pjnath $@ $(MAKE) -f $(RULES_MAK) APP=PJNATH_TEST app=pjnath-test $@ $(MAKE) -f $(RULES_MAK) APP=PJTURN_CLIENT app=pjturn-client $@ $(MAKE) -f $(RULES_MAK) APP=PJTURN_SRV app=pjturn-srv $@ depend: $(MAKE) -f $(RULES_MAK) APP=PJNATH app=pjnath $@ $(MAKE) -f $(RULES_MAK) APP=PJNATH_TEST app=pjnath-test $@ $(MAKE) -f $(RULES_MAK) APP=PJTURN_CLIENT app=pjturn-client $@ $(MAKE) -f $(RULES_MAK) APP=PJTURN_SRV app=pjturn-srv $@ echo '$(PJNATH_TEST_EXE): $(PJNATH_LIB) $(PJLIB_UTIL_LIB) $(PJLIB_LIB)' >> .pjnath-test-$(TARGET_NAME).depend echo '$(PJTURN_CLIENT_EXE): $(PJNATH_LIB) $(PJLIB_UTIL_LIB) $(PJLIB_LIB)' >> .pjturn-client-$(TARGET_NAME).depend echo '$(PJTURN_SRV_EXE): $(PJNATH_LIB) $(PJLIB_UTIL_LIB) $(PJLIB_LIB)' >> .pjturn-srv-$(TARGET_NAME).depend ================================================ FILE: deps/pjsip/pjnath/docs/doc_ice.h ================================================ /* $Id: doc_ice.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** @defgroup PJNATH_ICE ICE: Interactive Connectivity Establishment @brief Interactive Connectivity Establishment (ICE) @ingroup PJNATH */ /** @defgroup PJNATH_ICE_STREAM_TRANSPORT ICE stream transport @brief Transport for media streams using ICE @ingroup PJNATH_ICE */ /** @defgroup PJNATH_ICE_SESSION ICE Session @brief Transport Independent ICE Session @ingroup PJNATH_ICE */ /** @addtogroup PJNATH_ICE \section org Library organizations See Table of Contents below. \section ice_intro_sec Introduction to ICE Interactive Connectivity Establishment (ICE) is the ultimate weapon a client can have in its NAT traversal solution arsenals, as it promises that if there is indeed one path for two clients to communicate, then ICE will find this path. And if there are more than one paths which the clients can communicate, ICE will use the best/most efficient one. ICE works by combining several protocols (such as STUN and TURN) altogether and offering several candidate paths for the communication, thereby maximising the chance of success, but at the same time also has the capability to prioritize the candidates, so that the more expensive alternative (namely relay) will only be used as the last resort when else fails. ICE negotiation process involves several stages: - candidate gathering, where the client finds out all the possible addresses that it can use for the communication. It may find three types of candidates: host candidate to represent its physical NICs, server reflexive candidate for the address that has been resolved from STUN, and relay candidate for the address that the client has allocated from a TURN relay. - prioritizing these candidates. Typically the relay candidate will have the lowest priority to use since it's the most expensive. - encoding these candidates, sending it to remote peer, and negotiating it with offer-answer. - pairing the candidates, where it pairs every local candidates with every remote candidates that it receives from the remote peer. - checking the connectivity for each candidate pairs. - concluding the result. Since every possible path combinations are checked, if there is a path to communicate ICE will find it. \section icestrans_sec Using ICE transport The \ref PJNATH_ICE_STREAM_TRANSPORT is a ready to use object which performs the above ICE operations as well as provides application with interface to send and receive data using the negotiated path. Please see \ref PJNATH_ICE_STREAM_TRANSPORT on how to use this object. \section ice_owntransport_sec Creating custom ICE transport If the \ref PJNATH_ICE_STREAM_TRANSPORT is not suitable for use for some reason, you will need to implement your own ICE transport, by combining the \ref PJNATH_ICE_SESSION with your own means to send and receive packets. The \ref PJNATH_ICE_STREAM_TRANSPORT provides the best example on how to do this. \section ice_samples_sec Samples The \ref ice_demo_sample sample demonstrates how to use \ref PJNATH_ICE_STREAM_TRANSPORT without using signaling protocol such as SIP. It provides interactive user interface to create and manage the ICE sessions as well as to exchange SDP with another ice_demo instance. Also see \ref samples_page for other samples. */ ================================================ FILE: deps/pjsip/pjnath/docs/doc_mainpage.h ================================================ /* $Id: doc_mainpage.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** @mainpage PJNATH - Open Source ICE, STUN, and TURN Library PJNATH (PJSIP NAT Helper) is an open source library providing NAT traversal functionalities by using standard based protocols such as STUN, TURN, and ICE. \section background Background Network Address Translation (NAT) is commonly deployed everywhere primarily to alleviate the exhaustion of IPv4 address space by allowing multiple hosts to share a public/Internet address. While NAT would work well for typical client server communications (such as web and email), since it's always the client that initiates the conversation and normally client doesn't need to maintain the connection for a long time, installation of NAT would cause major problem for peer-to-peer communication, such as (and especially) VoIP. \ref nat_intro "Read more.." \section intro Introduction to PJNATH PJSIP NAT Helper (PJNATH) is a library which contains the implementation of standard based NAT traversal solutions. PJNATH can be used as a stand-alone library for your software, or you may use PJSUA-LIB library, a very high level library integrating PJSIP, PJMEDIA, and PJNATH into simple to use APIs. PJNATH has the following features: - STUNbis implementation,\n providing both ready to use STUN-aware socket and framework to implement higher level STUN based protocols such as TURN and ICE. The implementation complies to RFC 5389 standard.\n\n - NAT type detection, \n performs detection of the NAT type in front of the endpoint, according to RFC 3489. While the practice to detect the NAT type to assist NAT traversal has been deprecated in favor of ICE, the information may still be useful for troubleshooting purposes, hence the utility is provided.\n\n - Traversal Using Relays around NAT (TURN) implementation.\n TURN is a protocol for relaying communications by means of using relay, and combined with ICE it provides efficient last effort alternative for the communication path. The TURN implementation in PJNATH complies to draft-ietf-behave-turn-14 draft.\n\n - Interactive Connectivity Establishmen (ICE) implementation.\n ICE is a protocol for discovering communication path(s) between two endpoints. The implementation in PJNATH complies to draft-ietf-mmusic-ice-19.txt draft In the future, more protocols will be implemented (such as UPnP IGD, and SOCKS5). \section pjnath_organization_sec Library Organization The library provides the following main component groups: - \ref PJNATH_STUN\n\n - \ref PJNATH_TURN\n\n - \ref PJNATH_ICE\n\n - \ref PJNATH_NAT_DETECT\n\n Apart from the \ref PJNATH_NAT_DETECT, each component group are further divided into two functionalities: - Transport objects\n The transport objects (such as STUN transport, TURN transport, and ICE stream transport) are the implementation of the session object with particular transport/sockets. They are provided as ready to use objects for applications.\n\n - Transport independent/session layer\n The session objects (such as STUN session, TURN session, and ICE session) are the core object for maintaining the protocol session, and it is independent of transport (i.e. it does not "own" a socket). This way developers can reuse these session objects for any kind of transports, such as UDP, TCP, or TLS, with or without using PJLIB socket API. The session objects provide function and callback to send and receive packets respectively. For more information about each component groups, please click the component link above. \section pjnath_start_sec Getting Started with PJNATH \subsection dependency Library Dependencies The PJNATH library depends (and only depends) on PJLIB and PJLIB-UTIL libraries. All these libraries should have been packaged together with the main PJSIP distribution. You can download the PJSIP distribution from PJSIP website \subsection pjnath_using_sec Using the libraries Please click on the appropriate component under \ref pjnath_organization_sec section above, which will take you to the documentation on how to use the component. \subsection samples_sec Samples We attempt to provide simple samples to use each functionality of the PJNATH library. Please see \ref samples_page page for the list of samples. */ /** @defgroup samples_page PJNATH Samples and screenshots @brief Sample applications and screenshots */ ================================================ FILE: deps/pjsip/pjnath/docs/doc_nat.h ================================================ /* $Id: doc_nat.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** @defgroup nat_intro Introduction to Network Address Translation (NAT) and NAT Traversal @brief This page describes NAT and the problems caused by it and the solutions \section into Introduction to NAT NAT (Network Address Translation) is a mechanism where a device performs modifications to the TCP/IP address/port number of a packet and maps the IP address from one realm to another (usually from private IP address to public IP address and vice versa). This works by the NAT device allocating a temporary port number on the public side of the NAT upon forwarding outbound packet from the internal host towards the Internet, maintaining this mapping for some predefined time, and forwarding the inbound packets received from the Internet on this public port back to the internal host. NAT devices are installed primarily to alleviate the exhaustion of IPv4 address space by allowing multiple hosts to share a public/Internet address. Also due to its mapping nature (i.e. a mapping can only be created by a transmission from an internal host), NAT device is preferred to be installed even when IPv4 address exhaustion is not a problem (for example when there is only one host at home), to provide some sort of security/shield for the internal hosts against threats from the Internet. Despite the fact that NAT provides some shields for the internal network, one must distinguish NAT solution from firewall solution. NAT is not a firewall solution. A firewall is a security solution designed to enforce the security policy of an organization, while NAT is a connectivity solution to allow multiple hosts to use a single public IP address. Understandably both functionalities are difficult to separate at times, since many (typically consumer) products claims to do both with the same device and simply label the device a NAT box. But we do want to make this distinction rather clear, as PJNATH is a NAT traversal helper and not a firewall bypass solution (yet). \section problems The NAT traversal problems While NAT would work well for typical client server communications (such as web and email), since it's always the client that initiates the conversation and normally client doesn't need to maintain the connection for a long time, installation of NAT would cause major problem for peer-to-peer communication, such as (and especially) VoIP. These problems will be explained in more detail below. \subsection peer_addr Peer address problem In VoIP, normally we want the media (audio, and video) to flow directly between the clients, since relaying is costly (both in terms of bandwidth cost for service provider, and additional latency introduced by relaying). To do this, each client informs its media transport address to the other client , by sending it via the VoIP signaling path, and the other side would send its media to this transport address. And there lies the problem. If the client software is not NAT aware, then it would send its private IP address to the other client, and the other client would not be able to send media to this address. Traditionally this was solved by using STUN. With this mechanism, the client first finds out its public IP address/port by querying a STUN server, then send sthis public address instead of its private address to the other client. When both sides are using this mechanism, they can then send media packets to these addresses, thereby creating a mapping in the NAT (also called opening a "hole", hence this mechanism is also popularly called "hole punching") and both can then communicate with each other. But this mechanism does not work in all cases, as will be explained below. \subsection hairpin Hairpinning behavior Hairpin is a behavior where a NAT device forwards packets from a host in internal network (lets call it host A) back to some other host (host B) in the same internal network, when it detects that the (public IP address) destination of the packet is actually a mapped IP address that was created for the internal host (host B). This is a desirable behavior of a NAT, but unfortunately not all NAT devices support this. Lacking this behavior, two (internal) hosts behind the same NAT will not be able to communicate with each other if they exchange their public addresses (resolved by STUN above) to each other. \subsection symmetric Symmetric behavior NAT devices don't behave uniformly and people have been trying to classify their behavior into different classes. Traditionally NAT devices are classified into Full Cone, Restricted Cone, Port Restricted Cone, and Symmetric types, according to RFC 3489 section 5. A more recent method of classification, as explained by RFC 4787, divides the NAT behavioral types into two attributes: the mapping behavior attribute and the filtering behavior attribute. Each attribute can be one of three types: Endpoint-Independent, Address-Dependent, or Address and Port-Dependent. With this new classification method, a Symmetric NAT actually is an Address and Port-Dependent mapping NAT. Among these types, the Symmetric type is the hardest one to work with. The problem is because the NAT allocates different mapping (of the same internal host) for the communication to the STUN server and the communication to the other (external) hosts, so the IP address/port that is informed by one host to the other is meaningless for the recipient since this is not the actual IP address/port mapping that the NAT device creates. The result is when the recipient host tries to send a packet to this address, the NAT device would drop the packet since it does not recognize the sender of the packet as the "authorized" hosts to send to this address. There are two solutions for this. The first, we could make the client smarter by switching transmission of the media to the source address of the media packets. This would work since normally clients uses a well known trick called symmetric RTP, where they use one socket for both transmitting and receiving RTP/media packets. We also use this mechanism in PJMEDIA media transport. But this solution only works if a client behind a symmetric NAT is not communicating with other client behind either symmetric NAT or port-restricted NAT. The second solution is to use media relay, but as have been mentioned above, relaying is costly, both in terms of bandwidth cost for service provider and additional latency introduced by relaying. \subsection binding_timeout Binding timeout When a NAT device creates a binding (a public-private IP address mapping), it will associate a timer with it. The timer is used to destroy the binding once there is no activity/traffic associated with the binding. Because of this, a NAT aware application that wishes to keep the binding open must periodically send outbound packets, a mechanism known as keep-alive, or otherwise it will ultimately loose the binding and unable to receive incoming packets from Internet. \section solutions The NAT traversal solutions \subsection stun Old STUN (RFC 3489) The original STUN (Simple Traversal of User Datagram Protocol (UDP) Through Network Address Translators (NATs)) as defined by RFC 3489 (published in 2003, but the work was started as early as 2001) was meant to be a standalone, standard-based solution for the NAT connectivity problems above. It is equipped with NAT type detection algoritm and methods to hole-punch the NAT in order to let traffic to get through and has been proven to be quite successful in traversing many types of NATs, hence it has gained a lot of popularity as a simple and effective NAT traversal solution. But since then the smart people at IETF has realized that STUN alone is not going to be enough. Besides its nature that STUN solution cannot solve the symmetric-to-symmetric or port-restricted connection, people have also discovered that NAT behavior can change for different traffic (or for the same traffic overtime) hence it was concluded that NAT type detection could produce unreliable results hence one should not rely too much on it. Because of this, STUN has since moved its efforts to different strategy. Instead of attempting to provide a standalone solution, it's now providing a part solution and framework to build other (STUN based) protocols on top of it, such as TURN and ICE. \subsection stunbis STUN/STUNbis (RFC 5389) The Session Traversal Utilities for NAT (STUN) is the further development of the old STUN. While it still provides a mechanism for a client to query its public/mapped address to a STUN server, it has deprecated the use of NAT type detection, and now it serves as a framework to build other protocols on top of it (such as TURN and ICE). \subsection midcom_turn Old TURN (draft-rosenberg-midcom-turn) Traversal Using Relay NAT (TURN), a standard-based effort started as early as in November 2001, was meant to be the complementary method for the (old) STUN to complete the solution. The original idea was the host to use STUN to detect the NAT type, and when it has found that the NAT type is symmetric it would use TURN to relay the traffic. But as stated above, this approach was deemed to be unreliable, and now the prefered way to use TURN (and it's a new TURN specification as well) is to combine it with ICE. \subsection turn TURN (draft-ietf-behave-turn) Traversal Using Relays around NAT (TURN) is the latest development of TURN. While the protocol details have changed a lot, the objective is still the same, that is to provide relaying control for the application. As mentioned above, preferably TURN should be used with ICE since relaying is costly in terms of both bandwidth and latency, hence it should be used as the last resort. \subsection b2bua B2BUA approach A SIP Back to Back User Agents (B2BUA) is a SIP entity that sits in the middle of SIP traffic and acts as SIP user agents on both call legs. The primary motivations to have a B2BUA are to be able to provision the call (e.g. billing, enforcing policy) and to help with NAT traversal for the clients. Normally a B2BUA would be equipped with media relaying or otherwise it wouldn't be very useful. Products that fall into this category include SIP Session Border Controllers (SBC), and PBXs such as Asterisk are technically a B2BUA as well. The benefit of B2BUA with regard to helping NAT traversal is it does not require any modifications to the client to make it go through NATs. And since basically it is a relay, it should be able to traverse symmetric NAT successfully. However, since it is a relay, the usual relaying drawbacks apply, namely the bandwidth and latency issue. More over, since a B2BUA acts as user agent in either call-legs (i.e. it terminates the SIP signaling/call on one leg, albeit it creates another call on the other leg), it may also introduce serious issues with end-to-end SIP signaling. \subsection alg ALG approach Nowdays many NAT devices (such as consumer ADSL routers) are equipped with intelligence to inspect and fix VoIP traffic in its effort to help it with the NAT traversal. This feature is called Application Layer Gateway (ALG) intelligence. The idea is since the NAT device knows about the mapping, it might as well try to fix the application traffic so that the traffic could better traverse the NAT. Some tricks that are performed include for example replacing the private IP addresses/ports in the SIP/SDP packet with the mapped public address/port of the host that sends the packet. Despite many claims about its usefullness, in reality this has given us more problems than the fix. Too many devices such as these break the SIP signaling, and in more advanced case, ICE negotiation. Some examples of bad situations that we have encountered in the past: - NAT device alters the Via address/port fields in the SIP response message, making the response fail to pass SIP response verification as defined by SIP RFC. - In other case, the modifications in the Via headers of the SIP response hides the important information from the SIP server, nameny the actual IP address/port of the client as seen by the SIP server. - Modifications in the Contact URI of REGISTER request/response makes the client unable to detect it's registered binding. - Modifications in the IP addresses/ports in SDP causes ICE negotiation to fail with ice-mismatch status. - The complexity of the ALG processing in itself seems to have caused the device to behave erraticly with managing the address bindings (e.g. it creates a new binding for the second packet sent by the client, even when the previous packet was sent just second ago, or it just sends inbound packet to the wrong host). Many man-months efforts have been spent just to troubleshoot issues caused by these ALG (mal)functioning, and as it adds complexity to the problem rather than solving it, in general we do not like this approach at all and would prefer it to go away. \subsection upnp UPnP The Universal Plug and Play (UPnP) is a set of protocol specifications to control network appliances and one of its specification is to control NAT device. With this protocol, a client can instruct the NAT device to open a port in the NAT's public side and use this port for its communication. UPnP has gained popularity due to its simplicity, and one can expect it to be available on majority of NAT devices. The drawback of UPnP is since it uses multicast in its communication, it will only allow client to control one NAT device that is in the same multicast domain. While this normally is not a problem in household installations (where people normally only have one NAT router), it will not work if the client is behind cascaded routers installation. More over uPnP has serious issues with security due to its lack of authentication, it's probably not the prefered solution for organizations. \subsection other Other solutions Other solutions to NAT traversal includes: - SOCKS, which supports UDP protocol since SOCKS5. \section ice ICE Solution - The Protocol that Works Harder A new protocol is being standardized (it's in Work Group Last Call/WGLC stage at the time this article was written) by the IETF, called Interactive Connectivity Establishment (ICE). ICE is the ultimate weapon a client can have in its NAT traversal solution arsenals, as it promises that if there is indeed one path for two clients to communicate, then ICE will find this path. And if there are more than one paths which the clients can communicate, ICE will use the best/most efficient one. ICE works by combining several protocols (such as STUN and TURN) altogether and offering several candidate paths for the communication, thereby maximising the chance of success, but at the same time also has the capability to prioritize the candidates, so that the more expensive alternative (namely relay) will only be used as the last resort when else fails. ICE negotiation process involves several stages: - candidate gathering, where the client finds out all the possible addresses that it can use for the communication. It may find three types of candidates: host candidate to represent its physical NICs, server reflexive candidate for the address that has been resolved from STUN, and relay candidate for the address that the client has allocated from a TURN relay. - prioritizing these candidates. Typically the relay candidate will have the lowest priority to use since it's the most expensive. - encoding these candidates, sending it to remote peer, and negotiating it with offer-answer. - pairing the candidates, where it pairs every local candidates with every remote candidates that it receives from the remote peer. - checking the connectivity for each candidate pairs. - concluding the result. Since every possible path combinations are checked, if there is a path to communicate ICE will find it. There are many benetifs of ICE: - it's standard based. - it works where STUN works (and more) - unlike standalone STUN solution, it solves the hairpinning issue, since it also offers host candidates. - just as relaying solutions, it works with symmetric NATs. But unlike plain relaying, relay is only used as the last resort, thereby minimizing the bandwidth and latency issue of relaying. - it offers a generic framework for offering and checking address candidates. While the ICE core standard only talks about using STUN and TURN, implementors can add more types of candidates in the ICE offer, for example UDP over TCP or HTTP relays, or even uPnP candidates, and this could be done transparently for the remote peer hence it's compatible and usable even when the remote peer does not support these. - it also adds some kind of security particularly against DoS attacks, since media address must be acknowledged before it can be used. Having said that, ICE is a complex protocol to implement, making interoperability an issue, and at this time of writing we don't see many implementations of it yet. Fortunately, PJNATH has been one of the first hence more mature ICE implementation, being first released on mid-2007, and we have been testing our implementation at SIP Interoperability Test (SIPit) events regularly, so hopefully we are one of the most stable as well. \section pjnath PJNATH - The building blocks for effective NAT traversal solution PJSIP NAT Helper (PJNATH) is a library which contains the implementation of standard based NAT traversal solutions. PJNATH can be used as a stand-alone library for your software, or you may use PJSUA-LIB library, a very high level library integrating PJSIP, PJMEDIA, and PJNATH into simple to use APIs. PJNATH has the following features: - STUNbis implementation, providing both ready to use STUN-aware socket and framework to implement higher level STUN based protocols such as TURN and ICE. - NAT type detection, useful for troubleshooting purposes. - TURN implementation. - ICE implementation. More protocols will be implemented in the future. Go back to \ref index. */ ================================================ FILE: deps/pjsip/pjnath/docs/doc_samples.h ================================================ /* $Id: doc_samples.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** @addtogroup samples_page Several samples that are included in the PJSIP distributions. The screenshots below were taken on a Windows machine, but the library is very portable and it is known to run on platforms such as Linux, MacOS X, Windows Mobile, Symbian, and so on. - @ref ice_demo_sample\n This sample demonstrates how to use \ref PJNATH_ICE_STREAM_TRANSPORT without using signaling protocol such as SIP. It provides interactive user interface to create and manage the ICE sessions as well as to exchange SDP with another ice_demo instance.\n\n \image html ice_demo.jpg "ice_demo on WinXP" - @ref turn_client_sample\n This sample demonstrates how to use \ref PJNATH_TURN_SOCK and also \ref PJNATH_STUN_SOCK. It provides interactive user interface to manage allocation, permissions, and channel bindings.\n\n \image html pjturn_client.jpg "pjturn_client on WinXP" - TURN server sample\n This is a simple sample TURN server application, which we mainly use for testing (as back then there is no TURN server available).\n The source code for this application are in pjnath/src/pjturn-srv directory. */ /** \page turn_client_sample pjturn-client, a sample TURN client This is a simple, interactive TURN client application, with the following features: - DNS SRV resolution - TCP connection to TURN server - Optional fingerprint This file is pjnath/src/pjturn-client/client_main.c. Screenshot on WinXP: \image html pjturn_client.jpg "pjturn_client on WinXP" \includelineno client_main.c. */ /** \page ice_demo_sample ice_demo, an interactive ICE endpoint This sample demonstrates how to use \ref PJNATH_ICE_STREAM_TRANSPORT without using signaling protocol such as SIP. It provides interactive user interface to create and manage the ICE sessions as well as to exchange SDP with another ice_demo instance. Features of the demo application: - supports host, STUN, and TURN candidates - disabling of host candidates - DNS SRV resolution for STUN and TURN servers - TCP connection to TURN server - Optional use of fingerprint for TURN - prints and parse SDP containing ICE infos - exchange SDP with copy/paste This file is pjsip-apps/src/samples/icedemo.c Screenshot on WinXP: \image html ice_demo.jpg "ice_demo on WinXP" \includelineno icedemo.c. */ ================================================ FILE: deps/pjsip/pjnath/docs/doc_stun.h ================================================ /* $Id: doc_stun.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** @defgroup PJNATH_STUN STUN: Session Traversal Utilities for NAT @ingroup PJNATH @brief Open source STUN library */ /** @defgroup PJNATH_STUN_SOCK STUN-aware socket transport @brief STUN aware UDP socket transport @ingroup PJNATH_STUN */ /** @defgroup PJNATH_STUN_SESSION STUN session @brief STUN client and server session @ingroup PJNATH_STUN */ /** @defgroup PJNATH_STUN_BASE Base STUN objects @ingroup PJNATH_STUN @brief STUN data structures, objects, and configurations These section contains STUN base data structures as well as configurations. Among other things it contains STUN message representation and parsing, transactions, authentication framework, as well as compile-time and run-time configurations. */ /** @addtogroup PJNATH_STUN This module contains implementation of STUN library in PJNATH - the open source NAT helper containing STUN and ICE. \section stun_org_sec Library organizations The STUN part of PJNATH consists of the the following sections (see Table of Contents below). \section stun_using_sec Using the STUN transport The \ref PJNATH_STUN_SOCK is a ready to use object which provides send and receive interface for communicating UDP packets as well as means to communicate with the STUN server and manage the STUN mapped address. Some features of the \ref PJNATH_STUN_SOCK: - API to send and receive UDP packets, - interface to query the STUN mapped address info, - multiplex STUN and non-STUN incoming packets and distinguish between STUN responses that belong to internal requests with application data (the application data may be STUN packets as well), - resolution of the STUN server with DNS SRV query (if wanted), - maintaining STUN keep-alive, and - handle changes in STUN mapped address binding. Please see \ref PJNATH_STUN_SOCK for more information. \section stun_advanced_sec Advanced use of the STUN components The rest of the STUN part of the library provides lower level objects which can be used to build your own STUN based transport or protocols (officially called STUN usages). These will be explained briefly below. \subsection stun_sess_sec The STUN session A STUN session is interactive information exchange between two STUN endpoints that lasts for some period of time. It is typically started by an outgoing or incoming request, and consists of several requests, responses, and indications. All requests and responses within the session typically share a same credential. The \ref PJNATH_STUN_SESSION is a transport-independent object to manage a client or server STUN session. It is one of the core object in PJNATH, and it is used by several higher level objects including the \ref PJNATH_STUN_SOCK, \ref PJNATH_TURN_SESSION, and \ref PJNATH_ICE_SESSION. The \ref PJNATH_STUN_SESSION has the following features: - transport independent - authentication management - static or dynamic credential - client transaction management - server transaction management For more information, including how to use it please see \ref PJNATH_STUN_SESSION. \subsection stun_extending_sec Extending STUN to support other usages At present, the STUN subsystem in PJNATH supports STUN Binding, TURN, and ICE usages. If other usages are to be supported, typically you would need to add new STUN methods (and the corresponding request and response message types), attributes, and error codes to \ref PJNATH_STUN_MSG subsystem of PJNATH, as well as implementing the logic for the STUN usage. \section stunsamples_sec STUN samples The \ref turn_client_sample sample application also contains sample code to use \ref PJNATH_STUN_SOCK. Also see \ref samples_page for other samples. */ ================================================ FILE: deps/pjsip/pjnath/docs/doc_turn.h ================================================ /* $Id: doc_turn.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** @defgroup PJNATH_TURN TURN: Traversal Using Relays around NAT @brief TURN protocol implementation @ingroup PJNATH \section turn_intro_sec Introduction to TURN When a direct communication path cannot be found, it is necessary to use the services of an intermediate host that acts as a relay for the packets. This relay typically sits in the public Internet and relays packets between two hosts that both sit behind NATs. TURN allows a host behind a NAT (called the TURN client) to request that another host (called the TURN server) act as a relay. The client can arrange for the server to relay packets to and from certain other hosts (called peers) and can control aspects of how the relaying is done. The client does this by obtaining an IP address and port on the server, called the relayed-transport-address. When a peer sends a packet to the relayed-transport-address, the server relays the packet to the client. When the client sends a data packet to the server, the server relays it to the appropriate peer using the relayed- transport-address as the source. \section turn_op_sec Overview of TURN operations Discovering TURN server.\n Client learns the IP address of the TURN server either through some privisioning or by querying DNS SRV records for TURN service for the specified domain. Client may use UDP or TCP (or TLS) to connect to the TURN server. Authentication.\n All TURN operations requires the use of authentication (it uses STUN long term autentication method), hence client must be configured with the correct credential to use the service. Allocation.\n Client creates one "relay port" (or called relayed-transport-address in TURN terminology) in the TURN server by sending TURN \a Allocate request, hence this process is called creating allocation. Once the allocation is successful, client will be given the IP address and port of the "relay port" in the Allocate response. Sending data through the relay.\n Once allocation has been created, client may send data to any remote endpoints (called peers in TURN terminology) via the "relay port". It does so by sending Send Indication to the TURN server, giving the peer address in the indication message. But note that at this point peers are not allowed to send data towards the client (via the "relay port") before permission is installed for that peer. Creating permissions.\n Permission needs to be created in the TURN server so that a peer can send data to the client via the relay port (a peer in this case is identified by its IP address). Without this, when the TURN server receives data from the peer in the "relay port", it will drop this data. Receiving data from peers.\n Once permission has been installed for the peer, any data received by the TURN server (from that peer) in the "relay port" will be relayed back to client by using Data Indication. Using ChannelData.\n TURN provides optimized framing to the data by using ChannelData packetization. The client activates this format by sending ChannelBind request to the TURN server, which provides (channel) binding which maps a particular peer address with a channel number. Data sent or received to/for this peer will then use ChannelData format instead of Send or Data Indications. Refreshing the allocation, permissions, and channel bindings.\n Allocations, permissions, and channel bindings need to be refreshed periodically by client, or otherwise they will expire. Destroying the allocation.\n Once the "relay port" is no longer needed, client destroys the allocation by sending Refresh request with LIFETIME attribute set to zero. \section turn_org_sec Library organizations The TURN functionalities in PJNATH primarily consist of \ref PJNATH_TURN_SOCK and \ref PJNATH_TURN_SESSION. Please see more below. \section turn_using_sec Using TURN transport The \ref PJNATH_TURN_SOCK is a ready to use object for relaying application data via a TURN server, by managing all the operations above. Among other things it provides the following features: - resolution of the TURN server with DNS SRV - interface to create allocation, permissions, and channel bindings - interface to send and receive packets through the relay - provides callback to notify the application about incoming data - managing the allocation, permissions, and channel bindings Please see \ref PJNATH_TURN_SOCK for more documentation about and on how to use this object. \section turn_owntransport_sec Creating custom TURN transport The \ref PJNATH_TURN_SESSION is a transport-independent object to manage a client TURN session. It contains the core logic for managing the TURN client session as listed in TURN operations above, but in transport-independent manner (i.e. it doesn't have a socket), so that developer can integrate TURN client functionality into existing framework that already has its own means to send and receive data, or to support new transport types to TURN, such as TLS. You can create your own (custom) TURN transport by wrapping this into your own object, and provide it with the means to send and receive packets. Please see \ref PJNATH_TURN_SESSION for more information. \section turn_samples_sec Samples The \ref turn_client_sample is a sample application to use the \ref PJNATH_TURN_SOCK. Also there is a sample TURN server in the distribution as well. Also see \ref samples_page for other samples. */ /** * @defgroup PJNATH_TURN_SOCK TURN client transport * @brief Client transport utilizing TURN relay * @ingroup PJNATH_TURN */ /** * @defgroup PJNATH_TURN_SESSION TURN client session * @brief Transport independent TURN client session * @ingroup PJNATH_TURN */ ================================================ FILE: deps/pjsip/pjnath/docs/doxygen.cfg ================================================ # Doxyfile 1.3-rc3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # General configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "PJNATH Reference" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = $(PJ_VERSION) # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = docs/$(PJ_VERSION) # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, # Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en # (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, # Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian. OUTPUT_LANGUAGE = English # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these class will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = NO # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited # members of a class in the documentation of that class as if those members were # ordinary class members. Constructors, destructors and assignment operators of # the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. It is allowed to use relative paths in the argument list. STRIP_FROM_PATH = "c:\project\pjproject" # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower case letters. If set to YES upper case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # users are adviced to set this option to NO. CASE_SENSE_NAMES = YES # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explict @brief command for a brief description. JAVADOC_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = YES # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # reimplements. INHERIT_DOCS = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consist of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. # For instance some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources # only. Doxygen will then generate output that is more tailored for Java. # For instance namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES TYPEDEF_HIDES_STRUCT = YES #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = docs include/pjnath # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp # *.h++ *.idl *.odl FILE_PATTERNS = *.h *.c # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories # that are symbolic links (a Unix filesystem feature) are excluded from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. EXCLUDE_PATTERNS = "*_i.h" "*/compat/*" # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = ../pjsip-apps/src/samples src/pjturn-client # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = YES # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = docs # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. INPUT_FILTER = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .htm # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = docs/header.html # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = docs/footer.html # The HTML_STYLESHEET tag can be used to specify a user defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet HTML_STYLESHEET = docs/doxygen.css # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output dir. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non empty doxygen will try to run # the html help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the Html help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 1 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimised for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assigments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_XML = NO # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_PREDEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. PREDEFINED = PJ_DECL(x)=x PJ_DEF(x)=x PJ_IDECL(x)=x \ PJ_IDEF(x)=x PJ_INLINE(x)=x \ PJ_DECL_NO_RETURN(x)=x \ PJ_HAS_HIGH_RES_TIMER=1 \ PJ_LOG_MAX_LEVEL=4 \ PJ_HAS_SEMAPHORE=1 \ PJ_HAS_EVENT_OBJ=1 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse the # parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::addtions related to external references #--------------------------------------------------------------------------- # The TAGFILES tag can be used to specify one or more tagfiles. TAGFILES = ../pjlib/docs/pjlib.tag=../../../pjlib/docs/html ../pjlib-util/docs/pjlib-util.tag=../../../pjlib-util/docs/html ../pjsip/docs/pjsip.tag=../../../pjsip/docs/html ../pjmedia/docs/pjmedia.tag=../../../pjmedia/docs/html # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = ../pjnath/docs/pjnath.tag # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = NO # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). #PERL_PATH = /usr/bin/perl PERL_PATH = /c/Perl/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or # super classes. Setting the tag to NO turns the diagrams off. Note that this # option is superceded by the HAVE_DOT option below. This is only a fallback. It is # recommended to install and use dot, since it yield more powerful graphs. CLASS_DIAGRAMS = NO # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found on the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermedate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::addtions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO # The CGI_NAME tag should be the name of the CGI script that # starts the search engine (doxysearch) with the correct parameters. # A script with this name will be generated by doxygen. #CGI_NAME = search.cgi # The CGI_URL tag should be the absolute URL to the directory where the # cgi binaries are located. See the documentation of your http daemon for # details. #CGI_URL = # The DOC_URL tag should be the absolute URL to the directory where the # documentation is located. If left blank the absolute path to the # documentation, with file:// prepended to it, will be used. #DOC_URL = # The DOC_ABSPATH tag should be the absolute path to the directory where the # documentation is located. If left blank the directory on the local machine # will be used. #DOC_ABSPATH = # The BIN_ABSPATH tag must point to the directory where the doxysearch binary # is installed. #BIN_ABSPATH = /usr/local/bin/ # The EXT_DOC_PATHS tag can be used to specify one or more paths to # documentation generated for other projects. This allows doxysearch to search # the documentation for these projects as well. #EXT_DOC_PATHS = ================================================ FILE: deps/pjsip/pjnath/docs/doxygen.css ================================================ BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV { font-family: Geneva, Arial, Helvetica, sans-serif; } BODY,TD { font-size: 80%; } CODE { font-size: 120%; font-family: monospace; } .fragment, pre { font-size: 110%; font-family: monospace; } H1 { text-align: center; font-size: 240%; } H2 { font-size: 200%; margin-top : 60px; } H3 { font-size: 160%; } H4 { font-size: 120%; } CAPTION { font-weight: bold } DIV.qindex { width: 100%; background-color: #eeeeff; border: 1px solid #b0b0b0; text-align: center; margin: 2px; padding: 2px; line-height: 140%; } DIV.nav { width: 100%; background-color: #eeeeff; border: 1px solid #b0b0b0; text-align: center; margin: 2px; padding: 2px; line-height: 140%; } A.qindex { text-decoration: none; font-size: 120%; color: #1A419D; } A.qindex:visited { text-decoration: none; color: #1A419D } A.qindex:hover { text-decoration: none; background-color: #ddddff; } A.qindexHL { text-decoration: none; font-weight: bold; background-color: #6666cc; color: #ffffff; border: 1px double #9295C2; } A.qindexHL:hover { text-decoration: none; background-color: #6666cc; color: #ffffff; } A.qindexHL:visited { text-decoration: none; background-color: #6666cc; color: #ffffff } A.el { text-decoration: none; font-weight: bold } A.elRef { font-weight: bold } A.code:link { text-decoration: none; font-weight: normal; color: #0000FF; } A.code:visited { text-decoration: none; font-weight: normal; color: #0000FF} A.codeRef:link { font-weight: normal; color: #0000FF} A.codeRef:visited { font-weight: normal; color: #0000FF} A:hover { text-decoration: none; background-color: #f2f2ff } DL.el { margin-left: -1cm } PRE.fragment { border: 1px solid #CCCCCC; background-color: #f5f5f5; margin-top: 4px; margin-bottom: 4px; margin-left: 2px; margin-right: 8px; padding-left: 6px; padding-right: 6px; padding-top: 4px; padding-bottom: 4px; } DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px } TD.md { background-color: #F4F4FB; font-weight: bold; } TD.mdPrefix { background-color: #F4F4FB; color: #606060; font-size: 80%; } TD.mdname1 { background-color: #F4F4FB; font-weight: bold; color: #602020; } TD.mdname { background-color: #F4F4FB; font-weight: bold; color: #602020; width: 600px; } DIV.groupHeader { margin-left: 16px; margin-top: 12px; margin-bottom: 6px; font-weight: bold; } DIV.groupText { margin-left: 16px; font-style: italic; font-size: 90% } BODY { background: white; color: black; margin-right: 20px; margin-left: 20px; } TD.indexkey { background-color: #eeeeff; font-weight: bold; padding-right : 10px; padding-top : 2px; padding-left : 10px; padding-bottom : 2px; margin-left : 0px; margin-right : 0px; margin-top : 2px; margin-bottom : 2px; border: 1px solid #CCCCCC; } TD.indexvalue { background-color: #eeeeff; font-style: italic; padding-right : 10px; padding-top : 2px; padding-left : 10px; padding-bottom : 2px; margin-left : 0px; margin-right : 0px; margin-top : 2px; margin-bottom : 2px; border: 1px solid #CCCCCC; } TR.memlist { background-color: #f0f0f0; } P.formulaDsp { text-align: center; } IMG.formulaDsp { } IMG.formulaInl { vertical-align: middle; } SPAN.keyword { color: #008000 } SPAN.keywordtype { color: #604020 } SPAN.keywordflow { color: #e08000 } SPAN.comment { color: #800000 } SPAN.preprocessor { color: #806020 } SPAN.stringliteral { color: #002080 } SPAN.charliteral { color: #008080 } .mdTable { border: 1px solid #868686; background-color: #F4F4FB; } .mdRow { padding: 8px 10px; } .mdescLeft { padding: 0px 8px 4px 8px; font-size: 80%; font-style: italic; background-color: #FAFAFA; border-top: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; margin: 0px; } .mdescRight { padding: 0px 8px 4px 8px; font-size: 80%; font-style: italic; background-color: #FAFAFA; border-top: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; margin: 0px; } .memItemLeft { padding: 1px 0px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: solid; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memItemRight { padding: 1px 8px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: solid; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memTemplItemLeft { padding: 1px 0px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memTemplItemRight { padding: 1px 8px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memTemplParams { padding: 1px 0px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: solid; border-right-style: none; border-bottom-style: none; border-left-style: none; color: #606060; background-color: #FAFAFA; font-size: 80%; } .search { color: #003399; font-weight: bold; } FORM.search { margin-bottom: 0px; margin-top: 0px; } INPUT.search { font-size: 75%; color: #000080; font-weight: normal; background-color: #eeeeff; } TD.tiny { font-size: 75%; } a { color: #252E78; } a:visited { color: #3D2185; } .dirtab { padding: 4px; border-collapse: collapse; border: 1px solid #b0b0b0; } TH.dirtab { background: #eeeeff; font-weight: bold; } HR { height: 1px; border: none; border-top: 1px solid black; } ================================================ FILE: deps/pjsip/pjnath/docs/footer.html ================================================

 


PJNATH - Open Source NAT traversal helper library supporting STUN, TURN, and ICE
Copyright (C) 2006-2009 Teluu Inc.
================================================ FILE: deps/pjsip/pjnath/docs/header.html ================================================ $title ($projectnumber)

Home --> Documentations --> PJNATH Reference

================================================ FILE: deps/pjsip/pjnath/include/pjnath/config.h ================================================ /* $Id: config.h 4199 2012-07-05 10:52:55Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_CONFIG_H__ #define __PJNATH_CONFIG_H__ /** * @file config.h * @brief Compile time settings */ #include /** * @defgroup PJNATH_CONFIG Compile-time configurations * @brief Various compile time settings * @ingroup PJNATH_STUN_BASE * @{ */ /* ************************************************************************** * GENERAL */ /** * The log level for PJNATH error display. * * default 1 */ #ifndef PJNATH_ERROR_LEVEL # define PJNATH_ERROR_LEVEL 1 #endif /* ************************************************************************** * STUN CONFIGURATION */ /** * Maximum number of attributes in the STUN packet (for the new STUN * library). * * Default: 16 */ #ifndef PJ_STUN_MAX_ATTR # define PJ_STUN_MAX_ATTR 16 #endif /** * The default initial STUN round-trip time estimation (the RTO value * in RFC 3489-bis), in miliseconds. * This value is used to control the STUN request * retransmit time. The initial value of retransmission interval * would be set to this value, and will be doubled after each * retransmission. */ #ifndef PJ_STUN_RTO_VALUE # define PJ_STUN_RTO_VALUE 100 #endif /** * The STUN transaction timeout value, in miliseconds. * After the last retransmission is sent and if no response is received * after this time, the STUN transaction will be considered to have failed. * * The default value is 16x RTO (as per RFC 3489-bis). */ #ifndef PJ_STUN_TIMEOUT_VALUE # define PJ_STUN_TIMEOUT_VALUE (16 * PJ_STUN_RTO_VALUE) #endif /** * Maximum number of STUN transmission count. * * Default: 7 (as per RFC 3489-bis) */ #ifndef PJ_STUN_MAX_TRANSMIT_COUNT # define PJ_STUN_MAX_TRANSMIT_COUNT 7 #endif /** * Duration to keep response in the cache, in msec. * * Default: 10000 (as per RFC 3489-bis) */ #ifndef PJ_STUN_RES_CACHE_DURATION # define PJ_STUN_RES_CACHE_DURATION 10000 #endif /** * Maximum size of STUN message. */ #ifndef PJ_STUN_MAX_PKT_LEN # define PJ_STUN_MAX_PKT_LEN 800 #endif /** * Default STUN port as defined by RFC 3489. */ #define PJ_STUN_PORT 3478 /** * Padding character for string attributes. * * Default: ASCII 0 */ #ifndef PJ_STUN_STRING_ATTR_PAD_CHR # define PJ_STUN_STRING_ATTR_PAD_CHR 0 #endif /** * Enable pre-RFC3489bis-07 style of STUN MESSAGE-INTEGRITY and FINGERPRINT * calculation. By default this should be disabled since the calculation is * not backward compatible with current STUN specification. */ #ifndef PJ_STUN_OLD_STYLE_MI_FINGERPRINT # define PJ_STUN_OLD_STYLE_MI_FINGERPRINT 0 #endif /* ************************************************************************** * STUN TRANSPORT CONFIGURATION */ /** * The packet buffer size for the STUN transport. */ #ifndef PJ_STUN_SOCK_PKT_LEN # define PJ_STUN_SOCK_PKT_LEN 2000 #endif /** * The duration of the STUN keep-alive period, in seconds. */ #ifndef PJ_STUN_KEEP_ALIVE_SEC # define PJ_STUN_KEEP_ALIVE_SEC 15 #endif /* ************************************************************************** * TURN CONFIGURATION */ /** * Maximum DNS SRV entries to be processed in the DNS SRV response */ #ifndef PJ_TURN_MAX_DNS_SRV_CNT # define PJ_TURN_MAX_DNS_SRV_CNT 4 #endif /** * Maximum TURN packet size to be supported. */ #ifndef PJ_TURN_MAX_PKT_LEN # define PJ_TURN_MAX_PKT_LEN 3000 #endif /** * The TURN permission lifetime setting. This value should be taken from the * TURN protocol specification. */ #ifndef PJ_TURN_PERM_TIMEOUT # define PJ_TURN_PERM_TIMEOUT 300 #endif /** * The TURN channel binding lifetime. This value should be taken from the * TURN protocol specification. */ #ifndef PJ_TURN_CHANNEL_TIMEOUT # define PJ_TURN_CHANNEL_TIMEOUT 600 #endif /** * Number of seconds to refresh the permission/channel binding before the * permission/channel binding expires. This value should be greater than * PJ_TURN_PERM_TIMEOUT setting. */ #ifndef PJ_TURN_REFRESH_SEC_BEFORE # define PJ_TURN_REFRESH_SEC_BEFORE 60 #endif /** * The TURN session timer heart beat interval. When this timer occurs, the * TURN session will scan all the permissions/channel bindings to see which * need to be refreshed. */ #ifndef PJ_TURN_KEEP_ALIVE_SEC # define PJ_TURN_KEEP_ALIVE_SEC 15 #endif /* ************************************************************************** * ICE CONFIGURATION */ /** * Maximum number of ICE candidates. * * Default: 16 */ #ifndef PJ_ICE_MAX_CAND # define PJ_ICE_MAX_CAND 16 #endif /** * Maximum number of candidates for each ICE stream transport component. * * Default: 8 */ #ifndef PJ_ICE_ST_MAX_CAND # define PJ_ICE_ST_MAX_CAND 8 #endif /** * The number of bits to represent component IDs. This will affect * the maximum number of components (PJ_ICE_MAX_COMP) value. */ #ifndef PJ_ICE_COMP_BITS # define PJ_ICE_COMP_BITS 1 #endif /** * Maximum number of ICE components. */ #define PJ_ICE_MAX_COMP (2< * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_ERRNO_H__ #define __PJNATH_ERRNO_H__ /** * @file errno.h * @brief PJNATH specific error codes */ #include /** * @defgroup PJNATH_ERROR NAT Helper Library Error Codes * @brief PJNATH specific error code constants * @ingroup PJNATH_STUN_BASE * @{ */ /** * Start of error code relative to PJ_ERRNO_START_USER. * This value is 370000. */ #define PJNATH_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*4) /************************************************************ * STUN MESSAGING ERRORS ***********************************************************/ /** * Map STUN error code (300-699) into pj_status_t error space. */ #define PJ_STATUS_FROM_STUN_CODE(code) (PJNATH_ERRNO_START+code) /** * @hideinitializer * Invalid STUN message */ #define PJNATH_EINSTUNMSG (PJNATH_ERRNO_START+1) /* 370001 */ /** * @hideinitializer * Invalid STUN message length. */ #define PJNATH_EINSTUNMSGLEN (PJNATH_ERRNO_START+2) /* 370002 */ /** * @hideinitializer * Invalid or unexpected STUN message type */ #define PJNATH_EINSTUNMSGTYPE (PJNATH_ERRNO_START+3) /* 370003 */ /** * @hideinitializer * STUN transaction has timed out */ #define PJNATH_ESTUNTIMEDOUT (PJNATH_ERRNO_START+4) /* 370004 */ /** * @hideinitializer * Too many STUN attributes. */ #define PJNATH_ESTUNTOOMANYATTR (PJNATH_ERRNO_START+21) /* 370021 */ /** * @hideinitializer * Invalid STUN attribute length. */ #define PJNATH_ESTUNINATTRLEN (PJNATH_ERRNO_START+22) /* 370022 */ /** * @hideinitializer * Found duplicate STUN attribute. */ #define PJNATH_ESTUNDUPATTR (PJNATH_ERRNO_START+23) /* 370023 */ /** * @hideinitializer * STUN FINGERPRINT verification failed */ #define PJNATH_ESTUNFINGERPRINT (PJNATH_ERRNO_START+30) /* 370030 */ /** * @hideinitializer * Invalid STUN attribute after MESSAGE-INTEGRITY. */ #define PJNATH_ESTUNMSGINTPOS (PJNATH_ERRNO_START+31) /* 370031 */ /** * @hideinitializer * Invalid STUN attribute after FINGERPRINT. */ #define PJNATH_ESTUNFINGERPOS (PJNATH_ERRNO_START+33) /* 370033 */ /** * @hideinitializer * STUN (XOR-)MAPPED-ADDRESS attribute not found */ #define PJNATH_ESTUNNOMAPPEDADDR (PJNATH_ERRNO_START+40) /* 370040 */ /** * @hideinitializer * STUN IPv6 attribute not supported */ #define PJNATH_ESTUNIPV6NOTSUPP (PJNATH_ERRNO_START+41) /* 370041 */ /** * @hideinitializer * Invalid address family value in STUN message. */ #define PJNATH_EINVAF (PJNATH_ERRNO_START+42) /* 370042 */ /** * @hideinitializer * Invalid STUN server or server not configured. */ #define PJNATH_ESTUNINSERVER (PJNATH_ERRNO_START+50) /* 370050 */ /************************************************************ * STUN SESSION/TRANSPORT ERROR CODES ***********************************************************/ /** * @hideinitializer * STUN object has been destoyed. */ #define PJNATH_ESTUNDESTROYED (PJNATH_ERRNO_START+60) /* 370060 */ /************************************************************ * ICE ERROR CODES ***********************************************************/ /** * @hideinitializer * ICE session not available */ #define PJNATH_ENOICE (PJNATH_ERRNO_START+80) /* 370080 */ /** * @hideinitializer * ICE check is in progress */ #define PJNATH_EICEINPROGRESS (PJNATH_ERRNO_START+81) /* 370081 */ /** * @hideinitializer * This error indicates that ICE connectivity check has failed, because * there is at least one ICE component that does not have a valid check. * Normally this happens because the network topology had caused the * connectivity check to fail (e.g. no route between the two agents), * however other reasons may include software incompatibility between * the two agents, or incomplete candidates gathered by the agent(s). */ #define PJNATH_EICEFAILED (PJNATH_ERRNO_START+82) /* 370082 */ /** * @hideinitializer * Default destination does not match any ICE candidates */ #define PJNATH_EICEMISMATCH (PJNATH_ERRNO_START+83) /* 370083 */ /** * @hideinitializer * Invalid ICE component ID */ #define PJNATH_EICEINCOMPID (PJNATH_ERRNO_START+86) /* 370086 */ /** * @hideinitializer * Invalid ICE candidate ID */ #define PJNATH_EICEINCANDID (PJNATH_ERRNO_START+87) /* 370087 */ /** * @hideinitializer * Source address mismatch. This error occurs if the source address * of the response for ICE connectivity check is different than * the destination address of the request. */ #define PJNATH_EICEINSRCADDR (PJNATH_ERRNO_START+88) /* 370088 */ /** * @hideinitializer * Missing ICE SDP attribute */ #define PJNATH_EICEMISSINGSDP (PJNATH_ERRNO_START+90) /* 370090 */ /** * @hideinitializer * Invalid SDP "candidate" attribute */ #define PJNATH_EICEINCANDSDP (PJNATH_ERRNO_START+91) /* 370091 */ /** * @hideinitializer * No host candidate associated with srflx. This error occurs when * a server reflexive candidate is added without the matching * host candidate. */ #define PJNATH_EICENOHOSTCAND (PJNATH_ERRNO_START+92) /* 370092 */ /** * @hideinitializer * Controlled agent timed-out in waiting for the controlling agent to * send nominated check after all connectivity checks have completed. */ #define PJNATH_EICENOMTIMEOUT (PJNATH_ERRNO_START+93) /* 370093 */ /************************************************************ * TURN ERROR CODES ***********************************************************/ /** * @hideinitializer * Invalid or unsupported TURN transport. */ #define PJNATH_ETURNINTP (PJNATH_ERRNO_START+120) /* 370120 */ /** * @} */ #endif /* __PJNATH_ERRNO_H__ */ ================================================ FILE: deps/pjsip/pjnath/include/pjnath/ice_session.h ================================================ /* $Id: ice_session.h 4360 2013-02-21 11:26:35Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_ICE_SESSION_H__ #define __PJNATH_ICE_SESSION_H__ /** * @file ice_session.h * @brief ICE session management */ #include #include #include #include #include PJ_BEGIN_DECL /** * @addtogroup PJNATH_ICE_SESSION * @{ * * This module describes #pj_ice_sess, a transport independent ICE session, * part of PJNATH - the Open Source NAT helper library. * * \section pj_ice_sess_sec ICE Session * * An ICE session, represented by #pj_ice_sess structure, is the lowest * abstraction of ICE in PJNATH, and it is used to perform and manage * connectivity checks of transport address candidates within a * single media stream (note: this differs from what is described * in ICE draft, where an ICE session manages the whole media sessions * rather than just a single stream). * * The ICE session described here is independent from any transports, * meaning that the actual network I/O for this session would have to * be performed by the application, or higher layer abstraction. * Using this framework, application would give any incoming packets to * the ICE session, and it would provide the ICE session with a callback * to send outgoing message. * * For higher abstraction of ICE where transport is included, please * see \ref PJNATH_ICE_STREAM_TRANSPORT. * * \subsection pj_ice_sess_using_sec Using The ICE Session * * The steps below describe how to use ICE session. Alternatively application * can use the higher level ICE API, \ref PJNATH_ICE_STREAM_TRANSPORT, * which has provided the integration of ICE with socket transport. * * The steps to use ICE session is similar for both offerer and * answerer: * - create ICE session with #pj_ice_sess_create(). Among other things, * application needs to specify: * - STUN configuration (pj_stun_config), containing STUN settings * such as timeout values and the instances of timer heap and * ioqueue. * - Session name, useful for identifying this session in the log. * - Initial ICE role (#pj_ice_sess_role). The role can be changed * at later time with #pj_ice_sess_change_role(), and ICE session * can also change its role automatically when it detects role * conflict. * - Number of components in the media session. * - Callback to receive ICE events (#pj_ice_sess_cb) * - Optional local ICE username and password. If these arguments * are NULL, they will be generated randomly. * - Add local candidates for each component, with #pj_ice_sess_add_cand(). * A candidate is represented with #pj_ice_sess_cand structure. * Each component must be provided with at least one candidate, and * all components must have the same number of candidates. Failing * to comply with this will cause failure during pairing process. * - Create offer to describe local ICE candidates. ICE session does not * provide a function to create such offer, but application should be * able to create one since it knows about all components and candidates. * If application uses \ref PJNATH_ICE_STREAM_TRANSPORT, it can * enumerate local candidates by calling #pj_ice_strans_enum_cands(). * Application may use #pj_ice_sess_find_default_cand() to let ICE * session chooses the default transport address to be used in SDP * c= and m= lines. * - Send the offer to remote endpoint using signaling such as SIP. * - Once application has received the answer, it should parse this * answer, build array of remote candidates, and create check lists by * calling #pj_ice_sess_create_check_list(). This process is known as * pairing the candidates, and will result in the creation of check lists. * - Once checklist has been created, application then can call * #pj_ice_sess_start_check() to instruct ICE session to start * performing connectivity checks. The ICE session performs the * connectivity checks by processing each check in the checklists. * - Application will be notified about the result of ICE connectivity * checks via the callback that was given in #pj_ice_sess_create() * above. * * To send data, application calls #pj_ice_sess_send_data(). If ICE * negotiation has not completed, ICE session would simply drop the data, * and return error to caller. If ICE negotiation has completed * successfully, ICE session will in turn call the \a on_tx_pkt * callback of #pj_ice_sess_cb instance that was previously registered * in #pj_ice_sess_create() above. * * When application receives any packets on the underlying sockets, it * must call #pj_ice_sess_on_rx_pkt(). The ICE session will inspect the * packet to decide whether to process it locally (if the packet is a * STUN message and is part of ICE session) or otherwise pass it back to * application via \a on_rx_data callback. */ /** * Forward declaration for checklist. */ typedef struct pj_ice_sess_checklist pj_ice_sess_checklist; /** * This enumeration describes the type of an ICE candidate. */ typedef enum pj_ice_cand_type { /** * ICE host candidate. A host candidate represents the actual local * transport address in the host. */ PJ_ICE_CAND_TYPE_HOST, /** * ICE server reflexive candidate, which represents the public mapped * address of the local address, and is obtained by sending STUN * Binding request from the host candidate to a STUN server. */ PJ_ICE_CAND_TYPE_SRFLX, /** * ICE peer reflexive candidate, which is the address as seen by peer * agent during connectivity check. */ PJ_ICE_CAND_TYPE_PRFLX, /** * ICE relayed candidate, which represents the address allocated in * TURN server. */ PJ_ICE_CAND_TYPE_RELAYED, /** * Number of defined ICE candidate types. */ PJ_ICE_CAND_TYPE_MAX } pj_ice_cand_type; /** Forward declaration for pj_ice_sess */ typedef struct pj_ice_sess pj_ice_sess; /** Forward declaration for pj_ice_sess_check */ typedef struct pj_ice_sess_check pj_ice_sess_check; /** * This structure describes ICE component. * A media stream may require multiple components, each of which has * to work for the media stream as a whole to work. For media streams * based on RTP, there are two components per media stream - one for RTP, * and one for RTCP. */ typedef struct pj_ice_sess_comp { /** * Pointer to ICE check with highest priority which connectivity check * has been successful. The value will be NULL if a no successful check * has not been found for this component. */ pj_ice_sess_check *valid_check; /** * Pointer to ICE check with highest priority which connectivity check * has been successful and it has been nominated. The value may be NULL * if there is no such check yet. */ pj_ice_sess_check *nominated_check; /** * The STUN session to be used to send and receive STUN messages for this * component. */ pj_stun_session *stun_sess; } pj_ice_sess_comp; /** * Data structure to be attached to internal message processing. */ typedef struct pj_ice_msg_data { /** Transport ID for this message */ unsigned transport_id; /** Flag to indicate whether data.req contains data */ pj_bool_t has_req_data; /** The data */ union data { /** Request data */ struct request_data { pj_ice_sess *ice; /**< ICE session */ pj_ice_sess_checklist *clist; /**< Checklist */ unsigned ckid; /**< Check ID */ } req; } data; } pj_ice_msg_data; /** * This structure describes an ICE candidate. * ICE candidate is a transport address that is to be tested by ICE * procedures in order to determine its suitability for usage for * receipt of media. Candidates also have properties - their type * (server reflexive, relayed or host), priority, foundation, and * base. */ typedef struct pj_ice_sess_cand { /** * The candidate type, as described in #pj_ice_cand_type enumeration. */ pj_ice_cand_type type; /** * Status of this candidate. The value will be PJ_SUCCESS if candidate * address has been resolved successfully, PJ_EPENDING when the address * resolution process is in progress, or other value when the address * resolution has completed with failure. */ pj_status_t status; /** * The component ID of this candidate. Note that component IDs starts * with one for RTP and two for RTCP. In other words, it's not zero * based. */ pj_uint8_t comp_id; /** * Transport ID to be used to send packets for this candidate. */ pj_uint8_t transport_id; /** * Local preference value, which typically is 65535. */ pj_uint16_t local_pref; /** * The foundation string, which is an identifier which value will be * equivalent for two candidates that are of the same type, share the * same base, and come from the same STUN server. The foundation is * used to optimize ICE performance in the Frozen algorithm. */ pj_str_t foundation; /** * The candidate's priority, a 32-bit unsigned value which value will be * calculated by the ICE session when a candidate is registered to the * ICE session. */ pj_uint32_t prio; /** * IP address of this candidate. For host candidates, this represents * the local address of the socket. For reflexive candidates, the value * will be the public address allocated in NAT router for the host * candidate and as reported in MAPPED-ADDRESS or XOR-MAPPED-ADDRESS * attribute of STUN Binding request. For relayed candidate, the value * will be the address allocated in the TURN server by STUN Allocate * request. */ pj_sockaddr addr; /** * Base address of this candidate. "Base" refers to the address an agent * sends from for a particular candidate. For host candidates, the base * is the same as the host candidate itself. For reflexive candidates, * the base is the local IP address of the socket. For relayed candidates, * the base address is the transport address allocated in the TURN server * for this candidate. */ pj_sockaddr base_addr; /** * Related address, which is used for informational only and is not used * in any way by the ICE session. */ pj_sockaddr rel_addr; } pj_ice_sess_cand; /** * This enumeration describes the state of ICE check. */ typedef enum pj_ice_sess_check_state { /** * A check for this pair hasn't been performed, and it can't * yet be performed until some other check succeeds, allowing this * pair to unfreeze and move into the Waiting state. */ PJ_ICE_SESS_CHECK_STATE_FROZEN, /** * A check has not been performed for this pair, and can be * performed as soon as it is the highest priority Waiting pair on * the check list. */ PJ_ICE_SESS_CHECK_STATE_WAITING, /** * A check has not been performed for this pair, and can be * performed as soon as it is the highest priority Waiting pair on * the check list. */ PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, /** * A check has not been performed for this pair, and can be * performed as soon as it is the highest priority Waiting pair on * the check list. */ PJ_ICE_SESS_CHECK_STATE_SUCCEEDED, /** * A check for this pair was already done and failed, either * never producing any response or producing an unrecoverable failure * response. */ PJ_ICE_SESS_CHECK_STATE_FAILED } pj_ice_sess_check_state; /** * This structure describes an ICE connectivity check. An ICE check * contains a candidate pair, and will involve sending STUN Binding * Request transaction for the purposes of verifying connectivity. * A check is sent from the local candidate to the remote candidate * of a candidate pair. */ struct pj_ice_sess_check { /** * Pointer to local candidate entry of this check. */ pj_ice_sess_cand *lcand; /** * Pointer to remote candidate entry of this check. */ pj_ice_sess_cand *rcand; /** * Check priority. */ pj_timestamp prio; /** * Connectivity check state. */ pj_ice_sess_check_state state; /** * STUN transmit data containing STUN Binding request that was sent * as part of this check. The value will only be set when this check * has a pending transaction, and is used to cancel the transaction * when other check has succeeded. */ pj_stun_tx_data *tdata; /** * Flag to indicate whether this check is nominated. A nominated check * contains USE-CANDIDATE attribute in its STUN Binding request. */ pj_bool_t nominated; /** * When the check failed, this will contain the failure status of the * STUN transaction. */ pj_status_t err_code; }; /** * This enumeration describes ICE checklist state. */ typedef enum pj_ice_sess_checklist_state { /** * The checklist is not yet running. */ PJ_ICE_SESS_CHECKLIST_ST_IDLE, /** * In this state, ICE checks are still in progress for this * media stream. */ PJ_ICE_SESS_CHECKLIST_ST_RUNNING, /** * In this state, ICE checks have completed for this media stream, * either successfully or with failure. */ PJ_ICE_SESS_CHECKLIST_ST_COMPLETED } pj_ice_sess_checklist_state; /** * This structure represents ICE check list, that is an ordered set of * candidate pairs that an agent will use to generate checks. */ struct pj_ice_sess_checklist { /** * The checklist state. */ pj_ice_sess_checklist_state state; /** * Number of candidate pairs (checks). */ unsigned count; /** * Array of candidate pairs (checks). */ pj_ice_sess_check checks[PJ_ICE_MAX_CHECKS]; /** * A timer used to perform periodic check for this checklist. */ pj_timer_entry timer; }; /** * This structure contains callbacks that will be called by the ICE * session. */ typedef struct pj_ice_sess_cb { /** * An optional callback that will be called by the ICE session when * ICE negotiation has completed, successfully or with failure. * * @param ice The ICE session. * @param status Will contain PJ_SUCCESS if ICE negotiation is * successful, or some error code. */ void (*on_ice_complete)(pj_ice_sess *ice, pj_status_t status); /** * A mandatory callback which will be called by the ICE session when * it needs to send outgoing STUN packet. * * @param ice The ICE session. * @param comp_id ICE component ID. * @param transport_id Transport ID. * @param pkt The STUN packet. * @param size The size of the packet. * @param dst_addr Packet destination address. * @param dst_addr_len Length of destination address. */ pj_status_t (*on_tx_pkt)(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len); /** * A mandatory callback which will be called by the ICE session when * it receives packet which is not part of ICE negotiation. * * @param ice The ICE session. * @param comp_id ICE component ID. * @param transport_id Transport ID. * @param pkt The whole packet. * @param size Size of the packet. * @param src_addr Source address where this packet was received * from. * @param src_addr_len The length of source address. */ void (*on_rx_data)(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len); } pj_ice_sess_cb; /** * This enumeration describes the role of the ICE agent. */ typedef enum pj_ice_sess_role { /** * The role is unknown. */ PJ_ICE_SESS_ROLE_UNKNOWN, /** * The ICE agent is in controlled role. */ PJ_ICE_SESS_ROLE_CONTROLLED, /** * The ICE agent is in controlling role. */ PJ_ICE_SESS_ROLE_CONTROLLING } pj_ice_sess_role; /** * This structure represents an incoming check (an incoming Binding * request message), and is mainly used to keep early checks in the * list in the ICE session. An early check is a request received * from remote when we haven't received SDP answer yet, therefore we * can't perform triggered check. For such cases, keep the incoming * request in a list, and we'll do triggered checks (simultaneously) * as soon as we receive answer. */ typedef struct pj_ice_rx_check { PJ_DECL_LIST_MEMBER(struct pj_ice_rx_check); /**< Standard list */ unsigned comp_id; /**< Component ID. */ unsigned transport_id; /**< Transport ID. */ pj_sockaddr src_addr; /**< Source address of request */ unsigned src_addr_len; /**< Length of src address. */ pj_bool_t use_candidate; /**< USE-CANDIDATE is present? */ pj_uint32_t priority; /**< PRIORITY value in the req. */ pj_stun_uint64_attr *role_attr; /**< ICE-CONTROLLING/CONTROLLED */ } pj_ice_rx_check; /** * This structure describes various ICE session options. Application * configure the ICE session with these options by calling * #pj_ice_sess_set_options(). */ typedef struct pj_ice_sess_options { /** * Specify whether to use aggressive nomination. */ pj_bool_t aggressive; /** * For controlling agent if it uses regular nomination, specify the delay * to perform nominated check (connectivity check with USE-CANDIDATE * attribute) after all components have a valid pair. * * Default value is PJ_ICE_NOMINATED_CHECK_DELAY. */ unsigned nominated_check_delay; /** * For a controlled agent, specify how long it wants to wait (in * milliseconds) for the controlling agent to complete sending * connectivity check with nominated flag set to true for all components * after the controlled agent has found that all connectivity checks in * its checklist have been completed and there is at least one successful * (but not nominated) check for every component. * * Default value for this option is * ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT. Specify -1 to disable * this timer. */ int controlled_agent_want_nom_timeout; } pj_ice_sess_options; /** * This structure describes the ICE session. For this version of PJNATH, * an ICE session corresponds to a single media stream (unlike the ICE * session described in the ICE standard where an ICE session covers the * whole media and may consist of multiple media streams). The decision * to support only a single media session was chosen for simplicity, * while still allowing application to utilize multiple media streams by * creating multiple ICE sessions, one for each media stream. */ struct pj_ice_sess { char obj_name[PJ_MAX_OBJ_NAME]; /**< Object name. */ pj_pool_t *pool; /**< Pool instance. */ void *user_data; /**< App. data. */ pj_grp_lock_t *grp_lock; /**< Group lock */ pj_ice_sess_role role; /**< ICE role. */ pj_ice_sess_options opt; /**< Options */ pj_timestamp tie_breaker; /**< Tie breaker value */ pj_uint8_t *prefs; /**< Type preference. */ pj_bool_t is_nominating; /**< Nominating stage */ pj_bool_t is_complete; /**< Complete? */ pj_bool_t is_destroying; /**< Destroy is called */ pj_status_t ice_status; /**< Error status. */ pj_timer_entry timer; /**< ICE timer. */ pj_ice_sess_cb cb; /**< Callback. */ pj_stun_config stun_cfg; /**< STUN settings. */ /* STUN credentials */ pj_str_t tx_ufrag; /**< Remote ufrag. */ pj_str_t tx_uname; /**< Uname for TX. */ pj_str_t tx_pass; /**< Remote password. */ pj_str_t rx_ufrag; /**< Local ufrag. */ pj_str_t rx_uname; /**< Uname for RX */ pj_str_t rx_pass; /**< Local password. */ /* Components */ unsigned comp_cnt; /**< # of components. */ pj_ice_sess_comp comp[PJ_ICE_MAX_COMP]; /**< Component array */ unsigned comp_ka; /**< Next comp for KA */ /* Local candidates */ unsigned lcand_cnt; /**< # of local cand. */ pj_ice_sess_cand lcand[PJ_ICE_MAX_CAND]; /**< Array of cand. */ /* Remote candidates */ unsigned rcand_cnt; /**< # of remote cand. */ pj_ice_sess_cand rcand[PJ_ICE_MAX_CAND]; /**< Array of cand. */ /** Array of transport datas */ pj_ice_msg_data tp_data[4]; /* List of eearly checks */ pj_ice_rx_check early_check; /**< Early checks. */ /* Checklist */ pj_ice_sess_checklist clist; /**< Active checklist */ /* Valid list */ pj_ice_sess_checklist valid_list; /**< Valid list. */ /** Temporary buffer for misc stuffs to avoid using stack too much */ union { char txt[128]; char errmsg[PJ_ERR_MSG_SIZE]; } tmp; }; /** * This is a utility function to retrieve the string name for the * particular candidate type. * * @param type Candidate type. * * @return The string representation of the candidate type. */ PJ_DECL(const char*) pj_ice_get_cand_type_name(pj_ice_cand_type type); /** * This is a utility function to retrieve the string name for the * particular role type. * * @param role Role type. * * @return The string representation of the role. */ PJ_DECL(const char*) pj_ice_sess_role_name(pj_ice_sess_role role); /** * This is a utility function to calculate the foundation identification * for a candidate. * * @param pool Pool to allocate the foundation string. * @param foundation Pointer to receive the foundation string. * @param type Candidate type. * @param base_addr Base address of the candidate. */ PJ_DECL(void) pj_ice_calc_foundation(pj_pool_t *pool, pj_str_t *foundation, pj_ice_cand_type type, const pj_sockaddr *base_addr); /** * Initialize ICE session options with library default values. * * @param opt ICE session options. */ PJ_DECL(void) pj_ice_sess_options_default(pj_ice_sess_options *opt); /** * Create ICE session with the specified role and number of components. * Application would typically need to create an ICE session before * sending an offer or upon receiving one. After the session is created, * application can register candidates to the ICE session by calling * #pj_ice_sess_add_cand() function. * * @param stun_cfg The STUN configuration settings, containing among * other things the timer heap instance to be used * by the ICE session. * @param name Optional name to identify this ICE instance in * the log file. * @param role ICE role. * @param comp_cnt Number of components. * @param cb ICE callback. * @param local_ufrag Optional string to be used as local username to * authenticate incoming STUN binding request. If * the value is NULL, a random string will be * generated. * @param local_passwd Optional string to be used as local password. * @param grp_lock Optional group lock to be used by this session. * If NULL, the session will create one itself. * @param p_ice Pointer to receive the ICE session instance. * * @return PJ_SUCCESS if ICE session is created successfully. */ PJ_DECL(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, const char *name, pj_ice_sess_role role, unsigned comp_cnt, const pj_ice_sess_cb *cb, const pj_str_t *local_ufrag, const pj_str_t *local_passwd, pj_grp_lock_t *grp_lock, pj_ice_sess **p_ice); /** * Get the value of various options of the ICE session. * * @param ice The ICE session. * @param opt The options to be initialized with the values * from the ICE session. * * @return PJ_SUCCESS on success, or the appropriate error. */ PJ_DECL(pj_status_t) pj_ice_sess_get_options(pj_ice_sess *ice, pj_ice_sess_options *opt); /** * Specify various options for this ICE session. Application MUST only * call this function after the ICE session has been created but before * any connectivity check is started. * * Application should call #pj_ice_sess_get_options() to initialize the * options with their default values. * * @param ice The ICE session. * @param opt Options to be applied to the ICE session. * * @return PJ_SUCCESS on success, or the appropriate error. */ PJ_DECL(pj_status_t) pj_ice_sess_set_options(pj_ice_sess *ice, const pj_ice_sess_options *opt); /** * Destroy ICE session. This will cancel any connectivity checks currently * running, if any, and any other events scheduled by this session, as well * as all memory resources. * * @param ice ICE session instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pj_ice_sess_destroy(pj_ice_sess *ice); /** * Change session role. This happens for example when ICE session was * created with controlled role when receiving an offer, but it turns out * that the offer contains "a=ice-lite" attribute when the SDP gets * inspected. * * @param ice The ICE session. * @param new_role The new role to be set. * * @return PJ_SUCCESS on success, or the appropriate error. */ PJ_DECL(pj_status_t) pj_ice_sess_change_role(pj_ice_sess *ice, pj_ice_sess_role new_role); /** * Assign a custom preference values for ICE candidate types. By assigning * custom preference value, application can control the order of candidates * to be checked first. The default preference settings is to use 126 for * host candidates, 100 for server reflexive candidates, 110 for peer * reflexive candidates, an 0 for relayed candidates. * * Note that this function must be called before any candidates are added * to the ICE session. * * @param ice The ICE session. * @param prefs Array of candidate preference value. The values are * put in the array indexed by the candidate type as * specified in pj_ice_cand_type. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice, const pj_uint8_t prefs[4]); /** * Add a candidate to this ICE session. Application must add candidates for * each components ID before it can start pairing the candidates and * performing connectivity checks. * * @param ice ICE session instance. * @param comp_id Component ID of this candidate. * @param transport_id Transport ID to be used to send packets for this * candidate. * @param type Candidate type. * @param local_pref Local preference for this candidate, which * normally should be set to 65535. * @param foundation Foundation identification. * @param addr The candidate address. * @param base_addr The candidate's base address. * @param rel_addr Optional related address. * @param addr_len Length of addresses. * @param p_cand_id Optional pointer to receive the candidate ID. * * @return PJ_SUCCESS if candidate is successfully added. */ PJ_DECL(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, pj_ice_cand_type type, pj_uint16_t local_pref, const pj_str_t *foundation, const pj_sockaddr_t *addr, const pj_sockaddr_t *base_addr, const pj_sockaddr_t *rel_addr, int addr_len, unsigned *p_cand_id); /** * Find default candidate for the specified component ID, using this * rule: * - if the component has a successful candidate pair, then the * local candidate of this pair will be returned. * - otherwise a relay, reflexive, or host candidate will be selected * on that specified order. * * @param ice The ICE session instance. * @param comp_id The component ID. * @param p_cand_id Pointer to receive the candidate ID. * * @return PJ_SUCCESS if a candidate has been selected. */ PJ_DECL(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, unsigned comp_id, int *p_cand_id); /** * Pair the local and remote candidates to create check list. Application * typically would call this function after receiving SDP containing ICE * candidates from the remote host (either upon receiving the initial * offer, for UAS, or upon receiving the answer, for UAC). * * Note that ICE connectivity check will not start until application calls * #pj_ice_sess_start_check(). * * @param ice ICE session instance. * @param rem_ufrag Remote ufrag, as seen in the SDP received from * the remote agent. * @param rem_passwd Remote password, as seen in the SDP received from * the remote agent. * @param rem_cand_cnt Number of remote candidates. * @param rem_cand Remote candidate array. Remote candidates are * gathered from the SDP received from the remote * agent. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_ice_sess_create_check_list(pj_ice_sess *ice, const pj_str_t *rem_ufrag, const pj_str_t *rem_passwd, unsigned rem_cand_cnt, const pj_ice_sess_cand rem_cand[]); /** * Start ICE periodic check. This function will return immediately, and * application will be notified about the connectivity check status in * #pj_ice_sess_cb callback. * * @param ice The ICE session instance. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice); /** * Send data using this ICE session. If ICE checks have not produced a * valid check for the specified component ID, this function will return * with failure. Otherwise ICE session will send the packet to remote * destination using the nominated local candidate for the specified * component. * * This function will in turn call \a on_tx_pkt function in * #pj_ice_sess_cb callback to actually send the packet to the wire. * * @param ice The ICE session. * @param comp_id Component ID. * @param data The data or packet to be sent. * @param data_len Size of data or packet, in bytes. * * @return PJ_SUCCESS if data is sent successfully. */ PJ_DECL(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice, unsigned comp_id, const void *data, pj_size_t data_len); /** * Report the arrival of packet to the ICE session. Since ICE session * itself doesn't have any transports, it relies on application or * higher layer component to give incoming packets to the ICE session. * If the packet is not a STUN packet, this packet will be given back * to application via \a on_rx_data() callback in #pj_ice_sess_cb. * * @param ice The ICE session. * @param comp_id Component ID. * @param transport_id Number to identify where this packet was received * from. This parameter will be returned back to * application in \a on_tx_pkt() callback. * @param pkt Incoming packet. * @param pkt_size Size of incoming packet. * @param src_addr Source address of the packet. * @param src_addr_len Length of the address. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *src_addr, int src_addr_len); /** * @} */ PJ_END_DECL #endif /* __PJNATH_ICE_SESSION_H__ */ ================================================ FILE: deps/pjsip/pjnath/include/pjnath/ice_strans.h ================================================ /* $Id: ice_strans.h 4133 2012-05-21 14:00:17Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_ICE_STRANS_H__ #define __PJNATH_ICE_STRANS_H__ /** * @file ice_strans.h * @brief ICE Stream Transport */ #include #include #include #include #include #include PJ_BEGIN_DECL /** * @addtogroup PJNATH_ICE_STREAM_TRANSPORT * @{ * * This module describes ICE stream transport, as represented by #pj_ice_strans * structure, and is part of PJNATH - the Open Source NAT traversal helper * library. * * ICE stream transport, as represented by #pj_ice_strans structure, is an ICE * capable class for transporting media streams within a media session. * It consists of one or more transport sockets (typically two for RTP * based communication - one for RTP and one for RTCP), and an * \ref PJNATH_ICE_SESSION for performing connectivity checks among the. * various candidates of the transport addresses. * * * \section ice_strans_using_sec Using the ICE stream transport * * The steps below describe how to use ICE session: * * - initialize a #pj_ice_strans_cfg structure. This contains various * settings for the ICE stream transport, and among other things contains * the STUN and TURN settings.\n\n * - create the instance with #pj_ice_strans_create(). Among other things, * the function needs the following arguments: * - the #pj_ice_strans_cfg structure for the main configurations * - number of components to be supported * - instance of #pj_ice_strans_cb structure to report callbacks to * application.\n\n * - while the #pj_ice_strans_create() call completes immediately, the * initialization will be running in the background to gather the * candidates (for example STUN and TURN candidates, if they are enabled * in the #pj_ice_strans_cfg setting). Application will be notified when * the initialization completes in the \a on_ice_complete callback of * the #pj_ice_strans_cb structure (the \a op argument of this callback * will be PJ_ICE_STRANS_OP_INIT).\n\n * - when media stream is to be started (for example, a call is to be * started), create an ICE session by calling #pj_ice_strans_init_ice().\n\n * - the application now typically will need to communicate local ICE * information to remote host. It can achieve this by using the following * functions to query local ICE information: * - #pj_ice_strans_get_ufrag_pwd() * - #pj_ice_strans_enum_cands() * - #pj_ice_strans_get_def_cand()\n * The application may need to encode the above information as SDP.\n\n * - when the application receives remote ICE information (for example, from * the SDP received from remote), it can now start ICE negotiation, by * calling #pj_ice_strans_start_ice(). This function requires some * information about remote ICE agent such as remote ICE username fragment * and password as well as array of remote candidates.\n\n * - note that the PJNATH library does not work with SDP; application would * need to encode and parse the SDP itself.\n\n * - once ICE negotiation has been started, application will be notified * about the completion in the \a on_ice_complete() callback of the * #pj_ice_strans_cb.\n\n * - at any time, application may send or receive data. However the ICE * stream transport may not be able to send it depending on its current * state. Before ICE negotiation is started, the data will be sent using * default candidate of the component. After negotiation is completed, * data will be sent using the candidate from the successful/nominated * pair. The ICE stream transport may not be able to send data while * negotiation is in progress.\n\n * - application sends data by using #pj_ice_strans_sendto(). Incoming * data will be reported in \a on_rx_data() callback of the * #pj_ice_strans_cb.\n\n * - once the media session has finished (e.g. user hangs up the call), * destroy the ICE session with #pj_ice_strans_stop_ice().\n\n * - at this point, application may destroy the ICE stream transport itself, * or let it run so that it can be reused to create other ICE session. * The benefit of letting the ICE stream transport alive (without any * session active) is to avoid delay with the initialization, howerver * keeping the transport alive means the transport needs to keep the * STUN binding open by using keep-alive and also TURN allocation alive, * and this will consume power which is an important issue for mobile * applications.\n\n */ /** Forward declaration for ICE stream transport. */ typedef struct pj_ice_strans pj_ice_strans; /** Transport operation types to be reported on \a on_status() callback */ typedef enum pj_ice_strans_op { /** Initialization (candidate gathering) */ PJ_ICE_STRANS_OP_INIT, /** Negotiation */ PJ_ICE_STRANS_OP_NEGOTIATION, /** This operatino is used to report failure in keep-alive operation. * Currently it is only used to report TURN Refresh failure. */ PJ_ICE_STRANS_OP_KEEP_ALIVE } pj_ice_strans_op; /** * ICE stream transport's state. */ typedef enum pj_ice_strans_state { /** * ICE stream transport is not created. */ PJ_ICE_STRANS_STATE_NULL, /** * ICE candidate gathering process is in progress. */ PJ_ICE_STRANS_STATE_INIT, /** * ICE stream transport initialization/candidate gathering process is * complete, ICE session may be created on this stream transport. */ PJ_ICE_STRANS_STATE_READY, /** * New session has been created and the session is ready. */ PJ_ICE_STRANS_STATE_SESS_READY, /** * ICE negotiation is in progress. */ PJ_ICE_STRANS_STATE_NEGO, /** * ICE negotiation has completed successfully and media is ready * to be used. */ PJ_ICE_STRANS_STATE_RUNNING, /** * ICE negotiation has completed with failure. */ PJ_ICE_STRANS_STATE_FAILED } pj_ice_strans_state; /** * This structure contains callbacks that will be called by the * ICE stream transport. */ typedef struct pj_ice_strans_cb { /** * This callback will be called when the ICE transport receives * incoming packet from the sockets which is not related to ICE * (for example, normal RTP/RTCP packet destined for application). * * @param ice_st The ICE stream transport. * @param comp_id The component ID. * @param pkt The packet. * @param size Size of the packet. * @param src_addr Source address of the packet. * @param src_addr_len Length of the source address. */ void (*on_rx_data)(pj_ice_strans *ice_st, unsigned comp_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len); /** * Callback to report status of various ICE operations. * * @param ice_st The ICE stream transport. * @param op The operation which status is being reported. * @param status Operation status. */ void (*on_ice_complete)(pj_ice_strans *ice_st, pj_ice_strans_op op, pj_status_t status); /** * Callback to report ICE state changes. * * @param ice_st The ICE stream transport. * @param prev Previous state. * @param curr Current state. */ void (*on_ice_state)(pj_ice_strans *ice_st, pj_ice_strans_state prev, pj_ice_strans_state curr); } pj_ice_strans_cb; /** * This structure describes ICE stream transport configuration. Application * should initialize the structure by calling #pj_ice_strans_cfg_default() * before changing the settings. */ typedef struct pj_ice_strans_cfg { /** * Address family, IPv4 or IPv6. Currently only pj_AF_INET() (IPv4) * is supported, and this is the default value. */ int af; /** * STUN configuration which contains the timer heap and * ioqueue instance to be used, and STUN retransmission * settings. This setting is mandatory. * * The default value is all zero. Application must initialize * this setting with #pj_stun_config_init(). */ pj_stun_config stun_cfg; /** * DNS resolver to be used to resolve servers. If DNS SRV * resolution is required, the resolver must be set. * * The default value is NULL. */ pj_dns_resolver *resolver; /** * This contains various STUN session options. Once the ICE stream * transport is created, application may also change the options * with #pj_ice_strans_set_options(). */ pj_ice_sess_options opt; /** * STUN and local transport settings. This specifies the * settings for local UDP socket, which will be resolved * to get the STUN mapped address. */ struct { /** * Optional configuration for STUN transport. The default * value will be initialized with #pj_stun_sock_cfg_default(). */ pj_stun_sock_cfg cfg; /** * Maximum number of host candidates to be added. If the * value is zero, no host candidates will be added. * * Default: 64 */ unsigned max_host_cands; /** * Include loopback addresses in the host candidates. * * Default: PJ_FALSE */ pj_bool_t loop_addr; /** * Specify the STUN server domain or hostname or IP address. * If DNS SRV resolution is required, application must fill * in this setting with the domain name of the STUN server * and set the resolver instance in the \a resolver field. * Otherwise if the \a resolver setting is not set, this * field will be resolved with hostname resolution and in * this case the \a port field must be set. * * The \a port field should also be set even when DNS SRV * resolution is used, in case the DNS SRV resolution fails. * * When this field is empty, STUN mapped address resolution * will not be performed. In this case only ICE host candidates * will be added to the ICE transport, unless if \a no_host_cands * field is set. In this case, both host and srflx candidates * are disabled. * * The default value is empty. */ pj_str_t server; /** * The port number of the STUN server, when \a server * field specifies a hostname rather than domain name. This * field should also be set even when the \a server * specifies a domain name, to allow DNS SRV resolution * to fallback to DNS A/AAAA resolution when the DNS SRV * resolution fails. * * The default value is PJ_STUN_PORT. */ pj_uint16_t port; /** * Ignore STUN resolution error and proceed with just local * addresses. * * The default is PJ_FALSE */ pj_bool_t ignore_stun_error; } stun; /** * TURN specific settings. */ struct { /** * Optional TURN socket settings. The default values will be * initialized by #pj_turn_sock_cfg_default(). This contains * settings such as QoS. */ pj_turn_sock_cfg cfg; /** * Specify the TURN server domain or hostname or IP address. * If DNS SRV resolution is required, application must fill * in this setting with the domain name of the TURN server * and set the resolver instance in the \a resolver field. * Otherwise if the \a resolver setting is not set, this * field will be resolved with hostname resolution and in * this case the \a port field must be set. * * The \a port field should also be set even when DNS SRV * resolution is used, in case the DNS SRV resolution fails. * * When this field is empty, relay candidate will not be * created. * * The default value is empty. */ pj_str_t server; /** * The port number of the TURN server, when \a server * field specifies a hostname rather than domain name. This * field should also be set even when the \a server * specifies a domain name, to allow DNS SRV resolution * to fallback to DNS A/AAAA resolution when the DNS SRV * resolution fails. * * Default is zero. */ pj_uint16_t port; /** * Type of connection to the TURN server. * * Default is PJ_TURN_TP_UDP. */ pj_turn_tp_type conn_type; /** * Credential to be used for the TURN session. This setting * is mandatory. * * Default is to have no credential. */ pj_stun_auth_cred auth_cred; /** * Optional TURN Allocate parameter. The default value will be * initialized by #pj_turn_alloc_param_default(). */ pj_turn_alloc_param alloc_param; } turn; /** * Component specific settings, which will override the settings in * the STUN and TURN settings above. For example, setting the QoS * parameters here allows the application to have different QoS * traffic type for RTP and RTCP component. */ struct { /** * QoS traffic type to be set on this transport. When application * wants to apply QoS tagging to the transport, it's preferable to * set this field rather than \a qos_param fields since this is * more portable. * * Default value is PJ_QOS_TYPE_BEST_EFFORT. */ pj_qos_type qos_type; /** * Set the low level QoS parameters to the transport. This is a * lower level operation than setting the \a qos_type field and * may not be supported on all platforms. * * By default all settings in this structure are disabled. */ pj_qos_params qos_params; /** * Specify target value for socket receive buffer size. It will be * applied using setsockopt(). When it fails to set the specified * size, it will try with lower value until the highest possible is * successfully set. * * When this is set to zero, this component will apply socket receive * buffer size settings specified in STUN and TURN socket config * above, i.e: \a stun::cfg::so_rcvbuf_size and * \a turn::cfg::so_rcvbuf_size. Otherwise, this setting will be * applied to STUN and TURN sockets for this component, overriding * the setting specified in STUN/TURN socket config. * * Default: 0 */ unsigned so_rcvbuf_size; /** * Specify target value for socket send buffer size. It will be * applied using setsockopt(). When it fails to set the specified * size, it will try with lower value until the highest possible is * successfully set. * * When this is set to zero, this component will apply socket send * buffer size settings specified in STUN and TURN socket config * above, i.e: \a stun::cfg::so_sndbuf_size and * \a turn::cfg::so_sndbuf_size. Otherwise, this setting will be * applied to STUN and TURN sockets for this component, overriding * the setting specified in STUN/TURN socket config. * * Default: 0 */ unsigned so_sndbuf_size; } comp[PJ_ICE_MAX_COMP]; } pj_ice_strans_cfg; /** * Initialize ICE transport configuration with default values. * * @param cfg The configuration to be initialized. */ PJ_DECL(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg); /** * Copy configuration. * * @param pool Pool. * @param dst Destination. * @param src Source. */ PJ_DECL(void) pj_ice_strans_cfg_copy(pj_pool_t *pool, pj_ice_strans_cfg *dst, const pj_ice_strans_cfg *src); /** * Create and initialize the ICE stream transport with the specified * parameters. * * @param name Optional name for logging identification. * @param cfg Configuration. * @param comp_cnt Number of components. * @param user_data Arbitrary user data to be associated with this * ICE stream transport. * @param cb Callback. * @param p_ice_st Pointer to receive the ICE stream transport * instance. * * @return PJ_SUCCESS if ICE stream transport is created * successfully. */ PJ_DECL(pj_status_t) pj_ice_strans_create(const char *name, const pj_ice_strans_cfg *cfg, unsigned comp_cnt, void *user_data, const pj_ice_strans_cb *cb, pj_ice_strans **p_ice_st); /** * Get ICE session state. * * @param ice_st The ICE stream transport. * * @return ICE session state. */ PJ_DECL(pj_ice_strans_state) pj_ice_strans_get_state(pj_ice_strans *ice_st); /** * Get string representation of ICE state. * * @param state ICE stream transport state. * * @return String. */ PJ_DECL(const char*) pj_ice_strans_state_name(pj_ice_strans_state state); /** * Destroy the ICE stream transport. This will destroy the ICE session * inside the ICE stream transport, close all sockets and release all * other resources. * * @param ice_st The ICE stream transport. * * @return PJ_SUCCESS, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_ice_strans_destroy(pj_ice_strans *ice_st); /** * Get the user data associated with the ICE stream transport. * * @param ice_st The ICE stream transport. * * @return The user data. */ PJ_DECL(void*) pj_ice_strans_get_user_data(pj_ice_strans *ice_st); /** * Get the value of various options of the ICE stream transport. * * @param ice_st The ICE stream transport. * @param opt The options to be initialized with the values * from the ICE stream transport. * * @return PJ_SUCCESS on success, or the appropriate error. */ PJ_DECL(pj_status_t) pj_ice_strans_get_options(pj_ice_strans *ice_st, pj_ice_sess_options *opt); /** * Specify various options for this ICE stream transport. Application * should call #pj_ice_strans_get_options() to initialize the options * with their default values. * * @param ice_st The ICE stream transport. * @param opt Options to be applied to this ICE stream transport. * * @return PJ_SUCCESS on success, or the appropriate error. */ PJ_DECL(pj_status_t) pj_ice_strans_set_options(pj_ice_strans *ice_st, const pj_ice_sess_options *opt); /** * Get the group lock for this ICE stream transport. * * @param ice_st The ICE stream transport. * * @return The group lock. */ PJ_DECL(pj_grp_lock_t *) pj_ice_strans_get_grp_lock(pj_ice_strans *ice_st); /** * Initialize the ICE session in the ICE stream transport. * When application is about to send an offer containing ICE capability, * or when it receives an offer containing ICE capability, it must * call this function to initialize the internal ICE session. This would * register all transport address aliases for each component to the ICE * session as candidates. Then application can enumerate all local * candidates by calling #pj_ice_strans_enum_cands(), and encode these * candidates in the SDP to be sent to remote agent. * * @param ice_st The ICE stream transport. * @param role ICE role. * @param local_ufrag Optional local username fragment. * @param local_passwd Optional local password. * * @return PJ_SUCCESS, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, pj_ice_sess_role role, const pj_str_t *local_ufrag, const pj_str_t *local_passwd); /** * Check if the ICE stream transport has the ICE session created. The * ICE session is created with #pj_ice_strans_init_ice(). * * @param ice_st The ICE stream transport. * * @return PJ_TRUE if #pj_ice_strans_init_ice() has been * called. */ PJ_DECL(pj_bool_t) pj_ice_strans_has_sess(pj_ice_strans *ice_st); /** * Check if ICE negotiation is still running. * * @param ice_st The ICE stream transport. * * @return PJ_TRUE if ICE session has been created and ICE * negotiation negotiation is in progress. */ PJ_DECL(pj_bool_t) pj_ice_strans_sess_is_running(pj_ice_strans *ice_st); /** * Check if ICE negotiation has completed. * * @param ice_st The ICE stream transport. * * @return PJ_TRUE if ICE session has been created and the * negotiation is complete. */ PJ_DECL(pj_bool_t) pj_ice_strans_sess_is_complete(pj_ice_strans *ice_st); /** * Get the current/running component count. If ICE negotiation has not * been started, the number of components will be equal to the number * when the ICE stream transport was created. Once negotiation been * started, the number of components will be the lowest number of * component between local and remote agents. * * @param ice_st The ICE stream transport. * * @return The running number of components. */ PJ_DECL(unsigned) pj_ice_strans_get_running_comp_cnt(pj_ice_strans *ice_st); /** * Get the ICE username fragment and password of the ICE session. The * local username fragment and password can only be retrieved once ICE * session has been created with #pj_ice_strans_init_ice(). The remote * username fragment and password can only be retrieved once ICE session * has been started with #pj_ice_strans_start_ice(). * * Note that the string returned by this function is only valid throughout * the duration of the ICE session, and the application must not modify * these strings. Once the ICE session has been stopped with * #pj_ice_strans_stop_ice(), the pointer in the string will no longer be * valid. * * @param ice_st The ICE stream transport. * @param loc_ufrag Optional pointer to receive ICE username fragment * of local endpoint from the ICE session. * @param loc_pwd Optional pointer to receive ICE password of local * endpoint from the ICE session. * @param rem_ufrag Optional pointer to receive ICE username fragment * of remote endpoint from the ICE session. * @param rem_pwd Optional pointer to receive ICE password of remote * endpoint from the ICE session. * * @return PJ_SUCCESS if the strings have been retrieved * successfully. */ PJ_DECL(pj_status_t) pj_ice_strans_get_ufrag_pwd(pj_ice_strans *ice_st, pj_str_t *loc_ufrag, pj_str_t *loc_pwd, pj_str_t *rem_ufrag, pj_str_t *rem_pwd); /** * Get the number of local candidates for the specified component ID. * * @param ice_st The ICE stream transport. * @param comp_id Component ID. * * @return The number of candidates. */ PJ_DECL(unsigned) pj_ice_strans_get_cands_count(pj_ice_strans *ice_st, unsigned comp_id); /** * Enumerate the local candidates for the specified component. * * @param ice_st The ICE stream transport. * @param comp_id Component ID. * @param count On input, it specifies the maximum number of * elements. On output, it will be filled with * the number of candidates copied to the * array. * @param cand Array of candidates. * * @return PJ_SUCCESS, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_ice_strans_enum_cands(pj_ice_strans *ice_st, unsigned comp_id, unsigned *count, pj_ice_sess_cand cand[]); /** * Get the default candidate for the specified component. When this * function is called before ICE negotiation completes, the default * candidate is selected according to local preference criteria. When * this function is called after ICE negotiation completes, the * default candidate is the candidate that forms the valid pair. * * @param ice_st The ICE stream transport. * @param comp_id Component ID. * @param cand Pointer to receive the default candidate * information. */ PJ_DECL(pj_status_t) pj_ice_strans_get_def_cand(pj_ice_strans *ice_st, unsigned comp_id, pj_ice_sess_cand *cand); /** * Get the current ICE role. ICE session must have been initialized * before this function can be called. * * @param ice_st The ICE stream transport. * * @return Current ICE role. */ PJ_DECL(pj_ice_sess_role) pj_ice_strans_get_role(pj_ice_strans *ice_st); /** * Change session role. This happens for example when ICE session was * created with controlled role when receiving an offer, but it turns out * that the offer contains "a=ice-lite" attribute when the SDP gets * inspected. ICE session must have been initialized before this function * can be called. * * @param ice_st The ICE stream transport. * @param new_role The new role to be set. * * @return PJ_SUCCESS on success, or the appropriate error. */ PJ_DECL(pj_status_t) pj_ice_strans_change_role(pj_ice_strans *ice_st, pj_ice_sess_role new_role); /** * Start ICE connectivity checks. This function can only be called * after the ICE session has been created in the ICE stream transport * with #pj_ice_strans_init_ice(). * * This function must be called once application has received remote * candidate list (typically from the remote SDP). This function pairs * local candidates with remote candidates, and starts ICE connectivity * checks. The ICE session/transport will then notify the application * via the callback when ICE connectivity checks completes, either * successfully or with failure. * * @param ice_st The ICE stream transport. * @param rem_ufrag Remote ufrag, as seen in the SDP received from * the remote agent. * @param rem_passwd Remote password, as seen in the SDP received from * the remote agent. * @param rcand_cnt Number of remote candidates in the array. * @param rcand Remote candidates array. * * @return PJ_SUCCESS, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_ice_strans_start_ice(pj_ice_strans *ice_st, const pj_str_t *rem_ufrag, const pj_str_t *rem_passwd, unsigned rcand_cnt, const pj_ice_sess_cand rcand[]); /** * Retrieve the candidate pair that has been nominated and successfully * checked for the specified component. If ICE negotiation is still in * progress or it has failed, this function will return NULL. * * @param ice_st The ICE stream transport. * @param comp_id Component ID. * * @return The valid pair as ICE checklist structure if the * pair exist. */ PJ_DECL(const pj_ice_sess_check*) pj_ice_strans_get_valid_pair(const pj_ice_strans *ice_st, unsigned comp_id); /** * Retrieve the ICE session associated with this transport * * @param ice_st The ICE stream transport. * * @return The ICE session associated with this transport */ PJ_DECL(pj_ice_sess*) pj_ice_strans_get_session(const pj_ice_strans *ice_st); /** * Retrieve the ICE start time * * @param ice_st The ICE stream transport. * * @return The ICE start time */ PJ_DECL(pj_time_val) pj_ice_strans_get_start_time(const pj_ice_strans *ice_st); /** * Stop and destroy the ICE session inside this media transport. Application * needs to call this function once the media session is over (the call has * been disconnected). * * Application MAY reuse this ICE stream transport for subsequent calls. * In this case, it must call #pj_ice_strans_stop_ice() when the call is * disconnected, and reinitialize the ICE stream transport for subsequent * call with #pj_ice_strans_init_ice()/#pj_ice_strans_start_ice(). In this * case, the ICE stream transport will maintain the internal sockets and * continue to send STUN keep-alive packets and TURN Refresh request to * keep the NAT binding/TURN allocation open and to detect change in STUN * mapped address. * * If application does not want to reuse the ICE stream transport for * subsequent calls, it must call #pj_ice_strans_destroy() to destroy the * ICE stream transport altogether. * * @param ice_st The ICE stream transport. * * @return PJ_SUCCESS, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_ice_strans_stop_ice(pj_ice_strans *ice_st); /** * Send outgoing packet using this transport. * Application can send data (normally RTP or RTCP packets) at any time * by calling this function. This function takes a destination * address as one of the arguments, and this destination address should * be taken from the default transport address of the component (that is * the address in SDP c= and m= lines, or in a=rtcp attribute). * If ICE negotiation is in progress, this function will send the data * to the destination address. Otherwise if ICE negotiation has completed * successfully, this function will send the data to the nominated remote * address, as negotiated by ICE. * * @param ice_st The ICE stream transport. * @param comp_id Component ID. * @param data The data or packet to be sent. * @param data_len Size of data or packet, in bytes. * @param dst_addr The destination address. * @param dst_addr_len Length of destination address. * * @return PJ_SUCCESS if data is sent successfully. */ PJ_DECL(pj_status_t) pj_ice_strans_sendto(pj_ice_strans *ice_st, unsigned comp_id, const void *data, pj_size_t data_len, const pj_sockaddr_t *dst_addr, int dst_addr_len); /** * @} */ PJ_END_DECL #endif /* __PJNATH_ICE_STRANS_H__ */ ================================================ FILE: deps/pjsip/pjnath/include/pjnath/nat_detect.h ================================================ /* $Id: nat_detect.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_NAT_DETECT_H__ #define __PJNATH_NAT_DETECT_H__ /** * @file ice_session.h * @brief ICE session management */ #include PJ_BEGIN_DECL /** * @defgroup PJNATH_NAT_DETECT NAT Classification/Detection Tool * @brief NAT Classification/Detection Tool * @ingroup PJNATH * @{ * * This module provides one function to perform NAT classification and * detection. NAT type detection is performed by calling * #pj_stun_detect_nat_type() function. */ /** * This enumeration describes the NAT types, as specified by RFC 3489 * Section 5, NAT Variations. */ typedef enum pj_stun_nat_type { /** * NAT type is unknown because the detection has not been performed. */ PJ_STUN_NAT_TYPE_UNKNOWN, /** * NAT type is unknown because there is failure in the detection * process, possibly because server does not support RFC 3489. */ PJ_STUN_NAT_TYPE_ERR_UNKNOWN, /** * This specifies that the client has open access to Internet (or * at least, its behind a firewall that behaves like a full-cone NAT, * but without the translation) */ PJ_STUN_NAT_TYPE_OPEN, /** * This specifies that communication with server has failed, probably * because UDP packets are blocked. */ PJ_STUN_NAT_TYPE_BLOCKED, /** * Firewall that allows UDP out, and responses have to come back to * the source of the request (like a symmetric NAT, but no * translation. */ PJ_STUN_NAT_TYPE_SYMMETRIC_UDP, /** * A full cone NAT is one where all requests from the same internal * IP address and port are mapped to the same external IP address and * port. Furthermore, any external host can send a packet to the * internal host, by sending a packet to the mapped external address. */ PJ_STUN_NAT_TYPE_FULL_CONE, /** * A symmetric NAT is one where all requests from the same internal * IP address and port, to a specific destination IP address and port, * are mapped to the same external IP address and port. If the same * host sends a packet with the same source address and port, but to * a different destination, a different mapping is used. Furthermore, * only the external host that receives a packet can send a UDP packet * back to the internal host. */ PJ_STUN_NAT_TYPE_SYMMETRIC, /** * A restricted cone NAT is one where all requests from the same * internal IP address and port are mapped to the same external IP * address and port. Unlike a full cone NAT, an external host (with * IP address X) can send a packet to the internal host only if the * internal host had previously sent a packet to IP address X. */ PJ_STUN_NAT_TYPE_RESTRICTED, /** * A port restricted cone NAT is like a restricted cone NAT, but the * restriction includes port numbers. Specifically, an external host * can send a packet, with source IP address X and source port P, * to the internal host only if the internal host had previously sent * a packet to IP address X and port P. */ PJ_STUN_NAT_TYPE_PORT_RESTRICTED } pj_stun_nat_type; /** * This structure contains the result of NAT classification function. */ typedef struct pj_stun_nat_detect_result { /** * Status of the detection process. If this value is not PJ_SUCCESS, * the detection has failed and \a nat_type field will contain * PJ_STUN_NAT_TYPE_UNKNOWN. */ pj_status_t status; /** * The text describing the status, if the status is not PJ_SUCCESS. */ const char *status_text; /** * This contains the NAT type as detected by the detection procedure. * This value is only valid when the \a status is PJ_SUCCESS. */ pj_stun_nat_type nat_type; /** * Text describing that NAT type. */ const char *nat_type_name; } pj_stun_nat_detect_result; /** * Type of callback to be called when the NAT detection function has * completed. */ typedef void pj_stun_nat_detect_cb(void *user_data, const pj_stun_nat_detect_result *res); /** * Get the NAT name from the specified NAT type. * * @param type NAT type. * * @return NAT name. */ PJ_DECL(const char*) pj_stun_get_nat_name(pj_stun_nat_type type); /** * Perform NAT classification function according to the procedures * specified in RFC 3489. Once this function returns successfully, * the procedure will run in the "background" and will complete * asynchronously. Application can register a callback to be notified * when such detection has completed. * * @param server STUN server address. * @param stun_cfg A structure containing various STUN configurations, * such as the ioqueue and timer heap instance used * to receive network I/O and timer events. * @param user_data Application data, which will be returned back * in the callback. * @param cb Callback to be registered to receive notification * about detection result. * * @return If this function returns PJ_SUCCESS, the procedure * will complete asynchronously and callback will be * called when it completes. For other return * values, it means that an error has occured and * the procedure did not start. */ PJ_DECL(pj_status_t) pj_stun_detect_nat_type(const pj_sockaddr_in *server, pj_stun_config *stun_cfg, void *user_data, pj_stun_nat_detect_cb *cb); /** * @} */ PJ_END_DECL #endif /* __PJNATH_NAT_DETECT_H__ */ ================================================ FILE: deps/pjsip/pjnath/include/pjnath/stun_auth.h ================================================ /* $Id: stun_auth.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_STUN_AUTH_H__ #define __PJNATH_STUN_AUTH_H__ /** * @file stun_auth.h * @brief STUN authentication. */ #include PJ_BEGIN_DECL /* **************************************************************************/ /** * @defgroup PJNATH_STUN_AUTH STUN Authentication * @brief STUN authentication helper * @ingroup PJNATH_STUN_BASE * @{ */ /** * Type of authentication. */ typedef enum pj_stun_auth_type { /** * No authentication. */ PJ_STUN_AUTH_NONE = 0, /** * Authentication using short term credential. */ PJ_STUN_AUTH_SHORT_TERM = 1, /** * Authentication using long term credential. */ PJ_STUN_AUTH_LONG_TERM = 2 } pj_stun_auth_type; /** * Type of authentication data in the credential. */ typedef enum pj_stun_auth_cred_type { /** * The credential data contains a static credential to be matched * against the credential in the message. A static credential can be * used as both client side or server side authentication. */ PJ_STUN_AUTH_CRED_STATIC, /** * The credential data contains callbacks to be called to verify the * credential in the message. A dynamic credential is suitable when * performing server side authentication where server does not know * in advance the identity of the user requesting authentication. */ PJ_STUN_AUTH_CRED_DYNAMIC } pj_stun_auth_cred_type; /** * Type of encoding applied to the password stored in the credential. */ typedef enum pj_stun_passwd_type { /** * Plain text password. */ PJ_STUN_PASSWD_PLAIN = 0, /** * Hashed password, valid for long term credential only. The hash value * of the password is calculated as MD5(USERNAME ":" REALM ":" PASSWD) * with all quotes removed from the username and realm values. */ PJ_STUN_PASSWD_HASHED = 1 } pj_stun_passwd_type; /** * This structure contains the descriptions needed to perform server side * authentication. Depending on the \a type set in the structure, application * may specify a static username/password combination, or to have callbacks * called by the function to authenticate the credential dynamically. */ typedef struct pj_stun_auth_cred { /** * The type of authentication information in this structure. */ pj_stun_auth_cred_type type; /** * This union contains the authentication data. */ union { /** * This structure contains static data for performing authentication. * A non-empty realm indicates whether short term or long term * credential is used. */ struct { /** * If not-empty, it indicates that this is a long term credential. */ pj_str_t realm; /** * The username of the credential. */ pj_str_t username; /** * Data type to indicate the type of password in the \a data field. */ pj_stun_passwd_type data_type; /** * The data, which depends depends on the value of \a data_type * field. When \a data_type is zero, this field will contain the * plaintext password. */ pj_str_t data; /** * Optional NONCE. */ pj_str_t nonce; } static_cred; /** * This structure contains callback to be called by the framework * to authenticate the incoming message. */ struct { /** * User data which will be passed back to callback functions. */ void *user_data; /** * This callback is called by pj_stun_verify_credential() when * server needs to challenge the request with 401 response. * * @param user_data The user data as specified in the credential. * @param pool Pool to allocate memory. * @param realm On return, the function should fill in with * realm if application wants to use long term * credential. Otherwise application should set * empty string for the realm. * @param nonce On return, if application wants to use long * term credential, it MUST fill in the nonce * with some value. Otherwise if short term * credential is wanted, it MAY set this value. * If short term credential is wanted and the * application doesn't want to include NONCE, * then it must set this to empty string. * * @return The callback should return PJ_SUCCESS, or * otherwise response message will not be * created. */ pj_status_t (*get_auth)(void *user_data, pj_pool_t *pool, pj_str_t *realm, pj_str_t *nonce); /** * Get the credential to be put in outgoing request. * * @param msg The outgoing message where the credential is * to be applied. * @param user_data The user data as specified in the credential. * @param pool Pool where the callback can allocate memory * to fill in the credential. * @param realm On return, the callback may specify the realm * if long term credential is desired, otherwise * this string must be set to empty. * @param username On return, the callback must fill in with the * username. * @param nonce On return, the callback may optionally fill in * this argument with NONCE value if desired, * otherwise this argument must be set to empty. * @param data_type On return, the callback must set this argument * with the type of password in the data argument. * @param data On return, the callback must set this with * the password, encoded according to data_type * argument. * * @return The callback must return PJ_SUCCESS, otherwise * the message transmission will be cancelled. */ pj_status_t (*get_cred)(const pj_stun_msg *msg, void *user_data, pj_pool_t *pool, pj_str_t *realm, pj_str_t *username, pj_str_t *nonce, pj_stun_passwd_type *data_type, pj_str_t *data); /** * Get the password for the specified username. This function * is also used to check whether the username is valid. * * @param msg The STUN message where the password will be * applied to. * @param user_data The user data as specified in the credential. * @param realm The realm as specified in the message. * @param username The username as specified in the message. * @param pool Pool to allocate memory when necessary. * @param data_type On return, application should fill up this * argument with the type of data (which should * be zero if data is a plaintext password). * @param data On return, application should fill up this * argument with the password according to * data_type. * * @return The callback should return PJ_SUCCESS if * username has been successfully verified * and password was obtained. If non-PJ_SUCCESS * is returned, it is assumed that the * username is not valid. */ pj_status_t (*get_password)(const pj_stun_msg *msg, void *user_data, const pj_str_t *realm, const pj_str_t *username, pj_pool_t *pool, pj_stun_passwd_type *data_type, pj_str_t *data); /** * This callback will be called to verify that the NONCE given * in the message can be accepted. If this callback returns * PJ_FALSE, 438 (Stale Nonce) response will be created. * * This callback is optional. * * @param msg The STUN message where the nonce was received. * @param user_data The user data as specified in the credential. * @param realm The realm as specified in the message. * @param username The username as specified in the message. * @param nonce The nonce to be verified. * * @return The callback MUST return non-zero if the * NONCE can be accepted. */ pj_bool_t (*verify_nonce)(const pj_stun_msg *msg, void *user_data, const pj_str_t *realm, const pj_str_t *username, const pj_str_t *nonce); } dyn_cred; } data; } pj_stun_auth_cred; /** * This structure contains the credential information that is found and * used to authenticate incoming requests. Application may use this * information when generating authentication for the outgoing response. */ typedef struct pj_stun_req_cred_info { /** * The REALM value found in the incoming request. If short term * credential is used, the value will be empty. */ pj_str_t realm; /** * The USERNAME value found in the incoming request. */ pj_str_t username; /** * Optional NONCE. */ pj_str_t nonce; /** * Authentication key that was used to authenticate the incoming * request. This key is created with #pj_stun_create_key(), and * it can be used to encode the credential of the outgoing * response. */ pj_str_t auth_key; } pj_stun_req_cred_info; /** * Duplicate authentication credential. * * @param pool Pool to be used to allocate memory. * @param dst Destination credential. * @param src Source credential. */ PJ_DECL(void) pj_stun_auth_cred_dup(pj_pool_t *pool, pj_stun_auth_cred *dst, const pj_stun_auth_cred *src); /** * Duplicate request credential. * * @param pool Pool to be used to allocate memory. * @param dst Destination credential. * @param src Source credential. */ PJ_DECL(void) pj_stun_req_cred_info_dup(pj_pool_t *pool, pj_stun_req_cred_info *dst, const pj_stun_req_cred_info *src); /** * Create authentication key to be used for encoding the message with * MESSAGE-INTEGRITY. If short term credential is used (i.e. the realm * argument is NULL or empty), the key will be copied from the password. * If long term credential is used, the key will be calculated from the * MD5 hash of the realm, username, and password. * * @param pool Pool to allocate memory for the key. * @param key String to receive the key. * @param realm The realm of the credential, if long term credential * is to be used. If short term credential is wanted, * application can put NULL or empty string here. * @param username The username. * @param data_type Password encoding. * @param data The password. */ PJ_DECL(void) pj_stun_create_key(pj_pool_t *pool, pj_str_t *key, const pj_str_t *realm, const pj_str_t *username, pj_stun_passwd_type data_type, const pj_str_t *data); /** * Verify credential in the STUN request. Note that before calling this * function, application must have checked that the message contains * PJ_STUN_ATTR_MESSAGE_INTEGRITY attribute by calling pj_stun_msg_find_attr() * function, because this function will reject the message with 401 error * if it doesn't contain PJ_STUN_ATTR_MESSAGE_INTEGRITY attribute. * * @param pkt The original packet which has been parsed into * the message. This packet MUST NOT have been modified * after the parsing. * @param pkt_len The length of the packet. * @param msg The parsed message to be verified. * @param cred Pointer to credential to be used to authenticate * the message. * @param pool If response is to be created, then memory will * be allocated from this pool. * @param info Optional pointer to receive authentication information * found in the request and the credential that is used * to authenticate the request. * @param p_response Optional pointer to receive the response message * then the credential in the request fails to * authenticate. * * @return PJ_SUCCESS if credential is verified successfully. * If the verification fails and \a p_response is not * NULL, an appropriate response will be returned in * \a p_response. */ PJ_DECL(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_msg *msg, pj_stun_auth_cred *cred, pj_pool_t *pool, pj_stun_req_cred_info *info, pj_stun_msg **p_response); /** * Determine if STUN message can be authenticated. Some STUN error * responses cannot be authenticated since they cannot contain STUN * MESSAGE-INTEGRITY attribute. STUN Indication messages also cannot * be authenticated. * * @param msg The STUN message. * * @return Non-zero if the STUN message can be authenticated. */ PJ_DECL(pj_bool_t) pj_stun_auth_valid_for_msg(const pj_stun_msg *msg); /** * Verify credential in the STUN response. Note that before calling this * function, application must have checked that the message contains * PJ_STUN_ATTR_MESSAGE_INTEGRITY attribute by calling pj_stun_msg_find_attr() * function, because otherwise this function will report authentication * failure. * * @param pkt The original packet which has been parsed into * the message. This packet MUST NOT have been modified * after the parsing. * @param pkt_len The length of the packet. * @param msg The parsed message to be verified. * @param key Authentication key to calculate MESSAGE-INTEGRITY * value. Application can create this key by using * #pj_stun_create_key() function. * * @return PJ_SUCCESS if credential is verified successfully. */ PJ_DECL(pj_status_t) pj_stun_authenticate_response(const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_msg *msg, const pj_str_t *key); /** * @} */ PJ_END_DECL #endif /* __PJNATH_STUN_AUTH_H__ */ ================================================ FILE: deps/pjsip/pjnath/include/pjnath/stun_config.h ================================================ /* $Id: stun_config.h 4199 2012-07-05 10:52:55Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_STUN_CONFIG_H__ #define __PJNATH_STUN_CONFIG_H__ /** * @file stun_config.h * @brief STUN endpoint. */ #include #include #include #include PJ_BEGIN_DECL /* **************************************************************************/ /** * @defgroup PJNATH_STUN_CONFIG STUN Config * @brief STUN config * @ingroup PJNATH_STUN_BASE * @{ */ /** * STUN configuration. */ typedef struct pj_stun_config { /** * Pool factory to be used. */ pj_pool_factory *pf; /** * Ioqueue. */ pj_ioqueue_t *ioqueue; /** * Timer heap instance. */ pj_timer_heap_t *timer_heap; /** * Options. */ unsigned options; /** * The default initial STUN round-trip time estimation in msecs. * The value normally is PJ_STUN_RTO_VALUE. */ unsigned rto_msec; /** * The interval to cache outgoing STUN response in the STUN session, * in miliseconds. * * Default 10000 (10 seconds). */ unsigned res_cache_msec; /** * Software name to be included in all STUN requests and responses. * * Default: PJNATH_STUN_SOFTWARE_NAME. */ pj_str_t software_name; } pj_stun_config; /** * Initialize STUN config. */ PJ_INLINE(void) pj_stun_config_init(pj_stun_config *cfg, pj_pool_factory *factory, unsigned options, pj_ioqueue_t *ioqueue, pj_timer_heap_t *timer_heap) { pj_bzero(cfg, sizeof(*cfg)); cfg->pf = factory; cfg->options = options; cfg->ioqueue = ioqueue; cfg->timer_heap = timer_heap; cfg->rto_msec = PJ_STUN_RTO_VALUE; cfg->res_cache_msec = PJ_STUN_RES_CACHE_DURATION; cfg->software_name = pj_str((char*)PJNATH_STUN_SOFTWARE_NAME); } /** * Check that STUN config is valid. */ PJ_INLINE(pj_status_t) pj_stun_config_check_valid(const pj_stun_config *cfg) { PJ_ASSERT_RETURN(cfg->ioqueue && cfg->pf && cfg->timer_heap && cfg->rto_msec && cfg->res_cache_msec, PJ_EINVAL); return PJ_SUCCESS; } /** * @} */ PJ_END_DECL #endif /* __PJNATH_STUN_CONFIG_H__ */ ================================================ FILE: deps/pjsip/pjnath/include/pjnath/stun_msg.h ================================================ /* $Id: stun_msg.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_STUN_MSG_H__ #define __PJNATH_STUN_MSG_H__ /** * @file stun_msg.h * @brief STUN message components. */ #include #include PJ_BEGIN_DECL /* **************************************************************************/ /** * @defgroup PJNATH_STUN_MSG STUN Message Representation and Parsing * @ingroup PJNATH_STUN_BASE * @brief Low-level representation and parsing of STUN messages. * @{ */ /** * STUN magic cookie. */ #define PJ_STUN_MAGIC 0x2112A442 /** * STUN method constants. */ enum pj_stun_method_e { /** * STUN Binding method as defined by RFC 3489-bis. */ PJ_STUN_BINDING_METHOD = 1, /** * STUN Shared Secret method as defined by RFC 3489-bis. */ PJ_STUN_SHARED_SECRET_METHOD = 2, /** * STUN/TURN Allocate method as defined by draft-ietf-behave-turn */ PJ_STUN_ALLOCATE_METHOD = 3, /** * STUN/TURN Refresh method as defined by draft-ietf-behave-turn */ PJ_STUN_REFRESH_METHOD = 4, /** * STUN/TURN Send indication as defined by draft-ietf-behave-turn */ PJ_STUN_SEND_METHOD = 6, /** * STUN/TURN Data indication as defined by draft-ietf-behave-turn */ PJ_STUN_DATA_METHOD = 7, /** * STUN/TURN CreatePermission method as defined by draft-ietf-behave-turn */ PJ_STUN_CREATE_PERM_METHOD = 8, /** * STUN/TURN ChannelBind as defined by draft-ietf-behave-turn */ PJ_STUN_CHANNEL_BIND_METHOD = 9, /** * All known methods. */ PJ_STUN_METHOD_MAX }; /** * Retrieve the STUN method from the message-type field of the STUN * message. */ #define PJ_STUN_GET_METHOD(msg_type) ((msg_type) & 0xFEEF) /** * STUN message classes constants. */ enum pj_stun_msg_class_e { /** * This specifies that the message type is a STUN request message. */ PJ_STUN_REQUEST_CLASS = 0, /** * This specifies that the message type is a STUN indication message. */ PJ_STUN_INDICATION_CLASS = 1, /** * This specifies that the message type is a STUN successful response. */ PJ_STUN_SUCCESS_CLASS = 2, /** * This specifies that the message type is a STUN error response. */ PJ_STUN_ERROR_CLASS = 3 }; /** * Determine if the message type is a request. */ #define PJ_STUN_IS_REQUEST(msg_type) (((msg_type) & 0x0110) == 0x0000) /** * Determine if the message type is a successful response. */ #define PJ_STUN_IS_SUCCESS_RESPONSE(msg_type) (((msg_type) & 0x0110) == 0x0100) /** * The response bit in the message type. */ #define PJ_STUN_SUCCESS_RESPONSE_BIT (0x0100) /** * Determine if the message type is an error response. */ #define PJ_STUN_IS_ERROR_RESPONSE(msg_type) (((msg_type) & 0x0110) == 0x0110) /** * The error response bit in the message type. */ #define PJ_STUN_ERROR_RESPONSE_BIT (0x0110) /** * Determine if the message type is a response. */ #define PJ_STUN_IS_RESPONSE(msg_type) (((msg_type) & 0x0100) == 0x0100) /** * Determine if the message type is an indication message. */ #define PJ_STUN_IS_INDICATION(msg_type) (((msg_type) & 0x0110) == 0x0010) /** * The error response bit in the message type. */ #define PJ_STUN_INDICATION_BIT (0x0010) /** * This enumeration describes STUN message types. */ typedef enum pj_stun_msg_type { /** * STUN BINDING request. */ PJ_STUN_BINDING_REQUEST = 0x0001, /** * Successful response to STUN BINDING-REQUEST. */ PJ_STUN_BINDING_RESPONSE = 0x0101, /** * Error response to STUN BINDING-REQUEST. */ PJ_STUN_BINDING_ERROR_RESPONSE = 0x0111, /** * Binding Indication (ICE) */ PJ_STUN_BINDING_INDICATION = 0x0011, /** * STUN SHARED-SECRET reqeust. */ PJ_STUN_SHARED_SECRET_REQUEST = 0x0002, /** * Successful response to STUN SHARED-SECRET reqeust. */ PJ_STUN_SHARED_SECRET_RESPONSE = 0x0102, /** * Error response to STUN SHARED-SECRET reqeust. */ PJ_STUN_SHARED_SECRET_ERROR_RESPONSE = 0x0112, /** * STUN/TURN Allocate Request */ PJ_STUN_ALLOCATE_REQUEST = 0x0003, /** * Successful response to STUN/TURN Allocate Request */ PJ_STUN_ALLOCATE_RESPONSE = 0x0103, /** * Failure response to STUN/TURN Allocate Request */ PJ_STUN_ALLOCATE_ERROR_RESPONSE = 0x0113, /** * STUN/TURN REFRESH Request */ PJ_STUN_REFRESH_REQUEST = 0x0004, /** * Successful response to STUN REFRESH request */ PJ_STUN_REFRESH_RESPONSE = 0x0104, /** * Error response to STUN REFRESH request. */ PJ_STUN_REFRESH_ERROR_RESPONSE = 0x0114, /** * TURN Send indication */ PJ_STUN_SEND_INDICATION = 0x0016, /** * TURN Data indication */ PJ_STUN_DATA_INDICATION = 0x0017, /** * TURN CreatePermission request */ PJ_STUN_CREATE_PERM_REQUEST = 0x0008, /** * TURN CreatePermission successful response. */ PJ_STUN_CREATE_PERM_RESPONSE = 0x0108, /** * TURN CreatePermission failure response */ PJ_STUN_CREATE_PERM_ERROR_RESPONSE = 0x0118, /** * STUN/TURN ChannelBind Request */ PJ_STUN_CHANNEL_BIND_REQUEST = 0x0009, /** * Successful response to STUN ChannelBind request */ PJ_STUN_CHANNEL_BIND_RESPONSE = 0x0109, /** * Error response to STUN ChannelBind request. */ PJ_STUN_CHANNEL_BIND_ERROR_RESPONSE = 0x0119 } pj_stun_msg_type; /** * This enumeration describes STUN attribute types. */ typedef enum pj_stun_attr_type { PJ_STUN_ATTR_MAPPED_ADDR = 0x0001,/**< MAPPED-ADDRESS. */ PJ_STUN_ATTR_RESPONSE_ADDR = 0x0002,/**< RESPONSE-ADDRESS (deprcatd)*/ PJ_STUN_ATTR_CHANGE_REQUEST = 0x0003,/**< CHANGE-REQUEST (deprecated)*/ PJ_STUN_ATTR_SOURCE_ADDR = 0x0004,/**< SOURCE-ADDRESS (deprecated)*/ PJ_STUN_ATTR_CHANGED_ADDR = 0x0005,/**< CHANGED-ADDRESS (deprecatd)*/ PJ_STUN_ATTR_USERNAME = 0x0006,/**< USERNAME attribute. */ PJ_STUN_ATTR_PASSWORD = 0x0007,/**< was PASSWORD attribute. */ PJ_STUN_ATTR_MESSAGE_INTEGRITY = 0x0008,/**< MESSAGE-INTEGRITY. */ PJ_STUN_ATTR_ERROR_CODE = 0x0009,/**< ERROR-CODE. */ PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES = 0x000A,/**< UNKNOWN-ATTRIBUTES. */ PJ_STUN_ATTR_REFLECTED_FROM = 0x000B,/**< REFLECTED-FROM (deprecatd)*/ PJ_STUN_ATTR_CHANNEL_NUMBER = 0x000C,/**< TURN CHANNEL-NUMBER */ PJ_STUN_ATTR_LIFETIME = 0x000D,/**< TURN LIFETIME attr. */ PJ_STUN_ATTR_MAGIC_COOKIE = 0x000F,/**< MAGIC-COOKIE attr (deprec)*/ PJ_STUN_ATTR_BANDWIDTH = 0x0010,/**< TURN BANDWIDTH (deprec) */ PJ_STUN_ATTR_XOR_PEER_ADDR = 0x0012,/**< TURN XOR-PEER-ADDRESS */ PJ_STUN_ATTR_DATA = 0x0013,/**< DATA attribute. */ PJ_STUN_ATTR_REALM = 0x0014,/**< REALM attribute. */ PJ_STUN_ATTR_NONCE = 0x0015,/**< NONCE attribute. */ PJ_STUN_ATTR_XOR_RELAYED_ADDR = 0x0016,/**< TURN XOR-RELAYED-ADDRESS */ PJ_STUN_ATTR_REQ_ADDR_TYPE = 0x0017,/**< REQUESTED-ADDRESS-TYPE */ PJ_STUN_ATTR_EVEN_PORT = 0x0018,/**< TURN EVEN-PORT */ PJ_STUN_ATTR_REQ_TRANSPORT = 0x0019,/**< TURN REQUESTED-TRANSPORT */ PJ_STUN_ATTR_DONT_FRAGMENT = 0x001A,/**< TURN DONT-FRAGMENT */ PJ_STUN_ATTR_XOR_MAPPED_ADDR = 0x0020,/**< XOR-MAPPED-ADDRESS */ PJ_STUN_ATTR_TIMER_VAL = 0x0021,/**< TIMER-VAL attribute. */ PJ_STUN_ATTR_RESERVATION_TOKEN = 0x0022,/**< TURN RESERVATION-TOKEN */ PJ_STUN_ATTR_XOR_REFLECTED_FROM = 0x0023,/**< XOR-REFLECTED-FROM */ PJ_STUN_ATTR_PRIORITY = 0x0024,/**< PRIORITY */ PJ_STUN_ATTR_USE_CANDIDATE = 0x0025,/**< USE-CANDIDATE */ PJ_STUN_ATTR_ICMP = 0x0030,/**< ICMP (TURN) */ PJ_STUN_ATTR_END_MANDATORY_ATTR, PJ_STUN_ATTR_START_EXTENDED_ATTR= 0x8021, PJ_STUN_ATTR_SOFTWARE = 0x8022,/**< SOFTWARE attribute. */ PJ_STUN_ATTR_ALTERNATE_SERVER = 0x8023,/**< ALTERNATE-SERVER. */ PJ_STUN_ATTR_REFRESH_INTERVAL = 0x8024,/**< REFRESH-INTERVAL. */ PJ_STUN_ATTR_FINGERPRINT = 0x8028,/**< FINGERPRINT attribute. */ PJ_STUN_ATTR_ICE_CONTROLLED = 0x8029,/**< ICE-CCONTROLLED attribute.*/ PJ_STUN_ATTR_ICE_CONTROLLING = 0x802a,/**< ICE-CCONTROLLING attribute*/ PJ_STUN_ATTR_END_EXTENDED_ATTR } pj_stun_attr_type; /** * STUN error codes, which goes into STUN ERROR-CODE attribute. */ typedef enum pj_stun_status { PJ_STUN_SC_TRY_ALTERNATE = 300, /**< Try Alternate */ PJ_STUN_SC_BAD_REQUEST = 400, /**< Bad Request */ PJ_STUN_SC_UNAUTHORIZED = 401, /**< Unauthorized */ PJ_STUN_SC_FORBIDDEN = 403, /**< Forbidden (TURN) */ PJ_STUN_SC_UNKNOWN_ATTRIBUTE = 420, /**< Unknown Attribute */ #if 0 /* These were obsolete in recent rfc3489bis */ //PJ_STUN_SC_STALE_CREDENTIALS = 430, /**< Stale Credentials */ //PJ_STUN_SC_INTEGRITY_CHECK_FAILURE= 431, /**< Integrity Chk Fail */ //PJ_STUN_SC_MISSING_USERNAME = 432, /**< Missing Username */ //PJ_STUN_SC_USE_TLS = 433, /**< Use TLS */ //PJ_STUN_SC_MISSING_REALM = 434, /**< Missing Realm */ //PJ_STUN_SC_MISSING_NONCE = 435, /**< Missing Nonce */ //PJ_STUN_SC_UNKNOWN_USERNAME = 436, /**< Unknown Username */ #endif PJ_STUN_SC_ALLOCATION_MISMATCH = 437, /**< TURN Alloc Mismatch */ PJ_STUN_SC_STALE_NONCE = 438, /**< Stale Nonce */ PJ_STUN_SC_TRANSITIONING = 439, /**< Transitioning. */ PJ_STUN_SC_WRONG_CREDENTIALS = 441, /**< TURN Wrong Credentials */ PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO = 442, /**< Unsupported Transport or Protocol (TURN) */ PJ_STUN_SC_OPER_TCP_ONLY = 445, /**< Operation for TCP Only */ PJ_STUN_SC_CONNECTION_FAILURE = 446, /**< Connection Failure */ PJ_STUN_SC_CONNECTION_TIMEOUT = 447, /**< Connection Timeout */ PJ_STUN_SC_ALLOCATION_QUOTA_REACHED = 486, /**< Allocation Quota Reached (TURN) */ PJ_STUN_SC_ROLE_CONFLICT = 487, /**< Role Conflict */ PJ_STUN_SC_SERVER_ERROR = 500, /**< Server Error */ PJ_STUN_SC_INSUFFICIENT_CAPACITY = 508, /**< Insufficient Capacity (TURN) */ PJ_STUN_SC_GLOBAL_FAILURE = 600 /**< Global Failure */ } pj_stun_status; /** * This structure describes STUN message header. A STUN message has the * following format: * * \verbatim 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0 0| STUN Message Type | Message Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Magic Cookie | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Transaction ID +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ \endverbatim */ typedef struct pj_stun_msg_hdr { /** * STUN message type, which the first two bits must be zeroes. */ pj_uint16_t type; /** * The message length is the size, in bytes, of the message not * including the 20 byte STUN header. */ pj_uint16_t length; /** * The magic cookie is a fixed value, 0x2112A442 (PJ_STUN_MAGIC constant). * In the previous version of this specification [15] this field was part * of the transaction ID. */ pj_uint32_t magic; /** * The transaction ID is a 96 bit identifier. STUN transactions are * identified by their unique 96-bit transaction ID. For request/ * response transactions, the transaction ID is chosen by the STUN * client and MUST be unique for each new STUN transaction generated by * that STUN client. The transaction ID MUST be uniformly and randomly * distributed between 0 and 2**96 - 1. */ pj_uint8_t tsx_id[12]; } pj_stun_msg_hdr; /** * This structre describes STUN attribute header. Each attribute is * TLV encoded, with a 16 bit type, 16 bit length, and variable value. * Each STUN attribute ends on a 32 bit boundary: * * \verbatim 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Type | Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ \endverbatim */ typedef struct pj_stun_attr_hdr { /** * STUN attribute type. */ pj_uint16_t type; /** * The Length refers to the length of the actual useful content of the * Value portion of the attribute, measured in bytes. The value * in the Length field refers to the length of the Value part of the * attribute prior to padding - i.e., the useful content. */ pj_uint16_t length; } pj_stun_attr_hdr; /** * This structure describes STUN generic IP address attribute, used for * example to represent STUN MAPPED-ADDRESS attribute. * * The generic IP address attribute indicates the transport address. * It consists of an eight bit address family, and a sixteen bit port, * followed by a fixed length value representing the IP address. If the * address family is IPv4, the address is 32 bits, in network byte * order. If the address family is IPv6, the address is 128 bits in * network byte order. * * The format of the generic IP address attribute is: * * \verbatim 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |x x x x x x x x| Family | Port | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Address (variable) +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ \endverbatim */ typedef struct pj_stun_sockaddr_attr { /** * Standard STUN attribute header. */ pj_stun_attr_hdr hdr; /** * Flag to indicate whether this attribute should be sent in XOR-ed * format, or has been received in XOR-ed format. */ pj_bool_t xor_ed; /** * The socket address */ pj_sockaddr sockaddr; } pj_stun_sockaddr_attr; /** * This structure represents a generic STUN attributes with no payload, * and it is used for example by ICE USE-CANDIDATE attribute. */ typedef struct pj_stun_empty_attr { /** * Standard STUN attribute header. */ pj_stun_attr_hdr hdr; } pj_stun_empty_attr; /** * This structure represents generic STUN string attributes, such as STUN * USERNAME, PASSWORD, SOFTWARE, REALM, and NONCE attributes. */ typedef struct pj_stun_string_attr { /** * Standard STUN attribute header. */ pj_stun_attr_hdr hdr; /** * The string value. */ pj_str_t value; } pj_stun_string_attr; /** * This structure represents a generic STUN attributes with 32bit (unsigned) * integer value, such as STUN FINGERPRINT and REFRESH-INTERVAL attributes. */ typedef struct pj_stun_uint_attr { /** * Standard STUN attribute header. */ pj_stun_attr_hdr hdr; /** * The 32bit value, in host byte order. */ pj_uint32_t value; } pj_stun_uint_attr; /** * This structure represents a generic STUN attributes with 64bit (unsigned) * integer value, such as ICE-CONTROLLED and ICE-CONTROLLING attributes. */ typedef struct pj_stun_uint64_attr { /** * Standard STUN attribute header. */ pj_stun_attr_hdr hdr; /** * The 64bit value, in host byte order, represented with pj_timestamp. */ pj_timestamp value; } pj_stun_uint64_attr; /** * This structure represents generic STUN attributes to hold a raw binary * data. */ typedef struct pj_stun_binary_attr { /** * Standard STUN attribute header. */ pj_stun_attr_hdr hdr; /** * Special signature to indicate that this is a valid attribute even * though we don't have meta-data to describe this attribute. */ pj_uint32_t magic; /** * Length of the data. */ unsigned length; /** * The raw data. */ pj_uint8_t *data; } pj_stun_binary_attr; /** * This structure describes STUN MESSAGE-INTEGRITY attribute. * The MESSAGE-INTEGRITY attribute contains an HMAC-SHA1 [10] of the * STUN message. The MESSAGE-INTEGRITY attribute can be present in any * STUN message type. Since it uses the SHA1 hash, the HMAC will be 20 * bytes. */ typedef struct pj_stun_msgint_attr { /** * Standard STUN attribute header. */ pj_stun_attr_hdr hdr; /** * The 20 bytes hmac value. */ pj_uint8_t hmac[20]; } pj_stun_msgint_attr; /** * This structure describes STUN FINGERPRINT attribute. The FINGERPRINT * attribute can be present in all STUN messages. It is computed as * the CRC-32 of the STUN message up to (but excluding) the FINGERPRINT * attribute itself, xor-d with the 32 bit value 0x5354554e */ typedef struct pj_stun_uint_attr pj_stun_fingerprint_attr; /** * This structure represents STUN ERROR-CODE attribute. The ERROR-CODE * attribute is present in the Binding Error Response and Shared Secret * Error Response. It is a numeric value in the range of 100 to 699 * plus a textual reason phrase encoded in UTF-8 * * \verbatim 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0 |Class| Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Reason Phrase (variable) .. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ \endverbatim */ typedef struct pj_stun_errcode_attr { /** * Standard STUN attribute header. */ pj_stun_attr_hdr hdr; /** * STUN error code. */ int err_code; /** * The reason phrase. */ pj_str_t reason; } pj_stun_errcode_attr; /** * This describes STUN REALM attribute. * The REALM attribute is present in requests and responses. It * contains text which meets the grammar for "realm" as described in RFC * 3261 [11], and will thus contain a quoted string (including the * quotes). */ typedef struct pj_stun_string_attr pj_stun_realm_attr; /** * This describes STUN NONCE attribute. * The NONCE attribute is present in requests and in error responses. * It contains a sequence of qdtext or quoted-pair, which are defined in * RFC 3261 [11]. See RFC 2617 [7] for guidance on selection of nonce * values in a server. */ typedef struct pj_stun_string_attr pj_stun_nonce_attr; /** * This describes STUN UNKNOWN-ATTRIBUTES attribute. * The UNKNOWN-ATTRIBUTES attribute is present only in an error response * when the response code in the ERROR-CODE attribute is 420. * The attribute contains a list of 16 bit values, each of which * represents an attribute type that was not understood by the server. * If the number of unknown attributes is an odd number, one of the * attributes MUST be repeated in the list, so that the total length of * the list is a multiple of 4 bytes. */ typedef struct pj_stun_unknown_attr { /** * Standard STUN attribute header. */ pj_stun_attr_hdr hdr; /** * Number of unknown attributes in the array. */ unsigned attr_count; /** * Array of unknown attribute IDs. */ pj_uint16_t attrs[PJ_STUN_MAX_ATTR]; } pj_stun_unknown_attr; /** * This structure describes STUN MAPPED-ADDRESS attribute. * The MAPPED-ADDRESS attribute indicates the mapped transport address. */ typedef struct pj_stun_sockaddr_attr pj_stun_mapped_addr_attr; /** * This describes STUN XOR-MAPPED-ADDRESS attribute (which has the same * format as STUN MAPPED-ADDRESS attribute). * The XOR-MAPPED-ADDRESS attribute is present in responses. It * provides the same information that would present in the MAPPED- * ADDRESS attribute but because the NAT's public IP address is * obfuscated through the XOR function, STUN messages are able to pass * through NATs which would otherwise interfere with STUN. */ typedef struct pj_stun_sockaddr_attr pj_stun_xor_mapped_addr_attr; /** * This describes STUN SOFTWARE attribute. * The SOFTWARE attribute contains a textual description of the software * being used by the agent sending the message. It is used by clients * and servers. Its value SHOULD include manufacturer and version * number. */ typedef struct pj_stun_string_attr pj_stun_software_attr; /** * This describes STUN ALTERNATE-SERVER attribute. * The alternate server represents an alternate transport address for a * different STUN server to try. It is encoded in the same way as * MAPPED-ADDRESS. */ typedef struct pj_stun_sockaddr_attr pj_stun_alt_server_attr; /** * This describes STUN REFRESH-INTERVAL attribute. * The REFRESH-INTERVAL indicates the number of milliseconds that the * server suggests the client should use between refreshes of the NAT * bindings between the client and server. */ typedef struct pj_stun_uint_attr pj_stun_refresh_interval_attr; /** * This structure describes STUN RESPONSE-ADDRESS attribute. * The RESPONSE-ADDRESS attribute indicates where the response to a * Binding Request should be sent. Its syntax is identical to MAPPED- * ADDRESS. * * Note that the usage of this attribute has been deprecated by the * RFC 3489-bis standard. */ typedef struct pj_stun_sockaddr_attr pj_stun_response_addr_attr; /** * This structure describes STUN CHANGED-ADDRESS attribute. * The CHANGED-ADDRESS attribute indicates the IP address and port where * responses would have been sent from if the "change IP" and "change * port" flags had been set in the CHANGE-REQUEST attribute of the * Binding Request. The attribute is always present in a Binding * Response, independent of the value of the flags. Its syntax is * identical to MAPPED-ADDRESS. * * Note that the usage of this attribute has been deprecated by the * RFC 3489-bis standard. */ typedef struct pj_stun_sockaddr_attr pj_stun_changed_addr_attr; /** * This structure describes STUN CHANGE-REQUEST attribute. * The CHANGE-REQUEST attribute is used by the client to request that * the server use a different address and/or port when sending the * response. * * Bit 29 of the value is the "change IP" flag. If true, it requests * the server to send the Binding Response with a different IP address * than the one the Binding Request was received on. * * Bit 30 of the value is the "change port" flag. If true, it requests * the server to send the Binding Response with a different port than * the one the Binding Request was received on. * * Note that the usage of this attribute has been deprecated by the * RFC 3489-bis standard. */ typedef struct pj_stun_uint_attr pj_stun_change_request_attr; /** * This structure describes STUN SOURCE-ADDRESS attribute. * The SOURCE-ADDRESS attribute is present in Binding Responses. It * indicates the source IP address and port that the server is sending * the response from. Its syntax is identical to that of MAPPED- * ADDRESS. * * Note that the usage of this attribute has been deprecated by the * RFC 3489-bis standard. */ typedef struct pj_stun_sockaddr_attr pj_stun_src_addr_attr; /** * This describes the STUN REFLECTED-FROM attribute. * The REFLECTED-FROM attribute is present only in Binding Responses, * when the Binding Request contained a RESPONSE-ADDRESS attribute. The * attribute contains the identity (in terms of IP address) of the * source where the request came from. Its purpose is to provide * traceability, so that a STUN server cannot be used as a reflector for * denial-of-service attacks. */ typedef struct pj_stun_sockaddr_attr pj_stun_reflected_from_attr; /** * This describes STUN USERNAME attribute. * The USERNAME attribute is used for message integrity. It identifies * the shared secret used in the message integrity check. Consequently, * the USERNAME MUST be included in any request that contains the * MESSAGE-INTEGRITY attribute. */ typedef struct pj_stun_string_attr pj_stun_username_attr; /** * This describes STUN PASSWORD attribute. * If the message type is Shared Secret Response it MUST include the * PASSWORD attribute. */ typedef struct pj_stun_string_attr pj_stun_password_attr; /** * This describes TURN CHANNEL-NUMBER attribute. In this library, * this attribute is represented with 32bit integer. Application may * use #PJ_STUN_GET_CH_NB() and #PJ_STUN_SET_CH_NB() to extract/set * channel number value from the 32bit integral value. * * The CHANNEL-NUMBER attribute contains the number of the channel. * It is a 16-bit unsigned integer, followed by a two-octet RFFU field * which MUST be set to 0 on transmission and ignored on reception. \verbatim 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Channel Number | RFFU | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ \endverbatim */ typedef struct pj_stun_uint_attr pj_stun_channel_number_attr; /** * Get 16bit channel number from 32bit integral value. * Note that uint32 attributes are always stored in host byte order * after they have been parsed from the PDU, so no need to do ntohs() * here. */ #define PJ_STUN_GET_CH_NB(u32) ((pj_uint16_t)(u32>>16)) /** * Convert 16bit channel number into 32bit integral value. * Note that uint32 attributes will be converted to network byte order * when the attribute is written to packet, so no need to do htons() * here. */ #define PJ_STUN_SET_CH_NB(chnum) (((pj_uint32_t)chnum) << 16) /** * This describes STUN LIFETIME attribute. * The lifetime attribute represents the duration for which the server * will maintain an allocation in the absence of data traffic either * from or to the client. It is a 32 bit value representing the number * of seconds remaining until expiration. */ typedef struct pj_stun_uint_attr pj_stun_lifetime_attr; /** * This describes STUN BANDWIDTH attribute. * The bandwidth attribute represents the peak bandwidth, measured in * kbits per second, that the client expects to use on the binding. The * value represents the sum in the receive and send directions. */ typedef struct pj_stun_uint_attr pj_stun_bandwidth_attr; /** * This describes the STUN XOR-PEER-ADDRESS attribute. * The XOR-PEER-ADDRESS specifies the address and port of the peer as seen * from the TURN server. It is encoded in the same way as XOR-MAPPED- * ADDRESS. */ typedef struct pj_stun_sockaddr_attr pj_stun_xor_peer_addr_attr; /** * This describes the STUN DATA attribute. * The DATA attribute is present in Send Indications and Data * Indications. It contains raw payload data that is to be sent (in the * case of a Send Request) or was received (in the case of a Data * Indication).. */ typedef struct pj_stun_binary_attr pj_stun_data_attr; /** * This describes the STUN XOR-RELAYED-ADDRESS attribute. The * XOR-RELAYED-ADDRESS is present in Allocate responses. It specifies the * address and port that the server allocated to the client. It is * encoded in the same way as XOR-MAPPED-ADDRESS. */ typedef struct pj_stun_sockaddr_attr pj_stun_xor_relayed_addr_attr; /** * This describes the REQUESTED-ADDRESS-TYPE attribute. * The REQUESTED-ADDRESS-TYPE attribute is used by clients to request * the allocation of a specific address type from a server. The * following is the format of the REQUESTED-ADDRESS-TYPE attribute. \verbatim 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Family | Reserved | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ \endverbatim */ typedef struct pj_stun_uint_attr pj_stun_req_addr_type_attr; /** * This describes the TURN REQUESTED-TRANSPORT attribute, encoded in * STUN generic integer attribute. * * This attribute allows the client to request that the port in the * relayed-transport-address be even, and (optionally) that the server * reserve the next-higher port number. The attribute is 8 bits long. * Its format is: \verbatim 0 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ |R| RFFU | +-+-+-+-+-+-+-+-+ \endverbatim * The attribute contains a single 1-bit flag: * * R: If 1, the server is requested to reserve the next higher port * number (on the same IP address) for a subsequent allocation. If * 0, no such reservation is requested. * * The other 7 bits of the attribute must be set to zero on transmission * and ignored on reception. */ typedef struct pj_stun_uint_attr pj_stun_even_port_attr; /** * This describes the TURN REQUESTED-TRANSPORT attribute, encoded in * STUN generic integer attribute. * * This attribute is used by the client to request a specific transport * protocol for the allocated transport address. It has the following * format: \verbatim 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Protocol | RFFU | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ \endverbatim * The Protocol field specifies the desired protocol. The codepoints * used in this field are taken from those allowed in the Protocol field * in the IPv4 header and the NextHeader field in the IPv6 header * [Protocol-Numbers]. This specification only allows the use of * codepoint 17 (User Datagram Protocol). * * The RFFU field is set to zero on transmission and ignored on * receiption. It is reserved for future uses. */ typedef struct pj_stun_uint_attr pj_stun_req_transport_attr; /** * Get protocol value from 32bit TURN REQUESTED-TRANSPORT attribute. */ #define PJ_STUN_GET_RT_PROTO(u32) (u32 >> 24) /** * Convert protocol value to be placed in 32bit TURN REQUESTED-TRANSPORT * attribute. */ #define PJ_STUN_SET_RT_PROTO(proto) (((pj_uint32_t)(proto)) << 24) /** * This describes the TURN DONT-FRAGMENT attribute. * * This attribute is used by the client to request that the server set * the DF (Don't Fragment) bit in the IP header when relaying the * application data onward to the peer. This attribute has no value * part and thus the attribute length field is 0. */ typedef struct pj_stun_empty_attr pj_stun_dont_fragment_attr; /** * This describes the TURN RESERVATION-TOKEN attribute. * The RESERVATION-TOKEN attribute contains a token that uniquely * identifies a relayed transport address being held in reserve by the * server. The server includes this attribute in a success response to * tell the client about the token, and the client includes this * attribute in a subsequent Allocate request to request the server use * that relayed transport address for the allocation. * * The attribute value is a 64-bit-long field containing the token * value. */ typedef struct pj_stun_uint64_attr pj_stun_res_token_attr; /** * This describes the XOR-REFLECTED-FROM attribute, as described by * draft-macdonald-behave-nat-behavior-discovery-00. * The XOR-REFLECTED-FROM attribute is used in place of the REFLECTED- * FROM attribute. It provides the same information, but because the * NAT's public address is obfuscated through the XOR function, It can * pass through a NAT that would otherwise attempt to translate it to * the private network address. XOR-REFLECTED-FROM has identical syntax * to XOR-MAPPED-ADDRESS. */ typedef struct pj_stun_sockaddr_attr pj_stun_xor_reflected_from_attr; /** * This describes the PRIORITY attribute from draft-ietf-mmusic-ice-13. * The PRIORITY attribute indicates the priority that is to be * associated with a peer reflexive candidate, should one be discovered * by this check. It is a 32 bit unsigned integer, and has an attribute * type of 0x0024. */ typedef struct pj_stun_uint_attr pj_stun_priority_attr; /** * This describes the USE-CANDIDATE attribute from draft-ietf-mmusic-ice-13. * The USE-CANDIDATE attribute indicates that the candidate pair * resulting from this check should be used for transmission of media. * The attribute has no content (the Length field of the attribute is * zero); it serves as a flag. */ typedef struct pj_stun_empty_attr pj_stun_use_candidate_attr; /** * This describes the STUN TIMER-VAL attribute. * The TIMER-VAL attribute is used only in conjunction with the Set * Active Destination response. It conveys from the server, to the * client, the value of the timer used in the server state machine. */ typedef struct pj_stun_uint_attr pj_stun_timer_val_attr; /** * This describes ICE-CONTROLLING attribute. */ typedef struct pj_stun_uint64_attr pj_stun_ice_controlling_attr; /** * This describes ICE-CONTROLLED attribute. */ typedef struct pj_stun_uint64_attr pj_stun_ice_controlled_attr; /** * This describes TURN ICMP attribute */ typedef struct pj_stun_uint_attr pj_stun_icmp_attr; /** * This structure describes a parsed STUN message. All integral fields * in this structure (including IP addresses) will be in the host * byte order. */ typedef struct pj_stun_msg { /** * STUN message header. */ pj_stun_msg_hdr hdr; /** * Number of attributes in the STUN message. */ unsigned attr_count; /** * Array of STUN attributes. */ pj_stun_attr_hdr *attr[PJ_STUN_MAX_ATTR]; } pj_stun_msg; /** STUN decoding options */ enum pj_stun_decode_options { /** * Tell the decoder that the message was received from datagram * oriented transport (such as UDP). */ PJ_STUN_IS_DATAGRAM = 1, /** * Tell pj_stun_msg_decode() to check the validity of the STUN * message by calling pj_stun_msg_check() before starting to * decode the packet. */ PJ_STUN_CHECK_PACKET = 2, /** * This option current is only valid for #pj_stun_session_on_rx_pkt(). * When specified, it tells the session NOT to authenticate the * message. */ PJ_STUN_NO_AUTHENTICATE = 4, /** * Disable FINGERPRINT verification. This option can be used when calling * #pj_stun_msg_check() and #pj_stun_msg_decode() to disable the * verification of FINGERPRINT, for example when the STUN usage says when * FINGERPRINT mechanism shall not be used. */ PJ_STUN_NO_FINGERPRINT_CHECK = 8 }; /** * Get STUN message method name. * * @param msg_type The STUN message type (in host byte order) * * @return The STUN message method name string. */ PJ_DECL(const char*) pj_stun_get_method_name(unsigned msg_type); /** * Get STUN message class name. * * @param msg_type The STUN message type (in host byte order) * * @return The STUN message class name string. */ PJ_DECL(const char*) pj_stun_get_class_name(unsigned msg_type); /** * Get STUN attribute name. * * @return attr_type The STUN attribute type (in host byte order). * * @return The STUN attribute type name string. */ PJ_DECL(const char*) pj_stun_get_attr_name(unsigned attr_type); /** * Get STUN standard reason phrase for the specified error code. * * @param err_code The STUN error code. * * @return The STUN error reason phrase. */ PJ_DECL(pj_str_t) pj_stun_get_err_reason(int err_code); /** * Internal: set the padding character for string attribute. * The default padding character is PJ_STUN_STRING_ATTR_PAD_CHR. * * @return The previous padding character. */ PJ_DECL(int) pj_stun_set_padding_char(int chr); /** * Initialize a generic STUN message. * * @param msg The message structure to be initialized. * @param msg_type The 14bit message type (see pj_stun_msg_type * constants). * @param magic Magic value to be put to the mesage; for requests, * the value normally should be PJ_STUN_MAGIC. * @param tsx_id Optional transaction ID, or NULL to let the * function generates a random transaction ID. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pj_stun_msg_init(pj_stun_msg *msg, unsigned msg_type, pj_uint32_t magic, const pj_uint8_t tsx_id[12]); /** * Create a generic STUN message. * * @param pool Pool to create the STUN message. * @param msg_type The 14bit message type. * @param magic Magic value to be put to the mesage; for requests, * the value should be PJ_STUN_MAGIC. * @param tsx_id Optional transaction ID, or NULL to let the * function generates a random transaction ID. * @param p_msg Pointer to receive the message. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pj_stun_msg_create(pj_pool_t *pool, unsigned msg_type, pj_uint32_t magic, const pj_uint8_t tsx_id[12], pj_stun_msg **p_msg); /** * Clone a STUN message with all of its attributes. * * @param pool Pool to allocate memory for the new message. * @param msg The message to be cloned. * * @return The duplicate message. */ PJ_DECL(pj_stun_msg*) pj_stun_msg_clone(pj_pool_t *pool, const pj_stun_msg *msg); /** * Create STUN response message. * * @param pool Pool to create the mesage. * @param req_msg The request message. * @param err_code STUN error code. If this value is not zero, * then error response will be created, otherwise * successful response will be created. * @param err_msg Optional error message to explain err_code. * If this value is NULL and err_code is not zero, * the error string will be taken from the default * STUN error message. * @param p_response Pointer to receive the response. * * @return PJ_SUCCESS on success, or the appropriate error. */ PJ_DECL(pj_status_t) pj_stun_msg_create_response(pj_pool_t *pool, const pj_stun_msg *req_msg, unsigned err_code, const pj_str_t *err_msg, pj_stun_msg **p_response); /** * Add STUN attribute to STUN message. * * @param msg The STUN message. * @param attr The STUN attribute to be added to the message. * * @return PJ_SUCCESS on success, or PJ_ETOOMANY if there are * already too many attributes in the message. */ PJ_DECL(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg, pj_stun_attr_hdr *attr); /** * Print the STUN message structure to a packet buffer, ready to be * sent to remote destination. This function will take care about * calculating the MESSAGE-INTEGRITY digest as well as FINGERPRINT * value, if these attributes are present in the message. * * If application wants to apply credential to the message, it MUST * include a blank MESSAGE-INTEGRITY attribute in the message as the * last attribute or the attribute before FINGERPRINT. This function will * calculate the HMAC digest from the message using the supplied key in * the parameter. The key should be set to the password if short term * credential is used, or calculated from the MD5 hash of the realm, * username, and password using #pj_stun_create_key() if long term * credential is used. * * If FINGERPRINT attribute is present, this function will calculate * the FINGERPRINT CRC attribute for the message. The FINGERPRINT MUST * be added as the last attribute of the message. * * @param msg The STUN message to be printed. Upon return, * some fields in the header (such as message * length) will be updated. * @param pkt_buf The buffer to be filled with the packet. * @param buf_size Size of the buffer. * @param options Options, which currently must be zero. * @param key Authentication key to calculate MESSAGE-INTEGRITY * value. Application can create this key by using * #pj_stun_create_key() function. * @param p_msg_len Upon return, it will be filed with the size of * the packet in bytes, or negative value on error. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, pj_uint8_t *pkt_buf, pj_size_t buf_size, unsigned options, const pj_str_t *key, pj_size_t *p_msg_len); /** * Check that the PDU is potentially a valid STUN message. This function * is useful when application needs to multiplex STUN packets with other * application traffic. When this function returns PJ_SUCCESS, there is a * big chance that the packet is a STUN packet. * * Note that we cannot be sure that the PDU is a really valid STUN message * until we actually parse the PDU. * * @param pdu The packet buffer. * @param pdu_len The length of the packet buffer. * @param options Additional options to be applied in the checking, * which can be taken from pj_stun_decode_options. One * of the useful option is PJ_STUN_IS_DATAGRAM which * means that the pdu represents a whole STUN packet. * * @return PJ_SUCCESS if the PDU is a potentially valid STUN * message. */ PJ_DECL(pj_status_t) pj_stun_msg_check(const pj_uint8_t *pdu, pj_size_t pdu_len, unsigned options); /** * Decode incoming packet into STUN message. * * @param pool Pool to allocate the message. * @param pdu The incoming packet to be parsed. * @param pdu_len The length of the incoming packet. * @param options Parsing flags, according to pj_stun_decode_options. * @param p_msg Pointer to receive the parsed message. * @param p_parsed_len Optional pointer to receive how many bytes have * been parsed for the STUN message. This is useful * when the packet is received over stream oriented * transport. * @param p_response Optional pointer to receive an instance of response * message, if one can be created. If the packet being * decoded is a request message, and it contains error, * and a response can be created, then the STUN * response message will be returned on this argument. * * @return PJ_SUCCESS if a STUN message has been successfully * decoded. */ PJ_DECL(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, const pj_uint8_t *pdu, pj_size_t pdu_len, unsigned options, pj_stun_msg **p_msg, pj_size_t *p_parsed_len, pj_stun_msg **p_response); /** * Dump STUN message to a printable string output. * * @param msg The STUN message * @param buffer Buffer where the printable string output will * be printed on. * @param length Specify the maximum length of the buffer. * @param printed_len Optional pointer, which on output will be filled * up with the actual length of the output string. * * @return The message string output. */ #if PJ_LOG_MAX_LEVEL > 0 PJ_DECL(char*) pj_stun_msg_dump(const pj_stun_msg *msg, char *buffer, unsigned length, unsigned *printed_len); #else # define pj_stun_msg_dump(msg, buf, length, printed_len) "" #endif /** * Find STUN attribute in the STUN message, starting from the specified * index. * * @param msg The STUN message. * @param attr_type The attribute type to be found, from pj_stun_attr_type. * @param start_index The start index of the attribute in the message. * Specify zero to start searching from the first * attribute. * * @return The attribute instance, or NULL if it cannot be * found. */ PJ_DECL(pj_stun_attr_hdr*) pj_stun_msg_find_attr(const pj_stun_msg *msg, int attr_type, unsigned start_index); /** * Clone a STUN attribute. * * @param pool Pool to allocate memory. * @param attr Attribute to clone. * * @return Duplicate attribute. */ PJ_DECL(pj_stun_attr_hdr*) pj_stun_attr_clone(pj_pool_t *pool, const pj_stun_attr_hdr *attr); /** * Initialize generic STUN IP address attribute. The \a addr_len and * \a addr parameters specify whether the address is IPv4 or IPv4 * address. * * @param attr The socket address attribute to initialize. * @param attr_type Attribute type, from #pj_stun_attr_type. * @param xor_ed If non-zero, the port and address will be XOR-ed * with magic, to make the XOR-MAPPED-ADDRESS attribute. * @param addr A pj_sockaddr_in or pj_sockaddr_in6 structure. * @param addr_len Length of \a addr parameter. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_sockaddr_attr_init(pj_stun_sockaddr_attr *attr, int attr_type, pj_bool_t xor_ed, const pj_sockaddr_t *addr, unsigned addr_len); /** * Create a generic STUN IP address attribute. The \a addr_len and * \a addr parameters specify whether the address is IPv4 or IPv4 * address. * * @param pool The pool to allocate memory from. * @param attr_type Attribute type, from #pj_stun_attr_type. * @param xor_ed If non-zero, the port and address will be XOR-ed * with magic, to make the XOR-MAPPED-ADDRESS attribute. * @param addr A pj_sockaddr_in or pj_sockaddr_in6 structure. * @param addr_len Length of \a addr parameter. * @param p_attr Pointer to receive the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_sockaddr_attr_create(pj_pool_t *pool, int attr_type, pj_bool_t xor_ed, const pj_sockaddr_t *addr, unsigned addr_len, pj_stun_sockaddr_attr **p_attr); /** * Create and add generic STUN IP address attribute to a STUN message. * The \a addr_len and \a addr parameters specify whether the address is * IPv4 or IPv4 address. * * @param pool The pool to allocate memory from. * @param msg The STUN message. * @param attr_type Attribute type, from #pj_stun_attr_type. * @param xor_ed If non-zero, the port and address will be XOR-ed * with magic, to make the XOR-MAPPED-ADDRESS attribute. * @param addr A pj_sockaddr_in or pj_sockaddr_in6 structure. * @param addr_len Length of \a addr parameter. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_msg_add_sockaddr_attr(pj_pool_t *pool, pj_stun_msg *msg, int attr_type, pj_bool_t xor_ed, const pj_sockaddr_t *addr, unsigned addr_len); /** * Initialize a STUN generic string attribute. * * @param attr The string attribute to be initialized. * @param pool Pool to duplicate the value into the attribute, * if value is not NULL or empty. * @param attr_type Attribute type, from #pj_stun_attr_type. * @param value The string value to be assigned to the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_string_attr_init(pj_stun_string_attr *attr, pj_pool_t *pool, int attr_type, const pj_str_t *value); /** * Create a STUN generic string attribute. * * @param pool The pool to allocate memory from. * @param attr_type Attribute type, from #pj_stun_attr_type. * @param value The string value to be assigned to the attribute. * @param p_attr Pointer to receive the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_string_attr_create(pj_pool_t *pool, int attr_type, const pj_str_t *value, pj_stun_string_attr **p_attr); /** * Create and add STUN generic string attribute to the message. * * @param pool The pool to allocate memory from. * @param msg The STUN message. * @param attr_type Attribute type, from #pj_stun_attr_type. * @param value The string value to be assigned to the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_msg_add_string_attr(pj_pool_t *pool, pj_stun_msg *msg, int attr_type, const pj_str_t *value); /** * Create a STUN generic 32bit value attribute. * * @param pool The pool to allocate memory from. * @param attr_type Attribute type, from #pj_stun_attr_type. * @param value The 32bit value to be assigned to the attribute. * @param p_attr Pointer to receive the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_uint_attr_create(pj_pool_t *pool, int attr_type, pj_uint32_t value, pj_stun_uint_attr **p_attr); /** * Create and add STUN generic 32bit value attribute to the message. * * @param pool The pool to allocate memory from. * @param msg The STUN message * @param attr_type Attribute type, from #pj_stun_attr_type. * @param value The 32bit value to be assigned to the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_msg_add_uint_attr(pj_pool_t *pool, pj_stun_msg *msg, int attr_type, pj_uint32_t value); /** * Create a STUN generic 64bit value attribute. * * @param pool Pool to allocate memory from. * @param attr_type Attribute type, from #pj_stun_attr_type. * @param value Optional value to be assigned. * @param p_attr Pointer to receive the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_uint64_attr_create(pj_pool_t *pool, int attr_type, const pj_timestamp *value, pj_stun_uint64_attr **p_attr); /** * Create and add STUN generic 64bit value attribute to the message. * * @param pool The pool to allocate memory from. * @param msg The STUN message * @param attr_type Attribute type, from #pj_stun_attr_type. * @param value The 64bit value to be assigned to the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_msg_add_uint64_attr(pj_pool_t *pool, pj_stun_msg *msg, int attr_type, const pj_timestamp *value); /** * Create a STUN MESSAGE-INTEGRITY attribute. * * @param pool The pool to allocate memory from. * @param p_attr Pointer to receive the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_msgint_attr_create(pj_pool_t *pool, pj_stun_msgint_attr **p_attr); /** * Create and add STUN MESSAGE-INTEGRITY attribute. * * @param pool The pool to allocate memory from. * @param msg The STUN message * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_msg_add_msgint_attr(pj_pool_t *pool, pj_stun_msg *msg); /** * Create a STUN ERROR-CODE attribute. * * @param pool The pool to allocate memory from. * @param err_code STUN error code. * @param err_reason Optional STUN error reason. If NULL is given, the * standard error reason will be given. * @param p_attr Pointer to receive the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_errcode_attr_create(pj_pool_t *pool, int err_code, const pj_str_t *err_reason, pj_stun_errcode_attr **p_attr); /** * Create and add STUN ERROR-CODE attribute to the message. * * @param pool The pool to allocate memory from. * @param msg The STUN mesage. * @param err_code STUN error code. * @param err_reason Optional STUN error reason. If NULL is given, the * standard error reason will be given. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_msg_add_errcode_attr(pj_pool_t *pool, pj_stun_msg *msg, int err_code, const pj_str_t *err_reason); /** * Create instance of STUN UNKNOWN-ATTRIBUTES attribute and copy the * unknown attribute array to the attribute. * * @param pool The pool to allocate memory from. * @param attr_cnt Number of attributes in the array (can be zero). * @param attr Optional array of attributes. * @param p_attr Pointer to receive the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_unknown_attr_create(pj_pool_t *pool, unsigned attr_cnt, const pj_uint16_t attr[], pj_stun_unknown_attr **p_attr); /** * Create and add STUN UNKNOWN-ATTRIBUTES attribute to the message. * * @param pool The pool to allocate memory from. * @param msg The STUN message. * @param attr_cnt Number of attributes in the array (can be zero). * @param attr Optional array of attribute types. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_msg_add_unknown_attr(pj_pool_t *pool, pj_stun_msg *msg, unsigned attr_cnt, const pj_uint16_t attr[]); /** * Initialize STUN binary attribute. * * @param attr The attribute to be initialized. * @param pool Pool to copy data, if the data and length are set. * @param attr_type The attribute type, from #pj_stun_attr_type. * @param data Data to be coped to the attribute, or NULL * if no data to be copied now. * @param length Length of data, or zero if no data is to be * copied now. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_binary_attr_init(pj_stun_binary_attr *attr, pj_pool_t *pool, int attr_type, const pj_uint8_t *data, unsigned length); /** * Create STUN binary attribute. * * @param pool The pool to allocate memory from. * @param attr_type The attribute type, from #pj_stun_attr_type. * @param data Data to be coped to the attribute, or NULL * if no data to be copied now. * @param length Length of data, or zero if no data is to be * copied now. * @param p_attr Pointer to receive the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_binary_attr_create(pj_pool_t *pool, int attr_type, const pj_uint8_t *data, unsigned length, pj_stun_binary_attr **p_attr); /** * Create STUN binary attribute and add the attribute to the message. * * @param pool The pool to allocate memory from. * @param msg The STUN message. * @param attr_type The attribute type, from #pj_stun_attr_type. * @param data Data to be coped to the attribute, or NULL * if no data to be copied now. * @param length Length of data, or zero if no data is to be * copied now. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_msg_add_binary_attr(pj_pool_t *pool, pj_stun_msg *msg, int attr_type, const pj_uint8_t *data, unsigned length); /** * Create STUN empty attribute. * * @param pool The pool to allocate memory from. * @param attr_type The attribute type, from #pj_stun_attr_type. * @param p_attr Pointer to receive the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_empty_attr_create(pj_pool_t *pool, int attr_type, pj_stun_empty_attr **p_attr); /** * Create STUN empty attribute and add the attribute to the message. * * @param pool The pool to allocate memory from. * @param msg The STUN message. * @param attr_type The attribute type, from #pj_stun_attr_type. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_msg_add_empty_attr(pj_pool_t *pool, pj_stun_msg *msg, int attr_type); /** * @} */ PJ_END_DECL #endif /* __PJNATH_STUN_MSG_H__ */ ================================================ FILE: deps/pjsip/pjnath/include/pjnath/stun_session.h ================================================ /* $Id: stun_session.h 4360 2013-02-21 11:26:35Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_STUN_SESSION_H__ #define __PJNATH_STUN_SESSION_H__ /** * @file stun_session.h * @brief STUN session management for client/server. */ #include #include #include #include #include #include #include PJ_BEGIN_DECL /* **************************************************************************/ /** * @addtogroup PJNATH_STUN_SESSION * @{ * * This is is a transport-independent object to manage a client or server * STUN session. It has the following features: * * - transport independent:\n * the object does not have it's own socket, but rather it provides * functions and callbacks to send and receive packets. This way the * object can be used by different transport types (e.g. UDP, TCP, * TLS, etc.) as well as better integration to application which * already has its own means to send and receive packets. * * - authentication management:\n * the object manages STUN authentication throughout the lifetime of * the session. For client sessions, once it's given a credential to * authenticate itself with the server, the object will automatically * add authentication info (the MESSAGE-INTEGRITY) to the request as * well as authenticate the response. It will also handle long-term * authentication challenges, including handling of nonce expiration, * and retry the request automatically. For server sessions, it can * be configured to authenticate incoming requests automatically. * * - static or dynamic credential:\n * application may specify static or dynamic credential to be used by * the STUN session. Static credential means a static combination of * username and password (and these cannot change during the session * duration), while dynamic credential provides callback to ask the * application about which username/password to use everytime * authentication is about to be performed. * * - client transaction management:\n * outgoing requests may be sent with a STUN transaction for reliability, * and the object will manage the transaction internally (including * performing retransmissions). Application will be notified about the * result of the request when the response arrives (or the transaction * times out). When the request is challenged with authentication, the * object will retry the request with new authentication info, and * application will be notified about the final result of this request. * * - server transaction management:\n * application may ask response to incoming requests to be cached by * the object, and in this case the object will check for cached * response everytime request is received. The cached response will be * deleted once a timer expires. * * \section using_stun_sess_sec Using the STUN session * * The following steps describes how to use the STUN session: * * - create the object configuration:\n * The #pj_stun_config contains the configuration to create the STUN * session, such as the timer heap to register internal timers and * various STUN timeout values. You can initialize this structure by * calling #pj_stun_config_init() * * - create the STUN session:\n * by calling #pj_stun_session_create(). Among other things, this * function requires the instance of #pj_stun_config and also * #pj_stun_session_cb structure which stores callbacks to send * outgoing packets as well as to notify application about incoming * STUN requests, responses, and indicates and other events. * * - configure credential:\n * if authentication is required for the session, configure the * credential with #pj_stun_session_set_credential() * * - configuring other settings:\n * several APIs are provided to configure the behavior of the STUN * session (for example, to set the SOFTWARE attribute value, controls * the logging behavior, fine tune the mutex locking, etc.). Please see * the API reference for more info. * * - creating outgoing STUN requests or indications:\n * create the STUN message by using #pj_stun_session_create_req() or * #pj_stun_session_create_ind(). This will create a transmit data * buffer containing a blank STUN request or indication. You will then * typically need to add STUN attributes that are relevant to the * request or indication, but note that some default attributes will * be added by the session later when the message is sent (such as * SOFTWARE attribute and attributes related to authentication). * The message is now ready to be sent. * * - sending outgoing message:\n * use #pj_stun_session_send_msg() to send outgoing STUN messages (this * includes STUN requests, indications, and responses). The function has * options whether to retransmit the request (for non reliable transports) * or to cache the response if we're sending response. This function in * turn will call the \a on_send_msg() callback of #pj_stun_session_cb * to request the application to send the packet. * * - handling incoming packet:\n * call #pj_stun_session_on_rx_pkt() everytime the application receives * a STUN packet. This function will decode the packet and process the * packet according to the message, and normally this will cause one * of the callback in the #pj_stun_session_cb to be called to notify * the application about the event. * * - handling incoming requests:\n * incoming requests are notified to application in the \a on_rx_request * callback of the #pj_stun_session_cb. If authentication is enabled in * the session, the application will only receive this callback after * the incoming request has been authenticated (if the authentication * fails, the session would respond automatically with 401 error and * the callback will not be called). Application now must create and * send response for this request. * * - creating and sending response:\n * create the STUN response with #pj_stun_session_create_res(). This will * create a transmit data buffer containing a blank STUN response. You * will then typically need to add STUN attributes that are relevant to * the response, but note that some default attributes will * be added by the session later when the message is sent (such as * SOFTWARE attribute and attributes related to authentication). * The message is now ready to be sent. Use #pj_stun_session_send_msg() * (as explained above) to send the response. * * - convenient way to send response:\n * the #pj_stun_session_respond() is provided as a convenient way to * create and send simple STUN responses, such as error responses. * * - destroying the session:\n * once the session is done, use #pj_stun_session_destroy() to destroy * the session. */ /** Forward declaration for pj_stun_tx_data */ typedef struct pj_stun_tx_data pj_stun_tx_data; /** Forward declaration for pj_stun_rx_data */ typedef struct pj_stun_rx_data pj_stun_rx_data; /** Forward declaration for pj_stun_session */ typedef struct pj_stun_session pj_stun_session; /** * This is the callback to be registered to pj_stun_session, to send * outgoing message and to receive various notifications from the STUN * session. */ typedef struct pj_stun_session_cb { /** * Callback to be called by the STUN session to send outgoing message. * * @param sess The STUN session. * @param token The token associated with this outgoing message * and was set by the application. This token was * set by application in pj_stun_session_send_msg() * for outgoing messages that are initiated by the * application, or in pj_stun_session_on_rx_pkt() * if this message is a response that was internally * generated by the STUN session (for example, an * 401/Unauthorized response). Application may use * this facility for any purposes. * @param pkt Packet to be sent. * @param pkt_size Size of the packet to be sent. * @param dst_addr The destination address. * @param addr_len Length of destination address. * * @return The callback should return the status of the * packet sending. */ pj_status_t (*on_send_msg)(pj_stun_session *sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len); /** * Callback to be called on incoming STUN request message. This function * is called when application calls pj_stun_session_on_rx_pkt() and when * the STUN session has detected that the incoming STUN message is a * STUN request message. In the * callback processing, application MUST create a response by calling * pj_stun_session_create_response() function and send the response * with pj_stun_session_send_msg() function, before returning from * the callback. * * @param sess The STUN session. * @param pkt Pointer to the original STUN packet. * @param pkt_len Length of the STUN packet. * @param rdata Data containing incoming request message. * @param token The token that was set by the application when * calling pj_stun_session_on_rx_pkt() function. * @param src_addr Source address of the packet. * @param src_addr_len Length of the source address. * * @return The return value of this callback will be * returned back to pj_stun_session_on_rx_pkt() * function. */ pj_status_t (*on_rx_request)(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_rx_data *rdata, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len); /** * Callback to be called when response is received or the transaction * has timed out. This callback is called either when application calls * pj_stun_session_on_rx_pkt() with the packet containing a STUN * response for the client transaction, or when the internal timer of * the STUN client transaction has timed-out before a STUN response is * received. * * @param sess The STUN session. * @param status Status of the request. If the value if not * PJ_SUCCESS, the transaction has timed-out * or other error has occurred, and the response * argument may be NULL. * Note that when the status is not success, the * response may contain non-NULL value if the * response contains STUN ERROR-CODE attribute. * @param token The token that was set by the application when * calling pj_stun_session_send_msg() function. * Please not that this token IS NOT the token * that was given in pj_stun_session_on_rx_pkt(). * @param tdata The original STUN request. * @param response The response message, on successful transaction, * or otherwise MAY BE NULL if status is not success. * Note that when the status is not success, this * argument may contain non-NULL value if the * response contains STUN ERROR-CODE attribute. * @param src_addr The source address where the response was * received, or NULL if the response is NULL. * @param src_addr_len The length of the source address. */ void (*on_request_complete)(pj_stun_session *sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len); /** * Callback to be called on incoming STUN request message. This function * is called when application calls pj_stun_session_on_rx_pkt() and when * the STUN session has detected that the incoming STUN message is a * STUN indication message. * * @param sess The STUN session. * @param pkt Pointer to the original STUN packet. * @param pkt_len Length of the STUN packet. * @param msg The parsed STUN indication. * @param token The token that was set by the application when * calling pj_stun_session_on_rx_pkt() function. * @param src_addr Source address of the packet. * @param src_addr_len Length of the source address. * * @return The return value of this callback will be * returned back to pj_stun_session_on_rx_pkt() * function. */ pj_status_t (*on_rx_indication)(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_msg *msg, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len); } pj_stun_session_cb; /** * This structure describes incoming request message. */ struct pj_stun_rx_data { /** * The parsed request message. */ pj_stun_msg *msg; /** * Credential information that is found and used to authenticate * incoming request. Application may use this information when * generating authentication for the outgoing response. */ pj_stun_req_cred_info info; }; /** * This structure describe the outgoing STUN transmit data to carry the * message to be sent. */ struct pj_stun_tx_data { /** PJLIB list interface */ PJ_DECL_LIST_MEMBER(struct pj_stun_tx_data); pj_pool_t *pool; /**< Pool. */ pj_stun_session *sess; /**< The STUN session. */ pj_stun_msg *msg; /**< The STUN message. */ void *token; /**< The token. */ pj_stun_client_tsx *client_tsx; /**< Client STUN transaction. */ pj_bool_t retransmit; /**< Retransmit request? */ pj_uint32_t msg_magic; /**< Message magic. */ pj_uint8_t msg_key[12]; /**< Message/transaction key. */ pj_stun_req_cred_info auth_info; /**< Credential info */ void *pkt; /**< The STUN packet. */ unsigned max_len; /**< Length of packet buffer. */ pj_size_t pkt_size; /**< The actual length of STUN pkt. */ unsigned addr_len; /**< Length of destination address. */ const pj_sockaddr_t *dst_addr; /**< Destination address. */ pj_timer_entry res_timer; /**< Response cache timer. */ }; /** * These are the flags to control the message logging in the STUN session. */ typedef enum pj_stun_sess_msg_log_flag { PJ_STUN_SESS_LOG_TX_REQ=1, /**< Log outgoing STUN requests. */ PJ_STUN_SESS_LOG_TX_RES=2, /**< Log outgoing STUN responses. */ PJ_STUN_SESS_LOG_TX_IND=4, /**< Log outgoing STUN indications. */ PJ_STUN_SESS_LOG_RX_REQ=8, /**< Log incoming STUN requests. */ PJ_STUN_SESS_LOG_RX_RES=16, /**< Log incoming STUN responses */ PJ_STUN_SESS_LOG_RX_IND=32 /**< Log incoming STUN indications */ } pj_stun_sess_msg_log_flag; /** * Create a STUN session. * * @param cfg The STUN endpoint, to be used to register timers etc. * @param name Optional name to be associated with this instance. The * name will be used for example for logging purpose. * @param cb Session callback. * @param fingerprint Enable message fingerprint for outgoing messages. * @param grp_lock Optional group lock to be used by this session. * If NULL, the session will create one itself. * @param p_sess Pointer to receive STUN session instance. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_session_create(pj_stun_config *cfg, const char *name, const pj_stun_session_cb *cb, pj_bool_t fingerprint, pj_grp_lock_t *grp_lock, pj_stun_session **p_sess); /** * Destroy the STUN session and all objects created in the context of * this session. * * @param sess The STUN session instance. * * @return PJ_SUCCESS on success, or the appropriate error code. * This function will return PJ_EPENDING if the operation * cannot be performed immediately because callbacks are * being called; in this case the session will be destroyed * as soon as the last callback returns. */ PJ_DECL(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess); /** * Associated an arbitrary data with this STUN session. The user data may * be retrieved later with pj_stun_session_get_user_data() function. * * @param sess The STUN session instance. * @param user_data The user data. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_session_set_user_data(pj_stun_session *sess, void *user_data); /** * Retrieve the user data previously associated to this STUN session with * pj_stun_session_set_user_data(). * * @param sess The STUN session instance. * * @return The user data associated with this STUN session. */ PJ_DECL(void*) pj_stun_session_get_user_data(pj_stun_session *sess); /** * Get the group lock for this STUN session. * * @param sess The STUN session instance. * * @return The group lock. */ PJ_DECL(pj_grp_lock_t *) pj_stun_session_get_grp_lock(pj_stun_session *sess); /** * Set SOFTWARE name to be included in all requests and responses. * * @param sess The STUN session instance. * @param sw Software name string. If this argument is NULL or * empty, the session will not include SOFTWARE attribute * in STUN requests and responses. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_session_set_software_name(pj_stun_session *sess, const pj_str_t *sw); /** * Set credential to be used by this session. Once credential is set, all * outgoing messages will include MESSAGE-INTEGRITY, and all incoming * message will be authenticated against this credential. * * To disable authentication after it has been set, call this function * again with NULL as the argument. * * @param sess The STUN session instance. * @param auth_type Type of authentication. * @param cred The credential to be used by this session. If NULL * is specified, authentication will be disabled. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess, pj_stun_auth_type auth_type, const pj_stun_auth_cred *cred); /** * Configure message logging. By default all flags are enabled. * * @param sess The STUN session instance. * @param flags Bitmask combination of #pj_stun_sess_msg_log_flag */ PJ_DECL(void) pj_stun_session_set_log(pj_stun_session *sess, unsigned flags); /** * Configure whether the STUN session should utilize FINGERPRINT in * outgoing messages. * * @param sess The STUN session instance. * @param use Boolean for the setting. * * @return The previous configured value of FINGERPRINT * utilization of the sessoin. */ PJ_DECL(pj_bool_t) pj_stun_session_use_fingerprint(pj_stun_session *sess, pj_bool_t use); /** * Create a STUN request message. After the message has been successfully * created, application can send the message by calling * pj_stun_session_send_msg(). * * @param sess The STUN session instance. * @param msg_type The STUN request message type, from pj_stun_method_e or * from pj_stun_msg_type. * @param magic STUN magic, use PJ_STUN_MAGIC. * @param tsx_id Optional transaction ID. * @param p_tdata Pointer to receive STUN transmit data instance containing * the request. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, int msg_type, pj_uint32_t magic, const pj_uint8_t tsx_id[12], pj_stun_tx_data **p_tdata); /** * Create a STUN Indication message. After the message has been successfully * created, application can send the message by calling * pj_stun_session_send_msg(). * * @param sess The STUN session instance. * @param msg_type The STUN request message type, from pj_stun_method_e or * from pj_stun_msg_type. This function will add the * indication bit as necessary. * @param p_tdata Pointer to receive STUN transmit data instance containing * the message. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess, int msg_type, pj_stun_tx_data **p_tdata); /** * Create a STUN response message. After the message has been * successfully created, application can send the message by calling * pj_stun_session_send_msg(). Alternatively application may use * pj_stun_session_respond() to create and send response in one function * call. * * @param sess The STUN session instance. * @param rdata The STUN request where the response is to be created. * @param err_code Error code to be set in the response, if error response * is to be created, according to pj_stun_status enumeration. * This argument MUST be zero if successful response is * to be created. * @param err_msg Optional pointer for the error message string, when * creating error response. If the value is NULL and the * \a err_code is non-zero, then default error message will * be used. * @param p_tdata Pointer to receive the response message created. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_session_create_res(pj_stun_session *sess, const pj_stun_rx_data *rdata, unsigned err_code, const pj_str_t *err_msg, pj_stun_tx_data **p_tdata); /** * Send STUN message to the specified destination. This function will encode * the pj_stun_msg instance to a packet buffer, and add credential or * fingerprint if necessary. If the message is a request, the session will * also create and manage a STUN client transaction to be used to manage the * retransmission of the request. After the message has been encoded and * transaction is setup, the \a on_send_msg() callback of pj_stun_session_cb * (which is registered when the STUN session is created) will be called * to actually send the message to the wire. * * @param sess The STUN session instance. * @param token Optional token which will be given back to application in * \a on_send_msg() callback and \a on_request_complete() * callback, if the message is a STUN request message. * Internally this function will put the token in the * \a token field of pj_stun_tx_data, hence it will * overwrite any value that the application puts there. * @param cache_res If the message is a response message for an incoming * request, specify PJ_TRUE to instruct the STUN session * to cache this response for subsequent incoming request * retransmission. Otherwise this parameter will be ignored * for non-response message. * @param retransmit If the message is a request message, specify whether the * request should be retransmitted. Normally application will * specify TRUE if the underlying transport is UDP and FALSE * if the underlying transport is TCP or TLS. * @param dst_addr The destination socket address. * @param addr_len Length of destination address. * @param tdata The STUN transmit data containing the STUN message to * be sent. * * @return PJ_SUCCESS on success, or the appropriate error code. * This function will return PJNATH_ESTUNDESTROYED if * application has destroyed the session in * \a on_send_msg() callback. */ PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess, void *token, pj_bool_t cache_res, pj_bool_t retransmit, const pj_sockaddr_t *dst_addr, unsigned addr_len, pj_stun_tx_data *tdata); /** * This is a utility function to create and send response for an incoming * STUN request. Internally this function calls pj_stun_session_create_res() * and pj_stun_session_send_msg(). It is provided here as a matter of * convenience. * * @param sess The STUN session instance. * @param rdata The STUN request message to be responded. * @param code Error code to be set in the response, if error response * is to be created, according to pj_stun_status enumeration. * This argument MUST be zero if successful response is * to be created. * @param err_msg Optional pointer for the error message string, when * creating error response. If the value is NULL and the * \a err_code is non-zero, then default error message will * be used. * @param token Optional token which will be given back to application in * \a on_send_msg() callback and \a on_request_complete() * callback, if the message is a STUN request message. * Internally this function will put the token in the * \a token field of pj_stun_tx_data, hence it will * overwrite any value that the application puts there. * @param cache Specify whether session should cache this response for * future request retransmission. If TRUE, subsequent request * retransmission will be handled by the session and it * will not call request callback. * @param dst_addr Destination address of the response (or equal to the * source address of the original request). * @param addr_len Address length. * * @return PJ_SUCCESS on success, or the appropriate error code. * This function will return PJNATH_ESTUNDESTROYED if * application has destroyed the session in * \a on_send_msg() callback. */ PJ_DECL(pj_status_t) pj_stun_session_respond(pj_stun_session *sess, const pj_stun_rx_data *rdata, unsigned code, const char *err_msg, void *token, pj_bool_t cache, const pj_sockaddr_t *dst_addr, unsigned addr_len); /** * Cancel outgoing STUN transaction. This operation is only valid for outgoing * STUN request, to cease retransmission of the request and destroy the * STUN client transaction that is used to send the request. * * @param sess The STUN session instance. * @param tdata The request message previously sent. * @param notify Specify whether \a on_request_complete() callback should * be called. * @param status If \a on_request_complete() callback is to be called, * specify the error status to be given when calling the * callback. This error status MUST NOT be PJ_SUCCESS. * * @return PJ_SUCCESS if transaction is successfully cancelled. * This function will return PJNATH_ESTUNDESTROYED if * application has destroyed the session in * \a on_request_complete() callback. */ PJ_DECL(pj_status_t) pj_stun_session_cancel_req(pj_stun_session *sess, pj_stun_tx_data *tdata, pj_bool_t notify, pj_status_t status); /** * Explicitly request retransmission of the request. Normally application * doesn't need to do this, but this functionality is needed by ICE to * speed up connectivity check completion. * * @param sess The STUN session instance. * @param tdata The request message previously sent. * @param mod_count Boolean flag to indicate whether transmission count * needs to be incremented. * * @return PJ_SUCCESS on success, or the appropriate error. * This function will return PJNATH_ESTUNDESTROYED if * application has destroyed the session in \a on_send_msg() * callback. */ PJ_DECL(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess, pj_stun_tx_data *tdata, pj_bool_t mod_count); /** * Application must call this function to notify the STUN session about * the arrival of STUN packet. The STUN packet MUST have been checked * first with #pj_stun_msg_check() to verify that this is indeed a valid * STUN packet. * * The STUN session will decode the packet into pj_stun_msg, and process * the message accordingly. If the message is a response, it will search * through the outstanding STUN client transactions for a matching * transaction ID and hand over the response to the transaction. * * On successful message processing, application will be notified about * the message via one of the pj_stun_session_cb callback. * * @param sess The STUN session instance. * @param packet The packet containing STUN message. * @param pkt_size Size of the packet. * @param options Options, from #pj_stun_decode_options. * @param parsed_len Optional pointer to receive the size of the parsed * STUN message (useful if packet is received via a * stream oriented protocol). * @param token Optional token which will be given back to application * in the \a on_rx_request(), \a on_rx_indication() and * \a on_send_msg() callbacks. The token can be used to * associate processing or incoming request or indication * with some context. * @param src_addr The source address of the packet, which will also * be given back to application callbacks, along with * source address length. * @param src_addr_len Length of the source address. * * @return PJ_SUCCESS on success, or the appropriate error code. * This function will return PJNATH_ESTUNDESTROYED if * application has destroyed the session in one of the * callback. */ PJ_DECL(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, const void *packet, pj_size_t pkt_size, unsigned options, void *token, pj_size_t *parsed_len, const pj_sockaddr_t *src_addr, unsigned src_addr_len); /** * Destroy the transmit data. Call this function only when tdata has been * created but application doesn't want to send the message (perhaps * because of other error). * * @param sess The STUN session. * @param tdata The transmit data. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(void) pj_stun_msg_destroy_tdata(pj_stun_session *sess, pj_stun_tx_data *tdata); /** * @} */ PJ_END_DECL #endif /* __PJNATH_STUN_SESSION_H__ */ ================================================ FILE: deps/pjsip/pjnath/include/pjnath/stun_sock.h ================================================ /* $Id: stun_sock.h 4360 2013-02-21 11:26:35Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_STUN_SOCK_H__ #define __PJNATH_STUN_SOCK_H__ /** * @file stun_sock.h * @brief STUN aware socket transport */ #include #include #include #include #include #include PJ_BEGIN_DECL /** * @addtogroup PJNATH_STUN_SOCK * @{ * * The STUN transport provides asynchronous UDP like socket transport * with the additional STUN capability. It has the following features: * * - API to send and receive UDP packets * * - multiplex STUN and non-STUN incoming packets and distinguish between * STUN responses that belong to internal requests with application data * (the application data may be STUN packets as well) * * - DNS SRV resolution to the STUN server (if wanted), along with fallback * to DNS A resolution if SRV record is not found. * * - STUN keep-alive maintenance, and handle changes to the mapped address * (when the NAT binding changes) * */ /** * Opaque type to represent a STUN transport. */ typedef struct pj_stun_sock pj_stun_sock; /** * Types of operation being reported in \a on_status() callback of * pj_stun_sock_cb. Application may retrieve the string representation * of these constants with pj_stun_sock_op_name(). */ typedef enum pj_stun_sock_op { /** * Asynchronous DNS resolution. */ PJ_STUN_SOCK_DNS_OP = 1, /** * Initial STUN Binding request. */ PJ_STUN_SOCK_BINDING_OP, /** * Subsequent STUN Binding request for keeping the binding * alive. */ PJ_STUN_SOCK_KEEP_ALIVE_OP, /** * IP address change notification from the keep-alive operation. */ PJ_STUN_SOCK_MAPPED_ADDR_CHANGE } pj_stun_sock_op; /** * This structure contains callbacks that will be called by the STUN * transport to notify application about various events. */ typedef struct pj_stun_sock_cb { /** * Notification when incoming packet has been received. * * @param stun_sock The STUN transport. * @param data The packet. * @param data_len Length of the packet. * @param src_addr The source address of the packet. * @param addr_len The length of the source address. * * @return Application should normally return PJ_TRUE to let * the STUN transport continue its operation. However * it must return PJ_FALSE if it has destroyed the * STUN transport in this callback. */ pj_bool_t (*on_rx_data)(pj_stun_sock *stun_sock, void *pkt, unsigned pkt_len, const pj_sockaddr_t *src_addr, unsigned addr_len); /** * Notifification when asynchronous send operation has completed. * * @param stun_sock The STUN transport. * @param send_key The send operation key that was given in * #pj_stun_sock_sendto(). * @param sent If value is positive non-zero it indicates the * number of data sent. When the value is negative, * it contains the error code which can be retrieved * by negating the value (i.e. status=-sent). * * @return Application should normally return PJ_TRUE to let * the STUN transport continue its operation. However * it must return PJ_FALSE if it has destroyed the * STUN transport in this callback. */ pj_bool_t (*on_data_sent)(pj_stun_sock *stun_sock, pj_ioqueue_op_key_t *send_key, pj_ssize_t sent); /** * Notification when the status of the STUN transport has changed. This * callback may be called for the following conditions: * - the first time the publicly mapped address has been resolved from * the STUN server, this callback will be called with \a op argument * set to PJ_STUN_SOCK_BINDING_OP \a status argument set to * PJ_SUCCESS. * - anytime when the transport has detected that the publicly mapped * address has changed, this callback will be called with \a op * argument set to PJ_STUN_SOCK_KEEP_ALIVE_OP and \a status * argument set to PJ_SUCCESS. On this case and the case above, * application will get the resolved public address in the * #pj_stun_sock_info structure. * - for any terminal error (such as STUN time-out, DNS resolution * failure, or keep-alive failure), this callback will be called * with the \a status argument set to non-PJ_SUCCESS. * * @param stun_sock The STUN transport. * @param op The operation that triggers the callback. * @param status The status. * * @return Must return PJ_FALSE if it has destroyed the * STUN transport in this callback. Application should * normally destroy the socket and return PJ_FALSE * upon encountering terminal error, otherwise it * should return PJ_TRUE to let the STUN socket operation * continues. */ pj_bool_t (*on_status)(pj_stun_sock *stun_sock, pj_stun_sock_op op, pj_status_t status); } pj_stun_sock_cb; /** * This structure contains information about the STUN transport. Application * may query this information by calling #pj_stun_sock_get_info(). */ typedef struct pj_stun_sock_info { /** * The bound address of the socket. */ pj_sockaddr bound_addr; /** * IP address of the STUN server. */ pj_sockaddr srv_addr; /** * The publicly mapped address. It may contain zero address when the * mapped address has not been resolved. Application may query whether * this field contains valid address with pj_sockaddr_has_addr(). */ pj_sockaddr mapped_addr; /** * Number of interface address aliases. The interface address aliases * are list of all interface addresses in this host. */ unsigned alias_cnt; /** * Array of interface address aliases. */ pj_sockaddr aliases[PJ_ICE_ST_MAX_CAND]; } pj_stun_sock_info; /** * This describe the settings to be given to the STUN transport during its * creation. Application should initialize this structure by calling * #pj_stun_sock_cfg_default(). */ typedef struct pj_stun_sock_cfg { /** * The group lock to be used by the STUN socket. If NULL, the STUN socket * will create one internally. * * Default: NULL */ pj_grp_lock_t *grp_lock; /** * Packet buffer size. * * Default value is PJ_STUN_SOCK_PKT_LEN. */ unsigned max_pkt_size; /** * Specify the number of simultaneous asynchronous read operations to * be invoked to the ioqueue. Having more than one read operations will * increase performance on multiprocessor systems since the application * will be able to process more than one incoming packets simultaneously. * Default value is 1. */ unsigned async_cnt; /** * Specify the interface where the socket should be bound to. If the * address is zero, socket will be bound to INADDR_ANY. If the address * is non-zero, socket will be bound to this address only, and the * transport will have only one address alias (the \a alias_cnt field * in #pj_stun_sock_info structure. If the port is set to zero, the * socket will bind at any port (chosen by the OS). */ pj_sockaddr bound_addr; /** * Specify the port range for STUN socket binding, relative to the start * port number specified in \a bound_addr. Note that this setting is only * applicable when the start port number is non zero. * * Default value is zero. */ pj_uint16_t port_range; /** * Specify the STUN keep-alive duration, in seconds. The STUN transport * does keep-alive by sending STUN Binding request to the STUN server. * If this value is zero, the PJ_STUN_KEEP_ALIVE_SEC value will be used. * If the value is negative, it will disable STUN keep-alive. */ int ka_interval; /** * QoS traffic type to be set on this transport. When application wants * to apply QoS tagging to the transport, it's preferable to set this * field rather than \a qos_param fields since this is more portable. * * Default value is PJ_QOS_TYPE_BEST_EFFORT. */ pj_qos_type qos_type; /** * Set the low level QoS parameters to the transport. This is a lower * level operation than setting the \a qos_type field and may not be * supported on all platforms. * * By default all settings in this structure are disabled. */ pj_qos_params qos_params; /** * Specify if STUN socket should ignore any errors when setting the QoS * traffic type/parameters. * * Default: PJ_TRUE */ pj_bool_t qos_ignore_error; /** * Specify target value for socket receive buffer size. It will be * applied using setsockopt(). When it fails to set the specified size, * it will try with lower value until the highest possible is * successfully set. * * Default: 0 (OS default) */ unsigned so_rcvbuf_size; /** * Specify target value for socket send buffer size. It will be * applied using setsockopt(). When it fails to set the specified size, * it will try with lower value until the highest possible is * successfully set. * * Default: 0 (OS default) */ unsigned so_sndbuf_size; } pj_stun_sock_cfg; /** * Retrieve the name representing the specified operation. */ PJ_DECL(const char*) pj_stun_sock_op_name(pj_stun_sock_op op); /** * Initialize the STUN transport setting with its default values. * * @param cfg The STUN transport config. */ PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg); /** * Create the STUN transport using the specified configuration. Once * the STUN transport has been create, application should call * #pj_stun_sock_start() to start the transport. * * @param stun_cfg The STUN configuration which contains among other * things the ioqueue and timer heap instance for * the operation of this transport. * @param af Address family of socket. Currently pj_AF_INET() * and pj_AF_INET6() are supported. * @param name Optional name to be given to this transport to * assist debugging. * @param cb Callback to receive events/data from the transport. * @param cfg Optional transport settings. * @param user_data Arbitrary application data to be associated with * this transport. * @param p_sock Pointer to receive the created transport instance. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_stun_sock_create(pj_stun_config *stun_cfg, const char *name, int af, const pj_stun_sock_cb *cb, const pj_stun_sock_cfg *cfg, void *user_data, pj_stun_sock **p_sock); /** * Start the STUN transport. This will start the DNS SRV resolution for * the STUN server (if desired), and once the server is resolved, STUN * Binding request will be sent to resolve the publicly mapped address. * Once the initial STUN Binding response is received, the keep-alive * timer will be started. * * @param stun_sock The STUN transport instance. * @param domain The domain, hostname, or IP address of the TURN * server. When this parameter contains domain name, * the \a resolver parameter must be set to activate * DNS SRV resolution. * @param default_port The default STUN port number to use when DNS SRV * resolution is not used. If DNS SRV resolution is * used, the server port number will be set from the * DNS SRV records. The recommended value for this * parameter is PJ_STUN_PORT. * @param resolver If this parameter is not NULL, then the \a domain * parameter will be first resolved with DNS SRV and * then fallback to using DNS A/AAAA resolution when * DNS SRV resolution fails. If this parameter is * NULL, the \a domain parameter will be resolved as * hostname. * * @return PJ_SUCCESS if the operation has been successfully * queued, or the appropriate error code on failure. * When this function returns PJ_SUCCESS, the final * result of the allocation process will be notified * to application in \a on_state() callback. */ PJ_DECL(pj_status_t) pj_stun_sock_start(pj_stun_sock *stun_sock, const pj_str_t *domain, pj_uint16_t default_port, pj_dns_resolver *resolver); /** * Destroy the STUN transport. * * @param sock The STUN transport socket. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *sock); /** * Associate a user data with this STUN transport. The user data may then * be retrieved later with #pj_stun_sock_get_user_data(). * * @param stun_sock The STUN transport instance. * @param user_data Arbitrary data. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_stun_sock_set_user_data(pj_stun_sock *stun_sock, void *user_data); /** * Retrieve the previously assigned user data associated with this STUN * transport. * * @param stun_sock The STUN transport instance. * * @return The user/application data. */ PJ_DECL(void*) pj_stun_sock_get_user_data(pj_stun_sock *stun_sock); /** * Get the group lock for this STUN transport. * * @param stun_sock The STUN transport instance. * * @return The group lock. */ PJ_DECL(pj_grp_lock_t *) pj_stun_sock_get_grp_lock(pj_stun_sock *stun_sock); /** * Get the STUN transport info. The transport info contains, among other * things, the allocated relay address. * * @param stun_sock The STUN transport instance. * @param info Pointer to be filled with STUN transport info. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_stun_sock_get_info(pj_stun_sock *stun_sock, pj_stun_sock_info *info); /** * Send a data to the specified address. This function may complete * asynchronously and in this case \a on_data_sent() will be called. * * @param stun_sock The STUN transport instance. * @param send_key Optional send key for sending the packet down to * the ioqueue. This value will be given back to * \a on_data_sent() callback * @param pkt The data/packet to be sent to peer. * @param pkt_len Length of the data. * @param flag pj_ioqueue_sendto() flag. * @param dst_addr The remote address. * @param addr_len Length of the address. * * @return PJ_SUCCESS if data has been sent immediately, or * PJ_EPENDING if data cannot be sent immediately. In * this case the \a on_data_sent() callback will be * called when data is actually sent. Any other return * value indicates error condition. */ PJ_DECL(pj_status_t) pj_stun_sock_sendto(pj_stun_sock *stun_sock, pj_ioqueue_op_key_t *send_key, const void *pkt, unsigned pkt_len, unsigned flag, const pj_sockaddr_t *dst_addr, unsigned addr_len); /** * @} */ PJ_END_DECL #endif /* __PJNATH_STUN_SOCK_H__ */ ================================================ FILE: deps/pjsip/pjnath/include/pjnath/stun_transaction.h ================================================ /* $Id: stun_transaction.h 4360 2013-02-21 11:26:35Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_STUN_TRANSACTION_H__ #define __PJNATH_STUN_TRANSACTION_H__ /** * @file stun_transaction.h * @brief STUN transaction */ #include #include #include PJ_BEGIN_DECL /* **************************************************************************/ /** * @defgroup PJNATH_STUN_TRANSACTION STUN Client Transaction * @brief STUN client transaction * @ingroup PJNATH_STUN_BASE * @{ * The @ref PJNATH_STUN_TRANSACTION is used to manage outgoing STUN request, for example to retransmit the request and to notify application about the completion of the request. The @ref PJNATH_STUN_TRANSACTION does not use any networking operations, but instead application must supply the transaction with a callback to be used by the transaction to send outgoing requests. This way the STUN transaction is made more generic and can work with different types of networking codes in application. */ /** * Opaque declaration of STUN client transaction. */ typedef struct pj_stun_client_tsx pj_stun_client_tsx; /** * STUN client transaction callback. */ typedef struct pj_stun_tsx_cb { /** * This callback is called when the STUN transaction completed. * * @param tsx The STUN transaction. * @param status Status of the transaction. Status PJ_SUCCESS * means that the request has received a successful * response. * @param response The STUN response, which value may be NULL if * \a status is not PJ_SUCCESS. * @param src_addr The source address of the response, if response * is not NULL. * @param src_addr_len The length of the source address. */ void (*on_complete)(pj_stun_client_tsx *tsx, pj_status_t status, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len); /** * This callback is called by the STUN transaction when it wants to send * outgoing message. * * @param tsx The STUN transaction instance. * @param stun_pkt The STUN packet to be sent. * @param pkt_size Size of the STUN packet. * * @return If return value of the callback is not PJ_SUCCESS, * the transaction will fail. Application MUST return * PJNATH_ESTUNDESTROYED if it has destroyed the * transaction in this callback. */ pj_status_t (*on_send_msg)(pj_stun_client_tsx *tsx, const void *stun_pkt, pj_size_t pkt_size); /** * This callback is called after the timer that was scheduled by * #pj_stun_client_tsx_schedule_destroy() has elapsed. Application * should call #pj_stun_client_tsx_destroy() upon receiving this * callback. * * This callback is optional if application will not call * #pj_stun_client_tsx_schedule_destroy(). * * @param tsx The STUN transaction instance. */ void (*on_destroy)(pj_stun_client_tsx *tsx); } pj_stun_tsx_cb; /** * Create an instance of STUN client transaction. The STUN client * transaction is used to transmit outgoing STUN request and to * ensure the reliability of the request by periodically retransmitting * the request, if necessary. * * @param cfg The STUN endpoint, which will be used to retrieve * various settings for the transaction. * @param pool Pool to be used to allocate memory from. * @param grp_lock Group lock to synchronize. * @param cb Callback structure, to be used by the transaction * to send message and to notify the application about * the completion of the transaction. * @param p_tsx Pointer to receive the transaction instance. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_client_tsx_create( pj_stun_config *cfg, pj_pool_t *pool, pj_grp_lock_t *grp_lock, const pj_stun_tsx_cb *cb, pj_stun_client_tsx **p_tsx); /** * Schedule timer to destroy the transaction after the transaction is * complete. Application normally calls this function in the on_complete() * callback. When this timer elapsed, the on_destroy() callback will be * called. * * This is convenient to let the STUN transaction absorbs any response * for the previous request retransmissions. If application doesn't want * this, it can destroy the transaction immediately by calling * #pj_stun_client_tsx_destroy(). * * @param tsx The STUN transaction. * @param delay The delay interval before on_destroy() callback * is called. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_client_tsx_schedule_destroy(pj_stun_client_tsx *tsx, const pj_time_val *delay); /** * Destroy the STUN transaction immediately after the transaction is complete. * Application normally calls this function in the on_complete() callback. * * @param tsx The STUN transaction. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_client_tsx_destroy(pj_stun_client_tsx *tsx); /** * Stop the client transaction. * * @param tsx The STUN transaction. * * @return PJ_SUCCESS on success or PJ_EINVAL if the parameter * is NULL. */ PJ_DECL(pj_status_t) pj_stun_client_tsx_stop(pj_stun_client_tsx *tsx); /** * Check if transaction has completed. * * @param tsx The STUN transaction. * * @return Non-zero if transaction has completed. */ PJ_DECL(pj_bool_t) pj_stun_client_tsx_is_complete(pj_stun_client_tsx *tsx); /** * Associate an arbitrary data with the STUN transaction. This data * can be then retrieved later from the transaction, by using * pj_stun_client_tsx_get_data() function. * * @param tsx The STUN client transaction. * @param data Application data to be associated with the * STUN transaction. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pj_stun_client_tsx_set_data(pj_stun_client_tsx *tsx, void *data); /** * Get the user data that was previously associated with the STUN * transaction. * * @param tsx The STUN client transaction. * * @return The user data. */ PJ_DECL(void*) pj_stun_client_tsx_get_data(pj_stun_client_tsx *tsx); /** * Start the STUN client transaction by sending STUN request using * this transaction. If reliable transport such as TCP or TLS is used, * the retransmit flag should be set to PJ_FALSE because reliablity * will be assured by the transport layer. * * @param tsx The STUN client transaction. * @param retransmit Should this message be retransmitted by the * STUN transaction. * @param pkt The STUN packet to send. * @param pkt_len Length of STUN packet. * * @return PJ_SUCCESS on success, or PJNATH_ESTUNDESTROYED * when the user has destroyed the transaction in * \a on_send_msg() callback, or any other error code * as returned by \a on_send_msg() callback. */ PJ_DECL(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx, pj_bool_t retransmit, void *pkt, unsigned pkt_len); /** * Request to retransmit the request. Normally application should not need * to call this function since retransmission would be handled internally, * but this functionality is needed by ICE. * * @param tsx The STUN client transaction instance. * @param mod_count Boolean flag to indicate whether transmission count * needs to be incremented. * * @return PJ_SUCCESS on success, or PJNATH_ESTUNDESTROYED * when the user has destroyed the transaction in * \a on_send_msg() callback, or any other error code * as returned by \a on_send_msg() callback. */ PJ_DECL(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx, pj_bool_t mod_count); /** * Notify the STUN transaction about the arrival of STUN response. * If the STUN response contains a final error (300 and greater), the * transaction will be terminated and callback will be called. If the * STUN response contains response code 100-299, retransmission * will cease, but application must still call this function again * with a final response later to allow the transaction to complete. * * @param tsx The STUN client transaction instance. * @param msg The incoming STUN message. * @param src_addr The source address of the packet. * @param src_addr_len The length of the source address. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, const pj_stun_msg *msg, const pj_sockaddr_t*src_addr, unsigned src_addr_len); /** * @} */ PJ_END_DECL #endif /* __PJNATH_STUN_TRANSACTION_H__ */ ================================================ FILE: deps/pjsip/pjnath/include/pjnath/turn_session.h ================================================ /* $Id: turn_session.h 4360 2013-02-21 11:26:35Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_TURN_SESSION_H__ #define __PJNATH_TURN_SESSION_H__ /** * @file turn_session.h * @brief Transport independent TURN client session. */ #include #include PJ_BEGIN_DECL /* **************************************************************************/ /** @addtogroup PJNATH_TURN_SESSION @{ The \ref PJNATH_TURN_SESSION is a transport-independent object to manage a client TURN session. It contains the core logic for manage the TURN client session as listed in \ref turn_op_sec, but in transport-independent manner (i.e. it doesn't have a socket), so that developer can integrate TURN client functionality into existing framework that already has its own means to send and receive data, or to support new transport types to TURN, such as TLS. \section turn_sess_using_sec Using the TURN session These steps describes how to use the TURN session: - Creating the session:\n use #pj_turn_session_create() to create the session. - Configuring credential:\n all TURN operations requires the use of authentication (it uses STUN long term autentication method). Use #pj_turn_session_set_credential() to configure the TURN credential to be used by the session. - Configuring server:\n application must call #pj_turn_session_set_server() before it can send Allocate request (with pj_turn_session_alloc()). This function will resolve the TURN server using DNS SRV resolution if the \a resolver is set. The server resolution process will complete asynchronously, and application will be notified in \a on_state() callback of the #pj_turn_session_cb structurewith the session state set to PJ_TURN_STATE_RESOLVED. - Creating allocation:\n create one "relay port" (or called relayed-transport-address in TURN terminology) in the TURN server by using #pj_turn_session_alloc(). This will send Allocate request to the server. This function will complete immediately, and application will be notified about the allocation result in the \a on_state() callback of the #pj_turn_session_cb structure. - Getting the allocation result:\n if allocation is successful, the session state will progress to \a PJ_TURN_STATE_READY, otherwise the state will be \a PJ_TURN_STATE_DEALLOCATED or higher. Session state progression is reported in the \a on_state() callback of the #pj_turn_session_cb structure. On successful allocation, application may retrieve the allocation info by calling #pj_turn_session_get_info(). - Sending data through the relay.\n Once allocation has been created, client may send data to any remote endpoints (called peers in TURN terminology) via the "relay port". It does so by calling #pj_turn_session_sendto(), giving the peer address in the function argument. But note that at this point peers are not allowed to send data towards the client (via the "relay port") before permission is installed for that peer. - Creating permissions.\n Permission needs to be created in the TURN server so that a peer can send data to the client via the relay port (a peer in this case is identified by its IP address). Without this, when the TURN server receives data from the peer in the "relay port", it will drop this data. Create the permission by calling #pj_turn_session_set_perm(), specifying the peer IP address in the argument (the port part of the address is ignored). More than one IP addresses may be specified. - Receiving data from peers.\n Once permission has been installed for the peer, any data received by the TURN server (from that peer) in the "relay port" will be relayed back to client by the server, and application will be notified via \a on_rx_data callback of the #pj_turn_session_cb. - Using ChannelData.\n TURN provides optimized framing to the data by using ChannelData packetization. The client activates this format for the specified peer by calling #pj_turn_session_bind_channel(). Data sent or received to/for this peer will then use ChannelData format instead of Send or Data Indications. - Refreshing the allocation, permissions, and channel bindings.\n Allocations, permissions, and channel bindings will be refreshed by the session automatically when they about to expire. - Destroying the allocation.\n Once the "relay port" is no longer needed, client destroys the allocation by calling #pj_turn_session_shutdown(). This function will return immediately, and application will be notified about the deallocation result in the \a on_state() callback of the #pj_turn_session_cb structure. Once the state has reached PJ_TURN_STATE_DESTROYING, application must assume that the session will be destroyed shortly after. */ /** * Opaque declaration for TURN client session. */ typedef struct pj_turn_session pj_turn_session; /** * TURN transport types, which will be used both to specify the connection * type for reaching TURN server and the type of allocation transport to be * requested to server (the REQUESTED-TRANSPORT attribute). */ typedef enum pj_turn_tp_type { /** * UDP transport, which value corresponds to IANA protocol number. */ PJ_TURN_TP_UDP = 17, /** * TCP transport, which value corresponds to IANA protocol number. */ PJ_TURN_TP_TCP = 6, /** * TLS transport. The TLS transport will only be used as the connection * type to reach the server and never as the allocation transport type. */ PJ_TURN_TP_TLS = 255 } pj_turn_tp_type; /** TURN session state */ typedef enum pj_turn_state_t { /** * TURN session has just been created. */ PJ_TURN_STATE_NULL, /** * TURN server has been configured and now is being resolved via * DNS SRV resolution. */ PJ_TURN_STATE_RESOLVING, /** * TURN server has been resolved. If there is pending allocation to * be done, it will be invoked immediately. */ PJ_TURN_STATE_RESOLVED, /** * TURN session has issued ALLOCATE request and is waiting for response * from the TURN server. */ PJ_TURN_STATE_ALLOCATING, /** * TURN session has successfully allocated relay resoruce and now is * ready to be used. */ PJ_TURN_STATE_READY, /** * TURN session has issued deallocate request and is waiting for a * response from the TURN server. */ PJ_TURN_STATE_DEALLOCATING, /** * Deallocate response has been received. Normally the session will * proceed to DESTROYING state immediately. */ PJ_TURN_STATE_DEALLOCATED, /** * TURN session is being destroyed. */ PJ_TURN_STATE_DESTROYING } pj_turn_state_t; #pragma pack(1) /** * This structure ChannelData header. All the fields are in network byte * order when it's on the wire. */ typedef struct pj_turn_channel_data { pj_uint16_t ch_number; /**< Channel number. */ pj_uint16_t length; /**< Payload length. */ } pj_turn_channel_data; #pragma pack() /** * Callback to receive events from TURN session. */ typedef struct pj_turn_session_cb { /** * This callback will be called by the TURN session whenever it * needs to send outgoing message. Since the TURN session doesn't * have a socket on its own, this callback must be implemented. * * @param sess The TURN session. * @param pkt The packet/data to be sent. * @param pkt_len Length of the packet/data. * @param dst_addr Destination address of the packet. * @param addr_len Length of the destination address. * * @return The callback should return the status of the * send operation. */ pj_status_t (*on_send_pkt)(pj_turn_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_sockaddr_t *dst_addr, unsigned addr_len); /** * Notification when peer address has been bound successfully to * a channel number. * * This callback is optional since the nature of this callback is * for information only. * * @param sess The TURN session. * @param peer_addr The peer address. * @param addr_len Length of the peer address. * @param ch_num The channel number associated with this peer address. */ void (*on_channel_bound)(pj_turn_session *sess, const pj_sockaddr_t *peer_addr, unsigned addr_len, unsigned ch_num); /** * Notification when incoming data has been received, either through * Data indication or ChannelData message from the TURN server. * * @param sess The TURN session. * @param pkt The data/payload of the Data Indication or ChannelData * packet. * @param pkt_len Length of the data/payload. * @param peer_addr Peer address where this payload was received by * the TURN server. * @param addr_len Length of the peer address. */ void (*on_rx_data)(pj_turn_session *sess, void *pkt, unsigned pkt_len, const pj_sockaddr_t *peer_addr, unsigned addr_len); /** * Notification when TURN session state has changed. Application should * implement this callback at least to know that the TURN session is * going to be destroyed. * * @param sess The TURN session. * @param old_state The previous state of the session. * @param new_state The current state of the session. */ void (*on_state)(pj_turn_session *sess, pj_turn_state_t old_state, pj_turn_state_t new_state); } pj_turn_session_cb; /** * Allocation parameter, which can be given when application calls * pj_turn_session_alloc() to allocate relay address in the TURN server. * Application should call pj_turn_alloc_param_default() to initialize * this structure with the default values. */ typedef struct pj_turn_alloc_param { /** * The requested BANDWIDTH. Default is zero to not request any * specific bandwidth. Note that this attribute has been deprecated * after TURN-08 draft, hence application should only use this * attribute when talking to TURN-07 or older version. */ int bandwidth; /** * The requested LIFETIME. Default is zero to not request any * explicit allocation lifetime. */ int lifetime; /** * If set to non-zero, the TURN session will periodically send blank * Send Indication every PJ_TURN_KEEP_ALIVE_SEC to refresh local * NAT bindings. Default is zero. */ int ka_interval; } pj_turn_alloc_param; /** * This structure describes TURN session info. */ typedef struct pj_turn_session_info { /** * Session state. */ pj_turn_state_t state; /** * Last error (if session was terminated because of error) */ pj_status_t last_status; /** * Type of connection to the TURN server. */ pj_turn_tp_type conn_type; /** * The selected TURN server address. */ pj_sockaddr server; /** * Mapped address, as reported by the TURN server. */ pj_sockaddr mapped_addr; /** * The relay address */ pj_sockaddr relay_addr; /** * Current seconds before allocation expires. */ int lifetime; } pj_turn_session_info; /** * Initialize pj_turn_alloc_param with the default values. * * @param prm The TURN allocation parameter to be initialized. */ PJ_DECL(void) pj_turn_alloc_param_default(pj_turn_alloc_param *prm); /** * Duplicate pj_turn_alloc_param. * * @param pool Pool to allocate memory (currently not used) * @param dst Destination parameter. * @param src Source parameter. */ PJ_DECL(void) pj_turn_alloc_param_copy(pj_pool_t *pool, pj_turn_alloc_param *dst, const pj_turn_alloc_param *src); /** * Get string representation for the given TURN state. * * @param state The TURN session state. * * @return The state name as NULL terminated string. */ PJ_DECL(const char*) pj_turn_state_name(pj_turn_state_t state); /** * Create a TURN session instance with the specified address family and * connection type. Once TURN session instance is created, application * must call pj_turn_session_alloc() to allocate a relay address in the TURN * server. * * @param cfg The STUN configuration which contains among other * things the ioqueue and timer heap instance for * the operation of this session. * @param name Optional name to identify this session in the log. * @param af Address family of the client connection. Currently * pj_AF_INET() and pj_AF_INET6() are supported. * @param conn_type Connection type to the TURN server. * @param grp_lock Optional group lock object to be used by this session. * If this value is NULL, the session will create * a group lock internally. * @param cb Callback to receive events from the TURN session. * @param options Option flags, currently this value must be zero. * @param user_data Arbitrary application data to be associated with * this transport. * @param p_sess Pointer to receive the created instance of the * TURN session. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_session_create(const pj_stun_config *cfg, const char *name, int af, pj_turn_tp_type conn_type, pj_grp_lock_t *grp_lock, const pj_turn_session_cb *cb, unsigned options, void *user_data, pj_turn_session **p_sess); /** * Shutdown TURN client session. This will gracefully deallocate and * destroy the client session. * * @param sess The TURN client session. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_session_shutdown(pj_turn_session *sess); /** * Forcefully destroy the TURN session. This will destroy the session * immediately. If there is an active allocation, the server will not * be notified about the client destruction. * * @param sess The TURN client session. * @param last_err Optional error code to be set to the session, * which would be returned back in the \a info * parameter of #pj_turn_session_get_info(). If * this argument value is PJ_SUCCESS, the error * code will not be set. If the session already * has an error code set, this function will not * overwrite that error code either. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_session_destroy(pj_turn_session *sess, pj_status_t last_err); /** * Get the information about this TURN session and the allocation, if * any. * * @param sess The TURN client session. * @param info The structure to be initialized with the TURN * session info. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_session_get_info(pj_turn_session *sess, pj_turn_session_info *info); /** * Associate a user data with this TURN session. The user data may then * be retrieved later with pj_turn_session_get_user_data(). * * @param sess The TURN client session. * @param user_data Arbitrary data. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_session_set_user_data(pj_turn_session *sess, void *user_data); /** * Retrieve the previously assigned user data associated with this TURN * session. * * @param sess The TURN client session. * * @return The user/application data. */ PJ_DECL(void*) pj_turn_session_get_user_data(pj_turn_session *sess); /** * Get the group lock for this TURN session. * * @param sess The TURN client session. * * @return The group lock. */ PJ_DECL(pj_grp_lock_t *) pj_turn_session_get_grp_lock(pj_turn_session *sess); /** * Configure message logging. By default all flags are enabled. * * @param sess The TURN client session. * @param flags Bitmask combination of #pj_stun_sess_msg_log_flag */ PJ_DECL(void) pj_turn_session_set_log(pj_turn_session *sess, unsigned flags); /** * Configure the SOFTWARE name to be sent in all STUN requests by the * TURN session. * * @param sess The TURN client session. * @param sw Software name string. If this argument is NULL or * empty, the session will not include SOFTWARE attribute * in STUN requests and responses. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_turn_session_set_software_name(pj_turn_session *sess, const pj_str_t *sw); /** * Set the server or domain name of the server. Before the application * can send Allocate request (with pj_turn_session_alloc()), it must first * resolve the server address(es) using this function. This function will * resolve the TURN server using DNS SRV resolution if the \a resolver * is set. The server resolution process will complete asynchronously, * and application will be notified in \a on_state() callback with the * session state set to PJ_TURN_STATE_RESOLVED. * * Application may call with pj_turn_session_alloc() before the server * resolution completes. In this case, the operation will be queued by * the session, and it will be sent once the server resolution completes. * * @param sess The TURN client session. * @param domain The domain, hostname, or IP address of the TURN * server. When this parameter contains domain name, * the \a resolver parameter must be set to activate * DNS SRV resolution. * @param default_port The default TURN port number to use when DNS SRV * resolution is not used. If DNS SRV resolution is * used, the server port number will be set from the * DNS SRV records. * @param resolver If this parameter is not NULL, then the \a domain * parameter will be first resolved with DNS SRV and * then fallback to using DNS A/AAAA resolution when * DNS SRV resolution fails. If this parameter is * NULL, the \a domain parameter will be resolved as * hostname. * * @return PJ_SUCCESS if the operation has been successfully * queued, or the appropriate error code on failure. * When this function returns PJ_SUCCESS, the final * result of the resolution process will be notified * to application in \a on_state() callback. */ PJ_DECL(pj_status_t) pj_turn_session_set_server(pj_turn_session *sess, const pj_str_t *domain, int default_port, pj_dns_resolver *resolver); /** * Set credential to be used to authenticate against TURN server. * Application must call this function before sending Allocate request * with pj_turn_session_alloc(). * * @param sess The TURN client session * @param cred STUN credential to be used. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_session_set_credential(pj_turn_session *sess, const pj_stun_auth_cred *cred); /** * Allocate a relay address/resource in the TURN server by sending TURN * Allocate request. Application must first initiate the server resolution * process with pj_turn_session_set_server() and set the credential to be * used with pj_turn_session_set_credential() before calling this function. * * This function will complete asynchronously, and the application will be * notified about the allocation result in \a on_state() callback. The * TURN session state will move to PJ_TURN_STATE_READY if allocation is * successful, and PJ_TURN_STATE_DEALLOCATING or greater state if allocation * has failed. * * Once allocation has been successful, the TURN session will keep this * allocation alive until the session is destroyed, by sending periodic * allocation refresh to the TURN server. * * @param sess The TURN client session. * @param param Optional TURN allocation parameter. * * @return PJ_SUCCESS if the operation has been successfully * initiated or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess, const pj_turn_alloc_param *param); /** * Create or renew permission in the TURN server for the specified peer IP * addresses. Application must install permission for a particular (peer) * IP address before it sends any data to that IP address, or otherwise * the TURN server will drop the data. * * @param sess The TURN client session. * @param addr_cnt Number of IP addresses. * @param addr Array of peer IP addresses. Only the address family * and IP address portion of the socket address matter. * @param options Specify 1 to let the TURN client session automatically * renew the permission later when they are about to * expire. * * @return PJ_SUCCESS if the operation has been successfully * issued, or the appropriate error code. Note that * the operation itself will complete asynchronously. */ PJ_DECL(pj_status_t) pj_turn_session_set_perm(pj_turn_session *sess, unsigned addr_cnt, const pj_sockaddr addr[], unsigned options); /** * Send a data to the specified peer address via the TURN relay. This * function will encapsulate the data as STUN Send Indication or TURN * ChannelData packet and send the message to the TURN server. The TURN * server then will send the data to the peer. * * The allocation (pj_turn_session_alloc()) must have been successfully * created before application can relay any data. * * Since TURN session is transport independent, this function will * ultimately call \a on_send_pkt() callback to request the application * to actually send the packet containing the data to the TURN server. * * @param sess The TURN client session. * @param pkt The data/packet to be sent to peer. * @param pkt_len Length of the data. * @param peer_addr The remote peer address (the ultimate destination * of the data, and not the TURN server address). * @param addr_len Length of the address. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_session_sendto(pj_turn_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_sockaddr_t *peer_addr, unsigned addr_len); /** * Optionally establish channel binding for the specified a peer address. * This function will assign a unique channel number for the peer address * and request channel binding to the TURN server for this address. When * a channel has been bound to a peer, the TURN client and TURN server * will exchange data using ChannelData encapsulation format, which has * lower bandwidth overhead than Send Indication (the default format used * when peer address is not bound to a channel). * * This function will complete asynchronously, and application will be * notified about the result in \a on_channel_bound() callback. * * @param sess The TURN client session. * @param peer The remote peer address. * @param addr_len Length of the address. * * @return PJ_SUCCESS if the operation has been successfully * initiated, or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess, const pj_sockaddr_t *peer, unsigned addr_len); /** * Notify TURN client session upon receiving a packet from server. Since * the TURN session is transport independent, it does not read packet from * any sockets, and rather relies on application giving it packets that * are received from the TURN server. The session then processes this packet * and decides whether it is part of TURN protocol exchange or if it is a * data to be reported back to user, which in this case it will call the * \a on_rx_data() callback. * * @param sess The TURN client session. * @param pkt The packet as received from the TURN server. This * should contain either STUN encapsulated message or * a ChannelData packet. * @param pkt_len The length of the packet. * @param parsed_len Optional argument to receive the number of parsed * or processed data from the packet. * * @return The function may return non-PJ_SUCCESS if it receives * non-STUN and non-ChannelData packet, or if the * \a on_rx_data() returns non-PJ_SUCCESS; */ PJ_DECL(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess, void *pkt, pj_size_t pkt_len, pj_size_t *parsed_len); /** * @} */ PJ_END_DECL #endif /* __PJNATH_TURN_SESSION_H__ */ ================================================ FILE: deps/pjsip/pjnath/include/pjnath/turn_sock.h ================================================ /* $Id: turn_sock.h 4360 2013-02-21 11:26:35Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_TURN_SOCK_H__ #define __PJNATH_TURN_SOCK_H__ /** * @file turn_sock.h * @brief TURN relay using UDP client as transport protocol */ #include #include PJ_BEGIN_DECL /* **************************************************************************/ /** @addtogroup PJNATH_TURN_SOCK @{ This is a ready to use object for relaying application data via a TURN server, by managing all the operations in \ref turn_op_sec. \section turnsock_using_sec Using TURN transport This object provides a thin wrapper to the \ref PJNATH_TURN_SESSION, hence the API is very much the same (apart from the obvious difference in the names). Please see \ref PJNATH_TURN_SESSION for the documentation on how to use the session. \section turnsock_samples_sec Samples The \ref turn_client_sample is a sample application to use the \ref PJNATH_TURN_SOCK. Also see \ref samples_page for other samples. */ /** * Opaque declaration for TURN client. */ typedef struct pj_turn_sock pj_turn_sock; /** * This structure contains callbacks that will be called by the TURN * transport. */ typedef struct pj_turn_sock_cb { /** * Notification when incoming data has been received from the remote * peer via the TURN server. The data reported in this callback will * be the exact data as sent by the peer (e.g. the TURN encapsulation * such as Data Indication or ChannelData will be removed before this * function is called). * * @param turn_sock The TURN client transport. * @param data The data as received from the peer. * @param data_len Length of the data. * @param peer_addr The peer address. * @param addr_len The length of the peer address. */ void (*on_rx_data)(pj_turn_sock *turn_sock, void *pkt, unsigned pkt_len, const pj_sockaddr_t *peer_addr, unsigned addr_len); /** * Notification when TURN session state has changed. Application should * implement this callback to monitor the progress of the TURN session. * * @param turn_sock The TURN client transport. * @param old_state Previous state. * @param new_state Current state. */ void (*on_state)(pj_turn_sock *turn_sock, pj_turn_state_t old_state, pj_turn_state_t new_state); } pj_turn_sock_cb; /** * This structure describes options that can be specified when creating * the TURN socket. Application should call #pj_turn_sock_cfg_default() * to initialize this structure with its default values before using it. */ typedef struct pj_turn_sock_cfg { /** * The group lock to be used by the STUN socket. If NULL, the STUN socket * will create one internally. * * Default: NULL */ pj_grp_lock_t *grp_lock; /** * Packet buffer size. * * Default value is PJ_TURN_MAX_PKT_LEN. */ unsigned max_pkt_size; /** * QoS traffic type to be set on this transport. When application wants * to apply QoS tagging to the transport, it's preferable to set this * field rather than \a qos_param fields since this is more portable. * * Default value is PJ_QOS_TYPE_BEST_EFFORT. */ pj_qos_type qos_type; /** * Set the low level QoS parameters to the transport. This is a lower * level operation than setting the \a qos_type field and may not be * supported on all platforms. * * By default all settings in this structure are not set. */ pj_qos_params qos_params; /** * Specify if STUN socket should ignore any errors when setting the QoS * traffic type/parameters. * * Default: PJ_TRUE */ pj_bool_t qos_ignore_error; /** * Specify the interface where the socket should be bound to. If the * address is zero, socket will be bound to INADDR_ANY. If the address * is non-zero, socket will be bound to this address only. If the port is * set to zero, the socket will bind at any port (chosen by the OS). */ pj_sockaddr bound_addr; /** * Specify the port range for TURN socket binding, relative to the start * port number specified in \a bound_addr. Note that this setting is only * applicable when the start port number is non zero. * * Default value is zero. */ pj_uint16_t port_range; /** * Specify target value for socket receive buffer size. It will be * applied using setsockopt(). When it fails to set the specified size, * it will try with lower value until the highest possible has been * successfully set. * * Default: 0 (OS default) */ unsigned so_rcvbuf_size; /** * Specify target value for socket send buffer size. It will be * applied using setsockopt(). When it fails to set the specified size, * it will try with lower value until the highest possible has been * successfully set. * * Default: 0 (OS default) */ unsigned so_sndbuf_size; } pj_turn_sock_cfg; /** * Initialize pj_turn_sock_cfg structure with default values. */ PJ_DECL(void) pj_turn_sock_cfg_default(pj_turn_sock_cfg *cfg); /** * Create a TURN transport instance with the specified address family and * connection type. Once TURN transport instance is created, application * must call pj_turn_sock_alloc() to allocate a relay address in the TURN * server. * * @param cfg The STUN configuration which contains among other * things the ioqueue and timer heap instance for * the operation of this transport. * @param af Address family of the client connection. Currently * pj_AF_INET() and pj_AF_INET6() are supported. * @param conn_type Connection type to the TURN server. Both TCP and * UDP are supported. * @param cb Callback to receive events from the TURN transport. * @param setting Optional settings to be specified to the transport. * If this parameter is NULL, default values will be * used. * @param user_data Arbitrary application data to be associated with * this transport. * @param p_turn_sock Pointer to receive the created instance of the * TURN transport. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg, int af, pj_turn_tp_type conn_type, const pj_turn_sock_cb *cb, const pj_turn_sock_cfg *setting, void *user_data, pj_turn_sock **p_turn_sock); /** * Destroy the TURN transport instance. This will gracefully close the * connection between the client and the TURN server. Although this * function will return immediately, the TURN socket deletion may continue * in the background and the application may still get state changes * notifications from this transport. * * @param turn_sock The TURN transport instance. */ PJ_DECL(void) pj_turn_sock_destroy(pj_turn_sock *turn_sock); /** * Associate a user data with this TURN transport. The user data may then * be retrieved later with #pj_turn_sock_get_user_data(). * * @param turn_sock The TURN transport instance. * @param user_data Arbitrary data. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_sock_set_user_data(pj_turn_sock *turn_sock, void *user_data); /** * Retrieve the previously assigned user data associated with this TURN * transport. * * @param turn_sock The TURN transport instance. * * @return The user/application data. */ PJ_DECL(void*) pj_turn_sock_get_user_data(pj_turn_sock *turn_sock); /** * Get the group lock for this TURN transport. * * @param turn_sock The TURN transport instance. * * @return The group lock. */ PJ_DECL(pj_grp_lock_t *) pj_turn_sock_get_grp_lock(pj_turn_sock *turn_sock); /** * Get the TURN transport info. The transport info contains, among other * things, the allocated relay address. * * @param turn_sock The TURN transport instance. * @param info Pointer to be filled with TURN transport info. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_sock_get_info(pj_turn_sock *turn_sock, pj_turn_session_info *info); /** * Acquire the internal mutex of the TURN transport. Application may need * to call this function to synchronize access to other objects alongside * the TURN transport, to avoid deadlock. * * @param turn_sock The TURN transport instance. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_sock_lock(pj_turn_sock *turn_sock); /** * Release the internal mutex previously held with pj_turn_sock_lock(). * * @param turn_sock The TURN transport instance. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_sock_unlock(pj_turn_sock *turn_sock); /** * Set STUN message logging for this TURN session. * See #pj_stun_session_set_log(). * * @param turn_sock The TURN transport instance. * @param flags Bitmask combination of #pj_stun_sess_msg_log_flag */ PJ_DECL(void) pj_turn_sock_set_log(pj_turn_sock *turn_sock, unsigned flags); /** * Configure the SOFTWARE name to be sent in all STUN requests by the * TURN session. * * @param turn_sock The TURN transport instance. * @param sw Software name string. If this argument is NULL or * empty, the session will not include SOFTWARE attribute * in STUN requests and responses. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pj_turn_sock_set_software_name(pj_turn_sock *turn_sock, const pj_str_t *sw); /** * Allocate a relay address/resource in the TURN server. This function * will resolve the TURN server using DNS SRV (if desired) and send TURN * \a Allocate request using the specified credential to allocate a relay * address in the server. This function completes asynchronously, and * application will be notified when the allocation process has been * successful in the \a on_state() callback when the state is set to * PJ_TURN_STATE_READY. If the allocation fails, the state will be set * to PJ_TURN_STATE_DEALLOCATING or greater. * * @param turn_sock The TURN transport instance. * @param domain The domain, hostname, or IP address of the TURN * server. When this parameter contains domain name, * the \a resolver parameter must be set to activate * DNS SRV resolution. * @param default_port The default TURN port number to use when DNS SRV * resolution is not used. If DNS SRV resolution is * used, the server port number will be set from the * DNS SRV records. * @param resolver If this parameter is not NULL, then the \a domain * parameter will be first resolved with DNS SRV and * then fallback to using DNS A/AAAA resolution when * DNS SRV resolution fails. If this parameter is * NULL, the \a domain parameter will be resolved as * hostname. * @param cred The STUN credential to be used for the TURN server. * @param param Optional TURN allocation parameter. * * @return PJ_SUCCESS if the operation has been successfully * queued, or the appropriate error code on failure. * When this function returns PJ_SUCCESS, the final * result of the allocation process will be notified * to application in \a on_state() callback. * */ PJ_DECL(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock, const pj_str_t *domain, int default_port, pj_dns_resolver *resolver, const pj_stun_auth_cred *cred, const pj_turn_alloc_param *param); /** * Create or renew permission in the TURN server for the specified peer IP * addresses. Application must install permission for a particular (peer) * IP address before it sends any data to that IP address, or otherwise * the TURN server will drop the data. * * @param turn_sock The TURN transport instance. * @param addr_cnt Number of IP addresses. * @param addr Array of peer IP addresses. Only the address family * and IP address portion of the socket address matter. * @param options Specify 1 to let the TURN client session automatically * renew the permission later when they are about to * expire. * * @return PJ_SUCCESS if the operation has been successfully * issued, or the appropriate error code. Note that * the operation itself will complete asynchronously. */ PJ_DECL(pj_status_t) pj_turn_sock_set_perm(pj_turn_sock *turn_sock, unsigned addr_cnt, const pj_sockaddr addr[], unsigned options); /** * Send a data to the specified peer address via the TURN relay. This * function will encapsulate the data as STUN Send Indication or TURN * ChannelData packet and send the message to the TURN server. The TURN * server then will send the data to the peer. * * The allocation (pj_turn_sock_alloc()) must have been successfully * created before application can relay any data. * * @param turn_sock The TURN transport instance. * @param pkt The data/packet to be sent to peer. * @param pkt_len Length of the data. * @param peer_addr The remote peer address (the ultimate destination * of the data, and not the TURN server address). * @param addr_len Length of the address. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_sock_sendto(pj_turn_sock *turn_sock, const pj_uint8_t *pkt, unsigned pkt_len, const pj_sockaddr_t *peer_addr, unsigned addr_len); /** * Optionally establish channel binding for the specified a peer address. * This function will assign a unique channel number for the peer address * and request channel binding to the TURN server for this address. When * a channel has been bound to a peer, the TURN transport and TURN server * will exchange data using ChannelData encapsulation format, which has * lower bandwidth overhead than Send Indication (the default format used * when peer address is not bound to a channel). * * @param turn_sock The TURN transport instance. * @param peer The remote peer address. * @param addr_len Length of the address. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ PJ_DECL(pj_status_t) pj_turn_sock_bind_channel(pj_turn_sock *turn_sock, const pj_sockaddr_t *peer, unsigned addr_len); /** * @} */ PJ_END_DECL #endif /* __PJNATH_TURN_SOCK_H__ */ ================================================ FILE: deps/pjsip/pjnath/include/pjnath/types.h ================================================ /* $Id: types.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_TYPES_H__ #define __PJNATH_TYPES_H__ /** * @file types.h * @brief PJNATH types. */ #include #include /** * @defgroup PJNATH NAT Traversal Helper Library * @{ */ PJ_BEGIN_DECL /** * This constant describes a number to be used to identify an invalid TURN * channel number. */ #define PJ_TURN_INVALID_CHANNEL 0xFFFF /** * Initialize pjnath library. * * @return Initialization status. */ PJ_DECL(pj_status_t) pjnath_init(void); /** * Display error to the log. * * @param sender The sender name. * @param title Title message. * @param status The error status. */ #if PJNATH_ERROR_LEVEL <= PJ_LOG_MAX_LEVEL PJ_DECL(void) pjnath_perror(const char *sender, const char *title, pj_status_t status); #else # define pjnath_perror(sender, title, status) #endif PJ_END_DECL /** * @} */ #endif /* __PJNATH_TYPES_H__ */ ================================================ FILE: deps/pjsip/pjnath/include/pjnath.h ================================================ /* $Id: pjnath.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJNATH_H__ #define __PJNATH_H__ /** * @file pjnath.h * @brief PJNATH main header file. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif ================================================ FILE: deps/pjsip/pjnath/src/pjnath/errno.c ================================================ /* $Id: errno.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include /* PJNATH's own error codes/messages * MUST KEEP THIS ARRAY SORTED!! * Message must be limited to 64 chars! */ #if defined(PJ_HAS_ERROR_STRING) && PJ_HAS_ERROR_STRING!=0 static const struct { int code; const char *msg; } err_str[] = { /* STUN related error codes */ PJ_BUILD_ERR( PJNATH_EINSTUNMSG, "Invalid STUN message"), PJ_BUILD_ERR( PJNATH_EINSTUNMSGLEN, "Invalid STUN message length"), PJ_BUILD_ERR( PJNATH_EINSTUNMSGTYPE, "Invalid or unexpected STUN message type"), PJ_BUILD_ERR( PJNATH_ESTUNTIMEDOUT, "STUN transaction has timed out"), PJ_BUILD_ERR( PJNATH_ESTUNTOOMANYATTR, "Too many STUN attributes"), PJ_BUILD_ERR( PJNATH_ESTUNINATTRLEN, "Invalid STUN attribute length"), PJ_BUILD_ERR( PJNATH_ESTUNDUPATTR, "Found duplicate STUN attribute"), PJ_BUILD_ERR( PJNATH_ESTUNFINGERPRINT, "STUN FINGERPRINT verification failed"), PJ_BUILD_ERR( PJNATH_ESTUNMSGINTPOS, "Invalid STUN attribute after MESSAGE-INTEGRITY"), PJ_BUILD_ERR( PJNATH_ESTUNFINGERPOS, "Invalid STUN attribute after FINGERPRINT"), PJ_BUILD_ERR( PJNATH_ESTUNNOMAPPEDADDR, "STUN (XOR-)MAPPED-ADDRESS attribute not found"), PJ_BUILD_ERR( PJNATH_ESTUNIPV6NOTSUPP, "STUN IPv6 attribute not supported"), PJ_BUILD_ERR( PJNATH_EINVAF, "Invalid STUN address family value"), PJ_BUILD_ERR( PJNATH_ESTUNINSERVER, "Invalid STUN server or server not configured"), PJ_BUILD_ERR( PJNATH_ESTUNDESTROYED, "STUN object has been destoyed"), /* ICE related errors */ PJ_BUILD_ERR( PJNATH_ENOICE, "ICE session not available"), PJ_BUILD_ERR( PJNATH_EICEINPROGRESS, "ICE check is in progress"), PJ_BUILD_ERR( PJNATH_EICEFAILED, "All ICE checklists failed"), PJ_BUILD_ERR( PJNATH_EICEMISMATCH, "Default target doesn't match any ICE candidates"), PJ_BUILD_ERR( PJNATH_EICEINCOMPID, "Invalid ICE component ID"), PJ_BUILD_ERR( PJNATH_EICEINCANDID, "Invalid ICE candidate ID"), PJ_BUILD_ERR( PJNATH_EICEINSRCADDR, "Source address mismatch"), PJ_BUILD_ERR( PJNATH_EICEMISSINGSDP, "Missing ICE SDP attribute"), PJ_BUILD_ERR( PJNATH_EICEINCANDSDP, "Invalid SDP \"candidate\" attribute"), PJ_BUILD_ERR( PJNATH_EICENOHOSTCAND, "No host candidate associated with srflx"), PJ_BUILD_ERR( PJNATH_EICENOMTIMEOUT, "Controlled agent timed out waiting for nomination"), /* TURN related errors */ PJ_BUILD_ERR( PJNATH_ETURNINTP, "Invalid/unsupported transport"), }; #endif /* PJ_HAS_ERROR_STRING */ /* * pjnath_strerror() */ static pj_str_t pjnath_strerror(pj_status_t statcode, char *buf, pj_size_t bufsize ) { pj_str_t errstr; #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) if (statcode >= PJNATH_ERRNO_START && statcode < PJNATH_ERRNO_START + PJ_ERRNO_SPACE_SIZE) { /* Find the error in the table. * Use binary search! */ int first = 0; int n = PJ_ARRAY_SIZE(err_str); while (n > 0) { int half = n/2; int mid = first + half; if (err_str[mid].code < statcode) { first = mid+1; n -= (half+1); } else if (err_str[mid].code > statcode) { n = half; } else { first = mid; break; } } if (PJ_ARRAY_SIZE(err_str) && err_str[first].code == statcode) { pj_str_t msg; msg.ptr = (char*)err_str[first].msg; msg.slen = pj_ansi_strlen(err_str[first].msg); errstr.ptr = buf; pj_strncpy_with_null(&errstr, &msg, bufsize); return errstr; } } #endif /* PJ_HAS_ERROR_STRING */ /* Error not found. */ errstr.ptr = buf; errstr.slen = pj_ansi_snprintf(buf, bufsize, "Unknown pjnath error %d", statcode); if (errstr.slen < 1 || errstr.slen >= (int)bufsize) errstr.slen = bufsize-1; return errstr; } static pj_str_t pjnath_strerror2(pj_status_t statcode, char *buf, pj_size_t bufsize ) { int stun_code = statcode - PJ_STATUS_FROM_STUN_CODE(0); const pj_str_t cmsg = pj_stun_get_err_reason(stun_code); pj_str_t errstr; buf[bufsize-1] = '\0'; if (cmsg.slen == 0) { /* Not found */ errstr.ptr = buf; errstr.slen = pj_ansi_snprintf(buf, bufsize, "Unknown STUN err-code %d", stun_code); } else { errstr.ptr = buf; pj_strncpy(&errstr, &cmsg, bufsize); if (errstr.slen < (int)bufsize) buf[errstr.slen] = '\0'; else buf[bufsize-1] = '\0'; } if (errstr.slen < 1 || errstr.slen >= (int)bufsize) errstr.slen = bufsize-1; return errstr; } PJ_DEF(pj_status_t) pjnath_init(void) { pj_status_t status; status = pj_register_strerror(PJNATH_ERRNO_START, 299, &pjnath_strerror); pj_assert(status == PJ_SUCCESS); status = pj_register_strerror(PJ_STATUS_FROM_STUN_CODE(300), 699 - 300, &pjnath_strerror2); pj_assert(status == PJ_SUCCESS); return PJ_SUCCESS; } #if PJNATH_ERROR_LEVEL <= PJ_LOG_MAX_LEVEL PJ_DEF(void) pjnath_perror(const char *sender, const char *title, pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); #if PJNATH_ERROR_LEVEL==1 PJ_LOG(1,(sender, "%s: %s", title, errmsg)); #elif PJNATH_ERROR_LEVEL==2 PJ_LOG(2,(sender, "%s: %s", title, errmsg)); #elif PJNATH_ERROR_LEVEL==3 PJ_LOG(3,(sender, "%s: %s", title, errmsg)); #elif PJNATH_ERROR_LEVEL==4 PJ_LOG(4,(sender, "%s: %s", title, errmsg)); #elif PJNATH_ERROR_LEVEL==5 PJ_LOG(5,(sender, "%s: %s", title, errmsg)); #else # error Invalid PJNATH_ERROR_LEVEL value #endif } #endif /* PJNATH_ERROR_LEVEL <= PJ_LOG_MAX_LEVEL */ ================================================ FILE: deps/pjsip/pjnath/src/pjnath/ice_session.c ================================================ /* $Id: ice_session.c 4365 2013-02-21 18:06:51Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include /* String names for candidate types */ static const char *cand_type_names[] = { "host", "srflx", "prflx", "relay" }; /* String names for pj_ice_sess_check_state */ #if PJ_LOG_MAX_LEVEL >= 4 static const char *check_state_name[] = { "Frozen", "Waiting", "In Progress", "Succeeded", "Failed" }; static const char *clist_state_name[] = { "Idle", "Running", "Completed" }; #endif /* PJ_LOG_MAX_LEVEL >= 4 */ static const char *role_names[] = { "Unknown", "Controlled", "Controlling" }; enum timer_type { TIMER_NONE, /**< Timer not active */ TIMER_COMPLETION_CALLBACK, /**< Call on_ice_complete() callback */ TIMER_CONTROLLED_WAIT_NOM, /**< Controlled agent is waiting for controlling agent to send connectivity check with nominated flag after it has valid check for every components. */ TIMER_START_NOMINATED_CHECK,/**< Controlling agent start connectivity checks with USE-CANDIDATE flag. */ TIMER_KEEP_ALIVE /**< ICE keep-alive timer. */ }; /* Candidate type preference */ static pj_uint8_t cand_type_prefs[PJ_ICE_CAND_TYPE_MAX] = { #if PJ_ICE_CAND_TYPE_PREF_BITS < 8 /* Keep it to 2 bits */ 3, /**< PJ_ICE_HOST_PREF */ 1, /**< PJ_ICE_SRFLX_PREF. */ 2, /**< PJ_ICE_PRFLX_PREF */ 0 /**< PJ_ICE_RELAYED_PREF */ #else /* Default ICE session preferences, according to draft-ice */ 126, /**< PJ_ICE_HOST_PREF */ 100, /**< PJ_ICE_SRFLX_PREF. */ 110, /**< PJ_ICE_PRFLX_PREF */ 0 /**< PJ_ICE_RELAYED_PREF */ #endif }; #define THIS_FILE "ice_session.c" #define CHECK_NAME_LEN 128 #define LOG4(expr) PJ_LOG(4,expr) #define LOG5(expr) PJ_LOG(4,expr) #define GET_LCAND_ID(cand) (unsigned)(cand - ice->lcand) #define GET_CHECK_ID(cl, chk) (chk - (cl)->checks) /* The data that will be attached to the STUN session on each * component. */ typedef struct stun_data { pj_ice_sess *ice; unsigned comp_id; pj_ice_sess_comp *comp; } stun_data; /* The data that will be attached to the timer to perform * periodic check. */ typedef struct timer_data { pj_ice_sess *ice; pj_ice_sess_checklist *clist; } timer_data; /* This is the data that will be attached as token to outgoing * STUN messages. */ /* Forward declarations */ static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te); static void on_ice_complete(pj_ice_sess *ice, pj_status_t status); static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now); static void ice_on_destroy(void *obj); static void destroy_ice(pj_ice_sess *ice, pj_status_t reason); static pj_status_t start_periodic_check(pj_timer_heap_t *th, pj_timer_entry *te); static void start_nominated_check(pj_ice_sess *ice); static void periodic_timer(pj_timer_heap_t *th, pj_timer_entry *te); static void handle_incoming_check(pj_ice_sess *ice, const pj_ice_rx_check *rcheck); /* These are the callbacks registered to the STUN sessions */ static pj_status_t on_stun_send_msg(pj_stun_session *sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len); static pj_status_t on_stun_rx_request(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_rx_data *rdata, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len); static void on_stun_request_complete(pj_stun_session *stun_sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len); static pj_status_t on_stun_rx_indication(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_msg *msg, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len); /* These are the callbacks for performing STUN authentication */ static pj_status_t stun_auth_get_auth(void *user_data, pj_pool_t *pool, pj_str_t *realm, pj_str_t *nonce); static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg, void *user_data, pj_pool_t *pool, pj_str_t *realm, pj_str_t *username, pj_str_t *nonce, pj_stun_passwd_type *data_type, pj_str_t *data); static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, void *user_data, const pj_str_t *realm, const pj_str_t *username, pj_pool_t *pool, pj_stun_passwd_type *data_type, pj_str_t *data); PJ_DEF(const char*) pj_ice_get_cand_type_name(pj_ice_cand_type type) { PJ_ASSERT_RETURN(type <= PJ_ICE_CAND_TYPE_RELAYED, "???"); return cand_type_names[type]; } PJ_DEF(const char*) pj_ice_sess_role_name(pj_ice_sess_role role) { switch (role) { case PJ_ICE_SESS_ROLE_UNKNOWN: return "Unknown"; case PJ_ICE_SESS_ROLE_CONTROLLED: return "Controlled"; case PJ_ICE_SESS_ROLE_CONTROLLING: return "Controlling"; default: return "??"; } } /* Get the prefix for the foundation */ static int get_type_prefix(pj_ice_cand_type type) { switch (type) { case PJ_ICE_CAND_TYPE_HOST: return 'H'; case PJ_ICE_CAND_TYPE_SRFLX: return 'S'; case PJ_ICE_CAND_TYPE_PRFLX: return 'P'; case PJ_ICE_CAND_TYPE_RELAYED: return 'R'; default: pj_assert(!"Invalid type"); return 'U'; } } /* Calculate foundation: * Two candidates have the same foundation when they are "similar" - of * the same type and obtained from the same host candidate and STUN * server using the same protocol. Otherwise, their foundation is * different. */ PJ_DEF(void) pj_ice_calc_foundation(pj_pool_t *pool, pj_str_t *foundation, pj_ice_cand_type type, const pj_sockaddr *base_addr) { #if PJNATH_ICE_PRIO_STD char buf[64]; pj_uint32_t val; if (base_addr->addr.sa_family == pj_AF_INET()) { val = pj_ntohl(base_addr->ipv4.sin_addr.s_addr); } else { val = pj_hash_calc(0, pj_sockaddr_get_addr(base_addr), pj_sockaddr_get_addr_len(base_addr)); } pj_ansi_snprintf(buf, sizeof(buf), "%c%x", get_type_prefix(type), val); pj_strdup2(pool, foundation, buf); #else /* Much shorter version, valid for candidates added by * pj_ice_strans. */ foundation->ptr = (char*) pj_pool_alloc(pool, 1); *foundation->ptr = (char)get_type_prefix(type); foundation->slen = 1; PJ_UNUSED_ARG(base_addr); #endif } /* Init component */ static pj_status_t init_comp(pj_ice_sess *ice, unsigned comp_id, pj_ice_sess_comp *comp) { pj_stun_session_cb sess_cb; pj_stun_auth_cred auth_cred; stun_data *sd; pj_status_t status; /* Init STUN callbacks */ pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_request_complete = &on_stun_request_complete; sess_cb.on_rx_indication = &on_stun_rx_indication; sess_cb.on_rx_request = &on_stun_rx_request; sess_cb.on_send_msg = &on_stun_send_msg; /* Create STUN session for this candidate */ status = pj_stun_session_create(&ice->stun_cfg, NULL, &sess_cb, PJ_TRUE, ice->grp_lock, &comp->stun_sess); if (status != PJ_SUCCESS) return status; /* Associate data with this STUN session */ sd = PJ_POOL_ZALLOC_T(ice->pool, struct stun_data); sd->ice = ice; sd->comp_id = comp_id; sd->comp = comp; pj_stun_session_set_user_data(comp->stun_sess, sd); /* Init STUN authentication credential */ pj_bzero(&auth_cred, sizeof(auth_cred)); auth_cred.type = PJ_STUN_AUTH_CRED_DYNAMIC; auth_cred.data.dyn_cred.get_auth = &stun_auth_get_auth; auth_cred.data.dyn_cred.get_cred = &stun_auth_get_cred; auth_cred.data.dyn_cred.get_password = &stun_auth_get_password; auth_cred.data.dyn_cred.user_data = comp->stun_sess; pj_stun_session_set_credential(comp->stun_sess, PJ_STUN_AUTH_SHORT_TERM, &auth_cred); return PJ_SUCCESS; } /* Init options with default values */ PJ_DEF(void) pj_ice_sess_options_default(pj_ice_sess_options *opt) { opt->aggressive = PJ_TRUE; opt->nominated_check_delay = PJ_ICE_NOMINATED_CHECK_DELAY; opt->controlled_agent_want_nom_timeout = ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT; } /* * Create ICE session. */ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, const char *name, pj_ice_sess_role role, unsigned comp_cnt, const pj_ice_sess_cb *cb, const pj_str_t *local_ufrag, const pj_str_t *local_passwd, pj_grp_lock_t *grp_lock, pj_ice_sess **p_ice) { pj_pool_t *pool; pj_ice_sess *ice; unsigned i; pj_status_t status; PJ_ASSERT_RETURN(stun_cfg && cb && p_ice, PJ_EINVAL); if (name == NULL) name = "icess%p"; pool = pj_pool_create(stun_cfg->pf, name, PJNATH_POOL_LEN_ICE_SESS, PJNATH_POOL_INC_ICE_SESS, NULL); ice = PJ_POOL_ZALLOC_T(pool, pj_ice_sess); ice->pool = pool; ice->role = role; ice->tie_breaker.u32.hi = pj_rand(); ice->tie_breaker.u32.lo = pj_rand(); ice->prefs = cand_type_prefs; pj_ice_sess_options_default(&ice->opt); pj_timer_entry_init(&ice->timer, TIMER_NONE, (void*)ice, &on_timer); pj_ansi_snprintf(ice->obj_name, sizeof(ice->obj_name), name, ice); if (grp_lock) { ice->grp_lock = grp_lock; } else { status = pj_grp_lock_create(pool, NULL, &ice->grp_lock); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } } pj_grp_lock_add_ref(ice->grp_lock); pj_grp_lock_add_handler(ice->grp_lock, pool, ice, &ice_on_destroy); pj_memcpy(&ice->cb, cb, sizeof(*cb)); pj_memcpy(&ice->stun_cfg, stun_cfg, sizeof(*stun_cfg)); ice->comp_cnt = comp_cnt; for (i=0; icomp[i]; comp->valid_check = NULL; comp->nominated_check = NULL; status = init_comp(ice, i+1, comp); if (status != PJ_SUCCESS) { destroy_ice(ice, status); return status; } } /* Initialize transport datas */ for (i=0; itp_data); ++i) { ice->tp_data[i].transport_id = i; ice->tp_data[i].has_req_data = PJ_FALSE; } if (local_ufrag == NULL) { ice->rx_ufrag.ptr = (char*) pj_pool_alloc(ice->pool, PJ_ICE_UFRAG_LEN); pj_create_random_string(ice->rx_ufrag.ptr, PJ_ICE_UFRAG_LEN); ice->rx_ufrag.slen = PJ_ICE_UFRAG_LEN; } else { pj_strdup(ice->pool, &ice->rx_ufrag, local_ufrag); } if (local_passwd == NULL) { ice->rx_pass.ptr = (char*) pj_pool_alloc(ice->pool, PJ_ICE_UFRAG_LEN); pj_create_random_string(ice->rx_pass.ptr, PJ_ICE_UFRAG_LEN); ice->rx_pass.slen = PJ_ICE_UFRAG_LEN; } else { pj_strdup(ice->pool, &ice->rx_pass, local_passwd); } pj_list_init(&ice->early_check); /* Done */ *p_ice = ice; LOG4((ice->obj_name, "ICE session created, comp_cnt=%d, role is %s agent", comp_cnt, role_names[ice->role])); return PJ_SUCCESS; } /* * Get the value of various options of the ICE session. */ PJ_DEF(pj_status_t) pj_ice_sess_get_options(pj_ice_sess *ice, pj_ice_sess_options *opt) { PJ_ASSERT_RETURN(ice, PJ_EINVAL); pj_memcpy(opt, &ice->opt, sizeof(*opt)); return PJ_SUCCESS; } /* * Specify various options for this ICE session. */ PJ_DEF(pj_status_t) pj_ice_sess_set_options(pj_ice_sess *ice, const pj_ice_sess_options *opt) { PJ_ASSERT_RETURN(ice && opt, PJ_EINVAL); pj_memcpy(&ice->opt, opt, sizeof(*opt)); LOG5((ice->obj_name, "ICE nomination type set to %s", (ice->opt.aggressive ? "aggressive" : "regular"))); return PJ_SUCCESS; } /* * Callback to really destroy the session */ static void ice_on_destroy(void *obj) { pj_ice_sess *ice = (pj_ice_sess*) obj; if (ice->pool) { pj_pool_t *pool = ice->pool; ice->pool = NULL; pj_pool_release(pool); } LOG4((THIS_FILE, "ICE session %p destroyed", ice)); } /* * Destroy */ static void destroy_ice(pj_ice_sess *ice, pj_status_t reason) { unsigned i; if (reason == PJ_SUCCESS) { LOG4((ice->obj_name, "Destroying ICE session %p", ice)); } pj_grp_lock_acquire(ice->grp_lock); if (ice->is_destroying) { pj_grp_lock_release(ice->grp_lock); return; } ice->is_destroying = PJ_TRUE; pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer, PJ_FALSE); for (i=0; icomp_cnt; ++i) { if (ice->comp[i].stun_sess) { pj_stun_session_destroy(ice->comp[i].stun_sess); ice->comp[i].stun_sess = NULL; } } pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->clist.timer, PJ_FALSE); pj_grp_lock_dec_ref(ice->grp_lock); pj_grp_lock_release(ice->grp_lock); } /* * Destroy */ PJ_DEF(pj_status_t) pj_ice_sess_destroy(pj_ice_sess *ice) { PJ_ASSERT_RETURN(ice, PJ_EINVAL); destroy_ice(ice, PJ_SUCCESS); return PJ_SUCCESS; } /* * Change session role. */ PJ_DEF(pj_status_t) pj_ice_sess_change_role(pj_ice_sess *ice, pj_ice_sess_role new_role) { PJ_ASSERT_RETURN(ice, PJ_EINVAL); if (new_role != ice->role) { ice->role = new_role; LOG4((ice->obj_name, "Role changed to %s", role_names[new_role])); } return PJ_SUCCESS; } /* * Change type preference */ PJ_DEF(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice, const pj_uint8_t prefs[4]) { unsigned i; PJ_ASSERT_RETURN(ice && prefs, PJ_EINVAL); ice->prefs = (pj_uint8_t*) pj_pool_calloc(ice->pool, PJ_ICE_CAND_TYPE_MAX, sizeof(pj_uint8_t)); for (i=0; iprefs[i] = prefs[i]; } return PJ_SUCCESS; } /* Find component by ID */ static pj_ice_sess_comp *find_comp(const pj_ice_sess *ice, unsigned comp_id) { /* Ticket #1844: possible wrong assertion when remote has less ICE comp */ //pj_assert(comp_id > 0 && comp_id <= ice->comp_cnt); if (comp_id > ice->comp_cnt) return NULL; return (pj_ice_sess_comp*) &ice->comp[comp_id-1]; } /* Callback by STUN authentication when it needs to send 401 */ static pj_status_t stun_auth_get_auth(void *user_data, pj_pool_t *pool, pj_str_t *realm, pj_str_t *nonce) { PJ_UNUSED_ARG(user_data); PJ_UNUSED_ARG(pool); realm->slen = 0; nonce->slen = 0; return PJ_SUCCESS; } /* Get credential to be sent with outgoing message */ static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg, void *user_data, pj_pool_t *pool, pj_str_t *realm, pj_str_t *username, pj_str_t *nonce, pj_stun_passwd_type *data_type, pj_str_t *data) { pj_stun_session *sess = (pj_stun_session *)user_data; stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess); pj_ice_sess *ice = sd->ice; PJ_UNUSED_ARG(pool); realm->slen = nonce->slen = 0; if (PJ_STUN_IS_RESPONSE(msg->hdr.type)) { /* Outgoing responses need to have the same credential as * incoming requests. */ *username = ice->rx_uname; *data_type = PJ_STUN_PASSWD_PLAIN; *data = ice->rx_pass; } else { *username = ice->tx_uname; *data_type = PJ_STUN_PASSWD_PLAIN; *data = ice->tx_pass; } return PJ_SUCCESS; } /* Get password to be used to authenticate incoming message */ static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, void *user_data, const pj_str_t *realm, const pj_str_t *username, pj_pool_t *pool, pj_stun_passwd_type *data_type, pj_str_t *data) { pj_stun_session *sess = (pj_stun_session *)user_data; stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess); pj_ice_sess *ice = sd->ice; PJ_UNUSED_ARG(realm); PJ_UNUSED_ARG(pool); if (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) || PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) { /* Incoming response is authenticated with TX credential */ /* Verify username */ if (pj_strcmp(username, &ice->tx_uname) != 0) return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); *data_type = PJ_STUN_PASSWD_PLAIN; *data = ice->tx_pass; } else { /* Incoming request is authenticated with RX credential */ /* The agent MUST accept a credential if the username consists * of two values separated by a colon, where the first value is * equal to the username fragment generated by the agent in an offer * or answer for a session in-progress, and the MESSAGE-INTEGRITY * is the output of a hash of the password and the STUN packet's * contents. */ const char *pos; pj_str_t ufrag; pos = (const char*)pj_memchr(username->ptr, ':', username->slen); if (pos == NULL) return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); ufrag.ptr = (char*)username->ptr; ufrag.slen = (pos - username->ptr); if (pj_strcmp(&ufrag, &ice->rx_ufrag) != 0) return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); *data_type = PJ_STUN_PASSWD_PLAIN; *data = ice->rx_pass; } return PJ_SUCCESS; } static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice, pj_ice_cand_type type, pj_uint32_t local_pref, pj_uint32_t comp_id) { #if PJNATH_ICE_PRIO_STD return ((ice->prefs[type] & 0xFF) << 24) + ((local_pref & 0xFFFF) << 8) + (((256 - comp_id) & 0xFF) << 0); #else enum { type_mask = ((2 << PJ_ICE_CAND_TYPE_PREF_BITS) - 1), local_mask = ((2 << PJ_ICE_LOCAL_PREF_BITS) - 1), comp_mask = ((2 << PJ_ICE_COMP_BITS) - 1), comp_shift = 0, local_shift = (PJ_ICE_COMP_BITS), type_shift = (comp_shift + local_shift), max_comp = (2<prefs[type] & type_mask) << type_shift) + ((local_pref & local_mask) << local_shift) + (((max_comp - comp_id) & comp_mask) << comp_shift); #endif } /* * Add ICE candidate */ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, pj_ice_cand_type type, pj_uint16_t local_pref, const pj_str_t *foundation, const pj_sockaddr_t *addr, const pj_sockaddr_t *base_addr, const pj_sockaddr_t *rel_addr, int addr_len, unsigned *p_cand_id) { pj_ice_sess_cand *lcand; pj_status_t status = PJ_SUCCESS; char address[PJ_INET6_ADDRSTRLEN]; PJ_ASSERT_RETURN(ice && comp_id && foundation && addr && base_addr && addr_len, PJ_EINVAL); PJ_ASSERT_RETURN(comp_id <= ice->comp_cnt, PJ_EINVAL); pj_grp_lock_acquire(ice->grp_lock); if (ice->lcand_cnt >= PJ_ARRAY_SIZE(ice->lcand)) { status = PJ_ETOOMANY; goto on_error; } lcand = &ice->lcand[ice->lcand_cnt]; lcand->comp_id = (pj_uint8_t)comp_id; lcand->transport_id = (pj_uint8_t)transport_id; lcand->type = type; pj_strdup(ice->pool, &lcand->foundation, foundation); lcand->prio = CALC_CAND_PRIO(ice, type, local_pref, lcand->comp_id); pj_sockaddr_cp(&lcand->addr, addr); pj_sockaddr_cp(&lcand->base_addr, base_addr); if (rel_addr == NULL) rel_addr = base_addr; pj_memcpy(&lcand->rel_addr, rel_addr, addr_len); pj_ansi_strcpy(ice->tmp.txt, pj_sockaddr_print(&lcand->addr, address, sizeof(address), 0)); LOG4((ice->obj_name, "Candidate %d added: comp_id=%d, type=%s, foundation=%.*s, " "addr=%s:%d, base=%s:%d, prio=0x%x (%u)", ice->lcand_cnt, lcand->comp_id, cand_type_names[lcand->type], (int)lcand->foundation.slen, lcand->foundation.ptr, ice->tmp.txt, pj_sockaddr_get_port(&lcand->addr), pj_sockaddr_print(&lcand->base_addr, address, sizeof(address), 0), pj_sockaddr_get_port(&lcand->base_addr), lcand->prio, lcand->prio)); if (p_cand_id) *p_cand_id = ice->lcand_cnt; ++ice->lcand_cnt; on_error: pj_grp_lock_release(ice->grp_lock); return status; } /* Find default candidate ID for the component */ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, unsigned comp_id, int *cand_id) { unsigned i; PJ_ASSERT_RETURN(ice && comp_id && cand_id, PJ_EINVAL); PJ_ASSERT_RETURN(comp_id <= ice->comp_cnt, PJ_EINVAL); *cand_id = -1; pj_grp_lock_acquire(ice->grp_lock); /* First find in valid list if we have nominated pair */ for (i=0; ivalid_list.count; ++i) { pj_ice_sess_check *check = &ice->valid_list.checks[i]; if (check->lcand->comp_id == comp_id) { *cand_id = GET_LCAND_ID(check->lcand); pj_grp_lock_release(ice->grp_lock); return PJ_SUCCESS; } } /* If there's no nominated pair, find relayed candidate */ for (i=0; ilcand_cnt; ++i) { pj_ice_sess_cand *lcand = &ice->lcand[i]; if (lcand->comp_id==comp_id && lcand->type == PJ_ICE_CAND_TYPE_RELAYED) { *cand_id = GET_LCAND_ID(lcand); pj_grp_lock_release(ice->grp_lock); return PJ_SUCCESS; } } /* If there's no relayed candidate, find reflexive candidate */ for (i=0; ilcand_cnt; ++i) { pj_ice_sess_cand *lcand = &ice->lcand[i]; if (lcand->comp_id==comp_id && (lcand->type == PJ_ICE_CAND_TYPE_SRFLX || lcand->type == PJ_ICE_CAND_TYPE_PRFLX)) { *cand_id = GET_LCAND_ID(lcand); pj_grp_lock_release(ice->grp_lock); return PJ_SUCCESS; } } /* Otherwise return host candidate */ for (i=0; ilcand_cnt; ++i) { pj_ice_sess_cand *lcand = &ice->lcand[i]; if (lcand->comp_id==comp_id && lcand->type == PJ_ICE_CAND_TYPE_HOST) { *cand_id = GET_LCAND_ID(lcand); pj_grp_lock_release(ice->grp_lock); return PJ_SUCCESS; } } /* Still no candidate is found! :( */ pj_grp_lock_release(ice->grp_lock); pj_assert(!"Should have a candidate by now"); return PJ_EBUG; } #ifndef MIN # define MIN(a,b) (a < b ? a : b) #endif #ifndef MAX # define MAX(a,b) (a > b ? a : b) #endif static pj_timestamp CALC_CHECK_PRIO(const pj_ice_sess *ice, const pj_ice_sess_cand *lcand, const pj_ice_sess_cand *rcand) { pj_uint32_t O, A; pj_timestamp prio; /* Original formula: * pair priority = 2^32*MIN(O,A) + 2*MAX(O,A) + (O>A?1:0) */ if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) { O = lcand->prio; A = rcand->prio; } else { O = rcand->prio; A = lcand->prio; } /* return ((pj_uint64_t)1 << 32) * MIN(O, A) + (pj_uint64_t)2 * MAX(O, A) + (O>A ? 1 : 0); */ prio.u32.hi = MIN(O,A); prio.u32.lo = (MAX(O, A) << 1) + (O>A ? 1 : 0); return prio; } PJ_INLINE(int) CMP_CHECK_PRIO(const pj_ice_sess_check *c1, const pj_ice_sess_check *c2) { return pj_cmp_timestamp(&c1->prio, &c2->prio); } #if PJ_LOG_MAX_LEVEL >= 4 static const char *dump_check(char *buffer, unsigned bufsize, const pj_ice_sess_checklist *clist, const pj_ice_sess_check *check) { const pj_ice_sess_cand *lcand = check->lcand; const pj_ice_sess_cand *rcand = check->rcand; char laddr[PJ_INET6_ADDRSTRLEN], raddr[PJ_INET6_ADDRSTRLEN]; int len; PJ_CHECK_STACK(); len = pj_ansi_snprintf(buffer, bufsize, "%d: [%d] %s:%d-->%s:%d", (int)GET_CHECK_ID(clist, check), check->lcand->comp_id, pj_sockaddr_print(&lcand->addr, laddr, sizeof(laddr), 0), pj_sockaddr_get_port(&lcand->addr), pj_sockaddr_print(&rcand->addr, raddr, sizeof(raddr), 0), pj_sockaddr_get_port(&rcand->addr)); if (len < 0) len = 0; else if (len >= (int)bufsize) len = bufsize - 1; buffer[len] = '\0'; return buffer; } static void dump_checklist(const char *title, pj_ice_sess *ice, const pj_ice_sess_checklist *clist) { unsigned i; LOG4((ice->obj_name, "%s", title)); for (i=0; icount; ++i) { const pj_ice_sess_check *c = &clist->checks[i]; LOG4((ice->obj_name, " %s (%s, state=%s)", dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, c), (c->nominated ? "nominated" : "not nominated"), check_state_name[c->state])); } } #else #define dump_checklist(title, ice, clist) #endif static void check_set_state(pj_ice_sess *ice, pj_ice_sess_check *check, pj_ice_sess_check_state st, pj_status_t err_code) { pj_assert(check->state < PJ_ICE_SESS_CHECK_STATE_SUCCEEDED); LOG5((ice->obj_name, "Check %s: state changed from %s to %s", dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), check_state_name[check->state], check_state_name[st])); check->state = st; check->err_code = err_code; } static void clist_set_state(pj_ice_sess *ice, pj_ice_sess_checklist *clist, pj_ice_sess_checklist_state st) { if (clist->state != st) { LOG5((ice->obj_name, "Checklist: state changed from %s to %s", clist_state_name[clist->state], clist_state_name[st])); clist->state = st; } } /* Sort checklist based on priority */ static void sort_checklist(pj_ice_sess *ice, pj_ice_sess_checklist *clist) { unsigned i; pj_ice_sess_check **check_ptr[PJ_ICE_MAX_COMP*2]; unsigned check_ptr_cnt = 0; for (i=0; icomp_cnt; ++i) { if (ice->comp[i].valid_check) { check_ptr[check_ptr_cnt++] = &ice->comp[i].valid_check; } if (ice->comp[i].nominated_check) { check_ptr[check_ptr_cnt++] = &ice->comp[i].nominated_check; } } pj_assert(clist->count > 0); for (i=0; icount-1; ++i) { unsigned j, highest = i; for (j=i+1; jcount; ++j) { if (CMP_CHECK_PRIO(&clist->checks[j], &clist->checks[highest]) > 0) { highest = j; } } if (highest != i) { pj_ice_sess_check tmp; unsigned k; pj_memcpy(&tmp, &clist->checks[i], sizeof(pj_ice_sess_check)); pj_memcpy(&clist->checks[i], &clist->checks[highest], sizeof(pj_ice_sess_check)); pj_memcpy(&clist->checks[highest], &tmp, sizeof(pj_ice_sess_check)); /* Update valid and nominated check pointers, since we're moving * around checks */ for (k=0; kchecks[highest]) *check_ptr[k] = &clist->checks[i]; else if (*check_ptr[k] == &clist->checks[i]) *check_ptr[k] = &clist->checks[highest]; } } } } /* Prune checklist, this must have been done after the checklist * is sorted. */ static pj_status_t prune_checklist(pj_ice_sess *ice, pj_ice_sess_checklist *clist) { unsigned i; /* Since an agent cannot send requests directly from a reflexive * candidate, but only from its base, the agent next goes through the * sorted list of candidate pairs. For each pair where the local * candidate is server reflexive, the server reflexive candidate MUST be * replaced by its base. Once this has been done, the agent MUST prune * the list. This is done by removing a pair if its local and remote * candidates are identical to the local and remote candidates of a pair * higher up on the priority list. The result is a sequence of ordered * candidate pairs, called the check list for that media stream. */ /* First replace SRFLX candidates with their base */ for (i=0; icount; ++i) { pj_ice_sess_cand *srflx = clist->checks[i].lcand; if (clist->checks[i].lcand->type == PJ_ICE_CAND_TYPE_SRFLX) { /* Find the base for this candidate */ unsigned j; for (j=0; jlcand_cnt; ++j) { pj_ice_sess_cand *host = &ice->lcand[j]; if (host->type != PJ_ICE_CAND_TYPE_HOST) continue; if (pj_sockaddr_cmp(&srflx->base_addr, &host->addr) == 0) { /* Replace this SRFLX with its BASE */ clist->checks[i].lcand = host; break; } } if (j==ice->lcand_cnt) { char baddr[PJ_INET6_ADDRSTRLEN]; /* Host candidate not found this this srflx! */ LOG4((ice->obj_name, "Base candidate %s:%d not found for srflx candidate %d", pj_sockaddr_print(&srflx->base_addr, baddr, sizeof(baddr), 0), pj_sockaddr_get_port(&srflx->base_addr), GET_LCAND_ID(clist->checks[i].lcand))); return PJNATH_EICENOHOSTCAND; } } } /* Next remove a pair if its local and remote candidates are identical * to the local and remote candidates of a pair higher up on the priority * list */ /* * Not in ICE! * Remove host candidates if their base are the the same! */ for (i=0; icount; ++i) { pj_ice_sess_cand *licand = clist->checks[i].lcand; pj_ice_sess_cand *ricand = clist->checks[i].rcand; unsigned j; for (j=i+1; jcount;) { pj_ice_sess_cand *ljcand = clist->checks[j].lcand; pj_ice_sess_cand *rjcand = clist->checks[j].rcand; const char *reason = NULL; if ((licand == ljcand) && (ricand == rjcand)) { reason = "duplicate found"; } else if ((rjcand == ricand) && (pj_sockaddr_cmp(&ljcand->base_addr, &licand->base_addr)==0)) { reason = "equal base"; } if (reason != NULL) { /* Found duplicate, remove it */ LOG5((ice->obj_name, "Check %s pruned (%s)", dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, &clist->checks[j]), reason)); pj_array_erase(clist->checks, sizeof(clist->checks[0]), clist->count, j); --clist->count; } else { ++j; } } } return PJ_SUCCESS; } /* Timer callback */ static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te) { pj_ice_sess *ice = (pj_ice_sess*) te->user_data; enum timer_type type = (enum timer_type)te->id; PJ_UNUSED_ARG(th); pj_grp_lock_acquire(ice->grp_lock); te->id = TIMER_NONE; if (ice->is_destroying) { /* Stray timer, could happen when destroy is invoked while callback * is pending. */ pj_grp_lock_release(ice->grp_lock); return; } switch (type) { case TIMER_CONTROLLED_WAIT_NOM: LOG4((ice->obj_name, "Controlled agent timed-out in waiting for the controlling " "agent to send nominated check. Setting state to fail now..")); on_ice_complete(ice, PJNATH_EICENOMTIMEOUT); break; case TIMER_COMPLETION_CALLBACK: { void (*on_ice_complete)(pj_ice_sess *ice, pj_status_t status); pj_status_t ice_status; /* Start keep-alive timer but don't send any packets yet. * Need to do it here just in case app destroy the session * in the callback. */ if (ice->ice_status == PJ_SUCCESS) ice_keep_alive(ice, PJ_FALSE); /* Release mutex in case app destroy us in the callback */ ice_status = ice->ice_status; on_ice_complete = ice->cb.on_ice_complete; /* Notify app about ICE completion*/ if (on_ice_complete) (*on_ice_complete)(ice, ice_status); } break; case TIMER_START_NOMINATED_CHECK: start_nominated_check(ice); break; case TIMER_KEEP_ALIVE: ice_keep_alive(ice, PJ_TRUE); break; case TIMER_NONE: /* Nothing to do, just to get rid of gcc warning */ break; } pj_grp_lock_release(ice->grp_lock); } /* Send keep-alive */ static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now) { if (send_now) { /* Send Binding Indication for the component */ pj_ice_sess_comp *comp = &ice->comp[ice->comp_ka]; pj_stun_tx_data *tdata; pj_ice_sess_check *the_check; pj_ice_msg_data *msg_data; int addr_len; pj_bool_t saved; pj_status_t status; /* Must have nominated check by now */ pj_assert(comp->nominated_check != NULL); the_check = comp->nominated_check; /* Create the Binding Indication */ status = pj_stun_session_create_ind(comp->stun_sess, PJ_STUN_BINDING_INDICATION, &tdata); if (status != PJ_SUCCESS) goto done; /* Need the transport_id */ msg_data = PJ_POOL_ZALLOC_T(tdata->pool, pj_ice_msg_data); msg_data->transport_id = the_check->lcand->transport_id; /* Temporarily disable FINGERPRINT. The Binding Indication * SHOULD NOT contain any attributes. */ saved = pj_stun_session_use_fingerprint(comp->stun_sess, PJ_FALSE); /* Send to session */ addr_len = pj_sockaddr_get_len(&the_check->rcand->addr); status = pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE, PJ_FALSE, &the_check->rcand->addr, addr_len, tdata); /* Restore FINGERPRINT usage */ pj_stun_session_use_fingerprint(comp->stun_sess, saved); done: ice->comp_ka = (ice->comp_ka + 1) % ice->comp_cnt; } if (ice->timer.id == TIMER_NONE) { pj_time_val delay = { 0, 0 }; delay.msec = (PJ_ICE_SESS_KEEP_ALIVE_MIN + (pj_rand() % PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND)) * 1000 / ice->comp_cnt; pj_time_val_normalize(&delay); pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap, &ice->timer, &delay, TIMER_KEEP_ALIVE, ice->grp_lock); } else { pj_assert(!"Not expected any timer active"); } } /* This function is called when ICE processing completes */ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) { if (!ice->is_complete) { ice->is_complete = PJ_TRUE; ice->ice_status = status; pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer, TIMER_NONE); /* Log message */ LOG4((ice->obj_name, "ICE process complete, status=%s", pj_strerror(status, ice->tmp.errmsg, sizeof(ice->tmp.errmsg)).ptr)); dump_checklist("Valid list", ice, &ice->valid_list); /* Call callback */ if (ice->cb.on_ice_complete) { pj_time_val delay = {0, 0}; pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap, &ice->timer, &delay, TIMER_COMPLETION_CALLBACK, ice->grp_lock); } } } /* Update valid check and nominated check for the candidate */ static void update_comp_check(pj_ice_sess *ice, unsigned comp_id, pj_ice_sess_check *check) { pj_ice_sess_comp *comp; comp = find_comp(ice, comp_id); if (comp->valid_check == NULL) { comp->valid_check = check; } else { if (CMP_CHECK_PRIO(comp->valid_check, check) < 0) comp->valid_check = check; } if (check->nominated) { /* Update the nominated check for the component */ if (comp->nominated_check == NULL) { comp->nominated_check = check; } else { if (CMP_CHECK_PRIO(comp->nominated_check, check) < 0) comp->nominated_check = check; } } } /* This function is called when one check completes */ static pj_bool_t on_check_complete(pj_ice_sess *ice, pj_ice_sess_check *check) { pj_ice_sess_comp *comp; unsigned i; pj_assert(check->state >= PJ_ICE_SESS_CHECK_STATE_SUCCEEDED); comp = find_comp(ice, check->lcand->comp_id); /* 7.1.2.2.2. Updating Pair States * * The agent sets the state of the pair that generated the check to * Succeeded. The success of this check might also cause the state of * other checks to change as well. The agent MUST perform the following * two steps: * * 1. The agent changes the states for all other Frozen pairs for the * same media stream and same foundation to Waiting. Typically * these other pairs will have different component IDs but not * always. */ if (check->err_code==PJ_SUCCESS) { for (i=0; iclist.count; ++i) { pj_ice_sess_check *c = &ice->clist.checks[i]; if (pj_strcmp(&c->lcand->foundation, &check->lcand->foundation)==0 && c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) { check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, 0); } } LOG5((ice->obj_name, "Check %d is successful%s", GET_CHECK_ID(&ice->clist, check), (check->nominated ? " and nominated" : ""))); } /* 8.2. Updating States * * For both controlling and controlled agents, the state of ICE * processing depends on the presence of nominated candidate pairs in * the valid list and on the state of the check list: * * o If there are no nominated pairs in the valid list for a media * stream and the state of the check list is Running, ICE processing * continues. * * o If there is at least one nominated pair in the valid list: * * - The agent MUST remove all Waiting and Frozen pairs in the check * list for the same component as the nominated pairs for that * media stream * * - If an In-Progress pair in the check list is for the same * component as a nominated pair, the agent SHOULD cease * retransmissions for its check if its pair priority is lower * than the lowest priority nominated pair for that component */ if (check->err_code==PJ_SUCCESS && check->nominated) { for (i=0; iclist.count; ++i) { pj_ice_sess_check *c = &ice->clist.checks[i]; if (c->lcand->comp_id == check->lcand->comp_id) { if (c->state < PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) { /* Just fail Frozen/Waiting check */ LOG5((ice->obj_name, "Check %s to be failed because state is %s", dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, c), check_state_name[c->state])); check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_FAILED, PJ_ECANCELLED); } else if (c->state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS && (PJ_ICE_CANCEL_ALL || CMP_CHECK_PRIO(c, check) < 0)) { /* State is IN_PROGRESS, cancel transaction */ if (c->tdata) { LOG5((ice->obj_name, "Cancelling check %s (In Progress)", dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, c))); pj_stun_session_cancel_req(comp->stun_sess, c->tdata, PJ_FALSE, 0); c->tdata = NULL; check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_FAILED, PJ_ECANCELLED); } } } } } /* Still in 8.2. Updating States * * o Once there is at least one nominated pair in the valid list for * every component of at least one media stream and the state of the * check list is Running: * * * The agent MUST change the state of processing for its check * list for that media stream to Completed. * * * The agent MUST continue to respond to any checks it may still * receive for that media stream, and MUST perform triggered * checks if required by the processing of Section 7.2. * * * The agent MAY begin transmitting media for this media stream as * described in Section 11.1 */ /* See if all components have nominated pair. If they do, then mark * ICE processing as success, otherwise wait. */ for (i=0; icomp_cnt; ++i) { if (ice->comp[i].nominated_check == NULL) break; } if (i == ice->comp_cnt) { /* All components have nominated pair */ on_ice_complete(ice, PJ_SUCCESS); return PJ_TRUE; } /* Note: this is the stuffs that we don't do in 7.1.2.2.2, since our * ICE session only supports one media stream for now: * * 7.1.2.2.2. Updating Pair States * * 2. If there is a pair in the valid list for every component of this * media stream (where this is the actual number of components being * used, in cases where the number of components signaled in the SDP * differs from offerer to answerer), the success of this check may * unfreeze checks for other media streams. */ /* 7.1.2.3. Check List and Timer State Updates * Regardless of whether the check was successful or failed, the * completion of the transaction may require updating of check list and * timer states. * * If all of the pairs in the check list are now either in the Failed or * Succeeded state, and there is not a pair in the valid list for each * component of the media stream, the state of the check list is set to * Failed. */ /* * See if all checks in the checklist have completed. If we do, * then mark ICE processing as failed. */ for (i=0; iclist.count; ++i) { pj_ice_sess_check *c = &ice->clist.checks[i]; if (c->state < PJ_ICE_SESS_CHECK_STATE_SUCCEEDED) { break; } } if (i == ice->clist.count) { /* All checks have completed, but we don't have nominated pair. * If agent's role is controlled, check if all components have * valid pair. If it does, this means the controlled agent has * finished the check list and it's waiting for controlling * agent to send checks with USE-CANDIDATE flag set. */ if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLED) { for (i=0; i < ice->comp_cnt; ++i) { if (ice->comp[i].valid_check == NULL) break; } if (i < ice->comp_cnt) { /* This component ID doesn't have valid pair. * Mark ICE as failed. */ on_ice_complete(ice, PJNATH_EICEFAILED); return PJ_TRUE; } else { /* All components have a valid pair. * We should wait until we receive nominated checks. */ if (ice->timer.id == TIMER_NONE && ice->opt.controlled_agent_want_nom_timeout >= 0) { pj_time_val delay; delay.sec = 0; delay.msec = ice->opt.controlled_agent_want_nom_timeout; pj_time_val_normalize(&delay); pj_timer_heap_schedule_w_grp_lock( ice->stun_cfg.timer_heap, &ice->timer, &delay, TIMER_CONTROLLED_WAIT_NOM, ice->grp_lock); LOG5((ice->obj_name, "All checks have completed. Controlled agent now " "waits for nomination from controlling agent " "(timeout=%d msec)", ice->opt.controlled_agent_want_nom_timeout)); } return PJ_FALSE; } /* Unreached */ } else if (ice->is_nominating) { /* We are controlling agent and all checks have completed but * there's at least one component without nominated pair (or * more likely we don't have any nominated pairs at all). */ on_ice_complete(ice, PJNATH_EICEFAILED); return PJ_TRUE; } else { /* We are controlling agent and all checks have completed. If * we have valid list for every component, then move on to * sending nominated check, otherwise we have failed. */ for (i=0; icomp_cnt; ++i) { if (ice->comp[i].valid_check == NULL) break; } if (i < ice->comp_cnt) { /* At least one component doesn't have a valid check. Mark * ICE as failed. */ on_ice_complete(ice, PJNATH_EICEFAILED); return PJ_TRUE; } /* Now it's time to send connectivity check with nomination * flag set. */ LOG4((ice->obj_name, "All checks have completed, starting nominated checks now")); start_nominated_check(ice); return PJ_FALSE; } } /* If this connectivity check has been successful, scan all components * and see if they have a valid pair, if we are controlling and we haven't * started our nominated check yet. */ if (check->err_code == PJ_SUCCESS && ice->role==PJ_ICE_SESS_ROLE_CONTROLLING && !ice->is_nominating && ice->timer.id == TIMER_NONE) { pj_time_val delay; for (i=0; icomp_cnt; ++i) { if (ice->comp[i].valid_check == NULL) break; } if (i < ice->comp_cnt) { /* Some components still don't have valid pair, continue * processing. */ return PJ_FALSE; } LOG4((ice->obj_name, "Scheduling nominated check in %d ms", ice->opt.nominated_check_delay)); pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer, TIMER_NONE); /* All components have valid pair. Let connectivity checks run for * a little bit more time, then start our nominated check. */ delay.sec = 0; delay.msec = ice->opt.nominated_check_delay; pj_time_val_normalize(&delay); pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap, &ice->timer, &delay, TIMER_START_NOMINATED_CHECK, ice->grp_lock); return PJ_FALSE; } /* We still have checks to perform */ return PJ_FALSE; } /* Create checklist by pairing local candidates with remote candidates */ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list( pj_ice_sess *ice, const pj_str_t *rem_ufrag, const pj_str_t *rem_passwd, unsigned rem_cand_cnt, const pj_ice_sess_cand rem_cand[]) { pj_ice_sess_checklist *clist; char buf[128]; pj_str_t username; timer_data *td; unsigned i, j; unsigned highest_comp = 0; pj_status_t status; PJ_ASSERT_RETURN(ice && rem_ufrag && rem_passwd && rem_cand_cnt && rem_cand, PJ_EINVAL); PJ_ASSERT_RETURN(rem_cand_cnt + ice->rcand_cnt <= PJ_ICE_MAX_CAND, PJ_ETOOMANY); pj_grp_lock_acquire(ice->grp_lock); /* Save credentials */ username.ptr = buf; pj_strcpy(&username, rem_ufrag); pj_strcat2(&username, ":"); pj_strcat(&username, &ice->rx_ufrag); pj_strdup(ice->pool, &ice->tx_uname, &username); pj_strdup(ice->pool, &ice->tx_ufrag, rem_ufrag); pj_strdup(ice->pool, &ice->tx_pass, rem_passwd); pj_strcpy(&username, &ice->rx_ufrag); pj_strcat2(&username, ":"); pj_strcat(&username, rem_ufrag); pj_strdup(ice->pool, &ice->rx_uname, &username); /* Save remote candidates */ ice->rcand_cnt = 0; for (i=0; ircand[ice->rcand_cnt]; /* Ignore candidate which has no matching component ID */ if (rem_cand[i].comp_id==0 || rem_cand[i].comp_id > ice->comp_cnt) { continue; } if (rem_cand[i].comp_id > highest_comp) highest_comp = rem_cand[i].comp_id; pj_memcpy(cn, &rem_cand[i], sizeof(pj_ice_sess_cand)); pj_strdup(ice->pool, &cn->foundation, &rem_cand[i].foundation); ice->rcand_cnt++; } /* Generate checklist */ clist = &ice->clist; for (i=0; ilcand_cnt; ++i) { for (j=0; jrcand_cnt; ++j) { pj_ice_sess_cand *lcand = &ice->lcand[i]; pj_ice_sess_cand *rcand = &ice->rcand[j]; pj_ice_sess_check *chk = NULL; if (clist->count >= PJ_ICE_MAX_CHECKS) { pj_grp_lock_release(ice->grp_lock); return PJ_ETOOMANY; } chk = &clist->checks[clist->count]; /* A local candidate is paired with a remote candidate if * and only if the two candidates have the same component ID * and have the same IP address version. */ if ((lcand->comp_id != rcand->comp_id) || (lcand->addr.addr.sa_family != rcand->addr.addr.sa_family)) { continue; } chk->lcand = lcand; chk->rcand = rcand; chk->state = PJ_ICE_SESS_CHECK_STATE_FROZEN; chk->prio = CALC_CHECK_PRIO(ice, lcand, rcand); clist->count++; } } /* This could happen if candidates have no matching address families */ if (clist->count == 0) { LOG4((ice->obj_name, "Error: no checklist can be created")); pj_grp_lock_release(ice->grp_lock); return PJ_ENOTFOUND; } /* Sort checklist based on priority */ sort_checklist(ice, clist); /* Prune the checklist */ status = prune_checklist(ice, clist); if (status != PJ_SUCCESS) { pj_grp_lock_release(ice->grp_lock); return status; } /* Disable our components which don't have matching component */ for (i=highest_comp; icomp_cnt; ++i) { if (ice->comp[i].stun_sess) { pj_stun_session_destroy(ice->comp[i].stun_sess); pj_bzero(&ice->comp[i], sizeof(ice->comp[i])); } } ice->comp_cnt = highest_comp; /* Init timer entry in the checklist. Initially the timer ID is FALSE * because timer is not running. */ clist->timer.id = PJ_FALSE; td = PJ_POOL_ZALLOC_T(ice->pool, timer_data); td->ice = ice; td->clist = clist; clist->timer.user_data = (void*)td; clist->timer.cb = &periodic_timer; /* Log checklist */ dump_checklist("Checklist created:", ice, clist); pj_grp_lock_release(ice->grp_lock); return PJ_SUCCESS; } /* Perform check on the specified candidate pair. */ static pj_status_t perform_check(pj_ice_sess *ice, pj_ice_sess_checklist *clist, unsigned check_id, pj_bool_t nominate) { pj_ice_sess_comp *comp; pj_ice_msg_data *msg_data; pj_ice_sess_check *check; const pj_ice_sess_cand *lcand; const pj_ice_sess_cand *rcand; pj_uint32_t prio; pj_status_t status; check = &clist->checks[check_id]; lcand = check->lcand; rcand = check->rcand; comp = find_comp(ice, lcand->comp_id); LOG5((ice->obj_name, "Sending connectivity check for check %s", dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, check))); pj_log_push_indent(); /* Create request */ status = pj_stun_session_create_req(comp->stun_sess, PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC, NULL, &check->tdata); if (status != PJ_SUCCESS) { pjnath_perror(ice->obj_name, "Error creating STUN request", status); pj_log_pop_indent(); return status; } /* Attach data to be retrieved later when STUN request transaction * completes and on_stun_request_complete() callback is called. */ msg_data = PJ_POOL_ZALLOC_T(check->tdata->pool, pj_ice_msg_data); msg_data->transport_id = lcand->transport_id; msg_data->has_req_data = PJ_TRUE; msg_data->data.req.ice = ice; msg_data->data.req.clist = clist; msg_data->data.req.ckid = check_id; /* Add PRIORITY */ #if PJNATH_ICE_PRIO_STD prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 65535, lcand->comp_id); #else prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 0, lcand->comp_id); #endif pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg, PJ_STUN_ATTR_PRIORITY, prio); /* Add USE-CANDIDATE and set this check to nominated. * Also add ICE-CONTROLLING or ICE-CONTROLLED */ if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) { if (nominate) { pj_stun_msg_add_empty_attr(check->tdata->pool, check->tdata->msg, PJ_STUN_ATTR_USE_CANDIDATE); check->nominated = PJ_TRUE; } pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, PJ_STUN_ATTR_ICE_CONTROLLING, &ice->tie_breaker); } else { pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, PJ_STUN_ATTR_ICE_CONTROLLED, &ice->tie_breaker); } /* Note that USERNAME and MESSAGE-INTEGRITY will be added by the * STUN session. */ /* Initiate STUN transaction to send the request */ status = pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE, PJ_TRUE, &rcand->addr, pj_sockaddr_get_len(&rcand->addr), check->tdata); if (status != PJ_SUCCESS) { check->tdata = NULL; pjnath_perror(ice->obj_name, "Error sending STUN request", status); pj_log_pop_indent(); return status; } check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, PJ_SUCCESS); pj_log_pop_indent(); return PJ_SUCCESS; } /* Start periodic check for the specified checklist. * This callback is called by timer on every Ta (20msec by default) */ static pj_status_t start_periodic_check(pj_timer_heap_t *th, pj_timer_entry *te) { timer_data *td; pj_ice_sess *ice; pj_ice_sess_checklist *clist; unsigned i, start_count=0; pj_status_t status; td = (struct timer_data*) te->user_data; ice = td->ice; clist = td->clist; pj_grp_lock_acquire(ice->grp_lock); if (ice->is_destroying) { pj_grp_lock_release(ice->grp_lock); return PJ_SUCCESS; } /* Set timer ID to FALSE first */ te->id = PJ_FALSE; /* Set checklist state to Running */ clist_set_state(ice, clist, PJ_ICE_SESS_CHECKLIST_ST_RUNNING); LOG5((ice->obj_name, "Starting checklist periodic check")); pj_log_push_indent(); /* Send STUN Binding request for check with highest priority on * Waiting state. */ for (i=0; icount; ++i) { pj_ice_sess_check *check = &clist->checks[i]; if (check->state == PJ_ICE_SESS_CHECK_STATE_WAITING) { status = perform_check(ice, clist, i, ice->is_nominating); if (status != PJ_SUCCESS) { pj_grp_lock_release(ice->grp_lock); pj_log_pop_indent(); return status; } ++start_count; break; } } /* If we don't have anything in Waiting state, perform check to * highest priority pair that is in Frozen state. */ if (start_count==0) { for (i=0; icount; ++i) { pj_ice_sess_check *check = &clist->checks[i]; if (check->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) { status = perform_check(ice, clist, i, ice->is_nominating); if (status != PJ_SUCCESS) { pj_grp_lock_release(ice->grp_lock); pj_log_pop_indent(); return status; } ++start_count; break; } } } /* Cannot start check because there's no suitable candidate pair. */ if (start_count!=0) { /* Schedule for next timer */ pj_time_val timeout = {0, PJ_ICE_TA_VAL}; pj_time_val_normalize(&timeout); pj_timer_heap_schedule_w_grp_lock(th, te, &timeout, PJ_TRUE, ice->grp_lock); } pj_grp_lock_release(ice->grp_lock); pj_log_pop_indent(); return PJ_SUCCESS; } /* Start sending connectivity check with USE-CANDIDATE */ static void start_nominated_check(pj_ice_sess *ice) { pj_time_val delay; unsigned i; pj_status_t status; LOG4((ice->obj_name, "Starting nominated check..")); pj_log_push_indent(); pj_assert(ice->is_nominating == PJ_FALSE); /* Stop our timer if it's active */ if (ice->timer.id == TIMER_START_NOMINATED_CHECK) { pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer, TIMER_NONE); } /* For each component, set the check state of valid check with * highest priority to Waiting (it should have Success state now). */ for (i=0; icomp_cnt; ++i) { unsigned j; const pj_ice_sess_check *vc = ice->comp[i].valid_check; pj_assert(ice->comp[i].nominated_check == NULL); pj_assert(vc->err_code == PJ_SUCCESS); for (j=0; jclist.count; ++j) { pj_ice_sess_check *c = &ice->clist.checks[j]; if (c->lcand->transport_id == vc->lcand->transport_id && c->rcand == vc->rcand) { pj_assert(c->err_code == PJ_SUCCESS); c->state = PJ_ICE_SESS_CHECK_STATE_FROZEN; check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, PJ_SUCCESS); break; } } } /* And (re)start the periodic check */ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->clist.timer, PJ_FALSE); delay.sec = delay.msec = 0; status = pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap, &ice->clist.timer, &delay, PJ_TRUE, ice->grp_lock); if (status == PJ_SUCCESS) { LOG5((ice->obj_name, "Periodic timer rescheduled..")); } ice->is_nominating = PJ_TRUE; pj_log_pop_indent(); } /* Timer callback to perform periodic check */ static void periodic_timer(pj_timer_heap_t *th, pj_timer_entry *te) { start_periodic_check(th, te); } /* Utility: find string in string array */ static const pj_str_t *find_str(const pj_str_t *strlist[], unsigned count, const pj_str_t *str) { unsigned i; for (i=0; iclist.count > 0, PJ_EINVALIDOP); /* Lock session */ pj_grp_lock_acquire(ice->grp_lock); LOG4((ice->obj_name, "Starting ICE check..")); pj_log_push_indent(); /* If we are using aggressive nomination, set the is_nominating state */ if (ice->opt.aggressive) ice->is_nominating = PJ_TRUE; /* The agent examines the check list for the first media stream (a * media stream is the first media stream when it is described by * the first m-line in the SDP offer and answer). For that media * stream, it: * * - Groups together all of the pairs with the same foundation, * * - For each group, sets the state of the pair with the lowest * component ID to Waiting. If there is more than one such pair, * the one with the highest priority is used. */ clist = &ice->clist; /* Pickup the first pair for component 1. */ for (i=0; icount; ++i) { if (clist->checks[i].lcand->comp_id == 1) break; } if (i == clist->count) { pj_assert(!"Unable to find checklist for component 1"); pj_grp_lock_release(ice->grp_lock); pj_log_pop_indent(); return PJNATH_EICEINCOMPID; } /* Set this check to WAITING only if state is frozen. It may be possible * that this check has already been started by a trigger check */ if (clist->checks[i].state == PJ_ICE_SESS_CHECK_STATE_FROZEN) { check_set_state(ice, &clist->checks[i], PJ_ICE_SESS_CHECK_STATE_WAITING, PJ_SUCCESS); } cand0 = clist->checks[i].lcand; flist[flist_cnt++] = &clist->checks[i].lcand->foundation; /* Find all of the other pairs in that check list with the same * component ID, but different foundations, and sets all of their * states to Waiting as well. */ for (++i; icount; ++i) { const pj_ice_sess_cand *cand1; cand1 = clist->checks[i].lcand; if (cand1->comp_id==cand0->comp_id && find_str(flist, flist_cnt, &cand1->foundation)==NULL) { if (clist->checks[i].state == PJ_ICE_SESS_CHECK_STATE_FROZEN) { check_set_state(ice, &clist->checks[i], PJ_ICE_SESS_CHECK_STATE_WAITING, PJ_SUCCESS); } flist[flist_cnt++] = &cand1->foundation; } } /* First, perform all pending triggered checks, simultaneously. */ rcheck = ice->early_check.next; while (rcheck != &ice->early_check) { LOG4((ice->obj_name, "Performing delayed triggerred check for component %d", rcheck->comp_id)); pj_log_push_indent(); handle_incoming_check(ice, rcheck); rcheck = rcheck->next; pj_log_pop_indent(); } pj_list_init(&ice->early_check); /* Start periodic check */ /* We could start it immediately like below, but lets schedule timer * instead to reduce stack usage: * return start_periodic_check(ice->stun_cfg.timer_heap, &clist->timer); */ delay.sec = delay.msec = 0; status = pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap, &clist->timer, &delay, PJ_TRUE, ice->grp_lock); if (status != PJ_SUCCESS) { clist->timer.id = PJ_FALSE; } pj_grp_lock_release(ice->grp_lock); pj_log_pop_indent(); return status; } ////////////////////////////////////////////////////////////////////////////// /* Callback called by STUN session to send the STUN message. * STUN session also doesn't have a transport, remember?! */ static pj_status_t on_stun_send_msg(pj_stun_session *sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len) { stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess); pj_ice_sess *ice = sd->ice; pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token; pj_status_t status; pj_grp_lock_acquire(ice->grp_lock); if (ice->is_destroying) { /* Stray retransmit timer that could happen while * we're being destroyed */ pj_grp_lock_release(ice->grp_lock); return PJ_EINVALIDOP; } status = (*ice->cb.on_tx_pkt)(ice, sd->comp_id, msg_data->transport_id, pkt, pkt_size, dst_addr, addr_len); pj_grp_lock_release(ice->grp_lock); return status; } /* This callback is called when outgoing STUN request completed */ static void on_stun_request_complete(pj_stun_session *stun_sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token; pj_ice_sess *ice; pj_ice_sess_check *check, *new_check; pj_ice_sess_cand *lcand; pj_ice_sess_checklist *clist; pj_stun_xor_mapped_addr_attr *xaddr; unsigned i; PJ_UNUSED_ARG(stun_sess); PJ_UNUSED_ARG(src_addr_len); pj_assert(msg_data->has_req_data); ice = msg_data->data.req.ice; clist = msg_data->data.req.clist; check = &clist->checks[msg_data->data.req.ckid]; /* Mark STUN transaction as complete */ pj_assert(tdata == check->tdata); check->tdata = NULL; pj_grp_lock_acquire(ice->grp_lock); if (ice->is_destroying) { /* Not sure if this is possible but just in case */ pj_grp_lock_release(ice->grp_lock); return; } /* Init lcand to NULL. lcand will be found from the mapped address * found in the response. */ lcand = NULL; if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; if (status==PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_ROLE_CONFLICT)) { /* Role conclict response. * * 7.1.2.1. Failure Cases: * * If the request had contained the ICE-CONTROLLED attribute, * the agent MUST switch to the controlling role if it has not * already done so. If the request had contained the * ICE-CONTROLLING attribute, the agent MUST switch to the * controlled role if it has not already done so. Once it has * switched, the agent MUST immediately retry the request with * the ICE-CONTROLLING or ICE-CONTROLLED attribute reflecting * its new role. */ pj_ice_sess_role new_role = PJ_ICE_SESS_ROLE_UNKNOWN; pj_stun_msg *req = tdata->msg; if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_ICE_CONTROLLING, 0)) { new_role = PJ_ICE_SESS_ROLE_CONTROLLED; } else if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_ICE_CONTROLLED, 0)) { new_role = PJ_ICE_SESS_ROLE_CONTROLLING; } else { pj_assert(!"We should have put CONTROLLING/CONTROLLED attr!"); new_role = PJ_ICE_SESS_ROLE_CONTROLLED; } if (new_role != ice->role) { LOG4((ice->obj_name, "Changing role because of role conflict response")); pj_ice_sess_change_role(ice, new_role); } /* Resend request */ LOG4((ice->obj_name, "Resending check because of role conflict")); pj_log_push_indent(); check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_WAITING, 0); perform_check(ice, clist, msg_data->data.req.ckid, check->nominated || ice->is_nominating); pj_log_pop_indent(); pj_grp_lock_release(ice->grp_lock); return; } pj_strerror(status, errmsg, sizeof(errmsg)); LOG4((ice->obj_name, "Check %s%s: connectivity check FAILED: %s", dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), (check->nominated ? " (nominated)" : " (not nominated)"), errmsg)); pj_log_push_indent(); check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); on_check_complete(ice, check); pj_log_pop_indent(); pj_grp_lock_release(ice->grp_lock); return; } /* 7.1.2.1. Failure Cases * * The agent MUST check that the source IP address and port of the * response equals the destination IP address and port that the Binding * Request was sent to, and that the destination IP address and port of * the response match the source IP address and port that the Binding * Request was sent from. */ if (pj_sockaddr_cmp(&check->rcand->addr, (const pj_sockaddr*)src_addr)!=0) { status = PJNATH_EICEINSRCADDR; LOG4((ice->obj_name, "Check %s%s: connectivity check FAILED: source address mismatch", dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), (check->nominated ? " (nominated)" : " (not nominated)"))); pj_log_push_indent(); check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); on_check_complete(ice, check); pj_log_pop_indent(); pj_grp_lock_release(ice->grp_lock); return; } /* 7.1.2.2. Success Cases * * A check is considered to be a success if all of the following are * true: * * o the STUN transaction generated a success response * * o the source IP address and port of the response equals the * destination IP address and port that the Binding Request was sent * to * * o the destination IP address and port of the response match the * source IP address and port that the Binding Request was sent from */ LOG4((ice->obj_name, "Check %s%s: connectivity check SUCCESS", dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), (check->nominated ? " (nominated)" : " (not nominated)"))); /* Get the STUN XOR-MAPPED-ADDRESS attribute. */ xaddr = (pj_stun_xor_mapped_addr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR,0); if (!xaddr) { check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, PJNATH_ESTUNNOMAPPEDADDR); on_check_complete(ice, check); pj_grp_lock_release(ice->grp_lock); return; } /* Find local candidate that matches the XOR-MAPPED-ADDRESS */ pj_assert(lcand == NULL); for (i=0; ilcand_cnt; ++i) { /* Ticket #1891: apply additional check as there may be a shared * mapped address for different base/local addresses. */ if (pj_sockaddr_cmp(&xaddr->sockaddr, &ice->lcand[i].addr) == 0 && pj_sockaddr_cmp(&check->lcand->base_addr, &ice->lcand[i].base_addr) == 0) { /* Match */ lcand = &ice->lcand[i]; break; } } /* 7.1.2.2.1. Discovering Peer Reflexive Candidates * If the transport address returned in XOR-MAPPED-ADDRESS does not match * any of the local candidates that the agent knows about, the mapped * address represents a new candidate - a peer reflexive candidate. */ if (lcand == NULL) { unsigned cand_id; pj_str_t foundation; pj_ice_calc_foundation(ice->pool, &foundation, PJ_ICE_CAND_TYPE_PRFLX, &check->lcand->base_addr); /* Still in 7.1.2.2.1. Discovering Peer Reflexive Candidates * Its priority is set equal to the value of the PRIORITY attribute * in the Binding Request. * * I think the priority calculated by add_cand() should be the same * as the one calculated in perform_check(), so there's no need to * get the priority from the PRIORITY attribute. */ /* Add new peer reflexive candidate */ status = pj_ice_sess_add_cand(ice, check->lcand->comp_id, msg_data->transport_id, PJ_ICE_CAND_TYPE_PRFLX, 65535, &foundation, &xaddr->sockaddr, &check->lcand->base_addr, &check->lcand->base_addr, pj_sockaddr_get_len(&xaddr->sockaddr), &cand_id); if (status != PJ_SUCCESS) { check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); on_check_complete(ice, check); pj_grp_lock_release(ice->grp_lock); return; } /* Update local candidate */ lcand = &ice->lcand[cand_id]; } /* 7.1.2.2.3. Constructing a Valid Pair * Next, the agent constructs a candidate pair whose local candidate * equals the mapped address of the response, and whose remote candidate * equals the destination address to which the request was sent. */ /* Add pair to valid list, if it's not there, otherwise just update * nominated flag */ for (i=0; ivalid_list.count; ++i) { if (ice->valid_list.checks[i].lcand == lcand && ice->valid_list.checks[i].rcand == check->rcand) break; } if (i==ice->valid_list.count) { pj_assert(ice->valid_list.count < PJ_ICE_MAX_CHECKS); new_check = &ice->valid_list.checks[ice->valid_list.count++]; new_check->lcand = lcand; new_check->rcand = check->rcand; new_check->prio = CALC_CHECK_PRIO(ice, lcand, check->rcand); new_check->state = PJ_ICE_SESS_CHECK_STATE_SUCCEEDED; new_check->nominated = check->nominated; new_check->err_code = PJ_SUCCESS; } else { new_check = &ice->valid_list.checks[i]; ice->valid_list.checks[i].nominated = check->nominated; } /* Update valid check and nominated check for the component */ update_comp_check(ice, new_check->lcand->comp_id, new_check); /* Sort valid_list (must do so after update_comp_check(), otherwise * new_check will point to something else (#953) */ sort_checklist(ice, &ice->valid_list); /* 7.1.2.2.2. Updating Pair States * * The agent sets the state of the pair that generated the check to * Succeeded. The success of this check might also cause the state of * other checks to change as well. */ check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_SUCCEEDED, PJ_SUCCESS); /* Perform 7.1.2.2.2. Updating Pair States. * This may terminate ICE processing. */ if (on_check_complete(ice, check)) { /* ICE complete! */ pj_grp_lock_release(ice->grp_lock); return; } pj_grp_lock_release(ice->grp_lock); } /* This callback is called by the STUN session associated with a candidate * when it receives incoming request. */ static pj_status_t on_stun_rx_request(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_rx_data *rdata, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { stun_data *sd; const pj_stun_msg *msg = rdata->msg; pj_ice_msg_data *msg_data; pj_ice_sess *ice; pj_stun_priority_attr *prio_attr; pj_stun_use_candidate_attr *uc_attr; pj_stun_uint64_attr *role_attr; pj_stun_tx_data *tdata; pj_ice_rx_check *rcheck, tmp_rcheck; pj_status_t status; PJ_UNUSED_ARG(pkt); PJ_UNUSED_ARG(pkt_len); /* Reject any requests except Binding request */ if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) { pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, NULL, token, PJ_TRUE, src_addr, src_addr_len); return PJ_SUCCESS; } sd = (stun_data*) pj_stun_session_get_user_data(sess); ice = sd->ice; pj_grp_lock_acquire(ice->grp_lock); if (ice->is_destroying) { pj_grp_lock_release(ice->grp_lock); return PJ_EINVALIDOP; } /* * Note: * Be aware that when STUN request is received, we might not get * SDP answer yet, so we might not have remote candidates and * checklist yet. This case will be handled after we send * a response. */ /* Get PRIORITY attribute */ prio_attr = (pj_stun_priority_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PRIORITY, 0); if (prio_attr == NULL) { LOG5((ice->obj_name, "Received Binding request with no PRIORITY")); pj_grp_lock_release(ice->grp_lock); return PJ_SUCCESS; } /* Get USE-CANDIDATE attribute */ uc_attr = (pj_stun_use_candidate_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USE_CANDIDATE, 0); /* Get ICE-CONTROLLING or ICE-CONTROLLED */ role_attr = (pj_stun_uint64_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ICE_CONTROLLING, 0); if (role_attr == NULL) { role_attr = (pj_stun_uint64_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ICE_CONTROLLED, 0); } /* Handle the case when request comes before answer is received. * We need to put credential in the response, and since we haven't * got the response, copy the username from the request. */ if (ice->rcand_cnt == 0) { pj_stun_string_attr *uname_attr; uname_attr = (pj_stun_string_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0); pj_assert(uname_attr != NULL); pj_strdup(ice->pool, &ice->rx_uname, &uname_attr->value); } /* 7.2.1.1. Detecting and Repairing Role Conflicts */ if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING && role_attr && role_attr->hdr.type == PJ_STUN_ATTR_ICE_CONTROLLING) { if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) { /* Switch role to controlled */ LOG4((ice->obj_name, "Changing role because of ICE-CONTROLLING attribute")); pj_ice_sess_change_role(ice, PJ_ICE_SESS_ROLE_CONTROLLED); } else { /* Generate 487 response */ pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, NULL, token, PJ_TRUE, src_addr, src_addr_len); pj_grp_lock_release(ice->grp_lock); return PJ_SUCCESS; } } else if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLED && role_attr && role_attr->hdr.type == PJ_STUN_ATTR_ICE_CONTROLLED) { if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) { /* Generate 487 response */ pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, NULL, token, PJ_TRUE, src_addr, src_addr_len); pj_grp_lock_release(ice->grp_lock); return PJ_SUCCESS; } else { /* Switch role to controlled */ LOG4((ice->obj_name, "Changing role because of ICE-CONTROLLED attribute")); pj_ice_sess_change_role(ice, PJ_ICE_SESS_ROLE_CONTROLLING); } } /* * First send response to this request */ status = pj_stun_session_create_res(sess, rdata, 0, NULL, &tdata); if (status != PJ_SUCCESS) { pj_grp_lock_release(ice->grp_lock); return status; } /* Add XOR-MAPPED-ADDRESS attribute */ status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, src_addr, src_addr_len); /* Create a msg_data to be associated with this response */ msg_data = PJ_POOL_ZALLOC_T(tdata->pool, pj_ice_msg_data); msg_data->transport_id = ((pj_ice_msg_data*)token)->transport_id; msg_data->has_req_data = PJ_FALSE; /* Send the response */ status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, PJ_TRUE, src_addr, src_addr_len, tdata); /* * Handling early check. * * It's possible that we receive this request before we receive SDP * answer. In this case, we can't perform trigger check since we * don't have checklist yet, so just save this check in a pending * triggered check array to be acted upon later. */ if (ice->rcand_cnt == 0) { rcheck = PJ_POOL_ZALLOC_T(ice->pool, pj_ice_rx_check); } else { rcheck = &tmp_rcheck; } /* Init rcheck */ rcheck->comp_id = sd->comp_id; rcheck->transport_id = ((pj_ice_msg_data*)token)->transport_id; rcheck->src_addr_len = src_addr_len; pj_sockaddr_cp(&rcheck->src_addr, src_addr); rcheck->use_candidate = (uc_attr != NULL); rcheck->priority = prio_attr->value; rcheck->role_attr = role_attr; if (ice->rcand_cnt == 0) { /* We don't have answer yet, so keep this request for later */ LOG4((ice->obj_name, "Received an early check for comp %d", rcheck->comp_id)); pj_list_push_back(&ice->early_check, rcheck); } else { /* Handle this check */ handle_incoming_check(ice, rcheck); } pj_grp_lock_release(ice->grp_lock); return PJ_SUCCESS; } /* Handle incoming Binding request and perform triggered check. * This function may be called by on_stun_rx_request(), or when * SDP answer is received and we have received early checks. */ static void handle_incoming_check(pj_ice_sess *ice, const pj_ice_rx_check *rcheck) { pj_ice_sess_comp *comp; pj_ice_sess_cand *lcand = NULL; pj_ice_sess_cand *rcand; unsigned i; comp = find_comp(ice, rcheck->comp_id); /* Find remote candidate based on the source transport address of * the request. */ for (i=0; ircand_cnt; ++i) { if (pj_sockaddr_cmp(&rcheck->src_addr, &ice->rcand[i].addr)==0) break; } /* 7.2.1.3. Learning Peer Reflexive Candidates * If the source transport address of the request does not match any * existing remote candidates, it represents a new peer reflexive remote * candidate. */ if (i == ice->rcand_cnt) { char raddr[PJ_INET6_ADDRSTRLEN]; if (ice->rcand_cnt >= PJ_ICE_MAX_CAND) { LOG4((ice->obj_name, "Unable to add new peer reflexive candidate: too many " "candidates already (%d)", PJ_ICE_MAX_CAND)); return; } rcand = &ice->rcand[ice->rcand_cnt++]; rcand->comp_id = (pj_uint8_t)rcheck->comp_id; rcand->type = PJ_ICE_CAND_TYPE_PRFLX; rcand->prio = rcheck->priority; pj_sockaddr_cp(&rcand->addr, &rcheck->src_addr); /* Foundation is random, unique from other foundation */ rcand->foundation.ptr = (char*) pj_pool_alloc(ice->pool, 36); rcand->foundation.slen = pj_ansi_snprintf(rcand->foundation.ptr, 36, "f%p", rcand->foundation.ptr); LOG4((ice->obj_name, "Added new remote candidate from the request: %s:%d", pj_sockaddr_print(&rcand->addr, raddr, sizeof(raddr), 0), pj_sockaddr_get_port(&rcand->addr))); } else { /* Remote candidate found */ rcand = &ice->rcand[i]; } #if 0 /* Find again the local candidate by matching the base address * with the local candidates in the checklist. Checks may have * been pruned before, so it's possible that if we use the lcand * as it is, we wouldn't be able to find the check in the checklist * and we will end up creating a new check unnecessarily. */ for (i=0; iclist.count; ++i) { pj_ice_sess_check *c = &ice->clist.checks[i]; if (/*c->lcand == lcand ||*/ pj_sockaddr_cmp(&c->lcand->base_addr, &lcand->base_addr)==0) { lcand = c->lcand; break; } } #else /* Just get candidate with the highest priority and same transport ID * for the specified component ID in the checklist. */ for (i=0; iclist.count; ++i) { pj_ice_sess_check *c = &ice->clist.checks[i]; if (c->lcand->comp_id == rcheck->comp_id && c->lcand->transport_id == rcheck->transport_id) { lcand = c->lcand; break; } } if (lcand == NULL) { /* Should not happen, but just in case remote is sending a * Binding request for a component which it doesn't have. */ LOG4((ice->obj_name, "Received Binding request but no local candidate is found!")); return; } #endif /* * Create candidate pair for this request. */ /* * 7.2.1.4. Triggered Checks * * Now that we have local and remote candidate, check if we already * have this pair in our checklist. */ for (i=0; iclist.count; ++i) { pj_ice_sess_check *c = &ice->clist.checks[i]; if (c->lcand == lcand && c->rcand == rcand) break; } /* If the pair is already on the check list: * - If the state of that pair is Waiting or Frozen, its state is * changed to In-Progress and a check for that pair is performed * immediately. This is called a triggered check. * * - If the state of that pair is In-Progress, the agent SHOULD * generate an immediate retransmit of the Binding Request for the * check in progress. This is to facilitate rapid completion of * ICE when both agents are behind NAT. * * - If the state of that pair is Failed or Succeeded, no triggered * check is sent. */ if (i != ice->clist.count) { pj_ice_sess_check *c = &ice->clist.checks[i]; /* If USE-CANDIDATE is present, set nominated flag * Note: DO NOT overwrite nominated flag if one is already set. */ c->nominated = ((rcheck->use_candidate) || c->nominated); if (c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN || c->state == PJ_ICE_SESS_CHECK_STATE_WAITING) { /* See if we shall nominate this check */ pj_bool_t nominate = (c->nominated || ice->is_nominating); LOG5((ice->obj_name, "Performing triggered check for check %d",i)); pj_log_push_indent(); perform_check(ice, &ice->clist, i, nominate); pj_log_pop_indent(); } else if (c->state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) { /* Should retransmit immediately */ LOG5((ice->obj_name, "Triggered check for check %d not performed " "because it's in progress. Retransmitting", i)); pj_log_push_indent(); pj_stun_session_retransmit_req(comp->stun_sess, c->tdata, PJ_FALSE); pj_log_pop_indent(); } else if (c->state == PJ_ICE_SESS_CHECK_STATE_SUCCEEDED) { /* Check complete for this component. * Note this may end ICE process. */ pj_bool_t complete; unsigned j; /* If this check is nominated, scan the valid_list for the * same check and update the nominated flag. A controlled * agent might have finished the check earlier. */ if (rcheck->use_candidate) { for (j=0; jvalid_list.count; ++j) { pj_ice_sess_check *vc = &ice->valid_list.checks[j]; if (vc->lcand->transport_id == c->lcand->transport_id && vc->rcand == c->rcand) { /* Set nominated flag */ vc->nominated = PJ_TRUE; /* Update valid check and nominated check for the component */ update_comp_check(ice, vc->lcand->comp_id, vc); LOG5((ice->obj_name, "Valid check %s is nominated", dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->valid_list, vc))); } } } LOG5((ice->obj_name, "Triggered check for check %d not performed " "because it's completed", i)); pj_log_push_indent(); complete = on_check_complete(ice, c); pj_log_pop_indent(); if (complete) { return; } } } /* If the pair is not already on the check list: * - The pair is inserted into the check list based on its priority. * - Its state is set to In-Progress * - A triggered check for that pair is performed immediately. */ /* Note: only do this if we don't have too many checks in checklist */ else if (ice->clist.count < PJ_ICE_MAX_CHECKS) { pj_ice_sess_check *c = &ice->clist.checks[ice->clist.count]; pj_bool_t nominate; c->lcand = lcand; c->rcand = rcand; c->prio = CALC_CHECK_PRIO(ice, lcand, rcand); c->state = PJ_ICE_SESS_CHECK_STATE_WAITING; c->nominated = rcheck->use_candidate; c->err_code = PJ_SUCCESS; nominate = (c->nominated || ice->is_nominating); LOG4((ice->obj_name, "New triggered check added: %d", ice->clist.count)); pj_log_push_indent(); perform_check(ice, &ice->clist, ice->clist.count++, nominate); pj_log_pop_indent(); } else { LOG4((ice->obj_name, "Error: unable to perform triggered check: " "TOO MANY CHECKS IN CHECKLIST!")); } } static pj_status_t on_stun_rx_indication(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_msg *msg, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { struct stun_data *sd; PJ_UNUSED_ARG(sess); PJ_UNUSED_ARG(pkt); PJ_UNUSED_ARG(pkt_len); PJ_UNUSED_ARG(msg); PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); sd = (struct stun_data*) pj_stun_session_get_user_data(sess); pj_log_push_indent(); if (msg->hdr.type == PJ_STUN_BINDING_INDICATION) { LOG5((sd->ice->obj_name, "Received Binding Indication keep-alive " "for component %d", sd->comp_id)); } else { LOG4((sd->ice->obj_name, "Received unexpected %s indication " "for component %d", pj_stun_get_method_name(msg->hdr.type), sd->comp_id)); } pj_log_pop_indent(); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice, unsigned comp_id, const void *data, pj_size_t data_len) { pj_status_t status = PJ_SUCCESS; pj_ice_sess_comp *comp; pj_ice_sess_cand *cand; pj_uint8_t transport_id; pj_sockaddr addr; PJ_ASSERT_RETURN(ice && comp_id, PJ_EINVAL); /* It is possible that comp_cnt is less than comp_id, when remote * doesn't support all the components that we have. */ if (comp_id > ice->comp_cnt) { return PJNATH_EICEINCOMPID; } pj_grp_lock_acquire(ice->grp_lock); if (ice->is_destroying) { pj_grp_lock_release(ice->grp_lock); return PJ_EINVALIDOP; } comp = find_comp(ice, comp_id); if (comp == NULL) { status = PJNATH_EICEINCOMPID; pj_grp_lock_release(ice->grp_lock); goto on_return; } if (comp->valid_check == NULL) { status = PJNATH_EICEINPROGRESS; pj_grp_lock_release(ice->grp_lock); goto on_return; } cand = comp->valid_check->lcand; transport_id = cand->transport_id; pj_sockaddr_cp(&addr, &comp->valid_check->rcand->addr); /* Release the mutex now to avoid deadlock (see ticket #1451). */ pj_grp_lock_release(ice->grp_lock); PJ_RACE_ME(5); status = (*ice->cb.on_tx_pkt)(ice, comp_id, transport_id, data, data_len, &addr, pj_sockaddr_get_len(&addr)); on_return: return status; } PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *src_addr, int src_addr_len) { pj_status_t status = PJ_SUCCESS; pj_ice_sess_comp *comp; pj_ice_msg_data *msg_data = NULL; unsigned i; PJ_ASSERT_RETURN(ice, PJ_EINVAL); pj_grp_lock_acquire(ice->grp_lock); if (ice->is_destroying) { pj_grp_lock_release(ice->grp_lock); return PJ_EINVALIDOP; } comp = find_comp(ice, comp_id); if (comp == NULL) { pj_grp_lock_release(ice->grp_lock); return PJNATH_EICEINCOMPID; } /* Find transport */ for (i=0; itp_data); ++i) { if (ice->tp_data[i].transport_id == transport_id) { msg_data = &ice->tp_data[i]; break; } } if (msg_data == NULL) { pj_assert(!"Invalid transport ID"); pj_grp_lock_release(ice->grp_lock); return PJ_EINVAL; } /* Don't check fingerprint. We only need to distinguish STUN and non-STUN * packets. We don't need to verify the STUN packet too rigorously, that * will be done by the user. */ status = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_size, PJ_STUN_IS_DATAGRAM | PJ_STUN_NO_FINGERPRINT_CHECK); if (status == PJ_SUCCESS) { status = pj_stun_session_on_rx_pkt(comp->stun_sess, pkt, pkt_size, PJ_STUN_IS_DATAGRAM, msg_data, NULL, src_addr, src_addr_len); if (status != PJ_SUCCESS) { pj_strerror(status, ice->tmp.errmsg, sizeof(ice->tmp.errmsg)); LOG4((ice->obj_name, "Error processing incoming message: %s", ice->tmp.errmsg)); } pj_grp_lock_release(ice->grp_lock); } else { /* Not a STUN packet. Call application's callback instead, but release * the mutex now or otherwise we may get deadlock. */ pj_grp_lock_release(ice->grp_lock); PJ_RACE_ME(5); (*ice->cb.on_rx_data)(ice, comp_id, transport_id, pkt, pkt_size, src_addr, src_addr_len); status = PJ_SUCCESS; } return status; } ================================================ FILE: deps/pjsip/pjnath/src/pjnath/ice_strans.c ================================================ /* $Id: ice_strans.c 4412 2013-03-05 03:12:32Z riza $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define ENABLE_TRACE 0 #if defined(ENABLE_TRACE) && (ENABLE_TRACE != 0) # define TRACE_PKT(expr) PJ_LOG(5,expr) #else # define TRACE_PKT(expr) #endif /* Transport IDs */ enum tp_type { TP_NONE, TP_STUN, TP_TURN }; /* Candidate's local preference values. This is mostly used to * specify preference among candidates with the same type. Since * we don't have the facility to specify that, we'll just set it * all to the same value. */ #if PJNATH_ICE_PRIO_STD # define SRFLX_PREF 65535 # define HOST_PREF 65535 # define RELAY_PREF 65535 #else # define SRFLX_PREF 0 # define HOST_PREF 0 # define RELAY_PREF 0 #endif /* The candidate type preference when STUN candidate is used */ static pj_uint8_t srflx_pref_table[PJ_ICE_CAND_TYPE_MAX] = { #if PJNATH_ICE_PRIO_STD 100, /**< PJ_ICE_HOST_PREF */ 110, /**< PJ_ICE_SRFLX_PREF */ 126, /**< PJ_ICE_PRFLX_PREF */ 0 /**< PJ_ICE_RELAYED_PREF */ #else /* Keep it to 2 bits */ 1, /**< PJ_ICE_HOST_PREF */ 2, /**< PJ_ICE_SRFLX_PREF */ 3, /**< PJ_ICE_PRFLX_PREF */ 0 /**< PJ_ICE_RELAYED_PREF */ #endif }; /* ICE callbacks */ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status); static pj_status_t ice_tx_pkt(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len); static void ice_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len); /* STUN socket callbacks */ /* Notification when incoming packet has been received. */ static pj_bool_t stun_on_rx_data(pj_stun_sock *stun_sock, void *pkt, unsigned pkt_len, const pj_sockaddr_t *src_addr, unsigned addr_len); /* Notifification when asynchronous send operation has completed. */ static pj_bool_t stun_on_data_sent(pj_stun_sock *stun_sock, pj_ioqueue_op_key_t *send_key, pj_ssize_t sent); /* Notification when the status of the STUN transport has changed. */ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, pj_stun_sock_op op, pj_status_t status); /* TURN callbacks */ static void turn_on_rx_data(pj_turn_sock *turn_sock, void *pkt, unsigned pkt_len, const pj_sockaddr_t *peer_addr, unsigned addr_len); static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state, pj_turn_state_t new_state); /* Forward decls */ static void ice_st_on_destroy(void *obj); static void destroy_ice_st(pj_ice_strans *ice_st); #define ice_st_perror(ice_st,msg,rc) pjnath_perror(ice_st->obj_name,msg,rc) static void sess_init_update(pj_ice_strans *ice_st); /** * This structure describes an ICE stream transport component. A component * in ICE stream transport typically corresponds to a single socket created * for this component, and bound to a specific transport address. This * component may have multiple alias addresses, for example one alias * address for each interfaces in multi-homed host, another for server * reflexive alias, and another for relayed alias. For each transport * address alias, an ICE stream transport candidate (#pj_ice_sess_cand) will * be created, and these candidates will eventually registered to the ICE * session. */ typedef struct pj_ice_strans_comp { pj_ice_strans *ice_st; /**< ICE stream transport. */ unsigned comp_id; /**< Component ID. */ pj_stun_sock *stun_sock; /**< STUN transport. */ pj_turn_sock *turn_sock; /**< TURN relay transport. */ pj_bool_t turn_log_off; /**< TURN loggin off? */ unsigned turn_err_cnt; /**< TURN disconnected count. */ unsigned cand_cnt; /**< # of candidates/aliaes. */ pj_ice_sess_cand cand_list[PJ_ICE_ST_MAX_CAND]; /**< Cand array */ unsigned default_cand; /**< Default candidate. */ } pj_ice_strans_comp; /** * This structure represents the ICE stream transport. */ struct pj_ice_strans { char *obj_name; /**< Log ID. */ pj_pool_t *pool; /**< Pool used by this object. */ void *user_data; /**< Application data. */ pj_ice_strans_cfg cfg; /**< Configuration. */ pj_ice_strans_cb cb; /**< Application callback. */ pj_grp_lock_t *grp_lock; /**< Group lock. */ pj_ice_strans_state state; /**< Session state. */ pj_ice_sess *ice; /**< ICE session. */ pj_time_val start_time;/**< Time when ICE was started */ unsigned comp_cnt; /**< Number of components. */ pj_ice_strans_comp **comp; /**< Components array. */ pj_timer_entry ka_timer; /**< STUN keep-alive timer. */ pj_bool_t destroy_req;/**< Destroy has been called? */ pj_bool_t cb_called; /**< Init error callback called?*/ }; /* Set ICE state*/ static void set_ice_state(pj_ice_strans *ice_st, pj_ice_strans_state state) { pj_ice_strans_state prev = ice_st->state; if (prev != state) { ice_st->state = state; if (ice_st->cb.on_ice_state) (*ice_st->cb.on_ice_state)(ice_st, prev, state); } } /* Validate configuration */ static pj_status_t pj_ice_strans_cfg_check_valid(const pj_ice_strans_cfg *cfg) { pj_status_t status; status = pj_stun_config_check_valid(&cfg->stun_cfg); if (!status) return status; return PJ_SUCCESS; } /* * Initialize ICE transport configuration with default values. */ PJ_DEF(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg) { pj_bzero(cfg, sizeof(*cfg)); pj_stun_config_init(&cfg->stun_cfg, NULL, 0, NULL, NULL); pj_stun_sock_cfg_default(&cfg->stun.cfg); pj_turn_alloc_param_default(&cfg->turn.alloc_param); pj_turn_sock_cfg_default(&cfg->turn.cfg); pj_ice_sess_options_default(&cfg->opt); cfg->af = pj_AF_INET(); cfg->stun.port = PJ_STUN_PORT; cfg->turn.conn_type = PJ_TURN_TP_UDP; cfg->stun.max_host_cands = 64; cfg->stun.ignore_stun_error = PJ_FALSE; } /* * Copy configuration. */ PJ_DEF(void) pj_ice_strans_cfg_copy( pj_pool_t *pool, pj_ice_strans_cfg *dst, const pj_ice_strans_cfg *src) { pj_memcpy(dst, src, sizeof(*src)); if (src->stun.server.slen) pj_strdup(pool, &dst->stun.server, &src->stun.server); if (src->turn.server.slen) pj_strdup(pool, &dst->turn.server, &src->turn.server); pj_stun_auth_cred_dup(pool, &dst->turn.auth_cred, &src->turn.auth_cred); } /* * Add or update TURN candidate. */ static pj_status_t add_update_turn(pj_ice_strans *ice_st, pj_ice_strans_comp *comp) { pj_turn_sock_cb turn_sock_cb; pj_ice_sess_cand *cand = NULL; unsigned i; pj_status_t status; /* Find relayed candidate in the component */ for (i=0; icand_cnt; ++i) { if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_RELAYED) { cand = &comp->cand_list[i]; break; } } /* If candidate is found, invalidate it first */ if (cand) { cand->status = PJ_EPENDING; /* Also if this component's default candidate is set to relay, * move it temporarily to something else. */ if ((int)comp->default_cand == cand - comp->cand_list) { /* Init to something */ comp->default_cand = 0; /* Use srflx candidate as the default, if any */ for (i=0; icand_cnt; ++i) { if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_SRFLX) { comp->default_cand = i; break; } } } } /* Init TURN socket */ pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb)); turn_sock_cb.on_rx_data = &turn_on_rx_data; turn_sock_cb.on_state = &turn_on_state; /* Override with component specific QoS settings, if any */ if (ice_st->cfg.comp[comp->comp_id-1].qos_type) { ice_st->cfg.turn.cfg.qos_type = ice_st->cfg.comp[comp->comp_id-1].qos_type; } if (ice_st->cfg.comp[comp->comp_id-1].qos_params.flags) { pj_memcpy(&ice_st->cfg.turn.cfg.qos_params, &ice_st->cfg.comp[comp->comp_id-1].qos_params, sizeof(ice_st->cfg.turn.cfg.qos_params)); } /* Override with component specific socket buffer size settings, if any */ if (ice_st->cfg.comp[comp->comp_id-1].so_rcvbuf_size > 0) { ice_st->cfg.turn.cfg.so_rcvbuf_size = ice_st->cfg.comp[comp->comp_id-1].so_rcvbuf_size; } if (ice_st->cfg.comp[comp->comp_id-1].so_sndbuf_size > 0) { ice_st->cfg.turn.cfg.so_sndbuf_size = ice_st->cfg.comp[comp->comp_id-1].so_sndbuf_size; } /* Create the TURN transport */ status = pj_turn_sock_create(&ice_st->cfg.stun_cfg, ice_st->cfg.af, ice_st->cfg.turn.conn_type, &turn_sock_cb, &ice_st->cfg.turn.cfg, comp, &comp->turn_sock); if (status != PJ_SUCCESS) { return status; } /* Add pending job */ ///sess_add_ref(ice_st); /* Start allocation */ status=pj_turn_sock_alloc(comp->turn_sock, &ice_st->cfg.turn.server, ice_st->cfg.turn.port, ice_st->cfg.resolver, &ice_st->cfg.turn.auth_cred, &ice_st->cfg.turn.alloc_param); if (status != PJ_SUCCESS) { ///sess_dec_ref(ice_st); return status; } /* Add relayed candidate with pending status if there's no existing one */ if (cand == NULL) { cand = &comp->cand_list[comp->cand_cnt++]; cand->type = PJ_ICE_CAND_TYPE_RELAYED; cand->status = PJ_EPENDING; cand->local_pref = RELAY_PREF; cand->transport_id = TP_TURN; cand->comp_id = (pj_uint8_t) comp->comp_id; } PJ_LOG(4,(ice_st->obj_name, "Comp %d: TURN relay candidate waiting for allocation", comp->comp_id)); return PJ_SUCCESS; } static pj_bool_t ice_cand_equals(pj_ice_sess_cand *lcand, pj_ice_sess_cand *rcand) { if (lcand == NULL && rcand == NULL){ return PJ_TRUE; } if (lcand == NULL || rcand == NULL){ return PJ_FALSE; } if (lcand->type != rcand->type || lcand->status != rcand->status || lcand->comp_id != rcand->comp_id || lcand->transport_id != rcand->transport_id || lcand->local_pref != rcand->local_pref || lcand->prio != rcand->prio || pj_sockaddr_cmp(&lcand->addr, &rcand->addr) != 0 || pj_sockaddr_cmp(&lcand->base_addr, &rcand->base_addr) != 0) { return PJ_FALSE; } return PJ_TRUE; } /* * Create the component. */ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id) { pj_ice_strans_comp *comp = NULL; pj_status_t status; /* Verify arguments */ PJ_ASSERT_RETURN(ice_st && comp_id, PJ_EINVAL); /* Check that component ID present */ PJ_ASSERT_RETURN(comp_id <= ice_st->comp_cnt, PJNATH_EICEINCOMPID); /* Create component */ comp = PJ_POOL_ZALLOC_T(ice_st->pool, pj_ice_strans_comp); comp->ice_st = ice_st; comp->comp_id = comp_id; ice_st->comp[comp_id-1] = comp; /* Initialize default candidate */ comp->default_cand = 0; /* Create STUN transport if configured */ if (ice_st->cfg.stun.server.slen || ice_st->cfg.stun.max_host_cands) { pj_stun_sock_cb stun_sock_cb; pj_ice_sess_cand *cand; pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb)); stun_sock_cb.on_rx_data = &stun_on_rx_data; stun_sock_cb.on_status = &stun_on_status; stun_sock_cb.on_data_sent = &stun_on_data_sent; /* Override component specific QoS settings, if any */ if (ice_st->cfg.comp[comp_id-1].qos_type) { ice_st->cfg.stun.cfg.qos_type = ice_st->cfg.comp[comp_id-1].qos_type; } if (ice_st->cfg.comp[comp_id-1].qos_params.flags) { pj_memcpy(&ice_st->cfg.stun.cfg.qos_params, &ice_st->cfg.comp[comp_id-1].qos_params, sizeof(ice_st->cfg.stun.cfg.qos_params)); } /* Override component specific socket buffer size settings, if any */ if (ice_st->cfg.comp[comp_id-1].so_rcvbuf_size > 0) { ice_st->cfg.stun.cfg.so_rcvbuf_size = ice_st->cfg.comp[comp_id-1].so_rcvbuf_size; } if (ice_st->cfg.comp[comp_id-1].so_sndbuf_size > 0) { ice_st->cfg.stun.cfg.so_sndbuf_size = ice_st->cfg.comp[comp_id-1].so_sndbuf_size; } /* Create the STUN transport */ status = pj_stun_sock_create(&ice_st->cfg.stun_cfg, NULL, ice_st->cfg.af, &stun_sock_cb, &ice_st->cfg.stun.cfg, comp, &comp->stun_sock); if (status != PJ_SUCCESS) return status; /* Start STUN Binding resolution and add srflx candidate * only if server is set */ if (ice_st->cfg.stun.server.slen) { pj_stun_sock_info stun_sock_info; /* Add pending job */ ///sess_add_ref(ice_st); PJ_LOG(4,(ice_st->obj_name, "Comp %d: srflx candidate starts Binding discovery", comp_id)); pj_log_push_indent(); /* Start Binding resolution */ status = pj_stun_sock_start(comp->stun_sock, &ice_st->cfg.stun.server, ice_st->cfg.stun.port, ice_st->cfg.resolver); if (status != PJ_SUCCESS) { ///sess_dec_ref(ice_st); pj_log_pop_indent(); return status; } /* Enumerate addresses */ status = pj_stun_sock_get_info(comp->stun_sock, &stun_sock_info); if (status != PJ_SUCCESS) { ///sess_dec_ref(ice_st); pj_log_pop_indent(); return status; } /* Add srflx candidate with pending status. */ cand = &comp->cand_list[comp->cand_cnt++]; cand->type = PJ_ICE_CAND_TYPE_SRFLX; cand->status = PJ_EPENDING; cand->local_pref = SRFLX_PREF; cand->transport_id = TP_STUN; cand->comp_id = (pj_uint8_t) comp_id; pj_sockaddr_cp(&cand->base_addr, &stun_sock_info.aliases[0]); pj_sockaddr_cp(&cand->rel_addr, &cand->base_addr); pj_ice_calc_foundation(ice_st->pool, &cand->foundation, cand->type, &cand->base_addr); /* Set default candidate to srflx */ comp->default_cand = (unsigned)(cand - comp->cand_list); pj_log_pop_indent(); } /* Add local addresses to host candidates, unless max_host_cands * is set to zero. */ if (ice_st->cfg.stun.max_host_cands) { pj_stun_sock_info stun_sock_info; unsigned i; /* Enumerate addresses */ status = pj_stun_sock_get_info(comp->stun_sock, &stun_sock_info); if (status != PJ_SUCCESS) return status; for (i=0; icfg.stun.max_host_cands; ++i) { unsigned j; pj_bool_t cand_duplicate = PJ_FALSE; char addrinfo[PJ_INET6_ADDRSTRLEN+10]; const pj_sockaddr *addr = &stun_sock_info.aliases[i]; /* Leave one candidate for relay */ if (comp->cand_cnt >= PJ_ICE_ST_MAX_CAND-1) { PJ_LOG(4,(ice_st->obj_name, "Too many host candidates")); break; } /* Ignore loopback addresses unless cfg->stun.loop_addr * is set */ if ((pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==127) { if (ice_st->cfg.stun.loop_addr==PJ_FALSE) continue; } cand = &comp->cand_list[comp->cand_cnt]; cand->type = PJ_ICE_CAND_TYPE_HOST; cand->status = PJ_SUCCESS; cand->local_pref = HOST_PREF; cand->transport_id = TP_STUN; cand->comp_id = (pj_uint8_t) comp_id; pj_sockaddr_cp(&cand->addr, addr); pj_sockaddr_cp(&cand->base_addr, addr); pj_bzero(&cand->rel_addr, sizeof(cand->rel_addr)); /* Check if not already in list */ for (j=0; jcand_cnt; j++) { if (ice_cand_equals(cand, &comp->cand_list[j])) { cand_duplicate = PJ_TRUE; break; } } if (cand_duplicate) { PJ_LOG(4, (ice_st->obj_name, "Comp %d: host candidate %s is a duplicate", comp_id, pj_sockaddr_print(&cand->addr, addrinfo, sizeof(addrinfo), 3))); pj_bzero(&cand->addr, sizeof(cand->addr)); pj_bzero(&cand->base_addr, sizeof(cand->base_addr)); continue; } else { comp->cand_cnt+=1; } pj_ice_calc_foundation(ice_st->pool, &cand->foundation, cand->type, &cand->base_addr); PJ_LOG(4,(ice_st->obj_name, "Comp %d: host candidate %s added", comp_id, pj_sockaddr_print(&cand->addr, addrinfo, sizeof(addrinfo), 3))); } } } /* Create TURN relay if configured. */ if (ice_st->cfg.turn.server.slen) { add_update_turn(ice_st, comp); } /* It's possible that we end up without any candidates */ if (comp->cand_cnt == 0) { PJ_LOG(4,(ice_st->obj_name, "Error: no candidate is created due to settings")); return PJ_EINVAL; } return PJ_SUCCESS; } /* * Create ICE stream transport */ PJ_DEF(pj_status_t) pj_ice_strans_create( const char *name, const pj_ice_strans_cfg *cfg, unsigned comp_cnt, void *user_data, const pj_ice_strans_cb *cb, pj_ice_strans **p_ice_st) { pj_pool_t *pool; pj_ice_strans *ice_st; unsigned i; pj_status_t status; status = pj_ice_strans_cfg_check_valid(cfg); if (status != PJ_SUCCESS) return status; PJ_ASSERT_RETURN(comp_cnt && cb && p_ice_st && comp_cnt <= PJ_ICE_MAX_COMP , PJ_EINVAL); if (name == NULL) name = "ice%p"; pool = pj_pool_create(cfg->stun_cfg.pf, name, PJNATH_POOL_LEN_ICE_STRANS, PJNATH_POOL_INC_ICE_STRANS, NULL); ice_st = PJ_POOL_ZALLOC_T(pool, pj_ice_strans); ice_st->pool = pool; ice_st->obj_name = pool->obj_name; ice_st->user_data = user_data; PJ_LOG(4,(ice_st->obj_name, "Creating ICE stream transport with %d component(s)", comp_cnt)); pj_log_push_indent(); status = pj_grp_lock_create(pool, NULL, &ice_st->grp_lock); if (status != PJ_SUCCESS) { pj_pool_release(pool); pj_log_pop_indent(); return status; } pj_grp_lock_add_ref(ice_st->grp_lock); pj_grp_lock_add_handler(ice_st->grp_lock, pool, ice_st, &ice_st_on_destroy); pj_ice_strans_cfg_copy(pool, &ice_st->cfg, cfg); ice_st->cfg.stun.cfg.grp_lock = ice_st->grp_lock; ice_st->cfg.turn.cfg.grp_lock = ice_st->grp_lock; pj_memcpy(&ice_st->cb, cb, sizeof(*cb)); ice_st->comp_cnt = comp_cnt; ice_st->comp = (pj_ice_strans_comp**) pj_pool_calloc(pool, comp_cnt, sizeof(pj_ice_strans_comp*)); /* Move state to candidate gathering */ set_ice_state(ice_st, PJ_ICE_STRANS_STATE_INIT); /* Acquire initialization mutex to prevent callback to be * called before we finish initialization. */ pj_grp_lock_acquire(ice_st->grp_lock); for (i=0; igrp_lock); destroy_ice_st(ice_st); pj_log_pop_indent(); return status; } } /* Done with initialization */ pj_grp_lock_release(ice_st->grp_lock); PJ_LOG(4,(ice_st->obj_name, "ICE stream transport %p created", ice_st)); *p_ice_st = ice_st; /* Check if all candidates are ready (this may call callback) */ sess_init_update(ice_st); pj_log_pop_indent(); return PJ_SUCCESS; } /* REALLY destroy ICE */ static void ice_st_on_destroy(void *obj) { pj_ice_strans *ice_st = (pj_ice_strans*)obj; PJ_LOG(4,(ice_st->obj_name, "ICE stream transport %p destroyed", obj)); /* Done */ pj_pool_release(ice_st->pool); } /* Destroy ICE */ static void destroy_ice_st(pj_ice_strans *ice_st) { unsigned i; PJ_LOG(5,(ice_st->obj_name, "ICE stream transport %p destroy request..", ice_st)); pj_log_push_indent(); pj_grp_lock_acquire(ice_st->grp_lock); if (ice_st->destroy_req) { pj_grp_lock_release(ice_st->grp_lock); return; } ice_st->destroy_req = PJ_TRUE; /* Destroy ICE if we have ICE */ if (ice_st->ice) { pj_ice_sess_destroy(ice_st->ice); ice_st->ice = NULL; } /* Destroy all components */ for (i=0; icomp_cnt; ++i) { if (ice_st->comp[i]) { if (ice_st->comp[i]->stun_sock) { pj_stun_sock_destroy(ice_st->comp[i]->stun_sock); ice_st->comp[i]->stun_sock = NULL; } if (ice_st->comp[i]->turn_sock) { pj_turn_sock_destroy(ice_st->comp[i]->turn_sock); ice_st->comp[i]->turn_sock = NULL; } } } pj_grp_lock_dec_ref(ice_st->grp_lock); pj_grp_lock_release(ice_st->grp_lock); pj_log_pop_indent(); } /* Get ICE session state. */ PJ_DEF(pj_ice_strans_state) pj_ice_strans_get_state(pj_ice_strans *ice_st) { return ice_st->state; } /* State string */ PJ_DEF(const char*) pj_ice_strans_state_name(pj_ice_strans_state state) { const char *names[] = { "Null", "Candidate Gathering", "Candidate Gathering Complete", "Session Initialized", "Negotiation In Progress", "Negotiation Success", "Negotiation Failed" }; PJ_ASSERT_RETURN(state <= PJ_ICE_STRANS_STATE_FAILED, "???"); return names[state]; } /* Notification about failure */ static void sess_fail(pj_ice_strans *ice_st, pj_ice_strans_op op, const char *title, pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(ice_st->obj_name, "%s: %s", title, errmsg)); pj_log_push_indent(); if (op==PJ_ICE_STRANS_OP_INIT && ice_st->cb_called) { pj_log_pop_indent(); return; } ice_st->cb_called = PJ_TRUE; if (ice_st->cb.on_ice_complete) (*ice_st->cb.on_ice_complete)(ice_st, op, status); pj_log_pop_indent(); } /* Update initialization status */ static void sess_init_update(pj_ice_strans *ice_st) { unsigned i; /* Ignore if init callback has been called */ if (ice_st->cb_called) return; /* Notify application when all candidates have been gathered */ for (i=0; icomp_cnt; ++i) { unsigned j; pj_ice_strans_comp *comp = ice_st->comp[i]; for (j=0; jcand_cnt; ++j) { pj_ice_sess_cand *cand = &comp->cand_list[j]; if (cand->status == PJ_EPENDING) return; } } /* All candidates have been gathered */ ice_st->cb_called = PJ_TRUE; set_ice_state(ice_st, PJ_ICE_STRANS_STATE_READY); if (ice_st->cb.on_ice_complete) (*ice_st->cb.on_ice_complete)(ice_st, PJ_ICE_STRANS_OP_INIT, PJ_SUCCESS); } /* * Destroy ICE stream transport. */ PJ_DEF(pj_status_t) pj_ice_strans_destroy(pj_ice_strans *ice_st) { destroy_ice_st(ice_st); return PJ_SUCCESS; } /* * Get user data */ PJ_DEF(void*) pj_ice_strans_get_user_data(pj_ice_strans *ice_st) { PJ_ASSERT_RETURN(ice_st, NULL); return ice_st->user_data; } /* * Get the value of various options of the ICE stream transport. */ PJ_DEF(pj_status_t) pj_ice_strans_get_options( pj_ice_strans *ice_st, pj_ice_sess_options *opt) { PJ_ASSERT_RETURN(ice_st && opt, PJ_EINVAL); pj_memcpy(opt, &ice_st->cfg.opt, sizeof(*opt)); return PJ_SUCCESS; } /* * Specify various options for this ICE stream transport. */ PJ_DEF(pj_status_t) pj_ice_strans_set_options(pj_ice_strans *ice_st, const pj_ice_sess_options *opt) { PJ_ASSERT_RETURN(ice_st && opt, PJ_EINVAL); pj_memcpy(&ice_st->cfg.opt, opt, sizeof(*opt)); if (ice_st->ice) pj_ice_sess_set_options(ice_st->ice, &ice_st->cfg.opt); return PJ_SUCCESS; } /** * Get the group lock for this ICE stream transport. */ PJ_DEF(pj_grp_lock_t *) pj_ice_strans_get_grp_lock(pj_ice_strans *ice_st) { PJ_ASSERT_RETURN(ice_st, NULL); return ice_st->grp_lock; } /* * Create ICE! */ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, pj_ice_sess_role role, const pj_str_t *local_ufrag, const pj_str_t *local_passwd) { pj_status_t status; unsigned i; pj_ice_sess_cb ice_cb; //const pj_uint8_t srflx_prio[4] = { 100, 126, 110, 0 }; /* Check arguments */ PJ_ASSERT_RETURN(ice_st, PJ_EINVAL); /* Must not have ICE */ PJ_ASSERT_RETURN(ice_st->ice == NULL, PJ_EINVALIDOP); /* Components must have been created */ PJ_ASSERT_RETURN(ice_st->comp[0] != NULL, PJ_EINVALIDOP); /* Init callback */ pj_bzero(&ice_cb, sizeof(ice_cb)); ice_cb.on_ice_complete = &on_ice_complete; ice_cb.on_rx_data = &ice_rx_data; ice_cb.on_tx_pkt = &ice_tx_pkt; /* Create! */ status = pj_ice_sess_create(&ice_st->cfg.stun_cfg, ice_st->obj_name, role, ice_st->comp_cnt, &ice_cb, local_ufrag, local_passwd, ice_st->grp_lock, &ice_st->ice); if (status != PJ_SUCCESS) return status; /* Associate user data */ ice_st->ice->user_data = (void*)ice_st; /* Set options */ pj_ice_sess_set_options(ice_st->ice, &ice_st->cfg.opt); /* If default candidate for components are SRFLX one, upload a custom * type priority to ICE session so that SRFLX candidates will get * checked first. */ if (ice_st->comp[0]->cand_list[ice_st->comp[0]->default_cand].type == PJ_ICE_CAND_TYPE_SRFLX) { pj_ice_sess_set_prefs(ice_st->ice, srflx_pref_table); } /* Add components/candidates */ for (i=0; icomp_cnt; ++i) { unsigned j; pj_ice_strans_comp *comp = ice_st->comp[i]; /* Re-enable logging for Send/Data indications */ if (comp->turn_sock) { PJ_LOG(5,(ice_st->obj_name, "Disabling STUN Indication logging for " "component %d", i+1)); pj_turn_sock_set_log(comp->turn_sock, 0xFFFF); comp->turn_log_off = PJ_FALSE; } for (j=0; jcand_cnt; ++j) { pj_ice_sess_cand *cand = &comp->cand_list[j]; unsigned ice_cand_id; /* Skip if candidate is not ready */ if (cand->status != PJ_SUCCESS) { PJ_LOG(5,(ice_st->obj_name, "Candidate %d of comp %d is not added (pending)", j, i)); continue; } /* Must have address */ pj_assert(pj_sockaddr_has_addr(&cand->addr)); /* Add the candidate */ status = pj_ice_sess_add_cand(ice_st->ice, comp->comp_id, cand->transport_id, cand->type, cand->local_pref, &cand->foundation, &cand->addr, &cand->base_addr, &cand->rel_addr, pj_sockaddr_get_len(&cand->addr), (unsigned*)&ice_cand_id); if (status != PJ_SUCCESS) goto on_error; } } /* ICE session is ready for negotiation */ set_ice_state(ice_st, PJ_ICE_STRANS_STATE_SESS_READY); return PJ_SUCCESS; on_error: pj_ice_strans_stop_ice(ice_st); return status; } /* * Check if the ICE stream transport has the ICE session created. */ PJ_DEF(pj_bool_t) pj_ice_strans_has_sess(pj_ice_strans *ice_st) { PJ_ASSERT_RETURN(ice_st, PJ_FALSE); return ice_st->ice != NULL; } /* * Check if ICE negotiation is still running. */ PJ_DEF(pj_bool_t) pj_ice_strans_sess_is_running(pj_ice_strans *ice_st) { return ice_st && ice_st->ice && ice_st->ice->rcand_cnt && !pj_ice_strans_sess_is_complete(ice_st); } /* * Check if ICE negotiation has completed. */ PJ_DEF(pj_bool_t) pj_ice_strans_sess_is_complete(pj_ice_strans *ice_st) { return ice_st && ice_st->ice && ice_st->ice->is_complete; } /* * Get the current/running component count. */ PJ_DEF(unsigned) pj_ice_strans_get_running_comp_cnt(pj_ice_strans *ice_st) { PJ_ASSERT_RETURN(ice_st, PJ_EINVAL); if (ice_st->ice && ice_st->ice->rcand_cnt) { return ice_st->ice->comp_cnt; } else { return ice_st->comp_cnt; } } /* * Get the ICE username fragment and password of the ICE session. */ PJ_DEF(pj_status_t) pj_ice_strans_get_ufrag_pwd( pj_ice_strans *ice_st, pj_str_t *loc_ufrag, pj_str_t *loc_pwd, pj_str_t *rem_ufrag, pj_str_t *rem_pwd) { PJ_ASSERT_RETURN(ice_st && ice_st->ice, PJ_EINVALIDOP); if (loc_ufrag) *loc_ufrag = ice_st->ice->rx_ufrag; if (loc_pwd) *loc_pwd = ice_st->ice->rx_pass; if (rem_ufrag || rem_pwd) { PJ_ASSERT_RETURN(ice_st->ice->rcand_cnt != 0, PJ_EINVALIDOP); if (rem_ufrag) *rem_ufrag = ice_st->ice->tx_ufrag; if (rem_pwd) *rem_pwd = ice_st->ice->tx_pass; } return PJ_SUCCESS; } /* * Get number of candidates */ PJ_DEF(unsigned) pj_ice_strans_get_cands_count(pj_ice_strans *ice_st, unsigned comp_id) { unsigned i, cnt; PJ_ASSERT_RETURN(ice_st && ice_st->ice && comp_id && comp_id <= ice_st->comp_cnt, 0); cnt = 0; for (i=0; iice->lcand_cnt; ++i) { if (ice_st->ice->lcand[i].comp_id != comp_id) continue; ++cnt; } return cnt; } /* * Enum candidates */ PJ_DEF(pj_status_t) pj_ice_strans_enum_cands(pj_ice_strans *ice_st, unsigned comp_id, unsigned *count, pj_ice_sess_cand cand[]) { unsigned i, cnt; PJ_ASSERT_RETURN(ice_st && ice_st->ice && comp_id && comp_id <= ice_st->comp_cnt && count && cand, PJ_EINVAL); cnt = 0; for (i=0; iice->lcand_cnt && cnt<*count; ++i) { if (ice_st->ice->lcand[i].comp_id != comp_id) continue; pj_memcpy(&cand[cnt], &ice_st->ice->lcand[i], sizeof(pj_ice_sess_cand)); ++cnt; } *count = cnt; return PJ_SUCCESS; } /* * Get default candidate. */ PJ_DEF(pj_status_t) pj_ice_strans_get_def_cand( pj_ice_strans *ice_st, unsigned comp_id, pj_ice_sess_cand *cand) { const pj_ice_sess_check *valid_pair; PJ_ASSERT_RETURN(ice_st && comp_id && comp_id <= ice_st->comp_cnt && cand, PJ_EINVAL); valid_pair = pj_ice_strans_get_valid_pair(ice_st, comp_id); if (valid_pair) { pj_memcpy(cand, valid_pair->lcand, sizeof(pj_ice_sess_cand)); } else { pj_ice_strans_comp *comp = ice_st->comp[comp_id - 1]; pj_assert(comp->default_candcand_cnt); pj_memcpy(cand, &comp->cand_list[comp->default_cand], sizeof(pj_ice_sess_cand)); } return PJ_SUCCESS; } /* * Get the current ICE role. */ PJ_DEF(pj_ice_sess_role) pj_ice_strans_get_role(pj_ice_strans *ice_st) { PJ_ASSERT_RETURN(ice_st && ice_st->ice, PJ_ICE_SESS_ROLE_UNKNOWN); return ice_st->ice->role; } /* * Change session role. */ PJ_DEF(pj_status_t) pj_ice_strans_change_role( pj_ice_strans *ice_st, pj_ice_sess_role new_role) { PJ_ASSERT_RETURN(ice_st && ice_st->ice, PJ_EINVALIDOP); return pj_ice_sess_change_role(ice_st->ice, new_role); } /* * Start ICE processing ! */ PJ_DEF(pj_status_t) pj_ice_strans_start_ice( pj_ice_strans *ice_st, const pj_str_t *rem_ufrag, const pj_str_t *rem_passwd, unsigned rem_cand_cnt, const pj_ice_sess_cand rem_cand[]) { pj_status_t status; PJ_ASSERT_RETURN(ice_st && rem_ufrag && rem_passwd && rem_cand_cnt && rem_cand, PJ_EINVAL); /* Mark start time */ pj_gettimeofday(&ice_st->start_time); /* Build check list */ status = pj_ice_sess_create_check_list(ice_st->ice, rem_ufrag, rem_passwd, rem_cand_cnt, rem_cand); if (status != PJ_SUCCESS) return status; /* If we have TURN candidate, now is the time to create the permissions */ if (ice_st->comp[0]->turn_sock) { unsigned i; for (i=0; icomp_cnt; ++i) { pj_ice_strans_comp *comp = ice_st->comp[i]; pj_sockaddr addrs[PJ_ICE_ST_MAX_CAND]; unsigned j, count=0; /* Gather remote addresses for this component */ for (j=0; jturn_sock, count, addrs, 0); if (status != PJ_SUCCESS) { pj_ice_strans_stop_ice(ice_st); return status; } } } } /* Start ICE negotiation! */ status = pj_ice_sess_start_check(ice_st->ice); if (status != PJ_SUCCESS) { pj_ice_strans_stop_ice(ice_st); return status; } set_ice_state(ice_st, PJ_ICE_STRANS_STATE_NEGO); return status; } /* * Get valid pair. */ PJ_DEF(const pj_ice_sess_check*) pj_ice_strans_get_valid_pair(const pj_ice_strans *ice_st, unsigned comp_id) { PJ_ASSERT_RETURN(ice_st && comp_id && comp_id <= ice_st->comp_cnt, NULL); if (ice_st->ice == NULL) return NULL; return ice_st->ice->comp[comp_id-1].valid_check; } /* * Get ICE session. */ PJ_DEF(pj_ice_sess*) pj_ice_strans_get_session(const pj_ice_strans *ice_st) { return ice_st->ice; } /* * Get ICE start time. */ PJ_DEF(pj_time_val) pj_ice_strans_get_start_time(const pj_ice_strans *ice_st) { return ice_st->start_time; } /* * Stop ICE! */ PJ_DEF(pj_status_t) pj_ice_strans_stop_ice(pj_ice_strans *ice_st) { PJ_ASSERT_RETURN(ice_st, PJ_EINVAL); /* Protect with group lock, since this may cause race condition with * pj_ice_strans_sendto(). * See ticket #1877. */ pj_grp_lock_acquire(ice_st->grp_lock); if (ice_st->ice) { pj_ice_sess_destroy(ice_st->ice); ice_st->ice = NULL; } set_ice_state(ice_st, PJ_ICE_STRANS_STATE_INIT); pj_grp_lock_release(ice_st->grp_lock); return PJ_SUCCESS; } /* * Application wants to send outgoing packet. */ PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st, unsigned comp_id, const void *data, pj_size_t data_len, const pj_sockaddr_t *dst_addr, int dst_addr_len) { pj_ice_strans_comp *comp; unsigned def_cand; pj_status_t status; PJ_ASSERT_RETURN(ice_st && comp_id && comp_id <= ice_st->comp_cnt && dst_addr && dst_addr_len, PJ_EINVAL); comp = ice_st->comp[comp_id-1]; /* Check that default candidate for the component exists */ def_cand = comp->default_cand; if (def_cand >= comp->cand_cnt) return PJ_EINVALIDOP; /* Protect with group lock, since this may cause race condition with * pj_ice_strans_stop_ice(). * See ticket #1877. */ pj_grp_lock_acquire(ice_st->grp_lock); /* If ICE is available, send data with ICE, otherwise send with the * default candidate selected during initialization. * * https://trac.pjsip.org/repos/ticket/1416: * Once ICE has failed, also send data with the default candidate. */ if (ice_st->ice && ice_st->state == PJ_ICE_STRANS_STATE_RUNNING) { status = pj_ice_sess_send_data(ice_st->ice, comp_id, data, data_len); pj_grp_lock_release(ice_st->grp_lock); return status; } pj_grp_lock_release(ice_st->grp_lock); if (comp->cand_list[def_cand].status == PJ_SUCCESS) { if (comp->cand_list[def_cand].type == PJ_ICE_CAND_TYPE_RELAYED) { enum { msg_disable_ind = 0xFFFF & ~(PJ_STUN_SESS_LOG_TX_IND| PJ_STUN_SESS_LOG_RX_IND) }; /* https://trac.pjsip.org/repos/ticket/1316 */ if (comp->turn_sock == NULL) { /* TURN socket error */ return PJ_EINVALIDOP; } if (!comp->turn_log_off) { /* Disable logging for Send/Data indications */ PJ_LOG(5,(ice_st->obj_name, "Disabling STUN Indication logging for " "component %d", comp->comp_id)); pj_turn_sock_set_log(comp->turn_sock, msg_disable_ind); comp->turn_log_off = PJ_TRUE; } status = pj_turn_sock_sendto(comp->turn_sock, (const pj_uint8_t*)data, (unsigned)data_len, dst_addr, dst_addr_len); return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status; } else { status = pj_stun_sock_sendto(comp->stun_sock, NULL, data, (unsigned)data_len, 0, dst_addr, dst_addr_len); return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status; } } else return PJ_EINVALIDOP; } /* * Callback called by ICE session when ICE processing is complete, either * successfully or with failure. */ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) { pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data; pj_time_val t; unsigned msec; pj_grp_lock_add_ref(ice_st->grp_lock); pj_gettimeofday(&t); PJ_TIME_VAL_SUB(t, ice_st->start_time); msec = PJ_TIME_VAL_MSEC(t); if (ice_st->cb.on_ice_complete) { if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(ice_st->obj_name, "ICE negotiation failed after %ds:%03d: %s", msec/1000, msec%1000, errmsg)); } else { unsigned i; enum { msg_disable_ind = 0xFFFF & ~(PJ_STUN_SESS_LOG_TX_IND| PJ_STUN_SESS_LOG_RX_IND) }; PJ_LOG(4,(ice_st->obj_name, "ICE negotiation success after %ds:%03d", msec/1000, msec%1000)); for (i=0; icomp_cnt; ++i) { const pj_ice_sess_check *check; check = pj_ice_strans_get_valid_pair(ice_st, i+1); if (check) { char lip[PJ_INET6_ADDRSTRLEN+10]; char rip[PJ_INET6_ADDRSTRLEN+10]; pj_sockaddr_print(&check->lcand->addr, lip, sizeof(lip), 3); pj_sockaddr_print(&check->rcand->addr, rip, sizeof(rip), 3); if (check->lcand->transport_id == TP_TURN) { /* Activate channel binding for the remote address * for more efficient data transfer using TURN. */ status = pj_turn_sock_bind_channel( ice_st->comp[i]->turn_sock, &check->rcand->addr, sizeof(check->rcand->addr)); /* Disable logging for Send/Data indications */ PJ_LOG(5,(ice_st->obj_name, "Disabling STUN Indication logging for " "component %d", i+1)); pj_turn_sock_set_log(ice_st->comp[i]->turn_sock, msg_disable_ind); ice_st->comp[i]->turn_log_off = PJ_TRUE; } PJ_LOG(4,(ice_st->obj_name, " Comp %d: " "sending from %s candidate %s to " "%s candidate %s", i+1, pj_ice_get_cand_type_name(check->lcand->type), lip, pj_ice_get_cand_type_name(check->rcand->type), rip)); } else { PJ_LOG(4,(ice_st->obj_name, "Comp %d: disabled", i+1)); } } } set_ice_state(ice_st, (status==PJ_SUCCESS) ? PJ_ICE_STRANS_STATE_RUNNING : PJ_ICE_STRANS_STATE_FAILED); pj_log_push_indent(); (*ice_st->cb.on_ice_complete)(ice_st, PJ_ICE_STRANS_OP_NEGOTIATION, status); pj_log_pop_indent(); } pj_grp_lock_dec_ref(ice_st->grp_lock); } /* * Callback called by ICE session when it wants to send outgoing packet. */ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len) { pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data; pj_ice_strans_comp *comp; pj_status_t status; #if defined(ENABLE_TRACE) && (ENABLE_TRACE != 0) char daddr[PJ_INET6_ADDRSTRLEN]; #endif PJ_ASSERT_RETURN(comp_id && comp_id <= ice_st->comp_cnt, PJ_EINVAL); comp = ice_st->comp[comp_id-1]; TRACE_PKT((comp->ice_st->obj_name, "Component %d TX packet to %s:%d with transport %d", comp_id, pj_sockaddr_print(dst_addr, daddr, sizeof(addr), 0), pj_sockaddr_get_port(dst_addr), transport_id)); if (transport_id == TP_TURN) { if (comp->turn_sock) { status = pj_turn_sock_sendto(comp->turn_sock, (const pj_uint8_t*)pkt, (unsigned)size, dst_addr, dst_addr_len); } else { status = PJ_EINVALIDOP; } } else if (transport_id == TP_STUN) { status = pj_stun_sock_sendto(comp->stun_sock, NULL, pkt, (unsigned)size, 0, dst_addr, dst_addr_len); } else { pj_assert(!"Invalid transport ID"); status = PJ_EINVALIDOP; } return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status; } /* * Callback called by ICE session when it receives application data. */ static void ice_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data; PJ_UNUSED_ARG(transport_id); if (ice_st->cb.on_rx_data) { (*ice_st->cb.on_rx_data)(ice_st, comp_id, pkt, size, src_addr, src_addr_len); } } /* Notification when incoming packet has been received from * the STUN socket. */ static pj_bool_t stun_on_rx_data(pj_stun_sock *stun_sock, void *pkt, unsigned pkt_len, const pj_sockaddr_t *src_addr, unsigned addr_len) { pj_ice_strans_comp *comp; pj_ice_strans *ice_st; pj_status_t status; comp = (pj_ice_strans_comp*) pj_stun_sock_get_user_data(stun_sock); if (comp == NULL) { /* We have disassociated ourselves from the STUN socket */ return PJ_FALSE; } ice_st = comp->ice_st; pj_grp_lock_add_ref(ice_st->grp_lock); if (ice_st->ice == NULL) { /* The ICE session is gone, but we're still receiving packets. * This could also happen if remote doesn't do ICE. So just * report this to application. */ if (ice_st->cb.on_rx_data) { (*ice_st->cb.on_rx_data)(ice_st, comp->comp_id, pkt, pkt_len, src_addr, addr_len); } } else { /* Hand over the packet to ICE session */ status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, TP_STUN, pkt, pkt_len, src_addr, addr_len); if (status != PJ_SUCCESS) { ice_st_perror(comp->ice_st, "Error processing packet", status); } } return pj_grp_lock_dec_ref(ice_st->grp_lock) ? PJ_FALSE : PJ_TRUE; } /* Notifification when asynchronous send operation to the STUN socket * has completed. */ static pj_bool_t stun_on_data_sent(pj_stun_sock *stun_sock, pj_ioqueue_op_key_t *send_key, pj_ssize_t sent) { PJ_UNUSED_ARG(stun_sock); PJ_UNUSED_ARG(send_key); PJ_UNUSED_ARG(sent); return PJ_TRUE; } /* Notification when the status of the STUN transport has changed. */ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, pj_stun_sock_op op, pj_status_t status) { pj_ice_strans_comp *comp; pj_ice_strans *ice_st; pj_ice_sess_cand *cand = NULL; unsigned i; pj_assert(status != PJ_EPENDING); comp = (pj_ice_strans_comp*) pj_stun_sock_get_user_data(stun_sock); ice_st = comp->ice_st; pj_grp_lock_add_ref(ice_st->grp_lock); /* Wait until initialization completes */ pj_grp_lock_acquire(ice_st->grp_lock); /* Find the srflx cancidate */ for (i=0; icand_cnt; ++i) { if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_SRFLX) { cand = &comp->cand_list[i]; break; } } pj_grp_lock_release(ice_st->grp_lock); /* It is possible that we don't have srflx candidate even though this * callback is called. This could happen when we cancel adding srflx * candidate due to initialization error. */ if (cand == NULL) { return pj_grp_lock_dec_ref(ice_st->grp_lock) ? PJ_FALSE : PJ_TRUE; } switch (op) { case PJ_STUN_SOCK_DNS_OP: if (status != PJ_SUCCESS) { /* May not have cand, e.g. when error during init */ if (cand) cand->status = status; if (!ice_st->cfg.stun.ignore_stun_error) { sess_fail(ice_st, PJ_ICE_STRANS_OP_INIT, "DNS resolution failed", status); } else { PJ_LOG(4,(ice_st->obj_name, "STUN error is ignored for comp %d", comp->comp_id)); } } break; case PJ_STUN_SOCK_BINDING_OP: case PJ_STUN_SOCK_MAPPED_ADDR_CHANGE: if (status == PJ_SUCCESS) { pj_stun_sock_info info; status = pj_stun_sock_get_info(stun_sock, &info); if (status == PJ_SUCCESS) { char ipaddr[PJ_INET6_ADDRSTRLEN+10]; const char *op_name = (op==PJ_STUN_SOCK_BINDING_OP) ? "Binding discovery complete" : "srflx address changed"; pj_bool_t dup = PJ_FALSE; /* Eliminate the srflx candidate if the address is * equal to other (host) candidates. */ for (i=0; icand_cnt; ++i) { if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_HOST && pj_sockaddr_cmp(&comp->cand_list[i].addr, &info.mapped_addr) == 0) { dup = PJ_TRUE; break; } } if (dup) { /* Duplicate found, remove the srflx candidate */ unsigned idx = (unsigned)(cand - comp->cand_list); /* Update default candidate index */ if (comp->default_cand > idx) { --comp->default_cand; } else if (comp->default_cand == idx) { comp->default_cand = 0; } /* Remove srflx candidate */ pj_array_erase(comp->cand_list, sizeof(comp->cand_list[0]), comp->cand_cnt, idx); --comp->cand_cnt; } else { /* Otherwise update the address */ pj_sockaddr_cp(&cand->addr, &info.mapped_addr); cand->status = PJ_SUCCESS; } PJ_LOG(4,(comp->ice_st->obj_name, "Comp %d: %s, " "srflx address is %s", comp->comp_id, op_name, pj_sockaddr_print(&info.mapped_addr, ipaddr, sizeof(ipaddr), 3))); sess_init_update(ice_st); } } if (status != PJ_SUCCESS) { /* May not have cand, e.g. when error during init */ if (cand) cand->status = status; if (!ice_st->cfg.stun.ignore_stun_error || comp->cand_cnt==1) { sess_fail(ice_st, PJ_ICE_STRANS_OP_INIT, "STUN binding request failed", status); } else { PJ_LOG(4,(ice_st->obj_name, "STUN error is ignored for comp %d", comp->comp_id)); if (cand) { unsigned idx = (unsigned)(cand - comp->cand_list); /* Update default candidate index */ if (comp->default_cand == idx) { comp->default_cand = !idx; } } sess_init_update(ice_st); } } break; case PJ_STUN_SOCK_KEEP_ALIVE_OP: if (status != PJ_SUCCESS) { pj_assert(cand != NULL); cand->status = status; if (!ice_st->cfg.stun.ignore_stun_error) { sess_fail(ice_st, PJ_ICE_STRANS_OP_INIT, "STUN keep-alive failed", status); } else { PJ_LOG(4,(ice_st->obj_name, "STUN error is ignored")); } } break; } return pj_grp_lock_dec_ref(ice_st->grp_lock)? PJ_FALSE : PJ_TRUE; } /* Callback when TURN socket has received a packet */ static void turn_on_rx_data(pj_turn_sock *turn_sock, void *pkt, unsigned pkt_len, const pj_sockaddr_t *peer_addr, unsigned addr_len) { pj_ice_strans_comp *comp; pj_status_t status; comp = (pj_ice_strans_comp*) pj_turn_sock_get_user_data(turn_sock); if (comp == NULL) { /* We have disassociated ourselves from the TURN socket */ return; } pj_grp_lock_add_ref(comp->ice_st->grp_lock); if (comp->ice_st->ice == NULL) { /* The ICE session is gone, but we're still receiving packets. * This could also happen if remote doesn't do ICE and application * specifies TURN as the default address in SDP. * So in this case just give the packet to application. */ if (comp->ice_st->cb.on_rx_data) { (*comp->ice_st->cb.on_rx_data)(comp->ice_st, comp->comp_id, pkt, pkt_len, peer_addr, addr_len); } } else { /* Hand over the packet to ICE */ status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, TP_TURN, pkt, pkt_len, peer_addr, addr_len); if (status != PJ_SUCCESS) { ice_st_perror(comp->ice_st, "Error processing packet from TURN relay", status); } } pj_grp_lock_dec_ref(comp->ice_st->grp_lock); } /* Callback when TURN client state has changed */ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state, pj_turn_state_t new_state) { pj_ice_strans_comp *comp; comp = (pj_ice_strans_comp*) pj_turn_sock_get_user_data(turn_sock); if (comp == NULL) { /* Not interested in further state notification once the relay is * disconnecting. */ return; } PJ_LOG(5,(comp->ice_st->obj_name, "TURN client state changed %s --> %s", pj_turn_state_name(old_state), pj_turn_state_name(new_state))); pj_log_push_indent(); pj_grp_lock_add_ref(comp->ice_st->grp_lock); if (new_state == PJ_TURN_STATE_READY) { pj_turn_session_info rel_info; char ipaddr[PJ_INET6_ADDRSTRLEN+8]; pj_ice_sess_cand *cand = NULL; unsigned i; comp->turn_err_cnt = 0; /* Get allocation info */ pj_turn_sock_get_info(turn_sock, &rel_info); /* Wait until initialization completes */ pj_grp_lock_acquire(comp->ice_st->grp_lock); /* Find relayed candidate in the component */ for (i=0; icand_cnt; ++i) { if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_RELAYED) { cand = &comp->cand_list[i]; break; } } pj_assert(cand != NULL); pj_grp_lock_release(comp->ice_st->grp_lock); /* Update candidate */ pj_sockaddr_cp(&cand->addr, &rel_info.relay_addr); pj_sockaddr_cp(&cand->base_addr, &rel_info.relay_addr); pj_sockaddr_cp(&cand->rel_addr, &rel_info.mapped_addr); pj_ice_calc_foundation(comp->ice_st->pool, &cand->foundation, PJ_ICE_CAND_TYPE_RELAYED, &rel_info.relay_addr); cand->status = PJ_SUCCESS; /* Set default candidate to relay */ comp->default_cand = (unsigned)(cand - comp->cand_list); PJ_LOG(4,(comp->ice_st->obj_name, "Comp %d: TURN allocation complete, relay address is %s", comp->comp_id, pj_sockaddr_print(&rel_info.relay_addr, ipaddr, sizeof(ipaddr), 3))); sess_init_update(comp->ice_st); } else if (new_state >= PJ_TURN_STATE_DEALLOCATING) { pj_turn_session_info info; ++comp->turn_err_cnt; pj_turn_sock_get_info(turn_sock, &info); /* Unregister ourself from the TURN relay */ pj_turn_sock_set_user_data(turn_sock, NULL); comp->turn_sock = NULL; /* Set session to fail on error. last_status PJ_SUCCESS means normal * deallocation, which should not trigger sess_fail as it may have * been initiated by ICE destroy */ if (info.last_status != PJ_SUCCESS) { if (comp->ice_st->state < PJ_ICE_STRANS_STATE_READY) { sess_fail(comp->ice_st, PJ_ICE_STRANS_OP_INIT, "TURN allocation failed", info.last_status); } else if (comp->turn_err_cnt > 1) { sess_fail(comp->ice_st, PJ_ICE_STRANS_OP_KEEP_ALIVE, "TURN refresh failed", info.last_status); } else { PJ_PERROR(4,(comp->ice_st->obj_name, info.last_status, "Comp %d: TURN allocation failed, retrying", comp->comp_id)); add_update_turn(comp->ice_st, comp); } } } pj_grp_lock_dec_ref(comp->ice_st->grp_lock); pj_log_pop_indent(); } ================================================ FILE: deps/pjsip/pjnath/src/pjnath/nat_detect.c ================================================ /* $Id: nat_detect.c 4360 2013-02-21 11:26:35Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include static const char *nat_type_names[] = { "Unknown", "ErrUnknown", "Open", "Blocked", "Symmetric UDP", "Full Cone", "Symmetric", "Restricted", "Port Restricted" }; #define CHANGE_IP_FLAG 4 #define CHANGE_PORT_FLAG 2 #define CHANGE_IP_PORT_FLAG (CHANGE_IP_FLAG | CHANGE_PORT_FLAG) #define TEST_INTERVAL 50 enum test_type { ST_TEST_1, ST_TEST_2, ST_TEST_3, ST_TEST_1B, ST_MAX }; static const char *test_names[] = { "Test I: Binding request", "Test II: Binding request with change address and port request", "Test III: Binding request with change port request", "Test IB: Binding request to alternate address" }; enum timer_type { TIMER_TEST = 1, TIMER_DESTROY = 2 }; typedef struct nat_detect_session { pj_pool_t *pool; pj_grp_lock_t *grp_lock; pj_timer_heap_t *timer_heap; pj_timer_entry timer; unsigned timer_executed; void *user_data; pj_stun_nat_detect_cb *cb; pj_sock_t sock; pj_sockaddr_in local_addr; pj_ioqueue_key_t *key; pj_sockaddr_in server; pj_sockaddr_in *cur_server; pj_stun_session *stun_sess; pj_ioqueue_op_key_t read_op, write_op; pj_uint8_t rx_pkt[PJ_STUN_MAX_PKT_LEN]; pj_ssize_t rx_pkt_len; pj_sockaddr_in src_addr; int src_addr_len; struct result { pj_bool_t executed; pj_bool_t complete; pj_status_t status; pj_sockaddr_in ma; pj_sockaddr_in ca; pj_stun_tx_data *tdata; } result[ST_MAX]; } nat_detect_session; static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read); static void on_request_complete(pj_stun_session *sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len); static pj_status_t on_send_msg(pj_stun_session *sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len); static pj_status_t send_test(nat_detect_session *sess, enum test_type test_id, const pj_sockaddr_in *alt_addr, pj_uint32_t change_flag); static void on_sess_timer(pj_timer_heap_t *th, pj_timer_entry *te); static void sess_destroy(nat_detect_session *sess); static void sess_on_destroy(void *member); /* * Get the NAT name from the specified NAT type. */ PJ_DEF(const char*) pj_stun_get_nat_name(pj_stun_nat_type type) { PJ_ASSERT_RETURN(type >= 0 && type <= PJ_STUN_NAT_TYPE_PORT_RESTRICTED, "*Invalid*"); return nat_type_names[type]; } static int test_executed(nat_detect_session *sess) { unsigned i, count; for (i=0, count=0; iresult); ++i) { if (sess->result[i].executed) ++count; } return count; } static int test_completed(nat_detect_session *sess) { unsigned i, count; for (i=0, count=0; iresult); ++i) { if (sess->result[i].complete) ++count; } return count; } static pj_status_t get_local_interface(const pj_sockaddr_in *server, pj_in_addr *local_addr) { pj_sock_t sock; pj_sockaddr_in tmp; int addr_len; pj_status_t status; status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock); if (status != PJ_SUCCESS) return status; status = pj_sock_bind_in(sock, 0, 0); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } status = pj_sock_connect(sock, server, sizeof(pj_sockaddr_in)); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } addr_len = sizeof(pj_sockaddr_in); status = pj_sock_getsockname(sock, &tmp, &addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } local_addr->s_addr = tmp.sin_addr.s_addr; pj_sock_close(sock); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_stun_detect_nat_type(const pj_sockaddr_in *server, pj_stun_config *stun_cfg, void *user_data, pj_stun_nat_detect_cb *cb) { pj_pool_t *pool; nat_detect_session *sess; pj_stun_session_cb sess_cb; pj_ioqueue_callback ioqueue_cb; int addr_len; pj_status_t status; PJ_ASSERT_RETURN(server && stun_cfg, PJ_EINVAL); PJ_ASSERT_RETURN(stun_cfg->pf && stun_cfg->ioqueue && stun_cfg->timer_heap, PJ_EINVAL); /* * Init NAT detection session. */ pool = pj_pool_create(stun_cfg->pf, "natck%p", PJNATH_POOL_LEN_NATCK, PJNATH_POOL_INC_NATCK, NULL); if (!pool) return PJ_ENOMEM; sess = PJ_POOL_ZALLOC_T(pool, nat_detect_session); sess->pool = pool; sess->user_data = user_data; sess->cb = cb; status = pj_grp_lock_create(pool, NULL, &sess->grp_lock); if (status != PJ_SUCCESS) { /* Group lock not created yet, just destroy pool and return */ pj_pool_release(pool); return status; } pj_grp_lock_add_ref(sess->grp_lock); pj_grp_lock_add_handler(sess->grp_lock, pool, sess, &sess_on_destroy); pj_memcpy(&sess->server, server, sizeof(pj_sockaddr_in)); /* * Init timer to self-destroy. */ sess->timer_heap = stun_cfg->timer_heap; sess->timer.cb = &on_sess_timer; sess->timer.user_data = sess; /* * Initialize socket. */ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sess->sock); if (status != PJ_SUCCESS) goto on_error; /* * Bind to any. */ pj_bzero(&sess->local_addr, sizeof(pj_sockaddr_in)); sess->local_addr.sin_family = pj_AF_INET(); status = pj_sock_bind(sess->sock, &sess->local_addr, sizeof(pj_sockaddr_in)); if (status != PJ_SUCCESS) goto on_error; /* * Get local/bound address. */ addr_len = sizeof(sess->local_addr); status = pj_sock_getsockname(sess->sock, &sess->local_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; /* * Find out which interface is used to send to the server. */ status = get_local_interface(server, &sess->local_addr.sin_addr); if (status != PJ_SUCCESS) goto on_error; PJ_LOG(5,(sess->pool->obj_name, "Local address is %s:%d", pj_inet_ntoa(sess->local_addr.sin_addr), pj_ntohs(sess->local_addr.sin_port))); PJ_LOG(5,(sess->pool->obj_name, "Server set to %s:%d", pj_inet_ntoa(server->sin_addr), pj_ntohs(server->sin_port))); /* * Register socket to ioqueue to receive asynchronous input * notification. */ pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb)); ioqueue_cb.on_read_complete = &on_read_complete; status = pj_ioqueue_register_sock2(sess->pool, stun_cfg->ioqueue, sess->sock, sess->grp_lock, sess, &ioqueue_cb, &sess->key); if (status != PJ_SUCCESS) goto on_error; /* * Create STUN session. */ pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_request_complete = &on_request_complete; sess_cb.on_send_msg = &on_send_msg; status = pj_stun_session_create(stun_cfg, pool->obj_name, &sess_cb, PJ_FALSE, sess->grp_lock, &sess->stun_sess); if (status != PJ_SUCCESS) goto on_error; pj_stun_session_set_user_data(sess->stun_sess, sess); /* * Kick-off ioqueue reading. */ pj_ioqueue_op_key_init(&sess->read_op, sizeof(sess->read_op)); pj_ioqueue_op_key_init(&sess->write_op, sizeof(sess->write_op)); on_read_complete(sess->key, &sess->read_op, 0); /* * Start TEST_1 */ sess->timer.id = TIMER_TEST; on_sess_timer(stun_cfg->timer_heap, &sess->timer); return PJ_SUCCESS; on_error: sess_destroy(sess); return status; } static void sess_destroy(nat_detect_session *sess) { if (sess->stun_sess) { pj_stun_session_destroy(sess->stun_sess); sess->stun_sess = NULL; } if (sess->key) { pj_ioqueue_unregister(sess->key); sess->key = NULL; sess->sock = PJ_INVALID_SOCKET; } else if (sess->sock && sess->sock != PJ_INVALID_SOCKET) { pj_sock_close(sess->sock); sess->sock = PJ_INVALID_SOCKET; } if (sess->grp_lock) { pj_grp_lock_dec_ref(sess->grp_lock); } } static void sess_on_destroy(void *member) { nat_detect_session *sess = (nat_detect_session*)member; if (sess->pool) { pj_pool_release(sess->pool); } } static void end_session(nat_detect_session *sess, pj_status_t status, pj_stun_nat_type nat_type) { pj_stun_nat_detect_result result; char errmsg[PJ_ERR_MSG_SIZE]; pj_time_val delay; if (sess->timer.id != 0) { pj_timer_heap_cancel(sess->timer_heap, &sess->timer); sess->timer.id = 0; } pj_bzero(&result, sizeof(result)); errmsg[0] = '\0'; result.status_text = errmsg; result.status = status; pj_strerror(status, errmsg, sizeof(errmsg)); result.nat_type = nat_type; result.nat_type_name = nat_type_names[result.nat_type]; if (sess->cb) (*sess->cb)(sess->user_data, &result); delay.sec = 0; delay.msec = 0; sess->timer.id = TIMER_DESTROY; pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &delay); } /* * Callback upon receiving packet from network. */ static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { nat_detect_session *sess; pj_status_t status; sess = (nat_detect_session *) pj_ioqueue_get_user_data(key); pj_assert(sess != NULL); pj_grp_lock_acquire(sess->grp_lock); /* Ignore packet when STUN session has been destroyed */ if (!sess->stun_sess) goto on_return; if (bytes_read < 0) { if (-bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) && -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) { /* Permanent error */ end_session(sess, (pj_status_t)-bytes_read, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); goto on_return; } } else if (bytes_read > 0) { pj_stun_session_on_rx_pkt(sess->stun_sess, sess->rx_pkt, bytes_read, PJ_STUN_IS_DATAGRAM|PJ_STUN_CHECK_PACKET, NULL, NULL, &sess->src_addr, sess->src_addr_len); } sess->rx_pkt_len = sizeof(sess->rx_pkt); sess->src_addr_len = sizeof(sess->src_addr); status = pj_ioqueue_recvfrom(key, op_key, sess->rx_pkt, &sess->rx_pkt_len, PJ_IOQUEUE_ALWAYS_ASYNC, &sess->src_addr, &sess->src_addr_len); if (status != PJ_EPENDING) { pj_assert(status != PJ_SUCCESS); end_session(sess, status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); } on_return: pj_grp_lock_release(sess->grp_lock); } /* * Callback to send outgoing packet from STUN session. */ static pj_status_t on_send_msg(pj_stun_session *stun_sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len) { nat_detect_session *sess; pj_ssize_t pkt_len; pj_status_t status; PJ_UNUSED_ARG(token); sess = (nat_detect_session*) pj_stun_session_get_user_data(stun_sess); pkt_len = pkt_size; status = pj_ioqueue_sendto(sess->key, &sess->write_op, pkt, &pkt_len, 0, dst_addr, addr_len); return status; } /* * Callback upon request completion. */ static void on_request_complete(pj_stun_session *stun_sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { nat_detect_session *sess; pj_stun_sockaddr_attr *mattr = NULL; pj_stun_changed_addr_attr *ca = NULL; pj_uint32_t *tsx_id; int cmp; unsigned test_id; PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(tdata); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); sess = (nat_detect_session*) pj_stun_session_get_user_data(stun_sess); pj_grp_lock_acquire(sess->grp_lock); /* Find errors in the response */ if (status == PJ_SUCCESS) { /* Check error message */ if (PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type)) { pj_stun_errcode_attr *eattr; int err_code; eattr = (pj_stun_errcode_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0); if (eattr != NULL) err_code = eattr->err_code; else err_code = PJ_STUN_SC_SERVER_ERROR; status = PJ_STATUS_FROM_STUN_CODE(err_code); } else { /* Get MAPPED-ADDRESS or XOR-MAPPED-ADDRESS */ mattr = (pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0); if (mattr == NULL) { mattr = (pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0); } if (mattr == NULL) { status = PJNATH_ESTUNNOMAPPEDADDR; } /* Get CHANGED-ADDRESS attribute */ ca = (pj_stun_changed_addr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_CHANGED_ADDR, 0); if (ca == NULL) { status = PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR); } } } /* Save the result */ tsx_id = (pj_uint32_t*) tdata->msg->hdr.tsx_id; test_id = tsx_id[2]; if (test_id >= ST_MAX) { PJ_LOG(4,(sess->pool->obj_name, "Invalid transaction ID %u in response", test_id)); end_session(sess, PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR), PJ_STUN_NAT_TYPE_ERR_UNKNOWN); goto on_return; } PJ_LOG(5,(sess->pool->obj_name, "Completed %s, status=%d", test_names[test_id], status)); sess->result[test_id].complete = PJ_TRUE; sess->result[test_id].status = status; if (status == PJ_SUCCESS) { pj_memcpy(&sess->result[test_id].ma, &mattr->sockaddr.ipv4, sizeof(pj_sockaddr_in)); pj_memcpy(&sess->result[test_id].ca, &ca->sockaddr.ipv4, sizeof(pj_sockaddr_in)); } /* Send Test 1B only when Test 2 completes. Must not send Test 1B * before Test 2 completes to avoid creating mapping on the NAT. */ if (!sess->result[ST_TEST_1B].executed && sess->result[ST_TEST_2].complete && sess->result[ST_TEST_2].status != PJ_SUCCESS && sess->result[ST_TEST_1].complete && sess->result[ST_TEST_1].status == PJ_SUCCESS) { cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma, sizeof(pj_sockaddr_in)); if (cmp != 0) send_test(sess, ST_TEST_1B, &sess->result[ST_TEST_1].ca, 0); } if (test_completed(sess)<3 || test_completed(sess)!=test_executed(sess)) goto on_return; /* Handle the test result according to RFC 3489 page 22: +--------+ | Test | | 1 | +--------+ | | V /\ /\ N / \ Y / \ Y +--------+ UDP <-------/Resp\--------->/ IP \------------->| Test | Blocked \ ? / \Same/ | 2 | \ / \? / +--------+ \/ \/ | | N | | V V /\ +--------+ Sym. N / \ | Test | UDP <---/Resp\ | 2 | Firewall \ ? / +--------+ \ / | \/ V |Y /\ /\ | Symmetric N / \ +--------+ N / \ V NAT <--- / IP \<-----| Test |<--- /Resp\ Open \Same/ | 1B | \ ? / Internet \? / +--------+ \ / \/ \/ | |Y | | | V | Full | Cone V /\ +--------+ / \ Y | Test |------>/Resp\---->Restricted | 3 | \ ? / +--------+ \ / \/ |N | Port +------>Restricted Figure 2: Flow for type discovery process */ switch (sess->result[ST_TEST_1].status) { case PJNATH_ESTUNTIMEDOUT: /* * Test 1 has timed-out. Conclude with NAT_TYPE_BLOCKED. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_BLOCKED); break; case PJ_SUCCESS: /* * Test 1 is successful. Further tests are needed to detect * NAT type. Compare the MAPPED-ADDRESS with the local address. */ cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma, sizeof(pj_sockaddr_in)); if (cmp==0) { /* * MAPPED-ADDRESS and local address is equal. Need one more * test to determine NAT type. */ switch (sess->result[ST_TEST_2].status) { case PJ_SUCCESS: /* * Test 2 is also successful. We're in the open. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_OPEN); break; case PJNATH_ESTUNTIMEDOUT: /* * Test 2 has timed out. We're behind somekind of UDP * firewall. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_SYMMETRIC_UDP); break; default: /* * We've got other error with Test 2. */ end_session(sess, sess->result[ST_TEST_2].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } } else { /* * MAPPED-ADDRESS is different than local address. * We're behind NAT. */ switch (sess->result[ST_TEST_2].status) { case PJ_SUCCESS: /* * Test 2 is successful. We're behind a full-cone NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_FULL_CONE); break; case PJNATH_ESTUNTIMEDOUT: /* * Test 2 has timed-out Check result of test 1B.. */ switch (sess->result[ST_TEST_1B].status) { case PJ_SUCCESS: /* * Compare the MAPPED-ADDRESS of test 1B with the * MAPPED-ADDRESS returned in test 1.. */ cmp = pj_memcmp(&sess->result[ST_TEST_1].ma, &sess->result[ST_TEST_1B].ma, sizeof(pj_sockaddr_in)); if (cmp != 0) { /* * MAPPED-ADDRESS is different, we're behind a * symmetric NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_SYMMETRIC); } else { /* * MAPPED-ADDRESS is equal. We're behind a restricted * or port-restricted NAT, depending on the result of * test 3. */ switch (sess->result[ST_TEST_3].status) { case PJ_SUCCESS: /* * Test 3 is successful, we're behind a restricted * NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_RESTRICTED); break; case PJNATH_ESTUNTIMEDOUT: /* * Test 3 failed, we're behind a port restricted * NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_PORT_RESTRICTED); break; default: /* * Got other error with test 3. */ end_session(sess, sess->result[ST_TEST_3].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } } break; case PJNATH_ESTUNTIMEDOUT: /* * Strangely test 1B has failed. Maybe connectivity was * lost? Or perhaps port 3489 (the usual port number in * CHANGED-ADDRESS) is blocked? */ switch (sess->result[ST_TEST_3].status) { case PJ_SUCCESS: /* Although test 1B failed, test 3 was successful. * It could be that port 3489 is blocked, while the * NAT itself looks to be a Restricted one. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_RESTRICTED); break; default: /* Can't distinguish between Symmetric and Port * Restricted, so set the type to Unknown */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } break; default: /* * Got other error with test 1B. */ end_session(sess, sess->result[ST_TEST_1B].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } break; default: /* * We've got other error with Test 2. */ end_session(sess, sess->result[ST_TEST_2].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } } break; default: /* * We've got other error with Test 1. */ end_session(sess, sess->result[ST_TEST_1].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } on_return: pj_grp_lock_release(sess->grp_lock); } /* Perform test */ static pj_status_t send_test(nat_detect_session *sess, enum test_type test_id, const pj_sockaddr_in *alt_addr, pj_uint32_t change_flag) { pj_uint32_t magic, tsx_id[3]; pj_status_t status; sess->result[test_id].executed = PJ_TRUE; /* Randomize tsx id */ do { magic = pj_rand(); } while (magic == PJ_STUN_MAGIC); tsx_id[0] = pj_rand(); tsx_id[1] = pj_rand(); tsx_id[2] = test_id; /* Create BIND request */ status = pj_stun_session_create_req(sess->stun_sess, PJ_STUN_BINDING_REQUEST, magic, (pj_uint8_t*)tsx_id, &sess->result[test_id].tdata); if (status != PJ_SUCCESS) goto on_error; /* Add CHANGE-REQUEST attribute */ status = pj_stun_msg_add_uint_attr(sess->pool, sess->result[test_id].tdata->msg, PJ_STUN_ATTR_CHANGE_REQUEST, change_flag); if (status != PJ_SUCCESS) goto on_error; /* Configure alternate address */ if (alt_addr) sess->cur_server = (pj_sockaddr_in*) alt_addr; else sess->cur_server = &sess->server; PJ_LOG(5,(sess->pool->obj_name, "Performing %s to %s:%d", test_names[test_id], pj_inet_ntoa(sess->cur_server->sin_addr), pj_ntohs(sess->cur_server->sin_port))); /* Send the request */ status = pj_stun_session_send_msg(sess->stun_sess, NULL, PJ_TRUE, PJ_TRUE, sess->cur_server, sizeof(pj_sockaddr_in), sess->result[test_id].tdata); if (status != PJ_SUCCESS) goto on_error; return PJ_SUCCESS; on_error: sess->result[test_id].complete = PJ_TRUE; sess->result[test_id].status = status; return status; } /* Timer callback */ static void on_sess_timer(pj_timer_heap_t *th, pj_timer_entry *te) { nat_detect_session *sess; sess = (nat_detect_session*) te->user_data; if (te->id == TIMER_DESTROY) { pj_grp_lock_acquire(sess->grp_lock); pj_ioqueue_unregister(sess->key); sess->key = NULL; sess->sock = PJ_INVALID_SOCKET; te->id = 0; pj_grp_lock_release(sess->grp_lock); sess_destroy(sess); } else if (te->id == TIMER_TEST) { pj_bool_t next_timer; pj_grp_lock_acquire(sess->grp_lock); next_timer = PJ_FALSE; if (sess->timer_executed == 0) { send_test(sess, ST_TEST_1, NULL, 0); next_timer = PJ_TRUE; } else if (sess->timer_executed == 1) { send_test(sess, ST_TEST_2, NULL, CHANGE_IP_PORT_FLAG); next_timer = PJ_TRUE; } else if (sess->timer_executed == 2) { send_test(sess, ST_TEST_3, NULL, CHANGE_PORT_FLAG); } else { pj_assert(!"Shouldn't have timer at this state"); } ++sess->timer_executed; if (next_timer) { pj_time_val delay = {0, TEST_INTERVAL}; pj_timer_heap_schedule(th, te, &delay); } else { te->id = 0; } pj_grp_lock_release(sess->grp_lock); } else { pj_assert(!"Invalid timer ID"); } } ================================================ FILE: deps/pjsip/pjnath/src/pjnath/stun_auth.c ================================================ /* $Id: stun_auth.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #define THIS_FILE "stun_auth.c" /* Duplicate credential */ PJ_DEF(void) pj_stun_auth_cred_dup( pj_pool_t *pool, pj_stun_auth_cred *dst, const pj_stun_auth_cred *src) { dst->type = src->type; switch (src->type) { case PJ_STUN_AUTH_CRED_STATIC: pj_strdup(pool, &dst->data.static_cred.realm, &src->data.static_cred.realm); pj_strdup(pool, &dst->data.static_cred.username, &src->data.static_cred.username); dst->data.static_cred.data_type = src->data.static_cred.data_type; pj_strdup(pool, &dst->data.static_cred.data, &src->data.static_cred.data); pj_strdup(pool, &dst->data.static_cred.nonce, &src->data.static_cred.nonce); break; case PJ_STUN_AUTH_CRED_DYNAMIC: pj_memcpy(&dst->data.dyn_cred, &src->data.dyn_cred, sizeof(src->data.dyn_cred)); break; } } /* * Duplicate request credential. */ PJ_DEF(void) pj_stun_req_cred_info_dup( pj_pool_t *pool, pj_stun_req_cred_info *dst, const pj_stun_req_cred_info *src) { pj_strdup(pool, &dst->realm, &src->realm); pj_strdup(pool, &dst->username, &src->username); pj_strdup(pool, &dst->nonce, &src->nonce); pj_strdup(pool, &dst->auth_key, &src->auth_key); } /* Calculate HMAC-SHA1 key for long term credential, by getting * MD5 digest of username, realm, and password. */ static void calc_md5_key(pj_uint8_t digest[16], const pj_str_t *realm, const pj_str_t *username, const pj_str_t *passwd) { /* The 16-byte key for MESSAGE-INTEGRITY HMAC is formed by taking * the MD5 hash of the result of concatenating the following five * fields: (1) The username, with any quotes and trailing nulls * removed, (2) A single colon, (3) The realm, with any quotes and * trailing nulls removed, (4) A single colon, and (5) The * password, with any trailing nulls removed. */ pj_md5_context ctx; pj_str_t s; pj_md5_init(&ctx); #define REMOVE_QUOTE(s) if (s.slen && *s.ptr=='"') \ s.ptr++, s.slen--; \ if (s.slen && s.ptr[s.slen-1]=='"') \ s.slen--; /* Add username */ s = *username; REMOVE_QUOTE(s); pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, (unsigned)s.slen); /* Add single colon */ pj_md5_update(&ctx, (pj_uint8_t*)":", 1); /* Add realm */ s = *realm; REMOVE_QUOTE(s); pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, (unsigned)s.slen); #undef REMOVE_QUOTE /* Another colon */ pj_md5_update(&ctx, (pj_uint8_t*)":", 1); /* Add password */ pj_md5_update(&ctx, (pj_uint8_t*)passwd->ptr, (unsigned)passwd->slen); /* Done */ pj_md5_final(&ctx, digest); } /* * Create authentication key to be used for encoding the message with * MESSAGE-INTEGRITY. */ PJ_DEF(void) pj_stun_create_key(pj_pool_t *pool, pj_str_t *key, const pj_str_t *realm, const pj_str_t *username, pj_stun_passwd_type data_type, const pj_str_t *data) { PJ_ASSERT_ON_FAIL(pool && key && username && data, return); if (realm && realm->slen) { if (data_type == PJ_STUN_PASSWD_PLAIN) { key->ptr = (char*) pj_pool_alloc(pool, 16); calc_md5_key((pj_uint8_t*)key->ptr, realm, username, data); key->slen = 16; } else { pj_strdup(pool, key, data); } } else { pj_assert(data_type == PJ_STUN_PASSWD_PLAIN); pj_strdup(pool, key, data); } } /*unused PJ_INLINE(pj_uint16_t) GET_VAL16(const pj_uint8_t *pdu, unsigned pos) { return (pj_uint16_t) ((pdu[pos] << 8) + pdu[pos+1]); }*/ PJ_INLINE(void) PUT_VAL16(pj_uint8_t *buf, unsigned pos, pj_uint16_t hval) { buf[pos+0] = (pj_uint8_t) ((hval & 0xFF00) >> 8); buf[pos+1] = (pj_uint8_t) ((hval & 0x00FF) >> 0); } /* Send 401 response */ static pj_status_t create_challenge(pj_pool_t *pool, const pj_stun_msg *msg, int err_code, const char *errstr, const pj_str_t *realm, const pj_str_t *nonce, pj_stun_msg **p_response) { pj_stun_msg *response; pj_str_t tmp_nonce; pj_str_t err_msg; pj_status_t rc; rc = pj_stun_msg_create_response(pool, msg, err_code, (errstr?pj_cstr(&err_msg, errstr):NULL), &response); if (rc != PJ_SUCCESS) return rc; /* SHOULD NOT add REALM, NONCE, USERNAME, and M-I on 400 response */ if (err_code!=400 && realm && realm->slen) { rc = pj_stun_msg_add_string_attr(pool, response, PJ_STUN_ATTR_REALM, realm); if (rc != PJ_SUCCESS) return rc; /* long term must include nonce */ if (!nonce || nonce->slen == 0) { tmp_nonce = pj_str("pjstun"); nonce = &tmp_nonce; } } if (err_code!=400 && nonce && nonce->slen) { rc = pj_stun_msg_add_string_attr(pool, response, PJ_STUN_ATTR_NONCE, nonce); if (rc != PJ_SUCCESS) return rc; } *p_response = response; return PJ_SUCCESS; } /* Verify credential in the request */ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_msg *msg, pj_stun_auth_cred *cred, pj_pool_t *pool, pj_stun_req_cred_info *p_info, pj_stun_msg **p_response) { pj_stun_req_cred_info tmp_info; const pj_stun_msgint_attr *amsgi; unsigned i, amsgi_pos; pj_bool_t has_attr_beyond_mi; const pj_stun_username_attr *auser; const pj_stun_realm_attr *arealm; const pj_stun_realm_attr *anonce; pj_hmac_sha1_context ctx; pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE]; pj_stun_status err_code; const char *err_text = NULL; pj_status_t status; /* msg and credential MUST be specified */ PJ_ASSERT_RETURN(pkt && pkt_len && msg && cred, PJ_EINVAL); /* If p_response is specified, pool MUST be specified. */ PJ_ASSERT_RETURN(!p_response || pool, PJ_EINVAL); if (p_response) *p_response = NULL; if (!PJ_STUN_IS_REQUEST(msg->hdr.type)) p_response = NULL; if (p_info == NULL) p_info = &tmp_info; pj_bzero(p_info, sizeof(pj_stun_req_cred_info)); /* Get realm and nonce from credential */ p_info->realm.slen = p_info->nonce.slen = 0; if (cred->type == PJ_STUN_AUTH_CRED_STATIC) { p_info->realm = cred->data.static_cred.realm; p_info->nonce = cred->data.static_cred.nonce; } else if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) { status = cred->data.dyn_cred.get_auth(cred->data.dyn_cred.user_data, pool, &p_info->realm, &p_info->nonce); if (status != PJ_SUCCESS) return status; } else { pj_assert(!"Invalid credential type"); return PJ_EBUG; } /* Look for MESSAGE-INTEGRITY while counting the position */ amsgi_pos = 0; has_attr_beyond_mi = PJ_FALSE; amsgi = NULL; for (i=0; iattr_count; ++i) { if (msg->attr[i]->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY) { amsgi = (const pj_stun_msgint_attr*) msg->attr[i]; } else if (amsgi) { has_attr_beyond_mi = PJ_TRUE; break; } else { amsgi_pos += ((msg->attr[i]->length+3) & ~0x03) + 4; } } if (amsgi == NULL) { /* According to rfc3489bis-10 Sec 10.1.2/10.2.2, we should return 400 for short term, and 401 for long term. The rule has been changed from rfc3489bis-06 */ err_code = p_info->realm.slen ? PJ_STUN_SC_UNAUTHORIZED : PJ_STUN_SC_BAD_REQUEST; goto on_auth_failed; } /* Next check that USERNAME is present */ auser = (const pj_stun_username_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0); if (auser == NULL) { /* According to rfc3489bis-10 Sec 10.1.2/10.2.2, we should return 400 for both short and long term, since M-I is present. The rule has been changed from rfc3489bis-06 */ err_code = PJ_STUN_SC_BAD_REQUEST; err_text = "Missing USERNAME"; goto on_auth_failed; } /* Get REALM, if any */ arealm = (const pj_stun_realm_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0); /* Reject with 400 if we have long term credential and the request * is missing REALM attribute. */ if (p_info->realm.slen && arealm==NULL) { err_code = PJ_STUN_SC_BAD_REQUEST; err_text = "Missing REALM"; goto on_auth_failed; } /* Check if username match */ if (cred->type == PJ_STUN_AUTH_CRED_STATIC) { pj_bool_t username_ok; username_ok = !pj_strcmp(&auser->value, &cred->data.static_cred.username); if (username_ok) { pj_strdup(pool, &p_info->username, &cred->data.static_cred.username); pj_stun_create_key(pool, &p_info->auth_key, &p_info->realm, &auser->value, cred->data.static_cred.data_type, &cred->data.static_cred.data); } else { /* Username mismatch */ /* According to rfc3489bis-10 Sec 10.1.2/10.2.2, we should * return 401 */ err_code = PJ_STUN_SC_UNAUTHORIZED; goto on_auth_failed; } } else if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) { pj_stun_passwd_type data_type = PJ_STUN_PASSWD_PLAIN; pj_str_t password; pj_status_t rc; rc = cred->data.dyn_cred.get_password(msg, cred->data.dyn_cred.user_data, (arealm?&arealm->value:NULL), &auser->value, pool, &data_type, &password); if (rc == PJ_SUCCESS) { pj_strdup(pool, &p_info->username, &auser->value); pj_stun_create_key(pool, &p_info->auth_key, (arealm?&arealm->value:NULL), &auser->value, data_type, &password); } else { err_code = PJ_STUN_SC_UNAUTHORIZED; goto on_auth_failed; } } else { pj_assert(!"Invalid credential type"); return PJ_EBUG; } /* Get NONCE attribute */ anonce = (pj_stun_nonce_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_NONCE, 0); /* Check for long term/short term requirements. */ if (p_info->realm.slen != 0 && arealm == NULL) { /* Long term credential is required and REALM is not present */ err_code = PJ_STUN_SC_BAD_REQUEST; err_text = "Missing REALM"; goto on_auth_failed; } else if (p_info->realm.slen != 0 && arealm != NULL) { /* We want long term, and REALM is present */ /* NONCE must be present. */ if (anonce == NULL && p_info->nonce.slen) { err_code = PJ_STUN_SC_BAD_REQUEST; err_text = "Missing NONCE"; goto on_auth_failed; } /* Verify REALM matches */ if (pj_stricmp(&arealm->value, &p_info->realm)) { /* REALM doesn't match */ err_code = PJ_STUN_SC_UNAUTHORIZED; err_text = "Invalid REALM"; goto on_auth_failed; } /* Valid case, will validate the message integrity later */ } else if (p_info->realm.slen == 0 && arealm != NULL) { /* We want to use short term credential, but client uses long * term credential. The draft doesn't mention anything about * switching between long term and short term. */ /* For now just accept the credential, anyway it will probably * cause wrong message integrity value later. */ } else if (p_info->realm.slen==0 && arealm == NULL) { /* Short term authentication is wanted, and one is supplied */ /* Application MAY request NONCE to be supplied */ if (p_info->nonce.slen != 0) { err_code = PJ_STUN_SC_UNAUTHORIZED; err_text = "NONCE required"; goto on_auth_failed; } } /* If NONCE is present, validate it */ if (anonce) { pj_bool_t ok; if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC && cred->data.dyn_cred.verify_nonce != NULL) { ok=cred->data.dyn_cred.verify_nonce(msg, cred->data.dyn_cred.user_data, (arealm?&arealm->value:NULL), &auser->value, &anonce->value); } else if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) { ok = PJ_TRUE; } else { if (p_info->nonce.slen) { ok = !pj_strcmp(&anonce->value, &p_info->nonce); } else { ok = PJ_TRUE; } } if (!ok) { err_code = PJ_STUN_SC_STALE_NONCE; goto on_auth_failed; } } /* Now calculate HMAC of the message. */ pj_hmac_sha1_init(&ctx, (pj_uint8_t*)p_info->auth_key.ptr, (unsigned)p_info->auth_key.slen); #if PJ_STUN_OLD_STYLE_MI_FINGERPRINT /* Pre rfc3489bis-06 style of calculation */ pj_hmac_sha1_update(&ctx, pkt, 20); #else /* First calculate HMAC for the header. * The calculation is different depending on whether FINGERPRINT attribute * is present in the message. */ if (has_attr_beyond_mi) { pj_uint8_t hdr_copy[20]; pj_memcpy(hdr_copy, pkt, 20); PUT_VAL16(hdr_copy, 2, (pj_uint16_t)(amsgi_pos + 24)); pj_hmac_sha1_update(&ctx, hdr_copy, 20); } else { pj_hmac_sha1_update(&ctx, pkt, 20); } #endif /* PJ_STUN_OLD_STYLE_MI_FINGERPRINT */ /* Now update with the message body */ pj_hmac_sha1_update(&ctx, pkt+20, amsgi_pos); #if PJ_STUN_OLD_STYLE_MI_FINGERPRINT // This is no longer necessary as per rfc3489bis-08 if ((amsgi_pos+20) & 0x3F) { pj_uint8_t zeroes[64]; pj_bzero(zeroes, sizeof(zeroes)); pj_hmac_sha1_update(&ctx, zeroes, 64-((amsgi_pos+20) & 0x3F)); } #endif pj_hmac_sha1_final(&ctx, digest); /* Compare HMACs */ if (pj_memcmp(amsgi->hmac, digest, 20)) { /* HMAC value mismatch */ /* According to rfc3489bis-10 Sec 10.1.2 we should return 401 */ err_code = PJ_STUN_SC_UNAUTHORIZED; err_text = "MESSAGE-INTEGRITY mismatch"; goto on_auth_failed; } /* Everything looks okay! */ return PJ_SUCCESS; on_auth_failed: if (p_response) { create_challenge(pool, msg, err_code, err_text, &p_info->realm, &p_info->nonce, p_response); } return PJ_STATUS_FROM_STUN_CODE(err_code); } /* Determine if STUN message can be authenticated */ PJ_DEF(pj_bool_t) pj_stun_auth_valid_for_msg(const pj_stun_msg *msg) { unsigned msg_type = msg->hdr.type; const pj_stun_errcode_attr *err_attr; /* STUN requests and success response can be authenticated */ if (!PJ_STUN_IS_ERROR_RESPONSE(msg_type) && !PJ_STUN_IS_INDICATION(msg_type)) { return PJ_TRUE; } /* STUN Indication cannot be authenticated */ if (PJ_STUN_IS_INDICATION(msg_type)) return PJ_FALSE; /* Authentication for STUN error responses depend on the error * code. */ err_attr = (const pj_stun_errcode_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ERROR_CODE, 0); if (err_attr == NULL) { PJ_LOG(4,(THIS_FILE, "STUN error code attribute not present in " "error response")); return PJ_TRUE; } switch (err_attr->err_code) { case PJ_STUN_SC_BAD_REQUEST: /* 400 (Bad Request) */ case PJ_STUN_SC_UNAUTHORIZED: /* 401 (Unauthorized) */ case PJ_STUN_SC_STALE_NONCE: /* 438 (Stale Nonce) */ /* Due to the way this response is generated here, we can't really * authenticate 420 (Unknown Attribute) response */ case PJ_STUN_SC_UNKNOWN_ATTRIBUTE: return PJ_FALSE; default: return PJ_TRUE; } } /* Authenticate MESSAGE-INTEGRITY in the response */ PJ_DEF(pj_status_t) pj_stun_authenticate_response(const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_msg *msg, const pj_str_t *key) { const pj_stun_msgint_attr *amsgi; unsigned i, amsgi_pos; pj_bool_t has_attr_beyond_mi; pj_hmac_sha1_context ctx; pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE]; PJ_ASSERT_RETURN(pkt && pkt_len && msg && key, PJ_EINVAL); /* First check that MESSAGE-INTEGRITY is present */ amsgi = (const pj_stun_msgint_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0); if (amsgi == NULL) { return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); } /* Check that message length is valid */ if (msg->hdr.length < 24) { return PJNATH_EINSTUNMSGLEN; } /* Look for MESSAGE-INTEGRITY while counting the position */ amsgi_pos = 0; has_attr_beyond_mi = PJ_FALSE; amsgi = NULL; for (i=0; iattr_count; ++i) { if (msg->attr[i]->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY) { amsgi = (const pj_stun_msgint_attr*) msg->attr[i]; } else if (amsgi) { has_attr_beyond_mi = PJ_TRUE; break; } else { amsgi_pos += ((msg->attr[i]->length+3) & ~0x03) + 4; } } if (amsgi == NULL) { return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); } /* Now calculate HMAC of the message. */ pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key->ptr, (unsigned)key->slen); #if PJ_STUN_OLD_STYLE_MI_FINGERPRINT /* Pre rfc3489bis-06 style of calculation */ pj_hmac_sha1_update(&ctx, pkt, 20); #else /* First calculate HMAC for the header. * The calculation is different depending on whether FINGERPRINT attribute * is present in the message. */ if (has_attr_beyond_mi) { pj_uint8_t hdr_copy[20]; pj_memcpy(hdr_copy, pkt, 20); PUT_VAL16(hdr_copy, 2, (pj_uint16_t)(amsgi_pos+24)); pj_hmac_sha1_update(&ctx, hdr_copy, 20); } else { pj_hmac_sha1_update(&ctx, pkt, 20); } #endif /* PJ_STUN_OLD_STYLE_MI_FINGERPRINT */ /* Now update with the message body */ pj_hmac_sha1_update(&ctx, pkt+20, amsgi_pos); #if PJ_STUN_OLD_STYLE_MI_FINGERPRINT // This is no longer necessary as per rfc3489bis-08 if ((amsgi_pos+20) & 0x3F) { pj_uint8_t zeroes[64]; pj_bzero(zeroes, sizeof(zeroes)); pj_hmac_sha1_update(&ctx, zeroes, 64-((amsgi_pos+20) & 0x3F)); } #endif pj_hmac_sha1_final(&ctx, digest); /* Compare HMACs */ if (pj_memcmp(amsgi->hmac, digest, 20)) { /* HMAC value mismatch */ return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); } /* Everything looks okay! */ return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjnath/src/pjnath/stun_msg.c ================================================ /* $Id: stun_msg.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "stun_msg.c" #define STUN_XOR_FINGERPRINT 0x5354554eL static int padding_char; static const char *stun_method_names[PJ_STUN_METHOD_MAX] = { "Unknown", /* 0 */ "Binding", /* 1 */ "SharedSecret", /* 2 */ "Allocate", /* 3 */ "Refresh", /* 4 */ "???", /* 5 */ "Send", /* 6 */ "Data", /* 7 */ "CreatePermission", /* 8 */ "ChannelBind", /* 9 */ }; static struct { int err_code; const char *err_msg; } stun_err_msg_map[] = { { PJ_STUN_SC_TRY_ALTERNATE, "Try Alternate"}, { PJ_STUN_SC_BAD_REQUEST, "Bad Request"}, { PJ_STUN_SC_UNAUTHORIZED, "Unauthorized"}, { PJ_STUN_SC_FORBIDDEN, "Forbidden"}, { PJ_STUN_SC_UNKNOWN_ATTRIBUTE, "Unknown Attribute"}, //{ PJ_STUN_SC_STALE_CREDENTIALS, "Stale Credentials"}, //{ PJ_STUN_SC_INTEGRITY_CHECK_FAILURE, "Integrity Check Failure"}, //{ PJ_STUN_SC_MISSING_USERNAME, "Missing Username"}, //{ PJ_STUN_SC_USE_TLS, "Use TLS"}, //{ PJ_STUN_SC_MISSING_REALM, "Missing Realm"}, //{ PJ_STUN_SC_MISSING_NONCE, "Missing Nonce"}, //{ PJ_STUN_SC_UNKNOWN_USERNAME, "Unknown Username"}, { PJ_STUN_SC_ALLOCATION_MISMATCH, "Allocation Mismatch"}, { PJ_STUN_SC_STALE_NONCE, "Stale Nonce"}, { PJ_STUN_SC_TRANSITIONING, "Active Destination Already Set"}, { PJ_STUN_SC_WRONG_CREDENTIALS, "Wrong Credentials"}, { PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO, "Unsupported Transport Protocol"}, { PJ_STUN_SC_OPER_TCP_ONLY, "Operation for TCP Only"}, { PJ_STUN_SC_CONNECTION_FAILURE, "Connection Failure"}, { PJ_STUN_SC_CONNECTION_TIMEOUT, "Connection Timeout"}, { PJ_STUN_SC_ALLOCATION_QUOTA_REACHED, "Allocation Quota Reached"}, { PJ_STUN_SC_ROLE_CONFLICT, "Role Conflict"}, { PJ_STUN_SC_SERVER_ERROR, "Server Error"}, { PJ_STUN_SC_INSUFFICIENT_CAPACITY, "Insufficient Capacity"}, { PJ_STUN_SC_GLOBAL_FAILURE, "Global Failure"} }; struct attr_desc { const char *name; pj_status_t (*decode_attr)(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr); pj_status_t (*encode_attr)(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed); void* (*clone_attr)(pj_pool_t *pool, const void *src); }; static pj_status_t decode_sockaddr_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr); static pj_status_t decode_xored_sockaddr_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr); static pj_status_t encode_sockaddr_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed); static void* clone_sockaddr_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_string_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr); static pj_status_t encode_string_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed); static void* clone_string_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_msgint_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr); static pj_status_t encode_msgint_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed); static void* clone_msgint_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_errcode_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr); static pj_status_t encode_errcode_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed); static void* clone_errcode_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_unknown_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr); static pj_status_t encode_unknown_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed); static void* clone_unknown_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_uint_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr); static pj_status_t encode_uint_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed); static void* clone_uint_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_uint64_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr); static pj_status_t encode_uint64_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed); static void* clone_uint64_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_binary_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr); static pj_status_t encode_binary_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed); static void* clone_binary_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_empty_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr); static pj_status_t encode_empty_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed); static void* clone_empty_attr(pj_pool_t *pool, const void *src); static struct attr_desc mandatory_attr_desc[] = { { /* type zero */ NULL, NULL, NULL, NULL }, { /* PJ_STUN_ATTR_MAPPED_ADDR, */ "MAPPED-ADDRESS", &decode_sockaddr_attr, &encode_sockaddr_attr, &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_RESPONSE_ADDR, */ "RESPONSE-ADDRESS", &decode_sockaddr_attr, &encode_sockaddr_attr, &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_CHANGE_REQUEST, */ "CHANGE-REQUEST", &decode_uint_attr, &encode_uint_attr, &clone_uint_attr }, { /* PJ_STUN_ATTR_SOURCE_ADDR, */ "SOURCE-ADDRESS", &decode_sockaddr_attr, &encode_sockaddr_attr, &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_CHANGED_ADDR, */ "CHANGED-ADDRESS", &decode_sockaddr_attr, &encode_sockaddr_attr, &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_USERNAME, */ "USERNAME", &decode_string_attr, &encode_string_attr, &clone_string_attr }, { /* PJ_STUN_ATTR_PASSWORD, */ "PASSWORD", &decode_string_attr, &encode_string_attr, &clone_string_attr }, { /* PJ_STUN_ATTR_MESSAGE_INTEGRITY, */ "MESSAGE-INTEGRITY", &decode_msgint_attr, &encode_msgint_attr, &clone_msgint_attr }, { /* PJ_STUN_ATTR_ERROR_CODE, */ "ERROR-CODE", &decode_errcode_attr, &encode_errcode_attr, &clone_errcode_attr }, { /* PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES, */ "UNKNOWN-ATTRIBUTES", &decode_unknown_attr, &encode_unknown_attr, &clone_unknown_attr }, { /* PJ_STUN_ATTR_REFLECTED_FROM, */ "REFLECTED-FROM", &decode_sockaddr_attr, &encode_sockaddr_attr, &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_CHANNEL_NUMBER (0x000C) */ "CHANNEL-NUMBER", &decode_uint_attr, &encode_uint_attr, &clone_uint_attr }, { /* PJ_STUN_ATTR_LIFETIME, */ "LIFETIME", &decode_uint_attr, &encode_uint_attr, &clone_uint_attr }, { /* ID 0x000E is not assigned */ NULL, NULL, NULL, NULL }, { /* PJ_STUN_ATTR_MAGIC_COOKIE */ "MAGIC-COOKIE", &decode_uint_attr, &encode_uint_attr, &clone_uint_attr }, { /* PJ_STUN_ATTR_BANDWIDTH, */ "BANDWIDTH", &decode_uint_attr, &encode_uint_attr, &clone_uint_attr }, { /* ID 0x0011 is not assigned */ NULL, NULL, NULL, NULL }, { /* PJ_STUN_ATTR_XOR_PEER_ADDRESS, */ "XOR-PEER-ADDRESS", &decode_xored_sockaddr_attr, &encode_sockaddr_attr, &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_DATA, */ "DATA", &decode_binary_attr, &encode_binary_attr, &clone_binary_attr }, { /* PJ_STUN_ATTR_REALM, */ "REALM", &decode_string_attr, &encode_string_attr, &clone_string_attr }, { /* PJ_STUN_ATTR_NONCE, */ "NONCE", &decode_string_attr, &encode_string_attr, &clone_string_attr }, { /* PJ_STUN_ATTR_XOR_RELAYED_ADDR, */ "XOR-RELAYED-ADDRESS", &decode_xored_sockaddr_attr, &encode_sockaddr_attr, &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_REQUESTED_ADDR_TYPE, */ "REQUESTED-ADDRESS-TYPE", &decode_uint_attr, &encode_uint_attr, &clone_uint_attr }, { /* PJ_STUN_ATTR_EVEN_PORT, */ "EVEN-PORT", &decode_uint_attr, &encode_uint_attr, &clone_uint_attr }, { /* PJ_STUN_ATTR_REQUESTED_TRANSPORT, */ "REQUESTED-TRANSPORT", &decode_uint_attr, &encode_uint_attr, &clone_uint_attr }, { /* PJ_STUN_ATTR_DONT_FRAGMENT */ "DONT-FRAGMENT", &decode_empty_attr, &encode_empty_attr, &clone_empty_attr }, { /* ID 0x001B is not assigned */ NULL, NULL, NULL, NULL }, { /* ID 0x001C is not assigned */ NULL, NULL, NULL, NULL }, { /* ID 0x001D is not assigned */ NULL, NULL, NULL, NULL }, { /* ID 0x001E is not assigned */ NULL, NULL, NULL, NULL }, { /* ID 0x001F is not assigned */ NULL, NULL, NULL, NULL }, { /* PJ_STUN_ATTR_XOR_MAPPED_ADDRESS, */ "XOR-MAPPED-ADDRESS", &decode_xored_sockaddr_attr, &encode_sockaddr_attr, &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_TIMER_VAL, */ "TIMER-VAL", &decode_uint_attr, &encode_uint_attr, &clone_uint_attr }, { /* PJ_STUN_ATTR_RESERVATION_TOKEN, */ "RESERVATION-TOKEN", &decode_uint64_attr, &encode_uint64_attr, &clone_uint64_attr }, { /* PJ_STUN_ATTR_XOR_REFLECTED_FROM, */ "XOR-REFLECTED-FROM", &decode_xored_sockaddr_attr, &encode_sockaddr_attr, &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_PRIORITY, */ "PRIORITY", &decode_uint_attr, &encode_uint_attr, &clone_uint_attr }, { /* PJ_STUN_ATTR_USE_CANDIDATE, */ "USE-CANDIDATE", &decode_empty_attr, &encode_empty_attr, &clone_empty_attr }, { /* ID 0x0026 is not assigned */ NULL, NULL, NULL, NULL }, { /* ID 0x0027 is not assigned */ NULL, NULL, NULL, NULL }, { /* ID 0x0028 is not assigned */ NULL, NULL, NULL, NULL }, { /* ID 0x0029 is not assigned */ NULL, NULL, NULL, NULL }, { /* ID 0x002a is not assigned */ NULL, NULL, NULL, NULL }, { /* ID 0x002b is not assigned */ NULL, NULL, NULL, NULL }, { /* ID 0x002c is not assigned */ NULL, NULL, NULL, NULL }, { /* ID 0x002d is not assigned */ NULL, NULL, NULL, NULL }, { /* ID 0x002e is not assigned */ NULL, NULL, NULL, NULL }, { /* ID 0x002f is not assigned */ NULL, NULL, NULL, NULL }, { /* PJ_STUN_ATTR_ICMP, */ "ICMP", &decode_uint_attr, &encode_uint_attr, &clone_uint_attr }, /* Sentinel */ { /* PJ_STUN_ATTR_END_MANDATORY_ATTR */ NULL, NULL, NULL, NULL } }; static struct attr_desc extended_attr_desc[] = { { /* ID 0x8021 is not assigned */ NULL, NULL, NULL, NULL }, { /* PJ_STUN_ATTR_SOFTWARE, */ "SOFTWARE", &decode_string_attr, &encode_string_attr, &clone_string_attr }, { /* PJ_STUN_ATTR_ALTERNATE_SERVER, */ "ALTERNATE-SERVER", &decode_sockaddr_attr, &encode_sockaddr_attr, &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_REFRESH_INTERVAL, */ "REFRESH-INTERVAL", &decode_uint_attr, &encode_uint_attr, &clone_uint_attr }, { /* ID 0x8025 is not assigned*/ NULL, NULL, NULL, NULL }, { /* PADDING, 0x8026 */ NULL, NULL, NULL, NULL }, { /* CACHE-TIMEOUT, 0x8027 */ NULL, NULL, NULL, NULL }, { /* PJ_STUN_ATTR_FINGERPRINT, */ "FINGERPRINT", &decode_uint_attr, &encode_uint_attr, &clone_uint_attr }, { /* PJ_STUN_ATTR_ICE_CONTROLLED, */ "ICE-CONTROLLED", &decode_uint64_attr, &encode_uint64_attr, &clone_uint64_attr }, { /* PJ_STUN_ATTR_ICE_CONTROLLING, */ "ICE-CONTROLLING", &decode_uint64_attr, &encode_uint64_attr, &clone_uint64_attr } }; /* * Get STUN message type name. */ PJ_DEF(const char*) pj_stun_get_method_name(unsigned msg_type) { unsigned method = PJ_STUN_GET_METHOD(msg_type); if (method >= PJ_ARRAY_SIZE(stun_method_names)) return "???"; return stun_method_names[method]; } /* * Get STUN message class name. */ PJ_DEF(const char*) pj_stun_get_class_name(unsigned msg_type) { if (PJ_STUN_IS_REQUEST(msg_type)) return "request"; else if (PJ_STUN_IS_SUCCESS_RESPONSE(msg_type)) return "success response"; else if (PJ_STUN_IS_ERROR_RESPONSE(msg_type)) return "error response"; else if (PJ_STUN_IS_INDICATION(msg_type)) return "indication"; else return "???"; } static const struct attr_desc *find_attr_desc(unsigned attr_type) { struct attr_desc *desc; /* Check that attr_desc array is valid */ pj_assert(PJ_ARRAY_SIZE(mandatory_attr_desc)== PJ_STUN_ATTR_END_MANDATORY_ATTR+1); pj_assert(mandatory_attr_desc[PJ_STUN_ATTR_END_MANDATORY_ATTR].decode_attr == NULL); pj_assert(mandatory_attr_desc[PJ_STUN_ATTR_USE_CANDIDATE].decode_attr == &decode_empty_attr); pj_assert(PJ_ARRAY_SIZE(extended_attr_desc) == PJ_STUN_ATTR_END_EXTENDED_ATTR-PJ_STUN_ATTR_START_EXTENDED_ATTR); if (attr_type < PJ_STUN_ATTR_END_MANDATORY_ATTR) desc = &mandatory_attr_desc[attr_type]; else if (attr_type >= PJ_STUN_ATTR_START_EXTENDED_ATTR && attr_type < PJ_STUN_ATTR_END_EXTENDED_ATTR) desc = &extended_attr_desc[attr_type-PJ_STUN_ATTR_START_EXTENDED_ATTR]; else return NULL; return desc->decode_attr == NULL ? NULL : desc; } /* * Get STUN attribute name. */ PJ_DEF(const char*) pj_stun_get_attr_name(unsigned attr_type) { const struct attr_desc *attr_desc; attr_desc = find_attr_desc(attr_type); if (!attr_desc || attr_desc->name==NULL) return "???"; return attr_desc->name; } /** * Get STUN standard reason phrase for the specified error code. */ PJ_DEF(pj_str_t) pj_stun_get_err_reason(int err_code) { #if 0 /* Find error using linear search */ unsigned i; for (i=0; i 0) { int half = n/2; int mid = first + half; if (stun_err_msg_map[mid].err_code < err_code) { first = mid+1; n -= (half+1); } else if (stun_err_msg_map[mid].err_code > err_code) { n = half; } else { first = mid; break; } } if (stun_err_msg_map[first].err_code == err_code) { return pj_str((char*)stun_err_msg_map[first].err_msg); } else { return pj_str(NULL); } #endif } /* * Set padding character. */ PJ_DEF(int) pj_stun_set_padding_char(int chr) { int old_pad = padding_char; padding_char = chr; return old_pad; } ////////////////////////////////////////////////////////////////////////////// #define INIT_ATTR(a,t,l) (a)->hdr.type=(pj_uint16_t)(t), \ (a)->hdr.length=(pj_uint16_t)(l) #define ATTR_HDR_LEN 4 static pj_uint16_t GETVAL16H(const pj_uint8_t *buf, unsigned pos) { return (pj_uint16_t) ((buf[pos + 0] << 8) | \ (buf[pos + 1] << 0)); } /*unused PJ_INLINE(pj_uint16_t) GETVAL16N(const pj_uint8_t *buf, unsigned pos) { return pj_htons(GETVAL16H(buf,pos)); }*/ static void PUTVAL16H(pj_uint8_t *buf, unsigned pos, pj_uint16_t hval) { buf[pos+0] = (pj_uint8_t) ((hval & 0xFF00) >> 8); buf[pos+1] = (pj_uint8_t) ((hval & 0x00FF) >> 0); } PJ_INLINE(pj_uint32_t) GETVAL32H(const pj_uint8_t *buf, unsigned pos) { return (pj_uint32_t) ((buf[pos + 0] << 24UL) | \ (buf[pos + 1] << 16UL) | \ (buf[pos + 2] << 8UL) | \ (buf[pos + 3] << 0UL)); } /*unused PJ_INLINE(pj_uint32_t) GETVAL32N(const pj_uint8_t *buf, unsigned pos) { return pj_htonl(GETVAL32H(buf,pos)); }*/ static void PUTVAL32H(pj_uint8_t *buf, unsigned pos, pj_uint32_t hval) { buf[pos+0] = (pj_uint8_t) ((hval & 0xFF000000UL) >> 24); buf[pos+1] = (pj_uint8_t) ((hval & 0x00FF0000UL) >> 16); buf[pos+2] = (pj_uint8_t) ((hval & 0x0000FF00UL) >> 8); buf[pos+3] = (pj_uint8_t) ((hval & 0x000000FFUL) >> 0); } static void GETVAL64H(const pj_uint8_t *buf, unsigned pos, pj_timestamp *ts) { ts->u32.hi = GETVAL32H(buf, pos); ts->u32.lo = GETVAL32H(buf, pos+4); } static void PUTVAL64H(pj_uint8_t *buf, unsigned pos, const pj_timestamp *ts) { PUTVAL32H(buf, pos, ts->u32.hi); PUTVAL32H(buf, pos+4, ts->u32.lo); } static void GETATTRHDR(const pj_uint8_t *buf, pj_stun_attr_hdr *hdr) { hdr->type = GETVAL16H(buf, 0); hdr->length = GETVAL16H(buf, 2); } ////////////////////////////////////////////////////////////////////////////// /* * STUN generic IP address container */ #define STUN_GENERIC_IPV4_ADDR_LEN 8 #define STUN_GENERIC_IPV6_ADDR_LEN 20 /* * Init sockaddr attr */ PJ_DEF(pj_status_t) pj_stun_sockaddr_attr_init( pj_stun_sockaddr_attr *attr, int attr_type, pj_bool_t xor_ed, const pj_sockaddr_t *addr, unsigned addr_len) { unsigned attr_len; PJ_ASSERT_RETURN(attr && addr_len && addr, PJ_EINVAL); PJ_ASSERT_RETURN(addr_len == sizeof(pj_sockaddr_in) || addr_len == sizeof(pj_sockaddr_in6), PJ_EINVAL); attr_len = pj_sockaddr_get_addr_len(addr) + 4; INIT_ATTR(attr, attr_type, attr_len); pj_memcpy(&attr->sockaddr, addr, addr_len); attr->xor_ed = xor_ed; return PJ_SUCCESS; } /* * Create a generic STUN IP address attribute for IPv4 address. */ PJ_DEF(pj_status_t) pj_stun_sockaddr_attr_create(pj_pool_t *pool, int attr_type, pj_bool_t xor_ed, const pj_sockaddr_t *addr, unsigned addr_len, pj_stun_sockaddr_attr **p_attr) { pj_stun_sockaddr_attr *attr; PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL); attr = PJ_POOL_ZALLOC_T(pool, pj_stun_sockaddr_attr); *p_attr = attr; return pj_stun_sockaddr_attr_init(attr, attr_type, xor_ed, addr, addr_len); } /* * Create and add generic STUN IP address attribute to a STUN message. */ PJ_DEF(pj_status_t) pj_stun_msg_add_sockaddr_attr(pj_pool_t *pool, pj_stun_msg *msg, int attr_type, pj_bool_t xor_ed, const pj_sockaddr_t *addr, unsigned addr_len) { pj_stun_sockaddr_attr *attr; pj_status_t status; status = pj_stun_sockaddr_attr_create(pool, attr_type, xor_ed, addr, addr_len, &attr); if (status != PJ_SUCCESS) return status; return pj_stun_msg_add_attr(msg, &attr->hdr); } static pj_status_t decode_sockaddr_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr) { pj_stun_sockaddr_attr *attr; int af; unsigned addr_len; pj_uint32_t val; PJ_CHECK_STACK(); PJ_UNUSED_ARG(msghdr); /* Create the attribute */ attr = PJ_POOL_ZALLOC_T(pool, pj_stun_sockaddr_attr); GETATTRHDR(buf, &attr->hdr); /* Check that the attribute length is valid */ if (attr->hdr.length != STUN_GENERIC_IPV4_ADDR_LEN && attr->hdr.length != STUN_GENERIC_IPV6_ADDR_LEN) { return PJNATH_ESTUNINATTRLEN; } /* Check address family */ val = *(pj_uint8_t*)(buf + ATTR_HDR_LEN + 1); /* Check address family is valid */ if (val == 1) { if (attr->hdr.length != STUN_GENERIC_IPV4_ADDR_LEN) return PJNATH_ESTUNINATTRLEN; af = pj_AF_INET(); addr_len = 4; } else if (val == 2) { if (attr->hdr.length != STUN_GENERIC_IPV6_ADDR_LEN) return PJNATH_ESTUNINATTRLEN; af = pj_AF_INET6(); addr_len = 16; } else { /* Invalid address family */ return PJNATH_EINVAF; } /* Get port and address */ pj_sockaddr_init(af, &attr->sockaddr, NULL, 0); pj_sockaddr_set_port(&attr->sockaddr, GETVAL16H(buf, ATTR_HDR_LEN+2)); pj_memcpy(pj_sockaddr_get_addr(&attr->sockaddr), buf+ATTR_HDR_LEN+4, addr_len); /* Done */ *p_attr = (void*)attr; return PJ_SUCCESS; } static pj_status_t decode_xored_sockaddr_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr) { pj_stun_sockaddr_attr *attr; pj_status_t status; status = decode_sockaddr_attr(pool, buf, msghdr, p_attr); if (status != PJ_SUCCESS) return status; attr = *(pj_stun_sockaddr_attr**)p_attr; attr->xor_ed = PJ_TRUE; if (attr->sockaddr.addr.sa_family == pj_AF_INET()) { attr->sockaddr.ipv4.sin_port ^= pj_htons(PJ_STUN_MAGIC >> 16); attr->sockaddr.ipv4.sin_addr.s_addr ^= pj_htonl(PJ_STUN_MAGIC); } else if (attr->sockaddr.addr.sa_family == pj_AF_INET6()) { unsigned i; pj_uint8_t *dst = (pj_uint8_t*) &attr->sockaddr.ipv6.sin6_addr; pj_uint32_t magic = pj_htonl(PJ_STUN_MAGIC); attr->sockaddr.ipv6.sin6_port ^= pj_htons(PJ_STUN_MAGIC >> 16); /* If the IP address family is IPv6, X-Address is computed by * taking the mapped IP address in host byte order, XOR'ing it * with the concatenation of the magic cookie and the 96-bit * transaction ID, and converting the result to network byte * order. */ for (i=0; i<4; ++i) { dst[i] ^= ((const pj_uint8_t*)&magic)[i]; } pj_assert(sizeof(msghdr->tsx_id[0]) == 1); for (i=0; i<12; ++i) { dst[i+4] ^= msghdr->tsx_id[i]; } } else { return PJNATH_EINVAF; } /* Done */ *p_attr = attr; return PJ_SUCCESS; } static pj_status_t encode_sockaddr_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed) { pj_uint8_t *start_buf = buf; const pj_stun_sockaddr_attr *ca = (const pj_stun_sockaddr_attr *)a; PJ_CHECK_STACK(); /* Common: attribute type */ PUTVAL16H(buf, 0, ca->hdr.type); if (ca->sockaddr.addr.sa_family == pj_AF_INET()) { enum { ATTR_LEN = ATTR_HDR_LEN + STUN_GENERIC_IPV4_ADDR_LEN }; if (len < ATTR_LEN) return PJ_ETOOSMALL; /* attribute len */ PUTVAL16H(buf, 2, STUN_GENERIC_IPV4_ADDR_LEN); buf += ATTR_HDR_LEN; /* Ignored */ *buf++ = '\0'; /* Address family, 1 for IPv4 */ *buf++ = 1; /* IPv4 address */ if (ca->xor_ed) { pj_uint32_t addr; pj_uint16_t port; addr = ca->sockaddr.ipv4.sin_addr.s_addr; port = ca->sockaddr.ipv4.sin_port; port ^= pj_htons(PJ_STUN_MAGIC >> 16); addr ^= pj_htonl(PJ_STUN_MAGIC); /* Port */ pj_memcpy(buf, &port, 2); buf += 2; /* Address */ pj_memcpy(buf, &addr, 4); buf += 4; } else { /* Port */ pj_memcpy(buf, &ca->sockaddr.ipv4.sin_port, 2); buf += 2; /* Address */ pj_memcpy(buf, &ca->sockaddr.ipv4.sin_addr, 4); buf += 4; } pj_assert(buf - start_buf == ATTR_LEN); } else if (ca->sockaddr.addr.sa_family == pj_AF_INET6()) { /* IPv6 address */ enum { ATTR_LEN = ATTR_HDR_LEN + STUN_GENERIC_IPV6_ADDR_LEN }; if (len < ATTR_LEN) return PJ_ETOOSMALL; /* attribute len */ PUTVAL16H(buf, 2, STUN_GENERIC_IPV6_ADDR_LEN); buf += ATTR_HDR_LEN; /* Ignored */ *buf++ = '\0'; /* Address family, 2 for IPv6 */ *buf++ = 2; /* IPv6 address */ if (ca->xor_ed) { unsigned i; pj_uint8_t *dst; const pj_uint8_t *src; pj_uint32_t magic = pj_htonl(PJ_STUN_MAGIC); pj_uint16_t port = ca->sockaddr.ipv6.sin6_port; /* Port */ port ^= pj_htons(PJ_STUN_MAGIC >> 16); pj_memcpy(buf, &port, 2); buf += 2; /* Address */ dst = buf; src = (const pj_uint8_t*) &ca->sockaddr.ipv6.sin6_addr; for (i=0; i<4; ++i) { dst[i] = (pj_uint8_t)(src[i] ^ ((const pj_uint8_t*)&magic)[i]); } pj_assert(sizeof(msghdr->tsx_id[0]) == 1); for (i=0; i<12; ++i) { dst[i+4] = (pj_uint8_t)(src[i+4] ^ msghdr->tsx_id[i]); } buf += 16; } else { /* Port */ pj_memcpy(buf, &ca->sockaddr.ipv6.sin6_port, 2); buf += 2; /* Address */ pj_memcpy(buf, &ca->sockaddr.ipv6.sin6_addr, 16); buf += 16; } pj_assert(buf - start_buf == ATTR_LEN); } else { return PJNATH_EINVAF; } /* Done */ *printed = (unsigned)(buf - start_buf); return PJ_SUCCESS; } static void* clone_sockaddr_attr(pj_pool_t *pool, const void *src) { pj_stun_sockaddr_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_sockaddr_attr); pj_memcpy(dst, src, sizeof(pj_stun_sockaddr_attr)); return (void*)dst; } ////////////////////////////////////////////////////////////////////////////// /* * STUN generic string attribute */ /* * Initialize a STUN generic string attribute. */ PJ_DEF(pj_status_t) pj_stun_string_attr_init( pj_stun_string_attr *attr, pj_pool_t *pool, int attr_type, const pj_str_t *value) { if (value && value->slen) { INIT_ATTR(attr, attr_type, value->slen); attr->value.slen = value->slen; pj_strdup(pool, &attr->value, value); } else { INIT_ATTR(attr, attr_type, 0); } return PJ_SUCCESS; } /* * Create a STUN generic string attribute. */ PJ_DEF(pj_status_t) pj_stun_string_attr_create(pj_pool_t *pool, int attr_type, const pj_str_t *value, pj_stun_string_attr **p_attr) { pj_stun_string_attr *attr; PJ_ASSERT_RETURN(pool && value && p_attr, PJ_EINVAL); attr = PJ_POOL_ZALLOC_T(pool, pj_stun_string_attr); *p_attr = attr; return pj_stun_string_attr_init(attr, pool, attr_type, value); } /* * Create and add STUN generic string attribute to the message. */ PJ_DEF(pj_status_t) pj_stun_msg_add_string_attr(pj_pool_t *pool, pj_stun_msg *msg, int attr_type, const pj_str_t *value) { pj_stun_string_attr *attr = NULL; pj_status_t status; status = pj_stun_string_attr_create(pool, attr_type, value, &attr); if (status != PJ_SUCCESS) return status; return pj_stun_msg_add_attr(msg, &attr->hdr); } static pj_status_t decode_string_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr) { pj_stun_string_attr *attr; pj_str_t value; PJ_UNUSED_ARG(msghdr); /* Create the attribute */ attr = PJ_POOL_ZALLOC_T(pool, pj_stun_string_attr); GETATTRHDR(buf, &attr->hdr); /* Get pointer to the string in the message */ value.ptr = ((char*)buf + ATTR_HDR_LEN); value.slen = attr->hdr.length; /* Copy the string to the attribute */ pj_strdup(pool, &attr->value, &value); /* Done */ *p_attr = attr; return PJ_SUCCESS; } static pj_status_t encode_string_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed) { const pj_stun_string_attr *ca = (const pj_stun_string_attr*)a; PJ_CHECK_STACK(); PJ_UNUSED_ARG(msghdr); /* Calculated total attr_len (add padding if necessary) */ *printed = ((unsigned)ca->value.slen + ATTR_HDR_LEN + 3) & (~3); if (len < *printed) { *printed = 0; return PJ_ETOOSMALL; } PUTVAL16H(buf, 0, ca->hdr.type); /* Special treatment for SOFTWARE attribute: * This attribute had caused interop problem when talking to * legacy RFC 3489 STUN servers, due to different "length" * rules with RFC 5389. */ if (msghdr->magic != PJ_STUN_MAGIC || ca->hdr.type == PJ_STUN_ATTR_SOFTWARE) { /* Set the length to be 4-bytes aligned so that we can * communicate with RFC 3489 endpoints */ PUTVAL16H(buf, 2, (pj_uint16_t)((ca->value.slen + 3) & (~3))); } else { /* Use RFC 5389 rule */ PUTVAL16H(buf, 2, (pj_uint16_t)ca->value.slen); } /* Copy the string */ pj_memcpy(buf+ATTR_HDR_LEN, ca->value.ptr, ca->value.slen); /* Add padding character, if string is not 4-bytes aligned. */ if (ca->value.slen & 0x03) { pj_uint8_t pad[3]; pj_memset(pad, padding_char, sizeof(pad)); pj_memcpy(buf+ATTR_HDR_LEN+ca->value.slen, pad, 4-(ca->value.slen & 0x03)); } /* Done */ return PJ_SUCCESS; } static void* clone_string_attr(pj_pool_t *pool, const void *src) { const pj_stun_string_attr *asrc = (const pj_stun_string_attr*)src; pj_stun_string_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_string_attr); pj_memcpy(dst, src, sizeof(pj_stun_attr_hdr)); pj_strdup(pool, &dst->value, &asrc->value); return (void*)dst; } ////////////////////////////////////////////////////////////////////////////// /* * STUN empty attribute (used by USE-CANDIDATE). */ /* * Create a STUN empty attribute. */ PJ_DEF(pj_status_t) pj_stun_empty_attr_create(pj_pool_t *pool, int attr_type, pj_stun_empty_attr **p_attr) { pj_stun_empty_attr *attr; PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL); attr = PJ_POOL_ZALLOC_T(pool, pj_stun_empty_attr); INIT_ATTR(attr, attr_type, 0); *p_attr = attr; return PJ_SUCCESS; } /* * Create STUN empty attribute and add the attribute to the message. */ PJ_DEF(pj_status_t) pj_stun_msg_add_empty_attr( pj_pool_t *pool, pj_stun_msg *msg, int attr_type) { pj_stun_empty_attr *attr = NULL; pj_status_t status; status = pj_stun_empty_attr_create(pool, attr_type, &attr); if (status != PJ_SUCCESS) return status; return pj_stun_msg_add_attr(msg, &attr->hdr); } static pj_status_t decode_empty_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr) { pj_stun_empty_attr *attr; PJ_UNUSED_ARG(msghdr); /* Check that the struct address is valid */ pj_assert(sizeof(pj_stun_empty_attr) == ATTR_HDR_LEN); /* Create the attribute */ attr = PJ_POOL_ZALLOC_T(pool, pj_stun_empty_attr); GETATTRHDR(buf, &attr->hdr); /* Check that the attribute length is valid */ if (attr->hdr.length != 0) return PJNATH_ESTUNINATTRLEN; /* Done */ *p_attr = attr; return PJ_SUCCESS; } static pj_status_t encode_empty_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed) { const pj_stun_empty_attr *ca = (pj_stun_empty_attr*)a; PJ_UNUSED_ARG(msghdr); if (len < ATTR_HDR_LEN) return PJ_ETOOSMALL; PUTVAL16H(buf, 0, ca->hdr.type); PUTVAL16H(buf, 2, 0); /* Done */ *printed = ATTR_HDR_LEN; return PJ_SUCCESS; } static void* clone_empty_attr(pj_pool_t *pool, const void *src) { pj_stun_empty_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_empty_attr); pj_memcpy(dst, src, sizeof(pj_stun_empty_attr)); return (void*) dst; } ////////////////////////////////////////////////////////////////////////////// /* * STUN generic 32bit integer attribute. */ /* * Create a STUN generic 32bit value attribute. */ PJ_DEF(pj_status_t) pj_stun_uint_attr_create(pj_pool_t *pool, int attr_type, pj_uint32_t value, pj_stun_uint_attr **p_attr) { pj_stun_uint_attr *attr; PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL); attr = PJ_POOL_ZALLOC_T(pool, pj_stun_uint_attr); INIT_ATTR(attr, attr_type, 4); attr->value = value; *p_attr = attr; return PJ_SUCCESS; } /* Create and add STUN generic 32bit value attribute to the message. */ PJ_DEF(pj_status_t) pj_stun_msg_add_uint_attr(pj_pool_t *pool, pj_stun_msg *msg, int attr_type, pj_uint32_t value) { pj_stun_uint_attr *attr = NULL; pj_status_t status; status = pj_stun_uint_attr_create(pool, attr_type, value, &attr); if (status != PJ_SUCCESS) return status; return pj_stun_msg_add_attr(msg, &attr->hdr); } static pj_status_t decode_uint_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr) { pj_stun_uint_attr *attr; PJ_UNUSED_ARG(msghdr); /* Create the attribute */ attr = PJ_POOL_ZALLOC_T(pool, pj_stun_uint_attr); GETATTRHDR(buf, &attr->hdr); attr->value = GETVAL32H(buf, 4); /* Check that the attribute length is valid */ if (attr->hdr.length != 4) return PJNATH_ESTUNINATTRLEN; /* Done */ *p_attr = attr; return PJ_SUCCESS; } static pj_status_t encode_uint_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed) { const pj_stun_uint_attr *ca = (const pj_stun_uint_attr*)a; PJ_CHECK_STACK(); PJ_UNUSED_ARG(msghdr); if (len < 8) return PJ_ETOOSMALL; PUTVAL16H(buf, 0, ca->hdr.type); PUTVAL16H(buf, 2, (pj_uint16_t)4); PUTVAL32H(buf, 4, ca->value); /* Done */ *printed = 8; return PJ_SUCCESS; } static void* clone_uint_attr(pj_pool_t *pool, const void *src) { pj_stun_uint_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_uint_attr); pj_memcpy(dst, src, sizeof(pj_stun_uint_attr)); return (void*)dst; } ////////////////////////////////////////////////////////////////////////////// /* * Create a STUN generic 64bit value attribute. */ PJ_DEF(pj_status_t) pj_stun_uint64_attr_create(pj_pool_t *pool, int attr_type, const pj_timestamp *value, pj_stun_uint64_attr **p_attr) { pj_stun_uint64_attr *attr; PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL); attr = PJ_POOL_ZALLOC_T(pool, pj_stun_uint64_attr); INIT_ATTR(attr, attr_type, 8); if (value) { attr->value.u32.hi = value->u32.hi; attr->value.u32.lo = value->u32.lo; } *p_attr = attr; return PJ_SUCCESS; } /* Create and add STUN generic 64bit value attribute to the message. */ PJ_DEF(pj_status_t) pj_stun_msg_add_uint64_attr(pj_pool_t *pool, pj_stun_msg *msg, int attr_type, const pj_timestamp *value) { pj_stun_uint64_attr *attr = NULL; pj_status_t status; status = pj_stun_uint64_attr_create(pool, attr_type, value, &attr); if (status != PJ_SUCCESS) return status; return pj_stun_msg_add_attr(msg, &attr->hdr); } static pj_status_t decode_uint64_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr) { pj_stun_uint64_attr *attr; PJ_UNUSED_ARG(msghdr); /* Create the attribute */ attr = PJ_POOL_ZALLOC_T(pool, pj_stun_uint64_attr); GETATTRHDR(buf, &attr->hdr); if (attr->hdr.length != 8) return PJNATH_ESTUNINATTRLEN; GETVAL64H(buf, 4, &attr->value); /* Done */ *p_attr = attr; return PJ_SUCCESS; } static pj_status_t encode_uint64_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed) { const pj_stun_uint64_attr *ca = (const pj_stun_uint64_attr*)a; PJ_CHECK_STACK(); PJ_UNUSED_ARG(msghdr); if (len < 12) return PJ_ETOOSMALL; PUTVAL16H(buf, 0, ca->hdr.type); PUTVAL16H(buf, 2, (pj_uint16_t)8); PUTVAL64H(buf, 4, &ca->value); /* Done */ *printed = 12; return PJ_SUCCESS; } static void* clone_uint64_attr(pj_pool_t *pool, const void *src) { pj_stun_uint64_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_uint64_attr); pj_memcpy(dst, src, sizeof(pj_stun_uint64_attr)); return (void*)dst; } ////////////////////////////////////////////////////////////////////////////// /* * STUN MESSAGE-INTEGRITY attribute. */ /* * Create a STUN MESSAGE-INTEGRITY attribute. */ PJ_DEF(pj_status_t) pj_stun_msgint_attr_create(pj_pool_t *pool, pj_stun_msgint_attr **p_attr) { pj_stun_msgint_attr *attr; PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL); attr = PJ_POOL_ZALLOC_T(pool, pj_stun_msgint_attr); INIT_ATTR(attr, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 20); *p_attr = attr; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_stun_msg_add_msgint_attr(pj_pool_t *pool, pj_stun_msg *msg) { pj_stun_msgint_attr *attr = NULL; pj_status_t status; status = pj_stun_msgint_attr_create(pool, &attr); if (status != PJ_SUCCESS) return status; return pj_stun_msg_add_attr(msg, &attr->hdr); } static pj_status_t decode_msgint_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr) { pj_stun_msgint_attr *attr; PJ_UNUSED_ARG(msghdr); /* Create attribute */ attr = PJ_POOL_ZALLOC_T(pool, pj_stun_msgint_attr); GETATTRHDR(buf, &attr->hdr); /* Check that the attribute length is valid */ if (attr->hdr.length != 20) return PJNATH_ESTUNINATTRLEN; /* Copy hmac */ pj_memcpy(attr->hmac, buf+4, 20); /* Done */ *p_attr = attr; return PJ_SUCCESS; } static pj_status_t encode_msgint_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed) { const pj_stun_msgint_attr *ca = (const pj_stun_msgint_attr*)a; PJ_CHECK_STACK(); PJ_UNUSED_ARG(msghdr); if (len < 24) return PJ_ETOOSMALL; /* Copy and convert attribute to network byte order */ PUTVAL16H(buf, 0, ca->hdr.type); PUTVAL16H(buf, 2, ca->hdr.length); pj_memcpy(buf+4, ca->hmac, 20); /* Done */ *printed = 24; return PJ_SUCCESS; } static void* clone_msgint_attr(pj_pool_t *pool, const void *src) { pj_stun_msgint_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_msgint_attr); pj_memcpy(dst, src, sizeof(pj_stun_msgint_attr)); return (void*) dst; } ////////////////////////////////////////////////////////////////////////////// /* * STUN ERROR-CODE */ /* * Create a STUN ERROR-CODE attribute. */ PJ_DEF(pj_status_t) pj_stun_errcode_attr_create(pj_pool_t *pool, int err_code, const pj_str_t *err_reason, pj_stun_errcode_attr **p_attr) { pj_stun_errcode_attr *attr; char err_buf[80]; pj_str_t str; PJ_ASSERT_RETURN(pool && err_code && p_attr, PJ_EINVAL); if (err_reason == NULL) { str = pj_stun_get_err_reason(err_code); if (str.slen == 0) { str.slen = pj_ansi_snprintf(err_buf, sizeof(err_buf), "Unknown error %d", err_code); str.ptr = err_buf; } err_reason = &str; } attr = PJ_POOL_ZALLOC_T(pool, pj_stun_errcode_attr); INIT_ATTR(attr, PJ_STUN_ATTR_ERROR_CODE, 4+err_reason->slen); attr->err_code = err_code; pj_strdup(pool, &attr->reason, err_reason); *p_attr = attr; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_stun_msg_add_errcode_attr(pj_pool_t *pool, pj_stun_msg *msg, int err_code, const pj_str_t *err_reason) { pj_stun_errcode_attr *err_attr = NULL; pj_status_t status; status = pj_stun_errcode_attr_create(pool, err_code, err_reason, &err_attr); if (status != PJ_SUCCESS) return status; return pj_stun_msg_add_attr(msg, &err_attr->hdr); } static pj_status_t decode_errcode_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr) { pj_stun_errcode_attr *attr; pj_str_t value; PJ_UNUSED_ARG(msghdr); /* Create the attribute */ attr = PJ_POOL_ZALLOC_T(pool, pj_stun_errcode_attr); GETATTRHDR(buf, &attr->hdr); attr->err_code = buf[6] * 100 + buf[7]; /* Get pointer to the string in the message */ value.ptr = ((char*)buf + ATTR_HDR_LEN + 4); value.slen = attr->hdr.length - 4; /* Copy the string to the attribute */ pj_strdup(pool, &attr->reason, &value); /* Done */ *p_attr = attr; return PJ_SUCCESS; } static pj_status_t encode_errcode_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed) { const pj_stun_errcode_attr *ca = (const pj_stun_errcode_attr*)a; PJ_CHECK_STACK(); PJ_UNUSED_ARG(msghdr); if (len < ATTR_HDR_LEN + 4 + (unsigned)ca->reason.slen) return PJ_ETOOSMALL; /* Copy and convert attribute to network byte order */ PUTVAL16H(buf, 0, ca->hdr.type); PUTVAL16H(buf, 2, (pj_uint16_t)(4 + ca->reason.slen)); PUTVAL16H(buf, 4, 0); buf[6] = (pj_uint8_t)(ca->err_code / 100); buf[7] = (pj_uint8_t)(ca->err_code % 100); /* Copy error string */ pj_memcpy(buf + ATTR_HDR_LEN + 4, ca->reason.ptr, ca->reason.slen); /* Done */ *printed = (ATTR_HDR_LEN + 4 + (unsigned)ca->reason.slen + 3) & (~3); return PJ_SUCCESS; } static void* clone_errcode_attr(pj_pool_t *pool, const void *src) { const pj_stun_errcode_attr *asrc = (const pj_stun_errcode_attr*)src; pj_stun_errcode_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_errcode_attr); pj_memcpy(dst, src, sizeof(pj_stun_errcode_attr)); pj_strdup(pool, &dst->reason, &asrc->reason); return (void*)dst; } ////////////////////////////////////////////////////////////////////////////// /* * STUN UNKNOWN-ATTRIBUTES attribute */ /* * Create an empty instance of STUN UNKNOWN-ATTRIBUTES attribute. * * @param pool The pool to allocate memory from. * @param p_attr Pointer to receive the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DEF(pj_status_t) pj_stun_unknown_attr_create(pj_pool_t *pool, unsigned attr_cnt, const pj_uint16_t attr_array[], pj_stun_unknown_attr **p_attr) { pj_stun_unknown_attr *attr; unsigned i; PJ_ASSERT_RETURN(pool && attr_cnt < PJ_STUN_MAX_ATTR && p_attr, PJ_EINVAL); attr = PJ_POOL_ZALLOC_T(pool, pj_stun_unknown_attr); INIT_ATTR(attr, PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES, attr_cnt * 2); attr->attr_count = attr_cnt; for (i=0; iattrs[i] = attr_array[i]; } /* If the number of unknown attributes is an odd number, one of the * attributes MUST be repeated in the list. */ /* No longer necessary if ((attr_cnt & 0x01)) { attr->attrs[attr_cnt] = attr_array[attr_cnt-1]; } */ *p_attr = attr; return PJ_SUCCESS; } /* Create and add STUN UNKNOWN-ATTRIBUTES attribute to the message. */ PJ_DEF(pj_status_t) pj_stun_msg_add_unknown_attr(pj_pool_t *pool, pj_stun_msg *msg, unsigned attr_cnt, const pj_uint16_t attr_type[]) { pj_stun_unknown_attr *attr = NULL; pj_status_t status; status = pj_stun_unknown_attr_create(pool, attr_cnt, attr_type, &attr); if (status != PJ_SUCCESS) return status; return pj_stun_msg_add_attr(msg, &attr->hdr); } static pj_status_t decode_unknown_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr) { pj_stun_unknown_attr *attr; const pj_uint16_t *punk_attr; unsigned i; PJ_UNUSED_ARG(msghdr); attr = PJ_POOL_ZALLOC_T(pool, pj_stun_unknown_attr); GETATTRHDR(buf, &attr->hdr); attr->attr_count = (attr->hdr.length >> 1); if (attr->attr_count > PJ_STUN_MAX_ATTR) return PJ_ETOOMANY; punk_attr = (const pj_uint16_t*)(buf + ATTR_HDR_LEN); for (i=0; iattr_count; ++i) { attr->attrs[i] = pj_ntohs(punk_attr[i]); } /* Done */ *p_attr = attr; return PJ_SUCCESS; } static pj_status_t encode_unknown_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed) { const pj_stun_unknown_attr *ca = (const pj_stun_unknown_attr*) a; pj_uint16_t *dst_unk_attr; unsigned i; PJ_CHECK_STACK(); PJ_UNUSED_ARG(msghdr); /* Check that buffer is enough */ if (len < ATTR_HDR_LEN + (ca->attr_count << 1)) return PJ_ETOOSMALL; PUTVAL16H(buf, 0, ca->hdr.type); PUTVAL16H(buf, 2, (pj_uint16_t)(ca->attr_count << 1)); /* Copy individual attribute */ dst_unk_attr = (pj_uint16_t*)(buf + ATTR_HDR_LEN); for (i=0; i < ca->attr_count; ++i, ++dst_unk_attr) { *dst_unk_attr = pj_htons(ca->attrs[i]); } /* Done */ *printed = (ATTR_HDR_LEN + (ca->attr_count << 1) + 3) & (~3); return PJ_SUCCESS; } static void* clone_unknown_attr(pj_pool_t *pool, const void *src) { pj_stun_unknown_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_unknown_attr); pj_memcpy(dst, src, sizeof(pj_stun_unknown_attr)); return (void*)dst; } ////////////////////////////////////////////////////////////////////////////// /* * STUN generic binary attribute */ /* * Initialize STUN binary attribute. */ PJ_DEF(pj_status_t) pj_stun_binary_attr_init( pj_stun_binary_attr *attr, pj_pool_t *pool, int attr_type, const pj_uint8_t *data, unsigned length) { PJ_ASSERT_RETURN(attr_type, PJ_EINVAL); INIT_ATTR(attr, attr_type, length); attr->magic = PJ_STUN_MAGIC; if (data && length) { attr->length = length; attr->data = (pj_uint8_t*) pj_pool_alloc(pool, length); pj_memcpy(attr->data, data, length); } else { attr->data = NULL; attr->length = 0; } return PJ_SUCCESS; } /* * Create a blank binary attribute. */ PJ_DEF(pj_status_t) pj_stun_binary_attr_create(pj_pool_t *pool, int attr_type, const pj_uint8_t *data, unsigned length, pj_stun_binary_attr **p_attr) { pj_stun_binary_attr *attr; PJ_ASSERT_RETURN(pool && attr_type && p_attr, PJ_EINVAL); attr = PJ_POOL_ZALLOC_T(pool, pj_stun_binary_attr); *p_attr = attr; return pj_stun_binary_attr_init(attr, pool, attr_type, data, length); } /* Create and add binary attr. */ PJ_DEF(pj_status_t) pj_stun_msg_add_binary_attr(pj_pool_t *pool, pj_stun_msg *msg, int attr_type, const pj_uint8_t *data, unsigned length) { pj_stun_binary_attr *attr = NULL; pj_status_t status; status = pj_stun_binary_attr_create(pool, attr_type, data, length, &attr); if (status != PJ_SUCCESS) return status; return pj_stun_msg_add_attr(msg, &attr->hdr); } static pj_status_t decode_binary_attr(pj_pool_t *pool, const pj_uint8_t *buf, const pj_stun_msg_hdr *msghdr, void **p_attr) { pj_stun_binary_attr *attr; PJ_UNUSED_ARG(msghdr); /* Create the attribute */ attr = PJ_POOL_ZALLOC_T(pool, pj_stun_binary_attr); GETATTRHDR(buf, &attr->hdr); /* Copy the data to the attribute */ attr->length = attr->hdr.length; attr->data = (pj_uint8_t*) pj_pool_alloc(pool, attr->length); pj_memcpy(attr->data, buf+ATTR_HDR_LEN, attr->length); /* Done */ *p_attr = attr; return PJ_SUCCESS; } static pj_status_t encode_binary_attr(const void *a, pj_uint8_t *buf, unsigned len, const pj_stun_msg_hdr *msghdr, unsigned *printed) { const pj_stun_binary_attr *ca = (const pj_stun_binary_attr*)a; PJ_CHECK_STACK(); PJ_UNUSED_ARG(msghdr); /* Calculated total attr_len (add padding if necessary) */ *printed = (ca->length + ATTR_HDR_LEN + 3) & (~3); if (len < *printed) return PJ_ETOOSMALL; PUTVAL16H(buf, 0, ca->hdr.type); PUTVAL16H(buf, 2, (pj_uint16_t) ca->length); /* Copy the data */ pj_memcpy(buf+ATTR_HDR_LEN, ca->data, ca->length); /* Done */ return PJ_SUCCESS; } static void* clone_binary_attr(pj_pool_t *pool, const void *src) { const pj_stun_binary_attr *asrc = (const pj_stun_binary_attr*)src; pj_stun_binary_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_binary_attr); pj_memcpy(dst, src, sizeof(pj_stun_binary_attr)); if (asrc->length) { dst->data = (pj_uint8_t*) pj_pool_alloc(pool, asrc->length); pj_memcpy(dst->data, asrc->data, asrc->length); } return (void*)dst; } ////////////////////////////////////////////////////////////////////////////// /* * Initialize a generic STUN message. */ PJ_DEF(pj_status_t) pj_stun_msg_init( pj_stun_msg *msg, unsigned msg_type, pj_uint32_t magic, const pj_uint8_t tsx_id[12]) { PJ_ASSERT_RETURN(msg && msg_type, PJ_EINVAL); msg->hdr.type = (pj_uint16_t) msg_type; msg->hdr.length = 0; msg->hdr.magic = magic; msg->attr_count = 0; if (tsx_id) { pj_memcpy(&msg->hdr.tsx_id, tsx_id, sizeof(msg->hdr.tsx_id)); } else { struct transaction_id { pj_uint32_t proc_id; pj_uint32_t random; pj_uint32_t counter; } id; static pj_uint32_t pj_stun_tsx_id_counter; if (!pj_stun_tsx_id_counter) pj_stun_tsx_id_counter = pj_rand(); id.proc_id = pj_getpid(); id.random = pj_rand(); id.counter = pj_stun_tsx_id_counter++; pj_memcpy(&msg->hdr.tsx_id, &id, sizeof(msg->hdr.tsx_id)); } return PJ_SUCCESS; } /* * Create a blank STUN message. */ PJ_DEF(pj_status_t) pj_stun_msg_create( pj_pool_t *pool, unsigned msg_type, pj_uint32_t magic, const pj_uint8_t tsx_id[12], pj_stun_msg **p_msg) { pj_stun_msg *msg; PJ_ASSERT_RETURN(pool && msg_type && p_msg, PJ_EINVAL); msg = PJ_POOL_ZALLOC_T(pool, pj_stun_msg); *p_msg = msg; return pj_stun_msg_init(msg, msg_type, magic, tsx_id); } /* * Clone a STUN message with all of its attributes. */ PJ_DEF(pj_stun_msg*) pj_stun_msg_clone( pj_pool_t *pool, const pj_stun_msg *src) { pj_stun_msg *dst; unsigned i; PJ_ASSERT_RETURN(pool && src, NULL); dst = PJ_POOL_ZALLOC_T(pool, pj_stun_msg); pj_memcpy(dst, src, sizeof(pj_stun_msg)); /* Duplicate the attributes */ for (i=0, dst->attr_count=0; iattr_count; ++i) { dst->attr[dst->attr_count] = pj_stun_attr_clone(pool, src->attr[i]); if (dst->attr[dst->attr_count]) ++dst->attr_count; } return dst; } /* * Add STUN attribute to STUN message. */ PJ_DEF(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg, pj_stun_attr_hdr *attr) { PJ_ASSERT_RETURN(msg && attr, PJ_EINVAL); PJ_ASSERT_RETURN(msg->attr_count < PJ_STUN_MAX_ATTR, PJ_ETOOMANY); msg->attr[msg->attr_count++] = attr; return PJ_SUCCESS; } /* * Check that the PDU is potentially a valid STUN message. */ PJ_DEF(pj_status_t) pj_stun_msg_check(const pj_uint8_t *pdu, pj_size_t pdu_len, unsigned options) { pj_uint32_t msg_len; PJ_ASSERT_RETURN(pdu, PJ_EINVAL); if (pdu_len < sizeof(pj_stun_msg_hdr)) return PJNATH_EINSTUNMSGLEN; /* First byte of STUN message is always 0x00 or 0x01. */ if (*pdu != 0x00 && *pdu != 0x01) return PJNATH_EINSTUNMSGTYPE; /* Check the PDU length */ msg_len = GETVAL16H(pdu, 2); if ((msg_len + 20 > pdu_len) || ((options & PJ_STUN_IS_DATAGRAM) && msg_len + 20 != pdu_len)) { return PJNATH_EINSTUNMSGLEN; } /* STUN message is always padded to the nearest 4 bytes, thus * the last two bits of the length field are always zero. */ if ((msg_len & 0x03) != 0) { return PJNATH_EINSTUNMSGLEN; } /* If magic is set, then there is great possibility that this is * a STUN message. */ if (GETVAL32H(pdu, 4) == PJ_STUN_MAGIC) { /* Check if FINGERPRINT attribute is present */ if ((options & PJ_STUN_NO_FINGERPRINT_CHECK )==0 && GETVAL16H(pdu, msg_len + 20 - 8) == PJ_STUN_ATTR_FINGERPRINT) { pj_uint16_t attr_len = GETVAL16H(pdu, msg_len + 20 - 8 + 2); pj_uint32_t fingerprint = GETVAL32H(pdu, msg_len + 20 - 8 + 4); pj_uint32_t crc; if (attr_len != 4) return PJNATH_ESTUNINATTRLEN; crc = pj_crc32_calc(pdu, msg_len + 20 - 8); crc ^= STUN_XOR_FINGERPRINT; if (crc != fingerprint) return PJNATH_ESTUNFINGERPRINT; } } /* Could be a STUN message */ return PJ_SUCCESS; } /* Create error response */ PJ_DEF(pj_status_t) pj_stun_msg_create_response(pj_pool_t *pool, const pj_stun_msg *req_msg, unsigned err_code, const pj_str_t *err_msg, pj_stun_msg **p_response) { unsigned msg_type = req_msg->hdr.type; pj_stun_msg *response = NULL; pj_status_t status; PJ_ASSERT_RETURN(pool && p_response, PJ_EINVAL); PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(msg_type), PJNATH_EINSTUNMSGTYPE); /* Create response or error response */ if (err_code) msg_type |= PJ_STUN_ERROR_RESPONSE_BIT; else msg_type |= PJ_STUN_SUCCESS_RESPONSE_BIT; status = pj_stun_msg_create(pool, msg_type, req_msg->hdr.magic, req_msg->hdr.tsx_id, &response); if (status != PJ_SUCCESS) { return status; } /* Add error code attribute */ if (err_code) { status = pj_stun_msg_add_errcode_attr(pool, response, err_code, err_msg); if (status != PJ_SUCCESS) { return status; } } *p_response = response; return PJ_SUCCESS; } /* * Parse incoming packet into STUN message. */ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, const pj_uint8_t *pdu, pj_size_t pdu_len, unsigned options, pj_stun_msg **p_msg, pj_size_t *p_parsed_len, pj_stun_msg **p_response) { pj_stun_msg *msg; const pj_uint8_t *start_pdu = pdu; pj_bool_t has_msg_int = PJ_FALSE; pj_bool_t has_fingerprint = PJ_FALSE; pj_status_t status; PJ_UNUSED_ARG(options); PJ_ASSERT_RETURN(pool && pdu && pdu_len && p_msg, PJ_EINVAL); PJ_ASSERT_RETURN(sizeof(pj_stun_msg_hdr) == 20, PJ_EBUG); if (p_parsed_len) *p_parsed_len = 0; if (p_response) *p_response = NULL; /* Check if this is a STUN message, if necessary */ if (options & PJ_STUN_CHECK_PACKET) { status = pj_stun_msg_check(pdu, pdu_len, options); if (status != PJ_SUCCESS) return status; } /* Create the message, copy the header, and convert to host byte order */ msg = PJ_POOL_ZALLOC_T(pool, pj_stun_msg); pj_memcpy(&msg->hdr, pdu, sizeof(pj_stun_msg_hdr)); msg->hdr.type = pj_ntohs(msg->hdr.type); msg->hdr.length = pj_ntohs(msg->hdr.length); msg->hdr.magic = pj_ntohl(msg->hdr.magic); pdu += sizeof(pj_stun_msg_hdr); /* pdu_len -= sizeof(pj_stun_msg_hdr); */ pdu_len = msg->hdr.length; /* No need to create response if this is not a request */ if (!PJ_STUN_IS_REQUEST(msg->hdr.type)) p_response = NULL; /* Parse attributes */ while (pdu_len >= 4) { unsigned attr_type, attr_val_len; const struct attr_desc *adesc; /* Get attribute type and length. If length is not aligned * to 4 bytes boundary, add padding. */ attr_type = GETVAL16H(pdu, 0); attr_val_len = GETVAL16H(pdu, 2); attr_val_len = (attr_val_len + 3) & (~3); /* Check length */ if (pdu_len < attr_val_len) { pj_str_t err_msg; char err_msg_buf[80]; err_msg.ptr = err_msg_buf; err_msg.slen = pj_ansi_snprintf(err_msg_buf, sizeof(err_msg_buf), "Attribute %s has invalid length", pj_stun_get_attr_name(attr_type)); PJ_LOG(4,(THIS_FILE, "Error decoding message: %.*s", (int)err_msg.slen, err_msg.ptr)); if (p_response) { pj_stun_msg_create_response(pool, msg, PJ_STUN_SC_BAD_REQUEST, &err_msg, p_response); } return PJNATH_ESTUNINATTRLEN; } /* Get the attribute descriptor */ adesc = find_attr_desc(attr_type); if (adesc == NULL) { /* Unrecognized attribute */ pj_stun_binary_attr *attr = NULL; PJ_LOG(5,(THIS_FILE, "Unrecognized attribute type 0x%x", attr_type)); /* Is this a fatal condition? */ if (attr_type <= 0x7FFF) { /* This is a mandatory attribute, we must return error * if we don't understand the attribute. */ if (p_response) { unsigned err_code = PJ_STUN_SC_UNKNOWN_ATTRIBUTE; status = pj_stun_msg_create_response(pool, msg, err_code, NULL, p_response); if (status==PJ_SUCCESS) { pj_uint16_t d = (pj_uint16_t)attr_type; pj_stun_msg_add_unknown_attr(pool, *p_response, 1, &d); } } return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNKNOWN_ATTRIBUTE); } /* Make sure we have rooms for the new attribute */ if (msg->attr_count >= PJ_STUN_MAX_ATTR) { if (p_response) { pj_stun_msg_create_response(pool, msg, PJ_STUN_SC_SERVER_ERROR, NULL, p_response); } return PJNATH_ESTUNTOOMANYATTR; } /* Create binary attribute to represent this */ status = pj_stun_binary_attr_create(pool, attr_type, pdu+4, GETVAL16H(pdu, 2), &attr); if (status != PJ_SUCCESS) { if (p_response) { pj_stun_msg_create_response(pool, msg, PJ_STUN_SC_SERVER_ERROR, NULL, p_response); } PJ_LOG(4,(THIS_FILE, "Error parsing unknown STUN attribute type %d", attr_type)); return status; } /* Add the attribute */ msg->attr[msg->attr_count++] = &attr->hdr; } else { void *attr; char err_msg1[PJ_ERR_MSG_SIZE], err_msg2[PJ_ERR_MSG_SIZE]; /* Parse the attribute */ status = (adesc->decode_attr)(pool, pdu, &msg->hdr, &attr); if (status != PJ_SUCCESS) { pj_strerror(status, err_msg1, sizeof(err_msg1)); if (p_response) { pj_str_t e; e.ptr = err_msg2; e.slen= pj_ansi_snprintf(err_msg2, sizeof(err_msg2), "%s in %s", err_msg1, pj_stun_get_attr_name(attr_type)); if (e.slen < 1 || e.slen >= (int)sizeof(err_msg2)) e.slen = sizeof(err_msg2) - 1; pj_stun_msg_create_response(pool, msg, PJ_STUN_SC_BAD_REQUEST, &e, p_response); } PJ_LOG(4,(THIS_FILE, "Error parsing STUN attribute %s: %s", pj_stun_get_attr_name(attr_type), err_msg1)); return status; } if (attr_type == PJ_STUN_ATTR_MESSAGE_INTEGRITY && !has_fingerprint) { if (has_msg_int) { /* Already has MESSAGE-INTEGRITY */ if (p_response) { pj_stun_msg_create_response(pool, msg, PJ_STUN_SC_BAD_REQUEST, NULL, p_response); } return PJNATH_ESTUNDUPATTR; } has_msg_int = PJ_TRUE; } else if (attr_type == PJ_STUN_ATTR_FINGERPRINT) { if (has_fingerprint) { /* Already has FINGERPRINT */ if (p_response) { pj_stun_msg_create_response(pool, msg, PJ_STUN_SC_BAD_REQUEST, NULL, p_response); } return PJNATH_ESTUNDUPATTR; } has_fingerprint = PJ_TRUE; } else { if (has_fingerprint) { /* Another attribute is found which is not FINGERPRINT * after FINGERPRINT. Note that non-FINGERPRINT is * allowed to appear after M-I */ if (p_response) { pj_stun_msg_create_response(pool, msg, PJ_STUN_SC_BAD_REQUEST, NULL, p_response); } return PJNATH_ESTUNFINGERPOS; } } /* Make sure we have rooms for the new attribute */ if (msg->attr_count >= PJ_STUN_MAX_ATTR) { if (p_response) { pj_stun_msg_create_response(pool, msg, PJ_STUN_SC_SERVER_ERROR, NULL, p_response); } return PJNATH_ESTUNTOOMANYATTR; } /* Add the attribute */ msg->attr[msg->attr_count++] = (pj_stun_attr_hdr*)attr; } /* Next attribute */ if (attr_val_len + 4 >= pdu_len) { pdu += pdu_len; pdu_len = 0; } else { pdu += (attr_val_len + 4); pdu_len -= (attr_val_len + 4); } } if (pdu_len > 0) { /* Stray trailing bytes */ PJ_LOG(4,(THIS_FILE, "Error decoding STUN message: unparsed trailing %d bytes", pdu_len)); return PJNATH_EINSTUNMSGLEN; } *p_msg = msg; if (p_parsed_len) *p_parsed_len = (pdu - start_pdu); return PJ_SUCCESS; } /* static char *print_binary(const pj_uint8_t *data, unsigned data_len) { static char static_buffer[1024]; char *buffer = static_buffer; unsigned length=sizeof(static_buffer), i; if (length < data_len * 2 + 8) return ""; pj_ansi_sprintf(buffer, ", data="); buffer += 7; for (i=0; ihdr.type); PUTVAL16H(buf, 2, 0); /* length will be calculated later */ PUTVAL32H(buf, 4, msg->hdr.magic); pj_memcpy(buf+8, msg->hdr.tsx_id, sizeof(msg->hdr.tsx_id)); buf += sizeof(pj_stun_msg_hdr); buf_size -= sizeof(pj_stun_msg_hdr); /* Encode each attribute to the message */ for (i=0; iattr_count; ++i) { const struct attr_desc *adesc; const pj_stun_attr_hdr *attr_hdr = msg->attr[i]; if (attr_hdr->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY) { pj_assert(amsgint == NULL); amsgint = (pj_stun_msgint_attr*) attr_hdr; /* Stop when encountering MESSAGE-INTEGRITY */ break; } else if (attr_hdr->type == PJ_STUN_ATTR_FINGERPRINT) { afingerprint = (pj_stun_fingerprint_attr*) attr_hdr; break; } adesc = find_attr_desc(attr_hdr->type); if (adesc) { status = adesc->encode_attr(attr_hdr, buf, (unsigned)buf_size, &msg->hdr, &printed); } else { /* This may be a generic attribute */ const pj_stun_binary_attr *bin_attr = (const pj_stun_binary_attr*) attr_hdr; PJ_ASSERT_RETURN(bin_attr->magic == PJ_STUN_MAGIC, PJ_EBUG); status = encode_binary_attr(bin_attr, buf, (unsigned)buf_size, &msg->hdr, &printed); } if (status != PJ_SUCCESS) return status; buf += printed; buf_size -= printed; } /* We may have stopped printing attribute because we found * MESSAGE-INTEGRITY or FINGERPRINT. Scan the rest of the * attributes. */ for ( ++i; iattr_count; ++i) { const pj_stun_attr_hdr *attr_hdr = msg->attr[i]; /* There mustn't any attribute after FINGERPRINT */ PJ_ASSERT_RETURN(afingerprint == NULL, PJNATH_ESTUNFINGERPOS); if (attr_hdr->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY) { /* There mustn't be MESSAGE-INTEGRITY before */ PJ_ASSERT_RETURN(amsgint == NULL, PJNATH_ESTUNMSGINTPOS); amsgint = (pj_stun_msgint_attr*) attr_hdr; } else if (attr_hdr->type == PJ_STUN_ATTR_FINGERPRINT) { afingerprint = (pj_stun_fingerprint_attr*) attr_hdr; } } #if PJ_STUN_OLD_STYLE_MI_FINGERPRINT /* * This is the old style MESSAGE-INTEGRITY and FINGERPRINT * calculation, used in rfc3489bis-06 and older. */ /* We MUST update the message length in the header NOW before * calculating MESSAGE-INTEGRITY and FINGERPRINT. * Note that length is not including the 20 bytes header. */ if (amsgint && afingerprint) { body_len = (pj_uint16_t)((buf - start) - 20 + 24 + 8); } else if (amsgint) { body_len = (pj_uint16_t)((buf - start) - 20 + 24); } else if (afingerprint) { body_len = (pj_uint16_t)((buf - start) - 20 + 8); } else { body_len = (pj_uint16_t)((buf - start) - 20); } #else /* If MESSAGE-INTEGRITY is present, include the M-I attribute * in message length before calculating M-I */ if (amsgint) { body_len = (pj_uint16_t)((buf - start) - 20 + 24); } else { body_len = (pj_uint16_t)((buf - start) - 20); } #endif /* PJ_STUN_OLD_STYLE_MI_FINGERPRINT */ /* hdr->length = pj_htons(length); */ PUTVAL16H(start, 2, (pj_uint16_t)body_len); /* Calculate message integrity, if present */ if (amsgint != NULL) { pj_hmac_sha1_context ctx; /* Key MUST be specified */ PJ_ASSERT_RETURN(key, PJ_EINVALIDOP); /* MESSAGE-INTEGRITY must be the last attribute in the message, or * the last attribute before FINGERPRINT. */ if (msg->attr_count>1 && i < msg->attr_count-2) { /* Should not happen for message generated by us */ pj_assert(PJ_FALSE); return PJNATH_ESTUNMSGINTPOS; } else if (i == msg->attr_count-2) { if (msg->attr[i+1]->type != PJ_STUN_ATTR_FINGERPRINT) { /* Should not happen for message generated by us */ pj_assert(PJ_FALSE); return PJNATH_ESTUNMSGINTPOS; } else { afingerprint = (pj_stun_fingerprint_attr*) msg->attr[i+1]; } } /* Calculate HMAC-SHA1 digest, add zero padding to input * if necessary to make the input 64 bytes aligned. */ pj_hmac_sha1_init(&ctx, (const pj_uint8_t*)key->ptr, (unsigned)key->slen); pj_hmac_sha1_update(&ctx, (const pj_uint8_t*)start, (unsigned)(buf-start)); #if PJ_STUN_OLD_STYLE_MI_FINGERPRINT // These are obsoleted in rfc3489bis-08 if ((buf-start) & 0x3F) { pj_uint8_t zeroes[64]; pj_bzero(zeroes, sizeof(zeroes)); pj_hmac_sha1_update(&ctx, zeroes, 64-((buf-start) & 0x3F)); } #endif /* PJ_STUN_OLD_STYLE_MI_FINGERPRINT */ pj_hmac_sha1_final(&ctx, amsgint->hmac); /* Put this attribute in the message */ status = encode_msgint_attr(amsgint, buf, (unsigned)buf_size, &msg->hdr, &printed); if (status != PJ_SUCCESS) return status; buf += printed; buf_size -= printed; } /* Calculate FINGERPRINT if present */ if (afingerprint != NULL) { #if !PJ_STUN_OLD_STYLE_MI_FINGERPRINT /* Update message length */ PUTVAL16H(start, 2, (pj_uint16_t)(GETVAL16H(start, 2)+8)); #endif afingerprint->value = pj_crc32_calc(start, buf-start); afingerprint->value ^= STUN_XOR_FINGERPRINT; /* Put this attribute in the message */ status = encode_uint_attr(afingerprint, buf, (unsigned)buf_size, &msg->hdr, &printed); if (status != PJ_SUCCESS) return status; buf += printed; buf_size -= printed; } /* Update message length. */ msg->hdr.length = (pj_uint16_t) ((buf - start) - 20); /* Return the length */ if (p_msg_len) *p_msg_len = (buf - start); return PJ_SUCCESS; } /* * Find STUN attribute in the STUN message, starting from the specified * index. */ PJ_DEF(pj_stun_attr_hdr*) pj_stun_msg_find_attr( const pj_stun_msg *msg, int attr_type, unsigned index) { PJ_ASSERT_RETURN(msg, NULL); for (; index < msg->attr_count; ++index) { if (msg->attr[index]->type == attr_type) return (pj_stun_attr_hdr*) msg->attr[index]; } return NULL; } /* * Clone a STUN attribute. */ PJ_DEF(pj_stun_attr_hdr*) pj_stun_attr_clone( pj_pool_t *pool, const pj_stun_attr_hdr *attr) { const struct attr_desc *adesc; /* Get the attribute descriptor */ adesc = find_attr_desc(attr->type); if (adesc) { return (pj_stun_attr_hdr*) (*adesc->clone_attr)(pool, attr); } else { /* Clone generic attribute */ const pj_stun_binary_attr *bin_attr = (const pj_stun_binary_attr*) attr; PJ_ASSERT_RETURN(bin_attr->magic == PJ_STUN_MAGIC, NULL); if (bin_attr->magic == PJ_STUN_MAGIC) { return (pj_stun_attr_hdr*) clone_binary_attr(pool, attr); } else { return NULL; } } } ================================================ FILE: deps/pjsip/pjnath/src/pjnath/stun_msg_dump.c ================================================ /* $Id: stun_msg_dump.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #if PJ_LOG_MAX_LEVEL > 0 #define APPLY() if (len < 1 || len >= (end-p)) \ goto on_return; \ p += len static int print_binary(char *buffer, unsigned length, const pj_uint8_t *data, unsigned data_len) { unsigned i; if (length < data_len * 2 + 8) return -1; pj_ansi_sprintf(buffer, ", data="); buffer += 7; for (i=0; itype); char attr_buf[32]; int len; if (*attr_name == '?') { pj_ansi_snprintf(attr_buf, sizeof(attr_buf), "Attr 0x%x", ahdr->type); attr_name = attr_buf; } len = pj_ansi_snprintf(p, end-p, " %s: length=%d", attr_name, (int)ahdr->length); APPLY(); switch (ahdr->type) { case PJ_STUN_ATTR_MAPPED_ADDR: case PJ_STUN_ATTR_RESPONSE_ADDR: case PJ_STUN_ATTR_SOURCE_ADDR: case PJ_STUN_ATTR_CHANGED_ADDR: case PJ_STUN_ATTR_REFLECTED_FROM: case PJ_STUN_ATTR_XOR_PEER_ADDR: case PJ_STUN_ATTR_XOR_RELAYED_ADDR: case PJ_STUN_ATTR_XOR_MAPPED_ADDR: case PJ_STUN_ATTR_XOR_REFLECTED_FROM: case PJ_STUN_ATTR_ALTERNATE_SERVER: { const pj_stun_sockaddr_attr *attr; attr = (const pj_stun_sockaddr_attr*)ahdr; if (attr->sockaddr.addr.sa_family == pj_AF_INET()) { len = pj_ansi_snprintf(p, end-p, ", IPv4 addr=%s:%d\n", pj_inet_ntoa(attr->sockaddr.ipv4.sin_addr), pj_ntohs(attr->sockaddr.ipv4.sin_port)); } else if (attr->sockaddr.addr.sa_family == pj_AF_INET6()) { len = pj_ansi_snprintf(p, end-p, ", IPv6 addr present\n"); } else { len = pj_ansi_snprintf(p, end-p, ", INVALID ADDRESS FAMILY!\n"); } APPLY(); } break; case PJ_STUN_ATTR_CHANNEL_NUMBER: { const pj_stun_uint_attr *attr; attr = (const pj_stun_uint_attr*)ahdr; len = pj_ansi_snprintf(p, end-p, ", chnum=%u (0x%x)\n", (int)PJ_STUN_GET_CH_NB(attr->value), (int)PJ_STUN_GET_CH_NB(attr->value)); APPLY(); } break; case PJ_STUN_ATTR_CHANGE_REQUEST: case PJ_STUN_ATTR_LIFETIME: case PJ_STUN_ATTR_BANDWIDTH: case PJ_STUN_ATTR_REQ_ADDR_TYPE: case PJ_STUN_ATTR_EVEN_PORT: case PJ_STUN_ATTR_REQ_TRANSPORT: case PJ_STUN_ATTR_TIMER_VAL: case PJ_STUN_ATTR_PRIORITY: case PJ_STUN_ATTR_FINGERPRINT: case PJ_STUN_ATTR_REFRESH_INTERVAL: case PJ_STUN_ATTR_ICMP: { const pj_stun_uint_attr *attr; attr = (const pj_stun_uint_attr*)ahdr; len = pj_ansi_snprintf(p, end-p, ", value=%u (0x%x)\n", (pj_uint32_t)attr->value, (pj_uint32_t)attr->value); APPLY(); } break; case PJ_STUN_ATTR_USERNAME: case PJ_STUN_ATTR_PASSWORD: case PJ_STUN_ATTR_REALM: case PJ_STUN_ATTR_NONCE: case PJ_STUN_ATTR_SOFTWARE: { const pj_stun_string_attr *attr; attr = (pj_stun_string_attr*)ahdr; len = pj_ansi_snprintf(p, end-p, ", value=\"%.*s\"\n", (int)attr->value.slen, attr->value.ptr); APPLY(); } break; case PJ_STUN_ATTR_ERROR_CODE: { const pj_stun_errcode_attr *attr; attr = (const pj_stun_errcode_attr*) ahdr; len = pj_ansi_snprintf(p, end-p, ", err_code=%d, reason=\"%.*s\"\n", attr->err_code, (int)attr->reason.slen, attr->reason.ptr); APPLY(); } break; case PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES: { const pj_stun_unknown_attr *attr; unsigned j; attr = (const pj_stun_unknown_attr*) ahdr; len = pj_ansi_snprintf(p, end-p, ", unknown list:"); APPLY(); for (j=0; jattr_count; ++j) { len = pj_ansi_snprintf(p, end-p, " %d", (int)attr->attrs[j]); APPLY(); } } break; case PJ_STUN_ATTR_MESSAGE_INTEGRITY: { const pj_stun_msgint_attr *attr; attr = (const pj_stun_msgint_attr*) ahdr; len = print_binary(p, (unsigned)(end-p), attr->hmac, 20); APPLY(); } break; case PJ_STUN_ATTR_DATA: { const pj_stun_binary_attr *attr; attr = (const pj_stun_binary_attr*) ahdr; len = print_binary(p, (unsigned)(end-p), attr->data, attr->length); APPLY(); } break; case PJ_STUN_ATTR_ICE_CONTROLLED: case PJ_STUN_ATTR_ICE_CONTROLLING: case PJ_STUN_ATTR_RESERVATION_TOKEN: { const pj_stun_uint64_attr *attr; pj_uint8_t data[8]; int i; attr = (const pj_stun_uint64_attr*) ahdr; for (i=0; i<8; ++i) data[i] = ((const pj_uint8_t*)&attr->value)[7-i]; len = print_binary(p, (unsigned)(end-p), data, 8); APPLY(); } break; case PJ_STUN_ATTR_USE_CANDIDATE: case PJ_STUN_ATTR_DONT_FRAGMENT: default: len = pj_ansi_snprintf(p, end-p, "\n"); APPLY(); break; } return (int)(p-buffer); on_return: return len; } /* * Dump STUN message to a printable string output. */ PJ_DEF(char*) pj_stun_msg_dump(const pj_stun_msg *msg, char *buffer, unsigned length, unsigned *printed_len) { char *p, *end; int len; unsigned i; PJ_ASSERT_RETURN(msg && buffer && length, NULL); PJ_CHECK_STACK(); p = buffer; end = buffer + length; len = pj_ansi_snprintf(p, end-p, "STUN %s %s\n", pj_stun_get_method_name(msg->hdr.type), pj_stun_get_class_name(msg->hdr.type)); APPLY(); len = pj_ansi_snprintf(p, end-p, " Hdr: length=%d, magic=%08x, tsx_id=%08x%08x%08x\n" " Attributes:\n", msg->hdr.length, msg->hdr.magic, *(pj_uint32_t*)&msg->hdr.tsx_id[0], *(pj_uint32_t*)&msg->hdr.tsx_id[4], *(pj_uint32_t*)&msg->hdr.tsx_id[8]); APPLY(); for (i=0; iattr_count; ++i) { len = print_attr(p, (unsigned)(end-p), msg->attr[i]); APPLY(); } on_return: *p = '\0'; if (printed_len) *printed_len = (unsigned)(p-buffer); return buffer; #undef APPLY } #endif /* PJ_LOG_MAX_LEVEL > 0 */ ================================================ FILE: deps/pjsip/pjnath/src/pjnath/stun_session.c ================================================ /* $Id: stun_session.c 4360 2013-02-21 11:26:35Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include struct pj_stun_session { pj_stun_config *cfg; pj_pool_t *pool; pj_grp_lock_t *grp_lock; pj_stun_session_cb cb; void *user_data; pj_bool_t is_destroying; pj_bool_t use_fingerprint; pj_pool_t *rx_pool; #if PJ_LOG_MAX_LEVEL >= 5 char dump_buf[1000]; #endif unsigned log_flag; pj_stun_auth_type auth_type; pj_stun_auth_cred cred; int auth_retry; pj_str_t next_nonce; pj_str_t server_realm; pj_str_t srv_name; pj_stun_tx_data pending_request_list; pj_stun_tx_data cached_response_list; }; #define SNAME(s_) ((s_)->pool->obj_name) #define THIS_FILE "stun_session.c" #if 1 # define TRACE_(expr) PJ_LOG(5,expr) #else # define TRACE_(expr) #endif #define LOG_ERR_(sess,title,rc) PJ_PERROR(3,(sess->pool->obj_name,rc,title)) #define TDATA_POOL_SIZE PJNATH_POOL_LEN_STUN_TDATA #define TDATA_POOL_INC PJNATH_POOL_INC_STUN_TDATA static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, pj_status_t status, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len); static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx, const void *stun_pkt, pj_size_t pkt_size); static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx); static void stun_sess_on_destroy(void *comp); static pj_stun_tsx_cb tsx_cb = { &stun_tsx_on_complete, &stun_tsx_on_send_msg, &stun_tsx_on_destroy }; static pj_status_t tsx_add(pj_stun_session *sess, pj_stun_tx_data *tdata) { pj_list_push_front(&sess->pending_request_list, tdata); return PJ_SUCCESS; } static pj_status_t tsx_erase(pj_stun_session *sess, pj_stun_tx_data *tdata) { PJ_UNUSED_ARG(sess); pj_list_erase(tdata); return PJ_SUCCESS; } static pj_stun_tx_data* tsx_lookup(pj_stun_session *sess, const pj_stun_msg *msg) { pj_stun_tx_data *tdata; tdata = sess->pending_request_list.next; while (tdata != &sess->pending_request_list) { pj_assert(sizeof(tdata->msg_key)==sizeof(msg->hdr.tsx_id)); if (tdata->msg_magic == msg->hdr.magic && pj_memcmp(tdata->msg_key, msg->hdr.tsx_id, sizeof(msg->hdr.tsx_id))==0) { return tdata; } tdata = tdata->next; } return NULL; } static pj_status_t create_tdata(pj_stun_session *sess, pj_stun_tx_data **p_tdata) { pj_pool_t *pool; pj_stun_tx_data *tdata; /* Create pool and initialize basic tdata attributes */ pool = pj_pool_create(sess->cfg->pf, "tdata%p", TDATA_POOL_SIZE, TDATA_POOL_INC, NULL); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); tdata = PJ_POOL_ZALLOC_T(pool, pj_stun_tx_data); tdata->pool = pool; tdata->sess = sess; pj_list_init(tdata); *p_tdata = tdata; return PJ_SUCCESS; } static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx) { pj_stun_tx_data *tdata; tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx); pj_stun_client_tsx_stop(tsx); if (tdata) { pj_stun_session *sess = tdata->sess; pj_grp_lock_acquire(sess->grp_lock); tsx_erase(sess, tdata); pj_pool_release(tdata->pool); pj_grp_lock_release(sess->grp_lock); } pj_stun_client_tsx_destroy(tsx); TRACE_((THIS_FILE, "STUN transaction %p destroyed", tsx)); } static void destroy_tdata(pj_stun_tx_data *tdata, pj_bool_t force) { TRACE_((THIS_FILE, "tdata %p destroy request, force=%d, tsx=%p", tdata, force, tdata->client_tsx)); if (tdata->res_timer.id != PJ_FALSE) { pj_timer_heap_cancel_if_active(tdata->sess->cfg->timer_heap, &tdata->res_timer, PJ_FALSE); pj_list_erase(tdata); } if (force) { pj_list_erase(tdata); if (tdata->client_tsx) { pj_stun_client_tsx_stop(tdata->client_tsx); pj_stun_client_tsx_set_data(tdata->client_tsx, NULL); } pj_pool_release(tdata->pool); } else { if (tdata->client_tsx) { /* "Probably" this is to absorb retransmission */ pj_time_val delay = {0, 300}; pj_stun_client_tsx_schedule_destroy(tdata->client_tsx, &delay); } else { pj_pool_release(tdata->pool); } } } /* * Destroy the transmit data. */ PJ_DEF(void) pj_stun_msg_destroy_tdata( pj_stun_session *sess, pj_stun_tx_data *tdata) { PJ_UNUSED_ARG(sess); destroy_tdata(tdata, PJ_FALSE); } /* Timer callback to be called when it's time to destroy response cache */ static void on_cache_timeout(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { pj_stun_tx_data *tdata; pj_stun_session *sess; PJ_UNUSED_ARG(timer_heap); entry->id = PJ_FALSE; tdata = (pj_stun_tx_data*) entry->user_data; sess = tdata->sess; pj_grp_lock_acquire(sess->grp_lock); if (sess->is_destroying) { pj_grp_lock_release(sess->grp_lock); return; } PJ_LOG(5,(SNAME(tdata->sess), "Response cache deleted")); pj_list_erase(tdata); pj_grp_lock_release(sess->grp_lock); destroy_tdata(tdata, PJ_FALSE); } static pj_status_t apply_msg_options(pj_stun_session *sess, pj_pool_t *pool, const pj_stun_req_cred_info *auth_info, pj_stun_msg *msg) { pj_status_t status = 0; pj_str_t realm, username, nonce, auth_key; /* If the agent is sending a request, it SHOULD add a SOFTWARE attribute * to the request. The server SHOULD include a SOFTWARE attribute in all * responses. * * If magic value is not PJ_STUN_MAGIC, only apply the attribute for * responses. */ if (sess->srv_name.slen && pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_SOFTWARE, 0)==NULL && (PJ_STUN_IS_RESPONSE(msg->hdr.type) || (PJ_STUN_IS_REQUEST(msg->hdr.type) && msg->hdr.magic==PJ_STUN_MAGIC))) { pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE, &sess->srv_name); } if (pj_stun_auth_valid_for_msg(msg) && auth_info) { realm = auth_info->realm; username = auth_info->username; nonce = auth_info->nonce; auth_key = auth_info->auth_key; } else { realm.slen = username.slen = nonce.slen = auth_key.slen = 0; } /* Create and add USERNAME attribute if needed */ if (username.slen && PJ_STUN_IS_REQUEST(msg->hdr.type)) { status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_USERNAME, &username); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } /* Add REALM only when long term credential is used */ if (realm.slen && PJ_STUN_IS_REQUEST(msg->hdr.type)) { status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_REALM, &realm); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } /* Add NONCE when desired */ if (nonce.slen && (PJ_STUN_IS_REQUEST(msg->hdr.type) || PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))) { status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_NONCE, &nonce); } /* Add MESSAGE-INTEGRITY attribute */ if (username.slen && auth_key.slen) { status = pj_stun_msg_add_msgint_attr(pool, msg); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } /* Add FINGERPRINT attribute if necessary */ if (sess->use_fingerprint) { status = pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } return PJ_SUCCESS; } static pj_status_t handle_auth_challenge(pj_stun_session *sess, const pj_stun_tx_data *request, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len, pj_bool_t *notify_user) { const pj_stun_errcode_attr *ea; *notify_user = PJ_TRUE; if (response==NULL) return PJ_SUCCESS; if (sess->auth_type != PJ_STUN_AUTH_LONG_TERM) return PJ_SUCCESS; if (!PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type)) { sess->auth_retry = 0; return PJ_SUCCESS; } ea = (const pj_stun_errcode_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0); if (!ea) { PJ_LOG(4,(SNAME(sess), "Invalid error response: no ERROR-CODE" " attribute")); *notify_user = PJ_FALSE; return PJNATH_EINSTUNMSG; } if (ea->err_code == PJ_STUN_SC_UNAUTHORIZED || ea->err_code == PJ_STUN_SC_STALE_NONCE) { const pj_stun_nonce_attr *anonce; const pj_stun_realm_attr *arealm; pj_stun_tx_data *tdata; unsigned i; pj_status_t status; anonce = (const pj_stun_nonce_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_NONCE, 0); if (!anonce) { PJ_LOG(4,(SNAME(sess), "Invalid response: missing NONCE")); *notify_user = PJ_FALSE; return PJNATH_EINSTUNMSG; } /* Bail out if we've supplied the correct nonce */ if (pj_strcmp(&anonce->value, &sess->next_nonce)==0) { return PJ_SUCCESS; } /* Bail out if we've tried too many */ if (++sess->auth_retry > 3) { PJ_LOG(4,(SNAME(sess), "Error: authentication failed (too " "many retries)")); return PJ_STATUS_FROM_STUN_CODE(401); } /* Save next_nonce */ pj_strdup(sess->pool, &sess->next_nonce, &anonce->value); /* Copy the realm from the response */ arealm = (pj_stun_realm_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_REALM, 0); if (arealm) { pj_strdup(sess->pool, &sess->server_realm, &arealm->value); while (sess->server_realm.slen && !sess->server_realm.ptr[sess->server_realm.slen-1]) { --sess->server_realm.slen; } } /* Create new request */ status = pj_stun_session_create_req(sess, request->msg->hdr.type, request->msg->hdr.magic, NULL, &tdata); if (status != PJ_SUCCESS) return status; /* Duplicate all the attributes in the old request, except * USERNAME, REALM, M-I, and NONCE, which will be filled in * later. */ for (i=0; imsg->attr_count; ++i) { const pj_stun_attr_hdr *asrc = request->msg->attr[i]; if (asrc->type == PJ_STUN_ATTR_USERNAME || asrc->type == PJ_STUN_ATTR_REALM || asrc->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY || asrc->type == PJ_STUN_ATTR_NONCE) { continue; } tdata->msg->attr[tdata->msg->attr_count++] = pj_stun_attr_clone(tdata->pool, asrc); } /* Will retry the request with authentication, no need to * notify user. */ *notify_user = PJ_FALSE; PJ_LOG(4,(SNAME(sess), "Retrying request with new authentication")); /* Retry the request */ status = pj_stun_session_send_msg(sess, request->token, PJ_TRUE, request->retransmit, src_addr, src_addr_len, tdata); } else { sess->auth_retry = 0; } return PJ_SUCCESS; } static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, pj_status_t status, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_session *sess; pj_bool_t notify_user = PJ_TRUE; pj_stun_tx_data *tdata; tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx); sess = tdata->sess; /* Lock the session and prevent user from destroying us in the callback */ pj_grp_lock_acquire(sess->grp_lock); if (sess->is_destroying) { pj_stun_msg_destroy_tdata(sess, tdata); pj_grp_lock_release(sess->grp_lock); return; } /* Handle authentication challenge */ handle_auth_challenge(sess, tdata, response, src_addr, src_addr_len, ¬ify_user); if (notify_user && sess->cb.on_request_complete) { (*sess->cb.on_request_complete)(sess, status, tdata->token, tdata, response, src_addr, src_addr_len); } /* Destroy the transmit data. This will remove the transaction * from the pending list too. */ if (status == PJNATH_ESTUNTIMEDOUT) destroy_tdata(tdata, PJ_TRUE); else destroy_tdata(tdata, PJ_FALSE); tdata = NULL; pj_grp_lock_release(sess->grp_lock); } static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx, const void *stun_pkt, pj_size_t pkt_size) { pj_stun_tx_data *tdata; pj_stun_session *sess; pj_status_t status; tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx); sess = tdata->sess; /* Lock the session and prevent user from destroying us in the callback */ pj_grp_lock_acquire(sess->grp_lock); if (sess->is_destroying) { /* Stray timer */ pj_grp_lock_release(sess->grp_lock); return PJ_EINVALIDOP; } status = sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt, pkt_size, tdata->dst_addr, tdata->addr_len); if (pj_grp_lock_release(sess->grp_lock)) return PJ_EGONE; return status; } /* **************************************************************************/ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg, const char *name, const pj_stun_session_cb *cb, pj_bool_t fingerprint, pj_grp_lock_t *grp_lock, pj_stun_session **p_sess) { pj_pool_t *pool; pj_stun_session *sess; pj_status_t status; PJ_ASSERT_RETURN(cfg && cb && p_sess, PJ_EINVAL); if (name==NULL) name = "stuse%p"; pool = pj_pool_create(cfg->pf, name, PJNATH_POOL_LEN_STUN_SESS, PJNATH_POOL_INC_STUN_SESS, NULL); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); sess = PJ_POOL_ZALLOC_T(pool, pj_stun_session); sess->cfg = cfg; sess->pool = pool; pj_memcpy(&sess->cb, cb, sizeof(*cb)); sess->use_fingerprint = fingerprint; sess->log_flag = 0xFFFF; if (grp_lock) { sess->grp_lock = grp_lock; } else { status = pj_grp_lock_create(pool, NULL, &sess->grp_lock); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } } pj_grp_lock_add_ref(sess->grp_lock); pj_grp_lock_add_handler(sess->grp_lock, pool, sess, &stun_sess_on_destroy); pj_stun_session_set_software_name(sess, &cfg->software_name); sess->rx_pool = pj_pool_create(sess->cfg->pf, name, PJNATH_POOL_LEN_STUN_TDATA, PJNATH_POOL_INC_STUN_TDATA, NULL); pj_list_init(&sess->pending_request_list); pj_list_init(&sess->cached_response_list); *p_sess = sess; return PJ_SUCCESS; } static void stun_sess_on_destroy(void *comp) { pj_stun_session *sess = (pj_stun_session*)comp; while (!pj_list_empty(&sess->pending_request_list)) { pj_stun_tx_data *tdata = sess->pending_request_list.next; destroy_tdata(tdata, PJ_TRUE); } while (!pj_list_empty(&sess->cached_response_list)) { pj_stun_tx_data *tdata = sess->cached_response_list.next; destroy_tdata(tdata, PJ_TRUE); } if (sess->rx_pool) { pj_pool_release(sess->rx_pool); sess->rx_pool = NULL; } pj_pool_release(sess->pool); TRACE_((THIS_FILE, "STUN session %p destroyed", sess)); } PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess) { pj_stun_tx_data *tdata; PJ_ASSERT_RETURN(sess, PJ_EINVAL); TRACE_((SNAME(sess), "STUN session %p destroy request, ref_cnt=%d", sess, pj_grp_lock_get_ref(sess->grp_lock))); pj_grp_lock_acquire(sess->grp_lock); if (sess->is_destroying) { /* Prevent from decrementing the ref counter more than once */ pj_grp_lock_release(sess->grp_lock); return PJ_EINVALIDOP; } sess->is_destroying = PJ_TRUE; /* We need to stop transactions and cached response because they are * holding the group lock's reference counter while retransmitting. */ tdata = sess->pending_request_list.next; while (tdata != &sess->pending_request_list) { if (tdata->client_tsx) pj_stun_client_tsx_stop(tdata->client_tsx); tdata = tdata->next; } tdata = sess->cached_response_list.next; while (tdata != &sess->cached_response_list) { pj_timer_heap_cancel_if_active(tdata->sess->cfg->timer_heap, &tdata->res_timer, PJ_FALSE); tdata = tdata->next; } pj_grp_lock_dec_ref(sess->grp_lock); pj_grp_lock_release(sess->grp_lock); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_stun_session_set_user_data( pj_stun_session *sess, void *user_data) { PJ_ASSERT_RETURN(sess, PJ_EINVAL); pj_grp_lock_acquire(sess->grp_lock); sess->user_data = user_data; pj_grp_lock_release(sess->grp_lock); return PJ_SUCCESS; } PJ_DEF(void*) pj_stun_session_get_user_data(pj_stun_session *sess) { PJ_ASSERT_RETURN(sess, NULL); return sess->user_data; } PJ_DEF(pj_grp_lock_t *) pj_stun_session_get_grp_lock(pj_stun_session *sess) { PJ_ASSERT_RETURN(sess, NULL); return sess->grp_lock; } PJ_DEF(pj_status_t) pj_stun_session_set_software_name(pj_stun_session *sess, const pj_str_t *sw) { PJ_ASSERT_RETURN(sess, PJ_EINVAL); pj_grp_lock_acquire(sess->grp_lock); if (sw && sw->slen) pj_strdup(sess->pool, &sess->srv_name, sw); else sess->srv_name.slen = 0; pj_grp_lock_release(sess->grp_lock); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess, pj_stun_auth_type auth_type, const pj_stun_auth_cred *cred) { PJ_ASSERT_RETURN(sess, PJ_EINVAL); pj_grp_lock_acquire(sess->grp_lock); sess->auth_type = auth_type; if (cred) { pj_stun_auth_cred_dup(sess->pool, &sess->cred, cred); } else { sess->auth_type = PJ_STUN_AUTH_NONE; pj_bzero(&sess->cred, sizeof(sess->cred)); } pj_grp_lock_release(sess->grp_lock); return PJ_SUCCESS; } PJ_DEF(void) pj_stun_session_set_log( pj_stun_session *sess, unsigned flags) { PJ_ASSERT_ON_FAIL(sess, return); sess->log_flag = flags; } PJ_DEF(pj_bool_t) pj_stun_session_use_fingerprint(pj_stun_session *sess, pj_bool_t use) { pj_bool_t old_use; PJ_ASSERT_RETURN(sess, PJ_FALSE); old_use = sess->use_fingerprint; sess->use_fingerprint = use; return old_use; } static pj_status_t get_auth(pj_stun_session *sess, pj_stun_tx_data *tdata) { if (sess->cred.type == PJ_STUN_AUTH_CRED_STATIC) { //tdata->auth_info.realm = sess->cred.data.static_cred.realm; tdata->auth_info.realm = sess->server_realm; tdata->auth_info.username = sess->cred.data.static_cred.username; tdata->auth_info.nonce = sess->cred.data.static_cred.nonce; pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key, &tdata->auth_info.realm, &tdata->auth_info.username, sess->cred.data.static_cred.data_type, &sess->cred.data.static_cred.data); } else if (sess->cred.type == PJ_STUN_AUTH_CRED_DYNAMIC) { pj_str_t password; void *user_data = sess->cred.data.dyn_cred.user_data; pj_stun_passwd_type data_type = PJ_STUN_PASSWD_PLAIN; pj_status_t rc; rc = (*sess->cred.data.dyn_cred.get_cred)(tdata->msg, user_data, tdata->pool, &tdata->auth_info.realm, &tdata->auth_info.username, &tdata->auth_info.nonce, &data_type, &password); if (rc != PJ_SUCCESS) return rc; pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key, &tdata->auth_info.realm, &tdata->auth_info.username, data_type, &password); } else { pj_assert(!"Unknown credential type"); return PJ_EBUG; } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, int method, pj_uint32_t magic, const pj_uint8_t tsx_id[12], pj_stun_tx_data **p_tdata) { pj_stun_tx_data *tdata = NULL; pj_status_t status; PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL); pj_grp_lock_acquire(sess->grp_lock); if (sess->is_destroying) { pj_grp_lock_release(sess->grp_lock); return PJ_EINVALIDOP; } status = create_tdata(sess, &tdata); if (status != PJ_SUCCESS) goto on_error; /* Create STUN message */ status = pj_stun_msg_create(tdata->pool, method, magic, tsx_id, &tdata->msg); if (status != PJ_SUCCESS) goto on_error; /* copy the request's transaction ID as the transaction key. */ pj_assert(sizeof(tdata->msg_key)==sizeof(tdata->msg->hdr.tsx_id)); tdata->msg_magic = tdata->msg->hdr.magic; pj_memcpy(tdata->msg_key, tdata->msg->hdr.tsx_id, sizeof(tdata->msg->hdr.tsx_id)); /* Get authentication information for the request */ if (sess->auth_type == PJ_STUN_AUTH_NONE) { /* No authentication */ } else if (sess->auth_type == PJ_STUN_AUTH_SHORT_TERM) { /* MUST put authentication in request */ status = get_auth(sess, tdata); if (status != PJ_SUCCESS) goto on_error; } else if (sess->auth_type == PJ_STUN_AUTH_LONG_TERM) { /* Only put authentication information if we've received * response from server. */ if (sess->next_nonce.slen != 0) { status = get_auth(sess, tdata); if (status != PJ_SUCCESS) goto on_error; tdata->auth_info.nonce = sess->next_nonce; tdata->auth_info.realm = sess->server_realm; } } else { pj_assert(!"Invalid authentication type"); status = PJ_EBUG; goto on_error; } *p_tdata = tdata; pj_grp_lock_release(sess->grp_lock); return PJ_SUCCESS; on_error: if (tdata) pj_pool_release(tdata->pool); pj_grp_lock_release(sess->grp_lock); return status; } PJ_DEF(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess, int msg_type, pj_stun_tx_data **p_tdata) { pj_stun_tx_data *tdata = NULL; pj_status_t status; PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL); pj_grp_lock_acquire(sess->grp_lock); if (sess->is_destroying) { pj_grp_lock_release(sess->grp_lock); return PJ_EINVALIDOP; } status = create_tdata(sess, &tdata); if (status != PJ_SUCCESS) { pj_grp_lock_release(sess->grp_lock); return status; } /* Create STUN message */ msg_type |= PJ_STUN_INDICATION_BIT; status = pj_stun_msg_create(tdata->pool, msg_type, PJ_STUN_MAGIC, NULL, &tdata->msg); if (status != PJ_SUCCESS) { pj_pool_release(tdata->pool); pj_grp_lock_release(sess->grp_lock); return status; } *p_tdata = tdata; pj_grp_lock_release(sess->grp_lock); return PJ_SUCCESS; } /* * Create a STUN response message. */ PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess, const pj_stun_rx_data *rdata, unsigned err_code, const pj_str_t *err_msg, pj_stun_tx_data **p_tdata) { pj_status_t status; pj_stun_tx_data *tdata = NULL; pj_grp_lock_acquire(sess->grp_lock); if (sess->is_destroying) { pj_grp_lock_release(sess->grp_lock); return PJ_EINVALIDOP; } status = create_tdata(sess, &tdata); if (status != PJ_SUCCESS) { pj_grp_lock_release(sess->grp_lock); return status; } /* Create STUN response message */ status = pj_stun_msg_create_response(tdata->pool, rdata->msg, err_code, err_msg, &tdata->msg); if (status != PJ_SUCCESS) { pj_pool_release(tdata->pool); pj_grp_lock_release(sess->grp_lock); return status; } /* copy the request's transaction ID as the transaction key. */ pj_assert(sizeof(tdata->msg_key)==sizeof(rdata->msg->hdr.tsx_id)); tdata->msg_magic = rdata->msg->hdr.magic; pj_memcpy(tdata->msg_key, rdata->msg->hdr.tsx_id, sizeof(rdata->msg->hdr.tsx_id)); /* copy the credential found in the request */ pj_stun_req_cred_info_dup(tdata->pool, &tdata->auth_info, &rdata->info); *p_tdata = tdata; pj_grp_lock_release(sess->grp_lock); return PJ_SUCCESS; } /* Print outgoing message to log */ static void dump_tx_msg(pj_stun_session *sess, const pj_stun_msg *msg, unsigned pkt_size, const pj_sockaddr_t *addr) { char dst_name[PJ_INET6_ADDRSTRLEN+10]; if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && (sess->log_flag & PJ_STUN_SESS_LOG_TX_REQ)==0) || (PJ_STUN_IS_RESPONSE(msg->hdr.type) && (sess->log_flag & PJ_STUN_SESS_LOG_TX_RES)==0) || (PJ_STUN_IS_INDICATION(msg->hdr.type) && (sess->log_flag & PJ_STUN_SESS_LOG_TX_IND)==0)) { return; } pj_sockaddr_print(addr, dst_name, sizeof(dst_name), 3); PJ_LOG(5,(SNAME(sess), "TX %d bytes STUN message to %s:\n" "--- begin STUN message ---\n" "%s" "--- end of STUN message ---\n", pkt_size, dst_name, pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf), NULL))); } PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, void *token, pj_bool_t cache_res, pj_bool_t retransmit, const pj_sockaddr_t *server, unsigned addr_len, pj_stun_tx_data *tdata) { pj_status_t status; PJ_ASSERT_RETURN(sess && addr_len && server && tdata, PJ_EINVAL); /* Lock the session and prevent user from destroying us in the callback */ pj_grp_lock_acquire(sess->grp_lock); if (sess->is_destroying) { pj_grp_lock_release(sess->grp_lock); return PJ_EINVALIDOP; } pj_log_push_indent(); /* Allocate packet */ tdata->max_len = PJ_STUN_MAX_PKT_LEN; tdata->pkt = pj_pool_alloc(tdata->pool, tdata->max_len); tdata->token = token; tdata->retransmit = retransmit; /* Apply options */ status = apply_msg_options(sess, tdata->pool, &tdata->auth_info, tdata->msg); if (status != PJ_SUCCESS) { pj_stun_msg_destroy_tdata(sess, tdata); LOG_ERR_(sess, "Error applying options", status); goto on_return; } /* Encode message */ status = pj_stun_msg_encode(tdata->msg, (pj_uint8_t*)tdata->pkt, tdata->max_len, 0, &tdata->auth_info.auth_key, &tdata->pkt_size); if (status != PJ_SUCCESS) { pj_stun_msg_destroy_tdata(sess, tdata); LOG_ERR_(sess, "STUN encode() error", status); goto on_return; } /* Dump packet */ dump_tx_msg(sess, tdata->msg, (unsigned)tdata->pkt_size, server); /* If this is a STUN request message, then send the request with * a new STUN client transaction. */ if (PJ_STUN_IS_REQUEST(tdata->msg->hdr.type)) { /* Create STUN client transaction */ status = pj_stun_client_tsx_create(sess->cfg, tdata->pool, sess->grp_lock, &tsx_cb, &tdata->client_tsx); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); pj_stun_client_tsx_set_data(tdata->client_tsx, (void*)tdata); /* Save the remote address */ tdata->addr_len = addr_len; tdata->dst_addr = server; /* Send the request! */ status = pj_stun_client_tsx_send_msg(tdata->client_tsx, retransmit, tdata->pkt, (unsigned)tdata->pkt_size); if (status != PJ_SUCCESS && status != PJ_EPENDING) { pj_stun_msg_destroy_tdata(sess, tdata); LOG_ERR_(sess, "Error sending STUN request", status); goto on_return; } /* Add to pending request list */ tsx_add(sess, tdata); } else { if (cache_res && (PJ_STUN_IS_SUCCESS_RESPONSE(tdata->msg->hdr.type) || PJ_STUN_IS_ERROR_RESPONSE(tdata->msg->hdr.type))) { /* Requested to keep the response in the cache */ pj_time_val timeout; pj_memset(&tdata->res_timer, 0, sizeof(tdata->res_timer)); pj_timer_entry_init(&tdata->res_timer, PJ_FALSE, tdata, &on_cache_timeout); timeout.sec = sess->cfg->res_cache_msec / 1000; timeout.msec = sess->cfg->res_cache_msec % 1000; status = pj_timer_heap_schedule_w_grp_lock(sess->cfg->timer_heap, &tdata->res_timer, &timeout, PJ_TRUE, sess->grp_lock); if (status != PJ_SUCCESS) { pj_stun_msg_destroy_tdata(sess, tdata); LOG_ERR_(sess, "Error scheduling response timer", status); goto on_return; } pj_list_push_back(&sess->cached_response_list, tdata); } /* Otherwise for non-request message, send directly to transport. */ status = sess->cb.on_send_msg(sess, token, tdata->pkt, tdata->pkt_size, server, addr_len); if (status != PJ_SUCCESS && status != PJ_EPENDING) { pj_stun_msg_destroy_tdata(sess, tdata); LOG_ERR_(sess, "Error sending STUN request", status); goto on_return; } /* Destroy only when response is not cached*/ if (tdata->res_timer.id == 0) { pj_stun_msg_destroy_tdata(sess, tdata); } } on_return: pj_log_pop_indent(); if (pj_grp_lock_release(sess->grp_lock)) return PJ_EGONE; return status; } /* * Create and send STUN response message. */ PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess, const pj_stun_rx_data *rdata, unsigned code, const char *errmsg, void *token, pj_bool_t cache, const pj_sockaddr_t *dst_addr, unsigned addr_len) { pj_status_t status; pj_str_t reason; pj_stun_tx_data *tdata; pj_grp_lock_acquire(sess->grp_lock); if (sess->is_destroying) { pj_grp_lock_release(sess->grp_lock); return PJ_EINVALIDOP; } status = pj_stun_session_create_res(sess, rdata, code, (errmsg?pj_cstr(&reason,errmsg):NULL), &tdata); if (status != PJ_SUCCESS) { pj_grp_lock_release(sess->grp_lock); return status; } status = pj_stun_session_send_msg(sess, token, cache, PJ_FALSE, dst_addr, addr_len, tdata); pj_grp_lock_release(sess->grp_lock); return status; } /* * Cancel outgoing STUN transaction. */ PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess, pj_stun_tx_data *tdata, pj_bool_t notify, pj_status_t notify_status) { PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL); PJ_ASSERT_RETURN(!notify || notify_status!=PJ_SUCCESS, PJ_EINVAL); PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL); /* Lock the session and prevent user from destroying us in the callback */ pj_grp_lock_acquire(sess->grp_lock); if (sess->is_destroying) { pj_grp_lock_release(sess->grp_lock); return PJ_EINVALIDOP; } if (notify) { (sess->cb.on_request_complete)(sess, notify_status, tdata->token, tdata, NULL, NULL, 0); } /* Just destroy tdata. This will destroy the transaction as well */ pj_stun_msg_destroy_tdata(sess, tdata); pj_grp_lock_release(sess->grp_lock); return PJ_SUCCESS; } /* * Explicitly request retransmission of the request. */ PJ_DEF(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess, pj_stun_tx_data *tdata, pj_bool_t mod_count) { pj_status_t status; PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL); PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL); /* Lock the session and prevent user from destroying us in the callback */ pj_grp_lock_acquire(sess->grp_lock); if (sess->is_destroying) { pj_grp_lock_release(sess->grp_lock); return PJ_EINVALIDOP; } status = pj_stun_client_tsx_retransmit(tdata->client_tsx, mod_count); pj_grp_lock_release(sess->grp_lock); return status; } /* Send response */ static pj_status_t send_response(pj_stun_session *sess, void *token, pj_pool_t *pool, pj_stun_msg *response, const pj_stun_req_cred_info *auth_info, pj_bool_t retransmission, const pj_sockaddr_t *addr, unsigned addr_len) { pj_uint8_t *out_pkt; pj_size_t out_max_len, out_len; pj_status_t status; /* Apply options */ if (!retransmission) { status = apply_msg_options(sess, pool, auth_info, response); if (status != PJ_SUCCESS) return status; } /* Alloc packet buffer */ out_max_len = PJ_STUN_MAX_PKT_LEN; out_pkt = (pj_uint8_t*) pj_pool_alloc(pool, out_max_len); /* Encode */ status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0, &auth_info->auth_key, &out_len); if (status != PJ_SUCCESS) { LOG_ERR_(sess, "Error encoding message", status); return status; } /* Print log */ dump_tx_msg(sess, response, (unsigned)out_len, addr); /* Send packet */ status = sess->cb.on_send_msg(sess, token, out_pkt, (unsigned)out_len, addr, addr_len); return status; } /* Authenticate incoming message */ static pj_status_t authenticate_req(pj_stun_session *sess, void *token, const pj_uint8_t *pkt, unsigned pkt_len, pj_stun_rx_data *rdata, pj_pool_t *tmp_pool, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_msg *response; pj_status_t status; if (PJ_STUN_IS_ERROR_RESPONSE(rdata->msg->hdr.type) || sess->auth_type == PJ_STUN_AUTH_NONE) { return PJ_SUCCESS; } status = pj_stun_authenticate_request(pkt, pkt_len, rdata->msg, &sess->cred, tmp_pool, &rdata->info, &response); if (status != PJ_SUCCESS && response != NULL) { PJ_LOG(5,(SNAME(sess), "Message authentication failed")); send_response(sess, token, tmp_pool, response, &rdata->info, PJ_FALSE, src_addr, src_addr_len); } return status; } /* Handle incoming response */ static pj_status_t on_incoming_response(pj_stun_session *sess, unsigned options, const pj_uint8_t *pkt, unsigned pkt_len, pj_stun_msg *msg, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_tx_data *tdata; pj_status_t status; /* Lookup pending client transaction */ tdata = tsx_lookup(sess, msg); if (tdata == NULL) { PJ_LOG(5,(SNAME(sess), "Transaction not found, response silently discarded")); return PJ_SUCCESS; } if (sess->auth_type == PJ_STUN_AUTH_NONE) options |= PJ_STUN_NO_AUTHENTICATE; /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE * is specified in the option. */ if ((options & PJ_STUN_NO_AUTHENTICATE) == 0 && tdata->auth_info.auth_key.slen != 0 && pj_stun_auth_valid_for_msg(msg)) { status = pj_stun_authenticate_response(pkt, pkt_len, msg, &tdata->auth_info.auth_key); if (status != PJ_SUCCESS) { PJ_LOG(5,(SNAME(sess), "Response authentication failed")); return status; } } /* Pass the response to the transaction. * If the message is accepted, transaction callback will be called, * and this will call the session callback too. */ status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg, src_addr, src_addr_len); if (status != PJ_SUCCESS) { return status; } return PJ_SUCCESS; } /* For requests, check if we cache the response */ static pj_status_t check_cached_response(pj_stun_session *sess, pj_pool_t *tmp_pool, const pj_stun_msg *msg, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_tx_data *t; /* First lookup response in response cache */ t = sess->cached_response_list.next; while (t != &sess->cached_response_list) { if (t->msg_magic == msg->hdr.magic && t->msg->hdr.type == msg->hdr.type && pj_memcmp(t->msg_key, msg->hdr.tsx_id, sizeof(msg->hdr.tsx_id))==0) { break; } t = t->next; } if (t != &sess->cached_response_list) { /* Found response in the cache */ PJ_LOG(5,(SNAME(sess), "Request retransmission, sending cached response")); send_response(sess, t->token, tmp_pool, t->msg, &t->auth_info, PJ_TRUE, src_addr, src_addr_len); return PJ_SUCCESS; } return PJ_ENOTFOUND; } /* Handle incoming request */ static pj_status_t on_incoming_request(pj_stun_session *sess, unsigned options, void *token, pj_pool_t *tmp_pool, const pj_uint8_t *in_pkt, unsigned in_pkt_len, pj_stun_msg *msg, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_rx_data rdata; pj_status_t status; /* Init rdata */ rdata.msg = msg; pj_bzero(&rdata.info, sizeof(rdata.info)); if (sess->auth_type == PJ_STUN_AUTH_NONE) options |= PJ_STUN_NO_AUTHENTICATE; /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE * is specified in the option. */ if ((options & PJ_STUN_NO_AUTHENTICATE) == 0) { status = authenticate_req(sess, token, (const pj_uint8_t*) in_pkt, in_pkt_len,&rdata, tmp_pool, src_addr, src_addr_len); if (status != PJ_SUCCESS) { return status; } } /* Distribute to handler, or respond with Bad Request */ if (sess->cb.on_rx_request) { status = (*sess->cb.on_rx_request)(sess, in_pkt, in_pkt_len, &rdata, token, src_addr, src_addr_len); } else { pj_str_t err_text; pj_stun_msg *response; err_text = pj_str("Callback is not set to handle request"); status = pj_stun_msg_create_response(tmp_pool, msg, PJ_STUN_SC_BAD_REQUEST, &err_text, &response); if (status == PJ_SUCCESS && response) { status = send_response(sess, token, tmp_pool, response, NULL, PJ_FALSE, src_addr, src_addr_len); } } return status; } /* Handle incoming indication */ static pj_status_t on_incoming_indication(pj_stun_session *sess, void *token, pj_pool_t *tmp_pool, const pj_uint8_t *in_pkt, unsigned in_pkt_len, const pj_stun_msg *msg, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { PJ_UNUSED_ARG(tmp_pool); /* Distribute to handler */ if (sess->cb.on_rx_indication) { return (*sess->cb.on_rx_indication)(sess, in_pkt, in_pkt_len, msg, token, src_addr, src_addr_len); } else { return PJ_SUCCESS; } } /* Print outgoing message to log */ static void dump_rx_msg(pj_stun_session *sess, const pj_stun_msg *msg, unsigned pkt_size, const pj_sockaddr_t *addr) { char src_info[PJ_INET6_ADDRSTRLEN+10]; if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && (sess->log_flag & PJ_STUN_SESS_LOG_RX_REQ)==0) || (PJ_STUN_IS_RESPONSE(msg->hdr.type) && (sess->log_flag & PJ_STUN_SESS_LOG_RX_RES)==0) || (PJ_STUN_IS_INDICATION(msg->hdr.type) && (sess->log_flag & PJ_STUN_SESS_LOG_RX_IND)==0)) { return; } pj_sockaddr_print(addr, src_info, sizeof(src_info), 3); PJ_LOG(5,(SNAME(sess), "RX %d bytes STUN message from %s:\n" "--- begin STUN message ---\n" "%s" "--- end of STUN message ---\n", pkt_size, src_info, pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf), NULL))); } /* Incoming packet */ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, const void *packet, pj_size_t pkt_size, unsigned options, void *token, pj_size_t *parsed_len, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_msg *msg, *response; pj_status_t status; PJ_ASSERT_RETURN(sess && packet && pkt_size, PJ_EINVAL); /* Lock the session and prevent user from destroying us in the callback */ pj_grp_lock_acquire(sess->grp_lock); if (sess->is_destroying) { pj_grp_lock_release(sess->grp_lock); return PJ_EINVALIDOP; } pj_log_push_indent(); /* Reset pool */ pj_pool_reset(sess->rx_pool); /* Try to parse the message */ status = pj_stun_msg_decode(sess->rx_pool, (const pj_uint8_t*)packet, pkt_size, options, &msg, parsed_len, &response); if (status != PJ_SUCCESS) { LOG_ERR_(sess, "STUN msg_decode() error", status); if (response) { send_response(sess, token, sess->rx_pool, response, NULL, PJ_FALSE, src_addr, src_addr_len); } goto on_return; } dump_rx_msg(sess, msg, (unsigned)pkt_size, src_addr); /* For requests, check if we have cached response */ status = check_cached_response(sess, sess->rx_pool, msg, src_addr, src_addr_len); if (status == PJ_SUCCESS) { goto on_return; } /* Handle message */ if (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) || PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) { status = on_incoming_response(sess, options, (const pj_uint8_t*) packet, (unsigned)pkt_size, msg, src_addr, src_addr_len); } else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) { status = on_incoming_request(sess, options, token, sess->rx_pool, (const pj_uint8_t*) packet, (unsigned)pkt_size, msg, src_addr, src_addr_len); } else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) { status = on_incoming_indication(sess, token, sess->rx_pool, (const pj_uint8_t*) packet, (unsigned)pkt_size, msg, src_addr, src_addr_len); } else { pj_assert(!"Unexpected!"); status = PJ_EBUG; } on_return: pj_log_pop_indent(); if (pj_grp_lock_release(sess->grp_lock)) return PJ_EGONE; return status; } ================================================ FILE: deps/pjsip/pjnath/src/pjnath/stun_sock.c ================================================ /* $Id: stun_sock.c 4360 2013-02-21 11:26:35Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if 1 # define TRACE_(x) PJ_LOG(5,x) #else # define TRACE_(x) #endif enum { MAX_BIND_RETRY = 100 }; struct pj_stun_sock { char *obj_name; /* Log identification */ pj_pool_t *pool; /* Pool */ void *user_data; /* Application user data */ pj_bool_t is_destroying; /* Destroy already called */ int af; /* Address family */ pj_stun_config stun_cfg; /* STUN config (ioqueue etc)*/ pj_stun_sock_cb cb; /* Application callbacks */ int ka_interval; /* Keep alive interval */ pj_timer_entry ka_timer; /* Keep alive timer. */ pj_sockaddr srv_addr; /* Resolved server addr */ pj_sockaddr mapped_addr; /* Our public address */ pj_dns_srv_async_query *q; /* Pending DNS query */ pj_sock_t sock_fd; /* Socket descriptor */ pj_activesock_t *active_sock; /* Active socket object */ pj_ioqueue_op_key_t send_key; /* Default send key for app */ pj_ioqueue_op_key_t int_send_key; /* Send key for internal */ pj_uint16_t tsx_id[6]; /* .. to match STUN msg */ pj_stun_session *stun_sess; /* STUN session */ pj_grp_lock_t *grp_lock; /* Session group lock */ }; /* * Prototypes for static functions */ /* Destructor for group lock */ static void stun_sock_destructor(void *obj); /* This callback is called by the STUN session to send packet */ static pj_status_t sess_on_send_msg(pj_stun_session *sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len); /* This callback is called by the STUN session when outgoing transaction * is complete */ static void sess_on_request_complete(pj_stun_session *sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len); /* DNS resolver callback */ static void dns_srv_resolver_cb(void *user_data, pj_status_t status, const pj_dns_srv_record *rec); /* Start sending STUN Binding request */ static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock); /* Callback from active socket when incoming packet is received */ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, void *data, pj_size_t size, const pj_sockaddr_t *src_addr, int addr_len, pj_status_t status); /* Callback from active socket about send status */ static pj_bool_t on_data_sent(pj_activesock_t *asock, pj_ioqueue_op_key_t *send_key, pj_ssize_t sent); /* Schedule keep-alive timer */ static void start_ka_timer(pj_stun_sock *stun_sock); /* Keep-alive timer callback */ static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te); #define INTERNAL_MSG_TOKEN (void*)(pj_ssize_t)1 /* * Retrieve the name representing the specified operation. */ PJ_DEF(const char*) pj_stun_sock_op_name(pj_stun_sock_op op) { const char *names[] = { "?", "DNS resolution", "STUN Binding request", "Keep-alive", "Mapped addr. changed" }; return op < PJ_ARRAY_SIZE(names) ? names[op] : "???"; }; /* * Initialize the STUN transport setting with its default values. */ PJ_DEF(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg) { pj_bzero(cfg, sizeof(*cfg)); cfg->max_pkt_size = PJ_STUN_SOCK_PKT_LEN; cfg->async_cnt = 1; cfg->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; cfg->qos_type = PJ_QOS_TYPE_BEST_EFFORT; cfg->qos_ignore_error = PJ_TRUE; } /* Check that configuration setting is valid */ static pj_bool_t pj_stun_sock_cfg_is_valid(const pj_stun_sock_cfg *cfg) { return cfg->max_pkt_size > 1 && cfg->async_cnt >= 1; } /* * Create the STUN transport using the specified configuration. */ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, const char *name, int af, const pj_stun_sock_cb *cb, const pj_stun_sock_cfg *cfg, void *user_data, pj_stun_sock **p_stun_sock) { pj_pool_t *pool; pj_stun_sock *stun_sock; pj_stun_sock_cfg default_cfg; pj_sockaddr bound_addr; unsigned i; pj_uint16_t max_bind_retry; pj_status_t status; PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL); PJ_ASSERT_RETURN(af==pj_AF_INET()||af==pj_AF_INET6(), PJ_EAFNOTSUP); PJ_ASSERT_RETURN(!cfg || pj_stun_sock_cfg_is_valid(cfg), PJ_EINVAL); PJ_ASSERT_RETURN(cb->on_status, PJ_EINVAL); status = pj_stun_config_check_valid(stun_cfg); if (status != PJ_SUCCESS) return status; if (name == NULL) name = "stuntp%p"; if (cfg == NULL) { pj_stun_sock_cfg_default(&default_cfg); cfg = &default_cfg; } /* Create structure */ pool = pj_pool_create(stun_cfg->pf, name, 256, 512, NULL); stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock); stun_sock->pool = pool; stun_sock->obj_name = pool->obj_name; stun_sock->user_data = user_data; stun_sock->af = af; stun_sock->sock_fd = PJ_INVALID_SOCKET; pj_memcpy(&stun_sock->stun_cfg, stun_cfg, sizeof(*stun_cfg)); pj_memcpy(&stun_sock->cb, cb, sizeof(*cb)); stun_sock->ka_interval = cfg->ka_interval; if (stun_sock->ka_interval == 0) stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; if (cfg->grp_lock) { stun_sock->grp_lock = cfg->grp_lock; } else { status = pj_grp_lock_create(pool, NULL, &stun_sock->grp_lock); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } } pj_grp_lock_add_ref(stun_sock->grp_lock); pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock, &stun_sock_destructor); /* Create socket and bind socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &stun_sock->sock_fd); if (status != PJ_SUCCESS) goto on_error; /* Apply QoS, if specified */ status = pj_sock_apply_qos2(stun_sock->sock_fd, cfg->qos_type, &cfg->qos_params, 2, stun_sock->obj_name, NULL); if (status != PJ_SUCCESS && !cfg->qos_ignore_error) goto on_error; /* Apply socket buffer size */ if (cfg->so_rcvbuf_size > 0) { unsigned sobuf_size = cfg->so_rcvbuf_size; status = pj_sock_setsockopt_sobuf(stun_sock->sock_fd, pj_SO_RCVBUF(), PJ_TRUE, &sobuf_size); if (status != PJ_SUCCESS) { pj_perror(3, stun_sock->obj_name, status, "Failed setting SO_RCVBUF"); } else { if (sobuf_size < cfg->so_rcvbuf_size) { PJ_LOG(4, (stun_sock->obj_name, "Warning! Cannot set SO_RCVBUF as configured, " "now=%d, configured=%d", sobuf_size, cfg->so_rcvbuf_size)); } else { PJ_LOG(5, (stun_sock->obj_name, "SO_RCVBUF set to %d", sobuf_size)); } } } if (cfg->so_sndbuf_size > 0) { unsigned sobuf_size = cfg->so_sndbuf_size; status = pj_sock_setsockopt_sobuf(stun_sock->sock_fd, pj_SO_SNDBUF(), PJ_TRUE, &sobuf_size); if (status != PJ_SUCCESS) { pj_perror(3, stun_sock->obj_name, status, "Failed setting SO_SNDBUF"); } else { if (sobuf_size < cfg->so_sndbuf_size) { PJ_LOG(4, (stun_sock->obj_name, "Warning! Cannot set SO_SNDBUF as configured, " "now=%d, configured=%d", sobuf_size, cfg->so_sndbuf_size)); } else { PJ_LOG(5, (stun_sock->obj_name, "SO_SNDBUF set to %d", sobuf_size)); } } } /* Bind socket */ max_bind_retry = MAX_BIND_RETRY; if (cfg->port_range && cfg->port_range < max_bind_retry) max_bind_retry = cfg->port_range; pj_sockaddr_init(af, &bound_addr, NULL, 0); if (cfg->bound_addr.addr.sa_family == pj_AF_INET() || cfg->bound_addr.addr.sa_family == pj_AF_INET6()) { pj_sockaddr_cp(&bound_addr, &cfg->bound_addr); } status = pj_sock_bind_random(stun_sock->sock_fd, &bound_addr, cfg->port_range, max_bind_retry); if (status != PJ_SUCCESS) goto on_error; /* Create more useful information string about this transport */ #if 0 { pj_sockaddr bound_addr; int addr_len = sizeof(bound_addr); status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; stun_sock->info = pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+10); pj_sockaddr_print(&bound_addr, stun_sock->info, PJ_INET6_ADDRSTRLEN, 3); } #endif /* Init active socket configuration */ { pj_activesock_cfg activesock_cfg; pj_activesock_cb activesock_cb; pj_activesock_cfg_default(&activesock_cfg); activesock_cfg.grp_lock = stun_sock->grp_lock; activesock_cfg.async_cnt = cfg->async_cnt; activesock_cfg.concurrency = 0; /* Create the active socket */ pj_bzero(&activesock_cb, sizeof(activesock_cb)); activesock_cb.on_data_recvfrom = &on_data_recvfrom; activesock_cb.on_data_sent = &on_data_sent; status = pj_activesock_create(pool, stun_sock->sock_fd, pj_SOCK_DGRAM(), &activesock_cfg, stun_cfg->ioqueue, &activesock_cb, stun_sock, &stun_sock->active_sock); if (status != PJ_SUCCESS) goto on_error; /* Start asynchronous read operations */ status = pj_activesock_start_recvfrom(stun_sock->active_sock, pool, cfg->max_pkt_size, 0); if (status != PJ_SUCCESS) goto on_error; /* Init send keys */ pj_ioqueue_op_key_init(&stun_sock->send_key, sizeof(stun_sock->send_key)); pj_ioqueue_op_key_init(&stun_sock->int_send_key, sizeof(stun_sock->int_send_key)); } /* Create STUN session */ { pj_stun_session_cb sess_cb; pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_request_complete = &sess_on_request_complete; sess_cb.on_send_msg = &sess_on_send_msg; status = pj_stun_session_create(&stun_sock->stun_cfg, stun_sock->obj_name, &sess_cb, PJ_FALSE, stun_sock->grp_lock, &stun_sock->stun_sess); if (status != PJ_SUCCESS) goto on_error; } /* Associate us with the STUN session */ pj_stun_session_set_user_data(stun_sock->stun_sess, stun_sock); /* Initialize random numbers to be used as STUN transaction ID for * outgoing Binding request. We use the 80bit number to distinguish * STUN messages we sent with STUN messages that the application sends. * The last 16bit value in the array is a counter. */ for (i=0; itsx_id); ++i) { stun_sock->tsx_id[i] = (pj_uint16_t) pj_rand(); } stun_sock->tsx_id[5] = 0; /* Init timer entry */ stun_sock->ka_timer.cb = &ka_timer_cb; stun_sock->ka_timer.user_data = stun_sock; /* Done */ *p_stun_sock = stun_sock; return PJ_SUCCESS; on_error: pj_stun_sock_destroy(stun_sock); return status; } /* Start socket. */ PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock, const pj_str_t *domain, pj_uint16_t default_port, pj_dns_resolver *resolver) { pj_status_t status; PJ_ASSERT_RETURN(stun_sock && domain && default_port, PJ_EINVAL); pj_grp_lock_acquire(stun_sock->grp_lock); /* Check whether the domain contains IP address */ stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)stun_sock->af; status = pj_inet_pton(stun_sock->af, domain, pj_sockaddr_get_addr(&stun_sock->srv_addr)); if (status != PJ_SUCCESS) { stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)0; } /* If resolver is set, try to resolve with DNS SRV first. It * will fallback to DNS A/AAAA when no SRV record is found. */ if (status != PJ_SUCCESS && resolver) { const pj_str_t res_name = pj_str("_stun._udp."); unsigned opt; pj_assert(stun_sock->q == NULL); opt = PJ_DNS_SRV_FALLBACK_A; if (stun_sock->af == pj_AF_INET6()) { opt |= (PJ_DNS_SRV_RESOLVE_AAAA | PJ_DNS_SRV_FALLBACK_AAAA); } status = pj_dns_srv_resolve(domain, &res_name, default_port, stun_sock->pool, resolver, opt, stun_sock, &dns_srv_resolver_cb, &stun_sock->q); /* Processing will resume when the DNS SRV callback is called */ } else { if (status != PJ_SUCCESS) { pj_addrinfo ai; unsigned cnt = 1; status = pj_getaddrinfo(stun_sock->af, domain, &cnt, &ai); if (status != PJ_SUCCESS) return status; pj_sockaddr_cp(&stun_sock->srv_addr, &ai.ai_addr); } pj_sockaddr_set_port(&stun_sock->srv_addr, (pj_uint16_t)default_port); /* Start sending Binding request */ status = get_mapped_addr(stun_sock); } pj_grp_lock_release(stun_sock->grp_lock); return status; } /* Destructor */ static void stun_sock_destructor(void *obj) { pj_stun_sock *stun_sock = (pj_stun_sock*)obj; if (stun_sock->q) { pj_dns_srv_cancel_query(stun_sock->q, PJ_FALSE); stun_sock->q = NULL; } /* if (stun_sock->stun_sess) { pj_stun_session_destroy(stun_sock->stun_sess); stun_sock->stun_sess = NULL; } */ if (stun_sock->pool) { pj_pool_t *pool = stun_sock->pool; stun_sock->pool = NULL; pj_pool_release(pool); } TRACE_(("", "STUN sock %p destroyed", stun_sock)); } /* Destroy */ PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock) { TRACE_((stun_sock->obj_name, "STUN sock %p request, ref_cnt=%d", stun_sock, pj_grp_lock_get_ref(stun_sock->grp_lock))); pj_grp_lock_acquire(stun_sock->grp_lock); if (stun_sock->is_destroying) { /* Destroy already called */ pj_grp_lock_release(stun_sock->grp_lock); return PJ_EINVALIDOP; } stun_sock->is_destroying = PJ_TRUE; pj_timer_heap_cancel_if_active(stun_sock->stun_cfg.timer_heap, &stun_sock->ka_timer, 0); if (stun_sock->active_sock != NULL) { stun_sock->sock_fd = PJ_INVALID_SOCKET; pj_activesock_close(stun_sock->active_sock); } else if (stun_sock->sock_fd != PJ_INVALID_SOCKET) { pj_sock_close(stun_sock->sock_fd); stun_sock->sock_fd = PJ_INVALID_SOCKET; } if (stun_sock->stun_sess) { pj_stun_session_destroy(stun_sock->stun_sess); } pj_grp_lock_dec_ref(stun_sock->grp_lock); pj_grp_lock_release(stun_sock->grp_lock); return PJ_SUCCESS; } /* Associate user data */ PJ_DEF(pj_status_t) pj_stun_sock_set_user_data( pj_stun_sock *stun_sock, void *user_data) { PJ_ASSERT_RETURN(stun_sock, PJ_EINVAL); stun_sock->user_data = user_data; return PJ_SUCCESS; } /* Get user data */ PJ_DEF(void*) pj_stun_sock_get_user_data(pj_stun_sock *stun_sock) { PJ_ASSERT_RETURN(stun_sock, NULL); return stun_sock->user_data; } /* Get group lock */ PJ_DECL(pj_grp_lock_t *) pj_stun_sock_get_grp_lock(pj_stun_sock *stun_sock) { PJ_ASSERT_RETURN(stun_sock, NULL); return stun_sock->grp_lock; } /* Notify application that session has failed */ static pj_bool_t sess_fail(pj_stun_sock *stun_sock, pj_stun_sock_op op, pj_status_t status) { pj_bool_t ret; PJ_PERROR(4,(stun_sock->obj_name, status, "Session failed because %s failed", pj_stun_sock_op_name(op))); ret = (*stun_sock->cb.on_status)(stun_sock, op, status); return ret; } /* DNS resolver callback */ static void dns_srv_resolver_cb(void *user_data, pj_status_t status, const pj_dns_srv_record *rec) { pj_stun_sock *stun_sock = (pj_stun_sock*) user_data; pj_grp_lock_acquire(stun_sock->grp_lock); /* Clear query */ stun_sock->q = NULL; /* Handle error */ if (status != PJ_SUCCESS) { sess_fail(stun_sock, PJ_STUN_SOCK_DNS_OP, status); pj_grp_lock_release(stun_sock->grp_lock); return; } pj_assert(rec->count); pj_assert(rec->entry[0].server.addr_count); PJ_TODO(SUPPORT_IPV6_IN_RESOLVER); pj_assert(stun_sock->af == pj_AF_INET()); /* Set the address */ pj_sockaddr_in_init(&stun_sock->srv_addr.ipv4, NULL, rec->entry[0].port); stun_sock->srv_addr.ipv4.sin_addr = rec->entry[0].server.addr[0]; /* Start sending Binding request */ get_mapped_addr(stun_sock); pj_grp_lock_release(stun_sock->grp_lock); } /* Start sending STUN Binding request */ static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock) { pj_stun_tx_data *tdata; pj_status_t status; /* Increment request counter and create STUN Binding request */ ++stun_sock->tsx_id[5]; status = pj_stun_session_create_req(stun_sock->stun_sess, PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC, (const pj_uint8_t*)stun_sock->tsx_id, &tdata); if (status != PJ_SUCCESS) goto on_error; /* Send request */ status=pj_stun_session_send_msg(stun_sock->stun_sess, INTERNAL_MSG_TOKEN, PJ_FALSE, PJ_TRUE, &stun_sock->srv_addr, pj_sockaddr_get_len(&stun_sock->srv_addr), tdata); if (status != PJ_SUCCESS && status != PJ_EPENDING) goto on_error; return PJ_SUCCESS; on_error: sess_fail(stun_sock, PJ_STUN_SOCK_BINDING_OP, status); return status; } /* Get info */ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock, pj_stun_sock_info *info) { int addr_len; pj_status_t status; PJ_ASSERT_RETURN(stun_sock && info, PJ_EINVAL); pj_grp_lock_acquire(stun_sock->grp_lock); /* Copy STUN server address and mapped address */ pj_memcpy(&info->srv_addr, &stun_sock->srv_addr, sizeof(pj_sockaddr)); pj_memcpy(&info->mapped_addr, &stun_sock->mapped_addr, sizeof(pj_sockaddr)); /* Retrieve bound address */ addr_len = sizeof(info->bound_addr); status = pj_sock_getsockname(stun_sock->sock_fd, &info->bound_addr, &addr_len); if (status != PJ_SUCCESS) { pj_grp_lock_release(stun_sock->grp_lock); return status; } /* If socket is bound to a specific interface, then only put that * interface in the alias list. Otherwise query all the interfaces * in the host. */ if (pj_sockaddr_has_addr(&info->bound_addr)) { info->alias_cnt = 1; pj_sockaddr_cp(&info->aliases[0], &info->bound_addr); } else { pj_sockaddr def_addr; pj_uint16_t port = pj_sockaddr_get_port(&info->bound_addr); unsigned i; /* Get the default address */ status = pj_gethostip(stun_sock->af, &def_addr); if (status != PJ_SUCCESS) { pj_grp_lock_release(stun_sock->grp_lock); return status; } pj_sockaddr_set_port(&def_addr, port); /* Enum all IP interfaces in the host */ info->alias_cnt = PJ_ARRAY_SIZE(info->aliases); status = pj_enum_ip_interface(stun_sock->af, &info->alias_cnt, info->aliases); if (status != PJ_SUCCESS) { pj_grp_lock_release(stun_sock->grp_lock); return status; } /* Set the port number for each address. */ for (i=0; ialias_cnt; ++i) { pj_sockaddr_set_port(&info->aliases[i], port); } /* Put the default IP in the first slot */ for (i=0; ialias_cnt; ++i) { if (pj_sockaddr_cmp(&info->aliases[i], &def_addr)==0) { if (i!=0) { pj_sockaddr_cp(&info->aliases[i], &info->aliases[0]); pj_sockaddr_cp(&info->aliases[0], &def_addr); } break; } } } pj_grp_lock_release(stun_sock->grp_lock); return PJ_SUCCESS; } /* Send application data */ PJ_DEF(pj_status_t) pj_stun_sock_sendto( pj_stun_sock *stun_sock, pj_ioqueue_op_key_t *send_key, const void *pkt, unsigned pkt_len, unsigned flag, const pj_sockaddr_t *dst_addr, unsigned addr_len) { pj_ssize_t size; pj_status_t status; PJ_ASSERT_RETURN(stun_sock && pkt && dst_addr && addr_len, PJ_EINVAL); pj_grp_lock_acquire(stun_sock->grp_lock); if (!stun_sock->active_sock) { /* We have been shutdown, but this callback may still get called * by retransmit timer. */ pj_grp_lock_release(stun_sock->grp_lock); return PJ_EINVALIDOP; } if (send_key==NULL) send_key = &stun_sock->send_key; size = pkt_len; status = pj_activesock_sendto(stun_sock->active_sock, send_key, pkt, &size, flag, dst_addr, addr_len); pj_grp_lock_release(stun_sock->grp_lock); return status; } /* This callback is called by the STUN session to send packet */ static pj_status_t sess_on_send_msg(pj_stun_session *sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len) { pj_stun_sock *stun_sock; pj_ssize_t size; stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); if (!stun_sock || !stun_sock->active_sock) { /* We have been shutdown, but this callback may still get called * by retransmit timer. */ return PJ_EINVALIDOP; } pj_assert(token==INTERNAL_MSG_TOKEN); PJ_UNUSED_ARG(token); size = pkt_size; return pj_activesock_sendto(stun_sock->active_sock, &stun_sock->int_send_key, pkt, &size, 0, dst_addr, addr_len); } /* This callback is called by the STUN session when outgoing transaction * is complete */ static void sess_on_request_complete(pj_stun_session *sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_sock *stun_sock; const pj_stun_sockaddr_attr *mapped_attr; pj_stun_sock_op op; pj_bool_t mapped_changed; pj_bool_t resched = PJ_TRUE; stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess); if (!stun_sock) return; PJ_UNUSED_ARG(tdata); PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); /* Check if this is a keep-alive or the first Binding request */ if (pj_sockaddr_has_addr(&stun_sock->mapped_addr)) op = PJ_STUN_SOCK_KEEP_ALIVE_OP; else op = PJ_STUN_SOCK_BINDING_OP; /* Handle failure */ if (status != PJ_SUCCESS) { resched = sess_fail(stun_sock, op, status); goto on_return; } /* Get XOR-MAPPED-ADDRESS, or MAPPED-ADDRESS when XOR-MAPPED-ADDRESS * doesn't exist. */ mapped_attr = (const pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0); if (mapped_attr==NULL) { mapped_attr = (const pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0); } if (mapped_attr == NULL) { resched = sess_fail(stun_sock, op, PJNATH_ESTUNNOMAPPEDADDR); goto on_return; } /* Determine if mapped address has changed, and save the new mapped * address and call callback if so */ mapped_changed = !pj_sockaddr_has_addr(&stun_sock->mapped_addr) || pj_sockaddr_cmp(&stun_sock->mapped_addr, &mapped_attr->sockaddr) != 0; if (mapped_changed) { /* Print mapped adress */ { char addrinfo[PJ_INET6_ADDRSTRLEN+10]; PJ_LOG(4,(stun_sock->obj_name, "STUN mapped address found/changed: %s", pj_sockaddr_print(&mapped_attr->sockaddr, addrinfo, sizeof(addrinfo), 3))); } pj_sockaddr_cp(&stun_sock->mapped_addr, &mapped_attr->sockaddr); if (op==PJ_STUN_SOCK_KEEP_ALIVE_OP) op = PJ_STUN_SOCK_MAPPED_ADDR_CHANGE; } /* Notify user */ resched = (*stun_sock->cb.on_status)(stun_sock, op, PJ_SUCCESS); on_return: /* Start/restart keep-alive timer */ if (resched) start_ka_timer(stun_sock); } /* Schedule keep-alive timer */ static void start_ka_timer(pj_stun_sock *stun_sock) { pj_timer_heap_cancel_if_active(stun_sock->stun_cfg.timer_heap, &stun_sock->ka_timer, 0); pj_assert(stun_sock->ka_interval != 0); if (stun_sock->ka_interval > 0 && !stun_sock->is_destroying) { pj_time_val delay; delay.sec = stun_sock->ka_interval; delay.msec = 0; pj_timer_heap_schedule_w_grp_lock(stun_sock->stun_cfg.timer_heap, &stun_sock->ka_timer, &delay, PJ_TRUE, stun_sock->grp_lock); } } /* Keep-alive timer callback */ static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te) { pj_stun_sock *stun_sock; stun_sock = (pj_stun_sock *) te->user_data; PJ_UNUSED_ARG(th); pj_grp_lock_acquire(stun_sock->grp_lock); /* Time to send STUN Binding request */ if (get_mapped_addr(stun_sock) != PJ_SUCCESS) { pj_grp_lock_release(stun_sock->grp_lock); return; } /* Next keep-alive timer will be scheduled once the request * is complete. */ pj_grp_lock_release(stun_sock->grp_lock); } /* Callback from active socket when incoming packet is received */ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, void *data, pj_size_t size, const pj_sockaddr_t *src_addr, int addr_len, pj_status_t status) { pj_stun_sock *stun_sock; pj_stun_msg_hdr *hdr; pj_uint16_t type; stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock); if (!stun_sock) return PJ_FALSE; /* Log socket error */ if (status != PJ_SUCCESS) { PJ_PERROR(2,(stun_sock->obj_name, status, "recvfrom() error")); return PJ_TRUE; } pj_grp_lock_acquire(stun_sock->grp_lock); /* Check that this is STUN message */ status = pj_stun_msg_check((const pj_uint8_t*)data, size, PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET); if (status != PJ_SUCCESS) { /* Not STUN -- give it to application */ goto process_app_data; } /* Treat packet as STUN header and copy the STUN message type. * We don't want to access the type directly from the header * since it may not be properly aligned. */ hdr = (pj_stun_msg_hdr*) data; pj_memcpy(&type, &hdr->type, 2); type = pj_ntohs(type); /* If the packet is a STUN Binding response and part of the * transaction ID matches our internal ID, then this is * our internal STUN message (Binding request or keep alive). * Give it to our STUN session. */ if (!PJ_STUN_IS_RESPONSE(type) || PJ_STUN_GET_METHOD(type) != PJ_STUN_BINDING_METHOD || pj_memcmp(hdr->tsx_id, stun_sock->tsx_id, 10) != 0) { /* Not STUN Binding response, or STUN transaction ID mismatch. * This is not our message too -- give it to application. */ goto process_app_data; } /* This is our STUN Binding response. Give it to the STUN session */ status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, data, size, PJ_STUN_IS_DATAGRAM, NULL, NULL, src_addr, addr_len); status = pj_grp_lock_release(stun_sock->grp_lock); return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; process_app_data: if (stun_sock->cb.on_rx_data) { (*stun_sock->cb.on_rx_data)(stun_sock, data, (unsigned)size, src_addr, addr_len); status = pj_grp_lock_release(stun_sock->grp_lock); return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; } status = pj_grp_lock_release(stun_sock->grp_lock); return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE; } /* Callback from active socket about send status */ static pj_bool_t on_data_sent(pj_activesock_t *asock, pj_ioqueue_op_key_t *send_key, pj_ssize_t sent) { pj_stun_sock *stun_sock; stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock); if (!stun_sock) return PJ_FALSE; /* Don't report to callback if this is internal message */ if (send_key == &stun_sock->int_send_key) { return PJ_TRUE; } /* Report to callback */ if (stun_sock->cb.on_data_sent) { pj_bool_t ret; pj_grp_lock_acquire(stun_sock->grp_lock); /* If app gives NULL send_key in sendto() function, then give * NULL in the callback too */ if (send_key == &stun_sock->send_key) send_key = NULL; /* Call callback */ ret = (*stun_sock->cb.on_data_sent)(stun_sock, send_key, sent); pj_grp_lock_release(stun_sock->grp_lock); return ret; } return PJ_TRUE; } ================================================ FILE: deps/pjsip/pjnath/src/pjnath/stun_transaction.c ================================================ /* $Id: stun_transaction.c 4413 2013-03-05 06:29:15Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #define THIS_FILE "stun_transaction.c" #define TIMER_INACTIVE 0 #define TIMER_ACTIVE 1 struct pj_stun_client_tsx { char obj_name[PJ_MAX_OBJ_NAME]; pj_stun_tsx_cb cb; void *user_data; pj_grp_lock_t *grp_lock; pj_bool_t complete; pj_bool_t require_retransmit; unsigned rto_msec; pj_timer_entry retransmit_timer; unsigned transmit_count; pj_time_val retransmit_time; pj_timer_heap_t *timer_heap; pj_timer_entry destroy_timer; void *last_pkt; unsigned last_pkt_size; }; #if 1 # define TRACE_(expr) PJ_LOG(5,expr) #else # define TRACE_(expr) #endif static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, pj_timer_entry *timer); static void destroy_timer_callback(pj_timer_heap_t *timer_heap, pj_timer_entry *timer); /* * Create a STUN client transaction. */ PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_config *cfg, pj_pool_t *pool, pj_grp_lock_t *grp_lock, const pj_stun_tsx_cb *cb, pj_stun_client_tsx **p_tsx) { pj_stun_client_tsx *tsx; PJ_ASSERT_RETURN(cfg && cb && p_tsx, PJ_EINVAL); PJ_ASSERT_RETURN(cb->on_send_msg, PJ_EINVAL); tsx = PJ_POOL_ZALLOC_T(pool, pj_stun_client_tsx); tsx->rto_msec = cfg->rto_msec; tsx->timer_heap = cfg->timer_heap; tsx->grp_lock = grp_lock; pj_memcpy(&tsx->cb, cb, sizeof(*cb)); tsx->retransmit_timer.cb = &retransmit_timer_callback; tsx->retransmit_timer.user_data = tsx; tsx->destroy_timer.cb = &destroy_timer_callback; tsx->destroy_timer.user_data = tsx; pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), "utsx%p", tsx); *p_tsx = tsx; PJ_LOG(5,(tsx->obj_name, "STUN client transaction created")); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_stun_client_tsx_schedule_destroy( pj_stun_client_tsx *tsx, const pj_time_val *delay) { pj_status_t status; PJ_ASSERT_RETURN(tsx && delay, PJ_EINVAL); PJ_ASSERT_RETURN(tsx->cb.on_destroy, PJ_EINVAL); pj_grp_lock_acquire(tsx->grp_lock); /* Cancel previously registered timer */ pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->destroy_timer, TIMER_INACTIVE); /* Stop retransmission, just in case */ pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer, TIMER_INACTIVE); status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap, &tsx->destroy_timer, delay, TIMER_ACTIVE, tsx->grp_lock); if (status != PJ_SUCCESS) { pj_grp_lock_release(tsx->grp_lock); return status; } tsx->cb.on_complete = NULL; pj_grp_lock_release(tsx->grp_lock); TRACE_((tsx->obj_name, "STUN transaction %p schedule destroy", tsx)); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_stun_client_tsx_destroy(pj_stun_client_tsx *tsx) { /* * Currently tsx has no objects to destroy so we don't need to do anything * here. */ /* pj_stun_client_tsx_stop(tsx); */ PJ_UNUSED_ARG(tsx); return PJ_SUCCESS; } /* * Destroy transaction immediately. */ PJ_DEF(pj_status_t) pj_stun_client_tsx_stop(pj_stun_client_tsx *tsx) { PJ_ASSERT_RETURN(tsx, PJ_EINVAL); /* Don't call grp_lock_acquire() because we might be called on * group lock's destructor. */ pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer, TIMER_INACTIVE); pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->destroy_timer, TIMER_INACTIVE); PJ_LOG(5,(tsx->obj_name, "STUN client transaction %p stopped, ref_cnt=%d", tsx, pj_grp_lock_get_ref(tsx->grp_lock))); return PJ_SUCCESS; } /* * Check if transaction has completed. */ PJ_DEF(pj_bool_t) pj_stun_client_tsx_is_complete(pj_stun_client_tsx *tsx) { PJ_ASSERT_RETURN(tsx, PJ_FALSE); return tsx->complete; } /* * Set user data. */ PJ_DEF(pj_status_t) pj_stun_client_tsx_set_data(pj_stun_client_tsx *tsx, void *data) { PJ_ASSERT_RETURN(tsx, PJ_EINVAL); tsx->user_data = data; return PJ_SUCCESS; } /* * Get the user data */ PJ_DEF(void*) pj_stun_client_tsx_get_data(pj_stun_client_tsx *tsx) { PJ_ASSERT_RETURN(tsx, NULL); return tsx->user_data; } /* * Transmit message. */ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx, pj_bool_t mod_count) { pj_status_t status; PJ_ASSERT_RETURN(tsx->retransmit_timer.id == TIMER_INACTIVE || !tsx->require_retransmit || !mod_count, PJ_EBUSY); if (tsx->require_retransmit && mod_count) { /* Calculate retransmit/timeout delay */ if (tsx->transmit_count == 0) { tsx->retransmit_time.sec = 0; tsx->retransmit_time.msec = tsx->rto_msec; } else if (tsx->transmit_count < PJ_STUN_MAX_TRANSMIT_COUNT-1) { unsigned msec; msec = PJ_TIME_VAL_MSEC(tsx->retransmit_time); msec <<= 1; tsx->retransmit_time.sec = msec / 1000; tsx->retransmit_time.msec = msec % 1000; } else { tsx->retransmit_time.sec = PJ_STUN_TIMEOUT_VALUE / 1000; tsx->retransmit_time.msec = PJ_STUN_TIMEOUT_VALUE % 1000; } /* Schedule timer first because when send_msg() failed we can * cancel it (as opposed to when schedule_timer() failed we cannot * cancel transmission). */; status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap, &tsx->retransmit_timer, &tsx->retransmit_time, TIMER_ACTIVE, tsx->grp_lock); if (status != PJ_SUCCESS) { tsx->retransmit_timer.id = TIMER_INACTIVE; return status; } } if (mod_count) tsx->transmit_count++; PJ_LOG(5,(tsx->obj_name, "STUN sending message (transmit count=%d)", tsx->transmit_count)); pj_log_push_indent(); /* Send message */ status = tsx->cb.on_send_msg(tsx, tsx->last_pkt, tsx->last_pkt_size); if (status == PJNATH_ESTUNDESTROYED) { /* We've been destroyed, don't access the object. */ } else if (status != PJ_SUCCESS) { if (mod_count || status == PJ_EINVALIDOP) { pj_timer_heap_cancel_if_active( tsx->timer_heap, &tsx->retransmit_timer, TIMER_INACTIVE); } PJ_PERROR(4, (tsx->obj_name, status, "STUN error sending message")); } pj_log_pop_indent(); return status; } /* * Send outgoing message and start STUN transaction. */ PJ_DEF(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx, pj_bool_t retransmit, void *pkt, unsigned pkt_len) { pj_status_t status; PJ_ASSERT_RETURN(tsx && pkt && pkt_len, PJ_EINVAL); PJ_ASSERT_RETURN(tsx->retransmit_timer.id == 0, PJ_EBUSY); pj_grp_lock_acquire(tsx->grp_lock); /* Encode message */ tsx->last_pkt = pkt; tsx->last_pkt_size = pkt_len; /* Update STUN retransmit flag */ tsx->require_retransmit = retransmit; /* For TCP, schedule timeout timer after PJ_STUN_TIMEOUT_VALUE. * Since we don't have timeout timer, simulate this by using * retransmit timer. */ if (!retransmit) { unsigned timeout; pj_assert(tsx->retransmit_timer.id == 0); tsx->transmit_count = PJ_STUN_MAX_TRANSMIT_COUNT; timeout = tsx->rto_msec * 16; tsx->retransmit_time.sec = timeout / 1000; tsx->retransmit_time.msec = timeout % 1000; /* Schedule timer first because when send_msg() failed we can * cancel it (as opposed to when schedule_timer() failed we cannot * cancel transmission). */; status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap, &tsx->retransmit_timer, &tsx->retransmit_time, TIMER_ACTIVE, tsx->grp_lock); if (status != PJ_SUCCESS) { tsx->retransmit_timer.id = TIMER_INACTIVE; pj_grp_lock_release(tsx->grp_lock); return status; } } /* Send the message */ status = tsx_transmit_msg(tsx, PJ_TRUE); if (status != PJ_SUCCESS) { pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer, TIMER_INACTIVE); pj_grp_lock_release(tsx->grp_lock); return status; } pj_grp_lock_release(tsx->grp_lock); return PJ_SUCCESS; } /* Retransmit timer callback */ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, pj_timer_entry *timer) { pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data; pj_status_t status; PJ_UNUSED_ARG(timer_heap); pj_grp_lock_acquire(tsx->grp_lock); if (tsx->transmit_count >= PJ_STUN_MAX_TRANSMIT_COUNT) { /* tsx may be destroyed when calling the callback below */ pj_grp_lock_t *grp_lock = tsx->grp_lock; /* Retransmission count exceeded. Transaction has failed */ tsx->retransmit_timer.id = 0; PJ_LOG(4,(tsx->obj_name, "STUN timeout waiting for response")); pj_log_push_indent(); if (!tsx->complete) { tsx->complete = PJ_TRUE; if (tsx->cb.on_complete) { tsx->cb.on_complete(tsx, PJNATH_ESTUNTIMEDOUT, NULL, NULL, 0); } } pj_grp_lock_release(grp_lock); /* We might have been destroyed, don't try to access the object */ pj_log_pop_indent(); return; } tsx->retransmit_timer.id = 0; status = tsx_transmit_msg(tsx, PJ_TRUE); if (status != PJ_SUCCESS) { tsx->retransmit_timer.id = 0; if (!tsx->complete) { tsx->complete = PJ_TRUE; if (tsx->cb.on_complete) { tsx->cb.on_complete(tsx, status, NULL, NULL, 0); } } } pj_grp_lock_release(tsx->grp_lock); /* We might have been destroyed, don't try to access the object */ } /* * Request to retransmit the request. */ PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx, pj_bool_t mod_count) { if (tsx->destroy_timer.id != 0) { return PJ_SUCCESS; } if (mod_count) { pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer, TIMER_INACTIVE); } return tsx_transmit_msg(tsx, mod_count); } /* Timer callback to destroy transaction */ static void destroy_timer_callback(pj_timer_heap_t *timer_heap, pj_timer_entry *timer) { pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data; PJ_UNUSED_ARG(timer_heap); tsx->destroy_timer.id = PJ_FALSE; tsx->cb.on_destroy(tsx); /* Don't access transaction after this */ } /* * Notify the STUN transaction about the arrival of STUN response. */ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, const pj_stun_msg *msg, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_errcode_attr *err_attr; pj_status_t status; /* Must be STUN response message */ if (!PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) && !PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) { PJ_LOG(4,(tsx->obj_name, "STUN rx_msg() error: not response message")); return PJNATH_EINSTUNMSGTYPE; } /* We have a response with matching transaction ID. * We can cancel retransmit timer now. */ pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer, TIMER_INACTIVE); /* Find STUN error code attribute */ err_attr = (pj_stun_errcode_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ERROR_CODE, 0); if (err_attr && err_attr->err_code <= 200) { /* draft-ietf-behave-rfc3489bis-05.txt Section 8.3.2: * Any response between 100 and 299 MUST result in the cessation * of request retransmissions, but otherwise is discarded. */ PJ_LOG(4,(tsx->obj_name, "STUN rx_msg() error: received provisional %d code (%.*s)", err_attr->err_code, (int)err_attr->reason.slen, err_attr->reason.ptr)); return PJ_SUCCESS; } if (err_attr == NULL) { status = PJ_SUCCESS; } else { status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code); } /* Call callback */ if (!tsx->complete) { tsx->complete = PJ_TRUE; if (tsx->cb.on_complete) { tsx->cb.on_complete(tsx, status, msg, src_addr, src_addr_len); } /* We might have been destroyed, don't try to access the object */ } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjnath/src/pjnath/turn_session.c ================================================ /* $Id: turn_session.c 4368 2013-02-21 21:53:28Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define PJ_TURN_CHANNEL_MIN 0x4000 #define PJ_TURN_CHANNEL_MAX 0x7FFF /* inclusive */ #define PJ_TURN_CHANNEL_HTABLE_SIZE 8 #define PJ_TURN_PERM_HTABLE_SIZE 8 static const char *state_names[] = { "Null", "Resolving", "Resolved", "Allocating", "Ready", "Deallocating", "Deallocated", "Destroying" }; enum timer_id_t { TIMER_NONE, TIMER_KEEP_ALIVE, TIMER_DESTROY }; /* This structure describes a channel binding. A channel binding is index by * the channel number or IP address and port number of the peer. */ struct ch_t { /* The channel number */ pj_uint16_t num; /* PJ_TRUE if we've received successful response to ChannelBind request * for this channel. */ pj_bool_t bound; /* The peer IP address and port */ pj_sockaddr addr; /* The channel binding expiration */ pj_time_val expiry; }; /* This structure describes a permission. A permission is identified by the * IP address only. */ struct perm_t { /* Cache of hash value to speed-up lookup */ pj_uint32_t hval; /* The permission IP address. The port number MUST be zero */ pj_sockaddr addr; /* Number of peers that uses this permission. */ unsigned peer_cnt; /* Automatically renew this permission once it expires? */ pj_bool_t renew; /* The permission expiration */ pj_time_val expiry; /* Arbitrary/random pointer value (token) to map this perm with the * request to create it. It is used to invalidate this perm when the * request fails. */ void *req_token; }; /* The TURN client session structure */ struct pj_turn_session { pj_pool_t *pool; const char *obj_name; pj_turn_session_cb cb; void *user_data; pj_stun_config stun_cfg; pj_bool_t is_destroying; pj_grp_lock_t *grp_lock; int busy; pj_turn_state_t state; pj_status_t last_status; pj_bool_t pending_destroy; pj_stun_session *stun; unsigned lifetime; int ka_interval; pj_time_val expiry; pj_timer_heap_t *timer_heap; pj_timer_entry timer; pj_uint16_t default_port; pj_uint16_t af; pj_turn_tp_type conn_type; pj_uint16_t srv_addr_cnt; pj_sockaddr *srv_addr_list; pj_sockaddr *srv_addr; pj_bool_t pending_alloc; pj_turn_alloc_param alloc_param; pj_sockaddr mapped_addr; pj_sockaddr relay_addr; pj_hash_table_t *ch_table; pj_hash_table_t *perm_table; pj_uint32_t send_ind_tsx_id[3]; /* tx_pkt must be 16bit aligned */ pj_uint8_t tx_pkt[PJ_TURN_MAX_PKT_LEN]; pj_uint16_t next_ch; }; /* * Prototypes. */ static void sess_shutdown(pj_turn_session *sess, pj_status_t status); static void turn_sess_on_destroy(void *comp); static void do_destroy(pj_turn_session *sess); static void send_refresh(pj_turn_session *sess, int lifetime); static pj_status_t stun_on_send_msg(pj_stun_session *sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len); static void stun_on_request_complete(pj_stun_session *sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len); static pj_status_t stun_on_rx_indication(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_msg *msg, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len); static void dns_srv_resolver_cb(void *user_data, pj_status_t status, const pj_dns_srv_record *rec); static struct ch_t *lookup_ch_by_addr(pj_turn_session *sess, const pj_sockaddr_t *addr, unsigned addr_len, pj_bool_t update, pj_bool_t bind_channel); static struct ch_t *lookup_ch_by_chnum(pj_turn_session *sess, pj_uint16_t chnum); static struct perm_t *lookup_perm(pj_turn_session *sess, const pj_sockaddr_t *addr, unsigned addr_len, pj_bool_t update); static void invalidate_perm(pj_turn_session *sess, struct perm_t *perm); static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e); /* * Create default pj_turn_alloc_param. */ PJ_DEF(void) pj_turn_alloc_param_default(pj_turn_alloc_param *prm) { pj_bzero(prm, sizeof(*prm)); } /* * Duplicate pj_turn_alloc_param. */ PJ_DEF(void) pj_turn_alloc_param_copy( pj_pool_t *pool, pj_turn_alloc_param *dst, const pj_turn_alloc_param *src) { PJ_UNUSED_ARG(pool); pj_memcpy(dst, src, sizeof(*dst)); } /* * Get TURN state name. */ PJ_DEF(const char*) pj_turn_state_name(pj_turn_state_t state) { return state_names[state]; } /* * Create TURN client session. */ PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg, const char *name, int af, pj_turn_tp_type conn_type, pj_grp_lock_t *grp_lock, const pj_turn_session_cb *cb, unsigned options, void *user_data, pj_turn_session **p_sess) { pj_pool_t *pool; pj_turn_session *sess; pj_stun_session_cb stun_cb; pj_status_t status; PJ_ASSERT_RETURN(cfg && cfg->pf && cb && p_sess, PJ_EINVAL); PJ_ASSERT_RETURN(cb->on_send_pkt, PJ_EINVAL); PJ_UNUSED_ARG(options); if (name == NULL) name = "turn%p"; /* Allocate and create TURN session */ pool = pj_pool_create(cfg->pf, name, PJNATH_POOL_LEN_TURN_SESS, PJNATH_POOL_INC_TURN_SESS, NULL); sess = PJ_POOL_ZALLOC_T(pool, pj_turn_session); sess->pool = pool; sess->obj_name = pool->obj_name; sess->timer_heap = cfg->timer_heap; sess->af = (pj_uint16_t)af; sess->conn_type = conn_type; sess->ka_interval = PJ_TURN_KEEP_ALIVE_SEC; sess->user_data = user_data; sess->next_ch = PJ_TURN_CHANNEL_MIN; /* Copy STUN session */ pj_memcpy(&sess->stun_cfg, cfg, sizeof(pj_stun_config)); /* Copy callback */ pj_memcpy(&sess->cb, cb, sizeof(*cb)); /* Peer hash table */ sess->ch_table = pj_hash_create(pool, PJ_TURN_CHANNEL_HTABLE_SIZE); /* Permission hash table */ sess->perm_table = pj_hash_create(pool, PJ_TURN_PERM_HTABLE_SIZE); /* Session lock */ if (grp_lock) { sess->grp_lock = grp_lock; } else { status = pj_grp_lock_create(pool, NULL, &sess->grp_lock); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } } pj_grp_lock_add_ref(sess->grp_lock); pj_grp_lock_add_handler(sess->grp_lock, pool, sess, &turn_sess_on_destroy); /* Timer */ pj_timer_entry_init(&sess->timer, TIMER_NONE, sess, &on_timer_event); /* Create STUN session */ pj_bzero(&stun_cb, sizeof(stun_cb)); stun_cb.on_send_msg = &stun_on_send_msg; stun_cb.on_request_complete = &stun_on_request_complete; stun_cb.on_rx_indication = &stun_on_rx_indication; status = pj_stun_session_create(&sess->stun_cfg, sess->obj_name, &stun_cb, PJ_FALSE, sess->grp_lock, &sess->stun); if (status != PJ_SUCCESS) { do_destroy(sess); return status; } /* Attach ourself to STUN session */ pj_stun_session_set_user_data(sess->stun, sess); /* Done */ PJ_LOG(4,(sess->obj_name, "TURN client session created")); *p_sess = sess; return PJ_SUCCESS; } static void turn_sess_on_destroy(void *comp) { pj_turn_session *sess = (pj_turn_session*) comp; /* Destroy pool */ if (sess->pool) { pj_pool_t *pool = sess->pool; PJ_LOG(4,(sess->obj_name, "TURN client session destroyed")); sess->pool = NULL; pj_pool_release(pool); } } /* Destroy */ static void do_destroy(pj_turn_session *sess) { PJ_LOG(4,(sess->obj_name, "TURN session destroy request, ref_cnt=%d", pj_grp_lock_get_ref(sess->grp_lock))); pj_grp_lock_acquire(sess->grp_lock); if (sess->is_destroying) { pj_grp_lock_release(sess->grp_lock); return; } sess->is_destroying = PJ_TRUE; pj_timer_heap_cancel_if_active(sess->timer_heap, &sess->timer, TIMER_NONE); pj_stun_session_destroy(sess->stun); pj_grp_lock_dec_ref(sess->grp_lock); pj_grp_lock_release(sess->grp_lock); } /* Set session state */ static void set_state(pj_turn_session *sess, enum pj_turn_state_t state) { pj_turn_state_t old_state = sess->state; if (state==sess->state) return; PJ_LOG(4,(sess->obj_name, "State changed %s --> %s", state_names[old_state], state_names[state])); sess->state = state; if (sess->cb.on_state) { (*sess->cb.on_state)(sess, old_state, state); } } /* * Notify application and shutdown the TURN session. */ static void sess_shutdown(pj_turn_session *sess, pj_status_t status) { pj_bool_t can_destroy = PJ_TRUE; PJ_LOG(4,(sess->obj_name, "Request to shutdown in state %s, cause:%d", state_names[sess->state], status)); if (sess->last_status == PJ_SUCCESS && status != PJ_SUCCESS) sess->last_status = status; switch (sess->state) { case PJ_TURN_STATE_NULL: break; case PJ_TURN_STATE_RESOLVING: /* Wait for DNS callback invoked, it will call the this function * again. If the callback happens to get pending_destroy==FALSE, * the TURN allocation will call this function again. */ sess->pending_destroy = PJ_TRUE; can_destroy = PJ_FALSE; break; case PJ_TURN_STATE_RESOLVED: break; case PJ_TURN_STATE_ALLOCATING: /* We need to wait until allocation complete */ sess->pending_destroy = PJ_TRUE; can_destroy = PJ_FALSE; break; case PJ_TURN_STATE_READY: /* Send REFRESH with LIFETIME=0 */ can_destroy = PJ_FALSE; send_refresh(sess, 0); break; case PJ_TURN_STATE_DEALLOCATING: can_destroy = PJ_FALSE; /* This may recursively call this function again with * state==PJ_TURN_STATE_DEALLOCATED. */ /* No need to deallocate as we're already deallocating! * See https://trac.pjsip.org/repos/ticket/1551 send_refresh(sess, 0); */ break; case PJ_TURN_STATE_DEALLOCATED: case PJ_TURN_STATE_DESTROYING: break; } if (can_destroy) { /* Schedule destroy */ pj_time_val delay = {0, 0}; set_state(sess, PJ_TURN_STATE_DESTROYING); pj_timer_heap_cancel_if_active(sess->timer_heap, &sess->timer, TIMER_NONE); pj_timer_heap_schedule_w_grp_lock(sess->timer_heap, &sess->timer, &delay, TIMER_DESTROY, sess->grp_lock); } } /* * Public API to destroy TURN client session. */ PJ_DEF(pj_status_t) pj_turn_session_shutdown(pj_turn_session *sess) { PJ_ASSERT_RETURN(sess, PJ_EINVAL); pj_grp_lock_acquire(sess->grp_lock); sess_shutdown(sess, PJ_SUCCESS); pj_grp_lock_release(sess->grp_lock); return PJ_SUCCESS; } /** * Forcefully destroy the TURN session. */ PJ_DEF(pj_status_t) pj_turn_session_destroy( pj_turn_session *sess, pj_status_t last_err) { PJ_ASSERT_RETURN(sess, PJ_EINVAL); if (last_err != PJ_SUCCESS && sess->last_status == PJ_SUCCESS) sess->last_status = last_err; set_state(sess, PJ_TURN_STATE_DEALLOCATED); sess_shutdown(sess, PJ_SUCCESS); return PJ_SUCCESS; } /* * Get TURN session info. */ PJ_DEF(pj_status_t) pj_turn_session_get_info( pj_turn_session *sess, pj_turn_session_info *info) { pj_time_val now; PJ_ASSERT_RETURN(sess && info, PJ_EINVAL); pj_gettimeofday(&now); info->state = sess->state; info->conn_type = sess->conn_type; info->lifetime = sess->expiry.sec - now.sec; info->last_status = sess->last_status; if (sess->srv_addr) pj_memcpy(&info->server, sess->srv_addr, sizeof(info->server)); else pj_bzero(&info->server, sizeof(info->server)); pj_memcpy(&info->mapped_addr, &sess->mapped_addr, sizeof(sess->mapped_addr)); pj_memcpy(&info->relay_addr, &sess->relay_addr, sizeof(sess->relay_addr)); return PJ_SUCCESS; } /* * Re-assign user data. */ PJ_DEF(pj_status_t) pj_turn_session_set_user_data( pj_turn_session *sess, void *user_data) { sess->user_data = user_data; return PJ_SUCCESS; } /** * Retrieve user data. */ PJ_DEF(void*) pj_turn_session_get_user_data(pj_turn_session *sess) { return sess->user_data; } /** * Get group lock. */ PJ_DEF(pj_grp_lock_t *) pj_turn_session_get_grp_lock(pj_turn_session *sess) { PJ_ASSERT_RETURN(sess, NULL); return sess->grp_lock; } /* * Configure message logging. By default all flags are enabled. * * @param sess The TURN client session. * @param flags Bitmask combination of #pj_stun_sess_msg_log_flag */ PJ_DEF(void) pj_turn_session_set_log( pj_turn_session *sess, unsigned flags) { pj_stun_session_set_log(sess->stun, flags); } /* * Set software name */ PJ_DEF(pj_status_t) pj_turn_session_set_software_name( pj_turn_session *sess, const pj_str_t *sw) { pj_status_t status; pj_grp_lock_acquire(sess->grp_lock); status = pj_stun_session_set_software_name(sess->stun, sw); pj_grp_lock_release(sess->grp_lock); return status; } /** * Set the server or domain name of the server. */ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess, const pj_str_t *domain, int default_port, pj_dns_resolver *resolver) { pj_sockaddr tmp_addr; pj_bool_t is_ip_addr; pj_status_t status; PJ_ASSERT_RETURN(sess && domain, PJ_EINVAL); PJ_ASSERT_RETURN(sess->state == PJ_TURN_STATE_NULL, PJ_EINVALIDOP); pj_grp_lock_acquire(sess->grp_lock); /* See if "domain" contains just IP address */ tmp_addr.addr.sa_family = sess->af; status = pj_inet_pton(sess->af, domain, pj_sockaddr_get_addr(&tmp_addr)); is_ip_addr = (status == PJ_SUCCESS); if (!is_ip_addr && resolver) { /* Resolve with DNS SRV resolution, and fallback to DNS A resolution * if default_port is specified. */ unsigned opt = 0; pj_str_t res_name; switch (sess->conn_type) { case PJ_TURN_TP_UDP: res_name = pj_str("_turn._udp."); break; case PJ_TURN_TP_TCP: res_name = pj_str("_turn._tcp."); break; case PJ_TURN_TP_TLS: res_name = pj_str("_turns._tcp."); break; default: status = PJNATH_ETURNINTP; goto on_return; } /* Fallback to DNS A only if default port is specified */ if (default_port>0 && default_port<65536) { opt = PJ_DNS_SRV_FALLBACK_A; sess->default_port = (pj_uint16_t)default_port; } PJ_LOG(5,(sess->obj_name, "Resolving %.*s%.*s with DNS SRV", (int)res_name.slen, res_name.ptr, (int)domain->slen, domain->ptr)); set_state(sess, PJ_TURN_STATE_RESOLVING); /* User may have destroyed us in the callback */ if (sess->state != PJ_TURN_STATE_RESOLVING) { status = PJ_ECANCELLED; goto on_return; } status = pj_dns_srv_resolve(domain, &res_name, default_port, sess->pool, resolver, opt, sess, &dns_srv_resolver_cb, NULL); if (status != PJ_SUCCESS) { set_state(sess, PJ_TURN_STATE_NULL); goto on_return; } } else { /* Resolver is not specified, resolve with standard gethostbyname(). * The default_port MUST be specified in this case. */ pj_addrinfo *ai; unsigned i, cnt; /* Default port must be specified */ PJ_ASSERT_RETURN(default_port>0 && default_port<65536, PJ_EINVAL); sess->default_port = (pj_uint16_t)default_port; cnt = PJ_TURN_MAX_DNS_SRV_CNT; ai = (pj_addrinfo*) pj_pool_calloc(sess->pool, cnt, sizeof(pj_addrinfo)); PJ_LOG(5,(sess->obj_name, "Resolving %.*s with DNS A", (int)domain->slen, domain->ptr)); set_state(sess, PJ_TURN_STATE_RESOLVING); /* User may have destroyed us in the callback */ if (sess->state != PJ_TURN_STATE_RESOLVING) { status = PJ_ECANCELLED; goto on_return; } status = pj_getaddrinfo(sess->af, domain, &cnt, ai); if (status != PJ_SUCCESS) goto on_return; sess->srv_addr_cnt = (pj_uint16_t)cnt; sess->srv_addr_list = (pj_sockaddr*) pj_pool_calloc(sess->pool, cnt, sizeof(pj_sockaddr)); for (i=0; isrv_addr_list[i]; pj_memcpy(addr, &ai[i].ai_addr, sizeof(pj_sockaddr)); addr->addr.sa_family = sess->af; addr->ipv4.sin_port = pj_htons(sess->default_port); } sess->srv_addr = &sess->srv_addr_list[0]; set_state(sess, PJ_TURN_STATE_RESOLVED); } on_return: pj_grp_lock_release(sess->grp_lock); return status; } /** * Set credential to be used by the session. */ PJ_DEF(pj_status_t) pj_turn_session_set_credential(pj_turn_session *sess, const pj_stun_auth_cred *cred) { PJ_ASSERT_RETURN(sess && cred, PJ_EINVAL); PJ_ASSERT_RETURN(sess->stun, PJ_EINVALIDOP); pj_grp_lock_acquire(sess->grp_lock); pj_stun_session_set_credential(sess->stun, PJ_STUN_AUTH_LONG_TERM, cred); pj_grp_lock_release(sess->grp_lock); return PJ_SUCCESS; } /** * Create TURN allocation. */ PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess, const pj_turn_alloc_param *param) { pj_stun_tx_data *tdata; pj_bool_t retransmit; pj_status_t status; PJ_ASSERT_RETURN(sess, PJ_EINVAL); PJ_ASSERT_RETURN(sess->state>PJ_TURN_STATE_NULL && sess->state<=PJ_TURN_STATE_RESOLVED, PJ_EINVALIDOP); pj_grp_lock_acquire(sess->grp_lock); if (param && param != &sess->alloc_param) pj_turn_alloc_param_copy(sess->pool, &sess->alloc_param, param); if (sess->state < PJ_TURN_STATE_RESOLVED) { sess->pending_alloc = PJ_TRUE; PJ_LOG(4,(sess->obj_name, "Pending ALLOCATE in state %s", state_names[sess->state])); pj_grp_lock_release(sess->grp_lock); return PJ_SUCCESS; } /* Ready to allocate */ pj_assert(sess->state == PJ_TURN_STATE_RESOLVED); /* Create a bare request */ status = pj_stun_session_create_req(sess->stun, PJ_STUN_ALLOCATE_REQUEST, PJ_STUN_MAGIC, NULL, &tdata); if (status != PJ_SUCCESS) { pj_grp_lock_release(sess->grp_lock); return status; } /* MUST include REQUESTED-TRANSPORT attribute */ pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_REQ_TRANSPORT, PJ_STUN_SET_RT_PROTO(PJ_TURN_TP_UDP)); /* Include BANDWIDTH if requested */ if (sess->alloc_param.bandwidth > 0) { pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_BANDWIDTH, sess->alloc_param.bandwidth); } /* Include LIFETIME if requested */ if (sess->alloc_param.lifetime > 0) { pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_LIFETIME, sess->alloc_param.lifetime); } /* Server address must be set */ pj_assert(sess->srv_addr != NULL); /* Send request */ set_state(sess, PJ_TURN_STATE_ALLOCATING); retransmit = (sess->conn_type == PJ_TURN_TP_UDP); status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, retransmit, sess->srv_addr, pj_sockaddr_get_len(sess->srv_addr), tdata); if (status != PJ_SUCCESS) { /* Set state back to RESOLVED. We don't want to destroy session now, * let the application do it if it wants to. */ set_state(sess, PJ_TURN_STATE_RESOLVED); } pj_grp_lock_release(sess->grp_lock); return status; } /* * Install or renew permissions */ PJ_DEF(pj_status_t) pj_turn_session_set_perm( pj_turn_session *sess, unsigned addr_cnt, const pj_sockaddr addr[], unsigned options) { pj_stun_tx_data *tdata; pj_hash_iterator_t it_buf, *it; void *req_token; unsigned i, attr_added=0; pj_status_t status; PJ_ASSERT_RETURN(sess && addr_cnt && addr, PJ_EINVAL); pj_grp_lock_acquire(sess->grp_lock); /* Create a bare CreatePermission request */ status = pj_stun_session_create_req(sess->stun, PJ_STUN_CREATE_PERM_REQUEST, PJ_STUN_MAGIC, NULL, &tdata); if (status != PJ_SUCCESS) { pj_grp_lock_release(sess->grp_lock); return status; } /* Create request token to map the request to the perm structures * which the request belongs. */ req_token = (void*)(pj_ssize_t)pj_rand(); /* Process the addresses */ for (i=0; irenew = (options & 0x01); /* Only add to the request if the request doesn't contain this * address yet. */ if (perm->req_token != req_token) { perm->req_token = req_token; /* Add XOR-PEER-ADDRESS */ status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE, &addr[i], sizeof(addr[i])); if (status != PJ_SUCCESS) goto on_error; ++attr_added; } } pj_assert(attr_added != 0); /* Send the request */ status = pj_stun_session_send_msg(sess->stun, req_token, PJ_FALSE, (sess->conn_type==PJ_TURN_TP_UDP), sess->srv_addr, pj_sockaddr_get_len(sess->srv_addr), tdata); if (status != PJ_SUCCESS) { /* tdata is already destroyed */ tdata = NULL; goto on_error; } pj_grp_lock_release(sess->grp_lock); return PJ_SUCCESS; on_error: /* destroy tdata */ if (tdata) { pj_stun_msg_destroy_tdata(sess->stun, tdata); } /* invalidate perm structures associated with this request */ it = pj_hash_first(sess->perm_table, &it_buf); while (it) { struct perm_t *perm = (struct perm_t*) pj_hash_this(sess->perm_table, it); it = pj_hash_next(sess->perm_table, it); if (perm->req_token == req_token) invalidate_perm(sess, perm); } pj_grp_lock_release(sess->grp_lock); return status; } /* * Send REFRESH */ static void send_refresh(pj_turn_session *sess, int lifetime) { pj_stun_tx_data *tdata; pj_status_t status; PJ_ASSERT_ON_FAIL(sess->state==PJ_TURN_STATE_READY, return); /* Create a bare REFRESH request */ status = pj_stun_session_create_req(sess->stun, PJ_STUN_REFRESH_REQUEST, PJ_STUN_MAGIC, NULL, &tdata); if (status != PJ_SUCCESS) goto on_error; /* Add LIFETIME */ if (lifetime >= 0) { pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_LIFETIME, lifetime); } /* Send request */ if (lifetime == 0) { set_state(sess, PJ_TURN_STATE_DEALLOCATING); } status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, (sess->conn_type==PJ_TURN_TP_UDP), sess->srv_addr, pj_sockaddr_get_len(sess->srv_addr), tdata); if (status != PJ_SUCCESS) goto on_error; return; on_error: if (lifetime == 0) { set_state(sess, PJ_TURN_STATE_DEALLOCATED); sess_shutdown(sess, status); } } /** * Relay data to the specified peer through the session. */ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_sockaddr_t *addr, unsigned addr_len) { struct ch_t *ch; struct perm_t *perm; pj_status_t status; PJ_ASSERT_RETURN(sess && pkt && pkt_len && addr && addr_len, PJ_EINVAL); /* Return error if we're not ready */ if (sess->state != PJ_TURN_STATE_READY) { return PJ_EIGNORED; } /* Lock session now */ pj_grp_lock_acquire(sess->grp_lock); /* Lookup permission first */ perm = lookup_perm(sess, addr, pj_sockaddr_get_len(addr), PJ_FALSE); if (perm == NULL) { /* Permission doesn't exist, install it first */ char ipstr[PJ_INET6_ADDRSTRLEN+2]; PJ_LOG(4,(sess->obj_name, "sendto(): IP %s has no permission, requesting it first..", pj_sockaddr_print(addr, ipstr, sizeof(ipstr), 2))); status = pj_turn_session_set_perm(sess, 1, (const pj_sockaddr*)addr, 0); if (status != PJ_SUCCESS) { pj_grp_lock_release(sess->grp_lock); return status; } } /* See if the peer is bound to a channel number */ ch = lookup_ch_by_addr(sess, addr, pj_sockaddr_get_len(addr), PJ_FALSE, PJ_FALSE); if (ch && ch->num != PJ_TURN_INVALID_CHANNEL && ch->bound) { unsigned total_len; /* Peer is assigned a channel number, we can use ChannelData */ pj_turn_channel_data *cd = (pj_turn_channel_data*)sess->tx_pkt; pj_assert(sizeof(*cd)==4); /* Calculate total length, including paddings */ total_len = (pkt_len + sizeof(*cd) + 3) & (~3); if (total_len > sizeof(sess->tx_pkt)) { status = PJ_ETOOBIG; goto on_return; } cd->ch_number = pj_htons((pj_uint16_t)ch->num); cd->length = pj_htons((pj_uint16_t)pkt_len); pj_memcpy(cd+1, pkt, pkt_len); pj_assert(sess->srv_addr != NULL); status = sess->cb.on_send_pkt(sess, sess->tx_pkt, total_len, sess->srv_addr, pj_sockaddr_get_len(sess->srv_addr)); } else { /* Use Send Indication. */ pj_stun_sockaddr_attr peer_attr; pj_stun_binary_attr data_attr; pj_stun_msg send_ind; pj_size_t send_ind_len; /* Increment counter */ ++sess->send_ind_tsx_id[2]; /* Create blank SEND-INDICATION */ status = pj_stun_msg_init(&send_ind, PJ_STUN_SEND_INDICATION, PJ_STUN_MAGIC, (const pj_uint8_t*)sess->send_ind_tsx_id); if (status != PJ_SUCCESS) goto on_return; /* Add XOR-PEER-ADDRESS */ pj_stun_sockaddr_attr_init(&peer_attr, PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE, addr, addr_len); pj_stun_msg_add_attr(&send_ind, (pj_stun_attr_hdr*)&peer_attr); /* Add DATA attribute */ pj_stun_binary_attr_init(&data_attr, NULL, PJ_STUN_ATTR_DATA, NULL, 0); data_attr.data = (pj_uint8_t*)pkt; data_attr.length = pkt_len; pj_stun_msg_add_attr(&send_ind, (pj_stun_attr_hdr*)&data_attr); /* Encode the message */ status = pj_stun_msg_encode(&send_ind, sess->tx_pkt, sizeof(sess->tx_pkt), 0, NULL, &send_ind_len); if (status != PJ_SUCCESS) goto on_return; /* Send the Send Indication */ status = sess->cb.on_send_pkt(sess, sess->tx_pkt, (unsigned)send_ind_len, sess->srv_addr, pj_sockaddr_get_len(sess->srv_addr)); } on_return: pj_grp_lock_release(sess->grp_lock); return status; } /** * Bind a peer address to a channel number. */ PJ_DEF(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess, const pj_sockaddr_t *peer_adr, unsigned addr_len) { struct ch_t *ch; pj_stun_tx_data *tdata; pj_uint16_t ch_num; pj_status_t status; PJ_ASSERT_RETURN(sess && peer_adr && addr_len, PJ_EINVAL); PJ_ASSERT_RETURN(sess->state == PJ_TURN_STATE_READY, PJ_EINVALIDOP); pj_grp_lock_acquire(sess->grp_lock); /* Create blank ChannelBind request */ status = pj_stun_session_create_req(sess->stun, PJ_STUN_CHANNEL_BIND_REQUEST, PJ_STUN_MAGIC, NULL, &tdata); if (status != PJ_SUCCESS) goto on_return; /* Lookup if this peer has already been assigned a number */ ch = lookup_ch_by_addr(sess, peer_adr, pj_sockaddr_get_len(peer_adr), PJ_TRUE, PJ_FALSE); pj_assert(ch); if (ch->num != PJ_TURN_INVALID_CHANNEL) { /* Channel is already bound. This is a refresh request. */ ch_num = ch->num; } else { PJ_ASSERT_ON_FAIL(sess->next_ch <= PJ_TURN_CHANNEL_MAX, {status=PJ_ETOOMANY; goto on_return;}); ch->num = ch_num = sess->next_ch++; } /* Add CHANNEL-NUMBER attribute */ pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_CHANNEL_NUMBER, PJ_STUN_SET_CH_NB(ch_num)); /* Add XOR-PEER-ADDRESS attribute */ pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE, peer_adr, addr_len); /* Send the request, associate peer data structure with tdata * for future reference when we receive the ChannelBind response. */ status = pj_stun_session_send_msg(sess->stun, ch, PJ_FALSE, (sess->conn_type==PJ_TURN_TP_UDP), sess->srv_addr, pj_sockaddr_get_len(sess->srv_addr), tdata); on_return: pj_grp_lock_release(sess->grp_lock); return status; } /** * Notify TURN client session upon receiving a packet from server. * The packet maybe a STUN packet or ChannelData packet. */ PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess, void *pkt, pj_size_t pkt_len, pj_size_t *parsed_len) { pj_bool_t is_stun; pj_status_t status; pj_bool_t is_datagram; /* Packet could be ChannelData or STUN message (response or * indication). */ /* Start locking the session */ pj_grp_lock_acquire(sess->grp_lock); is_datagram = (sess->conn_type==PJ_TURN_TP_UDP); /* Quickly check if this is STUN message */ is_stun = ((((pj_uint8_t*)pkt)[0] & 0xC0) == 0); if (is_stun) { /* This looks like STUN, give it to the STUN session */ unsigned options; options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK; if (is_datagram) options |= PJ_STUN_IS_DATAGRAM; status=pj_stun_session_on_rx_pkt(sess->stun, pkt, pkt_len, options, NULL, parsed_len, sess->srv_addr, pj_sockaddr_get_len(sess->srv_addr)); } else { /* This must be ChannelData. */ pj_turn_channel_data cd; struct ch_t *ch; if (pkt_len < 4) { if (parsed_len) *parsed_len = 0; return PJ_ETOOSMALL; } /* Decode ChannelData packet */ pj_memcpy(&cd, pkt, sizeof(pj_turn_channel_data)); cd.ch_number = pj_ntohs(cd.ch_number); cd.length = pj_ntohs(cd.length); /* Check that size is sane */ if (pkt_len < cd.length+sizeof(cd)) { if (parsed_len) { if (is_datagram) { /* Discard the datagram */ *parsed_len = pkt_len; } else { /* Insufficient fragment */ *parsed_len = 0; } } status = PJ_ETOOSMALL; goto on_return; } else { if (parsed_len) { /* Apply padding too */ *parsed_len = ((cd.length + 3) & (~3)) + sizeof(cd); } } /* Lookup channel */ ch = lookup_ch_by_chnum(sess, cd.ch_number); if (!ch || !ch->bound) { status = PJ_ENOTFOUND; goto on_return; } /* Notify application */ if (sess->cb.on_rx_data) { (*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)pkt)+sizeof(cd), cd.length, &ch->addr, pj_sockaddr_get_len(&ch->addr)); } status = PJ_SUCCESS; } on_return: pj_grp_lock_release(sess->grp_lock); return status; } /* * This is a callback from STUN session to send outgoing packet. */ static pj_status_t stun_on_send_msg(pj_stun_session *stun, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len) { pj_turn_session *sess; PJ_UNUSED_ARG(token); sess = (pj_turn_session*) pj_stun_session_get_user_data(stun); return (*sess->cb.on_send_pkt)(sess, (const pj_uint8_t*)pkt, (unsigned)pkt_size, dst_addr, addr_len); } /* * Handle failed ALLOCATE or REFRESH request. This may switch to alternate * server if we have one. */ static void on_session_fail( pj_turn_session *sess, enum pj_stun_method_e method, pj_status_t status, const pj_str_t *reason) { sess->last_status = status; do { pj_str_t reason1; char err_msg[PJ_ERR_MSG_SIZE]; if (reason == NULL) { pj_strerror(status, err_msg, sizeof(err_msg)); reason1 = pj_str(err_msg); reason = &reason1; } PJ_LOG(4,(sess->obj_name, "%s error: %.*s", pj_stun_get_method_name(method), (int)reason->slen, reason->ptr)); /* If this is ALLOCATE response and we don't have more server * addresses to try, notify application and destroy the TURN * session. */ if (method==PJ_STUN_ALLOCATE_METHOD && sess->srv_addr == &sess->srv_addr_list[sess->srv_addr_cnt-1]) { set_state(sess, PJ_TURN_STATE_DEALLOCATED); sess_shutdown(sess, status); return; } /* Otherwise if this is not ALLOCATE response, notify application * that session has been TERMINATED. */ if (method!=PJ_STUN_ALLOCATE_METHOD) { set_state(sess, PJ_TURN_STATE_DEALLOCATED); sess_shutdown(sess, status); return; } /* Try next server */ ++sess->srv_addr; reason = NULL; PJ_LOG(4,(sess->obj_name, "Trying next server")); set_state(sess, PJ_TURN_STATE_RESOLVED); } while (0); } /* * Handle successful response to ALLOCATE or REFRESH request. */ static void on_allocate_success(pj_turn_session *sess, enum pj_stun_method_e method, const pj_stun_msg *msg) { const pj_stun_lifetime_attr *lf_attr; const pj_stun_xor_relayed_addr_attr *raddr_attr; const pj_stun_sockaddr_attr *mapped_attr; pj_str_t s; /* Must have LIFETIME attribute */ lf_attr = (const pj_stun_lifetime_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_LIFETIME, 0); if (lf_attr == NULL) { on_session_fail(sess, method, PJNATH_EINSTUNMSG, pj_cstr(&s, "Error: Missing LIFETIME attribute")); return; } /* If LIFETIME is zero, this is a deallocation */ if (lf_attr->value == 0) { set_state(sess, PJ_TURN_STATE_DEALLOCATED); sess_shutdown(sess, PJ_SUCCESS); return; } /* Update lifetime and keep-alive interval */ sess->lifetime = lf_attr->value; pj_gettimeofday(&sess->expiry); if (sess->lifetime < PJ_TURN_KEEP_ALIVE_SEC) { if (sess->lifetime <= 2) { on_session_fail(sess, method, PJ_ETOOSMALL, pj_cstr(&s, "Error: LIFETIME too small")); return; } sess->ka_interval = sess->lifetime - 2; sess->expiry.sec += (sess->ka_interval-1); } else { int timeout; sess->ka_interval = PJ_TURN_KEEP_ALIVE_SEC; timeout = sess->lifetime - PJ_TURN_REFRESH_SEC_BEFORE; if (timeout < sess->ka_interval) timeout = sess->ka_interval - 1; sess->expiry.sec += timeout; } /* Check that relayed transport address contains correct * address family. */ raddr_attr = (const pj_stun_xor_relayed_addr_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_RELAYED_ADDR, 0); if (raddr_attr == NULL && method==PJ_STUN_ALLOCATE_METHOD) { on_session_fail(sess, method, PJNATH_EINSTUNMSG, pj_cstr(&s, "Error: Received ALLOCATE without " "RELAY-ADDRESS attribute")); return; } if (raddr_attr && raddr_attr->sockaddr.addr.sa_family != sess->af) { on_session_fail(sess, method, PJNATH_EINSTUNMSG, pj_cstr(&s, "Error: RELAY-ADDRESS with non IPv4" " address family is not supported " "for now")); return; } if (raddr_attr && !pj_sockaddr_has_addr(&raddr_attr->sockaddr)) { on_session_fail(sess, method, PJNATH_EINSTUNMSG, pj_cstr(&s, "Error: Invalid IP address in " "RELAY-ADDRESS attribute")); return; } /* Save relayed address */ if (raddr_attr) { /* If we already have relay address, check if the relay address * in the response matches our relay address. */ if (pj_sockaddr_has_addr(&sess->relay_addr)) { if (pj_sockaddr_cmp(&sess->relay_addr, &raddr_attr->sockaddr)) { on_session_fail(sess, method, PJNATH_EINSTUNMSG, pj_cstr(&s, "Error: different RELAY-ADDRESS is" "returned by server")); return; } } else { /* Otherwise save the relayed address */ pj_memcpy(&sess->relay_addr, &raddr_attr->sockaddr, sizeof(pj_sockaddr)); } } /* Get mapped address */ mapped_attr = (const pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0); if (mapped_attr) { pj_memcpy(&sess->mapped_addr, &mapped_attr->sockaddr, sizeof(mapped_attr->sockaddr)); } /* Success */ /* Cancel existing keep-alive timer, if any */ pj_assert(sess->timer.id != TIMER_DESTROY); if (sess->timer.id == TIMER_KEEP_ALIVE) { pj_timer_heap_cancel_if_active(sess->timer_heap, &sess->timer, TIMER_NONE); } /* Start keep-alive timer once allocation succeeds */ if (sess->state < PJ_TURN_STATE_DEALLOCATING) { pj_time_val timeout; timeout.sec = sess->ka_interval; timeout.msec = 0; pj_timer_heap_schedule_w_grp_lock(sess->timer_heap, &sess->timer, &timeout, TIMER_KEEP_ALIVE, sess->grp_lock); set_state(sess, PJ_TURN_STATE_READY); } } /* * Notification from STUN session on request completion. */ static void stun_on_request_complete(pj_stun_session *stun, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_turn_session *sess; enum pj_stun_method_e method = (enum pj_stun_method_e) PJ_STUN_GET_METHOD(tdata->msg->hdr.type); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); sess = (pj_turn_session*)pj_stun_session_get_user_data(stun); if (method == PJ_STUN_ALLOCATE_METHOD) { /* Destroy if we have pending destroy request */ if (sess->pending_destroy) { if (status == PJ_SUCCESS) sess->state = PJ_TURN_STATE_READY; else sess->state = PJ_TURN_STATE_DEALLOCATED; sess_shutdown(sess, PJ_SUCCESS); return; } /* Handle ALLOCATE response */ if (status==PJ_SUCCESS && PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) { /* Successful Allocate response */ on_allocate_success(sess, method, response); } else { /* Failed Allocate request */ const pj_str_t *err_msg = NULL; if (status == PJ_SUCCESS) { const pj_stun_errcode_attr *err_attr; err_attr = (const pj_stun_errcode_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0); if (err_attr) { status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code); err_msg = &err_attr->reason; } else { status = PJNATH_EINSTUNMSG; } } on_session_fail(sess, method, status, err_msg); } } else if (method == PJ_STUN_REFRESH_METHOD) { /* Handle Refresh response */ if (status==PJ_SUCCESS && PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) { /* Success, schedule next refresh. */ on_allocate_success(sess, method, response); } else { /* Failed Refresh request */ const pj_str_t *err_msg = NULL; pj_assert(status != PJ_SUCCESS); if (response) { const pj_stun_errcode_attr *err_attr; err_attr = (const pj_stun_errcode_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0); if (err_attr) { status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code); err_msg = &err_attr->reason; } } /* Notify and destroy */ on_session_fail(sess, method, status, err_msg); } } else if (method == PJ_STUN_CHANNEL_BIND_METHOD) { /* Handle ChannelBind response */ if (status==PJ_SUCCESS && PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) { /* Successful ChannelBind response */ struct ch_t *ch = (struct ch_t*)token; pj_assert(ch->num != PJ_TURN_INVALID_CHANNEL); ch->bound = PJ_TRUE; /* Update hash table */ lookup_ch_by_addr(sess, &ch->addr, pj_sockaddr_get_len(&ch->addr), PJ_TRUE, PJ_TRUE); } else { /* Failed ChannelBind response */ pj_str_t reason = {"", 0}; int err_code = 0; char errbuf[PJ_ERR_MSG_SIZE]; pj_assert(status != PJ_SUCCESS); if (response) { const pj_stun_errcode_attr *err_attr; err_attr = (const pj_stun_errcode_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0); if (err_attr) { err_code = err_attr->err_code; status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code); reason = err_attr->reason; } } else { err_code = status; reason = pj_strerror(status, errbuf, sizeof(errbuf)); } PJ_LOG(1,(sess->obj_name, "ChannelBind failed: %d/%.*s", err_code, (int)reason.slen, reason.ptr)); if (err_code == PJ_STUN_SC_ALLOCATION_MISMATCH) { /* Allocation mismatch means allocation no longer exists */ on_session_fail(sess, PJ_STUN_CHANNEL_BIND_METHOD, status, &reason); return; } } } else if (method == PJ_STUN_CREATE_PERM_METHOD) { /* Handle CreatePermission response */ if (status==PJ_SUCCESS && PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) { /* No special handling when the request is successful. */ } else { /* Iterate the permission table and invalidate all permissions * that are related to this request. */ pj_hash_iterator_t it_buf, *it; char ipstr[PJ_INET6_ADDRSTRLEN+10]; int err_code; char errbuf[PJ_ERR_MSG_SIZE]; pj_str_t reason; pj_assert(status != PJ_SUCCESS); if (response) { const pj_stun_errcode_attr *eattr; eattr = (const pj_stun_errcode_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0); if (eattr) { err_code = eattr->err_code; reason = eattr->reason; } else { err_code = -1; reason = pj_str("?"); } } else { err_code = status; reason = pj_strerror(status, errbuf, sizeof(errbuf)); } it = pj_hash_first(sess->perm_table, &it_buf); while (it) { struct perm_t *perm = (struct perm_t*) pj_hash_this(sess->perm_table, it); it = pj_hash_next(sess->perm_table, it); if (perm->req_token == token) { PJ_LOG(1,(sess->obj_name, "CreatePermission failed for IP %s: %d/%.*s", pj_sockaddr_print(&perm->addr, ipstr, sizeof(ipstr), 2), err_code, (int)reason.slen, reason.ptr)); invalidate_perm(sess, perm); } } if (err_code == PJ_STUN_SC_ALLOCATION_MISMATCH) { /* Allocation mismatch means allocation no longer exists */ on_session_fail(sess, PJ_STUN_CREATE_PERM_METHOD, status, &reason); return; } } } else { PJ_LOG(4,(sess->obj_name, "Unexpected STUN %s response", pj_stun_get_method_name(response->hdr.type))); } } /* * Notification from STUN session on incoming STUN Indication * message. */ static pj_status_t stun_on_rx_indication(pj_stun_session *stun, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_msg *msg, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_turn_session *sess; pj_stun_xor_peer_addr_attr *peer_attr; pj_stun_icmp_attr *icmp; pj_stun_data_attr *data_attr; PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(pkt); PJ_UNUSED_ARG(pkt_len); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); sess = (pj_turn_session*)pj_stun_session_get_user_data(stun); /* Expecting Data Indication only */ if (msg->hdr.type != PJ_STUN_DATA_INDICATION) { PJ_LOG(4,(sess->obj_name, "Unexpected STUN %s indication", pj_stun_get_method_name(msg->hdr.type))); return PJ_EINVALIDOP; } /* Check if there is ICMP attribute in the message */ icmp = (pj_stun_icmp_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ICMP, 0); if (icmp != NULL) { /* This is a forwarded ICMP packet. Ignore it for now */ return PJ_SUCCESS; } /* Get XOR-PEER-ADDRESS attribute */ peer_attr = (pj_stun_xor_peer_addr_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0); /* Get DATA attribute */ data_attr = (pj_stun_data_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_DATA, 0); /* Must have both XOR-PEER-ADDRESS and DATA attributes */ if (!peer_attr || !data_attr) { PJ_LOG(4,(sess->obj_name, "Received Data indication with missing attributes")); return PJ_EINVALIDOP; } /* Notify application */ if (sess->cb.on_rx_data) { (*sess->cb.on_rx_data)(sess, data_attr->data, data_attr->length, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); } return PJ_SUCCESS; } /* * Notification on completion of DNS SRV resolution. */ static void dns_srv_resolver_cb(void *user_data, pj_status_t status, const pj_dns_srv_record *rec) { pj_turn_session *sess = (pj_turn_session*) user_data; unsigned i, cnt, tot_cnt; /* Check failure */ if (status != PJ_SUCCESS || sess->pending_destroy) { set_state(sess, PJ_TURN_STATE_DESTROYING); sess_shutdown(sess, status); return; } /* Calculate total number of server entries in the response */ tot_cnt = 0; for (i=0; icount; ++i) { tot_cnt += rec->entry[i].server.addr_count; } if (tot_cnt > PJ_TURN_MAX_DNS_SRV_CNT) tot_cnt = PJ_TURN_MAX_DNS_SRV_CNT; /* Allocate server entries */ sess->srv_addr_list = (pj_sockaddr*) pj_pool_calloc(sess->pool, tot_cnt, sizeof(pj_sockaddr)); /* Copy results to server entries */ for (i=0, cnt=0; icount && cntentry[i].server.addr_count && cntsrv_addr_list[cnt].ipv4; addr->sin_family = sess->af; addr->sin_port = pj_htons(rec->entry[i].port); addr->sin_addr.s_addr = rec->entry[i].server.addr[j].s_addr; ++cnt; } } sess->srv_addr_cnt = (pj_uint16_t)cnt; /* Set current server */ sess->srv_addr = &sess->srv_addr_list[0]; /* Set state to PJ_TURN_STATE_RESOLVED */ set_state(sess, PJ_TURN_STATE_RESOLVED); /* Run pending allocation */ if (sess->pending_alloc) { pj_turn_session_alloc(sess, NULL); } } /* * Lookup peer descriptor from its address. */ static struct ch_t *lookup_ch_by_addr(pj_turn_session *sess, const pj_sockaddr_t *addr, unsigned addr_len, pj_bool_t update, pj_bool_t bind_channel) { pj_uint32_t hval = 0; struct ch_t *ch; ch = (struct ch_t*) pj_hash_get(sess->ch_table, addr, addr_len, &hval); if (ch == NULL && update) { ch = PJ_POOL_ZALLOC_T(sess->pool, struct ch_t); ch->num = PJ_TURN_INVALID_CHANNEL; pj_memcpy(&ch->addr, addr, addr_len); /* Register by peer address */ pj_hash_set(sess->pool, sess->ch_table, &ch->addr, addr_len, hval, ch); } if (ch && update) { pj_gettimeofday(&ch->expiry); ch->expiry.sec += PJ_TURN_PERM_TIMEOUT - sess->ka_interval - 1; if (bind_channel) { pj_uint32_t hval2 = 0; /* Register by channel number */ pj_assert(ch->num != PJ_TURN_INVALID_CHANNEL && ch->bound); if (pj_hash_get(sess->ch_table, &ch->num, sizeof(ch->num), &hval2)==0) { pj_hash_set(sess->pool, sess->ch_table, &ch->num, sizeof(ch->num), hval2, ch); } } } /* Also create/update permission for this destination. Ideally we * should update this when we receive the successful response, * but that would cause duplicate CreatePermission to be sent * during refreshing. */ if (ch && update) { lookup_perm(sess, &ch->addr, pj_sockaddr_get_len(&ch->addr), PJ_TRUE); } return ch; } /* * Lookup channel descriptor from its channel number. */ static struct ch_t *lookup_ch_by_chnum(pj_turn_session *sess, pj_uint16_t chnum) { return (struct ch_t*) pj_hash_get(sess->ch_table, &chnum, sizeof(chnum), NULL); } /* * Lookup permission and optionally create if it doesn't exist. */ static struct perm_t *lookup_perm(pj_turn_session *sess, const pj_sockaddr_t *addr, unsigned addr_len, pj_bool_t update) { pj_uint32_t hval = 0; pj_sockaddr perm_addr; struct perm_t *perm; /* make sure port number if zero */ if (pj_sockaddr_get_port(addr) != 0) { pj_memcpy(&perm_addr, addr, addr_len); pj_sockaddr_set_port(&perm_addr, 0); addr = &perm_addr; } /* lookup and create if it doesn't exist and wanted */ perm = (struct perm_t*) pj_hash_get(sess->perm_table, addr, addr_len, &hval); if (perm == NULL && update) { perm = PJ_POOL_ZALLOC_T(sess->pool, struct perm_t); pj_memcpy(&perm->addr, addr, addr_len); perm->hval = hval; pj_hash_set(sess->pool, sess->perm_table, &perm->addr, addr_len, perm->hval, perm); } if (perm && update) { pj_gettimeofday(&perm->expiry); perm->expiry.sec += PJ_TURN_PERM_TIMEOUT - sess->ka_interval - 1; } return perm; } /* * Delete permission */ static void invalidate_perm(pj_turn_session *sess, struct perm_t *perm) { pj_hash_set(NULL, sess->perm_table, &perm->addr, pj_sockaddr_get_len(&perm->addr), perm->hval, NULL); } /* * Scan permission's hash table to refresh the permission. */ static unsigned refresh_permissions(pj_turn_session *sess, const pj_time_val *now) { pj_stun_tx_data *tdata = NULL; unsigned count = 0; void *req_token = NULL; pj_hash_iterator_t *it, itbuf; pj_status_t status; it = pj_hash_first(sess->perm_table, &itbuf); while (it) { struct perm_t *perm = (struct perm_t*) pj_hash_this(sess->perm_table, it); it = pj_hash_next(sess->perm_table, it); if (perm->expiry.sec-1 <= now->sec) { if (perm->renew) { /* Renew this permission */ if (tdata == NULL) { /* Create a bare CreatePermission request */ status = pj_stun_session_create_req( sess->stun, PJ_STUN_CREATE_PERM_REQUEST, PJ_STUN_MAGIC, NULL, &tdata); if (status != PJ_SUCCESS) { PJ_LOG(1,(sess->obj_name, "Error creating CreatePermission request: %d", status)); return 0; } /* Create request token to map the request to the perm * structures which the request belongs. */ req_token = (void*)(pj_ssize_t)pj_rand(); } status = pj_stun_msg_add_sockaddr_attr( tdata->pool, tdata->msg, PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE, &perm->addr, sizeof(perm->addr)); if (status != PJ_SUCCESS) { pj_stun_msg_destroy_tdata(sess->stun, tdata); return 0; } perm->expiry = *now; perm->expiry.sec += PJ_TURN_PERM_TIMEOUT-sess->ka_interval-1; perm->req_token = req_token; ++count; } else { /* This permission has expired and app doesn't want * us to renew, so delete it from the hash table. */ invalidate_perm(sess, perm); } } } if (tdata) { status = pj_stun_session_send_msg(sess->stun, req_token, PJ_FALSE, (sess->conn_type==PJ_TURN_TP_UDP), sess->srv_addr, pj_sockaddr_get_len(sess->srv_addr), tdata); if (status != PJ_SUCCESS) { PJ_LOG(1,(sess->obj_name, "Error sending CreatePermission request: %d", status)); count = 0; } } return count; } /* * Timer event. */ static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e) { pj_turn_session *sess = (pj_turn_session*)e->user_data; enum timer_id_t eid; PJ_UNUSED_ARG(th); pj_grp_lock_acquire(sess->grp_lock); eid = (enum timer_id_t) e->id; e->id = TIMER_NONE; if (eid == TIMER_KEEP_ALIVE) { pj_time_val now; pj_hash_iterator_t itbuf, *it; pj_bool_t resched = PJ_TRUE; pj_bool_t pkt_sent = PJ_FALSE; if (sess->state >= PJ_TURN_STATE_DEALLOCATING) { /* Ignore if we're deallocating */ goto on_return; } pj_gettimeofday(&now); /* Refresh allocation if it's time to do so */ if (PJ_TIME_VAL_LTE(sess->expiry, now)) { int lifetime = sess->alloc_param.lifetime; if (lifetime == 0) lifetime = -1; send_refresh(sess, lifetime); resched = PJ_FALSE; pkt_sent = PJ_TRUE; } /* Scan hash table to refresh bound channels */ it = pj_hash_first(sess->ch_table, &itbuf); while (it) { struct ch_t *ch = (struct ch_t*) pj_hash_this(sess->ch_table, it); if (ch->bound && PJ_TIME_VAL_LTE(ch->expiry, now)) { /* Send ChannelBind to refresh channel binding and * permission. */ pj_turn_session_bind_channel(sess, &ch->addr, pj_sockaddr_get_len(&ch->addr)); pkt_sent = PJ_TRUE; } it = pj_hash_next(sess->ch_table, it); } /* Scan permission table to refresh permissions */ if (refresh_permissions(sess, &now)) pkt_sent = PJ_TRUE; /* If no packet is sent, send a blank Send indication to * refresh local NAT. */ if (!pkt_sent && sess->alloc_param.ka_interval > 0) { pj_stun_tx_data *tdata; pj_status_t rc; /* Create blank SEND-INDICATION */ rc = pj_stun_session_create_ind(sess->stun, PJ_STUN_SEND_INDICATION, &tdata); if (rc == PJ_SUCCESS) { /* Add DATA attribute with zero length */ pj_stun_msg_add_binary_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_DATA, NULL, 0); /* Send the indication */ pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, PJ_FALSE, sess->srv_addr, pj_sockaddr_get_len(sess->srv_addr), tdata); } } /* Reshcedule timer */ if (resched) { pj_time_val delay; delay.sec = sess->ka_interval; delay.msec = 0; pj_timer_heap_schedule_w_grp_lock(sess->timer_heap, &sess->timer, &delay, TIMER_KEEP_ALIVE, sess->grp_lock); } } else if (eid == TIMER_DESTROY) { /* Time to destroy */ do_destroy(sess); } else { pj_assert(!"Unknown timer event"); } on_return: pj_grp_lock_release(sess->grp_lock); } ================================================ FILE: deps/pjsip/pjnath/src/pjnath/turn_sock.c ================================================ /* $Id: turn_sock.c 4360 2013-02-21 11:26:35Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include enum { TIMER_NONE, TIMER_DESTROY }; enum { MAX_BIND_RETRY = 100 }; #define INIT 0x1FFFFFFF struct pj_turn_sock { pj_pool_t *pool; const char *obj_name; pj_turn_session *sess; pj_turn_sock_cb cb; void *user_data; pj_bool_t is_destroying; pj_grp_lock_t *grp_lock; pj_turn_alloc_param alloc_param; pj_stun_config cfg; pj_turn_sock_cfg setting; pj_timer_entry timer; int af; pj_turn_tp_type conn_type; pj_activesock_t *active_sock; pj_ioqueue_op_key_t send_key; }; /* * Callback prototypes. */ static pj_status_t turn_on_send_pkt(pj_turn_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len); static void turn_on_channel_bound(pj_turn_session *sess, const pj_sockaddr_t *peer_addr, unsigned addr_len, unsigned ch_num); static void turn_on_rx_data(pj_turn_session *sess, void *pkt, unsigned pkt_len, const pj_sockaddr_t *peer_addr, unsigned addr_len); static void turn_on_state(pj_turn_session *sess, pj_turn_state_t old_state, pj_turn_state_t new_state); static pj_bool_t on_data_read(pj_activesock_t *asock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder); static pj_bool_t on_connect_complete(pj_activesock_t *asock, pj_status_t status); static void turn_sock_on_destroy(void *comp); static void destroy(pj_turn_sock *turn_sock); static void timer_cb(pj_timer_heap_t *th, pj_timer_entry *e); /* Init config */ PJ_DEF(void) pj_turn_sock_cfg_default(pj_turn_sock_cfg *cfg) { pj_bzero(cfg, sizeof(*cfg)); cfg->max_pkt_size = PJ_TURN_MAX_PKT_LEN; cfg->qos_type = PJ_QOS_TYPE_BEST_EFFORT; cfg->qos_ignore_error = PJ_TRUE; } /* * Create. */ PJ_DEF(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg, int af, pj_turn_tp_type conn_type, const pj_turn_sock_cb *cb, const pj_turn_sock_cfg *setting, void *user_data, pj_turn_sock **p_turn_sock) { pj_turn_sock *turn_sock; pj_turn_session_cb sess_cb; pj_turn_sock_cfg default_setting; pj_pool_t *pool; const char *name_tmpl; pj_status_t status; PJ_ASSERT_RETURN(cfg && p_turn_sock, PJ_EINVAL); PJ_ASSERT_RETURN(af==pj_AF_INET() || af==pj_AF_INET6(), PJ_EINVAL); PJ_ASSERT_RETURN(conn_type!=PJ_TURN_TP_TCP || PJ_HAS_TCP, PJ_EINVAL); if (!setting) { pj_turn_sock_cfg_default(&default_setting); setting = &default_setting; } switch (conn_type) { case PJ_TURN_TP_UDP: name_tmpl = "udprel%p"; break; case PJ_TURN_TP_TCP: name_tmpl = "tcprel%p"; break; default: PJ_ASSERT_RETURN(!"Invalid TURN conn_type", PJ_EINVAL); name_tmpl = "tcprel%p"; break; } /* Create and init basic data structure */ pool = pj_pool_create(cfg->pf, name_tmpl, PJNATH_POOL_LEN_TURN_SOCK, PJNATH_POOL_INC_TURN_SOCK, NULL); turn_sock = PJ_POOL_ZALLOC_T(pool, pj_turn_sock); turn_sock->pool = pool; turn_sock->obj_name = pool->obj_name; turn_sock->user_data = user_data; turn_sock->af = af; turn_sock->conn_type = conn_type; /* Copy STUN config (this contains ioqueue, timer heap, etc.) */ pj_memcpy(&turn_sock->cfg, cfg, sizeof(*cfg)); /* Copy setting (QoS parameters etc */ pj_memcpy(&turn_sock->setting, setting, sizeof(*setting)); /* Set callback */ if (cb) { pj_memcpy(&turn_sock->cb, cb, sizeof(*cb)); } /* Session lock */ if (setting && setting->grp_lock) { turn_sock->grp_lock = setting->grp_lock; } else { status = pj_grp_lock_create(pool, NULL, &turn_sock->grp_lock); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } } pj_grp_lock_add_ref(turn_sock->grp_lock); pj_grp_lock_add_handler(turn_sock->grp_lock, pool, turn_sock, &turn_sock_on_destroy); /* Init timer */ pj_timer_entry_init(&turn_sock->timer, TIMER_NONE, turn_sock, &timer_cb); /* Init TURN session */ pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_send_pkt = &turn_on_send_pkt; sess_cb.on_channel_bound = &turn_on_channel_bound; sess_cb.on_rx_data = &turn_on_rx_data; sess_cb.on_state = &turn_on_state; status = pj_turn_session_create(cfg, pool->obj_name, af, conn_type, turn_sock->grp_lock, &sess_cb, 0, turn_sock, &turn_sock->sess); if (status != PJ_SUCCESS) { destroy(turn_sock); return status; } /* Note: socket and ioqueue will be created later once the TURN server * has been resolved. */ *p_turn_sock = turn_sock; return PJ_SUCCESS; } /* * Destroy. */ static void turn_sock_on_destroy(void *comp) { pj_turn_sock *turn_sock = (pj_turn_sock*) comp; if (turn_sock->pool) { pj_pool_t *pool = turn_sock->pool; PJ_LOG(4,(turn_sock->obj_name, "TURN socket destroyed")); turn_sock->pool = NULL; pj_pool_release(pool); } } static void destroy(pj_turn_sock *turn_sock) { PJ_LOG(4,(turn_sock->obj_name, "TURN socket destroy request, ref_cnt=%d", pj_grp_lock_get_ref(turn_sock->grp_lock))); pj_grp_lock_acquire(turn_sock->grp_lock); if (turn_sock->is_destroying) { pj_grp_lock_release(turn_sock->grp_lock); return; } turn_sock->is_destroying = PJ_TRUE; if (turn_sock->sess) pj_turn_session_shutdown(turn_sock->sess); if (turn_sock->active_sock) pj_activesock_close(turn_sock->active_sock); pj_grp_lock_dec_ref(turn_sock->grp_lock); pj_grp_lock_release(turn_sock->grp_lock); } PJ_DEF(void) pj_turn_sock_destroy(pj_turn_sock *turn_sock) { pj_grp_lock_acquire(turn_sock->grp_lock); if (turn_sock->is_destroying) { pj_grp_lock_release(turn_sock->grp_lock); return; } if (turn_sock->sess) { pj_turn_session_shutdown(turn_sock->sess); /* This will ultimately call our state callback, and when * session state is DESTROYING we will schedule a timer to * destroy ourselves. */ } else { destroy(turn_sock); } pj_grp_lock_release(turn_sock->grp_lock); } /* Timer callback */ static void timer_cb(pj_timer_heap_t *th, pj_timer_entry *e) { pj_turn_sock *turn_sock = (pj_turn_sock*)e->user_data; int eid = e->id; PJ_UNUSED_ARG(th); e->id = TIMER_NONE; switch (eid) { case TIMER_DESTROY: destroy(turn_sock); break; default: pj_assert(!"Invalid timer id"); break; } } /* Display error */ static void show_err(pj_turn_sock *turn_sock, const char *title, pj_status_t status) { PJ_PERROR(4,(turn_sock->obj_name, status, title)); } /* On error, terminate session */ static void sess_fail(pj_turn_sock *turn_sock, const char *title, pj_status_t status) { show_err(turn_sock, title, status); if (turn_sock->sess) { pj_turn_session_destroy(turn_sock->sess, status); } } /* * Set user data. */ PJ_DEF(pj_status_t) pj_turn_sock_set_user_data( pj_turn_sock *turn_sock, void *user_data) { PJ_ASSERT_RETURN(turn_sock, PJ_EINVAL); turn_sock->user_data = user_data; return PJ_SUCCESS; } /* * Get user data. */ PJ_DEF(void*) pj_turn_sock_get_user_data(pj_turn_sock *turn_sock) { PJ_ASSERT_RETURN(turn_sock, NULL); return turn_sock->user_data; } /* * Get group lock. */ PJ_DEF(pj_grp_lock_t *) pj_turn_sock_get_grp_lock(pj_turn_sock *turn_sock) { PJ_ASSERT_RETURN(turn_sock, NULL); return turn_sock->grp_lock; } /** * Get info. */ PJ_DEF(pj_status_t) pj_turn_sock_get_info(pj_turn_sock *turn_sock, pj_turn_session_info *info) { PJ_ASSERT_RETURN(turn_sock && info, PJ_EINVAL); if (turn_sock->sess) { return pj_turn_session_get_info(turn_sock->sess, info); } else { pj_bzero(info, sizeof(*info)); info->state = PJ_TURN_STATE_NULL; return PJ_SUCCESS; } } /** * Lock the TURN socket. Application may need to call this function to * synchronize access to other objects to avoid deadlock. */ PJ_DEF(pj_status_t) pj_turn_sock_lock(pj_turn_sock *turn_sock) { return pj_grp_lock_acquire(turn_sock->grp_lock); } /** * Unlock the TURN socket. */ PJ_DEF(pj_status_t) pj_turn_sock_unlock(pj_turn_sock *turn_sock) { return pj_grp_lock_release(turn_sock->grp_lock); } /* * Set STUN message logging for this TURN session. */ PJ_DEF(void) pj_turn_sock_set_log( pj_turn_sock *turn_sock, unsigned flags) { pj_turn_session_set_log(turn_sock->sess, flags); } /* * Set software name */ PJ_DEF(pj_status_t) pj_turn_sock_set_software_name( pj_turn_sock *turn_sock, const pj_str_t *sw) { return pj_turn_session_set_software_name(turn_sock->sess, sw); } /* * Initialize. */ PJ_DEF(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock, const pj_str_t *domain, int default_port, pj_dns_resolver *resolver, const pj_stun_auth_cred *cred, const pj_turn_alloc_param *param) { pj_status_t status; PJ_ASSERT_RETURN(turn_sock && domain, PJ_EINVAL); PJ_ASSERT_RETURN(turn_sock->sess, PJ_EINVALIDOP); pj_grp_lock_acquire(turn_sock->grp_lock); /* Copy alloc param. We will call session_alloc() only after the * server address has been resolved. */ if (param) { pj_turn_alloc_param_copy(turn_sock->pool, &turn_sock->alloc_param, param); } else { pj_turn_alloc_param_default(&turn_sock->alloc_param); } /* Set credental */ if (cred) { status = pj_turn_session_set_credential(turn_sock->sess, cred); if (status != PJ_SUCCESS) { sess_fail(turn_sock, "Error setting credential", status); pj_grp_lock_release(turn_sock->grp_lock); return status; } } /* Resolve server */ status = pj_turn_session_set_server(turn_sock->sess, domain, default_port, resolver); if (status != PJ_SUCCESS) { sess_fail(turn_sock, "Error setting TURN server", status); pj_grp_lock_release(turn_sock->grp_lock); return status; } /* Done for now. The next work will be done when session state moved * to RESOLVED state. */ pj_grp_lock_release(turn_sock->grp_lock); return PJ_SUCCESS; } /* * Install permission */ PJ_DEF(pj_status_t) pj_turn_sock_set_perm( pj_turn_sock *turn_sock, unsigned addr_cnt, const pj_sockaddr addr[], unsigned options) { if (turn_sock->sess == NULL) return PJ_EINVALIDOP; return pj_turn_session_set_perm(turn_sock->sess, addr_cnt, addr, options); } /* * Send packet. */ PJ_DEF(pj_status_t) pj_turn_sock_sendto( pj_turn_sock *turn_sock, const pj_uint8_t *pkt, unsigned pkt_len, const pj_sockaddr_t *addr, unsigned addr_len) { PJ_ASSERT_RETURN(turn_sock && addr && addr_len, PJ_EINVAL); if (turn_sock->sess == NULL) return PJ_EINVALIDOP; return pj_turn_session_sendto(turn_sock->sess, pkt, pkt_len, addr, addr_len); } /* * Bind a peer address to a channel number. */ PJ_DEF(pj_status_t) pj_turn_sock_bind_channel( pj_turn_sock *turn_sock, const pj_sockaddr_t *peer, unsigned addr_len) { PJ_ASSERT_RETURN(turn_sock && peer && addr_len, PJ_EINVAL); PJ_ASSERT_RETURN(turn_sock->sess != NULL, PJ_EINVALIDOP); return pj_turn_session_bind_channel(turn_sock->sess, peer, addr_len); } /* * Notification when outgoing TCP socket has been connected. */ static pj_bool_t on_connect_complete(pj_activesock_t *asock, pj_status_t status) { pj_turn_sock *turn_sock; turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock); if (!turn_sock) return PJ_FALSE; pj_grp_lock_acquire(turn_sock->grp_lock); /* TURN session may have already been destroyed here. * See ticket #1557 (http://trac.pjsip.org/repos/ticket/1557). */ if (!turn_sock->sess) { sess_fail(turn_sock, "TURN session already destroyed", status); pj_grp_lock_release(turn_sock->grp_lock); return PJ_FALSE; } if (status != PJ_SUCCESS) { sess_fail(turn_sock, "TCP connect() error", status); pj_grp_lock_release(turn_sock->grp_lock); return PJ_FALSE; } if (turn_sock->conn_type != PJ_TURN_TP_UDP) { PJ_LOG(5,(turn_sock->obj_name, "TCP connected")); } /* Kick start pending read operation */ status = pj_activesock_start_read(asock, turn_sock->pool, turn_sock->setting.max_pkt_size, 0); /* Init send_key */ pj_ioqueue_op_key_init(&turn_sock->send_key, sizeof(turn_sock->send_key)); /* Send Allocate request */ status = pj_turn_session_alloc(turn_sock->sess, &turn_sock->alloc_param); if (status != PJ_SUCCESS) { sess_fail(turn_sock, "Error sending ALLOCATE", status); pj_grp_lock_release(turn_sock->grp_lock); return PJ_FALSE; } pj_grp_lock_release(turn_sock->grp_lock); return PJ_TRUE; } static pj_uint16_t GETVAL16H(const pj_uint8_t *buf, unsigned pos) { return (pj_uint16_t) ((buf[pos + 0] << 8) | \ (buf[pos + 1] << 0)); } /* Quick check to determine if there is enough packet to process in the * incoming buffer. Return the packet length, or zero if there's no packet. */ static unsigned has_packet(pj_turn_sock *turn_sock, const void *buf, pj_size_t bufsize) { pj_bool_t is_stun; if (turn_sock->conn_type == PJ_TURN_TP_UDP) return (unsigned)bufsize; /* Quickly check if this is STUN message, by checking the first two bits and * size field which must be multiple of 4 bytes */ is_stun = ((((pj_uint8_t*)buf)[0] & 0xC0) == 0) && ((GETVAL16H((const pj_uint8_t*)buf, 2) & 0x03)==0); if (is_stun) { pj_size_t msg_len = GETVAL16H((const pj_uint8_t*)buf, 2); return (unsigned)((msg_len+20 <= bufsize) ? msg_len+20 : 0); } else { /* This must be ChannelData. */ pj_turn_channel_data cd; if (bufsize < 4) return 0; /* Decode ChannelData packet */ pj_memcpy(&cd, buf, sizeof(pj_turn_channel_data)); cd.length = pj_ntohs(cd.length); if (bufsize >= cd.length+sizeof(cd)) return (cd.length+sizeof(cd)+3) & (~3); else return 0; } } /* * Notification from ioqueue when incoming UDP packet is received. */ static pj_bool_t on_data_read(pj_activesock_t *asock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder) { pj_turn_sock *turn_sock; pj_bool_t ret = PJ_TRUE; turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock); pj_grp_lock_acquire(turn_sock->grp_lock); if (status == PJ_SUCCESS && turn_sock->sess && !turn_sock->is_destroying) { /* Report incoming packet to TURN session, repeat while we have * "packet" in the buffer (required for stream-oriented transports) */ unsigned pkt_len; //PJ_LOG(5,(turn_sock->pool->obj_name, // "Incoming data, %lu bytes total buffer", size)); while ((pkt_len=has_packet(turn_sock, data, size)) != 0) { pj_size_t parsed_len; //const pj_uint8_t *pkt = (const pj_uint8_t*)data; //PJ_LOG(5,(turn_sock->pool->obj_name, // "Packet start: %02X %02X %02X %02X", // pkt[0], pkt[1], pkt[2], pkt[3])); //PJ_LOG(5,(turn_sock->pool->obj_name, // "Processing %lu bytes packet of %lu bytes total buffer", // pkt_len, size)); parsed_len = (unsigned)size; pj_turn_session_on_rx_pkt(turn_sock->sess, data, size, &parsed_len); /* parsed_len may be zero if we have parsing error, so use our * previous calculation to exhaust the bad packet. */ if (parsed_len == 0) parsed_len = pkt_len; if (parsed_len < (unsigned)size) { *remainder = size - parsed_len; pj_memmove(data, ((char*)data)+parsed_len, *remainder); } else { *remainder = 0; } size = *remainder; //PJ_LOG(5,(turn_sock->pool->obj_name, // "Buffer size now %lu bytes", size)); } } else if (status != PJ_SUCCESS && turn_sock->conn_type != PJ_TURN_TP_UDP) { sess_fail(turn_sock, "TCP connection closed", status); ret = PJ_FALSE; goto on_return; } on_return: pj_grp_lock_release(turn_sock->grp_lock); return ret; } /* * Callback from TURN session to send outgoing packet. */ static pj_status_t turn_on_send_pkt(pj_turn_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len) { pj_turn_sock *turn_sock = (pj_turn_sock*) pj_turn_session_get_user_data(sess); pj_ssize_t len = pkt_len; pj_status_t status; if (turn_sock == NULL || turn_sock->is_destroying) { /* We've been destroyed */ // https://trac.pjsip.org/repos/ticket/1316 //pj_assert(!"We should shutdown gracefully"); return PJ_EINVALIDOP; } PJ_UNUSED_ARG(dst_addr); PJ_UNUSED_ARG(dst_addr_len); status = pj_activesock_send(turn_sock->active_sock, &turn_sock->send_key, pkt, &len, 0); if (status != PJ_SUCCESS && status != PJ_EPENDING) { show_err(turn_sock, "socket send()", status); } return status; } /* * Callback from TURN session when a channel is successfully bound. */ static void turn_on_channel_bound(pj_turn_session *sess, const pj_sockaddr_t *peer_addr, unsigned addr_len, unsigned ch_num) { PJ_UNUSED_ARG(sess); PJ_UNUSED_ARG(peer_addr); PJ_UNUSED_ARG(addr_len); PJ_UNUSED_ARG(ch_num); } /* * Callback from TURN session upon incoming data. */ static void turn_on_rx_data(pj_turn_session *sess, void *pkt, unsigned pkt_len, const pj_sockaddr_t *peer_addr, unsigned addr_len) { pj_turn_sock *turn_sock = (pj_turn_sock*) pj_turn_session_get_user_data(sess); if (turn_sock == NULL || turn_sock->is_destroying) { /* We've been destroyed */ return; } if (turn_sock->cb.on_rx_data) { (*turn_sock->cb.on_rx_data)(turn_sock, pkt, pkt_len, peer_addr, addr_len); } } /* * Callback from TURN session when state has changed */ static void turn_on_state(pj_turn_session *sess, pj_turn_state_t old_state, pj_turn_state_t new_state) { pj_turn_sock *turn_sock = (pj_turn_sock*) pj_turn_session_get_user_data(sess); pj_status_t status; if (turn_sock == NULL) { /* We've been destroyed */ return; } /* Notify app first */ if (turn_sock->cb.on_state) { (*turn_sock->cb.on_state)(turn_sock, old_state, new_state); } /* Make sure user hasn't destroyed us in the callback */ if (turn_sock->sess && new_state == PJ_TURN_STATE_RESOLVED) { pj_turn_session_info info; pj_turn_session_get_info(turn_sock->sess, &info); new_state = info.state; } if (turn_sock->sess && new_state == PJ_TURN_STATE_RESOLVED) { /* * Once server has been resolved, initiate outgoing TCP * connection to the server. */ pj_turn_session_info info; char addrtxt[PJ_INET6_ADDRSTRLEN+8]; int sock_type; pj_sock_t sock; pj_activesock_cfg asock_cfg; pj_activesock_cb asock_cb; pj_sockaddr bound_addr, *cfg_bind_addr; pj_uint16_t max_bind_retry; /* Close existing connection, if any. This happens when * we're switching to alternate TURN server when either TCP * connection or ALLOCATE request failed. */ if (turn_sock->active_sock) { pj_activesock_close(turn_sock->active_sock); turn_sock->active_sock = NULL; } /* Get server address from session info */ pj_turn_session_get_info(sess, &info); if (turn_sock->conn_type == PJ_TURN_TP_UDP) sock_type = pj_SOCK_DGRAM(); else sock_type = pj_SOCK_STREAM(); /* Init socket */ status = pj_sock_socket(turn_sock->af, sock_type, 0, &sock); if (status != PJ_SUCCESS) { pj_turn_sock_destroy(turn_sock); return; } /* Bind socket */ cfg_bind_addr = &turn_sock->setting.bound_addr; max_bind_retry = MAX_BIND_RETRY; if (turn_sock->setting.port_range && turn_sock->setting.port_range < max_bind_retry) { max_bind_retry = turn_sock->setting.port_range; } pj_sockaddr_init(turn_sock->af, &bound_addr, NULL, 0); if (cfg_bind_addr->addr.sa_family == pj_AF_INET() || cfg_bind_addr->addr.sa_family == pj_AF_INET6()) { pj_sockaddr_cp(&bound_addr, cfg_bind_addr); } status = pj_sock_bind_random(sock, &bound_addr, turn_sock->setting.port_range, max_bind_retry); if (status != PJ_SUCCESS) { pj_turn_sock_destroy(turn_sock); return; } /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, turn_sock->setting.qos_type, &turn_sock->setting.qos_params, (turn_sock->setting.qos_ignore_error?2:1), turn_sock->pool->obj_name, NULL); if (status != PJ_SUCCESS && !turn_sock->setting.qos_ignore_error) { pj_turn_sock_destroy(turn_sock); return; } /* Apply socket buffer size */ if (turn_sock->setting.so_rcvbuf_size > 0) { unsigned sobuf_size = turn_sock->setting.so_rcvbuf_size; status = pj_sock_setsockopt_sobuf(sock, pj_SO_RCVBUF(), PJ_TRUE, &sobuf_size); if (status != PJ_SUCCESS) { pj_perror(3, turn_sock->obj_name, status, "Failed setting SO_RCVBUF"); } else { if (sobuf_size < turn_sock->setting.so_rcvbuf_size) { PJ_LOG(4, (turn_sock->obj_name, "Warning! Cannot set SO_RCVBUF as configured," " now=%d, configured=%d", sobuf_size, turn_sock->setting.so_rcvbuf_size)); } else { PJ_LOG(5, (turn_sock->obj_name, "SO_RCVBUF set to %d", sobuf_size)); } } } if (turn_sock->setting.so_sndbuf_size > 0) { unsigned sobuf_size = turn_sock->setting.so_sndbuf_size; status = pj_sock_setsockopt_sobuf(sock, pj_SO_SNDBUF(), PJ_TRUE, &sobuf_size); if (status != PJ_SUCCESS) { pj_perror(3, turn_sock->obj_name, status, "Failed setting SO_SNDBUF"); } else { if (sobuf_size < turn_sock->setting.so_sndbuf_size) { PJ_LOG(4, (turn_sock->obj_name, "Warning! Cannot set SO_SNDBUF as configured," " now=%d, configured=%d", sobuf_size, turn_sock->setting.so_sndbuf_size)); } else { PJ_LOG(5, (turn_sock->obj_name, "SO_SNDBUF set to %d", sobuf_size)); } } } /* Create active socket */ pj_activesock_cfg_default(&asock_cfg); asock_cfg.grp_lock = turn_sock->grp_lock; pj_bzero(&asock_cb, sizeof(asock_cb)); asock_cb.on_data_read = &on_data_read; asock_cb.on_connect_complete = &on_connect_complete; status = pj_activesock_create(turn_sock->pool, sock, sock_type, &asock_cfg, turn_sock->cfg.ioqueue, &asock_cb, turn_sock, &turn_sock->active_sock); if (status != PJ_SUCCESS) { pj_turn_sock_destroy(turn_sock); return; } PJ_LOG(5,(turn_sock->pool->obj_name, "Connecting to %s", pj_sockaddr_print(&info.server, addrtxt, sizeof(addrtxt), 3))); /* Initiate non-blocking connect */ #if PJ_HAS_TCP status=pj_activesock_start_connect(turn_sock->active_sock, turn_sock->pool, &info.server, pj_sockaddr_get_len(&info.server)); if (status == PJ_SUCCESS) { on_connect_complete(turn_sock->active_sock, PJ_SUCCESS); } else if (status != PJ_EPENDING) { pj_turn_sock_destroy(turn_sock); return; } #else on_connect_complete(turn_sock->active_sock, PJ_SUCCESS); #endif /* Done for now. Subsequent work will be done in * on_connect_complete() callback. */ } if (new_state >= PJ_TURN_STATE_DESTROYING && turn_sock->sess) { pj_time_val delay = {0, 0}; turn_sock->sess = NULL; pj_turn_session_set_user_data(sess, NULL); pj_timer_heap_cancel_if_active(turn_sock->cfg.timer_heap, &turn_sock->timer, 0); pj_timer_heap_schedule_w_grp_lock(turn_sock->cfg.timer_heap, &turn_sock->timer, &delay, TIMER_DESTROY, turn_sock->grp_lock); } } ================================================ FILE: deps/pjsip/pjnath/src/pjturn-client/client_main.c ================================================ /* $Id: client_main.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #define THIS_FILE "client_main.c" #define LOCAL_PORT 1998 #define BANDWIDTH 64 /* -1 to disable */ #define LIFETIME 600 /* -1 to disable */ #define REQ_TRANSPORT -1 /* 0: udp, 1: tcp, -1: disable */ #define REQ_PORT_PROPS -1 /* -1 to disable */ #define REQ_IP 0 /* IP address string */ //#define OPTIONS PJ_STUN_NO_AUTHENTICATE #define OPTIONS 0 struct peer { pj_stun_sock *stun_sock; pj_sockaddr mapped_addr; }; static struct global { pj_caching_pool cp; pj_pool_t *pool; pj_stun_config stun_config; pj_thread_t *thread; pj_bool_t quit; pj_dns_resolver *resolver; pj_turn_sock *relay; pj_sockaddr relay_addr; struct peer peer[2]; } g; static struct options { pj_bool_t use_tcp; char *srv_addr; char *srv_port; char *realm; char *user_name; char *password; pj_bool_t use_fingerprint; char *stun_server; char *nameserver; } o; static int worker_thread(void *unused); static void turn_on_rx_data(pj_turn_sock *relay, void *pkt, unsigned pkt_len, const pj_sockaddr_t *peer_addr, unsigned addr_len); static void turn_on_state(pj_turn_sock *relay, pj_turn_state_t old_state, pj_turn_state_t new_state); static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, pj_stun_sock_op op, pj_status_t status); static pj_bool_t stun_sock_on_rx_data(pj_stun_sock *stun_sock, void *pkt, unsigned pkt_len, const pj_sockaddr_t *src_addr, unsigned addr_len); static void my_perror(const char *title, pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(3,(THIS_FILE, "%s: %s", title, errmsg)); } #define CHECK(expr) status=expr; \ if (status!=PJ_SUCCESS) { \ my_perror(#expr, status); \ return status; \ } static int init() { int i; pj_status_t status; CHECK( pj_init() ); CHECK( pjlib_util_init() ); CHECK( pjnath_init() ); /* Check that server is specified */ if (!o.srv_addr) { printf("Error: server must be specified\n"); return PJ_EINVAL; } pj_caching_pool_init(&g.cp, &pj_pool_factory_default_policy, 0); g.pool = pj_pool_create(&g.cp.factory, "main", 1000, 1000, NULL); /* Init global STUN config */ pj_stun_config_init(&g.stun_config, &g.cp.factory, 0, NULL, NULL); /* Create global timer heap */ CHECK( pj_timer_heap_create(g.pool, 1000, &g.stun_config.timer_heap) ); /* Create global ioqueue */ CHECK( pj_ioqueue_create(g.pool, 16, &g.stun_config.ioqueue) ); /* * Create peers */ for (i=0; i<(int)PJ_ARRAY_SIZE(g.peer); ++i) { pj_stun_sock_cb stun_sock_cb; char name[] = "peer0"; pj_uint16_t port; pj_stun_sock_cfg ss_cfg; pj_str_t server; pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb)); stun_sock_cb.on_rx_data = &stun_sock_on_rx_data; stun_sock_cb.on_status = &stun_sock_on_status; g.peer[i].mapped_addr.addr.sa_family = pj_AF_INET(); pj_stun_sock_cfg_default(&ss_cfg); #if 1 /* make reading the log easier */ ss_cfg.ka_interval = 300; #endif name[strlen(name)-1] = '0'+i; status = pj_stun_sock_create(&g.stun_config, name, pj_AF_INET(), &stun_sock_cb, &ss_cfg, &g.peer[i], &g.peer[i].stun_sock); if (status != PJ_SUCCESS) { my_perror("pj_stun_sock_create()", status); return status; } if (o.stun_server) { server = pj_str(o.stun_server); port = PJ_STUN_PORT; } else { server = pj_str(o.srv_addr); port = (pj_uint16_t)(o.srv_port?atoi(o.srv_port):PJ_STUN_PORT); } status = pj_stun_sock_start(g.peer[i].stun_sock, &server, port, NULL); if (status != PJ_SUCCESS) { my_perror("pj_stun_sock_start()", status); return status; } } /* Start the worker thread */ CHECK( pj_thread_create(g.pool, "stun", &worker_thread, NULL, 0, 0, &g.thread) ); return PJ_SUCCESS; } static int client_shutdown() { unsigned i; if (g.thread) { g.quit = 1; pj_thread_join(g.thread); pj_thread_destroy(g.thread); g.thread = NULL; } if (g.relay) { pj_turn_sock_destroy(g.relay); g.relay = NULL; } for (i=0; i %s", pj_turn_state_name(old_state), pj_turn_state_name(new_state))); if (new_state == PJ_TURN_STATE_READY) { pj_turn_session_info info; pj_turn_sock_get_info(relay, &info); pj_memcpy(&g.relay_addr, &info.relay_addr, sizeof(pj_sockaddr)); } else if (new_state > PJ_TURN_STATE_READY && g.relay) { PJ_LOG(3,(THIS_FILE, "Relay shutting down..")); g.relay = NULL; } } static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, pj_stun_sock_op op, pj_status_t status) { struct peer *peer = (struct peer*) pj_stun_sock_get_user_data(stun_sock); if (status == PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "peer%d: %s success", peer-g.peer, pj_stun_sock_op_name(op))); } else { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(1,(THIS_FILE, "peer%d: %s error: %s", peer-g.peer, pj_stun_sock_op_name(op), errmsg)); return PJ_FALSE; } if (op==PJ_STUN_SOCK_BINDING_OP || op==PJ_STUN_SOCK_KEEP_ALIVE_OP) { pj_stun_sock_info info; int cmp; pj_stun_sock_get_info(stun_sock, &info); cmp = pj_sockaddr_cmp(&info.mapped_addr, &peer->mapped_addr); if (cmp) { char straddr[PJ_INET6_ADDRSTRLEN+10]; pj_sockaddr_cp(&peer->mapped_addr, &info.mapped_addr); pj_sockaddr_print(&peer->mapped_addr, straddr, sizeof(straddr), 3); PJ_LOG(3,(THIS_FILE, "peer%d: STUN mapped address is %s", peer-g.peer, straddr)); } } return PJ_TRUE; } static pj_bool_t stun_sock_on_rx_data(pj_stun_sock *stun_sock, void *pkt, unsigned pkt_len, const pj_sockaddr_t *src_addr, unsigned addr_len) { struct peer *peer = (struct peer*) pj_stun_sock_get_user_data(stun_sock); char straddr[PJ_INET6_ADDRSTRLEN+10]; ((char*)pkt)[pkt_len] = '\0'; pj_sockaddr_print(src_addr, straddr, sizeof(straddr), 3); PJ_LOG(3,(THIS_FILE, "peer%d: received %d bytes data from %s: %s", peer-g.peer, pkt_len, straddr, (char*)pkt)); return PJ_TRUE; } static void menu(void) { pj_turn_session_info info; char client_state[20], relay_addr[80], peer0_addr[80], peer1_addr[80]; if (g.relay) { pj_turn_sock_get_info(g.relay, &info); strcpy(client_state, pj_turn_state_name(info.state)); if (info.state >= PJ_TURN_STATE_READY) pj_sockaddr_print(&info.relay_addr, relay_addr, sizeof(relay_addr), 3); else strcpy(relay_addr, "0.0.0.0:0"); } else { strcpy(client_state, "NULL"); strcpy(relay_addr, "0.0.0.0:0"); } pj_sockaddr_print(&g.peer[0].mapped_addr, peer0_addr, sizeof(peer0_addr), 3); pj_sockaddr_print(&g.peer[1].mapped_addr, peer1_addr, sizeof(peer1_addr), 3); puts("\n"); puts("+=====================================================================+"); puts("| CLIENT | PEER-0 |"); puts("| | |"); printf("| State : %-12s | Address: %-21s |\n", client_state, peer0_addr); printf("| Relay addr: %-21s | |\n", relay_addr); puts("| | 0 Send data to relay address |"); puts("| a Allocate relay | |"); puts("| p,pp Set permission for peer 0/1 +--------------------------------+"); puts("| s,ss Send data to peer 0/1 | PEER-1 |"); puts("| b,bb BindChannel to peer 0/1 | |"); printf("| x Delete allocation | Address: %-21s |\n", peer1_addr); puts("+------------------------------------+ |"); puts("| q Quit d Dump | 1 Send data to relay adderss |"); puts("+------------------------------------+--------------------------------+"); printf(">>> "); fflush(stdout); } static void console_main(void) { while (!g.quit) { char input[32]; struct peer *peer; pj_status_t status; menu(); if (fgets(input, sizeof(input), stdin) == NULL) break; switch (input[0]) { case 'a': create_relay(); break; case 'd': pj_pool_factory_dump(&g.cp.factory, PJ_TRUE); break; case 's': if (g.relay == NULL) { puts("Error: no relay"); continue; } if (input[1]!='s') peer = &g.peer[0]; else peer = &g.peer[1]; strcpy(input, "Hello from client"); status = pj_turn_sock_sendto(g.relay, (const pj_uint8_t*)input, strlen(input)+1, &peer->mapped_addr, pj_sockaddr_get_len(&peer->mapped_addr)); if (status != PJ_SUCCESS) my_perror("turn_udp_sendto() failed", status); break; case 'b': if (g.relay == NULL) { puts("Error: no relay"); continue; } if (input[1]!='b') peer = &g.peer[0]; else peer = &g.peer[1]; status = pj_turn_sock_bind_channel(g.relay, &peer->mapped_addr, pj_sockaddr_get_len(&peer->mapped_addr)); if (status != PJ_SUCCESS) my_perror("turn_udp_bind_channel() failed", status); break; case 'p': if (g.relay == NULL) { puts("Error: no relay"); continue; } if (input[1]!='p') peer = &g.peer[0]; else peer = &g.peer[1]; status = pj_turn_sock_set_perm(g.relay, 1, &peer->mapped_addr, 1); if (status != PJ_SUCCESS) my_perror("pj_turn_sock_set_perm() failed", status); break; case 'x': if (g.relay == NULL) { puts("Error: no relay"); continue; } destroy_relay(); break; case '0': case '1': if (g.relay == NULL) { puts("No relay"); break; } peer = &g.peer[input[0]-'0']; sprintf(input, "Hello from peer%d", input[0]-'0'); pj_stun_sock_sendto(peer->stun_sock, NULL, input, strlen(input)+1, 0, &g.relay_addr, pj_sockaddr_get_len(&g.relay_addr)); break; case 'q': g.quit = PJ_TRUE; break; } } } static void usage(void) { puts("Usage: pjturn_client TURN-SERVER [OPTIONS]"); puts(""); puts("where TURN-SERVER is \"host[:port]\""); puts(""); puts("and OPTIONS:"); puts(" --tcp, -T Use TCP to connect to TURN server"); puts(" --realm, -r REALM Set realm of the credential to REALM"); puts(" --username, -u UID Set username of the credential to UID"); puts(" --password, -p PASSWD Set password of the credential to PASSWD"); puts(" --fingerprint, -F Use fingerprint for outgoing requests"); puts(" --stun-srv, -S NAME Use this STUN srv instead of TURN for Binding discovery"); puts(" --nameserver, -N IP Activate DNS SRV, use this DNS server"); puts(" --help, -h"); } int main(int argc, char *argv[]) { struct pj_getopt_option long_options[] = { { "realm", 1, 0, 'r'}, { "username", 1, 0, 'u'}, { "password", 1, 0, 'p'}, { "fingerprint",0, 0, 'F'}, { "tcp", 0, 0, 'T'}, { "help", 0, 0, 'h'}, { "stun-srv", 1, 0, 'S'}, { "nameserver", 1, 0, 'N'} }; int c, opt_id; char *pos; pj_status_t status; while((c=pj_getopt_long(argc,argv, "r:u:p:S:N:hFT", long_options, &opt_id))!=-1) { switch (c) { case 'r': o.realm = pj_optarg; break; case 'u': o.user_name = pj_optarg; break; case 'p': o.password = pj_optarg; break; case 'h': usage(); return 0; case 'F': o.use_fingerprint = PJ_TRUE; break; case 'T': o.use_tcp = PJ_TRUE; break; case 'S': o.stun_server = pj_optarg; break; case 'N': o.nameserver = pj_optarg; break; default: printf("Argument \"%s\" is not valid. Use -h to see help", argv[pj_optind]); return 1; } } if (pj_optind == argc) { puts("Error: TARGET is needed"); usage(); return 1; } if ((pos=pj_ansi_strchr(argv[pj_optind], ':')) != NULL) { o.srv_addr = argv[pj_optind]; *pos = '\0'; o.srv_port = pos+1; } else { o.srv_addr = argv[pj_optind]; } if ((status=init()) != 0) goto on_return; //if ((status=create_relay()) != 0) // goto on_return; console_main(); on_return: client_shutdown(); return status ? 1 : 0; } ================================================ FILE: deps/pjsip/pjnath/src/pjturn-srv/allocation.c ================================================ /* $Id: allocation.c 4360 2013-02-21 11:26:35Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "turn.h" #include "auth.h" #define THIS_FILE "allocation.c" enum { TIMER_ID_NONE, TIMER_ID_TIMEOUT, TIMER_ID_DESTROY }; #define DESTROY_DELAY {0, 500} #define PEER_TABLE_SIZE 32 #define MAX_CLIENT_BANDWIDTH 128 /* In Kbps */ #define DEFA_CLIENT_BANDWIDTH 64 #define MIN_LIFETIME 30 #define MAX_LIFETIME 600 #define DEF_LIFETIME 300 /* Parsed Allocation request. */ typedef struct alloc_request { unsigned tp_type; /* Requested transport */ char addr[PJ_INET6_ADDRSTRLEN]; /* Requested IP */ unsigned bandwidth; /* Requested bandwidth */ unsigned lifetime; /* Lifetime. */ unsigned rpp_bits; /* A bits */ unsigned rpp_port; /* Requested port */ } alloc_request; /* Prototypes */ static void destroy_allocation(pj_turn_allocation *alloc); static pj_status_t create_relay(pj_turn_srv *srv, pj_turn_allocation *alloc, const pj_stun_msg *msg, const alloc_request *req, pj_turn_relay_res *relay); static void destroy_relay(pj_turn_relay_res *relay); static void on_rx_from_peer(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read); static pj_status_t stun_on_send_msg(pj_stun_session *sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len); static pj_status_t stun_on_rx_request(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_rx_data *rdata, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len); static pj_status_t stun_on_rx_indication(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_msg *msg, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len); /* Log allocation error */ static void alloc_err(pj_turn_allocation *alloc, const char *title, pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(alloc->obj_name, "%s for client %s: %s", title, alloc->info, errmsg)); } /* Parse ALLOCATE request */ static pj_status_t parse_allocate_req(alloc_request *cfg, pj_stun_session *sess, const pj_stun_rx_data *rdata, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { const pj_stun_msg *req = rdata->msg; pj_stun_bandwidth_attr *attr_bw; pj_stun_req_transport_attr *attr_req_tp; pj_stun_res_token_attr *attr_res_token; pj_stun_lifetime_attr *attr_lifetime; pj_bzero(cfg, sizeof(*cfg)); /* Get BANDWIDTH attribute, if any. */ attr_bw = (pj_stun_uint_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_BANDWIDTH, 0); if (attr_bw) { cfg->bandwidth = attr_bw->value; } else { cfg->bandwidth = DEFA_CLIENT_BANDWIDTH; } /* Check if we can satisfy the bandwidth */ if (cfg->bandwidth > MAX_CLIENT_BANDWIDTH) { pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ALLOCATION_QUOTA_REACHED, "Invalid bandwidth", NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_ALLOCATION_QUOTA_REACHED); } /* MUST have REQUESTED-TRANSPORT attribute */ attr_req_tp = (pj_stun_uint_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_TRANSPORT, 0); if (attr_req_tp == NULL) { pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, "Missing REQUESTED-TRANSPORT attribute", NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); } cfg->tp_type = PJ_STUN_GET_RT_PROTO(attr_req_tp->value); /* Can only support UDP for now */ if (cfg->tp_type != PJ_TURN_TP_UDP) { pj_stun_session_respond(sess, rdata, PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO, NULL, NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO); } /* Get RESERVATION-TOKEN attribute, if any */ attr_res_token = (pj_stun_res_token_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_RESERVATION_TOKEN, 0); if (attr_res_token) { /* We don't support RESERVATION-TOKEN for now */ pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, "RESERVATION-TOKEN is not supported", NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); } /* Get LIFETIME attribute */ attr_lifetime = (pj_stun_uint_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_LIFETIME, 0); if (attr_lifetime) { cfg->lifetime = attr_lifetime->value; if (cfg->lifetime < MIN_LIFETIME) { pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, "LIFETIME too short", NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); } if (cfg->lifetime > MAX_LIFETIME) cfg->lifetime = MAX_LIFETIME; } else { cfg->lifetime = DEF_LIFETIME; } return PJ_SUCCESS; } /* Respond to ALLOCATE request */ static pj_status_t send_allocate_response(pj_turn_allocation *alloc, pj_stun_session *srv_sess, pj_turn_transport *transport, const pj_stun_rx_data *rdata) { pj_stun_tx_data *tdata; pj_status_t status; /* Respond the original ALLOCATE request */ status = pj_stun_session_create_res(srv_sess, rdata, 0, NULL, &tdata); if (status != PJ_SUCCESS) return status; /* Add XOR-RELAYED-ADDRESS attribute */ pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_XOR_RELAYED_ADDR, PJ_TRUE, &alloc->relay.hkey.addr, pj_sockaddr_get_len(&alloc->relay.hkey.addr)); /* Add LIFETIME. */ pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_LIFETIME, (unsigned)alloc->relay.lifetime); /* Add BANDWIDTH */ pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_BANDWIDTH, alloc->bandwidth); /* Add RESERVATION-TOKEN */ PJ_TODO(ADD_RESERVATION_TOKEN); /* Add XOR-MAPPED-ADDRESS */ pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr)); /* Send the response */ return pj_stun_session_send_msg(srv_sess, transport, PJ_TRUE, PJ_FALSE, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr), tdata); } /* * Init credential for the allocation. We use static credential, meaning that * the user's password must not change during allocation. */ static pj_status_t init_cred(pj_turn_allocation *alloc, const pj_stun_msg *req) { const pj_stun_username_attr *user; const pj_stun_realm_attr *realm; const pj_stun_nonce_attr *nonce; pj_status_t status; realm = (const pj_stun_realm_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REALM, 0); PJ_ASSERT_RETURN(realm != NULL, PJ_EBUG); user = (const pj_stun_username_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0); PJ_ASSERT_RETURN(user != NULL, PJ_EBUG); nonce = (const pj_stun_nonce_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_NONCE, 0); PJ_ASSERT_RETURN(nonce != NULL, PJ_EBUG); /* Lookup the password */ status = pj_turn_get_password(NULL, NULL, &realm->value, &user->value, alloc->pool, &alloc->cred.data.static_cred.data_type, &alloc->cred.data.static_cred.data); if (status != PJ_SUCCESS) return status; /* Save credential */ alloc->cred.type = PJ_STUN_AUTH_CRED_STATIC; pj_strdup(alloc->pool, &alloc->cred.data.static_cred.realm, &realm->value); pj_strdup(alloc->pool, &alloc->cred.data.static_cred.username, &user->value); pj_strdup(alloc->pool, &alloc->cred.data.static_cred.nonce, &nonce->value); return PJ_SUCCESS; } /* * Create new allocation. */ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_transport *transport, const pj_sockaddr_t *src_addr, unsigned src_addr_len, const pj_stun_rx_data *rdata, pj_stun_session *srv_sess, pj_turn_allocation **p_alloc) { pj_turn_srv *srv = transport->listener->server; const pj_stun_msg *msg = rdata->msg; pj_pool_t *pool; alloc_request req; pj_turn_allocation *alloc; pj_stun_session_cb sess_cb; char str_tmp[80]; pj_status_t status; /* Parse ALLOCATE request */ status = parse_allocate_req(&req, srv_sess, rdata, src_addr, src_addr_len); if (status != PJ_SUCCESS) return status; pool = pj_pool_create(srv->core.pf, "alloc%p", 1000, 1000, NULL); /* Init allocation structure */ alloc = PJ_POOL_ZALLOC_T(pool, pj_turn_allocation); alloc->pool = pool; alloc->obj_name = pool->obj_name; alloc->relay.tp.sock = PJ_INVALID_SOCKET; alloc->server = transport->listener->server; alloc->bandwidth = req.bandwidth; /* Set transport */ alloc->transport = transport; pj_turn_transport_add_ref(transport, alloc); alloc->hkey.tp_type = transport->listener->tp_type; pj_memcpy(&alloc->hkey.clt_addr, src_addr, src_addr_len); status = pj_lock_create_recursive_mutex(pool, alloc->obj_name, &alloc->lock); if (status != PJ_SUCCESS) { goto on_error; } /* Create peer hash table */ alloc->peer_table = pj_hash_create(pool, PEER_TABLE_SIZE); /* Create channel hash table */ alloc->ch_table = pj_hash_create(pool, PEER_TABLE_SIZE); /* Print info */ pj_ansi_strcpy(alloc->info, pj_turn_tp_type_name(transport->listener->tp_type)); alloc->info[3] = ':'; pj_sockaddr_print(src_addr, alloc->info+4, sizeof(alloc->info)-4, 3); /* Create STUN session to handle STUN communication with client */ pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_send_msg = &stun_on_send_msg; sess_cb.on_rx_request = &stun_on_rx_request; sess_cb.on_rx_indication = &stun_on_rx_indication; status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name, &sess_cb, PJ_FALSE, NULL, &alloc->sess); if (status != PJ_SUCCESS) { goto on_error; } /* Attach to STUN session */ pj_stun_session_set_user_data(alloc->sess, alloc); /* Init authentication credential */ status = init_cred(alloc, msg); if (status != PJ_SUCCESS) { goto on_error; } /* Attach authentication credential to STUN session */ pj_stun_session_set_credential(alloc->sess, PJ_STUN_AUTH_LONG_TERM, &alloc->cred); /* Create the relay resource */ status = create_relay(srv, alloc, msg, &req, &alloc->relay); if (status != PJ_SUCCESS) { goto on_error; } /* Register this allocation */ pj_turn_srv_register_allocation(srv, alloc); /* Respond to ALLOCATE request */ status = send_allocate_response(alloc, srv_sess, transport, rdata); if (status != PJ_SUCCESS) goto on_error; /* Done */ pj_sockaddr_print(&alloc->relay.hkey.addr, str_tmp, sizeof(str_tmp), 3); PJ_LOG(4,(alloc->obj_name, "Client %s created, relay addr=%s:%s", alloc->info, pj_turn_tp_type_name(req.tp_type), str_tmp)); /* Success */ *p_alloc = alloc; return PJ_SUCCESS; on_error: /* Send reply to the ALLOCATE request */ pj_strerror(status, str_tmp, sizeof(str_tmp)); pj_stun_session_respond(srv_sess, rdata, PJ_STUN_SC_BAD_REQUEST, str_tmp, transport, PJ_TRUE, src_addr, src_addr_len); /* Cleanup */ destroy_allocation(alloc); return status; } /* Destroy relay resource */ static void destroy_relay(pj_turn_relay_res *relay) { if (relay->timer.id) { pj_timer_heap_cancel(relay->allocation->server->core.timer_heap, &relay->timer); relay->timer.id = PJ_FALSE; } if (relay->tp.key) { pj_ioqueue_unregister(relay->tp.key); relay->tp.key = NULL; relay->tp.sock = PJ_INVALID_SOCKET; } else if (relay->tp.sock != PJ_INVALID_SOCKET) { pj_sock_close(relay->tp.sock); relay->tp.sock = PJ_INVALID_SOCKET; } /* Mark as shutdown */ relay->lifetime = 0; } /* * Really destroy allocation. */ static void destroy_allocation(pj_turn_allocation *alloc) { pj_pool_t *pool; /* Unregister this allocation */ pj_turn_srv_unregister_allocation(alloc->server, alloc); /* Destroy relay */ destroy_relay(&alloc->relay); /* Must lock only after destroying relay otherwise deadlock */ if (alloc->lock) { pj_lock_acquire(alloc->lock); } /* Unreference transport */ if (alloc->transport) { pj_turn_transport_dec_ref(alloc->transport, alloc); alloc->transport = NULL; } /* Destroy STUN session */ if (alloc->sess) { pj_stun_session_destroy(alloc->sess); alloc->sess = NULL; } /* Destroy lock */ if (alloc->lock) { pj_lock_release(alloc->lock); pj_lock_destroy(alloc->lock); alloc->lock = NULL; } /* Destroy pool */ pool = alloc->pool; if (pool) { alloc->pool = NULL; pj_pool_release(pool); } } PJ_DECL(void) pj_turn_allocation_destroy(pj_turn_allocation *alloc) { destroy_allocation(alloc); } /* * Handle transport closure. */ PJ_DEF(void) pj_turn_allocation_on_transport_closed( pj_turn_allocation *alloc, pj_turn_transport *tp) { PJ_LOG(5,(alloc->obj_name, "Transport %s unexpectedly closed, destroying " "allocation %s", tp->info, alloc->info)); pj_turn_transport_dec_ref(tp, alloc); alloc->transport = NULL; destroy_allocation(alloc); } /* Initiate shutdown sequence for this allocation and start destroy timer. * Once allocation is marked as shutting down, any packets will be * rejected/discarded */ static void alloc_shutdown(pj_turn_allocation *alloc) { pj_time_val destroy_delay = DESTROY_DELAY; /* Work with existing schedule */ if (alloc->relay.timer.id == TIMER_ID_TIMEOUT) { /* Cancel existing shutdown timer */ pj_timer_heap_cancel(alloc->server->core.timer_heap, &alloc->relay.timer); alloc->relay.timer.id = TIMER_ID_NONE; } else if (alloc->relay.timer.id == TIMER_ID_DESTROY) { /* We've been scheduled to be destroyed, ignore this * shutdown request. */ return; } pj_assert(alloc->relay.timer.id == TIMER_ID_NONE); /* Shutdown relay socket */ destroy_relay(&alloc->relay); /* Don't unregister from hash table because we still need to * handle REFRESH retransmission. */ /* Schedule destroy timer */ alloc->relay.timer.id = TIMER_ID_DESTROY; pj_timer_heap_schedule(alloc->server->core.timer_heap, &alloc->relay.timer, &destroy_delay); } /* Reschedule timeout using current lifetime setting */ static pj_status_t resched_timeout(pj_turn_allocation *alloc) { pj_time_val delay; pj_status_t status; pj_gettimeofday(&alloc->relay.expiry); alloc->relay.expiry.sec += alloc->relay.lifetime; pj_assert(alloc->relay.timer.id != TIMER_ID_DESTROY); if (alloc->relay.timer.id != 0) { pj_timer_heap_cancel(alloc->server->core.timer_heap, &alloc->relay.timer); alloc->relay.timer.id = TIMER_ID_NONE; } delay.sec = alloc->relay.lifetime; delay.msec = 0; alloc->relay.timer.id = TIMER_ID_TIMEOUT; status = pj_timer_heap_schedule(alloc->server->core.timer_heap, &alloc->relay.timer, &delay); if (status != PJ_SUCCESS) { alloc->relay.timer.id = TIMER_ID_NONE; return status; } return PJ_SUCCESS; } /* Timer timeout callback */ static void relay_timeout_cb(pj_timer_heap_t *heap, pj_timer_entry *e) { pj_turn_relay_res *rel; pj_turn_allocation *alloc; PJ_UNUSED_ARG(heap); rel = (pj_turn_relay_res*) e->user_data; alloc = rel->allocation; if (e->id == TIMER_ID_TIMEOUT) { e->id = TIMER_ID_NONE; PJ_LOG(4,(alloc->obj_name, "Client %s refresh timed-out, shutting down..", alloc->info)); alloc_shutdown(alloc); } else if (e->id == TIMER_ID_DESTROY) { e->id = TIMER_ID_NONE; PJ_LOG(4,(alloc->obj_name, "Client %s destroying..", alloc->info)); destroy_allocation(alloc); } } /* * Create relay. */ static pj_status_t create_relay(pj_turn_srv *srv, pj_turn_allocation *alloc, const pj_stun_msg *msg, const alloc_request *req, pj_turn_relay_res *relay) { enum { RETRY = 40 }; pj_pool_t *pool = alloc->pool; int retry, retry_max, sock_type; pj_ioqueue_callback icb; int af, namelen; pj_stun_string_attr *sa; pj_status_t status; pj_bzero(relay, sizeof(*relay)); relay->allocation = alloc; relay->tp.sock = PJ_INVALID_SOCKET; /* TODO: get the requested address family from somewhere */ af = alloc->transport->listener->addr.addr.sa_family; /* Save realm */ sa = (pj_stun_string_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0); PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP); pj_strdup(pool, &relay->realm, &sa->value); /* Save username */ sa = (pj_stun_string_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0); PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP); pj_strdup(pool, &relay->user, &sa->value); /* Lifetime and timeout */ relay->lifetime = req->lifetime; pj_timer_entry_init(&relay->timer, TIMER_ID_NONE, relay, &relay_timeout_cb); resched_timeout(alloc); /* Transport type */ relay->hkey.tp_type = req->tp_type; /* Create the socket */ if (req->tp_type == PJ_TURN_TP_UDP) { sock_type = pj_SOCK_DGRAM(); } else if (req->tp_type == PJ_TURN_TP_TCP) { sock_type = pj_SOCK_STREAM(); } else { pj_assert(!"Unknown transport"); return PJ_EINVALIDOP; } status = pj_sock_socket(af, sock_type, 0, &relay->tp.sock); if (status != PJ_SUCCESS) { pj_bzero(relay, sizeof(*relay)); return status; } /* Find suitable port for this allocation */ if (req->rpp_port) { retry_max = 1; } else { retry_max = RETRY; } for (retry=0; retrycore.lock); if (req->rpp_port) { port = (pj_uint16_t) req->rpp_port; } else if (req->tp_type == PJ_TURN_TP_UDP) { port = (pj_uint16_t) srv->ports.next_udp++; if (srv->ports.next_udp > srv->ports.max_udp) srv->ports.next_udp = srv->ports.min_udp; } else if (req->tp_type == PJ_TURN_TP_TCP) { port = (pj_uint16_t) srv->ports.next_tcp++; if (srv->ports.next_tcp > srv->ports.max_tcp) srv->ports.next_tcp = srv->ports.min_tcp; } else { pj_assert(!"Invalid transport"); port = 0; } pj_lock_release(srv->core.lock); pj_sockaddr_init(af, &bound_addr, NULL, port); status = pj_sock_bind(relay->tp.sock, &bound_addr, pj_sockaddr_get_len(&bound_addr)); if (status == PJ_SUCCESS) break; } if (status != PJ_SUCCESS) { /* Unable to allocate port */ PJ_LOG(4,(THIS_FILE, "Unable to allocate relay, giving up: err %d", status)); pj_sock_close(relay->tp.sock); relay->tp.sock = PJ_INVALID_SOCKET; return status; } /* Init relay key */ namelen = sizeof(relay->hkey.addr); status = pj_sock_getsockname(relay->tp.sock, &relay->hkey.addr, &namelen); if (status != PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "pj_sock_getsockname() failed: err %d", status)); pj_sock_close(relay->tp.sock); relay->tp.sock = PJ_INVALID_SOCKET; return status; } if (!pj_sockaddr_has_addr(&relay->hkey.addr)) { pj_sockaddr_copy_addr(&relay->hkey.addr, &alloc->transport->listener->addr); } if (!pj_sockaddr_has_addr(&relay->hkey.addr)) { pj_sockaddr tmp_addr; pj_gethostip(af, &tmp_addr); pj_sockaddr_copy_addr(&relay->hkey.addr, &tmp_addr); } /* Init ioqueue */ pj_bzero(&icb, sizeof(icb)); icb.on_read_complete = &on_rx_from_peer; status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, relay->tp.sock, relay, &icb, &relay->tp.key); if (status != PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "pj_ioqueue_register_sock() failed: err %d", status)); pj_sock_close(relay->tp.sock); relay->tp.sock = PJ_INVALID_SOCKET; return status; } /* Kick off pending read operation */ pj_ioqueue_op_key_init(&relay->tp.read_key, sizeof(relay->tp.read_key)); on_rx_from_peer(relay->tp.key, &relay->tp.read_key, 0); /* Done */ return PJ_SUCCESS; } /* Create and send error response */ static void send_reply_err(pj_turn_allocation *alloc, const pj_stun_rx_data *rdata, pj_bool_t cache, int code, const char *errmsg) { pj_status_t status; status = pj_stun_session_respond(alloc->sess, rdata, code, errmsg, NULL, cache, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr.addr)); if (status != PJ_SUCCESS) { alloc_err(alloc, "Error sending STUN error response", status); return; } } /* Create and send successful response */ static void send_reply_ok(pj_turn_allocation *alloc, const pj_stun_rx_data *rdata) { pj_status_t status; unsigned interval; pj_stun_tx_data *tdata; status = pj_stun_session_create_res(alloc->sess, rdata, 0, NULL, &tdata); if (status != PJ_SUCCESS) { alloc_err(alloc, "Error creating STUN success response", status); return; } /* Calculate time to expiration */ if (alloc->relay.lifetime != 0) { pj_time_val now; pj_gettimeofday(&now); interval = alloc->relay.expiry.sec - now.sec; } else { interval = 0; } /* Add LIFETIME if this is not ChannelBind. */ if (PJ_STUN_GET_METHOD(tdata->msg->hdr.type)!=PJ_STUN_CHANNEL_BIND_METHOD){ pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_LIFETIME, interval); /* Add BANDWIDTH if lifetime is not zero */ if (interval != 0) { pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_BANDWIDTH, alloc->bandwidth); } } status = pj_stun_session_send_msg(alloc->sess, NULL, PJ_TRUE, PJ_FALSE, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr), tdata); if (status != PJ_SUCCESS) { alloc_err(alloc, "Error sending STUN success response", status); return; } } /* Create new permission */ static pj_turn_permission *create_permission(pj_turn_allocation *alloc, const pj_sockaddr_t *peer_addr, unsigned addr_len) { pj_turn_permission *perm; perm = PJ_POOL_ZALLOC_T(alloc->pool, pj_turn_permission); pj_memcpy(&perm->hkey.peer_addr, peer_addr, addr_len); perm->allocation = alloc; perm->channel = PJ_TURN_INVALID_CHANNEL; pj_gettimeofday(&perm->expiry); perm->expiry.sec += PJ_TURN_PERM_TIMEOUT; /* Register to hash table (only the address part!) */ pj_hash_set(alloc->pool, alloc->peer_table, pj_sockaddr_get_addr(&perm->hkey.peer_addr), pj_sockaddr_get_addr_len(&perm->hkey.peer_addr), 0, perm); return perm; } /* Check if a permission isn't expired. Return NULL if expired. */ static pj_turn_permission *check_permission_expiry(pj_turn_permission *perm) { pj_turn_allocation *alloc = perm->allocation; pj_time_val now; pj_gettimeofday(&now); if (PJ_TIME_VAL_GT(perm->expiry, now)) { /* Permission has not expired */ return perm; } /* Remove from permission hash table */ pj_hash_set(NULL, alloc->peer_table, pj_sockaddr_get_addr(&perm->hkey.peer_addr), pj_sockaddr_get_addr_len(&perm->hkey.peer_addr), 0, NULL); /* Remove from channel hash table, if assigned a channel number */ if (perm->channel != PJ_TURN_INVALID_CHANNEL) { pj_hash_set(NULL, alloc->ch_table, &perm->channel, sizeof(perm->channel), 0, NULL); } return NULL; } /* Lookup permission in hash table by the peer address */ static pj_turn_permission* lookup_permission_by_addr(pj_turn_allocation *alloc, const pj_sockaddr_t *peer_addr, unsigned addr_len) { pj_turn_permission *perm; PJ_UNUSED_ARG(addr_len); /* Lookup in peer hash table */ perm = (pj_turn_permission*) pj_hash_get(alloc->peer_table, pj_sockaddr_get_addr(peer_addr), pj_sockaddr_get_addr_len(peer_addr), NULL); return perm ? check_permission_expiry(perm) : NULL; } /* Lookup permission in hash table by the channel number */ static pj_turn_permission* lookup_permission_by_chnum(pj_turn_allocation *alloc, unsigned chnum) { pj_uint16_t chnum16 = (pj_uint16_t)chnum; pj_turn_permission *perm; /* Lookup in peer hash table */ perm = (pj_turn_permission*) pj_hash_get(alloc->ch_table, &chnum16, sizeof(chnum16), NULL); return perm ? check_permission_expiry(perm) : NULL; } /* Update permission because of data from client to peer. * Return PJ_TRUE is permission is found. */ static pj_bool_t refresh_permission(pj_turn_permission *perm) { pj_gettimeofday(&perm->expiry); if (perm->channel == PJ_TURN_INVALID_CHANNEL) perm->expiry.sec += PJ_TURN_PERM_TIMEOUT; else perm->expiry.sec += PJ_TURN_CHANNEL_TIMEOUT; return PJ_TRUE; } /* * Handle incoming packet from client. This would have been called by * server upon receiving packet from a listener. */ PJ_DEF(void) pj_turn_allocation_on_rx_client_pkt(pj_turn_allocation *alloc, pj_turn_pkt *pkt) { pj_bool_t is_stun; pj_status_t status; /* Lock this allocation */ pj_lock_acquire(alloc->lock); /* Quickly check if this is STUN message */ is_stun = ((*((pj_uint8_t*)pkt->pkt) & 0xC0) == 0); if (is_stun) { /* * This could be an incoming STUN requests or indications. * Pass this through to the STUN session, which will call * our stun_on_rx_request() or stun_on_rx_indication() * callbacks. * * Note: currently it is necessary to specify the * PJ_STUN_NO_FINGERPRINT_CHECK otherwise the FINGERPRINT * attribute inside STUN Send Indication message will mess up * with fingerprint checking. */ unsigned options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK; pj_size_t parsed_len = 0; if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) options |= PJ_STUN_IS_DATAGRAM; status = pj_stun_session_on_rx_pkt(alloc->sess, pkt->pkt, pkt->len, options, NULL, &parsed_len, &pkt->src.clt_addr, pkt->src_addr_len); if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) { pkt->len = 0; } else if (parsed_len > 0) { if (parsed_len == pkt->len) { pkt->len = 0; } else { pj_memmove(pkt->pkt, pkt->pkt+parsed_len, pkt->len - parsed_len); pkt->len -= parsed_len; } } if (status != PJ_SUCCESS) { alloc_err(alloc, "Error handling STUN packet", status); goto on_return; } } else { /* * This is not a STUN packet, must be ChannelData packet. */ pj_turn_channel_data *cd = (pj_turn_channel_data*)pkt->pkt; pj_turn_permission *perm; pj_ssize_t len; pj_assert(sizeof(*cd)==4); /* For UDP check the packet length */ if (alloc->transport->listener->tp_type == PJ_TURN_TP_UDP) { if (pkt->len < pj_ntohs(cd->length)+sizeof(*cd)) { PJ_LOG(4,(alloc->obj_name, "ChannelData from %s discarded: UDP size error", alloc->info)); goto on_return; } } else { pj_assert(!"Unsupported transport"); goto on_return; } perm = lookup_permission_by_chnum(alloc, pj_ntohs(cd->ch_number)); if (!perm) { /* Discard */ PJ_LOG(4,(alloc->obj_name, "ChannelData from %s discarded: ch#0x%x not found", alloc->info, pj_ntohs(cd->ch_number))); goto on_return; } /* Relay the data */ len = pj_ntohs(cd->length); pj_sock_sendto(alloc->relay.tp.sock, cd+1, &len, 0, &perm->hkey.peer_addr, pj_sockaddr_get_len(&perm->hkey.peer_addr)); /* Refresh permission */ refresh_permission(perm); } on_return: /* Release lock */ pj_lock_release(alloc->lock); } /* * Handle incoming packet from peer. This function is called by * on_rx_from_peer(). */ static void handle_peer_pkt(pj_turn_allocation *alloc, pj_turn_relay_res *rel, char *pkt, pj_size_t len, const pj_sockaddr *src_addr) { pj_turn_permission *perm; /* Lookup permission */ perm = lookup_permission_by_addr(alloc, src_addr, pj_sockaddr_get_len(src_addr)); if (perm == NULL) { /* No permission, discard data */ return; } /* Send Data Indication or ChannelData, depends on whether * this permission is attached to a channel number. */ if (perm->channel != PJ_TURN_INVALID_CHANNEL) { /* Send ChannelData */ pj_turn_channel_data *cd = (pj_turn_channel_data*)rel->tp.tx_pkt; if (len > PJ_TURN_MAX_PKT_LEN) { char peer_addr[80]; pj_sockaddr_print(src_addr, peer_addr, sizeof(peer_addr), 3); PJ_LOG(4,(alloc->obj_name, "Client %s: discarded data from %s " "because it's too long (%d bytes)", alloc->info, peer_addr, len)); return; } /* Init header */ cd->ch_number = pj_htons(perm->channel); cd->length = pj_htons((pj_uint16_t)len); /* Copy data */ pj_memcpy(rel->tp.tx_pkt+sizeof(pj_turn_channel_data), pkt, len); /* Send to client */ alloc->transport->sendto(alloc->transport, rel->tp.tx_pkt, len+sizeof(pj_turn_channel_data), 0, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr)); } else { /* Send Data Indication */ pj_stun_tx_data *tdata; pj_status_t status; status = pj_stun_session_create_ind(alloc->sess, PJ_STUN_DATA_INDICATION, &tdata); if (status != PJ_SUCCESS) { alloc_err(alloc, "Error creating Data indication", status); return; } pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE, src_addr, pj_sockaddr_get_len(src_addr)); pj_stun_msg_add_binary_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_DATA, (const pj_uint8_t*)pkt, len); pj_stun_session_send_msg(alloc->sess, NULL, PJ_FALSE, PJ_FALSE, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr), tdata); } } /* * ioqueue notification on RX packets from the relay socket. */ static void on_rx_from_peer(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { pj_turn_relay_res *rel; pj_status_t status; rel = (pj_turn_relay_res*) pj_ioqueue_get_user_data(key); /* Lock the allocation */ pj_lock_acquire(rel->allocation->lock); do { if (bytes_read > 0) { handle_peer_pkt(rel->allocation, rel, rel->tp.rx_pkt, bytes_read, &rel->tp.src_addr); } /* Read next packet */ bytes_read = sizeof(rel->tp.rx_pkt); rel->tp.src_addr_len = sizeof(rel->tp.src_addr); status = pj_ioqueue_recvfrom(key, op_key, rel->tp.rx_pkt, &bytes_read, 0, &rel->tp.src_addr, &rel->tp.src_addr_len); if (status != PJ_EPENDING && status != PJ_SUCCESS) bytes_read = -status; } while (status != PJ_EPENDING && status != PJ_ECANCELLED); /* Release allocation lock */ pj_lock_release(rel->allocation->lock); } /* * Callback notification from STUN session when it wants to send * a STUN message towards the client. */ static pj_status_t stun_on_send_msg(pj_stun_session *sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len) { pj_turn_allocation *alloc; PJ_UNUSED_ARG(token); alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess); return alloc->transport->sendto(alloc->transport, pkt, pkt_size, 0, dst_addr, addr_len); } /* * Callback notification from STUN session when it receives STUN * requests. This callback was trigger by STUN incoming message * processing in pj_turn_allocation_on_rx_client_pkt(). */ static pj_status_t stun_on_rx_request(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_rx_data *rdata, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { const pj_stun_msg *msg = rdata->msg; pj_turn_allocation *alloc; PJ_UNUSED_ARG(pkt); PJ_UNUSED_ARG(pkt_len); PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess); /* Refuse to serve any request if we've been shutdown */ if (alloc->relay.lifetime == 0) { /* Reject with 437 if we're shutting down */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH, NULL); return PJ_SUCCESS; } if (msg->hdr.type == PJ_STUN_REFRESH_REQUEST) { /* * Handle REFRESH request */ pj_stun_lifetime_attr *lifetime; pj_stun_bandwidth_attr *bandwidth; /* Get LIFETIME attribute */ lifetime = (pj_stun_lifetime_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_LIFETIME, 0); /* Get BANDWIDTH attribute */ bandwidth = (pj_stun_bandwidth_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_BANDWIDTH, 0); /* TODO: process bandwidth */ PJ_UNUSED_ARG(bandwidth); if (lifetime && lifetime->value==0) { /* * This is deallocation request. */ alloc->relay.lifetime = 0; /* Respond first */ send_reply_ok(alloc, rdata); /* Shutdown allocation */ PJ_LOG(4,(alloc->obj_name, "Client %s request to dealloc, shutting down", alloc->info)); alloc_shutdown(alloc); } else { /* * This is a refresh request. */ /* Update lifetime */ if (lifetime) { alloc->relay.lifetime = lifetime->value; } /* Update bandwidth */ // TODO: /* Update expiration timer */ resched_timeout(alloc); /* Send reply */ send_reply_ok(alloc, rdata); } } else if (msg->hdr.type == PJ_STUN_CHANNEL_BIND_REQUEST) { /* * ChannelBind request. */ pj_stun_channel_number_attr *ch_attr; pj_stun_xor_peer_addr_attr *peer_attr; pj_turn_permission *p1, *p2; ch_attr = (pj_stun_channel_number_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_CHANNEL_NUMBER, 0); peer_attr = (pj_stun_xor_peer_addr_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0); if (!ch_attr || !peer_attr) { send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL); return PJ_SUCCESS; } /* Find permission with the channel number */ p1 = lookup_permission_by_chnum(alloc, PJ_STUN_GET_CH_NB(ch_attr->value)); /* If permission is found, this is supposed to be a channel bind * refresh. Make sure it's for the same peer. */ if (p1) { if (pj_sockaddr_cmp(&p1->hkey.peer_addr, &peer_attr->sockaddr)) { /* Address mismatch. Send 400 */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, "Peer address mismatch"); return PJ_SUCCESS; } /* Refresh permission */ refresh_permission(p1); /* Send response */ send_reply_ok(alloc, rdata); /* Done */ return PJ_SUCCESS; } /* If permission is not found, create a new one. Make sure the peer * has not alreadyy assigned with a channel number. */ p2 = lookup_permission_by_addr(alloc, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); if (p2 && p2->channel != PJ_TURN_INVALID_CHANNEL) { send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, "Peer address already assigned a channel number"); return PJ_SUCCESS; } /* Create permission if it doesn't exist */ if (!p2) { p2 = create_permission(alloc, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); if (!p2) return PJ_SUCCESS; } /* Assign channel number to permission */ p2->channel = PJ_STUN_GET_CH_NB(ch_attr->value); /* Register to hash table */ pj_assert(sizeof(p2->channel)==2); pj_hash_set(alloc->pool, alloc->ch_table, &p2->channel, sizeof(p2->channel), 0, p2); /* Update */ refresh_permission(p2); /* Reply */ send_reply_ok(alloc, rdata); return PJ_SUCCESS; } else if (msg->hdr.type == PJ_STUN_ALLOCATE_REQUEST) { /* Respond with 437 (section 6.3 turn-07) */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH, NULL); } else { /* Respond with Bad Request? */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL); } return PJ_SUCCESS; } /* * Callback notification from STUN session when it receives STUN * indications. This callback was trigger by STUN incoming message * processing in pj_turn_allocation_on_rx_client_pkt(). */ static pj_status_t stun_on_rx_indication(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_msg *msg, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_xor_peer_addr_attr *peer_attr; pj_stun_data_attr *data_attr; pj_turn_allocation *alloc; pj_turn_permission *perm; pj_ssize_t len; PJ_UNUSED_ARG(pkt); PJ_UNUSED_ARG(pkt_len); PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess); /* Only expect Send Indication */ if (msg->hdr.type != PJ_STUN_SEND_INDICATION) { /* Ignore */ return PJ_SUCCESS; } /* Get XOR-PEER-ADDRESS attribute */ peer_attr = (pj_stun_xor_peer_addr_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0); /* MUST have XOR-PEER-ADDRESS attribute */ if (!peer_attr) return PJ_SUCCESS; /* Get DATA attribute */ data_attr = (pj_stun_data_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_DATA, 0); /* Create/update/refresh the permission */ perm = lookup_permission_by_addr(alloc, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); if (perm == NULL) { perm = create_permission(alloc, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); } refresh_permission(perm); /* Return if we don't have data */ if (data_attr == NULL) return PJ_SUCCESS; /* Relay the data to peer */ len = data_attr->length; pj_sock_sendto(alloc->relay.tp.sock, data_attr->data, &len, 0, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjnath/src/pjturn-srv/auth.c ================================================ /* $Id: auth.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "auth.h" #include #define MAX_REALM 80 #define MAX_USERNAME 32 #define MAX_PASSWORD 32 #define MAX_NONCE 32 static char g_realm[MAX_REALM]; static struct cred_t { char username[MAX_USERNAME]; char passwd[MAX_PASSWORD]; } g_cred[] = { { "100", "100" }, { "700", "700" }, { "701", "701" } }; #define THIS_FILE "auth.c" #define THE_NONCE "pjnath" #define LOG(expr) PJ_LOG(3,expr) /* * Initialize TURN authentication subsystem. */ PJ_DEF(pj_status_t) pj_turn_auth_init(const char *realm) { PJ_ASSERT_RETURN(pj_ansi_strlen(realm) < MAX_REALM, PJ_ENAMETOOLONG); pj_ansi_strcpy(g_realm, realm); return PJ_SUCCESS; } /* * Shutdown TURN authentication subsystem. */ PJ_DEF(void) pj_turn_auth_dinit(void) { /* Nothing to do */ } /* * This function is called by pj_stun_verify_credential() when * server needs to challenge the request with 401 response. */ PJ_DEF(pj_status_t) pj_turn_get_auth(void *user_data, pj_pool_t *pool, pj_str_t *realm, pj_str_t *nonce) { PJ_UNUSED_ARG(user_data); PJ_UNUSED_ARG(pool); *realm = pj_str(g_realm); *nonce = pj_str(THE_NONCE); return PJ_SUCCESS; } /* * This function is called to get the password for the specified username. * This function is also used to check whether the username is valid. */ PJ_DEF(pj_status_t) pj_turn_get_password(const pj_stun_msg *msg, void *user_data, const pj_str_t *realm, const pj_str_t *username, pj_pool_t *pool, pj_stun_passwd_type *data_type, pj_str_t *data) { unsigned i; PJ_UNUSED_ARG(msg); PJ_UNUSED_ARG(user_data); PJ_UNUSED_ARG(pool); if (pj_stricmp2(realm, g_realm)) { LOG((THIS_FILE, "auth error: invalid realm '%.*s'", (int)realm->slen, realm->ptr)); return PJ_EINVAL; } for (i=0; islen, username->ptr)); return PJ_ENOTFOUND; } /* * This function will be called to verify that the NONCE given * in the message can be accepted. If this callback returns * PJ_FALSE, 438 (Stale Nonce) response will be created. */ PJ_DEF(pj_bool_t) pj_turn_verify_nonce(const pj_stun_msg *msg, void *user_data, const pj_str_t *realm, const pj_str_t *username, const pj_str_t *nonce) { PJ_UNUSED_ARG(msg); PJ_UNUSED_ARG(user_data); PJ_UNUSED_ARG(realm); PJ_UNUSED_ARG(username); if (pj_stricmp2(nonce, THE_NONCE)) { LOG((THIS_FILE, "auth error: invalid nonce '%.*s'", (int)nonce->slen, nonce->ptr)); return PJ_FALSE; } return PJ_TRUE; } ================================================ FILE: deps/pjsip/pjnath/src/pjturn-srv/auth.h ================================================ /* $Id: auth.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_TURN_SRV_AUTH_H__ #define __PJ_TURN_SRV_AUTH_H__ #include /** * Initialize TURN authentication subsystem. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pj_turn_auth_init(const char *realm); /** * Shutdown TURN authentication subsystem. */ PJ_DECL(void) pj_turn_auth_dinit(void); /** * This function is called by pj_stun_verify_credential() when * server needs to challenge the request with 401 response. * * @param user_data Should be ignored. * @param pool Pool to allocate memory. * @param realm On return, the function should fill in with * realm if application wants to use long term * credential. Otherwise application should set * empty string for the realm. * @param nonce On return, if application wants to use long * term credential, it MUST fill in the nonce * with some value. Otherwise if short term * credential is wanted, it MAY set this value. * If short term credential is wanted and the * application doesn't want to include NONCE, * then it must set this to empty string. * * @return The callback should return PJ_SUCCESS, or * otherwise response message will not be * created. */ PJ_DECL(pj_status_t) pj_turn_get_auth(void *user_data, pj_pool_t *pool, pj_str_t *realm, pj_str_t *nonce); /** * This function is called to get the password for the specified username. * This function is also used to check whether the username is valid. * * @param msg The STUN message where the password will be * applied to. * @param user_data Should be ignored. * @param realm The realm as specified in the message. * @param username The username as specified in the message. * @param pool Pool to allocate memory when necessary. * @param data_type On return, application should fill up this * argument with the type of data (which should * be zero if data is a plaintext password). * @param data On return, application should fill up this * argument with the password according to * data_type. * * @return The callback should return PJ_SUCCESS if * username has been successfully verified * and password was obtained. If non-PJ_SUCCESS * is returned, it is assumed that the * username is not valid. */ PJ_DECL(pj_status_t) pj_turn_get_password(const pj_stun_msg *msg, void *user_data, const pj_str_t *realm, const pj_str_t *username, pj_pool_t *pool, pj_stun_passwd_type *data_type, pj_str_t *data); /** * This function will be called to verify that the NONCE given * in the message can be accepted. If this callback returns * PJ_FALSE, 438 (Stale Nonce) response will be created. * * @param msg The STUN message where the nonce was received. * @param user_data Should be ignored. * @param realm The realm as specified in the message. * @param username The username as specified in the message. * @param nonce The nonce to be verified. * * @return The callback MUST return non-zero if the * NONCE can be accepted. */ PJ_DECL(pj_bool_t) pj_turn_verify_nonce(const pj_stun_msg *msg, void *user_data, const pj_str_t *realm, const pj_str_t *username, const pj_str_t *nonce); #endif /* __PJ_TURN_SRV_AUTH_H__ */ ================================================ FILE: deps/pjsip/pjnath/src/pjturn-srv/listener_tcp.c ================================================ /* $Id: listener_tcp.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "turn.h" #include #if PJ_HAS_TCP struct accept_op { pj_ioqueue_op_key_t op_key; pj_sock_t sock; pj_sockaddr src_addr; int src_addr_len; }; struct tcp_listener { pj_turn_listener base; pj_ioqueue_key_t *key; unsigned accept_cnt; struct accept_op *accept_op; /* Array of accept_op's */ }; static void lis_on_accept_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t sock, pj_status_t status); static pj_status_t lis_destroy(pj_turn_listener *listener); static void transport_create(pj_sock_t sock, pj_turn_listener *lis, pj_sockaddr_t *src_addr, int src_addr_len); static void show_err(const char *sender, const char *title, pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(sender, "%s: %s", title, errmsg)); } /* * Create a new listener on the specified port. */ PJ_DEF(pj_status_t) pj_turn_listener_create_tcp(pj_turn_srv *srv, int af, const pj_str_t *bound_addr, unsigned port, unsigned concurrency_cnt, unsigned flags, pj_turn_listener **p_listener) { pj_pool_t *pool; struct tcp_listener *tcp_lis; pj_ioqueue_callback ioqueue_cb; unsigned i; pj_status_t status; /* Create structure */ pool = pj_pool_create(srv->core.pf, "tcpl%p", 1000, 1000, NULL); tcp_lis = PJ_POOL_ZALLOC_T(pool, struct tcp_listener); tcp_lis->base.pool = pool; tcp_lis->base.obj_name = pool->obj_name; tcp_lis->base.server = srv; tcp_lis->base.tp_type = PJ_TURN_TP_TCP; tcp_lis->base.sock = PJ_INVALID_SOCKET; //tcp_lis->base.sendto = &tcp_sendto; tcp_lis->base.destroy = &lis_destroy; tcp_lis->accept_cnt = concurrency_cnt; tcp_lis->base.flags = flags; /* Create socket */ status = pj_sock_socket(af, pj_SOCK_STREAM(), 0, &tcp_lis->base.sock); if (status != PJ_SUCCESS) goto on_error; /* Init bind address */ status = pj_sockaddr_init(af, &tcp_lis->base.addr, bound_addr, (pj_uint16_t)port); if (status != PJ_SUCCESS) goto on_error; /* Create info */ pj_ansi_strcpy(tcp_lis->base.info, "TCP:"); pj_sockaddr_print(&tcp_lis->base.addr, tcp_lis->base.info+4, sizeof(tcp_lis->base.info)-4, 3); /* Bind socket */ status = pj_sock_bind(tcp_lis->base.sock, &tcp_lis->base.addr, pj_sockaddr_get_len(&tcp_lis->base.addr)); if (status != PJ_SUCCESS) goto on_error; /* Listen() */ status = pj_sock_listen(tcp_lis->base.sock, 5); if (status != PJ_SUCCESS) goto on_error; /* Register to ioqueue */ pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb)); ioqueue_cb.on_accept_complete = &lis_on_accept_complete; status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, tcp_lis->base.sock, tcp_lis, &ioqueue_cb, &tcp_lis->key); /* Create op keys */ tcp_lis->accept_op = (struct accept_op*)pj_pool_calloc(pool, concurrency_cnt, sizeof(struct accept_op)); /* Create each accept_op and kick off read operation */ for (i=0; ikey, &tcp_lis->accept_op[i].op_key, PJ_INVALID_SOCKET, PJ_EPENDING); } /* Done */ PJ_LOG(4,(tcp_lis->base.obj_name, "Listener %s created", tcp_lis->base.info)); *p_listener = &tcp_lis->base; return PJ_SUCCESS; on_error: lis_destroy(&tcp_lis->base); return status; } /* * Destroy listener. */ static pj_status_t lis_destroy(pj_turn_listener *listener) { struct tcp_listener *tcp_lis = (struct tcp_listener *)listener; unsigned i; if (tcp_lis->key) { pj_ioqueue_unregister(tcp_lis->key); tcp_lis->key = NULL; tcp_lis->base.sock = PJ_INVALID_SOCKET; } else if (tcp_lis->base.sock != PJ_INVALID_SOCKET) { pj_sock_close(tcp_lis->base.sock); tcp_lis->base.sock = PJ_INVALID_SOCKET; } for (i=0; iaccept_cnt; ++i) { /* Nothing to do */ } if (tcp_lis->base.pool) { pj_pool_t *pool = tcp_lis->base.pool; PJ_LOG(4,(tcp_lis->base.obj_name, "Listener %s destroyed", tcp_lis->base.info)); tcp_lis->base.pool = NULL; pj_pool_release(pool); } return PJ_SUCCESS; } /* * Callback on new TCP connection. */ static void lis_on_accept_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t sock, pj_status_t status) { struct tcp_listener *tcp_lis; struct accept_op *accept_op = (struct accept_op*) op_key; tcp_lis = (struct tcp_listener*) pj_ioqueue_get_user_data(key); PJ_UNUSED_ARG(sock); do { /* Report new connection. */ if (status == PJ_SUCCESS) { char addr[PJ_INET6_ADDRSTRLEN+8]; PJ_LOG(5,(tcp_lis->base.obj_name, "Incoming TCP from %s", pj_sockaddr_print(&accept_op->src_addr, addr, sizeof(addr), 3))); transport_create(accept_op->sock, &tcp_lis->base, &accept_op->src_addr, accept_op->src_addr_len); } else if (status != PJ_EPENDING) { show_err(tcp_lis->base.obj_name, "accept()", status); } /* Prepare next accept() */ accept_op->src_addr_len = sizeof(accept_op->src_addr); status = pj_ioqueue_accept(key, op_key, &accept_op->sock, NULL, &accept_op->src_addr, &accept_op->src_addr_len); } while (status != PJ_EPENDING && status != PJ_ECANCELLED && status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)); } /****************************************************************************/ /* * Transport */ enum { TIMER_NONE, TIMER_DESTROY }; /* The delay in seconds to be applied before TCP transport is destroyed when * no allocation is referencing it. This also means the initial time to wait * after the initial TCP connection establishment to receive a valid STUN * message in the transport. */ #define SHUTDOWN_DELAY 10 struct recv_op { pj_ioqueue_op_key_t op_key; pj_turn_pkt pkt; }; struct tcp_transport { pj_turn_transport base; pj_pool_t *pool; pj_timer_entry timer; pj_turn_allocation *alloc; int ref_cnt; pj_sock_t sock; pj_ioqueue_key_t *key; struct recv_op recv_op; pj_ioqueue_op_key_t send_op; }; static void tcp_on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read); static pj_status_t tcp_sendto(pj_turn_transport *tp, const void *packet, pj_size_t size, unsigned flag, const pj_sockaddr_t *addr, int addr_len); static void tcp_destroy(struct tcp_transport *tcp); static void tcp_add_ref(pj_turn_transport *tp, pj_turn_allocation *alloc); static void tcp_dec_ref(pj_turn_transport *tp, pj_turn_allocation *alloc); static void timer_callback(pj_timer_heap_t *timer_heap, pj_timer_entry *entry); static void transport_create(pj_sock_t sock, pj_turn_listener *lis, pj_sockaddr_t *src_addr, int src_addr_len) { pj_pool_t *pool; struct tcp_transport *tcp; pj_ioqueue_callback cb; pj_status_t status; pool = pj_pool_create(lis->server->core.pf, "tcp%p", 1000, 1000, NULL); tcp = PJ_POOL_ZALLOC_T(pool, struct tcp_transport); tcp->base.obj_name = pool->obj_name; tcp->base.listener = lis; tcp->base.info = lis->info; tcp->base.sendto = &tcp_sendto; tcp->base.add_ref = &tcp_add_ref; tcp->base.dec_ref = &tcp_dec_ref; tcp->pool = pool; tcp->sock = sock; pj_timer_entry_init(&tcp->timer, TIMER_NONE, tcp, &timer_callback); /* Register to ioqueue */ pj_bzero(&cb, sizeof(cb)); cb.on_read_complete = &tcp_on_read_complete; status = pj_ioqueue_register_sock(pool, lis->server->core.ioqueue, sock, tcp, &cb, &tcp->key); if (status != PJ_SUCCESS) { tcp_destroy(tcp); return; } /* Init pkt */ tcp->recv_op.pkt.pool = pj_pool_create(lis->server->core.pf, "tcpkt%p", 1000, 1000, NULL); tcp->recv_op.pkt.transport = &tcp->base; tcp->recv_op.pkt.src.tp_type = PJ_TURN_TP_TCP; tcp->recv_op.pkt.src_addr_len = src_addr_len; pj_memcpy(&tcp->recv_op.pkt.src.clt_addr, src_addr, src_addr_len); tcp_on_read_complete(tcp->key, &tcp->recv_op.op_key, -PJ_EPENDING); /* Should not access transport from now, it may have been destroyed */ } static void tcp_destroy(struct tcp_transport *tcp) { if (tcp->key) { pj_ioqueue_unregister(tcp->key); tcp->key = NULL; tcp->sock = 0; } else if (tcp->sock) { pj_sock_close(tcp->sock); tcp->sock = 0; } if (tcp->pool) { pj_pool_release(tcp->pool); } } static void timer_callback(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) { struct tcp_transport *tcp = (struct tcp_transport*) entry->user_data; PJ_UNUSED_ARG(timer_heap); tcp_destroy(tcp); } static void tcp_on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { struct tcp_transport *tcp; struct recv_op *recv_op = (struct recv_op*) op_key; pj_status_t status; tcp = (struct tcp_transport*) pj_ioqueue_get_user_data(key); do { /* Report to server or allocation, if we have allocation */ if (bytes_read > 0) { recv_op->pkt.len = bytes_read; pj_gettimeofday(&recv_op->pkt.rx_time); tcp_add_ref(&tcp->base, NULL); if (tcp->alloc) { pj_turn_allocation_on_rx_client_pkt(tcp->alloc, &recv_op->pkt); } else { pj_turn_srv_on_rx_pkt(tcp->base.listener->server, &recv_op->pkt); } pj_assert(tcp->ref_cnt > 0); tcp_dec_ref(&tcp->base, NULL); } else if (bytes_read != -PJ_EPENDING) { /* TCP connection closed/error. Notify client and then destroy * ourselves. * Note: the -PJ_EPENDING is the value passed during init. */ ++tcp->ref_cnt; if (tcp->alloc) { if (bytes_read != 0) { show_err(tcp->base.obj_name, "TCP socket error", -bytes_read); } else { PJ_LOG(5,(tcp->base.obj_name, "TCP socket closed")); } pj_turn_allocation_on_transport_closed(tcp->alloc, &tcp->base); tcp->alloc = NULL; } pj_assert(tcp->ref_cnt > 0); if (--tcp->ref_cnt == 0) { tcp_destroy(tcp); return; } } /* Reset pool */ pj_pool_reset(recv_op->pkt.pool); /* If packet is full discard it */ if (recv_op->pkt.len == sizeof(recv_op->pkt.pkt)) { PJ_LOG(4,(tcp->base.obj_name, "Buffer discarded")); recv_op->pkt.len = 0; } /* Read next packet */ bytes_read = sizeof(recv_op->pkt.pkt) - recv_op->pkt.len; status = pj_ioqueue_recv(tcp->key, op_key, recv_op->pkt.pkt + recv_op->pkt.len, &bytes_read, 0); if (status != PJ_EPENDING && status != PJ_SUCCESS) bytes_read = -status; } while (status != PJ_EPENDING && status != PJ_ECANCELLED && status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)); } static pj_status_t tcp_sendto(pj_turn_transport *tp, const void *packet, pj_size_t size, unsigned flag, const pj_sockaddr_t *addr, int addr_len) { struct tcp_transport *tcp = (struct tcp_transport*) tp; pj_ssize_t length = size; PJ_UNUSED_ARG(addr); PJ_UNUSED_ARG(addr_len); return pj_ioqueue_send(tcp->key, &tcp->send_op, packet, &length, flag); } static void tcp_add_ref(pj_turn_transport *tp, pj_turn_allocation *alloc) { struct tcp_transport *tcp = (struct tcp_transport*) tp; ++tcp->ref_cnt; if (tcp->alloc == NULL && alloc) { tcp->alloc = alloc; } /* Cancel shutdown timer if it's running */ if (tcp->timer.id != TIMER_NONE) { pj_timer_heap_cancel(tcp->base.listener->server->core.timer_heap, &tcp->timer); tcp->timer.id = TIMER_NONE; } } static void tcp_dec_ref(pj_turn_transport *tp, pj_turn_allocation *alloc) { struct tcp_transport *tcp = (struct tcp_transport*) tp; --tcp->ref_cnt; if (alloc && alloc == tcp->alloc) { tcp->alloc = NULL; } if (tcp->ref_cnt == 0 && tcp->timer.id == TIMER_NONE) { pj_time_val delay = { SHUTDOWN_DELAY, 0 }; tcp->timer.id = TIMER_DESTROY; pj_timer_heap_schedule(tcp->base.listener->server->core.timer_heap, &tcp->timer, &delay); } } #else /* PJ_HAS_TCP */ /* To avoid empty translation unit warning */ int listener_tcp_dummy = 0; #endif /* PJ_HAS_TCP */ ================================================ FILE: deps/pjsip/pjnath/src/pjturn-srv/listener_udp.c ================================================ /* $Id: listener_udp.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "turn.h" #include struct read_op { pj_ioqueue_op_key_t op_key; pj_turn_pkt pkt; }; struct udp_listener { pj_turn_listener base; pj_ioqueue_key_t *key; unsigned read_cnt; struct read_op **read_op; /* Array of read_op's */ pj_turn_transport tp; /* Transport instance */ }; static pj_status_t udp_destroy(pj_turn_listener *udp); static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read); static pj_status_t udp_sendto(pj_turn_transport *tp, const void *packet, pj_size_t size, unsigned flag, const pj_sockaddr_t *addr, int addr_len); static void udp_add_ref(pj_turn_transport *tp, pj_turn_allocation *alloc); static void udp_dec_ref(pj_turn_transport *tp, pj_turn_allocation *alloc); /* * Create a new listener on the specified port. */ PJ_DEF(pj_status_t) pj_turn_listener_create_udp( pj_turn_srv *srv, int af, const pj_str_t *bound_addr, unsigned port, unsigned concurrency_cnt, unsigned flags, pj_turn_listener **p_listener) { pj_pool_t *pool; struct udp_listener *udp; pj_ioqueue_callback ioqueue_cb; unsigned i; pj_status_t status; /* Create structure */ pool = pj_pool_create(srv->core.pf, "udp%p", 1000, 1000, NULL); udp = PJ_POOL_ZALLOC_T(pool, struct udp_listener); udp->base.pool = pool; udp->base.obj_name = pool->obj_name; udp->base.server = srv; udp->base.tp_type = PJ_TURN_TP_UDP; udp->base.sock = PJ_INVALID_SOCKET; udp->base.destroy = &udp_destroy; udp->read_cnt = concurrency_cnt; udp->base.flags = flags; udp->tp.obj_name = udp->base.obj_name; udp->tp.info = udp->base.info; udp->tp.listener = &udp->base; udp->tp.sendto = &udp_sendto; udp->tp.add_ref = &udp_add_ref; udp->tp.dec_ref = &udp_dec_ref; /* Create socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &udp->base.sock); if (status != PJ_SUCCESS) goto on_error; /* Init bind address */ status = pj_sockaddr_init(af, &udp->base.addr, bound_addr, (pj_uint16_t)port); if (status != PJ_SUCCESS) goto on_error; /* Create info */ pj_ansi_strcpy(udp->base.info, "UDP:"); pj_sockaddr_print(&udp->base.addr, udp->base.info+4, sizeof(udp->base.info)-4, 3); /* Bind socket */ status = pj_sock_bind(udp->base.sock, &udp->base.addr, pj_sockaddr_get_len(&udp->base.addr)); if (status != PJ_SUCCESS) goto on_error; /* Register to ioqueue */ pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb)); ioqueue_cb.on_read_complete = on_read_complete; status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, udp->base.sock, udp, &ioqueue_cb, &udp->key); /* Create op keys */ udp->read_op = (struct read_op**)pj_pool_calloc(pool, concurrency_cnt, sizeof(struct read_op*)); /* Create each read_op and kick off read operation */ for (i=0; icore.pf, "rop%p", 1000, 1000, NULL); udp->read_op[i] = PJ_POOL_ZALLOC_T(pool, struct read_op); udp->read_op[i]->pkt.pool = rpool; on_read_complete(udp->key, &udp->read_op[i]->op_key, 0); } /* Done */ PJ_LOG(4,(udp->base.obj_name, "Listener %s created", udp->base.info)); *p_listener = &udp->base; return PJ_SUCCESS; on_error: udp_destroy(&udp->base); return status; } /* * Destroy listener. */ static pj_status_t udp_destroy(pj_turn_listener *listener) { struct udp_listener *udp = (struct udp_listener *)listener; unsigned i; if (udp->key) { pj_ioqueue_unregister(udp->key); udp->key = NULL; udp->base.sock = PJ_INVALID_SOCKET; } else if (udp->base.sock != PJ_INVALID_SOCKET) { pj_sock_close(udp->base.sock); udp->base.sock = PJ_INVALID_SOCKET; } for (i=0; iread_cnt; ++i) { if (udp->read_op[i]->pkt.pool) { pj_pool_t *rpool = udp->read_op[i]->pkt.pool; udp->read_op[i]->pkt.pool = NULL; pj_pool_release(rpool); } } if (udp->base.pool) { pj_pool_t *pool = udp->base.pool; PJ_LOG(4,(udp->base.obj_name, "Listener %s destroyed", udp->base.info)); udp->base.pool = NULL; pj_pool_release(pool); } return PJ_SUCCESS; } /* * Callback to send packet. */ static pj_status_t udp_sendto(pj_turn_transport *tp, const void *packet, pj_size_t size, unsigned flag, const pj_sockaddr_t *addr, int addr_len) { pj_ssize_t len = size; return pj_sock_sendto(tp->listener->sock, packet, &len, flag, addr, addr_len); } static void udp_add_ref(pj_turn_transport *tp, pj_turn_allocation *alloc) { /* Do nothing */ PJ_UNUSED_ARG(tp); PJ_UNUSED_ARG(alloc); } static void udp_dec_ref(pj_turn_transport *tp, pj_turn_allocation *alloc) { /* Do nothing */ PJ_UNUSED_ARG(tp); PJ_UNUSED_ARG(alloc); } /* * Callback on received packet. */ static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { struct udp_listener *udp; struct read_op *read_op = (struct read_op*) op_key; pj_status_t status; udp = (struct udp_listener*) pj_ioqueue_get_user_data(key); do { pj_pool_t *rpool; /* Report to server */ if (bytes_read > 0) { read_op->pkt.len = bytes_read; pj_gettimeofday(&read_op->pkt.rx_time); pj_turn_srv_on_rx_pkt(udp->base.server, &read_op->pkt); } /* Reset pool */ rpool = read_op->pkt.pool; pj_pool_reset(rpool); read_op->pkt.pool = rpool; read_op->pkt.transport = &udp->tp; read_op->pkt.src.tp_type = udp->base.tp_type; /* Read next packet */ bytes_read = sizeof(read_op->pkt.pkt); read_op->pkt.src_addr_len = sizeof(read_op->pkt.src.clt_addr); pj_bzero(&read_op->pkt.src.clt_addr, sizeof(read_op->pkt.src.clt_addr)); status = pj_ioqueue_recvfrom(udp->key, op_key, read_op->pkt.pkt, &bytes_read, 0, &read_op->pkt.src.clt_addr, &read_op->pkt.src_addr_len); if (status != PJ_EPENDING && status != PJ_SUCCESS) bytes_read = -status; } while (status != PJ_EPENDING && status != PJ_ECANCELLED && status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)); } ================================================ FILE: deps/pjsip/pjnath/src/pjturn-srv/main.c ================================================ /* $Id: main.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "turn.h" #include "auth.h" #define REALM "pjsip.org" //#define TURN_PORT PJ_STUN_TURN_PORT #define TURN_PORT 34780 #define LOG_LEVEL 4 static pj_caching_pool g_cp; int err(const char *title, pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); printf("%s: %s\n", title, errmsg); return 1; } static void dump_status(pj_turn_srv *srv) { char addr[80]; pj_hash_iterator_t itbuf, *it; pj_time_val now; unsigned i; for (i=0; icore.lis_cnt; ++i) { pj_turn_listener *lis = srv->core.listener[i]; printf("Server address : %s\n", lis->info); } printf("Worker threads : %d\n", srv->core.thread_cnt); printf("Total mem usage: %u.%03uMB\n", (unsigned)(g_cp.used_size / 1000000), (unsigned)((g_cp.used_size % 1000000)/1000)); printf("UDP port range : %u %u %u (next/min/max)\n", srv->ports.next_udp, srv->ports.min_udp, srv->ports.max_udp); printf("TCP port range : %u %u %u (next/min/max)\n", srv->ports.next_tcp, srv->ports.min_tcp, srv->ports.max_tcp); printf("Clients # : %u\n", pj_hash_count(srv->tables.alloc)); puts(""); if (pj_hash_count(srv->tables.alloc)==0) { return; } puts("# Client addr. Alloc addr. Username Lftm Expy #prm #chl"); puts("------------------------------------------------------------------------------"); pj_gettimeofday(&now); it = pj_hash_first(srv->tables.alloc, &itbuf); i=1; while (it) { pj_turn_allocation *alloc = (pj_turn_allocation*) pj_hash_this(srv->tables.alloc, it); printf("%-3d %-22s %-22s %-8.*s %-4d %-4ld %-4d %-4d\n", i, alloc->info, pj_sockaddr_print(&alloc->relay.hkey.addr, addr, sizeof(addr), 3), (int)alloc->cred.data.static_cred.username.slen, alloc->cred.data.static_cred.username.ptr, alloc->relay.lifetime, alloc->relay.expiry.sec - now.sec, pj_hash_count(alloc->peer_table), pj_hash_count(alloc->ch_table)); it = pj_hash_next(srv->tables.alloc, it); ++i; } } static void menu(void) { puts(""); puts("Menu:"); puts(" d Dump status"); puts(" q Quit"); printf(">> "); } static void console_main(pj_turn_srv *srv) { pj_bool_t quit = PJ_FALSE; while (!quit) { char line[10]; menu(); if (fgets(line, sizeof(line), stdin) == NULL) break; switch (line[0]) { case 'd': dump_status(srv); break; case 'q': quit = PJ_TRUE; break; } } } int main() { pj_turn_srv *srv; pj_turn_listener *listener; pj_status_t status; status = pj_init(); if (status != PJ_SUCCESS) return err("pj_init() error", status); pjlib_util_init(); pjnath_init(); pj_caching_pool_init(&g_cp, NULL, 0); pj_turn_auth_init(REALM); status = pj_turn_srv_create(&g_cp.factory, &srv); if (status != PJ_SUCCESS) return err("Error creating server", status); status = pj_turn_listener_create_udp(srv, pj_AF_INET(), NULL, TURN_PORT, 1, 0, &listener); if (status != PJ_SUCCESS) return err("Error creating UDP listener", status); #if PJ_HAS_TCP status = pj_turn_listener_create_tcp(srv, pj_AF_INET(), NULL, TURN_PORT, 1, 0, &listener); if (status != PJ_SUCCESS) return err("Error creating listener", status); #endif status = pj_turn_srv_add_listener(srv, listener); if (status != PJ_SUCCESS) return err("Error adding listener", status); puts("Server is running"); pj_log_set_level(LOG_LEVEL); console_main(srv); pj_turn_srv_destroy(srv); pj_caching_pool_destroy(&g_cp); pj_shutdown(); return 0; } ================================================ FILE: deps/pjsip/pjnath/src/pjturn-srv/server.c ================================================ /* $Id: server.c 4360 2013-02-21 11:26:35Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "turn.h" #include "auth.h" #define MAX_CLIENTS 32 #define MAX_PEERS_PER_CLIENT 8 //#define MAX_HANDLES (MAX_CLIENTS*MAX_PEERS_PER_CLIENT+MAX_LISTENERS) #define MAX_HANDLES PJ_IOQUEUE_MAX_HANDLES #define MAX_TIMER (MAX_HANDLES * 2) #define MIN_PORT 49152 #define MAX_PORT 65535 #define MAX_LISTENERS 16 #define MAX_THREADS 2 #define MAX_NET_EVENTS 1000 /* Prototypes */ static int server_thread_proc(void *arg); static pj_status_t on_tx_stun_msg( pj_stun_session *sess, void *token, const void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *dst_addr, unsigned addr_len); static pj_status_t on_rx_stun_request(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_rx_data *rdata, void *user_data, const pj_sockaddr_t *src_addr, unsigned src_addr_len); struct saved_cred { pj_str_t realm; pj_str_t username; pj_str_t nonce; int data_type; pj_str_t data; }; /* * Get transport type name, normally for logging purpose only. */ PJ_DEF(const char*) pj_turn_tp_type_name(int tp_type) { /* Must be 3 characters long! */ if (tp_type == PJ_TURN_TP_UDP) { return "UDP"; } else if (tp_type == PJ_TURN_TP_TCP) { return "TCP"; } else { pj_assert(!"Unsupported transport"); return "???"; } } /* * Create server. */ PJ_DEF(pj_status_t) pj_turn_srv_create(pj_pool_factory *pf, pj_turn_srv **p_srv) { pj_pool_t *pool; pj_stun_session_cb sess_cb; pj_turn_srv *srv; unsigned i; pj_status_t status; PJ_ASSERT_RETURN(pf && p_srv, PJ_EINVAL); /* Create server and init core settings */ pool = pj_pool_create(pf, "srv%p", 1000, 1000, NULL); srv = PJ_POOL_ZALLOC_T(pool, pj_turn_srv); srv->obj_name = pool->obj_name; srv->core.pf = pf; srv->core.pool = pool; srv->core.tls_key = srv->core.tls_data = -1; /* Create ioqueue */ status = pj_ioqueue_create(pool, MAX_HANDLES, &srv->core.ioqueue); if (status != PJ_SUCCESS) goto on_error; /* Server mutex */ status = pj_lock_create_recursive_mutex(pool, srv->obj_name, &srv->core.lock); if (status != PJ_SUCCESS) goto on_error; /* Allocate TLS */ status = pj_thread_local_alloc(&srv->core.tls_key); if (status != PJ_SUCCESS) goto on_error; status = pj_thread_local_alloc(&srv->core.tls_data); if (status != PJ_SUCCESS) goto on_error; /* Create timer heap */ status = pj_timer_heap_create(pool, MAX_TIMER, &srv->core.timer_heap); if (status != PJ_SUCCESS) goto on_error; /* Configure lock for the timer heap */ pj_timer_heap_set_lock(srv->core.timer_heap, srv->core.lock, PJ_FALSE); /* Array of listeners */ srv->core.listener = (pj_turn_listener**) pj_pool_calloc(pool, MAX_LISTENERS, sizeof(srv->core.listener[0])); /* Create hash tables */ srv->tables.alloc = pj_hash_create(pool, MAX_CLIENTS); srv->tables.res = pj_hash_create(pool, MAX_CLIENTS); /* Init ports settings */ srv->ports.min_udp = srv->ports.next_udp = MIN_PORT; srv->ports.max_udp = MAX_PORT; srv->ports.min_tcp = srv->ports.next_tcp = MIN_PORT; srv->ports.max_tcp = MAX_PORT; /* Init STUN config */ pj_stun_config_init(&srv->core.stun_cfg, pf, 0, srv->core.ioqueue, srv->core.timer_heap); /* Init STUN credential */ srv->core.cred.type = PJ_STUN_AUTH_CRED_DYNAMIC; srv->core.cred.data.dyn_cred.user_data = srv; srv->core.cred.data.dyn_cred.get_auth = &pj_turn_get_auth; srv->core.cred.data.dyn_cred.get_password = &pj_turn_get_password; srv->core.cred.data.dyn_cred.verify_nonce = &pj_turn_verify_nonce; /* Create STUN session to handle new allocation */ pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_rx_request = &on_rx_stun_request; sess_cb.on_send_msg = &on_tx_stun_msg; status = pj_stun_session_create(&srv->core.stun_cfg, srv->obj_name, &sess_cb, PJ_FALSE, NULL, &srv->core.stun_sess); if (status != PJ_SUCCESS) { goto on_error; } pj_stun_session_set_user_data(srv->core.stun_sess, srv); pj_stun_session_set_credential(srv->core.stun_sess, PJ_STUN_AUTH_LONG_TERM, &srv->core.cred); /* Array of worker threads */ srv->core.thread_cnt = MAX_THREADS; srv->core.thread = (pj_thread_t**) pj_pool_calloc(pool, srv->core.thread_cnt, sizeof(pj_thread_t*)); /* Start the worker threads */ for (i=0; icore.thread_cnt; ++i) { status = pj_thread_create(pool, srv->obj_name, &server_thread_proc, srv, 0, 0, &srv->core.thread[i]); if (status != PJ_SUCCESS) goto on_error; } /* We're done. Application should add listeners now */ PJ_LOG(4,(srv->obj_name, "TURN server v%s is running", pj_get_version())); *p_srv = srv; return PJ_SUCCESS; on_error: pj_turn_srv_destroy(srv); return status; } /* * Handle timer and network events */ static void srv_handle_events(pj_turn_srv *srv, const pj_time_val *max_timeout) { /* timeout is 'out' var. This just to make compiler happy. */ pj_time_val timeout = { 0, 0}; unsigned net_event_count = 0; int c; /* Poll the timer. The timer heap has its own mutex for better * granularity, so we don't need to lock the server. */ timeout.sec = timeout.msec = 0; c = pj_timer_heap_poll( srv->core.timer_heap, &timeout ); /* timer_heap_poll should never ever returns negative value, or otherwise * ioqueue_poll() will block forever! */ pj_assert(timeout.sec >= 0 && timeout.msec >= 0); if (timeout.msec >= 1000) timeout.msec = 999; /* If caller specifies maximum time to wait, then compare the value with * the timeout to wait from timer, and use the minimum value. */ if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) { timeout = *max_timeout; } /* Poll ioqueue. * Repeat polling the ioqueue while we have immediate events, because * timer heap may process more than one events, so if we only process * one network events at a time (such as when IOCP backend is used), * the ioqueue may have trouble keeping up with the request rate. * * For example, for each send() request, one network event will be * reported by ioqueue for the send() completion. If we don't poll * the ioqueue often enough, the send() completion will not be * reported in timely manner. */ do { c = pj_ioqueue_poll( srv->core.ioqueue, &timeout); if (c < 0) { pj_thread_sleep(PJ_TIME_VAL_MSEC(timeout)); return; } else if (c == 0) { break; } else { net_event_count += c; timeout.sec = timeout.msec = 0; } } while (c > 0 && net_event_count < MAX_NET_EVENTS); } /* * Server worker thread proc. */ static int server_thread_proc(void *arg) { pj_turn_srv *srv = (pj_turn_srv*)arg; while (!srv->core.quit) { pj_time_val timeout_max = {0, 100}; srv_handle_events(srv, &timeout_max); } return 0; } /* * Destroy the server. */ PJ_DEF(pj_status_t) pj_turn_srv_destroy(pj_turn_srv *srv) { pj_hash_iterator_t itbuf, *it; unsigned i; /* Stop all worker threads */ srv->core.quit = PJ_TRUE; for (i=0; icore.thread_cnt; ++i) { if (srv->core.thread[i]) { pj_thread_join(srv->core.thread[i]); pj_thread_destroy(srv->core.thread[i]); srv->core.thread[i] = NULL; } } /* Destroy all allocations FIRST */ if (srv->tables.alloc) { it = pj_hash_first(srv->tables.alloc, &itbuf); while (it != NULL) { pj_turn_allocation *alloc = (pj_turn_allocation*) pj_hash_this(srv->tables.alloc, it); pj_hash_iterator_t *next = pj_hash_next(srv->tables.alloc, it); pj_turn_allocation_destroy(alloc); it = next; } } /* Destroy all listeners. */ for (i=0; icore.lis_cnt; ++i) { if (srv->core.listener[i]) { pj_turn_listener_destroy(srv->core.listener[i]); srv->core.listener[i] = NULL; } } /* Destroy STUN session */ if (srv->core.stun_sess) { pj_stun_session_destroy(srv->core.stun_sess); srv->core.stun_sess = NULL; } /* Destroy hash tables (well, sort of) */ if (srv->tables.alloc) { srv->tables.alloc = NULL; srv->tables.res = NULL; } /* Destroy timer heap */ if (srv->core.timer_heap) { pj_timer_heap_destroy(srv->core.timer_heap); srv->core.timer_heap = NULL; } /* Destroy ioqueue */ if (srv->core.ioqueue) { pj_ioqueue_destroy(srv->core.ioqueue); srv->core.ioqueue = NULL; } /* Destroy thread local IDs */ if (srv->core.tls_key != -1) { pj_thread_local_free(srv->core.tls_key); srv->core.tls_key = -1; } if (srv->core.tls_data != -1) { pj_thread_local_free(srv->core.tls_data); srv->core.tls_data = -1; } /* Destroy server lock */ if (srv->core.lock) { pj_lock_destroy(srv->core.lock); srv->core.lock = NULL; } /* Release pool */ if (srv->core.pool) { pj_pool_t *pool = srv->core.pool; srv->core.pool = NULL; pj_pool_release(pool); } /* Done */ return PJ_SUCCESS; } /* * Add listener. */ PJ_DEF(pj_status_t) pj_turn_srv_add_listener(pj_turn_srv *srv, pj_turn_listener *lis) { unsigned index; PJ_ASSERT_RETURN(srv && lis, PJ_EINVAL); PJ_ASSERT_RETURN(srv->core.lis_cnt < MAX_LISTENERS, PJ_ETOOMANY); /* Add to array */ index = srv->core.lis_cnt; srv->core.listener[index] = lis; lis->server = srv; lis->id = index; srv->core.lis_cnt++; PJ_LOG(4,(srv->obj_name, "Listener %s/%s added at index %d", lis->obj_name, lis->info, lis->id)); return PJ_SUCCESS; } /* * Destroy listener. */ PJ_DEF(pj_status_t) pj_turn_listener_destroy(pj_turn_listener *listener) { pj_turn_srv *srv = listener->server; unsigned i; /* Remove from our listener list */ pj_lock_acquire(srv->core.lock); for (i=0; icore.lis_cnt; ++i) { if (srv->core.listener[i] == listener) { srv->core.listener[i] = NULL; srv->core.lis_cnt--; listener->id = PJ_TURN_INVALID_LIS_ID; break; } } pj_lock_release(srv->core.lock); /* Destroy */ return listener->destroy(listener); } /** * Add a reference to a transport. */ PJ_DEF(void) pj_turn_transport_add_ref( pj_turn_transport *transport, pj_turn_allocation *alloc) { transport->add_ref(transport, alloc); } /** * Decrement transport reference counter. */ PJ_DEF(void) pj_turn_transport_dec_ref( pj_turn_transport *transport, pj_turn_allocation *alloc) { transport->dec_ref(transport, alloc); } /* * Register an allocation to the hash tables. */ PJ_DEF(pj_status_t) pj_turn_srv_register_allocation(pj_turn_srv *srv, pj_turn_allocation *alloc) { /* Add to hash tables */ pj_lock_acquire(srv->core.lock); pj_hash_set(alloc->pool, srv->tables.alloc, &alloc->hkey, sizeof(alloc->hkey), 0, alloc); pj_hash_set(alloc->pool, srv->tables.res, &alloc->relay.hkey, sizeof(alloc->relay.hkey), 0, &alloc->relay); pj_lock_release(srv->core.lock); return PJ_SUCCESS; } /* * Unregister an allocation from the hash tables. */ PJ_DEF(pj_status_t) pj_turn_srv_unregister_allocation(pj_turn_srv *srv, pj_turn_allocation *alloc) { /* Unregister from hash tables */ pj_lock_acquire(srv->core.lock); pj_hash_set(alloc->pool, srv->tables.alloc, &alloc->hkey, sizeof(alloc->hkey), 0, NULL); pj_hash_set(alloc->pool, srv->tables.res, &alloc->relay.hkey, sizeof(alloc->relay.hkey), 0, NULL); pj_lock_release(srv->core.lock); return PJ_SUCCESS; } /* Callback from our own STUN session whenever it needs to send * outgoing STUN packet. */ static pj_status_t on_tx_stun_msg( pj_stun_session *sess, void *token, const void *pdu, pj_size_t pdu_size, const pj_sockaddr_t *dst_addr, unsigned addr_len) { pj_turn_transport *transport = (pj_turn_transport*) token; PJ_ASSERT_RETURN(transport!=NULL, PJ_EINVALIDOP); PJ_UNUSED_ARG(sess); return transport->sendto(transport, pdu, pdu_size, 0, dst_addr, addr_len); } /* Respond to STUN request */ static pj_status_t stun_respond(pj_stun_session *sess, pj_turn_transport *transport, const pj_stun_rx_data *rdata, unsigned code, const char *errmsg, pj_bool_t cache, const pj_sockaddr_t *dst_addr, unsigned addr_len) { pj_status_t status; pj_str_t reason; pj_stun_tx_data *tdata; /* Create response */ status = pj_stun_session_create_res(sess, rdata, code, (errmsg?pj_cstr(&reason,errmsg):NULL), &tdata); if (status != PJ_SUCCESS) return status; /* Send the response */ return pj_stun_session_send_msg(sess, transport, cache, PJ_FALSE, dst_addr, addr_len, tdata); } /* Callback from our own STUN session when incoming request arrives. * This function is triggered by pj_stun_session_on_rx_pkt() call in * pj_turn_srv_on_rx_pkt() function below. */ static pj_status_t on_rx_stun_request(pj_stun_session *sess, const pj_uint8_t *pdu, unsigned pdu_len, const pj_stun_rx_data *rdata, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_turn_transport *transport; const pj_stun_msg *msg = rdata->msg; pj_turn_allocation *alloc; pj_status_t status; PJ_UNUSED_ARG(pdu); PJ_UNUSED_ARG(pdu_len); transport = (pj_turn_transport*) token; /* Respond any requests other than ALLOCATE with 437 response */ if (msg->hdr.type != PJ_STUN_ALLOCATE_REQUEST) { stun_respond(sess, transport, rdata, PJ_STUN_SC_ALLOCATION_MISMATCH, NULL, PJ_FALSE, src_addr, src_addr_len); return PJ_SUCCESS; } /* Create new allocation. The relay resource will be allocated * in this function. */ status = pj_turn_allocation_create(transport, src_addr, src_addr_len, rdata, sess, &alloc); if (status != PJ_SUCCESS) { /* STUN response has been sent, no need to reply here */ return PJ_SUCCESS; } /* Done. */ return PJ_SUCCESS; } /* Handle STUN Binding request */ static void handle_binding_request(pj_turn_pkt *pkt, unsigned options) { pj_stun_msg *request, *response; pj_uint8_t pdu[200]; pj_size_t len; pj_status_t status; /* Decode request */ status = pj_stun_msg_decode(pkt->pool, pkt->pkt, pkt->len, options, &request, NULL, NULL); if (status != PJ_SUCCESS) return; /* Create response */ status = pj_stun_msg_create_response(pkt->pool, request, 0, NULL, &response); if (status != PJ_SUCCESS) return; /* Add XOR-MAPPED-ADDRESS */ pj_stun_msg_add_sockaddr_attr(pkt->pool, response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &pkt->src.clt_addr, pkt->src_addr_len); /* Encode */ status = pj_stun_msg_encode(response, pdu, sizeof(pdu), 0, NULL, &len); if (status != PJ_SUCCESS) return; /* Send response */ pkt->transport->sendto(pkt->transport, pdu, len, 0, &pkt->src.clt_addr, pkt->src_addr_len); } /* * This callback is called by UDP listener on incoming packet. This is * the first entry for incoming packet (from client) to the server. From * here, the packet may be handed over to an allocation if an allocation * is found for the client address, or handed over to owned STUN session * if an allocation is not found. */ PJ_DEF(void) pj_turn_srv_on_rx_pkt(pj_turn_srv *srv, pj_turn_pkt *pkt) { pj_turn_allocation *alloc; /* Get TURN allocation from the source address */ pj_lock_acquire(srv->core.lock); alloc = (pj_turn_allocation*) pj_hash_get(srv->tables.alloc, &pkt->src, sizeof(pkt->src), NULL); pj_lock_release(srv->core.lock); /* If allocation is found, just hand over the packet to the * allocation. */ if (alloc) { pj_turn_allocation_on_rx_client_pkt(alloc, pkt); } else { /* Otherwise this is a new client */ unsigned options; pj_size_t parsed_len; pj_status_t status; /* Check that this is a STUN message */ options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK; if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) options |= PJ_STUN_IS_DATAGRAM; status = pj_stun_msg_check(pkt->pkt, pkt->len, options); if (status != PJ_SUCCESS) { /* If the first byte are not STUN, drop the packet. First byte * of STUN message is always 0x00 or 0x01. Otherwise wait for * more data as the data might have come from TCP. * * Also drop packet if it's unreasonably too big, as this might * indicate invalid data that's building up in the buffer. * * Or if packet is a datagram. */ if ((*pkt->pkt != 0x00 && *pkt->pkt != 0x01) || pkt->len > 1600 || (options & PJ_STUN_IS_DATAGRAM)) { char errmsg[PJ_ERR_MSG_SIZE]; char ip[PJ_INET6_ADDRSTRLEN+10]; pkt->len = 0; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(5,(srv->obj_name, "Non-STUN packet from %s is dropped: %s", pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3), errmsg)); } return; } /* Special handling for Binding Request. We won't give it to the * STUN session since this request is not authenticated. */ if (pkt->pkt[1] == 1) { handle_binding_request(pkt, options); return; } /* Hand over processing to STUN session. This will trigger * on_rx_stun_request() callback to be called if the STUN * message is a request. */ options &= ~PJ_STUN_CHECK_PACKET; parsed_len = 0; status = pj_stun_session_on_rx_pkt(srv->core.stun_sess, pkt->pkt, pkt->len, options, pkt->transport, &parsed_len, &pkt->src.clt_addr, pkt->src_addr_len); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; char ip[PJ_INET6_ADDRSTRLEN+10]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(5,(srv->obj_name, "Error processing STUN packet from %s: %s", pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3), errmsg)); } if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) { pkt->len = 0; } else if (parsed_len > 0) { if (parsed_len == pkt->len) { pkt->len = 0; } else { pj_memmove(pkt->pkt, pkt->pkt+parsed_len, pkt->len - parsed_len); pkt->len -= parsed_len; } } } } ================================================ FILE: deps/pjsip/pjnath/src/pjturn-srv/turn.h ================================================ /* $Id: turn.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJ_TURN_SRV_TURN_H__ #define __PJ_TURN_SRV_TURN_H__ #include #include typedef struct pj_turn_relay_res pj_turn_relay_res; typedef struct pj_turn_listener pj_turn_listener; typedef struct pj_turn_transport pj_turn_transport; typedef struct pj_turn_permission pj_turn_permission; typedef struct pj_turn_allocation pj_turn_allocation; typedef struct pj_turn_srv pj_turn_srv; typedef struct pj_turn_pkt pj_turn_pkt; #define PJ_TURN_INVALID_LIS_ID ((unsigned)-1) /** * Get transport type name string. */ PJ_DECL(const char*) pj_turn_tp_type_name(int tp_type); /** * This structure describes TURN relay resource. An allocation allocates * one relay resource, and optionally it may reserve another resource. */ struct pj_turn_relay_res { /** Hash table key */ struct { /** Transport type. */ int tp_type; /** Transport/relay address */ pj_sockaddr addr; } hkey; /** Allocation who requested or reserved this resource. */ pj_turn_allocation *allocation; /** Username used in credential */ pj_str_t user; /** Realm used in credential. */ pj_str_t realm; /** Lifetime, in seconds. */ unsigned lifetime; /** Relay/allocation expiration time */ pj_time_val expiry; /** Timeout timer entry */ pj_timer_entry timer; /** Transport. */ struct { /** Transport/relay socket */ pj_sock_t sock; /** Transport/relay ioqueue */ pj_ioqueue_key_t *key; /** Read operation key. */ pj_ioqueue_op_key_t read_key; /** The incoming packet buffer */ char rx_pkt[PJ_TURN_MAX_PKT_LEN]; /** Source address of the packet. */ pj_sockaddr src_addr; /** Source address length */ int src_addr_len; /** The outgoing packet buffer. This must be 3wbit aligned. */ char tx_pkt[PJ_TURN_MAX_PKT_LEN+4]; } tp; }; /****************************************************************************/ /* * TURN Allocation API */ /** * This structure describes key to lookup TURN allocations in the * allocation hash table. */ typedef struct pj_turn_allocation_key { int tp_type; /**< Transport type. */ pj_sockaddr clt_addr; /**< Client's address. */ } pj_turn_allocation_key; /** * This structure describes TURN pj_turn_allocation session. */ struct pj_turn_allocation { /** Hash table key to identify client. */ pj_turn_allocation_key hkey; /** Pool for this allocation. */ pj_pool_t *pool; /** Object name for logging identification */ char *obj_name; /** Client info (IP address and port) */ char info[80]; /** Mutex */ pj_lock_t *lock; /** Server instance. */ pj_turn_srv *server; /** Transport to send/receive packets to/from client. */ pj_turn_transport *transport; /** The relay resource for this allocation. */ pj_turn_relay_res relay; /** Relay resource reserved by this allocation, if any */ pj_turn_relay_res *resv; /** Requested bandwidth */ unsigned bandwidth; /** STUN session for this client */ pj_stun_session *sess; /** Credential for this STUN session. */ pj_stun_auth_cred cred; /** Peer hash table (keyed by peer address) */ pj_hash_table_t *peer_table; /** Channel hash table (keyed by channel number) */ pj_hash_table_t *ch_table; }; /** * This structure describes the hash table key to lookup TURN * permission. */ typedef struct pj_turn_permission_key { /** Peer address. */ pj_sockaddr peer_addr; } pj_turn_permission_key; /** * This structure describes TURN pj_turn_permission or channel. */ struct pj_turn_permission { /** Hash table key */ pj_turn_permission_key hkey; /** TURN allocation that owns this permission/channel */ pj_turn_allocation *allocation; /** Optional channel number, or PJ_TURN_INVALID_CHANNEL if channel number * is not requested for this permission. */ pj_uint16_t channel; /** Permission expiration time. */ pj_time_val expiry; }; /** * Create new allocation. */ PJ_DECL(pj_status_t) pj_turn_allocation_create(pj_turn_transport *transport, const pj_sockaddr_t *src_addr, unsigned src_addr_len, const pj_stun_rx_data *rdata, pj_stun_session *srv_sess, pj_turn_allocation **p_alloc); /** * Destroy allocation. */ PJ_DECL(void) pj_turn_allocation_destroy(pj_turn_allocation *alloc); /** * Handle incoming packet from client. */ PJ_DECL(void) pj_turn_allocation_on_rx_client_pkt(pj_turn_allocation *alloc, pj_turn_pkt *pkt); /** * Handle transport closure. */ PJ_DECL(void) pj_turn_allocation_on_transport_closed(pj_turn_allocation *alloc, pj_turn_transport *tp); /****************************************************************************/ /* * TURN Listener API */ /** * This structure describes TURN listener socket. A TURN listener socket * listens for incoming connections from clients. */ struct pj_turn_listener { /** Object name/identification */ char *obj_name; /** Slightly longer info about this listener */ char info[80]; /** TURN server instance. */ pj_turn_srv *server; /** Listener index in the server */ unsigned id; /** Pool for this listener. */ pj_pool_t *pool; /** Transport type. */ int tp_type; /** Bound address of this listener. */ pj_sockaddr addr; /** Socket. */ pj_sock_t sock; /** Flags. */ unsigned flags; /** Destroy handler */ pj_status_t (*destroy)(pj_turn_listener*); }; /** * This structure describes TURN transport socket which is used to send and * receive packets towards client. */ struct pj_turn_transport { /** Object name/identification */ char *obj_name; /** Slightly longer info about this listener */ char *info; /** Listener instance */ pj_turn_listener *listener; /** Sendto handler */ pj_status_t (*sendto)(pj_turn_transport *tp, const void *packet, pj_size_t size, unsigned flag, const pj_sockaddr_t *addr, int addr_len); /** Addref handler */ void (*add_ref)(pj_turn_transport *tp, pj_turn_allocation *alloc); /** Decref handler */ void (*dec_ref)(pj_turn_transport *tp, pj_turn_allocation *alloc); }; /** * An incoming packet. */ struct pj_turn_pkt { /** Pool for this packet */ pj_pool_t *pool; /** Transport where the packet was received. */ pj_turn_transport *transport; /** Packet buffer (must be 32bit aligned). */ pj_uint8_t pkt[PJ_TURN_MAX_PKT_LEN]; /** Size of the packet */ pj_size_t len; /** Arrival time. */ pj_time_val rx_time; /** Source transport type and source address. */ pj_turn_allocation_key src; /** Source address length. */ int src_addr_len; }; /** * Create a UDP listener on the specified port. */ PJ_DECL(pj_status_t) pj_turn_listener_create_udp(pj_turn_srv *srv, int af, const pj_str_t *bound_addr, unsigned port, unsigned concurrency_cnt, unsigned flags, pj_turn_listener **p_lis); /** * Create a TCP listener on the specified port. */ PJ_DECL(pj_status_t) pj_turn_listener_create_tcp(pj_turn_srv *srv, int af, const pj_str_t *bound_addr, unsigned port, unsigned concurrency_cnt, unsigned flags, pj_turn_listener **p_lis); /** * Destroy listener. */ PJ_DECL(pj_status_t) pj_turn_listener_destroy(pj_turn_listener *listener); /** * Add a reference to a transport. */ PJ_DECL(void) pj_turn_transport_add_ref(pj_turn_transport *transport, pj_turn_allocation *alloc); /** * Decrement transport reference counter. */ PJ_DECL(void) pj_turn_transport_dec_ref(pj_turn_transport *transport, pj_turn_allocation *alloc); /****************************************************************************/ /* * TURN Server API */ /** * This structure describes TURN pj_turn_srv instance. */ struct pj_turn_srv { /** Object name */ char *obj_name; /** Core settings */ struct { /** Pool factory */ pj_pool_factory *pf; /** Pool for this server instance. */ pj_pool_t *pool; /** Global Ioqueue */ pj_ioqueue_t *ioqueue; /** Mutex */ pj_lock_t *lock; /** Global timer heap instance. */ pj_timer_heap_t *timer_heap; /** Number of listeners */ unsigned lis_cnt; /** Array of listeners. */ pj_turn_listener **listener; /** STUN session to handle initial Allocate request. */ pj_stun_session *stun_sess; /** Number of worker threads. */ unsigned thread_cnt; /** Array of worker threads. */ pj_thread_t **thread; /** Thread quit signal */ pj_bool_t quit; /** STUN config. */ pj_stun_config stun_cfg; /** STUN auth credential. */ pj_stun_auth_cred cred; /** Thread local ID for storing credential */ long tls_key, tls_data; } core; /** Hash tables */ struct { /** Allocations hash table, indexed by transport type and * client address. */ pj_hash_table_t *alloc; /** Relay resource hash table, indexed by transport type and * relay address. */ pj_hash_table_t *res; } tables; /** Ports settings */ struct { /** Minimum UDP port number. */ pj_uint16_t min_udp; /** Maximum UDP port number. */ pj_uint16_t max_udp; /** Next UDP port number. */ pj_uint16_t next_udp; /** Minimum TCP port number. */ pj_uint16_t min_tcp; /** Maximum TCP port number. */ pj_uint16_t max_tcp; /** Next TCP port number. */ pj_uint16_t next_tcp; } ports; }; /** * Create server. */ PJ_DECL(pj_status_t) pj_turn_srv_create(pj_pool_factory *pf, pj_turn_srv **p_srv); /** * Destroy server. */ PJ_DECL(pj_status_t) pj_turn_srv_destroy(pj_turn_srv *srv); /** * Add listener. */ PJ_DECL(pj_status_t) pj_turn_srv_add_listener(pj_turn_srv *srv, pj_turn_listener *lis); /** * Register an allocation. */ PJ_DECL(pj_status_t) pj_turn_srv_register_allocation(pj_turn_srv *srv, pj_turn_allocation *alloc); /** * Unregister an allocation. */ PJ_DECL(pj_status_t) pj_turn_srv_unregister_allocation(pj_turn_srv *srv, pj_turn_allocation *alloc); /** * This callback is called by UDP listener on incoming packet. */ PJ_DECL(void) pj_turn_srv_on_rx_pkt(pj_turn_srv *srv, pj_turn_pkt *pkt); #endif /* __PJ_TURN_SRV_TURN_H__ */ ================================================ FILE: deps/pjsip/pjsip/build/Makefile ================================================ # For common OSes, test's main() is defined in main.c. # OS specific configuration may want to put it in different file. # For example, see os-rtems.mak in current directory. export TEST_OBJS = main.o include ../../build.mak include ../../version.mak include $(PJDIR)/build/common.mak RULES_MAK := $(PJDIR)/build/rules.mak PJLIB_LIB:=../../pjlib/lib/libpj-$(TARGET_NAME)$(LIBEXT) PJLIB_UTIL_LIB:=../../pjlib-util/lib/libpjlib-util-$(TARGET_NAME)$(LIBEXT) PJMEDIA_LIB:=../../pjmedia/lib/libpjmedia-$(TARGET_NAME)$(LIBEXT) PJMEDIA_CODEC_LIB:=../../pjmedia/lib/libpjmedia-codec-$(TARGET_NAME)$(LIBEXT) export PJSIP_LIB:=../lib/libpjsip-$(TARGET_NAME)$(LIBEXT) export PJSIP_UA_LIB:=../lib/libpjsip-ua-$(TARGET_NAME)$(LIBEXT) export PJSIP_SIMPLE_LIB:=../lib/libpjsip-simple-$(TARGET_NAME)$(LIBEXT) ############################################################################### # Gather all flags. # export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ $(CFLAGS) $(CC_INC)../include \ $(CC_INC)../../pjlib/include \ $(CC_INC)../../pjlib-util/include \ $(CC_INC)../../pjnath/include \ $(CC_INC)../../pjmedia/include export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ $(HOST_CXXFLAGS) $(CXXFLAGS) ############################################################################### # Defines for building PJSIP core library # export PJSIP_SRCDIR = ../src/pjsip export PJSIP_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ sip_config.o sip_multipart.o \ sip_errno.o sip_msg.o sip_parser.o sip_tel_uri.o sip_uri.o \ sip_endpoint.o sip_util.o sip_util_proxy.o \ sip_resolve.o sip_transport.o sip_transport_loop.o \ sip_transport_udp.o sip_transport_tcp.o \ sip_transport_tls.o sip_auth_aka.o sip_auth_client.o \ sip_auth_msg.o sip_auth_parser.o \ sip_auth_server.o \ sip_transaction.o sip_util_statefull.o \ sip_dialog.o sip_ua_layer.o export PJSIP_CFLAGS += $(_CFLAGS) ############################################################################### # Defines for building PJSIP UA library # export PJSIP_UA_SRCDIR = ../src/pjsip-ua export PJSIP_UA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ sip_inv.o sip_reg.o sip_replaces.o sip_xfer.o \ sip_100rel.o sip_timer.o export PJSIP_UA_CFLAGS += $(_CFLAGS) ############################################################################### # Defines for building PJSIP SIMPLE library # export PJSIP_SIMPLE_SRCDIR = ../src/pjsip-simple export PJSIP_SIMPLE_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ errno.o evsub.o evsub_msg.o iscomposing.o \ mwi.o pidf.o presence.o presence_body.o publishc.o \ rpid.o xpidf.o export PJSIP_SIMPLE_CFLAGS += $(_CFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Defines for building test application # export TEST_SRCDIR = ../src/test export TEST_OBJS += dlg_core_test.o dns_test.o msg_err_test.o \ msg_logger.o msg_test.o multipart_test.o regc_test.o \ test.o transport_loop_test.o transport_tcp_test.o \ transport_test.o transport_udp_test.o \ tsx_basic_test.o tsx_bench.o tsx_uac_test.o \ tsx_uas_test.o txdata_test.o uri_test.o \ inv_offer_answer_test.o export TEST_CFLAGS += $(_CFLAGS) export TEST_LDFLAGS += $(PJ_LDFLAGS) $(PJ_LDLIBS) $(LDFLAGS) export TEST_EXE := ../bin/pjsip-test-$(TARGET_NAME)$(HOST_EXE) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Main entry # # TARGETS := pjsip pjsip-ua pjsip-simple .PHONY: $(TARGETS) all: $(TARGETS) doc: cd .. && rm -rf docs/$(PJ_VERSION) && doxygen docs/doxygen.cfg @if [ -n "$(WWWDIR)" ] && ! [ -d "$(WWWDIR)/docs/$(PJ_VERSION)/pjsip/docs/html" ] ; then \ echo "Creating docs/$(PJ_VERSION)/pjsip/docs/html" ; \ mkdir -p $(WWWDIR)/docs/$(PJ_VERSION)/pjsip/docs/html ; \ fi @if [ -n "$(WWWDIR)" ] && [ -d "$(WWWDIR)/docs/$(PJ_VERSION)/pjsip/docs/html" ] ; then \ echo "Copying docs/$(PJ_VERSION) to $(WWWDIR)/docs/$(PJ_VERSION)/pjsip/docs/html.." ; \ cp -v -a ../docs/$(PJ_VERSION)/html/* $(WWWDIR)/docs/$(PJ_VERSION)/pjsip/docs/html/ ; \ fi dep: depend distclean: realclean .PHONY: dep depend pjsip pjsip-ua clean realclean distclean pjsip: $(MAKE) -f $(RULES_MAK) APP=PJSIP app=pjsip $(PJSIP_LIB) pjsip-ua: $(MAKE) -f $(RULES_MAK) APP=PJSIP_UA app=pjsip-ua $(PJSIP_UA_LIB) pjsip-simple: $(MAKE) -f $(RULES_MAK) APP=PJSIP_SIMPLE app=pjsip-simple $(PJSIP_SIMPLE_LIB) pjsip-test: pjsip $(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $(TEST_EXE) .PHONY: ../lib/pjsip.ko ../lib/pjsip.ko: echo Making $@ $(MAKE) -f $(RULES_MAK) APP=PJSIP app=pjsip $@ .PHONY: ../lib/pjsip-ua.ko ../lib/pjsip-ua.ko: echo Making $@ $(MAKE) -f $(RULES_MAK) APP=PJSIP_UA app=pjsip-ua $@ .PHONY: ../lib/pjsip-simple.ko ../lib/pjsip-simple.ko: echo Making $@ $(MAKE) -f $(RULES_MAK) APP=PJSIP_SIMPLE app=pjsip-simple $@ clean: $(MAKE) -f $(RULES_MAK) APP=PJSIP app=pjsip $@ $(MAKE) -f $(RULES_MAK) APP=PJSIP_UA app=pjsip-ua $@ $(MAKE) -f $(RULES_MAK) APP=PJSIP_SIMPLE app=pjsip-simple $@ $(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $@ depend: $(MAKE) -f $(RULES_MAK) APP=PJSIP app=pjsip $@ $(MAKE) -f $(RULES_MAK) APP=PJSIP_UA app=pjsip-ua $@ $(MAKE) -f $(RULES_MAK) APP=PJSIP_SIMPLE app=pjsip-simple $@ $(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $@ echo '$(TEST_EXE): $(PJMEDIA_LIB) $(PJSIP_SIMPLE_LIB) $(PJSIP_UA_LIB) $(PJSIP_LIB) $(PJNATH_LIB) $(PJLIB_UTIL_LIB) $(PJLIB_LIB)' >> .pjsip-test-$(TARGET_NAME).depend realclean: $(subst @@,$(subst /,$(HOST_PSEP),.pjsip-$(TARGET_NAME).depend),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),.pjsip-ua-$(TARGET_NAME).depend),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),.pjsip-simple-$(TARGET_NAME).depend),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=PJSIP app=pjsip $@ $(MAKE) -f $(RULES_MAK) APP=PJSIP_UA app=pjsip-ua $@ $(MAKE) -f $(RULES_MAK) APP=PJSIP_SIMPLE app=pjsip-simple $@ $(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $@ ================================================ FILE: deps/pjsip/pjsip/build/os-auto.mak.in ================================================ ================================================ FILE: deps/pjsip/pjsip/docs/PJSUA-TESTING.txt ================================================ TESTING SANITY CHECKS: - Do pjlib-test - Do pjsip-test BASIC FLOW TEST (compaq1.cfg, compaq2.cfg); - with other pjsua - with EyeBeam - Register - with Route - without Route - with TCP - Presence (client and server) - Call (UAC and UAS) - hold and being held - DTMF send/receive - IM and typing - Call transfer (with and without norefersub) - Call Hold - Re-Invite - DTMF - RTCP - TCP (if there's UDP route, then Contact will be UDP) - Repeat basic flow test without Route set (to wheter TCP is correctly specified in the contact etc) - STUN test (fwd1.cfg) COMPATIBILITY WITH FWD - presence (xpidf) MULTIPLE ACCOUNTS (combo.cfg) DIGEST with qop=auth (sipcenter?) AUDIO QUALITY - call to another pjsua that loops incoming call TEST THE SAMPLES LINUX TEST UPDATE DOCUMENTATION ================================================ FILE: deps/pjsip/pjsip/docs/TRANSPORT-PROBLEMS.TXT ================================================ Dialog's Contact Address Problems Background When creating a dialog (UAC or UAS dialog), application needs to specify local address to be put in the Contact header generated by the dialog. This address normally is derived from the transport address created by the application. This can be a complicated thing to do, because application needs to select the appropriate address based on the transport being used to contact remote peer, and it's not so easy especially when multiple transports are created, or when multiple interfaces are present in the host (multihoming). (Note: also similarly for client registration). So a change is being/to be made to allow the dialog to automate this process. If application specify NULL as the value of the local contact when creating a dialog, then the dialog should choose the best transport address according to the following rules. 1a. UAC UAC SHOULD select the Contact address based on the transport to be used to send initial request to target (this can be deduced by the ";transport" param in the target URI). For example, if initial transport is UDP, then the Contact SHOULD specify the appropriate UDP transport address. When there are multiple transports available to reach the destination (such as multiple UDP transports, although this is not really tested/supported), then which transport to use follows the policy used by the transport manager. Note that target can be the URI in the request URI, or the first route in the route set. Because initial route set is specified AFTER dialog is created, the Contact header generation is done when the initial request is created/sent. Unsolved problem(s): - what if the remote Contact header in the 2xx response indicates different transport? Ideally the local Contact needs to be updated too, but this is quite a complicated problem, because if we change the local Contact, then remote may change its Contact too, and this can result in an endles loop. 1b. UAS UAS MUST select the Contact address based on the value of Contact header and the Record-Route set in the incoming request. 2. Multihoming Problem: If application does not specify the address to be used when creating SIP transport, then transport address is calculated based on the primary IP address of the local host. Sometimes this is not the correct IP to be used by the dialog, especially when there are multiple interfaces in the host. If this "incorrect" address is specified in the Contact, then remote would not be able to send request to local host. Workaround: Change the behavior of determining local IP. Instead of calling gethostbyname() for local host, the local interface IP is determined by creating an UDP socket, do UDP connect() to some public host, and get the local IP address of the socket. Although this does not fully solve the problem, hopefully it solves many problems in typical desktop systems, where an "unwanted" adapter takes precendence over the "wanted" adapter on the host/address resolution. A fully working solution would probably involve having a table to track which interface to use based on the destination, or to query the host's routing table. ================================================ FILE: deps/pjsip/pjsip/docs/doxygen.cfg ================================================ # Doxyfile 1.3-rc3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # General configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "PJSIP Reference" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = $(PJ_VERSION) # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = docs/$(PJ_VERSION) # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, # Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en # (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, # Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian. OUTPUT_LANGUAGE = English # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these class will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = NO # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = YES # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited # members of a class in the documentation of that class as if those members were # ordinary class members. Constructors, destructors and assignment operators of # the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. It is allowed to use relative paths in the argument list. #STRIP_FROM_PATH = "/cygdrive/e/project/bulukucing.org/pjsip/src" STRIP_FROM_PATH = "/c/project/pjproject/pjsip" # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower case letters. If set to YES upper case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # users are adviced to set this option to NO. CASE_SENSE_NAMES = YES # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explict @brief command for a brief description. JAVADOC_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = YES # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # reimplements. INHERIT_DOCS = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consist of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. # For instance some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources # only. Doxygen will then generate output that is more tailored for Java. # For instance namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES TYPEDEF_HIDES_STRUCT = YES #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = docs include # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp # *.h++ *.idl *.odl FILE_PATTERNS = *.h *.hpp # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = *_i.h pjsua_internal.h # The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories # that are symbolic links (a Unix filesystem feature) are excluded from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. EXCLUDE_PATTERNS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = ../pjsip-apps/src/samples ../pjsip-apps/src/pjsua # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = docs ../pjmedia/docs # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. INPUT_FILTER = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = /c/project/pjproject/pjsip #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .htm # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = docs/header.html # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = docs/footer.html # The HTML_STYLESHEET tag can be used to specify a user defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output dir. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non empty doxygen will try to run # the html help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the Html help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimised for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assigments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_XML = NO # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_PREDEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. PREDEFINED = PJ_DECL(x)=x PJ_DEF(x)=x PJ_IDECL(x)=x \ PJ_IDEF(x)=x PJ_INLINE(x)=x PJ_DECL_DATA(x)=x \ PJMEDIA_HAS_SRTP=1 \ PJMEDIA_STREAM_ENABLE_KA=1 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse the # parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::addtions related to external references #--------------------------------------------------------------------------- # The TAGFILES tag can be used to specify one or more tagfiles. TAGFILES = ../pjlib/docs/pjlib.tag=../../../pjlib/docs/html ../pjlib-util/docs/pjlib-util.tag=../../../pjlib-util/docs/html ../pjnath/docs/pjnath.tag=../../../pjnath/docs/html ../pjmedia/docs/pjmedia.tag=../../../pjmedia/docs/html # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = docs/pjsip.tag # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = NO # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). #PERL_PATH = /usr/bin/perl PERL_PATH = /c/Perl/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or # super classes. Setting the tag to NO turns the diagrams off. Note that this # option is superceded by the HAVE_DOT option below. This is only a fallback. It is # recommended to install and use dot, since it yield more powerful graphs. CLASS_DIAGRAMS = YES # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found on the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_WIDTH = 1024 # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_HEIGHT = 1024 # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermedate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::addtions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO ================================================ FILE: deps/pjsip/pjsip/docs/doxygen.h ================================================ /* $Id$ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * @file doxygen.h * @brief PJSIP Doxygen's mainpage. */ /*////////////////////////////////////////////////////////////////////////// */ /* INTRODUCTION PAGE */ /** \n @mainpage PJSIP - Open Source SIP Stack \n \n @section intro_sec Introduction PJSIP is an Open Source SIP prototol stack, designed to be very small in footprint, have high performance, and very flexible. @subsection hist_sec History PJSIP has been actively developed since 2003, but its history goes well beyond that. The author has been developing SIP stack since 1999 during RFC 2543 era, and after several experimentation with different approaches in the programming (the first stack actually was in C++!), and also with the evolution of the SIP protocol itself, the current/third generation of PJSIP (the 0.2.9 version is the second generation) can be considered as pretty stable in term of design, and should incorporate all design considerations (and implementation tricks!) that have been learned over the years. Of course only time will tell if this statement can still be held true in the future. \n \n @section pjsipgetting_started Getting Started PJSIP consists of multiple levels of APIs, which each of them layered on top of another. Because of this, new readers may find it a bit difficult to find the place to start. In general, I think perhaps I can recommend two approaches on using PJSIP. \n @subsection getting_started_high Using PJSUA API @ref PJSUA_LIB wraps together all SIP components and media into a high level API, suitable for creating typical SIP user agent applications. It features easy to use API for: - multiple client registration (accounts), - high level SIP and media session (calls), - buddy list, presence and instant messaging, - powerful and very easy to use media manipulation, while maintaining some space for customization (custom SIP transport, custom SIP media, etc.) needed by some types of applications. @ref PJSUA_LIB is also aimed to be able to run on devices such as PDA or mobile phones, by carefully allowing application to set the appropriate threading strategy and memory limits (number of calls, media ports, etc.). However, @ref PJSUA_LIB may not be the most suitable API for some types of applications, since it is directed towards an easy to use API. For more more advanced use, you may better implement the application by using PJSIP + PJMEDIA directly, as described below. \n @subsection getting_started_pjsip_pjmedia Using PJSIP and PJMEDIA Directly For the ultimate flexibility and power, using PJSIP and PJMEDIA directly is the way to go. The drawback will be, of course, steeper learning curve. However, the following links may provide some useful information: - PJSIP Developer's Guide PDF document is the ultimate guide to understand PJSIP design concept. - there are some samples in pjsip-apps/src/samples directory. - @ref PJSUA_LIB source code may also be useful to see how high level API are implemented with PJSIP/PJMEDIA. - and finally, you can always Use the Source! \n \n @section this_doc About This Document This document contains the reference information about PJSIP. For more in-depth guide (and information in general), readers are encouraged to read the PJSIP Developer's Guide PDF document which can be downloaded from http://www.pjsip.org/docs.htm. \n @subsection doc_how_to_read How to Read This Document For main navigation, please go to Modules link on top of this page. This document was generated with Doxygen from PJSIP header files. \n \n @section pjsip_toc Documentation Contents Click on Modules link on top of this page to get the detailed table of contents. The following are top level sections in the Modules, as laid out in the following diagram: \image html pjsip-arch.jpg "Static Library Layout" Enumerating the static libraries from the bottom: - PJLIB, is the platform abstraction and framework library, on which all other libraries depend, - PJLIB-UTIL, provides auxiliary functions such as text scanning, XML, and STUN, - PJMEDIA is the multimedia framework, - PJMEDIA-CODEC is the placeholder for media codecs, - @ref PJSIP_CORE (PJSIP-CORE) is the very core of the PJSIP library, and contains the SIP @ref PJSIP_ENDPT, which is the owner/manager for all SIP objects in the application, messaging elements, parsing, transport management, module management, and stateless operations, and also contains: - The @ref PJSIP_TRANSACT module inside PJSIP-CORE provides stateful operation, and is the base for higher layer features such as dialogs, - The @ref PJSIP_UA module inside PJSIP-CORE manages dialogs, and supports dialog usages, - @ref PJSIP_SIMPLE (PJSIP-SIMPLE) provides the base SIP event framework (which uses the common/base dialog framework) and implements presence on top of it, and is also used by call transfer functions, - @ref PJSIP_HIGH_UA (PJSIP-UA) is the high level abstraction of INVITE sessions (using the common/base dialog framework). This library also provides SIP client registration and call transfer functionality, - and finally, @ref PJSUA_LIB (PJSUA-LIB) is the highest level of abstraction, which wraps together all above functionalities into high level, easy to use API. */ /** @page page_pjsip_samples PJSIP Samples I wish I could write more samples, but for now here are some samples or working applications that are available from the source tree: - @ref page_pjsip_sample_sipstateless_c\n This is about the simplest SIP application with PJSIP, all it does is respond all incoming requests with 501 (Not Implemented) response statelessly. - @ref page_pjsip_sample_simple_ua_c\n This is a very simple SIP User Agent application that only use PJSIP (without PJSIP-UA). It's able to make and receive call, and play media to the sound device. - @ref page_pjsip_sample_simple_pjsuaua_c\n Very simple SIP User Agent with registration, call, and media, using PJSUA-API, all in under 200 lines of code. - @ref page_pjsip_samples_pjsua\n This is the reference implementation for PJSIP and PJMEDIA. PJSUA is a console based application, designed to be simple enough to be readble, but powerful enough to demonstrate all features available in PJSIP and PJMEDIA.\n Screenshot on WinXP: \image html pjsua.jpg "pjsua on WinXP" - @ref page_pjmedia_samples_siprtp_c\n This is a useful program (integrated with PJSIP) to actively measure the network quality/impairment parameters by making one or more SIP calls (or receiving one or more SIP calls) and display the network impairment of each stream direction at the end of the call. The program is able to measure network quality parameters such as jitter, packet lost/reorder/duplicate, round trip time, etc.\n Note that the remote peer MUST support RTCP so that network quality of each direction can be calculated. Using siprtp for both endpoints is recommended.\n Screenshots on WinXP: \image html siprtp.jpg "siprtp screenshot on WinXP" - @ref page_pjsip_perf_c\n This sample contains a complete implementation of a SIP performance measurement tool. Unlike other tool such SIPp, pjsip-perf is geared more towards finding the performance of an endpoint by flooding the endpoint with some requests and time the completion of the requests.\n Screenshots on Linux console: \image html pjsip-perf.jpg "pjsip-perf screenshot on Linux" */ /** * \page page_pjsip_samples_pjsua PJSUA * * This is the reference implementation for PJSIP and PJMEDIA. * PJSUA is a console based application, designed to be simple enough * to be readble, but powerful enough to demonstrate all features * available in PJSIP and PJMEDIA. * * This file is pjsip-apps/src/pjsua/pjsua_app.c * * Screenshot on WinXP: \image html pjsua.jpg "pjsua on WinXP" * * \includelineno pjsua_app.c */ /** * \page page_pjsip_sample_simple_ua_c Samples: Simple UA * * This is a very simple SIP User Agent application that only use PJSIP * (without PJSIP-UA). It's able to make and receive call, and play * media to the sound device. * * \includelineno simpleua.c */ /** * \page page_pjsip_sample_simple_pjsuaua_c Samples: Simple PJSUA * * Very simple SIP User Agent with registration, call, and media, all * in under 200 lines of code. * * \includelineno simple_pjsua.c */ /** * \page page_pjsip_sample_sipstateless_c Samples: Stateless SIP Endpoint * * This is about the simplest SIP application with PJSIP, all it does is * respond all incoming requests with 501 (Not Implemented) response * statelessly. * * \includelineno sipstateless.c */ /** * \page page_pjmedia_samples_siprtp_c Samples: siprtp - SIP with RTCP Quality Monitoring * * This source is an example to demonstrate using SIP and RTP/RTCP framework * to measure the network quality/impairment from the SIP call. This * program can be used to make calls or to receive calls from other * SIP endpoint (or other siprtp program), and to display the media * quality statistics at the end of the call. * * Note that the remote peer must support RTCP. * * The layout of the program has been designed so that custom reporting * can be generated instead of plain human readable text. * * The source code of the file is pjsip-apps/src/samples/siprtp.c * * Screenshots on WinXP: \image html siprtp.jpg * * \includelineno siprtp.c */ /** * \page page_pjsip_perf_c Samples: pjsip-perf - SIP Performance Benchmarking Tool * * This sample contains a complete implementation of a SIP performance * measurement tool. Unlike other tool such SIPp, pjsip-perf is geared * more towards finding the performance of an endpoint by flooding the * endpoint with some requests and time the completion of the requests. * * The source code of the file is pjsip-apps/src/samples/pjsip-perf.c * * Screenshots on Linux console: \image html pjsip-perf.jpg * * \includelineno pjsip-perf.c */ ================================================ FILE: deps/pjsip/pjsip/docs/footer.html ================================================

 


PJSIP Open Source, high performance, small footprint, and very very portable SIP stack
Copyright (C) 2006-2008 Teluu Inc.
================================================ FILE: deps/pjsip/pjsip/docs/header.html ================================================ $title ($projectnumber)

Home --> Documentations --> PJSIP Reference

================================================ FILE: deps/pjsip/pjsip/include/pjsip/print_util.h ================================================ /* $Id: print_util.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_PRINT_H__ #define __PJSIP_PRINT_H__ #define copy_advance_char_check(buf,chr) \ do { \ if (1 >= (endbuf-buf)) return -1; \ *buf++ = chr; \ } while (0) #define copy_advance_check(buf,str) \ do { \ if ((str).slen >= (endbuf-buf)) return -1; \ pj_memcpy(buf, (str).ptr, (str).slen); \ buf += (str).slen; \ } while (0) #define copy_advance_pair_check(buf,str1,len1,str2) \ do { \ if (str2.slen) { \ printed = len1+(int)str2.slen; \ if (printed >= (endbuf-buf)) return -1; \ pj_memcpy(buf,str1,len1); \ pj_memcpy(buf+len1, str2.ptr, str2.slen); \ buf += printed; \ } \ } while (0) #define copy_advance_pair_quote_check(buf,str1,len1,str2,quotebegin,quoteend) \ do { \ if (str2.slen) { \ printed = len1+str2.slen+2; \ if (printed >= (endbuf-buf)) return -1; \ pj_memcpy(buf,str1,len1); \ *(buf+len1)=quotebegin; \ pj_memcpy(buf+len1+1, str2.ptr, str2.slen); \ *(buf+printed-1) = quoteend; \ buf += printed; \ } \ } while (0) #define copy_advance_pair_quote(buf,str1,len1,str2,quotebegin,quoteend) \ do { \ printed = len1+(int)str2.slen+2; \ if (printed >= (endbuf-buf)) return -1; \ pj_memcpy(buf,str1,len1); \ *(buf+len1)=quotebegin; \ pj_memcpy(buf+len1+1, str2.ptr, str2.slen); \ *(buf+printed-1) = quoteend; \ buf += printed; \ } while (0) #define copy_advance_pair_escape(buf,str1,len1,str2,unres) \ do { \ if (str2.slen) { \ if (len1+str2.slen >= (endbuf-buf)) return -1; \ pj_memcpy(buf,str1,len1); \ printed=(int)pj_strncpy2_escape(buf+len1,&str2,(endbuf-buf-len1), \ &unres);\ if (printed < 0) return -1; \ buf += (printed+len1); \ } \ } while (0) #define copy_advance_no_check(buf,str) \ do { \ pj_memcpy(buf, (str).ptr, (str).slen); \ buf += (str).slen; \ } while (0) #define copy_advance_escape(buf,str,unres) \ do { \ printed = \ (int)pj_strncpy2_escape(buf, &(str), (endbuf-buf), &(unres)); \ if (printed < 0) return -1; \ buf += printed; \ } while (0) #define copy_advance_pair_no_check(buf,str1,len1,str2) \ if (str2.slen) { \ pj_memcpy(buf,str1,len1); \ pj_memcpy(buf+len1, str2.ptr, str2.slen); \ buf += len1+str2.slen; \ } #define copy_advance copy_advance_check #define copy_advance_pair copy_advance_pair_check /* * Append str1 and quoted str2 and copy to buf. * No string is copied if str2 is empty. */ #define copy_advance_pair_quote_cond(buf,str1,len1,str2,quotebegin,quoteend) \ do { \ if (str2.slen && *str2.ptr!=quotebegin) \ copy_advance_pair_quote(buf,str1,len1,str2,quotebegin,quoteend); \ else \ copy_advance_pair(buf,str1,len1,str2); \ } while (0) /* * Append str1 and quoted str2 and copy to buf. * In case str2 is empty, str1 will be appended with empty quote. */ #define copy_advance_pair_quote_cond_always(buf,str1,len1,str2,quotebegin, \ quoteend)\ do { \ if (!str2.slen) \ copy_advance_pair_quote(buf,str1,len1,str2,quotebegin,quoteend); \ else \ copy_advance_pair_quote_cond(buf,str1,len1,str2,quotebegin,quoteend);\ } while (0) /* * Internal type declarations. */ typedef void* (*pjsip_hdr_clone_fptr)(pj_pool_t *, const void*); typedef int (*pjsip_hdr_print_fptr)(void *hdr, char *buf, pj_size_t len); typedef struct pjsip_hdr_name_info_t { char *name; unsigned name_len; char *sname; } pjsip_hdr_name_info_t; extern const pjsip_hdr_name_info_t pjsip_hdr_names[]; PJ_INLINE(void) init_hdr(void *hptr, pjsip_hdr_e htype, void *vptr) { pjsip_hdr *hdr = (pjsip_hdr*) hptr; hdr->type = htype; hdr->name.ptr = pjsip_hdr_names[htype].name; hdr->name.slen = pjsip_hdr_names[htype].name_len; if (pjsip_hdr_names[htype].sname) { hdr->sname.ptr = pjsip_hdr_names[htype].sname; hdr->sname.slen = 1; } else { hdr->sname = hdr->name; } hdr->vptr = (pjsip_hdr_vptr*) vptr; pj_list_init(hdr); } #endif /* __PJSIP_PRINT_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_auth.h ================================================ /* $Id: sip_auth.h 4214 2012-07-25 14:29:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_AUTH_SIP_AUTH_H__ #define __PJSIP_AUTH_SIP_AUTH_H__ /** * @file pjsip_auth.h * @brief SIP Authorization Module. */ #include #include PJ_BEGIN_DECL /** * @addtogroup PJSIP_AUTH * @ingroup PJSIP_CORE * @brief Client and server side authentication framework. */ /** * @defgroup PJSIP_AUTH_API Authentication API's * @ingroup PJSIP_AUTH * @brief Structures and functions to perform authentication. * @{ */ /** Length of digest string. */ #define PJSIP_MD5STRLEN 32 /** Type of data in the credential information in #pjsip_cred_info. */ typedef enum pjsip_cred_data_type { PJSIP_CRED_DATA_PLAIN_PASSWD=0, /**< Plain text password. */ PJSIP_CRED_DATA_DIGEST =1, /**< Hashed digest. */ PJSIP_CRED_DATA_EXT_AKA =16 /**< Extended AKA info is available */ } pjsip_cred_data_type; /** Authentication's quality of protection (qop) type. */ typedef enum pjsip_auth_qop_type { PJSIP_AUTH_QOP_NONE, /**< No quality of protection. */ PJSIP_AUTH_QOP_AUTH, /**< Authentication. */ PJSIP_AUTH_QOP_AUTH_INT, /**< Authentication with integrity protection. */ PJSIP_AUTH_QOP_UNKNOWN /**< Unknown protection. */ } pjsip_auth_qop_type; /** * Type of callback function to create authentication response. * Application can specify this callback in \a cb field of the credential info * (#pjsip_cred_info) and specifying PJSIP_CRED_DATA_DIGEST_CALLBACK as * \a data_type. When this function is called, most of the fields in the * \a auth authentication response will have been filled by the framework. * Application normally should just need to calculate the response digest * of the authentication response. * * @param pool Pool to allocate memory from if application needs to. * @param chal The authentication challenge sent by server in 401 * or 401 response, in either Proxy-Authenticate or * WWW-Authenticate header. * @param cred The credential that has been selected by the framework * to authenticate against the challenge. * @param auth The authentication response which application needs to * calculate the response digest. * * @return Application may return non-PJ_SUCCESS to abort the * authentication process. When this happens, the * framework will return failure to the original function * that requested authentication. */ typedef pj_status_t (*pjsip_cred_cb)(pj_pool_t *pool, const pjsip_digest_challenge *chal, const pjsip_cred_info *cred, const pj_str_t *method, pjsip_digest_credential *auth); /** * This structure describes credential information. * A credential information is a static, persistent information that identifies * username and password required to authorize to a specific realm. * * Note that since PJSIP 0.7.0.1, it is possible to make a credential that is * valid for any realms, by setting the realm to star/wildcard character, * i.e. realm = pj_str("*");. */ struct pjsip_cred_info { pj_str_t realm; /**< Realm. Use "*" to make a credential that can be used to authenticate against any challenges. */ pj_str_t scheme; /**< Scheme (e.g. "digest"). */ pj_str_t username; /**< User name. */ int data_type; /**< Type of data (0 for plaintext passwd). */ pj_str_t data; /**< The data, which can be a plaintext password or a hashed digest. */ /** Extended data */ union { /** Digest AKA credential information. Note that when AKA credential * is being used, the \a data field of this #pjsip_cred_info is * not used, but it still must be initialized to an empty string. * Please see \ref PJSIP_AUTH_AKA_API for more information. */ struct { pj_str_t k; /**< Permanent subscriber key. */ pj_str_t op; /**< Operator variant key. */ pj_str_t amf; /**< Authentication Management Field */ pjsip_cred_cb cb; /**< Callback to create AKA digest. */ } aka; } ext; }; /** * This structure describes cached value of previously sent Authorization * or Proxy-Authorization header. The authentication framework keeps a list * of this structure and will resend the same header to the same server * as long as the method, uri, and nonce stays the same. */ typedef struct pjsip_cached_auth_hdr { /** Standard list member */ PJ_DECL_LIST_MEMBER(struct pjsip_cached_auth_hdr); pjsip_method method; /**< To quickly see the method. */ pjsip_authorization_hdr *hdr; /**< The cached header. */ } pjsip_cached_auth_hdr; /** * This structure describes authentication information for the specified * realm. Each instance of this structure describes authentication "session" * between this endpoint and remote server. This "session" information is * usefull to keep information that persists for more than one challenge, * such as nonce-count and cnonce value. * * Other than that, this structure also keeps the last authorization headers * that have been sent in the cache list. */ typedef struct pjsip_cached_auth { /** Standard list member */ PJ_DECL_LIST_MEMBER(struct pjsip_cached_auth); pj_str_t realm; /**< Realm. */ pj_bool_t is_proxy; /**< Server type (401/407) */ pjsip_auth_qop_type qop_value; /**< qop required by server. */ unsigned stale_cnt; /**< Number of stale retry. */ #if PJSIP_AUTH_QOP_SUPPORT pj_uint32_t nc; /**< Nonce count. */ pj_str_t cnonce; /**< Cnonce value. */ #endif pjsip_www_authenticate_hdr *last_chal; /**< Last challenge seen. */ #if PJSIP_AUTH_HEADER_CACHING pjsip_cached_auth_hdr cached_hdr;/**< List of cached header for each method. */ #endif } pjsip_cached_auth; /** * This structure describes client authentication session preference. * The preference can be set by calling #pjsip_auth_clt_set_prefs(). */ typedef struct pjsip_auth_clt_pref { /** * If this flag is set, the authentication client framework will * send an empty Authorization header in each initial request. * Default is no. */ pj_bool_t initial_auth; /** * Specify the algorithm to use when empty Authorization header * is to be sent for each initial request (see above) */ pj_str_t algorithm; } pjsip_auth_clt_pref; /** * Duplicate a client authentication preference setting. * * @param pool The memory pool. * @param dst Destination client authentication preference. * @param src Source client authentication preference. */ PJ_DECL(void) pjsip_auth_clt_pref_dup(pj_pool_t *pool, pjsip_auth_clt_pref *dst, const pjsip_auth_clt_pref *src); /** * This structure describes client authentication sessions. It keeps * all the information needed to authorize the client against all downstream * servers. */ typedef struct pjsip_auth_clt_sess { pj_pool_t *pool; /**< Pool to use. */ pjsip_endpoint *endpt; /**< Endpoint where this belongs. */ pjsip_auth_clt_pref pref; /**< Preference/options. */ unsigned cred_cnt; /**< Number of credentials. */ pjsip_cred_info *cred_info; /**< Array of credential information*/ pjsip_cached_auth cached_auth; /**< Cached authorization info. */ } pjsip_auth_clt_sess; /** * Duplicate a credential info. * * @param pool The memory pool. * @param dst Destination credential. * @param src Source credential. */ PJ_DECL(void) pjsip_cred_info_dup(pj_pool_t *pool, pjsip_cred_info *dst, const pjsip_cred_info *src); /** * Compare two credential infos. * * @param cred1 The credential info to compare. * @param cred2 The credential info to compare. * * @return 0 if both credentials are equal. */ PJ_DECL(int) pjsip_cred_info_cmp(const pjsip_cred_info *cred1, const pjsip_cred_info *cred2); /** * Type of function to lookup credential for the specified name. * * @param pool Pool to initialize the credential info. * @param realm Realm to find the account. * @param acc_name Account name to look for. * @param cred_info The structure to put the credential when it's found. * * @return The function MUST return PJ_SUCCESS when it found * a correct credential for the specified account and * realm. Otherwise it may return PJSIP_EAUTHACCNOTFOUND * or PJSIP_EAUTHACCDISABLED. */ typedef pj_status_t pjsip_auth_lookup_cred( pj_pool_t *pool, const pj_str_t *realm, const pj_str_t *acc_name, pjsip_cred_info *cred_info ); /** * This structure describes input param for credential lookup. */ typedef struct pjsip_auth_lookup_cred_param { pj_str_t realm; /**< Realm to find the account. */ pj_str_t acc_name; /**< Account name to look for. */ pjsip_rx_data *rdata; /**< Incoming request to be authenticated. */ } pjsip_auth_lookup_cred_param; /** * Type of function to lookup credential for the specified name. * * @param pool Pool to initialize the credential info. * @param param The input param for credential lookup. * @param cred_info The structure to put the credential when it's found. * * @return The function MUST return PJ_SUCCESS when it found * a correct credential for the specified account and * realm. Otherwise it may return PJSIP_EAUTHACCNOTFOUND * or PJSIP_EAUTHACCDISABLED. */ typedef pj_status_t pjsip_auth_lookup_cred2( pj_pool_t *pool, const pjsip_auth_lookup_cred_param *param, pjsip_cred_info *cred_info ); /** Flag to specify that server is a proxy. */ #define PJSIP_AUTH_SRV_IS_PROXY 1 /** * This structure describes server authentication information. */ typedef struct pjsip_auth_srv { pj_str_t realm; /**< Realm to serve. */ pj_bool_t is_proxy; /**< Will issue 407 instead of 401 */ pjsip_auth_lookup_cred *lookup; /**< Lookup function. */ pjsip_auth_lookup_cred2 *lookup2; /**< Lookup function with additional info in its input param. */ } pjsip_auth_srv; /** * Initialize client authentication session data structure, and set the * session to use pool for its subsequent memory allocation. The argument * options should be set to zero for this PJSIP version. * * @param sess The client authentication session. * @param endpt Endpoint where this session belongs. * @param pool Pool to use. * @param options Must be zero. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_auth_clt_init( pjsip_auth_clt_sess *sess, pjsip_endpoint *endpt, pj_pool_t *pool, unsigned options); /** * Clone client initialization session. * * @param pool Pool to use. * @param sess Structure to put the duplicated session. * @param rhs The client session to be cloned. * * @return PJ_SUCCESS on success; */ PJ_DECL(pj_status_t) pjsip_auth_clt_clone( pj_pool_t *pool, pjsip_auth_clt_sess *sess, const pjsip_auth_clt_sess *rhs); /** * Set the credentials to be used during the session. This will duplicate * the specified credentials using client authentication's pool. * * @param sess The client authentication session. * @param cred_cnt Number of credentials. * @param c Array of credentials. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess, int cred_cnt, const pjsip_cred_info *c); /** * Set the preference for the client authentication session. * * @param sess The client authentication session. * @param p Preference. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_auth_clt_set_prefs(pjsip_auth_clt_sess *sess, const pjsip_auth_clt_pref *p); /** * Get the preference for the client authentication session. * * @param sess The client authentication session. * @param p Pointer to receive the preference. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_auth_clt_get_prefs(pjsip_auth_clt_sess *sess, pjsip_auth_clt_pref *p); /** * Initialize new request message with authorization headers. * This function will put Authorization/Proxy-Authorization headers to the * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING) * and the session has previously sent Authorization/Proxy-Authorization header * with the same method, then the same Authorization/Proxy-Authorization header * will be resent from the cache only if qop is not present. If the stack is * configured to automatically generate next Authorization/Proxy-Authorization * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy- * Authorization headers are calculated and generated when they are not present * in the case or if authorization session has qop. * * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag * are not set, this function will do nothing. The stack then will only send * Authorization/Proxy-Authorization to respond 401/407 response. * * @param sess The client authentication session. * @param tdata The request message to be initialized. * * @return PJ_SUCCESS if successfull. */ PJ_DECL(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess, pjsip_tx_data *tdata ); /** * Call this function when a transaction failed with 401 or 407 response. * This function will reinitialize the original request message with the * authentication challenge found in the response message, and add the * new authorization header in the authorization cache. * * Note that upon return the reference counter of the new transmit data * will be set to 1. * * @param sess The client authentication session. * @param rdata The response message containing 401/407 status. * @param old_request The original request message, which will be re- * created with authorization info. * @param new_request Pointer to receive new request message which * will contain all required authorization headers. * * @return PJ_SUCCESS if new request can be successfully * created to respond all the authentication * challenges. */ PJ_DECL(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess, const pjsip_rx_data *rdata, pjsip_tx_data *old_request, pjsip_tx_data **new_request ); /** * Initialize server authorization session data structure to serve the * specified realm and to use lookup_func function to look for the credential * info. * * @param pool Pool used to initialize the authentication server. * @param auth_srv The authentication server structure. * @param realm Realm to be served by the server. * @param lookup Account lookup function. * @param options Options, bitmask of: * - PJSIP_AUTH_SRV_IS_PROXY: to specify that the server * will authorize clients as a proxy server (instead of * as UAS), which means that Proxy-Authenticate will * be used instead of WWW-Authenticate. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_auth_srv_init( pj_pool_t *pool, pjsip_auth_srv *auth_srv, const pj_str_t *realm, pjsip_auth_lookup_cred *lookup, unsigned options ); /** * This structure describes initialization settings of server authorization * session. */ typedef struct pjsip_auth_srv_init_param { /** * Realm to be served by the server. */ const pj_str_t *realm; /** * Account lookup function. */ pjsip_auth_lookup_cred2 *lookup2; /** * Options, bitmask of: * - PJSIP_AUTH_SRV_IS_PROXY: to specify that the server will authorize * clients as a proxy server (instead of as UAS), which means that * Proxy-Authenticate will be used instead of WWW-Authenticate. */ unsigned options; } pjsip_auth_srv_init_param; /** * Initialize server authorization session data structure to serve the * specified realm and to use lookup_func function to look for the credential * info. * * @param pool Pool used to initialize the authentication server. * @param auth_srv The authentication server structure. * @param param The initialization param. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_auth_srv_init2( pj_pool_t *pool, pjsip_auth_srv *auth_srv, const pjsip_auth_srv_init_param *param); /** * Request the authorization server framework to verify the authorization * information in the specified request in rdata. * * @param auth_srv The server authentication structure. * @param rdata Incoming request to be authenticated. * @param status_code When not null, it will be filled with suitable * status code to be sent to the client. * * @return PJ_SUCCESS if request is successfully authenticated. * Otherwise the function may return one of the * following error codes: * - PJSIP_EAUTHNOAUTH * - PJSIP_EINVALIDAUTHSCHEME * - PJSIP_EAUTHACCNOTFOUND * - PJSIP_EAUTHACCDISABLED * - PJSIP_EAUTHINVALIDREALM * - PJSIP_EAUTHINVALIDDIGEST */ PJ_DECL(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv, pjsip_rx_data *rdata, int *status_code ); /** * Add authentication challenge headers to the outgoing response in tdata. * Application may specify its customized nonce and opaque for the challenge, * or can leave the value to NULL to make the function fills them in with * random characters. * * @param auth_srv The server authentication structure. * @param qop Optional qop value. * @param nonce Optional nonce value. * @param opaque Optional opaque value. * @param stale Stale indication. * @param tdata The outgoing response message. The response must have * 401 or 407 response code. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_auth_srv_challenge( pjsip_auth_srv *auth_srv, const pj_str_t *qop, const pj_str_t *nonce, const pj_str_t *opaque, pj_bool_t stale, pjsip_tx_data *tdata); /** * Helper function to create MD5 digest out of the specified * parameters. * * @param result String to store the response digest. This string * must have been preallocated by caller with the * buffer at least PJSIP_MD5STRLEN (32 bytes) in size. * @param nonce Optional nonce. * @param nc Nonce count. * @param cnonce Optional cnonce. * @param qop Optional qop. * @param uri URI. * @param realm Realm. * @param cred_info Credential info. * @param method SIP method. */ PJ_DECL(void) pjsip_auth_create_digest(pj_str_t *result, const pj_str_t *nonce, const pj_str_t *nc, const pj_str_t *cnonce, const pj_str_t *qop, const pj_str_t *uri, const pj_str_t *realm, const pjsip_cred_info *cred_info, const pj_str_t *method); /** * @} */ PJ_END_DECL #endif /* __PJSIP_AUTH_SIP_AUTH_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_auth_aka.h ================================================ /* $Id: sip_auth_aka.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_AUTH_SIP_AUTH_AKA_H__ #define __PJSIP_AUTH_SIP_AUTH_AKA_H__ /** * @file sip_auth_aka.h * @brief SIP Digest AKA Authorization Module. */ #include PJ_BEGIN_DECL /** * @defgroup PJSIP_AUTH_AKA_API Digest AKAv1 and AKAv2 Authentication API * @ingroup PJSIP_AUTH_API * @brief Digest AKAv1 and AKAv2 Authentication API * @{ * * This module implements HTTP digest authentication using Authentication * and Key Agreement (AKA) version 1 and version 2 (AKAv1-MD5 and AKAv2-MD5), * as specified in RFC 3310 and RFC 4169. SIP AKA authentication is used * by 3GPP and IMS systems. * * @section pjsip_aka_using Using Digest AKA Authentication * * Support for digest AKA authentication is currently made optional, so * application needs to declare \a PJSIP_HAS_DIGEST_AKA_AUTH to non-zero * in config_site.h to enable AKA support: * @code #define PJSIP_HAS_DIGEST_AKA_AUTH 1 @endcode * * In addition, application would need to link with libmilenage * library from \a third_party directory. * * Application then specifies digest AKA credential by initializing the * authentication credential as follows: * @code pjsip_cred_info cred; pj_bzero(&cred, sizeof(cred)); cred.scheme = pj_str("Digest"); cred.realm = pj_str("ims-domain.test"); cred.username = pj_str("user@ims-domain.test"); cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD | PJSIP_CRED_DATA_EXT_AKA; cred.data = pj_str("password"); // AKA extended info cred.ext.aka.k = pj_str("password"); cred.ext.aka.cb = &pjsip_auth_create_aka_response @endcode * * Description: * - To support AKA, application adds \a PJSIP_CRED_DATA_EXT_AKA flag in the * \a data_type field. This indicates that extended information specific to * AKA authentication is available in the credential, and that response * digest computation will use the callback function instead of the usual MD5 * digest computation. * * - The \a scheme for the credential is "Digest". * * - The \a realm is the expected realm in the challenge. Application may * also specify wildcard realm ("*") if it wishes to respond to any realms * in the challenge. * * - The \a data field is optional. Application may fill this with the password * if it wants to support both MD5 and AKA MD5 in a single credential. The * pjsip_auth_create_aka_response() function will use this field if the * challenge indicates "MD5" as the algorithm instead of "AKAv1-MD5" or * "AKAv2-MD5". * * - The \a ext.aka.k field specifies the permanent subscriber key to be used * for AKA authentication. Application may specify binary password containing * NULL character in this key, since the length of the key is indicated in * the \a slen field of the string. * * - The \a ext.aka.cb field specifies the callback function to calculate the * response digest. Application can specify pjsip_auth_create_aka_response() * in this field to use PJSIP's implementation, but it's free to provide * it's own function. * * - Optionally application may set \a ext.aka.op and \a ext.aka.amf in the * credential to specify AKA Operator variant key and AKA Authentication * Management Field information. */ /** * Length of Authentication Key (AK) in bytes. */ #define PJSIP_AKA_AKLEN 6 /** * Length of Authentication Management Field (AMF) in bytes. */ #define PJSIP_AKA_AMFLEN 2 /** * Length of AUTN in bytes. */ #define PJSIP_AKA_AUTNLEN 16 /** * Length of Confidentiality Key (CK) in bytes. */ #define PJSIP_AKA_CKLEN 16 /** * Length of Integrity Key (AK) in bytes. */ #define PJSIP_AKA_IKLEN 16 /** * Length of permanent/subscriber Key (K) in bytes. */ #define PJSIP_AKA_KLEN 16 /** * Length of AKA authentication code in bytes. */ #define PJSIP_AKA_MACLEN 8 /** * Length of operator key in bytes. */ #define PJSIP_AKA_OPLEN 16 /** * Length of random challenge (RAND) in bytes. */ #define PJSIP_AKA_RANDLEN 16 /** * Length of response digest in bytes. */ #define PJSIP_AKA_RESLEN 8 /** * Length of sequence number (SQN) in bytes. */ #define PJSIP_AKA_SQNLEN 6 /** * This function creates MD5, AKAv1-MD5, or AKAv2-MD5 response for * the specified challenge in \a chal, according to the algorithm * specified in the challenge, and based on the information in the * credential \a cred. * * Application may register this function as \a ext.aka.cb field of * #pjsip_cred_info structure to make PJSIP automatically call this * function to calculate the response digest. To do so, it needs to * add \a PJSIP_CRED_DATA_EXT_AKA flag in the \a data_type field of * the credential, and fills up other AKA specific information in * the credential. * * @param pool Pool to allocate memory. * @param chal The authentication challenge sent by server in 401 * or 401 response, as either Proxy-Authenticate or * WWW-Authenticate header. * @param cred The credential to be used. * @param method The request method. * @param auth The digest credential where the digest response * will be placed to. Upon calling this function, the * nonce, nc, cnonce, qop, uri, and realm fields of * this structure must have been set by caller. Upon * return, the \a response field will be initialized * by this function. * * @return PJ_SUCCESS if response has been created successfully. */ PJ_DECL(pj_status_t) pjsip_auth_create_aka_response( pj_pool_t *pool, const pjsip_digest_challenge*chal, const pjsip_cred_info *cred, const pj_str_t *method, pjsip_digest_credential *auth); /** * @} */ PJ_END_DECL #endif /* __PJSIP_AUTH_SIP_AUTH_AKA_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_auth_msg.h ================================================ /* $Id: sip_auth_msg.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_AUTH_SIP_AUTH_MSG_H__ #define __PJSIP_AUTH_SIP_AUTH_MSG_H__ #include PJ_BEGIN_DECL /** * @addtogroup PJSIP_MSG_HDR * @{ */ /** * Common credential structure represents common credential fields * present in Authorization/Proxy-Authorization header. */ struct pjsip_common_credential { pj_str_t realm; /**< Credential's realm. */ pjsip_param other_param; /**< Other parameters. */ }; /** * @see pjsip_common_credential */ typedef struct pjsip_common_credential pjsip_common_credential; /** * This structure describe credential used in Authorization and * Proxy-Authorization header for digest authentication scheme. */ struct pjsip_digest_credential { pj_str_t realm; /**< Realm of the credential */ pjsip_param other_param; /**< Other parameters. */ pj_str_t username; /**< Username parameter. */ pj_str_t nonce; /**< Nonce parameter. */ pj_str_t uri; /**< URI parameter. */ pj_str_t response; /**< Response digest. */ pj_str_t algorithm; /**< Algorithm. */ pj_str_t cnonce; /**< Cnonce. */ pj_str_t opaque; /**< Opaque value. */ pj_str_t qop; /**< Quality of protection. */ pj_str_t nc; /**< Nonce count. */ }; /** * @see pjsip_digest_credential */ typedef struct pjsip_digest_credential pjsip_digest_credential; /** * This structure describe credential used in Authorization and * Proxy-Authorization header for PGP authentication scheme. */ struct pjsip_pgp_credential { pj_str_t realm; /**< Realm. */ pjsip_param other_param; /**< Other parameters. */ pj_str_t version; /**< Version parameter. */ pj_str_t signature; /**< Signature parameter. */ pj_str_t signed_by; /**< Signed by parameter. */ pj_str_t nonce; /**< Nonce parameter. */ }; /** * @see pjsip_pgp_credential */ typedef struct pjsip_pgp_credential pjsip_pgp_credential; /** * This structure describes SIP Authorization header (and also SIP * Proxy-Authorization header). */ struct pjsip_authorization_hdr { /** Standard header fiends. */ PJSIP_DECL_HDR_MEMBER(struct pjsip_authorization_hdr); /** Authorization scheme. */ pj_str_t scheme; /** Type of credentials, depending on the scheme. */ union { pjsip_common_credential common; /**< Common fields. */ pjsip_digest_credential digest; /**< Digest credentials. */ pjsip_pgp_credential pgp; /**< PGP credentials. */ } credential; }; /** * @see pjsip_authorization_hdr. */ typedef struct pjsip_authorization_hdr pjsip_authorization_hdr; /** SIP Proxy-Authorization header shares the same structure as SIP Authorization header. */ typedef struct pjsip_authorization_hdr pjsip_proxy_authorization_hdr; /** * Create SIP Authorization header. * @param pool Pool where memory will be allocated from. * @return SIP Authorization header. */ PJ_DECL(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool); /** * Create SIP Proxy-Authorization header. * @param pool Pool where memory will be allocated from. * @return SIP Proxy-Authorization header. */ PJ_DECL(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool); /** * This structure describes common fields in authentication challenge * headers (WWW-Authenticate and Proxy-Authenticate). */ struct pjsip_common_challenge { pj_str_t realm; /**< Realm for the challenge. */ pjsip_param other_param; /**< Other parameters. */ }; /** * @see pjsip_common_challenge */ typedef struct pjsip_common_challenge pjsip_common_challenge; /** * This structure describes authentication challenge used in Proxy-Authenticate * or WWW-Authenticate for digest authentication scheme. */ struct pjsip_digest_challenge { pj_str_t realm; /**< Realm for the challenge. */ pjsip_param other_param; /**< Other parameters. */ pj_str_t domain; /**< Domain. */ pj_str_t nonce; /**< Nonce challenge. */ pj_str_t opaque; /**< Opaque value. */ int stale; /**< Stale parameter. */ pj_str_t algorithm; /**< Algorithm parameter. */ pj_str_t qop; /**< Quality of protection. */ }; /** * @see pjsip_digest_challenge */ typedef struct pjsip_digest_challenge pjsip_digest_challenge; /** * This structure describes authentication challenge used in Proxy-Authenticate * or WWW-Authenticate for PGP authentication scheme. */ struct pjsip_pgp_challenge { pj_str_t realm; /**< Realm for the challenge. */ pjsip_param other_param; /**< Other parameters. */ pj_str_t version; /**< PGP version. */ pj_str_t micalgorithm; /**< micalgorithm parameter. */ pj_str_t pubalgorithm; /**< pubalgorithm parameter. */ pj_str_t nonce; /**< Nonce challenge. */ }; /** * @see pjsip_pgp_challenge */ typedef struct pjsip_pgp_challenge pjsip_pgp_challenge; /** * This structure describe SIP WWW-Authenticate header (Proxy-Authenticate * header also uses the same structure). */ struct pjsip_www_authenticate_hdr { /** Standard header fields. */ PJSIP_DECL_HDR_MEMBER(struct pjsip_www_authenticate_hdr); /** Authentication scheme */ pj_str_t scheme; /** This union contains structures that are only relevant depending on the value of the scheme being used. */ union { pjsip_common_challenge common; /**< Common fields. */ pjsip_digest_challenge digest; /**< Digest challenge. */ pjsip_pgp_challenge pgp; /**< PGP challenge. */ } challenge; }; /** * WWW-Authenticate header. */ typedef struct pjsip_www_authenticate_hdr pjsip_www_authenticate_hdr; /** * Proxy-Authenticate header. */ typedef struct pjsip_www_authenticate_hdr pjsip_proxy_authenticate_hdr; /** * Create SIP WWW-Authenticate header. * * @param pool Pool where memory will be allocated from. * @return SIP WWW-Authenticate header. */ PJ_DECL(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool); /** * Create SIP Proxy-Authenticate header. * * @param pool Pool where memory will be allocated from. * @return SIP Proxy-Authenticate header. */ PJ_DECL(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool); /** * @} */ PJ_END_DECL #endif /* __PJSIP_AUTH_SIP_AUTH_MSG_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_auth_parser.h ================================================ /* $Id: sip_auth_parser.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_AUTH_SIP_AUTH_PARSER_H__ #define __PJSIP_AUTH_SIP_AUTH_PARSER_H__ /** * @file sip_auth_parser.h * @brief SIP Authorization Parser Module. */ #include PJ_BEGIN_DECL /** * Initialize and register authorization parser module. * This will register parser handler for various Authorization related headers * such as Authorization, WWW-Authenticate, Proxy-Authorizization, and * Proxy-Authenticate headers. * * This function is called automatically by the main SIP parser. * * @return PJ_SUCCESS or the appropriate status code. */ PJ_DECL(pj_status_t) pjsip_auth_init_parser(void); /** * DeInitialize authorization parser module. */ PJ_DECL(void) pjsip_auth_deinit_parser(); extern const pj_str_t pjsip_USERNAME_STR, /**< "username" string const. */ pjsip_REALM_STR, /**< "realm" string const. */ pjsip_NONCE_STR, /**< "nonce" string const. */ pjsip_URI_STR, /**< "uri" string const. */ pjsip_RESPONSE_STR, /**< "response" string const. */ pjsip_ALGORITHM_STR,/**< "algorithm" string const. */ pjsip_DOMAIN_STR, /**< "domain" string const. */ pjsip_STALE_STR, /**< "stale" string const. */ pjsip_QOP_STR, /**< "qop" string const. */ pjsip_CNONCE_STR, /**< "cnonce" string const. */ pjsip_OPAQUE_STR, /**< "opaque" string const. */ pjsip_NC_STR, /**< "nc" string const. */ pjsip_TRUE_STR, /**< "true" string const. */ pjsip_FALSE_STR, /**< "false" string const. */ pjsip_DIGEST_STR, /**< "digest" string const. */ pjsip_PGP_STR, /**< "pgp" string const. */ pjsip_MD5_STR, /**< "md5" string const. */ pjsip_AUTH_STR; /**< "auth" string const. */ PJ_END_DECL #endif /* __PJSIP_AUTH_SIP_AUTH_PARSER_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_autoconf.h.in ================================================ /* $Id: sip_autoconf.h.in 3295 2010-08-25 12:51:29Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_AUTOCONF_H__ #define __PJSIP_SIP_AUTOCONF_H__ /** * @file sip_autoconf.h * @brief Describes operating system specifics (automatically detected by * autoconf) */ /* * Enable/disable TLS transport, as configured by autoconf. * But only do this if user doesn't explicitly configure in pj/config_site.h. */ /* Since 1.5, the default setting will follow PJ_HAS_SSL_SOCK setting. */ //#ifndef PJSIP_HAS_TLS_TRANSPORT //#undef PJSIP_HAS_TLS_TRANSPORT //#endif #endif /* __PJSIP_SIP_AUTOCONF_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_config.h ================================================ /* $Id: sip_config.h 4530 2013-05-30 09:27:49Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_CONFIG_H__ #define __PJSIP_SIP_CONFIG_H__ /** * @file sip_config.h * @brief Compile time configuration. */ #include /** * @defgroup PJSIP_CORE Core SIP Library * @brief The core framework from which all other SIP components depends on. * * The PJSIP Core library only provides transport framework, event * dispatching/module framework, and SIP message representation and * parsing. It doesn't do anything usefull in itself! * * If application wants the stack to do anything usefull at all, * it must registers @ref PJSIP_MOD to the core library. Examples * of modules are @ref PJSIP_TRANSACT and @ref PJSUA_UA. */ /** * @defgroup PJSIP_BASE Base Types * @ingroup PJSIP_CORE * @brief Basic PJSIP types and configurations. */ /** * @defgroup PJSIP_CONFIG PJSIP Configurations/Settings * @ingroup PJSIP_BASE * @brief PJSIP compile time configurations. * @{ */ /* * Include sip_autoconf.h if autoconf is used (PJ_AUTOCONF is set) */ #if defined(PJ_AUTOCONF) # include #endif PJ_BEGIN_DECL /** * This structure describes PJSIP run-time configurations/settings. * Application may use #pjsip_cfg() function to modify the settings * before creating the stack. */ typedef struct pjsip_cfg_t { /** Global settings. */ struct { /** * Specify port number should be allowed to appear in To and From * header. Note that RFC 3261 disallow this, see Table 1 in section * 19.1.1 of the RFC. * * Default is PJSIP_ALLOW_PORT_IN_FROMTO_HDR. */ pj_bool_t allow_port_in_fromto_hdr; /** * Accept call replace in early state when invite is not initiated * by the user agent. RFC 3891 Section 3 disallows this, however, * for better interoperability reason, this might be ignored. * * Default is PJSIP_ACCEPT_REPLACE_IN_EARLY_STATE. */ pj_bool_t accept_replace_in_early_state; /** * Allow hash character ('#') to appear in outgoing URIs. See * https://trac.pjsip.org/repos/ticket/1569. * * Default is PJ_FALSE. */ pj_bool_t allow_tx_hash_in_uri; /** * Disable rport in request. * * Default is PJ_FALSE. */ pj_bool_t disable_rport; /** * Disable automatic switching from UDP to TCP if outgoing request * is greater than 1300 bytes. * * Default is PJSIP_DONT_SWITCH_TO_TCP. */ pj_bool_t disable_tcp_switch; /** * Disable automatic switching to TLS if target-URI does not use * "sips" scheme nor TLS transport, even when request-URI uses * "sips" scheme. * * Default is PJSIP_DONT_SWITCH_TO_TLS. */ pj_bool_t disable_tls_switch; /** * Enable call media session to always be updated to the latest * received early media SDP when receiving forked early media * (multiple 183 responses with different To tag). * * Default is PJSIP_FOLLOW_EARLY_MEDIA_FORK. */ pj_bool_t follow_early_media_fork; /** * Specify whether "alias" param should be added to the Via header * in any outgoing request with connection oriented transport. * * Default is PJSIP_REQ_HAS_VIA_ALIAS. */ pj_bool_t req_has_via_alias; /** * Resolve hostname when trying to get the network interface to be put * in Via or Contact header. * * Default is PJSIP_RESOLVE_HOSTNAME_TO_GET_INTERFACE. */ pj_bool_t resolve_hostname_to_get_interface; /** * Disable security check on incoming messages in a secure dialog. * A secure dialog is created when the request that creates the dialog * uses "sips" scheme in its request URI. Contact URI should use "sips" * scheme and the top-most Record-Route URI, if any, should use either * "sips" scheme or "transport=tls" param. See also * https://trac.pjsip.org/repos/ticket/1735. * * Default is PJ_FALSE. */ pj_bool_t disable_secure_dlg_check; } endpt; /** Transaction layer settings. */ struct { /** Maximum number of transactions. The value is initialized with * PJSIP_MAX_TSX_COUNT */ unsigned max_count; /* Timeout values: */ /** Transaction T1 timeout, in msec. Default value is PJSIP_T1_TIMEOUT */ unsigned t1; /** Transaction T2 timeout, in msec. Default value is PJSIP_T2_TIMEOUT */ unsigned t2; /** Transaction completed timer for non-INVITE, in msec. Default value * is PJSIP_T4_TIMEOUT */ unsigned t4; /** Transaction completed timer for INVITE, in msec. Default value is * PJSIP_TD_TIMEOUT. */ unsigned td; } tsx; /* Dialog layer settings .. TODO */ /** Client registration settings. */ struct { /** * Specify whether client registration should check for its * registered contact in Contact header of successful REGISTER * response to determine whether registration has been successful. * This setting may be disabled if non-compliant registrar is unable * to return correct Contact header. * * Default is PJSIP_REGISTER_CLIENT_CHECK_CONTACT */ pj_bool_t check_contact; /** * Specify whether client registration should add "x-uid" extension * parameter in all Contact URIs that it registers to assist the * matching of Contact URIs in the 200/OK REGISTER response, in * case the registrar is unable to return exact Contact URI in the * 200/OK response. * * Default is PJSIP_REGISTER_CLIENT_ADD_XUID_PARAM. */ pj_bool_t add_xuid_param; } regc; /** TCP transport settings */ struct { /** * Set the interval to send keep-alive packet for TCP transports. * If the value is zero, keep-alive will be disabled for TCP. * * Default is PJSIP_TCP_KEEP_ALIVE_INTERVAL. */ long keep_alive_interval; } tcp; /** TLS transport settings */ struct { /** * Set the interval to send keep-alive packet for TLS transports. * If the value is zero, keep-alive will be disabled for TLS. * * Default is PJSIP_TLS_KEEP_ALIVE_INTERVAL. */ long keep_alive_interval; } tls; } pjsip_cfg_t; #ifdef PJ_DLL /** * Get pjsip configuration instance. Application may modify the * settings before creating the SIP endpoint and modules. * * @return Configuration instance. */ PJ_DECL(pjsip_cfg_t*) pjsip_cfg(void); #else /* PJ_DLL */ extern pjsip_cfg_t pjsip_sip_cfg_var; /** * Get pjsip configuration instance. Application may modify the * settings before creating the SIP endpoint and modules. * * @return Configuration instance. */ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void) { return &pjsip_sip_cfg_var; } #endif /* PJ_DLL */ /** * Specify maximum transaction count in transaction hash table. * For efficiency, the value should be 2^n-1 since it will be * rounded up to 2^n. * * Default value is 1023 */ #ifndef PJSIP_MAX_TSX_COUNT # define PJSIP_MAX_TSX_COUNT (1024-1) #endif /** * Specify maximum number of dialogs in the dialog hash table. * For efficiency, the value should be 2^n-1 since it will be * rounded up to 2^n. * * Default value is 511. */ #ifndef PJSIP_MAX_DIALOG_COUNT # define PJSIP_MAX_DIALOG_COUNT (512-1) #endif /** * Specify maximum number of transports. * Default value is equal to maximum number of handles in ioqueue. * See also PJSIP_TPMGR_HTABLE_SIZE. */ #ifndef PJSIP_MAX_TRANSPORTS # define PJSIP_MAX_TRANSPORTS (PJ_IOQUEUE_MAX_HANDLES) #endif /** * Transport manager hash table size (must be 2^n-1). * See also PJSIP_MAX_TRANSPORTS */ #ifndef PJSIP_TPMGR_HTABLE_SIZE # define PJSIP_TPMGR_HTABLE_SIZE 31 #endif /** * Specify maximum URL size. */ #ifndef PJSIP_MAX_URL_SIZE # define PJSIP_MAX_URL_SIZE 256 #endif /** * Specify maximum number of modules. * This mainly affects the size of mod_data array in various components. */ #ifndef PJSIP_MAX_MODULE # define PJSIP_MAX_MODULE 32 #endif /** * Maximum packet length. We set it more than MTU since a SIP PDU * containing presence information can be quite large (>1500). */ #ifndef PJSIP_MAX_PKT_LEN # define PJSIP_MAX_PKT_LEN 4000 #endif /** * RFC 3261 section 18.1.1: * If a request is within 200 bytes of the path MTU, or if it is larger * than 1300 bytes and the path MTU is unknown, the request MUST be sent * using an RFC 2914 [43] congestion controlled transport protocol, such * as TCP. * * Disable the behavior of automatic switching to TCP whenever UDP packet * size exceeds the threshold defined in PJSIP_UDP_SIZE_THRESHOLD. * * This option can also be controlled at run-time by the \a disable_tcp_switch * setting in pjsip_cfg_t. * * Default is 0 (no). */ #ifndef PJSIP_DONT_SWITCH_TO_TCP # define PJSIP_DONT_SWITCH_TO_TCP 0 #endif /** * As specified RFC 3261 section 8.1.2, when request-URI uses "sips" scheme, * TLS must always be used regardless of the target-URI scheme or transport * type. * * This option will specify whether the behavior of automatic switching to TLS * should be disabled, i.e: regard the target-URI scheme or transport type. * * This option can also be controlled at run-time by the \a disable_tls_switch * setting in pjsip_cfg_t. * * Default is 0 (no). */ #ifndef PJSIP_DONT_SWITCH_TO_TLS # define PJSIP_DONT_SWITCH_TO_TLS 0 #endif /** * Specify whether the call media session should be updated to the latest * received early media SDP when receiving forked early media (multiple 183 * responses with different To tag). * * This option can also be controlled at run-time by the * \a follow_early_media_fork setting in pjsip_cfg_t. * * Default is PJ_TRUE. */ #ifndef PJSIP_FOLLOW_EARLY_MEDIA_FORK # define PJSIP_FOLLOW_EARLY_MEDIA_FORK PJ_TRUE #endif /** * Specify whether "alias" param should be added to the Via header * in any outgoing request with connection oriented transport. * * This option can also be controlled at run-time by the * \a req_has_via_alias setting in pjsip_cfg_t. * * Default is PJ_TRUE. */ #ifndef PJSIP_REQ_HAS_VIA_ALIAS # define PJSIP_REQ_HAS_VIA_ALIAS PJ_TRUE #endif /** * Resolve hostname when trying to get the network interface to be put in Via * or Contact header. * * This option can also be controlled at run-time by the * \a resolve_hostname_to_get_interface setting in pjsip_cfg_t. * * Default is PJ_FALSE. */ #ifndef PJSIP_RESOLVE_HOSTNAME_TO_GET_INTERFACE # define PJSIP_RESOLVE_HOSTNAME_TO_GET_INTERFACE PJ_FALSE #endif /** * Accept call replace in early state when invite is not initiated * by the user agent. RFC 3891 Section 3 disallows this, however, * for better interoperability reason, this might be ignored. * * This option can also be controlled at run-time by the * \a accept_replace_in_early_state setting in pjsip_cfg_t. * * Default is 0 (no). */ #ifndef PJSIP_ACCEPT_REPLACE_IN_EARLY_STATE # define PJSIP_ACCEPT_REPLACE_IN_EARLY_STATE 0 #endif /** * This setting controls the threshold of the UDP packet, which if it's * larger than this value the request will be sent with TCP. This setting * is useful only when PJSIP_DONT_SWITCH_TO_TCP is set to 0. * * Default is 1300 bytes. */ #ifndef PJSIP_UDP_SIZE_THRESHOLD # define PJSIP_UDP_SIZE_THRESHOLD 1300 #endif /** * Encode SIP headers in their short forms to reduce size. By default, * SIP headers in outgoing messages will be encoded in their full names. * If this option is enabled, then SIP headers for outgoing messages * will be encoded in their short forms, to reduce message size. * Note that this does not affect the ability of PJSIP to parse incoming * SIP messages, as the parser always supports parsing both the long * and short version of the headers. * * Note that there is also an undocumented variable defined in sip_msg.c * to control whether compact form should be used for encoding SIP * headers. The default value of this variable is PJSIP_ENCODE_SHORT_HNAME. * To change PJSIP behavior during run-time, application can use the * following construct: * \verbatim extern pj_bool_t pjsip_use_compact_form; // enable compact form pjsip_use_compact_form = PJ_TRUE; \endverbatim * * Default is 0 (no) */ #ifndef PJSIP_ENCODE_SHORT_HNAME # define PJSIP_ENCODE_SHORT_HNAME 0 #endif /** * Send Allow header in dialog establishing requests? * RFC 3261 Allow header SHOULD be included in dialog establishing * requests to inform remote agent about which SIP requests are * allowed within dialog. * * Note that there is also an undocumented variable defined in sip_dialog.c * to control whether Allow header should be included. The default value * of this variable is PJSIP_INCLUDE_ALLOW_HDR_IN_DLG. * To change PJSIP behavior during run-time, application can use the * following construct: * \verbatim extern pj_bool_t pjsip_include_allow_hdr_in_dlg; // do not transmit Allow header pjsip_include_allow_hdr_in_dlg = PJ_FALSE; \endverbatim * * Default is 1 (Yes) */ #ifndef PJSIP_INCLUDE_ALLOW_HDR_IN_DLG # define PJSIP_INCLUDE_ALLOW_HDR_IN_DLG 1 #endif /** * Allow SIP modules removal or insertions during operation? * If yes, then locking will be employed when endpoint need to * access module. */ #ifndef PJSIP_SAFE_MODULE # define PJSIP_SAFE_MODULE 1 #endif /** * Perform Via sent-by checking as specified in RFC 3261 Section 18.1.2, * which says that UAC MUST silently discard responses with Via sent-by * containing values that the UAC doesn't recognize as its transport * address. * * In PJSIP, this will cause response to be discarded and a message is * written to the log, saying something like: * "Dropping response Response msg 200/INVITE/cseq=608594373 (rdata00A99EF4) * from 1.2.3.4:5060 because sent-by is mismatch" * * The default behavior is yes, but when the UA supports IP address change * for the SIP transport, it will need to turn this checking off since * when the transport address is changed between request is sent and * response is received, the response will be discarded since its Via * sent-by now contains address that is different than the transport * address. * * Update: * As of version 2.1, the default value is 0. This change was part of * https://trac.pjsip.org/repos/ticket/1412 */ #ifndef PJSIP_CHECK_VIA_SENT_BY # define PJSIP_CHECK_VIA_SENT_BY 0 #endif /** * If non-zero, SIP parser will unescape the escape characters ('%') * in the original message, which means that it will modify the * original message. Otherwise the parser will create a copy of * the string and store the unescaped string to the new location. * * Unescaping in-place is faster, but less elegant (and it may * break certain applications). So normally it's disabled, unless * when benchmarking (to show off big performance). * * Default: 0 */ #ifndef PJSIP_UNESCAPE_IN_PLACE # define PJSIP_UNESCAPE_IN_PLACE 0 #endif /** * Specify port number should be allowed to appear in To and From * header. Note that RFC 3261 disallow this, see Table 1 in section * 19.1.1 of the RFC. This setting can also be altered at run-time * via pjsip_cfg setting, see pjsip_cfg_t.allow_port_in_fromto_hdr * field. * * Default: 0 */ #ifndef PJSIP_ALLOW_PORT_IN_FROMTO_HDR # define PJSIP_ALLOW_PORT_IN_FROMTO_HDR 0 #endif /** * This macro controls maximum numbers of ioqueue events to be processed * in a single pjsip_endpt_handle_events() poll. When PJSIP detects that * there are probably more events available from the network and total * events so far is less than this value, PJSIP will call pj_ioqueue_poll() * again to get more events. * * Value 1 works best for ioqueue with select() back-end, while for IOCP it is * probably best to set this value equal to PJSIP_MAX_TIMED_OUT_ENTRIES * since IOCP only processes one event at a time. * * Default: 1 */ #ifndef PJSIP_MAX_NET_EVENTS # define PJSIP_MAX_NET_EVENTS 1 #endif /** * Max entries to process in timer heap per poll. * * Default: 10 */ #ifndef PJSIP_MAX_TIMED_OUT_ENTRIES # define PJSIP_MAX_TIMED_OUT_ENTRIES 10 #endif /** * Idle timeout interval to be applied to outgoing transports (i.e. client * side) with no usage before the transport is destroyed. Value is in * seconds. * * Note that if the value is put lower than 33 seconds, it may cause some * pjsip test units to fail. See the comment on the following link: * https://trac.pjsip.org/repos/ticket/1465#comment:4 * * Default: 33 */ #ifndef PJSIP_TRANSPORT_IDLE_TIME # define PJSIP_TRANSPORT_IDLE_TIME 33 #endif /** * Idle timeout interval to be applied to incoming transports (i.e. server * side) with no usage before the transport is destroyed. Server typically * should let client close the connection, hence set this interval to a large * value. Value is in seconds. * * Default: 600 */ #ifndef PJSIP_TRANSPORT_SERVER_IDLE_TIME # define PJSIP_TRANSPORT_SERVER_IDLE_TIME 600 #endif /** * Maximum number of usages for a transport before a new transport is * created. This only applies for ephemeral transports such as TCP. * * Currently this is not used. * * Default: -1 */ #ifndef PJSIP_MAX_TRANSPORT_USAGE # define PJSIP_MAX_TRANSPORT_USAGE ((unsigned)-1) #endif /** * The TCP incoming connection backlog number to be set in accept(). * * Default: 5 * * @see PJSIP_TLS_TRANSPORT_BACKLOG */ #ifndef PJSIP_TCP_TRANSPORT_BACKLOG # define PJSIP_TCP_TRANSPORT_BACKLOG 5 #endif /** * Specify whether TCP listener should use SO_REUSEADDR option. This constant * will be used as the default value for the "reuse_addr" field in the * pjsip_tcp_transport_cfg structure. * * Default is FALSE on Windows and TRUE on non-Windows. * * @see PJSIP_TLS_TRANSPORT_REUSEADDR */ #ifndef PJSIP_TCP_TRANSPORT_REUSEADDR # if (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64) # define PJSIP_TCP_TRANSPORT_REUSEADDR 0 # else # define PJSIP_TCP_TRANSPORT_REUSEADDR 1 # endif #endif /** * Specify whether TCP transport should skip creating the listener. * Not having a listener means that application will not be able to * function in server mode and accept incoming connections. * * When enabling this setting, if you use PJSUA, it is recommended to set * pjsua_acc_config.contact_use_src_port to PJ_TRUE. * Warning: If contact_use_src_port is disabled or failed (because it's * unsupported in some platforms or automatically turned off due to * DNS server resolution), Contact header will be generated from * pj_getipinterface()/pj_gethostip(), but the address will not be * able to accept connections. * * Default is FALSE (listener will be created). */ #ifndef PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER # define PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER 0 #endif /** * Specify whether TLS transport should skip creating the listener. * Not having a listener means that application will not be able to * function in server mode and accept incoming connections. * * When enabling this setting, if you use PJSUA, it is recommended to set * pjsua_acc_config.contact_use_src_port to PJ_TRUE. * Warning: If contact_use_src_port is disabled or failed (because it's * unsupported in some platforms or automatically turned off due to * DNS server resolution), Contact header will be generated from * pj_getipinterface()/pj_gethostip(), but the address will not be * able to accept connections. * * Default is FALSE (listener will be created). */ #ifndef PJSIP_TLS_TRANSPORT_DONT_CREATE_LISTENER # define PJSIP_TLS_TRANSPORT_DONT_CREATE_LISTENER 0 #endif /** * Set the interval to send keep-alive packet for TCP transports. * If the value is zero, keep-alive will be disabled for TCP. * * This option can be changed in run-time by settting * \a tcp.keep_alive_interval field of pjsip_cfg(). * * Default: 90 (seconds) * * @see PJSIP_TCP_KEEP_ALIVE_DATA */ #ifndef PJSIP_TCP_KEEP_ALIVE_INTERVAL # define PJSIP_TCP_KEEP_ALIVE_INTERVAL 90 #endif /** * Set the payload of the TCP keep-alive packet. * * Default: CRLF */ #ifndef PJSIP_TCP_KEEP_ALIVE_DATA # define PJSIP_TCP_KEEP_ALIVE_DATA { "\r\n\r\n", 4 } #endif /** * Set the interval to send keep-alive packet for TLS transports. * If the value is zero, keep-alive will be disabled for TLS. * * This option can be changed in run-time by settting * \a tls.keep_alive_interval field of pjsip_cfg(). * * Default: 90 (seconds) * * @see PJSIP_TLS_KEEP_ALIVE_DATA */ #ifndef PJSIP_TLS_KEEP_ALIVE_INTERVAL # define PJSIP_TLS_KEEP_ALIVE_INTERVAL 90 #endif /** * Set the payload of the TLS keep-alive packet. * * Default: CRLF */ #ifndef PJSIP_TLS_KEEP_ALIVE_DATA # define PJSIP_TLS_KEEP_ALIVE_DATA { "\r\n\r\n", 4 } #endif /** * This macro specifies whether full DNS resolution should be used. * When enabled, #pjsip_resolve() will perform asynchronous DNS SRV and * A (or AAAA, when IPv6 is supported) resolution to resolve the SIP * domain. * * Note that even when this setting is enabled, asynchronous DNS resolution * will only be done when application calls #pjsip_endpt_create_resolver(), * configure the nameservers with pj_dns_resolver_set_ns(), and configure * the SIP endpoint's DNS resolver with #pjsip_endpt_set_resolver(). If * these steps are not followed, the domain will be resolved with normal * pj_gethostbyname() function. * * Turning off this setting will save the footprint by about 16KB, since * it should also exclude dns.o and resolve.o from PJLIB-UTIL. * * Default: 1 (enabled) * * @see PJSIP_MAX_RESOLVED_ADDRESSES */ #ifndef PJSIP_HAS_RESOLVER # define PJSIP_HAS_RESOLVER 1 #endif /** * Maximum number of addresses returned by the resolver. The number here * will slightly affect stack usage, since each entry will occupy about * 32 bytes of stack memory. * * Default: 8 * * @see PJSIP_HAS_RESOLVER */ #ifndef PJSIP_MAX_RESOLVED_ADDRESSES # define PJSIP_MAX_RESOLVED_ADDRESSES 8 #endif /** * Enable TLS SIP transport support. For most systems this means that * OpenSSL must be installed. * * Default: follow PJ_HAS_SSL_SOCK setting, which is 0 (disabled) by default. */ #ifndef PJSIP_HAS_TLS_TRANSPORT # define PJSIP_HAS_TLS_TRANSPORT PJ_HAS_SSL_SOCK #endif /** * The TLS pending incoming connection backlog number to be set in accept(). * * Default: 5 * * @see PJSIP_TCP_TRANSPORT_BACKLOG */ #ifndef PJSIP_TLS_TRANSPORT_BACKLOG # define PJSIP_TLS_TRANSPORT_BACKLOG 5 #endif /** * Specify whether TLS listener should use SO_REUSEADDR option. * * Default is FALSE on Windows and TRUE on non-Windows. * * @see PJSIP_TCP_TRANSPORT_REUSEADDR */ #ifndef PJSIP_TLS_TRANSPORT_REUSEADDR # if (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64) # define PJSIP_TLS_TRANSPORT_REUSEADDR 0 # else # define PJSIP_TLS_TRANSPORT_REUSEADDR 1 # endif #endif /* Endpoint. */ #define PJSIP_MAX_TIMER_COUNT (2*pjsip_cfg()->tsx.max_count + \ 2*PJSIP_MAX_DIALOG_COUNT) /** * Initial memory block for the endpoint. */ #ifndef PJSIP_POOL_LEN_ENDPT # define PJSIP_POOL_LEN_ENDPT (4000) #endif /** * Memory increment for endpoint. */ #ifndef PJSIP_POOL_INC_ENDPT # define PJSIP_POOL_INC_ENDPT (4000) #endif /* Transport related constants. */ /** * Initial memory block for rdata. */ #ifndef PJSIP_POOL_RDATA_LEN # define PJSIP_POOL_RDATA_LEN 4000 #endif /** * Memory increment for rdata. */ #ifndef PJSIP_POOL_RDATA_INC # define PJSIP_POOL_RDATA_INC 4000 #endif #define PJSIP_POOL_LEN_TRANSPORT 512 #define PJSIP_POOL_INC_TRANSPORT 512 /** * Initial memory block size for tdata. */ #ifndef PJSIP_POOL_LEN_TDATA # define PJSIP_POOL_LEN_TDATA 4000 #endif /** * Memory increment for tdata. */ #ifndef PJSIP_POOL_INC_TDATA # define PJSIP_POOL_INC_TDATA 4000 #endif /** * Initial memory size for UA layer */ #ifndef PJSIP_POOL_LEN_UA # define PJSIP_POOL_LEN_UA 512 #endif /** * Memory increment for UA layer. */ #ifndef PJSIP_POOL_INC_UA # define PJSIP_POOL_INC_UA 512 #endif #define PJSIP_MAX_FORWARDS_VALUE 70 #define PJSIP_RFC3261_BRANCH_ID "z9hG4bK" #define PJSIP_RFC3261_BRANCH_LEN 7 /* Transaction related constants. */ /** * Initial memory size for transaction layer. The bulk of pool usage * for transaction layer will be used to create the hash table, so * setting this value too high will not help too much with reducing * fragmentation and the memory will most likely be wasted. */ #ifndef PJSIP_POOL_TSX_LAYER_LEN # define PJSIP_POOL_TSX_LAYER_LEN 512 #endif /** * Memory increment for transaction layer. The bulk of pool usage * for transaction layer will be used to create the hash table, so * setting this value too high will not help too much with reducing * fragmentation and the memory will most likely be wasted. */ #ifndef PJSIP_POOL_TSX_LAYER_INC # define PJSIP_POOL_TSX_LAYER_INC 512 #endif /** * Initial memory size for a SIP transaction object. */ #ifndef PJSIP_POOL_TSX_LEN # define PJSIP_POOL_TSX_LEN 1536 /* 768 */ #endif /** * Memory increment for transaction object. */ #ifndef PJSIP_POOL_TSX_INC # define PJSIP_POOL_TSX_INC 256 #endif /** * Delay for non-100 1xx retransmission, in seconds. * Set to 0 to disable this feature. * * Default: 60 seconds */ #ifndef PJSIP_TSX_1XX_RETRANS_DELAY # define PJSIP_TSX_1XX_RETRANS_DELAY 60 #endif #define PJSIP_MAX_TSX_KEY_LEN (PJSIP_MAX_URL_SIZE*2) /* User agent. */ #define PJSIP_POOL_LEN_USER_AGENT 1024 #define PJSIP_POOL_INC_USER_AGENT 1024 /* Message/URL related constants. */ #define PJSIP_MAX_CALL_ID_LEN pj_GUID_STRING_LENGTH() #define PJSIP_MAX_TAG_LEN pj_GUID_STRING_LENGTH() #define PJSIP_MAX_BRANCH_LEN (PJSIP_RFC3261_BRANCH_LEN + pj_GUID_STRING_LENGTH() + 2) #define PJSIP_MAX_HNAME_LEN 64 /* Dialog related constants. */ #define PJSIP_POOL_LEN_DIALOG 1200 #define PJSIP_POOL_INC_DIALOG 512 /* Maximum header types. */ #define PJSIP_MAX_HEADER_TYPES 72 /* Maximum URI types. */ #define PJSIP_MAX_URI_TYPES 4 /***************************************************************************** * Default timeout settings, in miliseconds. */ /** Transaction T1 timeout value. */ #if !defined(PJSIP_T1_TIMEOUT) # define PJSIP_T1_TIMEOUT 500 #endif /** Transaction T2 timeout value. */ #if !defined(PJSIP_T2_TIMEOUT) # define PJSIP_T2_TIMEOUT 4000 #endif /** Transaction completed timer for non-INVITE */ #if !defined(PJSIP_T4_TIMEOUT) # define PJSIP_T4_TIMEOUT 5000 #endif /** Transaction completed timer for INVITE */ #if !defined(PJSIP_TD_TIMEOUT) # define PJSIP_TD_TIMEOUT 32000 #endif /***************************************************************************** * Authorization */ /** * If this flag is set, the stack will keep the Authorization/Proxy-Authorization * headers that are sent in a cache. Future requests with the same realm and * the same method will use the headers in the cache (as long as no qop is * required by server). * * Turning on this flag will make authorization process goes faster, but * will grow the memory usage undefinitely until the dialog/registration * session is terminated. * * Default: 0 */ #if !defined(PJSIP_AUTH_HEADER_CACHING) # define PJSIP_AUTH_HEADER_CACHING 0 #endif /** * If this flag is set, the stack will proactively send Authorization/Proxy- * Authorization header for next requests. If next request has the same method * with any of previous requests, then the last header which is saved in * the cache will be used (if PJSIP_AUTH_CACHING is set). Otherwise a fresh * header will be recalculated. If a particular server has requested qop, then * a fresh header will always be calculated. * * If this flag is NOT set, then the stack will only send Authorization/Proxy- * Authorization headers when it receives 401/407 response from server. * * Turning ON this flag will grow memory usage of a dialog/registration pool * indefinitely until it is terminated, because the stack needs to keep the * last WWW-Authenticate/Proxy-Authenticate challenge. * * Default: 0 */ #if !defined(PJSIP_AUTH_AUTO_SEND_NEXT) # define PJSIP_AUTH_AUTO_SEND_NEXT 0 #endif /** * Support qop="auth" directive. * This option also requires client to cache the last challenge offered by * server. * * Default: 1 */ #if !defined(PJSIP_AUTH_QOP_SUPPORT) # define PJSIP_AUTH_QOP_SUPPORT 1 #endif /** * Maximum number of stale retries when server keeps rejecting our request * with stale=true. * * Default: 3 */ #ifndef PJSIP_MAX_STALE_COUNT # define PJSIP_MAX_STALE_COUNT 3 #endif /** * Specify support for IMS/3GPP digest AKA authentication version 1 and 2 * (AKAv1-MD5 and AKAv2-MD5 respectively). * * Note that if this is enabled, application would need to link with * libmilenage library from \a third_party directory. * * Default: 0 (for now) */ #ifndef PJSIP_HAS_DIGEST_AKA_AUTH # define PJSIP_HAS_DIGEST_AKA_AUTH 0 #endif /** * Specify the number of seconds to refresh the client registration * before the registration expires. * * Default: 5 seconds */ #ifndef PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH # define PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH 5 #endif /** * Specify whether client registration should check for its registered * contact in Contact header of successful REGISTE response to determine * whether registration has been successful. This setting may be disabled * if non-compliant registrar is unable to return correct Contact header. * * This setting can be changed in run-time by settting \a regc.check_contact * field of pjsip_cfg(). * * Default is 1 */ #ifndef PJSIP_REGISTER_CLIENT_CHECK_CONTACT # define PJSIP_REGISTER_CLIENT_CHECK_CONTACT 1 #endif /** * Specify whether client registration should add "x-uid" extension * parameter in all Contact URIs that it registers to assist the * matching of Contact URIs in the 200/OK REGISTER response, in * case the registrar is unable to return exact Contact URI in the * 200/OK response. * * This setting can be changed in run-time by setting * \a regc.add_xuid_param field of pjsip_cfg(). * * Default is 0. */ #ifndef PJSIP_REGISTER_CLIENT_ADD_XUID_PARAM # define PJSIP_REGISTER_CLIENT_ADD_XUID_PARAM 0 #endif /***************************************************************************** * SIP Event framework and presence settings. */ /** * Specify the time (in seconds) to send SUBSCRIBE to refresh client * subscription before the actual interval expires. * * Default: 5 seconds */ #ifndef PJSIP_EVSUB_TIME_UAC_REFRESH # define PJSIP_EVSUB_TIME_UAC_REFRESH 5 #endif /** * Specify the time (in seconds) to send PUBLISH to refresh client * publication before the actual interval expires. * * Default: 5 seconds */ #ifndef PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH # define PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH 5 #endif /** * Specify the time (in seconds) to wait for the final NOTIFY from the * server after client has sent un-SUBSCRIBE request. * * Default: 5 seconds */ #ifndef PJSIP_EVSUB_TIME_UAC_TERMINATE # define PJSIP_EVSUB_TIME_UAC_TERMINATE 5 #endif /** * Specify the time (in seconds) for client subscription to wait for another * NOTIFY from the server, if it has rejected the last NOTIFY with non-2xx * final response (such as 401). If further NOTIFY is not received within * this period, the client will unsubscribe. * * Default: 5 seconds */ #ifndef PJSIP_EVSUB_TIME_UAC_WAIT_NOTIFY # define PJSIP_EVSUB_TIME_UAC_WAIT_NOTIFY 5 #endif /** * Specify the default expiration time for presence event subscription, for * both client and server subscription. For client subscription, application * can override this by specifying positive non-zero value in "expires" * parameter when calling #pjsip_pres_initiate(). For server subscription, * we would take the expiration value from the Expires header sent by client * in the SUBSCRIBE request if the header exists and its value is less than * this setting, otherwise this setting will be used. * * Default: 600 seconds (10 minutes) */ #ifndef PJSIP_PRES_DEFAULT_EXPIRES # define PJSIP_PRES_DEFAULT_EXPIRES 600 #endif /** * Specify the status code value to respond to bad message body in NOTIFY * request for presence. Scenarios that are considered bad include non- * PIDF/XML and non-XPIDF/XML body, multipart message bodies without PIDF/XML * nor XPIDF/XML part, and bad (parsing error) PIDF and X-PIDF bodies * themselves. * * Default value is 488. Application may change this to 200 to ignore the * unrecognised content (this is useful if the application wishes to handle * the content itself). Only non-3xx final response code is allowed here. * * Default: 488 (Not Acceptable Here) */ #ifndef PJSIP_PRES_BAD_CONTENT_RESPONSE # define PJSIP_PRES_BAD_CONTENT_RESPONSE 488 #endif /** * Add "timestamp" information in generated PIDF document for both server * subscription and presence publication. * * Default: 1 (yes) */ #ifndef PJSIP_PRES_PIDF_ADD_TIMESTAMP # define PJSIP_PRES_PIDF_ADD_TIMESTAMP 1 #endif /** * Default session interval for Session Timer (RFC 4028) extension, in * seconds. As specified in RFC 4028 Section 4, this value must not be * less than the absolute minimum for the Session-Expires header field * 90 seconds, and the recommended value is 1800 seconds. * * Default: 1800 seconds */ #ifndef PJSIP_SESS_TIMER_DEF_SE # define PJSIP_SESS_TIMER_DEF_SE 1800 #endif /** * Specify whether the client publication session should queue the * PUBLISH request should there be another PUBLISH transaction still * pending. If this is set to false, the client will return error * on the PUBLISH request if there is another PUBLISH transaction still * in progress. * * Default: 1 (yes) */ #ifndef PJSIP_PUBLISHC_QUEUE_REQUEST # define PJSIP_PUBLISHC_QUEUE_REQUEST 1 #endif /** * Specify the default expiration time for Message Waiting Indication * (RFC 3842) event subscription, for both client and server subscription. * For client subscription, application can override this by specifying * positive non-zero value in "expires" parameter when calling * #pjsip_mwi_initiate(). For server subscription, we would take the * expiration value from the Expires header sent by client in the SUBSCRIBE * request if the header exists and its value is less than this setting, * otherwise this setting will be used. * * Default: 3600 seconds */ #ifndef PJSIP_MWI_DEFAULT_EXPIRES # define PJSIP_MWI_DEFAULT_EXPIRES 3600 #endif /** * Specify whether transport manager should maintain a list of transmit * buffer instances, so any possible dangling instance can be cleaned up * when the transport manager is shutdown (see also ticket #1671). * Note that this feature will have slight impact on the performance as * mutex is employed in updating the list, i.e: on creation and destruction * of transmit data. * * Default: 0 (no) */ #ifndef PJSIP_HAS_TX_DATA_LIST # define PJSIP_HAS_TX_DATA_LIST 0 #endif /** * Specify whether to accept INVITE/re-INVITE with unknown content type, * by default the stack will reject this type of message as specified in * RFC3261 section 8.2.3. * Application that wishes to process the body could set this to PJ_TRUE, * be informed that SDP offer/answer will still be present. * * Default: PJ_FALSE */ #ifndef PJSIP_INV_ACCEPT_UNKNOWN_BODY # define PJSIP_INV_ACCEPT_UNKNOWN_BODY PJ_FALSE #endif PJ_END_DECL /** * @} */ #include #endif /* __PJSIP_SIP_CONFIG_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_dialog.h ================================================ /* $Id: sip_dialog.h 4173 2012-06-20 10:39:05Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_DIALOG_H__ #define __PJSIP_SIP_DIALOG_H__ /** * @file sip_dialog.h * @brief SIP Dialog abstraction */ #include #include #include #include #include #include #include /** * @defgroup PJSIP_DIALOG Base Dialog * @ingroup PJSIP_UA * @brief The base dialog framework to support dialog usages. * @{ * * The base dialog framework provides management for base dialog * properties such as From header, To header, CSeq * sequencing, Call-ID header, Contact header management, * dialog route-set management, and common authentication. * This basic dialog functionality will be shared by all dialog * usages of a particular dialog. * * More detailed information is explained in * PJSIP Developer's Guide * PDF document, and readers are encouraged to read the document to * get the concept behind dialog, dialog usages, and INVITE sessions. * * Application MUST initialize the user agent layer module by calling * #pjsip_ua_init_module() before using any of the dialog API, and link * the application with with pjsip-core library. */ PJ_BEGIN_DECL /* Deprecated API pjsip_dlg_create_uas() due to a fatal bug of possible * premature dialog destroy. Application should not change this setting, * unless it uses single worker thread. * See also https://trac.pjsip.org/repos/ticket/1902. */ #ifndef DEPRECATED_FOR_TICKET_1902 # define DEPRECATED_FOR_TICKET_1902 1 #endif /** * This structure is used to describe dialog's participants, which in this * case is local party (i.e. us) and remote party. */ typedef struct pjsip_dlg_party { pjsip_fromto_hdr *info; /**< From/To header, inc tag. */ pj_str_t info_str; /**< String rep of info header. */ pj_uint32_t tag_hval; /**< Hashed value of the tag. */ pjsip_contact_hdr *contact; /**< Contact header. */ pj_int32_t first_cseq;/**< First CSeq seen. */ pj_int32_t cseq; /**< Next sequence number. */ } pjsip_dlg_party; /** * Dialog state. */ typedef enum pjsip_dialog_state { /** Dialog is not established. */ PJSIP_DIALOG_STATE_NULL, /** Dialog has been established (probably early) */ PJSIP_DIALOG_STATE_ESTABLISHED } pjsip_dialog_state; /** * Dialog capability status. */ typedef enum pjsip_dialog_cap_status { /** Capability is unsupported. */ PJSIP_DIALOG_CAP_UNSUPPORTED = 0, /** Capability is supported */ PJSIP_DIALOG_CAP_SUPPORTED = 1, /** * Unknown capability status. This is usually because we lack the * capability info which is retrieved from capability header specified * in the dialog messages. */ PJSIP_DIALOG_CAP_UNKNOWN = 2 } pjsip_dialog_cap_status; /** * This structure describes the dialog structure. Application MUST NOT * try to SET the values here directly, but instead it MUST use the * appropriate dialog API. The dialog declaration only needs to be made * visible because other PJSIP modules need to see it (e.g. INVITE session, * the event framework, etc.). * * Application MAY READ the dialog contents directly after it acquires * dialog lock. * * To acquire dialog lock, use #pjsip_dlg_inc_lock(), and to release it, * use #pjsip_dlg_dec_lock(). DO NOT USE pj_mutex_lock()/pj_mutex_unlock() * on the dialog's mutex directly, because this will not protect against * dialog being destroyed. */ struct pjsip_dialog { /** The dialog set list. */ PJ_DECL_LIST_MEMBER(pjsip_dialog); /* Dialog's system properties. */ char obj_name[PJ_MAX_OBJ_NAME]; /**< Standard id. */ pj_pool_t *pool; /**< Dialog's pool. */ pj_mutex_t *mutex_; /**< Dialog's mutex. Do not call!! Use pjsip_dlg_inc_lock() instead! */ pjsip_user_agent *ua; /**< User agent instance. */ pjsip_endpoint *endpt; /**< Endpoint instance. */ /** The dialog set which this dialog belongs (opaque type). */ void *dlg_set; /* Dialog's session properties. */ pjsip_dialog_state state; /**< Dialog state. */ pjsip_uri *target; /**< Current target. */ pjsip_target_set target_set; /**< Target set, for UAC only. */ pjsip_hdr inv_hdr; /**< Headers from hparam in dest URL */ pjsip_dlg_party local; /**< Local party info. */ pjsip_dlg_party remote; /**< Remote party info. */ pjsip_hdr rem_cap_hdr;/**< List of remote capability header. */ pjsip_role_e role; /**< Initial role. */ pj_bool_t uac_has_2xx;/**< UAC has received 2xx response? */ pj_bool_t secure; /**< Use secure transport? */ pj_bool_t add_allow; /**< Add Allow header in requests? */ pjsip_cid_hdr *call_id; /**< Call-ID header. */ pjsip_route_hdr route_set; /**< Route set. */ pj_bool_t route_set_frozen; /**< Route set has been set. */ pjsip_auth_clt_sess auth_sess; /**< Client authentication session. */ /** Session counter. */ int sess_count; /**< Number of sessions. */ /** Transaction counter. */ int tsx_count; /**< Number of pending transactions. */ /** Transport selector. */ pjsip_tpselector tp_sel; /* Dialog usages. */ unsigned usage_cnt; /**< Number of registered usages. */ pjsip_module *usage[PJSIP_MAX_MODULE]; /**< Array of usages, priority sorted */ /** Module specific data. */ void *mod_data[PJSIP_MAX_MODULE]; /**< Module data. */ /** * If via_addr is set, it will be used as the "sent-by" field of the * Via header for outgoing requests as long as the request uses via_tp * transport. Normally application should not use or access these fields. */ pjsip_host_port via_addr; /**< Via address. */ const void *via_tp; /**< Via transport. */ }; /** * This utility function returns PJ_TRUE if the specified method is a * dialog creating request. This method property is used to determine * whether Contact header should be included in outgoing request. * * @param m The SIP method. * * @return PJ_TRUE if the method creates a dialog. */ PJ_DECL(pj_bool_t) pjsip_method_creates_dialog(const pjsip_method *m); /** * Create a new dialog and return the instance in p_dlg parameter. * After creating the dialog, application can add modules as dialog usages * by calling #pjsip_dlg_add_usage(). * * If the request has To tag parameter, dialog's local tag will be initialized * from this value. Otherwise a globally unique id generator will be invoked to * create dialog's local tag. * * This function also initializes the dialog's route set based on the * Record-Route headers in the request, if present. * * Note that initially, the session count in the dialog will be initialized * to zero. * * @param ua The user agent module instance. * @param local_uri Dialog local URI (i.e. From header). * @param local_contact Optional dialog local Contact to be put as Contact * header value, hence the format must follow * RFC 3261 Section 20.10: * When the header field value contains a display * name, the URI including all URI parameters is * enclosed in "<" and ">". If no "<" and ">" are * present, all parameters after the URI are header * parameters, not URI parameters. The display name * can be tokens, or a quoted string, if a larger * character set is desired. * If this argument is NULL, the Contact will be taken * from the local URI. * @param remote_uri Dialog remote URI (i.e. To header). * @param target Optional initial remote target. If this argument * is NULL, the initial target will be set to * remote URI. * @param p_dlg Pointer to receive the dialog. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_dlg_create_uac( pjsip_user_agent *ua, const pj_str_t *local_uri, const pj_str_t *local_contact, const pj_str_t *remote_uri, const pj_str_t *target, pjsip_dialog **p_dlg); #if !DEPRECATED_FOR_TICKET_1902 /** * Initialize UAS dialog from the information found in the incoming request * that creates a dialog (such as INVITE, REFER, or SUBSCRIBE), and set the * local Contact to contact. If contact is not specified, the local contact * is initialized from the URI in the To header in the request. * * This function will also create UAS transaction for the incoming request, * and associate the transaction to the rdata. Application can query the * transaction used to handle this request by calling #pjsip_rdata_get_tsx() * after this function returns. * * Note that initially, the session count in the dialog will be initialized * to zero. * * * @param ua The user agent module instance. * @param rdata The incoming request that creates the dialog, * such as INVITE, SUBSCRIBE, or REFER. * @param contact Optional dialog local Contact to be put as Contact * header value, hence the format must follow * RFC 3261 Section 20.10: * When the header field value contains a display * name, the URI including all URI parameters is * enclosed in "<" and ">". If no "<" and ">" are * present, all parameters after the URI are header * parameters, not URI parameters. The display name * can be tokens, or a quoted string, if a larger * character set is desired. * If this argument is NULL, the local contact will be * initialized from the value of To header in the * request. * @param p_dlg Pointer to receive the dialog. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua, pjsip_rx_data *rdata, const pj_str_t *contact, pjsip_dialog **p_dlg); #endif /** * Initialize UAS dialog from the information found in the incoming request * that creates a dialog (such as INVITE, REFER, or SUBSCRIBE), and set the * local Contact to contact. If contact is not specified, the local contact * is initialized from the URI in the To header in the request. * * This function will also create UAS transaction for the incoming request, * and associate the transaction to the rdata. Application can query the * transaction used to handle this request by calling #pjsip_rdata_get_tsx() * after this function returns. * * Note that initially, the session count in the dialog will be initialized * to 1 (one), and the dialog is locked. Application needs to explicitly call * #pjsip_dlg_dec_lock() to release the lock and decrease the session count. * * * @param ua The user agent module instance. * @param rdata The incoming request that creates the dialog, * such as INVITE, SUBSCRIBE, or REFER. * @param contact Optional dialog local Contact to be put as Contact * header value, hence the format must follow * RFC 3261 Section 20.10: * When the header field value contains a display * name, the URI including all URI parameters is * enclosed in "<" and ">". If no "<" and ">" are * present, all parameters after the URI are header * parameters, not URI parameters. The display name * can be tokens, or a quoted string, if a larger * character set is desired. * If this argument is NULL, the local contact will be * initialized from the value of To header in the * request. * @param p_dlg Pointer to receive the dialog. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_dlg_create_uas_and_inc_lock( pjsip_user_agent *ua, pjsip_rx_data *rdata, const pj_str_t *contact, pjsip_dialog **p_dlg); /** * Lock/bind dialog to a specific transport/listener. This is optional, * as normally transport will be selected automatically based on the * destination of messages upon resolver completion. When the dialog is * explicitly bound to the specific transport/listener, all transactions * originated by this dialog will use the specified transport/listener * when sending outgoing requests. * * Note that this doesn't affect the Contact header generated by this * dialog. Application must manually update the Contact header if * necessary, to adjust the address according to the transport being * selected. * * @param dlg The dialog instance. * @param sel Transport selector containing the specification of * transport or listener to be used by this dialog * to send requests. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_dlg_set_transport(pjsip_dialog *dlg, const pjsip_tpselector *sel); /** * Set the "sent-by" field of the Via header for outgoing requests. * * @param dlg The dialog instance. * @param via_addr Set via_addr to use for the Via header or NULL to use * the transport's published name. * @param via_tp via_addr will only be used if we are using via_tp * transport. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_dlg_set_via_sent_by(pjsip_dialog *dlg, pjsip_host_port *via_addr, pjsip_transport *via_tp); /** * Create a new (forked) dialog on receipt on forked response in rdata. * The new dialog will be created from original_dlg, except that it will have * new remote tag as copied from the To header in the response. Upon return, * the new_dlg will have been registered to the user agent. Applications just * need to add modules as dialog's usages. * * Note that initially, the session count in the dialog will be initialized * to zero. * * @param original_dlg The original UAC dialog. * @param rdata The incoming forked response message. * @param new_dlg Pointer to receive the new dialog. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_dlg_fork(const pjsip_dialog *original_dlg, const pjsip_rx_data *rdata, pjsip_dialog **new_dlg ); /** * Forcefully terminate the dialog. Application can only call this function * when there is no session associated to the dialog. If there are sessions * that use this dialog, this function will refuse to terminate the dialog. * For this case, application MUST call the appropriate termination function * for each dialog session (e.g. #pjsip_inv_terminate() to terminate INVITE * session). * * @param dlg The dialog. * * @return PJ_SUCCESS if dialog has been terminated. */ PJ_DECL(pj_status_t) pjsip_dlg_terminate( pjsip_dialog *dlg ); /** * Set dialog's initial route set to route_set list. This can only be called * for UAC dialog, before any request is sent. After dialog has been * established, the route set can not be changed. * * For UAS dialog, the route set will be initialized in * pjsip_dlg_create_uas_and_inc_lock() from the Record-Route headers in * the incoming request. * * The route_set argument is standard list of Route headers (i.e. with * sentinel). * * @param dlg The UAC dialog. * @param route_set List of Route header. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_dlg_set_route_set( pjsip_dialog *dlg, const pjsip_route_hdr *route_set ); /** * Increment the number of sessions in the dialog. Note that initially * (after created) the dialog has the session counter set to zero. * * @param dlg The dialog. * @param mod The module that increments the session counter. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_dlg_inc_session( pjsip_dialog *dlg, pjsip_module *mod); /** * Decrement the number of sessions in the dialog. Once the session counter * reach zero and there is no pending transaction, the dialog will be * destroyed. Note that this function may destroy the dialog immediately * if there is no pending transaction when this function is called. * * @param dlg The dialog. * @param mod The module that decrements the session counter. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_dlg_dec_session( pjsip_dialog *dlg, pjsip_module *mod); /** * Add a module as dialog usage, and optionally set the module specific data. * * @param dlg The dialog. * @param module The module to be registered as dialog usage. * @param mod_data Optional arbitrary data to be attached to dialog's * mod_data array at the module's index. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_dlg_add_usage( pjsip_dialog *dlg, pjsip_module *module, void *mod_data ); /** * Check if the specified module has been registered as usage to the dialog. * * @param dlg The dialog. * @param module The module. * * @return PJ_TRUE if the specified module is currently * registered as a usage to the dialog. */ PJ_DECL(pj_bool_t) pjsip_dlg_has_usage(pjsip_dialog *dlg, pjsip_module *module); /** * Attach module specific data to the dialog. Application can also set * the value directly by accessing dlg->mod_data[module_id]. * * @param dlg The dialog * @param mod_id The ID of the module from which the data is to be * set to the dialog. * @param data Arbitrary data. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_dlg_set_mod_data( pjsip_dialog *dlg, int mod_id, void *data ); /** * Get module specific data previously attached to the dialog. Application * can also get value directly by accessing dlg->mod_data[module_id]. * * @param dlg The dialog * @param mod_id The ID of the module from which the data is to be * retrieved from the dialog. * * @return The data that was previously set, or NULL. */ PJ_DECL(void*) pjsip_dlg_get_mod_data( pjsip_dialog *dlg, int mod_id); /** * Lock dialog and increment session counter termporarily, to prevent it * from being destroyed. * * @param dlg The dialog. */ PJ_DECL(void) pjsip_dlg_inc_lock( pjsip_dialog *dlg ); /** * Try to acquire dialog's lock, but return immediately if lock can not * be acquired. * * @param dlg The dialog. * * @return PJ_SUCCESS if lock has been acquired. */ PJ_DECL(pj_status_t) pjsip_dlg_try_inc_lock( pjsip_dialog *dlg ); /** * Unlock dialog and decrement temporary session counter. After this function * is called, dialog may be destroyed. * * @param dlg The dialog. */ PJ_DECL(void) pjsip_dlg_dec_lock( pjsip_dialog *dlg ); /** * Get the dialog instance in the incoming rdata. If an incoming message * matches an existing dialog, the user agent must have put the matching * dialog instance in the rdata, or otherwise this function will return * NULL if the message didn't match any existing dialog. * * This function can only be called after endpoint distributes the message * to the transaction layer or UA layer. In other words, application can * only call this function in the context of module that runs in priority * number higher than PJSIP_MOD_PRIORITY_UA_PROXY_LAYER. * * @param rdata Incoming message buffer. * * @return The dialog instance that "owns" the message. */ PJ_DECL(pjsip_dialog*) pjsip_rdata_get_dlg( pjsip_rx_data *rdata ); /** * Get the associated dialog for the specified transaction, if any. * * @param tsx The transaction. * * @return The dialog instance which has been registered * to the transaction as transaction user, or * NULL if the transaction is outside any dialogs. */ PJ_DECL(pjsip_dialog*) pjsip_tsx_get_dlg( pjsip_transaction *tsx ); /** * Create a basic/generic request with the specified method and optionally * specify the cseq. Use value -1 for cseq to have the dialog automatically * put next cseq number for the request. Otherwise for some requests, * e.q. CANCEL and ACK, application must put the CSeq in the original * INVITE request as the parameter. * * This function will also put Contact header where appropriate. * * @param dlg The dialog instance. * @param method The method of the request. * @param cseq Optional CSeq, which only needs to be specified * when creating ACK and CANCEL. For other requests, * specify -1 to use dialog's internal counter. * @param tdata Pointer to receive the request's transmit * data buffer. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_dlg_create_request( pjsip_dialog *dlg, const pjsip_method *method, int cseq, pjsip_tx_data **tdata); /** * Send request message to remote peer. If the request is not an ACK request, * the dialog will send the request statefully, by creating an UAC transaction * and send the request with the transaction. * * Also when the request is not ACK or CANCEL, the dialog will increment its * local cseq number and update the cseq in the request according to dialog's * cseq. * * If p_tsx is not null, this argument will be set with the transaction * instance that was used to send the request. * * This function will decrement the transmit data's reference counter * regardless the status of the operation. * * @param dlg The dialog. * @param tdata The request message to be sent. * @param mod_data_id Optional module data index to put an optional data * into the transaction. If no module data is to be * attached, this value should be -1. * @param mod_data Optional module data to be attached to the * transaction at mod_data_id index. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_dlg_send_request ( pjsip_dialog *dlg, pjsip_tx_data *tdata, int mod_data_id, void *mod_data); /** * Create a response message for the incoming request in rdata with status * code st_code and optional status text st_text. This function is different * than endpoint's API #pjsip_endpt_create_response() in that the dialog * function adds Contact header and Record-Routes headers in the response * where appropriate. * * @param dlg The dialog. * @param rdata The incoming request message for which the * response will be created. * @param st_code Status code. * @param st_text Optional string for custom status reason text. * @param tdata Pointer to receive the response message transmit * data buffer. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_dlg_create_response( pjsip_dialog *dlg, pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, pjsip_tx_data **tdata); /** * Modify previously sent response with other status code. Contact header * will be added when appropriate. * * @param dlg The dialog. * @param tdata The transmit data buffer containing response * message to be modified. * @param st_code New status code to be set. * @param st_text Optional string for custom status reason text. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_dlg_modify_response( pjsip_dialog *dlg, pjsip_tx_data *tdata, int st_code, const pj_str_t *st_text); /** * Send response message statefully. The transaction instance MUST be the * transaction that was reported on on_rx_request() callback. * * This function decrements the transmit data's reference counter regardless * the status of the operation. * * @param dlg The dialog. * @param tsx The UAS transaction associated with the incoming * request. If the request is within a dialog, or * a dialog has been created for the request that * creates the dialog, application can get the * transaction instance for the request by calling * #pjsip_rdata_get_tsx(). * @param tdata Response message to be sent. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_dlg_send_response( pjsip_dialog *dlg, pjsip_transaction *tsx, pjsip_tx_data *tdata); /** * This composite function sends response message statefully to an incoming * request message inside dialog. * * @param dlg The endpoint instance. * @param rdata The incoming request message. * @param st_code Status code of the response. * @param st_text Optional status text of the response. * @param hdr_list Optional header list to be added to the response. * @param body Optional message body to be added to the response. * * @return PJ_SUCCESS if response message has successfully been * sent. */ PJ_DECL(pj_status_t) pjsip_dlg_respond( pjsip_dialog *dlg, pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, const pjsip_hdr *hdr_list, const pjsip_msg_body *body ); /** * Check if remote peer have the specified capability as published * in the dialog messages from remote peer. * * Notes: * - The capability \a token lookup will apply exact match, but not * case-sensitive, for example: "text/html" will not match * "text / html" (notice the spaces). * * @param dlg The dialog. * @param htype The header type to be checked, which value may be: * - PJSIP_H_ACCEPT * - PJSIP_H_ALLOW * - PJSIP_H_SUPPORTED * @param hname If htype specifies PJSIP_H_OTHER, then the header name * must be supplied in this argument. Otherwise the value * must be set to NULL. * @param token The capability token to check. For example, if \a htype * is PJSIP_H_ALLOW, then \a token specifies the method * names; if \a htype is PJSIP_H_SUPPORTED, then \a token * specifies the extension names such as "100rel". * * @return PJSIP_DIALOG_CAP_SUPPORTED if the specified capability * is explicitly supported, see @pjsip_dialog_cap_status * for more info. */ PJ_DECL(pjsip_dialog_cap_status) pjsip_dlg_remote_has_cap( pjsip_dialog *dlg, int htype, const pj_str_t *hname, const pj_str_t *token); /** * Get the specified capability header from the remote capability headers * stored in the dialog. * * @param dlg The dialog. * @param htype The header type to be retrieved, which value may be: * - PJSIP_H_ACCEPT * - PJSIP_H_ALLOW * - PJSIP_H_SUPPORTED * @param hname If htype specifies PJSIP_H_OTHER, then the header name * must be supplied in this argument. Otherwise the value * must be set to NULL. * * @return The appropriate header, or NULL if the header is not * available. */ PJ_DECL(const pjsip_hdr*) pjsip_dlg_get_remote_cap_hdr(pjsip_dialog *dlg, int htype, const pj_str_t *hname); /** * Set remote capability from a SIP header containing array of capability * tags/values. * * @param dlg The dialog. * @param cap_hdr The SIP header. * * @return PJ_SUCCESS when successful, otherwise the appropriate * error code will be returned. */ PJ_DECL(pj_status_t) pjsip_dlg_set_remote_cap_hdr( pjsip_dialog *dlg, const pjsip_generic_array_hdr *cap_hdr); /** * Remove a remote capability header. * * @param dlg The dialog. * @param htype The header type to be removed, which value may be: * - PJSIP_H_ACCEPT * - PJSIP_H_ALLOW * - PJSIP_H_SUPPORTED * @param hname If htype specifies PJSIP_H_OTHER, then the header name * must be supplied in this argument. Otherwise the value * must be set to NULL. * * @return PJ_SUCCESS when successful, otherwise the appropriate * error code will be returned. */ PJ_DECL(pj_status_t) pjsip_dlg_remove_remote_cap_hdr(pjsip_dialog *dlg, int htype, const pj_str_t *hname); /** * Update remote capabilities from a received message. The header types * to be updated from the message will only be \a PJSIP_H_ACCEPT, * \a PJSIP_H_ALLOW, and \a PJSIP_H_SUPPORTED. * * @param dlg The dialog. * @param msg The received message. * @param strict If this is set to PJ_TRUE, any header types missing * from the message will cause removal of existing * header types in the capability list. Otherwise, the * capability list will not be modified when any header * type is missing. * * @return PJ_SUCCESS when successful, otherwise the appropriate * error code will be returned. */ PJ_DECL(pj_status_t) pjsip_dlg_update_remote_cap(pjsip_dialog *dlg, const pjsip_msg *msg, pj_bool_t strict); /** * @} */ /* * Internal (called by sip_ua_layer.c) */ /* Receives transaction event (called by user_agent module) */ void pjsip_dlg_on_tsx_state( pjsip_dialog *dlg, pjsip_transaction *tsx, pjsip_event *e ); void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata ); void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ); PJ_END_DECL #endif /* __PJSIP_SIP_DIALOG_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_endpoint.h ================================================ /* $Id: sip_endpoint.h 4275 2012-10-04 06:11:58Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_ENDPOINT_H__ #define __PJSIP_SIP_ENDPOINT_H__ /** * @file sip_endpoint.h * @brief SIP Endpoint. */ #include #include /** * @defgroup PJSIP_CORE_CORE At the Very Core * @ingroup PJSIP_CORE * @brief The very core of PJSIP. */ PJ_BEGIN_DECL /** * @defgroup PJSIP_ENDPT Endpoint * @ingroup PJSIP_CORE_CORE * @brief The master, owner of all objects * * SIP Endpoint instance (pjsip_endpoint) can be viewed as the master/owner of * all SIP objects in an application. It performs the following roles: * - it manages the allocation/deallocation of memory pools for all objects. * - it manages listeners and transports, and how they are used by * transactions. * - it receives incoming messages from transport layer and automatically * dispatches them to the correct transaction (or create a new one). * - it has a single instance of timer management (timer heap). * - it manages modules, which is the primary means of extending the library. * - it provides single polling function for all objects and distributes * events. * - it automatically handles incoming requests which can not be handled by * existing modules (such as when incoming request has unsupported method). * - and so on.. * * Application should only instantiate one SIP endpoint instance for every * process. * * @{ */ /** * Type of callback to register to pjsip_endpt_atexit(). */ typedef void (*pjsip_endpt_exit_callback)(pjsip_endpoint *endpt); /** * Create an instance of SIP endpoint from the specified pool factory. * The pool factory reference then will be kept by the endpoint, so that * future memory allocations by SIP components will be taken from the same * pool factory. * * @param pf Pool factory that will be used for the lifetime of * endpoint. * @param name Optional name to be specified for the endpoint. * If this parameter is NULL, then the name will use * local host name. * @param endpt Pointer to receive endpoint instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf, const char *name, pjsip_endpoint **endpt); /** * Destroy endpoint instance. Application must make sure that all pending * transactions have been terminated properly, because this function does not * check for the presence of pending transactions. * * @param endpt The SIP endpoint to be destroyed. */ PJ_DECL(void) pjsip_endpt_destroy(pjsip_endpoint *endpt); /** * Get endpoint name. * * @param endpt The SIP endpoint instance. * * @return Endpoint name, as was registered during endpoint * creation. The string is NULL terminated. */ PJ_DECL(const pj_str_t*) pjsip_endpt_name(const pjsip_endpoint *endpt); /** * Poll for events. Application must call this function periodically to ensure * that all events from both transports and timer heap are handled in timely * manner. This function, like all other endpoint functions, is thread safe, * and application may have more than one thread concurrently calling this function. * * @param endpt The endpoint. * @param max_timeout Maximum time to wait for events, or NULL to wait forever * until event is received. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_endpt_handle_events( pjsip_endpoint *endpt, const pj_time_val *max_timeout); /** * Handle events with additional info about number of events that * have been handled. * * @param endpt The endpoint. * @param max_timeout Maximum time to wait for events, or NULL to wait forever * until event is received. * @param count Optional argument to receive the number of events that * have been handled by the function. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_endpt_handle_events2(pjsip_endpoint *endpt, const pj_time_val *max_timeout, unsigned *count); /** * Schedule timer to endpoint's timer heap. Application must poll the endpoint * periodically (by calling #pjsip_endpt_handle_events) to ensure that the * timer events are handled in timely manner. When the timeout for the timer * has elapsed, the callback specified in the entry argument will be called. * This function, like all other endpoint functions, is thread safe. * * @param endpt The endpoint. * @param entry The timer entry. * @param delay The relative delay of the timer. * @return PJ_OK (zero) if successfull. */ #if PJ_TIMER_DEBUG #define pjsip_endpt_schedule_timer(ept,ent,d) \ pjsip_endpt_schedule_timer_dbg(ept, ent, d, \ __FILE__, __LINE__) PJ_DECL(pj_status_t) pjsip_endpt_schedule_timer_dbg(pjsip_endpoint *endpt, pj_timer_entry *entry, const pj_time_val *delay, const char *src_file, int src_line); #else PJ_DECL(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt, pj_timer_entry *entry, const pj_time_val *delay ); #endif /** * Cancel the previously registered timer. * This function, like all other endpoint functions, is thread safe. * * @param endpt The endpoint. * @param entry The timer entry previously registered. */ PJ_DECL(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, pj_timer_entry *entry ); /** * Get the timer heap instance of the SIP endpoint. * * @param endpt The endpoint. * * @return The timer heap instance. */ PJ_DECL(pj_timer_heap_t*) pjsip_endpt_get_timer_heap(pjsip_endpoint *endpt); /** * Register new module to the endpoint. * The endpoint will then call the load and start function in the module to * properly initialize the module, and assign a unique module ID for the * module. * * @param endpt The endpoint. * @param module The module to be registered. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_endpt_register_module( pjsip_endpoint *endpt, pjsip_module *module ); /** * Unregister a module from the endpoint. * The endpoint will then call the stop and unload function in the module to * properly shutdown the module. * * @param endpt The endpoint. * @param module The module to be registered. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_endpt_unregister_module( pjsip_endpoint *endpt, pjsip_module *module ); /** * This describes additional parameters to pjsip_endpt_process_rx_data() * function. Application MUST call pjsip_process_rdata_param_default() to * initialize this structure. */ typedef struct pjsip_process_rdata_param { /** * Specify the minimum priority number of the modules that are allowed * to process the message. Default is zero to allow all modules to * process the message. */ unsigned start_prio; /** * Specify the pointer of the module where processing will start. * The default is NULL, meaning processing will start from the start * of the module list. */ void *start_mod; /** * Set to N, then processing will start at Nth module after start * module (where start module can be an explicit module as specified * by \a start_mod or the start of module list when \a start_mod is * NULL). For example, if set to 1, then processing will start from * the next module after start module. Default is zero. */ unsigned idx_after_start; /** * Print nothing to log. Default is PJ_FALSE. */ pj_bool_t silent; } pjsip_process_rdata_param; /** * Initialize with default. * * @param p The param. */ PJ_DECL(void) pjsip_process_rdata_param_default(pjsip_process_rdata_param *p); /** * Manually distribute the specified pjsip_rx_data to registered modules. * Normally application does not need to call this function because received * messages will be given to endpoint automatically by transports. * * Application can use this function when it has postponed the processing of * an incoming message, for example to perform long operations such as * database operation or to consult other servers to decide what to do with * the message. In this case, application clones the original rdata, return * from the callback, and perform the long operation. Upon completing the * long operation, it resumes pjsip's module processing by calling this * function, and then free the cloned rdata. * * @param endpt The endpoint instance. * @param rdata The rdata to be distributed. * @param p Optional pointer to param to specify from which module * the processing should start. * @param p_handled Optional pointer to receive last return value of * module's \a on_rx_request() or \a on_rx_response() * callback. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_endpt_process_rx_data(pjsip_endpoint *endpt, pjsip_rx_data *rdata, pjsip_process_rdata_param *p, pj_bool_t *p_handled); /** * Create pool from the endpoint. All SIP components should allocate their * memory pool by calling this function, to make sure that the pools are * allocated from the same pool factory. This function, like all other endpoint * functions, is thread safe. * * @param endpt The SIP endpoint. * @param pool_name Name to be assigned to the pool. * @param initial The initial size of the pool. * @param increment The resize size. * @return Memory pool, or NULL on failure. * * @see pj_pool_create */ PJ_DECL(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt, const char *pool_name, pj_size_t initial, pj_size_t increment ); /** * Return back pool to endpoint to be released back to the pool factory. * This function, like all other endpoint functions, is thread safe. * * @param endpt The endpoint. * @param pool The pool to be destroyed. */ PJ_DECL(void) pjsip_endpt_release_pool( pjsip_endpoint *endpt, pj_pool_t *pool ); /** * Find transaction in endpoint's transaction table by the transaction's key. * This function normally is only used by modules. The key for a transaction * can be created by calling #pjsip_tsx_create_key. * * @param endpt The endpoint instance. * @param key Transaction key, as created with #pjsip_tsx_create_key. * * @return The transaction, or NULL if it's not found. */ PJ_DECL(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt, const pj_str_t *key ); /** * Register the transaction to the endpoint's transaction table. * This function should only be used internally by the stack. * * @param endpt The SIP endpoint. * @param tsx The transaction. */ PJ_DECL(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt, pjsip_transaction *tsx); /** * Forcefull destroy the transaction. This function should only be used * internally by the stack. * * @param endpt The endpoint. * @param tsx The transaction to destroy. */ PJ_DECL(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt, pjsip_transaction *tsx); /** * Create a new transmit data buffer. * This function, like all other endpoint functions, is thread safe. * * @param endpt The endpoint. * @param p_tdata Pointer to receive transmit data buffer. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt, pjsip_tx_data **p_tdata); /** * Create the DNS resolver instance. Application creates the DNS * resolver instance, set the nameserver to be used by the DNS * resolver, then set the DNS resolver to be used by the endpoint * by calling #pjsip_endpt_set_resolver(). * * @param endpt The SIP endpoint instance. * @param p_resv Pointer to receive the DNS resolver instance. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjsip_endpt_create_resolver(pjsip_endpoint *endpt, pj_dns_resolver **p_resv); /** * Set DNS resolver to be used by the SIP resolver. Application can set * the resolver instance to NULL to disable DNS resolution (perhaps * temporarily). When DNS resolver is disabled, the endpoint will resolve * hostnames with the normal pj_gethostbyname() function. * * @param endpt The SIP endpoint instance. * @param resv The resolver instance to be used by the SIP * endpoint. * * @return PJ_SUCCESS on success, or the appropriate error * code. */ PJ_DECL(pj_status_t) pjsip_endpt_set_resolver(pjsip_endpoint *endpt, pj_dns_resolver *resv); /** * Set the DNS external resolver implementation to use in the SIP resolver. * * Note that naturally when implementing its own resolver, application would not * need the internal resolver, hence this function will also destroy the * PJLIB-UTIL DNS resolver if any (e.g: set using #pjsip_endpt_set_resolver()). * Application that needs it, still be able create its own instance. * * @param res The SIP resolver engine. * @param ext_res The external resolver implementation callback. This argument * can be NULL to reset the whole external implementation. * However, it is prohibited to reset individual callback. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_endpt_set_ext_resolver(pjsip_endpoint *endpt, pjsip_ext_resolver *ext_res); /** * Get the DNS resolver being used by the SIP resolver. * * @param endpt The SIP endpoint instance. * * @return The DNS resolver instance currently being used * by the SIP endpoint. */ PJ_DECL(pj_dns_resolver*) pjsip_endpt_get_resolver(pjsip_endpoint *endpt); /** * Asynchronously resolve a SIP target host or domain according to rule * specified in RFC 3263 (Locating SIP Servers). When the resolving operation * has completed, the callback will be called. * * @param endpt The endpoint instance. * @param pool The pool to allocate resolver job. * @param target The target specification to be resolved. * @param token A user defined token to be passed back to callback function. * @param cb The callback function. */ PJ_DECL(void) pjsip_endpt_resolve( pjsip_endpoint *endpt, pj_pool_t *pool, pjsip_host_info *target, void *token, pjsip_resolver_callback *cb); /** * Get transport manager instance. * * @param endpt The endpoint. * * @return Transport manager instance. */ PJ_DECL(pjsip_tpmgr*) pjsip_endpt_get_tpmgr(pjsip_endpoint *endpt); /** * Get ioqueue instance. * * @param endpt The endpoint. * * @return The ioqueue. */ PJ_DECL(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt); /** * Find a SIP transport suitable for sending SIP message to the specified * address. If transport selector ("sel") is set, then the function will * check if the transport selected is suitable to send requests to the * specified address. * * @see pjsip_tpmgr_acquire_transport * * @param endpt The SIP endpoint instance. * @param type The type of transport to be acquired. * @param remote The remote address to send message to. * @param addr_len Length of the remote address. * @param sel Optional pointer to transport selector instance which is * used to find explicit transport, if required. * @param p_tp Pointer to receive the transport instance, if one is found. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_endpt_acquire_transport( pjsip_endpoint *endpt, pjsip_transport_type_e type, const pj_sockaddr_t *remote, int addr_len, const pjsip_tpselector *sel, pjsip_transport **p_tp); /** * Find a SIP transport suitable for sending SIP message to the specified * address by also considering the outgoing SIP message data. If transport * selector ("sel") is set, then the function will check if the transport * selected is suitable to send requests to the specified address. * * @see pjsip_tpmgr_acquire_transport * * @param endpt The SIP endpoint instance. * @param type The type of transport to be acquired. * @param remote The remote address to send message to. * @param addr_len Length of the remote address. * @param sel Optional pointer to transport selector instance which is * used to find explicit transport, if required. * @param tdata Optional pointer to SIP message data to be sent. * @param p_tp Pointer to receive the transport instance, if one is found. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_endpt_acquire_transport2(pjsip_endpoint *endpt, pjsip_transport_type_e type, const pj_sockaddr_t *remote, int addr_len, const pjsip_tpselector *sel, pjsip_tx_data *tdata, pjsip_transport **p_tp); /***************************************************************************** * * Capabilities Management * * Modules may implement new capabilities to the stack. These capabilities * are indicated by the appropriate SIP header fields, such as Accept, * Accept-Encoding, Accept-Language, Allow, Supported, etc. * * When a module provides new capabilities to the stack, it registers these * capabilities to the endpoint by supplying new tags (strings) to the * appropriate header fields. Application (or other modules) can then query * these header fields to get the list of supported capabilities, and may * include these headers in the outgoing message. ***************************************************************************** */ /** * Get the value of the specified capability header field. * * @param endpt The endpoint. * @param htype The header type to be retrieved, which value may be: * - PJSIP_H_ACCEPT * - PJSIP_H_ALLOW * - PJSIP_H_SUPPORTED * @param hname If htype specifies PJSIP_H_OTHER, then the header name * must be supplied in this argument. Otherwise the value * must be set to NULL. * * @return The appropriate header, or NULL if the header is not * available. */ PJ_DECL(const pjsip_hdr*) pjsip_endpt_get_capability( pjsip_endpoint *endpt, int htype, const pj_str_t *hname); /** * Check if we have the specified capability. * * @param endpt The endpoint. * @param htype The header type to be retrieved, which value may be: * - PJSIP_H_ACCEPT * - PJSIP_H_ALLOW * - PJSIP_H_SUPPORTED * @param hname If htype specifies PJSIP_H_OTHER, then the header name * must be supplied in this argument. Otherwise the value * must be set to NULL. * @param token The capability token to check. For example, if \a htype * is PJSIP_H_ALLOW, then \a token specifies the method * names; if \a htype is PJSIP_H_SUPPORTED, then \a token * specifies the extension names such as "100rel". * * @return PJ_TRUE if the specified capability is supported, * otherwise PJ_FALSE.. */ PJ_DECL(pj_bool_t) pjsip_endpt_has_capability( pjsip_endpoint *endpt, int htype, const pj_str_t *hname, const pj_str_t *token); /** * Add or register new capabilities as indicated by the tags to the * appropriate header fields in the endpoint. * * @param endpt The endpoint. * @param mod The module which registers the capability. * @param htype The header type to be set, which value may be: * - PJSIP_H_ACCEPT * - PJSIP_H_ALLOW * - PJSIP_H_SUPPORTED * @param hname If htype specifies PJSIP_H_OTHER, then the header name * must be supplied in this argument. Otherwise the value * must be set to NULL. * @param count The number of tags in the array. * @param tags Array of tags describing the capabilities or extensions * to be added to the appropriate header. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_endpt_add_capability( pjsip_endpoint *endpt, pjsip_module *mod, int htype, const pj_str_t *hname, unsigned count, const pj_str_t tags[]); /** * Get list of additional headers to be put in outgoing request message. * Currently only Max-Forwards are defined. * * @param e The endpoint. * * @return List of headers. */ PJ_DECL(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *e); /** * Dump endpoint status to the log. This will print the status to the log * with log level 3. * * @param endpt The endpoint. * @param detail If non zero, then it will dump a detailed output. * BEWARE that this option may crash the system because * it tries to access all memory pools. */ PJ_DECL(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail ); /** * Register cleanup function to be called by SIP endpoint when * #pjsip_endpt_destroy() is called. Note that application should not * use or access any endpoint resource (such as pool, ioqueue, timer heap) * from within the callback as such resource may have been released when * the callback function is invoked. * * @param endpt The SIP endpoint. * @param func The function to be registered. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_endpt_atexit(pjsip_endpoint *endpt, pjsip_endpt_exit_callback func); /** * @} */ /** * Log an error. */ PJ_DECL(void) pjsip_endpt_log_error( pjsip_endpoint *endpt, const char *sender, pj_status_t error_code, const char *format, ... ); #define PJSIP_ENDPT_LOG_ERROR(expr) \ pjsip_endpt_log_error expr #define PJSIP_ENDPT_TRACE(tracing,expr) \ do { \ if ((tracing)) \ PJ_LOG(4,expr); \ } while (0) /* * Internal functions. */ /* * Receive transaction events from transactions and put in the event queue * to be processed later. */ void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt ); PJ_END_DECL #endif /* __PJSIP_SIP_ENDPOINT_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_errno.h ================================================ /* $Id: sip_errno.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_ERRNO_H__ #define __PJSIP_SIP_ERRNO_H__ /** * @file sip_errno.h * @brief PJSIP Specific Error Code */ #include PJ_BEGIN_DECL /** * @defgroup PJSIP_CORE_ERRNO PJSIP Specific Error Code * @ingroup PJSIP_BASE * @brief PJSIP specific error constants. * @{ */ /* * PJSIP error codes occupies 170000 - 219000, and mapped as follows: * - 170100 - 170799: mapped to SIP status code in response msg. * - 171000 - 171999: mapped to errors generated from PJSIP core. */ /** * Start of error code relative to PJ_ERRNO_START_USER. */ #define PJSIP_ERRNO_START (PJ_ERRNO_START_USER) /** * Create error value from SIP status code. * @param code SIP status code. * @return Error code in pj_status_t namespace. */ #define PJSIP_ERRNO_FROM_SIP_STATUS(code) (PJSIP_ERRNO_START+code) /** * Get SIP status code from error value. * If conversion to SIP status code is not available, a SIP status code * 599 will be returned. * * @param status Error code in pj_status_t namespace. * @return SIP status code. */ #define PJSIP_ERRNO_TO_SIP_STATUS(status) \ ((status>=PJSIP_ERRNO_FROM_SIP_STATUS(100) && \ status * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_EVENT_H__ #define __PJSIP_SIP_EVENT_H__ /** * @file sip_event.h * @brief SIP Event */ PJ_BEGIN_DECL /** * @defgroup PJSIP_EVENT Event * @ingroup PJSIP_CORE_CORE * @brief Representation of events as they are distributed among modules. * @{ */ #include #include /** * Event IDs. */ typedef enum pjsip_event_id_e { /** Unidentified event. */ PJSIP_EVENT_UNKNOWN, /** Timer event, normally only used internally in transaction. */ PJSIP_EVENT_TIMER, /** Message transmission event. */ PJSIP_EVENT_TX_MSG, /** Message received event. */ PJSIP_EVENT_RX_MSG, /** Transport error event. */ PJSIP_EVENT_TRANSPORT_ERROR, /** Transaction state changed event. */ PJSIP_EVENT_TSX_STATE, /** Indicates that the event was triggered by user action. */ PJSIP_EVENT_USER } pjsip_event_id_e; /** * This structure describe event descriptor to fully identify a SIP event. * * Events are the only way for a lower layer object to inform something * to higher layer objects. Normally this is achieved by means of callback, * i.e. the higher layer objects register a callback to handle the event on * the lower layer objects. * * This event descriptor is used for example by transactions, to inform * endpoint about events, and by transports, to inform endpoint about * unexpected transport error. */ struct pjsip_event { /** This is necessary so that we can put events as a list. */ PJ_DECL_LIST_MEMBER(struct pjsip_event); /** The event type, can be any value of \b pjsip_event_id_e. */ pjsip_event_id_e type; /** * The event body as union, which fields depends on the event type. * By convention, the first member of each struct in the union must be * the pointer which is relevant to the event. */ union { /** Timer event. */ struct { pj_timer_entry *entry; /**< The timer entry. */ } timer; /** Transaction state has changed event. */ struct { union { pjsip_rx_data *rdata; /**< The incoming message. */ pjsip_tx_data *tdata; /**< The outgoing message. */ pj_timer_entry *timer; /**< The timer. */ pj_status_t status;/**< Transport error status. */ void *data; /**< Generic data. */ } src; pjsip_transaction *tsx; /**< The transaction. */ int prev_state; /**< Previous state. */ pjsip_event_id_e type; /**< Type of event source: * - PJSIP_EVENT_TX_MSG * - PJSIP_EVENT_RX_MSG, * - PJSIP_EVENT_TRANSPORT_ERROR * - PJSIP_EVENT_TIMER * - PJSIP_EVENT_USER */ } tsx_state; /** Message transmission event. */ struct { pjsip_tx_data *tdata; /**< The transmit data buffer. */ } tx_msg; /** Transmission error event. */ struct { pjsip_tx_data *tdata; /**< The transmit data. */ pjsip_transaction *tsx; /**< The transaction. */ } tx_error; /** Message arrival event. */ struct { pjsip_rx_data *rdata; /**< The receive data buffer. */ } rx_msg; /** User event. */ struct { void *user1; /**< User data 1. */ void *user2; /**< User data 2. */ void *user3; /**< User data 3. */ void *user4; /**< User data 4. */ } user; } body; }; /** * Init timer event. */ #define PJSIP_EVENT_INIT_TIMER(event,pentry) \ do { \ (event).type = PJSIP_EVENT_TIMER; \ (event).body.timer.entry = pentry; \ } while (0) /** * Init tsx state event. */ #define PJSIP_EVENT_INIT_TSX_STATE(event,ptsx,ptype,pdata,prev) \ do { \ (event).type = PJSIP_EVENT_TSX_STATE; \ (event).body.tsx_state.tsx = ptsx; \ (event).body.tsx_state.type = ptype; \ (event).body.tsx_state.src.data = pdata; \ (event).body.tsx_state.prev_state = prev; \ } while (0) /** * Init tx msg event. */ #define PJSIP_EVENT_INIT_TX_MSG(event,ptdata) \ do { \ (event).type = PJSIP_EVENT_TX_MSG; \ (event).body.tx_msg.tdata = ptdata; \ } while (0) /** * Init rx msg event. */ #define PJSIP_EVENT_INIT_RX_MSG(event,prdata) \ do { \ (event).type = PJSIP_EVENT_RX_MSG; \ (event).body.rx_msg.rdata = prdata; \ } while (0) /** * Init transport error event. */ #define PJSIP_EVENT_INIT_TRANSPORT_ERROR(event,ptsx,ptdata) \ do { \ (event).type = PJSIP_EVENT_TRANSPORT_ERROR; \ (event).body.tx_error.tsx = ptsx; \ (event).body.tx_error.tdata = ptdata; \ } while (0) /** * Init user event. */ #define PJSIP_EVENT_INIT_USER(event,u1,u2,u3,u4) \ do { \ (event).type = PJSIP_EVENT_USER; \ (event).body.user.user1 = (void*)u1; \ (event).body.user.user2 = (void*)u2; \ (event).body.user.user3 = (void*)u3; \ (event).body.user.user4 = (void*)u4; \ } while (0) /** * Get the event string from the event ID. * @param e the event ID. * @note defined in sip_util.c */ PJ_DECL(const char *) pjsip_event_str(pjsip_event_id_e e); /** * @} */ PJ_END_DECL #endif /* __PJSIP_SIP_EVENT_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_module.h ================================================ /* $Id: sip_module.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_MODULE_H__ #define __PJSIP_SIP_MODULE_H__ /** * @file sip_module.h * @brief Module helpers */ #include #include PJ_BEGIN_DECL /** * @defgroup PJSIP_MOD Modules * @ingroup PJSIP_CORE_CORE * @brief Modules are the primary means to extend PJSIP! * @{ * Modules are the primary means to extend PJSIP. Without modules, PJSIP * would not know how to handle messages, and will simply discard all * incoming messages. * * Modules are registered by creating and initializing #pjsip_module * structure, and register the structure to PJSIP with * #pjsip_endpt_register_module(). * * The PJSIP Developer's Guide * has a thorough discussion on this subject, and readers are encouraged * to read the document for more information. */ /** * The declaration for SIP module. This structure would be passed to * #pjsip_endpt_register_module() to register the module to PJSIP. */ struct pjsip_module { /** To allow chaining of modules in the endpoint. */ PJ_DECL_LIST_MEMBER(struct pjsip_module); /** * Module name to identify the module. * * This field MUST be initialized before registering the module. */ pj_str_t name; /** * Module ID. Application must initialize this field with -1 before * registering the module to PJSIP. After the module is registered, * this field will contain a unique ID to identify the module. */ int id; /** * Integer number to identify module initialization and start order with * regard to other modules. Higher number will make the module gets * initialized later. * * This field MUST be initialized before registering the module. */ int priority; /** * Optional function to be called to initialize the module. This function * will be called by endpoint during module registration. If the value * is NULL, then it's equal to returning PJ_SUCCESS. * * @param endpt The endpoint instance. * @return Module should return PJ_SUCCESS to indicate success. */ pj_status_t (*load)(pjsip_endpoint *endpt); /** * Optional function to be called to start the module. This function * will be called by endpoint during module registration. If the value * is NULL, then it's equal to returning PJ_SUCCESS. * * @return Module should return zero to indicate success. */ pj_status_t (*start)(void); /** * Optional function to be called to deinitialize the module before * it is unloaded. This function will be called by endpoint during * module unregistration. If the value is NULL, then it's equal to * returning PJ_SUCCESS. * * @return Module should return PJ_SUCCESS to indicate success. */ pj_status_t (*stop)(void); /** * Optional function to be called to deinitialize the module before * it is unloaded. This function will be called by endpoint during * module unregistration. If the value is NULL, then it's equal to * returning PJ_SUCCESS. * * @param mod The module. * * @return Module should return PJ_SUCCESS to indicate success. */ pj_status_t (*unload)(void); /** * Optional function to be called to process incoming request message. * * @param rdata The incoming message. * * @return Module should return PJ_TRUE if it handles the request, * or otherwise it should return PJ_FALSE to allow other * modules to handle the request. */ pj_bool_t (*on_rx_request)(pjsip_rx_data *rdata); /** * Optional function to be called to process incoming response message. * * @param rdata The incoming message. * * @return Module should return PJ_TRUE if it handles the * response, or otherwise it should return PJ_FALSE to * allow other modules to handle the response. */ pj_bool_t (*on_rx_response)(pjsip_rx_data *rdata); /** * Optional function to be called when transport layer is about to * transmit outgoing request message. * * @param tdata The outgoing request message. * * @return Module should return PJ_SUCCESS in all cases. * If non-zero (or PJ_FALSE) is returned, the message * will not be sent. */ pj_status_t (*on_tx_request)(pjsip_tx_data *tdata); /** * Optional function to be called when transport layer is about to * transmit outgoing response message. * * @param tdata The outgoing response message. * * @return Module should return PJ_SUCCESS in all cases. * If non-zero (or PJ_FALSE) is returned, the message * will not be sent. */ pj_status_t (*on_tx_response)(pjsip_tx_data *tdata); /** * Optional function to be called when this module is acting as * transaction user for the specified transaction, when the * transaction's state has changed. * * @param tsx The transaction. * @param event The event which has caused the transaction state * to change. */ void (*on_tsx_state)(pjsip_transaction *tsx, pjsip_event *event); }; /** * Module priority guidelines. */ enum pjsip_module_priority { /** * This is the priority used by transport layer. */ PJSIP_MOD_PRIORITY_TRANSPORT_LAYER = 8, /** * This is the priority used by transaction layer. */ PJSIP_MOD_PRIORITY_TSX_LAYER = 16, /** * This is the priority used by the user agent and proxy layer. */ PJSIP_MOD_PRIORITY_UA_PROXY_LAYER = 32, /** * This is the priority used by the dialog usages. */ PJSIP_MOD_PRIORITY_DIALOG_USAGE = 48, /** * This is the recommended priority to be used by applications. */ PJSIP_MOD_PRIORITY_APPLICATION = 64 }; /** * @} */ PJ_END_DECL #endif /* __PJSIP_SIP_MODULE_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_msg.h ================================================ /* $Id: sip_msg.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_MSG_H__ #define __PJSIP_SIP_MSG_H__ /** * @file pjsip/sip_msg.h * @brief SIP Message Structure. */ #include #include #include PJ_BEGIN_DECL /** * @defgroup PJSIP_MSG Messaging Elements * @ingroup PJSIP_CORE * @brief Various SIP message elements such as methods, headers, URIs, etc. * @{ */ /* **************************************************************************/ /** * @defgroup PJSIP_MSG_METHOD Methods * @brief Method names and manipulation. * @ingroup PJSIP_MSG * @{ */ /** * This enumeration declares SIP methods as described by RFC3261. Additional * methods do exist, and they are described by corresponding RFCs for the SIP * extentensions. Since they won't alter the characteristic of the processing * of the message, they don't need to be explicitly mentioned here. */ typedef enum pjsip_method_e { PJSIP_INVITE_METHOD, /**< INVITE method, for establishing dialogs. */ PJSIP_CANCEL_METHOD, /**< CANCEL method, for cancelling request. */ PJSIP_ACK_METHOD, /**< ACK method. */ PJSIP_BYE_METHOD, /**< BYE method, for terminating dialog. */ PJSIP_REGISTER_METHOD, /**< REGISTER method. */ PJSIP_OPTIONS_METHOD, /**< OPTIONS method. */ PJSIP_OTHER_METHOD /**< Other method. */ } pjsip_method_e; /** * This structure represents a SIP method. * Application must always use either #pjsip_method_init or #pjsip_method_set * to make sure that method name is initialized correctly. This way, the name * member will always contain a valid method string regardless whether the ID * is recognized or not. */ struct pjsip_method { pjsip_method_e id; /**< Method ID, from \a pjsip_method_e. */ pj_str_t name; /**< Method name, which will always contain the method string. */ }; /* * For convenience, standard method structures are defined in the library. */ /** INVITE method constant. @see pjsip_get_invite_method() */ PJ_DECL_DATA(const pjsip_method) pjsip_invite_method; /** CANCEL method constant. @see pjsip_get_cancel_method() */ PJ_DECL_DATA(const pjsip_method) pjsip_cancel_method; /** ACK method constant. @see pjsip_get_ack_method() */ PJ_DECL_DATA(const pjsip_method) pjsip_ack_method; /** BYE method constant. @see pjsip_get_bye_method() */ PJ_DECL_DATA(const pjsip_method) pjsip_bye_method; /** REGISTER method constant. @see pjsip_get_register_method() */ PJ_DECL_DATA(const pjsip_method) pjsip_register_method; /** OPTIONS method constant. @see pjsip_get_options_method() */ PJ_DECL_DATA(const pjsip_method) pjsip_options_method; /* * Accessor functions for standard SIP methods. */ /** Get INVITE method constant. */ PJ_DECL(const pjsip_method*) pjsip_get_invite_method(void); /** Get CANCEL method constant. */ PJ_DECL(const pjsip_method*) pjsip_get_cancel_method(void); /** Get ACK method constant. */ PJ_DECL(const pjsip_method*) pjsip_get_ack_method(void); /** Get BYE method constant. */ PJ_DECL(const pjsip_method*) pjsip_get_bye_method(void); /** Get REGISTER method constant.*/ PJ_DECL(const pjsip_method*) pjsip_get_register_method(void); /** Get OPTIONS method constant. */ PJ_DECL(const pjsip_method*) pjsip_get_options_method(void); /* * Accessor functions */ /** * Initialize the method structure from a string. * This function will check whether the method is a known method then set * both the id and name accordingly. * * @param m The method to initialize. * @param pool Pool where memory allocation will be allocated from, if required. * @param str The method string. */ PJ_DECL(void) pjsip_method_init( pjsip_method *m, pj_pool_t *pool, const pj_str_t *str); /** * Initialize the method structure from a string, without cloning the string. * See #pjsip_method_init. * * @param m The method structure to be initialized. * @param str The method string. */ PJ_DECL(void) pjsip_method_init_np( pjsip_method *m, pj_str_t *str); /** * Set the method with the predefined method ID. * This function will also set the name member of the structure to the correct * string according to the method. * * @param m The method structure. * @param id The method ID. */ PJ_DECL(void) pjsip_method_set( pjsip_method *m, pjsip_method_e id ); /** * Copy one method structure to another. If the method is of the known methods, * then memory allocation is not required. * * @param pool Pool to allocate memory from, if required. * @param method The destination method to copy to. * @param rhs The source method to copy from. */ PJ_DECL(void) pjsip_method_copy( pj_pool_t *pool, pjsip_method *method, const pjsip_method *rhs ); /** * Compare one method with another, and conveniently determine whether the * first method is equal, less than, or greater than the second method. * * @param m1 The first method. * @param m2 The second method. * * @return Zero if equal, otherwise will return -1 if less or +1 if greater. */ PJ_DECL(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2); /** * @} */ /* **************************************************************************/ /** * @defgroup PJSIP_MSG_HDR Header Fields * @brief Declarations for various SIP header fields. * @ingroup PJSIP_MSG * @{ */ /** * Header types, as defined by RFC3261. */ typedef enum pjsip_hdr_e { /* * These are the headers documented in RFC3261. Headers not documented * there must have type PJSIP_H_OTHER, and the header type itself is * recorded in the header name string. * * DO NOT CHANGE THE VALUE/ORDER OF THE HEADER IDs!!!. */ PJSIP_H_ACCEPT, PJSIP_H_ACCEPT_ENCODING_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_ACCEPT_LANGUAGE_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_ALERT_INFO_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_ALLOW, PJSIP_H_AUTHENTICATION_INFO_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_AUTHORIZATION, PJSIP_H_CALL_ID, PJSIP_H_CALL_INFO_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_CONTACT, PJSIP_H_CONTENT_DISPOSITION_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_CONTENT_ENCODING_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_CONTENT_LANGUAGE_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_CONTENT_LENGTH, PJSIP_H_CONTENT_TYPE, PJSIP_H_CSEQ, PJSIP_H_DATE_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_ERROR_INFO_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_EXPIRES, PJSIP_H_FROM, PJSIP_H_IN_REPLY_TO_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_MAX_FORWARDS, PJSIP_H_MIME_VERSION_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_MIN_EXPIRES, PJSIP_H_ORGANIZATION_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_PRIORITY_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_PROXY_AUTHENTICATE, PJSIP_H_PROXY_AUTHORIZATION, PJSIP_H_PROXY_REQUIRE_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_RECORD_ROUTE, PJSIP_H_REPLY_TO_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_REQUIRE, PJSIP_H_RETRY_AFTER, PJSIP_H_ROUTE, PJSIP_H_SERVER_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_SUBJECT_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_SUPPORTED, PJSIP_H_TIMESTAMP_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_TO, PJSIP_H_UNSUPPORTED, PJSIP_H_USER_AGENT_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_VIA, PJSIP_H_WARNING_UNIMP, /* N/A, use pjsip_generic_string_hdr */ PJSIP_H_WWW_AUTHENTICATE, PJSIP_H_OTHER } pjsip_hdr_e; /** * This structure provides the pointer to basic functions that are needed * for generic header operations. All header fields will have pointer to * this structure, so that they can be manipulated uniformly. */ typedef struct pjsip_hdr_vptr { /** * Function to clone the header. * * @param pool Memory pool to allocate the new header. * @param hdr Header to clone. * * @return A new instance of the header. */ void *(*clone)(pj_pool_t *pool, const void *hdr); /** * Pointer to function to shallow clone the header. * Shallow cloning will just make a memory copy of the original header, * thus all pointers in original header will be kept intact. Because the * function does not need to perform deep copy, the operation should be * faster, but the application must make sure that the original header * is still valid throughout the lifetime of new header. * * @param pool Memory pool to allocate the new header. * @param hdr The header to clone. */ void *(*shallow_clone)(pj_pool_t *pool, const void *hdr); /** Pointer to function to print the header to the specified buffer. * Returns the length of string written, or -1 if the remaining buffer * is not enough to hold the header. * * @param hdr The header to print. * @param buf The buffer. * @param len The size of the buffer. * * @return The size copied to buffer, or -1 if there's not enough space. */ int (*print_on)(void *hdr, char *buf, pj_size_t len); } pjsip_hdr_vptr; /** * Generic fields for all SIP headers are declared using this macro, to make * sure that all headers will have exactly the same layout in their start of * the storage. This behaves like C++ inheritance actually. */ #define PJSIP_DECL_HDR_MEMBER(hdr) \ /** List members. */ \ PJ_DECL_LIST_MEMBER(hdr); \ /** Header type */ \ pjsip_hdr_e type; \ /** Header name. */ \ pj_str_t name; \ /** Header short name version. */ \ pj_str_t sname; \ /** Virtual function table. */ \ pjsip_hdr_vptr *vptr /** * Generic SIP header structure, for generic manipulation for headers in the * message. All header fields can be typecasted to this type. */ struct pjsip_hdr { PJSIP_DECL_HDR_MEMBER(struct pjsip_hdr); }; /** * This generic function will clone any header, by calling "clone" function * in header's virtual function table. * * @param pool The pool to allocate memory from. * @param hdr The header to clone. * * @return A new instance copied from the original header. */ PJ_DECL(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr ); /** * This generic function will clone any header, by calling "shallow_clone" * function in header's virtual function table. * * @param pool The pool to allocate memory from. * @param hdr The header to clone. * * @return A new instance copied from the original header. */ PJ_DECL(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr ); /** * This generic function will print any header, by calling "print" * function in header's virtual function table. * * @param hdr The header to print. * @param buf The buffer. * @param len The size of the buffer. * * @return The size copied to buffer, or -1 if there's not enough space. */ PJ_DECL(int) pjsip_hdr_print_on( void *hdr, char *buf, pj_size_t len); /** * @} */ /* **************************************************************************/ /** * @defgroup PJSIP_MSG_LINE Request and Status Line. * @brief Request and status line structures and manipulation. * @ingroup PJSIP_MSG * @{ */ /** * This structure describes SIP request line. */ typedef struct pjsip_request_line { pjsip_method method; /**< Method for this request line. */ pjsip_uri *uri; /**< URI for this request line. */ } pjsip_request_line; /** * This structure describes SIP status line. */ typedef struct pjsip_status_line { int code; /**< Status code. */ pj_str_t reason; /**< Reason string. */ } pjsip_status_line; /** * This enumeration lists standard SIP status codes according to RFC 3261. * In addition, it also declares new status class 7xx for errors generated * by the stack. This status class however should not get transmitted on the * wire. */ typedef enum pjsip_status_code { PJSIP_SC_TRYING = 100, PJSIP_SC_RINGING = 180, PJSIP_SC_CALL_BEING_FORWARDED = 181, PJSIP_SC_QUEUED = 182, PJSIP_SC_PROGRESS = 183, PJSIP_SC_OK = 200, PJSIP_SC_ACCEPTED = 202, PJSIP_SC_MULTIPLE_CHOICES = 300, PJSIP_SC_MOVED_PERMANENTLY = 301, PJSIP_SC_MOVED_TEMPORARILY = 302, PJSIP_SC_USE_PROXY = 305, PJSIP_SC_ALTERNATIVE_SERVICE = 380, PJSIP_SC_BAD_REQUEST = 400, PJSIP_SC_UNAUTHORIZED = 401, PJSIP_SC_PAYMENT_REQUIRED = 402, PJSIP_SC_FORBIDDEN = 403, PJSIP_SC_NOT_FOUND = 404, PJSIP_SC_METHOD_NOT_ALLOWED = 405, PJSIP_SC_NOT_ACCEPTABLE = 406, PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED = 407, PJSIP_SC_REQUEST_TIMEOUT = 408, PJSIP_SC_GONE = 410, PJSIP_SC_REQUEST_ENTITY_TOO_LARGE = 413, PJSIP_SC_REQUEST_URI_TOO_LONG = 414, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE = 415, PJSIP_SC_UNSUPPORTED_URI_SCHEME = 416, PJSIP_SC_BAD_EXTENSION = 420, PJSIP_SC_EXTENSION_REQUIRED = 421, PJSIP_SC_SESSION_TIMER_TOO_SMALL = 422, PJSIP_SC_INTERVAL_TOO_BRIEF = 423, PJSIP_SC_TEMPORARILY_UNAVAILABLE = 480, PJSIP_SC_CALL_TSX_DOES_NOT_EXIST = 481, PJSIP_SC_LOOP_DETECTED = 482, PJSIP_SC_TOO_MANY_HOPS = 483, PJSIP_SC_ADDRESS_INCOMPLETE = 484, PJSIP_AC_AMBIGUOUS = 485, PJSIP_SC_BUSY_HERE = 486, PJSIP_SC_REQUEST_TERMINATED = 487, PJSIP_SC_NOT_ACCEPTABLE_HERE = 488, PJSIP_SC_BAD_EVENT = 489, PJSIP_SC_REQUEST_UPDATED = 490, PJSIP_SC_REQUEST_PENDING = 491, PJSIP_SC_UNDECIPHERABLE = 493, PJSIP_SC_INTERNAL_SERVER_ERROR = 500, PJSIP_SC_NOT_IMPLEMENTED = 501, PJSIP_SC_BAD_GATEWAY = 502, PJSIP_SC_SERVICE_UNAVAILABLE = 503, PJSIP_SC_SERVER_TIMEOUT = 504, PJSIP_SC_VERSION_NOT_SUPPORTED = 505, PJSIP_SC_MESSAGE_TOO_LARGE = 513, PJSIP_SC_PRECONDITION_FAILURE = 580, PJSIP_SC_BUSY_EVERYWHERE = 600, PJSIP_SC_DECLINE = 603, PJSIP_SC_DOES_NOT_EXIST_ANYWHERE = 604, PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE = 606, PJSIP_SC_TSX_TIMEOUT = PJSIP_SC_REQUEST_TIMEOUT, /*PJSIP_SC_TSX_RESOLVE_ERROR = 702,*/ PJSIP_SC_TSX_TRANSPORT_ERROR = PJSIP_SC_SERVICE_UNAVAILABLE, /* This is not an actual status code, but rather a constant * to force GCC to use 32bit to represent this enum, since * we have a code in PJSUA-LIB that assigns an integer * to this enum (see pjsua_acc_get_info() function). */ PJSIP_SC__force_32bit = 0x7FFFFFFF } pjsip_status_code; /** * Get the default status text for the status code. * * @param status_code SIP Status Code * * @return textual message for the status code. */ PJ_DECL(const pj_str_t*) pjsip_get_status_text(int status_code); /** * This macro returns non-zero (TRUE) if the specified status_code is * in the same class as the code_class. * * @param status_code The status code. * @param code_class The status code in the class (for example 100, 200). */ #define PJSIP_IS_STATUS_IN_CLASS(status_code, code_class) \ (status_code/100 == code_class/100) /** * @} */ /* **************************************************************************/ /** * @addtogroup PJSIP_MSG_MEDIA Media/MIME Type * @brief Media/MIME type declaration and manipulations. * @ingroup PJSIP_MSG * @{ */ /** * This structure describes SIP media type, as used for example in * Accept and Content-Type header.. */ typedef struct pjsip_media_type { pj_str_t type; /**< Media type. */ pj_str_t subtype; /**< Media subtype. */ pjsip_param param; /**< Media type parameters */ } pjsip_media_type; /** * Initialize the media type with the specified type and subtype string. * * @param mt The media type. * @param type Optionally specify the media type. * @param subtype Optionally specify the media subtype. */ PJ_DECL(void) pjsip_media_type_init(pjsip_media_type *mt, pj_str_t *type, pj_str_t *subtype); /** * Initialize the media type with the specified type and subtype string. * * @param mt The media type. * @param type Optionally specify the media type. * @param subtype Optionally specify the media subtype. */ PJ_DECL(void) pjsip_media_type_init2(pjsip_media_type *mt, char *type, char *subtype); /** * Compare two media types. * * @param mt1 The first media type. * @param mt2 The second media type. * @param cmp_param Specify how to compare the media type parameters: * - 0: do not compare parameters * - 1: compare parameters but ignore parameters that * only appear in one of the media type. * - 2: compare the parameters. * * @return Zero if both media types are equal, -1 if mt1 < mt2, * 1 if mt1 > mt2. */ PJ_DECL(int) pjsip_media_type_cmp(const pjsip_media_type *mt1, const pjsip_media_type *mt2, int cmp_param); /** * Copy SIP media type to another. * * @param pool Pool to duplicate strings. * @param dst Destination structure. * @param src Source structure. */ PJ_DECL(void) pjsip_media_type_cp(pj_pool_t *pool, pjsip_media_type *dst, const pjsip_media_type *src); /** * Print media type to the specified buffer. * * @param buf Destination buffer. * @param len Length of the buffer. * @param mt The media type to be printed. * * @return The number of characters printed to the buffer, or -1 * if there's not enough space in the buffer. */ PJ_DECL(int) pjsip_media_type_print(char *buf, unsigned len, const pjsip_media_type *mt); /** * @} */ /* **************************************************************************/ /** * @addtogroup PJSIP_MSG_BODY Message Body * @brief SIP message body structures and manipulation. * @ingroup PJSIP_MSG * @{ */ /** * Generic abstraction to message body. * When an incoming message is parsed (pjsip_parse_msg()), the parser fills in * all members with the appropriate value. The 'data' and 'len' member will * describe portion of incoming packet which denotes the message body. * When application needs to attach message body to outgoing SIP message, it * must fill in all members of this structure. */ struct pjsip_msg_body { /** MIME content type. * For incoming messages, the parser will fill in this member with the * content type found in Content-Type header. * * For outgoing messages, application may fill in this member with * appropriate value, because the stack will generate Content-Type header * based on the value specified here. * * If the content_type is empty, no Content-Type AND Content-Length header * will be added to the message. The stack assumes that application adds * these headers themselves. */ pjsip_media_type content_type; /** Pointer to buffer which holds the message body data. * For incoming messages, the parser will fill in this member with the * pointer to the body string. * * When sending outgoing message, this member doesn't need to point to the * actual message body string. It can be assigned with arbitrary pointer, * because the value will only need to be understood by the print_body() * function. The stack itself will not try to interpret this value, but * instead will always call the print_body() whenever it needs to get the * actual body string. */ void *data; /** The length of the data. * For incoming messages, the parser will fill in this member with the * actual length of message body. * * When sending outgoing message, again just like the "data" member, the * "len" member doesn't need to point to the actual length of the body * string. */ unsigned len; /** Pointer to function to print this message body. * Application must set a proper function here when sending outgoing * message. * * @param msg_body This structure itself. * @param buf The buffer. * @param size The buffer size. * * @return The length of the string printed, or -1 if there is * not enough space in the buffer to print the whole * message body. */ int (*print_body)(struct pjsip_msg_body *msg_body, char *buf, pj_size_t size); /** Clone the data part only of this message body. Note that this only * duplicates the data part of the body instead of the whole message * body. If application wants to duplicate the entire message body * structure, it must call #pjsip_msg_body_clone(). * * @param pool Pool used to clone the data. * @param data The data inside message body, to be cloned. * @param len The length of the data. * * @return New data duplicated from the original data. */ void* (*clone_data)(pj_pool_t *pool, const void *data, unsigned len); }; /** * General purpose function to print a SIP message body. * Uses the appropriate internal functions to print the string representation * of a SIP message body. It sets the output buffer to a statically allocated * buffer, so the caller is responsible to copy it. * * @param msg_body The message body. * @param buf Pointer to get the result buffer (statically allocated). * @param size The size of the buffer. * * @return The length copied to the buffer, or -1. */ PJ_DECL(int) pjsip_print_body( pjsip_msg_body *msg_body, char **buf, int *len); /** * General purpose function to textual data in a SIP body. Attach this function * in a SIP message body only if the data in pjsip_msg_body is a textual * message ready to be embedded in a SIP message. If the data in the message * body is not a textual body, then application must supply a custom function * to print that body. * * @param msg_body The message body. * @param buf Buffer to copy the message body to. * @param size The size of the buffer. * * @return The length copied to the buffer, or -1. */ PJ_DECL(int) pjsip_print_text_body( pjsip_msg_body *msg_body, char *buf, pj_size_t size); /** * General purpose function to clone textual data in a SIP body. Attach this * function as "clone_data" member of the SIP body only if the data type * is a text (i.e. C string, not pj_str_t), and the length indicates the * length of the text. * * @param pool Pool used to clone the data. * @param data Textual data. * @param len The length of the string. * * @return New text duplicated from the original text. */ PJ_DECL(void*) pjsip_clone_text_data( pj_pool_t *pool, const void *data, unsigned len); /** * Clone the message body in src_body to the dst_body. This will duplicate * the contents of the message body using the \a clone_data member of the * source message body. * * @param pool Pool to use to duplicate the message body. * @param dst_body Destination message body. * @param src_body Source message body to duplicate. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_msg_body_copy( pj_pool_t *pool, pjsip_msg_body *dst_body, const pjsip_msg_body *src_body ); /** * Create cloned message body. This will duplicate the contents of the message * body using the \a clone_data member of the source message body. * * @param pool Pool to use to duplicate the message body. * @param body Source message body to duplicate. * * @return The cloned message body on successfull. */ PJ_DECL(pjsip_msg_body*) pjsip_msg_body_clone( pj_pool_t *pool, const pjsip_msg_body *body ); /** * Create a text message body. Use this function to create message body when * the content is a simple text. For non-text message body (e.g. * pjmedia_sdp_session or pj_xml_node), application must construct the message * manually. * * @param pool Pool to allocate message body and its contents. * @param type MIME type (e.g. "text"). * @param subtype MIME subtype (e.g. "plain"). * @param text The text content to be put in the message body. * * @return A new message body with the specified Content-Type and * text. */ PJ_DECL(pjsip_msg_body*) pjsip_msg_body_create( pj_pool_t *pool, const pj_str_t *type, const pj_str_t *subtype, const pj_str_t *text ); /** * @} */ /* **************************************************************************/ /** * @defgroup PJSIP_MSG_MSG Message Structure * @brief SIP message (request and response) structure and operations. * @ingroup PJSIP_MSG * @{ */ /** * Message type (request or response). */ typedef enum pjsip_msg_type_e { PJSIP_REQUEST_MSG, /**< Indicates request message. */ PJSIP_RESPONSE_MSG /**< Indicates response message. */ } pjsip_msg_type_e; /** * This structure describes a SIP message. */ struct pjsip_msg { /** Message type (ie request or response). */ pjsip_msg_type_e type; /** The first line of the message can be either request line for request * messages, or status line for response messages. It is represented here * as a union. */ union { /** Request Line. */ struct pjsip_request_line req; /** Status Line. */ struct pjsip_status_line status; } line; /** List of message headers. */ pjsip_hdr hdr; /** Pointer to message body, or NULL if no message body is attached to * this mesage. */ pjsip_msg_body *body; }; /** * Create new request or response message. * * @param pool The pool. * @param type Message type. * @return New message, or THROW exception if failed. */ PJ_DECL(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type); /** * Perform a deep clone of a SIP message. * * @param pool The pool for creating the new message. * @param msg The message to be duplicated. * * @return New message, which is duplicated from the original * message. */ PJ_DECL(pjsip_msg*) pjsip_msg_clone( pj_pool_t *pool, const pjsip_msg *msg); /** * Find a header in the message by the header type. * * @param msg The message. * @param type The header type to find. * @param start The first header field where the search should begin. * If NULL is specified, then the search will begin from the * first header, otherwise the search will begin at the * specified header. * * @return The header field, or NULL if no header with the specified * type is found. */ PJ_DECL(void*) pjsip_msg_find_hdr( const pjsip_msg *msg, pjsip_hdr_e type, const void *start); /** * Find a header in the message by its name. * * @param msg The message. * @param name The header name to find. * @param start The first header field where the search should begin. * If NULL is specified, then the search will begin from the * first header, otherwise the search will begin at the * specified header. * * @return The header field, or NULL if no header with the specified * type is found. */ PJ_DECL(void*) pjsip_msg_find_hdr_by_name( const pjsip_msg *msg, const pj_str_t *name, const void *start); /** * Find a header in the message by its name and short name version. * * @param msg The message. * @param name The header name to find. * @param sname The short name version of the header name. * @param start The first header field where the search should begin. * If NULL is specified, then the search will begin from the * first header, otherwise the search will begin at the * specified header. * * @return The header field, or NULL if no header with the specified * type is found. */ PJ_DECL(void*) pjsip_msg_find_hdr_by_names(const pjsip_msg *msg, const pj_str_t *name, const pj_str_t *sname, const void *start); /** * Find and remove a header in the message. * * @param msg The message. * @param hdr The header type to find. * @param start The first header field where the search should begin, * or NULL to search from the first header in the message. * * @return The header field, or NULL if not found. */ PJ_DECL(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg, pjsip_hdr_e hdr, void *start); /** * Find and remove a header in the message. * * @param msg The message. * @param name The header name to find. * @param start The first header field where the search should begin, * or NULL to search from the first header in the message. * * @return The header field, or NULL if not found. */ PJ_DECL(void*) pjsip_msg_find_remove_hdr_by_name( pjsip_msg *msg, pj_str_t *name, void *start); /** * Add a header to the message, putting it last in the header list. * * @param msg The message. * @param hdr The header to add. * * @bug Once the header is put in a list (or message), it can not be put in * other list (or message). Otherwise Real Bad Thing will happen. */ PJ_INLINE(void) pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr ) { pj_list_insert_before(&msg->hdr, hdr); } /** * Add header field to the message, putting it in the front of the header list. * * @param msg The message. * @param hdr The header to add. * * @bug Once the header is put in a list (or message), it can not be put in * other list (or message). Otherwise Real Bad Thing will happen. */ PJ_INLINE(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr ) { pj_list_insert_after(&msg->hdr, hdr); } /** * Print the message to the specified buffer. * * @param msg The message to print. * @param buf The buffer * @param size The size of the buffer. * * @return The length of the printed characters (in bytes), or NEGATIVE * value if the message is too large for the specified buffer. */ PJ_DECL(pj_ssize_t) pjsip_msg_print(const pjsip_msg *msg, char *buf, pj_size_t size); /* * Some usefull macros to find common headers. */ /** * Find Call-ID header. * * @param msg The message. * @return Call-ID header instance. */ #define PJSIP_MSG_CID_HDR(msg) \ ((pjsip_cid_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CALL_ID, NULL)) /** * Find CSeq header. * * @param msg The message. * @return CSeq header instance. */ #define PJSIP_MSG_CSEQ_HDR(msg) \ ((pjsip_cseq_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL)) /** * Find From header. * * @param msg The message. * @return From header instance. */ #define PJSIP_MSG_FROM_HDR(msg) \ ((pjsip_from_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_FROM, NULL)) /** * Find To header. * * @param msg The message. * @return To header instance. */ #define PJSIP_MSG_TO_HDR(msg) \ ((pjsip_to_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_TO, NULL)) /** * @} */ /* **************************************************************************/ /** * @addtogroup PJSIP_MSG_HDR * @{ */ /** * Generic SIP header, which contains hname and a string hvalue. * Note that this header is not supposed to be used as 'base' class for headers. */ typedef struct pjsip_generic_string_hdr { /** Standard header field. */ PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_string_hdr); /** hvalue */ pj_str_t hvalue; } pjsip_generic_string_hdr; /** * Create a new instance of generic header. A generic header can have an * arbitrary header name. * * @param pool The pool. * @param hname The header name to be assigned to the header, or NULL to * assign the header name with some string. * @param hvalue Optional string to be assigned as the value. * * @return The header, or THROW exception. */ PJ_DECL(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool, const pj_str_t *hname, const pj_str_t *hvalue); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * @param hname The header name to be assigned to the header, or NULL to * assign the header name with some string later. * @param hvalue Optional string to be assigned as the value. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_init( pj_pool_t *pool, void *mem, const pj_str_t *hname, const pj_str_t *hvalue); /** * Construct a generic string header without allocating memory from the pool. * This function is useful to create a temporary header which life-time is * very short (for example, creating the header in the stack to be passed * as argument to a function which will copy the header). * * @param h The header to be initialized. * @param hname The header name to be assigned to the header, or NULL to * assign the header name with some string. * @param hvalue Optional string to be assigned as the value. * * @return The header, or THROW exception. */ PJ_DECL(void) pjsip_generic_string_hdr_init2(pjsip_generic_string_hdr *h, pj_str_t *hname, pj_str_t *hvalue); /* **************************************************************************/ /** * Generic SIP header, which contains hname and an integer ivalue. */ typedef struct pjsip_generic_int_hdr { /** Standard header field. */ PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_int_hdr); /** ivalue */ pj_int32_t ivalue; } pjsip_generic_int_hdr; /** * Create a new instance of generic header. A generic header can have an * arbitrary header name. * * @param pool The pool. * @param hname The header name to be assigned to the header, or NULL to * assign the header name with some string. * @param hvalue The value to be assigned to the header. * * @return The header, or THROW exception. */ PJ_DECL(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool, const pj_str_t *hname, int hvalue ); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * @param hname The header name to be assigned to the header, or NULL to * assign the header name with some string later. * @param value Value to be assigned to the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_init( pj_pool_t *pool, void *mem, const pj_str_t *hname, int value ); /* **************************************************************************/ /** Maximum elements in the header array. */ #define PJSIP_GENERIC_ARRAY_MAX_COUNT 32 /** * Generic array of string header. */ typedef struct pjsip_generic_array_hdr { /** Standard header fields. */ PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_array_hdr); /** Number of tags/elements. */ unsigned count; /** Tags/elements. */ pj_str_t values[PJSIP_GENERIC_ARRAY_MAX_COUNT]; } pjsip_generic_array_hdr; /** * Create generic array header. * * @param pool Pool to allocate memory from. * @param hname Header name. * * @return New generic array header. */ PJ_DECL(pjsip_generic_array_hdr*) pjsip_generic_array_hdr_create(pj_pool_t *pool, const pj_str_t *hname); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * @param hname The header name to be assigned to the header, or NULL to * assign the header name with some string later. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_generic_array_hdr*) pjsip_generic_array_hdr_init(pj_pool_t *pool, void *mem, const pj_str_t *hname); /* **************************************************************************/ /** Accept header. */ typedef pjsip_generic_array_hdr pjsip_accept_hdr; /** Maximum fields in Accept header. */ #define PJSIP_MAX_ACCEPT_COUNT PJSIP_GENERIC_ARRAY_MAX_COUNT /** * Create new Accept header instance. * * @param pool The pool. * * @return New Accept header instance. */ PJ_DECL(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_accept_hdr*) pjsip_accept_hdr_init( pj_pool_t *pool, void *mem ); /* **************************************************************************/ /** * Allow header. */ typedef pjsip_generic_array_hdr pjsip_allow_hdr; /** * Create new Allow header instance. * * @param pool The pool. * * @return New Allow header instance. */ PJ_DECL(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_allow_hdr*) pjsip_allow_hdr_init( pj_pool_t *pool, void *mem ); /* **************************************************************************/ /** * Call-ID header. */ typedef struct pjsip_cid_hdr { PJSIP_DECL_HDR_MEMBER(struct pjsip_cid_hdr); pj_str_t id; /**< Call-ID string. */ } pjsip_cid_hdr; /** * Create new Call-ID header. * * @param pool The pool. * * @return new Call-ID header. */ PJ_DECL(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool ); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_cid_hdr*) pjsip_cid_hdr_init( pj_pool_t *pool, void *mem ); /* **************************************************************************/ /** * Content-Length header. */ typedef struct pjsip_clen_hdr { PJSIP_DECL_HDR_MEMBER(struct pjsip_clen_hdr); int len; /**< Content length. */ } pjsip_clen_hdr; /** * Create new Content-Length header. * * @param pool the pool. * @return A new Content-Length header instance. */ PJ_DECL(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool ); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_clen_hdr*) pjsip_clen_hdr_init( pj_pool_t *pool, void *mem ); /* **************************************************************************/ /** * CSeq header. */ typedef struct pjsip_cseq_hdr { PJSIP_DECL_HDR_MEMBER(struct pjsip_cseq_hdr); pj_int32_t cseq; /**< CSeq number. */ pjsip_method method; /**< CSeq method. */ } pjsip_cseq_hdr; /** Create new CSeq header. * * @param pool The pool. * @return A new CSeq header instance. */ PJ_DECL(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool ); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_cseq_hdr*) pjsip_cseq_hdr_init( pj_pool_t *pool, void *mem ); /* **************************************************************************/ /** * Contact header. * In this library, contact header only contains single URI. If a message has * multiple URI in the Contact header, the URI will be put in separate Contact * headers. */ typedef struct pjsip_contact_hdr { PJSIP_DECL_HDR_MEMBER(struct pjsip_contact_hdr); int star; /**< The contact contains only a '*' character */ pjsip_uri *uri; /**< URI in the contact. */ int q1000; /**< The "q" value times 1000 (to avoid float) */ pj_int32_t expires; /**< Expires parameter, otherwise -1 if not present. */ pjsip_param other_param; /**< Other parameters, concatenated in a single string. */ } pjsip_contact_hdr; /** * Create a new Contact header. * * @param pool The pool. * @return A new instance of Contact header. */ PJ_DECL(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool ); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_contact_hdr*) pjsip_contact_hdr_init( pj_pool_t *pool, void *mem ); /* **************************************************************************/ /** * Content-Type. */ typedef struct pjsip_ctype_hdr { PJSIP_DECL_HDR_MEMBER(struct pjsip_ctype_hdr); pjsip_media_type media; /**< Media type. */ } pjsip_ctype_hdr; /** * Create a nwe Content Type header. * * @param pool The pool. * @return A new Content-Type header. */ PJ_DECL(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool ); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_ctype_hdr*) pjsip_ctype_hdr_init( pj_pool_t *pool, void *mem ); /* **************************************************************************/ /** Expires header. */ typedef pjsip_generic_int_hdr pjsip_expires_hdr; /** * Create a new Expires header. * * @param pool The pool. * @param value The expiration value. * * @return A new Expires header. */ PJ_DECL(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool, int value); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * @param value The expiration value. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_expires_hdr*) pjsip_expires_hdr_init( pj_pool_t *pool, void *mem, int value ); /* **************************************************************************/ /** * To or From header. */ typedef struct pjsip_fromto_hdr { PJSIP_DECL_HDR_MEMBER(struct pjsip_fromto_hdr); pjsip_uri *uri; /**< URI in From/To header. */ pj_str_t tag; /**< Header "tag" parameter. */ pjsip_param other_param; /**< Other params, concatenated as a single string. */ } pjsip_fromto_hdr; /** Alias for From header. */ typedef pjsip_fromto_hdr pjsip_from_hdr; /** Alias for To header. */ typedef pjsip_fromto_hdr pjsip_to_hdr; /** * Create a From header. * * @param pool The pool. * @return New instance of From header. */ PJ_DECL(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool ); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_from_hdr*) pjsip_from_hdr_init( pj_pool_t *pool, void *mem ); /** * Create a To header. * * @param pool The pool. * @return New instance of To header. */ PJ_DECL(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool ); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_to_hdr*) pjsip_to_hdr_init( pj_pool_t *pool, void *mem ); /** * Convert the header to a From header. * * @param hdr The generic from/to header. * @return "From" header. */ PJ_DECL(pjsip_from_hdr*) pjsip_fromto_hdr_set_from( pjsip_fromto_hdr *hdr ); /** * Convert the header to a To header. * * @param hdr The generic from/to header. * @return "To" header. */ PJ_DECL(pjsip_to_hdr*) pjsip_fromto_hdr_set_to( pjsip_fromto_hdr *hdr ); /* **************************************************************************/ /** * Max-Forwards header. */ typedef pjsip_generic_int_hdr pjsip_max_fwd_hdr; /** * Create new Max-Forwards header instance. * * @param pool The pool. * @param value The Max-Forwards value. * * @return New Max-Forwards header instance. */ PJ_DECL(pjsip_max_fwd_hdr*) pjsip_max_fwd_hdr_create(pj_pool_t *pool, int value); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * @param value The Max-Forwards value. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_max_fwd_hdr*) pjsip_max_fwd_hdr_init( pj_pool_t *pool, void *mem, int value ); /* **************************************************************************/ /** * Min-Expires header. */ typedef pjsip_generic_int_hdr pjsip_min_expires_hdr; /** * Create new Min-Expires header instance. * * @param pool The pool. * @param value The Min-Expires value. * * @return New Min-Expires header instance. */ PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool, int value); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * @param value The Min-Expires value. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_init( pj_pool_t *pool, void *mem, int value ); /* **************************************************************************/ /** * Record-Route and Route headers. */ typedef struct pjsip_routing_hdr { PJSIP_DECL_HDR_MEMBER(struct pjsip_routing_hdr); /**< Generic header fields. */ pjsip_name_addr name_addr; /**< The URL in the Route/Record-Route header. */ pjsip_param other_param; /**< Other parameter. */ } pjsip_routing_hdr; /** Alias for Record-Route header. */ typedef pjsip_routing_hdr pjsip_rr_hdr; /** Alias for Route header. */ typedef pjsip_routing_hdr pjsip_route_hdr; /** * Create new Record-Route header from the pool. * * @param pool The pool. * @return A new instance of Record-Route header. */ PJ_DECL(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool ); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_rr_hdr*) pjsip_rr_hdr_init( pj_pool_t *pool, void *mem ); /** * Create new Route header from the pool. * * @param pool The pool. * @return A new instance of "Route" header. */ PJ_DECL(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool ); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_route_hdr*) pjsip_route_hdr_init( pj_pool_t *pool, void *mem ); /** * Convert generic routing header to Record-Route header. * * @param r The generic routing header, or a "Routing" header. * @return Record-Route header. */ PJ_DECL(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *r ); /** * Convert generic routing header to "Route" header. * * @param r The generic routing header, or a "Record-Route" header. * @return "Route" header. */ PJ_DECL(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *r ); /* **************************************************************************/ /** * Require header. */ typedef pjsip_generic_array_hdr pjsip_require_hdr; /** * Create new Require header instance. * * @param pool The pool. * * @return New Require header instance. */ PJ_DECL(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool); /** * Initialize a preallocated memory with the header structure. This function * should only be called when application uses its own memory allocation to * allocate memory block for the specified header (e.g. in C++, when the * header is allocated with "new" operator). * For normal applications, they should use pjsip_xxx_hdr_create() instead, * which allocates memory and initialize it in one go. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_require_hdr*) pjsip_require_hdr_init( pj_pool_t *pool, void *mem ); /* **************************************************************************/ /** * Retry-After header. */ typedef struct pjsip_retry_after_hdr { /** Standard header field. */ PJSIP_DECL_HDR_MEMBER(struct pjsip_retry_after_hdr); pj_int32_t ivalue; /**< Retry-After value */ pjsip_param param; /**< Optional parameters */ pj_str_t comment; /**< Optional comments. */ } pjsip_retry_after_hdr; /** * Create new Retry-After header instance. * * @param pool The pool. * @param value The Retry-After value. * * @return New Retry-After header instance. */ PJ_DECL(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool, int value); /** * Initialize a preallocated memory with the header structure. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * @param value The Retry-After value. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_init( pj_pool_t *pool, void *mem, int value ); /* **************************************************************************/ /** * Supported header. */ typedef pjsip_generic_array_hdr pjsip_supported_hdr; /** * Create new Supported header instance. * * @param pool The pool. * * @return New Supported header instance. */ PJ_DECL(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool); /** * Initialize a preallocated memory with the header structure. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_supported_hdr*) pjsip_supported_hdr_init( pj_pool_t *pool, void *mem ); /* **************************************************************************/ /** * Unsupported header. */ typedef pjsip_generic_array_hdr pjsip_unsupported_hdr; /** * Create new Unsupported header instance. * * @param pool The pool. * * @return New Unsupported header instance. */ PJ_DECL(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool); /** * Initialize a preallocated memory with the header structure. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_init( pj_pool_t *pool, void *mem ); /* **************************************************************************/ /** * SIP Via header. * In this implementation, Via header can only have one element in each header. * If a message arrives with multiple elements in a single Via, then they will * be split up into multiple Via headers. */ typedef struct pjsip_via_hdr { PJSIP_DECL_HDR_MEMBER(struct pjsip_via_hdr); pj_str_t transport; /**< Transport type. */ pjsip_host_port sent_by; /**< Host and optional port */ int ttl_param; /**< TTL parameter, or -1 if it's not specified. */ int rport_param; /**< "rport" parameter, 0 to specify without port number, -1 means doesn't exist. */ pj_str_t maddr_param; /**< "maddr" parameter. */ pj_str_t recvd_param; /**< "received" parameter. */ pj_str_t branch_param; /**< "branch" parameter. */ pjsip_param other_param; /**< Other parameters, concatenated as single string. */ pj_str_t comment; /**< Comment. */ } pjsip_via_hdr; /** * Create a new Via header. * * @param pool The pool. * @return A new "Via" header instance. */ PJ_DECL(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool ); /** * Initialize a preallocated memory with the header structure. * * @param pool Pool for additional memory allocation if required. * @param mem Pre-allocated memory to be initialized as the header. * * @return The header instance, which points to the same memory * location as the mem argument. */ PJ_DECL(pjsip_via_hdr*) pjsip_via_hdr_init( pj_pool_t *pool, void *mem ); /* **************************************************************************/ /** * SIP Warning header. * In this version, Warning header is just a typedef for generic string * header. */ typedef pjsip_generic_string_hdr pjsip_warning_hdr; /** * Create a warning header with the specified contents. * * @param pool Pool to allocate memory from. * @param code Warning code, 300-399. * @param host The host portion of the Warning header. * @param text The warning text, which MUST not be quoted with * double quote. * * @return The Warning header field. */ PJ_DECL(pjsip_warning_hdr*) pjsip_warning_hdr_create( pj_pool_t *pool, int code, const pj_str_t *host, const pj_str_t *text); /** * Create a warning header and initialize the contents from the error * message for the specified status code. The warning code will be * set to 399. * * @param pool Pool to allocate memory from. * @param host The host portion of the Warning header. * @param status The error status code, which error text will be * put in as the Warning text. * * @return The Warning header field. */ PJ_DECL(pjsip_warning_hdr*) pjsip_warning_hdr_create_from_status( pj_pool_t *pool, const pj_str_t *host, pj_status_t status); /* **************************************************************************/ /** Accept-Encoding header. */ typedef pjsip_generic_string_hdr pjsip_accept_encoding_hdr; /** Create Accept-Encoding header. */ #define pjsip_accept_encoding_hdr_create pjsip_generic_string_hdr_create /** Accept-Language header. */ typedef pjsip_generic_string_hdr pjsip_accept_lang_hdr; /** Create Accept-Language header. */ #define pjsip_accept_lang_hdr_create pjsip_generic_string_hdr_create /** Alert-Info header. */ typedef pjsip_generic_string_hdr pjsip_alert_info_hdr; /** Create Alert-Info header. */ #define pjsip_alert_info_hdr_create pjsip_generic_string_hdr_create /** Authentication-Info header. */ typedef pjsip_generic_string_hdr pjsip_auth_info_hdr; /** Create Authentication-Info header. */ #define pjsip_auth_info_hdr_create pjsip_generic_string_hdr_create /** Call-Info header. */ typedef pjsip_generic_string_hdr pjsip_call_info_hdr; /** Create Call-Info header. */ #define pjsip_call_info_hdr_create pjsip_generic_string_hdr_create /** Content-Disposition header. */ typedef pjsip_generic_string_hdr pjsip_content_disposition_hdr; /** Create Content-Disposition header. */ #define pjsip_content_disposition_hdr_create pjsip_generic_string_hdr_create /** Content-Encoding header. */ typedef pjsip_generic_string_hdr pjsip_content_encoding_hdr; /** Create Content-Encoding header. */ #define pjsip_content_encoding_hdr_create pjsip_generic_string_hdr_create /** Content-Language header. */ typedef pjsip_generic_string_hdr pjsip_content_lang_hdr; /** Create Content-Language header. */ #define pjsip_content_lang_hdr_create pjsip_generic_string_hdr_create /** Date header. */ typedef pjsip_generic_string_hdr pjsip_date_hdr; /** Create Date header. */ #define pjsip_date_hdr_create pjsip_generic_string_hdr_create /** Error-Info header. */ typedef pjsip_generic_string_hdr pjsip_err_info_hdr; /** Create Error-Info header. */ #define pjsip_err_info_hdr_create pjsip_generic_string_hdr_create /** In-Reply-To header. */ typedef pjsip_generic_string_hdr pjsip_in_reply_to_hdr; /** Create In-Reply-To header. */ #define pjsip_in_reply_to_hdr_create pjsip_generic_string_hdr_create /** MIME-Version header. */ typedef pjsip_generic_string_hdr pjsip_mime_version_hdr; /** Create MIME-Version header. */ #define pjsip_mime_version_hdr_create pjsip_generic_string_hdr_create /** Organization header. */ typedef pjsip_generic_string_hdr pjsip_organization_hdr; /** Create Organization header. */ #define pjsip_organization_hdr_create pjsip_genric_string_hdr_create /** Priority header. */ typedef pjsip_generic_string_hdr pjsip_priority_hdr; /** Create Priority header. */ #define pjsip_priority_hdr_create pjsip_generic_string_hdr_create /** Proxy-Require header. */ typedef pjsip_generic_string_hdr pjsip_proxy_require_hdr; /** Reply-To header. */ typedef pjsip_generic_string_hdr pjsip_reply_to_hdr; /** Create Reply-To header. */ #define pjsip_reply_to_hdr_create pjsip_generic_string_hdr_create /** Server header. */ typedef pjsip_generic_string_hdr pjsip_server_hdr; /** Create Server header. */ #define pjsip_server_hdr_create pjsip_generic_string_hdr_create /** Subject header. */ typedef pjsip_generic_string_hdr pjsip_subject_hdr; /** Create Subject header. */ #define pjsip_subject_hdr_create pjsip_generic_string_hdr_create /** Timestamp header. */ typedef pjsip_generic_string_hdr pjsip_timestamp_hdr; /** Create Timestamp header. */ #define pjsip_timestamp_hdr_create pjsip_generic_string_hdr_create /** User-Agent header. */ typedef pjsip_generic_string_hdr pjsip_user_agent_hdr; /** Create User-Agent header. */ #define pjsip_user_agent_hdr_create pjsip_generic_string_hdr_create /** * @} */ /** * @} PJSIP_MSG */ PJ_END_DECL #endif /* __PJSIP_SIP_MSG_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_multipart.h ================================================ /* $Id: sip_multipart.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_MULTIPART_H__ #define __PJSIP_SIP_MULTIPART_H__ /** * @file pjsip/sip_multipart.h * @brief Multipart support. */ #include PJ_BEGIN_DECL /** * @defgroup PJSIP_MULTIPART Multipart message bodies. * @ingroup PJSIP_MSG * @brief Support for multipart message bodies. * @{ */ /** * This structure describes the individual body part inside a multipart * message body. It mainly contains the message body itself and optional * headers. */ typedef struct pjsip_multipart_part { /** * Standard list element. */ PJ_DECL_LIST_MEMBER(struct pjsip_multipart_part); /** * Optional message headers. */ pjsip_hdr hdr; /** * Pointer to the message body. */ pjsip_msg_body *body; } pjsip_multipart_part; /** * Create an empty multipart body. * * @param pool Memory pool to allocate memory from. * @param ctype Optional MIME media type of the multipart * bodies. If not specified, "multipart/mixed" * will be used. * @param boundary Optional string to be set as part boundary. * The boundary string excludes the leading * hyphens. If this parameter is NULL or empty, * a random boundary will be generated. * * @return Multipart body instance with no part. */ PJ_DECL(pjsip_msg_body*) pjsip_multipart_create(pj_pool_t *pool, const pjsip_media_type *ctype, const pj_str_t *boundary); /** * Create an empty multipart part. * * @param pool The memory pool. * * @return The multipart part. */ PJ_DECL(pjsip_multipart_part*) pjsip_multipart_create_part(pj_pool_t *pool); /** * Perform a deep clone to a multipart part. * * @param pool The memory pool. * @param part The part to be duplicated. * * @return Copy of the multipart part. */ PJ_DECL(pjsip_multipart_part*) pjsip_multipart_clone_part(pj_pool_t *pool, const pjsip_multipart_part *part); /** * Add a part into multipart bodies. * * @param pool The memory pool. * @param mp The multipart bodies. * @param part The part to be added into the bodies. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_multipart_add_part(pj_pool_t *pool, pjsip_msg_body *mp, pjsip_multipart_part *part); /** * Get the first part of multipart bodies. * * @param mp The multipart bodies. * * @return The first part, or NULL if the multipart * bodies currently doesn't hold any elements. */ PJ_DECL(pjsip_multipart_part*) pjsip_multipart_get_first_part(const pjsip_msg_body *mp); /** * Get the next part after the specified part. * * @param mp The multipart bodies. * @param part The part. * * @return The next part, or NULL if there is no other part after * the part. */ PJ_DECL(pjsip_multipart_part*) pjsip_multipart_get_next_part(const pjsip_msg_body *mp, pjsip_multipart_part *part); /** * Find a body inside multipart bodies which has the specified content type. * * @param mp The multipart body. * @param content_type Content type to find. * @param start If specified, the search will begin at * start->next. Otherwise it will begin at * the first part in the multipart bodies. * * @return The first part with the specified content type * if found, or NULL. */ PJ_DECL(pjsip_multipart_part*) pjsip_multipart_find_part( const pjsip_msg_body *mp, const pjsip_media_type *content_type, const pjsip_multipart_part *start); /** * Parse multipart message. * * @param pool Memory pool. * @param buf Input buffer. * @param len The buffer length. * @param ctype Content type of the multipart body. * @param options Parsing options, must be zero for now. * * @return Multipart message body. */ PJ_DECL(pjsip_msg_body*) pjsip_multipart_parse(pj_pool_t *pool, char *buf, pj_size_t len, const pjsip_media_type *ctype, unsigned options); /** * @} PJSIP_MULTIPART */ PJ_END_DECL #endif /* __PJSIP_SIP_MULTIPART_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_parser.h ================================================ /* $Id: sip_parser.h 4445 2013-03-20 11:29:08Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_PARSER_H__ #define __PJSIP_SIP_PARSER_H__ /** * @file sip_parser.h * @brief SIP Message Parser */ #include #include #include PJ_BEGIN_DECL /** * @defgroup PJSIP_PARSER Parser * @ingroup PJSIP_MSG * @brief Message and message elements parsing. * @{ */ /** * URI Parsing options. */ enum { /** If this option is specified, function #pjsip_parse_uri will return * the URI object as pjsip_name_addr instead of the corresponding * URI object. */ PJSIP_PARSE_URI_AS_NAMEADDR = 1, /** If this option is specified, function #pjsip_parse_uri and other * internal functions that this function calls will parse URI according * to convention for parsing From/To/Contact header. For example, when * the URI is not enclosed in brackets ("<" and ">"), all parameters * are treated as header parameters (not URI parameters). */ PJSIP_PARSE_URI_IN_FROM_TO_HDR = 2 }; /** * Parser syntax error exception value. */ extern int PJSIP_SYN_ERR_EXCEPTION; /** * This structure is used to get error reporting from parser. */ typedef struct pjsip_parser_err_report { /** Standard header fields. */ PJ_DECL_LIST_MEMBER(struct pjsip_parser_err_report); int except_code; /**< Error exception (e.g. PJSIP_SYN_ERR_EXCEPTION) */ int line; /**< Line number. */ int col; /**< Column number. */ pj_str_t hname; /**< Header name, if any. */ } pjsip_parser_err_report; /** * Parsing context, the default argument for parsing functions. */ typedef struct pjsip_parse_ctx { pj_scanner *scanner; /**< The scanner. */ pj_pool_t *pool; /**< The pool. */ pjsip_rx_data *rdata; /**< Optional rdata. */ } pjsip_parse_ctx; /** * Type of function to parse header. The parsing function must follow these * specification: * - It must not modify the input text. * - The hname and HCOLON has been parsed prior to invoking the handler. * - It returns the header instance on success. * - For error reporting, it must throw PJSIP_SYN_ERR_EXCEPTION exception * instead of just returning NULL. * When exception is thrown, the return value is ignored. * - It must read the header separator after finished reading the header * body. The separator types are described below, and if they don't exist, * exception must be thrown. Header separator can be a: * - newline, such as when the header is part of a SIP message. * - ampersand, such as when the header is part of an URI. * - for the last header, these separator is optional since parsing * can be terminated when seeing EOF. */ typedef pjsip_hdr* (pjsip_parse_hdr_func)(pjsip_parse_ctx *context); /** * Type of function to parse URI scheme. * Most of the specification of header parser handler (pjsip_parse_hdr_func) * also applies here (except the separator part). */ typedef void* (pjsip_parse_uri_func)(pj_scanner *scanner, pj_pool_t *pool, pj_bool_t parse_params); /** * Register header parser handler. The parser handler MUST follow the * specification of header parser handler function. New registration * overwrites previous registration with the same name. * * @param hname The header name. * @param hshortname The short header name or NULL. * @param fptr The pointer to function to parser the header. * * @return PJ_SUCCESS if success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_register_hdr_parser( const char *hname, const char *hshortname, pjsip_parse_hdr_func *fptr); /** * Unregister previously registered header parser handler. * All the arguments MUST exactly equal to the value specified upon * registration of the handler. * * @param hname The header name registered. * @param hshortname The short header name registered, or NULL. * @param fptr Previously registered function to parse the header. * * @return zero if unregistration was successfull. */ PJ_DECL(pj_status_t) pjsip_unregister_hdr_parser( const char *hname, const char *hshortname, pjsip_parse_hdr_func *fptr); /** * Register URI scheme parser handler. * * @param scheme The URI scheme registered. * @param func The URI parser function. * * @return zero on success. */ PJ_DECL(pj_status_t) pjsip_register_uri_parser( char *scheme, pjsip_parse_uri_func *func); /** * Unregister URI scheme parser handler. * All the arguments MUST exactly equal to the value specified upon * registration of the handler. * * @param scheme The URI scheme as registered previously. * @param func The function handler as registered previously. * * @return zero if the registration was successfull. */ PJ_DECL(pj_status_t) pjsip_unregister_uri_parser( const char *scheme, pjsip_parse_uri_func *func); /** * Parse an URI in the input and return the correct instance of URI. * * @param pool The pool to get memory allocations. * @param buf The input buffer, which MUST be NULL terminated. * @param size The length of the string (not counting NULL terminator). * @param options If no options are given (value is zero), the object * returned is dependent on the syntax of the URI, * eg. basic SIP URL, TEL URL, or name address. * If option PJSIP_PARSE_URI_AS_NAMEADDR is given, * then the returned object is always name address object, * with the relevant URI object contained in the name * address object. * @return The URI or NULL when failed. No exception is thrown by * this function (or any public parser functions). */ PJ_DECL(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool, char *buf, pj_size_t size, unsigned options); /** * Parse SIP status line. * * @param buf Text buffer to parse, which MUST be NULL terminated. * @param size The size of the buffer, excluding the NULL character. * @param status_line Structure to receive the parsed elements. * * @return PJ_SUCCESS if a status line is parsed successfully. */ PJ_DECL(pj_status_t) pjsip_parse_status_line(char *buf, pj_size_t size, pjsip_status_line *status_line); /** * Parse a packet buffer and build a full SIP message from the packet. This * function parses all parts of the message, including request/status line, * all headers, and the message body. The message body however is only * treated as a text block, ie. the function will not try to parse the content * of the body. * * @param pool The pool to allocate memory. * @param buf The input buffer, which MUST be NULL terminated. * @param size The length of the string (not counting NULL terminator). * @param err_list If this parameter is not NULL, then the parser will * put error messages during parsing in this list. * * @return The message or NULL when failed. No exception is thrown * by this function (or any public parser functions). */ PJ_DECL(pjsip_msg *) pjsip_parse_msg( pj_pool_t *pool, char *buf, pj_size_t size, pjsip_parser_err_report *err_list); /** * Parse a packet buffer and build a rdata. The resulting message will be * stored in \c msg field in the \c rdata. This behaves pretty much like * #pjsip_parse_msg(), except that it will also initialize the header fields * in the \c rdata. * * This function is normally called by the transport layer. * * @param buf The input buffer, which MUST be NULL terminated. * @param size The length of the string (not counting NULL terminator). * @param rdata The receive data buffer to store the message and * its elements. * * @return The message inside the rdata if successfull, or NULL. */ PJ_DECL(pjsip_msg *) pjsip_parse_rdata( char *buf, pj_size_t size, pjsip_rx_data *rdata ); /** * Check incoming packet to see if a (probably) valid SIP message has been * received. * * @param buf The input buffer, which must be NULL terminated. * @param size The buffer size. * @param is_datagram Put non-zero if transport is datagram oriented. * @param msg_size [out] If message is valid, this parameter will contain * the size of the SIP message (including body, if any). * * @return PJ_SUCCESS if a message is found, or an error code. */ PJ_DECL(pj_status_t) pjsip_find_msg(const char *buf, pj_size_t size, pj_bool_t is_datagram, pj_size_t *msg_size); /** * Parse the content of a header and return the header instance. * This function parses the content of a header (ie. part after colon) according * to the expected name, and will return the correct instance of header. * * @param pool Pool to allocate memory for the header. * @param hname Header name which is used to find the correct function * to parse the header. * @param line Header content, which must be NULL terminated. * @param size The length of the string (not counting NULL terminator, * if any). * @param parsed_len If the value is not NULL, then upon return the function * will fill the pointer with the length of the string * that has been parsed. This is usefull for two purposes, * one is when the string may contain more than one header * lines, and two when an error happen the value can * pinpoint the location of the error in the buffer. * * @return The instance of the header if parsing was successful, * or otherwise a NULL pointer will be returned. */ PJ_DECL(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname, char *line, pj_size_t size, int *parsed_len); /** * Parse header line(s). Multiple headers can be parsed by this function. * When there are multiple headers, the headers MUST be separated by either * a newline (as in SIP message) or ampersand mark (as in URI). This separator * is optional for the last header. * * @param pool The pool. * @param input The input text to parse, which must be NULL terminated. * @param size The text length. * @param hlist The header list to store the parsed headers. * This list must have been initialized before calling * this function. * @param options Specify 1 here to make parsing stop when error is * encountered when parsing the header. Otherwise the * error is silently ignored and parsing resumes to the * next line. * @return zero if successfull, or -1 if error is encountered. * Upon error, the \a hlist argument MAY contain * successfully parsed headers. */ PJ_DECL(pj_status_t) pjsip_parse_headers( pj_pool_t *pool, char *input, pj_size_t size, pjsip_hdr *hlist, unsigned options); /** * @} */ #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable:4510) // default constructor could not be generated # pragma warning(disable:4512) // assignment operator could not be generated # pragma warning(disable:4610) // user defined constructor required #endif /** * Parser constants. @see pjsip_parser_const() */ typedef struct pjsip_parser_const_t { const pj_str_t pjsip_USER_STR; /**< "user" string constant. */ const pj_str_t pjsip_METHOD_STR; /**< "method" string constant */ const pj_str_t pjsip_TRANSPORT_STR; /**< "transport" string const. */ const pj_str_t pjsip_MADDR_STR; /**< "maddr" string const. */ const pj_str_t pjsip_LR_STR; /**< "lr" string const. */ const pj_str_t pjsip_SIP_STR; /**< "sip" string constant. */ const pj_str_t pjsip_SIPS_STR; /**< "sips" string constant. */ const pj_str_t pjsip_TEL_STR; /**< "tel" string constant. */ const pj_str_t pjsip_BRANCH_STR; /**< "branch" string constant. */ const pj_str_t pjsip_TTL_STR; /**< "ttl" string constant. */ const pj_str_t pjsip_RECEIVED_STR; /**< "received" string const. */ const pj_str_t pjsip_Q_STR; /**< "q" string constant. */ const pj_str_t pjsip_EXPIRES_STR; /**< "expires" string constant. */ const pj_str_t pjsip_TAG_STR; /**< "tag" string constant. */ const pj_str_t pjsip_RPORT_STR; /**< "rport" string const. */ pj_cis_t pjsip_HOST_SPEC; /**< For scanning host part. */ pj_cis_t pjsip_DIGIT_SPEC; /**< Decimal digits */ pj_cis_t pjsip_ALPHA_SPEC; /**< Alpha (A-Z, a-z) */ pj_cis_t pjsip_ALNUM_SPEC; /**< Decimal + Alpha. */ pj_cis_t pjsip_TOKEN_SPEC; /**< Token. */ pj_cis_t pjsip_TOKEN_SPEC_ESC; /**< Token without '%' character */ pj_cis_t pjsip_VIA_PARAM_SPEC; /**< Via param is token + ":" for IPv6. */ pj_cis_t pjsip_VIA_PARAM_SPEC_ESC; /**< .. as above without '%' */ pj_cis_t pjsip_HEX_SPEC; /**< Hexadecimal digits. */ pj_cis_t pjsip_PARAM_CHAR_SPEC; /**< For scanning pname (or pvalue when it's not quoted.) in URI */ pj_cis_t pjsip_PARAM_CHAR_SPEC_ESC; /**< Variant without the escape ('%') char */ pj_cis_t pjsip_HDR_CHAR_SPEC; /**< Chars in hname/havalue in URL. */ pj_cis_t pjsip_HDR_CHAR_SPEC_ESC; /**< Variant without the escape ('%') char */ pj_cis_t pjsip_PROBE_USER_HOST_SPEC;/**< Hostname characters. */ pj_cis_t pjsip_PASSWD_SPEC; /**< Password. */ pj_cis_t pjsip_PASSWD_SPEC_ESC; /**< Variant without the escape ('%') char */ pj_cis_t pjsip_USER_SPEC; /**< User */ pj_cis_t pjsip_USER_SPEC_ESC; /**< Variant without the escape ('%') char */ pj_cis_t pjsip_USER_SPEC_LENIENT; /**< User, with additional '#' char */ pj_cis_t pjsip_USER_SPEC_LENIENT_ESC;/**< pjsip_USER_SPEC_ESC with '#' */ pj_cis_t pjsip_NOT_NEWLINE; /**< For eating up header, basically any chars except newlines or zero. */ pj_cis_t pjsip_NOT_COMMA_OR_NEWLINE;/**< Array elements. */ pj_cis_t pjsip_DISPLAY_SPEC; /**< Used when searching for display name. */ pj_cis_t pjsip_OTHER_URI_CONTENT; /**< Generic URI content. */ } pjsip_parser_const_t; #ifdef _MSC_VER # pragma warning(pop) #endif /** * Get parser constants. */ PJ_DECL(const pjsip_parser_const_t*) pjsip_parser_const(void); /* * Parser utilities. */ enum { PJSIP_PARSE_REMOVE_QUOTE = 1 }; /* Parse parameter in header (matching the character as token) */ PJ_DECL(void) pjsip_parse_param_imp(pj_scanner *scanner, pj_pool_t *pool, pj_str_t *pname, pj_str_t *pvalue, unsigned opt); /* Parse parameter in URL (matching the character as paramchar) */ PJ_DECL(void) pjsip_parse_uri_param_imp(pj_scanner *scanner, pj_pool_t *pool, pj_str_t *pname, pj_str_t *pvalue, unsigned opt); PJ_DECL(void) pjsip_concat_param_imp(pj_str_t *param, pj_pool_t *pool, const pj_str_t *pname, const pj_str_t *pvalue, int sepchar); PJ_DECL(void) pjsip_parse_end_hdr_imp ( pj_scanner *scanner ); /* Parse generic array header */ PJ_DECL(void) pjsip_parse_generic_array_hdr_imp(pjsip_generic_array_hdr *hdr, pj_scanner *scanner); PJ_END_DECL #endif /* __PJSIP_SIP_PARSER_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_private.h ================================================ /* $Id: sip_private.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_PRIVATE_H__ #define __PJSIP_SIP_PRIVATE_H__ /** * @file sip_private.h * @brief Private structures and functions for PJSIP Library. */ #include #endif /* __PJSIP_PRIVATE_I_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_resolve.h ================================================ /* $Id: sip_resolve.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_RESOLVE_H__ #define __PJSIP_SIP_RESOLVE_H__ /** * @file sip_resolve.h * @brief * This module contains the mechanism to resolve server address as specified by * RFC 3263 - Locating SIP Servers */ #include #include #include PJ_BEGIN_DECL /** * @defgroup PJSIP_RESOLVE SIP SRV Server Resolution (RFC 3263 - Locating SIP Servers) * @ingroup PJSIP_TRANSPORT * @brief Framework to resolve SIP servers based on RFC 3263. * @{ * \section PJSIP_RESOLVE_FEATURES Features * * This is the SIP server resolution framework, which is modelled after * RFC 3263 - Locating SIP Servers document. The SIP server resolution * framework is asynchronous; callback will be called once the server * address has been resolved (successfully or with errors). * * \subsection PJSIP_RESOLVE_CONFORMANT Conformance to RFC 3263 * * The SIP server resolution framework is modelled after RFC 3263 (Locating * SIP Servers) document, and it provides a single function (#pjsip_resolve()) * to resolve a domain into actual IP addresses of the servers, by querying * DNS SRV record and DNS A record where necessary. * * The #pjsip_resolve() function performs the server resolution according * to RFC 3263 with some additional fallback mechanisms, as follows: * - if the target name is an IP address, the callback will be called * immediately with the IP address. If port number was specified, this * port number will be used, otherwise the default port number for the * transport will be used (5060 for TCP/UDP, 5061 for TLS) if the transport * is specified. If the transport is not specified, UDP with port number * 5060 will be used. * - if target name is not an IP address but it contains port number, * then the target name is resolved with DNS A (or AAAA, when IPv6 is * supported in the future) query, and the port is taken from the * port number argument. The callback will be called once the DNS A * resolution completes. If the DNS A resolution returns multiple IP * addresses, these IP addresses will be returned to the caller. * - if target name is not an IP address and port number is not specified, * DNS SRV resolution will be performed for the specified name and * transport type (or UDP when transport is not specified), * then followed by DNS A (or AAAA, when IPv6 is supported) * resolution for each target in the SRV record. If DNS SRV * resolution returns error, DNS A (or AAAA) resolution will be * performed for the original target (it is assumed that the target domain * does not support SRV records). Upon successful completion, * application callback will be called with each IP address of the * target selected based on the load-balancing and fail-over criteria * below. * * The above server resolution procedure differs from RFC 3263 in these * regards: * - currently #pjsip_resolve() doesn't support DNS NAPTR record. * - if transport is not specified, it is assumed to be UDP (the proper * behavior is to query the NAPTR record, but we don't support this * yet). * * * \subsection PJSIP_SIP_RESOLVE_FAILOVER_LOADBALANCE Load-Balancing and Fail-Over * * When multiple targets are returned in the DNS SRV response, server entries * are selected based on the following rule (which is described in RFC 2782): * - targets will be sorted based on the priority first. * - for targets with the same priority, #pjsip_resolve() will select * only one target according to its weight. To select this one target, * the function associates running-sum for all targets, and generates * a random number between zero and the total running-sum (inclusive). * The target selected is the first target with running-sum greater than * or equal to this random number. * * The above procedure will select one target for each priority, allowing * application to fail-over to the next target when the previous target fails. * These targets are returned in the #pjsip_server_addresses structure * argument of the callback. * * \subsection PJSIP_SIP_RESOLVE_SIP_FEATURES SIP SRV Resolver Features * * Some features of the SIP resolver: * - DNS SRV entries are returned on sorted order based on priority * to allow failover to the next appropriate server. * - The procedure in RFC 2782 is used to select server with the same * priority to load-balance the servers load. * - A single function (#pjsip_resolve()) performs all server resolution * works, from resolving the SRV records to getting the actual IP addresses * of the servers with DNS A (or AAAA) resolution. * - When multiple DNS SRV records are returned, parallel DNS A (or AAAA) * queries will be issued simultaneously. * - The PJLIB-UTIL DNS resolver provides additional functionality such as * response caching, query aggregation, parallel nameservers, fallback * nameserver, etc., which will be described below. * - Enable application to provide its own resolver implementation. * * * \subsection PJSIP_RESOLVE_DNS_FEATURES DNS Resolver Features * * The PJSIP server resolution framework uses PJLIB-UTIL DNS resolver engine * for performing the asynchronous DNS request. The PJLIB-UTIL DNS resolver * has some useful features, such as: * - queries are asynchronous with configurable timeout, * - query aggregation to combine multiple pending queries to the same * DNS target into a single DNS request (to save message round-trip and * processing), * - response caching with TTL negotiated between the minimum TTL found in * the response and the maximum TTL allowed in the configuration, * - multiple nameservers, with active nameserver is selected from nameserver * which provides the best response time, * - fallback nameserver, with periodic detection of which name servers are * active or down. * - etc. * * Please consult PJLIB-UTIL DNS resolver documentation for more details. * * * \section PJSIP_RESOLVE_USING Using the Resolver * * To maintain backward compatibility, the resolver MUST be enabled manually. * With the default settings, the resolver WILL NOT perform DNS SRV resolution, * as it will just resolve the name with standard pj_gethostbyname() function. * * Application can enable the SRV resolver by creating the PJLIB-UTIL DNS * resolver with #pjsip_endpt_create_resolver(), configure the * nameservers of the PJLIB-UTIL DNS resolver object by calling * pj_dns_resolver_set_ns() function, and pass the DNS resolver object to * #pjsip_resolver_set_resolver() function. * * Once the resolver is set, it will be used automatically by PJSIP everytime * PJSIP needs to send SIP request/response messages. * * \section PJSIP_RESOLVE_EXT_RESOLVER External Resolver * * As an alternative to enabling PJLIB-UTIL DNS resolver, application can * provide its own resolver implementation by defining the callback in * pjsip_ext_resolver and pass the callback to * #pjsip_resolver_set_ext_resolver() function. Please note that if the * implementation needs feature from PJLIB-UTL DNS resolver, it has to create * its own PJLIB-UTL DNS resolver instance. * * \section PJSIP_RESOLVE_REFERENCE Reference * * Reference: * - RFC 2782: A DNS RR for specifying the location of services (DNS SRV) * - RFC 3263: Locating SIP Servers */ /** * The server addresses returned by the resolver. */ typedef struct pjsip_server_addresses { /** Number of address records. */ unsigned count; /** Address records. */ struct { /** Preferable transport to be used to contact this address. */ pjsip_transport_type_e type; /** Server priority (the lower the higher the priority). */ unsigned priority; /** Server weight (the higher the more load it can handle). */ unsigned weight; /** The server's address. */ pj_sockaddr addr; /** Address length. */ int addr_len; } entry[PJSIP_MAX_RESOLVED_ADDRESSES]; } pjsip_server_addresses; /** * The type of callback function to be called when resolver finishes the job. * * @param status The status of the operation, which is zero on success. * @param token The token that was associated with the job when application * call the resolve function. * @param addr The addresses resolved by the operation. */ typedef void pjsip_resolver_callback(pj_status_t status, void *token, const struct pjsip_server_addresses *addr); /** * This structure describes application callback to receive various event from * the SIP resolver engine. Application can use this for its own resolver * implementation. */ typedef struct pjsip_ext_resolver { /** * Notify application when the resolution should begin. * * @param resolver The resolver engine. * @param pool The pool to allocate resolver job. * @param target The target specification to be resolved. * @param token A user defined token to be passed back to callback * function. * @param cb The callback function. */ void (*resolve) (pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip_host_info *target, void *token, pjsip_resolver_callback *cb); } pjsip_ext_resolver; /** * Create SIP resolver engine. Note that this function is normally called * internally by pjsip_endpoint instance. * * @param pool Pool to allocate memory from. * @param p_res Pointer to receive SIP resolver instance. * * @return PJ_SUCCESS when resolver can be successfully created. */ PJ_DECL(pj_status_t) pjsip_resolver_create(pj_pool_t *pool, pjsip_resolver_t **p_res); /** * Set the DNS resolver instance of the SIP resolver engine. Before the * DNS resolver is set, the SIP resolver will use standard pj_gethostbyname() * to resolve addresses. * * Note that application normally will use #pjsip_endpt_set_resolver() instead * since it does not normally have access to the SIP resolver instance. * * @param res The SIP resolver engine. * @param dns_res The DNS resolver instance to be used by the SIP resolver. * This argument can be NULL to reset the internal DNS * instance. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_resolver_set_resolver(pjsip_resolver_t *res, pj_dns_resolver *dns_res); /** * Set the DNS external resolver implementation to use in the SIP resolver * engine. Naturally when implementing its own resolver, application would not * need the internal resolver, hence this function will also destroy the * PJLIB-UTIL DNS resolver if any (e.g: set using * #pjsip_resolver_set_resolver()). Application that needs it, still be able * create its own instance. * * Note that application normally will use #pjsip_endpt_set_ext_resolver() * instead since it does not normally have access to the SIP resolver instance. * * @param res The SIP resolver engine. * @param ext_res The external resolver implementation callback. This argument * can be NULL to reset the whole external implementation. * However, it is prohibited to reset individual callback. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_resolver_set_ext_resolver( pjsip_resolver_t *res, pjsip_ext_resolver *ext_res); /** * Get the DNS resolver instance of the SIP resolver engine. * * Note that application normally will use #pjsip_endpt_get_resolver() instead * since it does not normally have access to the SIP resolver instance. * * @param res The SIP resolver engine. * * @return The DNS resolver instance (may be NULL) */ PJ_DECL(pj_dns_resolver*) pjsip_resolver_get_resolver(pjsip_resolver_t *res); /** * Destroy resolver engine. Note that this will also destroy the internal * DNS resolver inside the engine. If application doesn't want the internal * DNS resolver to be destroyed, it should set the internal DNS resolver * to NULL before calling this function. * * Note that this function will normally called by the SIP endpoint instance * when the SIP endpoint instance is destroyed. * * @param resolver The resolver. */ PJ_DECL(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver); /** * Asynchronously resolve a SIP target host or domain according to rule * specified in RFC 3263 (Locating SIP Servers). When the resolving operation * has completed, the callback will be called. * * Note that application normally will use #pjsip_endpt_resolve() instead * since it does not normally have access to the SIP resolver instance. * * @param resolver The resolver engine. * @param pool The pool to allocate resolver job. * @param target The target specification to be resolved. * @param token A user defined token to be passed back to callback function. * @param cb The callback function. */ PJ_DECL(void) pjsip_resolve( pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip_host_info *target, void *token, pjsip_resolver_callback *cb); /** * @} */ PJ_END_DECL #endif /* __PJSIP_SIP_RESOLVE_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_tel_uri.h ================================================ /* $Id: sip_tel_uri.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_TEL_URI_H__ #define __PJSIP_TEL_URI_H__ /** * @file sip_tel_uri.h * @brief Tel: URI */ #include /** * @addtogroup PJSIP_TEL_URI tel URI Scheme * @ingroup PJSIP_URI * @brief Support for "tel:" URI scheme. * @{ */ PJ_BEGIN_DECL /** * tel: URI. */ typedef struct pjsip_tel_uri { pjsip_uri_vptr *vptr; /**< Pointer to virtual function table. */ pj_str_t number; /**< Global or local phone number */ pj_str_t context; /**< Phone context (for local number). */ pj_str_t ext_param; /**< Extension param. */ pj_str_t isub_param; /**< ISDN sub-address param. */ pjsip_param other_param;/**< Other parameter. */ } pjsip_tel_uri; /** * Create a new tel: URI. * * @param pool The pool. * * @return New instance of tel: URI. */ PJ_DECL(pjsip_tel_uri*) pjsip_tel_uri_create(pj_pool_t *pool); /** * This function compares two numbers for equality, according to rules as * specified in RFC 3966. * * @param nb1 The first number. * @param nb2 The second number. * * @return Zero if equal, -1 if nb1 is less than nb2, or +1 if * nb1 is greater than nb2. */ PJ_DECL(int) pjsip_tel_nb_cmp(const pj_str_t *nb1, const pj_str_t *nb2); PJ_END_DECL /** * @} */ #endif /* __PJSIP_TEL_URI_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_transaction.h ================================================ /* $Id: sip_transaction.h 4420 2013-03-05 11:59:54Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_TRANSACTION_H__ #define __PJSIP_SIP_TRANSACTION_H__ /** * @file sip_transaction.h * @brief SIP Transaction */ #include #include #include #include PJ_BEGIN_DECL /** * @defgroup PJSIP_TRANSACT Transaction Layer * @brief Provides statefull message processing. * * This module provides stateful processing to incoming or outgoing SIP * messages. * Before performing any stateful operations, application must register the * transaction layer module by calling #pjsip_tsx_layer_init_module(). * * Application should link with pjsip-core library to * use the transaction layer. */ /** * @defgroup PJSIP_TRANSACT_TRANSACTION Transaction * @ingroup PJSIP_TRANSACT * @brief Transaction instance for all types of SIP transactions. * @{ * The pjsip_transaction describes SIP transaction, and is used for * both INVITE and non-INVITE, UAC or UAS. Application must register the * transaction layer module with #pjsip_tsx_layer_init_module() before * performing any stateful operations. */ /** * This enumeration represents transaction state. */ typedef enum pjsip_tsx_state_e { PJSIP_TSX_STATE_NULL, /**< For UAC, before any message is sent. */ PJSIP_TSX_STATE_CALLING, /**< For UAC, just after request is sent. */ PJSIP_TSX_STATE_TRYING, /**< For UAS, just after request is received.*/ PJSIP_TSX_STATE_PROCEEDING, /**< For UAS/UAC, after provisional response.*/ PJSIP_TSX_STATE_COMPLETED, /**< For UAS/UAC, after final response. */ PJSIP_TSX_STATE_CONFIRMED, /**< For UAS, after ACK is received. */ PJSIP_TSX_STATE_TERMINATED, /**< For UAS/UAC, before it's destroyed. */ PJSIP_TSX_STATE_DESTROYED, /**< For UAS/UAC, will be destroyed now. */ PJSIP_TSX_STATE_MAX /**< Number of states. */ } pjsip_tsx_state_e; /** * This structure describes SIP transaction object. The transaction object * is used to handle both UAS and UAC transaction. */ struct pjsip_transaction { /* * Administrivia */ pj_pool_t *pool; /**< Pool owned by the tsx. */ pjsip_module *tsx_user; /**< Transaction user. */ pjsip_endpoint *endpt; /**< Endpoint instance. */ pj_bool_t terminating; /**< terminate() was called */ pj_grp_lock_t *grp_lock; /**< Transaction grp lock. */ pj_mutex_t *mutex_b; /**< Second mutex to avoid deadlock. It is used to protect timer. */ /* * Transaction identification. */ char obj_name[PJ_MAX_OBJ_NAME]; /**< Log info. */ pjsip_role_e role; /**< Role (UAS or UAC) */ pjsip_method method; /**< The method. */ pj_int32_t cseq; /**< The CSeq */ pj_str_t transaction_key;/**< Hash table key. */ pj_uint32_t hashed_key; /**< Key's hashed value. */ pj_str_t branch; /**< The branch Id. */ /* * State and status. */ int status_code; /**< Last status code seen. */ pj_str_t status_text; /**< Last reason phrase. */ pjsip_tsx_state_e state; /**< State. */ int handle_200resp; /**< UAS 200/INVITE retrsm.*/ int tracing; /**< Tracing enabled? */ /** Handler according to current state. */ pj_status_t (*state_handler)(struct pjsip_transaction *, pjsip_event *); /* * Transport. */ pjsip_transport *transport; /**< Transport to use. */ pj_bool_t is_reliable; /**< Transport is reliable. */ pj_sockaddr addr; /**< Destination address. */ int addr_len; /**< Address length. */ pjsip_response_addr res_addr; /**< Response address. */ unsigned transport_flag; /**< Miscelaneous flag. */ pj_status_t transport_err; /**< Internal error code. */ pjsip_tpselector tp_sel; /**< Transport selector. */ pjsip_tx_data *pending_tx; /**< Tdata which caused pending transport flag to be set on tsx. */ pjsip_tp_state_listener_key *tp_st_key; /**< Transport state listener key. */ /* * Messages and timer. */ pjsip_tx_data *last_tx; /**< Msg kept for retrans. */ int retransmit_count;/**< Retransmission count. */ pj_timer_entry retransmit_timer;/**< Retransmit timer. */ pj_timer_entry timeout_timer; /**< Timeout timer. */ /** Module specific data. */ void *mod_data[PJSIP_MAX_MODULE]; }; /** * Create and register transaction layer module to the specified endpoint. * * @param endpt The endpoint instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_tsx_layer_init_module(pjsip_endpoint *endpt); /** * Get the instance of the transaction layer module. * * @return The transaction layer module. */ PJ_DECL(pjsip_module*) pjsip_tsx_layer_instance(void); /** * Unregister and destroy transaction layer module. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_tsx_layer_destroy(void); /** * Retrieve the current number of transactions currently registered * in the hash table. * * @return Number of transactions. */ PJ_DECL(unsigned) pjsip_tsx_layer_get_tsx_count(void); /** * Find a transaction with the specified key. The transaction key normally * is created by calling #pjsip_tsx_create_key() from an incoming message. * * @param key The key string to find the transaction. * @param lock If non-zero, transaction will be locked before the * function returns, to make sure that it's not deleted * by other threads. * * @return The matching transaction instance, or NULL if transaction * can not be found. */ PJ_DECL(pjsip_transaction*) pjsip_tsx_layer_find_tsx( const pj_str_t *key, pj_bool_t lock ); /** * Create, initialize, and register a new transaction as UAC from the * specified transmit data (\c tdata). The transmit data must have a valid * \c Request-Line and \c CSeq header. * * If \c Via header does not exist, it will be created along with a unique * \c branch parameter. If it exists and contains branch parameter, then * the \c branch parameter will be used as is as the transaction key. If * it exists but branch parameter doesn't exist, a unique branch parameter * will be created. * * @param tsx_user Module to be registered as transaction user of the new * transaction, which will receive notification from the * transaction via on_tsx_state() callback. * @param tdata The outgoing request message. * @param p_tsx On return will contain the new transaction instance. * * @return PJ_SUCCESS if successfull. */ PJ_DECL(pj_status_t) pjsip_tsx_create_uac( pjsip_module *tsx_user, pjsip_tx_data *tdata, pjsip_transaction **p_tsx); /** * Variant of pjsip_tsx_create_uac() with additional parameter to specify * the group lock to use. Group lock can be used to synchronize locking * among several objects to prevent deadlock, and to synchronize the * lifetime of objects sharing the same group lock. * * See pjsip_tsx_create_uac() for general info about this function. * * @param tsx_user Module to be registered as transaction user of the new * transaction, which will receive notification from the * transaction via on_tsx_state() callback. * @param tdata The outgoing request message. * @param grp_lock Optional group lock to use by this transaction. If * the value is NULL, the transaction will create its * own group lock. * @param p_tsx On return will contain the new transaction instance. * * @return PJ_SUCCESS if successfull. */ PJ_DECL(pj_status_t) pjsip_tsx_create_uac2(pjsip_module *tsx_user, pjsip_tx_data *tdata, pj_grp_lock_t *grp_lock, pjsip_transaction **p_tsx); /** * Create, initialize, and register a new transaction as UAS from the * specified incoming request in \c rdata. After calling this function, * application MUST call #pjsip_tsx_recv_msg() so that transaction * moves from state NULL. * * @param tsx_user Module to be registered as transaction user of the new * transaction, which will receive notification from the * transaction via on_tsx_state() callback. * @param rdata The received incoming request. * @param p_tsx On return will contain the new transaction instance. * * @return PJ_SUCCESS if successfull. */ PJ_DECL(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user, pjsip_rx_data *rdata, pjsip_transaction **p_tsx ); /** * Variant of pjsip_tsx_create_uas() with additional parameter to specify * the group lock to use. Group lock can be used to synchronize locking * among several objects to prevent deadlock, and to synchronize the * lifetime of objects sharing the same group lock. * * See pjsip_tsx_create_uas() for general info about this function. * * @param tsx_user Module to be registered as transaction user of the new * transaction, which will receive notification from the * transaction via on_tsx_state() callback. * @param rdata The received incoming request. * @param grp_lock Optional group lock to use by this transaction. If * the value is NULL, the transaction will create its * own group lock. * @param p_tsx On return will contain the new transaction instance. * * @return PJ_SUCCESS if successfull. */ PJ_DECL(pj_status_t) pjsip_tsx_create_uas2(pjsip_module *tsx_user, pjsip_rx_data *rdata, pj_grp_lock_t *grp_lock, pjsip_transaction **p_tsx ); /** * Lock/bind transaction to a specific transport/listener. This is optional, * as normally transport will be selected automatically based on the * destination of the message upon resolver completion. * * @param tsx The transaction. * @param sel Transport selector containing the specification of * transport or listener to be used by this transaction * to send requests. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tsx_set_transport(pjsip_transaction *tsx, const pjsip_tpselector *sel); /** * Call this function to manually feed a message to the transaction. * For UAS transaction, application MUST call this function after * UAS transaction has been created. * * This function SHOULD only be called to pass initial request message * to UAS transaction. Before this function returns, on_tsx_state() * callback of the transaction user will be called. If response message * is passed to this function, then on_rx_response() will also be called * before on_tsx_state(). * * @param tsx The transaction. * @param rdata The message. */ PJ_DECL(void) pjsip_tsx_recv_msg( pjsip_transaction *tsx, pjsip_rx_data *rdata); /** * Transmit message in tdata with this transaction. It is possible to * pass NULL in tdata for UAC transaction, which in this case the last * message transmitted, or the request message which was specified when * calling #pjsip_tsx_create_uac(), will be sent. * * This function decrements the reference counter of the transmit buffer * only when it returns PJ_SUCCESS; * * @param tsx The transaction. * @param tdata The outgoing message. If NULL is specified, then the * last message transmitted (or the message specified * in UAC initialization) will be sent. * * @return PJ_SUCCESS if successfull. */ PJ_DECL(pj_status_t) pjsip_tsx_send_msg( pjsip_transaction *tsx, pjsip_tx_data *tdata); /** * Manually retransmit the last message transmitted by this transaction, * without updating the transaction state. This function is useful when * TU wants to maintain the retransmision by itself (for example, * retransmitting reliable provisional response). * * @param tsx The transaction. * @param tdata The outgoing message. If NULL is specified, then the * last message transmitted (or the message specified * in UAC initialization) will be sent. * * * @return PJ_SUCCESS if successful. */ PJ_DECL(pj_status_t) pjsip_tsx_retransmit_no_state(pjsip_transaction *tsx, pjsip_tx_data *tdata); /** * Create transaction key, which is used to match incoming requests * or response (retransmissions) against transactions. * * @param pool The pool * @param key Output key. * @param role The role of the transaction. * @param method The method to be put as a key. * @param rdata The received data to calculate. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *key, pjsip_role_e role, const pjsip_method *method, const pjsip_rx_data *rdata ); /** * Force terminate transaction. * * @param tsx The transaction. * @param code The status code to report. */ PJ_DECL(pj_status_t) pjsip_tsx_terminate( pjsip_transaction *tsx, int code ); /** * Cease retransmission on the UAC transaction. The UAC transaction is * still considered running, and it will complete when either final * response is received or the transaction times out. * * This operation normally is used for INVITE transaction only, when * the transaction is cancelled before any provisional response has been * received. * * @param tsx The transaction. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tsx_stop_retransmit(pjsip_transaction *tsx); /** * Start a timer to terminate transaction after the specified time * has elapsed. This function is only valid for INVITE transaction, * and only before final response is received for the INVITE transaction. * It is normally called after the UAC has sent CANCEL for this * INVITE transaction. * * The purpose of this function is to terminate the transaction if UAS * does not send final response to this INVITE transaction even after * it sends 200/OK to CANCEL (for example when the UAS complies to RFC * 2543). * * Once this timer is set, the transaction will be terminated either when * a final response is received or the timer expires. * * @param tsx The transaction. * @param millisec Timeout value in milliseconds. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tsx_set_timeout(pjsip_transaction *tsx, unsigned millisec); /** * Get the transaction instance in the incoming message. If the message * has a corresponding transaction, this function will return non NULL * value. * * @param rdata The incoming message buffer. * * @return The transaction instance associated with this message, * or NULL if the message doesn't match any transactions. */ PJ_DECL(pjsip_transaction*) pjsip_rdata_get_tsx( pjsip_rx_data *rdata ); /** * @} */ /* * Internal. */ /* * Dump transaction layer. */ PJ_DECL(void) pjsip_tsx_layer_dump(pj_bool_t detail); /** * Get the string name for the state. * @param state State */ PJ_DECL(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state); /** * Get the role name. * @param role Role. */ PJ_DECL(const char *) pjsip_role_name(pjsip_role_e role); PJ_END_DECL #endif /* __PJSIP_TRANSACT_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_transport.h ================================================ /* $Id: sip_transport.h 4275 2012-10-04 06:11:58Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_TRANSPORT_H__ #define __PJSIP_SIP_TRANSPORT_H__ /** * @file sip_transport.h * @brief SIP Transport */ #include #include #include #include #include #include #include PJ_BEGIN_DECL /** * @defgroup PJSIP_TRANSPORT Transport * @ingroup PJSIP_CORE * @brief This is the transport framework. * * The transport framework is fully extensible. Please see * PJSIP Developer's Guide PDF * document for more information. * * Application MUST register at least one transport to PJSIP before any * messages can be sent or received. Please see @ref PJSIP_TRANSPORT_UDP * on how to create/register UDP transport to the transport framework. * * @{ */ /***************************************************************************** * * GENERAL TRANSPORT (NAMES, TYPES, ETC.) * *****************************************************************************/ /* * Forward declaration for transport factory (since it is referenced by * the transport factory itself). */ typedef struct pjsip_tpfactory pjsip_tpfactory; /** * Flags for SIP transports. */ enum pjsip_transport_flags_e { PJSIP_TRANSPORT_RELIABLE = 1, /**< Transport is reliable. */ PJSIP_TRANSPORT_SECURE = 2, /**< Transport is secure. */ PJSIP_TRANSPORT_DATAGRAM = 4 /**< Datagram based transport. (it's also assumed to be connectionless) */ }; /** * Check if transport tp is reliable. */ #define PJSIP_TRANSPORT_IS_RELIABLE(tp) \ ((tp)->flag & PJSIP_TRANSPORT_RELIABLE) /** * Check if transport tp is secure. */ #define PJSIP_TRANSPORT_IS_SECURE(tp) \ ((tp)->flag & PJSIP_TRANSPORT_SECURE) /** * Register new transport type to PJSIP. The PJSIP transport framework * contains the info for some standard transports, as declared by * #pjsip_transport_type_e. Application may use non-standard transport * with PJSIP, but before it does so, it must register the information * about the new transport type to PJSIP by calling this function. * * @param tp_flag The flags describing characteristics of this * transport type. * @param tp_name Transport type name. * @param def_port Default port to be used for the transport. * @param p_tp_type On successful registration, it will be filled with * the registered type. This argument is optional. * * @return PJ_SUCCESS if registration is successful, or * PJSIP_ETYPEEXISTS if the same transport type has * already been registered. */ PJ_DECL(pj_status_t) pjsip_transport_register_type(unsigned tp_flag, const char *tp_name, int def_port, int *p_tp_type); /** * Get the transport type from the transport name. * * @param name Transport name, such as "TCP", or "UDP". * * @return The transport type, or PJSIP_TRANSPORT_UNSPECIFIED if * the name is not recognized as the name of supported * transport. */ PJ_DECL(pjsip_transport_type_e) pjsip_transport_get_type_from_name(const pj_str_t *name); /** * Get the transport type for the specified flags. * * @param flag The transport flag. * * @return Transport type. */ PJ_DECL(pjsip_transport_type_e) pjsip_transport_get_type_from_flag(unsigned flag); /** * Get the socket address family of a given transport type. * * @param type Transport type. * * @return Transport type. */ PJ_DECL(int) pjsip_transport_type_get_af(pjsip_transport_type_e type); /** * Get transport flag from type. * * @param type Transport type. * * @return Transport flags. */ PJ_DECL(unsigned) pjsip_transport_get_flag_from_type( pjsip_transport_type_e type ); /** * Get the default SIP port number for the specified type. * * @param type Transport type. * * @return The port number, which is the default SIP port number for * the specified type. */ PJ_DECL(int) pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type); /** * Get transport type name. * * @param t Transport type. * * @return Transport name. */ PJ_DECL(const char*) pjsip_transport_get_type_name(pjsip_transport_type_e t); /** * Get longer description for the specified transport type. * * @param t Transport type. * * @return Transport description. */ PJ_DECL(const char*) pjsip_transport_get_type_desc(pjsip_transport_type_e t); /***************************************************************************** * * TRANSPORT SELECTOR. * *****************************************************************************/ /** * This structure describes the type of data in pjsip_tpselector. */ typedef enum pjsip_tpselector_type { /** Transport is not specified. */ PJSIP_TPSELECTOR_NONE, /** Use the specific transport to send request. */ PJSIP_TPSELECTOR_TRANSPORT, /** Use the specific listener to send request. */ PJSIP_TPSELECTOR_LISTENER, } pjsip_tpselector_type; /** * This structure describes the transport/listener preference to be used * when sending outgoing requests. * * Normally transport will be selected automatically according to rules about * sending requests. But some applications (such as proxies or B2BUAs) may * want to explicitly use specific transport to send requests, for example * when they want to make sure that outgoing request should go from a specific * network interface. * * The pjsip_tpselector structure is used for that purpose, i.e. to allow * application specificly request that a particular transport/listener * should be used to send request. This structure is used when calling * pjsip_tsx_set_transport() and pjsip_dlg_set_transport(). */ typedef struct pjsip_tpselector { /** The type of data in the union */ pjsip_tpselector_type type; /** Union representing the transport/listener criteria to be used. */ union { pjsip_transport *transport; pjsip_tpfactory *listener; void *ptr; } u; } pjsip_tpselector; /** * Add transport/listener reference in the selector to prevent the specified * transport/listener from being destroyed while application still has * reference to it. * * @param sel The transport selector. */ PJ_DECL(void) pjsip_tpselector_add_ref(pjsip_tpselector *sel); /** * Decrement transport/listener reference in the selector. * @param sel The transport selector */ PJ_DECL(void) pjsip_tpselector_dec_ref(pjsip_tpselector *sel); /***************************************************************************** * * RECEIVE DATA BUFFER. * *****************************************************************************/ /** * A customized ioqueue async operation key which is used by transport * to locate rdata when a pending read operation completes. */ typedef struct pjsip_rx_data_op_key { pj_ioqueue_op_key_t op_key; /**< ioqueue op_key. */ pjsip_rx_data *rdata; /**< rdata associated with this */ } pjsip_rx_data_op_key; /** * Incoming message buffer. * This structure keep all the information regarding the received message. This * buffer lifetime is only very short, normally after the transaction has been * called, this buffer will be deleted/recycled. So care must be taken when * allocating storage from the pool of this buffer. */ struct pjsip_rx_data { /** * tp_info is part of rdata that remains static for the duration of the * buffer. It is initialized when the buffer was created by transport. */ struct { /** Memory pool for this buffer. */ pj_pool_t *pool; /** The transport object which received this packet. */ pjsip_transport *transport; /** Other transport specific data to be attached to this buffer. */ void *tp_data; /** Ioqueue key. */ pjsip_rx_data_op_key op_key; } tp_info; /** * pkt_info is initialized by transport when it receives an incoming * packet. */ struct { /** Time when the message was received. */ pj_time_val timestamp; /** Pointer to the original packet. */ char packet[PJSIP_MAX_PKT_LEN]; /** Zero termination for the packet. */ pj_uint32_t zero; /** The length of the packet received. */ pj_ssize_t len; /** The source address from which the packet was received. */ pj_sockaddr src_addr; /** The length of the source address. */ int src_addr_len; /** The IP source address string (NULL terminated). */ char src_name[PJ_INET6_ADDRSTRLEN]; /** The IP source port number. */ int src_port; } pkt_info; /** * msg_info is initialized by transport mgr (tpmgr) before this buffer * is passed to endpoint. */ struct { /** Start of msg buffer. */ char *msg_buf; /** Length fo message. */ int len; /** The parsed message, if any. */ pjsip_msg *msg; /** Short description about the message. * Application should use #pjsip_rx_data_get_info() instead. */ char *info; /** The Call-ID header as found in the message. */ pjsip_cid_hdr *cid; /** The From header as found in the message. */ pjsip_from_hdr *from; /** The To header as found in the message. */ pjsip_to_hdr *to; /** The topmost Via header as found in the message. */ pjsip_via_hdr *via; /** The CSeq header as found in the message. */ pjsip_cseq_hdr *cseq; /** Max forwards header. */ pjsip_max_fwd_hdr *max_fwd; /** The first route header. */ pjsip_route_hdr *route; /** The first record-route header. */ pjsip_rr_hdr *record_route; /** Content-type header. */ pjsip_ctype_hdr *ctype; /** Content-length header. */ pjsip_clen_hdr *clen; /** "Require" header containing aggregates of all Require * headers found in the message, or NULL. */ pjsip_require_hdr *require; /** "Supported" header containing aggregates of all Supported * headers found in the message, or NULL. */ pjsip_supported_hdr *supported; /** The list of error generated by the parser when parsing this message. */ pjsip_parser_err_report parse_err; } msg_info; /** * endpt_info is initialized by endpoint after this buffer reaches * endpoint. */ struct { /** * Data attached by modules to this message. */ void *mod_data[PJSIP_MAX_MODULE]; } endpt_info; }; /** * Get printable information about the message in the rdata. * * @param rdata The receive data buffer. * * @return Printable information. */ PJ_DECL(char*) pjsip_rx_data_get_info(pjsip_rx_data *rdata); /** * Clone pjsip_rx_data. This will duplicate the contents of * pjsip_rx_data and add reference count to the transport. * Once application has finished using the cloned pjsip_rx_data, * it must release it by calling #pjsip_rx_data_free_cloned(). * * By default (if flags is set to zero), this function copies the * transport pointer in \a tp_info, duplicates the \a pkt_info, * perform deep clone of the \a msg_info parts of the rdata, and * fills the \a endpt_info (i.e. the \a mod_data) with zeros. * * @param src The source to be cloned. * @param flags Optional flags. Must be zero for now. * @param p_rdata Pointer to receive the cloned rdata. * * @return PJ_SUCCESS on success or the appropriate error. */ PJ_DECL(pj_status_t) pjsip_rx_data_clone(const pjsip_rx_data *src, unsigned flags, pjsip_rx_data **p_rdata); /** * Free cloned pjsip_rx_data. This function must be and must only * be called for a cloned pjsip_rx_data. Specifically, it must NOT * be called for the original pjsip_rx_data that is returned by * transports. * * This function will free the memory used by the pjsip_rx_data and * decrement the transport reference counter. * * @param rdata The receive data buffer. * * @return PJ_SUCCESS on success or the appropriate error. */ PJ_DECL(pj_status_t) pjsip_rx_data_free_cloned(pjsip_rx_data *rdata); /***************************************************************************** * * TRANSMIT DATA BUFFER MANIPULATION. * *****************************************************************************/ /** Customized ioqueue async operation key, used by transport to keep * callback parameters. */ typedef struct pjsip_tx_data_op_key { /** ioqueue pending operation key. */ pj_ioqueue_op_key_t key; /** Transmit data associated with this key. */ pjsip_tx_data *tdata; /** Arbitrary token (attached by transport) */ void *token; /** Callback to be called when pending transmit operation has completed. */ void (*callback)(pjsip_transport*,void*,pj_ssize_t); } pjsip_tx_data_op_key; /** * Data structure for sending outgoing message. Application normally creates * this buffer by calling #pjsip_endpt_create_tdata. * * The lifetime of this buffer is controlled by the reference counter in this * structure, which is manipulated by calling #pjsip_tx_data_add_ref and * #pjsip_tx_data_dec_ref. When the reference counter has reached zero, then * this buffer will be destroyed. * * A transaction object normally will add reference counter to this buffer * when application calls #pjsip_tsx_send_msg, because it needs to keep the * message for retransmission. The transaction will release the reference * counter once its state has reached final state. */ struct pjsip_tx_data { /** This is for transmission queue; it's managed by transports. */ PJ_DECL_LIST_MEMBER(struct pjsip_tx_data); /** Memory pool for this buffer. */ pj_pool_t *pool; /** A name to identify this buffer. */ char obj_name[PJ_MAX_OBJ_NAME]; /** Short information describing this buffer and the message in it. * Application should use #pjsip_tx_data_get_info() instead of * directly accessing this member. */ char *info; /** For response message, this contains the reference to timestamp when * the original request message was received. The value of this field * is set when application creates response message to a request by * calling #pjsip_endpt_create_response. */ pj_time_val rx_timestamp; /** The transport manager for this buffer. */ pjsip_tpmgr *mgr; /** Ioqueue asynchronous operation key. */ pjsip_tx_data_op_key op_key; /** Lock object. */ pj_lock_t *lock; /** The message in this buffer. */ pjsip_msg *msg; /** Strict route header saved by #pjsip_process_route_set(), to be * restored by #pjsip_restore_strict_route_set(). */ pjsip_route_hdr *saved_strict_route; /** Buffer to the printed text representation of the message. When the * content of this buffer is set, then the transport will send the content * of this buffer instead of re-printing the message structure. If the * message structure has changed, then application must invalidate this * buffer by calling #pjsip_tx_data_invalidate_msg. */ pjsip_buffer buf; /** Reference counter. */ pj_atomic_t *ref_cnt; /** Being processed by transport? */ int is_pending; /** Transport manager internal. */ void *token; /** Callback to be called when this tx_data has been transmitted. */ void (*cb)(void*, pjsip_tx_data*, pj_ssize_t); /** Destination information, to be used to determine the network address * of the message. For a request, this information is initialized when * the request is sent with #pjsip_endpt_send_request_stateless() and * network address is resolved. For CANCEL request, this information * will be copied from the original INVITE to make sure that the CANCEL * request goes to the same physical network address as the INVITE * request. */ struct { /** Server name. */ pj_str_t name; /** Server addresses resolved. */ pjsip_server_addresses addr; /** Current server address being tried. */ unsigned cur_addr; } dest_info; /** Transport information, only valid during on_tx_request() and * on_tx_response() callback. */ struct { pjsip_transport *transport; /**< Transport being used. */ pj_sockaddr dst_addr; /**< Destination address. */ int dst_addr_len; /**< Length of address. */ char dst_name[PJ_INET6_ADDRSTRLEN]; /**< Destination address. */ int dst_port; /**< Destination port. */ } tp_info; /** * Transport selector, to specify which transport to be used. * The value here must be set with pjsip_tx_data_set_transport(), * to allow reference counter to be set properly. */ pjsip_tpselector tp_sel; /** * Special flag to indicate that this transmit data is a request that has * been updated with proper authentication response and is ready to be * sent for retry. */ pj_bool_t auth_retry; /** * Arbitrary data attached by PJSIP modules. */ void *mod_data[PJSIP_MAX_MODULE]; /** * If via_addr is set, it will be used as the "sent-by" field of the * Via header for outgoing requests as long as the request uses via_tp * transport. Normally application should not use or access these fields. */ pjsip_host_port via_addr; /**< Via address. */ const void *via_tp; /**< Via transport. */ }; /** * Create a new, blank transmit buffer. The reference count is initialized * to zero. * * @param mgr The transport manager. * @param tdata Pointer to receive transmit data. * * @return PJ_SUCCESS, or the appropriate error code. * * @see pjsip_endpt_create_tdata */ PJ_DECL(pj_status_t) pjsip_tx_data_create( pjsip_tpmgr *mgr, pjsip_tx_data **tdata ); /** * Add reference counter to the transmit buffer. The reference counter controls * the life time of the buffer, ie. when the counter reaches zero, then it * will be destroyed. * * @param tdata The transmit buffer. */ PJ_DECL(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata ); /** * Decrement reference counter of the transmit buffer. * When the transmit buffer is no longer used, it will be destroyed and * caller is informed with PJSIP_EBUFDESTROYED return status. * * @param tdata The transmit buffer data. * @return This function will always succeeded eventhough the return * status is non-zero. A status PJSIP_EBUFDESTROYED will be * returned to inform that buffer is destroyed. */ PJ_DECL(pj_status_t) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata ); /** * Print the SIP message to transmit data buffer's internal buffer. This * may allocate memory for the buffer, if the buffer has not been allocated * yet, and encode the SIP message to that buffer. * * @param tdata The transmit buffer. * * @return PJ_SUCCESS on success of the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tx_data_encode(pjsip_tx_data *tdata); /** * Check if transmit data buffer contains a valid message. * * @param tdata The transmit buffer. * @return Non-zero (PJ_TRUE) if buffer contains a valid message. */ PJ_DECL(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata ); /** * Invalidate the print buffer to force message to be re-printed. Call * when the message has changed after it has been printed to buffer. The * message is printed to buffer normally by transport when it is about to be * sent to the wire. Subsequent sending of the message will not cause * the message to be re-printed, unless application invalidates the buffer * by calling this function. * * @param tdata The transmit buffer. */ PJ_DECL(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata ); /** * Get short printable info about the transmit data. This will normally return * short information about the message. * * @param tdata The transmit buffer. * * @return Null terminated info string. */ PJ_DECL(char*) pjsip_tx_data_get_info( pjsip_tx_data *tdata ); /** * Set the explicit transport to be used when sending this transmit data. * Application should not need to call this function, but rather use * pjsip_tsx_set_transport() and pjsip_dlg_set_transport() instead (which * will call this function). * * @param tdata The transmit buffer. * @param sel Transport selector. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_tx_data_set_transport(pjsip_tx_data *tdata, const pjsip_tpselector *sel); /***************************************************************************** * * TRANSPORT * *****************************************************************************/ /** * Type of callback to receive transport operation status. */ typedef void (*pjsip_transport_callback)(pjsip_transport *tp, void *token, pj_ssize_t sent_bytes); /** * This structure describes transport key to be registered to hash table. */ typedef struct pjsip_transport_key { /** * Transport type. */ long type; /** * Destination address. */ pj_sockaddr rem_addr; } pjsip_transport_key; /** * Enumeration of transport direction types. */ typedef enum pjsip_transport_dir { PJSIP_TP_DIR_NONE, /**< Direction not set, normally used by connectionless transports such as UDP transport. */ PJSIP_TP_DIR_OUTGOING, /**< Outgoing connection or client mode, this is only for connection-oriented transports. */ PJSIP_TP_DIR_INCOMING, /**< Incoming connection or server mode, this is only for connection-oriented transports. */ } pjsip_transport_dir; /** * This structure represent the "public" interface of a SIP transport. * Applications normally extend this structure to include transport * specific members. */ struct pjsip_transport { char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */ pj_pool_t *pool; /**< Pool used by transport. */ pj_atomic_t *ref_cnt; /**< Reference counter. */ pj_lock_t *lock; /**< Lock object. */ pj_bool_t tracing; /**< Tracing enabled? */ pj_bool_t is_shutdown; /**< Being shutdown? */ pj_bool_t is_destroying; /**< Destroy in progress? */ /** Key for indexing this transport in hash table. */ pjsip_transport_key key; char *type_name; /**< Type name. */ unsigned flag; /**< #pjsip_transport_flags_e */ char *info; /**< Transport info/description.*/ int addr_len; /**< Length of addresses. */ pj_sockaddr local_addr; /**< Bound address. */ pjsip_host_port local_name; /**< Published name (eg. STUN). */ pjsip_host_port remote_name; /**< Remote address name. */ pjsip_transport_dir dir; /**< Connection direction. */ pjsip_endpoint *endpt; /**< Endpoint instance. */ pjsip_tpmgr *tpmgr; /**< Transport manager. */ pjsip_tpfactory *factory; /**< Factory instance. Note: it may be invalid/shutdown. */ pj_timer_entry idle_timer; /**< Timer when ref cnt is zero.*/ pj_timestamp last_recv_ts; /**< Last time receiving data. */ pj_size_t last_recv_len; /**< Last received data length. */ void *data; /**< Internal transport data. */ /** * Function to be called by transport manager to send SIP message. * * @param transport The transport to send the message. * @param packet The buffer to send. * @param length The length of the buffer to send. * @param op_key Completion token, which will be supplied to * caller when pending send operation completes. * @param rem_addr The remote destination address. * @param addr_len Size of remote address. * @param callback If supplied, the callback will be called * once a pending transmission has completed. If * the function completes immediately (i.e. return * code is not PJ_EPENDING), the callback will not * be called. * * @return Should return PJ_SUCCESS only if data has been * succesfully queued to operating system for * transmission. Otherwise it may return PJ_EPENDING * if the underlying transport can not send the * data immediately and will send it later, which in * this case caller doesn't have to do anything * except wait the calback to be called, if it * supplies one. * Other return values indicate the error code. */ pj_status_t (*send_msg)(pjsip_transport *transport, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, pjsip_transport_callback callback); /** * Instruct the transport to initiate graceful shutdown procedure. * After all objects release their reference to this transport, * the transport will be deleted. * * Note that application MUST use #pjsip_transport_shutdown() instead. * * @param transport The transport. * * @return PJ_SUCCESS on success. */ pj_status_t (*do_shutdown)(pjsip_transport *transport); /** * Forcefully destroy this transport regardless whether there are * objects that currently use this transport. This function should only * be called by transport manager or other internal objects (such as the * transport itself) who know what they're doing. Application should use * #pjsip_transport_shutdown() instead. * * @param transport The transport. * * @return PJ_SUCCESS on success. */ pj_status_t (*destroy)(pjsip_transport *transport); /* * Application may extend this structure.. */ }; /** * Register a transport instance to the transport manager. This function * is normally called by the transport instance when it is created * by application. * * @param mgr The transport manager. * @param tp The new transport to be registered. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr, pjsip_transport *tp ); /** * Start graceful shutdown procedure for this transport. After graceful * shutdown has been initiated, no new reference can be obtained for * the transport. However, existing objects that currently uses the * transport may still use this transport to send and receive packets. * * After all objects release their reference to this transport, * the transport will be destroyed immediately. * * @param tp The transport. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_transport_shutdown(pjsip_transport *tp); /** * Destroy a transport when there is no object currently uses the transport. * This function is normally called internally by transport manager or the * transport itself. Application should use #pjsip_transport_shutdown() * instead. * * @param tp The transport instance. * * @return PJ_SUCCESS on success or the appropriate error code. * Some of possible errors are PJSIP_EBUSY if the * transport's reference counter is not zero. */ PJ_DECL(pj_status_t) pjsip_transport_destroy( pjsip_transport *tp); /** * Add reference counter to the specified transport. Any objects that wishes * to keep the reference of the transport MUST increment the transport's * reference counter to prevent it from being destroyed. * * @param tp The transport instance. * * @return PJ_SUCCESS on success or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp ); /** * Decrement reference counter of the specified transport. When an object no * longer want to keep the reference to the transport, it must decrement the * reference counter. When the reference counter of the transport reaches * zero, the transport manager will start the idle timer to destroy the * transport if no objects acquire the reference counter during the idle * interval. * * @param tp The transport instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp ); /** * This function is called by transport instances to report an incoming * packet to the transport manager. The transport manager then would try to * parse all SIP messages in the packet, and for each parsed SIP message, it * would report the message to the SIP endpoint (#pjsip_endpoint). * * @param mgr The transport manager instance. * @param rdata The receive data buffer containing the packet. The * transport MUST fully initialize tp_info and pkt_info * member of the rdata. * * @return The number of bytes successfully processed from the * packet. If the transport is datagram oriented, the * value will be equal to the size of the packet. For * stream oriented transport (e.g. TCP, TLS), the value * returned may be less than the packet size, if * partial message is received. The transport then MUST * keep the remainder part and report it again to * this function once more data/packet is received. */ PJ_DECL(pj_ssize_t) pjsip_tpmgr_receive_packet(pjsip_tpmgr *mgr, pjsip_rx_data *rdata); /***************************************************************************** * * TRANSPORT FACTORY * *****************************************************************************/ /** * A transport factory is normally used for connection oriented transports * (such as TCP or TLS) to create instances of transports. It registers * a new transport type to the transport manager, and the transport manager * would ask the factory to create a transport instance when it received * command from application to send a SIP message using the specified * transport type. */ struct pjsip_tpfactory { /** This list is managed by transport manager. */ PJ_DECL_LIST_MEMBER(struct pjsip_tpfactory); char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */ pj_pool_t *pool; /**< Owned memory pool. */ pj_lock_t *lock; /**< Lock object. */ pjsip_transport_type_e type; /**< Transport type. */ char *type_name; /**< Type string name. */ unsigned flag; /**< Transport flag. */ pj_sockaddr local_addr; /**< Bound address. */ pjsip_host_port addr_name; /**< Published name. */ /** * Create new outbound connection suitable for sending SIP message * to specified remote address. * Note that the factory is responsible for both creating the * transport and registering it to the transport manager. */ pj_status_t (*create_transport)(pjsip_tpfactory *factory, pjsip_tpmgr *mgr, pjsip_endpoint *endpt, const pj_sockaddr *rem_addr, int addr_len, pjsip_transport **transport); /** * Create new outbound connection suitable for sending SIP message * to specified remote address by also considering outgoing SIP * message data. * Note that the factory is responsible for both creating the * transport and registering it to the transport manager. */ pj_status_t (*create_transport2)(pjsip_tpfactory *factory, pjsip_tpmgr *mgr, pjsip_endpoint *endpt, const pj_sockaddr *rem_addr, int addr_len, pjsip_tx_data *tdata, pjsip_transport **transport); /** * Destroy the listener. */ pj_status_t (*destroy)(pjsip_tpfactory *factory); /* * Application may extend this structure.. */ }; /** * Register a transport factory. * * @param mgr The transport manager. * @param tpf Transport factory. * * @return PJ_SUCCESS if listener was successfully created. */ PJ_DECL(pj_status_t) pjsip_tpmgr_register_tpfactory(pjsip_tpmgr *mgr, pjsip_tpfactory *tpf); /** * Unregister factory. * * @param mgr The transport manager. * @param tpf Transport factory. * * @return PJ_SUCCESS is sucessfully unregistered. */ PJ_DECL(pj_status_t) pjsip_tpmgr_unregister_tpfactory(pjsip_tpmgr *mgr, pjsip_tpfactory *tpf); /***************************************************************************** * * TRANSPORT MANAGER * *****************************************************************************/ /** * Type of callback to be called when transport manager receives incoming * SIP message. * * @param ep Endpoint. * @param status Receiption status. * @param rd Received packet. */ typedef void (*pjsip_rx_callback)(pjsip_endpoint *ep, pj_status_t status, pjsip_rx_data *rd); /** * Type of callback to be called before transport manager is about * to transmit SIP message. * * @param ep Endpoint. * @param td Transmit data. */ typedef pj_status_t (*pjsip_tx_callback)(pjsip_endpoint *ep, pjsip_tx_data*td); /** * Create a transport manager. Normally application doesn't need to call * this function directly, since a transport manager will be created and * destroyed automatically by the SIP endpoint. * * @param pool Pool. * @param endpt Endpoint instance. * @param rx_cb Callback to receive incoming message. * @param tx_cb Callback to be called before transport manager is sending * outgoing message. * @param p_mgr Pointer to receive the new transport manager. * * @return PJ_SUCCESS or the appropriate error code on error. */ PJ_DECL(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool, pjsip_endpoint * endpt, pjsip_rx_callback rx_cb, pjsip_tx_callback tx_cb, pjsip_tpmgr **p_mgr); /** * Find out the appropriate local address info (IP address and port) to * advertise in Contact header based on the remote address to be * contacted. The local address info would be the address name of the * transport or listener which will be used to send the request. * * In this implementation, it will only select the transport based on * the transport type in the request. * * @see pjsip_tpmgr_find_local_addr2() * * @param tpmgr The transport manager. * @param pool Pool to allocate memory for the IP address. * @param type Destination address to contact. * @param sel Optional pointer to prefered transport, if any. * @param ip_addr Pointer to receive the IP address. * @param port Pointer to receive the port number. * * @return PJ_SUCCESS, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr, pj_pool_t *pool, pjsip_transport_type_e type, const pjsip_tpselector *sel, pj_str_t *ip_addr, int *port); /** * Parameter for pjsip_tpmgr_find_local_addr2() function. */ typedef struct pjsip_tpmgr_fla2_param { /** * Specify transport type to use. This must be set. */ pjsip_transport_type_e tp_type; /** * Optional pointer to preferred transport, if any. */ const pjsip_tpselector *tp_sel; /** * Destination host, if known. The destination host is needed * if \a local_if field below is set. */ pj_str_t dst_host; /** * Specify if the function should return which local interface * to use for the specified destination in \a dst_host. By definition, * the returned address will always be local interface address. */ pj_bool_t local_if; /** * The returned address. */ pj_str_t ret_addr; /** * The returned port. */ pj_uint16_t ret_port; /** * Returned pointer to the transport. Only set if local_if is set. */ const void *ret_tp; } pjsip_tpmgr_fla2_param; /** * Initialize with default values. * * @param prm The parameter to be initialized. */ PJ_DECL(void) pjsip_tpmgr_fla2_param_default(pjsip_tpmgr_fla2_param *prm); /** * Find out the appropriate local address info (IP address and port) to * advertise in Contact or Via header header based on the remote address * to be contacted. The local address info would be the address name of the * transport or listener which will be used to send the request. * * @see pjsip_tpmgr_find_local_addr() * * @param tpmgr The transport manager. * @param pool Pool to allocate memory for the IP address. * @param param Function input and output parameters. * * @return PJ_SUCCESS, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tpmgr_find_local_addr2(pjsip_tpmgr *tpmgr, pj_pool_t *pool, pjsip_tpmgr_fla2_param *prm); /** * Return number of transports currently registered to the transport * manager. * * @param mgr The transport manager. * * @return Number of transports. */ PJ_DECL(unsigned) pjsip_tpmgr_get_transport_count(pjsip_tpmgr *mgr); /** * Destroy a transport manager. Normally application doesn't need to call * this function directly, since a transport manager will be created and * destroyed automatically by the SIP endpoint. * * @param mgr The transport manager. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_tpmgr_destroy(pjsip_tpmgr *mgr); /** * Dump transport info and status to log. * * @param mgr The transport manager. */ PJ_DECL(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr); /***************************************************************************** * * PUBLIC API * *****************************************************************************/ /** * Find transport to be used to send message to remote destination. If no * suitable transport is found, a new one will be created. * * This is an internal function since normally application doesn't have access * to transport manager. Application should use pjsip_endpt_acquire_transport() * instead. * * @param mgr The transport manager instance. * @param type The type of transport to be acquired. * @param remote The remote address to send message to. * @param addr_len Length of the remote address. * @param sel Optional pointer to transport selector instance which is * used to find explicit transport, if required. * @param tp Pointer to receive the transport instance, if one is found. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr, pjsip_transport_type_e type, const pj_sockaddr_t *remote, int addr_len, const pjsip_tpselector *sel, pjsip_transport **tp); /** * Find suitable transport for sending SIP message to specified remote * destination by also considering the outgoing SIP message. If no suitable * transport is found, a new one will be created. * * This is an internal function since normally application doesn't have access * to transport manager. Application should use pjsip_endpt_acquire_transport2() * instead. * * @param mgr The transport manager instance. * @param type The type of transport to be acquired. * @param remote The remote address to send message to. * @param addr_len Length of the remote address. * @param sel Optional pointer to transport selector instance which is * used to find explicit transport, if required. * @param tdata Optional pointer to data to be sent. * @param tp Pointer to receive the transport instance, if one is found. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, pjsip_transport_type_e type, const pj_sockaddr_t *remote, int addr_len, const pjsip_tpselector *sel, pjsip_tx_data *tdata, pjsip_transport **tp); /** * Type of callback to receive notification when message or raw data * has been sent. * * @param token The token that was given when calling the function * to send message or raw data. * @param tdata The transmit buffer used to send the message. * @param bytes_sent Number of bytes sent. On success, the value will be * positive number indicating the number of bytes sent. * On failure, the value will be a negative number of * the error code (i.e. bytes_sent = -status). */ typedef void (*pjsip_tp_send_callback)(void *token, pjsip_tx_data *tdata, pj_ssize_t bytes_sent); /** * This is a low-level function to send a SIP message using the specified * transport to the specified destination. * * @param tr The SIP transport to be used. * @param tdata Transmit data buffer containing SIP message. * @param addr Destination address. * @param addr_len Length of destination address. * @param token Arbitrary token to be returned back to callback. * @param cb Optional callback to be called to notify caller about * the completion status of the pending send operation. * * @return If the message has been sent successfully, this function * will return PJ_SUCCESS and the callback will not be * called. If message cannot be sent immediately, this * function will return PJ_EPENDING, and application will * be notified later about the completion via the callback. * Any statuses other than PJ_SUCCESS or PJ_EPENDING * indicates immediate failure, and in this case the * callback will not be called. */ PJ_DECL(pj_status_t) pjsip_transport_send( pjsip_transport *tr, pjsip_tx_data *tdata, const pj_sockaddr_t *addr, int addr_len, void *token, pjsip_tp_send_callback cb); /** * This is a low-level function to send raw data to a destination. * * See also #pjsip_endpt_send_raw() and #pjsip_endpt_send_raw_to_uri(). * * @param mgr Transport manager. * @param tp_type Transport type. * @param sel Optional pointer to transport selector instance if * application wants to use a specific transport instance * rather then letting transport manager finds the suitable * transport. * @param tdata Optional transmit data buffer to be used. If this value * is NULL, this function will create one internally. If * tdata is specified, this function will decrement the * reference counter upon completion. * @param raw_data The data to be sent. * @param data_len The length of the data. * @param addr Destination address. * @param addr_len Length of destination address. * @param token Arbitrary token to be returned back to callback. * @param cb Optional callback to be called to notify caller about * the completion status of the pending send operation. * * @return If the message has been sent successfully, this function * will return PJ_SUCCESS and the callback will not be * called. If message cannot be sent immediately, this * function will return PJ_EPENDING, and application will * be notified later about the completion via the callback. * Any statuses other than PJ_SUCCESS or PJ_EPENDING * indicates immediate failure, and in this case the * callback will not be called. */ PJ_DECL(pj_status_t) pjsip_tpmgr_send_raw(pjsip_tpmgr *mgr, pjsip_transport_type_e tp_type, const pjsip_tpselector *sel, pjsip_tx_data *tdata, const void *raw_data, pj_size_t data_len, const pj_sockaddr_t *addr, int addr_len, void *token, pjsip_tp_send_callback cb); /** * Enumeration of transport state types. */ typedef enum pjsip_transport_state { PJSIP_TP_STATE_CONNECTED, /**< Transport connected, applicable only to connection-oriented transports such as TCP and TLS. */ PJSIP_TP_STATE_DISCONNECTED, /**< Transport disconnected, applicable only to connection-oriented transports such as TCP and TLS. */ PJSIP_TP_STATE_SHUTDOWN, /**< Transport shutdown, either due to TCP/TLS disconnect error from the network, or when shutdown is initiated by PJSIP itself. */ PJSIP_TP_STATE_DESTROY, /**< Transport destroy, when transport is about to be destroyed. */ } pjsip_transport_state; /** * Definition of transport state listener key. */ typedef void pjsip_tp_state_listener_key; /** * Structure of transport state info passed by #pjsip_tp_state_callback. */ typedef struct pjsip_transport_state_info { /** * The last error code related to the transport state. */ pj_status_t status; /** * Optional extended info, the content is specific for each transport type. */ void *ext_info; /** * Optional user data. In global transport state notification, this will * always be NULL. */ void *user_data; } pjsip_transport_state_info; /** * Type of callback to receive transport state notifications, such as * transport connected/disconnected. Application may shutdown the transport * in this callback. * * @param tp The transport instance. * @param state The transport state. * @param info The transport state info. */ typedef void (*pjsip_tp_state_callback)( pjsip_transport *tp, pjsip_transport_state state, const pjsip_transport_state_info *info); /** * Set callback of global transport state notification. The caller will be * notified whenever the state of any transport is changed. The type of events * are defined in #pjsip_transport_state. * * Note that this function will override the existing callback, if any, so * application is recommended to keep the old callback and manually forward * the notification to the old callback, otherwise other component that * concerns about the transport state will no longer receive transport state * events. * * @param mgr Transport manager. * @param cb Callback to be called to notify caller about transport * state changing. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tpmgr_set_state_cb(pjsip_tpmgr *mgr, pjsip_tp_state_callback cb); /** * Get the callback of global transport state notification. * * @param mgr Transport manager. * * @return The transport state callback or NULL if it is not set. */ PJ_DECL(pjsip_tp_state_callback) pjsip_tpmgr_get_state_cb( const pjsip_tpmgr *mgr); /** * Add a listener to the specified transport for transport state notification. * * @param tp The transport. * @param cb Callback to be called to notify listener about transport * state changing. * @param user_data The user data. * @param key Output key, used to remove this listener. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_transport_add_state_listener ( pjsip_transport *tp, pjsip_tp_state_callback cb, void *user_data, pjsip_tp_state_listener_key **key); /** * Remove a listener from the specified transport for transport state * notification. * * @param tp The transport. * @param key The listener key. * @param user_data The user data, for validation purpose. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_transport_remove_state_listener ( pjsip_transport *tp, pjsip_tp_state_listener_key *key, const void *user_data); /** * Structure of dropped received data. */ typedef struct pjsip_tp_dropped_data { /** * The transport receiving the data. */ pjsip_transport *tp; /** * The data. */ void *data; /** * The data length. * If the status field below indicates an invalid SIP message * (PJSIP_EINVALIDMSG) and application detects a SIP message * at position p, it can pass the data back to PJSIP to be processed * by setting the len to p. This can be useful for apps which * wishes to use the same transport for SIP signalling and non-SIP * purposes (such as SIP outbound using STUN message). */ pj_size_t len; /** * The status or reason of drop. For example, a leading newlines (common * keep-alive packet) will be dropped with status PJ_EIGNORED, an invalid * SIP message will have status PJSIP_EINVALIDMSG, a SIP message overflow * will have status PJSIP_ERXOVERFLOW. */ pj_status_t status; } pjsip_tp_dropped_data; /** * Type of callback to data dropping notifications. * * @param data The dropped data. */ typedef void (*pjsip_tp_on_rx_dropped_cb)(pjsip_tp_dropped_data *data); /** * Set callback of data dropping. The caller will be notified whenever any * received data is dropped (due to leading newlines or keep-alive packet or * invalid SIP message). This callback can be useful for application, * for example, to implement custom keep-alive mechanism or connection * availability detection. * * @param mgr Transport manager. * @param cb The callback function, set to NULL to reset the callback. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tpmgr_set_drop_data_cb(pjsip_tpmgr *mgr, pjsip_tp_on_rx_dropped_cb cb); /** * @} */ PJ_END_DECL #endif /* __PJSIP_SIP_TRANSPORT_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_transport_loop.h ================================================ /* $Id: sip_transport_loop.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_TRANSPORT_LOOP_H__ #define __PJSIP_TRANSPORT_LOOP_H__ /** * @file sip_transport_loop.h * @brief * Loopback transport (for debugging) */ #include /** * @defgroup PJSIP_TRANSPORT_LOOP Loop Transport * @ingroup PJSIP_TRANSPORT * @brief Loopback transport (for testing purposes). * @{ * The loopback transport simply bounce back outgoing messages as * incoming messages. This feature is used mostly during automated * testing, to provide controlled behavior. */ PJ_BEGIN_DECL /** * Create and start datagram loop transport. * * @param endpt The endpoint instance. * @param transport Pointer to receive the transport instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_loop_start( pjsip_endpoint *endpt, pjsip_transport **transport); /** * Enable/disable flag to discard any packets sent using the specified * loop transport. * * @param tp The loop transport. * @param discard If non-zero, any outgoing packets will be discarded. * @param prev_value Optional argument to receive previous value of * the discard flag. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_loop_set_discard( pjsip_transport *tp, pj_bool_t discard, pj_bool_t *prev_value ); /** * Enable/disable flag to simulate network error. When this flag is set, * outgoing transmission will return either immediate error or error via * callback. If error is to be notified via callback, then the notification * will occur after some delay, which is controlled by #pjsip_loop_set_delay(). * * @param tp The loop transport. * @param fail_flag If set to 1, the transport will return fail to deliver * the message. If delay is zero, failure will occur * immediately; otherwise it will be reported in callback. * If set to zero, the transport will successfully deliver * the packet. * @param prev_value Optional argument to receive previous value of * the failure flag. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_loop_set_failure( pjsip_transport *tp, int fail_flag, int *prev_value ); /** * Set delay (in miliseconds) before packet is received by the other end * of the loop transport. This will also * control the delay for error notification callback. * * @param tp The loop transport. * @param delay Delay, in miliseconds. * @param prev_value Optional argument to receive previous value of the * delay. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_loop_set_recv_delay( pjsip_transport *tp, unsigned delay, unsigned *prev_value); /** * Set delay (in miliseconds) before send notification is delivered to sender. * This will also control the delay for error notification callback. * * @param tp The loop transport. * @param delay Delay, in miliseconds. * @param prev_value Optional argument to receive previous value of the * delay. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_loop_set_send_callback_delay( pjsip_transport *tp, unsigned delay, unsigned *prev_value); /** * Set both receive and send notification delay. * * @param tp The loop transport. * @param delay Delay, in miliseconds. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_loop_set_delay( pjsip_transport *tp, unsigned delay ); PJ_END_DECL /** * @} */ #endif /* __PJSIP_TRANSPORT_LOOP_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_transport_tcp.h ================================================ /* $Id: sip_transport_tcp.h 4506 2013-04-26 06:01:43Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_TRANSPORT_TCP_H__ #define __PJSIP_TRANSPORT_TCP_H__ /** * @file sip_transport_tcp.h * @brief SIP TCP Transport. */ #include #include /* Only declare the API if PJ_HAS_TCP is true */ #if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0 PJ_BEGIN_DECL /** * @defgroup PJSIP_TRANSPORT_TCP TCP Transport * @ingroup PJSIP_TRANSPORT * @brief API to create and register TCP transport. * @{ * The functions below are used to create TCP transport and register * the transport to the framework. */ /** * Settings to be specified when creating the TCP transport. Application * should initialize this structure with its default values by calling * pjsip_tcp_transport_cfg_default(). */ typedef struct pjsip_tcp_transport_cfg { /** * Address family to use. Valid values are pj_AF_INET() and * pj_AF_INET6(). Default is pj_AF_INET(). */ int af; /** * Optional address to bind the socket to. Default is to bind to * PJ_INADDR_ANY and to any available port. */ pj_sockaddr bind_addr; /** * Should SO_REUSEADDR be used for the listener socket. * Default value is PJSIP_TCP_TRANSPORT_REUSEADDR. */ pj_bool_t reuse_addr; /** * Optional published address, which is the address to be * advertised as the address of this SIP transport. * By default the bound address will be used as the published address. */ pjsip_host_port addr_name; /** * Number of simultaneous asynchronous accept() operations to be * supported. It is recommended that the number here corresponds to * the number of processors in the system (or the number of SIP * worker threads). * * Default: 1 */ unsigned async_cnt; /** * QoS traffic type to be set on this transport. When application wants * to apply QoS tagging to the transport, it's preferable to set this * field rather than \a qos_param fields since this is more portable. * * Default is QoS not set. */ pj_qos_type qos_type; /** * Set the low level QoS parameters to the transport. This is a lower * level operation than setting the \a qos_type field and may not be * supported on all platforms. * * Default is QoS not set. */ pj_qos_params qos_params; /** * Specify options to be set on the transport. * * By default there is no options. * */ pj_sockopt_params sockopt_params; } pjsip_tcp_transport_cfg; /** * Initialize pjsip_tcp_transport_cfg structure with default values for * the specifed address family. * * @param cfg The structure to initialize. * @param af Address family to be used. */ PJ_DECL(void) pjsip_tcp_transport_cfg_default(pjsip_tcp_transport_cfg *cfg, int af); /** * Register support for SIP TCP transport by creating TCP listener on * the specified address and port. This function will create an * instance of SIP TCP transport factory and register it to the * transport manager. * * @param endpt The SIP endpoint. * @param local Optional local address to bind, or specify the * address to bind the server socket to. Both IP * interface address and port fields are optional. * If IP interface address is not specified, socket * will be bound to PJ_INADDR_ANY. If port is not * specified, socket will be bound to any port * selected by the operating system. * @param async_cnt Number of simultaneous asynchronous accept() * operations to be supported. It is recommended that * the number here corresponds to the number of * processors in the system (or the number of SIP * worker threads). * @param p_factory Optional pointer to receive the instance of the * SIP TCP transport factory just created. * * @return PJ_SUCCESS when the transport has been successfully * started and registered to transport manager, or * the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tcp_transport_start(pjsip_endpoint *endpt, const pj_sockaddr_in *local, unsigned async_cnt, pjsip_tpfactory **p_factory); /** * A newer variant of #pjsip_tcp_transport_start(), which allows specifying * the published/public address of the TCP transport. * * @param endpt The SIP endpoint. * @param local Optional local address to bind, or specify the * address to bind the server socket to. Both IP * interface address and port fields are optional. * If IP interface address is not specified, socket * will be bound to PJ_INADDR_ANY. If port is not * specified, socket will be bound to any port * selected by the operating system. * @param a_name Optional published address, which is the address to be * advertised as the address of this SIP transport. * If this argument is NULL, then the bound address * will be used as the published address. * @param async_cnt Number of simultaneous asynchronous accept() * operations to be supported. It is recommended that * the number here corresponds to the number of * processors in the system (or the number of SIP * worker threads). * @param p_factory Optional pointer to receive the instance of the * SIP TCP transport factory just created. * * @return PJ_SUCCESS when the transport has been successfully * started and registered to transport manager, or * the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt, const pj_sockaddr_in *local, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory); /** * Another variant of #pjsip_tcp_transport_start(). * * @param endpt The SIP endpoint. * @param cfg TCP transport settings. Application should initialize * this setting with #pjsip_tcp_transport_cfg_default(). * @param p_factory Optional pointer to receive the instance of the * SIP TCP transport factory just created. * * @return PJ_SUCCESS when the transport has been successfully * started and registered to transport manager, or * the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tcp_transport_start3( pjsip_endpoint *endpt, const pjsip_tcp_transport_cfg *cfg, pjsip_tpfactory **p_factory ); /** * Retrieve the internal socket handle used by the TCP transport. Note * that this socket normally is registered to ioqueue, so application * needs to take care not to perform operation that disrupts ioqueue * operation. * * @param transport The TCP transport. * * @return The socket handle, or PJ_INVALID_SOCKET if no socket * is currently being used. */ PJ_DECL(pj_sock_t) pjsip_tcp_transport_get_socket(pjsip_transport *transport); PJ_END_DECL /** * @} */ #endif /* PJ_HAS_TCP */ #endif /* __PJSIP_TRANSPORT_TCP_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_transport_tls.h ================================================ /* $Id: sip_transport_tls.h 4506 2013-04-26 06:01:43Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_TRANSPORT_TLS_H__ #define __PJSIP_TRANSPORT_TLS_H__ /** * @file sip_transport_tls.h * @brief SIP TLS Transport. */ #include #include #include #include #include PJ_BEGIN_DECL /** * @defgroup PJSIP_TRANSPORT_TLS TLS Transport * @ingroup PJSIP_TRANSPORT * @brief API to create and register TLS transport. * @{ * The functions below are used to create TLS transport and register * the transport to the framework. */ /** * The default SSL method to be used by PJSIP. * Default is PJSIP_TLSV1_METHOD */ #ifndef PJSIP_SSL_DEFAULT_METHOD # define PJSIP_SSL_DEFAULT_METHOD PJSIP_TLSV1_METHOD #endif /** SSL protocol method constants. */ typedef enum pjsip_ssl_method { PJSIP_SSL_UNSPECIFIED_METHOD = 0, /**< Default protocol method. */ PJSIP_SSLV2_METHOD = 20, /**< Use SSLv2 method. */ PJSIP_SSLV3_METHOD = 30, /**< Use SSLv3 method. */ PJSIP_TLSV1_METHOD = 31, /**< Use TLSv1 method. */ PJSIP_TLSV1_1_METHOD = 32, /**< Use TLSv1_1 method. */ PJSIP_TLSV1_2_METHOD = 33, /**< Use TLSv1_2 method. */ PJSIP_SSLV23_METHOD = 23, /**< Use SSLv23 method. */ } pjsip_ssl_method; /** * The default enabled SSL proto to be used. * Default is all protocol above TLSv1 (TLSv1 & TLS v1.1 & TLS v1.2). */ #ifndef PJSIP_SSL_DEFAULT_PROTO # define PJSIP_SSL_DEFAULT_PROTO (PJ_SSL_SOCK_PROTO_TLS1 | \ PJ_SSL_SOCK_PROTO_TLS1_1 | \ PJ_SSL_SOCK_PROTO_TLS1_2) #endif /** * TLS transport settings. */ typedef struct pjsip_tls_setting { /** * Certificate of Authority (CA) list file. */ pj_str_t ca_list_file; /** * Certificate of Authority (CA) list directory path. */ pj_str_t ca_list_path; /** * Public endpoint certificate file, which will be used as client- * side certificate for outgoing TLS connection, and server-side * certificate for incoming TLS connection. */ pj_str_t cert_file; /** * Optional private key of the endpoint certificate to be used. */ pj_str_t privkey_file; /** * Password to open private key. */ pj_str_t password; /** * TLS protocol method from #pjsip_ssl_method. In the future, this field * might be deprecated in favor of proto field. For now, this field * is only applicable only when proto field is set to zero. * * Default is PJSIP_SSL_UNSPECIFIED_METHOD (0), which in turn will * use PJSIP_SSL_DEFAULT_METHOD, which default value is PJSIP_TLSV1_METHOD. */ pjsip_ssl_method method; /** * TLS protocol type from #pj_ssl_sock_proto. Use this field to enable * specific protocol type. Use bitwise OR operation to combine the protocol * type. * * Default is PJSIP_SSL_DEFAULT_PROTO. */ pj_uint32_t proto; /** * Number of ciphers contained in the specified cipher preference. * If this is set to zero, then default cipher list of the backend * will be used. * * Default: 0 (zero). */ unsigned ciphers_num; /** * Ciphers and order preference. The #pj_ssl_cipher_get_availables() * can be used to check the available ciphers supported by backend. */ pj_ssl_cipher *ciphers; /** * Specifies TLS transport behavior on the server TLS certificate * verification result: * - If \a verify_server is disabled (set to PJ_FALSE), TLS transport * will just notify the application via #pjsip_tp_state_callback with * state PJSIP_TP_STATE_CONNECTED regardless TLS verification result. * - If \a verify_server is enabled (set to PJ_TRUE), TLS transport * will be shutdown and application will be notified with state * PJSIP_TP_STATE_DISCONNECTED whenever there is any TLS verification * error, otherwise PJSIP_TP_STATE_CONNECTED will be notified. * * In any cases, application can inspect #pjsip_tls_state_info in the * callback to see the verification detail. * * Default value is PJ_FALSE. */ pj_bool_t verify_server; /** * Specifies TLS transport behavior on the client TLS certificate * verification result: * - If \a verify_client is disabled (set to PJ_FALSE), TLS transport * will just notify the application via #pjsip_tp_state_callback with * state PJSIP_TP_STATE_CONNECTED regardless TLS verification result. * - If \a verify_client is enabled (set to PJ_TRUE), TLS transport * will be shutdown and application will be notified with state * PJSIP_TP_STATE_DISCONNECTED whenever there is any TLS verification * error, otherwise PJSIP_TP_STATE_CONNECTED will be notified. * * In any cases, application can inspect #pjsip_tls_state_info in the * callback to see the verification detail. * * Default value is PJ_FALSE. */ pj_bool_t verify_client; /** * When acting as server (incoming TLS connections), reject inocming * connection if client doesn't supply a TLS certificate. * * This setting corresponds to SSL_VERIFY_FAIL_IF_NO_PEER_CERT flag. * Default value is PJ_FALSE. */ pj_bool_t require_client_cert; /** * TLS negotiation timeout to be applied for both outgoing and * incoming connection. If both sec and msec member is set to zero, * the SSL negotiation doesn't have a timeout. */ pj_time_val timeout; /** * Should SO_REUSEADDR be used for the listener socket. * Default value is PJSIP_TLS_TRANSPORT_REUSEADDR. */ pj_bool_t reuse_addr; /** * QoS traffic type to be set on this transport. When application wants * to apply QoS tagging to the transport, it's preferable to set this * field rather than \a qos_param fields since this is more portable. * * Default value is PJ_QOS_TYPE_BEST_EFFORT. */ pj_qos_type qos_type; /** * Set the low level QoS parameters to the transport. This is a lower * level operation than setting the \a qos_type field and may not be * supported on all platforms. * * By default all settings in this structure are disabled. */ pj_qos_params qos_params; /** * Specify if the transport should ignore any errors when setting the QoS * traffic type/parameters. * * Default: PJ_TRUE */ pj_bool_t qos_ignore_error; /** * Specify options to be set on the transport. * * By default there is no options. * */ pj_sockopt_params sockopt_params; /** * Specify if the transport should ignore any errors when setting the * sockopt parameters. * * Default: PJ_TRUE * */ pj_bool_t sockopt_ignore_error; } pjsip_tls_setting; /** * This structure defines TLS transport extended info in ext_info * field of #pjsip_transport_state_info for the transport state notification * callback #pjsip_tp_state_callback. */ typedef struct pjsip_tls_state_info { /** * SSL socket info. */ pj_ssl_sock_info *ssl_sock_info; } pjsip_tls_state_info; /** * Initialize TLS setting with default values. * * @param tls_opt The TLS setting to be initialized. */ PJ_INLINE(void) pjsip_tls_setting_default(pjsip_tls_setting *tls_opt) { pj_memset(tls_opt, 0, sizeof(*tls_opt)); tls_opt->reuse_addr = PJSIP_TLS_TRANSPORT_REUSEADDR; tls_opt->qos_type = PJ_QOS_TYPE_BEST_EFFORT; tls_opt->qos_ignore_error = PJ_TRUE; tls_opt->sockopt_ignore_error = PJ_TRUE; tls_opt->proto = PJSIP_SSL_DEFAULT_PROTO; } /** * Copy TLS setting. * * @param pool The pool to duplicate strings etc. * @param dst Destination structure. * @param src Source structure. */ PJ_INLINE(void) pjsip_tls_setting_copy(pj_pool_t *pool, pjsip_tls_setting *dst, const pjsip_tls_setting *src) { pj_memcpy(dst, src, sizeof(*dst)); pj_strdup_with_null(pool, &dst->ca_list_file, &src->ca_list_file); pj_strdup_with_null(pool, &dst->ca_list_path, &src->ca_list_path); pj_strdup_with_null(pool, &dst->cert_file, &src->cert_file); pj_strdup_with_null(pool, &dst->privkey_file, &src->privkey_file); pj_strdup_with_null(pool, &dst->password, &src->password); if (src->ciphers_num) { unsigned i; dst->ciphers = (pj_ssl_cipher*) pj_pool_calloc(pool, src->ciphers_num, sizeof(pj_ssl_cipher)); for (i=0; iciphers_num; ++i) dst->ciphers[i] = src->ciphers[i]; } } /** * Register support for SIP TLS transport by creating TLS listener on * the specified address and port. This function will create an * instance of SIP TLS transport factory and register it to the * transport manager. * * See also #pjsip_tls_transport_start2() which supports IPv6. * * @param endpt The SIP endpoint. * @param opt Optional TLS settings. * @param local Optional local address to bind, or specify the * address to bind the server socket to. Both IP * interface address and port fields are optional. * If IP interface address is not specified, socket * will be bound to PJ_INADDR_ANY. If port is not * specified, socket will be bound to any port * selected by the operating system. * @param a_name Optional published address, which is the address to be * advertised as the address of this SIP transport. * If this argument is NULL, then the bound address * will be used as the published address. * @param async_cnt Number of simultaneous asynchronous accept() * operations to be supported. It is recommended that * the number here corresponds to the number of * processors in the system (or the number of SIP * worker threads). * @param p_factory Optional pointer to receive the instance of the * SIP TLS transport factory just created. * * @return PJ_SUCCESS when the transport has been successfully * started and registered to transport manager, or * the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tls_transport_start(pjsip_endpoint *endpt, const pjsip_tls_setting *opt, const pj_sockaddr_in *local, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory); /** * Variant of #pjsip_tls_transport_start() that supports IPv6. To instantiate * IPv6 listener, set the address family of the "local" argument to IPv6 * (the host and port part may be left unspecified if not desired, i.e. by * filling them with zeroes). * * @param endpt The SIP endpoint. * @param opt Optional TLS settings. * @param local Optional local address to bind, or specify the * address to bind the server socket to. Both IP * interface address and port fields are optional. * If IP interface address is not specified, socket * will be bound to any address. If port is not * specified, socket will be bound to any port * selected by the operating system. * @param a_name Optional published address, which is the address to be * advertised as the address of this SIP transport. * If this argument is NULL, then the bound address * will be used as the published address. * @param async_cnt Number of simultaneous asynchronous accept() * operations to be supported. It is recommended that * the number here corresponds to the number of * processors in the system (or the number of SIP * worker threads). * @param p_factory Optional pointer to receive the instance of the * SIP TLS transport factory just created. * * @return PJ_SUCCESS when the transport has been successfully * started and registered to transport manager, or * the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_tls_transport_start2(pjsip_endpoint *endpt, const pjsip_tls_setting *opt, const pj_sockaddr *local, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory); PJ_END_DECL /** * @} */ #endif /* __PJSIP_TRANSPORT_TLS_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_transport_udp.h ================================================ /* $Id: sip_transport_udp.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_TRANSPORT_UDP_H__ #define __PJSIP_TRANSPORT_UDP_H__ /** * @file sip_transport_udp.h * @brief SIP UDP Transport. */ #include PJ_BEGIN_DECL /** * @defgroup PJSIP_TRANSPORT_UDP UDP Transport * @ingroup PJSIP_TRANSPORT * @brief API to create and register UDP transport. * @{ * The functions below are used to create UDP transport and register * the transport to the framework. */ /** * Flag that can be specified when calling #pjsip_udp_transport_pause() or * #pjsip_udp_transport_restart(). */ enum { /** * This flag tells the transport to keep the existing/internal socket * handle. */ PJSIP_UDP_TRANSPORT_KEEP_SOCKET = 1, /** * This flag tells the transport to destroy the existing/internal socket * handle. Naturally this flag and PJSIP_UDP_TRANSPORT_KEEP_SOCKET are * mutually exclusive. */ PJSIP_UDP_TRANSPORT_DESTROY_SOCKET = 2 }; /** * Start UDP transport. * * @param endpt The SIP endpoint. * @param local Optional local address to bind. If this argument * is NULL, the UDP transport will be bound to arbitrary * UDP port. * @param a_name Published address (only the host and port portion is * used). If this argument is NULL, then the bound address * will be used as the published address. * @param async_cnt Number of simultaneous async operations. * @param p_transport Pointer to receive the transport. * * @return PJ_SUCCESS when the transport has been successfully * started and registered to transport manager, or * the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_udp_transport_start(pjsip_endpoint *endpt, const pj_sockaddr_in *local, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_transport **p_transport); /** * Start IPv6 UDP transport. */ PJ_DECL(pj_status_t) pjsip_udp_transport_start6(pjsip_endpoint *endpt, const pj_sockaddr_in6 *local, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_transport **p_transport); /** * Attach IPv4 UDP socket as a new transport and start the transport. * * @param endpt The SIP endpoint. * @param sock UDP socket to use. * @param a_name Published address (only the host and port portion is * used). * @param async_cnt Number of simultaneous async operations. * @param p_transport Pointer to receive the transport. * * @return PJ_SUCCESS when the transport has been successfully * started and registered to transport manager, or * the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_udp_transport_attach(pjsip_endpoint *endpt, pj_sock_t sock, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_transport **p_transport); /** * Attach IPv4 or IPv6 UDP socket as a new transport and start the transport. * * @param endpt The SIP endpoint. * @param type Transport type, which is PJSIP_TRANSPORT_UDP for IPv4 * or PJSIP_TRANSPORT_UDP6 for IPv6 socket. * @param sock UDP socket to use. * @param a_name Published address (only the host and port portion is * used). * @param async_cnt Number of simultaneous async operations. * @param p_transport Pointer to receive the transport. * * @return PJ_SUCCESS when the transport has been successfully * started and registered to transport manager, or * the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_udp_transport_attach2(pjsip_endpoint *endpt, pjsip_transport_type_e type, pj_sock_t sock, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_transport **p_transport); /** * Retrieve the internal socket handle used by the UDP transport. Note * that this socket normally is registered to ioqueue, so if application * wants to make use of this socket, it should temporarily pause the * transport. * * @param transport The UDP transport. * * @return The socket handle, or PJ_INVALID_SOCKET if no socket * is currently being used (for example, when transport * is being paused). */ PJ_DECL(pj_sock_t) pjsip_udp_transport_get_socket(pjsip_transport *transport); /** * Temporarily pause or shutdown the transport. When transport is being * paused, it cannot be used by the SIP stack to send or receive SIP * messages. * * Two types of operations are supported by this function: * - to temporarily make this transport unavailable for SIP uses, but * otherwise keep the socket handle intact. Application then can * retrieve the socket handle with #pjsip_udp_transport_get_socket() * and use it to send/receive application data (for example, STUN * messages). In this case, application should specify * PJSIP_UDP_TRANSPORT_KEEP_SOCKET when calling this function, and * also to specify this flag when calling #pjsip_udp_transport_restart() * later. * - to temporarily shutdown the transport, including closing down * the internal socket handle. This is useful for example to * temporarily suspend the application for an indefinite period. In * this case, application should specify PJSIP_UDP_TRANSPORT_DESTROY_SOCKET * flag when calling this function, and specify a new socket when * calling #pjsip_udp_transport_restart(). * * @param transport The UDP transport. * @param option Pause option. * * @return PJ_SUCCESS if transport is paused successfully, * or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_udp_transport_pause(pjsip_transport *transport, unsigned option); /** * Restart the transport. Several operations are supported by this function: * - if transport was made temporarily unavailable to SIP stack with * pjsip_udp_transport_pause() and PJSIP_UDP_TRANSPORT_KEEP_SOCKET, * application can make the transport available to the SIP stack * again, by specifying PJSIP_UDP_TRANSPORT_KEEP_SOCKET flag here. * - if application wants to replace the internal socket with a new * socket, it must specify PJSIP_UDP_TRANSPORT_DESTROY_SOCKET when * calling this function, so that the internal socket will be destroyed * if it hasn't been closed. In this case, application has two choices * on how to create the new socket: 1) to let the transport create * the new socket, in this case the \a sock option should be set * to \a PJ_INVALID_SOCKET and optionally the \a local parameter can be * filled with the desired address and port where the new socket * should be bound to, or 2) to specify its own socket to be used * by this transport, by specifying a valid socket in \a sock argument * and set the \a local argument to NULL. In both cases, application * may specify the published address of the socket in \a a_name * argument. * * @param transport The UDP transport. * @param option Restart option. * @param sock Optional socket to be used by the transport. * @param local The address where the socket should be bound to. * If this argument is NULL, socket will be bound * to any available port. * @param a_name Optionally specify the published address for * this transport. If the socket is not replaced * (PJSIP_UDP_TRANSPORT_KEEP_SOCKET flag is * specified), then if this argument is NULL, the * previous value will be used. If the socket is * replaced and this argument is NULL, the bound * address will be used as the published address * of the transport. * * @return PJ_SUCCESS if transport can be restarted, or * the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_udp_transport_restart(pjsip_transport *transport, unsigned option, pj_sock_t sock, const pj_sockaddr_in *local, const pjsip_host_port *a_name); PJ_END_DECL /** * @} */ #endif /* __PJSIP_TRANSPORT_UDP_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_types.h ================================================ /* $Id: sip_types.h 4262 2012-09-20 06:00:23Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_TYPES_H__ #define __PJSIP_SIP_TYPES_H__ /* * My note: Doxygen PJSIP and PJSIP_CORE group is declared in * sip_config.h */ /** * @file sip_types.h * @brief Basic PJSIP types. */ #include #include /** * @addtogroup PJSIP_BASE */ /* @defgroup PJSIP_TYPES Basic Data Types * @ingroup PJSIP_BASE * @brief Basic data types. * @{ */ /** * Forward declaration for SIP transport. */ typedef struct pjsip_transport pjsip_transport; /** * Forward declaration for transport manager. */ typedef struct pjsip_tpmgr pjsip_tpmgr; /** * Transport types. */ typedef enum pjsip_transport_type_e { /** Unspecified. */ PJSIP_TRANSPORT_UNSPECIFIED, /** UDP. */ PJSIP_TRANSPORT_UDP, /** TCP. */ PJSIP_TRANSPORT_TCP, /** TLS. */ PJSIP_TRANSPORT_TLS, /** SCTP. */ PJSIP_TRANSPORT_SCTP, /** Loopback (stream, reliable) */ PJSIP_TRANSPORT_LOOP, /** Loopback (datagram, unreliable) */ PJSIP_TRANSPORT_LOOP_DGRAM, /** Start of user defined transport */ PJSIP_TRANSPORT_START_OTHER, /** Start of IPv6 transports */ PJSIP_TRANSPORT_IPV6 = 128, /** UDP over IPv6 */ PJSIP_TRANSPORT_UDP6 = PJSIP_TRANSPORT_UDP + PJSIP_TRANSPORT_IPV6, /** TCP over IPv6 */ PJSIP_TRANSPORT_TCP6 = PJSIP_TRANSPORT_TCP + PJSIP_TRANSPORT_IPV6, /** TLS over IPv6 */ PJSIP_TRANSPORT_TLS6 = PJSIP_TRANSPORT_TLS + PJSIP_TRANSPORT_IPV6 } pjsip_transport_type_e; /** * Forward declaration for endpoint (sip_endpoint.h). */ typedef struct pjsip_endpoint pjsip_endpoint; /** * Forward declaration for transactions (sip_transaction.h). */ typedef struct pjsip_transaction pjsip_transaction; /** * Forward declaration for events (sip_event.h). */ typedef struct pjsip_event pjsip_event; /** * Forward declaration for transmit data/buffer (sip_transport.h). */ typedef struct pjsip_tx_data pjsip_tx_data; /** * Forward declaration for receive data/buffer (sip_transport.h). */ typedef struct pjsip_rx_data pjsip_rx_data; /** * Forward declaration for message (sip_msg.h). */ typedef struct pjsip_msg pjsip_msg; /** * Forward declaration for message body (sip_msg.h). */ typedef struct pjsip_msg_body pjsip_msg_body; /** * Forward declaration for header field (sip_msg.h). */ typedef struct pjsip_hdr pjsip_hdr; /** * Forward declaration for URI (sip_uri.h). */ typedef struct pjsip_uri pjsip_uri; /** * Forward declaration for SIP method (sip_msg.h) */ typedef struct pjsip_method pjsip_method; /** * Opaque data type for the resolver engine (sip_resolve.h). */ typedef struct pjsip_resolver_t pjsip_resolver_t; /** * Forward declaration for credential. */ typedef struct pjsip_cred_info pjsip_cred_info; /** * Forward declaration for module (sip_module.h). */ typedef struct pjsip_module pjsip_module; /** * Forward declaration for user agent type (sip_ua_layer.h). */ typedef pjsip_module pjsip_user_agent; /** * Forward declaration for dialog (sip_dialog.h). */ typedef struct pjsip_dialog pjsip_dialog; /** * Transaction role. */ typedef enum pjsip_role_e { PJSIP_ROLE_UAC, /**< Role is UAC. */ PJSIP_ROLE_UAS, /**< Role is UAS. */ /* Alias: */ PJSIP_UAC_ROLE = PJSIP_ROLE_UAC, /**< Role is UAC. */ PJSIP_UAS_ROLE = PJSIP_ROLE_UAS /**< Role is UAS. */ } pjsip_role_e; /** * General purpose buffer. */ typedef struct pjsip_buffer { /** The start of the buffer. */ char *start; /** Pointer to current end of the buffer, which also indicates the position of subsequent buffer write. */ char *cur; /** The absolute end of the buffer. */ char *end; } pjsip_buffer; /** * General host:port pair, used for example as Via sent-by. */ typedef struct pjsip_host_port { pj_str_t host; /**< Host part or IP address. */ int port; /**< Port number. */ } pjsip_host_port; /** * Host information. */ typedef struct pjsip_host_info { unsigned flag; /**< Flags of pjsip_transport_flags_e. */ pjsip_transport_type_e type; /**< Transport type. */ pjsip_host_port addr; /**< Address information. */ } pjsip_host_info; /** * Convert exception ID into pj_status_t status. * * @param exception_id Exception Id. * * @return Error code for the specified exception Id. */ PJ_DECL(pj_status_t) pjsip_exception_to_status(int exception_id); /** * Return standard pj_status_t status from current exception. */ #define PJSIP_RETURN_EXCEPTION() pjsip_exception_to_status(PJ_GET_EXCEPTION()) /** * Attributes to inform that the function may throw exceptions. */ #define PJSIP_THROW_SPEC(list) /** * @} */ #endif /* __PJSIP_SIP_TYPES_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_ua_layer.h ================================================ /* $Id: sip_ua_layer.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_UA_LAYER_H__ #define __PJSIP_SIP_UA_LAYER_H__ /** * @file sip_ua_layer.h * @brief SIP User Agent Layer Module */ #include PJ_BEGIN_DECL /** * @defgroup PJSIP_UA Base User Agent Layer/Common Dialog Layer * @brief Dialog management. * * This module provides basic dialog management, which is used by higher * layer dialog usages such as INVITE sessions and SIP Event Subscription * framework (RFC 3265). Application should link with pjsip-core * library to use this base UA layer. The base UA layer module is initialized * with #pjsip_ua_init_module(). */ /** * @defgroup PJSUA_UA SIP User Agent Module * @ingroup PJSIP_UA * @brief Provides dialog management. * @{ * * Application MUST initialize the user agent layer module by calling * #pjsip_ua_init_module() before using any of the dialog API, and link * the application with with pjsip-core library. */ /** User agent initialization parameter. */ typedef struct pjsip_ua_init_param { /** Callback to be called when the UA layer detects that outgoing * dialog has forked. */ pjsip_dialog* (*on_dlg_forked)(pjsip_dialog *first_set, pjsip_rx_data *res); } pjsip_ua_init_param; /** * Initialize user agent layer and register it to the specified endpoint. * * @param endpt The endpoint where the user agent will be * registered. * @param prm UA initialization parameter. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_ua_init_module(pjsip_endpoint *endpt, const pjsip_ua_init_param *prm); /** * Get the instance of the user agent. * * @return The user agent module instance. */ PJ_DECL(pjsip_user_agent*) pjsip_ua_instance(void); /** * Retrieve the current number of dialog-set currently registered * in the hash table. Note that dialog-set is different than dialog * when the request forks. In this case, all dialogs created from * the original request will belong to the same dialog set. When * no forking occurs, the number of dialog sets will be equal to * the number of dialogs. * * @return Number of dialog sets. */ PJ_DECL(pj_uint32_t) pjsip_ua_get_dlg_set_count(void); /** * Find a dialog with the specified Call-ID and tags properties. This * function may optionally lock the matching dialog instance before * returning it back to the caller. * * @param call_id The call ID to be matched. * @param local_tag The local tag to be matched. * @param remote_tag The remote tag to be matched. * @param lock_dialog If non-zero, instruct the function to lock the * matching dialog with #pjsip_dlg_inc_lock(). * Application is responsible to release the dialog's * lock after it has finished manipulating the dialog, * by calling #pjsip_dlg_dec_lock(). * * @return The matching dialog instance, or NULL if no matching * dialog is found. */ PJ_DECL(pjsip_dialog*) pjsip_ua_find_dialog(const pj_str_t *call_id, const pj_str_t *local_tag, const pj_str_t *remote_tag, pj_bool_t lock_dialog); /** * Destroy the user agent layer. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_ua_destroy(void); /** * Dump user agent contents (e.g. all dialogs). * * @param detail If non-zero, list of dialogs will be printed. */ PJ_DECL(void) pjsip_ua_dump(pj_bool_t detail); /** * Get the endpoint instance of a user agent module. * * @param ua The user agent instance. * * @return The endpoint instance where the user agent is * registered. */ PJ_DECL(pjsip_endpoint*) pjsip_ua_get_endpt(pjsip_user_agent *ua); /** * @} */ /* * Internal (called by sip_dialog.c). */ PJ_DECL(pj_status_t) pjsip_ua_register_dlg( pjsip_user_agent *ua, pjsip_dialog *dlg ); PJ_DECL(pj_status_t) pjsip_ua_unregister_dlg(pjsip_user_agent *ua, pjsip_dialog *dlg ); PJ_END_DECL #endif /* __PJSIP_SIP_UA_LAYER_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_uri.h ================================================ /* $Id: sip_uri.h 4370 2013-02-26 05:30:00Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_URI_H__ #define __PJSIP_SIP_URI_H__ /** * @file sip_uri.h * @brief SIP URL Structures and Manipulations */ #include #include #include #include PJ_BEGIN_DECL /** * @defgroup PJSIP_URI URI * @brief URI types and manipulations. * @ingroup PJSIP_MSG */ /** * @addtogroup PJSIP_URI_PARAM URI Parameter Container * @ingroup PJSIP_URI * @brief Generic parameter elements container. * @{ */ /** * Generic parameter, normally used in other_param or header_param. */ typedef struct pjsip_param { PJ_DECL_LIST_MEMBER(struct pjsip_param); /**< Generic list member. */ pj_str_t name; /**< Param/header name. */ pj_str_t value; /**< Param/header value. */ } pjsip_param; /** * Find the specified parameter name in the list. The name will be compared * in case-insensitive comparison. * * @param param_list List of parameters to find. * @param name Parameter/header name to find. * * @return The parameter if found, or NULL. */ PJ_DECL(pjsip_param*) pjsip_param_find( const pjsip_param *param_list, const pj_str_t *name ); /** * Alias for pjsip_param_find() */ PJ_INLINE(pjsip_param*) pjsip_param_cfind(const pjsip_param *param_list, const pj_str_t *name) { return pjsip_param_find(param_list, name); } /** * Compare two parameter lists. * * @param param_list1 First parameter list. * @param param_list2 Second parameter list. * @param ig_nf If set to 1, do not compare parameters that only * appear in one of the list. * * @return Zero if the parameter list are equal, non-zero * otherwise. */ PJ_DECL(int) pjsip_param_cmp(const pjsip_param *param_list1, const pjsip_param *param_list2, pj_bool_t ig_nf); /** * Duplicate the parameters. * * @param pool Pool to allocate memory from. * @param dst_list Destination list. * @param src_list Source list. */ PJ_DECL(void) pjsip_param_clone(pj_pool_t *pool, pjsip_param *dst_list, const pjsip_param *src_list); /** * Duplicate the parameters. * * @param pool Pool to allocate memory from. * @param dst_list Destination list. * @param src_list Source list. */ PJ_DECL(void) pjsip_param_shallow_clone(pj_pool_t *pool, pjsip_param *dst_list, const pjsip_param *src_list); /** * Print parameters. * * @param param_list The parameter list. * @param buf Buffer. * @param size Size of buffer. * @param pname_unres Specification of allowed characters in pname. * @param pvalue_unres Specification of allowed characters in pvalue. * @param sep Separator character (either ';', ',', or '?'). * When separator is set to '?', this function will * automatically adjust the separator character to * '&' after the first parameter is printed. * * @return The number of bytes printed, or -1 on errr. */ PJ_DECL(pj_ssize_t) pjsip_param_print_on(const pjsip_param *param_list, char *buf, pj_size_t size, const pj_cis_t *pname_unres, const pj_cis_t *pvalue_unres, int sep); /** * @} */ /** * @defgroup PJSIP_URI_GENERIC Generic URI * @ingroup PJSIP_URI * @brief Generic representation for all types of URI. * @{ */ /** * URI context. */ typedef enum pjsip_uri_context_e { PJSIP_URI_IN_REQ_URI, /**< The URI is in Request URI. */ PJSIP_URI_IN_FROMTO_HDR, /**< The URI is in From/To header. */ PJSIP_URI_IN_CONTACT_HDR, /**< The URI is in Contact header. */ PJSIP_URI_IN_ROUTING_HDR, /**< The URI is in Route/Record-Route header. */ PJSIP_URI_IN_OTHER /**< Other context (web page, business card, etc.) */ } pjsip_uri_context_e; /** * URI 'virtual' function table. * All types of URI in this library (such as sip:, sips:, tel:, and name-addr) * will have pointer to this table as their first struct member. This table * provides polimorphic behaviour to the URI. */ typedef struct pjsip_uri_vptr { /** * Get URI scheme. * @param uri the URI (self). * @return the URI scheme. */ const pj_str_t* (*p_get_scheme)(const void *uri); /** * Get the URI object contained by this URI, or the URI itself if * it doesn't contain another URI. * @param uri the URI (self). */ void* (*p_get_uri)(void *uri); /** * Print URI components to the buffer, following the rule of which * components are allowed for the context. * @param context the context where the URI will be placed. * @param uri the URI (self). * @param buf the buffer. * @param size the size of the buffer. * @return the length printed. */ pj_ssize_t (*p_print)(pjsip_uri_context_e context, const void *uri, char *buf, pj_size_t size); /** * Compare two URIs according to the context. * @param context the context. * @param uri1 the first URI (self). * @param uri2 the second URI. * @return PJ_SUCCESS if equal, or otherwise the error status which * should point to the mismatch part. */ pj_status_t (*p_compare)(pjsip_uri_context_e context, const void *uri1, const void *uri2); /** * Clone URI. * @param pool the pool. * @param the URI to clone (self). * @return new URI. */ void *(*p_clone)(pj_pool_t *pool, const void *uri); } pjsip_uri_vptr; /** * The declaration of 'base class' for all URI scheme. */ struct pjsip_uri { /** All URIs must have URI virtual function table as their first member. */ pjsip_uri_vptr *vptr; }; /** * This macro checks that the URL is a "sip:" URL. * @param url The URL (pointer to) * @return non-zero if TRUE. */ #define PJSIP_URI_SCHEME_IS_SIP(url) \ (pj_stricmp2(pjsip_uri_get_scheme(url), "sip")==0) /** * This macro checks that the URL is a "sips:" URL (not SIP). * @param url The URL (pointer to) * @return non-zero if TRUE. */ #define PJSIP_URI_SCHEME_IS_SIPS(url) \ (pj_stricmp2(pjsip_uri_get_scheme(url), "sips")==0) /** * This macro checks that the URL is a "tel:" URL. * @param url The URL (pointer to) * @return non-zero if TRUE. */ #define PJSIP_URI_SCHEME_IS_TEL(url) \ (pj_stricmp2(pjsip_uri_get_scheme(url), "tel")==0) /** * Generic function to get the URI scheme. * @param uri the URI object. * @return the URI scheme. */ PJ_INLINE(const pj_str_t*) pjsip_uri_get_scheme(const void *uri) { return (*((pjsip_uri*)uri)->vptr->p_get_scheme)(uri); } /** * Generic function to get the URI object contained by this URI, or the URI * itself if it doesn't contain another URI. * * @param uri the URI. * @return the URI. */ PJ_INLINE(void*) pjsip_uri_get_uri(const void *uri) { return (*((pjsip_uri*)uri)->vptr->p_get_uri)((void*)uri); } /** * Generic function to compare two URIs. * * @param context Comparison context. * @param uri1 The first URI. * @param uri2 The second URI. * @return PJ_SUCCESS if equal, or otherwise the error status which * should point to the mismatch part. */ PJ_INLINE(pj_status_t) pjsip_uri_cmp(pjsip_uri_context_e context, const void *uri1, const void *uri2) { return (*((const pjsip_uri*)uri1)->vptr->p_compare)(context, uri1, uri2); } /** * Generic function to print an URI object. * * @param context Print context. * @param uri The URI to print. * @param buf The buffer. * @param size Size of the buffer. * @return Length printed. */ PJ_INLINE(int) pjsip_uri_print(pjsip_uri_context_e context, const void *uri, char *buf, pj_size_t size) { return (int)(*((const pjsip_uri*)uri)->vptr->p_print)(context, uri, buf, size); } /** * Generic function to clone an URI object. * * @param pool Pool. * @param uri URI to clone. * @return New URI. */ PJ_INLINE(void*) pjsip_uri_clone( pj_pool_t *pool, const void *uri ) { return (*((const pjsip_uri*)uri)->vptr->p_clone)(pool, uri); } /** * @} */ /** * @defgroup PJSIP_SIP_URI SIP URI Scheme and Name address * @ingroup PJSIP_URI * @brief SIP URL structure ("sip:" and "sips:") * @{ */ /** * SIP and SIPS URL scheme. */ typedef struct pjsip_sip_uri { pjsip_uri_vptr *vptr; /**< Pointer to virtual function table.*/ pj_str_t user; /**< Optional user part. */ pj_str_t passwd; /**< Optional password part. */ pj_str_t host; /**< Host part, always exists. */ int port; /**< Optional port number, or zero. */ pj_str_t user_param; /**< Optional user parameter */ pj_str_t method_param; /**< Optional method parameter. */ pj_str_t transport_param; /**< Optional transport parameter. */ int ttl_param; /**< Optional TTL param, or -1. */ int lr_param; /**< Optional loose routing param, or zero */ pj_str_t maddr_param; /**< Optional maddr param */ pjsip_param other_param; /**< Other parameters grouped together. */ pjsip_param header_param; /**< Optional header parameter. */ } pjsip_sip_uri; /** * SIP name-addr, which typically appear in From, To, and Contact header. * The SIP name-addr contains a generic URI and a display name. */ typedef struct pjsip_name_addr { /** Pointer to virtual function table. */ pjsip_uri_vptr *vptr; /** Optional display name. */ pj_str_t display; /** URI part. */ pjsip_uri *uri; } pjsip_name_addr; /** * Create new SIP URL and initialize all fields with zero or NULL. * @param pool The pool. * @param secure Flag to indicate whether secure transport should be used. * @return SIP URL. */ PJ_DECL(pjsip_sip_uri*) pjsip_sip_uri_create( pj_pool_t *pool, pj_bool_t secure ); /** * Change the SIP URI scheme to sip or sips based on the secure flag. * This would not change anything except the scheme. * @param uri The URI * @param secure Non-zero if sips is wanted. */ PJ_DECL(void) pjsip_sip_uri_set_secure( pjsip_sip_uri *uri, pj_bool_t secure ); /** * Initialize SIP URL (all fields are set to NULL or zero). * @param url The URL. * @param secure Create sips URI? */ PJ_DECL(void) pjsip_sip_uri_init(pjsip_sip_uri *url, pj_bool_t secure); /** * Perform full assignment to the SIP URL. * @param pool The pool. * @param url Destination URL. * @param rhs The source URL. */ PJ_DECL(void) pjsip_sip_uri_assign(pj_pool_t *pool, pjsip_sip_uri *url, const pjsip_sip_uri *rhs); /** * Create new instance of name address and initialize all fields with zero or * NULL. * @param pool The pool. * @return New SIP name address. */ PJ_DECL(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool); /** * Initialize with default value. * @param name_addr The name address. */ PJ_DECL(void) pjsip_name_addr_init(pjsip_name_addr *name_addr); /** * Perform full assignment to the name address. * @param pool The pool. * @param addr The destination name address. * @param rhs The source name address. */ PJ_DECL(void) pjsip_name_addr_assign(pj_pool_t *pool, pjsip_name_addr *addr, const pjsip_name_addr *rhs); /** * @} */ /** * @defgroup PJSIP_OTHER_URI Other URI schemes * @ingroup PJSIP_URI * @brief Container for non SIP/tel URI scheme (e.g. "http:", "mailto:") * @{ */ /** * Generic URI container for non SIP/tel URI scheme. */ typedef struct pjsip_other_uri { pjsip_uri_vptr *vptr; /**< Pointer to virtual function table. */ pj_str_t scheme; /**< The URI scheme (e.g. "mailto") */ pj_str_t content; /**< The whole URI content */ } pjsip_other_uri; /** * Create a generic URI object. * * @param pool The pool to allocate memory from. * * @return The URI instance. */ PJ_DECL(pjsip_other_uri*) pjsip_other_uri_create(pj_pool_t *pool); /** * @} */ PJ_END_DECL #endif /* __PJSIP_URL_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip/sip_util.h ================================================ /* $Id: sip_util.h 4347 2013-02-13 10:19:25Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_MISC_H__ #define __PJSIP_SIP_MISC_H__ #include #include #include PJ_BEGIN_DECL /** * @defgroup PJSIP_ENDPT_TARGET_URI Target URI Management * @ingroup PJSIP_CORE_CORE * @brief Management of target URI's in case of redirection * @{ * This module provides utility functions to manage target set for UAC. * The target set is provided as pjsip_target_set structure. Initially, * the target set for UAC contains only one target, that is the target of * the initial request. When 3xx/redirection class response is received, * the UAC can use the functionality of this module to add the URI's listed * in the Contact header(s) in the response to the target set, and retry * sending the request to the next destination/target. The UAC may retry * this sequentially until one of the target answers with succesful/2xx * response, or one target returns global error/6xx response, or all targets * are exhausted. * * This module is currently used by the \ref PJSIP_INV. */ /** * This structure describes a target, which can be chained together to form * a target set. Each target contains an URI, priority (as q-value), and * the last status code and reason phrase received from the target, if the * target has been contacted. If the target has not been contacted, the * status code field will be zero. */ typedef struct pjsip_target { PJ_DECL_LIST_MEMBER(struct pjsip_target);/**< Standard list element */ pjsip_uri *uri; /**< The target URI */ int q1000; /**< q-value multiplied by 1000 */ pjsip_status_code code; /**< Last status code received */ pj_str_t reason; /**< Last reason phrase received */ } pjsip_target; /** * This describes a target set. A target set contains a linked-list of * pjsip_target. */ typedef struct pjsip_target_set { pjsip_target head; /**< Target linked-list head */ pjsip_target *current; /**< Current target. */ } pjsip_target_set; /** * These enumerations specify the action to be performed to a redirect * response. */ typedef enum pjsip_redirect_op { /** * Reject the redirection to the current target. The UAC will * select the next target from the target set if exists. */ PJSIP_REDIRECT_REJECT, /** * Accept the redirection to the current target. The INVITE request * will be resent to the current target. */ PJSIP_REDIRECT_ACCEPT, /** * Accept the redirection to the current target and replace the To * header in the INVITE request with the current target. The INVITE * request will be resent to the current target. */ PJSIP_REDIRECT_ACCEPT_REPLACE, /** * Defer the redirection decision, for example to request permission * from the end user. */ PJSIP_REDIRECT_PENDING, /** * Stop the whole redirection process altogether. This will cause * the invite session to be disconnected. */ PJSIP_REDIRECT_STOP } pjsip_redirect_op; /** * Initialize target set. This will empty the list of targets in the * target set. * * @param tset The target set. */ PJ_INLINE(void) pjsip_target_set_init(pjsip_target_set *tset) { pj_list_init(&tset->head); tset->current = NULL; } /** * Add an URI to the target set, if the URI is not already in the target set. * The URI comparison rule of pjsip_uri_cmp() will be used to determine the * equality of this URI compared to existing URI's in the target set. The * URI will be cloned using the specified memory pool before it is added to * the list. * * The first URI added to the target set will also be made current target * by this function. * * @param tset The target set. * @param pool The memory pool to be used to duplicate the URI. * @param uri The URI to be checked and added. * @param q1000 The q-value multiplied by 1000. * * @return PJ_SUCCESS if the URI was added to the target set, * or PJ_EEXISTS if the URI already exists in the target * set, or other error codes. */ PJ_DECL(pj_status_t) pjsip_target_set_add_uri(pjsip_target_set *tset, pj_pool_t *pool, const pjsip_uri *uri, int q1000); /** * Extract URI's in the Contact headers of the specified (response) message * and add them to the target set. This function will also check if the * URI's already exist in the target set before adding them to the list. * * @param tset The target set. * @param pool The memory pool to be used to duplicate the URI's. * @param msg SIP message from which the Contact headers will be * scanned and the URI's to be extracted, checked, and * added to the target set. * * @return PJ_SUCCESS if at least one URI was added to the * target set, or PJ_EEXISTS if all URI's in the message * already exists in the target set or if the message * doesn't contain usable Contact headers, or other error * codes. */ PJ_DECL(pj_status_t) pjsip_target_set_add_from_msg(pjsip_target_set *tset, pj_pool_t *pool, const pjsip_msg *msg); /** * Get the next target to be retried. This function will scan the target set * for target which hasn't been tried, and return one target with the * highest q-value, if such target exists. This function will return NULL * if there is one target with 2xx or 6xx code or if all targets have been * tried. * * @param tset The target set. * * @return The next target to be tried, or NULL if all targets have * been tried or at least one target returns 2xx or 6xx * response. */ PJ_DECL(pjsip_target*) pjsip_target_set_get_next(const pjsip_target_set *tset); /** * Set the specified target as the current target in the target set. The * current target may be used by application to keep track on which target * is currently being operated on. * * @param tset The target set. * @param target The target to be set as current target. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_target_set_set_current(pjsip_target_set *tset, pjsip_target *target); /** * Set the status code and reason phrase of the specified target. * * @param target The target. * @param pool The memory pool to be used to duplicate the reason phrase. * @param code The SIP status code to be set to the target. * @param reason The reason phrase to be set to the target. * * @return PJ_SUCCESS on successful operation or the appropriate * error code. */ PJ_DECL(pj_status_t) pjsip_target_assign_status(pjsip_target *target, pj_pool_t *pool, int status_code, const pj_str_t *reason); /** * @} */ /** * @defgroup PJSIP_ENDPT_STATELESS Message Creation and Stateless Operations * @ingroup PJSIP_CORE_CORE * @brief Utilities to create various messages and base function to send messages. * @{ */ /** * Create an independent request message. This can be used to build any * request outside a dialog, such as OPTIONS, MESSAGE, etc. To create a request * inside a dialog, application should use #pjsip_dlg_create_request. * * This function adds the following headers in the request: * - From, To, Call-ID, and CSeq, * - Contact header, if contact is specified. * - A blank Via header. * - Additional request headers (such as Max-Forwards) which are copied * from endpoint configuration. * * In addition, the function adds a unique tag in the From header. * * Once a transmit data is created, the reference counter is initialized to 1. * * @param endpt Endpoint instance. * @param method SIP Method. * @param target Target URI. * @param from URL to put in From header. * @param to URL to put in To header. * @param contact Contact to be put as Contact header value, hence * the format must follow RFC 3261 Section 20.10: * When the header field value contains a display * name, the URI including all URI parameters is * enclosed in "<" and ">". If no "<" and ">" are * present, all parameters after the URI are header * parameters, not URI parameters. The display name * can be tokens, or a quoted string, if a larger * character set is desired. * @param call_id Optional Call-ID (put NULL to generate unique Call-ID). * @param cseq Optional CSeq (put -1 to generate random CSeq). * @param text Optional text body (put NULL to omit body). * @param p_tdata Pointer to receive the transmit data. * * @return PJ_SUCCESS, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt, const pjsip_method *method, const pj_str_t *target, const pj_str_t *from, const pj_str_t *to, const pj_str_t *contact, const pj_str_t *call_id, int cseq, const pj_str_t *text, pjsip_tx_data **p_tdata); /** * Create an independent request message from the specified headers. This * function will clone the headers and put them in the request. * * This function adds the following headers in the request: * - From, To, Call-ID, and CSeq, * - Contact header, if contact is specified. * - A blank Via header. * - Additional request headers (such as Max-Forwards) which are copied * from endpoint configuration. * * In addition, the function adds a unique tag in the From header. * * Once a transmit data is created, the reference counter is initialized to 1. * * @param endpt Endpoint instance. * @param method SIP Method. * @param target Target URI. * @param from From header. * @param to To header. * @param contact Contact header. * @param call_id Optional Call-ID (put NULL to generate unique Call-ID). * @param cseq Optional CSeq (put -1 to generate random CSeq). * @param text Optional text body (put NULL to omit body). * @param p_tdata Pointer to receive the transmit data. * * @return PJ_SUCCESS, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt, const pjsip_method *method, const pjsip_uri *target, const pjsip_from_hdr *from, const pjsip_to_hdr *to, const pjsip_contact_hdr *contact, const pjsip_cid_hdr *call_id, int cseq, const pj_str_t *text, pjsip_tx_data **p_tdata); /** * Construct a minimal response message for the received request. This function * will construct all the Via, Record-Route, Call-ID, From, To, CSeq, and * Call-ID headers from the request. * * Note: the txdata reference counter is set to ZERO!. * * @param endpt The endpoint. * @param rdata The request receive data. * @param st_code Status code to be put in the response. * @param st_text Optional status text, or NULL to get the default text. * @param p_tdata Pointer to receive the transmit data. * * @return PJ_SUCCESS, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt, const pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, pjsip_tx_data **p_tdata); /** * Construct a full ACK request for the received non-2xx final response. * This utility function is normally called by the transaction to construct * an ACK request to 3xx-6xx final response. * The generation of ACK message for 2xx final response is different than * this one. * * @param endpt The endpoint. * @param tdata This contains the original INVITE request * @param rdata The final response. * @param ack The ACK request created. * * @return PJ_SUCCESS, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt, const pjsip_tx_data *tdata, const pjsip_rx_data *rdata, pjsip_tx_data **ack); /** * Construct CANCEL request for the previously sent request. * * @param endpt The endpoint. * @param tdata The transmit buffer for the request being cancelled. * @param p_tdata Pointer to receive the transmit data. * * @return PJ_SUCCESS, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt, const pjsip_tx_data *tdata, pjsip_tx_data **p_tdata); /** * Get destination address and port and transport type information for the * specified URI. * * @param target_uri The destination URI. * @param request_uri Optional request URI to be considered. May be NULL. * @param pool Pool to allocate memory from. * @param dest_info To be filled with destination info. * * @return PJ_SUCCESS or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_get_dest_info(const pjsip_uri *target_uri, const pjsip_uri *request_uri, pj_pool_t *pool, pjsip_host_info *dest_info); /** * Find which destination to be used to send the request message, based * on the request URI and Route headers in the message. The procedure * used here follows the guidelines on sending the request in RFC 3261 * chapter 8.1.2. * * Note there was a change in the behavior of this function since version * 0.5.10.2. Previously this function may modify the request when strict * route is present (to update request URI and route-set). This is no * longer the case now, and this process is done in separate function * (see #pjsip_process_route_set()). * * @param tdata The transmit data containing the request message. * @param dest_info On return, it contains information about destination * host to contact, along with the preferable transport * type, if any. Caller will then normally proceed with * resolving this host with server resolution procedure * described in RFC 3263. * * @return PJ_SUCCESS, or the appropriate error code. * * @see pjsip_process_route_set */ PJ_DECL(pj_status_t) pjsip_get_request_dest(const pjsip_tx_data *tdata, pjsip_host_info *dest_info ); /** * Process route-set found in the request and calculate destination to be * used to send the request message, based on the request URI and Route * headers in the message. The procedure used here follows the guidelines * on sending the request in RFC 3261 chapter 8.1.2. * * This function may modify the message (request line and Route headers), * if the Route information specifies strict routing and the request * URI in the message is different than the calculated target URI. In that * case, the target URI will be put as the request URI of the request and * current request URI will be put as the last entry of the Route headers. * * @param tdata The transmit data containing the request message. * @param dest_info On return, it contains information about destination * host to contact, along with the preferable transport * type, if any. Caller will then normally proceed with * resolving this host with server resolution procedure * described in RFC 3263. * * @return PJ_SUCCESS, or the appropriate error code. * * @see pjsip_get_request_addr */ PJ_DECL(pj_status_t) pjsip_process_route_set(pjsip_tx_data *tdata, pjsip_host_info *dest_info ); /** * Swap the request URI and strict route back to the original position * before #pjsip_process_route_set() function is called. If no strict * route URI was found by #pjsip_process_route_set(), this function will * do nothing. * * This function should only used internally by PJSIP client authentication * module. * * @param tdata Transmit data containing request message. */ PJ_DECL(void) pjsip_restore_strict_route_set(pjsip_tx_data *tdata); /** * This structure holds the state of outgoing stateless request. */ typedef struct pjsip_send_state { /** Application token, which was specified when the function * #pjsip_endpt_send_request_stateless() is called. */ void *token; /** Endpoint instance. */ pjsip_endpoint *endpt; /** Transmit data buffer being sent. */ pjsip_tx_data *tdata; /** Current transport being used. */ pjsip_transport *cur_transport; /** The application callback which was specified when the function * #pjsip_endpt_send_request_stateless() was called. */ void (*app_cb)(struct pjsip_send_state*, pj_ssize_t sent, pj_bool_t *cont); } pjsip_send_state; /** * Declaration for callback function to be specified in * #pjsip_endpt_send_request_stateless(), #pjsip_endpt_send_response(), or * #pjsip_endpt_send_response2(). * * @param st Structure to keep transmission state. * @param sent Number of bytes sent. * @param cont When current transmission fails, specify whether * the function should fallback to next destination. */ typedef void (*pjsip_send_callback)(pjsip_send_state *st, pj_ssize_t sent, pj_bool_t *cont); /** * Send outgoing request statelessly The function will take care of which * destination and transport to use based on the information in the message, * taking care of URI in the request line and Route header. * * This function is different than #pjsip_transport_send() in that this * function adds/modify the Via header as necessary. * * @param endpt The endpoint instance. * @param tdata The transmit data to be sent. * @param token Arbitrary token to be given back on the callback. * @param cb Optional callback to notify transmission status (also * gives chance for application to discontinue retrying * sending to alternate address). * * @return PJ_SUCCESS, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_endpt_send_request_stateless( pjsip_endpoint *endpt, pjsip_tx_data *tdata, void *token, pjsip_send_callback cb); /** * This is a low-level function to send raw data to a destination. * * See also #pjsip_endpt_send_raw_to_uri(). * * @param endpt The SIP endpoint instance. * @param tp_type Transport type. * @param sel Optional pointer to transport selector instance if * application wants to use a specific transport instance * rather then letting transport manager finds the suitable * transport.. * @param raw_data The data to be sent. * @param data_len The length of the data. * @param addr Destination address. * @param addr_len Length of destination address. * @param token Arbitrary token to be returned back to callback. * @param cb Optional callback to be called to notify caller about * the completion status of the pending send operation. * * @return If the message has been sent successfully, this function * will return PJ_SUCCESS and the callback will not be * called. If message cannot be sent immediately, this * function will return PJ_EPENDING, and application will * be notified later about the completion via the callback. * Any statuses other than PJ_SUCCESS or PJ_EPENDING * indicates immediate failure, and in this case the * callback will not be called. */ PJ_DECL(pj_status_t) pjsip_endpt_send_raw(pjsip_endpoint *endpt, pjsip_transport_type_e tp_type, const pjsip_tpselector *sel, const void *raw_data, pj_size_t data_len, const pj_sockaddr_t *addr, int addr_len, void *token, pjsip_tp_send_callback cb); /** * Send raw data to the specified destination URI. The actual destination * address will be calculated from the URI, using normal SIP URI to host * resolution. * * See also #pjsip_endpt_send_raw(). * * @param endpt The SIP endpoint instance. * @param dst_uri Destination address URI. * @param sel Optional pointer to transport selector instance if * application wants to use a specific transport instance * rather then letting transport manager finds the suitable * transport.. * @param raw_data The data to be sent. * @param data_len The length of the data. * @param token Arbitrary token to be returned back to callback. * @param cb Optional callback to be called to notify caller about * the completion status of the pending send operation. * * @return If the message has been sent successfully, this function * will return PJ_SUCCESS and the callback will not be * called. If message cannot be sent immediately, this * function will return PJ_EPENDING, and application will * be notified later about the completion via the callback. * Any statuses other than PJ_SUCCESS or PJ_EPENDING * indicates immediate failure, and in this case the * callback will not be called. */ PJ_DECL(pj_status_t) pjsip_endpt_send_raw_to_uri(pjsip_endpoint *endpt, const pj_str_t *dst_uri, const pjsip_tpselector *sel, const void *raw_data, pj_size_t data_len, void *token, pjsip_tp_send_callback cb); /** * This structure describes destination information to send response. * It is initialized by calling #pjsip_get_response_addr(). * * If the response message should be sent using transport from which * the request was received, then transport, addr, and addr_len fields * are initialized. * * The dst_host field is also initialized. It should be used when server * fails to send the response using the transport from which the request * was received, or when the transport is NULL, which means server * must send the response to this address (this situation occurs when * maddr parameter is set, or when rport param is not set in the request). */ typedef struct pjsip_response_addr { pjsip_transport *transport; /**< Immediate transport to be used. */ pj_sockaddr addr; /**< Immediate address to send to. */ int addr_len; /**< Address length. */ pjsip_host_info dst_host; /**< Destination host to contact. */ } pjsip_response_addr; /** * Determine which address (and transport) to use to send response message * based on the received request. This function follows the specification * in section 18.2.2 of RFC 3261 and RFC 3581 for calculating the destination * address and transport. * * The information about destination to send the response will be returned * in res_addr argument. Please see #pjsip_response_addr for more info. * * @param pool The pool. * @param rdata The incoming request received by the server. * @param res_addr On return, it will be initialized with information about * destination address and transport to send the response. * * @return zero (PJ_OK) if successfull. */ PJ_DECL(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool, pjsip_rx_data *rdata, pjsip_response_addr *res_addr); /** * Send response in tdata statelessly. The function will take care of which * response destination and transport to use based on the information in the * Via header (such as the presence of rport, symmetric transport, etc.). * * This function will create a new ephemeral transport if no existing * transports can be used to send the message to the destination. The ephemeral * transport will be destroyed after some period if it is not used to send any * more messages. * * The behavior of this function complies with section 18.2.2 of RFC 3261 * and RFC 3581. * * @param endpt The endpoint instance. * @param res_addr The information about the address and transport to send * the response to. Application can get this information * by calling #pjsip_get_response_addr(). * @param tdata The response message to be sent. * @param token Token to be passed back when the callback is called. * @param cb Optional callback to notify the transmission status * to application, and to inform whether next address or * transport will be tried. * * @return PJ_SUCCESS if response has been successfully created and * sent to transport layer, or a non-zero error code. * However, even when it returns PJ_SUCCESS, there is no * guarantee that the response has been successfully sent. */ PJ_DECL(pj_status_t) pjsip_endpt_send_response( pjsip_endpoint *endpt, pjsip_response_addr *res_addr, pjsip_tx_data *tdata, void *token, pjsip_send_callback cb); /** * This is a convenient function which wraps #pjsip_get_response_addr() and * #pjsip_endpt_send_response() in a single function. * * @param endpt The endpoint instance. * @param rdata The original request to be responded. * @param tdata The response message to be sent. * @param token Token to be passed back when the callback is called. * @param cb Optional callback to notify the transmission status * to application, and to inform whether next address or * transport will be tried. * * @return PJ_SUCCESS if response has been successfully created and * sent to transport layer, or a non-zero error code. * However, even when it returns PJ_SUCCESS, there is no * guarantee that the response has been successfully sent. */ PJ_DECL(pj_status_t) pjsip_endpt_send_response2(pjsip_endpoint *endpt, pjsip_rx_data *rdata, pjsip_tx_data *tdata, void *token, pjsip_send_callback cb); /** * This composite function sends response message statelessly to an incoming * request message. Internally it calls #pjsip_endpt_create_response() and * #pjsip_endpt_send_response(). * * @param endpt The endpoint instance. * @param rdata The incoming request message. * @param st_code Status code of the response. * @param st_text Optional status text of the response. * @param hdr_list Optional header list to be added to the response. * @param body Optional message body to be added to the response. * * @return PJ_SUCCESS if response message has successfully been * sent. */ PJ_DECL(pj_status_t) pjsip_endpt_respond_stateless(pjsip_endpoint *endpt, pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, const pjsip_hdr *hdr_list, const pjsip_msg_body *body); /** * @} */ /** * @defgroup PJSIP_TRANSACT_UTIL Stateful Operations * @ingroup PJSIP_TRANSACT * @brief Utility function to send requests/responses statefully. * @{ */ /** * This composite function creates and sends response statefully for the * incoming request. * * @param endpt The endpoint instance. * @param tsx_user The module to be registered as transaction user. * @param rdata The incoming request message. * @param st_code Status code of the response. * @param st_text Optional status text of the response. * @param hdr_list Optional header list to be added to the response. * @param body Optional message body to be added to the response. * @param p_tsx Optional pointer to receive the transaction which was * created to send the response. * * @return PJ_SUCCESS if response message has successfully been * created. */ PJ_DECL(pj_status_t) pjsip_endpt_respond( pjsip_endpoint *endpt, pjsip_module *tsx_user, pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, const pjsip_hdr *hdr_list, const pjsip_msg_body *body, pjsip_transaction **p_tsx ); /** * Type of callback to be specified in #pjsip_endpt_send_request(). * * @param token The token that was given in #pjsip_endpt_send_request() * @param e Completion event. */ typedef void (*pjsip_endpt_send_callback)(void *token, pjsip_event *e); /** * Send outgoing request and initiate UAC transaction for the request. * This is an auxiliary function to be used by application to send arbitrary * requests outside a dialog. To send a request within a dialog, application * should use #pjsip_dlg_send_request instead. * * @param endpt The endpoint instance. * @param tdata The transmit data to be sent. * @param timeout Optional timeout for final response to be received, or -1 * if the transaction should not have a timeout restriction. * The value is in miliseconds. Note that this is not * implemented yet, so application needs to use its own timer * to handle timeout. * @param token Optional token to be associated with the transaction, and * to be passed to the callback. * @param cb Optional callback to be called when the transaction has * received a final response. The callback will be called with * the previously registered token and the event that triggers * the completion of the transaction. * * @return PJ_SUCCESS, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt, pjsip_tx_data *tdata, pj_int32_t timeout, void *token, pjsip_endpt_send_callback cb); /** * @} */ /** * @defgroup PJSIP_PROXY_CORE Core Proxy Layer * @brief Core proxy operations * @{ */ /** * Create new request message to be forwarded upstream to new destination URI * in uri. The new request is a full/deep clone of the request received in * rdata, unless if other copy mechanism is specified in the options. * The branch parameter, if not NULL, will be used as the branch-param in * the Via header. If it is NULL, then a unique branch parameter will be used. * * Note: this function DOES NOT perform Route information preprocessing as * described in RFC 3261 Section 16.4. Application must take care of * removing/updating the Route headers according of the rules as * described in that section. * * @param endpt The endpoint instance. * @param rdata The incoming request message. * @param uri The URI where the request will be forwarded to. * @param branch Optional branch parameter. Application may specify its * own branch, for example if it wishes to perform loop * detection. If the branch parameter is not specified, * this function will generate its own by calling * #pjsip_calculate_branch_id() function. * @param options Optional option flags when duplicating the message. * @param tdata The result. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_endpt_create_request_fwd(pjsip_endpoint *endpt, pjsip_rx_data *rdata, const pjsip_uri *uri, const pj_str_t *branch, unsigned options, pjsip_tx_data **tdata); /** * Create new response message to be forwarded downstream by the proxy from * the response message found in rdata. Note that this function practically * will clone the response as is, i.e. without checking the validity of the * response or removing top most Via header. This function will perform * full/deep clone of the response, unless other copy mechanism is used in * the options. * * @param endpt The endpoint instance. * @param rdata The incoming response message. * @param options Optional option flags when duplicate the message. * @param tdata The result * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_endpt_create_response_fwd( pjsip_endpoint *endpt, pjsip_rx_data *rdata, unsigned options, pjsip_tx_data **tdata); /** * Create a globally unique branch parameter based on the information in * the incoming request message, for the purpose of creating a new request * for forwarding. This is the default implementation used by * #pjsip_endpt_create_request_fwd() function if the branch parameter is * not specified. * * The default implementation here will just create an MD5 hash of the * top-most Via. * * Note that the returned string was allocated from rdata's pool. * * @param rdata The incoming request message. * * @return Unique branch-ID string. */ PJ_DECL(pj_str_t) pjsip_calculate_branch_id( pjsip_rx_data *rdata ); /** * @} */ PJ_END_DECL #endif /* __PJSIP_SIP_MISC_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-simple/errno.h ================================================ /* $Id: errno.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIMPLE_ERRNO_H__ #define __PJSIP_SIMPLE_ERRNO_H__ #include PJ_BEGIN_DECL /** * Start of error code relative to PJ_ERRNO_START_USER. */ #define PJSIP_SIMPLE_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*2) /************************************************************ * EVENT PACKAGE ERRORS ***********************************************************/ /** * @hideinitializer * No event package with the specified name. */ #define PJSIP_SIMPLE_ENOPKG (PJSIP_SIMPLE_ERRNO_START+1) /*270001*/ /** * @hideinitializer * Event package already exists. */ #define PJSIP_SIMPLE_EPKGEXISTS (PJSIP_SIMPLE_ERRNO_START+2) /*270002*/ /************************************************************ * PRESENCE ERROR ***********************************************************/ /** * @hideinitializer * Expecting SUBSCRIBE request */ #define PJSIP_SIMPLE_ENOTSUBSCRIBE (PJSIP_SIMPLE_ERRNO_START+20) /*270020*/ /** * @hideinitializer * No presence associated with subscription */ #define PJSIP_SIMPLE_ENOPRESENCE (PJSIP_SIMPLE_ERRNO_START+21) /*270021*/ /** * @hideinitializer * No presence info in server subscription */ #define PJSIP_SIMPLE_ENOPRESENCEINFO (PJSIP_SIMPLE_ERRNO_START+22) /*270022*/ /** * @hideinitializer * Bad Content-Type */ #define PJSIP_SIMPLE_EBADCONTENT (PJSIP_SIMPLE_ERRNO_START+23) /*270023*/ /** * @hideinitializer * Bad PIDF Message */ #define PJSIP_SIMPLE_EBADPIDF (PJSIP_SIMPLE_ERRNO_START+24) /*270024*/ /** * @hideinitializer * Bad XPIDF Message */ #define PJSIP_SIMPLE_EBADXPIDF (PJSIP_SIMPLE_ERRNO_START+25) /*270025*/ /** * @hideinitializer * Bad RPID Message */ #define PJSIP_SIMPLE_EBADRPID (PJSIP_SIMPLE_ERRNO_START+26) /*270026*/ /************************************************************ * ISCOMPOSING ERRORS ***********************************************************/ /** * @hideinitializer * Bad isComposing XML message. */ #define PJSIP_SIMPLE_EBADISCOMPOSE (PJSIP_SIMPLE_ERRNO_START+40) /*270040*/ /** * Get error message for the specified error code. Note that this * function is only able to decode PJSIP-SIMPLE specific error code. * Application should use pj_strerror(), which should be able to * decode all error codes belonging to all subsystems (e.g. pjlib, * pjmedia, pjsip, etc). * * @param status The error code. * @param buffer The buffer where to put the error message. * @param bufsize Size of the buffer. * * @return The error message as NULL terminated string, * wrapped with pj_str_t. */ PJ_DECL(pj_str_t) pjsipsimple_strerror(pj_status_t status, char *buffer, pj_size_t bufsize); PJ_END_DECL #endif /* __PJSIP_SIMPLE_ERRNO_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-simple/evsub.h ================================================ /* $Id: evsub.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIMPLE_EVSUB_H__ #define __PJSIP_SIMPLE_EVSUB_H__ /** * @file evsub.h * @brief SIP Specific Event Notification Extension (RFC 3265) */ #include /** * @defgroup PJSIP_EVENT_NOT SIP Event Notification (RFC 3265) Module * @ingroup PJSIP_SIMPLE * @brief Core Event Subscription framework, used by presence, call transfer, etc. * @{ * * This module provides the implementation of SIP Extension for SIP Specific * Event Notification (RFC 3265). It extends PJSIP by supporting SUBSCRIBE and * NOTIFY methods. * * This module itself is extensible; new event packages can be registered to * this module to handle specific extensions (such as presence). */ PJ_BEGIN_DECL /** * Opaque type for event subscription session. */ typedef struct pjsip_evsub pjsip_evsub; /** * This enumeration describes basic subscription state as described in the * RFC 3265. The standard specifies that extensions may define additional * states. In the case where the state is not known, the subscription state * will be set to PJSIP_EVSUB_STATE_UNKNOWN, and the token will be kept * in state_str member of the susbcription structure. */ typedef enum pjsip_evsub_state { PJSIP_EVSUB_STATE_NULL, /**< State is NULL. */ PJSIP_EVSUB_STATE_SENT, /**< Client has sent SUBSCRIBE request. */ PJSIP_EVSUB_STATE_ACCEPTED, /**< 2xx response to SUBSCRIBE has been sent/received. */ PJSIP_EVSUB_STATE_PENDING, /**< Subscription is pending. */ PJSIP_EVSUB_STATE_ACTIVE, /**< Subscription is active. */ PJSIP_EVSUB_STATE_TERMINATED,/**< Subscription is terminated. */ PJSIP_EVSUB_STATE_UNKNOWN, /**< Subscription state can not be determined. Application can query the state by calling #pjsip_evsub_get_state_name().*/ } pjsip_evsub_state; /** * Some options for the event subscription. */ enum { /** * If this flag is set, then outgoing request to create subscription * will not have id in the Event header (e.g. in REFER request). But if * there is an id in the incoming NOTIFY, that id will be used. */ PJSIP_EVSUB_NO_EVENT_ID = 1, }; /** * This structure describes callback that is registered by application or * package to receive notifications about subscription events. */ struct pjsip_evsub_user { /** * This callback is called when subscription state has changed. * Application MUST be prepared to receive NULL event and events with * type other than PJSIP_EVENT_TSX_STATE * * This callback is OPTIONAL. * * @param sub The subscription instance. * @param event The event that has caused the state to change, * which may be NULL or may have type other than * PJSIP_EVENT_TSX_STATE. */ void (*on_evsub_state)( pjsip_evsub *sub, pjsip_event *event); /** * This callback is called when transaction state has changed. * * @param sub The subscription instance. * @param tsx Transaction. * @param event The event. */ void (*on_tsx_state)(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event); /** * This callback is called when incoming SUBSCRIBE (or any method that * establishes the subscription in the first place) is received. It * allows application to specify what response should be sent to * remote, along with additional headers and message body to be put * in the response. * * This callback is OPTIONAL. * * However, implementation MUST send NOTIFY request upon receiving this * callback. The suggested behavior is to call * #pjsip_evsub_current_notify(), since this function takes care * about unsubscription request and calculates the appropriate expiration * interval. */ void (*on_rx_refresh)( pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body); /** * This callback is called when client/subscriber received incoming * NOTIFY request. It allows the application to specify what response * should be sent to remote, along with additional headers and message * body to be put in the response. * * This callback is OPTIONAL. When it is not implemented, the default * behavior is to respond incoming NOTIFY request with 200 (OK). * * @param sub The subscription instance. * @param rdata The received NOTIFY request. * @param p_st_code Application MUST set the value of this argument with * final status code (200-699) upon returning from the * callback. * @param p_st_text Custom status text, if any. * @param res_hdr Upon return, application can put additional headers * to be sent in the response in this list. * @param p_body Application MAY specify message body to be sent in * the response. */ void (*on_rx_notify)(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body); /** * This callback is called when it is time for the client to refresh * the subscription. * * This callback is OPTIONAL when PJSIP package such as presence or * refer is used; the event package will refresh subscription by sending * SUBSCRIBE with the interval set to current/last interval. * * @param sub The subscription instance. */ void (*on_client_refresh)(pjsip_evsub *sub); /** * This callback is called when server doesn't receive subscription * refresh after the specified subscription interval. * * This callback is OPTIONAL when PJSIP package such as presence or * refer is used; the event package send NOTIFY to terminate the * subscription. */ void (*on_server_timeout)(pjsip_evsub *sub); }; /** * @see pjsip_evsub_user */ typedef struct pjsip_evsub_user pjsip_evsub_user; /** * SUBSCRIBE method constant. @see pjsip_get_subscribe_method() */ PJ_DECL_DATA(const pjsip_method) pjsip_subscribe_method; /** * NOTIFY method constant. @see pjsip_get_notify_method() */ PJ_DECL_DATA(const pjsip_method) pjsip_notify_method; /** * SUBSCRIBE method constant. */ PJ_DECL(const pjsip_method*) pjsip_get_subscribe_method(void); /** * NOTIFY method constant. */ PJ_DECL(const pjsip_method*) pjsip_get_notify_method(void); /** * Initialize the event subscription module and register the module to the * specified endpoint. * * @param endpt The endpoint instance. * * @return PJ_SUCCESS if module can be created and registered * successfully. */ PJ_DECL(pj_status_t) pjsip_evsub_init_module(pjsip_endpoint *endpt); /** * Get the event subscription module instance that was previously created * and registered to endpoint. * * @return The event subscription module instance. */ PJ_DECL(pjsip_module*) pjsip_evsub_instance(void); /** * Register event package to the event subscription framework. * * @param pkg_mod The module that implements the event package being * registered. * @param event_name Event package identification. * @param expires Default subscription expiration time, in seconds. * @param accept_cnt Number of strings in Accept array. * @param accept Array of Accept value. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod, const pj_str_t *event_name, unsigned expires, unsigned accept_cnt, const pj_str_t accept[]); /** * Get the Allow-Events header. This header is built based on the packages * that are registered to the evsub module. * * @param m Pointer to event subscription module instance, or * NULL to use default instance (equal to * #pjsip_evsub_instance()). * * @return The Allow-Events header. */ PJ_DECL(const pjsip_hdr*) pjsip_evsub_get_allow_events_hdr(pjsip_module *m); /** * Create client subscription session. * * @param dlg The underlying dialog to use. * @param user_cb Callback to receive event subscription notifications. * @param event Event name. * @param option Bitmask of options. * @param p_evsub Pointer to receive event subscription instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, const pj_str_t *event, unsigned option, pjsip_evsub **p_evsub); /** * Create server subscription session. * * @param dlg The underlying dialog to use. * @param user_cb Callback to receive event subscription notifications. * @param rdata The incoming request that creates the event * subscription, such as SUBSCRIBE or REFER. * @param option Bitmask of options. * @param p_evsub Pointer to receive event subscription instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, unsigned option, pjsip_evsub **p_evsub); /** * Forcefully destroy the subscription session. This function should only * be called on special condition, such as when the subscription * initialization has failed. For other conditions, application MUST terminate * the subscription by sending the appropriate un(SUBSCRIBE) or NOTIFY. * * @param sub The event subscription. * @param notify Specify whether the state notification callback * should be called. * * @return PJ_SUCCESS if subscription session has been destroyed. */ PJ_DECL(pj_status_t) pjsip_evsub_terminate( pjsip_evsub *sub, pj_bool_t notify ); /** * Get subscription state. * * @param sub Event subscription instance. * * @return Subscription state. */ PJ_DECL(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub); /** * Get the string representation of the subscription state. * * @param sub Event subscription instance. * * @return NULL terminated string. */ PJ_DECL(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub); /** * Get subscription termination reason, if any. If remote did not * send termination reason, this function will return empty string. * * @param sub Event subscription instance. * * @return NULL terminated string. */ PJ_DECL(const pj_str_t*) pjsip_evsub_get_termination_reason(pjsip_evsub *sub); /** * Call this function to create request to initiate subscription, to * refresh subcription, or to request subscription termination. * * @param sub Client subscription instance. * @param method The method that establishes the subscription, such as * SUBSCRIBE or REFER. If this argument is NULL, then * SUBSCRIBE will be used. * @param expires Subscription expiration. If the value is set to zero, * this will request unsubscription. If the value is * negative, default expiration as defined by the package * will be used. * @param p_tdata Pointer to receive the request. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub, const pjsip_method *method, pj_int32_t expires, pjsip_tx_data **p_tdata); /** * Add a list of headers to the subscription instance. The list of headers * will be added to outgoing presence subscription requests. * * @param sub Subscription instance. * @param hdr_list List of headers to be added. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_evsub_add_header( pjsip_evsub *sub, const pjsip_hdr *hdr_list ); /** * Accept the incoming subscription request by sending 2xx response to * incoming SUBSCRIBE request. * * @param sub Server subscription instance. * @param rdata The incoming subscription request message. * @param st_code Status code, which MUST be final response. * @param hdr_list Optional list of headers to be added in the response. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub, pjsip_rx_data *rdata, int st_code, const pjsip_hdr *hdr_list ); /** * For notifier, create NOTIFY request to subscriber, and set the state * of the subscription. * * @param sub The server subscription (notifier) instance. * @param state New state to set. * @param state_str The state string name, if state contains value other * than active, pending, or terminated. Otherwise this * argument is ignored. * @param reason Specify reason if new state is terminated, otherwise * put NULL. * @param p_tdata Pointer to receive request message. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub, pjsip_evsub_state state, const pj_str_t *state_str, const pj_str_t *reason, pjsip_tx_data **p_tdata); /** * For notifier, create a NOTIFY request that reflects current subscription * status. * * @param sub The server subscription instance. * @param p_tdata Pointer to receive the request messge. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub, pjsip_tx_data **p_tdata ); /** * Send request message that was previously created with initiate(), notify(), * or current_notify(). Application may also send request created with other * functions, e.g. authentication. But the request MUST be either request * that creates/refresh subscription or NOTIFY request. * * @param sub The event subscription object. * @param tdata Request message to be send. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub, pjsip_tx_data *tdata); /** * Get the event subscription instance associated with the specified * transaction. * * @param tsx The transaction. * * @return The event subscription instance registered in the * transaction, if any. */ PJ_DECL(pjsip_evsub*) pjsip_tsx_get_evsub(pjsip_transaction *tsx); /** * Set event subscription's module data. * * @param sub The event subscription. * @param mod_id The module id. * @param data Arbitrary data. */ PJ_DECL(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id, void *data ); /** * Get event subscription's module data. * * @param sub The event subscription. * @param mod_id The module id. * * @return Data previously set at the specified id. */ PJ_DECL(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id ); /* Update evbsub internal refresh_time with the given interval */ PJ_DECL(void) pjsip_evsub_update_expires( pjsip_evsub *sub, pj_uint32_t interval ); /* Set the specified timer (UAC or UAS) to the specified time */ PJ_DECL(void) pjsip_evsub_set_timer( pjsip_evsub *sub, int timer_id, pj_int32_t seconds ); PJ_END_DECL /** * @} */ #endif /* __PJSIP_SIMPLE_EVSUB_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-simple/evsub_msg.h ================================================ /* $Id: evsub_msg.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ #define __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ /** * @file evsub_msg.h * @brief SIP Event Notification Headers (RFC 3265) */ #include /** * @defgroup PJSIP_EVENT_HDRS Additional Header Fields * @ingroup PJSIP_EVENT_NOT * @{ */ PJ_BEGIN_DECL /** Max events in Allow-Events header. */ #define PJSIP_MAX_ALLOW_EVENTS 16 /** * This structure describes Event header. */ typedef struct pjsip_event_hdr { /** Standard header fields. */ PJSIP_DECL_HDR_MEMBER(struct pjsip_event_hdr); pj_str_t event_type; /**< Event name. */ pj_str_t id_param; /**< Optional event ID parameter. */ pjsip_param other_param; /**< Other parameter. */ } pjsip_event_hdr; /** * Create an Event header. * * @param pool The pool. * * @return New Event header instance. */ PJ_DECL(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool); /** * This structure describes Allow-Events header. */ typedef pjsip_generic_array_hdr pjsip_allow_events_hdr; /** * Create a new Allow-Events header. * * @param pool The pool. * * @return Allow-Events header. */ PJ_DECL(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool); /** * This structure describes Subscription-State header. */ typedef struct pjsip_sub_state_hdr { /** Standard header fields. */ PJSIP_DECL_HDR_MEMBER(struct pjsip_sub_state_hdr); pj_str_t sub_state; /**< Subscription state. */ pj_str_t reason_param; /**< Optional termination reason. */ int expires_param; /**< Expires param, or -1. */ int retry_after; /**< Retry after param, or -1. */ pjsip_param other_param; /**< Other parameters. */ } pjsip_sub_state_hdr; /** * Create new Subscription-State header. * * @param pool The pool. * * @return Subscription-State header. */ PJ_DECL(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool); /** * Initialize parser for event notify module. */ PJ_DECL(void) pjsip_evsub_init_parser(void); PJ_END_DECL /** * @} */ #endif /* __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-simple/iscomposing.h ================================================ /* $Id: iscomposing.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIMPLE_ISCOMPOSING_H__ #define __PJSIP_SIMPLE_ISCOMPOSING_H__ /** * @file iscomposing.h * @brief Support for Indication of Message Composition (RFC 3994) */ #include #include /** * @defgroup PJSIP_ISCOMPOSING Message Composition Indication (RFC 3994) * @ingroup PJSIP_SIMPLE * @brief Support for Indication of Message Composition (RFC 3994) * @{ * * This implements message composition indication, as described in * RFC 3994. */ PJ_BEGIN_DECL /** * Create XML message with MIME type "application/im-iscomposing+xml" * to indicate the message composition status. * * @param pool Pool to allocate memory. * @param is_composing Message composition indication status. Set to * PJ_TRUE (or non-zero) to indicate that application * is currently composing an instant message. * @param lst_actv Optional attribute to indicate time of last * activity. If none is to be specified, the value * MUST be set to NULL. * @param content_tp Optional attribute to indicate the content type of * message being composed. If none is to be specified, * the value MUST be set to NULL. * @param refresh Optional attribute to indicate the interval when * next indication will be sent, only when * is_composing is non-zero. If none is to be * specified, the value MUST be set to -1. * * @return An XML message containing the message indication. * NULL will be returned when there's not enough * memory to allocate the message. */ PJ_DECL(pj_xml_node*) pjsip_iscomposing_create_xml(pj_pool_t *pool, pj_bool_t is_composing, const pj_time_val *lst_actv, const pj_str_t *content_tp, int refresh); /** * Create message body with Content-Type "application/im-iscomposing+xml" * to indicate the message composition status. * * @param pool Pool to allocate memory. * @param is_composing Message composition indication status. Set to * PJ_TRUE (or non-zero) to indicate that application * is currently composing an instant message. * @param lst_actv Optional attribute to indicate time of last * activity. If none is to be specified, the value * MUST be set to NULL. * @param content_tp Optional attribute to indicate the content type of * message being composed. If none is to be specified, * the value MUST be set to NULL. * @param refresh Optional attribute to indicate the interval when * next indication will be sent, only when * is_composing is non-zero. If none is to be * specified, the value MUST be set to -1. * * @return The SIP message body containing XML message * indication. NULL will be returned when there's not * enough memory to allocate the message. */ PJ_DECL(pjsip_msg_body*) pjsip_iscomposing_create_body( pj_pool_t *pool, pj_bool_t is_composing, const pj_time_val *lst_actv, const pj_str_t *content_tp, int refresh); /** * Parse the buffer and return message composition indication in the * message. * * @param pool Pool to allocate memory for the parsing process. * @param msg The message to be parsed. * @param len Length of the message. * @param p_is_composing Optional pointer to receive iscomposing status. * @param p_last_active Optional pointer to receive last active attribute. * @param p_content_type Optional pointer to receive content type attribute. * @param p_refresh Optional pointer to receive refresh time. * * @return PJ_SUCCESS if message can be successfully parsed. */ PJ_DECL(pj_status_t) pjsip_iscomposing_parse( pj_pool_t *pool, char *msg, pj_size_t len, pj_bool_t *p_is_composing, pj_str_t **p_last_active, pj_str_t **p_content_type, int *p_refresh ); /** * @} */ PJ_END_DECL #endif /* __PJSIP_SIMPLE_ISCOMPOSING_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-simple/mwi.h ================================================ /* $Id: mwi.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIMPLE_MWI_H__ #define __PJSIP_SIMPLE_MWI_H__ /** * @file mwi.h * @brief SIP Extension for MWI (RFC 3842) */ #include #include PJ_BEGIN_DECL /** * @defgroup mwi SIP Message Summary and Message Waiting Indication (RFC 3842) * @ingroup PJSIP_SIMPLE * @brief Support for SIP MWI Extension (RFC 3842) * @{ * * This module implements RFC 3842: A Message Summary and Message Waiting * Indication Event Package for the Session Initiation Protocol (SIP). * It uses the SIP Event Notification framework (evsub.h) and extends the * framework by implementing "message-summary" event package. */ /** * Initialize the MWI module and register it as endpoint module and * package to the event subscription module. * * @param endpt The endpoint instance. * @param mod_evsub The event subscription module instance. * * @return PJ_SUCCESS if the module is successfully * initialized and registered to both endpoint * and the event subscription module. */ PJ_DECL(pj_status_t) pjsip_mwi_init_module(pjsip_endpoint *endpt, pjsip_module *mod_evsub); /** * Get the MWI module instance. * * @return The MWI module instance. */ PJ_DECL(pjsip_module*) pjsip_mwi_instance(void); /** * Create MWI client subscription session. * * @param dlg The underlying dialog to use. * @param user_cb Pointer to callbacks to receive MWI subscription * events. * @param options Option flags. Currently only PJSIP_EVSUB_NO_EVENT_ID * is recognized. * @param p_evsub Pointer to receive the MWI subscription * session. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_mwi_create_uac( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, unsigned options, pjsip_evsub **p_evsub ); /** * Create MWI server subscription session. * * @param dlg The underlying dialog to use. * @param user_cb Pointer to callbacks to receive MWI subscription * events. * @param rdata The incoming SUBSCRIBE request that creates the event * subscription. * @param p_evsub Pointer to receive the MWI subscription * session. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_mwi_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, pjsip_evsub **p_evsub ); /** * Forcefully destroy the MWI subscription. This function should only * be called on special condition, such as when the subscription * initialization has failed. For other conditions, application MUST terminate * the subscription by sending the appropriate un(SUBSCRIBE) or NOTIFY. * * @param sub The MWI subscription. * @param notify Specify whether the state notification callback * should be called. * * @return PJ_SUCCESS if subscription session has been destroyed. */ PJ_DECL(pj_status_t) pjsip_mwi_terminate( pjsip_evsub *sub, pj_bool_t notify ); /** * Call this function to create request to initiate MWI subscription, to * refresh subcription, or to request subscription termination. * * @param sub Client subscription instance. * @param expires Subscription expiration. If the value is set to zero, * this will request unsubscription. * @param p_tdata Pointer to receive the request. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_mwi_initiate( pjsip_evsub *sub, pj_int32_t expires, pjsip_tx_data **p_tdata); /** * Accept the incoming subscription request by sending 2xx response to * incoming SUBSCRIBE request. * * @param sub Server subscription instance. * @param rdata The incoming subscription request message. * @param st_code Status code, which MUST be final response. * @param hdr_list Optional list of headers to be added in the response. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_mwi_accept( pjsip_evsub *sub, pjsip_rx_data *rdata, int st_code, const pjsip_hdr *hdr_list ); /** * For notifier, create NOTIFY request to subscriber, and set the state * of the subscription. * * @param sub The server subscription (notifier) instance. * @param state New state to set. * @param state_str The state string name, if state contains value other * than active, pending, or terminated. Otherwise this * argument is ignored. * @param reason Specify reason if new state is terminated, otherwise * put NULL. * @param mime_type MIME type/content type of the message body. * @param body Message body to be included in the NOTIFY request. * @param p_tdata Pointer to receive the request. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_mwi_notify( pjsip_evsub *sub, pjsip_evsub_state state, const pj_str_t *state_str, const pj_str_t *reason, const pjsip_media_type *mime_type, const pj_str_t *body, pjsip_tx_data **p_tdata); /** * Create NOTIFY request containing message body from the last NOITFY * message created. * * @param sub Server subscription object. * @param p_tdata Pointer to receive request. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_mwi_current_notify( pjsip_evsub *sub, pjsip_tx_data **p_tdata ); /** * Send request message that was previously created with initiate(), notify(), * or current_notify(). Application may also send request created with other * functions, e.g. authentication. But the request MUST be either request * that creates/refresh subscription or NOTIFY request. * * @param sub The subscription object. * @param tdata Request message to be sent. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_mwi_send_request( pjsip_evsub *sub, pjsip_tx_data *tdata ); /** * @} */ PJ_END_DECL #endif /* __PJSIP_SIMPLE_MWI_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-simple/pidf.h ================================================ /* $Id: pidf.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIMPLE_PIDF_H__ #define __PJSIP_SIMPLE_PIDF_H__ /** * @file pidf.h * @brief PIDF/Presence Information Data Format (RFC 3863) */ #include #include PJ_BEGIN_DECL /** * @defgroup PJSIP_SIMPLE_PIDF PIDF/Presence Information Data Format (RFC 3863) * @ingroup PJSIP_SIMPLE * @brief Support for PIDF/Presence Information Data Format (RFC 3863) * @{ * * This file provides tools for manipulating Presence Information Data * Format (PIDF) as described in RFC 3863. */ typedef struct pj_xml_node pjpidf_pres; typedef struct pj_xml_node pjpidf_tuple; typedef struct pj_xml_node pjpidf_status; typedef struct pj_xml_node pjpidf_note; typedef struct pjpidf_status_op { void (*construct)(pj_pool_t*, pjpidf_status*); pj_bool_t (*is_basic_open)(const pjpidf_status*); void (*set_basic_open)(pjpidf_status*, pj_bool_t); } pjpidf_status_op; typedef struct pjpidf_tuple_op { void (*construct)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); const pj_str_t* (*get_id)(const pjpidf_tuple* ); void (*set_id)(pj_pool_t*, pjpidf_tuple *, const pj_str_t*); pjpidf_status* (*get_status)(pjpidf_tuple* ); const pj_str_t* (*get_contact)(const pjpidf_tuple*); void (*set_contact)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); void (*set_contact_prio)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); const pj_str_t* (*get_contact_prio)(const pjpidf_tuple*); pjpidf_note* (*add_note)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); pjpidf_note* (*get_first_note)(pjpidf_tuple*); pjpidf_note* (*get_next_note)(pjpidf_tuple*, pjpidf_note*); const pj_str_t* (*get_timestamp)(const pjpidf_tuple*); void (*set_timestamp)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); void (*set_timestamp_np)(pj_pool_t*,pjpidf_tuple*, pj_str_t*); } pjpidf_tuple_op; typedef struct pjpidf_pres_op { void (*construct)(pj_pool_t*, pjpidf_pres*, const pj_str_t*); pjpidf_tuple* (*add_tuple)(pj_pool_t*, pjpidf_pres*, const pj_str_t*); pjpidf_tuple* (*get_first_tuple)(pjpidf_pres*); pjpidf_tuple* (*get_next_tuple)(pjpidf_pres*, pjpidf_tuple*); pjpidf_tuple* (*find_tuple)(pjpidf_pres*, const pj_str_t*); void (*remove_tuple)(pjpidf_pres*, pjpidf_tuple*); pjpidf_note* (*add_note)(pj_pool_t*, pjpidf_pres*, const pj_str_t*); pjpidf_note* (*get_first_note)(pjpidf_pres*); pjpidf_note* (*get_next_note)(pjpidf_pres*, pjpidf_note*); } pjpidf_pres_op; extern struct pjpidf_op_desc { pjpidf_pres_op pres; pjpidf_tuple_op tuple; pjpidf_status_op status; } pjpidf_op; /****************************************************************************** * Top level API for managing presence document. *****************************************************************************/ PJ_DECL(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity); PJ_DECL(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len); PJ_DECL(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len); /****************************************************************************** * API for managing Presence node. *****************************************************************************/ PJ_DECL(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres, const pj_str_t *entity); PJ_DECL(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres, const pj_str_t *id); PJ_DECL(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres); PJ_DECL(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres, pjpidf_tuple *t); PJ_DECL(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres, const pj_str_t *id); PJ_DECL(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, pjpidf_tuple*); PJ_DECL(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres, const pj_str_t *text); PJ_DECL(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres); PJ_DECL(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres*, pjpidf_note*); /****************************************************************************** * API for managing Tuple node. *****************************************************************************/ PJ_DECL(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *id); PJ_DECL(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t ); PJ_DECL(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *id); PJ_DECL(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t); PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t); PJ_DECL(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *contact); PJ_DECL(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *prio); PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t); PJ_DECL(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *text); PJ_DECL(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t); PJ_DECL(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n); PJ_DECL(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t); PJ_DECL(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *ts); PJ_DECL(void) pjpidf_tuple_set_timestamp_np( pj_pool_t*, pjpidf_tuple *t, pj_str_t *ts); /****************************************************************************** * API for managing Status node. *****************************************************************************/ PJ_DECL(void) pjpidf_status_construct(pj_pool_t*, pjpidf_status*); PJ_DECL(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status*); PJ_DECL(void) pjpidf_status_set_basic_open(pjpidf_status*, pj_bool_t); /** * @} */ PJ_END_DECL #endif /* __PJSIP_SIMPLE_PIDF_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-simple/presence.h ================================================ /* $Id: presence.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIMPLE_PRESENCE_H__ #define __PJSIP_SIMPLE_PRESENCE_H__ /** * @file presence.h * @brief SIP Extension for Presence (RFC 3856) */ #include #include #include #include PJ_BEGIN_DECL /** * @defgroup PJSIP_SIMPLE_PRES SIP Extension for Presence (RFC 3856) * @ingroup PJSIP_SIMPLE * @brief Support for SIP Extension for Presence (RFC 3856) * @{ * * This module contains the implementation of SIP Presence Extension as * described in RFC 3856. It uses the SIP Event Notification framework * (evsub.h) and extends the framework by implementing "presence" * event package. */ /** * Initialize the presence module and register it as endpoint module and * package to the event subscription module. * * @param endpt The endpoint instance. * @param mod_evsub The event subscription module instance. * * @return PJ_SUCCESS if the module is successfully * initialized and registered to both endpoint * and the event subscription module. */ PJ_DECL(pj_status_t) pjsip_pres_init_module(pjsip_endpoint *endpt, pjsip_module *mod_evsub); /** * Get the presence module instance. * * @return The presence module instance. */ PJ_DECL(pjsip_module*) pjsip_pres_instance(void); /** * Maximum presence status info. */ #define PJSIP_PRES_STATUS_MAX_INFO 8 /** * This structure describes presence status of a presentity. */ struct pjsip_pres_status { unsigned info_cnt; /**< Number of info in the status. */ struct { pj_bool_t basic_open; /**< Basic status/availability. */ pjrpid_element rpid; /**< Optional RPID info. */ pj_str_t id; /**< Tuple id. */ pj_str_t contact; /**< Optional contact address. */ pj_xml_node *tuple_node; /**< Pointer to tuple XML node of parsed PIDF body received from remote agent. Only valid for client subscription. If the last received NOTIFY request does not contain any PIDF body, this valud will be set to NULL */ } info[PJSIP_PRES_STATUS_MAX_INFO]; /**< Array of info. */ pj_bool_t _is_valid; /**< Internal flag. */ }; /** * @see pjsip_pres_status */ typedef struct pjsip_pres_status pjsip_pres_status; /** * Create presence client subscription session. * * @param dlg The underlying dialog to use. * @param user_cb Pointer to callbacks to receive presence subscription * events. * @param options Option flags. Currently only PJSIP_EVSUB_NO_EVENT_ID * is recognized. * @param p_evsub Pointer to receive the presence subscription * session. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, unsigned options, pjsip_evsub **p_evsub ); /** * Create presence server subscription session. * * @param dlg The underlying dialog to use. * @param user_cb Pointer to callbacks to receive presence subscription * events. * @param rdata The incoming SUBSCRIBE request that creates the event * subscription. * @param p_evsub Pointer to receive the presence subscription * session. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, pjsip_evsub **p_evsub ); /** * Forcefully destroy the presence subscription. This function should only * be called on special condition, such as when the subscription * initialization has failed. For other conditions, application MUST terminate * the subscription by sending the appropriate un(SUBSCRIBE) or NOTIFY. * * @param sub The presence subscription. * @param notify Specify whether the state notification callback * should be called. * * @return PJ_SUCCESS if subscription session has been destroyed. */ PJ_DECL(pj_status_t) pjsip_pres_terminate( pjsip_evsub *sub, pj_bool_t notify ); /** * Call this function to create request to initiate presence subscription, to * refresh subcription, or to request subscription termination. * * @param sub Client subscription instance. * @param expires Subscription expiration. If the value is set to zero, * this will request unsubscription. * @param p_tdata Pointer to receive the request. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub, pj_int32_t expires, pjsip_tx_data **p_tdata); /** * Add a list of headers to the subscription instance. The list of headers * will be added to outgoing presence subscription requests. * * @param sub Subscription instance. * @param hdr_list List of headers to be added. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_pres_add_header( pjsip_evsub *sub, const pjsip_hdr *hdr_list ); /** * Accept the incoming subscription request by sending 2xx response to * incoming SUBSCRIBE request. * * @param sub Server subscription instance. * @param rdata The incoming subscription request message. * @param st_code Status code, which MUST be final response. * @param hdr_list Optional list of headers to be added in the response. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub, pjsip_rx_data *rdata, int st_code, const pjsip_hdr *hdr_list ); /** * For notifier, create NOTIFY request to subscriber, and set the state * of the subscription. Application MUST set the presence status to the * appropriate state (by calling #pjsip_pres_set_status()) before calling * this function. * * @param sub The server subscription (notifier) instance. * @param state New state to set. * @param state_str The state string name, if state contains value other * than active, pending, or terminated. Otherwise this * argument is ignored. * @param reason Specify reason if new state is terminated, otherwise * put NULL. * @param p_tdata Pointer to receive the request. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_pres_notify( pjsip_evsub *sub, pjsip_evsub_state state, const pj_str_t *state_str, const pj_str_t *reason, pjsip_tx_data **p_tdata); /** * Create NOTIFY request to reflect current subscription status. * * @param sub Server subscription object. * @param p_tdata Pointer to receive request. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub, pjsip_tx_data **p_tdata ); /** * Send request message that was previously created with initiate(), notify(), * or current_notify(). Application may also send request created with other * functions, e.g. authentication. But the request MUST be either request * that creates/refresh subscription or NOTIFY request. * * @param sub The subscription object. * @param tdata Request message to be sent. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub, pjsip_tx_data *tdata ); /** * Get the presence status. Client normally would call this function * after receiving NOTIFY request from server. * * @param sub The client or server subscription. * @param status The structure to receive presence status. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub, pjsip_pres_status *status ); /** * Set the presence status. This operation is only valid for server * subscription. After calling this function, application would need to * send NOTIFY request to client. * * @param sub The server subscription. * @param status Status to be set. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub, const pjsip_pres_status *status ); /** * This is a utility function to create PIDF message body from PJSIP * presence status (pjsip_pres_status). * * @param pool The pool to allocate memory for the message body. * @param status Presence status to be converted into PIDF message * body. * @param entity The entity ID, which normally is equal to the * presentity ID publishing this presence info. * @param p_body Pointer to receive the SIP message body. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_pres_create_pidf( pj_pool_t *pool, const pjsip_pres_status *status, const pj_str_t *entity, pjsip_msg_body **p_body ); /** * This is a utility function to create X-PIDF message body from PJSIP * presence status (pjsip_pres_status). * * @param pool The pool to allocate memory for the message body. * @param status Presence status to be converted into X-PIDF message * body. * @param entity The entity ID, which normally is equal to the * presentity ID publishing this presence info. * @param p_body Pointer to receive the SIP message body. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_pres_create_xpidf(pj_pool_t *pool, const pjsip_pres_status *status, const pj_str_t *entity, pjsip_msg_body **p_body ); /** * This is a utility function to parse PIDF body into PJSIP presence status. * * @param rdata The incoming SIP message containing the PIDF body. * @param pool Pool to allocate memory to copy the strings into * the presence status structure. * @param status The presence status to be initialized. * * @return PJ_SUCCESS on success. * * @see pjsip_pres_parse_pidf2() */ PJ_DECL(pj_status_t) pjsip_pres_parse_pidf(pjsip_rx_data *rdata, pj_pool_t *pool, pjsip_pres_status *status); /** * This is a utility function to parse PIDF body into PJSIP presence status. * * @param body Text body, with one extra space at the end to place * NULL character temporarily during parsing. * @param body_len Length of the body, not including the NULL termination * character. * @param pool Pool to allocate memory to copy the strings into * the presence status structure. * @param status The presence status to be initialized. * * @return PJ_SUCCESS on success. * * @see pjsip_pres_parse_pidf() */ PJ_DECL(pj_status_t) pjsip_pres_parse_pidf2(char *body, unsigned body_len, pj_pool_t *pool, pjsip_pres_status *status); /** * This is a utility function to parse X-PIDF body into PJSIP presence status. * * @param rdata The incoming SIP message containing the X-PIDF body. * @param pool Pool to allocate memory to copy the strings into * the presence status structure. * @param status The presence status to be initialized. * * @return PJ_SUCCESS on success. * * @see pjsip_pres_parse_xpidf2() */ PJ_DECL(pj_status_t) pjsip_pres_parse_xpidf(pjsip_rx_data *rdata, pj_pool_t *pool, pjsip_pres_status *status); /** * This is a utility function to parse X-PIDF body into PJSIP presence status. * * @param body Text body, with one extra space at the end to place * NULL character temporarily during parsing. * @param body_len Length of the body, not including the NULL termination * character. * @param pool Pool to allocate memory to copy the strings into * the presence status structure. * @param status The presence status to be initialized. * * @return PJ_SUCCESS on success. * * @see pjsip_pres_parse_xpidf() */ PJ_DECL(pj_status_t) pjsip_pres_parse_xpidf2(char *body, unsigned body_len, pj_pool_t *pool, pjsip_pres_status *status); /** * @} */ PJ_END_DECL #endif /* __PJSIP_SIMPLE_PRESENCE_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-simple/publish.h ================================================ /* $Id: publish.h 4173 2012-06-20 10:39:05Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIMPLE_PUBLISH_H__ #define __PJSIP_SIMPLE_PUBLISH_H__ /** * @file publish.h * @brief SIP Extension for Event State Publication (PUBLISH, RFC 3903) */ #include #include PJ_BEGIN_DECL /** @defgroup PJSIP_SIMPLE_PUBLISH SIP Event State Publication (PUBLISH, RFC 3903) @ingroup PJSIP_SIMPLE @brief Support for SIP Event State Publication (PUBLISH, RFC 3903) @{ This module contains the implementation of Session Initiation Protocol (SIP) Extension for Event State Publication (PUBLISH) as defined by RFC 3903. */ /** * The SIP PUBLISH method constant. */ extern const pjsip_method pjsip_publish_method; /***************************************************************************** * @defgroup PJSIP_SIMPLE_PUBLISH_CLIENT SIP Event State Publication Client * @ingroup PJSIP_SIMPLE * @brief Event State Publication Clien * @{ */ /** Expiration not specified. */ #define PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED ((pj_uint32_t)0xFFFFFFFFUL) /** * Opaque declaration for client side event publication session. */ typedef struct pjsip_publishc pjsip_publishc; /** * Client publication options. Application should initialize this structure * with its default values by calling #pjsip_publishc_opt_default() */ typedef struct pjsip_publishc_opt { /** * Specify whether the client publication session should queue the * PUBLISH request should there be another PUBLISH transaction still * pending. If this is set to false, the client will return error * on the PUBLISH request if there is another PUBLISH transaction still * in progress. * * Default: PJSIP_PUBLISHC_QUEUE_REQUEST */ pj_bool_t queue_request; } pjsip_publishc_opt; /** Structure to hold parameters when calling application's callback. * The application's callback is called when the client publication process * has finished. */ struct pjsip_publishc_cbparam { pjsip_publishc *pubc; /**< Client publication structure. */ void *token; /**< Arbitrary token. */ pj_status_t status; /**< Error status. */ int code; /**< SIP status code received. */ pj_str_t reason; /**< SIP reason phrase received. */ pjsip_rx_data *rdata; /**< The complete received response. */ int expiration;/**< Next expiration interval. If the value is -1, it means the session will not renew itself. */ }; /** Type declaration for callback to receive publication result. */ typedef void pjsip_publishc_cb(struct pjsip_publishc_cbparam *param); /** * Initialize client publication session option with default values. * * @param opt The option. */ PJ_DECL(void) pjsip_publishc_opt_default(pjsip_publishc_opt *opt); /** * Initialize client publication module. * * @param endpt SIP endpoint. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_publishc_init_module(pjsip_endpoint *endpt); /** * Create client publication structure. * * @param endpt Endpoint, used to allocate pool from. * @param opt Options, or NULL to specify default options. * @param token Opaque data to be associated with the client publication. * @param cb Pointer to callback function to receive publication status. * @param p_pubc Pointer to receive client publication structure. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_publishc_create( pjsip_endpoint *endpt, const pjsip_publishc_opt *opt, void *token, pjsip_publishc_cb *cb, pjsip_publishc **p_pubc); /** * Destroy client publication structure. If a publication transaction is * in progress, then the structure will be deleted only after a final response * has been received, and in this case, the callback won't be called. * * @param pubc The client publication structure. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_publishc_destroy(pjsip_publishc *pubc); /** * Get the memory pool associated with a publication client session. * * @param pubc The client publication structure. * @return pool handle. */ PJ_DECL(pj_pool_t*) pjsip_publishc_get_pool(pjsip_publishc *pubc); /** * Initialize client publication structure with various information needed to * perform the publication. * * @param pubc The client publication structure. * @param event The Event identification (e.g. "presence"). * @param target_uri The URI of the presentity which the which the status * is being published. * @param from_uri The URI of the endpoint who sends the event * publication. Normally the value would be the same as * target_uri. * @param to_uri The URI to be put in To header. Normally the value * would be the same as target_uri. * @param expires The default expiration of the event publication. * If the value PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED is * given, then no default expiration will be applied. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_publishc_init(pjsip_publishc *pubc, const pj_str_t *event, const pj_str_t *target_uri, const pj_str_t *from_uri, const pj_str_t *to_uri, pj_uint32_t expires); /** * Set authentication credentials to use by this publication. * * @param pubc The publication structure. * @param count Number of credentials in the array. * @param c Array of credentials. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_publishc_set_credentials(pjsip_publishc *pubc, int count, const pjsip_cred_info c[]); /** * Set route set to be used for outgoing requests. * * @param pubc The client publication structure. * @param rs List containing Route headers. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_publishc_set_route_set(pjsip_publishc *pubc, const pjsip_route_hdr *rs); /** * Set list of headers to be added to each PUBLISH request generated by * the client publication session. Note that application can also add * the headers to the request after calling #pjsip_publishc_publish() * or #pjsip_publishc_unpublish(), but the benefit of this function is * the headers will also be added to requests generated internally by * the session, such as during session renewal/refresh. * * Note that calling this function will clear the previously added list * of headers. * * @param pubc The client publication structure. * @param hdr_list The list of headers. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_publishc_set_headers(pjsip_publishc *pubc, const pjsip_hdr *hdr_list); /** * Set the "sent-by" field of the Via header for outgoing requests. * * @param pubc The client publication structure. * @param via_addr Set via_addr to use for the Via header or NULL to use * the transport's published name. * @param via_tp via_addr will only be used if we are using via_tp * transport. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_publishc_set_via_sent_by(pjsip_publishc *pubc, pjsip_host_port *via_addr, pjsip_transport *via_tp); /** * Create PUBLISH request for the specified client publication structure. * Application can use this function to both create initial publication * or to modify existing publication. * * After the PUBLISH request is created, application MUST fill in the * body part of the request with the appropriate content for the Event * being published. * * Note that publication refresh are handled automatically by the session * (as long as auto_refresh argument below is non-zero), and application * should not use this function to perform publication refresh. * * @param pubc The client publication session. * @param auto_refresh If non zero, the library will automatically * refresh the next publication until application * unpublish. * @param p_tdata Pointer to receive the PUBLISH request. Note that * the request DOES NOT have a message body. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_publishc_publish(pjsip_publishc *pubc, pj_bool_t auto_refresh, pjsip_tx_data **p_tdata); /** * Create PUBLISH request to unpublish the current client publication. * * @param pubc The client publication structure. * @param p_tdata Pointer to receive the PUBLISH request. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_publishc_unpublish(pjsip_publishc *pubc, pjsip_tx_data **p_tdata); /** * Update the client publication expiration value. Note that this DOES NOT * automatically send outgoing PUBLISH request to update the publication * session. If application wants to do this, then it must construct a * PUBLISH request and send it to the server. * * @param pubc The client publication structure. * @param expires The new expires value. * * @return PU_SUCCESS on successfull. */ PJ_DECL(pj_status_t) pjsip_publishc_update_expires(pjsip_publishc *pubc, pj_uint32_t expires ); /** * Sends outgoing PUBLISH request. The process will complete asynchronously, * and application will be notified via the callback when the process * completes. * * If the session has another PUBLISH request outstanding, the behavior * depends on whether request queueing is enabled in the session (this was * set by setting \a queue_request field of #pjsip_publishc_opt to true * when calling #pjsip_publishc_create(). Default is true). If request * queueing is enabled, the request will be queued and the function will * return PJ_EPENDING. One the outstanding request is complete, the queued * request will be sent automatically. If request queueing is disabled, the * function will reject the request and return PJ_EBUSY. * * @param pubc The client publication structure. * @param tdata Transmit data. * * @return - PJ_SUCCESS on success, or * - PJ_EPENDING if request is queued, or * - PJ_EBUSY if request is rejected because another PUBLISH * request is in progress, or * - other status code to indicate the error. */ PJ_DECL(pj_status_t) pjsip_publishc_send(pjsip_publishc *pubc, pjsip_tx_data *tdata); /** * @} */ /** * @} */ PJ_END_DECL #endif /* __PJSIP_SIMPLE_PUBLISH_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-simple/rpid.h ================================================ /* $Id: rpid.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIMPLE_RPID_H__ #define __PJSIP_SIMPLE_RPID_H__ /** * @file rpid.h * @brief RPID: Rich Presence Extensions to the PIDF (RFC 4480) */ #include #include PJ_BEGIN_DECL /** * @defgroup PJSIP_SIMPLE_RPID RPID/Rich Presence Extensions to PIDF (RFC 4480) * @ingroup PJSIP_SIMPLE * @brief RPID/Rich Presence Extensions to PIDF (RFC 4480) * @{ * * This file provides tools for managing subset of RPID elements into * PIDF document. */ /** * This enumeration describes subset of standard activities as * described by RFC 4880, RPID: Rich Presence Extensions to the * Presence Information Data Format (PIDF). */ typedef enum pjrpid_activity { /** Activity is unknown. The activity would then be conceived * in the "note" field. */ PJRPID_ACTIVITY_UNKNOWN, /** The person is away */ PJRPID_ACTIVITY_AWAY, /** The person is busy */ PJRPID_ACTIVITY_BUSY } pjrpid_activity; /** * This enumeration describes types of RPID element. */ typedef enum pjrpid_element_type { /** RPID element */ PJRPID_ELEMENT_TYPE_PERSON } pjrpid_element_type; /** * This structure describes person information in RPID document. */ typedef struct pjrpid_element { /** Element type. */ pjrpid_element_type type; /** Optional id to set on the element. */ pj_str_t id; /** Activity type. */ pjrpid_activity activity; /** Optional text describing the person/element. */ pj_str_t note; } pjrpid_element; /** * Duplicate RPID element. * * @param pool Pool. * @param dst Destination structure. * @param src Source structure. */ PJ_DECL(void) pjrpid_element_dup(pj_pool_t *pool, pjrpid_element *dst, const pjrpid_element *src); /** * Add RPID element information into existing PIDF document. This will also * add the appropriate XML namespace attributes into the presence's XML * node, if the attributes are not already present, and also a element * to the first element of the PIDF document. * * @param pres The PIDF presence document. * @param pool Pool. * @param options Currently unused, and must be zero. * @param elem RPID element information to be added into the PIDF * document. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjrpid_add_element(pjpidf_pres *pres, pj_pool_t *pool, unsigned options, const pjrpid_element *elem); /** * Get RPID element information from PIDF document, if any. * * @param pres The PIDF document containing RPID elements. * @param pool Pool to duplicate the information. * @param elem Structure to receive the element information. * * @return PJ_SUCCESS if the document does contain RPID element * and the information has been parsed successfully. */ PJ_DECL(pj_status_t) pjrpid_get_element(const pjpidf_pres *pres, pj_pool_t *pool, pjrpid_element *elem); /** * @} */ PJ_END_DECL #endif /* __PJSIP_SIMPLE_RPID_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-simple/types.h ================================================ /* $Id: types.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIMPLE_TYPES_H__ #define __PJSIP_SIMPLE_TYPES_H__ #include #define PJSIP_EVSUB_POOL_LEN 4000 #define PJSIP_EVSUB_POOL_INC 4000 #endif /* __PJSIP_SIMPLE_TYPES_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-simple/xpidf.h ================================================ /* $Id: xpidf.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIMPLE_XPIDF_H__ #define __PJSIP_SIMPLE_XPIDF_H__ /** * @file xpidf.h * @brief XPIDF/Presence Information Data Format */ #include #include PJ_BEGIN_DECL /** * @defgroup PJSIP_SIMPLE_XPIDF XPIDF/Presence Information Data Format * @ingroup PJSIP_SIMPLE * @brief Support for XPIDF/Presence Information Data Format * @{ * * This is an old presence data format as described in: * draft-rosenberg-impp-pidf-00.txt. * * We won't support this format extensively here, as it seems there's not * too many implementations support this anymore, as it shouldn't. */ /** Type definitions for XPIDF root document. */ typedef pj_xml_node pjxpidf_pres; /** * Create a new XPIDF document. * * @param pool Pool. * @param uri URI to set in the XPIDF document. * * @return XPIDF document. */ PJ_DECL(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri); /** * Parse XPIDF document. * * @param pool Pool. * @param text Input text. * @param len Length of input text. * * @return XPIDF document. */ PJ_DECL(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len); /** * Print XPIDF document. * * @param pres The XPIDF document to print. * @param text Buffer to place the output. * @param len Length of the buffer. * * @return The length printed. */ PJ_DECL(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len); /** * Get URI in the XPIDF document * * @param pres XPIDF document * * @return The URI, or an empty string. */ PJ_DECL(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres); /** * Set the URI of the XPIDF document. * * @param pool Pool. * @param pres The XPIDF document. * @param uri URI to set in the XPIDF document. * * @return Zero on success. */ PJ_DECL(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres, const pj_str_t *uri); /** * Get presence status in the XPIDF document. * * @param pres XPIDF document. * * @return True to indicate the contact is online. */ PJ_DECL(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres); /** * Set presence status in the XPIDF document. * * @param pres XPIDF document. * @param status Status to set, True for online, False for offline. * * @return Zero on success. */ PJ_DECL(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t status); /** * @} */ PJ_END_DECL #endif /* __PJSIP_SIMPLE_XPIDF_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-ua/sip_100rel.h ================================================ /* $Id: sip_100rel.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __SIP_100REL_H__ #define __SIP_100REL_H__ /** * @file sip_100rel.h * @brief PRACK (Reliability of Provisional Responses) */ #include /** * @defgroup PJSIP_100REL 100rel/PRACK - Reliability of Provisional Responses * @ingroup PJSIP_HIGH_UA * @brief PRACK - Reliability of Provisional Responses * @{ * * This module provides management of Reliability of Provisional Responses * (\a 100rel and \a PRACK), as described in RFC 3262. * * Other than the #pjsip_100rel_init_module() function, the 100rel API * exported by this module are not intended to be used by application, but * rather they will be invoked by the \ref PJSIP_INV. * * \section pjsip_100rel_using Using Reliable Provisional Response * * \subsection pjsip_100rel_init Initializing 100rel Module * * Application must explicitly initialize 100rel module by calling * #pjsip_100rel_init_module() in application initialization function. * * Once the 100rel module is initialized, it will register \a PRACK method * in \a Allow header, and \a 100rel tag in \a Supported header. * * \subsection pjsip_100rel_sess Using 100rel in a Session * * For UAC, \a 100rel support will be enabled in the session if \a 100rel * support is enabled in the library (default is yes). * Outgoing INVITE request will include \a 100rel tag in \a Supported * header and \a PRACK method in \a Allow header. When callee endpoint * sends reliable provisional responses, the UAC will automatically send * \a PRACK request to acknowledge the response. If callee endpoint doesn't * send reliable provisional response, the response will be handled using * normal, non-100rel procedure (that is, \a PRACK will not be sent). * * If the UAC wants to mandate \a 100rel support, it can specify * #PJSIP_INV_REQUIRE_100REL in the \a options argument when calling * #pjsip_inv_create_uac(). In this case, PJSIP will add \a 100rel tag * in the \a Require header of the outgoing INVITE request. * * For UAS, if it wants to support \a 100rel but not to mandate it, * it must specify #PJSIP_INV_SUPPORT_100REL flag in the \a options * argument when calling #pjsip_inv_verify_request(), and pass the same * \a options variable when calling #pjsip_inv_verify_request. If UAC had * specified \a 100rel in it's list of extensions in \a Require header, * the UAS will send provisional responses reliably. If UAC only listed * \a 100rel in its \a Supported header but not in \a Require header, * or if UAC does not list \a 100rel support at all, the UAS WILL NOT * send provisional responses reliably. * The snippet below can be used to accomplish this task: * * \verbatim unsigned options = 0; options |= PJSIP_INV_SUPPORT_100REL; status = pjsip_inv_verify_request(rdata, &options, answer, NULL, endpt, &resp); if (status != PJ_SUCCESS) { // INVITE request cannot be handled. // Reject the request with the response in resp. ... return; } // Create UAS dialog, populate Contact header, etc. ... // Create UAS invite session status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv); .. \endverbatim * * For another requirement, if UAS wants to mandate \a 100rel support, * it can specify #PJSIP_INV_REQUIRE_100REL flag when calling * #pjsip_inv_verify_request(), and pass the \a options when calling * #pjsip_inv_verify_request. In this case, * \a 100rel extension will be used if UAC specifies \a 100rel in its * \a Supported header. If UAC does not list \a 100rel in \a Supported header, * the incoming INVITE request will be rejected with 421 (Extension Required) * response. For the sample code, it should be identical to the snippet * above, except that application must specify #PJSIP_INV_REQUIRE_100REL * flag in the \a options instead of #PJSIP_INV_SUPPORT_100REL. * * For yet another requirement, if UAS does not want to support * \a 100rel extension, it can reject incoming INVITE request with * 420 (Bad Extension) response whenever incoming INVITE request has * \a 100rel tag in its \a Require header. This can be done by specifying * zero as the \a options when calling #pjsip_inv_verify_request(). */ PJ_BEGIN_DECL /** * PRACK method constant. * @see pjsip_get_prack_method() */ PJ_DECL_DATA(const pjsip_method) pjsip_prack_method; /** * Get #pjsip_invite_method constant. */ PJ_DECL(const pjsip_method*) pjsip_get_prack_method(void); /** * Initialize 100rel module. This function must be called once during * application initialization, to register 100rel module to SIP endpoint. * * @param endpt The SIP endpoint instance. * * @return PJ_SUCCESS if module is successfully initialized. */ PJ_DECL(pj_status_t) pjsip_100rel_init_module(pjsip_endpoint *endpt); /** * Add 100rel support to the specified invite session. This function will * be called internally by the invite session if it detects that the * session needs 100rel support. * * @param inv The invite session. * * @return PJ_SUCCESS on successful. */ PJ_DECL(pj_status_t) pjsip_100rel_attach(pjsip_inv_session *inv); /** * Check if incoming response has reliable provisional response feature. * * @param rdata Receive data buffer containing the response. * * @return PJ_TRUE if the provisional response is reliable. */ PJ_DECL(pj_bool_t) pjsip_100rel_is_reliable(pjsip_rx_data *rdata); /** * Create PRACK request for the incoming reliable provisional response. * Note that PRACK request MUST be sent using #pjsip_100rel_send_prack(). * * @param inv The invite session. * @param rdata The incoming reliable provisional response. * @param p_tdata Upon return, it will be initialized with the * PRACK request. * * @return PJ_SUCCESS on successful. */ PJ_DECL(pj_status_t) pjsip_100rel_create_prack(pjsip_inv_session *inv, pjsip_rx_data *rdata, pjsip_tx_data **p_tdata); /** * Send PRACK request. * * @param inv The invite session. * @param tdata The PRACK request. * * @return PJ_SUCCESS on successful. */ PJ_DECL(pj_status_t) pjsip_100rel_send_prack(pjsip_inv_session *inv, pjsip_tx_data *tdata); /** * Handle incoming PRACK request. * * @param inv The invite session. * @param rdata Incoming PRACK request. * * @return PJ_SUCCESS on successful. */ PJ_DECL(pj_status_t) pjsip_100rel_on_rx_prack(pjsip_inv_session *inv, pjsip_rx_data *rdata); /** * Transmit INVITE response (provisional or final) reliably according to * 100rel specification. The 100rel module will take care of retransmitting * or enqueueing the response according to the current state of the * reliable response processing. This function will be called internally * by invite session. * * @param inv The invite session. * @param tdata The INVITE response. * * @return PJ_SUCCESS on successful. */ PJ_DECL(pj_status_t) pjsip_100rel_tx_response(pjsip_inv_session *inv, pjsip_tx_data *tdata); /** * Notify 100rel module that the invite session has been disconnected. * * @param inv The invite session. * * @return PJ_SUCCESS on successful. */ PJ_DECL(pj_status_t) pjsip_100rel_end_session(pjsip_inv_session *inv); PJ_END_DECL /** * @} */ #endif /* __SIP_100REL_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-ua/sip_inv.h ================================================ /* $Id: sip_inv.h 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __SIP_INVITE_SESSION_H__ #define __SIP_INVITE_SESSION_H__ /** * @file sip_inv.h * @brief INVITE sessions */ #include #include /** * @defgroup PJSIP_HIGH_UA User Agent Library * @brief Mid-level User Agent Library. * * This is the high level user agent library, which consists of: * - @ref PJSIP_INV, to encapsulate INVITE sessions and SDP * negotiation in the session, * - @ref PJSUA_REGC, high level client registration API, and * - @ref PJSUA_XFER. * * More detailed information is explained in * PJSIP Developer's Guide * PDF document, and readers are encouraged to read the document to * get the concept behind dialog, dialog usages, and INVITE sessions. * * The User Agent Library is implemented in pjsip-ua static * library. */ /** * @defgroup PJSIP_INV INVITE Session * @ingroup PJSIP_HIGH_UA * @brief Provides INVITE session management. * @{ * * The INVITE session uses the @ref PJSIP_DIALOG framework to manage * the underlying dialog, and is one type of usages that can use * a particular dialog instance (other usages are event subscription, * discussed in @ref PJSIP_EVENT_NOT). The INVITE session manages * the life-time of the session, and also manages the SDP negotiation. * * Application must link with pjsip-ua static library to use this API. * * More detailed information is explained in * PJSIP Developer's Guide * PDF document, and readers are encouraged to read the document to * get the concept behind dialog, dialog usages, and INVITE sessions. * * The INVITE session does NOT manage media. If application wants to * use API that encapsulates both signaling and media in a very easy * to use API, it can use @ref PJSUA_LIB for this purpose. */ PJ_BEGIN_DECL /** * @see pjsip_inv_session */ typedef struct pjsip_inv_session pjsip_inv_session; /** * This enumeration describes invite session state. */ typedef enum pjsip_inv_state { PJSIP_INV_STATE_NULL, /**< Before INVITE is sent or received */ PJSIP_INV_STATE_CALLING, /**< After INVITE is sent */ PJSIP_INV_STATE_INCOMING, /**< After INVITE is received. */ PJSIP_INV_STATE_EARLY, /**< After response with To tag. */ PJSIP_INV_STATE_CONNECTING, /**< After 2xx is sent/received. */ PJSIP_INV_STATE_CONFIRMED, /**< After ACK is sent/received. */ PJSIP_INV_STATE_DISCONNECTED, /**< Session is terminated. */ } pjsip_inv_state; /** * This structure contains callbacks to be registered by application to * receieve notifications from the framework about various events in * the invite session. */ typedef struct pjsip_inv_callback { /** * This callback is called when the invite sesion state has changed. * Application should inspect the session state (inv_sess->state) to get * the current state of the session. * * This callback is mandatory. * * @param inv The invite session. * @param e The event which has caused the invite session's * state to change. */ void (*on_state_changed)(pjsip_inv_session *inv, pjsip_event *e); /** * This callback is called when the invite usage module has created * a new dialog and invite because of forked outgoing request. * * This callback is mandatory. * * @param inv The new invite session. * @param e The event which has caused the dialog to fork. * The type of this event can be either * PJSIP_EVENT_RX_MSG or PJSIP_EVENT_RX_200_MSG. */ void (*on_new_session)(pjsip_inv_session *inv, pjsip_event *e); /** * This callback is called whenever any transactions within the session * has changed their state. Application MAY implement this callback, * e.g. to monitor the progress of an outgoing request, or to send * response to unhandled incoming request (such as INFO). * * This callback is optional. * * @param inv The invite session. * @param tsx The transaction, which state has changed. * @param e The event which has caused the transation state's * to change. */ void (*on_tsx_state_changed)(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e); /** * This callback is called when the invite session has received * new offer from peer. Application can inspect the remote offer * in "offer", and set the SDP answer with #pjsip_inv_set_sdp_answer(). * When the application sends a SIP message to send the answer, * this SDP answer will be negotiated with the offer, and the result * will be sent with the SIP message. * * @param inv The invite session. * @param offer Remote offer. */ void (*on_rx_offer)(pjsip_inv_session *inv, const pjmedia_sdp_session *offer); /** * This callback is optional, and is called when the invite session has * received a re-INVITE from the peer. It will be called after * on_rx_offer() callback and works only for re-INVITEs. It allows more * fine-grained control over the response to a re-INVITE, e.g. sending * a provisional response first. Application can return PJ_SUCCESS and * send a reply using the function #pjsip_inv_initial_answer() or * #pjsip_inv_answer(), as with the initial INVITE. If application * returns non-PJ_SUCCESS, it needs to set the SDP answer with * #pjsip_inv_set_sdp_answer() and the re-INVITE will be answered * automatically. * * Remarks: Application may need to monitor on_tsx_state_changed() * callback to check whether the re-INVITE is already answered * automatically with 487 due to being cancelled. * * @param inv The invite session. * @param offer Remote offer. * @param rdata The received re-INVITE request. * * @return - PJ_SUCCESS: application will answer the re-INVITE * manually * - non-PJ_SUCCESS: answer the re-INVITE automatically * using the SDP set via #pjsip_inv_set_sdp_answer() */ pj_status_t (*on_rx_reinvite)(pjsip_inv_session *inv, const pjmedia_sdp_session *offer, pjsip_rx_data *rdata); /** * This callback is optional, and it is used to ask the application * to create a fresh offer, when the invite session has received * re-INVITE without offer. This offer then will be sent in the * 200/OK response to the re-INVITE request. * * If application doesn't implement this callback, the invite session * will send the currently active SDP as the offer. * * @param inv The invite session. * @param p_offer Pointer to receive the SDP offer created by * application. */ void (*on_create_offer)(pjsip_inv_session *inv, pjmedia_sdp_session **p_offer); /** * This callback is called after SDP offer/answer session has completed. * The status argument specifies the status of the offer/answer, * as returned by pjmedia_sdp_neg_negotiate(). * * This callback is optional (from the point of view of the framework), * but all useful applications normally need to implement this callback. * * @param inv The invite session. * @param status The negotiation status. */ void (*on_media_update)(pjsip_inv_session *inv_ses, pj_status_t status); /** * This callback is called when the framework needs to send * ACK request after it receives incoming 2xx response for * INVITE. It allows application to manually handle the * transmission of ACK request, which is required by some 3PCC * scenarios. If this callback is not implemented, the framework * will handle the ACK transmission automatically. * * When this callback is overridden, application may delay the * sending of the ACK request (for example, when it needs to * wait for answer from the other call leg, in 3PCC scenarios). * * Application creates the ACK request * * Once it has sent the ACK request, the framework will keep * this ACK request in the cache. Subsequent receipt of 2xx response * will not cause this callback to be called, and instead automatic * retransmission of this ACK request from the cache will be done * by the framework. * * This callback is optional. */ void (*on_send_ack)(pjsip_inv_session *inv, pjsip_rx_data *rdata); /** * This callback is called when the session is about to resend the * INVITE request to the specified target, following the previously * received redirection response. * * Application may accept the redirection to the specified target * (the default behavior if this callback is implemented), reject * this target only and make the session continue to try the next * target in the list if such target exists, stop the whole * redirection process altogether and cause the session to be * disconnected, or defer the decision to ask for user confirmation. * * This callback is optional. If this callback is not implemented, * the default behavior is to NOT follow the redirection response. * * @param inv The invite session. * @param target The current target to be tried. * @param e The event that caused this callback to be called. * This could be the receipt of 3xx response, or * 4xx/5xx response received for the INVITE sent to * subsequent targets, or NULL if this callback is * called from within #pjsip_inv_process_redirect() * context. * * @return Action to be performed for the target. Set this * parameter to one of the value below: * - PJSIP_REDIRECT_ACCEPT: immediately accept the * redirection to this target. When set, the * session will immediately resend INVITE request * to the target after this callback returns. * - PJSIP_REDIRECT_REJECT: immediately reject this * target. The session will continue retrying with * next target if present, or disconnect the call * if there is no more target to try. * - PJSIP_REDIRECT_STOP: stop the whole redirection * process and immediately disconnect the call. The * on_state_changed() callback will be called with * PJSIP_INV_STATE_DISCONNECTED state immediately * after this callback returns. * - PJSIP_REDIRECT_PENDING: set to this value if * no decision can be made immediately (for example * to request confirmation from user). Application * then MUST call #pjsip_inv_process_redirect() * to either accept or reject the redirection upon * getting user decision. */ pjsip_redirect_op (*on_redirected)(pjsip_inv_session *inv, const pjsip_uri *target, const pjsip_event *e); } pjsip_inv_callback; /** * This enumeration shows various options that can be applied to a session. * The bitmask combination of these options need to be specified when * creating a session. After the dialog is established (including early), * the options member of #pjsip_inv_session shows which capabilities are * common in both endpoints. */ enum pjsip_inv_option { /** * Indicate support for reliable provisional response extension */ PJSIP_INV_SUPPORT_100REL = 1, /** * Indicate support for session timer extension. */ PJSIP_INV_SUPPORT_TIMER = 2, /** * Indicate support for UPDATE method. This is automatically implied * when creating outgoing dialog. After the dialog is established, * the options member of #pjsip_inv_session shows whether peer supports * this method as well. */ PJSIP_INV_SUPPORT_UPDATE = 4, /** * Indicate support for ICE */ PJSIP_INV_SUPPORT_ICE = 8, /** * Require ICE support. */ PJSIP_INV_REQUIRE_ICE = 16, /** * Require reliable provisional response extension. */ PJSIP_INV_REQUIRE_100REL = 32, /** * Require session timer extension. */ PJSIP_INV_REQUIRE_TIMER = 64, /** * Session timer extension will always be used even when peer doesn't * support/want session timer. */ PJSIP_INV_ALWAYS_USE_TIMER = 128 }; /* Forward declaration of Session Timers */ struct pjsip_timer; /** * This structure describes the invite session. * * Note regarding the invite session's pools. The inv_sess used to have * only one pool, which is just a pointer to the dialog's pool. Ticket * http://trac.pjsip.org/repos/ticket/877 has found that the memory * usage will grow considerably everytime re-INVITE or UPDATE is * performed. * * Ticket #877 then created two more memory pools for the inv_sess, so * now we have three memory pools: * - pool: to be used to allocate long term data for the session * - pool_prov and pool_active: this is a flip-flop pools to be used * interchangably during re-INVITE and UPDATE. pool_prov is * "provisional" pool, used to allocate SDP offer or answer for * the re-INVITE and UPDATE. Once SDP negotiation is done, the * provisional pool will be made as the active pool, then the * existing active pool will be reset, to release the memory * back to the OS. So these pool's lifetime is synchronized to * the SDP offer-answer negotiation. * * Higher level application such as PJSUA-LIB has been modified to * make use of these flip-flop pools, i.e. by creating media objects * from the provisional pool rather than from the long term pool. * * Other applications that want to use these pools must understand * that the flip-flop pool's lifetimes are synchronized to the * SDP offer-answer negotiation. */ struct pjsip_inv_session { char obj_name[PJ_MAX_OBJ_NAME]; /**< Log identification */ pj_pool_t *pool; /**< Long term pool. */ pj_pool_t *pool_prov; /**< Provisional pool */ pj_pool_t *pool_active; /**< Active/current pool*/ pjsip_inv_state state; /**< Invite sess state. */ pj_bool_t cancelling; /**< CANCEL requested */ pj_bool_t pending_cancel; /**< Wait to send CANCEL*/ pjsip_tx_data *pending_bye; /**< BYE to send later */ pjsip_status_code cause; /**< Disconnect cause. */ pj_str_t cause_text; /**< Cause text. */ pj_bool_t notify; /**< Internal. */ unsigned cb_called; /**< Cb has been called */ pjsip_dialog *dlg; /**< Underlying dialog. */ pjsip_role_e role; /**< Invite role. */ unsigned options; /**< Options in use. */ pjmedia_sdp_neg *neg; /**< Negotiator. */ unsigned sdp_neg_flags; /**< SDP neg flags. */ pjsip_transaction *invite_tsx; /**< 1st invite tsx. */ pjsip_tx_data *invite_req; /**< Saved invite req */ pjsip_tx_data *last_answer; /**< Last INVITE resp. */ pjsip_tx_data *last_ack; /**< Last ACK request */ pj_int32_t last_ack_cseq; /**< CSeq of last ACK */ void *mod_data[PJSIP_MAX_MODULE];/**< Modules data. */ struct pjsip_timer *timer; /**< Session Timers. */ pj_bool_t following_fork; /**< Internal, following forked media? */ }; /** * This structure represents SDP information in a pjsip_rx_data. Application * retrieve this information by calling #pjsip_rdata_get_sdp_info(). This * mechanism supports multipart message body. */ typedef struct pjsip_rdata_sdp_info { /** * Pointer and length of the text body in the incoming message. If * the pointer is NULL, it means the message does not contain SDP * body. */ pj_str_t body; /** * This will contain non-zero if an invalid SDP body is found in the * message. */ pj_status_t sdp_err; /** * A parsed and validated SDP body. */ pjmedia_sdp_session *sdp; } pjsip_rdata_sdp_info; /** * Initialize the invite usage module and register it to the endpoint. * The callback argument contains pointer to functions to be called on * occurences of events in invite sessions. * * @param endpt The endpoint instance. * @param cb Callback structure. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_inv_usage_init(pjsip_endpoint *endpt, const pjsip_inv_callback *cb); /** * Get the INVITE usage module instance. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pjsip_module*) pjsip_inv_usage_instance(void); /** * Dump user agent contents (e.g. all dialogs). */ PJ_DECL(void) pjsip_inv_usage_dump(void); /** * Create UAC invite session for the specified dialog in dlg. * * @param dlg The dialog which will be used by this invite session. * @param local_sdp If application has determined its media capability, * it can specify the SDP here. Otherwise it can leave * this to NULL, to let remote UAS specifies an offer. * @param options The options argument is bitmask combination of SIP * features in pjsip_inv_option enumeration. * @param p_inv On successful return, the invite session will be put * in this argument. * * @return The function will return PJ_SUCCESS if it can create * the session. Otherwise the appropriate error status * will be returned on failure. */ PJ_DECL(pj_status_t) pjsip_inv_create_uac(pjsip_dialog *dlg, const pjmedia_sdp_session *local_sdp, unsigned options, pjsip_inv_session **p_inv); /** * Application SHOULD call this function upon receiving the initial INVITE * request in rdata before creating the invite session (or even dialog), * to verify that the invite session can handle the INVITE request. * This function verifies that local endpoint is capable to handle required * SIP extensions in the request (i.e. Require header) and also the media, * if media description is present in the request. * * @param rdata The incoming INVITE request. * * @param options Upon calling this function, the options argument * MUST contain the desired SIP extensions to be * applied to the session. Upon return, this argument * will contain the SIP extension that will be applied * to the session, after considering the Supported, * Require, and Allow headers in the request. * * @param sdp If local media capability has been determined, * and if application wishes to verify that it can * handle the media offer in the incoming INVITE * request, it SHOULD specify its local media capability * in this argument. * If it is not specified, media verification will not * be performed by this function. * * @param dlg If tdata is not NULL, application needs to specify * how to create the response. Either dlg or endpt * argument MUST be specified, with dlg argument takes * precedence when both are specified. * * If a dialog has been created prior to calling this * function, then it MUST be specified in dlg argument. * Otherwise application MUST specify the endpt argument * (this is useful e.g. when application wants to send * the response statelessly). * * @param endpt If tdata is not NULL, application needs to specify * how to create the response. Either dlg or endpt * argument MUST be specified, with dlg argument takes * precedence when both are specified. * * @param tdata If this argument is not NULL, this function will * create the appropriate non-2xx final response message * when the verification fails. * * @return If everything has been negotiated successfully, * the function will return PJ_SUCCESS. Otherwise it * will return the reason of the failure as the return * code. * * This function is capable to create the appropriate * response message when the verification has failed. * If tdata is specified, then a non-2xx final response * will be created and put in this argument upon return, * when the verification has failed. * * If a dialog has been created prior to calling this * function, then it MUST be specified in dlg argument. * Otherwise application MUST specify the endpt argument * (this is useful e.g. when application wants to send * the response statelessly). * * @see pjsip_inv_verify_request2() */ PJ_DECL(pj_status_t) pjsip_inv_verify_request( pjsip_rx_data *rdata, unsigned *options, const pjmedia_sdp_session *sdp, pjsip_dialog *dlg, pjsip_endpoint *endpt, pjsip_tx_data **tdata); /** * Variant of #pjsip_inv_verify_request() which allows application to specify * the parsed SDP in the \a offer argument. This is useful to avoid having to * re-parse the SDP in the incoming request. * * @see pjsip_inv_verify_request() */ PJ_DECL(pj_status_t) pjsip_inv_verify_request2( pjsip_rx_data *rdata, unsigned *options, const pjmedia_sdp_session *offer, const pjmedia_sdp_session *answer, pjsip_dialog *dlg, pjsip_endpoint *endpt, pjsip_tx_data **tdata); /** * Variant of #pjsip_inv_verify_request() which allows application not to * specify the rdata (i.e. pass NULL as the rdata parameter) and specify * the parsed SDP in the \a offer argument and a temporary pool in the * \a tmp_pool argument. * This is useful if application no longer has access to the rdata. * * @see pjsip_inv_verify_request() */ PJ_DECL(pj_status_t) pjsip_inv_verify_request3( pjsip_rx_data *rdata, pj_pool_t *tmp_pool, unsigned *options, const pjmedia_sdp_session *offer, const pjmedia_sdp_session *answer, pjsip_dialog *dlg, pjsip_endpoint *endpt, pjsip_tx_data **tdata); /** * Create UAS invite session for the specified dialog in dlg. Application * SHOULD call the verification function before calling this function, * to ensure that it can create the session successfully. * * @param dlg The dialog to be used. * @param rdata Application MUST specify the received INVITE request * in rdata. The invite session needs to inspect the * received request to see if the request contains * features that it supports. * @param local_sdp If application has determined its media capability, * it can specify this capability in this argument. * If SDP is received in the initial INVITE, the UAS * capability specified in this argument doesn't have to * match the received offer; the SDP negotiator is able * to rearrange the media lines in the answer so that it * matches the offer. * @param options The options argument is bitmask combination of SIP * features in pjsip_inv_option enumeration. * @param p_inv Pointer to receive the newly created invite session. * * @return On successful, the invite session will be put in * p_inv argument and the function will return PJ_SUCCESS. * Otherwise the appropriate error status will be returned * on failure. */ PJ_DECL(pj_status_t) pjsip_inv_create_uas(pjsip_dialog *dlg, pjsip_rx_data *rdata, const pjmedia_sdp_session *local_sdp, unsigned options, pjsip_inv_session **p_inv); /** * Forcefully terminate and destroy INVITE session, regardless of * the state of the session. Note that this function should only be used * when there is failure in the INVITE session creation. After the * invite session has been created and initialized, normally application * SHOULD use #pjsip_inv_end_session() to end the INVITE session instead. * * Note also that this function may terminate the underlying dialog, if * there are no other sessions in the dialog. * * @param inv The invite session. * @param st_code Status code for the reason of the termination. * @param notify If set to non-zero, then on_state_changed() * callback will be called. * * @return PJ_SUCCESS if the INVITE session has been * terminated. */ PJ_DECL(pj_status_t) pjsip_inv_terminate( pjsip_inv_session *inv, int st_code, pj_bool_t notify ); /** * Restart UAC session and prepare the session for a new initial INVITE. * This function can be called for example when the application wants to * follow redirection response with a new call reusing this session so * that the new call will have the same Call-ID and From headers. After * the session is restarted, application may create and send a new INVITE * request. * * @param inv The invite session. * @param new_offer Should be set to PJ_TRUE since the application will * restart the session. * * @return PJ_SUCCESS on successful operation. */ PJ_DECL(pj_status_t) pjsip_inv_uac_restart(pjsip_inv_session *inv, pj_bool_t new_offer); /** * Accept or reject redirection response. Application MUST call this function * after it signaled PJSIP_REDIRECT_PENDING in the \a on_redirected() * callback, to notify the invite session whether to accept or reject the * redirection to the current target. Application can use the combination of * PJSIP_REDIRECT_PENDING command in \a on_redirected() callback and this * function to ask for user permission before redirecting the call. * * Note that if the application chooses to reject or stop redirection (by * using PJSIP_REDIRECT_REJECT or PJSIP_REDIRECT_STOP respectively), the * session disconnection callback will be called before this function returns. * And if the application rejects the target, the \a on_redirected() callback * may also be called before this function returns if there is another target * to try. * * @param inv The invite session. * @param cmd Redirection operation. The semantic of this argument * is similar to the description in the \a on_redirected() * callback, except that the PJSIP_REDIRECT_PENDING is * not accepted here. * @param e Should be set to NULL. * * @return PJ_SUCCESS on successful operation. */ PJ_DECL(pj_status_t) pjsip_inv_process_redirect(pjsip_inv_session *inv, pjsip_redirect_op cmd, pjsip_event *e); /** * Create the initial INVITE request for this session. This function can only * be called for UAC session. If local media capability is specified when * the invite session was created, then this function will put an SDP offer * in the outgoing INVITE request. Otherwise the outgoing request will not * contain SDP body. * * @param inv The UAC invite session. * @param p_tdata The initial INVITE request will be put in this * argument if it can be created successfully. * * @return PJ_SUCCESS if the INVITE request can be created. */ PJ_DECL(pj_status_t) pjsip_inv_invite( pjsip_inv_session *inv, pjsip_tx_data **p_tdata ); /** * Create the initial response message for the incoming INVITE request in * rdata with status code st_code and optional status text st_text. Use * #pjsip_inv_answer() to create subsequent response message. */ PJ_DECL(pj_status_t) pjsip_inv_initial_answer( pjsip_inv_session *inv, pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, const pjmedia_sdp_session *sdp, pjsip_tx_data **p_tdata); /** * Create a response message to an INVITE request. * * @param inv The UAS invite session. * @param st_code The st_code contains the status code to be sent, * which may be a provisional or final response. * @param st_text If custom status text is desired, application can * specify the text in st_text; otherwise if this * argument is NULL, default status text will be used. * @param local_sdp If application has specified its media capability * during creation of UAS invite session, the local_sdp * argument MUST be NULL. This is because application * can not perform more than one SDP offer/answer session * in a single INVITE transaction. * If application has not specified its media capability * during creation of UAS invite session, it MAY or MUST * specify its capability in local_sdp argument, * depending whether st_code indicates a 2xx final * response. * @param p_tdata Pointer to receive the response message created by * this function. * * @return PJ_SUCCESS if response message was created * successfully. */ PJ_DECL(pj_status_t) pjsip_inv_answer( pjsip_inv_session *inv, int st_code, const pj_str_t *st_text, const pjmedia_sdp_session *local_sdp, pjsip_tx_data **p_tdata ); /** * Set local offer or answer depending on negotiator state (it may also * create a negotiator if it doesn't exist yet). * * @param inv The invite session. * @param sdp The SDP description which will be set as * an offer/answer to remote. * * @return PJ_SUCCESS if local offer/answer can be accepted by * SDP negotiator. */ PJ_DECL(pj_status_t) pjsip_inv_set_local_sdp(pjsip_inv_session *inv, const pjmedia_sdp_session *sdp ); /** * Set local answer to respond to remote SDP offer, to be carried by * subsequent response (or request). * * @param inv The invite session. * @param sdp The SDP description which will be set as answer * to remote. * * @return PJ_SUCCESS if local answer can be accepted by * SDP negotiator. */ PJ_DECL(pj_status_t) pjsip_inv_set_sdp_answer(pjsip_inv_session *inv, const pjmedia_sdp_session *sdp ); /** * Create a SIP message to initiate invite session termination. Depending on * the state of the session, this function may return CANCEL request, * a non-2xx final response, a BYE request, or even no request. * * For UAS, if the session has not answered the incoming INVITE, this function * creates the non-2xx final response with the specified status code in * \a st_code and optional status text in \a st_text. * * For UAC, if the original INVITE has not been answered with a final * response, the behavior depends on whether provisional response has been * received. If provisional response has been received, this function will * create CANCEL request. If no provisional response has been received, the * function will not create CANCEL request (the function will return * PJ_SUCCESS but the \a p_tdata will contain NULL) because we cannot send * CANCEL before receiving provisional response. If then a provisional * response is received, the invite session will send CANCEL automatically. * * For both UAC and UAS, if the INVITE session has been answered with final * response, a BYE request will be created. * * @param inv The invite session. * @param st_code Status code to be used for terminating the session. * @param st_text Optional status text. * @param p_tdata Pointer to receive the message to be created. Note * that it's possible to receive NULL here while the * function returns PJ_SUCCESS, see the description. * * @return PJ_SUCCESS if termination is initiated. */ PJ_DECL(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv, int st_code, const pj_str_t *st_text, pjsip_tx_data **p_tdata ); /** * Create a CANCEL request for an ongoing re-INVITE transaction. If no * provisional response has been received, the function will not create * CANCEL request (the function will return PJ_SUCCESS but the \a p_tdata * will contain NULL) because we cannot send CANCEL before receiving * provisional response. If then a provisional response is received, * the invite session will send CANCEL automatically. * * @param inv The invite session. * @param p_tdata Pointer to receive the message to be created. Note * that it's possible to receive NULL here while the * function returns PJ_SUCCESS, see the description. * * @return PJ_SUCCESS if termination is initiated. */ PJ_DECL(pj_status_t) pjsip_inv_cancel_reinvite( pjsip_inv_session *inv, pjsip_tx_data **p_tdata ); /** * Create a re-INVITE request. * * @param inv The invite session. * @param new_contact If application wants to update its local contact and * inform peer to perform target refresh with a new * contact, it can specify the new contact in this * argument; otherwise this argument must be NULL. * @param new_offer Application MAY initiate a new SDP offer/answer * session in the request when there is no pending * answer to be sent or received. It can detect this * condition by observing the state of the SDP * negotiator of the invite session. If new offer * should be sent to remote, the offer must be specified * in this argument, otherwise it must be NULL. * @param p_tdata Pointer to receive the re-INVITE request message to * be created. * * @return PJ_SUCCESS if a re-INVITE request with the specified * characteristics (e.g. to contain new offer) can be * created. */ PJ_DECL(pj_status_t) pjsip_inv_reinvite(pjsip_inv_session *inv, const pj_str_t *new_contact, const pjmedia_sdp_session *new_offer, pjsip_tx_data **p_tdata ); /** * Create an UPDATE request to initiate new SDP offer. * * @param inv The invite session. * @param new_contact If application wants to update its local contact * and inform peer to perform target refresh with a new * contact, it can specify the new contact in this * argument; otherwise this argument must be NULL. * @param offer Offer to be sent to remote. This argument is * mandatory. * @param p_tdata Pointer to receive the UPDATE request message to * be created. * * @return PJ_SUCCESS if a UPDATE request with the specified * characteristics (e.g. to contain new offer) can be * created. */ PJ_DECL(pj_status_t) pjsip_inv_update ( pjsip_inv_session *inv, const pj_str_t *new_contact, const pjmedia_sdp_session *offer, pjsip_tx_data **p_tdata ); /** * Create an ACK request. Normally ACK request transmission is handled * by the framework. Application only needs to use this function if it * handles the ACK transmission manually, by overriding \a on_send_ack() * callback in #pjsip_inv_callback. * * Note that if the invite session has a pending offer to be answered * (for example when the last 2xx response to INVITE contains an offer), * application MUST have set the SDP answer with #pjsip_create_sdp_body() * prior to creating the ACK request. In this case, the ACK request * will be added with SDP message body. * * @param inv The invite session. * @param cseq Mandatory argument to specify the CSeq of the * ACK request. This value MUST match the value * of the INVITE transaction to be acknowledged. * @param p_tdata Pointer to receive the ACK request message to * be created. * * @return PJ_SUCCESS if ACK request has been created. */ PJ_DECL(pj_status_t) pjsip_inv_create_ack(pjsip_inv_session *inv, int cseq, pjsip_tx_data **p_tdata); /** * Send request or response message in tdata. * * @param inv The invite session. * @param tdata The message to be sent. * * @return PJ_SUCCESS if transaction can be initiated * successfully to send this message. Note that the * actual final state of the transaction itself will * be reported later, in on_tsx_state_changed() * callback. */ PJ_DECL(pj_status_t) pjsip_inv_send_msg(pjsip_inv_session *inv, pjsip_tx_data *tdata); /** * Get the invite session for the dialog, if any. * * @param dlg The dialog which invite session is being queried. * * @return The invite session instance which has been * associated with this dialog, or NULL. */ PJ_DECL(pjsip_inv_session*) pjsip_dlg_get_inv_session(pjsip_dialog *dlg); /** * Get the invite session instance associated with transaction tsx, if any. * * @param tsx The transaction, which invite session is being * queried. * * @return The invite session instance which has been * associated with this transaction, or NULL. */ PJ_DECL(pjsip_inv_session*) pjsip_tsx_get_inv_session(pjsip_transaction *tsx); /** * Get state names for INVITE session state. * * @param state The invite state. * * @return String describing the state. */ PJ_DECL(const char *) pjsip_inv_state_name(pjsip_inv_state state); /** * This is a utility function to create SIP body for SDP content. * * @param pool Pool to allocate memory. * @param sdp SDP session to be put in the SIP message body. * @param p_body Pointer to receive SIP message body containing * the SDP session. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_create_sdp_body(pj_pool_t *pool, pjmedia_sdp_session *sdp, pjsip_msg_body **p_body); /** * Retrieve SDP information from an incoming message. Application should * prefer to use this function rather than parsing the SDP manually since * this function supports multipart message body. * * This function will only parse the SDP once, the first time it is called * on the same message. Subsequent call on the same message will just pick * up the already parsed SDP from the message. * * @param rdata The incoming message. * * @return The SDP info. */ PJ_DECL(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata); PJ_END_DECL /** * @} */ #endif /* __SIP_INVITE_SESSION_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-ua/sip_regc.h ================================================ /* $Id: sip_regc.h 4173 2012-06-20 10:39:05Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_SIP_REG_H__ #define __PJSIP_SIP_REG_H__ /** * @file sip_regc.h * @brief SIP Registration Client */ #include #include #include /** * @defgroup PJSUA_REGC Client Registration * @ingroup PJSIP_HIGH_UA * @brief High Layer API for performing client registration. * @{ * * This provides API for performing client registration. Application must * link with pjsip-ua static library to use this API. */ PJ_BEGIN_DECL /** Typedef for client registration data. */ typedef struct pjsip_regc pjsip_regc; /** Maximum contacts in registration. */ #define PJSIP_REGC_MAX_CONTACT 10 /** Expiration not specified. */ #define PJSIP_REGC_EXPIRATION_NOT_SPECIFIED ((pj_uint32_t)0xFFFFFFFFUL) /** Buffer to hold all contacts. */ #define PJSIP_REGC_CONTACT_BUF_SIZE 512 /** Structure to hold parameters when calling application's callback. * The application's callback is called when the client registration process * has finished. */ struct pjsip_regc_cbparam { pjsip_regc *regc; /**< Client registration structure. */ void *token; /**< Arbitrary token set by application */ /** Error status. If this value is non-PJ_SUCCESS, some error has occured. * Note that even when this contains PJ_SUCCESS the registration might * have failed; in this case the \a code field will contain non * successful (non-2xx status class) code */ pj_status_t status; int code; /**< SIP status code received. */ pj_str_t reason; /**< SIP reason phrase received. */ pjsip_rx_data *rdata; /**< The complete received response. */ int expiration;/**< Next expiration interval. */ int contact_cnt;/**". If no "<" and ">" are * present, all parameters after the URI are header * parameters, not URI parameters. The display name * can be tokens, or a quoted string, if a larger * character set is desired. * @param expires Default expiration interval (in seconds) to be applied for * contact URL that doesn't have expiration settings. If the * value PJSIP_REGC_EXPIRATION_NOT_SPECIFIED is given, then * no default expiration will be applied. * @return zero on success. */ PJ_DECL(pj_status_t) pjsip_regc_init(pjsip_regc *regc, const pj_str_t *srv_url, const pj_str_t *from_url, const pj_str_t *to_url, int ccnt, const pj_str_t contact[], pj_uint32_t expires); /** * Set callback to be called when the registration received a final response. * This callback is different with the one specified during creation via * #pjsip_regc_create(). This callback will be called for any final response * (including 401/407/423) and before any subsequent requests are sent. * In case of unregistration, this callback will not be called. * * @param regc The client registration structure. * @param tsx_cb Pointer to callback function to receive registration status. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_regc_set_reg_tsx_cb(pjsip_regc *regc, pjsip_regc_tsx_cb *tsx_cb); /** * Set the "sent-by" field of the Via header for outgoing requests. * * @param regc The client registration structure. * @param via_addr Set via_addr to use for the Via header or NULL to use * the transport's published name. * @param via_tp via_addr will only be used if we are using via_tp * transport. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_regc_set_via_sent_by(pjsip_regc *regc, pjsip_host_port *via_addr, pjsip_transport *via_tp); /** * Set the number of seconds to refresh the client registration before * the registration expires. * * @param regc The registration structure. * @param delay The number of seconds to refresh the client * registration before the registration expires. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_regc_set_delay_before_refresh( pjsip_regc *regc, pj_uint32_t delay ); /** * Set authentication credentials to use by this registration. * * @param regc The registration structure. * @param count Number of credentials in the array. * @param cred Array of credentials. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc, int count, const pjsip_cred_info cred[] ); /** * Set authentication preference. * * @param regc The registration structure. * @param pref Authentication preference. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc, const pjsip_auth_clt_pref *pref); /** * Set route set to be used for outgoing requests. * * @param regc The client registration structure. * @param route_set List containing Route headers. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_regc_set_route_set(pjsip_regc *regc, const pjsip_route_hdr*route_set); /** * Lock/bind client registration to a specific transport/listener. * This is optional, as normally transport will be selected automatically * based on the destination of requests upon resolver completion. * When the client registration is explicitly bound to the specific * transport/listener, all UAC transactions originated by the client * registration will use the specified transport/listener when sending * outgoing requests. * * Note that this doesn't affect the Contact header set for this client * registration. Application must manually update the Contact header if * necessary, to adjust the address according to the transport being * selected. * * @param regc The client registration instance. * @param sel Transport selector containing the specification of * transport or listener to be used by this session * to send requests. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_regc_set_transport(pjsip_regc *regc, const pjsip_tpselector *sel); /** * Release the reference to current transport being used by the regc, if any. * The regc keeps the reference to the last transport being used in order * to prevent it from being destroyed. In some situation however, such as * when the transport is disconnected, it is necessary to instruct the * regc to release this reference so that the transport can be destroyed. * See https://trac.pjsip.org/repos/ticket/1481 for background info. * * @param regc The client registration instance. * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsip_regc_release_transport(pjsip_regc *regc); /** * Add headers to be added to outgoing REGISTER requests. * * @param regc The client registration structure. * @param hdr_list List containing SIP headers to be added for all outgoing * REGISTER requests. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_regc_add_headers(pjsip_regc *regc, const pjsip_hdr *hdr_list); /** * Create REGISTER request for the specified client registration structure. * * After successfull registration, application can inspect the contacts in * the client registration structure to list what contacts are associaciated * with the address of record being targeted in the registration. * * @param regc The client registration structure. * @param autoreg If non zero, the library will automatically refresh the * next registration until application unregister. * @param p_tdata Pointer to receive the REGISTER request. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg, pjsip_tx_data **p_tdata); /** * Create REGISTER request to unregister the contacts that were previously * registered by this client registration. * * @param regc The client registration structure. * @param p_tdata Pointer to receive the REGISTER request. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc, pjsip_tx_data **p_tdata); /** * Create REGISTER request to unregister all contacts from server records. * Note that this will unregister all registered contact for the AOR * including contacts registered by other user agents. To only unregister * contact registered by this client registration instance, use * #pjsip_regc_unregister() instead. * * @param regc The client registration structure. * @param p_tdata Pointer to receive the REGISTER request. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_regc_unregister_all(pjsip_regc *regc, pjsip_tx_data **p_tdata); /** * Update Contact details in the client registration structure. For each * contact, if the contact is not found in existing contact, it will be * added to the Contact list. If it matches existing contact, nothing * will be added. This function will also mark existing contacts which * are not specified in the new contact list as to be removed, by adding * "expires=0" parameter to these contacts. * * Once the contact list has been updated, application must update the * registration by creating a new REGISTER request and send it to the * registrar. This request will contain both old and new contacts; the * old contacts will have it's expires parameter set to zero to instruct * the registrar to remove the bindings. * * @param regc The client registration structure. * @param ccnt Number of contacts. * @param contact Array of contacts, each contact item must be formatted * as described in RFC 3261 Section 20.10: * When the header field value contains a display * name, the URI including all URI parameters is * enclosed in "<" and ">". If no "<" and ">" are * present, all parameters after the URI are header * parameters, not URI parameters. The display name * can be tokens, or a quoted string, if a larger * character set is desired. * @return PJ_SUCCESS if sucessfull. */ PJ_DECL(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc, int ccnt, const pj_str_t contact[] ); /** * Update the expires value. The next REGISTER request will contain * new expires value for the registration. * * @param regc The client registration structure. * @param expires The new expires value. * @return zero on successfull. */ PJ_DECL(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc, pj_uint32_t expires ); /** * Sends outgoing REGISTER request. * The process will complete asynchronously, and application * will be notified via the callback when the process completes. * * @param regc The client registration structure. * @param tdata Transmit data. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata); PJ_END_DECL /** * @} */ #endif /* __PJSIP_REG_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-ua/sip_replaces.h ================================================ /* $Id: sip_replaces.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_REPLACES_H__ #define __PJSIP_REPLACES_H__ /** * @file sip_replaces.h * @brief SIP Replaces support (RFC 3891 - SIP "Replaces" Header) */ #include /** * @defgroup PJSIP_REPLACES SIP Replaces support (RFC 3891 - "Replaces" Header) * @ingroup PJSIP_HIGH_UA * @brief SIP Replaces support (RFC 3891 - "Replaces" Header) * @{ * * This module implements support for Replaces header in PJSIP. The Replaces * specification is written in RFC 3891 - The Session Initiation Protocol (SIP) * "Replaces" Header, and can be used to enable a variety of features, * for example: "Attended Transfer" and "Call Pickup". * * * * \section PJSIP_REPLACES_USING_SEC Using PJSIP Replaces Support * * \subsection PJSIP_REPLACES_INIT_SUBSEC Initialization * * Application needs to call #pjsip_replaces_init_module() during application * initialization stage to register "replaces" support in PJSIP. * * * * \subsection PJSIP_REPLACES_UAC_SUBSEC UAC Behavior: Sending a Replaces Header * * A User Agent that wishes to replace a single existing early or * confirmed dialog with a new dialog of its own, MAY send the target * User Agent an INVITE request containing a Replaces header field. The * User Agent Client (UAC) places the Call-ID, to-tag, and from-tag * information for the target dialog in a single Replaces header field * and sends the new INVITE to the target. * * To initiate outgoing INVITE request with Replaces header, application * would create the INVITE request with #pjsip_inv_invite(), then adds * #pjsip_replaces_hdr instance into the request, filling up the Call-ID, * To-tag, and From-tag properties of the header with the identification * of the dialog to be replaced. Application may also optionally * set the \a early_only property of the header to indicate that it only * wants to replace early dialog. * * Note that when the outgoing INVITE request (with Replaces) is initiated * from an incoming REFER request (as in Attended Call Transfer case), * this process should be done rather more automatically by PJSIP. Upon * receiving incoming incoming REFER request, normally these processes * will be performed: * - Application finds \a Refer-To header, * - Application creates outgoing dialog/invite session, specifying * the URI in the \a Refer-To header as the initial remote target, * - The URI in the \a Refer-To header may contain header parameters such * as \a Replaces and \a Require headers. * - The dialog keeps the header fields in the header parameters * of the URI, and the invite session would add these headers into * the outgoing INVITE request. Because of this, the outgoing * INVITE request will contain the \a Replaces and \a Require headers. * * * For more information, please see the implementation of * #pjsua_call_xfer_replaces() in \ref PJSUA_LIB source code. * * * \subsection PJSIP_REPLACES_UAS_SUBSEC UAS Behavior: Receiving a Replaces Header * * The Replaces header contains information used to match an existing * SIP dialog (call-id, to-tag, and from-tag). Upon receiving an INVITE * with a Replaces header, the User Agent (UA) attempts to match this * information with a confirmed or early dialog. * * In PJSIP, if application wants to process the Replaces header in the * incoming INVITE request, it should call #pjsip_replaces_verify_request() * before creating the INVITE session. The #pjsip_replaces_verify_request() * function checks and verifies the request to see if Replaces request * can be processed. To be more specific, it performs the following * verification: * - checks that Replaces header is present. If not, the function will * return PJ_SUCCESS without doing anything. * - checks that no duplicate Replaces headers are present, or otherwise * it will return 400 "Bad Request" response. * - checks for matching dialog and verifies that the invite session has * the correct state, and may return 481 "Call/Transaction Does Not Exist", * 603 "Declined", or 486 "Busy Here" according to the processing rules * specified in RFC 3891. * - if matching dialog with correct state is found, it will give PJ_SUCCESS * status and return the matching dialog back to the application. * * The following pseudocode illustrates how application can process the * incoming INVITE if it wants to support Replaces extension: * \code // Incoming INVITE request handler pj_bool_t on_rx_invite(pjsip_rx_data *rdata) { pjsip_dialog *dlg, *replaced_dlg; pjsip_inv_session *inv; pjsip_tx_data *response; pj_status_t status; // Check whether Replaces header is present in the request and process accordingly. // status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE, &response); if (status != PJ_SUCCESS) { // Something wrong with Replaces request. // if (response) { pjsip_endpt_send_response(endpt, rdata, response, NULL, NULL); } else { // Respond with 500 (Internal Server Error) pjsip_endpt_respond_stateless(endpt, rdata, 500, NULL, NULL, NULL); } } // Create UAS Invite session as usual. // status = pjsip_dlg_create_uas_and_inc_lock(.., rdata, .., &dlg); .. status = pjsip_inv_create_uas(dlg, .., &inv); // Send initial 100 "Trying" to the INVITE request // status = pjsip_inv_initial_answer(inv, rdata, 100, ..., &response); if (status == PJ_SUCCESS) pjsip_inv_send_msg(inv, response); // This is where processing is different between normal call // (without Replaces) and call with Replaces. // if (replaced_dlg) { pjsip_inv_session *replaced_inv; // Always answer the new INVITE with 200, regardless whether // the replaced call is in early or confirmed state. // status = pjsip_inv_answer(inv, 200, NULL, NULL, &response); if (status == PJ_SUCCESS) pjsip_inv_send_msg(inv, response); // Get the INVITE session associated with the replaced dialog. // replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg); // Disconnect the "replaced" INVITE session. // status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL, &tdata); if (status == PJ_SUCCESS && tdata) status = pjsip_inv_send_msg(replaced_inv, tdata); // It's up to application to associate the new INVITE session // with the old (now terminated) session. For example, application // may assign the same User Interface object for the new INVITE // session. } else { // Process normal INVITE without Replaces. ... } } \endcode * * * For a complete sample implementation, please see \a pjsua_call_on_incoming() * function of \ref PJSUA_LIB in \a pjsua_call.c file. * * * \section PJSIP_REPLACES_REFERENCE References * * References: * - RFC 3891: The Session * Initiation Protocol (SIP) "Replaces" Header * - \ref PJSUA_XFER */ PJ_BEGIN_DECL /** * Declaration of SIP Replaces header (RFC 3891). */ typedef struct pjsip_replaces_hdr { /** Standard header field. */ PJSIP_DECL_HDR_MEMBER(struct pjsip_replaces_hdr); /** Call-Id */ pj_str_t call_id; /** to-tag */ pj_str_t to_tag; /** from-tag */ pj_str_t from_tag; /** early-only? */ pj_bool_t early_only; /** Other parameters */ pjsip_param other_param; } pjsip_replaces_hdr; /** * Initialize Replaces support in PJSIP. This would, among other things, * register the header parser for Replaces header. * * @param endpt The endpoint instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_replaces_init_module(pjsip_endpoint *endpt); /** * Create Replaces header. * * @param pool Pool to allocate the header instance from. * * @return An empty Replaces header instance. */ PJ_DECL(pjsip_replaces_hdr*) pjsip_replaces_hdr_create(pj_pool_t *pool); /** * Verify that incoming request with Replaces header can be processed. * This function will perform all necessary checks according to RFC 3891 * Section 3 "User Agent Server Behavior: Receiving a Replaces Header". * * @param rdata The incoming request to be verified. * @param p_dlg On return, it will be filled with the matching * dialog. * @param lock_dlg Specifies whether this function should acquire lock * to the matching dialog. If yes (and should be yes!), * then application will need to release the dialog's * lock with #pjsip_dlg_dec_lock() when the function * returns PJ_SUCCESS and the \a p_dlg parameter is filled * with the dialog instance. * @param p_tdata Upon error, it will be filled with the final response * to be sent to the request sender. * * @return The function returns the following: * - If the request doesn't contain Replaces header, the * function returns PJ_SUCCESS and \a p_dlg parameter * will be set to NULL. * - If the request contains Replaces header and a valid, * matching dialog is found, the function returns * PJ_SUCCESS and \a p_dlg parameter will be set to the * matching dialog instance. * - Upon error condition (as described by RFC 3891), the * function returns non-PJ_SUCCESS, and \a p_tdata * parameter SHOULD be set with a final response message * to be sent to the sender of the request. */ PJ_DECL(pj_status_t) pjsip_replaces_verify_request(pjsip_rx_data *rdata, pjsip_dialog **p_dlg, pj_bool_t lock_dlg, pjsip_tx_data **p_tdata); PJ_END_DECL /** * @} */ #endif /* __PJSIP_REPLACES_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-ua/sip_timer.h ================================================ /* $Id: sip_timer.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_TIMER_H__ #define __PJSIP_TIMER_H__ /** * @file sip_timer.h * @brief SIP Session Timers support (RFC 4028 - Session Timer in SIP) */ #include #include /** * @defgroup PJSIP_TIMER SIP Session Timers support (RFC 4028 - Session Timers in SIP) * @ingroup PJSIP_HIGH_UA * @brief SIP Session Timers support (RFC 4028 - Session Timers in SIP) * @{ * * \section PJSIP_TIMER_REFERENCE References * * References: * - RFC 4028: Session Timers * in the Session Initiation Protocol (SIP) */ PJ_BEGIN_DECL /** * Opaque declaration of Session Timers. */ typedef struct pjsip_timer pjsip_timer; /** * This structure describes Session Timers settings in an invite session. */ typedef struct pjsip_timer_setting { /** * Specify minimum session expiration period, in seconds. Must not be * lower than 90. Default is 90. */ unsigned min_se; /** * Specify session expiration period, in seconds. Must not be lower than * #min_se. Default is 1800. */ unsigned sess_expires; } pjsip_timer_setting; /** * SIP Session-Expires header (RFC 4028). */ typedef struct pjsip_sess_expires_hdr { /** Standard header field. */ PJSIP_DECL_HDR_MEMBER(struct pjsip_sess_expires_hdr); /** Session expiration period */ unsigned sess_expires; /** Refresher */ pj_str_t refresher; /** Other parameters */ pjsip_param other_param; } pjsip_sess_expires_hdr; /** * SIP Min-SE header (RFC 4028). */ typedef struct pjsip_min_se_hdr { /** Standard header field. */ PJSIP_DECL_HDR_MEMBER(struct pjsip_min_se_hdr); /** Minimum session expiration period */ unsigned min_se; /** Other parameters */ pjsip_param other_param; } pjsip_min_se_hdr; /** * Initialize Session Timers module. This function must be called once during * application initialization, to register this module to SIP endpoint. * * @param endpt The SIP endpoint instance. * * @return PJ_SUCCESS if module is successfully initialized. */ PJ_DECL(pj_status_t) pjsip_timer_init_module(pjsip_endpoint *endpt); /** * Initialize Session Timers setting with default values. * * @param setting Session Timers setting to be initialized. * * @return PJ_SUCCESS on successful. */ PJ_DECL(pj_status_t) pjsip_timer_setting_default(pjsip_timer_setting *setting); /** * Initialize Session Timers for an invite session. This function should be * called by application to apply Session Timers setting, otherwise invite * session will apply default setting to the Session Timers. * * @param inv The invite session. * @param setting Session Timers setting, see @pjsip_timer_setting. * If setting is NULL, default setting will be applied. * * @return PJ_SUCCESS on successful. */ PJ_DECL(pj_status_t) pjsip_timer_init_session( pjsip_inv_session *inv, const pjsip_timer_setting *setting); /** * Create Session-Expires header. * * @param pool Pool to allocate the header instance from. * * @return An empty Session-Expires header instance. */ PJ_DECL(pjsip_sess_expires_hdr*) pjsip_sess_expires_hdr_create( pj_pool_t *pool); /** * Create Min-SE header. * * @param pool Pool to allocate the header instance from. * * @return An empty Min-SE header instance. */ PJ_DECL(pjsip_min_se_hdr*) pjsip_min_se_hdr_create(pj_pool_t *pool); /** * Update outgoing request to insert Session Timers headers and also * signal Session Timers capability in Supported and/or Require headers. * * This function will be called internally by the invite session if it * detects that the session needs Session Timers support. * * @param inv The invite session. * @param tdata Outgoing INVITE or UPDATE request. * * @return PJ_SUCCESS on successful. */ PJ_DECL(pj_status_t) pjsip_timer_update_req(pjsip_inv_session *inv, pjsip_tx_data *tdata); /** * Process Session Timers headers in incoming response, this function * will only process incoming response with status code 422 (Session * Interval Too Small) or 2xx (final response). * * This function will be called internally by the invite session if it * detects that the session needs Session Timers support. * * @param inv The invite session. * @param rdata Incoming response data. * @param st_code Output buffer to store corresponding SIP status code * when function returning non-PJ_SUCCESS. * * @return PJ_SUCCESS on successful. */ PJ_DECL(pj_status_t) pjsip_timer_process_resp(pjsip_inv_session *inv, const pjsip_rx_data *rdata, pjsip_status_code *st_code); /** * Process Session Timers refresh error, this function will process * error from refresh request. The error will be handle according the * error code, i.e : BYE will be sent after error 503 (Transport Error). * * @param inv The invite session. * @param event The event that trigger the error. * * @return PJ_SUCCESS on successful. */ PJ_DECL(pj_status_t) pjsip_timer_handle_refresh_error( pjsip_inv_session *inv, pjsip_event *event); /** * Process Session Timers headers in incoming request, this function * will only process incoming INVITE and UPDATE request. * * This function will be called internally by the invite session if it * detects that the session needs Session Timers support. * * @param inv The invite session. * @param rdata Incoming INVITE or UPDATE request. * @param st_code Output buffer to store corresponding SIP status code * when function returning non-PJ_SUCCESS. * * @return PJ_SUCCESS on successful. */ PJ_DECL(pj_status_t) pjsip_timer_process_req(pjsip_inv_session *inv, const pjsip_rx_data *rdata, pjsip_status_code *st_code); /** * Update outgoing response to insert Session Timers headers and also * signal Session Timers capability in Supported and/or Require headers. * This function will only update outgoing response with status code * 422 (Session Interval Too Small) or 2xx (final response). * * This function will be called internally by the invite session if it * detects that the session needs Session Timers support. * * @param inv The invite session. * @param tdata Outgoing 422/2xx response. * * @return PJ_SUCCESS on successful. */ PJ_DECL(pj_status_t) pjsip_timer_update_resp(pjsip_inv_session *inv, pjsip_tx_data *tdata); /** * End Session Timers in an invite session. * * This function will be called internally by the invite session if it * detects that the session needs Session Timers support. * * @param inv The invite session. * * @return PJ_SUCCESS on successful. */ PJ_DECL(pj_status_t) pjsip_timer_end_session(pjsip_inv_session *inv); PJ_END_DECL /** * @} */ #endif /* __PJSIP_TIMER_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip-ua/sip_xfer.h ================================================ /* $Id: sip_xfer.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_XFER_H__ #define __PJSIP_XFER_H__ /** * @file sip_xfer.h * @brief SIP Transfer (REFER, RFC 3515) */ #include #include /** * @defgroup PJSUA_XFER SIP REFER (RFC 3515) for Call Transfer etc. * @ingroup PJSIP_HIGH_UA * @brief SIP REFER dialog usage (call transfer, etc.) * @{ * * This describes a generic handling of SIP REFER request. The SIP REFER * request is described in RFC 3515, and commonly used to perform call * transfer functionality. Other types of SIP REFER usages are described * in draft-worley-sip-many-refers-00 draft, for example: * - Remote Dial: where UAC sends REFER to instruct REFER recipient to * initiate an INVITE session to some target. * * A REFER request can be sent inside or outside existing dialog context, * although for call transfer case, it is more common to send REFER inside * existing INVITE session context. PJSIP supports both sending REFER request * inside or outside dialog context. * * The REFER framework uses @ref PJSIP_EVENT_NOT to manage the event * subscription created by the REFER request. Because of this, application * must link with pjsip-ua AND pjsip-simple static libraries * to use REFER functionality. * * Reference: * - RFC 3515: The Session * Initiation Protocol (SIP) Refer Method * - @ref PJSIP_EVENT_NOT */ PJ_BEGIN_DECL /** Declaration for REFER method constant. */ PJ_DECL_DATA(const pjsip_method) pjsip_refer_method; /** Get REFER method constant */ PJ_DECL(const pjsip_method*) pjsip_get_refer_method(void); /** * Initialize the REFER subsystem. * This currently does very little (only register REFER as supported method). */ PJ_DECL(pj_status_t) pjsip_xfer_init_module(pjsip_endpoint *endpt); /** * Create transferer (sender of REFER request). * * @param dlg The underlying dialog to use. * @param user_cb Pointer to callbacks to receive presence subscription * events. * @param p_evsub Pointer to receive the presence subscription * session. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_xfer_create_uac( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_evsub **p_evsub ); /** * Create transferee (receiver of REFER request). * * @param dlg The underlying dialog to use. * @param user_cb Pointer to callbacks to receive presence subscription * events. * @param rdata The incoming SUBSCRIBE request that creates the event * subscription. * @param p_evsub Pointer to receive the presence subscription * session. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_xfer_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, pjsip_evsub **p_evsub ); /** * Call this function to create request to initiate REFER subscription, * to refresh subscription, or to unsubscribe. For request other than * the initial REFER request, "refer_to_uri" argument may be NULL. * * @param sub Client subscription instance. * @param refer_to_uri URI to be put to the Refer-To header. This argument * may be NULL for subsequent REFER requests. * @param p_tdata Pointer to receive the request. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_xfer_initiate( pjsip_evsub *sub, const pj_str_t *refer_to_uri, pjsip_tx_data **p_tdata); /** * Accept the incoming REFER request by sending 2xx response. * * @param sub Server subscription instance. * @param rdata The incoming subscription request message. * @param st_code Status code, which MUST be 2xx. * @param hdr_list Optional list of headers to be added in the response. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_xfer_accept( pjsip_evsub *sub, pjsip_rx_data *rdata, int st_code, const pjsip_hdr *hdr_list ); /** * For notifier, create NOTIFY request to subscriber, and set the state * of the subscription. * * @param sub The server subscription (notifier) instance. * @param state New state to set. * @param xfer_st_code The call status code to be reported with the NOTIFY * request. * @param xfer_st_text Optional call status text to be reported with the * NOTIFY request. If the value is NULL, default * status text will be used. * @param p_tdata Pointer to receive the request. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_xfer_notify( pjsip_evsub *sub, pjsip_evsub_state state, int xfer_st_code, const pj_str_t *xfer_st_text, pjsip_tx_data **p_tdata); /** * Create NOTIFY request to reflect current subscription status. Application * can only call this function after it has sent NOTIFY before. * This will also re-send the last "message/sipfrag" body that was sent * in the previous NOTIFY. * * @param sub Server subscription object. * @param p_tdata Pointer to receive request. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_xfer_current_notify( pjsip_evsub *sub, pjsip_tx_data **p_tdata ); /** * Send request message that was previously created with initiate(), notify(), * or current_notify(). Application may also send request created with other * functions, e.g. authentication. But the request MUST be either request * that creates/refresh subscription or NOTIFY request. * * * @param sub The event subscription object. * @param tdata Request message to be send. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_xfer_send_request( pjsip_evsub *sub, pjsip_tx_data *tdata); PJ_END_DECL /** * @} */ #endif /* __PJSIP_XFER_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip.h ================================================ /* $Id: pjsip.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_H__ #define __PJSIP_H__ /* Base types. */ #include #include /* Messaging and parsing. */ #include #include #include #include #include /* Core */ #include #include #include #include /* Transport layer */ #include #include #include #include #include #include /* Authentication. */ #include #include /* Transaction layer. */ #include /* UA Layer. */ #include #include #endif /* __PJSIP_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip_auth.h ================================================ /* $Id: pjsip_auth.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_AUTH_H__ #define __PJSIP_AUTH_H__ /** * @defgroup PJSIP_AUTH SIP Authorization module */ /** * @file pjsip_auth.h * @brief SIP Authorization Module. */ #include #include #include #endif /* __PJSIP_AUTH_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip_simple.h ================================================ /* $Id: pjsip_simple.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * @defgroup PJSIP_SIMPLE Event and Presence Framework */ /** * @file pjsip_simple.h * @brief SIP SIMPLE Extension */ /* * Include this header file to get all functionalities for SIMPLE extension * (SIP for Instant Messaging and Presence Leveraging Extension). */ #ifndef __PJSIP_SIMPLE_H__ #define __PJSIP_SIMPLE_H__ #include #include #include #include #include #include #include #include #endif /* __PJSIP_SIMPLE_H__ */ ================================================ FILE: deps/pjsip/pjsip/include/pjsip_ua.h ================================================ /* $Id: pjsip_ua.h 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PJSIP_UA_H__ #define __PJSIP_UA_H__ #include #include #include #include #include #include #endif /* __PJSIP_UA_H__ */ ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_auth_aka.c ================================================ /* $Id: sip_auth_aka.c 3999 2012-03-30 07:10:13Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #if PJSIP_HAS_DIGEST_AKA_AUTH #include "../../third_party/milenage/milenage.h" /* * Create MD5-AKA1 digest response. */ PJ_DEF(pj_status_t) pjsip_auth_create_aka_response( pj_pool_t *pool, const pjsip_digest_challenge*chal, const pjsip_cred_info *cred, const pj_str_t *method, pjsip_digest_credential *auth) { pj_str_t nonce_bin; int aka_version; const pj_str_t pjsip_AKAv1_MD5 = { "AKAv1-MD5", 9 }; const pj_str_t pjsip_AKAv2_MD5 = { "AKAv2-MD5", 9 }; pj_uint8_t *chal_rand, *chal_sqnxoraka, *chal_mac; pj_uint8_t k[PJSIP_AKA_KLEN]; pj_uint8_t op[PJSIP_AKA_OPLEN]; pj_uint8_t amf[PJSIP_AKA_AMFLEN]; pj_uint8_t res[PJSIP_AKA_RESLEN]; pj_uint8_t ck[PJSIP_AKA_CKLEN]; pj_uint8_t ik[PJSIP_AKA_IKLEN]; pj_uint8_t ak[PJSIP_AKA_AKLEN]; pj_uint8_t sqn[PJSIP_AKA_SQNLEN]; pj_uint8_t xmac[PJSIP_AKA_MACLEN]; pjsip_cred_info aka_cred; int i, len; pj_status_t status; /* Check the algorithm is supported. */ if (chal->algorithm.slen==0 || pj_stricmp2(&chal->algorithm, "md5") == 0) { /* * A normal MD5 authentication is requested. Fallbackt to the usual * MD5 digest creation. */ pjsip_auth_create_digest(&auth->response, &auth->nonce, &auth->nc, &auth->cnonce, &auth->qop, &auth->uri, &auth->realm, cred, method); return PJ_SUCCESS; } else if (pj_stricmp(&chal->algorithm, &pjsip_AKAv1_MD5) == 0) { /* * AKA version 1 is requested. */ aka_version = 1; } else if (pj_stricmp(&chal->algorithm, &pjsip_AKAv2_MD5) == 0) { /* * AKA version 2 is requested. */ aka_version = 2; } else { /* Unsupported algorithm */ return PJSIP_EINVALIDALGORITHM; } /* Decode nonce */ nonce_bin.slen = len = PJ_BASE64_TO_BASE256_LEN(chal->nonce.slen); nonce_bin.ptr = pj_pool_alloc(pool, nonce_bin.slen + 1); status = pj_base64_decode(&chal->nonce, (pj_uint8_t*)nonce_bin.ptr, &len); nonce_bin.slen = len; if (status != PJ_SUCCESS) return PJSIP_EAUTHINNONCE; if (nonce_bin.slen < PJSIP_AKA_RANDLEN + PJSIP_AKA_AUTNLEN) return PJSIP_EAUTHINNONCE; /* Get RAND, AUTN, and MAC */ chal_rand = (pj_uint8_t*)(nonce_bin.ptr + 0); chal_sqnxoraka = (pj_uint8_t*) (nonce_bin.ptr + PJSIP_AKA_RANDLEN); chal_mac = (pj_uint8_t*) (nonce_bin.ptr + PJSIP_AKA_RANDLEN + PJSIP_AKA_SQNLEN + PJSIP_AKA_AMFLEN); /* Copy k. op, and amf */ pj_bzero(k, sizeof(k)); pj_bzero(op, sizeof(op)); pj_bzero(amf, sizeof(amf)); if (cred->ext.aka.k.slen) pj_memcpy(k, cred->ext.aka.k.ptr, cred->ext.aka.k.slen); if (cred->ext.aka.op.slen) pj_memcpy(op, cred->ext.aka.op.ptr, cred->ext.aka.op.slen); if (cred->ext.aka.amf.slen) pj_memcpy(amf, cred->ext.aka.amf.ptr, cred->ext.aka.amf.slen); /* Given key K and random challenge RAND, compute response RES, * confidentiality key CK, integrity key IK and anonymity key AK. */ f2345(k, chal_rand, res, ck, ik, ak, op); /* Compute sequence number SQN */ for (i=0; iresponse, &chal->nonce, &auth->nc, &auth->cnonce, &auth->qop, &auth->uri, &chal->realm, &aka_cred, method); } else if (aka_version == 2) { /* * For AKAv2, password is base64 encoded [1] parameters: * PRF(RES||IK||CK,"http-digest-akav2-password") * * The pseudo-random function (PRF) is HMAC-MD5 in this case. */ pj_str_t resikck; const pj_str_t AKAv2_Passwd = { "http-digest-akav2-password", 26 }; pj_uint8_t hmac_digest[16]; char tmp_buf[48]; int hmac64_len; resikck.slen = PJSIP_AKA_RESLEN + PJSIP_AKA_IKLEN + PJSIP_AKA_CKLEN; pj_assert(resikck.slen <= PJ_ARRAY_SIZE(tmp_buf)); resikck.ptr = tmp_buf; pj_memcpy(resikck.ptr + 0, res, PJSIP_AKA_RESLEN); pj_memcpy(resikck.ptr + PJSIP_AKA_RESLEN, ik, PJSIP_AKA_IKLEN); pj_memcpy(resikck.ptr + PJSIP_AKA_RESLEN + PJSIP_AKA_IKLEN, ck, PJSIP_AKA_CKLEN); pj_hmac_md5((const pj_uint8_t*)AKAv2_Passwd.ptr, AKAv2_Passwd.slen, (const pj_uint8_t*)resikck.ptr, resikck.slen, hmac_digest); aka_cred.data.slen = hmac64_len = PJ_BASE256_TO_BASE64_LEN(PJ_ARRAY_SIZE(hmac_digest)); pj_assert(aka_cred.data.slen+1 <= PJ_ARRAY_SIZE(tmp_buf)); aka_cred.data.ptr = tmp_buf; pj_base64_encode(hmac_digest, PJ_ARRAY_SIZE(hmac_digest), aka_cred.data.ptr, &len); aka_cred.data.slen = hmac64_len; pjsip_auth_create_digest(&auth->response, &chal->nonce, &auth->nc, &auth->cnonce, &auth->qop, &auth->uri, &chal->realm, &aka_cred, method); } else { pj_assert(!"Bug!"); return PJ_EBUG; } /* Done */ return PJ_SUCCESS; } #endif /* PJSIP_HAS_DIGEST_AKA_AUTH */ ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_auth_client.c ================================================ /* $Id: sip_auth_client.c 4322 2013-01-17 10:09:09Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include /* just to get pjsip_DIGEST_STR */ #include #include #include #include #include #include #include #include #include #include #include #include /* A macro just to get rid of type mismatch between char and unsigned char */ #define MD5_APPEND(pms,buf,len) pj_md5_update(pms, (const pj_uint8_t*)buf, \ (unsigned)len) /* Logging. */ #define THIS_FILE "sip_auth_client.c" #if 0 # define AUTH_TRACE_(expr) PJ_LOG(3, expr) #else # define AUTH_TRACE_(expr) #endif #define PASSWD_MASK 0x000F #define EXT_MASK 0x00F0 static void dup_bin(pj_pool_t *pool, pj_str_t *dst, const pj_str_t *src) { dst->slen = src->slen; if (dst->slen) { dst->ptr = (char*) pj_pool_alloc(pool, src->slen); pj_memcpy(dst->ptr, src->ptr, src->slen); } else { dst->ptr = NULL; } } PJ_DEF(void) pjsip_cred_info_dup(pj_pool_t *pool, pjsip_cred_info *dst, const pjsip_cred_info *src) { pj_memcpy(dst, src, sizeof(pjsip_cred_info)); pj_strdup_with_null(pool, &dst->realm, &src->realm); pj_strdup_with_null(pool, &dst->scheme, &src->scheme); pj_strdup_with_null(pool, &dst->username, &src->username); pj_strdup_with_null(pool, &dst->data, &src->data); if ((dst->data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) { dup_bin(pool, &dst->ext.aka.k, &src->ext.aka.k); dup_bin(pool, &dst->ext.aka.op, &src->ext.aka.op); dup_bin(pool, &dst->ext.aka.amf, &src->ext.aka.amf); } } PJ_DEF(int) pjsip_cred_info_cmp(const pjsip_cred_info *cred1, const pjsip_cred_info *cred2) { int result; result = pj_strcmp(&cred1->realm, &cred2->realm); if (result) goto on_return; result = pj_strcmp(&cred1->scheme, &cred2->scheme); if (result) goto on_return; result = pj_strcmp(&cred1->username, &cred2->username); if (result) goto on_return; result = pj_strcmp(&cred1->data, &cred2->data); if (result) goto on_return; result = (cred1->data_type != cred2->data_type); if (result) goto on_return; if ((cred1->data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) { result = pj_strcmp(&cred1->ext.aka.k, &cred2->ext.aka.k); if (result) goto on_return; result = pj_strcmp(&cred1->ext.aka.op, &cred2->ext.aka.op); if (result) goto on_return; result = pj_strcmp(&cred1->ext.aka.amf, &cred2->ext.aka.amf); if (result) goto on_return; } on_return: return result; } PJ_DEF(void) pjsip_auth_clt_pref_dup( pj_pool_t *pool, pjsip_auth_clt_pref *dst, const pjsip_auth_clt_pref *src) { pj_memcpy(dst, src, sizeof(pjsip_auth_clt_pref)); pj_strdup_with_null(pool, &dst->algorithm, &src->algorithm); } /* Transform digest to string. * output must be at least PJSIP_MD5STRLEN+1 bytes. * * NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED! */ static void digest2str(const unsigned char digest[], char *output) { int i; for (i = 0; i<16; ++i) { pj_val_to_hex_digit(digest[i], output); output += 2; } } /* * Create response digest based on the parameters and store the * digest ASCII in 'result'. */ PJ_DEF(void) pjsip_auth_create_digest( pj_str_t *result, const pj_str_t *nonce, const pj_str_t *nc, const pj_str_t *cnonce, const pj_str_t *qop, const pj_str_t *uri, const pj_str_t *realm, const pjsip_cred_info *cred_info, const pj_str_t *method) { char ha1[PJSIP_MD5STRLEN]; char ha2[PJSIP_MD5STRLEN]; unsigned char digest[16]; pj_md5_context pms; pj_assert(result->slen >= PJSIP_MD5STRLEN); AUTH_TRACE_((THIS_FILE, "Begin creating digest")); if ((cred_info->data_type & PASSWD_MASK) == PJSIP_CRED_DATA_PLAIN_PASSWD) { /*** *** ha1 = MD5(username ":" realm ":" password) ***/ pj_md5_init(&pms); MD5_APPEND( &pms, cred_info->username.ptr, cred_info->username.slen); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, realm->ptr, realm->slen); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, cred_info->data.ptr, cred_info->data.slen); pj_md5_final(&pms, digest); digest2str(digest, ha1); } else if ((cred_info->data_type & PASSWD_MASK) == PJSIP_CRED_DATA_DIGEST) { pj_assert(cred_info->data.slen == 32); pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen ); } else { pj_assert(!"Invalid data_type"); } AUTH_TRACE_((THIS_FILE, " ha1=%.32s", ha1)); /*** *** ha2 = MD5(method ":" req_uri) ***/ pj_md5_init(&pms); MD5_APPEND( &pms, method->ptr, method->slen); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, uri->ptr, uri->slen); pj_md5_final(&pms, digest); digest2str(digest, ha2); AUTH_TRACE_((THIS_FILE, " ha2=%.32s", ha2)); /*** *** When qop is not used: *** response = MD5(ha1 ":" nonce ":" ha2) *** *** When qop=auth is used: *** response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2) ***/ pj_md5_init(&pms); MD5_APPEND( &pms, ha1, PJSIP_MD5STRLEN); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, nonce->ptr, nonce->slen); if (qop && qop->slen != 0) { MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, nc->ptr, nc->slen); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, cnonce->ptr, cnonce->slen); MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, qop->ptr, qop->slen); } MD5_APPEND( &pms, ":", 1); MD5_APPEND( &pms, ha2, PJSIP_MD5STRLEN); /* This is the final response digest. */ pj_md5_final(&pms, digest); /* Convert digest to string and store in chal->response. */ result->slen = PJSIP_MD5STRLEN; digest2str(digest, result->ptr); AUTH_TRACE_((THIS_FILE, " digest=%.32s", result->ptr)); AUTH_TRACE_((THIS_FILE, "Digest created")); } /* * Finds out if qop offer contains "auth" token. */ static pj_bool_t has_auth_qop( pj_pool_t *pool, const pj_str_t *qop_offer) { pj_str_t qop; char *p; pj_strdup_with_null( pool, &qop, qop_offer); p = qop.ptr; while (*p) { *p = (char)pj_tolower(*p); ++p; } p = qop.ptr; while (*p) { if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') { int e = *(p+4); if (e=='"' || e==',' || e==0) return PJ_TRUE; else p += 4; } else { ++p; } } return PJ_FALSE; } /* * Generate response digest. * Most of the parameters to generate the digest (i.e. username, realm, uri, * and nonce) are expected to be in the credential. Additional parameters (i.e. * password and method param) should be supplied in the argument. * * The resulting digest will be stored in cred->response. * The pool is used to allocate 32 bytes to store the digest in cred->response. */ static pj_status_t respond_digest( pj_pool_t *pool, pjsip_digest_credential *cred, const pjsip_digest_challenge *chal, const pj_str_t *uri, const pjsip_cred_info *cred_info, const pj_str_t *cnonce, pj_uint32_t nc, const pj_str_t *method) { const pj_str_t pjsip_AKAv1_MD5_STR = { "AKAv1-MD5", 9 }; /* Check algorithm is supported. We support MD5 and AKAv1-MD5. */ if (chal->algorithm.slen==0 || (pj_stricmp(&chal->algorithm, &pjsip_MD5_STR)==0 || pj_stricmp(&chal->algorithm, &pjsip_AKAv1_MD5_STR)==0)) { ; } else { PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"", chal->algorithm.slen, chal->algorithm.ptr)); return PJSIP_EINVALIDALGORITHM; } /* Build digest credential from arguments. */ pj_strdup(pool, &cred->username, &cred_info->username); pj_strdup(pool, &cred->realm, &chal->realm); pj_strdup(pool, &cred->nonce, &chal->nonce); pj_strdup(pool, &cred->uri, uri); pj_strdup(pool, &cred->algorithm, &chal->algorithm); pj_strdup(pool, &cred->opaque, &chal->opaque); /* Allocate memory. */ cred->response.ptr = (char*) pj_pool_alloc(pool, PJSIP_MD5STRLEN); cred->response.slen = PJSIP_MD5STRLEN; if (chal->qop.slen == 0) { /* Server doesn't require quality of protection. */ if ((cred_info->data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) { /* Call application callback to create the response digest */ return (*cred_info->ext.aka.cb)(pool, chal, cred_info, method, cred); } else { /* Convert digest to string and store in chal->response. */ pjsip_auth_create_digest( &cred->response, &cred->nonce, NULL, NULL, NULL, uri, &chal->realm, cred_info, method); } } else if (has_auth_qop(pool, &chal->qop)) { /* Server requires quality of protection. * We respond with selecting "qop=auth" protection. */ cred->qop = pjsip_AUTH_STR; cred->nc.ptr = (char*) pj_pool_alloc(pool, 16); cred->nc.slen = pj_ansi_snprintf(cred->nc.ptr, 16, "%08u", nc); if (cnonce && cnonce->slen) { pj_strdup(pool, &cred->cnonce, cnonce); } else { pj_str_t dummy_cnonce = { "b39971", 6}; pj_strdup(pool, &cred->cnonce, &dummy_cnonce); } if ((cred_info->data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) { /* Call application callback to create the response digest */ return (*cred_info->ext.aka.cb)(pool, chal, cred_info, method, cred); } else { pjsip_auth_create_digest( &cred->response, &cred->nonce, &cred->nc, &cred->cnonce, &pjsip_AUTH_STR, uri, &chal->realm, cred_info, method ); } } else { /* Server requires quality protection that we don't support. */ PJ_LOG(4,(THIS_FILE, "Unsupported qop offer %.*s", chal->qop.slen, chal->qop.ptr)); return PJSIP_EINVALIDQOP; } return PJ_SUCCESS; } #if defined(PJSIP_AUTH_QOP_SUPPORT) && PJSIP_AUTH_QOP_SUPPORT!=0 /* * Update authentication session with a challenge. */ static void update_digest_session( pj_pool_t *ses_pool, pjsip_cached_auth *cached_auth, const pjsip_www_authenticate_hdr *hdr ) { if (hdr->challenge.digest.qop.slen == 0) { #if PJSIP_AUTH_AUTO_SEND_NEXT!=0 if (!cached_auth->last_chal || pj_stricmp2(&hdr->scheme, "digest")) { cached_auth->last_chal = (pjsip_www_authenticate_hdr*) pjsip_hdr_clone(ses_pool, hdr); } else { /* Only update if the new challenge is "significantly different" * than the one in the cache, to reduce memory usage. */ const pjsip_digest_challenge *d1 = &cached_auth->last_chal->challenge.digest; const pjsip_digest_challenge *d2 = &hdr->challenge.digest; if (pj_strcmp(&d1->domain, &d2->domain) || pj_strcmp(&d1->realm, &d2->realm) || pj_strcmp(&d1->nonce, &d2->nonce) || pj_strcmp(&d1->opaque, &d2->opaque) || pj_strcmp(&d1->algorithm, &d2->algorithm) || pj_strcmp(&d1->qop, &d2->qop)) { cached_auth->last_chal = (pjsip_www_authenticate_hdr*) pjsip_hdr_clone(ses_pool, hdr); } } #endif return; } /* Initialize cnonce and qop if not present. */ if (cached_auth->cnonce.slen == 0) { /* Save the whole challenge */ cached_auth->last_chal = (pjsip_www_authenticate_hdr*) pjsip_hdr_clone(ses_pool, hdr); /* Create cnonce */ pj_create_unique_string( ses_pool, &cached_auth->cnonce ); /* Initialize nonce-count */ cached_auth->nc = 1; /* Save realm. */ /* Note: allow empty realm (http://trac.pjsip.org/repos/ticket/1061) pj_assert(cached_auth->realm.slen != 0); */ if (cached_auth->realm.slen == 0) { pj_strdup(ses_pool, &cached_auth->realm, &hdr->challenge.digest.realm); } } else { /* Update last_nonce and nonce-count */ if (!pj_strcmp(&hdr->challenge.digest.nonce, &cached_auth->last_chal->challenge.digest.nonce)) { /* Same nonce, increment nonce-count */ ++cached_auth->nc; } else { /* Server gives new nonce. */ pj_strdup(ses_pool, &cached_auth->last_chal->challenge.digest.nonce, &hdr->challenge.digest.nonce); /* Has the opaque changed? */ if (pj_strcmp(&cached_auth->last_chal->challenge.digest.opaque, &hdr->challenge.digest.opaque)) { pj_strdup(ses_pool, &cached_auth->last_chal->challenge.digest.opaque, &hdr->challenge.digest.opaque); } cached_auth->nc = 1; } } } #endif /* PJSIP_AUTH_QOP_SUPPORT */ /* Find cached authentication in the list for the specified realm. */ static pjsip_cached_auth *find_cached_auth( pjsip_auth_clt_sess *sess, const pj_str_t *realm ) { pjsip_cached_auth *auth = sess->cached_auth.next; while (auth != &sess->cached_auth) { if (pj_stricmp(&auth->realm, realm) == 0) return auth; auth = auth->next; } return NULL; } /* Find credential to use for the specified realm and auth scheme. */ static const pjsip_cred_info* auth_find_cred( const pjsip_auth_clt_sess *sess, const pj_str_t *realm, const pj_str_t *auth_scheme) { unsigned i; int wildcard = -1; PJ_UNUSED_ARG(auth_scheme); for (i=0; icred_cnt; ++i) { if (pj_stricmp(&sess->cred_info[i].realm, realm) == 0) return &sess->cred_info[i]; else if (sess->cred_info[i].realm.slen == 1 && sess->cred_info[i].realm.ptr[0] == '*') { wildcard = i; } } /* No matching realm. See if we have credential with wildcard ('*') * as the realm. */ if (wildcard != -1) return &sess->cred_info[wildcard]; /* Nothing is suitable */ return NULL; } /* Init client session. */ PJ_DEF(pj_status_t) pjsip_auth_clt_init( pjsip_auth_clt_sess *sess, pjsip_endpoint *endpt, pj_pool_t *pool, unsigned options) { PJ_ASSERT_RETURN(sess && endpt && pool && (options==0), PJ_EINVAL); sess->pool = pool; sess->endpt = endpt; sess->cred_cnt = 0; sess->cred_info = NULL; pj_list_init(&sess->cached_auth); return PJ_SUCCESS; } /* Clone session. */ PJ_DEF(pj_status_t) pjsip_auth_clt_clone( pj_pool_t *pool, pjsip_auth_clt_sess *sess, const pjsip_auth_clt_sess *rhs ) { unsigned i; PJ_ASSERT_RETURN(pool && sess && rhs, PJ_EINVAL); pjsip_auth_clt_init(sess, (pjsip_endpoint*)rhs->endpt, pool, 0); sess->cred_cnt = rhs->cred_cnt; sess->cred_info = (pjsip_cred_info*) pj_pool_alloc(pool, sess->cred_cnt*sizeof(pjsip_cred_info)); for (i=0; icred_cnt; ++i) { pj_strdup(pool, &sess->cred_info[i].realm, &rhs->cred_info[i].realm); pj_strdup(pool, &sess->cred_info[i].scheme, &rhs->cred_info[i].scheme); pj_strdup(pool, &sess->cred_info[i].username, &rhs->cred_info[i].username); sess->cred_info[i].data_type = rhs->cred_info[i].data_type; pj_strdup(pool, &sess->cred_info[i].data, &rhs->cred_info[i].data); } /* TODO note: * Cloning the full authentication client is quite a big task. * We do only the necessary bits here, i.e. cloning the credentials. * The drawback of this basic approach is, a forked dialog will have to * re-authenticate itself on the next request because it has lost the * cached authentication headers. */ PJ_TODO(FULL_CLONE_OF_AUTH_CLIENT_SESSION); return PJ_SUCCESS; } /* Set client credentials. */ PJ_DEF(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess, int cred_cnt, const pjsip_cred_info *c) { PJ_ASSERT_RETURN(sess && c, PJ_EINVAL); if (cred_cnt == 0) { sess->cred_cnt = 0; } else { int i; sess->cred_info = (pjsip_cred_info*) pj_pool_alloc(sess->pool, cred_cnt * sizeof(*c)); for (i=0; icred_info[i].data_type = c[i].data_type; /* When data_type is PJSIP_CRED_DATA_EXT_AKA, * callback must be specified. */ if ((c[i].data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) { #if !PJSIP_HAS_DIGEST_AKA_AUTH if (!PJSIP_HAS_DIGEST_AKA_AUTH) { pj_assert(!"PJSIP_HAS_DIGEST_AKA_AUTH is not enabled"); return PJSIP_EAUTHINAKACRED; } #endif /* Callback must be specified */ PJ_ASSERT_RETURN(c[i].ext.aka.cb != NULL, PJ_EINVAL); /* Verify K len */ PJ_ASSERT_RETURN(c[i].ext.aka.k.slen <= PJSIP_AKA_KLEN, PJSIP_EAUTHINAKACRED); /* Verify OP len */ PJ_ASSERT_RETURN(c[i].ext.aka.op.slen <= PJSIP_AKA_OPLEN, PJSIP_EAUTHINAKACRED); /* Verify AMF len */ PJ_ASSERT_RETURN(c[i].ext.aka.amf.slen <= PJSIP_AKA_AMFLEN, PJSIP_EAUTHINAKACRED); sess->cred_info[i].ext.aka.cb = c[i].ext.aka.cb; pj_strdup(sess->pool, &sess->cred_info[i].ext.aka.k, &c[i].ext.aka.k); pj_strdup(sess->pool, &sess->cred_info[i].ext.aka.op, &c[i].ext.aka.op); pj_strdup(sess->pool, &sess->cred_info[i].ext.aka.amf, &c[i].ext.aka.amf); } pj_strdup(sess->pool, &sess->cred_info[i].scheme, &c[i].scheme); pj_strdup(sess->pool, &sess->cred_info[i].realm, &c[i].realm); pj_strdup(sess->pool, &sess->cred_info[i].username, &c[i].username); pj_strdup(sess->pool, &sess->cred_info[i].data, &c[i].data); } sess->cred_cnt = cred_cnt; } return PJ_SUCCESS; } /* * Set the preference for the client authentication session. */ PJ_DEF(pj_status_t) pjsip_auth_clt_set_prefs(pjsip_auth_clt_sess *sess, const pjsip_auth_clt_pref *p) { PJ_ASSERT_RETURN(sess && p, PJ_EINVAL); pj_memcpy(&sess->pref, p, sizeof(*p)); pj_strdup(sess->pool, &sess->pref.algorithm, &p->algorithm); //if (sess->pref.algorithm.slen == 0) // sess->pref.algorithm = pj_str("md5"); return PJ_SUCCESS; } /* * Get the preference for the client authentication session. */ PJ_DEF(pj_status_t) pjsip_auth_clt_get_prefs(pjsip_auth_clt_sess *sess, pjsip_auth_clt_pref *p) { PJ_ASSERT_RETURN(sess && p, PJ_EINVAL); pj_memcpy(p, &sess->pref, sizeof(pjsip_auth_clt_pref)); return PJ_SUCCESS; } /* * Create Authorization/Proxy-Authorization response header based on the challege * in WWW-Authenticate/Proxy-Authenticate header. */ static pj_status_t auth_respond( pj_pool_t *req_pool, const pjsip_www_authenticate_hdr *hdr, const pjsip_uri *uri, const pjsip_cred_info *cred_info, const pjsip_method *method, pj_pool_t *sess_pool, pjsip_cached_auth *cached_auth, pjsip_authorization_hdr **p_h_auth) { pjsip_authorization_hdr *hauth; char tmp[PJSIP_MAX_URL_SIZE]; pj_str_t uri_str; pj_pool_t *pool; pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(req_pool && hdr && uri && cred_info && method && sess_pool && cached_auth && p_h_auth, PJ_EINVAL); /* Print URL in the original request. */ uri_str.ptr = tmp; uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp,sizeof(tmp)); if (uri_str.slen < 1) { pj_assert(!"URL is too long!"); return PJSIP_EURITOOLONG; } # if (PJSIP_AUTH_HEADER_CACHING) { pool = sess_pool; PJ_UNUSED_ARG(req_pool); } # else { pool = req_pool; PJ_UNUSED_ARG(sess_pool); } # endif if (hdr->type == PJSIP_H_WWW_AUTHENTICATE) hauth = pjsip_authorization_hdr_create(pool); else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE) hauth = pjsip_proxy_authorization_hdr_create(pool); else { pj_assert(!"Invalid response header!"); return PJSIP_EINVALIDHDR; } /* Only support digest scheme at the moment. */ if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { pj_str_t *cnonce = NULL; pj_uint32_t nc = 1; /* Update the session (nonce-count etc) if required. */ # if PJSIP_AUTH_QOP_SUPPORT { if (cached_auth) { update_digest_session( sess_pool, cached_auth, hdr ); cnonce = &cached_auth->cnonce; nc = cached_auth->nc; } } # endif /* PJSIP_AUTH_QOP_SUPPORT */ hauth->scheme = pjsip_DIGEST_STR; status = respond_digest( pool, &hauth->credential.digest, &hdr->challenge.digest, &uri_str, cred_info, cnonce, nc, &method->name); if (status != PJ_SUCCESS) return status; /* Set qop type in auth session the first time only. */ if (hdr->challenge.digest.qop.slen != 0 && cached_auth) { if (cached_auth->qop_value == PJSIP_AUTH_QOP_NONE) { pj_str_t *qop_val = &hauth->credential.digest.qop; if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) { cached_auth->qop_value = PJSIP_AUTH_QOP_AUTH; } else { cached_auth->qop_value = PJSIP_AUTH_QOP_UNKNOWN; } } } } else { return PJSIP_EINVALIDAUTHSCHEME; } /* Keep the new authorization header in the cache, only * if no qop is not present. */ # if PJSIP_AUTH_HEADER_CACHING { if (hauth && cached_auth && cached_auth->qop_value == PJSIP_AUTH_QOP_NONE) { pjsip_cached_auth_hdr *cached_hdr; /* Delete old header with the same method. */ cached_hdr = cached_auth->cached_hdr.next; while (cached_hdr != &cached_auth->cached_hdr) { if (pjsip_method_cmp(method, &cached_hdr->method)==0) break; cached_hdr = cached_hdr->next; } /* Save the header to the list. */ if (cached_hdr != &cached_auth->cached_hdr) { cached_hdr->hdr = hauth; } else { cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr)); pjsip_method_copy( pool, &cached_hdr->method, method); cached_hdr->hdr = hauth; pj_list_insert_before( &cached_auth->cached_hdr, cached_hdr ); } } # if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && PJSIP_AUTH_AUTO_SEND_NEXT!=0 if (hdr != cached_auth->last_chal) { cached_auth->last_chal = pjsip_hdr_clone(sess_pool, hdr); } # endif } # endif *p_h_auth = hauth; return PJ_SUCCESS; } #if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && PJSIP_AUTH_AUTO_SEND_NEXT!=0 static pj_status_t new_auth_for_req( pjsip_tx_data *tdata, pjsip_auth_clt_sess *sess, pjsip_cached_auth *auth, pjsip_authorization_hdr **p_h_auth) { const pjsip_cred_info *cred; pjsip_authorization_hdr *hauth; pj_status_t status; PJ_ASSERT_RETURN(tdata && sess && auth, PJ_EINVAL); PJ_ASSERT_RETURN(auth->last_chal != NULL, PJSIP_EAUTHNOPREVCHAL); cred = auth_find_cred( sess, &auth->realm, &auth->last_chal->scheme ); if (!cred) return PJSIP_ENOCREDENTIAL; status = auth_respond( tdata->pool, auth->last_chal, tdata->msg->line.req.uri, cred, &tdata->msg->line.req.method, sess->pool, auth, &hauth); if (status != PJ_SUCCESS) return status; pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hauth); if (p_h_auth) *p_h_auth = hauth; return PJ_SUCCESS; } #endif /* Find credential in list of (Proxy-)Authorization headers */ static pjsip_authorization_hdr* get_header_for_realm(const pjsip_hdr *hdr_list, const pj_str_t *realm) { pjsip_authorization_hdr *h; h = (pjsip_authorization_hdr*)hdr_list->next; while (h != (pjsip_authorization_hdr*)hdr_list) { if (pj_stricmp(&h->credential.digest.realm, realm)==0) return h; h = h->next; } return NULL; } /* Initialize outgoing request. */ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess, pjsip_tx_data *tdata ) { const pjsip_method *method; pjsip_cached_auth *auth; pjsip_hdr added; PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL); PJ_ASSERT_RETURN(sess->pool, PJSIP_ENOTINITIALIZED); PJ_ASSERT_RETURN(tdata->msg->type==PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Init list */ pj_list_init(&added); /* Get the method. */ method = &tdata->msg->line.req.method; PJ_UNUSED_ARG(method); /* Warning about unused var caused by #if below */ auth = sess->cached_auth.next; while (auth != &sess->cached_auth) { /* Reset stale counter */ auth->stale_cnt = 0; if (auth->qop_value == PJSIP_AUTH_QOP_NONE) { # if defined(PJSIP_AUTH_HEADER_CACHING) && \ PJSIP_AUTH_HEADER_CACHING!=0 { pjsip_cached_auth_hdr *entry = auth->cached_hdr.next; while (entry != &auth->cached_hdr) { if (pjsip_method_cmp(&entry->method, method)==0) { pjsip_authorization_hdr *hauth; hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr); //pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); pj_list_push_back(&added, hauth); break; } entry = entry->next; } # if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \ PJSIP_AUTH_AUTO_SEND_NEXT!=0 { if (entry == &auth->cached_hdr) new_auth_for_req( tdata, sess, auth, NULL); } # endif } # elif defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \ PJSIP_AUTH_AUTO_SEND_NEXT!=0 { new_auth_for_req( tdata, sess, auth, NULL); } # endif } # if defined(PJSIP_AUTH_QOP_SUPPORT) && \ defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \ (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT) else if (auth->qop_value == PJSIP_AUTH_QOP_AUTH) { /* For qop="auth", we have to re-create the authorization header. */ const pjsip_cred_info *cred; pjsip_authorization_hdr *hauth; pj_status_t status; cred = auth_find_cred(sess, &auth->realm, &auth->last_chal->scheme); if (!cred) { auth = auth->next; continue; } status = auth_respond( tdata->pool, auth->last_chal, tdata->msg->line.req.uri, cred, &tdata->msg->line.req.method, sess->pool, auth, &hauth); if (status != PJ_SUCCESS) return status; //pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); pj_list_push_back(&added, hauth); } # endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */ auth = auth->next; } if (sess->pref.initial_auth == PJ_FALSE) { pjsip_hdr *h; /* Don't want to send initial empty Authorization header, so * just send whatever available in the list (maybe empty). */ h = added.next; while (h != &added) { pjsip_hdr *next = h->next; pjsip_msg_add_hdr(tdata->msg, h); h = next; } } else { /* For each realm, add either the cached authorization header * or add an empty authorization header. */ unsigned i; pj_str_t uri; uri.ptr = (char*)pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE); uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, tdata->msg->line.req.uri, uri.ptr, PJSIP_MAX_URL_SIZE); if (uri.slen < 1 || uri.slen >= PJSIP_MAX_URL_SIZE) return PJSIP_EURITOOLONG; for (i=0; icred_cnt; ++i) { pjsip_cred_info *c = &sess->cred_info[i]; pjsip_authorization_hdr *h; h = get_header_for_realm(&added, &c->realm); if (h) { pj_list_erase(h); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)h); } else { pjsip_authorization_hdr *hs; hs = pjsip_authorization_hdr_create(tdata->pool); pj_strdup(tdata->pool, &hs->scheme, &c->scheme); pj_strdup(tdata->pool, &hs->credential.digest.username, &c->username); pj_strdup(tdata->pool, &hs->credential.digest.realm, &c->realm); pj_strdup(tdata->pool, &hs->credential.digest.uri, &uri); pj_strdup(tdata->pool, &hs->credential.digest.algorithm, &sess->pref.algorithm); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hs); } } } return PJ_SUCCESS; } /* Process authorization challenge */ static pj_status_t process_auth( pj_pool_t *req_pool, const pjsip_www_authenticate_hdr *hchal, const pjsip_uri *uri, pjsip_tx_data *tdata, pjsip_auth_clt_sess *sess, pjsip_cached_auth *cached_auth, pjsip_authorization_hdr **h_auth) { const pjsip_cred_info *cred; pjsip_authorization_hdr *sent_auth = NULL; pjsip_hdr *hdr; pj_status_t status; /* See if we have sent authorization header for this realm */ hdr = tdata->msg->hdr.next; while (hdr != &tdata->msg->hdr) { if ((hchal->type == PJSIP_H_WWW_AUTHENTICATE && hdr->type == PJSIP_H_AUTHORIZATION) || (hchal->type == PJSIP_H_PROXY_AUTHENTICATE && hdr->type == PJSIP_H_PROXY_AUTHORIZATION)) { sent_auth = (pjsip_authorization_hdr*) hdr; if (pj_stricmp(&hchal->challenge.common.realm, &sent_auth->credential.common.realm )==0) { /* If this authorization has empty response, remove it. */ if (pj_stricmp(&sent_auth->scheme, &pjsip_DIGEST_STR)==0 && sent_auth->credential.digest.response.slen == 0) { /* This is empty authorization, remove it. */ hdr = hdr->next; pj_list_erase(sent_auth); continue; } else { /* Found previous authorization attempt */ break; } } } hdr = hdr->next; } /* If we have sent, see if server rejected because of stale nonce or * other causes. */ if (hdr != &tdata->msg->hdr) { pj_bool_t stale; /* Detect "stale" state */ stale = hchal->challenge.digest.stale; if (!stale) { /* If stale is false, check is nonce has changed. Some servers * (broken ones!) want to change nonce but they fail to set * stale to true. */ stale = pj_strcmp(&hchal->challenge.digest.nonce, &sent_auth->credential.digest.nonce); } if (stale == PJ_FALSE) { /* Our credential is rejected. No point in trying to re-supply * the same credential. */ PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s: " "server rejected with stale=false", sent_auth->credential.digest.username.slen, sent_auth->credential.digest.username.ptr, sent_auth->credential.digest.realm.slen, sent_auth->credential.digest.realm.ptr)); return PJSIP_EFAILEDCREDENTIAL; } cached_auth->stale_cnt++; if (cached_auth->stale_cnt >= PJSIP_MAX_STALE_COUNT) { /* Our credential is rejected. No point in trying to re-supply * the same credential. */ PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s: " "maximum number of stale retries exceeded", sent_auth->credential.digest.username.slen, sent_auth->credential.digest.username.ptr, sent_auth->credential.digest.realm.slen, sent_auth->credential.digest.realm.ptr)); return PJSIP_EAUTHSTALECOUNT; } /* Otherwise remove old, stale authorization header from the mesasge. * We will supply a new one. */ pj_list_erase(sent_auth); } /* Find credential to be used for the challenge. */ cred = auth_find_cred( sess, &hchal->challenge.common.realm, &hchal->scheme); if (!cred) { const pj_str_t *realm = &hchal->challenge.common.realm; PJ_LOG(4,(THIS_FILE, "Unable to set auth for %s: can not find credential for %.*s/%.*s", tdata->obj_name, realm->slen, realm->ptr, hchal->scheme.slen, hchal->scheme.ptr)); return PJSIP_ENOCREDENTIAL; } /* Respond to authorization challenge. */ status = auth_respond( req_pool, hchal, uri, cred, &tdata->msg->line.req.method, sess->pool, cached_auth, h_auth); return status; } /* Reinitialize outgoing request after 401/407 response is received. * The purpose of this function is: * - to add a Authorization/Proxy-Authorization header. * - to put the newly created Authorization/Proxy-Authorization header * in cached_list. */ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess, const pjsip_rx_data *rdata, pjsip_tx_data *old_request, pjsip_tx_data **new_request ) { pjsip_tx_data *tdata; const pjsip_hdr *hdr; unsigned chal_cnt; pjsip_via_hdr *via; pj_status_t status; PJ_ASSERT_RETURN(sess && rdata && old_request && new_request, PJ_EINVAL); PJ_ASSERT_RETURN(sess->pool, PJSIP_ENOTINITIALIZED); PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG, PJSIP_ENOTRESPONSEMSG); PJ_ASSERT_RETURN(old_request->msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); PJ_ASSERT_RETURN(rdata->msg_info.msg->line.status.code == 401 || rdata->msg_info.msg->line.status.code == 407, PJSIP_EINVALIDSTATUS); tdata = old_request; tdata->auth_retry = PJ_FALSE; /* * Respond to each authentication challenge. */ hdr = rdata->msg_info.msg->hdr.next; chal_cnt = 0; while (hdr != &rdata->msg_info.msg->hdr) { pjsip_cached_auth *cached_auth; const pjsip_www_authenticate_hdr *hchal; pjsip_authorization_hdr *hauth; /* Find WWW-Authenticate or Proxy-Authenticate header. */ while (hdr != &rdata->msg_info.msg->hdr && hdr->type != PJSIP_H_WWW_AUTHENTICATE && hdr->type != PJSIP_H_PROXY_AUTHENTICATE) { hdr = hdr->next; } if (hdr == &rdata->msg_info.msg->hdr) break; hchal = (const pjsip_www_authenticate_hdr*) hdr; ++chal_cnt; /* Find authentication session for this realm, create a new one * if not present. */ cached_auth = find_cached_auth(sess, &hchal->challenge.common.realm ); if (!cached_auth) { cached_auth = PJ_POOL_ZALLOC_T( sess->pool, pjsip_cached_auth); pj_strdup( sess->pool, &cached_auth->realm, &hchal->challenge.common.realm); cached_auth->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE); # if (PJSIP_AUTH_HEADER_CACHING) { pj_list_init(&cached_auth->cached_hdr); } # endif pj_list_insert_before( &sess->cached_auth, cached_auth ); } /* Create authorization header for this challenge, and update * authorization session. */ status = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri, tdata, sess, cached_auth, &hauth); if (status != PJ_SUCCESS) return status; /* Add to the message. */ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); /* Process next header. */ hdr = hdr->next; } /* Check if challenge is present */ if (chal_cnt == 0) return PJSIP_EAUTHNOCHAL; /* Remove branch param in Via header. */ via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); via->branch_param.slen = 0; /* Restore strict route set. * See http://trac.pjsip.org/repos/ticket/492 */ pjsip_restore_strict_route_set(tdata); /* Must invalidate the message! */ pjsip_tx_data_invalidate_msg(tdata); /* Retrying.. */ tdata->auth_retry = PJ_TRUE; /* Increment reference counter. */ pjsip_tx_data_add_ref(tdata); /* Done. */ *new_request = tdata; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_auth_msg.c ================================================ /* $Id: sip_auth_msg.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////////// /* * Authorization and Proxy-Authorization header. */ static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool, const pjsip_authorization_hdr *hdr); static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool, const pjsip_authorization_hdr *hdr); static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr, char *buf, pj_size_t size); static pjsip_hdr_vptr authorization_hdr_vptr = { (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_clone, (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_shallow_clone, (pjsip_hdr_print_fptr) &pjsip_authorization_hdr_print, }; PJ_DEF(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool) { pjsip_authorization_hdr *hdr; hdr = PJ_POOL_ZALLOC_T(pool, pjsip_authorization_hdr); init_hdr(hdr, PJSIP_H_AUTHORIZATION, &authorization_hdr_vptr); pj_list_init(&hdr->credential.common.other_param); return hdr; } PJ_DEF(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool) { pjsip_proxy_authorization_hdr *hdr; hdr = PJ_POOL_ZALLOC_T(pool, pjsip_proxy_authorization_hdr); init_hdr(hdr, PJSIP_H_PROXY_AUTHORIZATION, &authorization_hdr_vptr); pj_list_init(&hdr->credential.common.other_param); return hdr; } static int print_digest_credential(pjsip_digest_credential *cred, char *buf, pj_size_t size) { pj_ssize_t printed; char *startbuf = buf; char *endbuf = buf + size; const pjsip_parser_const_t *pc = pjsip_parser_const(); copy_advance_pair_quote_cond(buf, "username=", 9, cred->username, '"', '"'); copy_advance_pair_quote_cond_always(buf, ", realm=", 8, cred->realm, '"', '"'); copy_advance_pair_quote(buf, ", nonce=", 8, cred->nonce, '"', '"'); copy_advance_pair_quote_cond(buf, ", uri=", 6, cred->uri, '"', '"'); copy_advance_pair_quote(buf, ", response=", 11, cred->response, '"', '"'); copy_advance_pair(buf, ", algorithm=", 12, cred->algorithm); copy_advance_pair_quote_cond(buf, ", cnonce=", 9, cred->cnonce, '"', '"'); copy_advance_pair_quote_cond(buf, ", opaque=", 9, cred->opaque, '"', '"'); //Note: there's no dbl-quote in qop in Authorization header // (unlike WWW-Authenticate) //copy_advance_pair_quote_cond(buf, ", qop=", 6, cred->qop, '"', '"'); copy_advance_pair(buf, ", qop=", 6, cred->qop); copy_advance_pair(buf, ", nc=", 5, cred->nc); printed = pjsip_param_print_on(&cred->other_param, buf, endbuf-buf, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ','); if (printed < 0) return -1; buf += printed; return (int) (buf-startbuf); } static int print_pgp_credential(pjsip_pgp_credential *cred, char *buf, pj_size_t size) { PJ_UNUSED_ARG(cred); PJ_UNUSED_ARG(buf); PJ_UNUSED_ARG(size); return -1; } static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr, char *buf, pj_size_t size) { int printed; char *startbuf = buf; char *endbuf = buf + size; copy_advance(buf, hdr->name); *buf++ = ':'; *buf++ = ' '; copy_advance(buf, hdr->scheme); *buf++ = ' '; if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) { printed = print_digest_credential(&hdr->credential.digest, buf, endbuf - buf); } else if (pj_stricmp(&hdr->scheme, &pjsip_PGP_STR) == 0) { printed = print_pgp_credential(&hdr->credential.pgp, buf, endbuf - buf); } else { pj_assert(0); return -1; } if (printed == -1) return -1; buf += printed; *buf = '\0'; return (int)(buf-startbuf); } static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool, const pjsip_authorization_hdr *rhs) { /* This function also serves Proxy-Authorization header. */ pjsip_authorization_hdr *hdr; if (rhs->type == PJSIP_H_AUTHORIZATION) hdr = pjsip_authorization_hdr_create(pool); else hdr = pjsip_proxy_authorization_hdr_create(pool); pj_strdup(pool, &hdr->scheme, &rhs->scheme); if (pj_stricmp2(&hdr->scheme, "digest") == 0) { pj_strdup(pool, &hdr->credential.digest.username, &rhs->credential.digest.username); pj_strdup(pool, &hdr->credential.digest.realm, &rhs->credential.digest.realm); pj_strdup(pool, &hdr->credential.digest.nonce, &rhs->credential.digest.nonce); pj_strdup(pool, &hdr->credential.digest.uri, &rhs->credential.digest.uri); pj_strdup(pool, &hdr->credential.digest.response, &rhs->credential.digest.response); pj_strdup(pool, &hdr->credential.digest.algorithm, &rhs->credential.digest.algorithm); pj_strdup(pool, &hdr->credential.digest.cnonce, &rhs->credential.digest.cnonce); pj_strdup(pool, &hdr->credential.digest.opaque, &rhs->credential.digest.opaque); pj_strdup(pool, &hdr->credential.digest.qop, &rhs->credential.digest.qop); pj_strdup(pool, &hdr->credential.digest.nc, &rhs->credential.digest.nc); pjsip_param_clone(pool, &hdr->credential.digest.other_param, &rhs->credential.digest.other_param); } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) { pj_assert(0); return NULL; } else { pj_assert(0); return NULL; } return hdr; } static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool, const pjsip_authorization_hdr *rhs) { /* This function also serves Proxy-Authorization header. */ pjsip_authorization_hdr *hdr; hdr = PJ_POOL_ALLOC_T(pool, pjsip_authorization_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); pjsip_param_shallow_clone(pool, &hdr->credential.common.other_param, &rhs->credential.common.other_param); return hdr; } /////////////////////////////////////////////////////////////////////////////// /* * Proxy-Authenticate and WWW-Authenticate header. */ static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr, char *buf, pj_size_t size); static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool, const pjsip_www_authenticate_hdr *hdr); static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool, const pjsip_www_authenticate_hdr *hdr); static pjsip_hdr_vptr www_authenticate_hdr_vptr = { (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_clone, (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_shallow_clone, (pjsip_hdr_print_fptr) &pjsip_www_authenticate_hdr_print, }; PJ_DEF(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool) { pjsip_www_authenticate_hdr *hdr; hdr = PJ_POOL_ZALLOC_T(pool, pjsip_www_authenticate_hdr); init_hdr(hdr, PJSIP_H_WWW_AUTHENTICATE, &www_authenticate_hdr_vptr); pj_list_init(&hdr->challenge.common.other_param); return hdr; } PJ_DEF(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool) { pjsip_proxy_authenticate_hdr *hdr; hdr = PJ_POOL_ZALLOC_T(pool, pjsip_proxy_authenticate_hdr); init_hdr(hdr, PJSIP_H_PROXY_AUTHENTICATE, &www_authenticate_hdr_vptr); pj_list_init(&hdr->challenge.common.other_param); return hdr; } static int print_digest_challenge( pjsip_digest_challenge *chal, char *buf, pj_size_t size) { pj_ssize_t printed; char *startbuf = buf; char *endbuf = buf + size; const pjsip_parser_const_t *pc = pjsip_parser_const(); /* Allow empty realm, see http://trac.pjsip.org/repos/ticket/1061 */ copy_advance_pair_quote(buf, " realm=", 7, chal->realm, '"', '"'); copy_advance_pair_quote_cond(buf, ",domain=", 8, chal->domain, '"', '"'); copy_advance_pair_quote_cond(buf, ",nonce=", 7, chal->nonce, '"', '"'); copy_advance_pair_quote_cond(buf, ",opaque=", 8, chal->opaque, '"', '"'); if (chal->stale) { pj_str_t true_str = { "true", 4 }; copy_advance_pair(buf, ",stale=", 7, true_str); } copy_advance_pair(buf, ",algorithm=", 11, chal->algorithm); copy_advance_pair_quote_cond(buf, ",qop=", 5, chal->qop, '"', '"'); printed = pjsip_param_print_on(&chal->other_param, buf, endbuf-buf, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ','); if (printed < 0) return -1; buf += printed; return (int)(buf-startbuf); } static int print_pgp_challenge( pjsip_pgp_challenge *chal, char *buf, pj_size_t size) { PJ_UNUSED_ARG(chal); PJ_UNUSED_ARG(buf); PJ_UNUSED_ARG(size); return -1; } static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr, char *buf, pj_size_t size) { int printed; char *startbuf = buf; char *endbuf = buf + size; copy_advance(buf, hdr->name); *buf++ = ':'; *buf++ = ' '; copy_advance(buf, hdr->scheme); *buf++ = ' '; if (pj_stricmp2(&hdr->scheme, "digest") == 0) printed = print_digest_challenge(&hdr->challenge.digest, buf, endbuf - buf); else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) printed = print_pgp_challenge(&hdr->challenge.pgp, buf, endbuf - buf); else { pj_assert(0); return -1; } if (printed == -1) return -1; buf += printed; *buf = '\0'; return (int)(buf-startbuf); } static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool, const pjsip_www_authenticate_hdr *rhs) { /* This function also serves Proxy-Authenticate header. */ pjsip_www_authenticate_hdr *hdr; if (rhs->type == PJSIP_H_WWW_AUTHENTICATE) hdr = pjsip_www_authenticate_hdr_create(pool); else hdr = pjsip_proxy_authenticate_hdr_create(pool); pj_strdup(pool, &hdr->scheme, &rhs->scheme); if (pj_stricmp2(&hdr->scheme, "digest") == 0) { pj_strdup(pool, &hdr->challenge.digest.realm, &rhs->challenge.digest.realm); pj_strdup(pool, &hdr->challenge.digest.domain, &rhs->challenge.digest.domain); pj_strdup(pool, &hdr->challenge.digest.nonce, &rhs->challenge.digest.nonce); pj_strdup(pool, &hdr->challenge.digest.opaque, &rhs->challenge.digest.opaque); hdr->challenge.digest.stale = rhs->challenge.digest.stale; pj_strdup(pool, &hdr->challenge.digest.algorithm, &rhs->challenge.digest.algorithm); pj_strdup(pool, &hdr->challenge.digest.qop, &rhs->challenge.digest.qop); pjsip_param_clone(pool, &hdr->challenge.digest.other_param, &rhs->challenge.digest.other_param); } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) { pj_assert(0); return NULL; } else { pj_assert(0); return NULL; } return hdr; } static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool, const pjsip_www_authenticate_hdr *rhs) { /* This function also serves Proxy-Authenticate header. */ pjsip_www_authenticate_hdr *hdr; hdr = PJ_POOL_ALLOC_T(pool, pjsip_www_authenticate_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); pjsip_param_shallow_clone(pool, &hdr->challenge.common.other_param, &rhs->challenge.common.other_param); return hdr; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_auth_parser.c ================================================ /* $Id: sip_auth_parser.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include static pjsip_hdr* parse_hdr_authorization ( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_proxy_authorization ( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_www_authenticate ( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_proxy_authenticate ( pjsip_parse_ctx *ctx ); static void parse_digest_credential ( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_credential *cred); static void parse_pgp_credential ( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_credential *cred); static void parse_digest_challenge ( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_challenge *chal); static void parse_pgp_challenge ( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_challenge *chal); const pj_str_t pjsip_USERNAME_STR = { "username", 8 }, pjsip_REALM_STR = { "realm", 5}, pjsip_NONCE_STR = { "nonce", 5}, pjsip_URI_STR = { "uri", 3 }, pjsip_RESPONSE_STR = { "response", 8 }, pjsip_ALGORITHM_STR = { "algorithm", 9 }, pjsip_DOMAIN_STR = { "domain", 6 }, pjsip_STALE_STR = { "stale", 5}, pjsip_QOP_STR = { "qop", 3}, pjsip_CNONCE_STR = { "cnonce", 6}, pjsip_OPAQUE_STR = { "opaque", 6}, pjsip_NC_STR = { "nc", 2}, pjsip_TRUE_STR = { "true", 4}, pjsip_QUOTED_TRUE_STR = { "\"true\"", 6}, pjsip_FALSE_STR = { "false", 5}, pjsip_QUOTED_FALSE_STR = { "\"false\"", 7}, pjsip_DIGEST_STR = { "Digest", 6}, pjsip_QUOTED_DIGEST_STR = { "\"Digest\"", 8}, pjsip_PGP_STR = { "PGP", 3 }, pjsip_QUOTED_PGP_STR = { "\"PGP\"", 5 }, pjsip_MD5_STR = { "md5", 3 }, pjsip_QUOTED_MD5_STR = { "\"md5\"", 5}, pjsip_AUTH_STR = { "auth", 4}, pjsip_QUOTED_AUTH_STR = { "\"auth\"", 6 }; static void parse_digest_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_credential *cred) { pj_list_init(&cred->other_param); for (;;) { pj_str_t name, value; pjsip_parse_param_imp(scanner, pool, &name, &value, PJSIP_PARSE_REMOVE_QUOTE); if (!pj_stricmp(&name, &pjsip_USERNAME_STR)) { cred->username = value; } else if (!pj_stricmp(&name, &pjsip_REALM_STR)) { cred->realm = value; } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) { cred->nonce = value; } else if (!pj_stricmp(&name, &pjsip_URI_STR)) { cred->uri = value; } else if (!pj_stricmp(&name, &pjsip_RESPONSE_STR)) { cred->response = value; } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) { cred->algorithm = value; } else if (!pj_stricmp(&name, &pjsip_CNONCE_STR)) { cred->cnonce = value; } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) { cred->opaque = value; } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) { cred->qop = value; } else if (!pj_stricmp(&name, &pjsip_NC_STR)) { cred->nc = value; } else { pjsip_param *p = PJ_POOL_ALLOC_T(pool, pjsip_param); p->name = name; p->value = value; pj_list_insert_before(&cred->other_param, p); } /* Eat comma */ if (!pj_scan_is_eof(scanner) && *scanner->curptr == ',') pj_scan_get_char(scanner); else break; } } static void parse_pgp_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_credential *cred) { PJ_UNUSED_ARG(scanner); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(cred); PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); } static void parse_digest_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_challenge *chal) { pj_list_init(&chal->other_param); for (;;) { pj_str_t name, value, unquoted_value; pjsip_parse_param_imp(scanner, pool, &name, &value, 0); if (value.ptr && (value.ptr[0] == '"')) { unquoted_value.ptr = value.ptr + 1; unquoted_value.slen = value.slen - 2; } else { unquoted_value.ptr = value.ptr; unquoted_value.slen = value.slen; } if (!pj_stricmp(&name, &pjsip_REALM_STR)) { chal->realm = unquoted_value; } else if (!pj_stricmp(&name, &pjsip_DOMAIN_STR)) { chal->domain = unquoted_value; } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) { chal->nonce = unquoted_value; } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) { chal->opaque = unquoted_value; } else if (!pj_stricmp(&name, &pjsip_STALE_STR)) { if (!pj_stricmp(&value, &pjsip_TRUE_STR) || !pj_stricmp(&value, &pjsip_QUOTED_TRUE_STR)) { chal->stale = 1; } } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) { chal->algorithm = unquoted_value; } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) { chal->qop = unquoted_value; } else { pjsip_param *p = PJ_POOL_ALLOC_T(pool, pjsip_param); p->name = name; p->value = value; pj_list_insert_before(&chal->other_param, p); } /* Eat comma */ if (!pj_scan_is_eof(scanner) && *scanner->curptr == ',') pj_scan_get_char(scanner); else break; } } static void parse_pgp_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_challenge *chal) { PJ_UNUSED_ARG(scanner); PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(chal); PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); } static void int_parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool, pjsip_authorization_hdr *hdr) { const pjsip_parser_const_t *pc = pjsip_parser_const(); if (*scanner->curptr == '"') { pj_scan_get_quote(scanner, '"', '"', &hdr->scheme); hdr->scheme.ptr++; hdr->scheme.slen -= 2; } else { pj_scan_get(scanner, &pc->pjsip_TOKEN_SPEC, &hdr->scheme); } if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { parse_digest_credential(scanner, pool, &hdr->credential.digest); } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) { parse_pgp_credential( scanner, pool, &hdr->credential.pgp); } else { PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); } pjsip_parse_end_hdr_imp( scanner ); } static void int_parse_hdr_authenticate( pj_scanner *scanner, pj_pool_t *pool, pjsip_www_authenticate_hdr *hdr) { const pjsip_parser_const_t *pc = pjsip_parser_const(); if (*scanner->curptr == '"') { pj_scan_get_quote(scanner, '"', '"', &hdr->scheme); hdr->scheme.ptr++; hdr->scheme.slen -= 2; } else { pj_scan_get(scanner, &pc->pjsip_TOKEN_SPEC, &hdr->scheme); } if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { parse_digest_challenge(scanner, pool, &hdr->challenge.digest); } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) { parse_pgp_challenge(scanner, pool, &hdr->challenge.pgp); } else { PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); } pjsip_parse_end_hdr_imp( scanner ); } static pjsip_hdr* parse_hdr_authorization( pjsip_parse_ctx *ctx ) { pjsip_authorization_hdr *hdr = pjsip_authorization_hdr_create(ctx->pool); int_parse_hdr_authorization(ctx->scanner, ctx->pool, hdr); return (pjsip_hdr*)hdr; } static pjsip_hdr* parse_hdr_proxy_authorization( pjsip_parse_ctx *ctx ) { pjsip_proxy_authorization_hdr *hdr = pjsip_proxy_authorization_hdr_create(ctx->pool); int_parse_hdr_authorization(ctx->scanner, ctx->pool, hdr); return (pjsip_hdr*)hdr; } static pjsip_hdr* parse_hdr_www_authenticate( pjsip_parse_ctx *ctx ) { pjsip_www_authenticate_hdr *hdr = pjsip_www_authenticate_hdr_create(ctx->pool); int_parse_hdr_authenticate(ctx->scanner, ctx->pool, hdr); return (pjsip_hdr*)hdr; } static pjsip_hdr* parse_hdr_proxy_authenticate( pjsip_parse_ctx *ctx ) { pjsip_proxy_authenticate_hdr *hdr = pjsip_proxy_authenticate_hdr_create(ctx->pool); int_parse_hdr_authenticate(ctx->scanner, ctx->pool, hdr); return (pjsip_hdr*)hdr; } PJ_DEF(pj_status_t) pjsip_auth_init_parser() { pj_status_t status; status = pjsip_register_hdr_parser( "Authorization", NULL, &parse_hdr_authorization); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Proxy-Authorization", NULL, &parse_hdr_proxy_authorization); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "WWW-Authenticate", NULL, &parse_hdr_www_authenticate); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Proxy-Authenticate", NULL, &parse_hdr_proxy_authenticate); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); return PJ_SUCCESS; } PJ_DEF(void) pjsip_auth_deinit_parser() { } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_auth_parser_wrap.cpp ================================================ /* $Id: sip_auth_parser_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This file is a C++ wrapper, see ticket #886 for details. */ #include "sip_auth_parser.c" ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_auth_server.c ================================================ /* $Id: sip_auth_server.c 4214 2012-07-25 14:29:28Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include /* just to get pjsip_DIGEST_STR */ #include #include #include #include #include /* * Initialize server authorization session data structure to serve the * specified realm and to use lookup_func function to look for the credential * info. */ PJ_DEF(pj_status_t) pjsip_auth_srv_init( pj_pool_t *pool, pjsip_auth_srv *auth_srv, const pj_str_t *realm, pjsip_auth_lookup_cred *lookup, unsigned options ) { PJ_ASSERT_RETURN(pool && auth_srv && realm && lookup, PJ_EINVAL); pj_bzero(auth_srv, sizeof(*auth_srv)); pj_strdup( pool, &auth_srv->realm, realm); auth_srv->lookup = lookup; auth_srv->is_proxy = (options & PJSIP_AUTH_SRV_IS_PROXY); return PJ_SUCCESS; } /* * Initialize server authorization session data structure to serve the * specified realm and to use lookup_func function to look for the credential * info. */ PJ_DEF(pj_status_t) pjsip_auth_srv_init2( pj_pool_t *pool, pjsip_auth_srv *auth_srv, const pjsip_auth_srv_init_param *param) { PJ_ASSERT_RETURN(pool && auth_srv && param, PJ_EINVAL); pj_bzero(auth_srv, sizeof(*auth_srv)); pj_strdup( pool, &auth_srv->realm, param->realm); auth_srv->lookup2 = param->lookup2; auth_srv->is_proxy = (param->options & PJSIP_AUTH_SRV_IS_PROXY); return PJ_SUCCESS; } /* Verify incoming Authorization/Proxy-Authorization header against the * specified credential. */ static pj_status_t pjsip_auth_verify( const pjsip_authorization_hdr *hdr, const pj_str_t *method, const pjsip_cred_info *cred_info ) { if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) { char digest_buf[PJSIP_MD5STRLEN]; pj_str_t digest; const pjsip_digest_credential *dig = &hdr->credential.digest; /* Check that username and realm match. * These checks should have been performed before entering this * function. */ PJ_ASSERT_RETURN(pj_strcmp(&dig->username, &cred_info->username) == 0, PJ_EINVALIDOP); PJ_ASSERT_RETURN(pj_strcmp(&dig->realm, &cred_info->realm) == 0, PJ_EINVALIDOP); /* Prepare for our digest calculation. */ digest.ptr = digest_buf; digest.slen = PJSIP_MD5STRLEN; /* Create digest for comparison. */ pjsip_auth_create_digest(&digest, &hdr->credential.digest.nonce, &hdr->credential.digest.nc, &hdr->credential.digest.cnonce, &hdr->credential.digest.qop, &hdr->credential.digest.uri, &cred_info->realm, cred_info, method ); /* Compare digest. */ return (pj_stricmp(&digest, &hdr->credential.digest.response) == 0) ? PJ_SUCCESS : PJSIP_EAUTHINVALIDDIGEST; } else { pj_assert(!"Unsupported authentication scheme"); return PJSIP_EINVALIDAUTHSCHEME; } } /* * Request the authorization server framework to verify the authorization * information in the specified request in rdata. */ PJ_DEF(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv, pjsip_rx_data *rdata, int *status_code) { pjsip_authorization_hdr *h_auth; pjsip_msg *msg = rdata->msg_info.msg; pjsip_hdr_e htype; pj_str_t acc_name; pjsip_cred_info cred_info; pj_status_t status; PJ_ASSERT_RETURN(auth_srv && rdata, PJ_EINVAL); PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); htype = auth_srv->is_proxy ? PJSIP_H_PROXY_AUTHORIZATION : PJSIP_H_AUTHORIZATION; /* Initialize status with 200. */ *status_code = 200; /* Find authorization header for our realm. */ h_auth = (pjsip_authorization_hdr*) pjsip_msg_find_hdr(msg, htype, NULL); while (h_auth) { if (!pj_stricmp(&h_auth->credential.common.realm, &auth_srv->realm)) break; h_auth = h_auth->next; if (h_auth == (void*) &msg->hdr) { h_auth = NULL; break; } h_auth=(pjsip_authorization_hdr*)pjsip_msg_find_hdr(msg,htype,h_auth); } if (!h_auth) { *status_code = auth_srv->is_proxy ? 407 : 401; return PJSIP_EAUTHNOAUTH; } /* Check authorization scheme. */ if (pj_stricmp(&h_auth->scheme, &pjsip_DIGEST_STR) == 0) acc_name = h_auth->credential.digest.username; else { *status_code = auth_srv->is_proxy ? 407 : 401; return PJSIP_EINVALIDAUTHSCHEME; } /* Find the credential information for the account. */ if (auth_srv->lookup2) { pjsip_auth_lookup_cred_param param; pj_bzero(¶m, sizeof(param)); param.realm = auth_srv->realm; param.acc_name = acc_name; param.rdata = rdata; status = (*auth_srv->lookup2)(rdata->tp_info.pool, ¶m, &cred_info); if (status != PJ_SUCCESS) { *status_code = PJSIP_SC_FORBIDDEN; return status; } } else { status = (*auth_srv->lookup)(rdata->tp_info.pool, &auth_srv->realm, &acc_name, &cred_info); if (status != PJ_SUCCESS) { *status_code = PJSIP_SC_FORBIDDEN; return status; } } /* Authenticate with the specified credential. */ status = pjsip_auth_verify(h_auth, &msg->line.req.method.name, &cred_info); if (status != PJ_SUCCESS) { *status_code = PJSIP_SC_FORBIDDEN; } return status; } /* * Add authentication challenge headers to the outgoing response in tdata. * Application may specify its customized nonce and opaque for the challenge, * or can leave the value to NULL to make the function fills them in with * random characters. */ PJ_DEF(pj_status_t) pjsip_auth_srv_challenge( pjsip_auth_srv *auth_srv, const pj_str_t *qop, const pj_str_t *nonce, const pj_str_t *opaque, pj_bool_t stale, pjsip_tx_data *tdata) { pjsip_www_authenticate_hdr *hdr; char nonce_buf[16]; pj_str_t random; PJ_ASSERT_RETURN( auth_srv && tdata, PJ_EINVAL ); random.ptr = nonce_buf; random.slen = sizeof(nonce_buf); /* Create the header. */ if (auth_srv->is_proxy) hdr = pjsip_proxy_authenticate_hdr_create(tdata->pool); else hdr = pjsip_www_authenticate_hdr_create(tdata->pool); /* Initialize header. * Note: only support digest authentication now. */ hdr->scheme = pjsip_DIGEST_STR; hdr->challenge.digest.algorithm = pjsip_MD5_STR; if (nonce) { pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, nonce); } else { pj_create_random_string(nonce_buf, sizeof(nonce_buf)); pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, &random); } if (opaque) { pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, opaque); } else { pj_create_random_string(nonce_buf, sizeof(nonce_buf)); pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, &random); } if (qop) { pj_strdup(tdata->pool, &hdr->challenge.digest.qop, qop); } else { hdr->challenge.digest.qop.slen = 0; } pj_strdup(tdata->pool, &hdr->challenge.digest.realm, &auth_srv->realm); hdr->challenge.digest.stale = stale; pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_config.c ================================================ /* $Id: sip_config.c 4442 2013-03-19 07:39:25Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include /* pjsip configuration instance, initialized with default values */ pjsip_cfg_t pjsip_sip_cfg_var = { /* Global settings */ { PJSIP_ALLOW_PORT_IN_FROMTO_HDR, PJSIP_ACCEPT_REPLACE_IN_EARLY_STATE, 0, 0, PJSIP_DONT_SWITCH_TO_TCP, PJSIP_DONT_SWITCH_TO_TLS, PJSIP_FOLLOW_EARLY_MEDIA_FORK, PJSIP_REQ_HAS_VIA_ALIAS, PJSIP_RESOLVE_HOSTNAME_TO_GET_INTERFACE, 0 }, /* Transaction settings */ { PJSIP_MAX_TSX_COUNT, PJSIP_T1_TIMEOUT, PJSIP_T2_TIMEOUT, PJSIP_T4_TIMEOUT, PJSIP_TD_TIMEOUT }, /* Client registration client */ { PJSIP_REGISTER_CLIENT_CHECK_CONTACT }, /* TCP transport settings */ { PJSIP_TCP_KEEP_ALIVE_INTERVAL }, /* TLS transport settings */ { PJSIP_TLS_KEEP_ALIVE_INTERVAL } }; #ifdef PJ_DLL PJ_DEF(pjsip_cfg_t*) pjsip_cfg(void) { return &pjsip_sip_cfg_var; } #endif ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_dialog.c ================================================ /* $Id: sip_dialog.c 4208 2012-07-18 07:52:33Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "sip_dialog.c" long pjsip_dlg_lock_tls_id; /* Config */ pj_bool_t pjsip_include_allow_hdr_in_dlg = PJSIP_INCLUDE_ALLOW_HDR_IN_DLG; /* Contact header string */ static const pj_str_t HCONTACT = { "Contact", 7 }; PJ_DEF(pj_bool_t) pjsip_method_creates_dialog(const pjsip_method *m) { const pjsip_method subscribe = { PJSIP_OTHER_METHOD, {"SUBSCRIBE", 9}}; const pjsip_method refer = { PJSIP_OTHER_METHOD, {"REFER", 5}}; const pjsip_method notify = { PJSIP_OTHER_METHOD, {"NOTIFY", 6}}; const pjsip_method update = { PJSIP_OTHER_METHOD, {"UPDATE", 6}}; return m->id == PJSIP_INVITE_METHOD || (pjsip_method_cmp(m, &subscribe)==0) || (pjsip_method_cmp(m, &refer)==0) || (pjsip_method_cmp(m, ¬ify)==0) || (pjsip_method_cmp(m, &update)==0); } static pj_status_t create_dialog( pjsip_user_agent *ua, pjsip_dialog **p_dlg) { pjsip_endpoint *endpt; pj_pool_t *pool; pjsip_dialog *dlg; pj_status_t status; endpt = pjsip_ua_get_endpt(ua); if (!endpt) return PJ_EINVALIDOP; pool = pjsip_endpt_create_pool(endpt, "dlg%p", PJSIP_POOL_LEN_DIALOG, PJSIP_POOL_INC_DIALOG); if (!pool) return PJ_ENOMEM; dlg = PJ_POOL_ZALLOC_T(pool, pjsip_dialog); PJ_ASSERT_RETURN(dlg != NULL, PJ_ENOMEM); dlg->pool = pool; pj_ansi_snprintf(dlg->obj_name, sizeof(dlg->obj_name), "dlg%p", dlg); dlg->ua = ua; dlg->endpt = endpt; dlg->state = PJSIP_DIALOG_STATE_NULL; dlg->add_allow = pjsip_include_allow_hdr_in_dlg; pj_list_init(&dlg->inv_hdr); pj_list_init(&dlg->rem_cap_hdr); status = pj_mutex_create_recursive(pool, dlg->obj_name, &dlg->mutex_); if (status != PJ_SUCCESS) goto on_error; pjsip_target_set_init(&dlg->target_set); *p_dlg = dlg; return PJ_SUCCESS; on_error: if (dlg->mutex_) pj_mutex_destroy(dlg->mutex_); pjsip_endpt_release_pool(endpt, pool); return status; } static void destroy_dialog( pjsip_dialog *dlg, pj_bool_t unlock_mutex ) { if (dlg->mutex_) { if (unlock_mutex) pj_mutex_unlock(dlg->mutex_); pj_mutex_destroy(dlg->mutex_); dlg->mutex_ = NULL; } if (dlg->tp_sel.type != PJSIP_TPSELECTOR_NONE) { pjsip_tpselector_dec_ref(&dlg->tp_sel); pj_bzero(&dlg->tp_sel, sizeof(pjsip_tpselector)); } pjsip_endpt_release_pool(dlg->endpt, dlg->pool); } /* * Create an UAC dialog. */ PJ_DEF(pj_status_t) pjsip_dlg_create_uac( pjsip_user_agent *ua, const pj_str_t *local_uri, const pj_str_t *local_contact, const pj_str_t *remote_uri, const pj_str_t *target, pjsip_dialog **p_dlg) { pj_status_t status; pj_str_t tmp; pjsip_dialog *dlg; /* Check arguments. */ PJ_ASSERT_RETURN(ua && local_uri && remote_uri && p_dlg, PJ_EINVAL); /* Create dialog instance. */ status = create_dialog(ua, &dlg); if (status != PJ_SUCCESS) return status; /* Parse target. */ pj_strdup_with_null(dlg->pool, &tmp, target ? target : remote_uri); dlg->target = pjsip_parse_uri(dlg->pool, tmp.ptr, tmp.slen, 0); if (!dlg->target) { status = PJSIP_EINVALIDURI; goto on_error; } /* Put any header param in the target URI into INVITE header list. */ if (PJSIP_URI_SCHEME_IS_SIP(dlg->target) || PJSIP_URI_SCHEME_IS_SIPS(dlg->target)) { pjsip_param *param; pjsip_sip_uri *uri = (pjsip_sip_uri*)pjsip_uri_get_uri(dlg->target); param = uri->header_param.next; while (param != &uri->header_param) { pjsip_hdr *hdr; int c; c = param->value.ptr[param->value.slen]; param->value.ptr[param->value.slen] = '\0'; hdr = (pjsip_hdr*) pjsip_parse_hdr(dlg->pool, ¶m->name, param->value.ptr, param->value.slen, NULL); param->value.ptr[param->value.slen] = (char)c; if (hdr == NULL) { status = PJSIP_EINVALIDURI; goto on_error; } pj_list_push_back(&dlg->inv_hdr, hdr); param = param->next; } /* Now must remove any header params from URL, since that would * create another header in pjsip_endpt_create_request(). */ pj_list_init(&uri->header_param); } /* Add target to the target set */ pjsip_target_set_add_uri(&dlg->target_set, dlg->pool, dlg->target, 0); /* Init local info. */ dlg->local.info = pjsip_from_hdr_create(dlg->pool); pj_strdup_with_null(dlg->pool, &dlg->local.info_str, local_uri); dlg->local.info->uri = pjsip_parse_uri(dlg->pool, dlg->local.info_str.ptr, dlg->local.info_str.slen, 0); if (!dlg->local.info->uri) { status = PJSIP_EINVALIDURI; goto on_error; } /* Generate local tag. */ pj_create_unique_string(dlg->pool, &dlg->local.info->tag); /* Calculate hash value of local tag. */ dlg->local.tag_hval = pj_hash_calc_tolower(0, NULL, &dlg->local.info->tag); /* Randomize local CSeq. */ dlg->local.first_cseq = pj_rand() & 0x7FFF; dlg->local.cseq = dlg->local.first_cseq; /* Init local contact. */ pj_strdup_with_null(dlg->pool, &tmp, local_contact ? local_contact : local_uri); dlg->local.contact = (pjsip_contact_hdr*) pjsip_parse_hdr(dlg->pool, &HCONTACT, tmp.ptr, tmp.slen, NULL); if (!dlg->local.contact) { status = PJSIP_EINVALIDURI; goto on_error; } /* Init remote info. */ dlg->remote.info = pjsip_to_hdr_create(dlg->pool); pj_strdup_with_null(dlg->pool, &dlg->remote.info_str, remote_uri); dlg->remote.info->uri = pjsip_parse_uri(dlg->pool, dlg->remote.info_str.ptr, dlg->remote.info_str.slen, 0); if (!dlg->remote.info->uri) { status = PJSIP_EINVALIDURI; goto on_error; } /* Remove header param from remote.info_str, if any */ if (PJSIP_URI_SCHEME_IS_SIP(dlg->remote.info->uri) || PJSIP_URI_SCHEME_IS_SIPS(dlg->remote.info->uri)) { pjsip_sip_uri *sip_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(dlg->remote.info->uri); if (!pj_list_empty(&sip_uri->header_param)) { pj_str_t tmp2; /* Remove all header param */ pj_list_init(&sip_uri->header_param); /* Print URI */ tmp2.ptr = (char*) pj_pool_alloc(dlg->pool, dlg->remote.info_str.slen); tmp2.slen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, sip_uri, tmp2.ptr, dlg->remote.info_str.slen); if (tmp2.slen < 1) { status = PJSIP_EURITOOLONG; goto on_error; } /* Assign remote.info_str */ dlg->remote.info_str = tmp2; } } /* Initialize remote's CSeq to -1. */ dlg->remote.cseq = dlg->remote.first_cseq = -1; /* Initial role is UAC. */ dlg->role = PJSIP_ROLE_UAC; /* Secure? */ dlg->secure = PJSIP_URI_SCHEME_IS_SIPS(dlg->target); /* Generate Call-ID header. */ dlg->call_id = pjsip_cid_hdr_create(dlg->pool); pj_create_unique_string(dlg->pool, &dlg->call_id->id); /* Initial route set is empty. */ pj_list_init(&dlg->route_set); /* Init client authentication session. */ status = pjsip_auth_clt_init(&dlg->auth_sess, dlg->endpt, dlg->pool, 0); if (status != PJ_SUCCESS) goto on_error; /* Register this dialog to user agent. */ status = pjsip_ua_register_dlg( ua, dlg ); if (status != PJ_SUCCESS) goto on_error; /* Done! */ *p_dlg = dlg; PJ_LOG(5,(dlg->obj_name, "UAC dialog created")); return PJ_SUCCESS; on_error: destroy_dialog(dlg, PJ_FALSE); return status; } /* * Create UAS dialog. */ pj_status_t create_uas_dialog( pjsip_user_agent *ua, pjsip_rx_data *rdata, const pj_str_t *contact, pj_bool_t inc_lock, pjsip_dialog **p_dlg) { pj_status_t status; pjsip_hdr *pos = NULL; pjsip_contact_hdr *contact_hdr; pjsip_rr_hdr *rr; pjsip_transaction *tsx = NULL; pj_str_t tmp; enum { TMP_LEN=128}; pj_ssize_t len; pjsip_dialog *dlg; /* Check arguments. */ PJ_ASSERT_RETURN(ua && rdata && p_dlg, PJ_EINVAL); /* rdata must have request message. */ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Request must not have To tag. * This should have been checked in the user agent (or application?). */ PJ_ASSERT_RETURN(rdata->msg_info.to->tag.slen == 0, PJ_EINVALIDOP); /* The request must be a dialog establishing request. */ PJ_ASSERT_RETURN( pjsip_method_creates_dialog(&rdata->msg_info.msg->line.req.method), PJ_EINVALIDOP); /* Create dialog instance. */ status = create_dialog(ua, &dlg); if (status != PJ_SUCCESS) return status; /* Temprary string for getting the string representation of * both local and remote URI. */ tmp.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool, TMP_LEN); /* Init local info from the To header. */ dlg->local.info = (pjsip_fromto_hdr*) pjsip_hdr_clone(dlg->pool, rdata->msg_info.to); pjsip_fromto_hdr_set_from(dlg->local.info); /* Generate local tag. */ pj_create_unique_string(dlg->pool, &dlg->local.info->tag); /* Print the local info. */ len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, dlg->local.info->uri, tmp.ptr, TMP_LEN); if (len < 1) { pj_ansi_strcpy(tmp.ptr, "<-error: uri too long->"); tmp.slen = pj_ansi_strlen(tmp.ptr); } else tmp.slen = len; /* Save the local info. */ pj_strdup(dlg->pool, &dlg->local.info_str, &tmp); /* Calculate hash value of local tag. */ dlg->local.tag_hval = pj_hash_calc_tolower(0, NULL, &dlg->local.info->tag); /* Randomize local cseq */ dlg->local.first_cseq = pj_rand() & 0x7FFF; dlg->local.cseq = dlg->local.first_cseq; /* Init local contact. */ /* TODO: * Section 12.1.1, paragraph about using SIPS URI in Contact. * If the request that initiated the dialog contained a SIPS URI * in the Request-URI or in the top Record-Route header field value, * if there was any, or the Contact header field if there was no * Record-Route header field, the Contact header field in the response * MUST be a SIPS URI. */ if (contact) { pj_str_t tmp2; pj_strdup_with_null(dlg->pool, &tmp2, contact); dlg->local.contact = (pjsip_contact_hdr*) pjsip_parse_hdr(dlg->pool, &HCONTACT, tmp2.ptr, tmp2.slen, NULL); if (!dlg->local.contact) { status = PJSIP_EINVALIDURI; goto on_error; } } else { dlg->local.contact = pjsip_contact_hdr_create(dlg->pool); dlg->local.contact->uri = dlg->local.info->uri; } /* Init remote info from the From header. */ dlg->remote.info = (pjsip_fromto_hdr*) pjsip_hdr_clone(dlg->pool, rdata->msg_info.from); pjsip_fromto_hdr_set_to(dlg->remote.info); /* Print the remote info. */ len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, dlg->remote.info->uri, tmp.ptr, TMP_LEN); if (len < 1) { pj_ansi_strcpy(tmp.ptr, "<-error: uri too long->"); tmp.slen = pj_ansi_strlen(tmp.ptr); } else tmp.slen = len; /* Save the remote info. */ pj_strdup(dlg->pool, &dlg->remote.info_str, &tmp); /* Init remote's contact from Contact header. * Iterate the Contact URI until we find sip: or sips: scheme. */ do { contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, pos); if (contact_hdr) { if (!contact_hdr->uri || (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri))) { pos = (pjsip_hdr*)contact_hdr->next; if (pos == &rdata->msg_info.msg->hdr) contact_hdr = NULL; } else { break; } } } while (contact_hdr); if (!contact_hdr) { status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); goto on_error; } dlg->remote.contact = (pjsip_contact_hdr*) pjsip_hdr_clone(dlg->pool, (pjsip_hdr*)contact_hdr); /* Init remote's CSeq from CSeq header */ dlg->remote.cseq = dlg->remote.first_cseq = rdata->msg_info.cseq->cseq; /* Set initial target to remote's Contact. */ dlg->target = dlg->remote.contact->uri; /* Initial role is UAS */ dlg->role = PJSIP_ROLE_UAS; /* Secure? * RFC 3261 Section 12.1.1: * If the request arrived over TLS, and the Request-URI contained a * SIPS URI, the 'secure' flag is set to TRUE. */ dlg->secure = PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport) && PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri); /* Call-ID */ dlg->call_id = (pjsip_cid_hdr*) pjsip_hdr_clone(dlg->pool, rdata->msg_info.cid); /* Route set. * RFC 3261 Section 12.1.1: * The route set MUST be set to the list of URIs in the Record-Route * header field from the request, taken in order and preserving all URI * parameters. If no Record-Route header field is present in the request, * the route set MUST be set to the empty set. */ pj_list_init(&dlg->route_set); rr = rdata->msg_info.record_route; while (rr != NULL) { pjsip_route_hdr *route; /* Clone the Record-Route, change the type to Route header. */ route = (pjsip_route_hdr*) pjsip_hdr_clone(dlg->pool, rr); pjsip_routing_hdr_set_route(route); /* Add to route set. */ pj_list_push_back(&dlg->route_set, route); /* Find next Record-Route header. */ rr = rr->next; if (rr == (void*)&rdata->msg_info.msg->hdr) break; rr = (pjsip_route_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_RECORD_ROUTE, rr); } dlg->route_set_frozen = PJ_TRUE; /* Init client authentication session. */ status = pjsip_auth_clt_init(&dlg->auth_sess, dlg->endpt, dlg->pool, 0); if (status != PJ_SUCCESS) goto on_error; /* Increment the dialog's lock since tsx may cause the dialog to be * destroyed prematurely (such as in case of transport error). */ if (inc_lock) pjsip_dlg_inc_lock(dlg); /* Create UAS transaction for this request. */ status = pjsip_tsx_create_uas(dlg->ua, rdata, &tsx); if (status != PJ_SUCCESS) goto on_error; /* Associate this dialog to the transaction. */ tsx->mod_data[dlg->ua->id] = dlg; /* Increment tsx counter */ ++dlg->tsx_count; /* Calculate hash value of remote tag. */ dlg->remote.tag_hval = pj_hash_calc_tolower(0, NULL, &dlg->remote.info->tag); /* Update remote capabilities info */ pjsip_dlg_update_remote_cap(dlg, rdata->msg_info.msg, PJ_TRUE); /* Register this dialog to user agent. */ status = pjsip_ua_register_dlg( ua, dlg ); if (status != PJ_SUCCESS) goto on_error; /* Put this dialog in rdata's mod_data */ rdata->endpt_info.mod_data[ua->id] = dlg; PJ_TODO(DIALOG_APP_TIMER); /* Feed the first request to the transaction. */ pjsip_tsx_recv_msg(tsx, rdata); /* Done. */ *p_dlg = dlg; PJ_LOG(5,(dlg->obj_name, "UAS dialog created")); return PJ_SUCCESS; on_error: if (tsx) { pjsip_tsx_terminate(tsx, 500); pj_assert(dlg->tsx_count>0); --dlg->tsx_count; } if (inc_lock) { pjsip_dlg_dec_lock(dlg); } else { destroy_dialog(dlg, PJ_FALSE); } return status; } #if !DEPRECATED_FOR_TICKET_1902 /* * Create UAS dialog. */ PJ_DEF(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua, pjsip_rx_data *rdata, const pj_str_t *contact, pjsip_dialog **p_dlg) { return create_uas_dialog(ua, rdata, contact, PJ_FALSE, p_dlg); } #endif /* * Create UAS dialog and increase its session count. */ PJ_DEF(pj_status_t) pjsip_dlg_create_uas_and_inc_lock( pjsip_user_agent *ua, pjsip_rx_data *rdata, const pj_str_t *contact, pjsip_dialog **p_dlg) { return create_uas_dialog(ua, rdata, contact, PJ_TRUE, p_dlg); } /* * Bind dialog to a specific transport/listener. */ PJ_DEF(pj_status_t) pjsip_dlg_set_transport( pjsip_dialog *dlg, const pjsip_tpselector *sel) { /* Validate */ PJ_ASSERT_RETURN(dlg && sel, PJ_EINVAL); /* Start locking the dialog. */ pjsip_dlg_inc_lock(dlg); /* Decrement reference counter of previous transport selector */ pjsip_tpselector_dec_ref(&dlg->tp_sel); /* Copy transport selector structure .*/ pj_memcpy(&dlg->tp_sel, sel, sizeof(*sel)); /* Increment reference counter */ pjsip_tpselector_add_ref(&dlg->tp_sel); /* Unlock dialog. */ pjsip_dlg_dec_lock(dlg); return PJ_SUCCESS; } /* * Set "sent-by" field of Via header. */ PJ_DEF(pj_status_t) pjsip_dlg_set_via_sent_by( pjsip_dialog *dlg, pjsip_host_port *via_addr, pjsip_transport *via_tp) { PJ_ASSERT_RETURN(dlg, PJ_EINVAL); if (!via_addr) pj_bzero(&dlg->via_addr, sizeof(dlg->via_addr)); else { if (pj_strcmp(&dlg->via_addr.host, &via_addr->host)) pj_strdup(dlg->pool, &dlg->via_addr.host, &via_addr->host); dlg->via_addr.port = via_addr->port; } dlg->via_tp = via_tp; return PJ_SUCCESS; } /* * Create forked dialog from a response. */ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg, const pjsip_rx_data *rdata, pjsip_dialog **new_dlg ) { pjsip_dialog *dlg; const pjsip_msg *msg = rdata->msg_info.msg; const pjsip_hdr *end_hdr, *hdr; const pjsip_contact_hdr *contact; pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(first_dlg && rdata && new_dlg, PJ_EINVAL); /* rdata must be response message. */ PJ_ASSERT_RETURN(msg->type == PJSIP_RESPONSE_MSG, PJSIP_ENOTRESPONSEMSG); /* Status code MUST be 1xx (but not 100), or 2xx */ status = msg->line.status.code; PJ_ASSERT_RETURN( (status/100==1 && status!=100) || (status/100==2), PJ_EBUG); /* To tag must present in the response. */ PJ_ASSERT_RETURN(rdata->msg_info.to->tag.slen != 0, PJSIP_EMISSINGTAG); /* Find Contact header in the response */ contact = (const pjsip_contact_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL); if (contact == NULL || contact->uri == NULL) return PJSIP_EMISSINGHDR; /* Create the dialog. */ status = create_dialog((pjsip_user_agent*)first_dlg->ua, &dlg); if (status != PJ_SUCCESS) return status; /* Set remote target from the response. */ dlg->target = (pjsip_uri*) pjsip_uri_clone(dlg->pool, contact->uri); /* Clone local info. */ dlg->local.info = (pjsip_fromto_hdr*) pjsip_hdr_clone(dlg->pool, first_dlg->local.info); /* Clone local tag. */ pj_strdup(dlg->pool, &dlg->local.info->tag, &first_dlg->local.info->tag); dlg->local.tag_hval = first_dlg->local.tag_hval; /* Clone local CSeq. */ dlg->local.first_cseq = first_dlg->local.first_cseq; dlg->local.cseq = first_dlg->local.cseq; /* Clone local Contact. */ dlg->local.contact = (pjsip_contact_hdr*) pjsip_hdr_clone(dlg->pool, first_dlg->local.contact); /* Clone remote info. */ dlg->remote.info = (pjsip_fromto_hdr*) pjsip_hdr_clone(dlg->pool, first_dlg->remote.info); /* Set remote tag from the response. */ pj_strdup(dlg->pool, &dlg->remote.info->tag, &rdata->msg_info.to->tag); /* Initialize remote's CSeq to -1. */ dlg->remote.cseq = dlg->remote.first_cseq = -1; /* Initial role is UAC. */ dlg->role = PJSIP_ROLE_UAC; /* Dialog state depends on the response. */ status = msg->line.status.code/100; if (status == 1 || status == 2) dlg->state = PJSIP_DIALOG_STATE_ESTABLISHED; else { pj_assert(!"Invalid status code"); dlg->state = PJSIP_DIALOG_STATE_NULL; } /* Secure? */ dlg->secure = PJSIP_URI_SCHEME_IS_SIPS(dlg->target); /* Clone Call-ID header. */ dlg->call_id = (pjsip_cid_hdr*) pjsip_hdr_clone(dlg->pool, first_dlg->call_id); /* Get route-set from the response. */ pj_list_init(&dlg->route_set); end_hdr = &msg->hdr; for (hdr=msg->hdr.prev; hdr!=end_hdr; hdr=hdr->prev) { if (hdr->type == PJSIP_H_RECORD_ROUTE) { pjsip_route_hdr *r; r = (pjsip_route_hdr*) pjsip_hdr_clone(dlg->pool, hdr); pjsip_routing_hdr_set_route(r); pj_list_push_back(&dlg->route_set, r); } } //dlg->route_set_frozen = PJ_TRUE; /* Clone client authentication session. */ status = pjsip_auth_clt_clone(dlg->pool, &dlg->auth_sess, &first_dlg->auth_sess); if (status != PJ_SUCCESS) goto on_error; /* Register this dialog to user agent. */ status = pjsip_ua_register_dlg(dlg->ua, dlg ); if (status != PJ_SUCCESS) goto on_error; /* Done! */ *new_dlg = dlg; PJ_LOG(5,(dlg->obj_name, "Forked dialog created")); return PJ_SUCCESS; on_error: destroy_dialog(dlg, PJ_FALSE); return status; } /* * Destroy dialog. */ static pj_status_t unregister_and_destroy_dialog( pjsip_dialog *dlg, pj_bool_t unlock_mutex ) { pj_status_t status; /* Lock must have been held. */ /* Check dialog state. */ /* Number of sessions must be zero. */ PJ_ASSERT_RETURN(dlg->sess_count==0, PJ_EINVALIDOP); /* MUST not have pending transactions. */ PJ_ASSERT_RETURN(dlg->tsx_count==0, PJ_EINVALIDOP); /* Unregister from user agent. */ status = pjsip_ua_unregister_dlg(dlg->ua, dlg); if (status != PJ_SUCCESS) { pj_assert(!"Unexpected failed unregistration!"); return status; } /* Log */ PJ_LOG(5,(dlg->obj_name, "Dialog destroyed")); /* Destroy this dialog. */ destroy_dialog(dlg, unlock_mutex); return PJ_SUCCESS; } /* * Forcefully terminate dialog. */ PJ_DEF(pj_status_t) pjsip_dlg_terminate( pjsip_dialog *dlg ) { /* Number of sessions must be zero. */ PJ_ASSERT_RETURN(dlg->sess_count==0, PJ_EINVALIDOP); /* MUST not have pending transactions. */ PJ_ASSERT_RETURN(dlg->tsx_count==0, PJ_EINVALIDOP); return unregister_and_destroy_dialog(dlg, PJ_FALSE); } /* * Set route_set */ PJ_DEF(pj_status_t) pjsip_dlg_set_route_set( pjsip_dialog *dlg, const pjsip_route_hdr *route_set ) { pjsip_route_hdr *r; PJ_ASSERT_RETURN(dlg, PJ_EINVAL); pjsip_dlg_inc_lock(dlg); /* Clear route set. */ pj_list_init(&dlg->route_set); if (!route_set) { pjsip_dlg_dec_lock(dlg); return PJ_SUCCESS; } r = route_set->next; while (r != route_set) { pjsip_route_hdr *new_r; new_r = (pjsip_route_hdr*) pjsip_hdr_clone(dlg->pool, r); pj_list_push_back(&dlg->route_set, new_r); r = r->next; } pjsip_dlg_dec_lock(dlg); return PJ_SUCCESS; } /* * Increment session counter. */ PJ_DEF(pj_status_t) pjsip_dlg_inc_session( pjsip_dialog *dlg, pjsip_module *mod ) { PJ_ASSERT_RETURN(dlg && mod, PJ_EINVAL); pj_log_push_indent(); pjsip_dlg_inc_lock(dlg); ++dlg->sess_count; pjsip_dlg_dec_lock(dlg); PJ_LOG(5,(dlg->obj_name, "Session count inc to %d by %.*s", dlg->sess_count, (int)mod->name.slen, mod->name.ptr)); pj_log_pop_indent(); return PJ_SUCCESS; } /* * Lock dialog and increment session counter temporarily * to prevent it from being deleted. In addition, it must lock * the user agent's dialog table first, to prevent deadlock. */ PJ_DEF(void) pjsip_dlg_inc_lock(pjsip_dialog *dlg) { PJ_LOG(6,(dlg->obj_name, "Entering pjsip_dlg_inc_lock(), sess_count=%d", dlg->sess_count)); pj_mutex_lock(dlg->mutex_); dlg->sess_count++; PJ_LOG(6,(dlg->obj_name, "Leaving pjsip_dlg_inc_lock(), sess_count=%d", dlg->sess_count)); } /* Try to acquire dialog's mutex, but bail out if mutex can not be * acquired immediately. */ PJ_DEF(pj_status_t) pjsip_dlg_try_inc_lock(pjsip_dialog *dlg) { pj_status_t status; PJ_LOG(6,(dlg->obj_name,"Entering pjsip_dlg_try_inc_lock(), sess_count=%d", dlg->sess_count)); status = pj_mutex_trylock(dlg->mutex_); if (status != PJ_SUCCESS) { PJ_LOG(6,(dlg->obj_name, "pjsip_dlg_try_inc_lock() failed")); return status; } dlg->sess_count++; PJ_LOG(6,(dlg->obj_name, "Leaving pjsip_dlg_try_inc_lock(), sess_count=%d", dlg->sess_count)); return PJ_SUCCESS; } /* * Unlock dialog and decrement session counter. * It may delete the dialog! */ PJ_DEF(void) pjsip_dlg_dec_lock(pjsip_dialog *dlg) { PJ_ASSERT_ON_FAIL(dlg!=NULL, return); PJ_LOG(6,(dlg->obj_name, "Entering pjsip_dlg_dec_lock(), sess_count=%d", dlg->sess_count)); pj_assert(dlg->sess_count > 0); --dlg->sess_count; if (dlg->sess_count==0 && dlg->tsx_count==0) { pj_mutex_unlock(dlg->mutex_); pj_mutex_lock(dlg->mutex_); /* We are holding the dialog mutex here, so before we destroy * the dialog, make sure that we unlock it first to avoid * undefined behaviour on some platforms. See ticket #1886. */ unregister_and_destroy_dialog(dlg, PJ_TRUE); } else { pj_mutex_unlock(dlg->mutex_); } PJ_LOG(6,(THIS_FILE, "Leaving pjsip_dlg_dec_lock() (dlg=%p)", dlg)); } /* * Decrement session counter. */ PJ_DEF(pj_status_t) pjsip_dlg_dec_session( pjsip_dialog *dlg, pjsip_module *mod) { PJ_ASSERT_RETURN(dlg, PJ_EINVAL); pj_log_push_indent(); PJ_LOG(5,(dlg->obj_name, "Session count dec to %d by %.*s", dlg->sess_count-1, (int)mod->name.slen, mod->name.ptr)); pjsip_dlg_inc_lock(dlg); --dlg->sess_count; pjsip_dlg_dec_lock(dlg); pj_log_pop_indent(); return PJ_SUCCESS; } /* * Check if the module is registered as a usage */ PJ_DEF(pj_bool_t) pjsip_dlg_has_usage( pjsip_dialog *dlg, pjsip_module *mod) { unsigned index; pj_bool_t found = PJ_FALSE; pjsip_dlg_inc_lock(dlg); for (index=0; indexusage_cnt; ++index) { if (dlg->usage[index] == mod) { found = PJ_TRUE; break; } } pjsip_dlg_dec_lock(dlg); return found; } /* * Add usage. */ PJ_DEF(pj_status_t) pjsip_dlg_add_usage( pjsip_dialog *dlg, pjsip_module *mod, void *mod_data ) { unsigned index; PJ_ASSERT_RETURN(dlg && mod, PJ_EINVAL); PJ_ASSERT_RETURN(mod->id >= 0 && mod->id < PJSIP_MAX_MODULE, PJ_EINVAL); PJ_ASSERT_RETURN(dlg->usage_cnt < PJSIP_MAX_MODULE, PJ_EBUG); PJ_LOG(5,(dlg->obj_name, "Module %.*s added as dialog usage, data=%p", (int)mod->name.slen, mod->name.ptr, mod_data)); pjsip_dlg_inc_lock(dlg); /* Usages are sorted on priority, lowest number first. * Find position to put the new module, also makes sure that * this module has not been registered before. */ for (index=0; indexusage_cnt; ++index) { if (dlg->usage[index] == mod) { /* Module may be registered more than once in the same dialog. * For example, when call transfer fails, application may retry * call transfer on the same dialog. * So return PJ_SUCCESS here. */ PJ_LOG(4,(dlg->obj_name, "Module %.*s already registered as dialog usage, " "updating the data %p", (int)mod->name.slen, mod->name.ptr, mod_data)); dlg->mod_data[mod->id] = mod_data; pjsip_dlg_dec_lock(dlg); return PJ_SUCCESS; //pj_assert(!"This module is already registered"); //pjsip_dlg_dec_lock(dlg); //return PJSIP_ETYPEEXISTS; } if (dlg->usage[index]->priority > mod->priority) break; } /* index holds position to put the module. * Insert module at this index. */ pj_array_insert(dlg->usage, sizeof(dlg->usage[0]), dlg->usage_cnt, index, &mod); /* Set module data. */ dlg->mod_data[mod->id] = mod_data; /* Increment count. */ ++dlg->usage_cnt; pjsip_dlg_dec_lock(dlg); return PJ_SUCCESS; } /* * Attach module specific data to the dialog. Application can also set * the value directly by accessing dlg->mod_data[module_id]. */ PJ_DEF(pj_status_t) pjsip_dlg_set_mod_data( pjsip_dialog *dlg, int mod_id, void *data ) { PJ_ASSERT_RETURN(dlg, PJ_EINVAL); PJ_ASSERT_RETURN(mod_id >= 0 && mod_id < PJSIP_MAX_MODULE, PJ_EINVAL); dlg->mod_data[mod_id] = data; return PJ_SUCCESS; } /** * Get module specific data previously attached to the dialog. Application * can also get value directly by accessing dlg->mod_data[module_id]. */ PJ_DEF(void*) pjsip_dlg_get_mod_data( pjsip_dialog *dlg, int mod_id) { PJ_ASSERT_RETURN(dlg, NULL); PJ_ASSERT_RETURN(mod_id >= 0 && mod_id < PJSIP_MAX_MODULE, NULL); return dlg->mod_data[mod_id]; } /* * Create a new request within dialog (i.e. after the dialog session has been * established). The construction of such requests follows the rule in * RFC3261 section 12.2.1. */ static pj_status_t dlg_create_request_throw( pjsip_dialog *dlg, const pjsip_method *method, int cseq, pjsip_tx_data **p_tdata ) { pjsip_tx_data *tdata; pjsip_contact_hdr *contact; pjsip_route_hdr *route, *end_list; pj_status_t status; /* Contact Header field. * Contact can only be present in requests that establish dialog (in the * core SIP spec, only INVITE). */ if (pjsip_method_creates_dialog(method)) contact = dlg->local.contact; else contact = NULL; /* * Create the request by cloning from the headers in the * dialog. */ status = pjsip_endpt_create_request_from_hdr(dlg->endpt, method, dlg->target, dlg->local.info, dlg->remote.info, contact, dlg->call_id, cseq, NULL, &tdata); if (status != PJ_SUCCESS) return status; /* Just copy dialog route-set to Route header. * The transaction will do the processing as specified in Section 12.2.1 * of RFC 3261 in function tsx_process_route() in sip_transaction.c. */ route = dlg->route_set.next; end_list = &dlg->route_set; for (; route != end_list; route = route->next ) { pjsip_route_hdr *r; r = (pjsip_route_hdr*) pjsip_hdr_shallow_clone( tdata->pool, route ); pjsip_routing_hdr_set_route(r); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)r); } /* Copy authorization headers, if request is not ACK or CANCEL. */ if (method->id != PJSIP_ACK_METHOD && method->id != PJSIP_CANCEL_METHOD) { status = pjsip_auth_clt_init_req( &dlg->auth_sess, tdata ); if (status != PJ_SUCCESS) return status; } /* Done. */ *p_tdata = tdata; return PJ_SUCCESS; } /* * Create outgoing request. */ PJ_DEF(pj_status_t) pjsip_dlg_create_request( pjsip_dialog *dlg, const pjsip_method *method, int cseq, pjsip_tx_data **p_tdata) { pj_status_t status; pjsip_tx_data *tdata = NULL; PJ_USE_EXCEPTION; PJ_ASSERT_RETURN(dlg && method && p_tdata, PJ_EINVAL); /* Lock dialog. */ pjsip_dlg_inc_lock(dlg); /* Use outgoing CSeq and increment it by one. */ if (cseq < 0) cseq = dlg->local.cseq + 1; /* Keep compiler happy */ status = PJ_EBUG; /* Create the request. */ PJ_TRY { status = dlg_create_request_throw(dlg, method, cseq, &tdata); } PJ_CATCH_ANY { status = PJ_ENOMEM; } PJ_END; /* Failed! Delete transmit data. */ if (status != PJ_SUCCESS && tdata) { pjsip_tx_data_dec_ref( tdata ); tdata = NULL; } /* Unlock dialog. */ pjsip_dlg_dec_lock(dlg); *p_tdata = tdata; return status; } /* * Send request statefully, and update dialog'c CSeq. */ PJ_DEF(pj_status_t) pjsip_dlg_send_request( pjsip_dialog *dlg, pjsip_tx_data *tdata, int mod_data_id, void *mod_data) { pjsip_transaction *tsx; pjsip_msg *msg = tdata->msg; pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(dlg && tdata && tdata->msg, PJ_EINVAL); PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); pj_log_push_indent(); PJ_LOG(5,(dlg->obj_name, "Sending %s", pjsip_tx_data_get_info(tdata))); /* Lock and increment session */ pjsip_dlg_inc_lock(dlg); /* If via_addr is set, use this address for the Via header. */ if (dlg->via_addr.host.slen > 0) { tdata->via_addr = dlg->via_addr; tdata->via_tp = dlg->via_tp; } /* Update dialog's CSeq and message's CSeq if request is not * ACK nor CANCEL. */ if (msg->line.req.method.id != PJSIP_CANCEL_METHOD && msg->line.req.method.id != PJSIP_ACK_METHOD) { pjsip_cseq_hdr *ch; ch = PJSIP_MSG_CSEQ_HDR(msg); PJ_ASSERT_RETURN(ch!=NULL, PJ_EBUG); ch->cseq = dlg->local.cseq++; /* Force the whole message to be re-printed. */ pjsip_tx_data_invalidate_msg( tdata ); } /* Create a new transaction if method is not ACK. * The transaction user is the user agent module. */ if (msg->line.req.method.id != PJSIP_ACK_METHOD) { int tsx_count; status = pjsip_tsx_create_uac(dlg->ua, tdata, &tsx); if (status != PJ_SUCCESS) goto on_error; /* Set transport selector */ status = pjsip_tsx_set_transport(tsx, &dlg->tp_sel); pj_assert(status == PJ_SUCCESS); /* Attach this dialog to the transaction, so that user agent * will dispatch events to this dialog. */ tsx->mod_data[dlg->ua->id] = dlg; /* Copy optional caller's mod_data, if present */ if (mod_data_id >= 0 && mod_data_id < PJSIP_MAX_MODULE) tsx->mod_data[mod_data_id] = mod_data; /* Increment transaction counter. */ tsx_count = ++dlg->tsx_count; /* Send the message. */ status = pjsip_tsx_send_msg(tsx, tdata); if (status != PJ_SUCCESS) { if (dlg->tsx_count == tsx_count) pjsip_tsx_terminate(tsx, tsx->status_code); goto on_error; } } else { /* Set transport selector */ pjsip_tx_data_set_transport(tdata, &dlg->tp_sel); /* Send request */ status = pjsip_endpt_send_request_stateless(dlg->endpt, tdata, NULL, NULL); if (status != PJ_SUCCESS) goto on_error; } /* Unlock dialog, may destroy dialog. */ pjsip_dlg_dec_lock(dlg); pj_log_pop_indent(); return PJ_SUCCESS; on_error: /* Unlock dialog, may destroy dialog. */ pjsip_dlg_dec_lock(dlg); /* Whatever happen delete the message. */ pjsip_tx_data_dec_ref( tdata ); pj_log_pop_indent(); return status; } /* Add standard headers for certain types of response */ static void dlg_beautify_response(pjsip_dialog *dlg, pj_bool_t add_headers, int st_code, pjsip_tx_data *tdata) { pjsip_cseq_hdr *cseq; int st_class; const pjsip_hdr *c_hdr; pjsip_hdr *hdr; cseq = PJSIP_MSG_CSEQ_HDR(tdata->msg); pj_assert(cseq != NULL); st_class = st_code / 100; /* Contact, Allow, Supported header. */ if (add_headers && pjsip_method_creates_dialog(&cseq->method)) { /* Add Contact header for 1xx, 2xx, 3xx and 485 response. */ if (st_class==2 || st_class==3 || (st_class==1 && st_code != 100) || st_code==485) { /* Add contact header only if one is not present. */ if (pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL) == 0 && pjsip_msg_find_hdr_by_name(tdata->msg, &HCONTACT, NULL) == 0) { hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, dlg->local.contact); pjsip_msg_add_hdr(tdata->msg, hdr); } } /* Add Allow header in 18x, 2xx and 405 response. */ if ((((st_code/10==18 || st_class==2) && dlg->add_allow) || st_code==405) && pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ALLOW, NULL)==NULL) { c_hdr = pjsip_endpt_get_capability(dlg->endpt, PJSIP_H_ALLOW, NULL); if (c_hdr) { hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, c_hdr); pjsip_msg_add_hdr(tdata->msg, hdr); } } /* Add Supported header in 2xx response. */ if (st_class==2 && pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL)==NULL) { c_hdr = pjsip_endpt_get_capability(dlg->endpt, PJSIP_H_SUPPORTED, NULL); if (c_hdr) { hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, c_hdr); pjsip_msg_add_hdr(tdata->msg, hdr); } } } /* Add To tag in all responses except 100 */ if (st_code != 100) { pjsip_to_hdr *to; to = PJSIP_MSG_TO_HDR(tdata->msg); pj_assert(to != NULL); to->tag = dlg->local.info->tag; if (dlg->state == PJSIP_DIALOG_STATE_NULL) dlg->state = PJSIP_DIALOG_STATE_ESTABLISHED; } } /* * Create response. */ PJ_DEF(pj_status_t) pjsip_dlg_create_response( pjsip_dialog *dlg, pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, pjsip_tx_data **p_tdata) { pj_status_t status; pjsip_tx_data *tdata; /* Create generic response. * This will initialize response's Via, To, From, Call-ID, CSeq * and Record-Route headers from the request. */ status = pjsip_endpt_create_response(dlg->endpt, rdata, st_code, st_text, &tdata); if (status != PJ_SUCCESS) return status; /* Lock the dialog. */ pjsip_dlg_inc_lock(dlg); dlg_beautify_response(dlg, PJ_FALSE, st_code, tdata); /* Unlock the dialog. */ pjsip_dlg_dec_lock(dlg); /* Done. */ *p_tdata = tdata; return PJ_SUCCESS; } /* * Modify response. */ PJ_DEF(pj_status_t) pjsip_dlg_modify_response( pjsip_dialog *dlg, pjsip_tx_data *tdata, int st_code, const pj_str_t *st_text) { pjsip_hdr *hdr; PJ_ASSERT_RETURN(dlg && tdata && tdata->msg, PJ_EINVAL); PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_RESPONSE_MSG, PJSIP_ENOTRESPONSEMSG); PJ_ASSERT_RETURN(st_code >= 100 && st_code <= 699, PJ_EINVAL); /* Lock and increment session */ pjsip_dlg_inc_lock(dlg); /* Replace status code and reason */ tdata->msg->line.status.code = st_code; if (st_text) { pj_strdup(tdata->pool, &tdata->msg->line.status.reason, st_text); } else { tdata->msg->line.status.reason = *pjsip_get_status_text(st_code); } /* Remove existing Contact header (without this, when dialog sent * 180 and then 302, the Contact in 302 will not get updated). */ hdr = (pjsip_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL); if (hdr) pj_list_erase(hdr); /* Add tag etc. if necessary */ dlg_beautify_response(dlg, st_code/100 <= 2, st_code, tdata); /* Must add reference counter, since tsx_send_msg() will decrement it */ pjsip_tx_data_add_ref(tdata); /* Force to re-print message. */ pjsip_tx_data_invalidate_msg(tdata); /* Unlock dialog and dec session, may destroy dialog. */ pjsip_dlg_dec_lock(dlg); return PJ_SUCCESS; } /* * Send response statefully. */ PJ_DEF(pj_status_t) pjsip_dlg_send_response( pjsip_dialog *dlg, pjsip_transaction *tsx, pjsip_tx_data *tdata) { pj_status_t status; /* Sanity check. */ PJ_ASSERT_RETURN(dlg && tsx && tdata && tdata->msg, PJ_EINVAL); PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_RESPONSE_MSG, PJSIP_ENOTRESPONSEMSG); /* The transaction must belong to this dialog. */ PJ_ASSERT_RETURN(tsx->mod_data[dlg->ua->id] == dlg, PJ_EINVALIDOP); pj_log_push_indent(); PJ_LOG(5,(dlg->obj_name, "Sending %s", pjsip_tx_data_get_info(tdata))); /* Check that transaction method and cseq match the response. * This operation is sloooww (search CSeq header twice), that's why * we only do it in debug mode. */ #if defined(PJ_DEBUG) && PJ_DEBUG!=0 PJ_ASSERT_RETURN( PJSIP_MSG_CSEQ_HDR(tdata->msg)->cseq == tsx->cseq && pjsip_method_cmp(&PJSIP_MSG_CSEQ_HDR(tdata->msg)->method, &tsx->method)==0, PJ_EINVALIDOP); #endif /* Must acquire dialog first, to prevent deadlock */ pjsip_dlg_inc_lock(dlg); /* Last chance to add mandatory headers before the response is * sent. */ dlg_beautify_response(dlg, PJ_TRUE, tdata->msg->line.status.code, tdata); /* If the dialog is locked to transport, make sure that transaction * is locked to the same transport too. */ if (dlg->tp_sel.type != tsx->tp_sel.type || dlg->tp_sel.u.ptr != tsx->tp_sel.u.ptr) { status = pjsip_tsx_set_transport(tsx, &dlg->tp_sel); pj_assert(status == PJ_SUCCESS); } /* Ask transaction to send the response */ status = pjsip_tsx_send_msg(tsx, tdata); /* This function must decrement transmit data request counter * regardless of the operation status. The transaction only * decrements the counter if the operation is successful. */ if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); } pjsip_dlg_dec_lock(dlg); pj_log_pop_indent(); return status; } /* * Combo function to create and send response statefully. */ PJ_DEF(pj_status_t) pjsip_dlg_respond( pjsip_dialog *dlg, pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, const pjsip_hdr *hdr_list, const pjsip_msg_body *body ) { pj_status_t status; pjsip_tx_data *tdata; /* Sanity check. */ PJ_ASSERT_RETURN(dlg && rdata && rdata->msg_info.msg, PJ_EINVAL); PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* The transaction must belong to this dialog. */ PJ_ASSERT_RETURN(pjsip_rdata_get_tsx(rdata) && pjsip_rdata_get_tsx(rdata)->mod_data[dlg->ua->id] == dlg, PJ_EINVALIDOP); /* Create the response. */ status = pjsip_dlg_create_response(dlg, rdata, st_code, st_text, &tdata); if (status != PJ_SUCCESS) return status; /* Add additional header, if any */ if (hdr_list) { const pjsip_hdr *hdr; hdr = hdr_list->next; while (hdr != hdr_list) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr)); hdr = hdr->next; } } /* Add the message body, if any. */ if (body) { tdata->msg->body = pjsip_msg_body_clone( tdata->pool, body); } /* Send the response. */ return pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata); } /* This function is called by user agent upon receiving incoming request * message. */ void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata ) { pj_status_t status; pjsip_transaction *tsx = NULL; pj_bool_t processed = PJ_FALSE; unsigned i; PJ_LOG(5,(dlg->obj_name, "Received %s", pjsip_rx_data_get_info(rdata))); pj_log_push_indent(); /* Lock dialog and increment session. */ pjsip_dlg_inc_lock(dlg); /* Check CSeq */ if (rdata->msg_info.cseq->cseq <= dlg->remote.cseq && rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD && rdata->msg_info.msg->line.req.method.id != PJSIP_CANCEL_METHOD) { /* Invalid CSeq. * Respond statelessly with 500 (Internal Server Error) */ pj_str_t warn_text; /* Unlock dialog and dec session, may destroy dialog. */ pjsip_dlg_dec_lock(dlg); pj_assert(pjsip_rdata_get_tsx(rdata) == NULL); warn_text = pj_str("Invalid CSeq"); pjsip_endpt_respond_stateless(dlg->endpt, rdata, 500, &warn_text, NULL, NULL); pj_log_pop_indent(); return; } /* Update CSeq. */ dlg->remote.cseq = rdata->msg_info.cseq->cseq; /* Update To tag if necessary. * This only happens if UAS sends a new request before answering * our request (e.g. UAS sends NOTIFY before answering our * SUBSCRIBE request). */ if (dlg->remote.info->tag.slen == 0) { pj_strdup(dlg->pool, &dlg->remote.info->tag, &rdata->msg_info.from->tag); } /* Create UAS transaction for this request. */ if (pjsip_rdata_get_tsx(rdata) == NULL && rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { status = pjsip_tsx_create_uas(dlg->ua, rdata, &tsx); if (status != PJ_SUCCESS) { /* Once case for this is when re-INVITE contains same * Via branch value as previous INVITE (ticket #965). */ char errmsg[PJ_ERR_MSG_SIZE]; pj_str_t reason; reason = pj_strerror(status, errmsg, sizeof(errmsg)); pjsip_endpt_respond_stateless(dlg->endpt, rdata, 500, &reason, NULL, NULL); goto on_return; } /* Put this dialog in the transaction data. */ tsx->mod_data[dlg->ua->id] = dlg; /* Add transaction count. */ ++dlg->tsx_count; } /* Update the target URI if this is a target refresh request. * We have passed the basic checking for the request, I think we * should update the target URI regardless of whether the request * is accepted or not (e.g. when re-INVITE is answered with 488, * we would still need to update the target URI, otherwise our * target URI would be wrong, wouldn't it). */ if (pjsip_method_creates_dialog(&rdata->msg_info.cseq->method)) { pjsip_contact_hdr *contact; contact = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); if (contact && contact->uri && (dlg->remote.contact==NULL || pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) { dlg->remote.contact = (pjsip_contact_hdr*) pjsip_hdr_clone(dlg->pool, contact); dlg->target = dlg->remote.contact->uri; } } /* Report the request to dialog usages. */ for (i=0; iusage_cnt; ++i) { if (!dlg->usage[i]->on_rx_request) continue; processed = (*dlg->usage[i]->on_rx_request)(rdata); if (processed) break; } /* Feed the first request to the transaction. */ if (tsx) pjsip_tsx_recv_msg(tsx, rdata); /* If no dialog usages has claimed the processing of the transaction, * and if transaction has not sent final response, respond with * 500/Internal Server Error. */ if (!processed && tsx && tsx->status_code < 200) { pjsip_tx_data *tdata; const pj_str_t reason = { "Unhandled by dialog usages", 26}; PJ_LOG(4,(tsx->obj_name, "%s was unhandled by " "dialog usages, sending 500 response", pjsip_rx_data_get_info(rdata))); status = pjsip_dlg_create_response(dlg, rdata, 500, &reason, &tdata); if (status == PJ_SUCCESS) { status = pjsip_dlg_send_response(dlg, tsx, tdata); } } on_return: /* Unlock dialog and dec session, may destroy dialog. */ pjsip_dlg_dec_lock(dlg); pj_log_pop_indent(); } /* Update route-set from incoming message */ static void dlg_update_routeset(pjsip_dialog *dlg, const pjsip_rx_data *rdata) { const pjsip_hdr *hdr, *end_hdr; //pj_int32_t msg_cseq; const pjsip_msg *msg; const pjsip_method update = { PJSIP_OTHER_METHOD, {"UPDATE", 6}}; msg = rdata->msg_info.msg; //msg_cseq = rdata->msg_info.cseq->cseq; /* Ignore if route set has been frozen */ if (dlg->route_set_frozen) return; /* Ignore if the message is an UPDATE response (see ticket #1781) */ if (pjsip_method_cmp(&rdata->msg_info.cseq->method, &update) == 0) return; /* Only update route set if this message belongs to the same * transaction as the initial transaction that establishes dialog. */ if (dlg->role == PJSIP_ROLE_UAC) { /* Ignore subsequent request from remote */ if (msg->type != PJSIP_RESPONSE_MSG) return; /* Ignore subsequent responses with higher CSeq than initial CSeq. * Unfortunately this would be broken when the first request is * challenged! */ //if (msg_cseq != dlg->local.first_cseq) // return; } else { /* For callee dialog, route set should have been set by initial * request and it will have been rejected by dlg->route_set_frozen * check above. */ pj_assert(!"Should not happen"); } /* Based on the checks above, we should only get response message here */ pj_assert(msg->type == PJSIP_RESPONSE_MSG); /* Ignore if this is not 1xx or 2xx response */ if (msg->line.status.code >= 300) return; /* Reset route set */ pj_list_init(&dlg->route_set); /* Update route set */ end_hdr = &msg->hdr; for (hdr=msg->hdr.prev; hdr!=end_hdr; hdr=hdr->prev) { if (hdr->type == PJSIP_H_RECORD_ROUTE) { pjsip_route_hdr *r; r = (pjsip_route_hdr*) pjsip_hdr_clone(dlg->pool, hdr); pjsip_routing_hdr_set_route(r); pj_list_push_back(&dlg->route_set, r); } } PJ_LOG(5,(dlg->obj_name, "Route-set updated")); /* Freeze the route set only when the route set comes in 2xx response. * If it is in 1xx response, prepare to recompute the route set when * the 2xx response comes in. * * There is a debate whether route set should be frozen when the dialog * is established with reliable provisional response, but I think * it is safer to not freeze the route set (thus recompute the route set * upon receiving 2xx response). Also RFC 3261 says so in 13.2.2.4. * * The pjsip_method_creates_dialog() check protects from wrongly * freezing the route set upon receiving 200/OK response for PRACK. */ if (pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) && PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200)) { dlg->route_set_frozen = PJ_TRUE; PJ_LOG(5,(dlg->obj_name, "Route-set frozen")); } } /* This function is called by user agent upon receiving incoming response * message. */ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ) { unsigned i; int res_code; PJ_LOG(5,(dlg->obj_name, "Received %s", pjsip_rx_data_get_info(rdata))); pj_log_push_indent(); /* Lock the dialog and inc session. */ pjsip_dlg_inc_lock(dlg); /* Check that rdata already has dialog in mod_data. */ pj_assert(pjsip_rdata_get_dlg(rdata) == dlg); /* Keep the response's status code */ res_code = rdata->msg_info.msg->line.status.code; /* When we receive response that establishes dialog, update To tag, * route set and dialog target. * * The second condition of the "if" is a workaround for forking. * Originally, the dialog takes the first To tag seen and set it as * the remote tag. If the tag in 2xx response is different than this * tag, ACK will be sent with wrong To tag and incoming request with * this tag will be rejected with 481. * * The workaround for this is to take the To tag received in the * 2xx response and set it as remote tag. * * New update: * We also need to update the dialog for 1xx responses, to handle the * case when 100rel is used, otherwise PRACK will be sent to the * wrong target. */ if ((dlg->state == PJSIP_DIALOG_STATE_NULL && pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) && (res_code > 100 && res_code < 300) && rdata->msg_info.to->tag.slen) || (dlg->role==PJSIP_ROLE_UAC && !dlg->uac_has_2xx && res_code > 100 && res_code/100 <= 2 && pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) && pj_stricmp(&dlg->remote.info->tag, &rdata->msg_info.to->tag))) { pjsip_contact_hdr *contact; /* Update remote capability info, when To tags in the dialog remote * info and the incoming response are different, e.g: first response * with To-tag or forking, apply strict update. */ pjsip_dlg_update_remote_cap(dlg, rdata->msg_info.msg, pj_stricmp(&dlg->remote.info->tag, &rdata->msg_info.to->tag)); /* Update To tag. */ pj_strdup(dlg->pool, &dlg->remote.info->tag, &rdata->msg_info.to->tag); /* No need to update remote's tag_hval since its never used. */ /* RFC 3271 Section 12.1.2: * The route set MUST be set to the list of URIs in the Record-Route * header field from the response, taken in reverse order and * preserving all URI parameters. If no Record-Route header field * is present in the response, the route set MUST be set to the * empty set. This route set, even if empty, overrides any pre-existing * route set for future requests in this dialog. */ dlg_update_routeset(dlg, rdata); /* The remote target MUST be set to the URI from the Contact header * field of the response. */ contact = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); if (contact && contact->uri && (dlg->remote.contact==NULL || pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) { dlg->remote.contact = (pjsip_contact_hdr*) pjsip_hdr_clone(dlg->pool, contact); dlg->target = dlg->remote.contact->uri; } dlg->state = PJSIP_DIALOG_STATE_ESTABLISHED; /* Prevent dialog from being updated just in case more 2xx * gets through this dialog (it shouldn't happen). */ if (dlg->role==PJSIP_ROLE_UAC && !dlg->uac_has_2xx && res_code/100==2) { dlg->uac_has_2xx = PJ_TRUE; } } /* Update remote target (again) when receiving 2xx response messages * that's defined as target refresh. * * Also upon receiving 2xx response, recheck again the route set. * This is for compatibility with RFC 2543, as described in Section * 13.2.2.4 of RFC 3261: If the dialog identifier in the 2xx response matches the dialog identifier of an existing dialog, the dialog MUST be transitioned to the "confirmed" state, and the route set for the dialog MUST be recomputed based on the 2xx response using the procedures of Section 12.2.1.2. Note that the only piece of state that is recomputed is the route set. Other pieces of state such as the highest sequence numbers (remote and local) sent within the dialog are not recomputed. The route set only is recomputed for backwards compatibility. RFC 2543 did not mandate mirroring of the Record-Route header field in a 1xx, only 2xx. */ if (pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) && res_code/100 == 2) { pjsip_contact_hdr *contact; contact = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); if (contact && contact->uri && (dlg->remote.contact==NULL || pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, dlg->remote.contact->uri, contact->uri))) { dlg->remote.contact = (pjsip_contact_hdr*) pjsip_hdr_clone(dlg->pool, contact); dlg->target = dlg->remote.contact->uri; } dlg_update_routeset(dlg, rdata); /* Update remote capability info after the first 2xx response * (ticket #1539). Note that the remote capability retrieved here * will be assumed to remain unchanged for the duration of the dialog. */ if (dlg->role==PJSIP_ROLE_UAC && !dlg->uac_has_2xx) { pjsip_dlg_update_remote_cap(dlg, rdata->msg_info.msg, PJ_FALSE); dlg->uac_has_2xx = PJ_TRUE; } } /* Pass to dialog usages. */ for (i=0; iusage_cnt; ++i) { pj_bool_t processed; if (!dlg->usage[i]->on_rx_response) continue; processed = (*dlg->usage[i]->on_rx_response)(rdata); if (processed) break; } /* Handle the case of forked response, when the application creates * the forked dialog but not the invite session. In this case, the * forked 200/OK response will be unhandled, and we must send ACK * here. */ if (dlg->usage_cnt==0) { pj_status_t status; if (rdata->msg_info.cseq->method.id==PJSIP_INVITE_METHOD && rdata->msg_info.msg->line.status.code/100 == 2) { pjsip_tx_data *ack; status = pjsip_dlg_create_request(dlg, &pjsip_ack_method, rdata->msg_info.cseq->cseq, &ack); if (status == PJ_SUCCESS) status = pjsip_dlg_send_request(dlg, ack, -1, NULL); } else if (rdata->msg_info.msg->line.status.code==401 || rdata->msg_info.msg->line.status.code==407) { pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); pjsip_tx_data *tdata; status = pjsip_auth_clt_reinit_req( &dlg->auth_sess, rdata, tsx->last_tx, &tdata); if (status == PJ_SUCCESS) { /* Re-send request. */ status = pjsip_dlg_send_request(dlg, tdata, -1, NULL); } } } /* Unhandled response does not necessarily mean error because dialog usages may choose to process the transaction state instead. if (i==dlg->usage_cnt) { PJ_LOG(4,(dlg->obj_name, "%s was not claimed by any dialog usages", pjsip_rx_data_get_info(rdata))); } */ /* Unlock dialog and dec session, may destroy dialog. */ pjsip_dlg_dec_lock(dlg); pj_log_pop_indent(); } /* This function is called by user agent upon receiving transaction * state notification. */ void pjsip_dlg_on_tsx_state( pjsip_dialog *dlg, pjsip_transaction *tsx, pjsip_event *e ) { unsigned i; PJ_LOG(5,(dlg->obj_name, "Transaction %s state changed to %s", tsx->obj_name, pjsip_tsx_state_str(tsx->state))); pj_log_push_indent(); /* Lock the dialog and increment session. */ pjsip_dlg_inc_lock(dlg); /* Pass to dialog usages. */ for (i=0; iusage_cnt; ++i) { if (!dlg->usage[i]->on_tsx_state) continue; (*dlg->usage[i]->on_tsx_state)(tsx, e); } /* It is possible that the transaction is terminated and this function * is called while we're calling on_tsx_state(). So only decrement * the tsx_count if we're still attached to the transaction. */ if (tsx->state == PJSIP_TSX_STATE_TERMINATED && tsx->mod_data[dlg->ua->id] == dlg) { pj_assert(dlg->tsx_count>0); --dlg->tsx_count; tsx->mod_data[dlg->ua->id] = NULL; } /* Unlock dialog and dec session, may destroy dialog. */ pjsip_dlg_dec_lock(dlg); pj_log_pop_indent(); } /* * Check if the specified capability is supported by remote. */ PJ_DEF(pjsip_dialog_cap_status) pjsip_dlg_remote_has_cap( pjsip_dialog *dlg, int htype, const pj_str_t *hname, const pj_str_t *token) { const pjsip_generic_array_hdr *hdr; pjsip_dialog_cap_status cap_status = PJSIP_DIALOG_CAP_UNSUPPORTED; unsigned i; PJ_ASSERT_RETURN(dlg && token, PJSIP_DIALOG_CAP_UNKNOWN); pjsip_dlg_inc_lock(dlg); hdr = (const pjsip_generic_array_hdr*) pjsip_dlg_get_remote_cap_hdr(dlg, htype, hname); if (!hdr) { cap_status = PJSIP_DIALOG_CAP_UNKNOWN; } else { for (i=0; icount; ++i) { if (!pj_stricmp(&hdr->values[i], token)) { cap_status = PJSIP_DIALOG_CAP_SUPPORTED; break; } } } pjsip_dlg_dec_lock(dlg); return cap_status; } /* * Update remote capability of ACCEPT, ALLOW, and SUPPORTED from * the received message. */ PJ_DEF(pj_status_t) pjsip_dlg_update_remote_cap(pjsip_dialog *dlg, const pjsip_msg *msg, pj_bool_t strict) { pjsip_hdr_e htypes[] = { PJSIP_H_ACCEPT, PJSIP_H_ALLOW, PJSIP_H_SUPPORTED }; unsigned i; PJ_ASSERT_RETURN(dlg && msg, PJ_EINVAL); pjsip_dlg_inc_lock(dlg); /* Retrieve all specified capability header types */ for (i = 0; i < PJ_ARRAY_SIZE(htypes); ++i) { const pjsip_generic_array_hdr *hdr; pj_status_t status; /* Find this capability type in the message */ hdr = (const pjsip_generic_array_hdr*) pjsip_msg_find_hdr(msg, htypes[i], NULL); if (!hdr) { /* Not found. * If strict update is specified, remote this capability type * from the capability list. */ if (strict) pjsip_dlg_remove_remote_cap_hdr(dlg, htypes[i], NULL); } else { /* Found, a capability type may be specified in multiple headers, * so combine all the capability tags/values into a temporary * header. */ pjsip_generic_array_hdr tmp_hdr; /* Init temporary header */ pjsip_generic_array_hdr_init(dlg->pool, &tmp_hdr, NULL); pj_memcpy(&tmp_hdr, hdr, sizeof(pjsip_hdr)); while (hdr) { unsigned j; /* Append the header content to temporary header */ for(j=0; jcount && tmp_hdr.countvalues[j]; } /* Get the next header for this capability */ hdr = (const pjsip_generic_array_hdr*) pjsip_msg_find_hdr(msg, htypes[i], hdr->next); } /* Save this capability */ status = pjsip_dlg_set_remote_cap_hdr(dlg, &tmp_hdr); if (status != PJ_SUCCESS) { pjsip_dlg_dec_lock(dlg); return status; } } } pjsip_dlg_dec_lock(dlg); return PJ_SUCCESS; } /* * Get the value of the specified capability header field of remote. */ PJ_DEF(const pjsip_hdr*) pjsip_dlg_get_remote_cap_hdr(pjsip_dialog *dlg, int htype, const pj_str_t *hname) { pjsip_hdr *hdr; /* Check arguments. */ PJ_ASSERT_RETURN(dlg, NULL); PJ_ASSERT_RETURN((htype != PJSIP_H_OTHER) || (hname && hname->slen), NULL); pjsip_dlg_inc_lock(dlg); hdr = dlg->rem_cap_hdr.next; while (hdr != &dlg->rem_cap_hdr) { if ((htype != PJSIP_H_OTHER && htype == hdr->type) || (htype == PJSIP_H_OTHER && pj_stricmp(&hdr->name, hname) == 0)) { pjsip_dlg_dec_lock(dlg); return hdr; } hdr = hdr->next; } pjsip_dlg_dec_lock(dlg); return NULL; } /* * Set remote capability header from a SIP header containing array * of capability tags/values. */ PJ_DEF(pj_status_t) pjsip_dlg_set_remote_cap_hdr( pjsip_dialog *dlg, const pjsip_generic_array_hdr *cap_hdr) { pjsip_generic_array_hdr *hdr; /* Check arguments. */ PJ_ASSERT_RETURN(dlg && cap_hdr, PJ_EINVAL); pjsip_dlg_inc_lock(dlg); /* Find the header. */ hdr = (pjsip_generic_array_hdr*) pjsip_dlg_get_remote_cap_hdr(dlg, cap_hdr->type, &cap_hdr->name); /* Quick compare if the capability is up to date */ if (hdr && hdr->count == cap_hdr->count) { unsigned i; pj_bool_t uptodate = PJ_TRUE; for (i=0; icount; ++i) { if (pj_stricmp(&hdr->values[i], &cap_hdr->values[i])) uptodate = PJ_FALSE; } /* Capability is up to date, just return PJ_SUCCESS */ if (uptodate) { pjsip_dlg_dec_lock(dlg); return PJ_SUCCESS; } } /* Remove existing capability header if any */ if (hdr) pj_list_erase(hdr); /* Add the new capability header */ hdr = (pjsip_generic_array_hdr*) pjsip_hdr_clone(dlg->pool, cap_hdr); hdr->type = cap_hdr->type; pj_strdup(dlg->pool, &hdr->name, &cap_hdr->name); pj_list_push_back(&dlg->rem_cap_hdr, hdr); pjsip_dlg_dec_lock(dlg); /* Done. */ return PJ_SUCCESS; } /* * Remove a remote capability header. */ PJ_DEF(pj_status_t) pjsip_dlg_remove_remote_cap_hdr(pjsip_dialog *dlg, int htype, const pj_str_t *hname) { pjsip_generic_array_hdr *hdr; /* Check arguments. */ PJ_ASSERT_RETURN(dlg, PJ_EINVAL); PJ_ASSERT_RETURN((htype != PJSIP_H_OTHER) || (hname && hname->slen), PJ_EINVAL); pjsip_dlg_inc_lock(dlg); hdr = (pjsip_generic_array_hdr*) pjsip_dlg_get_remote_cap_hdr(dlg, htype, hname); if (!hdr) { pjsip_dlg_dec_lock(dlg); return PJ_ENOTFOUND; } pj_list_erase(hdr); pjsip_dlg_dec_lock(dlg); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_dialog_wrap.cpp ================================================ /* $Id: sip_dialog_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This file is a C++ wrapper, see ticket #886 for details. */ #include "sip_dialog.c" ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_endpoint.c ================================================ /* $Id: sip_endpoint.c 4529 2013-05-30 08:32:07Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PJSIP_EX_NO_MEMORY pj_NO_MEMORY_EXCEPTION() #define THIS_FILE "sip_endpoint.c" #define MAX_METHODS 32 /* List of SIP endpoint exit callback. */ typedef struct exit_cb { PJ_DECL_LIST_MEMBER (struct exit_cb); pjsip_endpt_exit_callback func; } exit_cb; /** * The SIP endpoint. */ struct pjsip_endpoint { /** Pool to allocate memory for the endpoint. */ pj_pool_t *pool; /** Mutex for the pool, hash table, and event list/queue. */ pj_mutex_t *mutex; /** Pool factory. */ pj_pool_factory *pf; /** Name. */ pj_str_t name; /** Timer heap. */ pj_timer_heap_t *timer_heap; /** Transport manager. */ pjsip_tpmgr *transport_mgr; /** Ioqueue. */ pj_ioqueue_t *ioqueue; /** Last ioqueue err */ pj_status_t ioq_last_err; /** DNS Resolver. */ pjsip_resolver_t *resolver; /** Modules lock. */ pj_rwmutex_t *mod_mutex; /** Modules. */ pjsip_module *modules[PJSIP_MAX_MODULE]; /** Module list, sorted by priority. */ pjsip_module module_list; /** Capability header list. */ pjsip_hdr cap_hdr; /** Additional request headers. */ pjsip_hdr req_hdr; /** List of exit callback. */ exit_cb exit_cb_list; }; #if defined(PJSIP_SAFE_MODULE) && PJSIP_SAFE_MODULE!=0 # define LOCK_MODULE_ACCESS(ept) pj_rwmutex_lock_read(ept->mod_mutex) # define UNLOCK_MODULE_ACCESS(ept) pj_rwmutex_unlock_read(ept->mod_mutex) #else # define LOCK_MODULE_ACCESS(endpt) # define UNLOCK_MODULE_ACCESS(endpt) #endif /* * Prototypes. */ static void endpt_on_rx_msg( pjsip_endpoint*, pj_status_t, pjsip_rx_data*); static pj_status_t endpt_on_tx_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata ); static pj_status_t unload_module(pjsip_endpoint *endpt, pjsip_module *mod); /* Defined in sip_parser.c */ void init_sip_parser(void); void deinit_sip_parser(void); /* Defined in sip_tel_uri.c */ pj_status_t pjsip_tel_uri_subsys_init(void); /* * This is the global handler for memory allocation failure, for pools that * are created by the endpoint (by default, all pools ARE allocated by * endpoint). The error is handled by throwing exception, and hopefully, * the exception will be handled by the application (or this library). */ static void pool_callback( pj_pool_t *pool, pj_size_t size ) { PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(size); PJ_THROW(PJSIP_EX_NO_MEMORY); } /* Compare module name, used for searching module based on name. */ static int cmp_mod_name(void *name, const void *mod) { return pj_stricmp((const pj_str_t*)name, &((pjsip_module*)mod)->name); } /* * Register new module to the endpoint. * The endpoint will then call the load and start function in the module to * properly initialize the module, and assign a unique module ID for the * module. */ PJ_DEF(pj_status_t) pjsip_endpt_register_module( pjsip_endpoint *endpt, pjsip_module *mod ) { pj_status_t status = PJ_SUCCESS; pjsip_module *m; unsigned i; pj_rwmutex_lock_write(endpt->mod_mutex); /* Make sure that this module has not been registered. */ PJ_ASSERT_ON_FAIL( pj_list_find_node(&endpt->module_list, mod) == NULL, {status = PJ_EEXISTS; goto on_return;}); /* Make sure that no module with the same name has been registered. */ PJ_ASSERT_ON_FAIL( pj_list_search(&endpt->module_list, &mod->name, &cmp_mod_name)==NULL, {status = PJ_EEXISTS; goto on_return; }); /* Find unused ID for this module. */ for (i=0; imodules); ++i) { if (endpt->modules[i] == NULL) break; } if (i == PJ_ARRAY_SIZE(endpt->modules)) { pj_assert(!"Too many modules registered!"); status = PJ_ETOOMANY; goto on_return; } /* Assign the ID. */ mod->id = i; /* Try to load the module. */ if (mod->load) { status = (*mod->load)(endpt); if (status != PJ_SUCCESS) goto on_return; } /* Try to start the module. */ if (mod->start) { status = (*mod->start)(); if (status != PJ_SUCCESS) goto on_return; } /* Save the module. */ endpt->modules[i] = mod; /* Put in the module list, sorted by priority. */ m = endpt->module_list.next; while (m != &endpt->module_list) { if (m->priority > mod->priority) break; m = m->next; } pj_list_insert_before(m, mod); /* Done. */ PJ_LOG(4,(THIS_FILE, "Module \"%.*s\" registered", (int)mod->name.slen, mod->name.ptr)); on_return: pj_rwmutex_unlock_write(endpt->mod_mutex); return status; } /* * Unregister a module from the endpoint. * The endpoint will then call the stop and unload function in the module to * properly shutdown the module. */ PJ_DEF(pj_status_t) pjsip_endpt_unregister_module( pjsip_endpoint *endpt, pjsip_module *mod ) { pj_status_t status; pj_rwmutex_lock_write(endpt->mod_mutex); /* Make sure the module exists in the list. */ PJ_ASSERT_ON_FAIL( pj_list_find_node(&endpt->module_list, mod) == mod, {status = PJ_ENOTFOUND;goto on_return;} ); /* Make sure the module exists in the array. */ PJ_ASSERT_ON_FAIL( mod->id>=0 && mod->id<(int)PJ_ARRAY_SIZE(endpt->modules) && endpt->modules[mod->id] == mod, {status = PJ_ENOTFOUND; goto on_return;}); /* Try to stop the module. */ if (mod->stop) { status = (*mod->stop)(); if (status != PJ_SUCCESS) goto on_return; } /* Unload module */ status = unload_module(endpt, mod); on_return: pj_rwmutex_unlock_write(endpt->mod_mutex); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(3,(THIS_FILE, "Module \"%.*s\" can not be unregistered: %s", (int)mod->name.slen, mod->name.ptr, errmsg)); } return status; } static pj_status_t unload_module(pjsip_endpoint *endpt, pjsip_module *mod) { pj_status_t status; /* Try to unload the module. */ if (mod->unload) { status = (*mod->unload)(); if (status != PJ_SUCCESS) return status; } /* Module MUST NOT set module ID to -1. */ pj_assert(mod->id >= 0); /* Remove module from array. */ endpt->modules[mod->id] = NULL; /* Remove module from list. */ pj_list_erase(mod); /* Set module Id to -1. */ mod->id = -1; /* Done. */ status = PJ_SUCCESS; PJ_LOG(4,(THIS_FILE, "Module \"%.*s\" unregistered", (int)mod->name.slen, mod->name.ptr)); return status; } /* * Get the value of the specified capability header field. */ PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_capability( pjsip_endpoint *endpt, int htype, const pj_str_t *hname) { pjsip_hdr *hdr = endpt->cap_hdr.next; /* Check arguments. */ PJ_ASSERT_RETURN(endpt != NULL, NULL); PJ_ASSERT_RETURN(htype != PJSIP_H_OTHER || hname, NULL); if (htype != PJSIP_H_OTHER) { while (hdr != &endpt->cap_hdr) { if (hdr->type == htype) return hdr; hdr = hdr->next; } } return NULL; } /* * Check if the specified capability is supported. */ PJ_DEF(pj_bool_t) pjsip_endpt_has_capability( pjsip_endpoint *endpt, int htype, const pj_str_t *hname, const pj_str_t *token) { const pjsip_generic_array_hdr *hdr; unsigned i; hdr = (const pjsip_generic_array_hdr*) pjsip_endpt_get_capability(endpt, htype, hname); if (!hdr) return PJ_FALSE; PJ_ASSERT_RETURN(token != NULL, PJ_FALSE); for (i=0; icount; ++i) { if (!pj_stricmp(&hdr->values[i], token)) return PJ_TRUE; } return PJ_FALSE; } /* * Add or register new capabilities as indicated by the tags to the * appropriate header fields in the endpoint. */ PJ_DEF(pj_status_t) pjsip_endpt_add_capability( pjsip_endpoint *endpt, pjsip_module *mod, int htype, const pj_str_t *hname, unsigned count, const pj_str_t tags[]) { pjsip_generic_array_hdr *hdr; unsigned i; PJ_UNUSED_ARG(mod); /* Check arguments. */ PJ_ASSERT_RETURN(endpt!=NULL && count>0 && tags, PJ_EINVAL); PJ_ASSERT_RETURN(htype==PJSIP_H_ACCEPT || htype==PJSIP_H_ALLOW || htype==PJSIP_H_SUPPORTED, PJ_EINVAL); /* Find the header. */ hdr = (pjsip_generic_array_hdr*) pjsip_endpt_get_capability(endpt, htype, hname); /* Create the header when it's not present */ if (hdr == NULL) { switch (htype) { case PJSIP_H_ACCEPT: hdr = pjsip_accept_hdr_create(endpt->pool); break; case PJSIP_H_ALLOW: hdr = pjsip_allow_hdr_create(endpt->pool); break; case PJSIP_H_SUPPORTED: hdr = pjsip_supported_hdr_create(endpt->pool); break; default: return PJ_EINVAL; } if (hdr) { pj_list_push_back(&endpt->cap_hdr, hdr); } } /* Add the tags to the header. */ for (i=0; ipool, &hdr->values[hdr->count], &tags[i]); ++hdr->count; } /* Done. */ return PJ_SUCCESS; } /* * Get additional headers to be put in outgoing request message. */ PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt) { return &endpt->req_hdr; } /* * Initialize endpoint. */ PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf, const char *name, pjsip_endpoint **p_endpt) { pj_status_t status; pj_pool_t *pool; pjsip_endpoint *endpt; pjsip_max_fwd_hdr *mf_hdr; pj_lock_t *lock = NULL; status = pj_register_strerror(PJSIP_ERRNO_START, PJ_ERRNO_SPACE_SIZE, &pjsip_strerror); pj_assert(status == PJ_SUCCESS); PJ_LOG(5, (THIS_FILE, "Creating endpoint instance...")); *p_endpt = NULL; /* Create pool */ pool = pj_pool_create(pf, "pept%p", PJSIP_POOL_LEN_ENDPT, PJSIP_POOL_INC_ENDPT, &pool_callback); if (!pool) return PJ_ENOMEM; /* Create endpoint. */ endpt = PJ_POOL_ZALLOC_T(pool, pjsip_endpoint); endpt->pool = pool; endpt->pf = pf; /* Init modules list. */ pj_list_init(&endpt->module_list); /* Initialize exit callback list. */ pj_list_init(&endpt->exit_cb_list); /* Create R/W mutex for module manipulation. */ status = pj_rwmutex_create(endpt->pool, "ept%p", &endpt->mod_mutex); if (status != PJ_SUCCESS) goto on_error; /* Init parser. */ init_sip_parser(); /* Init tel: uri */ pjsip_tel_uri_subsys_init(); /* Get name. */ if (name != NULL) { pj_str_t temp; pj_strdup_with_null(endpt->pool, &endpt->name, pj_cstr(&temp, name)); } else { pj_strdup_with_null(endpt->pool, &endpt->name, pj_gethostname()); } /* Create mutex for the events, etc. */ status = pj_mutex_create_recursive( endpt->pool, "ept%p", &endpt->mutex ); if (status != PJ_SUCCESS) { goto on_error; } /* Create timer heap to manage all timers within this endpoint. */ status = pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT, &endpt->timer_heap); if (status != PJ_SUCCESS) { goto on_error; } /* Set recursive lock for the timer heap. */ status = pj_lock_create_recursive_mutex( endpt->pool, "edpt%p", &lock); if (status != PJ_SUCCESS) { goto on_error; } pj_timer_heap_set_lock(endpt->timer_heap, lock, PJ_TRUE); /* Set maximum timed out entries to process in a single poll. */ pj_timer_heap_set_max_timed_out_per_poll(endpt->timer_heap, PJSIP_MAX_TIMED_OUT_ENTRIES); /* Create ioqueue. */ status = pj_ioqueue_create( endpt->pool, PJSIP_MAX_TRANSPORTS, &endpt->ioqueue); if (status != PJ_SUCCESS) { goto on_error; } /* Create transport manager. */ status = pjsip_tpmgr_create( endpt->pool, endpt, &endpt_on_rx_msg, &endpt_on_tx_msg, &endpt->transport_mgr); if (status != PJ_SUCCESS) { goto on_error; } /* Create asynchronous DNS resolver. */ status = pjsip_resolver_create(endpt->pool, &endpt->resolver); if (status != PJ_SUCCESS) { PJ_LOG(4, (THIS_FILE, "Error creating resolver instance")); goto on_error; } /* Initialize request headers. */ pj_list_init(&endpt->req_hdr); /* Add "Max-Forwards" for request header. */ mf_hdr = pjsip_max_fwd_hdr_create(endpt->pool, PJSIP_MAX_FORWARDS_VALUE); pj_list_insert_before( &endpt->req_hdr, mf_hdr); /* Initialize capability header list. */ pj_list_init(&endpt->cap_hdr); /* Done. */ *p_endpt = endpt; return status; on_error: if (endpt->transport_mgr) { pjsip_tpmgr_destroy(endpt->transport_mgr); endpt->transport_mgr = NULL; } if (endpt->ioqueue) { pj_ioqueue_destroy(endpt->ioqueue); endpt->ioqueue = NULL; } if (endpt->timer_heap) { pj_timer_heap_destroy(endpt->timer_heap); endpt->timer_heap = NULL; } if (endpt->mutex) { pj_mutex_destroy(endpt->mutex); endpt->mutex = NULL; } deinit_sip_parser(); if (endpt->mod_mutex) { pj_rwmutex_destroy(endpt->mod_mutex); endpt->mod_mutex = NULL; } pj_pool_release( endpt->pool ); PJ_LOG(4, (THIS_FILE, "Error creating endpoint")); return status; } /* * Destroy endpoint. */ PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt) { pjsip_module *mod; exit_cb *ecb; PJ_LOG(5, (THIS_FILE, "Destroying endpoing instance..")); /* Phase 1: stop all modules */ mod = endpt->module_list.prev; while (mod != &endpt->module_list) { pjsip_module *prev = mod->prev; if (mod->stop) { (*mod->stop)(); } mod = prev; } /* Phase 2: unload modules. */ mod = endpt->module_list.prev; while (mod != &endpt->module_list) { pjsip_module *prev = mod->prev; unload_module(endpt, mod); mod = prev; } /* Destroy resolver */ pjsip_resolver_destroy(endpt->resolver); /* Shutdown and destroy all transports. */ pjsip_tpmgr_destroy(endpt->transport_mgr); /* Destroy ioqueue */ pj_ioqueue_destroy(endpt->ioqueue); /* Destroy timer heap */ #if PJ_TIMER_DEBUG pj_timer_heap_dump(endpt->timer_heap); #endif pj_timer_heap_destroy(endpt->timer_heap); /* Call all registered exit callbacks */ ecb = endpt->exit_cb_list.next; while (ecb != &endpt->exit_cb_list) { (*ecb->func)(endpt); ecb = ecb->next; } /* Delete endpoint mutex. */ pj_mutex_destroy(endpt->mutex); /* Deinit parser */ deinit_sip_parser(); /* Delete module's mutex */ pj_rwmutex_destroy(endpt->mod_mutex); /* Finally destroy pool. */ pj_pool_release(endpt->pool); PJ_LOG(4, (THIS_FILE, "Endpoint %p destroyed", endpt)); } /* * Get endpoint name. */ PJ_DEF(const pj_str_t*) pjsip_endpt_name(const pjsip_endpoint *endpt) { return &endpt->name; } /* * Create new pool. */ PJ_DEF(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt, const char *pool_name, pj_size_t initial, pj_size_t increment ) { pj_pool_t *pool; /* Lock endpoint mutex. */ /* No need to lock mutex. Factory is thread safe. pj_mutex_lock(endpt->mutex); */ /* Create pool */ pool = pj_pool_create( endpt->pf, pool_name, initial, increment, &pool_callback); /* Unlock mutex. */ /* No need to lock mutex. Factory is thread safe. pj_mutex_unlock(endpt->mutex); */ if (!pool) { PJ_LOG(4, (THIS_FILE, "Unable to create pool %s!", pool_name)); } return pool; } /* * Return back pool to endpoint's pool manager to be either destroyed or * recycled. */ PJ_DEF(void) pjsip_endpt_release_pool( pjsip_endpoint *endpt, pj_pool_t *pool ) { PJ_LOG(6, (THIS_FILE, "Releasing pool %s", pj_pool_getobjname(pool))); /* Don't need to acquire mutex since pool factory is thread safe pj_mutex_lock(endpt->mutex); */ pj_pool_release( pool ); PJ_UNUSED_ARG(endpt); /* pj_mutex_unlock(endpt->mutex); */ } PJ_DEF(pj_status_t) pjsip_endpt_handle_events2(pjsip_endpoint *endpt, const pj_time_val *max_timeout, unsigned *p_count) { /* timeout is 'out' var. This just to make compiler happy. */ pj_time_val timeout = { 0, 0}; unsigned count = 0, net_event_count = 0; int c; PJ_LOG(6, (THIS_FILE, "pjsip_endpt_handle_events()")); /* Poll the timer. The timer heap has its own mutex for better * granularity, so we don't need to lock end endpoint. */ timeout.sec = timeout.msec = 0; c = pj_timer_heap_poll( endpt->timer_heap, &timeout ); if (c > 0) count += c; /* timer_heap_poll should never ever returns negative value, or otherwise * ioqueue_poll() will block forever! */ pj_assert(timeout.sec >= 0 && timeout.msec >= 0); if (timeout.msec >= 1000) timeout.msec = 999; /* If caller specifies maximum time to wait, then compare the value with * the timeout to wait from timer, and use the minimum value. */ if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) { timeout = *max_timeout; } /* Poll ioqueue. * Repeat polling the ioqueue while we have immediate events, because * timer heap may process more than one events, so if we only process * one network events at a time (such as when IOCP backend is used), * the ioqueue may have trouble keeping up with the request rate. * * For example, for each send() request, one network event will be * reported by ioqueue for the send() completion. If we don't poll * the ioqueue often enough, the send() completion will not be * reported in timely manner. */ do { c = pj_ioqueue_poll( endpt->ioqueue, &timeout); if (c < 0) { pj_status_t err = pj_get_netos_error(); pj_thread_sleep(PJ_TIME_VAL_MSEC(timeout)); if (p_count) *p_count = count; return err; } else if (c == 0) { break; } else { net_event_count += c; timeout.sec = timeout.msec = 0; } } while (c > 0 && net_event_count < PJSIP_MAX_NET_EVENTS); count += net_event_count; if (p_count) *p_count = count; return PJ_SUCCESS; } /* * Handle events. */ PJ_DEF(pj_status_t) pjsip_endpt_handle_events(pjsip_endpoint *endpt, const pj_time_val *max_timeout) { return pjsip_endpt_handle_events2(endpt, max_timeout, NULL); } /* * Schedule timer. */ #if PJ_TIMER_DEBUG PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer_dbg(pjsip_endpoint *endpt, pj_timer_entry *entry, const pj_time_val *delay, const char *src_file, int src_line) { PJ_LOG(6, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)", entry, delay->sec, delay->msec)); return pj_timer_heap_schedule_dbg(endpt->timer_heap, entry, delay, src_file, src_line); } #else PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt, pj_timer_entry *entry, const pj_time_val *delay ) { PJ_LOG(6, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)", entry, delay->sec, delay->msec)); return pj_timer_heap_schedule( endpt->timer_heap, entry, delay ); } #endif /* * Cancel the previously registered timer. */ PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, pj_timer_entry *entry ) { PJ_LOG(6, (THIS_FILE, "pjsip_endpt_cancel_timer(entry=%p)", entry)); pj_timer_heap_cancel( endpt->timer_heap, entry ); } /* * Get the timer heap instance of the SIP endpoint. */ PJ_DEF(pj_timer_heap_t*) pjsip_endpt_get_timer_heap(pjsip_endpoint *endpt) { return endpt->timer_heap; } /* Init with default */ PJ_DEF(void) pjsip_process_rdata_param_default(pjsip_process_rdata_param *p) { pj_bzero(p, sizeof(*p)); } /* Distribute rdata */ PJ_DEF(pj_status_t) pjsip_endpt_process_rx_data( pjsip_endpoint *endpt, pjsip_rx_data *rdata, pjsip_process_rdata_param *p, pj_bool_t *p_handled) { pjsip_msg *msg; pjsip_process_rdata_param def_prm; pjsip_module *mod; pj_bool_t handled = PJ_FALSE; unsigned i; pj_status_t status; PJ_ASSERT_RETURN(endpt && rdata, PJ_EINVAL); if (p==NULL) { p = &def_prm; pjsip_process_rdata_param_default(p); } msg = rdata->msg_info.msg; if (p_handled) *p_handled = PJ_FALSE; if (!p->silent) { PJ_LOG(5, (THIS_FILE, "Distributing rdata to modules: %s", pjsip_rx_data_get_info(rdata))); pj_log_push_indent(); } LOCK_MODULE_ACCESS(endpt); /* Find start module */ if (p->start_mod) { mod = (pjsip_module*) pj_list_find_node(&endpt->module_list, p->start_mod); if (!mod) { status = PJ_ENOTFOUND; goto on_return; } } else { mod = endpt->module_list.next; } /* Start after the specified index */ for (i=0; i < p->idx_after_start && mod != &endpt->module_list; ++i) { mod = mod->next; } /* Start with the specified priority */ while (mod != &endpt->module_list && mod->priority < (int)p->start_prio) { mod = mod->next; } if (mod == &endpt->module_list) { status = PJ_ENOTFOUND; goto on_return; } /* Distribute */ if (msg->type == PJSIP_REQUEST_MSG) { do { if (mod->on_rx_request) handled = (*mod->on_rx_request)(rdata); if (handled) break; mod = mod->next; } while (mod != &endpt->module_list); } else { do { if (mod->on_rx_response) handled = (*mod->on_rx_response)(rdata); if (handled) break; mod = mod->next; } while (mod != &endpt->module_list); } status = PJ_SUCCESS; on_return: if (p_handled) *p_handled = handled; UNLOCK_MODULE_ACCESS(endpt); if (!p->silent) { pj_log_pop_indent(); } return status; } /* * This is the callback that is called by the transport manager when it * receives a message from the network. */ static void endpt_on_rx_msg( pjsip_endpoint *endpt, pj_status_t status, pjsip_rx_data *rdata ) { pjsip_msg *msg = rdata->msg_info.msg; pjsip_process_rdata_param proc_prm; pj_bool_t handled = PJ_FALSE; PJ_UNUSED_ARG(msg); if (status != PJ_SUCCESS) { char info[30]; char errmsg[PJ_ERR_MSG_SIZE]; info[0] = '\0'; if (status == PJSIP_EMISSINGHDR) { pj_str_t p; p.ptr = info; p.slen = 0; if (rdata->msg_info.cid == NULL || rdata->msg_info.cid->id.slen) pj_strcpy2(&p, "Call-ID"); if (rdata->msg_info.from == NULL) pj_strcpy2(&p, " From"); if (rdata->msg_info.to == NULL) pj_strcpy2(&p, " To"); if (rdata->msg_info.via == NULL) pj_strcpy2(&p, " Via"); if (rdata->msg_info.cseq == NULL) pj_strcpy2(&p, " CSeq"); p.ptr[p.slen] = '\0'; } pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(1, (THIS_FILE, "Error processing packet from %s:%d: %s %s [code %d]:\n" "%.*s\n" "-- end of packet.", rdata->pkt_info.src_name, rdata->pkt_info.src_port, errmsg, info, status, (int)rdata->msg_info.len, rdata->msg_info.msg_buf)); return; } PJ_LOG(5, (THIS_FILE, "Processing incoming message: %s", pjsip_rx_data_get_info(rdata))); pj_log_push_indent(); #if defined(PJSIP_CHECK_VIA_SENT_BY) && PJSIP_CHECK_VIA_SENT_BY != 0 /* For response, check that the value in Via sent-by match the transport. * If not matched, silently drop the response. * Ref: RFC3261 Section 18.1.2 Receiving Response */ if (msg->type == PJSIP_RESPONSE_MSG) { const pj_str_t *local_addr; int port = rdata->msg_info.via->sent_by.port; pj_bool_t mismatch = PJ_FALSE; if (port == 0) { pjsip_transport_type_e type; type = (pjsip_transport_type_e)rdata->tp_info.transport->key.type; port = pjsip_transport_get_default_port_for_type(type); } local_addr = &rdata->tp_info.transport->local_name.host; if (pj_strcmp(&rdata->msg_info.via->sent_by.host, local_addr) != 0) { /* The RFC says that we should drop response when sent-by * address mismatch. But it could happen (e.g. with SER) when * endpoint with private IP is sending request to public * server. mismatch = PJ_TRUE; */ } else if (port != rdata->tp_info.transport->local_name.port) { /* Port or address mismatch, we should discard response */ /* But we saw one implementation (we don't want to name it to * protect the innocence) which put wrong sent-by port although * the "rport" parameter is correct. * So we discard the response only if the port doesn't match * both the port in sent-by and rport. We try to be lenient here! */ if (rdata->msg_info.via->rport_param != rdata->tp_info.transport->local_name.port) mismatch = PJ_TRUE; else { PJ_LOG(4,(THIS_FILE, "Message %s from %s has mismatch port in " "sent-by but the rport parameter is " "correct", pjsip_rx_data_get_info(rdata), rdata->pkt_info.src_name)); } } if (mismatch) { PJ_TODO(ENDPT_REPORT_WHEN_DROPPING_MESSAGE); PJ_LOG(4,(THIS_FILE, "Dropping response %s from %s:%d because " "sent-by is mismatch", pjsip_rx_data_get_info(rdata), rdata->pkt_info.src_name, rdata->pkt_info.src_port)); pj_log_pop_indent(); return; } } #endif pjsip_process_rdata_param_default(&proc_prm); proc_prm.silent = PJ_TRUE; pjsip_endpt_process_rx_data(endpt, rdata, &proc_prm, &handled); /* No module is able to handle the message */ if (!handled) { PJ_LOG(4,(THIS_FILE, "%s from %s:%d was dropped/unhandled by" " any modules", pjsip_rx_data_get_info(rdata), rdata->pkt_info.src_name, rdata->pkt_info.src_port)); } /* Must clear mod_data before returning rdata to transport, since * rdata may be reused. */ pj_bzero(&rdata->endpt_info, sizeof(rdata->endpt_info)); pj_log_pop_indent(); } /* * This callback is called by transport manager before message is sent. * Modules may inspect the message before it's actually sent. */ static pj_status_t endpt_on_tx_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata ) { pj_status_t status = PJ_SUCCESS; pjsip_module *mod; /* Distribute to modules, starting from modules with LOWEST priority */ LOCK_MODULE_ACCESS(endpt); mod = endpt->module_list.prev; if (tdata->msg->type == PJSIP_REQUEST_MSG) { while (mod != &endpt->module_list) { if (mod->on_tx_request) status = (*mod->on_tx_request)(tdata); if (status != PJ_SUCCESS) break; mod = mod->prev; } } else { while (mod != &endpt->module_list) { if (mod->on_tx_response) status = (*mod->on_tx_response)(tdata); if (status != PJ_SUCCESS) break; mod = mod->prev; } } UNLOCK_MODULE_ACCESS(endpt); return status; } /* * Create transmit data buffer. */ PJ_DEF(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt, pjsip_tx_data **p_tdata) { return pjsip_tx_data_create(endpt->transport_mgr, p_tdata); } /* * Create the DNS resolver instance. */ PJ_DEF(pj_status_t) pjsip_endpt_create_resolver(pjsip_endpoint *endpt, pj_dns_resolver **p_resv) { #if PJSIP_HAS_RESOLVER PJ_ASSERT_RETURN(endpt && p_resv, PJ_EINVAL); return pj_dns_resolver_create( endpt->pf, NULL, 0, endpt->timer_heap, endpt->ioqueue, p_resv); #else PJ_UNUSED_ARG(endpt); PJ_UNUSED_ARG(p_resv); pj_assert(!"Resolver is disabled (PJSIP_HAS_RESOLVER==0)"); return PJ_EINVALIDOP; #endif } /* * Set DNS resolver to be used by the SIP resolver. */ PJ_DEF(pj_status_t) pjsip_endpt_set_resolver( pjsip_endpoint *endpt, pj_dns_resolver *resv) { return pjsip_resolver_set_resolver(endpt->resolver, resv); } /* * Set DNS external resolver implementation to be used by the SIP resolver. */ PJ_DEF(pj_status_t) pjsip_endpt_set_ext_resolver(pjsip_endpoint *endpt, pjsip_ext_resolver *ext_res) { return pjsip_resolver_set_ext_resolver(endpt->resolver, ext_res); } /* * Get the DNS resolver being used by the SIP resolver. */ PJ_DEF(pj_dns_resolver*) pjsip_endpt_get_resolver(pjsip_endpoint *endpt) { PJ_ASSERT_RETURN(endpt, NULL); return pjsip_resolver_get_resolver(endpt->resolver); } /* * Resolve */ PJ_DEF(void) pjsip_endpt_resolve( pjsip_endpoint *endpt, pj_pool_t *pool, pjsip_host_info *target, void *token, pjsip_resolver_callback *cb) { pjsip_resolve( endpt->resolver, pool, target, token, cb); } /* * Get transport manager. */ PJ_DEF(pjsip_tpmgr*) pjsip_endpt_get_tpmgr(pjsip_endpoint *endpt) { return endpt->transport_mgr; } /* * Get ioqueue instance. */ PJ_DEF(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt) { return endpt->ioqueue; } /* * Find/create transport. */ PJ_DEF(pj_status_t) pjsip_endpt_acquire_transport(pjsip_endpoint *endpt, pjsip_transport_type_e type, const pj_sockaddr_t *remote, int addr_len, const pjsip_tpselector *sel, pjsip_transport **transport) { return pjsip_tpmgr_acquire_transport(endpt->transport_mgr, type, remote, addr_len, sel, transport); } /* * Find/create transport. */ PJ_DEF(pj_status_t) pjsip_endpt_acquire_transport2(pjsip_endpoint *endpt, pjsip_transport_type_e type, const pj_sockaddr_t *remote, int addr_len, const pjsip_tpselector *sel, pjsip_tx_data *tdata, pjsip_transport **transport) { return pjsip_tpmgr_acquire_transport2(endpt->transport_mgr, type, remote, addr_len, sel, tdata, transport); } /* * Report error. */ PJ_DEF(void) pjsip_endpt_log_error( pjsip_endpoint *endpt, const char *sender, pj_status_t error_code, const char *format, ... ) { #if PJ_LOG_MAX_LEVEL > 0 char newformat[256]; pj_size_t len; va_list marker; va_start(marker, format); PJ_UNUSED_ARG(endpt); len = pj_ansi_strlen(format); if (len < (int)sizeof(newformat)-30) { pj_str_t errstr; pj_ansi_strcpy(newformat, format); pj_ansi_snprintf(newformat+len, sizeof(newformat)-len-1, ": [err %d] ", error_code); len += pj_ansi_strlen(newformat+len); errstr = pj_strerror( error_code, newformat+len, sizeof(newformat)-len-1); len += errstr.slen; newformat[len] = '\0'; pj_log(sender, 1, newformat, marker); } else { pj_log(sender, 1, format, marker); } va_end(marker); #else PJ_UNUSED_ARG(format); PJ_UNUSED_ARG(error_code); PJ_UNUSED_ARG(sender); PJ_UNUSED_ARG(endpt); #endif } /* * Dump endpoint. */ PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail ) { #if PJ_LOG_MAX_LEVEL >= 3 PJ_LOG(5, (THIS_FILE, "pjsip_endpt_dump()")); /* Lock mutex. */ pj_mutex_lock(endpt->mutex); PJ_LOG(3, (THIS_FILE, "Dumping endpoint %p:", endpt)); /* Dumping pool factory. */ pj_pool_factory_dump(endpt->pf, detail); /* Pool health. */ PJ_LOG(3, (THIS_FILE," Endpoint pool capacity=%u, used_size=%u", pj_pool_get_capacity(endpt->pool), pj_pool_get_used_size(endpt->pool))); /* Resolver */ #if PJSIP_HAS_RESOLVER if (pjsip_endpt_get_resolver(endpt)) { pj_dns_resolver_dump(pjsip_endpt_get_resolver(endpt), detail); } #endif /* Transports. */ pjsip_tpmgr_dump_transports( endpt->transport_mgr ); /* Timer. */ #if PJ_TIMER_DEBUG pj_timer_heap_dump(endpt->timer_heap); #else PJ_LOG(3,(THIS_FILE, " Timer heap has %u entries", pj_timer_heap_count(endpt->timer_heap))); #endif /* Unlock mutex. */ pj_mutex_unlock(endpt->mutex); #else PJ_UNUSED_ARG(endpt); PJ_UNUSED_ARG(detail); PJ_LOG(3,(THIS_FILE, "pjsip_end_dump: can't dump because it's disabled.")); #endif } PJ_DEF(pj_status_t) pjsip_endpt_atexit( pjsip_endpoint *endpt, pjsip_endpt_exit_callback func) { exit_cb *new_cb; PJ_ASSERT_RETURN(endpt && func, PJ_EINVAL); new_cb = PJ_POOL_ZALLOC_T(endpt->pool, exit_cb); new_cb->func = func; pj_mutex_lock(endpt->mutex); pj_list_push_back(&endpt->exit_cb_list, new_cb); pj_mutex_unlock(endpt->mutex); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_endpoint_wrap.cpp ================================================ /* $Id: sip_endpoint_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This file is a C++ wrapper, see ticket #886 for details. */ #include "sip_endpoint.c" ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_errno.c ================================================ /* $Id: sip_errno.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include /* PJSIP's own error codes/messages * MUST KEEP THIS ARRAY SORTED!! */ #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) static const struct { int code; const char *msg; } err_str[] = { /* Generic SIP errors */ PJ_BUILD_ERR( PJSIP_EBUSY, "Object is busy" ), PJ_BUILD_ERR( PJSIP_ETYPEEXISTS , "Object with the same type exists" ), PJ_BUILD_ERR( PJSIP_ESHUTDOWN, "SIP stack shutting down" ), PJ_BUILD_ERR( PJSIP_ENOTINITIALIZED,"SIP object is not initialized." ), PJ_BUILD_ERR( PJSIP_ENOROUTESET, "Missing route set (for tel: URI)" ), /* Messaging errors */ PJ_BUILD_ERR( PJSIP_EINVALIDMSG, "Invalid message/syntax error" ), PJ_BUILD_ERR( PJSIP_ENOTREQUESTMSG, "Expecting request message"), PJ_BUILD_ERR( PJSIP_ENOTRESPONSEMSG,"Expecting response message"), PJ_BUILD_ERR( PJSIP_EMSGTOOLONG, "Message too long" ), PJ_BUILD_ERR( PJSIP_EPARTIALMSG, "Partial message" ), PJ_BUILD_ERR( PJSIP_EINVALIDSTATUS, "Invalid/unexpected SIP status code"), PJ_BUILD_ERR( PJSIP_EINVALIDURI, "Invalid URI" ), PJ_BUILD_ERR( PJSIP_EINVALIDSCHEME, "Invalid URI scheme" ), PJ_BUILD_ERR( PJSIP_EMISSINGREQURI, "Missing Request-URI" ), PJ_BUILD_ERR( PJSIP_EINVALIDREQURI, "Invalid Request URI" ), PJ_BUILD_ERR( PJSIP_EURITOOLONG, "URI is too long" ), PJ_BUILD_ERR( PJSIP_EMISSINGHDR, "Missing required header(s)" ), PJ_BUILD_ERR( PJSIP_EINVALIDHDR, "Invalid header field"), PJ_BUILD_ERR( PJSIP_EINVALIDVIA, "Invalid Via header" ), PJ_BUILD_ERR( PJSIP_EMULTIPLEVIA, "Multiple Via headers in response" ), PJ_BUILD_ERR( PJSIP_EMISSINGBODY, "Missing message body" ), PJ_BUILD_ERR( PJSIP_EINVALIDMETHOD, "Invalid/unexpected method" ), /* Transport errors */ PJ_BUILD_ERR( PJSIP_EUNSUPTRANSPORT,"Unsupported transport"), PJ_BUILD_ERR( PJSIP_EPENDINGTX, "Transmit buffer already pending"), PJ_BUILD_ERR( PJSIP_ERXOVERFLOW, "Rx buffer overflow"), PJ_BUILD_ERR( PJSIP_EBUFDESTROYED, "Buffer destroyed"), PJ_BUILD_ERR( PJSIP_ETPNOTSUITABLE, "Unsuitable transport selected"), PJ_BUILD_ERR( PJSIP_ETPNOTAVAIL, "Transport not available for use"), /* Transaction errors */ PJ_BUILD_ERR( PJSIP_ETSXDESTROYED, "Transaction has been destroyed"), PJ_BUILD_ERR( PJSIP_ENOTSX, "No transaction is associated with the object " "(expecting stateful processing)" ), /* URI comparison status */ PJ_BUILD_ERR( PJSIP_ECMPSCHEME, "URI scheme mismatch" ), PJ_BUILD_ERR( PJSIP_ECMPUSER, "URI user part mismatch" ), PJ_BUILD_ERR( PJSIP_ECMPPASSWD, "URI password part mismatch" ), PJ_BUILD_ERR( PJSIP_ECMPHOST, "URI host part mismatch" ), PJ_BUILD_ERR( PJSIP_ECMPPORT, "URI port mismatch" ), PJ_BUILD_ERR( PJSIP_ECMPTRANSPORTPRM,"URI transport param mismatch" ), PJ_BUILD_ERR( PJSIP_ECMPTTLPARAM, "URI ttl param mismatch" ), PJ_BUILD_ERR( PJSIP_ECMPUSERPARAM, "URI user param mismatch" ), PJ_BUILD_ERR( PJSIP_ECMPMETHODPARAM,"URI method param mismatch" ), PJ_BUILD_ERR( PJSIP_ECMPMADDRPARAM, "URI maddr param mismatch" ), PJ_BUILD_ERR( PJSIP_ECMPOTHERPARAM, "URI other param mismatch" ), PJ_BUILD_ERR( PJSIP_ECMPHEADERPARAM,"URI header parameter mismatch" ), /* Authentication. */ PJ_BUILD_ERR( PJSIP_EFAILEDCREDENTIAL, "Credential failed to authenticate"), PJ_BUILD_ERR( PJSIP_ENOCREDENTIAL, "No suitable credential"), PJ_BUILD_ERR( PJSIP_EINVALIDALGORITHM, "Invalid/unsupported digest algorithm" ), PJ_BUILD_ERR( PJSIP_EINVALIDQOP, "Invalid/unsupported digest qop" ), PJ_BUILD_ERR( PJSIP_EINVALIDAUTHSCHEME,"Unsupported authentication scheme" ), PJ_BUILD_ERR( PJSIP_EAUTHNOPREVCHAL, "No previous challenge" ), PJ_BUILD_ERR( PJSIP_EAUTHNOAUTH, "No suitable authorization header" ), PJ_BUILD_ERR( PJSIP_EAUTHACCNOTFOUND, "Account or credential not found" ), PJ_BUILD_ERR( PJSIP_EAUTHACCDISABLED, "Account or credential is disabled" ), PJ_BUILD_ERR( PJSIP_EAUTHINVALIDREALM, "Invalid authorization realm"), PJ_BUILD_ERR( PJSIP_EAUTHINVALIDDIGEST,"Invalid authorization digest" ), PJ_BUILD_ERR( PJSIP_EAUTHSTALECOUNT, "Maximum number of stale retries exceeded"), PJ_BUILD_ERR( PJSIP_EAUTHINNONCE, "Invalid nonce value in authentication challenge"), PJ_BUILD_ERR( PJSIP_EAUTHINAKACRED, "Invalid AKA credential"), PJ_BUILD_ERR( PJSIP_EAUTHNOCHAL, "No challenge is found"), /* UA/dialog layer. */ PJ_BUILD_ERR( PJSIP_EMISSINGTAG, "Missing From/To tag parameter" ), PJ_BUILD_ERR( PJSIP_ENOTREFER, "Expecting REFER request") , PJ_BUILD_ERR( PJSIP_ENOREFERSESSION,"Not associated with REFER subscription"), /* Invite session. */ PJ_BUILD_ERR( PJSIP_ESESSIONTERMINATED, "INVITE session already terminated" ), PJ_BUILD_ERR( PJSIP_ESESSIONSTATE, "Invalid INVITE session state" ), PJ_BUILD_ERR( PJSIP_ESESSIONINSECURE, "Require secure session/transport"), /* SSL errors */ PJ_BUILD_ERR( PJSIP_TLS_EUNKNOWN, "Unknown TLS error" ), PJ_BUILD_ERR( PJSIP_TLS_EINVMETHOD, "Invalid SSL protocol method" ), PJ_BUILD_ERR( PJSIP_TLS_ECACERT, "Error loading/verifying SSL CA list file"), PJ_BUILD_ERR( PJSIP_TLS_ECERTFILE, "Error loading SSL certificate chain file"), PJ_BUILD_ERR( PJSIP_TLS_EKEYFILE, "Error adding private key from SSL certificate file"), PJ_BUILD_ERR( PJSIP_TLS_ECIPHER, "Error setting SSL cipher list"), PJ_BUILD_ERR( PJSIP_TLS_ECTX, "Error creating SSL context"), PJ_BUILD_ERR( PJSIP_TLS_ESSLCONN, "Error creating SSL connection object"), PJ_BUILD_ERR( PJSIP_TLS_ECONNECT, "Unknown error when performing SSL connect()"), PJ_BUILD_ERR( PJSIP_TLS_EACCEPT, "Unknown error when performing SSL accept()"), PJ_BUILD_ERR( PJSIP_TLS_ESEND, "Unknown error when sending SSL data"), PJ_BUILD_ERR( PJSIP_TLS_EREAD, "Unknown error when reading SSL data"), PJ_BUILD_ERR( PJSIP_TLS_ETIMEDOUT, "SSL negotiation has timed out"), PJ_BUILD_ERR( PJSIP_TLS_ECERTVERIF, "SSL certificate verification error"), }; #endif /* PJ_HAS_ERROR_STRING */ /* * pjsip_strerror() */ PJ_DEF(pj_str_t) pjsip_strerror( pj_status_t statcode, char *buf, pj_size_t bufsize ) { pj_str_t errstr; #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) if (statcode >= PJSIP_ERRNO_START && statcode < PJSIP_ERRNO_START+800) { /* Status code. */ const pj_str_t *status_text = pjsip_get_status_text(PJSIP_ERRNO_TO_SIP_STATUS(statcode)); errstr.ptr = buf; pj_strncpy_with_null(&errstr, status_text, bufsize); return errstr; } else if (statcode >= PJSIP_ERRNO_START_PJSIP && statcode < PJSIP_ERRNO_START_PJSIP + 1000) { /* Find the error in the table. * Use binary search! */ int first = 0; int n = PJ_ARRAY_SIZE(err_str); while (n > 0) { int half = n/2; int mid = first + half; if (err_str[mid].code < statcode) { first = mid+1; n -= (half+1); } else if (err_str[mid].code > statcode) { n = half; } else { first = mid; break; } } if (PJ_ARRAY_SIZE(err_str) && err_str[first].code == statcode) { pj_str_t msg; msg.ptr = (char*)err_str[first].msg; msg.slen = pj_ansi_strlen(err_str[first].msg); errstr.ptr = buf; pj_strncpy_with_null(&errstr, &msg, bufsize); return errstr; } } #endif /* PJ_HAS_ERROR_STRING */ /* Error not found. */ errstr.ptr = buf; errstr.slen = pj_ansi_snprintf(buf, bufsize, "Unknown pjsip error %d", statcode); if (errstr.slen < 1 || errstr.slen >= (pj_ssize_t)bufsize) errstr.slen = bufsize - 1; return errstr; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_msg.c ================================================ /* $Id: sip_msg.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include PJ_DEF_DATA(const pjsip_method) pjsip_invite_method = { PJSIP_INVITE_METHOD, { "INVITE",6 }}; PJ_DEF_DATA(const pjsip_method) pjsip_cancel_method = { PJSIP_CANCEL_METHOD, { "CANCEL",6 }}; PJ_DEF_DATA(const pjsip_method) pjsip_ack_method = { PJSIP_ACK_METHOD, { "ACK",3}}; PJ_DEF_DATA(const pjsip_method) pjsip_bye_method = { PJSIP_BYE_METHOD, { "BYE",3}}; PJ_DEF_DATA(const pjsip_method) pjsip_register_method = { PJSIP_REGISTER_METHOD, { "REGISTER", 8}}; PJ_DEF_DATA(const pjsip_method) pjsip_options_method = { PJSIP_OPTIONS_METHOD, { "OPTIONS",7}}; /** INVITE method constant. */ PJ_DEF(const pjsip_method*) pjsip_get_invite_method(void) { return &pjsip_invite_method; } /** CANCEL method constant. */ PJ_DEF(const pjsip_method*) pjsip_get_cancel_method(void) { return &pjsip_cancel_method; } /** ACK method constant. */ PJ_DEF(const pjsip_method*) pjsip_get_ack_method(void) { return &pjsip_ack_method; } /** BYE method constant. */ PJ_DEF(const pjsip_method*) pjsip_get_bye_method(void) { return &pjsip_bye_method; } /** REGISTER method constant.*/ PJ_DEF(const pjsip_method*) pjsip_get_register_method(void) { return &pjsip_register_method; } /** OPTIONS method constant. */ PJ_DEF(const pjsip_method*) pjsip_get_options_method(void) { return &pjsip_options_method; } static const pj_str_t *method_names[] = { &pjsip_invite_method.name, &pjsip_cancel_method.name, &pjsip_ack_method.name, &pjsip_bye_method.name, &pjsip_register_method.name, &pjsip_options_method.name }; const pjsip_hdr_name_info_t pjsip_hdr_names[] = { { "Accept", 6, NULL }, // PJSIP_H_ACCEPT, { "Accept-Encoding", 15, NULL }, // PJSIP_H_ACCEPT_ENCODING, { "Accept-Language", 15, NULL }, // PJSIP_H_ACCEPT_LANGUAGE, { "Alert-Info", 10, NULL }, // PJSIP_H_ALERT_INFO, { "Allow", 5, NULL }, // PJSIP_H_ALLOW, { "Authentication-Info",19, NULL }, // PJSIP_H_AUTHENTICATION_INFO, { "Authorization", 13, NULL }, // PJSIP_H_AUTHORIZATION, { "Call-ID", 7, "i" }, // PJSIP_H_CALL_ID, { "Call-Info", 9, NULL }, // PJSIP_H_CALL_INFO, { "Contact", 7, "m" }, // PJSIP_H_CONTACT, { "Content-Disposition",19, NULL }, // PJSIP_H_CONTENT_DISPOSITION, { "Content-Encoding", 16, "e" }, // PJSIP_H_CONTENT_ENCODING, { "Content-Language", 16, NULL }, // PJSIP_H_CONTENT_LANGUAGE, { "Content-Length", 14, "l" }, // PJSIP_H_CONTENT_LENGTH, { "Content-Type", 12, "c" }, // PJSIP_H_CONTENT_TYPE, { "CSeq", 4, NULL }, // PJSIP_H_CSEQ, { "Date", 4, NULL }, // PJSIP_H_DATE, { "Error-Info", 10, NULL }, // PJSIP_H_ERROR_INFO, { "Expires", 7, NULL }, // PJSIP_H_EXPIRES, { "From", 4, "f" }, // PJSIP_H_FROM, { "In-Reply-To", 11, NULL }, // PJSIP_H_IN_REPLY_TO, { "Max-Forwards", 12, NULL }, // PJSIP_H_MAX_FORWARDS, { "MIME-Version", 12, NULL }, // PJSIP_H_MIME_VERSION, { "Min-Expires", 11, NULL }, // PJSIP_H_MIN_EXPIRES, { "Organization", 12, NULL }, // PJSIP_H_ORGANIZATION, { "Priority", 8, NULL }, // PJSIP_H_PRIORITY, { "Proxy-Authenticate", 18, NULL }, // PJSIP_H_PROXY_AUTHENTICATE, { "Proxy-Authorization",19, NULL }, // PJSIP_H_PROXY_AUTHORIZATION, { "Proxy-Require", 13, NULL }, // PJSIP_H_PROXY_REQUIRE, { "Record-Route", 12, NULL }, // PJSIP_H_RECORD_ROUTE, { "Reply-To", 8, NULL }, // PJSIP_H_REPLY_TO, { "Require", 7, NULL }, // PJSIP_H_REQUIRE, { "Retry-After", 11, NULL }, // PJSIP_H_RETRY_AFTER, { "Route", 5, NULL }, // PJSIP_H_ROUTE, { "Server", 6, NULL }, // PJSIP_H_SERVER, { "Subject", 7, "s" }, // PJSIP_H_SUBJECT, { "Supported", 9, "k" }, // PJSIP_H_SUPPORTED, { "Timestamp", 9, NULL }, // PJSIP_H_TIMESTAMP, { "To", 2, "t" }, // PJSIP_H_TO, { "Unsupported", 11, NULL }, // PJSIP_H_UNSUPPORTED, { "User-Agent", 10, NULL }, // PJSIP_H_USER_AGENT, { "Via", 3, "v" }, // PJSIP_H_VIA, { "Warning", 7, NULL }, // PJSIP_H_WARNING, { "WWW-Authenticate", 16, NULL }, // PJSIP_H_WWW_AUTHENTICATE, { "_Unknown-Header", 15, NULL }, // PJSIP_H_OTHER, }; pj_bool_t pjsip_use_compact_form = PJSIP_ENCODE_SHORT_HNAME; static pj_str_t status_phrase[710]; static int print_media_type(char *buf, unsigned len, const pjsip_media_type *media); static int init_status_phrase() { unsigned i; pj_str_t default_reason_phrase = { "Default status message", 22}; for (i=0; iid = me; m->name = *method_names[me]; } PJ_DEF(void) pjsip_method_init_np(pjsip_method *m, pj_str_t *str) { unsigned i; for (i=0; iptr, method_names[i]->ptr, str->slen)==0 || pj_stricmp(str, method_names[i])==0) { m->id = (pjsip_method_e)i; m->name = *method_names[i]; return; } } m->id = PJSIP_OTHER_METHOD; m->name = *str; } PJ_DEF(void) pjsip_method_copy( pj_pool_t *pool, pjsip_method *method, const pjsip_method *rhs ) { method->id = rhs->id; if (rhs->id != PJSIP_OTHER_METHOD) { method->name = rhs->name; } else { pj_strdup(pool, &method->name, &rhs->name); } } PJ_DEF(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2) { if (m1->id == m2->id) { if (m1->id != PJSIP_OTHER_METHOD) return 0; /* Method comparison is case sensitive! */ return pj_strcmp(&m1->name, &m2->name); } return ( m1->id < m2->id ) ? -1 : 1; } /////////////////////////////////////////////////////////////////////////////// /* * Message. */ PJ_DEF(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type) { pjsip_msg *msg = PJ_POOL_ALLOC_T(pool, pjsip_msg); pj_list_init(&msg->hdr); msg->type = type; msg->body = NULL; return msg; } PJ_DEF(pjsip_msg*) pjsip_msg_clone( pj_pool_t *pool, const pjsip_msg *src) { pjsip_msg *dst; const pjsip_hdr *sh; dst = pjsip_msg_create(pool, src->type); /* Clone request/status line */ if (src->type == PJSIP_REQUEST_MSG) { pjsip_method_copy(pool, &dst->line.req.method, &src->line.req.method); dst->line.req.uri = (pjsip_uri*) pjsip_uri_clone(pool, src->line.req.uri); } else { dst->line.status.code = src->line.status.code; pj_strdup(pool, &dst->line.status.reason, &src->line.status.reason); } /* Clone headers */ sh = src->hdr.next; while (sh != &src->hdr) { pjsip_hdr *dh = (pjsip_hdr*) pjsip_hdr_clone(pool, sh); pjsip_msg_add_hdr(dst, dh); sh = sh->next; } /* Clone message body */ if (src->body) { dst->body = pjsip_msg_body_clone(pool, src->body); } return dst; } PJ_DEF(void*) pjsip_msg_find_hdr( const pjsip_msg *msg, pjsip_hdr_e hdr_type, const void *start) { const pjsip_hdr *hdr=(const pjsip_hdr*) start, *end=&msg->hdr; if (hdr == NULL) { hdr = msg->hdr.next; } for (; hdr!=end; hdr = hdr->next) { if (hdr->type == hdr_type) return (void*)hdr; } return NULL; } PJ_DEF(void*) pjsip_msg_find_hdr_by_name( const pjsip_msg *msg, const pj_str_t *name, const void *start) { const pjsip_hdr *hdr=(const pjsip_hdr*)start, *end=&msg->hdr; if (hdr == NULL) { hdr = msg->hdr.next; } for (; hdr!=end; hdr = hdr->next) { if (pj_stricmp(&hdr->name, name) == 0) return (void*)hdr; } return NULL; } PJ_DEF(void*) pjsip_msg_find_hdr_by_names( const pjsip_msg *msg, const pj_str_t *name, const pj_str_t *sname, const void *start) { const pjsip_hdr *hdr=(const pjsip_hdr*)start, *end=&msg->hdr; if (hdr == NULL) { hdr = msg->hdr.next; } for (; hdr!=end; hdr = hdr->next) { if (pj_stricmp(&hdr->name, name) == 0) return (void*)hdr; if (pj_stricmp(&hdr->name, sname) == 0) return (void*)hdr; } return NULL; } PJ_DEF(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg, pjsip_hdr_e hdr_type, void *start) { pjsip_hdr *hdr = (pjsip_hdr*) pjsip_msg_find_hdr(msg, hdr_type, start); if (hdr) { pj_list_erase(hdr); } return hdr; } PJ_DEF(void*) pjsip_msg_find_remove_hdr_by_name( pjsip_msg *msg, pj_str_t *name, void *start) { pjsip_hdr *hdr = (pjsip_hdr*) pjsip_msg_find_hdr_by_name(msg, name, start); if (hdr) { pj_list_erase(hdr); } return hdr; } PJ_DEF(pj_ssize_t) pjsip_msg_print( const pjsip_msg *msg, char *buf, pj_size_t size) { char *p=buf, *end=buf+size; pj_ssize_t len; pjsip_hdr *hdr; pj_str_t clen_hdr = { "Content-Length: ", 16}; if (pjsip_use_compact_form) { clen_hdr.ptr = "l: "; clen_hdr.slen = 3; } /* Get a wild guess on how many bytes are typically needed. * We'll check this later in detail, but this serves as a quick check. */ if (size < 256) return -1; /* Print request line or status line depending on message type */ if (msg->type == PJSIP_REQUEST_MSG) { pjsip_uri *uri; /* Add method. */ len = msg->line.req.method.name.slen; pj_memcpy(p, msg->line.req.method.name.ptr, len); p += len; *p++ = ' '; /* Add URI */ uri = (pjsip_uri*) pjsip_uri_get_uri(msg->line.req.uri); len = pjsip_uri_print( PJSIP_URI_IN_REQ_URI, uri, p, end-p); if (len < 1) return -1; p += len; /* Add ' SIP/2.0' */ if (end-p < 16) return -1; pj_memcpy(p, " SIP/2.0\r\n", 10); p += 10; } else { /* Add 'SIP/2.0 ' */ pj_memcpy(p, "SIP/2.0 ", 8); p += 8; /* Add status code. */ len = pj_utoa(msg->line.status.code, p); p += len; *p++ = ' '; /* Add reason text. */ len = msg->line.status.reason.slen; pj_memcpy(p, msg->line.status.reason.ptr, len ); p += len; /* Add newline. */ *p++ = '\r'; *p++ = '\n'; } /* Print each of the headers. */ for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) { len = pjsip_hdr_print_on(hdr, p, end-p); if (len < 0) return -1; if (len > 0) { p += len; if (p+3 >= end) return -1; *p++ = '\r'; *p++ = '\n'; } } /* Process message body. */ if (msg->body) { enum { CLEN_SPACE = 5 }; char *clen_pos = NULL; /* Automaticly adds Content-Type and Content-Length headers, only * if content_type is set in the message body. */ if (msg->body->content_type.type.slen) { pj_str_t ctype_hdr = { "Content-Type: ", 14}; const pjsip_media_type *media = &msg->body->content_type; if (pjsip_use_compact_form) { ctype_hdr.ptr = "c: "; ctype_hdr.slen = 3; } /* Add Content-Type header. */ if ( (end-p) < 24 + media->type.slen + media->subtype.slen) { return -1; } pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen); p += ctype_hdr.slen; p += print_media_type(p, (unsigned)(end-p), media); *p++ = '\r'; *p++ = '\n'; /* Add Content-Length header. */ if ((end-p) < clen_hdr.slen + 12 + 2) { return -1; } pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen); p += clen_hdr.slen; /* Print blanks after "Content-Length:", this is where we'll put * the content length value after we know the length of the * body. */ pj_memset(p, ' ', CLEN_SPACE); clen_pos = p; p += CLEN_SPACE; *p++ = '\r'; *p++ = '\n'; } /* Add blank newline. */ *p++ = '\r'; *p++ = '\n'; /* Print the message body itself. */ len = (*msg->body->print_body)(msg->body, p, end-p); if (len < 0) { return -1; } p += len; /* Now that we have the length of the body, print this to the * Content-Length header. */ if (clen_pos) { char tmp[16]; len = pj_utoa((unsigned long)len, tmp); if (len > CLEN_SPACE) len = CLEN_SPACE; pj_memcpy(clen_pos+CLEN_SPACE-len, tmp, len); } } else { /* There's no message body. * Add Content-Length with zero value. */ if ((end-p) < clen_hdr.slen+8) { return -1; } pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen); p += clen_hdr.slen; *p++ = ' '; *p++ = '0'; *p++ = '\r'; *p++ = '\n'; *p++ = '\r'; *p++ = '\n'; } *p = '\0'; return p-buf; } /////////////////////////////////////////////////////////////////////////////// PJ_DEF(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr_ptr ) { const pjsip_hdr *hdr = (const pjsip_hdr*) hdr_ptr; return (*hdr->vptr->clone)(pool, hdr_ptr); } PJ_DEF(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr_ptr ) { const pjsip_hdr *hdr = (const pjsip_hdr*) hdr_ptr; return (*hdr->vptr->shallow_clone)(pool, hdr_ptr); } PJ_DEF(int) pjsip_hdr_print_on( void *hdr_ptr, char *buf, pj_size_t len) { pjsip_hdr *hdr = (pjsip_hdr*) hdr_ptr; return (*hdr->vptr->print_on)(hdr_ptr, buf, len); } /////////////////////////////////////////////////////////////////////////////// /* * Status/Reason Phrase */ PJ_DEF(const pj_str_t*) pjsip_get_status_text(int code) { static int is_initialized; if (is_initialized == 0) { is_initialized = 1; init_status_phrase(); } return (code>=100 && code<(int)(sizeof(status_phrase)/sizeof(status_phrase[0]))) ? &status_phrase[code] : &status_phrase[0]; } /////////////////////////////////////////////////////////////////////////////// /* * Media type */ /* * Init media type. */ PJ_DEF(void) pjsip_media_type_init( pjsip_media_type *mt, pj_str_t *type, pj_str_t *subtype) { pj_bzero(mt, sizeof(*mt)); pj_list_init(&mt->param); if (type) mt->type = *type; if (subtype) mt->subtype = *subtype; } PJ_DEF(void) pjsip_media_type_init2( pjsip_media_type *mt, char *type, char *subtype) { pj_str_t s_type, s_subtype; if (type) { s_type = pj_str(type); } else { s_type.ptr = NULL; s_type.slen = 0; } if (subtype) { s_subtype = pj_str(subtype); } else { s_subtype.ptr = NULL; s_subtype.slen = 0; } pjsip_media_type_init(mt, &s_type, &s_subtype); } /* * Compare two media types. */ PJ_DEF(int) pjsip_media_type_cmp( const pjsip_media_type *mt1, const pjsip_media_type *mt2, pj_bool_t cmp_param) { int rc; PJ_ASSERT_RETURN(mt1 && mt2, 1); rc = pj_stricmp(&mt1->type, &mt2->type); if (rc) return rc; rc = pj_stricmp(&mt1->subtype, &mt2->subtype); if (rc) return rc; if (cmp_param) { rc = pjsip_param_cmp(&mt1->param, &mt2->param, (cmp_param==1)); } return rc; } PJ_DEF(void) pjsip_media_type_cp( pj_pool_t *pool, pjsip_media_type *dst, const pjsip_media_type *src) { PJ_ASSERT_ON_FAIL(pool && dst && src, return); pj_strdup(pool, &dst->type, &src->type); pj_strdup(pool, &dst->subtype, &src->subtype); pjsip_param_clone(pool, &dst->param, &src->param); } /////////////////////////////////////////////////////////////////////////////// /* * Generic pjsip_hdr_names/hvalue header. */ static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr, char *buf, pj_size_t size); static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool, const pjsip_generic_string_hdr *hdr); static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool, const pjsip_generic_string_hdr *hdr ); static pjsip_hdr_vptr generic_hdr_vptr = { (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_clone, (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_shallow_clone, (pjsip_hdr_print_fptr) &pjsip_generic_string_hdr_print, }; PJ_DEF(void) pjsip_generic_string_hdr_init2(pjsip_generic_string_hdr *hdr, pj_str_t *hname, pj_str_t *hvalue) { init_hdr(hdr, PJSIP_H_OTHER, &generic_hdr_vptr); if (hname) { hdr->name = *hname; hdr->sname = *hname; } if (hvalue) { hdr->hvalue = *hvalue; } else { hdr->hvalue.ptr = NULL; hdr->hvalue.slen = 0; } } PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_init(pj_pool_t *pool, void *mem, const pj_str_t *hnames, const pj_str_t *hvalue) { pjsip_generic_string_hdr *hdr = (pjsip_generic_string_hdr*) mem; pj_str_t dup_hname, dup_hval; if (hnames) { pj_strdup(pool, &dup_hname, hnames); } else { dup_hname.slen = 0; } if (hvalue) { pj_strdup(pool, &dup_hval, hvalue); } else { dup_hval.slen = 0; } pjsip_generic_string_hdr_init2(hdr, &dup_hname, &dup_hval); return hdr; } PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create(pj_pool_t *pool, const pj_str_t *hnames, const pj_str_t *hvalue) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_generic_string_hdr)); return pjsip_generic_string_hdr_init(pool, mem, hnames, hvalue); } static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr, char *buf, pj_size_t size) { char *p = buf; const pj_str_t *hname = pjsip_use_compact_form? &hdr->sname : &hdr->name; if ((pj_ssize_t)size < hname->slen + hdr->hvalue.slen + 5) return -1; pj_memcpy(p, hname->ptr, hname->slen); p += hname->slen; *p++ = ':'; *p++ = ' '; pj_memcpy(p, hdr->hvalue.ptr, hdr->hvalue.slen); p += hdr->hvalue.slen; *p = '\0'; return (int)(p - buf); } static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool, const pjsip_generic_string_hdr *rhs) { pjsip_generic_string_hdr *hdr; hdr = pjsip_generic_string_hdr_create(pool, &rhs->name, &rhs->hvalue); hdr->type = rhs->type; pj_strdup(pool, &hdr->sname, &rhs->sname); return hdr; } static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool, const pjsip_generic_string_hdr *rhs ) { pjsip_generic_string_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_generic_string_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); return hdr; } /////////////////////////////////////////////////////////////////////////////// /* * Generic pjsip_hdr_names/integer value header. */ static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr, char *buf, pj_size_t size); static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool, const pjsip_generic_int_hdr *hdr); static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool, const pjsip_generic_int_hdr *hdr ); static pjsip_hdr_vptr generic_int_hdr_vptr = { (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_clone, (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_shallow_clone, (pjsip_hdr_print_fptr) &pjsip_generic_int_hdr_print, }; PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_init( pj_pool_t *pool, void *mem, const pj_str_t *hnames, int value) { pjsip_generic_int_hdr *hdr = (pjsip_generic_int_hdr*) mem; init_hdr(hdr, PJSIP_H_OTHER, &generic_int_hdr_vptr); if (hnames) { pj_strdup(pool, &hdr->name, hnames); hdr->sname = hdr->name; } hdr->ivalue = value; return hdr; } PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool, const pj_str_t *hnames, int value) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_generic_int_hdr)); return pjsip_generic_int_hdr_init(pool, mem, hnames, value); } static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr, char *buf, pj_size_t size) { char *p = buf; const pj_str_t *hname = pjsip_use_compact_form? &hdr->sname : &hdr->name; if ((pj_ssize_t)size < hname->slen + 15) return -1; pj_memcpy(p, hname->ptr, hname->slen); p += hname->slen; *p++ = ':'; *p++ = ' '; p += pj_utoa(hdr->ivalue, p); return (int)(p - buf); } static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool, const pjsip_generic_int_hdr *rhs) { pjsip_generic_int_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_generic_int_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); return hdr; } static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool, const pjsip_generic_int_hdr *rhs ) { pjsip_generic_int_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_generic_int_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); return hdr; } /////////////////////////////////////////////////////////////////////////////// /* * Generic array header. */ static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, char *buf, pj_size_t size); static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool, const pjsip_generic_array_hdr *hdr); static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool, const pjsip_generic_array_hdr *hdr); static pjsip_hdr_vptr generic_array_hdr_vptr = { (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_clone, (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_shallow_clone, (pjsip_hdr_print_fptr) &pjsip_generic_array_hdr_print, }; PJ_DEF(pjsip_generic_array_hdr*) pjsip_generic_array_hdr_init( pj_pool_t *pool, void *mem, const pj_str_t *hnames) { pjsip_generic_array_hdr *hdr = (pjsip_generic_array_hdr*) mem; init_hdr(hdr, PJSIP_H_OTHER, &generic_array_hdr_vptr); if (hnames) { pj_strdup(pool, &hdr->name, hnames); hdr->sname = hdr->name; } hdr->count = 0; return hdr; } PJ_DEF(pjsip_generic_array_hdr*) pjsip_generic_array_hdr_create( pj_pool_t *pool, const pj_str_t *hnames) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_generic_array_hdr)); return pjsip_generic_array_hdr_init(pool, mem, hnames); } static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, char *buf, pj_size_t size) { char *p = buf, *endbuf = buf+size; const pj_str_t *hname = pjsip_use_compact_form? &hdr->sname : &hdr->name; copy_advance(p, (*hname)); *p++ = ':'; *p++ = ' '; if (hdr->count > 0) { unsigned i; int printed; copy_advance(p, hdr->values[0]); for (i=1; icount; ++i) { copy_advance_pair(p, ", ", 2, hdr->values[i]); } } return (int)(p - buf); } static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool, const pjsip_generic_array_hdr *rhs) { unsigned i; pjsip_generic_array_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_generic_array_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); for (i=0; icount; ++i) { pj_strdup(pool, &hdr->values[i], &rhs->values[i]); } return hdr; } static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool, const pjsip_generic_array_hdr *rhs) { pjsip_generic_array_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_generic_array_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); return hdr; } /////////////////////////////////////////////////////////////////////////////// /* * Accept header. */ PJ_DEF(pjsip_accept_hdr*) pjsip_accept_hdr_init( pj_pool_t *pool, void *mem ) { pjsip_accept_hdr *hdr = (pjsip_accept_hdr*) mem; PJ_UNUSED_ARG(pool); init_hdr(hdr, PJSIP_H_ACCEPT, &generic_array_hdr_vptr); hdr->count = 0; return hdr; } PJ_DEF(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_accept_hdr)); return pjsip_accept_hdr_init(pool, mem); } /////////////////////////////////////////////////////////////////////////////// /* * Allow header. */ PJ_DEF(pjsip_allow_hdr*) pjsip_allow_hdr_init( pj_pool_t *pool, void *mem ) { pjsip_allow_hdr *hdr = (pjsip_allow_hdr*) mem; PJ_UNUSED_ARG(pool); init_hdr(hdr, PJSIP_H_ALLOW, &generic_array_hdr_vptr); hdr->count = 0; return hdr; } PJ_DEF(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_allow_hdr)); return pjsip_allow_hdr_init(pool, mem); } /////////////////////////////////////////////////////////////////////////////// /* * Call-ID header. */ PJ_DEF(pjsip_cid_hdr*) pjsip_cid_hdr_init( pj_pool_t *pool, void *mem ) { pjsip_cid_hdr *hdr = (pjsip_cid_hdr*) mem; PJ_UNUSED_ARG(pool); init_hdr(hdr, PJSIP_H_CALL_ID, &generic_hdr_vptr); return hdr; } PJ_DEF(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool ) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_cid_hdr)); return pjsip_cid_hdr_init(pool, mem); } /////////////////////////////////////////////////////////////////////////////// /* * Content-Length header. */ static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, char *buf, pj_size_t size); static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *hdr); #define pjsip_clen_hdr_shallow_clone pjsip_clen_hdr_clone static pjsip_hdr_vptr clen_hdr_vptr = { (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_clone, (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_shallow_clone, (pjsip_hdr_print_fptr) &pjsip_clen_hdr_print, }; PJ_DEF(pjsip_clen_hdr*) pjsip_clen_hdr_init( pj_pool_t *pool, void *mem ) { pjsip_clen_hdr *hdr = (pjsip_clen_hdr*) mem; PJ_UNUSED_ARG(pool); init_hdr(hdr, PJSIP_H_CONTENT_LENGTH, &clen_hdr_vptr); hdr->len = 0; return hdr; } PJ_DEF(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool ) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_clen_hdr)); return pjsip_clen_hdr_init(pool, mem); } static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, char *buf, pj_size_t size) { char *p = buf; int len; const pj_str_t *hname = pjsip_use_compact_form? &hdr->sname : &hdr->name; if ((pj_ssize_t)size < hname->slen + 14) return -1; pj_memcpy(p, hname->ptr, hname->slen); p += hname->slen; *p++ = ':'; *p++ = ' '; len = pj_utoa(hdr->len, p); p += len; *p = '\0'; return (int)(p-buf); } static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *rhs) { pjsip_clen_hdr *hdr = pjsip_clen_hdr_create(pool); hdr->len = rhs->len; return hdr; } /////////////////////////////////////////////////////////////////////////////// /* * CSeq header. */ static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size); static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr); static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr ); static pjsip_hdr_vptr cseq_hdr_vptr = { (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_clone, (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_shallow_clone, (pjsip_hdr_print_fptr) &pjsip_cseq_hdr_print, }; PJ_DEF(pjsip_cseq_hdr*) pjsip_cseq_hdr_init( pj_pool_t *pool, void *mem ) { pjsip_cseq_hdr *hdr = (pjsip_cseq_hdr*) mem; PJ_UNUSED_ARG(pool); init_hdr(hdr, PJSIP_H_CSEQ, &cseq_hdr_vptr); hdr->cseq = 0; hdr->method.id = PJSIP_OTHER_METHOD; hdr->method.name.ptr = NULL; hdr->method.name.slen = 0; return hdr; } PJ_DEF(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool ) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_cseq_hdr)); return pjsip_cseq_hdr_init(pool, mem); } static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size) { char *p = buf; int len; /* CSeq doesn't have compact form */ if ((pj_ssize_t)size < hdr->name.slen + hdr->method.name.slen + 15) return -1; pj_memcpy(p, hdr->name.ptr, hdr->name.slen); p += hdr->name.slen; *p++ = ':'; *p++ = ' '; len = pj_utoa(hdr->cseq, p); p += len; *p++ = ' '; pj_memcpy(p, hdr->method.name.ptr, hdr->method.name.slen); p += hdr->method.name.slen; *p = '\0'; return (int)(p-buf); } static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, const pjsip_cseq_hdr *rhs) { pjsip_cseq_hdr *hdr = pjsip_cseq_hdr_create(pool); hdr->cseq = rhs->cseq; pjsip_method_copy(pool, &hdr->method, &rhs->method); return hdr; } static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool, const pjsip_cseq_hdr *rhs ) { pjsip_cseq_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_cseq_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); return hdr; } /////////////////////////////////////////////////////////////////////////////// /* * Contact header. */ static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, pj_size_t size); static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool, const pjsip_contact_hdr *hdr); static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool, const pjsip_contact_hdr *); static pjsip_hdr_vptr contact_hdr_vptr = { (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_clone, (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_shallow_clone, (pjsip_hdr_print_fptr) &pjsip_contact_hdr_print, }; PJ_DEF(pjsip_contact_hdr*) pjsip_contact_hdr_init( pj_pool_t *pool, void *mem ) { pjsip_contact_hdr *hdr = (pjsip_contact_hdr*) mem; PJ_UNUSED_ARG(pool); pj_bzero(mem, sizeof(pjsip_contact_hdr)); init_hdr(hdr, PJSIP_H_CONTACT, &contact_hdr_vptr); hdr->expires = -1; pj_list_init(&hdr->other_param); return hdr; } PJ_DEF(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool ) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_contact_hdr)); return pjsip_contact_hdr_init(pool, mem); } static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, pj_size_t size) { const pj_str_t *hname = pjsip_use_compact_form? &hdr->sname : &hdr->name; const pjsip_parser_const_t *pc = pjsip_parser_const(); if (hdr->star) { char *p = buf; if ((pj_ssize_t)size < hname->slen + 6) return -1; pj_memcpy(p, hname->ptr, hname->slen); p += hname->slen; *p++ = ':'; *p++ = ' '; *p++ = '*'; return (int)(p - buf); } else { int printed; char *startbuf = buf; char *endbuf = buf + size; copy_advance(buf, (*hname)); copy_advance_char_check(buf, ':'); copy_advance_char_check(buf, ' '); printed = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, hdr->uri, buf, endbuf-buf); if (printed < 1) return -1; buf += printed; if (hdr->q1000) { unsigned frac; if (buf+19 >= endbuf) return -1; /* printed = sprintf(buf, ";q=%u.%03u", hdr->q1000/1000, hdr->q1000 % 1000); */ pj_memcpy(buf, ";q=", 3); printed = pj_utoa(hdr->q1000/1000, buf+3); buf += printed + 3; frac = hdr->q1000 % 1000; if (frac != 0) { *buf++ = '.'; if ((frac % 100)==0) frac /= 100; if ((frac % 10)==0) frac /= 10; printed = pj_utoa(frac, buf); buf += printed; } } if (hdr->expires >= 0) { if (buf+23 >= endbuf) return -1; pj_memcpy(buf, ";expires=", 9); printed = pj_utoa(hdr->expires, buf+9); buf += printed + 9; } printed = (int)pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ';'); if (printed < 0) return printed; buf += printed; return (int)(buf-startbuf); } } static pjsip_contact_hdr* pjsip_contact_hdr_clone(pj_pool_t *pool, const pjsip_contact_hdr *rhs) { pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(pool); hdr->star = rhs->star; if (hdr->star) return hdr; hdr->uri = (pjsip_uri*) pjsip_uri_clone(pool, rhs->uri); hdr->q1000 = rhs->q1000; hdr->expires = rhs->expires; pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param); return hdr; } static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool, const pjsip_contact_hdr *rhs) { pjsip_contact_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_contact_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param); return hdr; } /////////////////////////////////////////////////////////////////////////////// /* * Content-Type header.. */ static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, char *buf, pj_size_t size); static pjsip_ctype_hdr* pjsip_ctype_hdr_clone(pj_pool_t *pool, const pjsip_ctype_hdr *hdr); #define pjsip_ctype_hdr_shallow_clone pjsip_ctype_hdr_clone static pjsip_hdr_vptr ctype_hdr_vptr = { (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_clone, (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_shallow_clone, (pjsip_hdr_print_fptr) &pjsip_ctype_hdr_print, }; PJ_DEF(pjsip_ctype_hdr*) pjsip_ctype_hdr_init( pj_pool_t *pool, void *mem ) { pjsip_ctype_hdr *hdr = (pjsip_ctype_hdr*) mem; PJ_UNUSED_ARG(pool); pj_bzero(mem, sizeof(pjsip_ctype_hdr)); init_hdr(hdr, PJSIP_H_CONTENT_TYPE, &ctype_hdr_vptr); pj_list_init(&hdr->media.param); return hdr; } PJ_DEF(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool ) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_ctype_hdr)); return pjsip_ctype_hdr_init(pool, mem); } static int print_media_type(char *buf, unsigned len, const pjsip_media_type *media) { char *p = buf; pj_ssize_t printed; const pjsip_parser_const_t *pc; pj_memcpy(p, media->type.ptr, media->type.slen); p += media->type.slen; *p++ = '/'; pj_memcpy(p, media->subtype.ptr, media->subtype.slen); p += media->subtype.slen; pc = pjsip_parser_const(); printed = pjsip_param_print_on(&media->param, p, buf+len-p, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ';'); if (printed < 0) return -1; p += printed; return (int)(p-buf); } PJ_DEF(int) pjsip_media_type_print(char *buf, unsigned len, const pjsip_media_type *media) { return print_media_type(buf, len, media); } static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, char *buf, pj_size_t size) { char *p = buf; int len; const pj_str_t *hname = pjsip_use_compact_form? &hdr->sname : &hdr->name; if ((pj_ssize_t)size < hname->slen + hdr->media.type.slen + hdr->media.subtype.slen + 8) { return -1; } pj_memcpy(p, hname->ptr, hname->slen); p += hname->slen; *p++ = ':'; *p++ = ' '; len = print_media_type(p, (unsigned)(buf+size-p), &hdr->media); p += len; *p = '\0'; return (int)(p-buf); } static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool, const pjsip_ctype_hdr *rhs) { pjsip_ctype_hdr *hdr = pjsip_ctype_hdr_create(pool); pj_strdup(pool, &hdr->media.type, &rhs->media.type); pj_strdup(pool, &hdr->media.subtype, &rhs->media.subtype); pjsip_param_clone(pool, &hdr->media.param, &rhs->media.param); return hdr; } /////////////////////////////////////////////////////////////////////////////// /* * Expires header. */ PJ_DEF(pjsip_expires_hdr*) pjsip_expires_hdr_init( pj_pool_t *pool, void *mem, int value) { pjsip_expires_hdr *hdr = (pjsip_expires_hdr*) mem; PJ_UNUSED_ARG(pool); init_hdr(hdr, PJSIP_H_EXPIRES, &generic_int_hdr_vptr); hdr->ivalue = value; return hdr; } PJ_DEF(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool, int value ) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_expires_hdr)); return pjsip_expires_hdr_init(pool, mem, value); } /////////////////////////////////////////////////////////////////////////////// /* * To or From header. */ static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr, char *buf, pj_size_t size); static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool, const pjsip_fromto_hdr *hdr); static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool, const pjsip_fromto_hdr *hdr); static pjsip_hdr_vptr fromto_hdr_vptr = { (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_clone, (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_shallow_clone, (pjsip_hdr_print_fptr) &pjsip_fromto_hdr_print, }; PJ_DEF(pjsip_from_hdr*) pjsip_from_hdr_init( pj_pool_t *pool, void *mem ) { pjsip_from_hdr *hdr = (pjsip_from_hdr*) mem; PJ_UNUSED_ARG(pool); pj_bzero(mem, sizeof(pjsip_from_hdr)); init_hdr(hdr, PJSIP_H_FROM, &fromto_hdr_vptr); pj_list_init(&hdr->other_param); return hdr; } PJ_DEF(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool ) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_from_hdr)); return pjsip_from_hdr_init(pool, mem); } PJ_DEF(pjsip_to_hdr*) pjsip_to_hdr_init( pj_pool_t *pool, void *mem ) { pjsip_to_hdr *hdr = (pjsip_to_hdr*) mem; PJ_UNUSED_ARG(pool); pj_bzero(mem, sizeof(pjsip_to_hdr)); init_hdr(hdr, PJSIP_H_TO, &fromto_hdr_vptr); pj_list_init(&hdr->other_param); return hdr; } PJ_DEF(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool ) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_to_hdr)); return pjsip_to_hdr_init(pool, mem); } PJ_DEF(pjsip_from_hdr*) pjsip_fromto_hdr_set_from( pjsip_fromto_hdr *hdr ) { hdr->type = PJSIP_H_FROM; hdr->name.ptr = pjsip_hdr_names[PJSIP_H_FROM].name; hdr->name.slen = pjsip_hdr_names[PJSIP_H_FROM].name_len; hdr->sname.ptr = pjsip_hdr_names[PJSIP_H_FROM].sname; hdr->sname.slen = 1; return hdr; } PJ_DEF(pjsip_to_hdr*) pjsip_fromto_hdr_set_to( pjsip_fromto_hdr *hdr ) { hdr->type = PJSIP_H_TO; hdr->name.ptr = pjsip_hdr_names[PJSIP_H_TO].name; hdr->name.slen = pjsip_hdr_names[PJSIP_H_TO].name_len; hdr->sname.ptr = pjsip_hdr_names[PJSIP_H_TO].sname; hdr->sname.slen = 1; return hdr; } static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr, char *buf, pj_size_t size) { pj_ssize_t printed; char *startbuf = buf; char *endbuf = buf + size; const pj_str_t *hname = pjsip_use_compact_form? &hdr->sname : &hdr->name; const pjsip_parser_const_t *pc = pjsip_parser_const(); copy_advance(buf, (*hname)); *buf++ = ':'; *buf++ = ' '; printed = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, hdr->uri, buf, endbuf-buf); if (printed < 1) return -1; buf += printed; copy_advance_pair_escape(buf, ";tag=", 5, hdr->tag, pc->pjsip_TOKEN_SPEC); printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ';'); if (printed < 0) return -1; buf += printed; return (int)(buf-startbuf); } static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool, const pjsip_fromto_hdr *rhs) { pjsip_fromto_hdr *hdr = pjsip_from_hdr_create(pool); hdr->type = rhs->type; hdr->name = rhs->name; hdr->sname = rhs->sname; hdr->uri = (pjsip_uri*) pjsip_uri_clone(pool, rhs->uri); pj_strdup( pool, &hdr->tag, &rhs->tag); pjsip_param_clone( pool, &hdr->other_param, &rhs->other_param); return hdr; } static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool, const pjsip_fromto_hdr *rhs) { pjsip_fromto_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_fromto_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); pjsip_param_shallow_clone( pool, &hdr->other_param, &rhs->other_param); return hdr; } /////////////////////////////////////////////////////////////////////////////// /* * Max-Forwards header. */ PJ_DEF(pjsip_max_fwd_hdr*) pjsip_max_fwd_hdr_init( pj_pool_t *pool, void *mem, int value) { pjsip_max_fwd_hdr *hdr = (pjsip_max_fwd_hdr*) mem; PJ_UNUSED_ARG(pool); init_hdr(hdr, PJSIP_H_MAX_FORWARDS, &generic_int_hdr_vptr); hdr->ivalue = value; return hdr; } PJ_DEF(pjsip_max_fwd_hdr*) pjsip_max_fwd_hdr_create(pj_pool_t *pool, int value) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_max_fwd_hdr)); return pjsip_max_fwd_hdr_init(pool, mem, value); } /////////////////////////////////////////////////////////////////////////////// /* * Min-Expires header. */ PJ_DEF(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_init( pj_pool_t *pool, void *mem, int value ) { pjsip_min_expires_hdr *hdr = (pjsip_min_expires_hdr*) mem; PJ_UNUSED_ARG(pool); init_hdr(hdr, PJSIP_H_MIN_EXPIRES, &generic_int_hdr_vptr); hdr->ivalue = value; return hdr; } PJ_DEF(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool, int value ) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_min_expires_hdr)); return pjsip_min_expires_hdr_init(pool, mem, value ); } /////////////////////////////////////////////////////////////////////////////// /* * Record-Route and Route header. */ static int pjsip_routing_hdr_print( pjsip_routing_hdr *r, char *buf, pj_size_t size ); static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool, const pjsip_routing_hdr *r ); static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, const pjsip_routing_hdr *r ); static pjsip_hdr_vptr routing_hdr_vptr = { (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_clone, (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_shallow_clone, (pjsip_hdr_print_fptr) &pjsip_routing_hdr_print, }; PJ_DEF(pjsip_rr_hdr*) pjsip_rr_hdr_init( pj_pool_t *pool, void *mem ) { pjsip_rr_hdr *hdr = (pjsip_rr_hdr*) mem; PJ_UNUSED_ARG(pool); init_hdr(hdr, PJSIP_H_RECORD_ROUTE, &routing_hdr_vptr); pjsip_name_addr_init(&hdr->name_addr); pj_list_init(&hdr->other_param); return hdr; } PJ_DEF(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool ) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_rr_hdr)); return pjsip_rr_hdr_init(pool, mem); } PJ_DEF(pjsip_route_hdr*) pjsip_route_hdr_init( pj_pool_t *pool, void *mem ) { pjsip_route_hdr *hdr = (pjsip_route_hdr*) mem; PJ_UNUSED_ARG(pool); init_hdr(hdr, PJSIP_H_ROUTE, &routing_hdr_vptr); pjsip_name_addr_init(&hdr->name_addr); pj_list_init(&hdr->other_param); return hdr; } PJ_DEF(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool ) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_route_hdr)); return pjsip_route_hdr_init(pool, mem); } PJ_DEF(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *hdr ) { hdr->type = PJSIP_H_RECORD_ROUTE; hdr->name.ptr = pjsip_hdr_names[PJSIP_H_RECORD_ROUTE].name; hdr->name.slen = pjsip_hdr_names[PJSIP_H_RECORD_ROUTE].name_len; hdr->sname = hdr->name; return hdr; } PJ_DEF(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *hdr ) { hdr->type = PJSIP_H_ROUTE; hdr->name.ptr = pjsip_hdr_names[PJSIP_H_ROUTE].name; hdr->name.slen = pjsip_hdr_names[PJSIP_H_ROUTE].name_len; hdr->sname = hdr->name; return hdr; } static int pjsip_routing_hdr_print( pjsip_routing_hdr *hdr, char *buf, pj_size_t size ) { pj_ssize_t printed; char *startbuf = buf; char *endbuf = buf + size; const pjsip_parser_const_t *pc = pjsip_parser_const(); pjsip_sip_uri *sip_uri; pjsip_param *p; /* Check the proprietary param 'hide', don't print this header * if it exists in the route URI. */ if (PJSIP_URI_SCHEME_IS_SIPS(hdr->name_addr.uri) || PJSIP_URI_SCHEME_IS_SIP(hdr->name_addr.uri)) { sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->name_addr.uri); p = sip_uri->other_param.next; while (p != &sip_uri->other_param) { const pj_str_t st_hide = {"hide", 4}; if (pj_stricmp(&p->name, &st_hide) == 0) { /* Check if param 'hide' is specified without 'lr'. */ pj_assert(sip_uri->lr_param != 0); return 0; } p = p->next; } } /* Route and Record-Route don't compact forms */ copy_advance(buf, hdr->name); *buf++ = ':'; *buf++ = ' '; printed = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, &hdr->name_addr, buf, endbuf-buf); if (printed < 1) return -1; buf += printed; printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ';'); if (printed < 0) return -1; buf += printed; return (int)(buf-startbuf); } static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool, const pjsip_routing_hdr *rhs ) { pjsip_routing_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_routing_hdr); init_hdr(hdr, rhs->type, rhs->vptr); pjsip_name_addr_init(&hdr->name_addr); pjsip_name_addr_assign(pool, &hdr->name_addr, &rhs->name_addr); pjsip_param_clone( pool, &hdr->other_param, &rhs->other_param); return hdr; } static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, const pjsip_routing_hdr *rhs ) { pjsip_routing_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_routing_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); pjsip_param_shallow_clone( pool, &hdr->other_param, &rhs->other_param); return hdr; } /////////////////////////////////////////////////////////////////////////////// /* * Require header. */ PJ_DEF(pjsip_require_hdr*) pjsip_require_hdr_init( pj_pool_t *pool, void *mem ) { pjsip_require_hdr *hdr = (pjsip_require_hdr*) mem; PJ_UNUSED_ARG(pool); init_hdr(hdr, PJSIP_H_REQUIRE, &generic_array_hdr_vptr); hdr->count = 0; return hdr; } PJ_DEF(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_require_hdr)); return pjsip_require_hdr_init(pool, mem); } /////////////////////////////////////////////////////////////////////////////// /* * Retry-After header. */ static int pjsip_retry_after_hdr_print(pjsip_retry_after_hdr *r, char *buf, pj_size_t size ); static pjsip_retry_after_hdr* pjsip_retry_after_hdr_clone(pj_pool_t *pool, const pjsip_retry_after_hdr *r); static pjsip_retry_after_hdr* pjsip_retry_after_hdr_shallow_clone(pj_pool_t *pool, const pjsip_retry_after_hdr *r ); static pjsip_hdr_vptr retry_after_hdr_vptr = { (pjsip_hdr_clone_fptr) &pjsip_retry_after_hdr_clone, (pjsip_hdr_clone_fptr) &pjsip_retry_after_hdr_shallow_clone, (pjsip_hdr_print_fptr) &pjsip_retry_after_hdr_print, }; PJ_DEF(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_init( pj_pool_t *pool, void *mem, int value ) { pjsip_retry_after_hdr *hdr = (pjsip_retry_after_hdr*) mem; PJ_UNUSED_ARG(pool); init_hdr(hdr, PJSIP_H_RETRY_AFTER, &retry_after_hdr_vptr); hdr->ivalue = value; hdr->comment.slen = 0; pj_list_init(&hdr->param); return hdr; } PJ_DEF(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool, int value ) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_retry_after_hdr)); return pjsip_retry_after_hdr_init(pool, mem, value ); } static int pjsip_retry_after_hdr_print(pjsip_retry_after_hdr *hdr, char *buf, pj_size_t size) { char *p = buf; char *endbuf = buf + size; const pj_str_t *hname = &hdr->name; const pjsip_parser_const_t *pc = pjsip_parser_const(); pj_ssize_t printed; if ((pj_ssize_t)size < hdr->name.slen + 2+11) return -1; pj_memcpy(p, hdr->name.ptr, hdr->name.slen); p += hname->slen; *p++ = ':'; *p++ = ' '; p += pj_utoa(hdr->ivalue, p); if (hdr->comment.slen) { pj_bool_t enclosed; if (endbuf-p < hdr->comment.slen + 3) return -1; enclosed = (*hdr->comment.ptr == '('); if (!enclosed) *p++ = '('; pj_memcpy(p, hdr->comment.ptr, hdr->comment.slen); p += hdr->comment.slen; if (!enclosed) *p++ = ')'; if (!pj_list_empty(&hdr->param)) *p++ = ' '; } printed = pjsip_param_print_on(&hdr->param, p, endbuf-p, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ';'); if (printed < 0) return (int)printed; p += printed; return (int)(p - buf); } static pjsip_retry_after_hdr* pjsip_retry_after_hdr_clone(pj_pool_t *pool, const pjsip_retry_after_hdr *rhs) { pjsip_retry_after_hdr *hdr = pjsip_retry_after_hdr_create(pool, rhs->ivalue); pj_strdup(pool, &hdr->comment, &rhs->comment); pjsip_param_clone(pool, &hdr->param, &rhs->param); return hdr; } static pjsip_retry_after_hdr* pjsip_retry_after_hdr_shallow_clone(pj_pool_t *pool, const pjsip_retry_after_hdr *rhs) { pjsip_retry_after_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_retry_after_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); pjsip_param_shallow_clone(pool, &hdr->param, &rhs->param); return hdr; } /////////////////////////////////////////////////////////////////////////////// /* * Supported header. */ PJ_DEF(pjsip_supported_hdr*) pjsip_supported_hdr_init( pj_pool_t *pool, void *mem ) { pjsip_supported_hdr *hdr = (pjsip_supported_hdr*) mem; PJ_UNUSED_ARG(pool); init_hdr(hdr, PJSIP_H_SUPPORTED, &generic_array_hdr_vptr); hdr->count = 0; return hdr; } PJ_DEF(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_supported_hdr)); return pjsip_supported_hdr_init(pool, mem); } /////////////////////////////////////////////////////////////////////////////// /* * Unsupported header. */ PJ_DEF(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_init( pj_pool_t *pool, void *mem ) { pjsip_unsupported_hdr *hdr = (pjsip_unsupported_hdr*) mem; PJ_UNUSED_ARG(pool); init_hdr(hdr, PJSIP_H_UNSUPPORTED, &generic_array_hdr_vptr); hdr->count = 0; return hdr; } PJ_DEF(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_unsupported_hdr)); return pjsip_unsupported_hdr_init(pool, mem); } /////////////////////////////////////////////////////////////////////////////// /* * Via header. */ static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, char *buf, pj_size_t size); static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr); static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr ); static pjsip_hdr_vptr via_hdr_vptr = { (pjsip_hdr_clone_fptr) &pjsip_via_hdr_clone, (pjsip_hdr_clone_fptr) &pjsip_via_hdr_shallow_clone, (pjsip_hdr_print_fptr) &pjsip_via_hdr_print, }; PJ_DEF(pjsip_via_hdr*) pjsip_via_hdr_init( pj_pool_t *pool, void *mem ) { pjsip_via_hdr *hdr = (pjsip_via_hdr*) mem; PJ_UNUSED_ARG(pool); pj_bzero(mem, sizeof(pjsip_via_hdr)); init_hdr(hdr, PJSIP_H_VIA, &via_hdr_vptr); hdr->ttl_param = -1; hdr->rport_param = -1; pj_list_init(&hdr->other_param); return hdr; } PJ_DEF(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool ) { void *mem = pj_pool_alloc(pool, sizeof(pjsip_via_hdr)); return pjsip_via_hdr_init(pool, mem); } static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, char *buf, pj_size_t size) { pj_ssize_t printed; char *startbuf = buf; char *endbuf = buf + size; pj_str_t sip_ver = { "SIP/2.0/", 8 }; const pj_str_t *hname = pjsip_use_compact_form? &hdr->sname : &hdr->name; const pjsip_parser_const_t *pc = pjsip_parser_const(); if ((pj_ssize_t)size < hname->slen + sip_ver.slen + hdr->transport.slen + hdr->sent_by.host.slen + 12) { return -1; } /* pjsip_hdr_names */ copy_advance(buf, (*hname)); *buf++ = ':'; *buf++ = ' '; /* SIP/2.0/transport host:port */ pj_memcpy(buf, sip_ver.ptr, sip_ver.slen); buf += sip_ver.slen; //pj_memcpy(buf, hdr->transport.ptr, hdr->transport.slen); /* Convert transport type to UPPERCASE (some endpoints want that) */ { int i; for (i=0; itransport.slen; ++i) { buf[i] = (char)pj_toupper(hdr->transport.ptr[i]); } } buf += hdr->transport.slen; *buf++ = ' '; /* Check if host contains IPv6 */ if (pj_memchr(hdr->sent_by.host.ptr, ':', hdr->sent_by.host.slen)) { copy_advance_pair_quote_cond(buf, "", 0, hdr->sent_by.host, '[', ']'); } else { copy_advance_check(buf, hdr->sent_by.host); } if (hdr->sent_by.port != 0) { *buf++ = ':'; printed = pj_utoa(hdr->sent_by.port, buf); buf += printed; } if (hdr->ttl_param >= 0) { size = endbuf-buf; if (size < 14) return -1; pj_memcpy(buf, ";ttl=", 5); printed = pj_utoa(hdr->ttl_param, buf+5); buf += printed + 5; } if (hdr->rport_param >= 0) { size = endbuf-buf; if (size < 14) return -1; pj_memcpy(buf, ";rport", 6); buf += 6; if (hdr->rport_param > 0) { *buf++ = '='; buf += pj_utoa(hdr->rport_param, buf); } } if (hdr->maddr_param.slen) { /* Detect IPv6 IP address */ if (pj_memchr(hdr->maddr_param.ptr, ':', hdr->maddr_param.slen)) { copy_advance_pair_quote_cond(buf, ";maddr=", 7, hdr->maddr_param, '[', ']'); } else { copy_advance_pair(buf, ";maddr=", 7, hdr->maddr_param); } } copy_advance_pair(buf, ";received=", 10, hdr->recvd_param); copy_advance_pair_escape(buf, ";branch=", 8, hdr->branch_param, pc->pjsip_TOKEN_SPEC); printed = pjsip_param_print_on(&hdr->other_param, buf, endbuf-buf, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ';'); if (printed < 0) return -1; buf += printed; return (int)(buf-startbuf); } static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, const pjsip_via_hdr *rhs) { pjsip_via_hdr *hdr = pjsip_via_hdr_create(pool); pj_strdup(pool, &hdr->transport, &rhs->transport); pj_strdup(pool, &hdr->sent_by.host, &rhs->sent_by.host); hdr->sent_by.port = rhs->sent_by.port; hdr->ttl_param = rhs->ttl_param; hdr->rport_param = rhs->rport_param; pj_strdup(pool, &hdr->maddr_param, &rhs->maddr_param); pj_strdup(pool, &hdr->recvd_param, &rhs->recvd_param); pj_strdup(pool, &hdr->branch_param, &rhs->branch_param); pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param); return hdr; } static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool, const pjsip_via_hdr *rhs ) { pjsip_via_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_via_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param); return hdr; } /////////////////////////////////////////////////////////////////////////////// /* * Warning header. */ PJ_DEF(pjsip_warning_hdr*) pjsip_warning_hdr_create( pj_pool_t *pool, int code, const pj_str_t *host, const pj_str_t *text) { const pj_str_t str_warning = { "Warning", 7 }; pj_str_t hvalue; hvalue.ptr = (char*) pj_pool_alloc(pool, 10 + /* code */ host->slen + 2 + /* host */ text->slen + 2); /* text */ hvalue.slen = pj_ansi_sprintf(hvalue.ptr, "%u %.*s \"%.*s\"", code, (int)host->slen, host->ptr, (int)text->slen, text->ptr); return pjsip_generic_string_hdr_create(pool, &str_warning, &hvalue); } PJ_DEF(pjsip_warning_hdr*) pjsip_warning_hdr_create_from_status(pj_pool_t *pool, const pj_str_t *host, pj_status_t status) { char errbuf[PJ_ERR_MSG_SIZE]; pj_str_t text; text = pj_strerror(status, errbuf, sizeof(errbuf)); return pjsip_warning_hdr_create(pool, 399, host, &text); } /////////////////////////////////////////////////////////////////////////////// /* * Message body manipulations. */ PJ_DEF(int) pjsip_print_body(pjsip_msg_body *msg_body, char **buf, int *len) { static char s_buf[PJSIP_MAX_PKT_LEN]; int res; res = (*msg_body->print_body)(msg_body, s_buf, PJSIP_MAX_PKT_LEN); if (res < 0) { return -1; } *buf = s_buf; *len = res; return 0; } PJ_DEF(int) pjsip_print_text_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size) { if (size < msg_body->len) return -1; pj_memcpy(buf, msg_body->data, msg_body->len); return msg_body->len; } PJ_DEF(void*) pjsip_clone_text_data( pj_pool_t *pool, const void *data, unsigned len) { char *newdata = ""; if (len) { newdata = (char*) pj_pool_alloc(pool, len); pj_memcpy(newdata, data, len); } return newdata; } PJ_DEF(pj_status_t) pjsip_msg_body_copy( pj_pool_t *pool, pjsip_msg_body *dst_body, const pjsip_msg_body *src_body ) { /* First check if clone_data field is initialized. */ PJ_ASSERT_RETURN( src_body->clone_data!=NULL, PJ_EINVAL ); /* Duplicate content-type */ pjsip_media_type_cp(pool, &dst_body->content_type, &src_body->content_type); /* Duplicate data. */ dst_body->data = (*src_body->clone_data)(pool, src_body->data, src_body->len ); /* Length. */ dst_body->len = src_body->len; /* Function pointers. */ dst_body->print_body = src_body->print_body; dst_body->clone_data = src_body->clone_data; return PJ_SUCCESS; } PJ_DEF(pjsip_msg_body*) pjsip_msg_body_clone( pj_pool_t *pool, const pjsip_msg_body *body ) { pjsip_msg_body *new_body; pj_status_t status; new_body = PJ_POOL_ALLOC_T(pool, pjsip_msg_body); PJ_ASSERT_RETURN(new_body, NULL); status = pjsip_msg_body_copy(pool, new_body, body); return (status==PJ_SUCCESS) ? new_body : NULL; } PJ_DEF(pjsip_msg_body*) pjsip_msg_body_create( pj_pool_t *pool, const pj_str_t *type, const pj_str_t *subtype, const pj_str_t *text ) { pjsip_msg_body *body; PJ_ASSERT_RETURN(pool && type && subtype && text, NULL); body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body); PJ_ASSERT_RETURN(body != NULL, NULL); pj_strdup(pool, &body->content_type.type, type); pj_strdup(pool, &body->content_type.subtype, subtype); pj_list_init(&body->content_type.param); body->data = pj_pool_alloc(pool, text->slen); pj_memcpy(body->data, text->ptr, text->slen); body->len = (unsigned)text->slen; body->clone_data = &pjsip_clone_text_data; body->print_body = &pjsip_print_text_body; return body; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_multipart.c ================================================ /* $Id: sip_multipart.c 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "sip_multipart.c" #define IS_SPACE(c) ((c)==' ' || (c)=='\t') #if 0 # define TRACE_(x) PJ_LOG(4,x) #else # define TRACE_(x) #endif extern pj_bool_t pjsip_use_compact_form; /* Type of "data" in multipart pjsip_msg_body */ struct multipart_data { pj_str_t boundary; pjsip_multipart_part part_head; }; static int multipart_print_body(struct pjsip_msg_body *msg_body, char *buf, pj_size_t size) { const struct multipart_data *m_data; pj_str_t clen_hdr = { "Content-Length: ", 16}; pjsip_multipart_part *part; char *p = buf, *end = buf+size; #define SIZE_LEFT() (end-p) m_data = (const struct multipart_data*)msg_body->data; PJ_ASSERT_RETURN(m_data && !pj_list_empty(&m_data->part_head), PJ_EINVAL); part = m_data->part_head.next; while (part != &m_data->part_head) { enum { CLEN_SPACE = 5 }; char *clen_pos; const pjsip_hdr *hdr; clen_pos = NULL; /* Print delimiter */ if (SIZE_LEFT() <= (m_data->boundary.slen+8) << 1) return -1; *p++ = 13; *p++ = 10; *p++ = '-'; *p++ = '-'; pj_memcpy(p, m_data->boundary.ptr, m_data->boundary.slen); p += m_data->boundary.slen; *p++ = 13; *p++ = 10; /* Print optional headers */ hdr = part->hdr.next; while (hdr != &part->hdr) { int printed = pjsip_hdr_print_on((pjsip_hdr*)hdr, p, SIZE_LEFT()-2); if (printed < 0) return -1; p += printed; *p++ = '\r'; *p++ = '\n'; hdr = hdr->next; } /* Automaticly adds Content-Type and Content-Length headers, only * if content_type is set in the message body. */ if (part->body && part->body->content_type.type.slen) { pj_str_t ctype_hdr = { "Content-Type: ", 14}; const pjsip_media_type *media = &part->body->content_type; if (pjsip_use_compact_form) { ctype_hdr.ptr = "c: "; ctype_hdr.slen = 3; } /* Add Content-Type header. */ if ( (end-p) < 24 + media->type.slen + media->subtype.slen) { return -1; } pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen); p += ctype_hdr.slen; p += pjsip_media_type_print(p, (unsigned)(end-p), media); *p++ = '\r'; *p++ = '\n'; /* Add Content-Length header. */ if ((end-p) < clen_hdr.slen + 12 + 2) { return -1; } pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen); p += clen_hdr.slen; /* Print blanks after "Content-Length:", this is where we'll put * the content length value after we know the length of the * body. */ pj_memset(p, ' ', CLEN_SPACE); clen_pos = p; p += CLEN_SPACE; *p++ = '\r'; *p++ = '\n'; } /* Empty newline */ *p++ = 13; *p++ = 10; /* Print the body */ pj_assert(part->body != NULL); if (part->body) { int printed = part->body->print_body(part->body, p, SIZE_LEFT()); if (printed < 0) return -1; p += printed; /* Now that we have the length of the body, print this to the * Content-Length header. */ if (clen_pos) { char tmp[16]; int len; len = pj_utoa(printed, tmp); if (len > CLEN_SPACE) len = CLEN_SPACE; pj_memcpy(clen_pos+CLEN_SPACE-len, tmp, len); } } part = part->next; } /* Print closing delimiter */ if (SIZE_LEFT() < m_data->boundary.slen+8) return -1; *p++ = 13; *p++ = 10; *p++ = '-'; *p++ = '-'; pj_memcpy(p, m_data->boundary.ptr, m_data->boundary.slen); p += m_data->boundary.slen; *p++ = '-'; *p++ = '-'; *p++ = 13; *p++ = 10; #undef SIZE_LEFT return (int)(p - buf); } static void* multipart_clone_data(pj_pool_t *pool, const void *data, unsigned len) { const struct multipart_data *src; struct multipart_data *dst; const pjsip_multipart_part *src_part; PJ_UNUSED_ARG(len); src = (const struct multipart_data*) data; dst = PJ_POOL_ALLOC_T(pool, struct multipart_data); pj_list_init(&dst->part_head); pj_strdup(pool, &dst->boundary, &src->boundary); src_part = src->part_head.next; while (src_part != &src->part_head) { pjsip_multipart_part *dst_part; const pjsip_hdr *src_hdr; dst_part = pjsip_multipart_create_part(pool); src_hdr = src_part->hdr.next; while (src_hdr != &src_part->hdr) { pjsip_hdr *dst_hdr = (pjsip_hdr*)pjsip_hdr_clone(pool, src_hdr); pj_list_push_back(&dst_part->hdr, dst_hdr); src_hdr = src_hdr->next; } dst_part->body = pjsip_msg_body_clone(pool, src_part->body); pj_list_push_back(&dst->part_head, dst_part); src_part = src_part->next; } return (void*)dst; } /* * Create an empty multipart body. */ PJ_DEF(pjsip_msg_body*) pjsip_multipart_create( pj_pool_t *pool, const pjsip_media_type *ctype, const pj_str_t *boundary) { pjsip_msg_body *body; pjsip_param *ctype_param; struct multipart_data *mp_data; pj_str_t STR_BOUNDARY = { "boundary", 8 }; PJ_ASSERT_RETURN(pool, NULL); body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body); /* content-type */ if (ctype && ctype->type.slen) { pjsip_media_type_cp(pool, &body->content_type, ctype); } else { pj_str_t STR_MULTIPART = {"multipart", 9}; pj_str_t STR_MIXED = { "mixed", 5 }; pjsip_media_type_init(&body->content_type, &STR_MULTIPART, &STR_MIXED); } /* multipart data */ mp_data = PJ_POOL_ZALLOC_T(pool, struct multipart_data); pj_list_init(&mp_data->part_head); if (boundary) { pj_strdup(pool, &mp_data->boundary, boundary); } else { pj_create_unique_string(pool, &mp_data->boundary); } body->data = mp_data; /* Add ";boundary" parameter to content_type parameter. */ ctype_param = pjsip_param_find(&body->content_type.param, &STR_BOUNDARY); if (!ctype_param) { ctype_param = PJ_POOL_ALLOC_T(pool, pjsip_param); ctype_param->name = STR_BOUNDARY; pj_list_push_back(&body->content_type.param, ctype_param); } ctype_param->value = mp_data->boundary; /* function pointers */ body->print_body = &multipart_print_body; body->clone_data = &multipart_clone_data; return body; } /* * Create an empty multipart part. */ PJ_DEF(pjsip_multipart_part*) pjsip_multipart_create_part(pj_pool_t *pool) { pjsip_multipart_part *mp; mp = PJ_POOL_ZALLOC_T(pool, pjsip_multipart_part); pj_list_init(&mp->hdr); return mp; } /* * Deep clone. */ PJ_DEF(pjsip_multipart_part*) pjsip_multipart_clone_part(pj_pool_t *pool, const pjsip_multipart_part *src) { pjsip_multipart_part *dst; const pjsip_hdr *hdr; dst = pjsip_multipart_create_part(pool); hdr = src->hdr.next; while (hdr != &src->hdr) { pj_list_push_back(&dst->hdr, pjsip_hdr_clone(pool, hdr)); hdr = hdr->next; } dst->body = pjsip_msg_body_clone(pool, src->body); return dst; } /* * Add a part into multipart bodies. */ PJ_DEF(pj_status_t) pjsip_multipart_add_part( pj_pool_t *pool, pjsip_msg_body *mp, pjsip_multipart_part *part) { struct multipart_data *m_data; /* All params must be specified */ PJ_ASSERT_RETURN(pool && mp && part, PJ_EINVAL); /* mp must really point to an actual multipart msg body */ PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, PJ_EINVAL); /* The multipart part must contain a valid message body */ PJ_ASSERT_RETURN(part->body && part->body->print_body, PJ_EINVAL); m_data = (struct multipart_data*)mp->data; pj_list_push_back(&m_data->part_head, part); PJ_UNUSED_ARG(pool); return PJ_SUCCESS; } /* * Get the first part of multipart bodies. */ PJ_DEF(pjsip_multipart_part*) pjsip_multipart_get_first_part(const pjsip_msg_body *mp) { struct multipart_data *m_data; /* Must specify mandatory params */ PJ_ASSERT_RETURN(mp, NULL); /* mp must really point to an actual multipart msg body */ PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, NULL); m_data = (struct multipart_data*)mp->data; if (pj_list_empty(&m_data->part_head)) return NULL; return m_data->part_head.next; } /* * Get the next part after the specified part. */ PJ_DEF(pjsip_multipart_part*) pjsip_multipart_get_next_part(const pjsip_msg_body *mp, pjsip_multipart_part *part) { struct multipart_data *m_data; /* Must specify mandatory params */ PJ_ASSERT_RETURN(mp && part, NULL); /* mp must really point to an actual multipart msg body */ PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, NULL); m_data = (struct multipart_data*)mp->data; /* the part parameter must be really member of the list */ PJ_ASSERT_RETURN(pj_list_find_node(&m_data->part_head, part) != NULL, NULL); if (part->next == &m_data->part_head) return NULL; return part->next; } /* * Find a body inside multipart bodies which has the specified content type. */ PJ_DEF(pjsip_multipart_part*) pjsip_multipart_find_part( const pjsip_msg_body *mp, const pjsip_media_type *content_type, const pjsip_multipart_part *start) { struct multipart_data *m_data; pjsip_multipart_part *part; /* Must specify mandatory params */ PJ_ASSERT_RETURN(mp && content_type, NULL); /* mp must really point to an actual multipart msg body */ PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, NULL); m_data = (struct multipart_data*)mp->data; if (start) part = start->next; else part = m_data->part_head.next; while (part != &m_data->part_head) { if (pjsip_media_type_cmp(&part->body->content_type, content_type, 0)==0) { return part; } part = part->next; } return NULL; } /* Parse a multipart part. "pct" is parent content-type */ static pjsip_multipart_part *parse_multipart_part(pj_pool_t *pool, char *start, pj_size_t len, const pjsip_media_type *pct) { pjsip_multipart_part *part = pjsip_multipart_create_part(pool); char *p = start, *end = start+len, *end_hdr = NULL, *start_body = NULL; pjsip_ctype_hdr *ctype_hdr = NULL; TRACE_((THIS_FILE, "Parsing part: begin--\n%.*s\n--end", (int)len, start)); /* Find the end of header area, by looking at an empty line */ for (;;) { while (p!=end && *p!='\n') ++p; if (p==end) { start_body = end; break; } if ((p==start) || (p==start+1 && *(p-1)=='\r')) { /* Empty header section */ end_hdr = start; start_body = ++p; break; } else if (p==end-1) { /* Empty body section */ end_hdr = end; start_body = ++p; } else if ((p>=start+1 && *(p-1)=='\n') || (p>=start+2 && *(p-1)=='\r' && *(p-2)=='\n')) { /* Found it */ end_hdr = (*(p-1)=='\r') ? (p-1) : p; start_body = ++p; break; } else { ++p; } } /* Parse the headers */ if (end_hdr-start > 0) { pjsip_hdr *hdr; pj_status_t status; status = pjsip_parse_headers(pool, start, end_hdr-start, &part->hdr, 0); if (status != PJ_SUCCESS) { PJ_PERROR(2,(THIS_FILE, status, "Warning: error parsing multipart" " header")); } /* Find Content-Type header */ hdr = part->hdr.next; while (hdr != &part->hdr) { TRACE_((THIS_FILE, "Header parsed: %.*s", (int)hdr->name.slen, hdr->name.ptr)); if (hdr->type == PJSIP_H_CONTENT_TYPE) { ctype_hdr = (pjsip_ctype_hdr*)hdr; } hdr = hdr->next; } } /* Assign the body */ part->body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body); if (ctype_hdr) { pjsip_media_type_cp(pool, &part->body->content_type, &ctype_hdr->media); } else if (pct && pj_stricmp2(&pct->subtype, "digest")==0) { part->body->content_type.type = pj_str("message"); part->body->content_type.subtype = pj_str("rfc822"); } else { part->body->content_type.type = pj_str("text"); part->body->content_type.subtype = pj_str("plain"); } if (start_body < end) { part->body->data = start_body; part->body->len = (unsigned)(end - start_body); } else { part->body->data = (void*)""; part->body->len = 0; } TRACE_((THIS_FILE, "Body parsed: \"%.*s\"", (int)part->body->len, part->body->data)); part->body->print_body = &pjsip_print_text_body; part->body->clone_data = &pjsip_clone_text_data; return part; } /* Public function to parse multipart message bodies into its parts */ PJ_DEF(pjsip_msg_body*) pjsip_multipart_parse(pj_pool_t *pool, char *buf, pj_size_t len, const pjsip_media_type *ctype, unsigned options) { pj_str_t boundary, delim; char *curptr, *endptr; const pjsip_param *ctype_param; const pj_str_t STR_BOUNDARY = { "boundary", 8 }; pjsip_msg_body *body = NULL; PJ_ASSERT_RETURN(pool && buf && len && ctype && !options, NULL); TRACE_((THIS_FILE, "Started parsing multipart body")); /* Get the boundary value in the ctype */ boundary.ptr = NULL; boundary.slen = 0; ctype_param = pjsip_param_find(&ctype->param, &STR_BOUNDARY); if (ctype_param) { boundary = ctype_param->value; if (boundary.slen>2 && *boundary.ptr=='"') { /* Remove quote */ boundary.ptr++; boundary.slen -= 2; } TRACE_((THIS_FILE, "Boundary is specified: '%.*s'", (int)boundary.slen, boundary.ptr)); } if (!boundary.slen) { /* Boundary not found or not specified. Try to be clever, get * the boundary from the body. */ char *p=buf, *end=buf+len; PJ_LOG(4,(THIS_FILE, "Warning: boundary parameter not found or " "not specified when parsing multipart body")); /* Find the first "--". This "--" must be right after a CRLF, unless * it really appears at the start of the buffer. */ for (;;) { while (p!=end && *p!='-') ++p; if (p == end) break; if ((p+1buf && *(p-1)=='\n') || (p==buf))) { p+=2; break; } else { ++p; } } if (p==end) { /* Unable to determine boundary. Maybe this is not a multipart * message? */ PJ_LOG(4,(THIS_FILE, "Error: multipart boundary not specified and" " unable to calculate from the body")); return NULL; } boundary.ptr = p; while (p!=end && !pj_isspace(*p)) ++p; boundary.slen = p - boundary.ptr; TRACE_((THIS_FILE, "Boundary is calculated: '%.*s'", (int)boundary.slen, boundary.ptr)); } /* Build the delimiter: * delimiter = "--" boundary */ delim.slen = boundary.slen+2; delim.ptr = (char*)pj_pool_alloc(pool, (int)delim.slen); delim.ptr[0] = '-'; delim.ptr[1] = '-'; pj_memcpy(delim.ptr+2, boundary.ptr, boundary.slen); /* Start parsing the body, skip until the first delimiter. */ curptr = buf; endptr = buf + len; { pj_str_t strbody; strbody.ptr = buf; strbody.slen = len; curptr = pj_strstr(&strbody, &delim); if (!curptr) return NULL; } body = pjsip_multipart_create(pool, ctype, &boundary); for (;;) { char *start_body, *end_body; pjsip_multipart_part *part; /* Eat the boundary */ curptr += delim.slen; if (*curptr=='-' && curptr * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include /* rdata structure */ #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "sip_parser.c" #define ALNUM #define RESERVED ";/?:@&=+$," #define MARK "-_.!~*'()" #define UNRESERVED ALNUM MARK #define ESCAPED "%" #define USER_UNRESERVED "&=+$,;?/" #define PASS "&=+$," #define TOKEN "-.!%*_`'~+" /* '=' was removed for parsing * param */ #define HOST "_-." #define HEX_DIGIT "abcdefABCDEF" #define PARAM_CHAR "[]/:&+$" UNRESERVED ESCAPED #define HNV_UNRESERVED "[]/?:+$" #define HDR_CHAR HNV_UNRESERVED UNRESERVED ESCAPED /* A generic URI can consist of (For a complete BNF see RFC 2396): #?;:@&=+-_.!~*'()%$,/ */ #define GENERIC_URI_CHARS "#?;:@&=+-_.!~*'()%$,/" "%" #define UNREACHED(expr) #define IS_NEWLINE(c) ((c)=='\r' || (c)=='\n') #define IS_SPACE(c) ((c)==' ' || (c)=='\t') /* * Header parser records. */ typedef struct handler_rec { char hname[PJSIP_MAX_HNAME_LEN+1]; pj_size_t hname_len; pj_uint32_t hname_hash; pjsip_parse_hdr_func *handler; } handler_rec; static handler_rec handler[PJSIP_MAX_HEADER_TYPES]; static unsigned handler_count; static int parser_is_initialized; /* * URI parser records. */ typedef struct uri_parser_rec { pj_str_t scheme; pjsip_parse_uri_func *parse; } uri_parser_rec; static uri_parser_rec uri_handler[PJSIP_MAX_URI_TYPES]; static unsigned uri_handler_count; /* * Global vars (also extern). */ int PJSIP_SYN_ERR_EXCEPTION = -1; /* Parser constants */ static pjsip_parser_const_t pconst = { { "user", 4}, /* pjsip_USER_STR */ { "method", 6}, /* pjsip_METHOD_STR */ { "transport", 9}, /* pjsip_TRANSPORT_STR */ { "maddr", 5 }, /* pjsip_MADDR_STR */ { "lr", 2 }, /* pjsip_LR_STR */ { "sip", 3 }, /* pjsip_SIP_STR */ { "sips", 4 }, /* pjsip_SIPS_STR */ { "tel", 3 }, /* pjsip_TEL_STR */ { "branch", 6 }, /* pjsip_BRANCH_STR */ { "ttl", 3 }, /* pjsip_TTL_STR */ { "received", 8 }, /* pjsip_RECEIVED_STR */ { "q", 1 }, /* pjsip_Q_STR */ { "expires", 7 }, /* pjsip_EXPIRES_STR */ { "tag", 3 }, /* pjsip_TAG_STR */ { "rport", 5} /* pjsip_RPORT_STR */ }; /* Character Input Specification buffer. */ static pj_cis_buf_t cis_buf; /* * Forward decl. */ static pjsip_msg * int_parse_msg( pjsip_parse_ctx *ctx, pjsip_parser_err_report *err_list); static void int_parse_param( pj_scanner *scanner, pj_pool_t *pool, pj_str_t *pname, pj_str_t *pvalue, unsigned option); static void int_parse_uri_param( pj_scanner *scanner, pj_pool_t *pool, pj_str_t *pname, pj_str_t *pvalue, unsigned option); static void int_parse_hparam( pj_scanner *scanner, pj_pool_t *pool, pj_str_t *hname, pj_str_t *hvalue ); static void int_parse_req_line( pj_scanner *scanner, pj_pool_t *pool, pjsip_request_line *req_line); static int int_is_next_user( pj_scanner *scanner); static void int_parse_status_line( pj_scanner *scanner, pjsip_status_line *line); static void int_parse_user_pass( pj_scanner *scanner, pj_pool_t *pool, pj_str_t *user, pj_str_t *pass); static void int_parse_uri_host_port( pj_scanner *scanner, pj_str_t *p_host, int *p_port); static pjsip_uri * int_parse_uri_or_name_addr( pj_scanner *scanner, pj_pool_t *pool, unsigned option); static void* int_parse_sip_url( pj_scanner *scanner, pj_pool_t *pool, pj_bool_t parse_params); static pjsip_name_addr * int_parse_name_addr( pj_scanner *scanner, pj_pool_t *pool ); static void* int_parse_other_uri(pj_scanner *scanner, pj_pool_t *pool, pj_bool_t parse_params); static void parse_hdr_end( pj_scanner *scanner ); static pjsip_hdr* parse_hdr_accept( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_allow( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_call_id( pjsip_parse_ctx *ctx); static pjsip_hdr* parse_hdr_contact( pjsip_parse_ctx *ctx); static pjsip_hdr* parse_hdr_content_len( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_expires( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_from( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_max_forwards( pjsip_parse_ctx *ctx); static pjsip_hdr* parse_hdr_min_expires( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_rr( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_route( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_require( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_retry_after( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_supported( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_to( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_unsupported( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx ); static pjsip_hdr* parse_hdr_generic_string( pjsip_parse_ctx *ctx); /* Convert non NULL terminated string to integer. */ static unsigned long pj_strtoul_mindigit(const pj_str_t *str, unsigned mindig) { unsigned long value; unsigned i; value = 0; for (i=0; i<(unsigned)str->slen; ++i) { value = value * 10 + (str->ptr[i] - '0'); } for (; islen + pname->slen + pvalue->slen + 3; p = new_param = (char*) pj_pool_alloc(pool, len); if (param->slen) { pj_size_t old_len = param->slen; pj_memcpy(p, param->ptr, old_len); p += old_len; } *p++ = (char)sepchar; pj_memcpy(p, pname->ptr, pname->slen); p += pname->slen; if (pvalue->slen) { *p++ = '='; pj_memcpy(p, pvalue->ptr, pvalue->slen); p += pvalue->slen; } *p = '\0'; param->ptr = new_param; param->slen = p - new_param; } /* Initialize static properties of the parser. */ static pj_status_t init_parser() { pj_status_t status; /* * Syntax error exception number. */ pj_assert (PJSIP_SYN_ERR_EXCEPTION == -1); status = pj_exception_id_alloc("PJSIP syntax error", &PJSIP_SYN_ERR_EXCEPTION); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* * Init character input spec (cis) */ pj_cis_buf_init(&cis_buf); status = pj_cis_init(&cis_buf, &pconst.pjsip_DIGIT_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_num(&pconst.pjsip_DIGIT_SPEC); status = pj_cis_init(&cis_buf, &pconst.pjsip_ALPHA_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_alpha( &pconst.pjsip_ALPHA_SPEC ); status = pj_cis_init(&cis_buf, &pconst.pjsip_ALNUM_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_alpha( &pconst.pjsip_ALNUM_SPEC ); pj_cis_add_num( &pconst.pjsip_ALNUM_SPEC ); status = pj_cis_init(&cis_buf, &pconst.pjsip_NOT_NEWLINE); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str(&pconst.pjsip_NOT_NEWLINE, "\r\n"); pj_cis_invert(&pconst.pjsip_NOT_NEWLINE); status = pj_cis_init(&cis_buf, &pconst.pjsip_NOT_COMMA_OR_NEWLINE); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str( &pconst.pjsip_NOT_COMMA_OR_NEWLINE, ",\r\n"); pj_cis_invert(&pconst.pjsip_NOT_COMMA_OR_NEWLINE); status = pj_cis_dup(&pconst.pjsip_TOKEN_SPEC, &pconst.pjsip_ALNUM_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str( &pconst.pjsip_TOKEN_SPEC, TOKEN); status = pj_cis_dup(&pconst.pjsip_TOKEN_SPEC_ESC, &pconst.pjsip_TOKEN_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_del_str(&pconst.pjsip_TOKEN_SPEC_ESC, "%"); status = pj_cis_dup(&pconst.pjsip_VIA_PARAM_SPEC, &pconst.pjsip_TOKEN_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str(&pconst.pjsip_VIA_PARAM_SPEC, ":"); status = pj_cis_dup(&pconst.pjsip_VIA_PARAM_SPEC_ESC, &pconst.pjsip_TOKEN_SPEC_ESC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str(&pconst.pjsip_VIA_PARAM_SPEC, ":"); status = pj_cis_dup(&pconst.pjsip_HOST_SPEC, &pconst.pjsip_ALNUM_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str( &pconst.pjsip_HOST_SPEC, HOST); status = pj_cis_dup(&pconst.pjsip_HEX_SPEC, &pconst.pjsip_DIGIT_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str( &pconst.pjsip_HEX_SPEC, HEX_DIGIT); status = pj_cis_dup(&pconst.pjsip_PARAM_CHAR_SPEC, &pconst.pjsip_ALNUM_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str(&pconst.pjsip_PARAM_CHAR_SPEC, PARAM_CHAR); status = pj_cis_dup(&pconst.pjsip_PARAM_CHAR_SPEC_ESC, &pconst.pjsip_PARAM_CHAR_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_del_str(&pconst.pjsip_PARAM_CHAR_SPEC_ESC, ESCAPED); status = pj_cis_dup(&pconst.pjsip_HDR_CHAR_SPEC, &pconst.pjsip_ALNUM_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str(&pconst.pjsip_HDR_CHAR_SPEC, HDR_CHAR); status = pj_cis_dup(&pconst.pjsip_HDR_CHAR_SPEC_ESC, &pconst.pjsip_HDR_CHAR_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_del_str(&pconst.pjsip_HDR_CHAR_SPEC_ESC, ESCAPED); status = pj_cis_dup(&pconst.pjsip_USER_SPEC, &pconst.pjsip_ALNUM_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str( &pconst.pjsip_USER_SPEC, UNRESERVED ESCAPED USER_UNRESERVED ); status = pj_cis_dup(&pconst.pjsip_USER_SPEC_ESC, &pconst.pjsip_USER_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_del_str( &pconst.pjsip_USER_SPEC_ESC, ESCAPED); status = pj_cis_dup(&pconst.pjsip_USER_SPEC_LENIENT, &pconst.pjsip_USER_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str(&pconst.pjsip_USER_SPEC_LENIENT, "#"); status = pj_cis_dup(&pconst.pjsip_USER_SPEC_LENIENT_ESC, &pconst.pjsip_USER_SPEC_ESC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str(&pconst.pjsip_USER_SPEC_LENIENT_ESC, "#"); status = pj_cis_dup(&pconst.pjsip_PASSWD_SPEC, &pconst.pjsip_ALNUM_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str( &pconst.pjsip_PASSWD_SPEC, UNRESERVED ESCAPED PASS); status = pj_cis_dup(&pconst.pjsip_PASSWD_SPEC_ESC, &pconst.pjsip_PASSWD_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_del_str( &pconst.pjsip_PASSWD_SPEC_ESC, ESCAPED); status = pj_cis_init(&cis_buf, &pconst.pjsip_PROBE_USER_HOST_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str( &pconst.pjsip_PROBE_USER_HOST_SPEC, "@ \n>"); pj_cis_invert( &pconst.pjsip_PROBE_USER_HOST_SPEC ); status = pj_cis_init(&cis_buf, &pconst.pjsip_DISPLAY_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str( &pconst.pjsip_DISPLAY_SPEC, ":\r\n<"); pj_cis_invert(&pconst.pjsip_DISPLAY_SPEC); status = pj_cis_dup(&pconst.pjsip_OTHER_URI_CONTENT, &pconst.pjsip_ALNUM_SPEC); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); pj_cis_add_str( &pconst.pjsip_OTHER_URI_CONTENT, GENERIC_URI_CHARS); /* * Register URI parsers. */ status = pjsip_register_uri_parser("sip", &int_parse_sip_url); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_uri_parser("sips", &int_parse_sip_url); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* * Register header parsers. */ status = pjsip_register_hdr_parser( "Accept", NULL, &parse_hdr_accept); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Allow", NULL, &parse_hdr_allow); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Call-ID", "i", &parse_hdr_call_id); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Contact", "m", &parse_hdr_contact); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Content-Length", "l", &parse_hdr_content_len); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Content-Type", "c", &parse_hdr_content_type); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "CSeq", NULL, &parse_hdr_cseq); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Expires", NULL, &parse_hdr_expires); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "From", "f", &parse_hdr_from); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Max-Forwards", NULL, &parse_hdr_max_forwards); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Min-Expires", NULL, &parse_hdr_min_expires); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Record-Route", NULL, &parse_hdr_rr); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Route", NULL, &parse_hdr_route); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Require", NULL, &parse_hdr_require); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Retry-After", NULL, &parse_hdr_retry_after); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Supported", "k", &parse_hdr_supported); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "To", "t", &parse_hdr_to); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Unsupported", NULL, &parse_hdr_unsupported); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); status = pjsip_register_hdr_parser( "Via", "v", &parse_hdr_via); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* * Register auth parser. */ status = pjsip_auth_init_parser(); return status; } void init_sip_parser(void) { pj_enter_critical_section(); if (++parser_is_initialized == 1) { init_parser(); } pj_leave_critical_section(); } void deinit_sip_parser(void) { pj_enter_critical_section(); if (--parser_is_initialized == 0) { /* Clear header handlers */ pj_bzero(handler, sizeof(handler)); handler_count = 0; /* Clear URI handlers */ pj_bzero(uri_handler, sizeof(uri_handler)); uri_handler_count = 0; /* Deregister exception ID */ pj_exception_id_free(PJSIP_SYN_ERR_EXCEPTION); PJSIP_SYN_ERR_EXCEPTION = -1; } pj_leave_critical_section(); } /* Compare the handler record with header name, and return: * - 0 if handler match. * - <0 if handler is 'less' than the header name. * - >0 if handler is 'greater' than header name. */ PJ_INLINE(int) compare_handler( const handler_rec *r1, const char *name, pj_size_t name_len, pj_uint32_t hash ) { PJ_UNUSED_ARG(name_len); /* Compare hashed value. */ if (r1->hname_hash < hash) return -1; if (r1->hname_hash > hash) return 1; /* Compare length. */ /* if (r1->hname_len < name_len) return -1; if (r1->hname_len > name_len) return 1; */ /* Equal length and equal hash. compare the strings. */ return pj_memcmp(r1->hname, name, name_len); } /* Register one handler for one header name. */ static pj_status_t int_register_parser( const char *name, pjsip_parse_hdr_func *fptr ) { unsigned pos; handler_rec rec; if (handler_count >= PJ_ARRAY_SIZE(handler)) { pj_assert(!"Too many handlers!"); return PJ_ETOOMANY; } /* Initialize temporary handler. */ rec.handler = fptr; rec.hname_len = strlen(name); if (rec.hname_len >= sizeof(rec.hname)) { pj_assert(!"Header name is too long!"); return PJ_ENAMETOOLONG; } /* Copy name. */ pj_memcpy(rec.hname, name, rec.hname_len); rec.hname[rec.hname_len] = '\0'; /* Calculate hash value. */ rec.hname_hash = pj_hash_calc(0, rec.hname, (unsigned)rec.hname_len); /* Get the pos to insert the new handler. */ for (pos=0; pos < handler_count; ++pos) { int d; d = compare_handler(&handler[pos], rec.hname, rec.hname_len, rec.hname_hash); if (d == 0) { pj_assert(0); return PJ_EEXISTS; } if (d > 0) { break; } } /* Shift handlers. */ if (pos != handler_count) { pj_memmove( &handler[pos+1], &handler[pos], (handler_count-pos)*sizeof(handler_rec)); } /* Add new handler. */ pj_memcpy( &handler[pos], &rec, sizeof(handler_rec)); ++handler_count; return PJ_SUCCESS; } /* Register parser handler. If both header name and short name are valid, * then two instances of handler will be registered. */ PJ_DEF(pj_status_t) pjsip_register_hdr_parser( const char *hname, const char *hshortname, pjsip_parse_hdr_func *fptr) { unsigned i; pj_size_t len; char hname_lcase[PJSIP_MAX_HNAME_LEN+1]; pj_status_t status; /* Check that name is not too long */ len = pj_ansi_strlen(hname); if (len > PJSIP_MAX_HNAME_LEN) { pj_assert(!"Header name is too long!"); return PJ_ENAMETOOLONG; } /* Register the normal Mixed-Case name */ status = int_register_parser(hname, fptr); if (status != PJ_SUCCESS) { return status; } /* Get the lower-case name */ for (i=0; i 0; ) { unsigned half = n / 2; handler_rec *mid = first + half; comp = compare_handler(mid, hname->ptr, hname->slen, hash); if (comp < 0) { first = ++mid; n -= half + 1; } else if (comp==0) { first = mid; break; } else { n = half; } } return comp==0 ? first->handler : NULL; } /* Find handler to parse the header name. */ static pjsip_parse_hdr_func* find_handler(const pj_str_t *hname) { pj_uint32_t hash; char hname_copy[PJSIP_MAX_HNAME_LEN]; pj_str_t tmp; pjsip_parse_hdr_func *func; if (hname->slen >= PJSIP_MAX_HNAME_LEN) { /* Guaranteed not to be able to find handler. */ return NULL; } /* First, common case, try to find handler with exact name */ hash = pj_hash_calc(0, hname->ptr, (unsigned)hname->slen); func = find_handler_imp(hash, hname); if (func) return func; /* If not found, try converting the header name to lowercase and * search again. */ hash = pj_hash_calc_tolower(0, hname_copy, hname); tmp.ptr = hname_copy; tmp.slen = hname->slen; return find_handler_imp(hash, &tmp); } /* Find URI handler. */ static pjsip_parse_uri_func* find_uri_handler(const pj_str_t *scheme) { unsigned i; for (i=0; i= PJ_ARRAY_SIZE(uri_handler)) return PJ_ETOOMANY; uri_handler[uri_handler_count].scheme = pj_str((char*)scheme); uri_handler[uri_handler_count].parse = func; ++uri_handler_count; return PJ_SUCCESS; } /* Public function to parse SIP message. */ PJ_DEF(pjsip_msg*) pjsip_parse_msg( pj_pool_t *pool, char *buf, pj_size_t size, pjsip_parser_err_report *err_list) { pjsip_msg *msg = NULL; pj_scanner scanner; pjsip_parse_ctx context; pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error); context.scanner = &scanner; context.pool = pool; context.rdata = NULL; msg = int_parse_msg(&context, err_list); pj_scan_fini(&scanner); return msg; } /* Public function to parse as rdata.*/ PJ_DEF(pjsip_msg *) pjsip_parse_rdata( char *buf, pj_size_t size, pjsip_rx_data *rdata ) { pj_scanner scanner; pjsip_parse_ctx context; pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error); context.scanner = &scanner; context.pool = rdata->tp_info.pool; context.rdata = rdata; rdata->msg_info.msg = int_parse_msg(&context, &rdata->msg_info.parse_err); pj_scan_fini(&scanner); return rdata->msg_info.msg; } /* Determine if a message has been received. */ PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size, pj_bool_t is_datagram, pj_size_t *msg_size) { #if PJ_HAS_TCP const char *hdr_end; const char *body_start; const char *pos; const char *line; int content_length = -1; pj_str_t cur_msg; const pj_str_t end_hdr = { "\n\r\n", 3}; *msg_size = size; /* For datagram, the whole datagram IS the message. */ if (is_datagram) { return PJ_SUCCESS; } /* Find the end of header area by finding an empty line. * Don't use plain strstr() since we want to be able to handle * NULL character in the message */ cur_msg.ptr = (char*)buf; cur_msg.slen = size; pos = pj_strstr(&cur_msg, &end_hdr); if (pos == NULL) { return PJSIP_EPARTIALMSG; } hdr_end = pos+1; body_start = pos+3; /* Find "Content-Length" header the hard way. */ line = pj_strchr(&cur_msg, '\n'); while (line && line < hdr_end) { ++line; if ( ((*line=='C' || *line=='c') && strnicmp_alnum(line, "Content-Length", 14) == 0) || ((*line=='l' || *line=='L') && (*(line+1)==' ' || *(line+1)=='\t' || *(line+1)==':'))) { /* Try to parse the header. */ pj_scanner scanner; PJ_USE_EXCEPTION; pj_scan_init(&scanner, (char*)line, hdr_end-line, PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error); PJ_TRY { pj_str_t str_clen; /* Get "Content-Length" or "L" name */ if (*line=='C' || *line=='c') pj_scan_advance_n(&scanner, 14, PJ_TRUE); else if (*line=='l' || *line=='L') pj_scan_advance_n(&scanner, 1, PJ_TRUE); /* Get colon */ if (pj_scan_get_char(&scanner) != ':') { PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); } /* Get number */ pj_scan_get(&scanner, &pconst.pjsip_DIGIT_SPEC, &str_clen); /* Get newline. */ pj_scan_get_newline(&scanner); /* Found a valid Content-Length header. */ content_length = pj_strtoul(&str_clen); } PJ_CATCH_ANY { content_length = -1; } PJ_END pj_scan_fini(&scanner); } /* Found valid Content-Length? */ if (content_length != -1) break; /* Go to next line. */ cur_msg.slen -= (line - cur_msg.ptr); cur_msg.ptr = (char*)line; line = pj_strchr(&cur_msg, '\n'); } /* Found Content-Length? */ if (content_length == -1) { return PJSIP_EMISSINGHDR; } /* Enough packet received? */ *msg_size = (body_start - buf) + content_length; return (*msg_size) <= size ? PJ_SUCCESS : PJSIP_EPARTIALMSG; #else PJ_UNUSED_ARG(buf); PJ_UNUSED_ARG(is_datagram); *msg_size = size; return PJ_SUCCESS; #endif } /* Public function to parse URI */ PJ_DEF(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool, char *buf, pj_size_t size, unsigned option) { pj_scanner scanner; pjsip_uri *uri = NULL; PJ_USE_EXCEPTION; pj_scan_init(&scanner, buf, size, 0, &on_syntax_error); PJ_TRY { uri = int_parse_uri_or_name_addr(&scanner, pool, option); } PJ_CATCH_ANY { uri = NULL; } PJ_END; /* Must have exhausted all inputs. */ if (pj_scan_is_eof(&scanner) || IS_NEWLINE(*scanner.curptr)) { /* Success. */ pj_scan_fini(&scanner); return uri; } /* Still have some characters unparsed. */ pj_scan_fini(&scanner); return NULL; } /* SIP version */ static void parse_sip_version(pj_scanner *scanner) { pj_str_t SIP = { "SIP", 3 }; pj_str_t V2 = { "2.0", 3 }; pj_str_t sip, version; pj_scan_get( scanner, &pconst.pjsip_ALPHA_SPEC, &sip); if (pj_scan_get_char(scanner) != '/') on_syntax_error(scanner); pj_scan_get_n( scanner, 3, &version); if (pj_stricmp(&sip, &SIP) || pj_stricmp(&version, &V2)) on_syntax_error(scanner); } static pj_bool_t is_next_sip_version(pj_scanner *scanner) { pj_str_t SIP = { "SIP", 3 }; pj_str_t sip; int c; c = pj_scan_peek(scanner, &pconst.pjsip_ALPHA_SPEC, &sip); /* return TRUE if it is "SIP" followed by "/" or space. * we include space since the "/" may be separated by space, * although this would mean it would return TRUE if it is a * request and the method is "SIP"! */ return c && (c=='/' || c==' ' || c=='\t') && pj_stricmp(&sip, &SIP)==0; } /* Internal function to parse SIP message */ static pjsip_msg *int_parse_msg( pjsip_parse_ctx *ctx, pjsip_parser_err_report *err_list) { pj_bool_t parsing_headers; pjsip_msg *msg = NULL; pj_str_t hname; pjsip_ctype_hdr *ctype_hdr = NULL; pj_scanner *scanner = ctx->scanner; pj_pool_t *pool = ctx->pool; PJ_USE_EXCEPTION; parsing_headers = PJ_FALSE; retry_parse: PJ_TRY { if (parsing_headers) goto parse_headers; /* Skip leading newlines. */ while (IS_NEWLINE(*scanner->curptr)) { pj_scan_get_newline(scanner); } /* Check if we still have valid packet. * Sometimes endpoints just send blank (CRLF) packets just to keep * NAT bindings open. */ if (pj_scan_is_eof(scanner)) return NULL; /* Parse request or status line */ if (is_next_sip_version(scanner)) { msg = pjsip_msg_create(pool, PJSIP_RESPONSE_MSG); int_parse_status_line( scanner, &msg->line.status ); } else { msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG); int_parse_req_line(scanner, pool, &msg->line.req ); } parsing_headers = PJ_TRUE; parse_headers: /* Parse headers. */ do { pjsip_parse_hdr_func * func; pjsip_hdr *hdr = NULL; /* Init hname just in case parsing fails. * Ref: PROTOS #2412 */ hname.slen = 0; /* Get hname. */ pj_scan_get( scanner, &pconst.pjsip_TOKEN_SPEC, &hname); if (pj_scan_get_char( scanner ) != ':') { PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); } /* Find handler. */ func = find_handler(&hname); /* Call the handler if found. * If no handler is found, then treat the header as generic * hname/hvalue pair. */ if (func) { hdr = (*func)(ctx); /* Note: * hdr MAY BE NULL, if parsing does not yield a new header * instance, e.g. the values have been added to existing * header. See http://trac.pjsip.org/repos/ticket/940 */ /* Check if we've just parsed a Content-Type header. * We will check for a message body if we've got Content-Type * header. */ if (hdr && hdr->type == PJSIP_H_CONTENT_TYPE) { ctype_hdr = (pjsip_ctype_hdr*)hdr; } } else { hdr = parse_hdr_generic_string(ctx); hdr->name = hdr->sname = hname; } /* Single parse of header line can produce multiple headers. * For example, if one Contact: header contains Contact list * separated by comma, then these Contacts will be split into * different Contact headers. * So here we must insert list instead of just insert one header. */ if (hdr) pj_list_insert_nodes_before(&msg->hdr, hdr); /* Parse until EOF or an empty line is found. */ } while (!pj_scan_is_eof(scanner) && !IS_NEWLINE(*scanner->curptr)); parsing_headers = PJ_FALSE; /* If empty line is found, eat it. */ if (!pj_scan_is_eof(scanner)) { if (IS_NEWLINE(*scanner->curptr)) { pj_scan_get_newline(scanner); } } /* If we have Content-Type header, treat the rest of the message * as body. */ if (ctype_hdr && scanner->curptr!=scanner->end) { /* New: if Content-Type indicates that this is a multipart * message body, parse it. */ const pj_str_t STR_MULTIPART = { "multipart", 9 }; pjsip_msg_body *body; if (pj_stricmp(&ctype_hdr->media.type, &STR_MULTIPART)==0) { body = pjsip_multipart_parse(pool, scanner->curptr, scanner->end - scanner->curptr, &ctype_hdr->media, 0); } else { body = PJ_POOL_ALLOC_T(pool, pjsip_msg_body); pjsip_media_type_cp(pool, &body->content_type, &ctype_hdr->media); body->data = scanner->curptr; body->len = (unsigned)(scanner->end - scanner->curptr); body->print_body = &pjsip_print_text_body; body->clone_data = &pjsip_clone_text_data; } msg->body = body; } } PJ_CATCH_ANY { /* Exception was thrown during parsing. * Skip until newline, and parse next header. */ if (err_list) { pjsip_parser_err_report *err_info; err_info = PJ_POOL_ALLOC_T(pool, pjsip_parser_err_report); err_info->except_code = PJ_GET_EXCEPTION(); err_info->line = scanner->line; /* Scanner's column is zero based, so add 1 */ err_info->col = pj_scan_get_col(scanner) + 1; if (parsing_headers) err_info->hname = hname; else if (msg && msg->type == PJSIP_REQUEST_MSG) err_info->hname = pj_str("Request Line"); else if (msg && msg->type == PJSIP_RESPONSE_MSG) err_info->hname = pj_str("Status Line"); else err_info->hname.slen = 0; pj_list_insert_before(err_list, err_info); } if (parsing_headers) { if (!pj_scan_is_eof(scanner)) { /* Skip until next line. * Watch for header continuation. */ do { pj_scan_skip_line(scanner); } while (IS_SPACE(*scanner->curptr)); } /* Restore flag. Flag may be set in int_parse_sip_url() */ scanner->skip_ws = PJ_SCAN_AUTOSKIP_WS_HEADER; /* Continue parse next header, if any. */ if (!pj_scan_is_eof(scanner) && !IS_NEWLINE(*scanner->curptr)) { goto retry_parse; } } msg = NULL; } PJ_END; return msg; } /* Parse parameter (pname ["=" pvalue]). */ static void parse_param_imp( pj_scanner *scanner, pj_pool_t *pool, pj_str_t *pname, pj_str_t *pvalue, const pj_cis_t *spec, const pj_cis_t *esc_spec, unsigned option) { /* pname */ parser_get_and_unescape(scanner, pool, spec, esc_spec, pname); /* init pvalue */ pvalue->ptr = NULL; pvalue->slen = 0; /* pvalue, if any */ if (*scanner->curptr == '=') { pj_scan_get_char(scanner); if (!pj_scan_is_eof(scanner)) { /* pvalue can be a quoted string. */ if (*scanner->curptr == '"') { pj_scan_get_quote( scanner, '"', '"', pvalue); if (option & PJSIP_PARSE_REMOVE_QUOTE) { pvalue->ptr++; pvalue->slen -= 2; } } else if (*scanner->curptr == '[') { /* pvalue can be a quoted IPv6; in this case, the * '[' and ']' quote characters are to be removed * from the pvalue. */ pj_scan_get_char(scanner); pj_scan_get_until_ch(scanner, ']', pvalue); pj_scan_get_char(scanner); } else if(pj_cis_match(spec, *scanner->curptr)) { parser_get_and_unescape(scanner, pool, spec, esc_spec, pvalue); } } } } /* Parse parameter (pname ["=" pvalue]) using token. */ PJ_DEF(void) pjsip_parse_param_imp(pj_scanner *scanner, pj_pool_t *pool, pj_str_t *pname, pj_str_t *pvalue, unsigned option) { parse_param_imp(scanner, pool, pname, pvalue, &pconst.pjsip_TOKEN_SPEC, &pconst.pjsip_TOKEN_SPEC_ESC, option); } /* Parse parameter (pname ["=" pvalue]) using paramchar. */ PJ_DEF(void) pjsip_parse_uri_param_imp( pj_scanner *scanner, pj_pool_t *pool, pj_str_t *pname, pj_str_t *pvalue, unsigned option) { parse_param_imp(scanner,pool, pname, pvalue, &pconst.pjsip_PARAM_CHAR_SPEC, &pconst.pjsip_PARAM_CHAR_SPEC_ESC, option); } /* Parse parameter (";" pname ["=" pvalue]) in SIP header. */ static void int_parse_param( pj_scanner *scanner, pj_pool_t *pool, pj_str_t *pname, pj_str_t *pvalue, unsigned option) { /* Get ';' character */ pj_scan_get_char(scanner); /* Get pname and optionally pvalue */ pjsip_parse_param_imp(scanner, pool, pname, pvalue, option); } /* Parse parameter (";" pname ["=" pvalue]) in URI. */ static void int_parse_uri_param( pj_scanner *scanner, pj_pool_t *pool, pj_str_t *pname, pj_str_t *pvalue, unsigned option) { /* Get ';' character */ pj_scan_get_char(scanner); /* Get pname and optionally pvalue */ pjsip_parse_uri_param_imp(scanner, pool, pname, pvalue, option); } /* Parse header parameter. */ static void int_parse_hparam( pj_scanner *scanner, pj_pool_t *pool, pj_str_t *hname, pj_str_t *hvalue ) { /* Get '?' or '&' character. */ pj_scan_get_char(scanner); /* hname */ parser_get_and_unescape(scanner, pool, &pconst.pjsip_HDR_CHAR_SPEC, &pconst.pjsip_HDR_CHAR_SPEC_ESC, hname); /* Init hvalue */ hvalue->ptr = NULL; hvalue->slen = 0; /* pvalue, if any */ if (*scanner->curptr == '=') { pj_scan_get_char(scanner); if (!pj_scan_is_eof(scanner) && pj_cis_match(&pconst.pjsip_HDR_CHAR_SPEC, *scanner->curptr)) { parser_get_and_unescape(scanner, pool, &pconst.pjsip_HDR_CHAR_SPEC, &pconst.pjsip_HDR_CHAR_SPEC_ESC, hvalue); } } } /* Parse host part: * host = hostname / IPv4address / IPv6reference */ static void int_parse_host(pj_scanner *scanner, pj_str_t *host) { if (*scanner->curptr == '[') { /* Note: the '[' and ']' characters are removed from the host */ pj_scan_get_char(scanner); pj_scan_get_until_ch(scanner, ']', host); pj_scan_get_char(scanner); } else { pj_scan_get( scanner, &pconst.pjsip_HOST_SPEC, host); } } /* Parse host:port in URI. */ static void int_parse_uri_host_port( pj_scanner *scanner, pj_str_t *host, int *p_port) { int_parse_host(scanner, host); /* RFC3261 section 19.1.2: host don't need to be unescaped */ if (*scanner->curptr == ':') { pj_str_t port; pj_scan_get_char(scanner); pj_scan_get(scanner, &pconst.pjsip_DIGIT_SPEC, &port); *p_port = pj_strtoul(&port); } else { *p_port = 0; } } /* Determine if the next token in an URI is a user specification. */ static int int_is_next_user(pj_scanner *scanner) { pj_str_t dummy; int is_user; /* Find character '@'. If this character exist, then the token * must be a username. */ if (pj_scan_peek( scanner, &pconst.pjsip_PROBE_USER_HOST_SPEC, &dummy) == '@') is_user = 1; else is_user = 0; return is_user; } /* Parse user:pass tokens in an URI. */ static void int_parse_user_pass( pj_scanner *scanner, pj_pool_t *pool, pj_str_t *user, pj_str_t *pass) { parser_get_and_unescape(scanner, pool, &pconst.pjsip_USER_SPEC_LENIENT, &pconst.pjsip_USER_SPEC_LENIENT_ESC, user); if ( *scanner->curptr == ':') { pj_scan_get_char( scanner ); parser_get_and_unescape(scanner, pool, &pconst.pjsip_PASSWD_SPEC, &pconst.pjsip_PASSWD_SPEC_ESC, pass); } else { pass->ptr = NULL; pass->slen = 0; } /* Get the '@' */ pj_scan_get_char( scanner ); } /* Parse all types of URI. */ static pjsip_uri *int_parse_uri_or_name_addr( pj_scanner *scanner, pj_pool_t *pool, unsigned opt) { pjsip_uri *uri; int is_name_addr = 0; /* Exhaust any whitespaces. */ pj_scan_skip_whitespace(scanner); if (*scanner->curptr=='"' || *scanner->curptr=='<') { uri = (pjsip_uri*)int_parse_name_addr( scanner, pool ); is_name_addr = 1; } else { pj_str_t scheme; int next_ch; next_ch = pj_scan_peek( scanner, &pconst.pjsip_DISPLAY_SPEC, &scheme); if (next_ch==':') { pjsip_parse_uri_func *func = find_uri_handler(&scheme); if (func == NULL) { /* Unsupported URI scheme */ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); } uri = (pjsip_uri*) (*func)(scanner, pool, (opt & PJSIP_PARSE_URI_IN_FROM_TO_HDR)==0); } else { uri = (pjsip_uri*)int_parse_name_addr( scanner, pool ); is_name_addr = 1; } } /* Should we return the URI object as name address? */ if (opt & PJSIP_PARSE_URI_AS_NAMEADDR) { if (is_name_addr == 0) { pjsip_name_addr *name_addr; name_addr = pjsip_name_addr_create(pool); name_addr->uri = uri; uri = (pjsip_uri*)name_addr; } } return uri; } /* Parse URI. */ static pjsip_uri *int_parse_uri(pj_scanner *scanner, pj_pool_t *pool, pj_bool_t parse_params) { /* Bug: * This function should not call back int_parse_name_addr() because * it is called by that function. This would cause stack overflow * with PROTOS test #1223. if (*scanner->curptr=='"' || *scanner->curptr=='<') { return (pjsip_uri*)int_parse_name_addr( scanner, pool ); } else { */ pj_str_t scheme; int colon; pjsip_parse_uri_func *func; /* Get scheme. */ colon = pj_scan_peek(scanner, &pconst.pjsip_TOKEN_SPEC, &scheme); if (colon != ':') { PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); } func = find_uri_handler(&scheme); if (func) { return (pjsip_uri*)(*func)(scanner, pool, parse_params); } else { /* Unsupported URI scheme */ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); UNREACHED({ return NULL; /* Not reached. */ }) } /* } */ } /* Parse "sip:" and "sips:" URI. * This actually returns (pjsip_sip_uri*) type, */ static void* int_parse_sip_url( pj_scanner *scanner, pj_pool_t *pool, pj_bool_t parse_params) { pj_str_t scheme; pjsip_sip_uri *url = NULL; int colon; int skip_ws = scanner->skip_ws; scanner->skip_ws = 0; pj_scan_get(scanner, &pconst.pjsip_TOKEN_SPEC, &scheme); colon = pj_scan_get_char(scanner); if (colon != ':') { PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); } if (parser_stricmp(scheme, pconst.pjsip_SIP_STR)==0) { url = pjsip_sip_uri_create(pool, 0); } else if (parser_stricmp(scheme, pconst.pjsip_SIPS_STR)==0) { url = pjsip_sip_uri_create(pool, 1); } else { PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); /* should not reach here */ UNREACHED({ pj_assert(0); return 0; }) } if (int_is_next_user(scanner)) { int_parse_user_pass(scanner, pool, &url->user, &url->passwd); } /* Get host:port */ int_parse_uri_host_port(scanner, &url->host, &url->port); /* Get URL parameters. */ if (parse_params) { while (*scanner->curptr == ';' ) { pj_str_t pname, pvalue; int_parse_uri_param( scanner, pool, &pname, &pvalue, 0); if (!parser_stricmp(pname, pconst.pjsip_USER_STR) && pvalue.slen) { url->user_param = pvalue; } else if (!parser_stricmp(pname, pconst.pjsip_METHOD_STR) && pvalue.slen) { url->method_param = pvalue; } else if (!parser_stricmp(pname, pconst.pjsip_TRANSPORT_STR) && pvalue.slen) { url->transport_param = pvalue; } else if (!parser_stricmp(pname, pconst.pjsip_TTL_STR) && pvalue.slen) { url->ttl_param = pj_strtoul(&pvalue); } else if (!parser_stricmp(pname, pconst.pjsip_MADDR_STR) && pvalue.slen) { url->maddr_param = pvalue; } else if (!parser_stricmp(pname, pconst.pjsip_LR_STR)) { url->lr_param = 1; } else { pjsip_param *p = PJ_POOL_ALLOC_T(pool, pjsip_param); p->name = pname; p->value = pvalue; pj_list_insert_before(&url->other_param, p); } } } /* Get header params. */ if (parse_params && *scanner->curptr == '?') { do { pjsip_param *param; param = PJ_POOL_ALLOC_T(pool, pjsip_param); int_parse_hparam(scanner, pool, ¶m->name, ¶m->value); pj_list_insert_before(&url->header_param, param); } while (*scanner->curptr == '&'); } scanner->skip_ws = skip_ws; pj_scan_skip_whitespace(scanner); return url; } /* Parse nameaddr. */ static pjsip_name_addr *int_parse_name_addr( pj_scanner *scanner, pj_pool_t *pool ) { int has_bracket; pjsip_name_addr *name_addr; name_addr = pjsip_name_addr_create(pool); if (*scanner->curptr == '"') { pj_scan_get_quote( scanner, '"', '"', &name_addr->display); /* Trim the leading and ending quote */ name_addr->display.ptr++; name_addr->display.slen -= 2; } else if (*scanner->curptr != '<') { int next; pj_str_t dummy; /* This can be either the start of display name, * the start of URL ("sip:", "sips:", "tel:", etc.), or '<' char. * We're only interested in display name, because SIP URL * will be parser later. */ next = pj_scan_peek(scanner, &pconst.pjsip_DISPLAY_SPEC, &dummy); if (next == '<') { /* Ok, this is what we're looking for, a display name. */ pj_scan_get_until_ch( scanner, '<', &name_addr->display); pj_strtrim(&name_addr->display); } } /* Manually skip whitespace. */ pj_scan_skip_whitespace(scanner); /* Get the SIP-URL */ has_bracket = (*scanner->curptr == '<'); if (has_bracket) { pj_scan_get_char(scanner); } else if (name_addr->display.slen) { /* Must have bracket now (2012-10-26). * Allowing (invalid) name-addr to pass URI verification will * cause us to send invalid URI to the wire. */ PJ_THROW( PJSIP_SYN_ERR_EXCEPTION); } name_addr->uri = int_parse_uri( scanner, pool, PJ_TRUE ); if (has_bracket) { if (pj_scan_get_char(scanner) != '>') PJ_THROW( PJSIP_SYN_ERR_EXCEPTION); } return name_addr; } /* Parse other URI */ static void* int_parse_other_uri(pj_scanner *scanner, pj_pool_t *pool, pj_bool_t parse_params) { pjsip_other_uri *uri = 0; const pjsip_parser_const_t *pc = pjsip_parser_const(); int skip_ws = scanner->skip_ws; PJ_UNUSED_ARG(parse_params); scanner->skip_ws = 0; uri = pjsip_other_uri_create(pool); pj_scan_get(scanner, &pc->pjsip_TOKEN_SPEC, &uri->scheme); if (pj_scan_get_char(scanner) != ':') { PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); } pj_scan_get(scanner, &pc->pjsip_OTHER_URI_CONTENT, &uri->content); scanner->skip_ws = skip_ws; return uri; } /* Parse SIP request line. */ static void int_parse_req_line( pj_scanner *scanner, pj_pool_t *pool, pjsip_request_line *req_line) { pj_str_t token; pj_scan_get( scanner, &pconst.pjsip_TOKEN_SPEC, &token); pjsip_method_init_np( &req_line->method, &token); req_line->uri = int_parse_uri(scanner, pool, PJ_TRUE); parse_sip_version(scanner); pj_scan_get_newline( scanner ); } /* Parse status line. */ static void int_parse_status_line( pj_scanner *scanner, pjsip_status_line *status_line) { pj_str_t token; parse_sip_version(scanner); pj_scan_get( scanner, &pconst.pjsip_DIGIT_SPEC, &token); status_line->code = pj_strtoul(&token); if (*scanner->curptr != '\r' && *scanner->curptr != '\n') pj_scan_get( scanner, &pconst.pjsip_NOT_NEWLINE, &status_line->reason); else status_line->reason.slen=0, status_line->reason.ptr=NULL; pj_scan_get_newline( scanner ); } /* * Public API to parse SIP status line. */ PJ_DEF(pj_status_t) pjsip_parse_status_line( char *buf, pj_size_t size, pjsip_status_line *status_line) { pj_scanner scanner; PJ_USE_EXCEPTION; pj_bzero(status_line, sizeof(*status_line)); pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error); PJ_TRY { int_parse_status_line(&scanner, status_line); } PJ_CATCH_ANY { /* Tolerate the error if it is caused only by missing newline */ if (status_line->code == 0 && status_line->reason.slen == 0) { pj_scan_fini(&scanner); return PJSIP_EINVALIDMSG; } } PJ_END; pj_scan_fini(&scanner); return PJ_SUCCESS; } /* Parse ending of header. */ static void parse_hdr_end( pj_scanner *scanner ) { if (pj_scan_is_eof(scanner)) { ; /* Do nothing. */ } else if (*scanner->curptr == '&') { pj_scan_get_char(scanner); } else { pj_scan_get_newline(scanner); } } /* Parse ending of header. */ PJ_DEF(void) pjsip_parse_end_hdr_imp( pj_scanner *scanner ) { parse_hdr_end(scanner); } /* Parse generic array header. */ static void parse_generic_array_hdr( pjsip_generic_array_hdr *hdr, pj_scanner *scanner) { /* Some header fields allow empty elements in the value: * Accept, Allow, Supported */ if (pj_scan_is_eof(scanner) || *scanner->curptr == '\r' || *scanner->curptr == '\n') { goto end; } if (hdr->count >= PJ_ARRAY_SIZE(hdr->values)) { /* Too many elements */ on_syntax_error(scanner); return; } pj_scan_get( scanner, &pconst.pjsip_NOT_COMMA_OR_NEWLINE, &hdr->values[hdr->count]); hdr->count++; while ((hdr->count < PJSIP_GENERIC_ARRAY_MAX_COUNT) && (*scanner->curptr == ',')) { pj_scan_get_char(scanner); pj_scan_get( scanner, &pconst.pjsip_NOT_COMMA_OR_NEWLINE, &hdr->values[hdr->count]); hdr->count++; } end: parse_hdr_end(scanner); } /* Parse generic array header. */ PJ_DEF(void) pjsip_parse_generic_array_hdr_imp( pjsip_generic_array_hdr *hdr, pj_scanner *scanner) { parse_generic_array_hdr(hdr, scanner); } /* Parse generic string header. */ static void parse_generic_string_hdr( pjsip_generic_string_hdr *hdr, pjsip_parse_ctx *ctx) { pj_scanner *scanner = ctx->scanner; hdr->hvalue.slen = 0; /* header may be mangled hence the loop */ while (pj_cis_match(&pconst.pjsip_NOT_NEWLINE, *scanner->curptr)) { pj_str_t next, tmp; pj_scan_get( scanner, &pconst.pjsip_NOT_NEWLINE, &hdr->hvalue); if (pj_scan_is_eof(scanner) || IS_NEWLINE(*scanner->curptr)) break; /* mangled, get next fraction */ pj_scan_get( scanner, &pconst.pjsip_NOT_NEWLINE, &next); /* concatenate */ tmp.ptr = (char*)pj_pool_alloc(ctx->pool, hdr->hvalue.slen + next.slen + 2); tmp.slen = 0; pj_strcpy(&tmp, &hdr->hvalue); pj_strcat2(&tmp, " "); pj_strcat(&tmp, &next); tmp.ptr[tmp.slen] = '\0'; hdr->hvalue = tmp; } parse_hdr_end(scanner); } /* Parse generic integer header. */ static void parse_generic_int_hdr( pjsip_generic_int_hdr *hdr, pj_scanner *scanner ) { pj_str_t tmp; pj_scan_get( scanner, &pconst.pjsip_DIGIT_SPEC, &tmp); hdr->ivalue = pj_strtoul(&tmp); parse_hdr_end(scanner); } /* Parse Accept header. */ static pjsip_hdr* parse_hdr_accept(pjsip_parse_ctx *ctx) { pjsip_accept_hdr *accept = pjsip_accept_hdr_create(ctx->pool); parse_generic_array_hdr(accept, ctx->scanner); return (pjsip_hdr*)accept; } /* Parse Allow header. */ static pjsip_hdr* parse_hdr_allow(pjsip_parse_ctx *ctx) { pjsip_allow_hdr *allow = pjsip_allow_hdr_create(ctx->pool); parse_generic_array_hdr(allow, ctx->scanner); return (pjsip_hdr*)allow; } /* Parse Call-ID header. */ static pjsip_hdr* parse_hdr_call_id(pjsip_parse_ctx *ctx) { pjsip_cid_hdr *hdr = pjsip_cid_hdr_create(ctx->pool); pj_scan_get( ctx->scanner, &pconst.pjsip_NOT_NEWLINE, &hdr->id); parse_hdr_end(ctx->scanner); if (ctx->rdata) ctx->rdata->msg_info.cid = hdr; return (pjsip_hdr*)hdr; } /* Parse and interpret Contact param. */ static void int_parse_contact_param( pjsip_contact_hdr *hdr, pj_scanner *scanner, pj_pool_t *pool) { while ( *scanner->curptr == ';' ) { pj_str_t pname, pvalue; int_parse_param( scanner, pool, &pname, &pvalue, 0); if (!parser_stricmp(pname, pconst.pjsip_Q_STR) && pvalue.slen) { char *dot_pos = (char*) pj_memchr(pvalue.ptr, '.', pvalue.slen); if (!dot_pos) { hdr->q1000 = pj_strtoul(&pvalue) * 1000; } else { pj_str_t tmp = pvalue; tmp.slen = dot_pos - pvalue.ptr; hdr->q1000 = pj_strtoul(&tmp) * 1000; pvalue.slen = (pvalue.ptr+pvalue.slen) - (dot_pos+1); pvalue.ptr = dot_pos + 1; hdr->q1000 += pj_strtoul_mindigit(&pvalue, 3); } } else if (!parser_stricmp(pname, pconst.pjsip_EXPIRES_STR) && pvalue.slen) { hdr->expires = pj_strtoul(&pvalue); } else { pjsip_param *p = PJ_POOL_ALLOC_T(pool, pjsip_param); p->name = pname; p->value = pvalue; pj_list_insert_before(&hdr->other_param, p); } } } /* Parse Contact header. */ static pjsip_hdr* parse_hdr_contact( pjsip_parse_ctx *ctx ) { pjsip_contact_hdr *first = NULL; pj_scanner *scanner = ctx->scanner; do { pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(ctx->pool); if (first == NULL) first = hdr; else pj_list_insert_before(first, hdr); if (*scanner->curptr == '*') { pj_scan_get_char(scanner); hdr->star = 1; } else { hdr->star = 0; hdr->uri = int_parse_uri_or_name_addr(scanner, ctx->pool, PJSIP_PARSE_URI_AS_NAMEADDR | PJSIP_PARSE_URI_IN_FROM_TO_HDR); int_parse_contact_param(hdr, scanner, ctx->pool); } if (*scanner->curptr != ',') break; pj_scan_get_char(scanner); } while (1); parse_hdr_end(scanner); return (pjsip_hdr*)first; } /* Parse Content-Length header. */ static pjsip_hdr* parse_hdr_content_len( pjsip_parse_ctx *ctx ) { pj_str_t digit; pjsip_clen_hdr *hdr; hdr = pjsip_clen_hdr_create(ctx->pool); pj_scan_get(ctx->scanner, &pconst.pjsip_DIGIT_SPEC, &digit); hdr->len = pj_strtoul(&digit); parse_hdr_end(ctx->scanner); if (ctx->rdata) ctx->rdata->msg_info.clen = hdr; return (pjsip_hdr*)hdr; } /* Parse Content-Type header. */ static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx ) { pjsip_ctype_hdr *hdr; pj_scanner *scanner = ctx->scanner; hdr = pjsip_ctype_hdr_create(ctx->pool); /* Parse media type and subtype. */ pj_scan_get(scanner, &pconst.pjsip_TOKEN_SPEC, &hdr->media.type); pj_scan_get_char(scanner); pj_scan_get(scanner, &pconst.pjsip_TOKEN_SPEC, &hdr->media.subtype); /* Parse media parameters */ while (*scanner->curptr == ';') { pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param); int_parse_param(scanner, ctx->pool, ¶m->name, ¶m->value, 0); pj_list_push_back(&hdr->media.param, param); } parse_hdr_end(ctx->scanner); if (ctx->rdata) ctx->rdata->msg_info.ctype = hdr; return (pjsip_hdr*)hdr; } /* Parse CSeq header. */ static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx ) { pj_str_t cseq, method; pjsip_cseq_hdr *hdr; hdr = pjsip_cseq_hdr_create(ctx->pool); pj_scan_get( ctx->scanner, &pconst.pjsip_DIGIT_SPEC, &cseq); hdr->cseq = pj_strtoul(&cseq); pj_scan_get( ctx->scanner, &pconst.pjsip_TOKEN_SPEC, &method); pjsip_method_init_np(&hdr->method, &method); parse_hdr_end( ctx->scanner ); if (ctx->rdata) ctx->rdata->msg_info.cseq = hdr; return (pjsip_hdr*)hdr; } /* Parse Expires header. */ static pjsip_hdr* parse_hdr_expires(pjsip_parse_ctx *ctx) { pjsip_expires_hdr *hdr = pjsip_expires_hdr_create(ctx->pool, 0); parse_generic_int_hdr(hdr, ctx->scanner); return (pjsip_hdr*)hdr; } /* Parse From: or To: header. */ static void parse_hdr_fromto( pj_scanner *scanner, pj_pool_t *pool, pjsip_from_hdr *hdr) { hdr->uri = int_parse_uri_or_name_addr(scanner, pool, PJSIP_PARSE_URI_AS_NAMEADDR | PJSIP_PARSE_URI_IN_FROM_TO_HDR); while ( *scanner->curptr == ';' ) { pj_str_t pname, pvalue; int_parse_param( scanner, pool, &pname, &pvalue, 0); if (!parser_stricmp(pname, pconst.pjsip_TAG_STR)) { hdr->tag = pvalue; } else { pjsip_param *p = PJ_POOL_ALLOC_T(pool, pjsip_param); p->name = pname; p->value = pvalue; pj_list_insert_before(&hdr->other_param, p); } } parse_hdr_end(scanner); } /* Parse From: header. */ static pjsip_hdr* parse_hdr_from( pjsip_parse_ctx *ctx ) { pjsip_from_hdr *hdr = pjsip_from_hdr_create(ctx->pool); parse_hdr_fromto(ctx->scanner, ctx->pool, hdr); if (ctx->rdata) ctx->rdata->msg_info.from = hdr; return (pjsip_hdr*)hdr; } /* Parse Require: header. */ static pjsip_hdr* parse_hdr_require( pjsip_parse_ctx *ctx ) { pjsip_require_hdr *hdr; pj_bool_t new_hdr = (ctx->rdata==NULL || ctx->rdata->msg_info.require == NULL); if (ctx->rdata && ctx->rdata->msg_info.require) { hdr = ctx->rdata->msg_info.require; } else { hdr = pjsip_require_hdr_create(ctx->pool); if (ctx->rdata) ctx->rdata->msg_info.require = hdr; } parse_generic_array_hdr(hdr, ctx->scanner); return new_hdr ? (pjsip_hdr*)hdr : NULL; } /* Parse Retry-After: header. */ static pjsip_hdr* parse_hdr_retry_after(pjsip_parse_ctx *ctx) { pjsip_retry_after_hdr *hdr; pj_scanner *scanner = ctx->scanner; pj_str_t tmp; hdr = pjsip_retry_after_hdr_create(ctx->pool, 0); pj_scan_get(scanner, &pconst.pjsip_DIGIT_SPEC, &tmp); hdr->ivalue = pj_strtoul(&tmp); while (!pj_scan_is_eof(scanner) && *scanner->curptr!='\r' && *scanner->curptr!='\n') { if (*scanner->curptr=='(') { pj_scan_get_quote(scanner, '(', ')', &hdr->comment); /* Trim the leading and ending parens */ hdr->comment.ptr++; hdr->comment.slen -= 2; } else if (*scanner->curptr==';') { pjsip_param *prm = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param); int_parse_param(scanner, ctx->pool, &prm->name, &prm->value, 0); pj_list_push_back(&hdr->param, prm); } else { on_syntax_error(scanner); } } parse_hdr_end(scanner); return (pjsip_hdr*)hdr; } /* Parse Supported: header. */ static pjsip_hdr* parse_hdr_supported(pjsip_parse_ctx *ctx) { pjsip_supported_hdr *hdr; pj_bool_t new_hdr = (ctx->rdata==NULL || ctx->rdata->msg_info.supported == NULL); if (ctx->rdata && ctx->rdata->msg_info.supported) { hdr = ctx->rdata->msg_info.supported; } else { hdr = pjsip_supported_hdr_create(ctx->pool); if (ctx->rdata) ctx->rdata->msg_info.supported = hdr; } parse_generic_array_hdr(hdr, ctx->scanner); return new_hdr ? (pjsip_hdr*)hdr : NULL; } /* Parse To: header. */ static pjsip_hdr* parse_hdr_to( pjsip_parse_ctx *ctx ) { pjsip_to_hdr *hdr = pjsip_to_hdr_create(ctx->pool); parse_hdr_fromto(ctx->scanner, ctx->pool, hdr); if (ctx->rdata) ctx->rdata->msg_info.to = hdr; return (pjsip_hdr*)hdr; } /* Parse Unsupported: header. */ static pjsip_hdr* parse_hdr_unsupported(pjsip_parse_ctx *ctx) { pjsip_unsupported_hdr *hdr = pjsip_unsupported_hdr_create(ctx->pool); parse_generic_array_hdr(hdr, ctx->scanner); return (pjsip_hdr*)hdr; } /* Parse and interpret Via parameters. */ static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner, pj_pool_t *pool) { while ( *scanner->curptr == ';' ) { pj_str_t pname, pvalue; //Parse with PARAM_CHAR instead, to allow IPv6 //No, back to using int_parse_param() for the "`" character! //int_parse_param( scanner, pool, &pname, &pvalue, 0); //parse_param_imp(scanner, pool, &pname, &pvalue, // &pconst.pjsip_TOKEN_SPEC, // &pconst.pjsip_TOKEN_SPEC_ESC, 0); //int_parse_param(scanner, pool, &pname, &pvalue, 0); // This should be the correct one: // added special spec for Via parameter, basically token plus // ":" to allow IPv6 address in the received param. pj_scan_get_char(scanner); parse_param_imp(scanner, pool, &pname, &pvalue, &pconst.pjsip_VIA_PARAM_SPEC, &pconst.pjsip_VIA_PARAM_SPEC_ESC, 0); if (!parser_stricmp(pname, pconst.pjsip_BRANCH_STR) && pvalue.slen) { hdr->branch_param = pvalue; } else if (!parser_stricmp(pname, pconst.pjsip_TTL_STR) && pvalue.slen) { hdr->ttl_param = pj_strtoul(&pvalue); } else if (!parser_stricmp(pname, pconst.pjsip_MADDR_STR) && pvalue.slen) { hdr->maddr_param = pvalue; } else if (!parser_stricmp(pname, pconst.pjsip_RECEIVED_STR) && pvalue.slen) { hdr->recvd_param = pvalue; } else if (!parser_stricmp(pname, pconst.pjsip_RPORT_STR)) { if (pvalue.slen) hdr->rport_param = pj_strtoul(&pvalue); else hdr->rport_param = 0; } else { pjsip_param *p = PJ_POOL_ALLOC_T(pool, pjsip_param); p->name = pname; p->value = pvalue; pj_list_insert_before(&hdr->other_param, p); } } } /* Parse Max-Forwards header. */ static pjsip_hdr* parse_hdr_max_forwards( pjsip_parse_ctx *ctx ) { pjsip_max_fwd_hdr *hdr; hdr = pjsip_max_fwd_hdr_create(ctx->pool, 0); parse_generic_int_hdr(hdr, ctx->scanner); if (ctx->rdata) ctx->rdata->msg_info.max_fwd = hdr; return (pjsip_hdr*)hdr; } /* Parse Min-Expires header. */ static pjsip_hdr* parse_hdr_min_expires(pjsip_parse_ctx *ctx) { pjsip_min_expires_hdr *hdr; hdr = pjsip_min_expires_hdr_create(ctx->pool, 0); parse_generic_int_hdr(hdr, ctx->scanner); return (pjsip_hdr*)hdr; } /* Parse Route: or Record-Route: header. */ static void parse_hdr_rr_route( pj_scanner *scanner, pj_pool_t *pool, pjsip_routing_hdr *hdr ) { pjsip_name_addr *temp=int_parse_name_addr(scanner, pool); pj_memcpy(&hdr->name_addr, temp, sizeof(*temp)); while (*scanner->curptr == ';') { pjsip_param *p = PJ_POOL_ALLOC_T(pool, pjsip_param); int_parse_param(scanner, pool, &p->name, &p->value, 0); pj_list_insert_before(&hdr->other_param, p); } } /* Parse Record-Route header. */ static pjsip_hdr* parse_hdr_rr( pjsip_parse_ctx *ctx) { pjsip_rr_hdr *first = NULL; pj_scanner *scanner = ctx->scanner; do { pjsip_rr_hdr *hdr = pjsip_rr_hdr_create(ctx->pool); if (!first) { first = hdr; } else { pj_list_insert_before(first, hdr); } parse_hdr_rr_route(scanner, ctx->pool, hdr); if (*scanner->curptr == ',') { pj_scan_get_char(scanner); } else { break; } } while (1); parse_hdr_end(scanner); if (ctx->rdata && ctx->rdata->msg_info.record_route==NULL) ctx->rdata->msg_info.record_route = first; return (pjsip_hdr*)first; } /* Parse Route: header. */ static pjsip_hdr* parse_hdr_route( pjsip_parse_ctx *ctx ) { pjsip_route_hdr *first = NULL; pj_scanner *scanner = ctx->scanner; do { pjsip_route_hdr *hdr = pjsip_route_hdr_create(ctx->pool); if (!first) { first = hdr; } else { pj_list_insert_before(first, hdr); } parse_hdr_rr_route(scanner, ctx->pool, hdr); if (*scanner->curptr == ',') { pj_scan_get_char(scanner); } else { break; } } while (1); parse_hdr_end(scanner); if (ctx->rdata && ctx->rdata->msg_info.route==NULL) ctx->rdata->msg_info.route = first; return (pjsip_hdr*)first; } /* Parse Via: header. */ static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx ) { pjsip_via_hdr *first = NULL; pj_scanner *scanner = ctx->scanner; do { pjsip_via_hdr *hdr = pjsip_via_hdr_create(ctx->pool); if (!first) first = hdr; else pj_list_insert_before(first, hdr); parse_sip_version(scanner); if (pj_scan_get_char(scanner) != '/') on_syntax_error(scanner); pj_scan_get( scanner, &pconst.pjsip_TOKEN_SPEC, &hdr->transport); int_parse_host(scanner, &hdr->sent_by.host); if (*scanner->curptr==':') { pj_str_t digit; pj_scan_get_char(scanner); pj_scan_get(scanner, &pconst.pjsip_DIGIT_SPEC, &digit); hdr->sent_by.port = pj_strtoul(&digit); } int_parse_via_param(hdr, scanner, ctx->pool); if (*scanner->curptr == '(') { pj_scan_get_char(scanner); pj_scan_get_until_ch( scanner, ')', &hdr->comment); pj_scan_get_char( scanner ); } if (*scanner->curptr != ',') break; pj_scan_get_char(scanner); } while (1); parse_hdr_end(scanner); if (ctx->rdata && ctx->rdata->msg_info.via == NULL) ctx->rdata->msg_info.via = first; return (pjsip_hdr*)first; } /* Parse generic header. */ static pjsip_hdr* parse_hdr_generic_string( pjsip_parse_ctx *ctx ) { pjsip_generic_string_hdr *hdr; hdr = pjsip_generic_string_hdr_create(ctx->pool, NULL, NULL); parse_generic_string_hdr(hdr, ctx); return (pjsip_hdr*)hdr; } /* Public function to parse a header value. */ PJ_DEF(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname, char *buf, pj_size_t size, int *parsed_len ) { pj_scanner scanner; pjsip_hdr *hdr = NULL; pjsip_parse_ctx context; PJ_USE_EXCEPTION; pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error); context.scanner = &scanner; context.pool = pool; context.rdata = NULL; PJ_TRY { pjsip_parse_hdr_func *func = find_handler(hname); if (func) { hdr = (*func)(&context); } else { hdr = parse_hdr_generic_string(&context); hdr->type = PJSIP_H_OTHER; pj_strdup(pool, &hdr->name, hname); hdr->sname = hdr->name; } } PJ_CATCH_ANY { hdr = NULL; } PJ_END if (parsed_len) { *parsed_len = (unsigned)(scanner.curptr - scanner.begin); } pj_scan_fini(&scanner); return hdr; } /* Parse multiple header lines */ PJ_DEF(pj_status_t) pjsip_parse_headers( pj_pool_t *pool, char *input, pj_size_t size, pjsip_hdr *hlist, unsigned options) { enum { STOP_ON_ERROR = 1 }; pj_scanner scanner; pjsip_parse_ctx ctx; pj_str_t hname; PJ_USE_EXCEPTION; pj_scan_init(&scanner, input, size, PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error); pj_bzero(&ctx, sizeof(ctx)); ctx.scanner = &scanner; ctx.pool = pool; retry_parse: PJ_TRY { /* Parse headers. */ do { pjsip_parse_hdr_func * func; pjsip_hdr *hdr = NULL; /* Init hname just in case parsing fails. * Ref: PROTOS #2412 */ hname.slen = 0; /* Get hname. */ pj_scan_get( &scanner, &pconst.pjsip_TOKEN_SPEC, &hname); if (pj_scan_get_char( &scanner ) != ':') { PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); } /* Find handler. */ func = find_handler(&hname); /* Call the handler if found. * If no handler is found, then treat the header as generic * hname/hvalue pair. */ if (func) { hdr = (*func)(&ctx); } else { hdr = parse_hdr_generic_string(&ctx); hdr->name = hdr->sname = hname; } /* Single parse of header line can produce multiple headers. * For example, if one Contact: header contains Contact list * separated by comma, then these Contacts will be split into * different Contact headers. * So here we must insert list instead of just insert one header. */ if (hdr) pj_list_insert_nodes_before(hlist, hdr); /* Parse until EOF or an empty line is found. */ } while (!pj_scan_is_eof(&scanner) && !IS_NEWLINE(*scanner.curptr)); /* If empty line is found, eat it. */ if (!pj_scan_is_eof(&scanner)) { if (IS_NEWLINE(*scanner.curptr)) { pj_scan_get_newline(&scanner); } } } PJ_CATCH_ANY { PJ_LOG(4,(THIS_FILE, "Error parsing header: '%.*s' line %d col %d", (int)hname.slen, hname.ptr, scanner.line, pj_scan_get_col(&scanner))); /* Exception was thrown during parsing. */ if ((options & STOP_ON_ERROR) == STOP_ON_ERROR) { pj_scan_fini(&scanner); return PJSIP_EINVALIDHDR; } /* Skip until newline, and parse next header. */ if (!pj_scan_is_eof(&scanner)) { /* Skip until next line. * Watch for header continuation. */ do { pj_scan_skip_line(&scanner); } while (IS_SPACE(*scanner.curptr)); } /* Restore flag. Flag may be set in int_parse_sip_url() */ scanner.skip_ws = PJ_SCAN_AUTOSKIP_WS_HEADER; /* Continue parse next header, if any. */ if (!pj_scan_is_eof(&scanner) && !IS_NEWLINE(*scanner.curptr)) { goto retry_parse; } } PJ_END; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_parser_wrap.cpp ================================================ /* $Id: sip_parser_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This file is a C++ wrapper, see ticket #886 for details. */ #include "sip_parser.c" ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_resolve.c ================================================ /* $Id: sip_resolve.c 4108 2012-04-27 01:32:12Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "sip_resolve.c" #define ADDR_MAX_COUNT 8 struct naptr_target { pj_str_t res_type; /**< e.g. "_sip._udp" */ pj_str_t name; /**< Domain name. */ pjsip_transport_type_e type; /**< Transport type. */ unsigned order; /**< Order */ unsigned pref; /**< Preference. */ }; struct query { char *objname; pj_dns_type query_type; void *token; pjsip_resolver_callback *cb; pj_dns_async_query *object; pj_status_t last_error; /* Original request: */ struct { pjsip_host_info target; unsigned def_port; } req; /* NAPTR records: */ unsigned naptr_cnt; struct naptr_target naptr[8]; }; struct pjsip_resolver_t { pj_dns_resolver *res; pjsip_ext_resolver *ext_res; }; static void srv_resolver_cb(void *user_data, pj_status_t status, const pj_dns_srv_record *rec); static void dns_a_callback(void *user_data, pj_status_t status, pj_dns_parsed_packet *response); /* * Public API to create the resolver. */ PJ_DEF(pj_status_t) pjsip_resolver_create( pj_pool_t *pool, pjsip_resolver_t **p_res) { pjsip_resolver_t *resolver; PJ_ASSERT_RETURN(pool && p_res, PJ_EINVAL); resolver = PJ_POOL_ZALLOC_T(pool, pjsip_resolver_t); *p_res = resolver; return PJ_SUCCESS; } /* * Public API to set the DNS resolver instance for the SIP resolver. */ PJ_DEF(pj_status_t) pjsip_resolver_set_resolver(pjsip_resolver_t *res, pj_dns_resolver *dns_res) { #if PJSIP_HAS_RESOLVER res->res = dns_res; return PJ_SUCCESS; #else PJ_UNUSED_ARG(res); PJ_UNUSED_ARG(dns_res); pj_assert(!"Resolver is disabled (PJSIP_HAS_RESOLVER==0)"); return PJ_EINVALIDOP; #endif } /* * Public API to set the DNS external resolver implementation for the SIP * resolver. */ PJ_DEF(pj_status_t) pjsip_resolver_set_ext_resolver(pjsip_resolver_t *res, pjsip_ext_resolver *ext_res) { if (ext_res && !ext_res->resolve) return PJ_EINVAL; if (ext_res && res->res) { #if PJSIP_HAS_RESOLVER pj_dns_resolver_destroy(res->res, PJ_FALSE); #endif res->res = NULL; } res->ext_res = ext_res; return PJ_SUCCESS; } /* * Public API to get the internal DNS resolver. */ PJ_DEF(pj_dns_resolver*) pjsip_resolver_get_resolver(pjsip_resolver_t *res) { return res->res; } /* * Public API to create destroy the resolver */ PJ_DEF(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver) { if (resolver->res) { #if PJSIP_HAS_RESOLVER pj_dns_resolver_destroy(resolver->res, PJ_FALSE); #endif resolver->res = NULL; } } /* * Internal: * determine if an address is a valid IP address, and if it is, * return the IP version (4 or 6). */ static int get_ip_addr_ver(const pj_str_t *host) { pj_in_addr dummy; pj_in6_addr dummy6; /* First check with inet_aton() */ if (pj_inet_aton(host, &dummy) > 0) return 4; /* Then check if this is an IPv6 address */ if (pj_inet_pton(pj_AF_INET6(), host, &dummy6) == PJ_SUCCESS) return 6; /* Not an IP address */ return 0; } /* * This is the main function for performing server resolution. */ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip_host_info *target, void *token, pjsip_resolver_callback *cb) { pjsip_server_addresses svr_addr; pj_status_t status = PJ_SUCCESS; int ip_addr_ver; struct query *query; pjsip_transport_type_e type = target->type; /* If an external implementation has been provided use it instead */ if (resolver->ext_res) { (*resolver->ext_res->resolve)(resolver, pool, target, token, cb); return; } /* Is it IP address or hostname? And if it's an IP, which version? */ ip_addr_ver = get_ip_addr_ver(&target->addr.host); /* Set the transport type if not explicitly specified. * RFC 3263 section 4.1 specify rules to set up this. */ if (type == PJSIP_TRANSPORT_UNSPECIFIED) { if (ip_addr_ver || (target->addr.port != 0)) { #if PJ_HAS_TCP if (target->flag & PJSIP_TRANSPORT_SECURE) { type = PJSIP_TRANSPORT_TLS; } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) { type = PJSIP_TRANSPORT_TCP; } else #endif { type = PJSIP_TRANSPORT_UDP; } } else { /* No type or explicit port is specified, and the address is * not IP address. * In this case, full NAPTR resolution must be performed. * But we don't support it (yet). */ #if PJ_HAS_TCP if (target->flag & PJSIP_TRANSPORT_SECURE) { type = PJSIP_TRANSPORT_TLS; } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) { type = PJSIP_TRANSPORT_TCP; } else #endif { type = PJSIP_TRANSPORT_UDP; } } /* Add IPv6 flag for IPv6 address */ if (ip_addr_ver == 6) type = (pjsip_transport_type_e)((int)type + PJSIP_TRANSPORT_IPV6); } /* If target is an IP address, or if resolver is not configured, * we can just finish the resolution now using pj_gethostbyname() */ if (ip_addr_ver || resolver->res == NULL) { char addr_str[PJ_INET6_ADDRSTRLEN+10]; pj_uint16_t srv_port; if (ip_addr_ver != 0) { /* Target is an IP address, no need to resolve */ if (ip_addr_ver == 4) { pj_sockaddr_init(pj_AF_INET(), &svr_addr.entry[0].addr, NULL, 0); pj_inet_aton(&target->addr.host, &svr_addr.entry[0].addr.ipv4.sin_addr); } else { pj_sockaddr_init(pj_AF_INET6(), &svr_addr.entry[0].addr, NULL, 0); pj_inet_pton(pj_AF_INET6(), &target->addr.host, &svr_addr.entry[0].addr.ipv6.sin6_addr); } } else { pj_addrinfo ai; unsigned count; int af; PJ_LOG(5,(THIS_FILE, "DNS resolver not available, target '%.*s:%d' type=%s " "will be resolved with getaddrinfo()", target->addr.host.slen, target->addr.host.ptr, target->addr.port, pjsip_transport_get_type_name(target->type))); if (type & PJSIP_TRANSPORT_IPV6) { af = pj_AF_INET6(); } else { af = pj_AF_INET(); } /* Resolve */ count = 1; status = pj_getaddrinfo(af, &target->addr.host, &count, &ai); if (status != PJ_SUCCESS) { /* "Normalize" error to PJ_ERESOLVE. This is a special error * because it will be translated to SIP status 502 by * sip_transaction.c */ status = PJ_ERESOLVE; goto on_error; } svr_addr.entry[0].addr.addr.sa_family = (pj_uint16_t)af; pj_memcpy(&svr_addr.entry[0].addr, &ai.ai_addr, sizeof(pj_sockaddr)); } /* Set the port number */ if (target->addr.port == 0) { srv_port = (pj_uint16_t) pjsip_transport_get_default_port_for_type(type); } else { srv_port = (pj_uint16_t)target->addr.port; } pj_sockaddr_set_port(&svr_addr.entry[0].addr, srv_port); /* Call the callback. */ PJ_LOG(5,(THIS_FILE, "Target '%.*s:%d' type=%s resolved to " "'%s' type=%s (%s)", (int)target->addr.host.slen, target->addr.host.ptr, target->addr.port, pjsip_transport_get_type_name(target->type), pj_sockaddr_print(&svr_addr.entry[0].addr, addr_str, sizeof(addr_str), 3), pjsip_transport_get_type_name(type), pjsip_transport_get_type_desc(type))); svr_addr.count = 1; svr_addr.entry[0].priority = 0; svr_addr.entry[0].weight = 0; svr_addr.entry[0].type = type; svr_addr.entry[0].addr_len = pj_sockaddr_get_len(&svr_addr.entry[0].addr); (*cb)(status, token, &svr_addr); /* Done. */ return; } /* Target is not an IP address so we need to resolve it. */ #if PJSIP_HAS_RESOLVER /* Build the query state */ query = PJ_POOL_ZALLOC_T(pool, struct query); query->objname = THIS_FILE; query->token = token; query->cb = cb; query->req.target = *target; pj_strdup(pool, &query->req.target.addr.host, &target->addr.host); /* If port is not specified, start with SRV resolution * (should be with NAPTR, but we'll do that later) */ PJ_TODO(SUPPORT_DNS_NAPTR); /* Build dummy NAPTR entry */ query->naptr_cnt = 1; pj_bzero(&query->naptr[0], sizeof(query->naptr[0])); query->naptr[0].order = 0; query->naptr[0].pref = 0; query->naptr[0].type = type; pj_strdup(pool, &query->naptr[0].name, &target->addr.host); /* Start DNS SRV or A resolution, depending on whether port is specified */ if (target->addr.port == 0) { query->query_type = PJ_DNS_TYPE_SRV; query->req.def_port = 5060; if (type == PJSIP_TRANSPORT_TLS) { query->naptr[0].res_type = pj_str("_sips._tcp."); query->req.def_port = 5061; } else if (type == PJSIP_TRANSPORT_TCP) query->naptr[0].res_type = pj_str("_sip._tcp."); else if (type == PJSIP_TRANSPORT_UDP) query->naptr[0].res_type = pj_str("_sip._udp."); else { pj_assert(!"Unknown transport type"); query->naptr[0].res_type = pj_str("_sip._udp."); } } else { /* Otherwise if port is specified, start with A (or AAAA) host * resolution */ query->query_type = PJ_DNS_TYPE_A; query->naptr[0].res_type.slen = 0; query->req.def_port = target->addr.port; } /* Start the asynchronous query */ PJ_LOG(5, (query->objname, "Starting async DNS %s query: target=%.*s%.*s, transport=%s, " "port=%d", pj_dns_get_type_name(query->query_type), (int)query->naptr[0].res_type.slen, query->naptr[0].res_type.ptr, (int)query->naptr[0].name.slen, query->naptr[0].name.ptr, pjsip_transport_get_type_name(target->type), target->addr.port)); if (query->query_type == PJ_DNS_TYPE_SRV) { status = pj_dns_srv_resolve(&query->naptr[0].name, &query->naptr[0].res_type, query->req.def_port, pool, resolver->res, PJ_TRUE, query, &srv_resolver_cb, NULL); } else if (query->query_type == PJ_DNS_TYPE_A) { status = pj_dns_resolver_start_query(resolver->res, &query->naptr[0].name, PJ_DNS_TYPE_A, 0, &dns_a_callback, query, &query->object); } else { pj_assert(!"Unexpected"); status = PJ_EBUG; } if (status != PJ_SUCCESS) goto on_error; return; #else /* PJSIP_HAS_RESOLVER */ PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(query); PJ_UNUSED_ARG(srv_name); #endif /* PJSIP_HAS_RESOLVER */ on_error: if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; PJ_LOG(4,(THIS_FILE, "Failed to resolve '%.*s'. Err=%d (%s)", (int)target->addr.host.slen, target->addr.host.ptr, status, pj_strerror(status,errmsg,sizeof(errmsg)).ptr)); (*cb)(status, token, NULL); return; } } #if PJSIP_HAS_RESOLVER /* * This callback is called when target is resolved with DNS A query. */ static void dns_a_callback(void *user_data, pj_status_t status, pj_dns_parsed_packet *pkt) { struct query *query = (struct query*) user_data; pjsip_server_addresses srv; pj_dns_a_record rec; unsigned i; rec.addr_count = 0; /* Parse the response */ if (status == PJ_SUCCESS) { status = pj_dns_parse_a_response(pkt, &rec); } if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; /* Log error */ pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(query->objname, "DNS A record resolution failed: %s", errmsg)); /* Call the callback */ (*query->cb)(status, query->token, NULL); return; } /* Build server addresses and call callback */ srv.count = 0; for (i = 0; i < rec.addr_count && srv.count < PJSIP_MAX_RESOLVED_ADDRESSES; ++i) { srv.entry[srv.count].type = query->naptr[0].type; srv.entry[srv.count].priority = 0; srv.entry[srv.count].weight = 0; srv.entry[srv.count].addr_len = sizeof(pj_sockaddr_in); pj_sockaddr_in_init(&srv.entry[srv.count].addr.ipv4, 0, (pj_uint16_t)query->req.def_port); srv.entry[srv.count].addr.ipv4.sin_addr.s_addr = rec.addr[i].s_addr; ++srv.count; } /* Call the callback */ (*query->cb)(PJ_SUCCESS, query->token, &srv); } /* Callback to be called by DNS SRV resolution */ static void srv_resolver_cb(void *user_data, pj_status_t status, const pj_dns_srv_record *rec) { struct query *query = (struct query*) user_data; pjsip_server_addresses srv; unsigned i; if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; /* Log error */ pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(query->objname, "DNS A record resolution failed: %s", errmsg)); /* Call the callback */ (*query->cb)(status, query->token, NULL); return; } /* Build server addresses and call callback */ srv.count = 0; for (i=0; icount; ++i) { unsigned j; for (j = 0; j < rec->entry[i].server.addr_count && srv.count < PJSIP_MAX_RESOLVED_ADDRESSES; ++j) { srv.entry[srv.count].type = query->naptr[0].type; srv.entry[srv.count].priority = rec->entry[i].priority; srv.entry[srv.count].weight = rec->entry[i].weight; srv.entry[srv.count].addr_len = sizeof(pj_sockaddr_in); pj_sockaddr_in_init(&srv.entry[srv.count].addr.ipv4, 0, (pj_uint16_t)rec->entry[i].port); srv.entry[srv.count].addr.ipv4.sin_addr.s_addr = rec->entry[i].server.addr[j].s_addr; ++srv.count; } } /* Call the callback */ (*query->cb)(PJ_SUCCESS, query->token, &srv); } #endif /* PJSIP_HAS_RESOLVER */ ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_tel_uri.c ================================================ /* $Id: sip_tel_uri.c 4322 2013-01-17 10:09:09Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #define ALPHA #define DIGITS "0123456789" #define HEX "aAbBcCdDeEfF" #define HEX_DIGITS DIGITS HEX #define VISUAL_SEP "-.()" #define PHONE_DIGITS DIGITS VISUAL_SEP #define GLOBAL_DIGITS "+" PHONE_DIGITS #define LOCAL_DIGITS HEX_DIGITS "*#" VISUAL_SEP #define NUMBER_SPEC LOCAL_DIGITS GLOBAL_DIGITS #define PHONE_CONTEXT ALPHA GLOBAL_DIGITS //#define RESERVED ";/?:@&=+$," #define RESERVED "/:@&$,+" #define MARK "-_.!~*'()" #define UNRESERVED ALPHA DIGITS MARK #define ESCAPED "%" #define URIC RESERVED UNRESERVED ESCAPED "[]+" #define PARAM_UNRESERVED "[]/:&+$" #define PARAM_CHAR PARAM_UNRESERVED UNRESERVED ESCAPED static pj_cis_buf_t cis_buf; static pj_cis_t pjsip_TEL_NUMBER_SPEC; static pj_cis_t pjsip_TEL_EXT_VALUE_SPEC; static pj_cis_t pjsip_TEL_PHONE_CONTEXT_SPEC; static pj_cis_t pjsip_TEL_URIC_SPEC; static pj_cis_t pjsip_TEL_VISUAL_SEP_SPEC; static pj_cis_t pjsip_TEL_PNAME_SPEC; static pj_cis_t pjsip_TEL_PVALUE_SPEC; static pj_cis_t pjsip_TEL_PVALUE_SPEC_ESC; static pj_cis_t pjsip_TEL_PARSING_PVALUE_SPEC; static pj_cis_t pjsip_TEL_PARSING_PVALUE_SPEC_ESC; static pj_str_t pjsip_ISUB_STR = { "isub", 4 }; static pj_str_t pjsip_EXT_STR = { "ext", 3 }; static pj_str_t pjsip_PH_CTX_STR = { "phone-context", 13 }; static const pj_str_t *tel_uri_get_scheme( const pjsip_tel_uri* ); static void *tel_uri_get_uri( pjsip_tel_uri* ); static pj_ssize_t tel_uri_print( pjsip_uri_context_e context, const pjsip_tel_uri *url, char *buf, pj_size_t size); static int tel_uri_cmp( pjsip_uri_context_e context, const pjsip_tel_uri *url1, const pjsip_tel_uri *url2); static pjsip_tel_uri* tel_uri_clone(pj_pool_t *pool, const pjsip_tel_uri *rhs); static void* tel_uri_parse( pj_scanner *scanner, pj_pool_t *pool, pj_bool_t parse_params); typedef const pj_str_t* (*P_GET_SCHEME)(const void*); typedef void* (*P_GET_URI)(void*); typedef pj_ssize_t (*P_PRINT_URI)(pjsip_uri_context_e,const void *, char*,pj_size_t); typedef int (*P_CMP_URI)(pjsip_uri_context_e, const void*, const void*); typedef void* (*P_CLONE)(pj_pool_t*, const void*); static pjsip_uri_vptr tel_uri_vptr = { (P_GET_SCHEME) &tel_uri_get_scheme, (P_GET_URI) &tel_uri_get_uri, (P_PRINT_URI) &tel_uri_print, (P_CMP_URI) &tel_uri_cmp, (P_CLONE) &tel_uri_clone }; PJ_DEF(pjsip_tel_uri*) pjsip_tel_uri_create(pj_pool_t *pool) { pjsip_tel_uri *uri = PJ_POOL_ZALLOC_T(pool, pjsip_tel_uri); uri->vptr = &tel_uri_vptr; pj_list_init(&uri->other_param); return uri; } static const pj_str_t *tel_uri_get_scheme( const pjsip_tel_uri *uri ) { PJ_UNUSED_ARG(uri); return &pjsip_parser_const()->pjsip_TEL_STR; } static void *tel_uri_get_uri( pjsip_tel_uri *uri ) { return uri; } pj_status_t pjsip_tel_uri_subsys_init(void) { pj_status_t status; pj_cis_buf_init(&cis_buf); status = pj_cis_init(&cis_buf, &pjsip_TEL_EXT_VALUE_SPEC); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); pj_cis_add_str(&pjsip_TEL_EXT_VALUE_SPEC, PHONE_DIGITS); status = pj_cis_init(&cis_buf, &pjsip_TEL_NUMBER_SPEC); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); pj_cis_add_str(&pjsip_TEL_NUMBER_SPEC, NUMBER_SPEC); status = pj_cis_init(&cis_buf, &pjsip_TEL_VISUAL_SEP_SPEC); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); pj_cis_add_str(&pjsip_TEL_VISUAL_SEP_SPEC, VISUAL_SEP); status = pj_cis_init(&cis_buf, &pjsip_TEL_PHONE_CONTEXT_SPEC); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); pj_cis_add_alpha(&pjsip_TEL_PHONE_CONTEXT_SPEC); pj_cis_add_num(&pjsip_TEL_PHONE_CONTEXT_SPEC); pj_cis_add_str(&pjsip_TEL_PHONE_CONTEXT_SPEC, PHONE_CONTEXT); status = pj_cis_init(&cis_buf, &pjsip_TEL_URIC_SPEC); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); pj_cis_add_alpha(&pjsip_TEL_URIC_SPEC); pj_cis_add_num(&pjsip_TEL_URIC_SPEC); pj_cis_add_str(&pjsip_TEL_URIC_SPEC, URIC); status = pj_cis_init(&cis_buf, &pjsip_TEL_PNAME_SPEC); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); pj_cis_add_alpha(&pjsip_TEL_PNAME_SPEC); pj_cis_add_num(&pjsip_TEL_PNAME_SPEC); pj_cis_add_str(&pjsip_TEL_PNAME_SPEC, "-"); status = pj_cis_init(&cis_buf, &pjsip_TEL_PVALUE_SPEC); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); pj_cis_add_alpha(&pjsip_TEL_PVALUE_SPEC); pj_cis_add_num(&pjsip_TEL_PVALUE_SPEC); pj_cis_add_str(&pjsip_TEL_PVALUE_SPEC, PARAM_CHAR); status = pj_cis_dup(&pjsip_TEL_PVALUE_SPEC_ESC, &pjsip_TEL_PVALUE_SPEC); pj_cis_del_str(&pjsip_TEL_PVALUE_SPEC_ESC, "%"); status = pj_cis_dup(&pjsip_TEL_PARSING_PVALUE_SPEC, &pjsip_TEL_URIC_SPEC); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); pj_cis_add_cis(&pjsip_TEL_PARSING_PVALUE_SPEC, &pjsip_TEL_PVALUE_SPEC); pj_cis_add_str(&pjsip_TEL_PARSING_PVALUE_SPEC, "="); status = pj_cis_dup(&pjsip_TEL_PARSING_PVALUE_SPEC_ESC, &pjsip_TEL_PARSING_PVALUE_SPEC); pj_cis_del_str(&pjsip_TEL_PARSING_PVALUE_SPEC_ESC, "%"); status = pjsip_register_uri_parser("tel", &tel_uri_parse); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); return PJ_SUCCESS; } /* Print tel: URI */ static pj_ssize_t tel_uri_print( pjsip_uri_context_e context, const pjsip_tel_uri *uri, char *buf, pj_size_t size) { int printed; char *startbuf = buf; char *endbuf = buf+size-1; const pjsip_parser_const_t *pc = pjsip_parser_const(); PJ_UNUSED_ARG(context); /* Print scheme. */ copy_advance(buf, pc->pjsip_TEL_STR); *buf++ = ':'; /* Print number. */ copy_advance_escape(buf, uri->number, pjsip_TEL_NUMBER_SPEC); /* ISDN sub-address or extension must appear first. */ /* Extension param. */ copy_advance_pair_escape(buf, ";ext=", 5, uri->ext_param, pjsip_TEL_EXT_VALUE_SPEC); /* ISDN sub-address. */ copy_advance_pair_escape(buf, ";isub=", 6, uri->isub_param, pjsip_TEL_URIC_SPEC); /* Followed by phone context, if present. */ copy_advance_pair_escape(buf, ";phone-context=", 15, uri->context, pjsip_TEL_PHONE_CONTEXT_SPEC); /* Print other parameters. */ printed = (int)pjsip_param_print_on(&uri->other_param, buf, (endbuf-buf), &pjsip_TEL_PNAME_SPEC, &pjsip_TEL_PVALUE_SPEC, ';'); if (printed < 0) return -1; buf += printed; *buf = '\0'; return (buf-startbuf); } /* Compare two numbers, according to RFC 3966: * - both must be either local or global numbers. * - The 'global-number-digits' and the 'local-number-digits' must be * equal, after removing all visual separators. */ PJ_DEF(int) pjsip_tel_nb_cmp(const pj_str_t *number1, const pj_str_t *number2) { const char *s1 = number1->ptr, *e1 = number1->ptr + number1->slen, *s2 = number2->ptr, *e2 = number2->ptr + number2->slen; /* Compare each number, ignoreing visual separators. */ while (s1!=e1 && s2!=e2) { int diff; if (pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s1)) { ++s1; continue; } if (pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s2)) { ++s2; continue; } diff = pj_tolower(*s1) - pj_tolower(*s2); if (!diff) { ++s1, ++s2; continue; } else return diff; } /* Exhaust remaining visual separators. */ while (s1!=e1 && pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s1)) ++s1; while (s2!=e2 && pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s2)) ++s2; if (s1==e1 && s2==e2) return 0; else if (s1==e1) return -1; else return 1; } /* Compare two tel: URI */ static int tel_uri_cmp( pjsip_uri_context_e context, const pjsip_tel_uri *url1, const pjsip_tel_uri *url2) { int result; PJ_UNUSED_ARG(context); /* Scheme must match. */ if (url1->vptr != url2->vptr) return -1; /* Compare number. */ result = pjsip_tel_nb_cmp(&url1->number, &url2->number); if (result != 0) return result; /* Compare phone-context as hostname or as as global nb. */ if (url1->context.slen) { if (*url1->context.ptr != '+') result = pj_stricmp(&url1->context, &url2->context); else result = pjsip_tel_nb_cmp(&url1->context, &url2->context); if (result != 0) return result; } else if (url2->context.slen) return -1; /* Compare extension. */ if (url1->ext_param.slen) { result = pjsip_tel_nb_cmp(&url1->ext_param, &url2->ext_param); if (result != 0) return result; } /* Compare isub bytes by bytes. */ if (url1->isub_param.slen) { result = pj_stricmp(&url1->isub_param, &url2->isub_param); if (result != 0) return result; } /* Other parameters are compared regardless of the order. * If one URI has parameter not found in the other URI, the URIs are * not equal. */ if (url1->other_param.next != &url1->other_param) { const pjsip_param *p1, *p2; int cnt1 = 0, cnt2 = 0; p1 = url1->other_param.next; while (p1 != &url1->other_param) { p2 = pjsip_param_cfind(&url2->other_param, &p1->name); if (!p2 ) return 1; result = pj_stricmp(&p1->value, &p2->value); if (result != 0) return result; p1 = p1->next; ++cnt1; } p2 = url2->other_param.next; while (p2 != &url2->other_param) ++cnt2, p2 = p2->next; if (cnt1 < cnt2) return -1; else if (cnt1 > cnt2) return 1; } else if (url2->other_param.next != &url2->other_param) return -1; /* Equal. */ return 0; } /* Clone tel: URI */ static pjsip_tel_uri* tel_uri_clone(pj_pool_t *pool, const pjsip_tel_uri *rhs) { pjsip_tel_uri *uri = pjsip_tel_uri_create(pool); pj_strdup(pool, &uri->number, &rhs->number); pj_strdup(pool, &uri->context, &rhs->context); pj_strdup(pool, &uri->ext_param, &rhs->ext_param); pj_strdup(pool, &uri->isub_param, &rhs->isub_param); pjsip_param_clone(pool, &uri->other_param, &rhs->other_param); return uri; } /* Parse tel: URI * THis actually returns (pjsip_tel_uri *) type. */ static void* tel_uri_parse( pj_scanner *scanner, pj_pool_t *pool, pj_bool_t parse_params) { pjsip_tel_uri *uri; pj_str_t token; int skip_ws = scanner->skip_ws; const pjsip_parser_const_t *pc = pjsip_parser_const(); scanner->skip_ws = 0; /* Parse scheme. */ pj_scan_get(scanner, &pc->pjsip_TOKEN_SPEC, &token); if (pj_scan_get_char(scanner) != ':') PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); if (pj_stricmp_alnum(&token, &pc->pjsip_TEL_STR) != 0) PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); /* Create URI */ uri = pjsip_tel_uri_create(pool); /* Get the phone number. */ #if defined(PJSIP_UNESCAPE_IN_PLACE) && PJSIP_UNESCAPE_IN_PLACE!=0 pj_scan_get_unescape(scanner, &pjsip_TEL_NUMBER_SPEC, &uri->number); #else pj_scan_get(scanner, &pjsip_TEL_NUMBER_SPEC, &uri->number); uri->number = pj_str_unescape(pool, &uri->number); #endif /* Get all parameters. */ if (parse_params && *scanner->curptr==';') { pj_str_t pname, pvalue; do { /* Eat the ';' separator. */ pj_scan_get_char(scanner); /* Get pname. */ pj_scan_get(scanner, &pc->pjsip_PARAM_CHAR_SPEC, &pname); if (*scanner->curptr == '=') { pj_scan_get_char(scanner); # if defined(PJSIP_UNESCAPE_IN_PLACE) && PJSIP_UNESCAPE_IN_PLACE!=0 pj_scan_get_unescape(scanner, &pjsip_TEL_PARSING_PVALUE_SPEC_ESC, &pvalue); # else pj_scan_get(scanner, &pjsip_TEL_PARSING_PVALUE_SPEC, &pvalue); pvalue = pj_str_unescape(pool, &pvalue); # endif } else { pvalue.slen = 0; pvalue.ptr = NULL; } /* Save the parameters. */ if (pj_stricmp_alnum(&pname, &pjsip_ISUB_STR)==0) { uri->isub_param = pvalue; } else if (pj_stricmp_alnum(&pname, &pjsip_EXT_STR)==0) { uri->ext_param = pvalue; } else if (pj_stricmp_alnum(&pname, &pjsip_PH_CTX_STR)==0) { uri->context = pvalue; } else { pjsip_param *param = PJ_POOL_ALLOC_T(pool, pjsip_param); param->name = pname; param->value = pvalue; pj_list_insert_before(&uri->other_param, param); } } while (*scanner->curptr==';'); } scanner->skip_ws = skip_ws; pj_scan_skip_whitespace(scanner); return uri; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_tel_uri_wrap.cpp ================================================ /* $Id: sip_tel_uri_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This file is a C++ wrapper, see ticket #886 for details. */ #include "sip_tel_uri.c" ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_transaction.c ================================================ /* $Id: sip_transaction.c 4516 2013-05-06 09:10:56Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "sip_transaction.c" #if 0 #define TSX_TRACE_(expr) PJ_LOG(3,expr) #else #define TSX_TRACE_(expr) #endif /* When this macro is set, transaction will keep the hashed value * so that future lookup (to unregister transaction) does not need * to recalculate the hash again. It should gains a little bit of * performance, so generally we'd want this. */ #define PRECALC_HASH /* Defined in sip_util_statefull.c */ extern pjsip_module mod_stateful_util; /***************************************************************************** ** ** Declarations and static variable definitions section. ** ***************************************************************************** **/ /* Prototypes. */ static pj_status_t mod_tsx_layer_load(pjsip_endpoint *endpt); static pj_status_t mod_tsx_layer_start(void); static pj_status_t mod_tsx_layer_stop(void); static pj_status_t mod_tsx_layer_unload(void); static pj_bool_t mod_tsx_layer_on_rx_request(pjsip_rx_data *rdata); static pj_bool_t mod_tsx_layer_on_rx_response(pjsip_rx_data *rdata); /* Transaction layer module definition. */ static struct mod_tsx_layer { struct pjsip_module mod; pj_pool_t *pool; pjsip_endpoint *endpt; pj_mutex_t *mutex; pj_hash_table_t *htable; } mod_tsx_layer = { { NULL, NULL, /* List's prev and next. */ { "mod-tsx-layer", 13 }, /* Module name. */ -1, /* Module ID */ PJSIP_MOD_PRIORITY_TSX_LAYER, /* Priority. */ mod_tsx_layer_load, /* load(). */ mod_tsx_layer_start, /* start() */ mod_tsx_layer_stop, /* stop() */ mod_tsx_layer_unload, /* unload() */ mod_tsx_layer_on_rx_request, /* on_rx_request() */ mod_tsx_layer_on_rx_response, /* on_rx_response() */ NULL } }; /* Transaction state names */ static const char *state_str[] = { "Null", "Calling", "Trying", "Proceeding", "Completed", "Confirmed", "Terminated", "Destroyed", }; /* Role names */ static const char *role_name[] = { "UAC", "UAS" }; /* Transport flag. */ enum { TSX_HAS_PENDING_TRANSPORT = 1, TSX_HAS_PENDING_RESCHED = 2, TSX_HAS_PENDING_SEND = 4, TSX_HAS_PENDING_DESTROY = 8, TSX_HAS_RESOLVED_SERVER = 16, }; /* Timer timeout value constants */ static pj_time_val t1_timer_val = { PJSIP_T1_TIMEOUT/1000, PJSIP_T1_TIMEOUT%1000 }; static pj_time_val t2_timer_val = { PJSIP_T2_TIMEOUT/1000, PJSIP_T2_TIMEOUT%1000 }; static pj_time_val t4_timer_val = { PJSIP_T4_TIMEOUT/1000, PJSIP_T4_TIMEOUT%1000 }; static pj_time_val td_timer_val = { PJSIP_TD_TIMEOUT/1000, PJSIP_TD_TIMEOUT%1000 }; static pj_time_val timeout_timer_val = { (64*PJSIP_T1_TIMEOUT)/1000, (64*PJSIP_T1_TIMEOUT)%1000 }; #define TIMER_INACTIVE 0 #define RETRANSMIT_TIMER 1 #define TIMEOUT_TIMER 2 #define TRANSPORT_ERR_TIMER 3 /* Flags for tsx_set_state() */ enum { NO_NOTIFY = 1, NO_SCHEDULE_HANDLER = 2, }; /* Prototypes. */ static pj_status_t tsx_on_state_null( pjsip_transaction *tsx, pjsip_event *event); static pj_status_t tsx_on_state_calling( pjsip_transaction *tsx, pjsip_event *event); static pj_status_t tsx_on_state_trying( pjsip_transaction *tsx, pjsip_event *event); static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx, pjsip_event *event); static pj_status_t tsx_on_state_proceeding_uac( pjsip_transaction *tsx, pjsip_event *event); static pj_status_t tsx_on_state_completed_uas( pjsip_transaction *tsx, pjsip_event *event); static pj_status_t tsx_on_state_completed_uac( pjsip_transaction *tsx, pjsip_event *event); static pj_status_t tsx_on_state_confirmed( pjsip_transaction *tsx, pjsip_event *event); static pj_status_t tsx_on_state_terminated( pjsip_transaction *tsx, pjsip_event *event); static pj_status_t tsx_on_state_destroyed( pjsip_transaction *tsx, pjsip_event *event); static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry); static void tsx_tp_state_callback( pjsip_transport *tp, pjsip_transport_state state, const pjsip_transport_state_info *info); static void tsx_set_state( pjsip_transaction *tsx, pjsip_tsx_state_e state, pjsip_event_id_e event_src_type, void *event_src, int flag); static void tsx_set_status_code(pjsip_transaction *tsx, int code, const pj_str_t *reason); static pj_status_t tsx_create( pjsip_module *tsx_user, pj_grp_lock_t *grp_lock, pjsip_transaction **p_tsx); static void tsx_on_destroy(void *arg); static pj_status_t tsx_shutdown( pjsip_transaction *tsx ); static void tsx_resched_retransmission( pjsip_transaction *tsx ); static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched); static int tsx_send_msg( pjsip_transaction *tsx, pjsip_tx_data *tdata); static void tsx_update_transport( pjsip_transaction *tsx, pjsip_transport *tp); /* State handlers for UAC, indexed by state */ static int (*tsx_state_handler_uac[PJSIP_TSX_STATE_MAX])(pjsip_transaction *, pjsip_event *) = { &tsx_on_state_null, &tsx_on_state_calling, NULL, &tsx_on_state_proceeding_uac, &tsx_on_state_completed_uac, &tsx_on_state_confirmed, &tsx_on_state_terminated, &tsx_on_state_destroyed, }; /* State handlers for UAS */ static int (*tsx_state_handler_uas[PJSIP_TSX_STATE_MAX])(pjsip_transaction *, pjsip_event *) = { &tsx_on_state_null, NULL, &tsx_on_state_trying, &tsx_on_state_proceeding_uas, &tsx_on_state_completed_uas, &tsx_on_state_confirmed, &tsx_on_state_terminated, &tsx_on_state_destroyed, }; /***************************************************************************** ** ** Utilities ** ***************************************************************************** */ /* * Get transaction state name. */ PJ_DEF(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state) { return state_str[state]; } /* * Get the role name. */ PJ_DEF(const char *) pjsip_role_name(pjsip_role_e role) { return role_name[role]; } /* * Create transaction key for RFC2543 compliant messages, which don't have * unique branch parameter in the top most Via header. * * INVITE requests matches a transaction if the following attributes * match the original request: * - Request-URI * - To tag * - From tag * - Call-ID * - CSeq * - top Via header * * CANCEL matching is done similarly as INVITE, except: * - CSeq method will differ * - To tag is not matched. * * ACK matching is done similarly, except that: * - method of the CSeq will differ, * - To tag is matched to the response sent by the server transaction. * * The transaction key is constructed from the common components of above * components. Additional comparison is needed to fully match a transaction. */ static pj_status_t create_tsx_key_2543( pj_pool_t *pool, pj_str_t *str, pjsip_role_e role, const pjsip_method *method, const pjsip_rx_data *rdata ) { #define SEPARATOR '$' char *key, *p; pj_ssize_t len; pj_size_t len_required; pj_str_t *host; PJ_ASSERT_RETURN(pool && str && method && rdata, PJ_EINVAL); PJ_ASSERT_RETURN(rdata->msg_info.msg, PJ_EINVAL); PJ_ASSERT_RETURN(rdata->msg_info.via, PJSIP_EMISSINGHDR); PJ_ASSERT_RETURN(rdata->msg_info.cseq, PJSIP_EMISSINGHDR); PJ_ASSERT_RETURN(rdata->msg_info.from, PJSIP_EMISSINGHDR); host = &rdata->msg_info.via->sent_by.host; /* Calculate length required. */ len_required = 9 + /* CSeq number */ rdata->msg_info.from->tag.slen + /* From tag. */ rdata->msg_info.cid->id.slen + /* Call-ID */ host->slen + /* Via host. */ 9 + /* Via port. */ 16; /* Separator+Allowance. */ key = p = (char*) pj_pool_alloc(pool, len_required); /* Add role. */ *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's'); *p++ = SEPARATOR; /* Add method, except when method is INVITE or ACK. */ if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) { pj_memcpy(p, method->name.ptr, method->name.slen); p += method->name.slen; *p++ = '$'; } /* Add CSeq (only the number). */ len = pj_utoa(rdata->msg_info.cseq->cseq, p); p += len; *p++ = SEPARATOR; /* Add From tag. */ len = rdata->msg_info.from->tag.slen; pj_memcpy( p, rdata->msg_info.from->tag.ptr, len); p += len; *p++ = SEPARATOR; /* Add Call-ID. */ len = rdata->msg_info.cid->id.slen; pj_memcpy( p, rdata->msg_info.cid->id.ptr, len ); p += len; *p++ = SEPARATOR; /* Add top Via header. * We don't really care whether the port contains the real port (because * it can be omited if default port is used). Anyway this function is * only used to match request retransmission, and we expect that the * request retransmissions will contain the same port. */ pj_memcpy(p, host->ptr, host->slen); p += host->slen; *p++ = ':'; len = pj_utoa(rdata->msg_info.via->sent_by.port, p); p += len; *p++ = SEPARATOR; *p++ = '\0'; /* Done. */ str->ptr = key; str->slen = p-key; return PJ_SUCCESS; } /* * Create transaction key for RFC3161 compliant system. */ static pj_status_t create_tsx_key_3261( pj_pool_t *pool, pj_str_t *key, pjsip_role_e role, const pjsip_method *method, const pj_str_t *branch) { char *p; PJ_ASSERT_RETURN(pool && key && method && branch, PJ_EINVAL); p = key->ptr = (char*) pj_pool_alloc(pool, branch->slen + method->name.slen + 4 ); /* Add role. */ *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's'); *p++ = SEPARATOR; /* Add method, except when method is INVITE or ACK. */ if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) { pj_memcpy(p, method->name.ptr, method->name.slen); p += method->name.slen; *p++ = '$'; } /* Add branch ID. */ pj_memcpy(p, branch->ptr, branch->slen); p += branch->slen; /* Set length */ key->slen = p - key->ptr; return PJ_SUCCESS; } /* * Create key from the incoming data, to be used to search the transaction * in the transaction hash table. */ PJ_DEF(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *key, pjsip_role_e role, const pjsip_method *method, const pjsip_rx_data *rdata) { pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN}; /* Get the branch parameter in the top-most Via. * If branch parameter is started with "z9hG4bK", then the message was * generated by agent compliant with RFC3261. Otherwise, it will be * handled as RFC2543. */ const pj_str_t *branch = &rdata->msg_info.via->branch_param; if (pj_strnicmp(branch,&rfc3261_branch,PJSIP_RFC3261_BRANCH_LEN)==0) { /* Create transaction key. */ return create_tsx_key_3261(pool, key, role, method, branch); } else { /* Create the key for the message. This key will be matched up * with the transaction key. For RFC2563 transactions, the * transaction key was created by the same function, so it will * match the message. */ return create_tsx_key_2543( pool, key, role, method, rdata ); } } /***************************************************************************** ** ** Transaction layer module ** ***************************************************************************** **/ /* * Create transaction layer module and registers it to the endpoint. */ PJ_DEF(pj_status_t) pjsip_tsx_layer_init_module(pjsip_endpoint *endpt) { pj_pool_t *pool; pj_status_t status; PJ_ASSERT_RETURN(mod_tsx_layer.endpt==NULL, PJ_EINVALIDOP); /* Initialize timer values */ t1_timer_val.sec = pjsip_cfg()->tsx.t1 / 1000; t1_timer_val.msec = pjsip_cfg()->tsx.t1 % 1000; t2_timer_val.sec = pjsip_cfg()->tsx.t2 / 1000; t2_timer_val.msec = pjsip_cfg()->tsx.t2 % 1000; t4_timer_val.sec = pjsip_cfg()->tsx.t4 / 1000; t4_timer_val.msec = pjsip_cfg()->tsx.t4 % 1000; td_timer_val.sec = pjsip_cfg()->tsx.td / 1000; td_timer_val.msec = pjsip_cfg()->tsx.td % 1000; /* Changed the initialization below to use td_timer_val instead, to enable * customization to the timeout value. */ //timeout_timer_val.sec = (64 * pjsip_cfg()->tsx.t1) / 1000; //timeout_timer_val.msec = (64 * pjsip_cfg()->tsx.t1) % 1000; timeout_timer_val = td_timer_val; /* * Initialize transaction layer structure. */ /* Create pool for the module. */ pool = pjsip_endpt_create_pool(endpt, "tsxlayer", PJSIP_POOL_TSX_LAYER_LEN, PJSIP_POOL_TSX_LAYER_INC ); if (!pool) return PJ_ENOMEM; /* Initialize some attributes. */ mod_tsx_layer.pool = pool; mod_tsx_layer.endpt = endpt; /* Create hash table. */ mod_tsx_layer.htable = pj_hash_create( pool, pjsip_cfg()->tsx.max_count ); if (!mod_tsx_layer.htable) { pjsip_endpt_release_pool(endpt, pool); return PJ_ENOMEM; } /* Create group lock. */ status = pj_mutex_create_recursive(pool, "tsxlayer", &mod_tsx_layer.mutex); if (status != PJ_SUCCESS) { pjsip_endpt_release_pool(endpt, pool); return status; } /* * Register transaction layer module to endpoint. */ status = pjsip_endpt_register_module( endpt, &mod_tsx_layer.mod ); if (status != PJ_SUCCESS) { pj_mutex_destroy(mod_tsx_layer.mutex); pjsip_endpt_release_pool(endpt, pool); return status; } /* Register mod_stateful_util module (sip_util_statefull.c) */ status = pjsip_endpt_register_module(endpt, &mod_stateful_util); if (status != PJ_SUCCESS) { return status; } return PJ_SUCCESS; } /* * Get the instance of transaction layer module. */ PJ_DEF(pjsip_module*) pjsip_tsx_layer_instance(void) { return &mod_tsx_layer.mod; } /* * Unregister and destroy transaction layer module. */ PJ_DEF(pj_status_t) pjsip_tsx_layer_destroy(void) { /* Are we registered? */ PJ_ASSERT_RETURN(mod_tsx_layer.endpt!=NULL, PJ_EINVALIDOP); /* Unregister from endpoint. * Clean-ups will be done in the unload() module callback. */ return pjsip_endpt_unregister_module( mod_tsx_layer.endpt, &mod_tsx_layer.mod); } /* * Register the transaction to the hash table. */ static pj_status_t mod_tsx_layer_register_tsx( pjsip_transaction *tsx) { pj_assert(tsx->transaction_key.slen != 0); /* Lock hash table mutex. */ pj_mutex_lock(mod_tsx_layer.mutex); /* Check if no transaction with the same key exists. * Do not use PJ_ASSERT_RETURN since it evaluates the expression * twice! */ if(pj_hash_get_lower(mod_tsx_layer.htable, tsx->transaction_key.ptr, (unsigned)tsx->transaction_key.slen, NULL)) { pj_mutex_unlock(mod_tsx_layer.mutex); PJ_LOG(2,(THIS_FILE, "Unable to register %.*s transaction (key exists)", (int)tsx->method.name.slen, tsx->method.name.ptr)); return PJ_EEXISTS; } TSX_TRACE_((THIS_FILE, "Transaction %p registered with hkey=0x%p and key=%.*s", tsx, tsx->hashed_key, tsx->transaction_key.slen, tsx->transaction_key.ptr)); /* Register the transaction to the hash table. */ #ifdef PRECALC_HASH pj_hash_set_lower( tsx->pool, mod_tsx_layer.htable, tsx->transaction_key.ptr, (unsigned)tsx->transaction_key.slen, tsx->hashed_key, tsx); #else pj_hash_set_lower( tsx->pool, mod_tsx_layer.htable, tsx->transaction_key.ptr, tsx->transaction_key.slen, 0, tsx); #endif /* Unlock mutex. */ pj_mutex_unlock(mod_tsx_layer.mutex); return PJ_SUCCESS; } /* * Unregister the transaction from the hash table. */ static void mod_tsx_layer_unregister_tsx( pjsip_transaction *tsx) { if (mod_tsx_layer.mod.id == -1) { /* The transaction layer has been unregistered. This could happen * if the transaction was pending on transport and the application * is shutdown. See http://trac.pjsip.org/repos/ticket/1033. In * this case just do nothing. */ return; } pj_assert(tsx->transaction_key.slen != 0); //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL); /* Lock hash table mutex. */ pj_mutex_lock(mod_tsx_layer.mutex); /* Register the transaction to the hash table. */ #ifdef PRECALC_HASH pj_hash_set_lower( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr, (unsigned)tsx->transaction_key.slen, tsx->hashed_key, NULL); #else pj_hash_set_lower( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr, tsx->transaction_key.slen, 0, NULL); #endif TSX_TRACE_((THIS_FILE, "Transaction %p unregistered, hkey=0x%p and key=%.*s", tsx, tsx->hashed_key, tsx->transaction_key.slen, tsx->transaction_key.ptr)); /* Unlock mutex. */ pj_mutex_unlock(mod_tsx_layer.mutex); } /* * Retrieve the current number of transactions currently registered in * the hash table. */ PJ_DEF(unsigned) pjsip_tsx_layer_get_tsx_count(void) { unsigned count; /* Are we registered? */ PJ_ASSERT_RETURN(mod_tsx_layer.endpt!=NULL, 0); pj_mutex_lock(mod_tsx_layer.mutex); count = pj_hash_count(mod_tsx_layer.htable); pj_mutex_unlock(mod_tsx_layer.mutex); return count; } /* * Find a transaction. */ PJ_DEF(pjsip_transaction*) pjsip_tsx_layer_find_tsx( const pj_str_t *key, pj_bool_t lock ) { pjsip_transaction *tsx; pj_uint32_t hval = 0; pj_mutex_lock(mod_tsx_layer.mutex); tsx = (pjsip_transaction*) pj_hash_get_lower( mod_tsx_layer.htable, key->ptr, (unsigned)key->slen, &hval ); /* Prevent the transaction to get deleted before we have chance to lock it. */ if (tsx && lock) pj_grp_lock_add_ref(tsx->grp_lock); pj_mutex_unlock(mod_tsx_layer.mutex); TSX_TRACE_((THIS_FILE, "Finding tsx with hkey=0x%p and key=%.*s: found %p", hval, key->slen, key->ptr, tsx)); /* Simulate race condition! */ PJ_RACE_ME(5); if (tsx && lock) { pj_grp_lock_acquire(tsx->grp_lock); pj_grp_lock_dec_ref(tsx->grp_lock); } return tsx; } /* This module callback is called when module is being loaded by * endpoint. It does nothing for this module. */ static pj_status_t mod_tsx_layer_load(pjsip_endpoint *endpt) { PJ_UNUSED_ARG(endpt); return PJ_SUCCESS; } /* This module callback is called when module is being started by * endpoint. It does nothing for this module. */ static pj_status_t mod_tsx_layer_start(void) { return PJ_SUCCESS; } /* This module callback is called when module is being stopped by * endpoint. */ static pj_status_t mod_tsx_layer_stop(void) { pj_hash_iterator_t it_buf, *it; PJ_LOG(4,(THIS_FILE, "Stopping transaction layer module")); pj_mutex_lock(mod_tsx_layer.mutex); /* Destroy all transactions. */ it = pj_hash_first(mod_tsx_layer.htable, &it_buf); while (it) { pjsip_transaction *tsx = (pjsip_transaction*) pj_hash_this(mod_tsx_layer.htable, it); pj_hash_iterator_t *next = pj_hash_next(mod_tsx_layer.htable, it); if (tsx) { pjsip_tsx_terminate(tsx, PJSIP_SC_SERVICE_UNAVAILABLE); mod_tsx_layer_unregister_tsx(tsx); tsx_shutdown(tsx); } it = next; } pj_mutex_unlock(mod_tsx_layer.mutex); PJ_LOG(4,(THIS_FILE, "Stopped transaction layer module")); return PJ_SUCCESS; } /* Destroy this module */ static void tsx_layer_destroy(pjsip_endpoint *endpt) { PJ_UNUSED_ARG(endpt); /* Destroy mutex. */ pj_mutex_destroy(mod_tsx_layer.mutex); /* Release pool. */ pjsip_endpt_release_pool(mod_tsx_layer.endpt, mod_tsx_layer.pool); /* Mark as unregistered. */ mod_tsx_layer.endpt = NULL; PJ_LOG(4,(THIS_FILE, "Transaction layer module destroyed")); } /* This module callback is called when module is being unloaded by * endpoint. */ static pj_status_t mod_tsx_layer_unload(void) { /* Only self destroy when there's no transaction in the table. * Transaction may refuse to destroy when it has pending * transmission. If we destroy the module now, application will * crash when the pending transaction finally got error response * from transport and when it tries to unregister itself. */ if (pj_hash_count(mod_tsx_layer.htable) != 0) { if (pjsip_endpt_atexit(mod_tsx_layer.endpt, &tsx_layer_destroy) != PJ_SUCCESS) { PJ_LOG(3,(THIS_FILE, "Failed to register transaction layer " "module destroy.")); } return PJ_EBUSY; } tsx_layer_destroy(mod_tsx_layer.endpt); return PJ_SUCCESS; } /* This module callback is called when endpoint has received an * incoming request message. */ static pj_bool_t mod_tsx_layer_on_rx_request(pjsip_rx_data *rdata) { pj_str_t key; pj_uint32_t hval = 0; pjsip_transaction *tsx; pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAS, &rdata->msg_info.cseq->method, rdata); /* Find transaction. */ pj_mutex_lock( mod_tsx_layer.mutex ); tsx = (pjsip_transaction*) pj_hash_get_lower( mod_tsx_layer.htable, key.ptr, (unsigned)key.slen, &hval ); TSX_TRACE_((THIS_FILE, "Finding tsx for request, hkey=0x%p and key=%.*s, found %p", hval, key.slen, key.ptr, tsx)); if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) { /* Transaction not found. * Reject the request so that endpoint passes the request to * upper layer modules. */ pj_mutex_unlock( mod_tsx_layer.mutex); return PJ_FALSE; } /* Prevent the transaction to get deleted before we have chance to lock it * in pjsip_tsx_recv_msg(). */ pj_grp_lock_add_ref(tsx->grp_lock); /* Unlock hash table. */ pj_mutex_unlock( mod_tsx_layer.mutex ); /* Simulate race condition! */ PJ_RACE_ME(5); /* Pass the message to the transaction. */ pjsip_tsx_recv_msg(tsx, rdata ); pj_grp_lock_dec_ref(tsx->grp_lock); return PJ_TRUE; } /* This module callback is called when endpoint has received an * incoming response message. */ static pj_bool_t mod_tsx_layer_on_rx_response(pjsip_rx_data *rdata) { pj_str_t key; pj_uint32_t hval = 0; pjsip_transaction *tsx; pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAC, &rdata->msg_info.cseq->method, rdata); /* Find transaction. */ pj_mutex_lock( mod_tsx_layer.mutex ); tsx = (pjsip_transaction*) pj_hash_get_lower( mod_tsx_layer.htable, key.ptr, (unsigned)key.slen, &hval ); TSX_TRACE_((THIS_FILE, "Finding tsx for response, hkey=0x%p and key=%.*s, found %p", hval, key.slen, key.ptr, tsx)); if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) { /* Transaction not found. * Reject the request so that endpoint passes the request to * upper layer modules. */ pj_mutex_unlock( mod_tsx_layer.mutex); return PJ_FALSE; } /* Prevent the transaction to get deleted before we have chance to lock it * in pjsip_tsx_recv_msg(). */ pj_grp_lock_add_ref(tsx->grp_lock); /* Unlock hash table. */ pj_mutex_unlock( mod_tsx_layer.mutex ); /* Simulate race condition! */ PJ_RACE_ME(5); /* Pass the message to the transaction. */ pjsip_tsx_recv_msg(tsx, rdata ); pj_grp_lock_dec_ref(tsx->grp_lock); return PJ_TRUE; } /* * Get transaction instance in the rdata. */ PJ_DEF(pjsip_transaction*) pjsip_rdata_get_tsx( pjsip_rx_data *rdata ) { return (pjsip_transaction*) rdata->endpt_info.mod_data[mod_tsx_layer.mod.id]; } /* * Dump transaction layer. */ PJ_DEF(void) pjsip_tsx_layer_dump(pj_bool_t detail) { #if PJ_LOG_MAX_LEVEL >= 3 pj_hash_iterator_t itbuf, *it; /* Lock mutex. */ pj_mutex_lock(mod_tsx_layer.mutex); PJ_LOG(3, (THIS_FILE, "Dumping transaction table:")); PJ_LOG(3, (THIS_FILE, " Total %d transactions", pj_hash_count(mod_tsx_layer.htable))); if (detail) { it = pj_hash_first(mod_tsx_layer.htable, &itbuf); if (it == NULL) { PJ_LOG(3, (THIS_FILE, " - none - ")); } else { while (it != NULL) { pjsip_transaction *tsx = (pjsip_transaction*) pj_hash_this(mod_tsx_layer.htable,it); PJ_LOG(3, (THIS_FILE, " %s %s|%d|%s", tsx->obj_name, (tsx->last_tx? pjsip_tx_data_get_info(tsx->last_tx): "none"), tsx->status_code, pjsip_tsx_state_str(tsx->state))); it = pj_hash_next(mod_tsx_layer.htable, it); } } } /* Unlock mutex. */ pj_mutex_unlock(mod_tsx_layer.mutex); #endif } /***************************************************************************** ** ** Transaction ** ***************************************************************************** **/ /* Lock transaction for accessing the timeout timer only. */ static void lock_timer(pjsip_transaction *tsx) { pj_mutex_lock(tsx->mutex_b); } /* Unlock timer */ static void unlock_timer(pjsip_transaction *tsx) { pj_mutex_unlock(tsx->mutex_b); } /* Utility: schedule a timer */ static pj_status_t tsx_schedule_timer(pjsip_transaction *tsx, pj_timer_entry *entry, const pj_time_val *delay, int active_id) { pj_timer_heap_t *timer_heap = pjsip_endpt_get_timer_heap(tsx->endpt); pj_status_t status; pj_assert(active_id != 0); status = pj_timer_heap_schedule_w_grp_lock(timer_heap, entry, delay, active_id, tsx->grp_lock); return status; } /* Utility: cancel a timer */ static int tsx_cancel_timer(pjsip_transaction *tsx, pj_timer_entry *entry) { pj_timer_heap_t *timer_heap = pjsip_endpt_get_timer_heap(tsx->endpt); return pj_timer_heap_cancel_if_active(timer_heap, entry, TIMER_INACTIVE); } /* Create and initialize basic transaction structure. * This function is called by both UAC and UAS creation. */ static pj_status_t tsx_create( pjsip_module *tsx_user, pj_grp_lock_t *grp_lock, pjsip_transaction **p_tsx) { pj_pool_t *pool; pjsip_transaction *tsx; pj_status_t status; pool = pjsip_endpt_create_pool( mod_tsx_layer.endpt, "tsx", PJSIP_POOL_TSX_LEN, PJSIP_POOL_TSX_INC ); if (!pool) return PJ_ENOMEM; tsx = PJ_POOL_ZALLOC_T(pool, pjsip_transaction); tsx->pool = pool; tsx->tsx_user = tsx_user; tsx->endpt = mod_tsx_layer.endpt; pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), "tsx%p", tsx); pj_memcpy(pool->obj_name, tsx->obj_name, sizeof(pool->obj_name)); tsx->handle_200resp = 1; tsx->retransmit_timer.id = TIMER_INACTIVE; tsx->retransmit_timer.user_data = tsx; tsx->retransmit_timer.cb = &tsx_timer_callback; tsx->timeout_timer.id = TIMER_INACTIVE; tsx->timeout_timer.user_data = tsx; tsx->timeout_timer.cb = &tsx_timer_callback; if (grp_lock) { tsx->grp_lock = grp_lock; pj_grp_lock_add_ref(tsx->grp_lock); pj_grp_lock_add_handler(tsx->grp_lock, tsx->pool, tsx, &tsx_on_destroy); } else { status = pj_grp_lock_create_w_handler(pool, NULL, tsx, &tsx_on_destroy, &tsx->grp_lock); if (status != PJ_SUCCESS) { pjsip_endpt_release_pool(mod_tsx_layer.endpt, pool); return status; } pj_grp_lock_add_ref(tsx->grp_lock); } status = pj_mutex_create_simple(pool, tsx->obj_name, &tsx->mutex_b); if (status != PJ_SUCCESS) { tsx_shutdown(tsx); return status; } *p_tsx = tsx; return PJ_SUCCESS; } /* Really destroy transaction, when grp_lock reference is zero */ static void tsx_on_destroy( void *arg ) { pjsip_transaction *tsx = (pjsip_transaction*)arg; PJ_LOG(5,(tsx->obj_name, "Transaction destroyed!")); pj_mutex_destroy(tsx->mutex_b); pjsip_endpt_release_pool(tsx->endpt, tsx->pool); } /* Shutdown transaction. */ static pj_status_t tsx_shutdown( pjsip_transaction *tsx ) { /* Release the transport */ tsx_update_transport(tsx, NULL); /* Decrement reference counter in transport selector, only if * we haven't been called before */ if (!tsx->terminating) { pjsip_tpselector_dec_ref(&tsx->tp_sel); } /* Free last transmitted message. */ if (tsx->last_tx) { pjsip_tx_data_dec_ref( tsx->last_tx ); tsx->last_tx = NULL; } /* Cancel timeout timer. */ tsx_cancel_timer(tsx, &tsx->timeout_timer); /* Cancel retransmission timer. */ tsx_cancel_timer(tsx, &tsx->retransmit_timer); /* Clear some pending flags. */ tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED | TSX_HAS_PENDING_SEND); /* Refuse to destroy transaction if it has pending resolving. */ if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) { tsx->transport_flag |= TSX_HAS_PENDING_DESTROY; tsx->tsx_user = NULL; PJ_LOG(4,(tsx->obj_name, "Will destroy later because transport is " "in progress")); } if (!tsx->terminating) { tsx->terminating = PJ_TRUE; pj_grp_lock_dec_ref(tsx->grp_lock); } /* No acccess to tsx after this, it may have been destroyed */ return PJ_SUCCESS; } /* * Callback when timer expires. Transport error also piggybacks this event * to avoid deadlock (https://trac.pjsip.org/repos/ticket/1646). */ static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry) { pjsip_transaction *tsx = (pjsip_transaction*) entry->user_data; PJ_UNUSED_ARG(theap); if (entry->id == TRANSPORT_ERR_TIMER) { /* Posted transport error event */ entry->id = 0; if (tsx->state < PJSIP_TSX_STATE_TERMINATED) { pjsip_tsx_state_e prev_state; pj_time_val timeout = { 0, 0 }; pj_grp_lock_acquire(tsx->grp_lock); prev_state = tsx->state; /* Release transport as it's no longer working. */ tsx_update_transport(tsx, NULL); if (tsx->status_code < 200) { pj_str_t err; char errmsg[PJ_ERR_MSG_SIZE]; err = pj_strerror(tsx->transport_err, errmsg, sizeof(errmsg)); tsx_set_status_code(tsx, PJSIP_SC_TSX_TRANSPORT_ERROR, &err); } /* Set transaction state etc, but don't notify TU now, * otherwise we'll get a deadlock. See: * https://trac.pjsip.org/repos/ticket/1646 */ /* Also don't schedule tsx handler, otherwise we'll get race * condition of TU notifications due to delayed TERMINATED * state TU notification. It happened in multiple worker threads * environment between TERMINATED & DESTROYED! See: * https://trac.pjsip.org/repos/ticket/1902 */ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_TRANSPORT_ERROR, NULL, NO_NOTIFY | NO_SCHEDULE_HANDLER); pj_grp_lock_release(tsx->grp_lock); /* Now notify TU about state change, WITHOUT holding the * group lock. It should be safe to do so; transaction will * not get destroyed because group lock reference counter * has been incremented by the timer heap. */ if (tsx->tsx_user && tsx->tsx_user->on_tsx_state) { pjsip_event e; PJSIP_EVENT_INIT_TSX_STATE(e, tsx, PJSIP_EVENT_TRANSPORT_ERROR, NULL, prev_state); (*tsx->tsx_user->on_tsx_state)(tsx, &e); } /* Now let's schedule the tsx handler */ tsx_schedule_timer(tsx, &tsx->timeout_timer, &timeout, TIMEOUT_TIMER); } } else { pjsip_event event; entry->id = 0; PJ_LOG(5,(tsx->obj_name, "%s timer event", (entry==&tsx->retransmit_timer ? "Retransmit":"Timeout"))); pj_log_push_indent(); PJSIP_EVENT_INIT_TIMER(event, entry); /* Dispatch event to transaction. */ pj_grp_lock_acquire(tsx->grp_lock); (*tsx->state_handler)(tsx, &event); pj_grp_lock_release(tsx->grp_lock); pj_log_pop_indent(); } } /* * Set transaction state, and inform TU about the transaction state change. */ static void tsx_set_state( pjsip_transaction *tsx, pjsip_tsx_state_e state, pjsip_event_id_e event_src_type, void *event_src, int flag) { pjsip_tsx_state_e prev_state = tsx->state; /* New state must be greater than previous state */ pj_assert(state >= tsx->state); PJ_LOG(5, (tsx->obj_name, "State changed from %s to %s, event=%s", state_str[tsx->state], state_str[state], pjsip_event_str(event_src_type))); pj_log_push_indent(); /* Change state. */ tsx->state = state; /* Update the state handlers. */ if (tsx->role == PJSIP_ROLE_UAC) { tsx->state_handler = tsx_state_handler_uac[state]; } else { tsx->state_handler = tsx_state_handler_uas[state]; } /* Before informing TU about state changed, inform TU about * rx event. */ if (event_src_type==PJSIP_EVENT_RX_MSG && tsx->tsx_user && (flag & NO_NOTIFY)==0) { pjsip_rx_data *rdata = (pjsip_rx_data*) event_src; pj_assert(rdata != NULL); if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG && tsx->tsx_user->on_rx_response) { (*tsx->tsx_user->on_rx_response)(rdata); } } /* Inform TU about state changed. */ if (tsx->tsx_user && tsx->tsx_user->on_tsx_state && (flag & NO_NOTIFY) == 0) { pjsip_event e; PJSIP_EVENT_INIT_TSX_STATE(e, tsx, event_src_type, event_src, prev_state); (*tsx->tsx_user->on_tsx_state)(tsx, &e); } /* When the transaction is terminated, release transport, and free the * saved last transmitted message. */ if (state == PJSIP_TSX_STATE_TERMINATED) { pj_time_val timeout = { 0, 0 }; /* If we're still waiting for a message to be sent.. */ if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) { /* Disassociate ourselves from the outstanding transmit data * so that when the send callback is called we will be able * to ignore that (otherwise we'll get assertion, see * http://trac.pjsip.org/repos/ticket/1033) */ if (tsx->pending_tx) { tsx->pending_tx->mod_data[mod_tsx_layer.mod.id] = NULL; tsx->pending_tx = NULL; } tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT); } lock_timer(tsx); tsx_cancel_timer(tsx, &tsx->timeout_timer); if ((flag & NO_SCHEDULE_HANDLER) == 0) { tsx_schedule_timer(tsx, &tsx->timeout_timer, &timeout, TIMEOUT_TIMER); } unlock_timer(tsx); } else if (state == PJSIP_TSX_STATE_DESTROYED) { /* Unregister transaction. */ mod_tsx_layer_unregister_tsx(tsx); /* Destroy transaction. */ tsx_shutdown(tsx); } pj_log_pop_indent(); } /* * Create, initialize, and register UAC transaction. */ PJ_DEF(pj_status_t) pjsip_tsx_create_uac( pjsip_module *tsx_user, pjsip_tx_data *tdata, pjsip_transaction **p_tsx) { return pjsip_tsx_create_uac2(tsx_user, tdata, NULL, p_tsx); } PJ_DEF(pj_status_t) pjsip_tsx_create_uac2(pjsip_module *tsx_user, pjsip_tx_data *tdata, pj_grp_lock_t *grp_lock, pjsip_transaction **p_tsx) { pjsip_transaction *tsx; pjsip_msg *msg; pjsip_cseq_hdr *cseq; pjsip_via_hdr *via; pjsip_host_info dst_info; pj_status_t status; /* Validate arguments. */ PJ_ASSERT_RETURN(tdata && tdata->msg && p_tsx, PJ_EINVAL); PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Method MUST NOT be ACK! */ PJ_ASSERT_RETURN(tdata->msg->line.req.method.id != PJSIP_ACK_METHOD, PJ_EINVALIDOP); /* Keep shortcut */ msg = tdata->msg; /* Make sure CSeq header is present. */ cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL); if (!cseq) { pj_assert(!"CSeq header not present in outgoing message!"); return PJSIP_EMISSINGHDR; } /* Create transaction instance. */ status = tsx_create( tsx_user, grp_lock, &tsx); if (status != PJ_SUCCESS) return status; /* Lock transaction. * We don't need to lock the group lock if none was supplied, while the * newly created group lock has not been exposed. */ if (grp_lock) pj_grp_lock_acquire(tsx->grp_lock); /* Role is UAC. */ tsx->role = PJSIP_ROLE_UAC; /* Save method. */ pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method); /* Save CSeq. */ tsx->cseq = cseq->cseq; /* Generate Via header if it doesn't exist. */ via = (pjsip_via_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL); if (via == NULL) { via = pjsip_via_hdr_create(tdata->pool); pjsip_msg_insert_first_hdr(msg, (pjsip_hdr*) via); } /* Generate branch parameter if it doesn't exist. */ if (via->branch_param.slen == 0) { pj_str_t tmp; via->branch_param.ptr = (char*) pj_pool_alloc(tsx->pool, PJSIP_MAX_BRANCH_LEN); via->branch_param.slen = PJSIP_MAX_BRANCH_LEN; pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN); tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN + 2; *(tmp.ptr-2) = 80; *(tmp.ptr-1) = 106; pj_generate_unique_string( &tmp ); /* Save branch parameter. */ tsx->branch = via->branch_param; } else { /* Copy branch parameter. */ pj_strdup(tsx->pool, &tsx->branch, &via->branch_param); } /* Generate transaction key. */ create_tsx_key_3261( tsx->pool, &tsx->transaction_key, PJSIP_ROLE_UAC, &tsx->method, &via->branch_param); /* Calculate hashed key value. */ #ifdef PRECALC_HASH tsx->hashed_key = pj_hash_calc_tolower(0, NULL, &tsx->transaction_key); #endif PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen, tsx->transaction_key.ptr)); /* Begin with State_Null. * Manually set-up the state becase we don't want to call the callback. */ tsx->state = PJSIP_TSX_STATE_NULL; tsx->state_handler = &tsx_on_state_null; /* Save the message. */ tsx->last_tx = tdata; pjsip_tx_data_add_ref(tsx->last_tx); /* Determine whether reliable transport should be used initially. * This will be updated whenever transport has changed. */ status = pjsip_get_request_dest(tdata, &dst_info); if (status != PJ_SUCCESS) { if (grp_lock) pj_grp_lock_release(tsx->grp_lock); tsx_shutdown(tsx); return status; } tsx->is_reliable = (dst_info.flag & PJSIP_TRANSPORT_RELIABLE); /* Register transaction to hash table. */ status = mod_tsx_layer_register_tsx(tsx); if (status != PJ_SUCCESS) { /* The assertion is removed by #1090: pj_assert(!"Bug in branch_param generator (i.e. not unique)"); */ if (grp_lock) pj_grp_lock_release(tsx->grp_lock); tsx_shutdown(tsx); return status; } /* Unlock transaction and return. */ if (grp_lock) pj_grp_lock_release(tsx->grp_lock); pj_log_push_indent(); PJ_LOG(5,(tsx->obj_name, "Transaction created for %s", pjsip_tx_data_get_info(tdata))); pj_log_pop_indent(); *p_tsx = tsx; return PJ_SUCCESS; } /* * Create, initialize, and register UAS transaction. */ PJ_DEF(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user, pjsip_rx_data *rdata, pjsip_transaction **p_tsx) { return pjsip_tsx_create_uas2(tsx_user, rdata, NULL, p_tsx); } PJ_DEF(pj_status_t) pjsip_tsx_create_uas2(pjsip_module *tsx_user, pjsip_rx_data *rdata, pj_grp_lock_t *grp_lock, pjsip_transaction **p_tsx) { pjsip_transaction *tsx; pjsip_msg *msg; pj_str_t *branch; pjsip_cseq_hdr *cseq; pj_status_t status; /* Validate arguments. */ PJ_ASSERT_RETURN(rdata && rdata->msg_info.msg && p_tsx, PJ_EINVAL); /* Keep shortcut to message */ msg = rdata->msg_info.msg; /* Make sure this is a request message. */ PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Make sure method is not ACK */ PJ_ASSERT_RETURN(msg->line.req.method.id != PJSIP_ACK_METHOD, PJ_EINVALIDOP); /* Make sure CSeq header is present. */ cseq = rdata->msg_info.cseq; if (!cseq) return PJSIP_EMISSINGHDR; /* Make sure Via header is present. */ if (rdata->msg_info.via == NULL) return PJSIP_EMISSINGHDR; /* Check that method in CSeq header match request method. * Reference: PROTOS #1922 */ if (pjsip_method_cmp(&msg->line.req.method, &rdata->msg_info.cseq->method) != 0) { PJ_LOG(4,(THIS_FILE, "Error: CSeq header contains different " "method than the request line")); return PJSIP_EINVALIDHDR; } /* * Create transaction instance. */ status = tsx_create( tsx_user, grp_lock, &tsx); if (status != PJ_SUCCESS) return status; /* Lock transaction. */ pj_grp_lock_acquire(tsx->grp_lock); /* Role is UAS */ tsx->role = PJSIP_ROLE_UAS; /* Save method. */ pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method); /* Save CSeq */ tsx->cseq = cseq->cseq; /* Get transaction key either from branch for RFC3261 message, or * create transaction key. */ status = pjsip_tsx_create_key(tsx->pool, &tsx->transaction_key, PJSIP_ROLE_UAS, &tsx->method, rdata); if (status != PJ_SUCCESS) { pj_grp_lock_release(tsx->grp_lock); tsx_shutdown(tsx); return status; } /* Calculate hashed key value. */ #ifdef PRECALC_HASH tsx->hashed_key = pj_hash_calc_tolower(0, NULL, &tsx->transaction_key); #endif /* Duplicate branch parameter for transaction. */ branch = &rdata->msg_info.via->branch_param; pj_strdup(tsx->pool, &tsx->branch, branch); PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen, tsx->transaction_key.ptr)); /* Begin with state NULL. * Manually set-up the state becase we don't want to call the callback. */ tsx->state = PJSIP_TSX_STATE_NULL; tsx->state_handler = &tsx_on_state_null; /* Get response address. */ status = pjsip_get_response_addr( tsx->pool, rdata, &tsx->res_addr ); if (status != PJ_SUCCESS) { pj_grp_lock_release(tsx->grp_lock); tsx_shutdown(tsx); return status; } /* If it's decided that we should use current transport, keep the * transport. */ if (tsx->res_addr.transport) { tsx_update_transport(tsx, tsx->res_addr.transport); pj_memcpy(&tsx->addr, &tsx->res_addr.addr, tsx->res_addr.addr_len); tsx->addr_len = tsx->res_addr.addr_len; tsx->is_reliable = PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport); } else { tsx->is_reliable = (tsx->res_addr.dst_host.flag & PJSIP_TRANSPORT_RELIABLE); } /* Register the transaction. */ status = mod_tsx_layer_register_tsx(tsx); if (status != PJ_SUCCESS) { pj_grp_lock_release(tsx->grp_lock); tsx_shutdown(tsx); return status; } /* Put this transaction in rdata's mod_data. */ rdata->endpt_info.mod_data[mod_tsx_layer.mod.id] = tsx; /* Unlock transaction and return. */ pj_grp_lock_release(tsx->grp_lock); pj_log_push_indent(); PJ_LOG(5,(tsx->obj_name, "Transaction created for %s", pjsip_rx_data_get_info(rdata))); pj_log_pop_indent(); *p_tsx = tsx; return PJ_SUCCESS; } /* * Bind transaction to a specific transport/listener. */ PJ_DEF(pj_status_t) pjsip_tsx_set_transport(pjsip_transaction *tsx, const pjsip_tpselector *sel) { /* Must be UAC transaction */ PJ_ASSERT_RETURN(tsx && sel, PJ_EINVAL); /* Start locking the transaction. */ pj_grp_lock_acquire(tsx->grp_lock); /* Decrement reference counter of previous transport selector */ pjsip_tpselector_dec_ref(&tsx->tp_sel); /* Copy transport selector structure .*/ pj_memcpy(&tsx->tp_sel, sel, sizeof(*sel)); /* Increment reference counter */ pjsip_tpselector_add_ref(&tsx->tp_sel); /* Unlock transaction. */ pj_grp_lock_release(tsx->grp_lock); return PJ_SUCCESS; } /* * Set transaction status code and reason. */ static void tsx_set_status_code(pjsip_transaction *tsx, int code, const pj_str_t *reason) { tsx->status_code = code; if (reason) pj_strdup(tsx->pool, &tsx->status_text, reason); else tsx->status_text = *pjsip_get_status_text(code); } /* * Forcely terminate transaction. */ PJ_DEF(pj_status_t) pjsip_tsx_terminate( pjsip_transaction *tsx, int code ) { PJ_ASSERT_RETURN(tsx != NULL, PJ_EINVAL); PJ_LOG(5,(tsx->obj_name, "Request to terminate transaction")); PJ_ASSERT_RETURN(code >= 200, PJ_EINVAL); pj_log_push_indent(); pj_grp_lock_acquire(tsx->grp_lock); if (tsx->state < PJSIP_TSX_STATE_TERMINATED) { tsx_set_status_code(tsx, code, NULL); tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_USER, NULL, 0); } pj_grp_lock_release(tsx->grp_lock); pj_log_pop_indent(); return PJ_SUCCESS; } /* * Cease retransmission on the UAC transaction. The UAC transaction is * still considered running, and it will complete when either final * response is received or the transaction times out. */ PJ_DEF(pj_status_t) pjsip_tsx_stop_retransmit(pjsip_transaction *tsx) { PJ_ASSERT_RETURN(tsx != NULL, PJ_EINVAL); PJ_ASSERT_RETURN(tsx->role == PJSIP_ROLE_UAC && tsx->method.id == PJSIP_INVITE_METHOD, PJ_EINVALIDOP); PJ_LOG(5,(tsx->obj_name, "Request to stop retransmission")); pj_log_push_indent(); pj_grp_lock_acquire(tsx->grp_lock); /* Cancel retransmission timer. */ tsx_cancel_timer(tsx, &tsx->retransmit_timer); pj_grp_lock_release(tsx->grp_lock); pj_log_pop_indent(); return PJ_SUCCESS; } /* * Start a timer to terminate transaction after the specified time * has elapsed. */ PJ_DEF(pj_status_t) pjsip_tsx_set_timeout( pjsip_transaction *tsx, unsigned millisec) { pj_time_val timeout; PJ_ASSERT_RETURN(tsx != NULL, PJ_EINVAL); PJ_ASSERT_RETURN(tsx->role == PJSIP_ROLE_UAC && tsx->method.id == PJSIP_INVITE_METHOD, PJ_EINVALIDOP); /* Note: must not call pj_grp_lock_acquire(tsx->grp_lock) as * that would introduce deadlock. See #1121. */ lock_timer(tsx); /* Transaction should normally not have final response, but as * #1121 says there is a (tolerable) window of race condition * where this might happen. */ if (tsx->status_code >= 200 && tsx->timeout_timer.id != 0) { /* Timeout is already set */ unlock_timer(tsx); return PJ_EEXISTS; } tsx_cancel_timer(tsx, &tsx->timeout_timer); timeout.sec = 0; timeout.msec = millisec; pj_time_val_normalize(&timeout); tsx_schedule_timer(tsx, &tsx->timeout_timer, &timeout, TIMEOUT_TIMER); unlock_timer(tsx); return PJ_SUCCESS; } /* * This function is called by TU to send a message. */ PJ_DEF(pj_status_t) pjsip_tsx_send_msg( pjsip_transaction *tsx, pjsip_tx_data *tdata ) { pjsip_event event; pj_status_t status; if (tdata == NULL) tdata = tsx->last_tx; PJ_ASSERT_RETURN(tdata != NULL, PJ_EINVALIDOP); PJ_LOG(5,(tsx->obj_name, "Sending %s in state %s", pjsip_tx_data_get_info(tdata), state_str[tsx->state])); pj_log_push_indent(); PJSIP_EVENT_INIT_TX_MSG(event, tdata); /* Dispatch to transaction. */ pj_grp_lock_acquire(tsx->grp_lock); /* Set transport selector to tdata */ pjsip_tx_data_set_transport(tdata, &tsx->tp_sel); /* Dispatch to state handler */ status = (*tsx->state_handler)(tsx, &event); pj_grp_lock_release(tsx->grp_lock); /* Only decrement reference counter when it returns success. * (This is the specification from the .PDF design document). */ if (status == PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); } pj_log_pop_indent(); return status; } /* * This function is called by endpoint when incoming message for the * transaction is received. */ PJ_DEF(void) pjsip_tsx_recv_msg( pjsip_transaction *tsx, pjsip_rx_data *rdata) { pjsip_event event; PJ_LOG(5,(tsx->obj_name, "Incoming %s in state %s", pjsip_rx_data_get_info(rdata), state_str[tsx->state])); pj_log_push_indent(); /* Put the transaction in the rdata's mod_data. */ rdata->endpt_info.mod_data[mod_tsx_layer.mod.id] = tsx; /* Init event. */ PJSIP_EVENT_INIT_RX_MSG(event, rdata); /* Dispatch to transaction. */ pj_grp_lock_acquire(tsx->grp_lock); (*tsx->state_handler)(tsx, &event); pj_grp_lock_release(tsx->grp_lock); pj_log_pop_indent(); } /* Callback called by send message framework */ static void send_msg_callback( pjsip_send_state *send_state, pj_ssize_t sent, pj_bool_t *cont ) { pjsip_transaction *tsx = (pjsip_transaction*) send_state->token; pjsip_tx_data *tdata = send_state->tdata; /* Check if transaction has cancelled itself from this transmit * notification (https://trac.pjsip.org/repos/ticket/1033). * Also check if the transaction layer itself may have been shutdown * (https://trac.pjsip.org/repos/ticket/1535) */ if (mod_tsx_layer.mod.id < 0 || tdata->mod_data[mod_tsx_layer.mod.id] == NULL) { *cont = PJ_FALSE; /* Decrease pending send counter */ pj_grp_lock_dec_ref(tsx->grp_lock); return; } pj_grp_lock_acquire(tsx->grp_lock); /* Decrease pending send counter */ pj_grp_lock_dec_ref(tsx->grp_lock); /* Reset */ tdata->mod_data[mod_tsx_layer.mod.id] = NULL; tsx->pending_tx = NULL; if (sent > 0) { /* Successfully sent! */ pj_assert(send_state->cur_transport != NULL); if (tsx->transport != send_state->cur_transport) { /* Update transport. */ tsx_update_transport(tsx, send_state->cur_transport); /* Update remote address. */ tsx->addr_len = tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr_len; pj_memcpy(&tsx->addr, &tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr, tsx->addr_len); /* Update is_reliable flag. */ tsx->is_reliable = PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport); } /* Clear pending transport flag. */ tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT); /* Mark that we have resolved the addresses. */ tsx->transport_flag |= TSX_HAS_RESOLVED_SERVER; /* Pending destroy? */ if (tsx->transport_flag & TSX_HAS_PENDING_DESTROY) { tsx_set_state( tsx, PJSIP_TSX_STATE_DESTROYED, PJSIP_EVENT_UNKNOWN, NULL, 0 ); pj_grp_lock_release(tsx->grp_lock); return; } /* Need to transmit a message? */ if (tsx->transport_flag & TSX_HAS_PENDING_SEND) { tsx->transport_flag &= ~(TSX_HAS_PENDING_SEND); tsx_send_msg(tsx, tsx->last_tx); } /* Need to reschedule retransmission? * Note that when sending a pending message above, tsx_send_msg() * may set the flag TSX_HAS_PENDING_TRANSPORT. * Please refer to ticket #1875. */ if (tsx->transport_flag & TSX_HAS_PENDING_RESCHED && !(tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT)) { tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED); /* Only update when transport turns out to be unreliable. */ if (!tsx->is_reliable) { tsx_resched_retransmission(tsx); } } } else { /* Failed to send! */ pj_assert(sent != 0); /* If transaction is using the same transport as the failed one, * release the transport. */ if (send_state->cur_transport==tsx->transport) tsx_update_transport(tsx, NULL); /* Also stop processing if transaction has been flagged with * pending destroy (http://trac.pjsip.org/repos/ticket/906) */ if ((!*cont) || (tsx->transport_flag & TSX_HAS_PENDING_DESTROY)) { char errmsg[PJ_ERR_MSG_SIZE]; pjsip_status_code sc; pj_str_t err; tsx->transport_err = (pj_status_t)-sent; err =pj_strerror((pj_status_t)-sent, errmsg, sizeof(errmsg)); PJ_LOG(3,(tsx->obj_name, "Failed to send %s! err=%d (%s)", pjsip_tx_data_get_info(send_state->tdata), -sent, errmsg)); /* Clear pending transport flag. */ tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT); /* Mark that we have resolved the addresses. */ tsx->transport_flag |= TSX_HAS_RESOLVED_SERVER; /* Server resolution error is now mapped to 502 instead of 503, * since with 503 normally client should try again. * See http://trac.pjsip.org/repos/ticket/870 */ if (-sent==PJ_ERESOLVE || -sent==PJLIB_UTIL_EDNS_NXDOMAIN) sc = PJSIP_SC_BAD_GATEWAY; else sc = PJSIP_SC_TSX_TRANSPORT_ERROR; /* Terminate transaction, if it's not already terminated. */ tsx_set_status_code(tsx, sc, &err); if (tsx->state != PJSIP_TSX_STATE_TERMINATED && tsx->state != PJSIP_TSX_STATE_DESTROYED) { tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_TRANSPORT_ERROR, send_state->tdata, 0); } /* Don't forget to destroy if we have pending destroy flag * (http://trac.pjsip.org/repos/ticket/906) */ else if (tsx->transport_flag & TSX_HAS_PENDING_DESTROY) { tsx_set_state( tsx, PJSIP_TSX_STATE_DESTROYED, PJSIP_EVENT_TRANSPORT_ERROR, send_state->tdata, 0); } } else { PJ_PERROR(3,(tsx->obj_name, (pj_status_t)-sent, "Temporary failure in sending %s, " "will try next server", pjsip_tx_data_get_info(send_state->tdata))); /* Reset retransmission count */ tsx->retransmit_count = 0; /* And reset timeout timer */ if (tsx->timeout_timer.id) { lock_timer(tsx); tsx_cancel_timer(tsx, &tsx->timeout_timer); tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout_timer_val, TIMEOUT_TIMER); unlock_timer(tsx); } /* Put again pending tdata */ tdata->mod_data[mod_tsx_layer.mod.id] = tsx; tsx->pending_tx = tdata; /* Increment group lock again for the next sending retry, * to prevent us from being destroyed prematurely (ticket #1859). */ pj_grp_lock_add_ref(tsx->grp_lock); } } pj_grp_lock_release(tsx->grp_lock); } /* Transport callback. */ static void transport_callback(void *token, pjsip_tx_data *tdata, pj_ssize_t sent) { pjsip_transaction *tsx = (pjsip_transaction*) token; /* In other circumstances, locking tsx->grp_lock AFTER transport mutex * will introduce deadlock if another thread is currently sending a * SIP message to the transport. But this should be safe as there should * be no way this callback could be called while another thread is * sending a message. */ pj_grp_lock_acquire(tsx->grp_lock); tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT); pj_grp_lock_release(tsx->grp_lock); if (sent < 0) { pj_time_val delay = {0, 0}; char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror((pj_status_t)-sent, errmsg, sizeof(errmsg)); PJ_LOG(2,(tsx->obj_name, "Transport failed to send %s! Err=%d (%s)", pjsip_tx_data_get_info(tdata), -sent, errmsg)); /* Post the event for later processing, to avoid deadlock. * See https://trac.pjsip.org/repos/ticket/1646 */ lock_timer(tsx); tsx->transport_err = (pj_status_t)-sent; tsx_cancel_timer(tsx, &tsx->timeout_timer); tsx_schedule_timer(tsx, &tsx->timeout_timer, &delay, TRANSPORT_ERR_TIMER); unlock_timer(tsx); } /* Decrease pending send counter */ pj_grp_lock_dec_ref(tsx->grp_lock); } /* * Callback when transport state changes. */ static void tsx_tp_state_callback( pjsip_transport *tp, pjsip_transport_state state, const pjsip_transport_state_info *info) { PJ_UNUSED_ARG(tp); if (state == PJSIP_TP_STATE_DISCONNECTED) { pjsip_transaction *tsx; pj_time_val delay = {0, 0}; pj_assert(tp && info && info->user_data); tsx = (pjsip_transaction*)info->user_data; /* Post the event for later processing, to avoid deadlock. * See https://trac.pjsip.org/repos/ticket/1646 */ lock_timer(tsx); tsx->transport_err = info->status; tsx_cancel_timer(tsx, &tsx->timeout_timer); tsx_schedule_timer(tsx, &tsx->timeout_timer, &delay, TRANSPORT_ERR_TIMER); unlock_timer(tsx); } } /* * Send message to the transport. */ static pj_status_t tsx_send_msg( pjsip_transaction *tsx, pjsip_tx_data *tdata) { pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(tsx && tdata, PJ_EINVAL); /* Send later if transport is still pending. */ if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) { tsx->transport_flag |= TSX_HAS_PENDING_SEND; return PJ_SUCCESS; } /* Skip send if previous tdata transmission is pending (see #1665). */ if (tdata->is_pending) { PJ_LOG(2,(THIS_FILE, "Unable to send %s: message is pending", pjsip_tx_data_get_info(tdata))); return PJ_SUCCESS; } /* If we have the transport, send the message using that transport. * Otherwise perform full transport resolution. */ if (tsx->transport) { /* Increment group lock while waiting for send operation to complete, * to prevent us from being destroyed prematurely. See * https://trac.pjsip.org/repos/ticket/1646 */ pj_grp_lock_add_ref(tsx->grp_lock); tsx->transport_flag |= TSX_HAS_PENDING_TRANSPORT; status = pjsip_transport_send( tsx->transport, tdata, &tsx->addr, tsx->addr_len, tsx, &transport_callback); if (status == PJ_EPENDING) status = PJ_SUCCESS; else { /* Operation completes immediately */ tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT); pj_grp_lock_dec_ref(tsx->grp_lock); } if (status != PJ_SUCCESS) { PJ_PERROR(2,(tsx->obj_name, status, "Error sending %s", pjsip_tx_data_get_info(tdata))); /* On error, release transport to force using full transport * resolution procedure. */ tsx_update_transport(tsx, NULL); tsx->addr_len = 0; tsx->res_addr.transport = NULL; tsx->res_addr.addr_len = 0; } else { return PJ_SUCCESS; } } /* We are here because we don't have transport, or we failed to send * the message using existing transport. If we haven't resolved the * server before, then begin the long process of resolving the server * and send the message with possibly new server. */ pj_assert(status != PJ_SUCCESS || tsx->transport == NULL); /* If we have resolved the server, we treat the error as permanent error. * Terminate transaction with transport error failure. */ if (tsx->transport_flag & TSX_HAS_RESOLVED_SERVER) { char errmsg[PJ_ERR_MSG_SIZE]; pj_str_t err; if (status == PJ_SUCCESS) { pj_assert(!"Unexpected status!"); status = PJ_EUNKNOWN; } /* We have resolved the server!. * Treat this as permanent transport error. */ err = pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(2,(tsx->obj_name, "Transport error, terminating transaction. " "Err=%d (%s)", status, errmsg)); tsx_set_status_code(tsx, PJSIP_SC_TSX_TRANSPORT_ERROR, &err); tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_TRANSPORT_ERROR, NULL, 0 ); return status; } /* Must add reference counter because the send request functions * decrement the reference counter. */ pjsip_tx_data_add_ref(tdata); /* Also attach ourselves to the transmit data so that we'll be able * to unregister ourselves from the send notification of this * transmit data. */ tdata->mod_data[mod_tsx_layer.mod.id] = tsx; tsx->pending_tx = tdata; /* Increment group lock while waiting for send operation to complete, * to prevent us from being destroyed prematurely (ticket #1859). */ pj_grp_lock_add_ref(tsx->grp_lock); /* Begin resolving destination etc to send the message. */ if (tdata->msg->type == PJSIP_REQUEST_MSG) { tsx->transport_flag |= TSX_HAS_PENDING_TRANSPORT; status = pjsip_endpt_send_request_stateless(tsx->endpt, tdata, tsx, &send_msg_callback); if (status == PJ_EPENDING) status = PJ_SUCCESS; if (status != PJ_SUCCESS) { pj_grp_lock_dec_ref(tsx->grp_lock); pjsip_tx_data_dec_ref(tdata); tdata->mod_data[mod_tsx_layer.mod.id] = NULL; tsx->pending_tx = NULL; } /* Check if transaction is terminated. */ if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TERMINATED) status = tsx->transport_err; } else { tsx->transport_flag |= TSX_HAS_PENDING_TRANSPORT; status = pjsip_endpt_send_response( tsx->endpt, &tsx->res_addr, tdata, tsx, &send_msg_callback); if (status == PJ_EPENDING) status = PJ_SUCCESS; if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); tdata->mod_data[mod_tsx_layer.mod.id] = NULL; tsx->pending_tx = NULL; } /* Check if transaction is terminated. */ if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TERMINATED) status = tsx->transport_err; } return status; } /* * Manually retransmit the last messagewithout updating the transaction state. */ PJ_DEF(pj_status_t) pjsip_tsx_retransmit_no_state(pjsip_transaction *tsx, pjsip_tx_data *tdata) { pj_status_t status; pj_grp_lock_acquire(tsx->grp_lock); if (tdata == NULL) { tdata = tsx->last_tx; pjsip_tx_data_add_ref(tdata); } status = tsx_send_msg(tsx, tdata); pj_grp_lock_release(tsx->grp_lock); /* Only decrement reference counter when it returns success. * (This is the specification from the .PDF design document). */ if (status == PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); } return status; } /* * Retransmit last message sent. */ static void tsx_resched_retransmission( pjsip_transaction *tsx ) { pj_uint32_t msec_time; pj_assert((tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) == 0); if (tsx->role==PJSIP_ROLE_UAC && tsx->status_code >= 100) msec_time = pjsip_cfg()->tsx.t2; else msec_time = (1 << (tsx->retransmit_count)) * pjsip_cfg()->tsx.t1; if (tsx->role == PJSIP_ROLE_UAC) { pj_assert(tsx->status_code < 200); /* Retransmission for non-INVITE transaction caps-off at T2 */ if (msec_time > pjsip_cfg()->tsx.t2 && tsx->method.id != PJSIP_INVITE_METHOD) { msec_time = pjsip_cfg()->tsx.t2; } } else { /* For UAS, this can be retransmission of 2xx response for INVITE * or non-100 1xx response. */ if (tsx->status_code < 200) { /* non-100 1xx retransmission is at 60 seconds */ msec_time = PJSIP_TSX_1XX_RETRANS_DELAY * 1000; } else { /* Retransmission of INVITE final response also caps-off at T2 */ pj_assert(tsx->status_code >= 200); if (msec_time > pjsip_cfg()->tsx.t2) msec_time = pjsip_cfg()->tsx.t2; } } if (msec_time != 0) { pj_time_val timeout; timeout.sec = msec_time / 1000; timeout.msec = msec_time % 1000; tsx_schedule_timer( tsx, &tsx->retransmit_timer, &timeout, RETRANSMIT_TIMER); } } /* * Retransmit last message sent. */ static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched) { pj_status_t status; if (resched && pj_timer_entry_running(&tsx->retransmit_timer)) { /* We've been asked to reschedule but the timer is already rerunning. * This can only happen in a race condition where, between removing * this retransmit timer from the heap and actually scheduling it, * another thread has got in and rescheduled the timer itself. In * this scenario, the transmission has already happened and so we * should just quit out immediately, without either resending the * message or restarting the timer. */ return PJ_SUCCESS; } PJ_ASSERT_RETURN(tsx->last_tx!=NULL, PJ_EBUG); PJ_LOG(5,(tsx->obj_name, "Retransmiting %s, count=%d, restart?=%d", pjsip_tx_data_get_info(tsx->last_tx), tsx->retransmit_count, resched)); ++tsx->retransmit_count; /* Restart timer T1 first before sending the message to ensure that * retransmission timer is not engaged when loop transport is used. */ if (resched) { pj_assert(tsx->state != PJSIP_TSX_STATE_CONFIRMED); if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) { tsx->transport_flag |= TSX_HAS_PENDING_RESCHED; } else { tsx_resched_retransmission(tsx); } } status = tsx_send_msg( tsx, tsx->last_tx); if (status != PJ_SUCCESS) { return status; } return PJ_SUCCESS; } static void tsx_update_transport( pjsip_transaction *tsx, pjsip_transport *tp) { pj_assert(tsx); if (tsx->transport) { pjsip_transport_remove_state_listener(tsx->transport, tsx->tp_st_key, tsx); pjsip_transport_dec_ref( tsx->transport ); tsx->transport = NULL; } if (tp) { tsx->transport = tp; pjsip_transport_add_ref(tp); pjsip_transport_add_state_listener(tp, &tsx_tp_state_callback, tsx, &tsx->tp_st_key); if (tp->is_shutdown) { pjsip_transport_state_info info; pj_bzero(&info, sizeof(info)); info.user_data = tsx; info.status = PJSIP_SC_TSX_TRANSPORT_ERROR; tsx_tp_state_callback(tp, PJSIP_TP_STATE_DISCONNECTED, &info); } } } /* * Handler for events in state Null. */ static pj_status_t tsx_on_state_null( pjsip_transaction *tsx, pjsip_event *event ) { pj_status_t status; pj_assert(tsx->state == PJSIP_TSX_STATE_NULL); if (tsx->role == PJSIP_ROLE_UAS) { /* Set state to Trying. */ pj_assert(event->type == PJSIP_EVENT_RX_MSG && event->body.rx_msg.rdata->msg_info.msg->type == PJSIP_REQUEST_MSG); tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING, PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0); } else { pjsip_tx_data *tdata; /* Must be transmit event. * You may got this assertion when using loop transport with delay * set to zero. That would cause on_rx_response() callback to be * called before tsx_send_msg() has completed. */ PJ_ASSERT_RETURN(event->type == PJSIP_EVENT_TX_MSG, PJ_EBUG); /* Get the txdata */ tdata = event->body.tx_msg.tdata; /* Save the message for retransmission. */ if (tsx->last_tx && tsx->last_tx != tdata) { pjsip_tx_data_dec_ref(tsx->last_tx); tsx->last_tx = NULL; } if (tsx->last_tx != tdata) { tsx->last_tx = tdata; pjsip_tx_data_add_ref(tdata); } /* Send the message. */ status = tsx_send_msg( tsx, tdata); if (status != PJ_SUCCESS) { return status; } /* Start Timer B (or called timer F for non-INVITE) for transaction * timeout. */ lock_timer(tsx); tsx_cancel_timer( tsx, &tsx->timeout_timer ); tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout_timer_val, TIMEOUT_TIMER); unlock_timer(tsx); /* Start Timer A (or timer E) for retransmission only if unreliable * transport is being used. */ if (!tsx->is_reliable) { tsx->retransmit_count = 0; if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) { tsx->transport_flag |= TSX_HAS_PENDING_RESCHED; } else { tsx_schedule_timer(tsx, &tsx->retransmit_timer, &t1_timer_val, RETRANSMIT_TIMER); } } /* Move state. */ tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING, PJSIP_EVENT_TX_MSG, tdata, 0); } return PJ_SUCCESS; } /* * State Calling is for UAC after it sends request but before any responses * is received. */ static pj_status_t tsx_on_state_calling( pjsip_transaction *tsx, pjsip_event *event ) { pj_assert(tsx->state == PJSIP_TSX_STATE_CALLING); pj_assert(tsx->role == PJSIP_ROLE_UAC); if (event->type == PJSIP_EVENT_TIMER && event->body.timer.entry == &tsx->retransmit_timer) { pj_status_t status; /* Retransmit the request. */ status = tsx_retransmit( tsx, 1 ); if (status != PJ_SUCCESS) { return status; } } else if (event->type == PJSIP_EVENT_TIMER && event->body.timer.entry == &tsx->timeout_timer) { /* Cancel retransmission timer. */ tsx_cancel_timer(tsx, &tsx->retransmit_timer); tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED); /* Set status code */ tsx_set_status_code(tsx, PJSIP_SC_TSX_TIMEOUT, NULL); /* Inform TU. */ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0); /* Transaction is destroyed */ //return PJSIP_ETSXDESTROYED; } else if (event->type == PJSIP_EVENT_RX_MSG) { pjsip_msg *msg; int code; /* Get message instance */ msg = event->body.rx_msg.rdata->msg_info.msg; /* Better be a response message. */ if (msg->type != PJSIP_RESPONSE_MSG) return PJSIP_ENOTRESPONSEMSG; code = msg->line.status.code; /* If the response is final, cancel both retransmission and timeout * timer. */ if (code >= 200) { tsx_cancel_timer(tsx, &tsx->retransmit_timer); if (tsx->timeout_timer.id != 0) { lock_timer(tsx); tsx_cancel_timer(tsx, &tsx->timeout_timer); unlock_timer(tsx); } } else { /* Cancel retransmit timer (for non-INVITE transaction, the * retransmit timer will be rescheduled at T2. */ tsx_cancel_timer(tsx, &tsx->retransmit_timer); /* For provisional response, only cancel retransmit when this * is an INVITE transaction. For non-INVITE, section 17.1.2.1 * of RFC 3261 says that: * - retransmit timer is set to T2 * - timeout timer F is not deleted. */ if (tsx->method.id == PJSIP_INVITE_METHOD) { /* Cancel timeout timer */ lock_timer(tsx); tsx_cancel_timer(tsx, &tsx->timeout_timer); unlock_timer(tsx); } else { if (!tsx->is_reliable) { tsx_schedule_timer(tsx, &tsx->retransmit_timer, &t2_timer_val, RETRANSMIT_TIMER); } } } tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED); /* Discard retransmission message if it is not INVITE. * The INVITE tdata is needed in case we have to generate ACK for * the final response. */ /* Keep last_tx for authorization. */ //blp: always keep last_tx until transaction is destroyed //code = msg->line.status.code; //if (tsx->method.id != PJSIP_INVITE_METHOD && code!=401 && code!=407) { // pjsip_tx_data_dec_ref(tsx->last_tx); // tsx->last_tx = NULL; //} /* Processing is similar to state Proceeding. */ tsx_on_state_proceeding_uac( tsx, event); } else { pj_assert(!"Unexpected event"); return PJ_EBUG; } return PJ_SUCCESS; } /* * State Trying is for UAS after it received request but before any responses * is sent. * Note: this is different than RFC3261, which can use Trying state for * non-INVITE client transaction (bug in RFC?). */ static pj_status_t tsx_on_state_trying( pjsip_transaction *tsx, pjsip_event *event) { pj_status_t status; pj_assert(tsx->state == PJSIP_TSX_STATE_TRYING); /* This state is only for UAS */ pj_assert(tsx->role == PJSIP_ROLE_UAS); /* Better be transmission of response message. * If we've got request retransmission, this means that the TU hasn't * transmitted any responses within 500 ms, which is not allowed. If * this happens, just ignore the event (we couldn't retransmit last * response because we haven't sent any!). */ if (event->type != PJSIP_EVENT_TX_MSG) { return PJ_SUCCESS; } /* The rest of the processing of the event is exactly the same as in * "Proceeding" state. */ status = tsx_on_state_proceeding_uas( tsx, event); /* Inform the TU of the state transision if state is still State_Trying */ if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TRYING) { tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata, 0); } return status; } /* * Handler for events in Proceeding for UAS * This state happens after the TU sends provisional response. */ static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx, pjsip_event *event) { pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || tsx->state == PJSIP_TSX_STATE_TRYING); /* This state is only for UAS. */ pj_assert(tsx->role == PJSIP_ROLE_UAS); /* Receive request retransmission. */ if (event->type == PJSIP_EVENT_RX_MSG) { pj_status_t status; /* Must have last response sent. */ PJ_ASSERT_RETURN(tsx->last_tx != NULL, PJ_EBUG); /* Send last response */ if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) { tsx->transport_flag |= TSX_HAS_PENDING_SEND; } else { status = tsx_send_msg(tsx, tsx->last_tx); if (status != PJ_SUCCESS) return status; } } else if (event->type == PJSIP_EVENT_TX_MSG ) { pjsip_tx_data *tdata = event->body.tx_msg.tdata; pj_status_t status; /* The TU sends response message to the request. Save this message so * that we can retransmit the last response in case we receive request * retransmission. */ pjsip_msg *msg = tdata->msg; /* This can only be a response message. */ PJ_ASSERT_RETURN(msg->type==PJSIP_RESPONSE_MSG, PJSIP_ENOTRESPONSEMSG); /* Update last status */ tsx_set_status_code(tsx, msg->line.status.code, &msg->line.status.reason); /* Discard the saved last response (it will be updated later as * necessary). */ if (tsx->last_tx && tsx->last_tx != tdata) { pjsip_tx_data_dec_ref( tsx->last_tx ); tsx->last_tx = NULL; } /* Send the message. */ status = tsx_send_msg(tsx, tdata); if (status != PJ_SUCCESS) { return status; } // Update To tag header for RFC2543 transaction. // TODO: /* Update transaction state */ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) { if (tsx->last_tx != tdata) { tsx->last_tx = tdata; pjsip_tx_data_add_ref( tdata ); } tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, PJSIP_EVENT_TX_MSG, tdata, 0 ); /* Retransmit provisional response every 1 minute if this is * an INVITE provisional response greater than 100. */ if (PJSIP_TSX_1XX_RETRANS_DELAY > 0 && tsx->method.id==PJSIP_INVITE_METHOD && tsx->status_code>100) { /* Stop 1xx retransmission timer, if any */ tsx_cancel_timer(tsx, &tsx->retransmit_timer); /* Schedule retransmission */ tsx->retransmit_count = 0; if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) { tsx->transport_flag |= TSX_HAS_PENDING_RESCHED; } else { pj_time_val delay = {PJSIP_TSX_1XX_RETRANS_DELAY, 0}; tsx_schedule_timer( tsx, &tsx->retransmit_timer, &delay, RETRANSMIT_TIMER); } } } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { /* Stop 1xx retransmission timer, if any */ tsx_cancel_timer(tsx, &tsx->retransmit_timer); if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_200resp==0) { /* 2xx class message is not saved, because retransmission * is handled by TU. */ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_TX_MSG, tdata, 0 ); /* Transaction is destroyed. */ //return PJSIP_ETSXDESTROYED; } else { pj_time_val timeout; if (tsx->method.id == PJSIP_INVITE_METHOD) { tsx->retransmit_count = 0; if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) { tsx->transport_flag |= TSX_HAS_PENDING_RESCHED; } else { tsx_schedule_timer( tsx, &tsx->retransmit_timer, &t1_timer_val, RETRANSMIT_TIMER); } } /* Save last response sent for retransmission when request * retransmission is received. */ if (tsx->last_tx != tdata) { tsx->last_tx = tdata; pjsip_tx_data_add_ref(tdata); } /* Setup timeout timer: */ if (tsx->method.id == PJSIP_INVITE_METHOD) { /* Start Timer H at 64*T1 for INVITE server transaction, * regardless of transport. */ timeout = timeout_timer_val; } else if (!tsx->is_reliable) { /* For non-INVITE, start timer J at 64*T1 for unreliable * transport. */ timeout = timeout_timer_val; } else { /* Transaction terminates immediately for non-INVITE when * reliable transport is used. */ timeout.sec = timeout.msec = 0; } lock_timer(tsx); tsx_cancel_timer(tsx, &tsx->timeout_timer); tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout, TIMEOUT_TIMER); unlock_timer(tsx); /* Set state to "Completed" */ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, PJSIP_EVENT_TX_MSG, tdata, 0 ); } } else if (tsx->status_code >= 300) { /* Stop 1xx retransmission timer, if any */ tsx_cancel_timer(tsx, &tsx->retransmit_timer); /* 3xx-6xx class message causes transaction to move to * "Completed" state. */ if (tsx->last_tx != tdata) { tsx->last_tx = tdata; pjsip_tx_data_add_ref( tdata ); } /* For INVITE, start timer H for transaction termination * regardless whether transport is reliable or not. * For non-INVITE, start timer J with the value of 64*T1 for * non-reliable transports, and zero for reliable transports. */ lock_timer(tsx); tsx_cancel_timer(tsx, &tsx->timeout_timer); if (tsx->method.id == PJSIP_INVITE_METHOD) { /* Start timer H for INVITE */ tsx_schedule_timer(tsx, &tsx->timeout_timer, &timeout_timer_val, TIMEOUT_TIMER); } else if (!tsx->is_reliable) { /* Start timer J on 64*T1 seconds for non-INVITE */ tsx_schedule_timer(tsx, &tsx->timeout_timer, &timeout_timer_val, TIMEOUT_TIMER); } else { /* Start timer J on zero seconds for non-INVITE */ pj_time_val zero_time = { 0, 0 }; tsx_schedule_timer(tsx, &tsx->timeout_timer, &zero_time, TIMEOUT_TIMER); } unlock_timer(tsx); /* For INVITE, if unreliable transport is used, retransmission * timer G will be scheduled (retransmission). */ if (!tsx->is_reliable) { pjsip_cseq_hdr *cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL); if (cseq->method.id == PJSIP_INVITE_METHOD) { tsx->retransmit_count = 0; if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) { tsx->transport_flag |= TSX_HAS_PENDING_RESCHED; } else { tsx_schedule_timer(tsx, &tsx->retransmit_timer, &t1_timer_val, RETRANSMIT_TIMER); } } } /* Inform TU */ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, PJSIP_EVENT_TX_MSG, tdata, 0 ); } else { pj_assert(0); } } else if (event->type == PJSIP_EVENT_TIMER && event->body.timer.entry == &tsx->retransmit_timer) { /* Retransmission timer elapsed. */ pj_status_t status; /* Must not be triggered while transport is pending. */ pj_assert((tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) == 0); /* Must have last response to retransmit. */ pj_assert(tsx->last_tx != NULL); /* Retransmit the last response. */ status = tsx_retransmit( tsx, 1 ); if (status != PJ_SUCCESS) { return status; } } else if (event->type == PJSIP_EVENT_TIMER && event->body.timer.entry == &tsx->timeout_timer) { /* Timeout timer. should not happen? */ pj_assert(!"Should not happen(?)"); tsx_set_status_code(tsx, PJSIP_SC_TSX_TIMEOUT, NULL); tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0); return PJ_EBUG; } else { pj_assert(!"Unexpected event"); return PJ_EBUG; } return PJ_SUCCESS; } /* * Handler for events in Proceeding for UAC * This state happens after provisional response(s) has been received from * UAS. */ static pj_status_t tsx_on_state_proceeding_uac(pjsip_transaction *tsx, pjsip_event *event) { pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || tsx->state == PJSIP_TSX_STATE_CALLING); if (event->type != PJSIP_EVENT_TIMER) { pjsip_msg *msg; /* Must be incoming response, because we should not retransmit * request once response has been received. */ pj_assert(event->type == PJSIP_EVENT_RX_MSG); if (event->type != PJSIP_EVENT_RX_MSG) { return PJ_EINVALIDOP; } msg = event->body.rx_msg.rdata->msg_info.msg; /* Must be a response message. */ if (msg->type != PJSIP_RESPONSE_MSG) { pj_assert(!"Expecting response message!"); return PJSIP_ENOTRESPONSEMSG; } tsx_set_status_code(tsx, msg->line.status.code, &msg->line.status.reason); } else { if (event->body.timer.entry == &tsx->retransmit_timer) { /* Retransmit message. */ pj_status_t status; status = tsx_retransmit( tsx, 1 ); return status; } else { tsx_set_status_code(tsx, PJSIP_SC_TSX_TIMEOUT, NULL); } } if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) { /* Inform the message to TU. */ tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0 ); } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) { /* Stop timeout timer B/F. */ lock_timer(tsx); tsx_cancel_timer( tsx, &tsx->timeout_timer ); unlock_timer(tsx); /* For INVITE, the state moves to Terminated state (because ACK is * handled in TU). For non-INVITE, state moves to Completed. */ if (tsx->method.id == PJSIP_INVITE_METHOD) { tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0 ); //return PJSIP_ETSXDESTROYED; } else { pj_time_val timeout; /* For unreliable transport, start timer D (for INVITE) or * timer K for non-INVITE. */ if (!tsx->is_reliable) { if (tsx->method.id == PJSIP_INVITE_METHOD) { timeout = td_timer_val; } else { timeout = t4_timer_val; } } else { timeout.sec = timeout.msec = 0; } lock_timer(tsx); tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout, TIMEOUT_TIMER); unlock_timer(tsx); /* Cancel retransmission timer */ tsx_cancel_timer(tsx, &tsx->retransmit_timer); /* Move state to Completed, inform TU. */ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0 ); } } else if (event->type == PJSIP_EVENT_TIMER && event->body.timer.entry == &tsx->timeout_timer) { /* Inform TU. */ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0); } else if (tsx->status_code >= 300 && tsx->status_code <= 699) { #if 0 /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ /* * This is the old code; it's broken for authentication. */ pj_time_val timeout; pj_status_t status; /* Stop timer B. */ tsx_cancel_timer( tsx, &tsx->timeout_timer ); /* Generate and send ACK for INVITE. */ if (tsx->method.id == PJSIP_INVITE_METHOD) { pjsip_tx_data *ack; status = pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx, event->body.rx_msg.rdata, &ack); if (status != PJ_SUCCESS) return status; if (ack != tsx->last_tx) { pjsip_tx_data_dec_ref(tsx->last_tx); tsx->last_tx = ack; } status = tsx_send_msg( tsx, tsx->last_tx); if (status != PJ_SUCCESS) { return status; } } /* Start Timer D with TD/T4 timer if unreliable transport is used. */ if (!tsx->is_reliable) { if (tsx->method.id == PJSIP_INVITE_METHOD) { timeout = td_timer_val; } else { timeout = t4_timer_val; } } else { timeout.sec = timeout.msec = 0; } tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout, TIMEOUT_TIMER); /* Inform TU. * blp: You might be tempted to move this notification before * sending ACK, but I think you shouldn't. Better set-up * everything before calling tsx_user's callback to avoid * mess up. */ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ #endif /* New code, taken from 0.2.9.x branch */ pj_time_val timeout; pjsip_tx_data *ack_tdata = NULL; /* Cancel retransmission timer */ tsx_cancel_timer(tsx, &tsx->retransmit_timer); /* Stop timer B. */ lock_timer(tsx); tsx_cancel_timer( tsx, &tsx->timeout_timer ); unlock_timer(tsx); /* Generate and send ACK (for INVITE) */ if (tsx->method.id == PJSIP_INVITE_METHOD) { pj_status_t status; status = pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx, event->body.rx_msg.rdata, &ack_tdata); if (status != PJ_SUCCESS) return status; status = tsx_send_msg( tsx, ack_tdata); if (status != PJ_SUCCESS) return status; } /* Inform TU. */ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0); /* Generate and send ACK for INVITE. */ if (tsx->method.id == PJSIP_INVITE_METHOD) { if (ack_tdata != tsx->last_tx) { pjsip_tx_data_dec_ref(tsx->last_tx); tsx->last_tx = ack_tdata; /* This is a bug. tsx_send_msg() does NOT decrement tdata's reference counter, so if we add the reference counter here, tdata will have reference counter 2, causing it to leak. pjsip_tx_data_add_ref(ack_tdata); */ } } /* Start Timer D with TD/T4 timer if unreliable transport is used. */ /* Note: tsx->transport may be NULL! */ if (!tsx->is_reliable) { if (tsx->method.id == PJSIP_INVITE_METHOD) { timeout = td_timer_val; } else { timeout = t4_timer_val; } } else { timeout.sec = timeout.msec = 0; } lock_timer(tsx); /* In the short period above timer may have been inserted * by set_timeout() (by CANCEL). Cancel it if necessary. See: * https://trac.pjsip.org/repos/ticket/1374 */ tsx_cancel_timer( tsx, &tsx->timeout_timer ); tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout, TIMEOUT_TIMER); unlock_timer(tsx); } else { // Shouldn't happen because there's no timer for this state. pj_assert(!"Unexpected event"); return PJ_EBUG; } return PJ_SUCCESS; } /* * Handler for events in Completed state for UAS */ static pj_status_t tsx_on_state_completed_uas( pjsip_transaction *tsx, pjsip_event *event) { pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED); if (event->type == PJSIP_EVENT_RX_MSG) { pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg; /* This must be a request message retransmission. */ if (msg->type != PJSIP_REQUEST_MSG) return PJSIP_ENOTREQUESTMSG; /* On receive request retransmission, retransmit last response. */ if (msg->line.req.method.id != PJSIP_ACK_METHOD) { pj_status_t status; status = tsx_retransmit( tsx, 0 ); if (status != PJ_SUCCESS) { return status; } } else { pj_time_val timeout; /* Process incoming ACK request. */ /* Verify that this is an INVITE transaction */ if (tsx->method.id != PJSIP_INVITE_METHOD) { PJ_LOG(2, (tsx->obj_name, "Received illegal ACK for %.*s transaction", (int)tsx->method.name.slen, tsx->method.name.ptr)); return PJSIP_EINVALIDMETHOD; } /* Cease retransmission. */ tsx_cancel_timer(tsx, &tsx->retransmit_timer); tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED); /* Reschedule timeout timer. */ lock_timer(tsx); tsx_cancel_timer( tsx, &tsx->timeout_timer ); /* Timer I is T4 timer for unreliable transports, and * zero seconds for reliable transports. */ if (tsx->is_reliable) { timeout.sec = 0; timeout.msec = 0; } else { timeout.sec = t4_timer_val.sec; timeout.msec = t4_timer_val.msec; } tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout, TIMEOUT_TIMER); unlock_timer(tsx); /* Move state to "Confirmed" */ tsx_set_state( tsx, PJSIP_TSX_STATE_CONFIRMED, PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0 ); } } else if (event->type == PJSIP_EVENT_TIMER) { if (event->body.timer.entry == &tsx->retransmit_timer) { /* Retransmit message. */ pj_status_t status; status = tsx_retransmit( tsx, 1 ); if (status != PJ_SUCCESS) { return status; } } else { if (tsx->method.id == PJSIP_INVITE_METHOD) { /* For INVITE, this means that ACK was never received. * Set state to Terminated, and inform TU. */ tsx_set_status_code(tsx, PJSIP_SC_TSX_TIMEOUT, NULL); tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0 ); //return PJSIP_ETSXDESTROYED; } else { /* Transaction terminated, it can now be deleted. */ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0 ); //return PJSIP_ETSXDESTROYED; } } } else { /* Ignore request to transmit. */ PJ_ASSERT_RETURN(event->type == PJSIP_EVENT_TX_MSG && event->body.tx_msg.tdata == tsx->last_tx, PJ_EINVALIDOP); } return PJ_SUCCESS; } /* * Handler for events in Completed state for UAC transaction. */ static pj_status_t tsx_on_state_completed_uac( pjsip_transaction *tsx, pjsip_event *event) { pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED); if (event->type == PJSIP_EVENT_TIMER) { /* Ignore stray retransmit event * https://trac.pjsip.org/repos/ticket/1766 */ if (event->body.timer.entry != &tsx->timeout_timer) return PJ_SUCCESS; /* Move to Terminated state. */ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_TIMER, event->body.timer.entry, 0 ); /* Transaction has been destroyed. */ //return PJSIP_ETSXDESTROYED; } else if (event->type == PJSIP_EVENT_RX_MSG) { if (tsx->method.id == PJSIP_INVITE_METHOD) { /* On received of final response retransmission, retransmit the ACK. * TU doesn't need to be informed. */ pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg; pj_assert(msg->type == PJSIP_RESPONSE_MSG); if (msg->type==PJSIP_RESPONSE_MSG && msg->line.status.code >= 200) { pj_status_t status; status = tsx_retransmit( tsx, 0 ); if (status != PJ_SUCCESS) { return status; } } else { /* Very late retransmission of privisional response. */ pj_assert( msg->type == PJSIP_RESPONSE_MSG ); } } else { /* Just drop the response. */ } } else { pj_assert(!"Unexpected event"); return PJ_EINVALIDOP; } return PJ_SUCCESS; } /* * Handler for events in state Confirmed. */ static pj_status_t tsx_on_state_confirmed( pjsip_transaction *tsx, pjsip_event *event) { pj_assert(tsx->state == PJSIP_TSX_STATE_CONFIRMED); /* This state is only for UAS for INVITE. */ pj_assert(tsx->role == PJSIP_ROLE_UAS); pj_assert(tsx->method.id == PJSIP_INVITE_METHOD); /* Absorb any ACK received. */ if (event->type == PJSIP_EVENT_RX_MSG) { pjsip_msg *msg = event->body.rx_msg.rdata->msg_info.msg; /* Only expecting request message. */ if (msg->type != PJSIP_REQUEST_MSG) return PJSIP_ENOTREQUESTMSG; /* Must be an ACK request or a late INVITE retransmission. */ pj_assert(msg->line.req.method.id == PJSIP_ACK_METHOD || msg->line.req.method.id == PJSIP_INVITE_METHOD); } else if (event->type == PJSIP_EVENT_TIMER) { /* Ignore overlapped retransmit timer. * https://trac.pjsip.org/repos/ticket/1746 */ if (event->body.timer.entry == &tsx->retransmit_timer) { /* Ignore */ } else { /* Must be from timeout_timer_. */ pj_assert(event->body.timer.entry == &tsx->timeout_timer); /* Move to Terminated state. */ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0 ); /* Transaction has been destroyed. */ //return PJSIP_ETSXDESTROYED; } } else { pj_assert(!"Unexpected event"); return PJ_EBUG; } return PJ_SUCCESS; } /* * Handler for events in state Terminated. */ static pj_status_t tsx_on_state_terminated( pjsip_transaction *tsx, pjsip_event *event) { pj_assert(tsx->state == PJSIP_TSX_STATE_TERMINATED); /* Ignore events other than timer. This used to be an assertion but * events may genuinely arrive at this state. */ if (event->type != PJSIP_EVENT_TIMER) { return PJ_EIGNORED; } /* Destroy this transaction */ tsx_set_state(tsx, PJSIP_TSX_STATE_DESTROYED, event->type, event->body.user.user1, 0 ); return PJ_SUCCESS; } /* * Handler for events in state Destroyed. * Shouldn't happen! */ static pj_status_t tsx_on_state_destroyed(pjsip_transaction *tsx, pjsip_event *event) { PJ_UNUSED_ARG(tsx); PJ_UNUSED_ARG(event); // See https://trac.pjsip.org/repos/ticket/1432 //pj_assert(!"Not expecting any events!!"); return PJ_EIGNORED; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_transport.c ================================================ /* $Id: sip_transport.c 4530 2013-05-30 09:27:49Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "sip_transport.c" #if 0 # define TRACE_(x) PJ_LOG(5,x) static const char *addr_string(const pj_sockaddr_t *addr) { static char str[PJ_INET6_ADDRSTRLEN]; pj_inet_ntop(((const pj_sockaddr*)addr)->addr.sa_family, pj_sockaddr_get_addr(addr), str, sizeof(str)); return str; } #else # define TRACE_(x) #endif /* Prototype. */ static pj_status_t mod_on_tx_msg(pjsip_tx_data *tdata); /* This module has sole purpose to print transmit data to contigous buffer * before actually transmitted to the wire. */ static pjsip_module mod_msg_print = { NULL, NULL, /* prev and next */ { "mod-msg-print", 13}, /* Name. */ -1, /* Id */ PJSIP_MOD_PRIORITY_TRANSPORT_LAYER, /* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ NULL, /* unload() */ NULL, /* on_rx_request() */ NULL, /* on_rx_response() */ &mod_on_tx_msg, /* on_tx_request() */ &mod_on_tx_msg, /* on_tx_response() */ NULL, /* on_tsx_state() */ }; /* Transport list item */ typedef struct transport { PJ_DECL_LIST_MEMBER(struct transport); pjsip_transport *tp; } transport; /* * Transport manager. */ struct pjsip_tpmgr { pj_hash_table_t *table; pj_lock_t *lock; pjsip_endpoint *endpt; pjsip_tpfactory factory_list; #if defined(PJ_DEBUG) && PJ_DEBUG!=0 pj_atomic_t *tdata_counter; #endif void (*on_rx_msg)(pjsip_endpoint*, pj_status_t, pjsip_rx_data*); pj_status_t (*on_tx_msg)(pjsip_endpoint*, pjsip_tx_data*); pjsip_tp_state_callback tp_state_cb; pjsip_tp_on_rx_dropped_cb tp_drop_data_cb; /* Transmit data list, for transmit data cleanup when transport manager * is destroyed. */ pjsip_tx_data tdata_list; /* List of transports which are NOT stored in the hash table, so * that it can be properly cleaned up when transport manager * is destroyed. */ transport tp_list; }; /* Transport state listener list type */ typedef struct tp_state_listener { PJ_DECL_LIST_MEMBER(struct tp_state_listener); pjsip_tp_state_callback cb; void *user_data; } tp_state_listener; /* * Transport data. */ typedef struct transport_data { /* Transport listeners */ tp_state_listener st_listeners; tp_state_listener st_listeners_empty; } transport_data; /***************************************************************************** * * GENERAL TRANSPORT (NAMES, TYPES, ETC.) * *****************************************************************************/ /* * Transport names. */ static struct transport_names_t { pjsip_transport_type_e type; /* Transport type */ pj_uint16_t port; /* Default port number */ pj_str_t name; /* Id tag */ const char *description; /* Longer description */ unsigned flag; /* Flags */ char name_buf[16]; /* For user's transport */ } transport_names[16] = { { PJSIP_TRANSPORT_UNSPECIFIED, 0, {"Unspecified", 11}, "Unspecified", 0 }, { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}, "UDP transport", PJSIP_TRANSPORT_DATAGRAM }, { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}, "TCP transport", PJSIP_TRANSPORT_RELIABLE }, { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}, "TLS transport", PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE }, { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}, "SCTP transport", PJSIP_TRANSPORT_RELIABLE }, { PJSIP_TRANSPORT_LOOP, 15060, {"LOOP", 4}, "Loopback transport", PJSIP_TRANSPORT_RELIABLE }, { PJSIP_TRANSPORT_LOOP_DGRAM, 15060, {"LOOP-DGRAM", 10}, "Loopback datagram transport", PJSIP_TRANSPORT_DATAGRAM }, { PJSIP_TRANSPORT_UDP6, 5060, {"UDP", 3}, "UDP IPv6 transport", PJSIP_TRANSPORT_DATAGRAM }, { PJSIP_TRANSPORT_TCP6, 5060, {"TCP", 3}, "TCP IPv6 transport", PJSIP_TRANSPORT_RELIABLE }, { PJSIP_TRANSPORT_TLS6, 5061, {"TLS", 3}, "TLS IPv6 transport", PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE }, }; static void tp_state_callback(pjsip_transport *tp, pjsip_transport_state state, const pjsip_transport_state_info *info); static struct transport_names_t *get_tpname(pjsip_transport_type_e type) { unsigned i; for (i=0; islen == 0) return PJSIP_TRANSPORT_UNSPECIFIED; /* Get transport type from name. */ for (i=0; iflag; } /* * Get the default SIP port number for the specified type. */ PJ_DEF(int) pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type) { /* Return the port. */ return get_tpname(type)->port; } /* * Get transport name. */ PJ_DEF(const char*) pjsip_transport_get_type_name(pjsip_transport_type_e type) { /* Return the name. */ return get_tpname(type)->name.ptr; } /* * Get transport description. */ PJ_DEF(const char*) pjsip_transport_get_type_desc(pjsip_transport_type_e type) { /* Return the description. */ return get_tpname(type)->description; } /***************************************************************************** * * TRANSPORT SELECTOR * *****************************************************************************/ /* * Add transport/listener reference in the selector. */ PJ_DEF(void) pjsip_tpselector_add_ref(pjsip_tpselector *sel) { if (sel->type == PJSIP_TPSELECTOR_TRANSPORT && sel->u.transport != NULL) pjsip_transport_add_ref(sel->u.transport); else if (sel->type == PJSIP_TPSELECTOR_LISTENER && sel->u.listener != NULL) ; /* Hmm.. looks like we don't have reference counter for listener */ } /* * Decrement transport/listener reference in the selector. */ PJ_DEF(void) pjsip_tpselector_dec_ref(pjsip_tpselector *sel) { if (sel->type == PJSIP_TPSELECTOR_TRANSPORT && sel->u.transport != NULL) pjsip_transport_dec_ref(sel->u.transport); else if (sel->type == PJSIP_TPSELECTOR_LISTENER && sel->u.listener != NULL) ; /* Hmm.. looks like we don't have reference counter for listener */ } /***************************************************************************** * * TRANSMIT DATA BUFFER MANIPULATION. * *****************************************************************************/ /* * Create new transmit buffer. */ PJ_DEF(pj_status_t) pjsip_tx_data_create( pjsip_tpmgr *mgr, pjsip_tx_data **p_tdata ) { pj_pool_t *pool; pjsip_tx_data *tdata; pj_status_t status; PJ_ASSERT_RETURN(mgr && p_tdata, PJ_EINVAL); pool = pjsip_endpt_create_pool( mgr->endpt, "tdta%p", PJSIP_POOL_LEN_TDATA, PJSIP_POOL_INC_TDATA ); if (!pool) return PJ_ENOMEM; tdata = PJ_POOL_ZALLOC_T(pool, pjsip_tx_data); tdata->pool = pool; tdata->mgr = mgr; pj_memcpy(tdata->obj_name, pool->obj_name, PJ_MAX_OBJ_NAME); status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt); if (status != PJ_SUCCESS) { pjsip_endpt_release_pool( mgr->endpt, tdata->pool ); return status; } //status = pj_lock_create_simple_mutex(pool, "tdta%p", &tdata->lock); status = pj_lock_create_null_mutex(pool, "tdta%p", &tdata->lock); if (status != PJ_SUCCESS) { pjsip_endpt_release_pool( mgr->endpt, tdata->pool ); return status; } pj_ioqueue_op_key_init(&tdata->op_key.key, sizeof(tdata->op_key.key)); pj_list_init(tdata); #if defined(PJSIP_HAS_TX_DATA_LIST) && PJSIP_HAS_TX_DATA_LIST!=0 /* Append this just created tdata to transmit buffer list */ pj_lock_acquire(mgr->lock); pj_list_push_back(&mgr->tdata_list, tdata); pj_lock_release(mgr->lock); #endif #if defined(PJ_DEBUG) && PJ_DEBUG!=0 pj_atomic_inc( tdata->mgr->tdata_counter ); #endif *p_tdata = tdata; return PJ_SUCCESS; } /* * Add reference to tx buffer. */ PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata ) { pj_atomic_inc(tdata->ref_cnt); } static void tx_data_destroy(pjsip_tx_data *tdata) { PJ_LOG(5,(tdata->obj_name, "Destroying txdata %s", pjsip_tx_data_get_info(tdata))); pjsip_tpselector_dec_ref(&tdata->tp_sel); #if defined(PJ_DEBUG) && PJ_DEBUG!=0 pj_atomic_dec( tdata->mgr->tdata_counter ); #endif #if defined(PJSIP_HAS_TX_DATA_LIST) && PJSIP_HAS_TX_DATA_LIST!=0 /* Remove this tdata from transmit buffer list */ pj_lock_acquire(tdata->mgr->lock); pj_list_erase(tdata); pj_lock_release(tdata->mgr->lock); #endif pj_atomic_destroy( tdata->ref_cnt ); pj_lock_destroy( tdata->lock ); pjsip_endpt_release_pool( tdata->mgr->endpt, tdata->pool ); } /* * Decrease transport data reference, destroy it when the reference count * reaches zero. */ PJ_DEF(pj_status_t) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata ) { pj_assert( pj_atomic_get(tdata->ref_cnt) > 0); if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) { tx_data_destroy(tdata); return PJSIP_EBUFDESTROYED; } else { return PJ_SUCCESS; } } /* * Invalidate the content of the print buffer to force the message to be * re-printed when sent. */ PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata ) { tdata->buf.cur = tdata->buf.start; tdata->info = NULL; } /* * Print the SIP message to transmit data buffer's internal buffer. */ PJ_DEF(pj_status_t) pjsip_tx_data_encode(pjsip_tx_data *tdata) { /* Allocate buffer if necessary. */ if (tdata->buf.start == NULL) { PJ_USE_EXCEPTION; PJ_TRY { tdata->buf.start = (char*) pj_pool_alloc(tdata->pool, PJSIP_MAX_PKT_LEN); } PJ_CATCH_ANY { return PJ_ENOMEM; } PJ_END tdata->buf.cur = tdata->buf.start; tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN; } /* Do we need to reprint? */ if (!pjsip_tx_data_is_valid(tdata)) { pj_ssize_t size; size = pjsip_msg_print( tdata->msg, tdata->buf.start, tdata->buf.end - tdata->buf.start); if (size < 0) { return PJSIP_EMSGTOOLONG; } pj_assert(size != 0); tdata->buf.cur[size] = '\0'; tdata->buf.cur += size; } return PJ_SUCCESS; } PJ_DEF(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata ) { return tdata->buf.cur != tdata->buf.start; } static char *get_msg_info(pj_pool_t *pool, const char *obj_name, const pjsip_msg *msg) { char info_buf[128], *info; const pjsip_cseq_hdr *cseq; int len; cseq = (const pjsip_cseq_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL); PJ_ASSERT_RETURN(cseq != NULL, "INVALID MSG"); if (msg->type == PJSIP_REQUEST_MSG) { len = pj_ansi_snprintf(info_buf, sizeof(info_buf), "Request msg %.*s/cseq=%d (%s)", (int)msg->line.req.method.name.slen, msg->line.req.method.name.ptr, cseq->cseq, obj_name); } else { len = pj_ansi_snprintf(info_buf, sizeof(info_buf), "Response msg %d/%.*s/cseq=%d (%s)", msg->line.status.code, (int)cseq->method.name.slen, cseq->method.name.ptr, cseq->cseq, obj_name); } if (len < 1 || len >= (int)sizeof(info_buf)) { return (char*)obj_name; } info = (char*) pj_pool_alloc(pool, len+1); pj_memcpy(info, info_buf, len+1); return info; } PJ_DEF(char*) pjsip_tx_data_get_info( pjsip_tx_data *tdata ) { PJ_ASSERT_RETURN(tdata, "NULL"); /* tdata->info may be assigned by application so if it exists * just return it. */ if (tdata->info) return tdata->info; if (tdata->msg==NULL) return "NULL"; pj_lock_acquire(tdata->lock); tdata->info = get_msg_info(tdata->pool, tdata->obj_name, tdata->msg); pj_lock_release(tdata->lock); return tdata->info; } PJ_DEF(pj_status_t) pjsip_tx_data_set_transport(pjsip_tx_data *tdata, const pjsip_tpselector *sel) { PJ_ASSERT_RETURN(tdata && sel, PJ_EINVAL); pj_lock_acquire(tdata->lock); pjsip_tpselector_dec_ref(&tdata->tp_sel); pj_memcpy(&tdata->tp_sel, sel, sizeof(*sel)); pjsip_tpselector_add_ref(&tdata->tp_sel); pj_lock_release(tdata->lock); return PJ_SUCCESS; } PJ_DEF(char*) pjsip_rx_data_get_info(pjsip_rx_data *rdata) { char obj_name[PJ_MAX_OBJ_NAME]; PJ_ASSERT_RETURN(rdata->msg_info.msg, "INVALID MSG"); if (rdata->msg_info.info) return rdata->msg_info.info; pj_ansi_strcpy(obj_name, "rdata"); pj_ansi_snprintf(obj_name+5, sizeof(obj_name)-5, "%p", rdata); rdata->msg_info.info = get_msg_info(rdata->tp_info.pool, obj_name, rdata->msg_info.msg); return rdata->msg_info.info; } /* Clone pjsip_rx_data. */ PJ_DEF(pj_status_t) pjsip_rx_data_clone( const pjsip_rx_data *src, unsigned flags, pjsip_rx_data **p_rdata) { pj_pool_t *pool; pjsip_rx_data *dst; pjsip_hdr *hdr; PJ_ASSERT_RETURN(src && flags==0 && p_rdata, PJ_EINVAL); pool = pj_pool_create(src->tp_info.pool->factory, "rtd%p", PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC, NULL); if (!pool) return PJ_ENOMEM; dst = PJ_POOL_ZALLOC_T(pool, pjsip_rx_data); /* Parts of tp_info */ dst->tp_info.pool = pool; dst->tp_info.transport = (pjsip_transport*)src->tp_info.transport; /* pkt_info can be memcopied */ pj_memcpy(&dst->pkt_info, &src->pkt_info, sizeof(src->pkt_info)); /* msg_info needs deep clone */ dst->msg_info.msg_buf = dst->pkt_info.packet; dst->msg_info.len = src->msg_info.len; dst->msg_info.msg = pjsip_msg_clone(pool, src->msg_info.msg); pj_list_init(&dst->msg_info.parse_err); #define GET_MSG_HDR2(TYPE, type, var) \ case PJSIP_H_##TYPE: \ if (!dst->msg_info.var) { \ dst->msg_info.var = (pjsip_##type##_hdr*)hdr; \ } \ break #define GET_MSG_HDR(TYPE, var_type) GET_MSG_HDR2(TYPE, var_type, var_type) hdr = dst->msg_info.msg->hdr.next; while (hdr != &dst->msg_info.msg->hdr) { switch (hdr->type) { GET_MSG_HDR(CALL_ID, cid); GET_MSG_HDR(FROM, from); GET_MSG_HDR(TO, to); GET_MSG_HDR(VIA, via); GET_MSG_HDR(CSEQ, cseq); GET_MSG_HDR(MAX_FORWARDS, max_fwd); GET_MSG_HDR(ROUTE, route); GET_MSG_HDR2(RECORD_ROUTE, rr, record_route); GET_MSG_HDR(CONTENT_TYPE, ctype); GET_MSG_HDR(CONTENT_LENGTH, clen); GET_MSG_HDR(REQUIRE, require); GET_MSG_HDR(SUPPORTED, supported); default: break; } hdr = hdr->next; } #undef GET_MSG_HDR #undef GET_MSG_HDR2 *p_rdata = dst; /* Finally add transport ref */ return pjsip_transport_add_ref(dst->tp_info.transport); } /* Free previously cloned pjsip_rx_data. */ PJ_DEF(pj_status_t) pjsip_rx_data_free_cloned(pjsip_rx_data *rdata) { PJ_ASSERT_RETURN(rdata, PJ_EINVAL); pjsip_transport_dec_ref(rdata->tp_info.transport); pj_pool_release(rdata->tp_info.pool); return PJ_SUCCESS; } /***************************************************************************** * * TRANSPORT KEY * *****************************************************************************/ /***************************************************************************** * * TRANSPORT * *****************************************************************************/ static void transport_send_callback(pjsip_transport *transport, void *token, pj_ssize_t size) { pjsip_tx_data *tdata = (pjsip_tx_data*) token; PJ_UNUSED_ARG(transport); /* Mark pending off so that app can resend/reuse txdata from inside * the callback. */ tdata->is_pending = 0; /* Call callback, if any. */ if (tdata->cb) { (*tdata->cb)(tdata->token, tdata, size); } /* Decrement reference count. */ pjsip_tx_data_dec_ref(tdata); } /* This function is called by endpoint for on_tx_request() and on_tx_response() * notification. */ static pj_status_t mod_on_tx_msg(pjsip_tx_data *tdata) { return pjsip_tx_data_encode(tdata); } /* * Send a SIP message using the specified transport. */ PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr, pjsip_tx_data *tdata, const pj_sockaddr_t *addr, int addr_len, void *token, pjsip_tp_send_callback cb) { pj_status_t status; PJ_ASSERT_RETURN(tr && tdata && addr, PJ_EINVAL); /* Is it currently being sent? */ if (tdata->is_pending) { pj_assert(!"Invalid operation step!"); PJ_LOG(2,(THIS_FILE, "Unable to send %s: message is pending", pjsip_tx_data_get_info(tdata))); return PJSIP_EPENDINGTX; } /* Add reference to prevent deletion, and to cancel idle timer if * it's running. */ pjsip_transport_add_ref(tr); /* Fill in tp_info. */ tdata->tp_info.transport = tr; pj_memcpy(&tdata->tp_info.dst_addr, addr, addr_len); tdata->tp_info.dst_addr_len = addr_len; pj_inet_ntop(((pj_sockaddr*)addr)->addr.sa_family, pj_sockaddr_get_addr(addr), tdata->tp_info.dst_name, sizeof(tdata->tp_info.dst_name)); tdata->tp_info.dst_port = pj_sockaddr_get_port(addr); /* Distribute to modules. * When the message reach mod_msg_print, the contents of the message will * be "printed" to contiguous buffer. */ if (tr->tpmgr->on_tx_msg) { status = (*tr->tpmgr->on_tx_msg)(tr->endpt, tdata); if (status != PJ_SUCCESS) { pjsip_transport_dec_ref(tr); return status; } } /* Save callback data. */ tdata->token = token; tdata->cb = cb; /* Add reference counter. */ pjsip_tx_data_add_ref(tdata); /* Mark as pending. */ tdata->is_pending = 1; /* Send to transport. */ status = (*tr->send_msg)(tr, tdata, addr, addr_len, (void*)tdata, &transport_send_callback); if (status != PJ_EPENDING) { tdata->is_pending = 0; pjsip_tx_data_dec_ref(tdata); } pjsip_transport_dec_ref(tr); return status; } /* send_raw() callback */ static void send_raw_callback(pjsip_transport *transport, void *token, pj_ssize_t size) { pjsip_tx_data *tdata = (pjsip_tx_data*) token; /* Mark pending off so that app can resend/reuse txdata from inside * the callback. */ tdata->is_pending = 0; /* Call callback, if any. */ if (tdata->cb) { (*tdata->cb)(tdata->token, tdata, size); } /* Decrement tdata reference count. */ pjsip_tx_data_dec_ref(tdata); /* Decrement transport reference count */ pjsip_transport_dec_ref(transport); } /* Send raw data */ PJ_DEF(pj_status_t) pjsip_tpmgr_send_raw(pjsip_tpmgr *mgr, pjsip_transport_type_e tp_type, const pjsip_tpselector *sel, pjsip_tx_data *tdata, const void *raw_data, pj_size_t data_len, const pj_sockaddr_t *addr, int addr_len, void *token, pjsip_tp_send_callback cb) { pjsip_transport *tr; pj_status_t status; /* Acquire the transport */ status = pjsip_tpmgr_acquire_transport(mgr, tp_type, addr, addr_len, sel, &tr); if (status != PJ_SUCCESS) return status; /* Create transmit data buffer if one is not specified */ if (tdata == NULL) { status = pjsip_endpt_create_tdata(tr->endpt, &tdata); if (status != PJ_SUCCESS) { pjsip_transport_dec_ref(tr); return status; } tdata->info = "raw"; /* Add reference counter. */ pjsip_tx_data_add_ref(tdata); } /* Allocate buffer */ if (tdata->buf.start == NULL || (tdata->buf.end - tdata->buf.start) < (int)data_len) { /* Note: data_len may be zero, so allocate +1 */ tdata->buf.start = (char*) pj_pool_alloc(tdata->pool, data_len+1); tdata->buf.end = tdata->buf.start + data_len + 1; } /* Copy data, if any! (application may send zero len packet) */ if (data_len) { pj_memcpy(tdata->buf.start, raw_data, data_len); } tdata->buf.cur = tdata->buf.start + data_len; /* Save callback data. */ tdata->token = token; tdata->cb = cb; /* Mark as pending. */ tdata->is_pending = 1; /* Send to transport */ status = tr->send_msg(tr, tdata, addr, addr_len, tdata, &send_raw_callback); if (status != PJ_EPENDING) { /* callback will not be called, so destroy tdata now. */ pjsip_tx_data_dec_ref(tdata); pjsip_transport_dec_ref(tr); } return status; } static void transport_idle_callback(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { pjsip_transport *tp = (pjsip_transport*) entry->user_data; pj_assert(tp != NULL); PJ_UNUSED_ARG(timer_heap); entry->id = PJ_FALSE; pjsip_transport_destroy(tp); } static pj_bool_t is_transport_valid(pjsip_transport *tp, pjsip_tpmgr *tpmgr, const pjsip_transport_key *key, int key_len) { return (pj_hash_get(tpmgr->table, key, key_len, NULL) == (void*)tp); } /* * Add ref. */ PJ_DEF(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp ) { pjsip_tpmgr *tpmgr; pjsip_transport_key key; int key_len; PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL); /* Cache some vars for checking transport validity later */ tpmgr = tp->tpmgr; key_len = sizeof(tp->key.type) + tp->addr_len; pj_memcpy(&key, &tp->key, key_len); if (pj_atomic_inc_and_get(tp->ref_cnt) == 1) { pj_lock_acquire(tpmgr->lock); /* Verify again. But first, make sure transport is still valid * (see #1883). */ if (is_transport_valid(tp, tpmgr, &key, key_len) && pj_atomic_get(tp->ref_cnt) == 1) { if (tp->idle_timer.id != PJ_FALSE) { pjsip_endpt_cancel_timer(tp->tpmgr->endpt, &tp->idle_timer); tp->idle_timer.id = PJ_FALSE; } } pj_lock_release(tpmgr->lock); } return PJ_SUCCESS; } /* * Dec ref. */ PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp ) { pjsip_tpmgr *tpmgr; pjsip_transport_key key; int key_len; PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL); pj_assert(pj_atomic_get(tp->ref_cnt) > 0); /* Cache some vars for checking transport validity later */ tpmgr = tp->tpmgr; key_len = sizeof(tp->key.type) + tp->addr_len; pj_memcpy(&key, &tp->key, key_len); if (pj_atomic_dec_and_get(tp->ref_cnt) == 0) { pj_lock_acquire(tpmgr->lock); /* Verify again. Do not register timer if the transport is * being destroyed. But first, make sure transport is still valid * (see #1883). */ if (is_transport_valid(tp, tpmgr, &key, key_len) && !tp->is_destroying && pj_atomic_get(tp->ref_cnt) == 0) { pj_time_val delay; /* If transport is in graceful shutdown, then this is the * last user who uses the transport. Schedule to destroy the * transport immediately. Otherwise schedule idle timer. */ if (tp->is_shutdown) { delay.sec = delay.msec = 0; } else { delay.sec = (tp->dir==PJSIP_TP_DIR_OUTGOING) ? PJSIP_TRANSPORT_IDLE_TIME : PJSIP_TRANSPORT_SERVER_IDLE_TIME; delay.msec = 0; } pj_assert(tp->idle_timer.id == 0); tp->idle_timer.id = PJ_TRUE; pjsip_endpt_schedule_timer(tp->tpmgr->endpt, &tp->idle_timer, &delay); } pj_lock_release(tpmgr->lock); } return PJ_SUCCESS; } /** * Register a transport. */ PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr, pjsip_transport *tp ) { int key_len; pj_uint32_t hval; void *entry; /* Init. */ tp->tpmgr = mgr; pj_bzero(&tp->idle_timer, sizeof(tp->idle_timer)); tp->idle_timer.user_data = tp; tp->idle_timer.cb = &transport_idle_callback; /* * Register to hash table (see Trac ticket #42). */ key_len = sizeof(tp->key.type) + tp->addr_len; pj_lock_acquire(mgr->lock); /* If entry already occupied, unregister previous entry */ hval = 0; entry = pj_hash_get(mgr->table, &tp->key, key_len, &hval); if (entry != NULL) { transport *tp_ref; tp_ref = PJ_POOL_ZALLOC_T(((pjsip_transport *)entry)->pool, transport); /* * Add transport to the list before removing it from the hash table. * See ticket #1774 for more details. */ tp_ref->tp = (pjsip_transport *)entry; pj_list_push_back(&mgr->tp_list, tp_ref); pj_hash_set(NULL, mgr->table, &tp->key, key_len, hval, NULL); } /* Register new entry */ pj_hash_set(tp->pool, mgr->table, &tp->key, key_len, hval, tp); pj_lock_release(mgr->lock); TRACE_((THIS_FILE,"Transport %s registered: type=%s, remote=%s:%d", tp->obj_name, pjsip_transport_get_type_name(tp->key.type), addr_string(&tp->key.rem_addr), pj_sockaddr_get_port(&tp->key.rem_addr))); return PJ_SUCCESS; } /* Force destroy transport (e.g. during transport manager shutdown. */ static pj_status_t destroy_transport( pjsip_tpmgr *mgr, pjsip_transport *tp ) { int key_len; pj_uint32_t hval; void *entry; tp->is_destroying = PJ_TRUE; TRACE_((THIS_FILE, "Transport %s is being destroyed", tp->obj_name)); pj_lock_acquire(tp->lock); pj_lock_acquire(mgr->lock); /* * Unregister timer, if any. */ //pj_assert(tp->idle_timer.id == PJ_FALSE); if (tp->idle_timer.id != PJ_FALSE) { pjsip_endpt_cancel_timer(mgr->endpt, &tp->idle_timer); tp->idle_timer.id = PJ_FALSE; } /* * Unregister from hash table (see Trac ticket #42). */ key_len = sizeof(tp->key.type) + tp->addr_len; hval = 0; entry = pj_hash_get(mgr->table, &tp->key, key_len, &hval); if (entry == (void*)tp) { pj_hash_set(NULL, mgr->table, &tp->key, key_len, hval, NULL); } else { /* If not found in hash table, remove from the tranport list. */ transport *tp_iter = mgr->tp_list.next; while (tp_iter != &mgr->tp_list) { if (tp_iter->tp == tp) { pj_list_erase(tp_iter); break; } tp_iter = tp_iter->next; } } pj_lock_release(mgr->lock); pj_lock_release(tp->lock); /* Destroy. */ return tp->destroy(tp); } /* * Start graceful shutdown procedure for this transport. */ PJ_DEF(pj_status_t) pjsip_transport_shutdown(pjsip_transport *tp) { pjsip_tpmgr *mgr; pj_status_t status; pjsip_tp_state_callback state_cb; TRACE_((THIS_FILE, "Transport %s shutting down", tp->obj_name)); pj_lock_acquire(tp->lock); mgr = tp->tpmgr; pj_lock_acquire(mgr->lock); /* Do nothing if transport is being shutdown already */ if (tp->is_shutdown) { pj_lock_release(mgr->lock); pj_lock_release(tp->lock); return PJ_SUCCESS; } status = PJ_SUCCESS; /* Instruct transport to shutdown itself */ if (tp->do_shutdown) status = tp->do_shutdown(tp); /* Notify application of transport shutdown */ state_cb = pjsip_tpmgr_get_state_cb(tp->tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pj_bzero(&state_info, sizeof(state_info)); state_info.status = status; (*state_cb)(tp, PJSIP_TP_STATE_SHUTDOWN, &state_info); } if (status == PJ_SUCCESS) tp->is_shutdown = PJ_TRUE; /* If transport reference count is zero, start timer count-down */ if (pj_atomic_get(tp->ref_cnt) == 0) { pjsip_transport_add_ref(tp); pjsip_transport_dec_ref(tp); } pj_lock_release(mgr->lock); pj_lock_release(tp->lock); return status; } /** * Unregister transport. */ PJ_DEF(pj_status_t) pjsip_transport_destroy( pjsip_transport *tp) { pjsip_tp_state_callback state_cb; /* Must have no user. */ PJ_ASSERT_RETURN(pj_atomic_get(tp->ref_cnt) == 0, PJSIP_EBUSY); /* Notify application of transport destroy */ state_cb = pjsip_tpmgr_get_state_cb(tp->tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pj_bzero(&state_info, sizeof(state_info)); (*state_cb)(tp, PJSIP_TP_STATE_DESTROY, &state_info); } /* Destroy. */ return destroy_transport(tp->tpmgr, tp); } /***************************************************************************** * * TRANSPORT FACTORY * *****************************************************************************/ PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr, pjsip_tpfactory *tpf) { pjsip_tpfactory *p; pj_status_t status; pj_lock_acquire(mgr->lock); /* Check that no factory with the same type has been registered. */ status = PJ_SUCCESS; for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) { if (p->type == tpf->type) { status = PJSIP_ETYPEEXISTS; break; } if (p == tpf) { status = PJ_EEXISTS; break; } } if (status != PJ_SUCCESS) { pj_lock_release(mgr->lock); return status; } pj_list_insert_before(&mgr->factory_list, tpf); pj_lock_release(mgr->lock); return PJ_SUCCESS; } /** * Unregister factory. */ PJ_DEF(pj_status_t) pjsip_tpmgr_unregister_tpfactory( pjsip_tpmgr *mgr, pjsip_tpfactory *tpf) { pj_lock_acquire(mgr->lock); pj_assert(pj_list_find_node(&mgr->factory_list, tpf) == tpf); pj_list_erase(tpf); pj_lock_release(mgr->lock); return PJ_SUCCESS; } PJ_DECL(void) pjsip_tpmgr_fla2_param_default(pjsip_tpmgr_fla2_param *prm) { pj_bzero(prm, sizeof(*prm)); } static pj_bool_t pjsip_tpmgr_is_tpfactory_valid(pjsip_tpmgr *mgr, pjsip_tpfactory *tpf) { pjsip_tpfactory *p; pj_lock_acquire(mgr->lock); for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) { if (p == tpf) { pj_lock_release(mgr->lock); return PJ_TRUE; } } pj_lock_release(mgr->lock); return PJ_FALSE; } /***************************************************************************** * * TRANSPORT MANAGER * *****************************************************************************/ /* * Create a new transport manager. */ PJ_DEF(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool, pjsip_endpoint *endpt, pjsip_rx_callback rx_cb, pjsip_tx_callback tx_cb, pjsip_tpmgr **p_mgr) { pjsip_tpmgr *mgr; pj_status_t status; PJ_ASSERT_RETURN(pool && endpt && rx_cb && p_mgr, PJ_EINVAL); /* Register mod_msg_print module. */ status = pjsip_endpt_register_module(endpt, &mod_msg_print); if (status != PJ_SUCCESS) return status; /* Create and initialize transport manager. */ mgr = PJ_POOL_ZALLOC_T(pool, pjsip_tpmgr); mgr->endpt = endpt; mgr->on_rx_msg = rx_cb; mgr->on_tx_msg = tx_cb; pj_list_init(&mgr->factory_list); pj_list_init(&mgr->tdata_list); pj_list_init(&mgr->tp_list); mgr->table = pj_hash_create(pool, PJSIP_TPMGR_HTABLE_SIZE); if (!mgr->table) return PJ_ENOMEM; status = pj_lock_create_recursive_mutex(pool, "tmgr%p", &mgr->lock); if (status != PJ_SUCCESS) return status; #if defined(PJ_DEBUG) && PJ_DEBUG!=0 status = pj_atomic_create(pool, 0, &mgr->tdata_counter); if (status != PJ_SUCCESS) { pj_lock_destroy(mgr->lock); return status; } #endif /* Set transport state callback */ pjsip_tpmgr_set_state_cb(mgr, &tp_state_callback); PJ_LOG(5, (THIS_FILE, "Transport manager created.")); *p_mgr = mgr; return PJ_SUCCESS; } /* Get the interface to send packet to the specified address */ static pj_status_t get_net_interface(pjsip_transport_type_e tp_type, const pj_str_t *dst, pj_str_t *itf_str_addr) { int af; pj_sockaddr itf_addr; pj_status_t status = -1; af = (tp_type & PJSIP_TRANSPORT_IPV6)? PJ_AF_INET6 : PJ_AF_INET; if (pjsip_cfg()->endpt.resolve_hostname_to_get_interface) { status = pj_getipinterface(af, dst, &itf_addr, PJ_TRUE, NULL); } if (status != PJ_SUCCESS) { status = pj_getipinterface(af, dst, &itf_addr, PJ_FALSE, NULL); if (status != PJ_SUCCESS) { /* If it fails, e.g: on WM6(http://support.microsoft.com/kb/129065), * just fallback using pj_gethostip(), see ticket #1660. */ PJ_LOG(5,(THIS_FILE,"Warning: unable to determine local " "interface, fallback to default interface!")); status = pj_gethostip(af, &itf_addr); if (status != PJ_SUCCESS) return status; } } /* Print address */ pj_sockaddr_print(&itf_addr, itf_str_addr->ptr, PJ_INET6_ADDRSTRLEN, 0); itf_str_addr->slen = pj_ansi_strlen(itf_str_addr->ptr); return PJ_SUCCESS; } /* * Find out the appropriate local address info (IP address and port) to * advertise in Contact header based on the remote address to be * contacted. The local address info would be the address name of the * transport or listener which will be used to send the request. * * In this implementation, it will only select the transport based on * the transport type in the request. */ PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr2(pjsip_tpmgr *tpmgr, pj_pool_t *pool, pjsip_tpmgr_fla2_param *prm) { char tmp_buf[PJ_INET6_ADDRSTRLEN+10]; pj_str_t tmp_str; pj_status_t status = PJSIP_EUNSUPTRANSPORT; unsigned flag; /* Sanity checks */ PJ_ASSERT_RETURN(tpmgr && pool && prm, PJ_EINVAL); pj_strset(&tmp_str, tmp_buf, 0); prm->ret_addr.slen = 0; prm->ret_port = 0; prm->ret_tp = NULL; flag = pjsip_transport_get_flag_from_type(prm->tp_type); if (prm->tp_sel && prm->tp_sel->type == PJSIP_TPSELECTOR_TRANSPORT && prm->tp_sel->u.transport) { const pjsip_transport *tp = prm->tp_sel->u.transport; if (prm->local_if) { status = get_net_interface((pjsip_transport_type_e)tp->key.type, &prm->dst_host, &tmp_str); if (status != PJ_SUCCESS) goto on_return; pj_strdup(pool, &prm->ret_addr, &tmp_str); prm->ret_port = pj_sockaddr_get_port(&tp->local_addr); prm->ret_tp = tp; } else { pj_strdup(pool, &prm->ret_addr, &tp->local_name.host); prm->ret_port = (pj_uint16_t)tp->local_name.port; } status = PJ_SUCCESS; } else if (prm->tp_sel && prm->tp_sel->type == PJSIP_TPSELECTOR_LISTENER && prm->tp_sel->u.listener) { if (prm->local_if) { status = get_net_interface(prm->tp_sel->u.listener->type, &prm->dst_host, &tmp_str); if (status != PJ_SUCCESS) goto on_return; pj_strdup(pool, &prm->ret_addr, &tmp_str); } else { pj_strdup(pool, &prm->ret_addr, &prm->tp_sel->u.listener->addr_name.host); } prm->ret_port = (pj_uint16_t)prm->tp_sel->u.listener->addr_name.port; status = PJ_SUCCESS; } else if ((flag & PJSIP_TRANSPORT_DATAGRAM) != 0) { pj_sockaddr remote; int addr_len; pjsip_transport *tp; pj_bzero(&remote, sizeof(remote)); if (prm->tp_type & PJSIP_TRANSPORT_IPV6) { addr_len = sizeof(pj_sockaddr_in6); remote.addr.sa_family = pj_AF_INET6(); } else { addr_len = sizeof(pj_sockaddr_in); remote.addr.sa_family = pj_AF_INET(); } status = pjsip_tpmgr_acquire_transport(tpmgr, prm->tp_type, &remote, addr_len, NULL, &tp); if (status == PJ_SUCCESS) { if (prm->local_if) { status = get_net_interface((pjsip_transport_type_e) tp->key.type, &prm->dst_host, &tmp_str); if (status != PJ_SUCCESS) goto on_return; pj_strdup(pool, &prm->ret_addr, &tmp_str); prm->ret_port = pj_sockaddr_get_port(&tp->local_addr); prm->ret_tp = tp; } else { pj_strdup(pool, &prm->ret_addr, &tp->local_name.host); prm->ret_port = (pj_uint16_t)tp->local_name.port; } pjsip_transport_dec_ref(tp); } } else { /* For connection oriented transport, enum the factories */ pjsip_tpfactory *f; pj_lock_acquire(tpmgr->lock); f = tpmgr->factory_list.next; while (f != &tpmgr->factory_list) { if (f->type == prm->tp_type) break; f = f->next; } if (f != &tpmgr->factory_list) { if (prm->local_if) { status = get_net_interface(f->type, &prm->dst_host, &tmp_str); if (status == PJ_SUCCESS) { pj_strdup(pool, &prm->ret_addr, &tmp_str); } else { /* It could fail "normally" on certain cases, e.g. * when connecting to IPv6 link local address, it * will wail with EINVAL. * In this case, fallback to use the default interface * rather than failing the call. */ PJ_PERROR(5,(THIS_FILE, status, "Warning: unable to " "determine local interface")); pj_strdup(pool, &prm->ret_addr, &f->addr_name.host); status = PJ_SUCCESS; } } else { pj_strdup(pool, &prm->ret_addr, &f->addr_name.host); } prm->ret_port = (pj_uint16_t)f->addr_name.port; status = PJ_SUCCESS; } pj_lock_release(tpmgr->lock); } on_return: return status; } PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr, pj_pool_t *pool, pjsip_transport_type_e type, const pjsip_tpselector *sel, pj_str_t *ip_addr, int *port) { pjsip_tpmgr_fla2_param prm; pj_status_t status; pjsip_tpmgr_fla2_param_default(&prm); prm.tp_type = type; prm.tp_sel = sel; status = pjsip_tpmgr_find_local_addr2(tpmgr, pool, &prm); if (status != PJ_SUCCESS) return status; *ip_addr = prm.ret_addr; *port = prm.ret_port; return PJ_SUCCESS; } /* * Return number of transports currently registered to the transport * manager. */ PJ_DEF(unsigned) pjsip_tpmgr_get_transport_count(pjsip_tpmgr *mgr) { pj_hash_iterator_t itr_val; pj_hash_iterator_t *itr; int nr_of_transports = 0; pj_lock_acquire(mgr->lock); itr = pj_hash_first(mgr->table, &itr_val); while (itr) { nr_of_transports++; itr = pj_hash_next(mgr->table, itr); } pj_lock_release(mgr->lock); return nr_of_transports; } /* * pjsip_tpmgr_destroy() * * Destroy transport manager. */ PJ_DEF(pj_status_t) pjsip_tpmgr_destroy( pjsip_tpmgr *mgr ) { pj_hash_iterator_t itr_val; pj_hash_iterator_t *itr; pjsip_tpfactory *factory; pjsip_endpoint *endpt = mgr->endpt; PJ_LOG(5, (THIS_FILE, "Destroying transport manager")); pj_lock_acquire(mgr->lock); /* * Destroy all transports in the hash table. */ itr = pj_hash_first(mgr->table, &itr_val); while (itr != NULL) { pj_hash_iterator_t *next; pjsip_transport *transport; transport = (pjsip_transport*) pj_hash_this(mgr->table, itr); next = pj_hash_next(mgr->table, itr); destroy_transport(mgr, transport); itr = next; } /* * Destroy transports in the list. */ if (!pj_list_empty(&mgr->tp_list)) { transport *tp_iter = mgr->tp_list.next; while (tp_iter != &mgr->tp_list) { transport *next = tp_iter->next; destroy_transport(mgr, tp_iter->tp); tp_iter = next; } } /* * Destroy all factories/listeners. */ factory = mgr->factory_list.next; while (factory != &mgr->factory_list) { pjsip_tpfactory *next = factory->next; factory->destroy(factory); factory = next; } pj_lock_release(mgr->lock); #if defined(PJ_DEBUG) && PJ_DEBUG!=0 /* If you encounter assert error on this line, it means there are * leakings in transmit data (i.e. some transmit data have not been * destroyed). */ //pj_assert(pj_atomic_get(mgr->tdata_counter) == 0); if (pj_atomic_get(mgr->tdata_counter) != 0) { PJ_LOG(3,(THIS_FILE, "Warning: %d transmit buffer(s) not freed!", pj_atomic_get(mgr->tdata_counter))); } #endif /* * Destroy any dangling transmit buffer. */ if (!pj_list_empty(&mgr->tdata_list)) { pjsip_tx_data *tdata = mgr->tdata_list.next; while (tdata != &mgr->tdata_list) { pjsip_tx_data *next = tdata->next; tx_data_destroy(tdata); tdata = next; } PJ_LOG(3,(THIS_FILE, "Cleaned up dangling transmit buffer(s).")); } #if defined(PJ_DEBUG) && PJ_DEBUG!=0 pj_atomic_destroy(mgr->tdata_counter); #endif pj_lock_destroy(mgr->lock); /* Unregister mod_msg_print. */ if (mod_msg_print.id != -1) { pjsip_endpt_unregister_module(endpt, &mod_msg_print); } return PJ_SUCCESS; } /* * pjsip_tpmgr_receive_packet() * * Called by tranports when they receive a new packet. */ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, pjsip_rx_data *rdata) { pjsip_transport *tr = rdata->tp_info.transport; char *current_pkt; pj_size_t remaining_len; pj_size_t total_processed = 0; /* Check size. */ pj_assert(rdata->pkt_info.len > 0); if (rdata->pkt_info.len <= 0) return -1; current_pkt = rdata->pkt_info.packet; remaining_len = rdata->pkt_info.len; tr->last_recv_len = rdata->pkt_info.len; pj_get_timestamp(&tr->last_recv_ts); /* Must NULL terminate buffer. This is the requirement of the * parser etc. */ current_pkt[remaining_len] = '\0'; /* Process all message fragments. */ while (remaining_len > 0) { pjsip_msg *msg; char *p, *end; char saved; pj_size_t msg_fragment_size; /* Skip leading newlines as pjsip_find_msg() currently can't * handle leading newlines. */ for (p=current_pkt, end=p+remaining_len; p!=end; ++p) { if (*p != '\r' && *p != '\n') break; } if (p!=current_pkt) { remaining_len -= (p - current_pkt); total_processed += (p - current_pkt); /* Notify application about the dropped newlines */ if (mgr->tp_drop_data_cb) { pjsip_tp_dropped_data dd; pj_bzero(&dd, sizeof(dd)); dd.tp = tr; dd.data = current_pkt; dd.len = p - current_pkt; dd.status = PJ_EIGNORED; (*mgr->tp_drop_data_cb)(&dd); } current_pkt = p; if (remaining_len == 0) { return total_processed; } } /* Initialize default fragment size. */ msg_fragment_size = remaining_len; /* Clear and init msg_info in rdata. * Endpoint might inspect the values there when we call the callback * to report some errors. */ pj_bzero(&rdata->msg_info, sizeof(rdata->msg_info)); pj_list_init(&rdata->msg_info.parse_err); rdata->msg_info.msg_buf = current_pkt; rdata->msg_info.len = (int)remaining_len; /* For TCP transport, check if the whole message has been received. */ if ((tr->flag & PJSIP_TRANSPORT_DATAGRAM) == 0) { pj_status_t msg_status; msg_status = pjsip_find_msg(current_pkt, remaining_len, PJ_FALSE, &msg_fragment_size); if (msg_status != PJ_SUCCESS) { if (remaining_len == PJSIP_MAX_PKT_LEN) { mgr->on_rx_msg(mgr->endpt, PJSIP_ERXOVERFLOW, rdata); /* Notify application about the message overflow */ if (mgr->tp_drop_data_cb) { pjsip_tp_dropped_data dd; pj_bzero(&dd, sizeof(dd)); dd.tp = tr; dd.data = current_pkt; dd.len = msg_fragment_size; dd.status = PJSIP_ERXOVERFLOW; (*mgr->tp_drop_data_cb)(&dd); } /* Exhaust all data. */ return rdata->pkt_info.len; } else { /* Not enough data in packet. */ return total_processed; } } } /* Update msg_info. */ rdata->msg_info.len = (int)msg_fragment_size; /* Null terminate packet */ saved = current_pkt[msg_fragment_size]; current_pkt[msg_fragment_size] = '\0'; /* Parse the message. */ rdata->msg_info.msg = msg = pjsip_parse_rdata( current_pkt, msg_fragment_size, rdata); /* Restore null termination */ current_pkt[msg_fragment_size] = saved; /* Check for parsing syntax error */ if (msg==NULL || !pj_list_empty(&rdata->msg_info.parse_err)) { pjsip_parser_err_report *err; char buf[128]; pj_str_t tmp; /* Gather syntax error information */ tmp.ptr = buf; tmp.slen = 0; err = rdata->msg_info.parse_err.next; while (err != &rdata->msg_info.parse_err) { int len; len = pj_ansi_snprintf(tmp.ptr+tmp.slen, sizeof(buf)-tmp.slen, ": %s exception when parsing '%.*s' " "header on line %d col %d", pj_exception_id_name(err->except_code), (int)err->hname.slen, err->hname.ptr, err->line, err->col); if (len > 0 && len < (int) (sizeof(buf)-tmp.slen)) { tmp.slen += len; } err = err->next; } /* Only print error message if there's error. * Sometimes we receive blank packets (packets with only CRLF) * which were sent to keep NAT bindings. */ if (tmp.slen) { PJ_LOG(1, (THIS_FILE, "Error processing %d bytes packet from %s %s:%d %.*s:\n" "%.*s\n" "-- end of packet.", msg_fragment_size, rdata->tp_info.transport->type_name, rdata->pkt_info.src_name, rdata->pkt_info.src_port, (int)tmp.slen, tmp.ptr, (int)msg_fragment_size, rdata->msg_info.msg_buf)); } /* Notify application about the dropped data (syntax error) */ if (tmp.slen && mgr->tp_drop_data_cb) { pjsip_tp_dropped_data dd; pj_bzero(&dd, sizeof(dd)); dd.tp = tr; dd.data = current_pkt; dd.len = msg_fragment_size; dd.status = PJSIP_EINVALIDMSG; (*mgr->tp_drop_data_cb)(&dd); if (dd.len > 0 && dd.len < msg_fragment_size) msg_fragment_size = dd.len; } goto finish_process_fragment; } /* Perform basic header checking. */ if (rdata->msg_info.cid == NULL || rdata->msg_info.cid->id.slen == 0 || rdata->msg_info.from == NULL || rdata->msg_info.to == NULL || rdata->msg_info.via == NULL || rdata->msg_info.cseq == NULL) { mgr->on_rx_msg(mgr->endpt, PJSIP_EMISSINGHDR, rdata); goto finish_process_fragment; } /* For request: */ if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) { /* always add received parameter to the via. */ pj_strdup2(rdata->tp_info.pool, &rdata->msg_info.via->recvd_param, rdata->pkt_info.src_name); /* RFC 3581: * If message contains "rport" param, put the received port there. */ if (rdata->msg_info.via->rport_param == 0) { rdata->msg_info.via->rport_param = rdata->pkt_info.src_port; } } else { /* Drop malformed responses */ if (rdata->msg_info.msg->line.status.code < 100 || rdata->msg_info.msg->line.status.code >= 700) { mgr->on_rx_msg(mgr->endpt, PJSIP_EINVALIDSTATUS, rdata); goto finish_process_fragment; } } /* Drop response message if it has more than one Via. */ /* This is wrong. Proxy DOES receive responses with multiple * Via headers! Thanks Aldo for pointing * this out. if (msg->type == PJSIP_RESPONSE_MSG) { pjsip_hdr *hdr; hdr = (pjsip_hdr*)rdata->msg_info.via->next; if (hdr != &msg->hdr) { hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr); if (hdr) { mgr->on_rx_msg(mgr->endpt, PJSIP_EMULTIPLEVIA, rdata); goto finish_process_fragment; } } } */ /* Call the transport manager's upstream message callback. */ mgr->on_rx_msg(mgr->endpt, PJ_SUCCESS, rdata); finish_process_fragment: total_processed += msg_fragment_size; current_pkt += msg_fragment_size; remaining_len -= msg_fragment_size; } /* while (rdata->pkt_info.len > 0) */ return total_processed; } /* * pjsip_tpmgr_acquire_transport() * * Get transport suitable to communicate to remote. Create a new one * if necessary. */ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr, pjsip_transport_type_e type, const pj_sockaddr_t *remote, int addr_len, const pjsip_tpselector *sel, pjsip_transport **tp) { return pjsip_tpmgr_acquire_transport2(mgr, type, remote, addr_len, sel, NULL, tp); } /* * pjsip_tpmgr_acquire_transport2() * * Get transport suitable to communicate to remote. Create a new one * if necessary. */ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr, pjsip_transport_type_e type, const pj_sockaddr_t *remote, int addr_len, const pjsip_tpselector *sel, pjsip_tx_data *tdata, pjsip_transport **tp) { pjsip_tpfactory *factory; pj_status_t status; TRACE_((THIS_FILE,"Acquiring transport type=%s, remote=%s:%d", pjsip_transport_get_type_name(type), addr_string(remote), pj_sockaddr_get_port(remote))); pj_lock_acquire(mgr->lock); /* If transport is specified, then just use it if it is suitable * for the destination. */ if (sel && sel->type == PJSIP_TPSELECTOR_TRANSPORT && sel->u.transport) { pjsip_transport *seltp = sel->u.transport; /* See if the transport is (not) suitable */ if (seltp->key.type != type) { pj_lock_release(mgr->lock); return PJSIP_ETPNOTSUITABLE; } /* We could also verify that the destination address is reachable * from this transport (i.e. both are equal), but if application * has requested a specific transport to be used, assume that * it knows what to do. * * In other words, I don't think destination verification is a good * idea for now. */ /* Transport looks to be suitable to use, so just use it. */ pjsip_transport_add_ref(seltp); pj_lock_release(mgr->lock); *tp = seltp; TRACE_((THIS_FILE, "Transport %s acquired", seltp->obj_name)); return PJ_SUCCESS; } else { /* * This is the "normal" flow, where application doesn't specify * specific transport to be used to send message to. * In this case, lookup the transport from the hash table. */ pjsip_transport_key key; int key_len; pjsip_transport *transport; /* If listener is specified, verify that the listener type matches * the destination type. */ if (sel && sel->type == PJSIP_TPSELECTOR_LISTENER && sel->u.listener) { if (sel->u.listener->type != type) { pj_lock_release(mgr->lock); return PJSIP_ETPNOTSUITABLE; } } pj_bzero(&key, sizeof(key)); key_len = sizeof(key.type) + addr_len; /* First try to get exact destination. */ key.type = type; pj_memcpy(&key.rem_addr, remote, addr_len); transport = (pjsip_transport*) pj_hash_get(mgr->table, &key, key_len, NULL); if (transport == NULL) { unsigned flag = pjsip_transport_get_flag_from_type(type); const pj_sockaddr *remote_addr = (const pj_sockaddr*)remote; /* Ignore address for loop transports. */ if (type == PJSIP_TRANSPORT_LOOP || type == PJSIP_TRANSPORT_LOOP_DGRAM) { pj_sockaddr *addr = &key.rem_addr; pj_bzero(addr, addr_len); key_len = sizeof(key.type) + addr_len; transport = (pjsip_transport*) pj_hash_get(mgr->table, &key, key_len, NULL); } /* For datagram transports, try lookup with zero address. */ else if (flag & PJSIP_TRANSPORT_DATAGRAM) { pj_sockaddr *addr = &key.rem_addr; pj_bzero(addr, addr_len); addr->addr.sa_family = remote_addr->addr.sa_family; key_len = sizeof(key.type) + addr_len; transport = (pjsip_transport*) pj_hash_get(mgr->table, &key, key_len, NULL); } } /* If transport is found and listener is specified, verify listener */ else if (sel && sel->type == PJSIP_TPSELECTOR_LISTENER && sel->u.listener && transport->factory != sel->u.listener) { transport = NULL; /* This will cause a new transport to be created which will be a * 'duplicate' of the existing transport (same type & remote addr, * but different factory). Any future hash lookup will return * the new one, and eventually the old one will still be freed * (by application or #1774). */ } if (transport!=NULL && !transport->is_shutdown) { /* * Transport found! */ pjsip_transport_add_ref(transport); pj_lock_release(mgr->lock); *tp = transport; TRACE_((THIS_FILE, "Transport %s acquired", transport->obj_name)); return PJ_SUCCESS; } /* * Transport not found! * So we need to create one, find factory that can create * such transport. */ if (sel && sel->type == PJSIP_TPSELECTOR_LISTENER && sel->u.listener) { /* Application has requested that a specific listener is to * be used. */ /* Verify that the listener type matches the destination type */ if (sel->u.listener->type != type) { pj_lock_release(mgr->lock); return PJSIP_ETPNOTSUITABLE; } /* We'll use this listener to create transport */ factory = sel->u.listener; /* Verify if listener is still valid */ if (!pjsip_tpmgr_is_tpfactory_valid(mgr, factory)) { pj_lock_release(mgr->lock); PJ_LOG(3,(THIS_FILE, "Specified factory for creating " "transport is not found")); return PJ_ENOTFOUND; } } else { /* Find factory with type matches the destination type */ factory = mgr->factory_list.next; while (factory != &mgr->factory_list) { if (factory->type == type) break; factory = factory->next; } if (factory == &mgr->factory_list) { /* No factory can create the transport! */ pj_lock_release(mgr->lock); TRACE_((THIS_FILE, "No suitable factory was found either")); return PJSIP_EUNSUPTRANSPORT; } } } TRACE_((THIS_FILE, "Creating new transport from factory")); /* Request factory to create transport. */ if (factory->create_transport2) { status = factory->create_transport2(factory, mgr, mgr->endpt, (const pj_sockaddr*) remote, addr_len, tdata, tp); } else { status = factory->create_transport(factory, mgr, mgr->endpt, (const pj_sockaddr*) remote, addr_len, tp); } if (status == PJ_SUCCESS) { PJ_ASSERT_ON_FAIL(tp!=NULL, {pj_lock_release(mgr->lock); return PJ_EBUG;}); pjsip_transport_add_ref(*tp); (*tp)->factory = factory; } pj_lock_release(mgr->lock); return status; } /** * Dump transport info. */ PJ_DEF(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr) { #if PJ_LOG_MAX_LEVEL >= 3 pj_hash_iterator_t itr_val; pj_hash_iterator_t *itr; pjsip_tpfactory *factory; pj_lock_acquire(mgr->lock); #if defined(PJ_DEBUG) && PJ_DEBUG!=0 PJ_LOG(3,(THIS_FILE, " Outstanding transmit buffers: %d", pj_atomic_get(mgr->tdata_counter))); #endif PJ_LOG(3, (THIS_FILE, " Dumping listeners:")); factory = mgr->factory_list.next; while (factory != &mgr->factory_list) { PJ_LOG(3, (THIS_FILE, " %s %s:%.*s:%d", factory->obj_name, factory->type_name, (int)factory->addr_name.host.slen, factory->addr_name.host.ptr, (int)factory->addr_name.port)); factory = factory->next; } itr = pj_hash_first(mgr->table, &itr_val); if (itr) { PJ_LOG(3, (THIS_FILE, " Dumping transports:")); do { pjsip_transport *t = (pjsip_transport*) pj_hash_this(mgr->table, itr); PJ_LOG(3, (THIS_FILE, " %s %s (refcnt=%d%s)", t->obj_name, t->info, pj_atomic_get(t->ref_cnt), (t->idle_timer.id ? " [idle]" : ""))); itr = pj_hash_next(mgr->table, itr); } while (itr); } pj_lock_release(mgr->lock); #else PJ_UNUSED_ARG(mgr); #endif } /** * Set callback of global transport state notification. */ PJ_DEF(pj_status_t) pjsip_tpmgr_set_state_cb(pjsip_tpmgr *mgr, pjsip_tp_state_callback cb) { PJ_ASSERT_RETURN(mgr, PJ_EINVAL); mgr->tp_state_cb = cb; return PJ_SUCCESS; } /** * Get callback of global transport state notification. */ PJ_DEF(pjsip_tp_state_callback) pjsip_tpmgr_get_state_cb( const pjsip_tpmgr *mgr) { PJ_ASSERT_RETURN(mgr, NULL); return mgr->tp_state_cb; } /** * Allocate and init transport data. */ static void init_tp_data(pjsip_transport *tp) { transport_data *tp_data; pj_assert(tp && !tp->data); tp_data = PJ_POOL_ZALLOC_T(tp->pool, transport_data); pj_list_init(&tp_data->st_listeners); pj_list_init(&tp_data->st_listeners_empty); tp->data = tp_data; } static void tp_state_callback(pjsip_transport *tp, pjsip_transport_state state, const pjsip_transport_state_info *info) { transport_data *tp_data; pj_lock_acquire(tp->lock); tp_data = (transport_data*)tp->data; /* Notify the transport state listeners, if any. */ if (!tp_data || pj_list_empty(&tp_data->st_listeners)) { goto on_return; } else { pjsip_transport_state_info st_info; tp_state_listener *st_listener = tp_data->st_listeners.next; /* As we need to put the user data into the transport state info, * let's use a copy of transport state info. */ pj_memcpy(&st_info, info, sizeof(st_info)); while (st_listener != &tp_data->st_listeners) { st_info.user_data = st_listener->user_data; (*st_listener->cb)(tp, state, &st_info); st_listener = st_listener->next; } } on_return: pj_lock_release(tp->lock); } /** * Add a listener to the specified transport for transport state notification. */ PJ_DEF(pj_status_t) pjsip_transport_add_state_listener ( pjsip_transport *tp, pjsip_tp_state_callback cb, void *user_data, pjsip_tp_state_listener_key **key) { transport_data *tp_data; tp_state_listener *entry; PJ_ASSERT_RETURN(tp && cb && key, PJ_EINVAL); pj_lock_acquire(tp->lock); /* Init transport data, if it hasn't */ if (!tp->data) init_tp_data(tp); tp_data = (transport_data*)tp->data; /* Init the new listener entry. Use available empty slot, if any, * otherwise allocate it using the transport pool. */ if (!pj_list_empty(&tp_data->st_listeners_empty)) { entry = tp_data->st_listeners_empty.next; pj_list_erase(entry); } else { entry = PJ_POOL_ZALLOC_T(tp->pool, tp_state_listener); } entry->cb = cb; entry->user_data = user_data; /* Add the new listener entry to the listeners list */ pj_list_push_back(&tp_data->st_listeners, entry); *key = entry; pj_lock_release(tp->lock); return PJ_SUCCESS; } /** * Remove a listener from the specified transport for transport state * notification. */ PJ_DEF(pj_status_t) pjsip_transport_remove_state_listener ( pjsip_transport *tp, pjsip_tp_state_listener_key *key, const void *user_data) { transport_data *tp_data; tp_state_listener *entry; PJ_ASSERT_RETURN(tp && key, PJ_EINVAL); pj_lock_acquire(tp->lock); tp_data = (transport_data*)tp->data; /* Transport data is NULL or no registered listener? */ if (!tp_data || pj_list_empty(&tp_data->st_listeners)) { pj_lock_release(tp->lock); return PJ_ENOTFOUND; } entry = (tp_state_listener*)key; /* Validate the user data */ if (entry->user_data != user_data) { pj_assert(!"Invalid transport state listener key"); pj_lock_release(tp->lock); return PJ_EBUG; } /* Reset the entry and move it to the empty list */ entry->cb = NULL; entry->user_data = NULL; pj_list_erase(entry); pj_list_push_back(&tp_data->st_listeners_empty, entry); pj_lock_release(tp->lock); return PJ_SUCCESS; } /* * Set callback of data dropping. */ PJ_DEF(pj_status_t) pjsip_tpmgr_set_drop_data_cb(pjsip_tpmgr *mgr, pjsip_tp_on_rx_dropped_cb cb) { PJ_ASSERT_RETURN(mgr, PJ_EINVAL); mgr->tp_drop_data_cb = cb; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_transport_loop.c ================================================ /* $Id: sip_transport_loop.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #define ADDR_LOOP "128.0.0.1" #define ADDR_LOOP_DGRAM "129.0.0.1" /** This structure describes incoming packet. */ struct recv_list { PJ_DECL_LIST_MEMBER(struct recv_list); pjsip_rx_data rdata; }; /** This structure is used to keep delayed send failure. */ struct send_list { PJ_DECL_LIST_MEMBER(struct send_list); pj_time_val sent_time; pj_ssize_t sent; pjsip_tx_data *tdata; void *token; void (*callback)(pjsip_transport*, void*, pj_ssize_t); }; /** This structure describes the loop transport. */ struct loop_transport { pjsip_transport base; pj_pool_t *pool; pj_thread_t *thread; pj_bool_t thread_quit_flag; pj_bool_t discard; int fail_mode; unsigned recv_delay; unsigned send_delay; struct recv_list recv_list; struct send_list send_list; }; /* Helper function to create "incoming" packet */ static struct recv_list *create_incoming_packet( struct loop_transport *loop, pjsip_tx_data *tdata ) { pj_pool_t *pool; struct recv_list *pkt; pool = pjsip_endpt_create_pool(loop->base.endpt, "rdata", PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC+5); if (!pool) return NULL; pkt = PJ_POOL_ZALLOC_T(pool, struct recv_list); /* Initialize rdata. */ pkt->rdata.tp_info.pool = pool; pkt->rdata.tp_info.transport = &loop->base; /* Copy the packet. */ pj_memcpy(pkt->rdata.pkt_info.packet, tdata->buf.start, tdata->buf.cur - tdata->buf.start); pkt->rdata.pkt_info.len = tdata->buf.cur - tdata->buf.start; /* the source address */ pkt->rdata.pkt_info.src_addr.addr.sa_family = pj_AF_INET(); /* "Source address" info. */ pkt->rdata.pkt_info.src_addr_len = sizeof(pj_sockaddr_in); if (loop->base.key.type == PJSIP_TRANSPORT_LOOP) { pj_ansi_strcpy(pkt->rdata.pkt_info.src_name, ADDR_LOOP); } else { pj_ansi_strcpy(pkt->rdata.pkt_info.src_name, ADDR_LOOP_DGRAM); } pkt->rdata.pkt_info.src_port = loop->base.local_name.port; /* When do we need to "deliver" this packet. */ pj_gettimeofday(&pkt->rdata.pkt_info.timestamp); pkt->rdata.pkt_info.timestamp.msec += loop->recv_delay; pj_time_val_normalize(&pkt->rdata.pkt_info.timestamp); /* Done. */ return pkt; } /* Helper function to add pending notification callback. */ static pj_status_t add_notification( struct loop_transport *loop, pjsip_tx_data *tdata, pj_ssize_t sent, void *token, void (*callback)(pjsip_transport*, void*, pj_ssize_t)) { struct send_list *sent_status; pjsip_tx_data_add_ref(tdata); pj_lock_acquire(tdata->lock); sent_status = PJ_POOL_ALLOC_T(tdata->pool, struct send_list); pj_lock_release(tdata->lock); sent_status->sent = sent; sent_status->tdata = tdata; sent_status->token = token; sent_status->callback = callback; pj_gettimeofday(&sent_status->sent_time); sent_status->sent_time.msec += loop->send_delay; pj_time_val_normalize(&sent_status->sent_time); pj_lock_acquire(loop->base.lock); pj_list_push_back(&loop->send_list, sent_status); pj_lock_release(loop->base.lock); return PJ_SUCCESS; } /* Handler for sending outgoing message; called by transport manager. */ static pj_status_t loop_send_msg( pjsip_transport *tp, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, pjsip_transport_callback cb) { struct loop_transport *loop = (struct loop_transport*)tp; struct recv_list *recv_pkt; PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP || tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL); PJ_UNUSED_ARG(rem_addr); PJ_UNUSED_ARG(addr_len); /* Need to send failure? */ if (loop->fail_mode) { if (loop->send_delay == 0) { return PJ_STATUS_FROM_OS(OSERR_ECONNRESET); } else { add_notification(loop, tdata, -PJ_STATUS_FROM_OS(OSERR_ECONNRESET), token, cb); return PJ_EPENDING; } } /* Discard any packets? */ if (loop->discard) return PJ_SUCCESS; /* Create rdata for the "incoming" packet. */ recv_pkt = create_incoming_packet(loop, tdata); if (!recv_pkt) return PJ_ENOMEM; /* If delay is not configured, deliver this packet now! */ if (loop->recv_delay == 0) { pj_ssize_t size_eaten; size_eaten = pjsip_tpmgr_receive_packet( loop->base.tpmgr, &recv_pkt->rdata); pj_assert(size_eaten == recv_pkt->rdata.pkt_info.len); pjsip_endpt_release_pool(loop->base.endpt, recv_pkt->rdata.tp_info.pool); } else { /* Otherwise if delay is configured, add the "packet" to the * receive list to be processed by worker thread. */ pj_lock_acquire(loop->base.lock); pj_list_push_back(&loop->recv_list, recv_pkt); pj_lock_release(loop->base.lock); } if (loop->send_delay != 0) { add_notification(loop, tdata, tdata->buf.cur - tdata->buf.start, token, cb); return PJ_EPENDING; } else { return PJ_SUCCESS; } } /* Handler to destroy the transport; called by transport manager */ static pj_status_t loop_destroy(pjsip_transport *tp) { struct loop_transport *loop = (struct loop_transport*)tp; PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP || tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL); loop->thread_quit_flag = 1; /* Unlock transport mutex before joining thread. */ /// This raised assertion failed "mutex->owner == pj_thread_this()", /// where mutex->owner==NULL //pj_lock_release(tp->lock); pj_thread_join(loop->thread); pj_thread_destroy(loop->thread); /* Clear pending send notifications. */ while (!pj_list_empty(&loop->send_list)) { struct send_list *node = loop->send_list.next; /* Notify callback. */ if (node->callback) { (*node->callback)(&loop->base, node->token, -PJSIP_ESHUTDOWN); } pj_list_erase(node); pjsip_tx_data_dec_ref(node->tdata); } /* Clear "incoming" packets in the queue. */ while (!pj_list_empty(&loop->recv_list)) { struct recv_list *node = loop->recv_list.next; pj_list_erase(node); pjsip_endpt_release_pool(loop->base.endpt, node->rdata.tp_info.pool); } /* Self destruct.. heheh.. */ pj_lock_destroy(loop->base.lock); pj_atomic_destroy(loop->base.ref_cnt); pjsip_endpt_release_pool(loop->base.endpt, loop->base.pool); return PJ_SUCCESS; } /* Worker thread for loop transport. */ static int loop_transport_worker_thread(void *arg) { struct loop_transport *loop = (struct loop_transport*) arg; struct recv_list r; struct send_list s; pj_list_init(&r); pj_list_init(&s); while (!loop->thread_quit_flag) { pj_time_val now; pj_thread_sleep(1); pj_gettimeofday(&now); pj_lock_acquire(loop->base.lock); /* Move expired send notification to local list. */ while (!pj_list_empty(&loop->send_list)) { struct send_list *node = loop->send_list.next; /* Break when next node time is greater than now. */ if (PJ_TIME_VAL_GTE(node->sent_time, now)) break; /* Delete this from the list. */ pj_list_erase(node); /* Add to local list. */ pj_list_push_back(&s, node); } /* Move expired "incoming" packet to local list. */ while (!pj_list_empty(&loop->recv_list)) { struct recv_list *node = loop->recv_list.next; /* Break when next node time is greater than now. */ if (PJ_TIME_VAL_GTE(node->rdata.pkt_info.timestamp, now)) break; /* Delete this from the list. */ pj_list_erase(node); /* Add to local list. */ pj_list_push_back(&r, node); } pj_lock_release(loop->base.lock); /* Process send notification and incoming packet notification * without holding down the loop's mutex. */ while (!pj_list_empty(&s)) { struct send_list *node = s.next; pj_list_erase(node); /* Notify callback. */ if (node->callback) { (*node->callback)(&loop->base, node->token, node->sent); } /* Decrement tdata reference counter. */ pjsip_tx_data_dec_ref(node->tdata); } /* Process "incoming" packet. */ while (!pj_list_empty(&r)) { struct recv_list *node = r.next; pj_ssize_t size_eaten; pj_list_erase(node); /* Notify transport manager about the "incoming packet" */ size_eaten = pjsip_tpmgr_receive_packet(loop->base.tpmgr, &node->rdata); /* Must "eat" all the packets. */ pj_assert(size_eaten == node->rdata.pkt_info.len); /* Done. */ pjsip_endpt_release_pool(loop->base.endpt, node->rdata.tp_info.pool); } } return 0; } /* Start loop transport. */ PJ_DEF(pj_status_t) pjsip_loop_start( pjsip_endpoint *endpt, pjsip_transport **transport) { pj_pool_t *pool; struct loop_transport *loop; pj_status_t status; /* Create pool. */ pool = pjsip_endpt_create_pool(endpt, "loop", 4000, 4000); if (!pool) return PJ_ENOMEM; /* Create the loop structure. */ loop = PJ_POOL_ZALLOC_T(pool, struct loop_transport); /* Initialize transport properties. */ pj_ansi_snprintf(loop->base.obj_name, sizeof(loop->base.obj_name), "loop%p", loop); loop->base.pool = pool; status = pj_atomic_create(pool, 0, &loop->base.ref_cnt); if (status != PJ_SUCCESS) goto on_error; status = pj_lock_create_recursive_mutex(pool, "loop", &loop->base.lock); if (status != PJ_SUCCESS) goto on_error; loop->base.key.type = PJSIP_TRANSPORT_LOOP_DGRAM; //loop->base.key.rem_addr.sa_family = pj_AF_INET(); loop->base.type_name = "LOOP-DGRAM"; loop->base.info = "LOOP-DGRAM"; loop->base.flag = PJSIP_TRANSPORT_DATAGRAM; loop->base.local_name.host = pj_str(ADDR_LOOP_DGRAM); loop->base.local_name.port = pjsip_transport_get_default_port_for_type((pjsip_transport_type_e) loop->base.key.type); loop->base.addr_len = sizeof(pj_sockaddr_in); loop->base.dir = PJSIP_TP_DIR_NONE; loop->base.endpt = endpt; loop->base.tpmgr = pjsip_endpt_get_tpmgr(endpt); loop->base.send_msg = &loop_send_msg; loop->base.destroy = &loop_destroy; pj_list_init(&loop->recv_list); pj_list_init(&loop->send_list); /* Create worker thread. */ status = pj_thread_create(pool, "loop", &loop_transport_worker_thread, loop, 0, PJ_THREAD_SUSPENDED, &loop->thread); if (status != PJ_SUCCESS) goto on_error; /* Register to transport manager. */ status = pjsip_transport_register( loop->base.tpmgr, &loop->base); if (status != PJ_SUCCESS) goto on_error; /* Start the thread. */ status = pj_thread_resume(loop->thread); if (status != PJ_SUCCESS) goto on_error; /* * Done. */ if (transport) *transport = &loop->base; return PJ_SUCCESS; on_error: if (loop->base.lock) pj_lock_destroy(loop->base.lock); if (loop->thread) pj_thread_destroy(loop->thread); if (loop->base.ref_cnt) pj_atomic_destroy(loop->base.ref_cnt); pjsip_endpt_release_pool(endpt, loop->pool); return status; } PJ_DEF(pj_status_t) pjsip_loop_set_discard( pjsip_transport *tp, pj_bool_t discard, pj_bool_t *prev_value ) { struct loop_transport *loop = (struct loop_transport*)tp; PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP || tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL); if (prev_value) *prev_value = loop->discard; loop->discard = discard; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_loop_set_failure( pjsip_transport *tp, int fail_flag, int *prev_value ) { struct loop_transport *loop = (struct loop_transport*)tp; PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP || tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL); if (prev_value) *prev_value = loop->fail_mode; loop->fail_mode = fail_flag; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_loop_set_recv_delay( pjsip_transport *tp, unsigned delay, unsigned *prev_value) { struct loop_transport *loop = (struct loop_transport*)tp; PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP || tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL); if (prev_value) *prev_value = loop->recv_delay; loop->recv_delay = delay; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_loop_set_send_callback_delay( pjsip_transport *tp, unsigned delay, unsigned *prev_value) { struct loop_transport *loop = (struct loop_transport*)tp; PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP || tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL); if (prev_value) *prev_value = loop->send_delay; loop->send_delay = delay; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_loop_set_delay( pjsip_transport *tp, unsigned delay ) { struct loop_transport *loop = (struct loop_transport*)tp; PJ_ASSERT_RETURN(tp && (tp->key.type == PJSIP_TRANSPORT_LOOP || tp->key.type == PJSIP_TRANSPORT_LOOP_DGRAM), PJ_EINVAL); loop->recv_delay = delay; loop->send_delay = delay; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_transport_tcp.c ================================================ /* $Id: sip_transport_tcp.c 4506 2013-04-26 06:01:43Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include /* Only declare the API if PJ_HAS_TCP is true */ #if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0 #define THIS_FILE "sip_transport_tcp.c" #define MAX_ASYNC_CNT 16 #define POOL_LIS_INIT 512 #define POOL_LIS_INC 512 #define POOL_TP_INIT 512 #define POOL_TP_INC 512 struct tcp_listener; struct tcp_transport; /* * This is the TCP listener, which is a "descendant" of pjsip_tpfactory (the * SIP transport factory). */ struct tcp_listener { pjsip_tpfactory factory; pj_bool_t is_registered; pjsip_endpoint *endpt; pjsip_tpmgr *tpmgr; pj_activesock_t *asock; pj_sockaddr bound_addr; pj_qos_type qos_type; pj_qos_params qos_params; pj_sockopt_params sockopt_params; /* Group lock to be used by TCP listener and ioqueue key */ pj_grp_lock_t *grp_lock; }; /* * This structure is used to keep delayed transmit operation in a list. * A delayed transmission occurs when application sends tx_data when * the TCP connect/establishment is still in progress. These delayed * transmission will be "flushed" once the socket is connected (either * successfully or with errors). */ struct delayed_tdata { PJ_DECL_LIST_MEMBER(struct delayed_tdata); pjsip_tx_data_op_key *tdata_op_key; pj_time_val timeout; }; /* * This structure describes the TCP transport, and it's descendant of * pjsip_transport. */ struct tcp_transport { pjsip_transport base; pj_bool_t is_server; /* Do not save listener instance in the transport, because * listener might be destroyed during transport's lifetime. * See http://trac.pjsip.org/repos/ticket/491 struct tcp_listener *listener; */ pj_bool_t is_registered; pj_bool_t is_closing; pj_status_t close_reason; pj_sock_t sock; pj_activesock_t *asock; pj_bool_t has_pending_connect; /* Keep-alive timer. */ pj_timer_entry ka_timer; pj_time_val last_activity; pjsip_tx_data_op_key ka_op_key; pj_str_t ka_pkt; /* TCP transport can only have one rdata! * Otherwise chunks of incoming PDU may be received on different * buffer. */ pjsip_rx_data rdata; /* Pending transmission list. */ struct delayed_tdata delayed_list; /* Group lock to be used by TCP transport and ioqueue key */ pj_grp_lock_t *grp_lock; }; /**************************************************************************** * PROTOTYPES */ /* This callback is called when pending accept() operation completes. */ static pj_bool_t on_accept_complete(pj_activesock_t *asock, pj_sock_t newsock, const pj_sockaddr_t *src_addr, int src_addr_len); /* This callback is called by transport manager to destroy listener */ static pj_status_t lis_destroy(pjsip_tpfactory *factory); /* Clean up listener resources (group lock handler) */ static void lis_on_destroy(void *arg); /* This callback is called by transport manager to create transport */ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, pjsip_tpmgr *mgr, pjsip_endpoint *endpt, const pj_sockaddr *rem_addr, int addr_len, pjsip_transport **transport); /* Common function to create and initialize transport */ static pj_status_t tcp_create(struct tcp_listener *listener, pj_pool_t *pool, pj_sock_t sock, pj_bool_t is_server, const pj_sockaddr *local, const pj_sockaddr *remote, struct tcp_transport **p_tcp); static void tcp_perror(const char *sender, const char *title, pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(3,(sender, "%s: %s [code=%d]", title, errmsg, status)); } static void sockaddr_to_host_port( pj_pool_t *pool, pjsip_host_port *host_port, const pj_sockaddr *addr ) { host_port->host.ptr = (char*) pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+4); pj_sockaddr_print(addr, host_port->host.ptr, PJ_INET6_ADDRSTRLEN+4, 0); host_port->host.slen = pj_ansi_strlen(host_port->host.ptr); host_port->port = pj_sockaddr_get_port(addr); } static void tcp_init_shutdown(struct tcp_transport *tcp, pj_status_t status) { pjsip_tp_state_callback state_cb; if (tcp->close_reason == PJ_SUCCESS) tcp->close_reason = status; if (tcp->base.is_shutdown || tcp->base.is_destroying) return; /* Prevent immediate transport destroy by application, as transport * state notification callback may be stacked and transport instance * must remain valid at any point in the callback. */ pjsip_transport_add_ref(&tcp->base); /* Notify application of transport disconnected state */ state_cb = pjsip_tpmgr_get_state_cb(tcp->base.tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pj_bzero(&state_info, sizeof(state_info)); state_info.status = tcp->close_reason; (*state_cb)(&tcp->base, PJSIP_TP_STATE_DISCONNECTED, &state_info); } /* check again */ if (tcp->base.is_shutdown || tcp->base.is_destroying) { pjsip_transport_dec_ref(&tcp->base); return; } /* We can not destroy the transport since high level objects may * still keep reference to this transport. So we can only * instruct transport manager to gracefully start the shutdown * procedure for this transport. */ pjsip_transport_shutdown(&tcp->base); /* Now, it is ok to destroy the transport. */ pjsip_transport_dec_ref(&tcp->base); } /* * Initialize pjsip_tcp_transport_cfg structure with default values. */ PJ_DEF(void) pjsip_tcp_transport_cfg_default(pjsip_tcp_transport_cfg *cfg, int af) { pj_bzero(cfg, sizeof(*cfg)); cfg->af = af; pj_sockaddr_init(cfg->af, &cfg->bind_addr, NULL, 0); cfg->async_cnt = 1; cfg->reuse_addr = PJSIP_TCP_TRANSPORT_REUSEADDR; } /**************************************************************************** * The TCP listener/transport factory. */ /* * This is the public API to create, initialize, register, and start the * TCP listener. */ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3( pjsip_endpoint *endpt, const pjsip_tcp_transport_cfg *cfg, pjsip_tpfactory **p_factory ) { pj_pool_t *pool; pj_sock_t sock = PJ_INVALID_SOCKET; struct tcp_listener *listener; pj_activesock_cfg asock_cfg; pj_activesock_cb listener_cb; pj_sockaddr *listener_addr; int addr_len; pj_bool_t has_listener = PJ_FALSE; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && cfg->async_cnt, PJ_EINVAL); /* Verify that address given in a_name (if any) is valid */ if (cfg->addr_name.host.slen) { pj_sockaddr tmp; status = pj_sockaddr_init(cfg->af, &tmp, &cfg->addr_name.host, (pj_uint16_t)cfg->addr_name.port); if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) || (cfg->af==pj_AF_INET() && tmp.ipv4.sin_addr.s_addr==PJ_INADDR_NONE)) { /* Invalid address */ return PJ_EINVAL; } } pool = pjsip_endpt_create_pool(endpt, "tcptp", POOL_LIS_INIT, POOL_LIS_INC); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); listener = PJ_POOL_ZALLOC_T(pool, struct tcp_listener); listener->factory.pool = pool; listener->factory.type = cfg->af==pj_AF_INET() ? PJSIP_TRANSPORT_TCP : PJSIP_TRANSPORT_TCP6; listener->factory.type_name = (char*) pjsip_transport_get_type_name(listener->factory.type); listener->factory.flag = pjsip_transport_get_flag_from_type(listener->factory.type); listener->qos_type = cfg->qos_type; pj_memcpy(&listener->qos_params, &cfg->qos_params, sizeof(cfg->qos_params)); pj_memcpy(&listener->sockopt_params, &cfg->sockopt_params, sizeof(cfg->sockopt_params)); pj_ansi_strcpy(listener->factory.obj_name, "tcptp"); if (listener->factory.type==PJSIP_TRANSPORT_TCP6) pj_ansi_strcat(listener->factory.obj_name, "6"); status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name, &listener->factory.lock); if (status != PJ_SUCCESS) goto on_error; #if !(defined(PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER) && \ PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0) /* Create socket */ status = pj_sock_socket(cfg->af, pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) goto on_error; /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, cfg->qos_type, &cfg->qos_params, 2, listener->factory.obj_name, "SIP TCP listener socket"); /* Apply SO_REUSEADDR */ if (cfg->reuse_addr) { int enabled = 1; status = pj_sock_setsockopt(sock, pj_SOL_SOCKET(), pj_SO_REUSEADDR(), &enabled, sizeof(enabled)); if (status != PJ_SUCCESS) { PJ_PERROR(4,(listener->factory.obj_name, status, "Warning: error applying SO_REUSEADDR")); } } /* Apply socket options, if specified */ if (cfg->sockopt_params.cnt) status = pj_sock_setsockopt_params(sock, &cfg->sockopt_params); #else PJ_UNUSED_ARG(addr_len); #endif /* Bind address may be different than factory.local_addr because * factory.local_addr will be resolved below. */ pj_sockaddr_cp(&listener->bound_addr, &cfg->bind_addr); /* Bind socket */ listener_addr = &listener->factory.local_addr; pj_sockaddr_cp(listener_addr, &cfg->bind_addr); #if !(defined(PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER) && \ PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0) status = pj_sock_bind(sock, listener_addr, pj_sockaddr_get_len(listener_addr)); if (status != PJ_SUCCESS) goto on_error; /* Retrieve the bound address */ addr_len = pj_sockaddr_get_len(listener_addr); status = pj_sock_getsockname(sock, listener_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; #endif /* If published host/IP is specified, then use that address as the * listener advertised address. */ if (cfg->addr_name.host.slen) { /* Copy the address */ listener->factory.addr_name = cfg->addr_name; pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, &cfg->addr_name.host); listener->factory.addr_name.port = cfg->addr_name.port; } else { /* No published address is given, use the bound address */ /* If the address returns 0.0.0.0, use the default * interface address as the transport's address. */ if (!pj_sockaddr_has_addr(listener_addr)) { pj_sockaddr hostip; status = pj_gethostip(listener->bound_addr.addr.sa_family, &hostip); if (status != PJ_SUCCESS) goto on_error; pj_sockaddr_copy_addr(listener_addr, &hostip); } /* Save the address name */ sockaddr_to_host_port(listener->factory.pool, &listener->factory.addr_name, listener_addr); } /* If port is zero, get the bound port */ if (listener->factory.addr_name.port == 0) { listener->factory.addr_name.port = pj_sockaddr_get_port(listener_addr); } pj_ansi_snprintf(listener->factory.obj_name, sizeof(listener->factory.obj_name), "tcptp:%d", listener->factory.addr_name.port); #if !(defined(PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER) && \ PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0) /* Start listening to the address */ status = pj_sock_listen(sock, PJSIP_TCP_TRANSPORT_BACKLOG); if (status != PJ_SUCCESS) goto on_error; /* Create active socket */ pj_activesock_cfg_default(&asock_cfg); if (cfg->async_cnt > MAX_ASYNC_CNT) asock_cfg.async_cnt = MAX_ASYNC_CNT; else asock_cfg.async_cnt = cfg->async_cnt; #endif /* Create group lock */ status = pj_grp_lock_create(pool, NULL, &listener->grp_lock); if (status != PJ_SUCCESS) return status; pj_grp_lock_add_ref(listener->grp_lock); pj_grp_lock_add_handler(listener->grp_lock, pool, listener, &lis_on_destroy); asock_cfg.grp_lock = listener->grp_lock; pj_bzero(&listener_cb, sizeof(listener_cb)); listener_cb.on_accept_complete = &on_accept_complete; #if !(defined(PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER) && \ PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0) status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), &asock_cfg, pjsip_endpt_get_ioqueue(endpt), &listener_cb, listener, &listener->asock); #endif /* Register to transport manager */ listener->endpt = endpt; listener->tpmgr = pjsip_endpt_get_tpmgr(endpt); listener->factory.create_transport = lis_create_transport; listener->factory.destroy = lis_destroy; listener->is_registered = PJ_TRUE; status = pjsip_tpmgr_register_tpfactory(listener->tpmgr, &listener->factory); if (status != PJ_SUCCESS) { listener->is_registered = PJ_FALSE; goto on_error; } #if !(defined(PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER) && \ PJSIP_TCP_TRANSPORT_DONT_CREATE_LISTENER != 0) /* Start pending accept() operations */ status = pj_activesock_start_accept(listener->asock, pool); if (status != PJ_SUCCESS) goto on_error; has_listener = PJ_TRUE; #endif if (has_listener) { PJ_LOG(4,(listener->factory.obj_name, "SIP TCP listener ready for incoming connections at %.*s:%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port)); } else { PJ_LOG(4,(listener->factory.obj_name, "SIP TCP is ready " "(client only)")); } /* Return the pointer to user */ if (p_factory) *p_factory = &listener->factory; return PJ_SUCCESS; on_error: if (listener->asock==NULL && sock!=PJ_INVALID_SOCKET) pj_sock_close(sock); lis_destroy(&listener->factory); return status; } /* * This is the public API to create, initialize, register, and start the * TCP listener. */ PJ_DEF(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt, const pj_sockaddr_in *local, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory) { pjsip_tcp_transport_cfg cfg; pjsip_tcp_transport_cfg_default(&cfg, pj_AF_INET()); if (local) pj_sockaddr_cp(&cfg.bind_addr, local); else pj_sockaddr_init(cfg.af, &cfg.bind_addr, NULL, 0); if (a_name) pj_memcpy(&cfg.addr_name, a_name, sizeof(*a_name)); if (async_cnt) cfg.async_cnt = async_cnt; return pjsip_tcp_transport_start3(endpt, &cfg, p_factory); } /* * This is the public API to create, initialize, register, and start the * TCP listener. */ PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt, const pj_sockaddr_in *local, unsigned async_cnt, pjsip_tpfactory **p_factory) { return pjsip_tcp_transport_start2(endpt, local, NULL, async_cnt, p_factory); } /* Clean up listener resources */ static void lis_on_destroy(void *arg) { struct tcp_listener *listener = (struct tcp_listener *)arg; if (listener->factory.lock) { pj_lock_destroy(listener->factory.lock); listener->factory.lock = NULL; } if (listener->factory.pool) { pj_pool_t *pool = listener->factory.pool; PJ_LOG(4,(listener->factory.obj_name, "SIP TCP transport destroyed")); listener->factory.pool = NULL; pj_pool_release(pool); } } /* This callback is called by transport manager to destroy listener */ static pj_status_t lis_destroy(pjsip_tpfactory *factory) { struct tcp_listener *listener = (struct tcp_listener *)factory; if (listener->is_registered) { pjsip_tpmgr_unregister_tpfactory(listener->tpmgr, &listener->factory); listener->is_registered = PJ_FALSE; } if (listener->asock) { pj_activesock_close(listener->asock); listener->asock = NULL; } if (listener->grp_lock) { pj_grp_lock_t *grp_lock = listener->grp_lock; listener->grp_lock = NULL; pj_grp_lock_dec_ref(grp_lock); /* Listener may have been deleted at this point */ } else { lis_on_destroy(listener); } return PJ_SUCCESS; } /***************************************************************************/ /* * TCP Transport */ /* * Prototypes. */ /* Called by transport manager to send message */ static pj_status_t tcp_send_msg(pjsip_transport *transport, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, pjsip_transport_callback callback); /* Called by transport manager to shutdown */ static pj_status_t tcp_shutdown(pjsip_transport *transport); /* Called by transport manager to destroy transport */ static pj_status_t tcp_destroy_transport(pjsip_transport *transport); /* Utility to destroy transport */ static pj_status_t tcp_destroy(pjsip_transport *transport, pj_status_t reason); /* Callback on incoming data */ static pj_bool_t on_data_read(pj_activesock_t *asock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder); /* Callback when packet is sent */ static pj_bool_t on_data_sent(pj_activesock_t *asock, pj_ioqueue_op_key_t *send_key, pj_ssize_t sent); /* Callback when connect completes */ static pj_bool_t on_connect_complete(pj_activesock_t *asock, pj_status_t status); /* TCP keep-alive timer callback */ static void tcp_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e); /* Clean up TCP resources */ static void tcp_on_destroy(void *arg); /* * Common function to create TCP transport, called when pending accept() and * pending connect() complete. */ static pj_status_t tcp_create( struct tcp_listener *listener, pj_pool_t *pool, pj_sock_t sock, pj_bool_t is_server, const pj_sockaddr *local, const pj_sockaddr *remote, struct tcp_transport **p_tcp) { struct tcp_transport *tcp; pj_ioqueue_t *ioqueue; pj_activesock_cfg asock_cfg; pj_activesock_cb tcp_callback; const pj_str_t ka_pkt = PJSIP_TCP_KEEP_ALIVE_DATA; char print_addr[PJ_INET6_ADDRSTRLEN+10]; pj_status_t status; PJ_ASSERT_RETURN(sock != PJ_INVALID_SOCKET, PJ_EINVAL); if (pool == NULL) { pool = pjsip_endpt_create_pool(listener->endpt, "tcp", POOL_TP_INIT, POOL_TP_INC); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); } /* * Create and initialize basic transport structure. */ tcp = PJ_POOL_ZALLOC_T(pool, struct tcp_transport); tcp->is_server = is_server; tcp->sock = sock; /*tcp->listener = listener;*/ pj_list_init(&tcp->delayed_list); tcp->base.pool = pool; pj_ansi_snprintf(tcp->base.obj_name, PJ_MAX_OBJ_NAME, (is_server ? "tcps%p" :"tcpc%p"), tcp); status = pj_atomic_create(pool, 0, &tcp->base.ref_cnt); if (status != PJ_SUCCESS) { goto on_error; } status = pj_lock_create_recursive_mutex(pool, "tcp", &tcp->base.lock); if (status != PJ_SUCCESS) { goto on_error; } tcp->base.key.type = listener->factory.type; pj_sockaddr_cp(&tcp->base.key.rem_addr, remote); tcp->base.type_name = (char*)pjsip_transport_get_type_name( (pjsip_transport_type_e)tcp->base.key.type); tcp->base.flag = pjsip_transport_get_flag_from_type( (pjsip_transport_type_e)tcp->base.key.type); tcp->base.info = (char*) pj_pool_alloc(pool, 64); pj_ansi_snprintf(tcp->base.info, 64, "%s to %s", tcp->base.type_name, pj_sockaddr_print(remote, print_addr, sizeof(print_addr), 3)); tcp->base.addr_len = pj_sockaddr_get_len(remote); pj_sockaddr_cp(&tcp->base.local_addr, local); sockaddr_to_host_port(pool, &tcp->base.local_name, local); sockaddr_to_host_port(pool, &tcp->base.remote_name, remote); tcp->base.dir = is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING; tcp->base.endpt = listener->endpt; tcp->base.tpmgr = listener->tpmgr; tcp->base.send_msg = &tcp_send_msg; tcp->base.do_shutdown = &tcp_shutdown; tcp->base.destroy = &tcp_destroy_transport; /* Create group lock */ status = pj_grp_lock_create(pool, NULL, &tcp->grp_lock); if (status != PJ_SUCCESS) goto on_error; pj_grp_lock_add_ref(tcp->grp_lock); pj_grp_lock_add_handler(tcp->grp_lock, pool, tcp, &tcp_on_destroy); /* Create active socket */ pj_activesock_cfg_default(&asock_cfg); asock_cfg.async_cnt = 1; asock_cfg.grp_lock = tcp->grp_lock; pj_bzero(&tcp_callback, sizeof(tcp_callback)); tcp_callback.on_data_read = &on_data_read; tcp_callback.on_data_sent = &on_data_sent; tcp_callback.on_connect_complete = &on_connect_complete; ioqueue = pjsip_endpt_get_ioqueue(listener->endpt); status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), &asock_cfg, ioqueue, &tcp_callback, tcp, &tcp->asock); if (status != PJ_SUCCESS) { goto on_error; } /* Register transport to transport manager */ status = pjsip_transport_register(listener->tpmgr, &tcp->base); if (status != PJ_SUCCESS) { goto on_error; } tcp->is_registered = PJ_TRUE; /* Initialize keep-alive timer */ tcp->ka_timer.user_data = (void*)tcp; tcp->ka_timer.cb = &tcp_keep_alive_timer; pj_ioqueue_op_key_init(&tcp->ka_op_key.key, sizeof(pj_ioqueue_op_key_t)); pj_strdup(tcp->base.pool, &tcp->ka_pkt, &ka_pkt); /* Done setting up basic transport. */ *p_tcp = tcp; PJ_LOG(4,(tcp->base.obj_name, "TCP %s transport created", (tcp->is_server ? "server" : "client"))); return PJ_SUCCESS; on_error: tcp_destroy(&tcp->base, status); return status; } /* Flush all delayed transmision once the socket is connected. */ static void tcp_flush_pending_tx(struct tcp_transport *tcp) { pj_time_val now; pj_gettickcount(&now); pj_lock_acquire(tcp->base.lock); while (!pj_list_empty(&tcp->delayed_list)) { struct delayed_tdata *pending_tx; pjsip_tx_data *tdata; pj_ioqueue_op_key_t *op_key; pj_ssize_t size; pj_status_t status; pending_tx = tcp->delayed_list.next; pj_list_erase(pending_tx); tdata = pending_tx->tdata_op_key->tdata; op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key; if (pending_tx->timeout.sec > 0 && PJ_TIME_VAL_GT(now, pending_tx->timeout)) { continue; } /* send! */ size = tdata->buf.cur - tdata->buf.start; status = pj_activesock_send(tcp->asock, op_key, tdata->buf.start, &size, 0); if (status != PJ_EPENDING) { pj_lock_release(tcp->base.lock); on_data_sent(tcp->asock, op_key, size); pj_lock_acquire(tcp->base.lock); } } pj_lock_release(tcp->base.lock); } /* Called by transport manager to destroy transport */ static pj_status_t tcp_destroy_transport(pjsip_transport *transport) { struct tcp_transport *tcp = (struct tcp_transport*)transport; /* Transport would have been unregistered by now since this callback * is called by transport manager. */ tcp->is_registered = PJ_FALSE; return tcp_destroy(transport, tcp->close_reason); } /* Destroy TCP transport */ static pj_status_t tcp_destroy(pjsip_transport *transport, pj_status_t reason) { struct tcp_transport *tcp = (struct tcp_transport*)transport; if (tcp->close_reason == 0) tcp->close_reason = reason; if (tcp->is_registered) { tcp->is_registered = PJ_FALSE; pjsip_transport_destroy(transport); /* pjsip_transport_destroy will recursively call this function * again. */ return PJ_SUCCESS; } /* Mark transport as closing */ tcp->is_closing = PJ_TRUE; /* Stop keep-alive timer. */ if (tcp->ka_timer.id) { pjsip_endpt_cancel_timer(tcp->base.endpt, &tcp->ka_timer); tcp->ka_timer.id = PJ_FALSE; } /* Cancel all delayed transmits */ while (!pj_list_empty(&tcp->delayed_list)) { struct delayed_tdata *pending_tx; pj_ioqueue_op_key_t *op_key; pending_tx = tcp->delayed_list.next; pj_list_erase(pending_tx); op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key; on_data_sent(tcp->asock, op_key, -reason); } if (tcp->asock) { pj_activesock_close(tcp->asock); tcp->asock = NULL; tcp->sock = PJ_INVALID_SOCKET; } else if (tcp->sock != PJ_INVALID_SOCKET) { pj_sock_close(tcp->sock); tcp->sock = PJ_INVALID_SOCKET; } if (tcp->grp_lock) { pj_grp_lock_t *grp_lock = tcp->grp_lock; tcp->grp_lock = NULL; pj_grp_lock_dec_ref(grp_lock); /* Transport may have been deleted at this point */ } else { tcp_on_destroy(tcp); } return PJ_SUCCESS; } /* Clean up TCP resources */ static void tcp_on_destroy(void *arg) { struct tcp_transport *tcp = (struct tcp_transport*)arg; if (tcp->base.lock) { pj_lock_destroy(tcp->base.lock); tcp->base.lock = NULL; } if (tcp->base.ref_cnt) { pj_atomic_destroy(tcp->base.ref_cnt); tcp->base.ref_cnt = NULL; } if (tcp->rdata.tp_info.pool) { pj_pool_release(tcp->rdata.tp_info.pool); tcp->rdata.tp_info.pool = NULL; } if (tcp->base.pool) { pj_pool_t *pool; if (tcp->close_reason != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(tcp->close_reason, errmsg, sizeof(errmsg)); PJ_LOG(4,(tcp->base.obj_name, "TCP transport destroyed with reason %d: %s", tcp->close_reason, errmsg)); } else { PJ_LOG(4,(tcp->base.obj_name, "TCP transport destroyed normally")); } pool = tcp->base.pool; tcp->base.pool = NULL; pj_pool_release(pool); } } /* * This utility function creates receive data buffers and start * asynchronous recv() operations from the socket. It is called after * accept() or connect() operation complete. */ static pj_status_t tcp_start_read(struct tcp_transport *tcp) { pj_pool_t *pool; pj_uint32_t size; pj_sockaddr *rem_addr; void *readbuf[1]; pj_status_t status; /* Init rdata */ pool = pjsip_endpt_create_pool(tcp->base.endpt, "rtd%p", PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC); if (!pool) { tcp_perror(tcp->base.obj_name, "Unable to create pool", PJ_ENOMEM); return PJ_ENOMEM; } tcp->rdata.tp_info.pool = pool; tcp->rdata.tp_info.transport = &tcp->base; tcp->rdata.tp_info.tp_data = tcp; tcp->rdata.tp_info.op_key.rdata = &tcp->rdata; pj_ioqueue_op_key_init(&tcp->rdata.tp_info.op_key.op_key, sizeof(pj_ioqueue_op_key_t)); tcp->rdata.pkt_info.src_addr = tcp->base.key.rem_addr; tcp->rdata.pkt_info.src_addr_len = sizeof(tcp->rdata.pkt_info.src_addr); rem_addr = &tcp->base.key.rem_addr; pj_sockaddr_print(rem_addr, tcp->rdata.pkt_info.src_name, sizeof(tcp->rdata.pkt_info.src_name), 0); tcp->rdata.pkt_info.src_port = pj_sockaddr_get_port(rem_addr); size = sizeof(tcp->rdata.pkt_info.packet); readbuf[0] = tcp->rdata.pkt_info.packet; status = pj_activesock_start_read2(tcp->asock, tcp->base.pool, size, readbuf, 0); if (status != PJ_SUCCESS && status != PJ_EPENDING) { PJ_LOG(4, (tcp->base.obj_name, "pj_activesock_start_read() error, status=%d", status)); return status; } return PJ_SUCCESS; } /* This callback is called by transport manager for the TCP factory * to create outgoing transport to the specified destination. */ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, pjsip_tpmgr *mgr, pjsip_endpoint *endpt, const pj_sockaddr *rem_addr, int addr_len, pjsip_transport **p_transport) { struct tcp_listener *listener; struct tcp_transport *tcp; pj_sock_t sock; pj_sockaddr local_addr; pj_status_t status; /* Sanity checks */ PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr && addr_len && p_transport, PJ_EINVAL); /* Check that address is a sockaddr_in or sockaddr_in6*/ PJ_ASSERT_RETURN((rem_addr->addr.sa_family == pj_AF_INET() && addr_len == sizeof(pj_sockaddr_in)) || (rem_addr->addr.sa_family == pj_AF_INET6() && addr_len == sizeof(pj_sockaddr_in6)), PJ_EINVAL); listener = (struct tcp_listener*)factory; /* Create socket */ status = pj_sock_socket(rem_addr->addr.sa_family, pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) return status; /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, listener->qos_type, &listener->qos_params, 2, listener->factory.obj_name, "outgoing SIP TCP socket"); /* Apply socket options, if specified */ if (listener->sockopt_params.cnt) status = pj_sock_setsockopt_params(sock, &listener->sockopt_params); /* Bind to listener's address and any port */ pj_bzero(&local_addr, sizeof(local_addr)); pj_sockaddr_cp(&local_addr, &listener->bound_addr); pj_sockaddr_set_port(&local_addr, 0); status = pj_sock_bind(sock, &local_addr, pj_sockaddr_get_len(&local_addr)); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Get the local port */ addr_len = sizeof(local_addr); status = pj_sock_getsockname(sock, &local_addr, &addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Initially set the address from the listener's address */ if (!pj_sockaddr_has_addr(&local_addr)) { pj_sockaddr_copy_addr(&local_addr, &listener->factory.local_addr); } /* Create the transport descriptor */ status = tcp_create(listener, NULL, sock, PJ_FALSE, &local_addr, rem_addr, &tcp); if (status != PJ_SUCCESS) return status; /* Start asynchronous connect() operation */ tcp->has_pending_connect = PJ_TRUE; status = pj_activesock_start_connect(tcp->asock, tcp->base.pool, rem_addr, addr_len); if (status == PJ_SUCCESS) { on_connect_complete(tcp->asock, PJ_SUCCESS); } else if (status != PJ_EPENDING) { tcp_destroy(&tcp->base, status); return status; } if (tcp->has_pending_connect) { /* Update (again) local address, just in case local address currently * set is different now that asynchronous connect() is started. */ addr_len = sizeof(local_addr); if (pj_sock_getsockname(sock, &local_addr, &addr_len)==PJ_SUCCESS) { pj_sockaddr *tp_addr = &tcp->base.local_addr; /* Some systems (like old Win32 perhaps) may not set local address * properly before socket is fully connected. */ if (pj_sockaddr_cmp(tp_addr, &local_addr) && pj_sockaddr_has_addr(&local_addr) && pj_sockaddr_get_port(&local_addr) != 0) { pj_sockaddr_cp(tp_addr, &local_addr); sockaddr_to_host_port(tcp->base.pool, &tcp->base.local_name, &local_addr); } } PJ_LOG(4,(tcp->base.obj_name, "TCP transport %.*s:%d is connecting to %.*s:%d...", (int)tcp->base.local_name.host.slen, tcp->base.local_name.host.ptr, tcp->base.local_name.port, (int)tcp->base.remote_name.host.slen, tcp->base.remote_name.host.ptr, tcp->base.remote_name.port)); } /* Done */ *p_transport = &tcp->base; return PJ_SUCCESS; } /* * This callback is called by active socket when pending accept() operation * has completed. */ static pj_bool_t on_accept_complete(pj_activesock_t *asock, pj_sock_t sock, const pj_sockaddr_t *src_addr, int src_addr_len) { struct tcp_listener *listener; struct tcp_transport *tcp; char addr[PJ_INET6_ADDRSTRLEN+10]; pjsip_tp_state_callback state_cb; pj_sockaddr tmp_src_addr, tmp_dst_addr; int addr_len; pj_status_t status; PJ_UNUSED_ARG(src_addr_len); listener = (struct tcp_listener*) pj_activesock_get_user_data(asock); PJ_ASSERT_RETURN(sock != PJ_INVALID_SOCKET, PJ_TRUE); if (!listener->is_registered) return PJ_FALSE; PJ_LOG(4,(listener->factory.obj_name, "TCP listener %.*s:%d: got incoming TCP connection " "from %s, sock=%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port, pj_sockaddr_print(src_addr, addr, sizeof(addr), 3), sock)); /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, listener->qos_type, &listener->qos_params, 2, listener->factory.obj_name, "incoming SIP TCP socket"); /* Apply socket options, if specified */ if (listener->sockopt_params.cnt) status = pj_sock_setsockopt_params(sock, &listener->sockopt_params); /* tcp_create() expect pj_sockaddr, so copy src_addr to temporary var, * just in case. */ pj_bzero(&tmp_src_addr, sizeof(tmp_src_addr)); pj_sockaddr_cp(&tmp_src_addr, src_addr); /* Get local address */ addr_len = sizeof(tmp_dst_addr); status = pj_sock_getsockname(sock, &tmp_dst_addr, &addr_len); if (status != PJ_SUCCESS) { pj_sockaddr_cp(&tmp_dst_addr, &listener->factory.local_addr); } /* * Incoming connection! * Create TCP transport for the new socket. */ status = tcp_create( listener, NULL, sock, PJ_TRUE, &tmp_dst_addr, &tmp_src_addr, &tcp); if (status == PJ_SUCCESS) { status = tcp_start_read(tcp); if (status != PJ_SUCCESS) { PJ_LOG(3,(tcp->base.obj_name, "New transport cancelled")); tcp_destroy(&tcp->base, status); } else { if (tcp->base.is_shutdown || tcp->base.is_destroying) { return PJ_TRUE; } /* Start keep-alive timer */ if (pjsip_cfg()->tcp.keep_alive_interval) { pj_time_val delay = { 0 }; delay.sec = pjsip_cfg()->tcp.keep_alive_interval; pjsip_endpt_schedule_timer(listener->endpt, &tcp->ka_timer, &delay); tcp->ka_timer.id = PJ_TRUE; pj_gettimeofday(&tcp->last_activity); } /* Notify application of transport state accepted */ state_cb = pjsip_tpmgr_get_state_cb(tcp->base.tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pj_bzero(&state_info, sizeof(state_info)); (*state_cb)(&tcp->base, PJSIP_TP_STATE_CONNECTED, &state_info); } } } return PJ_TRUE; } /* * Callback from ioqueue when packet is sent. */ static pj_bool_t on_data_sent(pj_activesock_t *asock, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_sent) { struct tcp_transport *tcp = (struct tcp_transport*) pj_activesock_get_user_data(asock); pjsip_tx_data_op_key *tdata_op_key = (pjsip_tx_data_op_key*)op_key; /* Note that op_key may be the op_key from keep-alive, thus * it will not have tdata etc. */ tdata_op_key->tdata = NULL; if (tdata_op_key->callback) { /* * Notify sip_transport.c that packet has been sent. */ if (bytes_sent == 0) bytes_sent = -PJ_RETURN_OS_ERROR(OSERR_ENOTCONN); tdata_op_key->callback(&tcp->base, tdata_op_key->token, bytes_sent); /* Mark last activity time */ pj_gettimeofday(&tcp->last_activity); } /* Check for error/closure */ if (bytes_sent <= 0) { pj_status_t status; PJ_LOG(5,(tcp->base.obj_name, "TCP send() error, sent=%d", bytes_sent)); status = (bytes_sent == 0) ? PJ_RETURN_OS_ERROR(OSERR_ENOTCONN) : (pj_status_t)-bytes_sent; tcp_init_shutdown(tcp, status); return PJ_FALSE; } return PJ_TRUE; } /* * This callback is called by transport manager to send SIP message */ static pj_status_t tcp_send_msg(pjsip_transport *transport, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, pjsip_transport_callback callback) { struct tcp_transport *tcp = (struct tcp_transport*)transport; pj_ssize_t size; pj_bool_t delayed = PJ_FALSE; pj_status_t status = PJ_SUCCESS; /* Sanity check */ PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL); /* Check that there's no pending operation associated with the tdata */ PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX); /* Check the address is supported */ PJ_ASSERT_RETURN(rem_addr && (addr_len==sizeof(pj_sockaddr_in) || addr_len==sizeof(pj_sockaddr_in6)), PJ_EINVAL); /* Init op key. */ tdata->op_key.tdata = tdata; tdata->op_key.token = token; tdata->op_key.callback = callback; /* If asynchronous connect() has not completed yet, just put the * transmit data in the pending transmission list since we can not * use the socket yet. */ if (tcp->has_pending_connect) { /* * Looks like connect() is still in progress. Check again (this time * with holding the lock) to be sure. */ pj_lock_acquire(tcp->base.lock); if (tcp->has_pending_connect) { struct delayed_tdata *delayed_tdata; /* * connect() is still in progress. Put the transmit data to * the delayed list. * Starting from #1583 (https://trac.pjsip.org/repos/ticket/1583), * we also add timeout value for the transmit data. When the * connect() is completed, the timeout value will be checked to * determine whether the transmit data needs to be sent. */ delayed_tdata = PJ_POOL_ZALLOC_T(tdata->pool, struct delayed_tdata); delayed_tdata->tdata_op_key = &tdata->op_key; if (tdata->msg && tdata->msg->type == PJSIP_REQUEST_MSG) { pj_gettickcount(&delayed_tdata->timeout); delayed_tdata->timeout.msec += pjsip_cfg()->tsx.td; pj_time_val_normalize(&delayed_tdata->timeout); } pj_list_push_back(&tcp->delayed_list, delayed_tdata); status = PJ_EPENDING; /* Prevent pj_ioqueue_send() to be called below */ delayed = PJ_TRUE; } pj_lock_release(tcp->base.lock); } if (!delayed) { /* * Transport is ready to go. Send the packet to ioqueue to be * sent asynchronously. */ size = tdata->buf.cur - tdata->buf.start; status = pj_activesock_send(tcp->asock, (pj_ioqueue_op_key_t*)&tdata->op_key, tdata->buf.start, &size, 0); if (status != PJ_EPENDING) { /* Not pending (could be immediate success or error) */ tdata->op_key.tdata = NULL; /* Shutdown transport on closure/errors */ if (size <= 0) { PJ_LOG(5,(tcp->base.obj_name, "TCP send() error, sent=%d", size)); if (status == PJ_SUCCESS) status = PJ_RETURN_OS_ERROR(OSERR_ENOTCONN); tcp_init_shutdown(tcp, status); } } } return status; } /* * This callback is called by transport manager to shutdown transport. */ static pj_status_t tcp_shutdown(pjsip_transport *transport) { struct tcp_transport *tcp = (struct tcp_transport*)transport; /* Stop keep-alive timer. */ if (tcp->ka_timer.id) { pjsip_endpt_cancel_timer(tcp->base.endpt, &tcp->ka_timer); tcp->ka_timer.id = PJ_FALSE; } return PJ_SUCCESS; } /* * Callback from ioqueue that an incoming data is received from the socket. */ static pj_bool_t on_data_read(pj_activesock_t *asock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder) { enum { MAX_IMMEDIATE_PACKET = 10 }; struct tcp_transport *tcp; pjsip_rx_data *rdata; PJ_UNUSED_ARG(data); tcp = (struct tcp_transport*) pj_activesock_get_user_data(asock); rdata = &tcp->rdata; /* Don't do anything if transport is closing. */ if (tcp->is_closing) { tcp->is_closing++; return PJ_FALSE; } /* Houston, we have packet! Report the packet to transport manager * to be parsed. */ if (status == PJ_SUCCESS) { pj_size_t size_eaten; /* Mark this as an activity */ pj_gettimeofday(&tcp->last_activity); pj_assert((void*)rdata->pkt_info.packet == data); /* Init pkt_info part. */ rdata->pkt_info.len = size; rdata->pkt_info.zero = 0; pj_gettimeofday(&rdata->pkt_info.timestamp); /* Report to transport manager. * The transport manager will tell us how many bytes of the packet * have been processed (as valid SIP message). */ size_eaten = pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, rdata); pj_assert(size_eaten <= (pj_size_t)rdata->pkt_info.len); /* Move unprocessed data to the front of the buffer */ *remainder = size - size_eaten; if (*remainder > 0 && *remainder != size) { pj_memmove(rdata->pkt_info.packet, rdata->pkt_info.packet + size_eaten, *remainder); } } else { /* Transport is closed */ PJ_LOG(4,(tcp->base.obj_name, "TCP connection closed")); tcp_init_shutdown(tcp, status); return PJ_FALSE; } /* Reset pool. */ pj_pool_reset(rdata->tp_info.pool); return PJ_TRUE; } /* * Callback from ioqueue when asynchronous connect() operation completes. */ static pj_bool_t on_connect_complete(pj_activesock_t *asock, pj_status_t status) { struct tcp_transport *tcp; pj_sockaddr addr; int addrlen; pjsip_tp_state_callback state_cb; tcp = (struct tcp_transport*) pj_activesock_get_user_data(asock); /* Mark that pending connect() operation has completed. */ tcp->has_pending_connect = PJ_FALSE; /* If transport is being shutdown/destroyed, proceed as error connect. * Note that it is important to notify application via on_data_sent() * as otherwise the transport reference counter may never reach zero * (see #1898). */ if ((tcp->base.is_shutdown || tcp->base.is_destroying) && status == PJ_SUCCESS) { status = PJ_ECANCELLED; } /* Check connect() status */ if (status != PJ_SUCCESS) { tcp_perror(tcp->base.obj_name, "TCP connect() error", status); /* Cancel all delayed transmits */ while (!pj_list_empty(&tcp->delayed_list)) { struct delayed_tdata *pending_tx; pj_ioqueue_op_key_t *op_key; pending_tx = tcp->delayed_list.next; pj_list_erase(pending_tx); op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key; on_data_sent(tcp->asock, op_key, -status); } tcp_init_shutdown(tcp, status); return PJ_FALSE; } PJ_LOG(4,(tcp->base.obj_name, "TCP transport %.*s:%d is connected to %.*s:%d", (int)tcp->base.local_name.host.slen, tcp->base.local_name.host.ptr, tcp->base.local_name.port, (int)tcp->base.remote_name.host.slen, tcp->base.remote_name.host.ptr, tcp->base.remote_name.port)); /* Update (again) local address, just in case local address currently * set is different now that the socket is connected (could happen * on some systems, like old Win32 probably?). */ addrlen = sizeof(addr); if (pj_sock_getsockname(tcp->sock, &addr, &addrlen)==PJ_SUCCESS) { pj_sockaddr *tp_addr = &tcp->base.local_addr; if (pj_sockaddr_has_addr(&addr) && pj_sockaddr_cmp(&addr, tp_addr) != 0) { pj_sockaddr_cp(tp_addr, &addr); sockaddr_to_host_port(tcp->base.pool, &tcp->base.local_name, tp_addr); } } /* Start pending read */ status = tcp_start_read(tcp); if (status != PJ_SUCCESS) { tcp_init_shutdown(tcp, status); return PJ_FALSE; } /* Notify application of transport state connected */ state_cb = pjsip_tpmgr_get_state_cb(tcp->base.tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pj_bzero(&state_info, sizeof(state_info)); (*state_cb)(&tcp->base, PJSIP_TP_STATE_CONNECTED, &state_info); } /* Flush all pending send operations */ tcp_flush_pending_tx(tcp); /* Start keep-alive timer */ if (pjsip_cfg()->tcp.keep_alive_interval) { pj_time_val delay = { 0 }; delay.sec = pjsip_cfg()->tcp.keep_alive_interval; pjsip_endpt_schedule_timer(tcp->base.endpt, &tcp->ka_timer, &delay); tcp->ka_timer.id = PJ_TRUE; pj_gettimeofday(&tcp->last_activity); } return PJ_TRUE; } /* Transport keep-alive timer callback */ static void tcp_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e) { struct tcp_transport *tcp = (struct tcp_transport*) e->user_data; pj_time_val delay; pj_time_val now; pj_ssize_t size; pj_status_t status; PJ_UNUSED_ARG(th); tcp->ka_timer.id = PJ_TRUE; pj_gettimeofday(&now); PJ_TIME_VAL_SUB(now, tcp->last_activity); if (now.sec > 0 && now.sec < pjsip_cfg()->tcp.keep_alive_interval) { /* There has been activity, so don't send keep-alive */ delay.sec = pjsip_cfg()->tcp.keep_alive_interval - now.sec; delay.msec = 0; pjsip_endpt_schedule_timer(tcp->base.endpt, &tcp->ka_timer, &delay); tcp->ka_timer.id = PJ_TRUE; return; } PJ_LOG(5,(tcp->base.obj_name, "Sending %d byte(s) keep-alive to %.*s:%d", (int)tcp->ka_pkt.slen, (int)tcp->base.remote_name.host.slen, tcp->base.remote_name.host.ptr, tcp->base.remote_name.port)); /* Send the data */ size = tcp->ka_pkt.slen; status = pj_activesock_send(tcp->asock, &tcp->ka_op_key.key, tcp->ka_pkt.ptr, &size, 0); if (status != PJ_SUCCESS && status != PJ_EPENDING) { tcp_perror(tcp->base.obj_name, "Error sending keep-alive packet", status); tcp_init_shutdown(tcp, status); return; } /* Register next keep-alive */ delay.sec = pjsip_cfg()->tcp.keep_alive_interval; delay.msec = 0; pjsip_endpt_schedule_timer(tcp->base.endpt, &tcp->ka_timer, &delay); tcp->ka_timer.id = PJ_TRUE; } PJ_DEF(pj_sock_t) pjsip_tcp_transport_get_socket(pjsip_transport *transport) { struct tcp_transport *tcp = (struct tcp_transport*)transport; PJ_ASSERT_RETURN(transport, PJ_INVALID_SOCKET); return tcp->sock; } #endif /* PJ_HAS_TCP */ ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_transport_tls.c ================================================ /* $Id: sip_transport_tls.c 4506 2013-04-26 06:01:43Z bennylp $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0 #define THIS_FILE "sip_transport_tls.c" #define MAX_ASYNC_CNT 16 #define POOL_LIS_INIT 512 #define POOL_LIS_INC 512 #define POOL_TP_INIT 512 #define POOL_TP_INC 512 struct tls_listener; struct tls_transport; /* * Definition of TLS/SSL transport listener, and it's descendant of * pjsip_tpfactory. */ struct tls_listener { pjsip_tpfactory factory; pj_bool_t is_registered; pjsip_endpoint *endpt; pjsip_tpmgr *tpmgr; pj_ssl_sock_t *ssock; pj_sockaddr bound_addr; pj_ssl_cert_t *cert; pjsip_tls_setting tls_setting; /* Group lock to be used by TLS transport and ioqueue key */ pj_grp_lock_t *grp_lock; }; /* * This structure is used to keep delayed transmit operation in a list. * A delayed transmission occurs when application sends tx_data when * the TLS connect/establishment is still in progress. These delayed * transmission will be "flushed" once the socket is connected (either * successfully or with errors). */ struct delayed_tdata { PJ_DECL_LIST_MEMBER(struct delayed_tdata); pjsip_tx_data_op_key *tdata_op_key; pj_time_val timeout; }; /* * TLS/SSL transport, and it's descendant of pjsip_transport. */ struct tls_transport { pjsip_transport base; pj_bool_t is_server; pj_str_t remote_name; pj_bool_t is_registered; pj_bool_t is_closing; pj_status_t close_reason; pj_ssl_sock_t *ssock; pj_bool_t has_pending_connect; pj_bool_t verify_server; /* Keep-alive timer. */ pj_timer_entry ka_timer; pj_time_val last_activity; pjsip_tx_data_op_key ka_op_key; pj_str_t ka_pkt; /* TLS transport can only have one rdata! * Otherwise chunks of incoming PDU may be received on different * buffer. */ pjsip_rx_data rdata; /* Pending transmission list. */ struct delayed_tdata delayed_list; /* Group lock to be used by TLS transport and ioqueue key */ pj_grp_lock_t *grp_lock; }; /**************************************************************************** * PROTOTYPES */ /* This callback is called when pending accept() operation completes. */ static pj_bool_t on_accept_complete(pj_ssl_sock_t *ssock, pj_ssl_sock_t *new_ssock, const pj_sockaddr_t *src_addr, int src_addr_len); /* Callback on incoming data */ static pj_bool_t on_data_read(pj_ssl_sock_t *ssock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder); /* Callback when packet is sent */ static pj_bool_t on_data_sent(pj_ssl_sock_t *ssock, pj_ioqueue_op_key_t *send_key, pj_ssize_t sent); /* This callback is called by transport manager to destroy listener */ static pj_status_t lis_destroy(pjsip_tpfactory *factory); /* Clean up listener resources (group lock handler) */ static void lis_on_destroy(void *arg); /* This callback is called by transport manager to create transport */ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, pjsip_tpmgr *mgr, pjsip_endpoint *endpt, const pj_sockaddr *rem_addr, int addr_len, pjsip_tx_data *tdata, pjsip_transport **transport); /* Common function to create and initialize transport */ static pj_status_t tls_create(struct tls_listener *listener, pj_pool_t *pool, pj_ssl_sock_t *ssock, pj_bool_t is_server, const pj_sockaddr *local, const pj_sockaddr *remote, const pj_str_t *remote_name, struct tls_transport **p_tls); static void tls_perror(const char *sender, const char *title, pj_status_t status) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(3,(sender, "%s: %s [code=%d]", title, errmsg, status)); } static void sockaddr_to_host_port( pj_pool_t *pool, pjsip_host_port *host_port, const pj_sockaddr *addr ) { host_port->host.ptr = (char*) pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+4); pj_sockaddr_print(addr, host_port->host.ptr, PJ_INET6_ADDRSTRLEN+4, 0); host_port->host.slen = pj_ansi_strlen(host_port->host.ptr); host_port->port = pj_sockaddr_get_port(addr); } static pj_uint32_t ssl_get_proto(pjsip_ssl_method ssl_method, pj_uint32_t proto) { pj_uint32_t out_proto; if (proto) return proto; if (ssl_method == PJSIP_SSL_UNSPECIFIED_METHOD) ssl_method = PJSIP_SSL_DEFAULT_METHOD; switch(ssl_method) { case PJSIP_SSLV2_METHOD: out_proto = PJ_SSL_SOCK_PROTO_SSL2; break; case PJSIP_SSLV3_METHOD: out_proto = PJ_SSL_SOCK_PROTO_SSL3; break; case PJSIP_TLSV1_METHOD: out_proto = PJ_SSL_SOCK_PROTO_TLS1; break; case PJSIP_TLSV1_1_METHOD: out_proto = PJ_SSL_SOCK_PROTO_TLS1_1; break; case PJSIP_TLSV1_2_METHOD: out_proto = PJ_SSL_SOCK_PROTO_TLS1_2; break; case PJSIP_SSLV23_METHOD: out_proto = PJ_SSL_SOCK_PROTO_SSL23; break; default: out_proto = PJ_SSL_SOCK_PROTO_DEFAULT; break; } return out_proto; } static void tls_init_shutdown(struct tls_transport *tls, pj_status_t status) { pjsip_tp_state_callback state_cb; if (tls->close_reason == PJ_SUCCESS) tls->close_reason = status; if (tls->base.is_shutdown || tls->base.is_destroying) return; /* Prevent immediate transport destroy by application, as transport * state notification callback may be stacked and transport instance * must remain valid at any point in the callback. */ pjsip_transport_add_ref(&tls->base); /* Notify application of transport disconnected state */ state_cb = pjsip_tpmgr_get_state_cb(tls->base.tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pjsip_tls_state_info tls_info; pj_ssl_sock_info ssl_info; /* Init transport state info */ pj_bzero(&state_info, sizeof(state_info)); state_info.status = tls->close_reason; if (tls->ssock && pj_ssl_sock_get_info(tls->ssock, &ssl_info) == PJ_SUCCESS) { pj_bzero(&tls_info, sizeof(tls_info)); tls_info.ssl_sock_info = &ssl_info; state_info.ext_info = &tls_info; } (*state_cb)(&tls->base, PJSIP_TP_STATE_DISCONNECTED, &state_info); } /* check again */ if (tls->base.is_shutdown || tls->base.is_destroying) { pjsip_transport_dec_ref(&tls->base); return; } /* We can not destroy the transport since high level objects may * still keep reference to this transport. So we can only * instruct transport manager to gracefully start the shutdown * procedure for this transport. */ pjsip_transport_shutdown(&tls->base); /* Now, it is ok to destroy the transport. */ pjsip_transport_dec_ref(&tls->base); } /**************************************************************************** * The TLS listener/transport factory. */ /* * This is the public API to create, initialize, register, and start the * TLS listener. */ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt, const pjsip_tls_setting *opt, const pj_sockaddr_in *local_in, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory) { pj_sockaddr local; if (local_in) pj_sockaddr_cp(&local, local_in); return pjsip_tls_transport_start2(endpt, opt, (local_in? &local : NULL), a_name, async_cnt, p_factory); } PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt, const pjsip_tls_setting *opt, const pj_sockaddr *local, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory) { pj_pool_t *pool; pj_bool_t is_ipv6; int af, sip_ssl_method; pj_uint32_t sip_ssl_proto; struct tls_listener *listener; pj_ssl_sock_param ssock_param, newsock_param; pj_sockaddr *listener_addr; pj_bool_t has_listener; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL); is_ipv6 = (local && local->addr.sa_family == pj_AF_INET6()); af = is_ipv6 ? pj_AF_INET6() : pj_AF_INET(); /* Verify that address given in a_name (if any) is valid */ if (a_name && a_name->host.slen) { pj_sockaddr tmp; status = pj_sockaddr_init(af, &tmp, &a_name->host, (pj_uint16_t)a_name->port); if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) || (!is_ipv6 && tmp.ipv4.sin_addr.s_addr == PJ_INADDR_NONE)) { /* Invalid address */ return PJ_EINVAL; } } pool = pjsip_endpt_create_pool(endpt, "tlstp", POOL_LIS_INIT, POOL_LIS_INC); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); listener = PJ_POOL_ZALLOC_T(pool, struct tls_listener); listener->factory.pool = pool; if (is_ipv6) listener->factory.type = PJSIP_TRANSPORT_TLS6; else listener->factory.type = PJSIP_TRANSPORT_TLS; listener->factory.type_name = (char*) pjsip_transport_get_type_name(listener->factory.type); listener->factory.flag = pjsip_transport_get_flag_from_type(listener->factory.type); pj_ansi_strcpy(listener->factory.obj_name, "tlstp"); if (is_ipv6) pj_ansi_strcat(listener->factory.obj_name, "6"); if (opt) pjsip_tls_setting_copy(pool, &listener->tls_setting, opt); else pjsip_tls_setting_default(&listener->tls_setting); status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name, &listener->factory.lock); if (status != PJ_SUCCESS) goto on_error; if (async_cnt > MAX_ASYNC_CNT) async_cnt = MAX_ASYNC_CNT; /* Build SSL socket param */ pj_ssl_sock_param_default(&ssock_param); ssock_param.sock_af = af; ssock_param.cb.on_accept_complete = &on_accept_complete; ssock_param.async_cnt = async_cnt; ssock_param.ioqueue = pjsip_endpt_get_ioqueue(endpt); ssock_param.require_client_cert = listener->tls_setting.require_client_cert; ssock_param.timeout = listener->tls_setting.timeout; ssock_param.user_data = listener; ssock_param.verify_peer = PJ_FALSE; /* avoid SSL socket closing the socket * due to verification error */ if (ssock_param.send_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.send_buffer_size = PJSIP_MAX_PKT_LEN; if (ssock_param.read_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.read_buffer_size = PJSIP_MAX_PKT_LEN; ssock_param.ciphers_num = listener->tls_setting.ciphers_num; ssock_param.ciphers = listener->tls_setting.ciphers; ssock_param.reuse_addr = listener->tls_setting.reuse_addr; ssock_param.qos_type = listener->tls_setting.qos_type; ssock_param.qos_ignore_error = listener->tls_setting.qos_ignore_error; pj_memcpy(&ssock_param.qos_params, &listener->tls_setting.qos_params, sizeof(ssock_param.qos_params)); ssock_param.sockopt_ignore_error = listener->tls_setting.sockopt_ignore_error; /* Copy the sockopt */ pj_memcpy(&ssock_param.sockopt_params, &listener->tls_setting.sockopt_params, sizeof(listener->tls_setting.sockopt_params)); has_listener = PJ_FALSE; sip_ssl_method = listener->tls_setting.method; sip_ssl_proto = listener->tls_setting.proto; ssock_param.proto = ssl_get_proto(sip_ssl_method, sip_ssl_proto); /* Create group lock */ status = pj_grp_lock_create(pool, NULL, &listener->grp_lock); if (status != PJ_SUCCESS) return status; /* Setup group lock handler */ pj_grp_lock_add_ref(listener->grp_lock); pj_grp_lock_add_handler(listener->grp_lock, pool, listener, &lis_on_destroy); #if !(defined(PJSIP_TLS_TRANSPORT_DONT_CREATE_LISTENER) && \ PJSIP_TLS_TRANSPORT_DONT_CREATE_LISTENER != 0) ssock_param.grp_lock = listener->grp_lock; /* Create SSL socket */ status = pj_ssl_sock_create(pool, &ssock_param, &listener->ssock); if (status != PJ_SUCCESS) goto on_error; #endif /* Bind address may be different than factory.local_addr because * factory.local_addr will be resolved below. */ listener_addr = &listener->factory.local_addr; if (local) { pj_sockaddr_cp((pj_sockaddr_t*)listener_addr, (const pj_sockaddr_t*)local); pj_sockaddr_cp(&listener->bound_addr, local); } else { pj_sockaddr_init(af, listener_addr, NULL, 0); pj_sockaddr_init(af, &listener->bound_addr, NULL, 0); } #if !(defined(PJSIP_TLS_TRANSPORT_DONT_CREATE_LISTENER) && \ PJSIP_TLS_TRANSPORT_DONT_CREATE_LISTENER != 0) /* Check if certificate/CA list for SSL socket is set */ if (listener->tls_setting.cert_file.slen || listener->tls_setting.ca_list_file.slen || listener->tls_setting.ca_list_path.slen) { status = pj_ssl_cert_load_from_files2(pool, &listener->tls_setting.ca_list_file, &listener->tls_setting.ca_list_path, &listener->tls_setting.cert_file, &listener->tls_setting.privkey_file, &listener->tls_setting.password, &listener->cert); if (status != PJ_SUCCESS) goto on_error; status = pj_ssl_sock_set_certificate(listener->ssock, pool, listener->cert); if (status != PJ_SUCCESS) goto on_error; } /* Start accepting incoming connections. Note that some TLS/SSL backends * may not support for SSL socket server. */ has_listener = PJ_FALSE; pj_memcpy(&newsock_param, &ssock_param, sizeof(newsock_param)); newsock_param.async_cnt = 1; newsock_param.cb.on_data_read = &on_data_read; newsock_param.cb.on_data_sent = &on_data_sent; status = pj_ssl_sock_start_accept2(listener->ssock, pool, (pj_sockaddr_t*)listener_addr, pj_sockaddr_get_len((pj_sockaddr_t*)listener_addr), &newsock_param); if (status == PJ_SUCCESS || status == PJ_EPENDING) { pj_ssl_sock_info info; has_listener = PJ_TRUE; /* Retrieve the bound address */ status = pj_ssl_sock_get_info(listener->ssock, &info); if (status == PJ_SUCCESS) pj_sockaddr_cp(listener_addr, (pj_sockaddr_t*)&info.local_addr); } else if (status != PJ_ENOTSUP) { goto on_error; } #endif /* If published host/IP is specified, then use that address as the * listener advertised address. */ if (a_name && a_name->host.slen) { /* Copy the address */ listener->factory.addr_name = *a_name; pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, &a_name->host); listener->factory.addr_name.port = a_name->port; } else { /* No published address is given, use the bound address */ /* If the address returns 0.0.0.0, use the default * interface address as the transport's address. */ if (!pj_sockaddr_has_addr(listener_addr)) { pj_sockaddr hostip; status = pj_gethostip(af, &hostip); if (status != PJ_SUCCESS) goto on_error; pj_sockaddr_copy_addr(listener_addr, &hostip); } /* Save the address name */ sockaddr_to_host_port(listener->factory.pool, &listener->factory.addr_name, listener_addr); } /* If port is zero, get the bound port */ if (listener->factory.addr_name.port == 0) { listener->factory.addr_name.port = pj_sockaddr_get_port(listener_addr); } pj_ansi_snprintf(listener->factory.obj_name, sizeof(listener->factory.obj_name), "tlstp:%d", listener->factory.addr_name.port); /* Register to transport manager */ listener->endpt = endpt; listener->tpmgr = pjsip_endpt_get_tpmgr(endpt); listener->factory.create_transport2 = lis_create_transport; listener->factory.destroy = lis_destroy; listener->is_registered = PJ_TRUE; status = pjsip_tpmgr_register_tpfactory(listener->tpmgr, &listener->factory); if (status != PJ_SUCCESS) { listener->is_registered = PJ_FALSE; goto on_error; } if (has_listener) { PJ_LOG(4,(listener->factory.obj_name, "SIP TLS listener is ready for incoming connections " "at %.*s:%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port)); } else { PJ_LOG(4,(listener->factory.obj_name, "SIP TLS is ready " "(client only)")); } /* Return the pointer to user */ if (p_factory) *p_factory = &listener->factory; return PJ_SUCCESS; on_error: lis_destroy(&listener->factory); return status; } /* Clean up listener resources */ static void lis_on_destroy(void *arg) { struct tls_listener *listener = (struct tls_listener*)arg; if (listener->factory.lock) { pj_lock_destroy(listener->factory.lock); listener->factory.lock = NULL; } if (listener->factory.pool) { pj_pool_t *pool = listener->factory.pool; PJ_LOG(4,(listener->factory.obj_name, "SIP TLS transport destroyed")); listener->factory.pool = NULL; pj_pool_release(pool); } } /* This callback is called by transport manager to destroy listener */ static pj_status_t lis_destroy(pjsip_tpfactory *factory) { struct tls_listener *listener = (struct tls_listener *)factory; if (listener->is_registered) { pjsip_tpmgr_unregister_tpfactory(listener->tpmgr, &listener->factory); listener->is_registered = PJ_FALSE; } if (listener->ssock) { pj_ssl_sock_close(listener->ssock); listener->ssock = NULL; } if (listener->grp_lock) { pj_grp_lock_t *grp_lock = listener->grp_lock; listener->grp_lock = NULL; pj_grp_lock_dec_ref(grp_lock); /* Listener may have been deleted at this point */ } else { lis_on_destroy(listener); } return PJ_SUCCESS; } /***************************************************************************/ /* * TLS Transport */ /* * Prototypes. */ /* Called by transport manager to send message */ static pj_status_t tls_send_msg(pjsip_transport *transport, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, pjsip_transport_callback callback); /* Called by transport manager to shutdown */ static pj_status_t tls_shutdown(pjsip_transport *transport); /* Called by transport manager to destroy transport */ static pj_status_t tls_destroy_transport(pjsip_transport *transport); /* Utility to destroy transport */ static pj_status_t tls_destroy(pjsip_transport *transport, pj_status_t reason); /* Callback when connect completes */ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock, pj_status_t status); /* TLS keep-alive timer callback */ static void tls_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e); /* * Common function to create TLS transport, called when pending accept() and * pending connect() complete. */ static pj_status_t tls_create( struct tls_listener *listener, pj_pool_t *pool, pj_ssl_sock_t *ssock, pj_bool_t is_server, const pj_sockaddr *local, const pj_sockaddr *remote, const pj_str_t *remote_name, struct tls_transport **p_tls) { struct tls_transport *tls; const pj_str_t ka_pkt = PJSIP_TLS_KEEP_ALIVE_DATA; char print_addr[PJ_INET6_ADDRSTRLEN+10]; pj_status_t status; PJ_ASSERT_RETURN(listener && ssock && local && remote && p_tls, PJ_EINVAL); if (pool == NULL) { pool = pjsip_endpt_create_pool(listener->endpt, "tls", POOL_TP_INIT, POOL_TP_INC); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); } /* * Create and initialize basic transport structure. */ tls = PJ_POOL_ZALLOC_T(pool, struct tls_transport); tls->is_server = is_server; tls->verify_server = listener->tls_setting.verify_server; pj_list_init(&tls->delayed_list); tls->base.pool = pool; pj_ansi_snprintf(tls->base.obj_name, PJ_MAX_OBJ_NAME, (is_server ? "tlss%p" :"tlsc%p"), tls); status = pj_atomic_create(pool, 0, &tls->base.ref_cnt); if (status != PJ_SUCCESS) { goto on_error; } status = pj_lock_create_recursive_mutex(pool, "tls", &tls->base.lock); if (status != PJ_SUCCESS) { goto on_error; } if (remote_name) pj_strdup(pool, &tls->remote_name, remote_name); tls->base.key.type = listener->factory.type; pj_sockaddr_cp(&tls->base.key.rem_addr, remote); tls->base.type_name = (char*)pjsip_transport_get_type_name( (pjsip_transport_type_e)tls->base.key.type); tls->base.flag = pjsip_transport_get_flag_from_type( (pjsip_transport_type_e)tls->base.key.type); tls->base.info = (char*) pj_pool_alloc(pool, 64); pj_ansi_snprintf(tls->base.info, 64, "%s to %s", tls->base.type_name, pj_sockaddr_print(remote, print_addr, sizeof(print_addr), 3)); tls->base.addr_len = pj_sockaddr_get_len(remote); tls->base.dir = is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING; /* Set initial local address */ if (!pj_sockaddr_has_addr(local)) { pj_sockaddr_cp(&tls->base.local_addr, &listener->factory.local_addr); } else { pj_sockaddr_cp(&tls->base.local_addr, local); } sockaddr_to_host_port(pool, &tls->base.local_name, &tls->base.local_addr); if (tls->remote_name.slen) { tls->base.remote_name.host = tls->remote_name; tls->base.remote_name.port = pj_sockaddr_get_port(remote); } else { sockaddr_to_host_port(pool, &tls->base.remote_name, remote); } tls->base.endpt = listener->endpt; tls->base.tpmgr = listener->tpmgr; tls->base.send_msg = &tls_send_msg; tls->base.do_shutdown = &tls_shutdown; tls->base.destroy = &tls_destroy_transport; tls->ssock = ssock; /* Register transport to transport manager */ status = pjsip_transport_register(listener->tpmgr, &tls->base); if (status != PJ_SUCCESS) { goto on_error; } tls->is_registered = PJ_TRUE; /* Initialize keep-alive timer */ tls->ka_timer.user_data = (void*)tls; tls->ka_timer.cb = &tls_keep_alive_timer; pj_ioqueue_op_key_init(&tls->ka_op_key.key, sizeof(pj_ioqueue_op_key_t)); pj_strdup(tls->base.pool, &tls->ka_pkt, &ka_pkt); /* Done setting up basic transport. */ *p_tls = tls; PJ_LOG(4,(tls->base.obj_name, "TLS %s transport created", (tls->is_server ? "server" : "client"))); return PJ_SUCCESS; on_error: tls_destroy(&tls->base, status); return status; } /* Flush all delayed transmision once the socket is connected. */ static void tls_flush_pending_tx(struct tls_transport *tls) { pj_time_val now; pj_gettickcount(&now); pj_lock_acquire(tls->base.lock); while (!pj_list_empty(&tls->delayed_list)) { struct delayed_tdata *pending_tx; pjsip_tx_data *tdata; pj_ioqueue_op_key_t *op_key; pj_ssize_t size; pj_status_t status; pending_tx = tls->delayed_list.next; pj_list_erase(pending_tx); tdata = pending_tx->tdata_op_key->tdata; op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key; if (pending_tx->timeout.sec > 0 && PJ_TIME_VAL_GT(now, pending_tx->timeout)) { continue; } /* send! */ size = tdata->buf.cur - tdata->buf.start; status = pj_ssl_sock_send(tls->ssock, op_key, tdata->buf.start, &size, 0); if (status != PJ_EPENDING) { pj_lock_release(tls->base.lock); on_data_sent(tls->ssock, op_key, size); pj_lock_acquire(tls->base.lock); } } pj_lock_release(tls->base.lock); } /* Called by transport manager to destroy transport */ static pj_status_t tls_destroy_transport(pjsip_transport *transport) { struct tls_transport *tls = (struct tls_transport*)transport; /* Transport would have been unregistered by now since this callback * is called by transport manager. */ tls->is_registered = PJ_FALSE; return tls_destroy(transport, tls->close_reason); } /* Clean up TLS resources */ static void tls_on_destroy(void *arg) { struct tls_transport *tls = (struct tls_transport*)arg; if (tls->rdata.tp_info.pool) { pj_pool_release(tls->rdata.tp_info.pool); tls->rdata.tp_info.pool = NULL; } if (tls->base.lock) { pj_lock_destroy(tls->base.lock); tls->base.lock = NULL; } if (tls->base.ref_cnt) { pj_atomic_destroy(tls->base.ref_cnt); tls->base.ref_cnt = NULL; } if (tls->base.pool) { pj_pool_t *pool; if (tls->close_reason != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(tls->close_reason, errmsg, sizeof(errmsg)); PJ_LOG(4,(tls->base.obj_name, "TLS transport destroyed with reason %d: %s", tls->close_reason, errmsg)); } else { PJ_LOG(4,(tls->base.obj_name, "TLS transport destroyed normally")); } pool = tls->base.pool; tls->base.pool = NULL; pj_pool_release(pool); } } /* Destroy TLS transport */ static pj_status_t tls_destroy(pjsip_transport *transport, pj_status_t reason) { struct tls_transport *tls = (struct tls_transport*)transport; if (tls->close_reason == 0) tls->close_reason = reason; if (tls->is_registered) { tls->is_registered = PJ_FALSE; pjsip_transport_destroy(transport); /* pjsip_transport_destroy will recursively call this function * again. */ return PJ_SUCCESS; } /* Mark transport as closing */ tls->is_closing = PJ_TRUE; /* Stop keep-alive timer. */ if (tls->ka_timer.id) { pjsip_endpt_cancel_timer(tls->base.endpt, &tls->ka_timer); tls->ka_timer.id = PJ_FALSE; } /* Cancel all delayed transmits */ while (!pj_list_empty(&tls->delayed_list)) { struct delayed_tdata *pending_tx; pj_ioqueue_op_key_t *op_key; pending_tx = tls->delayed_list.next; pj_list_erase(pending_tx); op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key; on_data_sent(tls->ssock, op_key, -reason); } if (tls->ssock) { pj_ssl_sock_close(tls->ssock); tls->ssock = NULL; } if (tls->grp_lock) { pj_grp_lock_t *grp_lock = tls->grp_lock; tls->grp_lock = NULL; pj_grp_lock_dec_ref(grp_lock); /* Transport may have been deleted at this point */ } else { tls_on_destroy(tls); } return PJ_SUCCESS; } /* * This utility function creates receive data buffers and start * asynchronous recv() operations from the socket. It is called after * accept() or connect() operation complete. */ static pj_status_t tls_start_read(struct tls_transport *tls) { pj_pool_t *pool; pj_uint32_t size; pj_sockaddr *rem_addr; void *readbuf[1]; pj_status_t status; /* Init rdata */ pool = pjsip_endpt_create_pool(tls->base.endpt, "rtd%p", PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC); if (!pool) { tls_perror(tls->base.obj_name, "Unable to create pool", PJ_ENOMEM); return PJ_ENOMEM; } tls->rdata.tp_info.pool = pool; tls->rdata.tp_info.transport = &tls->base; tls->rdata.tp_info.tp_data = tls; tls->rdata.tp_info.op_key.rdata = &tls->rdata; pj_ioqueue_op_key_init(&tls->rdata.tp_info.op_key.op_key, sizeof(pj_ioqueue_op_key_t)); tls->rdata.pkt_info.src_addr = tls->base.key.rem_addr; tls->rdata.pkt_info.src_addr_len = sizeof(tls->rdata.pkt_info.src_addr); rem_addr = &tls->base.key.rem_addr; pj_sockaddr_print(rem_addr, tls->rdata.pkt_info.src_name, sizeof(tls->rdata.pkt_info.src_name), 0); tls->rdata.pkt_info.src_port = pj_sockaddr_get_port(rem_addr); size = sizeof(tls->rdata.pkt_info.packet); readbuf[0] = tls->rdata.pkt_info.packet; status = pj_ssl_sock_start_read2(tls->ssock, tls->base.pool, size, readbuf, 0); if (status != PJ_SUCCESS && status != PJ_EPENDING) { PJ_LOG(4, (tls->base.obj_name, "pj_ssl_sock_start_read() error, status=%d", status)); return status; } return PJ_SUCCESS; } /* This callback is called by transport manager for the TLS factory * to create outgoing transport to the specified destination. */ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, pjsip_tpmgr *mgr, pjsip_endpoint *endpt, const pj_sockaddr *rem_addr, int addr_len, pjsip_tx_data *tdata, pjsip_transport **p_transport) { struct tls_listener *listener; struct tls_transport *tls; int sip_ssl_method; pj_uint32_t sip_ssl_proto; pj_pool_t *pool; pj_grp_lock_t *glock; pj_ssl_sock_t *ssock; pj_ssl_sock_param ssock_param; pj_sockaddr local_addr; pj_str_t remote_name; pj_status_t status; /* Sanity checks */ PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr && addr_len && p_transport, PJ_EINVAL); /* Check that address is a sockaddr_in or sockaddr_in6*/ PJ_ASSERT_RETURN((rem_addr->addr.sa_family == pj_AF_INET() && addr_len == sizeof(pj_sockaddr_in)) || (rem_addr->addr.sa_family == pj_AF_INET6() && addr_len == sizeof(pj_sockaddr_in6)), PJ_EINVAL); listener = (struct tls_listener*)factory; pool = pjsip_endpt_create_pool(listener->endpt, "tls", POOL_TP_INIT, POOL_TP_INC); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); /* Get remote host name from tdata */ if (tdata) remote_name = tdata->dest_info.name; else pj_bzero(&remote_name, sizeof(remote_name)); /* Build SSL socket param */ pj_ssl_sock_param_default(&ssock_param); ssock_param.sock_af = (factory->type & PJSIP_TRANSPORT_IPV6) ? pj_AF_INET6() : pj_AF_INET(); ssock_param.cb.on_connect_complete = &on_connect_complete; ssock_param.cb.on_data_read = &on_data_read; ssock_param.cb.on_data_sent = &on_data_sent; ssock_param.async_cnt = 1; ssock_param.ioqueue = pjsip_endpt_get_ioqueue(listener->endpt); ssock_param.server_name = remote_name; ssock_param.timeout = listener->tls_setting.timeout; ssock_param.user_data = NULL; /* pending, must be set later */ ssock_param.verify_peer = PJ_FALSE; /* avoid SSL socket closing the socket * due to verification error */ if (ssock_param.send_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.send_buffer_size = PJSIP_MAX_PKT_LEN; if (ssock_param.read_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.read_buffer_size = PJSIP_MAX_PKT_LEN; ssock_param.ciphers_num = listener->tls_setting.ciphers_num; ssock_param.ciphers = listener->tls_setting.ciphers; ssock_param.qos_type = listener->tls_setting.qos_type; ssock_param.qos_ignore_error = listener->tls_setting.qos_ignore_error; pj_memcpy(&ssock_param.qos_params, &listener->tls_setting.qos_params, sizeof(ssock_param.qos_params)); ssock_param.sockopt_ignore_error = listener->tls_setting.sockopt_ignore_error; /* Copy the sockopt */ pj_memcpy(&ssock_param.sockopt_params, &listener->tls_setting.sockopt_params, sizeof(listener->tls_setting.sockopt_params)); sip_ssl_method = listener->tls_setting.method; sip_ssl_proto = listener->tls_setting.proto; ssock_param.proto = ssl_get_proto(sip_ssl_method, sip_ssl_proto); /* Create group lock */ status = pj_grp_lock_create(pool, NULL, &glock); if (status != PJ_SUCCESS) return status; ssock_param.grp_lock = glock; status = pj_ssl_sock_create(pool, &ssock_param, &ssock); if (status != PJ_SUCCESS) { pj_grp_lock_destroy(glock); return status; } /* Apply SSL certificate */ if (listener->cert) { status = pj_ssl_sock_set_certificate(ssock, pool, listener->cert); if (status != PJ_SUCCESS) { pj_grp_lock_destroy(glock); return status; } } /* Initially set bind address to listener's bind address */ pj_sockaddr_init(listener->bound_addr.addr.sa_family, &local_addr, NULL, 0); pj_sockaddr_copy_addr(&local_addr, &listener->bound_addr); /* Create the transport descriptor */ status = tls_create(listener, pool, ssock, PJ_FALSE, &local_addr, rem_addr, &remote_name, &tls); if (status != PJ_SUCCESS) { pj_grp_lock_destroy(glock); return status; } /* Set the "pending" SSL socket user data */ pj_ssl_sock_set_user_data(tls->ssock, tls); /* Set up the group lock */ tls->grp_lock = glock; pj_grp_lock_add_ref(tls->grp_lock); pj_grp_lock_add_handler(tls->grp_lock, pool, tls, &tls_on_destroy); /* Start asynchronous connect() operation */ tls->has_pending_connect = PJ_TRUE; status = pj_ssl_sock_start_connect(tls->ssock, tls->base.pool, (pj_sockaddr_t*)&local_addr, (pj_sockaddr_t*)rem_addr, addr_len); if (status == PJ_SUCCESS) { on_connect_complete(tls->ssock, PJ_SUCCESS); } else if (status != PJ_EPENDING) { tls_destroy(&tls->base, status); return status; } if (tls->has_pending_connect) { pj_ssl_sock_info info; /* Update local address, just in case local address currently set is * different now that asynchronous connect() is started. */ /* Retrieve the bound address */ status = pj_ssl_sock_get_info(tls->ssock, &info); if (status == PJ_SUCCESS) { pj_uint16_t new_port; new_port = pj_sockaddr_get_port((pj_sockaddr_t*)&info.local_addr); if (pj_sockaddr_has_addr((pj_sockaddr_t*)&info.local_addr)) { /* Update sockaddr */ pj_sockaddr_cp((pj_sockaddr_t*)&tls->base.local_addr, (pj_sockaddr_t*)&info.local_addr); } else if (new_port && new_port != pj_sockaddr_get_port( (pj_sockaddr_t*)&tls->base.local_addr)) { /* Update port only */ pj_sockaddr_set_port(&tls->base.local_addr, new_port); } sockaddr_to_host_port(tls->base.pool, &tls->base.local_name, &tls->base.local_addr); } PJ_LOG(4,(tls->base.obj_name, "TLS transport %.*s:%d is connecting to %.*s:%d...", (int)tls->base.local_name.host.slen, tls->base.local_name.host.ptr, tls->base.local_name.port, (int)tls->base.remote_name.host.slen, tls->base.remote_name.host.ptr, tls->base.remote_name.port)); } /* Done */ *p_transport = &tls->base; return PJ_SUCCESS; } /* * This callback is called by SSL socket when pending accept() operation * has completed. */ static pj_bool_t on_accept_complete(pj_ssl_sock_t *ssock, pj_ssl_sock_t *new_ssock, const pj_sockaddr_t *src_addr, int src_addr_len) { struct tls_listener *listener; struct tls_transport *tls; pj_ssl_sock_info ssl_info; char addr[PJ_INET6_ADDRSTRLEN+10]; pjsip_tp_state_callback state_cb; pj_sockaddr tmp_src_addr; pj_bool_t is_shutdown; pj_status_t status; PJ_UNUSED_ARG(src_addr_len); listener = (struct tls_listener*) pj_ssl_sock_get_user_data(ssock); PJ_ASSERT_RETURN(new_ssock, PJ_TRUE); if (!listener->is_registered) return PJ_FALSE; PJ_LOG(4,(listener->factory.obj_name, "TLS listener %.*s:%d: got incoming TLS connection " "from %s, sock=%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port, pj_sockaddr_print(src_addr, addr, sizeof(addr), 3), new_ssock)); /* Retrieve SSL socket info, close the socket if this is failed * as the SSL socket info availability is rather critical here. */ status = pj_ssl_sock_get_info(new_ssock, &ssl_info); if (status != PJ_SUCCESS) { pj_ssl_sock_close(new_ssock); return PJ_TRUE; } /* Copy to larger buffer, just in case */ pj_bzero(&tmp_src_addr, sizeof(tmp_src_addr)); pj_sockaddr_cp(&tmp_src_addr, src_addr); /* * Incoming connection! * Create TLS transport for the new socket. */ status = tls_create( listener, NULL, new_ssock, PJ_TRUE, &ssl_info.local_addr, &tmp_src_addr, NULL, &tls); if (status != PJ_SUCCESS) return PJ_TRUE; /* Set the "pending" SSL socket user data */ pj_ssl_sock_set_user_data(new_ssock, tls); /* Set up the group lock */ if (ssl_info.grp_lock) { tls->grp_lock = ssl_info.grp_lock; pj_grp_lock_add_ref(tls->grp_lock); pj_grp_lock_add_handler(tls->grp_lock, tls->base.pool, tls, &tls_on_destroy); } /* Prevent immediate transport destroy as application may access it * (getting info, etc) in transport state notification callback. */ pjsip_transport_add_ref(&tls->base); /* If there is verification error and verification is mandatory, shutdown * and destroy the transport. */ if (ssl_info.verify_status && listener->tls_setting.verify_client) { if (tls->close_reason == PJ_SUCCESS) tls->close_reason = PJSIP_TLS_ECERTVERIF; pjsip_transport_shutdown(&tls->base); } /* Notify transport state to application */ state_cb = pjsip_tpmgr_get_state_cb(tls->base.tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pjsip_tls_state_info tls_info; pjsip_transport_state tp_state; /* Init transport state info */ pj_bzero(&tls_info, sizeof(tls_info)); pj_bzero(&state_info, sizeof(state_info)); tls_info.ssl_sock_info = &ssl_info; state_info.ext_info = &tls_info; /* Set transport state based on verification status */ if (ssl_info.verify_status && listener->tls_setting.verify_client) { tp_state = PJSIP_TP_STATE_DISCONNECTED; state_info.status = PJSIP_TLS_ECERTVERIF; } else { tp_state = PJSIP_TP_STATE_CONNECTED; state_info.status = PJ_SUCCESS; } (*state_cb)(&tls->base, tp_state, &state_info); } /* Release transport reference. If transport is shutting down, it may * get destroyed here. */ is_shutdown = tls->base.is_shutdown; pjsip_transport_dec_ref(&tls->base); if (is_shutdown) return PJ_TRUE; status = tls_start_read(tls); if (status != PJ_SUCCESS) { PJ_LOG(3,(tls->base.obj_name, "New transport cancelled")); tls_init_shutdown(tls, status); tls_destroy(&tls->base, status); } else { /* Start keep-alive timer */ if (pjsip_cfg()->tls.keep_alive_interval) { pj_time_val delay = {pjsip_cfg()->tls.keep_alive_interval, 0}; pjsip_endpt_schedule_timer(listener->endpt, &tls->ka_timer, &delay); tls->ka_timer.id = PJ_TRUE; pj_gettimeofday(&tls->last_activity); } } return PJ_TRUE; } /* * Callback from ioqueue when packet is sent. */ static pj_bool_t on_data_sent(pj_ssl_sock_t *ssock, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_sent) { struct tls_transport *tls = (struct tls_transport*) pj_ssl_sock_get_user_data(ssock); pjsip_tx_data_op_key *tdata_op_key = (pjsip_tx_data_op_key*)op_key; /* Note that op_key may be the op_key from keep-alive, thus * it will not have tdata etc. */ tdata_op_key->tdata = NULL; if (tdata_op_key->callback) { /* * Notify sip_transport.c that packet has been sent. */ if (bytes_sent == 0) bytes_sent = -PJ_RETURN_OS_ERROR(OSERR_ENOTCONN); tdata_op_key->callback(&tls->base, tdata_op_key->token, bytes_sent); /* Mark last activity time */ pj_gettimeofday(&tls->last_activity); } /* Check for error/closure */ if (bytes_sent <= 0) { pj_status_t status; PJ_LOG(5,(tls->base.obj_name, "TLS send() error, sent=%d", bytes_sent)); status = (bytes_sent == 0) ? PJ_RETURN_OS_ERROR(OSERR_ENOTCONN) : (pj_status_t)-bytes_sent; tls_init_shutdown(tls, status); return PJ_FALSE; } return PJ_TRUE; } /* * This callback is called by transport manager to send SIP message */ static pj_status_t tls_send_msg(pjsip_transport *transport, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, pjsip_transport_callback callback) { struct tls_transport *tls = (struct tls_transport*)transport; pj_ssize_t size; pj_bool_t delayed = PJ_FALSE; pj_status_t status = PJ_SUCCESS; /* Sanity check */ PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL); /* Check that there's no pending operation associated with the tdata */ PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX); /* Check the address is supported */ PJ_ASSERT_RETURN(rem_addr && (addr_len==sizeof(pj_sockaddr_in) || addr_len==sizeof(pj_sockaddr_in6)), PJ_EINVAL); /* Init op key. */ tdata->op_key.tdata = tdata; tdata->op_key.token = token; tdata->op_key.callback = callback; /* If asynchronous connect() has not completed yet, just put the * transmit data in the pending transmission list since we can not * use the socket yet. */ if (tls->has_pending_connect) { /* * Looks like connect() is still in progress. Check again (this time * with holding the lock) to be sure. */ pj_lock_acquire(tls->base.lock); if (tls->has_pending_connect) { struct delayed_tdata *delayed_tdata; /* * connect() is still in progress. Put the transmit data to * the delayed list. * Starting from #1583 (https://trac.pjsip.org/repos/ticket/1583), * we also add timeout value for the transmit data. When the * connect() is completed, the timeout value will be checked to * determine whether the transmit data needs to be sent. */ delayed_tdata = PJ_POOL_ZALLOC_T(tdata->pool, struct delayed_tdata); delayed_tdata->tdata_op_key = &tdata->op_key; if (tdata->msg && tdata->msg->type == PJSIP_REQUEST_MSG) { pj_gettickcount(&delayed_tdata->timeout); delayed_tdata->timeout.msec += pjsip_cfg()->tsx.td; pj_time_val_normalize(&delayed_tdata->timeout); } pj_list_push_back(&tls->delayed_list, delayed_tdata); status = PJ_EPENDING; /* Prevent pj_ioqueue_send() to be called below */ delayed = PJ_TRUE; } pj_lock_release(tls->base.lock); } if (!delayed) { /* * Transport is ready to go. Send the packet to ioqueue to be * sent asynchronously. */ size = tdata->buf.cur - tdata->buf.start; status = pj_ssl_sock_send(tls->ssock, (pj_ioqueue_op_key_t*)&tdata->op_key, tdata->buf.start, &size, 0); if (status != PJ_EPENDING) { /* Not pending (could be immediate success or error) */ tdata->op_key.tdata = NULL; /* Shutdown transport on closure/errors */ if (size <= 0) { PJ_LOG(5,(tls->base.obj_name, "TLS send() error, sent=%d", size)); if (status == PJ_SUCCESS) status = PJ_RETURN_OS_ERROR(OSERR_ENOTCONN); tls_init_shutdown(tls, status); } } } return status; } /* * This callback is called by transport manager to shutdown transport. */ static pj_status_t tls_shutdown(pjsip_transport *transport) { struct tls_transport *tls = (struct tls_transport*)transport; /* Stop keep-alive timer. */ if (tls->ka_timer.id) { pjsip_endpt_cancel_timer(tls->base.endpt, &tls->ka_timer); tls->ka_timer.id = PJ_FALSE; } return PJ_SUCCESS; } /* * Callback from ioqueue that an incoming data is received from the socket. */ static pj_bool_t on_data_read(pj_ssl_sock_t *ssock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder) { enum { MAX_IMMEDIATE_PACKET = 10 }; struct tls_transport *tls; pjsip_rx_data *rdata; PJ_UNUSED_ARG(data); tls = (struct tls_transport*) pj_ssl_sock_get_user_data(ssock); rdata = &tls->rdata; /* Don't do anything if transport is closing. */ if (tls->is_closing) { tls->is_closing++; return PJ_FALSE; } /* Houston, we have packet! Report the packet to transport manager * to be parsed. */ if (status == PJ_SUCCESS) { pj_size_t size_eaten; /* Mark this as an activity */ pj_gettimeofday(&tls->last_activity); pj_assert((void*)rdata->pkt_info.packet == data); /* Init pkt_info part. */ rdata->pkt_info.len = size; rdata->pkt_info.zero = 0; pj_gettimeofday(&rdata->pkt_info.timestamp); /* Report to transport manager. * The transport manager will tell us how many bytes of the packet * have been processed (as valid SIP message). */ size_eaten = pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, rdata); pj_assert(size_eaten <= (pj_size_t)rdata->pkt_info.len); /* Move unprocessed data to the front of the buffer */ *remainder = size - size_eaten; if (*remainder > 0 && *remainder != size) { pj_memmove(rdata->pkt_info.packet, rdata->pkt_info.packet + size_eaten, *remainder); } } else { /* Transport is closed */ PJ_LOG(4,(tls->base.obj_name, "TLS connection closed")); tls_init_shutdown(tls, status); return PJ_FALSE; } /* Reset pool. */ pj_pool_reset(rdata->tp_info.pool); return PJ_TRUE; } /* * Callback from ioqueue when asynchronous connect() operation completes. */ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock, pj_status_t status) { struct tls_transport *tls; pj_ssl_sock_info ssl_info; pj_sockaddr addr, *tp_addr; pjsip_tp_state_callback state_cb; pj_bool_t is_shutdown; tls = (struct tls_transport*) pj_ssl_sock_get_user_data(ssock); /* If transport is being shutdown/destroyed, proceed as error connect. * Note that it is important to notify application via on_data_sent() * as otherwise the transport reference counter may never reach zero * (see #1898). */ if ((tls->base.is_shutdown || tls->base.is_destroying) && status == PJ_SUCCESS) { status = PJ_ECANCELLED; } /* Check connect() status */ if (status != PJ_SUCCESS) { tls_perror(tls->base.obj_name, "TLS connect() error", status); /* Cancel all delayed transmits */ while (!pj_list_empty(&tls->delayed_list)) { struct delayed_tdata *pending_tx; pj_ioqueue_op_key_t *op_key; pending_tx = tls->delayed_list.next; pj_list_erase(pending_tx); op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key; on_data_sent(tls->ssock, op_key, -status); } goto on_error; } /* Retrieve SSL socket info, shutdown the transport if this is failed * as the SSL socket info availability is rather critical here. */ status = pj_ssl_sock_get_info(tls->ssock, &ssl_info); if (status != PJ_SUCCESS) goto on_error; /* Update (again) local address, just in case local address currently * set is different now that the socket is connected (could happen * on some systems, like old Win32 probably?). */ tp_addr = &tls->base.local_addr; pj_sockaddr_cp((pj_sockaddr_t*)&addr, (pj_sockaddr_t*)&ssl_info.local_addr); if (pj_sockaddr_cmp(tp_addr, &addr) != 0) { pj_sockaddr_cp(tp_addr, &addr); sockaddr_to_host_port(tls->base.pool, &tls->base.local_name, tp_addr); } /* Server identity verification based on server certificate. */ if (ssl_info.remote_cert_info->version) { pj_str_t *remote_name; pj_ssl_cert_info *serv_cert = ssl_info.remote_cert_info; pj_bool_t matched = PJ_FALSE; unsigned i; /* Remote name may be hostname or IP address */ if (tls->remote_name.slen) remote_name = &tls->remote_name; else remote_name = &tls->base.remote_name.host; /* Start matching remote name with SubjectAltName fields of * server certificate. */ for (i = 0; i < serv_cert->subj_alt_name.cnt && !matched; ++i) { pj_str_t *cert_name = &serv_cert->subj_alt_name.entry[i].name; switch (serv_cert->subj_alt_name.entry[i].type) { case PJ_SSL_CERT_NAME_DNS: case PJ_SSL_CERT_NAME_IP: matched = !pj_stricmp(remote_name, cert_name); break; case PJ_SSL_CERT_NAME_URI: if (pj_strnicmp2(cert_name, "sip:", 4) == 0 || pj_strnicmp2(cert_name, "sips:", 5) == 0) { pj_str_t host_part; char *p; p = pj_strchr(cert_name, ':') + 1; pj_strset(&host_part, p, cert_name->slen - (p - cert_name->ptr)); matched = !pj_stricmp(remote_name, &host_part); } break; default: break; } } /* When still not matched or no SubjectAltName fields in server * certificate, try with Common Name of Subject field. */ if (!matched) { matched = !pj_stricmp(remote_name, &serv_cert->subject.cn); } if (!matched) { if (pj_strnicmp2(&serv_cert->subject.cn, "*.", 2) == 0) { PJ_LOG(1,(tls->base.obj_name, "RFC 5922 (section 7.2) does not allow TLS wildcard " "certificates. Advise your SIP provider, please!")); } ssl_info.verify_status |= PJ_SSL_CERT_EIDENTITY_NOT_MATCH; } } /* Prevent immediate transport destroy as application may access it * (getting info, etc) in transport state notification callback. */ pjsip_transport_add_ref(&tls->base); /* If there is verification error and verification is mandatory, shutdown * and destroy the transport. */ if (ssl_info.verify_status && tls->verify_server) { if (tls->close_reason == PJ_SUCCESS) tls->close_reason = PJSIP_TLS_ECERTVERIF; pjsip_transport_shutdown(&tls->base); } /* Notify transport state to application */ state_cb = pjsip_tpmgr_get_state_cb(tls->base.tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pjsip_tls_state_info tls_info; pjsip_transport_state tp_state; /* Init transport state info */ pj_bzero(&state_info, sizeof(state_info)); pj_bzero(&tls_info, sizeof(tls_info)); state_info.ext_info = &tls_info; tls_info.ssl_sock_info = &ssl_info; /* Set transport state based on verification status */ if (ssl_info.verify_status && tls->verify_server) { tp_state = PJSIP_TP_STATE_DISCONNECTED; state_info.status = PJSIP_TLS_ECERTVERIF; } else { tp_state = PJSIP_TP_STATE_CONNECTED; state_info.status = PJ_SUCCESS; } (*state_cb)(&tls->base, tp_state, &state_info); } /* Release transport reference. If transport is shutting down, it may * get destroyed here. */ is_shutdown = tls->base.is_shutdown; pjsip_transport_dec_ref(&tls->base); if (is_shutdown) { status = tls->close_reason; tls_perror(tls->base.obj_name, "TLS connect() error", status); /* Cancel all delayed transmits */ while (!pj_list_empty(&tls->delayed_list)) { struct delayed_tdata *pending_tx; pj_ioqueue_op_key_t *op_key; pending_tx = tls->delayed_list.next; pj_list_erase(pending_tx); op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key; on_data_sent(tls->ssock, op_key, -status); } return PJ_FALSE; } /* Mark that pending connect() operation has completed. */ tls->has_pending_connect = PJ_FALSE; PJ_LOG(4,(tls->base.obj_name, "TLS transport %.*s:%d is connected to %.*s:%d", (int)tls->base.local_name.host.slen, tls->base.local_name.host.ptr, tls->base.local_name.port, (int)tls->base.remote_name.host.slen, tls->base.remote_name.host.ptr, tls->base.remote_name.port)); /* Start pending read */ status = tls_start_read(tls); if (status != PJ_SUCCESS) goto on_error; /* Flush all pending send operations */ tls_flush_pending_tx(tls); /* Start keep-alive timer */ if (pjsip_cfg()->tls.keep_alive_interval) { pj_time_val delay = { pjsip_cfg()->tls.keep_alive_interval, 0 }; pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer, &delay); tls->ka_timer.id = PJ_TRUE; pj_gettimeofday(&tls->last_activity); } return PJ_TRUE; on_error: tls_init_shutdown(tls, status); return PJ_FALSE; } /* Transport keep-alive timer callback */ static void tls_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e) { struct tls_transport *tls = (struct tls_transport*) e->user_data; pj_time_val delay; pj_time_val now; pj_ssize_t size; pj_status_t status; PJ_UNUSED_ARG(th); tls->ka_timer.id = PJ_TRUE; pj_gettimeofday(&now); PJ_TIME_VAL_SUB(now, tls->last_activity); if (now.sec > 0 && now.sec < pjsip_cfg()->tls.keep_alive_interval) { /* There has been activity, so don't send keep-alive */ delay.sec = pjsip_cfg()->tls.keep_alive_interval - now.sec; delay.msec = 0; pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer, &delay); tls->ka_timer.id = PJ_TRUE; return; } PJ_LOG(5,(tls->base.obj_name, "Sending %d byte(s) keep-alive to %.*s:%d", (int)tls->ka_pkt.slen, (int)tls->base.remote_name.host.slen, tls->base.remote_name.host.ptr, tls->base.remote_name.port)); /* Send the data */ size = tls->ka_pkt.slen; status = pj_ssl_sock_send(tls->ssock, &tls->ka_op_key.key, tls->ka_pkt.ptr, &size, 0); if (status != PJ_SUCCESS && status != PJ_EPENDING) { tls_perror(tls->base.obj_name, "Error sending keep-alive packet", status); tls_init_shutdown(tls, status); return; } /* Register next keep-alive */ delay.sec = pjsip_cfg()->tls.keep_alive_interval; delay.msec = 0; pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer, &delay); tls->ka_timer.id = PJ_TRUE; } #endif /* PJSIP_HAS_TLS_TRANSPORT */ ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_transport_udp.c ================================================ /* $Id: sip_transport_udp.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "sip_transport_udp.c" /** * These are the target values for socket send and receive buffer sizes, * respectively. They will be applied to UDP socket with setsockopt(). * When transport failed to set these size, it will decrease it until * sufficiently large number has been successfully set. * * The buffer size is important, especially in WinXP/2000 machines. * Basicly the lower the size, the more packets will be lost (dropped?) * when we're sending (receiving?) packets in large volumes. * * The figure here is taken based on my experiment on WinXP/2000 machine, * and with this value, the rate of dropped packet is about 8% when * sending 1800 requests simultaneously (percentage taken as average * after 50K requests or so). * * More experiments are needed probably. */ /* 2010/01/14 * Too many people complained about seeing "Error setting SNDBUF" log, * so lets just remove this. People who want to have SNDBUF set can * still do so by declaring these two macros in config_site.h */ #ifndef PJSIP_UDP_SO_SNDBUF_SIZE /*# define PJSIP_UDP_SO_SNDBUF_SIZE (24*1024*1024)*/ # define PJSIP_UDP_SO_SNDBUF_SIZE 0 #endif #ifndef PJSIP_UDP_SO_RCVBUF_SIZE /*# define PJSIP_UDP_SO_RCVBUF_SIZE (24*1024*1024)*/ # define PJSIP_UDP_SO_RCVBUF_SIZE 0 #endif /* Struct udp_transport "inherits" struct pjsip_transport */ struct udp_transport { pjsip_transport base; pj_sock_t sock; pj_ioqueue_key_t *key; int rdata_cnt; pjsip_rx_data **rdata; int is_closing; pj_bool_t is_paused; /* Group lock to be used by UDP transport and ioqueue key */ pj_grp_lock_t *grp_lock; }; /* * Initialize transport's receive buffer from the specified pool. */ static void init_rdata(struct udp_transport *tp, unsigned rdata_index, pj_pool_t *pool, pjsip_rx_data **p_rdata) { pjsip_rx_data *rdata; /* Reset pool. */ //note: already done by caller //pj_pool_reset(pool); rdata = PJ_POOL_ZALLOC_T(pool, pjsip_rx_data); /* Init tp_info part. */ rdata->tp_info.pool = pool; rdata->tp_info.transport = &tp->base; rdata->tp_info.tp_data = (void*)(pj_ssize_t)rdata_index; rdata->tp_info.op_key.rdata = rdata; pj_ioqueue_op_key_init(&rdata->tp_info.op_key.op_key, sizeof(pj_ioqueue_op_key_t)); tp->rdata[rdata_index] = rdata; if (p_rdata) *p_rdata = rdata; } /* * udp_on_read_complete() * * This is callback notification from ioqueue that a pending recvfrom() * operation has completed. */ static void udp_on_read_complete( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { /* See https://trac.pjsip.org/repos/ticket/1197 */ enum { MAX_IMMEDIATE_PACKET = 50 }; pjsip_rx_data_op_key *rdata_op_key = (pjsip_rx_data_op_key*) op_key; pjsip_rx_data *rdata = rdata_op_key->rdata; struct udp_transport *tp = (struct udp_transport*)rdata->tp_info.transport; int i; pj_status_t status; /* Don't do anything if transport is closing. */ if (tp->is_closing) { tp->is_closing++; return; } /* Don't do anything if transport is being paused. */ if (tp->is_paused) return; /* * The idea of the loop is to process immediate data received by * pj_ioqueue_recvfrom(), as long as i < MAX_IMMEDIATE_PACKET. When * i is >= MAX_IMMEDIATE_PACKET, we force the recvfrom() operation to * complete asynchronously, to allow other sockets to get their data. */ for (i=0;; ++i) { enum { MIN_SIZE = 32 }; pj_uint32_t flags; /* Report the packet to transport manager. Only do so if packet size * is relatively big enough for a SIP packet. */ if (bytes_read > MIN_SIZE) { pj_ssize_t size_eaten; const pj_sockaddr *src_addr = &rdata->pkt_info.src_addr; /* Init pkt_info part. */ rdata->pkt_info.len = bytes_read; rdata->pkt_info.zero = 0; pj_gettimeofday(&rdata->pkt_info.timestamp); if (src_addr->addr.sa_family == pj_AF_INET()) { pj_ansi_strcpy(rdata->pkt_info.src_name, pj_inet_ntoa(src_addr->ipv4.sin_addr)); rdata->pkt_info.src_port = pj_ntohs(src_addr->ipv4.sin_port); } else { pj_inet_ntop(pj_AF_INET6(), pj_sockaddr_get_addr(&rdata->pkt_info.src_addr), rdata->pkt_info.src_name, sizeof(rdata->pkt_info.src_name)); rdata->pkt_info.src_port = pj_ntohs(src_addr->ipv6.sin6_port); } size_eaten = pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, rdata); if (size_eaten < 0) { pj_assert(!"It shouldn't happen!"); size_eaten = rdata->pkt_info.len; } /* Since this is UDP, the whole buffer is the message. */ rdata->pkt_info.len = 0; } else if (bytes_read <= MIN_SIZE) { /* TODO: */ } else if (-bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) && -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) { /* Report error to endpoint. */ PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt, rdata->tp_info.transport->obj_name, (pj_status_t)-bytes_read, "Warning: pj_ioqueue_recvfrom()" " callback error")); } if (i >= MAX_IMMEDIATE_PACKET) { /* Force ioqueue_recvfrom() to return PJ_EPENDING */ flags = PJ_IOQUEUE_ALWAYS_ASYNC; } else { flags = 0; } /* Reset pool. * Need to copy rdata fields to temp variable because they will * be invalid after pj_pool_reset(). */ { pj_pool_t *rdata_pool = rdata->tp_info.pool; struct udp_transport *rdata_tp ; unsigned rdata_index; rdata_tp = (struct udp_transport*)rdata->tp_info.transport; rdata_index = (unsigned)(unsigned long)(pj_ssize_t) rdata->tp_info.tp_data; pj_pool_reset(rdata_pool); init_rdata(rdata_tp, rdata_index, rdata_pool, &rdata); /* Change some vars to point to new location after * pool reset. */ op_key = &rdata->tp_info.op_key.op_key; } /* Only read next packet if transport is not being paused. This * check handles the case where transport is paused while endpoint * is still processing a SIP message. */ if (tp->is_paused) return; /* Read next packet. */ bytes_read = sizeof(rdata->pkt_info.packet); rdata->pkt_info.src_addr_len = sizeof(rdata->pkt_info.src_addr); status = pj_ioqueue_recvfrom(key, op_key, rdata->pkt_info.packet, &bytes_read, flags, &rdata->pkt_info.src_addr, &rdata->pkt_info.src_addr_len); if (status == PJ_SUCCESS) { /* Continue loop. */ pj_assert(i < MAX_IMMEDIATE_PACKET); } else if (status == PJ_EPENDING) { break; } else { if (i < MAX_IMMEDIATE_PACKET) { /* Report error to endpoint if this is not EWOULDBLOCK error.*/ if (status != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) && status != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && status != PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) { PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt, rdata->tp_info.transport->obj_name, status, "Warning: pj_ioqueue_recvfrom")); } /* Continue loop. */ bytes_read = 0; } else { /* This is fatal error. * Ioqueue operation will stop for this transport! */ PJSIP_ENDPT_LOG_ERROR((rdata->tp_info.transport->endpt, rdata->tp_info.transport->obj_name, status, "FATAL: pj_ioqueue_recvfrom() error, " "UDP transport stopping! Error")); break; } } } } /* * udp_on_write_complete() * * This is callback notification from ioqueue that a pending sendto() * operation has completed. */ static void udp_on_write_complete( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_sent) { struct udp_transport *tp = (struct udp_transport*) pj_ioqueue_get_user_data(key); pjsip_tx_data_op_key *tdata_op_key = (pjsip_tx_data_op_key*)op_key; tdata_op_key->tdata = NULL; if (tdata_op_key->callback) { tdata_op_key->callback(&tp->base, tdata_op_key->token, bytes_sent); } } /* * udp_send_msg() * * This function is called by transport manager (by transport->send_msg()) * to send outgoing message. */ static pj_status_t udp_send_msg( pjsip_transport *transport, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, pjsip_transport_callback callback) { struct udp_transport *tp = (struct udp_transport*)transport; pj_ssize_t size; pj_status_t status; PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL); PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX); /* Return error if transport is paused */ if (tp->is_paused) return PJSIP_ETPNOTAVAIL; /* Init op key. */ tdata->op_key.tdata = tdata; tdata->op_key.token = token; tdata->op_key.callback = callback; /* Send to ioqueue! */ size = tdata->buf.cur - tdata->buf.start; status = pj_ioqueue_sendto(tp->key, (pj_ioqueue_op_key_t*)&tdata->op_key, tdata->buf.start, &size, 0, rem_addr, addr_len); if (status != PJ_EPENDING) tdata->op_key.tdata = NULL; return status; } /* Clean up UDP resources */ static void udp_on_destroy(void *arg) { struct udp_transport *tp = (struct udp_transport*)arg; int i; /* Destroy rdata */ for (i=0; irdata_cnt; ++i) { pj_pool_release(tp->rdata[i]->tp_info.pool); } /* Destroy reference counter. */ if (tp->base.ref_cnt) pj_atomic_destroy(tp->base.ref_cnt); /* Destroy lock */ if (tp->base.lock) pj_lock_destroy(tp->base.lock); PJ_LOG(4,(tp->base.obj_name, "SIP UDP transport destroyed")); /* Destroy pool. */ pjsip_endpt_release_pool(tp->base.endpt, tp->base.pool); } /* * udp_destroy() * * This function is called by transport manager (by transport->destroy()). */ static pj_status_t udp_destroy( pjsip_transport *transport ) { struct udp_transport *tp = (struct udp_transport*)transport; int i; /* Mark this transport as closing. */ tp->is_closing = 1; /* Cancel all pending operations. */ /* blp: NO NO NO... * No need to post queued completion as we poll the ioqueue until * we've got events anyway. Posting completion will only cause * callback to be called twice with IOCP: one for the post completion * and another one for closing the socket. * for (i=0; irdata_cnt; ++i) { pj_ioqueue_post_completion(tp->key, &tp->rdata[i]->tp_info.op_key.op_key, -1); } */ /* Unregister from ioqueue. */ if (tp->key) { pj_ioqueue_unregister(tp->key); tp->key = NULL; } else { /* Close socket. */ if (tp->sock && tp->sock != PJ_INVALID_SOCKET) { pj_sock_close(tp->sock); tp->sock = PJ_INVALID_SOCKET; } } /* Must poll ioqueue because IOCP calls the callback when socket * is closed. We poll the ioqueue until all pending callbacks * have been called. */ for (i=0; i<50 && tp->is_closing < 1+tp->rdata_cnt; ++i) { int cnt; pj_time_val timeout = {0, 1}; cnt = pj_ioqueue_poll(pjsip_endpt_get_ioqueue(transport->endpt), &timeout); if (cnt == 0) break; } if (tp->grp_lock) { pj_grp_lock_t *grp_lock = tp->grp_lock; tp->grp_lock = NULL; pj_grp_lock_dec_ref(grp_lock); /* Transport may have been deleted at this point */ } else { udp_on_destroy(tp); } return PJ_SUCCESS; } /* * udp_shutdown() * * Start graceful UDP shutdown. */ static pj_status_t udp_shutdown(pjsip_transport *transport) { return pjsip_transport_dec_ref(transport); } /* Create socket */ static pj_status_t create_socket(int af, const pj_sockaddr_t *local_a, int addr_len, pj_sock_t *p_sock) { pj_sock_t sock; pj_sockaddr_in tmp_addr; pj_sockaddr_in6 tmp_addr6; pj_status_t status; status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock); if (status != PJ_SUCCESS) return status; if (local_a == NULL) { if (af == pj_AF_INET6()) { pj_bzero(&tmp_addr6, sizeof(tmp_addr6)); tmp_addr6.sin6_family = (pj_uint16_t)af; local_a = &tmp_addr6; addr_len = sizeof(tmp_addr6); } else { pj_sockaddr_in_init(&tmp_addr, NULL, 0); local_a = &tmp_addr; addr_len = sizeof(tmp_addr); } } status = pj_sock_bind(sock, local_a, addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } *p_sock = sock; return PJ_SUCCESS; } /* Generate transport's published address */ static pj_status_t get_published_name(pj_sock_t sock, char hostbuf[], int hostbufsz, pjsip_host_port *bound_name) { pj_sockaddr tmp_addr; int addr_len; pj_status_t status; addr_len = sizeof(tmp_addr); status = pj_sock_getsockname(sock, &tmp_addr, &addr_len); if (status != PJ_SUCCESS) return status; bound_name->host.ptr = hostbuf; if (tmp_addr.addr.sa_family == pj_AF_INET()) { bound_name->port = pj_ntohs(tmp_addr.ipv4.sin_port); /* If bound address specifies "0.0.0.0", get the IP address * of local hostname. */ if (tmp_addr.ipv4.sin_addr.s_addr == PJ_INADDR_ANY) { pj_sockaddr hostip; status = pj_gethostip(pj_AF_INET(), &hostip); if (status != PJ_SUCCESS) return status; pj_strcpy2(&bound_name->host, pj_inet_ntoa(hostip.ipv4.sin_addr)); } else { /* Otherwise use bound address. */ pj_strcpy2(&bound_name->host, pj_inet_ntoa(tmp_addr.ipv4.sin_addr)); status = PJ_SUCCESS; } } else { /* If bound address specifies "INADDR_ANY" (IPv6), get the * IP address of local hostname */ pj_uint32_t loop6[4] = { 0, 0, 0, 0}; bound_name->port = pj_ntohs(tmp_addr.ipv6.sin6_port); if (pj_memcmp(&tmp_addr.ipv6.sin6_addr, loop6, sizeof(loop6))==0) { status = pj_gethostip(tmp_addr.addr.sa_family, &tmp_addr); if (status != PJ_SUCCESS) return status; } status = pj_inet_ntop(tmp_addr.addr.sa_family, pj_sockaddr_get_addr(&tmp_addr), hostbuf, hostbufsz); if (status == PJ_SUCCESS) { bound_name->host.slen = pj_ansi_strlen(hostbuf); } } return status; } /* Set the published address of the transport */ static void udp_set_pub_name(struct udp_transport *tp, const pjsip_host_port *a_name) { enum { INFO_LEN = 80 }; char local_addr[PJ_INET6_ADDRSTRLEN+10]; pj_assert(a_name->host.slen != 0); pj_strdup_with_null(tp->base.pool, &tp->base.local_name.host, &a_name->host); tp->base.local_name.port = a_name->port; /* Update transport info. */ if (tp->base.info == NULL) { tp->base.info = (char*) pj_pool_alloc(tp->base.pool, INFO_LEN); } pj_sockaddr_print(&tp->base.local_addr, local_addr, sizeof(local_addr), 3); pj_ansi_snprintf( tp->base.info, INFO_LEN, "udp %s [published as %s:%d]", local_addr, tp->base.local_name.host.ptr, tp->base.local_name.port); } /* Set the socket handle of the transport */ static void udp_set_socket(struct udp_transport *tp, pj_sock_t sock, const pjsip_host_port *a_name) { #if PJSIP_UDP_SO_RCVBUF_SIZE || PJSIP_UDP_SO_SNDBUF_SIZE long sobuf_size; pj_status_t status; #endif /* Adjust socket rcvbuf size */ #if PJSIP_UDP_SO_RCVBUF_SIZE sobuf_size = PJSIP_UDP_SO_RCVBUF_SIZE; status = pj_sock_setsockopt(sock, pj_SOL_SOCKET(), pj_SO_RCVBUF(), &sobuf_size, sizeof(sobuf_size)); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(THIS_FILE, "Error setting SO_RCVBUF: %s [%d]", errmsg, status)); } #endif /* Adjust socket sndbuf size */ #if PJSIP_UDP_SO_SNDBUF_SIZE sobuf_size = PJSIP_UDP_SO_SNDBUF_SIZE; status = pj_sock_setsockopt(sock, pj_SOL_SOCKET(), pj_SO_SNDBUF(), &sobuf_size, sizeof(sobuf_size)); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(4,(THIS_FILE, "Error setting SO_SNDBUF: %s [%d]", errmsg, status)); } #endif /* Set the socket. */ tp->sock = sock; /* Init address name (published address) */ udp_set_pub_name(tp, a_name); } /* Register socket to ioqueue */ static pj_status_t register_to_ioqueue(struct udp_transport *tp) { pj_ioqueue_t *ioqueue; pj_ioqueue_callback ioqueue_cb; pj_status_t status; /* Ignore if already registered */ if (tp->key != NULL) return PJ_SUCCESS; /* Create group lock */ status = pj_grp_lock_create(tp->base.pool, NULL, &tp->grp_lock); if (status != PJ_SUCCESS) return status; pj_grp_lock_add_ref(tp->grp_lock); pj_grp_lock_add_handler(tp->grp_lock, tp->base.pool, tp, &udp_on_destroy); /* Register to ioqueue. */ ioqueue = pjsip_endpt_get_ioqueue(tp->base.endpt); pj_memset(&ioqueue_cb, 0, sizeof(ioqueue_cb)); ioqueue_cb.on_read_complete = &udp_on_read_complete; ioqueue_cb.on_write_complete = &udp_on_write_complete; return pj_ioqueue_register_sock2(tp->base.pool, ioqueue, tp->sock, tp->grp_lock, tp, &ioqueue_cb, &tp->key); } /* Start ioqueue asynchronous reading to all rdata */ static pj_status_t start_async_read(struct udp_transport *tp) { int i; pj_status_t status; /* Start reading the ioqueue. */ for (i=0; irdata_cnt; ++i) { pj_ssize_t size; size = sizeof(tp->rdata[i]->pkt_info.packet); tp->rdata[i]->pkt_info.src_addr_len = sizeof(tp->rdata[i]->pkt_info.src_addr); status = pj_ioqueue_recvfrom(tp->key, &tp->rdata[i]->tp_info.op_key.op_key, tp->rdata[i]->pkt_info.packet, &size, PJ_IOQUEUE_ALWAYS_ASYNC, &tp->rdata[i]->pkt_info.src_addr, &tp->rdata[i]->pkt_info.src_addr_len); if (status == PJ_SUCCESS) { pj_assert(!"Shouldn't happen because PJ_IOQUEUE_ALWAYS_ASYNC!"); udp_on_read_complete(tp->key, &tp->rdata[i]->tp_info.op_key.op_key, size); } else if (status != PJ_EPENDING) { /* Error! */ return status; } } return PJ_SUCCESS; } /* * pjsip_udp_transport_attach() * * Attach UDP socket and start transport. */ static pj_status_t transport_attach( pjsip_endpoint *endpt, pjsip_transport_type_e type, pj_sock_t sock, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_transport **p_transport) { pj_pool_t *pool; struct udp_transport *tp; const char *format, *ipv6_quoteb, *ipv6_quotee; unsigned i; pj_status_t status; PJ_ASSERT_RETURN(endpt && sock!=PJ_INVALID_SOCKET && a_name && async_cnt>0, PJ_EINVAL); /* Object name. */ if (type & PJSIP_TRANSPORT_IPV6) { format = "udpv6%p"; ipv6_quoteb = "["; ipv6_quotee = "]"; } else { format = "udp%p"; ipv6_quoteb = ipv6_quotee = ""; } /* Create pool. */ pool = pjsip_endpt_create_pool(endpt, format, PJSIP_POOL_LEN_TRANSPORT, PJSIP_POOL_INC_TRANSPORT); if (!pool) return PJ_ENOMEM; /* Create the UDP transport object. */ tp = PJ_POOL_ZALLOC_T(pool, struct udp_transport); /* Save pool. */ tp->base.pool = pool; pj_memcpy(tp->base.obj_name, pool->obj_name, PJ_MAX_OBJ_NAME); /* Init reference counter. */ status = pj_atomic_create(pool, 0, &tp->base.ref_cnt); if (status != PJ_SUCCESS) goto on_error; /* Init lock. */ status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &tp->base.lock); if (status != PJ_SUCCESS) goto on_error; /* Set type. */ tp->base.key.type = type; /* Remote address is left zero (except the family) */ tp->base.key.rem_addr.addr.sa_family = (pj_uint16_t) ((type & PJSIP_TRANSPORT_IPV6) ? pj_AF_INET6() : pj_AF_INET()); /* Type name. */ tp->base.type_name = "UDP"; /* Transport flag */ tp->base.flag = pjsip_transport_get_flag_from_type(type); /* Length of addressess. */ tp->base.addr_len = sizeof(tp->base.local_addr); /* Init local address. */ status = pj_sock_getsockname(sock, &tp->base.local_addr, &tp->base.addr_len); if (status != PJ_SUCCESS) goto on_error; /* Init remote name. */ if (type == PJSIP_TRANSPORT_UDP) tp->base.remote_name.host = pj_str("0.0.0.0"); else tp->base.remote_name.host = pj_str("::0"); tp->base.remote_name.port = 0; /* Init direction */ tp->base.dir = PJSIP_TP_DIR_NONE; /* Set endpoint. */ tp->base.endpt = endpt; /* Transport manager and timer will be initialized by tpmgr */ /* Attach socket and assign name. */ udp_set_socket(tp, sock, a_name); /* Register to ioqueue */ status = register_to_ioqueue(tp); if (status != PJ_SUCCESS) goto on_error; /* Set functions. */ tp->base.send_msg = &udp_send_msg; tp->base.do_shutdown = &udp_shutdown; tp->base.destroy = &udp_destroy; /* This is a permanent transport, so we initialize the ref count * to one so that transport manager don't destroy this transport * when there's no user! */ pj_atomic_inc(tp->base.ref_cnt); /* Register to transport manager. */ tp->base.tpmgr = pjsip_endpt_get_tpmgr(endpt); status = pjsip_transport_register( tp->base.tpmgr, (pjsip_transport*)tp); if (status != PJ_SUCCESS) goto on_error; /* Create rdata and put it in the array. */ tp->rdata_cnt = 0; tp->rdata = (pjsip_rx_data**) pj_pool_calloc(tp->base.pool, async_cnt, sizeof(pjsip_rx_data*)); for (i=0; ibase.ref_cnt, 0); pjsip_transport_destroy(&tp->base); return PJ_ENOMEM; } init_rdata(tp, i, rdata_pool, NULL); tp->rdata_cnt++; } /* Start reading the ioqueue. */ status = start_async_read(tp); if (status != PJ_SUCCESS) { pjsip_transport_destroy(&tp->base); return status; } /* Done. */ if (p_transport) *p_transport = &tp->base; PJ_LOG(4,(tp->base.obj_name, "SIP %s started, published address is %s%.*s%s:%d", pjsip_transport_get_type_desc((pjsip_transport_type_e)tp->base.key.type), ipv6_quoteb, (int)tp->base.local_name.host.slen, tp->base.local_name.host.ptr, ipv6_quotee, tp->base.local_name.port)); return PJ_SUCCESS; on_error: udp_destroy((pjsip_transport*)tp); return status; } PJ_DEF(pj_status_t) pjsip_udp_transport_attach( pjsip_endpoint *endpt, pj_sock_t sock, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_transport **p_transport) { return transport_attach(endpt, PJSIP_TRANSPORT_UDP, sock, a_name, async_cnt, p_transport); } PJ_DEF(pj_status_t) pjsip_udp_transport_attach2( pjsip_endpoint *endpt, pjsip_transport_type_e type, pj_sock_t sock, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_transport **p_transport) { return transport_attach(endpt, type, sock, a_name, async_cnt, p_transport); } /* * pjsip_udp_transport_start() * * Create a UDP socket in the specified address and start a transport. */ PJ_DEF(pj_status_t) pjsip_udp_transport_start( pjsip_endpoint *endpt, const pj_sockaddr_in *local_a, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_transport **p_transport) { pj_sock_t sock; pj_status_t status; char addr_buf[PJ_INET6_ADDRSTRLEN]; pjsip_host_port bound_name; PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL); status = create_socket(pj_AF_INET(), local_a, sizeof(pj_sockaddr_in), &sock); if (status != PJ_SUCCESS) return status; if (a_name == NULL) { /* Address name is not specified. * Build a name based on bound address. */ status = get_published_name(sock, addr_buf, sizeof(addr_buf), &bound_name); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } a_name = &bound_name; } return pjsip_udp_transport_attach( endpt, sock, a_name, async_cnt, p_transport ); } /* * pjsip_udp_transport_start() * * Create a UDP socket in the specified address and start a transport. */ PJ_DEF(pj_status_t) pjsip_udp_transport_start6(pjsip_endpoint *endpt, const pj_sockaddr_in6 *local_a, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_transport **p_transport) { pj_sock_t sock; pj_status_t status; char addr_buf[PJ_INET6_ADDRSTRLEN]; pjsip_host_port bound_name; PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL); status = create_socket(pj_AF_INET6(), local_a, sizeof(pj_sockaddr_in6), &sock); if (status != PJ_SUCCESS) return status; if (a_name == NULL) { /* Address name is not specified. * Build a name based on bound address. */ status = get_published_name(sock, addr_buf, sizeof(addr_buf), &bound_name); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } a_name = &bound_name; } return pjsip_udp_transport_attach2(endpt, PJSIP_TRANSPORT_UDP6, sock, a_name, async_cnt, p_transport); } /* * Retrieve the internal socket handle used by the UDP transport. */ PJ_DEF(pj_sock_t) pjsip_udp_transport_get_socket(pjsip_transport *transport) { struct udp_transport *tp; PJ_ASSERT_RETURN(transport != NULL, PJ_INVALID_SOCKET); tp = (struct udp_transport*) transport; return tp->sock; } /* * Temporarily pause or shutdown the transport. */ PJ_DEF(pj_status_t) pjsip_udp_transport_pause(pjsip_transport *transport, unsigned option) { struct udp_transport *tp; unsigned i; PJ_ASSERT_RETURN(transport != NULL, PJ_EINVAL); /* Flag must be specified */ PJ_ASSERT_RETURN((option & 0x03) != 0, PJ_EINVAL); tp = (struct udp_transport*) transport; /* Transport must not have been paused */ PJ_ASSERT_RETURN(tp->is_paused==0, PJ_EINVALIDOP); /* Set transport to paused first, so that when the read callback is * called by pj_ioqueue_post_completion() it will not try to * re-register the rdata. */ tp->is_paused = PJ_TRUE; /* Cancel the ioqueue operation. */ for (i=0; i<(unsigned)tp->rdata_cnt; ++i) { pj_ioqueue_post_completion(tp->key, &tp->rdata[i]->tp_info.op_key.op_key, -1); } /* Destroy the socket? */ if (option & PJSIP_UDP_TRANSPORT_DESTROY_SOCKET) { if (tp->key) { /* This implicitly closes the socket */ pj_ioqueue_unregister(tp->key); tp->key = NULL; } else { /* Close socket. */ if (tp->sock && tp->sock != PJ_INVALID_SOCKET) { pj_sock_close(tp->sock); tp->sock = PJ_INVALID_SOCKET; } } tp->sock = PJ_INVALID_SOCKET; } PJ_LOG(4,(tp->base.obj_name, "SIP UDP transport paused")); return PJ_SUCCESS; } /* * Restart transport. * * If option is KEEP_SOCKET, just re-activate ioqueue operation. * * If option is DESTROY_SOCKET: * - if socket is specified, replace. * - if socket is not specified, create and replace. */ PJ_DEF(pj_status_t) pjsip_udp_transport_restart(pjsip_transport *transport, unsigned option, pj_sock_t sock, const pj_sockaddr_in *local, const pjsip_host_port *a_name) { struct udp_transport *tp; pj_status_t status; PJ_ASSERT_RETURN(transport != NULL, PJ_EINVAL); /* Flag must be specified */ PJ_ASSERT_RETURN((option & 0x03) != 0, PJ_EINVAL); tp = (struct udp_transport*) transport; if (option & PJSIP_UDP_TRANSPORT_DESTROY_SOCKET) { char addr_buf[PJ_INET6_ADDRSTRLEN]; pjsip_host_port bound_name; /* Request to recreate transport */ /* Destroy existing socket, if any. */ if (tp->key) { /* This implicitly closes the socket */ pj_ioqueue_unregister(tp->key); tp->key = NULL; } else { /* Close socket. */ if (tp->sock && tp->sock != PJ_INVALID_SOCKET) { pj_sock_close(tp->sock); tp->sock = PJ_INVALID_SOCKET; } } tp->sock = PJ_INVALID_SOCKET; /* Create the socket if it's not specified */ if (sock == PJ_INVALID_SOCKET) { status = create_socket(pj_AF_INET(), local, sizeof(pj_sockaddr_in), &sock); if (status != PJ_SUCCESS) return status; } /* If transport published name is not specified, calculate it * from the bound address. */ if (a_name == NULL) { status = get_published_name(sock, addr_buf, sizeof(addr_buf), &bound_name); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } a_name = &bound_name; } /* Init local address. */ status = pj_sock_getsockname(sock, &tp->base.local_addr, &tp->base.addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Assign the socket and published address to transport. */ udp_set_socket(tp, sock, a_name); } else { /* For KEEP_SOCKET, transport must have been paused before */ PJ_ASSERT_RETURN(tp->is_paused, PJ_EINVALIDOP); /* If address name is specified, update it */ if (a_name != NULL) udp_set_pub_name(tp, a_name); } /* Re-register new or existing socket to ioqueue. */ status = register_to_ioqueue(tp); if (status != PJ_SUCCESS) { return status; } /* Restart async read operation. */ status = start_async_read(tp); if (status != PJ_SUCCESS) return status; /* Everything has been set up */ tp->is_paused = PJ_FALSE; PJ_LOG(4,(tp->base.obj_name, "SIP UDP transport restarted, published address is %.*s:%d", (int)tp->base.local_name.host.slen, tp->base.local_name.host.ptr, tp->base.local_name.port)); return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_transport_wrap.cpp ================================================ /* $Id: sip_transport_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This file is a C++ wrapper, see ticket #886 for details. */ #include "sip_transport.c" ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_ua_layer.c ================================================ /* $Id: sip_ua_layer.c 4420 2013-03-05 11:59:54Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "sip_ua_layer.c" /* * Static prototypes. */ static pj_status_t mod_ua_load(pjsip_endpoint *endpt); static pj_status_t mod_ua_unload(void); static pj_bool_t mod_ua_on_rx_request(pjsip_rx_data *rdata); static pj_bool_t mod_ua_on_rx_response(pjsip_rx_data *rdata); static void mod_ua_on_tsx_state(pjsip_transaction*, pjsip_event*); extern long pjsip_dlg_lock_tls_id; /* defined in sip_dialog.c */ /* This struct is used to represent list of dialog inside a dialog set. * We don't want to use pjsip_dialog for this purpose, to save some * memory (about 100 bytes per dialog set). */ struct dlg_set_head { PJ_DECL_LIST_MEMBER(pjsip_dialog); }; /* This struct represents a dialog set. * This is the value that will be put in the UA's hash table. */ struct dlg_set { /* To put this node in free dlg_set nodes in UA. */ PJ_DECL_LIST_MEMBER(struct dlg_set); /* This is the buffer to store this entry in the hash table. */ pj_hash_entry_buf ht_entry; /* List of dialog in this dialog set. */ struct dlg_set_head dlg_list; }; /* * Module interface. */ static struct user_agent { pjsip_module mod; pj_pool_t *pool; pjsip_endpoint *endpt; pj_mutex_t *mutex; pj_hash_table_t *dlg_table; pjsip_ua_init_param param; struct dlg_set free_dlgset_nodes; } mod_ua = { { NULL, NULL, /* prev, next. */ { "mod-ua", 6 }, /* Name. */ -1, /* Id */ PJSIP_MOD_PRIORITY_UA_PROXY_LAYER, /* Priority */ &mod_ua_load, /* load() */ NULL, /* start() */ NULL, /* stop() */ &mod_ua_unload, /* unload() */ &mod_ua_on_rx_request, /* on_rx_request() */ &mod_ua_on_rx_response, /* on_rx_response() */ NULL, /* on_tx_request. */ NULL, /* on_tx_response() */ &mod_ua_on_tsx_state, /* on_tsx_state() */ } }; /* * mod_ua_load() * * Called when module is being loaded by endpoint. */ static pj_status_t mod_ua_load(pjsip_endpoint *endpt) { pj_status_t status; /* Initialize the user agent. */ mod_ua.endpt = endpt; mod_ua.pool = pjsip_endpt_create_pool( endpt, "ua%p", PJSIP_POOL_LEN_UA, PJSIP_POOL_INC_UA); if (mod_ua.pool == NULL) return PJ_ENOMEM; status = pj_mutex_create_recursive(mod_ua.pool, " ua%p", &mod_ua.mutex); if (status != PJ_SUCCESS) return status; mod_ua.dlg_table = pj_hash_create(mod_ua.pool, PJSIP_MAX_DIALOG_COUNT); if (mod_ua.dlg_table == NULL) return PJ_ENOMEM; pj_list_init(&mod_ua.free_dlgset_nodes); /* Initialize dialog lock. */ status = pj_thread_local_alloc(&pjsip_dlg_lock_tls_id); if (status != PJ_SUCCESS) return status; pj_thread_local_set(pjsip_dlg_lock_tls_id, NULL); return PJ_SUCCESS; } /* * mod_ua_unload() * * Called when module is being unloaded. */ static pj_status_t mod_ua_unload(void) { pj_thread_local_free(pjsip_dlg_lock_tls_id); pj_mutex_destroy(mod_ua.mutex); /* Release pool */ if (mod_ua.pool) { pjsip_endpt_release_pool( mod_ua.endpt, mod_ua.pool ); } return PJ_SUCCESS; } /* * mod_ua_on_tsx_stats() * * Called on changed on transaction state. */ static void mod_ua_on_tsx_state( pjsip_transaction *tsx, pjsip_event *e) { pjsip_dialog *dlg; /* Get the dialog where this transaction belongs. */ dlg = (pjsip_dialog*) tsx->mod_data[mod_ua.mod.id]; /* If dialog instance has gone, it could mean that the dialog * may has been destroyed. */ if (dlg == NULL) return; /* Hand over the event to the dialog. */ pjsip_dlg_on_tsx_state(dlg, tsx, e); } /* * Init user agent module and register it to the endpoint. */ PJ_DEF(pj_status_t) pjsip_ua_init_module( pjsip_endpoint *endpt, const pjsip_ua_init_param *prm) { pj_status_t status; /* Check if module already registered. */ PJ_ASSERT_RETURN(mod_ua.mod.id == -1, PJ_EINVALIDOP); /* Copy param, if exists. */ if (prm) pj_memcpy(&mod_ua.param, prm, sizeof(pjsip_ua_init_param)); /* Register the module. */ status = pjsip_endpt_register_module(endpt, &mod_ua.mod); return status; } /* * Get the instance of the user agent. * */ PJ_DEF(pjsip_user_agent*) pjsip_ua_instance(void) { return &mod_ua.mod; } /* * Get the endpoint where this UA is currently registered. */ PJ_DEF(pjsip_endpoint*) pjsip_ua_get_endpt(pjsip_user_agent *ua) { PJ_UNUSED_ARG(ua); pj_assert(ua == &mod_ua.mod); return mod_ua.endpt; } /* * Destroy the user agent layer. */ PJ_DEF(pj_status_t) pjsip_ua_destroy(void) { /* Check if module already destroyed. */ PJ_ASSERT_RETURN(mod_ua.mod.id != -1, PJ_EINVALIDOP); return pjsip_endpt_unregister_module(mod_ua.endpt, &mod_ua.mod); } /* * Create key to identify dialog set. */ /* PJ_DEF(void) pjsip_ua_create_dlg_set_key( pj_pool_t *pool, pj_str_t *set_key, const pj_str_t *call_id, const pj_str_t *local_tag) { PJ_ASSERT_ON_FAIL(pool && set_key && call_id && local_tag, return;); set_key->slen = call_id->slen + local_tag->slen + 1; set_key->ptr = (char*) pj_pool_alloc(pool, set_key->slen); pj_assert(set_key->ptr != NULL); pj_memcpy(set_key->ptr, call_id->ptr, call_id->slen); set_key->ptr[call_id->slen] = '$'; pj_memcpy(set_key->ptr + call_id->slen + 1, local_tag->ptr, local_tag->slen); } */ /* * Acquire one dlg_set node to be put in the hash table. * This will first look in the free nodes list, then allocate * a new one from UA's pool when one is not available. */ static struct dlg_set *alloc_dlgset_node(void) { struct dlg_set *set; if (!pj_list_empty(&mod_ua.free_dlgset_nodes)) { set = mod_ua.free_dlgset_nodes.next; pj_list_erase(set); return set; } else { set = PJ_POOL_ALLOC_T(mod_ua.pool, struct dlg_set); return set; } } /* * Register new dialog. Called by pjsip_dlg_create_uac() and * pjsip_dlg_create_uas_and_inc_lock(); */ PJ_DEF(pj_status_t) pjsip_ua_register_dlg( pjsip_user_agent *ua, pjsip_dialog *dlg ) { /* Sanity check. */ PJ_ASSERT_RETURN(ua && dlg, PJ_EINVAL); /* For all dialogs, local tag (inc hash) must has been initialized. */ PJ_ASSERT_RETURN(dlg->local.info && dlg->local.info->tag.slen && dlg->local.tag_hval != 0, PJ_EBUG); /* For UAS dialog, remote tag (inc hash) must have been initialized. */ //PJ_ASSERT_RETURN(dlg->role==PJSIP_ROLE_UAC || // (dlg->role==PJSIP_ROLE_UAS && dlg->remote.info->tag.slen // && dlg->remote.tag_hval != 0), PJ_EBUG); /* Lock the user agent. */ pj_mutex_lock(mod_ua.mutex); /* For UAC, check if there is existing dialog in the same set. */ if (dlg->role == PJSIP_ROLE_UAC) { struct dlg_set *dlg_set; dlg_set = (struct dlg_set*) pj_hash_get_lower( mod_ua.dlg_table, dlg->local.info->tag.ptr, (unsigned)dlg->local.info->tag.slen, &dlg->local.tag_hval); if (dlg_set) { /* This is NOT the first dialog in the dialog set. * Just add this dialog in the list. */ pj_assert(dlg_set->dlg_list.next != (void*)&dlg_set->dlg_list); pj_list_push_back(&dlg_set->dlg_list, dlg); dlg->dlg_set = dlg_set; } else { /* This is the first dialog in the dialog set. * Create the dialog set and add this dialog to it. */ dlg_set = alloc_dlgset_node(); pj_list_init(&dlg_set->dlg_list); pj_list_push_back(&dlg_set->dlg_list, dlg); dlg->dlg_set = dlg_set; /* Register the dialog set in the hash table. */ pj_hash_set_np_lower(mod_ua.dlg_table, dlg->local.info->tag.ptr, (unsigned)dlg->local.info->tag.slen, dlg->local.tag_hval, dlg_set->ht_entry, dlg_set); } } else { /* For UAS, create the dialog set with a single dialog as member. */ struct dlg_set *dlg_set; dlg_set = alloc_dlgset_node(); pj_list_init(&dlg_set->dlg_list); pj_list_push_back(&dlg_set->dlg_list, dlg); dlg->dlg_set = dlg_set; pj_hash_set_np_lower(mod_ua.dlg_table, dlg->local.info->tag.ptr, (unsigned)dlg->local.info->tag.slen, dlg->local.tag_hval, dlg_set->ht_entry, dlg_set); } /* Unlock user agent. */ pj_mutex_unlock(mod_ua.mutex); /* Done. */ return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_ua_unregister_dlg( pjsip_user_agent *ua, pjsip_dialog *dlg ) { struct dlg_set *dlg_set; pjsip_dialog *d; /* Sanity-check arguments. */ PJ_ASSERT_RETURN(ua && dlg, PJ_EINVAL); /* Check that dialog has been registered. */ PJ_ASSERT_RETURN(dlg->dlg_set, PJ_EINVALIDOP); /* Lock user agent. */ pj_mutex_lock(mod_ua.mutex); /* Find this dialog from the dialog set. */ dlg_set = (struct dlg_set*) dlg->dlg_set; d = dlg_set->dlg_list.next; while (d != (pjsip_dialog*)&dlg_set->dlg_list && d != dlg) { d = d->next; } if (d != dlg) { pj_assert(!"Dialog is not registered!"); pj_mutex_unlock(mod_ua.mutex); return PJ_EINVALIDOP; } /* Remove this dialog from the list. */ pj_list_erase(dlg); /* If dialog list is empty, remove the dialog set from the hash table. */ if (pj_list_empty(&dlg_set->dlg_list)) { pj_hash_set_lower(NULL, mod_ua.dlg_table, dlg->local.info->tag.ptr, (unsigned)dlg->local.info->tag.slen, dlg->local.tag_hval, NULL); /* Return dlg_set to free nodes. */ pj_list_push_back(&mod_ua.free_dlgset_nodes, dlg_set); } /* Unlock user agent. */ pj_mutex_unlock(mod_ua.mutex); /* Done. */ return PJ_SUCCESS; } PJ_DEF(pjsip_dialog*) pjsip_rdata_get_dlg( pjsip_rx_data *rdata ) { return (pjsip_dialog*) rdata->endpt_info.mod_data[mod_ua.mod.id]; } PJ_DEF(pjsip_dialog*) pjsip_tsx_get_dlg( pjsip_transaction *tsx ) { return (pjsip_dialog*) tsx->mod_data[mod_ua.mod.id]; } /* * Retrieve the current number of dialog-set currently registered * in the hash table. */ PJ_DEF(unsigned) pjsip_ua_get_dlg_set_count(void) { unsigned count; PJ_ASSERT_RETURN(mod_ua.endpt, 0); pj_mutex_lock(mod_ua.mutex); count = pj_hash_count(mod_ua.dlg_table); pj_mutex_unlock(mod_ua.mutex); return count; } /* * Find a dialog. */ PJ_DEF(pjsip_dialog*) pjsip_ua_find_dialog(const pj_str_t *call_id, const pj_str_t *local_tag, const pj_str_t *remote_tag, pj_bool_t lock_dialog) { struct dlg_set *dlg_set; pjsip_dialog *dlg; PJ_ASSERT_RETURN(call_id && local_tag && remote_tag, NULL); /* Lock user agent. */ pj_mutex_lock(mod_ua.mutex); /* Lookup the dialog set. */ dlg_set = (struct dlg_set*) pj_hash_get_lower(mod_ua.dlg_table, local_tag->ptr, (unsigned)local_tag->slen, NULL); if (dlg_set == NULL) { /* Not found */ pj_mutex_unlock(mod_ua.mutex); return NULL; } /* Dialog set is found, now find the matching dialog based on the * remote tag. */ dlg = dlg_set->dlg_list.next; while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) { if (pj_stricmp(&dlg->remote.info->tag, remote_tag) == 0) break; dlg = dlg->next; } if (dlg == (pjsip_dialog*)&dlg_set->dlg_list) { /* Not found */ pj_mutex_unlock(mod_ua.mutex); return NULL; } /* Dialog has been found. It SHOULD have the right Call-ID!! */ if (pj_strcmp(&dlg->call_id->id, call_id)!=0) { PJ_LOG(6, (THIS_FILE, "Dialog not found: local and remote tags " "matched but not call id")); pj_mutex_unlock(mod_ua.mutex); return NULL; } if (lock_dialog) { if (pjsip_dlg_try_inc_lock(dlg) != PJ_SUCCESS) { /* * Unable to acquire dialog's lock while holding the user * agent's mutex. Release the UA mutex before retrying once * more. * * THIS MAY CAUSE RACE CONDITION! */ /* Unlock user agent. */ pj_mutex_unlock(mod_ua.mutex); /* Lock dialog */ pjsip_dlg_inc_lock(dlg); } else { /* Unlock user agent. */ pj_mutex_unlock(mod_ua.mutex); } } else { /* Unlock user agent. */ pj_mutex_unlock(mod_ua.mutex); } return dlg; } /* * Find the first dialog in dialog set in hash table for an incoming message. */ static struct dlg_set *find_dlg_set_for_msg( pjsip_rx_data *rdata ) { /* CANCEL message doesn't have To tag, so we must lookup the dialog * by finding the INVITE UAS transaction being cancelled. */ if (rdata->msg_info.cseq->method.id == PJSIP_CANCEL_METHOD) { pjsip_dialog *dlg; /* Create key for the rdata, but this time, use INVITE as the * method. */ pj_str_t key; pjsip_role_e role; pjsip_transaction *tsx; if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) role = PJSIP_ROLE_UAS; else role = PJSIP_ROLE_UAC; pjsip_tsx_create_key(rdata->tp_info.pool, &key, role, pjsip_get_invite_method(), rdata); /* Lookup the INVITE transaction */ tsx = pjsip_tsx_layer_find_tsx(&key, PJ_TRUE); /* We should find the dialog attached to the INVITE transaction */ if (tsx) { dlg = (pjsip_dialog*) tsx->mod_data[mod_ua.mod.id]; pj_grp_lock_release(tsx->grp_lock); /* Dlg may be NULL on some extreme condition * (e.g. during debugging where initially there is a dialog) */ return dlg ? (struct dlg_set*) dlg->dlg_set : NULL; } else { return NULL; } } else { pj_str_t *tag; struct dlg_set *dlg_set; if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) tag = &rdata->msg_info.to->tag; else tag = &rdata->msg_info.from->tag; /* Lookup the dialog set. */ dlg_set = (struct dlg_set*) pj_hash_get_lower(mod_ua.dlg_table, tag->ptr, (unsigned)tag->slen, NULL); return dlg_set; } } /* On received requests. */ static pj_bool_t mod_ua_on_rx_request(pjsip_rx_data *rdata) { struct dlg_set *dlg_set; pj_str_t *from_tag; pjsip_dialog *dlg; pj_status_t status; /* Optimized path: bail out early if request is not CANCEL and it doesn't * have To tag */ if (rdata->msg_info.to->tag.slen == 0 && rdata->msg_info.msg->line.req.method.id != PJSIP_CANCEL_METHOD) { return PJ_FALSE; } /* Incoming REGISTER may have tags in it */ if (rdata->msg_info.msg->line.req.method.id == PJSIP_REGISTER_METHOD) return PJ_FALSE; retry_on_deadlock: /* Lock user agent before looking up the dialog hash table. */ pj_mutex_lock(mod_ua.mutex); /* Lookup the dialog set, based on the To tag header. */ dlg_set = find_dlg_set_for_msg(rdata); /* If dialog is not found, respond with 481 (Call/Transaction * Does Not Exist). */ if (dlg_set == NULL) { /* Unable to find dialog. */ pj_mutex_unlock(mod_ua.mutex); if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { PJ_LOG(5,(THIS_FILE, "Unable to find dialogset for %s, answering with 481", pjsip_rx_data_get_info(rdata))); /* Respond with 481 . */ pjsip_endpt_respond_stateless( mod_ua.endpt, rdata, 481, NULL, NULL, NULL ); } return PJ_TRUE; } /* Dialog set has been found. * Find the dialog in the dialog set based on the content of the remote * tag. */ from_tag = &rdata->msg_info.from->tag; dlg = dlg_set->dlg_list.next; while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) { if (pj_stricmp(&dlg->remote.info->tag, from_tag) == 0) break; dlg = dlg->next; } /* Dialog may not be found, e.g. in this case: * - UAC sends SUBSCRIBE, then UAS sends NOTIFY before answering * SUBSCRIBE request with 2xx. * * In this case, we can accept the request ONLY when the original * dialog still has empty To tag. */ if (dlg == (pjsip_dialog*)&dlg_set->dlg_list) { pjsip_dialog *first_dlg = dlg_set->dlg_list.next; if (first_dlg->remote.info->tag.slen != 0) { /* Not found. Mulfunction UAC? */ pj_mutex_unlock(mod_ua.mutex); if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { PJ_LOG(5,(THIS_FILE, "Unable to find dialog for %s, answering with 481", pjsip_rx_data_get_info(rdata))); pjsip_endpt_respond_stateless(mod_ua.endpt, rdata, PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, NULL, NULL, NULL); } else { PJ_LOG(5,(THIS_FILE, "Unable to find dialog for %s", pjsip_rx_data_get_info(rdata))); } return PJ_TRUE; } dlg = first_dlg; } /* Mark the dialog id of the request. */ rdata->endpt_info.mod_data[mod_ua.mod.id] = dlg; /* Try to lock the dialog */ PJ_LOG(6,(dlg->obj_name, "UA layer acquiring dialog lock for request")); status = pjsip_dlg_try_inc_lock(dlg); if (status != PJ_SUCCESS) { /* Failed to acquire dialog mutex immediately, this could be * because of deadlock. Release UA mutex, yield, and retry * the whole thing once again. */ pj_mutex_unlock(mod_ua.mutex); pj_thread_sleep(0); goto retry_on_deadlock; } /* Done with processing in UA layer, release lock */ pj_mutex_unlock(mod_ua.mutex); /* Pass to dialog. */ pjsip_dlg_on_rx_request(dlg, rdata); /* Unlock the dialog. This may destroy the dialog */ pjsip_dlg_dec_lock(dlg); /* Report as handled. */ return PJ_TRUE; } /* On rx response notification. */ static pj_bool_t mod_ua_on_rx_response(pjsip_rx_data *rdata) { pjsip_transaction *tsx; struct dlg_set *dlg_set; pjsip_dialog *dlg; pj_status_t status; /* * Find the dialog instance for the response. * All outgoing dialog requests are sent statefully, which means * there will be an UAC transaction associated with this response, * and the dialog instance will be recorded in that transaction. * * But even when transaction is found, there is possibility that * the response is a forked response. */ retry_on_deadlock: dlg = NULL; /* Lock user agent dlg table before we're doing anything. */ pj_mutex_lock(mod_ua.mutex); /* Check if transaction is present. */ tsx = pjsip_rdata_get_tsx(rdata); if (tsx) { /* Check if dialog is present in the transaction. */ dlg = pjsip_tsx_get_dlg(tsx); if (!dlg) { /* Unlock dialog hash table. */ pj_mutex_unlock(mod_ua.mutex); return PJ_FALSE; } /* Get the dialog set. */ dlg_set = (struct dlg_set*) dlg->dlg_set; /* Even if transaction is found and (candidate) dialog has been * identified, it's possible that the request has forked. */ } else { /* Transaction is not present. * Check if this is a 2xx/OK response to INVITE, which in this * case the response will be handled directly by the * dialog. */ pjsip_cseq_hdr *cseq_hdr = rdata->msg_info.cseq; if (cseq_hdr->method.id != PJSIP_INVITE_METHOD || rdata->msg_info.msg->line.status.code / 100 != 2) { /* Not a 2xx response to INVITE. * This must be some stateless response sent by other modules, * or a very late response. */ /* Unlock dialog hash table. */ pj_mutex_unlock(mod_ua.mutex); return PJ_FALSE; } /* Get the dialog set. */ dlg_set = (struct dlg_set*) pj_hash_get_lower(mod_ua.dlg_table, rdata->msg_info.from->tag.ptr, (unsigned)rdata->msg_info.from->tag.slen, NULL); if (!dlg_set) { /* Unlock dialog hash table. */ pj_mutex_unlock(mod_ua.mutex); /* Strayed 2xx response!! */ PJ_LOG(4,(THIS_FILE, "Received strayed 2xx response (no dialog is found)" " from %s:%d: %s", rdata->pkt_info.src_name, rdata->pkt_info.src_port, pjsip_rx_data_get_info(rdata))); return PJ_TRUE; } } /* At this point, we must have the dialog set, and the dialog set * must have a dialog in the list. */ pj_assert(dlg_set && !pj_list_empty(&dlg_set->dlg_list)); /* Check for forked response. * Request will fork only for the initial INVITE request. */ //This doesn't work when there is authentication challenge, since //first_cseq evaluation will yield false. //if (rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD && // rdata->msg_info.cseq->cseq == dlg_set->dlg_list.next->local.first_cseq) if (rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD) { int st_code = rdata->msg_info.msg->line.status.code; pj_str_t *to_tag = &rdata->msg_info.to->tag; dlg = dlg_set->dlg_list.next; while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) { /* If there is dialog with no remote tag (i.e. dialog has not * been established yet), then send this response to that * dialog. */ if (dlg->remote.info->tag.slen == 0) break; /* Otherwise find the one with matching To tag. */ if (pj_stricmp(to_tag, &dlg->remote.info->tag) == 0) break; dlg = dlg->next; } /* If no dialog with matching remote tag is found, this must be * a forked response. Respond to this ONLY when response is non-100 * provisional response OR a 2xx response. */ if (dlg == (pjsip_dialog*)&dlg_set->dlg_list && ((st_code/100==1 && st_code!=100) || st_code/100==2)) { PJ_LOG(5,(THIS_FILE, "Received forked %s for existing dialog %s", pjsip_rx_data_get_info(rdata), dlg_set->dlg_list.next->obj_name)); /* Report to application about forked condition. * Application can either create a dialog or ignore the response. */ if (mod_ua.param.on_dlg_forked) { dlg = (*mod_ua.param.on_dlg_forked)(dlg_set->dlg_list.next, rdata); if (dlg == NULL) { pj_mutex_unlock(mod_ua.mutex); return PJ_TRUE; } } else { dlg = dlg_set->dlg_list.next; PJ_LOG(4,(THIS_FILE, "Unhandled forked %s from %s:%d, response will be " "handed over to the first dialog", pjsip_rx_data_get_info(rdata), rdata->pkt_info.src_name, rdata->pkt_info.src_port)); } } else if (dlg == (pjsip_dialog*)&dlg_set->dlg_list) { /* For 100 or non-2xx response which has different To tag, * pass the response to the first dialog. */ dlg = dlg_set->dlg_list.next; } } else { /* Either this is a non-INVITE response, or subsequent INVITE * within dialog. The dialog should have been identified when * the transaction was found. */ pj_assert(tsx != NULL); pj_assert(dlg != NULL); } /* The dialog must have been found. */ pj_assert(dlg != NULL); /* Put the dialog instance in the rdata. */ rdata->endpt_info.mod_data[mod_ua.mod.id] = dlg; /* Attempt to acquire lock to the dialog. */ PJ_LOG(6,(dlg->obj_name, "UA layer acquiring dialog lock for response")); status = pjsip_dlg_try_inc_lock(dlg); if (status != PJ_SUCCESS) { /* Failed to acquire dialog mutex. This could indicate a deadlock * situation, and for safety, try to avoid deadlock by releasing * UA mutex, yield, and retry the whole processing once again. */ pj_mutex_unlock(mod_ua.mutex); pj_thread_sleep(0); goto retry_on_deadlock; } /* We're done with processing in the UA layer, we can release the mutex */ pj_mutex_unlock(mod_ua.mutex); /* Pass the response to the dialog. */ pjsip_dlg_on_rx_response(dlg, rdata); /* Unlock the dialog. This may destroy the dialog. */ pjsip_dlg_dec_lock(dlg); /* Done. */ return PJ_TRUE; } #if PJ_LOG_MAX_LEVEL >= 3 static void print_dialog( const char *title, pjsip_dialog *dlg, char *buf, pj_size_t size) { int len; char userinfo[128]; len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo)); if (len < 0) pj_ansi_strcpy(userinfo, "<--uri too long-->"); else userinfo[len] = '\0'; len = pj_ansi_snprintf(buf, size, "%s[%s] %s", title, (dlg->state==PJSIP_DIALOG_STATE_NULL ? " - " : "est"), userinfo); if (len < 1 || len >= (int)size) { pj_ansi_strcpy(buf, "<--uri too long-->"); } else buf[len] = '\0'; } #endif /* * Dump user agent contents (e.g. all dialogs). */ PJ_DEF(void) pjsip_ua_dump(pj_bool_t detail) { #if PJ_LOG_MAX_LEVEL >= 3 pj_hash_iterator_t itbuf, *it; char dlginfo[128]; pj_mutex_lock(mod_ua.mutex); PJ_LOG(3, (THIS_FILE, "Number of dialog sets: %u", pj_hash_count(mod_ua.dlg_table))); if (detail && pj_hash_count(mod_ua.dlg_table)) { PJ_LOG(3, (THIS_FILE, "Dumping dialog sets:")); it = pj_hash_first(mod_ua.dlg_table, &itbuf); for (; it != NULL; it = pj_hash_next(mod_ua.dlg_table, it)) { struct dlg_set *dlg_set; pjsip_dialog *dlg; const char *title; dlg_set = (struct dlg_set*) pj_hash_this(mod_ua.dlg_table, it); if (!dlg_set || pj_list_empty(&dlg_set->dlg_list)) continue; /* First dialog in dialog set. */ dlg = dlg_set->dlg_list.next; if (dlg->role == PJSIP_ROLE_UAC) title = " [out] "; else title = " [in] "; print_dialog(title, dlg, dlginfo, sizeof(dlginfo)); PJ_LOG(3,(THIS_FILE, "%s", dlginfo)); /* Next dialog in dialog set (forked) */ dlg = dlg->next; while (dlg != (pjsip_dialog*) &dlg_set->dlg_list) { print_dialog(" [forked] ", dlg, dlginfo, sizeof(dlginfo)); dlg = dlg->next; } } } pj_mutex_unlock(mod_ua.mutex); #endif } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_uri.c ================================================ /* $Id: sip_uri.c 4228 2012-08-13 07:26:03Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include /* * Generic parameter manipulation. */ PJ_DEF(pjsip_param*) pjsip_param_find( const pjsip_param *param_list, const pj_str_t *name ) { pjsip_param *p = (pjsip_param*)param_list->next; while (p != param_list) { if (pj_stricmp(&p->name, name)==0) return p; p = p->next; } return NULL; } PJ_DEF(int) pjsip_param_cmp( const pjsip_param *param_list1, const pjsip_param *param_list2, pj_bool_t ig_nf) { const pjsip_param *p1; if ((ig_nf & 1)==0 && pj_list_size(param_list1)!=pj_list_size(param_list2)) return 1; p1 = param_list1->next; while (p1 != param_list1) { const pjsip_param *p2; p2 = pjsip_param_find(param_list2, &p1->name); if (p2 ) { int rc = pj_stricmp(&p1->value, &p2->value); if (rc != 0) return rc; } else if ((ig_nf & 1)==0) return 1; p1 = p1->next; } return 0; } PJ_DEF(void) pjsip_param_clone( pj_pool_t *pool, pjsip_param *dst_list, const pjsip_param *src_list) { const pjsip_param *p = src_list->next; pj_list_init(dst_list); while (p && p != src_list) { pjsip_param *new_param = PJ_POOL_ALLOC_T(pool, pjsip_param); pj_strdup(pool, &new_param->name, &p->name); pj_strdup(pool, &new_param->value, &p->value); pj_list_insert_before(dst_list, new_param); p = p->next; } } PJ_DEF(void) pjsip_param_shallow_clone( pj_pool_t *pool, pjsip_param *dst_list, const pjsip_param *src_list) { const pjsip_param *p = src_list->next; pj_list_init(dst_list); while (p != src_list) { pjsip_param *new_param = PJ_POOL_ALLOC_T(pool, pjsip_param); new_param->name = p->name; new_param->value = p->value; pj_list_insert_before(dst_list, new_param); p = p->next; } } PJ_DEF(pj_ssize_t) pjsip_param_print_on( const pjsip_param *param_list, char *buf, pj_size_t size, const pj_cis_t *pname_spec, const pj_cis_t *pvalue_spec, int sep) { const pjsip_param *p; char *startbuf; char *endbuf; int printed; p = param_list->next; if (p == NULL || p == param_list) return 0; startbuf = buf; endbuf = buf + size; PJ_UNUSED_ARG(pname_spec); do { copy_advance_char_check(buf, (char)sep); copy_advance_escape(buf, p->name, (*pname_spec)); if (p->value.slen) { copy_advance_char_check(buf, '='); if (*p->value.ptr == '"') copy_advance(buf, p->value); else copy_advance_escape(buf, p->value, (*pvalue_spec)); } p = p->next; if (sep == '?') sep = '&'; } while (p != param_list); return buf-startbuf; } /* * URI stuffs */ #define IS_SIPS(url) ((url)->vptr==&sips_url_vptr) static const pj_str_t *pjsip_url_get_scheme( const pjsip_sip_uri* ); static const pj_str_t *pjsips_url_get_scheme( const pjsip_sip_uri* ); static const pj_str_t *pjsip_name_addr_get_scheme( const pjsip_name_addr * ); static void *pjsip_get_uri( pjsip_uri *uri ); static void *pjsip_name_addr_get_uri( pjsip_name_addr *name ); static pj_str_t sip_str = { "sip", 3 }; static pj_str_t sips_str = { "sips", 4 }; static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool, const pjsip_name_addr *rhs); static pj_ssize_t pjsip_name_addr_print(pjsip_uri_context_e context, const pjsip_name_addr *name, char *buf, pj_size_t size); static int pjsip_name_addr_compare( pjsip_uri_context_e context, const pjsip_name_addr *naddr1, const pjsip_name_addr *naddr2); static pj_ssize_t pjsip_url_print( pjsip_uri_context_e context, const pjsip_sip_uri *url, char *buf, pj_size_t size); static int pjsip_url_compare( pjsip_uri_context_e context, const pjsip_sip_uri *url1, const pjsip_sip_uri *url2); static pjsip_sip_uri* pjsip_url_clone(pj_pool_t *pool, const pjsip_sip_uri *rhs); typedef const pj_str_t* (*P_GET_SCHEME)(const void*); typedef void* (*P_GET_URI)(void*); typedef pj_ssize_t (*P_PRINT_URI)(pjsip_uri_context_e,const void *, char*,pj_size_t); typedef int (*P_CMP_URI)(pjsip_uri_context_e, const void*, const void*); typedef void* (*P_CLONE)(pj_pool_t*, const void*); static pjsip_uri_vptr sip_url_vptr = { (P_GET_SCHEME) &pjsip_url_get_scheme, (P_GET_URI) &pjsip_get_uri, (P_PRINT_URI) &pjsip_url_print, (P_CMP_URI) &pjsip_url_compare, (P_CLONE) &pjsip_url_clone }; static pjsip_uri_vptr sips_url_vptr = { (P_GET_SCHEME) &pjsips_url_get_scheme, (P_GET_URI) &pjsip_get_uri, (P_PRINT_URI) &pjsip_url_print, (P_CMP_URI) &pjsip_url_compare, (P_CLONE) &pjsip_url_clone }; static pjsip_uri_vptr name_addr_vptr = { (P_GET_SCHEME) &pjsip_name_addr_get_scheme, (P_GET_URI) &pjsip_name_addr_get_uri, (P_PRINT_URI) &pjsip_name_addr_print, (P_CMP_URI) &pjsip_name_addr_compare, (P_CLONE) &pjsip_name_addr_clone }; static const pj_str_t *pjsip_url_get_scheme(const pjsip_sip_uri *url) { PJ_UNUSED_ARG(url); return &sip_str; } static const pj_str_t *pjsips_url_get_scheme(const pjsip_sip_uri *url) { PJ_UNUSED_ARG(url); return &sips_str; } static void *pjsip_get_uri( pjsip_uri *uri ) { return uri; } static void *pjsip_name_addr_get_uri( pjsip_name_addr *name ) { return pjsip_uri_get_uri(name->uri); } PJ_DEF(void) pjsip_sip_uri_set_secure( pjsip_sip_uri *url, pj_bool_t secure ) { url->vptr = secure ? &sips_url_vptr : &sip_url_vptr; } PJ_DEF(void) pjsip_sip_uri_init(pjsip_sip_uri *url, pj_bool_t secure) { pj_bzero(url, sizeof(*url)); url->ttl_param = -1; pjsip_sip_uri_set_secure(url, secure); pj_list_init(&url->other_param); pj_list_init(&url->header_param); } PJ_DEF(pjsip_sip_uri*) pjsip_sip_uri_create( pj_pool_t *pool, pj_bool_t secure ) { pjsip_sip_uri *url = PJ_POOL_ALLOC_T(pool, pjsip_sip_uri); pjsip_sip_uri_init(url, secure); return url; } static pj_ssize_t pjsip_url_print( pjsip_uri_context_e context, const pjsip_sip_uri *url, char *buf, pj_size_t size) { int printed; char *startbuf = buf; char *endbuf = buf+size; const pj_str_t *scheme; const pjsip_parser_const_t *pc = pjsip_parser_const(); *buf = '\0'; /* Print scheme ("sip:" or "sips:") */ scheme = pjsip_uri_get_scheme(url); copy_advance_check(buf, *scheme); copy_advance_char_check(buf, ':'); /* Print "user:password@", if any. */ if (url->user.slen) { const pj_cis_t *spec = pjsip_cfg()->endpt.allow_tx_hash_in_uri ? &pc->pjsip_USER_SPEC_LENIENT : &pc->pjsip_USER_SPEC; copy_advance_escape(buf, url->user, *spec); if (url->passwd.slen) { copy_advance_char_check(buf, ':'); copy_advance_escape(buf, url->passwd, pc->pjsip_PASSWD_SPEC); } copy_advance_char_check(buf, '@'); } /* Print host. */ pj_assert(url->host.slen != 0); /* Detect IPv6 IP address */ if (pj_memchr(url->host.ptr, ':', url->host.slen)) { copy_advance_pair_quote_cond(buf, "", 0, url->host, '[', ']'); } else { copy_advance_check(buf, url->host); } /* Only print port if it is explicitly specified. * Port is not allowed in To and From header, see Table 1 in * RFC 3261 Section 19.1.1 */ /* Note: ticket #1141 adds run-time setting to allow port number to * appear in From/To header. Default is still false. */ if (url->port && (context != PJSIP_URI_IN_FROMTO_HDR || pjsip_cfg()->endpt.allow_port_in_fromto_hdr)) { if (endbuf - buf < 10) return -1; copy_advance_char_check(buf, ':'); printed = pj_utoa(url->port, buf); buf += printed; } /* User param is allowed in all contexes */ copy_advance_pair_check(buf, ";user=", 6, url->user_param); /* Method param is only allowed in external/other context. */ if (context == PJSIP_URI_IN_OTHER) { copy_advance_pair_escape(buf, ";method=", 8, url->method_param, pc->pjsip_PARAM_CHAR_SPEC); } /* Transport is not allowed in From/To header. */ if (context != PJSIP_URI_IN_FROMTO_HDR) { copy_advance_pair_escape(buf, ";transport=", 11, url->transport_param, pc->pjsip_PARAM_CHAR_SPEC); } /* TTL param is not allowed in From, To, Route, and Record-Route header. */ if (url->ttl_param >= 0 && context != PJSIP_URI_IN_FROMTO_HDR && context != PJSIP_URI_IN_ROUTING_HDR) { if (endbuf - buf < 15) return -1; pj_memcpy(buf, ";ttl=", 5); printed = pj_utoa(url->ttl_param, buf+5); buf += printed + 5; } /* maddr param is not allowed in From and To header. */ if (context != PJSIP_URI_IN_FROMTO_HDR && url->maddr_param.slen) { /* Detect IPv6 IP address */ if (pj_memchr(url->maddr_param.ptr, ':', url->maddr_param.slen)) { copy_advance_pair_quote_cond(buf, ";maddr=", 7, url->maddr_param, '[', ']'); } else { copy_advance_pair_escape(buf, ";maddr=", 7, url->maddr_param, pc->pjsip_PARAM_CHAR_SPEC); } } /* lr param is not allowed in From, To, and Contact header. */ if (url->lr_param && context != PJSIP_URI_IN_FROMTO_HDR && context != PJSIP_URI_IN_CONTACT_HDR) { pj_str_t lr = { ";lr", 3 }; if (endbuf - buf < 3) return -1; copy_advance_check(buf, lr); } /* Other param. */ printed = (int)pjsip_param_print_on(&url->other_param, buf, endbuf-buf, &pc->pjsip_PARAM_CHAR_SPEC, &pc->pjsip_PARAM_CHAR_SPEC, ';'); if (printed < 0) return -1; buf += printed; /* Header param. * Header param is only allowed in these contexts: * - PJSIP_URI_IN_CONTACT_HDR * - PJSIP_URI_IN_OTHER */ if (context == PJSIP_URI_IN_CONTACT_HDR || context == PJSIP_URI_IN_OTHER) { printed = (int)pjsip_param_print_on(&url->header_param, buf, endbuf-buf, &pc->pjsip_HDR_CHAR_SPEC, &pc->pjsip_HDR_CHAR_SPEC, '?'); if (printed < 0) return -1; buf += printed; } *buf = '\0'; return buf-startbuf; } static pj_status_t pjsip_url_compare( pjsip_uri_context_e context, const pjsip_sip_uri *url1, const pjsip_sip_uri *url2) { const pjsip_param *p1; /* * Compare two SIP URL's according to Section 19.1.4 of RFC 3261. */ /* SIP and SIPS URI are never equivalent. * Note: just compare the vptr to avoid string comparison. * Pretty neat huh!! */ if (url1->vptr != url2->vptr) return PJSIP_ECMPSCHEME; /* Comparison of the userinfo of SIP and SIPS URIs is case-sensitive. * This includes userinfo containing passwords or formatted as * telephone-subscribers. */ if (pj_strcmp(&url1->user, &url2->user) != 0) return PJSIP_ECMPUSER; if (pj_strcmp(&url1->passwd, &url2->passwd) != 0) return PJSIP_ECMPPASSWD; /* Comparison of all other components of the URI is * case-insensitive unless explicitly defined otherwise. */ /* The ordering of parameters and header fields is not significant * in comparing SIP and SIPS URIs. */ /* Characters other than those in the reserved set (see RFC 2396 [5]) * are equivalent to their encoding. */ /* An IP address that is the result of a DNS lookup of a host name * does not match that host name. */ if (pj_stricmp(&url1->host, &url2->host) != 0) return PJSIP_ECMPHOST; /* A URI omitting any component with a default value will not match a URI * explicitly containing that component with its default value. * For instance, a URI omitting the optional port component will not match * a URI explicitly declaring port 5060. * The same is true for the transport-parameter, ttl-parameter, * user-parameter, and method components. */ /* Port is not allowed in To and From header. */ if (context != PJSIP_URI_IN_FROMTO_HDR) { if (url1->port != url2->port) return PJSIP_ECMPPORT; } /* Transport is not allowed in From/To header. */ if (context != PJSIP_URI_IN_FROMTO_HDR) { if (pj_stricmp(&url1->transport_param, &url2->transport_param) != 0) return PJSIP_ECMPTRANSPORTPRM; } /* TTL param is not allowed in From, To, Route, and Record-Route header. */ if (context != PJSIP_URI_IN_FROMTO_HDR && context != PJSIP_URI_IN_ROUTING_HDR) { if (url1->ttl_param != url2->ttl_param) return PJSIP_ECMPTTLPARAM; } /* User param is allowed in all contexes */ if (pj_stricmp(&url1->user_param, &url2->user_param) != 0) return PJSIP_ECMPUSERPARAM; /* Method param is only allowed in external/other context. */ if (context == PJSIP_URI_IN_OTHER) { if (pj_stricmp(&url1->method_param, &url2->method_param) != 0) return PJSIP_ECMPMETHODPARAM; } /* maddr param is not allowed in From and To header. */ if (context != PJSIP_URI_IN_FROMTO_HDR) { if (pj_stricmp(&url1->maddr_param, &url2->maddr_param) != 0) return PJSIP_ECMPMADDRPARAM; } /* lr parameter is ignored (?) */ /* lr param is not allowed in From, To, and Contact header. */ /* All other uri-parameters appearing in only one URI are ignored when * comparing the URIs. */ if (pjsip_param_cmp(&url1->other_param, &url2->other_param, 1)!=0) return PJSIP_ECMPOTHERPARAM; /* URI header components are never ignored. Any present header component * MUST be present in both URIs and match for the URIs to match. * The matching rules are defined for each header field in Section 20. */ p1 = url1->header_param.next; while (p1 != &url1->header_param) { const pjsip_param *p2; p2 = pjsip_param_find(&url2->header_param, &p1->name); if (p2) { /* It seems too much to compare two header params according to * the rule of each header. We'll just compare them string to * string.. */ if (pj_stricmp(&p1->value, &p2->value) != 0) return PJSIP_ECMPHEADERPARAM; } else { return PJSIP_ECMPHEADERPARAM; } p1 = p1->next; } /* Equal!! Pheuww.. */ return PJ_SUCCESS; } PJ_DEF(void) pjsip_sip_uri_assign(pj_pool_t *pool, pjsip_sip_uri *url, const pjsip_sip_uri *rhs) { pj_strdup( pool, &url->user, &rhs->user); pj_strdup( pool, &url->passwd, &rhs->passwd); pj_strdup( pool, &url->host, &rhs->host); url->port = rhs->port; pj_strdup( pool, &url->user_param, &rhs->user_param); pj_strdup( pool, &url->method_param, &rhs->method_param); pj_strdup( pool, &url->transport_param, &rhs->transport_param); url->ttl_param = rhs->ttl_param; pj_strdup( pool, &url->maddr_param, &rhs->maddr_param); pjsip_param_clone(pool, &url->other_param, &rhs->other_param); pjsip_param_clone(pool, &url->header_param, &rhs->header_param); url->lr_param = rhs->lr_param; } static pjsip_sip_uri* pjsip_url_clone(pj_pool_t *pool, const pjsip_sip_uri *rhs) { pjsip_sip_uri *url = PJ_POOL_ALLOC_T(pool, pjsip_sip_uri); if (!url) return NULL; pjsip_sip_uri_init(url, IS_SIPS(rhs)); pjsip_sip_uri_assign(pool, url, rhs); return url; } static const pj_str_t *pjsip_name_addr_get_scheme(const pjsip_name_addr *name) { pj_assert(name->uri != NULL); return pjsip_uri_get_scheme(name->uri); } PJ_DEF(void) pjsip_name_addr_init(pjsip_name_addr *name) { name->vptr = &name_addr_vptr; name->uri = NULL; name->display.slen = 0; name->display.ptr = NULL; } PJ_DEF(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool) { pjsip_name_addr *name_addr = PJ_POOL_ALLOC_T(pool, pjsip_name_addr); pjsip_name_addr_init(name_addr); return name_addr; } static pj_ssize_t pjsip_name_addr_print(pjsip_uri_context_e context, const pjsip_name_addr *name, char *buf, pj_size_t size) { int printed; char *startbuf = buf; char *endbuf = buf + size; pjsip_uri *uri; uri = (pjsip_uri*) pjsip_uri_get_uri(name->uri); pj_assert(uri != NULL); if (context != PJSIP_URI_IN_REQ_URI) { if (name->display.slen) { if (endbuf-buf < name->display.slen + 3) return -1; copy_advance_char_check(buf, '"'); copy_advance(buf, name->display); copy_advance_char_check(buf, '"'); copy_advance_char_check(buf, ' '); } copy_advance_char_check(buf, '<');; } printed = pjsip_uri_print(context,uri, buf, size-(buf-startbuf)); if (printed < 1) return -1; buf += printed; if (context != PJSIP_URI_IN_REQ_URI) { copy_advance_char_check(buf, '>'); } *buf = '\0'; return buf-startbuf; } PJ_DEF(void) pjsip_name_addr_assign(pj_pool_t *pool, pjsip_name_addr *dst, const pjsip_name_addr *src) { pj_strdup( pool, &dst->display, &src->display); dst->uri = (pjsip_uri*) pjsip_uri_clone(pool, src->uri); } static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool, const pjsip_name_addr *rhs) { pjsip_name_addr *addr = PJ_POOL_ALLOC_T(pool, pjsip_name_addr); if (!addr) return NULL; pjsip_name_addr_init(addr); pjsip_name_addr_assign(pool, addr, rhs); return addr; } static int pjsip_name_addr_compare( pjsip_uri_context_e context, const pjsip_name_addr *naddr1, const pjsip_name_addr *naddr2) { int d; /* Check that naddr2 is also a name_addr */ if (naddr1->vptr != naddr2->vptr) return -1; /* I'm not sure whether display name is included in the comparison. */ if (pj_strcmp(&naddr1->display, &naddr2->display) != 0) { return -1; } pj_assert( naddr1->uri != NULL ); pj_assert( naddr2->uri != NULL ); /* Compare name-addr as URL */ d = pjsip_uri_cmp( context, naddr1->uri, naddr2->uri); if (d) return d; return 0; } /////////////////////////////////////////////////////////////////////////////// static const pj_str_t *other_uri_get_scheme( const pjsip_other_uri*); static void *other_uri_get_uri( pjsip_other_uri*); static pj_ssize_t other_uri_print( pjsip_uri_context_e context, const pjsip_other_uri *url, char *buf, pj_size_t size); static int other_uri_cmp( pjsip_uri_context_e context, const pjsip_other_uri *url1, const pjsip_other_uri *url2); static pjsip_other_uri* other_uri_clone( pj_pool_t *pool, const pjsip_other_uri *rhs); static pjsip_uri_vptr other_uri_vptr = { (P_GET_SCHEME) &other_uri_get_scheme, (P_GET_URI) &other_uri_get_uri, (P_PRINT_URI) &other_uri_print, (P_CMP_URI) &other_uri_cmp, (P_CLONE) &other_uri_clone }; PJ_DEF(pjsip_other_uri*) pjsip_other_uri_create(pj_pool_t *pool) { pjsip_other_uri *uri = PJ_POOL_ZALLOC_T(pool, pjsip_other_uri); uri->vptr = &other_uri_vptr; return uri; } static const pj_str_t *other_uri_get_scheme( const pjsip_other_uri *uri ) { return &uri->scheme; } static void *other_uri_get_uri( pjsip_other_uri *uri ) { return uri; } static pj_ssize_t other_uri_print(pjsip_uri_context_e context, const pjsip_other_uri *uri, char *buf, pj_size_t size) { char *startbuf = buf; char *endbuf = buf + size; PJ_UNUSED_ARG(context); if (uri->scheme.slen + uri->content.slen + 1 > (int)size) return -1; /* Print scheme. */ copy_advance(buf, uri->scheme); *buf++ = ':'; /* Print content. */ copy_advance(buf, uri->content); return (buf - startbuf); } static int other_uri_cmp(pjsip_uri_context_e context, const pjsip_other_uri *uri1, const pjsip_other_uri *uri2) { PJ_UNUSED_ARG(context); /* Check that uri2 is also an other_uri */ if (uri1->vptr != uri2->vptr) return -1; /* Scheme must match. */ if (pj_stricmp(&uri1->scheme, &uri2->scheme) != 0) { return PJSIP_ECMPSCHEME; } /* Content must match. */ if(pj_stricmp(&uri1->content, &uri2->content) != 0) { return -1; } /* Equal. */ return 0; } /* Clone *: URI */ static pjsip_other_uri* other_uri_clone(pj_pool_t *pool, const pjsip_other_uri *rhs) { pjsip_other_uri *uri = pjsip_other_uri_create(pool); pj_strdup(pool, &uri->scheme, &rhs->scheme); pj_strdup(pool, &uri->content, &rhs->content); return uri; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_util.c ================================================ /* $Id: sip_util.c 4442 2013-03-19 07:39:25Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "endpoint" static const char *event_str[] = { "UNIDENTIFIED", "TIMER", "TX_MSG", "RX_MSG", "TRANSPORT_ERROR", "TSX_STATE", "USER", }; static pj_str_t str_TEXT = { "text", 4}, str_PLAIN = { "plain", 5 }; /* Add URI to target-set */ PJ_DEF(pj_status_t) pjsip_target_set_add_uri( pjsip_target_set *tset, pj_pool_t *pool, const pjsip_uri *uri, int q1000) { pjsip_target *t, *pos = NULL; PJ_ASSERT_RETURN(tset && pool && uri, PJ_EINVAL); /* Set q-value to 1 if it is not set */ if (q1000 <= 0) q1000 = 1000; /* Scan all the elements to see for duplicates, and at the same time * get the position where the new element should be inserted to * based on the q-value. */ t = tset->head.next; while (t != &tset->head) { if (pjsip_uri_cmp(PJSIP_URI_IN_REQ_URI, t->uri, uri)==PJ_SUCCESS) return PJ_EEXISTS; if (pos==NULL && t->q1000 < q1000) pos = t; t = t->next; } /* Create new element */ t = PJ_POOL_ZALLOC_T(pool, pjsip_target); t->uri = (pjsip_uri*)pjsip_uri_clone(pool, uri); t->q1000 = q1000; /* Insert */ if (pos == NULL) pj_list_push_back(&tset->head, t); else pj_list_insert_before(pos, t); /* Set current target if this is the first URI */ if (tset->current == NULL) tset->current = t; return PJ_SUCCESS; } /* Add URI's in the Contact header in the message to target-set */ PJ_DEF(pj_status_t) pjsip_target_set_add_from_msg( pjsip_target_set *tset, pj_pool_t *pool, const pjsip_msg *msg) { const pjsip_hdr *hdr; unsigned added = 0; PJ_ASSERT_RETURN(tset && pool && msg, PJ_EINVAL); /* Scan for Contact headers and add the URI */ hdr = msg->hdr.next; while (hdr != &msg->hdr) { if (hdr->type == PJSIP_H_CONTACT) { const pjsip_contact_hdr *cn_hdr = (const pjsip_contact_hdr*)hdr; if (!cn_hdr->star) { pj_status_t rc; rc = pjsip_target_set_add_uri(tset, pool, cn_hdr->uri, cn_hdr->q1000); if (rc == PJ_SUCCESS) ++added; } } hdr = hdr->next; } return added ? PJ_SUCCESS : PJ_EEXISTS; } /* Get next target, if any */ PJ_DEF(pjsip_target*) pjsip_target_set_get_next(const pjsip_target_set *tset) { const pjsip_target *t, *next = NULL; t = tset->head.next; while (t != &tset->head) { if (PJSIP_IS_STATUS_IN_CLASS(t->code, 200)) { /* No more target since one target has been successful */ return NULL; } if (PJSIP_IS_STATUS_IN_CLASS(t->code, 600)) { /* No more target since one target returned global error */ return NULL; } if (t->code==0 && next==NULL) { /* This would be the next target as long as we don't find * targets with 2xx or 6xx status after this. */ next = t; } t = t->next; } return (pjsip_target*)next; } /* Set current target */ PJ_DEF(pj_status_t) pjsip_target_set_set_current( pjsip_target_set *tset, pjsip_target *target) { PJ_ASSERT_RETURN(tset && target, PJ_EINVAL); PJ_ASSERT_RETURN(pj_list_find_node(tset, target) != NULL, PJ_ENOTFOUND); tset->current = target; return PJ_SUCCESS; } /* Assign status to a target */ PJ_DEF(pj_status_t) pjsip_target_assign_status( pjsip_target *target, pj_pool_t *pool, int status_code, const pj_str_t *reason) { PJ_ASSERT_RETURN(target && pool && status_code && reason, PJ_EINVAL); target->code = (pjsip_status_code)status_code; pj_strdup(pool, &target->reason, reason); return PJ_SUCCESS; } /* * Initialize transmit data (msg) with the headers and optional body. * This will just put the headers in the message as it is. Be carefull * when calling this function because once a header is put in a message, * it CAN NOT be put in other message until the first message is deleted, * because the way the header is put in the list. * That's why the session will shallow_clone it's headers before calling * this function. */ static void init_request_throw( pjsip_endpoint *endpt, pjsip_tx_data *tdata, pjsip_method *method, pjsip_uri *param_target, pjsip_from_hdr *param_from, pjsip_to_hdr *param_to, pjsip_contact_hdr *param_contact, pjsip_cid_hdr *param_call_id, pjsip_cseq_hdr *param_cseq, const pj_str_t *param_text) { pjsip_msg *msg; pjsip_msg_body *body; pjsip_via_hdr *via; const pjsip_hdr *endpt_hdr; /* Create the message. */ msg = tdata->msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); /* Init request URI. */ pj_memcpy(&msg->line.req.method, method, sizeof(*method)); msg->line.req.uri = param_target; /* Add additional request headers from endpoint. */ endpt_hdr = pjsip_endpt_get_request_headers(endpt)->next; while (endpt_hdr != pjsip_endpt_get_request_headers(endpt)) { pjsip_hdr *hdr = (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, endpt_hdr); pjsip_msg_add_hdr( tdata->msg, hdr ); endpt_hdr = endpt_hdr->next; } /* Add From header. */ if (param_from->tag.slen == 0) pj_create_unique_string(tdata->pool, ¶m_from->tag); pjsip_msg_add_hdr(msg, (pjsip_hdr*)param_from); /* Add To header. */ pjsip_msg_add_hdr(msg, (pjsip_hdr*)param_to); /* Add Contact header. */ if (param_contact) { pjsip_msg_add_hdr(msg, (pjsip_hdr*)param_contact); } /* Add Call-ID header. */ pjsip_msg_add_hdr(msg, (pjsip_hdr*)param_call_id); /* Add CSeq header. */ pjsip_msg_add_hdr(msg, (pjsip_hdr*)param_cseq); /* Add a blank Via header in the front of the message. */ via = pjsip_via_hdr_create(tdata->pool); via->rport_param = pjsip_cfg()->endpt.disable_rport ? -1 : 0; pjsip_msg_insert_first_hdr(msg, (pjsip_hdr*)via); /* Add header params as request headers */ if (PJSIP_URI_SCHEME_IS_SIP(param_target) || PJSIP_URI_SCHEME_IS_SIPS(param_target)) { pjsip_sip_uri *uri = (pjsip_sip_uri*) pjsip_uri_get_uri(param_target); pjsip_param *hparam; hparam = uri->header_param.next; while (hparam != &uri->header_param) { pjsip_generic_string_hdr *hdr; hdr = pjsip_generic_string_hdr_create(tdata->pool, &hparam->name, &hparam->value); pjsip_msg_add_hdr(msg, (pjsip_hdr*)hdr); hparam = hparam->next; } } /* Create message body. */ if (param_text) { body = PJ_POOL_ZALLOC_T(tdata->pool, pjsip_msg_body); body->content_type.type = str_TEXT; body->content_type.subtype = str_PLAIN; body->data = pj_pool_alloc(tdata->pool, param_text->slen ); pj_memcpy(body->data, param_text->ptr, param_text->slen); body->len = (unsigned)param_text->slen; body->print_body = &pjsip_print_text_body; msg->body = body; } PJ_LOG(5,(THIS_FILE, "%s created.", pjsip_tx_data_get_info(tdata))); } /* * Create arbitrary request. */ PJ_DEF(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt, const pjsip_method *method, const pj_str_t *param_target, const pj_str_t *param_from, const pj_str_t *param_to, const pj_str_t *param_contact, const pj_str_t *param_call_id, int param_cseq, const pj_str_t *param_text, pjsip_tx_data **p_tdata) { pjsip_uri *target; pjsip_tx_data *tdata; pjsip_from_hdr *from; pjsip_to_hdr *to; pjsip_contact_hdr *contact; pjsip_cseq_hdr *cseq = NULL; /* = NULL, warning in VC6 */ pjsip_cid_hdr *call_id; pj_str_t tmp; pj_status_t status; const pj_str_t STR_CONTACT = { "Contact", 7 }; PJ_USE_EXCEPTION; status = pjsip_endpt_create_tdata(endpt, &tdata); if (status != PJ_SUCCESS) return status; /* Init reference counter to 1. */ pjsip_tx_data_add_ref(tdata); PJ_TRY { /* Request target. */ pj_strdup_with_null(tdata->pool, &tmp, param_target); target = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 0); if (target == NULL) { status = PJSIP_EINVALIDREQURI; goto on_error; } /* From */ from = pjsip_from_hdr_create(tdata->pool); pj_strdup_with_null(tdata->pool, &tmp, param_from); from->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); if (from->uri == NULL) { status = PJSIP_EINVALIDHDR; goto on_error; } pj_create_unique_string(tdata->pool, &from->tag); /* To */ to = pjsip_to_hdr_create(tdata->pool); pj_strdup_with_null(tdata->pool, &tmp, param_to); to->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); if (to->uri == NULL) { status = PJSIP_EINVALIDHDR; goto on_error; } /* Contact. */ if (param_contact) { pj_strdup_with_null(tdata->pool, &tmp, param_contact); contact = (pjsip_contact_hdr*) pjsip_parse_hdr(tdata->pool, &STR_CONTACT, tmp.ptr, tmp.slen, NULL); if (contact == NULL) { status = PJSIP_EINVALIDHDR; goto on_error; } } else { contact = NULL; } /* Call-ID */ call_id = pjsip_cid_hdr_create(tdata->pool); if (param_call_id != NULL && param_call_id->slen) pj_strdup(tdata->pool, &call_id->id, param_call_id); else pj_create_unique_string(tdata->pool, &call_id->id); /* CSeq */ cseq = pjsip_cseq_hdr_create(tdata->pool); if (param_cseq >= 0) cseq->cseq = param_cseq; else cseq->cseq = pj_rand() & 0xFFFF; /* Method */ pjsip_method_copy(tdata->pool, &cseq->method, method); /* Create the request. */ init_request_throw( endpt, tdata, &cseq->method, target, from, to, contact, call_id, cseq, param_text); } PJ_CATCH_ANY { status = PJ_ENOMEM; goto on_error; } PJ_END *p_tdata = tdata; return PJ_SUCCESS; on_error: pjsip_tx_data_dec_ref(tdata); return status; } PJ_DEF(pj_status_t) pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt, const pjsip_method *method, const pjsip_uri *param_target, const pjsip_from_hdr *param_from, const pjsip_to_hdr *param_to, const pjsip_contact_hdr *param_contact, const pjsip_cid_hdr *param_call_id, int param_cseq, const pj_str_t *param_text, pjsip_tx_data **p_tdata) { pjsip_uri *target; pjsip_tx_data *tdata; pjsip_from_hdr *from; pjsip_to_hdr *to; pjsip_contact_hdr *contact; pjsip_cid_hdr *call_id; pjsip_cseq_hdr *cseq = NULL; /* The NULL because warning in VC6 */ pj_status_t status; PJ_USE_EXCEPTION; /* Check arguments. */ PJ_ASSERT_RETURN(endpt && method && param_target && param_from && param_to && p_tdata, PJ_EINVAL); /* Create new transmit data. */ status = pjsip_endpt_create_tdata(endpt, &tdata); if (status != PJ_SUCCESS) return status; /* Set initial reference counter to 1. */ pjsip_tx_data_add_ref(tdata); PJ_TRY { /* Duplicate target URI and headers. */ target = (pjsip_uri*) pjsip_uri_clone(tdata->pool, param_target); from = (pjsip_from_hdr*) pjsip_hdr_clone(tdata->pool, param_from); pjsip_fromto_hdr_set_from(from); to = (pjsip_to_hdr*) pjsip_hdr_clone(tdata->pool, param_to); pjsip_fromto_hdr_set_to(to); if (param_contact) { contact = (pjsip_contact_hdr*) pjsip_hdr_clone(tdata->pool, param_contact); } else { contact = NULL; } call_id = pjsip_cid_hdr_create(tdata->pool); if (param_call_id != NULL && param_call_id->id.slen) pj_strdup(tdata->pool, &call_id->id, ¶m_call_id->id); else pj_create_unique_string(tdata->pool, &call_id->id); cseq = pjsip_cseq_hdr_create(tdata->pool); if (param_cseq >= 0) cseq->cseq = param_cseq; else cseq->cseq = pj_rand() % 0xFFFF; pjsip_method_copy(tdata->pool, &cseq->method, method); /* Copy headers to the request. */ init_request_throw(endpt, tdata, &cseq->method, target, from, to, contact, call_id, cseq, param_text); } PJ_CATCH_ANY { status = PJ_ENOMEM; goto on_error; } PJ_END; *p_tdata = tdata; return PJ_SUCCESS; on_error: pjsip_tx_data_dec_ref(tdata); return status; } /* * Construct a minimal response message for the received request. */ PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt, const pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pjsip_msg *msg, *req_msg; pjsip_hdr *hdr; pjsip_to_hdr *to_hdr; pjsip_via_hdr *top_via = NULL, *via; pjsip_rr_hdr *rr; pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(endpt && rdata && p_tdata, PJ_EINVAL); /* Check status code. */ PJ_ASSERT_RETURN(st_code >= 100 && st_code <= 699, PJ_EINVAL); /* rdata must be a request message. */ req_msg = rdata->msg_info.msg; pj_assert(req_msg->type == PJSIP_REQUEST_MSG); /* Request MUST NOT be ACK request! */ PJ_ASSERT_RETURN(req_msg->line.req.method.id != PJSIP_ACK_METHOD, PJ_EINVALIDOP); /* Create a new transmit buffer. */ status = pjsip_endpt_create_tdata( endpt, &tdata); if (status != PJ_SUCCESS) return status; /* Set initial reference count to 1. */ pjsip_tx_data_add_ref(tdata); /* Create new response message. */ tdata->msg = msg = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG); /* Set status code and reason text. */ msg->line.status.code = st_code; if (st_text) pj_strdup(tdata->pool, &msg->line.status.reason, st_text); else msg->line.status.reason = *pjsip_get_status_text(st_code); /* Set TX data attributes. */ tdata->rx_timestamp = rdata->pkt_info.timestamp; /* Copy all the via headers, in order. */ via = rdata->msg_info.via; while (via) { pjsip_via_hdr *new_via; new_via = (pjsip_via_hdr*)pjsip_hdr_clone(tdata->pool, via); if (top_via == NULL) top_via = new_via; pjsip_msg_add_hdr( msg, (pjsip_hdr*)new_via); via = via->next; if (via != (void*)&req_msg->hdr) via = (pjsip_via_hdr*) pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, via); else break; } /* Copy all Record-Route headers, in order. */ rr = (pjsip_rr_hdr*) pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, NULL); while (rr) { pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, rr)); rr = rr->next; if (rr != (void*)&req_msg->hdr) rr = (pjsip_rr_hdr*) pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, rr); else break; } /* Copy Call-ID header. */ hdr = (pjsip_hdr*) pjsip_msg_find_hdr( req_msg, PJSIP_H_CALL_ID, NULL); pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr)); /* Copy From header. */ hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, rdata->msg_info.from); pjsip_msg_add_hdr( msg, hdr); /* Copy To header. */ to_hdr = (pjsip_to_hdr*) pjsip_hdr_clone(tdata->pool, rdata->msg_info.to); pjsip_msg_add_hdr( msg, (pjsip_hdr*)to_hdr); /* Must add To tag in the response (Section 8.2.6.2), except if this is * 100 (Trying) response. Same tag must be created for the same request * (e.g. same tag in provisional and final response). The easiest way * to do this is to derive the tag from Via branch parameter (or to * use it directly). */ if (to_hdr->tag.slen==0 && st_code > 100 && top_via) { to_hdr->tag = top_via->branch_param; } /* Copy CSeq header. */ hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, rdata->msg_info.cseq); pjsip_msg_add_hdr( msg, hdr); /* All done. */ *p_tdata = tdata; PJ_LOG(5,(THIS_FILE, "%s created", pjsip_tx_data_get_info(tdata))); return PJ_SUCCESS; } /* * Construct ACK for 3xx-6xx final response (according to chapter 17.1.1 of * RFC3261). Note that the generation of ACK for 2xx response is different, * and one must not use this function to generate such ACK. */ PJ_DEF(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt, const pjsip_tx_data *tdata, const pjsip_rx_data *rdata, pjsip_tx_data **ack_tdata) { pjsip_tx_data *ack = NULL; const pjsip_msg *invite_msg; const pjsip_from_hdr *from_hdr; const pjsip_to_hdr *to_hdr; const pjsip_cid_hdr *cid_hdr; const pjsip_cseq_hdr *cseq_hdr; const pjsip_hdr *hdr; pjsip_hdr *via; pjsip_to_hdr *to; pj_status_t status; /* rdata must be a non-2xx final response. */ pj_assert(rdata->msg_info.msg->type==PJSIP_RESPONSE_MSG && rdata->msg_info.msg->line.status.code >= 300); /* Initialize return value to NULL. */ *ack_tdata = NULL; /* The original INVITE message. */ invite_msg = tdata->msg; /* Get the headers from original INVITE request. */ # define FIND_HDR(m,HNAME) pjsip_msg_find_hdr(m, PJSIP_H_##HNAME, NULL) from_hdr = (const pjsip_from_hdr*) FIND_HDR(invite_msg, FROM); PJ_ASSERT_ON_FAIL(from_hdr != NULL, goto on_missing_hdr); to_hdr = (const pjsip_to_hdr*) FIND_HDR(invite_msg, TO); PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr); cid_hdr = (const pjsip_cid_hdr*) FIND_HDR(invite_msg, CALL_ID); PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr); cseq_hdr = (const pjsip_cseq_hdr*) FIND_HDR(invite_msg, CSEQ); PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr); # undef FIND_HDR /* Create new request message from the headers. */ status = pjsip_endpt_create_request_from_hdr(endpt, pjsip_get_ack_method(), tdata->msg->line.req.uri, from_hdr, to_hdr, NULL, cid_hdr, cseq_hdr->cseq, NULL, &ack); if (status != PJ_SUCCESS) return status; /* Update tag in To header with the one from the response (if any). */ to = (pjsip_to_hdr*) pjsip_msg_find_hdr(ack->msg, PJSIP_H_TO, NULL); pj_strdup(ack->pool, &to->tag, &rdata->msg_info.to->tag); /* Clear Via headers in the new request. */ while ((via=(pjsip_hdr*)pjsip_msg_find_hdr(ack->msg, PJSIP_H_VIA, NULL)) != NULL) pj_list_erase(via); /* Must contain single Via, just as the original INVITE. */ hdr = (pjsip_hdr*) pjsip_msg_find_hdr( invite_msg, PJSIP_H_VIA, NULL); pjsip_msg_insert_first_hdr( ack->msg, (pjsip_hdr*) pjsip_hdr_clone(ack->pool,hdr) ); /* If the original INVITE has Route headers, those header fields MUST * appear in the ACK. */ hdr = (pjsip_hdr*) pjsip_msg_find_hdr( invite_msg, PJSIP_H_ROUTE, NULL); while (hdr != NULL) { pjsip_msg_add_hdr( ack->msg, (pjsip_hdr*) pjsip_hdr_clone(ack->pool, hdr) ); hdr = hdr->next; if (hdr == &invite_msg->hdr) break; hdr = (pjsip_hdr*) pjsip_msg_find_hdr( invite_msg, PJSIP_H_ROUTE, hdr); } /* We're done. * "tdata" parameter now contains the ACK message. */ *ack_tdata = ack; return PJ_SUCCESS; on_missing_hdr: if (ack) pjsip_tx_data_dec_ref(ack); return PJSIP_EMISSINGHDR; } /* * Construct CANCEL request for the previously sent request, according to * chapter 9.1 of RFC3261. */ PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt, const pjsip_tx_data *req_tdata, pjsip_tx_data **p_tdata) { pjsip_tx_data *cancel_tdata = NULL; const pjsip_from_hdr *from_hdr; const pjsip_to_hdr *to_hdr; const pjsip_cid_hdr *cid_hdr; const pjsip_cseq_hdr *cseq_hdr; const pjsip_hdr *hdr; pjsip_hdr *via; pj_status_t status; /* The transmit buffer must INVITE request. */ PJ_ASSERT_RETURN(req_tdata->msg->type == PJSIP_REQUEST_MSG && req_tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD, PJ_EINVAL); /* Get the headers from original INVITE request. */ # define FIND_HDR(m,HNAME) pjsip_msg_find_hdr(m, PJSIP_H_##HNAME, NULL) from_hdr = (const pjsip_from_hdr*) FIND_HDR(req_tdata->msg, FROM); PJ_ASSERT_ON_FAIL(from_hdr != NULL, goto on_missing_hdr); to_hdr = (const pjsip_to_hdr*) FIND_HDR(req_tdata->msg, TO); PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr); cid_hdr = (const pjsip_cid_hdr*) FIND_HDR(req_tdata->msg, CALL_ID); PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr); cseq_hdr = (const pjsip_cseq_hdr*) FIND_HDR(req_tdata->msg, CSEQ); PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr); # undef FIND_HDR /* Create new request message from the headers. */ status = pjsip_endpt_create_request_from_hdr(endpt, pjsip_get_cancel_method(), req_tdata->msg->line.req.uri, from_hdr, to_hdr, NULL, cid_hdr, cseq_hdr->cseq, NULL, &cancel_tdata); if (status != PJ_SUCCESS) return status; /* Clear Via headers in the new request. */ while ((via=(pjsip_hdr*)pjsip_msg_find_hdr(cancel_tdata->msg, PJSIP_H_VIA, NULL)) != NULL) pj_list_erase(via); /* Must only have single Via which matches the top-most Via in the * request being cancelled. */ hdr = (pjsip_hdr*) pjsip_msg_find_hdr(req_tdata->msg, PJSIP_H_VIA, NULL); if (hdr) { pjsip_msg_insert_first_hdr(cancel_tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(cancel_tdata->pool, hdr)); } /* If the original request has Route header, the CANCEL request must also * has exactly the same. * Copy "Route" header from the request. */ hdr = (pjsip_hdr*) pjsip_msg_find_hdr(req_tdata->msg, PJSIP_H_ROUTE, NULL); while (hdr != NULL) { pjsip_msg_add_hdr(cancel_tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(cancel_tdata->pool, hdr)); hdr = hdr->next; if (hdr != &req_tdata->msg->hdr) hdr = (pjsip_hdr*) pjsip_msg_find_hdr(req_tdata->msg, PJSIP_H_ROUTE, hdr); else break; } /* Must also copy the saved strict route header, otherwise CANCEL will be * sent with swapped Route and request URI! */ if (req_tdata->saved_strict_route) { cancel_tdata->saved_strict_route = (pjsip_route_hdr*) pjsip_hdr_clone(cancel_tdata->pool, req_tdata->saved_strict_route); } /* Copy the destination host name from the original request */ pj_strdup(cancel_tdata->pool, &cancel_tdata->dest_info.name, &req_tdata->dest_info.name); /* Finally copy the destination info from the original request */ pj_memcpy(&cancel_tdata->dest_info, &req_tdata->dest_info, sizeof(req_tdata->dest_info)); /* Done. * Return the transmit buffer containing the CANCEL request. */ *p_tdata = cancel_tdata; return PJ_SUCCESS; on_missing_hdr: if (cancel_tdata) pjsip_tx_data_dec_ref(cancel_tdata); return PJSIP_EMISSINGHDR; } /* Fill-up destination information from a target URI */ PJ_DEF(pj_status_t) pjsip_get_dest_info(const pjsip_uri *target_uri, const pjsip_uri *request_uri, pj_pool_t *pool, pjsip_host_info *dest_info) { /* The target URI must be a SIP/SIPS URL so we can resolve it's address. * Otherwise we're in trouble (i.e. there's no host part in tel: URL). */ pj_bzero(dest_info, sizeof(*dest_info)); /* When request URI uses sips scheme, TLS must always be used regardless * of the target scheme or transport type (see ticket #1740). */ if (PJSIP_URI_SCHEME_IS_SIPS(target_uri) || (pjsip_cfg()->endpt.disable_tls_switch == 0 && request_uri && PJSIP_URI_SCHEME_IS_SIPS(request_uri))) { pjsip_uri *uri = (pjsip_uri*) target_uri; const pjsip_sip_uri *url=(const pjsip_sip_uri*)pjsip_uri_get_uri(uri); unsigned flag; if (!PJSIP_URI_SCHEME_IS_SIPS(target_uri)) { PJ_LOG(4,(THIS_FILE, "Automatic switch to TLS transport as " "request-URI uses ""sips"" scheme.")); } dest_info->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE); if (url->maddr_param.slen) pj_strdup(pool, &dest_info->addr.host, &url->maddr_param); else pj_strdup(pool, &dest_info->addr.host, &url->host); dest_info->addr.port = url->port; dest_info->type = pjsip_transport_get_type_from_name(&url->transport_param); /* Double-check that the transport parameter match. * Sample case: sips:host;transport=tcp * See https://trac.pjsip.org/repos/ticket/1319 */ flag = pjsip_transport_get_flag_from_type(dest_info->type); if ((flag & dest_info->flag) != dest_info->flag) { pjsip_transport_type_e t; t = pjsip_transport_get_type_from_flag(dest_info->flag); if (t != PJSIP_TRANSPORT_UNSPECIFIED) dest_info->type = t; } } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) { pjsip_uri *uri = (pjsip_uri*) target_uri; const pjsip_sip_uri *url=(const pjsip_sip_uri*)pjsip_uri_get_uri(uri); if (url->maddr_param.slen) pj_strdup(pool, &dest_info->addr.host, &url->maddr_param); else pj_strdup(pool, &dest_info->addr.host, &url->host); dest_info->addr.port = url->port; dest_info->type = pjsip_transport_get_type_from_name(&url->transport_param); dest_info->flag = pjsip_transport_get_flag_from_type(dest_info->type); } else { /* Should have never reached here; app should have configured route * set when sending to tel: URI pj_assert(!"Unsupported URI scheme!"); */ PJ_TODO(SUPPORT_REQUEST_ADDR_RESOLUTION_FOR_TEL_URI); return PJSIP_ENOROUTESET; } /* Handle IPv6 (http://trac.pjsip.org/repos/ticket/861) */ if (dest_info->type != PJSIP_TRANSPORT_UNSPECIFIED && pj_strchr(&dest_info->addr.host, ':')) { dest_info->type = (pjsip_transport_type_e) ((int)dest_info->type | PJSIP_TRANSPORT_IPV6); } return PJ_SUCCESS; } /* * Find which destination to be used to send the request message, based * on the request URI and Route headers in the message. The procedure * used here follows the guidelines on sending the request in RFC 3261 * chapter 8.1.2. */ PJ_DEF(pj_status_t) pjsip_get_request_dest(const pjsip_tx_data *tdata, pjsip_host_info *dest_info ) { const pjsip_uri *target_uri; const pjsip_route_hdr *first_route_hdr; PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); PJ_ASSERT_RETURN(dest_info != NULL, PJ_EINVAL); /* Get the first "Route" header from the message. */ first_route_hdr = (const pjsip_route_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL); if (first_route_hdr) { target_uri = first_route_hdr->name_addr.uri; } else { target_uri = tdata->msg->line.req.uri; } return pjsip_get_dest_info(target_uri, tdata->msg->line.req.uri, (pj_pool_t*)tdata->pool, dest_info); } /* * Process route-set found in the request and calculate * the destination to be used to send the request message, based * on the request URI and Route headers in the message. The procedure * used here follows the guidelines on sending the request in RFC 3261 * chapter 8.1.2. */ PJ_DEF(pj_status_t) pjsip_process_route_set(pjsip_tx_data *tdata, pjsip_host_info *dest_info ) { const pjsip_uri *new_request_uri, *target_uri; const pjsip_name_addr *topmost_route_uri; pjsip_route_hdr *first_route_hdr, *last_route_hdr; pj_status_t status; PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); PJ_ASSERT_RETURN(dest_info != NULL, PJ_EINVAL); /* If the request contains strict route, check that the strict route * has been restored to its original values before processing the * route set. The strict route is restored to the original values * with pjsip_restore_strict_route_set(). If caller did not restore * the strict route before calling this function, we need to call it * here, or otherwise the strict-route and Request-URI will be swapped * twice! */ if (tdata->saved_strict_route != NULL) { pjsip_restore_strict_route_set(tdata); } PJ_ASSERT_RETURN(tdata->saved_strict_route==NULL, PJ_EBUG); /* Find the first and last "Route" headers from the message. */ last_route_hdr = first_route_hdr = (pjsip_route_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL); if (first_route_hdr) { topmost_route_uri = &first_route_hdr->name_addr; while (last_route_hdr->next != (void*)&tdata->msg->hdr) { pjsip_route_hdr *hdr; hdr = (pjsip_route_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, last_route_hdr->next); if (!hdr) break; last_route_hdr = hdr; } } else { topmost_route_uri = NULL; } /* If Route headers exist, and the first element indicates loose-route, * the URI is taken from the Request-URI, and we keep all existing Route * headers intact. * If Route headers exist, and the first element DOESN'T indicate loose * route, the URI is taken from the first Route header, and remove the * first Route header from the message. * Otherwise if there's no Route headers, the URI is taken from the * Request-URI. */ if (topmost_route_uri) { pj_bool_t has_lr_param; if (PJSIP_URI_SCHEME_IS_SIP(topmost_route_uri) || PJSIP_URI_SCHEME_IS_SIPS(topmost_route_uri)) { const pjsip_sip_uri *url = (const pjsip_sip_uri*) pjsip_uri_get_uri((const void*)topmost_route_uri); has_lr_param = url->lr_param; } else { has_lr_param = 0; } if (has_lr_param) { new_request_uri = tdata->msg->line.req.uri; /* We shouldn't need to delete topmost Route if it has lr param. * But seems like it breaks some proxy implementation, so we * delete it anyway. */ /* pj_list_erase(first_route_hdr); if (first_route_hdr == last_route_hdr) last_route_hdr = NULL; */ } else { new_request_uri = (const pjsip_uri*) pjsip_uri_get_uri((pjsip_uri*)topmost_route_uri); pj_list_erase(first_route_hdr); tdata->saved_strict_route = first_route_hdr; if (first_route_hdr == last_route_hdr) first_route_hdr = last_route_hdr = NULL; } target_uri = (pjsip_uri*)topmost_route_uri; } else { target_uri = new_request_uri = tdata->msg->line.req.uri; } /* Fill up the destination host/port from the URI. */ status = pjsip_get_dest_info(target_uri, new_request_uri, tdata->pool, dest_info); if (status != PJ_SUCCESS) return status; /* If target URI is different than request URI, replace * request URI add put the original URI in the last Route header. */ if (new_request_uri && new_request_uri!=tdata->msg->line.req.uri) { pjsip_route_hdr *route = pjsip_route_hdr_create(tdata->pool); route->name_addr.uri = (pjsip_uri*) pjsip_uri_get_uri(tdata->msg->line.req.uri); if (last_route_hdr) pj_list_insert_after(last_route_hdr, route); else pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route); tdata->msg->line.req.uri = (pjsip_uri*)new_request_uri; } /* Success. */ return PJ_SUCCESS; } /* * Swap the request URI and strict route back to the original position * before #pjsip_process_route_set() function is called. This function * should only used internally by PJSIP client authentication module. */ PJ_DEF(void) pjsip_restore_strict_route_set(pjsip_tx_data *tdata) { pjsip_route_hdr *first_route_hdr, *last_route_hdr; /* Check if we have found strict route before */ if (tdata->saved_strict_route == NULL) { /* This request doesn't contain strict route */ return; } /* Find the first "Route" headers from the message. */ first_route_hdr = (pjsip_route_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL); if (first_route_hdr == NULL) { /* User has modified message route? We don't expect this! */ pj_assert(!"Message route was modified?"); tdata->saved_strict_route = NULL; return; } /* Find last Route header */ last_route_hdr = first_route_hdr; while (last_route_hdr->next != (void*)&tdata->msg->hdr) { pjsip_route_hdr *hdr; hdr = (pjsip_route_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, last_route_hdr->next); if (!hdr) break; last_route_hdr = hdr; } /* Put the last Route header as request URI, delete last Route * header, and insert the saved strict route as the first Route. */ tdata->msg->line.req.uri = last_route_hdr->name_addr.uri; pj_list_insert_before(first_route_hdr, tdata->saved_strict_route); pj_list_erase(last_route_hdr); /* Reset */ tdata->saved_strict_route = NULL; } /* Transport callback for sending stateless request. * This is one of the most bizzare function in pjsip, so * good luck if you happen to debug this function!! */ static void stateless_send_transport_cb( void *token, pjsip_tx_data *tdata, pj_ssize_t sent ) { pjsip_send_state *stateless_data = (pjsip_send_state*) token; PJ_UNUSED_ARG(tdata); pj_assert(tdata == stateless_data->tdata); for (;;) { pj_status_t status; pj_bool_t cont; pj_sockaddr_t *cur_addr; pjsip_transport_type_e cur_addr_type; int cur_addr_len; pjsip_via_hdr *via; if (sent == -PJ_EPENDING) { /* This is the initial process. * When the process started, this function will be called by * stateless_send_resolver_callback() with sent argument set to * -PJ_EPENDING. */ cont = PJ_TRUE; } else { /* There are two conditions here: * (1) Message is sent (i.e. sent > 0), * (2) Failure (i.e. sent <= 0) */ cont = (sent > 0) ? PJ_FALSE : (tdata->dest_info.cur_addrdest_info.addr.count-1); if (stateless_data->app_cb) { (*stateless_data->app_cb)(stateless_data, sent, &cont); } else { /* Doesn't have application callback. * Terminate the process. */ cont = PJ_FALSE; } } /* Finished with this transport. */ if (stateless_data->cur_transport) { pjsip_transport_dec_ref(stateless_data->cur_transport); stateless_data->cur_transport = NULL; } /* Done if application doesn't want to continue. */ if (sent > 0 || !cont) { pjsip_tx_data_dec_ref(tdata); return; } /* Try next address, if any, and only when this is not the * first invocation. */ if (sent != -PJ_EPENDING) { tdata->dest_info.cur_addr++; } /* Have next address? */ if (tdata->dest_info.cur_addr >= tdata->dest_info.addr.count) { /* This only happens when a rather buggy application has * sent 'cont' to PJ_TRUE when the initial value was PJ_FALSE. * In this case just stop the processing; we don't need to * call the callback again as application has been informed * before. */ pjsip_tx_data_dec_ref(tdata); return; } /* Keep current server address information handy. */ cur_addr = &tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr; cur_addr_type = tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].type; cur_addr_len = tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr_len; /* Acquire transport. */ status = pjsip_endpt_acquire_transport2(stateless_data->endpt, cur_addr_type, cur_addr, cur_addr_len, &tdata->tp_sel, tdata, &stateless_data->cur_transport); if (status != PJ_SUCCESS) { sent = -status; continue; } /* Modify Via header. */ via = (pjsip_via_hdr*) pjsip_msg_find_hdr( tdata->msg, PJSIP_H_VIA, NULL); if (!via) { /* Shouldn't happen if request was created with PJSIP API! * But we handle the case anyway for robustness. */ pj_assert(!"Via header not found!"); via = pjsip_via_hdr_create(tdata->pool); pjsip_msg_insert_first_hdr(tdata->msg, (pjsip_hdr*)via); } if (via->branch_param.slen == 0) { pj_str_t tmp; via->branch_param.ptr = (char*)pj_pool_alloc(tdata->pool, PJSIP_MAX_BRANCH_LEN); via->branch_param.slen = PJSIP_MAX_BRANCH_LEN; pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN); tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN + 2; *(tmp.ptr-2) = 80; *(tmp.ptr-1) = 106; pj_generate_unique_string(&tmp); } via->transport = pj_str(stateless_data->cur_transport->type_name); if (tdata->via_addr.host.slen > 0 && tdata->via_tp == (void *)stateless_data->cur_transport) { via->sent_by = tdata->via_addr; } else { via->sent_by = stateless_data->cur_transport->local_name; } via->rport_param = pjsip_cfg()->endpt.disable_rport ? -1 : 0; /* Add/remove "alias" param to/from Via header on connection * oriented/less transport, if configured. */ if (pjsip_cfg()->endpt.req_has_via_alias && tdata->msg->type == PJSIP_REQUEST_MSG) { const pj_str_t ALIAS_STR = {"alias", 5}; pjsip_param *alias_param; pj_bool_t is_datagram; alias_param = pjsip_param_find(&via->other_param, &ALIAS_STR); is_datagram = (stateless_data->cur_transport->flag & PJSIP_TRANSPORT_DATAGRAM); if (!is_datagram && !alias_param) { alias_param = PJ_POOL_ZALLOC_T(tdata->pool, pjsip_param); alias_param->name = ALIAS_STR; pj_list_push_back(&via->other_param, alias_param); } else if (is_datagram && alias_param) { pj_list_erase(alias_param); } } pjsip_tx_data_invalidate_msg(tdata); /* Send message using this transport. */ status = pjsip_transport_send( stateless_data->cur_transport, tdata, cur_addr, cur_addr_len, stateless_data, &stateless_send_transport_cb); if (status == PJ_SUCCESS) { /* Recursively call this function. */ sent = tdata->buf.cur - tdata->buf.start; stateless_send_transport_cb( stateless_data, tdata, sent ); return; } else if (status == PJ_EPENDING) { /* This callback will be called later. */ return; } else { /* Recursively call this function. */ sent = -status; stateless_send_transport_cb( stateless_data, tdata, sent ); return; } } } /* Resolver callback for sending stateless request. */ static void stateless_send_resolver_callback( pj_status_t status, void *token, const struct pjsip_server_addresses *addr) { pjsip_send_state *stateless_data = (pjsip_send_state*) token; pjsip_tx_data *tdata = stateless_data->tdata; /* Fail on server resolution. */ if (status != PJ_SUCCESS) { if (stateless_data->app_cb) { pj_bool_t cont = PJ_FALSE; (*stateless_data->app_cb)(stateless_data, -status, &cont); } pjsip_tx_data_dec_ref(tdata); return; } /* Copy server addresses */ if (addr && addr != &tdata->dest_info.addr) { pj_memcpy( &tdata->dest_info.addr, addr, sizeof(pjsip_server_addresses)); } pj_assert(tdata->dest_info.addr.count != 0); /* RFC 3261 section 18.1.1: * If a request is within 200 bytes of the path MTU, or if it is larger * than 1300 bytes and the path MTU is unknown, the request MUST be sent * using an RFC 2914 [43] congestion controlled transport protocol, such * as TCP. */ if (pjsip_cfg()->endpt.disable_tcp_switch==0 && tdata->msg->type == PJSIP_REQUEST_MSG && tdata->dest_info.addr.count > 0 && tdata->dest_info.addr.entry[0].type == PJSIP_TRANSPORT_UDP) { int len; /* Encode the request */ status = pjsip_tx_data_encode(tdata); if (status != PJ_SUCCESS) { if (stateless_data->app_cb) { pj_bool_t cont = PJ_FALSE; (*stateless_data->app_cb)(stateless_data, -status, &cont); } pjsip_tx_data_dec_ref(tdata); return; } /* Check if request message is larger than 1300 bytes. */ len = (int)(tdata->buf.cur - tdata->buf.start); if (len >= PJSIP_UDP_SIZE_THRESHOLD) { int i; int count = tdata->dest_info.addr.count; PJ_LOG(5,(THIS_FILE, "%s exceeds UDP size threshold (%u), " "sending with TCP", pjsip_tx_data_get_info(tdata), PJSIP_UDP_SIZE_THRESHOLD)); /* Insert "TCP version" of resolved UDP addresses at the * beginning. */ if (count * 2 > PJSIP_MAX_RESOLVED_ADDRESSES) count = PJSIP_MAX_RESOLVED_ADDRESSES / 2; for (i = 0; i < count; ++i) { pj_memcpy(&tdata->dest_info.addr.entry[i+count], &tdata->dest_info.addr.entry[i], sizeof(tdata->dest_info.addr.entry[0])); tdata->dest_info.addr.entry[i].type = PJSIP_TRANSPORT_TCP; } tdata->dest_info.addr.count = count * 2; } } /* Process the addresses. */ stateless_send_transport_cb( stateless_data, tdata, -PJ_EPENDING); } /* * Send stateless request. * The sending process consists of several stages: * - determine which host to contact (#pjsip_get_request_addr). * - resolve the host (#pjsip_endpt_resolve) * - establish transport (#pjsip_endpt_acquire_transport) * - send the message (#pjsip_transport_send) */ PJ_DEF(pj_status_t) pjsip_endpt_send_request_stateless(pjsip_endpoint *endpt, pjsip_tx_data *tdata, void *token, pjsip_send_callback cb) { pjsip_host_info dest_info; pjsip_send_state *stateless_data; pj_status_t status; PJ_ASSERT_RETURN(endpt && tdata, PJ_EINVAL); /* Get destination name to contact. */ status = pjsip_process_route_set(tdata, &dest_info); if (status != PJ_SUCCESS) return status; /* Keep stateless data. */ stateless_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsip_send_state); stateless_data->token = token; stateless_data->endpt = endpt; stateless_data->tdata = tdata; stateless_data->app_cb = cb; /* If destination info has not been initialized (this applies for most * all requests except CANCEL), resolve destination host. The processing * then resumed when the resolving callback is called. For CANCEL, the * destination info must have been copied from the original INVITE so * proceed to sending the request directly. */ if (tdata->dest_info.addr.count == 0) { /* Copy the destination host name to TX data */ pj_strdup(tdata->pool, &tdata->dest_info.name, &dest_info.addr.host); pjsip_endpt_resolve( endpt, tdata->pool, &dest_info, stateless_data, &stateless_send_resolver_callback); } else { PJ_LOG(5,(THIS_FILE, "%s: skipping target resolution because " "address is already set", pjsip_tx_data_get_info(tdata))); stateless_send_resolver_callback(PJ_SUCCESS, stateless_data, &tdata->dest_info.addr); } return PJ_SUCCESS; } /* * Send raw data to a destination. */ PJ_DEF(pj_status_t) pjsip_endpt_send_raw( pjsip_endpoint *endpt, pjsip_transport_type_e tp_type, const pjsip_tpselector *sel, const void *raw_data, pj_size_t data_len, const pj_sockaddr_t *addr, int addr_len, void *token, pjsip_tp_send_callback cb) { return pjsip_tpmgr_send_raw(pjsip_endpt_get_tpmgr(endpt), tp_type, sel, NULL, raw_data, data_len, addr, addr_len, token, cb); } /* Callback data for sending raw data */ struct send_raw_data { pjsip_endpoint *endpt; pjsip_tx_data *tdata; pjsip_tpselector *sel; void *app_token; pjsip_tp_send_callback app_cb; }; /* Resolver callback for sending raw data. */ static void send_raw_resolver_callback( pj_status_t status, void *token, const pjsip_server_addresses *addr) { struct send_raw_data *sraw_data = (struct send_raw_data*) token; if (status != PJ_SUCCESS) { if (sraw_data->app_cb) { (*sraw_data->app_cb)(sraw_data->app_token, sraw_data->tdata, -status); } } else { pj_size_t data_len; pj_assert(addr->count != 0); /* Avoid tdata destroyed by pjsip_tpmgr_send_raw(). */ pjsip_tx_data_add_ref(sraw_data->tdata); data_len = sraw_data->tdata->buf.cur - sraw_data->tdata->buf.start; status = pjsip_tpmgr_send_raw(pjsip_endpt_get_tpmgr(sraw_data->endpt), addr->entry[0].type, sraw_data->sel, sraw_data->tdata, sraw_data->tdata->buf.start, data_len, &addr->entry[0].addr, addr->entry[0].addr_len, sraw_data->app_token, sraw_data->app_cb); if (status == PJ_SUCCESS) { (*sraw_data->app_cb)(sraw_data->app_token, sraw_data->tdata, data_len); } else if (status != PJ_EPENDING) { (*sraw_data->app_cb)(sraw_data->app_token, sraw_data->tdata, -status); } } if (sraw_data->sel) { pjsip_tpselector_dec_ref(sraw_data->sel); } pjsip_tx_data_dec_ref(sraw_data->tdata); } /* * Send raw data to the specified destination URI. */ PJ_DEF(pj_status_t) pjsip_endpt_send_raw_to_uri(pjsip_endpoint *endpt, const pj_str_t *p_dst_uri, const pjsip_tpselector *sel, const void *raw_data, pj_size_t data_len, void *token, pjsip_tp_send_callback cb) { pjsip_tx_data *tdata; struct send_raw_data *sraw_data; pj_str_t dst_uri; pjsip_uri *uri; pjsip_host_info dest_info; pj_status_t status; /* Allocate buffer */ status = pjsip_endpt_create_tdata(endpt, &tdata); if (status != PJ_SUCCESS) return status; pjsip_tx_data_add_ref(tdata); /* Duplicate URI since parser requires URI to be NULL terminated */ pj_strdup_with_null(tdata->pool, &dst_uri, p_dst_uri); /* Parse URI */ uri = pjsip_parse_uri(tdata->pool, dst_uri.ptr, dst_uri.slen, 0); if (uri == NULL) { pjsip_tx_data_dec_ref(tdata); return PJSIP_EINVALIDURI; } /* Build destination info. */ status = pjsip_get_dest_info(uri, NULL, tdata->pool, &dest_info); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); return status; } /* Copy data (note: data_len may be zero!) */ tdata->buf.start = (char*) pj_pool_alloc(tdata->pool, data_len+1); tdata->buf.end = tdata->buf.start + data_len + 1; if (data_len) pj_memcpy(tdata->buf.start, raw_data, data_len); tdata->buf.cur = tdata->buf.start + data_len; /* Init send_raw_data */ sraw_data = PJ_POOL_ZALLOC_T(tdata->pool, struct send_raw_data); sraw_data->endpt = endpt; sraw_data->tdata = tdata; sraw_data->app_token = token; sraw_data->app_cb = cb; if (sel) { sraw_data->sel = PJ_POOL_ALLOC_T(tdata->pool, pjsip_tpselector); pj_memcpy(sraw_data->sel, sel, sizeof(pjsip_tpselector)); pjsip_tpselector_add_ref(sraw_data->sel); } /* Copy the destination host name to TX data */ pj_strdup(tdata->pool, &tdata->dest_info.name, &dest_info.addr.host); /* Resolve destination host. * The processing then resumed when the resolving callback is called. */ pjsip_endpt_resolve( endpt, tdata->pool, &dest_info, sraw_data, &send_raw_resolver_callback); return PJ_SUCCESS; } /* * Determine which address (and transport) to use to send response message * based on the received request. This function follows the specification * in section 18.2.2 of RFC 3261 and RFC 3581 for calculating the destination * address and transport. */ PJ_DEF(pj_status_t) pjsip_get_response_addr( pj_pool_t *pool, pjsip_rx_data *rdata, pjsip_response_addr *res_addr ) { pjsip_transport *src_transport = rdata->tp_info.transport; /* Check arguments. */ PJ_ASSERT_RETURN(pool && rdata && res_addr, PJ_EINVAL); /* rdata must be a request message! */ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJ_EINVAL); /* All requests must have "received" parameter. * This must always be done in transport layer. */ pj_assert(rdata->msg_info.via->recvd_param.slen != 0); /* Do the calculation based on RFC 3261 Section 18.2.2 and RFC 3581 */ if (PJSIP_TRANSPORT_IS_RELIABLE(src_transport)) { /* For reliable protocol such as TCP or SCTP, or TLS over those, the * response MUST be sent using the existing connection to the source * of the original request that created the transaction, if that * connection is still open. * If that connection is no longer open, the server SHOULD open a * connection to the IP address in the received parameter, if present, * using the port in the sent-by value, or the default port for that * transport, if no port is specified. * If that connection attempt fails, the server SHOULD use the * procedures in [4] for servers in order to determine the IP address * and port to open the connection and send the response to. */ res_addr->transport = rdata->tp_info.transport; pj_memcpy(&res_addr->addr, &rdata->pkt_info.src_addr, rdata->pkt_info.src_addr_len); res_addr->addr_len = rdata->pkt_info.src_addr_len; res_addr->dst_host.type=(pjsip_transport_type_e)src_transport->key.type; res_addr->dst_host.flag = src_transport->flag; pj_strdup( pool, &res_addr->dst_host.addr.host, &rdata->msg_info.via->recvd_param); res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port; if (res_addr->dst_host.addr.port == 0) { res_addr->dst_host.addr.port = pjsip_transport_get_default_port_for_type(res_addr->dst_host.type); } } else if (rdata->msg_info.via->maddr_param.slen) { /* Otherwise, if the Via header field value contains a maddr parameter, * the response MUST be forwarded to the address listed there, using * the port indicated in sent-by, or port 5060 if none is present. * If the address is a multicast address, the response SHOULD be sent * using the TTL indicated in the ttl parameter, or with a TTL of 1 if * that parameter is not present. */ res_addr->transport = NULL; res_addr->dst_host.type=(pjsip_transport_type_e)src_transport->key.type; res_addr->dst_host.flag = src_transport->flag; pj_strdup( pool, &res_addr->dst_host.addr.host, &rdata->msg_info.via->maddr_param); res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port; if (res_addr->dst_host.addr.port == 0) res_addr->dst_host.addr.port = 5060; } else if (rdata->msg_info.via->rport_param >= 0) { /* There is both a "received" parameter and an "rport" parameter, * the response MUST be sent to the IP address listed in the "received" * parameter, and the port in the "rport" parameter. * The response MUST be sent from the same address and port that the * corresponding request was received on. */ res_addr->transport = rdata->tp_info.transport; pj_memcpy(&res_addr->addr, &rdata->pkt_info.src_addr, rdata->pkt_info.src_addr_len); res_addr->addr_len = rdata->pkt_info.src_addr_len; res_addr->dst_host.type=(pjsip_transport_type_e)src_transport->key.type; res_addr->dst_host.flag = src_transport->flag; pj_strdup( pool, &res_addr->dst_host.addr.host, &rdata->msg_info.via->recvd_param); res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port; if (res_addr->dst_host.addr.port == 0) { res_addr->dst_host.addr.port = pjsip_transport_get_default_port_for_type(res_addr->dst_host.type); } } else { res_addr->transport = NULL; res_addr->dst_host.type=(pjsip_transport_type_e)src_transport->key.type; res_addr->dst_host.flag = src_transport->flag; pj_strdup( pool, &res_addr->dst_host.addr.host, &rdata->msg_info.via->recvd_param); res_addr->dst_host.addr.port = rdata->msg_info.via->sent_by.port; if (res_addr->dst_host.addr.port == 0) { res_addr->dst_host.addr.port = pjsip_transport_get_default_port_for_type(res_addr->dst_host.type); } } return PJ_SUCCESS; } /* * Callback called by transport during send_response. */ static void send_response_transport_cb(void *token, pjsip_tx_data *tdata, pj_ssize_t sent) { pjsip_send_state *send_state = (pjsip_send_state*) token; pj_bool_t cont = PJ_FALSE; /* Call callback, if any. */ if (send_state->app_cb) (*send_state->app_cb)(send_state, sent, &cont); /* Decrement transport reference counter. */ pjsip_transport_dec_ref(send_state->cur_transport); /* Decrement transmit data ref counter. */ pjsip_tx_data_dec_ref(tdata); } /* * Resolver calback during send_response. */ static void send_response_resolver_cb( pj_status_t status, void *token, const pjsip_server_addresses *addr ) { pjsip_send_state *send_state = (pjsip_send_state*) token; if (status != PJ_SUCCESS) { if (send_state->app_cb) { pj_bool_t cont = PJ_FALSE; (*send_state->app_cb)(send_state, -status, &cont); } pjsip_tx_data_dec_ref(send_state->tdata); return; } /* Only handle the first address resolved. */ /* Acquire transport. */ status = pjsip_endpt_acquire_transport2(send_state->endpt, addr->entry[0].type, &addr->entry[0].addr, addr->entry[0].addr_len, &send_state->tdata->tp_sel, send_state->tdata, &send_state->cur_transport); if (status != PJ_SUCCESS) { if (send_state->app_cb) { pj_bool_t cont = PJ_FALSE; (*send_state->app_cb)(send_state, -status, &cont); } pjsip_tx_data_dec_ref(send_state->tdata); return; } /* Update address in send_state. */ pj_memcpy(&send_state->tdata->dest_info.addr, addr, sizeof(*addr)); /* Send response using the transoprt. */ status = pjsip_transport_send( send_state->cur_transport, send_state->tdata, &addr->entry[0].addr, addr->entry[0].addr_len, send_state, &send_response_transport_cb); if (status == PJ_SUCCESS) { pj_ssize_t sent = send_state->tdata->buf.cur - send_state->tdata->buf.start; send_response_transport_cb(send_state, send_state->tdata, sent); } else if (status == PJ_EPENDING) { /* Transport callback will be called later. */ } else { send_response_transport_cb(send_state, send_state->tdata, -status); } } /* * Send response. */ PJ_DEF(pj_status_t) pjsip_endpt_send_response( pjsip_endpoint *endpt, pjsip_response_addr *res_addr, pjsip_tx_data *tdata, void *token, pjsip_send_callback cb) { /* Determine which transports and addresses to send the response, * based on Section 18.2.2 of RFC 3261. */ pjsip_send_state *send_state; pj_status_t status; /* Create structure to keep the sending state. */ send_state = PJ_POOL_ZALLOC_T(tdata->pool, pjsip_send_state); send_state->endpt = endpt; send_state->tdata = tdata; send_state->token = token; send_state->app_cb = cb; if (res_addr->transport != NULL) { send_state->cur_transport = res_addr->transport; pjsip_transport_add_ref(send_state->cur_transport); status = pjsip_transport_send( send_state->cur_transport, tdata, &res_addr->addr, res_addr->addr_len, send_state, &send_response_transport_cb ); if (status == PJ_SUCCESS) { pj_ssize_t sent = tdata->buf.cur - tdata->buf.start; send_response_transport_cb(send_state, tdata, sent); return PJ_SUCCESS; } else if (status == PJ_EPENDING) { /* Callback will be called later. */ return PJ_SUCCESS; } else { pjsip_transport_dec_ref(send_state->cur_transport); return status; } } else { /* Copy the destination host name to TX data */ pj_strdup(tdata->pool, &tdata->dest_info.name, &res_addr->dst_host.addr.host); pjsip_endpt_resolve(endpt, tdata->pool, &res_addr->dst_host, send_state, &send_response_resolver_cb); return PJ_SUCCESS; } } /* * Send response combo */ PJ_DEF(pj_status_t) pjsip_endpt_send_response2( pjsip_endpoint *endpt, pjsip_rx_data *rdata, pjsip_tx_data *tdata, void *token, pjsip_send_callback cb) { pjsip_response_addr res_addr; pj_status_t status; status = pjsip_get_response_addr(tdata->pool, rdata, &res_addr); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); return PJ_SUCCESS; } status = pjsip_endpt_send_response(endpt, &res_addr, tdata, token, cb); return status; } /* * Send response */ PJ_DEF(pj_status_t) pjsip_endpt_respond_stateless( pjsip_endpoint *endpt, pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, const pjsip_hdr *hdr_list, const pjsip_msg_body *body) { pj_status_t status; pjsip_response_addr res_addr; pjsip_tx_data *tdata; /* Verify arguments. */ PJ_ASSERT_RETURN(endpt && rdata, PJ_EINVAL); PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Check that no UAS transaction has been created for this request. * If UAS transaction has been created for this request, application * MUST send the response statefully using that transaction. */ PJ_ASSERT_RETURN(pjsip_rdata_get_tsx(rdata)==NULL, PJ_EINVALIDOP); /* Create response message */ status = pjsip_endpt_create_response( endpt, rdata, st_code, st_text, &tdata); if (status != PJ_SUCCESS) return status; /* Add the message headers, if any */ if (hdr_list) { const pjsip_hdr *hdr = hdr_list->next; while (hdr != hdr_list) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr) ); hdr = hdr->next; } } /* Add the message body, if any. */ if (body) { tdata->msg->body = pjsip_msg_body_clone( tdata->pool, body ); if (tdata->msg->body == NULL) { pjsip_tx_data_dec_ref(tdata); return status; } } /* Get where to send request. */ status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr ); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); return status; } /* Send! */ status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL ); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); return status; } return PJ_SUCCESS; } /* * Get the event string from the event ID. */ PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e) { return event_str[e]; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_util_proxy.c ================================================ /* $Id: sip_util_proxy.c 4208 2012-07-18 07:52:33Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include /** * Clone the incoming SIP request or response message. A forwarding proxy * typically would need to clone the incoming SIP message before processing * the message. * * Once a transmit data is created, the reference counter is initialized to 1. * * @param endpt The endpoint instance. * @param rdata The incoming SIP message. * @param p_tdata Pointer to receive the transmit data containing * the duplicated message. * * @return PJ_SUCCESS on success. */ /* PJ_DEF(pj_status_t) pjsip_endpt_clone_msg( pjsip_endpoint *endpt, const pjsip_rx_data *rdata, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pj_status_t status; status = pjsip_endpt_create_tdata(endpt, &tdata); if (status != PJ_SUCCESS) return status; tdata->msg = pjsip_msg_clone(tdata->pool, rdata->msg_info.msg); pjsip_tx_data_add_ref(tdata); *p_tdata = tdata; return PJ_SUCCESS; } */ /* * Create new request message to be forwarded upstream to new destination URI * in uri. */ PJ_DEF(pj_status_t) pjsip_endpt_create_request_fwd(pjsip_endpoint *endpt, pjsip_rx_data *rdata, const pjsip_uri *uri, const pj_str_t *branch, unsigned options, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pj_status_t status; PJ_USE_EXCEPTION; PJ_ASSERT_RETURN(endpt && rdata && p_tdata, PJ_EINVAL); PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); PJ_UNUSED_ARG(options); /* Request forwarding rule in RFC 3261 section 16.6: * * For each target, the proxy forwards the request following these * steps: * * 1. Make a copy of the received request * 2. Update the Request-URI * 3. Update the Max-Forwards header field * 4. Optionally add a Record-route header field value * 5. Optionally add additional header fields * 6. Postprocess routing information * 7. Determine the next-hop address, port, and transport * 8. Add a Via header field value * 9. Add a Content-Length header field if necessary * 10. Forward the new request * * Of these steps, we only do step 1-3, since the later will be * done by application. */ status = pjsip_endpt_create_tdata(endpt, &tdata); if (status != PJ_SUCCESS) return status; /* Always increment ref counter to 1 */ pjsip_tx_data_add_ref(tdata); /* Duplicate the request */ PJ_TRY { pjsip_msg *dst; const pjsip_msg *src = rdata->msg_info.msg; const pjsip_hdr *hsrc; /* Create the request */ tdata->msg = dst = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); /* Duplicate request method */ pjsip_method_copy(tdata->pool, &tdata->msg->line.req.method, &src->line.req.method); /* Set request URI */ if (uri) { dst->line.req.uri = (pjsip_uri*) pjsip_uri_clone(tdata->pool, uri); } else { dst->line.req.uri= (pjsip_uri*) pjsip_uri_clone(tdata->pool, src->line.req.uri); } /* Clone ALL headers */ hsrc = src->hdr.next; while (hsrc != &src->hdr) { pjsip_hdr *hdst; /* If this is the top-most Via header, insert our own before * cloning the header. */ if (hsrc == (pjsip_hdr*)rdata->msg_info.via) { pjsip_via_hdr *hvia; hvia = pjsip_via_hdr_create(tdata->pool); if (branch) pj_strdup(tdata->pool, &hvia->branch_param, branch); else { pj_str_t new_branch = pjsip_calculate_branch_id(rdata); pj_strdup(tdata->pool, &hvia->branch_param, &new_branch); } pjsip_msg_add_hdr(dst, (pjsip_hdr*)hvia); } /* Skip Content-Type and Content-Length as these would be * generated when the the message is printed. */ else if (hsrc->type == PJSIP_H_CONTENT_LENGTH || hsrc->type == PJSIP_H_CONTENT_TYPE) { hsrc = hsrc->next; continue; } #if 0 /* If this is the top-most Route header and it indicates loose * route, remove the header. */ else if (hsrc == (pjsip_hdr*)rdata->msg_info.route) { const pjsip_route_hdr *hroute = (const pjsip_route_hdr*) hsrc; const pjsip_sip_uri *sip_uri; if (!PJSIP_URI_SCHEME_IS_SIP(hroute->name_addr.uri) && !PJSIP_URI_SCHEME_IS_SIPS(hroute->name_addr.uri)) { /* This is a bad request! */ status = PJSIP_EINVALIDHDR; goto on_error; } sip_uri = (pjsip_sip_uri*) hroute->name_addr.uri; if (sip_uri->lr_param) { /* Yes lr param is present, skip this Route header */ hsrc = hsrc->next; continue; } } #endif /* Clone the header */ hdst = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hsrc); /* If this is Max-Forward header, decrement the value */ if (hdst->type == PJSIP_H_MAX_FORWARDS) { pjsip_max_fwd_hdr *hmaxfwd = (pjsip_max_fwd_hdr*)hdst; --hmaxfwd->ivalue; } /* Append header to new request */ pjsip_msg_add_hdr(dst, hdst); hsrc = hsrc->next; } /* 16.6.3: * If the copy does not contain a Max-Forwards header field, the * proxy MUST add one with a field value, which SHOULD be 70. */ if (rdata->msg_info.max_fwd == NULL) { pjsip_max_fwd_hdr *hmaxfwd = pjsip_max_fwd_hdr_create(tdata->pool, 70); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hmaxfwd); } /* Clone request body */ if (src->body) { dst->body = pjsip_msg_body_clone(tdata->pool, src->body); } } PJ_CATCH_ANY { status = PJ_ENOMEM; goto on_error; } PJ_END /* Done */ *p_tdata = tdata; return PJ_SUCCESS; on_error: pjsip_tx_data_dec_ref(tdata); return status; } PJ_DEF(pj_status_t) pjsip_endpt_create_response_fwd( pjsip_endpoint *endpt, pjsip_rx_data *rdata, unsigned options, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pj_status_t status; PJ_USE_EXCEPTION; PJ_UNUSED_ARG(options); status = pjsip_endpt_create_tdata(endpt, &tdata); if (status != PJ_SUCCESS) return status; pjsip_tx_data_add_ref(tdata); PJ_TRY { pjsip_msg *dst; const pjsip_msg *src = rdata->msg_info.msg; const pjsip_hdr *hsrc; /* Create the request */ tdata->msg = dst = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG); /* Clone the status line */ dst->line.status.code = src->line.status.code; pj_strdup(tdata->pool, &dst->line.status.reason, &src->line.status.reason); /* Duplicate all headers */ hsrc = src->hdr.next; while (hsrc != &src->hdr) { /* Skip Content-Type and Content-Length as these would be * generated when the the message is printed. */ if (hsrc->type == PJSIP_H_CONTENT_LENGTH || hsrc->type == PJSIP_H_CONTENT_TYPE) { hsrc = hsrc->next; continue; } /* Remove the first Via header */ else if (hsrc == (pjsip_hdr*) rdata->msg_info.via) { hsrc = hsrc->next; continue; } pjsip_msg_add_hdr(dst, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hsrc)); hsrc = hsrc->next; } /* Clone message body */ if (src->body) dst->body = pjsip_msg_body_clone(tdata->pool, src->body); } PJ_CATCH_ANY { status = PJ_ENOMEM; goto on_error; } PJ_END; *p_tdata = tdata; return PJ_SUCCESS; on_error: pjsip_tx_data_dec_ref(tdata); return status; } static void digest2str(const unsigned char digest[], char *output) { int i; for (i = 0; i<16; ++i) { pj_val_to_hex_digit(digest[i], output); output += 2; } } PJ_DEF(pj_str_t) pjsip_calculate_branch_id( pjsip_rx_data *rdata ) { pj_md5_context ctx; pj_uint8_t digest[16]; pj_str_t branch; pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN}; /* If incoming request does not have RFC 3261 branch value, create * a branch value from GUID . */ if (pj_strnicmp(&rdata->msg_info.via->branch_param, &rfc3261_branch, PJSIP_RFC3261_BRANCH_LEN) != 0 ) { pj_str_t tmp; branch.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_BRANCH_LEN); branch.slen = PJSIP_RFC3261_BRANCH_LEN; pj_memcpy(branch.ptr, PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN); tmp.ptr = branch.ptr + PJSIP_RFC3261_BRANCH_LEN + 2; *(tmp.ptr-2) = (pj_int8_t)(branch.slen+73); *(tmp.ptr-1) = (pj_int8_t)(branch.slen+99); pj_generate_unique_string( &tmp ); branch.slen = PJSIP_MAX_BRANCH_LEN; return branch; } /* Create branch ID for new request by calculating MD5 hash * of the branch parameter in top-most Via header. */ pj_md5_init(&ctx); pj_md5_update(&ctx, (pj_uint8_t*)rdata->msg_info.via->branch_param.ptr, (unsigned)rdata->msg_info.via->branch_param.slen); pj_md5_final(&ctx, digest); branch.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool, 34 + PJSIP_RFC3261_BRANCH_LEN); pj_memcpy(branch.ptr, PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN); branch.slen = PJSIP_RFC3261_BRANCH_LEN; *(branch.ptr+PJSIP_RFC3261_BRANCH_LEN) = (pj_int8_t)(branch.slen+73); *(branch.ptr+PJSIP_RFC3261_BRANCH_LEN+1) = (pj_int8_t)(branch.slen+99); digest2str(digest, branch.ptr+PJSIP_RFC3261_BRANCH_LEN+2); branch.slen = 34 + PJSIP_RFC3261_BRANCH_LEN; return branch; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_util_proxy_wrap.cpp ================================================ /* $Id: sip_util_proxy_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This file is a C++ wrapper, see ticket #886 for details. */ #include "sip_util_proxy.c" ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_util_statefull.c ================================================ /* $Id: sip_util_statefull.c 4169 2012-06-18 09:19:58Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include struct tsx_data { void *token; void (*cb)(void*, pjsip_event*); }; static void mod_util_on_tsx_state(pjsip_transaction*, pjsip_event*); /* This module will be registered in pjsip_endpt.c */ pjsip_module mod_stateful_util = { NULL, NULL, /* prev, next. */ { "mod-stateful-util", 17 }, /* Name. */ -1, /* Id */ PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ NULL, /* unload() */ NULL, /* on_rx_request() */ NULL, /* on_rx_response() */ NULL, /* on_tx_request. */ NULL, /* on_tx_response() */ &mod_util_on_tsx_state, /* on_tsx_state() */ }; static void mod_util_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event) { struct tsx_data *tsx_data; /* Check if the module has been unregistered (see ticket #1535) and also * verify the event type. */ if (mod_stateful_util.id < 0 || event->type != PJSIP_EVENT_TSX_STATE) return; tsx_data = (struct tsx_data*) tsx->mod_data[mod_stateful_util.id]; if (tsx_data == NULL) return; if (tsx->status_code < 200) return; /* Call the callback, if any, and prevent the callback to be called again * by clearing the transaction's module_data. */ tsx->mod_data[mod_stateful_util.id] = NULL; if (tsx_data->cb) { (*tsx_data->cb)(tsx_data->token, event); } } PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt, pjsip_tx_data *tdata, pj_int32_t timeout, void *token, pjsip_endpt_send_callback cb) { pjsip_transaction *tsx; struct tsx_data *tsx_data; pj_status_t status; PJ_ASSERT_RETURN(endpt && tdata && (timeout==-1 || timeout>0), PJ_EINVAL); /* Check that transaction layer module is registered to endpoint */ PJ_ASSERT_RETURN(mod_stateful_util.id != -1, PJ_EINVALIDOP); PJ_UNUSED_ARG(timeout); status = pjsip_tsx_create_uac(&mod_stateful_util, tdata, &tsx); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); return status; } pjsip_tsx_set_transport(tsx, &tdata->tp_sel); tsx_data = PJ_POOL_ALLOC_T(tsx->pool, struct tsx_data); tsx_data->token = token; tsx_data->cb = cb; tsx->mod_data[mod_stateful_util.id] = tsx_data; status = pjsip_tsx_send_msg(tsx, NULL); if (status != PJ_SUCCESS) pjsip_tx_data_dec_ref(tdata); return status; } /* * Send response statefully. */ PJ_DEF(pj_status_t) pjsip_endpt_respond( pjsip_endpoint *endpt, pjsip_module *tsx_user, pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, const pjsip_hdr *hdr_list, const pjsip_msg_body *body, pjsip_transaction **p_tsx ) { pj_status_t status; pjsip_tx_data *tdata; pjsip_transaction *tsx; /* Validate arguments. */ PJ_ASSERT_RETURN(endpt && rdata, PJ_EINVAL); if (p_tsx) *p_tsx = NULL; /* Create response message */ status = pjsip_endpt_create_response( endpt, rdata, st_code, st_text, &tdata); if (status != PJ_SUCCESS) return status; /* Add the message headers, if any */ if (hdr_list) { const pjsip_hdr *hdr = hdr_list->next; while (hdr != hdr_list) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr) ); hdr = hdr->next; } } /* Add the message body, if any. */ if (body) { tdata->msg->body = pjsip_msg_body_clone( tdata->pool, body ); if (tdata->msg->body == NULL) { pjsip_tx_data_dec_ref(tdata); return status; } } /* Create UAS transaction. */ status = pjsip_tsx_create_uas(tsx_user, rdata, &tsx); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); return status; } /* Feed the request to the transaction. */ pjsip_tsx_recv_msg(tsx, rdata); /* Send the message. */ status = pjsip_tsx_send_msg(tsx, tdata); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); } else if (p_tsx) { *p_tsx = tsx; } return status; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip/sip_util_wrap.cpp ================================================ /* $Id: sip_util_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * This file is a C++ wrapper, see ticket #886 for details. */ #include "sip_util.c" ================================================ FILE: deps/pjsip/pjsip/src/pjsip-simple/errno.c ================================================ /* $Id: errno.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include /* PJSIP-SIMPLE's own error codes/messages * MUST KEEP THIS ARRAY SORTED!! * Message must be limited to 64 chars! */ #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) static const struct { int code; const char *msg; } err_str[] = { /* Event errors */ { PJSIP_SIMPLE_ENOPKG, "No SIP event package with the specified name" }, { PJSIP_SIMPLE_EPKGEXISTS, "SIP event package already exist" }, /* Presence errors */ { PJSIP_SIMPLE_ENOTSUBSCRIBE, "Expecting SUBSCRIBE request" }, { PJSIP_SIMPLE_ENOPRESENCE, "No presence associated with the subscription" }, { PJSIP_SIMPLE_ENOPRESENCEINFO, "No presence info in the server subscription" }, { PJSIP_SIMPLE_EBADCONTENT, "Bad Content-Type for presence" }, { PJSIP_SIMPLE_EBADPIDF, "Bad PIDF content for presence" }, { PJSIP_SIMPLE_EBADXPIDF, "Bad XPIDF content for presence" }, { PJSIP_SIMPLE_EBADRPID, "Invalid or bad RPID document"}, /* isComposing errors. */ { PJSIP_SIMPLE_EBADISCOMPOSE, "Bad isComposing indication/XML message" }, }; #endif /* PJ_HAS_ERROR_STRING */ /* * pjsipsimple_strerror() */ PJ_DEF(pj_str_t) pjsipsimple_strerror( pj_status_t statcode, char *buf, pj_size_t bufsize ) { pj_str_t errstr; #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) if (statcode >= PJSIP_SIMPLE_ERRNO_START && statcode < PJSIP_SIMPLE_ERRNO_START + PJ_ERRNO_SPACE_SIZE) { /* Find the error in the table. * Use binary search! */ int first = 0; int n = PJ_ARRAY_SIZE(err_str); while (n > 0) { int half = n/2; int mid = first + half; if (err_str[mid].code < statcode) { first = mid+1; n -= (half+1); } else if (err_str[mid].code > statcode) { n = half; } else { first = mid; break; } } if (PJ_ARRAY_SIZE(err_str) && err_str[first].code == statcode) { pj_str_t msg; msg.ptr = (char*)err_str[first].msg; msg.slen = pj_ansi_strlen(err_str[first].msg); errstr.ptr = buf; pj_strncpy_with_null(&errstr, &msg, bufsize); return errstr; } } #endif /* PJ_HAS_ERROR_STRING */ /* Error not found. */ errstr.ptr = buf; errstr.slen = pj_ansi_snprintf(buf, bufsize, "Unknown pjsip-simple error %d", statcode); if (errstr.slen < 1 || errstr.slen >= (pj_ssize_t)bufsize) errstr.slen = bufsize - 1; return errstr; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-simple/evsub.c ================================================ /* $Id: evsub.c 4447 2013-03-21 08:28:21Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "evsub.c" /* * Global constant */ /* Let's define this enum, so that it'll trigger compilation error * when somebody define the same enum in sip_msg.h */ enum { PJSIP_SUBSCRIBE_METHOD = PJSIP_OTHER_METHOD, PJSIP_NOTIFY_METHOD = PJSIP_OTHER_METHOD }; PJ_DEF_DATA(const pjsip_method) pjsip_subscribe_method = { (pjsip_method_e) PJSIP_SUBSCRIBE_METHOD, { "SUBSCRIBE", 9 } }; PJ_DEF_DATA(const pjsip_method) pjsip_notify_method = { (pjsip_method_e) PJSIP_NOTIFY_METHOD, { "NOTIFY", 6 } }; /** * SUBSCRIBE method constant. */ PJ_DEF(const pjsip_method*) pjsip_get_subscribe_method() { return &pjsip_subscribe_method; } /** * NOTIFY method constant. */ PJ_DEF(const pjsip_method*) pjsip_get_notify_method() { return &pjsip_notify_method; } /* * Static prototypes. */ static void mod_evsub_on_tsx_state(pjsip_transaction*, pjsip_event*); static pj_status_t mod_evsub_unload(void); /* * State names. */ static pj_str_t evsub_state_names[] = { { "NULL", 4}, { "SENT", 4}, { "ACCEPTED", 8}, { "PENDING", 7}, { "ACTIVE", 6}, { "TERMINATED", 10}, { "UNKNOWN", 7} }; /* * Timer constants. */ /* Number of seconds to send SUBSCRIBE before the actual expiration */ #define TIME_UAC_REFRESH PJSIP_EVSUB_TIME_UAC_REFRESH /* Time to wait for the final NOTIFY after sending unsubscription */ #define TIME_UAC_TERMINATE PJSIP_EVSUB_TIME_UAC_TERMINATE /* If client responds NOTIFY with non-2xx final response (such as 401), * wait for this seconds for further NOTIFY, otherwise client will * unsubscribe */ #define TIME_UAC_WAIT_NOTIFY PJSIP_EVSUB_TIME_UAC_WAIT_NOTIFY /* * Timer id */ enum timer_id { /* No timer. */ TIMER_TYPE_NONE, /* Time to refresh client subscription. * The action is to call on_client_refresh() callback. */ TIMER_TYPE_UAC_REFRESH, /* UAS timeout after to subscription refresh. * The action is to call on_server_timeout() callback. */ TIMER_TYPE_UAS_TIMEOUT, /* UAC waiting for final NOTIFY after unsubscribing * The action is to terminate. */ TIMER_TYPE_UAC_TERMINATE, /* UAC waiting for further NOTIFY after sending non-2xx response to * NOTIFY. The action is to unsubscribe. */ TIMER_TYPE_UAC_WAIT_NOTIFY, /* Max nb of timer types. */ TIMER_TYPE_MAX }; static const char *timer_names[] = { "None", "UAC_REFRESH", "UAS_TIMEOUT", "UAC_TERMINATE", "UAC_WAIT_NOTIFY", "INVALID_TIMER" }; /* * Definition of event package. */ struct evpkg { PJ_DECL_LIST_MEMBER(struct evpkg); pj_str_t pkg_name; pjsip_module *pkg_mod; unsigned pkg_expires; pjsip_accept_hdr *pkg_accept; }; /* * Event subscription module (mod-evsub). */ static struct mod_evsub { pjsip_module mod; pj_pool_t *pool; pjsip_endpoint *endpt; struct evpkg pkg_list; pjsip_allow_events_hdr *allow_events_hdr; } mod_evsub = { { NULL, NULL, /* prev, next. */ { "mod-evsub", 9 }, /* Name. */ -1, /* Id */ PJSIP_MOD_PRIORITY_DIALOG_USAGE, /* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ &mod_evsub_unload, /* unload() */ NULL, /* on_rx_request() */ NULL, /* on_rx_response() */ NULL, /* on_tx_request. */ NULL, /* on_tx_response() */ &mod_evsub_on_tsx_state, /* on_tsx_state() */ } }; /* * Event subscription session. */ struct pjsip_evsub { char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */ pj_pool_t *pool; /**< Pool. */ pjsip_endpoint *endpt; /**< Endpoint instance. */ pjsip_dialog *dlg; /**< Underlying dialog. */ struct evpkg *pkg; /**< The event package. */ unsigned option; /**< Options. */ pjsip_evsub_user user; /**< Callback. */ pj_bool_t call_cb; /**< Notify callback? */ pjsip_role_e role; /**< UAC=subscriber, UAS=notifier */ pjsip_evsub_state state; /**< Subscription state. */ pj_str_t state_str; /**< String describing the state. */ pjsip_evsub_state dst_state; /**< Pending state to be set. */ pj_str_t dst_state_str;/**< Pending state to be set. */ pj_str_t term_reason; /**< Termination reason. */ pjsip_method method; /**< Method that established subscr.*/ pjsip_event_hdr *event; /**< Event description. */ pjsip_expires_hdr *expires; /**< Expires header */ pjsip_accept_hdr *accept; /**< Local Accept header. */ pjsip_hdr sub_hdr_list; /**< User-defined header. */ pj_time_val refresh_time; /**< Time to refresh. */ pj_timer_entry timer; /**< Internal timer. */ int pending_tsx; /**< Number of pending transactions.*/ pjsip_transaction *pending_sub; /**< Pending UAC SUBSCRIBE tsx. */ pj_timer_entry *pending_sub_timer; /**< Stop pending sub timer. */ pj_grp_lock_t *grp_lock; /* Session group lock */ void *mod_data[PJSIP_MAX_MODULE]; /**< Module data. */ }; /* * This is the structure that will be "attached" to dialog. * The purpose is to allow multiple subscriptions inside a dialog. */ struct dlgsub { PJ_DECL_LIST_MEMBER(struct dlgsub); pjsip_evsub *sub; }; /* Static vars. */ static const pj_str_t STR_EVENT = { "Event", 5 }; static const pj_str_t STR_EVENT_S = { "o", 1 }; static const pj_str_t STR_SUB_STATE = { "Subscription-State", 18 }; static const pj_str_t STR_TERMINATED = { "terminated", 10 }; static const pj_str_t STR_ACTIVE = { "active", 6 }; static const pj_str_t STR_PENDING = { "pending", 7 }; static const pj_str_t STR_TIMEOUT = { "timeout", 7}; static const pj_str_t STR_RETRY_AFTER = { "Retry-After", 11 }; /* * On unload module. */ static pj_status_t mod_evsub_unload(void) { pjsip_endpt_release_pool(mod_evsub.endpt, mod_evsub.pool); mod_evsub.pool = NULL; return PJ_SUCCESS; } /* Proto for pjsipsimple_strerror(). * Defined in errno.c */ PJ_DECL(pj_str_t) pjsipsimple_strerror( pj_status_t statcode, char *buf, pj_size_t bufsize ); /* * Init and register module. */ PJ_DEF(pj_status_t) pjsip_evsub_init_module(pjsip_endpoint *endpt) { pj_status_t status; pj_str_t method_tags[] = { { "SUBSCRIBE", 9}, { "NOTIFY", 6} }; status = pj_register_strerror(PJSIP_SIMPLE_ERRNO_START, PJ_ERRNO_SPACE_SIZE, &pjsipsimple_strerror); pj_assert(status == PJ_SUCCESS); PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL); PJ_ASSERT_RETURN(mod_evsub.mod.id == -1, PJ_EINVALIDOP); /* Keep endpoint for future reference: */ mod_evsub.endpt = endpt; /* Init event package list: */ pj_list_init(&mod_evsub.pkg_list); /* Create pool: */ mod_evsub.pool = pjsip_endpt_create_pool(endpt, "evsub", 512, 512); if (!mod_evsub.pool) return PJ_ENOMEM; /* Register module: */ status = pjsip_endpt_register_module(endpt, &mod_evsub.mod); if (status != PJ_SUCCESS) goto on_error; /* Create Allow-Events header: */ mod_evsub.allow_events_hdr = pjsip_allow_events_hdr_create(mod_evsub.pool); /* Register SIP-event specific headers parser: */ pjsip_evsub_init_parser(); /* Register new methods SUBSCRIBE and NOTIFY in Allow-ed header */ pjsip_endpt_add_capability(endpt, &mod_evsub.mod, PJSIP_H_ALLOW, NULL, 2, method_tags); /* Done. */ return PJ_SUCCESS; on_error: if (mod_evsub.pool) { pjsip_endpt_release_pool(endpt, mod_evsub.pool); mod_evsub.pool = NULL; } mod_evsub.endpt = NULL; return status; } /* * Get the instance of the module. */ PJ_DEF(pjsip_module*) pjsip_evsub_instance(void) { PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, NULL); return &mod_evsub.mod; } /* * Get the event subscription instance in the transaction. */ PJ_DEF(pjsip_evsub*) pjsip_tsx_get_evsub(pjsip_transaction *tsx) { return (pjsip_evsub*) tsx->mod_data[mod_evsub.mod.id]; } /* * Set event subscription's module data. */ PJ_DEF(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id, void *data ) { PJ_ASSERT_ON_FAIL(mod_id < PJSIP_MAX_MODULE, return); sub->mod_data[mod_id] = data; } /* * Get event subscription's module data. */ PJ_DEF(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id ) { PJ_ASSERT_RETURN(mod_id < PJSIP_MAX_MODULE, NULL); return sub->mod_data[mod_id]; } /* * Find registered event package with matching name. */ static struct evpkg* find_pkg(const pj_str_t *event_name) { struct evpkg *pkg; pkg = mod_evsub.pkg_list.next; while (pkg != &mod_evsub.pkg_list) { if (pj_stricmp(&pkg->pkg_name, event_name) == 0) { return pkg; } pkg = pkg->next; } return NULL; } /* * Register an event package */ PJ_DEF(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod, const pj_str_t *event_name, unsigned expires, unsigned accept_cnt, const pj_str_t accept[]) { struct evpkg *pkg; unsigned i; PJ_ASSERT_RETURN(pkg_mod && event_name, PJ_EINVAL); PJ_ASSERT_RETURN(accept_cnt < PJ_ARRAY_SIZE(pkg->pkg_accept->values), PJ_ETOOMANY); /* Make sure evsub module has been initialized */ PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, PJ_EINVALIDOP); /* Make sure no module with the specified name already registered: */ PJ_ASSERT_RETURN(find_pkg(event_name) == NULL, PJSIP_SIMPLE_EPKGEXISTS); /* Create new event package: */ pkg = PJ_POOL_ALLOC_T(mod_evsub.pool, struct evpkg); pkg->pkg_mod = pkg_mod; pkg->pkg_expires = expires; pj_strdup(mod_evsub.pool, &pkg->pkg_name, event_name); pkg->pkg_accept = pjsip_accept_hdr_create(mod_evsub.pool); pkg->pkg_accept->count = accept_cnt; for (i=0; ipkg_accept->values[i], &accept[i]); } /* Add to package list: */ pj_list_push_back(&mod_evsub.pkg_list, pkg); /* Add to Allow-Events header: */ if (mod_evsub.allow_events_hdr->count != PJ_ARRAY_SIZE(mod_evsub.allow_events_hdr->values)) { mod_evsub.allow_events_hdr->values[mod_evsub.allow_events_hdr->count] = pkg->pkg_name; ++mod_evsub.allow_events_hdr->count; } /* Add to endpoint's Accept header */ pjsip_endpt_add_capability(mod_evsub.endpt, &mod_evsub.mod, PJSIP_H_ACCEPT, NULL, pkg->pkg_accept->count, pkg->pkg_accept->values); /* Done */ PJ_LOG(5,(THIS_FILE, "Event pkg \"%.*s\" registered by %.*s", (int)event_name->slen, event_name->ptr, (int)pkg_mod->name.slen, pkg_mod->name.ptr)); return PJ_SUCCESS; } /* * Retrieve Allow-Events header */ PJ_DEF(const pjsip_hdr*) pjsip_evsub_get_allow_events_hdr(pjsip_module *m) { struct mod_evsub *mod; if (m == NULL) m = pjsip_evsub_instance(); mod = (struct mod_evsub*)m; return (pjsip_hdr*) mod->allow_events_hdr; } /* * Update expiration time. */ static void update_expires( pjsip_evsub *sub, pj_uint32_t interval ) { pj_gettimeofday(&sub->refresh_time); sub->refresh_time.sec += interval; } PJ_DEF(void) pjsip_evsub_update_expires( pjsip_evsub *sub, pj_uint32_t interval ) { update_expires(sub, interval); } /* * Schedule timer. */ static void set_timer( pjsip_evsub *sub, int timer_id, pj_int32_t seconds) { if (sub->timer.id != TIMER_TYPE_NONE) { PJ_LOG(5,(sub->obj_name, "%s %s timer", (timer_id==sub->timer.id ? "Updating" : "Cancelling"), timer_names[sub->timer.id])); pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); sub->timer.id = TIMER_TYPE_NONE; } if (timer_id != TIMER_TYPE_NONE) { pj_time_val timeout; PJ_ASSERT_ON_FAIL(seconds > 0, return); PJ_ASSERT_ON_FAIL(timer_id>TIMER_TYPE_NONE && timer_idendpt), &sub->timer, &timeout, timer_id, sub->grp_lock); PJ_LOG(5,(sub->obj_name, "Timer %s scheduled in %d seconds", timer_names[sub->timer.id], timeout.sec)); } } PJ_DEF(void) pjsip_evsub_set_timer( pjsip_evsub *sub, int timer_id, pj_int32_t seconds) { set_timer(sub, timer_id, seconds); } /* * Destructor. */ static void evsub_on_destroy(void *obj) { pjsip_evsub *sub = (pjsip_evsub*)obj; /* Decrement dialog's session */ pjsip_dlg_dec_session(sub->dlg, &mod_evsub.mod); } /* * Destroy session. */ static void evsub_destroy( pjsip_evsub *sub ) { struct dlgsub *dlgsub_head, *dlgsub; PJ_LOG(4,(sub->obj_name, "Subscription destroyed")); /* Kill timer */ set_timer(sub, TIMER_TYPE_NONE, 0); /* Kill timer for stopping pending sub (see ticket #1807) */ if (sub->pending_sub_timer && sub->pending_sub_timer->id == 1) { pjsip_endpt_cancel_timer(sub->endpt, sub->pending_sub_timer); sub->pending_sub_timer->id = 0; sub->pending_sub_timer = NULL; } /* Remove this session from dialog's list of subscription */ dlgsub_head = (struct dlgsub *) sub->dlg->mod_data[mod_evsub.mod.id]; dlgsub = dlgsub_head->next; while (dlgsub != dlgsub_head) { if (dlgsub->sub == sub) { pj_list_erase(dlgsub); break; } dlgsub = dlgsub->next; } pj_grp_lock_dec_ref(sub->grp_lock); } /* * Set subscription session state. */ static void set_state( pjsip_evsub *sub, pjsip_evsub_state state, const pj_str_t *state_str, pjsip_event *event, const pj_str_t *reason) { pjsip_evsub_state prev_state = sub->state; pj_str_t old_state_str = sub->state_str; pjsip_event dummy_event; sub->state = state; if (state_str && state_str->slen) pj_strdup_with_null(sub->pool, &sub->state_str, state_str); else sub->state_str = evsub_state_names[state]; if (reason && sub->term_reason.slen==0) pj_strdup(sub->pool, &sub->term_reason, reason); PJ_LOG(4,(sub->obj_name, "Subscription state changed %.*s --> %.*s", (int)old_state_str.slen, old_state_str.ptr, (int)sub->state_str.slen, sub->state_str.ptr)); pj_log_push_indent(); /* don't call the callback with NULL event, it may crash the app! */ if (!event) { PJSIP_EVENT_INIT_USER(dummy_event, 0, 0, 0, 0); event = &dummy_event; } if (sub->user.on_evsub_state && sub->call_cb) (*sub->user.on_evsub_state)(sub, event); if (state == PJSIP_EVSUB_STATE_TERMINATED && prev_state != PJSIP_EVSUB_STATE_TERMINATED) { /* Kill any timer. */ set_timer(sub, TIMER_TYPE_NONE, 0); if (sub->pending_tsx == 0) { evsub_destroy(sub); } } pj_log_pop_indent(); } /* * Timer callback. */ static void on_timer( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { pjsip_evsub *sub; int timer_id; PJ_UNUSED_ARG(timer_heap); sub = (pjsip_evsub*) entry->user_data; pjsip_dlg_inc_lock(sub->dlg); /* If this timer entry has just been rescheduled or cancelled * while waiting for dialog mutex, just return (see #1885 scenario 1). */ if (pj_timer_entry_running(entry) || entry->id == TIMER_TYPE_NONE) { pjsip_dlg_dec_lock(sub->dlg); return; } timer_id = entry->id; entry->id = TIMER_TYPE_NONE; switch (timer_id) { case TIMER_TYPE_UAC_REFRESH: /* Time for UAC to refresh subscription */ if (sub->user.on_client_refresh && sub->call_cb) { (*sub->user.on_client_refresh)(sub); } else { pjsip_tx_data *tdata; pj_status_t status; PJ_LOG(5,(sub->obj_name, "Refreshing subscription.")); pj_log_push_indent(); status = pjsip_evsub_initiate(sub, NULL, sub->expires->ivalue, &tdata); if (status == PJ_SUCCESS) pjsip_evsub_send_request(sub, tdata); pj_log_pop_indent(); } break; case TIMER_TYPE_UAS_TIMEOUT: /* Refresh from UAC has not been received */ if (sub->user.on_server_timeout && sub->call_cb) { (*sub->user.on_server_timeout)(sub); } else { pjsip_tx_data *tdata; pj_status_t status; PJ_LOG(5,(sub->obj_name, "Timeout waiting for refresh. " "Sending NOTIFY to terminate.")); pj_log_push_indent(); status = pjsip_evsub_notify( sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, &STR_TIMEOUT, &tdata); if (status == PJ_SUCCESS) pjsip_evsub_send_request(sub, tdata); pj_log_pop_indent(); } break; case TIMER_TYPE_UAC_TERMINATE: { pj_str_t timeout = {"timeout", 7}; PJ_LOG(5,(sub->obj_name, "Timeout waiting for final NOTIFY. " "Terminating..")); pj_log_push_indent(); set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL, &timeout); pj_log_pop_indent(); } break; case TIMER_TYPE_UAC_WAIT_NOTIFY: { pjsip_tx_data *tdata; pj_status_t status; PJ_LOG(5,(sub->obj_name, "Timeout waiting for subsequent NOTIFY (we did " "send non-2xx response for previous NOTIFY). " "Unsubscribing..")); pj_log_push_indent(); status = pjsip_evsub_initiate( sub, NULL, 0, &tdata); if (status == PJ_SUCCESS) pjsip_evsub_send_request(sub, tdata); pj_log_pop_indent(); } break; default: pj_assert(!"Invalid timer id"); } pjsip_dlg_dec_lock(sub->dlg); } /* * Create subscription session, used for both client and notifier. */ static pj_status_t evsub_create( pjsip_dialog *dlg, pjsip_role_e role, const pjsip_evsub_user *user_cb, const pj_str_t *event, unsigned option, pjsip_evsub **p_evsub ) { pjsip_evsub *sub; struct evpkg *pkg; struct dlgsub *dlgsub_head, *dlgsub; pj_status_t status; /* Make sure there's package register for the event name: */ pkg = find_pkg(event); if (pkg == NULL) return PJSIP_SIMPLE_ENOPKG; /* Must lock dialog before using pool etc. */ pjsip_dlg_inc_lock(dlg); /* Init attributes: */ sub = PJ_POOL_ZALLOC_T(dlg->pool, struct pjsip_evsub); sub->pool = dlg->pool; sub->endpt = dlg->endpt; sub->dlg = dlg; sub->pkg = pkg; sub->role = role; sub->call_cb = PJ_TRUE; sub->option = option; sub->state = PJSIP_EVSUB_STATE_NULL; sub->state_str = evsub_state_names[sub->state]; sub->expires = pjsip_expires_hdr_create(sub->pool, pkg->pkg_expires); sub->accept = (pjsip_accept_hdr*) pjsip_hdr_clone(sub->pool, pkg->pkg_accept); pj_list_init(&sub->sub_hdr_list); sub->timer.user_data = sub; sub->timer.cb = &on_timer; /* Set name. */ pj_ansi_snprintf(sub->obj_name, PJ_ARRAY_SIZE(sub->obj_name), "evsub%p", sub); /* Copy callback, if any: */ if (user_cb) pj_memcpy(&sub->user, user_cb, sizeof(pjsip_evsub_user)); /* Create Event header: */ sub->event = pjsip_event_hdr_create(sub->pool); pj_strdup(sub->pool, &sub->event->event_type, event); /* Check if another subscription has been registered to the dialog. In * that case, just add ourselves to the subscription list, otherwise * create and register a new subscription list. */ if (pjsip_dlg_has_usage(dlg, &mod_evsub.mod)) { dlgsub_head = (struct dlgsub*) dlg->mod_data[mod_evsub.mod.id]; dlgsub = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub); dlgsub->sub = sub; pj_list_push_back(dlgsub_head, dlgsub); } else { dlgsub_head = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub); dlgsub = PJ_POOL_ALLOC_T(sub->pool, struct dlgsub); dlgsub->sub = sub; pj_list_init(dlgsub_head); pj_list_push_back(dlgsub_head, dlgsub); /* Register as dialog usage: */ status = pjsip_dlg_add_usage(dlg, &mod_evsub.mod, dlgsub_head); if (status != PJ_SUCCESS) { pjsip_dlg_dec_lock(dlg); return status; } } PJ_LOG(5,(sub->obj_name, "%s subscription created, using dialog %s", (role==PJSIP_ROLE_UAC ? "UAC" : "UAS"), dlg->obj_name)); *p_evsub = sub; pjsip_dlg_dec_lock(dlg); return PJ_SUCCESS; } /* * Create client subscription session. */ PJ_DEF(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, const pj_str_t *event, unsigned option, pjsip_evsub **p_evsub) { pjsip_evsub *sub; pj_status_t status; PJ_ASSERT_RETURN(dlg && event && p_evsub, PJ_EINVAL); pjsip_dlg_inc_lock(dlg); status = evsub_create(dlg, PJSIP_UAC_ROLE, user_cb, event, option, &sub); if (status != PJ_SUCCESS) goto on_return; /* Add unique Id to Event header, only when PJSIP_EVSUB_NO_EVENT_ID * is not specified. */ if ((option & PJSIP_EVSUB_NO_EVENT_ID) == 0) { pj_create_unique_string(sub->pool, &sub->event->id_param); } /* Increment dlg session. */ pjsip_dlg_inc_session(sub->dlg, &mod_evsub.mod); /* Init group lock */ status = pj_grp_lock_create(dlg->pool, NULL, &sub->grp_lock); if (status != PJ_SUCCESS) { pjsip_dlg_dec_session(sub->dlg, &mod_evsub.mod); goto on_return; } pj_grp_lock_add_ref(sub->grp_lock); pj_grp_lock_add_handler(sub->grp_lock, dlg->pool, sub, &evsub_on_destroy); /* Done */ *p_evsub = sub; on_return: pjsip_dlg_dec_lock(dlg); return status; } /* * Create server subscription session from incoming request. */ PJ_DEF(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, unsigned option, pjsip_evsub **p_evsub) { pjsip_evsub *sub; pjsip_transaction *tsx; pjsip_accept_hdr *accept_hdr; pjsip_event_hdr *event_hdr; pjsip_expires_hdr *expires_hdr; pj_status_t status; /* Check arguments: */ PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL); /* MUST be request message: */ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Transaction MUST have been created (in the dialog) */ tsx = pjsip_rdata_get_tsx(rdata); PJ_ASSERT_RETURN(tsx != NULL, PJSIP_ENOTSX); /* No subscription must have been attached to transaction */ PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] == NULL, PJSIP_ETYPEEXISTS); /* Package MUST implement on_rx_refresh */ PJ_ASSERT_RETURN(user_cb->on_rx_refresh, PJ_EINVALIDOP); /* Request MUST have "Event" header. We need the Event header to get * the package name (don't want to add more arguments in the function). */ event_hdr = (pjsip_event_hdr*) pjsip_msg_find_hdr_by_names(rdata->msg_info.msg, &STR_EVENT, &STR_EVENT_S, NULL); if (event_hdr == NULL) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); } /* Start locking the mutex: */ pjsip_dlg_inc_lock(dlg); /* Create the session: */ status = evsub_create(dlg, PJSIP_UAS_ROLE, user_cb, &event_hdr->event_type, option, &sub); if (status != PJ_SUCCESS) goto on_return; /* Just duplicate Event header from the request */ sub->event = (pjsip_event_hdr*) pjsip_hdr_clone(sub->pool, event_hdr); /* Set the method: */ pjsip_method_copy(sub->pool, &sub->method, &rdata->msg_info.msg->line.req.method); /* Update expiration time according to client request: */ expires_hdr = (pjsip_expires_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); if (expires_hdr) { sub->expires->ivalue = expires_hdr->ivalue; } /* Update time. */ update_expires(sub, sub->expires->ivalue); /* Update Accept header: */ accept_hdr = (pjsip_accept_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL); if (accept_hdr) sub->accept = (pjsip_accept_hdr*)pjsip_hdr_clone(sub->pool,accept_hdr); /* Increment dlg session. */ pjsip_dlg_inc_session(dlg, &mod_evsub.mod); /* Init group lock */ status = pj_grp_lock_create(dlg->pool, NULL, &sub->grp_lock); if (status != PJ_SUCCESS) { pjsip_dlg_dec_session(sub->dlg, &mod_evsub.mod); goto on_return; } pj_grp_lock_add_ref(sub->grp_lock); pj_grp_lock_add_handler(sub->grp_lock, dlg->pool, sub, &evsub_on_destroy); /* We can start the session: */ sub->pending_tsx++; tsx->mod_data[mod_evsub.mod.id] = sub; /* Done. */ *p_evsub = sub; on_return: pjsip_dlg_dec_lock(dlg); return status; } /* * Forcefully destroy subscription. */ PJ_DEF(pj_status_t) pjsip_evsub_terminate( pjsip_evsub *sub, pj_bool_t notify ) { PJ_ASSERT_RETURN(sub, PJ_EINVAL); pjsip_dlg_inc_lock(sub->dlg); /* I think it's pretty safe to disable this check. if (sub->pending_tsx) { pj_assert(!"Unable to terminate when there's pending tsx"); pjsip_dlg_dec_lock(sub->dlg); return PJ_EINVALIDOP; } */ sub->call_cb = notify; set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL, NULL); pjsip_dlg_dec_lock(sub->dlg); return PJ_SUCCESS; } /* * Get subscription state. */ PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub) { return sub->state; } /* * Get state name. */ PJ_DEF(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub) { return sub->state_str.ptr; } /* * Get termination reason. */ PJ_DEF(const pj_str_t*) pjsip_evsub_get_termination_reason(pjsip_evsub *sub) { return &sub->term_reason; } /* * Initiate client subscription */ PJ_DEF(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub, const pjsip_method *method, pj_int32_t expires, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pj_status_t status; PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL); /* Use SUBSCRIBE if method is not specified */ if (method == NULL) method = &pjsip_subscribe_method; pjsip_dlg_inc_lock(sub->dlg); /* Update method: */ if (sub->state == PJSIP_EVSUB_STATE_NULL) pjsip_method_copy(sub->pool, &sub->method, method); status = pjsip_dlg_create_request( sub->dlg, method, -1, &tdata); if (status != PJ_SUCCESS) goto on_return; /* Add Event header: */ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, sub->event)); /* Update and add expires header: */ if (expires >= 0) sub->expires->ivalue = expires; pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, sub->expires)); /* Add Supported header (it's optional in RFC 3265, but some event package * RFC may bring this requirement to SHOULD strength - e.g. RFC 5373) */ { const pjsip_hdr *hdr = pjsip_endpt_get_capability(sub->endpt, PJSIP_H_SUPPORTED, NULL); if (hdr) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, hdr)); } } /* Add Accept header: */ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, sub->accept)); /* Add Allow-Events header: */ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr)); /* Add custom headers */ { const pjsip_hdr *hdr = sub->sub_hdr_list.next; while (hdr != &sub->sub_hdr_list) { pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, hdr)); hdr = hdr->next; } } *p_tdata = tdata; on_return: pjsip_dlg_dec_lock(sub->dlg); return status; } /* * Add custom headers. */ PJ_DEF(pj_status_t) pjsip_evsub_add_header( pjsip_evsub *sub, const pjsip_hdr *hdr_list ) { const pjsip_hdr *hdr; PJ_ASSERT_RETURN(sub && hdr_list, PJ_EINVAL); hdr = hdr_list->next; while (hdr != hdr_list) { pj_list_push_back(&sub->sub_hdr_list, (pjsip_hdr*) pjsip_hdr_clone(sub->pool, hdr)); hdr = hdr->next; } return PJ_SUCCESS; } /* * Accept incoming subscription request. */ PJ_DEF(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub, pjsip_rx_data *rdata, int st_code, const pjsip_hdr *hdr_list ) { pjsip_tx_data *tdata; pjsip_transaction *tsx; pj_status_t status; /* Check arguments */ PJ_ASSERT_RETURN(sub && rdata, PJ_EINVAL); /* Can only be for server subscription: */ PJ_ASSERT_RETURN(sub->role == PJSIP_ROLE_UAS, PJ_EINVALIDOP); /* Only expect 2xx status code (for now) */ PJ_ASSERT_RETURN(st_code/100 == 2, PJ_EINVALIDOP); /* Subscription MUST have been attached to the transaction. * Initial subscription request will be attached on evsub_create_uas(), * while subsequent requests will be attached in tsx_state() */ tsx = pjsip_rdata_get_tsx(rdata); PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] != NULL, PJ_EINVALIDOP); /* Lock dialog */ pjsip_dlg_inc_lock(sub->dlg); /* Create response: */ status = pjsip_dlg_create_response( sub->dlg, rdata, st_code, NULL, &tdata); if (status != PJ_SUCCESS) goto on_return; /* Add expires header: */ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, sub->expires)); /* Add additional header, if any. */ if (hdr_list) { const pjsip_hdr *hdr = hdr_list->next; while (hdr != hdr_list) { pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr)); hdr = hdr->next; } } /* Send the response: */ status = pjsip_dlg_send_response( sub->dlg, tsx, tdata ); if (status != PJ_SUCCESS) goto on_return; /* Set UAS timeout timer, when status code is 2xx and state is not * terminated. */ if (st_code/100 == 2 && sub->state != PJSIP_EVSUB_STATE_TERMINATED) { PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds", sub->expires->ivalue)); set_timer(sub, TIMER_TYPE_UAS_TIMEOUT, sub->expires->ivalue); } on_return: pjsip_dlg_dec_lock(sub->dlg); return status; } /* * Create Subscription-State header based on current server subscription * state. */ static pjsip_sub_state_hdr* sub_state_create( pj_pool_t *pool, pjsip_evsub *sub, pjsip_evsub_state state, const pj_str_t *state_str, const pj_str_t *reason ) { pjsip_sub_state_hdr *sub_state; pj_time_val now, delay; /* Get the remaining time before refresh is required */ pj_gettimeofday(&now); delay = sub->refresh_time; PJ_TIME_VAL_SUB(delay, now); /* Create the Subscription-State header */ sub_state = pjsip_sub_state_hdr_create(pool); /* Fill up the header */ switch (state) { case PJSIP_EVSUB_STATE_NULL: case PJSIP_EVSUB_STATE_SENT: pj_assert(!"Invalid state!"); /* Treat as pending */ case PJSIP_EVSUB_STATE_ACCEPTED: case PJSIP_EVSUB_STATE_PENDING: sub_state->sub_state = STR_PENDING; sub_state->expires_param = delay.sec; break; case PJSIP_EVSUB_STATE_ACTIVE: sub_state->sub_state = STR_ACTIVE; sub_state->expires_param = delay.sec; break; case PJSIP_EVSUB_STATE_TERMINATED: sub_state->sub_state = STR_TERMINATED; if (reason != NULL) pj_strdup(pool, &sub_state->reason_param, reason); break; case PJSIP_EVSUB_STATE_UNKNOWN: pj_assert(state_str != NULL); pj_strdup(pool, &sub_state->sub_state, state_str); break; } return sub_state; } /* * Create and send NOTIFY request. */ PJ_DEF(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub, pjsip_evsub_state state, const pj_str_t *state_str, const pj_str_t *reason, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pjsip_sub_state_hdr *sub_state; pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL); /* Lock dialog. */ pjsip_dlg_inc_lock(sub->dlg); /* Create NOTIFY request */ status = pjsip_dlg_create_request( sub->dlg, pjsip_get_notify_method(), -1, &tdata); if (status != PJ_SUCCESS) goto on_return; /* Add Event header */ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, sub->event)); /* Add Subscription-State header */ sub_state = sub_state_create(tdata->pool, sub, state, state_str, reason); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sub_state); /* Add Allow-Events header */ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr)); /* Add Authentication headers. */ pjsip_auth_clt_init_req( &sub->dlg->auth_sess, tdata ); /* Update reason */ if (reason) pj_strdup(sub->dlg->pool, &sub->term_reason, reason); /* Save destination state. */ sub->dst_state = state; if (state_str) pj_strdup(sub->pool, &sub->dst_state_str, state_str); else sub->dst_state_str.slen = 0; *p_tdata = tdata; on_return: /* Unlock dialog */ pjsip_dlg_dec_lock(sub->dlg); return status; } /* * Create NOTIFY to reflect current status. */ PJ_DEF(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub, pjsip_tx_data **p_tdata ) { return pjsip_evsub_notify( sub, sub->state, &sub->state_str, NULL, p_tdata ); } /* * Send request. */ PJ_DEF(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub, pjsip_tx_data *tdata) { pj_status_t status; /* Must be request message. */ PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Lock */ pjsip_dlg_inc_lock(sub->dlg); /* Send the request. */ status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL); if (status != PJ_SUCCESS) goto on_return; /* Special case for NOTIFY: * The new state was set in pjsip_evsub_notify(), but we apply the * new state now, when the request was actually sent. */ if (pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_notify_method)==0) { PJ_ASSERT_ON_FAIL( sub->dst_state!=PJSIP_EVSUB_STATE_NULL, {goto on_return;}); set_state(sub, sub->dst_state, (sub->dst_state_str.slen ? &sub->dst_state_str : NULL), NULL, NULL); sub->dst_state = PJSIP_EVSUB_STATE_NULL; sub->dst_state_str.slen = 0; } on_return: pjsip_dlg_dec_lock(sub->dlg); return status; } /* Callback to be called to terminate transaction. */ static void terminate_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { pj_str_t *key; pjsip_transaction *tsx; PJ_UNUSED_ARG(timer_heap); /* Clear timer ID */ entry->id = 0; key = (pj_str_t*)entry->user_data; tsx = pjsip_tsx_layer_find_tsx(key, PJ_FALSE); /* Chance of race condition here */ if (tsx) { pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_UPDATED); } } /* * Attach subscription session to newly created transaction, if appropriate. */ static pjsip_evsub *on_new_transaction( pjsip_transaction *tsx, pjsip_event *event) { /* * Newly created transaction will not have subscription session * attached to it. Find the subscription session from the dialog, * by matching the Event header. */ pjsip_dialog *dlg; pjsip_event_hdr *event_hdr; pjsip_msg *msg; struct dlgsub *dlgsub_head, *dlgsub; pjsip_evsub *sub; dlg = pjsip_tsx_get_dlg(tsx); if (!dlg) { pj_assert(!"Transaction should have a dialog instance!"); return NULL; } switch (event->body.tsx_state.type) { case PJSIP_EVENT_RX_MSG: msg = event->body.tsx_state.src.rdata->msg_info.msg; break; case PJSIP_EVENT_TX_MSG: msg = event->body.tsx_state.src.tdata->msg; break; default: if (tsx->role == PJSIP_ROLE_UAC) msg = tsx->last_tx->msg; else msg = NULL; break; } if (!msg) { //Note: // this transaction can be other transaction in the dialog. // The assertion below probably only valid for dialog that // only has one event subscription usage. //pj_assert(!"First transaction event is not TX or RX!"); return NULL; } event_hdr = (pjsip_event_hdr*) pjsip_msg_find_hdr_by_names(msg, &STR_EVENT, &STR_EVENT_S, NULL); if (!event_hdr) { /* Not subscription related message */ return NULL; } /* Find the subscription in the dialog, based on the content * of Event header: */ dlgsub_head = (struct dlgsub*) dlg->mod_data[mod_evsub.mod.id]; if (dlgsub_head == NULL) { dlgsub_head = PJ_POOL_ALLOC_T(dlg->pool, struct dlgsub); pj_list_init(dlgsub_head); dlg->mod_data[mod_evsub.mod.id] = dlgsub_head; } dlgsub = dlgsub_head->next; while (dlgsub != dlgsub_head) { if (pj_stricmp(&dlgsub->sub->event->event_type, &event_hdr->event_type)==0) { /* Event type matched. * Check if event ID matched too. */ if (pj_strcmp(&dlgsub->sub->event->id_param, &event_hdr->id_param)==0) { /* Skip this subscription if it has no event ID and has been * terminated (see ticket #1647). */ if ((dlgsub->sub->option & PJSIP_EVSUB_NO_EVENT_ID) && (pjsip_evsub_get_state(dlgsub->sub)== PJSIP_EVSUB_STATE_TERMINATED)) { dlgsub = dlgsub->next; continue; } else { break; } } /* * Otherwise if it is an UAC subscription, AND * PJSIP_EVSUB_NO_EVENT_ID flag is set, AND * the session's event id is NULL, AND * the incoming request is NOTIFY with event ID, then * we consider it as a match, and update the * session's event id. */ else if (dlgsub->sub->role == PJSIP_ROLE_UAC && (dlgsub->sub->option & PJSIP_EVSUB_NO_EVENT_ID)!=0 && dlgsub->sub->event->id_param.slen==0 && !pjsip_method_cmp(&tsx->method, &pjsip_notify_method)) { /* Update session's event id. */ pj_strdup(dlgsub->sub->pool, &dlgsub->sub->event->id_param, &event_hdr->id_param); break; } } dlgsub = dlgsub->next; } /* Note: * the second condition is for http://trac.pjsip.org/repos/ticket/911 */ if (dlgsub == dlgsub_head || (dlgsub->sub && pjsip_evsub_get_state(dlgsub->sub)==PJSIP_EVSUB_STATE_TERMINATED)) { const char *reason_msg = (dlgsub == dlgsub_head ? "Subscription Does Not Exist" : "Subscription already terminated"); /* This could be incoming request to create new subscription */ PJ_LOG(4,(THIS_FILE, "%s for %.*s, event=%.*s;id=%.*s", reason_msg, (int)tsx->method.name.slen, tsx->method.name.ptr, (int)event_hdr->event_type.slen, event_hdr->event_type.ptr, (int)event_hdr->id_param.slen, event_hdr->id_param.ptr)); /* If this is an incoming NOTIFY, reject with 481 */ if (tsx->state == PJSIP_TSX_STATE_TRYING && pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) { pj_str_t reason; pjsip_tx_data *tdata; pj_status_t status; pj_cstr(&reason, reason_msg); status = pjsip_dlg_create_response(dlg, event->body.tsx_state.src.rdata, 481, &reason, &tdata); if (status == PJ_SUCCESS) { status = pjsip_dlg_send_response(dlg, tsx, tdata); } } return NULL; } /* Found! */ sub = dlgsub->sub; /* Attach session to the transaction */ tsx->mod_data[mod_evsub.mod.id] = sub; sub->pending_tsx++; /* Special case for outgoing/UAC SUBSCRIBE/REFER transaction. * We can only have one pending UAC SUBSCRIBE/REFER, so if another * transaction is started while previous one still alive, terminate * the older one. * * Sample scenario: * - subscribe sent to destination that doesn't exist, transaction * is still retransmitting request, then unsubscribe is sent. */ if (tsx->role == PJSIP_ROLE_UAC && tsx->state == PJSIP_TSX_STATE_CALLING && (pjsip_method_cmp(&tsx->method, &sub->method) == 0 || pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0)) { if (sub->pending_sub && sub->pending_sub->state < PJSIP_TSX_STATE_COMPLETED) { pj_timer_entry *timer; pj_str_t *key; pj_time_val timeout = {0, 0}; PJ_LOG(4,(sub->obj_name, "Cancelling pending subscription request")); /* By convention, we use 490 (Request Updated) status code. * When transaction handler (below) see this status code, it * will ignore the transaction. */ /* This unfortunately may cause deadlock, because at the moment * we are holding dialog's mutex. If a response to this * transaction is in progress in another thread, that thread * will deadlock when trying to acquire dialog mutex, because * it is holding the transaction mutex. * * So the solution is to register timer to kill this transaction. */ //pjsip_tsx_terminate(sub->pending_sub, PJSIP_SC_REQUEST_UPDATED); timer = PJ_POOL_ZALLOC_T(dlg->pool, pj_timer_entry); key = PJ_POOL_ALLOC_T(dlg->pool, pj_str_t); pj_strdup(dlg->pool, key, &sub->pending_sub->transaction_key); timer->cb = &terminate_timer_cb; timer->user_data = key; timer->id = 1; sub->pending_sub_timer = timer; pjsip_endpt_schedule_timer(dlg->endpt, timer, &timeout); } sub->pending_sub = tsx; } return sub; } /* * Create response, adding custome headers and msg body. */ static pj_status_t create_response( pjsip_evsub *sub, pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, const pjsip_hdr *res_hdr, const pjsip_msg_body *body, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pjsip_hdr *hdr; pj_status_t status; status = pjsip_dlg_create_response(sub->dlg, rdata, st_code, st_text, &tdata); if (status != PJ_SUCCESS) return status; *p_tdata = tdata; /* Add response headers. */ hdr = res_hdr->next; while (hdr != res_hdr) { pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr)); hdr = hdr->next; } /* Add msg body, if any */ if (body) { tdata->msg->body = pjsip_msg_body_clone(tdata->pool, body); if (tdata->msg->body == NULL) { PJ_LOG(4,(THIS_FILE, "Error: unable to clone msg body")); /* Ignore */ return PJ_SUCCESS; } } return PJ_SUCCESS; } /* * Get subscription state from the value of Subscription-State header. */ static void get_hdr_state( pjsip_sub_state_hdr *sub_state, pjsip_evsub_state *state, pj_str_t **state_str ) { if (pj_stricmp(&sub_state->sub_state, &STR_TERMINATED)==0) { *state = PJSIP_EVSUB_STATE_TERMINATED; *state_str = NULL; } else if (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0) { *state = PJSIP_EVSUB_STATE_ACTIVE; *state_str = NULL; } else if (pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0) { *state = PJSIP_EVSUB_STATE_PENDING; *state_str = NULL; } else { *state = PJSIP_EVSUB_STATE_UNKNOWN; *state_str = &sub_state->sub_state; } } /* * Transaction event processing by UAC, after subscription is sent. */ static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event ) { if (pjsip_method_cmp(&tsx->method, &sub->method)==0 || pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method)==0) { /* Received response to outgoing request that establishes/refresh * subscription. */ /* First time initial request is sent. */ if (sub->state == PJSIP_EVSUB_STATE_NULL && tsx->state == PJSIP_TSX_STATE_CALLING) { set_state(sub, PJSIP_EVSUB_STATE_SENT, NULL, event, NULL); return; } /* Only interested in final response */ if (tsx->state != PJSIP_TSX_STATE_COMPLETED && tsx->state != PJSIP_TSX_STATE_TERMINATED) { return; } /* Clear pending subscription */ if (tsx == sub->pending_sub) { sub->pending_sub = NULL; } else if (sub->pending_sub != NULL) { /* This SUBSCRIBE transaction has been "renewed" with another * SUBSCRIBE, so we can just ignore this. For example, user * sent SUBSCRIBE followed immediately with UN-SUBSCRIBE. */ return; } /* Handle authentication. */ if (tsx->status_code==401 || tsx->status_code==407) { pjsip_tx_data *tdata; pj_status_t status; if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { /* Previously failed transaction has terminated */ return; } status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess, event->body.tsx_state.src.rdata, tsx->last_tx, &tdata); if (status == PJ_SUCCESS) status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL); if (status != PJ_SUCCESS) { /* Authentication failed! */ set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event, &tsx->status_text); return; } return; } if (tsx->status_code/100 == 2) { /* Successfull SUBSCRIBE request! * This could be: * - response to initial SUBSCRIBE request * - response to subsequent refresh * - response to unsubscription */ if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { /* Ignore; this transaction has been processed before */ return; } /* Update UAC refresh time, if response contains Expires header, * only when we're not unsubscribing. */ if (sub->expires->ivalue != 0) { pjsip_msg *msg; pjsip_expires_hdr *expires; msg = event->body.tsx_state.src.rdata->msg_info.msg; expires = (pjsip_expires_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); if (expires) { sub->expires->ivalue = expires->ivalue; } } /* Update time */ update_expires(sub, sub->expires->ivalue); /* Start UAC refresh timer, only when we're not unsubscribing */ if (sub->expires->ivalue != 0) { unsigned timeout = (sub->expires->ivalue > TIME_UAC_REFRESH) ? sub->expires->ivalue - TIME_UAC_REFRESH : sub->expires->ivalue; /* Reduce timeout by about 1 - 10 secs (randomized) */ if (timeout > 10) timeout += -10 + (pj_rand() % 10); PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout)); set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout); } else { /* Otherwise set timer to terminate client subscription when * NOTIFY to end subscription is not received. */ set_timer(sub, TIMER_TYPE_UAC_TERMINATE, TIME_UAC_TERMINATE); } /* Set state, if necessary */ pj_assert(sub->state != PJSIP_EVSUB_STATE_NULL); if (sub->state == PJSIP_EVSUB_STATE_SENT) { set_state(sub, PJSIP_EVSUB_STATE_ACCEPTED, NULL, event, NULL); } } else { /* Failed SUBSCRIBE request! * * The RFC 3265 says that if outgoing SUBSCRIBE fails with status * other than 481, the subscription is still considered valid for * the duration of the last Expires. * * Since we send refresh about 5 seconds (TIME_UAC_REFRESH) before * expiration, theoritically the expiration is still valid for the * next 5 seconds even when we receive non-481 failed response. * * Ah, what the heck! * * Just terminate now! * */ if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) { /* Ignore, has been handled before */ return; } /* Ignore 490 (Request Updated) status. * This happens when application sends SUBSCRIBE/REFER while * another one is still in progress. */ if (tsx->status_code == PJSIP_SC_REQUEST_UPDATED) { return; } /* Set state to TERMINATED */ set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event, &tsx->status_text); } } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) { /* Incoming NOTIFY. * This can be the result of: * - Initial subscription response * - UAS updating the resource info. * - Unsubscription response. */ int st_code = 200; pj_str_t *st_text = NULL; pjsip_hdr res_hdr; pjsip_msg_body *body = NULL; pjsip_rx_data *rdata; pjsip_msg *msg; pjsip_sub_state_hdr *sub_state; pjsip_evsub_state new_state; pj_str_t *new_state_str; pjsip_tx_data *tdata; pj_status_t status; /* Only want to handle initial NOTIFY receive event. */ if (tsx->state != PJSIP_TSX_STATE_TRYING) return; rdata = event->body.tsx_state.src.rdata; msg = rdata->msg_info.msg; pj_list_init(&res_hdr); /* Get subscription state header. */ sub_state = (pjsip_sub_state_hdr*) pjsip_msg_find_hdr_by_name(msg, &STR_SUB_STATE, NULL); if (sub_state == NULL) { pjsip_warning_hdr *warn_hdr; pj_str_t warn_text = { "Missing Subscription-State header", 33}; /* Bad request! Add warning header. */ st_code = PJSIP_SC_BAD_REQUEST; warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399, pjsip_endpt_name(sub->endpt), &warn_text); pj_list_push_back(&res_hdr, warn_hdr); } /* Call application registered callback to handle incoming NOTIFY, * if any. */ if (st_code==200 && sub->user.on_rx_notify && sub->call_cb) { (*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text, &res_hdr, &body); /* Application MUST specify final response! */ PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; }); /* Must be a valid status code */ PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; }); } /* If non-2xx should be returned, then send the response. * No need to update server subscription state. */ if (st_code >= 300) { status = create_response(sub, rdata, st_code, st_text, &res_hdr, body, &tdata); if (status == PJ_SUCCESS) { status = pjsip_dlg_send_response(sub->dlg, tsx, tdata); } /* Start timer to terminate subscription, just in case server * is not able to generate NOTIFY to our response. */ if (status == PJ_SUCCESS) { unsigned timeout = TIME_UAC_WAIT_NOTIFY; set_timer(sub, TIMER_TYPE_UAC_WAIT_NOTIFY, timeout); } else { char errmsg[PJ_ERR_MSG_SIZE]; pj_str_t reason; reason = pj_strerror(status, errmsg, sizeof(errmsg)); set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL, &reason); } return; } /* Update expiration from the value of expires param in * Subscription-State header, but ONLY when subscription state * is "active" or "pending", AND the header contains expires param. */ if (sub->expires->ivalue != 0 && sub_state->expires_param >= 0 && (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0 || pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0)) { int next_refresh = sub_state->expires_param; unsigned timeout; update_expires(sub, next_refresh); /* Start UAC refresh timer, only when we're not unsubscribing */ timeout = (next_refresh > TIME_UAC_REFRESH) ? next_refresh - TIME_UAC_REFRESH : next_refresh; PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout)); set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout); } /* Find out the state */ get_hdr_state(sub_state, &new_state, &new_state_str); /* Send response. */ status = create_response(sub, rdata, st_code, st_text, &res_hdr, body, &tdata); if (status == PJ_SUCCESS) status = pjsip_dlg_send_response(sub->dlg, tsx, tdata); /* Set the state */ if (status == PJ_SUCCESS) { set_state(sub, new_state, new_state_str, event, &sub_state->reason_param); } else { char errmsg[PJ_ERR_MSG_SIZE]; pj_str_t reason; reason = pj_strerror(status, errmsg, sizeof(errmsg)); set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event, &reason); } } else { /* * Unexpected method! */ PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s", (int)tsx->method.name.slen, tsx->method.name.ptr)); } } /* * Transaction event processing by UAS, after subscription is accepted. */ static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) { if (pjsip_method_cmp(&tsx->method, &sub->method) == 0 || pjsip_method_cmp(&tsx->method, &pjsip_subscribe_method) == 0) { /* * Incoming request (e.g. SUBSCRIBE or REFER) to refresh subsciption. * */ pjsip_rx_data *rdata; pjsip_event_hdr *event_hdr; pjsip_expires_hdr *expires; pjsip_msg *msg; pjsip_tx_data *tdata; int st_code = 200; pj_str_t *st_text = NULL; pjsip_hdr res_hdr; pjsip_msg_body *body = NULL; pjsip_evsub_state old_state; pj_str_t old_state_str; pj_str_t reason = { NULL, 0 }; pj_status_t status; /* Only wants to handle the first event when the request is * received. */ if (tsx->state != PJSIP_TSX_STATE_TRYING) return; rdata = event->body.tsx_state.src.rdata; msg = rdata->msg_info.msg; /* Set expiration time based on client request (in Expires header), * or package default expiration time. */ event_hdr = (pjsip_event_hdr*) pjsip_msg_find_hdr_by_names(msg, &STR_EVENT, &STR_EVENT, NULL); expires = (pjsip_expires_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); if (event_hdr && expires) { struct evpkg *evpkg; evpkg = find_pkg(&event_hdr->event_type); if (evpkg) { if (expires->ivalue < (pj_int32_t)evpkg->pkg_expires) sub->expires->ivalue = expires->ivalue; else sub->expires->ivalue = evpkg->pkg_expires; } } /* Update time (before calling on_rx_refresh, since application * will send NOTIFY. */ update_expires(sub, sub->expires->ivalue); /* Save old state. * If application respond with non-2xx, revert to old state. */ old_state = sub->state; old_state_str = sub->state_str; if (sub->expires->ivalue == 0) { sub->state = PJSIP_EVSUB_STATE_TERMINATED; sub->state_str = evsub_state_names[sub->state]; } else if (sub->state == PJSIP_EVSUB_STATE_NULL) { sub->state = PJSIP_EVSUB_STATE_ACCEPTED; sub->state_str = evsub_state_names[sub->state]; } /* Call application's on_rx_refresh, just in case it wants to send * response other than 200 (OK) */ pj_list_init(&res_hdr); if (sub->user.on_rx_refresh && sub->call_cb) { (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text, &res_hdr, &body); } /* Application MUST specify final response! */ PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; }); /* Must be a valid status code */ PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; }); /* Create and send response */ status = create_response(sub, rdata, st_code, st_text, &res_hdr, body, &tdata); if (status == PJ_SUCCESS) { /* Add expires header: */ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, sub->expires)); /* Send */ status = pjsip_dlg_send_response(sub->dlg, tsx, tdata); } /* Update state or revert state */ if (st_code/100==2) { if (sub->expires->ivalue == 0) { set_state(sub, sub->state, NULL, event, &reason); } else if (sub->state == PJSIP_EVSUB_STATE_NULL) { set_state(sub, sub->state, NULL, event, &reason); } /* Set UAS timeout timer, when state is not terminated. */ if (sub->state != PJSIP_EVSUB_STATE_TERMINATED) { PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds", sub->expires->ivalue)); set_timer(sub, TIMER_TYPE_UAS_TIMEOUT, sub->expires->ivalue); } } else { sub->state = old_state; sub->state_str = old_state_str; } } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) { /* Handle authentication */ if (tsx->state == PJSIP_TSX_STATE_COMPLETED && (tsx->status_code==401 || tsx->status_code==407)) { pjsip_tx_data *tdata; pj_status_t status; pjsip_rx_data *rdata = event->body.tsx_state.src.rdata; status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess, rdata, tsx->last_tx, &tdata); if (status == PJ_SUCCESS) status = pjsip_dlg_send_request(sub->dlg, tdata, -1, NULL); if (status != PJ_SUCCESS) { /* Can't authenticate. Terminate session (?) */ set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL, &tsx->status_text); return; } } if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) return; /* NOTIFY failure check */ if (tsx->status_code/100 != 2) { pj_bool_t should_terminate_sub = PJ_FALSE; if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { if (tsx->status_code == 481) { should_terminate_sub = PJ_TRUE; } else { pjsip_retry_after_hdr *retry_after; pjsip_rx_data *rdata = event->body.tsx_state.src.rdata; pjsip_msg *msg = rdata->msg_info.msg; retry_after = (pjsip_retry_after_hdr*) pjsip_msg_find_hdr_by_name(msg, &STR_RETRY_AFTER, NULL); if (!retry_after) { should_terminate_sub = PJ_TRUE; } } } else if (event->body.tsx_state.type == PJSIP_EVENT_TIMER) { if (tsx->status_code == 408) { should_terminate_sub = PJ_TRUE; } } /* * Terminate event usage if we receive non 2xx without retry_after * parameter, 481, 408 responses. */ if (should_terminate_sub) { set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event, &tsx->status_text); return; } } } else { /* * Unexpected method! */ PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s", (int)tsx->method.name.slen, tsx->method.name.ptr)); } } /* * Notification when transaction state has changed! */ static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event) { pjsip_evsub *sub = pjsip_tsx_get_evsub(tsx); if (sub == NULL) { sub = on_new_transaction(tsx, event); if (sub == NULL) return; } /* Call on_tsx_state callback, if any. */ if (sub->user.on_tsx_state && sub->call_cb) (*sub->user.on_tsx_state)(sub, tsx, event); /* Process the event: */ if (sub->role == PJSIP_ROLE_UAC) { on_tsx_state_uac(sub, tsx, event); } else { on_tsx_state_uas(sub, tsx, event); } /* Check transaction TERMINATE event */ if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { --sub->pending_tsx; if (sub->state == PJSIP_EVSUB_STATE_TERMINATED && sub->pending_tsx == 0) { evsub_destroy(sub); } } } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-simple/evsub_msg.c ================================================ /* $Id: evsub_msg.c 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include /* * Event header. */ static int pjsip_event_hdr_print( pjsip_event_hdr *hdr, char *buf, pj_size_t size); static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool, const pjsip_event_hdr *hdr); static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool, const pjsip_event_hdr*); static pjsip_hdr_vptr event_hdr_vptr = { (pjsip_hdr_clone_fptr) &pjsip_event_hdr_clone, (pjsip_hdr_clone_fptr) &pjsip_event_hdr_shallow_clone, (pjsip_hdr_print_fptr) &pjsip_event_hdr_print, }; PJ_DEF(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool) { pjsip_event_hdr *hdr = PJ_POOL_ZALLOC_T(pool, pjsip_event_hdr); hdr->type = PJSIP_H_OTHER; hdr->name.ptr = "Event"; hdr->name.slen = 5; hdr->sname.ptr = "o"; hdr->sname.slen = 1; hdr->vptr = &event_hdr_vptr; pj_list_init(hdr); pj_list_init(&hdr->other_param); return hdr; } static int pjsip_event_hdr_print( pjsip_event_hdr *hdr, char *buf, pj_size_t size) { char *p = buf; char *endbuf = buf+size; pj_ssize_t printed; const pjsip_parser_const_t *pc = pjsip_parser_const(); copy_advance(p, hdr->name); *p++ = ':'; *p++ = ' '; copy_advance(p, hdr->event_type); copy_advance_pair(p, ";id=", 4, hdr->id_param); printed = pjsip_param_print_on(&hdr->other_param, p, endbuf-p, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ';'); if (printed < 0) return (int)printed; p += printed; return (int)(p - buf); } static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool, const pjsip_event_hdr *rhs) { pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool); pj_strdup(pool, &hdr->event_type, &rhs->event_type); pj_strdup(pool, &hdr->id_param, &rhs->id_param); pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param); return hdr; } static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool, const pjsip_event_hdr *rhs ) { pjsip_event_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_event_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param); return hdr; } /* * Allow-Events header. */ PJ_DEF(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool) { const pj_str_t STR_ALLOW_EVENTS = { "Allow-Events", 12}; pjsip_allow_events_hdr *hdr; hdr = pjsip_generic_array_hdr_create(pool, &STR_ALLOW_EVENTS); if (hdr) { hdr->sname.ptr = "u"; hdr->sname.slen = 1; } return hdr; } /* * Subscription-State header. */ static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr, char *buf, pj_size_t size); static pjsip_sub_state_hdr* pjsip_sub_state_hdr_clone(pj_pool_t *pool, const pjsip_sub_state_hdr *hdr); static pjsip_sub_state_hdr* pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool, const pjsip_sub_state_hdr*); static pjsip_hdr_vptr sub_state_hdr_vptr = { (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_clone, (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_shallow_clone, (pjsip_hdr_print_fptr) &pjsip_sub_state_hdr_print, }; PJ_DEF(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool) { pj_str_t sub_state = { "Subscription-State", 18 }; pjsip_sub_state_hdr *hdr = PJ_POOL_ZALLOC_T(pool, pjsip_sub_state_hdr); hdr->type = PJSIP_H_OTHER; hdr->name = hdr->sname = sub_state; hdr->vptr = &sub_state_hdr_vptr; hdr->expires_param = -1; hdr->retry_after = -1; pj_list_init(hdr); pj_list_init(&hdr->other_param); return hdr; } static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr, char *buf, pj_size_t size) { char *p = buf; char *endbuf = buf+size; pj_ssize_t printed; const pjsip_parser_const_t *pc = pjsip_parser_const(); copy_advance(p, hdr->name); *p++ = ':'; *p++ = ' '; copy_advance_escape(p, hdr->sub_state, pc->pjsip_TOKEN_SPEC); copy_advance_pair_escape(p, ";reason=", 8, hdr->reason_param, pc->pjsip_TOKEN_SPEC); if (hdr->expires_param >= 0) { pj_memcpy(p, ";expires=", 9); p += 9; printed = pj_utoa(hdr->expires_param, p); p += printed; } if (hdr->retry_after >= 0) { pj_memcpy(p, ";retry-after=", 13); p += 9; printed = pj_utoa(hdr->retry_after, p); p += printed; } printed = pjsip_param_print_on( &hdr->other_param, p, endbuf-p, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ';'); if (printed < 0) return (int)printed; p += printed; return (int)(p - buf); } static pjsip_sub_state_hdr* pjsip_sub_state_hdr_clone(pj_pool_t *pool, const pjsip_sub_state_hdr *rhs) { pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool); pj_strdup(pool, &hdr->sub_state, &rhs->sub_state); pj_strdup(pool, &hdr->reason_param, &rhs->reason_param); hdr->retry_after = rhs->retry_after; hdr->expires_param = rhs->expires_param; pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param); return hdr; } static pjsip_sub_state_hdr* pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool, const pjsip_sub_state_hdr *rhs) { pjsip_sub_state_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_sub_state_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param); return hdr; } /* * Parse Event header. */ static pjsip_hdr *parse_hdr_event(pjsip_parse_ctx *ctx) { pjsip_event_hdr *hdr = pjsip_event_hdr_create(ctx->pool); const pj_str_t id_param = { "id", 2 }; const pjsip_parser_const_t *pc = pjsip_parser_const(); pj_scan_get(ctx->scanner, &pc->pjsip_TOKEN_SPEC, &hdr->event_type); while (*ctx->scanner->curptr == ';') { pj_str_t pname, pvalue; pj_scan_get_char(ctx->scanner); pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0); if (pj_stricmp(&pname, &id_param)==0) { hdr->id_param = pvalue; } else { pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param); param->name = pname; param->value = pvalue; pj_list_push_back(&hdr->other_param, param); } } pjsip_parse_end_hdr_imp( ctx->scanner ); return (pjsip_hdr*)hdr; } /* * Parse Subscription-State header. */ static pjsip_hdr* parse_hdr_sub_state( pjsip_parse_ctx *ctx ) { pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(ctx->pool); const pj_str_t reason = { "reason", 6 }, expires = { "expires", 7 }, retry_after = { "retry-after", 11 }; const pjsip_parser_const_t *pc = pjsip_parser_const(); pj_scan_get(ctx->scanner, &pc->pjsip_TOKEN_SPEC, &hdr->sub_state); while (*ctx->scanner->curptr == ';') { pj_str_t pname, pvalue; pj_scan_get_char(ctx->scanner); pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0); if (pj_stricmp(&pname, &reason) == 0) { hdr->reason_param = pvalue; } else if (pj_stricmp(&pname, &expires) == 0) { hdr->expires_param = pj_strtoul(&pvalue); } else if (pj_stricmp(&pname, &retry_after) == 0) { hdr->retry_after = pj_strtoul(&pvalue); } else { pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param); param->name = pname; param->value = pvalue; pj_list_push_back(&hdr->other_param, param); } } pjsip_parse_end_hdr_imp( ctx->scanner ); return (pjsip_hdr*)hdr; } /* * Parse Allow-Events header. */ static pjsip_hdr* parse_hdr_allow_events(pjsip_parse_ctx *ctx) { pjsip_allow_events_hdr *allow_events = pjsip_allow_events_hdr_create(ctx->pool); const pjsip_parser_const_t *pc = pjsip_parser_const(); pj_scanner *scanner = ctx->scanner; /* Some header fields allow empty elements in the value: * Accept, Allow, Supported */ if (pj_scan_is_eof(scanner) || *scanner->curptr == '\r' || *scanner->curptr == '\n') { goto end; } pj_scan_get( scanner, &pc->pjsip_NOT_COMMA_OR_NEWLINE, &allow_events->values[0]); allow_events->count++; while (*scanner->curptr == ',') { pj_scan_get_char(scanner); pj_scan_get( scanner, &pc->pjsip_NOT_COMMA_OR_NEWLINE, &allow_events->values[allow_events->count]); allow_events->count++; if (allow_events->count >= PJSIP_MAX_ALLOW_EVENTS) break; } end: pjsip_parse_end_hdr_imp(scanner); return (pjsip_hdr*)allow_events; } /* * Register header parsers. */ PJ_DEF(void) pjsip_evsub_init_parser(void) { pjsip_register_hdr_parser( "Event", "o", &parse_hdr_event); pjsip_register_hdr_parser( "Subscription-State", NULL, &parse_hdr_sub_state); pjsip_register_hdr_parser( "Allow-Events", NULL, &parse_hdr_allow_events); } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-simple/iscomposing.c ================================================ /* $Id: iscomposing.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include /* MIME */ static const pj_str_t STR_MIME_TYPE = { "application", 11 }; static const pj_str_t STR_MIME_SUBTYPE = { "im-iscomposing+xml", 18 }; /* XML node constants. */ static const pj_str_t STR_ISCOMPOSING = { "isComposing", 11 }; static const pj_str_t STR_STATE = { "state", 5 }; static const pj_str_t STR_ACTIVE = { "active", 6 }; static const pj_str_t STR_IDLE = { "idle", 4 }; static const pj_str_t STR_LASTACTIVE = { "lastactive", 10 }; static const pj_str_t STR_CONTENTTYPE = { "contenttype", 11 }; static const pj_str_t STR_REFRESH = { "refresh", 7 }; /* XML attributes constants */ static const pj_str_t STR_XMLNS_NAME = { "xmlns", 5 }; static const pj_str_t STR_XMLNS_VAL = { "urn:ietf:params:xml:ns:im-iscomposing", 37 }; static const pj_str_t STR_XMLNS_XSI_NAME = { "xmlns:xsi", 9 }; static const pj_str_t STR_XMLNS_XSI_VAL = { "http://www.w3.org/2001/XMLSchema-instance", 41 }; static const pj_str_t STR_XSI_SLOC_NAME = { "xsi:schemaLocation", 18 }; static const pj_str_t STR_XSI_SLOC_VAL = { "urn:ietf:params:xml:ns:im-composing iscomposing.xsd", 51 }; PJ_DEF(pj_xml_node*) pjsip_iscomposing_create_xml( pj_pool_t *pool, pj_bool_t is_composing, const pj_time_val *lst_actv, const pj_str_t *content_tp, int refresh) { pj_xml_node *doc, *node; pj_xml_attr *attr; /* Root document. */ doc = pj_xml_node_new(pool, &STR_ISCOMPOSING); /* Add attributes */ attr = pj_xml_attr_new(pool, &STR_XMLNS_NAME, &STR_XMLNS_VAL); pj_xml_add_attr(doc, attr); attr = pj_xml_attr_new(pool, &STR_XMLNS_XSI_NAME, &STR_XMLNS_XSI_VAL); pj_xml_add_attr(doc, attr); attr = pj_xml_attr_new(pool, &STR_XSI_SLOC_NAME, &STR_XSI_SLOC_VAL); pj_xml_add_attr(doc, attr); /* Add state. */ node = pj_xml_node_new(pool, &STR_STATE); if (is_composing) node->content = STR_ACTIVE; else node->content = STR_IDLE; pj_xml_add_node(doc, node); /* Add lastactive, if any. */ PJ_UNUSED_ARG(lst_actv); //if (!is_composing && lst_actv) { // PJ_TODO(IMPLEMENT_LAST_ACTIVE_ATTRIBUTE); //} /* Add contenttype, if any. */ if (content_tp) { node = pj_xml_node_new(pool, &STR_CONTENTTYPE); pj_strdup(pool, &node->content, content_tp); pj_xml_add_node(doc, node); } /* Add refresh, if any. */ if (is_composing && refresh > 1 && refresh < 3601) { node = pj_xml_node_new(pool, &STR_REFRESH); node->content.ptr = (char*) pj_pool_alloc(pool, 10); node->content.slen = pj_utoa(refresh, node->content.ptr); pj_xml_add_node(doc, node); } /* Done! */ return doc; } /* * Function to print XML message body. */ static int xml_print_body( struct pjsip_msg_body *msg_body, char *buf, pj_size_t size) { return pj_xml_print((const pj_xml_node*)msg_body->data, buf, size, PJ_TRUE); } /* * Function to clone XML document. */ static void* xml_clone_data(pj_pool_t *pool, const void *data, unsigned len) { PJ_UNUSED_ARG(len); return pj_xml_clone( pool, (const pj_xml_node*)data); } PJ_DEF(pjsip_msg_body*) pjsip_iscomposing_create_body( pj_pool_t *pool, pj_bool_t is_composing, const pj_time_val *lst_actv, const pj_str_t *content_tp, int refresh) { pj_xml_node *doc; pjsip_msg_body *body; doc = pjsip_iscomposing_create_xml( pool, is_composing, lst_actv, content_tp, refresh); if (doc == NULL) return NULL; body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body); body->content_type.type = STR_MIME_TYPE; body->content_type.subtype = STR_MIME_SUBTYPE; body->data = doc; body->len = 0; body->print_body = &xml_print_body; body->clone_data = &xml_clone_data; return body; } PJ_DEF(pj_status_t) pjsip_iscomposing_parse( pj_pool_t *pool, char *msg, pj_size_t len, pj_bool_t *p_is_composing, pj_str_t **p_last_active, pj_str_t **p_content_type, int *p_refresh ) { pj_xml_node *doc, *node; /* Set defaults: */ if (p_is_composing) *p_is_composing = PJ_FALSE; if (p_last_active) *p_last_active = NULL; if (p_content_type) *p_content_type = NULL; /* Parse XML */ doc = pj_xml_parse( pool, msg, len); if (!doc) return PJLIB_UTIL_EINXML; /* Root document must be "isComposing" */ if (pj_stricmp(&doc->name, &STR_ISCOMPOSING) != 0) return PJSIP_SIMPLE_EBADISCOMPOSE; /* Get the status. */ if (p_is_composing) { node = pj_xml_find_node(doc, &STR_STATE); if (node == NULL) return PJSIP_SIMPLE_EBADISCOMPOSE; *p_is_composing = (pj_stricmp(&node->content, &STR_ACTIVE)==0); } /* Get last active. */ if (p_last_active) { node = pj_xml_find_node(doc, &STR_LASTACTIVE); if (node) *p_last_active = &node->content; } /* Get content type */ if (p_content_type) { node = pj_xml_find_node(doc, &STR_CONTENTTYPE); if (node) *p_content_type = &node->content; } /* Get refresh */ if (p_refresh) { node = pj_xml_find_node(doc, &STR_REFRESH); if (node) *p_refresh = pj_strtoul(&node->content); } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-simple/mwi.c ================================================ /* $Id: mwi.c 4172 2012-06-19 14:35:18Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "mwi.c" /* * MWI module (mod-mdi) */ static struct pjsip_module mod_mwi = { NULL, NULL, /* prev, next. */ { "mod-mwi", 7 }, /* Name. */ -1, /* Id */ PJSIP_MOD_PRIORITY_DIALOG_USAGE,/* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ NULL, /* unload() */ NULL, /* on_rx_request() */ NULL, /* on_rx_response() */ NULL, /* on_tx_request. */ NULL, /* on_tx_response() */ NULL, /* on_tsx_state() */ }; /* * This structure describe an mwi agent (both client and server) */ typedef struct pjsip_mwi { pjsip_evsub *sub; /**< Event subscribtion record. */ pjsip_dialog *dlg; /**< The dialog. */ pjsip_evsub_user user_cb; /**< The user callback. */ /* These are for server subscriptions */ pj_pool_t *body_pool; /**< Pool to save message body */ pjsip_media_type mime_type; /**< MIME type of last msg body */ pj_str_t body; /**< Last sent message body */ } pjsip_mwi; /* * Forward decl for evsub callbacks. */ static void mwi_on_evsub_state( pjsip_evsub *sub, pjsip_event *event); static void mwi_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event); static void mwi_on_evsub_rx_refresh( pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body); static void mwi_on_evsub_rx_notify( pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body); static void mwi_on_evsub_client_refresh(pjsip_evsub *sub); static void mwi_on_evsub_server_timeout(pjsip_evsub *sub); /* * Event subscription callback for mwi. */ static pjsip_evsub_user mwi_user = { &mwi_on_evsub_state, &mwi_on_evsub_tsx_state, &mwi_on_evsub_rx_refresh, &mwi_on_evsub_rx_notify, &mwi_on_evsub_client_refresh, &mwi_on_evsub_server_timeout, }; /* * Some static constants. */ static const pj_str_t STR_EVENT = { "Event", 5 }; static const pj_str_t STR_MWI = { "message-summary", 15 }; static const pj_str_t STR_APP_SIMPLE_SMS = { "application/simple-message-summary", 34}; /* * Init mwi module. */ PJ_DEF(pj_status_t) pjsip_mwi_init_module( pjsip_endpoint *endpt, pjsip_module *mod_evsub) { pj_status_t status; pj_str_t accept[1]; /* Check arguments. */ PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL); /* Must have not been registered */ PJ_ASSERT_RETURN(mod_mwi.id == -1, PJ_EINVALIDOP); /* Register to endpoint */ status = pjsip_endpt_register_module(endpt, &mod_mwi); if (status != PJ_SUCCESS) return status; accept[0] = STR_APP_SIMPLE_SMS; /* Register event package to event module. */ status = pjsip_evsub_register_pkg( &mod_mwi, &STR_MWI, PJSIP_MWI_DEFAULT_EXPIRES, PJ_ARRAY_SIZE(accept), accept); if (status != PJ_SUCCESS) { pjsip_endpt_unregister_module(endpt, &mod_mwi); return status; } return PJ_SUCCESS; } /* * Get mwi module instance. */ PJ_DEF(pjsip_module*) pjsip_mwi_instance(void) { return &mod_mwi; } /* * Create client subscription. */ PJ_DEF(pj_status_t) pjsip_mwi_create_uac( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, unsigned options, pjsip_evsub **p_evsub ) { pj_status_t status; pjsip_mwi *mwi; pjsip_evsub *sub; PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL); PJ_UNUSED_ARG(options); pjsip_dlg_inc_lock(dlg); /* Create event subscription */ status = pjsip_evsub_create_uac( dlg, &mwi_user, &STR_MWI, options, &sub); if (status != PJ_SUCCESS) goto on_return; /* Create mwi */ mwi = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_mwi); mwi->dlg = dlg; mwi->sub = sub; if (user_cb) pj_memcpy(&mwi->user_cb, user_cb, sizeof(pjsip_evsub_user)); /* Attach to evsub */ pjsip_evsub_set_mod_data(sub, mod_mwi.id, mwi); *p_evsub = sub; on_return: pjsip_dlg_dec_lock(dlg); return status; } /* * Create server subscription. */ PJ_DEF(pj_status_t) pjsip_mwi_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, pjsip_evsub **p_evsub ) { pjsip_accept_hdr *accept; pjsip_event_hdr *event; pjsip_evsub *sub; pjsip_mwi *mwi; char obj_name[PJ_MAX_OBJ_NAME]; pj_status_t status; /* Check arguments */ PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL); /* Must be request message */ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Check that request is SUBSCRIBE */ PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_subscribe_method)==0, PJSIP_SIMPLE_ENOTSUBSCRIBE); /* Check that Event header contains "mwi" */ event = (pjsip_event_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL); if (!event) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); } if (pj_stricmp(&event->event_type, &STR_MWI) != 0) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT); } /* Check that request contains compatible Accept header. */ accept = (pjsip_accept_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL); if (accept) { unsigned i; for (i=0; icount; ++i) { if (pj_stricmp(&accept->values[i], &STR_APP_SIMPLE_SMS)==0) { break; } } if (i==accept->count) { /* Nothing is acceptable */ return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE); } } else { /* No Accept header. * Assume client supports "application/simple-message-summary" */ } /* Lock dialog */ pjsip_dlg_inc_lock(dlg); /* Create server subscription */ status = pjsip_evsub_create_uas( dlg, &mwi_user, rdata, 0, &sub); if (status != PJ_SUCCESS) goto on_return; /* Create server mwi subscription */ mwi = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_mwi); mwi->dlg = dlg; mwi->sub = sub; if (user_cb) pj_memcpy(&mwi->user_cb, user_cb, sizeof(pjsip_evsub_user)); pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "mwibd%p", dlg->pool); mwi->body_pool = pj_pool_create(dlg->pool->factory, obj_name, 512, 512, NULL); /* Attach to evsub */ pjsip_evsub_set_mod_data(sub, mod_mwi.id, mwi); /* Done: */ *p_evsub = sub; on_return: pjsip_dlg_dec_lock(dlg); return status; } /* * Forcefully terminate mwi. */ PJ_DEF(pj_status_t) pjsip_mwi_terminate( pjsip_evsub *sub, pj_bool_t notify ) { return pjsip_evsub_terminate(sub, notify); } /* * Create SUBSCRIBE */ PJ_DEF(pj_status_t) pjsip_mwi_initiate( pjsip_evsub *sub, pj_int32_t expires, pjsip_tx_data **p_tdata) { return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires, p_tdata); } /* * Accept incoming subscription. */ PJ_DEF(pj_status_t) pjsip_mwi_accept( pjsip_evsub *sub, pjsip_rx_data *rdata, int st_code, const pjsip_hdr *hdr_list ) { return pjsip_evsub_accept( sub, rdata, st_code, hdr_list ); } /* * Create message body and attach it to the (NOTIFY) request. */ static pj_status_t mwi_create_msg_body( pjsip_mwi *mwi, pjsip_tx_data *tdata) { pjsip_msg_body *body; pj_str_t dup_text; PJ_ASSERT_RETURN(mwi->mime_type.type.slen && mwi->body.slen, PJ_EINVALIDOP); /* Clone the message body and mime type */ pj_strdup(tdata->pool, &dup_text, &mwi->body); /* Create the message body */ body = PJ_POOL_ZALLOC_T(tdata->pool, pjsip_msg_body); pjsip_media_type_cp(tdata->pool, &body->content_type, &mwi->mime_type); body->data = dup_text.ptr; body->len = (unsigned)dup_text.slen; body->print_body = &pjsip_print_text_body; body->clone_data = &pjsip_clone_text_data; /* Attach to tdata */ tdata->msg->body = body; return PJ_SUCCESS; } /* * Create NOTIFY */ PJ_DEF(pj_status_t) pjsip_mwi_notify( pjsip_evsub *sub, pjsip_evsub_state state, const pj_str_t *state_str, const pj_str_t *reason, const pjsip_media_type *mime_type, const pj_str_t *body, pjsip_tx_data **p_tdata) { pjsip_mwi *mwi; pjsip_tx_data *tdata; pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(sub && mime_type && body && p_tdata, PJ_EINVAL); /* Get the mwi object. */ mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id); PJ_ASSERT_RETURN(mwi != NULL, PJ_EINVALIDOP); /* Lock object. */ pjsip_dlg_inc_lock(mwi->dlg); /* Create the NOTIFY request. */ status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata); if (status != PJ_SUCCESS) goto on_return; /* Update the cached message body */ if (mime_type || body) pj_pool_reset(mwi->body_pool); if (mime_type) pjsip_media_type_cp(mwi->body_pool, &mwi->mime_type, mime_type); if (body) pj_strdup(mwi->body_pool, &mwi->body, body); /* Create message body */ status = mwi_create_msg_body( mwi, tdata ); if (status != PJ_SUCCESS) goto on_return; /* Done. */ *p_tdata = tdata; on_return: pjsip_dlg_dec_lock(mwi->dlg); return status; } /* * Create NOTIFY that reflect current state. */ PJ_DEF(pj_status_t) pjsip_mwi_current_notify( pjsip_evsub *sub, pjsip_tx_data **p_tdata ) { pjsip_mwi *mwi; pjsip_tx_data *tdata; pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(sub && p_tdata, PJ_EINVAL); /* Get the mwi object. */ mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id); PJ_ASSERT_RETURN(mwi != NULL, PJ_EINVALIDOP); /* Lock object. */ pjsip_dlg_inc_lock(mwi->dlg); /* Create the NOTIFY request. */ status = pjsip_evsub_current_notify( sub, &tdata); if (status != PJ_SUCCESS) goto on_return; /* Create message body to reflect the mwi status. */ status = mwi_create_msg_body( mwi, tdata ); if (status != PJ_SUCCESS) goto on_return; /* Done. */ *p_tdata = tdata; on_return: pjsip_dlg_dec_lock(mwi->dlg); return status; } /* * Send request. */ PJ_DEF(pj_status_t) pjsip_mwi_send_request( pjsip_evsub *sub, pjsip_tx_data *tdata ) { return pjsip_evsub_send_request(sub, tdata); } /* * This callback is called by event subscription when subscription * state has changed. */ static void mwi_on_evsub_state( pjsip_evsub *sub, pjsip_event *event) { pjsip_mwi *mwi; mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id); PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;}); if (mwi->user_cb.on_evsub_state) (*mwi->user_cb.on_evsub_state)(sub, event); if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { if (mwi->body_pool) { pj_pool_release(mwi->body_pool); mwi->body_pool = NULL; } } } /* * Called when transaction state has changed. */ static void mwi_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) { pjsip_mwi *mwi; mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id); PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;}); if (mwi->user_cb.on_tsx_state) (*mwi->user_cb.on_tsx_state)(sub, tsx, event); } /* * Called when SUBSCRIBE is received. */ static void mwi_on_evsub_rx_refresh( pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) { pjsip_mwi *mwi; mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id); PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;}); if (mwi->user_cb.on_rx_refresh) { (*mwi->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text, res_hdr, p_body); } else { /* Implementors MUST send NOTIFY if it implements on_rx_refresh */ pjsip_tx_data *tdata; pj_str_t timeout = { "timeout", 7}; pj_status_t status; if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) { status = pjsip_mwi_notify( sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, &timeout, NULL, NULL, &tdata); } else { status = pjsip_mwi_current_notify(sub, &tdata); } if (status == PJ_SUCCESS) pjsip_mwi_send_request(sub, tdata); } } /* * Called when NOTIFY is received. */ static void mwi_on_evsub_rx_notify( pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) { pjsip_mwi *mwi; mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id); PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;}); /* Just notify application. */ if (mwi->user_cb.on_rx_notify) { (*mwi->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text, res_hdr, p_body); } } /* * Called when it's time to send SUBSCRIBE. */ static void mwi_on_evsub_client_refresh(pjsip_evsub *sub) { pjsip_mwi *mwi; mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id); PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;}); if (mwi->user_cb.on_client_refresh) { (*mwi->user_cb.on_client_refresh)(sub); } else { pj_status_t status; pjsip_tx_data *tdata; status = pjsip_mwi_initiate(sub, -1, &tdata); if (status == PJ_SUCCESS) pjsip_mwi_send_request(sub, tdata); } } /* * Called when no refresh is received after the interval. */ static void mwi_on_evsub_server_timeout(pjsip_evsub *sub) { pjsip_mwi *mwi; mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id); PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;}); if (mwi->user_cb.on_server_timeout) { (*mwi->user_cb.on_server_timeout)(sub); } else { pj_status_t status; pjsip_tx_data *tdata; pj_str_t reason = { "timeout", 7 }; status = pjsip_mwi_notify(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, &reason, NULL, NULL, &tdata); if (status == PJ_SUCCESS) pjsip_mwi_send_request(sub, tdata); } } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-simple/pidf.c ================================================ /* $Id: pidf.c 3841 2011-10-24 09:28:13Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include struct pjpidf_op_desc pjpidf_op = { { &pjpidf_pres_construct, &pjpidf_pres_add_tuple, &pjpidf_pres_get_first_tuple, &pjpidf_pres_get_next_tuple, &pjpidf_pres_find_tuple, &pjpidf_pres_remove_tuple, &pjpidf_pres_add_note, &pjpidf_pres_get_first_note, &pjpidf_pres_get_next_note }, { &pjpidf_tuple_construct, &pjpidf_tuple_get_id, &pjpidf_tuple_set_id, &pjpidf_tuple_get_status, &pjpidf_tuple_get_contact, &pjpidf_tuple_set_contact, &pjpidf_tuple_set_contact_prio, &pjpidf_tuple_get_contact_prio, &pjpidf_tuple_add_note, &pjpidf_tuple_get_first_note, &pjpidf_tuple_get_next_note, &pjpidf_tuple_get_timestamp, &pjpidf_tuple_set_timestamp, &pjpidf_tuple_set_timestamp_np }, { &pjpidf_status_construct, &pjpidf_status_is_basic_open, &pjpidf_status_set_basic_open } }; static pj_str_t PRESENCE = { "presence", 8 }; static pj_str_t ENTITY = { "entity", 6}; static pj_str_t TUPLE = { "tuple", 5 }; static pj_str_t ID = { "id", 2 }; static pj_str_t NOTE = { "note", 4 }; static pj_str_t STATUS = { "status", 6 }; static pj_str_t CONTACT = { "contact", 7 }; static pj_str_t PRIORITY = { "priority", 8 }; static pj_str_t TIMESTAMP = { "timestamp", 9 }; static pj_str_t BASIC = { "basic", 5 }; static pj_str_t OPEN = { "open", 4 }; static pj_str_t CLOSED = { "closed", 6 }; static pj_str_t EMPTY_STRING = { NULL, 0 }; static pj_str_t XMLNS = { "xmlns", 5 }; static pj_str_t PIDF_XMLNS = { "urn:ietf:params:xml:ns:pidf", 27 }; static void xml_init_node(pj_pool_t *pool, pj_xml_node *node, pj_str_t *name, const pj_str_t *value) { pj_list_init(&node->attr_head); pj_list_init(&node->node_head); node->name = *name; if (value) pj_strdup(pool, &node->content, value); else node->content.ptr=NULL, node->content.slen=0; } static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name, const pj_str_t *value) { pj_xml_attr *attr = PJ_POOL_ALLOC_T(pool, pj_xml_attr); attr->name = *name; pj_strdup(pool, &attr->value, value); return attr; } /* Presence */ PJ_DEF(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres, const pj_str_t *entity) { pj_xml_attr *attr; xml_init_node(pool, pres, &PRESENCE, NULL); attr = xml_create_attr(pool, &ENTITY, entity); pj_xml_add_attr(pres, attr); attr = xml_create_attr(pool, &XMLNS, &PIDF_XMLNS); pj_xml_add_attr(pres, attr); } PJ_DEF(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres, const pj_str_t *id) { pjpidf_tuple *t = PJ_POOL_ALLOC_T(pool, pjpidf_tuple); pjpidf_tuple_construct(pool, t, id); pj_xml_add_node(pres, t); return t; } PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres) { return pj_xml_find_node(pres, &TUPLE); } PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres, pjpidf_tuple *tuple) { return pj_xml_find_next_node(pres, tuple, &TUPLE); } static pj_bool_t find_tuple_by_id(const pj_xml_node *node, const void *id) { return pj_xml_find_attr(node, &ID, (const pj_str_t*)id) != NULL; } PJ_DEF(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres, const pj_str_t *id) { return pj_xml_find(pres, &TUPLE, id, &find_tuple_by_id); } PJ_DEF(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, pjpidf_tuple *t) { PJ_UNUSED_ARG(pres); pj_list_erase(t); } PJ_DEF(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres, const pj_str_t *text) { pjpidf_note *note = PJ_POOL_ALLOC_T(pool, pjpidf_note); xml_init_node(pool, note, &NOTE, text); pj_xml_add_node(pres, note); return note; } PJ_DEF(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres) { return pj_xml_find_node( pres, &NOTE); } PJ_DEF(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres *t, pjpidf_note *note) { return pj_xml_find_next_node(t, note, &NOTE); } /* Tuple */ PJ_DEF(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *id) { pj_xml_attr *attr; pjpidf_status *st; xml_init_node(pool, t, &TUPLE, NULL); attr = xml_create_attr(pool, &ID, id); pj_xml_add_attr(t, attr); st = PJ_POOL_ALLOC_T(pool, pjpidf_status); pjpidf_status_construct(pool, st); pj_xml_add_node(t, st); } PJ_DEF(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t) { const pj_xml_attr *attr = pj_xml_find_attr((pj_xml_node*)t, &ID, NULL); pj_assert(attr); return &attr->value; } PJ_DEF(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *id) { pj_xml_attr *attr = pj_xml_find_attr(t, &ID, NULL); pj_assert(attr); pj_strdup(pool, &attr->value, id); } PJ_DEF(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t) { pjpidf_status *st = (pjpidf_status*)pj_xml_find_node(t, &STATUS); pj_assert(st); return st; } PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t) { pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT); if (!node) return &EMPTY_STRING; return &node->content; } PJ_DEF(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *contact) { pj_xml_node *node = pj_xml_find_node(t, &CONTACT); if (!node) { node = PJ_POOL_ALLOC_T(pool, pj_xml_node); xml_init_node(pool, node, &CONTACT, contact); pj_xml_add_node(t, node); } else { pj_strdup(pool, &node->content, contact); } } PJ_DEF(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *prio) { pj_xml_node *node = pj_xml_find_node(t, &CONTACT); pj_xml_attr *attr; if (!node) { node = PJ_POOL_ALLOC_T(pool, pj_xml_node); xml_init_node(pool, node, &CONTACT, NULL); pj_xml_add_node(t, node); } attr = pj_xml_find_attr(node, &PRIORITY, NULL); if (!attr) { attr = xml_create_attr(pool, &PRIORITY, prio); pj_xml_add_attr(node, attr); } else { pj_strdup(pool, &attr->value, prio); } } PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t) { pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT); pj_xml_attr *attr; if (!node) return &EMPTY_STRING; attr = pj_xml_find_attr(node, &PRIORITY, NULL); if (!attr) return &EMPTY_STRING; return &attr->value; } PJ_DEF(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *text) { pjpidf_note *note = PJ_POOL_ALLOC_T(pool, pjpidf_note); xml_init_node(pool, note, &NOTE, text); pj_xml_add_node(t, note); return note; } PJ_DEF(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t) { return pj_xml_find_node(t, &NOTE); } PJ_DEF(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n) { return pj_xml_find_next_node(t, n, &NOTE); } PJ_DEF(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t) { pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &TIMESTAMP); return node ? &node->content : &EMPTY_STRING; } PJ_DEF(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *ts) { pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP); if (!node) { node = PJ_POOL_ALLOC_T(pool, pj_xml_node); xml_init_node(pool, node, &TIMESTAMP, ts); pj_xml_add_node(t, node); } else { pj_strdup(pool, &node->content, ts); } } PJ_DEF(void) pjpidf_tuple_set_timestamp_np(pj_pool_t *pool, pjpidf_tuple *t, pj_str_t *ts) { pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP); if (!node) { node = PJ_POOL_ALLOC_T(pool, pj_xml_node); xml_init_node(pool, node, &TIMESTAMP, ts); } else { node->content = *ts; } } /* Status */ PJ_DEF(void) pjpidf_status_construct(pj_pool_t *pool, pjpidf_status *st) { pj_xml_node *node; xml_init_node(pool, st, &STATUS, NULL); node = PJ_POOL_ALLOC_T(pool, pj_xml_node); xml_init_node(pool, node, &BASIC, &CLOSED); pj_xml_add_node(st, node); } PJ_DEF(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status *st) { pj_xml_node *node = pj_xml_find_node((pj_xml_node*)st, &BASIC); if (!node) return PJ_FALSE; return pj_stricmp(&node->content, &OPEN)==0; } PJ_DEF(void) pjpidf_status_set_basic_open(pjpidf_status *st, pj_bool_t open) { pj_xml_node *node = pj_xml_find_node(st, &BASIC); if (node) node->content = open ? OPEN : CLOSED; } PJ_DEF(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity) { pjpidf_pres *pres = PJ_POOL_ALLOC_T(pool, pjpidf_pres); pjpidf_pres_construct(pool, pres, entity); return pres; } PJ_DEF(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len) { pjpidf_pres *pres = pj_xml_parse(pool, text, len); if (pres && pres->name.slen >= 8) { pj_str_t name; name.ptr = pres->name.ptr + (pres->name.slen - 8); name.slen = 8; if (pj_stricmp(&name, &PRESENCE) == 0) return pres; } return NULL; } PJ_DEF(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len) { return pj_xml_print(pres, buf, len, PJ_TRUE); } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-simple/presence.c ================================================ /* $Id: presence.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "presence.c" #define PRES_DEFAULT_EXPIRES PJSIP_PRES_DEFAULT_EXPIRES #if PJSIP_PRES_BAD_CONTENT_RESPONSE < 200 || \ PJSIP_PRES_BAD_CONTENT_RESPONSE > 699 || \ PJSIP_PRES_BAD_CONTENT_RESPONSE/100 == 3 # error Invalid PJSIP_PRES_BAD_CONTENT_RESPONSE value #endif /* * Presence module (mod-presence) */ static struct pjsip_module mod_presence = { NULL, NULL, /* prev, next. */ { "mod-presence", 12 }, /* Name. */ -1, /* Id */ PJSIP_MOD_PRIORITY_DIALOG_USAGE,/* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ NULL, /* unload() */ NULL, /* on_rx_request() */ NULL, /* on_rx_response() */ NULL, /* on_tx_request. */ NULL, /* on_tx_response() */ NULL, /* on_tsx_state() */ }; /* * Presence message body type. */ typedef enum content_type_e { CONTENT_TYPE_NONE, CONTENT_TYPE_PIDF, CONTENT_TYPE_XPIDF, } content_type_e; /* * This structure describe a presentity, for both subscriber and notifier. */ struct pjsip_pres { pjsip_evsub *sub; /**< Event subscribtion record. */ pjsip_dialog *dlg; /**< The dialog. */ content_type_e content_type; /**< Content-Type. */ pj_pool_t *status_pool; /**< Pool for pres_status */ pjsip_pres_status status; /**< Presence status. */ pj_pool_t *tmp_pool; /**< Pool for tmp_status */ pjsip_pres_status tmp_status; /**< Temp, before NOTIFY is answred.*/ pjsip_evsub_user user_cb; /**< The user callback. */ }; typedef struct pjsip_pres pjsip_pres; /* * Forward decl for evsub callback. */ static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event); static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event); static void pres_on_evsub_rx_refresh( pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body); static void pres_on_evsub_rx_notify( pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body); static void pres_on_evsub_client_refresh(pjsip_evsub *sub); static void pres_on_evsub_server_timeout(pjsip_evsub *sub); /* * Event subscription callback for presence. */ static pjsip_evsub_user pres_user = { &pres_on_evsub_state, &pres_on_evsub_tsx_state, &pres_on_evsub_rx_refresh, &pres_on_evsub_rx_notify, &pres_on_evsub_client_refresh, &pres_on_evsub_server_timeout, }; /* * Some static constants. */ static const pj_str_t STR_EVENT = { "Event", 5 }; static const pj_str_t STR_PRESENCE = { "presence", 8 }; static const pj_str_t STR_APPLICATION = { "application", 11 }; static const pj_str_t STR_PIDF_XML = { "pidf+xml", 8}; static const pj_str_t STR_XPIDF_XML = { "xpidf+xml", 9}; static const pj_str_t STR_APP_PIDF_XML = { "application/pidf+xml", 20 }; static const pj_str_t STR_APP_XPIDF_XML = { "application/xpidf+xml", 21 }; /* * Init presence module. */ PJ_DEF(pj_status_t) pjsip_pres_init_module( pjsip_endpoint *endpt, pjsip_module *mod_evsub) { pj_status_t status; pj_str_t accept[2]; /* Check arguments. */ PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL); /* Must have not been registered */ PJ_ASSERT_RETURN(mod_presence.id == -1, PJ_EINVALIDOP); /* Register to endpoint */ status = pjsip_endpt_register_module(endpt, &mod_presence); if (status != PJ_SUCCESS) return status; accept[0] = STR_APP_PIDF_XML; accept[1] = STR_APP_XPIDF_XML; /* Register event package to event module. */ status = pjsip_evsub_register_pkg( &mod_presence, &STR_PRESENCE, PRES_DEFAULT_EXPIRES, PJ_ARRAY_SIZE(accept), accept); if (status != PJ_SUCCESS) { pjsip_endpt_unregister_module(endpt, &mod_presence); return status; } return PJ_SUCCESS; } /* * Get presence module instance. */ PJ_DEF(pjsip_module*) pjsip_pres_instance(void) { return &mod_presence; } /* * Create client subscription. */ PJ_DEF(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, unsigned options, pjsip_evsub **p_evsub ) { pj_status_t status; pjsip_pres *pres; char obj_name[PJ_MAX_OBJ_NAME]; pjsip_evsub *sub; PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL); pjsip_dlg_inc_lock(dlg); /* Create event subscription */ status = pjsip_evsub_create_uac( dlg, &pres_user, &STR_PRESENCE, options, &sub); if (status != PJ_SUCCESS) goto on_return; /* Create presence */ pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres); pres->dlg = dlg; pres->sub = sub; if (user_cb) pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user)); pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool); pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name, 512, 512, NULL); pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool); pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name, 512, 512, NULL); /* Attach to evsub */ pjsip_evsub_set_mod_data(sub, mod_presence.id, pres); *p_evsub = sub; on_return: pjsip_dlg_dec_lock(dlg); return status; } /* * Create server subscription. */ PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, pjsip_evsub **p_evsub ) { pjsip_accept_hdr *accept; pjsip_event_hdr *event; content_type_e content_type = CONTENT_TYPE_NONE; pjsip_evsub *sub; pjsip_pres *pres; char obj_name[PJ_MAX_OBJ_NAME]; pj_status_t status; /* Check arguments */ PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL); /* Must be request message */ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Check that request is SUBSCRIBE */ PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_subscribe_method)==0, PJSIP_SIMPLE_ENOTSUBSCRIBE); /* Check that Event header contains "presence" */ event = (pjsip_event_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL); if (!event) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); } if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT); } /* Check that request contains compatible Accept header. */ accept = (pjsip_accept_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL); if (accept) { unsigned i; for (i=0; icount; ++i) { if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) { content_type = CONTENT_TYPE_PIDF; break; } else if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) { content_type = CONTENT_TYPE_XPIDF; break; } } if (i==accept->count) { /* Nothing is acceptable */ return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE); } } else { /* No Accept header. * Treat as "application/pidf+xml" */ content_type = CONTENT_TYPE_PIDF; } /* Lock dialog */ pjsip_dlg_inc_lock(dlg); /* Create server subscription */ status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, 0, &sub); if (status != PJ_SUCCESS) goto on_return; /* Create server presence subscription */ pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres); pres->dlg = dlg; pres->sub = sub; pres->content_type = content_type; if (user_cb) pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user)); pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool); pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name, 512, 512, NULL); pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool); pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name, 512, 512, NULL); /* Attach to evsub */ pjsip_evsub_set_mod_data(sub, mod_presence.id, pres); /* Done: */ *p_evsub = sub; on_return: pjsip_dlg_dec_lock(dlg); return status; } /* * Forcefully terminate presence. */ PJ_DEF(pj_status_t) pjsip_pres_terminate( pjsip_evsub *sub, pj_bool_t notify ) { return pjsip_evsub_terminate(sub, notify); } /* * Create SUBSCRIBE */ PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub, pj_int32_t expires, pjsip_tx_data **p_tdata) { return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires, p_tdata); } /* * Add custom headers. */ PJ_DEF(pj_status_t) pjsip_pres_add_header( pjsip_evsub *sub, const pjsip_hdr *hdr_list ) { return pjsip_evsub_add_header( sub, hdr_list ); } /* * Accept incoming subscription. */ PJ_DEF(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub, pjsip_rx_data *rdata, int st_code, const pjsip_hdr *hdr_list ) { return pjsip_evsub_accept( sub, rdata, st_code, hdr_list ); } /* * Get presence status. */ PJ_DEF(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub, pjsip_pres_status *status ) { pjsip_pres *pres; PJ_ASSERT_RETURN(sub && status, PJ_EINVAL); pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE); if (pres->tmp_status._is_valid) { PJ_ASSERT_RETURN(pres->tmp_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE); pj_memcpy(status, &pres->tmp_status, sizeof(pjsip_pres_status)); } else { PJ_ASSERT_RETURN(pres->status_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE); pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status)); } return PJ_SUCCESS; } /* * Set presence status. */ PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub, const pjsip_pres_status *status ) { unsigned i; pj_pool_t *tmp; pjsip_pres *pres; PJ_ASSERT_RETURN(sub && status, PJ_EINVAL); pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE); for (i=0; iinfo_cnt; ++i) { pres->status.info[i].basic_open = status->info[i].basic_open; if (pres->status.info[i].id.slen) { /* Id already set */ } else if (status->info[i].id.slen == 0) { pj_create_unique_string(pres->dlg->pool, &pres->status.info[i].id); } else { pj_strdup(pres->dlg->pool, &pres->status.info[i].id, &status->info[i].id); } pj_strdup(pres->tmp_pool, &pres->status.info[i].contact, &status->info[i].contact); /* Duplicate */ pres->status.info[i].rpid.activity = status->info[i].rpid.activity; pj_strdup(pres->tmp_pool, &pres->status.info[i].rpid.id, &status->info[i].rpid.id); pj_strdup(pres->tmp_pool, &pres->status.info[i].rpid.note, &status->info[i].rpid.note); } pres->status.info_cnt = status->info_cnt; /* Swap pools */ tmp = pres->tmp_pool; pres->tmp_pool = pres->status_pool; pres->status_pool = tmp; pj_pool_reset(pres->tmp_pool); return PJ_SUCCESS; } /* * Create message body. */ static pj_status_t pres_create_msg_body( pjsip_pres *pres, pjsip_tx_data *tdata) { pj_str_t entity; /* Get publisher URI */ entity.ptr = (char*) pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE); entity.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->dlg->local.info->uri, entity.ptr, PJSIP_MAX_URL_SIZE); if (entity.slen < 1) return PJ_ENOMEM; if (pres->content_type == CONTENT_TYPE_PIDF) { return pjsip_pres_create_pidf(tdata->pool, &pres->status, &entity, &tdata->msg->body); } else if (pres->content_type == CONTENT_TYPE_XPIDF) { return pjsip_pres_create_xpidf(tdata->pool, &pres->status, &entity, &tdata->msg->body); } else { return PJSIP_SIMPLE_EBADCONTENT; } } /* * Create NOTIFY */ PJ_DEF(pj_status_t) pjsip_pres_notify( pjsip_evsub *sub, pjsip_evsub_state state, const pj_str_t *state_str, const pj_str_t *reason, pjsip_tx_data **p_tdata) { pjsip_pres *pres; pjsip_tx_data *tdata; pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(sub, PJ_EINVAL); /* Get the presence object. */ pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE); /* Must have at least one presence info, unless state is * PJSIP_EVSUB_STATE_TERMINATED. This could happen if subscription * has not been active (e.g. we're waiting for user authorization) * and remote cancels the subscription. */ PJ_ASSERT_RETURN(state==PJSIP_EVSUB_STATE_TERMINATED || pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO); /* Lock object. */ pjsip_dlg_inc_lock(pres->dlg); /* Create the NOTIFY request. */ status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata); if (status != PJ_SUCCESS) goto on_return; /* Create message body to reflect the presence status. * Only do this if we have presence status info to send (see above). */ if (pres->status.info_cnt > 0) { status = pres_create_msg_body( pres, tdata ); if (status != PJ_SUCCESS) goto on_return; } /* Done. */ *p_tdata = tdata; on_return: pjsip_dlg_dec_lock(pres->dlg); return status; } /* * Create NOTIFY that reflect current state. */ PJ_DEF(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub, pjsip_tx_data **p_tdata ) { pjsip_pres *pres; pjsip_tx_data *tdata; pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(sub, PJ_EINVAL); /* Get the presence object. */ pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE); /* We may not have a presence info yet, e.g. when we receive SUBSCRIBE * to refresh subscription while we're waiting for user authorization. */ //PJ_ASSERT_RETURN(pres->status.info_cnt > 0, // PJSIP_SIMPLE_ENOPRESENCEINFO); /* Lock object. */ pjsip_dlg_inc_lock(pres->dlg); /* Create the NOTIFY request. */ status = pjsip_evsub_current_notify( sub, &tdata); if (status != PJ_SUCCESS) goto on_return; /* Create message body to reflect the presence status. */ if (pres->status.info_cnt > 0) { status = pres_create_msg_body( pres, tdata ); if (status != PJ_SUCCESS) goto on_return; } /* Done. */ *p_tdata = tdata; on_return: pjsip_dlg_dec_lock(pres->dlg); return status; } /* * Send request. */ PJ_DEF(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub, pjsip_tx_data *tdata ) { return pjsip_evsub_send_request(sub, tdata); } /* * This callback is called by event subscription when subscription * state has changed. */ static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event) { pjsip_pres *pres; pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); if (pres->user_cb.on_evsub_state) (*pres->user_cb.on_evsub_state)(sub, event); if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { if (pres->status_pool) { pj_pool_release(pres->status_pool); pres->status_pool = NULL; } if (pres->tmp_pool) { pj_pool_release(pres->tmp_pool); pres->tmp_pool = NULL; } } } /* * Called when transaction state has changed. */ static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) { pjsip_pres *pres; pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); if (pres->user_cb.on_tsx_state) (*pres->user_cb.on_tsx_state)(sub, tsx, event); } /* * Called when SUBSCRIBE is received. */ static void pres_on_evsub_rx_refresh( pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) { pjsip_pres *pres; pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); if (pres->user_cb.on_rx_refresh) { (*pres->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text, res_hdr, p_body); } else { /* Implementors MUST send NOTIFY if it implements on_rx_refresh */ pjsip_tx_data *tdata; pj_str_t timeout = { "timeout", 7}; pj_status_t status; if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) { status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, &timeout, &tdata); } else { status = pjsip_pres_current_notify(sub, &tdata); } if (status == PJ_SUCCESS) pjsip_pres_send_request(sub, tdata); } } /* * Process the content of incoming NOTIFY request and update temporary * status. * * return PJ_SUCCESS if incoming request is acceptable. If return value * is not PJ_SUCCESS, res_hdr may be added with Warning header. */ static pj_status_t pres_process_rx_notify( pjsip_pres *pres, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr) { const pj_str_t STR_MULTIPART = { "multipart", 9 }; pjsip_ctype_hdr *ctype_hdr; pj_status_t status = PJ_SUCCESS; *p_st_text = NULL; /* Check Content-Type and msg body are present. */ ctype_hdr = rdata->msg_info.ctype; if (ctype_hdr==NULL || rdata->msg_info.msg->body==NULL) { pjsip_warning_hdr *warn_hdr; pj_str_t warn_text; *p_st_code = PJSIP_SC_BAD_REQUEST; warn_text = pj_str("Message body is not present"); warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399, pjsip_endpt_name(pres->dlg->endpt), &warn_text); pj_list_push_back(res_hdr, warn_hdr); return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); } /* Parse content. */ if (pj_stricmp(&ctype_hdr->media.type, &STR_MULTIPART)==0) { pjsip_multipart_part *mpart; pjsip_media_type ctype; pjsip_media_type_init(&ctype, (pj_str_t*)&STR_APPLICATION, (pj_str_t*)&STR_PIDF_XML); mpart = pjsip_multipart_find_part(rdata->msg_info.msg->body, &ctype, NULL); if (mpart) { status = pjsip_pres_parse_pidf2((char*)mpart->body->data, mpart->body->len, pres->tmp_pool, &pres->tmp_status); } if (mpart==NULL) { pjsip_media_type_init(&ctype, (pj_str_t*)&STR_APPLICATION, (pj_str_t*)&STR_XPIDF_XML); mpart = pjsip_multipart_find_part(rdata->msg_info.msg->body, &ctype, NULL); if (mpart) { status = pjsip_pres_parse_xpidf2((char*)mpart->body->data, mpart->body->len, pres->tmp_pool, &pres->tmp_status); } else { status = PJSIP_SIMPLE_EBADCONTENT; } } } else if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 && pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0) { status = pjsip_pres_parse_pidf( rdata, pres->tmp_pool, &pres->tmp_status); } else if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 && pj_stricmp(&ctype_hdr->media.subtype, &STR_XPIDF_XML)==0) { status = pjsip_pres_parse_xpidf( rdata, pres->tmp_pool, &pres->tmp_status); } else { status = PJSIP_SIMPLE_EBADCONTENT; } if (status != PJ_SUCCESS) { /* Unsupported or bad Content-Type */ if (PJSIP_PRES_BAD_CONTENT_RESPONSE >= 300) { pjsip_accept_hdr *accept_hdr; pjsip_warning_hdr *warn_hdr; *p_st_code = PJSIP_PRES_BAD_CONTENT_RESPONSE; /* Add Accept header */ accept_hdr = pjsip_accept_hdr_create(rdata->tp_info.pool); accept_hdr->values[accept_hdr->count++] = STR_APP_PIDF_XML; accept_hdr->values[accept_hdr->count++] = STR_APP_XPIDF_XML; pj_list_push_back(res_hdr, accept_hdr); /* Add Warning header */ warn_hdr = pjsip_warning_hdr_create_from_status( rdata->tp_info.pool, pjsip_endpt_name(pres->dlg->endpt), status); pj_list_push_back(res_hdr, warn_hdr); return status; } else { pj_assert(PJSIP_PRES_BAD_CONTENT_RESPONSE/100 == 2); PJ_PERROR(4,(THIS_FILE, status, "Ignoring presence error due to " "PJSIP_PRES_BAD_CONTENT_RESPONSE setting [%d]", PJSIP_PRES_BAD_CONTENT_RESPONSE)); *p_st_code = PJSIP_PRES_BAD_CONTENT_RESPONSE; status = PJ_SUCCESS; } } /* If application calls pres_get_status(), redirect the call to * retrieve the temporary status. */ pres->tmp_status._is_valid = PJ_TRUE; return PJ_SUCCESS; } /* * Called when NOTIFY is received. */ static void pres_on_evsub_rx_notify( pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) { pjsip_pres *pres; pj_status_t status; pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); if (rdata->msg_info.msg->body) { status = pres_process_rx_notify( pres, rdata, p_st_code, p_st_text, res_hdr ); if (status != PJ_SUCCESS) return; } else { #if 1 /* This is the newest change, http://trac.pjsip.org/repos/ticket/873 * Some app want to be notified about the empty NOTIFY, e.g. to * decide whether it should consider the buddy as offline. * In this case, leave the buddy state unchanged, but set the * "tuple_node" in pjsip_pres_status to NULL. */ unsigned i; for (i=0; istatus.info_cnt; ++i) { pres->status.info[i].tuple_node = NULL; } #elif 0 /* This has just been changed. Previously, we treat incoming NOTIFY * with no message body as having the presence subscription closed. * Now we treat it as no change in presence status (ref: EyeBeam). */ *p_st_code = 200; return; #else unsigned i; /* Subscription is terminated. Consider contact is offline */ pres->tmp_status._is_valid = PJ_TRUE; for (i=0; itmp_status.info_cnt; ++i) pres->tmp_status.info[i].basic_open = PJ_FALSE; #endif } /* Notify application. */ if (pres->user_cb.on_rx_notify) { (*pres->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text, res_hdr, p_body); } /* If application responded NOTIFY with 2xx, copy temporary status * to main status, and mark the temporary status as invalid. */ if ((*p_st_code)/100 == 2) { pj_pool_t *tmp; pj_memcpy(&pres->status, &pres->tmp_status, sizeof(pjsip_pres_status)); /* Swap the pool */ tmp = pres->tmp_pool; pres->tmp_pool = pres->status_pool; pres->status_pool = tmp; } pres->tmp_status._is_valid = PJ_FALSE; pj_pool_reset(pres->tmp_pool); /* Done */ } /* * Called when it's time to send SUBSCRIBE. */ static void pres_on_evsub_client_refresh(pjsip_evsub *sub) { pjsip_pres *pres; pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); if (pres->user_cb.on_client_refresh) { (*pres->user_cb.on_client_refresh)(sub); } else { pj_status_t status; pjsip_tx_data *tdata; status = pjsip_pres_initiate(sub, -1, &tdata); if (status == PJ_SUCCESS) pjsip_pres_send_request(sub, tdata); } } /* * Called when no refresh is received after the interval. */ static void pres_on_evsub_server_timeout(pjsip_evsub *sub) { pjsip_pres *pres; pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id); PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); if (pres->user_cb.on_server_timeout) { (*pres->user_cb.on_server_timeout)(sub); } else { pj_status_t status; pjsip_tx_data *tdata; pj_str_t reason = { "timeout", 7 }; status = pjsip_pres_notify(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, &reason, &tdata); if (status == PJ_SUCCESS) pjsip_pres_send_request(sub, tdata); } } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-simple/presence_body.c ================================================ /* $Id: presence_body.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #define THIS_FILE "presence_body.c" static const pj_str_t STR_APPLICATION = { "application", 11 }; static const pj_str_t STR_PIDF_XML = { "pidf+xml", 8 }; static const pj_str_t STR_XPIDF_XML = { "xpidf+xml", 9 }; /* * Function to print XML message body. */ static int pres_print_body(struct pjsip_msg_body *msg_body, char *buf, pj_size_t size) { return pj_xml_print((const pj_xml_node*)msg_body->data, buf, size, PJ_TRUE); } /* * Function to clone XML document. */ static void* xml_clone_data(pj_pool_t *pool, const void *data, unsigned len) { PJ_UNUSED_ARG(len); return pj_xml_clone( pool, (const pj_xml_node*) data); } /* * This is a utility function to create PIDF message body from PJSIP * presence status (pjsip_pres_status). */ PJ_DEF(pj_status_t) pjsip_pres_create_pidf( pj_pool_t *pool, const pjsip_pres_status *status, const pj_str_t *entity, pjsip_msg_body **p_body ) { pjpidf_pres *pidf; pjsip_msg_body *body; unsigned i; /* Create . */ pidf = pjpidf_create(pool, entity); /* Create */ for (i=0; iinfo_cnt; ++i) { pjpidf_tuple *pidf_tuple; pjpidf_status *pidf_status; pj_str_t id; /* Add tuple id. */ if (status->info[i].id.slen == 0) { /* xs:ID must start with letter */ //pj_create_unique_string(pool, &id); id.ptr = (char*)pj_pool_alloc(pool, PJ_GUID_STRING_LENGTH+2); id.ptr += 2; pj_generate_unique_string(&id); id.ptr -= 2; id.ptr[0] = 'p'; id.ptr[1] = 'j'; id.slen += 2; } else { id = status->info[i].id; } pidf_tuple = pjpidf_pres_add_tuple(pool, pidf, &id); /* Set */ if (status->info[i].contact.slen) pjpidf_tuple_set_contact(pool, pidf_tuple, &status->info[i].contact); /* Set basic status */ pidf_status = pjpidf_tuple_get_status(pidf_tuple); pjpidf_status_set_basic_open(pidf_status, status->info[i].basic_open); /* Add if configured */ #if defined(PJSIP_PRES_PIDF_ADD_TIMESTAMP) && PJSIP_PRES_PIDF_ADD_TIMESTAMP if (PJSIP_PRES_PIDF_ADD_TIMESTAMP) { char buf[50]; int tslen = 0; pj_time_val tv; pj_parsed_time pt; pj_gettimeofday(&tv); /* TODO: convert time to GMT! (unsupported by pjlib) */ pj_time_decode( &tv, &pt); tslen = pj_ansi_snprintf(buf, sizeof(buf), "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", pt.year, pt.mon+1, pt.day, pt.hour, pt.min, pt.sec, pt.msec); if (tslen > 0 && tslen < (int)sizeof(buf)) { pj_str_t time = pj_str(buf); pjpidf_tuple_set_timestamp(pool, pidf_tuple, &time); } } #endif } /* Create (RPID) */ if (status->info_cnt) { pjrpid_add_element(pidf, pool, 0, &status->info[0].rpid); } body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body); body->data = pidf; body->content_type.type = STR_APPLICATION; body->content_type.subtype = STR_PIDF_XML; body->print_body = &pres_print_body; body->clone_data = &xml_clone_data; *p_body = body; return PJ_SUCCESS; } /* * This is a utility function to create X-PIDF message body from PJSIP * presence status (pjsip_pres_status). */ PJ_DEF(pj_status_t) pjsip_pres_create_xpidf( pj_pool_t *pool, const pjsip_pres_status *status, const pj_str_t *entity, pjsip_msg_body **p_body ) { /* Note: PJSIP implementation of XPIDF is not complete! */ pjxpidf_pres *xpidf; pjsip_msg_body *body; PJ_LOG(4,(THIS_FILE, "Warning: XPIDF format is not fully supported " "by PJSIP")); /* Create XPIDF document. */ xpidf = pjxpidf_create(pool, entity); /* Set basic status. */ if (status->info_cnt > 0) pjxpidf_set_status( xpidf, status->info[0].basic_open); else pjxpidf_set_status( xpidf, PJ_FALSE); body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body); body->data = xpidf; body->content_type.type = STR_APPLICATION; body->content_type.subtype = STR_XPIDF_XML; body->print_body = &pres_print_body; body->clone_data = &xml_clone_data; *p_body = body; return PJ_SUCCESS; } /* * This is a utility function to parse PIDF body into PJSIP presence status. */ PJ_DEF(pj_status_t) pjsip_pres_parse_pidf( pjsip_rx_data *rdata, pj_pool_t *pool, pjsip_pres_status *pres_status) { return pjsip_pres_parse_pidf2((char*)rdata->msg_info.msg->body->data, rdata->msg_info.msg->body->len, pool, pres_status); } PJ_DEF(pj_status_t) pjsip_pres_parse_pidf2(char *body, unsigned body_len, pj_pool_t *pool, pjsip_pres_status *pres_status) { pjpidf_pres *pidf; pjpidf_tuple *pidf_tuple; pidf = pjpidf_parse(pool, body, body_len); if (pidf == NULL) return PJSIP_SIMPLE_EBADPIDF; pres_status->info_cnt = 0; pidf_tuple = pjpidf_pres_get_first_tuple(pidf); while (pidf_tuple && pres_status->info_cnt < PJSIP_PRES_STATUS_MAX_INFO) { pjpidf_status *pidf_status; pres_status->info[pres_status->info_cnt].tuple_node = pj_xml_clone(pool, pidf_tuple); pj_strdup(pool, &pres_status->info[pres_status->info_cnt].id, pjpidf_tuple_get_id(pidf_tuple)); pj_strdup(pool, &pres_status->info[pres_status->info_cnt].contact, pjpidf_tuple_get_contact(pidf_tuple)); pidf_status = pjpidf_tuple_get_status(pidf_tuple); if (pidf_status) { pres_status->info[pres_status->info_cnt].basic_open = pjpidf_status_is_basic_open(pidf_status); } else { pres_status->info[pres_status->info_cnt].basic_open = PJ_FALSE; } pidf_tuple = pjpidf_pres_get_next_tuple( pidf, pidf_tuple ); pres_status->info_cnt++; } /* Parse (RPID) */ pjrpid_get_element(pidf, pool, &pres_status->info[0].rpid); return PJ_SUCCESS; } /* * This is a utility function to parse X-PIDF body into PJSIP presence status. */ PJ_DEF(pj_status_t) pjsip_pres_parse_xpidf(pjsip_rx_data *rdata, pj_pool_t *pool, pjsip_pres_status *pres_status) { return pjsip_pres_parse_xpidf2((char*)rdata->msg_info.msg->body->data, rdata->msg_info.msg->body->len, pool, pres_status); } PJ_DEF(pj_status_t) pjsip_pres_parse_xpidf2(char *body, unsigned body_len, pj_pool_t *pool, pjsip_pres_status *pres_status) { pjxpidf_pres *xpidf; xpidf = pjxpidf_parse(pool, body, body_len); if (xpidf == NULL) return PJSIP_SIMPLE_EBADXPIDF; pres_status->info_cnt = 1; pj_strdup(pool, &pres_status->info[0].contact, pjxpidf_get_uri(xpidf)); pres_status->info[0].basic_open = pjxpidf_get_status(xpidf); pres_status->info[0].id.slen = 0; pres_status->info[0].tuple_node = NULL; return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-simple/publishc.c ================================================ /* $Id: publishc.c 4530 2013-05-30 09:27:49Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define REFRESH_TIMER 1 #define DELAY_BEFORE_REFRESH PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH #define THIS_FILE "publishc.c" /* Let's define this enum, so that it'll trigger compilation error * when somebody define the same enum in sip_msg.h */ enum { PJSIP_PUBLISH_METHOD = PJSIP_OTHER_METHOD, }; const pjsip_method pjsip_publish_method = { (pjsip_method_e)PJSIP_PUBLISH_METHOD, { "PUBLISH", 7 } }; /** * Pending request list. */ typedef struct pending_publish { PJ_DECL_LIST_MEMBER(struct pending_publish); pjsip_tx_data *tdata; } pending_publish; /** * SIP client publication structure. */ struct pjsip_publishc { pj_pool_t *pool; pjsip_endpoint *endpt; pj_bool_t _delete_flag; int pending_tsx; pj_bool_t in_callback; pj_mutex_t *mutex; pjsip_publishc_opt opt; void *token; pjsip_publishc_cb *cb; pj_str_t event; pj_str_t str_target_uri; pjsip_uri *target_uri; pjsip_cid_hdr *cid_hdr; pjsip_cseq_hdr *cseq_hdr; pj_str_t from_uri; pjsip_from_hdr *from_hdr; pjsip_to_hdr *to_hdr; pj_str_t etag; pjsip_expires_hdr *expires_hdr; pj_uint32_t expires; pjsip_route_hdr route_set; pjsip_hdr usr_hdr; pjsip_host_port via_addr; const void *via_tp; /* Authorization sessions. */ pjsip_auth_clt_sess auth_sess; /* Auto refresh publication. */ pj_bool_t auto_refresh; pj_time_val last_refresh; pj_time_val next_refresh; pj_timer_entry timer; /* Pending PUBLISH request */ pending_publish pending_reqs; pending_publish pending_reqs_empty; }; PJ_DEF(void) pjsip_publishc_opt_default(pjsip_publishc_opt *opt) { pj_bzero(opt, sizeof(*opt)); opt->queue_request = PJSIP_PUBLISHC_QUEUE_REQUEST; } /* * Initialize client publication module. */ PJ_DEF(pj_status_t) pjsip_publishc_init_module(pjsip_endpoint *endpt) { /* Note: Commented out the capability registration below, since it's wrong to include PUBLISH in Allow header of INVITE requests/ responses. 13.2.1 Creating the Initial INVITE An Allow header field (Section 20.5) SHOULD be present in the INVITE. It indicates what methods can be invoked within a dialog 20.5 Allow The Allow header field lists the set of methods supported by the UA generating the message. While the semantic of Allow header in non-dialog requests is unclear, it's probably best not to include PUBLISH in Allow header for now until we can find out how to customize the inclusion of methods in Allow header for in-dialog vs out-dialog requests. return pjsip_endpt_add_capability( endpt, NULL, PJSIP_H_ALLOW, NULL, 1, &pjsip_publish_method.name); */ PJ_UNUSED_ARG(endpt); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_publishc_create( pjsip_endpoint *endpt, const pjsip_publishc_opt *opt, void *token, pjsip_publishc_cb *cb, pjsip_publishc **p_pubc) { pj_pool_t *pool; pjsip_publishc *pubc; pjsip_publishc_opt default_opt; pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(endpt && cb && p_pubc, PJ_EINVAL); pool = pjsip_endpt_create_pool(endpt, "pubc%p", 1024, 1024); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); pubc = PJ_POOL_ZALLOC_T(pool, pjsip_publishc); pubc->pool = pool; pubc->endpt = endpt; pubc->token = token; pubc->cb = cb; pubc->expires = PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED; if (!opt) { pjsip_publishc_opt_default(&default_opt); opt = &default_opt; } pj_memcpy(&pubc->opt, opt, sizeof(*opt)); pj_list_init(&pubc->pending_reqs); pj_list_init(&pubc->pending_reqs_empty); status = pj_mutex_create_recursive(pubc->pool, "pubc%p", &pubc->mutex); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } status = pjsip_auth_clt_init(&pubc->auth_sess, endpt, pubc->pool, 0); if (status != PJ_SUCCESS) { pj_mutex_destroy(pubc->mutex); pj_pool_release(pool); return status; } pj_list_init(&pubc->route_set); pj_list_init(&pubc->usr_hdr); /* Done */ *p_pubc = pubc; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_publishc_destroy(pjsip_publishc *pubc) { PJ_ASSERT_RETURN(pubc, PJ_EINVAL); if (pubc->pending_tsx || pubc->in_callback) { pubc->_delete_flag = 1; pubc->cb = NULL; } else { /* Cancel existing timer, if any */ if (pubc->timer.id != 0) { pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer); pubc->timer.id = 0; } if (pubc->mutex) pj_mutex_destroy(pubc->mutex); pjsip_endpt_release_pool(pubc->endpt, pubc->pool); } return PJ_SUCCESS; } PJ_DEF(pj_pool_t*) pjsip_publishc_get_pool(pjsip_publishc *pubc) { return pubc->pool; } static void set_expires( pjsip_publishc *pubc, pj_uint32_t expires) { if (expires != pubc->expires && expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED) { pubc->expires_hdr = pjsip_expires_hdr_create(pubc->pool, expires); } else { pubc->expires_hdr = NULL; } } PJ_DEF(pj_status_t) pjsip_publishc_init(pjsip_publishc *pubc, const pj_str_t *event, const pj_str_t *target_uri, const pj_str_t *from_uri, const pj_str_t *to_uri, pj_uint32_t expires) { pj_str_t tmp; PJ_ASSERT_RETURN(pubc && event && target_uri && from_uri && to_uri && expires, PJ_EINVAL); /* Copy event type */ pj_strdup_with_null(pubc->pool, &pubc->event, event); /* Copy server URL. */ pj_strdup_with_null(pubc->pool, &pubc->str_target_uri, target_uri); /* Set server URL. */ tmp = pubc->str_target_uri; pubc->target_uri = pjsip_parse_uri( pubc->pool, tmp.ptr, tmp.slen, 0); if (pubc->target_uri == NULL) { return PJSIP_EINVALIDURI; } /* Set "From" header. */ pj_strdup_with_null(pubc->pool, &pubc->from_uri, from_uri); tmp = pubc->from_uri; pubc->from_hdr = pjsip_from_hdr_create(pubc->pool); pubc->from_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); if (!pubc->from_hdr->uri) { return PJSIP_EINVALIDURI; } /* Set "To" header. */ pj_strdup_with_null(pubc->pool, &tmp, to_uri); pubc->to_hdr = pjsip_to_hdr_create(pubc->pool); pubc->to_hdr->uri = pjsip_parse_uri(pubc->pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); if (!pubc->to_hdr->uri) { return PJSIP_EINVALIDURI; } /* Set "Expires" header, if required. */ set_expires( pubc, expires); /* Set "Call-ID" header. */ pubc->cid_hdr = pjsip_cid_hdr_create(pubc->pool); pj_create_unique_string(pubc->pool, &pubc->cid_hdr->id); /* Set "CSeq" header. */ pubc->cseq_hdr = pjsip_cseq_hdr_create(pubc->pool); pubc->cseq_hdr->cseq = pj_rand() % 0xFFFF; pjsip_method_set( &pubc->cseq_hdr->method, PJSIP_REGISTER_METHOD); /* Done. */ return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_publishc_set_credentials( pjsip_publishc *pubc, int count, const pjsip_cred_info cred[] ) { PJ_ASSERT_RETURN(pubc && count && cred, PJ_EINVAL); return pjsip_auth_clt_set_credentials(&pubc->auth_sess, count, cred); } PJ_DEF(pj_status_t) pjsip_publishc_set_route_set( pjsip_publishc *pubc, const pjsip_route_hdr *route_set) { const pjsip_route_hdr *chdr; PJ_ASSERT_RETURN(pubc && route_set, PJ_EINVAL); pj_list_init(&pubc->route_set); chdr = route_set->next; while (chdr != route_set) { pj_list_push_back(&pubc->route_set, pjsip_hdr_clone(pubc->pool, chdr)); chdr = chdr->next; } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_publishc_set_headers( pjsip_publishc *pubc, const pjsip_hdr *hdr_list) { const pjsip_hdr *h; PJ_ASSERT_RETURN(pubc && hdr_list, PJ_EINVAL); pj_list_init(&pubc->usr_hdr); h = hdr_list->next; while (h != hdr_list) { pj_list_push_back(&pubc->usr_hdr, pjsip_hdr_clone(pubc->pool, h)); h = h->next; } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_publishc_set_via_sent_by(pjsip_publishc *pubc, pjsip_host_port *via_addr, pjsip_transport *via_tp) { PJ_ASSERT_RETURN(pubc, PJ_EINVAL); if (!via_addr) pj_bzero(&pubc->via_addr, sizeof(pubc->via_addr)); else { if (pj_strcmp(&pubc->via_addr.host, &via_addr->host)) pj_strdup(pubc->pool, &pubc->via_addr.host, &via_addr->host); pubc->via_addr.port = via_addr->port; } pubc->via_tp = via_tp; return PJ_SUCCESS; } static pj_status_t create_request(pjsip_publishc *pubc, pjsip_tx_data **p_tdata) { const pj_str_t STR_EVENT = { "Event", 5 }; pj_status_t status; pjsip_generic_string_hdr *hdr; pjsip_tx_data *tdata; PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL); /* Create the request. */ status = pjsip_endpt_create_request_from_hdr( pubc->endpt, &pjsip_publish_method, pubc->target_uri, pubc->from_hdr, pubc->to_hdr, NULL, pubc->cid_hdr, pubc->cseq_hdr->cseq, NULL, &tdata); if (status != PJ_SUCCESS) return status; /* Add cached authorization headers. */ pjsip_auth_clt_init_req( &pubc->auth_sess, tdata ); /* Add Route headers from route set, ideally after Via header */ if (!pj_list_empty(&pubc->route_set)) { pjsip_hdr *route_pos; const pjsip_route_hdr *route; route_pos = (pjsip_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); if (!route_pos) route_pos = &tdata->msg->hdr; route = pubc->route_set.next; while (route != &pubc->route_set) { pjsip_hdr *new_hdr = (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, route); pj_list_insert_after(route_pos, new_hdr); route_pos = new_hdr; route = route->next; } } /* Add Event header */ hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_EVENT, &pubc->event); if (hdr) pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); /* Add SIP-If-Match if we have etag */ if (pubc->etag.slen) { const pj_str_t STR_HNAME = { "SIP-If-Match", 12 }; hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_HNAME, &pubc->etag); if (hdr) pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); } /* Add user headers */ if (!pj_list_empty(&pubc->usr_hdr)) { const pjsip_hdr *uhdr; uhdr = pubc->usr_hdr.next; while (uhdr != &pubc->usr_hdr) { pjsip_hdr *new_hdr = (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, uhdr); pjsip_msg_add_hdr(tdata->msg, new_hdr); uhdr = uhdr->next; } } /* Done. */ *p_tdata = tdata; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_publishc_publish(pjsip_publishc *pubc, pj_bool_t auto_refresh, pjsip_tx_data **p_tdata) { pj_status_t status; pjsip_tx_data *tdata; PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL); status = create_request(pubc, &tdata); if (status != PJ_SUCCESS) return status; /* Add Expires header */ if (pubc->expires_hdr) { pjsip_hdr *dup; dup = (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, pubc->expires_hdr); if (dup) pjsip_msg_add_hdr(tdata->msg, dup); } /* Cancel existing timer */ if (pubc->timer.id != 0) { pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer); pubc->timer.id = 0; } pubc->auto_refresh = auto_refresh; /* Done */ *p_tdata = tdata; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_publishc_unpublish(pjsip_publishc *pubc, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pjsip_msg *msg; pjsip_expires_hdr *expires; pj_status_t status; PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL); if (pubc->timer.id != 0) { pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer); pubc->timer.id = 0; } status = create_request(pubc, &tdata); if (status != PJ_SUCCESS) return status; msg = tdata->msg; /* Add Expires:0 header */ expires = pjsip_expires_hdr_create(tdata->pool, 0); pjsip_msg_add_hdr( msg, (pjsip_hdr*)expires); *p_tdata = tdata; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_publishc_update_expires( pjsip_publishc *pubc, pj_uint32_t expires ) { PJ_ASSERT_RETURN(pubc, PJ_EINVAL); set_expires( pubc, expires ); return PJ_SUCCESS; } static void call_callback(pjsip_publishc *pubc, pj_status_t status, int st_code, const pj_str_t *reason, pjsip_rx_data *rdata, pj_int32_t expiration) { struct pjsip_publishc_cbparam cbparam; cbparam.pubc = pubc; cbparam.token = pubc->token; cbparam.status = status; cbparam.code = st_code; cbparam.reason = *reason; cbparam.rdata = rdata; cbparam.expiration = expiration; (*pubc->cb)(&cbparam); } static void pubc_refresh_timer_cb( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { pjsip_publishc *pubc = (pjsip_publishc*) entry->user_data; pjsip_tx_data *tdata; pj_status_t status; PJ_UNUSED_ARG(timer_heap); entry->id = 0; status = pjsip_publishc_publish(pubc, 1, &tdata); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg)); call_callback(pubc, status, 400, &reason, NULL, -1); return; } status = pjsip_publishc_send(pubc, tdata); /* No need to call callback as it should have been called */ } static void tsx_callback(void *token, pjsip_event *event) { pj_status_t status; pjsip_publishc *pubc = (pjsip_publishc*) token; pjsip_transaction *tsx = event->body.tsx_state.tsx; /* Decrement pending transaction counter. */ pj_assert(pubc->pending_tsx > 0); --pubc->pending_tsx; /* Mark that we're in callback to prevent deletion (#1164) */ ++pubc->in_callback; /* If publication data has been deleted by user then remove publication * data from transaction's callback, and don't call callback. */ if (pubc->_delete_flag) { /* Nothing to do */ ; } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED || tsx->status_code == PJSIP_SC_UNAUTHORIZED) { pjsip_rx_data *rdata = event->body.tsx_state.src.rdata; pjsip_tx_data *tdata; status = pjsip_auth_clt_reinit_req( &pubc->auth_sess, rdata, tsx->last_tx, &tdata); if (status != PJ_SUCCESS) { call_callback(pubc, status, tsx->status_code, &rdata->msg_info.msg->line.status.reason, rdata, -1); } else { status = pjsip_publishc_send(pubc, tdata); } } else { pjsip_rx_data *rdata; pj_int32_t expiration = 0xFFFF; if (tsx->status_code/100 == 2) { pjsip_msg *msg; pjsip_expires_hdr *expires; pjsip_generic_string_hdr *etag_hdr; const pj_str_t STR_ETAG = { "SIP-ETag", 8 }; rdata = event->body.tsx_state.src.rdata; msg = rdata->msg_info.msg; /* Save ETag value */ etag_hdr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(msg, &STR_ETAG, NULL); if (etag_hdr) { pj_strdup(pubc->pool, &pubc->etag, &etag_hdr->hvalue); } else { pubc->etag.slen = 0; } /* Update expires value */ expires = (pjsip_expires_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); if (pubc->auto_refresh && expires) expiration = expires->ivalue; if (pubc->auto_refresh && expiration!=0 && expiration!=0xFFFF) { pj_time_val delay = { 0, 0}; /* Cancel existing timer, if any */ if (pubc->timer.id != 0) { pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer); pubc->timer.id = 0; } delay.sec = expiration - DELAY_BEFORE_REFRESH; if (pubc->expires != PJSIP_PUBC_EXPIRATION_NOT_SPECIFIED && delay.sec > (pj_int32_t)pubc->expires) { delay.sec = pubc->expires; } if (delay.sec < DELAY_BEFORE_REFRESH) delay.sec = DELAY_BEFORE_REFRESH; pubc->timer.cb = &pubc_refresh_timer_cb; pubc->timer.id = REFRESH_TIMER; pubc->timer.user_data = pubc; pjsip_endpt_schedule_timer( pubc->endpt, &pubc->timer, &delay); pj_gettimeofday(&pubc->last_refresh); pubc->next_refresh = pubc->last_refresh; pubc->next_refresh.sec += delay.sec; } } else { rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? event->body.tsx_state.src.rdata : NULL; } /* Call callback. */ if (expiration == 0xFFFF) expiration = -1; /* Temporarily increment pending_tsx to prevent callback from * destroying pubc. */ ++pubc->pending_tsx; call_callback(pubc, PJ_SUCCESS, tsx->status_code, (rdata ? &rdata->msg_info.msg->line.status.reason : pjsip_get_status_text(tsx->status_code)), rdata, expiration); --pubc->pending_tsx; /* If we have pending request(s), send them now */ pj_mutex_lock(pubc->mutex); while (!pj_list_empty(&pubc->pending_reqs)) { pending_publish *pp = pubc->pending_reqs.next; pjsip_tx_data *tdata = pp->tdata; /* Remove the request from pending request list, * and keep the unused entry into pending_reqs_empty pool. */ pj_list_erase(pp); pj_list_push_back(&pubc->pending_reqs_empty, pp); /* Add SIP-If-Match if we have etag and the request doesn't have * one (http://trac.pjsip.org/repos/ticket/996) */ if (pubc->etag.slen) { const pj_str_t STR_HNAME = { "SIP-If-Match", 12 }; pjsip_generic_string_hdr *sim_hdr; sim_hdr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(tdata->msg, &STR_HNAME, NULL); if (!sim_hdr) { /* Create the header */ sim_hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_HNAME, &pubc->etag); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sim_hdr); } else { /* Update */ if (pj_strcmp(&pubc->etag, &sim_hdr->hvalue)) pj_strdup(tdata->pool, &sim_hdr->hvalue, &pubc->etag); } } status = pjsip_publishc_send(pubc, tdata); if (status == PJ_EPENDING) { pj_assert(!"Not expected"); pjsip_tx_data_dec_ref(tdata); } else if (status == PJ_SUCCESS) { break; } } pj_mutex_unlock(pubc->mutex); } /* No longer in callback. */ --pubc->in_callback; /* Delete the record if user destroy pubc during the callback. */ if (pubc->_delete_flag && pubc->pending_tsx==0) { pjsip_publishc_destroy(pubc); } } PJ_DEF(pj_status_t) pjsip_publishc_send(pjsip_publishc *pubc, pjsip_tx_data *tdata) { pj_status_t status; pjsip_cseq_hdr *cseq_hdr; pj_uint32_t cseq; PJ_ASSERT_RETURN(pubc && tdata, PJ_EINVAL); /* Make sure we don't have pending transaction. */ pj_mutex_lock(pubc->mutex); if (pubc->pending_tsx) { if (pubc->opt.queue_request) { pending_publish *pp = NULL; if (pj_list_empty(&pubc->pending_reqs_empty)) { pp = PJ_POOL_ZALLOC_T(pubc->pool, pending_publish); } else { pp = pubc->pending_reqs_empty.next; pj_list_erase(pp); } pp->tdata = tdata; pj_list_push_back(&pubc->pending_reqs, pp); pj_mutex_unlock(pubc->mutex); PJ_LOG(4,(THIS_FILE, "Request is queued, pubc has another " "transaction pending")); return PJ_EPENDING; } else { pjsip_tx_data_dec_ref(tdata); pj_mutex_unlock(pubc->mutex); PJ_LOG(4,(THIS_FILE, "Unable to send request, pubc has another " "transaction pending")); return PJ_EBUSY; } } pj_mutex_unlock(pubc->mutex); /* If via_addr is set, use this address for the Via header. */ if (pubc->via_addr.host.slen > 0) { tdata->via_addr = pubc->via_addr; tdata->via_tp = pubc->via_tp; } /* Invalidate message buffer. */ pjsip_tx_data_invalidate_msg(tdata); /* Increment CSeq */ cseq = ++pubc->cseq_hdr->cseq; cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); cseq_hdr->cseq = cseq; /* Increment pending transaction first, since transaction callback * may be called even before send_request() returns! */ ++pubc->pending_tsx; status = pjsip_endpt_send_request(pubc->endpt, tdata, -1, pubc, &tsx_callback); if (status!=PJ_SUCCESS) { // no need to decrement, callback has been called and it should // already decremented pending_tsx. Decrementing this here may // cause accessing freed memory location. //--pubc->pending_tsx; PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status)); } return status; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-simple/rpid.c ================================================ /* $Id: rpid.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include static const pj_str_t DM_NAME = {"xmlns:dm", 8}; static const pj_str_t DM_VAL = {"urn:ietf:params:xml:ns:pidf:data-model", 38}; static const pj_str_t RPID_NAME = {"xmlns:rpid", 10}; static const pj_str_t RPID_VAL = {"urn:ietf:params:xml:ns:pidf:rpid", 32}; static const pj_str_t DM_NOTE = {"dm:note", 7}; static const pj_str_t DM_PERSON = {"dm:person", 9}; static const pj_str_t ID = {"id", 2}; static const pj_str_t NOTE = {"note", 4}; static const pj_str_t RPID_ACTIVITIES = {"rpid:activities", 15}; static const pj_str_t RPID_AWAY = {"rpid:away", 9}; static const pj_str_t RPID_BUSY = {"rpid:busy", 9}; static const pj_str_t RPID_UNKNOWN = {"rpid:unknown", 12}; /* Duplicate RPID element */ PJ_DEF(void) pjrpid_element_dup(pj_pool_t *pool, pjrpid_element *dst, const pjrpid_element *src) { pj_memcpy(dst, src, sizeof(pjrpid_element)); pj_strdup(pool, &dst->id, &src->id); pj_strdup(pool, &dst->note, &src->note); } /* Update RPID namespaces. */ static void update_namespaces(pjpidf_pres *pres, pj_pool_t *pool) { /* Check if namespace is already present. */ if (pj_xml_find_attr(pres, &DM_NAME, NULL) != NULL) return; pj_xml_add_attr(pres, pj_xml_attr_new(pool, &DM_NAME, &DM_VAL)); pj_xml_add_attr(pres, pj_xml_attr_new(pool, &RPID_NAME, &RPID_VAL)); } /* Comparison function to find node name substring */ static pj_bool_t substring_match(const pj_xml_node *node, const char *part_name, pj_ssize_t part_len) { pj_str_t end_name; if (part_len < 1) part_len = pj_ansi_strlen(part_name); if (node->name.slen < part_len) return PJ_FALSE; end_name.ptr = node->name.ptr + (node->name.slen - part_len); end_name.slen = part_len; return pj_strnicmp2(&end_name, part_name, part_len)==0; } /* Util to find child node with the specified substring */ static pj_xml_node *find_node(const pj_xml_node *parent, const char *part_name) { const pj_xml_node *node = parent->node_head.next, *head = (pj_xml_node*) &parent->node_head; pj_ssize_t part_len = pj_ansi_strlen(part_name); while (node != head) { if (substring_match(node, part_name, part_len)) return (pj_xml_node*) node; node = node->next; } return NULL; } /* * Add RPID element into existing PIDF document. */ PJ_DEF(pj_status_t) pjrpid_add_element(pjpidf_pres *pres, pj_pool_t *pool, unsigned options, const pjrpid_element *elem) { pj_xml_node *nd_person, *nd_activities, *nd_activity, *nd_note; pj_xml_attr *attr; PJ_ASSERT_RETURN(pres && pool && options==0 && elem, PJ_EINVAL); PJ_UNUSED_ARG(options); /* Check if we need to add RPID information into the PIDF document. */ if (elem->id.slen==0 && elem->activity==PJRPID_ACTIVITY_UNKNOWN && elem->note.slen==0) { /* No RPID information to be added. */ return PJ_SUCCESS; } /* Add to */ if (elem->note.slen != 0) { pj_xml_node *nd_tuple; nd_tuple = find_node(pres, "tuple"); if (nd_tuple) { nd_note = pj_xml_node_new(pool, &NOTE); pj_strdup(pool, &nd_note->content, &elem->note); pj_xml_add_node(nd_tuple, nd_note); nd_note = NULL; } } /* Update namespace */ update_namespaces(pres, pool); /* Add */ nd_person = pj_xml_node_new(pool, &DM_PERSON); if (elem->id.slen != 0) { attr = pj_xml_attr_new(pool, &ID, &elem->id); } else { pj_str_t person_id; /* xs:ID must start with letter */ //pj_create_unique_string(pool, &person_id); person_id.ptr = (char*)pj_pool_alloc(pool, PJ_GUID_STRING_LENGTH+2); person_id.ptr += 2; pj_generate_unique_string(&person_id); person_id.ptr -= 2; person_id.ptr[0] = 'p'; person_id.ptr[1] = 'j'; person_id.slen += 2; attr = pj_xml_attr_new(pool, &ID, &person_id); } pj_xml_add_attr(nd_person, attr); pj_xml_add_node(pres, nd_person); /* Add */ nd_activities = pj_xml_node_new(pool, &RPID_ACTIVITIES); pj_xml_add_node(nd_person, nd_activities); /* Add the activity */ switch (elem->activity) { case PJRPID_ACTIVITY_AWAY: nd_activity = pj_xml_node_new(pool, &RPID_AWAY); break; case PJRPID_ACTIVITY_BUSY: nd_activity = pj_xml_node_new(pool, &RPID_BUSY); break; case PJRPID_ACTIVITY_UNKNOWN: default: nd_activity = pj_xml_node_new(pool, &RPID_UNKNOWN); break; } pj_xml_add_node(nd_activities, nd_activity); /* Add custom text if required. */ if (elem->note.slen != 0) { nd_note = pj_xml_node_new(pool, &DM_NOTE); pj_strdup(pool, &nd_note->content, &elem->note); pj_xml_add_node(nd_person, nd_note); } /* Done */ return PJ_SUCCESS; } /* Get element from PIDF element */ static pj_status_t get_tuple_note(const pjpidf_pres *pres, pj_pool_t *pool, pjrpid_element *elem) { const pj_xml_node *nd_tuple, *nd_note; nd_tuple = find_node(pres, "tuple"); if (!nd_tuple) return PJSIP_SIMPLE_EBADRPID; nd_note = find_node(pres, "note"); if (nd_note) { pj_strdup(pool, &elem->note, &nd_note->content); return PJ_SUCCESS; } return PJSIP_SIMPLE_EBADRPID; } /* * Get RPID element from PIDF document, if any. */ PJ_DEF(pj_status_t) pjrpid_get_element(const pjpidf_pres *pres, pj_pool_t *pool, pjrpid_element *elem) { const pj_xml_node *nd_person, *nd_activities, *nd_note = NULL; const pj_xml_attr *attr; /* Reset */ pj_bzero(elem, sizeof(*elem)); elem->activity = PJRPID_ACTIVITY_UNKNOWN; /* Find */ nd_person = find_node(pres, "person"); if (!nd_person) { /* not found, try to get from */ return get_tuple_note(pres, pool, elem); } /* Get element id attribute */ attr = pj_xml_find_attr((pj_xml_node*)nd_person, &ID, NULL); if (attr) pj_strdup(pool, &elem->id, &attr->value); /* Get */ nd_activities = find_node(nd_person, "activities"); if (nd_activities) { const pj_xml_node *nd_activity; /* Try to get from */ nd_note = find_node(nd_activities, "note"); /* Get the activity */ nd_activity = nd_activities->node_head.next; if (nd_activity == nd_note) nd_activity = nd_activity->next; if (nd_activity != (pj_xml_node*) &nd_activities->node_head) { if (substring_match(nd_activity, "busy", -1)) elem->activity = PJRPID_ACTIVITY_BUSY; else if (substring_match(nd_activity, "away", -1)) elem->activity = PJRPID_ACTIVITY_AWAY; else elem->activity = PJRPID_ACTIVITY_UNKNOWN; } } /* If is not found, get from */ if (nd_note == NULL) nd_note = find_node(nd_person, "note"); if (nd_note) { pj_strdup(pool, &elem->note, &nd_note->content); } else { get_tuple_note(pres, pool, elem); } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-simple/xpidf.c ================================================ /* $Id: xpidf.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include static pj_str_t STR_PRESENCE = { "presence", 8 }; static pj_str_t STR_STATUS = { "status", 6 }; static pj_str_t STR_OPEN = { "open", 4 }; static pj_str_t STR_CLOSED = { "closed", 6 }; static pj_str_t STR_URI = { "uri", 3 }; static pj_str_t STR_ATOM = { "atom", 4 }; static pj_str_t STR_ATOMID = { "atomid", 6 }; static pj_str_t STR_ID = { "id", 2 }; static pj_str_t STR_ADDRESS = { "address", 7 }; static pj_str_t STR_SUBSCRIBE_PARAM = { ";method=SUBSCRIBE", 17 }; static pj_str_t STR_PRESENTITY = { "presentity", 10 }; static pj_str_t STR_EMPTY_STRING = { NULL, 0 }; static pj_xml_node* xml_create_node(pj_pool_t *pool, pj_str_t *name, const pj_str_t *value) { pj_xml_node *node; node = PJ_POOL_ALLOC_T(pool, pj_xml_node); pj_list_init(&node->attr_head); pj_list_init(&node->node_head); node->name = *name; if (value) pj_strdup(pool, &node->content, value); else node->content.ptr=NULL, node->content.slen=0; return node; } static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name, const pj_str_t *value) { pj_xml_attr *attr = PJ_POOL_ALLOC_T(pool, pj_xml_attr); attr->name = *name; pj_strdup(pool, &attr->value, value); return attr; } PJ_DEF(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri_cstr) { pjxpidf_pres *pres; pj_xml_node *presentity; pj_xml_node *atom; pj_xml_node *addr; pj_xml_node *status; pj_xml_attr *attr; pj_str_t uri; pj_str_t tmp; /* */ pres = xml_create_node(pool, &STR_PRESENCE, NULL); /* */ presentity = xml_create_node(pool, &STR_PRESENTITY, NULL); pj_xml_add_node(pres, presentity); /* uri attribute */ uri.ptr = (char*) pj_pool_alloc(pool, uri_cstr->slen + STR_SUBSCRIBE_PARAM.slen); pj_strcpy( &uri, uri_cstr); pj_strcat( &uri, &STR_SUBSCRIBE_PARAM); attr = xml_create_attr(pool, &STR_URI, &uri); pj_xml_add_attr(presentity, attr); /* */ atom = xml_create_node(pool, &STR_ATOM, NULL); pj_xml_add_node(pres, atom); /* atom id */ pj_create_unique_string(pool, &tmp); attr = xml_create_attr(pool, &STR_ATOMID, &tmp); pj_xml_add_attr(atom, attr); /* address */ addr = xml_create_node(pool, &STR_ADDRESS, NULL); pj_xml_add_node(atom, addr); /* address'es uri */ attr = xml_create_attr(pool, &STR_URI, uri_cstr); pj_xml_add_attr(addr, attr); /* status */ status = xml_create_node(pool, &STR_STATUS, NULL); pj_xml_add_node(addr, status); /* status attr */ attr = xml_create_attr(pool, &STR_STATUS, &STR_OPEN); pj_xml_add_attr(status, attr); return pres; } PJ_DEF(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len) { pjxpidf_pres *pres; pj_xml_node *node; pres = pj_xml_parse(pool, text, len); if (!pres) return NULL; /* Validate */ if (pj_stricmp(&pres->name, &STR_PRESENCE) != 0) return NULL; /* Validate */ node = pj_xml_find_node(pres, &STR_PRESENTITY); if (node == NULL) return NULL; if (pj_xml_find_attr(node, &STR_URI, NULL) == NULL) return NULL; /* Validate */ node = pj_xml_find_node(pres, &STR_ATOM); if (node == NULL) return NULL; if (pj_xml_find_attr(node, &STR_ATOMID, NULL) == NULL && pj_xml_find_attr(node, &STR_ID, NULL) == NULL) { return NULL; } /* Address */ node = pj_xml_find_node(node, &STR_ADDRESS); if (node == NULL) return NULL; if (pj_xml_find_attr(node, &STR_URI, NULL) == NULL) return NULL; /* Status */ node = pj_xml_find_node(node, &STR_STATUS); if (node == NULL) return NULL; if (pj_xml_find_attr(node, &STR_STATUS, NULL) == NULL) return NULL; return pres; } PJ_DEF(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len) { return pj_xml_print(pres, text, len, PJ_TRUE); } PJ_DEF(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres) { pj_xml_node *presentity; pj_xml_attr *attr; presentity = pj_xml_find_node(pres, &STR_PRESENTITY); if (!presentity) return &STR_EMPTY_STRING; attr = pj_xml_find_attr(presentity, &STR_URI, NULL); if (!attr) return &STR_EMPTY_STRING; return &attr->value; } PJ_DEF(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres, const pj_str_t *uri) { pj_xml_node *presentity; pj_xml_node *atom; pj_xml_node *addr; pj_xml_attr *attr; pj_str_t dup_uri; presentity = pj_xml_find_node(pres, &STR_PRESENTITY); if (!presentity) { pj_assert(0); return -1; } atom = pj_xml_find_node(pres, &STR_ATOM); if (!atom) { pj_assert(0); return -1; } addr = pj_xml_find_node(atom, &STR_ADDRESS); if (!addr) { pj_assert(0); return -1; } /* Set uri in presentity */ attr = pj_xml_find_attr(presentity, &STR_URI, NULL); if (!attr) { pj_assert(0); return -1; } pj_strdup(pool, &dup_uri, uri); attr->value = dup_uri; /* Set uri in address. */ attr = pj_xml_find_attr(addr, &STR_URI, NULL); if (!attr) { pj_assert(0); return -1; } attr->value = dup_uri; return 0; } PJ_DEF(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres) { pj_xml_node *atom; pj_xml_node *addr; pj_xml_node *status; pj_xml_attr *attr; atom = pj_xml_find_node(pres, &STR_ATOM); if (!atom) { pj_assert(0); return PJ_FALSE; } addr = pj_xml_find_node(atom, &STR_ADDRESS); if (!addr) { pj_assert(0); return PJ_FALSE; } status = pj_xml_find_node(addr, &STR_STATUS); if (!status) { pj_assert(0); return PJ_FALSE; } attr = pj_xml_find_attr(status, &STR_STATUS, NULL); if (!attr) { pj_assert(0); return PJ_FALSE; } return pj_stricmp(&attr->value, &STR_OPEN)==0 ? PJ_TRUE : PJ_FALSE; } PJ_DEF(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t online_status) { pj_xml_node *atom; pj_xml_node *addr; pj_xml_node *status; pj_xml_attr *attr; atom = pj_xml_find_node(pres, &STR_ATOM); if (!atom) { pj_assert(0); return -1; } addr = pj_xml_find_node(atom, &STR_ADDRESS); if (!addr) { pj_assert(0); return -1; } status = pj_xml_find_node(addr, &STR_STATUS); if (!status) { pj_assert(0); return -1; } attr = pj_xml_find_attr(status, &STR_STATUS, NULL); if (!attr) { pj_assert(0); return -1; } attr->value = ( online_status ? STR_OPEN : STR_CLOSED ); return 0; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-ua/sip_100rel.c ================================================ /* $Id: sip_100rel.c 4208 2012-07-18 07:52:33Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "sip_100rel.c" /* PRACK method */ PJ_DEF_DATA(const pjsip_method) pjsip_prack_method = { PJSIP_OTHER_METHOD, { "PRACK", 5 } }; typedef struct dlg_data dlg_data; /* * Static prototypes. */ static pj_status_t mod_100rel_load(pjsip_endpoint *endpt); static void on_retransmit(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry); static const pj_str_t tag_100rel = { "100rel", 6 }; static const pj_str_t RSEQ = { "RSeq", 4 }; static const pj_str_t RACK = { "RAck", 4 }; /* 100rel module */ static struct mod_100rel { pjsip_module mod; pjsip_endpoint *endpt; } mod_100rel = { { NULL, NULL, /* prev, next. */ { "mod-100rel", 10 }, /* Name. */ -1, /* Id */ PJSIP_MOD_PRIORITY_DIALOG_USAGE, /* Priority */ &mod_100rel_load, /* load() */ NULL, /* start() */ NULL, /* stop() */ NULL, /* unload() */ NULL, /* on_rx_request() */ NULL, /* on_rx_response() */ NULL, /* on_tx_request. */ NULL, /* on_tx_response() */ NULL, /* on_tsx_state() */ } }; /* List of pending transmission (may include the final response as well) */ typedef struct tx_data_list_t { PJ_DECL_LIST_MEMBER(struct tx_data_list_t); pj_uint32_t rseq; pjsip_tx_data *tdata; } tx_data_list_t; /* Below, UAS and UAC roles are of the INVITE transaction */ /* UAS state. */ typedef struct uas_state_t { pj_int32_t cseq; pj_uint32_t rseq; /* Initialized to -1 */ tx_data_list_t tx_data_list; unsigned retransmit_count; pj_timer_entry retransmit_timer; } uas_state_t; /* UAC state */ typedef struct uac_state_t { pj_str_t tag; /* To tag */ pj_int32_t cseq; pj_uint32_t rseq; /* Initialized to -1 */ struct uac_state_t *next; /* next call leg */ } uac_state_t; /* State attached to each dialog. */ struct dlg_data { pjsip_inv_session *inv; uas_state_t *uas_state; uac_state_t *uac_state_list; }; /***************************************************************************** ** ** Module ** ***************************************************************************** */ static pj_status_t mod_100rel_load(pjsip_endpoint *endpt) { mod_100rel.endpt = endpt; pjsip_endpt_add_capability(endpt, &mod_100rel.mod, PJSIP_H_ALLOW, NULL, 1, &pjsip_prack_method.name); pjsip_endpt_add_capability(endpt, &mod_100rel.mod, PJSIP_H_SUPPORTED, NULL, 1, &tag_100rel); return PJ_SUCCESS; } static pjsip_require_hdr *find_req_hdr(pjsip_msg *msg) { pjsip_require_hdr *hreq; hreq = (pjsip_require_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, NULL); while (hreq) { unsigned i; for (i=0; icount; ++i) { if (!pj_stricmp(&hreq->values[i], &tag_100rel)) { return hreq; } } if ((void*)hreq->next == (void*)&msg->hdr) return NULL; hreq = (pjsip_require_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, hreq->next); } return NULL; } /* * Get PRACK method constant. */ PJ_DEF(const pjsip_method*) pjsip_get_prack_method(void) { return &pjsip_prack_method; } /* * init module */ PJ_DEF(pj_status_t) pjsip_100rel_init_module(pjsip_endpoint *endpt) { if (mod_100rel.mod.id != -1) return PJ_SUCCESS; return pjsip_endpt_register_module(endpt, &mod_100rel.mod); } /* * API: attach 100rel support in invite session. Called by * sip_inv.c */ PJ_DEF(pj_status_t) pjsip_100rel_attach(pjsip_inv_session *inv) { dlg_data *dd; /* Check that 100rel module has been initialized */ PJ_ASSERT_RETURN(mod_100rel.mod.id >= 0, PJ_EINVALIDOP); /* Create and attach as dialog usage */ dd = PJ_POOL_ZALLOC_T(inv->dlg->pool, dlg_data); dd->inv = inv; pjsip_dlg_add_usage(inv->dlg, &mod_100rel.mod, (void*)dd); PJ_LOG(5,(dd->inv->dlg->obj_name, "100rel module attached")); return PJ_SUCCESS; } /* * Check if incoming response has reliable provisional response feature. */ PJ_DEF(pj_bool_t) pjsip_100rel_is_reliable(pjsip_rx_data *rdata) { pjsip_msg *msg = rdata->msg_info.msg; PJ_ASSERT_RETURN(msg->type == PJSIP_RESPONSE_MSG, PJ_FALSE); return msg->line.status.code > 100 && msg->line.status.code < 200 && rdata->msg_info.require != NULL && find_req_hdr(msg) != NULL; } /* * Create PRACK request for the incoming reliable provisional response. */ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv, pjsip_rx_data *rdata, pjsip_tx_data **p_tdata) { dlg_data *dd; uac_state_t *uac_state = NULL; const pj_str_t *to_tag = &rdata->msg_info.to->tag; pjsip_transaction *tsx; pjsip_msg *msg; pjsip_generic_string_hdr *rseq_hdr; pjsip_generic_string_hdr *rack_hdr; unsigned rseq; pj_str_t rack; char rack_buf[80]; pjsip_tx_data *tdata; pj_status_t status; *p_tdata = NULL; dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id]; PJ_ASSERT_RETURN(dd != NULL, PJSIP_ENOTINITIALIZED); tsx = pjsip_rdata_get_tsx(rdata); msg = rdata->msg_info.msg; /* Check our assumptions */ pj_assert( tsx->role == PJSIP_ROLE_UAC && tsx->method.id == PJSIP_INVITE_METHOD && msg->line.status.code > 100 && msg->line.status.code < 200); /* Get the RSeq header */ rseq_hdr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(msg, &RSEQ, NULL); if (rseq_hdr == NULL) { PJ_LOG(4,(dd->inv->dlg->obj_name, "Ignoring 100rel response with no RSeq header")); return PJSIP_EMISSINGHDR; } rseq = (pj_uint32_t) pj_strtoul(&rseq_hdr->hvalue); /* Find UAC state for the specified call leg */ uac_state = dd->uac_state_list; while (uac_state) { if (pj_stricmp(&uac_state->tag, to_tag)==0) break; uac_state = uac_state->next; } /* Create new UAC state if we don't have one */ if (uac_state == NULL) { uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool, uac_state_t); uac_state->cseq = rdata->msg_info.cseq->cseq; uac_state->rseq = rseq - 1; pj_strdup(dd->inv->dlg->pool, &uac_state->tag, to_tag); uac_state->next = dd->uac_state_list; dd->uac_state_list = uac_state; } /* If this is from new INVITE transaction, reset UAC state. */ if (rdata->msg_info.cseq->cseq != uac_state->cseq) { uac_state->cseq = rdata->msg_info.cseq->cseq; uac_state->rseq = rseq - 1; } /* Ignore provisional response retransmission */ if (rseq <= uac_state->rseq) { /* This should have been handled before */ return PJ_EIGNORED; /* Ignore provisional response with out-of-order RSeq */ } else if (rseq != uac_state->rseq + 1) { PJ_LOG(4,(dd->inv->dlg->obj_name, "Ignoring 100rel response because RSeq jump " "(expecting %u, got %u)", uac_state->rseq+1, rseq)); return PJ_EIGNORED; } /* Update our RSeq */ uac_state->rseq = rseq; /* Create PRACK */ status = pjsip_dlg_create_request(dd->inv->dlg, &pjsip_prack_method, -1, &tdata); if (status != PJ_SUCCESS) return status; /* If this response is a forked response from a different call-leg, * update the req URI (https://trac.pjsip.org/repos/ticket/1364) */ if (pj_stricmp(&uac_state->tag, &dd->inv->dlg->remote.info->tag)) { const pjsip_contact_hdr *mhdr; mhdr = (const pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); if (!mhdr || !mhdr->uri) { PJ_LOG(4,(dd->inv->dlg->obj_name, "Ignoring 100rel response with no or " "invalid Contact header")); pjsip_tx_data_dec_ref(tdata); return PJ_EIGNORED; } tdata->msg->line.req.uri = (pjsip_uri*) pjsip_uri_clone(tdata->pool, mhdr->uri); } /* Create RAck header */ rack.ptr = rack_buf; rack.slen = pj_ansi_snprintf(rack.ptr, sizeof(rack_buf), "%u %u %.*s", rseq, rdata->msg_info.cseq->cseq, (int)tsx->method.name.slen, tsx->method.name.ptr); if (rack.slen < 1 || rack.slen >= (int)sizeof(rack_buf)) { return PJ_ETOOSMALL; } rack_hdr = pjsip_generic_string_hdr_create(tdata->pool, &RACK, &rack); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) rack_hdr); /* Done */ *p_tdata = tdata; return PJ_SUCCESS; } /* * Send PRACK request. */ PJ_DEF(pj_status_t) pjsip_100rel_send_prack( pjsip_inv_session *inv, pjsip_tx_data *tdata) { dlg_data *dd; dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id]; PJ_ASSERT_ON_FAIL(dd != NULL, {pjsip_tx_data_dec_ref(tdata); return PJSIP_ENOTINITIALIZED; }); return pjsip_dlg_send_request(inv->dlg, tdata, mod_100rel.mod.id, (void*) dd); } /* Clear all responses in the transmission list */ static void clear_all_responses(dlg_data *dd) { tx_data_list_t *tl; tl = dd->uas_state->tx_data_list.next; while (tl != &dd->uas_state->tx_data_list) { pjsip_tx_data_dec_ref(tl->tdata); tl = tl->next; } pj_list_init(&dd->uas_state->tx_data_list); } /* * Notify 100rel module that the invite session has been disconnected. */ PJ_DEF(pj_status_t) pjsip_100rel_end_session(pjsip_inv_session *inv) { dlg_data *dd; dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id]; if (!dd) return PJ_SUCCESS; /* Make sure we don't have pending transmission */ if (dd->uas_state) { /* Cancel the retransmit timer */ if (dd->uas_state->retransmit_timer.id) { pjsip_endpt_cancel_timer(dd->inv->dlg->endpt, &dd->uas_state->retransmit_timer); dd->uas_state->retransmit_timer.id = PJ_FALSE; } if (!pj_list_empty(&dd->uas_state->tx_data_list)) { /* Clear all pending responses (drop 'em) */ clear_all_responses(dd); } } return PJ_SUCCESS; } static void parse_rack(const pj_str_t *rack, pj_uint32_t *p_rseq, pj_int32_t *p_seq, pj_str_t *p_method) { const char *p = rack->ptr, *end = p + rack->slen; pj_str_t token; token.ptr = (char*)p; while (p < end && pj_isdigit(*p)) ++p; token.slen = p - token.ptr; *p_rseq = pj_strtoul(&token); ++p; token.ptr = (char*)p; while (p < end && pj_isdigit(*p)) ++p; token.slen = p - token.ptr; *p_seq = pj_strtoul(&token); ++p; if (p < end) { p_method->ptr = (char*)p; p_method->slen = end - p; } else { p_method->ptr = NULL; p_method->slen = 0; } } /* * Handle incoming PRACK request. */ PJ_DEF(pj_status_t) pjsip_100rel_on_rx_prack( pjsip_inv_session *inv, pjsip_rx_data *rdata) { dlg_data *dd; pjsip_transaction *tsx; pjsip_msg *msg; pjsip_generic_string_hdr *rack_hdr; pjsip_tx_data *tdata; pj_uint32_t rseq; pj_int32_t cseq; pj_str_t method; pj_status_t status; tsx = pjsip_rdata_get_tsx(rdata); pj_assert(tsx != NULL); msg = rdata->msg_info.msg; dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id]; if (dd == NULL) { /* UAC sends us PRACK while we didn't send reliable provisional * response. Respond with 400 (?) */ const pj_str_t reason = pj_str("Unexpected PRACK"); status = pjsip_dlg_create_response(inv->dlg, rdata, 400, &reason, &tdata); if (status == PJ_SUCCESS) { status = pjsip_dlg_send_response(inv->dlg, tsx, tdata); } return PJSIP_ENOTINITIALIZED; } /* Always reply with 200/OK for PRACK */ status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata); if (status == PJ_SUCCESS) { status = pjsip_dlg_send_response(inv->dlg, tsx, tdata); } /* Ignore if we don't have pending transmission */ if (dd->uas_state == NULL || pj_list_empty(&dd->uas_state->tx_data_list)) { PJ_LOG(4,(dd->inv->dlg->obj_name, "PRACK ignored - no pending response")); return PJ_EIGNORED; } /* Find RAck header */ rack_hdr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(msg, &RACK, NULL); if (!rack_hdr) { /* RAck header not found */ PJ_LOG(4,(dd->inv->dlg->obj_name, "No RAck header")); return PJSIP_EMISSINGHDR; } /* Parse RAck header */ parse_rack(&rack_hdr->hvalue, &rseq, &cseq, &method); /* Match RAck against outgoing transmission */ if (rseq == dd->uas_state->tx_data_list.next->rseq && cseq == dd->uas_state->cseq) { /* * Yes this PRACK matches outgoing transmission. */ tx_data_list_t *tl = dd->uas_state->tx_data_list.next; if (dd->uas_state->retransmit_timer.id) { pjsip_endpt_cancel_timer(dd->inv->dlg->endpt, &dd->uas_state->retransmit_timer); dd->uas_state->retransmit_timer.id = PJ_FALSE; } /* Remove from the list */ if (tl != &dd->uas_state->tx_data_list) { pj_list_erase(tl); /* Destroy the response */ pjsip_tx_data_dec_ref(tl->tdata); } /* Schedule next packet */ dd->uas_state->retransmit_count = 0; if (!pj_list_empty(&dd->uas_state->tx_data_list)) { on_retransmit(NULL, &dd->uas_state->retransmit_timer); } } else { /* No it doesn't match */ PJ_LOG(4,(dd->inv->dlg->obj_name, "Rx PRACK with no matching reliable response")); return PJ_EIGNORED; } return PJ_SUCCESS; } /* * This is retransmit timer callback, called initially to send the response, * and subsequently when the retransmission time elapses. */ static void on_retransmit(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { dlg_data *dd; tx_data_list_t *tl; pjsip_tx_data *tdata; pj_bool_t final; pj_time_val delay; PJ_UNUSED_ARG(timer_heap); dd = (dlg_data*) entry->user_data; entry->id = PJ_FALSE; ++dd->uas_state->retransmit_count; if (dd->uas_state->retransmit_count >= 7) { /* If a reliable provisional response is retransmitted for 64*T1 seconds without reception of a corresponding PRACK, the UAS SHOULD reject the original request with a 5xx response. */ pj_str_t reason = pj_str("Reliable response timed out"); pj_status_t status; /* Clear all pending responses */ clear_all_responses(dd); /* Send 500 response */ status = pjsip_inv_end_session(dd->inv, 500, &reason, &tdata); if (status == PJ_SUCCESS) { pjsip_dlg_send_response(dd->inv->dlg, dd->inv->invite_tsx, tdata); } return; } pj_assert(!pj_list_empty(&dd->uas_state->tx_data_list)); tl = dd->uas_state->tx_data_list.next; tdata = tl->tdata; pjsip_tx_data_add_ref(tdata); final = tdata->msg->line.status.code >= 200; if (dd->uas_state->retransmit_count == 1) { pjsip_tsx_send_msg(dd->inv->invite_tsx, tdata); } else { pjsip_tsx_retransmit_no_state(dd->inv->invite_tsx, tdata); } if (final) { /* This is final response, which will be retransmitted by * UA layer. There's no more task to do, so clear the * transmission list and bail out. */ clear_all_responses(dd); return; } /* Schedule next retransmission */ if (dd->uas_state->retransmit_count < 6) { delay.sec = 0; delay.msec = (1 << dd->uas_state->retransmit_count) * pjsip_cfg()->tsx.t1; pj_time_val_normalize(&delay); } else { delay.sec = 1; delay.msec = 500; } pjsip_endpt_schedule_timer(dd->inv->dlg->endpt, &dd->uas_state->retransmit_timer, &delay); entry->id = PJ_TRUE; } /* Clone response. */ static pjsip_tx_data *clone_tdata(dlg_data *dd, const pjsip_tx_data *src) { pjsip_tx_data *dst; const pjsip_hdr *hsrc; pjsip_msg *msg; pj_status_t status; status = pjsip_endpt_create_tdata(dd->inv->dlg->endpt, &dst); if (status != PJ_SUCCESS) return NULL; msg = pjsip_msg_create(dst->pool, PJSIP_RESPONSE_MSG); dst->msg = msg; pjsip_tx_data_add_ref(dst); /* Duplicate status line */ msg->line.status.code = src->msg->line.status.code; pj_strdup(dst->pool, &msg->line.status.reason, &src->msg->line.status.reason); /* Duplicate all headers */ hsrc = src->msg->hdr.next; while (hsrc != &src->msg->hdr) { pjsip_hdr *h = (pjsip_hdr*) pjsip_hdr_clone(dst->pool, hsrc); pjsip_msg_add_hdr(msg, h); hsrc = hsrc->next; } /* Duplicate message body */ if (src->msg->body) msg->body = pjsip_msg_body_clone(dst->pool, src->msg->body); PJ_LOG(5,(dd->inv->dlg->obj_name, "Reliable response %s created", pjsip_tx_data_get_info(dst))); return dst; } /* Check if any pending response in transmission list has SDP */ static pj_bool_t has_sdp(dlg_data *dd) { tx_data_list_t *tl; tl = dd->uas_state->tx_data_list.next; while (tl != &dd->uas_state->tx_data_list) { if (tl->tdata->msg->body) return PJ_TRUE; tl = tl->next; } return PJ_FALSE; } /* Send response reliably */ PJ_DEF(pj_status_t) pjsip_100rel_tx_response(pjsip_inv_session *inv, pjsip_tx_data *tdata) { pjsip_cseq_hdr *cseq_hdr; pjsip_generic_string_hdr *rseq_hdr; pjsip_require_hdr *req_hdr; int status_code; dlg_data *dd; pjsip_tx_data *old_tdata; pj_status_t status; PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_RESPONSE_MSG, PJSIP_ENOTRESPONSEMSG); status_code = tdata->msg->line.status.code; /* 100 response doesn't need PRACK */ if (status_code == 100) return pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata); /* Get the 100rel data attached to this dialog */ dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id]; PJ_ASSERT_RETURN(dd != NULL, PJ_EINVALIDOP); /* Clone tdata. * We need to clone tdata because we may need to keep it in our * retransmission list, while the original dialog may modify it * if it wants to send another response. */ old_tdata = tdata; tdata = clone_tdata(dd, old_tdata); pjsip_tx_data_dec_ref(old_tdata); /* Get CSeq header, and make sure this is INVITE response */ cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); PJ_ASSERT_RETURN(cseq_hdr != NULL, PJ_EBUG); PJ_ASSERT_RETURN(cseq_hdr->method.id == PJSIP_INVITE_METHOD, PJ_EINVALIDOP); /* Remove existing Require header */ req_hdr = find_req_hdr(tdata->msg); if (req_hdr) { pj_list_erase(req_hdr); } /* Remove existing RSeq header */ rseq_hdr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(tdata->msg, &RSEQ, NULL); if (rseq_hdr) pj_list_erase(rseq_hdr); /* Different treatment for provisional and final response */ if (status_code/100 == 2) { /* RFC 3262 Section 3: UAS Behavior: The UAS MAY send a final response to the initial request before having received PRACKs for all unacknowledged reliable provisional responses, unless the final response is 2xx and any of the unacknowledged reliable provisional responses contained a session description. In that case, it MUST NOT send a final response until those provisional responses are acknowledged. */ if (dd->uas_state && has_sdp(dd)) { /* Yes we have transmitted 1xx with SDP reliably. * In this case, must queue the 2xx response. */ tx_data_list_t *tl; tl = PJ_POOL_ZALLOC_T(tdata->pool, tx_data_list_t); tl->tdata = tdata; tl->rseq = (pj_uint32_t)-1; pj_list_push_back(&dd->uas_state->tx_data_list, tl); /* Will send later */ status = PJ_SUCCESS; PJ_LOG(4,(dd->inv->dlg->obj_name, "2xx response will be sent after PRACK")); } else if (dd->uas_state) { /* RFC 3262 Section 3: UAS Behavior: If the UAS does send a final response when reliable responses are still unacknowledged, it SHOULD NOT continue to retransmit the unacknowledged reliable provisional responses, but it MUST be prepared to process PRACK requests for those outstanding responses. */ PJ_LOG(4,(dd->inv->dlg->obj_name, "No SDP sent so far, sending 2xx now")); /* Cancel the retransmit timer */ if (dd->uas_state->retransmit_timer.id) { pjsip_endpt_cancel_timer(dd->inv->dlg->endpt, &dd->uas_state->retransmit_timer); dd->uas_state->retransmit_timer.id = PJ_FALSE; } /* Clear all pending responses (drop 'em) */ clear_all_responses(dd); /* And transmit the 2xx response */ status=pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata); } else { /* We didn't send any reliable provisional response */ /* Transmit the 2xx response */ status=pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata); } } else if (status_code >= 300) { /* RFC 3262 Section 3: UAS Behavior: If the UAS does send a final response when reliable responses are still unacknowledged, it SHOULD NOT continue to retransmit the unacknowledged reliable provisional responses, but it MUST be prepared to process PRACK requests for those outstanding responses. */ /* Cancel the retransmit timer */ if (dd->uas_state && dd->uas_state->retransmit_timer.id) { pjsip_endpt_cancel_timer(dd->inv->dlg->endpt, &dd->uas_state->retransmit_timer); dd->uas_state->retransmit_timer.id = PJ_FALSE; /* Clear all pending responses (drop 'em) */ clear_all_responses(dd); } /* And transmit the 2xx response */ status=pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata); } else { /* * This is provisional response. */ char rseq_str[32]; pj_str_t rseq; tx_data_list_t *tl; /* Create UAS state if we don't have one */ if (dd->uas_state == NULL) { dd->uas_state = PJ_POOL_ZALLOC_T(inv->dlg->pool, uas_state_t); dd->uas_state->cseq = cseq_hdr->cseq; dd->uas_state->rseq = pj_rand() % 0x7FFF; pj_list_init(&dd->uas_state->tx_data_list); dd->uas_state->retransmit_timer.user_data = dd; dd->uas_state->retransmit_timer.cb = &on_retransmit; } /* Check that CSeq match */ PJ_ASSERT_RETURN(cseq_hdr->cseq == dd->uas_state->cseq, PJ_EINVALIDOP); /* Add Require header */ req_hdr = pjsip_require_hdr_create(tdata->pool); req_hdr->count = 1; req_hdr->values[0] = tag_100rel; pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)req_hdr); /* Add RSeq header */ pj_ansi_snprintf(rseq_str, sizeof(rseq_str), "%u", dd->uas_state->rseq); rseq = pj_str(rseq_str); rseq_hdr = pjsip_generic_string_hdr_create(tdata->pool, &RSEQ, &rseq); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)rseq_hdr); /* Create list entry for this response */ tl = PJ_POOL_ZALLOC_T(tdata->pool, tx_data_list_t); tl->tdata = tdata; tl->rseq = dd->uas_state->rseq++; /* Add to queue if there's pending response, otherwise * transmit immediately. */ if (!pj_list_empty(&dd->uas_state->tx_data_list)) { int code = tdata->msg->line.status.code; /* Will send later */ pj_list_push_back(&dd->uas_state->tx_data_list, tl); status = PJ_SUCCESS; PJ_LOG(4,(dd->inv->dlg->obj_name, "Reliable %d response enqueued (%d pending)", code, pj_list_size(&dd->uas_state->tx_data_list))); } else { pj_list_push_back(&dd->uas_state->tx_data_list, tl); dd->uas_state->retransmit_count = 0; on_retransmit(NULL, &dd->uas_state->retransmit_timer); status = PJ_SUCCESS; } } return status; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-ua/sip_inv.c ================================================ /* $Id: sip_inv.c 4521 2013-05-18 05:54:22Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Note on offer/answer: * * The offer/answer framework in this implementation assumes the occurence * of SDP in a particular request/response according to this table: offer answer Note: ======================================================================== INVITE X INVITE may contain offer 18x/INVITE X X Response may contain offer or answer 2xx/INVITE X X Response may contain offer or answer ACK X ACK may contain answer PRACK X PRACK can only contain answer 2xx/PRACK Response may not have offer nor answer UPDATE X UPDATE may only contain offer 2xx/UPDATE X Response may only contain answer ======================================================================== * */ #define THIS_FILE "sip_inv.c" static const char *inv_state_names[] = { "NULL", "CALLING", "INCOMING", "EARLY", "CONNECTING", "CONFIRMED", "DISCONNCTD", "TERMINATED", }; /* UPDATE method */ static const pjsip_method pjsip_update_method = { PJSIP_OTHER_METHOD, { "UPDATE", 6 } }; #define POOL_INIT_SIZE 256 #define POOL_INC_SIZE 256 /* * Static prototypes. */ static pj_status_t mod_inv_load(pjsip_endpoint *endpt); static pj_status_t mod_inv_unload(void); static pj_bool_t mod_inv_on_rx_request(pjsip_rx_data *rdata); static pj_bool_t mod_inv_on_rx_response(pjsip_rx_data *rdata); static void mod_inv_on_tsx_state(pjsip_transaction*, pjsip_event*); static void inv_on_state_null( pjsip_inv_session *inv, pjsip_event *e); static void inv_on_state_calling( pjsip_inv_session *inv, pjsip_event *e); static void inv_on_state_incoming( pjsip_inv_session *inv, pjsip_event *e); static void inv_on_state_early( pjsip_inv_session *inv, pjsip_event *e); static void inv_on_state_connecting( pjsip_inv_session *inv, pjsip_event *e); static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e); static void inv_on_state_disconnected( pjsip_inv_session *inv, pjsip_event *e); static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_rx_data *rdata); static pj_status_t inv_negotiate_sdp( pjsip_inv_session *inv ); static pjsip_msg_body *create_sdp_body(pj_pool_t *pool, const pjmedia_sdp_session *c_sdp); static pj_status_t process_answer( pjsip_inv_session *inv, int st_code, pjsip_tx_data *tdata, const pjmedia_sdp_session *local_sdp); static pj_status_t handle_timer_response(pjsip_inv_session *inv, const pjsip_rx_data *rdata, pj_bool_t end_sess_on_failure); static pj_bool_t inv_check_secure_dlg(pjsip_inv_session *inv, pjsip_event *e); static void (*inv_state_handler[])( pjsip_inv_session *inv, pjsip_event *e) = { &inv_on_state_null, &inv_on_state_calling, &inv_on_state_incoming, &inv_on_state_early, &inv_on_state_connecting, &inv_on_state_confirmed, &inv_on_state_disconnected, }; static struct mod_inv { pjsip_module mod; pjsip_endpoint *endpt; pjsip_inv_callback cb; } mod_inv = { { NULL, NULL, /* prev, next. */ { "mod-invite", 10 }, /* Name. */ -1, /* Id */ PJSIP_MOD_PRIORITY_DIALOG_USAGE, /* Priority */ &mod_inv_load, /* load() */ NULL, /* start() */ NULL, /* stop() */ &mod_inv_unload, /* unload() */ &mod_inv_on_rx_request, /* on_rx_request() */ &mod_inv_on_rx_response, /* on_rx_response() */ NULL, /* on_tx_request. */ NULL, /* on_tx_response() */ &mod_inv_on_tsx_state, /* on_tsx_state() */ } }; /* Invite session data to be attached to transaction. */ struct tsx_inv_data { pjsip_inv_session *inv; /* The invite session */ pj_bool_t sdp_done; /* SDP negotiation done for this tsx? */ pj_bool_t retrying; /* Resend (e.g. due to 401/407) */ pj_str_t done_tag; /* To tag in RX response with answer */ pj_bool_t done_early;/* Negotiation was done for early med? */ pj_bool_t has_sdp; /* Message with SDP? */ }; /* * Module load() */ static pj_status_t mod_inv_load(pjsip_endpoint *endpt) { pj_str_t allowed[] = {{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6}, { "UPDATE", 6}}; pj_str_t accepted = { "application/sdp", 15 }; /* Register supported methods: INVITE, ACK, BYE, CANCEL, UPDATE */ pjsip_endpt_add_capability(endpt, &mod_inv.mod, PJSIP_H_ALLOW, NULL, PJ_ARRAY_SIZE(allowed), allowed); /* Register "application/sdp" in Accept header */ pjsip_endpt_add_capability(endpt, &mod_inv.mod, PJSIP_H_ACCEPT, NULL, 1, &accepted); return PJ_SUCCESS; } /* * Module unload() */ static pj_status_t mod_inv_unload(void) { /* Should remove capability here */ return PJ_SUCCESS; } /* * Set session state. */ static void inv_set_state(pjsip_inv_session *inv, pjsip_inv_state state, pjsip_event *e) { pjsip_inv_state prev_state = inv->state; pj_bool_t dont_notify = PJ_FALSE; pj_status_t status; /* Prevent STATE_CALLING from being reported more than once because * of authentication * https://trac.pjsip.org/repos/ticket/1318 */ if (state==PJSIP_INV_STATE_CALLING && (inv->cb_called & (1 << PJSIP_INV_STATE_CALLING)) != 0) { dont_notify = PJ_TRUE; } /* If state is confirmed, check that SDP negotiation is done, * otherwise disconnect the session. */ if (state == PJSIP_INV_STATE_CONFIRMED) { struct tsx_inv_data *tsx_inv_data = NULL; if (inv->invite_tsx) { tsx_inv_data = (struct tsx_inv_data*) inv->invite_tsx->mod_data[mod_inv.mod.id]; } if (pjmedia_sdp_neg_get_state(inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE && (tsx_inv_data && !tsx_inv_data->sdp_done) ) { pjsip_tx_data *bye; PJ_LOG(4,(inv->obj_name, "SDP offer/answer incomplete, ending the " "session")); status = pjsip_inv_end_session(inv, PJSIP_SC_NOT_ACCEPTABLE, NULL, &bye); if (status == PJ_SUCCESS && bye) status = pjsip_inv_send_msg(inv, bye); return; } } /* Set state. */ inv->state = state; /* If state is DISCONNECTED, cause code MUST have been set. */ pj_assert(inv->state != PJSIP_INV_STATE_DISCONNECTED || inv->cause != 0); /* Mark the callback as called for this state */ inv->cb_called |= (1 << state); /* Call on_state_changed() callback. */ if (mod_inv.cb.on_state_changed && inv->notify && !dont_notify) (*mod_inv.cb.on_state_changed)(inv, e); /* Only decrement when previous state is not already DISCONNECTED */ if (inv->state == PJSIP_INV_STATE_DISCONNECTED && prev_state != PJSIP_INV_STATE_DISCONNECTED) { if (inv->last_ack) { pjsip_tx_data_dec_ref(inv->last_ack); inv->last_ack = NULL; } if (inv->invite_req) { pjsip_tx_data_dec_ref(inv->invite_req); inv->invite_req = NULL; } if (inv->pending_bye) { pjsip_tx_data_dec_ref(inv->pending_bye); inv->pending_bye = NULL; } pjsip_100rel_end_session(inv); pjsip_timer_end_session(inv); pjsip_dlg_dec_session(inv->dlg, &mod_inv.mod); /* Release the flip-flop pools */ pj_pool_release(inv->pool_prov); inv->pool_prov = NULL; pj_pool_release(inv->pool_active); inv->pool_active = NULL; } } /* * Set cause code. */ static void inv_set_cause(pjsip_inv_session *inv, int cause_code, const pj_str_t *cause_text) { if ((cause_code > inv->cause) || inv->pending_bye) { inv->cause = (pjsip_status_code) cause_code; if (cause_text) pj_strdup(inv->pool, &inv->cause_text, cause_text); else if (cause_code/100 == 2) inv->cause_text = pj_str("Normal call clearing"); else inv->cause_text = *pjsip_get_status_text(cause_code); } } /* * Check if outgoing request needs to have SDP answer. * This applies for both ACK and PRACK requests. */ static const pjmedia_sdp_session *inv_has_pending_answer(pjsip_inv_session *inv, pjsip_transaction *tsx) { pjmedia_sdp_neg_state neg_state; const pjmedia_sdp_session *sdp = NULL; pj_status_t status; /* If SDP negotiator is ready, start negotiation. */ /* Start nego when appropriate. */ neg_state = inv->neg ? pjmedia_sdp_neg_get_state(inv->neg) : PJMEDIA_SDP_NEG_STATE_NULL; if (neg_state == PJMEDIA_SDP_NEG_STATE_DONE) { /* Nothing to do */ } else if (neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO && pjmedia_sdp_neg_has_local_answer(inv->neg) ) { struct tsx_inv_data *tsx_inv_data; struct tsx_inv_data dummy; /* Get invite session's transaction data. * Note that tsx may be NULL, for example when application sends * delayed ACK request (at this time, the original INVITE * transaction may have been destroyed. */ if (tsx) { tsx_inv_data = (struct tsx_inv_data*)tsx->mod_data[mod_inv.mod.id]; } else { tsx_inv_data = &dummy; pj_bzero(&dummy, sizeof(dummy)); dummy.inv = inv; } status = inv_negotiate_sdp(inv); if (status != PJ_SUCCESS) return NULL; /* Mark this transaction has having SDP offer/answer done. */ tsx_inv_data->sdp_done = 1; status = pjmedia_sdp_neg_get_active_local(inv->neg, &sdp); } else { /* This remark is only valid for ACK. PJ_LOG(4,(inv->dlg->obj_name, "FYI, the SDP negotiator state (%s) is in a mess " "when sending this ACK/PRACK request", pjmedia_sdp_neg_state_str(neg_state))); */ } return sdp; } /* Process pending disconnection * http://trac.pjsip.org/repos/ticket/1712 */ static void inv_perform_pending_bye(pjsip_inv_session *inv) { if (inv->pending_bye) { pjsip_tx_data *bye = inv->pending_bye; pj_status_t status; PJ_LOG(4,(inv->dlg->obj_name, "Sending pending BYE")); inv->pending_bye = NULL; status = pjsip_inv_send_msg(inv, bye); if (status != PJ_SUCCESS) { PJ_PERROR(1,(inv->dlg->obj_name, status, "Failed sending pending BYE")); } } } /* * Send ACK for 2xx response. */ static pj_status_t inv_send_ack(pjsip_inv_session *inv, pjsip_event *e) { pjsip_rx_data *rdata; pjsip_event ack_e; pj_status_t status; if (e->type == PJSIP_EVENT_TSX_STATE) rdata = e->body.tsx_state.src.rdata; else if (e->type == PJSIP_EVENT_RX_MSG) rdata = e->body.rx_msg.rdata; else { pj_assert(!"Unsupported event type"); return PJ_EBUG; } /* Note that with https://trac.pjsip.org/repos/ticket/1725, this * function can be called to send ACK for previous INVITE 200/OK * retransmission */ PJ_LOG(5,(inv->obj_name, "Received %s, sending ACK", pjsip_rx_data_get_info(rdata))); /* Check if we have cached ACK request. Must not use the cached ACK * if it's still marked as pending by transport (#1011) */ if (inv->last_ack && rdata->msg_info.cseq->cseq == inv->last_ack_cseq && !inv->last_ack->is_pending) { pjsip_tx_data_add_ref(inv->last_ack); } else if (mod_inv.cb.on_send_ack) { /* If application handles ACK transmission manually, just notify the * callback */ PJ_LOG(5,(inv->obj_name, "Received %s, notifying application callback", pjsip_rx_data_get_info(rdata))); (*mod_inv.cb.on_send_ack)(inv, rdata); return PJ_SUCCESS; } else { status = pjsip_inv_create_ack(inv, rdata->msg_info.cseq->cseq, &inv->last_ack); if (status != PJ_SUCCESS) return status; } PJSIP_EVENT_INIT_TX_MSG(ack_e, inv->last_ack); /* Send ACK */ status = pjsip_dlg_send_request(inv->dlg, inv->last_ack, -1, NULL); if (status != PJ_SUCCESS) { /* Better luck next time */ pj_assert(!"Unable to send ACK!"); return status; } /* Set state to CONFIRMED (if we're not in CONFIRMED yet). * But don't set it to CONFIRMED if we're already DISCONNECTED * (this may have been a late 200/OK response. */ if (inv->state < PJSIP_INV_STATE_CONFIRMED) { inv_set_state(inv, PJSIP_INV_STATE_CONFIRMED, &ack_e); } return PJ_SUCCESS; } /* * Module on_rx_request() * * This callback is called for these events: * - endpoint receives request which was unhandled by higher priority * modules (e.g. transaction layer, dialog layer). * - dialog distributes incoming request to its usages. */ static pj_bool_t mod_inv_on_rx_request(pjsip_rx_data *rdata) { pjsip_method *method; pjsip_dialog *dlg; pjsip_inv_session *inv; /* Only wants to receive request from a dialog. */ dlg = pjsip_rdata_get_dlg(rdata); if (dlg == NULL) return PJ_FALSE; inv = (pjsip_inv_session*) dlg->mod_data[mod_inv.mod.id]; /* Report to dialog that we handle INVITE, CANCEL, BYE, ACK. * If we need to send response, it will be sent in the state * handlers. */ method = &rdata->msg_info.msg->line.req.method; if (method->id == PJSIP_INVITE_METHOD) { return PJ_TRUE; } /* BYE and CANCEL must have existing invite session */ if (method->id == PJSIP_BYE_METHOD || method->id == PJSIP_CANCEL_METHOD) { if (inv == NULL) return PJ_FALSE; return PJ_TRUE; } /* On receipt ACK request, when state is CONNECTING, * move state to CONFIRMED. */ if (method->id == PJSIP_ACK_METHOD && inv) { /* Ignore if we don't have INVITE in progress */ if (!inv->invite_tsx) { return PJ_TRUE; } /* Ignore ACK if pending INVITE transaction has not finished. */ if (inv->invite_tsx->state < PJSIP_TSX_STATE_COMPLETED) { return PJ_TRUE; } /* Ignore ACK with different CSeq * https://trac.pjsip.org/repos/ticket/1391 */ if (rdata->msg_info.cseq->cseq != inv->invite_tsx->cseq) { return PJ_TRUE; } /* Terminate INVITE transaction, if it's still present. */ if (inv->invite_tsx->state <= PJSIP_TSX_STATE_COMPLETED) { /* Before we terminate INVITE transaction, process the SDP * in the ACK request, if any. * Only do this when invite state is not already disconnected * (http://trac.pjsip.org/repos/ticket/640). */ if (inv->state < PJSIP_INV_STATE_DISCONNECTED) { inv_check_sdp_in_incoming_msg(inv, inv->invite_tsx, rdata); /* Check if local offer got no SDP answer and INVITE session * is in CONFIRMED state. */ if (pjmedia_sdp_neg_get_state(inv->neg)== PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER && inv->state==PJSIP_INV_STATE_CONFIRMED) { pjmedia_sdp_neg_cancel_offer(inv->neg); } } /* Now we can terminate the INVITE transaction */ pj_assert(inv->invite_tsx->status_code >= 200); pjsip_tsx_terminate(inv->invite_tsx, inv->invite_tsx->status_code); inv->invite_tsx = NULL; if (inv->last_answer) { pjsip_tx_data_dec_ref(inv->last_answer); inv->last_answer = NULL; } } /* On receipt of ACK, only set state to confirmed when state * is CONNECTING (e.g. we don't want to set the state to confirmed * when we receive ACK retransmission after sending non-2xx!) */ if (inv->state == PJSIP_INV_STATE_CONNECTING) { pjsip_event event; PJSIP_EVENT_INIT_RX_MSG(event, rdata); inv_set_state(inv, PJSIP_INV_STATE_CONFIRMED, &event); /* Send pending BYE if any: * http://trac.pjsip.org/repos/ticket/1712 * Do this after setting the state to CONFIRMED, so that we * have consistent CONFIRMED state between caller and callee. */ if (inv->pending_bye) inv_perform_pending_bye(inv); } } return PJ_FALSE; } /* This function will process Session Timer headers in received * 2xx or 422 response of INVITE/UPDATE request. */ static pj_status_t handle_timer_response(pjsip_inv_session *inv, const pjsip_rx_data *rdata, pj_bool_t end_sess_on_failure) { pjsip_status_code st_code; pj_status_t status; status = pjsip_timer_process_resp(inv, rdata, &st_code); if (status != PJ_SUCCESS && end_sess_on_failure) { pjsip_tx_data *tdata; pj_status_t status2; status2 = pjsip_inv_end_session(inv, st_code, NULL, &tdata); if (tdata && status2 == PJ_SUCCESS) pjsip_inv_send_msg(inv, tdata); } return status; } /* * Module on_rx_response(). * * This callback is called for these events: * - dialog distributes incoming 2xx response to INVITE (outside * transaction) to its usages. * - endpoint distributes strayed responses. */ static pj_bool_t mod_inv_on_rx_response(pjsip_rx_data *rdata) { pjsip_dialog *dlg; pjsip_inv_session *inv; pjsip_msg *msg = rdata->msg_info.msg; dlg = pjsip_rdata_get_dlg(rdata); /* Ignore responses outside dialog */ if (dlg == NULL) return PJ_FALSE; /* Ignore responses not belonging to invite session */ inv = pjsip_dlg_get_inv_session(dlg); if (inv == NULL) return PJ_FALSE; /* This MAY be retransmission of 2xx response to INVITE. * If it is, we need to send ACK. */ if (msg->type == PJSIP_RESPONSE_MSG && msg->line.status.code/100==2 && rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD) { /* The code inside "if" is called the second time 200/OK * retransmission is received. Also handle the situation * when we have another re-INVITE on going and 200/OK * retransmission is received. See: * https://trac.pjsip.org/repos/ticket/1725. * Also send ACK for 200/OK of pending re-INVITE after call is * disconnected (see https://trac.pjsip.org/repos/ticket/1755). */ if (inv->invite_tsx == NULL || inv->state == PJSIP_INV_STATE_DISCONNECTED || (inv->last_ack && inv->last_ack_cseq==rdata->msg_info.cseq->cseq)) { pjsip_event e; PJSIP_EVENT_INIT_RX_MSG(e, rdata); inv_send_ack(inv, &e); return PJ_TRUE; } } /* No other processing needs to be done here. */ return PJ_FALSE; } /* * Module on_tsx_state() * * This callback is called by dialog framework for all transactions * inside the dialog for all its dialog usages. */ static void mod_inv_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) { pjsip_dialog *dlg; pjsip_inv_session *inv; dlg = pjsip_tsx_get_dlg(tsx); if (dlg == NULL) return; inv = pjsip_dlg_get_inv_session(dlg); if (inv == NULL) return; /* Call state handler for the invite session. */ (*inv_state_handler[inv->state])(inv, e); /* Clear invite transaction when tsx is terminated. * Necessary for app that wants to send a new re-INVITE request immediately * after the transaction is terminated. */ if (tsx->state==PJSIP_TSX_STATE_TERMINATED && tsx == inv->invite_tsx) { inv->invite_tsx = NULL; if (inv->last_answer) { pjsip_tx_data_dec_ref(inv->last_answer); inv->last_answer = NULL; } } /* Call on_tsx_state. CANCEL request is a special case and has been * reported earlier in inv_respond_incoming_cancel() */ if (mod_inv.cb.on_tsx_state_changed && inv->notify && !(tsx->method.id==PJSIP_CANCEL_METHOD && e->body.tsx_state.type==PJSIP_EVENT_RX_MSG)) { (*mod_inv.cb.on_tsx_state_changed)(inv, tsx, e); } /* Clear invite transaction when tsx is confirmed. * Previously we set invite_tsx to NULL only when transaction has * terminated, but this didn't work when ACK has the same Via branch * value as the INVITE (see http://www.pjsip.org/trac/ticket/113) */ if (tsx->state>=PJSIP_TSX_STATE_CONFIRMED && tsx == inv->invite_tsx) { inv->invite_tsx = NULL; if (inv->last_answer) { pjsip_tx_data_dec_ref(inv->last_answer); inv->last_answer = NULL; } } } /* * Check if tx_data has sdp. */ static pj_bool_t tx_data_has_sdp(const pjsip_tx_data *tdata) { pjsip_msg_body *body = tdata->msg->body; pjsip_media_type app_sdp; PJ_ASSERT_RETURN(tdata, PJ_FALSE); pjsip_media_type_init2(&app_sdp, "application", "sdp"); if (body && pj_stricmp(&body->content_type.type, &app_sdp.type)==0 && pj_stricmp(&body->content_type.subtype, &app_sdp.subtype)==0) { return PJ_TRUE; } else if (body && pj_stricmp2(&body->content_type.type, "multipart") && (pj_stricmp2(&body->content_type.subtype, "mixed")==0 || pj_stricmp2(&body->content_type.subtype, "alternative")==0)) { pjsip_multipart_part *part; part = pjsip_multipart_find_part(body, &app_sdp, NULL); if (part) { return PJ_TRUE; } } return PJ_FALSE; } /* * Initialize the invite module. */ PJ_DEF(pj_status_t) pjsip_inv_usage_init( pjsip_endpoint *endpt, const pjsip_inv_callback *cb) { pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(endpt && cb, PJ_EINVAL); /* Some callbacks are mandatory */ PJ_ASSERT_RETURN(cb->on_state_changed && cb->on_new_session, PJ_EINVAL); /* Check if module already registered. */ PJ_ASSERT_RETURN(mod_inv.mod.id == -1, PJ_EINVALIDOP); /* Copy param. */ pj_memcpy(&mod_inv.cb, cb, sizeof(pjsip_inv_callback)); mod_inv.endpt = endpt; /* Register the module. */ status = pjsip_endpt_register_module(endpt, &mod_inv.mod); if (status != PJ_SUCCESS) return status; return PJ_SUCCESS; } /* * Get the instance of invite module. */ PJ_DEF(pjsip_module*) pjsip_inv_usage_instance(void) { return &mod_inv.mod; } /* * Return the invite session for the specified dialog. */ PJ_DEF(pjsip_inv_session*) pjsip_dlg_get_inv_session(pjsip_dialog *dlg) { return (pjsip_inv_session*) dlg->mod_data[mod_inv.mod.id]; } /* * Get INVITE state name. */ PJ_DEF(const char *) pjsip_inv_state_name(pjsip_inv_state state) { PJ_ASSERT_RETURN(state >= PJSIP_INV_STATE_NULL && state <= PJSIP_INV_STATE_DISCONNECTED, "??"); return inv_state_names[state]; } /* * Create UAC invite session. */ PJ_DEF(pj_status_t) pjsip_inv_create_uac( pjsip_dialog *dlg, const pjmedia_sdp_session *local_sdp, unsigned options, pjsip_inv_session **p_inv) { pjsip_inv_session *inv; pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(dlg && p_inv, PJ_EINVAL); /* Must lock dialog first */ pjsip_dlg_inc_lock(dlg); /* Normalize options */ if (options & PJSIP_INV_REQUIRE_100REL) options |= PJSIP_INV_SUPPORT_100REL; if (options & PJSIP_INV_REQUIRE_TIMER) options |= PJSIP_INV_SUPPORT_TIMER; /* Create the session */ inv = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_inv_session); pj_assert(inv != NULL); inv->pool = dlg->pool; inv->role = PJSIP_ROLE_UAC; inv->state = PJSIP_INV_STATE_NULL; inv->dlg = dlg; inv->options = options; inv->notify = PJ_TRUE; inv->cause = (pjsip_status_code) 0; /* Create flip-flop pool (see ticket #877) */ /* (using inv->obj_name as temporary variable for pool names */ pj_ansi_snprintf(inv->obj_name, PJ_MAX_OBJ_NAME, "inv%p", dlg->pool); inv->pool_prov = pjsip_endpt_create_pool(dlg->endpt, inv->obj_name, POOL_INIT_SIZE, POOL_INC_SIZE); inv->pool_active = pjsip_endpt_create_pool(dlg->endpt, inv->obj_name, POOL_INIT_SIZE, POOL_INC_SIZE); /* Object name will use the same dialog pointer. */ pj_ansi_snprintf(inv->obj_name, PJ_MAX_OBJ_NAME, "inv%p", dlg); /* Create negotiator if local_sdp is specified. */ if (local_sdp) { status = pjmedia_sdp_neg_create_w_local_offer(inv->pool, local_sdp, &inv->neg); if (status != PJ_SUCCESS) { pjsip_dlg_dec_lock(dlg); return status; } } /* Register invite as dialog usage. */ status = pjsip_dlg_add_usage(dlg, &mod_inv.mod, inv); if (status != PJ_SUCCESS) { pjsip_dlg_dec_lock(dlg); return status; } /* Increment dialog session */ pjsip_dlg_inc_session(dlg, &mod_inv.mod); /* Create 100rel handler */ pjsip_100rel_attach(inv); /* Done */ *p_inv = inv; pjsip_dlg_dec_lock(dlg); PJ_LOG(5,(inv->obj_name, "UAC invite session created for dialog %s", dlg->obj_name)); return PJ_SUCCESS; } PJ_DEF(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata) { pjsip_rdata_sdp_info *sdp_info; pjsip_msg_body *body = rdata->msg_info.msg->body; pjsip_ctype_hdr *ctype_hdr = rdata->msg_info.ctype; pjsip_media_type app_sdp; sdp_info = (pjsip_rdata_sdp_info*) rdata->endpt_info.mod_data[mod_inv.mod.id]; if (sdp_info) return sdp_info; sdp_info = PJ_POOL_ZALLOC_T(rdata->tp_info.pool, pjsip_rdata_sdp_info); PJ_ASSERT_RETURN(mod_inv.mod.id >= 0, sdp_info); rdata->endpt_info.mod_data[mod_inv.mod.id] = sdp_info; pjsip_media_type_init2(&app_sdp, "application", "sdp"); if (body && ctype_hdr && pj_stricmp(&ctype_hdr->media.type, &app_sdp.type)==0 && pj_stricmp(&ctype_hdr->media.subtype, &app_sdp.subtype)==0) { sdp_info->body.ptr = (char*)body->data; sdp_info->body.slen = body->len; } else if (body && ctype_hdr && pj_stricmp2(&ctype_hdr->media.type, "multipart")==0 && (pj_stricmp2(&ctype_hdr->media.subtype, "mixed")==0 || pj_stricmp2(&ctype_hdr->media.subtype, "alternative")==0)) { pjsip_multipart_part *part; part = pjsip_multipart_find_part(body, &app_sdp, NULL); if (part) { sdp_info->body.ptr = (char*)part->body->data; sdp_info->body.slen = part->body->len; } } if (sdp_info->body.ptr) { pj_status_t status; status = pjmedia_sdp_parse(rdata->tp_info.pool, sdp_info->body.ptr, sdp_info->body.slen, &sdp_info->sdp); if (status == PJ_SUCCESS) status = pjmedia_sdp_validate2(sdp_info->sdp, PJ_FALSE); if (status != PJ_SUCCESS) { sdp_info->sdp = NULL; PJ_PERROR(1,(THIS_FILE, status, "Error parsing/validating SDP body")); } sdp_info->sdp_err = status; } return sdp_info; } /* * Verify incoming INVITE request. */ PJ_DEF(pj_status_t) pjsip_inv_verify_request3(pjsip_rx_data *rdata, pj_pool_t *tmp_pool, unsigned *options, const pjmedia_sdp_session *r_sdp, const pjmedia_sdp_session *l_sdp, pjsip_dialog *dlg, pjsip_endpoint *endpt, pjsip_tx_data **p_tdata) { pjsip_msg *msg = NULL; pjsip_allow_hdr *allow = NULL; pjsip_supported_hdr *sup_hdr = NULL; pjsip_require_hdr *req_hdr = NULL; pjsip_contact_hdr *c_hdr = NULL; int code = 200; unsigned rem_option = 0; pj_status_t status = PJ_SUCCESS; pjsip_hdr res_hdr_list; pjsip_rdata_sdp_info *sdp_info; /* Init return arguments. */ if (p_tdata) *p_tdata = NULL; /* Verify arguments. */ PJ_ASSERT_RETURN(tmp_pool != NULL && options != NULL, PJ_EINVAL); /* Normalize options */ if (*options & PJSIP_INV_REQUIRE_100REL) *options |= PJSIP_INV_SUPPORT_100REL; if (*options & PJSIP_INV_REQUIRE_TIMER) *options |= PJSIP_INV_SUPPORT_TIMER; if (*options & PJSIP_INV_REQUIRE_ICE) *options |= PJSIP_INV_SUPPORT_ICE; if (rdata) { /* Get the message in rdata */ msg = rdata->msg_info.msg; /* Must be INVITE request. */ PJ_ASSERT_RETURN(msg && msg->type == PJSIP_REQUEST_MSG && msg->line.req.method.id == PJSIP_INVITE_METHOD, PJ_EINVAL); } /* If tdata is specified, then either dlg or endpt must be specified */ PJ_ASSERT_RETURN((!p_tdata) || (endpt || dlg), PJ_EINVAL); /* Get the endpoint */ endpt = endpt ? endpt : dlg->endpt; /* Init response header list */ pj_list_init(&res_hdr_list); /* Check the Contact header */ if (msg) { c_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL); } if (msg && (!c_hdr || !c_hdr->uri)) { /* Missing Contact header or Contact contains "*" */ pjsip_warning_hdr *w; pj_str_t warn_text; warn_text = pj_str("Bad/missing Contact header"); w = pjsip_warning_hdr_create(tmp_pool, 399, pjsip_endpt_name(endpt), &warn_text); if (w) { pj_list_push_back(&res_hdr_list, w); } code = PJSIP_SC_BAD_REQUEST; status = PJSIP_ERRNO_FROM_SIP_STATUS(code); goto on_return; } /* Ticket #1735: Check Contact/Record-Route header in a secure dialog. */ if (pjsip_cfg()->endpt.disable_secure_dlg_check == PJ_FALSE && msg && PJSIP_URI_SCHEME_IS_SIPS(msg->line.req.uri)) { /* Check Contact header */ if (!PJSIP_URI_SCHEME_IS_SIPS(c_hdr->uri)) status = PJSIP_ESESSIONINSECURE; /* Check top Record-Route header */ if (status == PJ_SUCCESS) { pjsip_rr_hdr *r = (pjsip_rr_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_RECORD_ROUTE, NULL); if (r && !PJSIP_URI_SCHEME_IS_SIPS(&r->name_addr)) { /* Not "sips", check if it is "sip" and has param * "transport=tls". */ if (PJSIP_URI_SCHEME_IS_SIP(&r->name_addr)) { pjsip_sip_uri *sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(r->name_addr.uri); if (pj_stricmp2(&sip_uri->transport_param, "tls")!=0) status = PJSIP_ESESSIONINSECURE; } else { /* Not "sips" nor "sip", treat it as insecure? */ status = PJSIP_ESESSIONINSECURE; } } } if (status != PJ_SUCCESS) { pjsip_warning_hdr *w; pj_str_t warn_text = pj_str("SIPS Required"); w = pjsip_warning_hdr_create(tmp_pool, 381, pjsip_endpt_name(endpt), &warn_text); if (w) { pj_list_push_back(&res_hdr_list, w); } code = PJSIP_SC_TEMPORARILY_UNAVAILABLE; goto on_return; } } /* Check the request body, see if it's something that we support, * only when the body hasn't been parsed before. */ if (r_sdp == NULL && rdata) { sdp_info = pjsip_rdata_get_sdp_info(rdata); } else { sdp_info = NULL; } if (r_sdp==NULL && msg && msg->body) { /* Check if body really contains SDP. */ if (sdp_info->body.ptr == NULL && !PJSIP_INV_ACCEPT_UNKNOWN_BODY) { /* Couldn't find "application/sdp" */ code = PJSIP_SC_UNSUPPORTED_MEDIA_TYPE; status = PJSIP_ERRNO_FROM_SIP_STATUS(code); if (p_tdata) { /* Add Accept header to response */ pjsip_accept_hdr *acc; acc = pjsip_accept_hdr_create(tmp_pool); PJ_ASSERT_RETURN(acc, PJ_ENOMEM); acc->values[acc->count++] = pj_str("application/sdp"); pj_list_push_back(&res_hdr_list, acc); } goto on_return; } if (sdp_info->sdp_err != PJ_SUCCESS) { /* Unparseable or invalid SDP */ code = PJSIP_SC_BAD_REQUEST; if (p_tdata) { /* Add Warning header. */ pjsip_warning_hdr *w; w = pjsip_warning_hdr_create_from_status(tmp_pool, pjsip_endpt_name(endpt), sdp_info->sdp_err); PJ_ASSERT_RETURN(w, PJ_ENOMEM); pj_list_push_back(&res_hdr_list, w); } goto on_return; } r_sdp = sdp_info->sdp; } if (r_sdp) { /* Negotiate with local SDP */ if (l_sdp) { pjmedia_sdp_neg *neg; /* Local SDP must be valid! */ PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(l_sdp))==PJ_SUCCESS, status); /* Create SDP negotiator */ status = pjmedia_sdp_neg_create_w_remote_offer( tmp_pool, l_sdp, r_sdp, &neg); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* Negotiate SDP */ status = pjmedia_sdp_neg_negotiate(tmp_pool, neg, 0); if (status != PJ_SUCCESS) { /* Incompatible media */ code = PJSIP_SC_NOT_ACCEPTABLE_HERE; if (p_tdata) { pjsip_accept_hdr *acc; pjsip_warning_hdr *w; /* Add Warning header. */ w = pjsip_warning_hdr_create_from_status( tmp_pool, pjsip_endpt_name(endpt), status); PJ_ASSERT_RETURN(w, PJ_ENOMEM); pj_list_push_back(&res_hdr_list, w); /* Add Accept header to response */ acc = pjsip_accept_hdr_create(tmp_pool); PJ_ASSERT_RETURN(acc, PJ_ENOMEM); acc->values[acc->count++] = pj_str("application/sdp"); pj_list_push_back(&res_hdr_list, acc); } goto on_return; } } } /* Check supported methods, see if peer supports UPDATE. * We just assume that peer supports standard INVITE, ACK, CANCEL, and BYE * implicitly by sending this INVITE. */ if (msg) { allow = (pjsip_allow_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_ALLOW, NULL); } if (allow) { unsigned i; const pj_str_t STR_UPDATE = { "UPDATE", 6 }; for (i=0; icount; ++i) { if (pj_stricmp(&allow->values[i], &STR_UPDATE)==0) break; } if (i != allow->count) { /* UPDATE is present in Allow */ rem_option |= PJSIP_INV_SUPPORT_UPDATE; } } /* Check Supported header */ if (msg) { sup_hdr = (pjsip_supported_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_SUPPORTED, NULL); } if (sup_hdr) { unsigned i; const pj_str_t STR_100REL = { "100rel", 6}; const pj_str_t STR_TIMER = { "timer", 5}; const pj_str_t STR_ICE = { "ice", 3 }; for (i=0; icount; ++i) { if (pj_stricmp(&sup_hdr->values[i], &STR_100REL)==0) rem_option |= PJSIP_INV_SUPPORT_100REL; else if (pj_stricmp(&sup_hdr->values[i], &STR_TIMER)==0) rem_option |= PJSIP_INV_SUPPORT_TIMER; else if (pj_stricmp(&sup_hdr->values[i], &STR_ICE)==0) rem_option |= PJSIP_INV_SUPPORT_ICE; } } /* Check Require header */ if (msg) { req_hdr = (pjsip_require_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, NULL); } if (req_hdr) { unsigned i; const pj_str_t STR_100REL = { "100rel", 6}; const pj_str_t STR_REPLACES = { "replaces", 8 }; const pj_str_t STR_TIMER = { "timer", 5 }; const pj_str_t STR_ICE = { "ice", 3 }; unsigned unsupp_cnt = 0; pj_str_t unsupp_tags[PJSIP_GENERIC_ARRAY_MAX_COUNT]; for (i=0; icount; ++i) { if ((*options & PJSIP_INV_SUPPORT_100REL) && pj_stricmp(&req_hdr->values[i], &STR_100REL)==0) { rem_option |= PJSIP_INV_REQUIRE_100REL; } else if ((*options & PJSIP_INV_SUPPORT_TIMER) && pj_stricmp(&req_hdr->values[i], &STR_TIMER)==0) { rem_option |= PJSIP_INV_REQUIRE_TIMER; } else if (pj_stricmp(&req_hdr->values[i], &STR_REPLACES)==0) { pj_bool_t supp; supp = pjsip_endpt_has_capability(endpt, PJSIP_H_SUPPORTED, NULL, &STR_REPLACES); if (!supp) unsupp_tags[unsupp_cnt++] = req_hdr->values[i]; } else if ((*options & PJSIP_INV_SUPPORT_ICE) && pj_stricmp(&req_hdr->values[i], &STR_ICE)==0) { rem_option |= PJSIP_INV_REQUIRE_ICE; } else if (!pjsip_endpt_has_capability(endpt, PJSIP_H_SUPPORTED, NULL, &req_hdr->values[i])) { /* Unknown/unsupported extension tag! */ unsupp_tags[unsupp_cnt++] = req_hdr->values[i]; } } /* Check if there are required tags that we don't support */ if (unsupp_cnt) { code = PJSIP_SC_BAD_EXTENSION; status = PJSIP_ERRNO_FROM_SIP_STATUS(code); if (p_tdata) { pjsip_unsupported_hdr *unsupp_hdr; const pjsip_hdr *h; /* Add Unsupported header. */ unsupp_hdr = pjsip_unsupported_hdr_create(tmp_pool); PJ_ASSERT_RETURN(unsupp_hdr != NULL, PJ_ENOMEM); unsupp_hdr->count = unsupp_cnt; for (i=0; ivalues[i] = unsupp_tags[i]; pj_list_push_back(&res_hdr_list, unsupp_hdr); /* Add Supported header. */ h = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED, NULL); pj_assert(h); if (h) { sup_hdr = (pjsip_supported_hdr*) pjsip_hdr_clone(tmp_pool, h); pj_list_push_back(&res_hdr_list, sup_hdr); } } goto on_return; } } /* Check if there are local requirements that are not supported * by peer. */ if ( msg && (((*options & PJSIP_INV_REQUIRE_100REL)!=0 && (rem_option & PJSIP_INV_SUPPORT_100REL)==0) || ((*options & PJSIP_INV_REQUIRE_TIMER)!=0 && (rem_option & PJSIP_INV_SUPPORT_TIMER)==0))) { code = PJSIP_SC_EXTENSION_REQUIRED; status = PJSIP_ERRNO_FROM_SIP_STATUS(code); if (p_tdata) { const pjsip_hdr *h; /* Add Require header. */ req_hdr = pjsip_require_hdr_create(tmp_pool); PJ_ASSERT_RETURN(req_hdr != NULL, PJ_ENOMEM); if (*options & PJSIP_INV_REQUIRE_100REL) req_hdr->values[req_hdr->count++] = pj_str("100rel"); if (*options & PJSIP_INV_REQUIRE_TIMER) req_hdr->values[req_hdr->count++] = pj_str("timer"); pj_list_push_back(&res_hdr_list, req_hdr); /* Add Supported header. */ h = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED, NULL); pj_assert(h); if (h) { sup_hdr = (pjsip_supported_hdr*) pjsip_hdr_clone(tmp_pool, h); pj_list_push_back(&res_hdr_list, sup_hdr); } } goto on_return; } /* If remote Require something that we support, make us Require * that feature too. */ if (rem_option & PJSIP_INV_REQUIRE_100REL) { pj_assert(*options & PJSIP_INV_SUPPORT_100REL); *options |= PJSIP_INV_REQUIRE_100REL; } if (rem_option & PJSIP_INV_REQUIRE_TIMER) { pj_assert(*options & PJSIP_INV_SUPPORT_TIMER); *options |= PJSIP_INV_REQUIRE_TIMER; } on_return: /* Create response if necessary */ if (code != 200 && p_tdata) { pjsip_tx_data *tdata; const pjsip_hdr *h; if (!rdata) { return PJSIP_ERRNO_FROM_SIP_STATUS(code); } if (dlg) { status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata); } else { status = pjsip_endpt_create_response(endpt, rdata, code, NULL, &tdata); } if (status != PJ_SUCCESS) return status; /* Add response headers. */ h = res_hdr_list.next; while (h != &res_hdr_list) { pjsip_hdr *cloned; cloned = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, h); PJ_ASSERT_RETURN(cloned, PJ_ENOMEM); pjsip_msg_add_hdr(tdata->msg, cloned); h = h->next; } *p_tdata = tdata; /* Can not return PJ_SUCCESS when response message is produced. * Ref: PROTOS test ~#2490 */ if (status == PJ_SUCCESS) status = PJSIP_ERRNO_FROM_SIP_STATUS(code); } return status; } /* * Verify incoming INVITE request. */ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata, unsigned *options, const pjmedia_sdp_session *r_sdp, const pjmedia_sdp_session *l_sdp, pjsip_dialog *dlg, pjsip_endpoint *endpt, pjsip_tx_data **p_tdata) { return pjsip_inv_verify_request3(rdata, rdata->tp_info.pool, options, r_sdp, l_sdp, dlg, endpt, p_tdata); } /* * Verify incoming INVITE request. */ PJ_DEF(pj_status_t) pjsip_inv_verify_request( pjsip_rx_data *rdata, unsigned *options, const pjmedia_sdp_session *l_sdp, pjsip_dialog *dlg, pjsip_endpoint *endpt, pjsip_tx_data **p_tdata) { return pjsip_inv_verify_request3(rdata, rdata->tp_info.pool, options, NULL, l_sdp, dlg, endpt, p_tdata); } /* * Create UAS invite session. */ PJ_DEF(pj_status_t) pjsip_inv_create_uas( pjsip_dialog *dlg, pjsip_rx_data *rdata, const pjmedia_sdp_session *local_sdp, unsigned options, pjsip_inv_session **p_inv) { pjsip_inv_session *inv; struct tsx_inv_data *tsx_inv_data; pjsip_msg *msg; pjsip_rdata_sdp_info *sdp_info; pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(dlg && rdata && p_inv, PJ_EINVAL); /* Dialog MUST have been initialised. */ PJ_ASSERT_RETURN(pjsip_rdata_get_tsx(rdata) != NULL, PJ_EINVALIDOP); msg = rdata->msg_info.msg; /* rdata MUST contain INVITE request */ PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG && msg->line.req.method.id == PJSIP_INVITE_METHOD, PJ_EINVALIDOP); /* Lock dialog */ pjsip_dlg_inc_lock(dlg); /* Normalize options */ if (options & PJSIP_INV_REQUIRE_100REL) options |= PJSIP_INV_SUPPORT_100REL; if (options & PJSIP_INV_REQUIRE_TIMER) options |= PJSIP_INV_SUPPORT_TIMER; /* Create the session */ inv = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_inv_session); pj_assert(inv != NULL); inv->pool = dlg->pool; inv->role = PJSIP_ROLE_UAS; inv->state = PJSIP_INV_STATE_NULL; inv->dlg = dlg; inv->options = options; inv->notify = PJ_TRUE; inv->cause = (pjsip_status_code) 0; /* Create flip-flop pool (see ticket #877) */ /* (using inv->obj_name as temporary variable for pool names */ pj_ansi_snprintf(inv->obj_name, PJ_MAX_OBJ_NAME, "inv%p", dlg->pool); inv->pool_prov = pjsip_endpt_create_pool(dlg->endpt, inv->obj_name, POOL_INIT_SIZE, POOL_INC_SIZE); inv->pool_active = pjsip_endpt_create_pool(dlg->endpt, inv->obj_name, POOL_INIT_SIZE, POOL_INC_SIZE); /* Object name will use the same dialog pointer. */ pj_ansi_snprintf(inv->obj_name, PJ_MAX_OBJ_NAME, "inv%p", dlg); /* Process SDP in message body, if present. */ sdp_info = pjsip_rdata_get_sdp_info(rdata); if (sdp_info->sdp_err) { pjsip_dlg_dec_lock(dlg); return sdp_info->sdp_err; } /* Create negotiator. */ if (sdp_info->sdp) { status = pjmedia_sdp_neg_create_w_remote_offer(inv->pool, local_sdp, sdp_info->sdp, &inv->neg); } else if (local_sdp) { status = pjmedia_sdp_neg_create_w_local_offer(inv->pool, local_sdp, &inv->neg); } else { status = PJ_SUCCESS; } if (status != PJ_SUCCESS) { pjsip_dlg_dec_lock(dlg); return status; } /* Register invite as dialog usage. */ status = pjsip_dlg_add_usage(dlg, &mod_inv.mod, inv); if (status != PJ_SUCCESS) { pjsip_dlg_dec_lock(dlg); return status; } /* Increment session in the dialog. */ pjsip_dlg_inc_session(dlg, &mod_inv.mod); /* Save the invite transaction. */ inv->invite_tsx = pjsip_rdata_get_tsx(rdata); /* Attach our data to the transaction. */ tsx_inv_data = PJ_POOL_ZALLOC_T(inv->invite_tsx->pool, struct tsx_inv_data); tsx_inv_data->inv = inv; tsx_inv_data->has_sdp = (sdp_info->sdp!=NULL); inv->invite_tsx->mod_data[mod_inv.mod.id] = tsx_inv_data; /* Create 100rel handler */ if (inv->options & PJSIP_INV_REQUIRE_100REL) { pjsip_100rel_attach(inv); } /* Done */ pjsip_dlg_dec_lock(dlg); *p_inv = inv; PJ_LOG(5,(inv->obj_name, "UAS invite session created for dialog %s", dlg->obj_name)); return PJ_SUCCESS; } /* * Forcefully terminate the session. */ PJ_DEF(pj_status_t) pjsip_inv_terminate( pjsip_inv_session *inv, int st_code, pj_bool_t notify) { PJ_ASSERT_RETURN(inv, PJ_EINVAL); /* Lock dialog. */ pjsip_dlg_inc_lock(inv->dlg); /* Set callback notify flag. */ inv->notify = notify; /* If there's pending transaction, terminate the transaction. * This may subsequently set the INVITE session state to * disconnected. */ if (inv->invite_tsx && inv->invite_tsx->state <= PJSIP_TSX_STATE_COMPLETED) { pjsip_tsx_terminate(inv->invite_tsx, st_code); } /* Set cause. */ inv_set_cause(inv, st_code, NULL); /* Forcefully terminate the session if state is not DISCONNECTED */ if (inv->state != PJSIP_INV_STATE_DISCONNECTED) { pjsip_event usr_event; PJSIP_EVENT_INIT_USER(usr_event, NULL, NULL, NULL, NULL); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, &usr_event); } /* Done. * The dec_lock() below will actually destroys the dialog if it * has no other session. */ pjsip_dlg_dec_lock(inv->dlg); return PJ_SUCCESS; } /* * Restart UAC session, possibly because app or us wants to re-send the * INVITE request due to 401/407 challenge or 3xx response. */ PJ_DEF(pj_status_t) pjsip_inv_uac_restart(pjsip_inv_session *inv, pj_bool_t new_offer) { PJ_ASSERT_RETURN(inv, PJ_EINVAL); inv->state = PJSIP_INV_STATE_NULL; inv->invite_tsx = NULL; if (inv->last_answer) { pjsip_tx_data_dec_ref(inv->last_answer); inv->last_answer = NULL; } if (new_offer && inv->neg) { pjmedia_sdp_neg_state neg_state; neg_state = pjmedia_sdp_neg_get_state(inv->neg); if (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { pjmedia_sdp_neg_cancel_offer(inv->neg); } } return PJ_SUCCESS; } static void *clone_sdp(pj_pool_t *pool, const void *data, unsigned len) { PJ_UNUSED_ARG(len); return pjmedia_sdp_session_clone(pool, (const pjmedia_sdp_session*)data); } static int print_sdp(pjsip_msg_body *body, char *buf, pj_size_t len) { return pjmedia_sdp_print((const pjmedia_sdp_session*)body->data, buf, len); } PJ_DEF(pj_status_t) pjsip_create_sdp_body( pj_pool_t *pool, pjmedia_sdp_session *sdp, pjsip_msg_body **p_body) { const pj_str_t STR_APPLICATION = { "application", 11}; const pj_str_t STR_SDP = { "sdp", 3 }; pjsip_msg_body *body; body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body); PJ_ASSERT_RETURN(body != NULL, PJ_ENOMEM); pjsip_media_type_init(&body->content_type, (pj_str_t*)&STR_APPLICATION, (pj_str_t*)&STR_SDP); body->data = sdp; body->len = 0; body->clone_data = &clone_sdp; body->print_body = &print_sdp; *p_body = body; return PJ_SUCCESS; } static pjsip_msg_body *create_sdp_body(pj_pool_t *pool, const pjmedia_sdp_session *c_sdp) { pjsip_msg_body *body; pj_status_t status; status = pjsip_create_sdp_body(pool, pjmedia_sdp_session_clone(pool, c_sdp), &body); if (status != PJ_SUCCESS) return NULL; return body; } /* Utility to remove a string value from generic array header */ static void remove_val_from_array_hdr(pjsip_generic_array_hdr *arr_hdr, const pj_str_t *val) { unsigned i; for (i=0; icount; ++i) { if (pj_stricmp(&arr_hdr->values[i], val)==0) { pj_array_erase(arr_hdr->values, sizeof(arr_hdr->values[0]), arr_hdr->count, i); --arr_hdr->count; break; } } } /* Remove disabled extensions, e.g: timer & 100rel, from Allow/Supported * headers (see ticket #1858). */ static void cleanup_allow_sup_hdr(unsigned inv_option, pjsip_tx_data *tdata, pjsip_allow_hdr *allow_hdr, pjsip_supported_hdr *sup_hdr) { /* If all extensions are enabled, nothing to do */ if ((inv_option & PJSIP_INV_SUPPORT_100REL) && (inv_option & PJSIP_INV_SUPPORT_TIMER)) { return; } if (!allow_hdr && tdata) { allow_hdr = (pjsip_allow_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ALLOW, NULL); } if (!sup_hdr && tdata) { sup_hdr = (pjsip_supported_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL); } /* Remove "timer" from Supported header if Session-Timers is * disabled (https://trac.pjsip.org/repos/ticket/1761) */ if ((inv_option & PJSIP_INV_SUPPORT_TIMER) == 0 && sup_hdr) { const pj_str_t STR_TIMER = { "timer", 5 }; remove_val_from_array_hdr(sup_hdr, &STR_TIMER); } if ((inv_option & PJSIP_INV_SUPPORT_100REL) == 0) { const pj_str_t STR_PRACK = { "PRACK", 5 }; const pj_str_t STR_100REL = { "100rel", 6 }; if (allow_hdr) remove_val_from_array_hdr(allow_hdr, &STR_PRACK); if (sup_hdr) remove_val_from_array_hdr(sup_hdr, &STR_100REL); } } /* * Create initial INVITE request. */ PJ_DEF(pj_status_t) pjsip_inv_invite( pjsip_inv_session *inv, pjsip_tx_data **p_tdata ) { pjsip_tx_data *tdata; const pjsip_hdr *hdr; pjsip_allow_hdr *allow_hdr = NULL; pjsip_supported_hdr *sup_hdr = NULL; pj_bool_t has_sdp; pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL); /* State MUST be NULL or CONFIRMED. */ PJ_ASSERT_RETURN(inv->state == PJSIP_INV_STATE_NULL || inv->state == PJSIP_INV_STATE_CONFIRMED, PJ_EINVALIDOP); /* Lock dialog. */ pjsip_dlg_inc_lock(inv->dlg); /* Create the INVITE request. */ status = pjsip_dlg_create_request(inv->dlg, pjsip_get_invite_method(), -1, &tdata); if (status != PJ_SUCCESS) goto on_return; /* If this is the first INVITE, then copy the headers from inv_hdr. * These are the headers parsed from the request URI when the * dialog was created. */ if (inv->state == PJSIP_INV_STATE_NULL) { hdr = inv->dlg->inv_hdr.next; while (hdr != &inv->dlg->inv_hdr) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, hdr)); hdr = hdr->next; } } /* See if we have SDP to send. */ if (inv->neg) { pjmedia_sdp_neg_state neg_state; neg_state = pjmedia_sdp_neg_get_state(inv->neg); has_sdp = (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER || (neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO && pjmedia_sdp_neg_has_local_answer(inv->neg))); } else { has_sdp = PJ_FALSE; } /* Add SDP, if any. */ if (has_sdp) { const pjmedia_sdp_session *offer; status = pjmedia_sdp_neg_get_neg_local(inv->neg, &offer); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); goto on_return; } tdata->msg->body = create_sdp_body(tdata->pool, offer); } /* Add Allow header. */ if (inv->dlg->add_allow) { hdr = pjsip_endpt_get_capability(inv->dlg->endpt, PJSIP_H_ALLOW, NULL); if (hdr) { allow_hdr = (pjsip_allow_hdr*) pjsip_hdr_shallow_clone(tdata->pool, hdr); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)allow_hdr); } } /* Add Supported header */ hdr = pjsip_endpt_get_capability(inv->dlg->endpt, PJSIP_H_SUPPORTED, NULL); if (hdr) { sup_hdr = (pjsip_supported_hdr*) pjsip_hdr_shallow_clone(tdata->pool, hdr); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sup_hdr); } /* Cleanup Allow & Supported headers from disabled extensions */ cleanup_allow_sup_hdr(inv->options, NULL, allow_hdr, sup_hdr); /* Add Require header. */ if ((inv->options & PJSIP_INV_REQUIRE_100REL) || (inv->options & PJSIP_INV_REQUIRE_TIMER)) { pjsip_require_hdr *hreq; hreq = pjsip_require_hdr_create(tdata->pool); if (inv->options & PJSIP_INV_REQUIRE_100REL) hreq->values[hreq->count++] = pj_str("100rel"); if (inv->options & PJSIP_INV_REQUIRE_TIMER) hreq->values[hreq->count++] = pj_str("timer"); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) hreq); } status = pjsip_timer_update_req(inv, tdata); if (status != PJ_SUCCESS) goto on_return; /* Done. */ *p_tdata = tdata; on_return: pjsip_dlg_dec_lock(inv->dlg); return status; } /* Util: swap pool */ static void swap_pool(pj_pool_t **p1, pj_pool_t **p2) { pj_pool_t *tmp = *p1; *p1 = *p2; *p2 = tmp; } /* * Initiate SDP negotiation in the SDP negotiator. */ static pj_status_t inv_negotiate_sdp( pjsip_inv_session *inv ) { pj_status_t status; PJ_ASSERT_RETURN(pjmedia_sdp_neg_get_state(inv->neg) == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, PJMEDIA_SDPNEG_EINSTATE); status = pjmedia_sdp_neg_negotiate(inv->pool_prov, inv->neg, 0); PJ_LOG(5,(inv->obj_name, "SDP negotiation done, status=%d", status)); if (mod_inv.cb.on_media_update && inv->notify) (*mod_inv.cb.on_media_update)(inv, status); /* Invite session may have been terminated by the application even * after a successful SDP negotiation, for example when no audio * codec is present in the offer (see ticket #1034). */ if (inv->state != PJSIP_INV_STATE_DISCONNECTED) { /* Swap the flip-flop pool when SDP negotiation success. */ if (status == PJ_SUCCESS) { swap_pool(&inv->pool_prov, &inv->pool_active); } /* Reset the provisional pool regardless SDP negotiation result. */ pj_pool_reset(inv->pool_prov); } else { status = PJSIP_ERRNO_FROM_SIP_STATUS(inv->cause); } return status; } /* * Check in incoming message for SDP offer/answer. */ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_rx_data *rdata) { struct tsx_inv_data *tsx_inv_data; pj_status_t status; pjsip_msg *msg; pjsip_rdata_sdp_info *sdp_info; /* Check if SDP is present in the message. */ msg = rdata->msg_info.msg; if (msg->body == NULL) { /* Message doesn't have body. */ return PJ_SUCCESS; } sdp_info = pjsip_rdata_get_sdp_info(rdata); if (sdp_info->body.ptr == NULL) { /* Message body is not "application/sdp" */ return PJMEDIA_SDP_EINSDP; } /* Only accept SDP in INVITE, UPDATE and ACK requests, 18x (reliable) and 183 provisional responses * and 200 final response. */ if (!(msg->type == PJSIP_REQUEST_MSG && msg->line.req.method.id == PJSIP_INVITE_METHOD) && !(msg->type == PJSIP_REQUEST_MSG && msg->line.req.method.id == PJSIP_ACK_METHOD) && !(msg->type == PJSIP_REQUEST_MSG && pjsip_method_cmp(&msg->line.req.method, &pjsip_update_method)==0) && !(msg->type == PJSIP_RESPONSE_MSG && msg->line.status.code/10==18 && pjsip_100rel_is_reliable(rdata)) && !(msg->type == PJSIP_RESPONSE_MSG && msg->line.status.code == 183) && !(msg->type == PJSIP_RESPONSE_MSG && msg->line.status.code == 200)) { PJ_LOG(4,(inv->obj_name, "ignored SDP body")); return PJ_SUCCESS; } /* Get/attach invite session's transaction data */ tsx_inv_data = (struct tsx_inv_data*) tsx->mod_data[mod_inv.mod.id]; if (tsx_inv_data == NULL) { tsx_inv_data = PJ_POOL_ZALLOC_T(tsx->pool, struct tsx_inv_data); tsx_inv_data->inv = inv; tsx_inv_data->has_sdp = (sdp_info->sdp!=NULL); tsx->mod_data[mod_inv.mod.id] = tsx_inv_data; } /* Initialize info that we are following forked media */ inv->following_fork = PJ_FALSE; /* MUST NOT do multiple SDP offer/answer in a single transaction, * EXCEPT if: * - this is an initial UAC INVITE transaction (i.e. not re-INVITE), and * - the previous negotiation was done on an early media (18x) and * this response is a final/2xx response, and * - the 2xx response has different To tag than the 18x response * (i.e. the request has forked). * * The exception above is to add a rudimentary support for early media * forking (sample case: custom ringback). See this ticket for more * info: http://trac.pjsip.org/repos/ticket/657 */ if (tsx_inv_data->sdp_done) { pj_str_t res_tag; int st_code; res_tag = rdata->msg_info.to->tag; st_code = rdata->msg_info.msg->line.status.code; /* Allow final/early response after SDP has been negotiated in early * media, IF this response is a final/early response with different * tag. * See ticket #1644 and #1764 for forked early media case. */ if (tsx->role == PJSIP_ROLE_UAC && (st_code/100 == 2 || (st_code/10 == 18 /* st_code == 18x */ && pjsip_cfg()->endpt.follow_early_media_fork)) && tsx_inv_data->done_early && pj_stricmp(&tsx_inv_data->done_tag, &res_tag)) { const pjmedia_sdp_session *reoffer_sdp = NULL; PJ_LOG(4,(inv->obj_name, "Received forked %s response " "after SDP negotiation has been done in early " "media. Renegotiating SDP..", (st_code/10==18? "early" : "final" ))); /* Retrieve original SDP offer from INVITE request */ reoffer_sdp = (const pjmedia_sdp_session*) tsx->last_tx->msg->body->data; /* Feed the original offer to negotiator */ status = pjmedia_sdp_neg_modify_local_offer2(inv->pool_prov, inv->neg, inv->sdp_neg_flags, reoffer_sdp); if (status != PJ_SUCCESS) { PJ_LOG(1,(inv->obj_name, "Error updating local offer for " "forked 2xx/18x response (err=%d)", status)); return status; } inv->following_fork = PJ_TRUE; } else { if (rdata->msg_info.msg->body) { PJ_LOG(4,(inv->obj_name, "SDP negotiation done, message " "body is ignored")); } return PJ_SUCCESS; } } /* Process the SDP body. */ if (sdp_info->sdp_err) { PJ_PERROR(4,(THIS_FILE, sdp_info->sdp_err, "Error parsing SDP in %s", pjsip_rx_data_get_info(rdata))); return PJMEDIA_SDP_EINSDP; } pj_assert(sdp_info->sdp != NULL); /* The SDP can be an offer or answer, depending on negotiator's state */ if (inv->neg == NULL || pjmedia_sdp_neg_get_state(inv->neg) == PJMEDIA_SDP_NEG_STATE_DONE) { /* This is an offer. */ PJ_LOG(5,(inv->obj_name, "Got SDP offer in %s", pjsip_rx_data_get_info(rdata))); if (inv->neg == NULL) { status=pjmedia_sdp_neg_create_w_remote_offer(inv->pool, NULL, sdp_info->sdp, &inv->neg); } else { status=pjmedia_sdp_neg_set_remote_offer(inv->pool_prov, inv->neg, sdp_info->sdp); } if (status != PJ_SUCCESS) { PJ_PERROR(4,(THIS_FILE, status, "Error processing SDP offer in %", pjsip_rx_data_get_info(rdata))); return PJMEDIA_SDP_EINSDP; } /* Inform application about remote offer. */ if (mod_inv.cb.on_rx_offer && inv->notify) { (*mod_inv.cb.on_rx_offer)(inv, sdp_info->sdp); } /* application must have supplied an answer at this point. */ if (pjmedia_sdp_neg_get_state(inv->neg) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) { if (mod_inv.cb.on_rx_reinvite && inv->notify && msg->type == PJSIP_REQUEST_MSG && msg->line.req.method.id == PJSIP_INVITE_METHOD) { /* Do not return failure first, allow the application * to set the answer in the on_rx_reinvite() callback. */ PJ_LOG(5,(inv->obj_name, "Ignoring on_rx_offer() status " "because on_rx_reinvite() is implemented")); return PJ_SUCCESS; } return PJ_EINVALIDOP; } } else if (pjmedia_sdp_neg_get_state(inv->neg) == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { int status_code; /* This is an answer. * Process and negotiate remote answer. */ PJ_LOG(5,(inv->obj_name, "Got SDP answer in %s", pjsip_rx_data_get_info(rdata))); status = pjmedia_sdp_neg_set_remote_answer(inv->pool_prov, inv->neg, sdp_info->sdp); if (status != PJ_SUCCESS) { PJ_PERROR(4,(THIS_FILE, status, "Error processing SDP answer in %s", pjsip_rx_data_get_info(rdata))); return PJMEDIA_SDP_EINSDP; } /* Negotiate SDP */ inv_negotiate_sdp(inv); /* Mark this transaction has having SDP offer/answer done, and * save the reference to the To tag */ tsx_inv_data->sdp_done = 1; status_code = rdata->msg_info.msg->line.status.code; tsx_inv_data->done_early = (status_code/100==1); pj_strdup(tsx->pool, &tsx_inv_data->done_tag, &rdata->msg_info.to->tag); } else { PJ_LOG(5,(THIS_FILE, "Ignored SDP in %s: negotiator state is %s", pjsip_rx_data_get_info(rdata), pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_get_state(inv->neg)))); } return PJ_SUCCESS; } /* * Process INVITE answer, for both initial and subsequent re-INVITE */ static pj_status_t process_answer( pjsip_inv_session *inv, int st_code, pjsip_tx_data *tdata, const pjmedia_sdp_session *local_sdp) { pj_status_t status; const pjmedia_sdp_session *sdp = NULL; /* If local_sdp is specified, then we MUST NOT have answered the * offer before. */ if (local_sdp && (st_code/100==1 || st_code/100==2)) { if (inv->neg == NULL) { status = pjmedia_sdp_neg_create_w_local_offer(inv->pool, local_sdp, &inv->neg); } else if (pjmedia_sdp_neg_get_state(inv->neg)== PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER) { status = pjmedia_sdp_neg_set_local_answer(inv->pool_prov, inv->neg, local_sdp); } else if (pjmedia_sdp_neg_get_state(inv->neg)== PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { /* Go forward with our local offer */ status = PJ_SUCCESS; } else { /* Can not specify local SDP at this state. */ pj_assert(0); status = PJMEDIA_SDPNEG_EINSTATE; } if (status != PJ_SUCCESS) return status; } /* If SDP negotiator is ready, start negotiation. */ if (st_code/100==2 || (st_code/10==18 && st_code!=180 && st_code!=181)) { pjmedia_sdp_neg_state neg_state; /* Start nego when appropriate. */ neg_state = inv->neg ? pjmedia_sdp_neg_get_state(inv->neg) : PJMEDIA_SDP_NEG_STATE_NULL; if (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { status = pjmedia_sdp_neg_get_neg_local(inv->neg, &sdp); } else if (neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO && pjmedia_sdp_neg_has_local_answer(inv->neg) ) { struct tsx_inv_data *tsx_inv_data; /* Get invite session's transaction data */ tsx_inv_data = (struct tsx_inv_data*) inv->invite_tsx->mod_data[mod_inv.mod.id]; status = inv_negotiate_sdp(inv); if (status != PJ_SUCCESS) return status; /* Mark this transaction has having SDP offer/answer done. */ tsx_inv_data->sdp_done = 1; status = pjmedia_sdp_neg_get_active_local(inv->neg, &sdp); } } /* Include SDP when it's available for 2xx and 18x (but not 180 and 181) * response. Subsequent response will include this SDP. * * Note note: * - When offer/answer has been completed in reliable 183, we MUST NOT * send SDP in 2xx response. So if we don't have SDP to send, clear * the SDP in the message body ONLY if 100rel is active in this * session. */ if (sdp) { tdata->msg->body = create_sdp_body(tdata->pool, sdp); } else { if (inv->options & PJSIP_INV_REQUIRE_100REL) { tdata->msg->body = NULL; } } /* Cancel SDP negotiation if this is a negative reply to a re-INVITE */ if (st_code >= 300 && inv->neg != NULL && inv->state == PJSIP_INV_STATE_CONFIRMED) { pjmedia_sdp_neg_state neg_state; neg_state = pjmedia_sdp_neg_get_state(inv->neg); if (neg_state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER) { pjmedia_sdp_neg_cancel_offer(inv->neg); } } return PJ_SUCCESS; } /* * Create first response to INVITE */ PJ_DEF(pj_status_t) pjsip_inv_initial_answer( pjsip_inv_session *inv, pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, const pjmedia_sdp_session *sdp, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pj_status_t status; pjsip_status_code st_code2; /* Verify arguments. */ PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL); /* Must have INVITE transaction. */ PJ_ASSERT_RETURN(inv->invite_tsx, PJ_EBUG); pj_log_push_indent(); pjsip_dlg_inc_lock(inv->dlg); /* Create response */ status = pjsip_dlg_create_response(inv->dlg, rdata, st_code, st_text, &tdata); if (status != PJ_SUCCESS) goto on_return; /* Invoke Session Timers module */ status = pjsip_timer_process_req(inv, rdata, &st_code2); if (status != PJ_SUCCESS) { pj_status_t status2; status2 = pjsip_dlg_modify_response(inv->dlg, tdata, st_code2, NULL); if (status2 != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); goto on_return; } status2 = pjsip_timer_update_resp(inv, tdata); if (status2 == PJ_SUCCESS) *p_tdata = tdata; else pjsip_tx_data_dec_ref(tdata); goto on_return; } /* Process SDP in answer */ status = process_answer(inv, st_code, tdata, sdp); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); goto on_return; } /* Cleanup Allow & Supported headers from disabled extensions */ cleanup_allow_sup_hdr(inv->options, tdata, NULL, NULL); /* Save this answer */ inv->last_answer = tdata; pjsip_tx_data_add_ref(inv->last_answer); PJ_LOG(5,(inv->dlg->obj_name, "Initial answer %s", pjsip_tx_data_get_info(inv->last_answer))); /* Invoke Session Timers */ pjsip_timer_update_resp(inv, tdata); *p_tdata = tdata; on_return: pjsip_dlg_dec_lock(inv->dlg); pj_log_pop_indent(); return status; } /* * Answer INVITE request. */ PJ_DEF(pj_status_t) pjsip_inv_answer( pjsip_inv_session *inv, int st_code, const pj_str_t *st_text, const pjmedia_sdp_session *local_sdp, pjsip_tx_data **p_tdata ) { pjsip_tx_data *last_res; pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL); /* Must have INVITE transaction. */ PJ_ASSERT_RETURN(inv->invite_tsx, PJ_EBUG); /* Must have created an answer before */ PJ_ASSERT_RETURN(inv->last_answer, PJ_EINVALIDOP); pj_log_push_indent(); pjsip_dlg_inc_lock(inv->dlg); /* Modify last response. */ last_res = inv->last_answer; status = pjsip_dlg_modify_response(inv->dlg, last_res, st_code, st_text); if (status != PJ_SUCCESS) goto on_return; /* For non-2xx final response, strip message body */ if (st_code >= 300) { last_res->msg->body = NULL; } /* Process SDP in answer */ status = process_answer(inv, st_code, last_res, local_sdp); if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(last_res); goto on_return; } /* Invoke Session Timers */ pjsip_timer_update_resp(inv, last_res); /* Cleanup Allow & Supported headers from disabled extensions */ cleanup_allow_sup_hdr(inv->options, last_res, NULL, NULL); *p_tdata = last_res; on_return: pjsip_dlg_dec_lock(inv->dlg); pj_log_pop_indent(); return status; } /* * Set local SDP offer/answer. */ PJ_DEF(pj_status_t) pjsip_inv_set_local_sdp(pjsip_inv_session *inv, const pjmedia_sdp_session *sdp ) { const pjmedia_sdp_session *offer; pj_status_t status; PJ_ASSERT_RETURN(inv && sdp, PJ_EINVAL); /* If we have remote SDP offer, set local answer to respond to the offer, * otherwise we set/modify our local offer (and create an SDP negotiator * if we don't have one yet). */ if (inv->neg) { pjmedia_sdp_neg_state neg_state = pjmedia_sdp_neg_get_state(inv->neg); if ((neg_state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER || neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) && pjmedia_sdp_neg_get_neg_remote(inv->neg, &offer) == PJ_SUCCESS) { status = pjsip_inv_set_sdp_answer(inv, sdp); } else if (neg_state == PJMEDIA_SDP_NEG_STATE_DONE) { status = pjmedia_sdp_neg_modify_local_offer2(inv->pool, inv->neg, inv->sdp_neg_flags, sdp); } else return PJMEDIA_SDPNEG_EINSTATE; } else { status = pjmedia_sdp_neg_create_w_local_offer(inv->pool, sdp, &inv->neg); } return status; } /* * Set SDP answer. */ PJ_DEF(pj_status_t) pjsip_inv_set_sdp_answer( pjsip_inv_session *inv, const pjmedia_sdp_session *sdp ) { pj_status_t status; PJ_ASSERT_RETURN(inv && sdp, PJ_EINVAL); pjsip_dlg_inc_lock(inv->dlg); status = pjmedia_sdp_neg_set_local_answer( inv->pool_prov, inv->neg, sdp); pjsip_dlg_dec_lock(inv->dlg); return status; } /* * End session. */ PJ_DEF(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv, int st_code, const pj_str_t *st_text, pjsip_tx_data **p_tdata ) { pjsip_tx_data *tdata; pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL); pj_log_push_indent(); /* Set cause code. */ inv_set_cause(inv, st_code, st_text); /* Create appropriate message. */ switch (inv->state) { case PJSIP_INV_STATE_CALLING: case PJSIP_INV_STATE_EARLY: case PJSIP_INV_STATE_INCOMING: if (inv->role == PJSIP_ROLE_UAC) { /* For UAC when session has not been confirmed, create CANCEL. */ /* MUST have the original UAC INVITE transaction. */ PJ_ASSERT_RETURN(inv->invite_tsx != NULL, PJ_EBUG); /* But CANCEL should only be called when we have received a * provisional response. If we haven't received any responses, * just destroy the transaction. */ if (inv->invite_tsx->status_code < 100) { /* Do not stop INVITE retransmission, see ticket #506 */ //pjsip_tsx_stop_retransmit(inv->invite_tsx); inv->cancelling = PJ_TRUE; inv->pending_cancel = PJ_TRUE; *p_tdata = NULL; PJ_LOG(4, (inv->obj_name, "Delaying CANCEL since no " "provisional response is received yet")); pj_log_pop_indent(); return PJ_SUCCESS; } /* The CSeq here assumes that the dialog is started with an * INVITE session. This may not be correct; dialog can be * started as SUBSCRIBE session. * So fix this! */ status = pjsip_endpt_create_cancel(inv->dlg->endpt, inv->invite_tsx->last_tx, &tdata); if (status != PJ_SUCCESS) { pj_log_pop_indent(); return status; } /* Set timeout for the INVITE transaction, in case UAS is not * able to respond the INVITE with 487 final response. The * timeout value is 64*T1. */ pjsip_tsx_set_timeout(inv->invite_tsx, 64 * pjsip_cfg()->tsx.t1); } else { /* For UAS, send a final response. */ tdata = inv->invite_tsx->last_tx; PJ_ASSERT_RETURN(tdata != NULL, PJ_EINVALIDOP); //status = pjsip_dlg_modify_response(inv->dlg, tdata, st_code, // st_text); status = pjsip_inv_answer(inv, st_code, st_text, NULL, &tdata); } break; case PJSIP_INV_STATE_CONNECTING: case PJSIP_INV_STATE_CONFIRMED: /* End Session Timer */ pjsip_timer_end_session(inv); /* For established dialog, send BYE */ status = pjsip_dlg_create_request(inv->dlg, pjsip_get_bye_method(), -1, &tdata); break; case PJSIP_INV_STATE_DISCONNECTED: /* No need to do anything. */ pj_log_pop_indent(); return PJSIP_ESESSIONTERMINATED; default: pj_assert(!"Invalid operation!"); pj_log_pop_indent(); return PJ_EINVALIDOP; } if (status != PJ_SUCCESS) { pj_log_pop_indent(); return status; } /* Done */ inv->cancelling = PJ_TRUE; *p_tdata = tdata; pj_log_pop_indent(); return PJ_SUCCESS; } /* * Cancel re-INVITE transaction. */ PJ_DEF(pj_status_t) pjsip_inv_cancel_reinvite( pjsip_inv_session *inv, pjsip_tx_data **p_tdata ) { pjsip_tx_data *tdata; pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL); pj_log_push_indent(); /* Create appropriate message. */ switch (inv->state) { case PJSIP_INV_STATE_CONFIRMED: /* MUST have the original UAC INVITE transaction */ PJ_ASSERT_RETURN(inv->invite_tsx != NULL, PJ_EBUG); /* CANCEL should only be called when we have received a * provisional response. */ if (inv->invite_tsx->status_code < 100) { inv->cancelling = PJ_TRUE; inv->pending_cancel = PJ_TRUE; *p_tdata = NULL; PJ_LOG(4, (inv->obj_name, "Delaying CANCEL since no " "provisional response is received yet")); pj_log_pop_indent(); return PJ_SUCCESS; } status = pjsip_endpt_create_cancel(inv->dlg->endpt, inv->invite_tsx->last_tx, &tdata); if (status != PJ_SUCCESS) { pj_log_pop_indent(); return status; } break; default: /* We cannot send CANCEL to a re-INVITE if the INVITE session is * not confirmed. */ pj_log_pop_indent(); return PJ_EINVALIDOP; } pj_log_pop_indent(); *p_tdata = tdata; return PJ_SUCCESS; } /* Following redirection recursion, get next target from the target set and * notify user. * * Returns PJ_FALSE if recursion fails (either because there's no more target * or user rejects the recursion). If we return PJ_FALSE, caller should * disconnect the session. * * Note: * the event 'e' argument may be NULL. */ static pj_bool_t inv_uac_recurse(pjsip_inv_session *inv, int code, const pj_str_t *reason, pjsip_event *e) { pjsip_redirect_op op; pjsip_target *target; /* Won't redirect if the callback is not implemented. */ if (mod_inv.cb.on_redirected == NULL) return PJ_FALSE; if (reason == NULL) reason = pjsip_get_status_text(code); /* Set status of current target */ pjsip_target_assign_status(inv->dlg->target_set.current, inv->dlg->pool, code, reason); /* Fetch next target from the target set. We only want to * process SIP/SIPS URI for now. */ for (;;) { target = pjsip_target_set_get_next(&inv->dlg->target_set); if (target == NULL) { /* No more target. */ return PJ_FALSE; } if (!PJSIP_URI_SCHEME_IS_SIP(target->uri) && !PJSIP_URI_SCHEME_IS_SIPS(target->uri)) { code = PJSIP_SC_UNSUPPORTED_URI_SCHEME; reason = pjsip_get_status_text(code); /* Mark this target as unusable and fetch next target. */ pjsip_target_assign_status(target, inv->dlg->pool, code, reason); } else { /* Found a target */ break; } } /* We have target in 'target'. Set this target as current target * and notify callback. */ pjsip_target_set_set_current(&inv->dlg->target_set, target); op = (*mod_inv.cb.on_redirected)(inv, target->uri, e); /* Check what the application wants to do now */ switch (op) { case PJSIP_REDIRECT_ACCEPT: case PJSIP_REDIRECT_ACCEPT_REPLACE: case PJSIP_REDIRECT_STOP: /* Must increment session counter, that's the convention of the * pjsip_inv_process_redirect(). */ pjsip_dlg_inc_session(inv->dlg, &mod_inv.mod); /* Act on the recursion */ pjsip_inv_process_redirect(inv, op, e); return PJ_TRUE; case PJSIP_REDIRECT_PENDING: /* Increment session so that the dialog/session is not destroyed * while we're waiting for user confirmation. */ pjsip_dlg_inc_session(inv->dlg, &mod_inv.mod); /* Also clear the invite_tsx variable, otherwise when this tsx is * terminated, it will also terminate the session. */ inv->invite_tsx = NULL; /* Done. The processing will continue once the application calls * pjsip_inv_process_redirect(). */ return PJ_TRUE; case PJSIP_REDIRECT_REJECT: /* Recursively call this function again to fetch next target, if any. */ return inv_uac_recurse(inv, PJSIP_SC_REQUEST_TERMINATED, NULL, e); } pj_assert(!"Should not reach here"); return PJ_FALSE; } /* Process redirection/recursion */ PJ_DEF(pj_status_t) pjsip_inv_process_redirect( pjsip_inv_session *inv, pjsip_redirect_op op, pjsip_event *e) { const pjsip_status_code cancel_code = PJSIP_SC_REQUEST_TERMINATED; pjsip_event usr_event; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(inv && op != PJSIP_REDIRECT_PENDING, PJ_EINVAL); if (e == NULL) { PJSIP_EVENT_INIT_USER(usr_event, NULL, NULL, NULL, NULL); e = &usr_event; } pjsip_dlg_inc_lock(inv->dlg); /* Decrement session. That's the convention here to prevent the dialog * or session from being destroyed while we're waiting for user * confirmation. */ pjsip_dlg_dec_session(inv->dlg, &mod_inv.mod); /* See what the application wants to do now */ switch (op) { case PJSIP_REDIRECT_ACCEPT: case PJSIP_REDIRECT_ACCEPT_REPLACE: /* User accept the redirection. Reset the session and resend the * INVITE request. */ { pjsip_tx_data *tdata; pjsip_via_hdr *via; /* Get the original INVITE request. */ tdata = inv->invite_req; pjsip_tx_data_add_ref(tdata); /* Restore strict route set. * See http://trac.pjsip.org/repos/ticket/492 */ pjsip_restore_strict_route_set(tdata); /* Set target */ tdata->msg->line.req.uri = (pjsip_uri*) pjsip_uri_clone(tdata->pool, inv->dlg->target_set.current->uri); /* Remove branch param in Via header. */ via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); via->branch_param.slen = 0; /* Process PJSIP_REDIRECT_ACCEPT_REPLACE */ if (op == PJSIP_REDIRECT_ACCEPT_REPLACE) { pjsip_to_hdr *to; pjsip_dialog *dlg = inv->dlg; enum { TMP_LEN = 128 }; char tmp[TMP_LEN]; int len; /* Replace To header */ to = PJSIP_MSG_TO_HDR(tdata->msg); to->uri = (pjsip_uri*) pjsip_uri_clone(tdata->pool, dlg->target_set.current->uri); to->tag.slen = 0; pj_list_init(&to->other_param); /* Re-init dialog remote info */ dlg->remote.info = (pjsip_to_hdr*) pjsip_hdr_clone(dlg->pool, to); /* Remove header param from remote info */ if (PJSIP_URI_SCHEME_IS_SIP(dlg->remote.info->uri) || PJSIP_URI_SCHEME_IS_SIPS(dlg->remote.info->uri)) { pjsip_sip_uri *sip_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(dlg->remote.info->uri); if (!pj_list_empty(&sip_uri->header_param)) { /* Remove all header param */ pj_list_init(&sip_uri->header_param); } } /* Print the remote info. */ len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, dlg->remote.info->uri, tmp, TMP_LEN); if (len < 1) { pj_ansi_strcpy(tmp, "<-error: uri too long->"); len = (int)pj_ansi_strlen(tmp); } pj_strdup2_with_null(dlg->pool, &dlg->remote.info_str, tmp); /* Secure? */ dlg->secure = PJSIP_URI_SCHEME_IS_SIPS(to->uri); } /* Reset message destination info (see #1248). */ pj_bzero(&tdata->dest_info, sizeof(tdata->dest_info)); /* Must invalidate the message! */ pjsip_tx_data_invalidate_msg(tdata); /* Reset the session */ pjsip_inv_uac_restart(inv, PJ_FALSE); /* (re)Send the INVITE request */ status = pjsip_inv_send_msg(inv, tdata); } break; case PJSIP_REDIRECT_STOP: /* User doesn't want the redirection. Disconnect the session now. */ inv_set_cause(inv, cancel_code, pjsip_get_status_text(cancel_code)); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); /* Caller should expect that the invite session is gone now, so * we don't need to set status to PJSIP_ESESSIONTERMINATED here. */ break; case PJSIP_REDIRECT_REJECT: /* Current target is rejected. Fetch next target if any. */ if (inv_uac_recurse(inv, cancel_code, NULL, NULL) == PJ_FALSE) { inv_set_cause(inv, cancel_code, pjsip_get_status_text(cancel_code)); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); /* Tell caller that the invite session is gone now */ status = PJSIP_ESESSIONTERMINATED; } break; case PJSIP_REDIRECT_PENDING: pj_assert(!"Should not happen"); break; } pjsip_dlg_dec_lock(inv->dlg); return status; } /* * Create re-INVITE. */ PJ_DEF(pj_status_t) pjsip_inv_reinvite( pjsip_inv_session *inv, const pj_str_t *new_contact, const pjmedia_sdp_session *new_offer, pjsip_tx_data **p_tdata ) { pj_status_t status; pjsip_contact_hdr *contact_hdr = NULL; /* Check arguments. */ PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL); /* Must NOT have a pending INVITE transaction */ if (inv->invite_tsx!=NULL) return PJ_EINVALIDOP; pj_log_push_indent(); pjsip_dlg_inc_lock(inv->dlg); if (new_contact) { pj_str_t tmp; const pj_str_t STR_CONTACT = { "Contact", 7 }; pj_strdup_with_null(inv->dlg->pool, &tmp, new_contact); contact_hdr = (pjsip_contact_hdr*) pjsip_parse_hdr(inv->dlg->pool, &STR_CONTACT, tmp.ptr, tmp.slen, NULL); if (!contact_hdr) { status = PJSIP_EINVALIDURI; goto on_return; } } if (new_offer) { if (!inv->neg) { status = pjmedia_sdp_neg_create_w_local_offer(inv->pool, new_offer, &inv->neg); if (status != PJ_SUCCESS) goto on_return; } else switch (pjmedia_sdp_neg_get_state(inv->neg)) { case PJMEDIA_SDP_NEG_STATE_NULL: pj_assert(!"Unexpected SDP neg state NULL"); status = PJ_EBUG; goto on_return; case PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER: PJ_LOG(4,(inv->obj_name, "pjsip_inv_reinvite: already have an offer, new " "offer is ignored")); break; case PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER: status = pjmedia_sdp_neg_set_local_answer(inv->pool_prov, inv->neg, new_offer); if (status != PJ_SUCCESS) goto on_return; break; case PJMEDIA_SDP_NEG_STATE_WAIT_NEGO: PJ_LOG(4,(inv->obj_name, "pjsip_inv_reinvite: SDP in WAIT_NEGO state, new " "offer is ignored")); break; case PJMEDIA_SDP_NEG_STATE_DONE: status = pjmedia_sdp_neg_modify_local_offer2( inv->pool_prov, inv->neg, inv->sdp_neg_flags, new_offer); if (status != PJ_SUCCESS) goto on_return; break; } } if (contact_hdr) inv->dlg->local.contact = contact_hdr; status = pjsip_inv_invite(inv, p_tdata); on_return: pjsip_dlg_dec_lock(inv->dlg); pj_log_pop_indent(); return status; } /* * Create UPDATE. */ PJ_DEF(pj_status_t) pjsip_inv_update ( pjsip_inv_session *inv, const pj_str_t *new_contact, const pjmedia_sdp_session *offer, pjsip_tx_data **p_tdata ) { pjsip_contact_hdr *contact_hdr = NULL; pjsip_tx_data *tdata = NULL; pjmedia_sdp_session *sdp_copy; const pjsip_hdr *hdr; pjsip_supported_hdr *sup_hdr = NULL; pj_status_t status = PJ_SUCCESS; /* Verify arguments. */ PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL); /* Dialog must have been established */ PJ_ASSERT_RETURN(inv->dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED, PJ_EINVALIDOP); /* Invite session must not have been disconnected */ PJ_ASSERT_RETURN(inv->state < PJSIP_INV_STATE_DISCONNECTED, PJ_EINVALIDOP); pj_log_push_indent(); /* Lock dialog. */ pjsip_dlg_inc_lock(inv->dlg); /* Process offer, if any */ if (offer) { if (pjmedia_sdp_neg_get_state(inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE) { PJ_LOG(4,(inv->dlg->obj_name, "Invalid SDP offer/answer state for UPDATE")); status = PJ_EINVALIDOP; goto on_error; } /* Notify negotiator about the new offer. This will fix the offer * with correct SDP origin. */ status = pjmedia_sdp_neg_modify_local_offer2(inv->pool_prov, inv->neg, inv->sdp_neg_flags, offer); if (status != PJ_SUCCESS) goto on_error; /* Retrieve the "fixed" offer from negotiator */ pjmedia_sdp_neg_get_neg_local(inv->neg, &offer); } /* Update Contact if required */ if (new_contact) { pj_str_t tmp; const pj_str_t STR_CONTACT = { "Contact", 7 }; pj_strdup_with_null(inv->dlg->pool, &tmp, new_contact); contact_hdr = (pjsip_contact_hdr*) pjsip_parse_hdr(inv->dlg->pool, &STR_CONTACT, tmp.ptr, tmp.slen, NULL); if (!contact_hdr) { status = PJSIP_EINVALIDURI; goto on_error; } inv->dlg->local.contact = contact_hdr; } /* Create request */ status = pjsip_dlg_create_request(inv->dlg, &pjsip_update_method, -1, &tdata); if (status != PJ_SUCCESS) goto on_error; /* Attach SDP body */ if (offer) { sdp_copy = pjmedia_sdp_session_clone(tdata->pool, offer); pjsip_create_sdp_body(tdata->pool, sdp_copy, &tdata->msg->body); } /* Session Timers spec (RFC 4028) says that Supported header MUST be put * in refresh requests. So here we'll just put the Supported header in * all cases regardless of whether session timers is used or not, just * in case this is a common behavior. */ hdr = pjsip_endpt_get_capability(inv->dlg->endpt, PJSIP_H_SUPPORTED, NULL); if (hdr) { sup_hdr = (pjsip_supported_hdr*) pjsip_hdr_shallow_clone(tdata->pool, hdr); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sup_hdr); } status = pjsip_timer_update_req(inv, tdata); if (status != PJ_SUCCESS) goto on_error; /* Cleanup Allow & Supported headers from disabled extensions */ cleanup_allow_sup_hdr(inv->options, NULL, NULL, sup_hdr); /* Unlock dialog. */ pjsip_dlg_dec_lock(inv->dlg); *p_tdata = tdata; pj_log_pop_indent(); return PJ_SUCCESS; on_error: if (tdata) pjsip_tx_data_dec_ref(tdata); /* Unlock dialog. */ pjsip_dlg_dec_lock(inv->dlg); pj_log_pop_indent(); return status; } /* * Create an ACK request. */ PJ_DEF(pj_status_t) pjsip_inv_create_ack(pjsip_inv_session *inv, int cseq, pjsip_tx_data **p_tdata) { const pjmedia_sdp_session *sdp = NULL; pj_status_t status; PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL); /* Lock dialog. */ pjsip_dlg_inc_lock(inv->dlg); /* Destroy last_ack */ if (inv->last_ack) { pjsip_tx_data_dec_ref(inv->last_ack); inv->last_ack = NULL; } /* Create new ACK request */ status = pjsip_dlg_create_request(inv->dlg, pjsip_get_ack_method(), cseq, &inv->last_ack); if (status != PJ_SUCCESS) { pjsip_dlg_dec_lock(inv->dlg); return status; } /* See if we have pending SDP answer to send */ sdp = inv_has_pending_answer(inv, inv->invite_tsx); if (sdp) { inv->last_ack->msg->body = create_sdp_body(inv->last_ack->pool, sdp); } /* Keep this for subsequent response retransmission */ inv->last_ack_cseq = cseq; pjsip_tx_data_add_ref(inv->last_ack); /* Done */ *p_tdata = inv->last_ack; /* Unlock dialog. */ pjsip_dlg_dec_lock(inv->dlg); return PJ_SUCCESS; } /* * Send a request or response message. */ PJ_DEF(pj_status_t) pjsip_inv_send_msg( pjsip_inv_session *inv, pjsip_tx_data *tdata) { pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(inv && tdata, PJ_EINVAL); pj_log_push_indent(); PJ_LOG(5,(inv->obj_name, "Sending %s", pjsip_tx_data_get_info(tdata))); if (tdata->msg->type == PJSIP_REQUEST_MSG) { struct tsx_inv_data *tsx_inv_data; pjsip_dlg_inc_lock(inv->dlg); /* Check again that we didn't receive incoming re-INVITE */ if (tdata->msg->line.req.method.id==PJSIP_INVITE_METHOD && inv->invite_tsx) { pjsip_tx_data_dec_ref(tdata); pjsip_dlg_dec_lock(inv->dlg); status = PJ_EINVALIDOP; goto on_error; } /* Don't send BYE before ACK is received * http://trac.pjsip.org/repos/ticket/1712 */ if (tdata->msg->line.req.method.id == PJSIP_BYE_METHOD && inv->role == PJSIP_ROLE_UAS && inv->state == PJSIP_INV_STATE_CONNECTING && inv->cause != PJSIP_SC_REQUEST_TIMEOUT && inv->cause != PJSIP_SC_TSX_TRANSPORT_ERROR) { if (inv->pending_bye) pjsip_tx_data_dec_ref(inv->pending_bye); inv->pending_bye = tdata; PJ_LOG(4, (inv->obj_name, "Delaying BYE request until " "ACK is received")); pjsip_dlg_dec_lock(inv->dlg); goto on_return; } /* Associate our data in outgoing invite transaction */ tsx_inv_data = PJ_POOL_ZALLOC_T(inv->pool, struct tsx_inv_data); tsx_inv_data->inv = inv; tsx_inv_data->has_sdp = tx_data_has_sdp(tdata); pjsip_dlg_dec_lock(inv->dlg); status = pjsip_dlg_send_request(inv->dlg, tdata, mod_inv.mod.id, tsx_inv_data); if (status != PJ_SUCCESS) { goto on_error; } } else { pjsip_cseq_hdr *cseq; /* Can only do this to send response to original INVITE * request. */ PJ_ASSERT_RETURN((cseq=(pjsip_cseq_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL))!=NULL && (cseq->cseq == inv->invite_tsx->cseq), PJ_EINVALIDOP); if (inv->options & PJSIP_INV_REQUIRE_100REL) { status = pjsip_100rel_tx_response(inv, tdata); } else { status = pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata); } if (status != PJ_SUCCESS) { goto on_error; } } /* Done */ on_return: pj_log_pop_indent(); return PJ_SUCCESS; on_error: pj_log_pop_indent(); return status; } /* * Respond to incoming CANCEL request. */ static void inv_respond_incoming_cancel(pjsip_inv_session *inv, pjsip_transaction *cancel_tsx, pjsip_event *e) { pjsip_tx_data *tdata; pjsip_transaction *invite_tsx; pjsip_rx_data *rdata; pj_str_t key; pj_status_t status; pj_assert(e->body.tsx_state.type == PJSIP_EVENT_RX_MSG); rdata = e->body.tsx_state.src.rdata; /* https://trac.pjsip.org/repos/ticket/1651 * Special treatment for CANCEL. Since here we're responding to CANCEL * automatically (including 487 to INVITE), application will see the * 200/OK response to CANCEL first in the callback, and then 487 to * INVITE, before the CANCEL request itself. And worse, pjsua application * may not see the CANCEL request at all because by the time the CANCEL * request is reported, call has been disconnected and further events * from the INVITE session has been suppressed. */ if (mod_inv.cb.on_tsx_state_changed && inv->notify) (*mod_inv.cb.on_tsx_state_changed)(inv, cancel_tsx, e); /* See if we have matching INVITE server transaction: */ pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAS, pjsip_get_invite_method(), rdata); invite_tsx = pjsip_tsx_layer_find_tsx(&key, PJ_TRUE); if (invite_tsx == NULL) { /* Invite transaction not found! * Respond CANCEL with 481 (RFC 3261 Section 9.2 page 55) */ status = pjsip_dlg_create_response( inv->dlg, rdata, 481, NULL, &tdata); } else { /* Always answer CANCEL will 200 (OK) regardless of * the state of the INVITE transaction. */ status = pjsip_dlg_create_response( inv->dlg, rdata, 200, NULL, &tdata); } /* See if we have created the response successfully. */ if (status != PJ_SUCCESS) return; /* Send the CANCEL response */ status = pjsip_dlg_send_response(inv->dlg, cancel_tsx, tdata); if (status != PJ_SUCCESS) return; /* See if we need to terminate the UAS INVITE transaction * with 487 (Request Terminated) response. */ if (invite_tsx && invite_tsx->status_code < 200) { pj_assert(invite_tsx->last_tx != NULL); tdata = invite_tsx->last_tx; status = pjsip_dlg_modify_response(inv->dlg, tdata, 487, NULL); if (status == PJ_SUCCESS) { /* Remove the message body */ tdata->msg->body = NULL; if (inv->options & PJSIP_INV_REQUIRE_100REL) { status = pjsip_100rel_tx_response(inv, tdata); } else { status = pjsip_dlg_send_response(inv->dlg, invite_tsx, tdata); } } } if (invite_tsx) pj_grp_lock_release(invite_tsx->grp_lock); } /* * Respond to incoming BYE request. */ static void inv_respond_incoming_bye( pjsip_inv_session *inv, pjsip_transaction *bye_tsx, pjsip_rx_data *rdata, pjsip_event *e ) { pj_status_t status; pjsip_tx_data *tdata; /* Respond BYE with 200: */ status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata); if (status != PJ_SUCCESS) return; status = pjsip_dlg_send_response(inv->dlg, bye_tsx, tdata); if (status != PJ_SUCCESS) return; /* Terminate session: */ if (inv->state != PJSIP_INV_STATE_DISCONNECTED) { inv_set_cause(inv, PJSIP_SC_OK, NULL); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); } } /* * Respond to BYE request. */ static void inv_handle_bye_response( pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_rx_data *rdata, pjsip_event *e ) { pj_status_t status; if (e->body.tsx_state.type != PJSIP_EVENT_RX_MSG) { inv_set_cause(inv, PJSIP_SC_OK, NULL); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); return; } /* Handle 401/407 challenge. */ if (tsx->status_code == 401 || tsx->status_code == 407) { pjsip_tx_data *tdata; status = pjsip_auth_clt_reinit_req( &inv->dlg->auth_sess, rdata, tsx->last_tx, &tdata); if (status != PJ_SUCCESS) { /* Does not have proper credentials. * End the session anyway. */ inv_set_cause(inv, PJSIP_SC_OK, NULL); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); } else { struct tsx_inv_data *tsx_inv_data; tsx_inv_data = (struct tsx_inv_data*)tsx->mod_data[mod_inv.mod.id]; if (tsx_inv_data) tsx_inv_data->retrying = PJ_TRUE; /* Re-send BYE. */ status = pjsip_inv_send_msg(inv, tdata); } } else { /* End the session. */ inv_set_cause(inv, PJSIP_SC_OK, NULL); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); } } /* * Respond to incoming UPDATE request. */ static void inv_respond_incoming_update(pjsip_inv_session *inv, pjsip_event *e) { pjmedia_sdp_neg_state neg_state; pj_status_t status; pjsip_tx_data *tdata = NULL; pjsip_rx_data *rdata; pjsip_status_code st_code; pj_assert(e->type == PJSIP_EVENT_TSX_STATE && e->body.tsx_state.type == PJSIP_EVENT_RX_MSG); rdata = e->body.tsx_state.src.rdata; /* Check routing URI scheme for secure dialog */ if (!inv_check_secure_dlg(inv, e)) return; /* Invoke Session Timers module */ status = pjsip_timer_process_req(inv, rdata, &st_code); if (status != PJ_SUCCESS) { status = pjsip_dlg_create_response(inv->dlg, rdata, st_code, NULL, &tdata); goto on_return; } neg_state = pjmedia_sdp_neg_get_state(inv->neg); /* If UPDATE doesn't contain SDP, just respond with 200/OK. * This is a valid scenario according to session-timer draft. */ if (rdata->msg_info.msg->body == NULL) { status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata); } /* Send 491 if we receive UPDATE while we're waiting for an answer */ else if (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { status = pjsip_dlg_create_response(inv->dlg, rdata, PJSIP_SC_REQUEST_PENDING, NULL, &tdata); } /* Send 500 with Retry-After header set randomly between 0 and 10 if we * receive UPDATE while we haven't sent answer. */ else if (neg_state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER || neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) { pjsip_retry_after_hdr *ra_hdr; int val; status = pjsip_dlg_create_response(inv->dlg, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, &tdata); val = (pj_rand() % 10); ra_hdr = pjsip_retry_after_hdr_create(tdata->pool, val); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ra_hdr); } else { /* We receive new offer from remote */ inv_check_sdp_in_incoming_msg(inv, pjsip_rdata_get_tsx(rdata), rdata); /* Application MUST have supplied the answer by now. * If so, negotiate the SDP. */ neg_state = pjmedia_sdp_neg_get_state(inv->neg); if (neg_state != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO || (status=inv_negotiate_sdp(inv)) != PJ_SUCCESS) { /* Negotiation has failed. If negotiator is still * stuck at non-DONE state, cancel any ongoing offer. */ neg_state = pjmedia_sdp_neg_get_state(inv->neg); if (neg_state != PJMEDIA_SDP_NEG_STATE_DONE) { pjmedia_sdp_neg_cancel_offer(inv->neg); } status = pjsip_dlg_create_response(inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL, &tdata); } else { /* New media has been negotiated successfully, send 200/OK */ status = pjsip_dlg_create_response(inv->dlg, rdata, PJSIP_SC_OK, NULL, &tdata); if (status == PJ_SUCCESS) { const pjmedia_sdp_session *sdp; status = pjmedia_sdp_neg_get_active_local(inv->neg, &sdp); if (status == PJ_SUCCESS) tdata->msg->body = create_sdp_body(tdata->pool, sdp); } } } on_return: /* Invoke Session Timers */ if (status == PJ_SUCCESS) status = pjsip_timer_update_resp(inv, tdata); if (status != PJ_SUCCESS) { if (tdata != NULL) { pjsip_tx_data_dec_ref(tdata); tdata = NULL; } return; } pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata), tdata); } /* * Handle incoming response to UAC UPDATE request. */ static pj_bool_t inv_handle_update_response( pjsip_inv_session *inv, pjsip_event *e) { pjsip_transaction *tsx = e->body.tsx_state.tsx; struct tsx_inv_data *tsx_inv_data; pj_bool_t handled = PJ_FALSE; pj_status_t status = -1; tsx_inv_data = (struct tsx_inv_data*)tsx->mod_data[mod_inv.mod.id]; pj_assert(tsx_inv_data); /* Handle 401/407 challenge. */ if (tsx->state == PJSIP_TSX_STATE_COMPLETED && (tsx->status_code == 401 || tsx->status_code == 407)) { pjsip_tx_data *tdata; status = pjsip_auth_clt_reinit_req( &inv->dlg->auth_sess, e->body.tsx_state.src.rdata, tsx->last_tx, &tdata); if (status != PJ_SUCCESS) { /* Somehow failed. Probably it's not a good idea to terminate * the session since this is just a request within dialog. And * even if we terminate we should send BYE. */ /* inv_set_cause(inv, PJSIP_SC_OK, NULL); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); */ } else { if (tsx_inv_data) tsx_inv_data->retrying = PJ_TRUE; /* Re-send request. */ status = pjsip_inv_send_msg(inv, tdata); } handled = PJ_TRUE; } /* Process 422 response */ else if (tsx->state == PJSIP_TSX_STATE_COMPLETED && tsx->status_code == 422) { status = handle_timer_response(inv, e->body.tsx_state.src.rdata, PJ_FALSE); handled = PJ_TRUE; } /* Process 2xx response */ else if (tsx->state == PJSIP_TSX_STATE_COMPLETED && tsx->status_code/100 == 2) { pjsip_rx_data *rdata = e->body.tsx_state.src.rdata; /* Check routing URI scheme for secure dialog */ if (inv_check_secure_dlg(inv, e)) { status = handle_timer_response(inv, rdata, PJ_FALSE); if (rdata->msg_info.msg->body) { /* Only process remote SDP if we have sent local offer */ if (inv->neg && pjmedia_sdp_neg_get_state(inv->neg) == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { status = inv_check_sdp_in_incoming_msg(inv, tsx, rdata); } else { PJ_LOG(5,(THIS_FILE, "Ignored message body in %s as no " "local offer was sent", pjsip_rx_data_get_info(rdata))); } } } handled = PJ_TRUE; } /* Process 502/503 error */ else if ((tsx->state == PJSIP_TSX_STATE_TERMINATED) && (tsx->status_code == 503 || tsx->status_code == 502)) { status = pjsip_timer_handle_refresh_error(inv, e); handled = PJ_TRUE; } /* Get/attach invite session's transaction data */ else { /* Session-Timer needs to see any error responses, to determine * whether peer supports UPDATE with empty body. */ if (tsx->state == PJSIP_TSX_STATE_COMPLETED && tsx->role == PJSIP_ROLE_UAC) { status = handle_timer_response(inv, e->body.tsx_state.src.rdata, PJ_FALSE); handled = PJ_TRUE; } } /* Cancel the negotiation if we don't get successful negotiation by now, * unless it's authentication challenge and the request is being retried. */ if (pjmedia_sdp_neg_get_state(inv->neg) == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER && tsx_inv_data && tsx_inv_data->sdp_done == PJ_FALSE && !tsx_inv_data->retrying && tsx_inv_data->has_sdp) { pjmedia_sdp_neg_cancel_offer(inv->neg); /* Prevent from us cancelling different offer! */ tsx_inv_data->sdp_done = PJ_TRUE; } return handled; } /* * Handle incoming reliable response. */ static void inv_handle_incoming_reliable_response(pjsip_inv_session *inv, pjsip_rx_data *rdata) { pjsip_tx_data *tdata; const pjmedia_sdp_session *sdp; pj_status_t status; /* Create PRACK */ status = pjsip_100rel_create_prack(inv, rdata, &tdata); if (status != PJ_SUCCESS) return; /* See if we need to attach SDP answer on the PRACK request */ sdp = inv_has_pending_answer(inv, pjsip_rdata_get_tsx(rdata)); if (sdp) { tdata->msg->body = create_sdp_body(tdata->pool, sdp); } /* Send PRACK (must be using 100rel module!) */ pjsip_100rel_send_prack(inv, tdata); } /* * Handle incoming PRACK. */ static void inv_respond_incoming_prack(pjsip_inv_session *inv, pjsip_rx_data *rdata) { pj_status_t status; /* Run through 100rel module to see if we can accept this * PRACK request. The 100rel will send 200/OK to PRACK request. */ status = pjsip_100rel_on_rx_prack(inv, rdata); if (status != PJ_SUCCESS) return; /* Now check for SDP answer in the PRACK request */ if (rdata->msg_info.msg->body) { status = inv_check_sdp_in_incoming_msg(inv, pjsip_rdata_get_tsx(rdata), rdata); } else { /* No SDP body */ status = -1; } /* If SDP negotiation has been successful, also mark the * SDP negotiation flag in the invite transaction to be * done too. */ if (status == PJ_SUCCESS && inv->invite_tsx) { struct tsx_inv_data *tsx_inv_data; /* Get/attach invite session's transaction data */ tsx_inv_data = (struct tsx_inv_data*) inv->invite_tsx->mod_data[mod_inv.mod.id]; if (tsx_inv_data == NULL) { tsx_inv_data = PJ_POOL_ZALLOC_T(inv->invite_tsx->pool, struct tsx_inv_data); tsx_inv_data->inv = inv; tsx_inv_data->has_sdp = PJ_TRUE; inv->invite_tsx->mod_data[mod_inv.mod.id] = tsx_inv_data; } tsx_inv_data->sdp_done = PJ_TRUE; } } /* Ticket #1735: If this is a secure dialog, make sure that any incoming * initial/subsequent INVITE/UPDATE request or the 2xx response to INVITE/ * UPDATE specifies secure Contact and Record-Route headers. */ static pj_bool_t inv_check_secure_dlg(pjsip_inv_session *inv, pjsip_event *e) { pjsip_transaction *tsx = e->body.tsx_state.tsx; pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx); if (pjsip_cfg()->endpt.disable_secure_dlg_check == PJ_FALSE && dlg->secure && e->body.tsx_state.type==PJSIP_EVENT_RX_MSG && ((tsx->role==PJSIP_ROLE_UAC && tsx->status_code/100 == 2) || (tsx->role==PJSIP_ROLE_UAS && tsx->state == PJSIP_TSX_STATE_TRYING)) && (tsx->method.id==PJSIP_INVITE_METHOD || pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0)) { const pjsip_msg *msg = e->body.tsx_state.src.rdata->msg_info.msg; pj_status_t status = PJ_SUCCESS; pjsip_contact_hdr *c; /* Check Contact header */ c = (pjsip_contact_hdr*) pjsip_msg_find_hdr(msg,PJSIP_H_CONTACT, NULL); if (!(c && c->uri && PJSIP_URI_SCHEME_IS_SIPS(c->uri))) status = PJSIP_ESESSIONINSECURE; /* Check top Record-Route header */ if (status == PJ_SUCCESS) { pjsip_rr_hdr *r = (pjsip_rr_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_RECORD_ROUTE, NULL); if (r && !PJSIP_URI_SCHEME_IS_SIPS(&r->name_addr)) { /* Not "sips", check if it is "sip" and has param * "transport=tls". */ if (PJSIP_URI_SCHEME_IS_SIP(&r->name_addr)) { pjsip_sip_uri *sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(r->name_addr.uri); if (pj_stricmp2(&sip_uri->transport_param, "tls")!=0) status = PJSIP_ESESSIONINSECURE; } else { /* Not "sips" nor "sip", treat it as insecure? */ status = PJSIP_ESESSIONINSECURE; } } } if (status == PJSIP_ESESSIONINSECURE) { /* Found non-SIPS scheme in Contact/Record-Route header */ pj_str_t warn_text = pj_str("SIPS Required"); if (tsx->role == PJSIP_ROLE_UAC) { /* If we are UAC, terminate the session */ pjsip_tx_data *bye; PJ_LOG(4,(inv->obj_name, "Secure dialog requires SIPS scheme in Contact and " "Record-Route headers, ending the session")); status = pjsip_inv_end_session(inv, 480, NULL, &bye); if (status == PJ_SUCCESS && bye) { pjsip_warning_hdr *w; w = pjsip_warning_hdr_create(bye->pool, 381, pjsip_endpt_name(dlg->endpt), &warn_text); if (w) pjsip_msg_add_hdr(bye->msg, (pjsip_hdr*)w); status = pjsip_inv_send_msg(inv, bye); } } else { /* If we are UAS, reject the request */ pjsip_rx_data *rdata = e->body.tsx_state.src.rdata; pjsip_tx_data *tdata; PJ_LOG(4,(inv->obj_name, "Secure dialog requires SIPS scheme in Contact and " "Route headers, rejecting the request")); status = pjsip_dlg_create_response(inv->dlg, rdata, 480, NULL, &tdata); if (status == PJ_SUCCESS) { pjsip_warning_hdr *w; w = pjsip_warning_hdr_create(tdata->pool, 381, pjsip_endpt_name(dlg->endpt), &warn_text); if (w) pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)w); pjsip_dlg_send_response(dlg, tsx, tdata); } } return PJ_FALSE; } } return PJ_TRUE; } /* * State NULL is before anything is sent/received. */ static void inv_on_state_null( pjsip_inv_session *inv, pjsip_event *e) { pjsip_transaction *tsx = e->body.tsx_state.tsx; pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx); PJ_ASSERT_ON_FAIL(tsx && dlg, return); if (tsx->method.id == PJSIP_INVITE_METHOD) { /* Keep the initial INVITE transaction. */ if (inv->invite_tsx == NULL) inv->invite_tsx = tsx; if (dlg->role == PJSIP_ROLE_UAC) { /* Save the original INVITE request. * We may need to resend the INVITE if we receive redirection * or session timer too small response. */ if (1) { if (inv->invite_req) { pjsip_tx_data_dec_ref(inv->invite_req); inv->invite_req = NULL; } inv->invite_req = tsx->last_tx; pjsip_tx_data_add_ref(inv->invite_req); } switch (tsx->state) { case PJSIP_TSX_STATE_CALLING: inv_set_state(inv, PJSIP_INV_STATE_CALLING, e); break; default: inv_on_state_calling(inv, e); break; } } else { switch (tsx->state) { case PJSIP_TSX_STATE_TRYING: inv_set_state(inv, PJSIP_INV_STATE_INCOMING, e); break; case PJSIP_TSX_STATE_PROCEEDING: inv_set_state(inv, PJSIP_INV_STATE_INCOMING, e); if (tsx->status_code > 100) inv_set_state(inv, PJSIP_INV_STATE_EARLY, e); break; case PJSIP_TSX_STATE_TERMINATED: /* there is a failure in sending response. */ inv_set_cause(inv, tsx->status_code, &tsx->status_text); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); break; default: inv_on_state_incoming(inv, e); break; } } } else { pj_assert(!"Unexpected transaction type"); } } /* * Generic UAC transaction handler: * - resend request on 401 or 407 response. * - terminate dialog on 408 and 481 response. * - resend request on 422 response. */ static pj_bool_t handle_uac_tsx_response(pjsip_inv_session *inv, pjsip_event *e) { /* RFC 3261 Section 12.2.1.2: * If the response for a request within a dialog is a 481 * (Call/Transaction Does Not Exist) or a 408 (Request Timeout), the UAC * SHOULD terminate the dialog. A UAC SHOULD also terminate a dialog if * no response at all is received for the request (the client * transaction would inform the TU about the timeout.) * * For INVITE initiated dialogs, terminating the dialog consists of * sending a BYE. * * Note: * according to X, this should terminate dialog usage only, not the * dialog. */ pjsip_transaction *tsx = e->body.tsx_state.tsx; pj_assert(tsx->role == PJSIP_UAC_ROLE); /* Note that 481 response to CANCEL does not terminate dialog usage, * but only the transaction. */ if (inv->state != PJSIP_INV_STATE_DISCONNECTED && ((tsx->status_code == PJSIP_SC_CALL_TSX_DOES_NOT_EXIST && tsx->method.id != PJSIP_CANCEL_METHOD) || (inv->state != PJSIP_INV_STATE_CONFIRMED && (tsx->status_code == PJSIP_SC_TSX_TIMEOUT || tsx->status_code == PJSIP_SC_TSX_TRANSPORT_ERROR)))) { pjsip_tx_data *bye; pj_status_t status; inv_set_cause(inv, tsx->status_code, &tsx->status_text); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); /* Send BYE */ status = pjsip_dlg_create_request(inv->dlg, pjsip_get_bye_method(), -1, &bye); if (status == PJ_SUCCESS) { pjsip_inv_send_msg(inv, bye); } return PJ_TRUE; /* Handled */ } /* Handle 401/407 challenge. */ else if (tsx->state == PJSIP_TSX_STATE_COMPLETED && (tsx->status_code == PJSIP_SC_UNAUTHORIZED || tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED)) { pjsip_tx_data *tdata; pj_status_t status; if (tsx->method.id == PJSIP_INVITE_METHOD) inv->invite_tsx = NULL; status = pjsip_auth_clt_reinit_req( &inv->dlg->auth_sess, e->body.tsx_state.src.rdata, tsx->last_tx, &tdata); if (status != PJ_SUCCESS) { /* Somehow failed. Probably it's not a good idea to terminate * the session since this is just a request within dialog. And * even if we terminate we should send BYE. */ /* inv_set_cause(inv, PJSIP_SC_OK, NULL); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); */ } else { struct tsx_inv_data *tsx_inv_data; tsx_inv_data = (struct tsx_inv_data*)tsx->mod_data[mod_inv.mod.id]; if (tsx_inv_data) tsx_inv_data->retrying = PJ_TRUE; /* Re-send request. */ status = pjsip_inv_send_msg(inv, tdata); } return PJ_TRUE; /* Handled */ } /* Handle session timer 422 response. */ else if (tsx->state == PJSIP_TSX_STATE_COMPLETED && tsx->status_code == PJSIP_SC_SESSION_TIMER_TOO_SMALL) { handle_timer_response(inv, e->body.tsx_state.src.rdata, PJ_FALSE); return PJ_TRUE; /* Handled */ } /* Process 502/503 error */ else if ((tsx->state == PJSIP_TSX_STATE_TERMINATED) && (tsx->status_code == 503 || tsx->status_code == 502)) { pjsip_timer_handle_refresh_error(inv, e); return PJ_TRUE; } else { return PJ_FALSE; /* Unhandled */ } } /* Handle call rejection, especially with regard to processing call * redirection. We need to handle the following scenarios: * - 3xx response is received -- see if on_redirected() callback is * implemented. If so, add the Contact URIs in the response to the * target set and notify user. * - 4xx - 6xx resposne is received -- see if we're currently recursing, * if so fetch the next target if any and notify the on_redirected() * callback. * - for other cases -- disconnect the session. */ static void handle_uac_call_rejection(pjsip_inv_session *inv, pjsip_event *e) { pjsip_transaction *tsx = e->body.tsx_state.tsx; pj_status_t status; if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 300)) { if (mod_inv.cb.on_redirected == NULL) { /* Redirection callback is not implemented, disconnect the * call. */ goto terminate_session; } else { const pjsip_msg *res_msg; res_msg = e->body.tsx_state.src.rdata->msg_info.msg; /* Gather all Contact URI's in the response and add them * to target set. The function will take care of removing * duplicate URI's. */ pjsip_target_set_add_from_msg(&inv->dlg->target_set, inv->dlg->pool, res_msg); /* Recurse to alternate targets if application allows us */ if (!inv_uac_recurse(inv, tsx->status_code, &tsx->status_text, e)) { /* Recursion fails, terminate session now */ goto terminate_session; } /* Done */ } } else if ((tsx->status_code==401 || tsx->status_code==407) && !inv->cancelling) { /* Handle authentication failure: * Resend the request with Authorization header. */ pjsip_tx_data *tdata; status = pjsip_auth_clt_reinit_req(&inv->dlg->auth_sess, e->body.tsx_state.src.rdata, tsx->last_tx, &tdata); if (status != PJ_SUCCESS) { /* Does not have proper credentials. If we are currently * recursing, try the next target. Otherwise end the session. */ if (!inv_uac_recurse(inv, tsx->status_code, &tsx->status_text, e)) { /* Recursion fails, terminate session now */ goto terminate_session; } } else { /* Restart session. */ pjsip_inv_uac_restart(inv, PJ_FALSE); /* Send the request. */ status = pjsip_inv_send_msg(inv, tdata); } } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED && tsx->status_code == PJSIP_SC_SESSION_TIMER_TOO_SMALL) { /* Handle session timer 422 response: * Resend the request with requested session timer setting. */ status = handle_timer_response(inv, e->body.tsx_state.src.rdata, PJ_FALSE); if (status != PJ_SUCCESS) goto terminate_session; } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 600)) { /* Global error */ goto terminate_session; } else { /* See if we have alternate target to try */ if (!inv_uac_recurse(inv, tsx->status_code, &tsx->status_text, e)) { /* Recursion fails, terminate session now */ goto terminate_session; } } return; terminate_session: inv_set_cause(inv, tsx->status_code, &tsx->status_text); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); } /* * State CALLING is after sending initial INVITE request but before * any response (with tag) is received. */ static void inv_on_state_calling( pjsip_inv_session *inv, pjsip_event *e) { pjsip_transaction *tsx = e->body.tsx_state.tsx; pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx); pj_status_t status; PJ_ASSERT_ON_FAIL(tsx && dlg, return); if (tsx == inv->invite_tsx) { switch (tsx->state) { case PJSIP_TSX_STATE_CALLING: inv_set_state(inv, PJSIP_INV_STATE_CALLING, e); break; case PJSIP_TSX_STATE_PROCEEDING: if (inv->pending_cancel) { pjsip_tx_data *cancel; inv->pending_cancel = PJ_FALSE; status = pjsip_inv_end_session(inv, 487, NULL, &cancel); if (status == PJ_SUCCESS && cancel) status = pjsip_inv_send_msg(inv, cancel); } if (dlg->remote.info->tag.slen) { inv_set_state(inv, PJSIP_INV_STATE_EARLY, e); inv_check_sdp_in_incoming_msg(inv, tsx, e->body.tsx_state.src.rdata); if (pjsip_100rel_is_reliable(e->body.tsx_state.src.rdata)) { inv_handle_incoming_reliable_response( inv, e->body.tsx_state.src.rdata); } } else { /* Ignore 100 (Trying) response, as it doesn't change * session state. It only ceases retransmissions. */ } break; case PJSIP_TSX_STATE_COMPLETED: if (tsx->status_code/100 == 2) { /* This should not happen. * When transaction receives 2xx, it should be terminated */ pj_assert(0); inv_set_state(inv, PJSIP_INV_STATE_CONNECTING, e); /* Check routing URI scheme for secure dialog */ if (!inv_check_secure_dlg(inv, e)) break; /* Process session timer response. */ status = handle_timer_response(inv, e->body.tsx_state.src.rdata, PJ_TRUE); if (status != PJ_SUCCESS) break; inv_check_sdp_in_incoming_msg(inv, tsx, e->body.tsx_state.src.rdata); } else { handle_uac_call_rejection(inv, e); } break; case PJSIP_TSX_STATE_TERMINATED: /* INVITE transaction can be terminated either because UAC * transaction received 2xx response or because of transport * error. */ if (tsx->status_code/100 == 2) { /* This must be receipt of 2xx response */ pj_assert(e->body.tsx_state.type == PJSIP_EVENT_RX_MSG); /* Set state to CONNECTING */ inv_set_state(inv, PJSIP_INV_STATE_CONNECTING, e); /* Check routing URI scheme for secure dialog */ if (!inv_check_secure_dlg(inv, e)) break; /* Process session timer response. */ status = handle_timer_response(inv, e->body.tsx_state.src.rdata, PJ_TRUE); if (status != PJ_SUCCESS) break; inv_check_sdp_in_incoming_msg(inv, tsx, e->body.tsx_state.src.rdata); /* Send ACK */ inv_send_ack(inv, e); } else { inv_set_cause(inv, tsx->status_code, &tsx->status_text); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); } break; default: break; } } else if (tsx->role == PJSIP_ROLE_UAC) { /* * Handle case when outgoing request is answered with 481 (Call/ * Transaction Does Not Exist), 408, or when it's timed out. In these * cases, disconnect session (i.e. dialog usage only). * Note that 481 response to CANCEL does not terminate dialog usage, * but only the transaction. */ if ((tsx->status_code == PJSIP_SC_CALL_TSX_DOES_NOT_EXIST && tsx->method.id != PJSIP_CANCEL_METHOD) || tsx->status_code == PJSIP_SC_REQUEST_TIMEOUT || tsx->status_code == PJSIP_SC_TSX_TIMEOUT || tsx->status_code == PJSIP_SC_TSX_TRANSPORT_ERROR) { inv_set_cause(inv, tsx->status_code, &tsx->status_text); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); } } else if (tsx->role == PJSIP_ROLE_UAS && tsx->state == PJSIP_TSX_STATE_TRYING && pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0) { /* * Handle a very early UPDATE */ inv_respond_incoming_update(inv, e); } } /* * State INCOMING is after we received the request, but before * responses with tag are sent. */ static void inv_on_state_incoming( pjsip_inv_session *inv, pjsip_event *e) { pjsip_transaction *tsx = e->body.tsx_state.tsx; pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx); PJ_ASSERT_ON_FAIL(tsx && dlg, return); if (tsx == inv->invite_tsx) { /* * Handle the INVITE state transition. */ switch (tsx->state) { case PJSIP_TSX_STATE_TRYING: inv_set_state(inv, PJSIP_INV_STATE_INCOMING, e); break; case PJSIP_TSX_STATE_PROCEEDING: /* * Transaction sent provisional response. */ if (tsx->status_code > 100) inv_set_state(inv, PJSIP_INV_STATE_EARLY, e); break; case PJSIP_TSX_STATE_COMPLETED: /* * Transaction sent final response. */ if (tsx->status_code/100 == 2) { inv_set_state(inv, PJSIP_INV_STATE_CONNECTING, e); } else { inv_set_cause(inv, tsx->status_code, &tsx->status_text); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); } break; case PJSIP_TSX_STATE_TERMINATED: /* * This happens on transport error (e.g. failed to send * response) */ inv_set_cause(inv, tsx->status_code, &tsx->status_text); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); break; default: pj_assert(!"Unexpected INVITE state"); break; } } else if (tsx->method.id == PJSIP_CANCEL_METHOD && tsx->role == PJSIP_ROLE_UAS && tsx->state < PJSIP_TSX_STATE_COMPLETED && e->body.tsx_state.type == PJSIP_EVENT_RX_MSG ) { /* * Handle incoming CANCEL request. */ inv_respond_incoming_cancel(inv, tsx, e); } } /* * State EARLY is for both UAS and UAC, after response with To tag * is sent/received. */ static void inv_on_state_early( pjsip_inv_session *inv, pjsip_event *e) { pjsip_transaction *tsx = e->body.tsx_state.tsx; pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx); PJ_ASSERT_ON_FAIL(tsx && dlg, return); if (tsx == inv->invite_tsx) { /* * Handle the INVITE state progress. */ switch (tsx->state) { case PJSIP_TSX_STATE_PROCEEDING: /* Send/received another provisional response. */ inv_set_state(inv, PJSIP_INV_STATE_EARLY, e); if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { inv_check_sdp_in_incoming_msg(inv, tsx, e->body.tsx_state.src.rdata); if (pjsip_100rel_is_reliable(e->body.tsx_state.src.rdata)) { inv_handle_incoming_reliable_response( inv, e->body.tsx_state.src.rdata); } } break; case PJSIP_TSX_STATE_COMPLETED: if (tsx->status_code/100 == 2) { inv_set_state(inv, PJSIP_INV_STATE_CONNECTING, e); if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { pj_status_t status; /* Check routing URI scheme for secure dialog */ if (!inv_check_secure_dlg(inv, e)) break; /* Process session timer response. */ status = handle_timer_response(inv, e->body.tsx_state.src.rdata, PJ_TRUE); if (status != PJ_SUCCESS) break; inv_check_sdp_in_incoming_msg(inv, tsx, e->body.tsx_state.src.rdata); } } else if (tsx->role == PJSIP_ROLE_UAC) { handle_uac_call_rejection(inv, e); } else { inv_set_cause(inv, tsx->status_code, &tsx->status_text); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); } break; case PJSIP_TSX_STATE_CONFIRMED: /* For some reason can go here (maybe when ACK for 2xx has * the same branch value as the INVITE transaction) */ case PJSIP_TSX_STATE_TERMINATED: /* INVITE transaction can be terminated either because UAC * transaction received 2xx response or because of transport * error. */ if (tsx->status_code/100 == 2) { /* This must be receipt of 2xx response */ /* Set state to CONNECTING */ inv_set_state(inv, PJSIP_INV_STATE_CONNECTING, e); if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { pj_status_t status; /* Check routing URI scheme for secure dialog */ if (!inv_check_secure_dlg(inv, e)) break; /* Process session timer response. */ status = handle_timer_response(inv, e->body.tsx_state.src.rdata, PJ_TRUE); if (status != PJ_SUCCESS) break; inv_check_sdp_in_incoming_msg(inv, tsx, e->body.tsx_state.src.rdata); } /* if UAC, send ACK and move state to confirmed. */ if (tsx->role == PJSIP_ROLE_UAC) { pj_assert(e->body.tsx_state.type == PJSIP_EVENT_RX_MSG); inv_send_ack(inv, e); } } else { inv_set_cause(inv, tsx->status_code, &tsx->status_text); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); } break; default: pj_assert(!"Unexpected INVITE tsx state"); break; } } else if (inv->role == PJSIP_ROLE_UAS && tsx->role == PJSIP_ROLE_UAS && tsx->method.id == PJSIP_CANCEL_METHOD && tsx->state < PJSIP_TSX_STATE_COMPLETED && e->body.tsx_state.type == PJSIP_EVENT_RX_MSG ) { /* * Handle incoming CANCEL request. */ inv_respond_incoming_cancel(inv, tsx, e); } else if (tsx->role == PJSIP_ROLE_UAS && tsx->state == PJSIP_TSX_STATE_TRYING && pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0) { /* * Handle incoming UPDATE */ inv_respond_incoming_update(inv, e); } else if (tsx->role == PJSIP_ROLE_UAC && (tsx->state == PJSIP_TSX_STATE_COMPLETED || tsx->state == PJSIP_TSX_STATE_TERMINATED) && pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0) { /* * Handle response to outgoing UPDATE request. */ inv_handle_update_response(inv, e); } else if (tsx->role == PJSIP_ROLE_UAS && tsx->state == PJSIP_TSX_STATE_TRYING && pjsip_method_cmp(&tsx->method, &pjsip_prack_method)==0) { /* * Handle incoming PRACK */ inv_respond_incoming_prack(inv, e->body.tsx_state.src.rdata); } else if (tsx->role == PJSIP_ROLE_UAC) { /* Generic handling for UAC tsx completion */ handle_uac_tsx_response(inv, e); } else if (tsx->role == PJSIP_ROLE_UAS && tsx->method.id == PJSIP_BYE_METHOD && tsx->status_code < 200 && e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { /* Received BYE before the 2xx/OK response to INVITE. * Assume that the 2xx/OK response is lost and the BYE * arrives earlier. */ inv_respond_incoming_bye(inv, tsx, e->body.tsx_state.src.rdata, e); if (inv->invite_tsx->role == PJSIP_ROLE_UAC) { /* Set timer just in case we will never get the final response * for INVITE. */ pjsip_tsx_set_timeout(inv->invite_tsx, 64*pjsip_cfg()->tsx.t1); } else if (inv->invite_tsx->status_code < 200) { pjsip_tx_data *tdata; pjsip_msg *msg; /* For UAS, send a final response. */ tdata = inv->invite_tsx->last_tx; PJ_ASSERT_ON_FAIL(tdata != NULL, return); msg = tdata->msg; msg->line.status.code = PJSIP_SC_REQUEST_TERMINATED; msg->line.status.reason = *pjsip_get_status_text(PJSIP_SC_REQUEST_TERMINATED); msg->body = NULL; pjsip_tx_data_invalidate_msg(tdata); pjsip_tx_data_add_ref(tdata); pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata); } } } /* * State CONNECTING is after 2xx response to INVITE is sent/received. */ static void inv_on_state_connecting( pjsip_inv_session *inv, pjsip_event *e) { pjsip_transaction *tsx = e->body.tsx_state.tsx; pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx); PJ_ASSERT_ON_FAIL(tsx && dlg, return); if (tsx == inv->invite_tsx) { /* * Handle INVITE state progression. */ switch (tsx->state) { case PJSIP_TSX_STATE_CONFIRMED: /* It can only go here if incoming ACK request has the same Via * branch parameter as the INVITE transaction. */ if (tsx->status_code/100 == 2) { if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { inv_check_sdp_in_incoming_msg(inv, tsx, e->body.tsx_state.src.rdata); } inv_set_state(inv, PJSIP_INV_STATE_CONFIRMED, e); /* Send pending BYE if any: * http://trac.pjsip.org/repos/ticket/1712 * Do this after setting the state to CONFIRMED, so that we * have consistent CONFIRMED state between caller and callee. */ if (inv->pending_bye) inv_perform_pending_bye(inv); } break; case PJSIP_TSX_STATE_TERMINATED: /* INVITE transaction can be terminated either because UAC * transaction received 2xx response or because of transport * error. */ if (tsx->status_code/100 != 2) { if (tsx->role == PJSIP_ROLE_UAC) { inv_set_cause(inv, tsx->status_code, &tsx->status_text); inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); } else { pjsip_tx_data *bye; pj_status_t status; inv_set_cause(inv, tsx->status_code, &tsx->status_text); /* Send BYE */ status = pjsip_dlg_create_request(inv->dlg, pjsip_get_bye_method(), -1, &bye); if (status == PJ_SUCCESS) { pjsip_inv_send_msg(inv, bye); if (inv->pending_bye) { pjsip_tx_data_dec_ref(inv->pending_bye); inv->pending_bye = NULL; } } } } break; case PJSIP_TSX_STATE_DESTROYED: /* Do nothing. */ break; default: pj_assert(!"Unexpected state"); break; } } else if (tsx->role == PJSIP_ROLE_UAS && tsx->method.id == PJSIP_BYE_METHOD && tsx->status_code < 200 && e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { /* * Handle incoming BYE. */ inv_respond_incoming_bye( inv, tsx, e->body.tsx_state.src.rdata, e ); } else if (tsx->method.id == PJSIP_BYE_METHOD && tsx->role == PJSIP_ROLE_UAC && (tsx->state == PJSIP_TSX_STATE_COMPLETED || tsx->state == PJSIP_TSX_STATE_TERMINATED)) { /* * Outgoing BYE */ inv_handle_bye_response( inv, tsx, e->body.tsx_state.src.rdata, e); } else if (tsx->method.id == PJSIP_CANCEL_METHOD && tsx->role == PJSIP_ROLE_UAS && tsx->status_code < 200 && e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { /* * Handle strandled incoming CANCEL or CANCEL for re-INVITE */ inv_respond_incoming_cancel(inv, tsx, e); } else if (tsx->role == PJSIP_ROLE_UAS && tsx->state == PJSIP_TSX_STATE_TRYING && pjsip_method_cmp(&tsx->method, &pjsip_invite_method)==0) { pjsip_rx_data *rdata = e->body.tsx_state.src.rdata; pjsip_tx_data *tdata; pj_status_t status; /* See https://trac.pjsip.org/repos/ticket/1455 * Handle incoming re-INVITE before current INVITE is confirmed. * According to RFC 5407: * - answer with 200 if we don't have pending offer-answer * - answer with 491 if we *have* pending offer-answer * * But unfortunately accepting the re-INVITE would mean we have * two outstanding INVITEs, and we don't support that because * we will get confused when we handle the ACK. */ status = pjsip_dlg_create_response(inv->dlg, rdata, PJSIP_SC_REQUEST_PENDING, NULL, &tdata); if (status != PJ_SUCCESS) return; pjsip_timer_update_resp(inv, tdata); status = pjsip_dlg_send_response(dlg, tsx, tdata); } else if (tsx->role == PJSIP_ROLE_UAS && tsx->state == PJSIP_TSX_STATE_TRYING && pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0) { /* * Handle incoming UPDATE */ inv_respond_incoming_update(inv, e); } else if (tsx->role == PJSIP_ROLE_UAC && (tsx->state == PJSIP_TSX_STATE_COMPLETED || tsx->state == PJSIP_TSX_STATE_TERMINATED) && pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0) { /* * Handle response to outgoing UPDATE request. */ if (inv_handle_update_response(inv, e) == PJ_FALSE) handle_uac_tsx_response(inv, e); } else if (tsx->role == PJSIP_ROLE_UAS && tsx->state == PJSIP_TSX_STATE_TRYING && pjsip_method_cmp(&tsx->method, &pjsip_prack_method)==0) { /* * Handle incoming PRACK */ inv_respond_incoming_prack(inv, e->body.tsx_state.src.rdata); } else if (tsx->role == PJSIP_ROLE_UAC) { /* Generic handling for UAC tsx completion */ handle_uac_tsx_response(inv, e); } } /* * State CONFIRMED is after ACK is sent/received. */ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e) { pjsip_transaction *tsx = e->body.tsx_state.tsx; pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx); PJ_ASSERT_ON_FAIL(tsx && dlg, return); if (tsx->method.id == PJSIP_BYE_METHOD && tsx->role == PJSIP_ROLE_UAC && (tsx->state == PJSIP_TSX_STATE_COMPLETED || tsx->state == PJSIP_TSX_STATE_TERMINATED)) { /* * Outgoing BYE */ inv_handle_bye_response( inv, tsx, e->body.tsx_state.src.rdata, e); } else if (tsx->method.id == PJSIP_BYE_METHOD && tsx->role == PJSIP_ROLE_UAS && tsx->status_code < 200 && e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { /* * Handle incoming BYE. */ inv_respond_incoming_bye( inv, tsx, e->body.tsx_state.src.rdata, e ); } else if (tsx->method.id == PJSIP_CANCEL_METHOD && tsx->role == PJSIP_ROLE_UAS && tsx->status_code < 200 && e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { /* * Handle strandled incoming CANCEL or CANCEL for re-INVITE */ inv_respond_incoming_cancel(inv, tsx, e); } else if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->role == PJSIP_ROLE_UAS) { /* * Handle incoming re-INVITE */ if (tsx->state == PJSIP_TSX_STATE_TRYING) { pjsip_rx_data *rdata = e->body.tsx_state.src.rdata; pjsip_tx_data *tdata; pj_status_t status; pjsip_rdata_sdp_info *sdp_info = NULL; pjsip_status_code st_code; /* Check if we have INVITE pending. */ if (inv->invite_tsx && inv->invite_tsx!=tsx) { int code; pj_str_t reason; reason = pj_str("Another INVITE transaction in progress"); if (inv->invite_tsx->role == PJSIP_ROLE_UAC) code = 491; else code = 500; /* Can not receive re-INVITE while another one is pending. */ status = pjsip_dlg_create_response( inv->dlg, rdata, code, &reason, &tdata); if (status != PJ_SUCCESS) return; if (code == 500) { /* MUST include Retry-After header with random value * between 0-10. */ pjsip_retry_after_hdr *ra_hdr; int val = (pj_rand() % 10); ra_hdr = pjsip_retry_after_hdr_create(tdata->pool, val); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ra_hdr); } status = pjsip_dlg_send_response( inv->dlg, tsx, tdata); return; } /* Check routing URI scheme for secure dialog */ if (!inv_check_secure_dlg(inv, e)) return; /* Save the invite transaction. */ inv->invite_tsx = tsx; /* Process session timers headers in the re-INVITE */ status = pjsip_timer_process_req(inv, rdata, &st_code); if (status != PJ_SUCCESS) { status = pjsip_dlg_create_response(inv->dlg, rdata, st_code, NULL, &tdata); if (status != PJ_SUCCESS) return; pjsip_timer_update_resp(inv, tdata); status = pjsip_dlg_send_response(dlg, tsx, tdata); return; } /* Send 491 if we receive re-INVITE while another offer/answer * negotiation is in progress */ if (pjmedia_sdp_neg_get_state(inv->neg) != PJMEDIA_SDP_NEG_STATE_DONE) { status = pjsip_dlg_create_response(inv->dlg, rdata, PJSIP_SC_REQUEST_PENDING, NULL, &tdata); if (status != PJ_SUCCESS) return; pjsip_timer_update_resp(inv, tdata); status = pjsip_dlg_send_response(dlg, tsx, tdata); return; } /* Process SDP in incoming message. */ status = inv_check_sdp_in_incoming_msg(inv, tsx, rdata); if (status == PJ_SUCCESS && mod_inv.cb.on_rx_reinvite && inv->notify) { pj_status_t rc; sdp_info = pjsip_rdata_get_sdp_info(rdata); rc = (*mod_inv.cb.on_rx_reinvite)(inv, sdp_info->sdp, rdata); if (rc == PJ_SUCCESS) { /* Application will send its own response. * Our job is done. */ PJ_LOG(5,(inv->obj_name, "on_rx_reinvite() returns %d", rc)); return; } /* If application lets us answer the re-INVITE, * application must set the SDP answer with * #pjsip_inv_set_sdp_answer(). */ if (pjmedia_sdp_neg_get_state(inv->neg) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) { status = PJ_EINVALIDOP; } } if (status != PJ_SUCCESS) { pj_bool_t reject_message = PJ_TRUE; if (status == PJMEDIA_SDP_EINSDP) { sdp_info = pjsip_rdata_get_sdp_info(rdata); if (sdp_info->body.ptr == NULL && PJSIP_INV_ACCEPT_UNKNOWN_BODY) { /* Message body is not "application/sdp" */ reject_message = PJ_FALSE; } } if (reject_message) { /* Not Acceptable */ const pjsip_hdr *accept; /* The incoming SDP is unacceptable. If the SDP negotiator * state has just been changed, i.e: DONE -> REMOTE_OFFER, * revert it back. */ if (pjmedia_sdp_neg_get_state(inv->neg) == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER) { pjmedia_sdp_neg_cancel_offer(inv->neg); } status = pjsip_dlg_create_response(inv->dlg, rdata, (status == PJMEDIA_SDP_EINSDP)?415:488, NULL, &tdata); if (status != PJ_SUCCESS) return; accept = pjsip_endpt_get_capability(dlg->endpt, PJSIP_H_ACCEPT, NULL); if (accept) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, accept)); } status = pjsip_dlg_send_response(dlg, tsx, tdata); return; } } /* Create 2xx ANSWER */ status = pjsip_dlg_create_response(dlg, rdata, 200, NULL, &tdata); if (status != PJ_SUCCESS) return; /* If the INVITE request has SDP body, send answer. * Otherwise generate offer from local active SDP. */ if (!sdp_info) sdp_info = pjsip_rdata_get_sdp_info(rdata); if (sdp_info->sdp != NULL) { status = process_answer(inv, 200, tdata, NULL); } else { /* INVITE does not have SDP. * If on_create_offer() callback is implemented, ask app. * to generate an offer, otherwise just send active local * SDP to signal that nothing gets modified. */ pjmedia_sdp_session *sdp = NULL; if (mod_inv.cb.on_create_offer) { (*mod_inv.cb.on_create_offer)(inv, &sdp); if (sdp) { /* Notify negotiator about the new offer. This will * fix the offer with correct SDP origin. */ status = pjmedia_sdp_neg_modify_local_offer2( inv->pool_prov, inv->neg, inv->sdp_neg_flags, sdp); /* Retrieve the "fixed" offer from negotiator */ if (status==PJ_SUCCESS) { const pjmedia_sdp_session *lsdp = NULL; pjmedia_sdp_neg_get_neg_local(inv->neg, &lsdp); sdp = (pjmedia_sdp_session*)lsdp; } } } if (sdp == NULL) { const pjmedia_sdp_session *active_sdp = NULL; status = pjmedia_sdp_neg_send_local_offer(inv->pool_prov, inv->neg, &active_sdp); if (status == PJ_SUCCESS) sdp = (pjmedia_sdp_session*) active_sdp; } if (sdp) { tdata->msg->body = create_sdp_body(tdata->pool, sdp); } } if (status != PJ_SUCCESS) { /* * SDP negotiation has failed. */ pj_status_t rc; pj_str_t reason; /* Delete the 2xx answer */ pjsip_tx_data_dec_ref(tdata); /* Create 500 response */ reason = pj_str("SDP negotiation failed"); rc = pjsip_dlg_create_response(dlg, rdata, 500, &reason, &tdata); if (rc == PJ_SUCCESS) { pjsip_warning_hdr *w; const pj_str_t *endpt_name; endpt_name = pjsip_endpt_name(dlg->endpt); w = pjsip_warning_hdr_create_from_status(tdata->pool, endpt_name, status); if (w) pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)w); pjsip_inv_send_msg(inv, tdata); } return; } /* Invoke Session Timers */ pjsip_timer_update_resp(inv, tdata); /* Send 2xx regardless of the status of negotiation */ status = pjsip_inv_send_msg(inv, tdata); } else if (tsx->state == PJSIP_TSX_STATE_CONFIRMED) { /* This is the case where ACK has the same branch as * the INVITE request. */ if (tsx->status_code/100 == 2 && e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { inv_check_sdp_in_incoming_msg(inv, tsx, e->body.tsx_state.src.rdata); /* Check if local offer got no SDP answer */ if (pjmedia_sdp_neg_get_state(inv->neg)== PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { pjmedia_sdp_neg_cancel_offer(inv->neg); } } } } else if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->role == PJSIP_ROLE_UAC) { /* * Handle outgoing re-INVITE */ if (tsx->state == PJSIP_TSX_STATE_CALLING) { /* Must not have other pending INVITE transaction */ pj_assert(inv->invite_tsx==NULL || tsx==inv->invite_tsx); /* Save pending invite transaction */ inv->invite_tsx = tsx; } else if (tsx->state == PJSIP_TSX_STATE_PROCEEDING) { /* CANCEL the re-INVITE if necessary */ if (inv->pending_cancel) { pj_status_t status; pjsip_tx_data *cancel; inv->pending_cancel = PJ_FALSE; status = pjsip_inv_cancel_reinvite(inv, &cancel); if (status == PJ_SUCCESS && cancel) status = pjsip_inv_send_msg(inv, cancel); } } else if (tsx->state == PJSIP_TSX_STATE_TERMINATED && tsx->status_code/100 == 2) { pj_status_t status; /* Re-INVITE was accepted. */ /* Check routing URI scheme for secure dialog */ if (!inv_check_secure_dlg(inv, e)) return; /* Process session timer response. */ status = handle_timer_response(inv, e->body.tsx_state.src.rdata, PJ_TRUE); if (status != PJ_SUCCESS) return; /* Process SDP */ inv_check_sdp_in_incoming_msg(inv, tsx, e->body.tsx_state.src.rdata); /* Check if local offer got no SDP answer */ if (pjmedia_sdp_neg_get_state(inv->neg)== PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { pjmedia_sdp_neg_cancel_offer(inv->neg); } /* Send ACK */ inv_send_ack(inv, e); } else if (handle_uac_tsx_response(inv, e)) { /* Handle response that terminates dialog */ /* Nothing to do (already handled) */ } else if (tsx->status_code >= 300 && tsx->status_code < 700 && e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) { /* Ticket #1654: do not cancel SDP offer when tsx state changing * from 'completed' to 'terminated', as it should have already * been cancelled when tsx state is 'completed'. */ pjmedia_sdp_neg_state neg_state; struct tsx_inv_data *tsx_inv_data; tsx_inv_data = (struct tsx_inv_data*)tsx->mod_data[mod_inv.mod.id]; /* Outgoing INVITE transaction has failed, cancel SDP nego */ neg_state = pjmedia_sdp_neg_get_state(inv->neg); if (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER && tsx_inv_data->retrying == PJ_FALSE) { pjmedia_sdp_neg_cancel_offer(inv->neg); } if (tsx == inv->invite_tsx) inv->invite_tsx = NULL; } } else if (tsx->role == PJSIP_ROLE_UAS && tsx->state == PJSIP_TSX_STATE_TRYING && pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0) { /* * Handle incoming UPDATE */ inv_respond_incoming_update(inv, e); } else if (tsx->role == PJSIP_ROLE_UAC && (tsx->state == PJSIP_TSX_STATE_COMPLETED || tsx->state == PJSIP_TSX_STATE_TERMINATED) && pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0) { /* * Handle response to outgoing UPDATE request. */ if (inv_handle_update_response(inv, e) == PJ_FALSE) handle_uac_tsx_response(inv, e); } else if (tsx->role == PJSIP_ROLE_UAS && tsx->state == PJSIP_TSX_STATE_TRYING && pjsip_method_cmp(&tsx->method, &pjsip_prack_method)==0) { /* * Handle strandled incoming PRACK */ inv_respond_incoming_prack(inv, e->body.tsx_state.src.rdata); } else if (tsx->role == PJSIP_ROLE_UAC) { /* * Handle 401/407/408/481/422 response */ handle_uac_tsx_response(inv, e); } } /* * After session has been terminated, but before dialog is destroyed * (because dialog has other usages, or because dialog is waiting for * the last transaction to terminate). */ static void inv_on_state_disconnected( pjsip_inv_session *inv, pjsip_event *e) { pjsip_transaction *tsx = e->body.tsx_state.tsx; pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx); PJ_ASSERT_ON_FAIL(tsx && dlg, return); if (tsx->role == PJSIP_ROLE_UAS && tsx->status_code < 200 && e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { pjsip_rx_data *rdata = e->body.tsx_state.src.rdata; /* * Respond BYE with 200/OK */ if (tsx->method.id == PJSIP_BYE_METHOD) { inv_respond_incoming_bye( inv, tsx, rdata, e ); } else if (tsx->method.id == PJSIP_CANCEL_METHOD) { /* * Respond CANCEL with 200/OK too. */ pjsip_tx_data *tdata; pj_status_t status; status = pjsip_dlg_create_response(dlg, rdata, 200, NULL, &tdata); if (status != PJ_SUCCESS) return; status = pjsip_dlg_send_response(dlg, tsx, tdata); if (status != PJ_SUCCESS) return; } } else if (tsx->role == PJSIP_ROLE_UAC) { /* * Handle 401/407/408/481/422 response */ handle_uac_tsx_response(inv, e); } } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-ua/sip_reg.c ================================================ /* $Id: sip_reg.c 4319 2013-01-16 10:20:55Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define REFRESH_TIMER 1 #define DELAY_BEFORE_REFRESH PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH #define THIS_FILE "sip_reg.c" /* Outgoing transaction timeout when server sends 100 but never replies * with final response. Value is in MILISECONDS! */ #define REGC_TSX_TIMEOUT 33000 enum { NOEXP = 0x1FFFFFFF }; static const pj_str_t XUID_PARAM_NAME = { "x-uid", 5 }; /* Current/pending operation */ enum regc_op { REGC_IDLE, REGC_REGISTERING, REGC_UNREGISTERING }; /** * SIP client registration structure. */ struct pjsip_regc { pj_pool_t *pool; pjsip_endpoint *endpt; pj_lock_t *lock; pj_bool_t _delete_flag; pj_bool_t has_tsx; pj_atomic_t *busy_ctr; enum regc_op current_op; pj_bool_t add_xuid_param; void *token; pjsip_regc_cb *cb; pjsip_regc_tsx_cb *tsx_cb; pj_str_t str_srv_url; pjsip_uri *srv_url; pjsip_cid_hdr *cid_hdr; pjsip_cseq_hdr *cseq_hdr; pj_str_t from_uri; pjsip_from_hdr *from_hdr; pjsip_to_hdr *to_hdr; pjsip_contact_hdr contact_hdr_list; pjsip_contact_hdr removed_contact_hdr_list; pjsip_expires_hdr *expires_hdr; pj_uint32_t expires; pj_uint32_t expires_requested; pj_uint32_t delay_before_refresh; pjsip_route_hdr route_set; pjsip_hdr hdr_list; pjsip_host_port via_addr; const void *via_tp; /* Authorization sessions. */ pjsip_auth_clt_sess auth_sess; /* Auto refresh registration. */ pj_bool_t auto_reg; pj_time_val last_reg; pj_time_val next_reg; pj_timer_entry timer; /* Transport selector */ pjsip_tpselector tp_sel; /* Last transport used. We acquire the transport to keep * it open. */ pjsip_transport *last_transport; }; PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token, pjsip_regc_cb *cb, pjsip_regc **p_regc) { pj_pool_t *pool; pjsip_regc *regc; pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL); pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); regc = PJ_POOL_ZALLOC_T(pool, pjsip_regc); regc->pool = pool; regc->endpt = endpt; regc->token = token; regc->cb = cb; regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED; regc->add_xuid_param = pjsip_cfg()->regc.add_xuid_param; status = pj_lock_create_recursive_mutex(pool, pool->obj_name, ®c->lock); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } status = pj_atomic_create(pool, 0, ®c->busy_ctr); if (status != PJ_SUCCESS) { pj_lock_destroy(regc->lock); pj_pool_release(pool); return status; } status = pjsip_auth_clt_init(®c->auth_sess, endpt, regc->pool, 0); if (status != PJ_SUCCESS) return status; pj_list_init(®c->route_set); pj_list_init(®c->hdr_list); pj_list_init(®c->contact_hdr_list); pj_list_init(®c->removed_contact_hdr_list); /* Done */ *p_regc = regc; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc) { PJ_ASSERT_RETURN(regc, PJ_EINVAL); pj_lock_acquire(regc->lock); if (regc->has_tsx || pj_atomic_get(regc->busy_ctr) != 0) { regc->_delete_flag = 1; regc->cb = NULL; pj_lock_release(regc->lock); } else { pjsip_tpselector_dec_ref(®c->tp_sel); if (regc->last_transport) { pjsip_transport_dec_ref(regc->last_transport); regc->last_transport = NULL; } if (regc->timer.id != 0) { pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); regc->timer.id = 0; } pj_atomic_destroy(regc->busy_ctr); pj_lock_release(regc->lock); pj_lock_destroy(regc->lock); regc->lock = NULL; pjsip_endpt_release_pool(regc->endpt, regc->pool); } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc, pjsip_regc_info *info ) { PJ_ASSERT_RETURN(regc && info, PJ_EINVAL); pj_lock_acquire(regc->lock); info->server_uri = regc->str_srv_url; info->client_uri = regc->from_uri; info->is_busy = (pj_atomic_get(regc->busy_ctr) || regc->has_tsx); info->auto_reg = regc->auto_reg; info->interval = regc->expires; info->transport = regc->last_transport; if (regc->has_tsx) info->next_reg = 0; else if (regc->auto_reg == 0) info->next_reg = 0; else if (regc->expires == PJSIP_REGC_EXPIRATION_NOT_SPECIFIED) info->next_reg = regc->expires; else { pj_time_val now, next_reg; next_reg = regc->next_reg; pj_gettimeofday(&now); PJ_TIME_VAL_SUB(next_reg, now); info->next_reg = next_reg.sec; } pj_lock_release(regc->lock); return PJ_SUCCESS; } PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc) { return regc->pool; } static void set_expires( pjsip_regc *regc, pj_uint32_t expires) { if (expires != regc->expires) { regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires); } else { regc->expires_hdr = NULL; } } static pj_status_t set_contact( pjsip_regc *regc, int contact_cnt, const pj_str_t contact[] ) { const pj_str_t CONTACT = { "Contact", 7 }; pjsip_contact_hdr *h; int i; /* Save existing contact list to removed_contact_hdr_list and * clear contact_hdr_list. */ pj_list_merge_last(®c->removed_contact_hdr_list, ®c->contact_hdr_list); /* Set the expiration of Contacts in to removed_contact_hdr_list * zero. */ h = regc->removed_contact_hdr_list.next; while (h != ®c->removed_contact_hdr_list) { h->expires = 0; h = h->next; } /* Process new contacts */ for (i=0; ipool, &tmp, &contact[i]); hdr = (pjsip_contact_hdr*) pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL); if (hdr == NULL) { PJ_LOG(4,(THIS_FILE, "Invalid Contact: \"%.*s\"", (int)tmp.slen, tmp.ptr)); return PJSIP_EINVALIDURI; } /* Find the new contact in old contact list. If found, remove * the old header from the old header list. */ h = regc->removed_contact_hdr_list.next; while (h != ®c->removed_contact_hdr_list) { int rc; rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, h->uri, hdr->uri); if (rc == 0) { /* Match */ pj_list_erase(h); break; } h = h->next; } /* If add_xuid_param option is enabled and Contact URI is sip/sips, * add xuid parameter to assist matching the Contact URI in the * REGISTER response later. */ if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) || PJSIP_URI_SCHEME_IS_SIPS(hdr->uri))) { pjsip_param *xuid_param; pjsip_sip_uri *sip_uri; xuid_param = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param); xuid_param->name = XUID_PARAM_NAME; pj_create_unique_string(regc->pool, &xuid_param->value); sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->uri); pj_list_push_back(&sip_uri->other_param, xuid_param); } pj_list_push_back(®c->contact_hdr_list, hdr); } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc, const pj_str_t *srv_url, const pj_str_t *from_url, const pj_str_t *to_url, int contact_cnt, const pj_str_t contact[], pj_uint32_t expires) { pj_str_t tmp; pj_status_t status; PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url && expires, PJ_EINVAL); /* Copy server URL. */ pj_strdup_with_null(regc->pool, ®c->str_srv_url, srv_url); /* Set server URL. */ tmp = regc->str_srv_url; regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0); if (regc->srv_url == NULL) { return PJSIP_EINVALIDURI; } /* Set "From" header. */ pj_strdup_with_null(regc->pool, ®c->from_uri, from_url); tmp = regc->from_uri; regc->from_hdr = pjsip_from_hdr_create(regc->pool); regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); if (!regc->from_hdr->uri) { PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", from_url->slen, from_url->ptr)); return PJSIP_EINVALIDURI; } /* Set "To" header. */ pj_strdup_with_null(regc->pool, &tmp, to_url); regc->to_hdr = pjsip_to_hdr_create(regc->pool); regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); if (!regc->to_hdr->uri) { PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr)); return PJSIP_EINVALIDURI; } /* Set "Contact" header. */ status = set_contact( regc, contact_cnt, contact); if (status != PJ_SUCCESS) return status; /* Set "Expires" header, if required. */ set_expires( regc, expires); regc->delay_before_refresh = DELAY_BEFORE_REFRESH; /* Set "Call-ID" header. */ regc->cid_hdr = pjsip_cid_hdr_create(regc->pool); pj_create_unique_string(regc->pool, ®c->cid_hdr->id); /* Set "CSeq" header. */ regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool); regc->cseq_hdr->cseq = pj_rand() % 0xFFFF; pjsip_method_set( ®c->cseq_hdr->method, PJSIP_REGISTER_METHOD); /* Done. */ return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc, int count, const pjsip_cred_info cred[] ) { PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL); return pjsip_auth_clt_set_credentials(®c->auth_sess, count, cred); } PJ_DEF(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc, const pjsip_auth_clt_pref *pref) { PJ_ASSERT_RETURN(regc && pref, PJ_EINVAL); return pjsip_auth_clt_set_prefs(®c->auth_sess, pref); } PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc, const pjsip_route_hdr *route_set) { const pjsip_route_hdr *chdr; PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL); pj_list_init(®c->route_set); chdr = route_set->next; while (chdr != route_set) { pj_list_push_back(®c->route_set, pjsip_hdr_clone(regc->pool, chdr)); chdr = chdr->next; } return PJ_SUCCESS; } /* * Bind client registration to a specific transport/listener. */ PJ_DEF(pj_status_t) pjsip_regc_set_transport( pjsip_regc *regc, const pjsip_tpselector *sel) { PJ_ASSERT_RETURN(regc && sel, PJ_EINVAL); pjsip_tpselector_dec_ref(®c->tp_sel); pj_memcpy(®c->tp_sel, sel, sizeof(*sel)); pjsip_tpselector_add_ref(®c->tp_sel); return PJ_SUCCESS; } /* Release transport */ PJ_DEF(pj_status_t) pjsip_regc_release_transport(pjsip_regc *regc) { PJ_ASSERT_RETURN(regc, PJ_EINVAL); if (regc->last_transport) { pjsip_transport_dec_ref(regc->last_transport); regc->last_transport = NULL; } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc, const pjsip_hdr *hdr_list) { const pjsip_hdr *hdr; PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL); //This is "add" operation, so don't remove headers. //pj_list_init(®c->hdr_list); hdr = hdr_list->next; while (hdr != hdr_list) { pj_list_push_back(®c->hdr_list, pjsip_hdr_clone(regc->pool, hdr)); hdr = hdr->next; } return PJ_SUCCESS; } static pj_status_t create_request(pjsip_regc *regc, pjsip_tx_data **p_tdata) { pj_status_t status; pjsip_tx_data *tdata; PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL); /* Create the request. */ status = pjsip_endpt_create_request_from_hdr( regc->endpt, pjsip_get_register_method(), regc->srv_url, regc->from_hdr, regc->to_hdr, NULL, regc->cid_hdr, regc->cseq_hdr->cseq, NULL, &tdata); if (status != PJ_SUCCESS) return status; /* Add cached authorization headers. */ pjsip_auth_clt_init_req( ®c->auth_sess, tdata ); /* Add Route headers from route set, ideally after Via header */ if (!pj_list_empty(®c->route_set)) { pjsip_hdr *route_pos; const pjsip_route_hdr *route; route_pos = (pjsip_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); if (!route_pos) route_pos = &tdata->msg->hdr; route = regc->route_set.next; while (route != ®c->route_set) { pjsip_hdr *new_hdr = (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, route); pj_list_insert_after(route_pos, new_hdr); route_pos = new_hdr; route = route->next; } } /* Add additional request headers */ if (!pj_list_empty(®c->hdr_list)) { const pjsip_hdr *hdr; hdr = regc->hdr_list.next; while (hdr != ®c->hdr_list) { pjsip_hdr *new_hdr = (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, hdr); pjsip_msg_add_hdr(tdata->msg, new_hdr); hdr = hdr->next; } } /* Done. */ *p_tdata = tdata; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg, pjsip_tx_data **p_tdata) { pjsip_msg *msg; pjsip_contact_hdr *hdr; const pjsip_hdr *h_allow; pj_status_t status; pjsip_tx_data *tdata; PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL); pj_lock_acquire(regc->lock); regc->expires_requested = 1; status = create_request(regc, &tdata); if (status != PJ_SUCCESS) { pj_lock_release(regc->lock); return status; } msg = tdata->msg; /* Add Contact headers. */ hdr = regc->contact_hdr_list.next; while (hdr != ®c->contact_hdr_list) { pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, hdr)); hdr = hdr->next; } /* Also add bindings which are to be removed */ while (!pj_list_empty(®c->removed_contact_hdr_list)) { hdr = regc->removed_contact_hdr_list.next; pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr)); pj_list_erase(hdr); } if (regc->expires_hdr) pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, regc->expires_hdr)); if (regc->timer.id != 0) { pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); regc->timer.id = 0; } /* Add Allow header (http://trac.pjsip.org/repos/ticket/1039) */ h_allow = pjsip_endpt_get_capability(regc->endpt, PJSIP_H_ALLOW, NULL); if (h_allow) { pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, h_allow)); } regc->auto_reg = autoreg; pj_lock_release(regc->lock); /* Done */ *p_tdata = tdata; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pjsip_msg *msg; pjsip_hdr *hdr; pj_status_t status; PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL); pj_lock_acquire(regc->lock); if (regc->timer.id != 0) { pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); regc->timer.id = 0; } regc->expires_requested = 0; status = create_request(regc, &tdata); if (status != PJ_SUCCESS) { pj_lock_release(regc->lock); return status; } msg = tdata->msg; /* Add Contact headers. */ hdr = (pjsip_hdr*)regc->contact_hdr_list.next; while ((void*)hdr != (void*)®c->contact_hdr_list) { pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, hdr)); hdr = hdr->next; } /* Also add bindings which are to be removed */ while (!pj_list_empty(®c->removed_contact_hdr_list)) { hdr = (pjsip_hdr*)regc->removed_contact_hdr_list.next; pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr)); pj_list_erase(hdr); } /* Add Expires:0 header */ hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0); pjsip_msg_add_hdr(msg, hdr); pj_lock_release(regc->lock); *p_tdata = tdata; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_regc_unregister_all(pjsip_regc *regc, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pjsip_contact_hdr *hcontact; pjsip_hdr *hdr; pjsip_msg *msg; pj_status_t status; PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL); pj_lock_acquire(regc->lock); if (regc->timer.id != 0) { pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); regc->timer.id = 0; } status = create_request(regc, &tdata); if (status != PJ_SUCCESS) { pj_lock_release(regc->lock); return status; } msg = tdata->msg; /* Clear removed_contact_hdr_list */ pj_list_init(®c->removed_contact_hdr_list); /* Add Contact:* header */ hcontact = pjsip_contact_hdr_create(tdata->pool); hcontact->star = 1; pjsip_msg_add_hdr(msg, (pjsip_hdr*)hcontact); /* Add Expires:0 header */ hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0); pjsip_msg_add_hdr(msg, hdr); pj_lock_release(regc->lock); *p_tdata = tdata; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc, int contact_cnt, const pj_str_t contact[] ) { pj_status_t status; PJ_ASSERT_RETURN(regc, PJ_EINVAL); pj_lock_acquire(regc->lock); status = set_contact( regc, contact_cnt, contact ); pj_lock_release(regc->lock); return status; } PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc, pj_uint32_t expires ) { PJ_ASSERT_RETURN(regc, PJ_EINVAL); pj_lock_acquire(regc->lock); set_expires( regc, expires ); pj_lock_release(regc->lock); return PJ_SUCCESS; } static void cbparam_init( struct pjsip_regc_cbparam *cbparam, pjsip_regc *regc, pj_status_t status, int st_code, const pj_str_t *reason, pjsip_rx_data *rdata, pj_int32_t expiration, int contact_cnt, pjsip_contact_hdr *contact[]) { cbparam->regc = regc; cbparam->token = regc->token; cbparam->status = status; cbparam->code = st_code; cbparam->reason = *reason; cbparam->rdata = rdata; cbparam->contact_cnt = contact_cnt; cbparam->expiration = (expiration >= 0? expiration: regc->expires_requested); if (contact_cnt) { pj_memcpy( cbparam->contact, contact, contact_cnt*sizeof(pjsip_contact_hdr*)); } } static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code, const pj_str_t *reason, pjsip_rx_data *rdata, pj_int32_t expiration, int contact_cnt, pjsip_contact_hdr *contact[]) { struct pjsip_regc_cbparam cbparam; if (!regc->cb) return; cbparam_init(&cbparam, regc, status, st_code, reason, rdata, expiration, contact_cnt, contact); (*regc->cb)(&cbparam); } static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { pjsip_regc *regc = (pjsip_regc*) entry->user_data; pjsip_tx_data *tdata; pj_status_t status; PJ_UNUSED_ARG(timer_heap); /* Temporarily increase busy flag to prevent regc from being deleted * in pjsip_regc_send() or in the callback */ pj_atomic_inc(regc->busy_ctr); entry->id = 0; status = pjsip_regc_register(regc, 1, &tdata); if (status == PJ_SUCCESS) { status = pjsip_regc_send(regc, tdata); } if (status != PJ_SUCCESS && regc->cb) { char errmsg[PJ_ERR_MSG_SIZE]; pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg)); call_callback(regc, status, 400, &reason, NULL, -1, 0, NULL); } /* Delete the record if user destroy regc during the callback. */ if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) { pjsip_regc_destroy(regc); } } static void schedule_registration ( pjsip_regc *regc, pj_int32_t expiration ) { if (regc->auto_reg && expiration > 0) { pj_time_val delay = { 0, 0}; pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(regc->endpt), ®c->timer, 0); delay.sec = expiration - regc->delay_before_refresh; if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED && delay.sec > (pj_int32_t)regc->expires) { delay.sec = regc->expires; } if (delay.sec < DELAY_BEFORE_REFRESH) delay.sec = DELAY_BEFORE_REFRESH; regc->timer.cb = ®c_refresh_timer_cb; regc->timer.id = REFRESH_TIMER; regc->timer.user_data = regc; pjsip_endpt_schedule_timer( regc->endpt, ®c->timer, &delay); pj_gettimeofday(®c->last_reg); regc->next_reg = regc->last_reg; regc->next_reg.sec += delay.sec; } } PJ_DEF(pj_status_t) pjsip_regc_set_reg_tsx_cb( pjsip_regc *regc, pjsip_regc_tsx_cb *tsx_cb) { PJ_ASSERT_RETURN(regc, PJ_EINVAL); regc->tsx_cb = tsx_cb; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_regc_set_via_sent_by( pjsip_regc *regc, pjsip_host_port *via_addr, pjsip_transport *via_tp) { PJ_ASSERT_RETURN(regc, PJ_EINVAL); if (!via_addr) pj_bzero(®c->via_addr, sizeof(regc->via_addr)); else { if (pj_strcmp(®c->via_addr.host, &via_addr->host)) pj_strdup(regc->pool, ®c->via_addr.host, &via_addr->host); regc->via_addr.port = via_addr->port; } regc->via_tp = via_tp; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_regc_set_delay_before_refresh( pjsip_regc *regc, pj_uint32_t delay ) { PJ_ASSERT_RETURN(regc, PJ_EINVAL); if (delay > regc->expires) return PJ_ETOOBIG; pj_lock_acquire(regc->lock); if (regc->delay_before_refresh != delay) { regc->delay_before_refresh = delay; if (regc->timer.id != 0) { /* Cancel registration timer */ pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); regc->timer.id = 0; /* Schedule next registration */ schedule_registration(regc, regc->expires); } } pj_lock_release(regc->lock); return PJ_SUCCESS; } static pj_int32_t calculate_response_expiration(const pjsip_regc *regc, const pjsip_rx_data *rdata, unsigned *contact_cnt, unsigned max_contact, pjsip_contact_hdr *contacts[]) { pj_int32_t expiration = NOEXP; const pjsip_msg *msg = rdata->msg_info.msg; const pjsip_hdr *hdr; /* Enumerate all Contact headers in the response */ *contact_cnt = 0; for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) { if (hdr->type == PJSIP_H_CONTACT && *contact_cnt < max_contact) { contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr; ++(*contact_cnt); } } if (regc->current_op == REGC_REGISTERING) { pj_bool_t has_our_contact = PJ_FALSE; const pjsip_expires_hdr *expires; /* Get Expires header */ expires = (const pjsip_expires_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); /* Try to find the Contact URIs that we register, in the response * to get the expires value. We'll try both with comparing the URI * and comparing the extension param only. */ if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) { unsigned i; for (i=0; i<*contact_cnt; ++i) { const pjsip_contact_hdr *our_hdr; our_hdr = (const pjsip_contact_hdr*) regc->contact_hdr_list.next; /* Match with our Contact header(s) */ while ((void*)our_hdr != (void*)®c->contact_hdr_list) { const pjsip_uri *uri1, *uri2; pj_bool_t matched = PJ_FALSE; /* Exclude the display name when comparing the URI * since server may not return it. */ uri1 = (const pjsip_uri*) pjsip_uri_get_uri(contacts[i]->uri); uri2 = (const pjsip_uri*) pjsip_uri_get_uri(our_hdr->uri); /* First try with exact matching, according to RFC 3261 * Section 19.1.4 URI Comparison */ if (pjsip_cfg()->regc.check_contact) { matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, uri1, uri2)==0; } /* If no match is found, try with matching the extension * parameter only if extension parameter was added. */ if (!matched && regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(uri1) || PJSIP_URI_SCHEME_IS_SIPS(uri1)) && (PJSIP_URI_SCHEME_IS_SIP(uri2) || PJSIP_URI_SCHEME_IS_SIPS(uri2))) { const pjsip_sip_uri *sip_uri1, *sip_uri2; const pjsip_param *p1, *p2; sip_uri1 = (const pjsip_sip_uri*)uri1; sip_uri2 = (const pjsip_sip_uri*)uri2; p1 = pjsip_param_cfind(&sip_uri1->other_param, &XUID_PARAM_NAME); p2 = pjsip_param_cfind(&sip_uri2->other_param, &XUID_PARAM_NAME); matched = p1 && p2 && pj_strcmp(&p1->value, &p2->value)==0; } if (matched) { has_our_contact = PJ_TRUE; if (contacts[i]->expires >= 0 && contacts[i]->expires < expiration) { /* Get the lowest expiration time. */ expiration = contacts[i]->expires; } break; } our_hdr = our_hdr->next; } /* while ((void.. */ } /* for (i=.. */ /* If matching Contact header(s) are found but the * header doesn't contain expires parameter, get the * expiration value from the Expires header. And * if Expires header is not present, get the expiration * value from the request. */ if (has_our_contact && expiration == NOEXP) { if (expires) { expiration = expires->ivalue; } else if (regc->expires_hdr) { expiration = regc->expires_hdr->ivalue; } else { /* We didn't request explicit expiration value, * and server doesn't specify it either. This * shouldn't happen unless we have a broken * registrar. */ expiration = 3600; } } } /* If we still couldn't get matching Contact header(s), it means * there must be something wrong with the registrar (e.g. it may * have modified the URI's in the response, which is prohibited). */ if (expiration==NOEXP) { /* If the number of Contact headers in the response matches * ours, they're all probably ours. Get the expiration * from there if this is the case, or from Expires header * if we don't have exact Contact header count, or * from the request as the last resort. */ pj_size_t our_contact_cnt; our_contact_cnt = pj_list_size(®c->contact_hdr_list); if (*contact_cnt == our_contact_cnt && *contact_cnt && contacts[0]->expires >= 0) { expiration = contacts[0]->expires; } else if (expires) expiration = expires->ivalue; else if (regc->expires_hdr) expiration = regc->expires_hdr->ivalue; else expiration = 3600; } } else { /* Just assume that the unregistration has been successful. */ expiration = 0; } /* Must have expiration value by now */ pj_assert(expiration != NOEXP); return expiration; } static void regc_tsx_callback(void *token, pjsip_event *event) { pj_status_t status; pjsip_regc *regc = (pjsip_regc*) token; pjsip_transaction *tsx = event->body.tsx_state.tsx; pj_bool_t handled = PJ_TRUE; pj_bool_t update_contact = PJ_FALSE; pj_atomic_inc(regc->busy_ctr); pj_lock_acquire(regc->lock); /* Decrement pending transaction counter. */ pj_assert(regc->has_tsx); regc->has_tsx = PJ_FALSE; /* Add reference to the transport */ if (tsx->transport != regc->last_transport) { if (regc->last_transport) { pjsip_transport_dec_ref(regc->last_transport); regc->last_transport = NULL; } if (tsx->transport) { regc->last_transport = tsx->transport; pjsip_transport_add_ref(regc->last_transport); } } if (regc->_delete_flag == 0 && regc->tsx_cb && regc->current_op == REGC_REGISTERING) { struct pjsip_regc_tsx_cb_param param; param.contact_cnt = -1; cbparam_init(¶m.cbparam, regc, PJ_SUCCESS, tsx->status_code, &tsx->status_text, (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? event->body.tsx_state.src.rdata : NULL, -1, 0, NULL); /* Call regc tsx callback before handling any response */ pj_lock_release(regc->lock); (*regc->tsx_cb)(¶m); pj_lock_acquire(regc->lock); if (param.contact_cnt >= 0) { /* Since we receive non-2xx response, it means that (some) contact * bindings haven't been established so we can safely remove these * contact headers. This is to avoid removing non-existent contact * bindings later. */ if (tsx->status_code/100 != 2) { pjsip_contact_hdr *h; h = regc->contact_hdr_list.next; while (h != ®c->contact_hdr_list) { pjsip_contact_hdr *next = h->next; if (h->expires == -1) { pj_list_erase(h); } h = next; } } /* Update contact address */ pjsip_regc_update_contact(regc, param.contact_cnt, param.contact); update_contact = PJ_TRUE; } } /* Handle 401/407 challenge (even when _delete_flag is set) */ if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED || tsx->status_code == PJSIP_SC_UNAUTHORIZED) { pjsip_rx_data *rdata = event->body.tsx_state.src.rdata; pjsip_tx_data *tdata; /* reset current op */ regc->current_op = REGC_IDLE; if (update_contact) { pjsip_msg *msg; pjsip_hdr *hdr, *ins_hdr; pjsip_contact_hdr *chdr; /* Delete Contact headers, but we shouldn't delete headers * which are supposed to remove contact bindings since * we cannot reconstruct those headers. */ msg = tsx->last_tx->msg; hdr = msg->hdr.next; ins_hdr = &msg->hdr; while (hdr != &msg->hdr) { pjsip_hdr *next = hdr->next; if (hdr->type == PJSIP_H_CONTACT) { chdr = (pjsip_contact_hdr *)hdr; if (chdr->expires != 0) { pj_list_erase(hdr); ins_hdr = next; } } hdr = next; } /* Add Contact headers. */ chdr = regc->contact_hdr_list.next; while (chdr != ®c->contact_hdr_list) { pj_list_insert_before(ins_hdr, (pjsip_hdr*) pjsip_hdr_shallow_clone(tsx->last_tx->pool, chdr)); chdr = chdr->next; } /* Also add bindings which are to be removed */ while (!pj_list_empty(®c->removed_contact_hdr_list)) { chdr = regc->removed_contact_hdr_list.next; pj_list_insert_before(ins_hdr, (pjsip_hdr*) pjsip_hdr_clone(tsx->last_tx->pool, chdr)); pj_list_erase(chdr); } } status = pjsip_auth_clt_reinit_req( ®c->auth_sess, rdata, tsx->last_tx, &tdata); if (status == PJ_SUCCESS) { status = pjsip_regc_send(regc, tdata); } if (status != PJ_SUCCESS) { /* Only call callback if application is still interested * in it. */ if (regc->_delete_flag == 0) { /* Should be safe to release the lock temporarily. * We do this to avoid deadlock. */ pj_lock_release(regc->lock); call_callback(regc, status, tsx->status_code, &rdata->msg_info.msg->line.status.reason, rdata, -1, 0, NULL); pj_lock_acquire(regc->lock); } } } else if (regc->_delete_flag) { /* User has called pjsip_regc_destroy(), so don't call callback. * This regc will be destroyed later in this function. */ /* Just reset current op */ regc->current_op = REGC_IDLE; } else if (tsx->status_code == PJSIP_SC_INTERVAL_TOO_BRIEF && regc->current_op == REGC_REGISTERING) { /* Handle 423 response automatically: * - set requested expiration to Min-Expires header, ONLY IF * the original request is a registration (as opposed to * unregistration) and the requested expiration was indeed * lower than Min-Expires) * - resend the request */ pjsip_rx_data *rdata = event->body.tsx_state.src.rdata; pjsip_min_expires_hdr *me_hdr; pjsip_tx_data *tdata; pj_int32_t min_exp; /* reset current op */ regc->current_op = REGC_IDLE; /* Update requested expiration */ me_hdr = (pjsip_min_expires_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_MIN_EXPIRES, NULL); if (me_hdr) { min_exp = me_hdr->ivalue; } else { /* Broken server, Min-Expires doesn't exist. * Just guestimate then, BUT ONLY if if this is the * first time we received such response. */ enum { /* Note: changing this value would require changing couple of * Python test scripts. */ UNSPECIFIED_MIN_EXPIRES = 3601 }; if (!regc->expires_hdr || regc->expires_hdr->ivalue != UNSPECIFIED_MIN_EXPIRES) { min_exp = UNSPECIFIED_MIN_EXPIRES; } else { handled = PJ_FALSE; PJ_LOG(4,(THIS_FILE, "Registration failed: 423 response " "without Min-Expires header is invalid")); goto handle_err; } } if (regc->expires_hdr && regc->expires_hdr->ivalue >= min_exp) { /* But we already send with greater expiration time, why does * the server send us with 423? Oh well, just fail the request. */ handled = PJ_FALSE; PJ_LOG(4,(THIS_FILE, "Registration failed: invalid " "Min-Expires header value in response")); goto handle_err; } set_expires(regc, min_exp); status = pjsip_regc_register(regc, regc->auto_reg, &tdata); if (status == PJ_SUCCESS) { status = pjsip_regc_send(regc, tdata); } if (status != PJ_SUCCESS) { /* Only call callback if application is still interested * in it. */ if (!regc->_delete_flag) { /* Should be safe to release the lock temporarily. * We do this to avoid deadlock. */ pj_lock_release(regc->lock); call_callback(regc, status, tsx->status_code, &rdata->msg_info.msg->line.status.reason, rdata, -1, 0, NULL); pj_lock_acquire(regc->lock); } } } else { handled = PJ_FALSE; } handle_err: if (!handled) { pjsip_rx_data *rdata; pj_int32_t expiration = NOEXP; unsigned contact_cnt = 0; pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT]; if (tsx->status_code/100 == 2) { rdata = event->body.tsx_state.src.rdata; /* Calculate expiration */ expiration = calculate_response_expiration(regc, rdata, &contact_cnt, PJSIP_REGC_MAX_CONTACT, contact); /* Schedule next registration */ schedule_registration(regc, expiration); } else { rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? event->body.tsx_state.src.rdata : NULL; } /* Update registration */ if (expiration==NOEXP) expiration=-1; regc->expires = expiration; /* Mark operation as complete */ regc->current_op = REGC_IDLE; /* Call callback. */ /* Should be safe to release the lock temporarily. * We do this to avoid deadlock. */ pj_lock_release(regc->lock); call_callback(regc, PJ_SUCCESS, tsx->status_code, (rdata ? &rdata->msg_info.msg->line.status.reason : &tsx->status_text), rdata, expiration, contact_cnt, contact); pj_lock_acquire(regc->lock); } pj_lock_release(regc->lock); /* Delete the record if user destroy regc during the callback. */ if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) { pjsip_regc_destroy(regc); } } PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata) { pj_status_t status; pjsip_cseq_hdr *cseq_hdr; pjsip_expires_hdr *expires_hdr; pj_uint32_t cseq; pj_atomic_inc(regc->busy_ctr); pj_lock_acquire(regc->lock); /* Make sure we don't have pending transaction. */ if (regc->has_tsx) { PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another " "transaction pending")); pjsip_tx_data_dec_ref( tdata ); pj_lock_release(regc->lock); pj_atomic_dec(regc->busy_ctr); return PJSIP_EBUSY; } /* Just regc->has_tsx check above should be enough. This assertion check * may cause problem, e.g: when regc_tsx_callback() invokes callback, * lock is released and 'has_tsx' is set to FALSE and 'current_op' has * not been updated to REGC_IDLE yet. */ //pj_assert(regc->current_op == REGC_IDLE); /* Invalidate message buffer. */ pjsip_tx_data_invalidate_msg(tdata); /* Increment CSeq */ cseq = ++regc->cseq_hdr->cseq; cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); cseq_hdr->cseq = cseq; /* Find Expires header */ expires_hdr = (pjsip_expires_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL); /* Bind to transport selector */ pjsip_tx_data_set_transport(tdata, ®c->tp_sel); regc->has_tsx = PJ_TRUE; /* Set current operation based on the value of Expires header */ if (expires_hdr && expires_hdr->ivalue==0) regc->current_op = REGC_UNREGISTERING; else regc->current_op = REGC_REGISTERING; if (expires_hdr && expires_hdr->ivalue) regc->expires_requested = expires_hdr->ivalue; /* Prevent deletion of tdata, e.g: when something wrong in sending, * we need tdata to retrieve the transport. */ pjsip_tx_data_add_ref(tdata); /* If via_addr is set, use this address for the Via header. */ if (regc->via_addr.host.slen > 0) { tdata->via_addr = regc->via_addr; tdata->via_tp = regc->via_tp; } /* Need to unlock the regc temporarily while sending the message to * prevent deadlock (https://trac.pjsip.org/repos/ticket/1247). * It should be safe to do this since the regc's refcount has been * incremented. */ pj_lock_release(regc->lock); /* Now send the message */ status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT, regc, ®c_tsx_callback); if (status!=PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status)); } /* Reacquire the lock */ pj_lock_acquire(regc->lock); /* Get last transport used and add reference to it */ if (tdata->tp_info.transport != regc->last_transport && status==PJ_SUCCESS) { if (regc->last_transport) { pjsip_transport_dec_ref(regc->last_transport); regc->last_transport = NULL; } if (tdata->tp_info.transport) { regc->last_transport = tdata->tp_info.transport; pjsip_transport_add_ref(regc->last_transport); } } /* Release tdata */ pjsip_tx_data_dec_ref(tdata); pj_lock_release(regc->lock); /* Delete the record if user destroy regc during the callback. */ if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) { pjsip_regc_destroy(regc); } return status; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-ua/sip_replaces.c ================================================ /* $Id: sip_replaces.c 4268 2012-09-28 08:56:08Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define THIS_FILE "sip_replaces.c" /* * Replaces header vptr. */ static int replaces_hdr_print( pjsip_replaces_hdr *hdr, char *buf, pj_size_t size); static pjsip_replaces_hdr* replaces_hdr_clone( pj_pool_t *pool, const pjsip_replaces_hdr *hdr); static pjsip_replaces_hdr* replaces_hdr_shallow_clone( pj_pool_t *pool, const pjsip_replaces_hdr*); static pjsip_hdr_vptr replaces_hdr_vptr = { (pjsip_hdr_clone_fptr) &replaces_hdr_clone, (pjsip_hdr_clone_fptr) &replaces_hdr_shallow_clone, (pjsip_hdr_print_fptr) &replaces_hdr_print, }; /* Globals */ static pjsip_endpoint *the_endpt; static pj_bool_t is_initialized; PJ_DEF(pjsip_replaces_hdr*) pjsip_replaces_hdr_create(pj_pool_t *pool) { pjsip_replaces_hdr *hdr = PJ_POOL_ZALLOC_T(pool, pjsip_replaces_hdr); hdr->type = PJSIP_H_OTHER; hdr->name.ptr = "Replaces"; hdr->name.slen = 8; hdr->vptr = &replaces_hdr_vptr; pj_list_init(hdr); pj_list_init(&hdr->other_param); return hdr; } static int replaces_hdr_print( pjsip_replaces_hdr *hdr, char *buf, pj_size_t size) { char *p = buf; char *endbuf = buf+size; pj_ssize_t printed; const pjsip_parser_const_t *pc = pjsip_parser_const(); copy_advance(p, hdr->name); *p++ = ':'; *p++ = ' '; copy_advance(p, hdr->call_id); copy_advance_pair(p, ";to-tag=", 8, hdr->to_tag); copy_advance_pair(p, ";from-tag=", 10, hdr->from_tag); if (hdr->early_only) { const pj_str_t str_early_only = { ";early-only", 11 }; copy_advance(p, str_early_only); } printed = pjsip_param_print_on(&hdr->other_param, p, endbuf-p, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ';'); if (printed < 0) return (int)printed; p += printed; return (int)(p - buf); } static pjsip_replaces_hdr* replaces_hdr_clone( pj_pool_t *pool, const pjsip_replaces_hdr *rhs) { pjsip_replaces_hdr *hdr = pjsip_replaces_hdr_create(pool); pj_strdup(pool, &hdr->call_id, &rhs->call_id); pj_strdup(pool, &hdr->to_tag, &rhs->to_tag); pj_strdup(pool, &hdr->from_tag, &rhs->from_tag); hdr->early_only = rhs->early_only; pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param); return hdr; } static pjsip_replaces_hdr* replaces_hdr_shallow_clone( pj_pool_t *pool, const pjsip_replaces_hdr *rhs ) { pjsip_replaces_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_replaces_hdr); pj_memcpy(hdr, rhs, sizeof(*hdr)); pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param); return hdr; } /* * Parse Replaces header. */ static pjsip_hdr *parse_hdr_replaces(pjsip_parse_ctx *ctx) { pjsip_replaces_hdr *hdr = pjsip_replaces_hdr_create(ctx->pool); const pj_str_t to_tag = { "to-tag", 6 }; const pj_str_t from_tag = { "from-tag", 8 }; const pj_str_t early_only_tag = { "early-only", 10 }; /*pj_scan_get(ctx->scanner, &pjsip_TOKEN_SPEC, &hdr->call_id);*/ /* Get Call-ID (until ';' is found). using pjsip_TOKEN_SPEC doesn't work * because it stops parsing when '@' character is found. */ pj_scan_get_until_ch(ctx->scanner, ';', &hdr->call_id); while (*ctx->scanner->curptr == ';') { pj_str_t pname, pvalue; pj_scan_get_char(ctx->scanner); pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0); if (pj_stricmp(&pname, &to_tag)==0) { hdr->to_tag = pvalue; } else if (pj_stricmp(&pname, &from_tag)==0) { hdr->from_tag = pvalue; } else if (pj_stricmp(&pname, &early_only_tag)==0) { hdr->early_only = PJ_TRUE; } else { pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param); param->name = pname; param->value = pvalue; pj_list_push_back(&hdr->other_param, param); } } pjsip_parse_end_hdr_imp( ctx->scanner ); return (pjsip_hdr*)hdr; } /* Deinitialize Replaces */ static void pjsip_replaces_deinit_module(pjsip_endpoint *endpt) { PJ_TODO(provide_initialized_flag_for_each_endpoint); PJ_UNUSED_ARG(endpt); is_initialized = PJ_FALSE; } /* * Initialize Replaces support in PJSIP. */ PJ_DEF(pj_status_t) pjsip_replaces_init_module(pjsip_endpoint *endpt) { pj_status_t status; const pj_str_t STR_REPLACES = { "replaces", 8 }; the_endpt = endpt; if (is_initialized) return PJ_SUCCESS; /* Register Replaces header parser */ status = pjsip_register_hdr_parser( "Replaces", NULL, &parse_hdr_replaces); if (status != PJ_SUCCESS) return status; /* Register "replaces" capability */ status = pjsip_endpt_add_capability(endpt, NULL, PJSIP_H_SUPPORTED, NULL, 1, &STR_REPLACES); /* Register deinit module to be executed when PJLIB shutdown */ if (pjsip_endpt_atexit(endpt, &pjsip_replaces_deinit_module) != PJ_SUCCESS) { /* Failure to register this function may cause this module won't * work properly when the stack is restarted (without quitting * application). */ pj_assert(!"Failed to register Replaces deinit."); PJ_LOG(1, (THIS_FILE, "Failed to register Replaces deinit.")); } is_initialized = PJ_TRUE; return PJ_SUCCESS; } /* * Verify that incoming request with Replaces header can be processed. */ PJ_DEF(pj_status_t) pjsip_replaces_verify_request( pjsip_rx_data *rdata, pjsip_dialog **p_dlg, pj_bool_t lock_dlg, pjsip_tx_data **p_tdata) { const pj_str_t STR_REPLACES = { "Replaces", 8 }; pjsip_replaces_hdr *rep_hdr; int code = 200; const char *warn_text = NULL; pjsip_hdr res_hdr_list; pjsip_dialog *dlg = NULL; pjsip_inv_session *inv; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(rdata && p_dlg, PJ_EINVAL); /* Check that pjsip_replaces_init_module() has been called. */ PJ_ASSERT_RETURN(the_endpt != NULL, PJ_EINVALIDOP); /* Init output arguments */ *p_dlg = NULL; if (p_tdata) *p_tdata = NULL; pj_list_init(&res_hdr_list); /* Find Replaces header */ rep_hdr = (pjsip_replaces_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_REPLACES, NULL); if (!rep_hdr) { /* No Replaces header. No further processing is necessary. */ return PJ_SUCCESS; } /* Check that there's no other Replaces header and return 400 Bad Request * if not. */ if (pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_REPLACES, rep_hdr->next)) { code = PJSIP_SC_BAD_REQUEST; warn_text = "Found multiple Replaces headers"; goto on_return; } /* Find the dialog identified by Replaces header (and always lock the * dialog no matter what application wants). */ dlg = pjsip_ua_find_dialog(&rep_hdr->call_id, &rep_hdr->to_tag, &rep_hdr->from_tag, PJ_TRUE); /* Respond with 481 "Call/Transaction Does Not Exist" response if * no dialog is found. */ if (dlg == NULL) { code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST; warn_text = "No dialog found for Replaces request"; goto on_return; } /* Get the invite session within the dialog */ inv = pjsip_dlg_get_inv_session(dlg); /* Return 481 if no invite session is present. */ if (inv == NULL) { code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST; warn_text = "No INVITE session found for Replaces request"; goto on_return; } /* Return 603 Declined response if invite session has already * terminated */ if (inv->state >= PJSIP_INV_STATE_DISCONNECTED) { code = PJSIP_SC_DECLINE; warn_text = "INVITE session already terminated"; goto on_return; } /* If "early-only" flag is present, check that the invite session * has not been confirmed yet. If the session has been confirmed, * return 486 "Busy Here" response. */ if (rep_hdr->early_only && inv->state >= PJSIP_INV_STATE_CONNECTING) { code = PJSIP_SC_BUSY_HERE; warn_text = "INVITE session already established"; goto on_return; } /* If the Replaces header field matches an early dialog that was not * initiated by this UA, it returns a 481 (Call/Transaction Does Not * Exist) response to the new INVITE. */ if (inv->state <= PJSIP_INV_STATE_EARLY && inv->role != PJSIP_ROLE_UAC) { /* Really return 481 only if call haven't reached early state or * accept-replace-in-early-state (ticket #1587) is not allowed. */ if (inv->state != PJSIP_INV_STATE_EARLY || pjsip_cfg()->endpt.accept_replace_in_early_state == PJ_FALSE) { code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST; warn_text = "Found early INVITE session but not initiated by " "this UA"; goto on_return; } } /* * Looks like everything is okay!! */ *p_dlg = dlg; status = PJ_SUCCESS; code = 200; on_return: /* Create response if necessary */ if (code != 200) { /* If we have dialog we must unlock it */ if (dlg) pjsip_dlg_dec_lock(dlg); /* Create response */ if (p_tdata) { pjsip_tx_data *tdata; const pjsip_hdr *h; status = pjsip_endpt_create_response(the_endpt, rdata, code, NULL, &tdata); if (status != PJ_SUCCESS) return status; /* Add response headers. */ h = res_hdr_list.next; while (h != &res_hdr_list) { pjsip_hdr *cloned; cloned = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, h); PJ_ASSERT_RETURN(cloned, PJ_ENOMEM); pjsip_msg_add_hdr(tdata->msg, cloned); h = h->next; } /* Add warn text, if any */ if (warn_text) { pjsip_warning_hdr *warn_hdr; pj_str_t warn_value = pj_str((char*)warn_text); warn_hdr=pjsip_warning_hdr_create(tdata->pool, 399, pjsip_endpt_name(the_endpt), &warn_value); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)warn_hdr); } *p_tdata = tdata; } /* Can not return PJ_SUCCESS when response message is produced. * Ref: PROTOS test ~#2490 */ if (status == PJ_SUCCESS) status = PJSIP_ERRNO_FROM_SIP_STATUS(code); } else { /* If application doesn't want to lock the dialog, unlock it */ if (!lock_dlg) pjsip_dlg_dec_lock(dlg); } return status; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-ua/sip_timer.c ================================================ /* $Id: sip_timer.c 4213 2012-07-23 13:31:26Z nanang $ */ /* * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #define THIS_FILE "sip_timer.c" /* Constant of Session Timers */ #define ABS_MIN_SE 90 /* Absolute Min-SE, in seconds */ #define REFRESHER_EXPIRE_TIMER_ID 2 /* Refresher expire timer id */ /* String definitions */ static const pj_str_t STR_SE = {"Session-Expires", 15}; static const pj_str_t STR_SHORT_SE = {"x", 1}; static const pj_str_t STR_MIN_SE = {"Min-SE", 6}; static const pj_str_t STR_REFRESHER = {"refresher", 9}; static const pj_str_t STR_UAC = {"uac", 3}; static const pj_str_t STR_UAS = {"uas", 3}; static const pj_str_t STR_TIMER = {"timer", 5}; /* Enumeration of refresher */ enum timer_refresher { TR_UNKNOWN, TR_UAC, TR_UAS }; /* Structure definition of Session Timers */ struct pjsip_timer { pj_bool_t active; /**< Active/inactive flag */ pjsip_timer_setting setting; /**< Session Timers setting */ enum timer_refresher refresher; /**< Session refresher */ pj_time_val last_refresh; /**< Timestamp of last refresh */ pj_timer_entry timer; /**< Timer entry */ pj_bool_t use_update; /**< Use UPDATE method to refresh the session */ pj_bool_t with_sdp; /**< SDP in UPDATE? */ pjsip_role_e role; /**< Role in last INVITE/ UPDATE transaction. */ void *refresh_tdata; /**< The tdata of refresh request */ pj_timer_entry expire_timer; /**< Timer entry for expire refresher */ }; /* External global vars */ extern pj_bool_t pjsip_use_compact_form; /* Local functions & vars */ static void stop_timer(pjsip_inv_session *inv); static void start_timer(pjsip_inv_session *inv); static pj_bool_t is_initialized; const pjsip_method pjsip_update_method = { PJSIP_OTHER_METHOD, {"UPDATE", 6}}; /* * Session-Expires header vptr. */ static int se_hdr_print(pjsip_sess_expires_hdr *hdr, char *buf, pj_size_t size); static pjsip_sess_expires_hdr* se_hdr_clone(pj_pool_t *pool, const pjsip_sess_expires_hdr *hdr); static pjsip_sess_expires_hdr* se_hdr_shallow_clone( pj_pool_t *pool, const pjsip_sess_expires_hdr* hdr); static pjsip_hdr_vptr se_hdr_vptr = { (pjsip_hdr_clone_fptr) &se_hdr_clone, (pjsip_hdr_clone_fptr) &se_hdr_shallow_clone, (pjsip_hdr_print_fptr) &se_hdr_print, }; /* * Min-SE header vptr. */ static int min_se_hdr_print(pjsip_min_se_hdr *hdr, char *buf, pj_size_t size); static pjsip_min_se_hdr* min_se_hdr_clone(pj_pool_t *pool, const pjsip_min_se_hdr *hdr); static pjsip_min_se_hdr* min_se_hdr_shallow_clone( pj_pool_t *pool, const pjsip_min_se_hdr* hdr); static pjsip_hdr_vptr min_se_hdr_vptr = { (pjsip_hdr_clone_fptr) &min_se_hdr_clone, (pjsip_hdr_clone_fptr) &min_se_hdr_shallow_clone, (pjsip_hdr_print_fptr) &min_se_hdr_print, }; /* * Session-Expires header vptr. */ static int se_hdr_print(pjsip_sess_expires_hdr *hdr, char *buf, pj_size_t size) { char *p = buf; char *endbuf = buf+size; pj_ssize_t printed; const pjsip_parser_const_t *pc = pjsip_parser_const(); const pj_str_t *hname = pjsip_use_compact_form? &hdr->sname : &hdr->name; /* Print header name and value */ if ((endbuf - p) < (hname->slen + 16)) return -1; copy_advance(p, (*hname)); *p++ = ':'; *p++ = ' '; printed = pj_utoa(hdr->sess_expires, p); p += printed; /* Print 'refresher' param */ if (hdr->refresher.slen) { if ((endbuf - p) < (STR_REFRESHER.slen + 2 + hdr->refresher.slen)) return -1; *p++ = ';'; copy_advance(p, STR_REFRESHER); *p++ = '='; copy_advance(p, hdr->refresher); } /* Print generic params */ printed = pjsip_param_print_on(&hdr->other_param, p, endbuf-p, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ';'); if (printed < 0) return (int)printed; p += printed; return (int)(p - buf); } static pjsip_sess_expires_hdr* se_hdr_clone(pj_pool_t *pool, const pjsip_sess_expires_hdr *hsrc) { pjsip_sess_expires_hdr *hdr = pjsip_sess_expires_hdr_create(pool); hdr->sess_expires = hsrc->sess_expires; pj_strdup(pool, &hdr->refresher, &hsrc->refresher); pjsip_param_clone(pool, &hdr->other_param, &hsrc->other_param); return hdr; } static pjsip_sess_expires_hdr* se_hdr_shallow_clone( pj_pool_t *pool, const pjsip_sess_expires_hdr* hsrc) { pjsip_sess_expires_hdr *hdr = PJ_POOL_ALLOC_T(pool,pjsip_sess_expires_hdr); pj_memcpy(hdr, hsrc, sizeof(*hdr)); pjsip_param_shallow_clone(pool, &hdr->other_param, &hsrc->other_param); return hdr; } /* * Min-SE header vptr. */ static int min_se_hdr_print(pjsip_min_se_hdr *hdr, char *buf, pj_size_t size) { char *p = buf; char *endbuf = buf+size; pj_ssize_t printed; const pjsip_parser_const_t *pc = pjsip_parser_const(); /* Print header name and value */ if ((endbuf - p) < (hdr->name.slen + 16)) return -1; copy_advance(p, hdr->name); *p++ = ':'; *p++ = ' '; printed = pj_utoa(hdr->min_se, p); p += printed; /* Print generic params */ printed = pjsip_param_print_on(&hdr->other_param, p, endbuf-p, &pc->pjsip_TOKEN_SPEC, &pc->pjsip_TOKEN_SPEC, ';'); if (printed < 0) return (int)printed; p += printed; return (int)(p - buf); } static pjsip_min_se_hdr* min_se_hdr_clone(pj_pool_t *pool, const pjsip_min_se_hdr *hsrc) { pjsip_min_se_hdr *hdr = pjsip_min_se_hdr_create(pool); hdr->min_se = hsrc->min_se; pjsip_param_clone(pool, &hdr->other_param, &hsrc->other_param); return hdr; } static pjsip_min_se_hdr* min_se_hdr_shallow_clone( pj_pool_t *pool, const pjsip_min_se_hdr* hsrc) { pjsip_min_se_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_min_se_hdr); pj_memcpy(hdr, hsrc, sizeof(*hdr)); pjsip_param_shallow_clone(pool, &hdr->other_param, &hsrc->other_param); return hdr; } /* * Parse Session-Expires header. */ static pjsip_hdr *parse_hdr_se(pjsip_parse_ctx *ctx) { pjsip_sess_expires_hdr *hdr = pjsip_sess_expires_hdr_create(ctx->pool); const pjsip_parser_const_t *pc = pjsip_parser_const(); pj_str_t token; pj_scan_get(ctx->scanner, &pc->pjsip_DIGIT_SPEC, &token); hdr->sess_expires = pj_strtoul(&token); while (*ctx->scanner->curptr == ';') { pj_str_t pname, pvalue; pj_scan_get_char(ctx->scanner); pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0); if (pj_stricmp(&pname, &STR_REFRESHER)==0) { hdr->refresher = pvalue; } else { pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param); param->name = pname; param->value = pvalue; pj_list_push_back(&hdr->other_param, param); } } pjsip_parse_end_hdr_imp( ctx->scanner ); return (pjsip_hdr*)hdr; } /* * Parse Min-SE header. */ static pjsip_hdr *parse_hdr_min_se(pjsip_parse_ctx *ctx) { pjsip_min_se_hdr *hdr = pjsip_min_se_hdr_create(ctx->pool); const pjsip_parser_const_t *pc = pjsip_parser_const(); pj_str_t token; pj_scan_get(ctx->scanner, &pc->pjsip_DIGIT_SPEC, &token); hdr->min_se = pj_strtoul(&token); while (*ctx->scanner->curptr == ';') { pj_str_t pname, pvalue; pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param); pj_scan_get_char(ctx->scanner); pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0); param->name = pname; param->value = pvalue; pj_list_push_back(&hdr->other_param, param); } pjsip_parse_end_hdr_imp( ctx->scanner ); return (pjsip_hdr*)hdr; } /* Add "Session-Expires" and "Min-SE" headers. Note that "Min-SE" header * can only be added to INVITE/UPDATE request and 422 response. */ static void add_timer_headers(pjsip_inv_session *inv, pjsip_tx_data *tdata, pj_bool_t add_se, pj_bool_t add_min_se) { pjsip_timer *timer = inv->timer; /* Add Session-Expires header */ if (add_se) { pjsip_sess_expires_hdr *hdr; hdr = pjsip_sess_expires_hdr_create(tdata->pool); hdr->sess_expires = timer->setting.sess_expires; if (timer->refresher != TR_UNKNOWN) hdr->refresher = (timer->refresher == TR_UAC? STR_UAC : STR_UAS); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) hdr); } /* Add Min-SE header */ if (add_min_se) { pjsip_min_se_hdr *hdr; hdr = pjsip_min_se_hdr_create(tdata->pool); hdr->min_se = timer->setting.min_se; pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) hdr); } } /* Timer callback. When the timer is fired, it can be time to refresh * the session if UA is the refresher, otherwise it is time to end * the session. */ static void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { pjsip_inv_session *inv = (pjsip_inv_session*) entry->user_data; pjsip_tx_data *tdata = NULL; pj_status_t status; pj_bool_t as_refresher; pj_assert(inv); PJ_UNUSED_ARG(timer_heap); /* Lock dialog. */ pjsip_dlg_inc_lock(inv->dlg); /* Check our role */ as_refresher = (inv->timer->refresher == TR_UAC && inv->timer->role == PJSIP_ROLE_UAC) || (inv->timer->refresher == TR_UAS && inv->timer->role == PJSIP_ROLE_UAS); /* Do action based on role(refresher or refreshee). * As refresher: * - send refresh, or * - end session if there is no response to the refresh request. * As refreshee: * - end session if there is no refresh request received. */ if (as_refresher && (entry->id != REFRESHER_EXPIRE_TIMER_ID)) { pj_time_val now; /* As refresher, reshedule the refresh request on the following: * - must not send re-INVITE if another INVITE or SDP negotiation * is in progress. * - must not send UPDATE with SDP if SDP negotiation is in progress */ pjmedia_sdp_neg_state neg_state = pjmedia_sdp_neg_get_state(inv->neg); inv->timer->timer.id = 0; if ( (!inv->timer->use_update && ( inv->invite_tsx != NULL || neg_state != PJMEDIA_SDP_NEG_STATE_DONE) ) || (inv->timer->use_update && inv->timer->with_sdp && neg_state != PJMEDIA_SDP_NEG_STATE_DONE ) ) { pj_time_val delay = {1, 0}; inv->timer->timer.id = 1; pjsip_endpt_schedule_timer(inv->dlg->endpt, &inv->timer->timer, &delay); pjsip_dlg_dec_lock(inv->dlg); return; } /* Refresher, refresh the session */ if (inv->timer->use_update) { const pjmedia_sdp_session *offer = NULL; if (inv->timer->with_sdp) { pjmedia_sdp_neg_get_active_local(inv->neg, &offer); } status = pjsip_inv_update(inv, NULL, offer, &tdata); } else { /* Create re-INVITE without modifying session */ pjsip_msg_body *body; const pjmedia_sdp_session *offer = NULL; pj_assert(pjmedia_sdp_neg_get_state(inv->neg) == PJMEDIA_SDP_NEG_STATE_DONE); status = pjsip_inv_invite(inv, &tdata); if (status == PJ_SUCCESS) status = pjmedia_sdp_neg_send_local_offer(inv->pool_prov, inv->neg, &offer); if (status == PJ_SUCCESS) status = pjmedia_sdp_neg_get_neg_local(inv->neg, &offer); if (status == PJ_SUCCESS) { status = pjsip_create_sdp_body(tdata->pool, (pjmedia_sdp_session*)offer, &body); tdata->msg->body = body; } } pj_gettimeofday(&now); PJ_LOG(4, (inv->pool->obj_name, "Refreshing session after %ds (expiration period=%ds)", (now.sec-inv->timer->last_refresh.sec), inv->timer->setting.sess_expires)); } else { pj_time_val now; if (as_refresher) inv->timer->expire_timer.id = 0; else inv->timer->timer.id = 0; /* Terminate the session */ status = pjsip_inv_end_session(inv, PJSIP_SC_REQUEST_TIMEOUT, NULL, &tdata); pj_gettimeofday(&now); PJ_LOG(3, (inv->pool->obj_name, "No session %s received after %ds " "(expiration period=%ds), stopping session now!", (as_refresher?"refresh response":"refresh"), (now.sec-inv->timer->last_refresh.sec), inv->timer->setting.sess_expires)); } /* Unlock dialog. */ pjsip_dlg_dec_lock(inv->dlg); /* Send message, if any */ if (tdata && status == PJ_SUCCESS) { inv->timer->refresh_tdata = tdata; status = pjsip_inv_send_msg(inv, tdata); } /* Print error message, if any */ if (status != PJ_SUCCESS) { PJ_PERROR(2, (inv->pool->obj_name, status, "Error in %s session timer", ((as_refresher && entry->id != REFRESHER_EXPIRE_TIMER_ID)? "refreshing" : "terminating"))); } } /* Start Session Timers */ static void start_timer(pjsip_inv_session *inv) { const pj_str_t UPDATE = { "UPDATE", 6 }; pjsip_timer *timer = inv->timer; pj_time_val delay = {0}; pj_assert(inv->timer->active == PJ_TRUE); stop_timer(inv); inv->timer->use_update = (pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &UPDATE) == PJSIP_DIALOG_CAP_SUPPORTED); if (!inv->timer->use_update) { /* INVITE always needs SDP */ inv->timer->with_sdp = PJ_TRUE; } pj_timer_entry_init(&timer->timer, 1, /* id */ inv, /* user data */ timer_cb); /* callback */ /* Set delay based on role, refresher or refreshee */ if ((timer->refresher == TR_UAC && inv->timer->role == PJSIP_ROLE_UAC) || (timer->refresher == TR_UAS && inv->timer->role == PJSIP_ROLE_UAS)) { /* Add refresher expire timer */ pj_timer_entry_init(&timer->expire_timer, REFRESHER_EXPIRE_TIMER_ID, /* id */ inv, /* user data */ timer_cb); /* callback */ delay.sec = timer->setting.sess_expires; /* Schedule the timer */ pjsip_endpt_schedule_timer(inv->dlg->endpt, &timer->expire_timer, &delay); /* Next refresh, the delay is half of session expire */ delay.sec = timer->setting.sess_expires / 2; } else { /* Send BYE if no refresh received until this timer fired, delay * is the minimum of 32 seconds and one third of the session interval * before session expiration. */ delay.sec = timer->setting.sess_expires - timer->setting.sess_expires/3; delay.sec = PJ_MAX((long)timer->setting.sess_expires-32, delay.sec); } /* Schedule the timer */ pjsip_endpt_schedule_timer(inv->dlg->endpt, &timer->timer, &delay); /* Update last refresh time */ pj_gettimeofday(&timer->last_refresh); } /* Stop Session Timers */ static void stop_timer(pjsip_inv_session *inv) { if (inv->timer->timer.id != 0) { pjsip_endpt_cancel_timer(inv->dlg->endpt, &inv->timer->timer); inv->timer->timer.id = 0; } if (inv->timer->expire_timer.id != 0) { pjsip_endpt_cancel_timer(inv->dlg->endpt, &inv->timer->expire_timer); inv->timer->expire_timer.id = 0; } } /* Deinitialize Session Timers */ static void pjsip_timer_deinit_module(pjsip_endpoint *endpt) { PJ_TODO(provide_initialized_flag_for_each_endpoint); PJ_UNUSED_ARG(endpt); is_initialized = PJ_FALSE; } /* * Initialize Session Timers support in PJSIP. */ PJ_DEF(pj_status_t) pjsip_timer_init_module(pjsip_endpoint *endpt) { pj_status_t status; PJ_ASSERT_RETURN(endpt, PJ_EINVAL); if (is_initialized) return PJ_SUCCESS; /* Register Session-Expires header parser */ status = pjsip_register_hdr_parser( STR_SE.ptr, STR_SHORT_SE.ptr, &parse_hdr_se); if (status != PJ_SUCCESS) return status; /* Register Min-SE header parser */ status = pjsip_register_hdr_parser( STR_MIN_SE.ptr, NULL, &parse_hdr_min_se); if (status != PJ_SUCCESS) return status; /* Register 'timer' capability to endpoint */ status = pjsip_endpt_add_capability(endpt, NULL, PJSIP_H_SUPPORTED, NULL, 1, &STR_TIMER); if (status != PJ_SUCCESS) return status; /* Register deinit module to be executed when PJLIB shutdown */ if (pjsip_endpt_atexit(endpt, &pjsip_timer_deinit_module) != PJ_SUCCESS) { /* Failure to register this function may cause this module won't * work properly when the stack is restarted (without quitting * application). */ pj_assert(!"Failed to register Session Timer deinit."); PJ_LOG(1, (THIS_FILE, "Failed to register Session Timer deinit.")); } is_initialized = PJ_TRUE; return PJ_SUCCESS; } /* * Initialize Session Timers setting with default values. */ PJ_DEF(pj_status_t) pjsip_timer_setting_default(pjsip_timer_setting *setting) { pj_bzero(setting, sizeof(pjsip_timer_setting)); setting->sess_expires = PJSIP_SESS_TIMER_DEF_SE; setting->min_se = ABS_MIN_SE; return PJ_SUCCESS; } /* * Initialize Session Timers in an INVITE session. */ PJ_DEF(pj_status_t) pjsip_timer_init_session( pjsip_inv_session *inv, const pjsip_timer_setting *setting) { pjsip_timer_setting *s; pj_assert(is_initialized); PJ_ASSERT_RETURN(inv, PJ_EINVAL); /* Allocate and/or reset Session Timers structure */ if (!inv->timer) inv->timer = PJ_POOL_ZALLOC_T(inv->pool, pjsip_timer); else pj_bzero(inv->timer, sizeof(pjsip_timer)); s = &inv->timer->setting; /* Init Session Timers setting */ if (setting) { PJ_ASSERT_RETURN(setting->min_se >= ABS_MIN_SE, PJ_ETOOSMALL); PJ_ASSERT_RETURN(setting->sess_expires >= setting->min_se, PJ_EINVAL); pj_memcpy(s, setting, sizeof(*s)); } else { pjsip_timer_setting_default(s); } return PJ_SUCCESS; } /* * Create Session-Expires header. */ PJ_DEF(pjsip_sess_expires_hdr*) pjsip_sess_expires_hdr_create( pj_pool_t *pool) { pjsip_sess_expires_hdr *hdr = PJ_POOL_ZALLOC_T(pool, pjsip_sess_expires_hdr); pj_assert(is_initialized); hdr->type = PJSIP_H_OTHER; hdr->name = STR_SE; hdr->sname = STR_SHORT_SE; hdr->vptr = &se_hdr_vptr; pj_list_init(hdr); pj_list_init(&hdr->other_param); return hdr; } /* * Create Min-SE header. */ PJ_DEF(pjsip_min_se_hdr*) pjsip_min_se_hdr_create(pj_pool_t *pool) { pjsip_min_se_hdr *hdr = PJ_POOL_ZALLOC_T(pool, pjsip_min_se_hdr); pj_assert(is_initialized); hdr->type = PJSIP_H_OTHER; hdr->name = STR_MIN_SE; hdr->vptr = &min_se_hdr_vptr; pj_list_init(hdr); pj_list_init(&hdr->other_param); return hdr; } /* * This function generates headers for Session Timers for intial and * refresh INVITE or UPDATE. */ PJ_DEF(pj_status_t) pjsip_timer_update_req(pjsip_inv_session *inv, pjsip_tx_data *tdata) { PJ_ASSERT_RETURN(inv && tdata, PJ_EINVAL); /* Check if Session Timers is supported */ if ((inv->options & PJSIP_INV_SUPPORT_TIMER) == 0) return PJ_SUCCESS; pj_assert(is_initialized); /* Make sure Session Timers is initialized */ if (inv->timer == NULL) pjsip_timer_init_session(inv, NULL); /* If refresher role (i.e: ours or peer) has been set/negotiated, * better to keep it. */ if (inv->timer->refresher != TR_UNKNOWN) { pj_bool_t as_refresher; /* Check our refresher role */ as_refresher = (inv->timer->refresher==TR_UAC && inv->timer->role==PJSIP_ROLE_UAC) || (inv->timer->refresher==TR_UAS && inv->timer->role==PJSIP_ROLE_UAS); /* Update transaction role */ inv->timer->role = PJSIP_ROLE_UAC; /* Update refresher role */ inv->timer->refresher = as_refresher? TR_UAC : TR_UAS; } /* Add Session Timers headers */ add_timer_headers(inv, tdata, PJ_TRUE, PJ_TRUE); return PJ_SUCCESS; } /* * This function will handle Session Timers part of INVITE/UPDATE * responses with code: * - 422 (Session Interval Too Small) * - 2xx final response */ PJ_DEF(pj_status_t) pjsip_timer_process_resp(pjsip_inv_session *inv, const pjsip_rx_data *rdata, pjsip_status_code *st_code) { const pjsip_msg *msg; PJ_ASSERT_ON_FAIL(inv && rdata, {if(st_code)*st_code=PJSIP_SC_INTERNAL_SERVER_ERROR;return PJ_EINVAL;}); /* Check if Session Timers is supported */ if ((inv->options & PJSIP_INV_SUPPORT_TIMER) == 0) return PJ_SUCCESS; pj_assert(is_initialized); msg = rdata->msg_info.msg; pj_assert(msg->type == PJSIP_RESPONSE_MSG); /* Only process response of INVITE or UPDATE */ if (rdata->msg_info.cseq->method.id != PJSIP_INVITE_METHOD && pjsip_method_cmp(&rdata->msg_info.cseq->method, &pjsip_update_method)) { return PJ_SUCCESS; } if (msg->line.status.code == PJSIP_SC_SESSION_TIMER_TOO_SMALL) { /* Our Session-Expires is too small, let's update it based on * Min-SE header in the response. */ pjsip_tx_data *tdata; pjsip_min_se_hdr *min_se_hdr; pjsip_hdr *hdr; pjsip_via_hdr *via; /* Get Min-SE value from response */ min_se_hdr = (pjsip_min_se_hdr*) pjsip_msg_find_hdr_by_name(msg, &STR_MIN_SE, NULL); if (min_se_hdr == NULL) { /* Response 422 MUST contain Min-SE header */ PJ_LOG(3, (inv->pool->obj_name, "Received 422 (Session Interval Too Small) response " "without Min-SE header!")); pjsip_timer_end_session(inv); return PJSIP_EMISSINGHDR; } /* Session Timers should have been initialized here */ pj_assert(inv->timer); /* Update Min-SE */ inv->timer->setting.min_se = PJ_MAX(min_se_hdr->min_se, inv->timer->setting.min_se); /* Update Session Timers setting */ if (inv->timer->setting.sess_expires < inv->timer->setting.min_se) inv->timer->setting.sess_expires = inv->timer->setting.min_se; /* Prepare to restart the request */ /* Get the original INVITE request. */ tdata = inv->invite_req; /* Remove branch param in Via header. */ via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); pj_assert(via); via->branch_param.slen = 0; /* Restore strict route set. * See http://trac.pjsip.org/repos/ticket/492 */ pjsip_restore_strict_route_set(tdata); /* Must invalidate the message! */ pjsip_tx_data_invalidate_msg(tdata); pjsip_tx_data_add_ref(tdata); /* Update Session Timers headers */ hdr = (pjsip_hdr*) pjsip_msg_find_hdr_by_name(tdata->msg, &STR_MIN_SE, NULL); if (hdr != NULL) pj_list_erase(hdr); hdr = (pjsip_hdr*) pjsip_msg_find_hdr_by_names(tdata->msg, &STR_SE, &STR_SHORT_SE, NULL); if (hdr != NULL) pj_list_erase(hdr); add_timer_headers(inv, tdata, PJ_TRUE, PJ_TRUE); /* Restart UAC */ pjsip_inv_uac_restart(inv, PJ_FALSE); pjsip_inv_send_msg(inv, tdata); return PJ_SUCCESS; } else if (msg->line.status.code/100 == 2) { pjsip_sess_expires_hdr *se_hdr; /* Find Session-Expires header */ se_hdr = (pjsip_sess_expires_hdr*) pjsip_msg_find_hdr_by_names( msg, &STR_SE, &STR_SHORT_SE, NULL); if (se_hdr == NULL) { /* Remote doesn't support/want Session Timers, check if local * require or force to use Session Timers. */ if (inv->options & PJSIP_INV_REQUIRE_TIMER) { if (st_code) *st_code = PJSIP_SC_EXTENSION_REQUIRED; pjsip_timer_end_session(inv); return PJSIP_ERRNO_FROM_SIP_STATUS( PJSIP_SC_EXTENSION_REQUIRED); } if ((inv->options & PJSIP_INV_ALWAYS_USE_TIMER) == 0) { /* Session Timers not forced */ pjsip_timer_end_session(inv); return PJ_SUCCESS; } } /* Make sure Session Timers is initialized */ if (inv->timer == NULL) pjsip_timer_init_session(inv, NULL); /* Session expiration period specified by remote is lower than our * Min-SE. */ if (se_hdr && se_hdr->sess_expires < inv->timer->setting.min_se) { /* See ticket #954, instead of returning non-PJ_SUCCESS (which * may cause disconnecting call/dialog), let's just accept the * SE and update our local SE, as long as it isn't less than 90s. */ if (se_hdr->sess_expires >= ABS_MIN_SE) { PJ_LOG(3, (inv->pool->obj_name, "Peer responds with bad Session-Expires, %ds, " "which is less than Min-SE specified in request, " "%ds. Well, let's just accept and use it.", se_hdr->sess_expires, inv->timer->setting.min_se)); inv->timer->setting.sess_expires = se_hdr->sess_expires; inv->timer->setting.min_se = se_hdr->sess_expires; } //if (st_code) // *st_code = PJSIP_SC_SESSION_TIMER_TOO_SMALL; //pjsip_timer_end_session(inv); //return PJSIP_ERRNO_FROM_SIP_STATUS( // PJSIP_SC_SESSION_TIMER_TOO_SMALL); } /* Update SE. Session-Expires in response cannot be lower than Min-SE. * Session-Expires in response can only be equal or lower than in * request. */ if (se_hdr && se_hdr->sess_expires <= inv->timer->setting.sess_expires && se_hdr->sess_expires >= inv->timer->setting.min_se) { /* Good SE from remote, update local SE */ inv->timer->setting.sess_expires = se_hdr->sess_expires; } /* Set the refresher */ if (se_hdr && pj_stricmp(&se_hdr->refresher, &STR_UAC) == 0) inv->timer->refresher = TR_UAC; else if (se_hdr && pj_stricmp(&se_hdr->refresher, &STR_UAS) == 0) inv->timer->refresher = TR_UAS; else /* UAS should set the refresher, however, there is a case that * UAS doesn't support/want Session Timers but the UAC insists * to use Session Timers. */ inv->timer->refresher = TR_UAC; /* Remember our role in this transaction */ inv->timer->role = PJSIP_ROLE_UAC; /* Finally, set active flag and start the Session Timers */ inv->timer->active = PJ_TRUE; start_timer(inv); } else if (pjsip_method_cmp(&rdata->msg_info.cseq->method, &pjsip_update_method) == 0 && msg->line.status.code >= 400 && msg->line.status.code < 600) { /* This is to handle error response to previous UPDATE that was * sent without SDP. In this case, retry sending UPDATE but * with SDP this time. * Note: the additional expressions are to check that the * UPDATE was really the one sent by us, not by other * call components (e.g. to change codec) */ if (inv->timer->timer.id == 0 && inv->timer->use_update && inv->timer->with_sdp == PJ_FALSE) { inv->timer->with_sdp = PJ_TRUE; timer_cb(NULL, &inv->timer->timer); } } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_timer_handle_refresh_error( pjsip_inv_session *inv, pjsip_event *event) { PJ_ASSERT_RETURN(inv && event, PJ_EINVAL); /* Check if Session Timers is supported */ if ((inv->options & PJSIP_INV_SUPPORT_TIMER) == 0) return PJ_SUCCESS; pj_assert(is_initialized); if (inv->timer && inv->timer->active) { pj_bool_t as_refresher; /* Check our role */ as_refresher = ((inv->timer->refresher == TR_UAC) && (inv->timer->role == PJSIP_ROLE_UAC)) || ((inv->timer->refresher == TR_UAS) && (inv->timer->role == PJSIP_ROLE_UAS)); if ((as_refresher) && (event->type == PJSIP_EVENT_TSX_STATE) && (inv->timer->refresh_tdata == event->body.tsx_state.tsx->last_tx)) { pjsip_status_code st_code; pjsip_tx_data *bye; pj_status_t status; st_code = (pjsip_status_code)event->body.tsx_state.tsx->status_code; PJ_LOG(3, (inv->pool->obj_name, "Receive error %d for refresh request %.*s/cseq=%d, " "stopping session now", st_code, event->body.tsx_state.tsx->method.name.slen, event->body.tsx_state.tsx->method.name.ptr, event->body.tsx_state.tsx->cseq)); status = pjsip_inv_end_session(inv, event->body.tsx_state.tsx->status_code, pjsip_get_status_text(st_code), &bye); if (status == PJ_SUCCESS && bye) status = pjsip_inv_send_msg(inv, bye); } } return PJ_SUCCESS; } /* * Handle incoming INVITE or UPDATE request. */ PJ_DEF(pj_status_t) pjsip_timer_process_req(pjsip_inv_session *inv, const pjsip_rx_data *rdata, pjsip_status_code *st_code) { pjsip_min_se_hdr *min_se_hdr; pjsip_sess_expires_hdr *se_hdr; const pjsip_msg *msg; unsigned min_se; PJ_ASSERT_ON_FAIL(inv && rdata, {if(st_code)*st_code=PJSIP_SC_INTERNAL_SERVER_ERROR;return PJ_EINVAL;}); /* Check if Session Timers is supported */ if ((inv->options & PJSIP_INV_SUPPORT_TIMER) == 0) return PJ_SUCCESS; pj_assert(is_initialized); msg = rdata->msg_info.msg; pj_assert(msg->type == PJSIP_REQUEST_MSG); /* Only process INVITE or UPDATE request */ if (msg->line.req.method.id != PJSIP_INVITE_METHOD && pjsip_method_cmp(&rdata->msg_info.cseq->method, &pjsip_update_method)) { return PJ_SUCCESS; } /* Find Session-Expires header */ se_hdr = (pjsip_sess_expires_hdr*) pjsip_msg_find_hdr_by_names( msg, &STR_SE, &STR_SHORT_SE, NULL); if (se_hdr == NULL) { /* Remote doesn't support/want Session Timers, check if local * require or force to use Session Timers. Note that Supported and * Require headers negotiation should have been verified by invite * session. */ if ((inv->options & (PJSIP_INV_REQUIRE_TIMER | PJSIP_INV_ALWAYS_USE_TIMER)) == 0) { /* Session Timers not forced/required */ pjsip_timer_end_session(inv); return PJ_SUCCESS; } } /* Make sure Session Timers is initialized */ if (inv->timer == NULL) pjsip_timer_init_session(inv, NULL); /* Find Min-SE header */ min_se_hdr = (pjsip_min_se_hdr*) pjsip_msg_find_hdr_by_name(msg, &STR_MIN_SE, NULL); /* Update Min-SE */ min_se = inv->timer->setting.min_se; if (min_se_hdr) min_se = PJ_MAX(min_se_hdr->min_se, min_se); /* Validate SE. Session-Expires cannot be lower than Min-SE * (or 90 seconds if Min-SE is not set). */ if (se_hdr && se_hdr->sess_expires < min_se) { if (st_code) *st_code = PJSIP_SC_SESSION_TIMER_TOO_SMALL; return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_SESSION_TIMER_TOO_SMALL); } /* Update SE. Note that there is a case that SE is not available in the * request (which means remote doesn't want/support it), but local insists * to use Session Timers. */ if (se_hdr) { /* Update SE as specified by peer. */ inv->timer->setting.sess_expires = se_hdr->sess_expires; } else if (inv->timer->setting.sess_expires < min_se) { /* There is no SE in the request (remote support Session Timers but * doesn't want to use it, it just specify Min-SE) and local SE is * lower than Min-SE specified by remote. */ inv->timer->setting.sess_expires = min_se; } /* Set the refresher */ if (se_hdr && pj_stricmp(&se_hdr->refresher, &STR_UAC) == 0) inv->timer->refresher = TR_UAC; else if (se_hdr && pj_stricmp(&se_hdr->refresher, &STR_UAS) == 0) inv->timer->refresher = TR_UAS; else { /* If refresher role (i.e: ours or peer) has been set/negotiated, * better to keep it. */ if (inv->timer->refresher != TR_UNKNOWN) { pj_bool_t as_refresher; /* Check our refresher role */ as_refresher = (inv->timer->refresher==TR_UAC && inv->timer->role==PJSIP_ROLE_UAC) || (inv->timer->refresher==TR_UAS && inv->timer->role==PJSIP_ROLE_UAS); /* Update refresher role */ inv->timer->refresher = as_refresher? TR_UAS : TR_UAC; } else { /* If UAC supports timer and Session-Expires header is present * in the request, set UAC as refresher. * If UAC doesn't support timer and a proxy inserts a * Session-Expires header, then UAS has to be the * refresher (according to RFC 4028 Section 9). */ pj_bool_t uac_supports_timer = PJ_FALSE; pjsip_supported_hdr *sup_hdr; sup_hdr = (pjsip_supported_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_SUPPORTED, NULL); if (sup_hdr) { unsigned i; for (i = 0; i < sup_hdr->count; i++) { if (pj_stricmp(&sup_hdr->values[i], &STR_TIMER) == 0) { uac_supports_timer = PJ_TRUE; break; } } } inv->timer->refresher = (uac_supports_timer && se_hdr)? TR_UAC: TR_UAS; } } /* Remember our role in this transaction */ inv->timer->role = PJSIP_ROLE_UAS; /* Set active flag */ inv->timer->active = PJ_TRUE; return PJ_SUCCESS; } /* * Handle outgoing response with status code 2xx & 422. */ PJ_DEF(pj_status_t) pjsip_timer_update_resp(pjsip_inv_session *inv, pjsip_tx_data *tdata) { pjsip_msg *msg; /* Check if Session Timers is supported */ if ((inv->options & PJSIP_INV_SUPPORT_TIMER) == 0) return PJ_SUCCESS; pj_assert(is_initialized); PJ_ASSERT_RETURN(inv && tdata, PJ_EINVAL); msg = tdata->msg; if (msg->line.status.code/100 == 2) { if (inv->timer && inv->timer->active) { /* Add Session-Expires header and start the timer */ add_timer_headers(inv, tdata, PJ_TRUE, PJ_FALSE); /* Add 'timer' to Require header (see ticket #1560). */ if (inv->timer->refresher == TR_UAC) { pjsip_require_hdr *req_hdr; pj_bool_t req_hdr_has_timer = PJ_FALSE; req_hdr = (pjsip_require_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_REQUIRE, NULL); if (req_hdr == NULL) { req_hdr = pjsip_require_hdr_create(tdata->pool); PJ_ASSERT_RETURN(req_hdr, PJ_ENOMEM); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)req_hdr); } else { unsigned i; for (i = 0; i < req_hdr->count; ++i) { if (pj_stricmp(&req_hdr->values[i], &STR_TIMER)) { req_hdr_has_timer = PJ_TRUE; break; } } } if (!req_hdr_has_timer) req_hdr->values[req_hdr->count++] = STR_TIMER; } /* Finally, start timer. */ start_timer(inv); } } else if (msg->line.status.code == PJSIP_SC_SESSION_TIMER_TOO_SMALL) { /* Add Min-SE header */ add_timer_headers(inv, tdata, PJ_FALSE, PJ_TRUE); } return PJ_SUCCESS; } /* * End the Session Timers. */ PJ_DEF(pj_status_t) pjsip_timer_end_session(pjsip_inv_session *inv) { PJ_ASSERT_RETURN(inv, PJ_EINVAL); if (inv->timer) { /* Reset active flag */ inv->timer->active = PJ_FALSE; /* Stop Session Timers */ stop_timer(inv); } return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/pjsip/src/pjsip-ua/sip_xfer.c ================================================ /* $Id: sip_xfer.c 3553 2011-05-05 06:14:19Z nanang $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include /* Subscription expiration */ #ifndef PJSIP_XFER_EXPIRES # define PJSIP_XFER_EXPIRES 600 #endif /* * Refer module (mod-refer) */ static struct pjsip_module mod_xfer = { NULL, NULL, /* prev, next. */ { "mod-refer", 9 }, /* Name. */ -1, /* Id */ PJSIP_MOD_PRIORITY_DIALOG_USAGE, /* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ NULL, /* unload() */ NULL, /* on_rx_request() */ NULL, /* on_rx_response() */ NULL, /* on_tx_request. */ NULL, /* on_tx_response() */ NULL, /* on_tsx_state() */ }; /* Declare PJSIP_REFER_METHOD, so that if somebody declares this in * sip_msg.h we can catch the error here. */ enum { PJSIP_REFER_METHOD = PJSIP_OTHER_METHOD }; PJ_DEF_DATA(const pjsip_method) pjsip_refer_method = { (pjsip_method_e) PJSIP_REFER_METHOD, { "REFER", 5} }; PJ_DEF(const pjsip_method*) pjsip_get_refer_method() { return &pjsip_refer_method; } /* * String constants */ static const pj_str_t STR_REFER = { "refer", 5 }; static const pj_str_t STR_MESSAGE = { "message", 7 }; static const pj_str_t STR_SIPFRAG = { "sipfrag", 7 }; /* * Transfer struct. */ struct pjsip_xfer { pjsip_evsub *sub; /**< Event subscribtion record. */ pjsip_dialog *dlg; /**< The dialog. */ pjsip_evsub_user user_cb; /**< The user callback. */ pj_str_t refer_to_uri; /**< The full Refer-To URI. */ int last_st_code; /**< st_code sent in last NOTIFY */ pj_str_t last_st_text; /**< st_text sent in last NOTIFY */ }; typedef struct pjsip_xfer pjsip_xfer; /* * Forward decl for evsub callback. */ static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event); static void xfer_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event); static void xfer_on_evsub_rx_refresh( pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body); static void xfer_on_evsub_rx_notify( pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body); static void xfer_on_evsub_client_refresh(pjsip_evsub *sub); static void xfer_on_evsub_server_timeout(pjsip_evsub *sub); /* * Event subscription callback for xference. */ static pjsip_evsub_user xfer_user = { &xfer_on_evsub_state, &xfer_on_evsub_tsx_state, &xfer_on_evsub_rx_refresh, &xfer_on_evsub_rx_notify, &xfer_on_evsub_client_refresh, &xfer_on_evsub_server_timeout, }; /* * Initialize the REFER subsystem. */ PJ_DEF(pj_status_t) pjsip_xfer_init_module(pjsip_endpoint *endpt) { const pj_str_t accept = { "message/sipfrag;version=2.0", 27 }; pj_status_t status; PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL); PJ_ASSERT_RETURN(mod_xfer.id == -1, PJ_EINVALIDOP); status = pjsip_endpt_register_module(endpt, &mod_xfer); if (status != PJ_SUCCESS) return status; status = pjsip_endpt_add_capability( endpt, &mod_xfer, PJSIP_H_ALLOW, NULL, 1, &pjsip_get_refer_method()->name); if (status != PJ_SUCCESS) return status; status = pjsip_evsub_register_pkg(&mod_xfer, &STR_REFER, PJSIP_XFER_EXPIRES, 1, &accept); if (status != PJ_SUCCESS) return status; return PJ_SUCCESS; } /* * Create transferer (sender of REFER request). * */ PJ_DEF(pj_status_t) pjsip_xfer_create_uac( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_evsub **p_evsub ) { pj_status_t status; pjsip_xfer *xfer; pjsip_evsub *sub; PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL); pjsip_dlg_inc_lock(dlg); /* Create event subscription */ status = pjsip_evsub_create_uac( dlg, &xfer_user, &STR_REFER, PJSIP_EVSUB_NO_EVENT_ID, &sub); if (status != PJ_SUCCESS) goto on_return; /* Create xfer session */ xfer = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_xfer); xfer->dlg = dlg; xfer->sub = sub; if (user_cb) pj_memcpy(&xfer->user_cb, user_cb, sizeof(pjsip_evsub_user)); /* Attach to evsub */ pjsip_evsub_set_mod_data(sub, mod_xfer.id, xfer); *p_evsub = sub; on_return: pjsip_dlg_dec_lock(dlg); return status; } /* * Create transferee (receiver of REFER request). * */ PJ_DEF(pj_status_t) pjsip_xfer_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, pjsip_evsub **p_evsub ) { pjsip_evsub *sub; pjsip_xfer *xfer; const pj_str_t STR_EVENT = {"Event", 5 }; pjsip_event_hdr *event_hdr; pj_status_t status; /* Check arguments */ PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL); /* Must be request message */ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Check that request is REFER */ PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_refer_method())==0, PJSIP_ENOTREFER); /* Lock dialog */ pjsip_dlg_inc_lock(dlg); /* The evsub framework expects an Event header in the request, * while a REFER request conveniently doesn't have one (pun intended!). * So create a dummy Event header. */ if (pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL)==NULL) { event_hdr = pjsip_event_hdr_create(rdata->tp_info.pool); event_hdr->event_type = STR_REFER; pjsip_msg_add_hdr(rdata->msg_info.msg, (pjsip_hdr*)event_hdr); } /* Create server subscription */ status = pjsip_evsub_create_uas( dlg, &xfer_user, rdata, PJSIP_EVSUB_NO_EVENT_ID, &sub); if (status != PJ_SUCCESS) goto on_return; /* Create server xfer subscription */ xfer = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_xfer); xfer->dlg = dlg; xfer->sub = sub; if (user_cb) pj_memcpy(&xfer->user_cb, user_cb, sizeof(pjsip_evsub_user)); /* Attach to evsub */ pjsip_evsub_set_mod_data(sub, mod_xfer.id, xfer); /* Done: */ *p_evsub = sub; on_return: pjsip_dlg_dec_lock(dlg); return status; } /* * Call this function to create request to initiate REFER subscription. * */ PJ_DEF(pj_status_t) pjsip_xfer_initiate( pjsip_evsub *sub, const pj_str_t *refer_to_uri, pjsip_tx_data **p_tdata) { pjsip_xfer *xfer; const pj_str_t refer_to = { "Refer-To", 8}; pjsip_tx_data *tdata; pjsip_generic_string_hdr *hdr; pj_status_t status; /* sub and p_tdata argument must be valid. */ PJ_ASSERT_RETURN(sub && p_tdata, PJ_EINVAL); /* Get the xfer object. */ xfer = (pjsip_xfer*) pjsip_evsub_get_mod_data(sub, mod_xfer.id); PJ_ASSERT_RETURN(xfer != NULL, PJSIP_ENOREFERSESSION); /* refer_to_uri argument MAY be NULL for subsequent REFER requests, * but it MUST be specified in the first REFER. */ PJ_ASSERT_RETURN((refer_to_uri || xfer->refer_to_uri.slen), PJ_EINVAL); /* Lock dialog. */ pjsip_dlg_inc_lock(xfer->dlg); /* Create basic REFER request */ status = pjsip_evsub_initiate(sub, pjsip_get_refer_method(), -1, &tdata); if (status != PJ_SUCCESS) goto on_return; /* Save Refer-To URI. */ if (refer_to_uri == NULL) { refer_to_uri = &xfer->refer_to_uri; } else { pj_strdup(xfer->dlg->pool, &xfer->refer_to_uri, refer_to_uri); } /* Create and add Refer-To header. */ hdr = pjsip_generic_string_hdr_create(tdata->pool, &refer_to, refer_to_uri); if (!hdr) { pjsip_tx_data_dec_ref(tdata); status = PJ_ENOMEM; goto on_return; } pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); /* Done. */ *p_tdata = tdata; status = PJ_SUCCESS; on_return: pjsip_dlg_dec_lock(xfer->dlg); return status; } /* * Accept the incoming REFER request by sending 2xx response. * */ PJ_DEF(pj_status_t) pjsip_xfer_accept( pjsip_evsub *sub, pjsip_rx_data *rdata, int st_code, const pjsip_hdr *hdr_list ) { /* * Don't need to add custom headers, so just call basic * evsub response. */ return pjsip_evsub_accept( sub, rdata, st_code, hdr_list ); } /* * For notifier, create NOTIFY request to subscriber, and set the state * of the subscription. */ PJ_DEF(pj_status_t) pjsip_xfer_notify( pjsip_evsub *sub, pjsip_evsub_state state, int xfer_st_code, const pj_str_t *xfer_st_text, pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pjsip_xfer *xfer; pjsip_param *param; const pj_str_t reason = { "noresource", 10 }; char *body; int bodylen; pjsip_msg_body *msg_body; pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(sub, PJ_EINVAL); /* Get the xfer object. */ xfer = (pjsip_xfer*) pjsip_evsub_get_mod_data(sub, mod_xfer.id); PJ_ASSERT_RETURN(xfer != NULL, PJSIP_ENOREFERSESSION); /* Lock object. */ pjsip_dlg_inc_lock(xfer->dlg); /* Create the NOTIFY request. * Note that reason is only used when state is TERMINATED, and * the defined termination reason for REFER is "noresource". */ status = pjsip_evsub_notify( sub, state, NULL, &reason, &tdata); if (status != PJ_SUCCESS) goto on_return; /* Check status text */ if (xfer_st_text==NULL || xfer_st_text->slen==0) xfer_st_text = pjsip_get_status_text(xfer_st_code); /* Save st_code and st_text, for current_notify() */ xfer->last_st_code = xfer_st_code; pj_strdup(xfer->dlg->pool, &xfer->last_st_text, xfer_st_text); /* Create sipfrag content. */ body = (char*) pj_pool_alloc(tdata->pool, 128); bodylen = pj_ansi_snprintf(body, 128, "SIP/2.0 %u %.*s\r\n", xfer_st_code, (int)xfer_st_text->slen, xfer_st_text->ptr); PJ_ASSERT_ON_FAIL(bodylen > 0 && bodylen < 128, {status=PJ_EBUG; pjsip_tx_data_dec_ref(tdata); goto on_return; }); /* Create SIP message body. */ msg_body = PJ_POOL_ZALLOC_T(tdata->pool, pjsip_msg_body); pjsip_media_type_init(&msg_body->content_type, (pj_str_t*)&STR_MESSAGE, (pj_str_t*)&STR_SIPFRAG); msg_body->data = body; msg_body->len = bodylen; msg_body->print_body = &pjsip_print_text_body; msg_body->clone_data = &pjsip_clone_text_data; param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param); param->name = pj_str("version"); param->value = pj_str("2.0"); pj_list_push_back(&msg_body->content_type.param, param); /* Attach sipfrag body. */ tdata->msg->body = msg_body; /* Done. */ *p_tdata = tdata; on_return: pjsip_dlg_dec_lock(xfer->dlg); return status; } /* * Send current state and the last sipfrag body. */ PJ_DEF(pj_status_t) pjsip_xfer_current_notify( pjsip_evsub *sub, pjsip_tx_data **p_tdata ) { pjsip_xfer *xfer; pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(sub, PJ_EINVAL); /* Get the xfer object. */ xfer = (pjsip_xfer*) pjsip_evsub_get_mod_data(sub, mod_xfer.id); PJ_ASSERT_RETURN(xfer != NULL, PJSIP_ENOREFERSESSION); pjsip_dlg_inc_lock(xfer->dlg); status = pjsip_xfer_notify(sub, pjsip_evsub_get_state(sub), xfer->last_st_code, &xfer->last_st_text, p_tdata); pjsip_dlg_dec_lock(xfer->dlg); return status; } /* * Send request message. */ PJ_DEF(pj_status_t) pjsip_xfer_send_request( pjsip_evsub *sub, pjsip_tx_data *tdata) { return pjsip_evsub_send_request(sub, tdata); } /* * This callback is called by event subscription when subscription * state has changed. */ static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event) { pjsip_xfer *xfer; xfer = (pjsip_xfer*) pjsip_evsub_get_mod_data(sub, mod_xfer.id); PJ_ASSERT_ON_FAIL(xfer!=NULL, {return;}); if (xfer->user_cb.on_evsub_state) (*xfer->user_cb.on_evsub_state)(sub, event); } /* * Called when transaction state has changed. */ static void xfer_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) { pjsip_xfer *xfer; xfer = (pjsip_xfer*) pjsip_evsub_get_mod_data(sub, mod_xfer.id); PJ_ASSERT_ON_FAIL(xfer!=NULL, {return;}); if (xfer->user_cb.on_tsx_state) (*xfer->user_cb.on_tsx_state)(sub, tsx, event); } /* * Called when REFER is received to refresh subscription. */ static void xfer_on_evsub_rx_refresh( pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) { pjsip_xfer *xfer; xfer = (pjsip_xfer*) pjsip_evsub_get_mod_data(sub, mod_xfer.id); PJ_ASSERT_ON_FAIL(xfer!=NULL, {return;}); if (xfer->user_cb.on_rx_refresh) { (*xfer->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text, res_hdr, p_body); } else { /* Implementors MUST send NOTIFY if it implements on_rx_refresh * (implementor == "us" from evsub point of view. */ pjsip_tx_data *tdata; pj_status_t status; if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) { status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_TERMINATED, xfer->last_st_code, &xfer->last_st_text, &tdata); } else { status = pjsip_xfer_current_notify(sub, &tdata); } if (status == PJ_SUCCESS) pjsip_xfer_send_request(sub, tdata); } } /* * Called when NOTIFY is received. */ static void xfer_on_evsub_rx_notify( pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) { pjsip_xfer *xfer; xfer = (pjsip_xfer*) pjsip_evsub_get_mod_data(sub, mod_xfer.id); PJ_ASSERT_ON_FAIL(xfer!=NULL, {return;}); if (xfer->user_cb.on_rx_notify) (*xfer->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text, res_hdr, p_body); } /* * Called when it's time to send SUBSCRIBE. */ static void xfer_on_evsub_client_refresh(pjsip_evsub *sub) { pjsip_xfer *xfer; xfer = (pjsip_xfer*) pjsip_evsub_get_mod_data(sub, mod_xfer.id); PJ_ASSERT_ON_FAIL(xfer!=NULL, {return;}); if (xfer->user_cb.on_client_refresh) { (*xfer->user_cb.on_client_refresh)(sub); } else { pj_status_t status; pjsip_tx_data *tdata; status = pjsip_evsub_initiate(sub, NULL, PJSIP_XFER_EXPIRES, &tdata); if (status == PJ_SUCCESS) pjsip_xfer_send_request(sub, tdata); } } /* * Called when no refresh is received after the interval. */ static void xfer_on_evsub_server_timeout(pjsip_evsub *sub) { pjsip_xfer *xfer; xfer = (pjsip_xfer*) pjsip_evsub_get_mod_data(sub, mod_xfer.id); PJ_ASSERT_ON_FAIL(xfer!=NULL, {return;}); if (xfer->user_cb.on_server_timeout) { (*xfer->user_cb.on_server_timeout)(sub); } else { pj_status_t status; pjsip_tx_data *tdata; status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED, xfer->last_st_code, &xfer->last_st_text, &tdata); if (status == PJ_SUCCESS) pjsip_xfer_send_request(sub, tdata); } } ================================================ FILE: deps/pjsip/third_party/README.txt ================================================ Third Party Software This directory contains third party software that is used by PJ project. = Building the third party libraries = Go to build directory, rather than building the library using the project files/Makefiles provided by the software. = Versions = speex: SVN -r12832 portaudio: SVN -r1186 gsm: gsm-1.0.12 ilbc: from RFC resample: lib-resample, I think version 1.7 srtp libsrtp-1.4.4 ================================================ FILE: deps/pjsip/third_party/bdsound/include/bdimad.h ================================================ /** * @file bdIMADpj.h * @brief bdSound IMproved Audio Device for PJSIP. */ /** * @defgroup bd_IMAD bdIMADpj bdSound IMproved Audio Device for PJSIP. * @ingroup audio_device_api * * bdSound IMproved Audio Device is a multiplatform audio interface * created to integrate in PJSIP library with no effort. * \n Porting bdIMADpj across the main operating systems is * straightforward, without the need of change a single line of code. * * - Features * - Echo cancellation (Full Duplex) * - Noise reduction * - Automatic Gain Control * - Audio Enhancement * * - Supported operating systems * - Windows * - Android * - MacOS X * - iOS * - Linux / Alsa * * - Supported platforms * - x86 * - x64 * - ARM Cortex-A8/A9/A15 with NEON * * Visit bdSound for updated * features, supported operating systems and platforms. * * Using PJSIP with bdIMAD audio device * * - Integration * \n Using bdIMAD within PJSIP is simple: * -# Request for bdIMADpj library to * bdSound: * bdSound will provide instruction to integrate the library depending on * the platform / O.S. / toolchain; * -# Add the bdimad_dev.c file to * pjmedia/src/pjmedia-audiodev folder; * -# Enable the bdIMAD audio device defining the periferal in the * pj/config_site.h and disabling all other devices: *
 *       #define PJMEDIA_AUDIO_DEV_HAS_BDIMAD 1
 *       
* * - Usage * \n There are only a couple of things the customer have to pay attention on * �when using bdIMAD library. * * - Initialization * \n Since the bdIMAD library provide itself the echo cancellation * and the latency management, is necessary to disable these features * in the PJSIP librariy applications. * \n For example in PJSUA sample application there is the need * to provide the following commands: *
 *       --ec-tail=0
 *       --no-vad
 *       --capture-lat=0
 *       --playback-lat=0
 *       
* * - Supported set capability * - PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING * \n Setting speaker volume. * - PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING * \n Setting michrophone volume. * - PJMEDIA_AUD_DEV_CAP_EC * \n Enable/disable echo cancellation. * * For additional information visit * www.bdsound.com * or write to info@bdsound.com * * @author bdSound * @version 1.0.1 * @copyright 2012 bdSound srl. All rights reserved. * */ /** * @defgroup groupFunction Functions * @ingroup bd_IMAD * * Functions defined in bdIMAD. */ /** * @defgroup groupCallback Callbacks * @ingroup bd_IMAD * * Callbacks defined in bdIMAD. */ /** * @defgroup groupStructEnum Structs and Enums * @ingroup bd_IMAD * * Struct and Enum defined in bdIMAD. */ #ifndef BD_IMAD_PJ_H__ #define BD_IMAD_PJ_H__ /** * @brief Macro for Windows DLL Support. */ #ifdef _BDIMADPJ_EXPORTDLL #ifdef WIN32 #define BDIMADPJ_API __declspec(dllexport) #else #define BDIMADPJ_API __attribute__((visibility("default"))) #endif #else #define BDIMADPJ_API #endif #define BD_IMAD_CAPTURE_DEVICES 1 #define BD_IMAD_PLAYBACK_DEVICES 0 #define BD_IMAD_DIAGNOSTIC_ENABLE 1 #define BD_IMAD_DIAGNOSTIC_DISABLE 0 #define BD_IMAD_BITS_X_SAMPLE 16 /**< Bits per sample */ typedef void* bdIMADpj; /** * @addtogroup groupCallback * @{ */ /** * @brief Callback used to fill the playback buffer of bdIMAD. * The function is called by bdIMAD each time are required sample to be played. * @param[in] *buffer pointer to the buffer with the audio * samples to be played(short type). * @param[in] nSamples number of samples required. * @param[in] user_data pointer to the user data structure * defined in the bdIMADpj_Setting_t * structure. * @return none. */ typedef int (* cb_fillPlayBackB_t) (void *buffer, int nSamples, void *user_data); /** * @brief Callback used to retrive the caputre buffer of bdIMAD. The function * is called by bdIMAD each time processed mic samples are available. * @param[out] *buffer pointer to the buffer with the audio * samples to download(short type). * @param[in] nSamples number of samples processed to download. * @param[in] user_data pointer to the user data structure * defined in the MainSet structure. * @return none. */ typedef void (* cb_emptyCaptureB_t) (void *buffer, int nSamples, void *user_data); /** * @} */ /** * @addtogroup groupStructEnum * @{ */ /** * @brief Error status returned by some functions in the library. */ typedef enum bdIMADpj_Status{ /**< No error. */ BD_PJ_OK = 0, /**< Watch bdIMADpj_Warnings_t structure to find the warnings. */ BD_PJ_WARN_BDIMAD_WARNING_ASSERTED = 1, /**< Error not identified. */ BD_PJ_ERROR_GENERIC = 2, /**< The pointer passed is NULL. */ BD_PJ_ERROR_NULL_POINTER = 3, /**< Allocation procedure failed. */ BD_PJ_ERROR_ALLOCATION = 4, /**< The parameter is not existent or the set/get function is not active. */ BD_PJ_ERROR_PARAMETER_NOT_FOUND = 5, /**< No capture device found. */ BD_PJ_ERROR_IMAD_NONE_CAPTURE_DEV = 10, /**< No play device found. */ BD_PJ_ERROR_IMAD_NONE_PLAY_DEV = 11, /**< Frame size not allowed. */ BD_PJ_ERROR_IMAD_FRAME_SIZE = 12, /**< Sample frequency not allowed. */ BD_PJ_ERROR_IMAD_SAMPLE_FREQ = 13, /**< Samples missing. */ BD_PJ_ERROR_IMAD_MISSING_SAMPLES = 14, /**< Device list is empty. */ BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY = 15, /**< Library not authorized, entering demo mode. */ BD_PJ_ERROR_IMAD_LIB_NOT_AUTHORIZED = 16, /**< The input channel memory has not been allocated. */ BD_PJ_ERROR_IMAD_INPUT_CH_NOT_ALLOCATED = 17, /**< The library has expired, entering demo mode. */ BD_PJ_ERROR_IMAD_LICENSE_EXPIRED = 18, /**< Open of capture device failed. */ BD_PJ_ERROR_IMAD_OPEN_CAPTURE_DEV_FAILED = 19, /**< Open of play device failed. */ BD_PJ_ERROR_IMAD_OPEN_PLAY_DEV_FAILED = 20, /**< Start of play device failed. */ BD_PJ_ERROR_IMAD_START_PLAY_DEV_FAILED = 21, /**< Start of capture device failed. */ BD_PJ_ERROR_IMAD_START_CAPTURE_DEV_FAILED = 22, /**< Start of time process failed. */ BD_PJ_ERROR_IMAD_START_TIME_PROCESS_FAILED = 23, /**< Start of thread process failed. */ BD_PJ_ERROR_IMAD_THREAD_PROCESS_FAILED = 24, /**< No volume control available. */ BD_PJ_ERROR_IMAD_NO_VOL_CONTROL_AVAILABLE = 25, } bdIMADpj_Status; /** * @brief Parameter to pass to set and get parameter functions. * * For each enumeration are defined the data type and the supported operations * on that parameter (set and get). */ typedef enum bdIMADpj_Parameter{ /**< int* \n set/get \n 1 enable / 0 disable echo cancellation. */ BD_PARAM_IMAD_PJ_AEC_ENABLE = 1, /**< int* \n set/get \n 1 enable / 0 disable microphone control * (when possible). */ BD_PARAM_IMAD_PJ_MIC_CONTROL_ENABLE = 2, /**< int* \n set/get \n 1 ebable / 0 disable noise reduction. */ BD_PARAM_IMAD_PJ_NOISE_REDUCTION_ENABLE = 3, /**< int* \n set \n number of channel to reset. Used to reset * the input channel statistics. To be used when the same channel * is assigned to another partecipant. */ BD_PARAM_IMAD_PJ_RESET_STATISTIC_IN_CH = 4, /**< float* \n set/get \n 0.0f -> 1.0f volume of * the microphone(when possible). */ BD_PARAM_IMAD_PJ_MIC_VOLUME = 5, /**< int* \n set/get \n 0 mute / 1 not mute on microphone * (when possible). */ BD_PARAM_IMAD_PJ_MIC_MUTE = 6, /**< float* \n set/get \n 0.0f -> 1.0f volume of the speaker. */ BD_PARAM_IMAD_PJ_SPK_VOLUME = 7, /**< int* \n set/get \n 0 mute / 1 not mute on speaker. */ BD_PARAM_IMAD_PJ_SPK_MUTE = 8, } bdIMADpj_Parameter; /** * @brief Instance structure for the information regarding the aec engine. */ typedef struct bdIMADpj_Setting_t{ /**< Sample frequency (8kHz or 16kHz). */ int SamplingFrequency; /**< Audio buffer managed by the aec bdIMAD functions. * (from 16ms to 80ms, 16ms recommended). */ int FrameSize_ms; /**< Points to the validation functions in the validation library. */ void *validate; /**< Points to the the callback function used for filling * the playback buffer of bdIMAD. */ cb_fillPlayBackB_t cb_fillPlayBackBuffer; /**< Points to user data to pass to the callback. */ void *cb_fillPlayBackBuffer_user_data; /**< Points to the callback function used for retreive the processed * audio present in the capture buffer of bdIMAD. */ cb_emptyCaptureB_t cb_emptyCaptureBuffer; /**< Points to user data to pass to the callback. */ void *cb_emptyCaptureBuffer_user_data; /**< Is a wide char pointer to the capture device name. */ wchar_t *CaptureDevice; /**< Is a wide char pointer to the play device name. */ wchar_t *PlayDevice; /**< True to enable diagnostic, false to disable. */ int DiagnosticEnable; /**< Directory which will contains the files generated for diagnostic. */ wchar_t *DiagnosticFolderPath; /**< Is an auxiliary settings pointer used internally by bdIMAD. */ void *bdIMADwr_SettingsData; } bdIMADpj_Setting_t; /** * @brief Instance structure for the warnings generated by the initialization * functions. */ typedef struct bdIMADpj_Warnings_t{ /**< The capture device indicated can't be opened, has been selected * the default capture device. */ int DefaultCaptureDeviceAutomaticallySelected; /**< The capture device opened has not volume control. */ int CaptureDeviceWithoutVolumeControl; /**< The play device indicated can't be opened, has been selected * the default play device. */ int DefaultPlayDeviceAutomaticallySelected; /**< The number of channel requested is out of range. The number of * channel opened is equal to the maximum. */ int NumberOfChannelsOutOfRange; /**< The diagnostic files could not be saved. */ int DiagnosticSaveNotAllowed; /**< The nlp level requested is not allowed, it has been automatically * changed to the default value. */ int nlpLevelChangeSettting; /**< No capture device is present. Anyway the bdSES has been * istantiated only for playback. */ int NoCaptureDevicePresent; /**< The cpu is not adapt to run the aec engine, the aec has been disabled. * This appens for very old cpu like pentium III. */ int oldCPUdetected_AECdisable; /**< Windows Direct Sound error. */ long directSoundError; /**< Windows Direct Sound volume error. */ long directSoundLevel; /**< No play device is present. Anyway the bdSES has been istantiated * only for capture. */ int NoPlayDevicePresent; } bdIMADpj_Warnings_t; /** * @brief Instance structure for the library version */ typedef struct bdIMADpj_libVersion_t{ int major; /**< major version. */ int minor; /**< minor version. */ int build; /**< build number. */ char *name; /**< name "bdIMADpj ver.X". */ char *version; /**< beta, RC, release. */ char *buildDate; /**< build date. */ } bdIMADpj_libVersion_t; /** * @brief Audio output routing setting to pass to set and get route output device functions. */ typedef enum bdIMADpj_out_dev_route{ /** Default route. */ BD_AUD_DEV_ROUTE_DEFAULT = 0, /** Route to loudspeaker */ BD_AUD_DEV_ROUTE_LOUDSPEAKER = 1, /** Route to earpiece */ BD_AUD_DEV_ROUTE_EARPIECE = 2 }bdIMADpj_out_dev_route; /** * @} */ /** * @addtogroup groupFunction * @{ */ #ifdef __cplusplus extern "C" { #endif /** * @brief Must be used to allocate and set to default parameter the memory * for the bdIMAD. * * The function generate a structure bdIMADpj_Setting_t filled with the * default settings. * \n The user can change this settings according to the need and then * launch the ::bdIMADpj_InitAEC. * \n The function generate also a warning structure (::bdIMADpj_Warnings_t) * that could be used in ::bdIMADpj_InitAEC to handle eventual warnings. * @param[out] **ppSettings Points to the pointer of the * allocated ::bdIMADpj_Setting_t. * @param[out] **ppWarningMessages Points to the pointer of the * allocated ::bdIMADpj_Warnings_t. * @return ::BD_PJ_OK if the function has been * performed successfully, otherwise return * an error (refer to ::bdIMADpj_Status). */ BDIMADPJ_API bdIMADpj_Status bdIMADpj_CreateStructures( bdIMADpj_Setting_t **ppSettings, bdIMADpj_Warnings_t **ppWarningMessages); /** * @brief Is used to free the memory for the ::bdIMADpj_Setting_t structure and * ::bdIMADpj_Warnings_t structure allocated with * the ::bdIMADpj_CreateStructures. * @param[in] **ppSettings Pointer to a memory location filled * with the address of the * ::bdIMADpj_Setting_t structure to free. * This address will be set to NULL. * @param[in] **ppWarningMessages Pointer to a memory location filled * with the address of the allocated * ::bdIMADpj_Warnings_t structure to free. * This address will be set to NULL. * @return ::BD_PJ_OK if the function has been * performed successfully, otherwise return * an error (refer to ::bdIMADpj_Status). */ BDIMADPJ_API bdIMADpj_Status bdIMADpj_FreeStructures( bdIMADpj_Setting_t **ppSettings, bdIMADpj_Warnings_t **ppWarningMessages); /** * @brief Is used to initialize the memory for bdIMAD with the settings * contained in the ppSettings. * @param[out] *pBdIMADInstance Is the pointer to the bdIMAD object. * @param[in] **ppSettings Pointer to pointer to a * ::bdIMADpj_Setting_t structure, filled * with initialization settings to be * applied to the bdIMAD. * \n Note, the pBdIMADInstance * is modified with the applied settings. * @param[out] **ppWarningMessages Pointer to pointer to a * ::bdIMADpj_Warnings_t structure, * which reports the warnings after the * initialization. * @return ::BD_PJ_OK if the function has been * performed successfully, otherwise return * an error (refer to ::bdIMADpj_Status). * \n If the error is * ::BD_PJ_WARN_BDIMAD_WARNING_ASSERTED * the init has been performed with success, * but with a different settings * respect to the ones required. * This mainly happens if the audio * device opened is different to the * one requested. */ BDIMADPJ_API bdIMADpj_Status bdIMADpj_InitAEC(bdIMADpj *pBdIMADInstance, bdIMADpj_Setting_t **ppSettings, bdIMADpj_Warnings_t **ppWarningMessages); /** * @brief Is used to free the bdIMAD object pointed by the * pBdIMADInstance. * @param[in] *pBdIMADInstance Pointer to the bdIMAD object to free. * @return ::BD_PJ_OK if the function has been * performed successfully, otherwise return * an error (refer to ::bdIMADpj_Status). */ BDIMADPJ_API bdIMADpj_Status bdIMADpj_FreeAEC(bdIMADpj *pBdIMADInstance); /** * @brief Is used to make a list of capure and play devices available * on the system. * @param[in] captureDevice Set to 1 to get the list of capture * devices. Set to 0 to get the list of * play devices. * @param[in] **deviceName Pointer to pointer to a wide char * containing the names of capture/play * devices. * @return ::BD_PJ_OK if the function has been * performed successfully, otherwise return * an error (refer to ::bdIMADpj_Status). */ BDIMADPJ_API bdIMADpj_Status bdIMADpj_getDeviceName(int captureDevice, wchar_t **deviceName); /** * @brief Is used to freeze the bdIMAD, stopping the audio playback * and recording. * @param[in] bdIMADInstance bdIMAD object. * @return ::BD_PJ_OK if the function has been * performed successfully, otherwise * return an error (refer to * ::bdIMADpj_Status). */ BDIMADPJ_API bdIMADpj_Status bdIMADpj_stop(bdIMADpj bdIMADInstance); /** * @brief Is used to put back in play the audio after it has been stopped by the * ::bdIMADpj_stop functions. * @param[in] bdIMADInstance bdIMAD object. * @return ::BD_PJ_OK if the function has been * performed successfully, otherwise * return an error (refer to * ::bdIMADpj_Status). */ BDIMADPJ_API bdIMADpj_Status bdIMADpj_run(bdIMADpj bdIMADInstance); /** * @brief Print on a standard output the warning messages. * @param[in] *pWarningMessages Pointer to the warning structure * to be printed. * @return ::BD_PJ_OK if the function has been * performed successfully, otherwise * return an error * (refer to ::bdIMADpj_Status). */ BDIMADPJ_API bdIMADpj_Status bdIMADpj_DisplayWarnings( bdIMADpj_Warnings_t *pWarningMessages); /** * @brief Clear the warning structure after being read. * @param[out] **ppWarningMessages Pointer to pointer to the warning * structure to be cleared. * @return ::BD_PJ_OK if the function has been * performed successfully, otherwise * return an error (refer to * ::bdIMADpj_Status). */ BDIMADPJ_API bdIMADpj_Status bdIMADpj_ClearAllWarnings( bdIMADpj_Warnings_t **ppWarningMessages); /** * @brief Is used to set a parameter of the bdIMAD object pointed by the * pBdIMADInstance. * @param[in] bdIMADInstance bdIMAD object. * @param[in] parameterName Indicate the parameter to set. * @param[in] *pValue Is a pointer to the value to set * cast to void. * \n In the ::bdIMADpj_Parameter * declaration is indicated the real type of * the value, depending on the * parameterName. * @return ::BD_PJ_OK if the function has been * performed successfully, otherwise * return an error (refer to * �::bdIMADpj_Status). */ BDIMADPJ_API bdIMADpj_Status bdIMADpj_setParameter(bdIMADpj bdIMADInstance, bdIMADpj_Parameter parameterName, void *pValue); /** * @brief Is used to get a parameter of the bdIMAD object pointed by the * pBdIMADInstance. * @param[in] bdIMADInstance bdIMAD object. * @param[in] parameterName Indicate the parameter to get. * @param[out] *pValue Is a pointer to the value to get cast * to void. \n In the * ::bdIMADpj_Parameter declaration is * indicated the real type of the value, * depending on the * parameterName. * @return ::BD_PJ_OK if the function has been * performed successfully, otherwise return * an error (refer to ::bdIMADpj_Status). */ BDIMADPJ_API bdIMADpj_Status bdIMADpj_getParameter(bdIMADpj bdIMADInstance, bdIMADpj_Parameter parameterName, void *pValue); /** * @brief Is used to set the route of the output device of the bdIMAD object pointed by the * pBdIMADInstance. * @param[in] bdIMADInstance bdIMAD object. * @param[in] outputRoute Indicate the route of the output device to set. * @param[out] **ppWarningMessages Pointer to pointer to a * ::bdIMADpj_Warnings_t structure, * which reports the warnings after the * set function. * @return ::BD_PJ_OK if the function has been * performed successfully, otherwise return * an error (refer to ::bdIMADpj_Status). */ BDIMADPJ_API bdIMADpj_Status bdIMADpj_setRouteOutputDevice(bdIMADpj bdIMADInstance, bdIMADpj_out_dev_route outputRoute, bdIMADpj_Warnings_t **ppWarningMessages); /** * @brief Is used to get the route of the output device of the bdIMAD object pointed by the * pBdIMADInstance. * @param[in] bdIMADInstance bdIMAD object. * @param[out] *outputRoute Is a pointer to the route of the output device currently setted. * @return ::BD_PJ_OK if the function has been * performed successfully, otherwise return * an error (refer to ::bdIMADpj_Status). */ BDIMADPJ_API bdIMADpj_Status bdIMADpj_getRouteOutputDevice(bdIMADpj bdIMADInstance, bdIMADpj_out_dev_route *outputRoute); #ifdef __cplusplus } #endif /** * @} */ #endif //BD_IMAD_PJ_H__ ================================================ FILE: deps/pjsip/third_party/build/Makefile ================================================ DIRS = resample milenage srtp include ../../build.mak include $(PJDIR)/build/common.mak all clean dep depend distclean realclean: for dir in $(DIRS); do \ if $(MAKE) $(MAKE_FLAGS) -C $$dir $@; then \ true; \ else \ exit 1; \ fi; \ done doc: ================================================ FILE: deps/pjsip/third_party/build/g7221/Makefile ================================================ include ../../../build.mak include ../../../build/common.mak export LIBDIR := ../../lib RULES_MAK := $(PJDIR)/build/rules.mak export G7221_CODEC_LIB := ../../lib/libg7221codec-$(TARGET_NAME)$(LIBEXT) ############################################################################### # Gather all flags. # export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ $(CFLAGS) $(CC_INC)../.. $(CC_INC)../../g7221/common \ $(CC_INC)../../g7221/common/stl-files \ $(CC_INC)../../../pjlib/include export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ $(HOST_CXXFLAGS) $(CXXFLAGS) export _LDFLAGS := $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ $(LDFLAGS) export G7221_CODEC_SRCDIR = ../../g7221 export G7221_CODEC_OBJS = common/common.o common/huff_tab.o common/tables.o \ common/basic_op.o \ decode/coef2sam.o decode/dct4_s.o decode/decoder.o \ encode/dct4_a.o encode/sam2coef.o encode/encoder.o export G7221_CODEC_CFLAGS = $(_CFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Main entry # # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. # TARGETS := libg7221codec all: $(TARGETS) doc: cd .. && doxygen docs/doxygen.cfg dep: depend distclean: realclean .PHONY: dep depend libg7221codec clean realclean distclean libg7221codec: $(MAKE) -f $(RULES_MAK) APP=G7221_CODEC app=libg7221codec $(G7221_CODEC_LIB) clean print_lib: $(MAKE) -f $(RULES_MAK) APP=G7221_CODEC app=libg7221codec $@ realclean: $(subst @@,$(subst /,$(HOST_PSEP),.ilbc-$(TARGET_NAME).depend),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=G7221_CODEC app=libg7221codec $@ depend: $(MAKE) -f $(RULES_MAK) APP=G7221_CODEC app=libg7221codec $@ ================================================ FILE: deps/pjsip/third_party/build/gsm/Makefile ================================================ include ../../../build.mak include ../../../build/common.mak export LIBDIR := ../../lib RULES_MAK := $(PJDIR)/build/rules.mak export GSM_CODEC_LIB := ../../lib/libgsmcodec-$(TARGET_NAME)$(LIBEXT) ############################################################################### # Gather all flags. # export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ $(CFLAGS) $(CC_INC). $(CC_INC)../../gsm/inc \ $(CC_INC)../../../pjlib/include export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ $(HOST_CXXFLAGS) $(CXXFLAGS) export _LDFLAGS := $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ $(LDFLAGS) export GSM_CODEC_SRCDIR = ../../gsm/src export GSM_CODEC_OBJS = add.o code.o decode.o \ gsm_create.o gsm_decode.o gsm_destroy.o \ gsm_encode.o gsm_explode.o gsm_implode.o \ gsm_option.o long_term.o \ lpc.o preprocess.o rpe.o short_term.o \ table.o export GSM_CODEC_CFLAGS = -DSASR -DWAV49 -DNeedFunctionPrototypes=1 $(_CFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Main entry # # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. # TARGETS := libgsmcodec all: $(TARGETS) doc: cd .. && doxygen docs/doxygen.cfg dep: depend distclean: realclean .PHONY: dep depend libgsmcodec clean realclean distclean libgsmcodec: $(MAKE) -f $(RULES_MAK) APP=GSM_CODEC app=libgsmcodec $(GSM_CODEC_LIB) clean print_lib: $(MAKE) -f $(RULES_MAK) APP=GSM_CODEC app=libgsmcodec $@ realclean: $(subst @@,$(subst /,$(HOST_PSEP),.ilbc-$(TARGET_NAME).depend),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=GSM_CODEC app=libgsmcodec $@ depend: $(MAKE) -f $(RULES_MAK) APP=GSM_CODEC app=libgsmcodec $@ ================================================ FILE: deps/pjsip/third_party/build/gsm/config.h ================================================ #ifdef _MSC_VER # pragma warning(disable: 4100) // unreferenced formal parameter # pragma warning(disable: 4101) // unreferenced local variable # pragma warning(disable: 4244) // conversion from 'double ' to 'float ' # pragma warning(disable: 4305) // truncation from 'const double ' to 'float ' # pragma warning(disable: 4018) // signed/unsigned mismatch //# pragma warning(disable: 4701) // local variable used without initialized #endif #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 402 # pragma GCC diagnostic ignored "-Wpragmas" # pragma GCC diagnostic ignored "-Wunused-const-variable" #endif #include #include "../../gsm/inc/config.h" ================================================ FILE: deps/pjsip/third_party/build/ilbc/Makefile ================================================ include ../../../build.mak include ../../../build/common.mak export LIBDIR := ../../lib RULES_MAK := $(PJDIR)/build/rules.mak export ILBC_LIB := ../../lib/libilbccodec-$(TARGET_NAME)$(LIBEXT) ############################################################################### # Gather all flags. # export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ $(CFLAGS) $(CC_INC). $(CC_INC)../../ilbc \ $(CC_INC)../../../pjlib/include export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ $(HOST_CXXFLAGS) $(CXXFLAGS) export _LDFLAGS := $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ $(LDFLAGS) export ILBC_SRCDIR = ../../ilbc export ILBC_OBJS = FrameClassify.o LPCdecode.o LPCencode.o \ StateConstructW.o StateSearchW.o anaFilter.o \ constants.o createCB.o doCPLC.o \ enhancer.o filter.o gainquant.o \ getCBvec.o helpfun.o hpInput.o \ hpOutput.o iCBConstruct.o iCBSearch.o \ iLBC_decode.o iLBC_encode.o lsf.o \ packing.o syntFilter.o export ILBC_CFLAGS = $(_CFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Main entry # # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. # TARGETS := libilbccodec all: $(TARGETS) doc: cd .. && doxygen docs/doxygen.cfg dep: depend distclean: realclean .PHONY: dep depend libilbccodec clean realclean distclean libilbccodec: $(MAKE) -f $(RULES_MAK) APP=ILBC app=libilbccodec $(ILBC_LIB) clean print_lib: $(MAKE) -f $(RULES_MAK) APP=ILBC app=libilbccodec $@ realclean: $(subst @@,$(subst /,$(HOST_PSEP),.ilbc-$(TARGET_NAME).depend),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=ILBC app=libilbccodec $@ depend: $(MAKE) -f $(RULES_MAK) APP=ILBC app=libilbccodec $@ ================================================ FILE: deps/pjsip/third_party/build/milenage/Makefile ================================================ include ../../../build.mak include ../../../build/common.mak export LIBDIR := ../../lib RULES_MAK := $(PJDIR)/build/rules.mak export MILENAGE_LIB := ../../lib/libmilenage-$(TARGET_NAME)$(LIBEXT) ############################################################################### # Gather all flags. # export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ $(CFLAGS) $(CC_INC). $(CC_INC)../../milenage/include \ $(CC_INC)../../../pjlib/include export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ $(HOST_CXXFLAGS) $(CXXFLAGS) export _LDFLAGS := $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ $(LDFLAGS) export MILENAGE_SRCDIR = ../../milenage export MILENAGE_OBJS = milenage.o rijndael.o export MILENAGE_CFLAGS = $(_CFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Main entry # # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. # TARGETS := libmilenage all: $(TARGETS) doc: cd .. && doxygen docs/doxygen.cfg dep: depend distclean: realclean .PHONY: dep depend libmilenage clean realclean distclean libmilenage: $(MAKE) -f $(RULES_MAK) APP=MILENAGE app=libmilenage $(MILENAGE_LIB) clean print_lib: $(MAKE) -f $(RULES_MAK) APP=MILENAGE app=libmilenage $@ realclean: $(subst @@,$(subst /,$(HOST_PSEP),.ilbc-$(TARGET_NAME).depend),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=MILENAGE app=libmilenage $@ depend: $(MAKE) -f $(RULES_MAK) APP=MILENAGE app=libmilenage $@ ================================================ FILE: deps/pjsip/third_party/build/opus/Makefile ================================================ include ../../../build.mak include ../../../build/common.mak export LIBDIR := ../../lib RULES_MAK := $(PJDIR)/build/rules.mak export OPUS_LIB := ../../lib/libopuscodec-$(TARGET_NAME)$(LIBEXT) ############################################################################### # Gather all flags. # OPUS_DEFS := \ -DOPUS_BUILD \ -DHAVE_LRINT \ -DVAR_ARRAYS OPUS_SRC_BASE := ../../opus include $(OPUS_SRC_BASE)/silk_sources.mk include $(OPUS_SRC_BASE)/celt_sources.mk include $(OPUS_SRC_BASE)/opus_sources.mk OPUS_INCS := \ $(CC_INC)$(OPUS_SRC_BASE)/include \ $(CC_INC)$(OPUS_SRC_BASE)/celt \ $(CC_INC)$(OPUS_SRC_BASE)/silk \ $(CC_INC)$(OPUS_SRC_BASE)/silk/float export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ $(OPUS_DEFS) \ $(CFLAGS) $(CC_INC). \ $(CC_INC)../../../pjlib/include \ $(OPUS_INCS) export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ $(HOST_CXXFLAGS) $(CXXFLAGS) export _LDFLAGS := $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ $(LDFLAGS) OPUS_SRCS_C = $(SILK_SOURCES) $(SILK_SOURCES_FLOAT) $(CELT_SOURCES) $(OPUS_SOURCES) $(OPUS_SOURCES_FLOAT) export OPUS_SRCDIR = $(OPUS_SRC_BASE) export OPUS_OBJS := $(patsubst %.c,%.o,$(OPUS_SRCS_C)) export OPUS_CFLAGS = $(_CFLAGS) export OPUS_CXXFLAGS = $(_CXXFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Main entry # # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. # TARGETS := libopuscodec all: $(TARGETS) doc: cd .. && doxygen docs/doxygen.cfg dep: depend distclean: realclean .PHONY: dep depend libopuscodec clean realclean distclean libopuscodec: $(MAKE) -f $(RULES_MAK) APP=OPUS app=libopuscodec $(OPUS_LIB) clean print_lib: $(MAKE) -f $(RULES_MAK) APP=OPUS app=libopuscodec $@ realclean: $(subst @@,$(subst /,$(HOST_PSEP),.opus-$(TARGET_NAME).depend),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=OPUS app=libopuscodec $@ depend: $(MAKE) -f $(RULES_MAK) APP=OPUS app=libopuscodec $@ ================================================ FILE: deps/pjsip/third_party/build/os-auto.mak.in ================================================ ifneq (@ac_no_gsm_codec@,1) ifeq (@ac_external_gsm@,1) # External else DIRS += gsm endif endif ifneq (@ac_no_ilbc_codec@,1) DIRS += ilbc endif ifneq (@ac_no_speex_codec@,1) ifeq (@ac_external_speex@,1) # External speex else DIRS += speex endif endif ifneq (@ac_no_g7221_codec@,1) DIRS += g7221 endif ifneq ($(findstring webrtc,@ac_webrtc_platform@),) DIRS += webrtc endif DIRS += opus DIRS += zsrtp ================================================ FILE: deps/pjsip/third_party/build/resample/Makefile ================================================ include ../../../build.mak include ../../../build/common.mak export LIBDIR := ../../lib RULES_MAK := $(PJDIR)/build/rules.mak ############################################################################### # Gather all flags. # export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ $(CFLAGS) $(CC_INC). $(CC_INC)../../resample/include \ $(CC_INC)../../../pjlib/include export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ $(HOST_CXXFLAGS) $(CXXFLAGS) export _LDFLAGS := $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ $(LDFLAGS) export RESAMPLE_SRCDIR = ../../resample/src export RESAMPLE_OBJS = resamplesubs.o export RESAMPLE_CFLAGS = $(_CFLAGS) SHLIB_NAME := libresample.$(SHLIB_SUFFIX) export RESAMPLE_SHLIB := ../../lib/$(SHLIB_NAME).$(PJ_VERSION_MAJOR) export RESAMPLE_LIB := ../../lib/libresample-$(TARGET_NAME)$(LIBEXT) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Main entry # # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. # ifeq ($(PJ_RESAMPLE_DLL),1) TARGETS := ../../lib/$(SHLIB_NAME) ifeq ($(SHLIB_SUFFIX),so) SHLIB_OPT := -Wl,-soname,$(SHLIB_NAME).$(PJ_VERSION_MAJOR) else SHLIB_OPT := endif export RESAMPLE_CFLAGS := -fPIC $(RESAMPLE_CFLAGS) export RESAMPLE_LDFLAGS := -shared $(SHLIB_OPT) $(RESAMPLE_LDFLAGS) else TARGETS := libresample endif all: $(TARGETS) doc: cd .. && doxygen docs/doxygen.cfg dep: depend distclean: realclean .PHONY: dep depend libresample clean realclean distclean libresample: $(MAKE) -f $(RULES_MAK) APP=RESAMPLE app=libresample $(RESAMPLE_LIB) ../../lib/$(SHLIB_NAME): $(RESAMPLE_SHLIB) ln -s $(SHLIB_NAME).$(PJ_VERSION_MAJOR) $@ $(RESAMPLE_SHLIB): $(MAKE) -f $(RULES_MAK) APP=RESAMPLE app=libresample $(RESAMPLE_SHLIB) clean print_lib: $(MAKE) -f $(RULES_MAK) APP=RESAMPLE app=libresample $@ realclean: $(subst @@,$(subst /,$(HOST_PSEP),../../lib/$(SHLIB_NAME)),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),$(RESAMPLE_SHLIB)),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=RESAMPLE app=libresample $@ depend: $(MAKE) -f $(RULES_MAK) APP=RESAMPLE app=libresample $@ ================================================ FILE: deps/pjsip/third_party/build/resample/config.h ================================================ #ifndef RESAMPLE_HAS_SMALL_FILTER # define RESAMPLE_HAS_SMALL_FILTER 1 #endif #ifndef RESAMPLE_HAS_LARGE_FILTER # define RESAMPLE_HAS_LARGE_FILTER 1 #endif ================================================ FILE: deps/pjsip/third_party/build/speex/Makefile ================================================ include ../../../build.mak include ../../../build/common.mak export LIBDIR := ../../lib RULES_MAK := $(PJDIR)/build/rules.mak export SPEEX_LIB := ../../lib/libspeex-$(TARGET_NAME)$(LIBEXT) ############################################################################### # Gather all flags. # export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ $(CFLAGS) $(CC_INC). $(CC_INC)../../speex/include \ $(CC_INC)../../speex/libspeex \ $(CC_INC)../../../pjlib/include export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ $(HOST_CXXFLAGS) $(CXXFLAGS) export _LDFLAGS := $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ $(APP_LDFLAGS) $(LDFLAGS) export SPEEX_SRCDIR = ../../speex/libspeex export SPEEX_OBJS = bits.o cb_search.o exc_10_16_table.o \ exc_10_32_table.o exc_20_32_table.o \ exc_5_256_table.o exc_5_64_table.o \ exc_8_128_table.o fftwrap.o filterbank.o \ filters.o gain_table.o gain_table_lbr.o \ hexc_10_32_table.o hexc_table.o \ high_lsp_tables.o \ kiss_fft.o kiss_fftr.o lpc.o \ lsp.o lsp_tables_nb.o ltp.o \ mdf.o modes.o modes_wb.o \ nb_celp.o preprocess.o \ quant_lsp.o resample.o sb_celp.o smallft.o \ speex.o speex_callbacks.o speex_header.o \ stereo.o vbr.o vq.o window.o export SPEEX_CFLAGS = -DHAVE_CONFIG_H $(_CFLAGS) export SPEEX_LDFLAGS := $(PJLIB_LDLIB) $(_LDFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Main entry # # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. # TARGETS := libspeex all: $(TARGETS) doc: cd .. && doxygen docs/doxygen.cfg dep: depend distclean: realclean .PHONY: dep depend libspeex clean realclean distclean libspeex: $(MAKE) -f $(RULES_MAK) APP=SPEEX app=libspeex $(SPEEX_LIB) clean print_lib: $(MAKE) -f $(RULES_MAK) APP=SPEEX app=libspeex $@ realclean: $(subst @@,$(subst /,$(HOST_PSEP),.ilbc-$(TARGET_NAME).depend),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=SPEEX app=libspeex $@ depend: $(MAKE) -f $(RULES_MAK) APP=SPEEX app=libspeex $@ ================================================ FILE: deps/pjsip/third_party/build/speex/config.h ================================================ #include /* Check if we need to use the fixed point version */ #if !defined(PJ_HAS_FLOATING_POINT) || PJ_HAS_FLOATING_POINT==0 # define FIXED_POINT # define USE_KISS_FFT #else # define FLOATING_POINT # define USE_SMALLFT #endif #define EXPORT #if (defined(PJ_WIN32) && PJ_WIN32!=0) || \ (defined(PJ_WIN64) && PJ_WIN64!=0) || \ (defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0) # include "../../speex/win32/config.h" #else #define inline __inline #define restrict #endif #ifdef _MSC_VER # pragma warning(disable: 4100) // unreferenced formal parameter # pragma warning(disable: 4101) // unreferenced local variable # pragma warning(disable: 4244) // conversion from 'double ' to 'float ' # pragma warning(disable: 4305) // truncation from 'const double ' to 'float ' # pragma warning(disable: 4018) // signed/unsigned mismatch # pragma warning(disable: 4456) // declaration of '[var]' hides previous local declaration //# pragma warning(disable: 4701) // local variable used without initialized #endif #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 402 # pragma GCC diagnostic ignored "-Wpragmas" # pragma GCC diagnostic ignored "-Wunused-but-set-variable" #endif #include /* * Override miscellaneous Speex functions. */ #define OVERRIDE_SPEEX_ERROR #define speex_error(str) PJ_LOG(4,("speex", "error: %s", str)) #define OVERRIDE_SPEEX_WARNING #define speex_warning(str) PJ_LOG(5,("speex", "warning: %s", str)) #define OVERRIDE_SPEEX_WARNING_INT #define speex_warning_int(str,val) PJ_LOG(5,("speex", "warning: %s: %d", str, val)) ================================================ FILE: deps/pjsip/third_party/build/speex/speex/speex_config_types.h ================================================ #include typedef pj_int16_t spx_int16_t; typedef pj_uint16_t spx_uint16_t; typedef pj_int32_t spx_int32_t; typedef pj_uint32_t spx_uint32_t; ================================================ FILE: deps/pjsip/third_party/build/srtp/Makefile ================================================ include ../../../build.mak include ../../../build/common.mak export LIBDIR := ../../lib RULES_MAK := $(PJDIR)/build/rules.mak export SRTP_LIB := ../../lib/libsrtp-$(TARGET_NAME)$(LIBEXT) ############################################################################### # Gather all flags. # export _CFLAGS := $(CC_INC). $(CC_INC)../../srtp/crypto/include \ $(CC_INC)../../srtp/include \ $(CC_INC)../../../pjlib/include \ $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ $(CFLAGS) export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ $(HOST_CXXFLAGS) $(CXXFLAGS) export _LDFLAGS := $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ $(APP_LDFLAGS) $(LDFLAGS) # libcrypt.a (the crypto engine) ciphers = crypto/cipher/cipher.o crypto/cipher/null_cipher.o \ crypto/cipher/aes.o crypto/cipher/aes_icm.o \ crypto/cipher/aes_cbc.o hashes = crypto/hash/null_auth.o crypto/hash/sha1.o \ crypto/hash/hmac.o crypto/hash/auth.o # crypto/hash/tmmhv2.o replay = crypto/replay/rdb.o crypto/replay/rdbx.o \ crypto/replay/ut_sim.o math = crypto/math/datatypes.o crypto/math/stat.o ust = crypto/ust/ust.o rng = crypto/rng/rand_source.o crypto/rng/prng.o crypto/rng/ctr_prng.o err = pjlib/srtp_err.o kernel = crypto/kernel/crypto_kernel.o crypto/kernel/alloc.o \ crypto/kernel/key.o $(rng) $(err) # $(ust) srtpobj = srtp/srtp.o cryptobj = $(ciphers) $(hashes) $(math) $(stat) $(kernel) $(replay) export SRTP_SRCDIR = ../../srtp export SRTP_OBJS = $(cryptobj) $(srtpobj) export SRTP_CFLAGS = -DHAVE_CONFIG_H $(_CFLAGS) export SRTP_LDFLAGS = $(PJLIB_LDLIB) $(_LDFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Main entry # # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. # TARGETS := libsrtp all: $(TARGETS) doc: cd .. && doxygen docs/doxygen.cfg dep: depend distclean: realclean .PHONY: dep depend libsrtp clean realclean distclean libsrtp: $(MAKE) -f $(RULES_MAK) APP=SRTP app=libsrtp $(SRTP_LIB) clean print_lib: $(MAKE) -f $(RULES_MAK) APP=SRTP app=libsrtp $@ realclean: $(subst @@,$(subst /,$(HOST_PSEP),.ilbc-$(TARGET_NAME).depend),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=SRTP app=libsrtp $@ depend: $(MAKE) -f $(RULES_MAK) APP=SRTP app=libsrtp $@ ================================================ FILE: deps/pjsip/third_party/build/srtp/srtp_config.h ================================================ /* $Id: srtp_config.h 2660 2009-04-28 19:38:43Z nanang $ */ /* * Copyright (C) 2003-2007 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __SRTP_CONFIG_H__ #define __SRTP_CONFIG_H__ #include /* We'll just define CISC if it's x86 family */ #if defined (PJ_M_I386) || defined(_i386_) || defined(i_386_) || \ defined(_X86_) || defined(x86) || defined(__i386__) || \ defined(__i386) || defined(_M_IX86) || defined(__I86__) || \ defined (PJ_M_X86_64) || defined(__amd64__) || defined(__amd64) || \ defined(__x86_64__) || defined(__x86_64) || \ defined(PJ_M_IA64) || defined(__ia64__) || defined(_IA64) || \ defined(__IA64__) || defined(_M_IA64) # define CPU_CISC 1 /* # define HAVE_X86 1 use X86 inlined assembly code */ #else /*# define CPU_RISC 1*/ # define CPU_CISC 1 #endif /* Define to compile in dynamic debugging system. */ #define ENABLE_DEBUGGING PJ_DEBUG /* Define to 1 if you have the header file. */ #if defined(PJ_HAS_ARPA_INET_H) && PJ_HAS_ARPA_INET_H!=0 # define HAVE_ARPA_INET_H 1 #endif /* Define to 1 if you have the header file. */ /* #undef HAVE_BYTESWAP_H */ /* Define to 1 if you have the `inet_aton' function. */ #if defined(PJ_SOCK_HAS_INET_PTON) && PJ_SOCK_HAS_INET_PTON # define HAVE_INET_ATON 1 #endif /* Define to 1 if you have the header file. */ #if defined(PJ_HAS_NETINET_IN_H) && PJ_HAS_NETINET_IN_H!=0 # define HAVE_NETINET_IN_H 1 #endif /* Define to 1 if you have the header file. */ #if defined(PJ_HAS_STDLIB_H) && PJ_HAS_STDLIB_H!=0 # define HAVE_STDLIB_H 1 #endif /* Define to 1 if you have the header file. */ #if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H!=0 # define HAVE_STRING_H 1 #endif /* Define to 1 if you have the header file. */ #if defined(PJ_HAS_SYS_SOCKET_H) && PJ_HAS_SYS_SOCKET_H!=0 # define HAVE_SYS_SOCKET_H 1 #endif /* Define to 1 if you have the header file. */ #if defined(PJ_HAS_SYS_TYPES_H) && PJ_HAS_SYS_TYPES_H!=0 # define HAVE_SYS_TYPES_H 1 #endif /* Define to 1 if you have the header file. */ /* Define to 1 if you have the `usleep' function. */ #if defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H!=0 # define HAVE_UNISTD_H 1 # define HAVE_USLEEP 1 #endif /* Define to 1 if you have the header file. */ #if (defined(PJ_WIN32) && PJ_WIN32!=0) || (defined(PJ_WIN64) && PJ_WIN64 != 0) # define HAVE_WINDOWS_H 1 #endif /* Define to 1 if you have the header file. */ #if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H!=0 # define HAVE_WINSOCK2_H 1 #endif #define HAVE_INT16_T 1 #define HAVE_INT32_T 1 #define HAVE_INT8_T 1 #define HAVE_UINT8_T 1 #define HAVE_UINT16_T 1 #define HAVE_UINT32_T 1 #define HAVE_UINT64_T 1 /* Define to 1 if you have the header file. */ #if defined(PJ_HAS_STDINT_H) && PJ_HAS_STDINT_H!=0 # define HAVE_STDINT_H 1 #else typedef pj_uint8_t uint8_t; typedef pj_uint16_t uint16_t; typedef pj_uint32_t uint32_t; typedef pj_uint64_t uint64_t; typedef pj_int8_t int8_t; typedef pj_int16_t int16_t; typedef pj_int32_t int32_t; typedef pj_int64_t int64_t; #endif /* These shouldn't really matter as long as HAVE_UINT64_T is set */ #define SIZEOF_UNSIGNED_LONG (sizeof(unsigned long)) #define SIZEOF_UNSIGNED_LONG_LONG 8 #if (_MSC_VER >= 1400) // VC8+ # ifndef _CRT_SECURE_NO_DEPRECATE # define _CRT_SECURE_NO_DEPRECATE # endif # ifndef _CRT_NONSTDC_NO_DEPRECATE # define _CRT_NONSTDC_NO_DEPRECATE # endif #endif // VC8+ #ifdef _MSC_VER # ifndef __cplusplus # define inline _inline # endif # pragma warning(disable:4311) # pragma warning(disable:4761) // integral mismatch # pragma warning(disable:4018) // signed/unsigned mismatch # pragma warning(disable:4244) // conversion from int64 to int # pragma warning(disable:4100) // unreferenced formal parameter #endif /* clock() */ #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 /* clock() causes unresolved symbol on linking */ # define _CLOCK_T_DEFINED # define CLOCKS_PER_SEC 1000 # define clock_t unsigned #include static clock_t clock(void) { return GetTickCount(); } #endif /* Path to random device */ /* #define DEV_URANDOM "/dev/urandom" */ /* Only with PJSIP: * Try to open PJ_DEV_URANDOM if present */ #if defined(PJ_HAS_FCNTL_H) && defined(PJ_HAS_UNISTD_H) # define PJ_DEV_URANDOM "/dev/urandom" #endif /* We have overridden libsrtp error mechanism, so these are not used. */ /* #undef ERR_REPORTING_FILE */ /* #undef ERR_REPORTING_STDOUT */ /* #undef USE_ERR_REPORTING_FILE */ /* #undef USE_SYSLOG */ /* #undef HAVE_SYSLOG_H */ /* Define this to use ISMAcryp code. */ /* #undef GENERIC_AESICM */ /* Define to 1 if you have the header file. */ /* #undef HAVE_INTTYPES_H */ /* Define to 1 if you have the `socket' function. */ /* #undef HAVE_SOCKET */ /* Define to 1 if you have the `socket' library (-lsocket). */ /* #undef HAVE_LIBSOCKET */ /* Define to 1 if you have the header file. */ /* #undef HAVE_MACHINE_TYPES_H */ /* Define to 1 if you have the header file. */ //#define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_INT_TYPES_H */ /* Define to use GDOI. */ /* #undef SRTP_GDOI */ /* Define to compile for kernel contexts. */ /* #undef SRTP_KERNEL */ /* Define to compile for Linux kernel context. */ /* #undef SRTP_KERNEL_LINUX */ /* Define to 1 if you have the ANSI C header files. */ //#define STDC_HEADERS 1 /* Endianness would have been set by pjlib. */ /* #undef WORDS_BIGENDIAN */ /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ /* Define to `unsigned' if does not define. */ /* #undef size_t */ #endif /* __SRTP_CONFIG_H__ */ ================================================ FILE: deps/pjsip/third_party/build/webrtc/Makefile ================================================ include ../../../build.mak include ../../../build/common.mak export LIBDIR := ../../lib RULES_MAK := $(PJDIR)/build/rules.mak export WEBRTCAEC_LIB := ../../lib/libwebrtcaec-$(TARGET_NAME)$(LIBEXT) WEBRTC_DEFS := \ -D__STDC_FORMAT_MACROS \ -DDYNAMIC_ANNOTATIONS_ENABLED=1 \ -D_DEBUG WEBRTC_CFLAGS := \ -fno-exceptions \ -Wall \ -Wno-unused-parameter \ -Wno-missing-field-initializers \ -D_FILE_OFFSET_BITS=64 \ -fvisibility=hidden \ -pipe \ -fno-strict-aliasing WEBRTC_CFLAGS_CC := \ -fno-threadsafe-statics \ -fvisibility-inlines-hidden \ -Wsign-compare WEBRTC_SRC_BASE := ../../webrtc WEBRTCAEC_INCS := \ $(CC_INC)$(WEBRTC_SRC_BASE)/src \ $(CC_INC)$(WEBRTC_SRC_BASE)/src/common_audio/signal_processing_library/main/interface \ $(CC_INC)$(WEBRTC_SRC_BASE)/src/modules/audio_processing/aec/main/interface \ $(CC_INC)$(WEBRTC_SRC_BASE)/src/modules/audio_processing/agc/main/interface \ $(CC_INC)$(WEBRTC_SRC_BASE)/src/modules/audio_processing/ns/main/interface \ $(CC_INC)$(WEBRTC_SRC_BASE)/src/modules/audio_processing/utility \ $(CC_INC)$(WEBRTC_SRC_BASE)/src/system_wrappers/source/spreadsortlib \ $(CC_INC)$(WEBRTC_SRC_BASE)/src/system_wrappers/interface export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) $(CFLAGS) \ $(WEBRTC_DEFS) $(WEBRTC_CFLAGS) \ $(CC_INC). \ $(CC_INC)../../../pjlib/include \ $(WEBRTCAEC_INCS) export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) $(HOST_CXXFLAGS) $(CXXFLAGS) \ $(WEBRTC_DEFS) $(WEBRTC_CFLAGS_CC) export _LDFLAGS := $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ $(LDFLAGS) AEC_OBJS := modules/audio_processing/aec/main/source/echo_cancellation.o \ modules/audio_processing/aec/main/source/aec_core.o \ modules/audio_processing/aec/main/source/aec_core_sse2.o \ modules/audio_processing/aec/main/source/aec_rdft.o \ modules/audio_processing/aec/main/source/aec_rdft_sse2.o \ modules/audio_processing/aec/main/source/resampler.o AGC_OBJS := modules/audio_processing/agc/main/source/analog_agc.o \ modules/audio_processing/agc/main/source/digital_agc.o APM_OBJS := modules/audio_processing/utility/ring_buffer.o \ modules/audio_processing/utility/fft4g.o SPL_OBJS := common_audio/signal_processing_library/main/source/auto_corr_to_refl_coef.o \ common_audio/signal_processing_library/main/source/auto_correlation.o \ common_audio/signal_processing_library/main/source/complex_fft.o \ common_audio/signal_processing_library/main/source/complex_ifft.o \ common_audio/signal_processing_library/main/source/complex_bit_reverse.o \ common_audio/signal_processing_library/main/source/copy_set_operations.o \ common_audio/signal_processing_library/main/source/cos_table.o \ common_audio/signal_processing_library/main/source/cross_correlation.o \ common_audio/signal_processing_library/main/source/division_operations.o \ common_audio/signal_processing_library/main/source/dot_product_with_scale.o \ common_audio/signal_processing_library/main/source/downsample_fast.o \ common_audio/signal_processing_library/main/source/energy.o \ common_audio/signal_processing_library/main/source/filter_ar.o \ common_audio/signal_processing_library/main/source/filter_ar_fast_q12.o \ common_audio/signal_processing_library/main/source/filter_ma_fast_q12.o \ common_audio/signal_processing_library/main/source/get_hanning_window.o \ common_audio/signal_processing_library/main/source/get_scaling_square.o \ common_audio/signal_processing_library/main/source/hanning_table.o \ common_audio/signal_processing_library/main/source/ilbc_specific_functions.o \ common_audio/signal_processing_library/main/source/levinson_durbin.o \ common_audio/signal_processing_library/main/source/lpc_to_refl_coef.o \ common_audio/signal_processing_library/main/source/min_max_operations.o \ common_audio/signal_processing_library/main/source/randn_table.o \ common_audio/signal_processing_library/main/source/randomization_functions.o \ common_audio/signal_processing_library/main/source/refl_coef_to_lpc.o \ common_audio/signal_processing_library/main/source/resample.o \ common_audio/signal_processing_library/main/source/resample_48khz.o \ common_audio/signal_processing_library/main/source/resample_by_2.o \ common_audio/signal_processing_library/main/source/resample_by_2_internal.o \ common_audio/signal_processing_library/main/source/resample_fractional.o \ common_audio/signal_processing_library/main/source/sin_table.o \ common_audio/signal_processing_library/main/source/sin_table_1024.o \ common_audio/signal_processing_library/main/source/spl_sqrt.o \ common_audio/signal_processing_library/main/source/spl_sqrt_floor.o \ common_audio/signal_processing_library/main/source/spl_version.o \ common_audio/signal_processing_library/main/source/splitting_filter.o \ common_audio/signal_processing_library/main/source/sqrt_of_one_minus_x_squared.o \ common_audio/signal_processing_library/main/source/vector_scaling_operations.o \ common_audio/signal_processing_library/main/source/webrtc_fft_t_1024_8.o \ common_audio/signal_processing_library/main/source/webrtc_fft_t_rad.o SWR_OBJS := system_wrappers/source/aligned_malloc.o \ system_wrappers/source/atomic32.o \ system_wrappers/source/condition_variable.o \ system_wrappers/source/cpu.o \ system_wrappers/source/cpu_features.o \ system_wrappers/source/critical_section.o \ system_wrappers/source/event.o \ system_wrappers/source/file_impl.o \ system_wrappers/source/list_no_stl.o \ system_wrappers/source/map.o \ system_wrappers/source/rw_lock.o \ system_wrappers/source/sort.o \ system_wrappers/source/thread.o \ system_wrappers/source/trace_impl.o NS_OBJS := modules/audio_processing/ns/main/source/noise_suppression.o \ modules/audio_processing/ns/main/source/ns_core.o export WEBRTCAEC_SRCDIR = $(WEBRTC_SRC_BASE)/src export WEBRTCAEC_OBJS = $(AEC_OBJS) $(AGC_OBJS) $(NS_OBJS) $(APM_OBJS) $(SWR_OBJS) $(SPL_OBJS) export WEBRTCAEC_CFLAGS = $(_CFLAGS) export WEBRTCAEC_CXXFLAGS = $(_CXXFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Main entry # # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. # TARGETS := libwebrtcaec all: $(TARGETS) doc: cd .. && doxygen docs/doxygen.cfg dep: depend distclean: realclean .PHONY: dep depend libwebrtcaec clean realclean distclean libwebrtcaec: $(MAKE) -f $(RULES_MAK) APP=WEBRTCAEC app=libwebrtcaec $(WEBRTCAEC_LIB) clean print_lib: $(MAKE) -f $(RULES_MAK) APP=WEBRTCAEC app=libwebrtcaec $@ realclean: $(subst @@,$(subst /,$(HOST_PSEP),.ilbc-$(TARGET_NAME).depend),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=WEBRTCAEC app=libwebrtcaec $@ depend: $(MAKE) -f $(RULES_MAK) APP=WEBRTCAEC app=libwebrtcaec $@ ================================================ FILE: deps/pjsip/third_party/build/webrtc/os-auto.mak.in ================================================ # @configure_input@ AC_WEBRTC_PLATFORM=@ac_webrtc_platform@ ifeq ($(AC_WEBRTC_PLATFORM),webrtc_linux) export CFLAGS += -DWEBRTC_TARGET_PC -DWEBRTC_LINUX -DWEBRTC_THREAD_RR export CFLAGS += -pthread export WEBRTCAEC_OBJS += system_wrappers/source/condition_variable_posix.o \ system_wrappers/source/critical_section_posix.o \ system_wrappers/source/event_posix.o \ system_wrappers/source/rw_lock_posix.o \ system_wrappers/source/thread_posix.o \ system_wrappers/source/trace_posix.o \ system_wrappers/source/cpu_linux.o endif ifeq ($(AC_WEBRTC_PLATFORM),webrtc_darwinos) export CFLAGS += -DWEBRTC_TARGET_MAC_INTEL -DWEBRTC_MAC_INTEL -DWEBRTC_MAC -DWEBRTC_THREAD_RR -DWEBRTC_CLOCK_TYPE_REALTIME export CFLAGS += -pthread export WEBRTCAEC_OBJS += system_wrappers/source/condition_variable_posix.o \ system_wrappers/source/critical_section_posix.o \ system_wrappers/source/event_posix.o \ system_wrappers/source/rw_lock_posix.o \ system_wrappers/source/thread_posix.o \ system_wrappers/source/trace_posix.o \ system_wrappers/source/cpu_mac.o endif ifeq ($(AC_WEBRTC_PLATFORM),webrtc_win32) export CFLAGS += -DWEBRTC_TARGET_PC -D_WIN32 -D_CRT_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_DEPRECATE -D__STD_C export WEBRTCAEC_OBJS += system_wrappers/source/condition_variable_windows.o \ system_wrappers/source/critical_section_windows.o \ system_wrappers/source/event_windows.o \ system_wrappers/source/rw_lock_windows.o \ system_wrappers/source/thread_windows.o \ system_wrappers/source/trace_windows.o \ system_wrappers/source/cpu_windows.o endif ================================================ FILE: deps/pjsip/third_party/build/zsrtp/Makefile ================================================ include ../../../build.mak include ../../../build/common.mak export LIBDIR := ../../lib RULES_MAK := $(PJDIR)/build/rules.mak export ZSRTP_LIB := ../../lib/libzsrtp-$(TARGET_NAME)$(LIBEXT) ############################################################################### # Gather all flags. # export _CFLAGS := $(CC_INC). $(CC_INC)../../zsrtp/include \ $(CC_INC)../../zsrtp/zrtp \ $(CC_INC)../../zsrtp/zrtp/zrtp \ $(CC_INC)../../zsrtp/zrtp/zrtp/libzrtpcpp \ $(CC_INC)../../zsrtp/zrtp/srtp \ $(CC_INC)../../zsrtp/zrtp/srtp/crypto \ $(CC_INC)../../../pjlib/include \ $(CC_INC)../../../pjlib-util/include \ $(CC_INC)../../../pjmedia/include \ $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ $(CFLAGS) -fno-strict-aliasing export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ $(HOST_CXXFLAGS) $(CXXFLAGS) export _LDFLAGS := $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ $(LDFLAGS) -lsqlite3 -lstdc++ ciphersossl = zrtp/srtp/crypto/openssl/SrtpSymCrypto.o \ zrtp/srtp/crypto/openssl/hmac.o \ zrtp/zrtp/crypto/openssl/zrtpDH.o \ zrtp/zrtp/crypto/openssl/hmac256.o \ zrtp/zrtp/crypto/openssl/sha256.o \ zrtp/zrtp/crypto/openssl/hmac384.o \ zrtp/zrtp/crypto/openssl/sha384.o \ zrtp/zrtp/crypto/openssl/aesCFB.o skeinmac = zrtp/cryptcommon/skein.o \ zrtp/cryptcommon/skein_block.o \ zrtp/cryptcommon/skeinApi.o \ zrtp/cryptcommon/macSkein.o \ zrtp/zrtp/crypto/skein256.o \ zrtp/zrtp/crypto/skein384.o \ zrtp/zrtp/crypto/skeinMac256.o \ zrtp/zrtp/crypto/skeinMac384.o twofish = zrtp/cryptcommon/twofish.o \ zrtp/cryptcommon/twofish_cfb.o \ zrtp/zrtp/crypto/twoCFB.o common = zrtp/common/osSpecifics.o # Gcrypt support currently not tested #ciphersgcrypt = crypto/gcrypt/gcryptAesSrtp.o crypto/gcrypt/gcrypthmac.o \ # crypto/gcrypt/InitializeGcrypt.o zrtpobj = zrtp/zrtp/ZrtpCallbackWrapper.o \ zrtp/zrtp/ZIDCacheDb.o \ zrtp/zrtp/ZIDRecordDb.o \ zrtp/zrtp/zrtpCacheSqliteBackend.o \ zrtp/zrtp/ZRtp.o \ zrtp/zrtp/ZrtpCrc32.o \ zrtp/zrtp/ZrtpPacketCommit.o \ zrtp/zrtp/ZrtpPacketConf2Ack.o \ zrtp/zrtp/ZrtpPacketConfirm.o \ zrtp/zrtp/ZrtpPacketDHPart.o \ zrtp/zrtp/ZrtpPacketGoClear.o \ zrtp/zrtp/ZrtpPacketClearAck.o \ zrtp/zrtp/ZrtpPacketHelloAck.o \ zrtp/zrtp/ZrtpPacketHello.o \ zrtp/zrtp/ZrtpPacketError.o \ zrtp/zrtp/ZrtpPacketErrorAck.o \ zrtp/zrtp/ZrtpPacketPingAck.o \ zrtp/zrtp/ZrtpPacketPing.o \ zrtp/zrtp/ZrtpPacketSASrelay.o \ zrtp/zrtp/ZrtpPacketRelayAck.o \ zrtp/zrtp/ZrtpStateClass.o \ zrtp/zrtp/ZrtpTextData.o \ zrtp/zrtp/ZrtpConfigure.o \ zrtp/zrtp/ZrtpCWrapper.o \ zrtp/zrtp/Base32.o \ zrtp/zrtp/zrtpB64Encode.o \ zrtp/zrtp/zrtpB64Decode.o srtpobj = srtp/ZsrtpCWrapper.o \ zrtp/srtp/CryptoContext.o \ zrtp/srtp/CryptoContextCtrl.o cryptobj = $(ciphersossl) $(skeinmac) $(twofish) export ZSRTP_SRCDIR = ../../zsrtp export ZSRTP_OBJS = $(zrtpobj) $(cryptobj) $(srtpobj) $(common) export ZSRTP_CFLAGS = $(_CFLAGS) export ZSRTP_CXXFLAGS = $(_CXXFLAGS) export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT ############################################################################### # Main entry # # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. # TARGETS := libzsrtp all: $(TARGETS) doc: cd .. && doxygen docs/doxygen.cfg dep: depend distclean: realclean .PHONY: dep depend libzsrtp clean realclean distclean libzsrtp: $(MAKE) -f $(RULES_MAK) APP=ZSRTP app=libzsrtp $(ZSRTP_LIB) clean print_lib: $(MAKE) -f $(RULES_MAK) APP=ZSRTP app=libzsrtp $@ realclean: $(subst @@,$(subst /,$(HOST_PSEP),.ilbc-$(TARGET_NAME).depend),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=ZSRTP app=libzsrtp $@ depend: $(MAKE) -f $(RULES_MAK) APP=ZSRTP app=libzsrtp $@ ================================================ FILE: deps/pjsip/third_party/g7221/common/basic_op.c ================================================ #include "config.h" #if !PJMEDIA_LIBG7221_FUNCS_INLINED || \ (PJMEDIA_LIBG7221_FUNCS_INLINED && defined(__BASIC_OP_H__)) /*___________________________________________________________________________ | | | Basic arithmetic operators. | |___________________________________________________________________________| */ /*___________________________________________________________________________ | | | Include-Files | |___________________________________________________________________________| */ //#include //#include #include "typedef.h" #include "basic_op.h" #include #if (WMOPS) #include "count.h" extern BASIC_OP multiCounter[MAXCOUNTERS]; extern int currCounter; #endif /*___________________________________________________________________________ | | | Constants and Globals | |___________________________________________________________________________| */ #if INCLUDE_UNSAFE Flag g7221_Overflow = 0; Flag g7221_Carry = 0; #endif /*___________________________________________________________________________ | | | Functions | |___________________________________________________________________________| */ /*___________________________________________________________________________ | | | Function Name : shr | | | | Purpose : | | | | Arithmetically shift the 16 bit input var1 right var2 positions with | | sign extension. If var2 is negative, arithmetically shift var1 left by | | -var2 with sign extension. Saturate the result in case of underflows or | | overflows. | | | | Complexity weight : 1 | | | | Inputs : | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ LIBG7221_DEF(Word16) shr (Word16 var1, Word16 var2) { if (var2 < 0) { if (var2 < -16) var2 = -16; return shl_nocheck(var1, (Word16) -var2); } else { return shr_nocheck(var1, var2); } } /* ------------------------- End of shr() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : shl | | | | Purpose : | | | | Arithmetically shift the 16 bit input var1 left var2 positions.Zero fill| | the var2 LSB of the result. If var2 is negative, arithmetically shift | | var1 right by -var2 with sign extension. Saturate the result in case of | | underflows or overflows. | | | | Complexity weight : 1 | | | | Inputs : | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ LIBG7221_DEF(Word16) shl (Word16 var1, Word16 var2) { if (var2 < 0) { return shr_nocheck(var1, (Word16) -var2); } else { return shl_nocheck(var1, var2); } } /* ------------------------- End of shl() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : mult | | | | Purpose : | | | | Performs the multiplication of var1 by var2 and gives a 16 bit result | | which is scaled i.e.: | | mult(var1,var2) = extract_l(L_shr((var1 times var2),15)) and | | mult(-32768,-32768) = 32767. | | | | Complexity weight : 1 | | | | Inputs : | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ LIBG7221_DEF(Word16) mult (Word16 var1, Word16 var2) { Word16 var_out; Word32 L_product; L_product = (Word32) var1 *(Word32) var2; L_product = (L_product & (Word32) 0xffff8000L) >> 15; if (L_product & (Word32) 0x00010000L) L_product = L_product | (Word32) 0xffff0000L; var_out = saturate (L_product); #if (WMOPS) multiCounter[currCounter].mult++; #endif return (var_out); } /* ------------------------- End of mult() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : L_msu | | | | Purpose : | | | | Multiply var1 by var2 and shift the result left by 1. Subtract the 32 | | bit result to L_var3 with saturation, return a 32 bit result: | | L_msu(L_var3,var1,var2) = L_sub(L_var3,L_mult(var1,var2)). | | | | Complexity weight : 1 | | | | Inputs : | | | | L_var3 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. | |___________________________________________________________________________| */ LIBG7221_DEF(Word32) L_msu (Word32 L_var3, Word16 var1, Word16 var2) { Word32 L_var_out; Word32 L_product; L_product = L_mult (var1, var2); #if (WMOPS) multiCounter[currCounter].L_mult--; #endif L_var_out = L_sub (L_var3, L_product); #if (WMOPS) multiCounter[currCounter].L_sub--; multiCounter[currCounter].L_msu++; #endif return (L_var_out); } /* ------------------------- End of L_msu() ------------------------- */ #if INCLUDE_UNSAFE /*___________________________________________________________________________ | | | Function Name : L_macNs | | | | Purpose : | | | | Multiply var1 by var2 and shift the result left by 1. Add the 32 bit | | result to L_var3 without saturation, return a 32 bit result. Generate | | carry and overflow values : | | L_macNs(L_var3,var1,var2) = L_add_c(L_var3,L_mult(var1,var2)). | | | | Complexity weight : 1 | | | | Inputs : | | | | L_var3 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. | | | | Caution : | | | | In some cases the Carry flag has to be cleared or set before using | | operators which take into account its value. | |___________________________________________________________________________| */ LIBG7221_DEF(Word32) L_macNs (Word32 L_var3, Word16 var1, Word16 var2) { Word32 L_var_out; L_var_out = L_mult (var1, var2); #if (WMOPS) multiCounter[currCounter].L_mult--; #endif L_var_out = L_add_c (L_var3, L_var_out); #if (WMOPS) multiCounter[currCounter].L_add_c--; multiCounter[currCounter].L_macNs++; #endif return (L_var_out); } #endif /* ------------------------- End of L_macNs() ------------------------- */ #if INCLUDE_UNSAFE /*___________________________________________________________________________ | | | Function Name : L_msuNs | | | | Purpose : | | | | Multiply var1 by var2 and shift the result left by 1. Subtract the 32 | | bit result from L_var3 without saturation, return a 32 bit result. Ge- | | nerate carry and overflow values : | | L_msuNs(L_var3,var1,var2) = L_sub_c(L_var3,L_mult(var1,var2)). | | | | Complexity weight : 1 | | | | Inputs : | | | | L_var3 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. | | | | Caution : | | | | In some cases the Carry flag has to be cleared or set before using | | operators which take into account its value. | |___________________________________________________________________________| */ LIBG7221_DEF(Word32) L_msuNs (Word32 L_var3, Word16 var1, Word16 var2) { Word32 L_var_out; L_var_out = L_mult (var1, var2); #if (WMOPS) multiCounter[currCounter].L_mult--; #endif L_var_out = L_sub_c (L_var3, L_var_out); #if (WMOPS) multiCounter[currCounter].L_sub_c--; multiCounter[currCounter].L_msuNs++; #endif return (L_var_out); } #endif /* ------------------------- End of L_msuNs() ------------------------- */ #if INCLUDE_UNSAFE /*___________________________________________________________________________ | | | Function Name : L_add_c | | | | Purpose : | | | | Performs 32 bits addition of the two 32 bits variables (L_var1+L_var2+C)| | with carry. No saturation. Generate carry and Overflow values. The car- | | ry and overflow values are binary variables which can be tested and as- | | signed values. | | | | Complexity weight : 2 | | | | Inputs : | | | | L_var1 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | L_var2 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. | | | | Caution : | | | | In some cases the Carry flag has to be cleared or set before using | | operators which take into account its value. | |___________________________________________________________________________| */ LIBG7221_DEF(Word32) L_add_c (Word32 L_var1, Word32 L_var2) { Word32 L_var_out; Word32 L_test; Flag carry_int = 0; L_var_out = L_var1 + L_var2 + GET_CARRY(); L_test = L_var1 + L_var2; if ((L_var1 > 0) && (L_var2 > 0) && (L_test < 0)) { SET_OVERFLOW(1); carry_int = 0; } else { if ((L_var1 < 0) && (L_var2 < 0)) { if (L_test >= 0) { SET_OVERFLOW(1); carry_int = 1; } else { SET_OVERFLOW(0); carry_int = 1; } } else { if (((L_var1 ^ L_var2) < 0) && (L_test >= 0)) { SET_OVERFLOW(0); carry_int = 1; } else { SET_OVERFLOW(0); carry_int = 0; } } } if (GET_CARRY()) { if (L_test == MAX_32) { SET_OVERFLOW(1); SET_CARRY(carry_int); } else { if (L_test == (Word32) 0xFFFFFFFFL) { SET_CARRY(1); } else { SET_CARRY(carry_int); } } } else { SET_CARRY(carry_int); } #if (WMOPS) multiCounter[currCounter].L_add_c++; #endif return (L_var_out); } #endif /* ------------------------- End of L_add_c() ------------------------- */ #if INCLUDE_UNSAFE /*___________________________________________________________________________ | | | Function Name : L_sub_c | | | | Purpose : | | | | Performs 32 bits subtraction of the two 32 bits variables with carry | | (borrow) : L_var1-L_var2-C. No saturation. Generate carry and Overflow | | values. The carry and overflow values are binary variables which can | | be tested and assigned values. | | | | Complexity weight : 2 | | | | Inputs : | | | | L_var1 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | L_var2 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. | | | | Caution : | | | | In some cases the Carry flag has to be cleared or set before using | | operators which take into account its value. | |___________________________________________________________________________| */ LIBG7221_DEF(Word32) L_sub_c (Word32 L_var1, Word32 L_var2) { Word32 L_var_out; Word32 L_test; Flag carry_int = 0; if (GET_CARRY()) { SET_CARRY(0); if (L_var2 != MIN_32) { L_var_out = L_add_c (L_var1, -L_var2); #if (WMOPS) multiCounter[currCounter].L_add_c--; #endif } else { L_var_out = L_var1 - L_var2; if (L_var1 > 0L) { SET_OVERFLOW(1); SET_CARRY(0); } } } else { L_var_out = L_var1 - L_var2 - (Word32) 0X00000001L; L_test = L_var1 - L_var2; if ((L_test < 0) && (L_var1 > 0) && (L_var2 < 0)) { SET_OVERFLOW(1); carry_int = 0; } else if ((L_test > 0) && (L_var1 < 0) && (L_var2 > 0)) { SET_OVERFLOW(1); carry_int = 1; } else if ((L_test > 0) && ((L_var1 ^ L_var2) > 0)) { SET_OVERFLOW(0); carry_int = 1; } if (L_test == MIN_32) { SET_OVERFLOW(1); SET_CARRY(carry_int); } else { SET_CARRY(carry_int); } } #if (WMOPS) multiCounter[currCounter].L_sub_c++; #endif return (L_var_out); } #endif /* ------------------------- End of L_sub_c() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : L_negate | | | | Purpose : | | | | Negate the 32 bit variable L_var1 with saturation; saturate in the case | | where input is -2147483648 (0x8000 0000). | | | | Complexity weight : 2 | | | | Inputs : | | | | L_var1 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. | |___________________________________________________________________________| */ LIBG7221_DEF(Word32) L_negate (Word32 L_var1) { Word32 L_var_out; L_var_out = (L_var1 == MIN_32) ? MAX_32 : -L_var1; #if (WMOPS) multiCounter[currCounter].L_negate++; #endif return (L_var_out); } /* ------------------------- End of L_negate() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : mult_r | | | | Purpose : | | | | Same as mult with rounding, i.e.: | | mult_r(var1,var2) = extract_l(L_shr(((var1 * var2) + 16384),15)) and | | mult_r(-32768,-32768) = 32767. | | | | Complexity weight : 2 | | | | Inputs : | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ LIBG7221_DEF(Word16) mult_r (Word16 var1, Word16 var2) { Word16 var_out; Word32 L_product_arr; L_product_arr = (Word32) var1 *(Word32) var2; /* product */ L_product_arr += (Word32) 0x00004000L; /* round */ L_product_arr &= (Word32) 0xffff8000L; L_product_arr >>= 15; /* shift */ if (L_product_arr & (Word32) 0x00010000L) /* sign extend when necessary */ { L_product_arr |= (Word32) 0xffff0000L; } var_out = saturate (L_product_arr); #if (WMOPS) multiCounter[currCounter].mult_r++; #endif return (var_out); } /* ------------------------- End of mult_r() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : shr_r | | | | Purpose : | | | | Same as shr(var1,var2) but with rounding. Saturate the result in case of| | underflows or overflows : | | - If var2 is greater than zero : | | if (sub(shl(shr(var1,var2),1),shr(var1,sub(var2,1)))) | | is equal to zero | | then | | shr_r(var1,var2) = shr(var1,var2) | | else | | shr_r(var1,var2) = add(shr(var1,var2),1) | | - If var2 is less than or equal to zero : | | shr_r(var1,var2) = shr(var1,var2). | | | | Complexity weight : 2 | | | | Inputs : | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ LIBG7221_DEF(Word16) shr_r (Word16 var1, Word16 var2) { Word16 var_out; if (var2 > 15) { var_out = 0; } else { var_out = shr (var1, var2); #if (WMOPS) multiCounter[currCounter].shr--; #endif if (var2 > 0) { if ((var1 & ((Word16) 1 << (var2 - 1))) != 0) { var_out++; } } } #if (WMOPS) multiCounter[currCounter].shr_r++; #endif return (var_out); } /* ------------------------- End of shr_r() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : mac_r | | | | Purpose : | | | | Multiply var1 by var2 and shift the result left by 1. Add the 32 bit | | result to L_var3 with saturation. Round the LS 16 bits of the result | | into the MS 16 bits with saturation and shift the result right by 16. | | Return a 16 bit result. | | mac_r(L_var3,var1,var2) = round(L_mac(L_var3,var1,var2)) | | | | Complexity weight : 2 | | | | Inputs : | | | | L_var3 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0x0000 8000 <= L_var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ LIBG7221_DEF(Word16) mac_r (Word32 L_var3, Word16 var1, Word16 var2) { Word16 var_out; L_var3 = L_mac (L_var3, var1, var2); #if (WMOPS) multiCounter[currCounter].L_mac--; #endif L_var3 = L_add (L_var3, (Word32) 0x00008000L); #if (WMOPS) multiCounter[currCounter].L_add--; #endif var_out = extract_h (L_var3); #if (WMOPS) multiCounter[currCounter].extract_h--; multiCounter[currCounter].mac_r++; #endif return (var_out); } /* ------------------------- End of mac_r() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : msu_r | | | | Purpose : | | | | Multiply var1 by var2 and shift the result left by 1. Subtract the 32 | | bit result to L_var3 with saturation. Round the LS 16 bits of the res- | | ult into the MS 16 bits with saturation and shift the result right by | | 16. Return a 16 bit result. | | msu_r(L_var3,var1,var2) = round(L_msu(L_var3,var1,var2)) | | | | Complexity weight : 2 | | | | Inputs : | | | | L_var3 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0x0000 8000 <= L_var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ LIBG7221_DEF(Word16) msu_r (Word32 L_var3, Word16 var1, Word16 var2) { Word16 var_out; L_var3 = L_msu (L_var3, var1, var2); #if (WMOPS) multiCounter[currCounter].L_msu--; #endif L_var3 = L_add (L_var3, (Word32) 0x00008000L); #if (WMOPS) multiCounter[currCounter].L_add--; #endif var_out = extract_h (L_var3); #if (WMOPS) multiCounter[currCounter].extract_h--; multiCounter[currCounter].msu_r++; #endif return (var_out); } /* ------------------------- End of msu_r() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : L_deposit_h | | | | Purpose : | | | | Deposit the 16 bit var1 into the 16 MS bits of the 32 bit output. The | | 16 LS bits of the output are zeroed. | | | | Complexity weight : 2 | | | | Inputs : | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= var_out <= 0x7fff 0000. | |___________________________________________________________________________| */ LIBG7221_DEF(Word32) L_deposit_h (Word16 var1) { Word32 L_var_out; L_var_out = (Word32) var1 << 16; #if (WMOPS) multiCounter[currCounter].L_deposit_h++; #endif return (L_var_out); } /* ------------------------- End of L_deposit_h() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : L_deposit_l | | | | Purpose : | | | | Deposit the 16 bit var1 into the 16 LS bits of the 32 bit output. The | | 16 MS bits of the output are sign extended. | | | | Complexity weight : 2 | | | | Inputs : | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0xFFFF 8000 <= var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ LIBG7221_DEF(Word32) L_deposit_l (Word16 var1) { Word32 L_var_out; L_var_out = (Word32) var1; #if (WMOPS) multiCounter[currCounter].L_deposit_l++; #endif return (L_var_out); } /* ------------------------- End of L_deposit_l() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : L_shr_r | | | | Purpose : | | | | Same as L_shr(L_var1,var2) but with rounding. Saturate the result in | | case of underflows or overflows : | | - If var2 is greater than zero : | | if (L_sub(L_shl(L_shr(L_var1,var2),1),L_shr(L_var1,sub(var2,1))))| | is equal to zero | | then | | L_shr_r(L_var1,var2) = L_shr(L_var1,var2) | | else | | L_shr_r(L_var1,var2) = L_add(L_shr(L_var1,var2),1) | | - If var2 is less than or equal to zero : | | L_shr_r(L_var1,var2) = L_shr(L_var1,var2). | | | | Complexity weight : 3 | | | | Inputs : | | | | L_var1 | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= var1 <= 0x7fff ffff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= var_out <= 0x7fff ffff. | |___________________________________________________________________________| */ LIBG7221_DEF(Word32) L_shr_r (Word32 L_var1, Word16 var2) { Word32 L_var_out; if (var2 > 31) { L_var_out = 0; } else { L_var_out = L_shr (L_var1, var2); #if (WMOPS) multiCounter[currCounter].L_shr--; #endif if (var2 > 0) { if ((L_var1 & ((Word32) 1 << (var2 - 1))) != 0) { L_var_out++; } } } #if (WMOPS) multiCounter[currCounter].L_shr_r++; #endif return (L_var_out); } /* ------------------------- End of L_shr_r() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : L_abs | | | | Purpose : | | | | Absolute value of L_var1; Saturate in case where the input is | | -214783648 | | | | Complexity weight : 3 | | | | Inputs : | | | | L_var1 | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= var1 <= 0x7fff ffff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x0000 0000 <= var_out <= 0x7fff ffff. | |___________________________________________________________________________| */ LIBG7221_DEF(Word32) L_abs (Word32 L_var1) { Word32 L_var_out; if (L_var1 == MIN_32) { L_var_out = MAX_32; } else { if (L_var1 < 0) { L_var_out = -L_var1; } else { L_var_out = L_var1; } } #if (WMOPS) multiCounter[currCounter].L_abs++; #endif return (L_var_out); } /* ------------------------- End of L_abs() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : norm_s | | | | Purpose : | | | | Produces the number of left shift needed to normalize the 16 bit varia- | | ble var1 for positive values on the interval with minimum of 16384 and | | maximum of 32767, and for negative values on the interval with minimum | | of -32768 and maximum of -16384; in order to normalize the result, the | | following operation must be done : | | norm_var1 = shl(var1,norm_s(var1)). | | | | Complexity weight : 15 | | | | Inputs : | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0x0000 0000 <= var_out <= 0x0000 000f. | |___________________________________________________________________________| */ LIBG7221_DEF(Word16) norm_s (Word16 var1) { Word16 var_out; if (var1 == 0) { var_out = 0; } else { if ((UWord16)var1 == (UWord16)0xffff) { var_out = 15; } else { if (var1 < 0) { var1 = (Word16)(~var1); } for (var_out = 0; var1 < 0x4000; var_out++) { var1 <<= 1; } } } #if (WMOPS) multiCounter[currCounter].norm_s++; #endif return (var_out); } /* ------------------------- End of norm_s() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : div_s | | | | Purpose : | | | | Produces a result which is the fractional integer division of var1 by | | var2; var1 and var2 must be positive and var2 must be greater or equal | | to var1; the result is positive (leading bit equal to 0) and truncated | | to 16 bits. | | If var1 = var2 then div(var1,var2) = 32767. | | | | Complexity weight : 18 | | | | Inputs : | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0x0000 0000 <= var1 <= var2 and var2 != 0. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : var1 <= var2 <= 0x0000 7fff and var2 != 0. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0x0000 0000 <= var_out <= 0x0000 7fff. | | It's a Q15 value (point between b15 and b14). | |___________________________________________________________________________| */ LIBG7221_DEF(Word16) div_s (Word16 var1, Word16 var2) { Word16 var_out = 0; Word16 iteration; Word32 L_num; Word32 L_denom; if ((var1 > var2) || (var1 < 0) || (var2 < 0)) { //printf ("Division Error var1=%d var2=%d\n", var1, var2); //abort(); /* exit (0); */ pj_assert(!"Division Error"); } if (var2 == 0) { //printf ("Division by 0, Fatal error \n"); //abort(); /* exit (0); */ assert(!"Division by 0"); } if (var1 == 0) { var_out = 0; } else { if (var1 == var2) { var_out = MAX_16; } else { L_num = L_deposit_l (var1); #if (WMOPS) multiCounter[currCounter].L_deposit_l--; #endif L_denom = L_deposit_l (var2); #if (WMOPS) multiCounter[currCounter].L_deposit_l--; #endif for (iteration = 0; iteration < 15; iteration++) { var_out <<= 1; L_num <<= 1; if (L_num >= L_denom) { L_num = L_sub (L_num, L_denom); #if (WMOPS) multiCounter[currCounter].L_sub--; #endif var_out = add (var_out, 1); #if (WMOPS) multiCounter[currCounter].add--; #endif } } } } #if (WMOPS) multiCounter[currCounter].div_s++; #endif return (var_out); } /* ------------------------- End of div_s() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : norm_l | | | | Purpose : | | | | Produces the number of left shifts needed to normalize the 32 bit varia-| | ble L_var1 for positive values on the interval with minimum of | | 1073741824 and maximum of 2147483647, and for negative values on the in-| | terval with minimum of -2147483648 and maximum of -1073741824; in order | | to normalize the result, the following operation must be done : | | norm_L_var1 = L_shl(L_var1,norm_l(L_var1)). | | | | Complexity weight : 30 | | | | Inputs : | | | | L_var1 | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= var1 <= 0x7fff ffff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0x0000 0000 <= var_out <= 0x0000 001f. | |___________________________________________________________________________| */ LIBG7221_DEF(Word16) norm_l (Word32 L_var1) { Word16 var_out; if (L_var1 == 0) { var_out = 0; } else { if (L_var1 == (Word32) 0xffffffffL) { var_out = 31; } else { if (L_var1 < 0) { L_var1 = ~L_var1; } for (var_out = 0; L_var1 < (Word32) 0x40000000L; var_out++) { L_var1 <<= 1; } } } #if (WMOPS) multiCounter[currCounter].norm_l++; #endif return (var_out); } /* ------------------------- End of norm_l() ------------------------- */ /* ***************************************************************** Additional operators extracted from the G.723.1 Library Adapted for WMOPS calculations ***************************************************************** */ /*___________________________________________________________________________ | | | Function Name : L_mls | | | | Purpose : | | | | Multiplies a 16 bit word v by a 32 bit word Lv and returns a 32 bit | | word (multiplying 16 by 32 bit words gives 48 bit word; the function | | extracts the 32 MSB and shift the result to the left by 1). | | | | A 32 bit word can be written as | | Lv = a + b * 2^16 | | where a= unsigned 16 LSBs and b= signed 16 MSBs. | | The function returns v * Lv / 2^15 which is equivalent to | | a*v / 2^15 + b*v*2 | | | | Complexity weight : 6 [to be confirmed] | | | | Inputs : | | | | Lv | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= var1 <= 0x7fff ffff. | | v | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0x8000 <= var1 <= 0x7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= var_out <= 0x7fff ffff. | | | |___________________________________________________________________________| */ LIBG7221_DEF(Word32) L_mls (Word32 Lv, Word16 v) { Word32 Temp ; Temp = Lv & (Word32) 0x0000ffff ; Temp = Temp * (Word32) v ; Temp = L_shr_nocheck( Temp, (Word16) 15 ) ; Temp = L_mac( Temp, v, extract_h(Lv) ) ; #if (WMOPS) multiCounter[currCounter].L_shr--; multiCounter[currCounter].L_mac--; multiCounter[currCounter].extract_h--; multiCounter[currCounter].L_mls++; #endif return Temp ; } /* ------------------------- End of L_mls() ------------------------- */ /*__________________________________________________________________________ | | | Function Name : div_l | | | | Purpose : | | | | Produces a result which is the fractional integer division of L_var1 by| | var2; L_var1 and var2 must be positive and var2 << 16 must be greater or| | equal to L_var1; the result is positive (leading bit equal to 0) and | | truncated to 16 bits. | | If L_var1 == var2 << 16 then div_l(L_var1,var2) = 32767. | | | | Complexity weight : 20 | | | | Inputs : | | | | L_var1 | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x0000 0000 <= var1 <= (var2 << 16) and var2 != 0. | | L_var1 must be considered as a Q.31 value | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : var1 <= (var2<< 16) <= 0x7fff0000 and var2 != 0. | | var2 must be considered as a Q.15 value | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0x0000 0000 <= var_out <= 0x0000 7fff. | | It's a Q15 value (point between b15 and b14). | |___________________________________________________________________________| */ LIBG7221_DEF(Word16) div_l (Word32 L_num, Word16 den) { Word16 var_out = (Word16)0; Word32 L_den; Word16 iteration; #if (WMOPS) multiCounter[currCounter].div_l++; #endif if ( den == (Word16) 0 ) { //printf("Division by 0 in div_l, Fatal error \n"); //exit(0); assert(!"Division by 0"); } if ( (L_num < (Word32) 0) || (den < (Word16) 0) ) { //printf("Division Error in div_l, Fatal error \n"); //exit(0); assert(!"Division Error"); } L_den = L_deposit_h( den ) ; #if (WMOPS) multiCounter[currCounter].L_deposit_h--; #endif if ( L_num >= L_den ){ return MAX_16 ; } else { L_num = L_shr_nocheck(L_num, (Word16)1) ; L_den = L_shr_nocheck(L_den, (Word16)1); #if (WMOPS) multiCounter[currCounter].L_shr-=2; #endif for(iteration=(Word16)0; iteration< (Word16)15;iteration++) { var_out = shl_nocheck( var_out, (Word16)1); L_num = L_shl_nocheck( L_num, (Word16)1); #if (WMOPS) multiCounter[currCounter].shl--; multiCounter[currCounter].L_shl--; #endif if (L_num >= L_den) { L_num = L_sub(L_num,L_den); var_out = add(var_out, (Word16)1); #if (WMOPS) multiCounter[currCounter].L_sub--; multiCounter[currCounter].add--; #endif } } return var_out; } } /* ------------------------- End of div_l() ------------------------- */ /*__________________________________________________________________________ | | | Function Name : i_mult | | | | Purpose : | | | | Integer 16-bit multiplication. No overflow protection is performed if | | ORIGINAL_G7231 is defined. | | | | Complexity weight : TBD | | | | Inputs : | | | | a | | 16 bit short signed integer (Word16). | | | | b | | 16 bit short signed integer (Word16). | | | | Outputs : | | | | none | | | | Return Value : | | | | 16 bit short signed integer (Word16). No overflow checks | | are performed if ORIGINAL_G7231 is defined. | |___________________________________________________________________________| */ LIBG7221_DEF(Word16) i_mult (Word16 a, Word16 b) { #ifdef ORIGINAL_G7231 return a*b ; #else Word32 register c=a*b; #if (WMOPS) multiCounter[currCounter].i_mult++; #endif return saturate(c) ; #endif } /* ------------------------- End of i_mult() ------------------------- */ /* ********************************************************************** The following three operators are not part of the original G.729/G.723.1 set of basic operators and implement shiftless accumulation operation. ********************************************************************** */ /*___________________________________________________________________________ | | Function Name : L_mult0 | | Purpose : | | L_mult0 is the 32 bit result of the multiplication of var1 times var2 | without one left shift. | | Complexity weight : 1 | | Inputs : | | var1 16 bit short signed integer (Word16) whose value falls in the | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | var2 16 bit short signed integer (Word16) whose value falls in the | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | Return Value : | | L_var_out | 32 bit long signed integer (Word32) whose value falls in the | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. |___________________________________________________________________________ */ LIBG7221_DEF(Word32) L_mult0 (Word16 var1,Word16 var2) { Word32 L_var_out; L_var_out = (Word32)var1 * (Word32)var2; #if (WMOPS) multiCounter[currCounter].L_mult0++; #endif return(L_var_out); } /* ------------------------- End of L_mult0() ------------------------- */ /*___________________________________________________________________________ | | Function Name : L_mac0 | | Purpose : | | Multiply var1 by var2 (without left shift) and add the 32 bit result to | L_var3 with saturation, return a 32 bit result: | L_mac0(L_var3,var1,var2) = L_add(L_var3,(L_mult0(var1,var2)). | | Complexity weight : 1 | | Inputs : | | L_var3 32 bit long signed integer (Word32) whose value falls in the | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | var1 16 bit short signed integer (Word16) whose value falls in the | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | var2 16 bit short signed integer (Word16) whose value falls in the | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | Return Value : | | L_var_out | 32 bit long signed integer (Word32) whose value falls in the | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. |___________________________________________________________________________ */ LIBG7221_DEF(Word32) L_mac0 (Word32 L_var3, Word16 var1, Word16 var2) { Word32 L_var_out; Word32 L_product; L_product = L_mult0(var1,var2); L_var_out = L_add(L_var3,L_product); #if (WMOPS) multiCounter[currCounter].L_mac0++; multiCounter[currCounter].L_mult0--; multiCounter[currCounter].L_add--; #endif return(L_var_out); } /* ------------------------- End of L_mac0() ------------------------- */ /*___________________________________________________________________________ | | Function Name : L_msu0 | | Purpose : | | Multiply var1 by var2 (without left shift) and subtract the 32 bit | result to L_var3 with saturation, return a 32 bit result: | L_msu0(L_var3,var1,var2) = L_sub(L_var3,(L_mult0(var1,var2)). | | Complexity weight : 1 | | Inputs : | | L_var3 32 bit long signed integer (Word32) whose value falls in the | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | var1 16 bit short signed integer (Word16) whose value falls in the | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | var2 16 bit short signed integer (Word16) whose value falls in the | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | Return Value : | | L_var_out | 32 bit long signed integer (Word32) whose value falls in the | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. |___________________________________________________________________________ */ LIBG7221_DEF(Word32) L_msu0 (Word32 L_var3, Word16 var1, Word16 var2) { Word32 L_var_out; Word32 L_product; L_product = L_mult0(var1,var2); L_var_out = L_sub(L_var3,L_product); #if (WMOPS) multiCounter[currCounter].L_msu0++; multiCounter[currCounter].L_mult0--; multiCounter[currCounter].L_sub--; #endif return(L_var_out); } /* ------------------------- End of L_msu0() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : LU_shl | | | | Purpose : | | | | Arithmetically shift the 32 bit input L_var1 left var2 positions. Zero | | fill the var2 LSB of the result. If var2 is negative, arithmetically | | shift L_var1 right by -var2 with sign extension. Saturate the result in | | case of underflows or overflows. | | | | Complexity weight : 2 | | | | Inputs : | | | | L_var1 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. | |___________________________________________________________________________| */ LIBG7221_DEF(UWord32) LU_shl (UWord32 L_var1, Word16 var2) { Word16 neg_var2; UWord32 L_var_out = 0; if (var2 <= 0) { if (var2 < -32) var2 = -32; neg_var2 = negate(var2); L_var_out = LU_shr (L_var1, neg_var2); #if (WMOPS) multiCounter[currCounter].negate--; multiCounter[currCounter].LU_shr--; #endif } else { for (; var2 > 0; var2--) { if (L_var1 > (UWord32) 0X7fffffffL) { SET_OVERFLOW(1); L_var_out = UMAX_32; break; } else { if (L_var1 < (UWord32) 0x00000001L) { SET_OVERFLOW(1); L_var_out = (UWord32)MIN_32; break; } } L_var1 *= 2; L_var_out = L_var1; } } #if (WMOPS) multiCounter[currCounter].LU_shl++; #endif return (L_var_out); } /* ------------------------- End of LU_shl() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : LU_shr | | | | Purpose : | | | | Arithmetically shift the 32 bit input L_var1 right var2 positions with | | sign extension. If var2 is negative, arithmetically shift L_var1 left | | by -var2 and zero fill the -var2 LSB of the result. Saturate the result | | in case of underflows or overflows. | | | | Complexity weight : 2 | | | | Inputs : | | | | L_var1 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. | |___________________________________________________________________________| */ LIBG7221_DEF(UWord32) LU_shr (UWord32 L_var1, Word16 var2) { Word16 neg_var2; UWord32 L_var_out; if (var2 < 0) { if (var2 < -32) var2 = -32; neg_var2 = negate(var2); L_var_out = LU_shl (L_var1, neg_var2); #if (WMOPS) multiCounter[currCounter].negate--; multiCounter[currCounter].LU_shl--; #endif } else { if (var2 >= 32) { L_var_out = 0L; } else { L_var_out = L_var1 >> var2; } } #if (WMOPS) multiCounter[currCounter].LU_shr++; #endif return (L_var_out); } /* ------------------------- End of LU_shr() ------------------------- */ #endif /* PJMEDIA_LIBG7221_FUNCS_INLINED */ /* ************************** END OF BASOP32.C ************************** */ ================================================ FILE: deps/pjsip/third_party/g7221/common/basic_op.h ================================================ #ifndef __BASIC_OP_H__ #define __BASIC_OP_H__ #include "config.h" /*___________________________________________________________________________ | | | Constants and Globals | |___________________________________________________________________________| */ #define MAX_32 (Word32)0x7fffffffL #define MIN_32 (Word32)0x80000000L #define MAX_16 (Word16)0x7fff #define MIN_16 (Word16)(pj_uint16_t)0x8000 #define UMAX_32 (UWord32)0xffffffffL #define UMIN_32 (UWord32)0x00000000L /*___________________________________________________________________________ | | | Prototypes for basic arithmetic operators | |___________________________________________________________________________| */ PJ_INLINE(Word16) add (Word16 var1, Word16 var2); /* Short add, 1 */ PJ_INLINE(Word16) sub (Word16 var1, Word16 var2); /* Short sub, 1 */ PJ_INLINE(Word16) abs_s (Word16 var1); /* Short abs, 1 */ LIBG7221_DECL(Word16) shl (Word16 var1, Word16 var2); /* Short shift left, 1 */ PJ_INLINE(Word16) shl_nocheck(Word16 var1, Word16 var2); LIBG7221_DECL(Word16) shr (Word16 var1, Word16 var2); /* Short shift right, 1 */ PJ_INLINE(Word16) shr_nocheck(Word16 var1, Word16 var2); LIBG7221_DECL(Word16) mult (Word16 var1, Word16 var2); /* Short mult, 1 */ PJ_INLINE(Word32) L_mult (Word16 var1, Word16 var2); /* Long mult, 1 */ PJ_INLINE(Word16) negate (Word16 var1); /* Short negate, 1 */ PJ_INLINE(Word16) extract_h (Word32 L_var1); /* Extract high, 1 */ PJ_INLINE(Word16) extract_l (Word32 L_var1); /* Extract low, 1 */ PJ_INLINE(Word16) itu_round (Word32 L_var1); /* Round, 1 */ PJ_INLINE(Word32) L_mac (Word32 L_var3, Word16 var1, Word16 var2); /* Mac, 1 */ LIBG7221_DECL(Word32) L_msu (Word32 L_var3, Word16 var1, Word16 var2); /* Msu, 1 */ LIBG7221_DECL(Word32) L_macNs (Word32 L_var3, Word16 var1, Word16 var2); /* Mac without sat, 1 */ LIBG7221_DECL(Word32) L_msuNs (Word32 L_var3, Word16 var1, Word16 var2); /* Msu without sat, 1 */ //PJ_INLINE(Word32) L_add (Word32 L_var1, Word32 L_var2); /* Long add, 2 */ PJ_INLINE(Word32) L_sub (Word32 L_var1, Word32 L_var2); /* Long sub, 2 */ LIBG7221_DECL(Word32) L_add_c (Word32 L_var1, Word32 L_var2); /* Long add with c, 2 */ LIBG7221_DECL(Word32) L_sub_c (Word32 L_var1, Word32 L_var2); /* Long sub with c, 2 */ LIBG7221_DECL(Word32) L_negate (Word32 L_var1); /* Long negate, 2 */ LIBG7221_DECL(Word16) mult_r (Word16 var1, Word16 var2); /* Mult with round, 2 */ PJ_INLINE(Word32) L_shl (Word32 L_var1, Word16 var2); /* Long shift left, 2 */ PJ_INLINE(Word32) L_shr (Word32 L_var1, Word16 var2); /* Long shift right, 2*/ LIBG7221_DECL(Word16) shr_r (Word16 var1, Word16 var2); /* Shift right with round, 2 */ LIBG7221_DECL(Word16) mac_r (Word32 L_var3, Word16 var1, Word16 var2); /* Mac with rounding,2 */ LIBG7221_DECL(Word16) msu_r (Word32 L_var3, Word16 var1, Word16 var2); /* Msu with rounding,2 */ LIBG7221_DECL(Word32) L_deposit_h (Word16 var1); /* 16 bit var1 -> MSB, 2 */ LIBG7221_DECL(Word32) L_deposit_l (Word16 var1); /* 16 bit var1 -> LSB, 2 */ LIBG7221_DECL(Word32) L_shr_r (Word32 L_var1, Word16 var2); /* Long shift right with round, 3 */ LIBG7221_DECL(Word32) L_abs (Word32 L_var1); /* Long abs, 3 */ LIBG7221_DECL(Word32) L_sat (Word32 L_var1); /* Long saturation, 4 */ LIBG7221_DECL(Word16) norm_s (Word16 var1); /* Short norm, 15 */ LIBG7221_DECL(Word16) div_s (Word16 var1, Word16 var2); /* Short division, 18 */ LIBG7221_DECL(Word16) norm_l (Word32 L_var1); /* Long norm, 30 */ /* Additional G.723.1 operators */ LIBG7221_DECL(Word32) L_mls( Word32, Word16 ) ; /* Weight FFS; currently assigned 1 */ LIBG7221_DECL(Word16) div_l( Word32, Word16 ) ; /* Weight FFS; currently assigned 1 */ LIBG7221_DECL(Word16) i_mult(Word16 a, Word16 b); /* Weight FFS; currently assigned 1 */ /* New shiftless operators, not used in G.729/G.723.1 */ LIBG7221_DECL(Word32) L_mult0(Word16 v1, Word16 v2); /* 32-bit Multiply w/o shift 1 */ LIBG7221_DECL(Word32) L_mac0(Word32 L_v3, Word16 v1, Word16 v2); /* 32-bit Mac w/o shift 1 */ LIBG7221_DECL(Word32) L_msu0(Word32 L_v3, Word16 v1, Word16 v2); /* 32-bit Msu w/o shift 1 */ /* Additional G.722.1 operators */ LIBG7221_DECL(UWord32) LU_shl (UWord32 L_var1, Word16 var2); LIBG7221_DECL(UWord32) LU_shr (UWord32 L_var1, Word16 var2); #define INCLUDE_UNSAFE 0 /* Local */ PJ_INLINE(Word16) saturate (Word32 L_var1); #if INCLUDE_UNSAFE extern Flag g7221_Overflow; extern Flag g7221_Carry; # define SET_OVERFLOW(n) g7221_Overflow = n # define SET_CARRY(n) g7221_Carry = n #else # define SET_OVERFLOW(n) # define SET_CARRY(n) # define GET_OVERFLOW() 0 # define GET_CARRY() 0 #endif #include "basic_op_i.h" #if PJMEDIA_LIBG7221_FUNCS_INLINED # include "basic_op.c" #endif #endif /* __BASIC_OP_H__ */ ================================================ FILE: deps/pjsip/third_party/g7221/common/basic_op_i.h ================================================ /*___________________________________________________________________________ | | | Function Name : extract_h | | | | Purpose : | | | | Return the 16 MSB of L_var1. | | | | Complexity weight : 1 | | | | Inputs : | | | | L_var1 | | 32 bit long signed integer (Word32 ) whose value falls in the | | range : 0x8000 0000 <= L_var1 <= 0x7fff ffff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ PJ_INLINE(Word16) extract_h (Word32 L_var1) { return (Word16) (L_var1 >> 16); } /* ------------------------- End of extract_h() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : extract_l | | | | Purpose : | | | | Return the 16 LSB of L_var1. | | | | Complexity weight : 1 | | | | Inputs : | | | | L_var1 | | 32 bit long signed integer (Word32 ) whose value falls in the | | range : 0x8000 0000 <= L_var1 <= 0x7fff ffff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ PJ_INLINE(Word16) extract_l (Word32 L_var1) { return (Word16) L_var1; } /* ------------------------- End of extract_l() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : saturate | | | | Purpose : | | | | Limit the 32 bit input to the range of a 16 bit word. | | | | Inputs : | | | | L_var1 | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var1 <= 0x7fff ffff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ PJ_INLINE(Word16) saturate (Word32 L_var1) { Word16 val16 = (Word16) L_var1; if (val16 == L_var1) return val16; if (L_var1 > MAX_16) return MAX_16; return MIN_16; } /* ------------------------- End of saturate() ------------------------- */ #if PJ_HAS_INT64 PJ_INLINE(Word32) L_saturate (pj_int64_t LL_var1) { pj_int32_t L_var1 = (pj_int32_t)LL_var1; if (LL_var1 == L_var1) return L_var1; else if (LL_var1 > MAX_32) return MAX_32; else return MIN_32; } #endif /*___________________________________________________________________________ | | | Function Name : add | | | | Purpose : | | | | Performs the addition (var1+var2) with overflow control and saturation;| | the 16 bit result is set at +32767 when overflow occurs or at -32768 | | when underflow occurs. | | | | Complexity weight : 1 | | | | Inputs : | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ PJ_INLINE(Word16) add (Word16 var1, Word16 var2) { return saturate (var1 + var2); } /* ------------------------- End of add() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : sub | | | | Purpose : | | | | Performs the subtraction (var1+var2) with overflow control and satu- | | ration; the 16 bit result is set at +32767 when overflow occurs or at | | -32768 when underflow occurs. | | | | Complexity weight : 1 | | | | Inputs : | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ PJ_INLINE(Word16) sub (Word16 var1, Word16 var2) { return saturate ((Word32) var1 - var2); } /* ------------------------- End of sub() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : negate | | | | Purpose : | | | | Negate var1 with saturation, saturate in the case where input is -32768:| | negate(var1) = sub(0,var1). | | | | Complexity weight : 1 | | | | Inputs : | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ PJ_INLINE(Word16) negate (Word16 var1) { return (Word16)((var1 == MIN_16) ? MAX_16 : -var1); } /* ------------------------- End of negate() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : L_add | | | | Purpose : | | | | 32 bits addition of the two 32 bits variables (L_var1+L_var2) with | | overflow control and saturation; the result is set at +2147483647 when | | overflow occurs or at -2147483648 when underflow occurs. | | | | Complexity weight : 2 | | | | Inputs : | | | | L_var1 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | L_var2 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. | |___________________________________________________________________________| */ PJ_INLINE(Word32) L_add (Word32 L_var1, Word32 L_var2) { #if PJ_HAS_INT64 return L_saturate(((pj_int64_t)L_var1) + L_var2); #else Word32 L_var_out; L_var_out = L_var1 + L_var2; if (((L_var1 ^ L_var2) & MIN_32) == 0) { if ((L_var_out ^ L_var1) & MIN_32) { SET_OVERFLOW(1); L_var_out = (L_var1 < 0) ? MIN_32 : MAX_32; } } return (L_var_out); #endif } /* ------------------------- End of L_add() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : L_sub | | | | Purpose : | | | | 32 bits subtraction of the two 32 bits variables (L_var1-L_var2) with | | overflow control and saturation; the result is set at +2147483647 when | | overflow occurs or at -2147483648 when underflow occurs. | | | | Complexity weight : 2 | | | | Inputs : | | | | L_var1 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | L_var2 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. | |___________________________________________________________________________| */ PJ_INLINE(Word32) L_sub (Word32 L_var1, Word32 L_var2) { #if PJ_HAS_INT64 return L_saturate((pj_int64_t)L_var1 - L_var2); #else Word32 L_var_out; L_var_out = L_var1 - L_var2; if (((L_var1 ^ L_var2) & MIN_32) != 0) { if ((L_var_out ^ L_var1) & MIN_32) { L_var_out = (L_var1 < 0L) ? MIN_32 : MAX_32; SET_OVERFLOW(1); } } return (L_var_out); #endif } /* ------------------------- End of L_sub() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : L_mult | | | | Purpose : | | | | L_mult is the 32 bit result of the multiplication of var1 times var2 | | with one shift left i.e.: | | L_mult(var1,var2) = L_shl((var1 times var2),1) and | | L_mult(-32768,-32768) = 2147483647. | | | | Complexity weight : 1 | | | | Inputs : | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. | |___________________________________________________________________________| */ PJ_INLINE(Word32) L_mult (Word16 var1, Word16 var2) { Word32 L_var_out; L_var_out = (Word32) var1 *(Word32) var2; if (L_var_out != (Word32) 0x40000000L) { return L_var_out << 1; } else { SET_OVERFLOW(1); return MAX_32; } } /* ------------------------- End of L_mult() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : L_mac | | | | Purpose : | | | | Multiply var1 by var2 and shift the result left by 1. Add the 32 bit | | result to L_var3 with saturation, return a 32 bit result: | | L_mac(L_var3,var1,var2) = L_add(L_var3,L_mult(var1,var2)). | | | | Complexity weight : 1 | | | | Inputs : | | | | L_var3 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. | |___________________________________________________________________________| */ PJ_INLINE(Word32) L_mac (Word32 L_var3, Word16 var1, Word16 var2) { return L_add (L_var3, L_mult (var1, var2)); } /* ------------------------- End of L_mac() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : round | | | | Purpose : | | | | Round the lower 16 bits of the 32 bit input number into the MS 16 bits | | with saturation. Shift the resulting bits right by 16 and return the 16 | | bit number: | | round(L_var1) = extract_h(L_add(L_var1,32768)) | | | | Complexity weight : 1 | | | | Inputs : | | | | L_var1 | | 32 bit long signed integer (Word32 ) whose value falls in the | | range : 0x8000 0000 <= L_var1 <= 0x7fff ffff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ PJ_INLINE(Word16) itu_round (Word32 L_var1) { return extract_h (L_add (L_var1, (Word32) 0x00008000L)); } /* ------------------------- End of round() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : L_shr | | | | Purpose : | | | | Arithmetically shift the 32 bit input L_var1 right var2 positions with | | sign extension. If var2 is negative, arithmetically shift L_var1 left | | by -var2 and zero fill the -var2 LSB of the result. Saturate the result | | in case of underflows or overflows. | | | | Complexity weight : 2 | | | | Inputs : | | | | L_var1 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. | |___________________________________________________________________________| */ PJ_INLINE(Word32) L_shr_nocheck(Word32 L_var1, Word16 var2) { #if 1 return L_var1 >> var2; #else if (var2 >= 31) { return (L_var1 < 0L) ? -1 : 0; } else { if (L_var1 < 0) { return ~((~L_var1) >> var2); } else { return L_var1 >> var2; } } #endif } PJ_INLINE(Word32) L_shl_nocheck (Word32 L_var1, Word16 var2) { #if PJ_HAS_INT64 return L_saturate( ((pj_int64_t)L_var1) << var2 ); #else for (; var2 > 0; var2--) { if (L_var1 > (Word32) 0X3fffffffL) { SET_OVERFLOW(1); return MAX_32; } else { if (L_var1 < (Word32) 0xc0000000L) { SET_OVERFLOW(1); return MIN_32; } } L_var1 <<= 1; } return (L_var1); #endif } PJ_INLINE(Word32) L_shr (Word32 L_var1, Word16 var2) { if (var2 < 0) { if (var2 < -32) var2 = -32; return L_shl_nocheck (L_var1, (Word16) -var2); } else { return L_shr_nocheck(L_var1, var2); } } /* ------------------------- End of L_shr() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : L_shl | | | | Purpose : | | | | Arithmetically shift the 32 bit input L_var1 left var2 positions. Zero | | fill the var2 LSB of the result. If var2 is negative, arithmetically | | shift L_var1 right by -var2 with sign extension. Saturate the result in | | case of underflows or overflows. | | | | Complexity weight : 2 | | | | Inputs : | | | | L_var1 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var3 <= 0x7fff ffff. | | | | var2 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | L_var_out | | 32 bit long signed integer (Word32) whose value falls in the | | range : 0x8000 0000 <= L_var_out <= 0x7fff ffff. | |___________________________________________________________________________| */ PJ_INLINE(Word32) L_shl (Word32 L_var1, Word16 var2) { if (var2 <= 0) { if (var2 < -32) var2 = -32; return L_shr_nocheck(L_var1, (Word16) -var2); } else { return L_shl_nocheck(L_var1, var2); } } /* ------------------------- End of L_shl() ------------------------- */ /*___________________________________________________________________________ | | | Function Name : abs_s | | | | Purpose : | | | | Absolute value of var1; abs_s(-32768) = 32767. | | | | Complexity weight : 1 | | | | Inputs : | | | | var1 | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0xffff 8000 <= var1 <= 0x0000 7fff. | | | | Outputs : | | | | none | | | | Return Value : | | | | var_out | | 16 bit short signed integer (Word16) whose value falls in the | | range : 0x0000 0000 <= var_out <= 0x0000 7fff. | |___________________________________________________________________________| */ PJ_INLINE(Word16) abs_s (Word16 var1) { #if 1 if (var1 >= 0) return var1; else if (var1 == MIN_16) return MAX_16; else return (Word16)-var1; #else if (var1 == MIN_16) { return MAX_16; } else { if (var1 < 0) { return (Word16)-var1; } else { return var1; } } #endif } /* ------------------------- End of abs_s() ------------------------- */ PJ_INLINE(Word16) shl_nocheck(Word16 var1, Word16 var2) { #if 1 /* blp: this should be more optimized */ return saturate (((Word32)var1) << var2); #else /* Original algorithm */ Word32 result = (Word32) var1 *((Word32) 1 << var2); if ((var2 > 15 && var1 != 0) || (result != (Word32) ((Word16) result))) { SET_OVERFLOW(1); return (Word16) ((var1 > 0) ? MAX_16 : MIN_16); } else { return extract_l (result); } #endif } PJ_INLINE(Word16) shr_nocheck(Word16 var1, Word16 var2) { #if 1 /* blp: this should yield the same value */ return (Word16) (var1 >> var2); #else /* Original algorithm */ if (var2 >= 15) { return (Word16)((var1 < 0) ? -1 : 0); } else { if (var1 < 0) { return (Word16) (~((~var1) >> var2)); } else { return (Word16)(var1 >> var2); } } #endif } ================================================ FILE: deps/pjsip/third_party/g7221/common/common.c ================================================ /**************************************************************************************** ** ** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C ** > Software Release 2.1 (2008-06) ** (Simple repackaging; no change from 2005-05 Release 2.0 code) ** ** 2004 Polycom, Inc. ** ** All rights reserved. ** ****************************************************************************************/ /**************************************************************************************** Filename: common.c Purpose: Contains the functions used for both G.722.1 Annex C encoder and decoder Design Notes: ****************************************************************************************/ /**************************************************************************************** Include files ****************************************************************************************/ #include "defs.h" #include "huff_def.h" #include "huff_tab.h" #include "tables.h" #include "count.h" /**************************************************************************************** Function: categorize Syntax: void categorize(Word16 number_of_available_bits, Word16 number_of_regions, Word16 num_categorization_control_possibilities, Word16 rms_index, Word16 power_categories, Word16 category_balances) inputs: number_of_regions num_categorization_control_possibilities number_of_available_bits rms_index[MAX_NUMBER_OF_REGIONS] outputs: power_categories[MAX_NUMBER_OF_REGIONS] category_balances[MAX_NUM_CATEGORIZATION_CONTROL_POSSIBILITIES-1] Description: Computes a series of categorizations WMOPS: 7kHz | 24kbit | 32kbit -------|--------------|---------------- AVG | 0.14 | 0.14 -------|--------------|---------------- MAX | 0.15 | 0.15 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 0.42 | 0.45 | 0.48 -------|--------------|----------------|---------------- MAX | 0.47 | 0.52 | 0.52 -------|--------------|----------------|---------------- ****************************************************************************************/ void categorize(Word16 number_of_available_bits, Word16 number_of_regions, Word16 num_categorization_control_possibilities, Word16 *rms_index, Word16 *power_categories, Word16 *category_balances) { Word16 offset; Word16 temp; Word16 frame_size; /* At higher bit rates, there is an increase for most categories in average bit consumption per region. We compensate for this by pretending we have fewer available bits. */ test(); if (number_of_regions == NUMBER_OF_REGIONS) { frame_size = DCT_LENGTH; } else { frame_size = MAX_DCT_LENGTH; } temp = sub(number_of_available_bits,frame_size); test(); if (temp > 0) { number_of_available_bits = sub(number_of_available_bits,frame_size); number_of_available_bits = extract_l(L_mult0(number_of_available_bits,5)); number_of_available_bits = shr_nocheck(number_of_available_bits,3); number_of_available_bits = add(number_of_available_bits,frame_size); } /* calculate the offset using the original category assignments */ offset = calc_offset(rms_index,number_of_regions,number_of_available_bits); /* compute the power categories based on the uniform offset */ compute_raw_pow_categories(power_categories,rms_index,number_of_regions,offset); /* adjust the category assignments */ /* compute the new power categories and category balances */ comp_powercat_and_catbalance(power_categories,category_balances,rms_index,number_of_available_bits,number_of_regions,num_categorization_control_possibilities,offset); } /*************************************************************************** Function: comp_powercat_and_catbalance Syntax: void comp_powercat_and_catbalance(Word16 *power_categories, Word16 *category_balances, Word16 *rms_index, Word16 number_of_available_bits, Word16 number_of_regions, Word16 num_categorization_control_possibilities, Word16 offset) inputs: *rms_index number_of_available_bits number_of_regions num_categorization_control_possibilities offset outputs: *power_categories *category_balances Description: Computes the power_categories and the category balances WMOPS: 7kHz | 24kbit | 32kbit -------|--------------|---------------- AVG | 0.10 | 0.10 -------|--------------|---------------- MAX | 0.11 | 0.11 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 0.32 | 0.35 | 0.38 -------|--------------|----------------|---------------- MAX | 0.38 | 0.42 | 0.43 -------|--------------|----------------|---------------- ***************************************************************************/ void comp_powercat_and_catbalance(Word16 *power_categories, Word16 *category_balances, Word16 *rms_index, Word16 number_of_available_bits, Word16 number_of_regions, Word16 num_categorization_control_possibilities, Word16 offset) { Word16 expected_number_of_code_bits; Word16 region; Word16 max_region; Word16 j; Word16 max_rate_categories[MAX_NUMBER_OF_REGIONS]; Word16 min_rate_categories[MAX_NUMBER_OF_REGIONS]; Word16 temp_category_balances[2*MAX_NUM_CATEGORIZATION_CONTROL_POSSIBILITIES]; Word16 raw_max, raw_min; Word16 raw_max_index=0, raw_min_index=0; Word16 max_rate_pointer, min_rate_pointer; Word16 max, min; Word16 itemp0; Word16 itemp1; Word16 min_plus_max; Word16 two_x_number_of_available_bits; Word16 temp; expected_number_of_code_bits = 0; move16(); for (region=0; region 0) { itemp0 = shl_nocheck(max_rate_categories[region],1); itemp1 = sub(offset,rms_index[region]); itemp0 = sub(itemp1,itemp0); temp = sub(itemp0,raw_min); test(); if (temp < 0) { raw_min = itemp0; raw_min_index = region; } } } max_rate_pointer = sub(max_rate_pointer,1); temp_category_balances[max_rate_pointer] = raw_min_index; move16(); max = sub(max,expected_bits_table[max_rate_categories[raw_min_index]]); max_rate_categories[raw_min_index] = sub(max_rate_categories[raw_min_index],1); move16(); max = add(max,expected_bits_table[max_rate_categories[raw_min_index]]); } else { raw_max = -99; move16(); /* Search from highest freq regions to lowest for best region to reassign to a lower bit rate category. */ max_region = sub(number_of_regions,1); for (region= max_region; region >= 0; region--) { temp = sub(min_rate_categories[region],(NUM_CATEGORIES-1)); test(); if (temp < 0) { itemp0 = shl_nocheck(min_rate_categories[region],1); itemp1 = sub(offset,rms_index[region]); itemp0 = sub(itemp1,itemp0); temp = sub(itemp0,raw_max); test(); if (temp > 0) { raw_max = itemp0; move16(); raw_max_index = region; move16(); } } } temp_category_balances[min_rate_pointer] = raw_max_index; move16(); min_rate_pointer = add(min_rate_pointer,1); min = sub(min,expected_bits_table[min_rate_categories[raw_max_index]]); min_rate_categories[raw_max_index] = add(min_rate_categories[raw_max_index],1); move16(); min = add(min,expected_bits_table[min_rate_categories[raw_max_index]]); } } for (region=0; region 0) { j = sub(NUM_CATEGORIES,1); move16(); } power_cats[region] = j; move16(); } bits = 0; move16(); /* compute the number of bits that will be used given the cat assignments */ for (region=0; region available_bits - 32) then divide the offset region for the bin search */ offset = sub(available_bits,32); temp = sub(bits,offset); test(); if (temp >= 0) { answer = test_offset; move16(); } delta = shr_nocheck(delta,1); test(); /* for the while loop */ } while (delta > 0); return(answer); } /*************************************************************************** Function: compute_raw_pow_categories Syntax: void compute_raw_pow_categories(Word16 *power_categories, Word16 *rms_index, Word16 number_of_regions, Word16 offset) inputs: *rms_index number_of_regions offset outputs: *power_categories Description: This function computes the power categories given the offset This is kind of redundant since they were already computed in calc_offset to determine the offset. WMOPS: | 24kbit | 32kbit -------|--------------|---------------- AVG | 0.01 | 0.01 -------|--------------|---------------- MAX | 0.01 | 0.01 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 0.01 | 0.01 | 0.01 -------|--------------|----------------|---------------- MAX | 0.01 | 0.01 | 0.01 -------|--------------|----------------|---------------- ***************************************************************************/ void compute_raw_pow_categories(Word16 *power_categories,Word16 *rms_index,Word16 number_of_regions,Word16 offset) { Word16 region; Word16 j; Word16 temp; for (region=0; region 0) j = sub(NUM_CATEGORIES,1); power_categories[region] = j; move16(); } } ================================================ FILE: deps/pjsip/third_party/g7221/common/config.h ================================================ #ifndef __LIBG7221_CONFIG_H__ #define __LIBG7221_CONFIG_H__ #include /** * Expand all basic operation functions as inline. Even if this is set to * zero, some critical functions would still be expanded as inline. Note * also that enabling this may generate some warning messages about functions * not being referenced (with gcc). * * Default: 0 (no) */ #ifndef PJMEDIA_LIBG7221_FUNCS_INLINED # define PJMEDIA_LIBG7221_FUNCS_INLINED 0 #endif /* Declare/define a function that may be expanded as inline. */ #if PJMEDIA_LIBG7221_FUNCS_INLINED # define LIBG7221_DECL(type) PJ_INLINE(type) # define LIBG7221_DEF(type) PJ_INLINE(type) #else # define LIBG7221_DECL(type) PJ_DECL(type) # define LIBG7221_DEF(type) PJ_DEF(type) #endif #endif /* __LIBG7221_CONFIG_H__ */ ================================================ FILE: deps/pjsip/third_party/g7221/common/count.h ================================================ /* $Id: count.h 2623 2009-04-20 18:38:15Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef COUNT_H #define COUNT_H #define move16() #define move32() #define logic16() #define logic32() #define test() #endif ================================================ FILE: deps/pjsip/third_party/g7221/common/defs.h ================================================ /*********************************************************************** ** ** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C ** > Software Release 2.1 (2008-06) ** (Simple repackaging; no change from 2005-05 Release 2.0 code) ** ** 2004 Polycom, Inc. ** ** All rights reserved. ** ***********************************************************************/ #include #include #include #include "g7221/common/typedef.h" #include "g7221/common/basic_op.h" #define PI 3.141592653589793238462 #define MAX_DCT_LENGTH 640 #define DCT_LENGTH 320 #define DCT_LENGTH_DIV_2 160 #define DCT_LENGTH_DIV_4 80 #define DCT_LENGTH_DIV_8 40 #define DCT_LENGTH_DIV_16 20 #define DCT_LENGTH_DIV_32 10 #define DCT_LENGTH_DIV_64 5 #define MAX(a,b) (a > b ? a : b) #define MIN(a,b) (a < b ? a : b) #define NUM_CATEGORIES 8 #define NUM_CATEGORIZATION_CONTROL_BITS 4 #define NUM_CATEGORIZATION_CONTROL_POSSIBILITIES 16 #define CORE_SIZE 10 #define DCT_LENGTH_LOG 6 #define MAX_DCT_LENGTH_LOG 7 /* region_size = (BLOCK_SIZE * 0.875)/NUM_REGIONS; */ #define NUMBER_OF_REGIONS 14 #define MAX_NUMBER_OF_REGIONS 28 #define REGION_SIZE 20 #define NUMBER_OF_VALID_COEFS (NUMBER_OF_REGIONS * REGION_SIZE) #define MAX_NUMBER_OF_VALID_COEFS (MAX_NUMBER_OF_REGIONS * REGION_SIZE) #define REGION_POWER_TABLE_SIZE 64 #define REGION_POWER_TABLE_NUM_NEGATIVES 24 #define MAX_NUM_CATEGORIZATION_CONTROL_BITS 5 #define MAX_NUM_CATEGORIZATION_CONTROL_POSSIBILITIES 32 #define ENCODER_SCALE_FACTOR 18318.0 /* The MLT output is incorrectly scaled by the factor product of ENCODER_SCALE_FACTOR and sqrt(160.) This is now (9/30/96) 1.0/2^(4.5) or 1/22.627. In the current implementation this must be an integer power of sqrt(2). The integer power is ESF_ADJUSTMENT_TO_RMS_INDEX. The -2 is to conform with the range defined in the spec. */ #define ESF_ADJUSTMENT_TO_RMS_INDEX (9-2) #define INTERMEDIATE_FILES_FLAG 0 /* Max bit rate is 48000 bits/sec. */ #define MAX_BITS_PER_FRAME 960 /***************************************************************************/ /* Type definitions */ /***************************************************************************/ typedef struct { Word16 code_bit_count; /* bit count of the current word */ Word16 current_word; /* current word in the bitstream being processed */ Word16 *code_word_ptr; /* pointer to the bitstream */ Word16 number_of_bits_left; /* number of bits left in the current word */ Word16 next_bit; /* next bit in the current word */ }Bit_Obj; typedef struct { Word16 seed0; Word16 seed1; Word16 seed2; Word16 seed3; }Rand_Obj; /***************************************************************************/ /* Function definitions */ /***************************************************************************/ extern Word16 compute_region_powers(Word16 *mlt_coefs, Word16 mag_shift, Word16 *drp_num_bits, UWord16 *drp_code_bits, Word16 *absolute_region_power_index, Word16 number_of_regions); void vector_quantize_mlts(Word16 number_of_available_bits, Word16 number_of_regions, Word16 num_categorization_control_possibilities, Word16 *mlt_coefs, Word16 *absolute_region_power_index, Word16 *power_categories, Word16 *category_balances, Word16 *p_categorization_control, Word16 *region_mlt_bit_counts, UWord32 *region_mlt_bits); Word16 vector_huffman(Word16 category, Word16 power_index, Word16 *raw_mlt_ptr, UWord32 *word_ptr); void adjust_abs_region_power_index(Word16 *absolute_region_power_index,Word16 *mlt_coefs,Word16 number_of_regions); void bits_to_words(UWord32 *region_mlt_bits,Word16 *region_mlt_bit_counts, Word16 *drp_num_bits,UWord16 *drp_code_bits,Word16 *out_words, Word16 categorization_control, Word16 number_of_regions, Word16 num_categorization_control_bits, Word16 number_of_bits_per_frame); void encoder(Word16 number_of_available_bits, Word16 number_of_regions, Word16 *mlt_coefs, Word16 mag_shift, Word16 *out_words); void decoder(Bit_Obj *bitobj, Rand_Obj *randobj, Word16 number_of_regions, Word16 *decoder_mlt_coefs, Word16 *p_mag_shift, Word16 *p_old_mag_shift, Word16 *old_decoder_mlt_coefs, Word16 frame_error_flag); Word16 samples_to_rmlt_coefs(const Word16 *new_samples,Word16 *history,Word16 *coefs,Word16 dct_length); void rmlt_coefs_to_samples(Word16 *coefs, Word16 *old_samples, Word16 *out_samples, Word16 dct_length, Word16 mag_shift); Word16 index_to_array(Word16 index,Word16 *array,Word16 category); void categorize(Word16 number_of_available_bits, Word16 number_of_regions, Word16 num_categorization_control_possibilities, Word16 *rms_index, Word16 *power_categories, Word16 *category_balances); Word16 calc_offset(Word16 *rms_index,Word16 number_of_regions,Word16 available_bits); void compute_raw_pow_categories(Word16 *power_categories,Word16 *rms_index,Word16 number_of_regions,Word16 offset); void comp_powercat_and_catbalance(Word16 *power_categories, Word16 *category_balances, Word16 *rms_index, Word16 number_of_available_bits, Word16 number_of_regions, Word16 num_categorization_control_possibilities, Word16 offset); void dct_type_iv_a (Word16 *input,Word16 *output,Word16 dct_length); void dct_type_iv_s(Word16 *input,Word16 *output,Word16 dct_length); void decode_envelope(Bit_Obj *bitobj, Word16 number_of_regions, Word16 *decoder_region_standard_deviation, Word16 *absolute_region_power_index, Word16 *p_mag_shift); void decode_vector_quantized_mlt_indices(Bit_Obj *bitobj, Rand_Obj *randobj, Word16 number_of_regions, Word16 *decoder_region_standard_deviation, Word16 *dedecoder_power_categories, Word16 *dedecoder_mlt_coefs); void rate_adjust_categories(Word16 categorization_control, Word16 *decoder_power_categories, Word16 *decoder_category_balances); void get_next_bit(Bit_Obj *bitobj); Word16 get_rand(Rand_Obj *randobj); void test_4_frame_errors(Bit_Obj *bitobj, Word16 number_of_regions, Word16 num_categorization_control_possibilities, Word16 *frame_error_flag, Word16 categorization_control, Word16 *absolute_region_power_index); void error_handling(Word16 number_of_coefs, Word16 number_of_valid_coefs, Word16 *frame_error_flag, Word16 *decoder_mlt_coefs, Word16 *old_decoder_mlt_coefs, Word16 *p_mag_shift, Word16 *p_old_mag_shift); ================================================ FILE: deps/pjsip/third_party/g7221/common/huff_def.h ================================================ /*********************************************************************** ** ** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C ** > Software Release 2.1 (2008-06) ** (Simple repackaging; no change from 2005-05 Release 2.0 code) ** ** 2004 Polycom, Inc. ** ** All rights reserved. ** ***********************************************************************/ #define REGION_POWER_STEPSIZE_DB 3.010299957 #define ABS_REGION_POWER_LEVELS 32 #define DIFF_REGION_POWER_LEVELS 24 #define DRP_DIFF_MIN -12 #define DRP_DIFF_MAX 11 #define MAX_NUM_BINS 16 #define MAX_VECTOR_INDICES 625 #define MAX_VECTOR_DIMENSION 5 extern Word16 differential_region_power_bits[MAX_NUMBER_OF_REGIONS][DIFF_REGION_POWER_LEVELS]; extern UWord16 differential_region_power_codes[MAX_NUMBER_OF_REGIONS][DIFF_REGION_POWER_LEVELS]; extern Word16 differential_region_power_decoder_tree[MAX_NUMBER_OF_REGIONS][DIFF_REGION_POWER_LEVELS-1][2]; extern Word16 mlt_quant_centroid[NUM_CATEGORIES][MAX_NUM_BINS]; extern Word16 expected_bits_table[NUM_CATEGORIES]; extern Word16 mlt_sqvh_bitcount_category_0[196]; extern UWord16 mlt_sqvh_code_category_0[196]; extern Word16 mlt_sqvh_bitcount_category_1[100]; extern UWord16 mlt_sqvh_code_category_1[100]; extern Word16 mlt_sqvh_bitcount_category_2[49]; extern UWord16 mlt_sqvh_code_category_2[49]; extern Word16 mlt_sqvh_bitcount_category_3[625]; extern UWord16 mlt_sqvh_code_category_3[625]; extern Word16 mlt_sqvh_bitcount_category_4[256]; extern UWord16 mlt_sqvh_code_category_4[256]; extern Word16 mlt_sqvh_bitcount_category_5[243]; extern UWord16 mlt_sqvh_code_category_5[243]; extern Word16 mlt_sqvh_bitcount_category_6[32]; extern UWord16 mlt_sqvh_code_category_6[32]; extern Word16 *table_of_bitcount_tables[NUM_CATEGORIES-1]; extern UWord16 *table_of_code_tables[NUM_CATEGORIES-1]; extern Word16 mlt_decoder_tree_category_0[180][2]; extern Word16 mlt_decoder_tree_category_1[93][2]; extern Word16 mlt_decoder_tree_category_2[47][2]; extern Word16 mlt_decoder_tree_category_3[519][2]; extern Word16 mlt_decoder_tree_category_4[208][2]; extern Word16 mlt_decoder_tree_category_5[191][2]; extern Word16 mlt_decoder_tree_category_6[31][2]; extern Word16 *table_of_decoder_tables[NUM_CATEGORIES-1]; ================================================ FILE: deps/pjsip/third_party/g7221/common/huff_tab.c ================================================ /*********************************************************************** ** ** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C ** > Software Release 2.1 (2008-06) ** (Simple repackaging; no change from 2005-05 Release 2.0 code) ** ** 2004 Polycom, Inc. ** ** All rights reserved. ** ***********************************************************************/ #include "defs.h" #include "huff_def.h" Word16 differential_region_power_bits[MAX_NUMBER_OF_REGIONS][DIFF_REGION_POWER_LEVELS] = { {99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}, { 4, 6, 5, 5, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 5, 7, 8, 9,11,11,12,12,12,12}, {10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 3, 3, 4, 5, 7, 9,11,12,13,15,15,15,16,16}, {12,10, 8, 6, 5, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 5, 5, 7, 9,11,13,14,14}, {13,10, 9, 9, 7, 7, 5, 5, 4, 3, 3, 3, 3, 3, 4, 4, 4, 5, 7, 9,11,13,13,13}, {12,13,10, 8, 6, 6, 5, 5, 4, 4, 3, 3, 3, 3, 3, 4, 5, 5, 6, 7, 9,11,14,14}, {12,11, 9, 8, 8, 7, 5, 4, 4, 3, 3, 3, 3, 3, 4, 4, 5, 5, 7, 8,10,13,14,14}, {15,16,15,12,10, 8, 6, 5, 4, 3, 3, 3, 2, 3, 4, 5, 5, 7, 9,11,13,16,16,16}, {14,14,11,10, 9, 7, 7, 5, 5, 4, 3, 3, 2, 3, 3, 4, 5, 7, 9, 9,12,14,15,15}, { 9, 9, 9, 8, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8,10,11,12,13,13}, {14,12,10, 8, 6, 6, 5, 4, 3, 3, 3, 3, 3, 3, 4, 5, 6, 8, 8, 9,11,14,14,14}, {13,10, 9, 8, 6, 6, 5, 4, 4, 4, 3, 3, 2, 3, 4, 5, 6, 8, 9, 9,11,12,14,14}, {16,13,12,11, 9, 6, 5, 5, 4, 4, 4, 3, 2, 3, 3, 4, 5, 7, 8,10,14,16,16,16}, {13,14,14,14,10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9,11,14,14,14}, {13,14,14,14,10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9,11,14,14,14}, {13,14,14,14,10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9,11,14,14,14}, {13,14,14,14,10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9,11,14,14,14}, {13,14,14,14,10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9,11,14,14,14}, {13,14,14,14,10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9,11,14,14,14}, {13,14,14,14,10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9,11,14,14,14}, {13,14,14,14,10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9,11,14,14,14}, {13,14,14,14,10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9,11,14,14,14}, {13,14,14,14,10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9,11,14,14,14}, {13,14,14,14,10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9,11,14,14,14}, {13,14,14,14,10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9,11,14,14,14}, {13,14,14,14,10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9,11,14,14,14}, {13,14,14,14,10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9,11,14,14,14}, {13,14,14,14,10, 8, 7, 7, 5, 4, 3, 3, 2, 3, 3, 4, 5, 5, 7, 9,11,14,14,14}}; UWord16 differential_region_power_codes[MAX_NUMBER_OF_REGIONS][DIFF_REGION_POWER_LEVELS] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 8,38,18,10, 7, 6, 3, 2, 0, 1, 7, 6, 5, 4,11,78,158,318,1278,1279,2552,2553,2554,2555}, {36, 8, 3, 5, 0, 1, 7, 6, 4, 3, 2, 5, 3, 4, 5,19,74,150,302,1213,1214,1215,2424,2425}, {2582,644,160,41, 5,11, 7, 5, 4, 1, 0, 6, 4, 7, 3, 6, 4,21,81,323,1290,5167,10332,10333}, {2940,366,181,180,47,46,27,10, 8, 5, 1, 0, 3, 7, 4, 9,12,26,44,182,734,2941,2942,2943}, {3982,7967,994,249,63,26,19,18,14, 8, 6, 1, 0, 2, 5, 7,12,30,27,125,496,1990,15932,15933}, {3254,1626,407,206,202,100,30,14, 3, 5, 3, 0, 2, 4, 2,13,24,31,102,207,812,6511,13020,13021}, {1110,2216,1111,139,35, 9, 3,20,11, 4, 2, 1, 3, 3, 1, 0,21, 5,16,68,276,2217,2218,2219}, {1013,1014,127,62,29, 6, 4,16, 0, 1, 3, 2, 3, 1, 5, 9,17, 5,28,30,252,1015,2024,2025}, {381,380,372,191,94,44,16,10, 7, 3, 1, 0, 2, 6, 9,17,45,92,187,746,1494,2991,5980,5981}, {3036,758,188,45,43,10, 4, 3, 6, 4, 2, 0, 3, 7,11,20,42,44,46,95,378,3037,3038,3039}, {751,92,45,20,26, 4,12, 7, 4, 0, 4, 1, 3, 5, 5, 3,27,21,44,47,186,374,1500,1501}, {45572U,5697,2849,1425,357,45,23, 6,10, 7, 2, 2, 3, 0, 4, 6, 7,88,179,713,11392,45573U,45574U,45575U}, {2511,5016,5018,5017,312,79,38,36,30,14, 6, 0, 2, 1, 3, 5, 8,31,37,157,626,5019,5020,5021}, {2511,5016,5018,5017,312,79,38,36,30,14, 6, 0, 2, 1, 3, 5, 8,31,37,157,626,5019,5020,5021}, {2511,5016,5018,5017,312,79,38,36,30,14, 6, 0, 2, 1, 3, 5, 8,31,37,157,626,5019,5020,5021}, {2511,5016,5018,5017,312,79,38,36,30,14, 6, 0, 2, 1, 3, 5, 8,31,37,157,626,5019,5020,5021}, {2511,5016,5018,5017,312,79,38,36,30,14, 6, 0, 2, 1, 3, 5, 8,31,37,157,626,5019,5020,5021}, {2511,5016,5018,5017,312,79,38,36,30,14, 6, 0, 2, 1, 3, 5, 8,31,37,157,626,5019,5020,5021}, {2511,5016,5018,5017,312,79,38,36,30,14, 6, 0, 2, 1, 3, 5, 8,31,37,157,626,5019,5020,5021}, {2511,5016,5018,5017,312,79,38,36,30,14, 6, 0, 2, 1, 3, 5, 8,31,37,157,626,5019,5020,5021}, {2511,5016,5018,5017,312,79,38,36,30,14, 6, 0, 2, 1, 3, 5, 8,31,37,157,626,5019,5020,5021}, {2511,5016,5018,5017,312,79,38,36,30,14, 6, 0, 2, 1, 3, 5, 8,31,37,157,626,5019,5020,5021}, {2511,5016,5018,5017,312,79,38,36,30,14, 6, 0, 2, 1, 3, 5, 8,31,37,157,626,5019,5020,5021}, {2511,5016,5018,5017,312,79,38,36,30,14, 6, 0, 2, 1, 3, 5, 8,31,37,157,626,5019,5020,5021}, {2511,5016,5018,5017,312,79,38,36,30,14, 6, 0, 2, 1, 3, 5, 8,31,37,157,626,5019,5020,5021}, {2511,5016,5018,5017,312,79,38,36,30,14, 6, 0, 2, 1, 3, 5, 8,31,37,157,626,5019,5020,5021}, {2511,5016,5018,5017,312,79,38,36,30,14, 6, 0, 2, 1, 3, 5, 8,31,37,157,626,5019,5020,5021}}; Word16 differential_region_power_decoder_tree[MAX_NUMBER_OF_REGIONS][DIFF_REGION_POWER_LEVELS-1][2] = { {{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0},{ 0, 0}}, {{ 1, 2},{ 3, 4},{ 5, 6},{ 7, 8},{ 9, 10},{ 11,-12},{-11,-10},{ -8, -9},{ -7, -6},{-13, 12},{ -5, -4},{ 0, 13},{ -3,-14},{ -2, 14},{ -1, 15},{-15, 16},{-16, 17},{-17, 18},{ 19, 20},{ 21, 22},{-18,-19},{-20,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{ 5, 6},{ 7, 8},{-10, -9},{ -8,-11},{ -7, -6},{ 9, -5},{ 10,-12},{ -4, 11},{-13, -3},{ 12, -2},{ 13,-14},{ -1, 14},{ 15,-15},{ 0, 16},{-16, 17},{-17, 18},{-18, 19},{ 20, 21},{ 22,-19},{-20,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{ 5, 6},{ 7, 8},{ 9, 10},{-12, 11},{-11,-13},{-10, -9},{ 12,-14},{ -8, -7},{-15, -6},{ 13, -5},{-16, -4},{ 14,-17},{ 15, -3},{ 16,-18},{ -2, 17},{ 18,-19},{ -1, 19},{-20, 20},{ 0, 21},{ 22,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{ 5, 6},{-11,-10},{ 7,-12},{ 8, -9},{ 9,-13},{-14, 10},{ -8,-15},{-16, 11},{ -7, 12},{-17, -6},{ 13, 14},{-18, 15},{ -5, -4},{ 16, 17},{ -3, -2},{-19, 18},{ -1, 19},{-20, 20},{ 21, 22},{ 0,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{ 5, 6},{-12,-11},{-13, 7},{ 8,-14},{-10, 9},{ 10,-15},{ -9, 11},{ -8, 12},{-16, 13},{ -7, -6},{-17, 14},{ -5,-18},{ 15, -4},{ 16,-19},{ 17, -3},{-20, 18},{ -2, 19},{-21, 20},{ 0, 21},{ 22, -1},{-22,-23}}, {{ 1, 2},{ 3, 4},{ 5, 6},{-11, 7},{-12,-10},{-13, -9},{ 8, 9},{-14, -8},{ 10,-15},{ -7, 11},{-16, 12},{ -6,-17},{ 13, 14},{ -5, 15},{-18, 16},{ -4, 17},{ -3,-19},{ 18, -2},{-20, 19},{ -1, 20},{ 0, 21},{ 22,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{ 5,-12},{ 6,-11},{-10,-13},{ -9, 7},{ 8,-14},{ 9, -8},{-15, 10},{ -7,-16},{ 11, -6},{ 12,-17},{ 13, -5},{-18, 14},{ 15, -4},{-19, 16},{ 17, -3},{-20, 18},{ 19, 20},{ 21, 22},{ 0, -2},{ -1,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{ 5,-12},{ 6,-13},{-11,-10},{ 7,-14},{ 8, -9},{ 9,-15},{ -8, 10},{ -7,-16},{ 11, 12},{ -6,-17},{ -5, 13},{ 14, 15},{-18, -4},{-19, 16},{ -3, 17},{ 18, -2},{-20, 19},{ 20, 21},{ 22, 0},{ -1,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{ 5, 6},{-11,-10},{-12, -9},{ 7, 8},{-13, -8},{ 9,-14},{ -7, 10},{ -6,-15},{ 11, 12},{ -5,-16},{ 13, 14},{-17, 15},{ -4, 16},{ 17,-18},{ 18, -3},{ -2, 19},{ -1, 0},{-19, 20},{-20, 21},{ 22,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{ 5, 6},{-11, 7},{-10,-12},{ -9, 8},{ -8,-13},{ 9, -7},{ 10,-14},{ -6, 11},{-15, 12},{ -5, 13},{-16, -4},{ 14, 15},{-17, -3},{-18, 16},{ 17,-19},{ -2, 18},{-20, 19},{ -1, 20},{ 21, 22},{ 0,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{ 5,-12},{ 6,-11},{ 7, 8},{-10,-13},{ -9, 9},{ -8,-14},{ 10, -7},{ 11,-15},{ -6, 12},{ -5, 13},{ -4,-16},{ 14, 15},{ -3,-17},{ 16, 17},{-18, -2},{ 18,-19},{ -1, 19},{-20, 20},{-21, 21},{ 22, 0},{-22,-23}}, {{ 1, 2},{ 3, 4},{ 5,-12},{-13, 6},{-11, 7},{-14, 8},{-10, 9},{-15, -9},{ -8, 10},{ -7,-16},{ 11, -6},{ 12, -5},{-17, 13},{ 14,-18},{ 15, -4},{ 16,-19},{ 17, -3},{ 18, -2},{ 19, -1},{-20, 20},{ 21, 22},{ 0,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{-12, 5},{-11,-13},{ 6,-14},{-10, 7},{ 8,-15},{ -9, 9},{-16, 10},{ -8,-17},{ 11, 12},{ -7,-18},{ -6, 13},{ 14, -5},{ 15,-19},{ -4, 16},{-20, 17},{ 18, 19},{ 20, 21},{ 22, 0},{ -1, -3},{ -2,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{-12, 5},{-11,-13},{ 6,-14},{-10, 7},{ 8,-15},{ -9, 9},{-16, 10},{ -8,-17},{ 11, 12},{ -7,-18},{ -6, 13},{ 14, -5},{ 15,-19},{ -4, 16},{-20, 17},{ 18, 19},{ 20, 21},{ 22, 0},{ -1, -3},{ -2,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{-12, 5},{-11,-13},{ 6,-14},{-10, 7},{ 8,-15},{ -9, 9},{-16, 10},{ -8,-17},{ 11, 12},{ -7,-18},{ -6, 13},{ 14, -5},{ 15,-19},{ -4, 16},{-20, 17},{ 18, 19},{ 20, 21},{ 22, 0},{ -1, -3},{ -2,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{-12, 5},{-11,-13},{ 6,-14},{-10, 7},{ 8,-15},{ -9, 9},{-16, 10},{ -8,-17},{ 11, 12},{ -7,-18},{ -6, 13},{ 14, -5},{ 15,-19},{ -4, 16},{-20, 17},{ 18, 19},{ 20, 21},{ 22, 0},{ -1, -3},{ -2,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{-12, 5},{-11,-13},{ 6,-14},{-10, 7},{ 8,-15},{ -9, 9},{-16, 10},{ -8,-17},{ 11, 12},{ -7,-18},{ -6, 13},{ 14, -5},{ 15,-19},{ -4, 16},{-20, 17},{ 18, 19},{ 20, 21},{ 22, 0},{ -1, -3},{ -2,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{-12, 5},{-11,-13},{ 6,-14},{-10, 7},{ 8,-15},{ -9, 9},{-16, 10},{ -8,-17},{ 11, 12},{ -7,-18},{ -6, 13},{ 14, -5},{ 15,-19},{ -4, 16},{-20, 17},{ 18, 19},{ 20, 21},{ 22, 0},{ -1, -3},{ -2,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{-12, 5},{-11,-13},{ 6,-14},{-10, 7},{ 8,-15},{ -9, 9},{-16, 10},{ -8,-17},{ 11, 12},{ -7,-18},{ -6, 13},{ 14, -5},{ 15,-19},{ -4, 16},{-20, 17},{ 18, 19},{ 20, 21},{ 22, 0},{ -1, -3},{ -2,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{-12, 5},{-11,-13},{ 6,-14},{-10, 7},{ 8,-15},{ -9, 9},{-16, 10},{ -8,-17},{ 11, 12},{ -7,-18},{ -6, 13},{ 14, -5},{ 15,-19},{ -4, 16},{-20, 17},{ 18, 19},{ 20, 21},{ 22, 0},{ -1, -3},{ -2,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{-12, 5},{-11,-13},{ 6,-14},{-10, 7},{ 8,-15},{ -9, 9},{-16, 10},{ -8,-17},{ 11, 12},{ -7,-18},{ -6, 13},{ 14, -5},{ 15,-19},{ -4, 16},{-20, 17},{ 18, 19},{ 20, 21},{ 22, 0},{ -1, -3},{ -2,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{-12, 5},{-11,-13},{ 6,-14},{-10, 7},{ 8,-15},{ -9, 9},{-16, 10},{ -8,-17},{ 11, 12},{ -7,-18},{ -6, 13},{ 14, -5},{ 15,-19},{ -4, 16},{-20, 17},{ 18, 19},{ 20, 21},{ 22, 0},{ -1, -3},{ -2,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{-12, 5},{-11,-13},{ 6,-14},{-10, 7},{ 8,-15},{ -9, 9},{-16, 10},{ -8,-17},{ 11, 12},{ -7,-18},{ -6, 13},{ 14, -5},{ 15,-19},{ -4, 16},{-20, 17},{ 18, 19},{ 20, 21},{ 22, 0},{ -1, -3},{ -2,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{-12, 5},{-11,-13},{ 6,-14},{-10, 7},{ 8,-15},{ -9, 9},{-16, 10},{ -8,-17},{ 11, 12},{ -7,-18},{ -6, 13},{ 14, -5},{ 15,-19},{ -4, 16},{-20, 17},{ 18, 19},{ 20, 21},{ 22, 0},{ -1, -3},{ -2,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{-12, 5},{-11,-13},{ 6,-14},{-10, 7},{ 8,-15},{ -9, 9},{-16, 10},{ -8,-17},{ 11, 12},{ -7,-18},{ -6, 13},{ 14, -5},{ 15,-19},{ -4, 16},{-20, 17},{ 18, 19},{ 20, 21},{ 22, 0},{ -1, -3},{ -2,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{-12, 5},{-11,-13},{ 6,-14},{-10, 7},{ 8,-15},{ -9, 9},{-16, 10},{ -8,-17},{ 11, 12},{ -7,-18},{ -6, 13},{ 14, -5},{ 15,-19},{ -4, 16},{-20, 17},{ 18, 19},{ 20, 21},{ 22, 0},{ -1, -3},{ -2,-21},{-22,-23}}, {{ 1, 2},{ 3, 4},{-12, 5},{-11,-13},{ 6,-14},{-10, 7},{ 8,-15},{ -9, 9},{-16, 10},{ -8,-17},{ 11, 12},{ -7,-18},{ -6, 13},{ 14, -5},{ 15,-19},{ -4, 16},{-20, 17},{ 18, 19},{ 20, 21},{ 22, 0},{ -1, -3},{ -2,-21},{-22,-23}}}; Word16 mlt_quant_centroid[NUM_CATEGORIES][MAX_NUM_BINS] = { { 0, 1606, 3119, 4586, 6049, 7502, 8941,10406,11851,13292,14736,16146,17566,19351, 0, 0}, { 0, 2229, 4341, 6401, 8471,10531,12583,14588,16673,18924, 0, 0, 0, 0, 0, 0}, { 0, 3055, 5998, 8929,11806,14680,17680, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 4121, 8192,12259,16322, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 5413,11071,16315, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 6785,14300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 8044, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 8019, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; Word16 expected_bits_table[NUM_CATEGORIES] = {52, 47, 43, 37, 29, 22, 16, 0}; Word16 mlt_sqvh_bitcount_category_0[196] = { 1, 4, 6, 6, 7, 7, 8, 8, 8, 9, 9,10,11,11, 4, 5, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,11,11, 5, 6, 7, 8, 8, 9, 9, 9, 9,10,10,10,11,12, 6, 7, 8, 9, 9, 9, 9,10,10,10,10,11,12,13, 7, 7, 8, 9, 9, 9,10,10, 10,10,11,11,12,13, 8, 8, 9, 9, 9,10,10,10,10,11, 11,12,13,14, 8, 8, 9, 9,10,10,11,11,11,12,12,13, 13,15, 8, 8, 9, 9,10,10,11,11,11,12,12,13,14,15, 9, 9, 9,10,10,10,11,11,12,13,12,14,15,16, 9, 9, 10,10,10,10,11,12,12,14,14,16,16,16, 9, 9,10,10, 11,11,12,13,13,14,14,15,15,16,10,10,10,11,11,12, 12,13,15,15,16,14,15,15,11,11,11,12,13,13,13,15, 16,16,16,16,14,15,11,11,12,13,13,14,15,16,16,16, 16,16,16,14}; UWord16 mlt_sqvh_code_category_0[196] = { 1, 2, 1, 24, 14, 51, 9, 68, 110, 26, 218, 54, 154, 761, 3, 10, 22, 8, 58, 22, 71, 16, 30, 50, 213, 75, 94, 632, 15, 18, 52, 23, 107, 5, 54, 63, 239, 46, 276, 271, 851, 252, 28, 10, 12, 1, 22, 133, 191, 55, 105, 278, 317, 554, 310, 276, 32, 50, 94, 20, 187, 219, 13, 268, 473, 445, 145, 849, 1277, 623, 1, 14, 0, 55, 238, 121, 120, 269, 318, 530, 639, 1117, 509, 556, 24, 78, 51, 153, 62, 308, 16, 25, 68, 1058, 428, 277, 2233, 1114, 92, 108, 141, 223, 270, 381, 24, 212, 760, 35, 1063, 279, 1717, 3439, 7, 21, 152, 73, 309, 310, 95, 944, 1890, 2232, 1891, 5107,10213, 4981, 61, 62, 9, 79, 474, 475, 848, 1059, 1056, 1716, 139, 4978, 4983, 4983, 140, 186, 76, 444, 144, 633, 1057, 838, 2237, 4472, 4473,10212,10212, 4983, 74, 78, 311, 213, 850, 1062, 1119, 508, 276, 277, 4982, 4473,10212,10212, 208, 70, 555, 418, 68, 510, 2552, 1115, 4980, 4979, 4982, 4982, 4473,10212, 215, 71, 253, 511, 839, 1718, 2488, 6876, 6877, 4979, 4979, 4982, 4982, 4473}; Word16 mlt_sqvh_bitcount_category_1[100] = { 1, 4, 5, 6, 7, 8, 8, 9,10,10, 4, 5, 6, 7, 7, 8, 8, 9, 9,11, 5, 5, 6, 7, 8, 8, 9, 9,10,11, 6, 6, 7, 8, 8, 9, 9,10,11,12, 7, 7, 8, 8, 9, 9,10,11, 11,13, 8, 8, 8, 9, 9,10,10,11,12,14, 8, 8, 8, 9, 10,11,11,12,13,15, 9, 9, 9,10,11,12,12,14,14,14, 9, 9, 9,10,11,12,14,16,14,14,10,10,11,12,13,14, 16,16,16,14}; UWord16 mlt_sqvh_code_category_1[100] = { 1, 2, 11, 27, 31, 9, 120, 31, 275, 310, 1, 0, 12, 5, 33, 54, 102, 111, 246, 448, 10, 14, 31, 39, 59, 100, 114, 202, 485, 969, 24, 26, 36, 52, 103, 30, 120, 242, 69, 1244, 35, 32, 14, 61, 113, 117, 233, 486, 487, 2491, 13, 12, 69, 110, 149, 35, 495, 449, 1978, 7751, 76, 75, 122, 136, 213, 68, 623, 930, 3959, 9961, 115, 16, 107, 225, 424, 850, 1936, 7916, 4981, 4981, 148, 154, 243, 407, 988, 851, 7750,19920, 7916, 4981, 406, 274, 464, 931, 3874, 7917, 19921,19920,19920, 7916}; Word16 mlt_sqvh_bitcount_category_2[49] = { 1, 4, 5, 7, 8, 9,10, 3, 4, 5, 7, 8, 9,10, 5, 5, 6, 7, 8,10,10, 7, 6, 7, 8, 9,10,12, 8, 8, 8, 9, 10,12,14, 8, 9, 9,10,11,15,16, 9,10,11,12,13,16, 15}; UWord16 mlt_sqvh_code_category_2[49] = { 1, 0, 10, 11, 28, 62, 363, 3, 2, 9, 8, 24, 53, 352, 7, 8, 13, 25, 89, 74, 355, 10, 23, 24, 29, 55, 354, 1449, 25, 19, 30, 52, 108, 438, 5793, 91, 36, 63, 353, 725,11584,23170, 180, 75, 218, 439, 2897,23171, 11584}; Word16 mlt_sqvh_bitcount_category_3[625] = { 2, 4, 6, 8,10, 5, 5, 6, 8,10, 7, 8, 8,10,12, 9, 9,10,12,15,10,11,13,16,16, 5, 6, 8,10,11, 5, 6, 8,10,12, 7, 7, 8,10,13, 9, 9,10,12,15,12,11,13, 16,16, 7, 9,10,12,15, 7, 8,10,12,13, 9, 9,11,13, 16,11,11,12,14,16,12,12,14,16,14, 9,11,12,16,16, 9,10,13,15,16,10,11,12,16,16,13,13,16,16,16,16, 16,15,16,16,11,13,16,16,15,11,13,15,16,16,13,13, 16,16,16,14,16,16,16,16,16,16,16,16,16, 4, 6, 8, 10,13, 6, 6, 8,10,13, 9, 8,10,12,16,10,10,11,15, 16,13,12,14,16,16, 5, 6, 8,11,13, 6, 6, 8,10,13, 8, 8, 9,11,14,10,10,12,12,16,13,12,13,15,16, 7, 8, 9,12,16, 7, 8,10,12,14, 9, 9,10,13,16,11,10, 12,15,16,13,13,16,16,15, 9,11,13,16,16, 9,10,12, 15,16,10,11,13,16,16,13,12,16,16,16,16,16,16,16, 16,11,13,16,16,16,11,13,16,16,16,12,13,15,16,16, 16,16,16,16,16,16,16,16,16,16, 6, 8,11,13,16, 8, 8,10,12,16,11,10,11,13,16,12,13,13,15,16,16,16, 14,16,15, 6, 8,10,13,16, 8, 8,10,12,16,10,10,11, 13,16,13,12,13,16,16,14,14,14,16,16, 8, 9,11,13, 16, 8, 9,11,16,14,10,10,12,15,16,12,12,13,16,16, 15,16,16,16,16,10,12,15,16,16,10,12,12,14,16,12, 12,13,16,16,14,15,16,16,16,16,16,16,16,16,12,15, 15,16,16,13,13,16,16,14,14,16,16,16,16,16,16,16, 16,16,14,15,16,16,16, 8,10,13,15,16,10,11,13,16, 16,13,13,14,16,16,16,16,16,16,16,16,16,16,16,16, 8,10,11,15,16, 9,10,12,16,16,12,12,15,16,16,16, 14,16,16,16,16,16,16,16,16, 9,11,14,16,16,10,11, 13,16,16,14,13,14,16,16,16,15,15,16,16,16,16,16, 16,16,11,13,16,16,16,11,13,15,16,16,13,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,15,16,16,16,16, 14,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16, 9,13,16,16,16,11,13,16,16,16,14,15, 16,16,16,15,16,16,16,16,16,16,16,16,16, 9,13,15, 15,16,12,13,14,16,16,16,15,16,16,16,16,16,16,16, 16,16,16,16,16,16,11,13,15,16,16,12,14,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,15,15,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,15, 16,16,13,16,16,16,16,16,16,16,16,16,16,16,16,16, 16}; UWord16 mlt_sqvh_code_category_3[625] = { 3, 8, 46, 145, 228, 4, 8, 47, 28, 455, 89, 2, 180, 5, 1335, 250, 12, 644, 1311, 139, 729, 251, 870, 2172, 2211, 5, 23, 112, 334, 1469, 21, 3, 5, 111, 2014, 88, 79, 152, 124, 2685, 297, 48, 110, 1310, 149, 501, 1231, 153, 2267, 2569, 57, 13, 653, 2587, 143, 75, 124, 118, 2611, 5242, 61, 50, 253, 3633, 2216, 476, 39, 57, 1926, 2236, 2586, 1329, 1920, 2566, 1926, 296, 233, 2590, 2240, 2217, 253, 613, 867, 144, 318, 614, 252, 2589, 2242, 2218, 872, 866, 2187, 2296, 2155, 2568, 2227, 150, 2567, 2296, 199, 2686, 2160, 2290,19145, 232, 2680, 128, 2192, 2212, 2684, 793, 2281, 2223, 2242, 1934, 2165, 2146, 2291, 2296, 2222, 2189, 2187, 2296, 2296, 6, 4, 82, 725, 3632, 15, 21, 56, 599, 148, 3, 162, 42, 411, 2301, 735, 654, 930, 137, 2586, 869, 1334, 1931, 2300, 2213, 9, 22, 146, 1290, 5240, 5, 12, 53, 630, 875, 80, 9, 8, 86, 2002, 210, 117, 56, 2019, 2162, 146, 397, 868, 131, 2151, 77, 160, 365, 2610, 2252, 59, 54, 41, 2591, 1928, 226, 14, 121, 5792, 2295, 1197, 728, 408, 130, 2157, 3635, 155, 2573, 2587, 130, 314, 64, 144, 2173, 2176, 115, 30, 409, 153, 2590, 631, 26, 4787, 2221, 2174, 2683, 1863, 2572, 319, 2150, 2177, 2194, 2571, 2257, 319, 65, 145, 2251, 2156, 2161, 909, 864, 2193, 2197, 2246, 2588, 5797, 156, 2258, 2221, 2158, 2199, 2214, 2152, 319, 2188, 2264, 2572, 319, 319, 30, 117, 219, 865, 2263, 147, 127, 239, 410, 2247, 27, 324, 1468, 2681, 2180, 1328, 5241, 147, 142, 2237, 2241, 2245, 1921, 2262, 142, 41, 11, 505, 2682, 2591, 0, 26, 229, 2015, 2577, 464, 98, 87, 5243, 2166, 149, 2016, 5244, 2190, 2198, 9573,11598,11599, 2235, 2190, 144, 298, 1004, 5245, 2277, 156, 104, 254, 2560, 1922, 612, 325, 2017, 129, 2588, 2608, 1330, 871, 2144, 2145, 132, 2147, 2148, 2149, 2144, 119, 1331, 133, 2153, 2154, 211, 58, 2609, 1923, 2159, 510, 163, 5246, 2163, 2164, 1924, 134, 2167, 2168, 2168, 2169, 2170, 2171, 2168, 2168, 1332, 135, 136, 2175, 2153, 150, 873, 2178, 2179, 1923, 1925, 2181, 2182, 2183, 2163, 2184, 2185, 2186, 2168, 2168, 1924, 134, 2167, 2168, 2168, 58, 326, 2687, 138, 2191, 31, 66, 874, 2195, 2196, 151, 152, 1927, 2200, 2201, 2202, 2203, 2204, 2205, 2206, 2207, 2208, 2209, 2210, 2205, 55, 103, 1230, 140, 2215, 118, 15, 1333, 2219, 2220, 2018, 511, 141, 2224, 2225, 2226, 1929, 2228, 2229, 2230, 2231, 2232, 2233, 2234, 2229, 366, 1005, 1930, 2238, 2239, 12, 1006, 5247, 2243, 2244, 1932, 3634, 1933, 2248, 2249, 2250, 145, 146, 2253, 2253, 2254, 2255, 2256, 2253, 2253, 1291, 5793, 2259, 2260, 2261, 477, 5794, 147, 2265, 2266, 5795, 2268, 2269, 2270, 2270, 2271, 2272, 2273, 2274, 2274, 2275, 2276, 2273, 2274, 2274, 148, 2278, 2279, 2280, 2260, 1935, 2282, 2283, 2284, 2265, 2285, 2286, 2287, 2270, 2270, 2288, 2289, 2273, 2274, 2274, 2271, 2272, 2273, 2274, 2274, 233, 5796, 2292, 2293, 2294, 1292, 3724, 2297, 2298, 2299, 2000, 151, 2302, 2303, 2200, 152, 2561, 2562, 2563, 2205, 2564, 2565, 2204, 2205, 2205, 363, 154, 154, 155, 2570, 59, 3725, 2001, 2574, 2575, 2576, 157, 2578, 2579, 2224, 2580, 2581, 2582, 2583, 2229, 2584, 2585, 2228, 2229, 2229, 654, 5798, 158, 2589, 2238, 2392, 2003, 2592, 2593, 2243, 2594, 2595, 2596, 2597, 2248, 2598, 2599, 2600, 2253, 2253, 2250, 145, 146, 2253, 2253, 2601, 2602, 2603, 2604, 2260, 2605, 2606, 2607, 6336, 2265, 6337, 6338, 6339, 2270, 2270, 6340, 6341, 2273, 2274, 2274, 2271, 2272, 2273, 2274, 2274, 6342, 6343, 2259, 2260, 2260,38288U,38289U, 147, 2265, 2265, 5795, 2268, 2269, 2270, 2270, 2271, 2272, 2273, 2274, 2274, 2271, 2272, 2273, 2274, 2274}; Word16 mlt_sqvh_bitcount_category_4[256] = { 2, 4, 7,10, 4, 5, 7,10, 7, 8,10,14,11,11,15,15, 4, 5, 9,12, 5, 5, 8,12, 8, 7,10,15,11,11,15,15, 7, 9,12,15, 8, 8,12,15,10,10,13,15,14,14,15,13, 11,13,15,15,11,13,15,15,14,15,15,13,15,15,13,13, 4, 5, 9,13, 5, 6, 9,13, 9, 9,11,15,14,13,15,15, 4, 6, 9,12, 5, 6, 9,13, 9, 8,11,15,13,12,15,15, 7, 9,12,15, 7, 8,11,15,10,10,14,15,14,15,15,14, 10,12,15,15,11,13,15,15,15,15,15,14,15,15,14,14, 6, 9,13,14, 8, 9,12,15,12,12,15,15,15,15,15,15, 7, 9,13,15, 8, 9,12,15,11,12,15,15,15,15,15,15, 9,11,15,15, 9,11,15,15,14,14,15,15,15,15,15,15, 14,15,15,15,14,15,15,15,15,15,15,15,14,14,15,15, 9,12,15,15,12,13,15,15,15,15,15,15,15,15,15,15, 10,12,15,15,12,14,15,15,15,15,15,15,15,15,15,15, 14,15,15,15,15,15,15,15,15,15,15,15,14,14,15,15, 15,15,15,15,15,15,15,15,14,14,15,15,14,14,15,15}; UWord16 mlt_sqvh_code_category_4[256] = { 1, 2, 4, 572, 10, 0, 69, 712, 91, 10, 46, 9182, 1426, 1430,30172,30194, 9, 28, 22, 2258, 16, 25, 142, 2179, 15, 111, 719, 1521, 1131, 1437, 1520,30196, 88, 283, 3803,30193, 13, 236, 2856,30166, 545, 951, 5709, 1522, 3241, 9180,30179, 5709, 1088, 4356,30410,30175, 1146, 377,30162,30163, 8715,30176,30165, 5709,30197,30184, 5709, 5709, 1, 23, 28, 5710, 26, 14, 29, 7538, 102, 103, 1429, 1524, 3237, 7060,30401,30201, 15, 13, 470, 3768, 24, 15, 281, 5747, 24, 181, 1128,30206, 5711, 3531,30156,30158, 116, 100, 2260,30187, 119, 234, 1764,30171, 716, 883, 9183,30164, 3236, 1528,30180, 9183, 885, 2870, 1532,30160, 1431, 5708,30192,30205,30402,30168,30173, 9183,30157,30161, 9183, 9183, 54, 25, 1621,15211, 180, 287, 2261,30198, 808, 811,30411,30413,30414,22986,22987,30411, 24, 273, 376,30159, 137, 280, 2871, 1523, 1768, 2259, 1525,30167, 1526,30169,30170, 1525, 443, 1434, 1527,30174, 474, 1769,30177,30178, 3238, 3239,30181,30181,30182,30183,30181,30181, 3240,30185,30186, 1527, 9181,30188,30189,30177,30190,30191,30181,30181, 3238, 3239,30181,30181, 440, 2857, 1529,30195, 2294, 7061, 1530,30199,30200, 1531,30202,30411,30203,30204,30411,30411, 203, 2872,30207,30400, 189,11492,30403,30404,30405,30406,30407, 1525,30408,30409, 1525, 1525, 8714, 1533,30412, 1527, 1534, 1535,30415,30177,30416,30417,30181,30181, 3238, 3239,30181,30181, 30418,30419, 1527, 1527,30420,30421,30177,30177, 3238, 3239,30181,30181, 3238, 3239,30181,30181}; Word16 mlt_sqvh_bitcount_category_5[243] = { 2, 4, 8, 4, 5, 9, 9,10,14, 4, 6,11, 5, 6,12,10, 11,15, 9,11,15,10,13,15,14,15, 6, 4, 6,12, 6, 7, 12,12,12,15, 5, 7,13, 6, 7,13,12,13,15,10,12,15, 11,13,15,15,15, 7, 8,13,15,11,12,15,15,15, 7,10, 13,15,12,15,15,15,15, 7,15,15, 7,15,15, 7, 6, 7, 7, 4, 5,11, 5, 7,12,11,12,15, 6, 7,13, 7, 8,14, 12,14,15,11,13,15,12,13,15,15,15, 8, 5, 6,13, 7, 8,15,12,14,15, 6, 8,14, 7, 8,15,14,15,15,12,12, 15,12,13,15,15,15, 8, 9,13,15,12,13,15,15,15, 8, 11,13,15,13,13,15,15,15, 8,14,15, 8,15,15, 8, 7, 8, 8, 8,10,15,11,12,15,15,15, 7,10,12,15,12,13, 15,15,15, 8,14,15, 7,15,15, 8, 7, 8, 8, 8,12,15, 12,13,15,15,15, 8,11,13,15,13,15,15,15,15, 8,15, 15, 8,15,15, 8, 7, 8, 8,14,15, 6,15,15, 8, 7, 8, 8,15,15, 8,15,15, 8, 7, 8, 8, 6, 8, 8, 7, 8, 8, 7, 8, 8}; UWord16 mlt_sqvh_code_category_5[243] = { 0, 5, 220, 10, 16, 443, 390, 391,14333, 11, 26, 1566, 26, 54, 3135, 508, 1558,28581, 255, 1782,28599, 885, 6208,28578,14335,28579, 54, 9, 35, 3129, 27, 68, 3537, 1562, 3568,28610, 25, 62, 4078, 58, 118, 7763, 3107, 7758,28563, 778, 3131,28598, 780, 7123,28630,28593,28586, 118, 243, 6210,28614, 1018, 3567,28601,28611,28570, 68, 388, 6256,28619, 1559,28562,28606,28565,28591, 118,28594,28571, 62,28618,28590, 118, 58, 118, 118, 4, 28, 1781, 31, 60, 3134, 1938, 3882,28574, 25, 96, 7757, 49, 126,14244, 3883,14334,28613, 1769, 4077,28602, 3106, 7756,28582,28621,28566, 126, 14, 61, 4079, 61, 138,28491, 3536, 8153,28573, 49, 96,12442, 119, 240,28490,12443,28560,28561, 3111, 3580, 28564, 3130, 7759,28567,28568,28569, 240, 444, 6209,28572, 3569, 6211,28575,28576,28577, 138, 778, 7760,28580, 7761, 7762,28583,28584,28585, 240,14319,28587, 96,28588,28589, 240, 119, 240, 240, 139, 968,28592, 1554, 3581,28595,28596,28597, 60, 971, 3560,28600, 3582, 7132, 28603,28604,28605, 126,14332,28607, 96,28608,28609, 126, 49, 126, 126, 241, 1558,28612, 1563, 6257,28615,28616,28617, 138, 1559, 7133,28620, 6220,28622,28623,28624,28625, 240,28626, 28627, 96,28628,28629, 240, 119, 240, 240, 8152,28631, 61,28632,28633, 138, 61, 138, 138,28634,28635, 96,28636,28637, 240, 119, 240, 240, 49, 96, 96, 119, 240, 240, 119, 240, 240}; Word16 mlt_sqvh_bitcount_category_6[32] = { 1, 4, 4, 6, 4, 6, 6, 8, 4, 6, 6, 8, 6, 9, 8,10, 4, 6, 7, 8, 6, 9, 8,11, 6, 9, 8,10, 8,10, 9,11}; UWord16 mlt_sqvh_code_category_6[32] = { 1, 2, 4, 2, 5, 29, 24, 101, 3, 31, 28, 105, 3, 5, 102, 424, 1, 30, 0, 107, 27, 200, 103, 806, 1, 4, 104, 402, 3, 425, 213, 807}; Word16 *table_of_bitcount_tables[NUM_CATEGORIES-1] = { mlt_sqvh_bitcount_category_0, mlt_sqvh_bitcount_category_1, mlt_sqvh_bitcount_category_2, mlt_sqvh_bitcount_category_3, mlt_sqvh_bitcount_category_4, mlt_sqvh_bitcount_category_5, mlt_sqvh_bitcount_category_6 }; UWord16 *table_of_code_tables[NUM_CATEGORIES-1] = { (UWord16 *)mlt_sqvh_code_category_0, (UWord16 *)mlt_sqvh_code_category_1, (UWord16 *)mlt_sqvh_code_category_2, (UWord16 *)mlt_sqvh_code_category_3, (UWord16 *)mlt_sqvh_code_category_4, (UWord16 *)mlt_sqvh_code_category_5, (UWord16 *)mlt_sqvh_code_category_6 }; Word16 mlt_decoder_tree_category_0[180][2] = { { 1, 0},{ 2, 3},{ 4, 5},{ 6, 7},{ 8, 9},{ -1, -14},{ 10, 11},{ 12, 13}, { 14, 15},{ 16, 17},{ 18, 19},{ -15, 20},{ 21, 22},{ 23, -28},{ 24, -2},{ 25, 26}, { 27, 28},{ 29, 30},{ 31, 32},{ -29, 33},{ -16, 34},{ -3, 35},{ 36, 37},{ -42, 38}, { 39, 40},{ 41, 42},{ 43, 44},{ -17, 45},{ -43, 46},{ 47, 48},{ -4, 49},{ -56, 50}, { 51, 52},{ 53, 54},{ 55, 56},{ -57, -5},{ -30, 57},{ 58, 59},{ -18, 60},{ 61, -70}, { 62, 63},{ 64, -6},{ 65, 66},{ -44, 67},{ -71, 68},{ 69, 70},{ -19, -31},{ -84, 71}, { 72, 73},{ 74, 75},{ 76, 77},{ -7, 78},{ 79, -20},{ 80, 81},{ -85, 82},{ -98, 83}, { -58, 84},{ 85, -32},{ -99, 86},{ -8, 87},{ 88, 89},{ -72, -45},{ 90, -33},{ 91,-112}, { -21, 92},{ -59,-113},{ -46, 93},{ -9, 94},{ -22, 95},{ 96, 97},{ 98, 99},{ -23, -86}, { 100, 101},{ -34, -73},{ 102,-126},{-127, -35},{ 103, -47},{ 104, 105},{ 106, 107},{-140,-100}, {-114, -87},{ 108, 109},{ 110, 111},{-141, -60},{ 112, -48},{ 113, -24},{ -10, -61},{ 114,-101}, { 115, 116},{ -74, -36},{ 117,-128},{ 118, -62},{ 119, 120},{ -37, 121},{ -11, -49},{ -88, 122}, { 123,-115},{-154, -25},{-142, 124},{-155,-129},{ 125, -50},{ 126, 127},{ -76, -75},{ 128, 129}, { -63, -77},{-102, -39},{ -38, 130},{ -51, 131},{ -89,-116},{-117,-156},{ 132, -52},{ -78, 133}, { 134,-103},{ 135, 136},{-143, -65},{ 137, -64},{-130,-131},{ -90, 138},{-104, -91},{ -92, 139}, {-169,-183},{ -26,-118},{ 140, 141},{-144, -66},{ -12, 142},{-168, 143},{-105,-157},{ 144,-182}, { 145, 146},{ -79, 147},{ -53,-170},{ 148, 149},{ -27,-145},{ 150, -80},{-106, -13},{-132, -67}, {-158, -40},{-119, 151},{ 152,-107},{ 153, 154},{ -41,-184},{ 155, 156},{ -54, 157},{-171, 158}, { -94, 159},{-134,-146},{ -93,-133},{-159,-108},{ 160, -81},{ 161,-160},{ 162, -68},{-120,-122}, {-172, 163},{ -55, -95},{ 164,-109},{-161, -82},{-173,-185},{ 165, -69},{-147,-186},{ 166, 167}, {-121, -96},{ 168,-148},{-174, 169},{ 170,-136},{ -83, 171},{ 172, 173},{-135,-110},{-187, 174}, {-149,-150},{ 175,-123},{-162,-163},{ -97,-175},{-188, 176},{ 177, 178},{ 179,-111},{-151,-124}, {-137,-177},{-176,-125},{-164,-138},{-189,-190},}; Word16 mlt_decoder_tree_category_1[93][2] = { { 1, 0},{ 2, 3},{ 4, 5},{ 6, 7},{ 8, -10},{ -1, 9},{ 10, 11},{ 12, 13}, { -11, 14},{ 15, 16},{ 17, 18},{ -20, -2},{ 19, 20},{ -21, 21},{ 22, 23},{ -12, 24}, { 25, 26},{ 27, 28},{ 29, 30},{ -30, 31},{ -31, -3},{ 32, -22},{ 33, -13},{ 34, 35}, { 36, 37},{ 38, 39},{ 40, -4},{ -41, -14},{ 41, -40},{ -32, 42},{ 43, -23},{ 44, 45}, { 46, 47},{ 48, -5},{ -51, -50},{ -42, 49},{ -33, 50},{ -15, 51},{ 52, 53},{ 54, -24}, { 55, -43},{ 56, -52},{ 57, -61},{ -60, 58},{ -25, 59},{ -16, -34},{ -6, 60},{ -62, 61}, { -71, 62},{ -35, -7},{ 63, -72},{ -53, -17},{ 64, -44},{ -26, -70},{ 65, -45},{ -36, 66}, { -63, 67},{ -80, -54},{ -81, 68},{ -27, 69},{ 70, -82},{ -18, 71},{ 72, -55},{ 73, -64}, { 74, -73},{ 75, -46},{ -37, 76},{ -91, -8},{ -9, 77},{ -90, -83},{ 78, -28},{ 79, -56}, { -65, -38},{ -74, 80},{ -19, -57},{ -92, 81},{ -47, -48},{ 82, -66},{ 83, -29},{ -84, 84}, { -75, -85},{ -67, -93},{ -39, 85},{ -76, 86},{ -58, 87},{ 88, -49},{ -94, 89},{ 90, -68}, { 91, -78},{ -86, -59},{ -77, -95},{ 92, -69},{ -87, -96},}; Word16 mlt_decoder_tree_category_2[47][2] = { { 1, 0},{ 2, 3},{ 4, 5},{ 6, -7},{ -1, 7},{ -8, 8},{ 9, 10},{ 11, 12}, { 13, -14},{ -15, -9},{ -2, 14},{ 15, 16},{ 17, 18},{ 19, -16},{ 20, -22},{ -10, 21}, { -21, -3},{ 22, 23},{ 24, 25},{ -23, -17},{ 26, 27},{ 28, -29},{ -11, -28},{ 29, 30}, { -4, -24},{ -30, 31},{ 32, -18},{ 33, -35},{ -36, 34},{ -31, -12},{ 35, -25},{ -5, -37}, { 36, 37},{ -42, 38},{ -19, -43},{ -32, 39},{ -13, -38},{ -26, -20},{ 40, -6},{ -44, 41}, { 42, -39},{ -33, -45},{ 43, -27},{ 44, -46},{ 45, -34},{ -40, 46},{ -41, -47},}; Word16 mlt_decoder_tree_category_3[519][2] = { { 1, 2},{ 3, 4},{ 5, 0},{ 6, 7},{ 8, 9},{ 10, 11},{ 12, 13},{ 14, 15}, { 16, 17},{-125, 18},{ -1, 19},{ 20, 21},{ 22, 23},{ 24, 25},{ -5, -25},{ 26, 27}, { -6,-150},{ 28, 29},{ 30, 31},{ 32, 33},{ 34, -30},{ 35, 36},{ 37, 38},{ 39, -31}, {-126,-155},{ 40, 41},{-156, 42},{ 43,-130},{ 44,-131},{-151, -26},{ 45, 46},{-250, 47}, { 48, 49},{ 50, 51},{ 52,-275},{ 53, 54},{ -2, -7},{ 55, 56},{ 57, 58},{ 59, 60}, { 61, 62},{ 63, 64},{ 65, 66},{ 67, 68},{ 69, 70},{ 71, -50},{ 72,-180},{ 73, 74}, { 75, 76},{ 77, -55},{ 78,-175},{ 79, -36},{ 80, 81},{ -35, -10},{ 82, 83},{-280, 84}, { -11, 85},{ 86, -32},{ 87, 88},{ 89,-161},{ 90,-276},{ 91, 92},{-281, 93},{ -8, 94}, { 95, 96},{ 97,-157},{-181,-400},{-132, 98},{-375, 99},{-160, 100},{-127, 101},{ -27, 102}, { 103,-251},{ -56, 104},{ 105,-256},{-300, -3},{-152,-255},{ 106, 107},{ -37, 108},{-305, 109}, {-176, 110},{-136, 111},{ -12, 112},{ 113, 114},{ 115,-135},{ 116, 117},{-162, 118},{ -16, -51}, {-186, 119},{ 120, 121},{ 122, 123},{ -41, 124},{ -61, 125},{ 126, 127},{ 128, 129},{ 130, -60}, { 131, 132},{-306, 133},{ 134,-205},{-405, 135},{ 136, 137},{ 138, 139},{-185, 140},{ 141,-500}, { -15, 142},{ 143, -80},{ -75, -40},{-301, 144},{ 145, 146},{-200, 147},{ 148, 149},{ 150, 151}, { 152,-525},{ 153,-177},{-425, 154},{ 155, -13},{-430, 156},{ 157,-406},{ 158, 159},{-206,-380}, { 160, 161},{ 162, 163},{ 164,-182},{-137, 165},{-286, 166},{ 167,-401},{ 168, 169},{ -42, -33}, { 170,-166},{ -57,-325},{ 171,-187},{ -38, 172},{ 173, 174},{-165,-330},{ -4,-282},{ 175,-257}, {-261,-311},{-376, 176},{ 177, 178},{ -28, 179},{ 180, -9},{-285, 181},{ 182, 183},{ 184,-277}, { 185,-133},{-310, -81},{ -85, 186},{-158,-210},{ -17, 187},{ 188, 189},{ 190, -52},{-141, 191}, { 192,-128},{-191, -20},{ 193,-140},{ 194, 195},{-211,-260},{ 196, 197},{ 198, 199},{ 200, -66}, {-201,-225},{-381, 201},{ 202, 203},{ 204, 205},{ 206, 207},{-163,-287},{ 208,-100},{ 209, 210}, { 211, 212},{ 213,-252},{-105, -76},{ 214, 215},{ 216, -21},{ -86, -62},{-307, 217},{ -65,-455}, {-550, 218},{ 219, 220},{ 221, 222},{ 223, 224},{ 225,-230},{-142, 226},{-302,-426},{-431, 227}, { 228, 229},{ 230,-190},{-402, -46},{-153,-450},{-505, 231},{ 232, 233},{ 234, 235},{ 236, 237}, { 238, 239},{-262, -29},{ 240, 241},{ 242, 243},{-167, -67},{-331,-530},{ 244, 245},{ 246, 247}, { 248, 249},{ 250, 251},{ 252, 253},{ 254, 255},{ 256, 257},{ 258, 259},{ 260, 261},{ 262,-336}, { 263,-171},{-192,-207},{-258,-138},{ 264, 265},{ 266, 267},{ 268, 269},{ 270, 271},{ 272, 273}, { 274, -45},{-335,-411},{ -43, -18},{-265, -71},{-316,-326},{-350,-407},{-146, -14},{ 275, 276}, { 277, 278},{ 279, 280},{ 281,-216},{ -34,-283},{-291,-312},{-410,-168},{-555, 282},{ -70, -53}, {-235, -87},{ -77,-183},{-315,-332},{-178, -58},{ 283, 284},{ 285, 286},{ 287, 288},{ 289, 290}, { 291, 292},{ 293, 294},{ 295, 296},{ 297, 298},{-202,-226},{-170,-267},{-134,-290},{-355,-385}, {-386, -47},{-526,-196},{ 299, 300},{ 301, 302},{ 303, 304},{ 305, 306},{ 307, 308},{ 309, 310}, { 311, 312},{ 313, 314},{ 315, 316},{ 317, 318},{ 319, 320},{ 321, 322},{ 323, 324},{ 325,-111}, {-231,-253},{ -91, -82},{-172,-145},{ -22,-317},{ -90,-356},{-382,-159},{ 326, 327},{ 328, 329}, { 330, 331},{ 332, 333},{ 334, 335},{-106,-263},{-278,-215},{-110, -39},{-101,-377},{-129, -63}, {-436,-195},{-506,-531},{ 336,-212},{-154,-266},{ -59,-288},{-292,-303},{-337,-432},{-188,-451}, {-456,-460},{-501,-236},{-551, 337},{ 338, 339},{ 340, 341},{ 342, 343},{ 344, 345},{ 346, 347}, { 348, 349},{ 350, 351},{ 352, 353},{ 354, 355},{ 356, 357},{ 358, 359},{ 360, 361},{ 362, 363}, { 364, 365},{ 366, 367},{ 368, 369},{ 370, 371},{ 372, 373},{ 374, 375},{ 376, 377},{ 378, 379}, { 380, 381},{ 382, 383},{ 384, 385},{ 386, 387},{ 388, 389},{ 390, 391},{ 392, 393},{ 394, 395}, { 396, 397},{ 398, 399},{ 400, 401},{ 402, 403},{ 404, 405},{ 406, 407},{ -72,-272},{-309,-333}, {-340,-360},{ -68,-387},{-184,-416},{-427,-147},{-435,-437},{-115,-480},{-510,-532},{-164,-556}, { 408,-295},{-296,-297},{-107,-313},{-193,-173},{-320,-327},{-341,-351},{-352,-143},{-378, -19}, {-403,-412},{-268, -54},{ -83,-441},{-442,-457},{-475, -44},{ -97,-511},{-515,-208},{-527,-528}, {-237,-536},{-552, 409},{ 410, 411},{ 412, 413},{ 414, 415},{ 416, 417},{ 418, 419},{ 420, 421}, { 422, 423},{ 424, 425},{ 426, 427},{ 428, 429},{ 430, 431},{ 432, 433},{ 434, 435},{ 436, 437}, { 438, 439},{ 440, 441},{ 442, 443},{ 444, 445},{ 446, 447},{ 448, 449},{ 450, 451},{ 452, 453}, { 454, 455},{ 456, 457},{ 458, 459},{ 460, 461},{ 462, 463},{ 464, 465},{ 466, 467},{ 468, 469}, { 470, 471},{ 472, 473},{ 474, 475},{ 476, 477},{ 478, 479},{ 480, 481},{ 482, 483},{ 484, 485}, { 486, 487},{ 488, 489},{ 490, 491},{ 492, 493},{ 494, 495},{ 496, 497},{ 498, 499},{ 500, 501}, { 502, 503},{ 504, 505},{ 506, 507},{ 508, 509},{ 510, 511},{ 512, 513},{ 514, 515},{ 516, 517}, { 518,-104},{ -84,-218},{-318,-319},{-117,-321},{-322,-323},{-219,-174},{-243,-328},{-329, -94}, {-228,-194},{-240,-334},{-102,-229},{-169,-338},{-339,-116},{-289,-342},{-343,-345},{-346,-347}, { -23,-203},{-214,-353},{-204,-220},{-357,-358},{-264,-361},{-362,-363},{-365,-366},{-367, -92}, {-245,-121},{-293,-379},{-108,-232},{-221,-383},{-384,-233},{-294,-241},{-388,-389},{-390,-391}, {-392,-393},{-394,-395},{-396,-397},{-398, -24},{-109,-149},{-242,-404},{ -64, -79},{ -89,-408}, {-409,-213},{-120,-113},{-413,-414},{-415, -96},{-417,-418},{-419,-420},{-421,-422},{-423,-298}, { -69,-269},{-428,-429},{ -78,-270},{ -88,-433},{-434,-271},{-234,-259},{-438,-439},{-440,-227}, {-179,-443},{-445,-446},{-447,-223},{-238,-452},{-453,-454},{-273,-254},{-246,-458},{-459, -48}, {-461,-462},{-463,-465},{-466,-467},{-468,-470},{-471,-304},{-476,-477},{-478,-112},{-481,-482}, {-483,-485},{-486,-487},{-490,-491},{-103,-118},{-502,-503},{-504,-189},{ -93,-507},{-508,-509}, {-148,-139},{-512,-513},{-308,-516},{-517,-518},{-520,-521},{ -73, -98},{ -95, -49},{-529,-222}, {-217,-197},{-533,-534},{-535,-284},{-537,-538},{-540,-541},{-542,-543},{-545,-546},{-144,-198}, {-314,-553},{-209,-279},{-557,-558},{-560,-561},{-562,-563},{-565,-566},{-567,-575},{-576,-577}, {-578,-580},{-581,-582},{-583,-585},{-586,-587},{-590,-591},{-600,-601},{-605,-606},}; Word16 mlt_decoder_tree_category_4[208][2] = { { 1, 2},{ 3, 0},{ 4, 5},{ 6, 7},{ 8, 9},{ 10, 11},{ 12, -64},{ -1, 13}, { 14, -16},{ -4, 15},{ 16, 17},{ 18, -80},{ -5, 19},{ 20, 21},{ -20, 22},{ 23, -65}, { -84, -21},{ -68, 24},{ -17, 25},{ 26, 27},{ 28, -81},{ -69, -85},{ 29, 30},{ 31, 32}, {-128, 33},{ 34, 35},{ -2, 36},{ 37, 38},{-144, 39},{ 40, -6},{ 41, 42},{ -32, 43}, { 44, -8},{ 45, -25},{ -96, 46},{ 47,-100},{ -9, 48},{ 49, -36},{ 50, -24},{ 51, 52}, { 53,-148},{ 54, 55},{ -22, 56},{ 57, 58},{-132, -89},{ 59, 60},{-101, 61},{ -37, 62}, { -18, 63},{ -88,-129},{ -66, -70},{ -97, 64},{ -72, -73},{ 65,-145},{-149, -86},{ 66, -33}, { 67,-133},{ 68, 69},{ 70, 71},{-192, 72},{ 73,-160},{ -82, 74},{-164, 75},{ -10, 76}, { 77,-208},{ 78, -40},{ 79, 80},{ -3, 81},{ -7, 82},{ 83, 84},{-104, 85},{ 86, -26}, { 87,-105},{ 88,-112},{ 89, 90},{ 91, -41},{ 92, 93},{ 94, 95},{ -48, 96},{ -90, 97}, { 98, -28},{ -52, 99},{ -12, 100},{ 101, -74},{ -13,-116},{-161, 102},{ 103, -29},{-102, 104}, {-152,-165},{ 105, 106},{ 107, 108},{ 109, 110},{ 111,-212},{ 112, 113},{-136, 114},{ 115,-137}, { 116, -23},{ -19,-153},{ -98,-134},{-196, 117},{ 118, 119},{ -38,-193},{-113,-150},{-209, 120}, { 121, -93},{ -83, 122},{ 123, 124},{ 125, 126},{ 127, 128},{ 129, 130},{ 131, -34},{-146, -53}, { 132, 133},{ 134, 135},{ 136, 137},{ 138,-130},{ -49, 139},{ 140, 141},{-117, -42},{ -67, -92}, { 142, -87},{ -77,-197},{ -71, 143},{ 144, 145},{ 146, 147},{ 148, 149},{ 150, 151},{ 152, 153}, { 154, 155},{ 156, 157},{ 158, 159},{ 160, 161},{ 162, 163},{ 164, 165},{ 166, 167},{ 168, 169}, {-108, -76},{-168,-169},{-176, -44},{-224, -56},{ -45,-180},{ -11,-106},{-213, 170},{ 171, 172}, { 173, 174},{ 175, 176},{ 177, 178},{ 179, 180},{ 181, 182},{ 183, 184},{ 185, 186},{ 187, 188}, { 189, 190},{ 191, 192},{ 193, 194},{ 195, 196},{ 197, 198},{ 199, 200},{ 201, 202},{ 203, 204}, { 205, 206},{ 207,-131},{ -30, -27},{ -43,-151},{ -75,-154},{-156,-162},{-109,-194},{-198,-201}, {-114,-225},{-228,-229},{-141,-142},{ -94,-124},{ -95,-147},{-115,-125},{ -54, -55},{-107, -58}, { -39,-155},{-121,-157},{-158,-103},{ -14,-122},{-163, -51},{ -57,-166},{-167, -46},{-110,-170}, {-172,-173},{ -61,-177},{-178, -99},{-181,-182},{-184,-185},{-118, -35},{ -15,-195},{ -31, -60}, {-135,-199},{-200, -79},{-202,-204},{-205,-119},{ -91,-210},{-211, -78},{-120,-214},{-215,-216}, {-217,-218},{-220,-221},{ -50,-138},{-226,-139},{-140,-230},{-232,-233},{-240,-241},{-244,-245}, }; Word16 mlt_decoder_tree_category_5[191][2] = { { 1, 2},{ 0, 3},{ 4, 5},{ 6, 7},{ 8, 9},{ 10, 11},{ -81, -1},{ 12, 13}, { 14, -27},{ -3, -9},{ 15, 16},{ 17, 18},{ 19, 20},{-108, 21},{ -4, 22},{ 23, -36}, { -12, 24},{ -82, 25},{ 26, -84},{ 27, -90},{ -10, -30},{ 28, 29},{ 30, -28},{ 31,-117}, { -13, 32},{ -39, 33},{ 34,-109},{ 35, -93},{ -85,-111},{ -37, 36},{ -31, 37},{ -91, 38}, { 39, 40},{ -40,-120},{ 41, 42},{-118, 43},{ -94, 44},{-112,-162},{ 45, 46},{ -2, 47}, { 48, 49},{-121,-189},{ 50, -54},{ 51, 52},{ 53, -18},{ 54, 55},{ -6, 56},{ 57, -5}, {-135, 58},{ 59, 60},{ 61, 62},{ -63, 63},{ 64, -7},{ -15, 65},{ 66, 67},{ -45, 68}, { 69, 70},{ 71, -21},{ 72, 73},{ 74, 75},{ 76, 77},{-163, 78},{ 79,-171},{-144, 80}, { -48, 81},{ -57, 82},{ 83, 84},{-165, 85},{ -16,-198},{ 86, 87},{ -11, 88},{ 89, -99}, { 90, -83},{ -19, 91},{ 92, 93},{ 94, 95},{ 96, 97},{ 98, 99},{ -87, 100},{ 101, 102}, {-190, -66},{ -33,-192},{ 103, 104},{ 105, 106},{-102, -42},{ 107,-126},{ 108, -29},{-129, -46}, { -86, -14},{-114, -32},{-172, 109},{ 110, -58},{ -34,-138},{ 111, 112},{ 113, 114},{ 115, 116}, { 117, 118},{ 119, 120},{-127,-166},{-174, 121},{ 122, 123},{ 124, 125},{ -88, -96},{ 126,-100}, { -38,-110},{ -22,-136},{ -55,-139},{-201, 127},{ -64,-193},{ 128, -49},{-175,-199},{ 129, 130}, { 131, 132},{ 133, 134},{ 135, 136},{ 137, 138},{ 139, 140},{ 141, 142},{ 143, 144},{ 145, 146}, { 147, 148},{ 149, 150},{-103, -92},{ -43,-130},{-145,-147},{-148, -41},{-216,-115},{-119,-123}, { -95, 151},{ 152, 153},{ 154, 155},{ 156, 157},{ 158, 159},{ 160, 161},{ 162, 163},{ 164, 165}, { 166, 167},{ 168, 169},{ 170, 171},{ 172, 173},{ 174, 175},{ 176, 177},{ 178, 179},{ 180, 181}, { 182, 183},{ 184, 185},{ 186, 187},{ 188, 189},{ 190,-153},{-180, -8},{ -97, -24},{-122,-113}, {-124,-125},{ -67, -44},{-128, -69},{-106,-131},{-132,-133},{ -61, -73},{-137,-116},{ -89,-140}, {-141,-142},{ -23, -25},{-146, -17},{-104,-149},{-150,-151},{ -52,-154},{-156,-157},{ -76, -70}, {-164, -51},{ -72,-167},{-168,-169},{ -47, -20},{-173, -59},{-101,-176},{-177,-178},{ -68,-181}, {-183,-184},{ -35, -60},{-191, -98},{ -56,-194},{-195,-196},{ -75, -65},{-200,-105},{-202,-203}, {-204,-205},{-207,-208},{-210,-211},{ -50,-217},{-219,-220},{-225,-226},{-228,-229},}; Word16 mlt_decoder_tree_category_6[31][2] = { { 1, 0},{ 2, 3},{ 4, 5},{ 6, 7},{ 8, -16},{ -1, -8},{ -2, -4},{ 9, 10}, { 11, 12},{ 13, 14},{ 15, 16},{ 17, -24},{ -3, -12},{ -6, 18},{ 19, -20},{ -10, -5}, { -17, -9},{ -18, 20},{ 21, 22},{ 23, 24},{ 25, -28},{ 26, -7},{ -14, -22},{ -26, -11}, { 27, -19},{ -25, -13},{ -21, 28},{ 29, -30},{ -27, 30},{ -15, -29},{ -23, -31},}; Word16 *table_of_decoder_tables[NUM_CATEGORIES-1] = { (Word16 *)mlt_decoder_tree_category_0, (Word16 *)mlt_decoder_tree_category_1, (Word16 *)mlt_decoder_tree_category_2, (Word16 *)mlt_decoder_tree_category_3, (Word16 *)mlt_decoder_tree_category_4, (Word16 *)mlt_decoder_tree_category_5, (Word16 *)mlt_decoder_tree_category_6, }; ================================================ FILE: deps/pjsip/third_party/g7221/common/huff_tab.h ================================================ /*********************************************************************** ** ** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C ** > Software Release 2.1 (2008-06) ** (Simple repackaging; no change from 2005-05 Release 2.0 code) ** ** 2004 Polycom, Inc. ** ** All rights reserved. ** ***********************************************************************/ extern Word16 differential_region_power_bits[MAX_NUMBER_OF_REGIONS][DIFF_REGION_POWER_LEVELS]; extern UWord16 differential_region_power_codes[MAX_NUMBER_OF_REGIONS][DIFF_REGION_POWER_LEVELS]; extern Word16 differential_region_power_decoder_tree[MAX_NUMBER_OF_REGIONS][DIFF_REGION_POWER_LEVELS-1][2]; extern Word16 mlt_quant_centroid[NUM_CATEGORIES][MAX_NUM_BINS]; extern Word16 expected_bits_table[NUM_CATEGORIES]; extern Word16 mlt_sqvh_bitcount_category_0[196]; extern UWord16 mlt_sqvh_code_category_0[196]; extern Word16 mlt_sqvh_bitcount_category_1[100]; extern UWord16 mlt_sqvh_code_category_1[100]; extern Word16 mlt_sqvh_bitcount_category_2[49]; extern UWord16 mlt_sqvh_code_category_2[49]; extern Word16 mlt_sqvh_bitcount_category_3[625]; extern UWord16 mlt_sqvh_code_category_3[625]; extern Word16 mlt_sqvh_bitcount_category_4[256]; extern UWord16 mlt_sqvh_code_category_4[256]; extern Word16 mlt_sqvh_bitcount_category_5[243]; extern UWord16 mlt_sqvh_code_category_5[243]; extern Word16 mlt_sqvh_bitcount_category_6[32]; extern UWord16 mlt_sqvh_code_category_6[32]; extern Word16 *table_of_bitcount_tables[NUM_CATEGORIES-1]; extern UWord16 *table_of_code_tables[NUM_CATEGORIES-1]; extern Word16 mlt_decoder_tree_category_0[180][2]; extern Word16 mlt_decoder_tree_category_1[93][2]; extern Word16 mlt_decoder_tree_category_2[47][2]; extern Word16 mlt_decoder_tree_category_3[519][2]; extern Word16 mlt_decoder_tree_category_4[208][2]; extern Word16 mlt_decoder_tree_category_5[191][2]; extern Word16 mlt_decoder_tree_category_6[31][2]; extern Word16 *table_of_decoder_tables[NUM_CATEGORIES-1]; ================================================ FILE: deps/pjsip/third_party/g7221/common/tables.c ================================================ /**************************************************************************** ** ** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C ** > Software Release 2.1 (2008-06) ** (Simple repackaging; no change from 2005-05 Release 2.0 code) ** ** 2004 Polycom, Inc. ** ** All rights reserved. ** ****************************************************************************/ /**************************************************************************** Filename: tables.c Purpose: Contains tables used by G.722.1 Annex C Design Notes: ****************************************************************************/ /*************************************************************************** Include files ***************************************************************************/ #include "defs.h" Word16 int_region_standard_deviation_table[REGION_POWER_TABLE_SIZE] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 3, 4, 6, 8, 11, 16, 23, 32, 45, 64, 91, 128, 181, 256, 362, 512, 724, 1024, 1448, 2048, 2896, 4096, 5793, 8192, 11585, 16384, 23170, 0,0,0,0,0,0, 0,0,0,0}; Word16 standard_deviation_inverse_table[REGION_POWER_TABLE_SIZE] = { 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 23170, 16384, 11585, 8192, 5793, 4096, 2896, 2048, 1448, 1024, 724, 512, 362, 256, 181, 128, 91, 64, 45, 32, 23, 16, 11, 8, 6, 4, 3, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}; Word16 step_size_inverse_table[NUM_CATEGORIES]={ 23167,16384,11585,8192,5793,4096,2896,2896 }; Word16 vector_dimension[NUM_CATEGORIES] = { 2, 2, 2, 4, 4, 5, 5, 1}; Word16 number_of_vectors[NUM_CATEGORIES] = {10,10,10, 5, 5, 4, 4,20}; /* The last category isn't really coded with scalar quantization. */ Word16 max_bin[NUM_CATEGORIES] = {13, 9, 6, 4, 3, 2, 1, 1}; Word16 max_bin_plus_one_inverse[NUM_CATEGORIES] = { 2341,3277,4682,6554,8193,10923,16385,16385 }; /* * Release 1.2. * Add new version of int_dead_zone[] to go with * changes to vector_huffman() in encoder.c. * */ /************** See new version of table below Word16 int_dead_zone[NUM_CATEGORIES]= { 9830,10813,11796,12780,13763,14746,16384,16384 }; ***************/ /******** New version of table added in Release 1.2 ********/ Word16 int_dead_zone[NUM_CATEGORIES]= { 2457, 2703, 2949, 3195, 3440, 3686, 4096, 4096 }; /* * Release 1.2. * Added this new table to go with * changes to vector_huffman() in encoder.c, * accompanies the new table for int_dead_zone[]. */ Word16 int_dead_zone_low_bits[NUM_CATEGORIES]= { 2, 1, 0, 0, 3, 2, 0, 0 }; Word16 samples_to_rmlt_window[DCT_LENGTH]= { 44, 134, 224, 314, 404, 494, 584, 674, 764, 853, 943, 1033, 1123, 1213, 1302, 1392, 1482, 1571, 1661, 1750, 1840, 1929, 2019, 2108, 2197, 2286, 2376, 2465, 2554, 2643, 2732, 2821, 2909, 2998, 3087, 3175, 3264, 3352, 3441, 3529, 3617, 3705, 3793, 3881, 3969, 4057, 4144, 4232, 4319, 4407, 4494, 4581, 4668, 4755, 4842, 4928, 5015, 5101, 5188, 5274, 5360, 5446, 5532, 5617, 5703, 5788, 5873, 5959, 6043, 6128, 6213, 6297, 6382, 6466, 6550, 6634, 6718, 6801, 6885, 6968, 7051, 7134, 7217, 7299, 7382, 7464, 7546, 7628, 7709, 7791, 7872, 7953, 8034, 8115, 8195, 8276, 8356, 8436, 8515, 8595, 8674, 8753, 8832, 8911, 8989, 9068, 9146, 9223, 9301, 9378, 9455, 9532, 9609, 9685, 9762, 9838, 9913, 9989, 10064, 10139, 10214, 10288, 10363, 10437, 10510, 10584, 10657, 10730, 10803, 10875, 10948, 11020, 11091, 11163, 11234, 11305, 11375, 11446, 11516, 11586, 11655, 11724, 11793, 11862, 11930, 11998, 12066, 12134, 12201, 12268, 12334, 12401, 12467, 12532, 12598, 12663, 12728, 12792, 12857, 12920, 12984, 13047, 13110, 13173, 13235, 13297, 13359, 13420, 13481, 13542, 13602, 13662, 13722, 13782, 13841, 13899, 13958, 14016, 14074, 14131, 14188, 14245, 14301, 14357, 14413, 14468, 14523, 14578, 14632, 14686, 14739, 14793, 14845, 14898, 14950, 15002, 15053, 15104, 15155, 15205, 15255, 15305, 15354, 15403, 15451, 15500, 15547, 15595, 15642, 15688, 15734, 15780, 15826, 15871, 15916, 15960, 16004, 16047, 16091, 16133, 16176, 16218, 16259, 16300, 16341, 16382, 16422, 16461, 16501, 16540, 16578, 16616, 16654, 16691, 16728, 16764, 16800, 16836, 16871, 16906, 16940, 16974, 17008, 17041, 17074, 17106, 17138, 17170, 17201, 17232, 17262, 17292, 17321, 17350, 17379, 17407, 17435, 17462, 17489, 17516, 17542, 17567, 17593, 17617, 17642, 17666, 17689, 17713, 17735, 17758, 17779, 17801, 17822, 17842, 17863, 17882, 17901, 17920, 17939, 17957, 17974, 17991, 18008, 18024, 18040, 18055, 18070, 18085, 18099, 18113, 18126, 18139, 18151, 18163, 18174, 18185, 18196, 18206, 18216, 18225, 18234, 18242, 18250, 18257, 18265, 18271, 18277, 18283, 18288, 18293, 18298, 18302, 18305, 18308, 18311, 18313, 18315, 18316, 18317, 18317, }; Word16 rmlt_to_samples_window[DCT_LENGTH]= { 44, 133, 222, 310, 399, 488, 577, 666, 754, 843, 932, 1020, 1109, 1198, 1286, 1375, 1464, 1552, 1641, 1729, 1817, 1906, 1994, 2082, 2171, 2259, 2347, 2435, 2523, 2611, 2699, 2786, 2874, 2962, 3049, 3137, 3224, 3312, 3399, 3486, 3573, 3660, 3747, 3834, 3921, 4008, 4094, 4181, 4267, 4353, 4439, 4526, 4611, 4697, 4783, 4869, 4954, 5040, 5125, 5210, 5295, 5380, 5465, 5549, 5634, 5718, 5802, 5886, 5970, 6054, 6138, 6221, 6304, 6388, 6471, 6553, 6636, 6719, 6801, 6883, 6965, 7047, 7129, 7211, 7292, 7373, 7454, 7535, 7616, 7696, 7777, 7857, 7937, 8016, 8096, 8175, 8254, 8333, 8412, 8491, 8569, 8647, 8725, 8803, 8880, 8957, 9035, 9111, 9188, 9264, 9341, 9417, 9492, 9568, 9643, 9718, 9793, 9868, 9942, 10016, 10090, 10163, 10237, 10310, 10383, 10455, 10528, 10600, 10672, 10743, 10815, 10886, 10957, 11027, 11098, 11168, 11237, 11307, 11376, 11445, 11514, 11582, 11650, 11718, 11785, 11853, 11920, 11986, 12053, 12119, 12185, 12250, 12315, 12380, 12445, 12509, 12573, 12637, 12701, 12764, 12826, 12889, 12951, 13013, 13075, 13136, 13197, 13257, 13318, 13378, 13437, 13497, 13556, 13614, 13673, 13731, 13788, 13846, 13903, 13959, 14016, 14072, 14128, 14183, 14238, 14292, 14347, 14401, 14454, 14508, 14561, 14613, 14665, 14717, 14769, 14820, 14871, 14921, 14971, 15021, 15070, 15119, 15168, 15216, 15264, 15311, 15359, 15405, 15452, 15498, 15544, 15589, 15634, 15678, 15722, 15766, 15810, 15853, 15895, 15938, 15979, 16021, 16062, 16103, 16143, 16183, 16223, 16262, 16300, 16339, 16377, 16414, 16452, 16488, 16525, 16561, 16596, 16632, 16666, 16701, 16735, 16768, 16801, 16834, 16867, 16899, 16930, 16961, 16992, 17022, 17052, 17082, 17111, 17140, 17168, 17196, 17223, 17250, 17277, 17303, 17329, 17354, 17379, 17404, 17428, 17452, 17475, 17498, 17520, 17542, 17564, 17585, 17606, 17626, 17646, 17665, 17684, 17703, 17721, 17739, 17756, 17773, 17790, 17806, 17821, 17836, 17851, 17865, 17879, 17893, 17906, 17918, 17931, 17942, 17954, 17965, 17975, 17985, 17995, 18004, 18012, 18021, 18028, 18036, 18043, 18049, 18055, 18061, 18066, 18071, 18076, 18079, 18083, 18086, 18089, 18091, 18093, 18094, 18095, 18095, }; Word16 max_samples_to_rmlt_window[MAX_DCT_LENGTH]={ 0, 43, 89, 133, 178, 222, 268, 314, 357, 403, 447, 493, 538, 582, 628, 671, 717, 763, 807, 853, 896, 942, 987, 1031, 1077, 1121, 1166, 1212, 1256, 1301, 1345, 1390, 1436, 1480, 1526, 1569, 1615, 1660, 1704, 1749, 1793, 1838, 1884, 1928, 1973, 2016, 2062, 2107, 2151, 2196, 2239, 2285, 2331, 2374, 2419, 2463, 2508, 2553, 2597, 2642, 2685, 2730, 2776, 2819, 2864, 2908, 2952, 2998, 3041, 3086, 3129, 3174, 3219, 3263, 3307, 3350, 3396, 3440, 3483, 3528, 3571, 3616, 3661, 3704, 3748, 3791, 3836, 3881, 3923, 3968, 4011, 4055, 4100, 4143, 4187, 4230, 4274, 4318, 4362, 4406, 4448, 4493, 4537, 4580, 4624, 4666, 4710, 4755, 4797, 4841, 4883, 4927, 4971, 5013, 5057, 5099, 5144, 5187, 5229, 5273, 5315, 5359, 5402, 5444, 5488, 5530, 5573, 5617, 5658, 5702, 5743, 5787, 5830, 5871, 5915, 5956, 6000, 6043, 6084, 6127, 6169, 6211, 6254, 6296, 6339, 6380, 6423, 6465, 6507, 6549, 6590, 6633, 6675, 6716, 6759, 6799, 6842, 6884, 6925, 6967, 7007, 7050, 7092, 7132, 7175, 7215, 7257, 7299, 7339, 7381, 7421, 7462, 7504, 7544, 7586, 7626, 7667, 7709, 7749, 7790, 7830, 7871, 7912, 7952, 7993, 8032, 8073, 8114, 8153, 8194, 8234, 8275, 8315, 8355, 8395, 8434, 8474, 8515, 8554, 8594, 8632, 8673, 8713, 8752, 8792, 8830, 8871, 8910, 8949, 8989, 9027, 9066, 9106, 9144, 9184, 9221, 9261, 9300, 9338, 9378, 9415, 9454, 9493, 9531, 9570, 9607, 9646, 9685, 9722, 9761, 9798, 9836, 9875, 9912, 9950, 9987, 10025, 10064, 10100, 10138, 10175, 10213, 10250, 10287, 10325, 10361, 10398, 10436, 10472, 10510, 10545, 10583, 10620, 10656, 10692, 10728, 10766, 10803, 10838, 10874, 10910, 10947, 10983, 11018, 11055, 11089, 11126, 11162, 11197, 11233, 11268, 11303, 11340, 11374, 11410, 11444, 11480, 11515, 11549, 11585, 11619, 11654, 11689, 11723, 11758, 11791, 11826, 11861, 11895, 11930, 11963, 11997, 12032, 12065, 12099, 12132, 12166, 12201, 12233, 12267, 12300, 12333, 12367, 12400, 12433, 12465, 12499, 12532, 12563, 12597, 12629, 12662, 12695, 12727, 12759, 12790, 12823, 12856, 12887, 12920, 12951, 12983, 13016, 13046, 13078, 13109, 13141, 13173, 13203, 13235, 13266, 13296, 13328, 13358, 13389, 13419, 13450, 13481, 13510, 13541, 13571, 13602, 13632, 13661, 13692, 13721, 13751, 13781, 13810, 13840, 13869, 13898, 13929, 13957, 13986, 14015, 14044, 14073, 14101, 14130, 14158, 14187, 14216, 14244, 14272, 14300, 14328, 14357, 14384, 14412, 14439, 14468, 14495, 14522, 14550, 14577, 14604, 14632, 14658, 14686, 14711, 14739, 14765, 14792, 14819, 14844, 14871, 14897, 14923, 14949, 14975, 15001, 15027, 15053, 15079, 15103, 15129, 15155, 15180, 15205, 15229, 15255, 15280, 15304, 15329, 15353, 15378, 15403, 15426, 15451, 15475, 15499, 15523, 15546, 15570, 15594, 15618, 15641, 15664, 15688, 15711, 15734, 15757, 15780, 15802, 15825, 15848, 15871, 15892, 15915, 15937, 15960, 15982, 16003, 16026, 16047, 16069, 16090, 16112, 16133, 16154, 16175, 16197, 16217, 16239, 16259, 16279, 16301, 16320, 16341, 16361, 16382, 16402, 16421, 16441, 16461, 16481, 16501, 16520, 16539, 16558, 16578, 16597, 16615, 16635, 16653, 16672, 16691, 16709, 16728, 16746, 16764, 16782, 16800, 16818, 16835, 16853, 16871, 16888, 16905, 16923, 16940, 16957, 16974, 16991, 17008, 17024, 17041, 17057, 17074, 17090, 17106, 17122, 17138, 17154, 17169, 17185, 17201, 17216, 17231, 17246, 17262, 17277, 17291, 17306, 17321, 17336, 17350, 17364, 17379, 17393, 17407, 17421, 17435, 17449, 17462, 17476, 17490, 17502, 17515, 17528, 17542, 17554, 17567, 17580, 17592, 17605, 17618, 17629, 17642, 17653, 17666, 17678, 17689, 17701, 17712, 17724, 17736, 17746, 17757, 17768, 17779, 17790, 17800, 17811, 17822, 17832, 17842, 17852, 17862, 17872, 17882, 17892, 17902, 17911, 17920, 17930, 17938, 17947, 17956, 17965, 17974, 17983, 17991, 17999, 18008, 18016, 18025, 18032, 18040, 18047, 18055, 18063, 18070, 18078, 18085, 18092, 18099, 18106, 18112, 18119, 18126, 18132, 18138, 18144, 18151, 18157, 18163, 18168, 18174, 18179, 18185, 18191, 18196, 18201, 18206, 18211, 18216, 18220, 18225, 18229, 18234, 18238, 18242, 18246, 18250, 18254, 18257, 18260, 18264, 18268, 18271, 18274, 18277, 18280, 18283, 18286, 18288, 18291, 18293, 18295, 18297, 18300, 18301, 18303, 18305, 18306, 18308, 18309, 18311, 18312, 18312, 18314, 18315, 18315, 18316, 18316, 18317, 18317, 18317 }; Word16 max_rmlt_to_samples_window[MAX_DCT_LENGTH]={ 0, 43, 88, 131, 176, 219, 265, 310, 353, 398, 442, 487, 532, 575, 620, 663, 709, 754, 797, 842, 885, 931, 975, 1019, 1064, 1107, 1152, 1197, 1240, 1286, 1329, 1373, 1419, 1462, 1507, 1550, 1595, 1640, 1683, 1728, 1771, 1816, 1861, 1904, 1949, 1992, 2037, 2081, 2125, 2170, 2212, 2258, 2302, 2345, 2390, 2433, 2477, 2522, 2565, 2610, 2652, 2697, 2742, 2784, 2829, 2872, 2916, 2961, 3004, 3048, 3091, 3136, 3180, 3223, 3267, 3310, 3354, 3399, 3441, 3485, 3528, 3572, 3616, 3659, 3703, 3745, 3790, 3834, 3876, 3920, 3962, 4006, 4050, 4093, 4136, 4179, 4222, 4266, 4309, 4352, 4394, 4438, 4482, 4524, 4568, 4610, 4653, 4697, 4739, 4782, 4824, 4867, 4911, 4953, 4996, 5038, 5081, 5124, 5166, 5209, 5251, 5294, 5337, 5378, 5421, 5463, 5506, 5548, 5590, 5633, 5674, 5717, 5759, 5800, 5843, 5884, 5927, 5970, 6011, 6053, 6094, 6136, 6178, 6219, 6262, 6302, 6345, 6387, 6428, 6470, 6510, 6552, 6594, 6635, 6677, 6717, 6759, 6801, 6841, 6883, 6922, 6964, 7006, 7046, 7087, 7127, 7169, 7210, 7250, 7291, 7331, 7372, 7413, 7453, 7494, 7533, 7574, 7615, 7655, 7695, 7735, 7776, 7816, 7855, 7896, 7935, 7975, 8016, 8054, 8095, 8134, 8174, 8214, 8253, 8293, 8332, 8371, 8412, 8450, 8490, 8528, 8568, 8607, 8646, 8685, 8723, 8763, 8802, 8840, 8879, 8917, 8956, 8995, 9033, 9072, 9109, 9148, 9187, 9225, 9264, 9301, 9340, 9378, 9415, 9454, 9491, 9529, 9567, 9604, 9642, 9679, 9717, 9755, 9791, 9829, 9866, 9903, 9941, 9977, 10015, 10051, 10089, 10126, 10162, 10199, 10235, 10272, 10309, 10345, 10382, 10417, 10454, 10491, 10526, 10563, 10598, 10635, 10672, 10706, 10742, 10778, 10814, 10850, 10885, 10921, 10955, 10991, 11027, 11061, 11097, 11131, 11166, 11202, 11236, 11271, 11305, 11340, 11376, 11409, 11444, 11478, 11513, 11547, 11580, 11615, 11648, 11683, 11717, 11751, 11785, 11817, 11852, 11886, 11918, 11952, 11985, 12018, 12053, 12085, 12118, 12150, 12184, 12217, 12249, 12282, 12314, 12347, 12380, 12411, 12444, 12476, 12508, 12541, 12572, 12604, 12635, 12668, 12700, 12731, 12763, 12794, 12826, 12858, 12888, 12920, 12950, 12982, 13013, 13043, 13074, 13105, 13135, 13166, 13196, 13227, 13257, 13287, 13317, 13347, 13377, 13407, 13437, 13467, 13496, 13525, 13555, 13585, 13614, 13643, 13672, 13701, 13730, 13760, 13787, 13817, 13845, 13873, 13903, 13930, 13959, 13987, 14015, 14043, 14071, 14099, 14126, 14154, 14183, 14209, 14237, 14264, 14292, 14319, 14346, 14373, 14400, 14427, 14454, 14480, 14507, 14533, 14560, 14586, 14612, 14639, 14664, 14691, 14717, 14742, 14768, 14793, 14819, 14845, 14870, 14896, 14920, 14945, 14971, 14996, 15020, 15044, 15070, 15094, 15118, 15143, 15167, 15192, 15216, 15239, 15263, 15287, 15311, 15335, 15358, 15382, 15405, 15428, 15452, 15474, 15498, 15520, 15543, 15566, 15588, 15611, 15633, 15656, 15678, 15700, 15722, 15744, 15766, 15788, 15809, 15831, 15852, 15874, 15895, 15916, 15937, 15958, 15979, 16000, 16020, 16041, 16061, 16082, 16103, 16122, 16143, 16162, 16183, 16203, 16222, 16242, 16261, 16281, 16300, 16319, 16339, 16357, 16377, 16396, 16414, 16433, 16451, 16470, 16488, 16506, 16525, 16542, 16561, 16579, 16596, 16614, 16631, 16649, 16667, 16683, 16700, 16717, 16735, 16752, 16768, 16785, 16801, 16818, 16834, 16850, 16867, 16883, 16899, 16915, 16930, 16945, 16961, 16977, 16992, 17007, 17022, 17037, 17052, 17067, 17081, 17096, 17111, 17126, 17140, 17154, 17168, 17182, 17196, 17209, 17223, 17237, 17250, 17264, 17277, 17290, 17303, 17315, 17329, 17341, 17354, 17367, 17379, 17391, 17404, 17415, 17428, 17439, 17451, 17463, 17475, 17486, 17497, 17509, 17520, 17531, 17542, 17552, 17563, 17574, 17584, 17595, 17605, 17616, 17626, 17636, 17646, 17655, 17665, 17675, 17684, 17694, 17703, 17712, 17721, 17730, 17739, 17747, 17756, 17764, 17773, 17781, 17789, 17798, 17806, 17813, 17821, 17829, 17836, 17843, 17851, 17858, 17866, 17872, 17879, 17886, 17893, 17899, 17906, 17912, 17918, 17924, 17931, 17937, 17942, 17948, 17953, 17959, 17964, 17970, 17975, 17980, 17985, 17990, 17995, 17999, 18004, 18008, 18012, 18016, 18021, 18025, 18028, 18032, 18036, 18039, 18043, 18046, 18049, 18052, 18055, 18058, 18061, 18064, 18067, 18069, 18071, 18073, 18075, 18078, 18079, 18081, 18083, 18084, 18086, 18087, 18089, 18090, 18090, 18091, 18092, 18093, 18094, 18094, 18095, 18095, 18095 }; ================================================ FILE: deps/pjsip/third_party/g7221/common/tables.h ================================================ /*********************************************************************** ** ** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C ** > Software Release 2.1 (2008-06) ** (Simple repackaging; no change from 2005-05 Release 2.0 code) ** ** 1999 PictureTel Coporation ** Andover, MA, USA ** ** All rights reserved. ** ***********************************************************************/ /*********************************************************************** Filename: tables.h Purpose: Contains table definitions used by G.722.1 Annex C Design Notes: ***********************************************************************/ /*********************************************************************** Include files ***********************************************************************/ #define REGION_POWER_TABLE_SIZE 64 #define NUM_CATEGORIES 8 #define DCT_LENGTH 320 #define MAX_DCT_LENGTH 640 extern Word16 int_region_standard_deviation_table[REGION_POWER_TABLE_SIZE]; extern Word16 standard_deviation_inverse_table[REGION_POWER_TABLE_SIZE]; extern Word16 step_size_inverse_table[NUM_CATEGORIES]; extern Word16 vector_dimension[NUM_CATEGORIES]; extern Word16 number_of_vectors[NUM_CATEGORIES]; /* The last category isn't really coded with scalar quantization. */ extern Word16 max_bin[NUM_CATEGORIES]; extern Word16 max_bin_plus_one_inverse[NUM_CATEGORIES]; extern Word16 int_dead_zone[NUM_CATEGORIES]; extern Word16 samples_to_rmlt_window[DCT_LENGTH]; extern Word16 rmlt_to_samples_window[DCT_LENGTH]; /* Add next line in Release 1.2 */ extern Word16 int_dead_zone_low_bits[NUM_CATEGORIES]; extern Word16 max_samples_to_rmlt_window[MAX_DCT_LENGTH]; extern Word16 max_rmlt_to_samples_window[MAX_DCT_LENGTH]; ================================================ FILE: deps/pjsip/third_party/g7221/common/typedef.h ================================================ /* $Id: typedef.h 2623 2009-04-20 18:38:15Z bennylp $ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef TYPEDEF_H #define TYPEDEF_H #include typedef pj_int8_t Word8; typedef pj_int16_t Word16; typedef pj_int32_t Word32; typedef pj_uint16_t UWord16; typedef pj_uint32_t UWord32; typedef int Flag; #endif /* TYPEDEF_H */ ================================================ FILE: deps/pjsip/third_party/g7221/decode/coef2sam.c ================================================ /***************************************************************************** ** ** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C ** > Software Release 2.1 (2008-06) ** (Simple repackaging; no change from 2005-05 Release 2.0 code) ** ** 2004 Polycom, Inc. ** ** All rights reserved. ** *****************************************************************************/ /***************************************************************************** * Filename: rmlt_coefs_to_samples.c * * Purpose: Convert Reversed MLT (Modulated Lapped Transform) * Coefficients to Samples * * The "Reversed MLT" is an overlapped block transform which uses * even symmetry * on the left, odd symmetry on the right and a * Type IV DCT as the block transform. * It is thus similar to a * MLT which uses odd symmetry on the left, even symmetry * on the * right and a Type IV DST as the block transform. In fact, it is * equivalent * to reversing the order of the samples, performing * an MLT and then negating all * the even-numbered coefficients. * *****************************************************************************/ /*************************************************************************** Include files ***************************************************************************/ #include "defs.h" #include "tables.h" #include "count.h" /*************************************************************************** Function: rmlt_coefs_to_samples Syntax: void rmlt_coefs_to_samples(Word16 *coefs, Word16 *old_samples, Word16 *out_samples, Word16 dct_length, Word16 mag_shift) inputs: Word16 *coefs Word16 *old_samples Word16 dct_length Word16 mag_shift outputs: Word16 *out_samples Description: Converts the mlt_coefs to samples Design Notes: WMOPS: 7kHz | 24kbit | 32kbit -------|--------------|---------------- AVG | 1.91 | 1.91 -------|--------------|---------------- MAX | 1.91 | 1.91 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 3.97 | 3.97 | 3.97 -------|--------------|----------------|---------------- MAX | 3.97 | 3.97 | 3.97 -------|--------------|----------------|---------------- ***************************************************************************/ void rmlt_coefs_to_samples(Word16 *coefs, Word16 *old_samples, Word16 *out_samples, Word16 dct_length, Word16 mag_shift) { Word16 index, vals_left; Word16 new_samples[MAX_DCT_LENGTH]; Word16 *new_ptr, *old_ptr; Word16 *win_new, *win_old; Word16 *out_ptr; Word16 half_dct_size; Word32 sum; half_dct_size = shr_nocheck(dct_length,1); /* Perform a Type IV (inverse) DCT on the coefficients */ dct_type_iv_s(coefs, new_samples, dct_length); test(); if (mag_shift > 0) { for(index=0;index 0; vals_left--) { sum = 0L; move32(); sum = L_mac(sum,*win_new++, *--new_ptr); sum = L_mac(sum,*--win_old, *old_ptr++); *out_ptr++ = itu_round(L_shl_nocheck(sum,2)); move16(); } /* Get the second half of the windowed samples */ for (vals_left = half_dct_size; vals_left > 0; vals_left--) { sum = 0L; move32(); sum = L_mac(sum,*win_new++, *new_ptr++); sum = L_mac(sum,negate(*--win_old), *--old_ptr); *out_ptr++ = itu_round(L_shl_nocheck(sum,2)); move16(); } /* Save the second half of the new samples for */ /* next time, when they will be the old samples. */ /* pointer arithmetic */ new_ptr = new_samples + half_dct_size; move16(); old_ptr = old_samples; move16(); for (vals_left = half_dct_size; vals_left > 0; vals_left--) { *old_ptr++ = *new_ptr++; move16(); } } ================================================ FILE: deps/pjsip/third_party/g7221/decode/dct4_s.c ================================================ /******************************************************************************** ** ** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C ** > Software Release 2.1 (2008-06) ** (Simple repackaging; no change from 2005-05 Release 2.0 code) ** ** 2004 Polycom, Inc. ** ** All rights reserved. ** ********************************************************************************/ /******************************************************************************** * Filename: dct_type_iv_s.c * * Purpose: Discrete Cosine Transform, Type IV used for inverse MLT * * The basis functions are * * cos(PI*(t+0.5)*(k+0.5)/block_length) * * for time t and basis function number k. Due to the symmetry of the expression * in t and k, it is clear that the forward and inverse transforms are the same. * *********************************************************************************/ /*************************************************************************** Include files ***************************************************************************/ #include "defs.h" #include "count.h" #include "dct4_s.h" /*************************************************************************** External variable declarations ***************************************************************************/ extern Word16 syn_bias_7khz[DCT_LENGTH]; extern Word16 dither[DCT_LENGTH]; extern Word16 max_dither[MAX_DCT_LENGTH]; extern Word16 dct_core_s[DCT_LENGTH_DIV_32][DCT_LENGTH_DIV_32]; extern cos_msin_t s_cos_msin_2[DCT_LENGTH_DIV_32]; extern cos_msin_t s_cos_msin_4[DCT_LENGTH_DIV_16]; extern cos_msin_t s_cos_msin_8[DCT_LENGTH_DIV_8]; extern cos_msin_t s_cos_msin_16[DCT_LENGTH_DIV_4]; extern cos_msin_t s_cos_msin_32[DCT_LENGTH_DIV_2]; extern cos_msin_t s_cos_msin_64[DCT_LENGTH]; extern cos_msin_t *s_cos_msin_table[]; /******************************************************************************** Function: dct_type_iv_s Syntax: void dct_type_iv_s (Word16 *input,Word16 *output,Word16 dct_length) Description: Discrete Cosine Transform, Type IV used for inverse MLT Design Notes: WMOPS: 7kHz | 24kbit | 32kbit -------|--------------|---------------- AVG | 1.74 | 1.74 -------|--------------|---------------- MAX | 1.74 | 1.74 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 3.62 | 3.62 | 3.62 -------|--------------|----------------|---------------- MAX | 3.62 | 3.62 | 3.62 -------|--------------|----------------|---------------- ********************************************************************************/ void dct_type_iv_s (Word16 *input,Word16 *output,Word16 dct_length) { Word16 buffer_a[MAX_DCT_LENGTH], buffer_b[MAX_DCT_LENGTH], buffer_c[MAX_DCT_LENGTH]; Word16 *in_ptr, *in_ptr_low, *in_ptr_high, *next_in_base; Word16 *out_ptr_low, *out_ptr_high, *next_out_base; Word16 *out_buffer, *in_buffer, *buffer_swap; Word16 in_val_low, in_val_high; Word16 out_val_low, out_val_high; Word16 in_low_even, in_low_odd; Word16 in_high_even, in_high_odd; Word16 out_low_even, out_low_odd; Word16 out_high_even, out_high_odd; Word16 *pair_ptr; Word16 cos_even, cos_odd, msin_even, msin_odd; Word16 set_span, set_count, set_count_log, pairs_left, sets_left; Word16 i,k; Word16 index; Word16 dummy; Word32 sum; cos_msin_t **table_ptr_ptr, *cos_msin_ptr; Word32 acca; Word16 temp; Word16 dct_length_log; Word16 *dither_ptr; /*++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ /* Do the sum/difference butterflies, the first part of */ /* converting one N-point transform into 32 - 10 point transforms */ /* transforms, where N = 1 << DCT_LENGTH_LOG. */ /*++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ test(); if (dct_length==DCT_LENGTH) { dct_length_log = DCT_LENGTH_LOG; move16(); dither_ptr = dither; move16(); } else { dct_length_log = MAX_DCT_LENGTH_LOG; move16(); dither_ptr = max_dither; move16(); } in_buffer = input; move16(); out_buffer = buffer_a; move16(); index=0; move16(); i=0; move16(); for (set_count_log = 0; set_count_log <= dct_length_log - 2; set_count_log++) { /*===========================================================*/ /* Initialization for the loop over sets at the current size */ /*===========================================================*/ /* set_span = 1 << (DCT_LENGTH_LOG - set_count_log); */ set_span = shr_nocheck(dct_length,set_count_log); set_count = shl_nocheck(1,set_count_log); in_ptr = in_buffer; move16(); next_out_base = out_buffer; move16(); /*=====================================*/ /* Loop over all the sets of this size */ /*=====================================*/ temp = sub(index,1); test(); if(temp < 0) { for (sets_left = set_count;sets_left > 0;sets_left--) { /*||||||||||||||||||||||||||||||||||||||||||||*/ /* Set up output pointers for the current set */ /*||||||||||||||||||||||||||||||||||||||||||||*/ /* pointer arithmetic */ out_ptr_low = next_out_base; move16(); next_out_base += set_span; move16(); out_ptr_high = next_out_base; move16(); /*||||||||||||||||||||||||||||||||||||||||||||||||||*/ /* Loop over all the butterflies in the current set */ /*||||||||||||||||||||||||||||||||||||||||||||||||||*/ do { in_val_low = *in_ptr++; move16(); in_val_high = *in_ptr++; move16(); /* BEST METHOD OF GETTING RID OF BIAS, BUT COMPUTATIONALLY UNPLEASANT */ /* ALTERNATIVE METHOD, SMEARS BIAS OVER THE ENTIRE FRAME, COMPUTATIONALLY SIMPLEST. */ /* IF THIS WORKS, IT'S PREFERABLE */ dummy = add(in_val_low,dither_ptr[i++]); // blp: addition of two 16bits vars, there's no way // they'll overflow a 32bit var //acca = L_add(dummy,in_val_high); acca = dummy + in_val_high; out_val_low = extract_l(L_shr_nocheck(acca,1)); dummy = add(in_val_low,dither_ptr[i++]); // blp: addition of two 16bits vars, there's no way // they'll overflow a 32bit var //acca = L_add(dummy,-in_val_high); acca = dummy - in_val_high; out_val_high = extract_l(L_shr_nocheck(acca,1)); *out_ptr_low++ = out_val_low; move16(); *--out_ptr_high = out_val_high; move16(); test(); /* this involves comparison of pointers */ /* pointer arithmetic */ } while (out_ptr_low < out_ptr_high); } /* End of loop over sets of the current size */ } else { for (sets_left = set_count; sets_left > 0; sets_left--) { /*||||||||||||||||||||||||||||||||||||||||||||*/ /* Set up output pointers for the current set */ /*||||||||||||||||||||||||||||||||||||||||||||*/ out_ptr_low = next_out_base; move16(); next_out_base += set_span; move16(); out_ptr_high = next_out_base; move16(); /*||||||||||||||||||||||||||||||||||||||||||||||||||*/ /* Loop over all the butterflies in the current set */ /*||||||||||||||||||||||||||||||||||||||||||||||||||*/ do { in_val_low = *in_ptr++; move16(); in_val_high = *in_ptr++; move16(); out_val_low = add(in_val_low,in_val_high); out_val_high = add(in_val_low,negate(in_val_high)); *out_ptr_low++ = out_val_low; move16(); *--out_ptr_high = out_val_high; move16(); test(); } while (out_ptr_low < out_ptr_high); } /* End of loop over sets of the current size */ } /*============================================================*/ /* Decide which buffers to use as input and output next time. */ /* Except for the first time (when the input buffer is the */ /* subroutine input) we just alternate the local buffers. */ /*============================================================*/ in_buffer = out_buffer; move16(); test(); if (out_buffer == buffer_a) { out_buffer = buffer_b; move16(); } else { out_buffer = buffer_a; move16(); } index = add(index,1); } /* End of loop over set sizes */ /*++++++++++++++++++++++++++++++++*/ /* Do 32 - 10 point transforms */ /*++++++++++++++++++++++++++++++++*/ pair_ptr = in_buffer; move16(); buffer_swap = buffer_c; move16(); for (pairs_left = 1 << (dct_length_log - 1); pairs_left > 0; pairs_left--) { for ( k=0; k= 0; set_count_log--) { /*===========================================================*/ /* Initialization for the loop over sets at the current size */ /*===========================================================*/ /* set_span = 1 << (DCT_LENGTH_LOG - set_count_log); */ set_span = shr_nocheck(dct_length,set_count_log); set_count = shl_nocheck(1,set_count_log); next_in_base = in_buffer; move16(); test(); if (set_count_log == 0) { next_out_base = output; move16(); } else { next_out_base = out_buffer; move16(); } /*=====================================*/ /* Loop over all the sets of this size */ /*=====================================*/ for (sets_left = set_count; sets_left > 0; sets_left--) { /*|||||||||||||||||||||||||||||||||||||||||*/ /* Set up the pointers for the current set */ /*|||||||||||||||||||||||||||||||||||||||||*/ in_ptr_low = next_in_base; move16(); temp = shr_nocheck(set_span,1); in_ptr_high = in_ptr_low + temp; move16(); next_in_base += set_span; move16(); out_ptr_low = next_out_base; move16(); next_out_base += set_span; move16(); out_ptr_high = next_out_base; move16(); cos_msin_ptr = *table_ptr_ptr; move16(); /*||||||||||||||||||||||||||||||||||||||||||||||||||||||*/ /* Loop over all the butterfly pairs in the current set */ /*||||||||||||||||||||||||||||||||||||||||||||||||||||||*/ do { in_low_even = *in_ptr_low++; move16(); in_low_odd = *in_ptr_low++; move16(); in_high_even = *in_ptr_high++; move16(); in_high_odd = *in_ptr_high++; move16(); cos_even = cos_msin_ptr[0].cosine; move16(); msin_even = cos_msin_ptr[0].minus_sine; move16(); cos_odd = cos_msin_ptr[1].cosine; move16(); msin_odd = cos_msin_ptr[1].minus_sine; move16(); cos_msin_ptr += 2; sum = 0L; move32(); sum = L_mac(sum,cos_even,in_low_even); sum = L_mac(sum,negate(msin_even),in_high_even); out_low_even = itu_round(L_shl_nocheck(sum,1)); sum = 0L; move32(); sum = L_mac(sum,msin_even,in_low_even); sum = L_mac(sum,cos_even,in_high_even); out_high_even = itu_round(L_shl_nocheck(sum,1)); sum = 0L; move32(); sum = L_mac(sum,cos_odd,in_low_odd); sum = L_mac(sum,msin_odd,in_high_odd); out_low_odd = itu_round(L_shl_nocheck(sum,1)); sum = 0L; move32(); sum = L_mac(sum,msin_odd,in_low_odd); sum = L_mac(sum,negate(cos_odd),in_high_odd); out_high_odd = itu_round(L_shl_nocheck(sum,1)); *out_ptr_low++ = out_low_even; move16(); *--out_ptr_high = out_high_even; move16(); *out_ptr_low++ = out_low_odd; move16(); *--out_ptr_high = out_high_odd; move16(); test(); } while (out_ptr_low < out_ptr_high); } /* End of loop over sets of the current size */ /*=============================================*/ /* Swap input and output buffers for next time */ /*=============================================*/ buffer_swap = in_buffer; move16(); in_buffer = out_buffer; move16(); out_buffer = buffer_swap; move16(); index = add(index,1); table_ptr_ptr++; } /*------------------------------------ ADD IN BIAS FOR OUTPUT -----------------------------------*/ if (dct_length==DCT_LENGTH) { for(i=0;i<320;i++) { // blp: addition of two 16bits vars, there's no way // they'll overflow a 32bit var //sum = L_add(output[i],syn_bias_7khz[i]); sum = output[i] + syn_bias_7khz[i]; acca = L_sub(sum,32767); test(); if (acca > 0) { sum = 32767L; move32(); } // blp: addition of two 16bits vars, there's no way // they'll overflow 32bit var //acca = L_add(sum,32768L); acca = sum + 32768; test(); if (acca < 0) { sum = -32768L; move32(); } output[i] = extract_l(sum); } } } ================================================ FILE: deps/pjsip/third_party/g7221/decode/dct4_s.h ================================================ /*********************************************************************** ** ** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C ** > Software Release 2.1 (2008-06) ** (Simple repackaging; no change from 2005-05 Release 2.0 code) ** ** 2004 Polycom, Inc. ** ** All rights reserved. ** ***********************************************************************/ /*********************************************************************** Filename: dct4_s.h Purpose: Contains tables used by dct4_s.c Design Notes: ***********************************************************************/ /*************************************************************************** Include files ***************************************************************************/ typedef struct { Word16 cosine; Word16 minus_sine; } cos_msin_t; /*************************************************************************** The dct_core_s table was generated by the following code for(i=0;i<10;++i) { for(k=0;k<10;++k) { dct_core_s[i][k]=(short) (FTOI(((.9*32768.)*cos(3.1415926*(k+0.5)*(i+0.5)/10.)))); } } ***************************************************************************/ Word16 dct_core_s[10][10] = { { 29400, 28676, 27246, 25145, 22425, 19153, 15409, 11286, 6885, 2314 }, { 28676, 22425, 11286, -2314, -15409, -25145, -29400, -27246, -19153, -6885 }, { 27246, 11286, -11286, -27246, -27246, -11286, 11286, 27246, 27246, 11286 }, { 25145, -2314, -27246, -22425, 6885, 28676, 19153, -11286, -29400, -15409 }, { 22425, -15409, -27246, 6885, 29400, 2314, -28676, -11286, 25145, 19153 }, { 19153, -25145, -11286, 28676, 2314, -29400, 6885, 27246, -15409, -22425 }, { 15409, -29400, 11286, 19153, -28676, 6885, 22425, -27246, 2314, 25145 }, { 11286, -27246, 27246, -11286, -11286, 27246, -27246, 11286, 11286, -27246 }, { 6885, -19153, 27246, -29400, 25145, -15409, 2314, 11286, -22425, 28676 }, { 2314, -6885, 11286, -15409, 19153, -22425, 25145, -27246, 28676, -29400 } }; Word16 syn_bias_7khz[DCT_LENGTH] = { -4, 4, -5, -2, 0, -4, 6, 2, -2, -4, -3, 3, 0, 0, -2, 4, 0, 0, 3, -6, 8, 5, 4, 5, -8, 0, -2, 0, 0, -3, 3, 0, 0, 0, 1, -1, -2, 0, 0, 2, -2, -5, -2, 3, 2, -1, -1, -6, 3, 1, -7, 4, 4, 0, 1, 4, 1, 0, 1, -5, -1, 1, -6, 0, -1, -1, 3, 0, -2, 1, 2, -4, 0, 9, 0, -3, 1, 1, 1, 0, -3, -2, -1, -4, -2, 0, 5, 2, -3, 5, 0, -2, 4, 4, 0, -6, -4, 2, 0, 0, 0, -1, -1, -2, 0, 6, 1, 0, 0, -1, 0, -4, -1, 0, -4, 1, -1, -5, 0, 1, 2, 4, 0, -8, -4, 0, -2, -2, 2, 5, -3, -1, 1, -4, 0, 0, 0, -1, -3, 0, -5, -4, 0, -2, 0, 7, 1, 0, 5, -2, -1, 2, 2, -2, 3, 7, -3, 4, 1, -4, 0, 0, 3, -7, -5, 0, 0, 4, 0, -2, -1, 0, -5, 0, 2, 0, 11, 5, -1, 0, 2, 2, -2, -2, 5, 4, -3, 1, 0, -2, 1, 3, 2, 0, 1, 0, 0, 0, 5, 6, -2, -1, 0, 2, 3, 2, 0, -3, 4, 5, 0, -1, 0, 3, 1, -2, -3, -2, -1, 2, -1, -1, -2, -7, 4, 6, -5, -6, -3, -4, 0, 2, -5, -2, 3, 0, 0, 0, 2, -2, -4, 3, 3, 1, 0, 0, 4, -1, 8, 13, 1, 2, 0, 2, 0, -1, 4, -3, 1, 0, -1, 3, 0, 0, -5, 0, 6, 2, 4, 5, 2, -1, -1, 3, 6, 1, 1, 2, -4, 0, -1, -6, -2, -2, 2, 1, 2, 6, 2, 0, -2, -2, 0, -1, 2, 0, 0, 3, -2, 1, 3, 1, 2, -1, -2, 2, 2, -4, 0, 0, -3, 0, -4, -3, 6, 7, 2, 2, 0, -3}; Word16 dither[DCT_LENGTH]= { 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0}; Word16 max_dither[MAX_DCT_LENGTH]= { 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 }; /******************************************************************************** The s_cos_min tables were generated by the following code: double angle, scale; int index; for (index = 0;index < length;index++) { angle = scale * ((double)index + 0.5); table[index].cosine = (short) (FTOI((18427)* cos(angle))); table[index].minus_sine = (short) (FTOI((18427)*(-sin(angle)))); } ********************************************************************************/ cos_msin_t s_cos_msin_2[DCT_LENGTH_DIV_32] = { { 18413 , -723 } , { 18299 , -2166 } , { 18073 , -3595 } , { 17735 , -5002 } , { 17288 , -6378 } , { 16734 , -7715 } , { 16077 , -9004 } , { 15321 , -10237 } , { 14471 , -11408 } , { 13531 , -12508 } }; cos_msin_t s_cos_msin_4[DCT_LENGTH_DIV_16] = { { 18423 , -362 } , { 18395 , -1085 } , { 18338 , -1806 } , { 18253 , -2525 } , { 18140 , -3239 } , { 17999 , -3949 } , { 17830 , -4653 } , { 17634 , -5349 } , { 17410 , -6037 } , { 17159 , -6716 } , { 16883 , -7385 } , { 16580 , -8042 } , { 16251 , -8686 } , { 15898 , -9318 } , { 15520 , -9935 } , { 15118 , -10536 } , { 14692 , -11122 } , { 14244 , -11690 } , { 13774 , -12240 } , { 13283 , -12772 } }; cos_msin_t s_cos_msin_8[DCT_LENGTH_DIV_8] = { { 18426 , -181 } , { 18419 , -543 } , { 18405 , -904 } , { 18384 , -1265 } , { 18355 , -1626 } , { 18320 , -1986 } , { 18277 , -2345 } , { 18228 , -2704 } , { 18171 , -3061 } , { 18107 , -3417 } , { 18037 , -3772 } , { 17959 , -4126 } , { 17875 , -4477 } , { 17783 , -4827 } , { 17685 , -5176 } , { 17580 , -5522 } , { 17468 , -5866 } , { 17350 , -6208 } , { 17225 , -6547 } , { 17093 , -6884 } , { 16954 , -7219 } , { 16809 , -7550 } , { 16658 , -7879 } , { 16500 , -8204 } , { 16336 , -8526 } , { 16165 , -8846 } , { 15988 , -9161 } , { 15805 , -9473 } , { 15616 , -9782 } , { 15421 , -10087 } , { 15220 , -10387 } , { 15013 , -10684 } , { 14801 , -10977 } , { 14582 , -11265 } , { 14358 , -11550 } , { 14129 , -11829 } , { 13894 , -12104 } , { 13654 , -12375 } , { 13408 , -12641 } , { 13157 , -12901 } }; cos_msin_t s_cos_msin_16[DCT_LENGTH_DIV_4] = { { 18427 , -90 } , { 18425 , -271 } , { 18421 , -452 } , { 18416 , -633 } , { 18409 , -814 } , { 18400 , -995 } , { 18389 , -1175 } , { 18377 , -1356 } , { 18363 , -1536 } , { 18347 , -1716 } , { 18329 , -1896 } , { 18310 , -2076 } , { 18288 , -2256 } , { 18265 , -2435 } , { 18241 , -2614 } , { 18214 , -2793 } , { 18186 , -2972 } , { 18156 , -3150 } , { 18124 , -3328 } , { 18090 , -3506 } , { 18055 , -3684 } , { 18018 , -3861 } , { 17979 , -4037 } , { 17939 , -4214 } , { 17897 , -4390 } , { 17853 , -4565 } , { 17807 , -4740 } , { 17760 , -4915 } , { 17710 , -5089 } , { 17660 , -5262 } , { 17607 , -5436 } , { 17553 , -5608 } , { 17497 , -5780 } , { 17439 , -5952 } , { 17380 , -6123 } , { 17319 , -6293 } , { 17257 , -6463 } , { 17192 , -6632 } , { 17126 , -6800 } , { 17059 , -6968 } , { 16990 , -7135 } , { 16919 , -7302 } , { 16846 , -7467 } , { 16772 , -7632 } , { 16696 , -7797 } , { 16619 , -7960 } , { 16540 , -8123 } , { 16459 , -8285 } , { 16377 , -8446 } , { 16294 , -8607 } , { 16208 , -8766 } , { 16121 , -8925 } , { 16033 , -9083 } , { 15943 , -9240 } , { 15852 , -9396 } , { 15759 , -9551 } , { 15664 , -9705 } , { 15568 , -9858 } , { 15471 , -10011 } , { 15372 , -10162 } , { 15271 , -10313 } , { 15169 , -10462 } , { 15066 , -10610 } , { 14961 , -10758 } , { 14854 , -10904 } , { 14747 , -11049 } , { 14637 , -11194 } , { 14527 , -11337 } , { 14415 , -11479 } , { 14301 , -11620 } , { 14187 , -11760 } , { 14071 , -11898 } , { 13953 , -12036 } , { 13834 , -12172 } , { 13714 , -12308 } , { 13593 , -12442 } , { 13470 , -12575 } , { 13346 , -12706 } , { 13220 , -12837 } , { 13094 , -12966 } }; cos_msin_t s_cos_msin_32[DCT_LENGTH_DIV_2] = { { 18427 , -45 } , { 18427 , -136 } , { 18426 , -226 } , { 18424 , -317 } , { 18423 , -407 } , { 18420 , -497 } , { 18418 , -588 } , { 18415 , -678 } , { 18411 , -769 } , { 18407 , -859 } , { 18403 , -949 } , { 18398 , -1040 } , { 18392 , -1130 } , { 18387 , -1220 } , { 18380 , -1310 } , { 18374 , -1401 } , { 18367 , -1491 } , { 18359 , -1581 } , { 18351 , -1671 } , { 18343 , -1761 } , { 18334 , -1851 } , { 18324 , -1941 } , { 18315 , -2031 } , { 18305 , -2121 } , { 18294 , -2211 } , { 18283 , -2301 } , { 18271 , -2390 } , { 18259 , -2480 } , { 18247 , -2570 } , { 18234 , -2659 } , { 18221 , -2749 } , { 18207 , -2838 } , { 18193 , -2927 } , { 18178 , -3017 } , { 18163 , -3106 } , { 18148 , -3195 } , { 18132 , -3284 } , { 18116 , -3373 } , { 18099 , -3462 } , { 18082 , -3551 } , { 18064 , -3639 } , { 18046 , -3728 } , { 18027 , -3816 } , { 18009 , -3905 } , { 17989 , -3993 } , { 17969 , -4081 } , { 17949 , -4170 } , { 17928 , -4258 } , { 17907 , -4346 } , { 17886 , -4434 } , { 17864 , -4521 } , { 17841 , -4609 } , { 17818 , -4696 } , { 17795 , -4784 } , { 17772 , -4871 } , { 17747 , -4958 } , { 17723 , -5045 } , { 17698 , -5132 } , { 17672 , -5219 } , { 17647 , -5306 } , { 17620 , -5392 } , { 17594 , -5479 } , { 17567 , -5565 } , { 17539 , -5651 } , { 17511 , -5737 } , { 17483 , -5823 } , { 17454 , -5909 } , { 17425 , -5994 } , { 17395 , -6080 } , { 17365 , -6165 } , { 17335 , -6250 } , { 17304 , -6335 } , { 17272 , -6420 } , { 17241 , -6505 } , { 17208 , -6590 } , { 17176 , -6674 } , { 17143 , -6758 } , { 17110 , -6842 } , { 17076 , -6926 } , { 17042 , -7010 } , { 17007 , -7093 } , { 16972 , -7177 } , { 16937 , -7260 } , { 16901 , -7343 } , { 16864 , -7426 } , { 16828 , -7509 } , { 16791 , -7591 } , { 16753 , -7674 } , { 16715 , -7756 } , { 16677 , -7838 } , { 16638 , -7919 } , { 16599 , -8001 } , { 16560 , -8082 } , { 16520 , -8164 } , { 16480 , -8245 } , { 16439 , -8325 } , { 16398 , -8406 } , { 16357 , -8486 } , { 16315 , -8567 } , { 16272 , -8647 } , { 16230 , -8726 } , { 16187 , -8806 } , { 16143 , -8885 } , { 16100 , -8964 } , { 16055 , -9043 } , { 16011 , -9122 } , { 15966 , -9200 } , { 15920 , -9279 } , { 15875 , -9357 } , { 15829 , -9435 } , { 15782 , -9512 } , { 15735 , -9589 } , { 15688 , -9667 } , { 15640 , -9744 } , { 15592 , -9820 } , { 15544 , -9897 } , { 15495 , -9973 } , { 15446 , -10049 } , { 15396 , -10124 } , { 15347 , -10200 } , { 15296 , -10275 } , { 15246 , -10350 } , { 15195 , -10425 } , { 15143 , -10499 } , { 15092 , -10573 } , { 15040 , -10647 } , { 14987 , -10721 } , { 14934 , -10794 } , { 14881 , -10868 } , { 14828 , -10941 } , { 14774 , -11013 } , { 14719 , -11086 } , { 14665 , -11158 } , { 14610 , -11230 } , { 14555 , -11301 } , { 14499 , -11372 } , { 14443 , -11444 } , { 14387 , -11514 } , { 14330 , -11585 } , { 14273 , -11655 } , { 14216 , -11725 } , { 14158 , -11795 } , { 14100 , -11864 } , { 14041 , -11933 } , { 13983 , -12002 } , { 13924 , -12070 } , { 13864 , -12138 } , { 13804 , -12206 } , { 13744 , -12274 } , { 13684 , -12341 } , { 13623 , -12408 } , { 13562 , -12475 } , { 13501 , -12541 } , { 13439 , -12608 } , { 13377 , -12673 } , { 13314 , -12739 } , { 13252 , -12804 } , { 13189 , -12869 } , { 13125 , -12934 } , { 13062 , -12998 } }; cos_msin_t s_cos_msin_64[DCT_LENGTH] = { {18426, -21}, {18426, -66}, {18426, -110}, {18426, -154}, {18425, -198}, {18425, -242}, {18424, -286}, {18424, -331}, {18423, -374}, {18421, -419}, {18421, -463}, {18419, -507}, {18418, -552}, {18417, -595}, {18415, -639}, {18414, -684}, {18412, -728}, {18410, -772}, {18408, -816}, {18406, -860}, {18404, -904}, {18402, -949}, {18400, -992}, {18397, -1037}, {18394, -1081}, {18392, -1125}, {18389, -1169}, {18387, -1213}, {18384, -1257}, {18380, -1301}, {18378, -1345}, {18374, -1389}, {18371, -1433}, {18367, -1477}, {18364, -1521}, {18360, -1566}, {18356, -1609}, {18352, -1653}, {18348, -1697}, {18344, -1742}, {18339, -1785}, {18335, -1829}, {18331, -1873}, {18326, -1917}, {18322, -1961}, {18317, -2005}, {18312, -2049}, {18307, -2092}, {18302, -2137}, {18297, -2180}, {18292, -2224}, {18286, -2268}, {18281, -2312}, {18275, -2356}, {18270, -2399}, {18264, -2443}, {18258, -2487}, {18252, -2531}, {18246, -2574}, {18240, -2618}, {18233, -2662}, {18227, -2706}, {18220, -2749}, {18214, -2793}, {18207, -2836}, {18200, -2880}, {18193, -2924}, {18186, -2967}, {18179, -3011}, {18172, -3055}, {18164, -3098}, {18157, -3142}, {18149, -3185}, {18141, -3229}, {18134, -3272}, {18126, -3316}, {18118, -3359}, {18109, -3403}, {18101, -3446}, {18094, -3489}, {18085, -3533}, {18076, -3576}, {18068, -3619}, {18059, -3663}, {18050, -3706}, {18041, -3749}, {18032, -3792}, {18023, -3836}, {18014, -3879}, {18005, -3922}, {17995, -3965}, {17986, -4008}, {17975, -4051}, {17966, -4094}, {17956, -4138}, {17946, -4180}, {17936, -4224}, {17926, -4266}, {17916, -4309}, {17905, -4353}, {17895, -4395}, {17884, -4438}, {17874, -4481}, {17863, -4524}, {17852, -4567}, {17841, -4609}, {17830, -4652}, {17819, -4695}, {17807, -4738}, {17796, -4780}, {17784, -4823}, {17772, -4865}, {17761, -4908}, {17749, -4951}, {17738, -4993}, {17725, -5036}, {17713, -5078}, {17701, -5121}, {17689, -5163}, {17676, -5205}, {17664, -5248}, {17651, -5290}, {17638, -5333}, {17626, -5375}, {17613, -5417}, {17599, -5459}, {17586, -5501}, {17573, -5544}, {17560, -5586}, {17546, -5627}, {17533, -5670}, {17519, -5712}, {17505, -5753}, {17492, -5795}, {17478, -5837}, {17464, -5879}, {17450, -5921}, {17435, -5963}, {17421, -6005}, {17406, -6046}, {17392, -6088}, {17377, -6130}, {17363, -6172}, {17348, -6213}, {17333, -6254}, {17318, -6296}, {17303, -6338}, {17288, -6379}, {17272, -6420}, {17257, -6462}, {17241, -6503}, {17225, -6545}, {17210, -6586}, {17194, -6627}, {17178, -6668}, {17162, -6709}, {17145, -6750}, {17130, -6791}, {17113, -6832}, {17097, -6874}, {17080, -6915}, {17064, -6956}, {17047, -6996}, {17030, -7037}, {17013, -7078}, {16996, -7119}, {16979, -7159}, {16962, -7200}, {16945, -7241}, {16927, -7281}, {16910, -7322}, {16892, -7362}, {16874, -7403}, {16856, -7444}, {16838, -7484}, {16821, -7524}, {16802, -7564}, {16784, -7605}, {16766, -7645}, {16748, -7685}, {16729, -7725}, {16711, -7765}, {16692, -7805}, {16674, -7845}, {16654, -7885}, {16635, -7925}, {16616, -7964}, {16597, -8004}, {16578, -8044}, {16559, -8084}, {16539, -8124}, {16520, -8164}, {16500, -8203}, {16480, -8242}, {16461, -8282}, {16441, -8322}, {16421, -8361}, {16401, -8400}, {16380, -8440}, {16360, -8479}, {16340, -8518}, {16319, -8557}, {16299, -8597}, {16278, -8635}, {16257, -8674}, {16237, -8713}, {16215, -8752}, {16195, -8791}, {16173, -8829}, {16152, -8868}, {16131, -8907}, {16110, -8946}, {16088, -8985}, {16067, -9023}, {16045, -9061}, {16023, -9100}, {16001, -9138}, {15979, -9176}, {15957, -9215}, {15935, -9253}, {15913, -9291}, {15891, -9329}, {15868, -9367}, {15846, -9405}, {15823, -9443}, {15800, -9481}, {15778, -9519}, {15755, -9557}, {15732, -9595}, {15709, -9632}, {15686, -9670}, {15662, -9708}, {15639, -9745}, {15615, -9782}, {15592, -9820}, {15569, -9857}, {15544, -9894}, {15521, -9932}, {15497, -9969}, {15473, -10006}, {15449, -10043}, {15425, -10080}, {15401, -10117}, {15377, -10154}, {15352, -10191}, {15327, -10227}, {15303, -10264}, {15278, -10301}, {15254, -10337}, {15229, -10374}, {15204, -10411}, {15180, -10447}, {15154, -10483}, {15129, -10519}, {15104, -10556}, {15078, -10592}, {15053, -10628}, {15027, -10664}, {15002, -10700}, {14976, -10736}, {14950, -10772}, {14924, -10808}, {14898, -10844}, {14872, -10879}, {14846, -10915}, {14820, -10950}, {14794, -10985}, {14767, -11021}, {14741, -11056}, {14714, -11092}, {14687, -11127}, {14661, -11162}, {14635, -11197}, {14607, -11232}, {14581, -11267}, {14554, -11302}, {14526, -11337}, {14499, -11372}, {14472, -11407}, {14444, -11441}, {14417, -11476}, {14389, -11511}, {14362, -11545}, {14334, -11579}, {14306, -11614}, {14278, -11648}, {14251, -11682}, {14222, -11716}, {14194, -11750}, {14166, -11784}, {14137, -11818}, {14109, -11852}, {14081, -11886}, {14053, -11919}, {14023, -11953}, {13995, -11987}, {13966, -12020}, {13937, -12054}, {13909, -12087}, {13879, -12120}, {13851, -12153}, {13821, -12187}, {13792, -12220}, {13763, -12253}, {13733, -12286}, {13704, -12319}, {13674, -12351}, {13645, -12385}, {13615, -12417}, {13585, -12450}, {13555, -12482}, {13525, -12514}, {13495, -12546}, {13465, -12579}, {13435, -12611}, {13405, -12644}, {13374, -12676}, {13345, -12708}, {13314, -12739}, {13283, -12772} }; cos_msin_t *s_cos_msin_table[] = {s_cos_msin_2, s_cos_msin_4, s_cos_msin_8, s_cos_msin_16, s_cos_msin_32, s_cos_msin_64 }; ================================================ FILE: deps/pjsip/third_party/g7221/decode/decoder.c ================================================ /*************************************************************************** ** ** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C ** > Software Release 2.1 (2008-06) ** (Simple repackaging; no change from 2005-05 Release 2.0 code) ** ** 2004 Polycom, Inc. ** ** All rights reserved. ** ***************************************************************************/ /*************************************************************************** Filename: decoder.c Purpose: Contains files used to implement the G.722.1 Annex C decoder Design Notes: ***************************************************************************/ /*************************************************************************** Include files ***************************************************************************/ #include "defs.h" #include "tables.h" #include "huff_def.h" #include "count.h" /*************************************************************************** Function: decoder Syntax: void decoder(Bit_Obj *bitobj, Rand_Obj *randobj, Word16 number_of_regions, Word16 *decoder_mlt_coefs, Word16 *p_mag_shift, Word16 *p_old_mag_shift, Word16 *old_decoder_mlt_coefs, Word16 frame_error_flag) inputs: Bit_Obj *bitobj Rand_Obj *randobj Word16 number_of_regions Word16 *p_old_mag_shift Word16 *old_decoder_mlt_coefs Word16 frame_error_flag outputs: Word16 *decoder_mlt_coefs, Word16 *p_mag_shift, Description: Decodes the out_words into mlt coefs using G.722.1 Annex C Design Notes: WMOPS: 7kHz | 24kbit | 32kbit -------|-------------|---------------- AVG | 0.84 | 0.94 -------|-------------|---------------- MAX | 0.90 | 1.00 -------|-------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|-------------|----------------|---------------- AVG | 1.31 | 1.56 | 1.88 -------|-------------|----------------|---------------- MAX | 1.59 | 1.80 | 1.98 -------|-------------|----------------|---------------- ***************************************************************************/ void decoder(Bit_Obj *bitobj, Rand_Obj *randobj, Word16 number_of_regions, Word16 *decoder_mlt_coefs, Word16 *p_mag_shift, Word16 *p_old_mag_shift, Word16 *old_decoder_mlt_coefs, Word16 frame_error_flag) { Word16 absolute_region_power_index[MAX_NUMBER_OF_REGIONS]; Word16 decoder_power_categories[MAX_NUMBER_OF_REGIONS]; Word16 decoder_category_balances[MAX_NUM_CATEGORIZATION_CONTROL_POSSIBILITIES-1]; UWord16 categorization_control; Word16 decoder_region_standard_deviation[MAX_NUMBER_OF_REGIONS]; Word16 i; Word16 num_categorization_control_bits; Word16 num_categorization_control_possibilities; Word16 number_of_coefs; Word16 number_of_valid_coefs; test(); if (number_of_regions==NUMBER_OF_REGIONS) { num_categorization_control_bits = NUM_CATEGORIZATION_CONTROL_BITS; move16(); num_categorization_control_possibilities = NUM_CATEGORIZATION_CONTROL_POSSIBILITIES; move16(); number_of_coefs = DCT_LENGTH; move16(); number_of_valid_coefs = NUMBER_OF_VALID_COEFS; move16(); } else { num_categorization_control_bits = MAX_NUM_CATEGORIZATION_CONTROL_BITS; move16(); num_categorization_control_possibilities = MAX_NUM_CATEGORIZATION_CONTROL_POSSIBILITIES; move16(); number_of_coefs = MAX_DCT_LENGTH; move16(); number_of_valid_coefs = MAX_NUMBER_OF_VALID_COEFS; move16(); } test(); if (frame_error_flag == 0) { /* convert the bits to absolute region power index and decoder_region_standard_deviation */ decode_envelope(bitobj, number_of_regions, decoder_region_standard_deviation, absolute_region_power_index, p_mag_shift); /* fill the categorization_control with NUM_CATEGORIZATION_CONTROL_BITS */ categorization_control = 0; for (i=0; inext_bit); } bitobj->number_of_bits_left = sub(bitobj->number_of_bits_left,num_categorization_control_bits); /* obtain decoder power categories and category balances */ /* based on the absolute region power index */ categorize(bitobj->number_of_bits_left, number_of_regions, num_categorization_control_possibilities, absolute_region_power_index, decoder_power_categories, decoder_category_balances); /* perform adjustmaents to the power categories and category balances based on the cat control */ rate_adjust_categories(categorization_control, decoder_power_categories, decoder_category_balances); /* decode the quantized bits into mlt coefs */ decode_vector_quantized_mlt_indices(bitobj, randobj, number_of_regions, decoder_region_standard_deviation, decoder_power_categories, decoder_mlt_coefs); /* test for frame errors */ test_4_frame_errors(bitobj, number_of_regions, num_categorization_control_possibilities, &frame_error_flag, categorization_control, absolute_region_power_index); } /* perform error handling operations */ error_handling(number_of_coefs, number_of_valid_coefs, &frame_error_flag, decoder_mlt_coefs, old_decoder_mlt_coefs, p_mag_shift, p_old_mag_shift); } /*************************************************************************** Function: decode_envelope Syntax: void decode_envelope(Bit_Obj *bitobj, Word16 number_of_regions, Word16 *decoder_region_standard_deviation, Word16 *absolute_region_power_index, Word16 *p_mag_shift) inputs: Bit_Obj *bitobj Word16 number_of_regions outputs: Word16 *decoder_region_standard_deviation Word16 *absolute_region_power_index Word16 *p_mag_shift Description: Recover differential_region_power_index from code bits Design Notes: WMOPS: 7kHz | 24kbit | 32kbit -------|--------------|---------------- AVG | 0.04 | 0.04 -------|--------------|---------------- MAX | 0.05 | 0.05 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 0.08 | 0.08 | 0.08 -------|--------------|----------------|---------------- MAX | 0.10 | 0.10 | 0.10 -------|--------------|----------------|---------------- ***************************************************************************/ void decode_envelope(Bit_Obj *bitobj, Word16 number_of_regions, Word16 *decoder_region_standard_deviation, Word16 *absolute_region_power_index, Word16 *p_mag_shift) { Word16 region; Word16 i; Word16 index; Word16 differential_region_power_index[MAX_NUMBER_OF_REGIONS]; Word16 max_index; Word16 temp; Word16 temp1; Word16 temp2; Word32 acca; index = 0; move16(); /* get 5 bits from the current code word */ for (i=0; i<5; i++) { get_next_bit(bitobj); index = shl_nocheck(index,1); index = add(index,bitobj->next_bit); } bitobj->number_of_bits_left = sub(bitobj->number_of_bits_left,5); /* ESF_ADJUSTMENT_TO_RMS_INDEX compensates for the current (9/30/96) IMLT being scaled to high by the ninth power of sqrt(2). */ differential_region_power_index[0] = sub(index,ESF_ADJUSTMENT_TO_RMS_INDEX); move16(); /* obtain differential_region_power_index */ for (region=1; regionnext_bit == 0) { index = differential_region_power_decoder_tree[region][index][0]; move16(); } else { index = differential_region_power_decoder_tree[region][index][1]; move16(); } bitobj->number_of_bits_left = sub(bitobj->number_of_bits_left,1); test(); } while (index > 0); differential_region_power_index[region] = negate(index); move16(); } /* Reconstruct absolute_region_power_index[] from differential_region_power_index[]. */ absolute_region_power_index[0] = differential_region_power_index[0]; move16(); for (region=1; region 0) { max_index = i; move16(); } temp = add(temp,int_region_standard_deviation_table[i]); } i = 9; move16(); temp1 = sub(temp,8); temp2 = sub(max_index,28); test(); test(); logic16(); test(); logic16(); while ((i >= 0) && ((temp1 >= 0) || (temp2 > 0))) { i = sub(i,1); temp = shr_nocheck(temp,1); max_index = sub(max_index,2); temp1 = sub(temp,8); temp2 = sub(max_index,28); test(); test(); logic16(); test(); logic16(); } *p_mag_shift = i; move16(); /* pointer arithmetic */ temp = (Word16 )(REGION_POWER_TABLE_NUM_NEGATIVES + (*p_mag_shift * 2)); for (region=0; region 0) { region = decoder_category_balances[i++]; move16(); decoder_power_categories[region] = add(decoder_power_categories[region],1); move16(); categorization_control = sub(categorization_control,1); } } /*************************************************************************** Function: decode_vector_quantized_mlt_indices Syntax: void decode_vector_quantized_mlt_indices(Bit_Obj *bitobj, Rand_Obj *randobj, Word16 number_of_regions, Word16 *decoder_region_standard_deviation, Word16 *decoder_power_categories, Word16 *decoder_mlt_coefs) inputs: Bit_Obj *bitobj Rand_Obj *randobj Word16 number_of_regions Word16 *decoder_region_standard_deviation Word16 *decoder_power_categories outputs: Word16 *decoder_mlt_coefs Description: Decode MLT coefficients Design Notes: WMOPS: 7kHz | 24kbit | 32kbit -------|--------------|---------------- AVG | 0.60 | 0.72 -------|--------------|---------------- MAX | 0.67 | 0.76 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 0.77 | 0.98 | 1.28 -------|--------------|----------------|---------------- MAX | 1.05 | 1.18 | 1.36 -------|--------------|----------------|---------------- ***************************************************************************/ void decode_vector_quantized_mlt_indices(Bit_Obj *bitobj, Rand_Obj *randobj, Word16 number_of_regions, Word16 *decoder_region_standard_deviation, Word16 *decoder_power_categories, Word16 *decoder_mlt_coefs) { Word16 standard_deviation; Word16 *decoder_mlt_ptr; Word16 decoder_mlt_value; Word16 noifillpos; Word16 noifillneg; Word16 noise_fill_factor[3] = {5793,8192,23170}; Word16 region; Word16 category; Word16 j,n; Word16 k[MAX_VECTOR_DIMENSION]; Word16 vec_dim; Word16 num_vecs; Word16 index; Word16 bit=0; Word16 signs_index=0; Word16 num_sign_bits; Word16 ran_out_of_bits_flag; Word16 *decoder_table_ptr; Word16 random_word; Word16 temp1; Word16 temp; Word32 acca; ran_out_of_bits_flag = 0; move16(); for (region=0; regionnumber_of_bits_left <= 0) { ran_out_of_bits_flag = 1; move16(); break; } get_next_bit(bitobj); test(); if (bitobj->next_bit == 0) { temp = shl_nocheck(index,1); index = (Word16)*(decoder_table_ptr + temp); move16(); } else { temp = shl_nocheck(index,1); index = (Word16)*(decoder_table_ptr + temp + 1); move16(); } bitobj->number_of_bits_left = sub(bitobj->number_of_bits_left,1); test(); } while (index > 0); test(); if (ran_out_of_bits_flag != 0) break; index = negate(index); /* convert index into array used to access the centroid table */ /* get the number of sign bits in the index */ num_sign_bits = index_to_array(index,k,category); temp = sub(bitobj->number_of_bits_left,num_sign_bits); test(); if (temp >= 0) { test(); if (num_sign_bits != 0) { signs_index = 0; move16(); for (j=0; jnext_bit); bitobj->number_of_bits_left = sub(bitobj->number_of_bits_left,1); } temp = sub(num_sign_bits,1); bit = shl_nocheck(1,(temp)); } for (j=0; jnumber_of_bits_left = sub(bitobj->number_of_bits_left,1); } /**************************************************************************************** Function: index_to_array Syntax: number_of_non_zero = index_to_array(Word16 index, Word16 array[MAX_VECTOR_DIMENSION], Word16 category) inputs: Word16 index Word16 category outputs: Word16 array[MAX_VECTOR_DIMENSION] - used in decoder to access mlt_quant_centroid table Word16 number_of_non_zero - number of non zero elements in the array Description: Computes an array of sign bits with the length of the category vector Returns the number of sign bits and the array WMOPS: 7kHz | 24kbit | 32kbit -------|--------------|---------------- AVG | 0.00 | 0.00 -------|--------------|---------------- MAX | 0.00 | 0.00 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 0.00 | 0.00 | 0.00 -------|--------------|----------------|---------------- MAX | 0.00 | 0.00 | 0.00 -------|--------------|----------------|---------------- ****************************************************************************************/ Word16 index_to_array(Word16 index,Word16 *array,Word16 category) { Word16 j,q,p; Word16 number_of_non_zero; Word16 max_bin_plus_one; Word16 inverse_of_max_bin_plus_one; Word16 temp; number_of_non_zero = 0; move16(); p = index; move16(); max_bin_plus_one = add(max_bin[category],1); inverse_of_max_bin_plus_one = max_bin_plus_one_inverse[category]; move16(); temp = sub(vector_dimension[category],1); for (j=temp; j>=0; j--) { q = mult(p,inverse_of_max_bin_plus_one); temp = extract_l(L_mult0(q,max_bin_plus_one)); array[j] = sub(p,temp); move16(); p = q; move16(); temp = array[j]; move16(); test(); if (temp != 0) number_of_non_zero = add(number_of_non_zero,1); } return(number_of_non_zero); } /*************************************************************************** Function: test_4_frame_errors Syntax: void test_4_frame_errors(Bit_Obj *bitobj, Word16 number_of_regions, Word16 num_categorization_control_possibilities, Word16 *frame_error_flag, Word16 categorization_control, Word16 *absolute_region_power_index) inputs: bit_obj number_of_regions num_categorization_control_possibilities frame_error_flag categorization_control absolute_region_power_index outputs: frame_error_flag Description: Tests for error conditions and sets the frame_error_flag accordingly Design Notes: WMOPS: 7kHz | 24kbit | 32kbit -------|--------------|---------------- AVG | 0.01 | 0.01 -------|--------------|---------------- MAX | 0.04 | 0.08 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 0.01 | 0.01 | 0.01 -------|--------------|----------------|---------------- MAX | 0.02 | 0.06 | 0.08 -------|--------------|----------------|---------------- ***************************************************************************/ void test_4_frame_errors(Bit_Obj *bitobj, Word16 number_of_regions, Word16 num_categorization_control_possibilities, Word16 *frame_error_flag, Word16 categorization_control, Word16 *absolute_region_power_index) { Word16 region; Word16 i; Word16 temp; Word32 acca; Word32 accb; /* Test for bit stream errors. */ test(); if (bitobj->number_of_bits_left > 0) { for (i=0; inumber_of_bits_left; i++) { get_next_bit(bitobj); test(); if (bitobj->next_bit == 0) { *frame_error_flag = 1; move16(); } } } else { temp = sub(categorization_control,sub(num_categorization_control_possibilities,1)); test(); if (temp < 0) { test(); if (bitobj->number_of_bits_left < 0) { *frame_error_flag |= 2; logic16(); } } } /* checks to ensure that abs_region_power_index is within range */ /* the error flag is set if it is out of range */ for (region=0; region 31) || (absolute_region_power_index[region] < -8) */ acca = L_add(absolute_region_power_index[region],ESF_ADJUSTMENT_TO_RMS_INDEX); accb = L_sub(acca,31); acca = L_add(acca,8); test(); /* the next line was modifed in release 1.2 to * correct miss typed code and error checking. */ if ((accb > 0) || (acca < 0)) { *frame_error_flag |= 4; logic16(); } } } /*************************************************************************** Function: error_handling Syntax: void error_handling(Word16 number_of_coefs, Word16 number_of_valid_coefs, Word16 *frame_error_flag, Word16 *decoder_mlt_coefs, Word16 *old_decoder_mlt_coefs, Word16 *p_mag_shift, Word16 *p_old_mag_shift) inputs: number_of_coefs number_of_valid_coefs frame_error_flag old_decoder_mlt_coefs p_old_mag_shift outputs: decoder_mlt_coefs old_decoder_mlt_coefs p_mag_shift p_old_mag_shift Description: If both the current and previous frames are errored, set the mlt coefficients to 0. If only the current frame is errored, then repeat the previous frame's mlt coefficients. Design Notes: WMOPS: 7kHz | 24kbit | 32kbit -------|--------------|---------------- AVG | 0.02 | 0.02 -------|--------------|---------------- MAX | 0.03 | 0.03 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 0.03 | 0.03 | 0.03 -------|--------------|----------------|---------------- MAX | 0.03 | 0.03 | 0.06 -------|--------------|----------------|---------------- ***************************************************************************/ void error_handling(Word16 number_of_coefs, Word16 number_of_valid_coefs, Word16 *frame_error_flag, Word16 *decoder_mlt_coefs, Word16 *old_decoder_mlt_coefs, Word16 *p_mag_shift, Word16 *p_old_mag_shift) { Word16 i; test(); if (*frame_error_flag != 0) { for (i = 0; i < number_of_valid_coefs; i++) { decoder_mlt_coefs[i] = old_decoder_mlt_coefs[i]; move16(); } for (i = 0; i < number_of_valid_coefs; i++) { old_decoder_mlt_coefs[i] = 0; move16(); } *p_mag_shift = *p_old_mag_shift; move16(); *p_old_mag_shift = 0; move16(); } else { /* Store in case next frame is errored. */ for (i = 0; i < number_of_valid_coefs; i++) { old_decoder_mlt_coefs[i] = decoder_mlt_coefs[i]; move16(); } *p_old_mag_shift = *p_mag_shift; move16(); } /* Zero out the upper 1/8 of the spectrum. */ for (i = number_of_valid_coefs; i < number_of_coefs; i++) { decoder_mlt_coefs[i] = 0; move16(); } } /**************************************************************************************** Function: get_next_bit Syntax: void get_next_bit(Bit_Obj *bitobj) Description: Returns the next bit in the current word inside the bit object WMOPS: 7kHz | 24kbit | 32kbit -------|--------------|---------------- AVG | 0.00 | 0.00 -------|--------------|---------------- MAX | 0.00 | 0.00 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 0.00 | 0.00 | 0.00 -------|--------------|----------------|---------------- MAX | 0.00 | 0.00 | 0.00 -------|--------------|----------------|---------------- ****************************************************************************************/ void get_next_bit(Bit_Obj *bitobj) { Word16 temp; test(); if (bitobj->code_bit_count == 0) { bitobj->current_word = *bitobj->code_word_ptr++; move16(); bitobj->code_bit_count = 16; move16(); } bitobj->code_bit_count = sub(bitobj->code_bit_count,1); temp = shr_nocheck(bitobj->current_word,bitobj->code_bit_count); logic16(); bitobj->next_bit = (Word16 )(temp & 1); } /**************************************************************************************** Function: get_rand Syntax: Word16 get_rand(Rand_Obj *randobj) Description: Returns a random Word16 based on the seeds inside the rand object WMOPS: 7kHz | 24kbit | 32kbit -------|--------------|---------------- AVG | 0.00 | 0.00 -------|--------------|---------------- MAX | 0.00 | 0.00 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 0.00 | 0.00 | 0.00 -------|--------------|----------------|---------------- MAX | 0.00 | 0.00 | 0.00 -------|--------------|----------------|---------------- ****************************************************************************************/ Word16 get_rand(Rand_Obj *randobj) { Word16 random_word; Word32 acca; acca = L_add(randobj->seed0,randobj->seed3); random_word = extract_l(acca); logic16(); test(); if ((random_word & 32768L) != 0) random_word = add(random_word,1); randobj->seed3 = randobj->seed2; move16(); randobj->seed2 = randobj->seed1; move16(); randobj->seed1 = randobj->seed0; move16(); randobj->seed0 = random_word; move16(); return(random_word); } ================================================ FILE: deps/pjsip/third_party/g7221/encode/dct4_a.c ================================================ /********************************************************************************* ** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C ** > Software Release 2.1 (2008-06) ** (Simple repackaging; no change from 2005-05 Release 2.0 code) ** ** 2004 Polycom, Inc. ** ** All rights reserved. ** *********************************************************************************/ /********************************************************************************* * Filename: dct_type_iv_a.c * * Purpose: Discrete Cosine Transform, Type IV used for MLT * * The basis functions are * * cos(PI*(t+0.5)*(k+0.5)/block_length) * * for time t and basis function number k. Due to the symmetry of the expression * in t and k, it is clear that the forward and inverse transforms are the same. * *********************************************************************************/ /********************************************************************************* Include files *********************************************************************************/ #include "defs.h" #include "count.h" #include "dct4_a.h" /********************************************************************************* External variable declarations *********************************************************************************/ extern Word16 anal_bias[DCT_LENGTH]; extern Word16 dct_core_a[DCT_LENGTH_DIV_32][DCT_LENGTH_DIV_32]; extern cos_msin_t a_cos_msin_2 [DCT_LENGTH_DIV_32]; extern cos_msin_t a_cos_msin_4 [DCT_LENGTH_DIV_16]; extern cos_msin_t a_cos_msin_8 [DCT_LENGTH_DIV_8]; extern cos_msin_t a_cos_msin_16[DCT_LENGTH_DIV_4]; extern cos_msin_t a_cos_msin_32[DCT_LENGTH_DIV_2]; extern cos_msin_t a_cos_msin_64[DCT_LENGTH]; extern cos_msin_t *a_cos_msin_table[]; /********************************************************************************* Function: dct_type_iv_a Syntax: void dct_type_iv_a (input, output, dct_length) Word16 input[], output[], dct_length; Description: Discrete Cosine Transform, Type IV used for MLT Design Notes: WMOPS: | 24kbit | 32kbit -------|--------------|---------------- AVG | 1.14 | 1.14 -------|--------------|---------------- MAX | 1.14 | 1.14 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 2.57 | 2.57 | 2.57 -------|--------------|----------------|---------------- MAX | 2.57 | 2.57 | 2.57 -------|--------------|----------------|---------------- *********************************************************************************/ void dct_type_iv_a (Word16 *input,Word16 *output,Word16 dct_length) { Word16 buffer_a[MAX_DCT_LENGTH], buffer_b[MAX_DCT_LENGTH], buffer_c[MAX_DCT_LENGTH]; Word16 *in_ptr, *in_ptr_low, *in_ptr_high, *next_in_base; Word16 *out_ptr_low, *out_ptr_high, *next_out_base; Word16 *out_buffer, *in_buffer, *buffer_swap; Word16 in_val_low, in_val_high; Word16 out_val_low, out_val_high; Word16 in_low_even, in_low_odd; Word16 in_high_even, in_high_odd; Word16 out_low_even, out_low_odd; Word16 out_high_even, out_high_odd; Word16 *pair_ptr; Word16 cos_even, cos_odd, msin_even, msin_odd; Word16 neg_cos_odd; Word16 neg_msin_even; Word32 sum; Word16 set_span, set_count, set_count_log, pairs_left, sets_left; Word16 i,k; Word16 index; cos_msin_t **table_ptr_ptr, *cos_msin_ptr; Word16 temp; Word32 acca; Word16 dct_length_log; /*++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ /* Do the sum/difference butterflies, the first part of */ /* converting one N-point transform into N/2 two-point */ /* transforms, where N = 1 << DCT_LENGTH_LOG. = 64/128 */ /*++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ test(); if (dct_length==DCT_LENGTH) { dct_length_log = DCT_LENGTH_LOG; /* Add bias offsets */ for (i=0;i0;sets_left--) { /*||||||||||||||||||||||||||||||||||||||||||||*/ /* Set up output pointers for the current set */ /*||||||||||||||||||||||||||||||||||||||||||||*/ out_ptr_low = next_out_base; next_out_base = next_out_base + set_span; out_ptr_high = next_out_base; /*||||||||||||||||||||||||||||||||||||||||||||||||||*/ /* Loop over all the butterflies in the current set */ /*||||||||||||||||||||||||||||||||||||||||||||||||||*/ do { in_val_low = *in_ptr++; in_val_high = *in_ptr++; // blp: addition of two 16bits vars, there's no way // they'll overflow a 32bit var //acca = L_add(in_val_low,in_val_high); acca = (in_val_low + in_val_high); acca = L_shr_nocheck(acca,1); out_val_low = extract_l(acca); acca = L_sub(in_val_low,in_val_high); acca = L_shr_nocheck(acca,1); out_val_high = extract_l(acca); *out_ptr_low++ = out_val_low; *--out_ptr_high = out_val_high; test(); } while (out_ptr_low < out_ptr_high); } /* End of loop over sets of the current size */ /*============================================================*/ /* Decide which buffers to use as input and output next time. */ /* Except for the first time (when the input buffer is the */ /* subroutine input) we just alternate the local buffers. */ /*============================================================*/ in_buffer = out_buffer; move16(); if (out_buffer == buffer_a) out_buffer = buffer_b; else out_buffer = buffer_a; index = add(index,1); } /* End of loop over set sizes */ /*++++++++++++++++++++++++++++++++*/ /* Do N/2 two-point transforms, */ /* where N = 1 << DCT_LENGTH_LOG */ /*++++++++++++++++++++++++++++++++*/ pair_ptr = in_buffer; move16(); buffer_swap = buffer_c; move16(); temp = sub(dct_length_log,1); temp = shl_nocheck(1,temp); for (pairs_left=temp; pairs_left > 0; pairs_left--) { for ( k=0; k= 0; set_count_log--) { /*===========================================================*/ /* Initialization for the loop over sets at the current size */ /*===========================================================*/ /* set_span = 1 << (DCT_LENGTH_LOG - set_count_log); */ set_span = shr_nocheck(dct_length,set_count_log); set_count = shl_nocheck(1,set_count_log); next_in_base = in_buffer; move16(); test(); if (set_count_log == 0) { next_out_base = output; } else { next_out_base = out_buffer; } /*=====================================*/ /* Loop over all the sets of this size */ /*=====================================*/ for (sets_left = set_count; sets_left > 0;sets_left--) { /*|||||||||||||||||||||||||||||||||||||||||*/ /* Set up the pointers for the current set */ /*|||||||||||||||||||||||||||||||||||||||||*/ in_ptr_low = next_in_base; move16(); temp = shr_nocheck(set_span,1); /* address arithmetic */ in_ptr_high = in_ptr_low + temp; next_in_base += set_span; out_ptr_low = next_out_base; next_out_base += set_span; out_ptr_high = next_out_base; cos_msin_ptr = *table_ptr_ptr; /*||||||||||||||||||||||||||||||||||||||||||||||||||||||*/ /* Loop over all the butterfly pairs in the current set */ /*||||||||||||||||||||||||||||||||||||||||||||||||||||||*/ do { /* address arithmetic */ in_low_even = *in_ptr_low++; in_low_odd = *in_ptr_low++; in_high_even = *in_ptr_high++; in_high_odd = *in_ptr_high++; cos_even = cos_msin_ptr[0].cosine; move16(); msin_even = cos_msin_ptr[0].minus_sine; move16(); cos_odd = cos_msin_ptr[1].cosine; move16(); msin_odd = cos_msin_ptr[1].minus_sine; move16(); cos_msin_ptr += 2; sum = 0L; sum=L_mac(sum,cos_even,in_low_even); neg_msin_even = negate(msin_even); sum=L_mac(sum,neg_msin_even,in_high_even); out_low_even = itu_round(sum); sum = 0L; sum=L_mac(sum,msin_even,in_low_even); sum=L_mac(sum,cos_even,in_high_even); out_high_even= itu_round(sum); sum = 0L; sum=L_mac(sum,cos_odd,in_low_odd); sum=L_mac(sum,msin_odd,in_high_odd); out_low_odd= itu_round(sum); sum = 0L; sum=L_mac(sum,msin_odd,in_low_odd); neg_cos_odd = negate(cos_odd); sum=L_mac(sum,neg_cos_odd,in_high_odd); out_high_odd= itu_round(sum); *out_ptr_low++ = out_low_even; *--out_ptr_high = out_high_even; *out_ptr_low++ = out_low_odd; *--out_ptr_high = out_high_odd; test(); } while (out_ptr_low < out_ptr_high); } /* End of loop over sets of the current size */ /*=============================================*/ /* Swap input and output buffers for next time */ /*=============================================*/ buffer_swap = in_buffer; in_buffer = out_buffer; out_buffer = buffer_swap; table_ptr_ptr++; } } ================================================ FILE: deps/pjsip/third_party/g7221/encode/dct4_a.h ================================================ /**************************************************************************** ** ** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C ** > Software Release 2.1 (2008-06) ** (Simple repackaging; no change from 2005-05 Release 2.0 code) ** ** 2004 Polycom, Inc. ** ** All rights reserved. ** ****************************************************************************/ /**************************************************************************** Filename: dct4_a.h Purpose: Contains tables used by dct4_a.c Design Notes: ****************************************************************************/ /*************************************************************************** Include files ***************************************************************************/ #include #include typedef struct { Word16 cosine; Word16 minus_sine; } cos_msin_t; cos_msin_t a_cos_msin_2[10] = { { 29805 , -1171 } , { 29621 , -3506 } , { 29255 , -5819 } , { 28708 , -8097 } , { 27984 , -10324 } , { 27088 , -12488 } , { 26025 , -14575 } , { 24801 , -16572 } , { 23425 , -18466 } , { 21903 , -20247 } }; cos_msin_t a_cos_msin_4[20] = { { 29822 , -586 } , { 29776 , -1756 } , { 29684 , -2924 } , { 29547 , -4087 } , { 29364 , -5244 } , { 29135 , -6392 } , { 28862 , -7531 } , { 28544 , -8659 } , { 28182 , -9773 } , { 27776 , -10871 } , { 27328 , -11954 } , { 26838 , -13017 } , { 26306 , -14061 } , { 25734 , -15083 } , { 25122 , -16081 } , { 24471 , -17055 } , { 23783 , -18003 } , { 23057 , -18923 } , { 22297 , -19813 } , { 21502 , -20673 } }; cos_msin_t a_cos_msin_8[40] = { { 29827 , -293 } , { 29815 , -878 } , { 29792 , -1464 } , { 29758 , -2048 } , { 29712 , -2632 } , { 29654 , -3215 } , { 29586 , -3797 } , { 29505 , -4377 } , { 29414 , -4955 } , { 29311 , -5532 } , { 29196 , -6106 } , { 29071 , -6678 } , { 28934 , -7248 } , { 28786 , -7814 } , { 28627 , -8378 } , { 28457 , -8938 } , { 28276 , -9495 } , { 28084 , -10049 } , { 27882 , -10598 } , { 27668 , -11144 } , { 27444 , -11685 } , { 27209 , -12221 } , { 26964 , -12753 } , { 26709 , -13280 } , { 26443 , -13802 } , { 26167 , -14318 } , { 25881 , -14829 } , { 25584 , -15335 } , { 25278 , -15834 } , { 24963 , -16327 } , { 24637 , -16814 } , { 24302 , -17295 } , { 23958 , -17769 } , { 23605 , -18236 } , { 23242 , -18695 } , { 22871 , -19148 } , { 22490 , -19594 } , { 22101 , -20031 } , { 21704 , -20461 } , { 21298 , -20884 } }; cos_msin_t a_cos_msin_16[80] = { { 29828 , -146 } , { 29825 , -439 } , { 29819 , -732 } , { 29811 , -1025 } , { 29799 , -1317 } , { 29785 , -1610 } , { 29767 , -1902 } , { 29747 , -2194 } , { 29724 , -2486 } , { 29698 , -2778 } , { 29670 , -3069 } , { 29638 , -3360 } , { 29604 , -3651 } , { 29567 , -3942 } , { 29526 , -4232 } , { 29483 , -4521 } , { 29438 , -4811 } , { 29389 , -5099 } , { 29338 , -5388 } , { 29283 , -5676 } , { 29226 , -5963 } , { 29166 , -6249 } , { 29103 , -6535 } , { 29038 , -6821 } , { 28969 , -7106 } , { 28898 , -7390 } , { 28824 , -7673 } , { 28748 , -7956 } , { 28668 , -8237 } , { 28586 , -8518 } , { 28501 , -8799 } , { 28413 , -9078 } , { 28323 , -9357 } , { 28229 , -9634 } , { 28133 , -9911 } , { 28035 , -10187 } , { 27933 , -10461 } , { 27829 , -10735 } , { 27723 , -11008 } , { 27613 , -11279 } , { 27501 , -11550 } , { 27387 , -11819 } , { 27269 , -12088 } , { 27149 , -12355 } , { 27027 , -12621 } , { 26901 , -12885 } , { 26774 , -13149 } , { 26643 , -13411 } , { 26510 , -13672 } , { 26375 , -13932 } , { 26237 , -14190 } , { 26096 , -14447 } , { 25953 , -14702 } , { 25807 , -14956 } , { 25659 , -15209 } , { 25509 , -15460 } , { 25356 , -15710 } , { 25200 , -15958 } , { 25043 , -16205 } , { 24882 , -16450 } , { 24720 , -16693 } , { 24554 , -16935 } , { 24387 , -17175 } , { 24217 , -17414 } , { 24045 , -17651 } , { 23871 , -17886 } , { 23694 , -18119 } , { 23515 , -18351 } , { 23334 , -18581 } , { 23150 , -18809 } , { 22964 , -19036 } , { 22776 , -19260 } , { 22586 , -19483 } , { 22394 , -19704 } , { 22199 , -19923 } , { 22003 , -20140 } , { 21804 , -20355 } , { 21603 , -20568 } , { 21400 , -20779 } , { 21195 , -20988 } }; cos_msin_t a_cos_msin_32[160]= { { 29828 , -73 } , { 29827 , -220 } , { 29826 , -366 } , { 29824 , -512 } , { 29821 , -659 } , { 29817 , -805 } , { 29813 , -952 } , { 29808 , -1098 } , { 29802 , -1244 } , { 29796 , -1390 } , { 29789 , -1537 } , { 29781 , -1683 } , { 29772 , -1829 } , { 29763 , -1975 } , { 29753 , -2121 } , { 29742 , -2267 } , { 29730 , -2413 } , { 29718 , -2559 } , { 29705 , -2705 } , { 29692 , -2851 } , { 29677 , -2997 } , { 29662 , -3142 } , { 29646 , -3288 } , { 29630 , -3433 } , { 29613 , -3579 } , { 29595 , -3724 } , { 29576 , -3869 } , { 29557 , -4014 } , { 29537 , -4159 } , { 29516 , -4304 } , { 29494 , -4449 } , { 29472 , -4594 } , { 29449 , -4738 } , { 29426 , -4883 } , { 29401 , -5027 } , { 29376 , -5172 } , { 29351 , -5316 } , { 29324 , -5460 } , { 29297 , -5604 } , { 29269 , -5747 } , { 29241 , -5891 } , { 29211 , -6034 } , { 29181 , -6178 } , { 29151 , -6321 } , { 29119 , -6464 } , { 29087 , -6607 } , { 29054 , -6749 } , { 29021 , -6892 } , { 28987 , -7034 } , { 28952 , -7177 } , { 28916 , -7319 } , { 28880 , -7460 } , { 28843 , -7602 } , { 28805 , -7744 } , { 28767 , -7885 } , { 28728 , -8026 } , { 28688 , -8167 } , { 28648 , -8308 } , { 28607 , -8448 } , { 28565 , -8589 } , { 28522 , -8729 } , { 28479 , -8869 } , { 28435 , -9008 } , { 28391 , -9148 } , { 28346 , -9287 } , { 28300 , -9426 } , { 28253 , -9565 } , { 28206 , -9703 } , { 28158 , -9842 } , { 28109 , -9980 } , { 28060 , -10118 } , { 28010 , -10255 } , { 27959 , -10393 } , { 27908 , -10530 } , { 27856 , -10667 } , { 27803 , -10803 } , { 27750 , -10940 } , { 27696 , -11076 } , { 27641 , -11212 } , { 27586 , -11347 } , { 27529 , -11482 } , { 27473 , -11617 } , { 27415 , -11752 } , { 27357 , -11886 } , { 27299 , -12021 } , { 27239 , -12154 } , { 27179 , -12288 } , { 27119 , -12421 } , { 27057 , -12554 } , { 26996 , -12687 } , { 26933 , -12819 } , { 26870 , -12951 } , { 26806 , -13083 } , { 26741 , -13215 } , { 26676 , -13346 } , { 26610 , -13476 } , { 26544 , -13607 } , { 26477 , -13737 } , { 26409 , -13867 } , { 26340 , -13996 } , { 26271 , -14125 } , { 26202 , -14254 } , { 26132 , -14383 } , { 26061 , -14511 } , { 25989 , -14638 } , { 25917 , -14766 } , { 25844 , -14893 } , { 25771 , -15020 } , { 25697 , -15146 } , { 25622 , -15272 } , { 25547 , -15397 } , { 25471 , -15523 } , { 25394 , -15648 } , { 25317 , -15772 } , { 25239 , -15896 } , { 25161 , -16020 } , { 25082 , -16143 } , { 25003 , -16266 } , { 24923 , -16389 } , { 24842 , -16511 } , { 24760 , -16632 } , { 24678 , -16754 } , { 24596 , -16875 } , { 24513 , -16995 } , { 24429 , -17115 } , { 24345 , -17235 } , { 24260 , -17354 } , { 24174 , -17473 } , { 24088 , -17592 } , { 24002 , -17710 } , { 23914 , -17827 } , { 23827 , -17945 } , { 23738 , -18061 } , { 23649 , -18178 } , { 23560 , -18293 } , { 23470 , -18409 } , { 23379 , -18524 } , { 23288 , -18638 } , { 23196 , -18752 } , { 23104 , -18866 } , { 23011 , -18979 } , { 22917 , -19092 } , { 22824 , -19204 } , { 22729 , -19316 } , { 22634 , -19427 } , { 22538 , -19538 } , { 22442 , -19649 } , { 22345 , -19759 } , { 22248 , -19868 } , { 22150 , -19977 } , { 22052 , -20086 } , { 21953 , -20194 } , { 21854 , -20301 } , { 21754 , -20408 } , { 21653 , -20515 } , { 21552 , -20621 } , { 21451 , -20726 } , { 21349 , -20831 } , { 21246 , -20936 } , { 21143 , -21040 } }; cos_msin_t a_cos_msin_64[320] = { {29827, -34}, {29827, -106}, {29827, -177}, {29827, -249}, {29826, -320}, {29825, -392}, {29824, -463}, {29823, -535}, {29821, -606}, {29819, -678}, {29818, -750}, {29816, -821}, {29814, -893}, {29812, -964}, {29809, -1035}, {29807, -1106}, {29804, -1177}, {29801, -1249}, {29797, -1320}, {29795, -1392}, {29791, -1463}, {29787, -1535}, {29784, -1606}, {29780, -1678}, {29776, -1749}, {29771, -1820}, {29767, -1892}, {29763, -1963}, {29758, -2035}, {29753, -2106}, {29748, -2177}, {29742, -2249}, {29737, -2320}, {29731, -2391}, {29726, -2462}, {29719, -2534}, {29713, -2605}, {29707, -2676}, {29701, -2747}, {29694, -2819}, {29686, -2890}, {29680, -2961}, {29673, -3032}, {29665, -3103}, {29658, -3174}, {29650, -3245}, {29643, -3316}, {29635, -3387}, {29626, -3459}, {29618, -3529}, {29610, -3600}, {29601, -3671}, {29592, -3742}, {29583, -3813}, {29574, -3884}, {29564, -3955}, {29554, -4026}, {29544, -4097}, {29535, -4167}, {29525, -4238}, {29514, -4309}, {29504, -4380}, {29493, -4450}, {29483, -4521}, {29472, -4591}, {29461, -4662}, {29450, -4733}, {29439, -4803}, {29427, -4874}, {29415, -4944}, {29403, -5015}, {29391, -5085}, {29379, -5155}, {29366, -5226}, {29353, -5296}, {29341, -5367}, {29328, -5438}, {29314, -5508}, {29301, -5578}, {29289, -5648}, {29274, -5718}, {29260, -5788}, {29247, -5858}, {29232, -5928}, {29218, -5998}, {29204, -6068}, {29188, -6139}, {29175, -6209}, {29159, -6279}, {29145, -6348}, {29128, -6418}, {29114, -6488}, {29097, -6557}, {29082, -6627}, {29066, -6697}, {29050, -6767}, {29034, -6837}, {29017, -6906}, {29001, -6975}, {28984, -7045}, {28966, -7114}, {28950, -7184}, {28933, -7254}, {28915, -7323}, {28897, -7392}, {28880, -7461}, {28862, -7530}, {28843, -7600}, {28825, -7669}, {28807, -7738}, {28788, -7806}, {28769, -7875}, {28751, -7944}, {28732, -8014}, {28712, -8082}, {28692, -8151}, {28672, -8219}, {28653, -8289}, {28633, -8357}, {28613, -8425}, {28593, -8494}, {28572, -8563}, {28551, -8632}, {28531, -8700}, {28510, -8768}, {28488, -8837}, {28468, -8905}, {28447, -8973}, {28425, -9041}, {28403, -9109}, {28381, -9177}, {28359, -9245}, {28336, -9313}, {28315, -9381}, {28292, -9448}, {28269, -9517}, {28246, -9584}, {28223, -9652}, {28200, -9720}, {28176, -9787}, {28153, -9854}, {28129, -9922}, {28105, -9990}, {28082, -10056}, {28057, -10124}, {28032, -10191}, {28009, -10258}, {27984, -10326}, {27959, -10392}, {27934, -10460}, {27909, -10526}, {27883, -10593}, {27858, -10661}, {27832, -10727}, {27807, -10794}, {27780, -10860}, {27754, -10927}, {27728, -10993}, {27701, -11059}, {27676, -11126}, {27648, -11192}, {27622, -11259}, {27595, -11324}, {27567, -11391}, {27540, -11456}, {27512, -11523}, {27484, -11588}, {27456, -11655}, {27429, -11720}, {27401, -11786}, {27372, -11852}, {27344, -11917}, {27315, -11982}, {27286, -12049}, {27257, -12114}, {27229, -12179}, {27199, -12244}, {27169, -12309}, {27140, -12375}, {27110, -12439}, {27080, -12505}, {27050, -12570}, {27019, -12634}, {26990, -12699}, {26958, -12764}, {26928, -12828}, {26897, -12892}, {26866, -12956}, {26835, -13021}, {26804, -13086}, {26773, -13149}, {26741, -13214}, {26709, -13278}, {26677, -13342}, {26645, -13406}, {26613, -13470}, {26581, -13534}, {26549, -13597}, {26515, -13661}, {26483, -13725}, {26450, -13788}, {26417, -13851}, {26384, -13915}, {26350, -13978}, {26316, -14041}, {26283, -14103}, {26248, -14166}, {26215, -14229}, {26180, -14292}, {26146, -14355}, {26112, -14417}, {26077, -14480}, {26042, -14543}, {26008, -14605}, {25972, -14667}, {25937, -14730}, {25901, -14792}, {25866, -14854}, {25830, -14916}, {25794, -14977}, {25759, -15039}, {25723, -15101}, {25687, -15162}, {25650, -15224}, {25613, -15286}, {25577, -15347}, {25540, -15408}, {25503, -15470}, {25465, -15531}, {25428, -15592}, {25391, -15653}, {25353, -15714}, {25315, -15774}, {25277, -15834}, {25240, -15895}, {25201, -15956}, {25162, -16016}, {25124, -16076}, {25086, -16136}, {25047, -16196}, {25008, -16256}, {24969, -16316}, {24930, -16375}, {24891, -16436}, {24851, -16496}, {24811, -16555}, {24772, -16615}, {24732, -16674}, {24692, -16732}, {24652, -16791}, {24612, -16852}, {24572, -16911}, {24531, -16969}, {24490, -17027}, {24449, -17086}, {24408, -17145}, {24367, -17203}, {24325, -17261}, {24284, -17320}, {24242, -17379}, {24200, -17436}, {24158, -17494}, {24116, -17552}, {24075, -17610}, {24032, -17668}, {23990, -17725}, {23947, -17782}, {23904, -17840}, {23862, -17897}, {23819, -17954}, {23775, -18011}, {23732, -18068}, {23689, -18125}, {23645, -18181}, {23602, -18238}, {23558, -18294}, {23514, -18351}, {23470, -18407}, {23426, -18464}, {23381, -18520}, {23337, -18576}, {23293, -18632}, {23248, -18688}, {23202, -18743}, {23158, -18799}, {23112, -18854}, {23068, -18910}, {23022, -18964}, {22977, -19020}, {22931, -19074}, {22885, -19129}, {22839, -19185}, {22793, -19239}, {22747, -19294}, {22700, -19348}, {22655, -19403}, {22607, -19457}, {22561, -19511}, {22514, -19565}, {22467, -19619}, {22421, -19673}, {22373, -19726}, {22326, -19780}, {22279, -19834}, {22230, -19887}, {22183, -19940}, {22135, -19993}, {22087, -20047}, {22039, -20099}, {21991, -20152}, {21942, -20205}, {21894, -20257}, {21845, -20309}, {21797, -20362}, {21748, -20413}, {21699, -20466}, {21650, -20518}, {21601, -20570}, {21551, -20621}, {21502, -20674} }; cos_msin_t *a_cos_msin_table[] = {a_cos_msin_2, a_cos_msin_4, a_cos_msin_8, a_cos_msin_16, a_cos_msin_32,a_cos_msin_64 }; Word16 dct_core_a[10][10] = { { 10453, 10196, 9688, 8941, 7973, 6810, 5479, 4013, 2448, 823 }, { 10196, 7973, 4013, -823, -5479, -8941, -10453, -9688, -6810, -2448 }, { 9688 , 4013, -4013, -9688, -9688, -4013, 4013, 9688, 9688, 4013 }, { 8941 , -823, -9688, -7973, 2448, 10196, 6810, -4013, -10453, -5479 }, { 7973 , -5479, -9688, 2448, 10453, 823, -10196, -4013, 8941, 6810 }, { 6810 , -8941, -4013, 10196, 823, -10453, 2448, 9688, -5479, -7973 }, { 5479 , -10453, 4013, 6810, -10196, 2448, 7973, -9688, 823, 8941 }, { 4013 , -9688, 9688, -4013, -4013, 9688, -9688, 4013, 4013, -9688 }, { 2448 , -6810, 9688, -10453, 8941, -5479, 823, 4013, -7973, 10196 }, { 823 , -2448, 4013, -5479, 6810, -7973, 8941, -9688, 10196, -10453 }}; Word16 anal_bias[320] = { 1, 1, 3, 1, 4, 1, 3, -2, 4, 3, 4, 1, 3, 0, 2, -3, 0, 0, 2, 2, 4, 1, 1, -5, 4, 1, 2, -1, 0, -1, 1, -2, 0, 2, 2, 2, 4, 1, 3, 0, 5, 3, 2, 0, 3, 0, 1, -4, 1, 1, 2, 0, 4, 0, 1, -4, 6, 1, 3, -1, 1, 0, 0, -4, 1, 1, 3, 1, 3, 2, 4, -2, 4, 3, 5, 1, 3, 0, 1, -3, 1, 1, 2, 0, 4, 1, 2, -4, 4, 2, 2, -1, 1, -1, 1, -4, 0, 0, 3, 0, 5, 2, 3, -1, 6, 2, 5, 0, 4, 0, 1, -3, 1, 0, 3, 0, 4, 0, 1, -3, 4, 1, 3, -1, 1, -2, 1, -4, 0, 1, 2, 1, 3, 2, 2, -2, 4, 3, 3, 0, 3, 0, 0, -2, 1, 0, 2, 0, 5, -1, 1, -3, 4, 2, 2, 0, 2, -3, 1, -4, -1, 1, 2, 2, 4, 1, 3, -1, 5, 2, 2, 0, 3, -1, 2, -3, 0, 1, 2, 2, 4, 0, 1, -5, 5, 1, 3, 0, 2, -1, 0, -2, 1, 2, 2, 2, 4, 1, 0, 0, 4, 2, 4, 1, 4, -1, 1, -4, 0, 1, 3, 1, 5, 1, 1, -2, 4, 0, 2, 0, 2, -1, 0, -2, 0, 1, 1, 1, 4, 2, 3, -2, 5, 4, 4, 0, 3, 0, 3, -4, 1, 2, 2, 0, 4, 1, 0, -3, 4, 2, 3, -1, 1, -1, 1, -4, 0, 2, 3, 1, 4, 1, 3, 0, 3, 3, 4, 1, 2, 0, 1, -3, 2, 2, 2, 1, 5, 0, 1, -4, 4, 1, 3, -2, 3, -1, 0, -2, 0, 2, 2, 0, 5, 1, 4, -1, 4, 3, 4, 1, 3, 0, 1, -4, 2, 0, 3, 1, 5, 0, 1, -5, 5, 2, 2, 0, 0, 0, 0, -4}; ================================================ FILE: deps/pjsip/third_party/g7221/encode/encoder.c ================================================ /*************************************************************************** ** ** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C ** > Software Release 2.1 (2008-06) ** (Simple repackaging; no change from 2005-05 Release 2.0 code) ** ** 2004 Polycom, Inc. ** ** All rights reserved. ** ***************************************************************************/ /*************************************************************************** Filename: encoder.c Purpose: Contains files used to implement the G.722.1 Annex C encoder Design Notes: ***************************************************************************/ /*************************************************************************** Include files ***************************************************************************/ #include #include #include "defs.h" #include "huff_def.h" #include "tables.h" #include "count.h" /*************************************************************************** Function: encoder Syntax: void encoder(Word16 number_of_available_bits, Word16 number_of_regions, Word16 mlt_coefs, Word16 mag_shift, Word16 out_words) inputs: number_of_available_bits number_of_regions mag_shift mlt_coefs[DCT_LENGTH] outputs: out_words[MAX_BITS_PER_FRAME/16] Description: Encodes the mlt coefs into out_words using G.722.1 Annex C WMOPS: 7kHz | 24kbit | 32kbit -------|--------------|---------------- AVG | 0.93 | 1.04 -------|--------------|---------------- MAX | 1.20 | 1.28 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 1.39 | 1.71 | 2.01 -------|--------------|----------------|---------------- MAX | 2.00 | 2.30 | 2.52 -------|--------------|----------------|---------------- ***************************************************************************/ void encoder(Word16 number_of_available_bits, Word16 number_of_regions, Word16 *mlt_coefs, Word16 mag_shift, Word16 *out_words) { Word16 num_categorization_control_bits; Word16 num_categorization_control_possibilities; Word16 number_of_bits_per_frame; Word16 number_of_envelope_bits; Word16 categorization_control; Word16 region; Word16 absolute_region_power_index[MAX_NUMBER_OF_REGIONS]; Word16 power_categories[MAX_NUMBER_OF_REGIONS]; Word16 category_balances[MAX_NUM_CATEGORIZATION_CONTROL_POSSIBILITIES-1]; Word16 drp_num_bits[MAX_NUMBER_OF_REGIONS+1]; UWord16 drp_code_bits[MAX_NUMBER_OF_REGIONS+1]; Word16 region_mlt_bit_counts[MAX_NUMBER_OF_REGIONS]; UWord32 region_mlt_bits[4*MAX_NUMBER_OF_REGIONS]; Word16 mag_shift_offset; Word16 temp; /* initialize variables */ test(); if (number_of_regions == NUMBER_OF_REGIONS) { num_categorization_control_bits = NUM_CATEGORIZATION_CONTROL_BITS; move16(); num_categorization_control_possibilities = NUM_CATEGORIZATION_CONTROL_POSSIBILITIES; move16(); } else { num_categorization_control_bits = MAX_NUM_CATEGORIZATION_CONTROL_BITS; move16(); num_categorization_control_possibilities = MAX_NUM_CATEGORIZATION_CONTROL_POSSIBILITIES; move16(); } number_of_bits_per_frame = number_of_available_bits; move16(); for (region=0; region= 0) { temp = extract_l(L_shr_nocheck(current_word,j)); out_word = add(out_word,temp); out_words[out_word_index++] = out_word; move16(); out_word_bits_free = 16; move16(); out_word_bits_free = sub(out_word_bits_free,j); acca = (current_word << out_word_bits_free); out_word = extract_l(acca); } else { j = negate(j); acca = (current_word << j); accb = L_deposit_l(out_word); acca = L_add(accb,acca); out_word = extract_l(acca); out_word_bits_free = sub(out_word_bits_free,current_word_bits_left); } } /* These code bits are left justified. */ for (region=0;region 0) current_word_bits_left = region_bit_count; else current_word_bits_left = 32; current_word = *in_word_ptr++; acca = L_deposit_l(out_word_index); acca = L_shl_nocheck(acca,4); acca = L_sub(acca,number_of_bits_per_frame); /* from while loop */ test(); test(); logic16(); while ((region_bit_count > 0) && (acca < 0)) { /* from while loop */ test(); test(); logic16(); temp = sub(current_word_bits_left,out_word_bits_free); test(); if (temp >= 0) { temp = sub(32,out_word_bits_free); accb = LU_shr(current_word,temp); slice = (UWord16)extract_l(accb); out_word = add(out_word,slice); test(); current_word <<= out_word_bits_free; current_word_bits_left = sub(current_word_bits_left,out_word_bits_free); out_words[out_word_index++] = extract_l(out_word); move16(); out_word = 0; move16(); out_word_bits_free = 16; move16(); } else { temp = sub(32,current_word_bits_left); accb = LU_shr(current_word,temp); slice = (UWord16)extract_l(accb); temp = sub(out_word_bits_free,current_word_bits_left); test(); accb = slice << temp; acca = L_deposit_l(out_word); acca = L_add(acca,accb); out_word = extract_l(acca); out_word_bits_free = sub(out_word_bits_free,current_word_bits_left); current_word_bits_left = 0; move16(); } test(); if (current_word_bits_left == 0) { current_word = *in_word_ptr++; region_bit_count = sub(region_bit_count,32); /* current_word_bits_left = MIN(32,region_bit_count); */ temp = sub(32,region_bit_count); test(); if(temp > 0) current_word_bits_left = region_bit_count; else current_word_bits_left = 32; } acca = L_deposit_l(out_word_index); acca = L_shl_nocheck(acca,4); acca = L_sub(acca,number_of_bits_per_frame); } accb = L_deposit_l(out_word_index); accb = L_shl_nocheck(accb,4); accb = L_sub(accb,number_of_bits_per_frame); } } /* Fill out with 1's. */ test(); while (acca < 0) { test(); current_word = 0x0000ffff; move32(); temp = sub(16,out_word_bits_free); acca = LU_shr(current_word,temp); slice = (UWord16)extract_l(acca); out_word = add(out_word,slice); out_words[out_word_index++] = out_word; move16(); out_word = 0; move16(); out_word_bits_free = 16; move16(); acca = L_deposit_l(out_word_index); acca = L_shl_nocheck(acca,4); acca = L_sub(acca,number_of_bits_per_frame); } } /*************************************************************************** Function: adjust_abs_region_power_index Syntax: adjust_abs_region_power_index(Word16 *absolute_region_power_index, Word16 *mlt_coefs, Word16 number_of_regions) inputs: *mlt_coefs *absolute_region_power_index number_of_regions outputs: *absolute_region_power_index Description: Adjusts the absolute power index WMOPS: 7kHz | 24kbit | 32kbit -------|--------------|---------------- AVG | 0.03 | 0.03 -------|--------------|---------------- MAX | 0.12 | 0.12 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 0.03 | 0.03 | 0.03 -------|--------------|----------------|---------------- MAX | 0.14 | 0.14 | 0.14 -------|--------------|----------------|---------------- ***************************************************************************/ void adjust_abs_region_power_index(Word16 *absolute_region_power_index,Word16 *mlt_coefs,Word16 number_of_regions) { Word16 n,i; Word16 region; Word16 *raw_mlt_ptr; Word32 acca; Word16 temp; for (region=0; region 0) { temp = extract_l(L_mult0(region,REGION_SIZE)); raw_mlt_ptr = &mlt_coefs[temp]; for (i=0; i 0) { test(); long_accumulator = L_shr_nocheck(long_accumulator,1); acca = (long_accumulator & 0x7fff0000L); logic32(); power_shift = add(power_shift,1); } acca = L_sub(long_accumulator,32767); temp = add(power_shift,15); test(); test(); logic16(); while ((acca <= 0) && (temp >= 0)) { test(); test(); logic16(); long_accumulator = L_shl_nocheck(long_accumulator,1); acca = L_sub(long_accumulator,32767); power_shift--; temp = add(power_shift,15); } long_accumulator = L_shr_nocheck(long_accumulator,1); /* 28963 corresponds to square root of 2 times REGION_SIZE(20). */ acca = L_sub(long_accumulator,28963); test(); if (acca >= 0) power_shift = add(power_shift,1); acca = L_deposit_l(mag_shift); acca = L_shl_nocheck(acca,1); acca = L_sub(power_shift,acca); acca = L_add(35,acca); acca = L_sub(acca,REGION_POWER_TABLE_NUM_NEGATIVES); absolute_region_power_index[region] = extract_l(acca); } /* Before we differentially encode the quantized region powers, adjust upward the valleys to make sure all the peaks can be accurately represented. */ temp = sub(number_of_regions,2); for (region = temp; region >= 0; region--) { temp1 = sub(absolute_region_power_index[region+1],DRP_DIFF_MAX); temp2 = sub(absolute_region_power_index[region],temp1); test(); if (temp2 < 0) { absolute_region_power_index[region] = temp1; move16(); } } /* The MLT is currently scaled too low by the factor ENCODER_SCALE_FACTOR(=18318)/32768 * (1./sqrt(160). This is the ninth power of 1 over the square root of 2. So later we will add ESF_ADJUSTMENT_TO_RMS_INDEX (now 9) to drp_code_bits[0]. */ /* drp_code_bits[0] can range from 1 to 31. 0 will be used only as an escape sequence. */ temp1 = sub(1,ESF_ADJUSTMENT_TO_RMS_INDEX); temp2 = sub(absolute_region_power_index[0],temp1); test(); if (temp2 < 0) { absolute_region_power_index[0] = temp1; move16(); } temp1 = sub(31,ESF_ADJUSTMENT_TO_RMS_INDEX); /* * The next line was corrected in Release 1.2 */ temp2 = sub(absolute_region_power_index[0], temp1); test(); if (temp2 > 0) { absolute_region_power_index[0] = temp1; move16(); } differential_region_power_index[0] = absolute_region_power_index[0]; move16(); number_of_bits = 5; move16(); drp_num_bits[0] = 5; move16(); drp_code_bits[0] = (UWord16)add(absolute_region_power_index[0],ESF_ADJUSTMENT_TO_RMS_INDEX); move16(); /* Lower limit the absolute region power indices to -8 and upper limit them to 31. Such extremes may be mathematically impossible anyway.*/ for (region=1; region 0) { absolute_region_power_index[region] = temp1; move16(); } } for (region=1; region 0)) { test(); test(); logic16(); (*p_categorization_control)--; region = category_balances[*p_categorization_control]; move16(); power_categories[region] = sub(power_categories[region],1); move16(); total_mlt_bits = sub(total_mlt_bits,region_mlt_bit_counts[region]); category = power_categories[region]; move16(); raw_mlt_ptr = &mlt_coefs[region*REGION_SIZE]; move16(); temp = sub(category,(NUM_CATEGORIES-1)); test(); if (temp < 0) { region_mlt_bit_counts[region] = vector_huffman(category, absolute_region_power_index[region],raw_mlt_ptr, ®ion_mlt_bits[shl_nocheck(region,2)]); } else { region_mlt_bit_counts[region] = 0; move16(); } total_mlt_bits = add(total_mlt_bits,region_mlt_bit_counts[region]); temp = sub(total_mlt_bits,number_of_available_bits); } /* If too many bits... */ /* Set up for while loop test */ temp1 = sub(total_mlt_bits,number_of_available_bits); temp2 = sub(*p_categorization_control,sub(num_categorization_control_possibilities,1)); test(); test(); logic16(); while ((temp1 > 0) && (temp2 < 0)) { /* operations for while contitions */ test(); test(); logic16(); region = category_balances[*p_categorization_control]; move16(); power_categories[region] = add(power_categories[region],1); move16(); total_mlt_bits = sub(total_mlt_bits,region_mlt_bit_counts[region]); category = power_categories[region]; move16(); temp = extract_l(L_mult0(region,REGION_SIZE)); raw_mlt_ptr = &mlt_coefs[temp]; move16(); temp = sub(category,(NUM_CATEGORIES-1)); test(); if (temp < 0) { region_mlt_bit_counts[region] = vector_huffman(category, absolute_region_power_index[region],raw_mlt_ptr, ®ion_mlt_bits[shl_nocheck(region,2)]); } else { region_mlt_bit_counts[region] = 0; move16(); } total_mlt_bits = add(total_mlt_bits,region_mlt_bit_counts[region]); (*p_categorization_control)++; temp1 = sub(total_mlt_bits,number_of_available_bits); temp2 = sub(*p_categorization_control,sub(num_categorization_control_possibilities,1)); } } /*************************************************************************** Function: vector_huffman Syntax: Word16 vector_huffman(Word16 category, Word16 power_index, Word16 *raw_mlt_ptr, UWord32 *word_ptr) inputs: Word16 category Word16 power_index Word16 *raw_mlt_ptr outputs: number_of_region_bits *word_ptr Description: Huffman encoding for each region based on category and power_index WMOPS: 7kHz | 24kbit | 32kbit -------|--------------|---------------- AVG | 0.03 | 0.03 -------|--------------|---------------- MAX | 0.04 | 0.04 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 0.03 | 0.03 | 0.03 -------|--------------|----------------|---------------- MAX | 0.04 | 0.04 | 0.04 -------|--------------|----------------|---------------- ***************************************************************************/ Word16 vector_huffman(Word16 category, Word16 power_index, Word16 *raw_mlt_ptr, UWord32 *word_ptr) { Word16 inv_of_step_size_times_std_dev; Word16 j,n; Word16 k; Word16 number_of_region_bits; Word16 number_of_non_zero; Word16 vec_dim; Word16 num_vecs; Word16 kmax, kmax_plus_one; Word16 index,signs_index; Word16 *bitcount_table_ptr; UWord16 *code_table_ptr; Word32 code_bits; Word16 number_of_code_bits; UWord32 current_word; Word16 current_word_bits_free; Word32 acca; Word32 accb; Word16 temp; Word16 mytemp; /* new variable in Release 1.2 */ Word16 myacca; /* new variable in Release 1.2 */ /* initialize variables */ vec_dim = vector_dimension[category]; move16(); num_vecs = number_of_vectors[category]; move16(); kmax = max_bin[category]; move16(); kmax_plus_one = add(kmax,1); move16(); current_word = 0L; move16(); current_word_bits_free = 32; move16(); number_of_region_bits = 0; move16(); /* set up table pointers */ bitcount_table_ptr = (Word16 *)table_of_bitcount_tables[category]; code_table_ptr = (UWord16 *) table_of_code_tables[category]; /* compute inverse of step size * standard deviation */ acca = L_mult(step_size_inverse_table[category],standard_deviation_inverse_table[power_index]); acca = L_shr_nocheck(acca,1); acca = L_add(acca,4096); acca = L_shr_nocheck(acca,13); /* * The next two lines are new to Release 1.2 */ mytemp = (Word16)(acca & 0x3); acca = L_shr_nocheck(acca,2); inv_of_step_size_times_std_dev = extract_l(acca); for (n=0; n 0) { signs_index = add(signs_index,1); } temp = sub(k,kmax); test(); if (temp > 0) { k = kmax; move16(); } } acca = L_shr_nocheck(L_mult(index,(kmax_plus_one)),1); index = extract_l(acca); index = add(index,k); raw_mlt_ptr++; } code_bits = *(code_table_ptr+index); number_of_code_bits = add((*(bitcount_table_ptr+index)),number_of_non_zero); number_of_region_bits = add(number_of_region_bits,number_of_code_bits); acca = code_bits << number_of_non_zero; accb = L_deposit_l(signs_index); acca = L_add(acca,accb); code_bits = acca; move32(); /* msb of codebits is transmitted first. */ j = sub(current_word_bits_free,number_of_code_bits); test(); if (j >= 0) { test(); acca = code_bits << j; current_word = L_add(current_word,acca); current_word_bits_free = j; move16(); } else { j = negate(j); acca = L_shr_nocheck(code_bits,j); current_word = L_add(current_word,acca); *word_ptr++ = current_word; move16(); current_word_bits_free = sub(32,j); test(); current_word = code_bits << current_word_bits_free; } } *word_ptr++ = current_word; move16(); return (number_of_region_bits); } ================================================ FILE: deps/pjsip/third_party/g7221/encode/sam2coef.c ================================================ /****************************************************************************** ** ** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C ** > Software Release 2.1 (2008-06) ** (Simple repackaging; no change from 2005-05 Release 2.0 code) ** ** 2004 Polycom, Inc. ** ** All rights reserved. ** ******************************************************************************/ /****************************************************************************** * Filename: samples_to_rmlt_coefs.c * * Purpose: Convert Samples to Reversed MLT (Modulated Lapped Transform) * Coefficients * * The "Reversed MLT" is an overlapped block transform which uses * even symmetry * on the left, odd symmetry on the right and a * Type IV DCT as the block transform. * It is thus similar to a * MLT which uses odd symmetry on the left, even symmetry * on the * right and a Type IV DST as the block transform. In fact, it is * equivalent * to reversing the order of the samples, performing * an MLT and then negating all * the even-numbered coefficients. * ******************************************************************************/ /*************************************************************************** Include files ***************************************************************************/ #include "defs.h" #include "tables.h" #include "count.h" /*************************************************************************** Function: samples_to_rmlt_coefs Syntax: Word16 samples_to_rmlt_coefs(new_samples, old_samples, coefs, dct_length) Word16 *new_samples; Word16 *old_samples; Word16 *coefs; Word16 dct_length; Description: Convert samples to MLT coefficients Design Notes: WMOPS: 7kHz | 24kbit | 32kbit -------|--------------|---------------- AVG | 1.40 | 1.40 -------|--------------|---------------- MAX | 1.40 | 1.40 -------|--------------|---------------- 14kHz | 24kbit | 32kbit | 48kbit -------|--------------|----------------|---------------- AVG | 3.07 | 3.07 | 3.07 -------|--------------|----------------|---------------- MAX | 3.10 | 3.10 | 3.10 -------|--------------|----------------|---------------- ***************************************************************************/ Word16 samples_to_rmlt_coefs(const Word16 *new_samples,Word16 *old_samples,Word16 *coefs,Word16 dct_length) { Word16 index, vals_left,mag_shift,n; Word16 windowed_data[MAX_DCT_LENGTH]; Word16 *old_ptr; const Word16 *new_ptr, *sam_low, *sam_high; Word16 *win_low, *win_high; Word16 *dst_ptr; Word16 neg_win_low; Word16 samp_high; Word16 half_dct_size; Word32 acca; Word32 accb; Word16 temp; Word16 temp1; Word16 temp2; Word16 temp5; half_dct_size = shr_nocheck(dct_length,1); /*++++++++++++++++++++++++++++++++++++++++++++*/ /* Get the first half of the windowed samples */ /*++++++++++++++++++++++++++++++++++++++++++++*/ dst_ptr = windowed_data; move16(); /* address arithmetic */ test(); if (dct_length==DCT_LENGTH) { win_high = samples_to_rmlt_window + half_dct_size; } else { win_high = max_samples_to_rmlt_window + half_dct_size; } win_low = win_high; move16(); /* address arithmetic */ sam_high = old_samples + half_dct_size; sam_low = sam_high; move16(); for (vals_left = half_dct_size;vals_left > 0;vals_left--) { acca = 0L; move32(); acca = L_mac(acca,*--win_low, *--sam_low); acca = L_mac(acca,*win_high++, *sam_high++); temp = itu_round(acca); *dst_ptr++ = temp; move16(); } /*+++++++++++++++++++++++++++++++++++++++++++++*/ /* Get the second half of the windowed samples */ /*+++++++++++++++++++++++++++++++++++++++++++++*/ sam_low = new_samples; move16(); /* address arithmetic */ sam_high = new_samples + dct_length; for (vals_left = half_dct_size; vals_left > 0; vals_left--) { acca = 0L; move32(); acca = L_mac(acca,*--win_high, *sam_low++); neg_win_low = negate(*win_low++); samp_high = *--sam_high; acca = L_mac(acca, neg_win_low, samp_high); temp = itu_round(acca); *dst_ptr++=temp; move16(); } /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ /* Save the new samples for next time, when they will be the old samples */ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ new_ptr = new_samples; move16(); old_ptr = old_samples; move16(); for (vals_left = dct_length;vals_left > 0;vals_left--) { *old_ptr++ = *new_ptr++; move16(); } /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ /* Calculate how many bits to shift up the input to the DCT. */ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ temp1=0; move16(); for(index=0;index 0) { move16(); temp1 = temp2; } } mag_shift=0; move16(); temp = sub(temp1,14000); test(); if (temp >= 0) { mag_shift = 0; move16(); } else { temp = sub(temp1,438); test(); if(temp < 0) temp = add(temp1,1); else { temp = temp1; move16(); } accb = L_mult(temp,9587); acca = L_shr_nocheck(accb,20); temp5 = extract_l(acca); temp = norm_s(temp5); test(); if (temp == 0) { mag_shift = 9; move16(); } else mag_shift = sub(temp,6); } acca = 0L; move32(); for(index=0; index 0) { for(index=0;index> is a signed arithmetic shift (-1 >> 1 == -1) # MULHACK = -DUSE_FLOAT_MUL ######### Define this if your host multiplies floats faster than integers, ######### e.g. on a SPARCstation. # FAST = -DFAST ######### Define together with USE_FLOAT_MUL to enable the GSM library's ######### approximation option for incorrect, but good-enough results. # LTP_CUT = -DLTP_CUT LTP_CUT = ######### Define to enable the GSM library's long-term correlation ######### approximation option---faster, but worse; works for ######### both integer and floating point multiplications. ######### This flag is still in the experimental stage. WAV49 = -DWAV49 # WAV49 = ######### Define to enable the GSM library's option to pack GSM frames ######### in the style used by the WAV #49 format. If you want to write ######### a tool that produces .WAV files which contain GSM-encoded data, ######### define this, and read about the GSM_OPT_WAV49 option in the ######### manual page on gsm_option(3). # Choose a compiler. The code works both with ANSI and K&R-C. # Use -DNeedFunctionPrototypes to compile with, -UNeedFunctionPrototypes to # compile without, function prototypes in the header files. # # You can use the -DSTUPID_COMPILER to circumvent some compilers' # static limits regarding the number of subexpressions in a statement. # CC = cc # CCFLAGS = -c -DSTUPID_COMPILER # CC = /usr/lang/acc # CCFLAGS = -c -O CC = gcc -ansi -pedantic CCFLAGS = -c -O2 -DNeedFunctionPrototypes=1 LD = $(CC) # LD = gcc # LDFLAGS = # If your compiler needs additional flags/libraries, regardless of # the source compiled, configure them here. # CCINC = -I/usr/gnu/lib/gcc-2.1/gcc-lib/sparc-sun-sunos4.1.2/2.1/include ######### Includes needed by $(CC) # LDINC = -L/usr/gnu/lib/gcc-2.1/gcc-lib/sparc-sun-sunos4.1.2/2.1 ######### Library paths needed by $(LD) # LDLIB = -lgcc ######### Additional libraries needed by $(LD) # Where do you want to install libraries, binaries, a header file # and the manual pages? # # Leave INSTALL_ROOT empty (or just don't execute "make install") to # not install gsm and toast outside of this directory. INSTALL_ROOT = # Where do you want to install the gsm library, header file, and manpages? # # Leave GSM_INSTALL_ROOT empty to not install the GSM library outside of # this directory. GSM_INSTALL_ROOT = $(INSTALL_ROOT) GSM_INSTALL_LIB = $(GSM_INSTALL_ROOT)/lib GSM_INSTALL_INC = $(GSM_INSTALL_ROOT)/inc GSM_INSTALL_MAN = $(GSM_INSTALL_ROOT)/man/man3 # Where do you want to install the toast binaries and their manpage? # # Leave TOAST_INSTALL_ROOT empty to not install the toast binaries outside # of this directory. TOAST_INSTALL_ROOT = $(INSTALL_ROOT) TOAST_INSTALL_BIN = $(TOAST_INSTALL_ROOT)/bin TOAST_INSTALL_MAN = $(TOAST_INSTALL_ROOT)/man/man1 # Other tools SHELL = /bin/sh LN = ln BASENAME = basename AR = ar ARFLAGS = cr RMFLAGS = FIND = find COMPRESS = compress COMPRESSFLAGS = # RANLIB = true RANLIB = ranlib # # You shouldn't have to configure below this line if you're porting. # # Local Directories ROOT = . ADDTST = $(ROOT)/add-test TST = $(ROOT)/tst MAN = $(ROOT)/man BIN = $(ROOT)/bin SRC = $(ROOT)/src LIB = $(ROOT)/lib TLS = $(ROOT)/tls INC = $(ROOT)/inc # Flags # DEBUG = -DNDEBUG ######### Remove -DNDEBUG to enable assertions. CFLAGS = $(CCFLAGS) $(SASR) $(DEBUG) $(MULHACK) $(FAST) $(LTP_CUT) \ $(WAV49) $(CCINC) -I$(INC) ######### It's $(CC) $(CFLAGS) LFLAGS = $(LDFLAGS) $(LDINC) ######### It's $(LD) $(LFLAGS) # Targets LIBGSM = $(LIB)/libgsm.a TOAST = $(BIN)/toast UNTOAST = $(BIN)/untoast TCAT = $(BIN)/tcat # Headers GSM_HEADERS = $(INC)/gsm.h HEADERS = $(INC)/proto.h \ $(INC)/unproto.h \ $(INC)/config.h \ $(INC)/private.h \ $(INC)/gsm.h \ $(INC)/toast.h \ $(TLS)/taste.h # Sources GSM_SOURCES = $(SRC)/add.c \ $(SRC)/code.c \ $(SRC)/debug.c \ $(SRC)/decode.c \ $(SRC)/long_term.c \ $(SRC)/lpc.c \ $(SRC)/preprocess.c \ $(SRC)/rpe.c \ $(SRC)/gsm_destroy.c \ $(SRC)/gsm_decode.c \ $(SRC)/gsm_encode.c \ $(SRC)/gsm_explode.c \ $(SRC)/gsm_implode.c \ $(SRC)/gsm_create.c \ $(SRC)/gsm_print.c \ $(SRC)/gsm_option.c \ $(SRC)/short_term.c \ $(SRC)/table.c TOAST_SOURCES = $(SRC)/toast.c \ $(SRC)/toast_lin.c \ $(SRC)/toast_ulaw.c \ $(SRC)/toast_alaw.c \ $(SRC)/toast_audio.c SOURCES = $(GSM_SOURCES) \ $(TOAST_SOURCES) \ $(ADDTST)/add_test.c \ $(TLS)/sour.c \ $(TLS)/ginger.c \ $(TLS)/sour1.dta \ $(TLS)/sour2.dta \ $(TLS)/bitter.c \ $(TLS)/bitter.dta \ $(TLS)/taste.c \ $(TLS)/sweet.c \ $(TST)/cod2lin.c \ $(TST)/cod2txt.c \ $(TST)/gsm2cod.c \ $(TST)/lin2cod.c \ $(TST)/lin2txt.c # Object files GSM_OBJECTS = $(SRC)/add.o \ $(SRC)/code.o \ $(SRC)/debug.o \ $(SRC)/decode.o \ $(SRC)/long_term.o \ $(SRC)/lpc.o \ $(SRC)/preprocess.o \ $(SRC)/rpe.o \ $(SRC)/gsm_destroy.o \ $(SRC)/gsm_decode.o \ $(SRC)/gsm_encode.o \ $(SRC)/gsm_explode.o \ $(SRC)/gsm_implode.o \ $(SRC)/gsm_create.o \ $(SRC)/gsm_print.o \ $(SRC)/gsm_option.o \ $(SRC)/short_term.o \ $(SRC)/table.o TOAST_OBJECTS = $(SRC)/toast.o \ $(SRC)/toast_lin.o \ $(SRC)/toast_ulaw.o \ $(SRC)/toast_alaw.o \ $(SRC)/toast_audio.o OBJECTS = $(GSM_OBJECTS) $(TOAST_OBJECTS) # Manuals GSM_MANUALS = $(MAN)/gsm.3 \ $(MAN)/gsm_explode.3 \ $(MAN)/gsm_option.3 \ $(MAN)/gsm_print.3 TOAST_MANUALS = $(MAN)/toast.1 MANUALS = $(GSM_MANUALS) $(TOAST_MANUALS) $(MAN)/bitter.1 # Other stuff in the distribution STUFF = ChangeLog \ INSTALL \ MACHINES \ MANIFEST \ Makefile \ README \ $(ADDTST)/add_test.dta \ $(TLS)/bitter.dta \ $(TST)/run # Install targets GSM_INSTALL_TARGETS = \ $(GSM_INSTALL_LIB)/libgsm.a \ $(GSM_INSTALL_INC)/gsm.h \ $(GSM_INSTALL_MAN)/gsm.3 \ $(GSM_INSTALL_MAN)/gsm_explode.3 \ $(GSM_INSTALL_MAN)/gsm_option.3 \ $(GSM_INSTALL_MAN)/gsm_print.3 TOAST_INSTALL_TARGETS = \ $(TOAST_INSTALL_BIN)/toast \ $(TOAST_INSTALL_BIN)/tcat \ $(TOAST_INSTALL_BIN)/untoast \ $(TOAST_INSTALL_MAN)/toast.1 # Default rules .c.o: $(CC) $(CFLAGS) $? @-mv `$(BASENAME) $@` $@ > /dev/null 2>&1 # Target rules all: $(LIBGSM) $(TOAST) $(TCAT) $(UNTOAST) @-echo $(ROOT): Done. tst: $(TST)/lin2cod $(TST)/cod2lin $(TOAST) $(TST)/test-result @-echo tst: Done. addtst: $(ADDTST)/add $(ADDTST)/add_test.dta $(ADDTST)/add < $(ADDTST)/add_test.dta > /dev/null @-echo addtst: Done. misc: $(TLS)/sweet $(TLS)/bitter $(TLS)/sour $(TLS)/ginger \ $(TST)/lin2txt $(TST)/cod2txt $(TST)/gsm2cod @-echo misc: Done. install: toastinstall gsminstall @-echo install: Done. # The basic API: libgsm $(LIBGSM): $(LIB) $(GSM_OBJECTS) -rm $(RMFLAGS) $(LIBGSM) $(AR) $(ARFLAGS) $(LIBGSM) $(GSM_OBJECTS) $(RANLIB) $(LIBGSM) # Toast, Untoast and Tcat -- the compress-like frontends to gsm. $(TOAST): $(BIN) $(TOAST_OBJECTS) $(LIBGSM) $(LD) $(LFLAGS) -o $(TOAST) $(TOAST_OBJECTS) $(LIBGSM) $(LDLIB) $(UNTOAST): $(BIN) $(TOAST) -rm $(RMFLAGS) $(UNTOAST) $(LN) $(TOAST) $(UNTOAST) $(TCAT): $(BIN) $(TOAST) -rm $(RMFLAGS) $(TCAT) $(LN) $(TOAST) $(TCAT) # The local bin and lib directories $(BIN): if [ ! -d $(BIN) ] ; then mkdir $(BIN) ; fi $(LIB): if [ ! -d $(LIB) ] ; then mkdir $(LIB) ; fi # Installation gsminstall: -if [ x"$(GSM_INSTALL_ROOT)" != x ] ; then \ make $(GSM_INSTALL_TARGETS) ; \ fi toastinstall: -if [ x"$(TOAST_INSTALL_ROOT)" != x ]; then \ make $(TOAST_INSTALL_TARGETS); \ fi gsmuninstall: -if [ x"$(GSM_INSTALL_ROOT)" != x ] ; then \ rm $(RMFLAGS) $(GSM_INSTALL_TARGETS) ; \ fi toastuninstall: -if [ x"$(TOAST_INSTALL_ROOT)" != x ] ; then \ rm $(RMFLAGS) $(TOAST_INSTALL_TARGETS); \ fi $(TOAST_INSTALL_BIN)/toast: $(TOAST) -rm $@ cp $(TOAST) $@ chmod 755 $@ $(TOAST_INSTALL_BIN)/untoast: $(TOAST_INSTALL_BIN)/toast -rm $@ ln $? $@ $(TOAST_INSTALL_BIN)/tcat: $(TOAST_INSTALL_BIN)/toast -rm $@ ln $? $@ $(TOAST_INSTALL_MAN)/toast.1: $(MAN)/toast.1 -rm $@ cp $? $@ chmod 444 $@ $(GSM_INSTALL_MAN)/gsm.3: $(MAN)/gsm.3 -rm $@ cp $? $@ chmod 444 $@ $(GSM_INSTALL_MAN)/gsm_option.3: $(MAN)/gsm_option.3 -rm $@ cp $? $@ chmod 444 $@ $(GSM_INSTALL_MAN)/gsm_explode.3: $(MAN)/gsm_explode.3 -rm $@ cp $? $@ chmod 444 $@ $(GSM_INSTALL_MAN)/gsm_print.3: $(MAN)/gsm_print.3 -rm $@ cp $? $@ chmod 444 $@ $(GSM_INSTALL_INC)/gsm.h: $(INC)/gsm.h -rm $@ cp $? $@ chmod 444 $@ $(GSM_INSTALL_LIB)/libgsm.a: $(LIBGSM) -rm $@ cp $? $@ chmod 444 $@ # Distribution dist: gsm-1.0.tar.Z @echo dist: Done. gsm-1.0.tar.Z: $(STUFF) $(SOURCES) $(HEADERS) $(MANUALS) ( cd $(ROOT)/..; \ tar cvf - `cat $(ROOT)/gsm-1.0/MANIFEST \ | sed '/^#/d'` \ ) | $(COMPRESS) $(COMPRESSFLAGS) > $(ROOT)/gsm-1.0.tar.Z # Clean uninstall: toastuninstall gsmuninstall @-echo uninstall: Done. semi-clean: -rm $(RMFLAGS) */*.o \ $(TST)/lin2cod $(TST)/lin2txt \ $(TST)/cod2lin $(TST)/cod2txt \ $(TST)/gsm2cod \ $(TST)/*.*.* -$(FIND) . \( -name core -o -name foo \) \ -print | xargs rm $(RMFLAGS) clean: semi-clean -rm $(RMFLAGS) $(LIBGSM) $(ADDTST)/add \ $(TOAST) $(TCAT) $(UNTOAST) \ $(ROOT)/gsm-1.0.tar.Z # Two tools that helped me generate gsm_encode.c and gsm_decode.c, # but aren't generally needed to port this. $(TLS)/sweet: $(TLS)/sweet.o $(TLS)/taste.o $(LD) $(LFLAGS) -o $(TLS)/sweet \ $(TLS)/sweet.o $(TLS)/taste.o $(LDLIB) $(TLS)/bitter: $(TLS)/bitter.o $(TLS)/taste.o $(LD) $(LFLAGS) -o $(TLS)/bitter \ $(TLS)/bitter.o $(TLS)/taste.o $(LDLIB) # A version of the same family that Jeff Chilton used to implement # the WAV #49 GSM format. $(TLS)/ginger: $(TLS)/ginger.o $(TLS)/taste.o $(LD) $(LFLAGS) -o $(TLS)/ginger \ $(TLS)/ginger.o $(TLS)/taste.o $(LDLIB) $(TLS)/sour: $(TLS)/sour.o $(TLS)/taste.o $(LD) $(LFLAGS) -o $(TLS)/sour \ $(TLS)/sour.o $(TLS)/taste.o $(LDLIB) # Run $(ADDTST)/add < $(ADDTST)/add_test.dta to make sure the # basic arithmetic functions work as intended. $(ADDTST)/add: $(ADDTST)/add_test.o $(LD) $(LFLAGS) -o $(ADDTST)/add $(ADDTST)/add_test.o $(LDLIB) # Various conversion programs between linear, text, .gsm and the code # format used by the tests we ran (.cod). We paid for the test data, # so I guess we can't just provide them with this package. Still, # if you happen to have them lying around, here's the code. # # You can use gsm2cod | cod2txt independently to look at what's # coded inside the compressed frames, although this shouldn't be # hard to roll on your own using the gsm_print() function from # the API. $(TST)/test-result: $(TST)/lin2cod $(TST)/cod2lin $(TOAST) $(TST)/run ( cd $(TST); ./run ) $(TST)/lin2txt: $(TST)/lin2txt.o $(LIBGSM) $(LD) $(LFLAGS) -o $(TST)/lin2txt \ $(TST)/lin2txt.o $(LIBGSM) $(LDLIB) $(TST)/lin2cod: $(TST)/lin2cod.o $(LIBGSM) $(LD) $(LFLAGS) -o $(TST)/lin2cod \ $(TST)/lin2cod.o $(LIBGSM) $(LDLIB) $(TST)/gsm2cod: $(TST)/gsm2cod.o $(LIBGSM) $(LD) $(LFLAGS) -o $(TST)/gsm2cod \ $(TST)/gsm2cod.o $(LIBGSM) $(LDLIB) $(TST)/cod2txt: $(TST)/cod2txt.o $(LIBGSM) $(LD) $(LFLAGS) -o $(TST)/cod2txt \ $(TST)/cod2txt.o $(LIBGSM) $(LDLIB) $(TST)/cod2lin: $(TST)/cod2lin.o $(LIBGSM) $(LD) $(LFLAGS) -o $(TST)/cod2lin \ $(TST)/cod2lin.o $(LIBGSM) $(LDLIB) ================================================ FILE: deps/pjsip/third_party/gsm/README ================================================ GSM 06.10 13 kbit/s RPE/LTP speech compression available -------------------------------------------------------- The Communications and Operating Systems Research Group (KBS) at the Technische Universitaet Berlin is currently working on a set of UNIX-based tools for computer-mediated telecooperation that will be made freely available. As part of this effort we are publishing an implementation of the European GSM 06.10 provisional standard for full-rate speech transcoding, prI-ETS 300 036, which uses RPE/LTP (residual pulse excitation/long term prediction) coding at 13 kbit/s. GSM 06.10 compresses frames of 160 13-bit samples (8 kHz sampling rate, i.e. a frame rate of 50 Hz) into 260 bits; for compatibility with typical UNIX applications, our implementation turns frames of 160 16-bit linear samples into 33-byte frames (1650 Bytes/s). The quality of the algorithm is good enough for reliable speaker recognition; even music often survives transcoding in recognizable form (given the bandwidth limitations of 8 kHz sampling rate). The interfaces offered are a front end modelled after compress(1), and a library API. Compression and decompression run faster than realtime on most SPARCstations. The implementation has been verified against the ETSI standard test patterns. Jutta Degener (jutta@cs.tu-berlin.de) Carsten Bormann (cabo@cs.tu-berlin.de) Communications and Operating Systems Research Group, TU Berlin Fax: +49.30.31425156, Phone: +49.30.31424315 -- Copyright 1992 by Jutta Degener and Carsten Bormann, Technische Universitaet Berlin. See the accompanying file "COPYRIGHT" for details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. ================================================ FILE: deps/pjsip/third_party/gsm/add-test/add_test.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/add_test.c,v 1.2 1994/05/10 20:18:17 jutta Exp $ */ #include #include #include #include "gsm.h" #include "../src/add.c" int interactive = 1; char * opname; longword L_op1, L_op2, L_expect; word op1, op2, expect; int do_expect; word M_gsm_add P((word op1, word op2)); word M_gsm_sub P((word op1, word op2)); word M_gsm_mult P((word op1, word op2)); word M_gsm_mult_r P((word op1, word op2)); word M_gsm_abs P((word op1)); longword M_gsm_L_mult P((word op1, word op2)); longword M_gsm_L_add P((longword op1, longword op2)); help() { puts( " add a b sub a b mult a b div a b" ); puts( "L_add A B L_sub A B L_mult A B mult_r a b" ); puts( "" ); puts( "abs a norm a >> a b << a b" ); puts( " L_>> A B L_<< A B" ); } char * strtek P2((str, sep), char * str, char * sep) { static char * S = (char *)0; char * c, * base; if (str) S = str; if (!S || !*S) return (char *)0; /* Skip delimiters. */ while (*S) { for (c = sep; *c && *c != *S; c++) ; if (*c) *S++ = 0; else break; } base = S; /* Skip non-delimiters. */ for (base = S; *S; S++) { for (c = sep; *c; c++) if (*c == *S) { *S++ = 0; return base; } } return base == S ? (char *)0 : base; } long value P1((s), char * s) { switch (*s) { case '-': switch (s[1]) { case '\0': return MIN_WORD; case '-': return MIN_LONGWORD; default: break; } break; case '+': switch (s[1]) { case '\0': return MAX_WORD; case '+': return MAX_LONGWORD; default: break; } default: break; } return strtol(s, (char **)0, 0); } char * parse P1((buf), char * buf) { char * s, * a; long l; if (a = strchr(buf, '=')) *a++ = 0; opname = s = strtek(buf, " \t("); if (!s) return (char *)0; op1 = op2 = L_op1 = L_op2 = 0; if (s = strtek( (char *)0, "( \t,")) { op1 = L_op1 = value(s); if (s = strtek( (char *)0, ", \t)")) op2 = L_op2 = value(s); } if (a) { do_expect = 1; while (*a == ' ' || *a == '\t') a++; expect = L_expect = value(a); } return opname; } void fprint_word P2((f, w), FILE * f, word w) { if (!w) putc('0', f); else fprintf(f, "0x%4.4x (%d%s)", (unsigned int)w, (int)w, w == MIN_WORD? "/-" : (w == MAX_WORD ? "/+" : "")); } void print_word P1((w), word w) { fprint_word( stdout, w ); } void fprint_longword P2((f, w), FILE * f, longword w) { if (!w) putc('0', f); else fprintf(f, "0x%8.8x (%ld%s)", w, w, w == MIN_WORD ? "/-" : (w == MAX_WORD ? "/+" : (w == MIN_LONGWORD ? "/--" : (w == MAX_LONGWORD ? "/++" : "")))); } void print_longword P1((w),longword w) { fprint_longword(stdout, w); } void do_longword P1((w), longword w) { if (interactive) print_longword(w); if (do_expect) { if (w != L_expect) { if (!interactive) fprint_longword(stderr, w); fprintf(stderr, " != %s (%ld, %ld) -- expected ", opname, L_op1, L_op2 ); fprint_longword(stderr, L_expect); putc( '\n', stderr ); } } else if (interactive) putchar('\n'); } void do_word P1((w), word w ) { if (interactive) print_word(w); if (do_expect) { if (w != expect) { if (!interactive) fprint_word(stderr, w); fprintf(stderr, " != %s (%ld, %ld) -- expected ", opname, L_op1, L_op2 ); fprint_word(stderr, expect); putc('\n', stderr); } } else if (interactive) putchar('\n'); } int main(ac, av) char ** av; { char buf[299]; char * c; FILE * in; if (ac > 2) { fprintf(stderr, "Usage: %s [filename]\n", av[0]); fail: #ifdef EXIT_FAILURE exit(EXIT_FAILURE); #else exit(1); #endif } if (ac < 2) in = stdin; else if (!(in = fopen(av[1], "r"))) { perror(av[1]); fprintf(stderr, "%s: cannot open file \"%s\" for reading\n", av[0], av[1]); goto fail; } interactive = isatty(fileno(in)); for (;;) { if (interactive) fprintf(stderr, "? "); if (!fgets(buf, sizeof(buf), in)) exit(0); if (c = strchr(buf, '\n')) *c = 0; if (*buf == ';' || *buf == '#') continue; if (*buf == '\'') { puts(buf + 1); continue; } if (*buf == '\"') { fprintf(stderr, "%s\n", buf + 1); continue; } c = parse(buf); if (!c) continue; if (!strcmp(c, "add")) { do_word( gsm_add( op1, op2 )); continue; } if (!strcmp(c, "M_add")) { do_word( M_gsm_add( op1, op2 )); continue; } if (!strcmp(c, "sub")) { do_word( gsm_sub( op1, op2 )); continue; } if (!strcmp(c, "M_sub")) { do_word( M_gsm_sub( op1, op2 )); continue; } if (!strcmp(c, "mult")) { do_word( gsm_mult( op1, op2 )); continue; } if (!strcmp(c, "M_mult")) { do_word( M_gsm_mult( op1, op2 )); continue; } if (!strcmp(c, "mult_r")) { do_word( gsm_mult_r(op1, op2)); continue; } if (!strcmp(c, "M_mult_r")) { do_word( M_gsm_mult_r(op1, op2)); continue; } if (!strcmp(c, "abs" )) { do_word( gsm_abs(op1) ); continue; } if (!strcmp(c, "M_abs" )) { do_word( M_gsm_abs(op1) ); continue; } if (!strcmp(c, "div" )) { do_word( gsm_div( op1, op2 )); continue; } if (!strcmp(c, "norm" )) { do_word( gsm_norm(L_op1)); continue; } if (!strcmp(c, "<<" )) { do_word( gsm_asl( op1, op2)); continue; } if (!strcmp(c, ">>" )) { do_word( gsm_asr( op1, op2 )); continue; } if (!strcmp(c, "L_mult")) { do_longword( gsm_L_mult( op1, op2 )); continue; } if (!strcmp(c, "M_L_mult")) { do_longword( M_gsm_L_mult( op1, op2 )); continue; } if (!strcmp(c, "L_add" )) { do_longword( gsm_L_add( L_op1, L_op2 )); continue; } if (!strcmp(c, "M_L_add" )) { do_longword( M_gsm_L_add( L_op1, L_op2 )); continue; } if (!strcmp(c, "L_sub" )) { do_longword( gsm_L_sub( L_op1, L_op2 )); continue; } if (!strcmp(c, "L_<<" )) { do_longword( gsm_L_asl( L_op1, L_op2 )); continue; } if (!strcmp(c, "L_>>")) { do_longword( gsm_L_asr( L_op1, L_op2 )); continue; } help(); } } #include "private.h" /* * Function stubs for macro implementations of commonly used * math functions */ word M_gsm_add P2((op1, op2),word op1, word op2) { longword ltmp; return GSM_ADD(op1, op2); } word M_gsm_sub P2((op1, op2), word op1, word op2) { longword ltmp; return GSM_SUB(op1, op2); } word M_gsm_mult P2((op1, op2), word op1, word op2) { return GSM_MULT(op1, op2); } word M_gsm_mult_r P2((op1, op2), word op1, word op2) { return GSM_MULT_R(op1, op2); } word M_gsm_abs P1((op1), word op1) { return GSM_ABS(op1); } longword M_gsm_L_mult P2((op1, op2), word op1, word op2) { return GSM_L_MULT(op1, op2); } longword M_gsm_L_add P2((op1, op2), longword op1, longword op2) { ulongword utmp; return GSM_L_ADD(op1, op2); } ================================================ FILE: deps/pjsip/third_party/gsm/add-test/add_test.dta ================================================ ; ; Copyright 1992 by Jutta Degener and Carsten Bormann, Technische ; Universitaet Berlin. See the accompanying file "COPYRIGHT" for ; details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. ; ; ; Lines starting with ' (in the first col) are echoed. ; Lines starting with " (in the first col) are echoed to stderr. ; Lines starting with ; or empty lines are ignored. ; ; The part after (including) a trailing '=' is what you expect; ; there will be output if the result is different. ; ; - and + by itself mean MIN_WORD and MAX_WORD, respectively; ; -- and ++ mean MIN_LONGWORD and MAX_LONGWORD. ; 'test the basic arithmetic operations used for the rpe-ltd filtering. ' 'add ================ ' basic add 0 0 = 0 add 7 4 = 11 add 4 6 = 10 add 1 1 = 2 ' negative operands add -7 4 = -3 add 4 -6 = -2 add -1 -3 = -4 add 7 -4 = 3 add -4 6 = 2 ' positive overflow ; (max-word = 32767) add + 1 = + add + + = + add -1 + = 32766 add 32766 2 = + add 1 32766 = + ' underflow ; (min-word = 32768) add - -1 = - add - - = - add 1 - = -32767 add -32767 -2 = - add -1 -32766 = -32767 add -32767 -1 = - add - + = -1 add + - = -1 add 0 - = - add 0 + = + ' 'L_add ================ ' basic L_add 0 0 = 0 L_add 7 4 = 11 L_add 4 6 = 10 L_add 1 1 = 2 ' negative operands L_add -7 4 = -3 L_add 4 -6 = -2 L_add -1 -3 = -4 L_add 7 -4 = 3 L_add -4 6 = 2 L_add 0 -1 = -1 ' positive overflow ; (max-longword = 2147483647) L_add ++ 1 = ++ L_add ++ ++ = ++ L_add -1 ++ = 2147483646 L_add 2147483646 2 = ++ L_add 1 2147483645 = 2147483646 ' underflow ; (min-longword = -2147483648) L_add -- -1 = -- L_add -- -- = -- L_add 1 -- = -2147483647 L_add -2147483647 -2 = -- L_add -1 -2147483646 = -2147483647 L_add -2147483647 -1 = -- L_add -- ++ = -1 L_add ++ -- = -1 L_add 0 -- = -- L_add 0 ++ = ++ ' 'sub ================ ' basic sub 0 0 = 0 sub 7 4 = 3 sub 4 6 = -2 sub 1 0 = 1 ' negative operands sub -7 4 = -11 sub 4 -6 = 10 sub -1 -3 = 2 sub 7 -4 = 11 sub -4 6 = -10 ' positive overflow ; (max-word = 32767) sub 1 - = + sub + + = 0 sub + 0 = + sub + -1 = + sub + 1 = 32766 sub 1 + = -32766 sub 0 + = -32767 ' underflow ; (min-word = 32768) sub - -1 = -32767 sub - 1 = - sub - - = 0 sub - + = - sub + - = + sub 1 - = + sub -1 - = + sub -32767 2 = - sub 0 - = + ' 'L_sub ================ ' basic L_sub 0 0 = 0 L_sub 7 4 = 3 L_sub 4 6 = -2 L_sub 1 0 = 1 ' negative operands L_sub -7 4 = -11 L_sub 4 -6 = 10 L_sub -1 -3 = 2 L_sub 7 -4 = 11 L_sub -4 6 = -10 ' positive overflow L_sub 1 -- = ++ L_sub ++ ++ = 0 L_sub ++ 0 = ++ L_sub ++ -1 = ++ L_sub ++ 1 = 2147483646 L_sub 1 ++ = -2147483646 L_sub 0 ++ = -2147483647 ' underflow L_sub -- -1 = -2147483647 L_sub -- 1 = -- L_sub -- -- = 0 L_sub -- ++ = -- L_sub + -- = ++ L_sub 1 -- = ++ L_sub -1 -- = ++ L_sub -2147483647 2 = -- L_sub 0 -- = ++ ' 'abs ================ ' basic abs 0 = 0 abs 2 = 2 abs -459 = 459 ' overflow abs + = + abs - = + abs -32767 = + abs 32766 = 32766 abs -32766 = 32766 ' 'mult ================ ; actually, a * b >> 15 ' basic mult 0 0 = 0 mult 0x100 0x100 = 2 mult 4711 0x4000 = 2355 ' negative operands mult -1 0 = 0 mult -0x100 0x100 = -2 mult 0x100 -0x100 = -2 mult -0x100 -0x100 = 2 mult -4711 0x4000 = -2356 mult 4711 -0x4000 = -2356 mult -4711 -0x4000 = 2355 ' overflow mult + + = 32766 mult + 0x4000 = 0x3fff mult 0x4000 + = 0x3fff mult + 1 = 0 mult + 2 = 1 mult + 3 = 2 ' underflow mult - - = + mult - + = -32767 mult + - = -32767 mult - 1 = -1 mult - 2 = -2 mult - 3 = -3 ' 'mult_r ================ ; actually, (a * b + 16384) >> 15 ' basic mult_r 0 0 = 0 mult_r 0x100 0x100 = 2 mult_r 4711 0x4000 = 2356 ' negative operands mult_r -1 0 = 0 mult_r -0x100 0x100 = -2 mult_r 0x100 -0x100 = -2 mult_r -0x100 -0x100 = 2 mult_r -4711 0x4000 = -2355 mult_r 4711 -0x4000 = -2355 mult_r -4711 -0x4000 = 2356 ' overflow mult_r + + = 32766 mult_r + 32766 = 32765 mult_r 32766 + = 32765 mult_r + 0x4000 = 0x4000 mult_r 0x4000 + = 0x4000 mult_r + 0x4001 = 0x4000 mult_r 0x4001 + = 0x4000 mult_r + 2 = 2 mult_r + 1 = 1 mult_r 1 + = 1 mult_r + 0 = 0 mult_r 0 + = 0 ' underflow mult_r - - = + mult_r - + = -32767 mult_r + - = -32767 mult_r - 1 = -1 mult_r - 2 = -2 mult_r - 3 = -3 ' 'L_mult ================ ; actually, (a * b) << 1 ; assert (a != MIN_WORD && b != MIN_WORD) ' basic L_mult 0 0 = 0 L_mult 2 3 = 12 L_mult 4711 5 = 47110 ' negative operands L_mult -2 3 = -12 L_mult 2 -3 = -12 L_mult -2 -3 = 12 L_mult -4711 5 = -47110 L_mult 4711 -5 = -47110 L_mult -4711 -5 = 47110 ' overflow L_mult + + = 2147352578 L_mult + -32767 = -2147352578 L_mult -32767 + = -2147352578 L_mult + 2 = 131068 L_mult + 1 = 65534 L_mult 1 + = 65534 L_mult + 0 = 0 L_mult 0 + = 0 ' 'div ================ ; actually, (32767 * a) / b ; assert (a > 0 && b >= a) ' basic div 1 1 = + div 4711 4711 = + div 5 10 = 0x4000 div 5 20 = 0x2000 div 5 40 = 0x1000 ' overflow div + + = + div 0x4000 + = 0x4000 div 1 + = 1 div 1 2 = 0x4000 ' 'norm ================ ' positive norm 1 = 30 norm 2 = 29 norm 3 = 29 norm 4 = 28 norm 5 = 28 ; etc, etc... norm 0x08000000 = 3 norm 0x10000000 = 2 norm 0x20000000 = 1 norm 0x20000001 = 1 norm 0x3fffffff = 1 norm 0x40000000 = 0 norm 0x40000001 = 0 norm 0x4ffffffe = 0 norm ++ = 0 ' negative norm -1 = 31 norm -2 = 30 norm -3 = 29 norm -4 = 29 norm -5 = 28 ; etc, etc... norm 0x4fffffff = 0 norm -- = 0 ' '>> ================ ' basic >> 1 1 = 0 >> 4 2 = 1 >> 0x1100 5 = 0x88 ' negative operand >> 1 -1 = 2 >> 1 -2 = 4 >> 0x88 -5 = 0x1100 ' overflow >> -1 4711 = -1 >> 1 4711 = 0 >> -4711 4711 = -1 >> 4711 4711 = 0 >> + 1 = 16383 >> - 1 = -16384 ' 'L_>> ================ ' basic L_>> 1 1 = 0 L_>> 4 2 = 1 L_>> 0x1100 5 = 0x88 ' negative operand L_>> 1 -1 = 2 L_>> 1 -2 = 4 L_>> 0x88 -5 = 0x1100 ' overflow L_>> -1 4711 = -1 L_>> 1 4711 = 0 L_>> -4711 4711 = -1 L_>> 4711 4711 = 0 L_>> ++ 1 = 1073741823 L_>> -- 1 = -1073741824 ' '<< ================ ' basic << 1 1 = 2 << 4 2 = 16 << 0x0088 5 = 0x1100 ' negative operand << 1 -1 = 0 << 4 -2 = 1 << 0x1100 -5 = 0x0088 ' overflow << -1 4711 = 0 << 1 4711 = 0 << -4711 4711 = 0 << 4711 4711 = 0 << 4711 -4711 = 0 << -4711 -4711 = -1 << + 1 = 0xfffe << -1 1 = 0xfffe << - 1 = 0 ' 'L_<< ================ ' basic L_<< 1 1 = 2 L_<< 4 2 = 16 L_<< 0x0088 5 = 0x1100 ' negative operand L_<< 1 -1 = 0 L_<< 4 -2 = 1 L_<< 0x1100 -5 = 0x0088 ' overflow L_<< -1 4711 = 0 L_<< 1 4711 = 0 L_<< -4711 4711 = 0 L_<< 4711 4711 = 0 L_<< 4711 -4711 = 0 L_<< -4711 -4711 = -1 L_<< ++ 1 = -2 L_<< -1 1 = -2 L_<< -- 1 = 0 'macros ' 'add ================ ' basic M_add 0 0 = 0 M_add 7 4 = 11 M_add 4 6 = 10 M_add 1 1 = 2 ' negative operands M_add -7 4 = -3 M_add 4 -6 = -2 M_add -1 -3 = -4 M_add 7 -4 = 3 M_add -4 6 = 2 ' positive overflow ; (max-word = 32767) M_add + 1 = + M_add + + = + M_add -1 + = 32766 M_add 32766 2 = + M_add 1 32766 = + ' underflow ; (min-word = 32768) M_add - -1 = - M_add - - = - M_add 1 - = -32767 M_add -32767 -2 = - M_add -1 -32766 = -32767 M_add -32767 -1 = - M_add - + = -1 M_add + - = -1 M_add 0 - = - M_add 0 + = + ' 'L_add ================ ' basic M_L_add 0 0 = 0 M_L_add 7 4 = 11 M_L_add 4 6 = 10 M_L_add 1 1 = 2 ' negative operands M_L_add -7 4 = -3 M_L_add 4 -6 = -2 M_L_add -1 -3 = -4 M_L_add 7 -4 = 3 M_L_add -4 6 = 2 M_L_add 0 -1 = -1 ' positive overflow ; (max-longword = 2147483647) M_L_add ++ 1 = ++ M_L_add ++ ++ = ++ M_L_add -1 ++ = 2147483646 M_L_add 2147483646 2 = ++ M_L_add 1 2147483645 = 2147483646 ' underflow ; (min-longword = -2147483648) M_L_add -- -1 = -- M_L_add -- -- = -- M_L_add 1 -- = -2147483647 M_L_add -2147483647 -2 = -- M_L_add -1 -2147483646 = -2147483647 M_L_add -2147483647 -1 = -- M_L_add -- ++ = -1 M_L_add ++ -- = -1 M_L_add 0 -- = -- M_L_add 0 ++ = ++ ' 'sub ================ ' basic M_sub 0 0 = 0 M_sub 7 4 = 3 M_sub 4 6 = -2 M_sub 1 0 = 1 ' negative operands M_sub -7 4 = -11 M_sub 4 -6 = 10 M_sub -1 -3 = 2 M_sub 7 -4 = 11 M_sub -4 6 = -10 ' positive overflow ; (max-word = 32767) M_sub 1 - = + M_sub + + = 0 M_sub + 0 = + M_sub + -1 = + M_sub + 1 = 32766 M_sub 1 + = -32766 M_sub 0 + = -32767 ' underflow ; (min-word = 32768) M_sub - -1 = -32767 M_sub - 1 = - M_sub - - = 0 M_sub - + = - M_sub + - = + M_sub 1 - = + M_sub -1 - = + M_sub -32767 2 = - M_sub 0 - = + ' ' 'abs ================ ' basic M_abs 0 = 0 M_abs 2 = 2 M_abs -459 = 459 ' overflow M_abs + = + M_abs - = + M_abs -32767 = + M_abs 32766 = 32766 M_abs -32766 = 32766 ' 'mult ================ ; actually, a * b >> 15 ' basic M_mult 0 0 = 0 M_mult 0x100 0x100 = 2 M_mult 4711 0x4000 = 2355 ' negative operands M_mult -1 0 = 0 M_mult -0x100 0x100 = -2 M_mult 0x100 -0x100 = -2 M_mult -0x100 -0x100 = 2 M_mult -4711 0x4000 = -2356 M_mult 4711 -0x4000 = -2356 M_mult -4711 -0x4000 = 2355 ' overflow M_mult + + = 32766 M_mult + 0x4000 = 0x3fff M_mult 0x4000 + = 0x3fff M_mult + 1 = 0 M_mult + 2 = 1 M_mult + 3 = 2 ' underflow ; M_mult - - = + assert !(a == b && b == MIN_WORD) M_mult - -32767 = + M_mult -32767 - = + M_mult - + = -32767 M_mult + - = -32767 M_mult - 1 = -1 M_mult - 2 = -2 M_mult - 3 = -3 ' 'mult_r ================ ; actually, (a * b + 16384) >> 15 ' basic M_mult_r 0 0 = 0 M_mult_r 0x100 0x100 = 2 M_mult_r 4711 0x4000 = 2356 ' negative operands M_mult_r -1 0 = 0 M_mult_r -0x100 0x100 = -2 M_mult_r 0x100 -0x100 = -2 M_mult_r -0x100 -0x100 = 2 M_mult_r -4711 0x4000 = -2355 M_mult_r 4711 -0x4000 = -2355 M_mult_r -4711 -0x4000 = 2356 ' overflow M_mult_r + + = 32766 M_mult_r + 32766 = 32765 M_mult_r 32766 + = 32765 M_mult_r + 0x4000 = 0x4000 M_mult_r 0x4000 + = 0x4000 M_mult_r + 0x4001 = 0x4000 M_mult_r 0x4001 + = 0x4000 M_mult_r + 2 = 2 M_mult_r + 1 = 1 M_mult_r 1 + = 1 M_mult_r + 0 = 0 M_mult_r 0 + = 0 ' underflow ; M_mult_r - - = + assert !(a == b && b == MIN_WORD) M_mult_r - -32767 = + M_mult_r -32767 - = + M_mult_r - + = -32767 M_mult_r + - = -32767 M_mult_r - 1 = -1 M_mult_r - 2 = -2 M_mult_r - 3 = -3 ' 'L_mult ================ ; actually, (a * b) << 1 ; assert (a != MIN_WORD && b != MIN_WORD) ' basic M_L_mult 0 0 = 0 M_L_mult 2 3 = 12 M_L_mult 4711 5 = 47110 ' negative operands M_L_mult -2 3 = -12 M_L_mult 2 -3 = -12 M_L_mult -2 -3 = 12 M_L_mult -4711 5 = -47110 M_L_mult 4711 -5 = -47110 M_L_mult -4711 -5 = 47110 ' overflow M_L_mult + + = 2147352578 M_L_mult + -32767 = -2147352578 M_L_mult -32767 + = -2147352578 M_L_mult + 2 = 131068 M_L_mult + 1 = 65534 M_L_mult 1 + = 65534 M_L_mult + 0 = 0 M_L_mult 0 + = 0 ================================================ FILE: deps/pjsip/third_party/gsm/inc/config.h ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/config.h,v 1.5 1996/07/02 11:26:20 jutta Exp $*/ #ifndef CONFIG_H #define CONFIG_H /*efine SIGHANDLER_T int * signal handlers are void */ /*efine HAS_SYSV_SIGNAL 1 * sigs not blocked/reset? */ #define HAS_STDLIB_H 1 /* /usr/include/stdlib.h */ #define HAS_LIMITS_H 1 /* /usr/include/limits.h */ #define HAS_FCNTL_H 1 /* /usr/include/fcntl.h */ #define HAS_ERRNO_DECL 1 /* errno.h declares errno */ #define HAS_FSTAT 1 /* fstat syscall */ #define HAS_FCHMOD 1 /* fchmod syscall */ #define HAS_CHMOD 1 /* chmod syscall */ #define HAS_FCHOWN 1 /* fchown syscall */ #define HAS_CHOWN 1 /* chown syscall */ /*efine HAS__FSETMODE 1 * _fsetmode -- set file mode */ #define HAS_STRING_H 1 /* /usr/include/string.h */ /*efine HAS_STRINGS_H 1 * /usr/include/strings.h */ #define HAS_UNISTD_H 1 /* /usr/include/unistd.h */ #define HAS_UTIME 1 /* POSIX utime(path, times) */ /*efine HAS_UTIMES 1 * use utimes() syscall instead */ #define HAS_UTIME_H 1 /* UTIME header file */ #define HAS_UTIMBUF 1 /* struct utimbuf */ /*efine HAS_UTIMEUSEC 1 * microseconds in utimbuf? */ #endif /* CONFIG_H */ ================================================ FILE: deps/pjsip/third_party/gsm/inc/gsm.h ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /*$Header: /home/kbs/jutta/src/gsm/gsm-1.0/inc/RCS/gsm.h,v 1.11 1996/07/05 18:02:56 jutta Exp $*/ #ifndef GSM_H #define GSM_H #ifdef __cplusplus # define NeedFunctionPrototypes 1 #endif #if __STDC__ # define NeedFunctionPrototypes 1 #endif #ifdef _NO_PROTO # undef NeedFunctionPrototypes #endif #ifdef NeedFunctionPrototypes # include /* for FILE * */ #endif #undef GSM_P #if NeedFunctionPrototypes # define GSM_P( protos ) protos #else # define GSM_P( protos ) ( /* protos */ ) #endif /* * Interface */ typedef struct gsm_state * gsm; typedef short gsm_signal; /* signed 16 bit */ typedef unsigned char gsm_byte; typedef gsm_byte gsm_frame[33]; /* 33 * 8 bits */ #define GSM_MAGIC 0xD /* 13 kbit/s RPE-LTP */ #define GSM_PATCHLEVEL 10 #define GSM_MINOR 0 #define GSM_MAJOR 1 #define GSM_OPT_VERBOSE 1 #define GSM_OPT_FAST 2 #define GSM_OPT_LTP_CUT 3 #define GSM_OPT_WAV49 4 #define GSM_OPT_FRAME_INDEX 5 #define GSM_OPT_FRAME_CHAIN 6 extern gsm gsm_create GSM_P((void)); extern void gsm_destroy GSM_P((gsm)); extern int gsm_print GSM_P((FILE *, gsm, gsm_byte *)); extern int gsm_option GSM_P((gsm, int, int *)); extern void gsm_encode GSM_P((gsm, gsm_signal *, gsm_byte *)); extern int gsm_decode GSM_P((gsm, gsm_byte *, gsm_signal *)); extern int gsm_explode GSM_P((gsm, gsm_byte *, gsm_signal *)); extern void gsm_implode GSM_P((gsm, gsm_signal *, gsm_byte *)); #undef GSM_P #endif /* GSM_H */ ================================================ FILE: deps/pjsip/third_party/gsm/inc/private.h ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/private.h,v 1.6 1996/07/02 10:15:26 jutta Exp $*/ #ifndef PRIVATE_H #define PRIVATE_H typedef short word; /* 16 bit signed int */ typedef long longword; /* 32 bit signed int */ typedef unsigned short uword; /* unsigned word */ typedef unsigned long ulongword; /* unsigned longword */ struct gsm_state { word dp0[ 280 ]; word e[ 50 ]; /* code.c */ word z1; /* preprocessing.c, Offset_com. */ longword L_z2; /* Offset_com. */ int mp; /* Preemphasis */ word u[8]; /* short_term_aly_filter.c */ word LARpp[2][8]; /* */ word j; /* */ word ltp_cut; /* long_term.c, LTP crosscorr. */ word nrp; /* 40 */ /* long_term.c, synthesis */ word v[9]; /* short_term.c, synthesis */ word msr; /* decoder.c, Postprocessing */ char verbose; /* only used if !NDEBUG */ char fast; /* only used if FAST */ char wav_fmt; /* only used if WAV49 defined */ unsigned char frame_index; /* odd/even chaining */ unsigned char frame_chain; /* half-byte to carry forward */ }; #define MIN_WORD (-32767 - 1) #define MAX_WORD 32767 #define MIN_LONGWORD (-2147483647 - 1) #define MAX_LONGWORD 2147483647 #ifdef SASR /* flag: >> is a signed arithmetic shift right */ #undef SASR #define SASR(x, by) ((x) >> (by)) #else #define SASR(x, by) ((x) >= 0 ? (x) >> (by) : (~(-((x) + 1) >> (by)))) #endif /* SASR */ #include "proto.h" /* * Prototypes from add.c */ extern word gsm_mult P((word a, word b)); extern longword gsm_L_mult P((word a, word b)); extern word gsm_mult_r P((word a, word b)); extern word gsm_div P((word num, word denum)); extern word gsm_add P(( word a, word b )); extern longword gsm_L_add P(( longword a, longword b )); extern word gsm_sub P((word a, word b)); extern longword gsm_L_sub P((longword a, longword b)); extern word gsm_abs P((word a)); extern word gsm_norm P(( longword a )); extern longword gsm_L_asl P((longword a, int n)); extern word gsm_asl P((word a, int n)); extern longword gsm_L_asr P((longword a, int n)); extern word gsm_asr P((word a, int n)); /* * Inlined functions from add.h */ /* * #define GSM_MULT_R(a, b) (* word a, word b, !(a == b == MIN_WORD) *) \ * (0x0FFFF & SASR(((longword)(a) * (longword)(b) + 16384), 15)) */ #define GSM_MULT_R(a, b) /* word a, word b, !(a == b == MIN_WORD) */ \ (SASR( ((longword)(a) * (longword)(b) + 16384), 15 )) # define GSM_MULT(a,b) /* word a, word b, !(a == b == MIN_WORD) */ \ (SASR( ((longword)(a) * (longword)(b)), 15 )) # define GSM_L_MULT(a, b) /* word a, word b */ \ (((longword)(a) * (longword)(b)) << 1) # define GSM_L_ADD(a, b) \ ( (a) < 0 ? ( (b) >= 0 ? (a) + (b) \ : (utmp = (ulongword)-((a) + 1) + (ulongword)-((b) + 1)) \ >= MAX_LONGWORD ? MIN_LONGWORD : -(longword)utmp-2 ) \ : ((b) <= 0 ? (a) + (b) \ : (utmp = (ulongword)(a) + (ulongword)(b)) >= MAX_LONGWORD \ ? MAX_LONGWORD : utmp)) /* * # define GSM_ADD(a, b) \ * ((ltmp = (longword)(a) + (longword)(b)) >= MAX_WORD \ * ? MAX_WORD : ltmp <= MIN_WORD ? MIN_WORD : ltmp) */ /* Nonportable, but faster: */ #define GSM_ADD(a, b) \ ((ulongword)((ltmp = (longword)(a) + (longword)(b)) - MIN_WORD) > \ MAX_WORD - MIN_WORD ? (ltmp > 0 ? MAX_WORD : MIN_WORD) : ltmp) # define GSM_SUB(a, b) \ ((ltmp = (longword)(a) - (longword)(b)) >= MAX_WORD \ ? MAX_WORD : ltmp <= MIN_WORD ? MIN_WORD : ltmp) # define GSM_ABS(a) ((a) < 0 ? ((a) == MIN_WORD ? MAX_WORD : -(a)) : (a)) /* Use these if necessary: # define GSM_MULT_R(a, b) gsm_mult_r(a, b) # define GSM_MULT(a, b) gsm_mult(a, b) # define GSM_L_MULT(a, b) gsm_L_mult(a, b) # define GSM_L_ADD(a, b) gsm_L_add(a, b) # define GSM_ADD(a, b) gsm_add(a, b) # define GSM_SUB(a, b) gsm_sub(a, b) # define GSM_ABS(a) gsm_abs(a) */ /* * More prototypes from implementations.. */ extern void Gsm_Coder P(( struct gsm_state * S, word * s, /* [0..159] samples IN */ word * LARc, /* [0..7] LAR coefficients OUT */ word * Nc, /* [0..3] LTP lag OUT */ word * bc, /* [0..3] coded LTP gain OUT */ word * Mc, /* [0..3] RPE grid selection OUT */ word * xmaxc,/* [0..3] Coded maximum amplitude OUT */ word * xMc /* [13*4] normalized RPE samples OUT */)); extern void Gsm_Long_Term_Predictor P(( /* 4x for 160 samples */ struct gsm_state * S, word * d, /* [0..39] residual signal IN */ word * dp, /* [-120..-1] d' IN */ word * e, /* [0..40] OUT */ word * dpp, /* [0..40] OUT */ word * Nc, /* correlation lag OUT */ word * bc /* gain factor OUT */)); extern void Gsm_LPC_Analysis P(( struct gsm_state * S, word * s, /* 0..159 signals IN/OUT */ word * LARc)); /* 0..7 LARc's OUT */ extern void Gsm_Preprocess P(( struct gsm_state * S, word * s, word * so)); extern void Gsm_Encoding P(( struct gsm_state * S, word * e, word * ep, word * xmaxc, word * Mc, word * xMc)); extern void Gsm_Short_Term_Analysis_Filter P(( struct gsm_state * S, word * LARc, /* coded log area ratio [0..7] IN */ word * d /* st res. signal [0..159] IN/OUT */)); extern void Gsm_Decoder P(( struct gsm_state * S, word * LARcr, /* [0..7] IN */ word * Ncr, /* [0..3] IN */ word * bcr, /* [0..3] IN */ word * Mcr, /* [0..3] IN */ word * xmaxcr, /* [0..3] IN */ word * xMcr, /* [0..13*4] IN */ word * s)); /* [0..159] OUT */ extern void Gsm_Decoding P(( struct gsm_state * S, word xmaxcr, word Mcr, word * xMcr, /* [0..12] IN */ word * erp)); /* [0..39] OUT */ extern void Gsm_Long_Term_Synthesis_Filtering P(( struct gsm_state* S, word Ncr, word bcr, word * erp, /* [0..39] IN */ word * drp)); /* [-120..-1] IN, [0..40] OUT */ void Gsm_RPE_Decoding P(( struct gsm_state *S, word xmaxcr, word Mcr, word * xMcr, /* [0..12], 3 bits IN */ word * erp)); /* [0..39] OUT */ void Gsm_RPE_Encoding P(( struct gsm_state * S, word * e, /* -5..-1][0..39][40..44 IN/OUT */ word * xmaxc, /* OUT */ word * Mc, /* OUT */ word * xMc)); /* [0..12] OUT */ extern void Gsm_Short_Term_Synthesis_Filter P(( struct gsm_state * S, word * LARcr, /* log area ratios [0..7] IN */ word * drp, /* received d [0...39] IN */ word * s)); /* signal s [0..159] OUT */ extern void Gsm_Update_of_reconstructed_short_time_residual_signal P(( word * dpp, /* [0...39] IN */ word * ep, /* [0...39] IN */ word * dp)); /* [-120...-1] IN/OUT */ /* * Tables from table.c */ #ifndef GSM_TABLE_C extern word gsm_A[8], gsm_B[8], gsm_MIC[8], gsm_MAC[8]; extern word gsm_INVA[8]; extern word gsm_DLB[4], gsm_QLB[4]; extern word gsm_H[11]; extern word gsm_NRFAC[8]; extern word gsm_FAC[8]; #endif /* GSM_TABLE_C */ /* * Debugging */ #ifdef NDEBUG # define gsm_debug_words(a, b, c, d) /* nil */ # define gsm_debug_longwords(a, b, c, d) /* nil */ # define gsm_debug_word(a, b) /* nil */ # define gsm_debug_longword(a, b) /* nil */ #else /* !NDEBUG => DEBUG */ extern void gsm_debug_words P((char * name, int, int, word *)); extern void gsm_debug_longwords P((char * name, int, int, longword *)); extern void gsm_debug_longword P((char * name, longword)); extern void gsm_debug_word P((char * name, word)); #endif /* !NDEBUG */ #include "unproto.h" #endif /* PRIVATE_H */ ================================================ FILE: deps/pjsip/third_party/gsm/inc/proto.h ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/proto.h,v 1.1 1992/10/28 00:11:08 jutta Exp $*/ #ifndef PROTO_H #define PROTO_H #if __cplusplus # define NeedFunctionPrototypes 1 #endif #if __STDC__ # define NeedFunctionPrototypes 1 #endif #ifdef _NO_PROTO # undef NeedFunctionPrototypes #endif #undef P /* gnu stdio.h actually defines this... */ #undef P0 #undef P1 #undef P2 #undef P3 #undef P4 #undef P5 #undef P6 #undef P7 #undef P8 #if NeedFunctionPrototypes # define P( protos ) protos # define P0() (void) # define P1(x, a) (a) # define P2(x, a, b) (a, b) # define P3(x, a, b, c) (a, b, c) # define P4(x, a, b, c, d) (a, b, c, d) # define P5(x, a, b, c, d, e) (a, b, c, d, e) # define P6(x, a, b, c, d, e, f) (a, b, c, d, e, f) # define P7(x, a, b, c, d, e, f, g) (a, b, c, d, e, f, g) # define P8(x, a, b, c, d, e, f, g, h) (a, b, c, d, e, f, g, h) #else /* !NeedFunctionPrototypes */ # define P( protos ) ( /* protos */ ) # define P0() () # define P1(x, a) x a; # define P2(x, a, b) x a; b; # define P3(x, a, b, c) x a; b; c; # define P4(x, a, b, c, d) x a; b; c; d; # define P5(x, a, b, c, d, e) x a; b; c; d; e; # define P6(x, a, b, c, d, e, f) x a; b; c; d; e; f; # define P7(x, a, b, c, d, e, f, g) x a; b; c; d; e; f; g; # define P8(x, a, b, c, d, e, f, g, h) x a; b; c; d; e; f; g; h; #endif /* !NeedFunctionPrototypes */ #endif /* PROTO_H */ ================================================ FILE: deps/pjsip/third_party/gsm/inc/toast.h ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/toast.h,v 1.4 1995/03/07 21:26:16 jutta Exp $ */ #ifndef TOAST_H #define TOAST_H /* Guard against multiple includes */ #include "config.h" #include #include #include #include #include #include #ifndef HAS_ERRNO_DECL extern int errno; #endif #ifdef HAS_LIMITS_H #include #endif #ifdef HAS_FCNTL_H # include #endif #ifdef HAS_UTIME # ifdef HAS_UTIME_H # include # endif #endif #include "gsm.h" #ifndef S_ISREG #define S_ISREG(x) ((x) & S_IFREG) #endif /* S_ISREG */ # define READ "rb" # define WRITE "wb" #ifdef O_BINARY # define O_WRITE_EXCL O_WRONLY|O_CREAT|O_EXCL|O_BINARY #else # define O_WRITE_EXCL O_WRONLY|O_CREAT|O_EXCL #endif #ifndef SIGHANDLER_T #define SIGHANDLER_T void /* what does a signal handler return? */ #endif #ifdef HAS_STRING_H #include #else # ifdef HAS_STRINGS_H # include # else # include "proto.h" extern int strlen P((char *)); extern char * strcpy P((char *, char *)); extern char * strcat P((char *, char *)); extern char * strrchr P((char *, int)); # include "unproto.h" # endif #endif #ifdef HAS_STDLIB_H #include #else # include "proto.h" # ifdef HAS_MALLOC_H # include # else extern char * malloc P((unsigned)); # endif extern int exit P((int)); # include "unproto.h" #endif #ifdef HAS_UNISTD_H # include #endif /* * This suffix is tacked onto/removed from filenames * similar to the way freeze and compress do it. */ #define SUFFIX_TOASTED ".gsm" #include "proto.h" extern int audio_init_input P((void)), audio_init_output P((void)); extern int ulaw_input P((gsm_signal*)), ulaw_output P((gsm_signal *)); extern int alaw_input P((gsm_signal*)), alaw_output P((gsm_signal *)); extern int linear_input P((gsm_signal*)), linear_output P((gsm_signal *)); #endif /* TOAST_H */ ================================================ FILE: deps/pjsip/third_party/gsm/inc/unproto.h ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/unproto.h,v 1.1 1992/10/28 00:11:08 jutta Exp $*/ #ifdef PROTO_H /* sic */ #undef PROTO_H #undef P #undef P0 #undef P1 #undef P2 #undef P3 #undef P4 #undef P5 #undef P6 #undef P7 #undef P8 #endif /* PROTO_H */ ================================================ FILE: deps/pjsip/third_party/gsm/man/bitter.1 ================================================ .\" .\" Copyright 1992 by Jutta Degener and Carsten Bormann, Technische .\" Universitaet Berlin. See the accompanying file "COPYRIGHT" for .\" details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. .\" .PU .TH BITTER 1 .SH NAME bitter, sweet \(em code-generators for packing bits .SH SYNOPSIS bitter < input > output .br sweet < input > output .SH "DESCRIPTION" Bitter and sweet are two filters which turn a description of the form .nf name number-of-bits name number-of-bits ... .nf into code. .PP Bitter generates code that packs the specified bits from their variables into an array of unsigned char referenced by an advancing pointer c. .PP Sweet generates code that unpacks the specified bits from an array of unsigned char referenced by a mutable pointer c into the named variables. .\" .SH OPTIONS .\" .SH "RETURN VALUE" .\" .SH ERRORS .SH EXAMPLES .nf % cat in amaretto 1 banana 2 cherry 3 strawberry 4 vanilla 15 walnut 15 % bitter < in *c++ = ((amaretto & 0x1) << 7) | ((banana & 0x3) << 5) | ((cherry & 0x7) << 2) | ((strawberry >> 2) & 0x3); *c++ = ((strawberry & 0x3) << 6) | ((vanilla >> 9) & 0x3F); *c++ = ((vanilla >> 1) & 0xFF); *c++ = ((vanilla & 0x1) << 7) | ((walnut >> 8) & 0x7F); *c++ = walnut & 0xFF; % sweet < in amaretto = (*c >> 7) & 0x1; banana = (*c >> 5) & 0x3; cherry = (*c >> 2) & 0x7; strawberry = (*c++ & 0x3) << 2; strawberry |= (*c >> 6) & 0x3; vanilla = (*c++ & 0x3F) << 9; vanilla |= (*c++ & 0xFF) << 1; vanilla |= (*c >> 7) & 0x1; walnut = (*c++ & 0x7F) << 8; walnut |= *c++; .SH NOTES This is a quick hack for the gsm_encode() and gsm_decode() routines. .SH BUGS Please direct bug reports to jutta@cs.tu-berlin.de and cabo@cs.tu-berlin.de. ================================================ FILE: deps/pjsip/third_party/gsm/man/gsm.3 ================================================ .\" .\" Copyright 1992 by Jutta Degener and Carsten Bormann, Technische .\" Universitaet Berlin. See the accompanying file "COPYRIGHT" for .\" details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. .\" .PU .TH GSM 3 .SH NAME gsm_create, gsm_destroy, gsm_encode, gsm_decode \(em GSM\ 06.10 lossy sound compression .SH SYNOPSIS .PP #include "gsm.h" .PP gsm gsm_create(); .PP void gsm_encode(handle, src, dst) .br gsm handle; .br gsm_signal src[160]; .br gsm_frame dst; .PP int gsm_decode(handle, src, dst) .br gsm handle; .br gsm_frame src; .br gsm_signal dst[160]; .PP void gsm_destroy(handle) .br gsm handle; .br .SH "DESCRIPTION" Gsm is an implementation of the final draft GSM 06.10 standard for full-rate speech transcoding. .PP gsm_create() initializes a gsm pass and returns a 'gsm' object which can be used as a handle in subsequent calls to gsm_decode(), gsm_encode() or gsm_destroy(). .PP gsm_encode() encodes an array of 160 13-bit samples (given as gsm_signal's, signed integral values of at least 16 bits) into a gsm_frame of 33 bytes. (gsm_frame is a type defined as an array of 33 gsm_bytes in gsm.h.) .PP gsm_decode() decodes a gsm_frame into an array of 160 13-bit samples (given as gsm_signals), which sound rather like what you handed to gsm_encode() on the other side of the wire. .PP gsm_destroy() finishes a gsm pass and frees all storage associated with it. .SS "Sample format" The following scaling is assumed for input to the algorithm: .br .nf 0 1 11 12 S..v..v..v..v..v..v..v..v..v..v..v..v..*..*..* .nf .br Only the top 13 bits are used as a signed input value. The output of gsm_decode() has the three lower bits set to zero. .\" .SH OPTIONS .SH "RETURN VALUE" gsm_create() returns an opaque handle object of type gsm, or 0 on error. gsm_decode() returns -1 if the passed frame is invalid, else 0. .SH EXAMPLE .nf #include "gsm.h" gsm handle; gsm_frame buf; gsm_signal sample[160]; int cc, soundfd; play() { /* read compressed data from standard input, write to soundfd */ if (!(handle = gsm_create())) error... while (cc = read(0, (char *)buf, sizeof buf)) { if (cc != sizeof buf) error... if (gsm_decode(handle, buf, sample) < 0) error... if (write(soundfd, sample, sizeof sample) != sizeof sample) error... } gsm_destroy(handle); } record() { /* read from soundfd, write compressed to standard output */ if (!(handle = gsm_create())) error... while (cc = read(soundfd, sample, sizeof sample)) { if (cc != sizeof sample) error... gsm_encode(handle, sample, buf); if (write(1, (char *)buf, sizeof buf) != sizeof sample) error... } gsm_destroy(handle); } .nf .SH BUGS Please direct bug reports to jutta@cs.tu-berlin.de and cabo@cs.tu-berlin.de. .SH "SEE ALSO" toast(1), gsm_print(3), gsm_explode(3), gsm_option(3) ================================================ FILE: deps/pjsip/third_party/gsm/man/gsm_explode.3 ================================================ .\" .\" Copyright 1992 by Jutta Degener and Carsten Bormann, Technische .\" Universitaet Berlin. See the accompanying file "COPYRIGHT" for .\" details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. .\" .PU .TH GSM_EXPLODE 3 .SH NAME gsm_explode, gsm_implode \(em GSM\ 06.10 supplementary functions for testing .SH SYNOPSIS #include "gsm.h" .PP void gsm_explode(g, frame, xframe) .br gsm g; .br gsm_frame frame; .br gsm_signal xframe[ 76 ]; .PP void gsm_implode(g, xframe, frame) .br gsm g; .br gsm_signal xframe[ 76 ]; .br gsm_frame frame; .SH "DESCRIPTION" Gsm is an implementation of the final draft GSM 06.10 standard for full-rate speech transcoding. Test data for implementations of this particular document can be bought and used to verify an implementation. .PP The encoded test data uses a format different from what one would use to transmit frames with the least number of bits. Gsm_explode() and gsm_implode() convert between the internal, small, 33-byte format and the 76-word format used by the test data. .PP .SH "RETURN VALUE" gsm_explode() returns -1 if the passed frame is invalid, else 0. .SH BUGS Please direct bug reports to jutta@cs.tu-berlin.de and cabo@cs.tu-berlin.de. .SH "SEE ALSO" gsm(3) ================================================ FILE: deps/pjsip/third_party/gsm/man/gsm_option.3 ================================================ .\" .\" Copyright 1992-1995 by Jutta Degener and Carsten Bormann, Technische .\" Universitaet Berlin. See the accompanying file "COPYRIGHT" for .\" details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. .\" .PU .TH GSM_OPTION 3 .SH NAME gsm_option \(em customizing the GSM 06.10 implementation .SH SYNOPSIS #include "gsm.h" .PP int gsm_option(handle, option, valueP); .br gsm handle; .br int option; .br int * valueP; .SH "DESCRIPTION" The gsm library is an implementation of the final draft GSM 06.10 standard for full-rate speech transcoding, a lossy speech compression algorithm. .PP The gsm_option() function can be used to set and query various options or flags that are not needed for regular GSM 06.10 encoding or decoding, but might be of interest in special cases. .PP The second argument to gsm_option specifies what parameter should be changed or queried. The third argument is either a null pointer, in which case the current value of that parameter is returned; or it is a pointer to an integer containing the value you want to set, in which case the previous value will be returned. .PP The following options are defined: .PP .I GSM_OPT_VERBOSE Verbosity level. .br .in+5 This option is only supported if the library was compiled with debugging turned on, and may be used by developers of compression algorithms to aid debugging. .br The verbosity level can be changed at any time during encoding or decoding. .in-5 .sp .PP .I GSM_OPT_FAST Faster compression algorithm. .br .in+5 This implementation offers a not strictly standard-compliant, but faster compression algorithm that is compatible with the regular method and does not noticably degrade audio quality. .br The value passed to .br .nf gsm_option(handle, GSM_OPT_FAST, & value) .fi .br functions as a boolean flag; if it is zero, the regular algorithm will be used, if not, the faster version will be used. .br The availability of this option depends on the hardware used; if it is not available, gsm_option will return -1 on an attempt to set or query it. .br This option can be set any time during encoding or decoding. .in-5 .ne 5 .sp .PP .I GSM_OPT_LTP_CUT Enable, disable, or query the LTP cut-off optimization. .br .in+5 During encoding, the search for the long-term correlation lag forms the bottleneck of the algorithm. The ltp-cut option enables an approximation that disregards most of the samples for purposes of finding that correlation, and hence speeds up the encoding at a noticable loss in quality. .br The value passed to .br .nf gsm_option(handle, GSM_OPT_LTP_CUT, & value) .fi .br turns the optimization on if nonzero, and off if zero. .br This option can be set any time during encoding or decoding; it will only affect the encoding pass, not the decoding. .sp .PP .I GSM_OPT_WAV49 WAV-style byte ordering. .br .in+5 A WAV file of type #49 contains GSM 06.10-encoded frames. Unfortunately, the framing and code ordering of the WAV version are incompatible with the native ones of this GSM 06.10 library. The GSM_OPT_WAV49 option turns on a different packing algorithm that produces alternating frames of 32 and 33 bytes (or makes it consume alternating frames of 33 and 32 bytes, note the opposite order of the two numbers) which, when concatenated, can be used in the body of a WAV #49 frame. It is up to the user program to write a WAV header, if any; neither the library itself nor the toast program produce complete WAV files. .br The value passed to .br .nf gsm_option(handle, GSM_OPT_WAV49, & value) .fi .br functions as a boolean flag; if it is zero, the library's native framing algorithm will be used, if nonzero, WAV-type packing is in effect. .br This option should be used before any frames are encoded. Whether or not it is supported at all depends on a compile-time switch, WAV49. Both option and compile time switch are new to the library as of patchlevel 9, and are considerably less tested than the well-worn rest of the it. .br Thanks to Jeff Chilton for the detective work and first free implementation of this version of the GSM 06.10 encoding. .sp .PP .I GSM_OPT_FRAME_CHAIN Query or set the chaining byte. .br .in+5 Between the two frames of a WAV-style encoding, the GSM 06.10 library must keep track of one half-byte that is technically part of the first frame, but will be written as the first four bits of the second. This half-byte are the lowest four bits of the value returned by, and optionally set by, .br .nf gsm_option(handle, GSM_OPT_FRAME_CHAIN, & value) .fi .br This option can be queried and set at any time. .sp .PP .I GSM_OPT_FRAME_INDEX Query or set the current frame's index in a format's alternating list of frames. .br .in+5 The WAV #49 framing uses two alternating types of frames. Which type the next GSM-coded frame belongs to can be queried, or, when decoding, announced, using .br .nf gsm_option(handle, GSM_OPT_FRAME_INDEX, & value) .fi .br For WAV-style framing, the value should be 0 or 1; the first frame of an encoding has an index of 0. At library initialization, the index is set to zero. .br The frame index can be queried and set at any time. Used in combination with the .IR GSM_OPT_FRAME_CHAIN , option, it can be used to position on arbitrary GSM frames within a format like WAV #49 (not accounting for the lost internal GSM state). .in-5 .SH "RETURN VALUE" gsm_option() returns -1 if an option is not supported, the previous value of the option otherwise. .SH BUGS Please direct bug reports to jutta@cs.tu-berlin.de and cabo@cs.tu-berlin.de. .SH "SEE ALSO" toast(1), gsm(3), gsm_explode(3), gsm_print(3) ================================================ FILE: deps/pjsip/third_party/gsm/man/gsm_print.3 ================================================ .\" .\" Copyright 1992 by Jutta Degener and Carsten Bormann, Technische .\" Universitaet Berlin. See the accompanying file "COPYRIGHT" for .\" details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. .\" .PU .TH GSM_PRINT 3 .SH NAME gsm_print \(em GSM\ 06.10 supplementary function for debugging .SH SYNOPSIS #include "gsm.h" #include int gsm_print(f, g, frame); .br FILE * f; .br gsm g; .br gsm_frame frame; .SH "DESCRIPTION" Gsm is an implementation of the final draft GSM 06.10 standard for full-rate speech transcoding, a lossy speech compression algorithm. The compressed form involves 76 variables with different numbers of significant bits packed into 33 bytes. .PP If you are interested in investigating the details of this coding scheme, gsm_print() can be used to dump the contents of individual gsm_frames to a file pointer provided by the application. .PP .SH "RETURN VALUE" gsm_print() returns -1 if the frame is invalid, else 0. .SH EXAMPLE A single frame looks like this: .br .nf LARc: 29 32 20 11 08 05 06 07 #1: Nc 0040 bc 0 Mc 1 xmaxc 60 06 04 00 03 03 06 04 02 02 04 05 04 01 #2: Nc 0045 bc 1 Mc 1 xmaxc 48 03 07 01 03 04 04 07 01 03 02 04 05 03 #3: Nc 0091 bc 1 Mc 1 xmaxc 46 00 03 03 07 01 06 02 04 05 03 03 02 04 #4: Nc 0120 bc 0 Mc 1 xmaxc 47 07 03 06 00 03 03 06 05 00 03 02 07 04 .nf .SH BUGS Please direct bug reports to jutta@cs.tu-berlin.de and cabo@cs.tu-berlin.de. .SH "SEE ALSO" gsm(3), gsm_explode(3) ================================================ FILE: deps/pjsip/third_party/gsm/man/toast.1 ================================================ .\" .\" Copyright 1992 by Jutta Degener and Carsten Bormann, Technische .\" Universitaet Berlin. See the accompanying file "COPYRIGHT" for .\" details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. .\" .if n .ds mU u .if t .ds mU \(*m .\" .TH TOAST 1 local .SH NAME toast \(em GSM\ 06.10 lossy sound compression .SH SYNOPSIS .ll +8 .B toast [ .B \-cdfpvhualsFC ] [ .I "filename...\&" ] .LP .B untoast [ .B \-cfpvhuaslF ] [ .I "filename...\&" ] .LP .B tcat [ .B \-vhuaslF ] [ .I "filename...\&" ] .ll -8 .SH DESCRIPTION Toast compresses the sound files given on its command line. Each file is replaced by a file with the extension .I \&.gsm . If no files are specified, the compression is applied to the standard input, and its result is written to standard output. .PP Toasted files can be restored to something not quite unlike their original form by running toast .I "\-d" , or .I untoast , on the \&.gsm-files or standard input. .PP The program .I tcat (the same as running .I "untoast \-c" ) uncompresses its input on standard output, but leaves the compressed .gsm\-files alone. .PP When files are compressed or uncompressed into other files, the ownership (if run by root), modes, accessed and modified times are maintained between both versions. .SH OPTIONS .TP .B \-c (cat) Write to the standard output; no files are changed. .TP .B \-d (decode) Decode, rather than encode, the files. .TP .B \-f (force) Force replacement of output files if they exist. If \-f is omitted and toast (or untoast) is run interactively from a terminal, the user is prompted as to whether the file should be replaced. .TP .B \-p (precious) Do not delete the source files. Source files are implicitly left alone whenever \-c is specified or tcat is run. .TP .B \-C (LTP cut-off) Ignore most sample values when calculating the GSM long-term correlation lag during encoding. (The multiplications that do this are a bottleneck of the algorithm.) The resulting encoding process will not produce exactly the same results as GSM 06.10 would, but remains close enough to be compatible. .br The .B \-C option applies only to the encoder and is silently ignored by the decoder. .TP .B \-F (fast) On systems with a floating point processor, but without a multiplication instruction, \-F sacrifices standard conformance to performance and nearly doubles the speed of the algorithm. .br The resulting encoding and decoding process will not produce exactly the same results as GSM 06.10 would, but remains close enough to be compatible. .br The default is standard-conforming operation. .TP .B \-v (version)\ outputs the version of toast (or untoast or tcat) to stdout and exits. .TP .B \-h (help)\ prints a short overview of the options. .PP Toast, untoast and tcat try to guess the appropriate audio data format from the file suffix. Command line options can also specify a format to be used for all files. .br The following formats are supported: .TP .B "\-u" (\(*mU-law) 8 kHz, 8 bit \(*mU-law encoding (file suffix .u) .TP .B "\-a" (A-law) 8 kHz, 8 bit A-law encoding (file suffix .A) .TP .B "\-s" (Sun audio) 8 kHz, 8 bit \(*mU-law encoding with audio header (file suffix .au) .TP .B "-l" (linear) 8 kHz, 16 bit signed linear encoding in host byte order with 13 significant bits (file suffix .l) .PP In absence of options or suffixes to specify a format, \(*mU-law encoding as forced by \-u is assumed. .PP .SH PECULIARITIES A four bit magic number is prefixed to each 32 1/2-byte GSM frame, mainly because 32 1/2-bytes are rather clumsy to handle. .SH WARNING The compression algorithm used is a lossy compression algorithm devised especially for speech; on no account should it be used for text, pictures or any other non-speech-data you consider valuable. .SH BUGS Please direct bug reports to jutta@cs.tu-berlin.de. .SH "SEE ALSO" gsm(3) .\" .\" Toast is dedicated to Bill Sienkiewicz, author of "Stray Toasters". ================================================ FILE: deps/pjsip/third_party/gsm/src/add.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/add.c,v 1.6 1996/07/02 09:57:33 jutta Exp $ */ /* * See private.h for the more commonly used macro versions. */ #include "config.h" #include #include #include "private.h" #include "gsm.h" #include "proto.h" #define saturate(x) \ ((x) < MIN_WORD ? MIN_WORD : (x) > MAX_WORD ? MAX_WORD: (x)) word gsm_add P2((a,b), word a, word b) { longword sum = (longword)a + (longword)b; return saturate(sum); } word gsm_sub P2((a,b), word a, word b) { longword diff = (longword)a - (longword)b; return saturate(diff); } word gsm_mult P2((a,b), word a, word b) { if (a == MIN_WORD && b == MIN_WORD) return MAX_WORD; else return SASR( (longword)a * (longword)b, 15 ); } word gsm_mult_r P2((a,b), word a, word b) { if (b == MIN_WORD && a == MIN_WORD) return MAX_WORD; else { longword prod = (longword)a * (longword)b + 16384; prod >>= 15; return prod & 0xFFFF; } } word gsm_abs P1((a), word a) { return a < 0 ? (a == MIN_WORD ? MAX_WORD : -a) : a; } longword gsm_L_mult P2((a,b),word a, word b) { assert( a != MIN_WORD || b != MIN_WORD ); return ((longword)a * (longword)b) << 1; } longword gsm_L_add P2((a,b), longword a, longword b) { if (a < 0) { if (b >= 0) return a + b; else { ulongword A = (ulongword)-(a + 1) + (ulongword)-(b + 1); return A >= MAX_LONGWORD ? MIN_LONGWORD :-(longword)A-2; } } else if (b <= 0) return a + b; else { ulongword A = (ulongword)a + (ulongword)b; return A > MAX_LONGWORD ? MAX_LONGWORD : A; } } longword gsm_L_sub P2((a,b), longword a, longword b) { if (a >= 0) { if (b >= 0) return a - b; else { /* a>=0, b<0 */ ulongword A = (ulongword)a + -(b + 1); return A >= MAX_LONGWORD ? MAX_LONGWORD : (A + 1); } } else if (b <= 0) return a - b; else { /* a<0, b>0 */ ulongword A = (ulongword)-(a + 1) + b; return A >= MAX_LONGWORD ? MIN_LONGWORD : -(longword)A - 1; } } static unsigned char const bitoff[ 256 ] = { 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; word gsm_norm P1((a), longword a ) /* * the number of left shifts needed to normalize the 32 bit * variable L_var1 for positive values on the interval * * with minimum of * minimum of 1073741824 (01000000000000000000000000000000) and * maximum of 2147483647 (01111111111111111111111111111111) * * * and for negative values on the interval with * minimum of -2147483648 (-10000000000000000000000000000000) and * maximum of -1073741824 ( -1000000000000000000000000000000). * * in order to normalize the result, the following * operation must be done: L_norm_var1 = L_var1 << norm( L_var1 ); * * (That's 'ffs', only from the left, not the right..) */ { assert(a != 0); if (a < 0) { if (a <= -1073741824) return 0; a = ~a; } return a & 0xffff0000 ? ( a & 0xff000000 ? -1 + bitoff[ 0xFF & (a >> 24) ] : 7 + bitoff[ 0xFF & (a >> 16) ] ) : ( a & 0xff00 ? 15 + bitoff[ 0xFF & (a >> 8) ] : 23 + bitoff[ 0xFF & a ] ); } longword gsm_L_asl P2((a,n), longword a, int n) { if (n >= 32) return 0; if (n <= -32) return -(a < 0); if (n < 0) return gsm_L_asr(a, -n); return a << n; } word gsm_asl P2((a,n), word a, int n) { if (n >= 16) return 0; if (n <= -16) return -(a < 0); if (n < 0) return gsm_asr(a, -n); return a << n; } longword gsm_L_asr P2((a,n), longword a, int n) { if (n >= 32) return -(a < 0); if (n <= -32) return 0; if (n < 0) return a << -n; # ifdef SASR return a >> n; # else if (a >= 0) return a >> n; else return -(longword)( -(ulongword)a >> n ); # endif } word gsm_asr P2((a,n), word a, int n) { if (n >= 16) return -(a < 0); if (n <= -16) return 0; if (n < 0) return a << -n; # ifdef SASR return a >> n; # else if (a >= 0) return a >> n; else return -(word)( -(uword)a >> n ); # endif } /* * (From p. 46, end of section 4.2.5) * * NOTE: The following lines gives [sic] one correct implementation * of the div(num, denum) arithmetic operation. Compute div * which is the integer division of num by denum: with denum * >= num > 0 */ word gsm_div P2((num,denum), word num, word denum) { longword L_num = num; longword L_denum = denum; word div = 0; int k = 15; /* The parameter num sometimes becomes zero. * Although this is explicitly guarded against in 4.2.5, * we assume that the result should then be zero as well. */ /* assert(num != 0); */ assert(num >= 0 && denum >= num); if (num == 0) return 0; while (k--) { div <<= 1; L_num <<= 1; if (L_num >= L_denum) { L_num -= L_denum; div++; } } return div; } ================================================ FILE: deps/pjsip/third_party/gsm/src/code.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/code.c,v 1.3 1996/07/02 09:59:05 jutta Exp $ */ #include "config.h" #ifdef HAS_STDLIB_H #include #else # include "proto.h" extern char * memcpy P((char *, char *, int)); #endif #include "private.h" #include "gsm.h" #include "proto.h" /* * 4.2 FIXED POINT IMPLEMENTATION OF THE RPE-LTP CODER */ void Gsm_Coder P8((S,s,LARc,Nc,bc,Mc,xmaxc,xMc), struct gsm_state * S, word * s, /* [0..159] samples IN */ /* * The RPE-LTD coder works on a frame by frame basis. The length of * the frame is equal to 160 samples. Some computations are done * once per frame to produce at the output of the coder the * LARc[1..8] parameters which are the coded LAR coefficients and * also to realize the inverse filtering operation for the entire * frame (160 samples of signal d[0..159]). These parts produce at * the output of the coder: */ word * LARc, /* [0..7] LAR coefficients OUT */ /* * Procedure 4.2.11 to 4.2.18 are to be executed four times per * frame. That means once for each sub-segment RPE-LTP analysis of * 40 samples. These parts produce at the output of the coder: */ word * Nc, /* [0..3] LTP lag OUT */ word * bc, /* [0..3] coded LTP gain OUT */ word * Mc, /* [0..3] RPE grid selection OUT */ word * xmaxc,/* [0..3] Coded maximum amplitude OUT */ word * xMc /* [13*4] normalized RPE samples OUT */ ) { int k; word * dp = S->dp0 + 120; /* [ -120...-1 ] */ word * dpp = dp; /* [ 0...39 ] */ word so[160]; Gsm_Preprocess (S, s, so); Gsm_LPC_Analysis (S, so, LARc); Gsm_Short_Term_Analysis_Filter (S, LARc, so); for (k = 0; k <= 3; k++, xMc += 13) { Gsm_Long_Term_Predictor ( S, so+k*40, /* d [0..39] IN */ dp, /* dp [-120..-1] IN */ S->e + 5, /* e [0..39] OUT */ dpp, /* dpp [0..39] OUT */ Nc++, bc++); Gsm_RPE_Encoding ( S, S->e + 5,/* e ][0..39][ IN/OUT */ xmaxc++, Mc++, xMc ); /* * Gsm_Update_of_reconstructed_short_time_residual_signal * ( dpp, S->e + 5, dp ); */ { register int i; register longword ltmp; for (i = 0; i <= 39; i++) dp[ i ] = GSM_ADD( S->e[5 + i], dpp[i] ); } dp += 40; dpp += 40; } (void)memcpy( (char *)S->dp0, (char *)(S->dp0 + 160), 120 * sizeof(*S->dp0) ); } ================================================ FILE: deps/pjsip/third_party/gsm/src/debug.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/debug.c,v 1.2 1993/01/29 18:22:20 jutta Exp $ */ #include "private.h" #ifndef NDEBUG /* If NDEBUG _is_ defined and no debugging should be performed, * calls to functions in this module are #defined to nothing * in private.h. */ #include #include "proto.h" void gsm_debug_words P4( (name, from, to, ptr), char * name, int from, int to, word * ptr) { int nprinted = 0; fprintf( stderr, "%s [%d .. %d]: ", name, from, to ); while (from <= to) { fprintf(stderr, "%d ", ptr[ from ] ); from++; if (nprinted++ >= 7) { nprinted = 0; if (from < to) putc('\n', stderr); } } putc('\n', stderr); } void gsm_debug_longwords P4( (name, from, to, ptr), char * name, int from, int to, longword * ptr) { int nprinted = 0; fprintf( stderr, "%s [%d .. %d]: ", name, from, to ); while (from <= to) { fprintf(stderr, "%d ", ptr[ from ] ); from++; if (nprinted++ >= 7) { nprinted = 0; if (from < to) putc('\n', stderr); } } putc('\n', stderr); } void gsm_debug_longword P2( (name, value), char * name, longword value ) { fprintf(stderr, "%s: %d\n", name, (long)value ); } void gsm_debug_word P2( (name, value), char * name, word value ) { fprintf(stderr, "%s: %d\n", name, (long)value); } #endif ================================================ FILE: deps/pjsip/third_party/gsm/src/decode.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/decode.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */ #include "config.h" #include #include "private.h" #include "gsm.h" #include "proto.h" /* * 4.3 FIXED POINT IMPLEMENTATION OF THE RPE-LTP DECODER */ static void Postprocessing P2((S,s), struct gsm_state * S, register word * s) { register int k; register word msr = S->msr; register longword ltmp; /* for GSM_ADD */ register word tmp; for (k = 160; k--; s++) { tmp = GSM_MULT_R( msr, 28180 ); msr = GSM_ADD(*s, tmp); /* Deemphasis */ *s = GSM_ADD(msr, msr) & 0xFFF8; /* Truncation & Upscaling */ } S->msr = msr; } void Gsm_Decoder P8((S,LARcr, Ncr,bcr,Mcr,xmaxcr,xMcr,s), struct gsm_state * S, word * LARcr, /* [0..7] IN */ word * Ncr, /* [0..3] IN */ word * bcr, /* [0..3] IN */ word * Mcr, /* [0..3] IN */ word * xmaxcr, /* [0..3] IN */ word * xMcr, /* [0..13*4] IN */ word * s) /* [0..159] OUT */ { int j, k; word erp[40], wt[160]; word * drp = S->dp0 + 120; for (j=0; j <= 3; j++, xmaxcr++, bcr++, Ncr++, Mcr++, xMcr += 13) { Gsm_RPE_Decoding( S, *xmaxcr, *Mcr, xMcr, erp ); Gsm_Long_Term_Synthesis_Filtering( S, *Ncr, *bcr, erp, drp ); for (k = 0; k <= 39; k++) wt[ j * 40 + k ] = drp[ k ]; } Gsm_Short_Term_Synthesis_Filter( S, LARcr, wt, s ); Postprocessing(S, s); } ================================================ FILE: deps/pjsip/third_party/gsm/src/gsm_create.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ #include "config.h" static char const ident[] = "$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_create.c,v 1.4 1996/07/02 09:59:05 jutta Exp $"; #ifdef HAS_STRING_H #include #else # include "proto.h" extern char * memset P((char *, int, int)); #endif #ifdef HAS_STDLIB_H # include #else # ifdef HAS_MALLOC_H # include # else extern char * malloc(); # endif #endif #include #include "gsm.h" #include "private.h" #include "proto.h" gsm gsm_create P0() { gsm r; r = (gsm)malloc(sizeof(struct gsm_state)); if (!r) return r; memset((char *)r, 0, sizeof(*r)); r->nrp = 40; return r; } ================================================ FILE: deps/pjsip/third_party/gsm/src/gsm_decode.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_decode.c,v 1.2 1996/07/02 09:59:05 jutta Exp $ */ #include "private.h" #include "gsm.h" #include "proto.h" int gsm_decode P3((s, c, target), gsm s, gsm_byte * c, gsm_signal * target) { word LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4]; #ifdef WAV49 if (s->wav_fmt) { uword sr = 0; s->frame_index = !s->frame_index; if (s->frame_index) { sr = *c++; LARc[0] = sr & 0x3f; sr >>= 6; sr |= (uword)*c++ << 2; LARc[1] = sr & 0x3f; sr >>= 6; sr |= (uword)*c++ << 4; LARc[2] = sr & 0x1f; sr >>= 5; LARc[3] = sr & 0x1f; sr >>= 5; sr |= (uword)*c++ << 2; LARc[4] = sr & 0xf; sr >>= 4; LARc[5] = sr & 0xf; sr >>= 4; sr |= (uword)*c++ << 2; /* 5 */ LARc[6] = sr & 0x7; sr >>= 3; LARc[7] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 4; Nc[0] = sr & 0x7f; sr >>= 7; bc[0] = sr & 0x3; sr >>= 2; Mc[0] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 1; xmaxc[0] = sr & 0x3f; sr >>= 6; xmc[0] = sr & 0x7; sr >>= 3; sr = *c++; xmc[1] = sr & 0x7; sr >>= 3; xmc[2] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[3] = sr & 0x7; sr >>= 3; xmc[4] = sr & 0x7; sr >>= 3; xmc[5] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; /* 10 */ xmc[6] = sr & 0x7; sr >>= 3; xmc[7] = sr & 0x7; sr >>= 3; xmc[8] = sr & 0x7; sr >>= 3; sr = *c++; xmc[9] = sr & 0x7; sr >>= 3; xmc[10] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[11] = sr & 0x7; sr >>= 3; xmc[12] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 4; Nc[1] = sr & 0x7f; sr >>= 7; bc[1] = sr & 0x3; sr >>= 2; Mc[1] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 1; xmaxc[1] = sr & 0x3f; sr >>= 6; xmc[13] = sr & 0x7; sr >>= 3; sr = *c++; /* 15 */ xmc[14] = sr & 0x7; sr >>= 3; xmc[15] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[16] = sr & 0x7; sr >>= 3; xmc[17] = sr & 0x7; sr >>= 3; xmc[18] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[19] = sr & 0x7; sr >>= 3; xmc[20] = sr & 0x7; sr >>= 3; xmc[21] = sr & 0x7; sr >>= 3; sr = *c++; xmc[22] = sr & 0x7; sr >>= 3; xmc[23] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[24] = sr & 0x7; sr >>= 3; xmc[25] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 4; /* 20 */ Nc[2] = sr & 0x7f; sr >>= 7; bc[2] = sr & 0x3; sr >>= 2; Mc[2] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 1; xmaxc[2] = sr & 0x3f; sr >>= 6; xmc[26] = sr & 0x7; sr >>= 3; sr = *c++; xmc[27] = sr & 0x7; sr >>= 3; xmc[28] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[29] = sr & 0x7; sr >>= 3; xmc[30] = sr & 0x7; sr >>= 3; xmc[31] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[32] = sr & 0x7; sr >>= 3; xmc[33] = sr & 0x7; sr >>= 3; xmc[34] = sr & 0x7; sr >>= 3; sr = *c++; /* 25 */ xmc[35] = sr & 0x7; sr >>= 3; xmc[36] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[37] = sr & 0x7; sr >>= 3; xmc[38] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 4; Nc[3] = sr & 0x7f; sr >>= 7; bc[3] = sr & 0x3; sr >>= 2; Mc[3] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 1; xmaxc[3] = sr & 0x3f; sr >>= 6; xmc[39] = sr & 0x7; sr >>= 3; sr = *c++; xmc[40] = sr & 0x7; sr >>= 3; xmc[41] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; /* 30 */ xmc[42] = sr & 0x7; sr >>= 3; xmc[43] = sr & 0x7; sr >>= 3; xmc[44] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[45] = sr & 0x7; sr >>= 3; xmc[46] = sr & 0x7; sr >>= 3; xmc[47] = sr & 0x7; sr >>= 3; sr = *c++; xmc[48] = sr & 0x7; sr >>= 3; xmc[49] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[50] = sr & 0x7; sr >>= 3; xmc[51] = sr & 0x7; sr >>= 3; s->frame_chain = sr & 0xf; } else { sr = s->frame_chain; sr |= (uword)*c++ << 4; /* 1 */ LARc[0] = sr & 0x3f; sr >>= 6; LARc[1] = sr & 0x3f; sr >>= 6; sr = *c++; LARc[2] = sr & 0x1f; sr >>= 5; sr |= (uword)*c++ << 3; LARc[3] = sr & 0x1f; sr >>= 5; LARc[4] = sr & 0xf; sr >>= 4; sr |= (uword)*c++ << 2; LARc[5] = sr & 0xf; sr >>= 4; LARc[6] = sr & 0x7; sr >>= 3; LARc[7] = sr & 0x7; sr >>= 3; sr = *c++; /* 5 */ Nc[0] = sr & 0x7f; sr >>= 7; sr |= (uword)*c++ << 1; bc[0] = sr & 0x3; sr >>= 2; Mc[0] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 5; xmaxc[0] = sr & 0x3f; sr >>= 6; xmc[0] = sr & 0x7; sr >>= 3; xmc[1] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[2] = sr & 0x7; sr >>= 3; xmc[3] = sr & 0x7; sr >>= 3; xmc[4] = sr & 0x7; sr >>= 3; sr = *c++; xmc[5] = sr & 0x7; sr >>= 3; xmc[6] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; /* 10 */ xmc[7] = sr & 0x7; sr >>= 3; xmc[8] = sr & 0x7; sr >>= 3; xmc[9] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[10] = sr & 0x7; sr >>= 3; xmc[11] = sr & 0x7; sr >>= 3; xmc[12] = sr & 0x7; sr >>= 3; sr = *c++; Nc[1] = sr & 0x7f; sr >>= 7; sr |= (uword)*c++ << 1; bc[1] = sr & 0x3; sr >>= 2; Mc[1] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 5; xmaxc[1] = sr & 0x3f; sr >>= 6; xmc[13] = sr & 0x7; sr >>= 3; xmc[14] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; /* 15 */ xmc[15] = sr & 0x7; sr >>= 3; xmc[16] = sr & 0x7; sr >>= 3; xmc[17] = sr & 0x7; sr >>= 3; sr = *c++; xmc[18] = sr & 0x7; sr >>= 3; xmc[19] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[20] = sr & 0x7; sr >>= 3; xmc[21] = sr & 0x7; sr >>= 3; xmc[22] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[23] = sr & 0x7; sr >>= 3; xmc[24] = sr & 0x7; sr >>= 3; xmc[25] = sr & 0x7; sr >>= 3; sr = *c++; Nc[2] = sr & 0x7f; sr >>= 7; sr |= (uword)*c++ << 1; /* 20 */ bc[2] = sr & 0x3; sr >>= 2; Mc[2] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 5; xmaxc[2] = sr & 0x3f; sr >>= 6; xmc[26] = sr & 0x7; sr >>= 3; xmc[27] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[28] = sr & 0x7; sr >>= 3; xmc[29] = sr & 0x7; sr >>= 3; xmc[30] = sr & 0x7; sr >>= 3; sr = *c++; xmc[31] = sr & 0x7; sr >>= 3; xmc[32] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[33] = sr & 0x7; sr >>= 3; xmc[34] = sr & 0x7; sr >>= 3; xmc[35] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; /* 25 */ xmc[36] = sr & 0x7; sr >>= 3; xmc[37] = sr & 0x7; sr >>= 3; xmc[38] = sr & 0x7; sr >>= 3; sr = *c++; Nc[3] = sr & 0x7f; sr >>= 7; sr |= (uword)*c++ << 1; bc[3] = sr & 0x3; sr >>= 2; Mc[3] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 5; xmaxc[3] = sr & 0x3f; sr >>= 6; xmc[39] = sr & 0x7; sr >>= 3; xmc[40] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[41] = sr & 0x7; sr >>= 3; xmc[42] = sr & 0x7; sr >>= 3; xmc[43] = sr & 0x7; sr >>= 3; sr = *c++; /* 30 */ xmc[44] = sr & 0x7; sr >>= 3; xmc[45] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[46] = sr & 0x7; sr >>= 3; xmc[47] = sr & 0x7; sr >>= 3; xmc[48] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[49] = sr & 0x7; sr >>= 3; xmc[50] = sr & 0x7; sr >>= 3; xmc[51] = sr & 0x7; sr >>= 3; } } else #endif { /* GSM_MAGIC = (*c >> 4) & 0xF; */ if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1; LARc[0] = (*c++ & 0xF) << 2; /* 1 */ LARc[0] |= (*c >> 6) & 0x3; LARc[1] = *c++ & 0x3F; LARc[2] = (*c >> 3) & 0x1F; LARc[3] = (*c++ & 0x7) << 2; LARc[3] |= (*c >> 6) & 0x3; LARc[4] = (*c >> 2) & 0xF; LARc[5] = (*c++ & 0x3) << 2; LARc[5] |= (*c >> 6) & 0x3; LARc[6] = (*c >> 3) & 0x7; LARc[7] = *c++ & 0x7; Nc[0] = (*c >> 1) & 0x7F; bc[0] = (*c++ & 0x1) << 1; bc[0] |= (*c >> 7) & 0x1; Mc[0] = (*c >> 5) & 0x3; xmaxc[0] = (*c++ & 0x1F) << 1; xmaxc[0] |= (*c >> 7) & 0x1; xmc[0] = (*c >> 4) & 0x7; xmc[1] = (*c >> 1) & 0x7; xmc[2] = (*c++ & 0x1) << 2; xmc[2] |= (*c >> 6) & 0x3; xmc[3] = (*c >> 3) & 0x7; xmc[4] = *c++ & 0x7; xmc[5] = (*c >> 5) & 0x7; xmc[6] = (*c >> 2) & 0x7; xmc[7] = (*c++ & 0x3) << 1; /* 10 */ xmc[7] |= (*c >> 7) & 0x1; xmc[8] = (*c >> 4) & 0x7; xmc[9] = (*c >> 1) & 0x7; xmc[10] = (*c++ & 0x1) << 2; xmc[10] |= (*c >> 6) & 0x3; xmc[11] = (*c >> 3) & 0x7; xmc[12] = *c++ & 0x7; Nc[1] = (*c >> 1) & 0x7F; bc[1] = (*c++ & 0x1) << 1; bc[1] |= (*c >> 7) & 0x1; Mc[1] = (*c >> 5) & 0x3; xmaxc[1] = (*c++ & 0x1F) << 1; xmaxc[1] |= (*c >> 7) & 0x1; xmc[13] = (*c >> 4) & 0x7; xmc[14] = (*c >> 1) & 0x7; xmc[15] = (*c++ & 0x1) << 2; xmc[15] |= (*c >> 6) & 0x3; xmc[16] = (*c >> 3) & 0x7; xmc[17] = *c++ & 0x7; xmc[18] = (*c >> 5) & 0x7; xmc[19] = (*c >> 2) & 0x7; xmc[20] = (*c++ & 0x3) << 1; xmc[20] |= (*c >> 7) & 0x1; xmc[21] = (*c >> 4) & 0x7; xmc[22] = (*c >> 1) & 0x7; xmc[23] = (*c++ & 0x1) << 2; xmc[23] |= (*c >> 6) & 0x3; xmc[24] = (*c >> 3) & 0x7; xmc[25] = *c++ & 0x7; Nc[2] = (*c >> 1) & 0x7F; bc[2] = (*c++ & 0x1) << 1; /* 20 */ bc[2] |= (*c >> 7) & 0x1; Mc[2] = (*c >> 5) & 0x3; xmaxc[2] = (*c++ & 0x1F) << 1; xmaxc[2] |= (*c >> 7) & 0x1; xmc[26] = (*c >> 4) & 0x7; xmc[27] = (*c >> 1) & 0x7; xmc[28] = (*c++ & 0x1) << 2; xmc[28] |= (*c >> 6) & 0x3; xmc[29] = (*c >> 3) & 0x7; xmc[30] = *c++ & 0x7; xmc[31] = (*c >> 5) & 0x7; xmc[32] = (*c >> 2) & 0x7; xmc[33] = (*c++ & 0x3) << 1; xmc[33] |= (*c >> 7) & 0x1; xmc[34] = (*c >> 4) & 0x7; xmc[35] = (*c >> 1) & 0x7; xmc[36] = (*c++ & 0x1) << 2; xmc[36] |= (*c >> 6) & 0x3; xmc[37] = (*c >> 3) & 0x7; xmc[38] = *c++ & 0x7; Nc[3] = (*c >> 1) & 0x7F; bc[3] = (*c++ & 0x1) << 1; bc[3] |= (*c >> 7) & 0x1; Mc[3] = (*c >> 5) & 0x3; xmaxc[3] = (*c++ & 0x1F) << 1; xmaxc[3] |= (*c >> 7) & 0x1; xmc[39] = (*c >> 4) & 0x7; xmc[40] = (*c >> 1) & 0x7; xmc[41] = (*c++ & 0x1) << 2; xmc[41] |= (*c >> 6) & 0x3; xmc[42] = (*c >> 3) & 0x7; xmc[43] = *c++ & 0x7; /* 30 */ xmc[44] = (*c >> 5) & 0x7; xmc[45] = (*c >> 2) & 0x7; xmc[46] = (*c++ & 0x3) << 1; xmc[46] |= (*c >> 7) & 0x1; xmc[47] = (*c >> 4) & 0x7; xmc[48] = (*c >> 1) & 0x7; xmc[49] = (*c++ & 0x1) << 2; xmc[49] |= (*c >> 6) & 0x3; xmc[50] = (*c >> 3) & 0x7; xmc[51] = *c & 0x7; /* 33 */ } Gsm_Decoder(s, LARc, Nc, bc, Mc, xmaxc, xmc, target); return 0; } ================================================ FILE: deps/pjsip/third_party/gsm/src/gsm_destroy.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_destroy.c,v 1.3 1994/11/28 19:52:25 jutta Exp $ */ #include "gsm.h" #include "config.h" #include "proto.h" #ifdef HAS_STDLIB_H # include #else # ifdef HAS_MALLOC_H # include # else extern void free(); # endif #endif void gsm_destroy P1((S), gsm S) { if (S) free((char *)S); } ================================================ FILE: deps/pjsip/third_party/gsm/src/gsm_encode.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_encode.c,v 1.2 1996/07/02 09:59:05 jutta Exp $ */ #include "private.h" #include "gsm.h" #include "proto.h" void gsm_encode P3((s, source, c), gsm s, gsm_signal * source, gsm_byte * c) { word LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4]; Gsm_Coder(s, source, LARc, Nc, bc, Mc, xmaxc, xmc); /* variable size GSM_MAGIC 4 LARc[0] 6 LARc[1] 6 LARc[2] 5 LARc[3] 5 LARc[4] 4 LARc[5] 4 LARc[6] 3 LARc[7] 3 Nc[0] 7 bc[0] 2 Mc[0] 2 xmaxc[0] 6 xmc[0] 3 xmc[1] 3 xmc[2] 3 xmc[3] 3 xmc[4] 3 xmc[5] 3 xmc[6] 3 xmc[7] 3 xmc[8] 3 xmc[9] 3 xmc[10] 3 xmc[11] 3 xmc[12] 3 Nc[1] 7 bc[1] 2 Mc[1] 2 xmaxc[1] 6 xmc[13] 3 xmc[14] 3 xmc[15] 3 xmc[16] 3 xmc[17] 3 xmc[18] 3 xmc[19] 3 xmc[20] 3 xmc[21] 3 xmc[22] 3 xmc[23] 3 xmc[24] 3 xmc[25] 3 Nc[2] 7 bc[2] 2 Mc[2] 2 xmaxc[2] 6 xmc[26] 3 xmc[27] 3 xmc[28] 3 xmc[29] 3 xmc[30] 3 xmc[31] 3 xmc[32] 3 xmc[33] 3 xmc[34] 3 xmc[35] 3 xmc[36] 3 xmc[37] 3 xmc[38] 3 Nc[3] 7 bc[3] 2 Mc[3] 2 xmaxc[3] 6 xmc[39] 3 xmc[40] 3 xmc[41] 3 xmc[42] 3 xmc[43] 3 xmc[44] 3 xmc[45] 3 xmc[46] 3 xmc[47] 3 xmc[48] 3 xmc[49] 3 xmc[50] 3 xmc[51] 3 */ #ifdef WAV49 if (s->wav_fmt) { s->frame_index = !s->frame_index; if (s->frame_index) { uword sr; sr = 0; sr = sr >> 6 | LARc[0] << 10; sr = sr >> 6 | LARc[1] << 10; *c++ = sr >> 4; sr = sr >> 5 | LARc[2] << 11; *c++ = sr >> 7; sr = sr >> 5 | LARc[3] << 11; sr = sr >> 4 | LARc[4] << 12; *c++ = sr >> 6; sr = sr >> 4 | LARc[5] << 12; sr = sr >> 3 | LARc[6] << 13; *c++ = sr >> 7; sr = sr >> 3 | LARc[7] << 13; sr = sr >> 7 | Nc[0] << 9; *c++ = sr >> 5; sr = sr >> 2 | bc[0] << 14; sr = sr >> 2 | Mc[0] << 14; sr = sr >> 6 | xmaxc[0] << 10; *c++ = sr >> 3; sr = sr >> 3 | xmc[0] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[1] << 13; sr = sr >> 3 | xmc[2] << 13; sr = sr >> 3 | xmc[3] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[4] << 13; sr = sr >> 3 | xmc[5] << 13; sr = sr >> 3 | xmc[6] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[7] << 13; sr = sr >> 3 | xmc[8] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[9] << 13; sr = sr >> 3 | xmc[10] << 13; sr = sr >> 3 | xmc[11] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[12] << 13; sr = sr >> 7 | Nc[1] << 9; *c++ = sr >> 5; sr = sr >> 2 | bc[1] << 14; sr = sr >> 2 | Mc[1] << 14; sr = sr >> 6 | xmaxc[1] << 10; *c++ = sr >> 3; sr = sr >> 3 | xmc[13] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[14] << 13; sr = sr >> 3 | xmc[15] << 13; sr = sr >> 3 | xmc[16] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[17] << 13; sr = sr >> 3 | xmc[18] << 13; sr = sr >> 3 | xmc[19] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[20] << 13; sr = sr >> 3 | xmc[21] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[22] << 13; sr = sr >> 3 | xmc[23] << 13; sr = sr >> 3 | xmc[24] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[25] << 13; sr = sr >> 7 | Nc[2] << 9; *c++ = sr >> 5; sr = sr >> 2 | bc[2] << 14; sr = sr >> 2 | Mc[2] << 14; sr = sr >> 6 | xmaxc[2] << 10; *c++ = sr >> 3; sr = sr >> 3 | xmc[26] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[27] << 13; sr = sr >> 3 | xmc[28] << 13; sr = sr >> 3 | xmc[29] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[30] << 13; sr = sr >> 3 | xmc[31] << 13; sr = sr >> 3 | xmc[32] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[33] << 13; sr = sr >> 3 | xmc[34] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[35] << 13; sr = sr >> 3 | xmc[36] << 13; sr = sr >> 3 | xmc[37] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[38] << 13; sr = sr >> 7 | Nc[3] << 9; *c++ = sr >> 5; sr = sr >> 2 | bc[3] << 14; sr = sr >> 2 | Mc[3] << 14; sr = sr >> 6 | xmaxc[3] << 10; *c++ = sr >> 3; sr = sr >> 3 | xmc[39] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[40] << 13; sr = sr >> 3 | xmc[41] << 13; sr = sr >> 3 | xmc[42] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[43] << 13; sr = sr >> 3 | xmc[44] << 13; sr = sr >> 3 | xmc[45] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[46] << 13; sr = sr >> 3 | xmc[47] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[48] << 13; sr = sr >> 3 | xmc[49] << 13; sr = sr >> 3 | xmc[50] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[51] << 13; sr = sr >> 4; *c = sr >> 8; s->frame_chain = *c; } else { uword sr; sr = 0; sr = sr >> 4 | s->frame_chain << 12; sr = sr >> 6 | LARc[0] << 10; *c++ = sr >> 6; sr = sr >> 6 | LARc[1] << 10; *c++ = sr >> 8; sr = sr >> 5 | LARc[2] << 11; sr = sr >> 5 | LARc[3] << 11; *c++ = sr >> 6; sr = sr >> 4 | LARc[4] << 12; sr = sr >> 4 | LARc[5] << 12; *c++ = sr >> 6; sr = sr >> 3 | LARc[6] << 13; sr = sr >> 3 | LARc[7] << 13; *c++ = sr >> 8; sr = sr >> 7 | Nc[0] << 9; sr = sr >> 2 | bc[0] << 14; *c++ = sr >> 7; sr = sr >> 2 | Mc[0] << 14; sr = sr >> 6 | xmaxc[0] << 10; *c++ = sr >> 7; sr = sr >> 3 | xmc[0] << 13; sr = sr >> 3 | xmc[1] << 13; sr = sr >> 3 | xmc[2] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[3] << 13; sr = sr >> 3 | xmc[4] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[5] << 13; sr = sr >> 3 | xmc[6] << 13; sr = sr >> 3 | xmc[7] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[8] << 13; sr = sr >> 3 | xmc[9] << 13; sr = sr >> 3 | xmc[10] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[11] << 13; sr = sr >> 3 | xmc[12] << 13; *c++ = sr >> 8; sr = sr >> 7 | Nc[1] << 9; sr = sr >> 2 | bc[1] << 14; *c++ = sr >> 7; sr = sr >> 2 | Mc[1] << 14; sr = sr >> 6 | xmaxc[1] << 10; *c++ = sr >> 7; sr = sr >> 3 | xmc[13] << 13; sr = sr >> 3 | xmc[14] << 13; sr = sr >> 3 | xmc[15] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[16] << 13; sr = sr >> 3 | xmc[17] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[18] << 13; sr = sr >> 3 | xmc[19] << 13; sr = sr >> 3 | xmc[20] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[21] << 13; sr = sr >> 3 | xmc[22] << 13; sr = sr >> 3 | xmc[23] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[24] << 13; sr = sr >> 3 | xmc[25] << 13; *c++ = sr >> 8; sr = sr >> 7 | Nc[2] << 9; sr = sr >> 2 | bc[2] << 14; *c++ = sr >> 7; sr = sr >> 2 | Mc[2] << 14; sr = sr >> 6 | xmaxc[2] << 10; *c++ = sr >> 7; sr = sr >> 3 | xmc[26] << 13; sr = sr >> 3 | xmc[27] << 13; sr = sr >> 3 | xmc[28] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[29] << 13; sr = sr >> 3 | xmc[30] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[31] << 13; sr = sr >> 3 | xmc[32] << 13; sr = sr >> 3 | xmc[33] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[34] << 13; sr = sr >> 3 | xmc[35] << 13; sr = sr >> 3 | xmc[36] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[37] << 13; sr = sr >> 3 | xmc[38] << 13; *c++ = sr >> 8; sr = sr >> 7 | Nc[3] << 9; sr = sr >> 2 | bc[3] << 14; *c++ = sr >> 7; sr = sr >> 2 | Mc[3] << 14; sr = sr >> 6 | xmaxc[3] << 10; *c++ = sr >> 7; sr = sr >> 3 | xmc[39] << 13; sr = sr >> 3 | xmc[40] << 13; sr = sr >> 3 | xmc[41] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[42] << 13; sr = sr >> 3 | xmc[43] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[44] << 13; sr = sr >> 3 | xmc[45] << 13; sr = sr >> 3 | xmc[46] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[47] << 13; sr = sr >> 3 | xmc[48] << 13; sr = sr >> 3 | xmc[49] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[50] << 13; sr = sr >> 3 | xmc[51] << 13; *c++ = sr >> 8; } } else #endif /* WAV49 */ { *c++ = ((GSM_MAGIC & 0xF) << 4) /* 1 */ | ((LARc[0] >> 2) & 0xF); *c++ = ((LARc[0] & 0x3) << 6) | (LARc[1] & 0x3F); *c++ = ((LARc[2] & 0x1F) << 3) | ((LARc[3] >> 2) & 0x7); *c++ = ((LARc[3] & 0x3) << 6) | ((LARc[4] & 0xF) << 2) | ((LARc[5] >> 2) & 0x3); *c++ = ((LARc[5] & 0x3) << 6) | ((LARc[6] & 0x7) << 3) | (LARc[7] & 0x7); *c++ = ((Nc[0] & 0x7F) << 1) | ((bc[0] >> 1) & 0x1); *c++ = ((bc[0] & 0x1) << 7) | ((Mc[0] & 0x3) << 5) | ((xmaxc[0] >> 1) & 0x1F); *c++ = ((xmaxc[0] & 0x1) << 7) | ((xmc[0] & 0x7) << 4) | ((xmc[1] & 0x7) << 1) | ((xmc[2] >> 2) & 0x1); *c++ = ((xmc[2] & 0x3) << 6) | ((xmc[3] & 0x7) << 3) | (xmc[4] & 0x7); *c++ = ((xmc[5] & 0x7) << 5) /* 10 */ | ((xmc[6] & 0x7) << 2) | ((xmc[7] >> 1) & 0x3); *c++ = ((xmc[7] & 0x1) << 7) | ((xmc[8] & 0x7) << 4) | ((xmc[9] & 0x7) << 1) | ((xmc[10] >> 2) & 0x1); *c++ = ((xmc[10] & 0x3) << 6) | ((xmc[11] & 0x7) << 3) | (xmc[12] & 0x7); *c++ = ((Nc[1] & 0x7F) << 1) | ((bc[1] >> 1) & 0x1); *c++ = ((bc[1] & 0x1) << 7) | ((Mc[1] & 0x3) << 5) | ((xmaxc[1] >> 1) & 0x1F); *c++ = ((xmaxc[1] & 0x1) << 7) | ((xmc[13] & 0x7) << 4) | ((xmc[14] & 0x7) << 1) | ((xmc[15] >> 2) & 0x1); *c++ = ((xmc[15] & 0x3) << 6) | ((xmc[16] & 0x7) << 3) | (xmc[17] & 0x7); *c++ = ((xmc[18] & 0x7) << 5) | ((xmc[19] & 0x7) << 2) | ((xmc[20] >> 1) & 0x3); *c++ = ((xmc[20] & 0x1) << 7) | ((xmc[21] & 0x7) << 4) | ((xmc[22] & 0x7) << 1) | ((xmc[23] >> 2) & 0x1); *c++ = ((xmc[23] & 0x3) << 6) | ((xmc[24] & 0x7) << 3) | (xmc[25] & 0x7); *c++ = ((Nc[2] & 0x7F) << 1) /* 20 */ | ((bc[2] >> 1) & 0x1); *c++ = ((bc[2] & 0x1) << 7) | ((Mc[2] & 0x3) << 5) | ((xmaxc[2] >> 1) & 0x1F); *c++ = ((xmaxc[2] & 0x1) << 7) | ((xmc[26] & 0x7) << 4) | ((xmc[27] & 0x7) << 1) | ((xmc[28] >> 2) & 0x1); *c++ = ((xmc[28] & 0x3) << 6) | ((xmc[29] & 0x7) << 3) | (xmc[30] & 0x7); *c++ = ((xmc[31] & 0x7) << 5) | ((xmc[32] & 0x7) << 2) | ((xmc[33] >> 1) & 0x3); *c++ = ((xmc[33] & 0x1) << 7) | ((xmc[34] & 0x7) << 4) | ((xmc[35] & 0x7) << 1) | ((xmc[36] >> 2) & 0x1); *c++ = ((xmc[36] & 0x3) << 6) | ((xmc[37] & 0x7) << 3) | (xmc[38] & 0x7); *c++ = ((Nc[3] & 0x7F) << 1) | ((bc[3] >> 1) & 0x1); *c++ = ((bc[3] & 0x1) << 7) | ((Mc[3] & 0x3) << 5) | ((xmaxc[3] >> 1) & 0x1F); *c++ = ((xmaxc[3] & 0x1) << 7) | ((xmc[39] & 0x7) << 4) | ((xmc[40] & 0x7) << 1) | ((xmc[41] >> 2) & 0x1); *c++ = ((xmc[41] & 0x3) << 6) /* 30 */ | ((xmc[42] & 0x7) << 3) | (xmc[43] & 0x7); *c++ = ((xmc[44] & 0x7) << 5) | ((xmc[45] & 0x7) << 2) | ((xmc[46] >> 1) & 0x3); *c++ = ((xmc[46] & 0x1) << 7) | ((xmc[47] & 0x7) << 4) | ((xmc[48] & 0x7) << 1) | ((xmc[49] >> 2) & 0x1); *c++ = ((xmc[49] & 0x3) << 6) | ((xmc[50] & 0x7) << 3) | (xmc[51] & 0x7); } } ================================================ FILE: deps/pjsip/third_party/gsm/src/gsm_explode.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_explode.c,v 1.2 1996/07/02 14:32:42 jutta Exp jutta $ */ #include "private.h" #include "gsm.h" #include "proto.h" int gsm_explode P3((s, c, target), gsm s, gsm_byte * c, gsm_signal * target) { # define LARc target # define Nc *((gsm_signal (*) [17])(target + 8)) # define bc *((gsm_signal (*) [17])(target + 9)) # define Mc *((gsm_signal (*) [17])(target + 10)) # define xmaxc *((gsm_signal (*) [17])(target + 11)) #ifdef WAV49 if (s->wav_fmt) { uword sr = 0; if (s->frame_index == 1) { sr = *c++; LARc[0] = sr & 0x3f; sr >>= 6; sr |= (uword)*c++ << 2; LARc[1] = sr & 0x3f; sr >>= 6; sr |= (uword)*c++ << 4; LARc[2] = sr & 0x1f; sr >>= 5; LARc[3] = sr & 0x1f; sr >>= 5; sr |= (uword)*c++ << 2; LARc[4] = sr & 0xf; sr >>= 4; LARc[5] = sr & 0xf; sr >>= 4; sr |= (uword)*c++ << 2; /* 5 */ LARc[6] = sr & 0x7; sr >>= 3; LARc[7] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 4; Nc[0] = sr & 0x7f; sr >>= 7; bc[0] = sr & 0x3; sr >>= 2; Mc[0] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 1; xmaxc[0] = sr & 0x3f; sr >>= 6; #undef xmc #define xmc (target + 12) xmc[0] = sr & 0x7; sr >>= 3; sr = *c++; xmc[1] = sr & 0x7; sr >>= 3; xmc[2] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[3] = sr & 0x7; sr >>= 3; xmc[4] = sr & 0x7; sr >>= 3; xmc[5] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; /* 10 */ xmc[6] = sr & 0x7; sr >>= 3; xmc[7] = sr & 0x7; sr >>= 3; xmc[8] = sr & 0x7; sr >>= 3; sr = *c++; xmc[9] = sr & 0x7; sr >>= 3; xmc[10] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[11] = sr & 0x7; sr >>= 3; xmc[12] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 4; Nc[1] = sr & 0x7f; sr >>= 7; bc[1] = sr & 0x3; sr >>= 2; Mc[1] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 1; xmaxc[1] = sr & 0x3f; sr >>= 6; #undef xmc #define xmc (target + 29 - 13) xmc[13] = sr & 0x7; sr >>= 3; sr = *c++; /* 15 */ xmc[14] = sr & 0x7; sr >>= 3; xmc[15] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[16] = sr & 0x7; sr >>= 3; xmc[17] = sr & 0x7; sr >>= 3; xmc[18] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[19] = sr & 0x7; sr >>= 3; xmc[20] = sr & 0x7; sr >>= 3; xmc[21] = sr & 0x7; sr >>= 3; sr = *c++; xmc[22] = sr & 0x7; sr >>= 3; xmc[23] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[24] = sr & 0x7; sr >>= 3; xmc[25] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 4; /* 20 */ Nc[2] = sr & 0x7f; sr >>= 7; bc[2] = sr & 0x3; sr >>= 2; Mc[2] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 1; xmaxc[2] = sr & 0x3f; sr >>= 6; #undef xmc #define xmc (target + 46 - 26) xmc[26] = sr & 0x7; sr >>= 3; sr = *c++; xmc[27] = sr & 0x7; sr >>= 3; xmc[28] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[29] = sr & 0x7; sr >>= 3; xmc[30] = sr & 0x7; sr >>= 3; xmc[31] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[32] = sr & 0x7; sr >>= 3; xmc[33] = sr & 0x7; sr >>= 3; xmc[34] = sr & 0x7; sr >>= 3; sr = *c++; /* 25 */ xmc[35] = sr & 0x7; sr >>= 3; xmc[36] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[37] = sr & 0x7; sr >>= 3; xmc[38] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 4; Nc[3] = sr & 0x7f; sr >>= 7; bc[3] = sr & 0x3; sr >>= 2; Mc[3] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 1; xmaxc[3] = sr & 0x3f; sr >>= 6; #undef xmc #define xmc (target + 63 - 39) xmc[39] = sr & 0x7; sr >>= 3; sr = *c++; xmc[40] = sr & 0x7; sr >>= 3; xmc[41] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; /* 30 */ xmc[42] = sr & 0x7; sr >>= 3; xmc[43] = sr & 0x7; sr >>= 3; xmc[44] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[45] = sr & 0x7; sr >>= 3; xmc[46] = sr & 0x7; sr >>= 3; xmc[47] = sr & 0x7; sr >>= 3; sr = *c++; xmc[48] = sr & 0x7; sr >>= 3; xmc[49] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[50] = sr & 0x7; sr >>= 3; xmc[51] = sr & 0x7; sr >>= 3; s->frame_chain = sr & 0xf; } else { sr = s->frame_chain; sr |= (uword)*c++ << 4; /* 1 */ LARc[0] = sr & 0x3f; sr >>= 6; LARc[1] = sr & 0x3f; sr >>= 6; sr = *c++; LARc[2] = sr & 0x1f; sr >>= 5; sr |= (uword)*c++ << 3; LARc[3] = sr & 0x1f; sr >>= 5; LARc[4] = sr & 0xf; sr >>= 4; sr |= (uword)*c++ << 2; LARc[5] = sr & 0xf; sr >>= 4; LARc[6] = sr & 0x7; sr >>= 3; LARc[7] = sr & 0x7; sr >>= 3; sr = *c++; /* 5 */ Nc[0] = sr & 0x7f; sr >>= 7; sr |= (uword)*c++ << 1; bc[0] = sr & 0x3; sr >>= 2; Mc[0] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 5; xmaxc[0] = sr & 0x3f; sr >>= 6; #undef xmc #define xmc (target + 12) xmc[0] = sr & 0x7; sr >>= 3; xmc[1] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[2] = sr & 0x7; sr >>= 3; xmc[3] = sr & 0x7; sr >>= 3; xmc[4] = sr & 0x7; sr >>= 3; sr = *c++; xmc[5] = sr & 0x7; sr >>= 3; xmc[6] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; /* 10 */ xmc[7] = sr & 0x7; sr >>= 3; xmc[8] = sr & 0x7; sr >>= 3; xmc[9] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[10] = sr & 0x7; sr >>= 3; xmc[11] = sr & 0x7; sr >>= 3; xmc[12] = sr & 0x7; sr >>= 3; sr = *c++; Nc[1] = sr & 0x7f; sr >>= 7; sr |= (uword)*c++ << 1; bc[1] = sr & 0x3; sr >>= 2; Mc[1] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 5; xmaxc[1] = sr & 0x3f; sr >>= 6; #undef xmc #define xmc (target + 29 - 13) xmc[13] = sr & 0x7; sr >>= 3; xmc[14] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; /* 15 */ xmc[15] = sr & 0x7; sr >>= 3; xmc[16] = sr & 0x7; sr >>= 3; xmc[17] = sr & 0x7; sr >>= 3; sr = *c++; xmc[18] = sr & 0x7; sr >>= 3; xmc[19] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[20] = sr & 0x7; sr >>= 3; xmc[21] = sr & 0x7; sr >>= 3; xmc[22] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[23] = sr & 0x7; sr >>= 3; xmc[24] = sr & 0x7; sr >>= 3; xmc[25] = sr & 0x7; sr >>= 3; sr = *c++; Nc[2] = sr & 0x7f; sr >>= 7; sr |= (uword)*c++ << 1; /* 20 */ bc[2] = sr & 0x3; sr >>= 2; Mc[2] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 5; xmaxc[2] = sr & 0x3f; sr >>= 6; #undef xmc #define xmc (target + 46 - 26) xmc[26] = sr & 0x7; sr >>= 3; xmc[27] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[28] = sr & 0x7; sr >>= 3; xmc[29] = sr & 0x7; sr >>= 3; xmc[30] = sr & 0x7; sr >>= 3; sr = *c++; xmc[31] = sr & 0x7; sr >>= 3; xmc[32] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[33] = sr & 0x7; sr >>= 3; xmc[34] = sr & 0x7; sr >>= 3; xmc[35] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; /* 25 */ xmc[36] = sr & 0x7; sr >>= 3; xmc[37] = sr & 0x7; sr >>= 3; xmc[38] = sr & 0x7; sr >>= 3; sr = *c++; Nc[3] = sr & 0x7f; sr >>= 7; sr |= (uword)*c++ << 1; bc[3] = sr & 0x3; sr >>= 2; Mc[3] = sr & 0x3; sr >>= 2; sr |= (uword)*c++ << 5; xmaxc[3] = sr & 0x3f; sr >>= 6; #undef xmc #define xmc (target + 63 - 39) xmc[39] = sr & 0x7; sr >>= 3; xmc[40] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[41] = sr & 0x7; sr >>= 3; xmc[42] = sr & 0x7; sr >>= 3; xmc[43] = sr & 0x7; sr >>= 3; sr = *c++; /* 30 */ xmc[44] = sr & 0x7; sr >>= 3; xmc[45] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 2; xmc[46] = sr & 0x7; sr >>= 3; xmc[47] = sr & 0x7; sr >>= 3; xmc[48] = sr & 0x7; sr >>= 3; sr |= (uword)*c++ << 1; xmc[49] = sr & 0x7; sr >>= 3; xmc[50] = sr & 0x7; sr >>= 3; xmc[51] = sr & 0x7; sr >>= 3; } } else #endif { /* GSM_MAGIC = (*c >> 4) & 0xF; */ if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1; LARc[0] = (*c++ & 0xF) << 2; /* 1 */ LARc[0] |= (*c >> 6) & 0x3; LARc[1] = *c++ & 0x3F; LARc[2] = (*c >> 3) & 0x1F; LARc[3] = (*c++ & 0x7) << 2; LARc[3] |= (*c >> 6) & 0x3; LARc[4] = (*c >> 2) & 0xF; LARc[5] = (*c++ & 0x3) << 2; LARc[5] |= (*c >> 6) & 0x3; LARc[6] = (*c >> 3) & 0x7; LARc[7] = *c++ & 0x7; Nc[0] = (*c >> 1) & 0x7F; bc[0] = (*c++ & 0x1) << 1; bc[0] |= (*c >> 7) & 0x1; Mc[0] = (*c >> 5) & 0x3; xmaxc[0] = (*c++ & 0x1F) << 1; xmaxc[0] |= (*c >> 7) & 0x1; #undef xmc #define xmc (target + 12) xmc[0] = (*c >> 4) & 0x7; xmc[1] = (*c >> 1) & 0x7; xmc[2] = (*c++ & 0x1) << 2; xmc[2] |= (*c >> 6) & 0x3; xmc[3] = (*c >> 3) & 0x7; xmc[4] = *c++ & 0x7; xmc[5] = (*c >> 5) & 0x7; xmc[6] = (*c >> 2) & 0x7; xmc[7] = (*c++ & 0x3) << 1; /* 10 */ xmc[7] |= (*c >> 7) & 0x1; xmc[8] = (*c >> 4) & 0x7; xmc[9] = (*c >> 1) & 0x7; xmc[10] = (*c++ & 0x1) << 2; xmc[10] |= (*c >> 6) & 0x3; xmc[11] = (*c >> 3) & 0x7; xmc[12] = *c++ & 0x7; Nc[1] = (*c >> 1) & 0x7F; bc[1] = (*c++ & 0x1) << 1; bc[1] |= (*c >> 7) & 0x1; Mc[1] = (*c >> 5) & 0x3; xmaxc[1] = (*c++ & 0x1F) << 1; xmaxc[1] |= (*c >> 7) & 0x1; #undef xmc #define xmc (target + 29 - 13) xmc[13] = (*c >> 4) & 0x7; xmc[14] = (*c >> 1) & 0x7; xmc[15] = (*c++ & 0x1) << 2; xmc[15] |= (*c >> 6) & 0x3; xmc[16] = (*c >> 3) & 0x7; xmc[17] = *c++ & 0x7; xmc[18] = (*c >> 5) & 0x7; xmc[19] = (*c >> 2) & 0x7; xmc[20] = (*c++ & 0x3) << 1; xmc[20] |= (*c >> 7) & 0x1; xmc[21] = (*c >> 4) & 0x7; xmc[22] = (*c >> 1) & 0x7; xmc[23] = (*c++ & 0x1) << 2; xmc[23] |= (*c >> 6) & 0x3; xmc[24] = (*c >> 3) & 0x7; xmc[25] = *c++ & 0x7; Nc[2] = (*c >> 1) & 0x7F; bc[2] = (*c++ & 0x1) << 1; /* 20 */ bc[2] |= (*c >> 7) & 0x1; Mc[2] = (*c >> 5) & 0x3; xmaxc[2] = (*c++ & 0x1F) << 1; xmaxc[2] |= (*c >> 7) & 0x1; #undef xmc #define xmc (target + 46 - 26) xmc[26] = (*c >> 4) & 0x7; xmc[27] = (*c >> 1) & 0x7; xmc[28] = (*c++ & 0x1) << 2; xmc[28] |= (*c >> 6) & 0x3; xmc[29] = (*c >> 3) & 0x7; xmc[30] = *c++ & 0x7; xmc[31] = (*c >> 5) & 0x7; xmc[32] = (*c >> 2) & 0x7; xmc[33] = (*c++ & 0x3) << 1; xmc[33] |= (*c >> 7) & 0x1; xmc[34] = (*c >> 4) & 0x7; xmc[35] = (*c >> 1) & 0x7; xmc[36] = (*c++ & 0x1) << 2; xmc[36] |= (*c >> 6) & 0x3; xmc[37] = (*c >> 3) & 0x7; xmc[38] = *c++ & 0x7; Nc[3] = (*c >> 1) & 0x7F; bc[3] = (*c++ & 0x1) << 1; bc[3] |= (*c >> 7) & 0x1; Mc[3] = (*c >> 5) & 0x3; xmaxc[3] = (*c++ & 0x1F) << 1; xmaxc[3] |= (*c >> 7) & 0x1; #undef xmc #define xmc (target + 63 - 39) xmc[39] = (*c >> 4) & 0x7; xmc[40] = (*c >> 1) & 0x7; xmc[41] = (*c++ & 0x1) << 2; xmc[41] |= (*c >> 6) & 0x3; xmc[42] = (*c >> 3) & 0x7; xmc[43] = *c++ & 0x7; /* 30 */ xmc[44] = (*c >> 5) & 0x7; xmc[45] = (*c >> 2) & 0x7; xmc[46] = (*c++ & 0x3) << 1; xmc[46] |= (*c >> 7) & 0x1; xmc[47] = (*c >> 4) & 0x7; xmc[48] = (*c >> 1) & 0x7; xmc[49] = (*c++ & 0x1) << 2; xmc[49] |= (*c >> 6) & 0x3; xmc[50] = (*c >> 3) & 0x7; xmc[51] = *c & 0x7; /* 33 */ } return 0; } ================================================ FILE: deps/pjsip/third_party/gsm/src/gsm_implode.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_implode.c,v 1.2 1996/07/02 14:32:43 jutta Exp jutta $ */ #include "private.h" #include "gsm.h" #include "proto.h" void gsm_implode P3((s, source, c), gsm s, gsm_signal * source, gsm_byte * c) { /* variable size index GSM_MAGIC 4 - LARc[0] 6 0 LARc[1] 6 1 LARc[2] 5 2 LARc[3] 5 3 LARc[4] 4 4 LARc[5] 4 5 LARc[6] 3 6 LARc[7] 3 7 Nc[0] 7 8 bc[0] 2 9 Mc[0] 2 10 xmaxc[0] 6 11 xmc[0] 3 12 xmc[1] 3 13 xmc[2] 3 14 xmc[3] 3 15 xmc[4] 3 16 xmc[5] 3 17 xmc[6] 3 18 xmc[7] 3 19 xmc[8] 3 20 xmc[9] 3 21 xmc[10] 3 22 xmc[11] 3 23 xmc[12] 3 24 Nc[1] 7 25 bc[1] 2 26 Mc[1] 2 27 xmaxc[1] 6 28 xmc[13] 3 29 xmc[14] 3 30 xmc[15] 3 31 xmc[16] 3 32 xmc[17] 3 33 xmc[18] 3 34 xmc[19] 3 35 xmc[20] 3 36 xmc[21] 3 37 xmc[22] 3 38 xmc[23] 3 39 xmc[24] 3 40 xmc[25] 3 41 Nc[2] 7 42 bc[2] 2 43 Mc[2] 2 44 xmaxc[2] 6 45 xmc[26] 3 46 xmc[27] 3 47 xmc[28] 3 48 xmc[29] 3 49 xmc[30] 3 50 xmc[31] 3 51 xmc[32] 3 52 xmc[33] 3 53 xmc[34] 3 54 xmc[35] 3 55 xmc[36] 3 56 xmc[37] 3 57 xmc[38] 3 58 Nc[3] 7 59 bc[3] 2 60 Mc[3] 2 61 xmaxc[3] 6 62 xmc[39] 3 63 xmc[40] 3 64 xmc[41] 3 65 xmc[42] 3 66 xmc[43] 3 67 xmc[44] 3 68 xmc[45] 3 69 xmc[46] 3 70 xmc[47] 3 71 xmc[48] 3 72 xmc[49] 3 73 xmc[50] 3 74 xmc[51] 3 75 */ /* There are 76 parameters per frame. The first eight are * unique. The remaining 68 are four identical subframes of * 17 parameters each. gsm_implode converts from a representation * of these parameters as values in one array of signed words * to the "packed" version of a GSM frame. */ # define LARc source # define Nc *((gsm_signal (*) [17])(source + 8)) # define bc *((gsm_signal (*) [17])(source + 9)) # define Mc *((gsm_signal (*) [17])(source + 10)) # define xmaxc *((gsm_signal (*) [17])(source + 11)) #ifdef WAV49 if (s->wav_fmt) { uword sr = 0; if (s->frame_index) { sr = sr >> 6 | LARc[0] << 10; sr = sr >> 6 | LARc[1] << 10; *c++ = sr >> 4; sr = sr >> 5 | LARc[2] << 11; *c++ = sr >> 7; sr = sr >> 5 | LARc[3] << 11; sr = sr >> 4 | LARc[4] << 12; *c++ = sr >> 6; sr = sr >> 4 | LARc[5] << 12; sr = sr >> 3 | LARc[6] << 13; *c++ = sr >> 7; sr = sr >> 3 | LARc[7] << 13; sr = sr >> 7 | Nc[0] << 9; *c++ = sr >> 5; sr = sr >> 2 | bc[0] << 14; sr = sr >> 2 | Mc[0] << 14; sr = sr >> 6 | xmaxc[0] << 10; *c++ = sr >> 3; #undef xmc #define xmc (source + 12) sr = sr >> 3 | xmc[0] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[1] << 13; sr = sr >> 3 | xmc[2] << 13; sr = sr >> 3 | xmc[3] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[4] << 13; sr = sr >> 3 | xmc[5] << 13; sr = sr >> 3 | xmc[6] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[7] << 13; sr = sr >> 3 | xmc[8] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[9] << 13; sr = sr >> 3 | xmc[10] << 13; sr = sr >> 3 | xmc[11] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[12] << 13; sr = sr >> 7 | Nc[1] << 9; *c++ = sr >> 5; sr = sr >> 2 | bc[1] << 14; sr = sr >> 2 | Mc[1] << 14; sr = sr >> 6 | xmaxc[1] << 10; *c++ = sr >> 3; #undef xmc #define xmc (source + 29 - 13) sr = sr >> 3 | xmc[13] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[14] << 13; sr = sr >> 3 | xmc[15] << 13; sr = sr >> 3 | xmc[16] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[17] << 13; sr = sr >> 3 | xmc[18] << 13; sr = sr >> 3 | xmc[19] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[20] << 13; sr = sr >> 3 | xmc[21] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[22] << 13; sr = sr >> 3 | xmc[23] << 13; sr = sr >> 3 | xmc[24] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[25] << 13; sr = sr >> 7 | Nc[2] << 9; *c++ = sr >> 5; sr = sr >> 2 | bc[2] << 14; sr = sr >> 2 | Mc[2] << 14; sr = sr >> 6 | xmaxc[2] << 10; *c++ = sr >> 3; #undef xmc #define xmc (source + 46 - 26) sr = sr >> 3 | xmc[26] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[27] << 13; sr = sr >> 3 | xmc[28] << 13; sr = sr >> 3 | xmc[29] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[30] << 13; sr = sr >> 3 | xmc[31] << 13; sr = sr >> 3 | xmc[32] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[33] << 13; sr = sr >> 3 | xmc[34] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[35] << 13; sr = sr >> 3 | xmc[36] << 13; sr = sr >> 3 | xmc[37] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[38] << 13; sr = sr >> 7 | Nc[3] << 9; *c++ = sr >> 5; sr = sr >> 2 | bc[3] << 14; sr = sr >> 2 | Mc[3] << 14; sr = sr >> 6 | xmaxc[3] << 10; *c++ = sr >> 3; #undef xmc #define xmc (source + 63 - 39) sr = sr >> 3 | xmc[39] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[40] << 13; sr = sr >> 3 | xmc[41] << 13; sr = sr >> 3 | xmc[42] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[43] << 13; sr = sr >> 3 | xmc[44] << 13; sr = sr >> 3 | xmc[45] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[46] << 13; sr = sr >> 3 | xmc[47] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[48] << 13; sr = sr >> 3 | xmc[49] << 13; sr = sr >> 3 | xmc[50] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[51] << 13; sr = sr >> 4; *c = sr >> 8; s->frame_chain = *c; } else { sr = sr >> 4 | s->frame_chain << 12; sr = sr >> 6 | LARc[0] << 10; *c++ = sr >> 6; sr = sr >> 6 | LARc[1] << 10; *c++ = sr >> 8; sr = sr >> 5 | LARc[2] << 11; sr = sr >> 5 | LARc[3] << 11; *c++ = sr >> 6; sr = sr >> 4 | LARc[4] << 12; sr = sr >> 4 | LARc[5] << 12; *c++ = sr >> 6; sr = sr >> 3 | LARc[6] << 13; sr = sr >> 3 | LARc[7] << 13; *c++ = sr >> 8; sr = sr >> 7 | Nc[0] << 9; sr = sr >> 2 | bc[0] << 14; *c++ = sr >> 7; sr = sr >> 2 | Mc[0] << 14; sr = sr >> 6 | xmaxc[0] << 10; *c++ = sr >> 7; #undef xmc #define xmc (source + 12) sr = sr >> 3 | xmc[0] << 13; sr = sr >> 3 | xmc[1] << 13; sr = sr >> 3 | xmc[2] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[3] << 13; sr = sr >> 3 | xmc[4] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[5] << 13; sr = sr >> 3 | xmc[6] << 13; sr = sr >> 3 | xmc[7] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[8] << 13; sr = sr >> 3 | xmc[9] << 13; sr = sr >> 3 | xmc[10] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[11] << 13; sr = sr >> 3 | xmc[12] << 13; *c++ = sr >> 8; sr = sr >> 7 | Nc[1] << 9; sr = sr >> 2 | bc[1] << 14; *c++ = sr >> 7; sr = sr >> 2 | Mc[1] << 14; sr = sr >> 6 | xmaxc[1] << 10; *c++ = sr >> 7; #undef xmc #define xmc (source + 29 - 13) sr = sr >> 3 | xmc[13] << 13; sr = sr >> 3 | xmc[14] << 13; sr = sr >> 3 | xmc[15] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[16] << 13; sr = sr >> 3 | xmc[17] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[18] << 13; sr = sr >> 3 | xmc[19] << 13; sr = sr >> 3 | xmc[20] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[21] << 13; sr = sr >> 3 | xmc[22] << 13; sr = sr >> 3 | xmc[23] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[24] << 13; sr = sr >> 3 | xmc[25] << 13; *c++ = sr >> 8; sr = sr >> 7 | Nc[2] << 9; sr = sr >> 2 | bc[2] << 14; *c++ = sr >> 7; sr = sr >> 2 | Mc[2] << 14; sr = sr >> 6 | xmaxc[2] << 10; *c++ = sr >> 7; #undef xmc #define xmc (source + 46 - 26) sr = sr >> 3 | xmc[26] << 13; sr = sr >> 3 | xmc[27] << 13; sr = sr >> 3 | xmc[28] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[29] << 13; sr = sr >> 3 | xmc[30] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[31] << 13; sr = sr >> 3 | xmc[32] << 13; sr = sr >> 3 | xmc[33] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[34] << 13; sr = sr >> 3 | xmc[35] << 13; sr = sr >> 3 | xmc[36] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[37] << 13; sr = sr >> 3 | xmc[38] << 13; *c++ = sr >> 8; sr = sr >> 7 | Nc[3] << 9; sr = sr >> 2 | bc[3] << 14; *c++ = sr >> 7; sr = sr >> 2 | Mc[3] << 14; sr = sr >> 6 | xmaxc[3] << 10; *c++ = sr >> 7; #undef xmc #define xmc (source + 63 - 39) sr = sr >> 3 | xmc[39] << 13; sr = sr >> 3 | xmc[40] << 13; sr = sr >> 3 | xmc[41] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[42] << 13; sr = sr >> 3 | xmc[43] << 13; *c++ = sr >> 8; sr = sr >> 3 | xmc[44] << 13; sr = sr >> 3 | xmc[45] << 13; sr = sr >> 3 | xmc[46] << 13; *c++ = sr >> 7; sr = sr >> 3 | xmc[47] << 13; sr = sr >> 3 | xmc[48] << 13; sr = sr >> 3 | xmc[49] << 13; *c++ = sr >> 6; sr = sr >> 3 | xmc[50] << 13; sr = sr >> 3 | xmc[51] << 13; *c++ = sr >> 8; } } else #endif { *c++ = ((GSM_MAGIC & 0xF) << 4) /* 1 */ | ((LARc[0] >> 2) & 0xF); *c++ = ((LARc[0] & 0x3) << 6) | (LARc[1] & 0x3F); *c++ = ((LARc[2] & 0x1F) << 3) | ((LARc[3] >> 2) & 0x7); *c++ = ((LARc[3] & 0x3) << 6) | ((LARc[4] & 0xF) << 2) | ((LARc[5] >> 2) & 0x3); *c++ = ((LARc[5] & 0x3) << 6) | ((LARc[6] & 0x7) << 3) | (LARc[7] & 0x7); *c++ = ((Nc[0] & 0x7F) << 1) | ((bc[0] >> 1) & 0x1); *c++ = ((bc[0] & 0x1) << 7) | ((Mc[0] & 0x3) << 5) | ((xmaxc[0] >> 1) & 0x1F); *c++ = ((xmaxc[0] & 0x1) << 7) #undef xmc #define xmc (source + 12) | ((xmc[0] & 0x7) << 4) | ((xmc[1] & 0x7) << 1) | ((xmc[2] >> 2) & 0x1); *c++ = ((xmc[2] & 0x3) << 6) | ((xmc[3] & 0x7) << 3) | (xmc[4] & 0x7); *c++ = ((xmc[5] & 0x7) << 5) /* 10 */ | ((xmc[6] & 0x7) << 2) | ((xmc[7] >> 1) & 0x3); *c++ = ((xmc[7] & 0x1) << 7) | ((xmc[8] & 0x7) << 4) | ((xmc[9] & 0x7) << 1) | ((xmc[10] >> 2) & 0x1); *c++ = ((xmc[10] & 0x3) << 6) | ((xmc[11] & 0x7) << 3) | (xmc[12] & 0x7); *c++ = ((Nc[1] & 0x7F) << 1) | ((bc[1] >> 1) & 0x1); *c++ = ((bc[1] & 0x1) << 7) | ((Mc[1] & 0x3) << 5) | ((xmaxc[1] >> 1) & 0x1F); *c++ = ((xmaxc[1] & 0x1) << 7) #undef xmc #define xmc (source + 29 - 13) | ((xmc[13] & 0x7) << 4) | ((xmc[14] & 0x7) << 1) | ((xmc[15] >> 2) & 0x1); *c++ = ((xmc[15] & 0x3) << 6) | ((xmc[16] & 0x7) << 3) | (xmc[17] & 0x7); *c++ = ((xmc[18] & 0x7) << 5) | ((xmc[19] & 0x7) << 2) | ((xmc[20] >> 1) & 0x3); *c++ = ((xmc[20] & 0x1) << 7) | ((xmc[21] & 0x7) << 4) | ((xmc[22] & 0x7) << 1) | ((xmc[23] >> 2) & 0x1); *c++ = ((xmc[23] & 0x3) << 6) | ((xmc[24] & 0x7) << 3) | (xmc[25] & 0x7); *c++ = ((Nc[2] & 0x7F) << 1) /* 20 */ | ((bc[2] >> 1) & 0x1); *c++ = ((bc[2] & 0x1) << 7) | ((Mc[2] & 0x3) << 5) | ((xmaxc[2] >> 1) & 0x1F); *c++ = ((xmaxc[2] & 0x1) << 7) #undef xmc #define xmc (source + 46 - 26) | ((xmc[26] & 0x7) << 4) | ((xmc[27] & 0x7) << 1) | ((xmc[28] >> 2) & 0x1); *c++ = ((xmc[28] & 0x3) << 6) | ((xmc[29] & 0x7) << 3) | (xmc[30] & 0x7); *c++ = ((xmc[31] & 0x7) << 5) | ((xmc[32] & 0x7) << 2) | ((xmc[33] >> 1) & 0x3); *c++ = ((xmc[33] & 0x1) << 7) | ((xmc[34] & 0x7) << 4) | ((xmc[35] & 0x7) << 1) | ((xmc[36] >> 2) & 0x1); *c++ = ((xmc[36] & 0x3) << 6) | ((xmc[37] & 0x7) << 3) | (xmc[38] & 0x7); *c++ = ((Nc[3] & 0x7F) << 1) | ((bc[3] >> 1) & 0x1); *c++ = ((bc[3] & 0x1) << 7) | ((Mc[3] & 0x3) << 5) | ((xmaxc[3] >> 1) & 0x1F); *c++ = ((xmaxc[3] & 0x1) << 7) #undef xmc #define xmc (source + 63 - 39) | ((xmc[39] & 0x7) << 4) | ((xmc[40] & 0x7) << 1) | ((xmc[41] >> 2) & 0x1); *c++ = ((xmc[41] & 0x3) << 6) /* 30 */ | ((xmc[42] & 0x7) << 3) | (xmc[43] & 0x7); *c++ = ((xmc[44] & 0x7) << 5) | ((xmc[45] & 0x7) << 2) | ((xmc[46] >> 1) & 0x3); *c++ = ((xmc[46] & 0x1) << 7) | ((xmc[47] & 0x7) << 4) | ((xmc[48] & 0x7) << 1) | ((xmc[49] >> 2) & 0x1); *c++ = ((xmc[49] & 0x3) << 6) | ((xmc[50] & 0x7) << 3) | (xmc[51] & 0x7); } } ================================================ FILE: deps/pjsip/third_party/gsm/src/gsm_option.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_option.c,v 1.3 1996/07/02 09:59:05 jutta Exp $ */ #include "private.h" #include "gsm.h" #include "proto.h" int gsm_option P3((r, opt, val), gsm r, int opt, int * val) { int result = -1; switch (opt) { case GSM_OPT_LTP_CUT: #ifdef LTP_CUT result = r->ltp_cut; if (val) r->ltp_cut = *val; #endif break; case GSM_OPT_VERBOSE: #ifndef NDEBUG result = r->verbose; if (val) r->verbose = *val; #endif break; case GSM_OPT_FAST: #if defined(FAST) && defined(USE_FLOAT_MUL) result = r->fast; if (val) r->fast = !!*val; #endif break; case GSM_OPT_FRAME_CHAIN: #ifdef WAV49 result = r->frame_chain; if (val) r->frame_chain = *val; #endif break; case GSM_OPT_FRAME_INDEX: #ifdef WAV49 result = r->frame_index; if (val) r->frame_index = *val; #endif break; case GSM_OPT_WAV49: #ifdef WAV49 result = r->wav_fmt; if (val) r->wav_fmt = !!*val; #endif break; default: break; } return result; } ================================================ FILE: deps/pjsip/third_party/gsm/src/gsm_print.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_print.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */ #include #include "private.h" #include "gsm.h" #include "proto.h" int gsm_print P3((f, s, c), FILE * f, gsm s, gsm_byte * c) { word LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4]; /* GSM_MAGIC = (*c >> 4) & 0xF; */ if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1; LARc[0] = (*c++ & 0xF) << 2; /* 1 */ LARc[0] |= (*c >> 6) & 0x3; LARc[1] = *c++ & 0x3F; LARc[2] = (*c >> 3) & 0x1F; LARc[3] = (*c++ & 0x7) << 2; LARc[3] |= (*c >> 6) & 0x3; LARc[4] = (*c >> 2) & 0xF; LARc[5] = (*c++ & 0x3) << 2; LARc[5] |= (*c >> 6) & 0x3; LARc[6] = (*c >> 3) & 0x7; LARc[7] = *c++ & 0x7; Nc[0] = (*c >> 1) & 0x7F; bc[0] = (*c++ & 0x1) << 1; bc[0] |= (*c >> 7) & 0x1; Mc[0] = (*c >> 5) & 0x3; xmaxc[0] = (*c++ & 0x1F) << 1; xmaxc[0] |= (*c >> 7) & 0x1; xmc[0] = (*c >> 4) & 0x7; xmc[1] = (*c >> 1) & 0x7; xmc[2] = (*c++ & 0x1) << 2; xmc[2] |= (*c >> 6) & 0x3; xmc[3] = (*c >> 3) & 0x7; xmc[4] = *c++ & 0x7; xmc[5] = (*c >> 5) & 0x7; xmc[6] = (*c >> 2) & 0x7; xmc[7] = (*c++ & 0x3) << 1; /* 10 */ xmc[7] |= (*c >> 7) & 0x1; xmc[8] = (*c >> 4) & 0x7; xmc[9] = (*c >> 1) & 0x7; xmc[10] = (*c++ & 0x1) << 2; xmc[10] |= (*c >> 6) & 0x3; xmc[11] = (*c >> 3) & 0x7; xmc[12] = *c++ & 0x7; Nc[1] = (*c >> 1) & 0x7F; bc[1] = (*c++ & 0x1) << 1; bc[1] |= (*c >> 7) & 0x1; Mc[1] = (*c >> 5) & 0x3; xmaxc[1] = (*c++ & 0x1F) << 1; xmaxc[1] |= (*c >> 7) & 0x1; xmc[13] = (*c >> 4) & 0x7; xmc[14] = (*c >> 1) & 0x7; xmc[15] = (*c++ & 0x1) << 2; xmc[15] |= (*c >> 6) & 0x3; xmc[16] = (*c >> 3) & 0x7; xmc[17] = *c++ & 0x7; xmc[18] = (*c >> 5) & 0x7; xmc[19] = (*c >> 2) & 0x7; xmc[20] = (*c++ & 0x3) << 1; xmc[20] |= (*c >> 7) & 0x1; xmc[21] = (*c >> 4) & 0x7; xmc[22] = (*c >> 1) & 0x7; xmc[23] = (*c++ & 0x1) << 2; xmc[23] |= (*c >> 6) & 0x3; xmc[24] = (*c >> 3) & 0x7; xmc[25] = *c++ & 0x7; Nc[2] = (*c >> 1) & 0x7F; bc[2] = (*c++ & 0x1) << 1; /* 20 */ bc[2] |= (*c >> 7) & 0x1; Mc[2] = (*c >> 5) & 0x3; xmaxc[2] = (*c++ & 0x1F) << 1; xmaxc[2] |= (*c >> 7) & 0x1; xmc[26] = (*c >> 4) & 0x7; xmc[27] = (*c >> 1) & 0x7; xmc[28] = (*c++ & 0x1) << 2; xmc[28] |= (*c >> 6) & 0x3; xmc[29] = (*c >> 3) & 0x7; xmc[30] = *c++ & 0x7; xmc[31] = (*c >> 5) & 0x7; xmc[32] = (*c >> 2) & 0x7; xmc[33] = (*c++ & 0x3) << 1; xmc[33] |= (*c >> 7) & 0x1; xmc[34] = (*c >> 4) & 0x7; xmc[35] = (*c >> 1) & 0x7; xmc[36] = (*c++ & 0x1) << 2; xmc[36] |= (*c >> 6) & 0x3; xmc[37] = (*c >> 3) & 0x7; xmc[38] = *c++ & 0x7; Nc[3] = (*c >> 1) & 0x7F; bc[3] = (*c++ & 0x1) << 1; bc[3] |= (*c >> 7) & 0x1; Mc[3] = (*c >> 5) & 0x3; xmaxc[3] = (*c++ & 0x1F) << 1; xmaxc[3] |= (*c >> 7) & 0x1; xmc[39] = (*c >> 4) & 0x7; xmc[40] = (*c >> 1) & 0x7; xmc[41] = (*c++ & 0x1) << 2; xmc[41] |= (*c >> 6) & 0x3; xmc[42] = (*c >> 3) & 0x7; xmc[43] = *c++ & 0x7; /* 30 */ xmc[44] = (*c >> 5) & 0x7; xmc[45] = (*c >> 2) & 0x7; xmc[46] = (*c++ & 0x3) << 1; xmc[46] |= (*c >> 7) & 0x1; xmc[47] = (*c >> 4) & 0x7; xmc[48] = (*c >> 1) & 0x7; xmc[49] = (*c++ & 0x1) << 2; xmc[49] |= (*c >> 6) & 0x3; xmc[50] = (*c >> 3) & 0x7; xmc[51] = *c & 0x7; /* 33 */ fprintf(f, "LARc:\t%2.2d %2.2d %2.2d %2.2d %2.2d %2.2d %2.2d %2.2d\n", LARc[0],LARc[1],LARc[2],LARc[3],LARc[4],LARc[5],LARc[6],LARc[7]); fprintf(f, "#1: Nc %4.4d bc %d Mc %d xmaxc %d\n", Nc[0], bc[0], Mc[0], xmaxc[0]); fprintf(f, "\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n", xmc[0],xmc[1],xmc[2],xmc[3],xmc[4],xmc[5],xmc[6], xmc[7],xmc[8],xmc[9],xmc[10],xmc[11],xmc[12] ); fprintf(f, "#2: Nc %4.4d bc %d Mc %d xmaxc %d\n", Nc[1], bc[1], Mc[1], xmaxc[1]); fprintf(f, "\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n", xmc[13+0],xmc[13+1],xmc[13+2],xmc[13+3],xmc[13+4],xmc[13+5], xmc[13+6], xmc[13+7],xmc[13+8],xmc[13+9],xmc[13+10],xmc[13+11], xmc[13+12] ); fprintf(f, "#3: Nc %4.4d bc %d Mc %d xmaxc %d\n", Nc[2], bc[2], Mc[2], xmaxc[2]); fprintf(f, "\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n", xmc[26+0],xmc[26+1],xmc[26+2],xmc[26+3],xmc[26+4],xmc[26+5], xmc[26+6], xmc[26+7],xmc[26+8],xmc[26+9],xmc[26+10],xmc[26+11], xmc[26+12] ); fprintf(f, "#4: Nc %4.4d bc %d Mc %d xmaxc %d\n", Nc[3], bc[3], Mc[3], xmaxc[3]); fprintf(f, "\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n", xmc[39+0],xmc[39+1],xmc[39+2],xmc[39+3],xmc[39+4],xmc[39+5], xmc[39+6], xmc[39+7],xmc[39+8],xmc[39+9],xmc[39+10],xmc[39+11], xmc[39+12] ); return 0; } ================================================ FILE: deps/pjsip/third_party/gsm/src/long_term.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/long_term.c,v 1.6 1996/07/02 12:33:19 jutta Exp $ */ #include "config.h" #include #include #include "private.h" #include "gsm.h" #include "proto.h" /* * 4.2.11 .. 4.2.12 LONG TERM PREDICTOR (LTP) SECTION */ /* * This module computes the LTP gain (bc) and the LTP lag (Nc) * for the long term analysis filter. This is done by calculating a * maximum of the cross-correlation function between the current * sub-segment short term residual signal d[0..39] (output of * the short term analysis filter; for simplification the index * of this array begins at 0 and ends at 39 for each sub-segment of the * RPE-LTP analysis) and the previous reconstructed short term * residual signal dp[ -120 .. -1 ]. A dynamic scaling must be * performed to avoid overflow. */ /* The next procedure exists in six versions. First two integer * version (if USE_FLOAT_MUL is not defined); then four floating * point versions, twice with proper scaling (USE_FLOAT_MUL defined), * once without (USE_FLOAT_MUL and FAST defined, and fast run-time * option used). Every pair has first a Cut version (see the -C * option to toast or the LTP_CUT option to gsm_option()), then the * uncut one. (For a detailed explanation of why this is altogether * a bad idea, see Henry Spencer and Geoff Collyer, ``#ifdef Considered * Harmful''.) */ #ifndef USE_FLOAT_MUL #ifdef LTP_CUT static void Cut_Calculation_of_the_LTP_parameters P5((st, d,dp,bc_out,Nc_out), struct gsm_state * st, register word * d, /* [0..39] IN */ register word * dp, /* [-120..-1] IN */ word * bc_out, /* OUT */ word * Nc_out /* OUT */ ) { register int k, lambda; word Nc, bc; word wt[40]; longword L_result; longword L_max, L_power; word R, S, dmax, scal, best_k; word ltp_cut; register word temp, wt_k; /* Search of the optimum scaling of d[0..39]. */ dmax = 0; for (k = 0; k <= 39; k++) { temp = d[k]; temp = GSM_ABS( temp ); if (temp > dmax) { dmax = temp; best_k = k; } } temp = 0; if (dmax == 0) scal = 0; else { assert(dmax > 0); temp = gsm_norm( (longword)dmax << 16 ); } if (temp > 6) scal = 0; else scal = 6 - temp; assert(scal >= 0); /* Search for the maximum cross-correlation and coding of the LTP lag */ L_max = 0; Nc = 40; /* index for the maximum cross-correlation */ wt_k = SASR(d[best_k], scal); for (lambda = 40; lambda <= 120; lambda++) { L_result = (longword)wt_k * dp[best_k - lambda]; if (L_result > L_max) { Nc = lambda; L_max = L_result; } } *Nc_out = Nc; L_max <<= 1; /* Rescaling of L_max */ assert(scal <= 100 && scal >= -100); L_max = L_max >> (6 - scal); /* sub(6, scal) */ assert( Nc <= 120 && Nc >= 40); /* Compute the power of the reconstructed short term residual * signal dp[..] */ L_power = 0; for (k = 0; k <= 39; k++) { register longword L_temp; L_temp = SASR( dp[k - Nc], 3 ); L_power += L_temp * L_temp; } L_power <<= 1; /* from L_MULT */ /* Normalization of L_max and L_power */ if (L_max <= 0) { *bc_out = 0; return; } if (L_max >= L_power) { *bc_out = 3; return; } temp = gsm_norm( L_power ); R = SASR( L_max << temp, 16 ); S = SASR( L_power << temp, 16 ); /* Coding of the LTP gain */ /* Table 4.3a must be used to obtain the level DLB[i] for the * quantization of the LTP gain b to get the coded version bc. */ for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break; *bc_out = bc; } #endif /* LTP_CUT */ static void Calculation_of_the_LTP_parameters P4((d,dp,bc_out,Nc_out), register word * d, /* [0..39] IN */ register word * dp, /* [-120..-1] IN */ word * bc_out, /* OUT */ word * Nc_out /* OUT */ ) { register int k, lambda; word Nc, bc; word wt[40]; longword L_max, L_power; word R, S, dmax, scal; register word temp; /* Search of the optimum scaling of d[0..39]. */ dmax = 0; for (k = 0; k <= 39; k++) { temp = d[k]; temp = GSM_ABS( temp ); if (temp > dmax) dmax = temp; } temp = 0; if (dmax == 0) scal = 0; else { assert(dmax > 0); temp = gsm_norm( (longword)dmax << 16 ); } if (temp > 6) scal = 0; else scal = 6 - temp; assert(scal >= 0); /* Initialization of a working array wt */ for (k = 0; k <= 39; k++) wt[k] = SASR( d[k], scal ); /* Search for the maximum cross-correlation and coding of the LTP lag */ L_max = 0; Nc = 40; /* index for the maximum cross-correlation */ for (lambda = 40; lambda <= 120; lambda++) { # undef STEP # define STEP(k) (longword)wt[k] * dp[k - lambda] register longword L_result; L_result = STEP(0) ; L_result += STEP(1) ; L_result += STEP(2) ; L_result += STEP(3) ; L_result += STEP(4) ; L_result += STEP(5) ; L_result += STEP(6) ; L_result += STEP(7) ; L_result += STEP(8) ; L_result += STEP(9) ; L_result += STEP(10) ; L_result += STEP(11) ; L_result += STEP(12) ; L_result += STEP(13) ; L_result += STEP(14) ; L_result += STEP(15) ; L_result += STEP(16) ; L_result += STEP(17) ; L_result += STEP(18) ; L_result += STEP(19) ; L_result += STEP(20) ; L_result += STEP(21) ; L_result += STEP(22) ; L_result += STEP(23) ; L_result += STEP(24) ; L_result += STEP(25) ; L_result += STEP(26) ; L_result += STEP(27) ; L_result += STEP(28) ; L_result += STEP(29) ; L_result += STEP(30) ; L_result += STEP(31) ; L_result += STEP(32) ; L_result += STEP(33) ; L_result += STEP(34) ; L_result += STEP(35) ; L_result += STEP(36) ; L_result += STEP(37) ; L_result += STEP(38) ; L_result += STEP(39) ; if (L_result > L_max) { Nc = lambda; L_max = L_result; } } *Nc_out = Nc; L_max <<= 1; /* Rescaling of L_max */ assert(scal <= 100 && scal >= -100); L_max = L_max >> (6 - scal); /* sub(6, scal) */ assert( Nc <= 120 && Nc >= 40); /* Compute the power of the reconstructed short term residual * signal dp[..] */ L_power = 0; for (k = 0; k <= 39; k++) { register longword L_temp; L_temp = SASR( dp[k - Nc], 3 ); L_power += L_temp * L_temp; } L_power <<= 1; /* from L_MULT */ /* Normalization of L_max and L_power */ if (L_max <= 0) { *bc_out = 0; return; } if (L_max >= L_power) { *bc_out = 3; return; } temp = gsm_norm( L_power ); R = SASR( L_max << temp, 16 ); S = SASR( L_power << temp, 16 ); /* Coding of the LTP gain */ /* Table 4.3a must be used to obtain the level DLB[i] for the * quantization of the LTP gain b to get the coded version bc. */ for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break; *bc_out = bc; } #else /* USE_FLOAT_MUL */ #ifdef LTP_CUT static void Cut_Calculation_of_the_LTP_parameters P5((st, d,dp,bc_out,Nc_out), struct gsm_state * st, /* IN */ register word * d, /* [0..39] IN */ register word * dp, /* [-120..-1] IN */ word * bc_out, /* OUT */ word * Nc_out /* OUT */ ) { register int k, lambda; word Nc, bc; word ltp_cut; float wt_float[40]; float dp_float_base[120], * dp_float = dp_float_base + 120; longword L_max, L_power; word R, S, dmax, scal; register word temp; /* Search of the optimum scaling of d[0..39]. */ dmax = 0; for (k = 0; k <= 39; k++) { temp = d[k]; temp = GSM_ABS( temp ); if (temp > dmax) dmax = temp; } temp = 0; if (dmax == 0) scal = 0; else { assert(dmax > 0); temp = gsm_norm( (longword)dmax << 16 ); } if (temp > 6) scal = 0; else scal = 6 - temp; assert(scal >= 0); ltp_cut = (longword)SASR(dmax, scal) * st->ltp_cut / 100; /* Initialization of a working array wt */ for (k = 0; k < 40; k++) { register word w = SASR( d[k], scal ); if (w < 0 ? w > -ltp_cut : w < ltp_cut) { wt_float[k] = 0.0; } else { wt_float[k] = w; } } for (k = -120; k < 0; k++) dp_float[k] = dp[k]; /* Search for the maximum cross-correlation and coding of the LTP lag */ L_max = 0; Nc = 40; /* index for the maximum cross-correlation */ for (lambda = 40; lambda <= 120; lambda += 9) { /* Calculate L_result for l = lambda .. lambda + 9. */ register float *lp = dp_float - lambda; register float W; register float a = lp[-8], b = lp[-7], c = lp[-6], d = lp[-5], e = lp[-4], f = lp[-3], g = lp[-2], h = lp[-1]; register float E; register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0, S5 = 0, S6 = 0, S7 = 0, S8 = 0; # undef STEP # define STEP(K, a, b, c, d, e, f, g, h) \ if ((W = wt_float[K]) != 0.0) { \ E = W * a; S8 += E; \ E = W * b; S7 += E; \ E = W * c; S6 += E; \ E = W * d; S5 += E; \ E = W * e; S4 += E; \ E = W * f; S3 += E; \ E = W * g; S2 += E; \ E = W * h; S1 += E; \ a = lp[K]; \ E = W * a; S0 += E; } else (a = lp[K]) # define STEP_A(K) STEP(K, a, b, c, d, e, f, g, h) # define STEP_B(K) STEP(K, b, c, d, e, f, g, h, a) # define STEP_C(K) STEP(K, c, d, e, f, g, h, a, b) # define STEP_D(K) STEP(K, d, e, f, g, h, a, b, c) # define STEP_E(K) STEP(K, e, f, g, h, a, b, c, d) # define STEP_F(K) STEP(K, f, g, h, a, b, c, d, e) # define STEP_G(K) STEP(K, g, h, a, b, c, d, e, f) # define STEP_H(K) STEP(K, h, a, b, c, d, e, f, g) STEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3); STEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7); STEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11); STEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15); STEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19); STEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23); STEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27); STEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31); STEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35); STEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39); if (S0 > L_max) { L_max = S0; Nc = lambda; } if (S1 > L_max) { L_max = S1; Nc = lambda + 1; } if (S2 > L_max) { L_max = S2; Nc = lambda + 2; } if (S3 > L_max) { L_max = S3; Nc = lambda + 3; } if (S4 > L_max) { L_max = S4; Nc = lambda + 4; } if (S5 > L_max) { L_max = S5; Nc = lambda + 5; } if (S6 > L_max) { L_max = S6; Nc = lambda + 6; } if (S7 > L_max) { L_max = S7; Nc = lambda + 7; } if (S8 > L_max) { L_max = S8; Nc = lambda + 8; } } *Nc_out = Nc; L_max <<= 1; /* Rescaling of L_max */ assert(scal <= 100 && scal >= -100); L_max = L_max >> (6 - scal); /* sub(6, scal) */ assert( Nc <= 120 && Nc >= 40); /* Compute the power of the reconstructed short term residual * signal dp[..] */ L_power = 0; for (k = 0; k <= 39; k++) { register longword L_temp; L_temp = SASR( dp[k - Nc], 3 ); L_power += L_temp * L_temp; } L_power <<= 1; /* from L_MULT */ /* Normalization of L_max and L_power */ if (L_max <= 0) { *bc_out = 0; return; } if (L_max >= L_power) { *bc_out = 3; return; } temp = gsm_norm( L_power ); R = SASR( L_max << temp, 16 ); S = SASR( L_power << temp, 16 ); /* Coding of the LTP gain */ /* Table 4.3a must be used to obtain the level DLB[i] for the * quantization of the LTP gain b to get the coded version bc. */ for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break; *bc_out = bc; } #endif /* LTP_CUT */ static void Calculation_of_the_LTP_parameters P4((d,dp,bc_out,Nc_out), register word * d, /* [0..39] IN */ register word * dp, /* [-120..-1] IN */ word * bc_out, /* OUT */ word * Nc_out /* OUT */ ) { register int k, lambda; word Nc, bc; float wt_float[40]; float dp_float_base[120], * dp_float = dp_float_base + 120; longword L_max, L_power; word R, S, dmax, scal; register word temp; /* Search of the optimum scaling of d[0..39]. */ dmax = 0; for (k = 0; k <= 39; k++) { temp = d[k]; temp = GSM_ABS( temp ); if (temp > dmax) dmax = temp; } temp = 0; if (dmax == 0) scal = 0; else { assert(dmax > 0); temp = gsm_norm( (longword)dmax << 16 ); } if (temp > 6) scal = 0; else scal = 6 - temp; assert(scal >= 0); /* Initialization of a working array wt */ for (k = 0; k < 40; k++) wt_float[k] = SASR( d[k], scal ); for (k = -120; k < 0; k++) dp_float[k] = dp[k]; /* Search for the maximum cross-correlation and coding of the LTP lag */ L_max = 0; Nc = 40; /* index for the maximum cross-correlation */ for (lambda = 40; lambda <= 120; lambda += 9) { /* Calculate L_result for l = lambda .. lambda + 9. */ register float *lp = dp_float - lambda; register float W; register float a = lp[-8], b = lp[-7], c = lp[-6], d = lp[-5], e = lp[-4], f = lp[-3], g = lp[-2], h = lp[-1]; register float E; register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0, S5 = 0, S6 = 0, S7 = 0, S8 = 0; # undef STEP # define STEP(K, a, b, c, d, e, f, g, h) \ W = wt_float[K]; \ E = W * a; S8 += E; \ E = W * b; S7 += E; \ E = W * c; S6 += E; \ E = W * d; S5 += E; \ E = W * e; S4 += E; \ E = W * f; S3 += E; \ E = W * g; S2 += E; \ E = W * h; S1 += E; \ a = lp[K]; \ E = W * a; S0 += E # define STEP_A(K) STEP(K, a, b, c, d, e, f, g, h) # define STEP_B(K) STEP(K, b, c, d, e, f, g, h, a) # define STEP_C(K) STEP(K, c, d, e, f, g, h, a, b) # define STEP_D(K) STEP(K, d, e, f, g, h, a, b, c) # define STEP_E(K) STEP(K, e, f, g, h, a, b, c, d) # define STEP_F(K) STEP(K, f, g, h, a, b, c, d, e) # define STEP_G(K) STEP(K, g, h, a, b, c, d, e, f) # define STEP_H(K) STEP(K, h, a, b, c, d, e, f, g) STEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3); STEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7); STEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11); STEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15); STEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19); STEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23); STEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27); STEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31); STEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35); STEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39); if (S0 > L_max) { L_max = S0; Nc = lambda; } if (S1 > L_max) { L_max = S1; Nc = lambda + 1; } if (S2 > L_max) { L_max = S2; Nc = lambda + 2; } if (S3 > L_max) { L_max = S3; Nc = lambda + 3; } if (S4 > L_max) { L_max = S4; Nc = lambda + 4; } if (S5 > L_max) { L_max = S5; Nc = lambda + 5; } if (S6 > L_max) { L_max = S6; Nc = lambda + 6; } if (S7 > L_max) { L_max = S7; Nc = lambda + 7; } if (S8 > L_max) { L_max = S8; Nc = lambda + 8; } } *Nc_out = Nc; L_max <<= 1; /* Rescaling of L_max */ assert(scal <= 100 && scal >= -100); L_max = L_max >> (6 - scal); /* sub(6, scal) */ assert( Nc <= 120 && Nc >= 40); /* Compute the power of the reconstructed short term residual * signal dp[..] */ L_power = 0; for (k = 0; k <= 39; k++) { register longword L_temp; L_temp = SASR( dp[k - Nc], 3 ); L_power += L_temp * L_temp; } L_power <<= 1; /* from L_MULT */ /* Normalization of L_max and L_power */ if (L_max <= 0) { *bc_out = 0; return; } if (L_max >= L_power) { *bc_out = 3; return; } temp = gsm_norm( L_power ); R = SASR( L_max << temp, 16 ); S = SASR( L_power << temp, 16 ); /* Coding of the LTP gain */ /* Table 4.3a must be used to obtain the level DLB[i] for the * quantization of the LTP gain b to get the coded version bc. */ for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break; *bc_out = bc; } #ifdef FAST #ifdef LTP_CUT static void Cut_Fast_Calculation_of_the_LTP_parameters P5((st, d,dp,bc_out,Nc_out), struct gsm_state * st, /* IN */ register word * d, /* [0..39] IN */ register word * dp, /* [-120..-1] IN */ word * bc_out, /* OUT */ word * Nc_out /* OUT */ ) { register int k, lambda; register float wt_float; word Nc, bc; word wt_max, best_k, ltp_cut; float dp_float_base[120], * dp_float = dp_float_base + 120; register float L_result, L_max, L_power; wt_max = 0; for (k = 0; k < 40; ++k) { if ( d[k] > wt_max) wt_max = d[best_k = k]; else if (-d[k] > wt_max) wt_max = -d[best_k = k]; } assert(wt_max >= 0); wt_float = (float)wt_max; for (k = -120; k < 0; ++k) dp_float[k] = (float)dp[k]; /* Search for the maximum cross-correlation and coding of the LTP lag */ L_max = 0; Nc = 40; /* index for the maximum cross-correlation */ for (lambda = 40; lambda <= 120; lambda++) { L_result = wt_float * dp_float[best_k - lambda]; if (L_result > L_max) { Nc = lambda; L_max = L_result; } } *Nc_out = Nc; if (L_max <= 0.) { *bc_out = 0; return; } /* Compute the power of the reconstructed short term residual * signal dp[..] */ dp_float -= Nc; L_power = 0; for (k = 0; k < 40; ++k) { register float f = dp_float[k]; L_power += f * f; } if (L_max >= L_power) { *bc_out = 3; return; } /* Coding of the LTP gain * Table 4.3a must be used to obtain the level DLB[i] for the * quantization of the LTP gain b to get the coded version bc. */ lambda = L_max / L_power * 32768.; for (bc = 0; bc <= 2; ++bc) if (lambda <= gsm_DLB[bc]) break; *bc_out = bc; } #endif /* LTP_CUT */ static void Fast_Calculation_of_the_LTP_parameters P4((d,dp,bc_out,Nc_out), register word * d, /* [0..39] IN */ register word * dp, /* [-120..-1] IN */ word * bc_out, /* OUT */ word * Nc_out /* OUT */ ) { register int k, lambda; word Nc, bc; float wt_float[40]; float dp_float_base[120], * dp_float = dp_float_base + 120; register float L_max, L_power; for (k = 0; k < 40; ++k) wt_float[k] = (float)d[k]; for (k = -120; k < 0; ++k) dp_float[k] = (float)dp[k]; /* Search for the maximum cross-correlation and coding of the LTP lag */ L_max = 0; Nc = 40; /* index for the maximum cross-correlation */ for (lambda = 40; lambda <= 120; lambda += 9) { /* Calculate L_result for l = lambda .. lambda + 9. */ register float *lp = dp_float - lambda; register float W; register float a = lp[-8], b = lp[-7], c = lp[-6], d = lp[-5], e = lp[-4], f = lp[-3], g = lp[-2], h = lp[-1]; register float E; register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0, S5 = 0, S6 = 0, S7 = 0, S8 = 0; # undef STEP # define STEP(K, a, b, c, d, e, f, g, h) \ W = wt_float[K]; \ E = W * a; S8 += E; \ E = W * b; S7 += E; \ E = W * c; S6 += E; \ E = W * d; S5 += E; \ E = W * e; S4 += E; \ E = W * f; S3 += E; \ E = W * g; S2 += E; \ E = W * h; S1 += E; \ a = lp[K]; \ E = W * a; S0 += E # define STEP_A(K) STEP(K, a, b, c, d, e, f, g, h) # define STEP_B(K) STEP(K, b, c, d, e, f, g, h, a) # define STEP_C(K) STEP(K, c, d, e, f, g, h, a, b) # define STEP_D(K) STEP(K, d, e, f, g, h, a, b, c) # define STEP_E(K) STEP(K, e, f, g, h, a, b, c, d) # define STEP_F(K) STEP(K, f, g, h, a, b, c, d, e) # define STEP_G(K) STEP(K, g, h, a, b, c, d, e, f) # define STEP_H(K) STEP(K, h, a, b, c, d, e, f, g) STEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3); STEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7); STEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11); STEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15); STEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19); STEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23); STEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27); STEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31); STEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35); STEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39); if (S0 > L_max) { L_max = S0; Nc = lambda; } if (S1 > L_max) { L_max = S1; Nc = lambda + 1; } if (S2 > L_max) { L_max = S2; Nc = lambda + 2; } if (S3 > L_max) { L_max = S3; Nc = lambda + 3; } if (S4 > L_max) { L_max = S4; Nc = lambda + 4; } if (S5 > L_max) { L_max = S5; Nc = lambda + 5; } if (S6 > L_max) { L_max = S6; Nc = lambda + 6; } if (S7 > L_max) { L_max = S7; Nc = lambda + 7; } if (S8 > L_max) { L_max = S8; Nc = lambda + 8; } } *Nc_out = Nc; if (L_max <= 0.) { *bc_out = 0; return; } /* Compute the power of the reconstructed short term residual * signal dp[..] */ dp_float -= Nc; L_power = 0; for (k = 0; k < 40; ++k) { register float f = dp_float[k]; L_power += f * f; } if (L_max >= L_power) { *bc_out = 3; return; } /* Coding of the LTP gain * Table 4.3a must be used to obtain the level DLB[i] for the * quantization of the LTP gain b to get the coded version bc. */ lambda = L_max / L_power * 32768.; for (bc = 0; bc <= 2; ++bc) if (lambda <= gsm_DLB[bc]) break; *bc_out = bc; } #endif /* FAST */ #endif /* USE_FLOAT_MUL */ /* 4.2.12 */ static void Long_term_analysis_filtering P6((bc,Nc,dp,d,dpp,e), word bc, /* IN */ word Nc, /* IN */ register word * dp, /* previous d [-120..-1] IN */ register word * d, /* d [0..39] IN */ register word * dpp, /* estimate [0..39] OUT */ register word * e /* long term res. signal [0..39] OUT */ ) /* * In this part, we have to decode the bc parameter to compute * the samples of the estimate dpp[0..39]. The decoding of bc needs the * use of table 4.3b. The long term residual signal e[0..39] * is then calculated to be fed to the RPE encoding section. */ { register int k; register longword ltmp; # undef STEP # define STEP(BP) \ for (k = 0; k <= 39; k++) { \ dpp[k] = GSM_MULT_R( BP, dp[k - Nc]); \ e[k] = GSM_SUB( d[k], dpp[k] ); \ } switch (bc) { case 0: STEP( 3277 ); break; case 1: STEP( 11469 ); break; case 2: STEP( 21299 ); break; case 3: STEP( 32767 ); break; } } void Gsm_Long_Term_Predictor P7((S,d,dp,e,dpp,Nc,bc), /* 4x for 160 samples */ struct gsm_state * S, word * d, /* [0..39] residual signal IN */ word * dp, /* [-120..-1] d' IN */ word * e, /* [0..39] OUT */ word * dpp, /* [0..39] OUT */ word * Nc, /* correlation lag OUT */ word * bc /* gain factor OUT */ ) { assert( d ); assert( dp ); assert( e ); assert( dpp); assert( Nc ); assert( bc ); #if defined(FAST) && defined(USE_FLOAT_MUL) if (S->fast) #if defined (LTP_CUT) if (S->ltp_cut) Cut_Fast_Calculation_of_the_LTP_parameters(S, d, dp, bc, Nc); else #endif /* LTP_CUT */ Fast_Calculation_of_the_LTP_parameters(d, dp, bc, Nc ); else #endif /* FAST & USE_FLOAT_MUL */ #ifdef LTP_CUT if (S->ltp_cut) Cut_Calculation_of_the_LTP_parameters(S, d, dp, bc, Nc); else #endif Calculation_of_the_LTP_parameters(d, dp, bc, Nc); Long_term_analysis_filtering( *bc, *Nc, dp, d, dpp, e ); } /* 4.3.2 */ void Gsm_Long_Term_Synthesis_Filtering P5((S,Ncr,bcr,erp,drp), struct gsm_state * S, word Ncr, word bcr, register word * erp, /* [0..39] IN */ register word * drp /* [-120..-1] IN, [-120..40] OUT */ ) /* * This procedure uses the bcr and Ncr parameter to realize the * long term synthesis filtering. The decoding of bcr needs * table 4.3b. */ { register longword ltmp; /* for ADD */ register int k; word brp, drpp, Nr; /* Check the limits of Nr. */ Nr = Ncr < 40 || Ncr > 120 ? S->nrp : Ncr; S->nrp = Nr; assert(Nr >= 40 && Nr <= 120); /* Decoding of the LTP gain bcr */ brp = gsm_QLB[ bcr ]; /* Computation of the reconstructed short term residual * signal drp[0..39] */ assert(brp != MIN_WORD); for (k = 0; k <= 39; k++) { drpp = GSM_MULT_R( brp, drp[ k - Nr ] ); drp[k] = GSM_ADD( erp[k], drpp ); } /* * Update of the reconstructed short term residual signal * drp[ -1..-120 ] */ for (k = 0; k <= 119; k++) drp[ -120 + k ] = drp[ -80 + k ]; } ================================================ FILE: deps/pjsip/third_party/gsm/src/lpc.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/lpc.c,v 1.5 1994/12/30 23:14:54 jutta Exp $ */ #include "config.h" #include #include #include "private.h" #include "gsm.h" #include "proto.h" #undef P /* * 4.2.4 .. 4.2.7 LPC ANALYSIS SECTION */ /* 4.2.4 */ static void Autocorrelation P2((s, L_ACF), word * s, /* [0..159] IN/OUT */ longword * L_ACF) /* [0..8] OUT */ /* * The goal is to compute the array L_ACF[k]. The signal s[i] must * be scaled in order to avoid an overflow situation. */ { register int k, i; word temp, smax, scalauto; #ifdef USE_FLOAT_MUL float float_s[160]; #endif /* Dynamic scaling of the array s[0..159] */ /* Search for the maximum. */ smax = 0; for (k = 0; k <= 159; k++) { temp = GSM_ABS( s[k] ); if (temp > smax) smax = temp; } /* Computation of the scaling factor. */ if (smax == 0) scalauto = 0; else { assert(smax > 0); scalauto = 4 - gsm_norm( (longword)smax << 16 );/* sub(4,..) */ } /* Scaling of the array s[0...159] */ if (scalauto > 0) { # ifdef USE_FLOAT_MUL # define SCALE(n) \ case n: for (k = 0; k <= 159; k++) \ float_s[k] = (float) \ (s[k] = GSM_MULT_R(s[k], 16384 >> (n-1)));\ break; # else # define SCALE(n) \ case n: for (k = 0; k <= 159; k++) \ s[k] = GSM_MULT_R( s[k], 16384 >> (n-1) );\ break; # endif /* USE_FLOAT_MUL */ switch (scalauto) { SCALE(1) SCALE(2) SCALE(3) SCALE(4) } # undef SCALE } # ifdef USE_FLOAT_MUL else for (k = 0; k <= 159; k++) float_s[k] = (float) s[k]; # endif /* Compute the L_ACF[..]. */ { # ifdef USE_FLOAT_MUL register float * sp = float_s; register float sl = *sp; # define STEP(k) L_ACF[k] += (longword)(sl * sp[ -(k) ]); # else word * sp = s; word sl = *sp; # define STEP(k) L_ACF[k] += ((longword)sl * sp[ -(k) ]); # endif # define NEXTI sl = *++sp for (k = 9; k--; L_ACF[k] = 0) ; STEP (0); NEXTI; STEP(0); STEP(1); NEXTI; STEP(0); STEP(1); STEP(2); NEXTI; STEP(0); STEP(1); STEP(2); STEP(3); NEXTI; STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); NEXTI; STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); NEXTI; STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); STEP(6); NEXTI; STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); STEP(6); STEP(7); for (i = 8; i <= 159; i++) { NEXTI; STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); STEP(6); STEP(7); STEP(8); } for (k = 9; k--; L_ACF[k] <<= 1) ; } /* Rescaling of the array s[0..159] */ if (scalauto > 0) { assert(scalauto <= 4); for (k = 160; k--; *s++ <<= scalauto) ; } } #if defined(USE_FLOAT_MUL) && defined(FAST) static void Fast_Autocorrelation P2((s, L_ACF), word * s, /* [0..159] IN/OUT */ longword * L_ACF) /* [0..8] OUT */ { register int k, i; float f_L_ACF[9]; float scale; float s_f[160]; register float *sf = s_f; for (i = 0; i < 160; ++i) sf[i] = s[i]; for (k = 0; k <= 8; k++) { register float L_temp2 = 0; register float *sfl = sf - k; for (i = k; i < 160; ++i) L_temp2 += sf[i] * sfl[i]; f_L_ACF[k] = L_temp2; } scale = MAX_LONGWORD / f_L_ACF[0]; for (k = 0; k <= 8; k++) { L_ACF[k] = f_L_ACF[k] * scale; } } #endif /* defined (USE_FLOAT_MUL) && defined (FAST) */ /* 4.2.5 */ static void Reflection_coefficients P2( (L_ACF, r), longword * L_ACF, /* 0...8 IN */ register word * r /* 0...7 OUT */ ) { register int i, m, n; register word temp; register longword ltmp; word ACF[9]; /* 0..8 */ word P[ 9]; /* 0..8 */ word K[ 9]; /* 2..8 */ /* Schur recursion with 16 bits arithmetic. */ if (L_ACF[0] == 0) { for (i = 8; i--; *r++ = 0) ; return; } assert( L_ACF[0] != 0 ); temp = gsm_norm( L_ACF[0] ); assert(temp >= 0 && temp < 32); /* ? overflow ? */ for (i = 0; i <= 8; i++) ACF[i] = SASR( L_ACF[i] << temp, 16 ); /* Initialize array P[..] and K[..] for the recursion. */ for (i = 1; i <= 7; i++) K[ i ] = ACF[ i ]; for (i = 0; i <= 8; i++) P[ i ] = ACF[ i ]; /* Compute reflection coefficients */ for (n = 1; n <= 8; n++, r++) { temp = P[1]; temp = GSM_ABS(temp); if (P[0] < temp) { for (i = n; i <= 8; i++) *r++ = 0; return; } *r = gsm_div( temp, P[0] ); assert(*r >= 0); if (P[1] > 0) *r = -*r; /* r[n] = sub(0, r[n]) */ assert (*r != MIN_WORD); if (n == 8) return; /* Schur recursion */ temp = GSM_MULT_R( P[1], *r ); P[0] = GSM_ADD( P[0], temp ); for (m = 1; m <= 8 - n; m++) { temp = GSM_MULT_R( K[ m ], *r ); P[m] = GSM_ADD( P[ m+1 ], temp ); temp = GSM_MULT_R( P[ m+1 ], *r ); K[m] = GSM_ADD( K[ m ], temp ); } } } /* 4.2.6 */ static void Transformation_to_Log_Area_Ratios P1((r), register word * r /* 0..7 IN/OUT */ ) /* * The following scaling for r[..] and LAR[..] has been used: * * r[..] = integer( real_r[..]*32768. ); -1 <= real_r < 1. * LAR[..] = integer( real_LAR[..] * 16384 ); * with -1.625 <= real_LAR <= 1.625 */ { register word temp; register int i; /* Computation of the LAR[0..7] from the r[0..7] */ for (i = 1; i <= 8; i++, r++) { temp = *r; temp = GSM_ABS(temp); assert(temp >= 0); if (temp < 22118) { temp >>= 1; } else if (temp < 31130) { assert( temp >= 11059 ); temp -= 11059; } else { assert( temp >= 26112 ); temp -= 26112; temp <<= 2; } *r = *r < 0 ? -temp : temp; assert( *r != MIN_WORD ); } } /* 4.2.7 */ static void Quantization_and_coding P1((LAR), register word * LAR /* [0..7] IN/OUT */ ) { register word temp; longword ltmp; /* This procedure needs four tables; the following equations * give the optimum scaling for the constants: * * A[0..7] = integer( real_A[0..7] * 1024 ) * B[0..7] = integer( real_B[0..7] * 512 ) * MAC[0..7] = maximum of the LARc[0..7] * MIC[0..7] = minimum of the LARc[0..7] */ # undef STEP # define STEP( A, B, MAC, MIC ) \ temp = GSM_MULT( A, *LAR ); \ temp = GSM_ADD( temp, B ); \ temp = GSM_ADD( temp, 256 ); \ temp = SASR( temp, 9 ); \ *LAR = temp>MAC ? MAC - MIC : (tempfast) Fast_Autocorrelation (s, L_ACF ); else #endif Autocorrelation (s, L_ACF ); Reflection_coefficients (L_ACF, LARc ); Transformation_to_Log_Area_Ratios (LARc); Quantization_and_coding (LARc); } ================================================ FILE: deps/pjsip/third_party/gsm/src/preprocess.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/preprocess.c,v 1.2 1994/05/10 20:18:45 jutta Exp $ */ #include "config.h" #include #include #include "private.h" #include "gsm.h" #include "proto.h" /* 4.2.0 .. 4.2.3 PREPROCESSING SECTION * * After A-law to linear conversion (or directly from the * Ato D converter) the following scaling is assumed for * input to the RPE-LTP algorithm: * * in: 0.1.....................12 * S.v.v.v.v.v.v.v.v.v.v.v.v.*.*.* * * Where S is the sign bit, v a valid bit, and * a "don't care" bit. * The original signal is called sop[..] * * out: 0.1................... 12 * S.S.v.v.v.v.v.v.v.v.v.v.v.v.0.0 */ void Gsm_Preprocess P3((S, s, so), struct gsm_state * S, word * s, word * so ) /* [0..159] IN/OUT */ { word z1 = S->z1; longword L_z2 = S->L_z2; word mp = S->mp; word s1; longword L_s2; longword L_temp; word msp, lsp; word SO; longword ltmp; /* for ADD */ ulongword utmp; /* for L_ADD */ register int k = 160; while (k--) { /* 4.2.1 Downscaling of the input signal */ SO = SASR( *s, 3 ) << 2; s++; assert (SO >= -0x4000); /* downscaled by */ assert (SO <= 0x3FFC); /* previous routine. */ /* 4.2.2 Offset compensation * * This part implements a high-pass filter and requires extended * arithmetic precision for the recursive part of this filter. * The input of this procedure is the array so[0...159] and the * output the array sof[ 0...159 ]. */ /* Compute the non-recursive part */ s1 = SO - z1; /* s1 = gsm_sub( *so, z1 ); */ z1 = SO; assert(s1 != MIN_WORD); /* Compute the recursive part */ L_s2 = s1; L_s2 <<= 15; /* Execution of a 31 bv 16 bits multiplication */ msp = SASR( L_z2, 15 ); lsp = L_z2-((longword)msp<<15); /* gsm_L_sub(L_z2,(msp<<15)); */ L_s2 += GSM_MULT_R( lsp, 32735 ); L_temp = (longword)msp * 32735; /* GSM_L_MULT(msp,32735) >> 1;*/ L_z2 = GSM_L_ADD( L_temp, L_s2 ); /* Compute sof[k] with rounding */ L_temp = GSM_L_ADD( L_z2, 16384 ); /* 4.2.3 Preemphasis */ msp = GSM_MULT_R( mp, -28180 ); mp = SASR( L_temp, 15 ); *so++ = GSM_ADD( mp, msp ); } S->z1 = z1; S->L_z2 = L_z2; S->mp = mp; } ================================================ FILE: deps/pjsip/third_party/gsm/src/rpe.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/rpe.c,v 1.3 1994/05/10 20:18:46 jutta Exp $ */ #include "config.h" #include #include #include "private.h" #include "gsm.h" #include "proto.h" /* 4.2.13 .. 4.2.17 RPE ENCODING SECTION */ /* 4.2.13 */ static void Weighting_filter P2((e, x), register word * e, /* signal [-5..0.39.44] IN */ word * x /* signal [0..39] OUT */ ) /* * The coefficients of the weighting filter are stored in a table * (see table 4.4). The following scaling is used: * * H[0..10] = integer( real_H[ 0..10] * 8192 ); */ { /* word wt[ 50 ]; */ register longword L_result; register int k /* , i */ ; /* Initialization of a temporary working array wt[0...49] */ /* for (k = 0; k <= 4; k++) wt[k] = 0; * for (k = 5; k <= 44; k++) wt[k] = *e++; * for (k = 45; k <= 49; k++) wt[k] = 0; * * (e[-5..-1] and e[40..44] are allocated by the caller, * are initially zero and are not written anywhere.) */ e -= 5; /* Compute the signal x[0..39] */ for (k = 0; k <= 39; k++) { L_result = 8192 >> 1; /* for (i = 0; i <= 10; i++) { * L_temp = GSM_L_MULT( wt[k+i], gsm_H[i] ); * L_result = GSM_L_ADD( L_result, L_temp ); * } */ #undef STEP #define STEP( i, H ) (e[ k + i ] * (longword)H) /* Every one of these multiplications is done twice -- * but I don't see an elegant way to optimize this. * Do you? */ #ifdef STUPID_COMPILER L_result += STEP( 0, -134 ) ; L_result += STEP( 1, -374 ) ; /* + STEP( 2, 0 ) */ L_result += STEP( 3, 2054 ) ; L_result += STEP( 4, 5741 ) ; L_result += STEP( 5, 8192 ) ; L_result += STEP( 6, 5741 ) ; L_result += STEP( 7, 2054 ) ; /* + STEP( 8, 0 ) */ L_result += STEP( 9, -374 ) ; L_result += STEP( 10, -134 ) ; #else L_result += STEP( 0, -134 ) + STEP( 1, -374 ) /* + STEP( 2, 0 ) */ + STEP( 3, 2054 ) + STEP( 4, 5741 ) + STEP( 5, 8192 ) + STEP( 6, 5741 ) + STEP( 7, 2054 ) /* + STEP( 8, 0 ) */ + STEP( 9, -374 ) + STEP(10, -134 ) ; #endif /* L_result = GSM_L_ADD( L_result, L_result ); (* scaling(x2) *) * L_result = GSM_L_ADD( L_result, L_result ); (* scaling(x4) *) * * x[k] = SASR( L_result, 16 ); */ /* 2 adds vs. >>16 => 14, minus one shift to compensate for * those we lost when replacing L_MULT by '*'. */ L_result = SASR( L_result, 13 ); x[k] = ( L_result < MIN_WORD ? MIN_WORD : (L_result > MAX_WORD ? MAX_WORD : L_result )); } } /* 4.2.14 */ static void RPE_grid_selection P3((x,xM,Mc_out), word * x, /* [0..39] IN */ word * xM, /* [0..12] OUT */ word * Mc_out /* OUT */ ) /* * The signal x[0..39] is used to select the RPE grid which is * represented by Mc. */ { /* register word temp1; */ register int /* m, */ i; register longword L_result, L_temp; longword EM; /* xxx should be L_EM? */ word Mc; longword L_common_0_3; EM = 0; Mc = 0; /* for (m = 0; m <= 3; m++) { * L_result = 0; * * * for (i = 0; i <= 12; i++) { * * temp1 = SASR( x[m + 3*i], 2 ); * * assert(temp1 != MIN_WORD); * * L_temp = GSM_L_MULT( temp1, temp1 ); * L_result = GSM_L_ADD( L_temp, L_result ); * } * * if (L_result > EM) { * Mc = m; * EM = L_result; * } * } */ #undef STEP #define STEP( m, i ) L_temp = SASR( x[m + 3 * i], 2 ); \ L_result += L_temp * L_temp; /* common part of 0 and 3 */ L_result = 0; STEP( 0, 1 ); STEP( 0, 2 ); STEP( 0, 3 ); STEP( 0, 4 ); STEP( 0, 5 ); STEP( 0, 6 ); STEP( 0, 7 ); STEP( 0, 8 ); STEP( 0, 9 ); STEP( 0, 10); STEP( 0, 11); STEP( 0, 12); L_common_0_3 = L_result; /* i = 0 */ STEP( 0, 0 ); L_result <<= 1; /* implicit in L_MULT */ EM = L_result; /* i = 1 */ L_result = 0; STEP( 1, 0 ); STEP( 1, 1 ); STEP( 1, 2 ); STEP( 1, 3 ); STEP( 1, 4 ); STEP( 1, 5 ); STEP( 1, 6 ); STEP( 1, 7 ); STEP( 1, 8 ); STEP( 1, 9 ); STEP( 1, 10); STEP( 1, 11); STEP( 1, 12); L_result <<= 1; if (L_result > EM) { Mc = 1; EM = L_result; } /* i = 2 */ L_result = 0; STEP( 2, 0 ); STEP( 2, 1 ); STEP( 2, 2 ); STEP( 2, 3 ); STEP( 2, 4 ); STEP( 2, 5 ); STEP( 2, 6 ); STEP( 2, 7 ); STEP( 2, 8 ); STEP( 2, 9 ); STEP( 2, 10); STEP( 2, 11); STEP( 2, 12); L_result <<= 1; if (L_result > EM) { Mc = 2; EM = L_result; } /* i = 3 */ L_result = L_common_0_3; STEP( 3, 12 ); L_result <<= 1; if (L_result > EM) { Mc = 3; EM = L_result; } /**/ /* Down-sampling by a factor 3 to get the selected xM[0..12] * RPE sequence. */ for (i = 0; i <= 12; i ++) xM[i] = x[Mc + 3*i]; *Mc_out = Mc; } /* 4.12.15 */ static void APCM_quantization_xmaxc_to_exp_mant P3((xmaxc,exp_out,mant_out), word xmaxc, /* IN */ word * exp_out, /* OUT */ word * mant_out ) /* OUT */ { word exp, mant; /* Compute exponent and mantissa of the decoded version of xmaxc */ exp = 0; if (xmaxc > 15) exp = SASR(xmaxc, 3) - 1; mant = xmaxc - (exp << 3); if (mant == 0) { exp = -4; mant = 7; } else { while (mant <= 7) { mant = mant << 1 | 1; exp--; } mant -= 8; } assert( exp >= -4 && exp <= 6 ); assert( mant >= 0 && mant <= 7 ); *exp_out = exp; *mant_out = mant; } static void APCM_quantization P5((xM,xMc,mant_out,exp_out,xmaxc_out), word * xM, /* [0..12] IN */ word * xMc, /* [0..12] OUT */ word * mant_out, /* OUT */ word * exp_out, /* OUT */ word * xmaxc_out /* OUT */ ) { int i, itest; word xmax, xmaxc, temp, temp1, temp2; word exp, mant; /* Find the maximum absolute value xmax of xM[0..12]. */ xmax = 0; for (i = 0; i <= 12; i++) { temp = xM[i]; temp = GSM_ABS(temp); if (temp > xmax) xmax = temp; } /* Qantizing and coding of xmax to get xmaxc. */ exp = 0; temp = SASR( xmax, 9 ); itest = 0; for (i = 0; i <= 5; i++) { itest |= (temp <= 0); temp = SASR( temp, 1 ); assert(exp <= 5); if (itest == 0) exp++; /* exp = add (exp, 1) */ } assert(exp <= 6 && exp >= 0); temp = exp + 5; assert(temp <= 11 && temp >= 0); xmaxc = gsm_add( SASR(xmax, temp), exp << 3 ); /* Quantizing and coding of the xM[0..12] RPE sequence * to get the xMc[0..12] */ APCM_quantization_xmaxc_to_exp_mant( xmaxc, &exp, &mant ); /* This computation uses the fact that the decoded version of xmaxc * can be calculated by using the exponent and the mantissa part of * xmaxc (logarithmic table). * So, this method avoids any division and uses only a scaling * of the RPE samples by a function of the exponent. A direct * multiplication by the inverse of the mantissa (NRFAC[0..7] * found in table 4.5) gives the 3 bit coded version xMc[0..12] * of the RPE samples. */ /* Direct computation of xMc[0..12] using table 4.5 */ assert( exp <= 4096 && exp >= -4096); assert( mant >= 0 && mant <= 7 ); temp1 = 6 - exp; /* normalization by the exponent */ temp2 = gsm_NRFAC[ mant ]; /* inverse mantissa */ for (i = 0; i <= 12; i++) { assert(temp1 >= 0 && temp1 < 16); temp = xM[i] << temp1; temp = GSM_MULT( temp, temp2 ); temp = SASR(temp, 12); xMc[i] = temp + 4; /* see note below */ } /* NOTE: This equation is used to make all the xMc[i] positive. */ *mant_out = mant; *exp_out = exp; *xmaxc_out = xmaxc; } /* 4.2.16 */ static void APCM_inverse_quantization P4((xMc,mant,exp,xMp), register word * xMc, /* [0..12] IN */ word mant, word exp, register word * xMp) /* [0..12] OUT */ /* * This part is for decoding the RPE sequence of coded xMc[0..12] * samples to obtain the xMp[0..12] array. Table 4.6 is used to get * the mantissa of xmaxc (FAC[0..7]). */ { int i; word temp, temp1, temp2, temp3; longword ltmp; assert( mant >= 0 && mant <= 7 ); temp1 = gsm_FAC[ mant ]; /* see 4.2-15 for mant */ temp2 = gsm_sub( 6, exp ); /* see 4.2-15 for exp */ temp3 = gsm_asl( 1, gsm_sub( temp2, 1 )); for (i = 13; i--;) { assert( *xMc <= 7 && *xMc >= 0 ); /* 3 bit unsigned */ /* temp = gsm_sub( *xMc++ << 1, 7 ); */ temp = (*xMc++ << 1) - 7; /* restore sign */ assert( temp <= 7 && temp >= -7 ); /* 4 bit signed */ temp <<= 12; /* 16 bit signed */ temp = GSM_MULT_R( temp1, temp ); temp = GSM_ADD( temp, temp3 ); *xMp++ = gsm_asr( temp, temp2 ); } } /* 4.2.17 */ static void RPE_grid_positioning P3((Mc,xMp,ep), word Mc, /* grid position IN */ register word * xMp, /* [0..12] IN */ register word * ep /* [0..39] OUT */ ) /* * This procedure computes the reconstructed long term residual signal * ep[0..39] for the LTP analysis filter. The inputs are the Mc * which is the grid position selection and the xMp[0..12] decoded * RPE samples which are upsampled by a factor of 3 by inserting zero * values. */ { int i = 13; assert(0 <= Mc && Mc <= 3); switch (Mc) { case 3: *ep++ = 0; case 2: do { *ep++ = 0; case 1: *ep++ = 0; case 0: *ep++ = *xMp++; } while (--i); } while (++Mc < 4) *ep++ = 0; /* int i, k; for (k = 0; k <= 39; k++) ep[k] = 0; for (i = 0; i <= 12; i++) { ep[ Mc + (3*i) ] = xMp[i]; } */ } /* 4.2.18 */ /* This procedure adds the reconstructed long term residual signal * ep[0..39] to the estimated signal dpp[0..39] from the long term * analysis filter to compute the reconstructed short term residual * signal dp[-40..-1]; also the reconstructed short term residual * array dp[-120..-41] is updated. */ #if 0 /* Has been inlined in code.c */ void Gsm_Update_of_reconstructed_short_time_residual_signal P3((dpp, ep, dp), word * dpp, /* [0...39] IN */ word * ep, /* [0...39] IN */ word * dp) /* [-120...-1] IN/OUT */ { int k; for (k = 0; k <= 79; k++) dp[ -120 + k ] = dp[ -80 + k ]; for (k = 0; k <= 39; k++) dp[ -40 + k ] = gsm_add( ep[k], dpp[k] ); } #endif /* Has been inlined in code.c */ void Gsm_RPE_Encoding P5((S,e,xmaxc,Mc,xMc), struct gsm_state * S, word * e, /* -5..-1][0..39][40..44 IN/OUT */ word * xmaxc, /* OUT */ word * Mc, /* OUT */ word * xMc) /* [0..12] OUT */ { word x[40]; word xM[13], xMp[13]; word mant, exp; Weighting_filter(e, x); RPE_grid_selection(x, xM, Mc); APCM_quantization( xM, xMc, &mant, &exp, xmaxc); APCM_inverse_quantization( xMc, mant, exp, xMp); RPE_grid_positioning( *Mc, xMp, e ); } void Gsm_RPE_Decoding P5((S, xmaxcr, Mcr, xMcr, erp), struct gsm_state * S, word xmaxcr, word Mcr, word * xMcr, /* [0..12], 3 bits IN */ word * erp /* [0..39] OUT */ ) { word exp, mant; word xMp[ 13 ]; APCM_quantization_xmaxc_to_exp_mant( xmaxcr, &exp, &mant ); APCM_inverse_quantization( xMcr, mant, exp, xMp ); RPE_grid_positioning( Mcr, xMp, erp ); } ================================================ FILE: deps/pjsip/third_party/gsm/src/short_term.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/short_term.c,v 1.2 1994/05/10 20:18:47 jutta Exp $ */ #include "config.h" #include #include #include "private.h" #include "gsm.h" #include "proto.h" /* * SHORT TERM ANALYSIS FILTERING SECTION */ /* 4.2.8 */ static void Decoding_of_the_coded_Log_Area_Ratios P2((LARc,LARpp), word * LARc, /* coded log area ratio [0..7] IN */ word * LARpp) /* out: decoded .. */ { register word temp1 /* , temp2 */; register long ltmp; /* for GSM_ADD */ /* This procedure requires for efficient implementation * two tables. * * INVA[1..8] = integer( (32768 * 8) / real_A[1..8]) * MIC[1..8] = minimum value of the LARc[1..8] */ /* Compute the LARpp[1..8] */ /* for (i = 1; i <= 8; i++, B++, MIC++, INVA++, LARc++, LARpp++) { * * temp1 = GSM_ADD( *LARc, *MIC ) << 10; * temp2 = *B << 1; * temp1 = GSM_SUB( temp1, temp2 ); * * assert(*INVA != MIN_WORD); * * temp1 = GSM_MULT_R( *INVA, temp1 ); * *LARpp = GSM_ADD( temp1, temp1 ); * } */ #undef STEP #define STEP( B, MIC, INVA ) \ temp1 = GSM_ADD( *LARc++, MIC ) << 10; \ temp1 = GSM_SUB( temp1, B << 1 ); \ temp1 = GSM_MULT_R( INVA, temp1 ); \ *LARpp++ = GSM_ADD( temp1, temp1 ); STEP( 0, -32, 13107 ); STEP( 0, -32, 13107 ); STEP( 2048, -16, 13107 ); STEP( -2560, -16, 13107 ); STEP( 94, -8, 19223 ); STEP( -1792, -8, 17476 ); STEP( -341, -4, 31454 ); STEP( -1144, -4, 29708 ); /* NOTE: the addition of *MIC is used to restore * the sign of *LARc. */ } /* 4.2.9 */ /* Computation of the quantized reflection coefficients */ /* 4.2.9.1 Interpolation of the LARpp[1..8] to get the LARp[1..8] */ /* * Within each frame of 160 analyzed speech samples the short term * analysis and synthesis filters operate with four different sets of * coefficients, derived from the previous set of decoded LARs(LARpp(j-1)) * and the actual set of decoded LARs (LARpp(j)) * * (Initial value: LARpp(j-1)[1..8] = 0.) */ static void Coefficients_0_12 P3((LARpp_j_1, LARpp_j, LARp), register word * LARpp_j_1, register word * LARpp_j, register word * LARp) { register int i; register longword ltmp; for (i = 1; i <= 8; i++, LARp++, LARpp_j_1++, LARpp_j++) { *LARp = GSM_ADD( SASR( *LARpp_j_1, 2 ), SASR( *LARpp_j, 2 )); *LARp = GSM_ADD( *LARp, SASR( *LARpp_j_1, 1)); } } static void Coefficients_13_26 P3((LARpp_j_1, LARpp_j, LARp), register word * LARpp_j_1, register word * LARpp_j, register word * LARp) { register int i; register longword ltmp; for (i = 1; i <= 8; i++, LARpp_j_1++, LARpp_j++, LARp++) { *LARp = GSM_ADD( SASR( *LARpp_j_1, 1), SASR( *LARpp_j, 1 )); } } static void Coefficients_27_39 P3((LARpp_j_1, LARpp_j, LARp), register word * LARpp_j_1, register word * LARpp_j, register word * LARp) { register int i; register longword ltmp; for (i = 1; i <= 8; i++, LARpp_j_1++, LARpp_j++, LARp++) { *LARp = GSM_ADD( SASR( *LARpp_j_1, 2 ), SASR( *LARpp_j, 2 )); *LARp = GSM_ADD( *LARp, SASR( *LARpp_j, 1 )); } } static void Coefficients_40_159 P2((LARpp_j, LARp), register word * LARpp_j, register word * LARp) { register int i; for (i = 1; i <= 8; i++, LARp++, LARpp_j++) *LARp = *LARpp_j; } /* 4.2.9.2 */ static void LARp_to_rp P1((LARp), register word * LARp) /* [0..7] IN/OUT */ /* * The input of this procedure is the interpolated LARp[0..7] array. * The reflection coefficients, rp[i], are used in the analysis * filter and in the synthesis filter. */ { register int i; register word temp; register longword ltmp; for (i = 1; i <= 8; i++, LARp++) { /* temp = GSM_ABS( *LARp ); * * if (temp < 11059) temp <<= 1; * else if (temp < 20070) temp += 11059; * else temp = GSM_ADD( temp >> 2, 26112 ); * * *LARp = *LARp < 0 ? -temp : temp; */ if (*LARp < 0) { temp = *LARp == MIN_WORD ? MAX_WORD : -(*LARp); *LARp = - ((temp < 11059) ? temp << 1 : ((temp < 20070) ? temp + 11059 : GSM_ADD( temp >> 2, 26112 ))); } else { temp = *LARp; *LARp = (temp < 11059) ? temp << 1 : ((temp < 20070) ? temp + 11059 : GSM_ADD( temp >> 2, 26112 )); } } } /* 4.2.10 */ static void Short_term_analysis_filtering P4((S,rp,k_n,s), struct gsm_state * S, register word * rp, /* [0..7] IN */ register int k_n, /* k_end - k_start */ register word * s /* [0..n-1] IN/OUT */ ) /* * This procedure computes the short term residual signal d[..] to be fed * to the RPE-LTP loop from the s[..] signal and from the local rp[..] * array (quantized reflection coefficients). As the call of this * procedure can be done in many ways (see the interpolation of the LAR * coefficient), it is assumed that the computation begins with index * k_start (for arrays d[..] and s[..]) and stops with index k_end * (k_start and k_end are defined in 4.2.9.1). This procedure also * needs to keep the array u[0..7] in memory for each call. */ { register word * u = S->u; register int i; register word di, zzz, ui, sav, rpi; register longword ltmp; for (; k_n--; s++) { di = sav = *s; for (i = 0; i < 8; i++) { /* YYY */ ui = u[i]; rpi = rp[i]; u[i] = sav; zzz = GSM_MULT_R(rpi, di); sav = GSM_ADD( ui, zzz); zzz = GSM_MULT_R(rpi, ui); di = GSM_ADD( di, zzz ); } *s = di; } } #if defined(USE_FLOAT_MUL) && defined(FAST) static void Fast_Short_term_analysis_filtering P4((S,rp,k_n,s), struct gsm_state * S, register word * rp, /* [0..7] IN */ register int k_n, /* k_end - k_start */ register word * s /* [0..n-1] IN/OUT */ ) { register word * u = S->u; register int i; float uf[8], rpf[8]; register float scalef = 3.0517578125e-5; register float sav, di, temp; for (i = 0; i < 8; ++i) { uf[i] = u[i]; rpf[i] = rp[i] * scalef; } for (; k_n--; s++) { sav = di = *s; for (i = 0; i < 8; ++i) { register float rpfi = rpf[i]; register float ufi = uf[i]; uf[i] = sav; temp = rpfi * di + ufi; di += rpfi * ufi; sav = temp; } *s = di; } for (i = 0; i < 8; ++i) u[i] = uf[i]; } #endif /* ! (defined (USE_FLOAT_MUL) && defined (FAST)) */ static void Short_term_synthesis_filtering P5((S,rrp,k,wt,sr), struct gsm_state * S, register word * rrp, /* [0..7] IN */ register int k, /* k_end - k_start */ register word * wt, /* [0..k-1] IN */ register word * sr /* [0..k-1] OUT */ ) { register word * v = S->v; register int i; register word sri, tmp1, tmp2; register longword ltmp; /* for GSM_ADD & GSM_SUB */ while (k--) { sri = *wt++; for (i = 8; i--;) { /* sri = GSM_SUB( sri, gsm_mult_r( rrp[i], v[i] ) ); */ tmp1 = rrp[i]; tmp2 = v[i]; tmp2 = ( tmp1 == MIN_WORD && tmp2 == MIN_WORD ? MAX_WORD : 0x0FFFF & (( (longword)tmp1 * (longword)tmp2 + 16384) >> 15)) ; sri = GSM_SUB( sri, tmp2 ); /* v[i+1] = GSM_ADD( v[i], gsm_mult_r( rrp[i], sri ) ); */ tmp1 = ( tmp1 == MIN_WORD && sri == MIN_WORD ? MAX_WORD : 0x0FFFF & (( (longword)tmp1 * (longword)sri + 16384) >> 15)) ; v[i+1] = GSM_ADD( v[i], tmp1); } *sr++ = v[0] = sri; } } #if defined(FAST) && defined(USE_FLOAT_MUL) static void Fast_Short_term_synthesis_filtering P5((S,rrp,k,wt,sr), struct gsm_state * S, register word * rrp, /* [0..7] IN */ register int k, /* k_end - k_start */ register word * wt, /* [0..k-1] IN */ register word * sr /* [0..k-1] OUT */ ) { register word * v = S->v; register int i; float va[9], rrpa[8]; register float scalef = 3.0517578125e-5, temp; for (i = 0; i < 8; ++i) { va[i] = v[i]; rrpa[i] = (float)rrp[i] * scalef; } while (k--) { register float sri = *wt++; for (i = 8; i--;) { sri -= rrpa[i] * va[i]; if (sri < -32768.) sri = -32768.; else if (sri > 32767.) sri = 32767.; temp = va[i] + rrpa[i] * sri; if (temp < -32768.) temp = -32768.; else if (temp > 32767.) temp = 32767.; va[i+1] = temp; } *sr++ = va[0] = sri; } for (i = 0; i < 9; ++i) v[i] = va[i]; } #endif /* defined(FAST) && defined(USE_FLOAT_MUL) */ void Gsm_Short_Term_Analysis_Filter P3((S,LARc,s), struct gsm_state * S, word * LARc, /* coded log area ratio [0..7] IN */ word * s /* signal [0..159] IN/OUT */ ) { word * LARpp_j = S->LARpp[ S->j ]; word * LARpp_j_1 = S->LARpp[ S->j ^= 1 ]; word LARp[8]; #undef FILTER #if defined(FAST) && defined(USE_FLOAT_MUL) # define FILTER (* (S->fast \ ? Fast_Short_term_analysis_filtering \ : Short_term_analysis_filtering )) #else # define FILTER Short_term_analysis_filtering #endif Decoding_of_the_coded_Log_Area_Ratios( LARc, LARpp_j ); Coefficients_0_12( LARpp_j_1, LARpp_j, LARp ); LARp_to_rp( LARp ); FILTER( S, LARp, 13, s); Coefficients_13_26( LARpp_j_1, LARpp_j, LARp); LARp_to_rp( LARp ); FILTER( S, LARp, 14, s + 13); Coefficients_27_39( LARpp_j_1, LARpp_j, LARp); LARp_to_rp( LARp ); FILTER( S, LARp, 13, s + 27); Coefficients_40_159( LARpp_j, LARp); LARp_to_rp( LARp ); FILTER( S, LARp, 120, s + 40); } void Gsm_Short_Term_Synthesis_Filter P4((S, LARcr, wt, s), struct gsm_state * S, word * LARcr, /* received log area ratios [0..7] IN */ word * wt, /* received d [0..159] IN */ word * s /* signal s [0..159] OUT */ ) { word * LARpp_j = S->LARpp[ S->j ]; word * LARpp_j_1 = S->LARpp[ S->j ^=1 ]; word LARp[8]; #undef FILTER #if defined(FAST) && defined(USE_FLOAT_MUL) # define FILTER (* (S->fast \ ? Fast_Short_term_synthesis_filtering \ : Short_term_synthesis_filtering )) #else # define FILTER Short_term_synthesis_filtering #endif Decoding_of_the_coded_Log_Area_Ratios( LARcr, LARpp_j ); Coefficients_0_12( LARpp_j_1, LARpp_j, LARp ); LARp_to_rp( LARp ); FILTER( S, LARp, 13, wt, s ); Coefficients_13_26( LARpp_j_1, LARpp_j, LARp); LARp_to_rp( LARp ); FILTER( S, LARp, 14, wt + 13, s + 13 ); Coefficients_27_39( LARpp_j_1, LARpp_j, LARp); LARp_to_rp( LARp ); FILTER( S, LARp, 13, wt + 27, s + 27 ); Coefficients_40_159( LARpp_j, LARp ); LARp_to_rp( LARp ); FILTER(S, LARp, 120, wt + 40, s + 40); } ================================================ FILE: deps/pjsip/third_party/gsm/src/table.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/table.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */ /* Most of these tables are inlined at their point of use. */ /* 4.4 TABLES USED IN THE FIXED POINT IMPLEMENTATION OF THE RPE-LTP * CODER AND DECODER * * (Most of them inlined, so watch out.) */ #define GSM_TABLE_C #include "private.h" #include "gsm.h" /* Table 4.1 Quantization of the Log.-Area Ratios */ /* i 1 2 3 4 5 6 7 8 */ word gsm_A[8] = {20480, 20480, 20480, 20480, 13964, 15360, 8534, 9036}; word gsm_B[8] = { 0, 0, 2048, -2560, 94, -1792, -341, -1144}; word gsm_MIC[8] = { -32, -32, -16, -16, -8, -8, -4, -4 }; word gsm_MAC[8] = { 31, 31, 15, 15, 7, 7, 3, 3 }; /* Table 4.2 Tabulation of 1/A[1..8] */ word gsm_INVA[8]={ 13107, 13107, 13107, 13107, 19223, 17476, 31454, 29708 }; /* Table 4.3a Decision level of the LTP gain quantizer */ /* bc 0 1 2 3 */ word gsm_DLB[4] = { 6554, 16384, 26214, 32767 }; /* Table 4.3b Quantization levels of the LTP gain quantizer */ /* bc 0 1 2 3 */ word gsm_QLB[4] = { 3277, 11469, 21299, 32767 }; /* Table 4.4 Coefficients of the weighting filter */ /* i 0 1 2 3 4 5 6 7 8 9 10 */ word gsm_H[11] = {-134, -374, 0, 2054, 5741, 8192, 5741, 2054, 0, -374, -134 }; /* Table 4.5 Normalized inverse mantissa used to compute xM/xmax */ /* i 0 1 2 3 4 5 6 7 */ word gsm_NRFAC[8] = { 29128, 26215, 23832, 21846, 20165, 18725, 17476, 16384 }; /* Table 4.6 Normalized direct mantissa used to compute xM/xmax */ /* i 0 1 2 3 4 5 6 7 */ word gsm_FAC[8] = { 18431, 20479, 22527, 24575, 26623, 28671, 30719, 32767 }; ================================================ FILE: deps/pjsip/third_party/gsm/src/toast.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/toast.c,v 1.8 1996/07/02 10:41:04 jutta Exp $ */ #include "toast.h" /* toast -- lossy sound compression using the gsm library. */ char * progname; int f_decode = 0; /* decode rather than encode (-d) */ int f_cat = 0; /* write to stdout; implies -p (-c) */ int f_force = 0; /* don't ask about replacements (-f) */ int f_precious = 0; /* avoid deletion of original (-p) */ int f_fast = 0; /* use faster fpt algorithm (-F) */ int f_verbose = 0; /* debugging (-V) */ int f_ltp_cut = 0; /* LTP cut-off margin (-C) */ struct stat instat; /* stat (inname) */ FILE *in, *out; char *inname, *outname; /* * The function (*output)() writes a frame of 160 samples given as * 160 signed 16 bit values (gsm_signals) to . * The function (*input)() reads one such frame from . * The function (*init_output)() begins output (e.g. writes a header)., * The function (*init_input)() begins input (e.g. skips a header). * * There are different versions of input, output, init_input and init_output * for different formats understood by toast; which ones are used * depends on the command line arguments and, in their absence, the * filename; the fallback is #defined in toast.h * * The specific implementations of input, output, init_input and init_output * for a format `foo' live in toast_foo.c. */ int (*output ) P((gsm_signal *)), (*input ) P((gsm_signal *)); int (*init_input) P((void)), (*init_output) P((void)); static int generic_init P0() { return 0; } /* NOP */ struct fmtdesc { char * name, * longname, * suffix; int (* init_input ) P((void)), (* init_output) P((void)); int (* input ) P((gsm_signal * )), (* output) P((gsm_signal * )); } f_audio = { "audio", "8 kHz, 8 bit u-law encoding with Sun audio header", ".au", audio_init_input, audio_init_output, ulaw_input, ulaw_output }, f_ulaw = { "u-law", "plain 8 kHz, 8 bit u-law encoding", ".u", generic_init, generic_init, ulaw_input, ulaw_output }, f_alaw = { "A-law", "8 kHz, 8 bit A-law encoding", ".A", generic_init, generic_init, alaw_input, alaw_output }, f_linear = { "linear", "16 bit (13 significant) signed 8 kHz signal", ".l", generic_init, generic_init, linear_input, linear_output }; struct fmtdesc * alldescs[] = { &f_audio, &f_alaw, &f_ulaw, &f_linear, (struct fmtdesc *)NULL }; #define DEFAULT_FORMAT f_ulaw /* default audio format, others */ /* are: f_alaw,f_audio,f_linear */ struct fmtdesc * f_format = 0; /* * basename + suffix of a pathname */ static char * endname P1((name), char * name) { if (name) { char * s = strrchr(name, '/'); if (s && s[1]) name = s + 1; } return name; } /* * Try to figure out what we're supposed to do from the argv[0], if * any, and set the parameters accordingly. */ static void parse_argv0 P1((av0), char * av0 ) { int l; progname = av0 = endname(av0 ? av0 : "toast"); /* If the name starts with `un', we want to decode, not code. * If the name ends in `cat', we want to write to stdout, * and decode as well. */ if (!strncmp(av0, "un", 2)) f_decode = 1; if ( (l = strlen(av0)) >= 3 /* strlen("cat") */ && !strcmp( av0 + l - 3, "cat" )) f_cat = f_decode = 1; } /* * Check whether the name (possibly generated by appending * .gsm to something else) is short enough for this system. */ static int length_okay P1((name), char * name) { long max_filename_length = 0; char * end; /* If our _pathname_ is too long, we'll usually not be * able to open the file at all -- don't worry about that. * * But if the _filename_ is too long, there is danger of * silent truncation on some systems, which results * in the target replacing the source! */ if (!name) return 0; end = endname(name); #ifdef NAME_MAX max_filename_length = NAME_MAX; #else #ifdef _PC_NAME_MAX #ifdef USE_PATHCONF { char * s, tmp; /* s = dirname(name) */ if ((s = end) > name) { if (s > name + 1) s--; tmp = s; *s = 0; } errno = 0; max_filename_length = pathconf(s > name ? name : ".", _PC_NAME_MAX); if (max_filename_length == -1 && errno) { perror( s > name ? name : "." ); fprintf(stderr, "%s: cannot get dynamic filename length limit for %s.\n", progname, s > name ? name : "."); return 0; } if (s > name) *s = tmp; } #endif /* USE_PATHCONF */ #endif /* _PC_NAME_MAX */ #endif /* !NAME_MAX */ if (max_filename_length > 0 && strlen(end) > max_filename_length) { fprintf(stderr, "%s: filename \"%s\" is too long (maximum is %ld)\n", progname, endname(name), max_filename_length ); return 0; } return 1; } /* * Return a pointer the suffix of a string, if any. * A suffix alone has no suffix, an empty suffix can not be had. */ static char * suffix P2((name, suf), char *name, char * suf) { size_t nlen = strlen(name); size_t slen = strlen(suf); if (!slen || nlen <= slen) return (char *)0; name += nlen - slen; return memcmp(name, suf, slen) ? (char *)0 : name; } static void catch_signals P1((fun), SIGHANDLER_T (*fun) ()) { #ifdef SIGHUP signal( SIGHUP, fun ); #endif #ifdef SIGINT signal( SIGINT, fun ); #endif #ifdef SIGPIPE signal( SIGPIPE, fun ); #endif #ifdef SIGTERM signal( SIGTERM, fun ); #endif #ifdef SIGXFSZ signal( SIGXFSZ, fun ); #endif } static SIGHANDLER_T onintr P0() { char * tmp = outname; #ifdef HAS_SYSV_SIGNALS catch_signals( SIG_IGN ); #endif outname = (char *)0; if (tmp) (void)unlink(tmp); exit(1); } /* * Allocate some memory and complain if it fails. */ static char * emalloc P1((len), size_t len) { char * s; if (!(s = malloc(len))) { fprintf(stderr, "%s: failed to malloc %d bytes -- abort\n", progname, len); onintr(); exit(1); } return s; } static char* normalname P3((name, want, cut), char *name, char *want,char *cut) { size_t maxlen; char * s, * p; p = (char *)0; if (!name) return p; maxlen = strlen(name) + 1 + strlen(want) + strlen(cut); p = strcpy(emalloc(maxlen), name); if (s = suffix(p, cut)) strcpy(s, want); else if (*want && !suffix(p, want)) strcat(p, want); return p; } /* * Generate a `plain' (non-encoded) name from a given name. */ static char * plainname P1((name), char *name) { return normalname(name, "", SUFFIX_TOASTED ); } /* * Generate a `code' name from a given name. */ static char * codename P1((name), char *name) { return normalname( name, SUFFIX_TOASTED, "" ); } /* * If we're supposed to ask (fileno (stderr) is a tty, and f_force not * set), ask the user whether to overwrite a file or not. */ static int ok_to_replace P1(( name ), char * name) { int reply, c; if (f_force) return 1; /* YES, do replace */ if (!isatty(fileno(stderr))) return 0; /* NO, don't replace */ fprintf(stderr, "%s already exists; do you wish to overwrite %s (y or n)? ", name, name); fflush(stderr); for (c = reply = getchar(); c != '\n' && c != EOF; c = getchar()) ; if (reply == 'y') return 1; fprintf(stderr, "\tnot overwritten\n"); return 0; } static void update_mode P0() { if (!instat.st_nlink) return; /* couldn't stat in */ #ifdef HAS_FCHMOD if (fchmod(fileno(out), instat.st_mode & 07777)) { perror(outname); fprintf(stderr, "%s: could not change file mode of \"%s\"\n", progname, outname); } #else #ifdef HAS_CHMOD if (outname && chmod(outname, instat.st_mode & 07777)) { perror(outname); fprintf(stderr, "%s: could not change file mode of \"%s\"\n", progname, outname); } #endif /* HAS_CHMOD */ #endif /* HAS_FCHMOD */ } static void update_own P0() { if (!instat.st_nlink) return; /* couldn't stat in */ #ifdef HAS_FCHOWN (void)fchown(fileno(out), instat.st_uid, instat.st_gid); #else #ifdef HAS_CHOWN (void)chown(outname, instat.st_uid, instat.st_gid); #endif /* HAS_CHOWN */ #endif /* HAS_FCHOWN */ } static void update_times P0() { if (!instat.st_nlink) return; /* couldn't stat in */ #ifdef HAS_UTIMES if (outname) { struct timeval tv[2]; tv[0].tv_sec = instat.st_atime; tv[1].tv_sec = instat.st_mtime; tv[0].tv_usec = tv[1].tv_usec = 0; (void) utimes(outname, tv); } #else #ifdef HAS_UTIME if (outname) { #ifdef HAS_UTIMBUF struct utimbuf ut; ut.actime = instat.st_atime; ut.modtime = instat.st_mtime; # ifdef HAS_UTIMEUSEC ut.acusec = instat.st_ausec; ut.modusec = instat.st_musec; # endif /* HAS_UTIMEUSEC */ (void) utime(outname, &ut); #else /* UTIMBUF */ time_t ut[2]; ut[0] = instat.st_atime; ut[1] = instat.st_mtime; (void) utime(outname, ut); #endif /* UTIMBUF */ } #endif /* HAS_UTIME */ #endif /* HAS_UTIMES */ } static int okay_as_input P3((name,f,st), char* name, FILE* f, struct stat * st) { # ifdef HAS_FSTAT if (fstat(fileno(f), st) < 0) # else if (stat(name, st) < 0) # endif { perror(name); fprintf(stderr, "%s: cannot stat \"%s\"\n", progname, name); return 0; } if (!S_ISREG(st->st_mode)) { fprintf(stderr, "%s: \"%s\" is not a regular file -- unchanged.\n", progname, name); return 0; } if (st->st_nlink > 1 && !f_cat && !f_precious) { fprintf(stderr, "%s: \"%s\" has %s other link%s -- unchanged.\n", progname,name,st->st_nlink - 1,"s" + (st->st_nlink<=2)); return 0; } return 1; } static void prepare_io P1(( desc), struct fmtdesc * desc) { output = desc->output; input = desc->input; init_input = desc->init_input; init_output = desc->init_output; } static struct fmtdesc * grok_format P1((name), char * name) { char * c; struct fmtdesc ** f; if (name) { c = plainname(name); for (f = alldescs; *f; f++) { if ( (*f)->suffix && *(*f)->suffix && suffix(c, (*f)->suffix)) { free(c); return *f; } } free(c); } return (struct fmtdesc *)0; } static int open_input P2((name, st), char * name, struct stat * st) { struct fmtdesc * f = f_format; st->st_nlink = 0; /* indicates `undefined' value */ if (!name) { inname = (char *)NULL; in = stdin; #ifdef HAS__FSETMODE _fsetmode(in, "b"); #endif } else { if (f_decode) inname = codename(name); else { if (!f_cat && suffix(name, SUFFIX_TOASTED)) { fprintf(stderr, "%s: %s already has \"%s\" suffix -- unchanged.\n", progname, name, SUFFIX_TOASTED ); return 0; } inname = strcpy(emalloc(strlen(name)+1), name); } if (!(in = fopen(inname, READ))) { perror(inname); /* not guaranteed to be valid here */ fprintf(stderr, "%s: cannot open \"%s\" for reading\n", progname, inname); return 0; } if (!okay_as_input(inname, in, st)) return 0; if (!f) f = grok_format(inname); } prepare_io( f ? f : & DEFAULT_FORMAT ); return 1; } static int open_output P1((name), char *name) { if (!name || f_cat) { out = stdout; outname = (char *)NULL; #ifdef HAS__FSETMODE _fsetmode(out, "b"); #endif } else { int outfd = -1; char * o; o = (*(f_decode ? plainname : codename))(name); if (!length_okay(o)) return 0; if ((outfd = open(o, O_WRITE_EXCL, 0666)) >= 0) out = fdopen(outfd, WRITE); else if (errno != EEXIST) out = (FILE *)NULL; else if (ok_to_replace(o)) out = fopen(o, WRITE); else return 0; if (!out) { perror(o); fprintf(stderr, "%s: can't open \"%s\" for writing\n", progname, o); if (outfd >= 0) (void)close(outfd); return 0; } outname = o; } return 1; } static int process_encode P0() { gsm r; gsm_signal s[ 160 ]; gsm_frame d; int cc; if (!(r = gsm_create())) { perror(progname); return -1; } (void)gsm_option(r, GSM_OPT_FAST, &f_fast); (void)gsm_option(r, GSM_OPT_VERBOSE, &f_verbose); (void)gsm_option(r, GSM_OPT_LTP_CUT, &f_ltp_cut); while ((cc = (*input)(s)) > 0) { if (cc < sizeof(s) / sizeof(*s)) memset((char *)(s+cc), 0, sizeof(s)-(cc * sizeof(*s))); gsm_encode(r, s, d); if (fwrite((char *)d, sizeof(d), 1, out) != 1) { perror(outname ? outname : "stdout"); fprintf(stderr, "%s: error writing to %s\n", progname, outname ? outname : "stdout"); gsm_destroy(r); return -1; } } if (cc < 0) { perror(inname ? inname : "stdin"); fprintf(stderr, "%s: error reading from %s\n", progname, inname ? inname : "stdin"); gsm_destroy(r); return -1; } gsm_destroy(r); return 0; } static int process_decode P0() { gsm r; gsm_frame s; gsm_signal d[ 160 ]; int cc; if (!(r = gsm_create())) { /* malloc failed */ perror(progname); return -1; } (void)gsm_option(r, GSM_OPT_FAST, &f_fast); (void)gsm_option(r, GSM_OPT_VERBOSE, &f_verbose); while ((cc = fread(s, 1, sizeof(s), in)) > 0) { if (cc != sizeof(s)) { if (cc >= 0) fprintf(stderr, "%s: incomplete frame (%d byte%s missing) from %s\n", progname, sizeof(s) - cc, "s" + (sizeof(s) - cc == 1), inname ? inname : "stdin" ); gsm_destroy(r); errno = 0; return -1; } if (gsm_decode(r, s, d)) { fprintf(stderr, "%s: bad frame in %s\n", progname, inname ? inname : "stdin"); gsm_destroy(r); errno = 0; return -1; } if ((*output)(d) < 0) { perror(outname); fprintf(stderr, "%s: error writing to %s\n", progname, outname); gsm_destroy(r); return -1; } } if (cc < 0) { perror(inname ? inname : "stdin" ); fprintf(stderr, "%s: error reading from %s\n", progname, inname ? inname : "stdin"); gsm_destroy(r); return -1; } gsm_destroy(r); return 0; } static int process P1((name), char * name) { int step = 0; out = (FILE *)0; in = (FILE *)0; outname = (char *)0; inname = (char *)0; if (!open_input(name, &instat) || !open_output(name)) goto err; if ((*(f_decode ? init_output : init_input))()) { fprintf(stderr, "%s: error %s %s\n", progname, f_decode ? "writing header to" : "reading header from", f_decode ? (outname ? outname : "stdout") : (inname ? inname : "stdin")); goto err; } if ((*(f_decode ? process_decode : process_encode))()) goto err; if (fflush(out) < 0 || ferror(out)) { perror(outname ? outname : "stdout"); fprintf(stderr, "%s: error writing \"%s\"\n", progname, outname ? outname:"stdout"); goto err; } if (out != stdout) { update_times(); update_mode (); update_own (); if (fclose(out) < 0) { perror(outname); fprintf(stderr, "%s: error writing \"%s\"\n", progname, outname); goto err; } if (outname != name) free(outname); outname = (char *)0; } out = (FILE *)0; if (in != stdin) { (void)fclose(in), in = (FILE *)0; if (!f_cat && !f_precious) { if (unlink(inname) < 0) { perror(inname); fprintf(stderr, "%s: source \"%s\" not deleted.\n", progname, inname); } goto err; } if (inname != name) free(inname); inname = (char *)0; } return 0; /* * Error handling and cleanup. */ err: if (out && out != stdout) { (void)fclose(out), out = (FILE *)0; if (unlink(outname) < 0 && errno != ENOENT && errno != EINTR) { perror(outname); fprintf(stderr, "%s: could not unlink \"%s\"\n", progname, outname); } } if (in && in != stdin) (void)fclose(in), in = (FILE *)0; if (inname && inname != name) free(inname); if (outname && outname != name) free(outname); return -1; } static void version P0() { printf( "%s 1.0, version %s\n", progname, "$Id: toast.c,v 1.8 1996/07/02 10:41:04 jutta Exp $" ); } static void help P0() { printf("Usage: %s [-fcpdhvaulsFC] [files...]\n", progname); printf("\n"); printf(" -f force Replace existing files without asking\n"); printf(" -c cat Write to stdout, do not remove source files\n"); printf(" -d decode Decode data (default is encode)\n"); printf(" -p precious Do not delete the source\n"); printf("\n"); printf(" -u u-law Force 8 kHz/8 bit u-law in/output format\n"); printf(" -s sun .au Force Sun .au u-law in/output format\n"); printf(" -a A-law Force 8 kHz/8 bit A-law in/output format\n"); printf(" -l linear Force 16 bit linear in/output format\n"); printf("\n"); printf(" -F fast Sacrifice conformance to performance\n"); printf(" -C cutoff Ignore most samples during LTP\n"); printf(" -v version Show version information\n"); printf(" -h help Print this text\n"); printf("\n"); } static void set_format P1((f), struct fmtdesc * f) { if (f_format && f_format != f) { fprintf( stderr, "%s: only one of -[uals] is possible (%s -h for help)\n", progname, progname); exit(1); } f_format = f; } int main P2((ac, av), int ac, char **av) { int opt; extern int optind; extern char * optarg; parse_argv0(*av); while ((opt = getopt(ac, av, "fcdpvhuaslVFC:")) != EOF) switch (opt) { case 'd': f_decode = 1; break; case 'f': f_force = 1; break; case 'c': f_cat = 1; break; case 'p': f_precious = 1; break; case 'F': f_fast = 1; break; case 'C': f_ltp_cut = 100; break; #ifndef NDEBUG case 'V': f_verbose = 1; break; /* undocumented */ #endif case 'u': set_format( &f_ulaw ); break; case 'l': set_format( &f_linear ); break; case 'a': set_format( &f_alaw ); break; case 's': set_format( &f_audio ); break; case 'v': version(); exit(0); case 'h': help(); exit(0); default: usage: fprintf(stderr, "Usage: %s [-fcpdhvuaslFC] [files...] (-h for help)\n", progname); exit(1); } f_precious |= f_cat; av += optind; ac -= optind; catch_signals(onintr); if (ac <= 0) process( (char *)0 ); else while (ac--) process( *av++ ); exit(0); } ================================================ FILE: deps/pjsip/third_party/gsm/src/toast_alaw.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /home/kbs/jutta/src/gsm/gsm-1.0/src/RCS/toast_alaw.c,v 1.2 1996/07/05 17:23:46 jutta Exp $ */ #include "toast.h" /* toast_alaw.c -- manipulate A-law encoded sound. */ extern FILE * in, * out; #define A2S(x) (a2s[ (unsigned char )(x) ]) #define S2A(x) (s2a[ ((unsigned short)(x)) >> 4 ]) static unsigned short a2s[] = { 60032, 60288, 59520, 59776, 61056, 61312, 60544, 60800, 57984, 58240, 57472, 57728, 59008, 59264, 58496, 58752, 62784, 62912, 62528, 62656, 63296, 63424, 63040, 63168, 61760, 61888, 61504, 61632, 62272, 62400, 62016, 62144, 43520, 44544, 41472, 42496, 47616, 48640, 45568, 46592, 35328, 36352, 33280, 34304, 39424, 40448, 37376, 38400, 54528, 55040, 53504, 54016, 56576, 57088, 55552, 56064, 50432, 50944, 49408, 49920, 52480, 52992, 51456, 51968, 65192, 65208, 65160, 65176, 65256, 65272, 65224, 65240, 65064, 65080, 65032, 65048, 65128, 65144, 65096, 65112, 65448, 65464, 65416, 65432, 65512, 65528, 65480, 65496, 65320, 65336, 65288, 65304, 65384, 65400, 65352, 65368, 64160, 64224, 64032, 64096, 64416, 64480, 64288, 64352, 63648, 63712, 63520, 63584, 63904, 63968, 63776, 63840, 64848, 64880, 64784, 64816, 64976, 65008, 64912, 64944, 64592, 64624, 64528, 64560, 64720, 64752, 64656, 64688, 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, 344, 328, 376, 360, 280, 264, 312, 296, 472, 456, 504, 488, 408, 392, 440, 424, 88, 72, 120, 104, 24, 8, 56, 40, 216, 200, 248, 232, 152, 136, 184, 168, 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, 688, 656, 752, 720, 560, 528, 624, 592, 944, 912, 1008, 976, 816, 784, 880, 848 }; static unsigned char s2a[] = { 213,212,215,214,209,208,211,210,221,220,223,222,217,216,219,218, 197,196,199,198,193,192,195,194,205,204,207,206,201,200,203,202, 245,245,244,244,247,247,246,246,241,241,240,240,243,243,242,242, 253,253,252,252,255,255,254,254,249,249,248,248,251,251,250,250, 229,229,229,229,228,228,228,228,231,231,231,231,230,230,230,230, 225,225,225,225,224,224,224,224,227,227,227,227,226,226,226,226, 237,237,237,237,236,236,236,236,239,239,239,239,238,238,238,238, 233,233,233,233,232,232,232,232,235,235,235,235,234,234,234,234, 149,149,149,149,149,149,149,149,148,148,148,148,148,148,148,148, 151,151,151,151,151,151,151,151,150,150,150,150,150,150,150,150, 145,145,145,145,145,145,145,145,144,144,144,144,144,144,144,144, 147,147,147,147,147,147,147,147,146,146,146,146,146,146,146,146, 157,157,157,157,157,157,157,157,156,156,156,156,156,156,156,156, 159,159,159,159,159,159,159,159,158,158,158,158,158,158,158,158, 153,153,153,153,153,153,153,153,152,152,152,152,152,152,152,152, 155,155,155,155,155,155,155,155,154,154,154,154,154,154,154,154, 133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133, 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132, 135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135, 134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134, 129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129, 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, 131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131, 130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130, 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141, 140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, 142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142, 137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137, 136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136, 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, 138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138, 181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, 181,181,181,181,181,181,181,181,181,181,181,181,181,181,181,181, 180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180, 180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180, 183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183, 183,183,183,183,183,183,183,183,183,183,183,183,183,183,183,183, 182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, 182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, 177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, 177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177, 176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, 176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176, 179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179, 179,179,179,179,179,179,179,179,179,179,179,179,179,179,179,179, 178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178, 178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178, 189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189, 189,189,189,189,189,189,189,189,189,189,189,189,189,189,189,189, 188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188, 188,188,188,188,188,188,188,188,188,188,188,188,188,188,188,188, 191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, 191,191,191,191,191,191,191,191,191,191,191,191,191,191,191,191, 190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190, 190,190,190,190,190,190,190,190,190,190,190,190,190,190,190,190, 185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185, 185,185,185,185,185,185,185,185,185,185,185,185,185,185,185,185, 184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, 184,184,184,184,184,184,184,184,184,184,184,184,184,184,184,184, 187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187, 187,187,187,187,187,187,187,187,187,187,187,187,187,187,187,187, 186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186, 186,186,186,186,186,186,186,186,186,186,186,186,186,186,186,186, 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, 165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, 164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164, 167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167, 167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167, 167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167, 167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167, 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166, 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166, 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166, 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166, 161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, 161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, 161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, 161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161, 160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, 160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, 160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, 160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, 163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163, 163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163, 163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163, 163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163, 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162, 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162, 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162, 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162, 173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, 173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, 173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, 173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173, 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, 172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, 175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175, 175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175, 175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175, 175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175, 174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, 174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, 174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, 174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, 169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, 169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, 169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, 169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, 168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, 168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, 168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, 168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, 171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171, 171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171, 171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171, 171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171, 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 106,106,106,106,107,107,107,107,104,104,104,104,105,105,105,105, 110,110,110,110,111,111,111,111,108,108,108,108,109,109,109,109, 98, 98, 98, 98, 99, 99, 99, 99, 96, 96, 96, 96, 97, 97, 97, 97, 102,102,102,102,103,103,103,103,100,100,100,100,101,101,101,101, 122,122,123,123,120,120,121,121,126,126,127,127,124,124,125,125, 114,114,115,115,112,112,113,113,118,118,119,119,116,116,117,117, 74, 75, 72, 73, 78, 79, 76, 77, 66, 67, 64, 65, 70, 71, 68, 69, 90, 91, 88, 89, 94, 95, 92, 93, 82, 83, 80, 81, 86, 87, 84, 85 }; int alaw_input P1((buf), gsm_signal * buf) { int i, c; for (i = 0; i < 160 && (c = fgetc(in)) != EOF; i++) buf[i] = A2S( c ); if (c == EOF && ferror(in)) return -1; return i; } int alaw_output P1((buf), gsm_signal * buf) { int i; for (i = 0; i < 160; i++, buf++) if (fputc( S2A( *buf ), out) == EOF) return -1; return 0; } ================================================ FILE: deps/pjsip/third_party/gsm/src/toast_audio.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/toast_audio.c,v 1.6 1995/03/07 21:21:24 jutta Exp $ */ #include "toast.h" /* toast_audio -- functions to manipulate SunOS audio files. * * This is reverse engineered from our present soundfiles * and in no way portable, durable or aesthetically pleasing. */ extern FILE * in, * out; extern char * inname; extern char * progname; extern int (*output) P((gsm_signal *)), (*input ) P((gsm_signal *)); extern int alaw_input P((gsm_signal *)), ulaw_input P((gsm_signal *)), linear_input P((gsm_signal *)); extern int ulaw_output P((gsm_signal *)); static int put_u32 P2((f, u), FILE * f, unsigned long u) { /* Write a 32-bit unsigned value msb first. */ if ( putc( (char)((u>>24) & 0x0FF), f) == EOF || putc( (char)((u>>16) & 0x0FF), f) == EOF || putc( (char)((u>> 8) & 0x0FF), f) == EOF || putc( (char)( u & 0x0FF), f) == EOF) return -1; return 0; } static int get_u32 P2((f, up), FILE * f, unsigned long * up) { /* Read a 32-bit unsigned value msb first. */ int i; unsigned long u; if ( (i = getc(f)) == EOF || ((u = (unsigned char)i), (i = getc(f)) == EOF) || ((u = (u<<8)|(unsigned char)i), (i = getc(f)) == EOF) || ((u = (u<<8)|(unsigned char)i), (i = getc(f)) == EOF)) return -1; *up = (u<<8)|(unsigned char)i; return 0; } int audio_init_input P0() { unsigned long len, enc; /* unsigned 32 bits */ if ( fgetc(in) != '.' || fgetc(in) != 's' || fgetc(in) != 'n' || fgetc(in) != 'd' || get_u32( in, &len ) || get_u32( in, &enc ) /* skip this */ || get_u32( in, &enc )) { fprintf(stderr, "%s: bad (missing?) header in Sun audio file \"%s\";\n\ Try one of -u, -a, -l instead (%s -h for help).\n", progname, inname ? inname : "stdin", progname); return -1; } switch (enc) { case 1: input = ulaw_input; break; case 2: input = alaw_input; break; case 3: input = linear_input; break; default: fprintf(stderr, "%s: warning: file format #%lu for %s not implemented, defaulting to u-law.\n", progname, enc, inname); input = ulaw_input; break; } while (len > 4*4) if (getc(in) == EOF) { fprintf(stderr, "%s: EOF in header of Sun audio file \"%s\";\n\ Try one of -u, -a, -l instead (%s -h for help).\n", progname, inname ? inname : "stdin", progname); return -1; } else len--; return 0; } int audio_init_output P0() { if ( fputs(".snd", out) == EOF || put_u32(out, 32) || put_u32(out, ~(unsigned long)0) || put_u32(out, 1) || put_u32(out, 8000) || put_u32(out, 1) || put_u32(out, 0) || put_u32(out, 0)) return -1; return 0; } ================================================ FILE: deps/pjsip/third_party/gsm/src/toast_lin.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/toast_lin.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */ #include "toast.h" /* toast_linear.c -- read and write 16 bit linear sound in host byte order. */ extern FILE *in, *out; int linear_input (buf) gsm_signal * buf; { return fread( (char *)buf, sizeof(*buf), 160, in ); } int linear_output P1((buf), gsm_signal * buf) { return -( fwrite( (char *)buf, sizeof(*buf), 160, out ) != 160 ); } ================================================ FILE: deps/pjsip/third_party/gsm/src/toast_ulaw.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/toast_ulaw.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */ #include "toast.h" /* toast_ulaw -- functions to manipulate u-law encoded sound. */ extern FILE *in, *out; #define U2S(x) (u2s[ (unsigned char)(x) ]) #define S2U(x) (s2u[ ((unsigned short)(x)) >> 3 ]) static unsigned short u2s[] = { 33280, 34308, 35336, 36364, 37393, 38421, 39449, 40477, 41505, 42534, 43562, 44590, 45618, 46647, 47675, 48703, 49474, 49988, 50503, 51017, 51531, 52045, 52559, 53073, 53587, 54101, 54616, 55130, 55644, 56158, 56672, 57186, 57572, 57829, 58086, 58343, 58600, 58857, 59114, 59371, 59628, 59885, 60142, 60399, 60656, 60913, 61171, 61428, 61620, 61749, 61877, 62006, 62134, 62263, 62392, 62520, 62649, 62777, 62906, 63034, 63163, 63291, 63420, 63548, 63645, 63709, 63773, 63838, 63902, 63966, 64030, 64095, 64159, 64223, 64287, 64352, 64416, 64480, 64544, 64609, 64657, 64689, 64721, 64753, 64785, 64818, 64850, 64882, 64914, 64946, 64978, 65010, 65042, 65075, 65107, 65139, 65163, 65179, 65195, 65211, 65227, 65243, 65259, 65275, 65291, 65308, 65324, 65340, 65356, 65372, 65388, 65404, 65416, 65424, 65432, 65440, 65448, 65456, 65464, 65472, 65480, 65488, 65496, 65504, 65512, 65520, 65528, 0, 32256, 31228, 30200, 29172, 28143, 27115, 26087, 25059, 24031, 23002, 21974, 20946, 19918, 18889, 17861, 16833, 16062, 15548, 15033, 14519, 14005, 13491, 12977, 12463, 11949, 11435, 10920, 10406, 9892, 9378, 8864, 8350, 7964, 7707, 7450, 7193, 6936, 6679, 6422, 6165, 5908, 5651, 5394, 5137, 4880, 4623, 4365, 4108, 3916, 3787, 3659, 3530, 3402, 3273, 3144, 3016, 2887, 2759, 2630, 2502, 2373, 2245, 2116, 1988, 1891, 1827, 1763, 1698, 1634, 1570, 1506, 1441, 1377, 1313, 1249, 1184, 1120, 1056, 992, 927, 879, 847, 815, 783, 751, 718, 686, 654, 622, 590, 558, 526, 494, 461, 429, 397, 373, 357, 341, 325, 309, 293, 277, 261, 245, 228, 212, 196, 180, 164, 148, 132, 120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0 }; static unsigned char s2u[] = { 0377,0376,0375,0374,0373,0372,0371,0370,0367,0366,0365,0364,0363,0362,0361, 0360,0357,0357,0356,0356,0355,0355,0354,0354,0353,0353,0352,0352,0351,0351, 0350,0350,0347,0347,0346,0346,0345,0345,0344,0344,0343,0343,0342,0342,0341, 0341,0340,0340,0337,0337,0337,0337,0336,0336,0336,0336,0335,0335,0335,0335, 0334,0334,0334,0334,0333,0333,0333,0333,0332,0332,0332,0332,0331,0331,0331, 0331,0330,0330,0330,0330,0327,0327,0327,0327,0326,0326,0326,0326,0325,0325, 0325,0325,0324,0324,0324,0324,0323,0323,0323,0323,0322,0322,0322,0322,0321, 0321,0321,0321,0320,0320,0320,0320,0317,0317,0317,0317,0317,0317,0317,0317, 0316,0316,0316,0316,0316,0316,0316,0316,0315,0315,0315,0315,0315,0315,0315, 0315,0314,0314,0314,0314,0314,0314,0314,0314,0313,0313,0313,0313,0313,0313, 0313,0313,0312,0312,0312,0312,0312,0312,0312,0312,0311,0311,0311,0311,0311, 0311,0311,0311,0310,0310,0310,0310,0310,0310,0310,0310,0307,0307,0307,0307, 0307,0307,0307,0307,0306,0306,0306,0306,0306,0306,0306,0306,0305,0305,0305, 0305,0305,0305,0305,0305,0304,0304,0304,0304,0304,0304,0304,0304,0303,0303, 0303,0303,0303,0303,0303,0303,0303,0302,0302,0302,0302,0302,0302,0302,0302, 0301,0301,0301,0301,0301,0301,0301,0301,0300,0300,0300,0300,0300,0300,0300, 0300,0277,0277,0277,0277,0277,0277,0277,0277,0277,0277,0277,0277,0277,0277, 0277,0277,0276,0276,0276,0276,0276,0276,0276,0276,0276,0276,0276,0276,0276, 0276,0276,0276,0275,0275,0275,0275,0275,0275,0275,0275,0275,0275,0275,0275, 0275,0275,0275,0275,0274,0274,0274,0274,0274,0274,0274,0274,0274,0274,0274, 0274,0274,0274,0274,0274,0273,0273,0273,0273,0273,0273,0273,0273,0273,0273, 0273,0273,0273,0273,0273,0273,0272,0272,0272,0272,0272,0272,0272,0272,0272, 0272,0272,0272,0272,0272,0272,0272,0271,0271,0271,0271,0271,0271,0271,0271, 0271,0271,0271,0271,0271,0271,0271,0271,0270,0270,0270,0270,0270,0270,0270, 0270,0270,0270,0270,0270,0270,0270,0270,0270,0267,0267,0267,0267,0267,0267, 0267,0267,0267,0267,0267,0267,0267,0267,0267,0267,0266,0266,0266,0266,0266, 0266,0266,0266,0266,0266,0266,0266,0266,0266,0266,0266,0265,0265,0265,0265, 0265,0265,0265,0265,0265,0265,0265,0265,0265,0265,0265,0265,0264,0264,0264, 0264,0264,0264,0264,0264,0264,0264,0264,0264,0264,0264,0264,0264,0263,0263, 0263,0263,0263,0263,0263,0263,0263,0263,0263,0263,0263,0263,0263,0263,0262, 0262,0262,0262,0262,0262,0262,0262,0262,0262,0262,0262,0262,0262,0262,0262, 0262,0261,0261,0261,0261,0261,0261,0261,0261,0261,0261,0261,0261,0261,0261, 0261,0261,0260,0260,0260,0260,0260,0260,0260,0260,0260,0260,0260,0260,0260, 0260,0260,0260,0257,0257,0257,0257,0257,0257,0257,0257,0257,0257,0257,0257, 0257,0257,0257,0257,0257,0257,0257,0257,0257,0257,0257,0257,0257,0257,0257, 0257,0257,0257,0257,0257,0256,0256,0256,0256,0256,0256,0256,0256,0256,0256, 0256,0256,0256,0256,0256,0256,0256,0256,0256,0256,0256,0256,0256,0256,0256, 0256,0256,0256,0256,0256,0256,0256,0255,0255,0255,0255,0255,0255,0255,0255, 0255,0255,0255,0255,0255,0255,0255,0255,0255,0255,0255,0255,0255,0255,0255, 0255,0255,0255,0255,0255,0255,0255,0255,0255,0254,0254,0254,0254,0254,0254, 0254,0254,0254,0254,0254,0254,0254,0254,0254,0254,0254,0254,0254,0254,0254, 0254,0254,0254,0254,0254,0254,0254,0254,0254,0254,0254,0253,0253,0253,0253, 0253,0253,0253,0253,0253,0253,0253,0253,0253,0253,0253,0253,0253,0253,0253, 0253,0253,0253,0253,0253,0253,0253,0253,0253,0253,0253,0253,0253,0252,0252, 0252,0252,0252,0252,0252,0252,0252,0252,0252,0252,0252,0252,0252,0252,0252, 0252,0252,0252,0252,0252,0252,0252,0252,0252,0252,0252,0252,0252,0252,0252, 0251,0251,0251,0251,0251,0251,0251,0251,0251,0251,0251,0251,0251,0251,0251, 0251,0251,0251,0251,0251,0251,0251,0251,0251,0251,0251,0251,0251,0251,0251, 0251,0251,0251,0250,0250,0250,0250,0250,0250,0250,0250,0250,0250,0250,0250, 0250,0250,0250,0250,0250,0250,0250,0250,0250,0250,0250,0250,0250,0250,0250, 0250,0250,0250,0250,0250,0247,0247,0247,0247,0247,0247,0247,0247,0247,0247, 0247,0247,0247,0247,0247,0247,0247,0247,0247,0247,0247,0247,0247,0247,0247, 0247,0247,0247,0247,0247,0247,0247,0246,0246,0246,0246,0246,0246,0246,0246, 0246,0246,0246,0246,0246,0246,0246,0246,0246,0246,0246,0246,0246,0246,0246, 0246,0246,0246,0246,0246,0246,0246,0246,0246,0245,0245,0245,0245,0245,0245, 0245,0245,0245,0245,0245,0245,0245,0245,0245,0245,0245,0245,0245,0245,0245, 0245,0245,0245,0245,0245,0245,0245,0245,0245,0245,0245,0244,0244,0244,0244, 0244,0244,0244,0244,0244,0244,0244,0244,0244,0244,0244,0244,0244,0244,0244, 0244,0244,0244,0244,0244,0244,0244,0244,0244,0244,0244,0244,0244,0243,0243, 0243,0243,0243,0243,0243,0243,0243,0243,0243,0243,0243,0243,0243,0243,0243, 0243,0243,0243,0243,0243,0243,0243,0243,0243,0243,0243,0243,0243,0243,0243, 0242,0242,0242,0242,0242,0242,0242,0242,0242,0242,0242,0242,0242,0242,0242, 0242,0242,0242,0242,0242,0242,0242,0242,0242,0242,0242,0242,0242,0242,0242, 0242,0242,0242,0241,0241,0241,0241,0241,0241,0241,0241,0241,0241,0241,0241, 0241,0241,0241,0241,0241,0241,0241,0241,0241,0241,0241,0241,0241,0241,0241, 0241,0241,0241,0241,0241,0240,0240,0240,0240,0240,0240,0240,0240,0240,0240, 0240,0240,0240,0240,0240,0240,0240,0240,0240,0240,0240,0240,0240,0240,0240, 0240,0240,0240,0240,0240,0240,0240,0237,0237,0237,0237,0237,0237,0237,0237, 0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237, 0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237, 0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237, 0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0237,0236,0236,0236,0236, 0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236, 0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236, 0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236, 0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236,0236, 0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235, 0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235, 0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235, 0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235,0235, 0235,0235,0235,0235,0235,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234, 0234,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234, 0234,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234, 0234,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234,0234, 0234,0234,0234,0234,0234,0234,0234,0234,0234,0233,0233,0233,0233,0233,0233, 0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233, 0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233, 0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233, 0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0233,0232,0232, 0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232, 0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232, 0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232, 0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232,0232, 0232,0232,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231, 0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231, 0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231, 0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231,0231, 0231,0231,0231,0231,0231,0231,0231,0230,0230,0230,0230,0230,0230,0230,0230, 0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230, 0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230, 0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230, 0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0230,0227,0227,0227,0227, 0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227, 0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227, 0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227, 0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227,0227, 0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226, 0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226, 0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226, 0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226,0226, 0226,0226,0226,0226,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225, 0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225, 0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225, 0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225,0225, 0225,0225,0225,0225,0225,0225,0225,0225,0225,0224,0224,0224,0224,0224,0224, 0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224, 0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224, 0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224, 0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0224,0223,0223, 0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223, 0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223, 0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223, 0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223,0223, 0223,0223,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222, 0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222, 0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222, 0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222,0222, 0222,0222,0222,0222,0222,0222,0221,0221,0221,0221,0221,0221,0221,0221,0221, 0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221, 0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221, 0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221, 0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0221,0220,0220,0220,0220, 0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220, 0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220, 0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220, 0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220,0220, 0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217, 0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217, 0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217, 0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217, 0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217, 0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217, 0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217, 0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217,0217, 0217,0217,0217,0217,0217,0217,0217,0217,0217,0216,0216,0216,0216,0216,0216, 0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216, 0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216, 0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216, 0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216, 0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216, 0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216, 0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216, 0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216,0216, 0216,0216,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215, 0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215, 0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215, 0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215, 0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215, 0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215, 0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215, 0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215, 0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0215,0214,0214,0214,0214, 0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214, 0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214, 0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214, 0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214, 0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214, 0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214, 0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214, 0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214,0214, 0214,0214,0214,0214,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213, 0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213, 0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213, 0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213, 0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213, 0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213, 0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213, 0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213, 0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0213,0212,0212, 0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212, 0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212, 0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212, 0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212, 0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212, 0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212, 0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212, 0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212,0212, 0212,0212,0212,0212,0212,0212,0211,0211,0211,0211,0211,0211,0211,0211,0211, 0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211, 0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211, 0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211, 0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211, 0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211, 0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211, 0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211, 0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211,0211, 0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210, 0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210, 0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210, 0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210, 0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210, 0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210, 0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210, 0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210,0210, 0210,0210,0210,0210,0210,0210,0210,0210,0207,0207,0207,0207,0207,0207,0207, 0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207, 0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207, 0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207, 0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207, 0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207, 0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207, 0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207, 0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207,0207, 0207,0207,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206, 0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206, 0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206, 0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206, 0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206, 0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206, 0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206, 0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0206, 0206,0206,0206,0206,0206,0206,0206,0206,0206,0206,0205,0205,0205,0205,0205, 0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205, 0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205, 0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205, 0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205, 0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205, 0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205, 0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205, 0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205,0205, 0205,0205,0205,0205,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204, 0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204, 0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204, 0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204, 0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204, 0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204, 0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204, 0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204, 0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0204,0203,0203,0203, 0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203, 0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203, 0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203, 0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203, 0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203, 0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203, 0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203, 0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203,0203, 0203,0203,0203,0203,0203,0203,0202,0202,0202,0202,0202,0202,0202,0202,0202, 0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202, 0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202, 0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202, 0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202, 0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202, 0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202, 0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202, 0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0202,0201, 0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201, 0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201, 0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201, 0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201, 0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201, 0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201, 0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201, 0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201,0201, 0201,0201,0201,0201,0201,0201,0201,0201,0200,0200,0200,0200,0200,0200,0200, 0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200, 0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200, 0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200, 0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200, 0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200, 0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200, 0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200, 0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200,0200, 0200,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000, 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000, 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000, 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000, 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000, 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000, 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000, 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000, 0000,0000,0000,0000,0000,0000,0000,0000,0000,0001,0001,0001,0001,0001,0001, 0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001, 0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001, 0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001, 0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001, 0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001, 0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001, 0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001, 0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001,0001, 0001,0001,0001,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002, 0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002, 0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002, 0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002, 0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002, 0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002, 0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002, 0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002, 0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0002,0003,0003,0003,0003, 0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003, 0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003, 0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003, 0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003, 0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003, 0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003, 0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003, 0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003,0003, 0003,0003,0003,0003,0003,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004, 0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004, 0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004, 0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004, 0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004, 0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004, 0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004, 0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004, 0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0004,0005,0005, 0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005, 0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005, 0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005, 0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005, 0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005, 0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005, 0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005, 0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005,0005, 0005,0005,0005,0005,0005,0005,0005,0006,0006,0006,0006,0006,0006,0006,0006, 0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006, 0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006, 0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006, 0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006, 0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006, 0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006, 0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006, 0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006,0006, 0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007, 0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007, 0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007, 0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007, 0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007, 0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007, 0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007, 0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007,0007, 0007,0007,0007,0007,0007,0007,0007,0007,0007,0010,0010,0010,0010,0010,0010, 0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010, 0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010, 0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010, 0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010, 0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010, 0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010, 0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010, 0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010,0010, 0010,0010,0010,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011, 0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011, 0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011, 0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011, 0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011, 0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011, 0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011, 0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011, 0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0011,0012,0012,0012,0012, 0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012, 0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012, 0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012, 0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012, 0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012, 0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012, 0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012, 0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012,0012, 0012,0012,0012,0012,0012,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013, 0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013, 0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013, 0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013, 0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013, 0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013, 0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013, 0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013, 0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0013,0014,0014, 0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014, 0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014, 0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014, 0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014, 0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014, 0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014, 0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014, 0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014,0014, 0014,0014,0014,0014,0014,0014,0014,0015,0015,0015,0015,0015,0015,0015,0015, 0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015, 0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015, 0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015, 0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015, 0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015, 0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015, 0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015, 0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015, 0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016, 0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016, 0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016, 0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016, 0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016, 0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016, 0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016, 0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016,0016, 0016,0016,0016,0016,0016,0016,0016,0016,0016,0017,0017,0017,0017,0017,0017, 0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017, 0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017, 0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017, 0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017, 0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017, 0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017, 0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017, 0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017,0017, 0017,0017,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020, 0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020, 0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020, 0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020,0020, 0020,0020,0020,0020,0020,0020,0021,0021,0021,0021,0021,0021,0021,0021,0021, 0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021, 0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021, 0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021, 0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0021,0022,0022,0022,0022, 0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022, 0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022, 0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022, 0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022,0022, 0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023, 0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023, 0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023, 0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023,0023, 0023,0023,0023,0023,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024, 0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024, 0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024, 0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024,0024, 0024,0024,0024,0024,0024,0024,0024,0024,0024,0025,0025,0025,0025,0025,0025, 0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025, 0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025, 0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025, 0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0025,0026,0026, 0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026, 0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026, 0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026, 0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026,0026, 0026,0026,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027, 0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027, 0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027, 0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027,0027, 0027,0027,0027,0027,0027,0027,0030,0030,0030,0030,0030,0030,0030,0030,0030, 0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030, 0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030, 0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030, 0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0030,0031,0031,0031,0031, 0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031, 0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031, 0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031, 0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031,0031, 0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032, 0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032, 0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032, 0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032,0032, 0032,0032,0032,0032,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033, 0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033, 0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033, 0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033,0033, 0033,0033,0033,0033,0033,0033,0033,0033,0034,0034,0034,0034,0034,0034,0034, 0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034, 0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034, 0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034, 0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0034,0035,0035, 0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035, 0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035, 0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035, 0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035,0035, 0035,0035,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036, 0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036, 0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036, 0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036,0036, 0036,0036,0036,0036,0036,0036,0037,0037,0037,0037,0037,0037,0037,0037,0037, 0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0037, 0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0037, 0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0037, 0037,0037,0037,0037,0037,0037,0037,0037,0037,0037,0040,0040,0040,0040,0040, 0040,0040,0040,0040,0040,0040,0040,0040,0040,0040,0040,0040,0040,0040,0040, 0040,0040,0040,0040,0040,0040,0040,0040,0040,0040,0040,0040,0040,0041,0041, 0041,0041,0041,0041,0041,0041,0041,0041,0041,0041,0041,0041,0041,0041,0041, 0041,0041,0041,0041,0041,0041,0041,0041,0041,0041,0041,0041,0041,0041,0041, 0042,0042,0042,0042,0042,0042,0042,0042,0042,0042,0042,0042,0042,0042,0042, 0042,0042,0042,0042,0042,0042,0042,0042,0042,0042,0042,0042,0042,0042,0042, 0042,0042,0043,0043,0043,0043,0043,0043,0043,0043,0043,0043,0043,0043,0043, 0043,0043,0043,0043,0043,0043,0043,0043,0043,0043,0043,0043,0043,0043,0043, 0043,0043,0043,0043,0044,0044,0044,0044,0044,0044,0044,0044,0044,0044,0044, 0044,0044,0044,0044,0044,0044,0044,0044,0044,0044,0044,0044,0044,0044,0044, 0044,0044,0044,0044,0044,0044,0045,0045,0045,0045,0045,0045,0045,0045,0045, 0045,0045,0045,0045,0045,0045,0045,0045,0045,0045,0045,0045,0045,0045,0045, 0045,0045,0045,0045,0045,0045,0045,0045,0046,0046,0046,0046,0046,0046,0046, 0046,0046,0046,0046,0046,0046,0046,0046,0046,0046,0046,0046,0046,0046,0046, 0046,0046,0046,0046,0046,0046,0046,0046,0046,0046,0047,0047,0047,0047,0047, 0047,0047,0047,0047,0047,0047,0047,0047,0047,0047,0047,0047,0047,0047,0047, 0047,0047,0047,0047,0047,0047,0047,0047,0047,0047,0047,0047,0047,0050,0050, 0050,0050,0050,0050,0050,0050,0050,0050,0050,0050,0050,0050,0050,0050,0050, 0050,0050,0050,0050,0050,0050,0050,0050,0050,0050,0050,0050,0050,0050,0050, 0051,0051,0051,0051,0051,0051,0051,0051,0051,0051,0051,0051,0051,0051,0051, 0051,0051,0051,0051,0051,0051,0051,0051,0051,0051,0051,0051,0051,0051,0051, 0051,0051,0052,0052,0052,0052,0052,0052,0052,0052,0052,0052,0052,0052,0052, 0052,0052,0052,0052,0052,0052,0052,0052,0052,0052,0052,0052,0052,0052,0052, 0052,0052,0052,0052,0053,0053,0053,0053,0053,0053,0053,0053,0053,0053,0053, 0053,0053,0053,0053,0053,0053,0053,0053,0053,0053,0053,0053,0053,0053,0053, 0053,0053,0053,0053,0053,0053,0054,0054,0054,0054,0054,0054,0054,0054,0054, 0054,0054,0054,0054,0054,0054,0054,0054,0054,0054,0054,0054,0054,0054,0054, 0054,0054,0054,0054,0054,0054,0054,0054,0055,0055,0055,0055,0055,0055,0055, 0055,0055,0055,0055,0055,0055,0055,0055,0055,0055,0055,0055,0055,0055,0055, 0055,0055,0055,0055,0055,0055,0055,0055,0055,0055,0056,0056,0056,0056,0056, 0056,0056,0056,0056,0056,0056,0056,0056,0056,0056,0056,0056,0056,0056,0056, 0056,0056,0056,0056,0056,0056,0056,0056,0056,0056,0056,0056,0057,0057,0057, 0057,0057,0057,0057,0057,0057,0057,0057,0057,0057,0057,0057,0057,0057,0057, 0057,0057,0057,0057,0057,0057,0057,0057,0057,0057,0057,0057,0057,0057,0057, 0060,0060,0060,0060,0060,0060,0060,0060,0060,0060,0060,0060,0060,0060,0060, 0060,0061,0061,0061,0061,0061,0061,0061,0061,0061,0061,0061,0061,0061,0061, 0061,0061,0062,0062,0062,0062,0062,0062,0062,0062,0062,0062,0062,0062,0062, 0062,0062,0062,0063,0063,0063,0063,0063,0063,0063,0063,0063,0063,0063,0063, 0063,0063,0063,0063,0064,0064,0064,0064,0064,0064,0064,0064,0064,0064,0064, 0064,0064,0064,0064,0064,0065,0065,0065,0065,0065,0065,0065,0065,0065,0065, 0065,0065,0065,0065,0065,0065,0066,0066,0066,0066,0066,0066,0066,0066,0066, 0066,0066,0066,0066,0066,0066,0066,0067,0067,0067,0067,0067,0067,0067,0067, 0067,0067,0067,0067,0067,0067,0067,0067,0070,0070,0070,0070,0070,0070,0070, 0070,0070,0070,0070,0070,0070,0070,0070,0070,0071,0071,0071,0071,0071,0071, 0071,0071,0071,0071,0071,0071,0071,0071,0071,0071,0072,0072,0072,0072,0072, 0072,0072,0072,0072,0072,0072,0072,0072,0072,0072,0072,0073,0073,0073,0073, 0073,0073,0073,0073,0073,0073,0073,0073,0073,0073,0073,0073,0074,0074,0074, 0074,0074,0074,0074,0074,0074,0074,0074,0074,0074,0074,0074,0074,0075,0075, 0075,0075,0075,0075,0075,0075,0075,0075,0075,0075,0075,0075,0075,0075,0075, 0076,0076,0076,0076,0076,0076,0076,0076,0076,0076,0076,0076,0076,0076,0076, 0076,0077,0077,0077,0077,0077,0077,0077,0077,0077,0077,0077,0077,0077,0077, 0077,0077,0100,0100,0100,0100,0100,0100,0100,0100,0101,0101,0101,0101,0101, 0101,0101,0101,0102,0102,0102,0102,0102,0102,0102,0102,0103,0103,0103,0103, 0103,0103,0103,0103,0104,0104,0104,0104,0104,0104,0104,0104,0105,0105,0105, 0105,0105,0105,0105,0105,0106,0106,0106,0106,0106,0106,0106,0106,0107,0107, 0107,0107,0107,0107,0107,0107,0110,0110,0110,0110,0110,0110,0110,0110,0111, 0111,0111,0111,0111,0111,0111,0111,0112,0112,0112,0112,0112,0112,0112,0112, 0113,0113,0113,0113,0113,0113,0113,0113,0114,0114,0114,0114,0114,0114,0114, 0114,0115,0115,0115,0115,0115,0115,0115,0115,0116,0116,0116,0116,0116,0116, 0116,0116,0117,0117,0117,0117,0117,0117,0117,0117,0120,0120,0120,0120,0121, 0121,0121,0121,0122,0122,0122,0122,0123,0123,0123,0123,0124,0124,0124,0124, 0125,0125,0125,0125,0126,0126,0126,0126,0127,0127,0127,0127,0130,0130,0130, 0130,0131,0131,0131,0131,0132,0132,0132,0132,0133,0133,0133,0133,0134,0134, 0134,0134,0135,0135,0135,0135,0136,0136,0136,0136,0137,0137,0137,0137,0140, 0140,0141,0141,0142,0142,0143,0143,0144,0144,0145,0145,0146,0146,0147,0147, 0150,0150,0150,0151,0151,0152,0152,0153,0153,0154,0154,0155,0155,0156,0156, 0157,0157,0160,0161,0162,0163,0164,0165,0166,0167,0170,0171,0172,0173,0174, 0175,0176 }; int ulaw_input P1((buf), gsm_signal * buf) { int i, c; for (i = 0; i < 160 && (c = fgetc(in)) != EOF; i++) buf[i] = U2S(c); if (c == EOF && ferror(in)) return -1; return i; } int ulaw_output P1((buf), gsm_signal * buf) { int i; for(i = 0; i < 160; i++, buf++) if (fputc( (char)S2U( (unsigned short)*buf ), out) == EOF) return -1; return 0; } ================================================ FILE: deps/pjsip/third_party/gsm/tls/bitter.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/bitter.c,v 1.1 1992/10/28 00:28:39 jutta Exp $*/ /* Generate code to pack a bit array from a name:#bits description */ #include #include "taste.h" #include "proto.h" void write_code P2((s_spex, n_spex), struct spex * s_spex, int n_spex) { struct spex * sp = s_spex; int bits = 8; int vars; if (!n_spex) return; vars = sp->varsize; while (n_spex) { if (bits == 8) printf("\t*c++ = "); else printf("\t | "); if (vars == bits) { printf( (bits==8? "%s & 0x%lX;\n" : "(%s & 0x%lX);\n"), sp->var, ~(0xfffffffe << (bits - 1))); if (!-- n_spex) break; sp++; vars = sp->varsize; bits = 8; } else if (vars < bits) { printf( "((%s & 0x%lX) << %d)", sp->var, ~(0xfffffffe << (vars - 1)), bits - vars); bits -= vars; if (!--n_spex) { puts(";"); break; } else putchar('\n'); sp++; vars = sp->varsize; } else { printf("((%s >> %d) & 0x%X);\n", sp->var, vars - bits, ~(0xfffffffe << (bits - 1))); vars -= bits; bits = 8; } } } ================================================ FILE: deps/pjsip/third_party/gsm/tls/bitter.dta ================================================ ; ; Copyright 1992 by Jutta Degener and Carsten Bormann, Technische ; Universitaet Berlin. See the accompanying file "COPYRIGHT" for ; details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. ; ; ; Variable Size GSM_MAGIC 4 LARc[0] 6 LARc[1] 6 LARc[2] 5 LARc[3] 5 LARc[4] 4 LARc[5] 4 LARc[6] 3 LARc[7] 3 Nc[0] 7 bc[0] 2 Mc[0] 2 xmaxc[0] 6 xmc[0] 3 xmc[1] 3 xmc[2] 3 xmc[3] 3 xmc[4] 3 xmc[5] 3 xmc[6] 3 xmc[7] 3 xmc[8] 3 xmc[9] 3 xmc[10] 3 xmc[11] 3 xmc[12] 3 Nc[1] 7 bc[1] 2 Mc[1] 2 xmaxc[1] 6 xmc[13] 3 xmc[14] 3 xmc[15] 3 xmc[16] 3 xmc[17] 3 xmc[18] 3 xmc[19] 3 xmc[20] 3 xmc[21] 3 xmc[22] 3 xmc[23] 3 xmc[24] 3 xmc[25] 3 Nc[2] 7 bc[2] 2 Mc[2] 2 xmaxc[2] 6 xmc[26] 3 xmc[27] 3 xmc[28] 3 xmc[29] 3 xmc[30] 3 xmc[31] 3 xmc[32] 3 xmc[33] 3 xmc[34] 3 xmc[35] 3 xmc[36] 3 xmc[37] 3 xmc[38] 3 Nc[3] 7 bc[3] 2 Mc[3] 2 xmaxc[3] 6 xmc[39] 3 xmc[40] 3 xmc[41] 3 xmc[42] 3 xmc[43] 3 xmc[44] 3 xmc[45] 3 xmc[46] 3 xmc[47] 3 xmc[48] 3 xmc[49] 3 xmc[50] 3 xmc[51] 3 ================================================ FILE: deps/pjsip/third_party/gsm/tls/ginger.c ================================================ /* * Copyright 1996 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /*$Header*/ /* Generate code to pack a bit array from a name:#bits description */ #include #include "taste.h" #include "proto.h" #include /* This module is the opposite of sour. Sweet was already taken, * that's why it's called ginger. (Add one point if that reminds * you of Gary Larson.) */ #define WORD_BITS 16 /* sizeof(uword) * CHAR_BIT on the * target architecture---if this isn't 16, * you're in trouble with this library anyway. */ #define BYTE_BITS 8 /* CHAR_BIT on the target architecture--- * if this isn't 8, you're in *deep* trouble. */ void write_code P2((s_spex, n_spex), struct spex * s_spex, int n_spex) { struct spex * sp = s_spex; int n_in = 0; printf("uword sr = 0;\n"); for (; n_spex > 0; n_spex--, sp++) { while (n_in < sp->varsize) { if (n_in) printf("sr |= (uword)*c++ << %d;\n", n_in); else printf("sr = *c++;\n"); n_in += BYTE_BITS; } printf("%s = sr & %#x; sr >>= %d;\n", sp->var, ~(~0U << sp->varsize), sp->varsize); n_in -= sp->varsize; } if (n_in > 0) { fprintf(stderr, "%d bits left over\n", n_in); } } ================================================ FILE: deps/pjsip/third_party/gsm/tls/sour.c ================================================ /* * Copyright 1996 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /*$Header*/ /* Generate code to pack a bit array from a name:#bits description, * WAV #49 style. */ #include #include "taste.h" #include "proto.h" #include /* This module goes back to one Jeff Chilton used for his implementation * of the #49 WAV GSM format. (In his original patch 8, it replaced * bitter.c.) * * In Microsoft's WAV #49 version of the GSM format, two 32 1/2 * byte GSM frames are packed together to make one WAV frame, and * the GSM parameters are packed into bytes right-to-left rather * than left-to-right. * * That is, where toast's GSM format writes * * aaaaaabb bbbbcccc cdddddee ... * ___1____ ___2____ ___3____ * * for parameters a (6 bits), b (6 bits), c (5 bits), d (5 bits), e .. * the WAV format has * * bbaaaaaa ccccbbbb eedddddc ... * ___1____ ___2____ ___3____ * * (This format looks a lot prettier if one pictures octets coming * in through a fifo queue from the left, rather than waiting in the * right-hand remainder of a C array.) */ #define WORD_BITS 16 /* sizeof(uword) * CHAR_BIT on the * target architecture---if this isn't 16, * you're in trouble with this library anyway. */ #define BYTE_BITS 8 /* CHAR_BIT on the target architecture--- * if this isn't 8, you're in *deep* trouble. */ void write_code P2((s_spex, n_spex), struct spex * s_spex, int n_spex) { struct spex * sp = s_spex; int n_in = 0; printf("uword sr = 0;\n"); for (; n_spex > 0; n_spex--, sp++) { /* insert old * new var value unused * here * * [____________xxxxxx**********] * * <----- n_in ------> */ printf("sr = sr >> %d | %s << %d;\n", sp->varsize, sp->var, WORD_BITS - sp->varsize); n_in += sp->varsize; while (n_in >= BYTE_BITS) { printf("*c++ = sr >> %d;\n", WORD_BITS - n_in); n_in -= BYTE_BITS; } } while (n_in >= BYTE_BITS) { printf("*c++ = sr >> %d;\n", WORD_BITS - n_in); n_in -= BYTE_BITS; } if (n_in > 0) { fprintf(stderr, "warning: %d bits left over\n", n_in); } } ================================================ FILE: deps/pjsip/third_party/gsm/tls/sour1.dta ================================================ ; ; Copyright 1992 by Jutta Degener and Carsten Bormann, Technische ; Universitaet Berlin. See the accompanying file "COPYRIGHT" for ; details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. ; ; ; Variable Size LARc[0] 6 LARc[1] 6 LARc[2] 5 LARc[3] 5 LARc[4] 4 LARc[5] 4 LARc[6] 3 LARc[7] 3 Nc[0] 7 bc[0] 2 Mc[0] 2 xmaxc[0] 6 xmc[0] 3 xmc[1] 3 xmc[2] 3 xmc[3] 3 xmc[4] 3 xmc[5] 3 xmc[6] 3 xmc[7] 3 xmc[8] 3 xmc[9] 3 xmc[10] 3 xmc[11] 3 xmc[12] 3 Nc[1] 7 bc[1] 2 Mc[1] 2 xmaxc[1] 6 xmc[13] 3 xmc[14] 3 xmc[15] 3 xmc[16] 3 xmc[17] 3 xmc[18] 3 xmc[19] 3 xmc[20] 3 xmc[21] 3 xmc[22] 3 xmc[23] 3 xmc[24] 3 xmc[25] 3 Nc[2] 7 bc[2] 2 Mc[2] 2 xmaxc[2] 6 xmc[26] 3 xmc[27] 3 xmc[28] 3 xmc[29] 3 xmc[30] 3 xmc[31] 3 xmc[32] 3 xmc[33] 3 xmc[34] 3 xmc[35] 3 xmc[36] 3 xmc[37] 3 xmc[38] 3 Nc[3] 7 bc[3] 2 Mc[3] 2 xmaxc[3] 6 xmc[39] 3 xmc[40] 3 xmc[41] 3 xmc[42] 3 xmc[43] 3 xmc[44] 3 xmc[45] 3 xmc[46] 3 xmc[47] 3 xmc[48] 3 xmc[49] 3 xmc[50] 3 xmc[51] 3 ================================================ FILE: deps/pjsip/third_party/gsm/tls/sour2.dta ================================================ ; ; Copyright 1992 by Jutta Degener and Carsten Bormann, Technische ; Universitaet Berlin. See the accompanying file "COPYRIGHT" for ; details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. ; ; ; Variable Size g->chain 4 LARc[0] 6 LARc[1] 6 LARc[2] 5 LARc[3] 5 LARc[4] 4 LARc[5] 4 LARc[6] 3 LARc[7] 3 Nc[0] 7 bc[0] 2 Mc[0] 2 xmaxc[0] 6 xmc[0] 3 xmc[1] 3 xmc[2] 3 xmc[3] 3 xmc[4] 3 xmc[5] 3 xmc[6] 3 xmc[7] 3 xmc[8] 3 xmc[9] 3 xmc[10] 3 xmc[11] 3 xmc[12] 3 Nc[1] 7 bc[1] 2 Mc[1] 2 xmaxc[1] 6 xmc[13] 3 xmc[14] 3 xmc[15] 3 xmc[16] 3 xmc[17] 3 xmc[18] 3 xmc[19] 3 xmc[20] 3 xmc[21] 3 xmc[22] 3 xmc[23] 3 xmc[24] 3 xmc[25] 3 Nc[2] 7 bc[2] 2 Mc[2] 2 xmaxc[2] 6 xmc[26] 3 xmc[27] 3 xmc[28] 3 xmc[29] 3 xmc[30] 3 xmc[31] 3 xmc[32] 3 xmc[33] 3 xmc[34] 3 xmc[35] 3 xmc[36] 3 xmc[37] 3 xmc[38] 3 Nc[3] 7 bc[3] 2 Mc[3] 2 xmaxc[3] 6 xmc[39] 3 xmc[40] 3 xmc[41] 3 xmc[42] 3 xmc[43] 3 xmc[44] 3 xmc[45] 3 xmc[46] 3 xmc[47] 3 xmc[48] 3 xmc[49] 3 xmc[50] 3 xmc[51] 3 ================================================ FILE: deps/pjsip/third_party/gsm/tls/sweet.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/sweet.c,v 1.2 1996/07/02 10:15:53 jutta Exp $*/ /* Generate code to unpack a bit array from name:#bits description */ #include #include "taste.h" #include "proto.h" void write_code P2((s_spex, n_spex), struct spex * s_spex, int n_spex) { struct spex * sp = s_spex; int bits = 8; int vars; if (!n_spex) return; vars = sp->varsize; while (n_spex) { if (vars == sp->varsize) { printf("\t%s = ", sp->var); } else printf("\t%s |= ", sp->var); if (vars == bits) { if (bits == 8) printf( "*c++;\n" ); else printf( "*c++ & 0x%lX;\n", ~(0xfffffffe << (bits - 1)) ); if (!-- n_spex) break; sp++; vars = sp->varsize; bits = 8; } else if (vars < bits) { printf( "(*c >> %d) & 0x%lX;\n", bits - vars, ~(0xfffffffe << (vars - 1))); bits -= vars; if (!--n_spex) break; sp++; vars = sp->varsize; } else { /* vars > bits. We're eating lower-all of c, * but we must shift it. */ printf( "(*c++ & 0x%X) << %d;\n", ~(0xfffffffe << (bits - 1)), vars - bits ); vars -= bits; bits = 8; } } } ================================================ FILE: deps/pjsip/third_party/gsm/tls/taste.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/taste.c,v 1.1 1992/10/28 00:28:39 jutta Exp $*/ #include #include #include #include "config.h" #ifdef HAS_STDLIB_H # include #else #include "proto.h" # ifdef HAS_MALLOC_H # include # else extern char * malloc P((char *)), * realloc P((char *,int)); # endif extern int exit P((int)); #endif #include "proto.h" /* * common code to sweet.c and bitter.c: read the name:#bits description. */ #include "taste.h" static struct spex * s_spex; static int n_spex, m_spex; extern void write_code P((struct spex *, int)); char * strsave P1((str), char * str) /* strdup() + errors */ { int n = strlen(str) + 1; char * s = malloc(n); if (!s) { fprintf(stderr, "Failed to malloc %d bytes, abort\n", strlen(str) + 1); exit(1); } return memcpy(s, str, n); } struct spex * new_spex P0() { if (n_spex >= m_spex) { m_spex += 500; if (!(s_spex = (struct spex *)(n_spex ? realloc((char *)s_spex, m_spex * sizeof(*s_spex)) : malloc( m_spex * sizeof(*s_spex))))) { fprintf(stderr, "Failed to malloc %d bytes, abort\n", m_spex * sizeof(*s_spex)); exit(1); } } return s_spex + n_spex; } char * strtek P2((str, sep), char * str, char * sep) { static char * S = (char *)0; char * c, * base; if (str) S = str; if (!S || !*S) return (char *)0; /* Skip delimiters. */ while (*S) { for (c = sep; *c && *c != *S; c++) ; if (*c) *S++ = 0; else break; } base = S; /* Skip non-delimiters. */ for (base = S; *S; S++) { for (c = sep; *c; c++) if (*c == *S) { *S++ = 0; return base; } } return base == S ? (char *)0 : base; } int read_spex P0() { char buf[200]; char * s, *t; struct spex * sp = s_spex; while (fgets(buf, sizeof buf, stdin)) { char * nl; if (nl = strchr(buf, '\n')) *nl = '\0'; if (!*buf || *buf == ';') continue; s = strtek(buf, " \t"); if (!s) { fprintf(stderr, "? %s\n", buf); continue; } sp = new_spex(); sp->var = strsave(s); s = strtek((char*)0, " \t"); if (!s) { fprintf(stderr, "varsize?\n"); continue; } sp->varsize = strtol(s, (char **)0, 0); n_spex++; } return sp - s_spex; } int main P0() { read_spex(); write_code(s_spex, n_spex); exit(0); } ================================================ FILE: deps/pjsip/third_party/gsm/tls/taste.h ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /* * common code to sweet.c and bitter.c */ #ifndef TASTE_H #define TASTE_H struct spex { char * var; int varsize; } ; #endif /* TASTE_H */ ================================================ FILE: deps/pjsip/third_party/gsm/tst/cod2lin.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/cod2lin.c,v 1.2 1996/07/02 14:33:10 jutta Exp jutta $*/ #include #include #include "gsm.h" #include "proto.h" char * pname; int debug = 0; int verbosity = 0; int fast = 0; int wav = 0; int error = 0; usage P0() { fprintf(stderr, "Usage: %s [-vwF] [files...]\n", pname); exit(1); } void process P2((f, filename), FILE * f, char * filename) { gsm_frame buf; gsm_signal source[160]; int cc; gsm r; (void)memset(source, 0x00, sizeof(source)); if (!(r = gsm_create())) { perror("gsm_create"); error = 1; return ; } gsm_option(r, GSM_OPT_VERBOSE, &verbosity); gsm_option(r, GSM_OPT_FAST, &fast); gsm_option(r, GSM_OPT_WAV49, &wav); for (;;) { cc = fread((char *)source, sizeof(*source), 76, f); if (cc == 0) { gsm_destroy(r); return; } if (cc != 76) { error = 1; fprintf(stderr, "%s: %s -- %d trailing bytes ignored\n", pname, filename, cc); gsm_destroy(r); return; } gsm_implode(r, source, buf); gsm_decode(r, buf, source); if (write(1, source, sizeof(source)) != sizeof(source)) { perror("write"); error = 1; gsm_destroy(r); return; } } } main P2((ac, av), int ac, char ** av) { int opt; extern char * optarg; extern int optind; FILE * f; if (!(pname = av[0])) pname = "cod2out"; while ((opt = getopt(ac, av, "vwF")) != EOF) switch (opt) { case 'v': verbosity++; break; case 'w': wav++; break; case 'F': fast++; break; default: usage(); } ac -= optind; av += optind; if (!ac) process(stdin, "*stdin*"); else for (; *av; av++) { if (!(f = fopen(*av, "r"))) perror(*av); else { process(f, *av); fclose(f); } } exit(error); } ================================================ FILE: deps/pjsip/third_party/gsm/tst/cod2txt.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/cod2txt.c,v 1.1 1994/10/21 20:52:11 jutta Exp $*/ #include #include #include "gsm.h" #include "proto.h" char * pname; int debug = 0; int verbosity = 0; int error = 0; usage P0() { fprintf(stderr, "Usage: %s [files...]\n", pname); exit(1); } void process P2((f, filename), FILE * f, char * filename) { gsm_frame buf; gsm_signal source[160]; int cc; gsm r; int nr=0; (void)memset(source, 0, sizeof(source)); if (!(r = gsm_create())) { perror("gsm_create"); error = 1; return ; } gsm_option(r, GSM_OPT_VERBOSE, &verbosity); for (;;) { cc = fread((char *)source, sizeof(*source), 76, f); if (cc == 0) { gsm_destroy(r); return; } if (cc != 76) { error = 1; fprintf(stderr, "%s: %s -- %d trailing bytes ignored\n", pname, filename, cc); gsm_destroy(r); return; } gsm_implode(r, source, buf); printf("[%d] ", ++nr); if (gsm_print(stdout, r, buf)) { fprintf(stderr, "%s: %s: bad magic\n", pname, filename); gsm_destroy(r); return; } } } main P2((ac, av), int ac, char ** av) { int opt; extern char * optarg; extern int optind; FILE * f; if (!(pname = av[0])) pname = "cod2txt"; ac--; av++; if (!ac) process(stdin, "*stdin*"); else for (; *av; av++) { if (!(f = fopen(*av, "r"))) perror(*av); else { process(f, *av); fclose(f); } } exit(error); } ================================================ FILE: deps/pjsip/third_party/gsm/tst/gsm2cod.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm2cod.c,v 1.1 1994/10/21 20:52:11 jutta Exp $*/ #include #include #include "gsm.h" #include "proto.h" char * pname; int debug = 0; int verbosity = 0; int error = 0; usage P0() { fprintf(stderr, "Usage: %s [files...]\n", pname); exit(1); } void process P2((f, filename), FILE * f, char * filename) { gsm_frame buf; gsm_signal source[76]; int cc; gsm r; int nr=0; (void)memset(source, 0, sizeof(source)); if (!(r = gsm_create())) { perror("gsm_create"); error = 1; return ; } gsm_option(r, GSM_OPT_VERBOSE, &verbosity); for (;;) { cc = fread((char *)buf, sizeof(buf), 1, f); if (cc == 0) { gsm_destroy(r); return; } if (cc != 1) { error = 1; fprintf(stderr, "%s: %s -- trailing bytes ignored\n", pname, filename); gsm_destroy(r); return; } gsm_explode(r, buf, source); if (write(1, (char *)source, sizeof(source))!= sizeof(source)) { perror("write"); error = 1; gsm_destroy(r); return; } } } main P2((ac, av), int ac, char ** av) { int opt; extern char * optarg; extern int optind; FILE * f; if (!(pname = av[0])) pname = "gsm2cod"; ac--; av++; if (!ac) process(stdin, "*stdin*"); else for (; *av; av++) { if (!(f = fopen(*av, "r"))) perror(*av); else { process(f, *av); fclose(f); } } exit(error); } ================================================ FILE: deps/pjsip/third_party/gsm/tst/lin2cod.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/lin2cod.c,v 1.2 1996/07/02 14:33:13 jutta Exp jutta $*/ #include #include "gsm.h" #include "proto.h" char * pname; int debug = 0; int verbosity = 0; int fast = 0; int wav = 0; int error = 0; usage P0() { fprintf(stderr, "Usage: %s [-vwF] [files...]\n", pname); exit(1); } void process P2((f, filename), FILE * f, char * filename) { gsm_frame buf; short source[160]; int cc; gsm r; if (!(r = gsm_create())) { perror("gsm_create"); error = 1; return ; } gsm_option(r, GSM_OPT_VERBOSE, &verbosity); gsm_option(r, GSM_OPT_FAST, &fast); gsm_option(r, GSM_OPT_WAV49, &wav); for (;;) { if ((cc = fread((char *)source, 1, sizeof(source), f)) == 0) { gsm_destroy(r); #ifdef COUNT_OVERFLOW dump_overflow(stderr); #endif return; } if (cc != sizeof(source)) { error = 1; perror(filename); fprintf(stderr, "%s: cannot read input from %s\n", pname, filename); gsm_destroy(r); return; } gsm_encode(r, source, buf); gsm_explode(r, buf, source); /* 76 shorts */ if (write(1, source, sizeof(*source) * 76) != sizeof(*source) * 76) { perror("write"); error = 1; gsm_destroy(r); return; } } } main P2((ac, av), int ac, char ** av) { int opt; extern char * optarg; extern int optind; FILE * f; if (!(pname = av[0])) pname = "inp2cod"; while ((opt = getopt(ac, av, "vwF")) != EOF) switch (opt) { case 'v': verbosity++; break; case 'w': wav++; break; case 'F': fast++; break; default: usage(); } ac -= optind; av += optind; if (!ac) process(stdin, "*stdin*"); else for (; *av; av++) { if (!(f = fopen(*av, "r"))) perror(*av); else { process(f, *av); fclose(f); } } exit(error); } ================================================ FILE: deps/pjsip/third_party/gsm/tst/lin2txt.c ================================================ /* * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische * Universitaet Berlin. See the accompanying file "COPYRIGHT" for * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. */ /*$Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/lin2txt.c,v 1.1 1994/10/21 20:52:11 jutta Exp $*/ #include #include "gsm.h" #include "proto.h" char * pname; int debug = 0; int verbosity = 0; int error = 0; usage P0() { fprintf(stderr, "Usage: %s [-v] [files...]\n", pname); exit(1); } void process P2((f, filename), FILE * f, char * filename) { short source[160]; int cc, j, k; gsm r; if (!(r = gsm_create())) { perror("gsm_create"); error = 1; return ; } gsm_option(r, GSM_OPT_VERBOSE, &verbosity); for (;;) { if ((cc = fread((char *)source, 1, sizeof(source), f)) == 0) { gsm_destroy(r); #ifdef COUNT_OVERFLOW dump_overflow(stderr); #endif return; } printf("{\t"); for (j = 0; j < 4; j++) { printf("{\t"); for (k = 0; k < 40; k++) { printf("%d", (int)source[ j * 40 + k ]); if (k < 39) { printf(", "); if (k % 4 == 3) printf("\n\t\t"); } else { printf("\t}"); if (j == 3) printf("\t},\n"); else printf(",\n\t"); } } } } } main P2((ac, av), int ac, char ** av) { int opt; extern char * optarg; extern int optind; FILE * f; if (!(pname = av[0])) pname = "inp2txt"; while ((opt = getopt(ac, av, "v")) != EOF) switch (opt) { case 'v': verbosity++; break; default: usage(); } ac -= optind; av += optind; if (!ac) process(stdin, "*stdin*"); else for (; *av; av++) { if (!(f = fopen(*av, "r"))) perror(*av); else { process(f, *av); fclose(f); } } exit(error); } ================================================ FILE: deps/pjsip/third_party/gsm/tst/run ================================================ : # # Copyright 1992 by Jutta Degener and Carsten Bormann, Technische # Universitaet Berlin. See the accompanying file "COPYRIGHT" for # details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. # if [ ! -f 1.inp ] ; then echo Sorry, but we cannot provide the test data with this release. exit fi echo -n 'Linear to code: ' for i in 1 2 3 4 do echo -n $i.. ./lin2cod < $i.inp | cmp - $i.cod done echo "" echo -n 'Code to linear: ' for i in 1 2 3 4 do echo -n $i.. ./cod2lin < $i.cod | cmp - $i.out done echo "" echo -n 'Toast: ' for i in 1 2 3 4 do echo -n $i.. ../bin/toast -l < $i.inp | ../bin/toast -dl | cmp - $i.out done echo "" ================================================ FILE: deps/pjsip/third_party/ilbc/FrameClassify.c ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code FrameClassify.c Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #include "iLBC_define.h" /*---------------------------------------------------------------* * Classification of subframes to localize start state *--------------------------------------------------------------*/ int FrameClassify( /* index to the max-energy sub-frame */ iLBC_Enc_Inst_t *iLBCenc_inst, /* (i/o) the encoder state structure */ float *residual /* (i) lpc residual signal */ ) { float max_ssqEn, fssqEn[NSUB_MAX], bssqEn[NSUB_MAX], *pp; int n, l, max_ssqEn_n; const float ssqEn_win[NSUB_MAX-1]={(float)0.8,(float)0.9, (float)1.0,(float)0.9,(float)0.8}; const float sampEn_win[5]={(float)1.0/(float)6.0, (float)2.0/(float)6.0, (float)3.0/(float)6.0, (float)4.0/(float)6.0, (float)5.0/(float)6.0}; /* init the front and back energies to zero */ memset(fssqEn, 0, NSUB_MAX*sizeof(float)); memset(bssqEn, 0, NSUB_MAX*sizeof(float)); /* Calculate front of first seqence */ n=0; pp=residual; for (l=0; l<5; l++) { fssqEn[n] += sampEn_win[l] * (*pp) * (*pp); pp++; } for (l=5; lnsub-1; n++) { pp=residual+n*SUBL; for (l=0; l<5; l++) { fssqEn[n] += sampEn_win[l] * (*pp) * (*pp); bssqEn[n] += (*pp) * (*pp); pp++; } for (l=5; lnsub-1; pp=residual+n*SUBL; for (l=0; lmode==20) l=1; else l=0; max_ssqEn=(fssqEn[0]+bssqEn[1])*ssqEn_win[l]; max_ssqEn_n=1; for (n=2; nnsub; n++) { l++; if ((fssqEn[n-1]+bssqEn[n])*ssqEn_win[l] > max_ssqEn) { max_ssqEn=(fssqEn[n-1]+bssqEn[n]) * ssqEn_win[l]; max_ssqEn_n=n; } } return max_ssqEn_n; } ================================================ FILE: deps/pjsip/third_party/ilbc/FrameClassify.h ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code FrameClassify.h Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #ifndef __iLBC_FRAMECLASSIFY_H #define __iLBC_FRAMECLASSIFY_H int FrameClassify( /* index to the max-energy sub-frame */ iLBC_Enc_Inst_t *iLBCenc_inst, /* (i/o) the encoder state structure */ float *residual /* (i) lpc residual signal */ ); #endif ================================================ FILE: deps/pjsip/third_party/ilbc/LPCdecode.c ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code LPC_decode.c Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #include #include #include "helpfun.h" #include "lsf.h" #include "iLBC_define.h" #include "constants.h" /*---------------------------------------------------------------* * interpolation of lsf coefficients for the decoder *--------------------------------------------------------------*/ void LSFinterpolate2a_dec( float *a, /* (o) lpc coefficients for a sub-frame */ float *lsf1, /* (i) first lsf coefficient vector */ float *lsf2, /* (i) second lsf coefficient vector */ float coef, /* (i) interpolation weight */ int length /* (i) length of lsf vectors */ ){ float lsftmp[LPC_FILTERORDER]; interpolate(lsftmp, lsf1, lsf2, coef, length); lsf2a(a, lsftmp); } /*---------------------------------------------------------------* * obtain dequantized lsf coefficients from quantization index *--------------------------------------------------------------*/ void SimplelsfDEQ( float *lsfdeq, /* (o) dequantized lsf coefficients */ int *index, /* (i) quantization index */ int lpc_n /* (i) number of LPCs */ ){ int i, j, pos, cb_pos; /* decode first LSF */ pos = 0; cb_pos = 0; for (i = 0; i < LSF_NSPLIT; i++) { for (j = 0; j < dim_lsfCbTbl[i]; j++) { lsfdeq[pos + j] = lsfCbTbl[cb_pos + (long)(index[i])*dim_lsfCbTbl[i] + j]; } pos += dim_lsfCbTbl[i]; cb_pos += size_lsfCbTbl[i]*dim_lsfCbTbl[i]; } if (lpc_n>1) { /* decode last LSF */ pos = 0; cb_pos = 0; for (i = 0; i < LSF_NSPLIT; i++) { for (j = 0; j < dim_lsfCbTbl[i]; j++) { lsfdeq[LPC_FILTERORDER + pos + j] = lsfCbTbl[cb_pos + (long)(index[LSF_NSPLIT + i])* dim_lsfCbTbl[i] + j]; } pos += dim_lsfCbTbl[i]; cb_pos += size_lsfCbTbl[i]*dim_lsfCbTbl[i]; } } } /*----------------------------------------------------------------* * obtain synthesis and weighting filters form lsf coefficients *---------------------------------------------------------------*/ void DecoderInterpolateLSF( float *syntdenum, /* (o) synthesis filter coefficients */ float *weightdenum, /* (o) weighting denumerator coefficients */ float *lsfdeq, /* (i) dequantized lsf coefficients */ int length, /* (i) length of lsf coefficient vector */ iLBC_Dec_Inst_t *iLBCdec_inst /* (i) the decoder state structure */ ){ int i, pos, lp_length; float lp[LPC_FILTERORDER + 1], *lsfdeq2; lsfdeq2 = lsfdeq + length; lp_length = length + 1; if (iLBCdec_inst->mode==30) { /* sub-frame 1: Interpolation between old and first */ LSFinterpolate2a_dec(lp, iLBCdec_inst->lsfdeqold, lsfdeq, lsf_weightTbl_30ms[0], length); memcpy(syntdenum,lp,lp_length*sizeof(float)); bwexpand(weightdenum, lp, LPC_CHIRP_WEIGHTDENUM, lp_length); /* sub-frames 2 to 6: interpolation between first and last LSF */ pos = lp_length; for (i = 1; i < 6; i++) { LSFinterpolate2a_dec(lp, lsfdeq, lsfdeq2, lsf_weightTbl_30ms[i], length); memcpy(syntdenum + pos,lp,lp_length*sizeof(float)); bwexpand(weightdenum + pos, lp, LPC_CHIRP_WEIGHTDENUM, lp_length); pos += lp_length; } } else { pos = 0; for (i = 0; i < iLBCdec_inst->nsub; i++) { LSFinterpolate2a_dec(lp, iLBCdec_inst->lsfdeqold, lsfdeq, lsf_weightTbl_20ms[i], length); memcpy(syntdenum+pos,lp,lp_length*sizeof(float)); bwexpand(weightdenum+pos, lp, LPC_CHIRP_WEIGHTDENUM, lp_length); pos += lp_length; } } /* update memory */ if (iLBCdec_inst->mode==30) memcpy(iLBCdec_inst->lsfdeqold, lsfdeq2, length*sizeof(float)); else memcpy(iLBCdec_inst->lsfdeqold, lsfdeq, length*sizeof(float)); } ================================================ FILE: deps/pjsip/third_party/ilbc/LPCdecode.h ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code LPC_decode.h Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #ifndef __iLBC_LPC_DECODE_H #define __iLBC_LPC_DECODE_H void LSFinterpolate2a_dec( float *a, /* (o) lpc coefficients for a sub-frame */ float *lsf1, /* (i) first lsf coefficient vector */ float *lsf2, /* (i) second lsf coefficient vector */ float coef, /* (i) interpolation weight */ int length /* (i) length of lsf vectors */ ); void SimplelsfDEQ( float *lsfdeq, /* (o) dequantized lsf coefficients */ int *index, /* (i) quantization index */ int lpc_n /* (i) number of LPCs */ ); void DecoderInterpolateLSF( float *syntdenum, /* (o) synthesis filter coefficients */ float *weightdenum, /* (o) weighting denumerator coefficients */ float *lsfdeq, /* (i) dequantized lsf coefficients */ int length, /* (i) length of lsf coefficient vector */ iLBC_Dec_Inst_t *iLBCdec_inst /* (i) the decoder state structure */ ); #endif ================================================ FILE: deps/pjsip/third_party/ilbc/LPCencode.c ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code LPCencode.c Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #include #include "iLBC_define.h" #include "helpfun.h" #include "lsf.h" #include "constants.h" /*----------------------------------------------------------------* * lpc analysis (subrutine to LPCencode) *---------------------------------------------------------------*/ void SimpleAnalysis( float *lsf, /* (o) lsf coefficients */ float *data, /* (i) new data vector */ iLBC_Enc_Inst_t *iLBCenc_inst /* (i/o) the encoder state structure */ ){ int k, is; float temp[BLOCKL_MAX], lp[LPC_FILTERORDER + 1]; float lp2[LPC_FILTERORDER + 1]; float r[LPC_FILTERORDER + 1]; is=LPC_LOOKBACK+BLOCKL_MAX-iLBCenc_inst->blockl; memcpy(iLBCenc_inst->lpc_buffer+is,data, iLBCenc_inst->blockl*sizeof(float)); /* No lookahead, last window is asymmetric */ for (k = 0; k < iLBCenc_inst->lpc_n; k++) { is = LPC_LOOKBACK; if (k < (iLBCenc_inst->lpc_n - 1)) { window(temp, lpc_winTbl, iLBCenc_inst->lpc_buffer, BLOCKL_MAX); } else { window(temp, lpc_asymwinTbl, iLBCenc_inst->lpc_buffer + is, BLOCKL_MAX); } autocorr(r, temp, BLOCKL_MAX, LPC_FILTERORDER); window(r, r, lpc_lagwinTbl, LPC_FILTERORDER + 1); levdurb(lp, temp, r, LPC_FILTERORDER); bwexpand(lp2, lp, LPC_CHIRP_SYNTDENUM, LPC_FILTERORDER+1); a2lsf(lsf + k*LPC_FILTERORDER, lp2); } is=LPC_LOOKBACK+BLOCKL_MAX-iLBCenc_inst->blockl; memmove(iLBCenc_inst->lpc_buffer, iLBCenc_inst->lpc_buffer+LPC_LOOKBACK+BLOCKL_MAX-is, is*sizeof(float)); } /*----------------------------------------------------------------* * lsf interpolator and conversion from lsf to a coefficients * (subrutine to SimpleInterpolateLSF) *---------------------------------------------------------------*/ void LSFinterpolate2a_enc( float *a, /* (o) lpc coefficients */ float *lsf1,/* (i) first set of lsf coefficients */ float *lsf2,/* (i) second set of lsf coefficients */ float coef, /* (i) weighting coefficient to use between lsf1 and lsf2 */ long length /* (i) length of coefficient vectors */ ){ float lsftmp[LPC_FILTERORDER]; interpolate(lsftmp, lsf1, lsf2, coef, length); lsf2a(a, lsftmp); } /*----------------------------------------------------------------* * lsf interpolator (subrutine to LPCencode) *---------------------------------------------------------------*/ void SimpleInterpolateLSF( float *syntdenum, /* (o) the synthesis filter denominator resulting from the quantized interpolated lsf */ float *weightdenum, /* (o) the weighting filter denominator resulting from the unquantized interpolated lsf */ float *lsf, /* (i) the unquantized lsf coefficients */ float *lsfdeq, /* (i) the dequantized lsf coefficients */ float *lsfold, /* (i) the unquantized lsf coefficients of the previous signal frame */ float *lsfdeqold, /* (i) the dequantized lsf coefficients of the previous signal frame */ int length, /* (i) should equate LPC_FILTERORDER */ iLBC_Enc_Inst_t *iLBCenc_inst /* (i/o) the encoder state structure */ ){ int i, pos, lp_length; float lp[LPC_FILTERORDER + 1], *lsf2, *lsfdeq2; lsf2 = lsf + length; lsfdeq2 = lsfdeq + length; lp_length = length + 1; if (iLBCenc_inst->mode==30) { /* sub-frame 1: Interpolation between old and first set of lsf coefficients */ LSFinterpolate2a_enc(lp, lsfdeqold, lsfdeq, lsf_weightTbl_30ms[0], length); memcpy(syntdenum,lp,lp_length*sizeof(float)); LSFinterpolate2a_enc(lp, lsfold, lsf, lsf_weightTbl_30ms[0], length); bwexpand(weightdenum, lp, LPC_CHIRP_WEIGHTDENUM, lp_length); /* sub-frame 2 to 6: Interpolation between first and second set of lsf coefficients */ pos = lp_length; for (i = 1; i < iLBCenc_inst->nsub; i++) { LSFinterpolate2a_enc(lp, lsfdeq, lsfdeq2, lsf_weightTbl_30ms[i], length); memcpy(syntdenum + pos,lp,lp_length*sizeof(float)); LSFinterpolate2a_enc(lp, lsf, lsf2, lsf_weightTbl_30ms[i], length); bwexpand(weightdenum + pos, lp, LPC_CHIRP_WEIGHTDENUM, lp_length); pos += lp_length; } } else { pos = 0; for (i = 0; i < iLBCenc_inst->nsub; i++) { LSFinterpolate2a_enc(lp, lsfdeqold, lsfdeq, lsf_weightTbl_20ms[i], length); memcpy(syntdenum+pos,lp,lp_length*sizeof(float)); LSFinterpolate2a_enc(lp, lsfold, lsf, lsf_weightTbl_20ms[i], length); bwexpand(weightdenum+pos, lp, LPC_CHIRP_WEIGHTDENUM, lp_length); pos += lp_length; } } /* update memory */ if (iLBCenc_inst->mode==30) { memcpy(lsfold, lsf2, length*sizeof(float)); memcpy(lsfdeqold, lsfdeq2, length*sizeof(float)); } else { memcpy(lsfold, lsf, length*sizeof(float)); memcpy(lsfdeqold, lsfdeq, length*sizeof(float)); } } /*----------------------------------------------------------------* * lsf quantizer (subrutine to LPCencode) *---------------------------------------------------------------*/ void SimplelsfQ( float *lsfdeq, /* (o) dequantized lsf coefficients (dimension FILTERORDER) */ int *index, /* (o) quantization index */ float *lsf, /* (i) the lsf coefficient vector to be quantized (dimension FILTERORDER ) */ int lpc_n /* (i) number of lsf sets to quantize */ ){ /* Quantize first LSF with memoryless split VQ */ SplitVQ(lsfdeq, index, lsf, lsfCbTbl, LSF_NSPLIT, dim_lsfCbTbl, size_lsfCbTbl); if (lpc_n==2) { /* Quantize second LSF with memoryless split VQ */ SplitVQ(lsfdeq + LPC_FILTERORDER, index + LSF_NSPLIT, lsf + LPC_FILTERORDER, lsfCbTbl, LSF_NSPLIT, dim_lsfCbTbl, size_lsfCbTbl); } } /*----------------------------------------------------------------* * lpc encoder *---------------------------------------------------------------*/ void LPCencode( float *syntdenum, /* (i/o) synthesis filter coefficients before/after encoding */ float *weightdenum, /* (i/o) weighting denumerator coefficients before/after encoding */ int *lsf_index, /* (o) lsf quantization index */ float *data, /* (i) lsf coefficients to quantize */ iLBC_Enc_Inst_t *iLBCenc_inst /* (i/o) the encoder state structure */ ){ float lsf[LPC_FILTERORDER * LPC_N_MAX]; float lsfdeq[LPC_FILTERORDER * LPC_N_MAX]; SimpleAnalysis(lsf, data, iLBCenc_inst); SimplelsfQ(lsfdeq, lsf_index, lsf, iLBCenc_inst->lpc_n); LSF_check(lsfdeq, LPC_FILTERORDER, iLBCenc_inst->lpc_n); SimpleInterpolateLSF(syntdenum, weightdenum, lsf, lsfdeq, iLBCenc_inst->lsfold, iLBCenc_inst->lsfdeqold, LPC_FILTERORDER, iLBCenc_inst); } ================================================ FILE: deps/pjsip/third_party/ilbc/LPCencode.h ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code LPCencode.h Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #ifndef __iLBC_LPCENCOD_H #define __iLBC_LPCENCOD_H void LPCencode( float *syntdenum, /* (i/o) synthesis filter coefficients before/after encoding */ float *weightdenum, /* (i/o) weighting denumerator coefficients before/after encoding */ int *lsf_index, /* (o) lsf quantization index */ float *data, /* (i) lsf coefficients to quantize */ iLBC_Enc_Inst_t *iLBCenc_inst /* (i/o) the encoder state structure */ ); #endif ================================================ FILE: deps/pjsip/third_party/ilbc/StateConstructW.c ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code StateConstructW.c Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #include #include #include "iLBC_define.h" #include "constants.h" #include "filter.h" /*----------------------------------------------------------------* * decoding of the start state *---------------------------------------------------------------*/ void StateConstructW( int idxForMax, /* (i) 6-bit index for the quantization of max amplitude */ int *idxVec, /* (i) vector of quantization indexes */ float *syntDenum, /* (i) synthesis filter denumerator */ float *out, /* (o) the decoded state vector */ int len /* (i) length of a state vector */ ){ float maxVal, tmpbuf[LPC_FILTERORDER+2*STATE_LEN], *tmp, numerator[LPC_FILTERORDER+1]; float foutbuf[LPC_FILTERORDER+2*STATE_LEN], *fout; int k,tmpi; /* decoding of the maximum value */ maxVal = state_frgqTbl[idxForMax]; maxVal = (float)pow(10,maxVal)/(float)4.5; /* initialization of buffers and coefficients */ memset(tmpbuf, 0, LPC_FILTERORDER*sizeof(float)); memset(foutbuf, 0, LPC_FILTERORDER*sizeof(float)); for (k=0; k #include #include "iLBC_define.h" #include "constants.h" #include "filter.h" #include "helpfun.h" /*----------------------------------------------------------------* * predictive noise shaping encoding of scaled start state * (subrutine for StateSearchW) *---------------------------------------------------------------*/ void AbsQuantW( iLBC_Enc_Inst_t *iLBCenc_inst, /* (i) Encoder instance */ float *in, /* (i) vector to encode */ float *syntDenum, /* (i) denominator of synthesis filter */ float *weightDenum, /* (i) denominator of weighting filter */ int *out, /* (o) vector of quantizer indexes */ int len, /* (i) length of vector to encode and vector of quantizer indexes */ int state_first /* (i) position of start state in the 80 vec */ ){ float *syntOut; float syntOutBuf[LPC_FILTERORDER+STATE_SHORT_LEN_30MS]; float toQ, xq; int n; int index; /* initialization of buffer for filtering */ memset(syntOutBuf, 0, LPC_FILTERORDER*sizeof(float)); /* initialization of pointer for filtering */ syntOut = &syntOutBuf[LPC_FILTERORDER]; /* synthesis and weighting filters on input */ if (state_first) { AllPoleFilter (in, weightDenum, SUBL, LPC_FILTERORDER); } else { AllPoleFilter (in, weightDenum, iLBCenc_inst->state_short_len-SUBL, LPC_FILTERORDER); } /* encoding loop */ for (n=0; nstate_short_len-SUBL))) { syntDenum += (LPC_FILTERORDER+1); weightDenum += (LPC_FILTERORDER+1); /* synthesis and weighting filters on input */ AllPoleFilter (&in[n], weightDenum, len-n, LPC_FILTERORDER); } /* prediction of synthesized and weighted input */ syntOut[n] = 0.0; AllPoleFilter (&syntOut[n], weightDenum, 1, LPC_FILTERORDER); /* quantization */ toQ = in[n]-syntOut[n]; sort_sq(&xq, &index, toQ, state_sq3Tbl, 8); out[n]=index; syntOut[n] = state_sq3Tbl[out[n]]; /* update of the prediction filter */ AllPoleFilter(&syntOut[n], weightDenum, 1, LPC_FILTERORDER); } } /*----------------------------------------------------------------* * encoding of start state *---------------------------------------------------------------*/ void StateSearchW( iLBC_Enc_Inst_t *iLBCenc_inst, /* (i) Encoder instance */ float *residual,/* (i) target residual vector */ float *syntDenum, /* (i) lpc synthesis filter */ float *weightDenum, /* (i) weighting filter denuminator */ int *idxForMax, /* (o) quantizer index for maximum amplitude */ int *idxVec, /* (o) vector of quantization indexes */ int len, /* (i) length of all vectors */ int state_first /* (i) position of start state in the 80 vec */ ){ float dtmp, maxVal; float tmpbuf[LPC_FILTERORDER+2*STATE_SHORT_LEN_30MS]; float *tmp, numerator[1+LPC_FILTERORDER]; float foutbuf[LPC_FILTERORDER+2*STATE_SHORT_LEN_30MS], *fout; int k; float qmax, scal; /* initialization of buffers and filter coefficients */ memset(tmpbuf, 0, LPC_FILTERORDER*sizeof(float)); memset(foutbuf, 0, LPC_FILTERORDER*sizeof(float)); for (k=0; k maxVal*maxVal){ maxVal = fout[k]; } } maxVal=(float)fabs(maxVal); /* encoding of the maximum amplitude value */ if (maxVal < 10.0) { maxVal = 10.0; } maxVal = (float)log10(maxVal); sort_sq(&dtmp, idxForMax, maxVal, state_frgqTbl, 64); /* decoding of the maximum amplitude representation value, and corresponding scaling of start state */ maxVal=state_frgqTbl[*idxForMax]; qmax = (float)pow(10,maxVal); scal = (float)(4.5)/qmax; for (k=0; k #include "iLBC_define.h" /*----------------------------------------------------------------* * LP analysis filter. *---------------------------------------------------------------*/ void anaFilter( float *In, /* (i) Signal to be filtered */ float *a, /* (i) LP parameters */ int len,/* (i) Length of signal */ float *Out, /* (o) Filtered signal */ float *mem /* (i/o) Filter state */ ){ int i, j; float *po, *pi, *pm, *pa; po = Out; /* Filter first part using memory from past */ for (i=0; i #include /*----------------------------------------------------------------* * Construct an additional codebook vector by filtering the * initial codebook buffer. This vector is then used to expand * the codebook with an additional section. *---------------------------------------------------------------*/ void filteredCBvecs( float *cbvectors, /* (o) Codebook vectors for the higher section */ float *mem, /* (i) Buffer to create codebook vector from */ int lMem /* (i) Length of buffer */ ){ int j, k; float *pp, *pp1; float tempbuff2[CB_MEML+CB_FILTERLEN]; float *pos; memset(tempbuff2, 0, (CB_HALFFILTERLEN-1)*sizeof(float)); memcpy(&tempbuff2[CB_HALFFILTERLEN-1], mem, lMem*sizeof(float)); memset(&tempbuff2[lMem+CB_HALFFILTERLEN-1], 0, (CB_HALFFILTERLEN+1)*sizeof(float)); /* Create codebook vector for higher section by filtering */ /* do filtering */ pos=cbvectors; memset(pos, 0, lMem*sizeof(float)); for (k=0; k0.0) { invenergy[tmpIndex]=(float)1.0/(energy[tmpIndex]+EPS); } else { invenergy[tmpIndex] = (float) 0.0; } if (stage==0) { measure = (float)-10000000.0; if (crossDot > 0.0) { measure = crossDot*crossDot*invenergy[tmpIndex]; } } else { measure = crossDot*crossDot*invenergy[tmpIndex]; } /* check if measure is better */ ftmp = crossDot*invenergy[tmpIndex]; if ((measure>*max_measure) && (fabs(ftmp) #include #include #include "iLBC_define.h" /*----------------------------------------------------------------* * Compute cross correlation and pitch gain for pitch prediction * of last subframe at given lag. *---------------------------------------------------------------*/ void compCorr( float *cc, /* (o) cross correlation coefficient */ float *gc, /* (o) gain */ float *pm, float *buffer, /* (i) signal buffer */ int lag, /* (i) pitch lag */ int bLen, /* (i) length of buffer */ int sRange /* (i) correlation search length */ ){ int i; float ftmp1, ftmp2, ftmp3; /* Guard against getting outside buffer */ if ((bLen-sRange-lag)<0) { sRange=bLen-lag; } ftmp1 = 0.0; ftmp2 = 0.0; ftmp3 = 0.0; for (i=0; i 0.0) { *cc = ftmp1*ftmp1/ftmp2; *gc = (float)fabs(ftmp1/ftmp2); *pm=(float)fabs(ftmp1)/ ((float)sqrt(ftmp2)*(float)sqrt(ftmp3)); } else { *cc = 0.0; *gc = 0.0; *pm=0.0; } } /*----------------------------------------------------------------* * Packet loss concealment routine. Conceals a residual signal * and LP parameters. If no packet loss, update state. *---------------------------------------------------------------*/ void doThePLC( float *PLCresidual, /* (o) concealed residual */ float *PLClpc, /* (o) concealed LP parameters */ int PLI, /* (i) packet loss indicator 0 - no PL, 1 = PL */ float *decresidual, /* (i) decoded residual */ float *lpc, /* (i) decoded LPC (only used for no PL) */ int inlag, /* (i) pitch lag */ iLBC_Dec_Inst_t *iLBCdec_inst /* (i/o) decoder instance */ ){ int lag=20, randlag; float gain, maxcc; float use_gain; float gain_comp, maxcc_comp, per, max_per=0; int i, pick, use_lag; float ftmp, randvec[BLOCKL_MAX], pitchfact, energy; /* Packet Loss */ if (PLI == 1) { iLBCdec_inst->consPLICount += 1; /* if previous frame not lost, determine pitch pred. gain */ if (iLBCdec_inst->prevPLI != 1) { /* Search around the previous lag to find the best pitch period */ lag=inlag-3; compCorr(&maxcc, &gain, &max_per, iLBCdec_inst->prevResidual, lag, iLBCdec_inst->blockl, 60); for (i=inlag-2;i<=inlag+3;i++) { compCorr(&maxcc_comp, &gain_comp, &per, iLBCdec_inst->prevResidual, i, iLBCdec_inst->blockl, 60); if (maxcc_comp>maxcc) { maxcc=maxcc_comp; gain=gain_comp; lag=i; max_per=per; } } } /* previous frame lost, use recorded lag and periodicity */ else { lag=iLBCdec_inst->prevLag; max_per=iLBCdec_inst->per; } /* downscaling */ use_gain=1.0; if (iLBCdec_inst->consPLICount*iLBCdec_inst->blockl>320) use_gain=(float)0.9; else if (iLBCdec_inst->consPLICount* iLBCdec_inst->blockl>2*320) use_gain=(float)0.7; else if (iLBCdec_inst->consPLICount* iLBCdec_inst->blockl>3*320) use_gain=(float)0.5; else if (iLBCdec_inst->consPLICount* iLBCdec_inst->blockl>4*320) use_gain=(float)0.0; /* mix noise and pitch repeatition */ ftmp=(float)sqrt(max_per); if (ftmp>(float)0.7) pitchfact=(float)1.0; else if (ftmp>(float)0.4) pitchfact=(ftmp-(float)0.4)/((float)0.7-(float)0.4); else pitchfact=0.0; /* avoid repetition of same pitch cycle */ use_lag=lag; if (lag<80) { use_lag=2*lag; } /* compute concealed residual */ energy = 0.0; for (i=0; iblockl; i++) { /* noise component */ iLBCdec_inst->seed=(iLBCdec_inst->seed*69069L+1) & (0x80000000L-1); randlag = 50 + ((signed long) iLBCdec_inst->seed)%70; pick = i - randlag; if (pick < 0) { randvec[i] = iLBCdec_inst->prevResidual[ iLBCdec_inst->blockl+pick]; } else { randvec[i] = randvec[pick]; } /* pitch repeatition component */ pick = i - use_lag; if (pick < 0) { PLCresidual[i] = iLBCdec_inst->prevResidual[ iLBCdec_inst->blockl+pick]; } else { PLCresidual[i] = PLCresidual[pick]; } /* mix random and periodicity component */ if (i<80) PLCresidual[i] = use_gain*(pitchfact * PLCresidual[i] + ((float)1.0 - pitchfact) * randvec[i]); else if (i<160) PLCresidual[i] = (float)0.95*use_gain*(pitchfact * PLCresidual[i] + ((float)1.0 - pitchfact) * randvec[i]); else PLCresidual[i] = (float)0.9*use_gain*(pitchfact * PLCresidual[i] + ((float)1.0 - pitchfact) * randvec[i]); energy += PLCresidual[i] * PLCresidual[i]; } /* less than 30 dB, use only noise */ if (sqrt(energy/(float)iLBCdec_inst->blockl) < 30.0) { gain=0.0; for (i=0; iblockl; i++) { PLCresidual[i] = randvec[i]; } } /* use old LPC */ memcpy(PLClpc,iLBCdec_inst->prevLpc, (LPC_FILTERORDER+1)*sizeof(float)); } /* no packet loss, copy input */ else { memcpy(PLCresidual, decresidual, iLBCdec_inst->blockl*sizeof(float)); memcpy(PLClpc, lpc, (LPC_FILTERORDER+1)*sizeof(float)); iLBCdec_inst->consPLICount = 0; } /* update state */ if (PLI) { iLBCdec_inst->prevLag = lag; iLBCdec_inst->per=max_per; } iLBCdec_inst->prevPLI = PLI; memcpy(iLBCdec_inst->prevLpc, PLClpc, (LPC_FILTERORDER+1)*sizeof(float)); memcpy(iLBCdec_inst->prevResidual, PLCresidual, iLBCdec_inst->blockl*sizeof(float)); } ================================================ FILE: deps/pjsip/third_party/ilbc/doCPLC.h ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code doCPLC.h Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #ifndef __iLBC_DOLPC_H #define __iLBC_DOLPC_H void doThePLC( float *PLCresidual, /* (o) concealed residual */ float *PLClpc, /* (o) concealed LP parameters */ int PLI, /* (i) packet loss indicator 0 - no PL, 1 = PL */ float *decresidual, /* (i) decoded residual */ float *lpc, /* (i) decoded LPC (only used for no PL) */ int inlag, /* (i) pitch lag */ iLBC_Dec_Inst_t *iLBCdec_inst /* (i/o) decoder instance */ ); #endif ================================================ FILE: deps/pjsip/third_party/ilbc/enhancer.c ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code enhancer.c Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #include #include #include "iLBC_define.h" #include "constants.h" #include "filter.h" /*----------------------------------------------------------------* * Find index in array such that the array element with said * index is the element of said array closest to "value" * according to the squared-error criterion *---------------------------------------------------------------*/ void NearestNeighbor( int *index, /* (o) index of array element closest to value */ float *array, /* (i) data array */ float value,/* (i) value */ int arlength/* (i) dimension of data array */ ){ int i; float bestcrit,crit; crit=array[0]-value; bestcrit=crit*crit; *index=0; for (i=1; i dim1 ) { hfl2=(int) (dim1/2); for (j=0; j= idatal) { searchSegEndPos=idatal-ENH_BLOCKL-1; } corrdim=searchSegEndPos-searchSegStartPos+1; /* compute upsampled correlation (corr33) and find location of max */ mycorr1(corrVec,idata+searchSegStartPos, corrdim+ENH_BLOCKL-1,idata+centerStartPos,ENH_BLOCKL); enh_upsample(corrVecUps,corrVec,corrdim,ENH_FL0); tloc=0; maxv=corrVecUps[0]; for (i=1; imaxv) { tloc=i; maxv=corrVecUps[i]; } } /* make vector can be upsampled without ever running outside bounds */ *updStartPos= (float)searchSegStartPos + (float)tloc/(float)ENH_UPS0+(float)1.0; tloc2=(int)(tloc/ENH_UPS0); if (tloc>tloc2*ENH_UPS0) { tloc2++; } st=searchSegStartPos+tloc2-ENH_FL0; if (st<0) { memset(vect,0,-st*sizeof(float)); memcpy(&vect[-st],idata, (ENH_VECTL+st)*sizeof(float)); } else { en=st+ENH_VECTL; if (en>idatal) { memcpy(vect, &idata[st], (ENH_VECTL-(en-idatal))*sizeof(float)); memset(&vect[ENH_VECTL-(en-idatal)], 0, (en-idatal)*sizeof(float)); } else { memcpy(vect, &idata[st], ENH_VECTL*sizeof(float)); } } fraction=tloc2*ENH_UPS0-tloc; /* compute the segment (this is actually a convolution) */ mycorr1(seg,vect,ENH_VECTL,polyphaserTbl+(2*ENH_FL0+1)*fraction, 2*ENH_FL0+1); } /*----------------------------------------------------------------* * find the smoothed output data *---------------------------------------------------------------*/ void smath( float *odata, /* (o) smoothed output */ float *sseq,/* (i) said second sequence of waveforms */ int hl, /* (i) 2*hl+1 is sseq dimension */ float alpha0/* (i) max smoothing energy fraction */ ){ int i,k; float w00,w10,w11,A,B,C,*psseq,err,errs; float surround[BLOCKL_MAX]; /* shape contributed by other than current */ float wt[2*ENH_HL+1]; /* waveform weighting to get surround shape */ float denom; /* create shape of contribution from all waveforms except the current one */ for (i=1; i<=2*hl+1; i++) { wt[i-1] = (float)0.5*(1 - (float)cos(2*PI*i/(2*hl+2))); } wt[hl]=0.0; /* for clarity, not used */ for (i=0; i alpha0 * w00) { if ( w00 < 1) { w00=1; } denom = (w11*w00-w10*w10)/(w00*w00); if (denom > 0.0001) { /* eliminates numerical problems for if smooth */ A = (float)sqrt( (alpha0- alpha0*alpha0/4)/denom); B = -alpha0/2 - A * w10/w00; B = B+1; } else { /* essentially no difference between cycles; smoothing not needed */ A= 0.0; B= 1.0; } /* create smoothed sequence */ psseq=sseq+hl*ENH_BLOCKL; for (i=0; i=0; q--) { blockStartPos[q]=blockStartPos[q+1]-period[lagBlock[q+1]]; NearestNeighbor(lagBlock+q,plocs, blockStartPos[q]+ ENH_BLOCKL_HALF-period[lagBlock[q+1]], periodl); if (blockStartPos[q]-ENH_OVERHANG>=0) { refiner(sseq+q*ENH_BLOCKL, blockStartPos+q, idata, idatal, centerStartPos, blockStartPos[q], period[lagBlock[q+1]]); } else { psseq=sseq+q*ENH_BLOCKL; memset(psseq, 0, ENH_BLOCKL*sizeof(float)); } } /* future */ for (i=0; i 0.0) { return (float)(ftmp1*ftmp1/ftmp2); } else { return (float)0.0; } } /*----------------------------------------------------------------* * interface for enhancer *---------------------------------------------------------------*/ int enhancerInterface( float *out, /* (o) enhanced signal */ float *in, /* (i) unenhanced signal */ iLBC_Dec_Inst_t *iLBCdec_inst /* (i) buffers etc */ ){ float *enh_buf, *enh_period; int iblock, isample; int lag=0, ilag, i, ioffset; float cc, maxcc; float ftmp1, ftmp2; float *inPtr, *enh_bufPtr1, *enh_bufPtr2; float plc_pred[ENH_BLOCKL]; float lpState[6], downsampled[(ENH_NBLOCKS*ENH_BLOCKL+120)/2]; int inLen=ENH_NBLOCKS*ENH_BLOCKL+120; int start, plc_blockl, inlag; enh_buf=iLBCdec_inst->enh_buf; enh_period=iLBCdec_inst->enh_period; memmove(enh_buf, &enh_buf[iLBCdec_inst->blockl], (ENH_BUFL-iLBCdec_inst->blockl)*sizeof(float)); memcpy(&enh_buf[ENH_BUFL-iLBCdec_inst->blockl], in, iLBCdec_inst->blockl*sizeof(float)); if (iLBCdec_inst->mode==30) plc_blockl=ENH_BLOCKL; else plc_blockl=40; /* when 20 ms frame, move processing one block */ ioffset=0; if (iLBCdec_inst->mode==20) ioffset=1; i=3-ioffset; memmove(enh_period, &enh_period[i], (ENH_NBLOCKS_TOT-i)*sizeof(float)); /* Set state information to the 6 samples right before the samples to be downsampled. */ memcpy(lpState, enh_buf+(ENH_NBLOCKS_EXTRA+ioffset)*ENH_BLOCKL-126, 6*sizeof(float)); /* Down sample a factor 2 to save computations */ DownSample(enh_buf+(ENH_NBLOCKS_EXTRA+ioffset)*ENH_BLOCKL-120, lpFilt_coefsTbl, inLen-ioffset*ENH_BLOCKL, lpState, downsampled); /* Estimate the pitch in the down sampled domain. */ for (iblock = 0; iblock maxcc) { maxcc = cc; lag = ilag; } } /* Store the estimated lag in the non-downsampled domain */ enh_period[iblock+ENH_NBLOCKS_EXTRA+ioffset] = (float)lag*2; } /* PLC was performed on the previous packet */ if (iLBCdec_inst->prev_enh_pl==1) { inlag=(int)enh_period[ENH_NBLOCKS_EXTRA+ioffset]; lag = inlag-1; maxcc = xCorrCoef(in, in+lag, plc_blockl); for (ilag=inlag; ilag<=inlag+1; ilag++) { cc = xCorrCoef(in, in+ilag, plc_blockl); if (cc > maxcc) { maxcc = cc; lag = ilag; } } enh_period[ENH_NBLOCKS_EXTRA+ioffset-1]=(float)lag; /* compute new concealed residual for the old lookahead, mix the forward PLC with a backward PLC from the new frame */ inPtr=&in[lag-1]; enh_bufPtr1=&plc_pred[plc_blockl-1]; if (lag>plc_blockl) { start=plc_blockl; } else { start=lag; } for (isample = start; isample>0; isample--) { *enh_bufPtr1-- = *inPtr--; } enh_bufPtr2=&enh_buf[ENH_BUFL-1-iLBCdec_inst->blockl]; for (isample = (plc_blockl-1-lag); isample>=0; isample--) { *enh_bufPtr1-- = *enh_bufPtr2--; } /* limit energy change */ ftmp2=0.0; ftmp1=0.0; for (i=0;iblockl-i]* enh_buf[ENH_BUFL-1-iLBCdec_inst->blockl-i]; ftmp1+=plc_pred[i]*plc_pred[i]; } ftmp1=(float)sqrt(ftmp1/(float)plc_blockl); ftmp2=(float)sqrt(ftmp2/(float)plc_blockl); if (ftmp1>(float)2.0*ftmp2 && ftmp1>0.0) { for (i=0;iblockl]; for (i=0; imode==20) { /* Enhancer with 40 samples delay */ for (iblock = 0; iblock<2; iblock++) { enhancer(out+iblock*ENH_BLOCKL, enh_buf, ENH_BUFL, (5+iblock)*ENH_BLOCKL+40, ENH_ALPHA0, enh_period, enh_plocsTbl, ENH_NBLOCKS_TOT); } } else if (iLBCdec_inst->mode==30) { /* Enhancer with 80 samples delay */ for (iblock = 0; iblock<3; iblock++) { enhancer(out+iblock*ENH_BLOCKL, enh_buf, ENH_BUFL, (4+iblock)*ENH_BLOCKL, ENH_ALPHA0, enh_period, enh_plocsTbl, ENH_NBLOCKS_TOT); } } return (lag*2); } ================================================ FILE: deps/pjsip/third_party/ilbc/enhancer.h ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code enhancer.h Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #ifndef __ENHANCER_H #define __ENHANCER_H #include "iLBC_define.h" float xCorrCoef( float *target, /* (i) first array */ float *regressor, /* (i) second array */ int subl /* (i) dimension arrays */ ); int enhancerInterface( float *out, /* (o) the enhanced recidual signal */ float *in, /* (i) the recidual signal to enhance */ iLBC_Dec_Inst_t *iLBCdec_inst /* (i/o) the decoder state structure */ ); #endif ================================================ FILE: deps/pjsip/third_party/ilbc/filter.c ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code filter.c Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #include "iLBC_define.h" /*----------------------------------------------------------------* * all-pole filter *---------------------------------------------------------------*/ void AllPoleFilter( float *InOut, /* (i/o) on entrance InOut[-orderCoef] to InOut[-1] contain the state of the filter (delayed samples). InOut[0] to InOut[lengthInOut-1] contain the filter input, on en exit InOut[-orderCoef] to InOut[-1] is unchanged and InOut[0] to InOut[lengthInOut-1] contain filtered samples */ float *Coef,/* (i) filter coefficients, Coef[0] is assumed to be 1.0 */ int lengthInOut,/* (i) number of input/output samples */ int orderCoef /* (i) number of filter coefficients */ ){ int n,k; for(n=0;n #include #include "constants.h" #include "filter.h" /*----------------------------------------------------------------* * quantizer for the gain in the gain-shape coding of residual *---------------------------------------------------------------*/ float gainquant(/* (o) quantized gain value */ float in, /* (i) gain value */ float maxIn,/* (i) maximum of gain value */ int cblen, /* (i) number of quantization indices */ int *index /* (o) quantization index */ ){ int i, tindex; float minmeasure,measure, *cb, scale; /* ensure a lower bound on the scaling factor */ scale=maxIn; if (scale<0.1) { scale=(float)0.1; } /* select the quantization table */ if (cblen == 8) { cb = gain_sq3Tbl; } else if (cblen == 16) { cb = gain_sq4Tbl; } else { cb = gain_sq5Tbl; } /* select the best index in the quantization table */ minmeasure=10000000.0; tindex=0; for (i=0; i /*----------------------------------------------------------------* * Construct codebook vector for given index. *---------------------------------------------------------------*/ void getCBvec( float *cbvec, /* (o) Constructed codebook vector */ float *mem, /* (i) Codebook buffer */ int index, /* (i) Codebook index */ int lMem, /* (i) Length of codebook buffer */ int cbveclen/* (i) Codebook vector length */ ){ int j, k, n, memInd, sFilt; float tmpbuf[CB_MEML]; int base_size; int ilow, ihigh; float alfa, alfa1; /* Determine size of codebook sections */ base_size=lMem-cbveclen+1; if (cbveclen==SUBL) { base_size+=cbveclen/2; } /* No filter -> First codebook section */ if (index #include "iLBC_define.h" #include "constants.h" /*----------------------------------------------------------------* * calculation of auto correlation *---------------------------------------------------------------*/ void autocorr( float *r, /* (o) autocorrelation vector */ const float *x, /* (i) data vector */ int N, /* (i) length of data vector */ int order /* largest lag for calculated autocorrelations */ ){ int lag, n; float sum; for (lag = 0; lag <= order; lag++) { sum = 0; for (n = 0; n < N - lag; n++) { sum += x[n] * x[n+lag]; } r[lag] = sum; } } /*----------------------------------------------------------------* * window multiplication *---------------------------------------------------------------*/ void window( float *z, /* (o) the windowed data */ const float *x, /* (i) the original data vector */ const float *y, /* (i) the window */ int N /* (i) length of all vectors */ ){ int i; for (i = 0; i < N; i++) { z[i] = x[i] * y[i]; } } /*----------------------------------------------------------------* * levinson-durbin solution for lpc coefficients *---------------------------------------------------------------*/ void levdurb( float *a, /* (o) lpc coefficient vector starting with 1.0 */ float *k, /* (o) reflection coefficients */ float *r, /* (i) autocorrelation vector */ int order /* (i) order of lpc filter */ ){ float sum, alpha; int m, m_h, i; a[0] = 1.0; if (r[0] < EPS) { /* if r[0] <= 0, set LPC coeff. to zero */ for (i = 0; i < order; i++) { k[i] = 0; a[i+1] = 0; } } else { a[1] = k[0] = -r[1]/r[0]; alpha = r[0] + r[1] * k[0]; for (m = 1; m < order; m++){ sum = r[m + 1]; for (i = 0; i < m; i++){ sum += a[i+1] * r[m - i]; } k[m] = -sum / alpha; alpha += k[m] * sum; m_h = (m + 1) >> 1; for (i = 0; i < m_h; i++){ sum = a[i+1] + k[m] * a[m - i]; a[m - i] += k[m] * a[i+1]; a[i+1] = sum; } a[m+1] = k[m]; } } } /*----------------------------------------------------------------* * interpolation between vectors *---------------------------------------------------------------*/ void interpolate( float *out, /* (o) the interpolated vector */ float *in1, /* (i) the first vector for the interpolation */ float *in2, /* (i) the second vector for the interpolation */ float coef, /* (i) interpolation weights */ int length /* (i) length of all vectors */ ){ int i; float invcoef; invcoef = (float)1.0 - coef; for (i = 0; i < length; i++) { out[i] = coef * in1[i] + invcoef * in2[i]; } } /*----------------------------------------------------------------* * lpc bandwidth expansion *---------------------------------------------------------------*/ void bwexpand( float *out, /* (o) the bandwidth expanded lpc coefficients */ float *in, /* (i) the lpc coefficients before bandwidth expansion */ float coef, /* (i) the bandwidth expansion factor */ int length /* (i) the length of lpc coefficient vectors */ ){ int i; float chirp; chirp = coef; out[0] = in[0]; for (i = 1; i < length; i++) { out[i] = chirp * in[i]; chirp *= coef; } } /*----------------------------------------------------------------* * vector quantization *---------------------------------------------------------------*/ void vq( float *Xq, /* (o) the quantized vector */ int *index, /* (o) the quantization index */ const float *CB,/* (i) the vector quantization codebook */ float *X, /* (i) the vector to quantize */ int n_cb, /* (i) the number of vectors in the codebook */ int dim /* (i) the dimension of all vectors */ ){ int i, j; int pos, minindex; float dist, tmp, mindist; pos = 0; mindist = FLOAT_MAX; minindex = 0; for (j = 0; j < n_cb; j++) { dist = X[0] - CB[pos]; dist *= dist; for (i = 1; i < dim; i++) { tmp = X[i] - CB[pos + i]; dist += tmp*tmp; } if (dist < mindist) { mindist = dist; minindex = j; } pos += dim; } for (i = 0; i < dim; i++) { Xq[i] = CB[minindex*dim + i]; } *index = minindex; } /*----------------------------------------------------------------* * split vector quantization *---------------------------------------------------------------*/ void SplitVQ( float *qX, /* (o) the quantized vector */ int *index, /* (o) a vector of indexes for all vector codebooks in the split */ float *X, /* (i) the vector to quantize */ const float *CB,/* (i) the quantizer codebook */ int nsplit, /* the number of vector splits */ const int *dim, /* the dimension of X and qX */ const int *cbsize /* the number of vectors in the codebook */ ){ int cb_pos, X_pos, i; cb_pos = 0; X_pos= 0; for (i = 0; i < nsplit; i++) { vq(qX + X_pos, index + i, CB + cb_pos, X + X_pos, cbsize[i], dim[i]); X_pos += dim[i]; cb_pos += dim[i] * cbsize[i]; } } /*----------------------------------------------------------------* * scalar quantization *---------------------------------------------------------------*/ void sort_sq( float *xq, /* (o) the quantized value */ int *index, /* (o) the quantization index */ float x, /* (i) the value to quantize */ const float *cb,/* (i) the quantization codebook */ int cb_size /* (i) the size of the quantization codebook */ ){ int i; if (x <= cb[0]) { *index = 0; *xq = cb[0]; } else { i = 0; while ((x > cb[i]) && i < cb_size - 1) { i++; } if (x > ((cb[i] + cb[i - 1])/2)) { *index = i; *xq = cb[i]; } else { *index = i - 1; *xq = cb[i - 1]; } } } /*----------------------------------------------------------------* * check for stability of lsf coefficients *---------------------------------------------------------------*/ int LSF_check( /* (o) 1 for stable lsf vectors and 0 for nonstable ones */ float *lsf, /* (i) a table of lsf vectors */ int dim, /* (i) the dimension of each lsf vector */ int NoAn /* (i) the number of lsf vectors in the table */ ){ int k,n,m, Nit=2, change=0,pos; //float tmp; static float eps=(float)0.039; /* 50 Hz */ static float eps2=(float)0.0195; static float maxlsf=(float)3.14; /* 4000 Hz */ static float minlsf=(float)0.01; /* 0 Hz */ /* LSF separation check*/ for (n=0; nmaxlsf) { lsf[pos]=maxlsf; change=1; } } } } return change; } ================================================ FILE: deps/pjsip/third_party/ilbc/helpfun.h ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code helpfun.h Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #ifndef __iLBC_HELPFUN_H #define __iLBC_HELPFUN_H void autocorr( float *r, /* (o) autocorrelation vector */ const float *x, /* (i) data vector */ int N, /* (i) length of data vector */ int order /* largest lag for calculated autocorrelations */ ); void window( float *z, /* (o) the windowed data */ const float *x, /* (i) the original data vector */ const float *y, /* (i) the window */ int N /* (i) length of all vectors */ ); void levdurb( float *a, /* (o) lpc coefficient vector starting with 1.0 */ float *k, /* (o) reflection coefficients */ float *r, /* (i) autocorrelation vector */ int order /* (i) order of lpc filter */ ); void interpolate( float *out, /* (o) the interpolated vector */ float *in1, /* (i) the first vector for the interpolation */ float *in2, /* (i) the second vector for the interpolation */ float coef, /* (i) interpolation weights */ int length /* (i) length of all vectors */ ); void bwexpand( float *out, /* (o) the bandwidth expanded lpc coefficients */ float *in, /* (i) the lpc coefficients before bandwidth expansion */ float coef, /* (i) the bandwidth expansion factor */ int length /* (i) the length of lpc coefficient vectors */ ); void vq( float *Xq, /* (o) the quantized vector */ int *index, /* (o) the quantization index */ const float *CB,/* (i) the vector quantization codebook */ float *X, /* (i) the vector to quantize */ int n_cb, /* (i) the number of vectors in the codebook */ int dim /* (i) the dimension of all vectors */ ); void SplitVQ( float *qX, /* (o) the quantized vector */ int *index, /* (o) a vector of indexes for all vector codebooks in the split */ float *X, /* (i) the vector to quantize */ const float *CB,/* (i) the quantizer codebook */ int nsplit, /* the number of vector splits */ const int *dim, /* the dimension of X and qX */ const int *cbsize /* the number of vectors in the codebook */ ); void sort_sq( float *xq, /* (o) the quantized value */ int *index, /* (o) the quantization index */ float x, /* (i) the value to quantize */ const float *cb,/* (i) the quantization codebook */ int cb_size /* (i) the size of the quantization codebook */ ); int LSF_check( /* (o) 1 for stable lsf vectors and 0 for nonstable ones */ float *lsf, /* (i) a table of lsf vectors */ int dim, /* (i) the dimension of each lsf vector */ int NoAn /* (i) the number of lsf vectors in the table */ ); #endif ================================================ FILE: deps/pjsip/third_party/ilbc/hpInput.c ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code hpInput.c Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #include "constants.h" /*----------------------------------------------------------------* * Input high-pass filter *---------------------------------------------------------------*/ void hpInput( float *In, /* (i) vector to filter */ int len, /* (i) length of vector to filter */ float *Out, /* (o) the resulting filtered vector */ float *mem /* (i/o) the filter state */ ){ int i; float *pi, *po; /* all-zero section*/ pi = &In[0]; po = &Out[0]; for (i=0; i #include "iLBC_define.h" #include "gainquant.h" #include "getCBvec.h" /*----------------------------------------------------------------* * Convert the codebook indexes to make the search easier *---------------------------------------------------------------*/ void index_conv_enc( int *index /* (i/o) Codebook indexes */ ){ int k; for (k=1; k=108)&&(index[k]<172)) { index[k]-=64; } else if (index[k]>=236) { index[k]-=128; } else { /* ERROR */ } } } void index_conv_dec( int *index /* (i/o) Codebook indexes */ ){ int k; for (k=1; k=44)&&(index[k]<108)) { index[k]+=64; } else if ((index[k]>=108)&&(index[k]<128)) { index[k]+=128; } else { /* ERROR */ } } } /*----------------------------------------------------------------* * Construct decoded vector from codebook and gains. *---------------------------------------------------------------*/ void iCBConstruct( float *decvector, /* (o) Decoded vector */ int *index, /* (i) Codebook indices */ int *gain_index,/* (i) Gain quantization indices */ float *mem, /* (i) Buffer for codevector construction */ int lMem, /* (i) Length of buffer */ int veclen, /* (i) Length of vector */ int nStages /* (i) Number of codebook stages */ ){ int j,k; float gain[CB_NSTAGES]; float cbvec[SUBL]; /* gain de-quantization */ gain[0] = gaindequant(gain_index[0], 1.0, 32); if (nStages > 1) { gain[1] = gaindequant(gain_index[1], (float)fabs(gain[0]), 16); } if (nStages > 2) { gain[2] = gaindequant(gain_index[2], (float)fabs(gain[1]), 8); } /* codebook vector construction and construction of total vector */ getCBvec(cbvec, mem, index[0], lMem, veclen); for (j=0;j 1) { for (k=1; k #include #include "iLBC_define.h" #include "gainquant.h" #include "createCB.h" #include "filter.h" #include "constants.h" /*----------------------------------------------------------------* * Search routine for codebook encoding and gain quantization. *---------------------------------------------------------------*/ void iCBSearch( iLBC_Enc_Inst_t *iLBCenc_inst, /* (i) the encoder state structure */ int *index, /* (o) Codebook indices */ int *gain_index,/* (o) Gain quantization indices */ float *intarget,/* (i) Target vector for encoding */ float *mem, /* (i) Buffer for codebook construction */ int lMem, /* (i) Length of buffer */ int lTarget, /* (i) Length of vector */ int nStages, /* (i) Number of codebook stages */ float *weightDenum, /* (i) weighting filter coefficients */ float *weightState, /* (i) weighting filter state */ int block /* (i) the sub-block number */ ){ int i, j, icount, stage, best_index, range, counter; float max_measure, gain, measure, crossDot, ftmp; float gains[CB_NSTAGES]; float target[SUBL]; int base_index, sInd, eInd, base_size; int sIndAug=0, eIndAug=0; float buf[CB_MEML+SUBL+2*LPC_FILTERORDER]; float invenergy[CB_EXPAND*128], energy[CB_EXPAND*128]; float *pp, *ppi=0, *ppo=0, *ppe=0; float cbvectors[CB_MEML]; float tene, cene, cvec[SUBL]; float aug_vec[SUBL]; memset(cvec,0,SUBL*sizeof(float)); /* Determine size of codebook sections */ base_size=lMem-lTarget+1; if (lTarget==SUBL) { base_size=lMem-lTarget+1+lTarget/2; } /* setup buffer for weighting */ memcpy(buf,weightState,sizeof(float)*LPC_FILTERORDER); memcpy(buf+LPC_FILTERORDER,mem,lMem*sizeof(float)); memcpy(buf+LPC_FILTERORDER+lMem,intarget,lTarget*sizeof(float)); /* weighting */ AllPoleFilter(buf+LPC_FILTERORDER, weightDenum, lMem+lTarget, LPC_FILTERORDER); /* Construct the codebook and target needed */ memcpy(target, buf+LPC_FILTERORDER+lMem, lTarget*sizeof(float)); tene=0.0; for (i=0; i0.0) { invenergy[0] = (float) 1.0 / (*ppe + EPS); } else { invenergy[0] = (float) 0.0; } ppe++; measure=(float)-10000000.0; if (crossDot > 0.0) { measure = crossDot*crossDot*invenergy[0]; } } else { measure = crossDot*crossDot*invenergy[0]; } /* check if measure is better */ ftmp = crossDot*invenergy[0]; if ((measure>max_measure) && (fabs(ftmp)0.0) { invenergy[icount] = (float)1.0/(energy[icount]+EPS); } else { invenergy[icount] = (float) 0.0; } measure=(float)-10000000.0; if (crossDot > 0.0) { measure = crossDot*crossDot*invenergy[icount]; } } else { measure = crossDot*crossDot*invenergy[icount]; } /* check if measure is better */ ftmp = crossDot*invenergy[icount]; if ((measure>max_measure) && (fabs(ftmp) range) { sInd -= (eInd-range); eInd = range; } } else { /* base_index >= (base_size-20) */ if (sInd < (base_size-20)) { sIndAug = 20; sInd = 0; eInd = 0; eIndAug = 19 + CB_RESRANGE; if(eIndAug > 39) { eInd = eIndAug-39; eIndAug = 39; } } else { sIndAug = 20 + sInd - (base_size-20); eIndAug = 39; sInd = 0; eInd = CB_RESRANGE - (eIndAug-sIndAug+1); } } } else { /* lTarget = 22 or 23 */ if (sInd < 0) { eInd -= sInd; sInd = 0; } if(eInd > range) { sInd -= (eInd - range); eInd = range; } } //} # endif /* CB_RESRANGE == -1 */ /* search of higher codebook section */ /* index search range */ counter = sInd; sInd += base_size; eInd += base_size; if (stage==0) { ppe = energy+base_size; *ppe=0.0; pp=cbvectors+lMem-lTarget; for (j=0; j0.0) { invenergy[icount] =(float)1.0/(energy[icount]+EPS); } else { invenergy[icount] =(float)0.0; } if (stage==0) { measure=(float)-10000000.0; if (crossDot > 0.0) { measure = crossDot*crossDot* invenergy[icount]; } } else { measure = crossDot*crossDot*invenergy[icount]; } /* check if measure is better */ ftmp = crossDot*invenergy[icount]; if ((measure>max_measure) && (fabs(ftmp)CB_MAXGAIN) { gain = (float)CB_MAXGAIN; } gain = gainquant(gain, 1.0, 32, &gain_index[stage]); } else { if (stage==1) { gain = gainquant(gain, (float)fabs(gains[stage-1]), 16, &gain_index[stage]); } else { gain = gainquant(gain, (float)fabs(gains[stage-1]), 8, &gain_index[stage]); } } /* Extract the best (according to measure) codebook vector */ if (lTarget==(STATE_LEN-iLBCenc_inst->state_short_len)) { if (index[stage] #include #include "iLBC_define.h" #include "StateConstructW.h" #include "LPCdecode.h" #include "iCBConstruct.h" #include "doCPLC.h" #include "helpfun.h" #include "constants.h" #include "packing.h" #include "string.h" #include "enhancer.h" #include "hpOutput.h" #include "syntFilter.h" /*----------------------------------------------------------------* * Initiation of decoder instance. *---------------------------------------------------------------*/ short initDecode( /* (o) Number of decoded samples */ iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) Decoder instance */ int mode, /* (i) frame size mode */ int use_enhancer /* (i) 1 to use enhancer 0 to run without enhancer */ ){ int i; iLBCdec_inst->mode = mode; if (mode==30) { iLBCdec_inst->blockl = BLOCKL_30MS; iLBCdec_inst->nsub = NSUB_30MS; iLBCdec_inst->nasub = NASUB_30MS; iLBCdec_inst->lpc_n = LPC_N_30MS; iLBCdec_inst->no_of_bytes = NO_OF_BYTES_30MS; iLBCdec_inst->no_of_words = NO_OF_WORDS_30MS; iLBCdec_inst->state_short_len=STATE_SHORT_LEN_30MS; /* ULP init */ iLBCdec_inst->ULP_inst=&ULP_30msTbl; } else if (mode==20) { iLBCdec_inst->blockl = BLOCKL_20MS; iLBCdec_inst->nsub = NSUB_20MS; iLBCdec_inst->nasub = NASUB_20MS; iLBCdec_inst->lpc_n = LPC_N_20MS; iLBCdec_inst->no_of_bytes = NO_OF_BYTES_20MS; iLBCdec_inst->no_of_words = NO_OF_WORDS_20MS; iLBCdec_inst->state_short_len=STATE_SHORT_LEN_20MS; /* ULP init */ iLBCdec_inst->ULP_inst=&ULP_20msTbl; } else { exit(2); } memset(iLBCdec_inst->syntMem, 0, LPC_FILTERORDER*sizeof(float)); memcpy((*iLBCdec_inst).lsfdeqold, lsfmeanTbl, LPC_FILTERORDER*sizeof(float)); memset(iLBCdec_inst->old_syntdenum, 0, ((LPC_FILTERORDER + 1)*NSUB_MAX)*sizeof(float)); for (i=0; iold_syntdenum[i*(LPC_FILTERORDER+1)]=1.0; iLBCdec_inst->last_lag = 20; iLBCdec_inst->prevLag = 120; iLBCdec_inst->per = 0.0; iLBCdec_inst->consPLICount = 0; iLBCdec_inst->prevPLI = 0; iLBCdec_inst->prevLpc[0] = 1.0; memset(iLBCdec_inst->prevLpc+1,0, LPC_FILTERORDER*sizeof(float)); memset(iLBCdec_inst->prevResidual, 0, BLOCKL_MAX*sizeof(float)); iLBCdec_inst->seed=777; memset(iLBCdec_inst->hpomem, 0, 4*sizeof(float)); iLBCdec_inst->use_enhancer = use_enhancer; memset(iLBCdec_inst->enh_buf, 0, ENH_BUFL*sizeof(float)); for (i=0;ienh_period[i]=(float)40.0; iLBCdec_inst->prev_enh_pl = 0; return (short)(iLBCdec_inst->blockl); } /*----------------------------------------------------------------* * frame residual decoder function (subrutine to iLBC_decode) *---------------------------------------------------------------*/ void Decode( iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) the decoder state structure */ float *decresidual, /* (o) decoded residual frame */ int start, /* (i) location of start state */ int idxForMax, /* (i) codebook index for the maximum value */ int *idxVec, /* (i) codebook indexes for the samples in the start state */ float *syntdenum, /* (i) the decoded synthesis filter coefficients */ int *cb_index, /* (i) the indexes for the adaptive codebook */ int *gain_index, /* (i) the indexes for the corresponding gains */ int *extra_cb_index, /* (i) the indexes for the adaptive codebook part of start state */ int *extra_gain_index, /* (i) the indexes for the corresponding gains */ int state_first /* (i) 1 if non adaptive part of start state comes first 0 if that part comes last */ ){ float reverseDecresidual[BLOCKL_MAX], mem[CB_MEML]; int k, meml_gotten, Nfor, Nback, i; int diff, start_pos; int subcount, subframe; diff = STATE_LEN - iLBCdec_inst->state_short_len; if (state_first == 1) { start_pos = (start-1)*SUBL; } else { start_pos = (start-1)*SUBL + diff; } /* decode scalar part of start state */ StateConstructW(idxForMax, idxVec, &syntdenum[(start-1)*(LPC_FILTERORDER+1)], &decresidual[start_pos], iLBCdec_inst->state_short_len); if (state_first) { /* put adaptive part in the end */ /* setup memory */ memset(mem, 0, (CB_MEML-iLBCdec_inst->state_short_len)*sizeof(float)); memcpy(mem+CB_MEML-iLBCdec_inst->state_short_len, decresidual+start_pos, iLBCdec_inst->state_short_len*sizeof(float)); /* construct decoded vector */ iCBConstruct( &decresidual[start_pos+iLBCdec_inst->state_short_len], extra_cb_index, extra_gain_index, mem+CB_MEML-stMemLTbl, stMemLTbl, diff, CB_NSTAGES); } else {/* put adaptive part in the beginning */ /* create reversed vectors for prediction */ for (k=0; kstate_short_len)]; } /* setup memory */ meml_gotten = iLBCdec_inst->state_short_len; for (k=0; knsub-start-1; if ( Nfor > 0 ){ /* setup memory */ memset(mem, 0, (CB_MEML-STATE_LEN)*sizeof(float)); memcpy(mem+CB_MEML-STATE_LEN, decresidual+(start-1)*SUBL, STATE_LEN*sizeof(float)); /* loop over sub-frames to encode */ for (subframe=0; subframe 0 ) { /* setup memory */ meml_gotten = SUBL*(iLBCdec_inst->nsub+1-start); if ( meml_gotten > CB_MEML ) { meml_gotten=CB_MEML; } for (k=0; k0) { /* the data are good */ /* decode data */ pbytes=bytes; pos=0; /* Set everything to zero before decoding */ for (k=0; kstate_short_len; k++) { idxVec[k]=0; } for (k=0; knasub; i++) { for (k=0; knasub; i++) { for (k=0; klpc_n; k++){ unpack( &pbytes, &lastpart, iLBCdec_inst->ULP_inst->lsf_bits[k][ulp], &pos); packcombine(&lsf_i[k], lastpart, iLBCdec_inst->ULP_inst->lsf_bits[k][ulp]); } /* Start block info */ unpack( &pbytes, &lastpart, iLBCdec_inst->ULP_inst->start_bits[ulp], &pos); packcombine(&start, lastpart, iLBCdec_inst->ULP_inst->start_bits[ulp]); unpack( &pbytes, &lastpart, iLBCdec_inst->ULP_inst->startfirst_bits[ulp], &pos); packcombine(&state_first, lastpart, iLBCdec_inst->ULP_inst->startfirst_bits[ulp]); unpack( &pbytes, &lastpart, iLBCdec_inst->ULP_inst->scale_bits[ulp], &pos); packcombine(&idxForMax, lastpart, iLBCdec_inst->ULP_inst->scale_bits[ulp]); for (k=0; kstate_short_len; k++) { unpack( &pbytes, &lastpart, iLBCdec_inst->ULP_inst->state_bits[ulp], &pos); packcombine(idxVec+k, lastpart, iLBCdec_inst->ULP_inst->state_bits[ulp]); } /* 23/22 (20ms/30ms) sample block */ for (k=0; kULP_inst->extra_cb_index[k][ulp], &pos); packcombine(extra_cb_index+k, lastpart, iLBCdec_inst->ULP_inst->extra_cb_index[k][ulp]); } for (k=0; kULP_inst->extra_cb_gain[k][ulp], &pos); packcombine(extra_gain_index+k, lastpart, iLBCdec_inst->ULP_inst->extra_cb_gain[k][ulp]); } /* The two/four (20ms/30ms) 40 sample sub-blocks */ for (i=0; inasub; i++) { for (k=0; kULP_inst->cb_index[i][k][ulp], &pos); packcombine(cb_index+i*CB_NSTAGES+k, lastpart, iLBCdec_inst->ULP_inst->cb_index[i][k][ulp]); } } for (i=0; inasub; i++) { for (k=0; kULP_inst->cb_gain[i][k][ulp], &pos); packcombine(gain_index+i*CB_NSTAGES+k, lastpart, iLBCdec_inst->ULP_inst->cb_gain[i][k][ulp]); } } } /* Extract last bit. If it is 1 this indicates an empty/lost frame */ unpack( &pbytes, &last_bit, 1, &pos); /* Check for bit errors or empty/lost frames */ if (start<1) mode = 0; if (iLBCdec_inst->mode==20 && start>3) mode = 0; if (iLBCdec_inst->mode==30 && start>5) mode = 0; if (last_bit==1) mode = 0; if (mode==1) { /* No bit errors was detected, continue decoding */ /* adjust index */ index_conv_dec(cb_index); /* decode the lsf */ SimplelsfDEQ(lsfdeq, lsf_i, iLBCdec_inst->lpc_n); LSF_check(lsfdeq, LPC_FILTERORDER, iLBCdec_inst->lpc_n); DecoderInterpolateLSF(syntdenum, weightdenum, lsfdeq, LPC_FILTERORDER, iLBCdec_inst); Decode(iLBCdec_inst, decresidual, start, idxForMax, idxVec, syntdenum, cb_index, gain_index, extra_cb_index, extra_gain_index, state_first); /* preparing the plc for a future loss! */ doThePLC(PLCresidual, PLClpc, 0, decresidual, syntdenum + (LPC_FILTERORDER + 1)*(iLBCdec_inst->nsub - 1), (*iLBCdec_inst).last_lag, iLBCdec_inst); memcpy(decresidual, PLCresidual, iLBCdec_inst->blockl*sizeof(float)); } } if (mode == 0) { /* the data is bad (either a PLC call * was made or a severe bit error was detected) */ /* packet loss conceal */ memset(zeros, 0, BLOCKL_MAX*sizeof(float)); one[0] = 1; memset(one+1, 0, LPC_FILTERORDER*sizeof(float)); start=0; doThePLC(PLCresidual, PLClpc, 1, zeros, one, (*iLBCdec_inst).last_lag, iLBCdec_inst); memcpy(decresidual, PLCresidual, iLBCdec_inst->blockl*sizeof(float)); order_plus_one = LPC_FILTERORDER + 1; for (i = 0; i < iLBCdec_inst->nsub; i++) { memcpy(syntdenum+(i*order_plus_one), PLClpc, order_plus_one*sizeof(float)); } } if (iLBCdec_inst->use_enhancer == 1) { /* post filtering */ iLBCdec_inst->last_lag = enhancerInterface(data, decresidual, iLBCdec_inst); /* synthesis filtering */ if (iLBCdec_inst->mode==20) { /* Enhancer has 40 samples delay */ i=0; syntFilter(data + i*SUBL, iLBCdec_inst->old_syntdenum + (i+iLBCdec_inst->nsub-1)*(LPC_FILTERORDER+1), SUBL, iLBCdec_inst->syntMem); for (i=1; i < iLBCdec_inst->nsub; i++) { syntFilter(data + i*SUBL, syntdenum + (i-1)*(LPC_FILTERORDER+1), SUBL, iLBCdec_inst->syntMem); } } else if (iLBCdec_inst->mode==30) { /* Enhancer has 80 samples delay */ for (i=0; i < 2; i++) { syntFilter(data + i*SUBL, iLBCdec_inst->old_syntdenum + (i+iLBCdec_inst->nsub-2)*(LPC_FILTERORDER+1), SUBL, iLBCdec_inst->syntMem); } for (i=2; i < iLBCdec_inst->nsub; i++) { syntFilter(data + i*SUBL, syntdenum + (i-2)*(LPC_FILTERORDER+1), SUBL, iLBCdec_inst->syntMem); } } } else { /* Find last lag */ lag = 20; maxcc = xCorrCoef(&decresidual[BLOCKL_MAX-ENH_BLOCKL], &decresidual[BLOCKL_MAX-ENH_BLOCKL-lag], ENH_BLOCKL); for (ilag=21; ilag<120; ilag++) { cc = xCorrCoef(&decresidual[BLOCKL_MAX-ENH_BLOCKL], &decresidual[BLOCKL_MAX-ENH_BLOCKL-ilag], ENH_BLOCKL); if (cc > maxcc) { maxcc = cc; lag = ilag; } } iLBCdec_inst->last_lag = lag; /* copy data and run synthesis filter */ memcpy(data, decresidual, iLBCdec_inst->blockl*sizeof(float)); for (i=0; i < iLBCdec_inst->nsub; i++) { syntFilter(data + i*SUBL, syntdenum + i*(LPC_FILTERORDER+1), SUBL, iLBCdec_inst->syntMem); } } /* high pass filtering on output if desired, otherwise copy to out */ hpOutput(data, iLBCdec_inst->blockl, decblock,iLBCdec_inst->hpomem); /* memcpy(decblock,data,iLBCdec_inst->blockl*sizeof(float));*/ memcpy(iLBCdec_inst->old_syntdenum, syntdenum, iLBCdec_inst->nsub*(LPC_FILTERORDER+1)*sizeof(float)); iLBCdec_inst->prev_enh_pl=0; if (mode==0) { /* PLC was used */ iLBCdec_inst->prev_enh_pl=1; } } ================================================ FILE: deps/pjsip/third_party/ilbc/iLBC_decode.h ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code iLBC_decode.h Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #ifndef __iLBC_ILBCDECODE_H #define __iLBC_ILBCDECODE_H #include "iLBC_define.h" short initDecode( /* (o) Number of decoded samples */ iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) Decoder instance */ int mode, /* (i) frame size mode */ int use_enhancer /* (i) 1 to use enhancer 0 to run without enhancer */ ); void iLBC_decode( float *decblock, /* (o) decoded signal block */ unsigned char *bytes, /* (i) encoded signal bits */ iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) the decoder state structure */ int mode /* (i) 0: bad packet, PLC, 1: normal */ ); #endif ================================================ FILE: deps/pjsip/third_party/ilbc/iLBC_define.h ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code iLBC_define.h Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #include #ifndef __iLBC_ILBCDEFINE_H #define __iLBC_ILBCDEFINE_H /* general codec settings */ #define FS (float)8000.0 #define BLOCKL_20MS 160 #define BLOCKL_30MS 240 #define BLOCKL_MAX 240 #define NSUB_20MS 4 #define NSUB_30MS 6 #define NSUB_MAX 6 #define NASUB_20MS 2 #define NASUB_30MS 4 #define NASUB_MAX 4 #define SUBL 40 #define STATE_LEN 80 #define STATE_SHORT_LEN_30MS 58 #define STATE_SHORT_LEN_20MS 57 /* LPC settings */ #define LPC_FILTERORDER 10 #define LPC_CHIRP_SYNTDENUM (float)0.9025 #define LPC_CHIRP_WEIGHTDENUM (float)0.4222 #define LPC_LOOKBACK 60 #define LPC_N_20MS 1 #define LPC_N_30MS 2 #define LPC_N_MAX 2 #define LPC_ASYMDIFF 20 #define LPC_BW (float)60.0 #define LPC_WN (float)1.0001 #define LSF_NSPLIT 3 #define LSF_NUMBER_OF_STEPS 4 #define LPC_HALFORDER (LPC_FILTERORDER/2) /* cb settings */ #define CB_NSTAGES 3 #define CB_EXPAND 2 #define CB_MEML 147 #define CB_FILTERLEN 2*4 #define CB_HALFFILTERLEN 4 #define CB_RESRANGE 34 #define CB_MAXGAIN (float)1.3 /* enhancer */ #define ENH_BLOCKL 80 /* block length */ #define ENH_BLOCKL_HALF (ENH_BLOCKL/2) #define ENH_HL 3 /* 2*ENH_HL+1 is number blocks in said second sequence */ #define ENH_SLOP 2 /* max difference estimated and correct pitch period */ #define ENH_PLOCSL 20 /* pitch-estimates and pitch- locations buffer length */ #define ENH_OVERHANG 2 #define ENH_UPS0 4 /* upsampling rate */ #define ENH_FL0 3 /* 2*FLO+1 is the length of each filter */ #define ENH_VECTL (ENH_BLOCKL+2*ENH_FL0) #define ENH_CORRDIM (2*ENH_SLOP+1) #define ENH_NBLOCKS (BLOCKL_MAX/ENH_BLOCKL) #define ENH_NBLOCKS_EXTRA 5 #define ENH_NBLOCKS_TOT 8 /* ENH_NBLOCKS + ENH_NBLOCKS_EXTRA */ #define ENH_BUFL (ENH_NBLOCKS_TOT)*ENH_BLOCKL #define ENH_ALPHA0 (float)0.05 /* Down sampling */ #define FILTERORDER_DS 7 #define DELAY_DS 3 #define FACTOR_DS 2 /* bit stream defs */ #define NO_OF_BYTES_20MS 38 #define NO_OF_BYTES_30MS 50 #define NO_OF_WORDS_20MS 19 #define NO_OF_WORDS_30MS 25 #define STATE_BITS 3 #define BYTE_LEN 8 #define ULP_CLASSES 3 /* help parameters */ #define FLOAT_MAX (float)1.0e37 #define EPS (float)2.220446049250313e-016 #define PI (float)3.14159265358979323846 #define MIN_SAMPLE -32768 #define MAX_SAMPLE 32767 #define TWO_PI (float)6.283185307 #define PI2 (float)0.159154943 /* type definition encoder instance */ typedef struct iLBC_ULP_Inst_t_ { int lsf_bits[6][ULP_CLASSES+2]; int start_bits[ULP_CLASSES+2]; int startfirst_bits[ULP_CLASSES+2]; int scale_bits[ULP_CLASSES+2]; int state_bits[ULP_CLASSES+2]; int extra_cb_index[CB_NSTAGES][ULP_CLASSES+2]; int extra_cb_gain[CB_NSTAGES][ULP_CLASSES+2]; int cb_index[NSUB_MAX][CB_NSTAGES][ULP_CLASSES+2]; int cb_gain[NSUB_MAX][CB_NSTAGES][ULP_CLASSES+2]; } iLBC_ULP_Inst_t; /* type definition encoder instance */ typedef struct iLBC_Enc_Inst_t_ { /* flag for frame size mode */ int mode; /* basic parameters for different frame sizes */ int blockl; int nsub; int nasub; int no_of_bytes, no_of_words; int lpc_n; int state_short_len; const iLBC_ULP_Inst_t *ULP_inst; /* analysis filter state */ float anaMem[LPC_FILTERORDER]; /* old lsf parameters for interpolation */ float lsfold[LPC_FILTERORDER]; float lsfdeqold[LPC_FILTERORDER]; /* signal buffer for LP analysis */ float lpc_buffer[LPC_LOOKBACK + BLOCKL_MAX]; /* state of input HP filter */ float hpimem[4]; } iLBC_Enc_Inst_t; /* type definition decoder instance */ typedef struct iLBC_Dec_Inst_t_ { /* flag for frame size mode */ int mode; /* basic parameters for different frame sizes */ int blockl; int nsub; int nasub; int no_of_bytes, no_of_words; int lpc_n; int state_short_len; const iLBC_ULP_Inst_t *ULP_inst; /* synthesis filter state */ float syntMem[LPC_FILTERORDER]; /* old LSF for interpolation */ float lsfdeqold[LPC_FILTERORDER]; /* pitch lag estimated in enhancer and used in PLC */ int last_lag; /* PLC state information */ int prevLag, consPLICount, prevPLI, prev_enh_pl; float prevLpc[LPC_FILTERORDER+1]; float prevResidual[NSUB_MAX*SUBL]; float per; unsigned long seed; /* previous synthesis filter parameters */ float old_syntdenum[(LPC_FILTERORDER + 1)*NSUB_MAX]; /* state of output HP filter */ float hpomem[4]; /* enhancer state information */ int use_enhancer; float enh_buf[ENH_BUFL]; float enh_period[ENH_NBLOCKS_TOT]; } iLBC_Dec_Inst_t; #endif ================================================ FILE: deps/pjsip/third_party/ilbc/iLBC_encode.c ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code iLBC_encode.c Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #include #include #include #include "iLBC_define.h" #include "LPCencode.h" #include "FrameClassify.h" #include "StateSearchW.h" #include "StateConstructW.h" #include "helpfun.h" #include "constants.h" #include "packing.h" #include "iCBSearch.h" #include "iCBConstruct.h" #include "hpInput.h" #include "anaFilter.h" #include "syntFilter.h" /*----------------------------------------------------------------* * Initiation of encoder instance. *---------------------------------------------------------------*/ short initEncode( /* (o) Number of bytes encoded */ iLBC_Enc_Inst_t *iLBCenc_inst, /* (i/o) Encoder instance */ int mode /* (i) frame size mode */ ){ iLBCenc_inst->mode = mode; if (mode==30) { iLBCenc_inst->blockl = BLOCKL_30MS; iLBCenc_inst->nsub = NSUB_30MS; iLBCenc_inst->nasub = NASUB_30MS; iLBCenc_inst->lpc_n = LPC_N_30MS; iLBCenc_inst->no_of_bytes = NO_OF_BYTES_30MS; iLBCenc_inst->no_of_words = NO_OF_WORDS_30MS; iLBCenc_inst->state_short_len=STATE_SHORT_LEN_30MS; /* ULP init */ iLBCenc_inst->ULP_inst=&ULP_30msTbl; } else if (mode==20) { iLBCenc_inst->blockl = BLOCKL_20MS; iLBCenc_inst->nsub = NSUB_20MS; iLBCenc_inst->nasub = NASUB_20MS; iLBCenc_inst->lpc_n = LPC_N_20MS; iLBCenc_inst->no_of_bytes = NO_OF_BYTES_20MS; iLBCenc_inst->no_of_words = NO_OF_WORDS_20MS; iLBCenc_inst->state_short_len=STATE_SHORT_LEN_20MS; /* ULP init */ iLBCenc_inst->ULP_inst=&ULP_20msTbl; } else { exit(2); } memset((*iLBCenc_inst).anaMem, 0, LPC_FILTERORDER*sizeof(float)); memcpy((*iLBCenc_inst).lsfold, lsfmeanTbl, LPC_FILTERORDER*sizeof(float)); memcpy((*iLBCenc_inst).lsfdeqold, lsfmeanTbl, LPC_FILTERORDER*sizeof(float)); memset((*iLBCenc_inst).lpc_buffer, 0, (LPC_LOOKBACK+BLOCKL_MAX)*sizeof(float)); memset((*iLBCenc_inst).hpimem, 0, 4*sizeof(float)); return (short)(iLBCenc_inst->no_of_bytes); } /*----------------------------------------------------------------* * main encoder function *---------------------------------------------------------------*/ void iLBC_encode( unsigned char *bytes, /* (o) encoded data bits iLBC */ float *block, /* (o) speech vector to encode */ iLBC_Enc_Inst_t *iLBCenc_inst /* (i/o) the general encoder state */ ){ float data[BLOCKL_MAX]; float residual[BLOCKL_MAX], reverseResidual[BLOCKL_MAX]; int start, idxForMax, idxVec[STATE_LEN]; float reverseDecresidual[BLOCKL_MAX], mem[CB_MEML]; int n, k, meml_gotten, Nfor, Nback, i, pos; int gain_index[CB_NSTAGES*NASUB_MAX], extra_gain_index[CB_NSTAGES]; int cb_index[CB_NSTAGES*NASUB_MAX],extra_cb_index[CB_NSTAGES]; int lsf_i[LSF_NSPLIT*LPC_N_MAX]; unsigned char *pbytes; int diff, start_pos, state_first; float en1, en2; int index, ulp, firstpart; int subcount, subframe; float weightState[LPC_FILTERORDER]; float syntdenum[NSUB_MAX*(LPC_FILTERORDER+1)]; float weightdenum[NSUB_MAX*(LPC_FILTERORDER+1)]; float decresidual[BLOCKL_MAX]; /* high pass filtering of input signal if such is not done prior to calling this function */ hpInput(block, iLBCenc_inst->blockl, data, (*iLBCenc_inst).hpimem); /* otherwise simply copy */ /*memcpy(data,block,iLBCenc_inst->blockl*sizeof(float));*/ /* LPC of hp filtered input data */ LPCencode(syntdenum, weightdenum, lsf_i, data, iLBCenc_inst); /* inverse filter to get residual */ for (n=0; nnsub; n++) { anaFilter(&data[n*SUBL], &syntdenum[n*(LPC_FILTERORDER+1)], SUBL, &residual[n*SUBL], iLBCenc_inst->anaMem); } /* find state location */ start = FrameClassify(iLBCenc_inst, residual); /* check if state should be in first or last part of the two subframes */ diff = STATE_LEN - iLBCenc_inst->state_short_len; en1 = 0; index = (start-1)*SUBL; for (i = 0; i < iLBCenc_inst->state_short_len; i++) { en1 += residual[index+i]*residual[index+i]; } en2 = 0; index = (start-1)*SUBL+diff; for (i = 0; i < iLBCenc_inst->state_short_len; i++) { en2 += residual[index+i]*residual[index+i]; } if (en1 > en2) { state_first = 1; start_pos = (start-1)*SUBL; } else { state_first = 0; start_pos = (start-1)*SUBL + diff; } /* scalar quantization of state */ StateSearchW(iLBCenc_inst, &residual[start_pos], &syntdenum[(start-1)*(LPC_FILTERORDER+1)], &weightdenum[(start-1)*(LPC_FILTERORDER+1)], &idxForMax, idxVec, iLBCenc_inst->state_short_len, state_first); StateConstructW(idxForMax, idxVec, &syntdenum[(start-1)*(LPC_FILTERORDER+1)], &decresidual[start_pos], iLBCenc_inst->state_short_len); /* predictive quantization in state */ if (state_first) { /* put adaptive part in the end */ /* setup memory */ memset(mem, 0, (CB_MEML-iLBCenc_inst->state_short_len)*sizeof(float)); memcpy(mem+CB_MEML-iLBCenc_inst->state_short_len, decresidual+start_pos, iLBCenc_inst->state_short_len*sizeof(float)); memset(weightState, 0, LPC_FILTERORDER*sizeof(float)); /* encode sub-frames */ iCBSearch(iLBCenc_inst, extra_cb_index, extra_gain_index, &residual[start_pos+iLBCenc_inst->state_short_len], mem+CB_MEML-stMemLTbl, stMemLTbl, diff, CB_NSTAGES, &weightdenum[start*(LPC_FILTERORDER+1)], weightState, 0); /* construct decoded vector */ iCBConstruct( &decresidual[start_pos+iLBCenc_inst->state_short_len], extra_cb_index, extra_gain_index, mem+CB_MEML-stMemLTbl, stMemLTbl, diff, CB_NSTAGES); } else { /* put adaptive part in the beginning */ /* create reversed vectors for prediction */ for (k=0; kstate_short_len)]; } /* setup memory */ meml_gotten = iLBCenc_inst->state_short_len; for (k=0; knsub-start-1; if ( Nfor > 0 ) { /* setup memory */ memset(mem, 0, (CB_MEML-STATE_LEN)*sizeof(float)); memcpy(mem+CB_MEML-STATE_LEN, decresidual+(start-1)*SUBL, STATE_LEN*sizeof(float)); memset(weightState, 0, LPC_FILTERORDER*sizeof(float)); /* loop over sub-frames to encode */ for (subframe=0; subframe 0 ) { /* create reverse order vectors */ for (n=0; nnsub+1-start); if ( meml_gotten > CB_MEML ) { meml_gotten=CB_MEML; } for (k=0; klpc_n; k++) { packsplit(&lsf_i[k], &firstpart, &lsf_i[k], iLBCenc_inst->ULP_inst->lsf_bits[k][ulp], iLBCenc_inst->ULP_inst->lsf_bits[k][ulp]+ iLBCenc_inst->ULP_inst->lsf_bits[k][ulp+1]+ iLBCenc_inst->ULP_inst->lsf_bits[k][ulp+2]); dopack( &pbytes, firstpart, iLBCenc_inst->ULP_inst->lsf_bits[k][ulp], &pos); } /* Start block info */ packsplit(&start, &firstpart, &start, iLBCenc_inst->ULP_inst->start_bits[ulp], iLBCenc_inst->ULP_inst->start_bits[ulp]+ iLBCenc_inst->ULP_inst->start_bits[ulp+1]+ iLBCenc_inst->ULP_inst->start_bits[ulp+2]); dopack( &pbytes, firstpart, iLBCenc_inst->ULP_inst->start_bits[ulp], &pos); packsplit(&state_first, &firstpart, &state_first, iLBCenc_inst->ULP_inst->startfirst_bits[ulp], iLBCenc_inst->ULP_inst->startfirst_bits[ulp]+ iLBCenc_inst->ULP_inst->startfirst_bits[ulp+1]+ iLBCenc_inst->ULP_inst->startfirst_bits[ulp+2]); dopack( &pbytes, firstpart, iLBCenc_inst->ULP_inst->startfirst_bits[ulp], &pos); packsplit(&idxForMax, &firstpart, &idxForMax, iLBCenc_inst->ULP_inst->scale_bits[ulp], iLBCenc_inst->ULP_inst->scale_bits[ulp]+ iLBCenc_inst->ULP_inst->scale_bits[ulp+1]+ iLBCenc_inst->ULP_inst->scale_bits[ulp+2]); dopack( &pbytes, firstpart, iLBCenc_inst->ULP_inst->scale_bits[ulp], &pos); for (k=0; kstate_short_len; k++) { packsplit(idxVec+k, &firstpart, idxVec+k, iLBCenc_inst->ULP_inst->state_bits[ulp], iLBCenc_inst->ULP_inst->state_bits[ulp]+ iLBCenc_inst->ULP_inst->state_bits[ulp+1]+ iLBCenc_inst->ULP_inst->state_bits[ulp+2]); dopack( &pbytes, firstpart, iLBCenc_inst->ULP_inst->state_bits[ulp], &pos); } /* 23/22 (20ms/30ms) sample block */ for (k=0;kULP_inst->extra_cb_index[k][ulp], iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp]+ iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp+1]+ iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp+2]); dopack( &pbytes, firstpart, iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp], &pos); } for (k=0;kULP_inst->extra_cb_gain[k][ulp], iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp]+ iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp+1]+ iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp+2]); dopack( &pbytes, firstpart, iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp], &pos); } /* The two/four (20ms/30ms) 40 sample sub-blocks */ for (i=0; inasub; i++) { for (k=0; kULP_inst->cb_index[i][k][ulp], iLBCenc_inst->ULP_inst->cb_index[i][k][ulp]+ iLBCenc_inst->ULP_inst->cb_index[i][k][ulp+1]+ iLBCenc_inst->ULP_inst->cb_index[i][k][ulp+2]); dopack( &pbytes, firstpart, iLBCenc_inst->ULP_inst->cb_index[i][k][ulp], &pos); } } for (i=0; inasub; i++) { for (k=0; kULP_inst->cb_gain[i][k][ulp], iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp]+ iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp+1]+ iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp+2]); dopack( &pbytes, firstpart, iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp], &pos); } } } /* set the last bit to zero (otherwise the decoder will treat it as a lost frame) */ dopack( &pbytes, 0, 1, &pos); } ================================================ FILE: deps/pjsip/third_party/ilbc/iLBC_encode.h ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code iLBC_encode.h Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #ifndef __iLBC_ILBCENCODE_H #define __iLBC_ILBCENCODE_H #include "iLBC_define.h" short initEncode( /* (o) Number of bytes encoded */ iLBC_Enc_Inst_t *iLBCenc_inst, /* (i/o) Encoder instance */ int mode /* (i) frame size mode */ ); void iLBC_encode( unsigned char *bytes, /* (o) encoded data bits iLBC */ float *block, /* (o) speech vector to encode */ iLBC_Enc_Inst_t *iLBCenc_inst /* (i/o) the general encoder state */ ); #endif ================================================ FILE: deps/pjsip/third_party/ilbc/iLBC_test.c ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code iLBC_test.c Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #include #include #include #include #include "iLBC_define.h" #include "iLBC_encode.h" #include "iLBC_decode.h" /* Runtime statistics */ #include #define ILBCNOOFWORDS_MAX (NO_OF_BYTES_30MS/2) /*----------------------------------------------------------------* * Encoder interface function *---------------------------------------------------------------*/ short encode( /* (o) Number of bytes encoded */ iLBC_Enc_Inst_t *iLBCenc_inst, /* (i/o) Encoder instance */ short *encoded_data, /* (o) The encoded bytes */ short *data /* (i) The signal block to encode*/ ){ float block[BLOCKL_MAX]; int k; /* convert signal to float */ for (k=0; kblockl; k++) block[k] = (float)data[k]; /* do the actual encoding */ iLBC_encode((unsigned char *)encoded_data, block, iLBCenc_inst); return (iLBCenc_inst->no_of_bytes); } /*----------------------------------------------------------------* * Decoder interface function *---------------------------------------------------------------*/ short decode( /* (o) Number of decoded samples */ iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) Decoder instance */ short *decoded_data, /* (o) Decoded signal block*/ short *encoded_data, /* (i) Encoded bytes */ short mode /* (i) 0=PL, 1=Normal */ ){ int k; float decblock[BLOCKL_MAX], dtmp; /* check if mode is valid */ if (mode<0 || mode>1) { printf("\nERROR - Wrong mode - 0, 1 allowed\n"); exit(3);} /* do actual decoding of block */ iLBC_decode(decblock, (unsigned char *)encoded_data, iLBCdec_inst, mode); /* convert to short */ for (k=0; kblockl; k++){ dtmp=decblock[k]; if (dtmpMAX_SAMPLE) dtmp=MAX_SAMPLE; decoded_data[k] = (short) dtmp; } return (iLBCdec_inst->blockl); } /*---------------------------------------------------------------* * Main program to test iLBC encoding and decoding * * Usage: * exefile_name.exe * * : Input file, speech for encoder (16-bit pcm file) * : Bit stream output from the encoder * : Output file, decoded speech (16-bit pcm file) * : Bit error file, optional (16-bit) * 1 - Packet received correctly * 0 - Packet Lost * *--------------------------------------------------------------*/ int main(int argc, char* argv[]) { /* Runtime statistics */ float starttime; float runtime; float outtime; FILE *ifileid,*efileid,*ofileid, *cfileid; short data[BLOCKL_MAX]; short encoded_data[ILBCNOOFWORDS_MAX], decoded_data[BLOCKL_MAX]; int len; short pli, mode; int blockcount = 0; int packetlosscount = 0; /* Create structs */ iLBC_Enc_Inst_t Enc_Inst; iLBC_Dec_Inst_t Dec_Inst; /* get arguments and open files */ if ((argc!=5) && (argc!=6)) { fprintf(stderr, "\n*-----------------------------------------------*\n"); fprintf(stderr, " %s <20,30> input encoded decoded (channel)\n\n", argv[0]); fprintf(stderr, " mode : Frame size for the encoding/decoding\n"); fprintf(stderr, " 20 - 20 ms\n"); fprintf(stderr, " 30 - 30 ms\n"); fprintf(stderr, " input : Speech for encoder (16-bit pcm file)\n"); fprintf(stderr, " encoded : Encoded bit stream\n"); fprintf(stderr, " decoded : Decoded speech (16-bit pcm file)\n"); fprintf(stderr, " channel : Packet loss pattern, optional (16-bit)\n"); fprintf(stderr, " 1 - Packet received correctly\n"); fprintf(stderr, " 0 - Packet Lost\n"); fprintf(stderr, "*-----------------------------------------------*\n\n"); exit(1); } mode=atoi(argv[1]); if (mode != 20 && mode != 30) { fprintf(stderr,"Wrong mode %s, must be 20, or 30\n", argv[1]); exit(2); } if ( (ifileid=fopen(argv[2],"rb")) == NULL) { fprintf(stderr,"Cannot open input file %s\n", argv[2]); exit(2);} if ( (efileid=fopen(argv[3],"wb")) == NULL) { fprintf(stderr, "Cannot open encoded file %s\n", argv[3]); exit(1);} if ( (ofileid=fopen(argv[4],"wb")) == NULL) { fprintf(stderr, "Cannot open decoded file %s\n", argv[4]); exit(1);} if (argc==6) { if( (cfileid=fopen(argv[5],"rb")) == NULL) { fprintf(stderr, "Cannot open channel file %s\n", argv[5]); exit(1); } } else { cfileid=NULL; } /* print info */ fprintf(stderr, "\n"); fprintf(stderr, "*---------------------------------------------------*\n"); fprintf(stderr, "* *\n"); fprintf(stderr, "* iLBC test program *\n"); fprintf(stderr, "* *\n"); fprintf(stderr, "* *\n"); fprintf(stderr, "*---------------------------------------------------*\n"); fprintf(stderr,"\nMode : %2d ms\n", mode); fprintf(stderr,"Input file : %s\n", argv[2]); fprintf(stderr,"Encoded file : %s\n", argv[3]); fprintf(stderr,"Output file : %s\n", argv[4]); if (argc==6) { fprintf(stderr,"Channel file : %s\n", argv[5]); } fprintf(stderr,"\n"); /* Initialization */ initEncode(&Enc_Inst, mode); initDecode(&Dec_Inst, mode, 1); /* Runtime statistics */ starttime=clock()/(float)CLOCKS_PER_SEC; /* loop over input blocks */ while (fread(data,sizeof(short),Enc_Inst.blockl,ifileid)== (size_t)Enc_Inst.blockl) { blockcount++; /* encoding */ fprintf(stderr, "--- Encoding block %i --- ",blockcount); len=encode(&Enc_Inst, encoded_data, data); fprintf(stderr, "\r"); /* write byte file */ fwrite(encoded_data, sizeof(unsigned char), len, efileid); /* get channel data if provided */ if (argc==6) { if (fread(&pli, sizeof(short), 1, cfileid)) { if ((pli!=0)&&(pli!=1)) { fprintf(stderr, "Error in channel file\n"); exit(0); } if (pli==0) { /* Packet loss -> remove info from frame */ memset(encoded_data, 0, sizeof(short)*ILBCNOOFWORDS_MAX); packetlosscount++; } } else { fprintf(stderr, "Error. Channel file too short\n"); exit(0); } } else { pli=1; } /* decoding */ fprintf(stderr, "--- Decoding block %i --- ",blockcount); len=decode(&Dec_Inst, decoded_data, encoded_data, pli); fprintf(stderr, "\r"); /* write output file */ fwrite(decoded_data,sizeof(short),len,ofileid); } /* Runtime statistics */ runtime = (float)(clock()/(float)CLOCKS_PER_SEC-starttime); outtime = (float)((float)blockcount*(float)mode/1000.0); printf("\n\nLength of speech file: %.1f s\n", outtime); printf("Packet loss : %.1f%%\n", 100.0*(float)packetlosscount/(float)blockcount); printf("Time to run iLBC :"); printf(" %.1f s (%.1f %% of realtime)\n\n", runtime, (100*runtime/outtime)); /* close files */ fclose(ifileid); fclose(efileid); fclose(ofileid); if (argc==6) { fclose(cfileid); } return(0); } ================================================ FILE: deps/pjsip/third_party/ilbc/lsf.c ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code lsf.c Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #include #include #include "iLBC_define.h" /*----------------------------------------------------------------* * conversion from lpc coefficients to lsf coefficients *---------------------------------------------------------------*/ void a2lsf( float *freq,/* (o) lsf coefficients */ float *a /* (i) lpc coefficients */ ){ float steps[LSF_NUMBER_OF_STEPS] = {(float)0.00635, (float)0.003175, (float)0.0015875, (float)0.00079375}; float step; int step_idx; int lsp_index; float p[LPC_HALFORDER]; float q[LPC_HALFORDER]; float p_pre[LPC_HALFORDER]; float q_pre[LPC_HALFORDER]; float old_p, old_q, *old; float *pq_coef; float omega, old_omega; int i; float hlp, hlp1, hlp2, hlp3, hlp4, hlp5; for (i=0; i= 0.5)){ if (step_idx == (LSF_NUMBER_OF_STEPS - 1)){ if (fabs(hlp5) >= fabs(*old)) { freq[lsp_index] = omega - step; } else { freq[lsp_index] = omega; } if ((*old) >= 0.0){ *old = (float)-1.0 * FLOAT_MAX; } else { *old = FLOAT_MAX; } omega = old_omega; step_idx = 0; step_idx = LSF_NUMBER_OF_STEPS; } else { if (step_idx == 0) { old_omega = omega; } step_idx++; omega -= steps[step_idx]; /* Go back one grid step */ step = steps[step_idx]; } } else { /* increment omega until they are of different sign, and we know there is at least one root between omega and old_omega */ *old = hlp5; omega += step; } } } for (i = 0; i= 0.5)){ if (freq[0] <= 0.0) { freq[0] = (float)0.022; } if (freq[LPC_FILTERORDER - 1] >= 0.5) { freq[LPC_FILTERORDER - 1] = (float)0.499; } hlp = (freq[LPC_FILTERORDER - 1] - freq[0]) / (float) (LPC_FILTERORDER - 1); for (i=1; i #include #include "iLBC_define.h" #include "constants.h" #include "helpfun.h" #include "string.h" /*----------------------------------------------------------------* * splitting an integer into first most significant bits and * remaining least significant bits *---------------------------------------------------------------*/ void packsplit( int *index, /* (i) the value to split */ int *firstpart, /* (o) the value specified by most significant bits */ int *rest, /* (o) the value specified by least significant bits */ int bitno_firstpart, /* (i) number of bits in most significant part */ int bitno_total /* (i) number of bits in full range of value */ ){ int bitno_rest = bitno_total-bitno_firstpart; *firstpart = *index>>(bitno_rest); *rest = *index-(*firstpart<<(bitno_rest)); } /*----------------------------------------------------------------* * combining a value corresponding to msb's with a value * corresponding to lsb's *---------------------------------------------------------------*/ void packcombine( int *index, /* (i/o) the msb value in the combined value out */ int rest, /* (i) the lsb value */ int bitno_rest /* (i) the number of bits in the lsb part */ ){ *index = *index<0) { /* Jump to the next byte if end of this byte is reached*/ if (*pos==8) { *pos=0; (*bitstream)++; **bitstream=0; } posLeft=8-(*pos); /* Insert index into the bitstream */ if (bitno <= posLeft) { **bitstream |= (unsigned char)(index<<(posLeft-bitno)); *pos+=bitno; bitno=0; } else { **bitstream |= (unsigned char)(index>>(bitno-posLeft)); *pos=8; index-=((index>>(bitno-posLeft))<<(bitno-posLeft)); bitno-=posLeft; } } } /*----------------------------------------------------------------* * unpacking of bits from bitstream, i.e., vector of bytes *---------------------------------------------------------------*/ void unpack( unsigned char **bitstream, /* (i/o) on entrance pointer to place in bitstream to unpack new data from, on exit pointer to place in bitstream to unpack future data from */ int *index, /* (o) resulting value */ int bitno, /* (i) number of bits used to represent the value */ int *pos /* (i/o) read position in the current byte */ ){ int BitsLeft; *index=0; while (bitno>0) { /* move forward in bitstream when the end of the byte is reached */ if (*pos==8) { *pos=0; (*bitstream)++; } BitsLeft=8-(*pos); /* Extract bits to index */ if (BitsLeft>=bitno) { *index+=((((**bitstream)<<(*pos)) & 0xFF)>>(8-bitno)); *pos+=bitno; bitno=0; } else { if ((8-bitno)>0) { *index+=((((**bitstream)<<(*pos)) & 0xFF)>> (8-bitno)); *pos=8; } else { *index+=(((int)(((**bitstream)<<(*pos)) & 0xFF))<< (bitno-8)); *pos=8; } bitno-=BitsLeft; } } } ================================================ FILE: deps/pjsip/third_party/ilbc/packing.h ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code packing.h Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #ifndef __PACKING_H #define __PACKING_H void packsplit( int *index, /* (i) the value to split */ int *firstpart, /* (o) the value specified by most significant bits */ int *rest, /* (o) the value specified by least significant bits */ int bitno_firstpart, /* (i) number of bits in most significant part */ int bitno_total /* (i) number of bits in full range of value */ ); void packcombine( int *index, /* (i/o) the msb value in the combined value out */ int rest, /* (i) the lsb value */ int bitno_rest /* (i) the number of bits in the lsb part */ ); void dopack( unsigned char **bitstream, /* (i/o) on entrance pointer to place in bitstream to pack new data, on exit pointer to place in bitstream to pack future data */ int index, /* (i) the value to pack */ int bitno, /* (i) the number of bits that the value will fit within */ int *pos /* (i/o) write position in the current byte */ ); void unpack( unsigned char **bitstream, /* (i/o) on entrance pointer to place in bitstream to unpack new data from, on exit pointer to place in bitstream to unpack future data from */ int *index, /* (o) resulting value */ int bitno, /* (i) number of bits used to represent the value */ int *pos /* (i/o) read position in the current byte */ ); #endif ================================================ FILE: deps/pjsip/third_party/ilbc/syntFilter.c ================================================ /****************************************************************** iLBC Speech Coder ANSI-C Source Code syntFilter.c Copyright (C) The Internet Society (2004). All Rights Reserved. ******************************************************************/ #include "iLBC_define.h" /*----------------------------------------------------------------* * LP synthesis filter. *---------------------------------------------------------------*/ void syntFilter( float *Out, /* (i/o) Signal to be filtered */ float *a, /* (i) LP parameters */ int len, /* (i) Length of signal */ float *mem /* (i/o) Filter state */ ){ int i, j; float *po, *pi, *pa, *pm; po=Out; /* Filter first part using memory from past */ for (i=0; i 9th October 2000. * It uses a number of large (4k) lookup tables to implement the * algorithm in an efficient manner. * * Note: in this implementation the State is stored in four 32-bit * words, one per column of the State, with the top byte of the * column being the _least_ significant byte of the word. * *-----------------------------------------------------------------*/ #include #if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN != 0 # define LITTLE_ENDIAN /* For INTEL architecture */ #endif typedef unsigned char u8; typedef unsigned int u32; /* Circular byte rotates of 32 bit values */ #define rot1(x) ((x << 8) | (x >> 24)) #define rot2(x) ((x << 16) | (x >> 16)) #define rot3(x) ((x << 24) | (x >> 8)) /* Extract a byte from a 32-bit u32 */ #define byte0(x) ((u8)(x)) #define byte1(x) ((u8)(x >> 8)) #define byte2(x) ((u8)(x >> 16)) #define byte3(x) ((u8)(x >> 24)) /* Put or get a 32 bit u32 (v) in machine order from a byte * * address in (x) */ #ifdef LITTLE_ENDIAN #define u32_in(x) (*(u32*)(x)) #define u32_out(x,y) (*(u32*)(x) = y) #else /* Invert byte order in a 32 bit variable */ __inline u32 byte_swap(const u32 x) { return rot1(x) & 0x00ff00ff | rot3(x) & 0xff00ff00; } __inline u32 u32_in(const u8 x[]) { return byte_swap(*(u32*)x); }; __inline void u32_out(u8 x[], const u32 v) { *(u32*)x = byte_swap(v); }; #endif /*--------------- The lookup tables ----------------------------*/ static u32 rnd_con[10] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 }; static u32 ft_tab[4][256] = { { 0xA56363C6,0x847C7CF8,0x997777EE,0x8D7B7BF6,0x0DF2F2FF,0xBD6B6BD6,0xB16F6FDE,0x54C5C591, 0x50303060,0x03010102,0xA96767CE,0x7D2B2B56,0x19FEFEE7,0x62D7D7B5,0xE6ABAB4D,0x9A7676EC, 0x45CACA8F,0x9D82821F,0x40C9C989,0x877D7DFA,0x15FAFAEF,0xEB5959B2,0xC947478E,0x0BF0F0FB, 0xECADAD41,0x67D4D4B3,0xFDA2A25F,0xEAAFAF45,0xBF9C9C23,0xF7A4A453,0x967272E4,0x5BC0C09B, 0xC2B7B775,0x1CFDFDE1,0xAE93933D,0x6A26264C,0x5A36366C,0x413F3F7E,0x02F7F7F5,0x4FCCCC83, 0x5C343468,0xF4A5A551,0x34E5E5D1,0x08F1F1F9,0x937171E2,0x73D8D8AB,0x53313162,0x3F15152A, 0x0C040408,0x52C7C795,0x65232346,0x5EC3C39D,0x28181830,0xA1969637,0x0F05050A,0xB59A9A2F, 0x0907070E,0x36121224,0x9B80801B,0x3DE2E2DF,0x26EBEBCD,0x6927274E,0xCDB2B27F,0x9F7575EA, 0x1B090912,0x9E83831D,0x742C2C58,0x2E1A1A34,0x2D1B1B36,0xB26E6EDC,0xEE5A5AB4,0xFBA0A05B, 0xF65252A4,0x4D3B3B76,0x61D6D6B7,0xCEB3B37D,0x7B292952,0x3EE3E3DD,0x712F2F5E,0x97848413, 0xF55353A6,0x68D1D1B9,0000000000,0x2CEDEDC1,0x60202040,0x1FFCFCE3,0xC8B1B179,0xED5B5BB6, 0xBE6A6AD4,0x46CBCB8D,0xD9BEBE67,0x4B393972,0xDE4A4A94,0xD44C4C98,0xE85858B0,0x4ACFCF85, 0x6BD0D0BB,0x2AEFEFC5,0xE5AAAA4F,0x16FBFBED,0xC5434386,0xD74D4D9A,0x55333366,0x94858511, 0xCF45458A,0x10F9F9E9,0x06020204,0x817F7FFE,0xF05050A0,0x443C3C78,0xBA9F9F25,0xE3A8A84B, 0xF35151A2,0xFEA3A35D,0xC0404080,0x8A8F8F05,0xAD92923F,0xBC9D9D21,0x48383870,0x04F5F5F1, 0xDFBCBC63,0xC1B6B677,0x75DADAAF,0x63212142,0x30101020,0x1AFFFFE5,0x0EF3F3FD,0x6DD2D2BF, 0x4CCDCD81,0x140C0C18,0x35131326,0x2FECECC3,0xE15F5FBE,0xA2979735,0xCC444488,0x3917172E, 0x57C4C493,0xF2A7A755,0x827E7EFC,0x473D3D7A,0xAC6464C8,0xE75D5DBA,0x2B191932,0x957373E6, 0xA06060C0,0x98818119,0xD14F4F9E,0x7FDCDCA3,0x66222244,0x7E2A2A54,0xAB90903B,0x8388880B, 0xCA46468C,0x29EEEEC7,0xD3B8B86B,0x3C141428,0x79DEDEA7,0xE25E5EBC,0x1D0B0B16,0x76DBDBAD, 0x3BE0E0DB,0x56323264,0x4E3A3A74,0x1E0A0A14,0xDB494992,0x0A06060C,0x6C242448,0xE45C5CB8, 0x5DC2C29F,0x6ED3D3BD,0xEFACAC43,0xA66262C4,0xA8919139,0xA4959531,0x37E4E4D3,0x8B7979F2, 0x32E7E7D5,0x43C8C88B,0x5937376E,0xB76D6DDA,0x8C8D8D01,0x64D5D5B1,0xD24E4E9C,0xE0A9A949, 0xB46C6CD8,0xFA5656AC,0x07F4F4F3,0x25EAEACF,0xAF6565CA,0x8E7A7AF4,0xE9AEAE47,0x18080810, 0xD5BABA6F,0x887878F0,0x6F25254A,0x722E2E5C,0x241C1C38,0xF1A6A657,0xC7B4B473,0x51C6C697, 0x23E8E8CB,0x7CDDDDA1,0x9C7474E8,0x211F1F3E,0xDD4B4B96,0xDCBDBD61,0x868B8B0D,0x858A8A0F, 0x907070E0,0x423E3E7C,0xC4B5B571,0xAA6666CC,0xD8484890,0x05030306,0x01F6F6F7,0x120E0E1C, 0xA36161C2,0x5F35356A,0xF95757AE,0xD0B9B969,0x91868617,0x58C1C199,0x271D1D3A,0xB99E9E27, 0x38E1E1D9,0x13F8F8EB,0xB398982B,0x33111122,0xBB6969D2,0x70D9D9A9,0x898E8E07,0xA7949433, 0xB69B9B2D,0x221E1E3C,0x92878715,0x20E9E9C9,0x49CECE87,0xFF5555AA,0x78282850,0x7ADFDFA5, 0x8F8C8C03,0xF8A1A159,0x80898909,0x170D0D1A,0xDABFBF65,0x31E6E6D7,0xC6424284,0xB86868D0, 0xC3414182,0xB0999929,0x772D2D5A,0x110F0F1E,0xCBB0B07B,0xFC5454A8,0xD6BBBB6D,0x3A16162C }, { 0x6363C6A5,0x7C7CF884,0x7777EE99,0x7B7BF68D,0xF2F2FF0D,0x6B6BD6BD,0x6F6FDEB1,0xC5C59154, 0x30306050,0x01010203,0x6767CEA9,0x2B2B567D,0xFEFEE719,0xD7D7B562,0xABAB4DE6,0x7676EC9A, 0xCACA8F45,0x82821F9D,0xC9C98940,0x7D7DFA87,0xFAFAEF15,0x5959B2EB,0x47478EC9,0xF0F0FB0B, 0xADAD41EC,0xD4D4B367,0xA2A25FFD,0xAFAF45EA,0x9C9C23BF,0xA4A453F7,0x7272E496,0xC0C09B5B, 0xB7B775C2,0xFDFDE11C,0x93933DAE,0x26264C6A,0x36366C5A,0x3F3F7E41,0xF7F7F502,0xCCCC834F, 0x3434685C,0xA5A551F4,0xE5E5D134,0xF1F1F908,0x7171E293,0xD8D8AB73,0x31316253,0x15152A3F, 0x0404080C,0xC7C79552,0x23234665,0xC3C39D5E,0x18183028,0x969637A1,0x05050A0F,0x9A9A2FB5, 0x07070E09,0x12122436,0x80801B9B,0xE2E2DF3D,0xEBEBCD26,0x27274E69,0xB2B27FCD,0x7575EA9F, 0x0909121B,0x83831D9E,0x2C2C5874,0x1A1A342E,0x1B1B362D,0x6E6EDCB2,0x5A5AB4EE,0xA0A05BFB, 0x5252A4F6,0x3B3B764D,0xD6D6B761,0xB3B37DCE,0x2929527B,0xE3E3DD3E,0x2F2F5E71,0x84841397, 0x5353A6F5,0xD1D1B968,0000000000,0xEDEDC12C,0x20204060,0xFCFCE31F,0xB1B179C8,0x5B5BB6ED, 0x6A6AD4BE,0xCBCB8D46,0xBEBE67D9,0x3939724B,0x4A4A94DE,0x4C4C98D4,0x5858B0E8,0xCFCF854A, 0xD0D0BB6B,0xEFEFC52A,0xAAAA4FE5,0xFBFBED16,0x434386C5,0x4D4D9AD7,0x33336655,0x85851194, 0x45458ACF,0xF9F9E910,0x02020406,0x7F7FFE81,0x5050A0F0,0x3C3C7844,0x9F9F25BA,0xA8A84BE3, 0x5151A2F3,0xA3A35DFE,0x404080C0,0x8F8F058A,0x92923FAD,0x9D9D21BC,0x38387048,0xF5F5F104, 0xBCBC63DF,0xB6B677C1,0xDADAAF75,0x21214263,0x10102030,0xFFFFE51A,0xF3F3FD0E,0xD2D2BF6D, 0xCDCD814C,0x0C0C1814,0x13132635,0xECECC32F,0x5F5FBEE1,0x979735A2,0x444488CC,0x17172E39, 0xC4C49357,0xA7A755F2,0x7E7EFC82,0x3D3D7A47,0x6464C8AC,0x5D5DBAE7,0x1919322B,0x7373E695, 0x6060C0A0,0x81811998,0x4F4F9ED1,0xDCDCA37F,0x22224466,0x2A2A547E,0x90903BAB,0x88880B83, 0x46468CCA,0xEEEEC729,0xB8B86BD3,0x1414283C,0xDEDEA779,0x5E5EBCE2,0x0B0B161D,0xDBDBAD76, 0xE0E0DB3B,0x32326456,0x3A3A744E,0x0A0A141E,0x494992DB,0x06060C0A,0x2424486C,0x5C5CB8E4, 0xC2C29F5D,0xD3D3BD6E,0xACAC43EF,0x6262C4A6,0x919139A8,0x959531A4,0xE4E4D337,0x7979F28B, 0xE7E7D532,0xC8C88B43,0x37376E59,0x6D6DDAB7,0x8D8D018C,0xD5D5B164,0x4E4E9CD2,0xA9A949E0, 0x6C6CD8B4,0x5656ACFA,0xF4F4F307,0xEAEACF25,0x6565CAAF,0x7A7AF48E,0xAEAE47E9,0x08081018, 0xBABA6FD5,0x7878F088,0x25254A6F,0x2E2E5C72,0x1C1C3824,0xA6A657F1,0xB4B473C7,0xC6C69751, 0xE8E8CB23,0xDDDDA17C,0x7474E89C,0x1F1F3E21,0x4B4B96DD,0xBDBD61DC,0x8B8B0D86,0x8A8A0F85, 0x7070E090,0x3E3E7C42,0xB5B571C4,0x6666CCAA,0x484890D8,0x03030605,0xF6F6F701,0x0E0E1C12, 0x6161C2A3,0x35356A5F,0x5757AEF9,0xB9B969D0,0x86861791,0xC1C19958,0x1D1D3A27,0x9E9E27B9, 0xE1E1D938,0xF8F8EB13,0x98982BB3,0x11112233,0x6969D2BB,0xD9D9A970,0x8E8E0789,0x949433A7, 0x9B9B2DB6,0x1E1E3C22,0x87871592,0xE9E9C920,0xCECE8749,0x5555AAFF,0x28285078,0xDFDFA57A, 0x8C8C038F,0xA1A159F8,0x89890980,0x0D0D1A17,0xBFBF65DA,0xE6E6D731,0x424284C6,0x6868D0B8, 0x414182C3,0x999929B0,0x2D2D5A77,0x0F0F1E11,0xB0B07BCB,0x5454A8FC,0xBBBB6DD6,0x16162C3A }, { 0x63C6A563,0x7CF8847C,0x77EE9977,0x7BF68D7B,0xF2FF0DF2,0x6BD6BD6B,0x6FDEB16F,0xC59154C5, 0x30605030,0x01020301,0x67CEA967,0x2B567D2B,0xFEE719FE,0xD7B562D7,0xAB4DE6AB,0x76EC9A76, 0xCA8F45CA,0x821F9D82,0xC98940C9,0x7DFA877D,0xFAEF15FA,0x59B2EB59,0x478EC947,0xF0FB0BF0, 0xAD41ECAD,0xD4B367D4,0xA25FFDA2,0xAF45EAAF,0x9C23BF9C,0xA453F7A4,0x72E49672,0xC09B5BC0, 0xB775C2B7,0xFDE11CFD,0x933DAE93,0x264C6A26,0x366C5A36,0x3F7E413F,0xF7F502F7,0xCC834FCC, 0x34685C34,0xA551F4A5,0xE5D134E5,0xF1F908F1,0x71E29371,0xD8AB73D8,0x31625331,0x152A3F15, 0x04080C04,0xC79552C7,0x23466523,0xC39D5EC3,0x18302818,0x9637A196,0x050A0F05,0x9A2FB59A, 0x070E0907,0x12243612,0x801B9B80,0xE2DF3DE2,0xEBCD26EB,0x274E6927,0xB27FCDB2,0x75EA9F75, 0x09121B09,0x831D9E83,0x2C58742C,0x1A342E1A,0x1B362D1B,0x6EDCB26E,0x5AB4EE5A,0xA05BFBA0, 0x52A4F652,0x3B764D3B,0xD6B761D6,0xB37DCEB3,0x29527B29,0xE3DD3EE3,0x2F5E712F,0x84139784, 0x53A6F553,0xD1B968D1,0000000000,0xEDC12CED,0x20406020,0xFCE31FFC,0xB179C8B1,0x5BB6ED5B, 0x6AD4BE6A,0xCB8D46CB,0xBE67D9BE,0x39724B39,0x4A94DE4A,0x4C98D44C,0x58B0E858,0xCF854ACF, 0xD0BB6BD0,0xEFC52AEF,0xAA4FE5AA,0xFBED16FB,0x4386C543,0x4D9AD74D,0x33665533,0x85119485, 0x458ACF45,0xF9E910F9,0x02040602,0x7FFE817F,0x50A0F050,0x3C78443C,0x9F25BA9F,0xA84BE3A8, 0x51A2F351,0xA35DFEA3,0x4080C040,0x8F058A8F,0x923FAD92,0x9D21BC9D,0x38704838,0xF5F104F5, 0xBC63DFBC,0xB677C1B6,0xDAAF75DA,0x21426321,0x10203010,0xFFE51AFF,0xF3FD0EF3,0xD2BF6DD2, 0xCD814CCD,0x0C18140C,0x13263513,0xECC32FEC,0x5FBEE15F,0x9735A297,0x4488CC44,0x172E3917, 0xC49357C4,0xA755F2A7,0x7EFC827E,0x3D7A473D,0x64C8AC64,0x5DBAE75D,0x19322B19,0x73E69573, 0x60C0A060,0x81199881,0x4F9ED14F,0xDCA37FDC,0x22446622,0x2A547E2A,0x903BAB90,0x880B8388, 0x468CCA46,0xEEC729EE,0xB86BD3B8,0x14283C14,0xDEA779DE,0x5EBCE25E,0x0B161D0B,0xDBAD76DB, 0xE0DB3BE0,0x32645632,0x3A744E3A,0x0A141E0A,0x4992DB49,0x060C0A06,0x24486C24,0x5CB8E45C, 0xC29F5DC2,0xD3BD6ED3,0xAC43EFAC,0x62C4A662,0x9139A891,0x9531A495,0xE4D337E4,0x79F28B79, 0xE7D532E7,0xC88B43C8,0x376E5937,0x6DDAB76D,0x8D018C8D,0xD5B164D5,0x4E9CD24E,0xA949E0A9, 0x6CD8B46C,0x56ACFA56,0xF4F307F4,0xEACF25EA,0x65CAAF65,0x7AF48E7A,0xAE47E9AE,0x08101808, 0xBA6FD5BA,0x78F08878,0x254A6F25,0x2E5C722E,0x1C38241C,0xA657F1A6,0xB473C7B4,0xC69751C6, 0xE8CB23E8,0xDDA17CDD,0x74E89C74,0x1F3E211F,0x4B96DD4B,0xBD61DCBD,0x8B0D868B,0x8A0F858A, 0x70E09070,0x3E7C423E,0xB571C4B5,0x66CCAA66,0x4890D848,0x03060503,0xF6F701F6,0x0E1C120E, 0x61C2A361,0x356A5F35,0x57AEF957,0xB969D0B9,0x86179186,0xC19958C1,0x1D3A271D,0x9E27B99E, 0xE1D938E1,0xF8EB13F8,0x982BB398,0x11223311,0x69D2BB69,0xD9A970D9,0x8E07898E,0x9433A794, 0x9B2DB69B,0x1E3C221E,0x87159287,0xE9C920E9,0xCE8749CE,0x55AAFF55,0x28507828,0xDFA57ADF, 0x8C038F8C,0xA159F8A1,0x89098089,0x0D1A170D,0xBF65DABF,0xE6D731E6,0x4284C642,0x68D0B868, 0x4182C341,0x9929B099,0x2D5A772D,0x0F1E110F,0xB07BCBB0,0x54A8FC54,0xBB6DD6BB,0x162C3A16 }, { 0xC6A56363,0xF8847C7C,0xEE997777,0xF68D7B7B,0xFF0DF2F2,0xD6BD6B6B,0xDEB16F6F,0x9154C5C5, 0x60503030,0x02030101,0xCEA96767,0x567D2B2B,0xE719FEFE,0xB562D7D7,0x4DE6ABAB,0xEC9A7676, 0x8F45CACA,0x1F9D8282,0x8940C9C9,0xFA877D7D,0xEF15FAFA,0xB2EB5959,0x8EC94747,0xFB0BF0F0, 0x41ECADAD,0xB367D4D4,0x5FFDA2A2,0x45EAAFAF,0x23BF9C9C,0x53F7A4A4,0xE4967272,0x9B5BC0C0, 0x75C2B7B7,0xE11CFDFD,0x3DAE9393,0x4C6A2626,0x6C5A3636,0x7E413F3F,0xF502F7F7,0x834FCCCC, 0x685C3434,0x51F4A5A5,0xD134E5E5,0xF908F1F1,0xE2937171,0xAB73D8D8,0x62533131,0x2A3F1515, 0x080C0404,0x9552C7C7,0x46652323,0x9D5EC3C3,0x30281818,0x37A19696,0x0A0F0505,0x2FB59A9A, 0x0E090707,0x24361212,0x1B9B8080,0xDF3DE2E2,0xCD26EBEB,0x4E692727,0x7FCDB2B2,0xEA9F7575, 0x121B0909,0x1D9E8383,0x58742C2C,0x342E1A1A,0x362D1B1B,0xDCB26E6E,0xB4EE5A5A,0x5BFBA0A0, 0xA4F65252,0x764D3B3B,0xB761D6D6,0x7DCEB3B3,0x527B2929,0xDD3EE3E3,0x5E712F2F,0x13978484, 0xA6F55353,0xB968D1D1,0000000000,0xC12CEDED,0x40602020,0xE31FFCFC,0x79C8B1B1,0xB6ED5B5B, 0xD4BE6A6A,0x8D46CBCB,0x67D9BEBE,0x724B3939,0x94DE4A4A,0x98D44C4C,0xB0E85858,0x854ACFCF, 0xBB6BD0D0,0xC52AEFEF,0x4FE5AAAA,0xED16FBFB,0x86C54343,0x9AD74D4D,0x66553333,0x11948585, 0x8ACF4545,0xE910F9F9,0x04060202,0xFE817F7F,0xA0F05050,0x78443C3C,0x25BA9F9F,0x4BE3A8A8, 0xA2F35151,0x5DFEA3A3,0x80C04040,0x058A8F8F,0x3FAD9292,0x21BC9D9D,0x70483838,0xF104F5F5, 0x63DFBCBC,0x77C1B6B6,0xAF75DADA,0x42632121,0x20301010,0xE51AFFFF,0xFD0EF3F3,0xBF6DD2D2, 0x814CCDCD,0x18140C0C,0x26351313,0xC32FECEC,0xBEE15F5F,0x35A29797,0x88CC4444,0x2E391717, 0x9357C4C4,0x55F2A7A7,0xFC827E7E,0x7A473D3D,0xC8AC6464,0xBAE75D5D,0x322B1919,0xE6957373, 0xC0A06060,0x19988181,0x9ED14F4F,0xA37FDCDC,0x44662222,0x547E2A2A,0x3BAB9090,0x0B838888, 0x8CCA4646,0xC729EEEE,0x6BD3B8B8,0x283C1414,0xA779DEDE,0xBCE25E5E,0x161D0B0B,0xAD76DBDB, 0xDB3BE0E0,0x64563232,0x744E3A3A,0x141E0A0A,0x92DB4949,0x0C0A0606,0x486C2424,0xB8E45C5C, 0x9F5DC2C2,0xBD6ED3D3,0x43EFACAC,0xC4A66262,0x39A89191,0x31A49595,0xD337E4E4,0xF28B7979, 0xD532E7E7,0x8B43C8C8,0x6E593737,0xDAB76D6D,0x018C8D8D,0xB164D5D5,0x9CD24E4E,0x49E0A9A9, 0xD8B46C6C,0xACFA5656,0xF307F4F4,0xCF25EAEA,0xCAAF6565,0xF48E7A7A,0x47E9AEAE,0x10180808, 0x6FD5BABA,0xF0887878,0x4A6F2525,0x5C722E2E,0x38241C1C,0x57F1A6A6,0x73C7B4B4,0x9751C6C6, 0xCB23E8E8,0xA17CDDDD,0xE89C7474,0x3E211F1F,0x96DD4B4B,0x61DCBDBD,0x0D868B8B,0x0F858A8A, 0xE0907070,0x7C423E3E,0x71C4B5B5,0xCCAA6666,0x90D84848,0x06050303,0xF701F6F6,0x1C120E0E, 0xC2A36161,0x6A5F3535,0xAEF95757,0x69D0B9B9,0x17918686,0x9958C1C1,0x3A271D1D,0x27B99E9E, 0xD938E1E1,0xEB13F8F8,0x2BB39898,0x22331111,0xD2BB6969,0xA970D9D9,0x07898E8E,0x33A79494, 0x2DB69B9B,0x3C221E1E,0x15928787,0xC920E9E9,0x8749CECE,0xAAFF5555,0x50782828,0xA57ADFDF, 0x038F8C8C,0x59F8A1A1,0x09808989,0x1A170D0D,0x65DABFBF,0xD731E6E6,0x84C64242,0xD0B86868, 0x82C34141,0x29B09999,0x5A772D2D,0x1E110F0F,0x7BCBB0B0,0xA8FC5454,0x6DD6BBBB,0x2C3A1616 } }; static u32 fl_tab[4][256] = { { 0x00000063,0x0000007C,0x00000077,0x0000007B,0x000000F2,0x0000006B,0x0000006F,0x000000C5, 0x00000030,0x00000001,0x00000067,0x0000002B,0x000000FE,0x000000D7,0x000000AB,0x00000076, 0x000000CA,0x00000082,0x000000C9,0x0000007D,0x000000FA,0x00000059,0x00000047,0x000000F0, 0x000000AD,0x000000D4,0x000000A2,0x000000AF,0x0000009C,0x000000A4,0x00000072,0x000000C0, 0x000000B7,0x000000FD,0x00000093,0x00000026,0x00000036,0x0000003F,0x000000F7,0x000000CC, 0x00000034,0x000000A5,0x000000E5,0x000000F1,0x00000071,0x000000D8,0x00000031,0x00000015, 0x00000004,0x000000C7,0x00000023,0x000000C3,0x00000018,0x00000096,0x00000005,0x0000009A, 0x00000007,0x00000012,0x00000080,0x000000E2,0x000000EB,0x00000027,0x000000B2,0x00000075, 0x00000009,0x00000083,0x0000002C,0x0000001A,0x0000001B,0x0000006E,0x0000005A,0x000000A0, 0x00000052,0x0000003B,0x000000D6,0x000000B3,0x00000029,0x000000E3,0x0000002F,0x00000084, 0x00000053,0x000000D1,0x00000000,0x000000ED,0x00000020,0x000000FC,0x000000B1,0x0000005B, 0x0000006A,0x000000CB,0x000000BE,0x00000039,0x0000004A,0x0000004C,0x00000058,0x000000CF, 0x000000D0,0x000000EF,0x000000AA,0x000000FB,0x00000043,0x0000004D,0x00000033,0x00000085, 0x00000045,0x000000F9,0x00000002,0x0000007F,0x00000050,0x0000003C,0x0000009F,0x000000A8, 0x00000051,0x000000A3,0x00000040,0x0000008F,0x00000092,0x0000009D,0x00000038,0x000000F5, 0x000000BC,0x000000B6,0x000000DA,0x00000021,0x00000010,0x000000FF,0x000000F3,0x000000D2, 0x000000CD,0x0000000C,0x00000013,0x000000EC,0x0000005F,0x00000097,0x00000044,0x00000017, 0x000000C4,0x000000A7,0x0000007E,0x0000003D,0x00000064,0x0000005D,0x00000019,0x00000073, 0x00000060,0x00000081,0x0000004F,0x000000DC,0x00000022,0x0000002A,0x00000090,0x00000088, 0x00000046,0x000000EE,0x000000B8,0x00000014,0x000000DE,0x0000005E,0x0000000B,0x000000DB, 0x000000E0,0x00000032,0x0000003A,0x0000000A,0x00000049,0x00000006,0x00000024,0x0000005C, 0x000000C2,0x000000D3,0x000000AC,0x00000062,0x00000091,0x00000095,0x000000E4,0x00000079, 0x000000E7,0x000000C8,0x00000037,0x0000006D,0x0000008D,0x000000D5,0x0000004E,0x000000A9, 0x0000006C,0x00000056,0x000000F4,0x000000EA,0x00000065,0x0000007A,0x000000AE,0x00000008, 0x000000BA,0x00000078,0x00000025,0x0000002E,0x0000001C,0x000000A6,0x000000B4,0x000000C6, 0x000000E8,0x000000DD,0x00000074,0x0000001F,0x0000004B,0x000000BD,0x0000008B,0x0000008A, 0x00000070,0x0000003E,0x000000B5,0x00000066,0x00000048,0x00000003,0x000000F6,0x0000000E, 0x00000061,0x00000035,0x00000057,0x000000B9,0x00000086,0x000000C1,0x0000001D,0x0000009E, 0x000000E1,0x000000F8,0x00000098,0x00000011,0x00000069,0x000000D9,0x0000008E,0x00000094, 0x0000009B,0x0000001E,0x00000087,0x000000E9,0x000000CE,0x00000055,0x00000028,0x000000DF, 0x0000008C,0x000000A1,0x00000089,0x0000000D,0x000000BF,0x000000E6,0x00000042,0x00000068, 0x00000041,0x00000099,0x0000002D,0x0000000F,0x000000B0,0x00000054,0x000000BB,0x00000016 }, { 0x00006300,0x00007C00,0x00007700,0x00007B00,0x0000F200,0x00006B00,0x00006F00,0x0000C500, 0x00003000,0x00000100,0x00006700,0x00002B00,0x0000FE00,0x0000D700,0x0000AB00,0x00007600, 0x0000CA00,0x00008200,0x0000C900,0x00007D00,0x0000FA00,0x00005900,0x00004700,0x0000F000, 0x0000AD00,0x0000D400,0x0000A200,0x0000AF00,0x00009C00,0x0000A400,0x00007200,0x0000C000, 0x0000B700,0x0000FD00,0x00009300,0x00002600,0x00003600,0x00003F00,0x0000F700,0x0000CC00, 0x00003400,0x0000A500,0x0000E500,0x0000F100,0x00007100,0x0000D800,0x00003100,0x00001500, 0x00000400,0x0000C700,0x00002300,0x0000C300,0x00001800,0x00009600,0x00000500,0x00009A00, 0x00000700,0x00001200,0x00008000,0x0000E200,0x0000EB00,0x00002700,0x0000B200,0x00007500, 0x00000900,0x00008300,0x00002C00,0x00001A00,0x00001B00,0x00006E00,0x00005A00,0x0000A000, 0x00005200,0x00003B00,0x0000D600,0x0000B300,0x00002900,0x0000E300,0x00002F00,0x00008400, 0x00005300,0x0000D100,0000000000,0x0000ED00,0x00002000,0x0000FC00,0x0000B100,0x00005B00, 0x00006A00,0x0000CB00,0x0000BE00,0x00003900,0x00004A00,0x00004C00,0x00005800,0x0000CF00, 0x0000D000,0x0000EF00,0x0000AA00,0x0000FB00,0x00004300,0x00004D00,0x00003300,0x00008500, 0x00004500,0x0000F900,0x00000200,0x00007F00,0x00005000,0x00003C00,0x00009F00,0x0000A800, 0x00005100,0x0000A300,0x00004000,0x00008F00,0x00009200,0x00009D00,0x00003800,0x0000F500, 0x0000BC00,0x0000B600,0x0000DA00,0x00002100,0x00001000,0x0000FF00,0x0000F300,0x0000D200, 0x0000CD00,0x00000C00,0x00001300,0x0000EC00,0x00005F00,0x00009700,0x00004400,0x00001700, 0x0000C400,0x0000A700,0x00007E00,0x00003D00,0x00006400,0x00005D00,0x00001900,0x00007300, 0x00006000,0x00008100,0x00004F00,0x0000DC00,0x00002200,0x00002A00,0x00009000,0x00008800, 0x00004600,0x0000EE00,0x0000B800,0x00001400,0x0000DE00,0x00005E00,0x00000B00,0x0000DB00, 0x0000E000,0x00003200,0x00003A00,0x00000A00,0x00004900,0x00000600,0x00002400,0x00005C00, 0x0000C200,0x0000D300,0x0000AC00,0x00006200,0x00009100,0x00009500,0x0000E400,0x00007900, 0x0000E700,0x0000C800,0x00003700,0x00006D00,0x00008D00,0x0000D500,0x00004E00,0x0000A900, 0x00006C00,0x00005600,0x0000F400,0x0000EA00,0x00006500,0x00007A00,0x0000AE00,0x00000800, 0x0000BA00,0x00007800,0x00002500,0x00002E00,0x00001C00,0x0000A600,0x0000B400,0x0000C600, 0x0000E800,0x0000DD00,0x00007400,0x00001F00,0x00004B00,0x0000BD00,0x00008B00,0x00008A00, 0x00007000,0x00003E00,0x0000B500,0x00006600,0x00004800,0x00000300,0x0000F600,0x00000E00, 0x00006100,0x00003500,0x00005700,0x0000B900,0x00008600,0x0000C100,0x00001D00,0x00009E00, 0x0000E100,0x0000F800,0x00009800,0x00001100,0x00006900,0x0000D900,0x00008E00,0x00009400, 0x00009B00,0x00001E00,0x00008700,0x0000E900,0x0000CE00,0x00005500,0x00002800,0x0000DF00, 0x00008C00,0x0000A100,0x00008900,0x00000D00,0x0000BF00,0x0000E600,0x00004200,0x00006800, 0x00004100,0x00009900,0x00002D00,0x00000F00,0x0000B000,0x00005400,0x0000BB00,0x00001600 }, { 0x00630000,0x007C0000,0x00770000,0x007B0000,0x00F20000,0x006B0000,0x006F0000,0x00C50000, 0x00300000,0x00010000,0x00670000,0x002B0000,0x00FE0000,0x00D70000,0x00AB0000,0x00760000, 0x00CA0000,0x00820000,0x00C90000,0x007D0000,0x00FA0000,0x00590000,0x00470000,0x00F00000, 0x00AD0000,0x00D40000,0x00A20000,0x00AF0000,0x009C0000,0x00A40000,0x00720000,0x00C00000, 0x00B70000,0x00FD0000,0x00930000,0x00260000,0x00360000,0x003F0000,0x00F70000,0x00CC0000, 0x00340000,0x00A50000,0x00E50000,0x00F10000,0x00710000,0x00D80000,0x00310000,0x00150000, 0x00040000,0x00C70000,0x00230000,0x00C30000,0x00180000,0x00960000,0x00050000,0x009A0000, 0x00070000,0x00120000,0x00800000,0x00E20000,0x00EB0000,0x00270000,0x00B20000,0x00750000, 0x00090000,0x00830000,0x002C0000,0x001A0000,0x001B0000,0x006E0000,0x005A0000,0x00A00000, 0x00520000,0x003B0000,0x00D60000,0x00B30000,0x00290000,0x00E30000,0x002F0000,0x00840000, 0x00530000,0x00D10000,0000000000,0x00ED0000,0x00200000,0x00FC0000,0x00B10000,0x005B0000, 0x006A0000,0x00CB0000,0x00BE0000,0x00390000,0x004A0000,0x004C0000,0x00580000,0x00CF0000, 0x00D00000,0x00EF0000,0x00AA0000,0x00FB0000,0x00430000,0x004D0000,0x00330000,0x00850000, 0x00450000,0x00F90000,0x00020000,0x007F0000,0x00500000,0x003C0000,0x009F0000,0x00A80000, 0x00510000,0x00A30000,0x00400000,0x008F0000,0x00920000,0x009D0000,0x00380000,0x00F50000, 0x00BC0000,0x00B60000,0x00DA0000,0x00210000,0x00100000,0x00FF0000,0x00F30000,0x00D20000, 0x00CD0000,0x000C0000,0x00130000,0x00EC0000,0x005F0000,0x00970000,0x00440000,0x00170000, 0x00C40000,0x00A70000,0x007E0000,0x003D0000,0x00640000,0x005D0000,0x00190000,0x00730000, 0x00600000,0x00810000,0x004F0000,0x00DC0000,0x00220000,0x002A0000,0x00900000,0x00880000, 0x00460000,0x00EE0000,0x00B80000,0x00140000,0x00DE0000,0x005E0000,0x000B0000,0x00DB0000, 0x00E00000,0x00320000,0x003A0000,0x000A0000,0x00490000,0x00060000,0x00240000,0x005C0000, 0x00C20000,0x00D30000,0x00AC0000,0x00620000,0x00910000,0x00950000,0x00E40000,0x00790000, 0x00E70000,0x00C80000,0x00370000,0x006D0000,0x008D0000,0x00D50000,0x004E0000,0x00A90000, 0x006C0000,0x00560000,0x00F40000,0x00EA0000,0x00650000,0x007A0000,0x00AE0000,0x00080000, 0x00BA0000,0x00780000,0x00250000,0x002E0000,0x001C0000,0x00A60000,0x00B40000,0x00C60000, 0x00E80000,0x00DD0000,0x00740000,0x001F0000,0x004B0000,0x00BD0000,0x008B0000,0x008A0000, 0x00700000,0x003E0000,0x00B50000,0x00660000,0x00480000,0x00030000,0x00F60000,0x000E0000, 0x00610000,0x00350000,0x00570000,0x00B90000,0x00860000,0x00C10000,0x001D0000,0x009E0000, 0x00E10000,0x00F80000,0x00980000,0x00110000,0x00690000,0x00D90000,0x008E0000,0x00940000, 0x009B0000,0x001E0000,0x00870000,0x00E90000,0x00CE0000,0x00550000,0x00280000,0x00DF0000, 0x008C0000,0x00A10000,0x00890000,0x000D0000,0x00BF0000,0x00E60000,0x00420000,0x00680000, 0x00410000,0x00990000,0x002D0000,0x000F0000,0x00B00000,0x00540000,0x00BB0000,0x00160000 }, { 0x63000000,0x7C000000,0x77000000,0x7B000000,0xF2000000,0x6B000000,0x6F000000,0xC5000000, 0x30000000,0x01000000,0x67000000,0x2B000000,0xFE000000,0xD7000000,0xAB000000,0x76000000, 0xCA000000,0x82000000,0xC9000000,0x7D000000,0xFA000000,0x59000000,0x47000000,0xF0000000, 0xAD000000,0xD4000000,0xA2000000,0xAF000000,0x9C000000,0xA4000000,0x72000000,0xC0000000, 0xB7000000,0xFD000000,0x93000000,0x26000000,0x36000000,0x3F000000,0xF7000000,0xCC000000, 0x34000000,0xA5000000,0xE5000000,0xF1000000,0x71000000,0xD8000000,0x31000000,0x15000000, 0x04000000,0xC7000000,0x23000000,0xC3000000,0x18000000,0x96000000,0x05000000,0x9A000000, 0x07000000,0x12000000,0x80000000,0xE2000000,0xEB000000,0x27000000,0xB2000000,0x75000000, 0x09000000,0x83000000,0x2C000000,0x1A000000,0x1B000000,0x6E000000,0x5A000000,0xA0000000, 0x52000000,0x3B000000,0xD6000000,0xB3000000,0x29000000,0xE3000000,0x2F000000,0x84000000, 0x53000000,0xD1000000,0000000000,0xED000000,0x20000000,0xFC000000,0xB1000000,0x5B000000, 0x6A000000,0xCB000000,0xBE000000,0x39000000,0x4A000000,0x4C000000,0x58000000,0xCF000000, 0xD0000000,0xEF000000,0xAA000000,0xFB000000,0x43000000,0x4D000000,0x33000000,0x85000000, 0x45000000,0xF9000000,0x02000000,0x7F000000,0x50000000,0x3C000000,0x9F000000,0xA8000000, 0x51000000,0xA3000000,0x40000000,0x8F000000,0x92000000,0x9D000000,0x38000000,0xF5000000, 0xBC000000,0xB6000000,0xDA000000,0x21000000,0x10000000,0xFF000000,0xF3000000,0xD2000000, 0xCD000000,0x0C000000,0x13000000,0xEC000000,0x5F000000,0x97000000,0x44000000,0x17000000, 0xC4000000,0xA7000000,0x7E000000,0x3D000000,0x64000000,0x5D000000,0x19000000,0x73000000, 0x60000000,0x81000000,0x4F000000,0xDC000000,0x22000000,0x2A000000,0x90000000,0x88000000, 0x46000000,0xEE000000,0xB8000000,0x14000000,0xDE000000,0x5E000000,0x0B000000,0xDB000000, 0xE0000000,0x32000000,0x3A000000,0x0A000000,0x49000000,0x06000000,0x24000000,0x5C000000, 0xC2000000,0xD3000000,0xAC000000,0x62000000,0x91000000,0x95000000,0xE4000000,0x79000000, 0xE7000000,0xC8000000,0x37000000,0x6D000000,0x8D000000,0xD5000000,0x4E000000,0xA9000000, 0x6C000000,0x56000000,0xF4000000,0xEA000000,0x65000000,0x7A000000,0xAE000000,0x08000000, 0xBA000000,0x78000000,0x25000000,0x2E000000,0x1C000000,0xA6000000,0xB4000000,0xC6000000, 0xE8000000,0xDD000000,0x74000000,0x1F000000,0x4B000000,0xBD000000,0x8B000000,0x8A000000, 0x70000000,0x3E000000,0xB5000000,0x66000000,0x48000000,0x03000000,0xF6000000,0x0E000000, 0x61000000,0x35000000,0x57000000,0xB9000000,0x86000000,0xC1000000,0x1D000000,0x9E000000, 0xE1000000,0xF8000000,0x98000000,0x11000000,0x69000000,0xD9000000,0x8E000000,0x94000000, 0x9B000000,0x1E000000,0x87000000,0xE9000000,0xCE000000,0x55000000,0x28000000,0xDF000000, 0x8C000000,0xA1000000,0x89000000,0x0D000000,0xBF000000,0xE6000000,0x42000000,0x68000000, 0x41000000,0x99000000,0x2D000000,0x0F000000,0xB0000000,0x54000000,0xBB000000,0x16000000 } }; /*----------------- The workspace ------------------------------*/ static u32 Ekey[44]; /* The expanded key */ /*------ The round Function. 4 table lookups and 4 Exors ------*/ #define f_rnd(x, n) \ ( ft_tab[0][byte0(x[n])] \ ^ ft_tab[1][byte1(x[(n + 1) & 3])] \ ^ ft_tab[2][byte2(x[(n + 2) & 3])] \ ^ ft_tab[3][byte3(x[(n + 3) & 3])] ) #define f_round(bo, bi, k) \ bo[0] = f_rnd(bi, 0) ^ k[0]; \ bo[1] = f_rnd(bi, 1) ^ k[1]; \ bo[2] = f_rnd(bi, 2) ^ k[2]; \ bo[3] = f_rnd(bi, 3) ^ k[3]; \ k += 4 /*--- The S Box lookup used in constructing the Key schedule ---*/ #define ls_box(x) \ ( fl_tab[0][byte0(x)] \ ^ fl_tab[1][byte1(x)] \ ^ fl_tab[2][byte2(x)] \ ^ fl_tab[3][byte3(x)] ) /*------------ The last round function (no MixColumn) ----------*/ #define lf_rnd(x, n) \ ( fl_tab[0][byte0(x[n])] \ ^ fl_tab[1][byte1(x[(n + 1) & 3])] \ ^ fl_tab[2][byte2(x[(n + 2) & 3])] \ ^ fl_tab[3][byte3(x[(n + 3) & 3])] ) /*----------------------------------------------------------- * RijndaelKeySchedule * Initialise the key schedule from a supplied key */ void RijndaelKeySchedule(u8 key[16]) { u32 t; u32 *ek=Ekey, /* pointer to the expanded key */ *rc=rnd_con; /* pointer to the round constant */ Ekey[0] = u32_in(key ); Ekey[1] = u32_in(key + 4); Ekey[2] = u32_in(key + 8); Ekey[3] = u32_in(key + 12); while(ek < Ekey + 40) { t = rot3(ek[3]); ek[4] = ek[0] ^ ls_box(t) ^ *rc++; ek[5] = ek[1] ^ ek[4]; ek[6] = ek[2] ^ ek[5]; ek[7] = ek[3] ^ ek[6]; ek += 4; } } /*----------------------------------------------------------- * RijndaelEncrypt * Encrypt an input block */ void RijndaelEncrypt(u8 in[16], u8 out[16]) { u32 b0[4], b1[4], *kp = Ekey; b0[0] = u32_in(in ) ^ *kp++; b0[1] = u32_in(in + 4) ^ *kp++; b0[2] = u32_in(in + 8) ^ *kp++; b0[3] = u32_in(in + 12) ^ *kp++; f_round(b1, b0, kp); f_round(b0, b1, kp); f_round(b1, b0, kp); f_round(b0, b1, kp); f_round(b1, b0, kp); f_round(b0, b1, kp); f_round(b1, b0, kp); f_round(b0, b1, kp); f_round(b1, b0, kp); u32_out(out, lf_rnd(b1, 0) ^ kp[0]); u32_out(out + 4, lf_rnd(b1, 1) ^ kp[1]); u32_out(out + 8, lf_rnd(b1, 2) ^ kp[2]); u32_out(out + 12, lf_rnd(b1, 3) ^ kp[3]); } ================================================ FILE: deps/pjsip/third_party/milenage/rijndael.h ================================================ /*------------------------------------------------------------------- * Example algorithms f1, f1*, f2, f3, f4, f5, f5* *------------------------------------------------------------------- * * A sample implementation of the example 3GPP authentication and * key agreement functions f1, f1*, f2, f3, f4, f5 and f5*. This is * a byte-oriented implementation of the functions, and of the block * cipher kernel function Rijndael. * * This has been coded for clarity, not necessarily for efficiency. * * The functions f2, f3, f4 and f5 share the same inputs and have * been coded together as a single function. f1, f1* and f5* are * all coded separately. * *-----------------------------------------------------------------*/ #ifndef RIJNDAEL_H #define RIJNDAEL_H void RijndaelKeySchedule( u8 key[16] ); void RijndaelEncrypt( u8 input[16], u8 output[16] ); #endif ================================================ FILE: deps/pjsip/third_party/mp3/BladeMP3EncDLL.h ================================================ /* * Blade Type of DLL Interface for Lame encoder * * Copyright (c) 1999-2002 A.L. Faber * Based on bladedll.h version 1.0 written by Jukka Poikolainen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ //#define _BLADEDLL 1 #ifndef ___BLADEDLL_H_INCLUDED___ #define ___BLADEDLL_H_INCLUDED___ #ifdef __GNUC__ //#define ATTRIBUTE_PACKED __attribute__((packed)) #define ATTRIBUTE_PACKED #else #define ATTRIBUTE_PACKED #pragma pack(push) #pragma pack(1) #endif #ifdef __cplusplus extern "C" { #endif /* encoding formats */ #define BE_CONFIG_MP3 0 #define BE_CONFIG_LAME 256 /* type definitions */ typedef unsigned long HBE_STREAM; typedef HBE_STREAM *PHBE_STREAM; typedef unsigned long BE_ERR; /* error codes */ #define BE_ERR_SUCCESSFUL 0x00000000 #define BE_ERR_INVALID_FORMAT 0x00000001 #define BE_ERR_INVALID_FORMAT_PARAMETERS 0x00000002 #define BE_ERR_NO_MORE_HANDLES 0x00000003 #define BE_ERR_INVALID_HANDLE 0x00000004 #define BE_ERR_BUFFER_TOO_SMALL 0x00000005 /* other constants */ #define BE_MAX_HOMEPAGE 128 /* format specific variables */ #define BE_MP3_MODE_STEREO 0 #define BE_MP3_MODE_JSTEREO 1 #define BE_MP3_MODE_DUALCHANNEL 2 #define BE_MP3_MODE_MONO 3 #define MPEG1 1 #define MPEG2 0 #ifdef _BLADEDLL #undef FLOAT #include #endif #define CURRENT_STRUCT_VERSION 1 #define CURRENT_STRUCT_SIZE sizeof(BE_CONFIG) // is currently 331 bytes typedef enum { VBR_METHOD_NONE = -1, VBR_METHOD_DEFAULT = 0, VBR_METHOD_OLD = 1, VBR_METHOD_NEW = 2, VBR_METHOD_MTRH = 3, VBR_METHOD_ABR = 4 } VBRMETHOD; typedef enum { LQP_NOPRESET =-1, // QUALITY PRESETS LQP_NORMAL_QUALITY = 0, LQP_LOW_QUALITY = 1, LQP_HIGH_QUALITY = 2, LQP_VOICE_QUALITY = 3, LQP_R3MIX = 4, LQP_VERYHIGH_QUALITY = 5, LQP_STANDARD = 6, LQP_FAST_STANDARD = 7, LQP_EXTREME = 8, LQP_FAST_EXTREME = 9, LQP_INSANE = 10, LQP_ABR = 11, LQP_CBR = 12, LQP_MEDIUM = 13, LQP_FAST_MEDIUM = 14, // NEW PRESET VALUES LQP_PHONE =1000, LQP_SW =2000, LQP_AM =3000, LQP_FM =4000, LQP_VOICE =5000, LQP_RADIO =6000, LQP_TAPE =7000, LQP_HIFI =8000, LQP_CD =9000, LQP_STUDIO =10000 } LAME_QUALITY_PRESET; typedef struct { unsigned long dwConfig; // BE_CONFIG_XXXXX // Currently only BE_CONFIG_MP3 is supported union { struct { unsigned long dwSampleRate; // 48000, 44100 and 32000 allowed unsigned char byMode; // BE_MP3_MODE_STEREO, BE_MP3_MODE_DUALCHANNEL, BE_MP3_MODE_MONO unsigned short wBitrate; // 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256 and 320 allowed int bPrivate; int bCRC; int bCopyright; int bOriginal; } mp3; // BE_CONFIG_MP3 struct { // STRUCTURE INFORMATION unsigned long dwStructVersion; unsigned long dwStructSize; // BASIC ENCODER SETTINGS unsigned long dwSampleRate; // SAMPLERATE OF INPUT FILE unsigned long dwReSampleRate; // DOWNSAMPLERATE, 0=ENCODER DECIDES long nMode; // BE_MP3_MODE_STEREO, BE_MP3_MODE_DUALCHANNEL, BE_MP3_MODE_MONO unsigned long dwBitrate; // CBR bitrate, VBR min bitrate unsigned long dwMaxBitrate; // CBR ignored, VBR Max bitrate long nPreset; // Quality preset, use one of the settings of the LAME_QUALITY_PRESET enum unsigned long dwMpegVersion; // FUTURE USE, MPEG-1 OR MPEG-2 unsigned long dwPsyModel; // FUTURE USE, SET TO 0 unsigned long dwEmphasis; // FUTURE USE, SET TO 0 // BIT STREAM SETTINGS int bPrivate; // Set Private Bit (TRUE/FALSE) int bCRC; // Insert CRC (TRUE/FALSE) int bCopyright; // Set Copyright Bit (TRUE/FALSE) int bOriginal; // Set Original Bit (TRUE/FALSE) // VBR STUFF int bWriteVBRHeader; // WRITE XING VBR HEADER (TRUE/FALSE) int bEnableVBR; // USE VBR ENCODING (TRUE/FALSE) int nVBRQuality; // VBR QUALITY 0..9 unsigned long dwVbrAbr_bps; // Use ABR in stead of nVBRQuality VBRMETHOD nVbrMethod; int bNoRes; // Disable Bit resorvoir (TRUE/FALSE) // MISC SETTINGS int bStrictIso; // Use strict ISO encoding rules (TRUE/FALSE) unsigned short nQuality; // Quality Setting, HIGH unsigned char should be NOT LOW byte, otherwhise quality=5 // FUTURE USE, SET TO 0, align strucutre to 331 bytes unsigned char btReserved[255-4*sizeof(unsigned long) - sizeof( unsigned short )]; } LHV1; // LAME header version 1 struct { unsigned long dwSampleRate; unsigned char byMode; unsigned short wBitrate; unsigned char byEncodingMethod; } aac; } format; } BE_CONFIG, *PBE_CONFIG ATTRIBUTE_PACKED; typedef struct { // BladeEnc DLL Version number unsigned char byDLLMajorVersion; unsigned char byDLLMinorVersion; // BladeEnc Engine Version Number unsigned char byMajorVersion; unsigned char byMinorVersion; // DLL Release date unsigned char byDay; unsigned char byMonth; unsigned short wYear; // BladeEnc Homepage URL char zHomepage[BE_MAX_HOMEPAGE + 1]; unsigned char byAlphaLevel; unsigned char byBetaLevel; unsigned char byMMXEnabled; unsigned char btReserved[125]; } BE_VERSION, *PBE_VERSION ATTRIBUTE_PACKED; #ifndef _BLADEDLL typedef unsigned long (*BEINITSTREAM) (PBE_CONFIG, unsigned long *, unsigned long *, PHBE_STREAM); typedef unsigned long (*BEENCODECHUNK) (HBE_STREAM, unsigned long, short *, unsigned char *, unsigned long *); // added for floating point audio -- DSPguru, jd typedef unsigned long (*BEENCODECHUNKFLOATS16NI) (HBE_STREAM, unsigned long, float *, float *, unsigned char *, unsigned long *); typedef unsigned long (*BEDEINITSTREAM) (HBE_STREAM, unsigned char *, unsigned long *); typedef unsigned long (*BECLOSESTREAM) (HBE_STREAM); typedef void (*BEVERSION) (PBE_VERSION); typedef unsigned long (*BEWRITEVBRHEADER) (const char*); typedef unsigned long (*BEWRITEINFOTAG) (HBE_STREAM, const char * ); #define TEXT_BEINITSTREAM "beInitStream" #define TEXT_BEENCODECHUNK "beEncodeChunk" #define TEXT_BEENCODECHUNKFLOATS16NI "beEncodeChunkFloatS16NI" #define TEXT_BEDEINITSTREAM "beDeinitStream" #define TEXT_BECLOSESTREAM "beCloseStream" #define TEXT_BEVERSION "beVersion" #define TEXT_BEWRITEVBRHEADER "beWriteVBRHeader" #define TEXT_BEFLUSHNOGAP "beFlushNoGap" #define TEXT_BEWRITEINFOTAG "beWriteInfoTag" #else __declspec(dllexport) unsigned long beInitStream(PBE_CONFIG pbeConfig, Punsigned long dwSamples, Punsigned long dwBufferSize, PHBE_STREAM phbeStream); __declspec(dllexport) unsigned long beEncodeChunk(HBE_STREAM hbeStream, unsigned long nSamples, PSHORT pSamples, Punsigned char pOutput, Punsigned long pdwOutput); // added for floating point audio -- DSPguru, jd __declspec(dllexport) unsigned long beEncodeChunkFloatS16NI(HBE_STREAM hbeStream, unsigned long nSamples, PFLOAT buffer_l, PFLOAT buffer_r, Punsigned char pOutput, Punsigned long pdwOutput); __declspec(dllexport) unsigned long beDeinitStream(HBE_STREAM hbeStream, Punsigned char pOutput, Punsigned long pdwOutput); __declspec(dllexport) unsigned long beCloseStream(HBE_STREAM hbeStream); __declspec(dllexport) VOID beVersion(PBE_VERSION pbeVersion); __declspec(dllexport) unsigned long beWriteVBRHeader(LPCSTR lpszFileName); __declspec(dllexport) unsigned long beFlushNoGap(HBE_STREAM hbeStream, Punsigned char pOutput, Punsigned long pdwOutput); __declspec(dllexport) unsigned long beWriteInfoTag( HBE_STREAM hbeStream, LPCSTR lpszFileName ); #endif #ifndef __GNUC__ #pragma pack(pop) #endif #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/mp3/mp3_port.h ================================================ /* $Id: mp3_port.h 1177 2007-04-09 07:06:08Z bennylp $ */ /* * Copyright (C) 2003-2007 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Contributed by: * Toni < buldozer at aufbix dot org > */ #ifndef __PJMEDIA_MP3_PORT_H__ #define __PJMEDIA_MP3_PORT_H__ /** * @file mp3_port.h * @brief MP3 writer */ #include /** * @defgroup PJMEDIA_MP3_FILE_REC MP3 Audio File Writer (Recorder) * @ingroup PJMEDIA_PORT * @brief MP3 Audio File Writer (Recorder) * @{ * * This section describes MP3 file writer. Currently it only works on Windows * using BladeEncDLL of the LAME MP3 encoder. Note that the LAME_ENC.DLL * file must exist in the PATH so that the encoder can work properly. * * The MP3 file writer is created with #pjmedia_mp3_writer_port_create() which * among other things specifies the desired file name and audio properties. * It then takes PCM input when #pjmedia_port_put_frame() is called and encode * the PCM input into MP3 streams before writing it to the .mp3 file. */ PJ_BEGIN_DECL /** * This structure contains encoding options that can be specified during * MP3 writer port creation. Application should always zero the structure * before setting some value to make sure that default options will be used. */ typedef struct pjmedia_mp3_encoder_option { /** Specify whether variable bit rate should be used. Variable bitrate * would normally produce better quality at the expense of probably * larger file. */ pj_bool_t vbr; /** Target bitrate, in bps. If VBR is enabled, this settings specifies * the average bit-rate requested, and will make the encoder ignore * the quality setting. For CBR, this specifies the actual bitrate, * and if this option is zero, it will be set to the sampling rate * multiplied by number of channels. */ unsigned bit_rate; /** Encoding quality, 0-9, with 0 is the highest quality. For VBR, the * quality setting will only take effect when bit_rate setting is zero. */ unsigned quality; } pjmedia_mp3_encoder_option; /** * Create a media port to record PCM media to a MP3 file. After the port * is created, application can call #pjmedia_port_put_frame() to feed the * port with PCM frames. The port then will encode the PCM frame into MP3 * stream, and store it to MP3 file specified in the argument. * * When application has finished with writing MP3 file, it must destroy the * media port with #pjmedia_port_destroy() so that the MP3 file can be * closed properly. * * @param pool Pool to create memory buffers for this port. * @param filename File name. * @param clock_rate The sampling rate. * @param channel_count Number of channels. * @param samples_per_frame Number of samples per frame. * @param bits_per_sample Number of bits per sample (eg 16). * @param option Optional option to set encoding parameters. * @param p_port Pointer to receive the file port instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_mp3_writer_port_create(pj_pool_t *pool, const char *filename, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, const pjmedia_mp3_encoder_option *option, pjmedia_port **p_port ); /** * Register the callback to be called when the file writing has reached * certain size. Application can use this callback, for example, to limit * the size of the output file. * * @param port The file writer port. * @param pos The file position on which the callback will be called. * @param user_data User data to be specified in the callback, and will be * given on the callback. * @param cb Callback to be called. If the callback returns non- * PJ_SUCCESS, the writing will stop. Note that if * application destroys the port in the callback, it must * return non-PJ_SUCCESS here. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_mp3_writer_port_set_cb( pjmedia_port *port, pj_size_t pos, void *user_data, pj_status_t (*cb)(pjmedia_port *port, void *usr_data)); /** * @} */ PJ_END_DECL #endif /* __PJMEDIA_MP3_PORT_H__ */ ================================================ FILE: deps/pjsip/third_party/mp3/mp3_writer.c ================================================ /* $Id: mp3_writer.c 4483 2013-04-19 09:52:02Z ming $ */ /* * Copyright (C) 2003-2007 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Contributed by: * Toni < buldozer at aufbix dot org > */ #include "mp3_port.h" #include #include #include #include #include #include #include #include /* Include BladeDLL declarations */ #include "BladeMP3EncDLL.h" #define THIS_FILE "mp3_writer.c" #define SIGNATURE PJMEDIA_SIG_CLASS_PORT_AUD('M','W') #define BYTES_PER_SAMPLE 2 static struct BladeDLL { void *hModule; int refCount; BEINITSTREAM beInitStream; BEENCODECHUNK beEncodeChunk; BEDEINITSTREAM beDeinitStream; BECLOSESTREAM beCloseStream; BEVERSION beVersion; BEWRITEVBRHEADER beWriteVBRHeader; BEWRITEINFOTAG beWriteInfoTag; } BladeDLL; struct mp3_file_port { pjmedia_port base; pj_size_t total; pj_oshandle_t fd; pj_size_t cb_size; pj_status_t (*cb)(pjmedia_port*, void*); unsigned silence_duration; pj_str_t mp3_filename; pjmedia_mp3_encoder_option mp3_option; unsigned mp3_samples_per_frame; pj_int16_t *mp3_sample_buf; unsigned mp3_sample_pos; HBE_STREAM mp3_stream; unsigned char *mp3_buf; }; static pj_status_t file_put_frame(pjmedia_port *this_port, const pjmedia_frame *frame); static pj_status_t file_get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t file_on_destroy(pjmedia_port *this_port); #if defined(PJ_WIN32) || defined(_WIN32) || defined(WIN32) #include #define DLL_NAME PJ_T("LAME_ENC.DLL") /* * Load BladeEncoder DLL. */ static pj_status_t init_blade_dll(void) { if (BladeDLL.refCount == 0) { #define GET_PROC(type, name) \ BladeDLL.name = (type)GetProcAddress(BladeDLL.hModule, PJ_T(#name)); \ if (BladeDLL.name == NULL) { \ PJ_LOG(1,(THIS_FILE, "Unable to find %s in %s", #name, DLL_NAME)); \ return PJ_RETURN_OS_ERROR(GetLastError()); \ } BE_VERSION beVersion; BladeDLL.hModule = (void*)LoadLibrary(DLL_NAME); if (BladeDLL.hModule == NULL) { pj_status_t status = PJ_RETURN_OS_ERROR(GetLastError()); char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(1,(THIS_FILE, "Unable to load %s: %s", DLL_NAME, errmsg)); return status; } GET_PROC(BEINITSTREAM, beInitStream); GET_PROC(BEENCODECHUNK, beEncodeChunk); GET_PROC(BEDEINITSTREAM, beDeinitStream); GET_PROC(BECLOSESTREAM, beCloseStream); GET_PROC(BEVERSION, beVersion); GET_PROC(BEWRITEVBRHEADER, beWriteVBRHeader); GET_PROC(BEWRITEINFOTAG, beWriteInfoTag); #undef GET_PROC BladeDLL.beVersion(&beVersion); PJ_LOG(4,(THIS_FILE, "%s encoder v%d.%d loaded (%s)", DLL_NAME, beVersion.byMajorVersion, beVersion.byMinorVersion, beVersion.zHomepage)); } ++BladeDLL.refCount; return PJ_SUCCESS; } /* * Decrement the reference counter of the DLL. */ static void deinit_blade_dll() { --BladeDLL.refCount; if (BladeDLL.refCount == 0 && BladeDLL.hModule) { FreeLibrary(BladeDLL.hModule); BladeDLL.hModule = NULL; PJ_LOG(4,(THIS_FILE, "%s unloaded", DLL_NAME)); } } #else static pj_status_t init_blade_dll(void) { PJ_LOG(1,(THIS_FILE, "Error: MP3 writer port only works on Windows for now")); return PJ_ENOTSUP; } static void deinit_blade_dll() { } #endif /* * Initialize MP3 encoder. */ static pj_status_t init_mp3_encoder(struct mp3_file_port *fport, pj_pool_t *pool) { BE_CONFIG LConfig; unsigned long InSamples; unsigned long OutBuffSize; long MP3Err; /* * Initialize encoder configuration. */ pj_bzero(&LConfig, sizeof(BE_CONFIG)); LConfig.dwConfig = BE_CONFIG_LAME; LConfig.format.LHV1.dwStructVersion = 1; LConfig.format.LHV1.dwStructSize = sizeof(BE_CONFIG); LConfig.format.LHV1.dwSampleRate = PJMEDIA_PIA_SRATE(&fport->base.info); LConfig.format.LHV1.dwReSampleRate = 0; if (PJMEDIA_PIA_CCNT(&fport->base.info)==1) LConfig.format.LHV1.nMode = BE_MP3_MODE_MONO; else if (PJMEDIA_PIA_CCNT(&fport->base.info)==2) LConfig.format.LHV1.nMode = BE_MP3_MODE_STEREO; else return PJMEDIA_ENCCHANNEL; LConfig.format.LHV1.dwBitrate = fport->mp3_option.bit_rate / 1000; LConfig.format.LHV1.nPreset = LQP_NOPRESET; LConfig.format.LHV1.bCopyright = 0; LConfig.format.LHV1.bCRC = 1; LConfig.format.LHV1.bOriginal = 1; LConfig.format.LHV1.bPrivate = 0; if (!fport->mp3_option.vbr) { LConfig.format.LHV1.nVbrMethod = VBR_METHOD_NONE; LConfig.format.LHV1.bWriteVBRHeader = 0; LConfig.format.LHV1.bEnableVBR = 0; } else { LConfig.format.LHV1.nVbrMethod = VBR_METHOD_DEFAULT; LConfig.format.LHV1.bWriteVBRHeader = 1; LConfig.format.LHV1.dwVbrAbr_bps = fport->mp3_option.bit_rate; LConfig.format.LHV1.nVBRQuality = (pj_uint16_t) fport->mp3_option.quality; LConfig.format.LHV1.bEnableVBR = 1; } LConfig.format.LHV1.nQuality = (pj_uint16_t) (((0-fport->mp3_option.quality-1)<<8) | fport->mp3_option.quality); /* * Init MP3 stream. */ InSamples = 0; MP3Err = BladeDLL.beInitStream(&LConfig, &InSamples, &OutBuffSize, &fport->mp3_stream); if (MP3Err != BE_ERR_SUCCESSFUL) return PJMEDIA_ERROR; /* * Allocate sample buffer. */ fport->mp3_samples_per_frame = (unsigned)InSamples; fport->mp3_sample_buf = pj_pool_alloc(pool, fport->mp3_samples_per_frame * 2); if (!fport->mp3_sample_buf) return PJ_ENOMEM; /* * Allocate encoded MP3 buffer. */ fport->mp3_buf = pj_pool_alloc(pool, (pj_size_t)OutBuffSize); if (fport->mp3_buf == NULL) return PJ_ENOMEM; return PJ_SUCCESS; } /* * Create MP3 file writer port. */ PJ_DEF(pj_status_t) pjmedia_mp3_writer_port_create( pj_pool_t *pool, const char *filename, unsigned sampling_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, const pjmedia_mp3_encoder_option *param_option, pjmedia_port **p_port ) { struct mp3_file_port *fport; pj_status_t status; status = init_blade_dll(); if (status != PJ_SUCCESS) return status; /* Check arguments. */ PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL); /* Only supports 16bits per sample for now. */ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); /* Create file port instance. */ fport = pj_pool_zalloc(pool, sizeof(struct mp3_file_port)); PJ_ASSERT_RETURN(fport != NULL, PJ_ENOMEM); /* Initialize port info. */ pj_strdup2_with_null(pool, &fport->mp3_filename, filename); pjmedia_port_info_init(&fport->base.info, &fport->mp3_filename, SIGNATURE, sampling_rate, channel_count, bits_per_sample, samples_per_frame); fport->base.get_frame = &file_get_frame; fport->base.put_frame = &file_put_frame; fport->base.on_destroy = &file_on_destroy; /* Open file in write and read mode. * We need the read mode because we'll modify the WAVE header once * the recording has completed. */ status = pj_file_open(pool, filename, PJ_O_WRONLY, &fport->fd); if (status != PJ_SUCCESS) { deinit_blade_dll(); return status; } /* Copy and initialize option with default settings */ if (param_option) { pj_memcpy(&fport->mp3_option, param_option, sizeof(pjmedia_mp3_encoder_option)); } else { pj_bzero(&fport->mp3_option, sizeof(pjmedia_mp3_encoder_option)); fport->mp3_option.vbr = PJ_TRUE; } /* Calculate bitrate if it's not specified, only if it's not VBR. */ if (fport->mp3_option.bit_rate == 0 && !fport->mp3_option.vbr) fport->mp3_option.bit_rate = sampling_rate * channel_count; /* Set default quality if it's not specified */ if (fport->mp3_option.quality == 0) fport->mp3_option.quality = 2; /* Init mp3 encoder */ status = init_mp3_encoder(fport, pool); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); deinit_blade_dll(); return status; } /* Done. */ *p_port = &fport->base; PJ_LOG(4,(THIS_FILE, "MP3 file writer '%.*s' created: samp.rate=%dKHz, " "bitrate=%dkbps%s, quality=%d", (int)fport->base.info.name.slen, fport->base.info.name.ptr, PJMEDIA_PIA_SRATE(&fport->base.info), fport->mp3_option.bit_rate/1000, (fport->mp3_option.vbr ? " (VBR)" : ""), fport->mp3_option.quality)); return PJ_SUCCESS; } /* * Register callback. */ PJ_DEF(pj_status_t) pjmedia_mp3_writer_port_set_cb( pjmedia_port *port, pj_size_t pos, void *user_data, pj_status_t (*cb)(pjmedia_port *port, void *usr_data)) { struct mp3_file_port *fport; /* Sanity check */ PJ_ASSERT_RETURN(port && cb, PJ_EINVAL); /* Check that this is really a writer port */ PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP); fport = (struct mp3_file_port*) port; fport->cb_size = pos; fport->base.port_data.pdata = user_data; fport->cb = cb; return PJ_SUCCESS; } /* * Put a frame into the buffer. When the buffer is full, flush the buffer * to the file. */ static pj_status_t file_put_frame(pjmedia_port *this_port, const pjmedia_frame *frame) { struct mp3_file_port *fport = (struct mp3_file_port *)this_port; unsigned long MP3Err; pj_ssize_t bytes; pj_status_t status; unsigned long WriteSize; /* Record silence if input is no-frame */ if (frame->type == PJMEDIA_FRAME_TYPE_NONE || frame->size == 0) { unsigned samples_left = PJMEDIA_PIA_SPF(&fport->base.info); unsigned samples_copied = 0; /* Only want to record at most 1 second of silence */ if (fport->silence_duration >= PJMEDIA_PIA_SRATE(&fport->base.info)) return PJ_SUCCESS; while (samples_left) { unsigned samples_needed = fport->mp3_samples_per_frame - fport->mp3_sample_pos; if (samples_needed > samples_left) samples_needed = samples_left; pjmedia_zero_samples(fport->mp3_sample_buf + fport->mp3_sample_pos, samples_needed); fport->mp3_sample_pos += samples_needed; samples_left -= samples_needed; samples_copied += samples_needed; /* Encode if we have full frame */ if (fport->mp3_sample_pos == fport->mp3_samples_per_frame) { /* Clear position */ fport->mp3_sample_pos = 0; /* Encode ! */ MP3Err = BladeDLL.beEncodeChunk(fport->mp3_stream, fport->mp3_samples_per_frame, fport->mp3_sample_buf, fport->mp3_buf, &WriteSize); if (MP3Err != BE_ERR_SUCCESSFUL) return PJMEDIA_ERROR; /* Write the chunk */ bytes = WriteSize; status = pj_file_write(fport->fd, fport->mp3_buf, &bytes); if (status != PJ_SUCCESS) return status; /* Increment total written. */ fport->total += bytes; } } fport->silence_duration += PJMEDIA_PIA_SPF(&fport->base.info); } /* If encoder is expecting different sample size, then we need to * buffer the samples. */ else if (fport->mp3_samples_per_frame != PJMEDIA_PIA_SPF(&fport->base.info)) { unsigned samples_left = frame->size / 2; unsigned samples_copied = 0; const pj_int16_t *src_samples = frame->buf; fport->silence_duration = 0; while (samples_left) { unsigned samples_needed = fport->mp3_samples_per_frame - fport->mp3_sample_pos; if (samples_needed > samples_left) samples_needed = samples_left; pjmedia_copy_samples(fport->mp3_sample_buf + fport->mp3_sample_pos, src_samples + samples_copied, samples_needed); fport->mp3_sample_pos += samples_needed; samples_left -= samples_needed; samples_copied += samples_needed; /* Encode if we have full frame */ if (fport->mp3_sample_pos == fport->mp3_samples_per_frame) { /* Clear position */ fport->mp3_sample_pos = 0; /* Encode ! */ MP3Err = BladeDLL.beEncodeChunk(fport->mp3_stream, fport->mp3_samples_per_frame, fport->mp3_sample_buf, fport->mp3_buf, &WriteSize); if (MP3Err != BE_ERR_SUCCESSFUL) return PJMEDIA_ERROR; /* Write the chunk */ bytes = WriteSize; status = pj_file_write(fport->fd, fport->mp3_buf, &bytes); if (status != PJ_SUCCESS) return status; /* Increment total written. */ fport->total += bytes; } } } else { fport->silence_duration = 0; /* Encode ! */ MP3Err = BladeDLL.beEncodeChunk(fport->mp3_stream, fport->mp3_samples_per_frame, frame->buf, fport->mp3_buf, &WriteSize); if (MP3Err != BE_ERR_SUCCESSFUL) return PJMEDIA_ERROR; /* Write the chunk */ bytes = WriteSize; status = pj_file_write(fport->fd, fport->mp3_buf, &bytes); if (status != PJ_SUCCESS) return status; /* Increment total written. */ fport->total += bytes; } /* Increment total written, and check if we need to call callback */ if (fport->cb && fport->total >= fport->cb_size) { pj_status_t (*cb)(pjmedia_port*, void*); pj_status_t status; cb = fport->cb; fport->cb = NULL; status = (*cb)(this_port, this_port->port_data.pdata); return status; } return PJ_SUCCESS; } /* * Get frame, basicy is a no-op operation. */ static pj_status_t file_get_frame(pjmedia_port *this_port, pjmedia_frame *frame) { PJ_UNUSED_ARG(this_port); PJ_UNUSED_ARG(frame); return PJ_EINVALIDOP; } /* * Close the port, modify file header with updated file length. */ static pj_status_t file_on_destroy(pjmedia_port *this_port) { struct mp3_file_port *fport = (struct mp3_file_port*)this_port; pj_status_t status; unsigned long WriteSize; unsigned long MP3Err; /* Close encoder */ MP3Err = BladeDLL.beDeinitStream(fport->mp3_stream, fport->mp3_buf, &WriteSize); if (MP3Err == BE_ERR_SUCCESSFUL) { pj_ssize_t bytes = WriteSize; status = pj_file_write(fport->fd, fport->mp3_buf, &bytes); } /* Close file */ status = pj_file_close(fport->fd); /* Write additional VBR header */ if (fport->mp3_option.vbr) { MP3Err = BladeDLL.beWriteVBRHeader(fport->mp3_filename.ptr); } /* Decrement DLL reference counter */ deinit_blade_dll(); /* Done. */ return PJ_SUCCESS; } ================================================ FILE: deps/pjsip/third_party/opus/.gitignore ================================================ Makefile Makefile.in aclocal.m4 autom4te.cache *.kdevelop.pcs *.kdevses compile config.guess config.h config.h.in config.log config.status config.sub configure depcomp install-sh .deps .libs *.la testcelt libtool ltmain.sh missing stamp-h1 *.sw *.o *.lo *~ tests/*test tools/celtdec tools/celtenc celt.pc celt.spec celt/dump_modes/dump_modes *.vcxproj.user opus.sdf opus.suo version.h celt/Debug celt/Release celt/x64 silk/Debug silk/Release silk/x64 silk/fixed/Debug silk/fixed/Release silk/fixed/x64 silk/float/Debug silk/float/Release silk/float/x64 src/Debug src/Release src/x64 ================================================ FILE: deps/pjsip/third_party/opus/AUTHORS ================================================ Jean-Marc Valin (jmvalin@jmvalin.ca) Koen Vos (koenvos74@gmail.com) Timothy Terriberry (tterribe@xiph.org) Karsten Vandborg Sorensen (karsten.vandborg.sorensen@skype.net) Soren Skak Jensen (ssjensen@gn.com) Gregory Maxwell (greg@xiph.org) ================================================ FILE: deps/pjsip/third_party/opus/COPYING ================================================ Copyright 2001-2011 Xiph.Org, Skype Limited, Octasic, Jean-Marc Valin, Timothy B. Terriberry, CSIRO, Gregory Maxwell, Mark Borgerding, Erik de Castro Lopo Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Opus is subject to the royalty-free patent licenses which are specified at: Xiph.Org Foundation: https://datatracker.ietf.org/ipr/1524/ Microsoft Corporation: https://datatracker.ietf.org/ipr/1914/ Broadcom Corporation: https://datatracker.ietf.org/ipr/1526/ ================================================ FILE: deps/pjsip/third_party/opus/ChangeLog ================================================ ================================================ FILE: deps/pjsip/third_party/opus/INSTALL ================================================ Installation Instructions ************************* Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. Basic Installation ================== Briefly, the shell command `./configure && make && make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. Some packages provide this `INSTALL' file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in *note Makefile Conventions: (standards)Makefile Conventions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 4. Type `make install' to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the `make install' phase executed with root privileges. 5. Optionally, type `make installcheck' to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior `make install' required root privileges, verifies that the installation completed correctly. 6. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 7. Often, you can also type `make uninstall' to remove the installed files again. In practice, not all packages have tested that uninstallation works correctly, even though it is required by the GNU Coding Standards. 8. Some packages, particularly those that use Automake, provide `make distcheck', which can by used by developers to test that all other targets like `make install' and `make uninstall' work correctly. This target is generally not run by end users. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. This is known as a "VPATH" build. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. On MacOS X 10.5 and later systems, you can create libraries and executables that work on multiple system types--known as "fat" or "universal" binaries--by specifying multiple `-arch' options to the compiler but only a single `-arch' option to the preprocessor. Like this: ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CPP="gcc -E" CXXCPP="g++ -E" This is not guaranteed to produce working output in all cases, you may have to build one architecture at a time and combine the results using the `lipo' tool if you have problems. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX', where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of `${prefix}', so that specifying just `--prefix' will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to `configure'; however, many packages provide one or both of the following shortcuts of passing variable assignments to the `make install' command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, `make install prefix=/alternate/directory' will choose an alternate location for all directory configuration variables that were expressed in terms of `${prefix}'. Any directories that were specified during `configure', but not in terms of `${prefix}', must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the `DESTDIR' variable. For example, `make install DESTDIR=/alternate/directory' will prepend `/alternate/directory' before all installation names. The approach of `DESTDIR' overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of `${prefix}' at `configure' time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Some packages offer the ability to configure how verbose the execution of `make' will be. For these packages, running `./configure --enable-silent-rules' sets the default to minimal output, which can be overridden with `make V=1'; while running `./configure --disable-silent-rules' sets the default to verbose, which can be overridden with `make V=0'. Particular systems ================== On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC is not installed, it is recommended to use the following options in order to use an ANSI C compiler: ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" and if that doesn't work, install pre-built binaries of GCC for HP-UX. HP-UX `make' updates targets which have the same time stamps as their prerequisites, which makes it generally unusable when shipped generated files such as `configure' are involved. Use GNU `make' instead. On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its `' header file. The option `-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended to try ./configure CC="cc" and if that doesn't work, try ./configure CC="cc -nodtk" On Solaris, don't put `/usr/ucb' early in your `PATH'. This directory contains several dysfunctional programs; working variants of these programs are available in `/usr/bin'. So, if you need `/usr/ucb' in your `PATH', put it _after_ `/usr/bin'. On Haiku, software installed for all users goes in `/boot/common', not `/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf limitation. Until the limitation is lifted, you can use this workaround: CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of all of the options to `configure', and exit. `--help=short' `--help=recursive' Print a summary of the options unique to this package's `configure', and exit. The `short' variant lists options used only in the top level, while the `recursive' variant lists options also present in any nested packages. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--prefix=DIR' Use DIR as the installation prefix. *note Installation Names:: for more details, including other options available for fine-tuning the installation locations. `--no-create' `-n' Run the configure checks, but stop before creating any output files. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. ================================================ FILE: deps/pjsip/third_party/opus/Makefile.am ================================================ # Provide the full test output for failed tests when using the parallel # test suite (which is enabled by default with automake 1.13+). export VERBOSE = yes AUTOMAKE_OPTIONS = subdir-objects ACLOCAL_AMFLAGS = -I m4 lib_LTLIBRARIES = libopus.la DIST_SUBDIRS = doc AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/celt -I$(top_srcdir)/silk \ -I$(top_srcdir)/silk/float -I$(top_srcdir)/silk/fixed $(NE10_CFLAGS) include celt_sources.mk include silk_sources.mk include opus_sources.mk if FIXED_POINT SILK_SOURCES += $(SILK_SOURCES_FIXED) if HAVE_SSE4_1 SILK_SOURCES += $(SILK_SOURCES_SSE4_1) $(SILK_SOURCES_FIXED_SSE4_1) endif else SILK_SOURCES += $(SILK_SOURCES_FLOAT) if HAVE_SSE4_1 SILK_SOURCES += $(SILK_SOURCES_SSE4_1) endif endif if DISABLE_FLOAT_API else OPUS_SOURCES += $(OPUS_SOURCES_FLOAT) endif if HAVE_SSE CELT_SOURCES += $(CELT_SOURCES_SSE) endif if HAVE_SSE2 CELT_SOURCES += $(CELT_SOURCES_SSE2) endif if HAVE_SSE4_1 CELT_SOURCES += $(CELT_SOURCES_SSE4_1) endif if CPU_ARM CELT_SOURCES += $(CELT_SOURCES_ARM) SILK_SOURCES += $(SILK_SOURCES_ARM) if OPUS_ARM_NEON_INTR CELT_SOURCES += $(CELT_SOURCES_ARM_NEON_INTR) endif if HAVE_ARM_NE10 CELT_SOURCES += $(CELT_SOURCES_ARM_NE10) endif if OPUS_ARM_EXTERNAL_ASM noinst_LTLIBRARIES = libarmasm.la libarmasm_la_SOURCES = $(CELT_SOURCES_ARM_ASM:.s=-gnu.S) BUILT_SOURCES = $(CELT_SOURCES_ARM_ASM:.s=-gnu.S) \ $(CELT_AM_SOURCES_ARM_ASM:.s.in=.s) \ $(CELT_AM_SOURCES_ARM_ASM:.s.in=-gnu.S) endif endif CLEANFILES = $(CELT_SOURCES_ARM_ASM:.s=-gnu.S) \ $(CELT_AM_SOURCES_ARM_ASM:.s.in=-gnu.S) include celt_headers.mk include silk_headers.mk include opus_headers.mk libopus_la_SOURCES = $(CELT_SOURCES) $(SILK_SOURCES) $(OPUS_SOURCES) libopus_la_LDFLAGS = -no-undefined -version-info @OPUS_LT_CURRENT@:@OPUS_LT_REVISION@:@OPUS_LT_AGE@ libopus_la_LIBADD = $(NE10_LIBS) $(LIBM) if OPUS_ARM_EXTERNAL_ASM libopus_la_LIBADD += libarmasm.la endif pkginclude_HEADERS = include/opus.h include/opus_multistream.h include/opus_types.h include/opus_defines.h noinst_HEADERS = $(OPUS_HEAD) $(SILK_HEAD) $(CELT_HEAD) if EXTRA_PROGRAMS noinst_PROGRAMS = opus_demo repacketizer_demo opus_compare tests/test_opus_api tests/test_opus_encode tests/test_opus_decode tests/test_opus_padding celt/tests/test_unit_cwrs32 celt/tests/test_unit_dft celt/tests/test_unit_entropy celt/tests/test_unit_laplace celt/tests/test_unit_mathops celt/tests/test_unit_mdct celt/tests/test_unit_rotation celt/tests/test_unit_types TESTS = celt/tests/test_unit_types celt/tests/test_unit_mathops celt/tests/test_unit_entropy celt/tests/test_unit_laplace celt/tests/test_unit_dft celt/tests/test_unit_mdct celt/tests/test_unit_rotation celt/tests/test_unit_cwrs32 tests/test_opus_api tests/test_opus_decode tests/test_opus_encode tests/test_opus_padding opus_demo_SOURCES = src/opus_demo.c opus_demo_LDADD = libopus.la $(NE10_LIBS) $(LIBM) repacketizer_demo_SOURCES = src/repacketizer_demo.c repacketizer_demo_LDADD = libopus.la $(NE10_LIBS) $(LIBM) opus_compare_SOURCES = src/opus_compare.c opus_compare_LDADD = $(LIBM) tests_test_opus_api_SOURCES = tests/test_opus_api.c tests/test_opus_common.h tests_test_opus_api_LDADD = libopus.la $(NE10_LIBS) $(LIBM) tests_test_opus_encode_SOURCES = tests/test_opus_encode.c tests/test_opus_common.h tests_test_opus_encode_LDADD = libopus.la $(NE10_LIBS) $(LIBM) tests_test_opus_decode_SOURCES = tests/test_opus_decode.c tests/test_opus_common.h tests_test_opus_decode_LDADD = libopus.la $(NE10_LIBS) $(LIBM) tests_test_opus_padding_SOURCES = tests/test_opus_padding.c tests/test_opus_common.h tests_test_opus_padding_LDADD = libopus.la $(NE10_LIBS) $(LIBM) celt_tests_test_unit_cwrs32_SOURCES = celt/tests/test_unit_cwrs32.c celt_tests_test_unit_cwrs32_LDADD = $(LIBM) celt_tests_test_unit_dft_SOURCES = celt/tests/test_unit_dft.c celt_tests_test_unit_dft_LDADD = $(NE10_LIBS) $(LIBM) if OPUS_ARM_EXTERNAL_ASM celt_tests_test_unit_dft_LDADD += libarmasm.la endif celt_tests_test_unit_entropy_SOURCES = celt/tests/test_unit_entropy.c celt_tests_test_unit_entropy_LDADD = $(LIBM) celt_tests_test_unit_laplace_SOURCES = celt/tests/test_unit_laplace.c celt_tests_test_unit_laplace_LDADD = $(LIBM) celt_tests_test_unit_mathops_SOURCES = celt/tests/test_unit_mathops.c celt_tests_test_unit_mathops_LDADD = $(NE10_LIBS) $(LIBM) if OPUS_ARM_EXTERNAL_ASM celt_tests_test_unit_mathops_LDADD += libarmasm.la endif celt_tests_test_unit_mdct_SOURCES = celt/tests/test_unit_mdct.c celt_tests_test_unit_mdct_LDADD = $(NE10_LIBS) $(LIBM) if OPUS_ARM_EXTERNAL_ASM celt_tests_test_unit_mdct_LDADD += libarmasm.la endif celt_tests_test_unit_rotation_SOURCES = celt/tests/test_unit_rotation.c celt_tests_test_unit_rotation_LDADD = $(NE10_LIBS) $(LIBM) if OPUS_ARM_EXTERNAL_ASM celt_tests_test_unit_rotation_LDADD += libarmasm.la endif celt_tests_test_unit_types_SOURCES = celt/tests/test_unit_types.c celt_tests_test_unit_types_LDADD = $(LIBM) endif if CUSTOM_MODES pkginclude_HEADERS += include/opus_custom.h if EXTRA_PROGRAMS noinst_PROGRAMS += opus_custom_demo opus_custom_demo_SOURCES = celt/opus_custom_demo.c opus_custom_demo_LDADD = libopus.la $(LIBM) endif endif EXTRA_DIST = version.mk \ opus.pc.in \ opus-uninstalled.pc.in \ opus.m4 \ Makefile.mips \ Makefile.unix \ tests/run_vectors.sh \ celt/arm/arm2gnu.pl \ celt/arm/celt_pitch_xcorr_arm.s \ win32/VS2010/silk_float.vcxproj \ win32/VS2010/celt.vcxproj.filters \ win32/VS2010/opus.vcxproj \ win32/VS2010/silk_common.vcxproj.filters \ win32/VS2010/silk_float.vcxproj.filters \ win32/VS2010/test_opus_encode.vcxproj.filters \ win32/VS2010/silk_common.vcxproj \ win32/VS2010/test_opus_encode.vcxproj \ win32/VS2010/opus_demo.vcxproj \ win32/VS2010/test_opus_api.vcxproj.filters \ win32/VS2010/test_opus_api.vcxproj \ win32/VS2010/test_opus_decode.vcxproj.filters \ win32/VS2010/silk_fixed.vcxproj.filters \ win32/VS2010/opus_demo.vcxproj.filters \ win32/VS2010/silk_fixed.vcxproj \ win32/VS2010/opus.vcxproj.filters \ win32/VS2010/test_opus_decode.vcxproj \ win32/VS2010/celt.vcxproj \ win32/VS2010/opus.sln \ win32/genversion.bat \ win32/config.h pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = opus.pc m4datadir = $(datadir)/aclocal m4data_DATA = opus.m4 # Targets to build and install just the library without the docs opus check-opus install-opus: export NO_DOXYGEN = 1 opus: all check-opus: check install-opus: install # Or just the docs docs: ( cd doc && $(MAKE) $(AM_MAKEFLAGS) ) install-docs: ( cd doc && $(MAKE) $(AM_MAKEFLAGS) install ) # Or everything (by default) all-local: @[ -n "$(NO_DOXYGEN)" ] || ( cd doc && $(MAKE) $(AM_MAKEFLAGS) ) install-data-local: @[ -n "$(NO_DOXYGEN)" ] || ( cd doc && $(MAKE) $(AM_MAKEFLAGS) install ) clean-local: -( cd doc && $(MAKE) $(AM_MAKEFLAGS) clean ) uninstall-local: ( cd doc && $(MAKE) $(AM_MAKEFLAGS) uninstall ) # We check this every time make is run, with configure.ac being touched to # trigger an update of the build system files if update_version changes the # current PACKAGE_VERSION (or if package_version was modified manually by a # user with either AUTO_UPDATE=no or no update_version script present - the # latter being the normal case for tarball releases). # # We can't just add the package_version file to CONFIGURE_DEPENDENCIES since # simply running autoconf will not actually regenerate configure for us when # the content of that file changes (due to autoconf dependency checking not # knowing about that without us creating yet another file for it to include). # # The MAKECMDGOALS check is a gnu-make'ism, but will degrade 'gracefully' for # makes that don't support it. The only loss of functionality is not forcing # an update of package_version for `make dist` if AUTO_UPDATE=no, but that is # unlikely to be a real problem for any real user. $(top_srcdir)/configure.ac: force @case "$(MAKECMDGOALS)" in \ dist-hook) exit 0 ;; \ dist-* | dist | distcheck | distclean) _arg=release ;; \ esac; \ if ! $(top_srcdir)/update_version $$_arg 2> /dev/null; then \ if [ ! -e $(top_srcdir)/package_version ]; then \ echo 'PACKAGE_VERSION="unknown"' > $(top_srcdir)/package_version; \ fi; \ . $(top_srcdir)/package_version || exit 1; \ [ "$(PACKAGE_VERSION)" != "$$PACKAGE_VERSION" ] || exit 0; \ fi; \ touch $@ force: # Create a minimal package_version file when make dist is run. dist-hook: echo 'PACKAGE_VERSION="$(PACKAGE_VERSION)"' > $(top_distdir)/package_version .PHONY: opus check-opus install-opus docs install-docs # automake doesn't do dependency tracking for asm files, that I can tell $(CELT_SOURCES_ARM_ASM:%.s=%-gnu.S): celt/arm/armopts-gnu.S $(CELT_SOURCES_ARM_ASM:%.s=%-gnu.S): $(top_srcdir)/celt/arm/arm2gnu.pl # convert ARM asm to GNU as format %-gnu.S: $(top_srcdir)/%.s $(top_srcdir)/celt/arm/arm2gnu.pl @ARM2GNU_PARAMS@ < $< > $@ # For autoconf-modified sources (e.g., armopts.s) %-gnu.S: %.s $(top_srcdir)/celt/arm/arm2gnu.pl @ARM2GNU_PARAMS@ < $< > $@ OPT_UNIT_TEST_OBJ = $(celt_tests_test_unit_mathops_SOURCES:.c=.o) \ $(celt_tests_test_unit_rotation_SOURCES:.c=.o) \ $(celt_tests_test_unit_mdct_SOURCES:.c=.o) \ $(celt_tests_test_unit_dft_SOURCES:.c=.o) if HAVE_SSE SSE_OBJ = $(CELT_SOURCES_SSE:.c=.lo) $(SSE_OBJ) $(OPT_UNIT_TEST_OBJ): CFLAGS += $(OPUS_X86_SSE_CFLAGS) endif if HAVE_SSE2 SSE2_OBJ = $(CELT_SOURCES_SSE2:.c=.lo) $(SSE2_OBJ) $(OPT_UNIT_TEST_OBJ): CFLAGS += $(OPUS_X86_SSE2_CFLAGS) endif if HAVE_SSE4_1 SSE4_1_OBJ = $(CELT_SOURCES_SSE4_1:.c=.lo) \ $(SILK_SOURCES_SSE4_1:.c=.lo) \ $(SILK_SOURCES_FIXED_SSE4_1:.c=.lo) $(SSE4_1_OBJ) $(OPT_UNIT_TEST_OBJ): CFLAGS += $(OPUS_X86_SSE4_1_CFLAGS) endif if OPUS_ARM_NEON_INTR CELT_ARM_NEON_INTR_OBJ = $(CELT_SOURCES_ARM_NEON_INTR:.c=.lo) $(CELT_ARM_NEON_INTR_OBJ) $(OPT_UNIT_TEST_OBJ): CFLAGS += \ $(OPUS_ARM_NEON_INTR_CFLAGS) $(NE10_CFLAGS) endif ================================================ FILE: deps/pjsip/third_party/opus/Makefile.mips ================================================ #################### COMPILE OPTIONS ####################### # Uncomment this for fixed-point build FIXED_POINT=1 # It is strongly recommended to uncomment one of these # VAR_ARRAYS: Use C99 variable-length arrays for stack allocation # USE_ALLOCA: Use alloca() for stack allocation # If none is defined, then the fallback is a non-threadsafe global array CFLAGS := -DUSE_ALLOCA $(CFLAGS) #CFLAGS := -DVAR_ARRAYS $(CFLAGS) # These options affect performance # HAVE_LRINTF: Use C99 intrinsics to speed up float-to-int conversion #CFLAGS := -DHAVE_LRINTF $(CFLAGS) ###################### END OF OPTIONS ###################### -include package_version include silk_sources.mk include celt_sources.mk include opus_sources.mk ifdef FIXED_POINT SILK_SOURCES += $(SILK_SOURCES_FIXED) else SILK_SOURCES += $(SILK_SOURCES_FLOAT) OPUS_SOURCES += $(OPUS_SOURCES_FLOAT) endif EXESUFFIX = LIBPREFIX = lib LIBSUFFIX = .a OBJSUFFIX = .o CC = $(TOOLCHAIN_PREFIX)cc$(TOOLCHAIN_SUFFIX) AR = $(TOOLCHAIN_PREFIX)ar RANLIB = $(TOOLCHAIN_PREFIX)ranlib CP = $(TOOLCHAIN_PREFIX)cp cppflags-from-defines = $(addprefix -D,$(1)) cppflags-from-includes = $(addprefix -I,$(1)) ldflags-from-ldlibdirs = $(addprefix -L,$(1)) ldlibs-from-libs = $(addprefix -l,$(1)) WARNINGS = -Wall -W -Wstrict-prototypes -Wextra -Wcast-align -Wnested-externs -Wshadow CFLAGS += -mips32r2 -mno-mips16 -std=gnu99 -O2 -g $(WARNINGS) -DENABLE_ASSERTIONS -DMIPSr1_ASM -DOPUS_BUILD -mdspr2 -march=74kc -mtune=74kc -mmt -mgp32 CINCLUDES = include silk celt ifdef FIXED_POINT CFLAGS += -DFIXED_POINT=1 -DDISABLE_FLOAT_API CINCLUDES += silk/fixed else CINCLUDES += silk/float endif LIBS = m LDLIBDIRS = ./ CFLAGS += $(call cppflags-from-defines,$(CDEFINES)) CFLAGS += $(call cppflags-from-includes,$(CINCLUDES)) LDFLAGS += $(call ldflags-from-ldlibdirs,$(LDLIBDIRS)) LDLIBS += $(call ldlibs-from-libs,$(LIBS)) COMPILE.c.cmdline = $(CC) -c $(CFLAGS) -o $@ $< LINK.o = $(CC) $(LDPREFLAGS) $(LDFLAGS) LINK.o.cmdline = $(LINK.o) $^ $(LDLIBS) -o $@$(EXESUFFIX) ARCHIVE.cmdline = $(AR) $(ARFLAGS) $@ $^ && $(RANLIB) $@ %$(OBJSUFFIX):%.c $(COMPILE.c.cmdline) %$(OBJSUFFIX):%.cpp $(COMPILE.cpp.cmdline) # Directives # Variable definitions LIB_NAME = opus TARGET = $(LIBPREFIX)$(LIB_NAME)$(LIBSUFFIX) SRCS_C = $(SILK_SOURCES) $(CELT_SOURCES) $(OPUS_SOURCES) OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(SRCS_C)) OPUSDEMO_SRCS_C = src/opus_demo.c OPUSDEMO_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(OPUSDEMO_SRCS_C)) TESTOPUSAPI_SRCS_C = tests/test_opus_api.c TESTOPUSAPI_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSAPI_SRCS_C)) TESTOPUSDECODE_SRCS_C = tests/test_opus_decode.c TESTOPUSDECODE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSDECODE_SRCS_C)) TESTOPUSENCODE_SRCS_C = tests/test_opus_encode.c TESTOPUSENCODE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSENCODE_SRCS_C)) TESTOPUSPADDING_SRCS_C = tests/test_opus_padding.c TESTOPUSPADDING_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSPADDING_SRCS_C)) OPUSCOMPARE_SRCS_C = src/opus_compare.c OPUSCOMPARE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(OPUSCOMPARE_SRCS_C)) TESTS := test_opus_api test_opus_decode test_opus_encode test_opus_padding # Rules all: lib opus_demo opus_compare $(TESTS) lib: $(TARGET) check: all for test in $(TESTS); do ./$$test; done $(TARGET): $(OBJS) $(ARCHIVE.cmdline) opus_demo$(EXESUFFIX): $(OPUSDEMO_OBJS) $(TARGET) $(LINK.o.cmdline) test_opus_api$(EXESUFFIX): $(TESTOPUSAPI_OBJS) $(TARGET) $(LINK.o.cmdline) test_opus_decode$(EXESUFFIX): $(TESTOPUSDECODE_OBJS) $(TARGET) $(LINK.o.cmdline) test_opus_encode$(EXESUFFIX): $(TESTOPUSENCODE_OBJS) $(TARGET) $(LINK.o.cmdline) test_opus_padding$(EXESUFFIX): $(TESTOPUSPADDING_OBJS) $(TARGET) $(LINK.o.cmdline) opus_compare$(EXESUFFIX): $(OPUSCOMPARE_OBJS) $(LINK.o.cmdline) celt/celt.o: CFLAGS += -DPACKAGE_VERSION='$(PACKAGE_VERSION)' celt/celt.o: package_version package_version: force @if [ -x ./update_version ]; then \ ./update_version || true; \ elif [ ! -e ./package_version ]; then \ echo 'PACKAGE_VERSION="unknown"' > ./package_version; \ fi force: clean: rm -f opus_demo$(EXESUFFIX) opus_compare$(EXESUFFIX) $(TARGET) \ test_opus_api$(EXESUFFIX) test_opus_decode$(EXESUFFIX) \ test_opus_encode$(EXESUFFIX) test_opus_padding$(EXESUFFIX) \ $(OBJS) $(OPUSDEMO_OBJS) $(OPUSCOMPARE_OBJS) $(TESTOPUSAPI_OBJS) \ $(TESTOPUSDECODE_OBJS) $(TESTOPUSENCODE_OBJS) $(TESTOPUSPADDING_OBJS) .PHONY: all lib clean force check ================================================ FILE: deps/pjsip/third_party/opus/Makefile.unix ================================================ #################### COMPILE OPTIONS ####################### # Uncomment this for fixed-point build #FIXED_POINT=1 # It is strongly recommended to uncomment one of these # VAR_ARRAYS: Use C99 variable-length arrays for stack allocation # USE_ALLOCA: Use alloca() for stack allocation # If none is defined, then the fallback is a non-threadsafe global array CFLAGS := -DUSE_ALLOCA $(CFLAGS) #CFLAGS := -DVAR_ARRAYS $(CFLAGS) # These options affect performance # HAVE_LRINTF: Use C99 intrinsics to speed up float-to-int conversion #CFLAGS := -DHAVE_LRINTF $(CFLAGS) ###################### END OF OPTIONS ###################### -include package_version include silk_sources.mk include celt_sources.mk include opus_sources.mk ifdef FIXED_POINT SILK_SOURCES += $(SILK_SOURCES_FIXED) else SILK_SOURCES += $(SILK_SOURCES_FLOAT) OPUS_SOURCES += $(OPUS_SOURCES_FLOAT) endif EXESUFFIX = LIBPREFIX = lib LIBSUFFIX = .a OBJSUFFIX = .o CC = $(TOOLCHAIN_PREFIX)cc$(TOOLCHAIN_SUFFIX) AR = $(TOOLCHAIN_PREFIX)ar RANLIB = $(TOOLCHAIN_PREFIX)ranlib CP = $(TOOLCHAIN_PREFIX)cp cppflags-from-defines = $(addprefix -D,$(1)) cppflags-from-includes = $(addprefix -I,$(1)) ldflags-from-ldlibdirs = $(addprefix -L,$(1)) ldlibs-from-libs = $(addprefix -l,$(1)) WARNINGS = -Wall -W -Wstrict-prototypes -Wextra -Wcast-align -Wnested-externs -Wshadow CFLAGS += -O2 -g $(WARNINGS) -DOPUS_BUILD CINCLUDES = include silk celt ifdef FIXED_POINT CFLAGS += -DFIXED_POINT=1 -DDISABLE_FLOAT_API CINCLUDES += silk/fixed else CINCLUDES += silk/float endif LIBS = m LDLIBDIRS = ./ CFLAGS += $(call cppflags-from-defines,$(CDEFINES)) CFLAGS += $(call cppflags-from-includes,$(CINCLUDES)) LDFLAGS += $(call ldflags-from-ldlibdirs,$(LDLIBDIRS)) LDLIBS += $(call ldlibs-from-libs,$(LIBS)) COMPILE.c.cmdline = $(CC) -c $(CFLAGS) -o $@ $< LINK.o = $(CC) $(LDPREFLAGS) $(LDFLAGS) LINK.o.cmdline = $(LINK.o) $^ $(LDLIBS) -o $@$(EXESUFFIX) ARCHIVE.cmdline = $(AR) $(ARFLAGS) $@ $^ && $(RANLIB) $@ %$(OBJSUFFIX):%.c $(COMPILE.c.cmdline) %$(OBJSUFFIX):%.cpp $(COMPILE.cpp.cmdline) # Directives # Variable definitions LIB_NAME = opus TARGET = $(LIBPREFIX)$(LIB_NAME)$(LIBSUFFIX) SRCS_C = $(SILK_SOURCES) $(CELT_SOURCES) $(OPUS_SOURCES) OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(SRCS_C)) OPUSDEMO_SRCS_C = src/opus_demo.c OPUSDEMO_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(OPUSDEMO_SRCS_C)) TESTOPUSAPI_SRCS_C = tests/test_opus_api.c TESTOPUSAPI_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSAPI_SRCS_C)) TESTOPUSDECODE_SRCS_C = tests/test_opus_decode.c TESTOPUSDECODE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSDECODE_SRCS_C)) TESTOPUSENCODE_SRCS_C = tests/test_opus_encode.c TESTOPUSENCODE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSENCODE_SRCS_C)) TESTOPUSPADDING_SRCS_C = tests/test_opus_padding.c TESTOPUSPADDING_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSPADDING_SRCS_C)) OPUSCOMPARE_SRCS_C = src/opus_compare.c OPUSCOMPARE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(OPUSCOMPARE_SRCS_C)) TESTS := test_opus_api test_opus_decode test_opus_encode test_opus_padding # Rules all: lib opus_demo opus_compare $(TESTS) lib: $(TARGET) check: all for test in $(TESTS); do ./$$test; done $(TARGET): $(OBJS) $(ARCHIVE.cmdline) opus_demo$(EXESUFFIX): $(OPUSDEMO_OBJS) $(TARGET) $(LINK.o.cmdline) test_opus_api$(EXESUFFIX): $(TESTOPUSAPI_OBJS) $(TARGET) $(LINK.o.cmdline) test_opus_decode$(EXESUFFIX): $(TESTOPUSDECODE_OBJS) $(TARGET) $(LINK.o.cmdline) test_opus_encode$(EXESUFFIX): $(TESTOPUSENCODE_OBJS) $(TARGET) $(LINK.o.cmdline) test_opus_padding$(EXESUFFIX): $(TESTOPUSPADDING_OBJS) $(TARGET) $(LINK.o.cmdline) opus_compare$(EXESUFFIX): $(OPUSCOMPARE_OBJS) $(LINK.o.cmdline) celt/celt.o: CFLAGS += -DPACKAGE_VERSION='$(PACKAGE_VERSION)' celt/celt.o: package_version package_version: force @if [ -x ./update_version ]; then \ ./update_version || true; \ elif [ ! -e ./package_version ]; then \ echo 'PACKAGE_VERSION="unknown"' > ./package_version; \ fi force: clean: rm -f opus_demo$(EXESUFFIX) opus_compare$(EXESUFFIX) $(TARGET) \ test_opus_api$(EXESUFFIX) test_opus_decode$(EXESUFFIX) \ test_opus_encode$(EXESUFFIX) test_opus_padding$(EXESUFFIX) \ $(OBJS) $(OPUSDEMO_OBJS) $(OPUSCOMPARE_OBJS) $(TESTOPUSAPI_OBJS) \ $(TESTOPUSDECODE_OBJS) $(TESTOPUSENCODE_OBJS) $(TESTOPUSPADDING_OBJS) .PHONY: all lib clean force check ================================================ FILE: deps/pjsip/third_party/opus/NEWS ================================================ ================================================ FILE: deps/pjsip/third_party/opus/README ================================================ == Opus audio codec == Opus is a codec for interactive speech and audio transmission over the Internet. Opus can handle a wide range of interactive audio applications, including Voice over IP, videoconferencing, in-game chat, and even remote live music performances. It can scale from low bit-rate narrowband speech to very high quality stereo music. Opus, when coupled with an appropriate container format, is also suitable for non-realtime stored-file applications such as music distribution, game soundtracks, portable music players, jukeboxes, and other applications that have historically used high latency formats such as MP3, AAC, or Vorbis. Opus is specified by IETF RFC 6716: https://tools.ietf.org/html/rfc6716 The Opus format and this implementation of it are subject to the royalty- free patent and copyright licenses specified in the file COPYING. This package implements a shared library for encoding and decoding raw Opus bitstreams. Raw Opus bitstreams should be used over RTP according to https://tools.ietf.org/html/rfc7587 The package also includes a number of test tools used for testing the correct operation of the library. The bitstreams read/written by these tools should not be used for Opus file distribution: They include additional debugging data and cannot support seeking. Opus stored in files should use the Ogg encapsulation for Opus which is described at: https://wiki.xiph.org/OggOpus An opus-tools package is available which provides encoding and decoding of Ogg encapsulated Opus files and includes a number of useful features. Opus-tools can be found at: https://git.xiph.org/?p=opus-tools.git or on the main Opus website: https://opus-codec.org/ == Compiling libopus == To build from a distribution tarball, you only need to do the following: % ./configure % make To build from the git repository, the following steps are necessary: 1) Clone the repository: % git clone https://git.xiph.org/opus.git % cd opus 2) Compiling the source % ./autogen.sh % ./configure % make 3) Install the codec libraries (optional) % sudo make install Once you have compiled the codec, there will be a opus_demo executable in the top directory. Usage: opus_demo [-e] [options] opus_demo -d [options] mode: voip | audio | restricted-lowdelay options: -e : only runs the encoder (output the bit-stream) -d : only runs the decoder (reads the bit-stream as input) -cbr : enable constant bitrate; default: variable bitrate -cvbr : enable constrained variable bitrate; default: unconstrained -bandwidth : audio bandwidth (from narrowband to fullband); default: sampling rate -framesize <2.5|5|10|20|40|60> : frame size in ms; default: 20 -max_payload : maximum payload size in bytes, default: 1024 -complexity : complexity, 0 (lowest) ... 10 (highest); default: 10 -inbandfec : enable SILK inband FEC -forcemono : force mono encoding, even for stereo input -dtx : enable SILK DTX -loss : simulate packet loss, in percent (0-100); default: 0 input and output are little-endian signed 16-bit PCM files or opus bitstreams with simple opus_demo proprietary framing. == Testing == This package includes a collection of automated unit and system tests which SHOULD be run after compiling the package especially the first time it is run on a new platform. To run the integrated tests: % make check There is also collection of standard test vectors which are not included in this package for size reasons but can be obtained from: https://opus-codec.org/testvectors/opus_testvectors.tar.gz To run compare the code to these test vectors: % curl -O https://opus-codec.org/testvectors/opus_testvectors.tar.gz % tar -zxf opus_testvectors.tar.gz % ./tests/run_vectors.sh ./ opus_testvectors 48000 == Portability notes == This implementation uses floating-point by default but can be compiled to use only fixed-point arithmetic by setting --enable-fixed-point (if using autoconf) or by defining the FIXED_POINT macro (if building manually). The fixed point implementation has somewhat lower audio quality and is slower on platforms with fast FPUs, it is normally only used in embedded environments. The implementation can be compiled with either a C89 or a C99 compiler. While it does not rely on any _undefined behavior_ as defined by C89 or C99, it relies on common _implementation-defined behavior_ for two's complement architectures: o Right shifts of negative values are consistent with two's complement arithmetic, so that a>>b is equivalent to floor(a/(2^b)), o For conversion to a signed integer of N bits, the value is reduced modulo 2^N to be within range of the type, o The result of integer division of a negative value is truncated towards zero, and o The compiler provides a 64-bit integer type (a C99 requirement which is supported by most C89 compilers). ================================================ FILE: deps/pjsip/third_party/opus/celt/_kiss_fft_guts.h ================================================ /*Copyright (c) 2003-2004, Mark Borgerding All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ #ifndef KISS_FFT_GUTS_H #define KISS_FFT_GUTS_H #define MIN(a,b) ((a)<(b) ? (a):(b)) #define MAX(a,b) ((a)>(b) ? (a):(b)) /* kiss_fft.h defines kiss_fft_scalar as either short or a float type and defines typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */ #include "kiss_fft.h" /* Explanation of macros dealing with complex math: C_MUL(m,a,b) : m = a*b C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise C_SUB( res, a,b) : res = a - b C_SUBFROM( res , a) : res -= a C_ADDTO( res , a) : res += a * */ #ifdef FIXED_POINT #include "arch.h" #define SAMP_MAX 2147483647 #define TWID_MAX 32767 #define TRIG_UPSCALE 1 #define SAMP_MIN -SAMP_MAX # define S_MUL(a,b) MULT16_32_Q15(b, a) # define C_MUL(m,a,b) \ do{ (m).r = SUB32(S_MUL((a).r,(b).r) , S_MUL((a).i,(b).i)); \ (m).i = ADD32(S_MUL((a).r,(b).i) , S_MUL((a).i,(b).r)); }while(0) # define C_MULC(m,a,b) \ do{ (m).r = ADD32(S_MUL((a).r,(b).r) , S_MUL((a).i,(b).i)); \ (m).i = SUB32(S_MUL((a).i,(b).r) , S_MUL((a).r,(b).i)); }while(0) # define C_MULBYSCALAR( c, s ) \ do{ (c).r = S_MUL( (c).r , s ) ;\ (c).i = S_MUL( (c).i , s ) ; }while(0) # define DIVSCALAR(x,k) \ (x) = S_MUL( x, (TWID_MAX-((k)>>1))/(k)+1 ) # define C_FIXDIV(c,div) \ do { DIVSCALAR( (c).r , div); \ DIVSCALAR( (c).i , div); }while (0) #define C_ADD( res, a,b)\ do {(res).r=ADD32((a).r,(b).r); (res).i=ADD32((a).i,(b).i); \ }while(0) #define C_SUB( res, a,b)\ do {(res).r=SUB32((a).r,(b).r); (res).i=SUB32((a).i,(b).i); \ }while(0) #define C_ADDTO( res , a)\ do {(res).r = ADD32((res).r, (a).r); (res).i = ADD32((res).i,(a).i);\ }while(0) #define C_SUBFROM( res , a)\ do {(res).r = ADD32((res).r,(a).r); (res).i = SUB32((res).i,(a).i); \ }while(0) #if defined(OPUS_ARM_INLINE_ASM) #include "arm/kiss_fft_armv4.h" #endif #if defined(OPUS_ARM_INLINE_EDSP) #include "arm/kiss_fft_armv5e.h" #endif #if defined(MIPSr1_ASM) #include "mips/kiss_fft_mipsr1.h" #endif #else /* not FIXED_POINT*/ # define S_MUL(a,b) ( (a)*(b) ) #define C_MUL(m,a,b) \ do{ (m).r = (a).r*(b).r - (a).i*(b).i;\ (m).i = (a).r*(b).i + (a).i*(b).r; }while(0) #define C_MULC(m,a,b) \ do{ (m).r = (a).r*(b).r + (a).i*(b).i;\ (m).i = (a).i*(b).r - (a).r*(b).i; }while(0) #define C_MUL4(m,a,b) C_MUL(m,a,b) # define C_FIXDIV(c,div) /* NOOP */ # define C_MULBYSCALAR( c, s ) \ do{ (c).r *= (s);\ (c).i *= (s); }while(0) #endif #ifndef CHECK_OVERFLOW_OP # define CHECK_OVERFLOW_OP(a,op,b) /* noop */ #endif #ifndef C_ADD #define C_ADD( res, a,b)\ do { \ CHECK_OVERFLOW_OP((a).r,+,(b).r)\ CHECK_OVERFLOW_OP((a).i,+,(b).i)\ (res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \ }while(0) #define C_SUB( res, a,b)\ do { \ CHECK_OVERFLOW_OP((a).r,-,(b).r)\ CHECK_OVERFLOW_OP((a).i,-,(b).i)\ (res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \ }while(0) #define C_ADDTO( res , a)\ do { \ CHECK_OVERFLOW_OP((res).r,+,(a).r)\ CHECK_OVERFLOW_OP((res).i,+,(a).i)\ (res).r += (a).r; (res).i += (a).i;\ }while(0) #define C_SUBFROM( res , a)\ do {\ CHECK_OVERFLOW_OP((res).r,-,(a).r)\ CHECK_OVERFLOW_OP((res).i,-,(a).i)\ (res).r -= (a).r; (res).i -= (a).i; \ }while(0) #endif /* C_ADD defined */ #ifdef FIXED_POINT /*# define KISS_FFT_COS(phase) TRIG_UPSCALE*floor(MIN(32767,MAX(-32767,.5+32768 * cos (phase)))) # define KISS_FFT_SIN(phase) TRIG_UPSCALE*floor(MIN(32767,MAX(-32767,.5+32768 * sin (phase))))*/ # define KISS_FFT_COS(phase) floor(.5+TWID_MAX*cos (phase)) # define KISS_FFT_SIN(phase) floor(.5+TWID_MAX*sin (phase)) # define HALF_OF(x) ((x)>>1) #elif defined(USE_SIMD) # define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) ) # define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) ) # define HALF_OF(x) ((x)*_mm_set1_ps(.5f)) #else # define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase) # define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase) # define HALF_OF(x) ((x)*.5f) #endif #define kf_cexp(x,phase) \ do{ \ (x)->r = KISS_FFT_COS(phase);\ (x)->i = KISS_FFT_SIN(phase);\ }while(0) #define kf_cexp2(x,phase) \ do{ \ (x)->r = TRIG_UPSCALE*celt_cos_norm((phase));\ (x)->i = TRIG_UPSCALE*celt_cos_norm((phase)-32768);\ }while(0) #endif /* KISS_FFT_GUTS_H */ ================================================ FILE: deps/pjsip/third_party/opus/celt/arch.h ================================================ /* Copyright (c) 2003-2008 Jean-Marc Valin Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Written by Jean-Marc Valin */ /** @file arch.h @brief Various architecture definitions for CELT */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef ARCH_H #define ARCH_H #include "opus_types.h" #include "opus_defines.h" # if !defined(__GNUC_PREREQ) # if defined(__GNUC__)&&defined(__GNUC_MINOR__) # define __GNUC_PREREQ(_maj,_min) \ ((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min)) # else # define __GNUC_PREREQ(_maj,_min) 0 # endif # endif #define CELT_SIG_SCALE 32768.f #define celt_fatal(str) _celt_fatal(str, __FILE__, __LINE__); #ifdef ENABLE_ASSERTIONS #include #include #ifdef __GNUC__ __attribute__((noreturn)) #endif static OPUS_INLINE void _celt_fatal(const char *str, const char *file, int line) { fprintf (stderr, "Fatal (internal) error in %s, line %d: %s\n", file, line, str); abort(); } #define celt_assert(cond) {if (!(cond)) {celt_fatal("assertion failed: " #cond);}} #define celt_assert2(cond, message) {if (!(cond)) {celt_fatal("assertion failed: " #cond "\n" message);}} #else #define celt_assert(cond) #define celt_assert2(cond, message) #endif #define IMUL32(a,b) ((a)*(b)) #define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum 16-bit value. */ #define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */ #define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum 32-bit value. */ #define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */ #define IMIN(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum int value. */ #define IMAX(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum int value. */ #define UADD32(a,b) ((a)+(b)) #define USUB32(a,b) ((a)-(b)) #define PRINT_MIPS(file) #ifdef FIXED_POINT typedef opus_int16 opus_val16; typedef opus_int32 opus_val32; typedef opus_val32 celt_sig; typedef opus_val16 celt_norm; typedef opus_val32 celt_ener; #define Q15ONE 32767 #define SIG_SHIFT 12 #define NORM_SCALING 16384 #define DB_SHIFT 10 #define EPSILON 1 #define VERY_SMALL 0 #define VERY_LARGE16 ((opus_val16)32767) #define Q15_ONE ((opus_val16)32767) #define SCALEIN(a) (a) #define SCALEOUT(a) (a) #define ABS16(x) ((x) < 0 ? (-(x)) : (x)) #define ABS32(x) ((x) < 0 ? (-(x)) : (x)) static OPUS_INLINE opus_int16 SAT16(opus_int32 x) { return x > 32767 ? 32767 : x < -32768 ? -32768 : (opus_int16)x; } #ifdef FIXED_DEBUG #include "fixed_debug.h" #else #include "fixed_generic.h" #ifdef OPUS_ARM_INLINE_EDSP #include "arm/fixed_armv5e.h" #elif defined (OPUS_ARM_INLINE_ASM) #include "arm/fixed_armv4.h" #elif defined (BFIN_ASM) #include "fixed_bfin.h" #elif defined (TI_C5X_ASM) #include "fixed_c5x.h" #elif defined (TI_C6X_ASM) #include "fixed_c6x.h" #endif #endif #else /* FIXED_POINT */ typedef float opus_val16; typedef float opus_val32; typedef float celt_sig; typedef float celt_norm; typedef float celt_ener; #ifdef FLOAT_APPROX /* This code should reliably detect NaN/inf even when -ffast-math is used. Assumes IEEE 754 format. */ static OPUS_INLINE int celt_isnan(float x) { union {float f; opus_uint32 i;} in; in.f = x; return ((in.i>>23)&0xFF)==0xFF && (in.i&0x007FFFFF)!=0; } #else #ifdef __FAST_MATH__ #error Cannot build libopus with -ffast-math unless FLOAT_APPROX is defined. This could result in crashes on extreme (e.g. NaN) input #endif #define celt_isnan(x) ((x)!=(x)) #endif #define Q15ONE 1.0f #define NORM_SCALING 1.f #define EPSILON 1e-15f #define VERY_SMALL 1e-30f #define VERY_LARGE16 1e15f #define Q15_ONE ((opus_val16)1.f) /* This appears to be the same speed as C99's fabsf() but it's more portable. */ #define ABS16(x) ((float)fabs(x)) #define ABS32(x) ((float)fabs(x)) #define QCONST16(x,bits) (x) #define QCONST32(x,bits) (x) #define NEG16(x) (-(x)) #define NEG32(x) (-(x)) #define EXTRACT16(x) (x) #define EXTEND32(x) (x) #define SHR16(a,shift) (a) #define SHL16(a,shift) (a) #define SHR32(a,shift) (a) #define SHL32(a,shift) (a) #define PSHR32(a,shift) (a) #define VSHR32(a,shift) (a) #define PSHR(a,shift) (a) #define SHR(a,shift) (a) #define SHL(a,shift) (a) #define SATURATE(x,a) (x) #define SATURATE16(x) (x) #define ROUND16(a,shift) (a) #define HALF16(x) (.5f*(x)) #define HALF32(x) (.5f*(x)) #define ADD16(a,b) ((a)+(b)) #define SUB16(a,b) ((a)-(b)) #define ADD32(a,b) ((a)+(b)) #define SUB32(a,b) ((a)-(b)) #define MULT16_16_16(a,b) ((a)*(b)) #define MULT16_16(a,b) ((opus_val32)(a)*(opus_val32)(b)) #define MAC16_16(c,a,b) ((c)+(opus_val32)(a)*(opus_val32)(b)) #define MULT16_32_Q15(a,b) ((a)*(b)) #define MULT16_32_Q16(a,b) ((a)*(b)) #define MULT32_32_Q31(a,b) ((a)*(b)) #define MAC16_32_Q15(c,a,b) ((c)+(a)*(b)) #define MAC16_32_Q16(c,a,b) ((c)+(a)*(b)) #define MULT16_16_Q11_32(a,b) ((a)*(b)) #define MULT16_16_Q11(a,b) ((a)*(b)) #define MULT16_16_Q13(a,b) ((a)*(b)) #define MULT16_16_Q14(a,b) ((a)*(b)) #define MULT16_16_Q15(a,b) ((a)*(b)) #define MULT16_16_P15(a,b) ((a)*(b)) #define MULT16_16_P13(a,b) ((a)*(b)) #define MULT16_16_P14(a,b) ((a)*(b)) #define MULT16_32_P16(a,b) ((a)*(b)) #define DIV32_16(a,b) (((opus_val32)(a))/(opus_val16)(b)) #define DIV32(a,b) (((opus_val32)(a))/(opus_val32)(b)) #define SCALEIN(a) ((a)*CELT_SIG_SCALE) #define SCALEOUT(a) ((a)*(1/CELT_SIG_SCALE)) #define SIG2WORD16(x) (x) #endif /* !FIXED_POINT */ #ifndef GLOBAL_STACK_SIZE #ifdef FIXED_POINT #define GLOBAL_STACK_SIZE 100000 #else #define GLOBAL_STACK_SIZE 100000 #endif #endif #endif /* ARCH_H */ ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/arm2gnu.pl ================================================ #!/usr/bin/perl # Copyright (C) 2002-2013 Xiph.org Foundation # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # - Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # - Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER # OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. my $bigend; # little/big endian my $nxstack; my $apple = 0; my $symprefix = ""; $nxstack = 0; eval 'exec /usr/local/bin/perl -S $0 ${1+"$@"}' if $running_under_some_shell; while ($ARGV[0] =~ /^-/) { $_ = shift; last if /^--$/; if (/^-n$/) { $nflag++; next; } if (/^--apple$/) { $apple = 1; $symprefix = "_"; next; } die "I don't recognize this switch: $_\\n"; } $printit++ unless $nflag; $\ = "\n"; # automatically add newline on print $n=0; $thumb = 0; # ARM mode by default, not Thumb. @proc_stack = (); printf (" .syntax unified\n"); LINE: while (<>) { # For ADRLs we need to add a new line after the substituted one. $addPadding = 0; # First, we do not dare to touch *anything* inside double quotes, do we? # Second, if you want a dollar character in the string, # insert two of them -- that's how ARM C and assembler treat strings. s/^([A-Za-z_]\w*)[ \t]+DCB[ \t]*\"/$1: .ascii \"/ && do { s/\$\$/\$/g; next }; s/\bDCB\b[ \t]*\"/.ascii \"/ && do { s/\$\$/\$/g; next }; s/^(\S+)\s+RN\s+(\S+)/$1 .req r$2/ && do { s/\$\$/\$/g; next }; # If there's nothing on a line but a comment, don't try to apply any further # substitutions (this is a cheap hack to avoid mucking up the license header) s/^([ \t]*);/$1@/ && do { s/\$\$/\$/g; next }; # If substituted -- leave immediately ! s/@/,:/; s/;/@/; while ( /@.*'/ ) { s/(@.*)'/$1/g; } s/\{FALSE\}/0/g; s/\{TRUE\}/1/g; s/\{(\w\w\w\w+)\}/$1/g; s/\bINCLUDE[ \t]*([^ \t\n]+)/.include \"$1\"/; s/\bGET[ \t]*([^ \t\n]+)/.include \"${ my $x=$1; $x =~ s|\.s|-gnu.S|; \$x }\"/; s/\bIMPORT\b/.extern/; s/\bEXPORT\b\s*/.global $symprefix/; s/^(\s+)\[/$1IF/; s/^(\s+)\|/$1ELSE/; s/^(\s+)\]/$1ENDIF/; s/IF *:DEF:/ .ifdef/; s/IF *:LNOT: *:DEF:/ .ifndef/; s/ELSE/ .else/; s/ENDIF/ .endif/; if( /\bIF\b/ ) { s/\bIF\b/ .if/; s/=/==/; } if ( $n == 2) { s/\$/\\/g; } if ($n == 1) { s/\$//g; s/label//g; $n = 2; } if ( /MACRO/ ) { s/MACRO *\n/.macro/; $n=1; } if ( /\bMEND\b/ ) { s/\bMEND\b/.endm/; $n=0; } # ".rdata" doesn't work in 'as' version 2.13.2, as it is ".rodata" there. # if ( /\bAREA\b/ ) { my $align; $align = "2"; if ( /ALIGN=(\d+)/ ) { $align = $1; } if ( /CODE/ ) { $nxstack = 1; } s/^(.+)CODE(.+)READONLY(.*)/ .text/; s/^(.+)DATA(.+)READONLY(.*)/ .section .rdata/; s/^(.+)\|\|\.data\|\|(.+)/ .data/; s/^(.+)\|\|\.bss\|\|(.+)/ .bss/; s/$/; .p2align $align/; # Enable NEON instructions but don't produce a binary that requires # ARMv7. RVCT does not have equivalent directives, so we just do this # for all CODE areas. if ( /.text/ ) { # Separating .arch, .fpu, etc., by semicolons does not work (gas # thinks the semicolon is part of the arch name, even when there's # whitespace separating them). Sadly this means our line numbers # won't match the original source file (we could use the .line # directive, which is documented to be obsolete, but then gdb will # show the wrong line in the translated source file). s/$/; .arch armv7-a\n .fpu neon\n .object_arch armv4t/ unless ($apple); } } s/\|\|\.constdata\$(\d+)\|\|/.L_CONST$1/; # ||.constdata$3|| s/\|\|\.bss\$(\d+)\|\|/.L_BSS$1/; # ||.bss$2|| s/\|\|\.data\$(\d+)\|\|/.L_DATA$1/; # ||.data$2|| s/\|\|([a-zA-Z0-9_]+)\@([a-zA-Z0-9_]+)\|\|/@ $&/; s/^(\s+)\%(\s)/ .space $1/; s/\|(.+)\.(\d+)\|/\.$1_$2/; # |L80.123| -> .L80_123 s/\bCODE32\b/.code 32/ && do {$thumb = 0}; s/\bCODE16\b/.code 16/ && do {$thumb = 1}; if (/\bPROC\b/) { my $prefix; my $proc; /^([A-Za-z_\.]\w+)\b/; $proc = $1; $prefix = ""; if ($proc) { $prefix = $prefix.sprintf("\t.type\t%s, %%function; ",$proc) unless ($apple); # Make sure we $prefix isn't empty here (for the $apple case). # We handle mangling the label here, make sure it doesn't match # the label handling below (if $prefix would be empty). $prefix = "; "; push(@proc_stack, $proc); s/^[A-Za-z_\.]\w+/$symprefix$&:/; } $prefix = $prefix."\t.thumb_func; " if ($thumb); s/\bPROC\b/@ $&/; $_ = $prefix.$_; } s/^(\s*)(S|Q|SH|U|UQ|UH)ASX\b/$1$2ADDSUBX/; s/^(\s*)(S|Q|SH|U|UQ|UH)SAX\b/$1$2SUBADDX/; if (/\bENDP\b/) { my $proc; s/\bENDP\b/@ $&/; $proc = pop(@proc_stack); $_ = "\t.size $proc, .-$proc".$_ if ($proc && !$apple); } s/\bSUBT\b/@ $&/; s/\bDATA\b/@ $&/; # DATA directive is deprecated -- Asm guide, p.7-25 s/\bKEEP\b/@ $&/; s/\bEXPORTAS\b/@ $&/; s/\|\|(.)+\bEQU\b/@ $&/; s/\|\|([\w\$]+)\|\|/$1/; s/\bENTRY\b/@ $&/; s/\bASSERT\b/@ $&/; s/\bGBLL\b/@ $&/; s/\bGBLA\b/@ $&/; s/^\W+OPT\b/@ $&/; s/:OR:/|/g; s/:SHL:/<>/g; s/:AND:/&/g; s/:LAND:/&&/g; s/CPSR/cpsr/; s/SPSR/spsr/; s/ALIGN$/.balign 4/; s/ALIGN\s+([0-9x]+)$/.balign $1/; s/psr_cxsf/psr_all/; s/LTORG/.ltorg/; s/^([A-Za-z_]\w*)[ \t]+EQU/ .set $1,/; s/^([A-Za-z_]\w*)[ \t]+SETL/ .set $1,/; s/^([A-Za-z_]\w*)[ \t]+SETA/ .set $1,/; s/^([A-Za-z_]\w*)[ \t]+\*/ .set $1,/; # {PC} + 0xdeadfeed --> . + 0xdeadfeed s/\{PC\} \+/ \. +/; # Single hex constant on the line ! # # >>> NOTE <<< # Double-precision floats in gcc are always mixed-endian, which means # bytes in two words are little-endian, but words are big-endian. # So, 0x0000deadfeed0000 would be stored as 0x0000dead at low address # and 0xfeed0000 at high address. # s/\bDCFD\b[ \t]+0x([a-fA-F0-9]{8})([a-fA-F0-9]{8})/.long 0x$1, 0x$2/; # Only decimal constants on the line, no hex ! s/\bDCFD\b[ \t]+([0-9\.\-]+)/.double $1/; # Single hex constant on the line ! # s/\bDCFS\b[ \t]+0x([a-f0-9]{8})([a-f0-9]{8})/.long 0x$1, 0x$2/; # Only decimal constants on the line, no hex ! # s/\bDCFS\b[ \t]+([0-9\.\-]+)/.double $1/; s/\bDCFS[ \t]+0x/.word 0x/; s/\bDCFS\b/.float/; s/^([A-Za-z_]\w*)[ \t]+DCD/$1 .word/; s/\bDCD\b/.word/; s/^([A-Za-z_]\w*)[ \t]+DCW/$1 .short/; s/\bDCW\b/.short/; s/^([A-Za-z_]\w*)[ \t]+DCB/$1 .byte/; s/\bDCB\b/.byte/; s/^([A-Za-z_]\w*)[ \t]+\%/.comm $1,/; s/^[A-Za-z_\.]\w+/$&:/; s/^(\d+)/$1:/; s/\%(\d+)/$1b_or_f/; s/\%[Bb](\d+)/$1b/; s/\%[Ff](\d+)/$1f/; s/\%[Ff][Tt](\d+)/$1f/; s/&([\dA-Fa-f]+)/0x$1/; if ( /\b2_[01]+\b/ ) { s/\b2_([01]+)\b/conv$1&&&&/g; while ( /[01][01][01][01]&&&&/ ) { s/0000&&&&/&&&&0/g; s/0001&&&&/&&&&1/g; s/0010&&&&/&&&&2/g; s/0011&&&&/&&&&3/g; s/0100&&&&/&&&&4/g; s/0101&&&&/&&&&5/g; s/0110&&&&/&&&&6/g; s/0111&&&&/&&&&7/g; s/1000&&&&/&&&&8/g; s/1001&&&&/&&&&9/g; s/1010&&&&/&&&&A/g; s/1011&&&&/&&&&B/g; s/1100&&&&/&&&&C/g; s/1101&&&&/&&&&D/g; s/1110&&&&/&&&&E/g; s/1111&&&&/&&&&F/g; } s/000&&&&/&&&&0/g; s/001&&&&/&&&&1/g; s/010&&&&/&&&&2/g; s/011&&&&/&&&&3/g; s/100&&&&/&&&&4/g; s/101&&&&/&&&&5/g; s/110&&&&/&&&&6/g; s/111&&&&/&&&&7/g; s/00&&&&/&&&&0/g; s/01&&&&/&&&&1/g; s/10&&&&/&&&&2/g; s/11&&&&/&&&&3/g; s/0&&&&/&&&&0/g; s/1&&&&/&&&&1/g; s/conv&&&&/0x/g; } if ( /commandline/) { if( /-bigend/) { $bigend=1; } } if ( /\bDCDU\b/ ) { my $cmd=$_; my $value; my $prefix; my $w1; my $w2; my $w3; my $w4; s/\s+DCDU\b/@ $&/; $cmd =~ /\bDCDU\b\s+0x(\d+)/; $value = $1; $value =~ /(\w\w)(\w\w)(\w\w)(\w\w)/; $w1 = $1; $w2 = $2; $w3 = $3; $w4 = $4; if( $bigend ne "") { # big endian $prefix = "\t.byte\t0x".$w1.";". "\t.byte\t0x".$w2.";". "\t.byte\t0x".$w3.";". "\t.byte\t0x".$w4."; "; } else { # little endian $prefix = "\t.byte\t0x".$w4.";". "\t.byte\t0x".$w3.";". "\t.byte\t0x".$w2.";". "\t.byte\t0x".$w1."; "; } $_=$prefix.$_; } if ( /\badrl\b/i ) { s/\badrl\s+(\w+)\s*,\s*(\w+)/ldr $1,=$2/i; $addPadding = 1; } s/\bEND\b/@ END/; } continue { printf ("%s", $_) if $printit; if ($addPadding != 0) { printf (" mov r0,r0\n"); $addPadding = 0; } } #If we had a code section, mark that this object doesn't need an executable # stack. if ($nxstack && !$apple) { printf (" .section\t.note.GNU-stack,\"\",\%\%progbits\n"); } ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/arm_celt_map.c ================================================ /* Copyright (c) 2010 Xiph.Org Foundation * Copyright (c) 2013 Parrot */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pitch.h" #include "kiss_fft.h" #include "mdct.h" #if defined(OPUS_HAVE_RTCD) # if defined(FIXED_POINT) opus_val32 (*const CELT_PITCH_XCORR_IMPL[OPUS_ARCHMASK+1])(const opus_val16 *, const opus_val16 *, opus_val32 *, int , int) = { celt_pitch_xcorr_c, /* ARMv4 */ MAY_HAVE_EDSP(celt_pitch_xcorr), /* EDSP */ MAY_HAVE_MEDIA(celt_pitch_xcorr), /* Media */ MAY_HAVE_NEON(celt_pitch_xcorr) /* NEON */ }; # else /* !FIXED_POINT */ # if defined(OPUS_ARM_MAY_HAVE_NEON_INTR) void (*const CELT_PITCH_XCORR_IMPL[OPUS_ARCHMASK+1])(const opus_val16 *, const opus_val16 *, opus_val32 *, int, int) = { celt_pitch_xcorr_c, /* ARMv4 */ celt_pitch_xcorr_c, /* EDSP */ celt_pitch_xcorr_c, /* Media */ celt_pitch_xcorr_float_neon /* Neon */ }; # endif # endif /* FIXED_POINT */ # if defined(OPUS_ARM_MAY_HAVE_NEON_INTR) # if defined(HAVE_ARM_NE10) # if defined(CUSTOM_MODES) int (*const OPUS_FFT_ALLOC_ARCH_IMPL[OPUS_ARCHMASK+1])(kiss_fft_state *st) = { opus_fft_alloc_arch_c, /* ARMv4 */ opus_fft_alloc_arch_c, /* EDSP */ opus_fft_alloc_arch_c, /* Media */ opus_fft_alloc_arm_neon /* Neon with NE10 library support */ }; void (*const OPUS_FFT_FREE_ARCH_IMPL[OPUS_ARCHMASK+1])(kiss_fft_state *st) = { opus_fft_free_arch_c, /* ARMv4 */ opus_fft_free_arch_c, /* EDSP */ opus_fft_free_arch_c, /* Media */ opus_fft_free_arm_neon /* Neon with NE10 */ }; # endif /* CUSTOM_MODES */ void (*const OPUS_FFT[OPUS_ARCHMASK+1])(const kiss_fft_state *cfg, const kiss_fft_cpx *fin, kiss_fft_cpx *fout) = { opus_fft_c, /* ARMv4 */ opus_fft_c, /* EDSP */ opus_fft_c, /* Media */ opus_fft_neon /* Neon with NE10 */ }; void (*const OPUS_IFFT[OPUS_ARCHMASK+1])(const kiss_fft_state *cfg, const kiss_fft_cpx *fin, kiss_fft_cpx *fout) = { opus_ifft_c, /* ARMv4 */ opus_ifft_c, /* EDSP */ opus_ifft_c, /* Media */ opus_ifft_neon /* Neon with NE10 */ }; void (*const CLT_MDCT_FORWARD_IMPL[OPUS_ARCHMASK+1])(const mdct_lookup *l, kiss_fft_scalar *in, kiss_fft_scalar * OPUS_RESTRICT out, const opus_val16 *window, int overlap, int shift, int stride, int arch) = { clt_mdct_forward_c, /* ARMv4 */ clt_mdct_forward_c, /* EDSP */ clt_mdct_forward_c, /* Media */ clt_mdct_forward_neon /* Neon with NE10 */ }; void (*const CLT_MDCT_BACKWARD_IMPL[OPUS_ARCHMASK+1])(const mdct_lookup *l, kiss_fft_scalar *in, kiss_fft_scalar * OPUS_RESTRICT out, const opus_val16 *window, int overlap, int shift, int stride, int arch) = { clt_mdct_backward_c, /* ARMv4 */ clt_mdct_backward_c, /* EDSP */ clt_mdct_backward_c, /* Media */ clt_mdct_backward_neon /* Neon with NE10 */ }; # endif /* HAVE_ARM_NE10 */ # endif /* OPUS_ARM_MAY_HAVE_NEON_INTR */ #endif /* OPUS_HAVE_RTCD */ ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/armcpu.c ================================================ /* Copyright (c) 2010 Xiph.Org Foundation * Copyright (c) 2013 Parrot */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Original code from libtheora modified to suit to Opus */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef OPUS_HAVE_RTCD #include "armcpu.h" #include "cpu_support.h" #include "os_support.h" #include "opus_types.h" #define OPUS_CPU_ARM_V4 (1) #define OPUS_CPU_ARM_EDSP (1<<1) #define OPUS_CPU_ARM_MEDIA (1<<2) #define OPUS_CPU_ARM_NEON (1<<3) #if defined(_MSC_VER) /*For GetExceptionCode() and EXCEPTION_ILLEGAL_INSTRUCTION.*/ # define WIN32_LEAN_AND_MEAN # define WIN32_EXTRA_LEAN # include static OPUS_INLINE opus_uint32 opus_cpu_capabilities(void){ opus_uint32 flags; flags=0; /* MSVC has no OPUS_INLINE __asm support for ARM, but it does let you __emit * instructions via their assembled hex code. * All of these instructions should be essentially nops. */ # if defined(OPUS_ARM_MAY_HAVE_EDSP) __try{ /*PLD [r13]*/ __emit(0xF5DDF000); flags|=OPUS_CPU_ARM_EDSP; } __except(GetExceptionCode()==EXCEPTION_ILLEGAL_INSTRUCTION){ /*Ignore exception.*/ } # if defined(OPUS_ARM_MAY_HAVE_MEDIA) __try{ /*SHADD8 r3,r3,r3*/ __emit(0xE6333F93); flags|=OPUS_CPU_ARM_MEDIA; } __except(GetExceptionCode()==EXCEPTION_ILLEGAL_INSTRUCTION){ /*Ignore exception.*/ } # if defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) __try{ /*VORR q0,q0,q0*/ __emit(0xF2200150); flags|=OPUS_CPU_ARM_NEON; } __except(GetExceptionCode()==EXCEPTION_ILLEGAL_INSTRUCTION){ /*Ignore exception.*/ } # endif # endif # endif return flags; } #elif defined(__linux__) /* Linux based */ opus_uint32 opus_cpu_capabilities(void) { opus_uint32 flags = 0; FILE *cpuinfo; /* Reading /proc/self/auxv would be easier, but that doesn't work reliably on * Android */ cpuinfo = fopen("/proc/cpuinfo", "r"); if(cpuinfo != NULL) { /* 512 should be enough for anybody (it's even enough for all the flags that * x86 has accumulated... so far). */ char buf[512]; while(fgets(buf, 512, cpuinfo) != NULL) { # if defined(OPUS_ARM_MAY_HAVE_EDSP) || defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) /* Search for edsp and neon flag */ if(memcmp(buf, "Features", 8) == 0) { char *p; # if defined(OPUS_ARM_MAY_HAVE_EDSP) p = strstr(buf, " edsp"); if(p != NULL && (p[5] == ' ' || p[5] == '\n')) flags |= OPUS_CPU_ARM_EDSP; # endif # if defined(OPUS_ARM_MAY_HAVE_NEON) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) p = strstr(buf, " neon"); if(p != NULL && (p[5] == ' ' || p[5] == '\n')) flags |= OPUS_CPU_ARM_NEON; # endif } # endif # if defined(OPUS_ARM_MAY_HAVE_MEDIA) /* Search for media capabilities (>= ARMv6) */ if(memcmp(buf, "CPU architecture:", 17) == 0) { int version; version = atoi(buf+17); if(version >= 6) flags |= OPUS_CPU_ARM_MEDIA; } # endif } fclose(cpuinfo); } return flags; } #else /* The feature registers which can tell us what the processor supports are * accessible in priveleged modes only, so we can't have a general user-space * detection method like on x86.*/ # error "Configured to use ARM asm but no CPU detection method available for " \ "your platform. Reconfigure with --disable-rtcd (or send patches)." #endif int opus_select_arch(void) { opus_uint32 flags = opus_cpu_capabilities(); int arch = 0; if(!(flags & OPUS_CPU_ARM_EDSP)) return arch; arch++; if(!(flags & OPUS_CPU_ARM_MEDIA)) return arch; arch++; if(!(flags & OPUS_CPU_ARM_NEON)) return arch; arch++; return arch; } #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/armcpu.h ================================================ /* Copyright (c) 2010 Xiph.Org Foundation * Copyright (c) 2013 Parrot */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if !defined(ARMCPU_H) # define ARMCPU_H # if defined(OPUS_ARM_MAY_HAVE_EDSP) # define MAY_HAVE_EDSP(name) name ## _edsp # else # define MAY_HAVE_EDSP(name) name ## _c # endif # if defined(OPUS_ARM_MAY_HAVE_MEDIA) # define MAY_HAVE_MEDIA(name) name ## _media # else # define MAY_HAVE_MEDIA(name) MAY_HAVE_EDSP(name) # endif # if defined(OPUS_ARM_MAY_HAVE_NEON) # define MAY_HAVE_NEON(name) name ## _neon # else # define MAY_HAVE_NEON(name) MAY_HAVE_MEDIA(name) # endif # if defined(OPUS_ARM_PRESUME_EDSP) # define PRESUME_EDSP(name) name ## _edsp # else # define PRESUME_EDSP(name) name ## _c # endif # if defined(OPUS_ARM_PRESUME_MEDIA) # define PRESUME_MEDIA(name) name ## _media # else # define PRESUME_MEDIA(name) PRESUME_EDSP(name) # endif # if defined(OPUS_ARM_PRESUME_NEON) # define PRESUME_NEON(name) name ## _neon # else # define PRESUME_NEON(name) PRESUME_MEDIA(name) # endif # if defined(OPUS_HAVE_RTCD) int opus_select_arch(void); # endif #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/armopts.s.in ================================================ /* Copyright (C) 2013 Mozilla Corporation */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ ; Set the following to 1 if we have EDSP instructions ; (LDRD/STRD, etc., ARMv5E and later). OPUS_ARM_MAY_HAVE_EDSP * @OPUS_ARM_MAY_HAVE_EDSP@ ; Set the following to 1 if we have ARMv6 media instructions. OPUS_ARM_MAY_HAVE_MEDIA * @OPUS_ARM_MAY_HAVE_MEDIA@ ; Set the following to 1 if we have NEON (some ARMv7) OPUS_ARM_MAY_HAVE_NEON * @OPUS_ARM_MAY_HAVE_NEON@ END ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/celt_ne10_fft.c ================================================ /* Copyright (c) 2015 Xiph.Org Foundation Written by Viswanath Puttagunta */ /** @file celt_ne10_fft.c @brief ARM Neon optimizations for fft using NE10 library */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SKIP_CONFIG_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #endif #include #include #include "os_support.h" #include "kiss_fft.h" #include "stack_alloc.h" #if !defined(FIXED_POINT) # define NE10_FFT_ALLOC_C2C_TYPE_NEON ne10_fft_alloc_c2c_float32_neon # define NE10_FFT_CFG_TYPE_T ne10_fft_cfg_float32_t # define NE10_FFT_STATE_TYPE_T ne10_fft_state_float32_t # define NE10_FFT_DESTROY_C2C_TYPE ne10_fft_destroy_c2c_float32 # define NE10_FFT_CPX_TYPE_T ne10_fft_cpx_float32_t # define NE10_FFT_C2C_1D_TYPE_NEON ne10_fft_c2c_1d_float32_neon #else # define NE10_FFT_ALLOC_C2C_TYPE_NEON(nfft) ne10_fft_alloc_c2c_int32_neon(nfft) # define NE10_FFT_CFG_TYPE_T ne10_fft_cfg_int32_t # define NE10_FFT_STATE_TYPE_T ne10_fft_state_int32_t # define NE10_FFT_DESTROY_C2C_TYPE ne10_fft_destroy_c2c_int32 # define NE10_FFT_DESTROY_C2C_TYPE ne10_fft_destroy_c2c_int32 # define NE10_FFT_CPX_TYPE_T ne10_fft_cpx_int32_t # define NE10_FFT_C2C_1D_TYPE_NEON ne10_fft_c2c_1d_int32_neon #endif #if defined(CUSTOM_MODES) /* nfft lengths in NE10 that support scaled fft */ # define NE10_FFTSCALED_SUPPORT_MAX 4 static const int ne10_fft_scaled_support[NE10_FFTSCALED_SUPPORT_MAX] = { 480, 240, 120, 60 }; int opus_fft_alloc_arm_neon(kiss_fft_state *st) { int i; size_t memneeded = sizeof(struct arch_fft_state); st->arch_fft = (arch_fft_state *)opus_alloc(memneeded); if (!st->arch_fft) return -1; for (i = 0; i < NE10_FFTSCALED_SUPPORT_MAX; i++) { if(st->nfft == ne10_fft_scaled_support[i]) break; } if (i == NE10_FFTSCALED_SUPPORT_MAX) { /* This nfft length (scaled fft) is not supported in NE10 */ st->arch_fft->is_supported = 0; st->arch_fft->priv = NULL; } else { st->arch_fft->is_supported = 1; st->arch_fft->priv = (void *)NE10_FFT_ALLOC_C2C_TYPE_NEON(st->nfft); if (st->arch_fft->priv == NULL) { return -1; } } return 0; } void opus_fft_free_arm_neon(kiss_fft_state *st) { NE10_FFT_CFG_TYPE_T cfg; if (!st->arch_fft) return; cfg = (NE10_FFT_CFG_TYPE_T)st->arch_fft->priv; if (cfg) NE10_FFT_DESTROY_C2C_TYPE(cfg); opus_free(st->arch_fft); } #endif void opus_fft_neon(const kiss_fft_state *st, const kiss_fft_cpx *fin, kiss_fft_cpx *fout) { NE10_FFT_STATE_TYPE_T state; NE10_FFT_CFG_TYPE_T cfg = &state; VARDECL(NE10_FFT_CPX_TYPE_T, buffer); SAVE_STACK; ALLOC(buffer, st->nfft, NE10_FFT_CPX_TYPE_T); if (!st->arch_fft->is_supported) { /* This nfft length (scaled fft) not supported in NE10 */ opus_fft_c(st, fin, fout); } else { memcpy((void *)cfg, st->arch_fft->priv, sizeof(NE10_FFT_STATE_TYPE_T)); state.buffer = (NE10_FFT_CPX_TYPE_T *)&buffer[0]; #if !defined(FIXED_POINT) state.is_forward_scaled = 1; NE10_FFT_C2C_1D_TYPE_NEON((NE10_FFT_CPX_TYPE_T *)fout, (NE10_FFT_CPX_TYPE_T *)fin, cfg, 0); #else NE10_FFT_C2C_1D_TYPE_NEON((NE10_FFT_CPX_TYPE_T *)fout, (NE10_FFT_CPX_TYPE_T *)fin, cfg, 0, 1); #endif } RESTORE_STACK; } void opus_ifft_neon(const kiss_fft_state *st, const kiss_fft_cpx *fin, kiss_fft_cpx *fout) { NE10_FFT_STATE_TYPE_T state; NE10_FFT_CFG_TYPE_T cfg = &state; VARDECL(NE10_FFT_CPX_TYPE_T, buffer); SAVE_STACK; ALLOC(buffer, st->nfft, NE10_FFT_CPX_TYPE_T); if (!st->arch_fft->is_supported) { /* This nfft length (scaled fft) not supported in NE10 */ opus_ifft_c(st, fin, fout); } else { memcpy((void *)cfg, st->arch_fft->priv, sizeof(NE10_FFT_STATE_TYPE_T)); state.buffer = (NE10_FFT_CPX_TYPE_T *)&buffer[0]; #if !defined(FIXED_POINT) state.is_backward_scaled = 0; NE10_FFT_C2C_1D_TYPE_NEON((NE10_FFT_CPX_TYPE_T *)fout, (NE10_FFT_CPX_TYPE_T *)fin, cfg, 1); #else NE10_FFT_C2C_1D_TYPE_NEON((NE10_FFT_CPX_TYPE_T *)fout, (NE10_FFT_CPX_TYPE_T *)fin, cfg, 1, 0); #endif } RESTORE_STACK; } ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/celt_ne10_mdct.c ================================================ /* Copyright (c) 2015 Xiph.Org Foundation Written by Viswanath Puttagunta */ /** @file celt_ne10_mdct.c @brief ARM Neon optimizations for mdct using NE10 library */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SKIP_CONFIG_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #endif #include "kiss_fft.h" #include "_kiss_fft_guts.h" #include "mdct.h" #include "stack_alloc.h" void clt_mdct_forward_neon(const mdct_lookup *l, kiss_fft_scalar *in, kiss_fft_scalar * OPUS_RESTRICT out, const opus_val16 *window, int overlap, int shift, int stride, int arch) { int i; int N, N2, N4; VARDECL(kiss_fft_scalar, f); VARDECL(kiss_fft_cpx, f2); const kiss_fft_state *st = l->kfft[shift]; const kiss_twiddle_scalar *trig; SAVE_STACK; N = l->n; trig = l->trig; for (i=0;i>= 1; trig += N; } N2 = N>>1; N4 = N>>2; ALLOC(f, N2, kiss_fft_scalar); ALLOC(f2, N4, kiss_fft_cpx); /* Consider the input to be composed of four blocks: [a, b, c, d] */ /* Window, shuffle, fold */ { /* Temp pointers to make it really clear to the compiler what we're doing */ const kiss_fft_scalar * OPUS_RESTRICT xp1 = in+(overlap>>1); const kiss_fft_scalar * OPUS_RESTRICT xp2 = in+N2-1+(overlap>>1); kiss_fft_scalar * OPUS_RESTRICT yp = f; const opus_val16 * OPUS_RESTRICT wp1 = window+(overlap>>1); const opus_val16 * OPUS_RESTRICT wp2 = window+(overlap>>1)-1; for(i=0;i<((overlap+3)>>2);i++) { /* Real part arranged as -d-cR, Imag part arranged as -b+aR*/ *yp++ = MULT16_32_Q15(*wp2, xp1[N2]) + MULT16_32_Q15(*wp1,*xp2); *yp++ = MULT16_32_Q15(*wp1, *xp1) - MULT16_32_Q15(*wp2, xp2[-N2]); xp1+=2; xp2-=2; wp1+=2; wp2-=2; } wp1 = window; wp2 = window+overlap-1; for(;i>2);i++) { /* Real part arranged as a-bR, Imag part arranged as -c-dR */ *yp++ = *xp2; *yp++ = *xp1; xp1+=2; xp2-=2; } for(;ii,t[N4+i]) - S_MUL(fp->r,t[i]); yi = S_MUL(fp->r,t[N4+i]) + S_MUL(fp->i,t[i]); *yp1 = yr; *yp2 = yi; fp++; yp1 += 2*stride; yp2 -= 2*stride; } } RESTORE_STACK; } void clt_mdct_backward_neon(const mdct_lookup *l, kiss_fft_scalar *in, kiss_fft_scalar * OPUS_RESTRICT out, const opus_val16 * OPUS_RESTRICT window, int overlap, int shift, int stride, int arch) { int i; int N, N2, N4; VARDECL(kiss_fft_scalar, f); const kiss_twiddle_scalar *trig; const kiss_fft_state *st = l->kfft[shift]; N = l->n; trig = l->trig; for (i=0;i>= 1; trig += N; } N2 = N>>1; N4 = N>>2; ALLOC(f, N2, kiss_fft_scalar); /* Pre-rotate */ { /* Temp pointers to make it really clear to the compiler what we're doing */ const kiss_fft_scalar * OPUS_RESTRICT xp1 = in; const kiss_fft_scalar * OPUS_RESTRICT xp2 = in+stride*(N2-1); kiss_fft_scalar * OPUS_RESTRICT yp = f; const kiss_twiddle_scalar * OPUS_RESTRICT t = &trig[0]; for(i=0;i>1)), arch); /* Post-rotate and de-shuffle from both ends of the buffer at once to make it in-place. */ { kiss_fft_scalar * yp0 = out+(overlap>>1); kiss_fft_scalar * yp1 = out+(overlap>>1)+N2-2; const kiss_twiddle_scalar *t = &trig[0]; /* Loop to (N4+1)>>1 to handle odd N4. When N4 is odd, the middle pair will be computed twice. */ for(i=0;i<(N4+1)>>1;i++) { kiss_fft_scalar re, im, yr, yi; kiss_twiddle_scalar t0, t1; re = yp0[0]; im = yp0[1]; t0 = t[i]; t1 = t[N4+i]; /* We'd scale up by 2 here, but instead it's done when mixing the windows */ yr = S_MUL(re,t0) + S_MUL(im,t1); yi = S_MUL(re,t1) - S_MUL(im,t0); re = yp1[0]; im = yp1[1]; yp0[0] = yr; yp1[1] = yi; t0 = t[(N4-i-1)]; t1 = t[(N2-i-1)]; /* We'd scale up by 2 here, but instead it's done when mixing the windows */ yr = S_MUL(re,t0) + S_MUL(im,t1); yi = S_MUL(re,t1) - S_MUL(im,t0); yp1[0] = yr; yp0[1] = yi; yp0 += 2; yp1 -= 2; } } /* Mirror on both sides for TDAC */ { kiss_fft_scalar * OPUS_RESTRICT xp1 = out+overlap-1; kiss_fft_scalar * OPUS_RESTRICT yp1 = out; const opus_val16 * OPUS_RESTRICT wp1 = window; const opus_val16 * OPUS_RESTRICT wp2 = window+overlap-1; for(i = 0; i < overlap/2; i++) { kiss_fft_scalar x1, x2; x1 = *xp1; x2 = *yp1; *yp1++ = MULT16_32_Q15(*wp2, x2) - MULT16_32_Q15(*wp1, x1); *xp1-- = MULT16_32_Q15(*wp1, x2) + MULT16_32_Q15(*wp2, x1); wp1++; wp2--; } } RESTORE_STACK; } ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/celt_neon_intr.c ================================================ /* Copyright (c) 2014-2015 Xiph.Org Foundation Written by Viswanath Puttagunta */ /** @file celt_neon_intr.c @brief ARM Neon Intrinsic optimizations for celt */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "../pitch.h" #if !defined(FIXED_POINT) /* * Function: xcorr_kernel_neon_float * --------------------------------- * Computes 4 correlation values and stores them in sum[4] */ static void xcorr_kernel_neon_float(const float32_t *x, const float32_t *y, float32_t sum[4], int len) { float32x4_t YY[3]; float32x4_t YEXT[3]; float32x4_t XX[2]; float32x2_t XX_2; float32x4_t SUMM; const float32_t *xi = x; const float32_t *yi = y; celt_assert(len>0); YY[0] = vld1q_f32(yi); SUMM = vdupq_n_f32(0); /* Consume 8 elements in x vector and 12 elements in y * vector. However, the 12'th element never really gets * touched in this loop. So, if len == 8, then we only * must access y[0] to y[10]. y[11] must not be accessed * hence make sure len > 8 and not len >= 8 */ while (len > 8) { yi += 4; YY[1] = vld1q_f32(yi); yi += 4; YY[2] = vld1q_f32(yi); XX[0] = vld1q_f32(xi); xi += 4; XX[1] = vld1q_f32(xi); xi += 4; SUMM = vmlaq_lane_f32(SUMM, YY[0], vget_low_f32(XX[0]), 0); YEXT[0] = vextq_f32(YY[0], YY[1], 1); SUMM = vmlaq_lane_f32(SUMM, YEXT[0], vget_low_f32(XX[0]), 1); YEXT[1] = vextq_f32(YY[0], YY[1], 2); SUMM = vmlaq_lane_f32(SUMM, YEXT[1], vget_high_f32(XX[0]), 0); YEXT[2] = vextq_f32(YY[0], YY[1], 3); SUMM = vmlaq_lane_f32(SUMM, YEXT[2], vget_high_f32(XX[0]), 1); SUMM = vmlaq_lane_f32(SUMM, YY[1], vget_low_f32(XX[1]), 0); YEXT[0] = vextq_f32(YY[1], YY[2], 1); SUMM = vmlaq_lane_f32(SUMM, YEXT[0], vget_low_f32(XX[1]), 1); YEXT[1] = vextq_f32(YY[1], YY[2], 2); SUMM = vmlaq_lane_f32(SUMM, YEXT[1], vget_high_f32(XX[1]), 0); YEXT[2] = vextq_f32(YY[1], YY[2], 3); SUMM = vmlaq_lane_f32(SUMM, YEXT[2], vget_high_f32(XX[1]), 1); YY[0] = YY[2]; len -= 8; } /* Consume 4 elements in x vector and 8 elements in y * vector. However, the 8'th element in y never really gets * touched in this loop. So, if len == 4, then we only * must access y[0] to y[6]. y[7] must not be accessed * hence make sure len>4 and not len>=4 */ if (len > 4) { yi += 4; YY[1] = vld1q_f32(yi); XX[0] = vld1q_f32(xi); xi += 4; SUMM = vmlaq_lane_f32(SUMM, YY[0], vget_low_f32(XX[0]), 0); YEXT[0] = vextq_f32(YY[0], YY[1], 1); SUMM = vmlaq_lane_f32(SUMM, YEXT[0], vget_low_f32(XX[0]), 1); YEXT[1] = vextq_f32(YY[0], YY[1], 2); SUMM = vmlaq_lane_f32(SUMM, YEXT[1], vget_high_f32(XX[0]), 0); YEXT[2] = vextq_f32(YY[0], YY[1], 3); SUMM = vmlaq_lane_f32(SUMM, YEXT[2], vget_high_f32(XX[0]), 1); YY[0] = YY[1]; len -= 4; } while (--len > 0) { XX_2 = vld1_dup_f32(xi++); SUMM = vmlaq_lane_f32(SUMM, YY[0], XX_2, 0); YY[0]= vld1q_f32(++yi); } XX_2 = vld1_dup_f32(xi); SUMM = vmlaq_lane_f32(SUMM, YY[0], XX_2, 0); vst1q_f32(sum, SUMM); } /* * Function: xcorr_kernel_neon_float_process1 * --------------------------------- * Computes single correlation values and stores in *sum */ static void xcorr_kernel_neon_float_process1(const float32_t *x, const float32_t *y, float32_t *sum, int len) { float32x4_t XX[4]; float32x4_t YY[4]; float32x2_t XX_2; float32x2_t YY_2; float32x4_t SUMM; float32x2_t SUMM_2[2]; const float32_t *xi = x; const float32_t *yi = y; SUMM = vdupq_n_f32(0); /* Work on 16 values per iteration */ while (len >= 16) { XX[0] = vld1q_f32(xi); xi += 4; XX[1] = vld1q_f32(xi); xi += 4; XX[2] = vld1q_f32(xi); xi += 4; XX[3] = vld1q_f32(xi); xi += 4; YY[0] = vld1q_f32(yi); yi += 4; YY[1] = vld1q_f32(yi); yi += 4; YY[2] = vld1q_f32(yi); yi += 4; YY[3] = vld1q_f32(yi); yi += 4; SUMM = vmlaq_f32(SUMM, YY[0], XX[0]); SUMM = vmlaq_f32(SUMM, YY[1], XX[1]); SUMM = vmlaq_f32(SUMM, YY[2], XX[2]); SUMM = vmlaq_f32(SUMM, YY[3], XX[3]); len -= 16; } /* Work on 8 values */ if (len >= 8) { XX[0] = vld1q_f32(xi); xi += 4; XX[1] = vld1q_f32(xi); xi += 4; YY[0] = vld1q_f32(yi); yi += 4; YY[1] = vld1q_f32(yi); yi += 4; SUMM = vmlaq_f32(SUMM, YY[0], XX[0]); SUMM = vmlaq_f32(SUMM, YY[1], XX[1]); len -= 8; } /* Work on 4 values */ if (len >= 4) { XX[0] = vld1q_f32(xi); xi += 4; YY[0] = vld1q_f32(yi); yi += 4; SUMM = vmlaq_f32(SUMM, YY[0], XX[0]); len -= 4; } /* Start accumulating results */ SUMM_2[0] = vget_low_f32(SUMM); if (len >= 2) { /* While at it, consume 2 more values if available */ XX_2 = vld1_f32(xi); xi += 2; YY_2 = vld1_f32(yi); yi += 2; SUMM_2[0] = vmla_f32(SUMM_2[0], YY_2, XX_2); len -= 2; } SUMM_2[1] = vget_high_f32(SUMM); SUMM_2[0] = vadd_f32(SUMM_2[0], SUMM_2[1]); SUMM_2[0] = vpadd_f32(SUMM_2[0], SUMM_2[0]); /* Ok, now we have result accumulated in SUMM_2[0].0 */ if (len > 0) { /* Case when you have one value left */ XX_2 = vld1_dup_f32(xi); YY_2 = vld1_dup_f32(yi); SUMM_2[0] = vmla_f32(SUMM_2[0], XX_2, YY_2); } vst1_lane_f32(sum, SUMM_2[0], 0); } void celt_pitch_xcorr_float_neon(const opus_val16 *_x, const opus_val16 *_y, opus_val32 *xcorr, int len, int max_pitch) { int i; celt_assert(max_pitch > 0); celt_assert((((unsigned char *)_x-(unsigned char *)NULL)&3)==0); for (i = 0; i < (max_pitch-3); i += 4) { xcorr_kernel_neon_float((const float32_t *)_x, (const float32_t *)_y+i, (float32_t *)xcorr+i, len); } /* In case max_pitch isn't multiple of 4 * compute single correlation value per iteration */ for (; i < max_pitch; i++) { xcorr_kernel_neon_float_process1((const float32_t *)_x, (const float32_t *)_y+i, (float32_t *)xcorr+i, len); } } #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/celt_pitch_xcorr_arm-gnu.S ================================================ .syntax unified @ Copyright (c) 2007-2008 CSIRO @ Copyright (c) 2007-2009 Xiph.Org Foundation @ Copyright (c) 2013 Parrot @ Written by Aurélien Zanelli @ @ Redistribution and use in source and binary forms, with or without @ modification, are permitted provided that the following conditions @ are met: @ @ - Redistributions of source code must retain the above copyright @ notice, this list of conditions and the following disclaimer. @ @ - Redistributions in binary form must reproduce the above copyright @ notice, this list of conditions and the following disclaimer in the @ documentation and/or other materials provided with the distribution. @ @ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER @ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, @ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, @ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF @ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING @ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS @ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .text; .p2align 2; .arch armv7-a .fpu neon .object_arch armv4t .include "celt/arm/armopts-gnu.S" .if OPUS_ARM_MAY_HAVE_EDSP .global celt_pitch_xcorr_edsp .endif .if OPUS_ARM_MAY_HAVE_NEON .global celt_pitch_xcorr_neon .endif .if OPUS_ARM_MAY_HAVE_NEON @ Compute sum[k]=sum(x[j]*y[j+k],j=0...len-1), k=0...3 ; xcorr_kernel_neon: @ PROC xcorr_kernel_neon_start: @ input: @ r3 = int len @ r4 = opus_val16 *x @ r5 = opus_val16 *y @ q0 = opus_val32 sum[4] @ output: @ q0 = opus_val32 sum[4] @ preserved: r0-r3, r6-r11, d2, q4-q7, q9-q15 @ internal usage: @ r12 = int j @ d3 = y_3|y_2|y_1|y_0 @ q2 = y_B|y_A|y_9|y_8|y_7|y_6|y_5|y_4 @ q3 = x_7|x_6|x_5|x_4|x_3|x_2|x_1|x_0 @ q8 = scratch @ @ Load y[0...3] @ This requires len>0 to always be valid (which we assert in the C code). VLD1.16 {d5}, [r5]! SUBS r12, r3, #8 BLE xcorr_kernel_neon_process4 @ Process 8 samples at a time. @ This loop loads one y value more than we actually need. Therefore we have to @ stop as soon as there are 8 or fewer samples left (instead of 7), to avoid @ reading past the end of the array. xcorr_kernel_neon_process8: @ This loop has 19 total instructions (10 cycles to issue, minimum), with @ - 2 cycles of ARM insrtuctions, @ - 10 cycles of load/store/byte permute instructions, and @ - 9 cycles of data processing instructions. @ On a Cortex A8, we dual-issue the maximum amount (9 cycles) between the @ latter two categories, meaning the whole loop should run in 10 cycles per @ iteration, barring cache misses. @ @ Load x[0...7] VLD1.16 {d6, d7}, [r4]! @ Unlike VMOV, VAND is a data processsing instruction (and doesn't get @ assembled to VMOV, like VORR would), so it dual-issues with the prior VLD1. VAND d3, d5, d5 SUBS r12, r12, #8 @ Load y[4...11] VLD1.16 {d4, d5}, [r5]! VMLAL.S16 q0, d3, d6[0] VEXT.16 d16, d3, d4, #1 VMLAL.S16 q0, d4, d7[0] VEXT.16 d17, d4, d5, #1 VMLAL.S16 q0, d16, d6[1] VEXT.16 d16, d3, d4, #2 VMLAL.S16 q0, d17, d7[1] VEXT.16 d17, d4, d5, #2 VMLAL.S16 q0, d16, d6[2] VEXT.16 d16, d3, d4, #3 VMLAL.S16 q0, d17, d7[2] VEXT.16 d17, d4, d5, #3 VMLAL.S16 q0, d16, d6[3] VMLAL.S16 q0, d17, d7[3] BGT xcorr_kernel_neon_process8 @ Process 4 samples here if we have > 4 left (still reading one extra y value). xcorr_kernel_neon_process4: ADDS r12, r12, #4 BLE xcorr_kernel_neon_process2 @ Load x[0...3] VLD1.16 d6, [r4]! @ Use VAND since it's a data processing instruction again. VAND d4, d5, d5 SUB r12, r12, #4 @ Load y[4...7] VLD1.16 d5, [r5]! VMLAL.S16 q0, d4, d6[0] VEXT.16 d16, d4, d5, #1 VMLAL.S16 q0, d16, d6[1] VEXT.16 d16, d4, d5, #2 VMLAL.S16 q0, d16, d6[2] VEXT.16 d16, d4, d5, #3 VMLAL.S16 q0, d16, d6[3] @ Process 2 samples here if we have > 2 left (still reading one extra y value). xcorr_kernel_neon_process2: ADDS r12, r12, #2 BLE xcorr_kernel_neon_process1 @ Load x[0...1] VLD2.16 {d6[],d7[]}, [r4]! @ Use VAND since it's a data processing instruction again. VAND d4, d5, d5 SUB r12, r12, #2 @ Load y[4...5] VLD1.32 {d5[]}, [r5]! VMLAL.S16 q0, d4, d6 VEXT.16 d16, d4, d5, #1 @ Replace bottom copy of {y5,y4} in d5 with {y3,y2} from d4, using VSRI @ instead of VEXT, since it's a data-processing instruction. VSRI.64 d5, d4, #32 VMLAL.S16 q0, d16, d7 @ Process 1 sample using the extra y value we loaded above. xcorr_kernel_neon_process1: @ Load next *x VLD1.16 {d6[]}, [r4]! ADDS r12, r12, #1 @ y[0...3] are left in d5 from prior iteration(s) (if any) VMLAL.S16 q0, d5, d6 MOVLE pc, lr @ Now process 1 last sample, not reading ahead. @ Load last *y VLD1.16 {d4[]}, [r5]! VSRI.64 d4, d5, #16 @ Load last *x VLD1.16 {d6[]}, [r4]! VMLAL.S16 q0, d4, d6 MOV pc, lr .size xcorr_kernel_neon, .-xcorr_kernel_neon @ ENDP @ opus_val32 celt_pitch_xcorr_neon(opus_val16 *_x, opus_val16 *_y, @ opus_val32 *xcorr, int len, int max_pitch) ; celt_pitch_xcorr_neon: @ PROC @ input: @ r0 = opus_val16 *_x @ r1 = opus_val16 *_y @ r2 = opus_val32 *xcorr @ r3 = int len @ output: @ r0 = int maxcorr @ internal usage: @ r4 = opus_val16 *x (for xcorr_kernel_neon()) @ r5 = opus_val16 *y (for xcorr_kernel_neon()) @ r6 = int max_pitch @ r12 = int j @ q15 = int maxcorr[4] (q15 is not used by xcorr_kernel_neon()) STMFD sp!, {r4-r6, lr} LDR r6, [sp, #16] VMOV.S32 q15, #1 @ if (max_pitch < 4) goto celt_pitch_xcorr_neon_process4_done SUBS r6, r6, #4 BLT celt_pitch_xcorr_neon_process4_done celt_pitch_xcorr_neon_process4: @ xcorr_kernel_neon parameters: @ r3 = len, r4 = _x, r5 = _y, q0 = {0, 0, 0, 0} MOV r4, r0 MOV r5, r1 VEOR q0, q0, q0 @ xcorr_kernel_neon only modifies r4, r5, r12, and q0...q3. @ So we don't save/restore any other registers. BL xcorr_kernel_neon_start SUBS r6, r6, #4 VST1.32 {q0}, [r2]! @ _y += 4 ADD r1, r1, #8 VMAX.S32 q15, q15, q0 @ if (max_pitch < 4) goto celt_pitch_xcorr_neon_process4_done BGE celt_pitch_xcorr_neon_process4 @ We have less than 4 sums left to compute. celt_pitch_xcorr_neon_process4_done: ADDS r6, r6, #4 @ Reduce maxcorr to a single value VMAX.S32 d30, d30, d31 VPMAX.S32 d30, d30, d30 @ if (max_pitch <= 0) goto celt_pitch_xcorr_neon_done BLE celt_pitch_xcorr_neon_done @ Now compute each remaining sum one at a time. celt_pitch_xcorr_neon_process_remaining: MOV r4, r0 MOV r5, r1 VMOV.I32 q0, #0 SUBS r12, r3, #8 BLT celt_pitch_xcorr_neon_process_remaining4 @ Sum terms 8 at a time. celt_pitch_xcorr_neon_process_remaining_loop8: @ Load x[0...7] VLD1.16 {q1}, [r4]! @ Load y[0...7] VLD1.16 {q2}, [r5]! SUBS r12, r12, #8 VMLAL.S16 q0, d4, d2 VMLAL.S16 q0, d5, d3 BGE celt_pitch_xcorr_neon_process_remaining_loop8 @ Sum terms 4 at a time. celt_pitch_xcorr_neon_process_remaining4: ADDS r12, r12, #4 BLT celt_pitch_xcorr_neon_process_remaining4_done @ Load x[0...3] VLD1.16 {d2}, [r4]! @ Load y[0...3] VLD1.16 {d3}, [r5]! SUB r12, r12, #4 VMLAL.S16 q0, d3, d2 celt_pitch_xcorr_neon_process_remaining4_done: @ Reduce the sum to a single value. VADD.S32 d0, d0, d1 VPADDL.S32 d0, d0 ADDS r12, r12, #4 BLE celt_pitch_xcorr_neon_process_remaining_loop_done @ Sum terms 1 at a time. celt_pitch_xcorr_neon_process_remaining_loop1: VLD1.16 {d2[]}, [r4]! VLD1.16 {d3[]}, [r5]! SUBS r12, r12, #1 VMLAL.S16 q0, d2, d3 BGT celt_pitch_xcorr_neon_process_remaining_loop1 celt_pitch_xcorr_neon_process_remaining_loop_done: VST1.32 {d0[0]}, [r2]! VMAX.S32 d30, d30, d0 SUBS r6, r6, #1 @ _y++ ADD r1, r1, #2 @ if (--max_pitch > 0) goto celt_pitch_xcorr_neon_process_remaining BGT celt_pitch_xcorr_neon_process_remaining celt_pitch_xcorr_neon_done: VMOV.32 r0, d30[0] LDMFD sp!, {r4-r6, pc} .size celt_pitch_xcorr_neon, .-celt_pitch_xcorr_neon @ ENDP .endif .if OPUS_ARM_MAY_HAVE_EDSP @ This will get used on ARMv7 devices without NEON, so it has been optimized @ to take advantage of dual-issuing where possible. ; xcorr_kernel_edsp: @ PROC xcorr_kernel_edsp_start: @ input: @ r3 = int len @ r4 = opus_val16 *_x (must be 32-bit aligned) @ r5 = opus_val16 *_y (must be 32-bit aligned) @ r6...r9 = opus_val32 sum[4] @ output: @ r6...r9 = opus_val32 sum[4] @ preserved: r0-r5 @ internal usage @ r2 = int j @ r12,r14 = opus_val16 x[4] @ r10,r11 = opus_val16 y[4] STMFD sp!, {r2,r4,r5,lr} LDR r10, [r5], #4 @ Load y[0...1] SUBS r2, r3, #4 @ j = len-4 LDR r11, [r5], #4 @ Load y[2...3] BLE xcorr_kernel_edsp_process4_done LDR r12, [r4], #4 @ Load x[0...1] @ Stall xcorr_kernel_edsp_process4: @ The multiplies must issue from pipeline 0, and can't dual-issue with each @ other. Every other instruction here dual-issues with a multiply, and is @ thus "free". There should be no stalls in the body of the loop. SMLABB r6, r12, r10, r6 @ sum[0] = MAC16_16(sum[0],x_0,y_0) LDR r14, [r4], #4 @ Load x[2...3] SMLABT r7, r12, r10, r7 @ sum[1] = MAC16_16(sum[1],x_0,y_1) SUBS r2, r2, #4 @ j-=4 SMLABB r8, r12, r11, r8 @ sum[2] = MAC16_16(sum[2],x_0,y_2) SMLABT r9, r12, r11, r9 @ sum[3] = MAC16_16(sum[3],x_0,y_3) SMLATT r6, r12, r10, r6 @ sum[0] = MAC16_16(sum[0],x_1,y_1) LDR r10, [r5], #4 @ Load y[4...5] SMLATB r7, r12, r11, r7 @ sum[1] = MAC16_16(sum[1],x_1,y_2) SMLATT r8, r12, r11, r8 @ sum[2] = MAC16_16(sum[2],x_1,y_3) SMLATB r9, r12, r10, r9 @ sum[3] = MAC16_16(sum[3],x_1,y_4) LDRGT r12, [r4], #4 @ Load x[0...1] SMLABB r6, r14, r11, r6 @ sum[0] = MAC16_16(sum[0],x_2,y_2) SMLABT r7, r14, r11, r7 @ sum[1] = MAC16_16(sum[1],x_2,y_3) SMLABB r8, r14, r10, r8 @ sum[2] = MAC16_16(sum[2],x_2,y_4) SMLABT r9, r14, r10, r9 @ sum[3] = MAC16_16(sum[3],x_2,y_5) SMLATT r6, r14, r11, r6 @ sum[0] = MAC16_16(sum[0],x_3,y_3) LDR r11, [r5], #4 @ Load y[6...7] SMLATB r7, r14, r10, r7 @ sum[1] = MAC16_16(sum[1],x_3,y_4) SMLATT r8, r14, r10, r8 @ sum[2] = MAC16_16(sum[2],x_3,y_5) SMLATB r9, r14, r11, r9 @ sum[3] = MAC16_16(sum[3],x_3,y_6) BGT xcorr_kernel_edsp_process4 xcorr_kernel_edsp_process4_done: ADDS r2, r2, #4 BLE xcorr_kernel_edsp_done LDRH r12, [r4], #2 @ r12 = *x++ SUBS r2, r2, #1 @ j-- @ Stall SMLABB r6, r12, r10, r6 @ sum[0] = MAC16_16(sum[0],x,y_0) LDRHGT r14, [r4], #2 @ r14 = *x++ SMLABT r7, r12, r10, r7 @ sum[1] = MAC16_16(sum[1],x,y_1) SMLABB r8, r12, r11, r8 @ sum[2] = MAC16_16(sum[2],x,y_2) SMLABT r9, r12, r11, r9 @ sum[3] = MAC16_16(sum[3],x,y_3) BLE xcorr_kernel_edsp_done SMLABT r6, r14, r10, r6 @ sum[0] = MAC16_16(sum[0],x,y_1) SUBS r2, r2, #1 @ j-- SMLABB r7, r14, r11, r7 @ sum[1] = MAC16_16(sum[1],x,y_2) LDRH r10, [r5], #2 @ r10 = y_4 = *y++ SMLABT r8, r14, r11, r8 @ sum[2] = MAC16_16(sum[2],x,y_3) LDRHGT r12, [r4], #2 @ r12 = *x++ SMLABB r9, r14, r10, r9 @ sum[3] = MAC16_16(sum[3],x,y_4) BLE xcorr_kernel_edsp_done SMLABB r6, r12, r11, r6 @ sum[0] = MAC16_16(sum[0],tmp,y_2) CMP r2, #1 @ j-- SMLABT r7, r12, r11, r7 @ sum[1] = MAC16_16(sum[1],tmp,y_3) LDRH r2, [r5], #2 @ r2 = y_5 = *y++ SMLABB r8, r12, r10, r8 @ sum[2] = MAC16_16(sum[2],tmp,y_4) LDRHGT r14, [r4] @ r14 = *x SMLABB r9, r12, r2, r9 @ sum[3] = MAC16_16(sum[3],tmp,y_5) BLE xcorr_kernel_edsp_done SMLABT r6, r14, r11, r6 @ sum[0] = MAC16_16(sum[0],tmp,y_3) LDRH r11, [r5] @ r11 = y_6 = *y SMLABB r7, r14, r10, r7 @ sum[1] = MAC16_16(sum[1],tmp,y_4) SMLABB r8, r14, r2, r8 @ sum[2] = MAC16_16(sum[2],tmp,y_5) SMLABB r9, r14, r11, r9 @ sum[3] = MAC16_16(sum[3],tmp,y_6) xcorr_kernel_edsp_done: LDMFD sp!, {r2,r4,r5,pc} .size xcorr_kernel_edsp, .-xcorr_kernel_edsp @ ENDP ; celt_pitch_xcorr_edsp: @ PROC @ input: @ r0 = opus_val16 *_x (must be 32-bit aligned) @ r1 = opus_val16 *_y (only needs to be 16-bit aligned) @ r2 = opus_val32 *xcorr @ r3 = int len @ output: @ r0 = maxcorr @ internal usage @ r4 = opus_val16 *x @ r5 = opus_val16 *y @ r6 = opus_val32 sum0 @ r7 = opus_val32 sum1 @ r8 = opus_val32 sum2 @ r9 = opus_val32 sum3 @ r1 = int max_pitch @ r12 = int j STMFD sp!, {r4-r11, lr} MOV r5, r1 LDR r1, [sp, #36] MOV r4, r0 TST r5, #3 @ maxcorr = 1 MOV r0, #1 BEQ celt_pitch_xcorr_edsp_process1u_done @ Compute one sum at the start to make y 32-bit aligned. SUBS r12, r3, #4 @ r14 = sum = 0 MOV r14, #0 LDRH r8, [r5], #2 BLE celt_pitch_xcorr_edsp_process1u_loop4_done LDR r6, [r4], #4 MOV r8, r8, LSL #16 celt_pitch_xcorr_edsp_process1u_loop4: LDR r9, [r5], #4 SMLABT r14, r6, r8, r14 @ sum = MAC16_16(sum, x_0, y_0) LDR r7, [r4], #4 SMLATB r14, r6, r9, r14 @ sum = MAC16_16(sum, x_1, y_1) LDR r8, [r5], #4 SMLABT r14, r7, r9, r14 @ sum = MAC16_16(sum, x_2, y_2) SUBS r12, r12, #4 @ j-=4 SMLATB r14, r7, r8, r14 @ sum = MAC16_16(sum, x_3, y_3) LDRGT r6, [r4], #4 BGT celt_pitch_xcorr_edsp_process1u_loop4 MOV r8, r8, LSR #16 celt_pitch_xcorr_edsp_process1u_loop4_done: ADDS r12, r12, #4 celt_pitch_xcorr_edsp_process1u_loop1: LDRHGE r6, [r4], #2 @ Stall SMLABBGE r14, r6, r8, r14 @ sum = MAC16_16(sum, *x, *y) SUBSGE r12, r12, #1 LDRHGT r8, [r5], #2 BGT celt_pitch_xcorr_edsp_process1u_loop1 @ Restore _x SUB r4, r4, r3, LSL #1 @ Restore and advance _y SUB r5, r5, r3, LSL #1 @ maxcorr = max(maxcorr, sum) CMP r0, r14 ADD r5, r5, #2 MOVLT r0, r14 SUBS r1, r1, #1 @ xcorr[i] = sum STR r14, [r2], #4 BLE celt_pitch_xcorr_edsp_done celt_pitch_xcorr_edsp_process1u_done: @ if (max_pitch < 4) goto celt_pitch_xcorr_edsp_process2 SUBS r1, r1, #4 BLT celt_pitch_xcorr_edsp_process2 celt_pitch_xcorr_edsp_process4: @ xcorr_kernel_edsp parameters: @ r3 = len, r4 = _x, r5 = _y, r6...r9 = sum[4] = {0, 0, 0, 0} MOV r6, #0 MOV r7, #0 MOV r8, #0 MOV r9, #0 BL xcorr_kernel_edsp_start @ xcorr_kernel_edsp(_x, _y+i, xcorr+i, len) @ maxcorr = max(maxcorr, sum0, sum1, sum2, sum3) CMP r0, r6 @ _y+=4 ADD r5, r5, #8 MOVLT r0, r6 CMP r0, r7 MOVLT r0, r7 CMP r0, r8 MOVLT r0, r8 CMP r0, r9 MOVLT r0, r9 STMIA r2!, {r6-r9} SUBS r1, r1, #4 BGE celt_pitch_xcorr_edsp_process4 celt_pitch_xcorr_edsp_process2: ADDS r1, r1, #2 BLT celt_pitch_xcorr_edsp_process1a SUBS r12, r3, #4 @ {r10, r11} = {sum0, sum1} = {0, 0} MOV r10, #0 MOV r11, #0 LDR r8, [r5], #4 BLE celt_pitch_xcorr_edsp_process2_loop_done LDR r6, [r4], #4 LDR r9, [r5], #4 celt_pitch_xcorr_edsp_process2_loop4: SMLABB r10, r6, r8, r10 @ sum0 = MAC16_16(sum0, x_0, y_0) LDR r7, [r4], #4 SMLABT r11, r6, r8, r11 @ sum1 = MAC16_16(sum1, x_0, y_1) SUBS r12, r12, #4 @ j-=4 SMLATT r10, r6, r8, r10 @ sum0 = MAC16_16(sum0, x_1, y_1) LDR r8, [r5], #4 SMLATB r11, r6, r9, r11 @ sum1 = MAC16_16(sum1, x_1, y_2) LDRGT r6, [r4], #4 SMLABB r10, r7, r9, r10 @ sum0 = MAC16_16(sum0, x_2, y_2) SMLABT r11, r7, r9, r11 @ sum1 = MAC16_16(sum1, x_2, y_3) SMLATT r10, r7, r9, r10 @ sum0 = MAC16_16(sum0, x_3, y_3) LDRGT r9, [r5], #4 SMLATB r11, r7, r8, r11 @ sum1 = MAC16_16(sum1, x_3, y_4) BGT celt_pitch_xcorr_edsp_process2_loop4 celt_pitch_xcorr_edsp_process2_loop_done: ADDS r12, r12, #2 BLE celt_pitch_xcorr_edsp_process2_1 LDR r6, [r4], #4 @ Stall SMLABB r10, r6, r8, r10 @ sum0 = MAC16_16(sum0, x_0, y_0) LDR r9, [r5], #4 SMLABT r11, r6, r8, r11 @ sum1 = MAC16_16(sum1, x_0, y_1) SUB r12, r12, #2 SMLATT r10, r6, r8, r10 @ sum0 = MAC16_16(sum0, x_1, y_1) MOV r8, r9 SMLATB r11, r6, r9, r11 @ sum1 = MAC16_16(sum1, x_1, y_2) celt_pitch_xcorr_edsp_process2_1: LDRH r6, [r4], #2 ADDS r12, r12, #1 @ Stall SMLABB r10, r6, r8, r10 @ sum0 = MAC16_16(sum0, x_0, y_0) LDRHGT r7, [r4], #2 SMLABT r11, r6, r8, r11 @ sum1 = MAC16_16(sum1, x_0, y_1) BLE celt_pitch_xcorr_edsp_process2_done LDRH r9, [r5], #2 SMLABT r10, r7, r8, r10 @ sum0 = MAC16_16(sum0, x_0, y_1) SMLABB r11, r7, r9, r11 @ sum1 = MAC16_16(sum1, x_0, y_2) celt_pitch_xcorr_edsp_process2_done: @ Restore _x SUB r4, r4, r3, LSL #1 @ Restore and advance _y SUB r5, r5, r3, LSL #1 @ maxcorr = max(maxcorr, sum0) CMP r0, r10 ADD r5, r5, #2 MOVLT r0, r10 SUB r1, r1, #2 @ maxcorr = max(maxcorr, sum1) CMP r0, r11 @ xcorr[i] = sum STR r10, [r2], #4 MOVLT r0, r11 STR r11, [r2], #4 celt_pitch_xcorr_edsp_process1a: ADDS r1, r1, #1 BLT celt_pitch_xcorr_edsp_done SUBS r12, r3, #4 @ r14 = sum = 0 MOV r14, #0 BLT celt_pitch_xcorr_edsp_process1a_loop_done LDR r6, [r4], #4 LDR r8, [r5], #4 LDR r7, [r4], #4 LDR r9, [r5], #4 celt_pitch_xcorr_edsp_process1a_loop4: SMLABB r14, r6, r8, r14 @ sum = MAC16_16(sum, x_0, y_0) SUBS r12, r12, #4 @ j-=4 SMLATT r14, r6, r8, r14 @ sum = MAC16_16(sum, x_1, y_1) LDRGE r6, [r4], #4 SMLABB r14, r7, r9, r14 @ sum = MAC16_16(sum, x_2, y_2) LDRGE r8, [r5], #4 SMLATT r14, r7, r9, r14 @ sum = MAC16_16(sum, x_3, y_3) LDRGE r7, [r4], #4 LDRGE r9, [r5], #4 BGE celt_pitch_xcorr_edsp_process1a_loop4 celt_pitch_xcorr_edsp_process1a_loop_done: ADDS r12, r12, #2 LDRGE r6, [r4], #4 LDRGE r8, [r5], #4 @ Stall SMLABBGE r14, r6, r8, r14 @ sum = MAC16_16(sum, x_0, y_0) SUBGE r12, r12, #2 SMLATTGE r14, r6, r8, r14 @ sum = MAC16_16(sum, x_1, y_1) ADDS r12, r12, #1 LDRHGE r6, [r4], #2 LDRHGE r8, [r5], #2 @ Stall SMLABBGE r14, r6, r8, r14 @ sum = MAC16_16(sum, *x, *y) @ maxcorr = max(maxcorr, sum) CMP r0, r14 @ xcorr[i] = sum STR r14, [r2], #4 MOVLT r0, r14 celt_pitch_xcorr_edsp_done: LDMFD sp!, {r4-r11, pc} .size celt_pitch_xcorr_edsp, .-celt_pitch_xcorr_edsp @ ENDP .endif @ END: .section .note.GNU-stack,"",%progbits ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/celt_pitch_xcorr_arm.s ================================================ ; Copyright (c) 2007-2008 CSIRO ; Copyright (c) 2007-2009 Xiph.Org Foundation ; Copyright (c) 2013 Parrot ; Written by Aurélien Zanelli ; ; Redistribution and use in source and binary forms, with or without ; modification, are permitted provided that the following conditions ; are met: ; ; - Redistributions of source code must retain the above copyright ; notice, this list of conditions and the following disclaimer. ; ; - Redistributions in binary form must reproduce the above copyright ; notice, this list of conditions and the following disclaimer in the ; documentation and/or other materials provided with the distribution. ; ; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ; ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER ; OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. AREA |.text|, CODE, READONLY GET celt/arm/armopts.s IF OPUS_ARM_MAY_HAVE_EDSP EXPORT celt_pitch_xcorr_edsp ENDIF IF OPUS_ARM_MAY_HAVE_NEON EXPORT celt_pitch_xcorr_neon ENDIF IF OPUS_ARM_MAY_HAVE_NEON ; Compute sum[k]=sum(x[j]*y[j+k],j=0...len-1), k=0...3 xcorr_kernel_neon PROC xcorr_kernel_neon_start ; input: ; r3 = int len ; r4 = opus_val16 *x ; r5 = opus_val16 *y ; q0 = opus_val32 sum[4] ; output: ; q0 = opus_val32 sum[4] ; preserved: r0-r3, r6-r11, d2, q4-q7, q9-q15 ; internal usage: ; r12 = int j ; d3 = y_3|y_2|y_1|y_0 ; q2 = y_B|y_A|y_9|y_8|y_7|y_6|y_5|y_4 ; q3 = x_7|x_6|x_5|x_4|x_3|x_2|x_1|x_0 ; q8 = scratch ; ; Load y[0...3] ; This requires len>0 to always be valid (which we assert in the C code). VLD1.16 {d5}, [r5]! SUBS r12, r3, #8 BLE xcorr_kernel_neon_process4 ; Process 8 samples at a time. ; This loop loads one y value more than we actually need. Therefore we have to ; stop as soon as there are 8 or fewer samples left (instead of 7), to avoid ; reading past the end of the array. xcorr_kernel_neon_process8 ; This loop has 19 total instructions (10 cycles to issue, minimum), with ; - 2 cycles of ARM insrtuctions, ; - 10 cycles of load/store/byte permute instructions, and ; - 9 cycles of data processing instructions. ; On a Cortex A8, we dual-issue the maximum amount (9 cycles) between the ; latter two categories, meaning the whole loop should run in 10 cycles per ; iteration, barring cache misses. ; ; Load x[0...7] VLD1.16 {d6, d7}, [r4]! ; Unlike VMOV, VAND is a data processsing instruction (and doesn't get ; assembled to VMOV, like VORR would), so it dual-issues with the prior VLD1. VAND d3, d5, d5 SUBS r12, r12, #8 ; Load y[4...11] VLD1.16 {d4, d5}, [r5]! VMLAL.S16 q0, d3, d6[0] VEXT.16 d16, d3, d4, #1 VMLAL.S16 q0, d4, d7[0] VEXT.16 d17, d4, d5, #1 VMLAL.S16 q0, d16, d6[1] VEXT.16 d16, d3, d4, #2 VMLAL.S16 q0, d17, d7[1] VEXT.16 d17, d4, d5, #2 VMLAL.S16 q0, d16, d6[2] VEXT.16 d16, d3, d4, #3 VMLAL.S16 q0, d17, d7[2] VEXT.16 d17, d4, d5, #3 VMLAL.S16 q0, d16, d6[3] VMLAL.S16 q0, d17, d7[3] BGT xcorr_kernel_neon_process8 ; Process 4 samples here if we have > 4 left (still reading one extra y value). xcorr_kernel_neon_process4 ADDS r12, r12, #4 BLE xcorr_kernel_neon_process2 ; Load x[0...3] VLD1.16 d6, [r4]! ; Use VAND since it's a data processing instruction again. VAND d4, d5, d5 SUB r12, r12, #4 ; Load y[4...7] VLD1.16 d5, [r5]! VMLAL.S16 q0, d4, d6[0] VEXT.16 d16, d4, d5, #1 VMLAL.S16 q0, d16, d6[1] VEXT.16 d16, d4, d5, #2 VMLAL.S16 q0, d16, d6[2] VEXT.16 d16, d4, d5, #3 VMLAL.S16 q0, d16, d6[3] ; Process 2 samples here if we have > 2 left (still reading one extra y value). xcorr_kernel_neon_process2 ADDS r12, r12, #2 BLE xcorr_kernel_neon_process1 ; Load x[0...1] VLD2.16 {d6[],d7[]}, [r4]! ; Use VAND since it's a data processing instruction again. VAND d4, d5, d5 SUB r12, r12, #2 ; Load y[4...5] VLD1.32 {d5[]}, [r5]! VMLAL.S16 q0, d4, d6 VEXT.16 d16, d4, d5, #1 ; Replace bottom copy of {y5,y4} in d5 with {y3,y2} from d4, using VSRI ; instead of VEXT, since it's a data-processing instruction. VSRI.64 d5, d4, #32 VMLAL.S16 q0, d16, d7 ; Process 1 sample using the extra y value we loaded above. xcorr_kernel_neon_process1 ; Load next *x VLD1.16 {d6[]}, [r4]! ADDS r12, r12, #1 ; y[0...3] are left in d5 from prior iteration(s) (if any) VMLAL.S16 q0, d5, d6 MOVLE pc, lr ; Now process 1 last sample, not reading ahead. ; Load last *y VLD1.16 {d4[]}, [r5]! VSRI.64 d4, d5, #16 ; Load last *x VLD1.16 {d6[]}, [r4]! VMLAL.S16 q0, d4, d6 MOV pc, lr ENDP ; opus_val32 celt_pitch_xcorr_neon(opus_val16 *_x, opus_val16 *_y, ; opus_val32 *xcorr, int len, int max_pitch) celt_pitch_xcorr_neon PROC ; input: ; r0 = opus_val16 *_x ; r1 = opus_val16 *_y ; r2 = opus_val32 *xcorr ; r3 = int len ; output: ; r0 = int maxcorr ; internal usage: ; r4 = opus_val16 *x (for xcorr_kernel_neon()) ; r5 = opus_val16 *y (for xcorr_kernel_neon()) ; r6 = int max_pitch ; r12 = int j ; q15 = int maxcorr[4] (q15 is not used by xcorr_kernel_neon()) STMFD sp!, {r4-r6, lr} LDR r6, [sp, #16] VMOV.S32 q15, #1 ; if (max_pitch < 4) goto celt_pitch_xcorr_neon_process4_done SUBS r6, r6, #4 BLT celt_pitch_xcorr_neon_process4_done celt_pitch_xcorr_neon_process4 ; xcorr_kernel_neon parameters: ; r3 = len, r4 = _x, r5 = _y, q0 = {0, 0, 0, 0} MOV r4, r0 MOV r5, r1 VEOR q0, q0, q0 ; xcorr_kernel_neon only modifies r4, r5, r12, and q0...q3. ; So we don't save/restore any other registers. BL xcorr_kernel_neon_start SUBS r6, r6, #4 VST1.32 {q0}, [r2]! ; _y += 4 ADD r1, r1, #8 VMAX.S32 q15, q15, q0 ; if (max_pitch < 4) goto celt_pitch_xcorr_neon_process4_done BGE celt_pitch_xcorr_neon_process4 ; We have less than 4 sums left to compute. celt_pitch_xcorr_neon_process4_done ADDS r6, r6, #4 ; Reduce maxcorr to a single value VMAX.S32 d30, d30, d31 VPMAX.S32 d30, d30, d30 ; if (max_pitch <= 0) goto celt_pitch_xcorr_neon_done BLE celt_pitch_xcorr_neon_done ; Now compute each remaining sum one at a time. celt_pitch_xcorr_neon_process_remaining MOV r4, r0 MOV r5, r1 VMOV.I32 q0, #0 SUBS r12, r3, #8 BLT celt_pitch_xcorr_neon_process_remaining4 ; Sum terms 8 at a time. celt_pitch_xcorr_neon_process_remaining_loop8 ; Load x[0...7] VLD1.16 {q1}, [r4]! ; Load y[0...7] VLD1.16 {q2}, [r5]! SUBS r12, r12, #8 VMLAL.S16 q0, d4, d2 VMLAL.S16 q0, d5, d3 BGE celt_pitch_xcorr_neon_process_remaining_loop8 ; Sum terms 4 at a time. celt_pitch_xcorr_neon_process_remaining4 ADDS r12, r12, #4 BLT celt_pitch_xcorr_neon_process_remaining4_done ; Load x[0...3] VLD1.16 {d2}, [r4]! ; Load y[0...3] VLD1.16 {d3}, [r5]! SUB r12, r12, #4 VMLAL.S16 q0, d3, d2 celt_pitch_xcorr_neon_process_remaining4_done ; Reduce the sum to a single value. VADD.S32 d0, d0, d1 VPADDL.S32 d0, d0 ADDS r12, r12, #4 BLE celt_pitch_xcorr_neon_process_remaining_loop_done ; Sum terms 1 at a time. celt_pitch_xcorr_neon_process_remaining_loop1 VLD1.16 {d2[]}, [r4]! VLD1.16 {d3[]}, [r5]! SUBS r12, r12, #1 VMLAL.S16 q0, d2, d3 BGT celt_pitch_xcorr_neon_process_remaining_loop1 celt_pitch_xcorr_neon_process_remaining_loop_done VST1.32 {d0[0]}, [r2]! VMAX.S32 d30, d30, d0 SUBS r6, r6, #1 ; _y++ ADD r1, r1, #2 ; if (--max_pitch > 0) goto celt_pitch_xcorr_neon_process_remaining BGT celt_pitch_xcorr_neon_process_remaining celt_pitch_xcorr_neon_done VMOV.32 r0, d30[0] LDMFD sp!, {r4-r6, pc} ENDP ENDIF IF OPUS_ARM_MAY_HAVE_EDSP ; This will get used on ARMv7 devices without NEON, so it has been optimized ; to take advantage of dual-issuing where possible. xcorr_kernel_edsp PROC xcorr_kernel_edsp_start ; input: ; r3 = int len ; r4 = opus_val16 *_x (must be 32-bit aligned) ; r5 = opus_val16 *_y (must be 32-bit aligned) ; r6...r9 = opus_val32 sum[4] ; output: ; r6...r9 = opus_val32 sum[4] ; preserved: r0-r5 ; internal usage ; r2 = int j ; r12,r14 = opus_val16 x[4] ; r10,r11 = opus_val16 y[4] STMFD sp!, {r2,r4,r5,lr} LDR r10, [r5], #4 ; Load y[0...1] SUBS r2, r3, #4 ; j = len-4 LDR r11, [r5], #4 ; Load y[2...3] BLE xcorr_kernel_edsp_process4_done LDR r12, [r4], #4 ; Load x[0...1] ; Stall xcorr_kernel_edsp_process4 ; The multiplies must issue from pipeline 0, and can't dual-issue with each ; other. Every other instruction here dual-issues with a multiply, and is ; thus "free". There should be no stalls in the body of the loop. SMLABB r6, r12, r10, r6 ; sum[0] = MAC16_16(sum[0],x_0,y_0) LDR r14, [r4], #4 ; Load x[2...3] SMLABT r7, r12, r10, r7 ; sum[1] = MAC16_16(sum[1],x_0,y_1) SUBS r2, r2, #4 ; j-=4 SMLABB r8, r12, r11, r8 ; sum[2] = MAC16_16(sum[2],x_0,y_2) SMLABT r9, r12, r11, r9 ; sum[3] = MAC16_16(sum[3],x_0,y_3) SMLATT r6, r12, r10, r6 ; sum[0] = MAC16_16(sum[0],x_1,y_1) LDR r10, [r5], #4 ; Load y[4...5] SMLATB r7, r12, r11, r7 ; sum[1] = MAC16_16(sum[1],x_1,y_2) SMLATT r8, r12, r11, r8 ; sum[2] = MAC16_16(sum[2],x_1,y_3) SMLATB r9, r12, r10, r9 ; sum[3] = MAC16_16(sum[3],x_1,y_4) LDRGT r12, [r4], #4 ; Load x[0...1] SMLABB r6, r14, r11, r6 ; sum[0] = MAC16_16(sum[0],x_2,y_2) SMLABT r7, r14, r11, r7 ; sum[1] = MAC16_16(sum[1],x_2,y_3) SMLABB r8, r14, r10, r8 ; sum[2] = MAC16_16(sum[2],x_2,y_4) SMLABT r9, r14, r10, r9 ; sum[3] = MAC16_16(sum[3],x_2,y_5) SMLATT r6, r14, r11, r6 ; sum[0] = MAC16_16(sum[0],x_3,y_3) LDR r11, [r5], #4 ; Load y[6...7] SMLATB r7, r14, r10, r7 ; sum[1] = MAC16_16(sum[1],x_3,y_4) SMLATT r8, r14, r10, r8 ; sum[2] = MAC16_16(sum[2],x_3,y_5) SMLATB r9, r14, r11, r9 ; sum[3] = MAC16_16(sum[3],x_3,y_6) BGT xcorr_kernel_edsp_process4 xcorr_kernel_edsp_process4_done ADDS r2, r2, #4 BLE xcorr_kernel_edsp_done LDRH r12, [r4], #2 ; r12 = *x++ SUBS r2, r2, #1 ; j-- ; Stall SMLABB r6, r12, r10, r6 ; sum[0] = MAC16_16(sum[0],x,y_0) LDRHGT r14, [r4], #2 ; r14 = *x++ SMLABT r7, r12, r10, r7 ; sum[1] = MAC16_16(sum[1],x,y_1) SMLABB r8, r12, r11, r8 ; sum[2] = MAC16_16(sum[2],x,y_2) SMLABT r9, r12, r11, r9 ; sum[3] = MAC16_16(sum[3],x,y_3) BLE xcorr_kernel_edsp_done SMLABT r6, r14, r10, r6 ; sum[0] = MAC16_16(sum[0],x,y_1) SUBS r2, r2, #1 ; j-- SMLABB r7, r14, r11, r7 ; sum[1] = MAC16_16(sum[1],x,y_2) LDRH r10, [r5], #2 ; r10 = y_4 = *y++ SMLABT r8, r14, r11, r8 ; sum[2] = MAC16_16(sum[2],x,y_3) LDRHGT r12, [r4], #2 ; r12 = *x++ SMLABB r9, r14, r10, r9 ; sum[3] = MAC16_16(sum[3],x,y_4) BLE xcorr_kernel_edsp_done SMLABB r6, r12, r11, r6 ; sum[0] = MAC16_16(sum[0],tmp,y_2) CMP r2, #1 ; j-- SMLABT r7, r12, r11, r7 ; sum[1] = MAC16_16(sum[1],tmp,y_3) LDRH r2, [r5], #2 ; r2 = y_5 = *y++ SMLABB r8, r12, r10, r8 ; sum[2] = MAC16_16(sum[2],tmp,y_4) LDRHGT r14, [r4] ; r14 = *x SMLABB r9, r12, r2, r9 ; sum[3] = MAC16_16(sum[3],tmp,y_5) BLE xcorr_kernel_edsp_done SMLABT r6, r14, r11, r6 ; sum[0] = MAC16_16(sum[0],tmp,y_3) LDRH r11, [r5] ; r11 = y_6 = *y SMLABB r7, r14, r10, r7 ; sum[1] = MAC16_16(sum[1],tmp,y_4) SMLABB r8, r14, r2, r8 ; sum[2] = MAC16_16(sum[2],tmp,y_5) SMLABB r9, r14, r11, r9 ; sum[3] = MAC16_16(sum[3],tmp,y_6) xcorr_kernel_edsp_done LDMFD sp!, {r2,r4,r5,pc} ENDP celt_pitch_xcorr_edsp PROC ; input: ; r0 = opus_val16 *_x (must be 32-bit aligned) ; r1 = opus_val16 *_y (only needs to be 16-bit aligned) ; r2 = opus_val32 *xcorr ; r3 = int len ; output: ; r0 = maxcorr ; internal usage ; r4 = opus_val16 *x ; r5 = opus_val16 *y ; r6 = opus_val32 sum0 ; r7 = opus_val32 sum1 ; r8 = opus_val32 sum2 ; r9 = opus_val32 sum3 ; r1 = int max_pitch ; r12 = int j STMFD sp!, {r4-r11, lr} MOV r5, r1 LDR r1, [sp, #36] MOV r4, r0 TST r5, #3 ; maxcorr = 1 MOV r0, #1 BEQ celt_pitch_xcorr_edsp_process1u_done ; Compute one sum at the start to make y 32-bit aligned. SUBS r12, r3, #4 ; r14 = sum = 0 MOV r14, #0 LDRH r8, [r5], #2 BLE celt_pitch_xcorr_edsp_process1u_loop4_done LDR r6, [r4], #4 MOV r8, r8, LSL #16 celt_pitch_xcorr_edsp_process1u_loop4 LDR r9, [r5], #4 SMLABT r14, r6, r8, r14 ; sum = MAC16_16(sum, x_0, y_0) LDR r7, [r4], #4 SMLATB r14, r6, r9, r14 ; sum = MAC16_16(sum, x_1, y_1) LDR r8, [r5], #4 SMLABT r14, r7, r9, r14 ; sum = MAC16_16(sum, x_2, y_2) SUBS r12, r12, #4 ; j-=4 SMLATB r14, r7, r8, r14 ; sum = MAC16_16(sum, x_3, y_3) LDRGT r6, [r4], #4 BGT celt_pitch_xcorr_edsp_process1u_loop4 MOV r8, r8, LSR #16 celt_pitch_xcorr_edsp_process1u_loop4_done ADDS r12, r12, #4 celt_pitch_xcorr_edsp_process1u_loop1 LDRHGE r6, [r4], #2 ; Stall SMLABBGE r14, r6, r8, r14 ; sum = MAC16_16(sum, *x, *y) SUBSGE r12, r12, #1 LDRHGT r8, [r5], #2 BGT celt_pitch_xcorr_edsp_process1u_loop1 ; Restore _x SUB r4, r4, r3, LSL #1 ; Restore and advance _y SUB r5, r5, r3, LSL #1 ; maxcorr = max(maxcorr, sum) CMP r0, r14 ADD r5, r5, #2 MOVLT r0, r14 SUBS r1, r1, #1 ; xcorr[i] = sum STR r14, [r2], #4 BLE celt_pitch_xcorr_edsp_done celt_pitch_xcorr_edsp_process1u_done ; if (max_pitch < 4) goto celt_pitch_xcorr_edsp_process2 SUBS r1, r1, #4 BLT celt_pitch_xcorr_edsp_process2 celt_pitch_xcorr_edsp_process4 ; xcorr_kernel_edsp parameters: ; r3 = len, r4 = _x, r5 = _y, r6...r9 = sum[4] = {0, 0, 0, 0} MOV r6, #0 MOV r7, #0 MOV r8, #0 MOV r9, #0 BL xcorr_kernel_edsp_start ; xcorr_kernel_edsp(_x, _y+i, xcorr+i, len) ; maxcorr = max(maxcorr, sum0, sum1, sum2, sum3) CMP r0, r6 ; _y+=4 ADD r5, r5, #8 MOVLT r0, r6 CMP r0, r7 MOVLT r0, r7 CMP r0, r8 MOVLT r0, r8 CMP r0, r9 MOVLT r0, r9 STMIA r2!, {r6-r9} SUBS r1, r1, #4 BGE celt_pitch_xcorr_edsp_process4 celt_pitch_xcorr_edsp_process2 ADDS r1, r1, #2 BLT celt_pitch_xcorr_edsp_process1a SUBS r12, r3, #4 ; {r10, r11} = {sum0, sum1} = {0, 0} MOV r10, #0 MOV r11, #0 LDR r8, [r5], #4 BLE celt_pitch_xcorr_edsp_process2_loop_done LDR r6, [r4], #4 LDR r9, [r5], #4 celt_pitch_xcorr_edsp_process2_loop4 SMLABB r10, r6, r8, r10 ; sum0 = MAC16_16(sum0, x_0, y_0) LDR r7, [r4], #4 SMLABT r11, r6, r8, r11 ; sum1 = MAC16_16(sum1, x_0, y_1) SUBS r12, r12, #4 ; j-=4 SMLATT r10, r6, r8, r10 ; sum0 = MAC16_16(sum0, x_1, y_1) LDR r8, [r5], #4 SMLATB r11, r6, r9, r11 ; sum1 = MAC16_16(sum1, x_1, y_2) LDRGT r6, [r4], #4 SMLABB r10, r7, r9, r10 ; sum0 = MAC16_16(sum0, x_2, y_2) SMLABT r11, r7, r9, r11 ; sum1 = MAC16_16(sum1, x_2, y_3) SMLATT r10, r7, r9, r10 ; sum0 = MAC16_16(sum0, x_3, y_3) LDRGT r9, [r5], #4 SMLATB r11, r7, r8, r11 ; sum1 = MAC16_16(sum1, x_3, y_4) BGT celt_pitch_xcorr_edsp_process2_loop4 celt_pitch_xcorr_edsp_process2_loop_done ADDS r12, r12, #2 BLE celt_pitch_xcorr_edsp_process2_1 LDR r6, [r4], #4 ; Stall SMLABB r10, r6, r8, r10 ; sum0 = MAC16_16(sum0, x_0, y_0) LDR r9, [r5], #4 SMLABT r11, r6, r8, r11 ; sum1 = MAC16_16(sum1, x_0, y_1) SUB r12, r12, #2 SMLATT r10, r6, r8, r10 ; sum0 = MAC16_16(sum0, x_1, y_1) MOV r8, r9 SMLATB r11, r6, r9, r11 ; sum1 = MAC16_16(sum1, x_1, y_2) celt_pitch_xcorr_edsp_process2_1 LDRH r6, [r4], #2 ADDS r12, r12, #1 ; Stall SMLABB r10, r6, r8, r10 ; sum0 = MAC16_16(sum0, x_0, y_0) LDRHGT r7, [r4], #2 SMLABT r11, r6, r8, r11 ; sum1 = MAC16_16(sum1, x_0, y_1) BLE celt_pitch_xcorr_edsp_process2_done LDRH r9, [r5], #2 SMLABT r10, r7, r8, r10 ; sum0 = MAC16_16(sum0, x_0, y_1) SMLABB r11, r7, r9, r11 ; sum1 = MAC16_16(sum1, x_0, y_2) celt_pitch_xcorr_edsp_process2_done ; Restore _x SUB r4, r4, r3, LSL #1 ; Restore and advance _y SUB r5, r5, r3, LSL #1 ; maxcorr = max(maxcorr, sum0) CMP r0, r10 ADD r5, r5, #2 MOVLT r0, r10 SUB r1, r1, #2 ; maxcorr = max(maxcorr, sum1) CMP r0, r11 ; xcorr[i] = sum STR r10, [r2], #4 MOVLT r0, r11 STR r11, [r2], #4 celt_pitch_xcorr_edsp_process1a ADDS r1, r1, #1 BLT celt_pitch_xcorr_edsp_done SUBS r12, r3, #4 ; r14 = sum = 0 MOV r14, #0 BLT celt_pitch_xcorr_edsp_process1a_loop_done LDR r6, [r4], #4 LDR r8, [r5], #4 LDR r7, [r4], #4 LDR r9, [r5], #4 celt_pitch_xcorr_edsp_process1a_loop4 SMLABB r14, r6, r8, r14 ; sum = MAC16_16(sum, x_0, y_0) SUBS r12, r12, #4 ; j-=4 SMLATT r14, r6, r8, r14 ; sum = MAC16_16(sum, x_1, y_1) LDRGE r6, [r4], #4 SMLABB r14, r7, r9, r14 ; sum = MAC16_16(sum, x_2, y_2) LDRGE r8, [r5], #4 SMLATT r14, r7, r9, r14 ; sum = MAC16_16(sum, x_3, y_3) LDRGE r7, [r4], #4 LDRGE r9, [r5], #4 BGE celt_pitch_xcorr_edsp_process1a_loop4 celt_pitch_xcorr_edsp_process1a_loop_done ADDS r12, r12, #2 LDRGE r6, [r4], #4 LDRGE r8, [r5], #4 ; Stall SMLABBGE r14, r6, r8, r14 ; sum = MAC16_16(sum, x_0, y_0) SUBGE r12, r12, #2 SMLATTGE r14, r6, r8, r14 ; sum = MAC16_16(sum, x_1, y_1) ADDS r12, r12, #1 LDRHGE r6, [r4], #2 LDRHGE r8, [r5], #2 ; Stall SMLABBGE r14, r6, r8, r14 ; sum = MAC16_16(sum, *x, *y) ; maxcorr = max(maxcorr, sum) CMP r0, r14 ; xcorr[i] = sum STR r14, [r2], #4 MOVLT r0, r14 celt_pitch_xcorr_edsp_done LDMFD sp!, {r4-r11, pc} ENDP ENDIF END ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/fft_arm.h ================================================ /* Copyright (c) 2015 Xiph.Org Foundation Written by Viswanath Puttagunta */ /** @file fft_arm.h @brief ARM Neon Intrinsic optimizations for fft using NE10 library */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if !defined(FFT_ARM_H) #define FFT_ARM_H #include "config.h" #include "kiss_fft.h" #if defined(HAVE_ARM_NE10) int opus_fft_alloc_arm_neon(kiss_fft_state *st); void opus_fft_free_arm_neon(kiss_fft_state *st); void opus_fft_neon(const kiss_fft_state *st, const kiss_fft_cpx *fin, kiss_fft_cpx *fout); void opus_ifft_neon(const kiss_fft_state *st, const kiss_fft_cpx *fin, kiss_fft_cpx *fout); #if !defined(OPUS_HAVE_RTCD) #define OVERRIDE_OPUS_FFT (1) #define opus_fft_alloc_arch(_st, arch) \ ((void)(arch), opus_fft_alloc_arm_neon(_st)) #define opus_fft_free_arch(_st, arch) \ ((void)(arch), opus_fft_free_arm_neon(_st)) #define opus_fft(_st, _fin, _fout, arch) \ ((void)(arch), opus_fft_neon(_st, _fin, _fout)) #define opus_ifft(_st, _fin, _fout, arch) \ ((void)(arch), opus_ifft_neon(_st, _fin, _fout)) #endif /* OPUS_HAVE_RTCD */ #endif /* HAVE_ARM_NE10 */ #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/fixed_armv4.h ================================================ /* Copyright (C) 2013 Xiph.Org Foundation and contributors */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FIXED_ARMv4_H #define FIXED_ARMv4_H /** 16x32 multiplication, followed by a 16-bit shift right. Results fits in 32 bits */ #undef MULT16_32_Q16 static OPUS_INLINE opus_val32 MULT16_32_Q16_armv4(opus_val16 a, opus_val32 b) { unsigned rd_lo; int rd_hi; __asm__( "#MULT16_32_Q16\n\t" "smull %0, %1, %2, %3\n\t" : "=&r"(rd_lo), "=&r"(rd_hi) : "%r"(b),"r"(a<<16) ); return rd_hi; } #define MULT16_32_Q16(a, b) (MULT16_32_Q16_armv4(a, b)) /** 16x32 multiplication, followed by a 15-bit shift right. Results fits in 32 bits */ #undef MULT16_32_Q15 static OPUS_INLINE opus_val32 MULT16_32_Q15_armv4(opus_val16 a, opus_val32 b) { unsigned rd_lo; int rd_hi; __asm__( "#MULT16_32_Q15\n\t" "smull %0, %1, %2, %3\n\t" : "=&r"(rd_lo), "=&r"(rd_hi) : "%r"(b), "r"(a<<16) ); /*We intentionally don't OR in the high bit of rd_lo for speed.*/ return rd_hi<<1; } #define MULT16_32_Q15(a, b) (MULT16_32_Q15_armv4(a, b)) /** 16x32 multiply, followed by a 15-bit shift right and 32-bit add. b must fit in 31 bits. Result fits in 32 bits. */ #undef MAC16_32_Q15 #define MAC16_32_Q15(c, a, b) ADD32(c, MULT16_32_Q15(a, b)) /** 16x32 multiply, followed by a 16-bit shift right and 32-bit add. Result fits in 32 bits. */ #undef MAC16_32_Q16 #define MAC16_32_Q16(c, a, b) ADD32(c, MULT16_32_Q16(a, b)) /** 32x32 multiplication, followed by a 31-bit shift right. Results fits in 32 bits */ #undef MULT32_32_Q31 #define MULT32_32_Q31(a,b) (opus_val32)((((opus_int64)(a)) * ((opus_int64)(b)))>>31) #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/fixed_armv5e.h ================================================ /* Copyright (C) 2007-2009 Xiph.Org Foundation Copyright (C) 2003-2008 Jean-Marc Valin Copyright (C) 2007-2008 CSIRO Copyright (C) 2013 Parrot */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FIXED_ARMv5E_H #define FIXED_ARMv5E_H #include "fixed_armv4.h" /** 16x32 multiplication, followed by a 16-bit shift right. Results fits in 32 bits */ #undef MULT16_32_Q16 static OPUS_INLINE opus_val32 MULT16_32_Q16_armv5e(opus_val16 a, opus_val32 b) { int res; __asm__( "#MULT16_32_Q16\n\t" "smulwb %0, %1, %2\n\t" : "=r"(res) : "r"(b),"r"(a) ); return res; } #define MULT16_32_Q16(a, b) (MULT16_32_Q16_armv5e(a, b)) /** 16x32 multiplication, followed by a 15-bit shift right. Results fits in 32 bits */ #undef MULT16_32_Q15 static OPUS_INLINE opus_val32 MULT16_32_Q15_armv5e(opus_val16 a, opus_val32 b) { int res; __asm__( "#MULT16_32_Q15\n\t" "smulwb %0, %1, %2\n\t" : "=r"(res) : "r"(b), "r"(a) ); return res<<1; } #define MULT16_32_Q15(a, b) (MULT16_32_Q15_armv5e(a, b)) /** 16x32 multiply, followed by a 15-bit shift right and 32-bit add. b must fit in 31 bits. Result fits in 32 bits. */ #undef MAC16_32_Q15 static OPUS_INLINE opus_val32 MAC16_32_Q15_armv5e(opus_val32 c, opus_val16 a, opus_val32 b) { int res; __asm__( "#MAC16_32_Q15\n\t" "smlawb %0, %1, %2, %3;\n" : "=r"(res) : "r"(b<<1), "r"(a), "r"(c) ); return res; } #define MAC16_32_Q15(c, a, b) (MAC16_32_Q15_armv5e(c, a, b)) /** 16x32 multiply, followed by a 16-bit shift right and 32-bit add. Result fits in 32 bits. */ #undef MAC16_32_Q16 static OPUS_INLINE opus_val32 MAC16_32_Q16_armv5e(opus_val32 c, opus_val16 a, opus_val32 b) { int res; __asm__( "#MAC16_32_Q16\n\t" "smlawb %0, %1, %2, %3;\n" : "=r"(res) : "r"(b), "r"(a), "r"(c) ); return res; } #define MAC16_32_Q16(c, a, b) (MAC16_32_Q16_armv5e(c, a, b)) /** 16x16 multiply-add where the result fits in 32 bits */ #undef MAC16_16 static OPUS_INLINE opus_val32 MAC16_16_armv5e(opus_val32 c, opus_val16 a, opus_val16 b) { int res; __asm__( "#MAC16_16\n\t" "smlabb %0, %1, %2, %3;\n" : "=r"(res) : "r"(a), "r"(b), "r"(c) ); return res; } #define MAC16_16(c, a, b) (MAC16_16_armv5e(c, a, b)) /** 16x16 multiplication where the result fits in 32 bits */ #undef MULT16_16 static OPUS_INLINE opus_val32 MULT16_16_armv5e(opus_val16 a, opus_val16 b) { int res; __asm__( "#MULT16_16\n\t" "smulbb %0, %1, %2;\n" : "=r"(res) : "r"(a), "r"(b) ); return res; } #define MULT16_16(a, b) (MULT16_16_armv5e(a, b)) #ifdef OPUS_ARM_INLINE_MEDIA #undef SIG2WORD16 static OPUS_INLINE opus_val16 SIG2WORD16_armv6(opus_val32 x) { celt_sig res; __asm__( "#SIG2WORD16\n\t" "ssat %0, #16, %1, ASR #12\n\t" : "=r"(res) : "r"(x+2048) ); return EXTRACT16(res); } #define SIG2WORD16(x) (SIG2WORD16_armv6(x)) #endif /* OPUS_ARM_INLINE_MEDIA */ #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/kiss_fft_armv4.h ================================================ /*Copyright (c) 2013, Xiph.Org Foundation and contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ #ifndef KISS_FFT_ARMv4_H #define KISS_FFT_ARMv4_H #if !defined(KISS_FFT_GUTS_H) #error "This file should only be included from _kiss_fft_guts.h" #endif #ifdef FIXED_POINT #undef C_MUL #define C_MUL(m,a,b) \ do{ \ int br__; \ int bi__; \ int tt__; \ __asm__ __volatile__( \ "#C_MUL\n\t" \ "ldrsh %[br], [%[bp], #0]\n\t" \ "ldm %[ap], {r0,r1}\n\t" \ "ldrsh %[bi], [%[bp], #2]\n\t" \ "smull %[tt], %[mi], r1, %[br]\n\t" \ "smlal %[tt], %[mi], r0, %[bi]\n\t" \ "rsb %[bi], %[bi], #0\n\t" \ "smull %[br], %[mr], r0, %[br]\n\t" \ "mov %[tt], %[tt], lsr #15\n\t" \ "smlal %[br], %[mr], r1, %[bi]\n\t" \ "orr %[mi], %[tt], %[mi], lsl #17\n\t" \ "mov %[br], %[br], lsr #15\n\t" \ "orr %[mr], %[br], %[mr], lsl #17\n\t" \ : [mr]"=r"((m).r), [mi]"=r"((m).i), \ [br]"=&r"(br__), [bi]"=r"(bi__), [tt]"=r"(tt__) \ : [ap]"r"(&(a)), [bp]"r"(&(b)) \ : "r0", "r1" \ ); \ } \ while(0) #undef C_MUL4 #define C_MUL4(m,a,b) \ do{ \ int br__; \ int bi__; \ int tt__; \ __asm__ __volatile__( \ "#C_MUL4\n\t" \ "ldrsh %[br], [%[bp], #0]\n\t" \ "ldm %[ap], {r0,r1}\n\t" \ "ldrsh %[bi], [%[bp], #2]\n\t" \ "smull %[tt], %[mi], r1, %[br]\n\t" \ "smlal %[tt], %[mi], r0, %[bi]\n\t" \ "rsb %[bi], %[bi], #0\n\t" \ "smull %[br], %[mr], r0, %[br]\n\t" \ "mov %[tt], %[tt], lsr #17\n\t" \ "smlal %[br], %[mr], r1, %[bi]\n\t" \ "orr %[mi], %[tt], %[mi], lsl #15\n\t" \ "mov %[br], %[br], lsr #17\n\t" \ "orr %[mr], %[br], %[mr], lsl #15\n\t" \ : [mr]"=r"((m).r), [mi]"=r"((m).i), \ [br]"=&r"(br__), [bi]"=r"(bi__), [tt]"=r"(tt__) \ : [ap]"r"(&(a)), [bp]"r"(&(b)) \ : "r0", "r1" \ ); \ } \ while(0) #undef C_MULC #define C_MULC(m,a,b) \ do{ \ int br__; \ int bi__; \ int tt__; \ __asm__ __volatile__( \ "#C_MULC\n\t" \ "ldrsh %[br], [%[bp], #0]\n\t" \ "ldm %[ap], {r0,r1}\n\t" \ "ldrsh %[bi], [%[bp], #2]\n\t" \ "smull %[tt], %[mr], r0, %[br]\n\t" \ "smlal %[tt], %[mr], r1, %[bi]\n\t" \ "rsb %[bi], %[bi], #0\n\t" \ "smull %[br], %[mi], r1, %[br]\n\t" \ "mov %[tt], %[tt], lsr #15\n\t" \ "smlal %[br], %[mi], r0, %[bi]\n\t" \ "orr %[mr], %[tt], %[mr], lsl #17\n\t" \ "mov %[br], %[br], lsr #15\n\t" \ "orr %[mi], %[br], %[mi], lsl #17\n\t" \ : [mr]"=r"((m).r), [mi]"=r"((m).i), \ [br]"=&r"(br__), [bi]"=r"(bi__), [tt]"=r"(tt__) \ : [ap]"r"(&(a)), [bp]"r"(&(b)) \ : "r0", "r1" \ ); \ } \ while(0) #endif /* FIXED_POINT */ #endif /* KISS_FFT_ARMv4_H */ ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/kiss_fft_armv5e.h ================================================ /*Copyright (c) 2013, Xiph.Org Foundation and contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ #ifndef KISS_FFT_ARMv5E_H #define KISS_FFT_ARMv5E_H #if !defined(KISS_FFT_GUTS_H) #error "This file should only be included from _kiss_fft_guts.h" #endif #ifdef FIXED_POINT #if defined(__thumb__)||defined(__thumb2__) #define LDRD_CONS "Q" #else #define LDRD_CONS "Uq" #endif #undef C_MUL #define C_MUL(m,a,b) \ do{ \ int mr1__; \ int mr2__; \ int mi__; \ long long aval__; \ int bval__; \ __asm__( \ "#C_MUL\n\t" \ "ldrd %[aval], %H[aval], %[ap]\n\t" \ "ldr %[bval], %[bp]\n\t" \ "smulwb %[mi], %H[aval], %[bval]\n\t" \ "smulwb %[mr1], %[aval], %[bval]\n\t" \ "smulwt %[mr2], %H[aval], %[bval]\n\t" \ "smlawt %[mi], %[aval], %[bval], %[mi]\n\t" \ : [mr1]"=r"(mr1__), [mr2]"=r"(mr2__), [mi]"=r"(mi__), \ [aval]"=&r"(aval__), [bval]"=r"(bval__) \ : [ap]LDRD_CONS(a), [bp]"m"(b) \ ); \ (m).r = SHL32(SUB32(mr1__, mr2__), 1); \ (m).i = SHL32(mi__, 1); \ } \ while(0) #undef C_MUL4 #define C_MUL4(m,a,b) \ do{ \ int mr1__; \ int mr2__; \ int mi__; \ long long aval__; \ int bval__; \ __asm__( \ "#C_MUL4\n\t" \ "ldrd %[aval], %H[aval], %[ap]\n\t" \ "ldr %[bval], %[bp]\n\t" \ "smulwb %[mi], %H[aval], %[bval]\n\t" \ "smulwb %[mr1], %[aval], %[bval]\n\t" \ "smulwt %[mr2], %H[aval], %[bval]\n\t" \ "smlawt %[mi], %[aval], %[bval], %[mi]\n\t" \ : [mr1]"=r"(mr1__), [mr2]"=r"(mr2__), [mi]"=r"(mi__), \ [aval]"=&r"(aval__), [bval]"=r"(bval__) \ : [ap]LDRD_CONS(a), [bp]"m"(b) \ ); \ (m).r = SHR32(SUB32(mr1__, mr2__), 1); \ (m).i = SHR32(mi__, 1); \ } \ while(0) #undef C_MULC #define C_MULC(m,a,b) \ do{ \ int mr__; \ int mi1__; \ int mi2__; \ long long aval__; \ int bval__; \ __asm__( \ "#C_MULC\n\t" \ "ldrd %[aval], %H[aval], %[ap]\n\t" \ "ldr %[bval], %[bp]\n\t" \ "smulwb %[mr], %[aval], %[bval]\n\t" \ "smulwb %[mi1], %H[aval], %[bval]\n\t" \ "smulwt %[mi2], %[aval], %[bval]\n\t" \ "smlawt %[mr], %H[aval], %[bval], %[mr]\n\t" \ : [mr]"=r"(mr__), [mi1]"=r"(mi1__), [mi2]"=r"(mi2__), \ [aval]"=&r"(aval__), [bval]"=r"(bval__) \ : [ap]LDRD_CONS(a), [bp]"m"(b) \ ); \ (m).r = SHL32(mr__, 1); \ (m).i = SHL32(SUB32(mi1__, mi2__), 1); \ } \ while(0) #endif /* FIXED_POINT */ #endif /* KISS_FFT_GUTS_H */ ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/mdct_arm.h ================================================ /* Copyright (c) 2015 Xiph.Org Foundation Written by Viswanath Puttagunta */ /** @file arm_mdct.h @brief ARM Neon Intrinsic optimizations for mdct using NE10 library */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if !defined(MDCT_ARM_H) #define MDCT_ARM_H #include "config.h" #include "mdct.h" #if defined(HAVE_ARM_NE10) /** Compute a forward MDCT and scale by 4/N, trashes the input array */ void clt_mdct_forward_neon(const mdct_lookup *l, kiss_fft_scalar *in, kiss_fft_scalar * OPUS_RESTRICT out, const opus_val16 *window, int overlap, int shift, int stride, int arch); void clt_mdct_backward_neon(const mdct_lookup *l, kiss_fft_scalar *in, kiss_fft_scalar * OPUS_RESTRICT out, const opus_val16 *window, int overlap, int shift, int stride, int arch); #if !defined(OPUS_HAVE_RTCD) #define OVERRIDE_OPUS_MDCT (1) #define clt_mdct_forward(_l, _in, _out, _window, _int, _shift, _stride, _arch) \ clt_mdct_forward_neon(_l, _in, _out, _window, _int, _shift, _stride, _arch) #define clt_mdct_backward(_l, _in, _out, _window, _int, _shift, _stride, _arch) \ clt_mdct_backward_neon(_l, _in, _out, _window, _int, _shift, _stride, _arch) #endif /* OPUS_HAVE_RTCD */ #endif /* HAVE_ARM_NE10 */ #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/arm/pitch_arm.h ================================================ /* Copyright (c) 2010 Xiph.Org Foundation * Copyright (c) 2013 Parrot */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if !defined(PITCH_ARM_H) # define PITCH_ARM_H # include "armcpu.h" # if defined(FIXED_POINT) # if defined(OPUS_ARM_MAY_HAVE_NEON) opus_val32 celt_pitch_xcorr_neon(const opus_val16 *_x, const opus_val16 *_y, opus_val32 *xcorr, int len, int max_pitch); # endif # if defined(OPUS_ARM_MAY_HAVE_MEDIA) # define celt_pitch_xcorr_media MAY_HAVE_EDSP(celt_pitch_xcorr) # endif # if defined(OPUS_ARM_MAY_HAVE_EDSP) opus_val32 celt_pitch_xcorr_edsp(const opus_val16 *_x, const opus_val16 *_y, opus_val32 *xcorr, int len, int max_pitch); # endif # if !defined(OPUS_HAVE_RTCD) # define OVERRIDE_PITCH_XCORR (1) # define celt_pitch_xcorr(_x, _y, xcorr, len, max_pitch, arch) \ ((void)(arch),PRESUME_NEON(celt_pitch_xcorr)(_x, _y, xcorr, len, max_pitch)) # endif #else /* Start !FIXED_POINT */ /* Float case */ #if defined(OPUS_ARM_MAY_HAVE_NEON_INTR) void celt_pitch_xcorr_float_neon(const opus_val16 *_x, const opus_val16 *_y, opus_val32 *xcorr, int len, int max_pitch); #if !defined(OPUS_HAVE_RTCD) || defined(OPUS_ARM_PRESUME_NEON_INTR) #define OVERRIDE_PITCH_XCORR (1) # define celt_pitch_xcorr(_x, _y, xcorr, len, max_pitch, arch) \ ((void)(arch),celt_pitch_xcorr_float_neon(_x, _y, xcorr, len, max_pitch)) #endif #endif #endif /* end !FIXED_POINT */ #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/bands.c ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Copyright (c) 2008-2009 Gregory Maxwell Written by Jean-Marc Valin and Gregory Maxwell */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "bands.h" #include "modes.h" #include "vq.h" #include "cwrs.h" #include "stack_alloc.h" #include "os_support.h" #include "mathops.h" #include "rate.h" #include "quant_bands.h" #include "pitch.h" int hysteresis_decision(opus_val16 val, const opus_val16 *thresholds, const opus_val16 *hysteresis, int N, int prev) { int i; for (i=0;iprev && val < thresholds[prev]+hysteresis[prev]) i=prev; if (i thresholds[prev-1]-hysteresis[prev-1]) i=prev; return i; } opus_uint32 celt_lcg_rand(opus_uint32 seed) { return 1664525 * seed + 1013904223; } /* This is a cos() approximation designed to be bit-exact on any platform. Bit exactness with this approximation is important because it has an impact on the bit allocation */ static opus_int16 bitexact_cos(opus_int16 x) { opus_int32 tmp; opus_int16 x2; tmp = (4096+((opus_int32)(x)*(x)))>>13; celt_assert(tmp<=32767); x2 = tmp; x2 = (32767-x2) + FRAC_MUL16(x2, (-7651 + FRAC_MUL16(x2, (8277 + FRAC_MUL16(-626, x2))))); celt_assert(x2<=32766); return 1+x2; } static int bitexact_log2tan(int isin,int icos) { int lc; int ls; lc=EC_ILOG(icos); ls=EC_ILOG(isin); icos<<=15-lc; isin<<=15-ls; return (ls-lc)*(1<<11) +FRAC_MUL16(isin, FRAC_MUL16(isin, -2597) + 7932) -FRAC_MUL16(icos, FRAC_MUL16(icos, -2597) + 7932); } #ifdef FIXED_POINT /* Compute the amplitude (sqrt energy) in each of the bands */ void compute_band_energies(const CELTMode *m, const celt_sig *X, celt_ener *bandE, int end, int C, int LM) { int i, c, N; const opus_int16 *eBands = m->eBands; N = m->shortMdctSize< 0) { int shift = celt_ilog2(maxval) - 14 + (((m->logN[i]>>BITRES)+LM+1)>>1); j=eBands[i]<0) { do { sum = MAC16_16(sum, EXTRACT16(SHR32(X[j+c*N],shift)), EXTRACT16(SHR32(X[j+c*N],shift))); } while (++jnbEBands] = EPSILON+VSHR32(EXTEND32(celt_sqrt(sum)),-shift); } else { bandE[i+c*m->nbEBands] = EPSILON; } /*printf ("%f ", bandE[i+c*m->nbEBands]);*/ } } while (++ceBands; N = M*m->shortMdctSize; c=0; do { i=0; do { opus_val16 g; int j,shift; opus_val16 E; shift = celt_zlog2(bandE[i+c*m->nbEBands])-13; E = VSHR32(bandE[i+c*m->nbEBands], shift); g = EXTRACT16(celt_rcp(SHL32(E,3))); j=M*eBands[i]; do { X[j+c*N] = MULT16_16_Q15(VSHR32(freq[j+c*N],shift-1),g); } while (++jeBands; N = m->shortMdctSize<nbEBands] = celt_sqrt(sum); /*printf ("%f ", bandE[i+c*m->nbEBands]);*/ } } while (++ceBands; N = M*m->shortMdctSize; c=0; do { for (i=0;inbEBands]); for (j=M*eBands[i];jeBands; N = M*m->shortMdctSize; bound = M*eBands[end]; if (downsample!=1) bound = IMIN(bound, N/downsample); if (silence) { bound = 0; start = end = 0; } f = freq; x = X+M*eBands[start]; for (i=0;i>DB_SHIFT); if (shift>31) { shift=0; g=0; } else { /* Handle the fractional part. */ g = celt_exp2_frac(lg&((1<eBands[i+1]-m->eBands[i]; /* depth in 1/8 bits */ celt_assert(pulses[i]>=0); depth = celt_udiv(1+pulses[i], (m->eBands[i+1]-m->eBands[i]))>>LM; #ifdef FIXED_POINT thresh32 = SHR32(celt_exp2(-SHL16(depth, 10-BITRES)),1); thresh = MULT16_32_Q15(QCONST16(0.5f, 15), MIN32(32767,thresh32)); { opus_val32 t; t = N0<>1; t = SHL32(t, (7-shift)<<1); sqrt_1 = celt_rsqrt_norm(t); } #else thresh = .5f*celt_exp2(-.125f*depth); sqrt_1 = celt_rsqrt(N0<nbEBands+i]; prev2 = prev2logE[c*m->nbEBands+i]; if (C==1) { prev1 = MAX16(prev1,prev1logE[m->nbEBands+i]); prev2 = MAX16(prev2,prev2logE[m->nbEBands+i]); } Ediff = EXTEND32(logE[c*m->nbEBands+i])-EXTEND32(MIN16(prev1,prev2)); Ediff = MAX32(0, Ediff); #ifdef FIXED_POINT if (Ediff < 16384) { opus_val32 r32 = SHR32(celt_exp2(-EXTRACT16(Ediff)),1); r = 2*MIN16(16383,r32); } else { r = 0; } if (LM==3) r = MULT16_16_Q14(23170, MIN32(23169, r)); r = SHR16(MIN16(thresh, r),1); r = SHR32(MULT16_16_Q15(sqrt_1, r),shift); #else /* r needs to be multiplied by 2 or 2*sqrt(2) depending on LM because short blocks don't have the same energy as long */ r = 2.f*celt_exp2(-Ediff); if (LM==3) r *= 1.41421356f; r = MIN16(thresh, r); r = r*sqrt_1; #endif X = X_+c*size+(m->eBands[i]<nbEBands]))-13; #endif left = VSHR32(bandE[i],shift); right = VSHR32(bandE[i+m->nbEBands],shift); norm = EPSILON + celt_sqrt(EPSILON+MULT16_16(left,left)+MULT16_16(right,right)); a1 = DIV32_16(SHL32(EXTEND32(left),14),norm); a2 = DIV32_16(SHL32(EXTEND32(right),14),norm); for (j=0;j>1; kr = celt_ilog2(Er)>>1; #endif t = VSHR32(El, (kl-7)<<1); lgain = celt_rsqrt_norm(t); t = VSHR32(Er, (kr-7)<<1); rgain = celt_rsqrt_norm(t); #ifdef FIXED_POINT if (kl < 7) kl = 7; if (kr < 7) kr = 7; #endif for (j=0;jeBands; int decision; int hf_sum=0; celt_assert(end>0); N0 = M*m->shortMdctSize; if (M*(eBands[end]-eBands[end-1]) <= 8) return SPREAD_NONE; c=0; do { for (i=0;im->nbEBands-4) hf_sum += celt_udiv(32*(tcount[1]+tcount[0]), N); tmp = (2*tcount[2] >= N) + (2*tcount[1] >= N) + (2*tcount[0] >= N); sum += tmp*256; nbBands++; } } while (++cnbEBands+end)); *hf_average = (*hf_average+hf_sum)>>1; hf_sum = *hf_average; if (*tapset_decision==2) hf_sum += 4; else if (*tapset_decision==0) hf_sum -= 4; if (hf_sum > 22) *tapset_decision=2; else if (hf_sum > 18) *tapset_decision=1; else *tapset_decision=0; } /*printf("%d %d %d\n", hf_sum, *hf_average, *tapset_decision);*/ celt_assert(nbBands>0); /* end has to be non-zero */ celt_assert(sum>=0); sum = celt_udiv(sum, nbBands); /* Recursive averaging */ sum = (sum+*average)>>1; *average = sum; /* Hysteresis */ sum = (3*sum + (((3-last_decision)<<7) + 64) + 2)>>2; if (sum < 80) { decision = SPREAD_AGGRESSIVE; } else if (sum < 256) { decision = SPREAD_NORMAL; } else if (sum < 384) { decision = SPREAD_LIGHT; } else { decision = SPREAD_NONE; } #ifdef FUZZING decision = rand()&0x3; *tapset_decision=rand()%3; #endif return decision; } /* Indexing table for converting from natural Hadamard to ordery Hadamard This is essentially a bit-reversed Gray, on top of which we've added an inversion of the order because we want the DC at the end rather than the beginning. The lines are for N=2, 4, 8, 16 */ static const int ordery_table[] = { 1, 0, 3, 0, 2, 1, 7, 0, 4, 3, 6, 1, 5, 2, 15, 0, 8, 7, 12, 3, 11, 4, 14, 1, 9, 6, 13, 2, 10, 5, }; static void deinterleave_hadamard(celt_norm *X, int N0, int stride, int hadamard) { int i,j; VARDECL(celt_norm, tmp); int N; SAVE_STACK; N = N0*stride; ALLOC(tmp, N, celt_norm); celt_assert(stride>0); if (hadamard) { const int *ordery = ordery_table+stride-2; for (i=0;i>= 1; for (i=0;i>1)) { qn = 1; } else { qn = exp2_table8[qb&0x7]>>(14-(qb>>BITRES)); qn = (qn+1)>>1<<1; } celt_assert(qn <= 256); return qn; } struct band_ctx { int encode; const CELTMode *m; int i; int intensity; int spread; int tf_change; ec_ctx *ec; opus_int32 remaining_bits; const celt_ener *bandE; opus_uint32 seed; int arch; }; struct split_ctx { int inv; int imid; int iside; int delta; int itheta; int qalloc; }; static void compute_theta(struct band_ctx *ctx, struct split_ctx *sctx, celt_norm *X, celt_norm *Y, int N, int *b, int B, int B0, int LM, int stereo, int *fill) { int qn; int itheta=0; int delta; int imid, iside; int qalloc; int pulse_cap; int offset; opus_int32 tell; int inv=0; int encode; const CELTMode *m; int i; int intensity; ec_ctx *ec; const celt_ener *bandE; encode = ctx->encode; m = ctx->m; i = ctx->i; intensity = ctx->intensity; ec = ctx->ec; bandE = ctx->bandE; /* Decide on the resolution to give to the split parameter theta */ pulse_cap = m->logN[i]+LM*(1<>1) - (stereo&&N==2 ? QTHETA_OFFSET_TWOPHASE : QTHETA_OFFSET); qn = compute_qn(N, *b, offset, pulse_cap, stereo); if (stereo && i>=intensity) qn = 1; if (encode) { /* theta is the atan() of the ratio between the (normalized) side and mid. With just that parameter, we can re-scale both mid and side because we know that 1) they have unit norm and 2) they are orthogonal. */ itheta = stereo_itheta(X, Y, stereo, N, ctx->arch); } tell = ec_tell_frac(ec); if (qn!=1) { if (encode) itheta = (itheta*qn+8192)>>14; /* Entropy coding of the angle. We use a uniform pdf for the time split, a step for stereo, and a triangular one for the rest. */ if (stereo && N>2) { int p0 = 3; int x = itheta; int x0 = qn/2; int ft = p0*(x0+1) + x0; /* Use a probability of p0 up to itheta=8192 and then use 1 after */ if (encode) { ec_encode(ec,x<=x0?p0*x:(x-1-x0)+(x0+1)*p0,x<=x0?p0*(x+1):(x-x0)+(x0+1)*p0,ft); } else { int fs; fs=ec_decode(ec,ft); if (fs<(x0+1)*p0) x=fs/p0; else x=x0+1+(fs-(x0+1)*p0); ec_dec_update(ec,x<=x0?p0*x:(x-1-x0)+(x0+1)*p0,x<=x0?p0*(x+1):(x-x0)+(x0+1)*p0,ft); itheta = x; } } else if (B0>1 || stereo) { /* Uniform pdf */ if (encode) ec_enc_uint(ec, itheta, qn+1); else itheta = ec_dec_uint(ec, qn+1); } else { int fs=1, ft; ft = ((qn>>1)+1)*((qn>>1)+1); if (encode) { int fl; fs = itheta <= (qn>>1) ? itheta + 1 : qn + 1 - itheta; fl = itheta <= (qn>>1) ? itheta*(itheta + 1)>>1 : ft - ((qn + 1 - itheta)*(qn + 2 - itheta)>>1); ec_encode(ec, fl, fl+fs, ft); } else { /* Triangular pdf */ int fl=0; int fm; fm = ec_decode(ec, ft); if (fm < ((qn>>1)*((qn>>1) + 1)>>1)) { itheta = (isqrt32(8*(opus_uint32)fm + 1) - 1)>>1; fs = itheta + 1; fl = itheta*(itheta + 1)>>1; } else { itheta = (2*(qn + 1) - isqrt32(8*(opus_uint32)(ft - fm - 1) + 1))>>1; fs = qn + 1 - itheta; fl = ft - ((qn + 1 - itheta)*(qn + 2 - itheta)>>1); } ec_dec_update(ec, fl, fl+fs, ft); } } celt_assert(itheta>=0); itheta = celt_udiv((opus_int32)itheta*16384, qn); if (encode && stereo) { if (itheta==0) intensity_stereo(m, X, Y, bandE, i, N); else stereo_split(X, Y, N); } /* NOTE: Renormalising X and Y *may* help fixed-point a bit at very high rate. Let's do that at higher complexity */ } else if (stereo) { if (encode) { inv = itheta > 8192; if (inv) { int j; for (j=0;j2<remaining_bits > 2<inv = inv; sctx->imid = imid; sctx->iside = iside; sctx->delta = delta; sctx->itheta = itheta; sctx->qalloc = qalloc; } static unsigned quant_band_n1(struct band_ctx *ctx, celt_norm *X, celt_norm *Y, int b, celt_norm *lowband_out) { #ifdef RESYNTH int resynth = 1; #else int resynth = !ctx->encode; #endif int c; int stereo; celt_norm *x = X; int encode; ec_ctx *ec; encode = ctx->encode; ec = ctx->ec; stereo = Y != NULL; c=0; do { int sign=0; if (ctx->remaining_bits>=1<remaining_bits -= 1<encode; #endif celt_norm *Y=NULL; int encode; const CELTMode *m; int i; int spread; ec_ctx *ec; encode = ctx->encode; m = ctx->m; i = ctx->i; spread = ctx->spread; ec = ctx->ec; /* If we need 1.5 more bit than we can produce, split the band in two. */ cache = m->cache.bits + m->cache.index[(LM+1)*m->nbEBands+i]; if (LM != -1 && b > cache[cache[0]]+12 && N>2) { int mbits, sbits, delta; int itheta; int qalloc; struct split_ctx sctx; celt_norm *next_lowband2=NULL; opus_int32 rebalance; N >>= 1; Y = X+N; LM -= 1; if (B==1) fill = (fill&1)|(fill<<1); B = (B+1)>>1; compute_theta(ctx, &sctx, X, Y, N, &b, B, B0, LM, 0, &fill); imid = sctx.imid; iside = sctx.iside; delta = sctx.delta; itheta = sctx.itheta; qalloc = sctx.qalloc; #ifdef FIXED_POINT mid = imid; side = iside; #else mid = (1.f/32768)*imid; side = (1.f/32768)*iside; #endif /* Give more bits to low-energy MDCTs than they would otherwise deserve */ if (B0>1 && (itheta&0x3fff)) { if (itheta > 8192) /* Rough approximation for pre-echo masking */ delta -= delta>>(4-LM); else /* Corresponds to a forward-masking slope of 1.5 dB per 10 ms */ delta = IMIN(0, delta + (N<>(5-LM))); } mbits = IMAX(0, IMIN(b, (b-delta)/2)); sbits = b-mbits; ctx->remaining_bits -= qalloc; if (lowband) next_lowband2 = lowband+N; /* >32-bit split case */ rebalance = ctx->remaining_bits; if (mbits >= sbits) { cm = quant_partition(ctx, X, N, mbits, B, lowband, LM, MULT16_16_P15(gain,mid), fill); rebalance = mbits - (rebalance-ctx->remaining_bits); if (rebalance > 3<>B)<<(B0>>1); } else { cm = quant_partition(ctx, Y, N, sbits, B, next_lowband2, LM, MULT16_16_P15(gain,side), fill>>B)<<(B0>>1); rebalance = sbits - (rebalance-ctx->remaining_bits); if (rebalance > 3<remaining_bits -= curr_bits; /* Ensures we can never bust the budget */ while (ctx->remaining_bits < 0 && q > 0) { ctx->remaining_bits += curr_bits; q--; curr_bits = pulses2bits(m, i, LM, q); ctx->remaining_bits -= curr_bits; } if (q!=0) { int K = get_pulses(q); /* Finally do the actual quantization */ if (encode) { cm = alg_quant(X, N, K, spread, B, ec #ifdef RESYNTH , gain #endif ); } else { cm = alg_unquant(X, N, K, spread, B, ec, gain); } } else { /* If there's no pulse, fill the band anyway */ int j; if (resynth) { unsigned cm_mask; /* B can be as large as 16, so this shift might overflow an int on a 16-bit platform; use a long to get defined behavior.*/ cm_mask = (unsigned)(1UL<seed = celt_lcg_rand(ctx->seed); X[j] = (celt_norm)((opus_int32)ctx->seed>>20); } cm = cm_mask; } else { /* Folded spectrum */ for (j=0;jseed = celt_lcg_rand(ctx->seed); /* About 48 dB below the "normal" folding level */ tmp = QCONST16(1.0f/256, 10); tmp = (ctx->seed)&0x8000 ? tmp : -tmp; X[j] = lowband[j]+tmp; } cm = fill; } renormalise_vector(X, N, gain, ctx->arch); } } } } return cm; } /* This function is responsible for encoding and decoding a band for the mono case. */ static unsigned quant_band(struct band_ctx *ctx, celt_norm *X, int N, int b, int B, celt_norm *lowband, int LM, celt_norm *lowband_out, opus_val16 gain, celt_norm *lowband_scratch, int fill) { int N0=N; int N_B=N; int N_B0; int B0=B; int time_divide=0; int recombine=0; int longBlocks; unsigned cm=0; #ifdef RESYNTH int resynth = 1; #else int resynth = !ctx->encode; #endif int k; int encode; int tf_change; encode = ctx->encode; tf_change = ctx->tf_change; longBlocks = B0==1; N_B = celt_udiv(N_B, B); /* Special case for one sample */ if (N==1) { return quant_band_n1(ctx, X, NULL, b, lowband_out); } if (tf_change>0) recombine = tf_change; /* Band recombining to increase frequency resolution */ if (lowband_scratch && lowband && (recombine || ((N_B&1) == 0 && tf_change<0) || B0>1)) { OPUS_COPY(lowband_scratch, lowband, N); lowband = lowband_scratch; } for (k=0;k>k, 1<>k, 1<>4]<<2; } B>>=recombine; N_B<<=recombine; /* Increasing the time resolution */ while ((N_B&1) == 0 && tf_change<0) { if (encode) haar1(X, N_B, B); if (lowband) haar1(lowband, N_B, B); fill |= fill<>= 1; time_divide++; tf_change++; } B0=B; N_B0 = N_B; /* Reorganize the samples in time order instead of frequency order */ if (B0>1) { if (encode) deinterleave_hadamard(X, N_B>>recombine, B0<>recombine, B0<1) interleave_hadamard(X, N_B>>recombine, B0<>= 1; N_B <<= 1; cm |= cm>>B; haar1(X, N_B, B); } for (k=0;k>k, 1<encode; #endif int mbits, sbits, delta; int itheta; int qalloc; struct split_ctx sctx; int orig_fill; int encode; ec_ctx *ec; encode = ctx->encode; ec = ctx->ec; /* Special case for one sample */ if (N==1) { return quant_band_n1(ctx, X, Y, b, lowband_out); } orig_fill = fill; compute_theta(ctx, &sctx, X, Y, N, &b, B, B, LM, 1, &fill); inv = sctx.inv; imid = sctx.imid; iside = sctx.iside; delta = sctx.delta; itheta = sctx.itheta; qalloc = sctx.qalloc; #ifdef FIXED_POINT mid = imid; side = iside; #else mid = (1.f/32768)*imid; side = (1.f/32768)*iside; #endif /* This is a special case for N=2 that only works for stereo and takes advantage of the fact that mid and side are orthogonal to encode the side with just one bit. */ if (N==2) { int c; int sign=0; celt_norm *x2, *y2; mbits = b; sbits = 0; /* Only need one bit for the side. */ if (itheta != 0 && itheta != 16384) sbits = 1< 8192; ctx->remaining_bits -= qalloc+sbits; x2 = c ? Y : X; y2 = c ? X : Y; if (sbits) { if (encode) { /* Here we only need to encode a sign for the side. */ sign = x2[0]*y2[1] - x2[1]*y2[0] < 0; ec_enc_bits(ec, sign, 1); } else { sign = ec_dec_bits(ec, 1); } } sign = 1-2*sign; /* We use orig_fill here because we want to fold the side, but if itheta==16384, we'll have cleared the low bits of fill. */ cm = quant_band(ctx, x2, N, mbits, B, lowband, LM, lowband_out, Q15ONE, lowband_scratch, orig_fill); /* We don't split N=2 bands, so cm is either 1 or 0 (for a fold-collapse), and there's no need to worry about mixing with the other channel. */ y2[0] = -sign*x2[1]; y2[1] = sign*x2[0]; if (resynth) { celt_norm tmp; X[0] = MULT16_16_Q15(mid, X[0]); X[1] = MULT16_16_Q15(mid, X[1]); Y[0] = MULT16_16_Q15(side, Y[0]); Y[1] = MULT16_16_Q15(side, Y[1]); tmp = X[0]; X[0] = SUB16(tmp,Y[0]); Y[0] = ADD16(tmp,Y[0]); tmp = X[1]; X[1] = SUB16(tmp,Y[1]); Y[1] = ADD16(tmp,Y[1]); } } else { /* "Normal" split code */ opus_int32 rebalance; mbits = IMAX(0, IMIN(b, (b-delta)/2)); sbits = b-mbits; ctx->remaining_bits -= qalloc; rebalance = ctx->remaining_bits; if (mbits >= sbits) { /* In stereo mode, we do not apply a scaling to the mid because we need the normalized mid for folding later. */ cm = quant_band(ctx, X, N, mbits, B, lowband, LM, lowband_out, Q15ONE, lowband_scratch, fill); rebalance = mbits - (rebalance-ctx->remaining_bits); if (rebalance > 3<>B); } else { /* For a stereo split, the high bits of fill are always zero, so no folding will be done to the side. */ cm = quant_band(ctx, Y, N, sbits, B, NULL, LM, NULL, side, NULL, fill>>B); rebalance = sbits - (rebalance-ctx->remaining_bits); if (rebalance > 3<arch); if (inv) { int j; for (j=0;jeBands; celt_norm * OPUS_RESTRICT norm, * OPUS_RESTRICT norm2; VARDECL(celt_norm, _norm); celt_norm *lowband_scratch; int B; int M; int lowband_offset; int update_lowband = 1; int C = Y_ != NULL ? 2 : 1; int norm_offset; #ifdef RESYNTH int resynth = 1; #else int resynth = !encode; #endif struct band_ctx ctx; SAVE_STACK; M = 1<nbEBands-1]-norm_offset), celt_norm); norm = _norm; norm2 = norm + M*eBands[m->nbEBands-1]-norm_offset; /* We can use the last band as scratch space because we don't need that scratch space for the last band. */ lowband_scratch = X_+M*eBands[m->nbEBands-1]; lowband_offset = 0; ctx.bandE = bandE; ctx.ec = ec; ctx.encode = encode; ctx.intensity = intensity; ctx.m = m; ctx.seed = *seed; ctx.spread = spread; ctx.arch = arch; for (i=start;i= M*eBands[start] && (update_lowband || lowband_offset==0)) lowband_offset = i; tf_change = tf_res[i]; ctx.tf_change = tf_change; if (i>=m->effEBands) { X=norm; if (Y_!=NULL) Y = norm; lowband_scratch = NULL; } if (i==end-1) lowband_scratch = NULL; /* Get a conservative estimate of the collapse_mask's for the bands we're going to be folding from. */ if (lowband_offset != 0 && (spread!=SPREAD_AGGRESSIVE || B>1 || tf_change<0)) { int fold_start; int fold_end; int fold_i; /* This ensures we never repeat spectral content within one band */ effective_lowband = IMAX(0, M*eBands[lowband_offset]-norm_offset-N); fold_start = lowband_offset; while(M*eBands[--fold_start] > effective_lowband+norm_offset); fold_end = lowband_offset-1; while(M*eBands[++fold_end] < effective_lowband+norm_offset+N); x_cm = y_cm = 0; fold_i = fold_start; do { x_cm |= collapse_masks[fold_i*C+0]; y_cm |= collapse_masks[fold_i*C+C-1]; } while (++fold_i(N< #include "celt.h" #include "pitch.h" #include "bands.h" #include "modes.h" #include "entcode.h" #include "quant_bands.h" #include "rate.h" #include "stack_alloc.h" #include "mathops.h" #include "float_cast.h" #include #include "celt_lpc.h" #include "vq.h" #ifndef PACKAGE_VERSION #define PACKAGE_VERSION "unknown" #endif #if defined(MIPSr1_ASM) #include "mips/celt_mipsr1.h" #endif int resampling_factor(opus_int32 rate) { int ret; switch (rate) { case 48000: ret = 1; break; case 24000: ret = 2; break; case 16000: ret = 3; break; case 12000: ret = 4; break; case 8000: ret = 6; break; default: #ifndef CUSTOM_MODES celt_assert(0); #endif ret = 0; break; } return ret; } #if !defined(OVERRIDE_COMB_FILTER_CONST) || defined(NON_STATIC_COMB_FILTER_CONST_C) /* This version should be faster on ARM */ #ifdef OPUS_ARM_ASM #ifndef NON_STATIC_COMB_FILTER_CONST_C static #endif void comb_filter_const_c(opus_val32 *y, opus_val32 *x, int T, int N, opus_val16 g10, opus_val16 g11, opus_val16 g12) { opus_val32 x0, x1, x2, x3, x4; int i; x4 = SHL32(x[-T-2], 1); x3 = SHL32(x[-T-1], 1); x2 = SHL32(x[-T], 1); x1 = SHL32(x[-T+1], 1); for (i=0;inbEBands;i++) { int N; N=(m->eBands[i+1]-m->eBands[i])<cache.caps[m->nbEBands*(2*LM+C-1)+i]+64)*C*N>>2; } } const char *opus_strerror(int error) { static const char * const error_strings[8] = { "success", "invalid argument", "buffer too small", "internal error", "corrupted stream", "request not implemented", "invalid state", "memory allocation failed" }; if (error > 0 || error < -7) return "unknown error"; else return error_strings[-error]; } const char *opus_get_version_string(void) { return "libopus " PACKAGE_VERSION /* Applications may rely on the presence of this substring in the version string to determine if they have a fixed-point or floating-point build at runtime. */ #ifdef FIXED_POINT "-fixed" #endif #ifdef FUZZING "-fuzzing" #endif ; } ================================================ FILE: deps/pjsip/third_party/opus/celt/celt.h ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Copyright (c) 2008 Gregory Maxwell Written by Jean-Marc Valin and Gregory Maxwell */ /** @file celt.h @brief Contains all the functions for encoding and decoding audio */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CELT_H #define CELT_H #include "opus_types.h" #include "opus_defines.h" #include "opus_custom.h" #include "entenc.h" #include "entdec.h" #include "arch.h" #ifdef __cplusplus extern "C" { #endif #define CELTEncoder OpusCustomEncoder #define CELTDecoder OpusCustomDecoder #define CELTMode OpusCustomMode typedef struct { int valid; float tonality; float tonality_slope; float noisiness; float activity; float music_prob; int bandwidth; }AnalysisInfo; #define __celt_check_mode_ptr_ptr(ptr) ((ptr) + ((ptr) - (const CELTMode**)(ptr))) #define __celt_check_analysis_ptr(ptr) ((ptr) + ((ptr) - (const AnalysisInfo*)(ptr))) /* Encoder/decoder Requests */ /* Expose this option again when variable framesize actually works */ #define OPUS_FRAMESIZE_VARIABLE 5010 /**< Optimize the frame size dynamically */ #define CELT_SET_PREDICTION_REQUEST 10002 /** Controls the use of interframe prediction. 0=Independent frames 1=Short term interframe prediction allowed 2=Long term prediction allowed */ #define CELT_SET_PREDICTION(x) CELT_SET_PREDICTION_REQUEST, __opus_check_int(x) #define CELT_SET_INPUT_CLIPPING_REQUEST 10004 #define CELT_SET_INPUT_CLIPPING(x) CELT_SET_INPUT_CLIPPING_REQUEST, __opus_check_int(x) #define CELT_GET_AND_CLEAR_ERROR_REQUEST 10007 #define CELT_GET_AND_CLEAR_ERROR(x) CELT_GET_AND_CLEAR_ERROR_REQUEST, __opus_check_int_ptr(x) #define CELT_SET_CHANNELS_REQUEST 10008 #define CELT_SET_CHANNELS(x) CELT_SET_CHANNELS_REQUEST, __opus_check_int(x) /* Internal */ #define CELT_SET_START_BAND_REQUEST 10010 #define CELT_SET_START_BAND(x) CELT_SET_START_BAND_REQUEST, __opus_check_int(x) #define CELT_SET_END_BAND_REQUEST 10012 #define CELT_SET_END_BAND(x) CELT_SET_END_BAND_REQUEST, __opus_check_int(x) #define CELT_GET_MODE_REQUEST 10015 /** Get the CELTMode used by an encoder or decoder */ #define CELT_GET_MODE(x) CELT_GET_MODE_REQUEST, __celt_check_mode_ptr_ptr(x) #define CELT_SET_SIGNALLING_REQUEST 10016 #define CELT_SET_SIGNALLING(x) CELT_SET_SIGNALLING_REQUEST, __opus_check_int(x) #define CELT_SET_TONALITY_REQUEST 10018 #define CELT_SET_TONALITY(x) CELT_SET_TONALITY_REQUEST, __opus_check_int(x) #define CELT_SET_TONALITY_SLOPE_REQUEST 10020 #define CELT_SET_TONALITY_SLOPE(x) CELT_SET_TONALITY_SLOPE_REQUEST, __opus_check_int(x) #define CELT_SET_ANALYSIS_REQUEST 10022 #define CELT_SET_ANALYSIS(x) CELT_SET_ANALYSIS_REQUEST, __celt_check_analysis_ptr(x) #define OPUS_SET_LFE_REQUEST 10024 #define OPUS_SET_LFE(x) OPUS_SET_LFE_REQUEST, __opus_check_int(x) #define OPUS_SET_ENERGY_MASK_REQUEST 10026 #define OPUS_SET_ENERGY_MASK(x) OPUS_SET_ENERGY_MASK_REQUEST, __opus_check_val16_ptr(x) /* Encoder stuff */ int celt_encoder_get_size(int channels); int celt_encode_with_ec(OpusCustomEncoder * OPUS_RESTRICT st, const opus_val16 * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes, ec_enc *enc); int celt_encoder_init(CELTEncoder *st, opus_int32 sampling_rate, int channels, int arch); /* Decoder stuff */ int celt_decoder_get_size(int channels); int celt_decoder_init(CELTDecoder *st, opus_int32 sampling_rate, int channels); int celt_decode_with_ec(OpusCustomDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, opus_val16 * OPUS_RESTRICT pcm, int frame_size, ec_dec *dec, int accum); #define celt_encoder_ctl opus_custom_encoder_ctl #define celt_decoder_ctl opus_custom_decoder_ctl #ifdef CUSTOM_MODES #define OPUS_CUSTOM_NOSTATIC #else #define OPUS_CUSTOM_NOSTATIC static OPUS_INLINE #endif static const unsigned char trim_icdf[11] = {126, 124, 119, 109, 87, 41, 19, 9, 4, 2, 0}; /* Probs: NONE: 21.875%, LIGHT: 6.25%, NORMAL: 65.625%, AGGRESSIVE: 6.25% */ static const unsigned char spread_icdf[4] = {25, 23, 2, 0}; static const unsigned char tapset_icdf[3]={2,1,0}; #ifdef CUSTOM_MODES static const unsigned char toOpusTable[20] = { 0xE0, 0xE8, 0xF0, 0xF8, 0xC0, 0xC8, 0xD0, 0xD8, 0xA0, 0xA8, 0xB0, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x80, 0x88, 0x90, 0x98, }; static const unsigned char fromOpusTable[16] = { 0x80, 0x88, 0x90, 0x98, 0x40, 0x48, 0x50, 0x58, 0x20, 0x28, 0x30, 0x38, 0x00, 0x08, 0x10, 0x18 }; static OPUS_INLINE int toOpus(unsigned char c) { int ret=0; if (c<0xA0) ret = toOpusTable[c>>3]; if (ret == 0) return -1; else return ret|(c&0x7); } static OPUS_INLINE int fromOpus(unsigned char c) { if (c<0x80) return -1; else return fromOpusTable[(c>>3)-16] | (c&0x7); } #endif /* CUSTOM_MODES */ #define COMBFILTER_MAXPERIOD 1024 #define COMBFILTER_MINPERIOD 15 extern const signed char tf_select_table[4][8]; int resampling_factor(opus_int32 rate); void celt_preemphasis(const opus_val16 * OPUS_RESTRICT pcmp, celt_sig * OPUS_RESTRICT inp, int N, int CC, int upsample, const opus_val16 *coef, celt_sig *mem, int clip); void comb_filter(opus_val32 *y, opus_val32 *x, int T0, int T1, int N, opus_val16 g0, opus_val16 g1, int tapset0, int tapset1, const opus_val16 *window, int overlap, int arch); #ifdef NON_STATIC_COMB_FILTER_CONST_C void comb_filter_const_c(opus_val32 *y, opus_val32 *x, int T, int N, opus_val16 g10, opus_val16 g11, opus_val16 g12); #endif #ifndef OVERRIDE_COMB_FILTER_CONST # define comb_filter_const(y, x, T, N, g10, g11, g12, arch) \ ((void)(arch),comb_filter_const_c(y, x, T, N, g10, g11, g12)) #endif void init_caps(const CELTMode *m,int *cap,int LM,int C); #ifdef RESYNTH void deemphasis(celt_sig *in[], opus_val16 *pcm, int N, int C, int downsample, const opus_val16 *coef, celt_sig *mem); void celt_synthesis(const CELTMode *mode, celt_norm *X, celt_sig * out_syn[], opus_val16 *oldBandE, int start, int effEnd, int C, int CC, int isTransient, int LM, int downsample, int silence); #endif #ifdef __cplusplus } #endif #endif /* CELT_H */ ================================================ FILE: deps/pjsip/third_party/opus/celt/celt_decoder.c ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2010 Xiph.Org Foundation Copyright (c) 2008 Gregory Maxwell Written by Jean-Marc Valin and Gregory Maxwell */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #define CELT_DECODER_C #include "cpu_support.h" #include "os_support.h" #include "mdct.h" #include #include "celt.h" #include "pitch.h" #include "bands.h" #include "modes.h" #include "entcode.h" #include "quant_bands.h" #include "rate.h" #include "stack_alloc.h" #include "mathops.h" #include "float_cast.h" #include #include "celt_lpc.h" #include "vq.h" #if defined(SMALL_FOOTPRINT) && defined(FIXED_POINT) #define NORM_ALIASING_HACK #endif /**********************************************************************/ /* */ /* DECODER */ /* */ /**********************************************************************/ #define DECODE_BUFFER_SIZE 2048 /** Decoder state @brief Decoder state */ struct OpusCustomDecoder { const OpusCustomMode *mode; int overlap; int channels; int stream_channels; int downsample; int start, end; int signalling; int arch; /* Everything beyond this point gets cleared on a reset */ #define DECODER_RESET_START rng opus_uint32 rng; int error; int last_pitch_index; int loss_count; int postfilter_period; int postfilter_period_old; opus_val16 postfilter_gain; opus_val16 postfilter_gain_old; int postfilter_tapset; int postfilter_tapset_old; celt_sig preemph_memD[2]; celt_sig _decode_mem[1]; /* Size = channels*(DECODE_BUFFER_SIZE+mode->overlap) */ /* opus_val16 lpc[], Size = channels*LPC_ORDER */ /* opus_val16 oldEBands[], Size = 2*mode->nbEBands */ /* opus_val16 oldLogE[], Size = 2*mode->nbEBands */ /* opus_val16 oldLogE2[], Size = 2*mode->nbEBands */ /* opus_val16 backgroundLogE[], Size = 2*mode->nbEBands */ }; int celt_decoder_get_size(int channels) { const CELTMode *mode = opus_custom_mode_create(48000, 960, NULL); return opus_custom_decoder_get_size(mode, channels); } OPUS_CUSTOM_NOSTATIC int opus_custom_decoder_get_size(const CELTMode *mode, int channels) { int size = sizeof(struct CELTDecoder) + (channels*(DECODE_BUFFER_SIZE+mode->overlap)-1)*sizeof(celt_sig) + channels*LPC_ORDER*sizeof(opus_val16) + 4*2*mode->nbEBands*sizeof(opus_val16); return size; } #ifdef CUSTOM_MODES CELTDecoder *opus_custom_decoder_create(const CELTMode *mode, int channels, int *error) { int ret; CELTDecoder *st = (CELTDecoder *)opus_alloc(opus_custom_decoder_get_size(mode, channels)); ret = opus_custom_decoder_init(st, mode, channels); if (ret != OPUS_OK) { opus_custom_decoder_destroy(st); st = NULL; } if (error) *error = ret; return st; } #endif /* CUSTOM_MODES */ int celt_decoder_init(CELTDecoder *st, opus_int32 sampling_rate, int channels) { int ret; ret = opus_custom_decoder_init(st, opus_custom_mode_create(48000, 960, NULL), channels); if (ret != OPUS_OK) return ret; st->downsample = resampling_factor(sampling_rate); if (st->downsample==0) return OPUS_BAD_ARG; else return OPUS_OK; } OPUS_CUSTOM_NOSTATIC int opus_custom_decoder_init(CELTDecoder *st, const CELTMode *mode, int channels) { if (channels < 0 || channels > 2) return OPUS_BAD_ARG; if (st==NULL) return OPUS_ALLOC_FAIL; OPUS_CLEAR((char*)st, opus_custom_decoder_get_size(mode, channels)); st->mode = mode; st->overlap = mode->overlap; st->stream_channels = st->channels = channels; st->downsample = 1; st->start = 0; st->end = st->mode->effEBands; st->signalling = 1; st->arch = opus_select_arch(); st->loss_count = 0; opus_custom_decoder_ctl(st, OPUS_RESET_STATE); return OPUS_OK; } #ifdef CUSTOM_MODES void opus_custom_decoder_destroy(CELTDecoder *st) { opus_free(st); } #endif /* CUSTOM_MODES */ #ifndef RESYNTH static #endif void deemphasis(celt_sig *in[], opus_val16 *pcm, int N, int C, int downsample, const opus_val16 *coef, celt_sig *mem, int accum) { int c; int Nd; int apply_downsampling=0; opus_val16 coef0; VARDECL(celt_sig, scratch); SAVE_STACK; #ifndef FIXED_POINT (void)accum; celt_assert(accum==0); #endif ALLOC(scratch, N, celt_sig); coef0 = coef[0]; Nd = N/downsample; c=0; do { int j; celt_sig * OPUS_RESTRICT x; opus_val16 * OPUS_RESTRICT y; celt_sig m = mem[c]; x =in[c]; y = pcm+c; #ifdef CUSTOM_MODES if (coef[1] != 0) { opus_val16 coef1 = coef[1]; opus_val16 coef3 = coef[3]; for (j=0;j1) { /* Shortcut for the standard (non-custom modes) case */ for (j=0;joverlap; nbEBands = mode->nbEBands; N = mode->shortMdctSize<shortMdctSize; shift = mode->maxLM; } else { B = 1; NB = mode->shortMdctSize<maxLM-LM; } if (CC==2&&C==1) { /* Copying a mono streams to two channels */ celt_sig *freq2; denormalise_bands(mode, X, freq, oldBandE, start, effEnd, M, downsample, silence); /* Store a temporary copy in the output buffer because the IMDCT destroys its input. */ freq2 = out_syn[1]+overlap/2; OPUS_COPY(freq2, freq, N); for (b=0;bmdct, &freq2[b], out_syn[0]+NB*b, mode->window, overlap, shift, B, arch); for (b=0;bmdct, &freq[b], out_syn[1]+NB*b, mode->window, overlap, shift, B, arch); } else if (CC==1&&C==2) { /* Downmixing a stereo stream to mono */ celt_sig *freq2; freq2 = out_syn[0]+overlap/2; denormalise_bands(mode, X, freq, oldBandE, start, effEnd, M, downsample, silence); /* Use the output buffer as temp array before downmixing. */ denormalise_bands(mode, X+N, freq2, oldBandE+nbEBands, start, effEnd, M, downsample, silence); for (i=0;imdct, &freq[b], out_syn[0]+NB*b, mode->window, overlap, shift, B, arch); } else { /* Normal case (mono or stereo) */ c=0; do { denormalise_bands(mode, X+c*N, freq, oldBandE+c*nbEBands, start, effEnd, M, downsample, silence); for (b=0;bmdct, &freq[b], out_syn[c]+NB*b, mode->window, overlap, shift, B, arch); } while (++cstorage*8; tell = ec_tell(dec); logp = isTransient ? 2 : 4; tf_select_rsv = LM>0 && tell+logp+1<=budget; budget -= tf_select_rsv; tf_changed = curr = 0; for (i=start;i>1, opus_val16 ); pitch_downsample(decode_mem, lp_pitch_buf, DECODE_BUFFER_SIZE, C, arch); pitch_search(lp_pitch_buf+(PLC_PITCH_LAG_MAX>>1), lp_pitch_buf, DECODE_BUFFER_SIZE-PLC_PITCH_LAG_MAX, PLC_PITCH_LAG_MAX-PLC_PITCH_LAG_MIN, &pitch_index, arch); pitch_index = PLC_PITCH_LAG_MAX-pitch_index; RESTORE_STACK; return pitch_index; } static void celt_decode_lost(CELTDecoder * OPUS_RESTRICT st, int N, int LM) { int c; int i; const int C = st->channels; celt_sig *decode_mem[2]; celt_sig *out_syn[2]; opus_val16 *lpc; opus_val16 *oldBandE, *oldLogE, *oldLogE2, *backgroundLogE; const OpusCustomMode *mode; int nbEBands; int overlap; int start; int loss_count; int noise_based; const opus_int16 *eBands; SAVE_STACK; mode = st->mode; nbEBands = mode->nbEBands; overlap = mode->overlap; eBands = mode->eBands; c=0; do { decode_mem[c] = st->_decode_mem + c*(DECODE_BUFFER_SIZE+overlap); out_syn[c] = decode_mem[c]+DECODE_BUFFER_SIZE-N; } while (++c_decode_mem+(DECODE_BUFFER_SIZE+overlap)*C); oldBandE = lpc+C*LPC_ORDER; oldLogE = oldBandE + 2*nbEBands; oldLogE2 = oldLogE + 2*nbEBands; backgroundLogE = oldLogE2 + 2*nbEBands; loss_count = st->loss_count; start = st->start; noise_based = loss_count >= 5 || start != 0; if (noise_based) { /* Noise-based PLC/CNG */ #ifdef NORM_ALIASING_HACK celt_norm *X; #else VARDECL(celt_norm, X); #endif opus_uint32 seed; int end; int effEnd; opus_val16 decay; end = st->end; effEnd = IMAX(start, IMIN(end, mode->effEBands)); #ifdef NORM_ALIASING_HACK /* This is an ugly hack that breaks aliasing rules and would be easily broken, but it saves almost 4kB of stack. */ X = (celt_norm*)(out_syn[C-1]+overlap/2); #else ALLOC(X, C*N, celt_norm); /**< Interleaved normalised MDCTs */ #endif /* Energy decay */ decay = loss_count==0 ? QCONST16(1.5f, DB_SHIFT) : QCONST16(.5f, DB_SHIFT); c=0; do { for (i=start;irng; for (c=0;c>20); } renormalise_vector(X+boffs, blen, Q15ONE, st->arch); } } st->rng = seed; c=0; do { OPUS_MOVE(decode_mem[c], decode_mem[c]+N, DECODE_BUFFER_SIZE-N+(overlap>>1)); } while (++cdownsample, 0, st->arch); } else { /* Pitch-based PLC */ const opus_val16 *window; opus_val16 fade = Q15ONE; int pitch_index; VARDECL(opus_val32, etmp); VARDECL(opus_val16, exc); if (loss_count == 0) { st->last_pitch_index = pitch_index = celt_plc_pitch_search(decode_mem, C, st->arch); } else { pitch_index = st->last_pitch_index; fade = QCONST16(.8f,15); } ALLOC(etmp, overlap, opus_val32); ALLOC(exc, MAX_PERIOD, opus_val16); window = mode->window; c=0; do { opus_val16 decay; opus_val16 attenuation; opus_val32 S1=0; celt_sig *buf; int extrapolation_offset; int extrapolation_len; int exc_length; int j; buf = decode_mem[c]; for (i=0;iarch); /* Add a noise floor of -40 dB. */ #ifdef FIXED_POINT ac[0] += SHR32(ac[0],13); #else ac[0] *= 1.0001f; #endif /* Use lag windowing to stabilize the Levinson-Durbin recursion. */ for (i=1;i<=LPC_ORDER;i++) { /*ac[i] *= exp(-.5*(2*M_PI*.002*i)*(2*M_PI*.002*i));*/ #ifdef FIXED_POINT ac[i] -= MULT16_32_Q15(2*i*i, ac[i]); #else ac[i] -= ac[i]*(0.008f*0.008f)*i*i; #endif } _celt_lpc(lpc+c*LPC_ORDER, ac, LPC_ORDER); } /* We want the excitation for 2 pitch periods in order to look for a decaying signal, but we can't get more than MAX_PERIOD. */ exc_length = IMIN(2*pitch_index, MAX_PERIOD); /* Initialize the LPC history with the samples just before the start of the region for which we're computing the excitation. */ { opus_val16 lpc_mem[LPC_ORDER]; for (i=0;iarch); } /* Check if the waveform is decaying, and if so how fast. We do this to avoid adding energy when concealing in a segment with decaying energy. */ { opus_val32 E1=1, E2=1; int decay_length; #ifdef FIXED_POINT int shift = IMAX(0,2*celt_zlog2(celt_maxabs16(&exc[MAX_PERIOD-exc_length], exc_length))-20); #endif decay_length = exc_length>>1; for (i=0;i= pitch_index) { j -= pitch_index; attenuation = MULT16_16_Q15(attenuation, decay); } buf[DECODE_BUFFER_SIZE-N+i] = SHL32(EXTEND32(MULT16_16_Q15(attenuation, exc[extrapolation_offset+j])), SIG_SHIFT); /* Compute the energy of the previously decoded signal whose excitation we're copying. */ tmp = ROUND16( buf[DECODE_BUFFER_SIZE-MAX_PERIOD-N+extrapolation_offset+j], SIG_SHIFT); S1 += SHR32(MULT16_16(tmp, tmp), 8); } { opus_val16 lpc_mem[LPC_ORDER]; /* Copy the last decoded samples (prior to the overlap region) to synthesis filter memory so we can have a continuous signal. */ for (i=0;iarch); } /* Check if the synthesis energy is higher than expected, which can happen with the signal changes during our window. If so, attenuate. */ { opus_val32 S2=0; for (i=0;i SHR32(S2,2))) #else /* The float test is written this way to catch NaNs in the output of the IIR filter at the same time. */ if (!(S1 > 0.2f*S2)) #endif { for (i=0;ipostfilter_period, st->postfilter_period, overlap, -st->postfilter_gain, -st->postfilter_gain, st->postfilter_tapset, st->postfilter_tapset, NULL, 0, st->arch); /* Simulate TDAC on the concealed audio so that it blends with the MDCT of the next frame. */ for (i=0;iloss_count = loss_count+1; RESTORE_STACK; } int celt_decode_with_ec(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, opus_val16 * OPUS_RESTRICT pcm, int frame_size, ec_dec *dec, int accum) { int c, i, N; int spread_decision; opus_int32 bits; ec_dec _dec; #ifdef NORM_ALIASING_HACK celt_norm *X; #else VARDECL(celt_norm, X); #endif VARDECL(int, fine_quant); VARDECL(int, pulses); VARDECL(int, cap); VARDECL(int, offsets); VARDECL(int, fine_priority); VARDECL(int, tf_res); VARDECL(unsigned char, collapse_masks); celt_sig *decode_mem[2]; celt_sig *out_syn[2]; opus_val16 *lpc; opus_val16 *oldBandE, *oldLogE, *oldLogE2, *backgroundLogE; int shortBlocks; int isTransient; int intra_ener; const int CC = st->channels; int LM, M; int start; int end; int effEnd; int codedBands; int alloc_trim; int postfilter_pitch; opus_val16 postfilter_gain; int intensity=0; int dual_stereo=0; opus_int32 total_bits; opus_int32 balance; opus_int32 tell; int dynalloc_logp; int postfilter_tapset; int anti_collapse_rsv; int anti_collapse_on=0; int silence; int C = st->stream_channels; const OpusCustomMode *mode; int nbEBands; int overlap; const opus_int16 *eBands; ALLOC_STACK; mode = st->mode; nbEBands = mode->nbEBands; overlap = mode->overlap; eBands = mode->eBands; start = st->start; end = st->end; frame_size *= st->downsample; lpc = (opus_val16*)(st->_decode_mem+(DECODE_BUFFER_SIZE+overlap)*CC); oldBandE = lpc+CC*LPC_ORDER; oldLogE = oldBandE + 2*nbEBands; oldLogE2 = oldLogE + 2*nbEBands; backgroundLogE = oldLogE2 + 2*nbEBands; #ifdef CUSTOM_MODES if (st->signalling && data!=NULL) { int data0=data[0]; /* Convert "standard mode" to Opus header */ if (mode->Fs==48000 && mode->shortMdctSize==120) { data0 = fromOpus(data0); if (data0<0) return OPUS_INVALID_PACKET; } st->end = end = IMAX(1, mode->effEBands-2*(data0>>5)); LM = (data0>>3)&0x3; C = 1 + ((data0>>2)&0x1); data++; len--; if (LM>mode->maxLM) return OPUS_INVALID_PACKET; if (frame_size < mode->shortMdctSize<shortMdctSize<maxLM;LM++) if (mode->shortMdctSize<mode->maxLM) return OPUS_BAD_ARG; } M=1<1275 || pcm==NULL) return OPUS_BAD_ARG; N = M*mode->shortMdctSize; c=0; do { decode_mem[c] = st->_decode_mem + c*(DECODE_BUFFER_SIZE+overlap); out_syn[c] = decode_mem[c]+DECODE_BUFFER_SIZE-N; } while (++c mode->effEBands) effEnd = mode->effEBands; if (data == NULL || len<=1) { celt_decode_lost(st, N, LM); deemphasis(out_syn, pcm, N, CC, st->downsample, mode->preemph, st->preemph_memD, accum); RESTORE_STACK; return frame_size/st->downsample; } if (dec == NULL) { ec_dec_init(&_dec,(unsigned char*)data,len); dec = &_dec; } if (C==1) { for (i=0;i= total_bits) silence = 1; else if (tell==1) silence = ec_dec_bit_logp(dec, 15); else silence = 0; if (silence) { /* Pretend we've read all the remaining bits */ tell = len*8; dec->nbits_total+=tell-ec_tell(dec); } postfilter_gain = 0; postfilter_pitch = 0; postfilter_tapset = 0; if (start==0 && tell+16 <= total_bits) { if(ec_dec_bit_logp(dec, 1)) { int qg, octave; octave = ec_dec_uint(dec, 6); postfilter_pitch = (16< 0 && tell+3 <= total_bits) { isTransient = ec_dec_bit_logp(dec, 3); tell = ec_tell(dec); } else isTransient = 0; if (isTransient) shortBlocks = M; else shortBlocks = 0; /* Decode the global flags (first symbols in the stream) */ intra_ener = tell+3<=total_bits ? ec_dec_bit_logp(dec, 3) : 0; /* Get band energies */ unquant_coarse_energy(mode, start, end, oldBandE, intra_ener, dec, C, LM); ALLOC(tf_res, nbEBands, int); tf_decode(start, end, isTransient, tf_res, LM, dec); tell = ec_tell(dec); spread_decision = SPREAD_NORMAL; if (tell+4 <= total_bits) spread_decision = ec_dec_icdf(dec, spread_icdf, 5); ALLOC(cap, nbEBands, int); init_caps(mode,cap,LM,C); ALLOC(offsets, nbEBands, int); dynalloc_logp = 6; total_bits<<=BITRES; tell = ec_tell_frac(dec); for (i=start;i0) dynalloc_logp = IMAX(2, dynalloc_logp-1); } ALLOC(fine_quant, nbEBands, int); alloc_trim = tell+(6<=2&&bits>=((LM+2)<rng, st->arch); if (anti_collapse_rsv > 0) { anti_collapse_on = ec_dec_bits(dec, 1); } unquant_energy_finalise(mode, start, end, oldBandE, fine_quant, fine_priority, len*8-ec_tell(dec), dec, C); if (anti_collapse_on) anti_collapse(mode, X, collapse_masks, LM, C, N, start, end, oldBandE, oldLogE, oldLogE2, pulses, st->rng, st->arch); if (silence) { for (i=0;idownsample, silence, st->arch); c=0; do { st->postfilter_period=IMAX(st->postfilter_period, COMBFILTER_MINPERIOD); st->postfilter_period_old=IMAX(st->postfilter_period_old, COMBFILTER_MINPERIOD); comb_filter(out_syn[c], out_syn[c], st->postfilter_period_old, st->postfilter_period, mode->shortMdctSize, st->postfilter_gain_old, st->postfilter_gain, st->postfilter_tapset_old, st->postfilter_tapset, mode->window, overlap, st->arch); if (LM!=0) comb_filter(out_syn[c]+mode->shortMdctSize, out_syn[c]+mode->shortMdctSize, st->postfilter_period, postfilter_pitch, N-mode->shortMdctSize, st->postfilter_gain, postfilter_gain, st->postfilter_tapset, postfilter_tapset, mode->window, overlap, st->arch); } while (++cpostfilter_period_old = st->postfilter_period; st->postfilter_gain_old = st->postfilter_gain; st->postfilter_tapset_old = st->postfilter_tapset; st->postfilter_period = postfilter_pitch; st->postfilter_gain = postfilter_gain; st->postfilter_tapset = postfilter_tapset; if (LM!=0) { st->postfilter_period_old = st->postfilter_period; st->postfilter_gain_old = st->postfilter_gain; st->postfilter_tapset_old = st->postfilter_tapset; } if (C==1) OPUS_COPY(&oldBandE[nbEBands], oldBandE, nbEBands); /* In case start or end were to change */ if (!isTransient) { opus_val16 max_background_increase; OPUS_COPY(oldLogE2, oldLogE, 2*nbEBands); OPUS_COPY(oldLogE, oldBandE, 2*nbEBands); /* In normal circumstances, we only allow the noise floor to increase by up to 2.4 dB/second, but when we're in DTX, we allow up to 6 dB increase for each update.*/ if (st->loss_count < 10) max_background_increase = M*QCONST16(0.001f,DB_SHIFT); else max_background_increase = QCONST16(1.f,DB_SHIFT); for (i=0;i<2*nbEBands;i++) backgroundLogE[i] = MIN16(backgroundLogE[i] + max_background_increase, oldBandE[i]); } else { for (i=0;i<2*nbEBands;i++) oldLogE[i] = MIN16(oldLogE[i], oldBandE[i]); } c=0; do { for (i=0;irng = dec->rng; deemphasis(out_syn, pcm, N, CC, st->downsample, mode->preemph, st->preemph_memD, accum); st->loss_count = 0; RESTORE_STACK; if (ec_tell(dec) > 8*len) return OPUS_INTERNAL_ERROR; if(ec_get_error(dec)) st->error = 1; return frame_size/st->downsample; } #ifdef CUSTOM_MODES #ifdef FIXED_POINT int opus_custom_decode(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, opus_int16 * OPUS_RESTRICT pcm, int frame_size) { return celt_decode_with_ec(st, data, len, pcm, frame_size, NULL, 0); } #ifndef DISABLE_FLOAT_API int opus_custom_decode_float(CELTDecoder * OPUS_RESTRICT st, const unsigned char *data, int len, float * OPUS_RESTRICT pcm, int frame_size) { int j, ret, C, N; VARDECL(opus_int16, out); ALLOC_STACK; if (pcm==NULL) return OPUS_BAD_ARG; C = st->channels; N = frame_size; ALLOC(out, C*N, opus_int16); ret=celt_decode_with_ec(st, data, len, out, frame_size, NULL, 0); if (ret>0) for (j=0;jchannels; N = frame_size; ALLOC(out, C*N, celt_sig); ret=celt_decode_with_ec(st, data, len, out, frame_size, NULL, 0); if (ret>0) for (j=0;j=st->mode->nbEBands) goto bad_arg; st->start = value; } break; case CELT_SET_END_BAND_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if (value<1 || value>st->mode->nbEBands) goto bad_arg; st->end = value; } break; case CELT_SET_CHANNELS_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if (value<1 || value>2) goto bad_arg; st->stream_channels = value; } break; case CELT_GET_AND_CLEAR_ERROR_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (value==NULL) goto bad_arg; *value=st->error; st->error = 0; } break; case OPUS_GET_LOOKAHEAD_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (value==NULL) goto bad_arg; *value = st->overlap/st->downsample; } break; case OPUS_RESET_STATE: { int i; opus_val16 *lpc, *oldBandE, *oldLogE, *oldLogE2; lpc = (opus_val16*)(st->_decode_mem+(DECODE_BUFFER_SIZE+st->overlap)*st->channels); oldBandE = lpc+st->channels*LPC_ORDER; oldLogE = oldBandE + 2*st->mode->nbEBands; oldLogE2 = oldLogE + 2*st->mode->nbEBands; OPUS_CLEAR((char*)&st->DECODER_RESET_START, opus_custom_decoder_get_size(st->mode, st->channels)- ((char*)&st->DECODER_RESET_START - (char*)st)); for (i=0;i<2*st->mode->nbEBands;i++) oldLogE[i]=oldLogE2[i]=-QCONST16(28.f,DB_SHIFT); } break; case OPUS_GET_PITCH_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (value==NULL) goto bad_arg; *value = st->postfilter_period; } break; case CELT_GET_MODE_REQUEST: { const CELTMode ** value = va_arg(ap, const CELTMode**); if (value==0) goto bad_arg; *value=st->mode; } break; case CELT_SET_SIGNALLING_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); st->signalling = value; } break; case OPUS_GET_FINAL_RANGE_REQUEST: { opus_uint32 * value = va_arg(ap, opus_uint32 *); if (value==0) goto bad_arg; *value=st->rng; } break; default: goto bad_request; } va_end(ap); return OPUS_OK; bad_arg: va_end(ap); return OPUS_BAD_ARG; bad_request: va_end(ap); return OPUS_UNIMPLEMENTED; } ================================================ FILE: deps/pjsip/third_party/opus/celt/celt_encoder.c ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2010 Xiph.Org Foundation Copyright (c) 2008 Gregory Maxwell Written by Jean-Marc Valin and Gregory Maxwell */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #define CELT_ENCODER_C #include "cpu_support.h" #include "os_support.h" #include "mdct.h" #include #include "celt.h" #include "pitch.h" #include "bands.h" #include "modes.h" #include "entcode.h" #include "quant_bands.h" #include "rate.h" #include "stack_alloc.h" #include "mathops.h" #include "float_cast.h" #include #include "celt_lpc.h" #include "vq.h" /** Encoder state @brief Encoder state */ struct OpusCustomEncoder { const OpusCustomMode *mode; /**< Mode used by the encoder */ int channels; int stream_channels; int force_intra; int clip; int disable_pf; int complexity; int upsample; int start, end; opus_int32 bitrate; int vbr; int signalling; int constrained_vbr; /* If zero, VBR can do whatever it likes with the rate */ int loss_rate; int lsb_depth; int variable_duration; int lfe; int arch; /* Everything beyond this point gets cleared on a reset */ #define ENCODER_RESET_START rng opus_uint32 rng; int spread_decision; opus_val32 delayedIntra; int tonal_average; int lastCodedBands; int hf_average; int tapset_decision; int prefilter_period; opus_val16 prefilter_gain; int prefilter_tapset; #ifdef RESYNTH int prefilter_period_old; opus_val16 prefilter_gain_old; int prefilter_tapset_old; #endif int consec_transient; AnalysisInfo analysis; opus_val32 preemph_memE[2]; opus_val32 preemph_memD[2]; /* VBR-related parameters */ opus_int32 vbr_reservoir; opus_int32 vbr_drift; opus_int32 vbr_offset; opus_int32 vbr_count; opus_val32 overlap_max; opus_val16 stereo_saving; int intensity; opus_val16 *energy_mask; opus_val16 spec_avg; #ifdef RESYNTH /* +MAX_PERIOD/2 to make space for overlap */ celt_sig syn_mem[2][2*MAX_PERIOD+MAX_PERIOD/2]; #endif celt_sig in_mem[1]; /* Size = channels*mode->overlap */ /* celt_sig prefilter_mem[], Size = channels*COMBFILTER_MAXPERIOD */ /* opus_val16 oldBandE[], Size = channels*mode->nbEBands */ /* opus_val16 oldLogE[], Size = channels*mode->nbEBands */ /* opus_val16 oldLogE2[], Size = channels*mode->nbEBands */ }; int celt_encoder_get_size(int channels) { CELTMode *mode = opus_custom_mode_create(48000, 960, NULL); return opus_custom_encoder_get_size(mode, channels); } OPUS_CUSTOM_NOSTATIC int opus_custom_encoder_get_size(const CELTMode *mode, int channels) { int size = sizeof(struct CELTEncoder) + (channels*mode->overlap-1)*sizeof(celt_sig) /* celt_sig in_mem[channels*mode->overlap]; */ + channels*COMBFILTER_MAXPERIOD*sizeof(celt_sig) /* celt_sig prefilter_mem[channels*COMBFILTER_MAXPERIOD]; */ + 3*channels*mode->nbEBands*sizeof(opus_val16); /* opus_val16 oldBandE[channels*mode->nbEBands]; */ /* opus_val16 oldLogE[channels*mode->nbEBands]; */ /* opus_val16 oldLogE2[channels*mode->nbEBands]; */ return size; } #ifdef CUSTOM_MODES CELTEncoder *opus_custom_encoder_create(const CELTMode *mode, int channels, int *error) { int ret; CELTEncoder *st = (CELTEncoder *)opus_alloc(opus_custom_encoder_get_size(mode, channels)); /* init will handle the NULL case */ ret = opus_custom_encoder_init(st, mode, channels); if (ret != OPUS_OK) { opus_custom_encoder_destroy(st); st = NULL; } if (error) *error = ret; return st; } #endif /* CUSTOM_MODES */ static int opus_custom_encoder_init_arch(CELTEncoder *st, const CELTMode *mode, int channels, int arch) { if (channels < 0 || channels > 2) return OPUS_BAD_ARG; if (st==NULL || mode==NULL) return OPUS_ALLOC_FAIL; OPUS_CLEAR((char*)st, opus_custom_encoder_get_size(mode, channels)); st->mode = mode; st->stream_channels = st->channels = channels; st->upsample = 1; st->start = 0; st->end = st->mode->effEBands; st->signalling = 1; st->arch = arch; st->constrained_vbr = 1; st->clip = 1; st->bitrate = OPUS_BITRATE_MAX; st->vbr = 0; st->force_intra = 0; st->complexity = 5; st->lsb_depth=24; opus_custom_encoder_ctl(st, OPUS_RESET_STATE); return OPUS_OK; } #ifdef CUSTOM_MODES int opus_custom_encoder_init(CELTEncoder *st, const CELTMode *mode, int channels) { return opus_custom_encoder_init_arch(st, mode, channels, opus_select_arch()); } #endif int celt_encoder_init(CELTEncoder *st, opus_int32 sampling_rate, int channels, int arch) { int ret; ret = opus_custom_encoder_init_arch(st, opus_custom_mode_create(48000, 960, NULL), channels, arch); if (ret != OPUS_OK) return ret; st->upsample = resampling_factor(sampling_rate); return OPUS_OK; } #ifdef CUSTOM_MODES void opus_custom_encoder_destroy(CELTEncoder *st) { opus_free(st); } #endif /* CUSTOM_MODES */ static int transient_analysis(const opus_val32 * OPUS_RESTRICT in, int len, int C, opus_val16 *tf_estimate, int *tf_chan) { int i; VARDECL(opus_val16, tmp); opus_val32 mem0,mem1; int is_transient = 0; opus_int32 mask_metric = 0; int c; opus_val16 tf_max; int len2; /* Table of 6*64/x, trained on real data to minimize the average error */ static const unsigned char inv_table[128] = { 255,255,156,110, 86, 70, 59, 51, 45, 40, 37, 33, 31, 28, 26, 25, 23, 22, 21, 20, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12, 12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, }; SAVE_STACK; ALLOC(tmp, len, opus_val16); len2=len/2; for (c=0;c=0;i--) { #ifdef FIXED_POINT /* FIXME: Use PSHR16() instead */ tmp[i] = mem0 + PSHR32(tmp[i]-mem0,3); #else tmp[i] = mem0 + MULT16_16_P15(QCONST16(0.125f,15),tmp[i]-mem0); #endif mem0 = tmp[i]; maxE = MAX16(maxE, mem0); } /*for (i=0;i>1))); #else mean = celt_sqrt(mean * maxE*.5*len2); #endif /* Inverse of the mean energy in Q15+6 */ norm = SHL32(EXTEND32(len2),6+14)/ADD32(EPSILON,SHR32(mean,1)); /* Compute harmonic mean discarding the unreliable boundaries The data is smooth, so we only take 1/4th of the samples */ unmask=0; for (i=12;imask_metric) { *tf_chan = c; mask_metric = unmask; } } is_transient = mask_metric>200; /* Arbitrary metric for VBR boost */ tf_max = MAX16(0,celt_sqrt(27*mask_metric)-42); /* *tf_estimate = 1 + MIN16(1, sqrt(MAX16(0, tf_max-30))/20); */ *tf_estimate = celt_sqrt(MAX32(0, SHL32(MULT16_16(QCONST16(0.0069,14),MIN16(163,tf_max)),14)-QCONST32(0.139,28))); /*printf("%d %f\n", tf_max, mask_metric);*/ RESTORE_STACK; #ifdef FUZZING is_transient = rand()&0x1; #endif /*printf("%d %f %d\n", is_transient, (float)*tf_estimate, tf_max);*/ return is_transient; } /* Looks for sudden increases of energy to decide whether we need to patch the transient decision */ static int patch_transient_decision(opus_val16 *newE, opus_val16 *oldE, int nbEBands, int start, int end, int C) { int i, c; opus_val32 mean_diff=0; opus_val16 spread_old[26]; /* Apply an aggressive (-6 dB/Bark) spreading function to the old frame to avoid false detection caused by irrelevant bands */ if (C==1) { spread_old[start] = oldE[start]; for (i=start+1;i=start;i--) spread_old[i] = MAX16(spread_old[i], spread_old[i+1]-QCONST16(1.0f, DB_SHIFT)); /* Compute mean increase */ c=0; do { for (i=IMAX(2,start);i QCONST16(1.f, DB_SHIFT); } /** Apply window and compute the MDCT for all sub-frames and all channels in a frame */ static void compute_mdcts(const CELTMode *mode, int shortBlocks, celt_sig * OPUS_RESTRICT in, celt_sig * OPUS_RESTRICT out, int C, int CC, int LM, int upsample, int arch) { const int overlap = mode->overlap; int N; int B; int shift; int i, b, c; if (shortBlocks) { B = shortBlocks; N = mode->shortMdctSize; shift = mode->maxLM; } else { B = 1; N = mode->shortMdctSize<maxLM-LM; } c=0; do { for (b=0;bmdct, in+c*(B*N+overlap)+b*N, &out[b+c*N*B], mode->window, overlap, shift, B, arch); } } while (++ceBands[len]-m->eBands[len-1])<eBands[len]-m->eBands[len-1])<eBands[i+1]-m->eBands[i])<eBands[i+1]-m->eBands[i])==1; OPUS_COPY(tmp, &X[tf_chan*N0 + (m->eBands[i]<eBands[i]<>LM, 1<>k, 1<=0;i--) { if (tf_res[i+1] == 1) tf_res[i] = path1[i+1]; else tf_res[i] = path0[i+1]; } /*printf("%d %f\n", *tf_sum, tf_estimate);*/ RESTORE_STACK; #ifdef FUZZING tf_select = rand()&0x1; tf_res[0] = rand()&0x1; for (i=1;istorage*8; tell = ec_tell(enc); logp = isTransient ? 2 : 4; /* Reserve space to code the tf_select decision. */ tf_select_rsv = LM>0 && tell+logp+1 <= budget; budget -= tf_select_rsv; curr = tf_changed = 0; for (i=start;ieBands[i]<eBands[i]<eBands[i+1]-m->eBands[i])<eBands[i]<eBands[i]<eBands[i+1]-m->eBands[i])<nbEBands]*(opus_int32)(2+2*i-end); } } while (++cvalid) { trim -= MAX16(-QCONST16(2.f, 8), MIN16(QCONST16(2.f, 8), (opus_val16)(QCONST16(2.f, 8)*(analysis->tonality_slope+.05f)))); } #else (void)analysis; #endif #ifdef FIXED_POINT trim_index = PSHR32(trim, 8); #else trim_index = (int)floor(.5f+trim); #endif trim_index = IMAX(0, IMIN(10, trim_index)); /*printf("%d\n", trim_index);*/ #ifdef FUZZING trim_index = rand()%11; #endif return trim_index; } static int stereo_analysis(const CELTMode *m, const celt_norm *X, int LM, int N0) { int i; int thetas; opus_val32 sumLR = EPSILON, sumMS = EPSILON; /* Use the L1 norm to model the entropy of the L/R signal vs the M/S signal */ for (i=0;i<13;i++) { int j; for (j=m->eBands[i]<eBands[i+1]<eBands[13]<<(LM+1))+thetas, sumMS) > MULT16_32_Q15(m->eBands[13]<<(LM+1), sumLR); } #define MSWAP(a,b) do {opus_val16 tmp = a;a=b;b=tmp;} while(0) static opus_val16 median_of_5(const opus_val16 *x) { opus_val16 t0, t1, t2, t3, t4; t2 = x[2]; if (x[0] > x[1]) { t0 = x[1]; t1 = x[0]; } else { t0 = x[0]; t1 = x[1]; } if (x[3] > x[4]) { t3 = x[4]; t4 = x[3]; } else { t3 = x[3]; t4 = x[4]; } if (t0 > t3) { MSWAP(t0, t3); MSWAP(t1, t4); } if (t2 > t1) { if (t1 < t3) return MIN16(t2, t3); else return MIN16(t4, t1); } else { if (t2 < t3) return MIN16(t1, t3); else return MIN16(t2, t4); } } static opus_val16 median_of_3(const opus_val16 *x) { opus_val16 t0, t1, t2; if (x[0] > x[1]) { t0 = x[1]; t1 = x[0]; } else { t0 = x[0]; t1 = x[1]; } t2 = x[2]; if (t1 < t2) return t1; else if (t0 < t2) return t2; else return t0; } static opus_val16 dynalloc_analysis(const opus_val16 *bandLogE, const opus_val16 *bandLogE2, int nbEBands, int start, int end, int C, int *offsets, int lsb_depth, const opus_int16 *logN, int isTransient, int vbr, int constrained_vbr, const opus_int16 *eBands, int LM, int effectiveBytes, opus_int32 *tot_boost_, int lfe, opus_val16 *surround_dynalloc) { int i, c; opus_int32 tot_boost=0; opus_val16 maxDepth; VARDECL(opus_val16, follower); VARDECL(opus_val16, noise_floor); SAVE_STACK; ALLOC(follower, C*nbEBands, opus_val16); ALLOC(noise_floor, C*nbEBands, opus_val16); OPUS_CLEAR(offsets, nbEBands); /* Dynamic allocation code */ maxDepth=-QCONST16(31.9f, DB_SHIFT); for (i=0;i 50 && LM>=1 && !lfe) { int last=0; c=0;do { opus_val16 offset; opus_val16 tmp; opus_val16 *f; f = &follower[c*nbEBands]; f[0] = bandLogE2[c*nbEBands]; for (i=1;i bandLogE2[c*nbEBands+i-1]+QCONST16(.5f,DB_SHIFT)) last=i; f[i] = MIN16(f[i-1]+QCONST16(1.5f,DB_SHIFT), bandLogE2[c*nbEBands+i]); } for (i=last-1;i>=0;i--) f[i] = MIN16(f[i], MIN16(f[i+1]+QCONST16(2.f,DB_SHIFT), bandLogE2[c*nbEBands+i])); /* Combine with a median filter to avoid dynalloc triggering unnecessarily. The "offset" value controls how conservative we are -- a higher offset reduces the impact of the median filter and makes dynalloc use more bits. */ offset = QCONST16(1.f, DB_SHIFT); for (i=2;i=12) follower[i] = HALF16(follower[i]); follower[i] = MIN16(follower[i], QCONST16(4, DB_SHIFT)); width = C*(eBands[i+1]-eBands[i])< 48) { boost = (int)SHR32(EXTEND32(follower[i])*8,DB_SHIFT); boost_bits = (boost*width<>BITRES>>3 > effectiveBytes/4) { opus_int32 cap = ((effectiveBytes/4)<mode; overlap = mode->overlap; ALLOC(_pre, CC*(N+COMBFILTER_MAXPERIOD), celt_sig); pre[0] = _pre; pre[1] = _pre + (N+COMBFILTER_MAXPERIOD); c=0; do { OPUS_COPY(pre[c], prefilter_mem+c*COMBFILTER_MAXPERIOD, COMBFILTER_MAXPERIOD); OPUS_COPY(pre[c]+COMBFILTER_MAXPERIOD, in+c*(N+overlap)+overlap, N); } while (++c>1, opus_val16); pitch_downsample(pre, pitch_buf, COMBFILTER_MAXPERIOD+N, CC, st->arch); /* Don't search for the fir last 1.5 octave of the range because there's too many false-positives due to short-term correlation */ pitch_search(pitch_buf+(COMBFILTER_MAXPERIOD>>1), pitch_buf, N, COMBFILTER_MAXPERIOD-3*COMBFILTER_MINPERIOD, &pitch_index, st->arch); pitch_index = COMBFILTER_MAXPERIOD-pitch_index; gain1 = remove_doubling(pitch_buf, COMBFILTER_MAXPERIOD, COMBFILTER_MINPERIOD, N, &pitch_index, st->prefilter_period, st->prefilter_gain, st->arch); if (pitch_index > COMBFILTER_MAXPERIOD-2) pitch_index = COMBFILTER_MAXPERIOD-2; gain1 = MULT16_16_Q15(QCONST16(.7f,15),gain1); /*printf("%d %d %f %f\n", pitch_change, pitch_index, gain1, st->analysis.tonality);*/ if (st->loss_rate>2) gain1 = HALF32(gain1); if (st->loss_rate>4) gain1 = HALF32(gain1); if (st->loss_rate>8) gain1 = 0; } else { gain1 = 0; pitch_index = COMBFILTER_MINPERIOD; } /* Gain threshold for enabling the prefilter/postfilter */ pf_threshold = QCONST16(.2f,15); /* Adjusting the threshold based on rate and continuity */ if (abs(pitch_index-st->prefilter_period)*10>pitch_index) pf_threshold += QCONST16(.2f,15); if (nbAvailableBytes<25) pf_threshold += QCONST16(.1f,15); if (nbAvailableBytes<35) pf_threshold += QCONST16(.1f,15); if (st->prefilter_gain > QCONST16(.4f,15)) pf_threshold -= QCONST16(.1f,15); if (st->prefilter_gain > QCONST16(.55f,15)) pf_threshold -= QCONST16(.1f,15); /* Hard threshold at 0.2 */ pf_threshold = MAX16(pf_threshold, QCONST16(.2f,15)); if (gain1prefilter_gain)prefilter_gain; #ifdef FIXED_POINT qg = ((gain1+1536)>>10)/3-1; #else qg = (int)floor(.5f+gain1*32/3)-1; #endif qg = IMAX(0, IMIN(7, qg)); gain1 = QCONST16(0.09375f,15)*(qg+1); pf_on = 1; } /*printf("%d %f\n", pitch_index, gain1);*/ c=0; do { int offset = mode->shortMdctSize-overlap; st->prefilter_period=IMAX(st->prefilter_period, COMBFILTER_MINPERIOD); OPUS_COPY(in+c*(N+overlap), st->in_mem+c*(overlap), overlap); if (offset) comb_filter(in+c*(N+overlap)+overlap, pre[c]+COMBFILTER_MAXPERIOD, st->prefilter_period, st->prefilter_period, offset, -st->prefilter_gain, -st->prefilter_gain, st->prefilter_tapset, st->prefilter_tapset, NULL, 0, st->arch); comb_filter(in+c*(N+overlap)+overlap+offset, pre[c]+COMBFILTER_MAXPERIOD+offset, st->prefilter_period, pitch_index, N-offset, -st->prefilter_gain, -gain1, st->prefilter_tapset, prefilter_tapset, mode->window, overlap, st->arch); OPUS_COPY(st->in_mem+c*(overlap), in+c*(N+overlap)+N, overlap); if (N>COMBFILTER_MAXPERIOD) { OPUS_MOVE(prefilter_mem+c*COMBFILTER_MAXPERIOD, pre[c]+N, COMBFILTER_MAXPERIOD); } else { OPUS_MOVE(prefilter_mem+c*COMBFILTER_MAXPERIOD, prefilter_mem+c*COMBFILTER_MAXPERIOD+N, COMBFILTER_MAXPERIOD-N); OPUS_MOVE(prefilter_mem+c*COMBFILTER_MAXPERIOD+COMBFILTER_MAXPERIOD-N, pre[c]+COMBFILTER_MAXPERIOD, N); } } while (++cnbEBands; eBands = mode->eBands; coded_bands = lastCodedBands ? lastCodedBands : nbEBands; coded_bins = eBands[coded_bands]<analysis.activity, st->analysis.tonality, tf_estimate, st->stereo_saving, tot_boost, coded_bands);*/ #ifndef DISABLE_FLOAT_API if (analysis->valid && analysis->activity<.4) target -= (opus_int32)((coded_bins<activity)); #endif /* Stereo savings */ if (C==2) { int coded_stereo_bands; int coded_stereo_dof; opus_val16 max_frac; coded_stereo_bands = IMIN(intensity, coded_bands); coded_stereo_dof = (eBands[coded_stereo_bands]<valid && !lfe) { opus_int32 tonal_target; float tonal; /* Tonality boost (compensating for the average). */ tonal = MAX16(0.f,analysis->tonality-.15f)-0.09f; tonal_target = target + (opus_int32)((coded_bins<tonality, tonal);*/ target = tonal_target; } #else (void)analysis; (void)pitch_change; #endif if (has_surround_mask&&!lfe) { opus_int32 surround_target = target + (opus_int32)SHR32(MULT16_16(surround_masking,coded_bins<end, st->intensity, surround_target, target, st->bitrate);*/ target = IMAX(target/4, surround_target); } { opus_int32 floor_depth; int bins; bins = eBands[nbEBands-2]<>2); target = IMIN(target, floor_depth); /*printf("%f %d\n", maxDepth, floor_depth);*/ } if ((!has_surround_mask||lfe) && (constrained_vbr || bitrate<64000)) { opus_val16 rate_factor; #ifdef FIXED_POINT rate_factor = MAX16(0,(bitrate-32000)); #else rate_factor = MAX16(0,(1.f/32768)*(bitrate-32000)); #endif if (constrained_vbr) rate_factor = MIN16(rate_factor, QCONST16(0.67f, 15)); target = base_target + (opus_int32)MULT16_32_Q15(rate_factor, target-base_target); } if (!has_surround_mask && tf_estimate < QCONST16(.2f, 14)) { opus_val16 amount; opus_val16 tvbr_factor; amount = MULT16_16_Q15(QCONST16(.0000031f, 30), IMAX(0, IMIN(32000, 96000-bitrate))); tvbr_factor = SHR32(MULT16_16(temporal_vbr, amount), DB_SHIFT); target += (opus_int32)MULT16_32_Q15(tvbr_factor, target); } /* Don't allow more than doubling the rate */ target = IMIN(2*base_target, target); return target; } int celt_encode_with_ec(CELTEncoder * OPUS_RESTRICT st, const opus_val16 * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes, ec_enc *enc) { int i, c, N; opus_int32 bits; ec_enc _enc; VARDECL(celt_sig, in); VARDECL(celt_sig, freq); VARDECL(celt_norm, X); VARDECL(celt_ener, bandE); VARDECL(opus_val16, bandLogE); VARDECL(opus_val16, bandLogE2); VARDECL(int, fine_quant); VARDECL(opus_val16, error); VARDECL(int, pulses); VARDECL(int, cap); VARDECL(int, offsets); VARDECL(int, fine_priority); VARDECL(int, tf_res); VARDECL(unsigned char, collapse_masks); celt_sig *prefilter_mem; opus_val16 *oldBandE, *oldLogE, *oldLogE2; int shortBlocks=0; int isTransient=0; const int CC = st->channels; const int C = st->stream_channels; int LM, M; int tf_select; int nbFilledBytes, nbAvailableBytes; int start; int end; int effEnd; int codedBands; int tf_sum; int alloc_trim; int pitch_index=COMBFILTER_MINPERIOD; opus_val16 gain1 = 0; int dual_stereo=0; int effectiveBytes; int dynalloc_logp; opus_int32 vbr_rate; opus_int32 total_bits; opus_int32 total_boost; opus_int32 balance; opus_int32 tell; int prefilter_tapset=0; int pf_on; int anti_collapse_rsv; int anti_collapse_on=0; int silence=0; int tf_chan = 0; opus_val16 tf_estimate; int pitch_change=0; opus_int32 tot_boost; opus_val32 sample_max; opus_val16 maxDepth; const OpusCustomMode *mode; int nbEBands; int overlap; const opus_int16 *eBands; int secondMdct; int signalBandwidth; int transient_got_disabled=0; opus_val16 surround_masking=0; opus_val16 temporal_vbr=0; opus_val16 surround_trim = 0; opus_int32 equiv_rate = 510000; VARDECL(opus_val16, surround_dynalloc); ALLOC_STACK; mode = st->mode; nbEBands = mode->nbEBands; overlap = mode->overlap; eBands = mode->eBands; start = st->start; end = st->end; tf_estimate = 0; if (nbCompressedBytes<2 || pcm==NULL) { RESTORE_STACK; return OPUS_BAD_ARG; } frame_size *= st->upsample; for (LM=0;LM<=mode->maxLM;LM++) if (mode->shortMdctSize<mode->maxLM) { RESTORE_STACK; return OPUS_BAD_ARG; } M=1<shortMdctSize; prefilter_mem = st->in_mem+CC*(overlap); oldBandE = (opus_val16*)(st->in_mem+CC*(overlap+COMBFILTER_MAXPERIOD)); oldLogE = oldBandE + CC*nbEBands; oldLogE2 = oldLogE + CC*nbEBands; if (enc==NULL) { tell=1; nbFilledBytes=0; } else { tell=ec_tell(enc); nbFilledBytes=(tell+4)>>3; } #ifdef CUSTOM_MODES if (st->signalling && enc==NULL) { int tmp = (mode->effEBands-end)>>1; end = st->end = IMAX(1, mode->effEBands-tmp); compressed[0] = tmp<<5; compressed[0] |= LM<<3; compressed[0] |= (C==2)<<2; /* Convert "standard mode" to Opus header */ if (mode->Fs==48000 && mode->shortMdctSize==120) { int c0 = toOpus(compressed[0]); if (c0<0) { RESTORE_STACK; return OPUS_BAD_ARG; } compressed[0] = c0; } compressed++; nbCompressedBytes--; } #else celt_assert(st->signalling==0); #endif /* Can't produce more than 1275 output bytes */ nbCompressedBytes = IMIN(nbCompressedBytes,1275); nbAvailableBytes = nbCompressedBytes - nbFilledBytes; if (st->vbr && st->bitrate!=OPUS_BITRATE_MAX) { opus_int32 den=mode->Fs>>BITRES; vbr_rate=(st->bitrate*frame_size+(den>>1))/den; #ifdef CUSTOM_MODES if (st->signalling) vbr_rate -= 8<>(3+BITRES); } else { opus_int32 tmp; vbr_rate = 0; tmp = st->bitrate*frame_size; if (tell>1) tmp += tell; if (st->bitrate!=OPUS_BITRATE_MAX) nbCompressedBytes = IMAX(2, IMIN(nbCompressedBytes, (tmp+4*mode->Fs)/(8*mode->Fs)-!!st->signalling)); effectiveBytes = nbCompressedBytes; } if (st->bitrate != OPUS_BITRATE_MAX) equiv_rate = st->bitrate - (40*C+20)*((400>>LM) - 50); if (enc==NULL) { ec_enc_init(&_enc, compressed, nbCompressedBytes); enc = &_enc; } if (vbr_rate>0) { /* Computes the max bit-rate allowed in VBR mode to avoid violating the target rate and buffering. We must do this up front so that bust-prevention logic triggers correctly if we don't have enough bits. */ if (st->constrained_vbr) { opus_int32 vbr_bound; opus_int32 max_allowed; /* We could use any multiple of vbr_rate as bound (depending on the delay). This is clamped to ensure we use at least two bytes if the encoder was entirely empty, but to allow 0 in hybrid mode. */ vbr_bound = vbr_rate; max_allowed = IMIN(IMAX(tell==1?2:0, (vbr_rate+vbr_bound-st->vbr_reservoir)>>(BITRES+3)), nbAvailableBytes); if(max_allowed < nbAvailableBytes) { nbCompressedBytes = nbFilledBytes+max_allowed; nbAvailableBytes = max_allowed; ec_enc_shrink(enc, nbCompressedBytes); } } } total_bits = nbCompressedBytes*8; effEnd = end; if (effEnd > mode->effEBands) effEnd = mode->effEBands; ALLOC(in, CC*(N+overlap), celt_sig); sample_max=MAX32(st->overlap_max, celt_maxabs16(pcm, C*(N-overlap)/st->upsample)); st->overlap_max=celt_maxabs16(pcm+C*(N-overlap)/st->upsample, C*overlap/st->upsample); sample_max=MAX32(sample_max, st->overlap_max); #ifdef FIXED_POINT silence = (sample_max==0); #else silence = (sample_max <= (opus_val16)1/(1<lsb_depth)); #endif #ifdef FUZZING if ((rand()&0x3F)==0) silence = 1; #endif if (tell==1) ec_enc_bit_logp(enc, silence, 15); else silence=0; if (silence) { /*In VBR mode there is no need to send more than the minimum. */ if (vbr_rate>0) { effectiveBytes=nbCompressedBytes=IMIN(nbCompressedBytes, nbFilledBytes+2); total_bits=nbCompressedBytes*8; nbAvailableBytes=2; ec_enc_shrink(enc, nbCompressedBytes); } /* Pretend we've filled all the remaining bits with zeros (that's what the initialiser did anyway) */ tell = nbCompressedBytes*8; enc->nbits_total+=tell-ec_tell(enc); } c=0; do { int need_clip=0; #ifndef FIXED_POINT need_clip = st->clip && sample_max>65536.f; #endif celt_preemphasis(pcm+c, in+c*(N+overlap)+overlap, N, CC, st->upsample, mode->preemph, st->preemph_memE+c, need_clip); } while (++clfe&&nbAvailableBytes>3) || nbAvailableBytes>12*C) && start==0 && !silence && !st->disable_pf && st->complexity >= 5 && !(st->consec_transient && LM!=3 && st->variable_duration==OPUS_FRAMESIZE_VARIABLE); prefilter_tapset = st->tapset_decision; pf_on = run_prefilter(st, in, prefilter_mem, CC, N, prefilter_tapset, &pitch_index, &gain1, &qg, enabled, nbAvailableBytes); if ((gain1 > QCONST16(.4f,15) || st->prefilter_gain > QCONST16(.4f,15)) && (!st->analysis.valid || st->analysis.tonality > .3) && (pitch_index > 1.26*st->prefilter_period || pitch_index < .79*st->prefilter_period)) pitch_change = 1; if (pf_on==0) { if(start==0 && tell+16<=total_bits) ec_enc_bit_logp(enc, 0, 1); } else { /*This block is not gated by a total bits check only because of the nbAvailableBytes check above.*/ int octave; ec_enc_bit_logp(enc, 1, 1); pitch_index += 1; octave = EC_ILOG(pitch_index)-5; ec_enc_uint(enc, octave, 6); ec_enc_bits(enc, pitch_index-(16<complexity >= 1 && !st->lfe) { isTransient = transient_analysis(in, N+overlap, CC, &tf_estimate, &tf_chan); } if (LM>0 && ec_tell(enc)+3<=total_bits) { if (isTransient) shortBlocks = M; } else { isTransient = 0; transient_got_disabled=1; } ALLOC(freq, CC*N, celt_sig); /**< Interleaved signal MDCTs */ ALLOC(bandE,nbEBands*CC, celt_ener); ALLOC(bandLogE,nbEBands*CC, opus_val16); secondMdct = shortBlocks && st->complexity>=8; ALLOC(bandLogE2, C*nbEBands, opus_val16); if (secondMdct) { compute_mdcts(mode, 0, in, freq, C, CC, LM, st->upsample, st->arch); compute_band_energies(mode, freq, bandE, effEnd, C, LM); amp2Log2(mode, effEnd, end, bandE, bandLogE2, C); for (i=0;iupsample, st->arch); if (CC==2&&C==1) tf_chan = 0; compute_band_energies(mode, freq, bandE, effEnd, C, LM); if (st->lfe) { for (i=2;ienergy_mask&&!st->lfe) { int mask_end; int midband; int count_dynalloc; opus_val32 mask_avg=0; opus_val32 diff=0; int count=0; mask_end = IMAX(2,st->lastCodedBands); for (c=0;cenergy_mask[nbEBands*c+i], QCONST16(.25f, DB_SHIFT)), -QCONST16(2.0f, DB_SHIFT)); if (mask > 0) mask = HALF16(mask); mask_avg += MULT16_16(mask, eBands[i+1]-eBands[i]); count += eBands[i+1]-eBands[i]; diff += MULT16_16(mask, 1+2*i-mask_end); } } celt_assert(count>0); mask_avg = DIV32_16(mask_avg,count); mask_avg += QCONST16(.2f, DB_SHIFT); diff = diff*6/(C*(mask_end-1)*(mask_end+1)*mask_end); /* Again, being conservative */ diff = HALF32(diff); diff = MAX32(MIN32(diff, QCONST32(.031f, DB_SHIFT)), -QCONST32(.031f, DB_SHIFT)); /* Find the band that's in the middle of the coded spectrum */ for (midband=0;eBands[midband+1] < eBands[mask_end]/2;midband++); count_dynalloc=0; for(i=0;ienergy_mask[i], st->energy_mask[nbEBands+i]); else unmask = st->energy_mask[i]; unmask = MIN16(unmask, QCONST16(.0f, DB_SHIFT)); unmask -= lin; if (unmask > QCONST16(.25f, DB_SHIFT)) { surround_dynalloc[i] = unmask - QCONST16(.25f, DB_SHIFT); count_dynalloc++; } } if (count_dynalloc>=3) { /* If we need dynalloc in many bands, it's probably because our initial masking rate was too low. */ mask_avg += QCONST16(.25f, DB_SHIFT); if (mask_avg>0) { /* Something went really wrong in the original calculations, disabling masking. */ mask_avg = 0; diff = 0; OPUS_CLEAR(surround_dynalloc, mask_end); } else { for(i=0;ilfe) { opus_val16 follow=-QCONST16(10.0f,DB_SHIFT); opus_val32 frame_avg=0; opus_val16 offset = shortBlocks?HALF16(SHL16(LM, DB_SHIFT)):0; for(i=start;ispec_avg); temporal_vbr = MIN16(QCONST16(3.f, DB_SHIFT), MAX16(-QCONST16(1.5f, DB_SHIFT), temporal_vbr)); st->spec_avg += MULT16_16_Q15(QCONST16(.02f, 15), temporal_vbr); } /*for (i=0;i<21;i++) printf("%f ", bandLogE[i]); printf("\n");*/ if (!secondMdct) { OPUS_COPY(bandLogE2, bandLogE, C*nbEBands); } /* Last chance to catch any transient we might have missed in the time-domain analysis */ if (LM>0 && ec_tell(enc)+3<=total_bits && !isTransient && st->complexity>=5 && !st->lfe) { if (patch_transient_decision(bandLogE, oldBandE, nbEBands, start, end, C)) { isTransient = 1; shortBlocks = M; compute_mdcts(mode, shortBlocks, in, freq, C, CC, LM, st->upsample, st->arch); compute_band_energies(mode, freq, bandE, effEnd, C, LM); amp2Log2(mode, effEnd, end, bandE, bandLogE, C); /* Compensate for the scaling of short vs long mdcts */ for (i=0;i0 && ec_tell(enc)+3<=total_bits) ec_enc_bit_logp(enc, isTransient, 3); ALLOC(X, C*N, celt_norm); /**< Interleaved normalised MDCTs */ /* Band normalisation */ normalise_bands(mode, freq, X, bandE, effEnd, C, M); ALLOC(tf_res, nbEBands, int); /* Disable variable tf resolution for hybrid and at very low bitrate */ if (effectiveBytes>=15*C && start==0 && st->complexity>=2 && !st->lfe) { int lambda; if (effectiveBytes<40) lambda = 12; else if (effectiveBytes<60) lambda = 6; else if (effectiveBytes<100) lambda = 4; else lambda = 3; lambda*=2; tf_select = tf_analysis(mode, effEnd, isTransient, tf_res, lambda, X, N, LM, &tf_sum, tf_estimate, tf_chan); for (i=effEnd;iforce_intra, &st->delayedIntra, st->complexity >= 4, st->loss_rate, st->lfe); tf_encode(start, end, isTransient, tf_res, LM, tf_select, enc); if (ec_tell(enc)+4<=total_bits) { if (st->lfe) { st->tapset_decision = 0; st->spread_decision = SPREAD_NORMAL; } else if (shortBlocks || st->complexity < 3 || nbAvailableBytes < 10*C || start != 0) { if (st->complexity == 0) st->spread_decision = SPREAD_NONE; else st->spread_decision = SPREAD_NORMAL; } else { /* Disable new spreading+tapset estimator until we can show it works better than the old one. So far it seems like spreading_decision() works best. */ #if 0 if (st->analysis.valid) { static const opus_val16 spread_thresholds[3] = {-QCONST16(.6f, 15), -QCONST16(.2f, 15), -QCONST16(.07f, 15)}; static const opus_val16 spread_histeresis[3] = {QCONST16(.15f, 15), QCONST16(.07f, 15), QCONST16(.02f, 15)}; static const opus_val16 tapset_thresholds[2] = {QCONST16(.0f, 15), QCONST16(.15f, 15)}; static const opus_val16 tapset_histeresis[2] = {QCONST16(.1f, 15), QCONST16(.05f, 15)}; st->spread_decision = hysteresis_decision(-st->analysis.tonality, spread_thresholds, spread_histeresis, 3, st->spread_decision); st->tapset_decision = hysteresis_decision(st->analysis.tonality_slope, tapset_thresholds, tapset_histeresis, 2, st->tapset_decision); } else #endif { st->spread_decision = spreading_decision(mode, X, &st->tonal_average, st->spread_decision, &st->hf_average, &st->tapset_decision, pf_on&&!shortBlocks, effEnd, C, M); } /*printf("%d %d\n", st->tapset_decision, st->spread_decision);*/ /*printf("%f %d %f %d\n\n", st->analysis.tonality, st->spread_decision, st->analysis.tonality_slope, st->tapset_decision);*/ } ec_enc_icdf(enc, st->spread_decision, spread_icdf, 5); } ALLOC(offsets, nbEBands, int); maxDepth = dynalloc_analysis(bandLogE, bandLogE2, nbEBands, start, end, C, offsets, st->lsb_depth, mode->logN, isTransient, st->vbr, st->constrained_vbr, eBands, LM, effectiveBytes, &tot_boost, st->lfe, surround_dynalloc); /* For LFE, everything interesting is in the first band */ if (st->lfe) offsets[0] = IMIN(8, effectiveBytes/3); ALLOC(cap, nbEBands, int); init_caps(mode,cap,LM,C); dynalloc_logp = 6; total_bits<<=BITRES; total_boost = 0; tell = ec_tell_frac(enc); for (i=start;iintensity = hysteresis_decision((opus_val16)(equiv_rate/1000), intensity_thresholds, intensity_histeresis, 21, st->intensity); st->intensity = IMIN(end,IMAX(start, st->intensity)); } alloc_trim = 5; if (tell+(6<lfe) alloc_trim = 5; else alloc_trim = alloc_trim_analysis(mode, X, bandLogE, end, LM, C, N, &st->analysis, &st->stereo_saving, tf_estimate, st->intensity, surround_trim, st->arch); ec_enc_icdf(enc, alloc_trim, trim_icdf, 7); tell = ec_tell_frac(enc); } /* Variable bitrate */ if (vbr_rate>0) { opus_val16 alpha; opus_int32 delta; /* The target rate in 8th bits per frame */ opus_int32 target, base_target; opus_int32 min_allowed; int lm_diff = mode->maxLM - LM; /* Don't attempt to use more than 510 kb/s, even for frames smaller than 20 ms. The CELT allocator will just not be able to use more than that anyway. */ nbCompressedBytes = IMIN(nbCompressedBytes,1275>>(3-LM)); base_target = vbr_rate - ((40*C+20)<constrained_vbr) base_target += (st->vbr_offset>>lm_diff); target = compute_vbr(mode, &st->analysis, base_target, LM, equiv_rate, st->lastCodedBands, C, st->intensity, st->constrained_vbr, st->stereo_saving, tot_boost, tf_estimate, pitch_change, maxDepth, st->variable_duration, st->lfe, st->energy_mask!=NULL, surround_masking, temporal_vbr); /* The current offset is removed from the target and the space used so far is added*/ target=target+tell; /* In VBR mode the frame size must not be reduced so much that it would result in the encoder running out of bits. The margin of 2 bytes ensures that none of the bust-prevention logic in the decoder will have triggered so far. */ min_allowed = ((tell+total_boost+(1<<(BITRES+3))-1)>>(BITRES+3)) + 2 - nbFilledBytes; nbAvailableBytes = (target+(1<<(BITRES+2)))>>(BITRES+3); nbAvailableBytes = IMAX(min_allowed,nbAvailableBytes); nbAvailableBytes = IMIN(nbCompressedBytes,nbAvailableBytes+nbFilledBytes) - nbFilledBytes; /* By how much did we "miss" the target on that frame */ delta = target - vbr_rate; target=nbAvailableBytes<<(BITRES+3); /*If the frame is silent we don't adjust our drift, otherwise the encoder will shoot to very high rates after hitting a span of silence, but we do allow the bitres to refill. This means that we'll undershoot our target in CVBR/VBR modes on files with lots of silence. */ if(silence) { nbAvailableBytes = 2; target = 2*8<vbr_count < 970) { st->vbr_count++; alpha = celt_rcp(SHL32(EXTEND32(st->vbr_count+20),16)); } else alpha = QCONST16(.001f,15); /* How many bits have we used in excess of what we're allowed */ if (st->constrained_vbr) st->vbr_reservoir += target - vbr_rate; /*printf ("%d\n", st->vbr_reservoir);*/ /* Compute the offset we need to apply in order to reach the target */ if (st->constrained_vbr) { st->vbr_drift += (opus_int32)MULT16_32_Q15(alpha,(delta*(1<vbr_offset-st->vbr_drift); st->vbr_offset = -st->vbr_drift; } /*printf ("%d\n", st->vbr_drift);*/ if (st->constrained_vbr && st->vbr_reservoir < 0) { /* We're under the min value -- increase rate */ int adjust = (-st->vbr_reservoir)/(8<vbr_reservoir = 0; /*printf ("+%d\n", adjust);*/ } nbCompressedBytes = IMIN(nbCompressedBytes,nbAvailableBytes+nbFilledBytes); /*printf("%d\n", nbCompressedBytes*50*8);*/ /* This moves the raw bits to take into account the new compressed size */ ec_enc_shrink(enc, nbCompressedBytes); } /* Bit allocation */ ALLOC(fine_quant, nbEBands, int); ALLOC(pulses, nbEBands, int); ALLOC(fine_priority, nbEBands, int); /* bits = packet size - where we are - safety*/ bits = (((opus_int32)nbCompressedBytes*8)<=2&&bits>=((LM+2)<analysis.valid) { int min_bandwidth; if (equiv_rate < (opus_int32)32000*C) min_bandwidth = 13; else if (equiv_rate < (opus_int32)48000*C) min_bandwidth = 16; else if (equiv_rate < (opus_int32)60000*C) min_bandwidth = 18; else if (equiv_rate < (opus_int32)80000*C) min_bandwidth = 19; else min_bandwidth = 20; signalBandwidth = IMAX(st->analysis.bandwidth, min_bandwidth); } #endif if (st->lfe) signalBandwidth = 1; codedBands = compute_allocation(mode, start, end, offsets, cap, alloc_trim, &st->intensity, &dual_stereo, bits, &balance, pulses, fine_quant, fine_priority, C, LM, enc, 1, st->lastCodedBands, signalBandwidth); if (st->lastCodedBands) st->lastCodedBands = IMIN(st->lastCodedBands+1,IMAX(st->lastCodedBands-1,codedBands)); else st->lastCodedBands = codedBands; quant_fine_energy(mode, start, end, oldBandE, error, fine_quant, enc, C); /* Residual quantisation */ ALLOC(collapse_masks, C*nbEBands, unsigned char); quant_all_bands(1, mode, start, end, X, C==2 ? X+N : NULL, collapse_masks, bandE, pulses, shortBlocks, st->spread_decision, dual_stereo, st->intensity, tf_res, nbCompressedBytes*(8<rng, st->arch); if (anti_collapse_rsv > 0) { anti_collapse_on = st->consec_transient<2; #ifdef FUZZING anti_collapse_on = rand()&0x1; #endif ec_enc_bits(enc, anti_collapse_on, 1); } quant_energy_finalise(mode, start, end, oldBandE, error, fine_quant, fine_priority, nbCompressedBytes*8-ec_tell(enc), enc, C); if (silence) { for (i=0;irng); } c=0; do { OPUS_MOVE(st->syn_mem[c], st->syn_mem[c]+N, 2*MAX_PERIOD-N+overlap/2); } while (++csyn_mem[c]+2*MAX_PERIOD-N; } while (++cupsample, silence, st->arch); c=0; do { st->prefilter_period=IMAX(st->prefilter_period, COMBFILTER_MINPERIOD); st->prefilter_period_old=IMAX(st->prefilter_period_old, COMBFILTER_MINPERIOD); comb_filter(out_mem[c], out_mem[c], st->prefilter_period_old, st->prefilter_period, mode->shortMdctSize, st->prefilter_gain_old, st->prefilter_gain, st->prefilter_tapset_old, st->prefilter_tapset, mode->window, overlap); if (LM!=0) comb_filter(out_mem[c]+mode->shortMdctSize, out_mem[c]+mode->shortMdctSize, st->prefilter_period, pitch_index, N-mode->shortMdctSize, st->prefilter_gain, gain1, st->prefilter_tapset, prefilter_tapset, mode->window, overlap); } while (++cupsample, mode->preemph, st->preemph_memD); st->prefilter_period_old = st->prefilter_period; st->prefilter_gain_old = st->prefilter_gain; st->prefilter_tapset_old = st->prefilter_tapset; } #endif st->prefilter_period = pitch_index; st->prefilter_gain = gain1; st->prefilter_tapset = prefilter_tapset; #ifdef RESYNTH if (LM!=0) { st->prefilter_period_old = st->prefilter_period; st->prefilter_gain_old = st->prefilter_gain; st->prefilter_tapset_old = st->prefilter_tapset; } #endif if (CC==2&&C==1) { OPUS_COPY(&oldBandE[nbEBands], oldBandE, nbEBands); } if (!isTransient) { OPUS_COPY(oldLogE2, oldLogE, CC*nbEBands); OPUS_COPY(oldLogE, oldBandE, CC*nbEBands); } else { for (i=0;iconsec_transient++; else st->consec_transient=0; st->rng = enc->rng; /* If there's any room left (can only happen for very high rates), it's already filled with zeros */ ec_enc_done(enc); #ifdef CUSTOM_MODES if (st->signalling) nbCompressedBytes++; #endif RESTORE_STACK; if (ec_get_error(enc)) return OPUS_INTERNAL_ERROR; else return nbCompressedBytes; } #ifdef CUSTOM_MODES #ifdef FIXED_POINT int opus_custom_encode(CELTEncoder * OPUS_RESTRICT st, const opus_int16 * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes) { return celt_encode_with_ec(st, pcm, frame_size, compressed, nbCompressedBytes, NULL); } #ifndef DISABLE_FLOAT_API int opus_custom_encode_float(CELTEncoder * OPUS_RESTRICT st, const float * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes) { int j, ret, C, N; VARDECL(opus_int16, in); ALLOC_STACK; if (pcm==NULL) return OPUS_BAD_ARG; C = st->channels; N = frame_size; ALLOC(in, C*N, opus_int16); for (j=0;jchannels; N=frame_size; ALLOC(in, C*N, celt_sig); for (j=0;j10) goto bad_arg; st->complexity = value; } break; case CELT_SET_START_BAND_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if (value<0 || value>=st->mode->nbEBands) goto bad_arg; st->start = value; } break; case CELT_SET_END_BAND_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if (value<1 || value>st->mode->nbEBands) goto bad_arg; st->end = value; } break; case CELT_SET_PREDICTION_REQUEST: { int value = va_arg(ap, opus_int32); if (value<0 || value>2) goto bad_arg; st->disable_pf = value<=1; st->force_intra = value==0; } break; case OPUS_SET_PACKET_LOSS_PERC_REQUEST: { int value = va_arg(ap, opus_int32); if (value<0 || value>100) goto bad_arg; st->loss_rate = value; } break; case OPUS_SET_VBR_CONSTRAINT_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); st->constrained_vbr = value; } break; case OPUS_SET_VBR_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); st->vbr = value; } break; case OPUS_SET_BITRATE_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if (value<=500 && value!=OPUS_BITRATE_MAX) goto bad_arg; value = IMIN(value, 260000*st->channels); st->bitrate = value; } break; case CELT_SET_CHANNELS_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if (value<1 || value>2) goto bad_arg; st->stream_channels = value; } break; case OPUS_SET_LSB_DEPTH_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if (value<8 || value>24) goto bad_arg; st->lsb_depth=value; } break; case OPUS_GET_LSB_DEPTH_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); *value=st->lsb_depth; } break; case OPUS_SET_EXPERT_FRAME_DURATION_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); st->variable_duration = value; } break; case OPUS_RESET_STATE: { int i; opus_val16 *oldBandE, *oldLogE, *oldLogE2; oldBandE = (opus_val16*)(st->in_mem+st->channels*(st->mode->overlap+COMBFILTER_MAXPERIOD)); oldLogE = oldBandE + st->channels*st->mode->nbEBands; oldLogE2 = oldLogE + st->channels*st->mode->nbEBands; OPUS_CLEAR((char*)&st->ENCODER_RESET_START, opus_custom_encoder_get_size(st->mode, st->channels)- ((char*)&st->ENCODER_RESET_START - (char*)st)); for (i=0;ichannels*st->mode->nbEBands;i++) oldLogE[i]=oldLogE2[i]=-QCONST16(28.f,DB_SHIFT); st->vbr_offset = 0; st->delayedIntra = 1; st->spread_decision = SPREAD_NORMAL; st->tonal_average = 256; st->hf_average = 0; st->tapset_decision = 0; } break; #ifdef CUSTOM_MODES case CELT_SET_INPUT_CLIPPING_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); st->clip = value; } break; #endif case CELT_SET_SIGNALLING_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); st->signalling = value; } break; case CELT_SET_ANALYSIS_REQUEST: { AnalysisInfo *info = va_arg(ap, AnalysisInfo *); if (info) OPUS_COPY(&st->analysis, info, 1); } break; case CELT_GET_MODE_REQUEST: { const CELTMode ** value = va_arg(ap, const CELTMode**); if (value==0) goto bad_arg; *value=st->mode; } break; case OPUS_GET_FINAL_RANGE_REQUEST: { opus_uint32 * value = va_arg(ap, opus_uint32 *); if (value==0) goto bad_arg; *value=st->rng; } break; case OPUS_SET_LFE_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); st->lfe = value; } break; case OPUS_SET_ENERGY_MASK_REQUEST: { opus_val16 *value = va_arg(ap, opus_val16*); st->energy_mask = value; } break; default: goto bad_request; } va_end(ap); return OPUS_OK; bad_arg: va_end(ap); return OPUS_BAD_ARG; bad_request: va_end(ap); return OPUS_UNIMPLEMENTED; } ================================================ FILE: deps/pjsip/third_party/opus/celt/celt_lpc.c ================================================ /* Copyright (c) 2009-2010 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "celt_lpc.h" #include "stack_alloc.h" #include "mathops.h" #include "pitch.h" void _celt_lpc( opus_val16 *_lpc, /* out: [0...p-1] LPC coefficients */ const opus_val32 *ac, /* in: [0...p] autocorrelation values */ int p ) { int i, j; opus_val32 r; opus_val32 error = ac[0]; #ifdef FIXED_POINT opus_val32 lpc[LPC_ORDER]; #else float *lpc = _lpc; #endif for (i = 0; i < p; i++) lpc[i] = 0; if (ac[0] != 0) { for (i = 0; i < p; i++) { /* Sum up this iteration's reflection coefficient */ opus_val32 rr = 0; for (j = 0; j < i; j++) rr += MULT32_32_Q31(lpc[j],ac[i - j]); rr += SHR32(ac[i + 1],3); r = -frac_div32(SHL32(rr,3), error); /* Update LPC coefficients and total error */ lpc[i] = SHR32(r,3); for (j = 0; j < (i+1)>>1; j++) { opus_val32 tmp1, tmp2; tmp1 = lpc[j]; tmp2 = lpc[i-1-j]; lpc[j] = tmp1 + MULT32_32_Q31(r,tmp2); lpc[i-1-j] = tmp2 + MULT32_32_Q31(r,tmp1); } error = error - MULT32_32_Q31(MULT32_32_Q31(r,r),error); /* Bail out once we get 30 dB gain */ #ifdef FIXED_POINT if (error=1;j--) { mem[j]=mem[j-1]; } mem[0] = ROUND16(sum,SIG_SHIFT); _y[i] = sum; } #else int i,j; VARDECL(opus_val16, rden); VARDECL(opus_val16, y); SAVE_STACK; celt_assert((ord&3)==0); ALLOC(rden, ord, opus_val16); ALLOC(y, N+ord, opus_val16); for(i=0;i0); celt_assert(overlap>=0); if (overlap == 0) { xptr = x; } else { for (i=0;i0) { for(i=0;i= 536870912) { int shift2=1; if (ac[0] >= 1073741824) shift2++; for (i=0;i<=lag;i++) ac[i] = SHR32(ac[i], shift2); shift += shift2; } #endif RESTORE_STACK; return shift; } ================================================ FILE: deps/pjsip/third_party/opus/celt/celt_lpc.h ================================================ /* Copyright (c) 2009-2010 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef PLC_H #define PLC_H #include "arch.h" #include "cpu_support.h" #if defined(OPUS_X86_MAY_HAVE_SSE4_1) #include "x86/celt_lpc_sse.h" #endif #define LPC_ORDER 24 void _celt_lpc(opus_val16 *_lpc, const opus_val32 *ac, int p); void celt_fir_c( const opus_val16 *x, const opus_val16 *num, opus_val16 *y, int N, int ord, opus_val16 *mem, int arch); #if !defined(OVERRIDE_CELT_FIR) #define celt_fir(x, num, y, N, ord, mem, arch) \ (celt_fir_c(x, num, y, N, ord, mem, arch)) #endif void celt_iir(const opus_val32 *x, const opus_val16 *den, opus_val32 *y, int N, int ord, opus_val16 *mem, int arch); int _celt_autocorr(const opus_val16 *x, opus_val32 *ac, const opus_val16 *window, int overlap, int lag, int n, int arch); #endif /* PLC_H */ ================================================ FILE: deps/pjsip/third_party/opus/celt/cpu_support.h ================================================ /* Copyright (c) 2010 Xiph.Org Foundation * Copyright (c) 2013 Parrot */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CPU_SUPPORT_H #define CPU_SUPPORT_H #include "opus_types.h" #include "opus_defines.h" #if defined(OPUS_HAVE_RTCD) && \ (defined(OPUS_ARM_ASM) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR)) #include "arm/armcpu.h" /* We currently support 4 ARM variants: * arch[0] -> ARMv4 * arch[1] -> ARMv5E * arch[2] -> ARMv6 * arch[3] -> NEON */ #define OPUS_ARCHMASK 3 #elif (defined(OPUS_X86_MAY_HAVE_SSE) && !defined(OPUS_X86_PRESUME_SSE)) || \ (defined(OPUS_X86_MAY_HAVE_SSE2) && !defined(OPUS_X86_PRESUME_SSE2)) || \ (defined(OPUS_X86_MAY_HAVE_SSE4_1) && !defined(OPUS_X86_PRESUME_SSE4_1)) || \ (defined(OPUS_X86_MAY_HAVE_AVX) && !defined(OPUS_X86_PRESUME_AVX)) #include "x86/x86cpu.h" /* We currently support 5 x86 variants: * arch[0] -> non-sse * arch[1] -> sse * arch[2] -> sse2 * arch[3] -> sse4.1 * arch[4] -> avx */ #define OPUS_ARCHMASK 7 int opus_select_arch(void); #else #define OPUS_ARCHMASK 0 static OPUS_INLINE int opus_select_arch(void) { return 0; } #endif #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/cwrs.c ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Copyright (c) 2007-2009 Timothy B. Terriberry Written by Timothy B. Terriberry and Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_support.h" #include "cwrs.h" #include "mathops.h" #include "arch.h" #ifdef CUSTOM_MODES /*Guaranteed to return a conservatively large estimate of the binary logarithm with frac bits of fractional precision. Tested for all possible 32-bit inputs with frac=4, where the maximum overestimation is 0.06254243 bits.*/ int log2_frac(opus_uint32 val, int frac) { int l; l=EC_ILOG(val); if(val&(val-1)){ /*This is (val>>l-16), but guaranteed to round up, even if adding a bias before the shift would cause overflow (e.g., for 0xFFFFxxxx). Doesn't work for val=0, but that case fails the test above.*/ if(l>16)val=((val-1)>>(l-16))+1; else val<<=16-l; l=(l-1)<>16); l+=b<>b; val=(val*val+0x7FFF)>>15; } while(frac-->0); /*If val is not exactly 0x8000, then we have to round up the remainder.*/ return l+(val>0x8000); } /*Exact powers of two require no rounding.*/ else return (l-1)<0 ? sum(k=1...K,2**k*choose(N,k)*choose(K-1,k-1)) : 1, where choose() is the binomial function. A table of values for N<10 and K<10 looks like: V[10][10] = { {1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 2, 2, 2, 2, 2, 2, 2, 2, 2}, {1, 4, 8, 12, 16, 20, 24, 28, 32, 36}, {1, 6, 18, 38, 66, 102, 146, 198, 258, 326}, {1, 8, 32, 88, 192, 360, 608, 952, 1408, 1992}, {1, 10, 50, 170, 450, 1002, 1970, 3530, 5890, 9290}, {1, 12, 72, 292, 912, 2364, 5336, 10836, 20256, 35436}, {1, 14, 98, 462, 1666, 4942, 12642, 28814, 59906, 115598}, {1, 16, 128, 688, 2816, 9424, 27008, 68464, 157184, 332688}, {1, 18, 162, 978, 4482, 16722, 53154, 148626, 374274, 864146} }; U(N,K) = the number of such combinations wherein N-1 objects are taken at most K-1 at a time. This is given by U(N,K) = sum(k=0...K-1,V(N-1,k)) = K>0 ? (V(N-1,K-1) + V(N,K-1))/2 : 0. The latter expression also makes clear that U(N,K) is half the number of such combinations wherein the first object is taken at least once. Although it may not be clear from either of these definitions, U(N,K) is the natural function to work with when enumerating the pulse vector codebooks, not V(N,K). U(N,K) is not well-defined for N=0, but with the extension U(0,K) = K>0 ? 0 : 1, the function becomes symmetric: U(N,K) = U(K,N), with a similar table: U[10][10] = { {1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {0, 1, 3, 5, 7, 9, 11, 13, 15, 17}, {0, 1, 5, 13, 25, 41, 61, 85, 113, 145}, {0, 1, 7, 25, 63, 129, 231, 377, 575, 833}, {0, 1, 9, 41, 129, 321, 681, 1289, 2241, 3649}, {0, 1, 11, 61, 231, 681, 1683, 3653, 7183, 13073}, {0, 1, 13, 85, 377, 1289, 3653, 8989, 19825, 40081}, {0, 1, 15, 113, 575, 2241, 7183, 19825, 48639, 108545}, {0, 1, 17, 145, 833, 3649, 13073, 40081, 108545, 265729} }; With this extension, V(N,K) may be written in terms of U(N,K): V(N,K) = U(N,K) + U(N,K+1) for all N>=0, K>=0. Thus U(N,K+1) represents the number of combinations where the first element is positive or zero, and U(N,K) represents the number of combinations where it is negative. With a large enough table of U(N,K) values, we could write O(N) encoding and O(min(N*log(K),N+K)) decoding routines, but such a table would be prohibitively large for small embedded devices (K may be as large as 32767 for small N, and N may be as large as 200). Both functions obey the same recurrence relation: V(N,K) = V(N-1,K) + V(N,K-1) + V(N-1,K-1), U(N,K) = U(N-1,K) + U(N,K-1) + U(N-1,K-1), for all N>0, K>0, with different initial conditions at N=0 or K=0. This allows us to construct a row of one of the tables above given the previous row or the next row. Thus we can derive O(NK) encoding and decoding routines with O(K) memory using only addition and subtraction. When encoding, we build up from the U(2,K) row and work our way forwards. When decoding, we need to start at the U(N,K) row and work our way backwards, which requires a means of computing U(N,K). U(N,K) may be computed from two previous values with the same N: U(N,K) = ((2*N-1)*U(N,K-1) - U(N,K-2))/(K-1) + U(N,K-2) for all N>1, and since U(N,K) is symmetric, a similar relation holds for two previous values with the same K: U(N,K>1) = ((2*K-1)*U(N-1,K) - U(N-2,K))/(N-1) + U(N-2,K) for all K>1. This allows us to construct an arbitrary row of the U(N,K) table by starting with the first two values, which are constants. This saves roughly 2/3 the work in our O(NK) decoding routine, but costs O(K) multiplications. Similar relations can be derived for V(N,K), but are not used here. For N>0 and K>0, U(N,K) and V(N,K) take on the form of an (N-1)-degree polynomial for fixed N. The first few are U(1,K) = 1, U(2,K) = 2*K-1, U(3,K) = (2*K-2)*K+1, U(4,K) = (((4*K-6)*K+8)*K-3)/3, U(5,K) = ((((2*K-4)*K+10)*K-8)*K+3)/3, and V(1,K) = 2, V(2,K) = 4*K, V(3,K) = 4*K*K+2, V(4,K) = 8*(K*K+2)*K/3, V(5,K) = ((4*K*K+20)*K*K+6)/3, for all K>0. This allows us to derive O(N) encoding and O(N*log(K)) decoding routines for small N (and indeed decoding is also O(N) for N<3). @ARTICLE{Fis86, author="Thomas R. Fischer", title="A Pyramid Vector Quantizer", journal="IEEE Transactions on Information Theory", volume="IT-32", number=4, pages="568--583", month=Jul, year=1986 }*/ #if !defined(SMALL_FOOTPRINT) /*U(N,K) = U(K,N) := N>0?K>0?U(N-1,K)+U(N,K-1)+U(N-1,K-1):0:K>0?1:0*/ # define CELT_PVQ_U(_n,_k) (CELT_PVQ_U_ROW[IMIN(_n,_k)][IMAX(_n,_k)]) /*V(N,K) := U(N,K)+U(N,K+1) = the number of PVQ codewords for a band of size N with K pulses allocated to it.*/ # define CELT_PVQ_V(_n,_k) (CELT_PVQ_U(_n,_k)+CELT_PVQ_U(_n,(_k)+1)) /*For each V(N,K) supported, we will access element U(min(N,K+1),max(N,K+1)). Thus, the number of entries in row I is the larger of the maximum number of pulses we will ever allocate for a given N=I (K=128, or however many fit in 32 bits, whichever is smaller), plus one, and the maximum N for which K=I-1 pulses fit in 32 bits. The largest band size in an Opus Custom mode is 208. Otherwise, we can limit things to the set of N which can be achieved by splitting a band from a standard Opus mode: 176, 144, 96, 88, 72, 64, 48, 44, 36, 32, 24, 22, 18, 16, 8, 4, 2).*/ #if defined(CUSTOM_MODES) static const opus_uint32 CELT_PVQ_U_DATA[1488]={ #else static const opus_uint32 CELT_PVQ_U_DATA[1272]={ #endif /*N=0, K=0...176:*/ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, #if defined(CUSTOM_MODES) /*...208:*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, #endif /*N=1, K=1...176:*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, #if defined(CUSTOM_MODES) /*...208:*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, #endif /*N=2, K=2...176:*/ 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 257, 259, 261, 263, 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 285, 287, 289, 291, 293, 295, 297, 299, 301, 303, 305, 307, 309, 311, 313, 315, 317, 319, 321, 323, 325, 327, 329, 331, 333, 335, 337, 339, 341, 343, 345, 347, 349, 351, #if defined(CUSTOM_MODES) /*...208:*/ 353, 355, 357, 359, 361, 363, 365, 367, 369, 371, 373, 375, 377, 379, 381, 383, 385, 387, 389, 391, 393, 395, 397, 399, 401, 403, 405, 407, 409, 411, 413, 415, #endif /*N=3, K=3...176:*/ 13, 25, 41, 61, 85, 113, 145, 181, 221, 265, 313, 365, 421, 481, 545, 613, 685, 761, 841, 925, 1013, 1105, 1201, 1301, 1405, 1513, 1625, 1741, 1861, 1985, 2113, 2245, 2381, 2521, 2665, 2813, 2965, 3121, 3281, 3445, 3613, 3785, 3961, 4141, 4325, 4513, 4705, 4901, 5101, 5305, 5513, 5725, 5941, 6161, 6385, 6613, 6845, 7081, 7321, 7565, 7813, 8065, 8321, 8581, 8845, 9113, 9385, 9661, 9941, 10225, 10513, 10805, 11101, 11401, 11705, 12013, 12325, 12641, 12961, 13285, 13613, 13945, 14281, 14621, 14965, 15313, 15665, 16021, 16381, 16745, 17113, 17485, 17861, 18241, 18625, 19013, 19405, 19801, 20201, 20605, 21013, 21425, 21841, 22261, 22685, 23113, 23545, 23981, 24421, 24865, 25313, 25765, 26221, 26681, 27145, 27613, 28085, 28561, 29041, 29525, 30013, 30505, 31001, 31501, 32005, 32513, 33025, 33541, 34061, 34585, 35113, 35645, 36181, 36721, 37265, 37813, 38365, 38921, 39481, 40045, 40613, 41185, 41761, 42341, 42925, 43513, 44105, 44701, 45301, 45905, 46513, 47125, 47741, 48361, 48985, 49613, 50245, 50881, 51521, 52165, 52813, 53465, 54121, 54781, 55445, 56113, 56785, 57461, 58141, 58825, 59513, 60205, 60901, 61601, #if defined(CUSTOM_MODES) /*...208:*/ 62305, 63013, 63725, 64441, 65161, 65885, 66613, 67345, 68081, 68821, 69565, 70313, 71065, 71821, 72581, 73345, 74113, 74885, 75661, 76441, 77225, 78013, 78805, 79601, 80401, 81205, 82013, 82825, 83641, 84461, 85285, 86113, #endif /*N=4, K=4...176:*/ 63, 129, 231, 377, 575, 833, 1159, 1561, 2047, 2625, 3303, 4089, 4991, 6017, 7175, 8473, 9919, 11521, 13287, 15225, 17343, 19649, 22151, 24857, 27775, 30913, 34279, 37881, 41727, 45825, 50183, 54809, 59711, 64897, 70375, 76153, 82239, 88641, 95367, 102425, 109823, 117569, 125671, 134137, 142975, 152193, 161799, 171801, 182207, 193025, 204263, 215929, 228031, 240577, 253575, 267033, 280959, 295361, 310247, 325625, 341503, 357889, 374791, 392217, 410175, 428673, 447719, 467321, 487487, 508225, 529543, 551449, 573951, 597057, 620775, 645113, 670079, 695681, 721927, 748825, 776383, 804609, 833511, 863097, 893375, 924353, 956039, 988441, 1021567, 1055425, 1090023, 1125369, 1161471, 1198337, 1235975, 1274393, 1313599, 1353601, 1394407, 1436025, 1478463, 1521729, 1565831, 1610777, 1656575, 1703233, 1750759, 1799161, 1848447, 1898625, 1949703, 2001689, 2054591, 2108417, 2163175, 2218873, 2275519, 2333121, 2391687, 2451225, 2511743, 2573249, 2635751, 2699257, 2763775, 2829313, 2895879, 2963481, 3032127, 3101825, 3172583, 3244409, 3317311, 3391297, 3466375, 3542553, 3619839, 3698241, 3777767, 3858425, 3940223, 4023169, 4107271, 4192537, 4278975, 4366593, 4455399, 4545401, 4636607, 4729025, 4822663, 4917529, 5013631, 5110977, 5209575, 5309433, 5410559, 5512961, 5616647, 5721625, 5827903, 5935489, 6044391, 6154617, 6266175, 6379073, 6493319, 6608921, 6725887, 6844225, 6963943, 7085049, 7207551, #if defined(CUSTOM_MODES) /*...208:*/ 7331457, 7456775, 7583513, 7711679, 7841281, 7972327, 8104825, 8238783, 8374209, 8511111, 8649497, 8789375, 8930753, 9073639, 9218041, 9363967, 9511425, 9660423, 9810969, 9963071, 10116737, 10271975, 10428793, 10587199, 10747201, 10908807, 11072025, 11236863, 11403329, 11571431, 11741177, 11912575, #endif /*N=5, K=5...176:*/ 321, 681, 1289, 2241, 3649, 5641, 8361, 11969, 16641, 22569, 29961, 39041, 50049, 63241, 78889, 97281, 118721, 143529, 172041, 204609, 241601, 283401, 330409, 383041, 441729, 506921, 579081, 658689, 746241, 842249, 947241, 1061761, 1186369, 1321641, 1468169, 1626561, 1797441, 1981449, 2179241, 2391489, 2618881, 2862121, 3121929, 3399041, 3694209, 4008201, 4341801, 4695809, 5071041, 5468329, 5888521, 6332481, 6801089, 7295241, 7815849, 8363841, 8940161, 9545769, 10181641, 10848769, 11548161, 12280841, 13047849, 13850241, 14689089, 15565481, 16480521, 17435329, 18431041, 19468809, 20549801, 21675201, 22846209, 24064041, 25329929, 26645121, 28010881, 29428489, 30899241, 32424449, 34005441, 35643561, 37340169, 39096641, 40914369, 42794761, 44739241, 46749249, 48826241, 50971689, 53187081, 55473921, 57833729, 60268041, 62778409, 65366401, 68033601, 70781609, 73612041, 76526529, 79526721, 82614281, 85790889, 89058241, 92418049, 95872041, 99421961, 103069569, 106816641, 110664969, 114616361, 118672641, 122835649, 127107241, 131489289, 135983681, 140592321, 145317129, 150160041, 155123009, 160208001, 165417001, 170752009, 176215041, 181808129, 187533321, 193392681, 199388289, 205522241, 211796649, 218213641, 224775361, 231483969, 238341641, 245350569, 252512961, 259831041, 267307049, 274943241, 282741889, 290705281, 298835721, 307135529, 315607041, 324252609, 333074601, 342075401, 351257409, 360623041, 370174729, 379914921, 389846081, 399970689, 410291241, 420810249, 431530241, 442453761, 453583369, 464921641, 476471169, 488234561, 500214441, 512413449, 524834241, 537479489, 550351881, 563454121, 576788929, 590359041, 604167209, 618216201, 632508801, #if defined(CUSTOM_MODES) /*...208:*/ 647047809, 661836041, 676876329, 692171521, 707724481, 723538089, 739615241, 755958849, 772571841, 789457161, 806617769, 824056641, 841776769, 859781161, 878072841, 896654849, 915530241, 934702089, 954173481, 973947521, 994027329, 1014416041, 1035116809, 1056132801, 1077467201, 1099123209, 1121104041, 1143412929, 1166053121, 1189027881, 1212340489, 1235994241, #endif /*N=6, K=6...96:*/ 1683, 3653, 7183, 13073, 22363, 36365, 56695, 85305, 124515, 177045, 246047, 335137, 448427, 590557, 766727, 982729, 1244979, 1560549, 1937199, 2383409, 2908411, 3522221, 4235671, 5060441, 6009091, 7095093, 8332863, 9737793, 11326283, 13115773, 15124775, 17372905, 19880915, 22670725, 25765455, 29189457, 32968347, 37129037, 41699767, 46710137, 52191139, 58175189, 64696159, 71789409, 79491819, 87841821, 96879431, 106646281, 117185651, 128542501, 140763503, 153897073, 167993403, 183104493, 199284183, 216588185, 235074115, 254801525, 275831935, 298228865, 322057867, 347386557, 374284647, 402823977, 433078547, 465124549, 499040399, 534906769, 572806619, 612825229, 655050231, 699571641, 746481891, 795875861, 847850911, 902506913, 959946283, 1020274013, 1083597703, 1150027593, 1219676595, 1292660325, 1369097135, 1449108145, 1532817275, 1620351277, 1711839767, 1807415257, 1907213187, 2011371957, 2120032959, #if defined(CUSTOM_MODES) /*...109:*/ 2233340609U, 2351442379U, 2474488829U, 2602633639U, 2736033641U, 2874848851U, 3019242501U, 3169381071U, 3325434321U, 3487575323U, 3655980493U, 3830829623U, 4012305913U, #endif /*N=7, K=7...54*/ 8989, 19825, 40081, 75517, 134245, 227305, 369305, 579125, 880685, 1303777, 1884961, 2668525, 3707509, 5064793, 6814249, 9041957, 11847485, 15345233, 19665841, 24957661, 31388293, 39146185, 48442297, 59511829, 72616013, 88043969, 106114625, 127178701, 151620757, 179861305, 212358985, 249612805, 292164445, 340600625, 395555537, 457713341, 527810725, 606639529, 695049433, 793950709, 904317037, 1027188385, 1163673953, 1314955181, 1482288821, 1667010073, 1870535785, 2094367717, #if defined(CUSTOM_MODES) /*...60:*/ 2340095869U, 2609401873U, 2904062449U, 3225952925U, 3577050821U, 3959439497U, #endif /*N=8, K=8...37*/ 48639, 108545, 224143, 433905, 795455, 1392065, 2340495, 3800305, 5984767, 9173505, 13726991, 20103025, 28875327, 40754369, 56610575, 77500017, 104692735, 139703809, 184327311, 240673265, 311207743, 398796225, 506750351, 638878193, 799538175, 993696769, 1226990095, 1505789553, 1837271615, 2229491905U, #if defined(CUSTOM_MODES) /*...40:*/ 2691463695U, 3233240945U, 3866006015U, #endif /*N=9, K=9...28:*/ 265729, 598417, 1256465, 2485825, 4673345, 8405905, 14546705, 24331777, 39490049, 62390545, 96220561, 145198913, 214828609, 312193553, 446304145, 628496897, 872893441, 1196924561, 1621925137, 2173806145U, #if defined(CUSTOM_MODES) /*...29:*/ 2883810113U, #endif /*N=10, K=10...24:*/ 1462563, 3317445, 7059735, 14218905, 27298155, 50250765, 89129247, 152951073, 254831667, 413442773, 654862247, 1014889769, 1541911931, 2300409629U, 3375210671U, /*N=11, K=11...19:*/ 8097453, 18474633, 39753273, 81270333, 158819253, 298199265, 540279585, 948062325, 1616336765, #if defined(CUSTOM_MODES) /*...20:*/ 2684641785U, #endif /*N=12, K=12...18:*/ 45046719, 103274625, 224298231, 464387817, 921406335, 1759885185, 3248227095U, /*N=13, K=13...16:*/ 251595969, 579168825, 1267854873, 2653649025U, /*N=14, K=14:*/ 1409933619 }; #if defined(CUSTOM_MODES) static const opus_uint32 *const CELT_PVQ_U_ROW[15]={ CELT_PVQ_U_DATA+ 0,CELT_PVQ_U_DATA+ 208,CELT_PVQ_U_DATA+ 415, CELT_PVQ_U_DATA+ 621,CELT_PVQ_U_DATA+ 826,CELT_PVQ_U_DATA+1030, CELT_PVQ_U_DATA+1233,CELT_PVQ_U_DATA+1336,CELT_PVQ_U_DATA+1389, CELT_PVQ_U_DATA+1421,CELT_PVQ_U_DATA+1441,CELT_PVQ_U_DATA+1455, CELT_PVQ_U_DATA+1464,CELT_PVQ_U_DATA+1470,CELT_PVQ_U_DATA+1473 }; #else static const opus_uint32 *const CELT_PVQ_U_ROW[15]={ CELT_PVQ_U_DATA+ 0,CELT_PVQ_U_DATA+ 176,CELT_PVQ_U_DATA+ 351, CELT_PVQ_U_DATA+ 525,CELT_PVQ_U_DATA+ 698,CELT_PVQ_U_DATA+ 870, CELT_PVQ_U_DATA+1041,CELT_PVQ_U_DATA+1131,CELT_PVQ_U_DATA+1178, CELT_PVQ_U_DATA+1207,CELT_PVQ_U_DATA+1226,CELT_PVQ_U_DATA+1240, CELT_PVQ_U_DATA+1248,CELT_PVQ_U_DATA+1254,CELT_PVQ_U_DATA+1257 }; #endif #if defined(CUSTOM_MODES) void get_required_bits(opus_int16 *_bits,int _n,int _maxk,int _frac){ int k; /*_maxk==0 => there's nothing to do.*/ celt_assert(_maxk>0); _bits[0]=0; for(k=1;k<=_maxk;k++)_bits[k]=log2_frac(CELT_PVQ_V(_n,k),_frac); } #endif static opus_uint32 icwrs(int _n,const int *_y){ opus_uint32 i; int j; int k; celt_assert(_n>=2); j=_n-1; i=_y[j]<0; k=abs(_y[j]); do{ j--; i+=CELT_PVQ_U(_n-j,k); k+=abs(_y[j]); if(_y[j]<0)i+=CELT_PVQ_U(_n-j,k+1); } while(j>0); return i; } void encode_pulses(const int *_y,int _n,int _k,ec_enc *_enc){ celt_assert(_k>0); ec_enc_uint(_enc,icwrs(_n,_y),CELT_PVQ_V(_n,_k)); } static opus_val32 cwrsi(int _n,int _k,opus_uint32 _i,int *_y){ opus_uint32 p; int s; int k0; opus_int16 val; opus_val32 yy=0; celt_assert(_k>0); celt_assert(_n>1); while(_n>2){ opus_uint32 q; /*Lots of pulses case:*/ if(_k>=_n){ const opus_uint32 *row; row=CELT_PVQ_U_ROW[_n]; /*Are the pulses in this dimension negative?*/ p=row[_k+1]; s=-(_i>=p); _i-=p&s; /*Count how many pulses were placed in this dimension.*/ k0=_k; q=row[_n]; if(q>_i){ celt_assert(p>q); _k=_n; do p=CELT_PVQ_U_ROW[--_k][_n]; while(p>_i); } else for(p=row[_k];p>_i;p=row[_k])_k--; _i-=p; val=(k0-_k+s)^s; *_y++=val; yy=MAC16_16(yy,val,val); } /*Lots of dimensions case:*/ else{ /*Are there any pulses in this dimension at all?*/ p=CELT_PVQ_U_ROW[_k][_n]; q=CELT_PVQ_U_ROW[_k+1][_n]; if(p<=_i&&_i=q); _i-=q&s; /*Count how many pulses were placed in this dimension.*/ k0=_k; do p=CELT_PVQ_U_ROW[--_k][_n]; while(p>_i); _i-=p; val=(k0-_k+s)^s; *_y++=val; yy=MAC16_16(yy,val,val); } } _n--; } /*_n==2*/ p=2*_k+1; s=-(_i>=p); _i-=p&s; k0=_k; _k=(_i+1)>>1; if(_k)_i-=2*_k-1; val=(k0-_k+s)^s; *_y++=val; yy=MAC16_16(yy,val,val); /*_n==1*/ s=-(int)_i; val=(_k+s)^s; *_y=val; yy=MAC16_16(yy,val,val); return yy; } opus_val32 decode_pulses(int *_y,int _n,int _k,ec_dec *_dec){ return cwrsi(_n,_k,ec_dec_uint(_dec,CELT_PVQ_V(_n,_k)),_y); } #else /* SMALL_FOOTPRINT */ /*Computes the next row/column of any recurrence that obeys the relation u[i][j]=u[i-1][j]+u[i][j-1]+u[i-1][j-1]. _ui0 is the base case for the new row/column.*/ static OPUS_INLINE void unext(opus_uint32 *_ui,unsigned _len,opus_uint32 _ui0){ opus_uint32 ui1; unsigned j; /*This do-while will overrun the array if we don't have storage for at least 2 values.*/ j=1; do { ui1=UADD32(UADD32(_ui[j],_ui[j-1]),_ui0); _ui[j-1]=_ui0; _ui0=ui1; } while (++j<_len); _ui[j-1]=_ui0; } /*Computes the previous row/column of any recurrence that obeys the relation u[i-1][j]=u[i][j]-u[i][j-1]-u[i-1][j-1]. _ui0 is the base case for the new row/column.*/ static OPUS_INLINE void uprev(opus_uint32 *_ui,unsigned _n,opus_uint32 _ui0){ opus_uint32 ui1; unsigned j; /*This do-while will overrun the array if we don't have storage for at least 2 values.*/ j=1; do { ui1=USUB32(USUB32(_ui[j],_ui[j-1]),_ui0); _ui[j-1]=_ui0; _ui0=ui1; } while (++j<_n); _ui[j-1]=_ui0; } /*Compute V(_n,_k), as well as U(_n,0..._k+1). _u: On exit, _u[i] contains U(_n,i) for i in [0..._k+1].*/ static opus_uint32 ncwrs_urow(unsigned _n,unsigned _k,opus_uint32 *_u){ opus_uint32 um2; unsigned len; unsigned k; len=_k+2; /*We require storage at least 3 values (e.g., _k>0).*/ celt_assert(len>=3); _u[0]=0; _u[1]=um2=1; /*If _n==0, _u[0] should be 1 and the rest should be 0.*/ /*If _n==1, _u[i] should be 1 for i>1.*/ celt_assert(_n>=2); /*If _k==0, the following do-while loop will overflow the buffer.*/ celt_assert(_k>0); k=2; do _u[k]=(k<<1)-1; while(++k0); j=0; do{ opus_uint32 p; int s; int yj; p=_u[_k+1]; s=-(_i>=p); _i-=p&s; yj=_k; p=_u[_k]; while(p>_i)p=_u[--_k]; _i-=p; yj-=_k; val=(yj+s)^s; _y[j]=val; yy=MAC16_16(yy,val,val); uprev(_u,_k+2,0); } while(++j<_n); return yy; } /*Returns the index of the given combination of K elements chosen from a set of size 1 with associated sign bits. _y: The vector of pulses, whose sum of absolute values is K. _k: Returns K.*/ static OPUS_INLINE opus_uint32 icwrs1(const int *_y,int *_k){ *_k=abs(_y[0]); return _y[0]<0; } /*Returns the index of the given combination of K elements chosen from a set of size _n with associated sign bits. _y: The vector of pulses, whose sum of absolute values must be _k. _nc: Returns V(_n,_k).*/ static OPUS_INLINE opus_uint32 icwrs(int _n,int _k,opus_uint32 *_nc,const int *_y, opus_uint32 *_u){ opus_uint32 i; int j; int k; /*We can't unroll the first two iterations of the loop unless _n>=2.*/ celt_assert(_n>=2); _u[0]=0; for(k=1;k<=_k+1;k++)_u[k]=(k<<1)-1; i=icwrs1(_y+_n-1,&k); j=_n-2; i+=_u[k]; k+=abs(_y[j]); if(_y[j]<0)i+=_u[k+1]; while(j-->0){ unext(_u,_k+2,0); i+=_u[k]; k+=abs(_y[j]); if(_y[j]<0)i+=_u[k+1]; } *_nc=_u[k]+_u[k+1]; return i; } #ifdef CUSTOM_MODES void get_required_bits(opus_int16 *_bits,int _n,int _maxk,int _frac){ int k; /*_maxk==0 => there's nothing to do.*/ celt_assert(_maxk>0); _bits[0]=0; if (_n==1) { for (k=1;k<=_maxk;k++) _bits[k] = 1<<_frac; } else { VARDECL(opus_uint32,u); SAVE_STACK; ALLOC(u,_maxk+2U,opus_uint32); ncwrs_urow(_n,_maxk,u); for(k=1;k<=_maxk;k++) _bits[k]=log2_frac(u[k]+u[k+1],_frac); RESTORE_STACK; } } #endif /* CUSTOM_MODES */ void encode_pulses(const int *_y,int _n,int _k,ec_enc *_enc){ opus_uint32 i; VARDECL(opus_uint32,u); opus_uint32 nc; SAVE_STACK; celt_assert(_k>0); ALLOC(u,_k+2U,opus_uint32); i=icwrs(_n,_k,&nc,_y,u); ec_enc_uint(_enc,i,nc); RESTORE_STACK; } opus_val32 decode_pulses(int *_y,int _n,int _k,ec_dec *_dec){ VARDECL(opus_uint32,u); int ret; SAVE_STACK; celt_assert(_k>0); ALLOC(u,_k+2U,opus_uint32); ret = cwrsi(_n,_k,ec_dec_uint(_dec,ncwrs_urow(_n,_k,u)),_y,u); RESTORE_STACK; return ret; } #endif /* SMALL_FOOTPRINT */ ================================================ FILE: deps/pjsip/third_party/opus/celt/cwrs.h ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Copyright (c) 2007-2009 Timothy B. Terriberry Written by Timothy B. Terriberry and Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CWRS_H #define CWRS_H #include "arch.h" #include "stack_alloc.h" #include "entenc.h" #include "entdec.h" #ifdef CUSTOM_MODES int log2_frac(opus_uint32 val, int frac); #endif void get_required_bits(opus_int16 *bits, int N, int K, int frac); void encode_pulses(const int *_y, int N, int K, ec_enc *enc); opus_val32 decode_pulses(int *_y, int N, int K, ec_dec *dec); #endif /* CWRS_H */ ================================================ FILE: deps/pjsip/third_party/opus/celt/ecintrin.h ================================================ /* Copyright (c) 2003-2008 Timothy B. Terriberry Copyright (c) 2008 Xiph.Org Foundation */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*Some common macros for potential platform-specific optimization.*/ #include "opus_types.h" #include #include #include "arch.h" #if !defined(_ecintrin_H) # define _ecintrin_H (1) /*Some specific platforms may have optimized intrinsic or OPUS_INLINE assembly versions of these functions which can substantially improve performance. We define macros for them to allow easy incorporation of these non-ANSI features.*/ /*Modern gcc (4.x) can compile the naive versions of min and max with cmov if given an appropriate architecture, but the branchless bit-twiddling versions are just as fast, and do not require any special target architecture. Earlier gcc versions (3.x) compiled both code to the same assembly instructions, because of the way they represented ((_b)>(_a)) internally.*/ # define EC_MINI(_a,_b) ((_a)+(((_b)-(_a))&-((_b)<(_a)))) /*Count leading zeros. This macro should only be used for implementing ec_ilog(), if it is defined. All other code should use EC_ILOG() instead.*/ #if defined(_MSC_VER) && (_MSC_VER >= 1400) # include /*In _DEBUG mode this is not an intrinsic by default.*/ # pragma intrinsic(_BitScanReverse) static __inline int ec_bsr(unsigned long _x){ unsigned long ret; _BitScanReverse(&ret,_x); return (int)ret; } # define EC_CLZ0 (1) # define EC_CLZ(_x) (-ec_bsr(_x)) #elif defined(ENABLE_TI_DSPLIB) # include "dsplib.h" # define EC_CLZ0 (31) # define EC_CLZ(_x) (_lnorm(_x)) #elif __GNUC_PREREQ(3,4) # if INT_MAX>=2147483647 # define EC_CLZ0 ((int)sizeof(unsigned)*CHAR_BIT) # define EC_CLZ(_x) (__builtin_clz(_x)) # elif LONG_MAX>=2147483647L # define EC_CLZ0 ((int)sizeof(unsigned long)*CHAR_BIT) # define EC_CLZ(_x) (__builtin_clzl(_x)) # endif #endif #if defined(EC_CLZ) /*Note that __builtin_clz is not defined when _x==0, according to the gcc documentation (and that of the BSR instruction that implements it on x86). The majority of the time we can never pass it zero. When we need to, it can be special cased.*/ # define EC_ILOG(_x) (EC_CLZ0-EC_CLZ(_x)) #else int ec_ilog(opus_uint32 _v); # define EC_ILOG(_x) (ec_ilog(_x)) #endif #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/entcode.c ================================================ /* Copyright (c) 2001-2011 Timothy B. Terriberry */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "entcode.h" #include "arch.h" #if !defined(EC_CLZ) /*This is a fallback for systems where we don't know how to access a BSR or CLZ instruction (see ecintrin.h). If you are optimizing Opus on a new platform and it has a native CLZ or BZR (e.g. cell, MIPS, x86, etc) then making it available to Opus will be an easy performance win.*/ int ec_ilog(opus_uint32 _v){ /*On a Pentium M, this branchless version tested as the fastest on 1,000,000,000 random 32-bit integers, edging out a similar version with branches, and a 256-entry LUT version.*/ int ret; int m; ret=!!_v; m=!!(_v&0xFFFF0000)<<4; _v>>=m; ret|=m; m=!!(_v&0xFF00)<<3; _v>>=m; ret|=m; m=!!(_v&0xF0)<<2; _v>>=m; ret|=m; m=!!(_v&0xC)<<1; _v>>=m; ret|=m; ret+=!!(_v&0x2); return ret; } #endif #if 1 /* This is a faster version of ec_tell_frac() that takes advantage of the low (1/8 bit) resolution to use just a linear function followed by a lookup to determine the exact transition thresholds. */ opus_uint32 ec_tell_frac(ec_ctx *_this){ static const unsigned correction[8] = {35733, 38967, 42495, 46340, 50535, 55109, 60097, 65535}; opus_uint32 nbits; opus_uint32 r; int l; unsigned b; nbits=_this->nbits_total<rng); r=_this->rng>>(l-16); b = (r>>12)-8; b += r>correction[b]; l = (l<<3)+b; return nbits-l; } #else opus_uint32 ec_tell_frac(ec_ctx *_this){ opus_uint32 nbits; opus_uint32 r; int l; int i; /*To handle the non-integral number of bits still left in the encoder/decoder state, we compute the worst-case number of bits of val that must be encoded to ensure that the value is inside the range for any possible subsequent bits. The computation here is independent of val itself (the decoder does not even track that value), even though the real number of bits used after ec_enc_done() may be 1 smaller if rng is a power of two and the corresponding trailing bits of val are all zeros. If we did try to track that special case, then coding a value with a probability of 1/(1<nbits_total<rng); r=_this->rng>>(l-16); for(i=BITRES;i-->0;){ int b; r=r*r>>15; b=(int)(r>>16); l=l<<1|b; r>>=b; } return nbits-l; } #endif #ifdef USE_SMALL_DIV_TABLE /* Result of 2^32/(2*i+1), except for i=0. */ const opus_uint32 SMALL_DIV_TABLE[129] = { 0xFFFFFFFF, 0x55555555, 0x33333333, 0x24924924, 0x1C71C71C, 0x1745D174, 0x13B13B13, 0x11111111, 0x0F0F0F0F, 0x0D79435E, 0x0C30C30C, 0x0B21642C, 0x0A3D70A3, 0x097B425E, 0x08D3DCB0, 0x08421084, 0x07C1F07C, 0x07507507, 0x06EB3E45, 0x06906906, 0x063E7063, 0x05F417D0, 0x05B05B05, 0x0572620A, 0x05397829, 0x05050505, 0x04D4873E, 0x04A7904A, 0x047DC11F, 0x0456C797, 0x04325C53, 0x04104104, 0x03F03F03, 0x03D22635, 0x03B5CC0E, 0x039B0AD1, 0x0381C0E0, 0x0369D036, 0x03531DEC, 0x033D91D2, 0x0329161F, 0x03159721, 0x03030303, 0x02F14990, 0x02E05C0B, 0x02D02D02, 0x02C0B02C, 0x02B1DA46, 0x02A3A0FD, 0x0295FAD4, 0x0288DF0C, 0x027C4597, 0x02702702, 0x02647C69, 0x02593F69, 0x024E6A17, 0x0243F6F0, 0x0239E0D5, 0x02302302, 0x0226B902, 0x021D9EAD, 0x0214D021, 0x020C49BA, 0x02040810, 0x01FC07F0, 0x01F44659, 0x01ECC07B, 0x01E573AC, 0x01DE5D6E, 0x01D77B65, 0x01D0CB58, 0x01CA4B30, 0x01C3F8F0, 0x01BDD2B8, 0x01B7D6C3, 0x01B20364, 0x01AC5701, 0x01A6D01A, 0x01A16D3F, 0x019C2D14, 0x01970E4F, 0x01920FB4, 0x018D3018, 0x01886E5F, 0x0183C977, 0x017F405F, 0x017AD220, 0x01767DCE, 0x01724287, 0x016E1F76, 0x016A13CD, 0x01661EC6, 0x01623FA7, 0x015E75BB, 0x015AC056, 0x01571ED3, 0x01539094, 0x01501501, 0x014CAB88, 0x0149539E, 0x01460CBC, 0x0142D662, 0x013FB013, 0x013C995A, 0x013991C2, 0x013698DF, 0x0133AE45, 0x0130D190, 0x012E025C, 0x012B404A, 0x01288B01, 0x0125E227, 0x01234567, 0x0120B470, 0x011E2EF3, 0x011BB4A4, 0x01194538, 0x0116E068, 0x011485F0, 0x0112358E, 0x010FEF01, 0x010DB20A, 0x010B7E6E, 0x010953F3, 0x01073260, 0x0105197F, 0x0103091B, 0x01010101 }; #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/entcode.h ================================================ /* Copyright (c) 2001-2011 Timothy B. Terriberry Copyright (c) 2008-2009 Xiph.Org Foundation */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "opus_types.h" #include "opus_defines.h" #if !defined(_entcode_H) # define _entcode_H (1) # include # include # include "ecintrin.h" extern const opus_uint32 SMALL_DIV_TABLE[129]; #ifdef OPUS_ARM_ASM #define USE_SMALL_DIV_TABLE #endif /*OPT: ec_window must be at least 32 bits, but if you have fast arithmetic on a larger type, you can speed up the decoder by using it here.*/ typedef opus_uint32 ec_window; typedef struct ec_ctx ec_ctx; typedef struct ec_ctx ec_enc; typedef struct ec_ctx ec_dec; # define EC_WINDOW_SIZE ((int)sizeof(ec_window)*CHAR_BIT) /*The number of bits to use for the range-coded part of unsigned integers.*/ # define EC_UINT_BITS (8) /*The resolution of fractional-precision bit usage measurements, i.e., 3 => 1/8th bits.*/ # define BITRES 3 /*The entropy encoder/decoder context. We use the same structure for both, so that common functions like ec_tell() can be used on either one.*/ struct ec_ctx{ /*Buffered input/output.*/ unsigned char *buf; /*The size of the buffer.*/ opus_uint32 storage; /*The offset at which the last byte containing raw bits was read/written.*/ opus_uint32 end_offs; /*Bits that will be read from/written at the end.*/ ec_window end_window; /*Number of valid bits in end_window.*/ int nend_bits; /*The total number of whole bits read/written. This does not include partial bits currently in the range coder.*/ int nbits_total; /*The offset at which the next range coder byte will be read/written.*/ opus_uint32 offs; /*The number of values in the current range.*/ opus_uint32 rng; /*In the decoder: the difference between the top of the current range and the input value, minus one. In the encoder: the low end of the current range.*/ opus_uint32 val; /*In the decoder: the saved normalization factor from ec_decode(). In the encoder: the number of oustanding carry propagating symbols.*/ opus_uint32 ext; /*A buffered input/output symbol, awaiting carry propagation.*/ int rem; /*Nonzero if an error occurred.*/ int error; }; static OPUS_INLINE opus_uint32 ec_range_bytes(ec_ctx *_this){ return _this->offs; } static OPUS_INLINE unsigned char *ec_get_buffer(ec_ctx *_this){ return _this->buf; } static OPUS_INLINE int ec_get_error(ec_ctx *_this){ return _this->error; } /*Returns the number of bits "used" by the encoded or decoded symbols so far. This same number can be computed in either the encoder or the decoder, and is suitable for making coding decisions. Return: The number of bits. This will always be slightly larger than the exact value (e.g., all rounding error is in the positive direction).*/ static OPUS_INLINE int ec_tell(ec_ctx *_this){ return _this->nbits_total-EC_ILOG(_this->rng); } /*Returns the number of bits "used" by the encoded or decoded symbols so far. This same number can be computed in either the encoder or the decoder, and is suitable for making coding decisions. Return: The number of bits scaled by 2**BITRES. This will always be slightly larger than the exact value (e.g., all rounding error is in the positive direction).*/ opus_uint32 ec_tell_frac(ec_ctx *_this); /* Tested exhaustively for all n and for 1<=d<=256 */ static OPUS_INLINE opus_uint32 celt_udiv(opus_uint32 n, opus_uint32 d) { celt_assert(d>0); #ifdef USE_SMALL_DIV_TABLE if (d>256) return n/d; else { opus_uint32 t, q; t = EC_ILOG(d&-d); q = (opus_uint64)SMALL_DIV_TABLE[d>>t]*(n>>(t-1))>>32; return q+(n-q*d >= d); } #else return n/d; #endif } static OPUS_INLINE opus_int32 celt_sudiv(opus_int32 n, opus_int32 d) { celt_assert(d>0); #ifdef USE_SMALL_DIV_TABLE if (n<0) return -(opus_int32)celt_udiv(-n, d); else return celt_udiv(n, d); #else return n/d; #endif } #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/entdec.c ================================================ /* Copyright (c) 2001-2011 Timothy B. Terriberry Copyright (c) 2008-2009 Xiph.Org Foundation */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "os_support.h" #include "arch.h" #include "entdec.h" #include "mfrngcod.h" /*A range decoder. This is an entropy decoder based upon \cite{Mar79}, which is itself a rediscovery of the FIFO arithmetic code introduced by \cite{Pas76}. It is very similar to arithmetic encoding, except that encoding is done with digits in any base, instead of with bits, and so it is faster when using larger bases (i.e.: a byte). The author claims an average waste of $\frac{1}{2}\log_b(2b)$ bits, where $b$ is the base, longer than the theoretical optimum, but to my knowledge there is no published justification for this claim. This only seems true when using near-infinite precision arithmetic so that the process is carried out with no rounding errors. An excellent description of implementation details is available at http://www.arturocampos.com/ac_range.html A recent work \cite{MNW98} which proposes several changes to arithmetic encoding for efficiency actually re-discovers many of the principles behind range encoding, and presents a good theoretical analysis of them. End of stream is handled by writing out the smallest number of bits that ensures that the stream will be correctly decoded regardless of the value of any subsequent bits. ec_tell() can be used to determine how many bits were needed to decode all the symbols thus far; other data can be packed in the remaining bits of the input buffer. @PHDTHESIS{Pas76, author="Richard Clark Pasco", title="Source coding algorithms for fast data compression", school="Dept. of Electrical Engineering, Stanford University", address="Stanford, CA", month=May, year=1976 } @INPROCEEDINGS{Mar79, author="Martin, G.N.N.", title="Range encoding: an algorithm for removing redundancy from a digitised message", booktitle="Video & Data Recording Conference", year=1979, address="Southampton", month=Jul } @ARTICLE{MNW98, author="Alistair Moffat and Radford Neal and Ian H. Witten", title="Arithmetic Coding Revisited", journal="{ACM} Transactions on Information Systems", year=1998, volume=16, number=3, pages="256--294", month=Jul, URL="http://www.stanford.edu/class/ee398a/handouts/papers/Moffat98ArithmCoding.pdf" }*/ static int ec_read_byte(ec_dec *_this){ return _this->offs<_this->storage?_this->buf[_this->offs++]:0; } static int ec_read_byte_from_end(ec_dec *_this){ return _this->end_offs<_this->storage? _this->buf[_this->storage-++(_this->end_offs)]:0; } /*Normalizes the contents of val and rng so that rng lies entirely in the high-order symbol.*/ static void ec_dec_normalize(ec_dec *_this){ /*If the range is too small, rescale it and input some bits.*/ while(_this->rng<=EC_CODE_BOT){ int sym; _this->nbits_total+=EC_SYM_BITS; _this->rng<<=EC_SYM_BITS; /*Use up the remaining bits from our last symbol.*/ sym=_this->rem; /*Read the next value from the input.*/ _this->rem=ec_read_byte(_this); /*Take the rest of the bits we need from this new symbol.*/ sym=(sym<rem)>>(EC_SYM_BITS-EC_CODE_EXTRA); /*And subtract them from val, capped to be less than EC_CODE_TOP.*/ _this->val=((_this->val<buf=_buf; _this->storage=_storage; _this->end_offs=0; _this->end_window=0; _this->nend_bits=0; /*This is the offset from which ec_tell() will subtract partial bits. The final value after the ec_dec_normalize() call will be the same as in the encoder, but we have to compensate for the bits that are added there.*/ _this->nbits_total=EC_CODE_BITS+1 -((EC_CODE_BITS-EC_CODE_EXTRA)/EC_SYM_BITS)*EC_SYM_BITS; _this->offs=0; _this->rng=1U<rem=ec_read_byte(_this); _this->val=_this->rng-1-(_this->rem>>(EC_SYM_BITS-EC_CODE_EXTRA)); _this->error=0; /*Normalize the interval.*/ ec_dec_normalize(_this); } unsigned ec_decode(ec_dec *_this,unsigned _ft){ unsigned s; _this->ext=celt_udiv(_this->rng,_ft); s=(unsigned)(_this->val/_this->ext); return _ft-EC_MINI(s+1,_ft); } unsigned ec_decode_bin(ec_dec *_this,unsigned _bits){ unsigned s; _this->ext=_this->rng>>_bits; s=(unsigned)(_this->val/_this->ext); return (1U<<_bits)-EC_MINI(s+1U,1U<<_bits); } void ec_dec_update(ec_dec *_this,unsigned _fl,unsigned _fh,unsigned _ft){ opus_uint32 s; s=IMUL32(_this->ext,_ft-_fh); _this->val-=s; _this->rng=_fl>0?IMUL32(_this->ext,_fh-_fl):_this->rng-s; ec_dec_normalize(_this); } /*The probability of having a "one" is 1/(1<<_logp).*/ int ec_dec_bit_logp(ec_dec *_this,unsigned _logp){ opus_uint32 r; opus_uint32 d; opus_uint32 s; int ret; r=_this->rng; d=_this->val; s=r>>_logp; ret=dval=d-s; _this->rng=ret?s:r-s; ec_dec_normalize(_this); return ret; } int ec_dec_icdf(ec_dec *_this,const unsigned char *_icdf,unsigned _ftb){ opus_uint32 r; opus_uint32 d; opus_uint32 s; opus_uint32 t; int ret; s=_this->rng; d=_this->val; r=s>>_ftb; ret=-1; do{ t=s; s=IMUL32(r,_icdf[++ret]); } while(dval=d-s; _this->rng=t-s; ec_dec_normalize(_this); return ret; } opus_uint32 ec_dec_uint(ec_dec *_this,opus_uint32 _ft){ unsigned ft; unsigned s; int ftb; /*In order to optimize EC_ILOG(), it is undefined for the value 0.*/ celt_assert(_ft>1); _ft--; ftb=EC_ILOG(_ft); if(ftb>EC_UINT_BITS){ opus_uint32 t; ftb-=EC_UINT_BITS; ft=(unsigned)(_ft>>ftb)+1; s=ec_decode(_this,ft); ec_dec_update(_this,s,s+1,ft); t=(opus_uint32)s<error=1; return _ft; } else{ _ft++; s=ec_decode(_this,(unsigned)_ft); ec_dec_update(_this,s,s+1,(unsigned)_ft); return s; } } opus_uint32 ec_dec_bits(ec_dec *_this,unsigned _bits){ ec_window window; int available; opus_uint32 ret; window=_this->end_window; available=_this->nend_bits; if((unsigned)available<_bits){ do{ window|=(ec_window)ec_read_byte_from_end(_this)<>=_bits; available-=_bits; _this->end_window=window; _this->nend_bits=available; _this->nbits_total+=_bits; return ret; } ================================================ FILE: deps/pjsip/third_party/opus/celt/entdec.h ================================================ /* Copyright (c) 2001-2011 Timothy B. Terriberry Copyright (c) 2008-2009 Xiph.Org Foundation */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if !defined(_entdec_H) # define _entdec_H (1) # include # include "entcode.h" /*Initializes the decoder. _buf: The input buffer to use. Return: 0 on success, or a negative value on error.*/ void ec_dec_init(ec_dec *_this,unsigned char *_buf,opus_uint32 _storage); /*Calculates the cumulative frequency for the next symbol. This can then be fed into the probability model to determine what that symbol is, and the additional frequency information required to advance to the next symbol. This function cannot be called more than once without a corresponding call to ec_dec_update(), or decoding will not proceed correctly. _ft: The total frequency of the symbols in the alphabet the next symbol was encoded with. Return: A cumulative frequency representing the encoded symbol. If the cumulative frequency of all the symbols before the one that was encoded was fl, and the cumulative frequency of all the symbols up to and including the one encoded is fh, then the returned value will fall in the range [fl,fh).*/ unsigned ec_decode(ec_dec *_this,unsigned _ft); /*Equivalent to ec_decode() with _ft==1<<_bits.*/ unsigned ec_decode_bin(ec_dec *_this,unsigned _bits); /*Advance the decoder past the next symbol using the frequency information the symbol was encoded with. Exactly one call to ec_decode() must have been made so that all necessary intermediate calculations are performed. _fl: The cumulative frequency of all symbols that come before the symbol decoded. _fh: The cumulative frequency of all symbols up to and including the symbol decoded. Together with _fl, this defines the range [_fl,_fh) in which the value returned above must fall. _ft: The total frequency of the symbols in the alphabet the symbol decoded was encoded in. This must be the same as passed to the preceding call to ec_decode().*/ void ec_dec_update(ec_dec *_this,unsigned _fl,unsigned _fh,unsigned _ft); /* Decode a bit that has a 1/(1<<_logp) probability of being a one */ int ec_dec_bit_logp(ec_dec *_this,unsigned _logp); /*Decodes a symbol given an "inverse" CDF table. No call to ec_dec_update() is necessary after this call. _icdf: The "inverse" CDF, such that symbol s falls in the range [s>0?ft-_icdf[s-1]:0,ft-_icdf[s]), where ft=1<<_ftb. The values must be monotonically non-increasing, and the last value must be 0. _ftb: The number of bits of precision in the cumulative distribution. Return: The decoded symbol s.*/ int ec_dec_icdf(ec_dec *_this,const unsigned char *_icdf,unsigned _ftb); /*Extracts a raw unsigned integer with a non-power-of-2 range from the stream. The bits must have been encoded with ec_enc_uint(). No call to ec_dec_update() is necessary after this call. _ft: The number of integers that can be decoded (one more than the max). This must be at least one, and no more than 2**32-1. Return: The decoded bits.*/ opus_uint32 ec_dec_uint(ec_dec *_this,opus_uint32 _ft); /*Extracts a sequence of raw bits from the stream. The bits must have been encoded with ec_enc_bits(). No call to ec_dec_update() is necessary after this call. _ftb: The number of bits to extract. This must be between 0 and 25, inclusive. Return: The decoded bits.*/ opus_uint32 ec_dec_bits(ec_dec *_this,unsigned _ftb); #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/entenc.c ================================================ /* Copyright (c) 2001-2011 Timothy B. Terriberry Copyright (c) 2008-2009 Xiph.Org Foundation */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(HAVE_CONFIG_H) # include "config.h" #endif #include "os_support.h" #include "arch.h" #include "entenc.h" #include "mfrngcod.h" /*A range encoder. See entdec.c and the references for implementation details \cite{Mar79,MNW98}. @INPROCEEDINGS{Mar79, author="Martin, G.N.N.", title="Range encoding: an algorithm for removing redundancy from a digitised message", booktitle="Video \& Data Recording Conference", year=1979, address="Southampton", month=Jul } @ARTICLE{MNW98, author="Alistair Moffat and Radford Neal and Ian H. Witten", title="Arithmetic Coding Revisited", journal="{ACM} Transactions on Information Systems", year=1998, volume=16, number=3, pages="256--294", month=Jul, URL="http://www.stanford.edu/class/ee398/handouts/papers/Moffat98ArithmCoding.pdf" }*/ static int ec_write_byte(ec_enc *_this,unsigned _value){ if(_this->offs+_this->end_offs>=_this->storage)return -1; _this->buf[_this->offs++]=(unsigned char)_value; return 0; } static int ec_write_byte_at_end(ec_enc *_this,unsigned _value){ if(_this->offs+_this->end_offs>=_this->storage)return -1; _this->buf[_this->storage-++(_this->end_offs)]=(unsigned char)_value; return 0; } /*Outputs a symbol, with a carry bit. If there is a potential to propagate a carry over several symbols, they are buffered until it can be determined whether or not an actual carry will occur. If the counter for the buffered symbols overflows, then the stream becomes undecodable. This gives a theoretical limit of a few billion symbols in a single packet on 32-bit systems. The alternative is to truncate the range in order to force a carry, but requires similar carry tracking in the decoder, needlessly slowing it down.*/ static void ec_enc_carry_out(ec_enc *_this,int _c){ if(_c!=EC_SYM_MAX){ /*No further carry propagation possible, flush buffer.*/ int carry; carry=_c>>EC_SYM_BITS; /*Don't output a byte on the first write. This compare should be taken care of by branch-prediction thereafter.*/ if(_this->rem>=0)_this->error|=ec_write_byte(_this,_this->rem+carry); if(_this->ext>0){ unsigned sym; sym=(EC_SYM_MAX+carry)&EC_SYM_MAX; do _this->error|=ec_write_byte(_this,sym); while(--(_this->ext)>0); } _this->rem=_c&EC_SYM_MAX; } else _this->ext++; } static OPUS_INLINE void ec_enc_normalize(ec_enc *_this){ /*If the range is too small, output some bits and rescale it.*/ while(_this->rng<=EC_CODE_BOT){ ec_enc_carry_out(_this,(int)(_this->val>>EC_CODE_SHIFT)); /*Move the next-to-high-order symbol into the high-order position.*/ _this->val=(_this->val<rng<<=EC_SYM_BITS; _this->nbits_total+=EC_SYM_BITS; } } void ec_enc_init(ec_enc *_this,unsigned char *_buf,opus_uint32 _size){ _this->buf=_buf; _this->end_offs=0; _this->end_window=0; _this->nend_bits=0; /*This is the offset from which ec_tell() will subtract partial bits.*/ _this->nbits_total=EC_CODE_BITS+1; _this->offs=0; _this->rng=EC_CODE_TOP; _this->rem=-1; _this->val=0; _this->ext=0; _this->storage=_size; _this->error=0; } void ec_encode(ec_enc *_this,unsigned _fl,unsigned _fh,unsigned _ft){ opus_uint32 r; r=celt_udiv(_this->rng,_ft); if(_fl>0){ _this->val+=_this->rng-IMUL32(r,(_ft-_fl)); _this->rng=IMUL32(r,(_fh-_fl)); } else _this->rng-=IMUL32(r,(_ft-_fh)); ec_enc_normalize(_this); } void ec_encode_bin(ec_enc *_this,unsigned _fl,unsigned _fh,unsigned _bits){ opus_uint32 r; r=_this->rng>>_bits; if(_fl>0){ _this->val+=_this->rng-IMUL32(r,((1U<<_bits)-_fl)); _this->rng=IMUL32(r,(_fh-_fl)); } else _this->rng-=IMUL32(r,((1U<<_bits)-_fh)); ec_enc_normalize(_this); } /*The probability of having a "one" is 1/(1<<_logp).*/ void ec_enc_bit_logp(ec_enc *_this,int _val,unsigned _logp){ opus_uint32 r; opus_uint32 s; opus_uint32 l; r=_this->rng; l=_this->val; s=r>>_logp; r-=s; if(_val)_this->val=l+r; _this->rng=_val?s:r; ec_enc_normalize(_this); } void ec_enc_icdf(ec_enc *_this,int _s,const unsigned char *_icdf,unsigned _ftb){ opus_uint32 r; r=_this->rng>>_ftb; if(_s>0){ _this->val+=_this->rng-IMUL32(r,_icdf[_s-1]); _this->rng=IMUL32(r,_icdf[_s-1]-_icdf[_s]); } else _this->rng-=IMUL32(r,_icdf[_s]); ec_enc_normalize(_this); } void ec_enc_uint(ec_enc *_this,opus_uint32 _fl,opus_uint32 _ft){ unsigned ft; unsigned fl; int ftb; /*In order to optimize EC_ILOG(), it is undefined for the value 0.*/ celt_assert(_ft>1); _ft--; ftb=EC_ILOG(_ft); if(ftb>EC_UINT_BITS){ ftb-=EC_UINT_BITS; ft=(_ft>>ftb)+1; fl=(unsigned)(_fl>>ftb); ec_encode(_this,fl,fl+1,ft); ec_enc_bits(_this,_fl&(((opus_uint32)1<end_window; used=_this->nend_bits; celt_assert(_bits>0); if(used+_bits>EC_WINDOW_SIZE){ do{ _this->error|=ec_write_byte_at_end(_this,(unsigned)window&EC_SYM_MAX); window>>=EC_SYM_BITS; used-=EC_SYM_BITS; } while(used>=EC_SYM_BITS); } window|=(ec_window)_fl<end_window=window; _this->nend_bits=used; _this->nbits_total+=_bits; } void ec_enc_patch_initial_bits(ec_enc *_this,unsigned _val,unsigned _nbits){ int shift; unsigned mask; celt_assert(_nbits<=EC_SYM_BITS); shift=EC_SYM_BITS-_nbits; mask=((1<<_nbits)-1)<offs>0){ /*The first byte has been finalized.*/ _this->buf[0]=(unsigned char)((_this->buf[0]&~mask)|_val<rem>=0){ /*The first byte is still awaiting carry propagation.*/ _this->rem=(_this->rem&~mask)|_val<rng<=(EC_CODE_TOP>>_nbits)){ /*The renormalization loop has never been run.*/ _this->val=(_this->val&~((opus_uint32)mask<error=-1; } void ec_enc_shrink(ec_enc *_this,opus_uint32 _size){ celt_assert(_this->offs+_this->end_offs<=_size); OPUS_MOVE(_this->buf+_size-_this->end_offs, _this->buf+_this->storage-_this->end_offs,_this->end_offs); _this->storage=_size; } void ec_enc_done(ec_enc *_this){ ec_window window; int used; opus_uint32 msk; opus_uint32 end; int l; /*We output the minimum number of bits that ensures that the symbols encoded thus far will be decoded correctly regardless of the bits that follow.*/ l=EC_CODE_BITS-EC_ILOG(_this->rng); msk=(EC_CODE_TOP-1)>>l; end=(_this->val+msk)&~msk; if((end|msk)>=_this->val+_this->rng){ l++; msk>>=1; end=(_this->val+msk)&~msk; } while(l>0){ ec_enc_carry_out(_this,(int)(end>>EC_CODE_SHIFT)); end=(end<rem>=0||_this->ext>0)ec_enc_carry_out(_this,0); /*If we have buffered extra bits, flush them as well.*/ window=_this->end_window; used=_this->nend_bits; while(used>=EC_SYM_BITS){ _this->error|=ec_write_byte_at_end(_this,(unsigned)window&EC_SYM_MAX); window>>=EC_SYM_BITS; used-=EC_SYM_BITS; } /*Clear any excess space and add any remaining extra bits to the last byte.*/ if(!_this->error){ OPUS_CLEAR(_this->buf+_this->offs, _this->storage-_this->offs-_this->end_offs); if(used>0){ /*If there's no range coder data at all, give up.*/ if(_this->end_offs>=_this->storage)_this->error=-1; else{ l=-l; /*If we've busted, don't add too many extra bits to the last byte; it would corrupt the range coder data, and that's more important.*/ if(_this->offs+_this->end_offs>=_this->storage&&lerror=-1; } _this->buf[_this->storage-_this->end_offs-1]|=(unsigned char)window; } } } } ================================================ FILE: deps/pjsip/third_party/opus/celt/entenc.h ================================================ /* Copyright (c) 2001-2011 Timothy B. Terriberry Copyright (c) 2008-2009 Xiph.Org Foundation */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if !defined(_entenc_H) # define _entenc_H (1) # include # include "entcode.h" /*Initializes the encoder. _buf: The buffer to store output bytes in. _size: The size of the buffer, in chars.*/ void ec_enc_init(ec_enc *_this,unsigned char *_buf,opus_uint32 _size); /*Encodes a symbol given its frequency information. The frequency information must be discernable by the decoder, assuming it has read only the previous symbols from the stream. It is allowable to change the frequency information, or even the entire source alphabet, so long as the decoder can tell from the context of the previously encoded information that it is supposed to do so as well. _fl: The cumulative frequency of all symbols that come before the one to be encoded. _fh: The cumulative frequency of all symbols up to and including the one to be encoded. Together with _fl, this defines the range [_fl,_fh) in which the decoded value will fall. _ft: The sum of the frequencies of all the symbols*/ void ec_encode(ec_enc *_this,unsigned _fl,unsigned _fh,unsigned _ft); /*Equivalent to ec_encode() with _ft==1<<_bits.*/ void ec_encode_bin(ec_enc *_this,unsigned _fl,unsigned _fh,unsigned _bits); /* Encode a bit that has a 1/(1<<_logp) probability of being a one */ void ec_enc_bit_logp(ec_enc *_this,int _val,unsigned _logp); /*Encodes a symbol given an "inverse" CDF table. _s: The index of the symbol to encode. _icdf: The "inverse" CDF, such that symbol _s falls in the range [_s>0?ft-_icdf[_s-1]:0,ft-_icdf[_s]), where ft=1<<_ftb. The values must be monotonically non-increasing, and the last value must be 0. _ftb: The number of bits of precision in the cumulative distribution.*/ void ec_enc_icdf(ec_enc *_this,int _s,const unsigned char *_icdf,unsigned _ftb); /*Encodes a raw unsigned integer in the stream. _fl: The integer to encode. _ft: The number of integers that can be encoded (one more than the max). This must be at least one, and no more than 2**32-1.*/ void ec_enc_uint(ec_enc *_this,opus_uint32 _fl,opus_uint32 _ft); /*Encodes a sequence of raw bits in the stream. _fl: The bits to encode. _ftb: The number of bits to encode. This must be between 1 and 25, inclusive.*/ void ec_enc_bits(ec_enc *_this,opus_uint32 _fl,unsigned _ftb); /*Overwrites a few bits at the very start of an existing stream, after they have already been encoded. This makes it possible to have a few flags up front, where it is easy for decoders to access them without parsing the whole stream, even if their values are not determined until late in the encoding process, without having to buffer all the intermediate symbols in the encoder. In order for this to work, at least _nbits bits must have already been encoded using probabilities that are an exact power of two. The encoder can verify the number of encoded bits is sufficient, but cannot check this latter condition. _val: The bits to encode (in the least _nbits significant bits). They will be decoded in order from most-significant to least. _nbits: The number of bits to overwrite. This must be no more than 8.*/ void ec_enc_patch_initial_bits(ec_enc *_this,unsigned _val,unsigned _nbits); /*Compacts the data to fit in the target size. This moves up the raw bits at the end of the current buffer so they are at the end of the new buffer size. The caller must ensure that the amount of data that's already been written will fit in the new size. _size: The number of bytes in the new buffer. This must be large enough to contain the bits already written, and must be no larger than the existing size.*/ void ec_enc_shrink(ec_enc *_this,opus_uint32 _size); /*Indicates that there are no more symbols to encode. All reamining output bytes are flushed to the output buffer. ec_enc_init() must be called before the encoder can be used again.*/ void ec_enc_done(ec_enc *_this); #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/fixed_debug.h ================================================ /* Copyright (C) 2003-2008 Jean-Marc Valin Copyright (C) 2007-2012 Xiph.Org Foundation */ /** @file fixed_debug.h @brief Fixed-point operations with debugging */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FIXED_DEBUG_H #define FIXED_DEBUG_H #include #include "opus_defines.h" #ifdef CELT_C OPUS_EXPORT opus_int64 celt_mips=0; #else extern opus_int64 celt_mips; #endif #define MULT16_16SU(a,b) ((opus_val32)(opus_val16)(a)*(opus_val32)(opus_uint16)(b)) #define MULT32_32_Q31(a,b) ADD32(ADD32(SHL32(MULT16_16(SHR32((a),16),SHR((b),16)),1), SHR32(MULT16_16SU(SHR32((a),16),((b)&0x0000ffff)),15)), SHR32(MULT16_16SU(SHR32((b),16),((a)&0x0000ffff)),15)) /** 16x32 multiplication, followed by a 16-bit shift right. Results fits in 32 bits */ #define MULT16_32_Q16(a,b) ADD32(MULT16_16((a),SHR32((b),16)), SHR32(MULT16_16SU((a),((b)&0x0000ffff)),16)) #define MULT16_32_P16(a,b) MULT16_32_PX(a,b,16) #define QCONST16(x,bits) ((opus_val16)(.5+(x)*(((opus_val32)1)<<(bits)))) #define QCONST32(x,bits) ((opus_val32)(.5+(x)*(((opus_val32)1)<<(bits)))) #define VERIFY_SHORT(x) ((x)<=32767&&(x)>=-32768) #define VERIFY_INT(x) ((x)<=2147483647LL&&(x)>=-2147483648LL) #define VERIFY_UINT(x) ((x)<=(2147483647LLU<<1)) #define SHR(a,b) SHR32(a,b) #define PSHR(a,b) PSHR32(a,b) static OPUS_INLINE short NEG16(int x) { int res; if (!VERIFY_SHORT(x)) { fprintf (stderr, "NEG16: input is not short: %d\n", (int)x); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = -x; if (!VERIFY_SHORT(res)) { fprintf (stderr, "NEG16: output is not short: %d\n", (int)res); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips++; return res; } static OPUS_INLINE int NEG32(opus_int64 x) { opus_int64 res; if (!VERIFY_INT(x)) { fprintf (stderr, "NEG16: input is not int: %d\n", (int)x); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = -x; if (!VERIFY_INT(res)) { fprintf (stderr, "NEG16: output is not int: %d\n", (int)res); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips+=2; return res; } #define EXTRACT16(x) EXTRACT16_(x, __FILE__, __LINE__) static OPUS_INLINE short EXTRACT16_(int x, char *file, int line) { int res; if (!VERIFY_SHORT(x)) { fprintf (stderr, "EXTRACT16: input is not short: %d in %s: line %d\n", x, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = x; celt_mips++; return res; } #define EXTEND32(x) EXTEND32_(x, __FILE__, __LINE__) static OPUS_INLINE int EXTEND32_(int x, char *file, int line) { int res; if (!VERIFY_SHORT(x)) { fprintf (stderr, "EXTEND32: input is not short: %d in %s: line %d\n", x, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = x; celt_mips++; return res; } #define SHR16(a, shift) SHR16_(a, shift, __FILE__, __LINE__) static OPUS_INLINE short SHR16_(int a, int shift, char *file, int line) { int res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(shift)) { fprintf (stderr, "SHR16: inputs are not short: %d >> %d in %s: line %d\n", a, shift, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = a>>shift; if (!VERIFY_SHORT(res)) { fprintf (stderr, "SHR16: output is not short: %d in %s: line %d\n", res, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips++; return res; } #define SHL16(a, shift) SHL16_(a, shift, __FILE__, __LINE__) static OPUS_INLINE short SHL16_(int a, int shift, char *file, int line) { int res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(shift)) { fprintf (stderr, "SHL16: inputs are not short: %d %d in %s: line %d\n", a, shift, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = a<>shift; if (!VERIFY_INT(res)) { fprintf (stderr, "SHR32: output is not int: %d\n", (int)res); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips+=2; return res; } #define SHL32(a, shift) SHL32_(a, shift, __FILE__, __LINE__) static OPUS_INLINE int SHL32_(opus_int64 a, int shift, char *file, int line) { opus_int64 res; if (!VERIFY_INT(a) || !VERIFY_SHORT(shift)) { fprintf (stderr, "SHL32: inputs are not int: %lld %d in %s: line %d\n", a, shift, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = a<>1))),shift)) #define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift))) #define ROUND16(x,a) (celt_mips--,EXTRACT16(PSHR32((x),(a)))) #define HALF16(x) (SHR16(x,1)) #define HALF32(x) (SHR32(x,1)) //#define SHR(a,shift) ((a) >> (shift)) //#define SHL(a,shift) ((a) << (shift)) #define ADD16(a, b) ADD16_(a, b, __FILE__, __LINE__) static OPUS_INLINE short ADD16_(int a, int b, char *file, int line) { int res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "ADD16: inputs are not short: %d %d in %s: line %d\n", a, b, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = a+b; if (!VERIFY_SHORT(res)) { fprintf (stderr, "ADD16: output is not short: %d+%d=%d in %s: line %d\n", a,b,res, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips++; return res; } #define SUB16(a, b) SUB16_(a, b, __FILE__, __LINE__) static OPUS_INLINE short SUB16_(int a, int b, char *file, int line) { int res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "SUB16: inputs are not short: %d %d in %s: line %d\n", a, b, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = a-b; if (!VERIFY_SHORT(res)) { fprintf (stderr, "SUB16: output is not short: %d in %s: line %d\n", res, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips++; return res; } #define ADD32(a, b) ADD32_(a, b, __FILE__, __LINE__) static OPUS_INLINE int ADD32_(opus_int64 a, opus_int64 b, char *file, int line) { opus_int64 res; if (!VERIFY_INT(a) || !VERIFY_INT(b)) { fprintf (stderr, "ADD32: inputs are not int: %d %d in %s: line %d\n", (int)a, (int)b, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = a+b; if (!VERIFY_INT(res)) { fprintf (stderr, "ADD32: output is not int: %d in %s: line %d\n", (int)res, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips+=2; return res; } #define SUB32(a, b) SUB32_(a, b, __FILE__, __LINE__) static OPUS_INLINE int SUB32_(opus_int64 a, opus_int64 b, char *file, int line) { opus_int64 res; if (!VERIFY_INT(a) || !VERIFY_INT(b)) { fprintf (stderr, "SUB32: inputs are not int: %d %d in %s: line %d\n", (int)a, (int)b, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = a-b; if (!VERIFY_INT(res)) { fprintf (stderr, "SUB32: output is not int: %d in %s: line %d\n", (int)res, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips+=2; return res; } #undef UADD32 #define UADD32(a, b) UADD32_(a, b, __FILE__, __LINE__) static OPUS_INLINE unsigned int UADD32_(opus_uint64 a, opus_uint64 b, char *file, int line) { opus_uint64 res; if (!VERIFY_UINT(a) || !VERIFY_UINT(b)) { fprintf (stderr, "UADD32: inputs are not uint32: %llu %llu in %s: line %d\n", a, b, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = a+b; if (!VERIFY_UINT(res)) { fprintf (stderr, "UADD32: output is not uint32: %llu in %s: line %d\n", res, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips+=2; return res; } #undef USUB32 #define USUB32(a, b) USUB32_(a, b, __FILE__, __LINE__) static OPUS_INLINE unsigned int USUB32_(opus_uint64 a, opus_uint64 b, char *file, int line) { opus_uint64 res; if (!VERIFY_UINT(a) || !VERIFY_UINT(b)) { fprintf (stderr, "USUB32: inputs are not uint32: %llu %llu in %s: line %d\n", a, b, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } if (a=((opus_val32)(1)<<(15+Q))) { fprintf (stderr, "MULT16_32_Q%d: second operand too large: %d %d in %s: line %d\n", Q, (int)a, (int)b, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = (((opus_int64)a)*(opus_int64)b) >> Q; if (!VERIFY_INT(res)) { fprintf (stderr, "MULT16_32_Q%d: output is not int: %d*%d=%d in %s: line %d\n", Q, (int)a, (int)b,(int)res, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } if (Q==15) celt_mips+=3; else celt_mips+=4; return res; } #define MULT16_32_PX(a, b, Q) MULT16_32_PX_(a, b, Q, __FILE__, __LINE__) static OPUS_INLINE int MULT16_32_PX_(int a, opus_int64 b, int Q, char *file, int line) { opus_int64 res; if (!VERIFY_SHORT(a) || !VERIFY_INT(b)) { fprintf (stderr, "MULT16_32_P%d: inputs are not short+int: %d %d in %s: line %d\n\n", Q, (int)a, (int)b, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } if (ABS32(b)>=((opus_int64)(1)<<(15+Q))) { fprintf (stderr, "MULT16_32_Q%d: second operand too large: %d %d in %s: line %d\n\n", Q, (int)a, (int)b,file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = ((((opus_int64)a)*(opus_int64)b) + (((opus_val32)(1)<>1))>> Q; if (!VERIFY_INT(res)) { fprintf (stderr, "MULT16_32_P%d: output is not int: %d*%d=%d in %s: line %d\n\n", Q, (int)a, (int)b,(int)res, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } if (Q==15) celt_mips+=4; else celt_mips+=5; return res; } #define MULT16_32_Q15(a,b) MULT16_32_QX(a,b,15) #define MAC16_32_Q15(c,a,b) (celt_mips-=2,ADD32((c),MULT16_32_Q15((a),(b)))) #define MAC16_32_Q16(c,a,b) (celt_mips-=2,ADD32((c),MULT16_32_Q16((a),(b)))) static OPUS_INLINE int SATURATE(int a, int b) { if (a>b) a=b; if (a<-b) a = -b; celt_mips+=3; return a; } static OPUS_INLINE opus_int16 SATURATE16(opus_int32 a) { celt_mips+=3; if (a>32767) return 32767; else if (a<-32768) return -32768; else return a; } static OPUS_INLINE int MULT16_16_Q11_32(int a, int b) { opus_int64 res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_Q11: inputs are not short: %d %d\n", a, b); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = ((opus_int64)a)*b; res >>= 11; if (!VERIFY_INT(res)) { fprintf (stderr, "MULT16_16_Q11: output is not short: %d*%d=%d\n", (int)a, (int)b, (int)res); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips+=3; return res; } static OPUS_INLINE short MULT16_16_Q13(int a, int b) { opus_int64 res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_Q13: inputs are not short: %d %d\n", a, b); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = ((opus_int64)a)*b; res >>= 13; if (!VERIFY_SHORT(res)) { fprintf (stderr, "MULT16_16_Q13: output is not short: %d*%d=%d\n", a, b, (int)res); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips+=3; return res; } static OPUS_INLINE short MULT16_16_Q14(int a, int b) { opus_int64 res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_Q14: inputs are not short: %d %d\n", a, b); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = ((opus_int64)a)*b; res >>= 14; if (!VERIFY_SHORT(res)) { fprintf (stderr, "MULT16_16_Q14: output is not short: %d\n", (int)res); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips+=3; return res; } #define MULT16_16_Q15(a, b) MULT16_16_Q15_(a, b, __FILE__, __LINE__) static OPUS_INLINE short MULT16_16_Q15_(int a, int b, char *file, int line) { opus_int64 res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_Q15: inputs are not short: %d %d in %s: line %d\n", a, b, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = ((opus_int64)a)*b; res >>= 15; if (!VERIFY_SHORT(res)) { fprintf (stderr, "MULT16_16_Q15: output is not short: %d in %s: line %d\n", (int)res, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips+=1; return res; } static OPUS_INLINE short MULT16_16_P13(int a, int b) { opus_int64 res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_P13: inputs are not short: %d %d\n", a, b); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = ((opus_int64)a)*b; res += 4096; if (!VERIFY_INT(res)) { fprintf (stderr, "MULT16_16_P13: overflow: %d*%d=%d\n", a, b, (int)res); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res >>= 13; if (!VERIFY_SHORT(res)) { fprintf (stderr, "MULT16_16_P13: output is not short: %d*%d=%d\n", a, b, (int)res); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips+=4; return res; } static OPUS_INLINE short MULT16_16_P14(int a, int b) { opus_int64 res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_P14: inputs are not short: %d %d\n", a, b); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = ((opus_int64)a)*b; res += 8192; if (!VERIFY_INT(res)) { fprintf (stderr, "MULT16_16_P14: overflow: %d*%d=%d\n", a, b, (int)res); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res >>= 14; if (!VERIFY_SHORT(res)) { fprintf (stderr, "MULT16_16_P14: output is not short: %d*%d=%d\n", a, b, (int)res); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips+=4; return res; } static OPUS_INLINE short MULT16_16_P15(int a, int b) { opus_int64 res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_P15: inputs are not short: %d %d\n", a, b); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = ((opus_int64)a)*b; res += 16384; if (!VERIFY_INT(res)) { fprintf (stderr, "MULT16_16_P15: overflow: %d*%d=%d\n", a, b, (int)res); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res >>= 15; if (!VERIFY_SHORT(res)) { fprintf (stderr, "MULT16_16_P15: output is not short: %d*%d=%d\n", a, b, (int)res); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips+=2; return res; } #define DIV32_16(a, b) DIV32_16_(a, b, __FILE__, __LINE__) static OPUS_INLINE int DIV32_16_(opus_int64 a, opus_int64 b, char *file, int line) { opus_int64 res; if (b==0) { fprintf(stderr, "DIV32_16: divide by zero: %d/%d in %s: line %d\n", (int)a, (int)b, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif return 0; } if (!VERIFY_INT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "DIV32_16: inputs are not int/short: %d %d in %s: line %d\n", (int)a, (int)b, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = a/b; if (!VERIFY_SHORT(res)) { fprintf (stderr, "DIV32_16: output is not short: %d / %d = %d in %s: line %d\n", (int)a,(int)b,(int)res, file, line); if (res>32767) res = 32767; if (res<-32768) res = -32768; #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips+=35; return res; } #define DIV32(a, b) DIV32_(a, b, __FILE__, __LINE__) static OPUS_INLINE int DIV32_(opus_int64 a, opus_int64 b, char *file, int line) { opus_int64 res; if (b==0) { fprintf(stderr, "DIV32: divide by zero: %d/%d in %s: line %d\n", (int)a, (int)b, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif return 0; } if (!VERIFY_INT(a) || !VERIFY_INT(b)) { fprintf (stderr, "DIV32: inputs are not int/short: %d %d in %s: line %d\n", (int)a, (int)b, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } res = a/b; if (!VERIFY_INT(res)) { fprintf (stderr, "DIV32: output is not int: %d in %s: line %d\n", (int)res, file, line); #ifdef FIXED_DEBUG_ASSERT celt_assert(0); #endif } celt_mips+=70; return res; } static OPUS_INLINE opus_val16 SIG2WORD16_generic(celt_sig x) { x = PSHR32(x, SIG_SHIFT); x = MAX32(x, -32768); x = MIN32(x, 32767); return EXTRACT16(x); } #define SIG2WORD16(x) (SIG2WORD16_generic(x)) #undef PRINT_MIPS #define PRINT_MIPS(file) do {fprintf (file, "total complexity = %llu MIPS\n", celt_mips);} while (0); #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/fixed_generic.h ================================================ /* Copyright (C) 2007-2009 Xiph.Org Foundation Copyright (C) 2003-2008 Jean-Marc Valin Copyright (C) 2007-2008 CSIRO */ /** @file fixed_generic.h @brief Generic fixed-point operations */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FIXED_GENERIC_H #define FIXED_GENERIC_H /** Multiply a 16-bit signed value by a 16-bit unsigned value. The result is a 32-bit signed value */ #define MULT16_16SU(a,b) ((opus_val32)(opus_val16)(a)*(opus_val32)(opus_uint16)(b)) /** 16x32 multiplication, followed by a 16-bit shift right. Results fits in 32 bits */ #define MULT16_32_Q16(a,b) ADD32(MULT16_16((a),SHR((b),16)), SHR(MULT16_16SU((a),((b)&0x0000ffff)),16)) /** 16x32 multiplication, followed by a 16-bit shift right (round-to-nearest). Results fits in 32 bits */ #define MULT16_32_P16(a,b) ADD32(MULT16_16((a),SHR((b),16)), PSHR(MULT16_16SU((a),((b)&0x0000ffff)),16)) /** 16x32 multiplication, followed by a 15-bit shift right. Results fits in 32 bits */ #define MULT16_32_Q15(a,b) ADD32(SHL(MULT16_16((a),SHR((b),16)),1), SHR(MULT16_16SU((a),((b)&0x0000ffff)),15)) /** 32x32 multiplication, followed by a 31-bit shift right. Results fits in 32 bits */ #define MULT32_32_Q31(a,b) ADD32(ADD32(SHL(MULT16_16(SHR((a),16),SHR((b),16)),1), SHR(MULT16_16SU(SHR((a),16),((b)&0x0000ffff)),15)), SHR(MULT16_16SU(SHR((b),16),((a)&0x0000ffff)),15)) /** Compile-time conversion of float constant to 16-bit value */ #define QCONST16(x,bits) ((opus_val16)(.5+(x)*(((opus_val32)1)<<(bits)))) /** Compile-time conversion of float constant to 32-bit value */ #define QCONST32(x,bits) ((opus_val32)(.5+(x)*(((opus_val32)1)<<(bits)))) /** Negate a 16-bit value */ #define NEG16(x) (-(x)) /** Negate a 32-bit value */ #define NEG32(x) (-(x)) /** Change a 32-bit value into a 16-bit value. The value is assumed to fit in 16-bit, otherwise the result is undefined */ #define EXTRACT16(x) ((opus_val16)(x)) /** Change a 16-bit value into a 32-bit value */ #define EXTEND32(x) ((opus_val32)(x)) /** Arithmetic shift-right of a 16-bit value */ #define SHR16(a,shift) ((a) >> (shift)) /** Arithmetic shift-left of a 16-bit value */ #define SHL16(a,shift) ((opus_int16)((opus_uint16)(a)<<(shift))) /** Arithmetic shift-right of a 32-bit value */ #define SHR32(a,shift) ((a) >> (shift)) /** Arithmetic shift-left of a 32-bit value */ #define SHL32(a,shift) ((opus_int32)((opus_uint32)(a)<<(shift))) /** 32-bit arithmetic shift right with rounding-to-nearest instead of rounding down */ #define PSHR32(a,shift) (SHR32((a)+((EXTEND32(1)<<((shift))>>1)),shift)) /** 32-bit arithmetic shift right where the argument can be negative */ #define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift))) /** "RAW" macros, should not be used outside of this header file */ #define SHR(a,shift) ((a) >> (shift)) #define SHL(a,shift) SHL32(a,shift) #define PSHR(a,shift) (SHR((a)+((EXTEND32(1)<<((shift))>>1)),shift)) #define SATURATE(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) #define SATURATE16(x) (EXTRACT16((x)>32767 ? 32767 : (x)<-32768 ? -32768 : (x))) /** Shift by a and round-to-neareast 32-bit value. Result is a 16-bit value */ #define ROUND16(x,a) (EXTRACT16(PSHR32((x),(a)))) /** Divide by two */ #define HALF16(x) (SHR16(x,1)) #define HALF32(x) (SHR32(x,1)) /** Add two 16-bit values */ #define ADD16(a,b) ((opus_val16)((opus_val16)(a)+(opus_val16)(b))) /** Subtract two 16-bit values */ #define SUB16(a,b) ((opus_val16)(a)-(opus_val16)(b)) /** Add two 32-bit values */ #define ADD32(a,b) ((opus_val32)(a)+(opus_val32)(b)) /** Subtract two 32-bit values */ #define SUB32(a,b) ((opus_val32)(a)-(opus_val32)(b)) /** 16x16 multiplication where the result fits in 16 bits */ #define MULT16_16_16(a,b) ((((opus_val16)(a))*((opus_val16)(b)))) /* (opus_val32)(opus_val16) gives TI compiler a hint that it's 16x16->32 multiply */ /** 16x16 multiplication where the result fits in 32 bits */ #define MULT16_16(a,b) (((opus_val32)(opus_val16)(a))*((opus_val32)(opus_val16)(b))) /** 16x16 multiply-add where the result fits in 32 bits */ #define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b)))) /** 16x32 multiply, followed by a 15-bit shift right and 32-bit add. b must fit in 31 bits. Result fits in 32 bits. */ #define MAC16_32_Q15(c,a,b) ADD32((c),ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))) /** 16x32 multiplication, followed by a 16-bit shift right and 32-bit add. Results fits in 32 bits */ #define MAC16_32_Q16(c,a,b) ADD32((c),ADD32(MULT16_16((a),SHR((b),16)), SHR(MULT16_16SU((a),((b)&0x0000ffff)),16))) #define MULT16_16_Q11_32(a,b) (SHR(MULT16_16((a),(b)),11)) #define MULT16_16_Q11(a,b) (SHR(MULT16_16((a),(b)),11)) #define MULT16_16_Q13(a,b) (SHR(MULT16_16((a),(b)),13)) #define MULT16_16_Q14(a,b) (SHR(MULT16_16((a),(b)),14)) #define MULT16_16_Q15(a,b) (SHR(MULT16_16((a),(b)),15)) #define MULT16_16_P13(a,b) (SHR(ADD32(4096,MULT16_16((a),(b))),13)) #define MULT16_16_P14(a,b) (SHR(ADD32(8192,MULT16_16((a),(b))),14)) #define MULT16_16_P15(a,b) (SHR(ADD32(16384,MULT16_16((a),(b))),15)) /** Divide a 32-bit value by a 16-bit value. Result fits in 16 bits */ #define DIV32_16(a,b) ((opus_val16)(((opus_val32)(a))/((opus_val16)(b)))) /** Divide a 32-bit value by a 32-bit value. Result fits in 32 bits */ #define DIV32(a,b) (((opus_val32)(a))/((opus_val32)(b))) #if defined(MIPSr1_ASM) #include "mips/fixed_generic_mipsr1.h" #endif static OPUS_INLINE opus_val16 SIG2WORD16_generic(celt_sig x) { x = PSHR32(x, SIG_SHIFT); x = MAX32(x, -32768); x = MIN32(x, 32767); return EXTRACT16(x); } #define SIG2WORD16(x) (SIG2WORD16_generic(x)) #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/float_cast.h ================================================ /* Copyright (C) 2001 Erik de Castro Lopo */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Version 1.1 */ #ifndef FLOAT_CAST_H #define FLOAT_CAST_H #include "arch.h" /*============================================================================ ** On Intel Pentium processors (especially PIII and probably P4), converting ** from float to int is very slow. To meet the C specs, the code produced by ** most C compilers targeting Pentium needs to change the FPU rounding mode ** before the float to int conversion is performed. ** ** Changing the FPU rounding mode causes the FPU pipeline to be flushed. It ** is this flushing of the pipeline which is so slow. ** ** Fortunately the ISO C99 specifications define the functions lrint, lrintf, ** llrint and llrintf which fix this problem as a side effect. ** ** On Unix-like systems, the configure process should have detected the ** presence of these functions. If they weren't found we have to replace them ** here with a standard C cast. */ /* ** The C99 prototypes for lrint and lrintf are as follows: ** ** long int lrintf (float x) ; ** long int lrint (double x) ; */ /* The presence of the required functions are detected during the configure ** process and the values HAVE_LRINT and HAVE_LRINTF are set accordingly in ** the config.h file. */ #if (HAVE_LRINTF) /* These defines enable functionality introduced with the 1999 ISO C ** standard. They must be defined before the inclusion of math.h to ** engage them. If optimisation is enabled, these functions will be ** inlined. With optimisation switched off, you have to link in the ** maths library using -lm. */ #define _ISOC9X_SOURCE 1 #define _ISOC99_SOURCE 1 #define __USE_ISOC9X 1 #define __USE_ISOC99 1 #include #define float2int(x) lrintf(x) #elif (defined(HAVE_LRINT)) #define _ISOC9X_SOURCE 1 #define _ISOC99_SOURCE 1 #define __USE_ISOC9X 1 #define __USE_ISOC99 1 #include #define float2int(x) lrint(x) #elif (defined(_MSC_VER) && _MSC_VER >= 1400) && defined (_M_X64) #include __inline long int float2int(float value) { return _mm_cvtss_si32(_mm_load_ss(&value)); } #elif (defined(_MSC_VER) && _MSC_VER >= 1400) && defined (_M_IX86) #include /* Win32 doesn't seem to have these functions. ** Therefore implement OPUS_INLINE versions of these functions here. */ __inline long int float2int (float flt) { int intgr; _asm { fld flt fistp intgr } ; return intgr ; } #else #if (defined(__GNUC__) && defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) /* supported by gcc in C99 mode, but not by all other compilers */ #warning "Don't have the functions lrint() and lrintf ()." #warning "Replacing these functions with a standard C cast." #endif /* __STDC_VERSION__ >= 199901L */ #include #define float2int(flt) ((int)(floor(.5+flt))) #endif #ifndef DISABLE_FLOAT_API static OPUS_INLINE opus_int16 FLOAT2INT16(float x) { x = x*CELT_SIG_SCALE; x = MAX32(x, -32768); x = MIN32(x, 32767); return (opus_int16)float2int(x); } #endif /* DISABLE_FLOAT_API */ #endif /* FLOAT_CAST_H */ ================================================ FILE: deps/pjsip/third_party/opus/celt/kiss_fft.c ================================================ /*Copyright (c) 2003-2004, Mark Borgerding Lots of modifications by Jean-Marc Valin Copyright (c) 2005-2007, Xiph.Org Foundation Copyright (c) 2008, Xiph.Org Foundation, CSIRO All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ /* This code is originally from Mark Borgerding's KISS-FFT but has been heavily modified to better suit Opus */ #ifndef SKIP_CONFIG_H # ifdef HAVE_CONFIG_H # include "config.h" # endif #endif #include "_kiss_fft_guts.h" #include "arch.h" #include "os_support.h" #include "mathops.h" #include "stack_alloc.h" /* The guts header contains all the multiplication and addition macros that are defined for complex numbers. It also delares the kf_ internal functions. */ static void kf_bfly2( kiss_fft_cpx * Fout, int m, int N ) { kiss_fft_cpx * Fout2; int i; (void)m; #ifdef CUSTOM_MODES if (m==1) { celt_assert(m==1); for (i=0;itwiddles; /* m is guaranteed to be a multiple of 4. */ for (j=0;jtwiddles[fstride*m]; #endif for (i=0;itwiddles; /* For non-custom modes, m is guaranteed to be a multiple of 4. */ k=m; do { C_MUL(scratch[1],Fout[m] , *tw1); C_MUL(scratch[2],Fout[m2] , *tw2); C_ADD(scratch[3],scratch[1],scratch[2]); C_SUB(scratch[0],scratch[1],scratch[2]); tw1 += fstride; tw2 += fstride*2; Fout[m].r = Fout->r - HALF_OF(scratch[3].r); Fout[m].i = Fout->i - HALF_OF(scratch[3].i); C_MULBYSCALAR( scratch[0] , epi3.i ); C_ADDTO(*Fout,scratch[3]); Fout[m2].r = Fout[m].r + scratch[0].i; Fout[m2].i = Fout[m].i - scratch[0].r; Fout[m].r -= scratch[0].i; Fout[m].i += scratch[0].r; ++Fout; } while(--k); } } #ifndef OVERRIDE_kf_bfly5 static void kf_bfly5( kiss_fft_cpx * Fout, const size_t fstride, const kiss_fft_state *st, int m, int N, int mm ) { kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4; int i, u; kiss_fft_cpx scratch[13]; const kiss_twiddle_cpx *tw; kiss_twiddle_cpx ya,yb; kiss_fft_cpx * Fout_beg = Fout; #ifdef FIXED_POINT ya.r = 10126; ya.i = -31164; yb.r = -26510; yb.i = -19261; #else ya = st->twiddles[fstride*m]; yb = st->twiddles[fstride*2*m]; #endif tw=st->twiddles; for (i=0;ir += scratch[7].r + scratch[8].r; Fout0->i += scratch[7].i + scratch[8].i; scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r); scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r); scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i); scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i); C_SUB(*Fout1,scratch[5],scratch[6]); C_ADD(*Fout4,scratch[5],scratch[6]); scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r); scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r); scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i); scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i); C_ADD(*Fout2,scratch[11],scratch[12]); C_SUB(*Fout3,scratch[11],scratch[12]); ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4; } } } #endif /* OVERRIDE_kf_bfly5 */ #endif #ifdef CUSTOM_MODES static void compute_bitrev_table( int Fout, opus_int16 *f, const size_t fstride, int in_stride, opus_int16 * factors, const kiss_fft_state *st ) { const int p=*factors++; /* the radix */ const int m=*factors++; /* stage's fft length/p */ /*printf ("fft %d %d %d %d %d %d\n", p*m, m, p, s2, fstride*in_stride, N);*/ if (m==1) { int j; for (j=0;j32000 || (opus_int32)p*(opus_int32)p > n) p = n; /* no more factors, skip to end */ } n /= p; #ifdef RADIX_TWO_ONLY if (p!=2 && p != 4) #else if (p>5) #endif { return 0; } facbuf[2*stages] = p; if (p==2 && stages > 1) { facbuf[2*stages] = 4; facbuf[2] = 2; } stages++; } while (n > 1); n = nbak; /* Reverse the order to get the radix 4 at the end, so we can use the fast degenerate case. It turns out that reversing the order also improves the noise behaviour. */ for (i=0;i= memneeded) st = (kiss_fft_state*)mem; *lenmem = memneeded; } if (st) { opus_int16 *bitrev; kiss_twiddle_cpx *twiddles; st->nfft=nfft; #ifdef FIXED_POINT st->scale_shift = celt_ilog2(st->nfft); if (st->nfft == 1<scale_shift) st->scale = Q15ONE; else st->scale = (1073741824+st->nfft/2)/st->nfft>>(15-st->scale_shift); #else st->scale = 1.f/nfft; #endif if (base != NULL) { st->twiddles = base->twiddles; st->shift = 0; while (st->shift < 32 && nfft<shift != base->nfft) st->shift++; if (st->shift>=32) goto fail; } else { st->twiddles = twiddles = (kiss_twiddle_cpx*)KISS_FFT_MALLOC(sizeof(kiss_twiddle_cpx)*nfft); compute_twiddles(twiddles, nfft); st->shift = -1; } if (!kf_factor(nfft,st->factors)) { goto fail; } /* bitrev */ st->bitrev = bitrev = (opus_int16*)KISS_FFT_MALLOC(sizeof(opus_int16)*nfft); if (st->bitrev==NULL) goto fail; compute_bitrev_table(0, bitrev, 1,1, st->factors,st); /* Initialize architecture specific fft parameters */ if (opus_fft_alloc_arch(st, arch)) goto fail; } return st; fail: opus_fft_free(st, arch); return NULL; } kiss_fft_state *opus_fft_alloc(int nfft,void * mem,size_t * lenmem, int arch) { return opus_fft_alloc_twiddles(nfft, mem, lenmem, NULL, arch); } void opus_fft_free_arch_c(kiss_fft_state *st) { (void)st; } void opus_fft_free(const kiss_fft_state *cfg, int arch) { if (cfg) { opus_fft_free_arch((kiss_fft_state *)cfg, arch); opus_free((opus_int16*)cfg->bitrev); if (cfg->shift < 0) opus_free((kiss_twiddle_cpx*)cfg->twiddles); opus_free((kiss_fft_state*)cfg); } } #endif /* CUSTOM_MODES */ void opus_fft_impl(const kiss_fft_state *st,kiss_fft_cpx *fout) { int m2, m; int p; int L; int fstride[MAXFACTORS]; int i; int shift; /* st->shift can be -1 */ shift = st->shift>0 ? st->shift : 0; fstride[0] = 1; L=0; do { p = st->factors[2*L]; m = st->factors[2*L+1]; fstride[L+1] = fstride[L]*p; L++; } while(m!=1); m = st->factors[2*L-1]; for (i=L-1;i>=0;i--) { if (i!=0) m2 = st->factors[2*i-1]; else m2 = 1; switch (st->factors[2*i]) { case 2: kf_bfly2(fout, m, fstride[i]); break; case 4: kf_bfly4(fout,fstride[i]<scale_shift-1; #endif scale = st->scale; celt_assert2 (fin != fout, "In-place FFT not supported"); /* Bit-reverse the input */ for (i=0;infft;i++) { kiss_fft_cpx x = fin[i]; fout[st->bitrev[i]].r = SHR32(MULT16_32_Q16(scale, x.r), scale_shift); fout[st->bitrev[i]].i = SHR32(MULT16_32_Q16(scale, x.i), scale_shift); } opus_fft_impl(st, fout); } void opus_ifft_c(const kiss_fft_state *st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout) { int i; celt_assert2 (fin != fout, "In-place FFT not supported"); /* Bit-reverse the input */ for (i=0;infft;i++) fout[st->bitrev[i]] = fin[i]; for (i=0;infft;i++) fout[i].i = -fout[i].i; opus_fft_impl(st, fout); for (i=0;infft;i++) fout[i].i = -fout[i].i; } ================================================ FILE: deps/pjsip/third_party/opus/celt/kiss_fft.h ================================================ /*Copyright (c) 2003-2004, Mark Borgerding Lots of modifications by Jean-Marc Valin Copyright (c) 2005-2007, Xiph.Org Foundation Copyright (c) 2008, Xiph.Org Foundation, CSIRO All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ #ifndef KISS_FFT_H #define KISS_FFT_H #include #include #include "arch.h" #include "cpu_support.h" #ifdef __cplusplus extern "C" { #endif #ifdef USE_SIMD # include # define kiss_fft_scalar __m128 #define KISS_FFT_MALLOC(nbytes) memalign(16,nbytes) #else #define KISS_FFT_MALLOC opus_alloc #endif #ifdef FIXED_POINT #include "arch.h" # define kiss_fft_scalar opus_int32 # define kiss_twiddle_scalar opus_int16 #else # ifndef kiss_fft_scalar /* default is float */ # define kiss_fft_scalar float # define kiss_twiddle_scalar float # define KF_SUFFIX _celt_single # endif #endif typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; typedef struct { kiss_twiddle_scalar r; kiss_twiddle_scalar i; }kiss_twiddle_cpx; #define MAXFACTORS 8 /* e.g. an fft of length 128 has 4 factors as far as kissfft is concerned 4*4*4*2 */ typedef struct arch_fft_state{ int is_supported; void *priv; } arch_fft_state; typedef struct kiss_fft_state{ int nfft; opus_val16 scale; #ifdef FIXED_POINT int scale_shift; #endif int shift; opus_int16 factors[2*MAXFACTORS]; const opus_int16 *bitrev; const kiss_twiddle_cpx *twiddles; arch_fft_state *arch_fft; } kiss_fft_state; #if defined(HAVE_ARM_NE10) #include "arm/fft_arm.h" #endif /*typedef struct kiss_fft_state* kiss_fft_cfg;*/ /** * opus_fft_alloc * * Initialize a FFT (or IFFT) algorithm's cfg/state buffer. * * typical usage: kiss_fft_cfg mycfg=opus_fft_alloc(1024,0,NULL,NULL); * * The return value from fft_alloc is a cfg buffer used internally * by the fft routine or NULL. * * If lenmem is NULL, then opus_fft_alloc will allocate a cfg buffer using malloc. * The returned value should be free()d when done to avoid memory leaks. * * The state can be placed in a user supplied buffer 'mem': * If lenmem is not NULL and mem is not NULL and *lenmem is large enough, * then the function places the cfg in mem and the size used in *lenmem * and returns mem. * * If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough), * then the function returns NULL and places the minimum cfg * buffer size in *lenmem. * */ kiss_fft_state *opus_fft_alloc_twiddles(int nfft,void * mem,size_t * lenmem, const kiss_fft_state *base, int arch); kiss_fft_state *opus_fft_alloc(int nfft,void * mem,size_t * lenmem, int arch); /** * opus_fft(cfg,in_out_buf) * * Perform an FFT on a complex input buffer. * for a forward FFT, * fin should be f[0] , f[1] , ... ,f[nfft-1] * fout will be F[0] , F[1] , ... ,F[nfft-1] * Note that each element is complex and can be accessed like f[k].r and f[k].i * */ void opus_fft_c(const kiss_fft_state *cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); void opus_ifft_c(const kiss_fft_state *cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); void opus_fft_impl(const kiss_fft_state *st,kiss_fft_cpx *fout); void opus_ifft_impl(const kiss_fft_state *st,kiss_fft_cpx *fout); void opus_fft_free(const kiss_fft_state *cfg, int arch); void opus_fft_free_arch_c(kiss_fft_state *st); int opus_fft_alloc_arch_c(kiss_fft_state *st); #if !defined(OVERRIDE_OPUS_FFT) /* Is run-time CPU detection enabled on this platform? */ #if defined(OPUS_HAVE_RTCD) && (defined(HAVE_ARM_NE10)) extern int (*const OPUS_FFT_ALLOC_ARCH_IMPL[OPUS_ARCHMASK+1])( kiss_fft_state *st); #define opus_fft_alloc_arch(_st, arch) \ ((*OPUS_FFT_ALLOC_ARCH_IMPL[(arch)&OPUS_ARCHMASK])(_st)) extern void (*const OPUS_FFT_FREE_ARCH_IMPL[OPUS_ARCHMASK+1])( kiss_fft_state *st); #define opus_fft_free_arch(_st, arch) \ ((*OPUS_FFT_FREE_ARCH_IMPL[(arch)&OPUS_ARCHMASK])(_st)) extern void (*const OPUS_FFT[OPUS_ARCHMASK+1])(const kiss_fft_state *cfg, const kiss_fft_cpx *fin, kiss_fft_cpx *fout); #define opus_fft(_cfg, _fin, _fout, arch) \ ((*OPUS_FFT[(arch)&OPUS_ARCHMASK])(_cfg, _fin, _fout)) extern void (*const OPUS_IFFT[OPUS_ARCHMASK+1])(const kiss_fft_state *cfg, const kiss_fft_cpx *fin, kiss_fft_cpx *fout); #define opus_ifft(_cfg, _fin, _fout, arch) \ ((*OPUS_IFFT[(arch)&OPUS_ARCHMASK])(_cfg, _fin, _fout)) #else /* else for if defined(OPUS_HAVE_RTCD) && (defined(HAVE_ARM_NE10)) */ #define opus_fft_alloc_arch(_st, arch) \ ((void)(arch), opus_fft_alloc_arch_c(_st)) #define opus_fft_free_arch(_st, arch) \ ((void)(arch), opus_fft_free_arch_c(_st)) #define opus_fft(_cfg, _fin, _fout, arch) \ ((void)(arch), opus_fft_c(_cfg, _fin, _fout)) #define opus_ifft(_cfg, _fin, _fout, arch) \ ((void)(arch), opus_ifft_c(_cfg, _fin, _fout)) #endif /* end if defined(OPUS_HAVE_RTCD) && (defined(HAVE_ARM_NE10)) */ #endif /* end if !defined(OVERRIDE_OPUS_FFT) */ #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/laplace.c ================================================ /* Copyright (c) 2007 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "laplace.h" #include "mathops.h" /* The minimum probability of an energy delta (out of 32768). */ #define LAPLACE_LOG_MINP (0) #define LAPLACE_MINP (1<>15; } void ec_laplace_encode(ec_enc *enc, int *value, unsigned fs, int decay) { unsigned fl; int val = *value; fl = 0; if (val) { int s; int i; s = -(val<0); val = (val+s)^s; fl = fs; fs = ec_laplace_get_freq1(fs, decay); /* Search the decaying part of the PDF.*/ for (i=1; fs > 0 && i < val; i++) { fs *= 2; fl += fs+2*LAPLACE_MINP; fs = (fs*(opus_int32)decay)>>15; } /* Everything beyond that has probability LAPLACE_MINP. */ if (!fs) { int di; int ndi_max; ndi_max = (32768-fl+LAPLACE_MINP-1)>>LAPLACE_LOG_MINP; ndi_max = (ndi_max-s)>>1; di = IMIN(val - i, ndi_max - 1); fl += (2*di+1+s)*LAPLACE_MINP; fs = IMIN(LAPLACE_MINP, 32768-fl); *value = (i+di+s)^s; } else { fs += LAPLACE_MINP; fl += fs&~s; } celt_assert(fl+fs<=32768); celt_assert(fs>0); } ec_encode_bin(enc, fl, fl+fs, 15); } int ec_laplace_decode(ec_dec *dec, unsigned fs, int decay) { int val=0; unsigned fl; unsigned fm; fm = ec_decode_bin(dec, 15); fl = 0; if (fm >= fs) { val++; fl = fs; fs = ec_laplace_get_freq1(fs, decay)+LAPLACE_MINP; /* Search the decaying part of the PDF.*/ while(fs > LAPLACE_MINP && fm >= fl+2*fs) { fs *= 2; fl += fs; fs = ((fs-2*LAPLACE_MINP)*(opus_int32)decay)>>15; fs += LAPLACE_MINP; val++; } /* Everything beyond that has probability LAPLACE_MINP. */ if (fs <= LAPLACE_MINP) { int di; di = (fm-fl)>>(LAPLACE_LOG_MINP+1); val += di; fl += 2*di*LAPLACE_MINP; } if (fm < fl+fs) val = -val; else fl += fs; } celt_assert(fl<32768); celt_assert(fs>0); celt_assert(fl<=fm); celt_assert(fm>1; b=1U<>=1; bshift--; } while(bshift>=0); return g; } #ifdef FIXED_POINT opus_val32 frac_div32(opus_val32 a, opus_val32 b) { opus_val16 rcp; opus_val32 result, rem; int shift = celt_ilog2(b)-29; a = VSHR32(a,shift); b = VSHR32(b,shift); /* 16-bit reciprocal */ rcp = ROUND16(celt_rcp(ROUND16(b,16)),3); result = MULT16_32_Q15(rcp, a); rem = PSHR32(a,2)-MULT32_32_Q31(result, b); result = ADD32(result, SHL32(MULT16_32_Q15(rcp, rem),2)); if (result >= 536870912) /* 2^29 */ return 2147483647; /* 2^31 - 1 */ else if (result <= -536870912) /* -2^29 */ return -2147483647; /* -2^31 */ else return SHL32(result, 2); } /** Reciprocal sqrt approximation in the range [0.25,1) (Q16 in, Q14 out) */ opus_val16 celt_rsqrt_norm(opus_val32 x) { opus_val16 n; opus_val16 r; opus_val16 r2; opus_val16 y; /* Range of n is [-16384,32767] ([-0.5,1) in Q15). */ n = x-32768; /* Get a rough initial guess for the root. The optimal minimax quadratic approximation (using relative error) is r = 1.437799046117536+n*(-0.823394375837328+n*0.4096419668459485). Coefficients here, and the final result r, are Q14.*/ r = ADD16(23557, MULT16_16_Q15(n, ADD16(-13490, MULT16_16_Q15(n, 6713)))); /* We want y = x*r*r-1 in Q15, but x is 32-bit Q16 and r is Q14. We can compute the result from n and r using Q15 multiplies with some adjustment, carefully done to avoid overflow. Range of y is [-1564,1594]. */ r2 = MULT16_16_Q15(r, r); y = SHL16(SUB16(ADD16(MULT16_16_Q15(r2, n), r2), 16384), 1); /* Apply a 2nd-order Householder iteration: r += r*y*(y*0.375-0.5). This yields the Q14 reciprocal square root of the Q16 x, with a maximum relative error of 1.04956E-4, a (relative) RMSE of 2.80979E-5, and a peak absolute error of 2.26591/16384. */ return ADD16(r, MULT16_16_Q15(r, MULT16_16_Q15(y, SUB16(MULT16_16_Q15(y, 12288), 16384)))); } /** Sqrt approximation (QX input, QX/2 output) */ opus_val32 celt_sqrt(opus_val32 x) { int k; opus_val16 n; opus_val32 rt; static const opus_val16 C[5] = {23175, 11561, -3011, 1699, -664}; if (x==0) return 0; else if (x>=1073741824) return 32767; k = (celt_ilog2(x)>>1)-7; x = VSHR32(x, 2*k); n = x-32768; rt = ADD16(C[0], MULT16_16_Q15(n, ADD16(C[1], MULT16_16_Q15(n, ADD16(C[2], MULT16_16_Q15(n, ADD16(C[3], MULT16_16_Q15(n, (C[4]))))))))); rt = VSHR32(rt,7-k); return rt; } #define L1 32767 #define L2 -7651 #define L3 8277 #define L4 -626 static OPUS_INLINE opus_val16 _celt_cos_pi_2(opus_val16 x) { opus_val16 x2; x2 = MULT16_16_P15(x,x); return ADD16(1,MIN16(32766,ADD32(SUB16(L1,x2), MULT16_16_P15(x2, ADD32(L2, MULT16_16_P15(x2, ADD32(L3, MULT16_16_P15(L4, x2 )))))))); } #undef L1 #undef L2 #undef L3 #undef L4 opus_val16 celt_cos_norm(opus_val32 x) { x = x&0x0001ffff; if (x>SHL32(EXTEND32(1), 16)) x = SUB32(SHL32(EXTEND32(1), 17),x); if (x&0x00007fff) { if (x0, "celt_rcp() only defined for positive values"); i = celt_ilog2(x); /* n is Q15 with range [0,1). */ n = VSHR32(x,i-15)-32768; /* Start with a linear approximation: r = 1.8823529411764706-0.9411764705882353*n. The coefficients and the result are Q14 in the range [15420,30840].*/ r = ADD16(30840, MULT16_16_Q15(-15420, n)); /* Perform two Newton iterations: r -= r*((r*n)-1.Q15) = r*((r*n)+(r-1.Q15)). */ r = SUB16(r, MULT16_16_Q15(r, ADD16(MULT16_16_Q15(r, n), ADD16(r, -32768)))); /* We subtract an extra 1 in the second iteration to avoid overflow; it also neatly compensates for truncation error in the rest of the process. */ r = SUB16(r, ADD16(1, MULT16_16_Q15(r, ADD16(MULT16_16_Q15(r, n), ADD16(r, -32768))))); /* r is now the Q15 solution to 2/(n+1), with a maximum relative error of 7.05346E-5, a (relative) RMSE of 2.14418E-5, and a peak absolute error of 1.24665/32768. */ return VSHR32(EXTEND32(r),i-16); } #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/mathops.h ================================================ /* Copyright (c) 2002-2008 Jean-Marc Valin Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Written by Jean-Marc Valin */ /** @file mathops.h @brief Various math functions */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MATHOPS_H #define MATHOPS_H #include "arch.h" #include "entcode.h" #include "os_support.h" /* Multiplies two 16-bit fractional values. Bit-exactness of this macro is important */ #define FRAC_MUL16(a,b) ((16384+((opus_int32)(opus_int16)(a)*(opus_int16)(b)))>>15) unsigned isqrt32(opus_uint32 _val); #ifndef OVERRIDE_CELT_MAXABS16 static OPUS_INLINE opus_val32 celt_maxabs16(const opus_val16 *x, int len) { int i; opus_val16 maxval = 0; opus_val16 minval = 0; for (i=0;i>23)-127; in.i -= integer<<23; frac = in.f - 1.5f; frac = -0.41445418f + frac*(0.95909232f + frac*(-0.33951290f + frac*0.16541097f)); return 1+integer+frac; } /** Base-2 exponential approximation (2^x). */ static OPUS_INLINE float celt_exp2(float x) { int integer; float frac; union { float f; opus_uint32 i; } res; integer = floor(x); if (integer < -50) return 0; frac = x-integer; /* K0 = 1, K1 = log(2), K2 = 3-4*log(2), K3 = 3*log(2) - 2 */ res.f = 0.99992522f + frac * (0.69583354f + frac * (0.22606716f + 0.078024523f*frac)); res.i = (res.i + (integer<<23)) & 0x7fffffff; return res.f; } #else #define celt_log2(x) ((float)(1.442695040888963387*log(x))) #define celt_exp2(x) ((float)exp(0.6931471805599453094*(x))) #endif #endif #ifdef FIXED_POINT #include "os_support.h" #ifndef OVERRIDE_CELT_ILOG2 /** Integer log in base2. Undefined for zero and negative numbers */ static OPUS_INLINE opus_int16 celt_ilog2(opus_int32 x) { celt_assert2(x>0, "celt_ilog2() only defined for strictly positive numbers"); return EC_ILOG(x)-1; } #endif /** Integer log in base2. Defined for zero, but not for negative numbers */ static OPUS_INLINE opus_int16 celt_zlog2(opus_val32 x) { return x <= 0 ? 0 : celt_ilog2(x); } opus_val16 celt_rsqrt_norm(opus_val32 x); opus_val32 celt_sqrt(opus_val32 x); opus_val16 celt_cos_norm(opus_val32 x); /** Base-2 logarithm approximation (log2(x)). (Q14 input, Q10 output) */ static OPUS_INLINE opus_val16 celt_log2(opus_val32 x) { int i; opus_val16 n, frac; /* -0.41509302963303146, 0.9609890551383969, -0.31836011537636605, 0.15530808010959576, -0.08556153059057618 */ static const opus_val16 C[5] = {-6801+(1<<(13-DB_SHIFT)), 15746, -5217, 2545, -1401}; if (x==0) return -32767; i = celt_ilog2(x); n = VSHR32(x,i-15)-32768-16384; frac = ADD16(C[0], MULT16_16_Q15(n, ADD16(C[1], MULT16_16_Q15(n, ADD16(C[2], MULT16_16_Q15(n, ADD16(C[3], MULT16_16_Q15(n, C[4])))))))); return SHL16(i-13,DB_SHIFT)+SHR16(frac,14-DB_SHIFT); } /* K0 = 1 K1 = log(2) K2 = 3-4*log(2) K3 = 3*log(2) - 2 */ #define D0 16383 #define D1 22804 #define D2 14819 #define D3 10204 static OPUS_INLINE opus_val32 celt_exp2_frac(opus_val16 x) { opus_val16 frac; frac = SHL16(x, 4); return ADD16(D0, MULT16_16_Q15(frac, ADD16(D1, MULT16_16_Q15(frac, ADD16(D2 , MULT16_16_Q15(D3,frac)))))); } /** Base-2 exponential approximation (2^x). (Q10 input, Q16 output) */ static OPUS_INLINE opus_val32 celt_exp2(opus_val16 x) { int integer; opus_val16 frac; integer = SHR16(x,10); if (integer>14) return 0x7f000000; else if (integer < -15) return 0; frac = celt_exp2_frac(x-SHL16(integer,10)); return VSHR32(EXTEND32(frac), -integer-2); } opus_val32 celt_rcp(opus_val32 x); #define celt_div(a,b) MULT32_32_Q31((opus_val32)(a),celt_rcp(b)) opus_val32 frac_div32(opus_val32 a, opus_val32 b); #define M1 32767 #define M2 -21 #define M3 -11943 #define M4 4936 /* Atan approximation using a 4th order polynomial. Input is in Q15 format and normalized by pi/4. Output is in Q15 format */ static OPUS_INLINE opus_val16 celt_atan01(opus_val16 x) { return MULT16_16_P15(x, ADD32(M1, MULT16_16_P15(x, ADD32(M2, MULT16_16_P15(x, ADD32(M3, MULT16_16_P15(M4, x))))))); } #undef M1 #undef M2 #undef M3 #undef M4 /* atan2() approximation valid for positive input values */ static OPUS_INLINE opus_val16 celt_atan2p(opus_val16 y, opus_val16 x) { if (y < x) { opus_val32 arg; arg = celt_div(SHL32(EXTEND32(y),15),x); if (arg >= 32767) arg = 32767; return SHR16(celt_atan01(EXTRACT16(arg)),1); } else { opus_val32 arg; arg = celt_div(SHL32(EXTEND32(x),15),y); if (arg >= 32767) arg = 32767; return 25736-SHR16(celt_atan01(EXTRACT16(arg)),1); } } #endif /* FIXED_POINT */ #endif /* MATHOPS_H */ ================================================ FILE: deps/pjsip/third_party/opus/celt/mdct.c ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2008 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This is a simple MDCT implementation that uses a N/4 complex FFT to do most of the work. It should be relatively straightforward to plug in pretty much and FFT here. This replaces the Vorbis FFT (and uses the exact same API), which was a bit too messy and that was ending up duplicating code (might as well use the same FFT everywhere). The algorithm is similar to (and inspired from) Fabrice Bellard's MDCT implementation in FFMPEG, but has differences in signs, ordering and scaling in many places. */ #ifndef SKIP_CONFIG_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #endif #include "mdct.h" #include "kiss_fft.h" #include "_kiss_fft_guts.h" #include #include "os_support.h" #include "mathops.h" #include "stack_alloc.h" #if defined(MIPSr1_ASM) #include "mips/mdct_mipsr1.h" #endif #ifdef CUSTOM_MODES int clt_mdct_init(mdct_lookup *l,int N, int maxshift, int arch) { int i; kiss_twiddle_scalar *trig; int shift; int N2=N>>1; l->n = N; l->maxshift = maxshift; for (i=0;i<=maxshift;i++) { if (i==0) l->kfft[i] = opus_fft_alloc(N>>2>>i, 0, 0, arch); else l->kfft[i] = opus_fft_alloc_twiddles(N>>2>>i, 0, 0, l->kfft[0], arch); #ifndef ENABLE_TI_DSPLIB55 if (l->kfft[i]==NULL) return 0; #endif } l->trig = trig = (kiss_twiddle_scalar*)opus_alloc((N-(N2>>maxshift))*sizeof(kiss_twiddle_scalar)); if (l->trig==NULL) return 0; for (shift=0;shift<=maxshift;shift++) { /* We have enough points that sine isn't necessary */ #if defined(FIXED_POINT) #if 1 for (i=0;i>= 1; N >>= 1; } return 1; } void clt_mdct_clear(mdct_lookup *l, int arch) { int i; for (i=0;i<=l->maxshift;i++) opus_fft_free(l->kfft[i], arch); opus_free((kiss_twiddle_scalar*)l->trig); } #endif /* CUSTOM_MODES */ /* Forward MDCT trashes the input array */ #ifndef OVERRIDE_clt_mdct_forward void clt_mdct_forward_c(const mdct_lookup *l, kiss_fft_scalar *in, kiss_fft_scalar * OPUS_RESTRICT out, const opus_val16 *window, int overlap, int shift, int stride, int arch) { int i; int N, N2, N4; VARDECL(kiss_fft_scalar, f); VARDECL(kiss_fft_cpx, f2); const kiss_fft_state *st = l->kfft[shift]; const kiss_twiddle_scalar *trig; opus_val16 scale; #ifdef FIXED_POINT /* Allows us to scale with MULT16_32_Q16(), which is faster than MULT16_32_Q15() on ARM. */ int scale_shift = st->scale_shift-1; #endif SAVE_STACK; (void)arch; scale = st->scale; N = l->n; trig = l->trig; for (i=0;i>= 1; trig += N; } N2 = N>>1; N4 = N>>2; ALLOC(f, N2, kiss_fft_scalar); ALLOC(f2, N4, kiss_fft_cpx); /* Consider the input to be composed of four blocks: [a, b, c, d] */ /* Window, shuffle, fold */ { /* Temp pointers to make it really clear to the compiler what we're doing */ const kiss_fft_scalar * OPUS_RESTRICT xp1 = in+(overlap>>1); const kiss_fft_scalar * OPUS_RESTRICT xp2 = in+N2-1+(overlap>>1); kiss_fft_scalar * OPUS_RESTRICT yp = f; const opus_val16 * OPUS_RESTRICT wp1 = window+(overlap>>1); const opus_val16 * OPUS_RESTRICT wp2 = window+(overlap>>1)-1; for(i=0;i<((overlap+3)>>2);i++) { /* Real part arranged as -d-cR, Imag part arranged as -b+aR*/ *yp++ = MULT16_32_Q15(*wp2, xp1[N2]) + MULT16_32_Q15(*wp1,*xp2); *yp++ = MULT16_32_Q15(*wp1, *xp1) - MULT16_32_Q15(*wp2, xp2[-N2]); xp1+=2; xp2-=2; wp1+=2; wp2-=2; } wp1 = window; wp2 = window+overlap-1; for(;i>2);i++) { /* Real part arranged as a-bR, Imag part arranged as -c-dR */ *yp++ = *xp2; *yp++ = *xp1; xp1+=2; xp2-=2; } for(;ibitrev[i]] = yc; } } /* N/4 complex FFT, does not downscale anymore */ opus_fft_impl(st, f2); /* Post-rotate */ { /* Temp pointers to make it really clear to the compiler what we're doing */ const kiss_fft_cpx * OPUS_RESTRICT fp = f2; kiss_fft_scalar * OPUS_RESTRICT yp1 = out; kiss_fft_scalar * OPUS_RESTRICT yp2 = out+stride*(N2-1); const kiss_twiddle_scalar *t = &trig[0]; /* Temp pointers to make it really clear to the compiler what we're doing */ for(i=0;ii,t[N4+i]) - S_MUL(fp->r,t[i]); yi = S_MUL(fp->r,t[N4+i]) + S_MUL(fp->i,t[i]); *yp1 = yr; *yp2 = yi; fp++; yp1 += 2*stride; yp2 -= 2*stride; } } RESTORE_STACK; } #endif /* OVERRIDE_clt_mdct_forward */ #ifndef OVERRIDE_clt_mdct_backward void clt_mdct_backward_c(const mdct_lookup *l, kiss_fft_scalar *in, kiss_fft_scalar * OPUS_RESTRICT out, const opus_val16 * OPUS_RESTRICT window, int overlap, int shift, int stride, int arch) { int i; int N, N2, N4; const kiss_twiddle_scalar *trig; (void) arch; N = l->n; trig = l->trig; for (i=0;i>= 1; trig += N; } N2 = N>>1; N4 = N>>2; /* Pre-rotate */ { /* Temp pointers to make it really clear to the compiler what we're doing */ const kiss_fft_scalar * OPUS_RESTRICT xp1 = in; const kiss_fft_scalar * OPUS_RESTRICT xp2 = in+stride*(N2-1); kiss_fft_scalar * OPUS_RESTRICT yp = out+(overlap>>1); const kiss_twiddle_scalar * OPUS_RESTRICT t = &trig[0]; const opus_int16 * OPUS_RESTRICT bitrev = l->kfft[shift]->bitrev; for(i=0;ikfft[shift], (kiss_fft_cpx*)(out+(overlap>>1))); /* Post-rotate and de-shuffle from both ends of the buffer at once to make it in-place. */ { kiss_fft_scalar * yp0 = out+(overlap>>1); kiss_fft_scalar * yp1 = out+(overlap>>1)+N2-2; const kiss_twiddle_scalar *t = &trig[0]; /* Loop to (N4+1)>>1 to handle odd N4. When N4 is odd, the middle pair will be computed twice. */ for(i=0;i<(N4+1)>>1;i++) { kiss_fft_scalar re, im, yr, yi; kiss_twiddle_scalar t0, t1; /* We swap real and imag because we're using an FFT instead of an IFFT. */ re = yp0[1]; im = yp0[0]; t0 = t[i]; t1 = t[N4+i]; /* We'd scale up by 2 here, but instead it's done when mixing the windows */ yr = S_MUL(re,t0) + S_MUL(im,t1); yi = S_MUL(re,t1) - S_MUL(im,t0); /* We swap real and imag because we're using an FFT instead of an IFFT. */ re = yp1[1]; im = yp1[0]; yp0[0] = yr; yp1[1] = yi; t0 = t[(N4-i-1)]; t1 = t[(N2-i-1)]; /* We'd scale up by 2 here, but instead it's done when mixing the windows */ yr = S_MUL(re,t0) + S_MUL(im,t1); yi = S_MUL(re,t1) - S_MUL(im,t0); yp1[0] = yr; yp0[1] = yi; yp0 += 2; yp1 -= 2; } } /* Mirror on both sides for TDAC */ { kiss_fft_scalar * OPUS_RESTRICT xp1 = out+overlap-1; kiss_fft_scalar * OPUS_RESTRICT yp1 = out; const opus_val16 * OPUS_RESTRICT wp1 = window; const opus_val16 * OPUS_RESTRICT wp2 = window+overlap-1; for(i = 0; i < overlap/2; i++) { kiss_fft_scalar x1, x2; x1 = *xp1; x2 = *yp1; *yp1++ = MULT16_32_Q15(*wp2, x2) - MULT16_32_Q15(*wp1, x1); *xp1-- = MULT16_32_Q15(*wp1, x2) + MULT16_32_Q15(*wp2, x1); wp1++; wp2--; } } } #endif /* OVERRIDE_clt_mdct_backward */ ================================================ FILE: deps/pjsip/third_party/opus/celt/mdct.h ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2008 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This is a simple MDCT implementation that uses a N/4 complex FFT to do most of the work. It should be relatively straightforward to plug in pretty much and FFT here. This replaces the Vorbis FFT (and uses the exact same API), which was a bit too messy and that was ending up duplicating code (might as well use the same FFT everywhere). The algorithm is similar to (and inspired from) Fabrice Bellard's MDCT implementation in FFMPEG, but has differences in signs, ordering and scaling in many places. */ #ifndef MDCT_H #define MDCT_H #include "opus_defines.h" #include "kiss_fft.h" #include "arch.h" typedef struct { int n; int maxshift; const kiss_fft_state *kfft[4]; const kiss_twiddle_scalar * OPUS_RESTRICT trig; } mdct_lookup; #if defined(HAVE_ARM_NE10) #include "arm/mdct_arm.h" #endif int clt_mdct_init(mdct_lookup *l,int N, int maxshift, int arch); void clt_mdct_clear(mdct_lookup *l, int arch); /** Compute a forward MDCT and scale by 4/N, trashes the input array */ void clt_mdct_forward_c(const mdct_lookup *l, kiss_fft_scalar *in, kiss_fft_scalar * OPUS_RESTRICT out, const opus_val16 *window, int overlap, int shift, int stride, int arch); /** Compute a backward MDCT (no scaling) and performs weighted overlap-add (scales implicitly by 1/2) */ void clt_mdct_backward_c(const mdct_lookup *l, kiss_fft_scalar *in, kiss_fft_scalar * OPUS_RESTRICT out, const opus_val16 * OPUS_RESTRICT window, int overlap, int shift, int stride, int arch); #if !defined(OVERRIDE_OPUS_MDCT) /* Is run-time CPU detection enabled on this platform? */ #if defined(OPUS_HAVE_RTCD) && defined(HAVE_ARM_NE10) extern void (*const CLT_MDCT_FORWARD_IMPL[OPUS_ARCHMASK+1])( const mdct_lookup *l, kiss_fft_scalar *in, kiss_fft_scalar * OPUS_RESTRICT out, const opus_val16 *window, int overlap, int shift, int stride, int arch); #define clt_mdct_forward(_l, _in, _out, _window, _overlap, _shift, _stride, _arch) \ ((*CLT_MDCT_FORWARD_IMPL[(arch)&OPUS_ARCHMASK])(_l, _in, _out, \ _window, _overlap, _shift, \ _stride, _arch)) extern void (*const CLT_MDCT_BACKWARD_IMPL[OPUS_ARCHMASK+1])( const mdct_lookup *l, kiss_fft_scalar *in, kiss_fft_scalar * OPUS_RESTRICT out, const opus_val16 *window, int overlap, int shift, int stride, int arch); #define clt_mdct_backward(_l, _in, _out, _window, _overlap, _shift, _stride, _arch) \ (*CLT_MDCT_BACKWARD_IMPL[(arch)&OPUS_ARCHMASK])(_l, _in, _out, \ _window, _overlap, _shift, \ _stride, _arch) #else /* if defined(OPUS_HAVE_RTCD) && defined(HAVE_ARM_NE10) */ #define clt_mdct_forward(_l, _in, _out, _window, _overlap, _shift, _stride, _arch) \ clt_mdct_forward_c(_l, _in, _out, _window, _overlap, _shift, _stride, _arch) #define clt_mdct_backward(_l, _in, _out, _window, _overlap, _shift, _stride, _arch) \ clt_mdct_backward_c(_l, _in, _out, _window, _overlap, _shift, _stride, _arch) #endif /* end if defined(OPUS_HAVE_RTCD) && defined(HAVE_ARM_NE10) && !defined(FIXED_POINT) */ #endif /* end if !defined(OVERRIDE_OPUS_MDCT) */ #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/mfrngcod.h ================================================ /* Copyright (c) 2001-2008 Timothy B. Terriberry Copyright (c) 2008-2009 Xiph.Org Foundation */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if !defined(_mfrngcode_H) # define _mfrngcode_H (1) # include "entcode.h" /*Constants used by the entropy encoder/decoder.*/ /*The number of bits to output at a time.*/ # define EC_SYM_BITS (8) /*The total number of bits in each of the state registers.*/ # define EC_CODE_BITS (32) /*The maximum symbol value.*/ # define EC_SYM_MAX ((1U<>EC_SYM_BITS) /*The number of bits available for the last, partial symbol in the code field.*/ # define EC_CODE_EXTRA ((EC_CODE_BITS-2)%EC_SYM_BITS+1) #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/mips/celt_mipsr1.h ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2010 Xiph.Org Foundation Copyright (c) 2008 Gregory Maxwell Written by Jean-Marc Valin and Gregory Maxwell */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __CELT_MIPSR1_H__ #define __CELT_MIPSR1_H__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #define CELT_C #include "os_support.h" #include "mdct.h" #include #include "celt.h" #include "pitch.h" #include "bands.h" #include "modes.h" #include "entcode.h" #include "quant_bands.h" #include "rate.h" #include "stack_alloc.h" #include "mathops.h" #include "float_cast.h" #include #include "celt_lpc.h" #include "vq.h" #define OVERRIDE_comb_filter void comb_filter(opus_val32 *y, opus_val32 *x, int T0, int T1, int N, opus_val16 g0, opus_val16 g1, int tapset0, int tapset1, const opus_val16 *window, int overlap, int arch) { int i; opus_val32 x0, x1, x2, x3, x4; (void)arch; /* printf ("%d %d %f %f\n", T0, T1, g0, g1); */ opus_val16 g00, g01, g02, g10, g11, g12; static const opus_val16 gains[3][3] = { {QCONST16(0.3066406250f, 15), QCONST16(0.2170410156f, 15), QCONST16(0.1296386719f, 15)}, {QCONST16(0.4638671875f, 15), QCONST16(0.2680664062f, 15), QCONST16(0.f, 15)}, {QCONST16(0.7998046875f, 15), QCONST16(0.1000976562f, 15), QCONST16(0.f, 15)}}; if (g0==0 && g1==0) { /* OPT: Happens to work without the OPUS_MOVE(), but only because the current encoder already copies x to y */ if (x!=y) OPUS_MOVE(y, x, N); return; } g00 = MULT16_16_P15(g0, gains[tapset0][0]); g01 = MULT16_16_P15(g0, gains[tapset0][1]); g02 = MULT16_16_P15(g0, gains[tapset0][2]); g10 = MULT16_16_P15(g1, gains[tapset1][0]); g11 = MULT16_16_P15(g1, gains[tapset1][1]); g12 = MULT16_16_P15(g1, gains[tapset1][2]); x1 = x[-T1+1]; x2 = x[-T1 ]; x3 = x[-T1-1]; x4 = x[-T1-2]; /* If the filter didn't change, we don't need the overlap */ if (g0==g1 && T0==T1 && tapset0==tapset1) overlap=0; for (i=0;itwiddles[fstride*m]; yb = st->twiddles[fstride*2*m]; #endif tw=st->twiddles; for (i=0;ir += scratch[7].r + scratch[8].r; Fout0->i += scratch[7].i + scratch[8].i; scratch[5].r = scratch[0].r + S_MUL_ADD(scratch[7].r,ya.r,scratch[8].r,yb.r); scratch[5].i = scratch[0].i + S_MUL_ADD(scratch[7].i,ya.r,scratch[8].i,yb.r); scratch[6].r = S_MUL_ADD(scratch[10].i,ya.i,scratch[9].i,yb.i); scratch[6].i = -S_MUL_ADD(scratch[10].r,ya.i,scratch[9].r,yb.i); C_SUB(*Fout1,scratch[5],scratch[6]); C_ADD(*Fout4,scratch[5],scratch[6]); scratch[11].r = scratch[0].r + S_MUL_ADD(scratch[7].r,yb.r,scratch[8].r,ya.r); scratch[11].i = scratch[0].i + S_MUL_ADD(scratch[7].i,yb.r,scratch[8].i,ya.r); scratch[12].r = S_MUL_SUB(scratch[9].i,ya.i,scratch[10].i,yb.i); scratch[12].i = S_MUL_SUB(scratch[10].r,yb.i,scratch[9].r,ya.i); C_ADD(*Fout2,scratch[11],scratch[12]); C_SUB(*Fout3,scratch[11],scratch[12]); ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4; } } } #endif /* KISS_FFT_MIPSR1_H */ ================================================ FILE: deps/pjsip/third_party/opus/celt/mips/mdct_mipsr1.h ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2008 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This is a simple MDCT implementation that uses a N/4 complex FFT to do most of the work. It should be relatively straightforward to plug in pretty much and FFT here. This replaces the Vorbis FFT (and uses the exact same API), which was a bit too messy and that was ending up duplicating code (might as well use the same FFT everywhere). The algorithm is similar to (and inspired from) Fabrice Bellard's MDCT implementation in FFMPEG, but has differences in signs, ordering and scaling in many places. */ #ifndef __MDCT_MIPSR1_H__ #define __MDCT_MIPSR1_H__ #ifndef SKIP_CONFIG_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #endif #include "mdct.h" #include "kiss_fft.h" #include "_kiss_fft_guts.h" #include #include "os_support.h" #include "mathops.h" #include "stack_alloc.h" /* Forward MDCT trashes the input array */ #define OVERRIDE_clt_mdct_forward void clt_mdct_forward(const mdct_lookup *l, kiss_fft_scalar *in, kiss_fft_scalar * OPUS_RESTRICT out, const opus_val16 *window, int overlap, int shift, int stride, int arch) { int i; int N, N2, N4; VARDECL(kiss_fft_scalar, f); VARDECL(kiss_fft_cpx, f2); const kiss_fft_state *st = l->kfft[shift]; const kiss_twiddle_scalar *trig; opus_val16 scale; #ifdef FIXED_POINT /* Allows us to scale with MULT16_32_Q16(), which is faster than MULT16_32_Q15() on ARM. */ int scale_shift = st->scale_shift-1; #endif (void)arch; SAVE_STACK; scale = st->scale; N = l->n; trig = l->trig; for (i=0;i>= 1; trig += N; } N2 = N>>1; N4 = N>>2; ALLOC(f, N2, kiss_fft_scalar); ALLOC(f2, N4, kiss_fft_cpx); /* Consider the input to be composed of four blocks: [a, b, c, d] */ /* Window, shuffle, fold */ { /* Temp pointers to make it really clear to the compiler what we're doing */ const kiss_fft_scalar * OPUS_RESTRICT xp1 = in+(overlap>>1); const kiss_fft_scalar * OPUS_RESTRICT xp2 = in+N2-1+(overlap>>1); kiss_fft_scalar * OPUS_RESTRICT yp = f; const opus_val16 * OPUS_RESTRICT wp1 = window+(overlap>>1); const opus_val16 * OPUS_RESTRICT wp2 = window+(overlap>>1)-1; for(i=0;i<((overlap+3)>>2);i++) { /* Real part arranged as -d-cR, Imag part arranged as -b+aR*/ *yp++ = S_MUL_ADD(*wp2, xp1[N2],*wp1,*xp2); *yp++ = S_MUL_SUB(*wp1, *xp1,*wp2, xp2[-N2]); xp1+=2; xp2-=2; wp1+=2; wp2-=2; } wp1 = window; wp2 = window+overlap-1; for(;i>2);i++) { /* Real part arranged as a-bR, Imag part arranged as -c-dR */ *yp++ = *xp2; *yp++ = *xp1; xp1+=2; xp2-=2; } for(;ibitrev[i]] = yc; } } /* N/4 complex FFT, does not downscale anymore */ opus_fft_impl(st, f2); /* Post-rotate */ { /* Temp pointers to make it really clear to the compiler what we're doing */ const kiss_fft_cpx * OPUS_RESTRICT fp = f2; kiss_fft_scalar * OPUS_RESTRICT yp1 = out; kiss_fft_scalar * OPUS_RESTRICT yp2 = out+stride*(N2-1); const kiss_twiddle_scalar *t = &trig[0]; /* Temp pointers to make it really clear to the compiler what we're doing */ for(i=0;ii,t[N4+i] , fp->r,t[i]); yi = S_MUL_ADD(fp->r,t[N4+i] ,fp->i,t[i]); *yp1 = yr; *yp2 = yi; fp++; yp1 += 2*stride; yp2 -= 2*stride; } } RESTORE_STACK; } #define OVERRIDE_clt_mdct_backward void clt_mdct_backward(const mdct_lookup *l, kiss_fft_scalar *in, kiss_fft_scalar * OPUS_RESTRICT out, const opus_val16 * OPUS_RESTRICT window, int overlap, int shift, int stride, int arch) { int i; int N, N2, N4; const kiss_twiddle_scalar *trig; (void)arch; N = l->n; trig = l->trig; for (i=0;i>= 1; trig += N; } N2 = N>>1; N4 = N>>2; /* Pre-rotate */ { /* Temp pointers to make it really clear to the compiler what we're doing */ const kiss_fft_scalar * OPUS_RESTRICT xp1 = in; const kiss_fft_scalar * OPUS_RESTRICT xp2 = in+stride*(N2-1); kiss_fft_scalar * OPUS_RESTRICT yp = out+(overlap>>1); const kiss_twiddle_scalar * OPUS_RESTRICT t = &trig[0]; const opus_int16 * OPUS_RESTRICT bitrev = l->kfft[shift]->bitrev; for(i=0;ikfft[shift], (kiss_fft_cpx*)(out+(overlap>>1))); /* Post-rotate and de-shuffle from both ends of the buffer at once to make it in-place. */ { kiss_fft_scalar * OPUS_RESTRICT yp0 = out+(overlap>>1); kiss_fft_scalar * OPUS_RESTRICT yp1 = out+(overlap>>1)+N2-2; const kiss_twiddle_scalar *t = &trig[0]; /* Loop to (N4+1)>>1 to handle odd N4. When N4 is odd, the middle pair will be computed twice. */ for(i=0;i<(N4+1)>>1;i++) { kiss_fft_scalar re, im, yr, yi; kiss_twiddle_scalar t0, t1; /* We swap real and imag because we're using an FFT instead of an IFFT. */ re = yp0[1]; im = yp0[0]; t0 = t[i]; t1 = t[N4+i]; /* We'd scale up by 2 here, but instead it's done when mixing the windows */ yr = S_MUL_ADD(re,t0 , im,t1); yi = S_MUL_SUB(re,t1 , im,t0); /* We swap real and imag because we're using an FFT instead of an IFFT. */ re = yp1[1]; im = yp1[0]; yp0[0] = yr; yp1[1] = yi; t0 = t[(N4-i-1)]; t1 = t[(N2-i-1)]; /* We'd scale up by 2 here, but instead it's done when mixing the windows */ yr = S_MUL_ADD(re,t0,im,t1); yi = S_MUL_SUB(re,t1,im,t0); yp1[0] = yr; yp0[1] = yi; yp0 += 2; yp1 -= 2; } } /* Mirror on both sides for TDAC */ { kiss_fft_scalar * OPUS_RESTRICT xp1 = out+overlap-1; kiss_fft_scalar * OPUS_RESTRICT yp1 = out; const opus_val16 * OPUS_RESTRICT wp1 = window; const opus_val16 * OPUS_RESTRICT wp2 = window+overlap-1; for(i = 0; i < overlap/2; i++) { kiss_fft_scalar x1, x2; x1 = *xp1; x2 = *yp1; *yp1++ = MULT16_32_Q15(*wp2, x2) - MULT16_32_Q15(*wp1, x1); *xp1-- = MULT16_32_Q15(*wp1, x2) + MULT16_32_Q15(*wp2, x1); wp1++; wp2--; } } } #endif /* __MDCT_MIPSR1_H__ */ ================================================ FILE: deps/pjsip/third_party/opus/celt/mips/pitch_mipsr1.h ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Written by Jean-Marc Valin */ /** @file pitch.h @brief Pitch analysis */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef PITCH_MIPSR1_H #define PITCH_MIPSR1_H #define OVERRIDE_DUAL_INNER_PROD static inline void dual_inner_prod(const opus_val16 *x, const opus_val16 *y01, const opus_val16 *y02, int N, opus_val32 *xy1, opus_val32 *xy2, int arch) { int j; opus_val32 xy01=0; opus_val32 xy02=0; (void)arch; asm volatile("MULT $ac1, $0, $0"); asm volatile("MULT $ac2, $0, $0"); /* Compute the norm of X+Y and X-Y as |X|^2 + |Y|^2 +/- sum(xy) */ for (j=0;j=0;i--) { celt_norm x1, x2; x1 = Xptr[0]; x2 = Xptr[stride]; Xptr[stride] = EXTRACT16(PSHR32(MAC16_16(MULT16_16(c, x2), s, x1), 15)); *Xptr-- = EXTRACT16(PSHR32(MAC16_16(MULT16_16(c, x1), ms, x2), 15)); } } #define OVERRIDE_renormalise_vector #define renormalise_vector(X, N, gain, arch) \ (renormalise_vector_mips(X, N, gain, arch)) void renormalise_vector_mips(celt_norm *X, int N, opus_val16 gain, int arch) { int i; #ifdef FIXED_POINT int k; #endif opus_val32 E = EPSILON; opus_val16 g; opus_val32 t; celt_norm *xptr = X; int X0, X1; (void)arch; asm volatile("mult $ac1, $0, $0"); asm volatile("MTLO %0, $ac1" : :"r" (E)); /*if(N %4) printf("error");*/ for (i=0;i>1; #endif t = VSHR32(E, 2*(k-7)); g = MULT16_16_P15(celt_rsqrt_norm(t),gain); xptr = X; for (i=0;i= Fs) break; /* Find where the linear part ends (i.e. where the spacing is more than min_width */ for (lin=0;lin= res) break; low = (bark_freq[lin]+res/2)/res; high = nBark-lin; *nbEBands = low+high; eBands = opus_alloc(sizeof(opus_int16)*(*nbEBands+2)); if (eBands==NULL) return NULL; /* Linear spacing (min_width) */ for (i=0;i0) offset = eBands[low-1]*res - bark_freq[lin-1]; /* Spacing follows critical bands */ for (i=0;i frame_size) eBands[*nbEBands] = frame_size; for (i=1;i<*nbEBands-1;i++) { if (eBands[i+1]-eBands[i] < eBands[i]-eBands[i-1]) { eBands[i] -= (2*eBands[i]-eBands[i-1]-eBands[i+1])/2; } } /* Remove any empty bands. */ for (i=j=0;i<*nbEBands;i++) if(eBands[i+1]>eBands[j]) eBands[++j]=eBands[i+1]; *nbEBands=j; for (i=1;i<*nbEBands;i++) { /* Every band must be smaller than the last band. */ celt_assert(eBands[i]-eBands[i-1]<=eBands[*nbEBands]-eBands[*nbEBands-1]); /* Each band must be no larger than twice the size of the previous one. */ celt_assert(eBands[i+1]-eBands[i]<=2*(eBands[i]-eBands[i-1])); } return eBands; } static void compute_allocation_table(CELTMode *mode) { int i, j; unsigned char *allocVectors; int maxBands = sizeof(eband5ms)/sizeof(eband5ms[0])-1; mode->nbAllocVectors = BITALLOC_SIZE; allocVectors = opus_alloc(sizeof(unsigned char)*(BITALLOC_SIZE*mode->nbEBands)); if (allocVectors==NULL) return; /* Check for standard mode */ if (mode->Fs == 400*(opus_int32)mode->shortMdctSize) { for (i=0;inbEBands;i++) allocVectors[i] = band_allocation[i]; mode->allocVectors = allocVectors; return; } /* If not the standard mode, interpolate */ /* Compute per-codec-band allocation from per-critical-band matrix */ for (i=0;inbEBands;j++) { int k; for (k=0;k mode->eBands[j]*(opus_int32)mode->Fs/mode->shortMdctSize) break; } if (k>maxBands-1) allocVectors[i*mode->nbEBands+j] = band_allocation[i*maxBands + maxBands-1]; else { opus_int32 a0, a1; a1 = mode->eBands[j]*(opus_int32)mode->Fs/mode->shortMdctSize - 400*(opus_int32)eband5ms[k-1]; a0 = 400*(opus_int32)eband5ms[k] - mode->eBands[j]*(opus_int32)mode->Fs/mode->shortMdctSize; allocVectors[i*mode->nbEBands+j] = (a0*band_allocation[i*maxBands+k-1] + a1*band_allocation[i*maxBands+k])/(a0+a1); } } } /*printf ("\n"); for (i=0;inbEBands;j++) printf ("%d ", allocVectors[i*mode->nbEBands+j]); printf ("\n"); } exit(0);*/ mode->allocVectors = allocVectors; } #endif /* CUSTOM_MODES */ CELTMode *opus_custom_mode_create(opus_int32 Fs, int frame_size, int *error) { int i; #ifdef CUSTOM_MODES CELTMode *mode=NULL; int res; opus_val16 *window; opus_int16 *logN; int LM; int arch = opus_select_arch(); ALLOC_STACK; #if !defined(VAR_ARRAYS) && !defined(USE_ALLOCA) if (global_stack==NULL) goto failure; #endif #endif #ifndef CUSTOM_MODES_ONLY for (i=0;iFs && (frame_size<shortMdctSize*static_mode_list[i]->nbShortMdcts) { if (error) *error = OPUS_OK; return (CELTMode*)static_mode_list[i]; } } } #endif /* CUSTOM_MODES_ONLY */ #ifndef CUSTOM_MODES if (error) *error = OPUS_BAD_ARG; return NULL; #else /* The good thing here is that permutation of the arguments will automatically be invalid */ if (Fs < 8000 || Fs > 96000) { if (error) *error = OPUS_BAD_ARG; return NULL; } if (frame_size < 40 || frame_size > 1024 || frame_size%2!=0) { if (error) *error = OPUS_BAD_ARG; return NULL; } /* Frames of less than 1ms are not supported. */ if ((opus_int32)frame_size*1000 < Fs) { if (error) *error = OPUS_BAD_ARG; return NULL; } if ((opus_int32)frame_size*75 >= Fs && (frame_size%16)==0) { LM = 3; } else if ((opus_int32)frame_size*150 >= Fs && (frame_size%8)==0) { LM = 2; } else if ((opus_int32)frame_size*300 >= Fs && (frame_size%4)==0) { LM = 1; } else { LM = 0; } /* Shorts longer than 3.3ms are not supported. */ if ((opus_int32)(frame_size>>LM)*300 > Fs) { if (error) *error = OPUS_BAD_ARG; return NULL; } mode = opus_alloc(sizeof(CELTMode)); if (mode==NULL) goto failure; mode->Fs = Fs; /* Pre/de-emphasis depends on sampling rate. The "standard" pre-emphasis is defined as A(z) = 1 - 0.85*z^-1 at 48 kHz. Other rates should approximate that. */ if(Fs < 12000) /* 8 kHz */ { mode->preemph[0] = QCONST16(0.3500061035f, 15); mode->preemph[1] = -QCONST16(0.1799926758f, 15); mode->preemph[2] = QCONST16(0.2719968125f, SIG_SHIFT); /* exact 1/preemph[3] */ mode->preemph[3] = QCONST16(3.6765136719f, 13); } else if(Fs < 24000) /* 16 kHz */ { mode->preemph[0] = QCONST16(0.6000061035f, 15); mode->preemph[1] = -QCONST16(0.1799926758f, 15); mode->preemph[2] = QCONST16(0.4424998650f, SIG_SHIFT); /* exact 1/preemph[3] */ mode->preemph[3] = QCONST16(2.2598876953f, 13); } else if(Fs < 40000) /* 32 kHz */ { mode->preemph[0] = QCONST16(0.7799987793f, 15); mode->preemph[1] = -QCONST16(0.1000061035f, 15); mode->preemph[2] = QCONST16(0.7499771125f, SIG_SHIFT); /* exact 1/preemph[3] */ mode->preemph[3] = QCONST16(1.3333740234f, 13); } else /* 48 kHz */ { mode->preemph[0] = QCONST16(0.8500061035f, 15); mode->preemph[1] = QCONST16(0.0f, 15); mode->preemph[2] = QCONST16(1.f, SIG_SHIFT); mode->preemph[3] = QCONST16(1.f, 13); } mode->maxLM = LM; mode->nbShortMdcts = 1<shortMdctSize = frame_size/mode->nbShortMdcts; res = (mode->Fs+mode->shortMdctSize)/(2*mode->shortMdctSize); mode->eBands = compute_ebands(Fs, mode->shortMdctSize, res, &mode->nbEBands); if (mode->eBands==NULL) goto failure; #if !defined(SMALL_FOOTPRINT) /* Make sure we don't allocate a band larger than our PVQ table. 208 should be enough, but let's be paranoid. */ if ((mode->eBands[mode->nbEBands] - mode->eBands[mode->nbEBands-1])< 208) { goto failure; } #endif mode->effEBands = mode->nbEBands; while (mode->eBands[mode->effEBands] > mode->shortMdctSize) mode->effEBands--; /* Overlap must be divisible by 4 */ mode->overlap = ((mode->shortMdctSize>>2)<<2); compute_allocation_table(mode); if (mode->allocVectors==NULL) goto failure; window = (opus_val16*)opus_alloc(mode->overlap*sizeof(opus_val16)); if (window==NULL) goto failure; #ifndef FIXED_POINT for (i=0;ioverlap;i++) window[i] = Q15ONE*sin(.5*M_PI* sin(.5*M_PI*(i+.5)/mode->overlap) * sin(.5*M_PI*(i+.5)/mode->overlap)); #else for (i=0;ioverlap;i++) window[i] = MIN32(32767,floor(.5+32768.*sin(.5*M_PI* sin(.5*M_PI*(i+.5)/mode->overlap) * sin(.5*M_PI*(i+.5)/mode->overlap)))); #endif mode->window = window; logN = (opus_int16*)opus_alloc(mode->nbEBands*sizeof(opus_int16)); if (logN==NULL) goto failure; for (i=0;inbEBands;i++) logN[i] = log2_frac(mode->eBands[i+1]-mode->eBands[i], BITRES); mode->logN = logN; compute_pulse_cache(mode, mode->maxLM); if (clt_mdct_init(&mode->mdct, 2*mode->shortMdctSize*mode->nbShortMdcts, mode->maxLM, arch) == 0) goto failure; if (error) *error = OPUS_OK; return mode; failure: if (error) *error = OPUS_ALLOC_FAIL; if (mode!=NULL) opus_custom_mode_destroy(mode); return NULL; #endif /* !CUSTOM_MODES */ } #ifdef CUSTOM_MODES void opus_custom_mode_destroy(CELTMode *mode) { int arch = opus_select_arch(); if (mode == NULL) return; #ifndef CUSTOM_MODES_ONLY { int i; for (i=0;ieBands); opus_free((opus_int16*)mode->allocVectors); opus_free((opus_val16*)mode->window); opus_free((opus_int16*)mode->logN); opus_free((opus_int16*)mode->cache.index); opus_free((unsigned char*)mode->cache.bits); opus_free((unsigned char*)mode->cache.caps); clt_mdct_clear(&mode->mdct, arch); opus_free((CELTMode *)mode); } #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/modes.h ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Copyright (c) 2008 Gregory Maxwell Written by Jean-Marc Valin and Gregory Maxwell */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MODES_H #define MODES_H #include "opus_types.h" #include "celt.h" #include "arch.h" #include "mdct.h" #include "entenc.h" #include "entdec.h" #define MAX_PERIOD 1024 typedef struct { int size; const opus_int16 *index; const unsigned char *bits; const unsigned char *caps; } PulseCache; /** Mode definition (opaque) @brief Mode definition */ struct OpusCustomMode { opus_int32 Fs; int overlap; int nbEBands; int effEBands; opus_val16 preemph[4]; const opus_int16 *eBands; /**< Definition for each "pseudo-critical band" */ int maxLM; int nbShortMdcts; int shortMdctSize; int nbAllocVectors; /**< Number of lines in the matrix below */ const unsigned char *allocVectors; /**< Number of bits in each band for several rates */ const opus_int16 *logN; const opus_val16 *window; mdct_lookup mdct; PulseCache cache; }; #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/opus_custom_demo.c ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "opus_custom.h" #include "arch.h" #include #include #include #include #define MAX_PACKET 1275 int main(int argc, char *argv[]) { int err; char *inFile, *outFile; FILE *fin, *fout; OpusCustomMode *mode=NULL; OpusCustomEncoder *enc; OpusCustomDecoder *dec; int len; opus_int32 frame_size, channels, rate; int bytes_per_packet; unsigned char data[MAX_PACKET]; int complexity; #if !(defined (FIXED_POINT) && !defined(CUSTOM_MODES)) && defined(RESYNTH) int i; double rmsd = 0; #endif int count = 0; opus_int32 skip; opus_int16 *in, *out; if (argc != 9 && argc != 8 && argc != 7) { fprintf (stderr, "Usage: test_opus_custom " " [ [packet loss rate]] " " \n"); return 1; } rate = (opus_int32)atol(argv[1]); channels = atoi(argv[2]); frame_size = atoi(argv[3]); mode = opus_custom_mode_create(rate, frame_size, NULL); if (mode == NULL) { fprintf(stderr, "failed to create a mode\n"); return 1; } bytes_per_packet = atoi(argv[4]); if (bytes_per_packet < 0 || bytes_per_packet > MAX_PACKET) { fprintf (stderr, "bytes per packet must be between 0 and %d\n", MAX_PACKET); return 1; } inFile = argv[argc-2]; fin = fopen(inFile, "rb"); if (!fin) { fprintf (stderr, "Could not open input file %s\n", argv[argc-2]); return 1; } outFile = argv[argc-1]; fout = fopen(outFile, "wb+"); if (!fout) { fprintf (stderr, "Could not open output file %s\n", argv[argc-1]); fclose(fin); return 1; } enc = opus_custom_encoder_create(mode, channels, &err); if (err != 0) { fprintf(stderr, "Failed to create the encoder: %s\n", opus_strerror(err)); fclose(fin); fclose(fout); return 1; } dec = opus_custom_decoder_create(mode, channels, &err); if (err != 0) { fprintf(stderr, "Failed to create the decoder: %s\n", opus_strerror(err)); fclose(fin); fclose(fout); return 1; } opus_custom_decoder_ctl(dec, OPUS_GET_LOOKAHEAD(&skip)); if (argc>7) { complexity=atoi(argv[5]); opus_custom_encoder_ctl(enc,OPUS_SET_COMPLEXITY(complexity)); } in = (opus_int16*)malloc(frame_size*channels*sizeof(opus_int16)); out = (opus_int16*)malloc(frame_size*channels*sizeof(opus_int16)); while (!feof(fin)) { int ret; err = fread(in, sizeof(short), frame_size*channels, fin); if (feof(fin)) break; len = opus_custom_encode(enc, in, frame_size, data, bytes_per_packet); if (len <= 0) fprintf (stderr, "opus_custom_encode() failed: %s\n", opus_strerror(len)); /* This is for simulating bit errors */ #if 0 int errors = 0; int eid = 0; /* This simulates random bit error */ for (i=0;i 0) { rmsd = sqrt(rmsd/(1.0*frame_size*channels*count)); fprintf (stderr, "Error: encoder doesn't match decoder\n"); fprintf (stderr, "RMS mismatch is %f\n", rmsd); return 1; } else { fprintf (stderr, "Encoder matches decoder!!\n"); } #endif return 0; } ================================================ FILE: deps/pjsip/third_party/opus/celt/os_support.h ================================================ /* Copyright (C) 2007 Jean-Marc Valin File: os_support.h This is the (tiny) OS abstraction layer. Aside from math.h, this is the only place where system headers are allowed. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef OS_SUPPORT_H #define OS_SUPPORT_H #ifdef CUSTOM_SUPPORT # include "custom_support.h" #endif #include "opus_types.h" #include "opus_defines.h" #include #include #include /** Opus wrapper for malloc(). To do your own dynamic allocation, all you need to do is replace this function and opus_free */ #ifndef OVERRIDE_OPUS_ALLOC static OPUS_INLINE void *opus_alloc (size_t size) { return malloc(size); } #endif /** Same as celt_alloc(), except that the area is only needed inside a CELT call (might cause problem with wideband though) */ #ifndef OVERRIDE_OPUS_ALLOC_SCRATCH static OPUS_INLINE void *opus_alloc_scratch (size_t size) { /* Scratch space doesn't need to be cleared */ return opus_alloc(size); } #endif /** Opus wrapper for free(). To do your own dynamic allocation, all you need to do is replace this function and opus_alloc */ #ifndef OVERRIDE_OPUS_FREE static OPUS_INLINE void opus_free (void *ptr) { free(ptr); } #endif /** Copy n elements from src to dst. The 0* term provides compile-time type checking */ #ifndef OVERRIDE_OPUS_COPY #define OPUS_COPY(dst, src, n) (memcpy((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) )) #endif /** Copy n elements from src to dst, allowing overlapping regions. The 0* term provides compile-time type checking */ #ifndef OVERRIDE_OPUS_MOVE #define OPUS_MOVE(dst, src, n) (memmove((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) )) #endif /** Set n elements of dst to zero */ #ifndef OVERRIDE_OPUS_CLEAR #define OPUS_CLEAR(dst, n) (memset((dst), 0, (n)*sizeof(*(dst)))) #endif /*#ifdef __GNUC__ #pragma GCC poison printf sprintf #pragma GCC poison malloc free realloc calloc #endif*/ #endif /* OS_SUPPORT_H */ ================================================ FILE: deps/pjsip/third_party/opus/celt/pitch.c ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Written by Jean-Marc Valin */ /** @file pitch.c @brief Pitch analysis */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "pitch.h" #include "os_support.h" #include "modes.h" #include "stack_alloc.h" #include "mathops.h" #include "celt_lpc.h" static void find_best_pitch(opus_val32 *xcorr, opus_val16 *y, int len, int max_pitch, int *best_pitch #ifdef FIXED_POINT , int yshift, opus_val32 maxcorr #endif ) { int i, j; opus_val32 Syy=1; opus_val16 best_num[2]; opus_val32 best_den[2]; #ifdef FIXED_POINT int xshift; xshift = celt_ilog2(maxcorr)-14; #endif best_num[0] = -1; best_num[1] = -1; best_den[0] = 0; best_den[1] = 0; best_pitch[0] = 0; best_pitch[1] = 1; for (j=0;j0) { opus_val16 num; opus_val32 xcorr16; xcorr16 = EXTRACT16(VSHR32(xcorr[i], xshift)); #ifndef FIXED_POINT /* Considering the range of xcorr16, this should avoid both underflows and overflows (inf) when squaring xcorr16 */ xcorr16 *= 1e-12f; #endif num = MULT16_16_Q15(xcorr16,xcorr16); if (MULT16_32_Q15(num,best_den[1]) > MULT16_32_Q15(best_num[1],Syy)) { if (MULT16_32_Q15(num,best_den[0]) > MULT16_32_Q15(best_num[0],Syy)) { best_num[1] = best_num[0]; best_den[1] = best_den[0]; best_pitch[1] = best_pitch[0]; best_num[0] = num; best_den[0] = Syy; best_pitch[0] = i; } else { best_num[1] = num; best_den[1] = Syy; best_pitch[1] = i; } } } Syy += SHR32(MULT16_16(y[i+len],y[i+len]),yshift) - SHR32(MULT16_16(y[i],y[i]),yshift); Syy = MAX32(1, Syy); } } static void celt_fir5(const opus_val16 *x, const opus_val16 *num, opus_val16 *y, int N, opus_val16 *mem) { int i; opus_val16 num0, num1, num2, num3, num4; opus_val32 mem0, mem1, mem2, mem3, mem4; num0=num[0]; num1=num[1]; num2=num[2]; num3=num[3]; num4=num[4]; mem0=mem[0]; mem1=mem[1]; mem2=mem[2]; mem3=mem[3]; mem4=mem[4]; for (i=0;i>1;i++) x_lp[i] = SHR32(HALF32(HALF32(x[0][(2*i-1)]+x[0][(2*i+1)])+x[0][2*i]), shift); x_lp[0] = SHR32(HALF32(HALF32(x[0][1])+x[0][0]), shift); if (C==2) { for (i=1;i>1;i++) x_lp[i] += SHR32(HALF32(HALF32(x[1][(2*i-1)]+x[1][(2*i+1)])+x[1][2*i]), shift); x_lp[0] += SHR32(HALF32(HALF32(x[1][1])+x[1][0]), shift); } _celt_autocorr(x_lp, ac, NULL, 0, 4, len>>1, arch); /* Noise floor -40 dB */ #ifdef FIXED_POINT ac[0] += SHR32(ac[0],13); #else ac[0] *= 1.0001f; #endif /* Lag windowing */ for (i=1;i<=4;i++) { /*ac[i] *= exp(-.5*(2*M_PI*.002*i)*(2*M_PI*.002*i));*/ #ifdef FIXED_POINT ac[i] -= MULT16_32_Q15(2*i*i, ac[i]); #else ac[i] -= ac[i]*(.008f*i)*(.008f*i); #endif } _celt_lpc(lpc, ac, 4); for (i=0;i<4;i++) { tmp = MULT16_16_Q15(QCONST16(.9f,15), tmp); lpc[i] = MULT16_16_Q15(lpc[i], tmp); } /* Add a zero */ lpc2[0] = lpc[0] + QCONST16(.8f,SIG_SHIFT); lpc2[1] = lpc[1] + MULT16_16_Q15(c1,lpc[0]); lpc2[2] = lpc[2] + MULT16_16_Q15(c1,lpc[1]); lpc2[3] = lpc[3] + MULT16_16_Q15(c1,lpc[2]); lpc2[4] = MULT16_16_Q15(c1,lpc[3]); celt_fir5(x_lp, lpc2, x_lp, len>>1, mem); } /* Pure C implementation. */ #ifdef FIXED_POINT opus_val32 #else void #endif #if defined(OVERRIDE_PITCH_XCORR) celt_pitch_xcorr_c(const opus_val16 *_x, const opus_val16 *_y, opus_val32 *xcorr, int len, int max_pitch) #else celt_pitch_xcorr(const opus_val16 *_x, const opus_val16 *_y, opus_val32 *xcorr, int len, int max_pitch, int arch) #endif { #if 0 /* This is a simple version of the pitch correlation that should work well on DSPs like Blackfin and TI C5x/C6x */ int i, j; #ifdef FIXED_POINT opus_val32 maxcorr=1; #endif #if !defined(OVERRIDE_PITCH_XCORR) (void)arch; #endif for (i=0;i0); celt_assert((((unsigned char *)_x-(unsigned char *)NULL)&3)==0); for (i=0;i0); celt_assert(max_pitch>0); lag = len+max_pitch; ALLOC(x_lp4, len>>2, opus_val16); ALLOC(y_lp4, lag>>2, opus_val16); ALLOC(xcorr, max_pitch>>1, opus_val32); /* Downsample by 2 again */ for (j=0;j>2;j++) x_lp4[j] = x_lp[2*j]; for (j=0;j>2;j++) y_lp4[j] = y[2*j]; #ifdef FIXED_POINT xmax = celt_maxabs16(x_lp4, len>>2); ymax = celt_maxabs16(y_lp4, lag>>2); shift = celt_ilog2(MAX32(1, MAX32(xmax, ymax)))-11; if (shift>0) { for (j=0;j>2;j++) x_lp4[j] = SHR16(x_lp4[j], shift); for (j=0;j>2;j++) y_lp4[j] = SHR16(y_lp4[j], shift); /* Use double the shift for a MAC */ shift *= 2; } else { shift = 0; } #endif /* Coarse search with 4x decimation */ #ifdef FIXED_POINT maxcorr = #endif celt_pitch_xcorr(x_lp4, y_lp4, xcorr, len>>2, max_pitch>>2, arch); find_best_pitch(xcorr, y_lp4, len>>2, max_pitch>>2, best_pitch #ifdef FIXED_POINT , 0, maxcorr #endif ); /* Finer search with 2x decimation */ #ifdef FIXED_POINT maxcorr=1; #endif for (i=0;i>1;i++) { opus_val32 sum; xcorr[i] = 0; if (abs(i-2*best_pitch[0])>2 && abs(i-2*best_pitch[1])>2) continue; #ifdef FIXED_POINT sum = 0; for (j=0;j>1;j++) sum += SHR32(MULT16_16(x_lp[j],y[i+j]), shift); #else sum = celt_inner_prod_c(x_lp, y+i, len>>1); #endif xcorr[i] = MAX32(-1, sum); #ifdef FIXED_POINT maxcorr = MAX32(maxcorr, sum); #endif } find_best_pitch(xcorr, y, len>>1, max_pitch>>1, best_pitch #ifdef FIXED_POINT , shift+1, maxcorr #endif ); /* Refine by pseudo-interpolation */ if (best_pitch[0]>0 && best_pitch[0]<(max_pitch>>1)-1) { opus_val32 a, b, c; a = xcorr[best_pitch[0]-1]; b = xcorr[best_pitch[0]]; c = xcorr[best_pitch[0]+1]; if ((c-a) > MULT16_32_Q15(QCONST16(.7f,15),b-a)) offset = 1; else if ((a-c) > MULT16_32_Q15(QCONST16(.7f,15),b-c)) offset = -1; else offset = 0; } else { offset = 0; } *pitch = 2*best_pitch[0]-offset; RESTORE_STACK; } static const int second_check[16] = {0, 0, 3, 2, 3, 2, 5, 2, 3, 2, 3, 2, 5, 2, 3, 2}; opus_val16 remove_doubling(opus_val16 *x, int maxperiod, int minperiod, int N, int *T0_, int prev_period, opus_val16 prev_gain, int arch) { int k, i, T, T0; opus_val16 g, g0; opus_val16 pg; opus_val32 xy,xx,yy,xy2; opus_val32 xcorr[3]; opus_val32 best_xy, best_yy; int offset; int minperiod0; VARDECL(opus_val32, yy_lookup); SAVE_STACK; minperiod0 = minperiod; maxperiod /= 2; minperiod /= 2; *T0_ /= 2; prev_period /= 2; N /= 2; x += maxperiod; if (*T0_>=maxperiod) *T0_=maxperiod-1; T = T0 = *T0_; ALLOC(yy_lookup, maxperiod+1, opus_val32); dual_inner_prod(x, x, x-T0, N, &xx, &xy, arch); yy_lookup[0] = xx; yy=xx; for (i=1;i<=maxperiod;i++) { yy = yy+MULT16_16(x[-i],x[-i])-MULT16_16(x[N-i],x[N-i]); yy_lookup[i] = MAX32(0, yy); } yy = yy_lookup[T0]; best_xy = xy; best_yy = yy; #ifdef FIXED_POINT { opus_val32 x2y2; int sh, t; x2y2 = 1+HALF32(MULT32_32_Q31(xx,yy)); sh = celt_ilog2(x2y2)>>1; t = VSHR32(x2y2, 2*(sh-7)); g = g0 = VSHR32(MULT16_32_Q15(celt_rsqrt_norm(t), xy),sh+1); } #else g = g0 = xy/celt_sqrt(1+xx*yy); #endif /* Look for any pitch at T/k */ for (k=2;k<=15;k++) { int T1, T1b; opus_val16 g1; opus_val16 cont=0; opus_val16 thresh; T1 = celt_udiv(2*T0+k, 2*k); if (T1 < minperiod) break; /* Look for another strong correlation at T1b */ if (k==2) { if (T1+T0>maxperiod) T1b = T0; else T1b = T0+T1; } else { T1b = celt_udiv(2*second_check[k]*T0+k, 2*k); } dual_inner_prod(x, &x[-T1], &x[-T1b], N, &xy, &xy2, arch); xy += xy2; yy = yy_lookup[T1] + yy_lookup[T1b]; #ifdef FIXED_POINT { opus_val32 x2y2; int sh, t; x2y2 = 1+MULT32_32_Q31(xx,yy); sh = celt_ilog2(x2y2)>>1; t = VSHR32(x2y2, 2*(sh-7)); g1 = VSHR32(MULT16_32_Q15(celt_rsqrt_norm(t), xy),sh+1); } #else g1 = xy/celt_sqrt(1+2.f*xx*1.f*yy); #endif if (abs(T1-prev_period)<=1) cont = prev_gain; else if (abs(T1-prev_period)<=2 && 5*k*k < T0) cont = HALF32(prev_gain); else cont = 0; thresh = MAX16(QCONST16(.3f,15), MULT16_16_Q15(QCONST16(.7f,15),g0)-cont); /* Bias against very high pitch (very short period) to avoid false-positives due to short-term correlation */ if (T1<3*minperiod) thresh = MAX16(QCONST16(.4f,15), MULT16_16_Q15(QCONST16(.85f,15),g0)-cont); else if (T1<2*minperiod) thresh = MAX16(QCONST16(.5f,15), MULT16_16_Q15(QCONST16(.9f,15),g0)-cont); if (g1 > thresh) { best_xy = xy; best_yy = yy; T = T1; g = g1; } } best_xy = MAX32(0, best_xy); if (best_yy <= best_xy) pg = Q15ONE; else pg = SHR32(frac_div32(best_xy,best_yy+1),16); for (k=0;k<3;k++) xcorr[k] = celt_inner_prod(x, x-(T+k-1), N, arch); if ((xcorr[2]-xcorr[0]) > MULT16_32_Q15(QCONST16(.7f,15),xcorr[1]-xcorr[0])) offset = 1; else if ((xcorr[0]-xcorr[2]) > MULT16_32_Q15(QCONST16(.7f,15),xcorr[1]-xcorr[2])) offset = -1; else offset = 0; if (pg > g) pg = g; *T0_ = 2*T+offset; if (*T0_=3); y_3=0; /* gcc doesn't realize that y_3 can't be used uninitialized */ y_0=*y++; y_1=*y++; y_2=*y++; for (j=0;j #include "os_support.h" #include "arch.h" #include "mathops.h" #include "stack_alloc.h" #include "rate.h" #ifdef FIXED_POINT /* Mean energy in each band quantized in Q4 */ const signed char eMeans[25] = { 103,100, 92, 85, 81, 77, 72, 70, 78, 75, 73, 71, 78, 74, 69, 72, 70, 74, 76, 71, 60, 60, 60, 60, 60 }; #else /* Mean energy in each band quantized in Q4 and converted back to float */ const opus_val16 eMeans[25] = { 6.437500f, 6.250000f, 5.750000f, 5.312500f, 5.062500f, 4.812500f, 4.500000f, 4.375000f, 4.875000f, 4.687500f, 4.562500f, 4.437500f, 4.875000f, 4.625000f, 4.312500f, 4.500000f, 4.375000f, 4.625000f, 4.750000f, 4.437500f, 3.750000f, 3.750000f, 3.750000f, 3.750000f, 3.750000f }; #endif /* prediction coefficients: 0.9, 0.8, 0.65, 0.5 */ #ifdef FIXED_POINT static const opus_val16 pred_coef[4] = {29440, 26112, 21248, 16384}; static const opus_val16 beta_coef[4] = {30147, 22282, 12124, 6554}; static const opus_val16 beta_intra = 4915; #else static const opus_val16 pred_coef[4] = {29440/32768., 26112/32768., 21248/32768., 16384/32768.}; static const opus_val16 beta_coef[4] = {30147/32768., 22282/32768., 12124/32768., 6554/32768.}; static const opus_val16 beta_intra = 4915/32768.; #endif /*Parameters of the Laplace-like probability models used for the coarse energy. There is one pair of parameters for each frame size, prediction type (inter/intra), and band number. The first number of each pair is the probability of 0, and the second is the decay rate, both in Q8 precision.*/ static const unsigned char e_prob_model[4][2][42] = { /*120 sample frames.*/ { /*Inter*/ { 72, 127, 65, 129, 66, 128, 65, 128, 64, 128, 62, 128, 64, 128, 64, 128, 92, 78, 92, 79, 92, 78, 90, 79, 116, 41, 115, 40, 114, 40, 132, 26, 132, 26, 145, 17, 161, 12, 176, 10, 177, 11 }, /*Intra*/ { 24, 179, 48, 138, 54, 135, 54, 132, 53, 134, 56, 133, 55, 132, 55, 132, 61, 114, 70, 96, 74, 88, 75, 88, 87, 74, 89, 66, 91, 67, 100, 59, 108, 50, 120, 40, 122, 37, 97, 43, 78, 50 } }, /*240 sample frames.*/ { /*Inter*/ { 83, 78, 84, 81, 88, 75, 86, 74, 87, 71, 90, 73, 93, 74, 93, 74, 109, 40, 114, 36, 117, 34, 117, 34, 143, 17, 145, 18, 146, 19, 162, 12, 165, 10, 178, 7, 189, 6, 190, 8, 177, 9 }, /*Intra*/ { 23, 178, 54, 115, 63, 102, 66, 98, 69, 99, 74, 89, 71, 91, 73, 91, 78, 89, 86, 80, 92, 66, 93, 64, 102, 59, 103, 60, 104, 60, 117, 52, 123, 44, 138, 35, 133, 31, 97, 38, 77, 45 } }, /*480 sample frames.*/ { /*Inter*/ { 61, 90, 93, 60, 105, 42, 107, 41, 110, 45, 116, 38, 113, 38, 112, 38, 124, 26, 132, 27, 136, 19, 140, 20, 155, 14, 159, 16, 158, 18, 170, 13, 177, 10, 187, 8, 192, 6, 175, 9, 159, 10 }, /*Intra*/ { 21, 178, 59, 110, 71, 86, 75, 85, 84, 83, 91, 66, 88, 73, 87, 72, 92, 75, 98, 72, 105, 58, 107, 54, 115, 52, 114, 55, 112, 56, 129, 51, 132, 40, 150, 33, 140, 29, 98, 35, 77, 42 } }, /*960 sample frames.*/ { /*Inter*/ { 42, 121, 96, 66, 108, 43, 111, 40, 117, 44, 123, 32, 120, 36, 119, 33, 127, 33, 134, 34, 139, 21, 147, 23, 152, 20, 158, 25, 154, 26, 166, 21, 173, 16, 184, 13, 184, 10, 150, 13, 139, 15 }, /*Intra*/ { 22, 178, 63, 114, 74, 82, 84, 83, 92, 82, 103, 62, 96, 72, 96, 67, 101, 73, 107, 72, 113, 55, 118, 52, 125, 52, 118, 52, 117, 55, 135, 49, 137, 39, 157, 32, 145, 29, 97, 33, 77, 40 } } }; static const unsigned char small_energy_icdf[3]={2,1,0}; static opus_val32 loss_distortion(const opus_val16 *eBands, opus_val16 *oldEBands, int start, int end, int len, int C) { int c, i; opus_val32 dist = 0; c=0; do { for (i=start;inbEBands]; oldE = MAX16(-QCONST16(9.f,DB_SHIFT), oldEBands[i+c*m->nbEBands]); #ifdef FIXED_POINT f = SHL32(EXTEND32(x),7) - PSHR32(MULT16_16(coef,oldE), 8) - prev[c]; /* Rounding to nearest integer here is really important! */ qi = (f+QCONST32(.5f,DB_SHIFT+7))>>(DB_SHIFT+7); decay_bound = EXTRACT16(MAX32(-QCONST16(28.f,DB_SHIFT), SUB32((opus_val32)oldEBands[i+c*m->nbEBands],max_decay))); #else f = x-coef*oldE-prev[c]; /* Rounding to nearest integer here is really important! */ qi = (int)floor(.5f+f); decay_bound = MAX16(-QCONST16(28.f,DB_SHIFT), oldEBands[i+c*m->nbEBands]) - max_decay; #endif /* Prevent the energy from going down too quickly (e.g. for bands that have just one bin) */ if (qi < 0 && x < decay_bound) { qi += (int)SHR16(SUB16(decay_bound,x), DB_SHIFT); if (qi > 0) qi = 0; } qi0 = qi; /* If we don't have enough bits to encode all the energy, just assume something safe. */ tell = ec_tell(enc); bits_left = budget-tell-3*C*(end-i); if (i!=start && bits_left < 30) { if (bits_left < 24) qi = IMIN(1, qi); if (bits_left < 16) qi = IMAX(-1, qi); } if (lfe && i>=2) qi = IMIN(qi, 0); if (budget-tell >= 15) { int pi; pi = 2*IMIN(i,20); ec_laplace_encode(enc, &qi, prob_model[pi]<<7, prob_model[pi+1]<<6); } else if(budget-tell >= 2) { qi = IMAX(-1, IMIN(qi, 1)); ec_enc_icdf(enc, 2*qi^-(qi<0), small_energy_icdf, 2); } else if(budget-tell >= 1) { qi = IMIN(0, qi); ec_enc_bit_logp(enc, -qi, 1); } else qi = -1; error[i+c*m->nbEBands] = PSHR32(f,7) - SHL16(qi,DB_SHIFT); badness += abs(qi0-qi); q = (opus_val32)SHL32(EXTEND32(qi),DB_SHIFT); tmp = PSHR32(MULT16_16(coef,oldE),8) + prev[c] + SHL32(q,7); #ifdef FIXED_POINT tmp = MAX32(-QCONST32(28.f, DB_SHIFT+7), tmp); #endif oldEBands[i+c*m->nbEBands] = PSHR32(tmp, 7); prev[c] = prev[c] + SHL32(q,7) - MULT16_16(beta,PSHR32(q,8)); } while (++c < C); } return lfe ? 0 : badness; } void quant_coarse_energy(const CELTMode *m, int start, int end, int effEnd, const opus_val16 *eBands, opus_val16 *oldEBands, opus_uint32 budget, opus_val16 *error, ec_enc *enc, int C, int LM, int nbAvailableBytes, int force_intra, opus_val32 *delayedIntra, int two_pass, int loss_rate, int lfe) { int intra; opus_val16 max_decay; VARDECL(opus_val16, oldEBands_intra); VARDECL(opus_val16, error_intra); ec_enc enc_start_state; opus_uint32 tell; int badness1=0; opus_int32 intra_bias; opus_val32 new_distortion; SAVE_STACK; intra = force_intra || (!two_pass && *delayedIntra>2*C*(end-start) && nbAvailableBytes > (end-start)*C); intra_bias = (opus_int32)((budget**delayedIntra*loss_rate)/(C*512)); new_distortion = loss_distortion(eBands, oldEBands, start, effEnd, m->nbEBands, C); tell = ec_tell(enc); if (tell+3 > budget) two_pass = intra = 0; max_decay = QCONST16(16.f,DB_SHIFT); if (end-start>10) { #ifdef FIXED_POINT max_decay = MIN32(max_decay, SHL32(EXTEND32(nbAvailableBytes),DB_SHIFT-3)); #else max_decay = MIN32(max_decay, .125f*nbAvailableBytes); #endif } if (lfe) max_decay = QCONST16(3.f,DB_SHIFT); enc_start_state = *enc; ALLOC(oldEBands_intra, C*m->nbEBands, opus_val16); ALLOC(error_intra, C*m->nbEBands, opus_val16); OPUS_COPY(oldEBands_intra, oldEBands, C*m->nbEBands); if (two_pass || intra) { badness1 = quant_coarse_energy_impl(m, start, end, eBands, oldEBands_intra, budget, tell, e_prob_model[LM][1], error_intra, enc, C, LM, 1, max_decay, lfe); } if (!intra) { unsigned char *intra_buf; ec_enc enc_intra_state; opus_int32 tell_intra; opus_uint32 nstart_bytes; opus_uint32 nintra_bytes; opus_uint32 save_bytes; int badness2; VARDECL(unsigned char, intra_bits); tell_intra = ec_tell_frac(enc); enc_intra_state = *enc; nstart_bytes = ec_range_bytes(&enc_start_state); nintra_bytes = ec_range_bytes(&enc_intra_state); intra_buf = ec_get_buffer(&enc_intra_state) + nstart_bytes; save_bytes = nintra_bytes-nstart_bytes; if (save_bytes == 0) save_bytes = ALLOC_NONE; ALLOC(intra_bits, save_bytes, unsigned char); /* Copy bits from intra bit-stream */ OPUS_COPY(intra_bits, intra_buf, nintra_bytes - nstart_bytes); *enc = enc_start_state; badness2 = quant_coarse_energy_impl(m, start, end, eBands, oldEBands, budget, tell, e_prob_model[LM][intra], error, enc, C, LM, 0, max_decay, lfe); if (two_pass && (badness1 < badness2 || (badness1 == badness2 && ((opus_int32)ec_tell_frac(enc))+intra_bias > tell_intra))) { *enc = enc_intra_state; /* Copy intra bits to bit-stream */ OPUS_COPY(intra_buf, intra_bits, nintra_bytes - nstart_bytes); OPUS_COPY(oldEBands, oldEBands_intra, C*m->nbEBands); OPUS_COPY(error, error_intra, C*m->nbEBands); intra = 1; } } else { OPUS_COPY(oldEBands, oldEBands_intra, C*m->nbEBands); OPUS_COPY(error, error_intra, C*m->nbEBands); } if (intra) *delayedIntra = new_distortion; else *delayedIntra = ADD32(MULT16_32_Q15(MULT16_16_Q15(pred_coef[LM], pred_coef[LM]),*delayedIntra), new_distortion); RESTORE_STACK; } void quant_fine_energy(const CELTMode *m, int start, int end, opus_val16 *oldEBands, opus_val16 *error, int *fine_quant, ec_enc *enc, int C) { int i, c; /* Encode finer resolution */ for (i=start;inbEBands]+QCONST16(.5f,DB_SHIFT))>>(DB_SHIFT-fine_quant[i]); #else q2 = (int)floor((error[i+c*m->nbEBands]+.5f)*frac); #endif if (q2 > frac-1) q2 = frac-1; if (q2<0) q2 = 0; ec_enc_bits(enc, q2, fine_quant[i]); #ifdef FIXED_POINT offset = SUB16(SHR32(SHL32(EXTEND32(q2),DB_SHIFT)+QCONST16(.5f,DB_SHIFT),fine_quant[i]),QCONST16(.5f,DB_SHIFT)); #else offset = (q2+.5f)*(1<<(14-fine_quant[i]))*(1.f/16384) - .5f; #endif oldEBands[i+c*m->nbEBands] += offset; error[i+c*m->nbEBands] -= offset; /*printf ("%f ", error[i] - offset);*/ } while (++c < C); } } void quant_energy_finalise(const CELTMode *m, int start, int end, opus_val16 *oldEBands, opus_val16 *error, int *fine_quant, int *fine_priority, int bits_left, ec_enc *enc, int C) { int i, prio, c; /* Use up the remaining bits */ for (prio=0;prio<2;prio++) { for (i=start;i=C ;i++) { if (fine_quant[i] >= MAX_FINE_BITS || fine_priority[i]!=prio) continue; c=0; do { int q2; opus_val16 offset; q2 = error[i+c*m->nbEBands]<0 ? 0 : 1; ec_enc_bits(enc, q2, 1); #ifdef FIXED_POINT offset = SHR16(SHL16(q2,DB_SHIFT)-QCONST16(.5f,DB_SHIFT),fine_quant[i]+1); #else offset = (q2-.5f)*(1<<(14-fine_quant[i]-1))*(1.f/16384); #endif oldEBands[i+c*m->nbEBands] += offset; bits_left--; } while (++c < C); } } } void unquant_coarse_energy(const CELTMode *m, int start, int end, opus_val16 *oldEBands, int intra, ec_dec *dec, int C, int LM) { const unsigned char *prob_model = e_prob_model[LM][intra]; int i, c; opus_val32 prev[2] = {0, 0}; opus_val16 coef; opus_val16 beta; opus_int32 budget; opus_int32 tell; if (intra) { coef = 0; beta = beta_intra; } else { beta = beta_coef[LM]; coef = pred_coef[LM]; } budget = dec->storage*8; /* Decode at a fixed coarse resolution */ for (i=start;i=15) { int pi; pi = 2*IMIN(i,20); qi = ec_laplace_decode(dec, prob_model[pi]<<7, prob_model[pi+1]<<6); } else if(budget-tell>=2) { qi = ec_dec_icdf(dec, small_energy_icdf, 2); qi = (qi>>1)^-(qi&1); } else if(budget-tell>=1) { qi = -ec_dec_bit_logp(dec, 1); } else qi = -1; q = (opus_val32)SHL32(EXTEND32(qi),DB_SHIFT); oldEBands[i+c*m->nbEBands] = MAX16(-QCONST16(9.f,DB_SHIFT), oldEBands[i+c*m->nbEBands]); tmp = PSHR32(MULT16_16(coef,oldEBands[i+c*m->nbEBands]),8) + prev[c] + SHL32(q,7); #ifdef FIXED_POINT tmp = MAX32(-QCONST32(28.f, DB_SHIFT+7), tmp); #endif oldEBands[i+c*m->nbEBands] = PSHR32(tmp, 7); prev[c] = prev[c] + SHL32(q,7) - MULT16_16(beta,PSHR32(q,8)); } while (++c < C); } } void unquant_fine_energy(const CELTMode *m, int start, int end, opus_val16 *oldEBands, int *fine_quant, ec_dec *dec, int C) { int i, c; /* Decode finer resolution */ for (i=start;inbEBands] += offset; } while (++c < C); } } void unquant_energy_finalise(const CELTMode *m, int start, int end, opus_val16 *oldEBands, int *fine_quant, int *fine_priority, int bits_left, ec_dec *dec, int C) { int i, prio, c; /* Use up the remaining bits */ for (prio=0;prio<2;prio++) { for (i=start;i=C ;i++) { if (fine_quant[i] >= MAX_FINE_BITS || fine_priority[i]!=prio) continue; c=0; do { int q2; opus_val16 offset; q2 = ec_dec_bits(dec, 1); #ifdef FIXED_POINT offset = SHR16(SHL16(q2,DB_SHIFT)-QCONST16(.5f,DB_SHIFT),fine_quant[i]+1); #else offset = (q2-.5f)*(1<<(14-fine_quant[i]-1))*(1.f/16384); #endif oldEBands[i+c*m->nbEBands] += offset; bits_left--; } while (++c < C); } } } void amp2Log2(const CELTMode *m, int effEnd, int end, celt_ener *bandE, opus_val16 *bandLogE, int C) { int c, i; c=0; do { for (i=0;inbEBands] = celt_log2(SHL32(bandE[i+c*m->nbEBands],2)) - SHL16((opus_val16)eMeans[i],6); for (i=effEnd;inbEBands+i] = -QCONST16(14.f,DB_SHIFT); } while (++c < C); } ================================================ FILE: deps/pjsip/third_party/opus/celt/quant_bands.h ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef QUANT_BANDS #define QUANT_BANDS #include "arch.h" #include "modes.h" #include "entenc.h" #include "entdec.h" #include "mathops.h" #ifdef FIXED_POINT extern const signed char eMeans[25]; #else extern const opus_val16 eMeans[25]; #endif void amp2Log2(const CELTMode *m, int effEnd, int end, celt_ener *bandE, opus_val16 *bandLogE, int C); void log2Amp(const CELTMode *m, int start, int end, celt_ener *eBands, const opus_val16 *oldEBands, int C); void quant_coarse_energy(const CELTMode *m, int start, int end, int effEnd, const opus_val16 *eBands, opus_val16 *oldEBands, opus_uint32 budget, opus_val16 *error, ec_enc *enc, int C, int LM, int nbAvailableBytes, int force_intra, opus_val32 *delayedIntra, int two_pass, int loss_rate, int lfe); void quant_fine_energy(const CELTMode *m, int start, int end, opus_val16 *oldEBands, opus_val16 *error, int *fine_quant, ec_enc *enc, int C); void quant_energy_finalise(const CELTMode *m, int start, int end, opus_val16 *oldEBands, opus_val16 *error, int *fine_quant, int *fine_priority, int bits_left, ec_enc *enc, int C); void unquant_coarse_energy(const CELTMode *m, int start, int end, opus_val16 *oldEBands, int intra, ec_dec *dec, int C, int LM); void unquant_fine_energy(const CELTMode *m, int start, int end, opus_val16 *oldEBands, int *fine_quant, ec_dec *dec, int C); void unquant_energy_finalise(const CELTMode *m, int start, int end, opus_val16 *oldEBands, int *fine_quant, int *fine_priority, int bits_left, ec_dec *dec, int C); #endif /* QUANT_BANDS */ ================================================ FILE: deps/pjsip/third_party/opus/celt/rate.c ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "modes.h" #include "cwrs.h" #include "arch.h" #include "os_support.h" #include "entcode.h" #include "rate.h" static const unsigned char LOG2_FRAC_TABLE[24]={ 0, 8,13, 16,19,21,23, 24,26,27,28,29,30,31,32, 32,33,34,34,35,36,36,37,37 }; #ifdef CUSTOM_MODES /*Determines if V(N,K) fits in a 32-bit unsigned integer. N and K are themselves limited to 15 bits.*/ static int fits_in32(int _n, int _k) { static const opus_int16 maxN[15] = { 32767, 32767, 32767, 1476, 283, 109, 60, 40, 29, 24, 20, 18, 16, 14, 13}; static const opus_int16 maxK[15] = { 32767, 32767, 32767, 32767, 1172, 238, 95, 53, 36, 27, 22, 18, 16, 15, 13}; if (_n>=14) { if (_k>=14) return 0; else return _n <= maxN[_k]; } else { return _k <= maxK[_n]; } } void compute_pulse_cache(CELTMode *m, int LM) { int C; int i; int j; int curr=0; int nbEntries=0; int entryN[100], entryK[100], entryI[100]; const opus_int16 *eBands = m->eBands; PulseCache *cache = &m->cache; opus_int16 *cindex; unsigned char *bits; unsigned char *cap; cindex = (opus_int16 *)opus_alloc(sizeof(cache->index[0])*m->nbEBands*(LM+2)); cache->index = cindex; /* Scan for all unique band sizes */ for (i=0;i<=LM+1;i++) { for (j=0;jnbEBands;j++) { int k; int N = (eBands[j+1]-eBands[j])<>1; cindex[i*m->nbEBands+j] = -1; /* Find other bands that have the same size */ for (k=0;k<=i;k++) { int n; for (n=0;nnbEBands && (k!=i || n>1) { cindex[i*m->nbEBands+j] = cindex[k*m->nbEBands+n]; break; } } } if (cache->index[i*m->nbEBands+j] == -1 && N!=0) { int K; entryN[nbEntries] = N; K = 0; while (fits_in32(N,get_pulses(K+1)) && KnbEBands+j] = curr; entryI[nbEntries] = curr; curr += K+1; nbEntries++; } } } bits = (unsigned char *)opus_alloc(sizeof(unsigned char)*curr); cache->bits = bits; cache->size = curr; /* Compute the cache for all unique sizes */ for (i=0;icaps = cap = (unsigned char *)opus_alloc(sizeof(cache->caps[0])*(LM+1)*2*m->nbEBands); for (i=0;i<=LM;i++) { for (C=1;C<=2;C++) { for (j=0;jnbEBands;j++) { int N0; int max_bits; N0 = m->eBands[j+1]-m->eBands[j]; /* N=1 bands only have a sign bit and fine bits. */ if (N0<1 are even, including custom modes.*/ if (N0 > 2) { N0>>=1; LM0--; } /* N0=1 bands can't be split down to N<2. */ else if (N0 <= 1) { LM0=IMIN(i,1); N0<<=LM0; } /* Compute the cost for the lowest-level PVQ of a fully split band. */ pcache = bits + cindex[(LM0+1)*m->nbEBands+j]; max_bits = pcache[pcache[0]]+1; /* Add in the cost of coding regular splits. */ N = N0; for(k=0;klogN[j]+((LM0+k)<>1)-QTHETA_OFFSET; /* The number of qtheta bits we'll allocate if the remainder is to be max_bits. The average measured cost for theta is 0.89701 times qb, approximated here as 459/512. */ num=459*(opus_int32)((2*N-1)*offset+max_bits); den=((opus_int32)(2*N-1)<<9)-459; qb = IMIN((num+(den>>1))/den, 57); celt_assert(qb >= 0); max_bits += qb; N <<= 1; } /* Add in the cost of a stereo split, if necessary. */ if (C==2) { max_bits <<= 1; offset = ((m->logN[j]+(i<>1)-(N==2?QTHETA_OFFSET_TWOPHASE:QTHETA_OFFSET); ndof = 2*N-1-(N==2); /* The average measured cost for theta with the step PDF is 0.95164 times qb, approximated here as 487/512. */ num = (N==2?512:487)*(opus_int32)(max_bits+ndof*offset); den = ((opus_int32)ndof<<9)-(N==2?512:487); qb = IMIN((num+(den>>1))/den, (N==2?64:61)); celt_assert(qb >= 0); max_bits += qb; } /* Add the fine bits we'll use. */ /* Compensate for the extra DoF in stereo */ ndof = C*N + ((C==2 && N>2) ? 1 : 0); /* Offset the number of fine bits by log2(N)/2 + FINE_OFFSET compared to their "fair share" of total/N */ offset = ((m->logN[j] + (i<>1)-FINE_OFFSET; /* N=2 is the only point that doesn't match the curve */ if (N==2) offset += 1<>2; /* The number of fine bits we'll allocate if the remainder is to be max_bits. */ num = max_bits+ndof*offset; den = (ndof-1)<>1))/den, MAX_FINE_BITS); celt_assert(qb >= 0); max_bits += C*qb<eBands[j+1]-m->eBands[j])<= 0); celt_assert(max_bits < 256); *cap++ = (unsigned char)max_bits; } } } } #endif /* CUSTOM_MODES */ #define ALLOC_STEPS 6 static OPUS_INLINE int interp_bits2pulses(const CELTMode *m, int start, int end, int skip_start, const int *bits1, const int *bits2, const int *thresh, const int *cap, opus_int32 total, opus_int32 *_balance, int skip_rsv, int *intensity, int intensity_rsv, int *dual_stereo, int dual_stereo_rsv, int *bits, int *ebits, int *fine_priority, int C, int LM, ec_ctx *ec, int encode, int prev, int signalBandwidth) { opus_int32 psum; int lo, hi; int i, j; int logM; int stereo; int codedBands=-1; int alloc_floor; opus_int32 left, percoeff; int done; opus_int32 balance; SAVE_STACK; alloc_floor = C<1; logM = LM<>1; psum = 0; done = 0; for (j=end;j-->start;) { int tmp = bits1[j] + (mid*(opus_int32)bits2[j]>>ALLOC_STEPS); if (tmp >= thresh[j] || done) { done = 1; /* Don't allocate more than we can actually use */ psum += IMIN(tmp, cap[j]); } else { if (tmp >= alloc_floor) psum += alloc_floor; } } if (psum > total) hi = mid; else lo = mid; } psum = 0; /*printf ("interp bisection gave %d\n", lo);*/ done = 0; for (j=end;j-->start;) { int tmp = bits1[j] + (lo*bits2[j]>>ALLOC_STEPS); if (tmp < thresh[j] && !done) { if (tmp >= alloc_floor) tmp = alloc_floor; else tmp = 0; } else done = 1; /* Don't allocate more than we can actually use */ tmp = IMIN(tmp, cap[j]); bits[j] = tmp; psum += tmp; } /* Decide which bands to skip, working backwards from the end. */ for (codedBands=end;;codedBands--) { int band_width; int band_bits; int rem; j = codedBands-1; /* Never skip the first band, nor a band that has been boosted by dynalloc. In the first case, we'd be coding a bit to signal we're going to waste all the other bits. In the second case, we'd be coding a bit to redistribute all the bits we just signaled should be cocentrated in this band. */ if (j<=skip_start) { /* Give the bit we reserved to end skipping back. */ total += skip_rsv; break; } /*Figure out how many left-over bits we would be adding to this band. This can include bits we've stolen back from higher, skipped bands.*/ left = total-psum; percoeff = celt_udiv(left, m->eBands[codedBands]-m->eBands[start]); left -= (m->eBands[codedBands]-m->eBands[start])*percoeff; rem = IMAX(left-(m->eBands[j]-m->eBands[start]),0); band_width = m->eBands[codedBands]-m->eBands[j]; band_bits = (int)(bits[j] + percoeff*band_width + rem); /*Only code a skip decision if we're above the threshold for this band. Otherwise it is force-skipped. This ensures that we have enough bits to code the skip flag.*/ if (band_bits >= IMAX(thresh[j], alloc_floor+(1< ((j>4 && j<=signalBandwidth)) #endif { ec_enc_bit_logp(ec, 1, 1); break; } ec_enc_bit_logp(ec, 0, 1); } else if (ec_dec_bit_logp(ec, 1)) { break; } /*We used a bit to skip this band.*/ psum += 1< 0) intensity_rsv = LOG2_FRAC_TABLE[j-start]; psum += intensity_rsv; if (band_bits >= alloc_floor) { /*If we have enough for a fine energy bit per channel, use it.*/ psum += alloc_floor; bits[j] = alloc_floor; } else { /*Otherwise this band gets nothing at all.*/ bits[j] = 0; } } celt_assert(codedBands > start); /* Code the intensity and dual stereo parameters. */ if (intensity_rsv > 0) { if (encode) { *intensity = IMIN(*intensity, codedBands); ec_enc_uint(ec, *intensity-start, codedBands+1-start); } else *intensity = start+ec_dec_uint(ec, codedBands+1-start); } else *intensity = 0; if (*intensity <= start) { total += dual_stereo_rsv; dual_stereo_rsv = 0; } if (dual_stereo_rsv > 0) { if (encode) ec_enc_bit_logp(ec, *dual_stereo, 1); else *dual_stereo = ec_dec_bit_logp(ec, 1); } else *dual_stereo = 0; /* Allocate the remaining bits */ left = total-psum; percoeff = celt_udiv(left, m->eBands[codedBands]-m->eBands[start]); left -= (m->eBands[codedBands]-m->eBands[start])*percoeff; for (j=start;jeBands[j+1]-m->eBands[j])); for (j=start;jeBands[j+1]-m->eBands[j]); bits[j] += tmp; left -= tmp; } /*for (j=0;j= 0); N0 = m->eBands[j+1]-m->eBands[j]; N=N0<1) { excess = MAX32(bit-cap[j],0); bits[j] = bit-excess; /* Compensate for the extra DoF in stereo */ den=(C*N+ ((C==2 && N>2 && !*dual_stereo && j<*intensity) ? 1 : 0)); NClogN = den*(m->logN[j] + logM); /* Offset for the number of fine bits by log2(N)/2 + FINE_OFFSET compared to their "fair share" of total/N */ offset = (NClogN>>1)-den*FINE_OFFSET; /* N=2 is the only point that doesn't match the curve */ if (N==2) offset += den<>2; /* Changing the offset for allocating the second and third fine energy bit */ if (bits[j] + offset < den*2<>2; else if (bits[j] + offset < den*3<>3; /* Divide with rounding */ ebits[j] = IMAX(0, (bits[j] + offset + (den<<(BITRES-1)))); ebits[j] = celt_udiv(ebits[j], den)>>BITRES; /* Make sure not to bust */ if (C*ebits[j] > (bits[j]>>BITRES)) ebits[j] = bits[j] >> stereo >> BITRES; /* More than that is useless because that's about as far as PVQ can go */ ebits[j] = IMIN(ebits[j], MAX_FINE_BITS); /* If we rounded down or capped this band, make it a candidate for the final fine energy pass */ fine_priority[j] = ebits[j]*(den<= bits[j]+offset; /* Remove the allocated fine bits; the rest are assigned to PVQ */ bits[j] -= C*ebits[j]< 0) { int extra_fine; int extra_bits; extra_fine = IMIN(excess>>(stereo+BITRES),MAX_FINE_BITS-ebits[j]); ebits[j] += extra_fine; extra_bits = extra_fine*C<= excess-balance; excess -= extra_bits; } balance = excess; celt_assert(bits[j] >= 0); celt_assert(ebits[j] >= 0); } /* Save any remaining bits over the cap for the rebalancing in quant_all_bands(). */ *_balance = balance; /* The skipped bands use all their bits for fine energy. */ for (;j> stereo >> BITRES; celt_assert(C*ebits[j]<nbEBands; skip_start = start; /* Reserve a bit to signal the end of manually skipped bands. */ skip_rsv = total >= 1<total) intensity_rsv = 0; else { total -= intensity_rsv; dual_stereo_rsv = total>=1<eBands[j+1]-m->eBands[j])<>4); /* Tilt of the allocation curve */ trim_offset[j] = C*(m->eBands[j+1]-m->eBands[j])*(alloc_trim-5-LM)*(end-j-1) *(1<<(LM+BITRES))>>6; /* Giving less resolution to single-coefficient bands because they get more benefit from having one coarse value per coefficient*/ if ((m->eBands[j+1]-m->eBands[j])<nbAllocVectors - 1; do { int done = 0; int psum = 0; int mid = (lo+hi) >> 1; for (j=end;j-->start;) { int bitsj; int N = m->eBands[j+1]-m->eBands[j]; bitsj = C*N*m->allocVectors[mid*len+j]<>2; if (bitsj > 0) bitsj = IMAX(0, bitsj + trim_offset[j]); bitsj += offsets[j]; if (bitsj >= thresh[j] || done) { done = 1; /* Don't allocate more than we can actually use */ psum += IMIN(bitsj, cap[j]); } else { if (bitsj >= C< total) hi = mid - 1; else lo = mid + 1; /*printf ("lo = %d, hi = %d\n", lo, hi);*/ } while (lo <= hi); hi = lo--; /*printf ("interp between %d and %d\n", lo, hi);*/ for (j=start;jeBands[j+1]-m->eBands[j]; bits1j = C*N*m->allocVectors[lo*len+j]<>2; bits2j = hi>=m->nbAllocVectors ? cap[j] : C*N*m->allocVectors[hi*len+j]<>2; if (bits1j > 0) bits1j = IMAX(0, bits1j + trim_offset[j]); if (bits2j > 0) bits2j = IMAX(0, bits2j + trim_offset[j]); if (lo > 0) bits1j += offsets[j]; bits2j += offsets[j]; if (offsets[j]>0) skip_start = j; bits2j = IMAX(0,bits2j-bits1j); bits1[j] = bits1j; bits2[j] = bits2j; } codedBands = interp_bits2pulses(m, start, end, skip_start, bits1, bits2, thresh, cap, total, balance, skip_rsv, intensity, intensity_rsv, dual_stereo, dual_stereo_rsv, pulses, ebits, fine_priority, C, LM, ec, encode, prev, signalBandwidth); RESTORE_STACK; return codedBands; } ================================================ FILE: deps/pjsip/third_party/opus/celt/rate.h ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef RATE_H #define RATE_H #define MAX_PSEUDO 40 #define LOG_MAX_PSEUDO 6 #define CELT_MAX_PULSES 128 #define MAX_FINE_BITS 8 #define FINE_OFFSET 21 #define QTHETA_OFFSET 4 #define QTHETA_OFFSET_TWOPHASE 16 #include "cwrs.h" #include "modes.h" void compute_pulse_cache(CELTMode *m, int LM); static OPUS_INLINE int get_pulses(int i) { return i<8 ? i : (8 + (i&7)) << ((i>>3)-1); } static OPUS_INLINE int bits2pulses(const CELTMode *m, int band, int LM, int bits) { int i; int lo, hi; const unsigned char *cache; LM++; cache = m->cache.bits + m->cache.index[LM*m->nbEBands+band]; lo = 0; hi = cache[0]; bits--; for (i=0;i>1; /* OPT: Make sure this is implemented with a conditional move */ if ((int)cache[mid] >= bits) hi = mid; else lo = mid; } if (bits- (lo == 0 ? -1 : (int)cache[lo]) <= (int)cache[hi]-bits) return lo; else return hi; } static OPUS_INLINE int pulses2bits(const CELTMode *m, int band, int LM, int pulses) { const unsigned char *cache; LM++; cache = m->cache.bits + m->cache.index[LM*m->nbEBands+band]; return pulses == 0 ? 0 : cache[pulses]+1; } /** Compute the pulse allocation, i.e. how many pulses will go in each * band. @param m mode @param offsets Requested increase or decrease in the number of bits for each band @param total Number of bands @param pulses Number of pulses per band (returned) @return Total number of bits allocated */ int compute_allocation(const CELTMode *m, int start, int end, const int *offsets, const int *cap, int alloc_trim, int *intensity, int *dual_stero, opus_int32 total, opus_int32 *balance, int *pulses, int *ebits, int *fine_priority, int C, int LM, ec_ctx *ec, int encode, int prev, int signalBandwidth); #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/stack_alloc.h ================================================ /* Copyright (C) 2002-2003 Jean-Marc Valin Copyright (C) 2007-2009 Xiph.Org Foundation */ /** @file stack_alloc.h @brief Temporary memory allocation on stack */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef STACK_ALLOC_H #define STACK_ALLOC_H #include "opus_types.h" #include "opus_defines.h" #if (!defined (VAR_ARRAYS) && !defined (USE_ALLOCA) && !defined (NONTHREADSAFE_PSEUDOSTACK)) #error "Opus requires one of VAR_ARRAYS, USE_ALLOCA, or NONTHREADSAFE_PSEUDOSTACK be defined to select the temporary allocation mode." #endif #ifdef USE_ALLOCA # ifdef WIN32 # include # else # ifdef HAVE_ALLOCA_H # include # else # include # endif # endif #endif /** * @def ALIGN(stack, size) * * Aligns the stack to a 'size' boundary * * @param stack Stack * @param size New size boundary */ /** * @def PUSH(stack, size, type) * * Allocates 'size' elements of type 'type' on the stack * * @param stack Stack * @param size Number of elements * @param type Type of element */ /** * @def VARDECL(var) * * Declare variable on stack * * @param var Variable to declare */ /** * @def ALLOC(var, size, type) * * Allocate 'size' elements of 'type' on stack * * @param var Name of variable to allocate * @param size Number of elements * @param type Type of element */ #if defined(VAR_ARRAYS) #define VARDECL(type, var) #define ALLOC(var, size, type) type var[size] #define SAVE_STACK #define RESTORE_STACK #define ALLOC_STACK /* C99 does not allow VLAs of size zero */ #define ALLOC_NONE 1 #elif defined(USE_ALLOCA) #define VARDECL(type, var) type *var # ifdef WIN32 # define ALLOC(var, size, type) var = ((type*)_alloca(sizeof(type)*(size))) # else # define ALLOC(var, size, type) var = ((type*)alloca(sizeof(type)*(size))) # endif #define SAVE_STACK #define RESTORE_STACK #define ALLOC_STACK #define ALLOC_NONE 0 #else #ifdef CELT_C char *scratch_ptr=0; char *global_stack=0; #else extern char *global_stack; extern char *scratch_ptr; #endif /* CELT_C */ #ifdef ENABLE_VALGRIND #include #ifdef CELT_C char *global_stack_top=0; #else extern char *global_stack_top; #endif /* CELT_C */ #define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) #define PUSH(stack, size, type) (VALGRIND_MAKE_MEM_NOACCESS(stack, global_stack_top-stack),ALIGN((stack),sizeof(type)/sizeof(char)),VALGRIND_MAKE_MEM_UNDEFINED(stack, ((size)*sizeof(type)/sizeof(char))),(stack)+=(2*(size)*sizeof(type)/sizeof(char)),(type*)((stack)-(2*(size)*sizeof(type)/sizeof(char)))) #define RESTORE_STACK ((global_stack = _saved_stack),VALGRIND_MAKE_MEM_NOACCESS(global_stack, global_stack_top-global_stack)) #define ALLOC_STACK char *_saved_stack; ((global_stack = (global_stack==0) ? ((global_stack_top=opus_alloc_scratch(GLOBAL_STACK_SIZE*2)+(GLOBAL_STACK_SIZE*2))-(GLOBAL_STACK_SIZE*2)) : global_stack),VALGRIND_MAKE_MEM_NOACCESS(global_stack, global_stack_top-global_stack)); _saved_stack = global_stack; #else #define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) #define PUSH(stack, size, type) (ALIGN((stack),sizeof(type)/sizeof(char)),(stack)+=(size)*(sizeof(type)/sizeof(char)),(type*)((stack)-(size)*(sizeof(type)/sizeof(char)))) #if 0 /* Set this to 1 to instrument pseudostack usage */ #define RESTORE_STACK (printf("%ld %s:%d\n", global_stack-scratch_ptr, __FILE__, __LINE__),global_stack = _saved_stack) #else #define RESTORE_STACK (global_stack = _saved_stack) #endif #define ALLOC_STACK char *_saved_stack; (global_stack = (global_stack==0) ? (scratch_ptr=opus_alloc_scratch(GLOBAL_STACK_SIZE)) : global_stack); _saved_stack = global_stack; #endif /* ENABLE_VALGRIND */ #include "os_support.h" #define VARDECL(type, var) type *var #define ALLOC(var, size, type) var = PUSH(global_stack, size, type) #define SAVE_STACK char *_saved_stack = global_stack; #define ALLOC_NONE 0 #endif /* VAR_ARRAYS */ #ifdef ENABLE_VALGRIND #include #define OPUS_CHECK_ARRAY(ptr, len) VALGRIND_CHECK_MEM_IS_DEFINED(ptr, len*sizeof(*ptr)) #define OPUS_CHECK_VALUE(value) VALGRIND_CHECK_VALUE_IS_DEFINED(value) #define OPUS_CHECK_ARRAY_COND(ptr, len) VALGRIND_CHECK_MEM_IS_DEFINED(ptr, len*sizeof(*ptr)) #define OPUS_CHECK_VALUE_COND(value) VALGRIND_CHECK_VALUE_IS_DEFINED(value) #define OPUS_PRINT_INT(value) do {fprintf(stderr, #value " = %d at %s:%d\n", value, __FILE__, __LINE__);}while(0) #define OPUS_FPRINTF fprintf #else static OPUS_INLINE int _opus_false(void) {return 0;} #define OPUS_CHECK_ARRAY(ptr, len) _opus_false() #define OPUS_CHECK_VALUE(value) _opus_false() #define OPUS_PRINT_INT(value) do{}while(0) #define OPUS_FPRINTF (void) #endif #endif /* STACK_ALLOC_H */ ================================================ FILE: deps/pjsip/third_party/opus/celt/static_modes_fixed.h ================================================ /* The contents of this file was automatically generated by dump_modes.c with arguments: 48000 960 It contains static definitions for some pre-defined modes. */ #include "modes.h" #include "rate.h" #ifdef HAVE_ARM_NE10 #define OVERRIDE_FFT 1 #include "static_modes_fixed_arm_ne10.h" #endif #ifndef DEF_WINDOW120 #define DEF_WINDOW120 static const opus_val16 window120[120] = { 2, 20, 55, 108, 178, 266, 372, 494, 635, 792, 966, 1157, 1365, 1590, 1831, 2089, 2362, 2651, 2956, 3276, 3611, 3961, 4325, 4703, 5094, 5499, 5916, 6346, 6788, 7241, 7705, 8179, 8663, 9156, 9657, 10167, 10684, 11207, 11736, 12271, 12810, 13353, 13899, 14447, 14997, 15547, 16098, 16648, 17197, 17744, 18287, 18827, 19363, 19893, 20418, 20936, 21447, 21950, 22445, 22931, 23407, 23874, 24330, 24774, 25208, 25629, 26039, 26435, 26819, 27190, 27548, 27893, 28224, 28541, 28845, 29135, 29411, 29674, 29924, 30160, 30384, 30594, 30792, 30977, 31151, 31313, 31463, 31602, 31731, 31849, 31958, 32057, 32148, 32229, 32303, 32370, 32429, 32481, 32528, 32568, 32604, 32634, 32661, 32683, 32701, 32717, 32729, 32740, 32748, 32754, 32758, 32762, 32764, 32766, 32767, 32767, 32767, 32767, 32767, 32767, }; #endif #ifndef DEF_LOGN400 #define DEF_LOGN400 static const opus_int16 logN400[21] = { 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 16, 16, 16, 21, 21, 24, 29, 34, 36, }; #endif #ifndef DEF_PULSE_CACHE50 #define DEF_PULSE_CACHE50 static const opus_int16 cache_index50[105] = { -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 41, 41, 41, 82, 82, 123, 164, 200, 222, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 123, 123, 123, 164, 164, 240, 266, 283, 295, 41, 41, 41, 41, 41, 41, 41, 41, 123, 123, 123, 123, 240, 240, 240, 266, 266, 305, 318, 328, 336, 123, 123, 123, 123, 123, 123, 123, 123, 240, 240, 240, 240, 305, 305, 305, 318, 318, 343, 351, 358, 364, 240, 240, 240, 240, 240, 240, 240, 240, 305, 305, 305, 305, 343, 343, 343, 351, 351, 370, 376, 382, 387, }; static const unsigned char cache_bits50[392] = { 40, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 40, 15, 23, 28, 31, 34, 36, 38, 39, 41, 42, 43, 44, 45, 46, 47, 47, 49, 50, 51, 52, 53, 54, 55, 55, 57, 58, 59, 60, 61, 62, 63, 63, 65, 66, 67, 68, 69, 70, 71, 71, 40, 20, 33, 41, 48, 53, 57, 61, 64, 66, 69, 71, 73, 75, 76, 78, 80, 82, 85, 87, 89, 91, 92, 94, 96, 98, 101, 103, 105, 107, 108, 110, 112, 114, 117, 119, 121, 123, 124, 126, 128, 40, 23, 39, 51, 60, 67, 73, 79, 83, 87, 91, 94, 97, 100, 102, 105, 107, 111, 115, 118, 121, 124, 126, 129, 131, 135, 139, 142, 145, 148, 150, 153, 155, 159, 163, 166, 169, 172, 174, 177, 179, 35, 28, 49, 65, 78, 89, 99, 107, 114, 120, 126, 132, 136, 141, 145, 149, 153, 159, 165, 171, 176, 180, 185, 189, 192, 199, 205, 211, 216, 220, 225, 229, 232, 239, 245, 251, 21, 33, 58, 79, 97, 112, 125, 137, 148, 157, 166, 174, 182, 189, 195, 201, 207, 217, 227, 235, 243, 251, 17, 35, 63, 86, 106, 123, 139, 152, 165, 177, 187, 197, 206, 214, 222, 230, 237, 250, 25, 31, 55, 75, 91, 105, 117, 128, 138, 146, 154, 161, 168, 174, 180, 185, 190, 200, 208, 215, 222, 229, 235, 240, 245, 255, 16, 36, 65, 89, 110, 128, 144, 159, 173, 185, 196, 207, 217, 226, 234, 242, 250, 11, 41, 74, 103, 128, 151, 172, 191, 209, 225, 241, 255, 9, 43, 79, 110, 138, 163, 186, 207, 227, 246, 12, 39, 71, 99, 123, 144, 164, 182, 198, 214, 228, 241, 253, 9, 44, 81, 113, 142, 168, 192, 214, 235, 255, 7, 49, 90, 127, 160, 191, 220, 247, 6, 51, 95, 134, 170, 203, 234, 7, 47, 87, 123, 155, 184, 212, 237, 6, 52, 97, 137, 174, 208, 240, 5, 57, 106, 151, 192, 231, 5, 59, 111, 158, 202, 243, 5, 55, 103, 147, 187, 224, 5, 60, 113, 161, 206, 248, 4, 65, 122, 175, 224, 4, 67, 127, 182, 234, }; static const unsigned char cache_caps50[168] = { 224, 224, 224, 224, 224, 224, 224, 224, 160, 160, 160, 160, 185, 185, 185, 178, 178, 168, 134, 61, 37, 224, 224, 224, 224, 224, 224, 224, 224, 240, 240, 240, 240, 207, 207, 207, 198, 198, 183, 144, 66, 40, 160, 160, 160, 160, 160, 160, 160, 160, 185, 185, 185, 185, 193, 193, 193, 183, 183, 172, 138, 64, 38, 240, 240, 240, 240, 240, 240, 240, 240, 207, 207, 207, 207, 204, 204, 204, 193, 193, 180, 143, 66, 40, 185, 185, 185, 185, 185, 185, 185, 185, 193, 193, 193, 193, 193, 193, 193, 183, 183, 172, 138, 65, 39, 207, 207, 207, 207, 207, 207, 207, 207, 204, 204, 204, 204, 201, 201, 201, 188, 188, 176, 141, 66, 40, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 194, 194, 194, 184, 184, 173, 139, 65, 39, 204, 204, 204, 204, 204, 204, 204, 204, 201, 201, 201, 201, 198, 198, 198, 187, 187, 175, 140, 66, 40, }; #endif #ifndef FFT_TWIDDLES48000_960 #define FFT_TWIDDLES48000_960 static const kiss_twiddle_cpx fft_twiddles48000_960[480] = { {32767, 0}, {32766, -429}, {32757, -858}, {32743, -1287}, {32724, -1715}, {32698, -2143}, {32667, -2570}, {32631, -2998}, {32588, -3425}, {32541, -3851}, {32488, -4277}, {32429, -4701}, {32364, -5125}, {32295, -5548}, {32219, -5971}, {32138, -6393}, {32051, -6813}, {31960, -7231}, {31863, -7650}, {31760, -8067}, {31652, -8481}, {31539, -8895}, {31419, -9306}, {31294, -9716}, {31165, -10126}, {31030, -10532}, {30889, -10937}, {30743, -11340}, {30592, -11741}, {30436, -12141}, {30274, -12540}, {30107, -12935}, {29936, -13328}, {29758, -13718}, {29577, -14107}, {29390, -14493}, {29197, -14875}, {29000, -15257}, {28797, -15635}, {28590, -16010}, {28379, -16384}, {28162, -16753}, {27940, -17119}, {27714, -17484}, {27482, -17845}, {27246, -18205}, {27006, -18560}, {26760, -18911}, {26510, -19260}, {26257, -19606}, {25997, -19947}, {25734, -20286}, {25466, -20621}, {25194, -20952}, {24918, -21281}, {24637, -21605}, {24353, -21926}, {24063, -22242}, {23770, -22555}, {23473, -22865}, {23171, -23171}, {22866, -23472}, {22557, -23769}, {22244, -24063}, {21927, -24352}, {21606, -24636}, {21282, -24917}, {20954, -25194}, {20622, -25465}, {20288, -25733}, {19949, -25997}, {19607, -26255}, {19261, -26509}, {18914, -26760}, {18561, -27004}, {18205, -27246}, {17846, -27481}, {17485, -27713}, {17122, -27940}, {16755, -28162}, {16385, -28378}, {16012, -28590}, {15636, -28797}, {15258, -28999}, {14878, -29197}, {14494, -29389}, {14108, -29576}, {13720, -29757}, {13329, -29934}, {12937, -30107}, {12540, -30274}, {12142, -30435}, {11744, -30592}, {11342, -30743}, {10939, -30889}, {10534, -31030}, {10127, -31164}, {9718, -31294}, {9307, -31418}, {8895, -31537}, {8482, -31652}, {8067, -31759}, {7650, -31862}, {7233, -31960}, {6815, -32051}, {6393, -32138}, {5973, -32219}, {5549, -32294}, {5127, -32364}, {4703, -32429}, {4278, -32487}, {3852, -32541}, {3426, -32588}, {2999, -32630}, {2572, -32667}, {2144, -32698}, {1716, -32724}, {1287, -32742}, {860, -32757}, {430, -32766}, {0, -32767}, {-429, -32766}, {-858, -32757}, {-1287, -32743}, {-1715, -32724}, {-2143, -32698}, {-2570, -32667}, {-2998, -32631}, {-3425, -32588}, {-3851, -32541}, {-4277, -32488}, {-4701, -32429}, {-5125, -32364}, {-5548, -32295}, {-5971, -32219}, {-6393, -32138}, {-6813, -32051}, {-7231, -31960}, {-7650, -31863}, {-8067, -31760}, {-8481, -31652}, {-8895, -31539}, {-9306, -31419}, {-9716, -31294}, {-10126, -31165}, {-10532, -31030}, {-10937, -30889}, {-11340, -30743}, {-11741, -30592}, {-12141, -30436}, {-12540, -30274}, {-12935, -30107}, {-13328, -29936}, {-13718, -29758}, {-14107, -29577}, {-14493, -29390}, {-14875, -29197}, {-15257, -29000}, {-15635, -28797}, {-16010, -28590}, {-16384, -28379}, {-16753, -28162}, {-17119, -27940}, {-17484, -27714}, {-17845, -27482}, {-18205, -27246}, {-18560, -27006}, {-18911, -26760}, {-19260, -26510}, {-19606, -26257}, {-19947, -25997}, {-20286, -25734}, {-20621, -25466}, {-20952, -25194}, {-21281, -24918}, {-21605, -24637}, {-21926, -24353}, {-22242, -24063}, {-22555, -23770}, {-22865, -23473}, {-23171, -23171}, {-23472, -22866}, {-23769, -22557}, {-24063, -22244}, {-24352, -21927}, {-24636, -21606}, {-24917, -21282}, {-25194, -20954}, {-25465, -20622}, {-25733, -20288}, {-25997, -19949}, {-26255, -19607}, {-26509, -19261}, {-26760, -18914}, {-27004, -18561}, {-27246, -18205}, {-27481, -17846}, {-27713, -17485}, {-27940, -17122}, {-28162, -16755}, {-28378, -16385}, {-28590, -16012}, {-28797, -15636}, {-28999, -15258}, {-29197, -14878}, {-29389, -14494}, {-29576, -14108}, {-29757, -13720}, {-29934, -13329}, {-30107, -12937}, {-30274, -12540}, {-30435, -12142}, {-30592, -11744}, {-30743, -11342}, {-30889, -10939}, {-31030, -10534}, {-31164, -10127}, {-31294, -9718}, {-31418, -9307}, {-31537, -8895}, {-31652, -8482}, {-31759, -8067}, {-31862, -7650}, {-31960, -7233}, {-32051, -6815}, {-32138, -6393}, {-32219, -5973}, {-32294, -5549}, {-32364, -5127}, {-32429, -4703}, {-32487, -4278}, {-32541, -3852}, {-32588, -3426}, {-32630, -2999}, {-32667, -2572}, {-32698, -2144}, {-32724, -1716}, {-32742, -1287}, {-32757, -860}, {-32766, -430}, {-32767, 0}, {-32766, 429}, {-32757, 858}, {-32743, 1287}, {-32724, 1715}, {-32698, 2143}, {-32667, 2570}, {-32631, 2998}, {-32588, 3425}, {-32541, 3851}, {-32488, 4277}, {-32429, 4701}, {-32364, 5125}, {-32295, 5548}, {-32219, 5971}, {-32138, 6393}, {-32051, 6813}, {-31960, 7231}, {-31863, 7650}, {-31760, 8067}, {-31652, 8481}, {-31539, 8895}, {-31419, 9306}, {-31294, 9716}, {-31165, 10126}, {-31030, 10532}, {-30889, 10937}, {-30743, 11340}, {-30592, 11741}, {-30436, 12141}, {-30274, 12540}, {-30107, 12935}, {-29936, 13328}, {-29758, 13718}, {-29577, 14107}, {-29390, 14493}, {-29197, 14875}, {-29000, 15257}, {-28797, 15635}, {-28590, 16010}, {-28379, 16384}, {-28162, 16753}, {-27940, 17119}, {-27714, 17484}, {-27482, 17845}, {-27246, 18205}, {-27006, 18560}, {-26760, 18911}, {-26510, 19260}, {-26257, 19606}, {-25997, 19947}, {-25734, 20286}, {-25466, 20621}, {-25194, 20952}, {-24918, 21281}, {-24637, 21605}, {-24353, 21926}, {-24063, 22242}, {-23770, 22555}, {-23473, 22865}, {-23171, 23171}, {-22866, 23472}, {-22557, 23769}, {-22244, 24063}, {-21927, 24352}, {-21606, 24636}, {-21282, 24917}, {-20954, 25194}, {-20622, 25465}, {-20288, 25733}, {-19949, 25997}, {-19607, 26255}, {-19261, 26509}, {-18914, 26760}, {-18561, 27004}, {-18205, 27246}, {-17846, 27481}, {-17485, 27713}, {-17122, 27940}, {-16755, 28162}, {-16385, 28378}, {-16012, 28590}, {-15636, 28797}, {-15258, 28999}, {-14878, 29197}, {-14494, 29389}, {-14108, 29576}, {-13720, 29757}, {-13329, 29934}, {-12937, 30107}, {-12540, 30274}, {-12142, 30435}, {-11744, 30592}, {-11342, 30743}, {-10939, 30889}, {-10534, 31030}, {-10127, 31164}, {-9718, 31294}, {-9307, 31418}, {-8895, 31537}, {-8482, 31652}, {-8067, 31759}, {-7650, 31862}, {-7233, 31960}, {-6815, 32051}, {-6393, 32138}, {-5973, 32219}, {-5549, 32294}, {-5127, 32364}, {-4703, 32429}, {-4278, 32487}, {-3852, 32541}, {-3426, 32588}, {-2999, 32630}, {-2572, 32667}, {-2144, 32698}, {-1716, 32724}, {-1287, 32742}, {-860, 32757}, {-430, 32766}, {0, 32767}, {429, 32766}, {858, 32757}, {1287, 32743}, {1715, 32724}, {2143, 32698}, {2570, 32667}, {2998, 32631}, {3425, 32588}, {3851, 32541}, {4277, 32488}, {4701, 32429}, {5125, 32364}, {5548, 32295}, {5971, 32219}, {6393, 32138}, {6813, 32051}, {7231, 31960}, {7650, 31863}, {8067, 31760}, {8481, 31652}, {8895, 31539}, {9306, 31419}, {9716, 31294}, {10126, 31165}, {10532, 31030}, {10937, 30889}, {11340, 30743}, {11741, 30592}, {12141, 30436}, {12540, 30274}, {12935, 30107}, {13328, 29936}, {13718, 29758}, {14107, 29577}, {14493, 29390}, {14875, 29197}, {15257, 29000}, {15635, 28797}, {16010, 28590}, {16384, 28379}, {16753, 28162}, {17119, 27940}, {17484, 27714}, {17845, 27482}, {18205, 27246}, {18560, 27006}, {18911, 26760}, {19260, 26510}, {19606, 26257}, {19947, 25997}, {20286, 25734}, {20621, 25466}, {20952, 25194}, {21281, 24918}, {21605, 24637}, {21926, 24353}, {22242, 24063}, {22555, 23770}, {22865, 23473}, {23171, 23171}, {23472, 22866}, {23769, 22557}, {24063, 22244}, {24352, 21927}, {24636, 21606}, {24917, 21282}, {25194, 20954}, {25465, 20622}, {25733, 20288}, {25997, 19949}, {26255, 19607}, {26509, 19261}, {26760, 18914}, {27004, 18561}, {27246, 18205}, {27481, 17846}, {27713, 17485}, {27940, 17122}, {28162, 16755}, {28378, 16385}, {28590, 16012}, {28797, 15636}, {28999, 15258}, {29197, 14878}, {29389, 14494}, {29576, 14108}, {29757, 13720}, {29934, 13329}, {30107, 12937}, {30274, 12540}, {30435, 12142}, {30592, 11744}, {30743, 11342}, {30889, 10939}, {31030, 10534}, {31164, 10127}, {31294, 9718}, {31418, 9307}, {31537, 8895}, {31652, 8482}, {31759, 8067}, {31862, 7650}, {31960, 7233}, {32051, 6815}, {32138, 6393}, {32219, 5973}, {32294, 5549}, {32364, 5127}, {32429, 4703}, {32487, 4278}, {32541, 3852}, {32588, 3426}, {32630, 2999}, {32667, 2572}, {32698, 2144}, {32724, 1716}, {32742, 1287}, {32757, 860}, {32766, 430}, }; #ifndef FFT_BITREV480 #define FFT_BITREV480 static const opus_int16 fft_bitrev480[480] = { 0, 96, 192, 288, 384, 32, 128, 224, 320, 416, 64, 160, 256, 352, 448, 8, 104, 200, 296, 392, 40, 136, 232, 328, 424, 72, 168, 264, 360, 456, 16, 112, 208, 304, 400, 48, 144, 240, 336, 432, 80, 176, 272, 368, 464, 24, 120, 216, 312, 408, 56, 152, 248, 344, 440, 88, 184, 280, 376, 472, 4, 100, 196, 292, 388, 36, 132, 228, 324, 420, 68, 164, 260, 356, 452, 12, 108, 204, 300, 396, 44, 140, 236, 332, 428, 76, 172, 268, 364, 460, 20, 116, 212, 308, 404, 52, 148, 244, 340, 436, 84, 180, 276, 372, 468, 28, 124, 220, 316, 412, 60, 156, 252, 348, 444, 92, 188, 284, 380, 476, 1, 97, 193, 289, 385, 33, 129, 225, 321, 417, 65, 161, 257, 353, 449, 9, 105, 201, 297, 393, 41, 137, 233, 329, 425, 73, 169, 265, 361, 457, 17, 113, 209, 305, 401, 49, 145, 241, 337, 433, 81, 177, 273, 369, 465, 25, 121, 217, 313, 409, 57, 153, 249, 345, 441, 89, 185, 281, 377, 473, 5, 101, 197, 293, 389, 37, 133, 229, 325, 421, 69, 165, 261, 357, 453, 13, 109, 205, 301, 397, 45, 141, 237, 333, 429, 77, 173, 269, 365, 461, 21, 117, 213, 309, 405, 53, 149, 245, 341, 437, 85, 181, 277, 373, 469, 29, 125, 221, 317, 413, 61, 157, 253, 349, 445, 93, 189, 285, 381, 477, 2, 98, 194, 290, 386, 34, 130, 226, 322, 418, 66, 162, 258, 354, 450, 10, 106, 202, 298, 394, 42, 138, 234, 330, 426, 74, 170, 266, 362, 458, 18, 114, 210, 306, 402, 50, 146, 242, 338, 434, 82, 178, 274, 370, 466, 26, 122, 218, 314, 410, 58, 154, 250, 346, 442, 90, 186, 282, 378, 474, 6, 102, 198, 294, 390, 38, 134, 230, 326, 422, 70, 166, 262, 358, 454, 14, 110, 206, 302, 398, 46, 142, 238, 334, 430, 78, 174, 270, 366, 462, 22, 118, 214, 310, 406, 54, 150, 246, 342, 438, 86, 182, 278, 374, 470, 30, 126, 222, 318, 414, 62, 158, 254, 350, 446, 94, 190, 286, 382, 478, 3, 99, 195, 291, 387, 35, 131, 227, 323, 419, 67, 163, 259, 355, 451, 11, 107, 203, 299, 395, 43, 139, 235, 331, 427, 75, 171, 267, 363, 459, 19, 115, 211, 307, 403, 51, 147, 243, 339, 435, 83, 179, 275, 371, 467, 27, 123, 219, 315, 411, 59, 155, 251, 347, 443, 91, 187, 283, 379, 475, 7, 103, 199, 295, 391, 39, 135, 231, 327, 423, 71, 167, 263, 359, 455, 15, 111, 207, 303, 399, 47, 143, 239, 335, 431, 79, 175, 271, 367, 463, 23, 119, 215, 311, 407, 55, 151, 247, 343, 439, 87, 183, 279, 375, 471, 31, 127, 223, 319, 415, 63, 159, 255, 351, 447, 95, 191, 287, 383, 479, }; #endif #ifndef FFT_BITREV240 #define FFT_BITREV240 static const opus_int16 fft_bitrev240[240] = { 0, 48, 96, 144, 192, 16, 64, 112, 160, 208, 32, 80, 128, 176, 224, 4, 52, 100, 148, 196, 20, 68, 116, 164, 212, 36, 84, 132, 180, 228, 8, 56, 104, 152, 200, 24, 72, 120, 168, 216, 40, 88, 136, 184, 232, 12, 60, 108, 156, 204, 28, 76, 124, 172, 220, 44, 92, 140, 188, 236, 1, 49, 97, 145, 193, 17, 65, 113, 161, 209, 33, 81, 129, 177, 225, 5, 53, 101, 149, 197, 21, 69, 117, 165, 213, 37, 85, 133, 181, 229, 9, 57, 105, 153, 201, 25, 73, 121, 169, 217, 41, 89, 137, 185, 233, 13, 61, 109, 157, 205, 29, 77, 125, 173, 221, 45, 93, 141, 189, 237, 2, 50, 98, 146, 194, 18, 66, 114, 162, 210, 34, 82, 130, 178, 226, 6, 54, 102, 150, 198, 22, 70, 118, 166, 214, 38, 86, 134, 182, 230, 10, 58, 106, 154, 202, 26, 74, 122, 170, 218, 42, 90, 138, 186, 234, 14, 62, 110, 158, 206, 30, 78, 126, 174, 222, 46, 94, 142, 190, 238, 3, 51, 99, 147, 195, 19, 67, 115, 163, 211, 35, 83, 131, 179, 227, 7, 55, 103, 151, 199, 23, 71, 119, 167, 215, 39, 87, 135, 183, 231, 11, 59, 107, 155, 203, 27, 75, 123, 171, 219, 43, 91, 139, 187, 235, 15, 63, 111, 159, 207, 31, 79, 127, 175, 223, 47, 95, 143, 191, 239, }; #endif #ifndef FFT_BITREV120 #define FFT_BITREV120 static const opus_int16 fft_bitrev120[120] = { 0, 24, 48, 72, 96, 8, 32, 56, 80, 104, 16, 40, 64, 88, 112, 4, 28, 52, 76, 100, 12, 36, 60, 84, 108, 20, 44, 68, 92, 116, 1, 25, 49, 73, 97, 9, 33, 57, 81, 105, 17, 41, 65, 89, 113, 5, 29, 53, 77, 101, 13, 37, 61, 85, 109, 21, 45, 69, 93, 117, 2, 26, 50, 74, 98, 10, 34, 58, 82, 106, 18, 42, 66, 90, 114, 6, 30, 54, 78, 102, 14, 38, 62, 86, 110, 22, 46, 70, 94, 118, 3, 27, 51, 75, 99, 11, 35, 59, 83, 107, 19, 43, 67, 91, 115, 7, 31, 55, 79, 103, 15, 39, 63, 87, 111, 23, 47, 71, 95, 119, }; #endif #ifndef FFT_BITREV60 #define FFT_BITREV60 static const opus_int16 fft_bitrev60[60] = { 0, 12, 24, 36, 48, 4, 16, 28, 40, 52, 8, 20, 32, 44, 56, 1, 13, 25, 37, 49, 5, 17, 29, 41, 53, 9, 21, 33, 45, 57, 2, 14, 26, 38, 50, 6, 18, 30, 42, 54, 10, 22, 34, 46, 58, 3, 15, 27, 39, 51, 7, 19, 31, 43, 55, 11, 23, 35, 47, 59, }; #endif #ifndef FFT_STATE48000_960_0 #define FFT_STATE48000_960_0 static const kiss_fft_state fft_state48000_960_0 = { 480, /* nfft */ 17476, /* scale */ 8, /* scale_shift */ -1, /* shift */ {5, 96, 3, 32, 4, 8, 2, 4, 4, 1, 0, 0, 0, 0, 0, 0, }, /* factors */ fft_bitrev480, /* bitrev */ fft_twiddles48000_960, /* bitrev */ #ifdef OVERRIDE_FFT (arch_fft_state *)&cfg_arch_480, #else NULL, #endif }; #endif #ifndef FFT_STATE48000_960_1 #define FFT_STATE48000_960_1 static const kiss_fft_state fft_state48000_960_1 = { 240, /* nfft */ 17476, /* scale */ 7, /* scale_shift */ 1, /* shift */ {5, 48, 3, 16, 4, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, }, /* factors */ fft_bitrev240, /* bitrev */ fft_twiddles48000_960, /* bitrev */ #ifdef OVERRIDE_FFT (arch_fft_state *)&cfg_arch_240, #else NULL, #endif }; #endif #ifndef FFT_STATE48000_960_2 #define FFT_STATE48000_960_2 static const kiss_fft_state fft_state48000_960_2 = { 120, /* nfft */ 17476, /* scale */ 6, /* scale_shift */ 2, /* shift */ {5, 24, 3, 8, 2, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, }, /* factors */ fft_bitrev120, /* bitrev */ fft_twiddles48000_960, /* bitrev */ #ifdef OVERRIDE_FFT (arch_fft_state *)&cfg_arch_120, #else NULL, #endif }; #endif #ifndef FFT_STATE48000_960_3 #define FFT_STATE48000_960_3 static const kiss_fft_state fft_state48000_960_3 = { 60, /* nfft */ 17476, /* scale */ 5, /* scale_shift */ 3, /* shift */ {5, 12, 3, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, /* factors */ fft_bitrev60, /* bitrev */ fft_twiddles48000_960, /* bitrev */ #ifdef OVERRIDE_FFT (arch_fft_state *)&cfg_arch_60, #else NULL, #endif }; #endif #endif #ifndef MDCT_TWIDDLES960 #define MDCT_TWIDDLES960 static const opus_val16 mdct_twiddles960[1800] = { 32767, 32767, 32767, 32766, 32765, 32763, 32761, 32759, 32756, 32753, 32750, 32746, 32742, 32738, 32733, 32728, 32722, 32717, 32710, 32704, 32697, 32690, 32682, 32674, 32666, 32657, 32648, 32639, 32629, 32619, 32609, 32598, 32587, 32576, 32564, 32552, 32539, 32526, 32513, 32500, 32486, 32472, 32457, 32442, 32427, 32411, 32395, 32379, 32362, 32345, 32328, 32310, 32292, 32274, 32255, 32236, 32217, 32197, 32177, 32157, 32136, 32115, 32093, 32071, 32049, 32027, 32004, 31981, 31957, 31933, 31909, 31884, 31859, 31834, 31809, 31783, 31756, 31730, 31703, 31676, 31648, 31620, 31592, 31563, 31534, 31505, 31475, 31445, 31415, 31384, 31353, 31322, 31290, 31258, 31226, 31193, 31160, 31127, 31093, 31059, 31025, 30990, 30955, 30920, 30884, 30848, 30812, 30775, 30738, 30701, 30663, 30625, 30587, 30548, 30509, 30470, 30430, 30390, 30350, 30309, 30269, 30227, 30186, 30144, 30102, 30059, 30016, 29973, 29930, 29886, 29842, 29797, 29752, 29707, 29662, 29616, 29570, 29524, 29477, 29430, 29383, 29335, 29287, 29239, 29190, 29142, 29092, 29043, 28993, 28943, 28892, 28842, 28791, 28739, 28688, 28636, 28583, 28531, 28478, 28425, 28371, 28317, 28263, 28209, 28154, 28099, 28044, 27988, 27932, 27876, 27820, 27763, 27706, 27648, 27591, 27533, 27474, 27416, 27357, 27298, 27238, 27178, 27118, 27058, 26997, 26936, 26875, 26814, 26752, 26690, 26628, 26565, 26502, 26439, 26375, 26312, 26247, 26183, 26119, 26054, 25988, 25923, 25857, 25791, 25725, 25658, 25592, 25524, 25457, 25389, 25322, 25253, 25185, 25116, 25047, 24978, 24908, 24838, 24768, 24698, 24627, 24557, 24485, 24414, 24342, 24270, 24198, 24126, 24053, 23980, 23907, 23834, 23760, 23686, 23612, 23537, 23462, 23387, 23312, 23237, 23161, 23085, 23009, 22932, 22856, 22779, 22701, 22624, 22546, 22468, 22390, 22312, 22233, 22154, 22075, 21996, 21916, 21836, 21756, 21676, 21595, 21515, 21434, 21352, 21271, 21189, 21107, 21025, 20943, 20860, 20777, 20694, 20611, 20528, 20444, 20360, 20276, 20192, 20107, 20022, 19937, 19852, 19767, 19681, 19595, 19509, 19423, 19336, 19250, 19163, 19076, 18988, 18901, 18813, 18725, 18637, 18549, 18460, 18372, 18283, 18194, 18104, 18015, 17925, 17835, 17745, 17655, 17565, 17474, 17383, 17292, 17201, 17110, 17018, 16927, 16835, 16743, 16650, 16558, 16465, 16372, 16279, 16186, 16093, 15999, 15906, 15812, 15718, 15624, 15529, 15435, 15340, 15245, 15150, 15055, 14960, 14864, 14769, 14673, 14577, 14481, 14385, 14288, 14192, 14095, 13998, 13901, 13804, 13706, 13609, 13511, 13414, 13316, 13218, 13119, 13021, 12923, 12824, 12725, 12626, 12527, 12428, 12329, 12230, 12130, 12030, 11930, 11831, 11730, 11630, 11530, 11430, 11329, 11228, 11128, 11027, 10926, 10824, 10723, 10622, 10520, 10419, 10317, 10215, 10113, 10011, 9909, 9807, 9704, 9602, 9499, 9397, 9294, 9191, 9088, 8985, 8882, 8778, 8675, 8572, 8468, 8364, 8261, 8157, 8053, 7949, 7845, 7741, 7637, 7532, 7428, 7323, 7219, 7114, 7009, 6905, 6800, 6695, 6590, 6485, 6380, 6274, 6169, 6064, 5958, 5853, 5747, 5642, 5536, 5430, 5325, 5219, 5113, 5007, 4901, 4795, 4689, 4583, 4476, 4370, 4264, 4157, 4051, 3945, 3838, 3732, 3625, 3518, 3412, 3305, 3198, 3092, 2985, 2878, 2771, 2664, 2558, 2451, 2344, 2237, 2130, 2023, 1916, 1809, 1702, 1594, 1487, 1380, 1273, 1166, 1059, 952, 844, 737, 630, 523, 416, 308, 201, 94, -13, -121, -228, -335, -442, -550, -657, -764, -871, -978, -1086, -1193, -1300, -1407, -1514, -1621, -1728, -1835, -1942, -2049, -2157, -2263, -2370, -2477, -2584, -2691, -2798, -2905, -3012, -3118, -3225, -3332, -3439, -3545, -3652, -3758, -3865, -3971, -4078, -4184, -4290, -4397, -4503, -4609, -4715, -4821, -4927, -5033, -5139, -5245, -5351, -5457, -5562, -5668, -5774, -5879, -5985, -6090, -6195, -6301, -6406, -6511, -6616, -6721, -6826, -6931, -7036, -7140, -7245, -7349, -7454, -7558, -7663, -7767, -7871, -7975, -8079, -8183, -8287, -8390, -8494, -8597, -8701, -8804, -8907, -9011, -9114, -9217, -9319, -9422, -9525, -9627, -9730, -9832, -9934, -10037, -10139, -10241, -10342, -10444, -10546, -10647, -10748, -10850, -10951, -11052, -11153, -11253, -11354, -11455, -11555, -11655, -11756, -11856, -11955, -12055, -12155, -12254, -12354, -12453, -12552, -12651, -12750, -12849, -12947, -13046, -13144, -13242, -13340, -13438, -13536, -13633, -13731, -13828, -13925, -14022, -14119, -14216, -14312, -14409, -14505, -14601, -14697, -14793, -14888, -14984, -15079, -15174, -15269, -15364, -15459, -15553, -15647, -15741, -15835, -15929, -16023, -16116, -16210, -16303, -16396, -16488, -16581, -16673, -16766, -16858, -16949, -17041, -17133, -17224, -17315, -17406, -17497, -17587, -17678, -17768, -17858, -17948, -18037, -18127, -18216, -18305, -18394, -18483, -18571, -18659, -18747, -18835, -18923, -19010, -19098, -19185, -19271, -19358, -19444, -19531, -19617, -19702, -19788, -19873, -19959, -20043, -20128, -20213, -20297, -20381, -20465, -20549, -20632, -20715, -20798, -20881, -20963, -21046, -21128, -21210, -21291, -21373, -21454, -21535, -21616, -21696, -21776, -21856, -21936, -22016, -22095, -22174, -22253, -22331, -22410, -22488, -22566, -22643, -22721, -22798, -22875, -22951, -23028, -23104, -23180, -23256, -23331, -23406, -23481, -23556, -23630, -23704, -23778, -23852, -23925, -23998, -24071, -24144, -24216, -24288, -24360, -24432, -24503, -24574, -24645, -24716, -24786, -24856, -24926, -24995, -25064, -25133, -25202, -25270, -25339, -25406, -25474, -25541, -25608, -25675, -25742, -25808, -25874, -25939, -26005, -26070, -26135, -26199, -26264, -26327, -26391, -26455, -26518, -26581, -26643, -26705, -26767, -26829, -26891, -26952, -27013, -27073, -27133, -27193, -27253, -27312, -27372, -27430, -27489, -27547, -27605, -27663, -27720, -27777, -27834, -27890, -27946, -28002, -28058, -28113, -28168, -28223, -28277, -28331, -28385, -28438, -28491, -28544, -28596, -28649, -28701, -28752, -28803, -28854, -28905, -28955, -29006, -29055, -29105, -29154, -29203, -29251, -29299, -29347, -29395, -29442, -29489, -29535, -29582, -29628, -29673, -29719, -29764, -29808, -29853, -29897, -29941, -29984, -30027, -30070, -30112, -30154, -30196, -30238, -30279, -30320, -30360, -30400, -30440, -30480, -30519, -30558, -30596, -30635, -30672, -30710, -30747, -30784, -30821, -30857, -30893, -30929, -30964, -30999, -31033, -31068, -31102, -31135, -31168, -31201, -31234, -31266, -31298, -31330, -31361, -31392, -31422, -31453, -31483, -31512, -31541, -31570, -31599, -31627, -31655, -31682, -31710, -31737, -31763, -31789, -31815, -31841, -31866, -31891, -31915, -31939, -31963, -31986, -32010, -32032, -32055, -32077, -32099, -32120, -32141, -32162, -32182, -32202, -32222, -32241, -32260, -32279, -32297, -32315, -32333, -32350, -32367, -32383, -32399, -32415, -32431, -32446, -32461, -32475, -32489, -32503, -32517, -32530, -32542, -32555, -32567, -32579, -32590, -32601, -32612, -32622, -32632, -32641, -32651, -32659, -32668, -32676, -32684, -32692, -32699, -32706, -32712, -32718, -32724, -32729, -32734, -32739, -32743, -32747, -32751, -32754, -32757, -32760, -32762, -32764, -32765, -32767, -32767, -32767, 32767, 32767, 32765, 32761, 32756, 32750, 32742, 32732, 32722, 32710, 32696, 32681, 32665, 32647, 32628, 32608, 32586, 32562, 32538, 32512, 32484, 32455, 32425, 32393, 32360, 32326, 32290, 32253, 32214, 32174, 32133, 32090, 32046, 32001, 31954, 31906, 31856, 31805, 31753, 31700, 31645, 31588, 31530, 31471, 31411, 31349, 31286, 31222, 31156, 31089, 31020, 30951, 30880, 30807, 30733, 30658, 30582, 30504, 30425, 30345, 30263, 30181, 30096, 30011, 29924, 29836, 29747, 29656, 29564, 29471, 29377, 29281, 29184, 29086, 28987, 28886, 28784, 28681, 28577, 28471, 28365, 28257, 28147, 28037, 27925, 27812, 27698, 27583, 27467, 27349, 27231, 27111, 26990, 26868, 26744, 26620, 26494, 26367, 26239, 26110, 25980, 25849, 25717, 25583, 25449, 25313, 25176, 25038, 24900, 24760, 24619, 24477, 24333, 24189, 24044, 23898, 23751, 23602, 23453, 23303, 23152, 22999, 22846, 22692, 22537, 22380, 22223, 22065, 21906, 21746, 21585, 21423, 21261, 21097, 20933, 20767, 20601, 20434, 20265, 20096, 19927, 19756, 19584, 19412, 19239, 19065, 18890, 18714, 18538, 18361, 18183, 18004, 17824, 17644, 17463, 17281, 17098, 16915, 16731, 16546, 16361, 16175, 15988, 15800, 15612, 15423, 15234, 15043, 14852, 14661, 14469, 14276, 14083, 13889, 13694, 13499, 13303, 13107, 12910, 12713, 12515, 12317, 12118, 11918, 11718, 11517, 11316, 11115, 10913, 10710, 10508, 10304, 10100, 9896, 9691, 9486, 9281, 9075, 8869, 8662, 8455, 8248, 8040, 7832, 7623, 7415, 7206, 6996, 6787, 6577, 6366, 6156, 5945, 5734, 5523, 5311, 5100, 4888, 4675, 4463, 4251, 4038, 3825, 3612, 3399, 3185, 2972, 2758, 2544, 2330, 2116, 1902, 1688, 1474, 1260, 1045, 831, 617, 402, 188, -27, -241, -456, -670, -885, -1099, -1313, -1528, -1742, -1956, -2170, -2384, -2598, -2811, -3025, -3239, -3452, -3665, -3878, -4091, -4304, -4516, -4728, -4941, -5153, -5364, -5576, -5787, -5998, -6209, -6419, -6629, -6839, -7049, -7258, -7467, -7676, -7884, -8092, -8300, -8507, -8714, -8920, -9127, -9332, -9538, -9743, -9947, -10151, -10355, -10558, -10761, -10963, -11165, -11367, -11568, -11768, -11968, -12167, -12366, -12565, -12762, -12960, -13156, -13352, -13548, -13743, -13937, -14131, -14324, -14517, -14709, -14900, -15091, -15281, -15470, -15659, -15847, -16035, -16221, -16407, -16593, -16777, -16961, -17144, -17326, -17508, -17689, -17869, -18049, -18227, -18405, -18582, -18758, -18934, -19108, -19282, -19455, -19627, -19799, -19969, -20139, -20308, -20475, -20642, -20809, -20974, -21138, -21301, -21464, -21626, -21786, -21946, -22105, -22263, -22420, -22575, -22730, -22884, -23037, -23189, -23340, -23490, -23640, -23788, -23935, -24080, -24225, -24369, -24512, -24654, -24795, -24934, -25073, -25211, -25347, -25482, -25617, -25750, -25882, -26013, -26143, -26272, -26399, -26526, -26651, -26775, -26898, -27020, -27141, -27260, -27379, -27496, -27612, -27727, -27841, -27953, -28065, -28175, -28284, -28391, -28498, -28603, -28707, -28810, -28911, -29012, -29111, -29209, -29305, -29401, -29495, -29587, -29679, -29769, -29858, -29946, -30032, -30118, -30201, -30284, -30365, -30445, -30524, -30601, -30677, -30752, -30825, -30897, -30968, -31038, -31106, -31172, -31238, -31302, -31365, -31426, -31486, -31545, -31602, -31658, -31713, -31766, -31818, -31869, -31918, -31966, -32012, -32058, -32101, -32144, -32185, -32224, -32262, -32299, -32335, -32369, -32401, -32433, -32463, -32491, -32518, -32544, -32568, -32591, -32613, -32633, -32652, -32669, -32685, -32700, -32713, -32724, -32735, -32744, -32751, -32757, -32762, -32766, -32767, 32767, 32764, 32755, 32741, 32720, 32694, 32663, 32626, 32583, 32535, 32481, 32421, 32356, 32286, 32209, 32128, 32041, 31948, 31850, 31747, 31638, 31523, 31403, 31278, 31148, 31012, 30871, 30724, 30572, 30415, 30253, 30086, 29913, 29736, 29553, 29365, 29172, 28974, 28771, 28564, 28351, 28134, 27911, 27684, 27452, 27216, 26975, 26729, 26478, 26223, 25964, 25700, 25432, 25159, 24882, 24601, 24315, 24026, 23732, 23434, 23133, 22827, 22517, 22204, 21886, 21565, 21240, 20912, 20580, 20244, 19905, 19563, 19217, 18868, 18516, 18160, 17802, 17440, 17075, 16708, 16338, 15964, 15588, 15210, 14829, 14445, 14059, 13670, 13279, 12886, 12490, 12093, 11693, 11291, 10888, 10482, 10075, 9666, 9255, 8843, 8429, 8014, 7597, 7180, 6760, 6340, 5919, 5496, 5073, 4649, 4224, 3798, 3372, 2945, 2517, 2090, 1661, 1233, 804, 375, -54, -483, -911, -1340, -1768, -2197, -2624, -3052, -3479, -3905, -4330, -4755, -5179, -5602, -6024, -6445, -6865, -7284, -7702, -8118, -8533, -8946, -9358, -9768, -10177, -10584, -10989, -11392, -11793, -12192, -12589, -12984, -13377, -13767, -14155, -14541, -14924, -15305, -15683, -16058, -16430, -16800, -17167, -17531, -17892, -18249, -18604, -18956, -19304, -19649, -19990, -20329, -20663, -20994, -21322, -21646, -21966, -22282, -22595, -22904, -23208, -23509, -23806, -24099, -24387, -24672, -24952, -25228, -25499, -25766, -26029, -26288, -26541, -26791, -27035, -27275, -27511, -27741, -27967, -28188, -28405, -28616, -28823, -29024, -29221, -29412, -29599, -29780, -29957, -30128, -30294, -30455, -30611, -30761, -30906, -31046, -31181, -31310, -31434, -31552, -31665, -31773, -31875, -31972, -32063, -32149, -32229, -32304, -32373, -32437, -32495, -32547, -32594, -32635, -32671, -32701, -32726, -32745, -32758, -32766, 32767, 32754, 32717, 32658, 32577, 32473, 32348, 32200, 32029, 31837, 31624, 31388, 31131, 30853, 30553, 30232, 29891, 29530, 29148, 28746, 28324, 27883, 27423, 26944, 26447, 25931, 25398, 24847, 24279, 23695, 23095, 22478, 21846, 21199, 20538, 19863, 19174, 18472, 17757, 17030, 16291, 15541, 14781, 14010, 13230, 12441, 11643, 10837, 10024, 9204, 8377, 7545, 6708, 5866, 5020, 4171, 3319, 2464, 1608, 751, -107, -965, -1822, -2678, -3532, -4383, -5232, -6077, -6918, -7754, -8585, -9409, -10228, -11039, -11843, -12639, -13426, -14204, -14972, -15730, -16477, -17213, -17937, -18648, -19347, -20033, -20705, -21363, -22006, -22634, -23246, -23843, -24423, -24986, -25533, -26062, -26573, -27066, -27540, -27995, -28431, -28848, -29245, -29622, -29979, -30315, -30630, -30924, -31197, -31449, -31679, -31887, -32074, -32239, -32381, -32501, -32600, -32675, -32729, -32759, }; #endif static const CELTMode mode48000_960_120 = { 48000, /* Fs */ 120, /* overlap */ 21, /* nbEBands */ 21, /* effEBands */ {27853, 0, 4096, 8192, }, /* preemph */ eband5ms, /* eBands */ 3, /* maxLM */ 8, /* nbShortMdcts */ 120, /* shortMdctSize */ 11, /* nbAllocVectors */ band_allocation, /* allocVectors */ logN400, /* logN */ window120, /* window */ {1920, 3, {&fft_state48000_960_0, &fft_state48000_960_1, &fft_state48000_960_2, &fft_state48000_960_3, }, mdct_twiddles960}, /* mdct */ {392, cache_index50, cache_bits50, cache_caps50}, /* cache */ }; /* List of all the available modes */ #define TOTAL_MODES 1 static const CELTMode * const static_mode_list[TOTAL_MODES] = { &mode48000_960_120, }; ================================================ FILE: deps/pjsip/third_party/opus/celt/static_modes_fixed_arm_ne10.h ================================================ /* The contents of this file was automatically generated by * dump_mode_arm_ne10.c with arguments: 48000 960 * It contains static definitions for some pre-defined modes. */ #include #ifndef NE10_FFT_PARAMS48000_960 #define NE10_FFT_PARAMS48000_960 static const ne10_int32_t ne10_factors_480[64] = { 4, 40, 4, 30, 2, 15, 5, 3, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static const ne10_int32_t ne10_factors_240[64] = { 3, 20, 4, 15, 5, 3, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static const ne10_int32_t ne10_factors_120[64] = { 3, 10, 2, 15, 5, 3, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static const ne10_int32_t ne10_factors_60[64] = { 2, 5, 5, 3, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static const ne10_fft_cpx_int32_t ne10_twiddles_480[480] = { {0,0}, {2147483647,0}, {2147483647,0}, {2147483647,0}, {1961823921,-873460313}, {1436946998,-1595891394}, {2147483647,0}, {1436946998,-1595891394}, {-224473265,-2135719496}, {2147483647,0}, {663608871,-2042378339}, {-1737350854,-1262259096}, {2147483647,0}, {-224473265,-2135719496}, {-2100555935,446487152}, {2147483647,0}, {2100555974,-446486968}, {1961823921,-873460313}, {1737350743,-1262259248}, {1436946998,-1595891394}, {1073741769,-1859775424}, {663608871,-2042378339}, {224473078,-2135719516}, {-224473265,-2135719496}, {-663609049,-2042378281}, {-1073741932,-1859775330}, {-1436947137,-1595891268}, {-1737350854,-1262259096}, {-1961823997,-873460141}, {-2100556013,-446486785}, {2147483647,0}, {2144540595,-112390613}, {2135719506,-224473172}, {2121044558,-335940465}, {2100555974,-446486968}, {2074309912,-555809682}, {2042378310,-663608960}, {2004848691,-769589332}, {1961823921,-873460313}, {1913421927,-974937199}, {1859775377,-1073741851}, {1801031311,-1169603450}, {1737350743,-1262259248}, {1668908218,-1351455280}, {1595891331,-1436947067}, {1518500216,-1518500282}, {1436946998,-1595891394}, {1351455207,-1668908277}, {1262259172,-1737350799}, {1169603371,-1801031362}, {1073741769,-1859775424}, {974937230,-1913421912}, {873460227,-1961823959}, {769589125,-2004848771}, {663608871,-2042378339}, {555809715,-2074309903}, {446486876,-2100555994}, {335940246,-2121044593}, {224473078,-2135719516}, {112390647,-2144540593}, {2147483647,0}, {2135719506,-224473172}, {2100555974,-446486968}, {2042378310,-663608960}, {1961823921,-873460313}, {1859775377,-1073741851}, {1737350743,-1262259248}, {1595891331,-1436947067}, {1436946998,-1595891394}, {1262259172,-1737350799}, {1073741769,-1859775424}, {873460227,-1961823959}, {663608871,-2042378339}, {446486876,-2100555994}, {224473078,-2135719516}, {-94,-2147483647}, {-224473265,-2135719496}, {-446487060,-2100555955}, {-663609049,-2042378281}, {-873460398,-1961823883}, {-1073741932,-1859775330}, {-1262259116,-1737350839}, {-1436947137,-1595891268}, {-1595891628,-1436946738}, {-1737350854,-1262259096}, {-1859775343,-1073741910}, {-1961823997,-873460141}, {-2042378447,-663608538}, {-2100556013,-446486785}, {-2135719499,-224473240}, {2147483647,0}, {2121044558,-335940465}, {2042378310,-663608960}, {1913421927,-974937199}, {1737350743,-1262259248}, {1518500216,-1518500282}, {1262259172,-1737350799}, {974937230,-1913421912}, {663608871,-2042378339}, {335940246,-2121044593}, {-94,-2147483647}, {-335940431,-2121044564}, {-663609049,-2042378281}, {-974937397,-1913421827}, {-1262259116,-1737350839}, {-1518500258,-1518500240}, {-1737350854,-1262259096}, {-1913422071,-974936918}, {-2042378447,-663608538}, {-2121044568,-335940406}, {-2147483647,188}, {-2121044509,335940777}, {-2042378331,663608895}, {-1913421900,974937252}, {-1737350633,1262259400}, {-1518499993,1518500506}, {-1262258813,1737351059}, {-974936606,1913422229}, {-663609179,2042378239}, {-335940566,2121044542}, {2147483647,0}, {2147299667,-28109693}, {2146747758,-56214570}, {2145828015,-84309815}, {2144540595,-112390613}, {2142885719,-140452154}, {2140863671,-168489630}, {2138474797,-196498235}, {2135719506,-224473172}, {2132598271,-252409646}, {2129111626,-280302871}, {2125260168,-308148068}, {2121044558,-335940465}, {2116465518,-363675300}, {2111523833,-391347822}, {2106220349,-418953288}, {2100555974,-446486968}, {2094531681,-473944146}, {2088148500,-501320115}, {2081407525,-528610186}, {2074309912,-555809682}, {2066856885,-582913912}, {2059049696,-609918325}, {2050889698,-636818231}, {2042378310,-663608960}, {2033516972,-690285983}, {2024307180,-716844791}, {2014750533,-743280770}, {2004848691,-769589332}, {1994603329,-795766029}, {1984016179,-821806435}, {1973089077,-847706028}, {1961823921,-873460313}, {1950222618,-899064934}, {1938287127,-924515564}, {1926019520,-949807783}, {1913421927,-974937199}, {1900496481,-999899565}, {1887245364,-1024690661}, {1873670877,-1049306180}, {1859775377,-1073741851}, {1845561215,-1097993541}, {1831030826,-1122057097}, {1816186632,-1145928502}, {1801031311,-1169603450}, {1785567394,-1193077993}, {1769797456,-1216348214}, {1753724345,-1239409914}, {1737350743,-1262259248}, {1720679456,-1284892300}, {1703713340,-1307305194}, {1686455222,-1329494189}, {1668908218,-1351455280}, {1651075255,-1373184807}, {1632959307,-1394679144}, {1614563642,-1415934412}, {1595891331,-1436947067}, {1576945572,-1457713510}, {1557729613,-1478230181}, {1538246655,-1498493658}, {1518500216,-1518500282}, {1498493590,-1538246721}, {1478230113,-1557729677}, {1457713441,-1576945636}, {1436946998,-1595891394}, {1415934341,-1614563704}, {1394679073,-1632959368}, {1373184735,-1651075315}, {1351455207,-1668908277}, {1329494115,-1686455280}, {1307305120,-1703713397}, {1284892225,-1720679512}, {1262259172,-1737350799}, {1239409837,-1753724400}, {1216348136,-1769797510}, {1193077915,-1785567446}, {1169603371,-1801031362}, {1145928423,-1816186682}, {1122057017,-1831030875}, {1097993571,-1845561197}, {1073741769,-1859775424}, {1049305987,-1873670985}, {1024690635,-1887245378}, {999899482,-1900496524}, {974937230,-1913421912}, {949807699,-1926019561}, {924515422,-1938287195}, {899064965,-1950222603}, {873460227,-1961823959}, {847705824,-1973089164}, {821806407,-1984016190}, {795765941,-1994603364}, {769589125,-2004848771}, {743280682,-2014750566}, {716844642,-2024307233}, {690286016,-2033516961}, {663608871,-2042378339}, {636818019,-2050889764}, {609918296,-2059049705}, {582913822,-2066856911}, {555809715,-2074309903}, {528610126,-2081407540}, {501319962,-2088148536}, {473944148,-2094531680}, {446486876,-2100555994}, {418953102,-2106220386}, {391347792,-2111523838}, {363675176,-2116465540}, {335940246,-2121044593}, {308148006,-2125260177}, {280302715,-2129111646}, {252409648,-2132598271}, {224473078,-2135719516}, {196498046,-2138474814}, {168489600,-2140863674}, {140452029,-2142885728}, {112390647,-2144540593}, {84309753,-2145828017}, {56214412,-2146747762}, {28109695,-2147299667}, {2147483647,0}, {2146747758,-56214570}, {2144540595,-112390613}, {2140863671,-168489630}, {2135719506,-224473172}, {2129111626,-280302871}, {2121044558,-335940465}, {2111523833,-391347822}, {2100555974,-446486968}, {2088148500,-501320115}, {2074309912,-555809682}, {2059049696,-609918325}, {2042378310,-663608960}, {2024307180,-716844791}, {2004848691,-769589332}, {1984016179,-821806435}, {1961823921,-873460313}, {1938287127,-924515564}, {1913421927,-974937199}, {1887245364,-1024690661}, {1859775377,-1073741851}, {1831030826,-1122057097}, {1801031311,-1169603450}, {1769797456,-1216348214}, {1737350743,-1262259248}, {1703713340,-1307305194}, {1668908218,-1351455280}, {1632959307,-1394679144}, {1595891331,-1436947067}, {1557729613,-1478230181}, {1518500216,-1518500282}, {1478230113,-1557729677}, {1436946998,-1595891394}, {1394679073,-1632959368}, {1351455207,-1668908277}, {1307305120,-1703713397}, {1262259172,-1737350799}, {1216348136,-1769797510}, {1169603371,-1801031362}, {1122057017,-1831030875}, {1073741769,-1859775424}, {1024690635,-1887245378}, {974937230,-1913421912}, {924515422,-1938287195}, {873460227,-1961823959}, {821806407,-1984016190}, {769589125,-2004848771}, {716844642,-2024307233}, {663608871,-2042378339}, {609918296,-2059049705}, {555809715,-2074309903}, {501319962,-2088148536}, {446486876,-2100555994}, {391347792,-2111523838}, {335940246,-2121044593}, {280302715,-2129111646}, {224473078,-2135719516}, {168489600,-2140863674}, {112390647,-2144540593}, {56214412,-2146747762}, {-94,-2147483647}, {-56214600,-2146747757}, {-112390835,-2144540584}, {-168489787,-2140863659}, {-224473265,-2135719496}, {-280302901,-2129111622}, {-335940431,-2121044564}, {-391347977,-2111523804}, {-446487060,-2100555955}, {-501320144,-2088148493}, {-555809896,-2074309855}, {-609918476,-2059049651}, {-663609049,-2042378281}, {-716844819,-2024307170}, {-769589300,-2004848703}, {-821806581,-1984016118}, {-873460398,-1961823883}, {-924515591,-1938287114}, {-974937397,-1913421827}, {-1024690575,-1887245411}, {-1073741932,-1859775330}, {-1122057395,-1831030643}, {-1169603421,-1801031330}, {-1216348291,-1769797403}, {-1262259116,-1737350839}, {-1307305268,-1703713283}, {-1351455453,-1668908078}, {-1394679021,-1632959413}, {-1436947137,-1595891268}, {-1478230435,-1557729372}, {-1518500258,-1518500240}, {-1557729742,-1478230045}, {-1595891628,-1436946738}, {-1632959429,-1394679001}, {-1668908417,-1351455035}, {-1703713298,-1307305248}, {-1737350854,-1262259096}, {-1769797708,-1216347848}, {-1801031344,-1169603400}, {-1831030924,-1122056937}, {-1859775343,-1073741910}, {-1887245423,-1024690552}, {-1913422071,-974936918}, {-1938287125,-924515568}, {-1961823997,-873460141}, {-1984016324,-821806084}, {-2004848713,-769589276}, {-2024307264,-716844553}, {-2042378447,-663608538}, {-2059049731,-609918206}, {-2074309994,-555809377}, {-2088148499,-501320119}, {-2100556013,-446486785}, {-2111523902,-391347448}, {-2121044568,-335940406}, {-2129111659,-280302621}, {-2135719499,-224473240}, {-2140863681,-168489506}, {-2144540612,-112390298}, {-2146747758,-56214574}, {2147483647,0}, {2145828015,-84309815}, {2140863671,-168489630}, {2132598271,-252409646}, {2121044558,-335940465}, {2106220349,-418953288}, {2088148500,-501320115}, {2066856885,-582913912}, {2042378310,-663608960}, {2014750533,-743280770}, {1984016179,-821806435}, {1950222618,-899064934}, {1913421927,-974937199}, {1873670877,-1049306180}, {1831030826,-1122057097}, {1785567394,-1193077993}, {1737350743,-1262259248}, {1686455222,-1329494189}, {1632959307,-1394679144}, {1576945572,-1457713510}, {1518500216,-1518500282}, {1457713441,-1576945636}, {1394679073,-1632959368}, {1329494115,-1686455280}, {1262259172,-1737350799}, {1193077915,-1785567446}, {1122057017,-1831030875}, {1049305987,-1873670985}, {974937230,-1913421912}, {899064965,-1950222603}, {821806407,-1984016190}, {743280682,-2014750566}, {663608871,-2042378339}, {582913822,-2066856911}, {501319962,-2088148536}, {418953102,-2106220386}, {335940246,-2121044593}, {252409648,-2132598271}, {168489600,-2140863674}, {84309753,-2145828017}, {-94,-2147483647}, {-84309940,-2145828010}, {-168489787,-2140863659}, {-252409834,-2132598249}, {-335940431,-2121044564}, {-418953286,-2106220349}, {-501320144,-2088148493}, {-582914003,-2066856860}, {-663609049,-2042378281}, {-743280858,-2014750501}, {-821806581,-1984016118}, {-899065136,-1950222525}, {-974937397,-1913421827}, {-1049306374,-1873670768}, {-1122057395,-1831030643}, {-1193078284,-1785567199}, {-1262259116,-1737350839}, {-1329494061,-1686455323}, {-1394679021,-1632959413}, {-1457713485,-1576945595}, {-1518500258,-1518500240}, {-1576945613,-1457713466}, {-1632959429,-1394679001}, {-1686455338,-1329494041}, {-1737350854,-1262259096}, {-1785567498,-1193077837}, {-1831030924,-1122056937}, {-1873671031,-1049305905}, {-1913422071,-974936918}, {-1950222750,-899064648}, {-1984016324,-821806084}, {-2014750687,-743280354}, {-2042378447,-663608538}, {-2066856867,-582913978}, {-2088148499,-501320119}, {-2106220354,-418953261}, {-2121044568,-335940406}, {-2132598282,-252409555}, {-2140863681,-168489506}, {-2145828021,-84309659}, {-2147483647,188}, {-2145828006,84310034}, {-2140863651,168489881}, {-2132598237,252409928}, {-2121044509,335940777}, {-2106220281,418953629}, {-2088148411,501320484}, {-2066856765,582914339}, {-2042378331,663608895}, {-2014750557,743280706}, {-1984016181,821806431}, {-1950222593,899064989}, {-1913421900,974937252}, {-1873670848,1049306232}, {-1831030728,1122057257}, {-1785567289,1193078149}, {-1737350633,1262259400}, {-1686455106,1329494336}, {-1632959185,1394679287}, {-1576945358,1457713742}, {-1518499993,1518500506}, {-1457713209,1576945850}, {-1394678735,1632959656}, {-1329493766,1686455555}, {-1262258813,1737351059}, {-1193077546,1785567692}, {-1122056638,1831031107}, {-1049305599,1873671202}, {-974936606,1913422229}, {-899064330,1950222896}, {-821805761,1984016458}, {-743280025,2014750808}, {-663609179,2042378239}, {-582914134,2066856823}, {-501320277,2088148461}, {-418953420,2106220322}, {-335940566,2121044542}, {-252409716,2132598263}, {-168489668,2140863668}, {-84309821,2145828015}, }; static const ne10_fft_cpx_int32_t ne10_twiddles_240[240] = { {0,0}, {2147483647,0}, {2147483647,0}, {2147483647,0}, {1961823921,-873460313}, {1436946998,-1595891394}, {2147483647,0}, {1436946998,-1595891394}, {-224473265,-2135719496}, {2147483647,0}, {663608871,-2042378339}, {-1737350854,-1262259096}, {2147483647,0}, {-224473265,-2135719496}, {-2100555935,446487152}, {2147483647,0}, {2135719506,-224473172}, {2100555974,-446486968}, {2042378310,-663608960}, {1961823921,-873460313}, {1859775377,-1073741851}, {1737350743,-1262259248}, {1595891331,-1436947067}, {1436946998,-1595891394}, {1262259172,-1737350799}, {1073741769,-1859775424}, {873460227,-1961823959}, {663608871,-2042378339}, {446486876,-2100555994}, {224473078,-2135719516}, {2147483647,0}, {2100555974,-446486968}, {1961823921,-873460313}, {1737350743,-1262259248}, {1436946998,-1595891394}, {1073741769,-1859775424}, {663608871,-2042378339}, {224473078,-2135719516}, {-224473265,-2135719496}, {-663609049,-2042378281}, {-1073741932,-1859775330}, {-1436947137,-1595891268}, {-1737350854,-1262259096}, {-1961823997,-873460141}, {-2100556013,-446486785}, {2147483647,0}, {2042378310,-663608960}, {1737350743,-1262259248}, {1262259172,-1737350799}, {663608871,-2042378339}, {-94,-2147483647}, {-663609049,-2042378281}, {-1262259116,-1737350839}, {-1737350854,-1262259096}, {-2042378447,-663608538}, {-2147483647,188}, {-2042378331,663608895}, {-1737350633,1262259400}, {-1262258813,1737351059}, {-663609179,2042378239}, {2147483647,0}, {2146747758,-56214570}, {2144540595,-112390613}, {2140863671,-168489630}, {2135719506,-224473172}, {2129111626,-280302871}, {2121044558,-335940465}, {2111523833,-391347822}, {2100555974,-446486968}, {2088148500,-501320115}, {2074309912,-555809682}, {2059049696,-609918325}, {2042378310,-663608960}, {2024307180,-716844791}, {2004848691,-769589332}, {1984016179,-821806435}, {1961823921,-873460313}, {1938287127,-924515564}, {1913421927,-974937199}, {1887245364,-1024690661}, {1859775377,-1073741851}, {1831030826,-1122057097}, {1801031311,-1169603450}, {1769797456,-1216348214}, {1737350743,-1262259248}, {1703713340,-1307305194}, {1668908218,-1351455280}, {1632959307,-1394679144}, {1595891331,-1436947067}, {1557729613,-1478230181}, {1518500216,-1518500282}, {1478230113,-1557729677}, {1436946998,-1595891394}, {1394679073,-1632959368}, {1351455207,-1668908277}, {1307305120,-1703713397}, {1262259172,-1737350799}, {1216348136,-1769797510}, {1169603371,-1801031362}, {1122057017,-1831030875}, {1073741769,-1859775424}, {1024690635,-1887245378}, {974937230,-1913421912}, {924515422,-1938287195}, {873460227,-1961823959}, {821806407,-1984016190}, {769589125,-2004848771}, {716844642,-2024307233}, {663608871,-2042378339}, {609918296,-2059049705}, {555809715,-2074309903}, {501319962,-2088148536}, {446486876,-2100555994}, {391347792,-2111523838}, {335940246,-2121044593}, {280302715,-2129111646}, {224473078,-2135719516}, {168489600,-2140863674}, {112390647,-2144540593}, {56214412,-2146747762}, {2147483647,0}, {2144540595,-112390613}, {2135719506,-224473172}, {2121044558,-335940465}, {2100555974,-446486968}, {2074309912,-555809682}, {2042378310,-663608960}, {2004848691,-769589332}, {1961823921,-873460313}, {1913421927,-974937199}, {1859775377,-1073741851}, {1801031311,-1169603450}, {1737350743,-1262259248}, {1668908218,-1351455280}, {1595891331,-1436947067}, {1518500216,-1518500282}, {1436946998,-1595891394}, {1351455207,-1668908277}, {1262259172,-1737350799}, {1169603371,-1801031362}, {1073741769,-1859775424}, {974937230,-1913421912}, {873460227,-1961823959}, {769589125,-2004848771}, {663608871,-2042378339}, {555809715,-2074309903}, {446486876,-2100555994}, {335940246,-2121044593}, {224473078,-2135719516}, {112390647,-2144540593}, {-94,-2147483647}, {-112390835,-2144540584}, {-224473265,-2135719496}, {-335940431,-2121044564}, {-446487060,-2100555955}, {-555809896,-2074309855}, {-663609049,-2042378281}, {-769589300,-2004848703}, {-873460398,-1961823883}, {-974937397,-1913421827}, {-1073741932,-1859775330}, {-1169603421,-1801031330}, {-1262259116,-1737350839}, {-1351455453,-1668908078}, {-1436947137,-1595891268}, {-1518500258,-1518500240}, {-1595891628,-1436946738}, {-1668908417,-1351455035}, {-1737350854,-1262259096}, {-1801031344,-1169603400}, {-1859775343,-1073741910}, {-1913422071,-974936918}, {-1961823997,-873460141}, {-2004848713,-769589276}, {-2042378447,-663608538}, {-2074309994,-555809377}, {-2100556013,-446486785}, {-2121044568,-335940406}, {-2135719499,-224473240}, {-2144540612,-112390298}, {2147483647,0}, {2140863671,-168489630}, {2121044558,-335940465}, {2088148500,-501320115}, {2042378310,-663608960}, {1984016179,-821806435}, {1913421927,-974937199}, {1831030826,-1122057097}, {1737350743,-1262259248}, {1632959307,-1394679144}, {1518500216,-1518500282}, {1394679073,-1632959368}, {1262259172,-1737350799}, {1122057017,-1831030875}, {974937230,-1913421912}, {821806407,-1984016190}, {663608871,-2042378339}, {501319962,-2088148536}, {335940246,-2121044593}, {168489600,-2140863674}, {-94,-2147483647}, {-168489787,-2140863659}, {-335940431,-2121044564}, {-501320144,-2088148493}, {-663609049,-2042378281}, {-821806581,-1984016118}, {-974937397,-1913421827}, {-1122057395,-1831030643}, {-1262259116,-1737350839}, {-1394679021,-1632959413}, {-1518500258,-1518500240}, {-1632959429,-1394679001}, {-1737350854,-1262259096}, {-1831030924,-1122056937}, {-1913422071,-974936918}, {-1984016324,-821806084}, {-2042378447,-663608538}, {-2088148499,-501320119}, {-2121044568,-335940406}, {-2140863681,-168489506}, {-2147483647,188}, {-2140863651,168489881}, {-2121044509,335940777}, {-2088148411,501320484}, {-2042378331,663608895}, {-1984016181,821806431}, {-1913421900,974937252}, {-1831030728,1122057257}, {-1737350633,1262259400}, {-1632959185,1394679287}, {-1518499993,1518500506}, {-1394678735,1632959656}, {-1262258813,1737351059}, {-1122056638,1831031107}, {-974936606,1913422229}, {-821805761,1984016458}, {-663609179,2042378239}, {-501320277,2088148461}, {-335940566,2121044542}, {-168489668,2140863668}, }; static const ne10_fft_cpx_int32_t ne10_twiddles_120[120] = { {0,0}, {2147483647,0}, {2147483647,0}, {2147483647,0}, {1961823921,-873460313}, {1436946998,-1595891394}, {2147483647,0}, {1436946998,-1595891394}, {-224473265,-2135719496}, {2147483647,0}, {663608871,-2042378339}, {-1737350854,-1262259096}, {2147483647,0}, {-224473265,-2135719496}, {-2100555935,446487152}, {2147483647,0}, {2100555974,-446486968}, {1961823921,-873460313}, {1737350743,-1262259248}, {1436946998,-1595891394}, {1073741769,-1859775424}, {663608871,-2042378339}, {224473078,-2135719516}, {-224473265,-2135719496}, {-663609049,-2042378281}, {-1073741932,-1859775330}, {-1436947137,-1595891268}, {-1737350854,-1262259096}, {-1961823997,-873460141}, {-2100556013,-446486785}, {2147483647,0}, {2144540595,-112390613}, {2135719506,-224473172}, {2121044558,-335940465}, {2100555974,-446486968}, {2074309912,-555809682}, {2042378310,-663608960}, {2004848691,-769589332}, {1961823921,-873460313}, {1913421927,-974937199}, {1859775377,-1073741851}, {1801031311,-1169603450}, {1737350743,-1262259248}, {1668908218,-1351455280}, {1595891331,-1436947067}, {1518500216,-1518500282}, {1436946998,-1595891394}, {1351455207,-1668908277}, {1262259172,-1737350799}, {1169603371,-1801031362}, {1073741769,-1859775424}, {974937230,-1913421912}, {873460227,-1961823959}, {769589125,-2004848771}, {663608871,-2042378339}, {555809715,-2074309903}, {446486876,-2100555994}, {335940246,-2121044593}, {224473078,-2135719516}, {112390647,-2144540593}, {2147483647,0}, {2135719506,-224473172}, {2100555974,-446486968}, {2042378310,-663608960}, {1961823921,-873460313}, {1859775377,-1073741851}, {1737350743,-1262259248}, {1595891331,-1436947067}, {1436946998,-1595891394}, {1262259172,-1737350799}, {1073741769,-1859775424}, {873460227,-1961823959}, {663608871,-2042378339}, {446486876,-2100555994}, {224473078,-2135719516}, {-94,-2147483647}, {-224473265,-2135719496}, {-446487060,-2100555955}, {-663609049,-2042378281}, {-873460398,-1961823883}, {-1073741932,-1859775330}, {-1262259116,-1737350839}, {-1436947137,-1595891268}, {-1595891628,-1436946738}, {-1737350854,-1262259096}, {-1859775343,-1073741910}, {-1961823997,-873460141}, {-2042378447,-663608538}, {-2100556013,-446486785}, {-2135719499,-224473240}, {2147483647,0}, {2121044558,-335940465}, {2042378310,-663608960}, {1913421927,-974937199}, {1737350743,-1262259248}, {1518500216,-1518500282}, {1262259172,-1737350799}, {974937230,-1913421912}, {663608871,-2042378339}, {335940246,-2121044593}, {-94,-2147483647}, {-335940431,-2121044564}, {-663609049,-2042378281}, {-974937397,-1913421827}, {-1262259116,-1737350839}, {-1518500258,-1518500240}, {-1737350854,-1262259096}, {-1913422071,-974936918}, {-2042378447,-663608538}, {-2121044568,-335940406}, {-2147483647,188}, {-2121044509,335940777}, {-2042378331,663608895}, {-1913421900,974937252}, {-1737350633,1262259400}, {-1518499993,1518500506}, {-1262258813,1737351059}, {-974936606,1913422229}, {-663609179,2042378239}, {-335940566,2121044542}, }; static const ne10_fft_cpx_int32_t ne10_twiddles_60[60] = { {0,0}, {2147483647,0}, {2147483647,0}, {2147483647,0}, {1961823921,-873460313}, {1436946998,-1595891394}, {2147483647,0}, {1436946998,-1595891394}, {-224473265,-2135719496}, {2147483647,0}, {663608871,-2042378339}, {-1737350854,-1262259096}, {2147483647,0}, {-224473265,-2135719496}, {-2100555935,446487152}, {2147483647,0}, {2135719506,-224473172}, {2100555974,-446486968}, {2042378310,-663608960}, {1961823921,-873460313}, {1859775377,-1073741851}, {1737350743,-1262259248}, {1595891331,-1436947067}, {1436946998,-1595891394}, {1262259172,-1737350799}, {1073741769,-1859775424}, {873460227,-1961823959}, {663608871,-2042378339}, {446486876,-2100555994}, {224473078,-2135719516}, {2147483647,0}, {2100555974,-446486968}, {1961823921,-873460313}, {1737350743,-1262259248}, {1436946998,-1595891394}, {1073741769,-1859775424}, {663608871,-2042378339}, {224473078,-2135719516}, {-224473265,-2135719496}, {-663609049,-2042378281}, {-1073741932,-1859775330}, {-1436947137,-1595891268}, {-1737350854,-1262259096}, {-1961823997,-873460141}, {-2100556013,-446486785}, {2147483647,0}, {2042378310,-663608960}, {1737350743,-1262259248}, {1262259172,-1737350799}, {663608871,-2042378339}, {-94,-2147483647}, {-663609049,-2042378281}, {-1262259116,-1737350839}, {-1737350854,-1262259096}, {-2042378447,-663608538}, {-2147483647,188}, {-2042378331,663608895}, {-1737350633,1262259400}, {-1262258813,1737351059}, {-663609179,2042378239}, }; static const ne10_fft_state_int32_t ne10_fft_state_int32_t_480 = { 120, (ne10_int32_t *)ne10_factors_480, (ne10_fft_cpx_int32_t *)ne10_twiddles_480, NULL, (ne10_fft_cpx_int32_t *)&ne10_twiddles_480[120], }; static const arch_fft_state cfg_arch_480 = { 1, (void *)&ne10_fft_state_int32_t_480, }; static const ne10_fft_state_int32_t ne10_fft_state_int32_t_240 = { 60, (ne10_int32_t *)ne10_factors_240, (ne10_fft_cpx_int32_t *)ne10_twiddles_240, NULL, (ne10_fft_cpx_int32_t *)&ne10_twiddles_240[60], }; static const arch_fft_state cfg_arch_240 = { 1, (void *)&ne10_fft_state_int32_t_240, }; static const ne10_fft_state_int32_t ne10_fft_state_int32_t_120 = { 30, (ne10_int32_t *)ne10_factors_120, (ne10_fft_cpx_int32_t *)ne10_twiddles_120, NULL, (ne10_fft_cpx_int32_t *)&ne10_twiddles_120[30], }; static const arch_fft_state cfg_arch_120 = { 1, (void *)&ne10_fft_state_int32_t_120, }; static const ne10_fft_state_int32_t ne10_fft_state_int32_t_60 = { 15, (ne10_int32_t *)ne10_factors_60, (ne10_fft_cpx_int32_t *)ne10_twiddles_60, NULL, (ne10_fft_cpx_int32_t *)&ne10_twiddles_60[15], }; static const arch_fft_state cfg_arch_60 = { 1, (void *)&ne10_fft_state_int32_t_60, }; #endif /* end NE10_FFT_PARAMS48000_960 */ ================================================ FILE: deps/pjsip/third_party/opus/celt/static_modes_float.h ================================================ /* The contents of this file was automatically generated by dump_modes.c with arguments: 48000 960 It contains static definitions for some pre-defined modes. */ #include "modes.h" #include "rate.h" #ifdef HAVE_ARM_NE10 #define OVERRIDE_FFT 1 #include "static_modes_float_arm_ne10.h" #endif #ifndef DEF_WINDOW120 #define DEF_WINDOW120 static const opus_val16 window120[120] = { 6.7286966e-05f, 0.00060551348f, 0.0016815970f, 0.0032947962f, 0.0054439943f, 0.0081276923f, 0.011344001f, 0.015090633f, 0.019364886f, 0.024163635f, 0.029483315f, 0.035319905f, 0.041668911f, 0.048525347f, 0.055883718f, 0.063737999f, 0.072081616f, 0.080907428f, 0.090207705f, 0.099974111f, 0.11019769f, 0.12086883f, 0.13197729f, 0.14351214f, 0.15546177f, 0.16781389f, 0.18055550f, 0.19367290f, 0.20715171f, 0.22097682f, 0.23513243f, 0.24960208f, 0.26436860f, 0.27941419f, 0.29472040f, 0.31026818f, 0.32603788f, 0.34200931f, 0.35816177f, 0.37447407f, 0.39092462f, 0.40749142f, 0.42415215f, 0.44088423f, 0.45766484f, 0.47447104f, 0.49127978f, 0.50806798f, 0.52481261f, 0.54149077f, 0.55807973f, 0.57455701f, 0.59090049f, 0.60708841f, 0.62309951f, 0.63891306f, 0.65450896f, 0.66986776f, 0.68497077f, 0.69980010f, 0.71433873f, 0.72857055f, 0.74248043f, 0.75605424f, 0.76927895f, 0.78214257f, 0.79463430f, 0.80674445f, 0.81846456f, 0.82978733f, 0.84070669f, 0.85121779f, 0.86131698f, 0.87100183f, 0.88027111f, 0.88912479f, 0.89756398f, 0.90559094f, 0.91320904f, 0.92042270f, 0.92723738f, 0.93365955f, 0.93969656f, 0.94535671f, 0.95064907f, 0.95558353f, 0.96017067f, 0.96442171f, 0.96834849f, 0.97196334f, 0.97527906f, 0.97830883f, 0.98106616f, 0.98356480f, 0.98581869f, 0.98784191f, 0.98964856f, 0.99125274f, 0.99266849f, 0.99390969f, 0.99499004f, 0.99592297f, 0.99672162f, 0.99739874f, 0.99796667f, 0.99843728f, 0.99882195f, 0.99913147f, 0.99937606f, 0.99956527f, 0.99970802f, 0.99981248f, 0.99988613f, 0.99993565f, 0.99996697f, 0.99998518f, 0.99999457f, 0.99999859f, 0.99999982f, 1.0000000f, }; #endif #ifndef DEF_LOGN400 #define DEF_LOGN400 static const opus_int16 logN400[21] = { 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 16, 16, 16, 21, 21, 24, 29, 34, 36, }; #endif #ifndef DEF_PULSE_CACHE50 #define DEF_PULSE_CACHE50 static const opus_int16 cache_index50[105] = { -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 41, 41, 41, 82, 82, 123, 164, 200, 222, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 123, 123, 123, 164, 164, 240, 266, 283, 295, 41, 41, 41, 41, 41, 41, 41, 41, 123, 123, 123, 123, 240, 240, 240, 266, 266, 305, 318, 328, 336, 123, 123, 123, 123, 123, 123, 123, 123, 240, 240, 240, 240, 305, 305, 305, 318, 318, 343, 351, 358, 364, 240, 240, 240, 240, 240, 240, 240, 240, 305, 305, 305, 305, 343, 343, 343, 351, 351, 370, 376, 382, 387, }; static const unsigned char cache_bits50[392] = { 40, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 40, 15, 23, 28, 31, 34, 36, 38, 39, 41, 42, 43, 44, 45, 46, 47, 47, 49, 50, 51, 52, 53, 54, 55, 55, 57, 58, 59, 60, 61, 62, 63, 63, 65, 66, 67, 68, 69, 70, 71, 71, 40, 20, 33, 41, 48, 53, 57, 61, 64, 66, 69, 71, 73, 75, 76, 78, 80, 82, 85, 87, 89, 91, 92, 94, 96, 98, 101, 103, 105, 107, 108, 110, 112, 114, 117, 119, 121, 123, 124, 126, 128, 40, 23, 39, 51, 60, 67, 73, 79, 83, 87, 91, 94, 97, 100, 102, 105, 107, 111, 115, 118, 121, 124, 126, 129, 131, 135, 139, 142, 145, 148, 150, 153, 155, 159, 163, 166, 169, 172, 174, 177, 179, 35, 28, 49, 65, 78, 89, 99, 107, 114, 120, 126, 132, 136, 141, 145, 149, 153, 159, 165, 171, 176, 180, 185, 189, 192, 199, 205, 211, 216, 220, 225, 229, 232, 239, 245, 251, 21, 33, 58, 79, 97, 112, 125, 137, 148, 157, 166, 174, 182, 189, 195, 201, 207, 217, 227, 235, 243, 251, 17, 35, 63, 86, 106, 123, 139, 152, 165, 177, 187, 197, 206, 214, 222, 230, 237, 250, 25, 31, 55, 75, 91, 105, 117, 128, 138, 146, 154, 161, 168, 174, 180, 185, 190, 200, 208, 215, 222, 229, 235, 240, 245, 255, 16, 36, 65, 89, 110, 128, 144, 159, 173, 185, 196, 207, 217, 226, 234, 242, 250, 11, 41, 74, 103, 128, 151, 172, 191, 209, 225, 241, 255, 9, 43, 79, 110, 138, 163, 186, 207, 227, 246, 12, 39, 71, 99, 123, 144, 164, 182, 198, 214, 228, 241, 253, 9, 44, 81, 113, 142, 168, 192, 214, 235, 255, 7, 49, 90, 127, 160, 191, 220, 247, 6, 51, 95, 134, 170, 203, 234, 7, 47, 87, 123, 155, 184, 212, 237, 6, 52, 97, 137, 174, 208, 240, 5, 57, 106, 151, 192, 231, 5, 59, 111, 158, 202, 243, 5, 55, 103, 147, 187, 224, 5, 60, 113, 161, 206, 248, 4, 65, 122, 175, 224, 4, 67, 127, 182, 234, }; static const unsigned char cache_caps50[168] = { 224, 224, 224, 224, 224, 224, 224, 224, 160, 160, 160, 160, 185, 185, 185, 178, 178, 168, 134, 61, 37, 224, 224, 224, 224, 224, 224, 224, 224, 240, 240, 240, 240, 207, 207, 207, 198, 198, 183, 144, 66, 40, 160, 160, 160, 160, 160, 160, 160, 160, 185, 185, 185, 185, 193, 193, 193, 183, 183, 172, 138, 64, 38, 240, 240, 240, 240, 240, 240, 240, 240, 207, 207, 207, 207, 204, 204, 204, 193, 193, 180, 143, 66, 40, 185, 185, 185, 185, 185, 185, 185, 185, 193, 193, 193, 193, 193, 193, 193, 183, 183, 172, 138, 65, 39, 207, 207, 207, 207, 207, 207, 207, 207, 204, 204, 204, 204, 201, 201, 201, 188, 188, 176, 141, 66, 40, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 194, 194, 194, 184, 184, 173, 139, 65, 39, 204, 204, 204, 204, 204, 204, 204, 204, 201, 201, 201, 201, 198, 198, 198, 187, 187, 175, 140, 66, 40, }; #endif #ifndef FFT_TWIDDLES48000_960 #define FFT_TWIDDLES48000_960 static const kiss_twiddle_cpx fft_twiddles48000_960[480] = { {1.0000000f, -0.0000000f}, {0.99991433f, -0.013089596f}, {0.99965732f, -0.026176948f}, {0.99922904f, -0.039259816f}, {0.99862953f, -0.052335956f}, {0.99785892f, -0.065403129f}, {0.99691733f, -0.078459096f}, {0.99580493f, -0.091501619f}, {0.99452190f, -0.10452846f}, {0.99306846f, -0.11753740f}, {0.99144486f, -0.13052619f}, {0.98965139f, -0.14349262f}, {0.98768834f, -0.15643447f}, {0.98555606f, -0.16934950f}, {0.98325491f, -0.18223553f}, {0.98078528f, -0.19509032f}, {0.97814760f, -0.20791169f}, {0.97534232f, -0.22069744f}, {0.97236992f, -0.23344536f}, {0.96923091f, -0.24615329f}, {0.96592583f, -0.25881905f}, {0.96245524f, -0.27144045f}, {0.95881973f, -0.28401534f}, {0.95501994f, -0.29654157f}, {0.95105652f, -0.30901699f}, {0.94693013f, -0.32143947f}, {0.94264149f, -0.33380686f}, {0.93819134f, -0.34611706f}, {0.93358043f, -0.35836795f}, {0.92880955f, -0.37055744f}, {0.92387953f, -0.38268343f}, {0.91879121f, -0.39474386f}, {0.91354546f, -0.40673664f}, {0.90814317f, -0.41865974f}, {0.90258528f, -0.43051110f}, {0.89687274f, -0.44228869f}, {0.89100652f, -0.45399050f}, {0.88498764f, -0.46561452f}, {0.87881711f, -0.47715876f}, {0.87249601f, -0.48862124f}, {0.86602540f, -0.50000000f}, {0.85940641f, -0.51129309f}, {0.85264016f, -0.52249856f}, {0.84572782f, -0.53361452f}, {0.83867057f, -0.54463904f}, {0.83146961f, -0.55557023f}, {0.82412619f, -0.56640624f}, {0.81664156f, -0.57714519f}, {0.80901699f, -0.58778525f}, {0.80125381f, -0.59832460f}, {0.79335334f, -0.60876143f}, {0.78531693f, -0.61909395f}, {0.77714596f, -0.62932039f}, {0.76884183f, -0.63943900f}, {0.76040597f, -0.64944805f}, {0.75183981f, -0.65934582f}, {0.74314483f, -0.66913061f}, {0.73432251f, -0.67880075f}, {0.72537437f, -0.68835458f}, {0.71630194f, -0.69779046f}, {0.70710678f, -0.70710678f}, {0.69779046f, -0.71630194f}, {0.68835458f, -0.72537437f}, {0.67880075f, -0.73432251f}, {0.66913061f, -0.74314483f}, {0.65934582f, -0.75183981f}, {0.64944805f, -0.76040597f}, {0.63943900f, -0.76884183f}, {0.62932039f, -0.77714596f}, {0.61909395f, -0.78531693f}, {0.60876143f, -0.79335334f}, {0.59832460f, -0.80125381f}, {0.58778525f, -0.80901699f}, {0.57714519f, -0.81664156f}, {0.56640624f, -0.82412619f}, {0.55557023f, -0.83146961f}, {0.54463904f, -0.83867057f}, {0.53361452f, -0.84572782f}, {0.52249856f, -0.85264016f}, {0.51129309f, -0.85940641f}, {0.50000000f, -0.86602540f}, {0.48862124f, -0.87249601f}, {0.47715876f, -0.87881711f}, {0.46561452f, -0.88498764f}, {0.45399050f, -0.89100652f}, {0.44228869f, -0.89687274f}, {0.43051110f, -0.90258528f}, {0.41865974f, -0.90814317f}, {0.40673664f, -0.91354546f}, {0.39474386f, -0.91879121f}, {0.38268343f, -0.92387953f}, {0.37055744f, -0.92880955f}, {0.35836795f, -0.93358043f}, {0.34611706f, -0.93819134f}, {0.33380686f, -0.94264149f}, {0.32143947f, -0.94693013f}, {0.30901699f, -0.95105652f}, {0.29654157f, -0.95501994f}, {0.28401534f, -0.95881973f}, {0.27144045f, -0.96245524f}, {0.25881905f, -0.96592583f}, {0.24615329f, -0.96923091f}, {0.23344536f, -0.97236992f}, {0.22069744f, -0.97534232f}, {0.20791169f, -0.97814760f}, {0.19509032f, -0.98078528f}, {0.18223553f, -0.98325491f}, {0.16934950f, -0.98555606f}, {0.15643447f, -0.98768834f}, {0.14349262f, -0.98965139f}, {0.13052619f, -0.99144486f}, {0.11753740f, -0.99306846f}, {0.10452846f, -0.99452190f}, {0.091501619f, -0.99580493f}, {0.078459096f, -0.99691733f}, {0.065403129f, -0.99785892f}, {0.052335956f, -0.99862953f}, {0.039259816f, -0.99922904f}, {0.026176948f, -0.99965732f}, {0.013089596f, -0.99991433f}, {6.1230318e-17f, -1.0000000f}, {-0.013089596f, -0.99991433f}, {-0.026176948f, -0.99965732f}, {-0.039259816f, -0.99922904f}, {-0.052335956f, -0.99862953f}, {-0.065403129f, -0.99785892f}, {-0.078459096f, -0.99691733f}, {-0.091501619f, -0.99580493f}, {-0.10452846f, -0.99452190f}, {-0.11753740f, -0.99306846f}, {-0.13052619f, -0.99144486f}, {-0.14349262f, -0.98965139f}, {-0.15643447f, -0.98768834f}, {-0.16934950f, -0.98555606f}, {-0.18223553f, -0.98325491f}, {-0.19509032f, -0.98078528f}, {-0.20791169f, -0.97814760f}, {-0.22069744f, -0.97534232f}, {-0.23344536f, -0.97236992f}, {-0.24615329f, -0.96923091f}, {-0.25881905f, -0.96592583f}, {-0.27144045f, -0.96245524f}, {-0.28401534f, -0.95881973f}, {-0.29654157f, -0.95501994f}, {-0.30901699f, -0.95105652f}, {-0.32143947f, -0.94693013f}, {-0.33380686f, -0.94264149f}, {-0.34611706f, -0.93819134f}, {-0.35836795f, -0.93358043f}, {-0.37055744f, -0.92880955f}, {-0.38268343f, -0.92387953f}, {-0.39474386f, -0.91879121f}, {-0.40673664f, -0.91354546f}, {-0.41865974f, -0.90814317f}, {-0.43051110f, -0.90258528f}, {-0.44228869f, -0.89687274f}, {-0.45399050f, -0.89100652f}, {-0.46561452f, -0.88498764f}, {-0.47715876f, -0.87881711f}, {-0.48862124f, -0.87249601f}, {-0.50000000f, -0.86602540f}, {-0.51129309f, -0.85940641f}, {-0.52249856f, -0.85264016f}, {-0.53361452f, -0.84572782f}, {-0.54463904f, -0.83867057f}, {-0.55557023f, -0.83146961f}, {-0.56640624f, -0.82412619f}, {-0.57714519f, -0.81664156f}, {-0.58778525f, -0.80901699f}, {-0.59832460f, -0.80125381f}, {-0.60876143f, -0.79335334f}, {-0.61909395f, -0.78531693f}, {-0.62932039f, -0.77714596f}, {-0.63943900f, -0.76884183f}, {-0.64944805f, -0.76040597f}, {-0.65934582f, -0.75183981f}, {-0.66913061f, -0.74314483f}, {-0.67880075f, -0.73432251f}, {-0.68835458f, -0.72537437f}, {-0.69779046f, -0.71630194f}, {-0.70710678f, -0.70710678f}, {-0.71630194f, -0.69779046f}, {-0.72537437f, -0.68835458f}, {-0.73432251f, -0.67880075f}, {-0.74314483f, -0.66913061f}, {-0.75183981f, -0.65934582f}, {-0.76040597f, -0.64944805f}, {-0.76884183f, -0.63943900f}, {-0.77714596f, -0.62932039f}, {-0.78531693f, -0.61909395f}, {-0.79335334f, -0.60876143f}, {-0.80125381f, -0.59832460f}, {-0.80901699f, -0.58778525f}, {-0.81664156f, -0.57714519f}, {-0.82412619f, -0.56640624f}, {-0.83146961f, -0.55557023f}, {-0.83867057f, -0.54463904f}, {-0.84572782f, -0.53361452f}, {-0.85264016f, -0.52249856f}, {-0.85940641f, -0.51129309f}, {-0.86602540f, -0.50000000f}, {-0.87249601f, -0.48862124f}, {-0.87881711f, -0.47715876f}, {-0.88498764f, -0.46561452f}, {-0.89100652f, -0.45399050f}, {-0.89687274f, -0.44228869f}, {-0.90258528f, -0.43051110f}, {-0.90814317f, -0.41865974f}, {-0.91354546f, -0.40673664f}, {-0.91879121f, -0.39474386f}, {-0.92387953f, -0.38268343f}, {-0.92880955f, -0.37055744f}, {-0.93358043f, -0.35836795f}, {-0.93819134f, -0.34611706f}, {-0.94264149f, -0.33380686f}, {-0.94693013f, -0.32143947f}, {-0.95105652f, -0.30901699f}, {-0.95501994f, -0.29654157f}, {-0.95881973f, -0.28401534f}, {-0.96245524f, -0.27144045f}, {-0.96592583f, -0.25881905f}, {-0.96923091f, -0.24615329f}, {-0.97236992f, -0.23344536f}, {-0.97534232f, -0.22069744f}, {-0.97814760f, -0.20791169f}, {-0.98078528f, -0.19509032f}, {-0.98325491f, -0.18223553f}, {-0.98555606f, -0.16934950f}, {-0.98768834f, -0.15643447f}, {-0.98965139f, -0.14349262f}, {-0.99144486f, -0.13052619f}, {-0.99306846f, -0.11753740f}, {-0.99452190f, -0.10452846f}, {-0.99580493f, -0.091501619f}, {-0.99691733f, -0.078459096f}, {-0.99785892f, -0.065403129f}, {-0.99862953f, -0.052335956f}, {-0.99922904f, -0.039259816f}, {-0.99965732f, -0.026176948f}, {-0.99991433f, -0.013089596f}, {-1.0000000f, -1.2246064e-16f}, {-0.99991433f, 0.013089596f}, {-0.99965732f, 0.026176948f}, {-0.99922904f, 0.039259816f}, {-0.99862953f, 0.052335956f}, {-0.99785892f, 0.065403129f}, {-0.99691733f, 0.078459096f}, {-0.99580493f, 0.091501619f}, {-0.99452190f, 0.10452846f}, {-0.99306846f, 0.11753740f}, {-0.99144486f, 0.13052619f}, {-0.98965139f, 0.14349262f}, {-0.98768834f, 0.15643447f}, {-0.98555606f, 0.16934950f}, {-0.98325491f, 0.18223553f}, {-0.98078528f, 0.19509032f}, {-0.97814760f, 0.20791169f}, {-0.97534232f, 0.22069744f}, {-0.97236992f, 0.23344536f}, {-0.96923091f, 0.24615329f}, {-0.96592583f, 0.25881905f}, {-0.96245524f, 0.27144045f}, {-0.95881973f, 0.28401534f}, {-0.95501994f, 0.29654157f}, {-0.95105652f, 0.30901699f}, {-0.94693013f, 0.32143947f}, {-0.94264149f, 0.33380686f}, {-0.93819134f, 0.34611706f}, {-0.93358043f, 0.35836795f}, {-0.92880955f, 0.37055744f}, {-0.92387953f, 0.38268343f}, {-0.91879121f, 0.39474386f}, {-0.91354546f, 0.40673664f}, {-0.90814317f, 0.41865974f}, {-0.90258528f, 0.43051110f}, {-0.89687274f, 0.44228869f}, {-0.89100652f, 0.45399050f}, {-0.88498764f, 0.46561452f}, {-0.87881711f, 0.47715876f}, {-0.87249601f, 0.48862124f}, {-0.86602540f, 0.50000000f}, {-0.85940641f, 0.51129309f}, {-0.85264016f, 0.52249856f}, {-0.84572782f, 0.53361452f}, {-0.83867057f, 0.54463904f}, {-0.83146961f, 0.55557023f}, {-0.82412619f, 0.56640624f}, {-0.81664156f, 0.57714519f}, {-0.80901699f, 0.58778525f}, {-0.80125381f, 0.59832460f}, {-0.79335334f, 0.60876143f}, {-0.78531693f, 0.61909395f}, {-0.77714596f, 0.62932039f}, {-0.76884183f, 0.63943900f}, {-0.76040597f, 0.64944805f}, {-0.75183981f, 0.65934582f}, {-0.74314483f, 0.66913061f}, {-0.73432251f, 0.67880075f}, {-0.72537437f, 0.68835458f}, {-0.71630194f, 0.69779046f}, {-0.70710678f, 0.70710678f}, {-0.69779046f, 0.71630194f}, {-0.68835458f, 0.72537437f}, {-0.67880075f, 0.73432251f}, {-0.66913061f, 0.74314483f}, {-0.65934582f, 0.75183981f}, {-0.64944805f, 0.76040597f}, {-0.63943900f, 0.76884183f}, {-0.62932039f, 0.77714596f}, {-0.61909395f, 0.78531693f}, {-0.60876143f, 0.79335334f}, {-0.59832460f, 0.80125381f}, {-0.58778525f, 0.80901699f}, {-0.57714519f, 0.81664156f}, {-0.56640624f, 0.82412619f}, {-0.55557023f, 0.83146961f}, {-0.54463904f, 0.83867057f}, {-0.53361452f, 0.84572782f}, {-0.52249856f, 0.85264016f}, {-0.51129309f, 0.85940641f}, {-0.50000000f, 0.86602540f}, {-0.48862124f, 0.87249601f}, {-0.47715876f, 0.87881711f}, {-0.46561452f, 0.88498764f}, {-0.45399050f, 0.89100652f}, {-0.44228869f, 0.89687274f}, {-0.43051110f, 0.90258528f}, {-0.41865974f, 0.90814317f}, {-0.40673664f, 0.91354546f}, {-0.39474386f, 0.91879121f}, {-0.38268343f, 0.92387953f}, {-0.37055744f, 0.92880955f}, {-0.35836795f, 0.93358043f}, {-0.34611706f, 0.93819134f}, {-0.33380686f, 0.94264149f}, {-0.32143947f, 0.94693013f}, {-0.30901699f, 0.95105652f}, {-0.29654157f, 0.95501994f}, {-0.28401534f, 0.95881973f}, {-0.27144045f, 0.96245524f}, {-0.25881905f, 0.96592583f}, {-0.24615329f, 0.96923091f}, {-0.23344536f, 0.97236992f}, {-0.22069744f, 0.97534232f}, {-0.20791169f, 0.97814760f}, {-0.19509032f, 0.98078528f}, {-0.18223553f, 0.98325491f}, {-0.16934950f, 0.98555606f}, {-0.15643447f, 0.98768834f}, {-0.14349262f, 0.98965139f}, {-0.13052619f, 0.99144486f}, {-0.11753740f, 0.99306846f}, {-0.10452846f, 0.99452190f}, {-0.091501619f, 0.99580493f}, {-0.078459096f, 0.99691733f}, {-0.065403129f, 0.99785892f}, {-0.052335956f, 0.99862953f}, {-0.039259816f, 0.99922904f}, {-0.026176948f, 0.99965732f}, {-0.013089596f, 0.99991433f}, {-1.8369095e-16f, 1.0000000f}, {0.013089596f, 0.99991433f}, {0.026176948f, 0.99965732f}, {0.039259816f, 0.99922904f}, {0.052335956f, 0.99862953f}, {0.065403129f, 0.99785892f}, {0.078459096f, 0.99691733f}, {0.091501619f, 0.99580493f}, {0.10452846f, 0.99452190f}, {0.11753740f, 0.99306846f}, {0.13052619f, 0.99144486f}, {0.14349262f, 0.98965139f}, {0.15643447f, 0.98768834f}, {0.16934950f, 0.98555606f}, {0.18223553f, 0.98325491f}, {0.19509032f, 0.98078528f}, {0.20791169f, 0.97814760f}, {0.22069744f, 0.97534232f}, {0.23344536f, 0.97236992f}, {0.24615329f, 0.96923091f}, {0.25881905f, 0.96592583f}, {0.27144045f, 0.96245524f}, {0.28401534f, 0.95881973f}, {0.29654157f, 0.95501994f}, {0.30901699f, 0.95105652f}, {0.32143947f, 0.94693013f}, {0.33380686f, 0.94264149f}, {0.34611706f, 0.93819134f}, {0.35836795f, 0.93358043f}, {0.37055744f, 0.92880955f}, {0.38268343f, 0.92387953f}, {0.39474386f, 0.91879121f}, {0.40673664f, 0.91354546f}, {0.41865974f, 0.90814317f}, {0.43051110f, 0.90258528f}, {0.44228869f, 0.89687274f}, {0.45399050f, 0.89100652f}, {0.46561452f, 0.88498764f}, {0.47715876f, 0.87881711f}, {0.48862124f, 0.87249601f}, {0.50000000f, 0.86602540f}, {0.51129309f, 0.85940641f}, {0.52249856f, 0.85264016f}, {0.53361452f, 0.84572782f}, {0.54463904f, 0.83867057f}, {0.55557023f, 0.83146961f}, {0.56640624f, 0.82412619f}, {0.57714519f, 0.81664156f}, {0.58778525f, 0.80901699f}, {0.59832460f, 0.80125381f}, {0.60876143f, 0.79335334f}, {0.61909395f, 0.78531693f}, {0.62932039f, 0.77714596f}, {0.63943900f, 0.76884183f}, {0.64944805f, 0.76040597f}, {0.65934582f, 0.75183981f}, {0.66913061f, 0.74314483f}, {0.67880075f, 0.73432251f}, {0.68835458f, 0.72537437f}, {0.69779046f, 0.71630194f}, {0.70710678f, 0.70710678f}, {0.71630194f, 0.69779046f}, {0.72537437f, 0.68835458f}, {0.73432251f, 0.67880075f}, {0.74314483f, 0.66913061f}, {0.75183981f, 0.65934582f}, {0.76040597f, 0.64944805f}, {0.76884183f, 0.63943900f}, {0.77714596f, 0.62932039f}, {0.78531693f, 0.61909395f}, {0.79335334f, 0.60876143f}, {0.80125381f, 0.59832460f}, {0.80901699f, 0.58778525f}, {0.81664156f, 0.57714519f}, {0.82412619f, 0.56640624f}, {0.83146961f, 0.55557023f}, {0.83867057f, 0.54463904f}, {0.84572782f, 0.53361452f}, {0.85264016f, 0.52249856f}, {0.85940641f, 0.51129309f}, {0.86602540f, 0.50000000f}, {0.87249601f, 0.48862124f}, {0.87881711f, 0.47715876f}, {0.88498764f, 0.46561452f}, {0.89100652f, 0.45399050f}, {0.89687274f, 0.44228869f}, {0.90258528f, 0.43051110f}, {0.90814317f, 0.41865974f}, {0.91354546f, 0.40673664f}, {0.91879121f, 0.39474386f}, {0.92387953f, 0.38268343f}, {0.92880955f, 0.37055744f}, {0.93358043f, 0.35836795f}, {0.93819134f, 0.34611706f}, {0.94264149f, 0.33380686f}, {0.94693013f, 0.32143947f}, {0.95105652f, 0.30901699f}, {0.95501994f, 0.29654157f}, {0.95881973f, 0.28401534f}, {0.96245524f, 0.27144045f}, {0.96592583f, 0.25881905f}, {0.96923091f, 0.24615329f}, {0.97236992f, 0.23344536f}, {0.97534232f, 0.22069744f}, {0.97814760f, 0.20791169f}, {0.98078528f, 0.19509032f}, {0.98325491f, 0.18223553f}, {0.98555606f, 0.16934950f}, {0.98768834f, 0.15643447f}, {0.98965139f, 0.14349262f}, {0.99144486f, 0.13052619f}, {0.99306846f, 0.11753740f}, {0.99452190f, 0.10452846f}, {0.99580493f, 0.091501619f}, {0.99691733f, 0.078459096f}, {0.99785892f, 0.065403129f}, {0.99862953f, 0.052335956f}, {0.99922904f, 0.039259816f}, {0.99965732f, 0.026176948f}, {0.99991433f, 0.013089596f}, }; #ifndef FFT_BITREV480 #define FFT_BITREV480 static const opus_int16 fft_bitrev480[480] = { 0, 96, 192, 288, 384, 32, 128, 224, 320, 416, 64, 160, 256, 352, 448, 8, 104, 200, 296, 392, 40, 136, 232, 328, 424, 72, 168, 264, 360, 456, 16, 112, 208, 304, 400, 48, 144, 240, 336, 432, 80, 176, 272, 368, 464, 24, 120, 216, 312, 408, 56, 152, 248, 344, 440, 88, 184, 280, 376, 472, 4, 100, 196, 292, 388, 36, 132, 228, 324, 420, 68, 164, 260, 356, 452, 12, 108, 204, 300, 396, 44, 140, 236, 332, 428, 76, 172, 268, 364, 460, 20, 116, 212, 308, 404, 52, 148, 244, 340, 436, 84, 180, 276, 372, 468, 28, 124, 220, 316, 412, 60, 156, 252, 348, 444, 92, 188, 284, 380, 476, 1, 97, 193, 289, 385, 33, 129, 225, 321, 417, 65, 161, 257, 353, 449, 9, 105, 201, 297, 393, 41, 137, 233, 329, 425, 73, 169, 265, 361, 457, 17, 113, 209, 305, 401, 49, 145, 241, 337, 433, 81, 177, 273, 369, 465, 25, 121, 217, 313, 409, 57, 153, 249, 345, 441, 89, 185, 281, 377, 473, 5, 101, 197, 293, 389, 37, 133, 229, 325, 421, 69, 165, 261, 357, 453, 13, 109, 205, 301, 397, 45, 141, 237, 333, 429, 77, 173, 269, 365, 461, 21, 117, 213, 309, 405, 53, 149, 245, 341, 437, 85, 181, 277, 373, 469, 29, 125, 221, 317, 413, 61, 157, 253, 349, 445, 93, 189, 285, 381, 477, 2, 98, 194, 290, 386, 34, 130, 226, 322, 418, 66, 162, 258, 354, 450, 10, 106, 202, 298, 394, 42, 138, 234, 330, 426, 74, 170, 266, 362, 458, 18, 114, 210, 306, 402, 50, 146, 242, 338, 434, 82, 178, 274, 370, 466, 26, 122, 218, 314, 410, 58, 154, 250, 346, 442, 90, 186, 282, 378, 474, 6, 102, 198, 294, 390, 38, 134, 230, 326, 422, 70, 166, 262, 358, 454, 14, 110, 206, 302, 398, 46, 142, 238, 334, 430, 78, 174, 270, 366, 462, 22, 118, 214, 310, 406, 54, 150, 246, 342, 438, 86, 182, 278, 374, 470, 30, 126, 222, 318, 414, 62, 158, 254, 350, 446, 94, 190, 286, 382, 478, 3, 99, 195, 291, 387, 35, 131, 227, 323, 419, 67, 163, 259, 355, 451, 11, 107, 203, 299, 395, 43, 139, 235, 331, 427, 75, 171, 267, 363, 459, 19, 115, 211, 307, 403, 51, 147, 243, 339, 435, 83, 179, 275, 371, 467, 27, 123, 219, 315, 411, 59, 155, 251, 347, 443, 91, 187, 283, 379, 475, 7, 103, 199, 295, 391, 39, 135, 231, 327, 423, 71, 167, 263, 359, 455, 15, 111, 207, 303, 399, 47, 143, 239, 335, 431, 79, 175, 271, 367, 463, 23, 119, 215, 311, 407, 55, 151, 247, 343, 439, 87, 183, 279, 375, 471, 31, 127, 223, 319, 415, 63, 159, 255, 351, 447, 95, 191, 287, 383, 479, }; #endif #ifndef FFT_BITREV240 #define FFT_BITREV240 static const opus_int16 fft_bitrev240[240] = { 0, 48, 96, 144, 192, 16, 64, 112, 160, 208, 32, 80, 128, 176, 224, 4, 52, 100, 148, 196, 20, 68, 116, 164, 212, 36, 84, 132, 180, 228, 8, 56, 104, 152, 200, 24, 72, 120, 168, 216, 40, 88, 136, 184, 232, 12, 60, 108, 156, 204, 28, 76, 124, 172, 220, 44, 92, 140, 188, 236, 1, 49, 97, 145, 193, 17, 65, 113, 161, 209, 33, 81, 129, 177, 225, 5, 53, 101, 149, 197, 21, 69, 117, 165, 213, 37, 85, 133, 181, 229, 9, 57, 105, 153, 201, 25, 73, 121, 169, 217, 41, 89, 137, 185, 233, 13, 61, 109, 157, 205, 29, 77, 125, 173, 221, 45, 93, 141, 189, 237, 2, 50, 98, 146, 194, 18, 66, 114, 162, 210, 34, 82, 130, 178, 226, 6, 54, 102, 150, 198, 22, 70, 118, 166, 214, 38, 86, 134, 182, 230, 10, 58, 106, 154, 202, 26, 74, 122, 170, 218, 42, 90, 138, 186, 234, 14, 62, 110, 158, 206, 30, 78, 126, 174, 222, 46, 94, 142, 190, 238, 3, 51, 99, 147, 195, 19, 67, 115, 163, 211, 35, 83, 131, 179, 227, 7, 55, 103, 151, 199, 23, 71, 119, 167, 215, 39, 87, 135, 183, 231, 11, 59, 107, 155, 203, 27, 75, 123, 171, 219, 43, 91, 139, 187, 235, 15, 63, 111, 159, 207, 31, 79, 127, 175, 223, 47, 95, 143, 191, 239, }; #endif #ifndef FFT_BITREV120 #define FFT_BITREV120 static const opus_int16 fft_bitrev120[120] = { 0, 24, 48, 72, 96, 8, 32, 56, 80, 104, 16, 40, 64, 88, 112, 4, 28, 52, 76, 100, 12, 36, 60, 84, 108, 20, 44, 68, 92, 116, 1, 25, 49, 73, 97, 9, 33, 57, 81, 105, 17, 41, 65, 89, 113, 5, 29, 53, 77, 101, 13, 37, 61, 85, 109, 21, 45, 69, 93, 117, 2, 26, 50, 74, 98, 10, 34, 58, 82, 106, 18, 42, 66, 90, 114, 6, 30, 54, 78, 102, 14, 38, 62, 86, 110, 22, 46, 70, 94, 118, 3, 27, 51, 75, 99, 11, 35, 59, 83, 107, 19, 43, 67, 91, 115, 7, 31, 55, 79, 103, 15, 39, 63, 87, 111, 23, 47, 71, 95, 119, }; #endif #ifndef FFT_BITREV60 #define FFT_BITREV60 static const opus_int16 fft_bitrev60[60] = { 0, 12, 24, 36, 48, 4, 16, 28, 40, 52, 8, 20, 32, 44, 56, 1, 13, 25, 37, 49, 5, 17, 29, 41, 53, 9, 21, 33, 45, 57, 2, 14, 26, 38, 50, 6, 18, 30, 42, 54, 10, 22, 34, 46, 58, 3, 15, 27, 39, 51, 7, 19, 31, 43, 55, 11, 23, 35, 47, 59, }; #endif #ifndef FFT_STATE48000_960_0 #define FFT_STATE48000_960_0 static const kiss_fft_state fft_state48000_960_0 = { 480, /* nfft */ 0.002083333f, /* scale */ -1, /* shift */ {5, 96, 3, 32, 4, 8, 2, 4, 4, 1, 0, 0, 0, 0, 0, 0, }, /* factors */ fft_bitrev480, /* bitrev */ fft_twiddles48000_960, /* bitrev */ #ifdef OVERRIDE_FFT (arch_fft_state *)&cfg_arch_480, #else NULL, #endif }; #endif #ifndef FFT_STATE48000_960_1 #define FFT_STATE48000_960_1 static const kiss_fft_state fft_state48000_960_1 = { 240, /* nfft */ 0.004166667f, /* scale */ 1, /* shift */ {5, 48, 3, 16, 4, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, }, /* factors */ fft_bitrev240, /* bitrev */ fft_twiddles48000_960, /* bitrev */ #ifdef OVERRIDE_FFT (arch_fft_state *)&cfg_arch_240, #else NULL, #endif }; #endif #ifndef FFT_STATE48000_960_2 #define FFT_STATE48000_960_2 static const kiss_fft_state fft_state48000_960_2 = { 120, /* nfft */ 0.008333333f, /* scale */ 2, /* shift */ {5, 24, 3, 8, 2, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, }, /* factors */ fft_bitrev120, /* bitrev */ fft_twiddles48000_960, /* bitrev */ #ifdef OVERRIDE_FFT (arch_fft_state *)&cfg_arch_120, #else NULL, #endif }; #endif #ifndef FFT_STATE48000_960_3 #define FFT_STATE48000_960_3 static const kiss_fft_state fft_state48000_960_3 = { 60, /* nfft */ 0.016666667f, /* scale */ 3, /* shift */ {5, 12, 3, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, /* factors */ fft_bitrev60, /* bitrev */ fft_twiddles48000_960, /* bitrev */ #ifdef OVERRIDE_FFT (arch_fft_state *)&cfg_arch_60, #else NULL, #endif }; #endif #endif #ifndef MDCT_TWIDDLES960 #define MDCT_TWIDDLES960 static const opus_val16 mdct_twiddles960[1800] = { 0.99999994f, 0.99999321f, 0.99997580f, 0.99994773f, 0.99990886f, 0.99985933f, 0.99979913f, 0.99972820f, 0.99964654f, 0.99955416f, 0.99945110f, 0.99933738f, 0.99921292f, 0.99907774f, 0.99893188f, 0.99877530f, 0.99860805f, 0.99843007f, 0.99824142f, 0.99804211f, 0.99783206f, 0.99761140f, 0.99737996f, 0.99713790f, 0.99688518f, 0.99662173f, 0.99634761f, 0.99606287f, 0.99576741f, 0.99546129f, 0.99514455f, 0.99481714f, 0.99447906f, 0.99413031f, 0.99377096f, 0.99340093f, 0.99302030f, 0.99262899f, 0.99222708f, 0.99181455f, 0.99139136f, 0.99095762f, 0.99051321f, 0.99005818f, 0.98959261f, 0.98911643f, 0.98862964f, 0.98813224f, 0.98762429f, 0.98710573f, 0.98657662f, 0.98603696f, 0.98548669f, 0.98492593f, 0.98435456f, 0.98377270f, 0.98318028f, 0.98257732f, 0.98196387f, 0.98133987f, 0.98070538f, 0.98006040f, 0.97940493f, 0.97873890f, 0.97806245f, 0.97737551f, 0.97667813f, 0.97597027f, 0.97525197f, 0.97452319f, 0.97378403f, 0.97303438f, 0.97227436f, 0.97150391f, 0.97072303f, 0.96993178f, 0.96913016f, 0.96831810f, 0.96749574f, 0.96666300f, 0.96581990f, 0.96496642f, 0.96410263f, 0.96322852f, 0.96234411f, 0.96144938f, 0.96054435f, 0.95962906f, 0.95870346f, 0.95776761f, 0.95682150f, 0.95586514f, 0.95489854f, 0.95392174f, 0.95293468f, 0.95193744f, 0.95093000f, 0.94991243f, 0.94888461f, 0.94784665f, 0.94679856f, 0.94574034f, 0.94467193f, 0.94359344f, 0.94250488f, 0.94140619f, 0.94029742f, 0.93917859f, 0.93804967f, 0.93691075f, 0.93576175f, 0.93460274f, 0.93343377f, 0.93225473f, 0.93106574f, 0.92986679f, 0.92865789f, 0.92743903f, 0.92621022f, 0.92497152f, 0.92372292f, 0.92246443f, 0.92119598f, 0.91991776f, 0.91862965f, 0.91733170f, 0.91602397f, 0.91470635f, 0.91337901f, 0.91204184f, 0.91069490f, 0.90933824f, 0.90797186f, 0.90659571f, 0.90520984f, 0.90381432f, 0.90240908f, 0.90099424f, 0.89956969f, 0.89813554f, 0.89669174f, 0.89523834f, 0.89377540f, 0.89230281f, 0.89082074f, 0.88932908f, 0.88782793f, 0.88631725f, 0.88479710f, 0.88326746f, 0.88172835f, 0.88017982f, 0.87862182f, 0.87705445f, 0.87547767f, 0.87389153f, 0.87229604f, 0.87069118f, 0.86907703f, 0.86745358f, 0.86582077f, 0.86417878f, 0.86252749f, 0.86086690f, 0.85919720f, 0.85751826f, 0.85583007f, 0.85413277f, 0.85242635f, 0.85071075f, 0.84898609f, 0.84725231f, 0.84550947f, 0.84375757f, 0.84199661f, 0.84022665f, 0.83844769f, 0.83665979f, 0.83486289f, 0.83305705f, 0.83124226f, 0.82941860f, 0.82758605f, 0.82574469f, 0.82389444f, 0.82203537f, 0.82016748f, 0.81829083f, 0.81640542f, 0.81451124f, 0.81260836f, 0.81069672f, 0.80877650f, 0.80684757f, 0.80490994f, 0.80296379f, 0.80100900f, 0.79904562f, 0.79707366f, 0.79509324f, 0.79310423f, 0.79110676f, 0.78910083f, 0.78708643f, 0.78506362f, 0.78303236f, 0.78099275f, 0.77894479f, 0.77688843f, 0.77482378f, 0.77275085f, 0.77066964f, 0.76858020f, 0.76648247f, 0.76437658f, 0.76226246f, 0.76014024f, 0.75800985f, 0.75587130f, 0.75372469f, 0.75157005f, 0.74940729f, 0.74723655f, 0.74505776f, 0.74287105f, 0.74067634f, 0.73847371f, 0.73626316f, 0.73404479f, 0.73181850f, 0.72958434f, 0.72734243f, 0.72509271f, 0.72283524f, 0.72057003f, 0.71829706f, 0.71601641f, 0.71372813f, 0.71143216f, 0.70912862f, 0.70681745f, 0.70449871f, 0.70217246f, 0.69983864f, 0.69749737f, 0.69514859f, 0.69279242f, 0.69042879f, 0.68805778f, 0.68567938f, 0.68329364f, 0.68090063f, 0.67850029f, 0.67609268f, 0.67367786f, 0.67125577f, 0.66882652f, 0.66639012f, 0.66394657f, 0.66149592f, 0.65903819f, 0.65657341f, 0.65410155f, 0.65162271f, 0.64913690f, 0.64664418f, 0.64414448f, 0.64163786f, 0.63912445f, 0.63660413f, 0.63407701f, 0.63154310f, 0.62900239f, 0.62645501f, 0.62390089f, 0.62134010f, 0.61877263f, 0.61619854f, 0.61361790f, 0.61103064f, 0.60843682f, 0.60583651f, 0.60322970f, 0.60061646f, 0.59799677f, 0.59537065f, 0.59273821f, 0.59009939f, 0.58745426f, 0.58480281f, 0.58214509f, 0.57948118f, 0.57681108f, 0.57413477f, 0.57145232f, 0.56876373f, 0.56606907f, 0.56336832f, 0.56066155f, 0.55794877f, 0.55523002f, 0.55250537f, 0.54977477f, 0.54703826f, 0.54429591f, 0.54154772f, 0.53879374f, 0.53603399f, 0.53326851f, 0.53049731f, 0.52772039f, 0.52493787f, 0.52214974f, 0.51935595f, 0.51655668f, 0.51375180f, 0.51094145f, 0.50812566f, 0.50530440f, 0.50247771f, 0.49964568f, 0.49680826f, 0.49396557f, 0.49111754f, 0.48826426f, 0.48540577f, 0.48254207f, 0.47967321f, 0.47679919f, 0.47392011f, 0.47103590f, 0.46814668f, 0.46525243f, 0.46235323f, 0.45944905f, 0.45653993f, 0.45362595f, 0.45070711f, 0.44778344f, 0.44485497f, 0.44192174f, 0.43898380f, 0.43604112f, 0.43309379f, 0.43014181f, 0.42718524f, 0.42422408f, 0.42125839f, 0.41828820f, 0.41531351f, 0.41233435f, 0.40935081f, 0.40636289f, 0.40337059f, 0.40037400f, 0.39737311f, 0.39436796f, 0.39135858f, 0.38834500f, 0.38532731f, 0.38230544f, 0.37927949f, 0.37624949f, 0.37321547f, 0.37017745f, 0.36713544f, 0.36408952f, 0.36103970f, 0.35798600f, 0.35492846f, 0.35186714f, 0.34880206f, 0.34573323f, 0.34266070f, 0.33958447f, 0.33650464f, 0.33342120f, 0.33033419f, 0.32724363f, 0.32414958f, 0.32105204f, 0.31795108f, 0.31484672f, 0.31173897f, 0.30862790f, 0.30551350f, 0.30239585f, 0.29927495f, 0.29615086f, 0.29302359f, 0.28989318f, 0.28675964f, 0.28362307f, 0.28048345f, 0.27734083f, 0.27419522f, 0.27104670f, 0.26789525f, 0.26474094f, 0.26158381f, 0.25842386f, 0.25526115f, 0.25209570f, 0.24892756f, 0.24575676f, 0.24258332f, 0.23940729f, 0.23622867f, 0.23304754f, 0.22986393f, 0.22667783f, 0.22348931f, 0.22029841f, 0.21710514f, 0.21390954f, 0.21071166f, 0.20751151f, 0.20430915f, 0.20110460f, 0.19789790f, 0.19468907f, 0.19147816f, 0.18826519f, 0.18505022f, 0.18183327f, 0.17861435f, 0.17539354f, 0.17217083f, 0.16894630f, 0.16571994f, 0.16249183f, 0.15926196f, 0.15603039f, 0.15279715f, 0.14956227f, 0.14632578f, 0.14308774f, 0.13984816f, 0.13660708f, 0.13336454f, 0.13012058f, 0.12687522f, 0.12362850f, 0.12038045f, 0.11713112f, 0.11388054f, 0.11062872f, 0.10737573f, 0.10412160f, 0.10086634f, 0.097609997f, 0.094352618f, 0.091094226f, 0.087834857f, 0.084574550f, 0.081313334f, 0.078051247f, 0.074788325f, 0.071524605f, 0.068260118f, 0.064994894f, 0.061728980f, 0.058462404f, 0.055195201f, 0.051927410f, 0.048659060f, 0.045390189f, 0.042120833f, 0.038851023f, 0.035580799f, 0.032310195f, 0.029039243f, 0.025767982f, 0.022496443f, 0.019224664f, 0.015952680f, 0.012680525f, 0.0094082337f, 0.0061358409f, 0.0028633832f, -0.00040910527f, -0.0036815894f, -0.0069540343f, -0.010226404f, -0.013498665f, -0.016770782f, -0.020042717f, -0.023314439f, -0.026585912f, -0.029857099f, -0.033127967f, -0.036398482f, -0.039668605f, -0.042938303f, -0.046207540f, -0.049476285f, -0.052744497f, -0.056012146f, -0.059279196f, -0.062545612f, -0.065811358f, -0.069076397f, -0.072340697f, -0.075604223f, -0.078866936f, -0.082128808f, -0.085389800f, -0.088649876f, -0.091909006f, -0.095167145f, -0.098424271f, -0.10168034f, -0.10493532f, -0.10818918f, -0.11144188f, -0.11469338f, -0.11794366f, -0.12119267f, -0.12444039f, -0.12768677f, -0.13093179f, -0.13417540f, -0.13741758f, -0.14065829f, -0.14389749f, -0.14713514f, -0.15037122f, -0.15360570f, -0.15683852f, -0.16006967f, -0.16329910f, -0.16652679f, -0.16975269f, -0.17297678f, -0.17619900f, -0.17941935f, -0.18263777f, -0.18585424f, -0.18906870f, -0.19228116f, -0.19549155f, -0.19869985f, -0.20190603f, -0.20511003f, -0.20831184f, -0.21151142f, -0.21470875f, -0.21790376f, -0.22109644f, -0.22428675f, -0.22747467f, -0.23066014f, -0.23384315f, -0.23702365f, -0.24020162f, -0.24337701f, -0.24654980f, -0.24971995f, -0.25288740f, -0.25605217f, -0.25921419f, -0.26237345f, -0.26552987f, -0.26868346f, -0.27183419f, -0.27498198f, -0.27812684f, -0.28126872f, -0.28440759f, -0.28754342f, -0.29067615f, -0.29380578f, -0.29693225f, -0.30005556f, -0.30317566f, -0.30629250f, -0.30940607f, -0.31251630f, -0.31562322f, -0.31872672f, -0.32182685f, -0.32492352f, -0.32801670f, -0.33110636f, -0.33419248f, -0.33727503f, -0.34035397f, -0.34342924f, -0.34650084f, -0.34956875f, -0.35263291f, -0.35569328f, -0.35874987f, -0.36180258f, -0.36485144f, -0.36789638f, -0.37093741f, -0.37397444f, -0.37700745f, -0.38003644f, -0.38306138f, -0.38608220f, -0.38909888f, -0.39211139f, -0.39511973f, -0.39812380f, -0.40112361f, -0.40411916f, -0.40711036f, -0.41009718f, -0.41307965f, -0.41605768f, -0.41903123f, -0.42200032f, -0.42496487f, -0.42792490f, -0.43088034f, -0.43383113f, -0.43677729f, -0.43971881f, -0.44265559f, -0.44558764f, -0.44851488f, -0.45143735f, -0.45435500f, -0.45726776f, -0.46017563f, -0.46307856f, -0.46597654f, -0.46886954f, -0.47175750f, -0.47464043f, -0.47751826f, -0.48039100f, -0.48325855f, -0.48612097f, -0.48897815f, -0.49183011f, -0.49467680f, -0.49751821f, -0.50035429f, -0.50318497f, -0.50601029f, -0.50883019f, -0.51164466f, -0.51445359f, -0.51725709f, -0.52005500f, -0.52284735f, -0.52563411f, -0.52841520f, -0.53119069f, -0.53396046f, -0.53672451f, -0.53948283f, -0.54223537f, -0.54498214f, -0.54772300f, -0.55045801f, -0.55318713f, -0.55591035f, -0.55862761f, -0.56133890f, -0.56404412f, -0.56674337f, -0.56943649f, -0.57212353f, -0.57480448f, -0.57747924f, -0.58014780f, -0.58281022f, -0.58546633f, -0.58811617f, -0.59075975f, -0.59339696f, -0.59602785f, -0.59865236f, -0.60127044f, -0.60388207f, -0.60648727f, -0.60908598f, -0.61167812f, -0.61426371f, -0.61684275f, -0.61941516f, -0.62198097f, -0.62454009f, -0.62709254f, -0.62963831f, -0.63217729f, -0.63470948f, -0.63723493f, -0.63975352f, -0.64226526f, -0.64477009f, -0.64726806f, -0.64975911f, -0.65224314f, -0.65472025f, -0.65719032f, -0.65965337f, -0.66210932f, -0.66455823f, -0.66700000f, -0.66943461f, -0.67186207f, -0.67428231f, -0.67669535f, -0.67910111f, -0.68149966f, -0.68389088f, -0.68627477f, -0.68865126f, -0.69102043f, -0.69338220f, -0.69573659f, -0.69808346f, -0.70042288f, -0.70275480f, -0.70507920f, -0.70739603f, -0.70970529f, -0.71200693f, -0.71430099f, -0.71658736f, -0.71886611f, -0.72113711f, -0.72340041f, -0.72565591f, -0.72790372f, -0.73014367f, -0.73237586f, -0.73460019f, -0.73681659f, -0.73902518f, -0.74122584f, -0.74341851f, -0.74560326f, -0.74778003f, -0.74994880f, -0.75210953f, -0.75426215f, -0.75640678f, -0.75854325f, -0.76067162f, -0.76279181f, -0.76490390f, -0.76700771f, -0.76910341f, -0.77119076f, -0.77326995f, -0.77534080f, -0.77740335f, -0.77945763f, -0.78150350f, -0.78354102f, -0.78557014f, -0.78759086f, -0.78960317f, -0.79160696f, -0.79360235f, -0.79558921f, -0.79756755f, -0.79953730f, -0.80149853f, -0.80345118f, -0.80539525f, -0.80733067f, -0.80925739f, -0.81117553f, -0.81308490f, -0.81498563f, -0.81687760f, -0.81876087f, -0.82063532f, -0.82250100f, -0.82435787f, -0.82620591f, -0.82804507f, -0.82987541f, -0.83169687f, -0.83350939f, -0.83531296f, -0.83710766f, -0.83889335f, -0.84067005f, -0.84243774f, -0.84419644f, -0.84594607f, -0.84768665f, -0.84941816f, -0.85114056f, -0.85285389f, -0.85455805f, -0.85625303f, -0.85793889f, -0.85961550f, -0.86128294f, -0.86294121f, -0.86459017f, -0.86622989f, -0.86786032f, -0.86948150f, -0.87109333f, -0.87269586f, -0.87428904f, -0.87587279f, -0.87744725f, -0.87901229f, -0.88056785f, -0.88211405f, -0.88365078f, -0.88517809f, -0.88669586f, -0.88820416f, -0.88970292f, -0.89119220f, -0.89267188f, -0.89414203f, -0.89560264f, -0.89705360f, -0.89849502f, -0.89992678f, -0.90134889f, -0.90276134f, -0.90416414f, -0.90555727f, -0.90694070f, -0.90831441f, -0.90967834f, -0.91103262f, -0.91237706f, -0.91371179f, -0.91503674f, -0.91635185f, -0.91765714f, -0.91895264f, -0.92023826f, -0.92151409f, -0.92277998f, -0.92403603f, -0.92528218f, -0.92651838f, -0.92774469f, -0.92896110f, -0.93016750f, -0.93136400f, -0.93255049f, -0.93372697f, -0.93489349f, -0.93604994f, -0.93719643f, -0.93833286f, -0.93945926f, -0.94057560f, -0.94168180f, -0.94277799f, -0.94386405f, -0.94494003f, -0.94600588f, -0.94706154f, -0.94810712f, -0.94914252f, -0.95016778f, -0.95118284f, -0.95218778f, -0.95318246f, -0.95416695f, -0.95514119f, -0.95610523f, -0.95705903f, -0.95800257f, -0.95893586f, -0.95985889f, -0.96077162f, -0.96167403f, -0.96256620f, -0.96344805f, -0.96431959f, -0.96518075f, -0.96603161f, -0.96687216f, -0.96770233f, -0.96852213f, -0.96933156f, -0.97013056f, -0.97091925f, -0.97169751f, -0.97246534f, -0.97322279f, -0.97396982f, -0.97470641f, -0.97543252f, -0.97614825f, -0.97685349f, -0.97754824f, -0.97823256f, -0.97890645f, -0.97956979f, -0.98022264f, -0.98086500f, -0.98149687f, -0.98211825f, -0.98272908f, -0.98332942f, -0.98391914f, -0.98449844f, -0.98506713f, -0.98562527f, -0.98617285f, -0.98670989f, -0.98723638f, -0.98775226f, -0.98825759f, -0.98875231f, -0.98923647f, -0.98971003f, -0.99017298f, -0.99062532f, -0.99106705f, -0.99149817f, -0.99191868f, -0.99232858f, -0.99272782f, -0.99311644f, -0.99349445f, -0.99386179f, -0.99421853f, -0.99456459f, -0.99489999f, -0.99522477f, -0.99553883f, -0.99584228f, -0.99613506f, -0.99641716f, -0.99668860f, -0.99694937f, -0.99719942f, -0.99743885f, -0.99766755f, -0.99788558f, -0.99809295f, -0.99828959f, -0.99847561f, -0.99865085f, -0.99881548f, -0.99896932f, -0.99911255f, -0.99924499f, -0.99936682f, -0.99947786f, -0.99957830f, -0.99966794f, -0.99974692f, -0.99981517f, -0.99987274f, -0.99991959f, -0.99995571f, -0.99998116f, -0.99999589f, 0.99999964f, 0.99997288f, 0.99990326f, 0.99979085f, 0.99963558f, 0.99943751f, 0.99919659f, 0.99891287f, 0.99858636f, 0.99821711f, 0.99780506f, 0.99735034f, 0.99685282f, 0.99631262f, 0.99572974f, 0.99510419f, 0.99443603f, 0.99372530f, 0.99297196f, 0.99217612f, 0.99133772f, 0.99045694f, 0.98953366f, 0.98856801f, 0.98756003f, 0.98650974f, 0.98541719f, 0.98428243f, 0.98310548f, 0.98188645f, 0.98062533f, 0.97932225f, 0.97797716f, 0.97659022f, 0.97516143f, 0.97369087f, 0.97217858f, 0.97062469f, 0.96902919f, 0.96739221f, 0.96571374f, 0.96399397f, 0.96223283f, 0.96043050f, 0.95858705f, 0.95670253f, 0.95477700f, 0.95281059f, 0.95080340f, 0.94875544f, 0.94666684f, 0.94453770f, 0.94236809f, 0.94015813f, 0.93790787f, 0.93561745f, 0.93328691f, 0.93091643f, 0.92850608f, 0.92605597f, 0.92356616f, 0.92103678f, 0.91846794f, 0.91585976f, 0.91321236f, 0.91052586f, 0.90780038f, 0.90503591f, 0.90223277f, 0.89939094f, 0.89651060f, 0.89359182f, 0.89063478f, 0.88763964f, 0.88460642f, 0.88153529f, 0.87842643f, 0.87527996f, 0.87209594f, 0.86887461f, 0.86561602f, 0.86232042f, 0.85898781f, 0.85561842f, 0.85221243f, 0.84876984f, 0.84529096f, 0.84177583f, 0.83822471f, 0.83463764f, 0.83101481f, 0.82735640f, 0.82366252f, 0.81993335f, 0.81616908f, 0.81236988f, 0.80853581f, 0.80466717f, 0.80076402f, 0.79682660f, 0.79285502f, 0.78884947f, 0.78481019f, 0.78073722f, 0.77663082f, 0.77249116f, 0.76831841f, 0.76411277f, 0.75987434f, 0.75560343f, 0.75130010f, 0.74696463f, 0.74259710f, 0.73819780f, 0.73376691f, 0.72930455f, 0.72481096f, 0.72028631f, 0.71573079f, 0.71114463f, 0.70652801f, 0.70188117f, 0.69720417f, 0.69249737f, 0.68776089f, 0.68299496f, 0.67819971f, 0.67337549f, 0.66852236f, 0.66364062f, 0.65873051f, 0.65379208f, 0.64882571f, 0.64383155f, 0.63880974f, 0.63376063f, 0.62868434f, 0.62358117f, 0.61845124f, 0.61329484f, 0.60811216f, 0.60290343f, 0.59766883f, 0.59240872f, 0.58712316f, 0.58181250f, 0.57647687f, 0.57111657f, 0.56573176f, 0.56032276f, 0.55488980f, 0.54943299f, 0.54395270f, 0.53844911f, 0.53292239f, 0.52737290f, 0.52180082f, 0.51620632f, 0.51058978f, 0.50495136f, 0.49929130f, 0.49360985f, 0.48790723f, 0.48218375f, 0.47643960f, 0.47067502f, 0.46489030f, 0.45908567f, 0.45326138f, 0.44741765f, 0.44155475f, 0.43567297f, 0.42977250f, 0.42385364f, 0.41791660f, 0.41196167f, 0.40598908f, 0.39999911f, 0.39399201f, 0.38796803f, 0.38192743f, 0.37587047f, 0.36979741f, 0.36370850f, 0.35760403f, 0.35148421f, 0.34534934f, 0.33919969f, 0.33303553f, 0.32685706f, 0.32066461f, 0.31445843f, 0.30823877f, 0.30200592f, 0.29576012f, 0.28950164f, 0.28323078f, 0.27694780f, 0.27065292f, 0.26434645f, 0.25802869f, 0.25169984f, 0.24536023f, 0.23901010f, 0.23264973f, 0.22627939f, 0.21989937f, 0.21350993f, 0.20711134f, 0.20070387f, 0.19428782f, 0.18786344f, 0.18143101f, 0.17499080f, 0.16854310f, 0.16208819f, 0.15562633f, 0.14915779f, 0.14268288f, 0.13620184f, 0.12971498f, 0.12322257f, 0.11672486f, 0.11022217f, 0.10371475f, 0.097202882f, 0.090686858f, 0.084166944f, 0.077643424f, 0.071116582f, 0.064586692f, 0.058054037f, 0.051518895f, 0.044981543f, 0.038442269f, 0.031901345f, 0.025359053f, 0.018815678f, 0.012271495f, 0.0057267868f, -0.00081816671f, -0.0073630852f, -0.013907688f, -0.020451695f, -0.026994826f, -0.033536803f, -0.040077340f, -0.046616159f, -0.053152986f, -0.059687532f, -0.066219524f, -0.072748676f, -0.079274714f, -0.085797355f, -0.092316322f, -0.098831341f, -0.10534211f, -0.11184838f, -0.11834986f, -0.12484626f, -0.13133731f, -0.13782275f, -0.14430228f, -0.15077563f, -0.15724251f, -0.16370267f, -0.17015581f, -0.17660165f, -0.18303993f, -0.18947038f, -0.19589271f, -0.20230664f, -0.20871192f, -0.21510825f, -0.22149536f, -0.22787298f, -0.23424086f, -0.24059868f, -0.24694622f, -0.25328314f, -0.25960925f, -0.26592422f, -0.27222782f, -0.27851975f, -0.28479972f, -0.29106751f, -0.29732284f, -0.30356544f, -0.30979502f, -0.31601134f, -0.32221413f, -0.32840309f, -0.33457801f, -0.34073856f, -0.34688455f, -0.35301566f, -0.35913166f, -0.36523229f, -0.37131724f, -0.37738630f, -0.38343921f, -0.38947567f, -0.39549544f, -0.40149832f, -0.40748394f, -0.41345215f, -0.41940263f, -0.42533514f, -0.43124944f, -0.43714526f, -0.44302234f, -0.44888046f, -0.45471936f, -0.46053877f, -0.46633846f, -0.47211814f, -0.47787762f, -0.48361665f, -0.48933494f, -0.49503228f, -0.50070840f, -0.50636309f, -0.51199609f, -0.51760709f, -0.52319598f, -0.52876246f, -0.53430629f, -0.53982723f, -0.54532504f, -0.55079949f, -0.55625033f, -0.56167740f, -0.56708032f, -0.57245898f, -0.57781315f, -0.58314258f, -0.58844697f, -0.59372622f, -0.59897995f, -0.60420811f, -0.60941035f, -0.61458647f, -0.61973625f, -0.62485951f, -0.62995601f, -0.63502556f, -0.64006782f, -0.64508271f, -0.65007001f, -0.65502942f, -0.65996075f, -0.66486382f, -0.66973841f, -0.67458433f, -0.67940134f, -0.68418926f, -0.68894786f, -0.69367695f, -0.69837630f, -0.70304573f, -0.70768511f, -0.71229410f, -0.71687263f, -0.72142041f, -0.72593731f, -0.73042315f, -0.73487765f, -0.73930067f, -0.74369204f, -0.74805158f, -0.75237900f, -0.75667429f, -0.76093709f, -0.76516730f, -0.76936477f, -0.77352923f, -0.77766061f, -0.78175867f, -0.78582323f, -0.78985411f, -0.79385114f, -0.79781419f, -0.80174309f, -0.80563760f, -0.80949765f, -0.81332302f, -0.81711352f, -0.82086903f, -0.82458937f, -0.82827437f, -0.83192390f, -0.83553779f, -0.83911592f, -0.84265804f, -0.84616417f, -0.84963393f, -0.85306740f, -0.85646427f, -0.85982448f, -0.86314780f, -0.86643422f, -0.86968350f, -0.87289548f, -0.87607014f, -0.87920725f, -0.88230664f, -0.88536829f, -0.88839203f, -0.89137769f, -0.89432514f, -0.89723432f, -0.90010506f, -0.90293723f, -0.90573072f, -0.90848541f, -0.91120118f, -0.91387796f, -0.91651553f, -0.91911387f, -0.92167282f, -0.92419231f, -0.92667222f, -0.92911243f, -0.93151283f, -0.93387336f, -0.93619382f, -0.93847424f, -0.94071442f, -0.94291431f, -0.94507378f, -0.94719279f, -0.94927126f, -0.95130903f, -0.95330608f, -0.95526224f, -0.95717752f, -0.95905179f, -0.96088499f, -0.96267700f, -0.96442777f, -0.96613729f, -0.96780539f, -0.96943200f, -0.97101706f, -0.97256058f, -0.97406244f, -0.97552258f, -0.97694093f, -0.97831738f, -0.97965199f, -0.98094457f, -0.98219514f, -0.98340368f, -0.98457009f, -0.98569429f, -0.98677629f, -0.98781598f, -0.98881340f, -0.98976845f, -0.99068111f, -0.99155134f, -0.99237907f, -0.99316430f, -0.99390697f, -0.99460709f, -0.99526459f, -0.99587947f, -0.99645168f, -0.99698120f, -0.99746799f, -0.99791211f, -0.99831343f, -0.99867201f, -0.99898779f, -0.99926084f, -0.99949104f, -0.99967843f, -0.99982297f, -0.99992472f, -0.99998361f, 0.99999869f, 0.99989158f, 0.99961317f, 0.99916345f, 0.99854255f, 0.99775058f, 0.99678761f, 0.99565387f, 0.99434954f, 0.99287480f, 0.99122995f, 0.98941529f, 0.98743105f, 0.98527765f, 0.98295540f, 0.98046476f, 0.97780609f, 0.97497988f, 0.97198665f, 0.96882683f, 0.96550101f, 0.96200979f, 0.95835376f, 0.95453346f, 0.95054960f, 0.94640291f, 0.94209403f, 0.93762374f, 0.93299282f, 0.92820197f, 0.92325211f, 0.91814411f, 0.91287869f, 0.90745693f, 0.90187967f, 0.89614785f, 0.89026248f, 0.88422459f, 0.87803519f, 0.87169534f, 0.86520612f, 0.85856867f, 0.85178405f, 0.84485358f, 0.83777827f, 0.83055943f, 0.82319832f, 0.81569612f, 0.80805415f, 0.80027372f, 0.79235619f, 0.78430289f, 0.77611518f, 0.76779449f, 0.75934225f, 0.75075996f, 0.74204898f, 0.73321080f, 0.72424710f, 0.71515924f, 0.70594883f, 0.69661748f, 0.68716675f, 0.67759830f, 0.66791373f, 0.65811473f, 0.64820296f, 0.63818014f, 0.62804794f, 0.61780810f, 0.60746247f, 0.59701276f, 0.58646071f, 0.57580817f, 0.56505698f, 0.55420899f, 0.54326600f, 0.53222996f, 0.52110273f, 0.50988621f, 0.49858227f, 0.48719296f, 0.47572014f, 0.46416581f, 0.45253196f, 0.44082057f, 0.42903364f, 0.41717321f, 0.40524128f, 0.39323992f, 0.38117120f, 0.36903715f, 0.35683987f, 0.34458145f, 0.33226398f, 0.31988961f, 0.30746040f, 0.29497850f, 0.28244606f, 0.26986524f, 0.25723818f, 0.24456702f, 0.23185398f, 0.21910121f, 0.20631088f, 0.19348522f, 0.18062639f, 0.16773662f, 0.15481812f, 0.14187308f, 0.12890373f, 0.11591230f, 0.10290100f, 0.089872077f, 0.076827750f, 0.063770257f, 0.050701842f, 0.037624735f, 0.024541186f, 0.011453429f, -0.0016362892f, -0.014725727f, -0.027812643f, -0.040894791f, -0.053969935f, -0.067035832f, -0.080090240f, -0.093130924f, -0.10615565f, -0.11916219f, -0.13214831f, -0.14511178f, -0.15805040f, -0.17096193f, -0.18384418f, -0.19669491f, -0.20951195f, -0.22229309f, -0.23503613f, -0.24773891f, -0.26039925f, -0.27301496f, -0.28558388f, -0.29810387f, -0.31057280f, -0.32298848f, -0.33534884f, -0.34765175f, -0.35989508f, -0.37207675f, -0.38419467f, -0.39624676f, -0.40823093f, -0.42014518f, -0.43198743f, -0.44375566f, -0.45544785f, -0.46706200f, -0.47859612f, -0.49004826f, -0.50141639f, -0.51269865f, -0.52389306f, -0.53499764f, -0.54601061f, -0.55693001f, -0.56775403f, -0.57848072f, -0.58910829f, -0.59963489f, -0.61005878f, -0.62037814f, -0.63059121f, -0.64069623f, -0.65069145f, -0.66057515f, -0.67034572f, -0.68000144f, -0.68954057f, -0.69896162f, -0.70826286f, -0.71744281f, -0.72649974f, -0.73543227f, -0.74423873f, -0.75291771f, -0.76146764f, -0.76988715f, -0.77817470f, -0.78632891f, -0.79434842f, -0.80223179f, -0.80997771f, -0.81758487f, -0.82505190f, -0.83237761f, -0.83956063f, -0.84659988f, -0.85349399f, -0.86024189f, -0.86684239f, -0.87329435f, -0.87959671f, -0.88574833f, -0.89174819f, -0.89759529f, -0.90328854f, -0.90882701f, -0.91420978f, -0.91943592f, -0.92450452f, -0.92941469f, -0.93416560f, -0.93875647f, -0.94318646f, -0.94745487f, -0.95156091f, -0.95550388f, -0.95928317f, -0.96289814f, -0.96634805f, -0.96963239f, -0.97275060f, -0.97570217f, -0.97848648f, -0.98110318f, -0.98355180f, -0.98583186f, -0.98794299f, -0.98988485f, -0.99165714f, -0.99325943f, -0.99469161f, -0.99595332f, -0.99704438f, -0.99796462f, -0.99871385f, -0.99929196f, -0.99969882f, -0.99993443f, 0.99999464f, 0.99956632f, 0.99845290f, 0.99665523f, 0.99417448f, 0.99101239f, 0.98717111f, 0.98265326f, 0.97746199f, 0.97160077f, 0.96507365f, 0.95788515f, 0.95004016f, 0.94154406f, 0.93240267f, 0.92262226f, 0.91220951f, 0.90117162f, 0.88951606f, 0.87725091f, 0.86438453f, 0.85092574f, 0.83688372f, 0.82226819f, 0.80708915f, 0.79135692f, 0.77508235f, 0.75827658f, 0.74095112f, 0.72311783f, 0.70478898f, 0.68597710f, 0.66669506f, 0.64695615f, 0.62677377f, 0.60616189f, 0.58513457f, 0.56370622f, 0.54189157f, 0.51970547f, 0.49716324f, 0.47428027f, 0.45107225f, 0.42755505f, 0.40374488f, 0.37965798f, 0.35531086f, 0.33072025f, 0.30590299f, 0.28087607f, 0.25565663f, 0.23026201f, 0.20470956f, 0.17901683f, 0.15320139f, 0.12728097f, 0.10127331f, 0.075196236f, 0.049067631f, 0.022905400f, -0.0032725304f, -0.029448219f, -0.055603724f, -0.081721120f, -0.10778251f, -0.13377003f, -0.15966587f, -0.18545228f, -0.21111161f, -0.23662624f, -0.26197869f, -0.28715160f, -0.31212771f, -0.33688989f, -0.36142120f, -0.38570482f, -0.40972409f, -0.43346253f, -0.45690393f, -0.48003218f, -0.50283146f, -0.52528608f, -0.54738069f, -0.56910020f, -0.59042966f, -0.61135447f, -0.63186026f, -0.65193301f, -0.67155898f, -0.69072473f, -0.70941705f, -0.72762316f, -0.74533063f, -0.76252723f, -0.77920127f, -0.79534131f, -0.81093621f, -0.82597536f, -0.84044844f, -0.85434550f, -0.86765707f, -0.88037395f, -0.89248747f, -0.90398932f, -0.91487163f, -0.92512697f, -0.93474823f, -0.94372886f, -0.95206273f, -0.95974404f, -0.96676767f, -0.97312868f, -0.97882277f, -0.98384601f, -0.98819500f, -0.99186671f, -0.99485862f, -0.99716878f, -0.99879545f, -0.99973762f, }; #endif static const CELTMode mode48000_960_120 = { 48000, /* Fs */ 120, /* overlap */ 21, /* nbEBands */ 21, /* effEBands */ {0.85000610f, 0.0000000f, 1.0000000f, 1.0000000f, }, /* preemph */ eband5ms, /* eBands */ 3, /* maxLM */ 8, /* nbShortMdcts */ 120, /* shortMdctSize */ 11, /* nbAllocVectors */ band_allocation, /* allocVectors */ logN400, /* logN */ window120, /* window */ {1920, 3, {&fft_state48000_960_0, &fft_state48000_960_1, &fft_state48000_960_2, &fft_state48000_960_3, }, mdct_twiddles960}, /* mdct */ {392, cache_index50, cache_bits50, cache_caps50}, /* cache */ }; /* List of all the available modes */ #define TOTAL_MODES 1 static const CELTMode * const static_mode_list[TOTAL_MODES] = { &mode48000_960_120, }; ================================================ FILE: deps/pjsip/third_party/opus/celt/static_modes_float_arm_ne10.h ================================================ /* The contents of this file was automatically generated by * dump_mode_arm_ne10.c with arguments: 48000 960 * It contains static definitions for some pre-defined modes. */ #include #ifndef NE10_FFT_PARAMS48000_960 #define NE10_FFT_PARAMS48000_960 static const ne10_int32_t ne10_factors_480[64] = { 4, 40, 4, 30, 2, 15, 5, 3, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static const ne10_int32_t ne10_factors_240[64] = { 3, 20, 4, 15, 5, 3, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static const ne10_int32_t ne10_factors_120[64] = { 3, 10, 2, 15, 5, 3, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static const ne10_int32_t ne10_factors_60[64] = { 2, 5, 5, 3, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static const ne10_fft_cpx_float32_t ne10_twiddles_480[480] = { {1.0000000f,0.0000000f}, {1.0000000f,-0.0000000f}, {1.0000000f,-0.0000000f}, {1.0000000f,-0.0000000f}, {0.91354543f,-0.40673664f}, {0.66913056f,-0.74314487f}, {1.0000000f,-0.0000000f}, {0.66913056f,-0.74314487f}, {-0.10452851f,-0.99452192f}, {1.0000000f,-0.0000000f}, {0.30901697f,-0.95105654f}, {-0.80901700f,-0.58778518f}, {1.0000000f,-0.0000000f}, {-0.10452851f,-0.99452192f}, {-0.97814757f,0.20791179f}, {1.0000000f,-0.0000000f}, {0.97814763f,-0.20791170f}, {0.91354543f,-0.40673664f}, {0.80901700f,-0.58778524f}, {0.66913056f,-0.74314487f}, {0.49999997f,-0.86602545f}, {0.30901697f,-0.95105654f}, {0.10452842f,-0.99452192f}, {-0.10452851f,-0.99452192f}, {-0.30901703f,-0.95105648f}, {-0.50000006f,-0.86602533f}, {-0.66913068f,-0.74314475f}, {-0.80901700f,-0.58778518f}, {-0.91354549f,-0.40673658f}, {-0.97814763f,-0.20791161f}, {1.0000000f,-0.0000000f}, {0.99862951f,-0.052335959f}, {0.99452192f,-0.10452846f}, {0.98768836f,-0.15643448f}, {0.97814763f,-0.20791170f}, {0.96592581f,-0.25881904f}, {0.95105648f,-0.30901700f}, {0.93358040f,-0.35836795f}, {0.91354543f,-0.40673664f}, {0.89100653f,-0.45399052f}, {0.86602545f,-0.50000000f}, {0.83867055f,-0.54463905f}, {0.80901700f,-0.58778524f}, {0.77714598f,-0.62932038f}, {0.74314475f,-0.66913062f}, {0.70710677f,-0.70710683f}, {0.66913056f,-0.74314487f}, {0.62932038f,-0.77714598f}, {0.58778524f,-0.80901700f}, {0.54463899f,-0.83867055f}, {0.49999997f,-0.86602545f}, {0.45399052f,-0.89100653f}, {0.40673661f,-0.91354549f}, {0.35836786f,-0.93358046f}, {0.30901697f,-0.95105654f}, {0.25881907f,-0.96592581f}, {0.20791166f,-0.97814763f}, {0.15643437f,-0.98768836f}, {0.10452842f,-0.99452192f}, {0.052335974f,-0.99862951f}, {1.0000000f,-0.0000000f}, {0.99452192f,-0.10452846f}, {0.97814763f,-0.20791170f}, {0.95105648f,-0.30901700f}, {0.91354543f,-0.40673664f}, {0.86602545f,-0.50000000f}, {0.80901700f,-0.58778524f}, {0.74314475f,-0.66913062f}, {0.66913056f,-0.74314487f}, {0.58778524f,-0.80901700f}, {0.49999997f,-0.86602545f}, {0.40673661f,-0.91354549f}, {0.30901697f,-0.95105654f}, {0.20791166f,-0.97814763f}, {0.10452842f,-0.99452192f}, {-4.3711388e-08f,-1.0000000f}, {-0.10452851f,-0.99452192f}, {-0.20791174f,-0.97814757f}, {-0.30901703f,-0.95105648f}, {-0.40673670f,-0.91354543f}, {-0.50000006f,-0.86602533f}, {-0.58778518f,-0.80901700f}, {-0.66913068f,-0.74314475f}, {-0.74314493f,-0.66913044f}, {-0.80901700f,-0.58778518f}, {-0.86602539f,-0.50000006f}, {-0.91354549f,-0.40673658f}, {-0.95105654f,-0.30901679f}, {-0.97814763f,-0.20791161f}, {-0.99452192f,-0.10452849f}, {1.0000000f,-0.0000000f}, {0.98768836f,-0.15643448f}, {0.95105648f,-0.30901700f}, {0.89100653f,-0.45399052f}, {0.80901700f,-0.58778524f}, {0.70710677f,-0.70710683f}, {0.58778524f,-0.80901700f}, {0.45399052f,-0.89100653f}, {0.30901697f,-0.95105654f}, {0.15643437f,-0.98768836f}, {-4.3711388e-08f,-1.0000000f}, {-0.15643445f,-0.98768836f}, {-0.30901703f,-0.95105648f}, {-0.45399061f,-0.89100647f}, {-0.58778518f,-0.80901700f}, {-0.70710677f,-0.70710677f}, {-0.80901700f,-0.58778518f}, {-0.89100659f,-0.45399037f}, {-0.95105654f,-0.30901679f}, {-0.98768836f,-0.15643445f}, {-1.0000000f,8.7422777e-08f}, {-0.98768830f,0.15643461f}, {-0.95105654f,0.30901697f}, {-0.89100653f,0.45399055f}, {-0.80901694f,0.58778536f}, {-0.70710665f,0.70710689f}, {-0.58778507f,0.80901712f}, {-0.45399022f,0.89100665f}, {-0.30901709f,0.95105648f}, {-0.15643452f,0.98768830f}, {1.0000000f,-0.0000000f}, {0.99991435f,-0.013089596f}, {0.99965733f,-0.026176950f}, {0.99922901f,-0.039259817f}, {0.99862951f,-0.052335959f}, {0.99785894f,-0.065403134f}, {0.99691731f,-0.078459099f}, {0.99580491f,-0.091501623f}, {0.99452192f,-0.10452846f}, {0.99306846f,-0.11753740f}, {0.99144489f,-0.13052620f}, {0.98965138f,-0.14349262f}, {0.98768836f,-0.15643448f}, {0.98555607f,-0.16934951f}, {0.98325491f,-0.18223552f}, {0.98078525f,-0.19509032f}, {0.97814763f,-0.20791170f}, {0.97534233f,-0.22069745f}, {0.97236991f,-0.23344538f}, {0.96923089f,-0.24615330f}, {0.96592581f,-0.25881904f}, {0.96245521f,-0.27144045f}, {0.95881975f,-0.28401536f}, {0.95501995f,-0.29654160f}, {0.95105648f,-0.30901700f}, {0.94693011f,-0.32143945f}, {0.94264150f,-0.33380687f}, {0.93819129f,-0.34611708f}, {0.93358040f,-0.35836795f}, {0.92880952f,-0.37055743f}, {0.92387956f,-0.38268346f}, {0.91879117f,-0.39474389f}, {0.91354543f,-0.40673664f}, {0.90814316f,-0.41865975f}, {0.90258527f,-0.43051112f}, {0.89687270f,-0.44228873f}, {0.89100653f,-0.45399052f}, {0.88498765f,-0.46561453f}, {0.87881708f,-0.47715878f}, {0.87249601f,-0.48862126f}, {0.86602545f,-0.50000000f}, {0.85940641f,-0.51129311f}, {0.85264015f,-0.52249855f}, {0.84572786f,-0.53361452f}, {0.83867055f,-0.54463905f}, {0.83146960f,-0.55557024f}, {0.82412618f,-0.56640625f}, {0.81664151f,-0.57714522f}, {0.80901700f,-0.58778524f}, {0.80125380f,-0.59832460f}, {0.79335332f,-0.60876143f}, {0.78531694f,-0.61909395f}, {0.77714598f,-0.62932038f}, {0.76884180f,-0.63943899f}, {0.76040596f,-0.64944810f}, {0.75183982f,-0.65934587f}, {0.74314475f,-0.66913062f}, {0.73432249f,-0.67880076f}, {0.72537434f,-0.68835455f}, {0.71630192f,-0.69779050f}, {0.70710677f,-0.70710683f}, {0.69779044f,-0.71630198f}, {0.68835455f,-0.72537440f}, {0.67880070f,-0.73432255f}, {0.66913056f,-0.74314487f}, {0.65934581f,-0.75183982f}, {0.64944804f,-0.76040596f}, {0.63943899f,-0.76884186f}, {0.62932038f,-0.77714598f}, {0.61909395f,-0.78531694f}, {0.60876137f,-0.79335338f}, {0.59832460f,-0.80125386f}, {0.58778524f,-0.80901700f}, {0.57714516f,-0.81664151f}, {0.56640625f,-0.82412618f}, {0.55557019f,-0.83146960f}, {0.54463899f,-0.83867055f}, {0.53361452f,-0.84572786f}, {0.52249849f,-0.85264015f}, {0.51129311f,-0.85940641f}, {0.49999997f,-0.86602545f}, {0.48862118f,-0.87249601f}, {0.47715876f,-0.87881708f}, {0.46561447f,-0.88498765f}, {0.45399052f,-0.89100653f}, {0.44228867f,-0.89687276f}, {0.43051103f,-0.90258533f}, {0.41865975f,-0.90814316f}, {0.40673661f,-0.91354549f}, {0.39474380f,-0.91879129f}, {0.38268343f,-0.92387956f}, {0.37055740f,-0.92880958f}, {0.35836786f,-0.93358046f}, {0.34611705f,-0.93819135f}, {0.33380681f,-0.94264150f}, {0.32143947f,-0.94693011f}, {0.30901697f,-0.95105654f}, {0.29654151f,-0.95501995f}, {0.28401533f,-0.95881975f}, {0.27144039f,-0.96245527f}, {0.25881907f,-0.96592581f}, {0.24615327f,-0.96923089f}, {0.23344530f,-0.97236991f}, {0.22069745f,-0.97534233f}, {0.20791166f,-0.97814763f}, {0.19509023f,-0.98078531f}, {0.18223552f,-0.98325491f}, {0.16934945f,-0.98555607f}, {0.15643437f,-0.98768836f}, {0.14349259f,-0.98965138f}, {0.13052613f,-0.99144489f}, {0.11753740f,-0.99306846f}, {0.10452842f,-0.99452192f}, {0.091501534f,-0.99580491f}, {0.078459084f,-0.99691731f}, {0.065403074f,-0.99785894f}, {0.052335974f,-0.99862951f}, {0.039259788f,-0.99922901f}, {0.026176875f,-0.99965733f}, {0.013089597f,-0.99991435f}, {1.0000000f,-0.0000000f}, {0.99965733f,-0.026176950f}, {0.99862951f,-0.052335959f}, {0.99691731f,-0.078459099f}, {0.99452192f,-0.10452846f}, {0.99144489f,-0.13052620f}, {0.98768836f,-0.15643448f}, {0.98325491f,-0.18223552f}, {0.97814763f,-0.20791170f}, {0.97236991f,-0.23344538f}, {0.96592581f,-0.25881904f}, {0.95881975f,-0.28401536f}, {0.95105648f,-0.30901700f}, {0.94264150f,-0.33380687f}, {0.93358040f,-0.35836795f}, {0.92387956f,-0.38268346f}, {0.91354543f,-0.40673664f}, {0.90258527f,-0.43051112f}, {0.89100653f,-0.45399052f}, {0.87881708f,-0.47715878f}, {0.86602545f,-0.50000000f}, {0.85264015f,-0.52249855f}, {0.83867055f,-0.54463905f}, {0.82412618f,-0.56640625f}, {0.80901700f,-0.58778524f}, {0.79335332f,-0.60876143f}, {0.77714598f,-0.62932038f}, {0.76040596f,-0.64944810f}, {0.74314475f,-0.66913062f}, {0.72537434f,-0.68835455f}, {0.70710677f,-0.70710683f}, {0.68835455f,-0.72537440f}, {0.66913056f,-0.74314487f}, {0.64944804f,-0.76040596f}, {0.62932038f,-0.77714598f}, {0.60876137f,-0.79335338f}, {0.58778524f,-0.80901700f}, {0.56640625f,-0.82412618f}, {0.54463899f,-0.83867055f}, {0.52249849f,-0.85264015f}, {0.49999997f,-0.86602545f}, {0.47715876f,-0.87881708f}, {0.45399052f,-0.89100653f}, {0.43051103f,-0.90258533f}, {0.40673661f,-0.91354549f}, {0.38268343f,-0.92387956f}, {0.35836786f,-0.93358046f}, {0.33380681f,-0.94264150f}, {0.30901697f,-0.95105654f}, {0.28401533f,-0.95881975f}, {0.25881907f,-0.96592581f}, {0.23344530f,-0.97236991f}, {0.20791166f,-0.97814763f}, {0.18223552f,-0.98325491f}, {0.15643437f,-0.98768836f}, {0.13052613f,-0.99144489f}, {0.10452842f,-0.99452192f}, {0.078459084f,-0.99691731f}, {0.052335974f,-0.99862951f}, {0.026176875f,-0.99965733f}, {-4.3711388e-08f,-1.0000000f}, {-0.026176963f,-0.99965733f}, {-0.052336060f,-0.99862951f}, {-0.078459173f,-0.99691731f}, {-0.10452851f,-0.99452192f}, {-0.13052621f,-0.99144489f}, {-0.15643445f,-0.98768836f}, {-0.18223560f,-0.98325491f}, {-0.20791174f,-0.97814757f}, {-0.23344538f,-0.97236991f}, {-0.25881916f,-0.96592581f}, {-0.28401542f,-0.95881969f}, {-0.30901703f,-0.95105648f}, {-0.33380687f,-0.94264150f}, {-0.35836795f,-0.93358040f}, {-0.38268352f,-0.92387950f}, {-0.40673670f,-0.91354543f}, {-0.43051112f,-0.90258527f}, {-0.45399061f,-0.89100647f}, {-0.47715873f,-0.87881708f}, {-0.50000006f,-0.86602533f}, {-0.52249867f,-0.85264009f}, {-0.54463905f,-0.83867055f}, {-0.56640631f,-0.82412612f}, {-0.58778518f,-0.80901700f}, {-0.60876143f,-0.79335332f}, {-0.62932050f,-0.77714586f}, {-0.64944804f,-0.76040596f}, {-0.66913068f,-0.74314475f}, {-0.68835467f,-0.72537428f}, {-0.70710677f,-0.70710677f}, {-0.72537446f,-0.68835449f}, {-0.74314493f,-0.66913044f}, {-0.76040596f,-0.64944804f}, {-0.77714604f,-0.62932026f}, {-0.79335332f,-0.60876143f}, {-0.80901700f,-0.58778518f}, {-0.82412624f,-0.56640613f}, {-0.83867055f,-0.54463899f}, {-0.85264021f,-0.52249849f}, {-0.86602539f,-0.50000006f}, {-0.87881714f,-0.47715873f}, {-0.89100659f,-0.45399037f}, {-0.90258527f,-0.43051112f}, {-0.91354549f,-0.40673658f}, {-0.92387956f,-0.38268328f}, {-0.93358040f,-0.35836792f}, {-0.94264150f,-0.33380675f}, {-0.95105654f,-0.30901679f}, {-0.95881975f,-0.28401530f}, {-0.96592587f,-0.25881892f}, {-0.97236991f,-0.23344538f}, {-0.97814763f,-0.20791161f}, {-0.98325491f,-0.18223536f}, {-0.98768836f,-0.15643445f}, {-0.99144489f,-0.13052608f}, {-0.99452192f,-0.10452849f}, {-0.99691737f,-0.078459039f}, {-0.99862957f,-0.052335810f}, {-0.99965733f,-0.026176952f}, {1.0000000f,-0.0000000f}, {0.99922901f,-0.039259817f}, {0.99691731f,-0.078459099f}, {0.99306846f,-0.11753740f}, {0.98768836f,-0.15643448f}, {0.98078525f,-0.19509032f}, {0.97236991f,-0.23344538f}, {0.96245521f,-0.27144045f}, {0.95105648f,-0.30901700f}, {0.93819129f,-0.34611708f}, {0.92387956f,-0.38268346f}, {0.90814316f,-0.41865975f}, {0.89100653f,-0.45399052f}, {0.87249601f,-0.48862126f}, {0.85264015f,-0.52249855f}, {0.83146960f,-0.55557024f}, {0.80901700f,-0.58778524f}, {0.78531694f,-0.61909395f}, {0.76040596f,-0.64944810f}, {0.73432249f,-0.67880076f}, {0.70710677f,-0.70710683f}, {0.67880070f,-0.73432255f}, {0.64944804f,-0.76040596f}, {0.61909395f,-0.78531694f}, {0.58778524f,-0.80901700f}, {0.55557019f,-0.83146960f}, {0.52249849f,-0.85264015f}, {0.48862118f,-0.87249601f}, {0.45399052f,-0.89100653f}, {0.41865975f,-0.90814316f}, {0.38268343f,-0.92387956f}, {0.34611705f,-0.93819135f}, {0.30901697f,-0.95105654f}, {0.27144039f,-0.96245527f}, {0.23344530f,-0.97236991f}, {0.19509023f,-0.98078531f}, {0.15643437f,-0.98768836f}, {0.11753740f,-0.99306846f}, {0.078459084f,-0.99691731f}, {0.039259788f,-0.99922901f}, {-4.3711388e-08f,-1.0000000f}, {-0.039259877f,-0.99922901f}, {-0.078459173f,-0.99691731f}, {-0.11753749f,-0.99306846f}, {-0.15643445f,-0.98768836f}, {-0.19509032f,-0.98078525f}, {-0.23344538f,-0.97236991f}, {-0.27144048f,-0.96245521f}, {-0.30901703f,-0.95105648f}, {-0.34611711f,-0.93819129f}, {-0.38268352f,-0.92387950f}, {-0.41865984f,-0.90814310f}, {-0.45399061f,-0.89100647f}, {-0.48862135f,-0.87249595f}, {-0.52249867f,-0.85264009f}, {-0.55557036f,-0.83146954f}, {-0.58778518f,-0.80901700f}, {-0.61909389f,-0.78531694f}, {-0.64944804f,-0.76040596f}, {-0.67880076f,-0.73432249f}, {-0.70710677f,-0.70710677f}, {-0.73432249f,-0.67880070f}, {-0.76040596f,-0.64944804f}, {-0.78531694f,-0.61909389f}, {-0.80901700f,-0.58778518f}, {-0.83146966f,-0.55557019f}, {-0.85264021f,-0.52249849f}, {-0.87249607f,-0.48862115f}, {-0.89100659f,-0.45399037f}, {-0.90814322f,-0.41865960f}, {-0.92387956f,-0.38268328f}, {-0.93819135f,-0.34611690f}, {-0.95105654f,-0.30901679f}, {-0.96245521f,-0.27144048f}, {-0.97236991f,-0.23344538f}, {-0.98078531f,-0.19509031f}, {-0.98768836f,-0.15643445f}, {-0.99306846f,-0.11753736f}, {-0.99691737f,-0.078459039f}, {-0.99922901f,-0.039259743f}, {-1.0000000f,8.7422777e-08f}, {-0.99922901f,0.039259918f}, {-0.99691731f,0.078459218f}, {-0.99306846f,0.11753753f}, {-0.98768830f,0.15643461f}, {-0.98078525f,0.19509049f}, {-0.97236985f,0.23344554f}, {-0.96245515f,0.27144065f}, {-0.95105654f,0.30901697f}, {-0.93819135f,0.34611705f}, {-0.92387956f,0.38268346f}, {-0.90814316f,0.41865975f}, {-0.89100653f,0.45399055f}, {-0.87249601f,0.48862129f}, {-0.85264015f,0.52249861f}, {-0.83146960f,0.55557030f}, {-0.80901694f,0.58778536f}, {-0.78531688f,0.61909401f}, {-0.76040590f,0.64944816f}, {-0.73432243f,0.67880082f}, {-0.70710665f,0.70710689f}, {-0.67880058f,0.73432261f}, {-0.64944792f,0.76040608f}, {-0.61909378f,0.78531706f}, {-0.58778507f,0.80901712f}, {-0.55557001f,0.83146977f}, {-0.52249837f,0.85264033f}, {-0.48862100f,0.87249613f}, {-0.45399022f,0.89100665f}, {-0.41865945f,0.90814328f}, {-0.38268313f,0.92387968f}, {-0.34611672f,0.93819147f}, {-0.30901709f,0.95105648f}, {-0.27144054f,0.96245521f}, {-0.23344545f,0.97236991f}, {-0.19509038f,0.98078525f}, {-0.15643452f,0.98768830f}, {-0.11753743f,0.99306846f}, {-0.078459114f,0.99691731f}, {-0.039259821f,0.99922901f}, }; static const ne10_fft_cpx_float32_t ne10_twiddles_240[240] = { {1.0000000f,0.0000000f}, {1.0000000f,-0.0000000f}, {1.0000000f,-0.0000000f}, {1.0000000f,-0.0000000f}, {0.91354543f,-0.40673664f}, {0.66913056f,-0.74314487f}, {1.0000000f,-0.0000000f}, {0.66913056f,-0.74314487f}, {-0.10452851f,-0.99452192f}, {1.0000000f,-0.0000000f}, {0.30901697f,-0.95105654f}, {-0.80901700f,-0.58778518f}, {1.0000000f,-0.0000000f}, {-0.10452851f,-0.99452192f}, {-0.97814757f,0.20791179f}, {1.0000000f,-0.0000000f}, {0.99452192f,-0.10452846f}, {0.97814763f,-0.20791170f}, {0.95105648f,-0.30901700f}, {0.91354543f,-0.40673664f}, {0.86602545f,-0.50000000f}, {0.80901700f,-0.58778524f}, {0.74314475f,-0.66913062f}, {0.66913056f,-0.74314487f}, {0.58778524f,-0.80901700f}, {0.49999997f,-0.86602545f}, {0.40673661f,-0.91354549f}, {0.30901697f,-0.95105654f}, {0.20791166f,-0.97814763f}, {0.10452842f,-0.99452192f}, {1.0000000f,-0.0000000f}, {0.97814763f,-0.20791170f}, {0.91354543f,-0.40673664f}, {0.80901700f,-0.58778524f}, {0.66913056f,-0.74314487f}, {0.49999997f,-0.86602545f}, {0.30901697f,-0.95105654f}, {0.10452842f,-0.99452192f}, {-0.10452851f,-0.99452192f}, {-0.30901703f,-0.95105648f}, {-0.50000006f,-0.86602533f}, {-0.66913068f,-0.74314475f}, {-0.80901700f,-0.58778518f}, {-0.91354549f,-0.40673658f}, {-0.97814763f,-0.20791161f}, {1.0000000f,-0.0000000f}, {0.95105648f,-0.30901700f}, {0.80901700f,-0.58778524f}, {0.58778524f,-0.80901700f}, {0.30901697f,-0.95105654f}, {-4.3711388e-08f,-1.0000000f}, {-0.30901703f,-0.95105648f}, {-0.58778518f,-0.80901700f}, {-0.80901700f,-0.58778518f}, {-0.95105654f,-0.30901679f}, {-1.0000000f,8.7422777e-08f}, {-0.95105654f,0.30901697f}, {-0.80901694f,0.58778536f}, {-0.58778507f,0.80901712f}, {-0.30901709f,0.95105648f}, {1.0000000f,-0.0000000f}, {0.99965733f,-0.026176950f}, {0.99862951f,-0.052335959f}, {0.99691731f,-0.078459099f}, {0.99452192f,-0.10452846f}, {0.99144489f,-0.13052620f}, {0.98768836f,-0.15643448f}, {0.98325491f,-0.18223552f}, {0.97814763f,-0.20791170f}, {0.97236991f,-0.23344538f}, {0.96592581f,-0.25881904f}, {0.95881975f,-0.28401536f}, {0.95105648f,-0.30901700f}, {0.94264150f,-0.33380687f}, {0.93358040f,-0.35836795f}, {0.92387956f,-0.38268346f}, {0.91354543f,-0.40673664f}, {0.90258527f,-0.43051112f}, {0.89100653f,-0.45399052f}, {0.87881708f,-0.47715878f}, {0.86602545f,-0.50000000f}, {0.85264015f,-0.52249855f}, {0.83867055f,-0.54463905f}, {0.82412618f,-0.56640625f}, {0.80901700f,-0.58778524f}, {0.79335332f,-0.60876143f}, {0.77714598f,-0.62932038f}, {0.76040596f,-0.64944810f}, {0.74314475f,-0.66913062f}, {0.72537434f,-0.68835455f}, {0.70710677f,-0.70710683f}, {0.68835455f,-0.72537440f}, {0.66913056f,-0.74314487f}, {0.64944804f,-0.76040596f}, {0.62932038f,-0.77714598f}, {0.60876137f,-0.79335338f}, {0.58778524f,-0.80901700f}, {0.56640625f,-0.82412618f}, {0.54463899f,-0.83867055f}, {0.52249849f,-0.85264015f}, {0.49999997f,-0.86602545f}, {0.47715876f,-0.87881708f}, {0.45399052f,-0.89100653f}, {0.43051103f,-0.90258533f}, {0.40673661f,-0.91354549f}, {0.38268343f,-0.92387956f}, {0.35836786f,-0.93358046f}, {0.33380681f,-0.94264150f}, {0.30901697f,-0.95105654f}, {0.28401533f,-0.95881975f}, {0.25881907f,-0.96592581f}, {0.23344530f,-0.97236991f}, {0.20791166f,-0.97814763f}, {0.18223552f,-0.98325491f}, {0.15643437f,-0.98768836f}, {0.13052613f,-0.99144489f}, {0.10452842f,-0.99452192f}, {0.078459084f,-0.99691731f}, {0.052335974f,-0.99862951f}, {0.026176875f,-0.99965733f}, {1.0000000f,-0.0000000f}, {0.99862951f,-0.052335959f}, {0.99452192f,-0.10452846f}, {0.98768836f,-0.15643448f}, {0.97814763f,-0.20791170f}, {0.96592581f,-0.25881904f}, {0.95105648f,-0.30901700f}, {0.93358040f,-0.35836795f}, {0.91354543f,-0.40673664f}, {0.89100653f,-0.45399052f}, {0.86602545f,-0.50000000f}, {0.83867055f,-0.54463905f}, {0.80901700f,-0.58778524f}, {0.77714598f,-0.62932038f}, {0.74314475f,-0.66913062f}, {0.70710677f,-0.70710683f}, {0.66913056f,-0.74314487f}, {0.62932038f,-0.77714598f}, {0.58778524f,-0.80901700f}, {0.54463899f,-0.83867055f}, {0.49999997f,-0.86602545f}, {0.45399052f,-0.89100653f}, {0.40673661f,-0.91354549f}, {0.35836786f,-0.93358046f}, {0.30901697f,-0.95105654f}, {0.25881907f,-0.96592581f}, {0.20791166f,-0.97814763f}, {0.15643437f,-0.98768836f}, {0.10452842f,-0.99452192f}, {0.052335974f,-0.99862951f}, {-4.3711388e-08f,-1.0000000f}, {-0.052336060f,-0.99862951f}, {-0.10452851f,-0.99452192f}, {-0.15643445f,-0.98768836f}, {-0.20791174f,-0.97814757f}, {-0.25881916f,-0.96592581f}, {-0.30901703f,-0.95105648f}, {-0.35836795f,-0.93358040f}, {-0.40673670f,-0.91354543f}, {-0.45399061f,-0.89100647f}, {-0.50000006f,-0.86602533f}, {-0.54463905f,-0.83867055f}, {-0.58778518f,-0.80901700f}, {-0.62932050f,-0.77714586f}, {-0.66913068f,-0.74314475f}, {-0.70710677f,-0.70710677f}, {-0.74314493f,-0.66913044f}, {-0.77714604f,-0.62932026f}, {-0.80901700f,-0.58778518f}, {-0.83867055f,-0.54463899f}, {-0.86602539f,-0.50000006f}, {-0.89100659f,-0.45399037f}, {-0.91354549f,-0.40673658f}, {-0.93358040f,-0.35836792f}, {-0.95105654f,-0.30901679f}, {-0.96592587f,-0.25881892f}, {-0.97814763f,-0.20791161f}, {-0.98768836f,-0.15643445f}, {-0.99452192f,-0.10452849f}, {-0.99862957f,-0.052335810f}, {1.0000000f,-0.0000000f}, {0.99691731f,-0.078459099f}, {0.98768836f,-0.15643448f}, {0.97236991f,-0.23344538f}, {0.95105648f,-0.30901700f}, {0.92387956f,-0.38268346f}, {0.89100653f,-0.45399052f}, {0.85264015f,-0.52249855f}, {0.80901700f,-0.58778524f}, {0.76040596f,-0.64944810f}, {0.70710677f,-0.70710683f}, {0.64944804f,-0.76040596f}, {0.58778524f,-0.80901700f}, {0.52249849f,-0.85264015f}, {0.45399052f,-0.89100653f}, {0.38268343f,-0.92387956f}, {0.30901697f,-0.95105654f}, {0.23344530f,-0.97236991f}, {0.15643437f,-0.98768836f}, {0.078459084f,-0.99691731f}, {-4.3711388e-08f,-1.0000000f}, {-0.078459173f,-0.99691731f}, {-0.15643445f,-0.98768836f}, {-0.23344538f,-0.97236991f}, {-0.30901703f,-0.95105648f}, {-0.38268352f,-0.92387950f}, {-0.45399061f,-0.89100647f}, {-0.52249867f,-0.85264009f}, {-0.58778518f,-0.80901700f}, {-0.64944804f,-0.76040596f}, {-0.70710677f,-0.70710677f}, {-0.76040596f,-0.64944804f}, {-0.80901700f,-0.58778518f}, {-0.85264021f,-0.52249849f}, {-0.89100659f,-0.45399037f}, {-0.92387956f,-0.38268328f}, {-0.95105654f,-0.30901679f}, {-0.97236991f,-0.23344538f}, {-0.98768836f,-0.15643445f}, {-0.99691737f,-0.078459039f}, {-1.0000000f,8.7422777e-08f}, {-0.99691731f,0.078459218f}, {-0.98768830f,0.15643461f}, {-0.97236985f,0.23344554f}, {-0.95105654f,0.30901697f}, {-0.92387956f,0.38268346f}, {-0.89100653f,0.45399055f}, {-0.85264015f,0.52249861f}, {-0.80901694f,0.58778536f}, {-0.76040590f,0.64944816f}, {-0.70710665f,0.70710689f}, {-0.64944792f,0.76040608f}, {-0.58778507f,0.80901712f}, {-0.52249837f,0.85264033f}, {-0.45399022f,0.89100665f}, {-0.38268313f,0.92387968f}, {-0.30901709f,0.95105648f}, {-0.23344545f,0.97236991f}, {-0.15643452f,0.98768830f}, {-0.078459114f,0.99691731f}, }; static const ne10_fft_cpx_float32_t ne10_twiddles_120[120] = { {1.0000000f,0.0000000f}, {1.0000000f,-0.0000000f}, {1.0000000f,-0.0000000f}, {1.0000000f,-0.0000000f}, {0.91354543f,-0.40673664f}, {0.66913056f,-0.74314487f}, {1.0000000f,-0.0000000f}, {0.66913056f,-0.74314487f}, {-0.10452851f,-0.99452192f}, {1.0000000f,-0.0000000f}, {0.30901697f,-0.95105654f}, {-0.80901700f,-0.58778518f}, {1.0000000f,-0.0000000f}, {-0.10452851f,-0.99452192f}, {-0.97814757f,0.20791179f}, {1.0000000f,-0.0000000f}, {0.97814763f,-0.20791170f}, {0.91354543f,-0.40673664f}, {0.80901700f,-0.58778524f}, {0.66913056f,-0.74314487f}, {0.49999997f,-0.86602545f}, {0.30901697f,-0.95105654f}, {0.10452842f,-0.99452192f}, {-0.10452851f,-0.99452192f}, {-0.30901703f,-0.95105648f}, {-0.50000006f,-0.86602533f}, {-0.66913068f,-0.74314475f}, {-0.80901700f,-0.58778518f}, {-0.91354549f,-0.40673658f}, {-0.97814763f,-0.20791161f}, {1.0000000f,-0.0000000f}, {0.99862951f,-0.052335959f}, {0.99452192f,-0.10452846f}, {0.98768836f,-0.15643448f}, {0.97814763f,-0.20791170f}, {0.96592581f,-0.25881904f}, {0.95105648f,-0.30901700f}, {0.93358040f,-0.35836795f}, {0.91354543f,-0.40673664f}, {0.89100653f,-0.45399052f}, {0.86602545f,-0.50000000f}, {0.83867055f,-0.54463905f}, {0.80901700f,-0.58778524f}, {0.77714598f,-0.62932038f}, {0.74314475f,-0.66913062f}, {0.70710677f,-0.70710683f}, {0.66913056f,-0.74314487f}, {0.62932038f,-0.77714598f}, {0.58778524f,-0.80901700f}, {0.54463899f,-0.83867055f}, {0.49999997f,-0.86602545f}, {0.45399052f,-0.89100653f}, {0.40673661f,-0.91354549f}, {0.35836786f,-0.93358046f}, {0.30901697f,-0.95105654f}, {0.25881907f,-0.96592581f}, {0.20791166f,-0.97814763f}, {0.15643437f,-0.98768836f}, {0.10452842f,-0.99452192f}, {0.052335974f,-0.99862951f}, {1.0000000f,-0.0000000f}, {0.99452192f,-0.10452846f}, {0.97814763f,-0.20791170f}, {0.95105648f,-0.30901700f}, {0.91354543f,-0.40673664f}, {0.86602545f,-0.50000000f}, {0.80901700f,-0.58778524f}, {0.74314475f,-0.66913062f}, {0.66913056f,-0.74314487f}, {0.58778524f,-0.80901700f}, {0.49999997f,-0.86602545f}, {0.40673661f,-0.91354549f}, {0.30901697f,-0.95105654f}, {0.20791166f,-0.97814763f}, {0.10452842f,-0.99452192f}, {-4.3711388e-08f,-1.0000000f}, {-0.10452851f,-0.99452192f}, {-0.20791174f,-0.97814757f}, {-0.30901703f,-0.95105648f}, {-0.40673670f,-0.91354543f}, {-0.50000006f,-0.86602533f}, {-0.58778518f,-0.80901700f}, {-0.66913068f,-0.74314475f}, {-0.74314493f,-0.66913044f}, {-0.80901700f,-0.58778518f}, {-0.86602539f,-0.50000006f}, {-0.91354549f,-0.40673658f}, {-0.95105654f,-0.30901679f}, {-0.97814763f,-0.20791161f}, {-0.99452192f,-0.10452849f}, {1.0000000f,-0.0000000f}, {0.98768836f,-0.15643448f}, {0.95105648f,-0.30901700f}, {0.89100653f,-0.45399052f}, {0.80901700f,-0.58778524f}, {0.70710677f,-0.70710683f}, {0.58778524f,-0.80901700f}, {0.45399052f,-0.89100653f}, {0.30901697f,-0.95105654f}, {0.15643437f,-0.98768836f}, {-4.3711388e-08f,-1.0000000f}, {-0.15643445f,-0.98768836f}, {-0.30901703f,-0.95105648f}, {-0.45399061f,-0.89100647f}, {-0.58778518f,-0.80901700f}, {-0.70710677f,-0.70710677f}, {-0.80901700f,-0.58778518f}, {-0.89100659f,-0.45399037f}, {-0.95105654f,-0.30901679f}, {-0.98768836f,-0.15643445f}, {-1.0000000f,8.7422777e-08f}, {-0.98768830f,0.15643461f}, {-0.95105654f,0.30901697f}, {-0.89100653f,0.45399055f}, {-0.80901694f,0.58778536f}, {-0.70710665f,0.70710689f}, {-0.58778507f,0.80901712f}, {-0.45399022f,0.89100665f}, {-0.30901709f,0.95105648f}, {-0.15643452f,0.98768830f}, }; static const ne10_fft_cpx_float32_t ne10_twiddles_60[60] = { {1.0000000f,0.0000000f}, {1.0000000f,-0.0000000f}, {1.0000000f,-0.0000000f}, {1.0000000f,-0.0000000f}, {0.91354543f,-0.40673664f}, {0.66913056f,-0.74314487f}, {1.0000000f,-0.0000000f}, {0.66913056f,-0.74314487f}, {-0.10452851f,-0.99452192f}, {1.0000000f,-0.0000000f}, {0.30901697f,-0.95105654f}, {-0.80901700f,-0.58778518f}, {1.0000000f,-0.0000000f}, {-0.10452851f,-0.99452192f}, {-0.97814757f,0.20791179f}, {1.0000000f,-0.0000000f}, {0.99452192f,-0.10452846f}, {0.97814763f,-0.20791170f}, {0.95105648f,-0.30901700f}, {0.91354543f,-0.40673664f}, {0.86602545f,-0.50000000f}, {0.80901700f,-0.58778524f}, {0.74314475f,-0.66913062f}, {0.66913056f,-0.74314487f}, {0.58778524f,-0.80901700f}, {0.49999997f,-0.86602545f}, {0.40673661f,-0.91354549f}, {0.30901697f,-0.95105654f}, {0.20791166f,-0.97814763f}, {0.10452842f,-0.99452192f}, {1.0000000f,-0.0000000f}, {0.97814763f,-0.20791170f}, {0.91354543f,-0.40673664f}, {0.80901700f,-0.58778524f}, {0.66913056f,-0.74314487f}, {0.49999997f,-0.86602545f}, {0.30901697f,-0.95105654f}, {0.10452842f,-0.99452192f}, {-0.10452851f,-0.99452192f}, {-0.30901703f,-0.95105648f}, {-0.50000006f,-0.86602533f}, {-0.66913068f,-0.74314475f}, {-0.80901700f,-0.58778518f}, {-0.91354549f,-0.40673658f}, {-0.97814763f,-0.20791161f}, {1.0000000f,-0.0000000f}, {0.95105648f,-0.30901700f}, {0.80901700f,-0.58778524f}, {0.58778524f,-0.80901700f}, {0.30901697f,-0.95105654f}, {-4.3711388e-08f,-1.0000000f}, {-0.30901703f,-0.95105648f}, {-0.58778518f,-0.80901700f}, {-0.80901700f,-0.58778518f}, {-0.95105654f,-0.30901679f}, {-1.0000000f,8.7422777e-08f}, {-0.95105654f,0.30901697f}, {-0.80901694f,0.58778536f}, {-0.58778507f,0.80901712f}, {-0.30901709f,0.95105648f}, }; static const ne10_fft_state_float32_t ne10_fft_state_float32_t_480 = { 120, (ne10_int32_t *)ne10_factors_480, (ne10_fft_cpx_float32_t *)ne10_twiddles_480, NULL, (ne10_fft_cpx_float32_t *)&ne10_twiddles_480[120], /* is_forward_scaled = true */ (ne10_int32_t) 1, /* is_backward_scaled = false */ (ne10_int32_t) 0, }; static const arch_fft_state cfg_arch_480 = { 1, (void *)&ne10_fft_state_float32_t_480, }; static const ne10_fft_state_float32_t ne10_fft_state_float32_t_240 = { 60, (ne10_int32_t *)ne10_factors_240, (ne10_fft_cpx_float32_t *)ne10_twiddles_240, NULL, (ne10_fft_cpx_float32_t *)&ne10_twiddles_240[60], /* is_forward_scaled = true */ (ne10_int32_t) 1, /* is_backward_scaled = false */ (ne10_int32_t) 0, }; static const arch_fft_state cfg_arch_240 = { 1, (void *)&ne10_fft_state_float32_t_240, }; static const ne10_fft_state_float32_t ne10_fft_state_float32_t_120 = { 30, (ne10_int32_t *)ne10_factors_120, (ne10_fft_cpx_float32_t *)ne10_twiddles_120, NULL, (ne10_fft_cpx_float32_t *)&ne10_twiddles_120[30], /* is_forward_scaled = true */ (ne10_int32_t) 1, /* is_backward_scaled = false */ (ne10_int32_t) 0, }; static const arch_fft_state cfg_arch_120 = { 1, (void *)&ne10_fft_state_float32_t_120, }; static const ne10_fft_state_float32_t ne10_fft_state_float32_t_60 = { 15, (ne10_int32_t *)ne10_factors_60, (ne10_fft_cpx_float32_t *)ne10_twiddles_60, NULL, (ne10_fft_cpx_float32_t *)&ne10_twiddles_60[15], /* is_forward_scaled = true */ (ne10_int32_t) 1, /* is_backward_scaled = false */ (ne10_int32_t) 0, }; static const arch_fft_state cfg_arch_60 = { 1, (void *)&ne10_fft_state_float32_t_60, }; #endif /* end NE10_FFT_PARAMS48000_960 */ ================================================ FILE: deps/pjsip/third_party/opus/celt/tests/test_unit_cwrs32.c ================================================ /* Copyright (c) 2008-2011 Xiph.Org Foundation, Mozilla Corporation, Gregory Maxwell Written by Jean-Marc Valin, Gregory Maxwell, and Timothy B. Terriberry */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #ifndef CUSTOM_MODES #define CUSTOM_MODES #else #define TEST_CUSTOM_MODES #endif #define CELT_C #include "stack_alloc.h" #include "entenc.c" #include "entdec.c" #include "entcode.c" #include "cwrs.c" #include "mathops.c" #include "rate.h" #define NMAX (240) #define KMAX (128) #ifdef TEST_CUSTOM_MODES #define NDIMS (44) static const int pn[NDIMS]={ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 22, 24, 26, 28, 30, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 88, 96, 104, 112, 120, 128, 144, 160, 176, 192, 208 }; static const int pkmax[NDIMS]={ 128, 128, 128, 128, 88, 52, 36, 26, 22, 18, 16, 15, 13, 12, 12, 11, 10, 9, 9, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4 }; #else /* TEST_CUSTOM_MODES */ #define NDIMS (22) static const int pn[NDIMS]={ 2, 3, 4, 6, 8, 9, 11, 12, 16, 18, 22, 24, 32, 36, 44, 48, 64, 72, 88, 96, 144, 176 }; static const int pkmax[NDIMS]={ 128, 128, 128, 88, 36, 26, 18, 16, 12, 11, 9, 9, 7, 7, 6, 6, 5, 5, 5, 5, 4, 4 }; #endif int main(void){ int t; int n; ALLOC_STACK; for(t=0;tpkmax[t])break; printf("Testing CWRS with N=%i, K=%i...\n",n,k); #if defined(SMALL_FOOTPRINT) nc=ncwrs_urow(n,k,uu); #else nc=CELT_PVQ_V(n,k); #endif inc=nc/20000; if(inc<1)inc=1; for(i=0;i");*/ #if defined(SMALL_FOOTPRINT) ii=icwrs(n,k,&v,y,u); #else ii=icwrs(n,y); v=CELT_PVQ_V(n,k); #endif if(ii!=i){ fprintf(stderr,"Combination-index mismatch (%lu!=%lu).\n", (long)ii,(long)i); return 1; } if(v!=nc){ fprintf(stderr,"Combination count mismatch (%lu!=%lu).\n", (long)v,(long)nc); return 2; } /*printf(" %6u\n",i);*/ } /*printf("\n");*/ } } return 0; } ================================================ FILE: deps/pjsip/third_party/opus/celt/tests/test_unit_dft.c ================================================ /* Copyright (c) 2008 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #define SKIP_CONFIG_H #ifndef CUSTOM_MODES #define CUSTOM_MODES #endif #include #define CELT_C #define TEST_UNIT_DFT_C #include "stack_alloc.h" #include "kiss_fft.h" #include "kiss_fft.c" #include "mathops.c" #include "entcode.c" #if defined(OPUS_X86_MAY_HAVE_SSE2) || defined(OPUS_X86_MAY_HAVE_SSE4_1) # include "x86/x86cpu.c" #elif defined(OPUS_ARM_ASM) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) # include "arm/armcpu.c" # include "celt_lpc.c" # include "pitch.c" # if defined(OPUS_ARM_MAY_HAVE_NEON_INTR) # include "arm/celt_neon_intr.c" # if defined(HAVE_ARM_NE10) # include "mdct.c" # include "arm/celt_ne10_fft.c" # include "arm/celt_ne10_mdct.c" # endif # endif # include "arm/arm_celt_map.c" #endif #ifndef M_PI #define M_PI 3.141592653 #endif int ret = 0; void check(kiss_fft_cpx * in,kiss_fft_cpx * out,int nfft,int isinverse) { int bin,k; double errpow=0,sigpow=0, snr; for (bin=0;bin1) { int k; for (k=1;k #include #include #include #include "entcode.h" #include "entenc.h" #include "entdec.h" #include #include "entenc.c" #include "entdec.c" #include "entcode.c" #ifndef M_LOG2E # define M_LOG2E 1.4426950408889634074 #endif #define DATA_SIZE 10000000 #define DATA_SIZE2 10000 int main(int _argc,char **_argv){ ec_enc enc; ec_dec dec; long nbits; long nbits2; double entropy; int ft; int ftb; int sz; int i; int ret; unsigned int sym; unsigned int seed; unsigned char *ptr; const char *env_seed; ret=0; entropy=0; if (_argc > 2) { fprintf(stderr, "Usage: %s []\n", _argv[0]); return 1; } env_seed = getenv("SEED"); if (_argc > 1) seed = atoi(_argv[1]); else if (env_seed) seed = atoi(env_seed); else seed = time(NULL); /*Testing encoding of raw bit values.*/ ptr = (unsigned char *)malloc(DATA_SIZE); ec_enc_init(&enc,ptr, DATA_SIZE); for(ft=2;ft<1024;ft++){ for(i=0;i>(rand()%11U))+1U)+10; sz=rand()/((RAND_MAX>>(rand()%9U))+1U); data=(unsigned *)malloc(sz*sizeof(*data)); tell=(unsigned *)malloc((sz+1)*sizeof(*tell)); ec_enc_init(&enc,ptr,DATA_SIZE2); zeros = rand()%13==0; tell[0]=ec_tell_frac(&enc); for(j=0;j>(rand()%9U))+1U); logp1=(unsigned *)malloc(sz*sizeof(*logp1)); data=(unsigned *)malloc(sz*sizeof(*data)); tell=(unsigned *)malloc((sz+1)*sizeof(*tell)); enc_method=(unsigned *)malloc(sz*sizeof(*enc_method)); ec_enc_init(&enc,ptr,DATA_SIZE2); tell[0]=ec_tell_frac(&enc); for(j=0;j>1)+1); logp1[j]=(rand()%15)+1; enc_method[j]=rand()/((RAND_MAX>>2)+1); switch(enc_method[j]){ case 0:{ ec_encode(&enc,data[j]?(1<>2)+1); switch(dec_method){ case 0:{ fs=ec_decode(&dec,1<=(1<=(1< #include #include "laplace.h" #define CELT_C #include "stack_alloc.h" #include "entenc.c" #include "entdec.c" #include "entcode.c" #include "laplace.c" #define DATA_SIZE 40000 int ec_laplace_get_start_freq(int decay) { opus_uint32 ft = 32768 - LAPLACE_MINP*(2*LAPLACE_NMIN+1); int fs = (ft*(16384-decay))/(16384+decay); return fs+LAPLACE_MINP; } int main(void) { int i; int ret = 0; ec_enc enc; ec_dec dec; unsigned char *ptr; int val[10000], decay[10000]; ALLOC_STACK; ptr = (unsigned char *)malloc(DATA_SIZE); ec_enc_init(&enc,ptr,DATA_SIZE); val[0] = 3; decay[0] = 6000; val[1] = 0; decay[1] = 5800; val[2] = -1; decay[2] = 5600; for (i=3;i<10000;i++) { val[i] = rand()%15-7; decay[i] = rand()%11000+5000; } for (i=0;i<10000;i++) ec_laplace_encode(&enc, &val[i], ec_laplace_get_start_freq(decay[i]), decay[i]); ec_enc_done(&enc); ec_dec_init(&dec,ec_get_buffer(&enc),ec_range_bytes(&enc)); for (i=0;i<10000;i++) { int d = ec_laplace_decode(&dec, ec_laplace_get_start_freq(decay[i]), decay[i]); if (d != val[i]) { fprintf (stderr, "Got %d instead of %d\n", d, val[i]); ret = 1; } } free(ptr); return ret; } ================================================ FILE: deps/pjsip/third_party/opus/celt/tests/test_unit_mathops.c ================================================ /* Copyright (c) 2008-2011 Xiph.Org Foundation, Mozilla Corporation, Gregory Maxwell Written by Jean-Marc Valin, Gregory Maxwell, and Timothy B. Terriberry */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef CUSTOM_MODES #define CUSTOM_MODES #endif #define CELT_C #include #include #include "mathops.c" #include "entenc.c" #include "entdec.c" #include "entcode.c" #include "bands.c" #include "quant_bands.c" #include "laplace.c" #include "vq.c" #include "cwrs.c" #include "pitch.c" #include "celt_lpc.c" #include "celt.c" #if defined(OPUS_X86_MAY_HAVE_SSE) || defined(OPUS_X86_MAY_HAVE_SSE2) || defined(OPUS_X86_MAY_HAVE_SSE4_1) # if defined(OPUS_X86_MAY_HAVE_SSE) # include "x86/pitch_sse.c" # endif # if defined(OPUS_X86_MAY_HAVE_SSE2) # include "x86/pitch_sse2.c" # endif # if defined(OPUS_X86_MAY_HAVE_SSE4_1) # include "x86/pitch_sse4_1.c" # include "x86/celt_lpc_sse.c" # endif # include "x86/x86_celt_map.c" #elif defined(OPUS_ARM_ASM) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) # include "arm/armcpu.c" # if defined(OPUS_ARM_MAY_HAVE_NEON_INTR) # include "arm/celt_neon_intr.c" # if defined(HAVE_ARM_NE10) # include "kiss_fft.c" # include "mdct.c" # include "arm/celt_ne10_fft.c" # include "arm/celt_ne10_mdct.c" # endif # endif # include "arm/arm_celt_map.c" #endif #ifdef FIXED_POINT #define WORD "%d" #else #define WORD "%f" #endif int ret = 0; void testdiv(void) { opus_int32 i; for (i=1;i<=327670;i++) { double prod; opus_val32 val; val = celt_rcp(i); #ifdef FIXED_POINT prod = (1./32768./65526.)*val*i; #else prod = val*i; #endif if (fabs(prod-1) > .00025) { fprintf (stderr, "div failed: 1/%d="WORD" (product = %f)\n", i, val, prod); ret = 1; } } } void testsqrt(void) { opus_int32 i; for (i=1;i<=1000000000;i++) { double ratio; opus_val16 val; val = celt_sqrt(i); ratio = val/sqrt(i); if (fabs(ratio - 1) > .0005 && fabs(val-sqrt(i)) > 2) { fprintf (stderr, "sqrt failed: sqrt(%d)="WORD" (ratio = %f)\n", i, val, ratio); ret = 1; } i+= i>>10; } } void testbitexactcos(void) { int i; opus_int32 min_d,max_d,last,chk; chk=max_d=0; last=min_d=32767; for(i=64;i<=16320;i++) { opus_int32 d; opus_int32 q=bitexact_cos(i); chk ^= q*i; d = last - q; if (d>max_d)max_d=d; if (dmax_d)max_d=d; if (d0.0009) { fprintf (stderr, "celt_log2 failed: fabs((1.442695040888963387*log(x))-celt_log2(x))>0.001 (x = %f, error = %f)\n", x,error); ret = 1; } } } void testexp2(void) { float x; for (x=-11.0;x<24.0;x+=0.0007) { float error = fabs(x-(1.442695040888963387*log(celt_exp2(x)))); if (error>0.0002) { fprintf (stderr, "celt_exp2 failed: fabs(x-(1.442695040888963387*log(celt_exp2(x))))>0.0005 (x = %f, error = %f)\n", x,error); ret = 1; } } } void testexp2log2(void) { float x; for (x=-11.0;x<24.0;x+=0.0007) { float error = fabs(x-(celt_log2(celt_exp2(x)))); if (error>0.001) { fprintf (stderr, "celt_log2/celt_exp2 failed: fabs(x-(celt_log2(celt_exp2(x))))>0.001 (x = %f, error = %f)\n", x,error); ret = 1; } } } #else void testlog2(void) { opus_val32 x; for (x=8;x<1073741824;x+=(x>>3)) { float error = fabs((1.442695040888963387*log(x/16384.0))-celt_log2(x)/1024.0); if (error>0.003) { fprintf (stderr, "celt_log2 failed: x = %ld, error = %f\n", (long)x,error); ret = 1; } } } void testexp2(void) { opus_val16 x; for (x=-32768;x<15360;x++) { float error1 = fabs(x/1024.0-(1.442695040888963387*log(celt_exp2(x)/65536.0))); float error2 = fabs(exp(0.6931471805599453094*x/1024.0)-celt_exp2(x)/65536.0); if (error1>0.0002&&error2>0.00004) { fprintf (stderr, "celt_exp2 failed: x = "WORD", error1 = %f, error2 = %f\n", x,error1,error2); ret = 1; } } } void testexp2log2(void) { opus_val32 x; for (x=8;x<65536;x+=(x>>3)) { float error = fabs(x-0.25*celt_exp2(celt_log2(x)))/16384; if (error>0.004) { fprintf (stderr, "celt_log2/celt_exp2 failed: fabs(x-(celt_exp2(celt_log2(x))))>0.001 (x = %ld, error = %f)\n", (long)x,error); ret = 1; } } } void testilog2(void) { opus_val32 x; for (x=1;x<=268435455;x+=127) { opus_val32 lg; opus_val32 y; lg = celt_ilog2(x); if (lg<0 || lg>=31) { printf("celt_ilog2 failed: 0<=celt_ilog2(x)<31 (x = %d, celt_ilog2(x) = %d)\n",x,lg); ret = 1; } y = 1<>1)>=y) { printf("celt_ilog2 failed: 2**celt_ilog2(x)<=x<2**(celt_ilog2(x)+1) (x = %d, 2**celt_ilog2(x) = %d)\n",x,y); ret = 1; } } } #endif int main(void) { testbitexactcos(); testbitexactlog2tan(); testdiv(); testsqrt(); testlog2(); testexp2(); testexp2log2(); #ifdef FIXED_POINT testilog2(); #endif return ret; } ================================================ FILE: deps/pjsip/third_party/opus/celt/tests/test_unit_mdct.c ================================================ /* Copyright (c) 2008-2011 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #define SKIP_CONFIG_H #ifndef CUSTOM_MODES #define CUSTOM_MODES #endif #include #define CELT_C #include "mdct.h" #include "stack_alloc.h" #include "kiss_fft.c" #include "mdct.c" #include "mathops.c" #include "entcode.c" #if defined(OPUS_X86_MAY_HAVE_SSE2) || defined(OPUS_X86_MAY_HAVE_SSE4_1) # include "x86/x86cpu.c" #elif defined(OPUS_ARM_ASM) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) # include "arm/armcpu.c" # include "pitch.c" # include "celt_lpc.c" # if defined(OPUS_ARM_MAY_HAVE_NEON_INTR) # include "arm/celt_neon_intr.c" # if defined(HAVE_ARM_NE10) # include "arm/celt_ne10_fft.c" # include "arm/celt_ne10_mdct.c" # endif # endif # include "arm/arm_celt_map.c" #endif #ifndef M_PI #define M_PI 3.141592653 #endif int ret = 0; void check(kiss_fft_scalar * in,kiss_fft_scalar * out,int nfft,int isinverse) { int bin,k; double errpow=0,sigpow=0; double snr; for (bin=0;bin1) { int k; for (k=1;k #include #include "vq.c" #include "cwrs.c" #include "entcode.c" #include "entenc.c" #include "entdec.c" #include "mathops.c" #include "bands.h" #include "pitch.c" #include "celt_lpc.c" #include "celt.c" #include #if defined(OPUS_X86_MAY_HAVE_SSE) || defined(OPUS_X86_MAY_HAVE_SSE2) || defined(OPUS_X86_MAY_HAVE_SSE4_1) # if defined(OPUS_X86_MAY_HAVE_SSE) # include "x86/pitch_sse.c" # endif # if defined(OPUS_X86_MAY_HAVE_SSE2) # include "x86/pitch_sse2.c" # endif # if defined(OPUS_X86_MAY_HAVE_SSE4_1) # include "x86/pitch_sse4_1.c" # include "x86/celt_lpc_sse.c" # endif # include "x86/x86_celt_map.c" #elif defined(OPUS_ARM_ASM) || defined(OPUS_ARM_MAY_HAVE_NEON_INTR) # include "arm/armcpu.c" # if defined(OPUS_ARM_MAY_HAVE_NEON_INTR) # include "arm/celt_neon_intr.c" # if defined(HAVE_ARM_NE10) # include "kiss_fft.c" # include "mdct.c" # include "arm/celt_ne10_fft.c" # include "arm/celt_ne10_mdct.c" # endif # endif # include "arm/arm_celt_map.c" #endif #define MAX_SIZE 100 int ret=0; void test_rotation(int N, int K) { int i; double err = 0, ener = 0, snr, snr0; opus_val16 x0[MAX_SIZE]; opus_val16 x1[MAX_SIZE]; for (i=0;i 20) { fprintf(stderr, "FAIL!\n"); ret = 1; } } int main(void) { ALLOC_STACK; test_rotation(15, 3); test_rotation(23, 5); test_rotation(50, 3); test_rotation(80, 1); return ret; } ================================================ FILE: deps/pjsip/third_party/opus/celt/tests/test_unit_types.c ================================================ /* Copyright (c) 2008-2011 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "opus_types.h" #include int main(void) { opus_int16 i = 1; i <<= 14; if (i>>14 != 1) { fprintf(stderr, "opus_int16 isn't 16 bits\n"); return 1; } if (sizeof(opus_int16)*2 != sizeof(opus_int32)) { fprintf(stderr, "16*2 != 32\n"); return 1; } return 0; } ================================================ FILE: deps/pjsip/third_party/opus/celt/vq.c ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mathops.h" #include "cwrs.h" #include "vq.h" #include "arch.h" #include "os_support.h" #include "bands.h" #include "rate.h" #include "pitch.h" #ifndef OVERRIDE_vq_exp_rotation1 static void exp_rotation1(celt_norm *X, int len, int stride, opus_val16 c, opus_val16 s) { int i; opus_val16 ms; celt_norm *Xptr; Xptr = X; ms = NEG16(s); for (i=0;i=0;i--) { celt_norm x1, x2; x1 = Xptr[0]; x2 = Xptr[stride]; Xptr[stride] = EXTRACT16(PSHR32(MAC16_16(MULT16_16(c, x2), s, x1), 15)); *Xptr-- = EXTRACT16(PSHR32(MAC16_16(MULT16_16(c, x1), ms, x2), 15)); } } #endif /* OVERRIDE_vq_exp_rotation1 */ static void exp_rotation(celt_norm *X, int len, int dir, int stride, int K, int spread) { static const int SPREAD_FACTOR[3]={15,10,5}; int i; opus_val16 c, s; opus_val16 gain, theta; int stride2=0; int factor; if (2*K>=len || spread==SPREAD_NONE) return; factor = SPREAD_FACTOR[spread-1]; gain = celt_div((opus_val32)MULT16_16(Q15_ONE,len),(opus_val32)(len+factor*K)); theta = HALF16(MULT16_16_Q15(gain,gain)); c = celt_cos_norm(EXTEND32(theta)); s = celt_cos_norm(EXTEND32(SUB16(Q15ONE,theta))); /* sin(theta) */ if (len>=8*stride) { stride2 = 1; /* This is just a simple (equivalent) way of computing sqrt(len/stride) with rounding. It's basically incrementing long as (stride2+0.5)^2 < len/stride. */ while ((stride2*stride2+stride2)*stride + (stride>>2) < len) stride2++; } /*NOTE: As a minor optimization, we could be passing around log2(B), not B, for both this and for extract_collapse_mask().*/ len = celt_udiv(len, stride); for (i=0;i>1; #endif t = VSHR32(Ryy, 2*(k-7)); g = MULT16_16_P15(celt_rsqrt_norm(t),gain); i=0; do X[i] = EXTRACT16(PSHR32(MULT16_16(g, iy[i]), k+1)); while (++i < N); } static unsigned extract_collapse_mask(int *iy, int N, int B) { unsigned collapse_mask; int N0; int i; if (B<=1) return 1; /*NOTE: As a minor optimization, we could be passing around log2(B), not B, for both this and for exp_rotation().*/ N0 = celt_udiv(N, B); collapse_mask = 0; i=0; do { int j; unsigned tmp=0; j=0; do { tmp |= iy[i*N0+j]; } while (++j0, "alg_quant() needs at least one pulse"); celt_assert2(N>1, "alg_quant() needs at least two dimensions"); ALLOC(y, N, celt_norm); ALLOC(iy, N, int); ALLOC(signx, N, opus_val16); exp_rotation(X, N, 1, B, K, spread); /* Get rid of the sign */ sum = 0; j=0; do { if (X[j]>0) signx[j]=1; else { signx[j]=-1; X[j]=-X[j]; } iy[j] = 0; y[j] = 0; } while (++j (N>>1)) { opus_val16 rcp; j=0; do { sum += X[j]; } while (++j EPSILON && sum < 64)) #endif { X[0] = QCONST16(1.f,14); j=1; do X[j]=0; while (++j=1, "Allocated too many pulses in the quick pass"); /* This should never happen, but just in case it does (e.g. on silence) we fill the first bin with pulses. */ #ifdef FIXED_POINT_DEBUG celt_assert2(pulsesLeft<=N+3, "Not enough pulses in the quick pass"); #endif if (pulsesLeft > N+3) { opus_val16 tmp = (opus_val16)pulsesLeft; yy = MAC16_16(yy, tmp, tmp); yy = MAC16_16(yy, tmp, y[0]); iy[0] += pulsesLeft; pulsesLeft=0; } s = 1; for (i=0;i= best_num/best_den, but that way we can do it without any division */ /* OPT: Make sure to use conditional moves here */ if (MULT16_16(best_den, Rxy) > MULT16_16(Ryy, best_num)) { best_den = Ryy; best_num = Rxy; best_id = j; } } while (++j0, "alg_unquant() needs at least one pulse"); celt_assert2(N>1, "alg_unquant() needs at least two dimensions"); ALLOC(iy, N, int); Ryy = decode_pulses(iy, N, K, dec); normalise_residual(iy, X, N, Ryy, gain); exp_rotation(X, N, -1, B, K, spread); collapse_mask = extract_collapse_mask(iy, N, B); RESTORE_STACK; return collapse_mask; } #ifndef OVERRIDE_renormalise_vector void renormalise_vector(celt_norm *X, int N, opus_val16 gain, int arch) { int i; #ifdef FIXED_POINT int k; #endif opus_val32 E; opus_val16 g; opus_val32 t; celt_norm *xptr; E = EPSILON + celt_inner_prod(X, X, N, arch); #ifdef FIXED_POINT k = celt_ilog2(E)>>1; #endif t = VSHR32(E, 2*(k-7)); g = MULT16_16_P15(celt_rsqrt_norm(t),gain); xptr = X; for (i=0;i #include #include #include "celt_lpc.h" #include "stack_alloc.h" #include "mathops.h" #include "pitch.h" #include "x86cpu.h" #if defined(FIXED_POINT) void celt_fir_sse4_1(const opus_val16 *_x, const opus_val16 *num, opus_val16 *_y, int N, int ord, opus_val16 *mem, int arch) { int i,j; VARDECL(opus_val16, rnum); VARDECL(opus_val16, x); __m128i vecNoA; opus_int32 noA ; SAVE_STACK; ALLOC(rnum, ord, opus_val16); ALLOC(x, N+ord, opus_val16); for(i=0;i> 1; vecNoA = _mm_set_epi32(noA, noA, noA, noA); for (i=0;i #include "arch.h" void xcorr_kernel_sse(const opus_val16 *x, const opus_val16 *y, opus_val32 sum[4], int len) { int j; __m128 xsum1, xsum2; xsum1 = _mm_loadu_ps(sum); xsum2 = _mm_setzero_ps(); for (j = 0; j < len-3; j += 4) { __m128 x0 = _mm_loadu_ps(x+j); __m128 yj = _mm_loadu_ps(y+j); __m128 y3 = _mm_loadu_ps(y+j+3); xsum1 = _mm_add_ps(xsum1,_mm_mul_ps(_mm_shuffle_ps(x0,x0,0x00),yj)); xsum2 = _mm_add_ps(xsum2,_mm_mul_ps(_mm_shuffle_ps(x0,x0,0x55), _mm_shuffle_ps(yj,y3,0x49))); xsum1 = _mm_add_ps(xsum1,_mm_mul_ps(_mm_shuffle_ps(x0,x0,0xaa), _mm_shuffle_ps(yj,y3,0x9e))); xsum2 = _mm_add_ps(xsum2,_mm_mul_ps(_mm_shuffle_ps(x0,x0,0xff),y3)); } if (j < len) { xsum1 = _mm_add_ps(xsum1,_mm_mul_ps(_mm_load1_ps(x+j),_mm_loadu_ps(y+j))); if (++j < len) { xsum2 = _mm_add_ps(xsum2,_mm_mul_ps(_mm_load1_ps(x+j),_mm_loadu_ps(y+j))); if (++j < len) { xsum1 = _mm_add_ps(xsum1,_mm_mul_ps(_mm_load1_ps(x+j),_mm_loadu_ps(y+j))); } } } _mm_storeu_ps(sum,_mm_add_ps(xsum1,xsum2)); } void dual_inner_prod_sse(const opus_val16 *x, const opus_val16 *y01, const opus_val16 *y02, int N, opus_val32 *xy1, opus_val32 *xy2) { int i; __m128 xsum1, xsum2; xsum1 = _mm_setzero_ps(); xsum2 = _mm_setzero_ps(); for (i=0;i #include #include "macros.h" #include "celt_lpc.h" #include "stack_alloc.h" #include "mathops.h" #include "pitch.h" #if defined(OPUS_X86_MAY_HAVE_SSE2) && defined(FIXED_POINT) opus_val32 celt_inner_prod_sse2(const opus_val16 *x, const opus_val16 *y, int N) { opus_int i, dataSize16; opus_int32 sum; __m128i inVec1_76543210, inVec1_FEDCBA98, acc1; __m128i inVec2_76543210, inVec2_FEDCBA98, acc2; sum = 0; dataSize16 = N & ~15; acc1 = _mm_setzero_si128(); acc2 = _mm_setzero_si128(); for (i=0;i= 8) { inVec1_76543210 = _mm_loadu_si128((__m128i *)(&x[i + 0])); inVec2_76543210 = _mm_loadu_si128((__m128i *)(&y[i + 0])); inVec1_76543210 = _mm_madd_epi16(inVec1_76543210, inVec2_76543210); acc1 = _mm_add_epi32(acc1, inVec1_76543210); i += 8; } acc1 = _mm_add_epi32(acc1, _mm_unpackhi_epi64( acc1, acc1)); acc1 = _mm_add_epi32(acc1, _mm_shufflelo_epi16( acc1, 0x0E)); sum += _mm_cvtsi128_si32(acc1); for (;i #include #include "macros.h" #include "celt_lpc.h" #include "stack_alloc.h" #include "mathops.h" #include "pitch.h" #if defined(OPUS_X86_MAY_HAVE_SSE4_1) && defined(FIXED_POINT) #include #include "x86cpu.h" opus_val32 celt_inner_prod_sse4_1(const opus_val16 *x, const opus_val16 *y, int N) { opus_int i, dataSize16; opus_int32 sum; __m128i inVec1_76543210, inVec1_FEDCBA98, acc1; __m128i inVec2_76543210, inVec2_FEDCBA98, acc2; __m128i inVec1_3210, inVec2_3210; sum = 0; dataSize16 = N & ~15; acc1 = _mm_setzero_si128(); acc2 = _mm_setzero_si128(); for (i=0;i= 8) { inVec1_76543210 = _mm_loadu_si128((__m128i *)(&x[i + 0])); inVec2_76543210 = _mm_loadu_si128((__m128i *)(&y[i + 0])); inVec1_76543210 = _mm_madd_epi16(inVec1_76543210, inVec2_76543210); acc1 = _mm_add_epi32(acc1, inVec1_76543210); i += 8; } if (N - i >= 4) { inVec1_3210 = OP_CVTEPI16_EPI32_M64(&x[i + 0]); inVec2_3210 = OP_CVTEPI16_EPI32_M64(&y[i + 0]); inVec1_3210 = _mm_mullo_epi32(inVec1_3210, inVec2_3210); acc1 = _mm_add_epi32(acc1, inVec1_3210); i += 4; } acc1 = _mm_add_epi32(acc1, _mm_unpackhi_epi64(acc1, acc1)); acc1 = _mm_add_epi32(acc1, _mm_shufflelo_epi16(acc1, 0x0E)); sum += _mm_cvtsi128_si32(acc1); for (;i= 3); sum0 = _mm_setzero_si128(); sum1 = _mm_setzero_si128(); sum2 = _mm_setzero_si128(); sum3 = _mm_setzero_si128(); for (j=0;j<(len-7);j+=8) { vecX = _mm_loadu_si128((__m128i *)(&x[j + 0])); vecY0 = _mm_loadu_si128((__m128i *)(&y[j + 0])); vecY1 = _mm_loadu_si128((__m128i *)(&y[j + 1])); vecY2 = _mm_loadu_si128((__m128i *)(&y[j + 2])); vecY3 = _mm_loadu_si128((__m128i *)(&y[j + 3])); sum0 = _mm_add_epi32(sum0, _mm_madd_epi16(vecX, vecY0)); sum1 = _mm_add_epi32(sum1, _mm_madd_epi16(vecX, vecY1)); sum2 = _mm_add_epi32(sum2, _mm_madd_epi16(vecX, vecY2)); sum3 = _mm_add_epi32(sum3, _mm_madd_epi16(vecX, vecY3)); } sum0 = _mm_add_epi32(sum0, _mm_unpackhi_epi64( sum0, sum0)); sum0 = _mm_add_epi32(sum0, _mm_shufflelo_epi16( sum0, 0x0E)); sum1 = _mm_add_epi32(sum1, _mm_unpackhi_epi64( sum1, sum1)); sum1 = _mm_add_epi32(sum1, _mm_shufflelo_epi16( sum1, 0x0E)); sum2 = _mm_add_epi32(sum2, _mm_unpackhi_epi64( sum2, sum2)); sum2 = _mm_add_epi32(sum2, _mm_shufflelo_epi16( sum2, 0x0E)); sum3 = _mm_add_epi32(sum3, _mm_unpackhi_epi64( sum3, sum3)); sum3 = _mm_add_epi32(sum3, _mm_shufflelo_epi16( sum3, 0x0E)); vecSum = _mm_unpacklo_epi64(_mm_unpacklo_epi32(sum0, sum1), _mm_unpacklo_epi32(sum2, sum3)); for (;j<(len-3);j+=4) { vecX = OP_CVTEPI16_EPI32_M64(&x[j + 0]); vecX0 = _mm_shuffle_epi32(vecX, 0x00); vecX1 = _mm_shuffle_epi32(vecX, 0x55); vecX2 = _mm_shuffle_epi32(vecX, 0xaa); vecX3 = _mm_shuffle_epi32(vecX, 0xff); vecY0 = OP_CVTEPI16_EPI32_M64(&y[j + 0]); vecY1 = OP_CVTEPI16_EPI32_M64(&y[j + 1]); vecY2 = OP_CVTEPI16_EPI32_M64(&y[j + 2]); vecY3 = OP_CVTEPI16_EPI32_M64(&y[j + 3]); sum0 = _mm_mullo_epi32(vecX0, vecY0); sum1 = _mm_mullo_epi32(vecX1, vecY1); sum2 = _mm_mullo_epi32(vecX2, vecY2); sum3 = _mm_mullo_epi32(vecX3, vecY3); sum0 = _mm_add_epi32(sum0, sum1); sum2 = _mm_add_epi32(sum2, sum3); vecSum = _mm_add_epi32(vecSum, sum0); vecSum = _mm_add_epi32(vecSum, sum2); } for (;j static _inline void cpuid(unsigned int CPUInfo[4], unsigned int InfoType) { __cpuid((int*)CPUInfo, InfoType); } #else #if defined(CPU_INFO_BY_C) #include #endif static void cpuid(unsigned int CPUInfo[4], unsigned int InfoType) { #if defined(CPU_INFO_BY_ASM) #if defined(__i386__) && defined(__PIC__) /* %ebx is PIC register in 32-bit, so mustn't clobber it. */ __asm__ __volatile__ ( "xchg %%ebx, %1\n" "cpuid\n" "xchg %%ebx, %1\n": "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) : "0" (InfoType) ); #else __asm__ __volatile__ ( "cpuid": "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) : "0" (InfoType) ); #endif #elif defined(CPU_INFO_BY_C) __get_cpuid(InfoType, &(CPUInfo[0]), &(CPUInfo[1]), &(CPUInfo[2]), &(CPUInfo[3])); #endif } #endif typedef struct CPU_Feature{ /* SIMD: 128-bit */ int HW_SSE; int HW_SSE2; int HW_SSE41; /* SIMD: 256-bit */ int HW_AVX; } CPU_Feature; static void opus_cpu_feature_check(CPU_Feature *cpu_feature) { unsigned int info[4] = {0}; unsigned int nIds = 0; cpuid(info, 0); nIds = info[0]; if (nIds >= 1){ cpuid(info, 1); cpu_feature->HW_SSE = (info[3] & (1 << 25)) != 0; cpu_feature->HW_SSE2 = (info[3] & (1 << 26)) != 0; cpu_feature->HW_SSE41 = (info[2] & (1 << 19)) != 0; cpu_feature->HW_AVX = (info[2] & (1 << 28)) != 0; } else { cpu_feature->HW_SSE = 0; cpu_feature->HW_SSE2 = 0; cpu_feature->HW_SSE41 = 0; cpu_feature->HW_AVX = 0; } } int opus_select_arch(void) { CPU_Feature cpu_feature; int arch; opus_cpu_feature_check(&cpu_feature); arch = 0; if (!cpu_feature.HW_SSE) { return arch; } arch++; if (!cpu_feature.HW_SSE2) { return arch; } arch++; if (!cpu_feature.HW_SSE41) { return arch; } arch++; if (!cpu_feature.HW_AVX) { return arch; } arch++; return arch; } #endif ================================================ FILE: deps/pjsip/third_party/opus/celt/x86/x86cpu.h ================================================ /* Copyright (c) 2014, Cisco Systems, INC Written by XiangMingZhu WeiZhou MinPeng YanWang Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if !defined(X86CPU_H) # define X86CPU_H # if defined(OPUS_X86_MAY_HAVE_SSE) # define MAY_HAVE_SSE(name) name ## _sse # else # define MAY_HAVE_SSE(name) name ## _c # endif # if defined(OPUS_X86_MAY_HAVE_SSE2) # define MAY_HAVE_SSE2(name) name ## _sse2 # else # define MAY_HAVE_SSE2(name) name ## _c # endif # if defined(OPUS_X86_MAY_HAVE_SSE4_1) # define MAY_HAVE_SSE4_1(name) name ## _sse4_1 # else # define MAY_HAVE_SSE4_1(name) name ## _c # endif # if defined(OPUS_X86_MAY_HAVE_AVX) # define MAY_HAVE_AVX(name) name ## _avx # else # define MAY_HAVE_AVX(name) name ## _c # endif # if defined(OPUS_HAVE_RTCD) int opus_select_arch(void); # endif /*gcc appears to emit MOVDQA's to load the argument of an _mm_cvtepi8_epi32() or _mm_cvtepi16_epi32() when optimizations are disabled, even though the actual PMOVSXWD instruction takes an m32 or m64. Unlike a normal memory reference, these require 16-byte alignment and load a full 16 bytes (instead of 4 or 8), possibly reading out of bounds. We can insert an explicit MOVD or MOVQ using _mm_cvtsi32_si128() or _mm_loadl_epi64(), which should have the same semantics as an m32 or m64 reference in the PMOVSXWD instruction itself, but gcc is not smart enough to optimize this out when optimizations ARE enabled. Clang, in contrast, requires us to do this always for _mm_cvtepi8_epi32 (which is fair, since technically the compiler is always allowed to do the dereference before invoking the function implementing the intrinsic). However, it is smart enough to eliminate the extra MOVD instruction. For _mm_cvtepi16_epi32, it does the right thing, though does *not* optimize out the extra MOVQ if it's specified explicitly */ # if defined(__clang__) || !defined(__OPTIMIZE__) # define OP_CVTEPI8_EPI32_M32(x) \ (_mm_cvtepi8_epi32(_mm_cvtsi32_si128(*(int *)(x)))) # else # define OP_CVTEPI8_EPI32_M32(x) \ (_mm_cvtepi8_epi32(*(__m128i *)(x))) #endif # if !defined(__OPTIMIZE__) # define OP_CVTEPI16_EPI32_M64(x) \ (_mm_cvtepi16_epi32(_mm_loadl_epi64((__m128i *)(x)))) # else # define OP_CVTEPI16_EPI32_M64(x) \ (_mm_cvtepi16_epi32(*(__m128i *)(x))) # endif #endif ================================================ FILE: deps/pjsip/third_party/opus/celt_headers.mk ================================================ CELT_HEAD = \ celt/arch.h \ celt/bands.h \ celt/celt.h \ celt/cpu_support.h \ include/opus_types.h \ include/opus_defines.h \ include/opus_custom.h \ celt/cwrs.h \ celt/ecintrin.h \ celt/entcode.h \ celt/entdec.h \ celt/entenc.h \ celt/fixed_debug.h \ celt/fixed_generic.h \ celt/float_cast.h \ celt/_kiss_fft_guts.h \ celt/kiss_fft.h \ celt/laplace.h \ celt/mathops.h \ celt/mdct.h \ celt/mfrngcod.h \ celt/modes.h \ celt/os_support.h \ celt/pitch.h \ celt/celt_lpc.h \ celt/x86/celt_lpc_sse.h \ celt/quant_bands.h \ celt/rate.h \ celt/stack_alloc.h \ celt/vq.h \ celt/static_modes_float.h \ celt/static_modes_fixed.h \ celt/static_modes_float_arm_ne10.h \ celt/static_modes_fixed_arm_ne10.h \ celt/arm/armcpu.h \ celt/arm/fixed_armv4.h \ celt/arm/fixed_armv5e.h \ celt/arm/kiss_fft_armv4.h \ celt/arm/kiss_fft_armv5e.h \ celt/arm/pitch_arm.h \ celt/arm/fft_arm.h \ celt/arm/mdct_arm.h \ celt/mips/celt_mipsr1.h \ celt/mips/fixed_generic_mipsr1.h \ celt/mips/kiss_fft_mipsr1.h \ celt/mips/mdct_mipsr1.h \ celt/mips/pitch_mipsr1.h \ celt/mips/vq_mipsr1.h \ celt/x86/pitch_sse.h \ celt/x86/x86cpu.h ================================================ FILE: deps/pjsip/third_party/opus/celt_sources.mk ================================================ CELT_SOURCES = celt/bands.c \ celt/celt.c \ celt/celt_encoder.c \ celt/celt_decoder.c \ celt/cwrs.c \ celt/entcode.c \ celt/entdec.c \ celt/entenc.c \ celt/kiss_fft.c \ celt/laplace.c \ celt/mathops.c \ celt/mdct.c \ celt/modes.c \ celt/pitch.c \ celt/celt_lpc.c \ celt/quant_bands.c \ celt/rate.c \ celt/vq.c CELT_SOURCES_SSE = celt/x86/x86cpu.c \ celt/x86/x86_celt_map.c \ celt/x86/pitch_sse.c CELT_SOURCES_SSE2 = celt/x86/pitch_sse2.c CELT_SOURCES_SSE4_1 = celt/x86/celt_lpc_sse.c \ celt/x86/pitch_sse4_1.c CELT_SOURCES_ARM = \ celt/arm/armcpu.c \ celt/arm/arm_celt_map.c CELT_SOURCES_ARM_ASM = \ celt/arm/celt_pitch_xcorr_arm.s CELT_AM_SOURCES_ARM_ASM = \ celt/arm/armopts.s.in CELT_SOURCES_ARM_NEON_INTR = \ celt/arm/celt_neon_intr.c CELT_SOURCES_ARM_NE10= \ celt/arm/celt_ne10_fft.c \ celt/arm/celt_ne10_mdct.c ================================================ FILE: deps/pjsip/third_party/opus/configure.ac ================================================ dnl Process this file with autoconf to produce a configure script. -*-m4-*- dnl The package_version file will be automatically synced to the git revision dnl by the update_version script when configured in the repository, but will dnl remain constant in tarball releases unless it is manually edited. m4_define([CURRENT_VERSION], m4_esyscmd([ ./update_version 2>/dev/null || true if test -e package_version; then . ./package_version printf "$PACKAGE_VERSION" else printf "unknown" fi ])) AC_INIT([opus],[CURRENT_VERSION],[opus@xiph.org]) AC_CONFIG_SRCDIR(src/opus_encoder.c) AC_CONFIG_MACRO_DIR([m4]) dnl enable silent rules on automake 1.11 and later m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) # For libtool. dnl Please update these for releases. OPUS_LT_CURRENT=5 OPUS_LT_REVISION=2 OPUS_LT_AGE=5 AC_SUBST(OPUS_LT_CURRENT) AC_SUBST(OPUS_LT_REVISION) AC_SUBST(OPUS_LT_AGE) AM_INIT_AUTOMAKE([no-define]) AM_MAINTAINER_MODE([enable]) AC_CANONICAL_HOST AC_MINGW32 AM_PROG_LIBTOOL AM_PROG_CC_C_O AC_PROG_CC_C99 AC_C_CONST AC_C_INLINE AM_PROG_AS AC_DEFINE([OPUS_BUILD], [], [This is a build of OPUS]) #Use a hacked up version of autoconf's AC_C_RESTRICT because it's not #strong enough a test to detect old buggy versions of GCC (e.g. 2.95.3) #Note: Both this and the test for variable-size arrays below are also # done by AC_PROG_CC_C99, but not thoroughly enough apparently. AC_CACHE_CHECK([for C/C++ restrict keyword], ac_cv_c_restrict, [ac_cv_c_restrict=no # The order here caters to the fact that C++ does not require restrict. for ac_kw in __restrict __restrict__ _Restrict restrict; do AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[typedef int * int_ptr; int foo (int_ptr $ac_kw ip, int * $ac_kw baz[]) { return ip[0]; }]], [[int s[1]; int * $ac_kw t = s; t[0] = 0; return foo(t, (void *)0)]])], [ac_cv_c_restrict=$ac_kw]) test "$ac_cv_c_restrict" != no && break done ]) AH_VERBATIM([restrict], [/* Define to the equivalent of the C99 'restrict' keyword, or to nothing if this is not supported. Do not define if restrict is supported directly. */ #undef restrict /* Work around a bug in Sun C++: it does not support _Restrict or __restrict__, even though the corresponding Sun C compiler ends up with "#define restrict _Restrict" or "#define restrict __restrict__" in the previous line. Perhaps some future version of Sun C++ will work with restrict; if so, hopefully it defines __RESTRICT like Sun C does. */ #if defined __SUNPRO_CC && !defined __RESTRICT # define _Restrict # define __restrict__ #endif]) case $ac_cv_c_restrict in restrict) ;; no) AC_DEFINE([restrict], []) ;; *) AC_DEFINE_UNQUOTED([restrict], [$ac_cv_c_restrict]) ;; esac AC_MSG_CHECKING(for C99 variable-size arrays) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[static int x; char a[++x]; a[sizeof a - 1] = 0; int N; return a[0];]])], [ has_var_arrays=yes use_alloca="no (using var arrays)" AC_DEFINE([VAR_ARRAYS], [1], [Use C99 variable-size arrays]) ],[ has_var_arrays=no ]) AC_MSG_RESULT([$has_var_arrays]) AS_IF([test "$has_var_arrays" = "no"], [ AC_CHECK_HEADERS([alloca.h]) AC_MSG_CHECKING(for alloca) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int foo=10; int *array = alloca(foo);]])], [ use_alloca=yes; AC_DEFINE([USE_ALLOCA], [], [Make use of alloca]) ],[ use_alloca=no ]) AC_MSG_RESULT([$use_alloca]) ]) LT_LIB_M AC_ARG_ENABLE([fixed-point], [AS_HELP_STRING([--enable-fixed-point], [compile without floating point (for machines without a fast enough FPU)])],, [enable_fixed_point=no]) AS_IF([test "$enable_fixed_point" = "yes"],[ enable_float="no" AC_DEFINE([FIXED_POINT], [1], [Compile as fixed-point (for machines without a fast enough FPU)]) PC_BUILD="fixed-point" ],[ enable_float="yes"; PC_BUILD="floating-point" ]) AM_CONDITIONAL([FIXED_POINT], [test "$enable_fixed_point" = "yes"]) AC_ARG_ENABLE([fixed-point-debug], [AS_HELP_STRING([--enable-fixed-point-debug], [debug fixed-point implementation])],, [enable_fixed_point_debug=no]) AS_IF([test "$enable_fixed_point_debug" = "yes"],[ AC_DEFINE([FIXED_DEBUG], [1], [Debug fixed-point implementation]) ]) AC_ARG_ENABLE([float_api], [AS_HELP_STRING([--disable-float-api], [compile without the floating point API (for machines with no float library)])],, [enable_float_api=yes]) AM_CONDITIONAL([DISABLE_FLOAT_API], [test "$enable_float_api" = "no"]) AS_IF([test "$enable_float_api" = "no"],[ AC_DEFINE([DISABLE_FLOAT_API], [1], [Do not build the float API]) ]) AC_ARG_ENABLE([custom-modes], [AS_HELP_STRING([--enable-custom-modes], [enable non-Opus modes, e.g. 44.1 kHz & 2^n frames])],, [enable_custom_modes=no]) AS_IF([test "$enable_custom_modes" = "yes"],[ AC_DEFINE([CUSTOM_MODES], [1], [Custom modes]) PC_BUILD="$PC_BUILD, custom modes" ]) AM_CONDITIONAL([CUSTOM_MODES], [test "$enable_custom_modes" = "yes"]) has_float_approx=no #case "$host_cpu" in #i[[3456]]86 | x86_64 | powerpc64 | powerpc32 | ia64) # has_float_approx=yes # ;; #esac AC_ARG_ENABLE([float-approx], [AS_HELP_STRING([--enable-float-approx], [enable fast approximations for floating point])], [if test "$enable_float_approx" = "yes"; then AC_WARN([Floating point approximations are not supported on all platforms.]) fi ], [enable_float_approx=$has_float_approx]) AS_IF([test "$enable_float_approx" = "yes"],[ AC_DEFINE([FLOAT_APPROX], [1], [Float approximations]) ]) AC_ARG_ENABLE([asm], [AS_HELP_STRING([--disable-asm], [Disable assembly optimizations])],, [enable_asm=yes]) AC_ARG_ENABLE([rtcd], [AS_HELP_STRING([--disable-rtcd], [Disable run-time CPU capabilities detection])],, [enable_rtcd=yes]) AC_ARG_ENABLE([intrinsics], [AS_HELP_STRING([--enable-intrinsics], [Enable intrinsics optimizations for ARM(float) X86(fixed)])],, [enable_intrinsics=no]) rtcd_support=no cpu_arm=no AS_IF([test x"${enable_asm}" = x"yes"],[ inline_optimization="No inline ASM for your platform, please send patches" case $host_cpu in arm*) dnl Currently we only have asm for fixed-point AS_IF([test "$enable_float" != "yes"],[ cpu_arm=yes AC_DEFINE([OPUS_ARM_ASM], [], [Make use of ARM asm optimization]) AS_GCC_INLINE_ASSEMBLY( [inline_optimization="ARM"], [inline_optimization="disabled"] ) AS_ASM_ARM_EDSP([OPUS_ARM_INLINE_EDSP=1],[OPUS_ARM_INLINE_EDSP=0]) AS_ASM_ARM_MEDIA([OPUS_ARM_INLINE_MEDIA=1], [OPUS_ARM_INLINE_MEDIA=0]) AS_ASM_ARM_NEON([OPUS_ARM_INLINE_NEON=1],[OPUS_ARM_INLINE_NEON=0]) AS_IF([test x"$inline_optimization" = x"ARM"],[ AM_CONDITIONAL([OPUS_ARM_INLINE_ASM],[true]) AC_DEFINE([OPUS_ARM_INLINE_ASM], 1, [Use generic ARMv4 inline asm optimizations]) AS_IF([test x"$OPUS_ARM_INLINE_EDSP" = x"1"],[ AC_DEFINE([OPUS_ARM_INLINE_EDSP], [1], [Use ARMv5E inline asm optimizations]) inline_optimization="$inline_optimization (EDSP)" ]) AS_IF([test x"$OPUS_ARM_INLINE_MEDIA" = x"1"],[ AC_DEFINE([OPUS_ARM_INLINE_MEDIA], [1], [Use ARMv6 inline asm optimizations]) inline_optimization="$inline_optimization (Media)" ]) AS_IF([test x"$OPUS_ARM_INLINE_NEON" = x"1"],[ AC_DEFINE([OPUS_ARM_INLINE_NEON], 1, [Use ARM NEON inline asm optimizations]) inline_optimization="$inline_optimization (NEON)" ]) ]) dnl We need Perl to translate RVCT-syntax asm to gas syntax. AC_CHECK_PROG([HAVE_PERL], perl, yes, no) AS_IF([test x"$HAVE_PERL" = x"yes"],[ AM_CONDITIONAL([OPUS_ARM_EXTERNAL_ASM],[true]) asm_optimization="ARM" AS_IF([test x"$OPUS_ARM_INLINE_EDSP" = x"1"], [ OPUS_ARM_PRESUME_EDSP=1 OPUS_ARM_MAY_HAVE_EDSP=1 ], [ OPUS_ARM_PRESUME_EDSP=0 OPUS_ARM_MAY_HAVE_EDSP=0 ]) AS_IF([test x"$OPUS_ARM_INLINE_MEDIA" = x"1"], [ OPUS_ARM_PRESUME_MEDIA=1 OPUS_ARM_MAY_HAVE_MEDIA=1 ], [ OPUS_ARM_PRESUME_MEDIA=0 OPUS_ARM_MAY_HAVE_MEDIA=0 ]) AS_IF([test x"$OPUS_ARM_INLINE_NEON" = x"1"], [ OPUS_ARM_PRESUME_NEON=1 OPUS_ARM_MAY_HAVE_NEON=1 ], [ OPUS_ARM_PRESUME_NEON=0 OPUS_ARM_MAY_HAVE_NEON=0 ]) AS_IF([test x"$enable_rtcd" = x"yes"],[ AS_IF([test x"$OPUS_ARM_MAY_HAVE_EDSP" != x"1"],[ AC_MSG_NOTICE( [Trying to force-enable armv5e EDSP instructions...]) AS_ASM_ARM_EDSP_FORCE([OPUS_ARM_MAY_HAVE_EDSP=1]) ]) AS_IF([test x"$OPUS_ARM_MAY_HAVE_MEDIA" != x"1"],[ AC_MSG_NOTICE( [Trying to force-enable ARMv6 media instructions...]) AS_ASM_ARM_MEDIA_FORCE([OPUS_ARM_MAY_HAVE_MEDIA=1]) ]) AS_IF([test x"$OPUS_ARM_MAY_HAVE_NEON" != x"1"],[ AC_MSG_NOTICE( [Trying to force-enable NEON instructions...]) AS_ASM_ARM_NEON_FORCE([OPUS_ARM_MAY_HAVE_NEON=1]) ]) ]) rtcd_support= AS_IF([test x"$OPUS_ARM_MAY_HAVE_EDSP" = x"1"],[ AC_DEFINE(OPUS_ARM_MAY_HAVE_EDSP, 1, [Define if assembler supports EDSP instructions]) AS_IF([test x"$OPUS_ARM_PRESUME_EDSP" = x"1"],[ AC_DEFINE(OPUS_ARM_PRESUME_EDSP, 1, [Define if binary requires EDSP instruction support]) asm_optimization="$asm_optimization (EDSP)" ], [rtcd_support="$rtcd_support (EDSP)"] ) ]) AC_SUBST(OPUS_ARM_MAY_HAVE_EDSP) AS_IF([test x"$OPUS_ARM_MAY_HAVE_MEDIA" = x"1"],[ AC_DEFINE(OPUS_ARM_MAY_HAVE_MEDIA, 1, [Define if assembler supports ARMv6 media instructions]) AS_IF([test x"$OPUS_ARM_PRESUME_MEDIA" = x"1"],[ AC_DEFINE(OPUS_ARM_PRESUME_MEDIA, 1, [Define if binary requires ARMv6 media instruction support]) asm_optimization="$asm_optimization (Media)" ], [rtcd_support="$rtcd_support (Media)"] ) ]) AC_SUBST(OPUS_ARM_MAY_HAVE_MEDIA) AS_IF([test x"$OPUS_ARM_MAY_HAVE_NEON" = x"1"],[ AC_DEFINE(OPUS_ARM_MAY_HAVE_NEON, 1, [Define if compiler supports NEON instructions]) AS_IF([test x"$OPUS_ARM_PRESUME_NEON" = x"1"], [ AC_DEFINE(OPUS_ARM_PRESUME_NEON, 1, [Define if binary requires NEON instruction support]) asm_optimization="$asm_optimization (NEON)" ], [rtcd_support="$rtcd_support (NEON)"] ) ]) AC_SUBST(OPUS_ARM_MAY_HAVE_NEON) dnl Make sure turning on RTCD gets us at least one dnl instruction set. AS_IF([test x"$rtcd_support" != x""], [rtcd_support=ARM"$rtcd_support"], [rtcd_support="no"] ) AC_MSG_CHECKING([for apple style tools]) AC_PREPROC_IFELSE([AC_LANG_PROGRAM([ #ifndef __APPLE__ #error 1 #endif],[])], [AC_MSG_RESULT([yes]); ARM2GNU_PARAMS="--apple"], [AC_MSG_RESULT([no]); ARM2GNU_PARAMS=""]) AC_SUBST(ARM2GNU_PARAMS) ], [ AC_MSG_WARN( [*** ARM assembly requires perl -- disabling optimizations]) asm_optimization="(missing perl dependency for ARM)" ]) ]) ;; esac ],[ inline_optimization="disabled" asm_optimization="disabled" ]) AM_CONDITIONAL([OPUS_ARM_INLINE_ASM], [test x"${inline_optimization%% *}" = x"ARM"]) AM_CONDITIONAL([OPUS_ARM_EXTERNAL_ASM], [test x"${asm_optimization%% *}" = x"ARM"]) AM_CONDITIONAL([HAVE_SSE], [false]) AM_CONDITIONAL([HAVE_SSE2], [false]) AM_CONDITIONAL([HAVE_SSE4_1], [false]) AM_CONDITIONAL([HAVE_AVX], [false]) m4_define([DEFAULT_X86_SSE_CFLAGS], [-msse]) m4_define([DEFAULT_X86_SSE2_CFLAGS], [-msse2]) m4_define([DEFAULT_X86_SSE4_1_CFLAGS], [-msse4.1]) m4_define([DEFAULT_X86_AVX_CFLAGS], [-mavx]) m4_define([DEFAULT_ARM_NEON_INTR_CFLAGS], [-mfpu=neon]) # With GCC on ARM32 softfp architectures (e.g. Android, or older Ubuntu) you need to specify # -mfloat-abi=softfp for -mfpu=neon to work. However, on ARM32 hardfp architectures (e.g. newer Ubuntu), # this option will break things. # As a heuristic, if host matches arm*eabi* but not arm*hf*, it's probably soft-float. m4_define([DEFAULT_ARM_NEON_SOFTFP_INTR_CFLAGS], [-mfpu=neon -mfloat-abi=softfp]) AS_CASE([$host], [arm*hf*], [AS_VAR_SET([RESOLVED_DEFAULT_ARM_NEON_INTR_CFLAGS], "DEFAULT_ARM_NEON_INTR_CFLAGS")], [arm*eabi*], [AS_VAR_SET([RESOLVED_DEFAULT_ARM_NEON_INTR_CFLAGS], "DEFAULT_ARM_NEON_SOFTFP_INTR_CFLAGS")], [AS_VAR_SET([RESOLVED_DEFAULT_ARM_NEON_INTR_CFLAGS], "DEFAULT_ARM_NEON_INTR_CFLAGS")]) AC_ARG_VAR([X86_SSE_CFLAGS], [C compiler flags to compile SSE intrinsics @<:@default=]DEFAULT_X86_SSE_CFLAGS[@:>@]) AC_ARG_VAR([X86_SSE2_CFLAGS], [C compiler flags to compile SSE2 intrinsics @<:@default=]DEFAULT_X86_SSE2_CFLAGS[@:>@]) AC_ARG_VAR([X86_SSE4_1_CFLAGS], [C compiler flags to compile SSE4.1 intrinsics @<:@default=]DEFAULT_X86_SSE4_1_CFLAGS[@:>@]) AC_ARG_VAR([X86_AVX_CFLAGS], [C compiler flags to compile AVX intrinsics @<:@default=]DEFAULT_X86_AVX_CFLAGS[@:>@]) AC_ARG_VAR([ARM_NEON_INTR_CFLAGS], [C compiler flags to compile ARM NEON intrinsics @<:@default=]DEFAULT_ARM_NEON_INTR_CFLAGS / DEFAULT_ARM_NEON_SOFTFP_INTR_CFLAGS[@:>@]) AS_VAR_SET_IF([X86_SSE_CFLAGS], [], [AS_VAR_SET([X86_SSE_CFLAGS], "DEFAULT_X86_SSE_CFLAGS")]) AS_VAR_SET_IF([X86_SSE2_CFLAGS], [], [AS_VAR_SET([X86_SSE2_CFLAGS], "DEFAULT_X86_SSE2_CFLAGS")]) AS_VAR_SET_IF([X86_SSE4_1_CFLAGS], [], [AS_VAR_SET([X86_SSE4_1_CFLAGS], "DEFAULT_X86_SSE4_1_CFLAGS")]) AS_VAR_SET_IF([X86_AVX_CFLAGS], [], [AS_VAR_SET([X86_AVX_CFLAGS], "DEFAULT_X86_AVX_CFLAGS")]) AS_VAR_SET_IF([ARM_NEON_INTR_CFLAGS], [], [AS_VAR_SET([ARM_NEON_INTR_CFLAGS], ["$RESOLVED_DEFAULT_ARM_NEON_INTR_CFLAGS"])]) AC_DEFUN([OPUS_PATH_NE10], [ AC_ARG_WITH(NE10, AC_HELP_STRING([--with-NE10=PFX],[Prefix where libNE10 is installed (optional)]), NE10_prefix="$withval", NE10_prefix="") AC_ARG_WITH(NE10-libraries, AC_HELP_STRING([--with-NE10-libraries=DIR], [Directory where libNE10 library is installed (optional)]), NE10_libraries="$withval", NE10_libraries="") AC_ARG_WITH(NE10-includes, AC_HELP_STRING([--with-NE10-includes=DIR], [Directory where libNE10 header files are installed (optional)]), NE10_includes="$withval", NE10_includes="") if test "x$NE10_libraries" != "x" ; then NE10_LIBS="-L$NE10_libraries" elif test "x$NE10_prefix" = "xno" || test "x$NE10_prefix" = "xyes" ; then NE10_LIBS="" elif test "x$NE10_prefix" != "x" ; then NE10_LIBS="-L$NE10_prefix/lib" elif test "x$prefix" != "xNONE" ; then NE10_LIBS="-L$prefix/lib" fi if test "x$NE10_prefix" != "xno" ; then NE10_LIBS="$NE10_LIBS -lNE10" fi if test "x$NE10_includes" != "x" ; then NE10_CFLAGS="-I$NE10_includes" elif test "x$NE10_prefix" = "xno" || test "x$NE10_prefix" = "xyes" ; then NE10_CFLAGS="" elif test "x$ogg_prefix" != "x" ; then NE10_CFLAGS="-I$NE10_prefix/include" elif test "x$prefix" != "xNONE"; then NE10_CFLAGS="-I$prefix/include" fi AC_MSG_CHECKING(for NE10) save_CFLAGS="$CFLAGS"; CFLAGS="$NE10_CFLAGS" save_LIBS="$LIBS"; LIBS="$NE10_LIBS $LIBM" AC_LINK_IFELSE( [ AC_LANG_PROGRAM( [[#include ]], [[ ne10_fft_cfg_float32_t cfg; cfg = ne10_fft_alloc_c2c_float32_neon(480); ]] ) ],[ HAVE_ARM_NE10=1 AC_MSG_RESULT([yes]) ],[ HAVE_ARM_NE10=0 AC_MSG_RESULT([no]) NE10_CFLAGS="" NE10_LIBS="" ] ) CFLAGS="$save_CFLAGS"; LIBS="$save_LIBS" #Now we know if libNE10 is installed or not AS_IF([test x"$HAVE_ARM_NE10" = x"1"], [ AC_DEFINE([HAVE_ARM_NE10], 1, [NE10 library is installed on host. Make sure it is on target!]) AC_SUBST(HAVE_ARM_NE10) AC_SUBST(NE10_CFLAGS) AC_SUBST(NE10_LIBS) ] ) ] ) AS_IF([test x"$enable_intrinsics" = x"yes"],[ intrinsics_support="" AS_CASE([$host_cpu], [arm*], [ cpu_arm=yes OPUS_CHECK_INTRINSICS( [ARM Neon], [$ARM_NEON_INTR_CFLAGS], [OPUS_ARM_MAY_HAVE_NEON_INTR], [OPUS_ARM_PRESUME_NEON_INTR], [[#include ]], [[ static float32x4_t A0, A1, SUMM; SUMM = vmlaq_f32(SUMM, A0, A1); ]] ) AS_IF([test x"$OPUS_ARM_MAY_HAVE_NEON_INTR" = x"1" && test x"$OPUS_ARM_PRESUME_NEON_INTR" != x"1"], [ OPUS_ARM_NEON_INTR_CFLAGS="$ARM_NEON_INTR_CFLAGS" AC_SUBST([OPUS_ARM_NEON_INTR_CFLAGS]) ] ) AS_IF([test x"$OPUS_ARM_MAY_HAVE_NEON_INTR" = x"1"], [ AC_DEFINE([OPUS_ARM_MAY_HAVE_NEON_INTR], 1, [Compiler supports ARMv7 Neon Intrinsics]) intrinsics_support="$intrinsics_support (Neon_Intrinsics)" AS_IF([test x"enable_rtcd" != x"" && test x"$OPUS_ARM_PRESUME_NEON_INTR" != x"1"], [rtcd_support="$rtcd_support (ARMv7_Neon_Intrinsics)"]) AS_IF([test x"$OPUS_ARM_PRESUME_NEON_INTR" = x"1"], [AC_DEFINE([OPUS_ARM_PRESUME_NEON_INTR], 1, [Define if binary requires NEON intrinsics support])]) OPUS_PATH_NE10() AS_IF([test x"$NE10_LIBS" != x""], [ intrinsics_support="$intrinsics_support (NE10)" AS_IF([test x"enable_rtcd" != x"" \ && test x"$OPUS_ARM_PRESUME_NEON_INTR" != x"1"], [rtcd_support="$rtcd_support (NE10)"]) ]) AS_IF([test x"$rtcd_support" = x""], [rtcd_support=no]) AS_IF([test x"$intrinsics_support" = x""], [intrinsics_support=no], [intrinsics_support="arm$intrinsics_support"]) ], [ AC_MSG_WARN([Compiler does not support ARM intrinsics]) intrinsics_support=no ]) ], [i?86|x86_64], [ OPUS_CHECK_INTRINSICS( [SSE], [$X86_SSE_CFLAGS], [OPUS_X86_MAY_HAVE_SSE], [OPUS_X86_PRESUME_SSE], [[#include ]], [[ static __m128 mtest; mtest = _mm_setzero_ps(); ]] ) AS_IF([test x"$OPUS_X86_MAY_HAVE_SSE" = x"1" && test x"$OPUS_X86_PRESUME_SSE" != x"1"], [ OPUS_X86_SSE_CFLAGS="$X86_SSE_CFLAGS" AC_SUBST([OPUS_X86_SSE_CFLAGS]) ] ) OPUS_CHECK_INTRINSICS( [SSE2], [$X86_SSE2_CFLAGS], [OPUS_X86_MAY_HAVE_SSE2], [OPUS_X86_PRESUME_SSE2], [[#include ]], [[ static __m128i mtest; mtest = _mm_setzero_si128(); ]] ) AS_IF([test x"$OPUS_X86_MAY_HAVE_SSE2" = x"1" && test x"$OPUS_X86_PRESUME_SSE2" != x"1"], [ OPUS_X86_SSE2_CFLAGS="$X86_SSE2_CFLAGS" AC_SUBST([OPUS_X86_SSE2_CFLAGS]) ] ) OPUS_CHECK_INTRINSICS( [SSE4.1], [$X86_SSE4_1_CFLAGS], [OPUS_X86_MAY_HAVE_SSE4_1], [OPUS_X86_PRESUME_SSE4_1], [[#include ]], [[ static __m128i mtest; mtest = _mm_setzero_si128(); mtest = _mm_cmpeq_epi64(mtest, mtest); ]] ) AS_IF([test x"$OPUS_X86_MAY_HAVE_SSE4_1" = x"1" && test x"$OPUS_X86_PRESUME_SSE4_1" != x"1"], [ OPUS_X86_SSE4_1_CFLAGS="$X86_SSE4_1_CFLAGS" AC_SUBST([OPUS_X86_SSE4_1_CFLAGS]) ] ) OPUS_CHECK_INTRINSICS( [AVX], [$X86_AVX_CFLAGS], [OPUS_X86_MAY_HAVE_AVX], [OPUS_X86_PRESUME_AVX], [[#include ]], [[ static __m256 mtest; mtest = _mm256_setzero_ps(); ]] ) AS_IF([test x"$OPUS_X86_MAY_HAVE_AVX" = x"1" && test x"$OPUS_X86_PRESUME_AVX" != x"1"], [ OPUS_X86_AVX_CFLAGS="$X86_AVX_CFLAGS" AC_SUBST([OPUS_X86_AVX_CFLAGS]) ] ) AS_IF([test x"$rtcd_support" = x"no"], [rtcd_support=""]) AS_IF([test x"$OPUS_X86_MAY_HAVE_SSE" = x"1"], [ AC_DEFINE([OPUS_X86_MAY_HAVE_SSE], 1, [Compiler supports X86 SSE Intrinsics]) intrinsics_support="$intrinsics_support SSE" AS_IF([test x"$OPUS_X86_PRESUME_SSE" = x"1"], [AC_DEFINE([OPUS_X86_PRESUME_SSE], 1, [Define if binary requires SSE intrinsics support])], [rtcd_support="$rtcd_support SSE"]) ], [ AC_MSG_WARN([Compiler does not support SSE intrinsics]) ]) AS_IF([test x"$OPUS_X86_MAY_HAVE_SSE2" = x"1"], [ AC_DEFINE([OPUS_X86_MAY_HAVE_SSE2], 1, [Compiler supports X86 SSE2 Intrinsics]) intrinsics_support="$intrinsics_support SSE2" AS_IF([test x"$OPUS_X86_PRESUME_SSE2" = x"1"], [AC_DEFINE([OPUS_X86_PRESUME_SSE2], 1, [Define if binary requires SSE2 intrinsics support])], [rtcd_support="$rtcd_support SSE2"]) ], [ AC_MSG_WARN([Compiler does not support SSE2 intrinsics]) ]) AS_IF([test x"$OPUS_X86_MAY_HAVE_SSE4_1" = x"1"], [ AC_DEFINE([OPUS_X86_MAY_HAVE_SSE4_1], 1, [Compiler supports X86 SSE4.1 Intrinsics]) intrinsics_support="$intrinsics_support SSE4.1" AS_IF([test x"$OPUS_X86_PRESUME_SSE4_1" = x"1"], [AC_DEFINE([OPUS_X86_PRESUME_SSE4_1], 1, [Define if binary requires SSE4.1 intrinsics support])], [rtcd_support="$rtcd_support SSE4.1"]) ], [ AC_MSG_WARN([Compiler does not support SSE4.1 intrinsics]) ]) AS_IF([test x"$OPUS_X86_MAY_HAVE_AVX" = x"1"], [ AC_DEFINE([OPUS_X86_MAY_HAVE_AVX], 1, [Compiler supports X86 AVX Intrinsics]) intrinsics_support="$intrinsics_support AVX" AS_IF([test x"$OPUS_X86_PRESUME_AVX" = x"1"], [AC_DEFINE([OPUS_X86_PRESUME_AVX], 1, [Define if binary requires AVX intrinsics support])], [rtcd_support="$rtcd_support AVX"]) ], [ AC_MSG_WARN([Compiler does not support AVX intrinsics]) ]) AS_IF([test x"$intrinsics_support" = x""], [intrinsics_support=no], [intrinsics_support="x86$intrinsics_support"] ) AS_IF([test x"$rtcd_support" = x""], [rtcd_support=no], [rtcd_support="x86$rtcd_support"], ) AS_IF([test x"$enable_rtcd" = x"yes" && test x"$rtcd_support" != x""],[ get_cpuid_by_asm="no" AC_MSG_CHECKING([How to get X86 CPU Info]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]],[[ unsigned int CPUInfo0; unsigned int CPUInfo1; unsigned int CPUInfo2; unsigned int CPUInfo3; unsigned int InfoType; __asm__ __volatile__ ( "cpuid": "=a" (CPUInfo0), "=b" (CPUInfo1), "=c" (CPUInfo2), "=d" (CPUInfo3) : "a" (InfoType), "c" (0) ); ]])], [get_cpuid_by_asm="yes" AC_MSG_RESULT([Inline Assembly]) AC_DEFINE([CPU_INFO_BY_ASM], [1], [Get CPU Info by asm method])], [AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]],[[ unsigned int CPUInfo0; unsigned int CPUInfo1; unsigned int CPUInfo2; unsigned int CPUInfo3; unsigned int InfoType; __get_cpuid(InfoType, &CPUInfo0, &CPUInfo1, &CPUInfo2, &CPUInfo3); ]])], [AC_MSG_RESULT([C method]) AC_DEFINE([CPU_INFO_BY_C], [1], [Get CPU Info by c method])], [AC_MSG_ERROR([no supported Get CPU Info method, please disable intrinsics])])])]) ], [ AC_MSG_WARN([No intrinsics support for your architecture]) intrinsics_support="no" ]) ], [ intrinsics_support="no" ]) AM_CONDITIONAL([CPU_ARM], [test "$cpu_arm" = "yes"]) AM_CONDITIONAL([OPUS_ARM_NEON_INTR], [test x"$OPUS_ARM_MAY_HAVE_NEON_INTR" = x"1"]) AM_CONDITIONAL([HAVE_ARM_NE10], [test x"$HAVE_ARM_NE10" = x"1"]) AM_CONDITIONAL([HAVE_SSE], [test x"$OPUS_X86_MAY_HAVE_SSE" = x"1"]) AM_CONDITIONAL([HAVE_SSE2], [test x"$OPUS_X86_MAY_HAVE_SSE2" = x"1"]) AM_CONDITIONAL([HAVE_SSE4_1], [test x"$OPUS_X86_MAY_HAVE_SSE4_1" = x"1"]) AM_CONDITIONAL([HAVE_AVX], [test x"$OPUS_X86_MAY_HAVE_AVX" = x"1"]) AS_IF([test x"$enable_rtcd" = x"yes"],[ AS_IF([test x"$rtcd_support" != x"no"],[ AC_DEFINE([OPUS_HAVE_RTCD], [1], [Use run-time CPU capabilities detection]) OPUS_HAVE_RTCD=1 AC_SUBST(OPUS_HAVE_RTCD) ]) ],[ rtcd_support="disabled" ]) AC_ARG_ENABLE([assertions], [AS_HELP_STRING([--enable-assertions],[enable additional software error checking])],, [enable_assertions=no]) AS_IF([test "$enable_assertions" = "yes"], [ AC_DEFINE([ENABLE_ASSERTIONS], [1], [Assertions]) ]) AC_ARG_ENABLE([fuzzing], [AS_HELP_STRING([--enable-fuzzing],[causes the encoder to make random decisions])],, [enable_fuzzing=no]) AS_IF([test "$enable_fuzzing" = "yes"], [ AC_DEFINE([FUZZING], [1], [Fuzzing]) ]) AC_ARG_ENABLE([doc], [AS_HELP_STRING([--disable-doc], [Do not build API documentation])],, [enable_doc=yes]) AS_IF([test "$enable_doc" = "yes"], [ AC_CHECK_PROG(HAVE_DOXYGEN, [doxygen], [yes], [no]) ],[ HAVE_DOXYGEN=no ]) AM_CONDITIONAL([HAVE_DOXYGEN], [test "$HAVE_DOXYGEN" = "yes"]) AC_ARG_ENABLE([extra-programs], [AS_HELP_STRING([--disable-extra-programs], [Do not build extra programs (demo and tests)])],, [enable_extra_programs=yes]) AM_CONDITIONAL([EXTRA_PROGRAMS], [test "$enable_extra_programs" = "yes"]) saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fvisibility=hidden" AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) CFLAGS="$saved_CFLAGS" ]) CFLAGS="$CFLAGS -W" warn_CFLAGS="-Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes" saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $warn_CFLAGS" AC_MSG_CHECKING([if ${CC} supports ${warn_CFLAGS}]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) CFLAGS="$saved_CFLAGS" ]) saved_LIBS="$LIBS" LIBS="$LIBS $LIBM" AC_CHECK_FUNCS([lrintf]) AC_CHECK_FUNCS([lrint]) LIBS="$saved_LIBS" AC_CHECK_FUNCS([__malloc_hook]) AC_SUBST([PC_BUILD]) AC_CONFIG_FILES([ Makefile opus.pc opus-uninstalled.pc celt/arm/armopts.s doc/Makefile doc/Doxyfile ]) AC_CONFIG_HEADERS([config.h]) AC_OUTPUT AC_MSG_NOTICE([ ------------------------------------------------------------------------ $PACKAGE_NAME $PACKAGE_VERSION: Automatic configuration OK. Compiler support: C99 var arrays: ................ ${has_var_arrays} C99 lrintf: .................... ${ac_cv_func_lrintf} Use alloca: .................... ${use_alloca} General configuration: Floating point support: ........ ${enable_float} Fast float approximations: ..... ${enable_float_approx} Fixed point debugging: ......... ${enable_fixed_point_debug} Inline Assembly Optimizations: . ${inline_optimization} External Assembly Optimizations: ${asm_optimization} Intrinsics Optimizations.......: ${intrinsics_support} Run-time CPU detection: ........ ${rtcd_support} Custom modes: .................. ${enable_custom_modes} Assertion checking: ............ ${enable_assertions} Fuzzing: ....................... ${enable_fuzzing} API documentation: ............. ${enable_doc} Extra programs: ................ ${enable_extra_programs} ------------------------------------------------------------------------ Type "make; make install" to compile and install Type "make check" to run the test suite ]) ================================================ FILE: deps/pjsip/third_party/opus/doc/Doxyfile.in ================================================ # Doxyfile 1.7.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = Opus # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = @VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "Opus audio codec (RFC 6716): API and operations manual" # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = @top_srcdir@/include/opus.h \ @top_srcdir@/include/opus_types.h \ @top_srcdir@/include/opus_defines.h \ @top_srcdir@/include/opus_multistream.h \ @top_srcdir@/include/opus_custom.h # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = @top_srcdir@/doc/header.html # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = @top_srcdir@/doc/footer.html # If the HTML_TIMESTAMP tag is set to YES then the generated HTML documentation will contain the timesstamp. HTML_TIMESTAMP = NO # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = @top_srcdir@/doc/customdoxygen.css # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = @top_srcdir@/doc/opus_logo.svg # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 0 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the # mathjax.org site, so you can quickly see the result without installing # MathJax, but it is strongly recommended to install a local copy of MathJax # before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = letter # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = YES # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = OPUS_EXPORT= OPUS_CUSTOM_EXPORT= OPUS_CUSTOM_EXPORT_STATIC= OPUS_WARN_UNUSED_RESULT= OPUS_ARG_NONNULL(_x)= # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called Helvetica to the output # directory and reference it in all dot files that doxygen generates. # When you want a differently looking font you can specify the font name # using DOT_FONTNAME. You need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES ================================================ FILE: deps/pjsip/third_party/opus/doc/Makefile.am ================================================ ## Process this file with automake to produce Makefile.in DOCINPUTS = $(top_srcdir)/include/opus.h \ $(top_srcdir)/include/opus_multistream.h \ $(top_srcdir)/include/opus_defines.h \ $(top_srcdir)/include/opus_types.h \ $(top_srcdir)/include/opus_custom.h \ $(top_srcdir)/doc/header.html \ $(top_srcdir)/doc/footer.html \ $(top_srcdir)/doc/customdoxygen.css EXTRA_DIST = customdoxygen.css Doxyfile.in footer.html header.html \ opus_logo.svg trivial_example.c if HAVE_DOXYGEN all-local: doxygen-build.stamp doxygen-build.stamp: Doxyfile $(DOCINPUTS) doxygen touch $@ install-data-local: $(INSTALL) -d $(DESTDIR)$(docdir)/html/search for f in `find html -type f \! -name "installdox"`; do \ $(INSTALL_DATA) $$f $(DESTDIR)$(docdir)/$$f; \ done $(INSTALL) -d $(DESTDIR)$(mandir)/man3 cd man && find man3 -type f -name opus_*.3 \ -exec $(INSTALL_DATA) \{} $(DESTDIR)$(mandir)/man3 \; clean-local: $(RM) -r html $(RM) -r latex $(RM) -r man $(RM) doxygen-build.stamp uninstall-local: $(RM) -r $(DESTDIR)$(docdir)/html $(RM) $(DESTDIR)$(mandir)/man3/opus_*.3 $(DESTDIR)$(mandir)/man3/opus.h.3 endif ================================================ FILE: deps/pjsip/third_party/opus/doc/TODO ================================================ define audio bandwidth as frequency range repeat padding recommendation ptime: refer to RFC Opus does not provide any confidentiality or integrity protection ================================================ FILE: deps/pjsip/third_party/opus/doc/customdoxygen.css ================================================ /* The standard CSS for doxygen */ body, table, div, p, dl { font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; font-size: 13px; line-height: 1.3; } /* @group Heading Levels */ h1 { font-size: 150%; } .title { font-size: 150%; font-weight: bold; margin: 10px 2px; } h2 { font-size: 120%; } h3 { font-size: 100%; } dt { font-weight: bold; } div.multicol { -moz-column-gap: 1em; -webkit-column-gap: 1em; -moz-column-count: 3; -webkit-column-count: 3; } p.startli, p.startdd, p.starttd { margin-top: 2px; } p.endli { margin-bottom: 0px; } p.enddd { margin-bottom: 4px; } p.endtd { margin-bottom: 2px; } /* @end */ caption { font-weight: bold; } span.legend { font-size: 70%; text-align: center; } h3.version { font-size: 90%; text-align: center; } div.qindex, div.navtab{ background-color: #F1F1F1; border: 1px solid #BDBDBD; text-align: center; } div.qindex, div.navpath { width: 100%; line-height: 140%; } div.navtab { margin-right: 15px; } /* @group Link Styling */ a { color: #646464; font-weight: normal; text-decoration: none; } .contents a:visited { color: #747474; } a:hover { text-decoration: underline; } a.qindex { font-weight: bold; } a.qindexHL { font-weight: bold; background-color: #B8B8B8; color: #ffffff; border: 1px double #A8A8A8; } .contents a.qindexHL:visited { color: #ffffff; } a.el { font-weight: bold; } a.elRef { } a.code, a.code:visited { color: #4665A2; } a.codeRef, a.codeRef:visited { color: #4665A2; } /* @end */ dl.el { margin-left: -1cm; } .fragment { font-family: monospace, fixed; font-size: 105%; } pre.fragment { border: 1px solid #D5D5D5; background-color: #FCFCFC; padding: 4px 6px; margin: 4px 8px 4px 2px; overflow: auto; word-wrap: break-word; font-size: 9pt; line-height: 125%; } div.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px; padding: 0.2em; border: solid thin #333; border-radius: 0.5em; -webkit-border-radius: .5em; -moz-border-radius: .5em; box-shadow: 2px 2px 3px #999; -webkit-box-shadow: 2px 2px 3px #999; -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); } div.groupHeader { margin-left: 16px; margin-top: 12px; font-weight: bold; } div.groupText { margin-left: 16px; font-style: italic; } body { background-color: white; color: black; margin: 0; } div.contents { margin-top: 10px; margin-left: 8px; margin-right: 8px; } td.indexkey { background-color: #F1F1F1; font-weight: bold; border: 1px solid #D5D5D5; margin: 2px 0px 2px 0; padding: 2px 10px; white-space: nowrap; vertical-align: top; } td.indexvalue { background-color: #F1F1F1; border: 1px solid #D5D5D5; padding: 2px 10px; margin: 2px 0px; } tr.memlist { background-color: #F2F2F2; } p.formulaDsp { text-align: center; } img.formulaDsp { } img.formulaInl { vertical-align: middle; } div.center { text-align: center; margin-top: 0px; margin-bottom: 0px; padding: 0px; } div.center img { border: 0px; } address.footer { text-align: right; padding-right: 12px; } img.footer { border: 0px; vertical-align: middle; } /* @group Code Colorization */ span.keyword { color: #008000 } span.keywordtype { color: #604020 } span.keywordflow { color: #e08000 } span.comment { color: #800000 } span.preprocessor { color: #806020 } span.stringliteral { color: #002080 } span.charliteral { color: #008080 } span.vhdldigit { color: #ff00ff } span.vhdlchar { color: #000000 } span.vhdlkeyword { color: #700070 } span.vhdllogic { color: #ff0000 } blockquote { background-color: #F9F9F9; border-left: 2px solid #B8B8B8; margin: 0 24px 0 4px; padding: 0 12px 0 16px; } /* @end */ /* .search { color: #003399; font-weight: bold; } form.search { margin-bottom: 0px; margin-top: 0px; } input.search { font-size: 75%; color: #000080; font-weight: normal; background-color: #e8eef2; } */ td.tiny { font-size: 75%; } .dirtab { padding: 4px; border-collapse: collapse; border: 1px solid #BDBDBD; } th.dirtab { background: #F1F1F1; font-weight: bold; } hr { height: 0px; border: none; border-top: 1px solid #7A7A7A; } hr.footer { height: 1px; } /* @group Member Descriptions */ table.memberdecls { border-spacing: 0px; padding: 0px; } .mdescLeft, .mdescRight, .memItemLeft, .memItemRight, .memTemplItemLeft, .memTemplItemRight, .memTemplParams { background-color: #FAFAFA; border: none; margin: 4px; padding: 1px 0 0 8px; } .mdescLeft, .mdescRight { padding: 0px 8px 4px 8px; color: #555; } .memItemLeft, .memItemRight, .memTemplParams { border-top: 1px solid #D5D5D5; } .memItemLeft, .memTemplItemLeft { white-space: nowrap; } .memItemRight { width: 100%; } .memTemplParams { color: #747474; white-space: nowrap; } /* @end */ /* @group Member Details */ /* Styles for detailed member documentation */ .memtemplate { font-size: 80%; color: #747474; font-weight: normal; margin-left: 9px; } .memnav { background-color: #F1F1F1; border: 1px solid #BDBDBD; text-align: center; margin: 2px; margin-right: 15px; padding: 2px; } .mempage { width: 100%; } .memitem { padding: 0; margin-bottom: 10px; margin-right: 5px; } .memname { white-space: nowrap; font-weight: bold; margin-left: 6px; } .memproto, dl.reflist dt { border-top: 1px solid #C0C0C0; border-left: 1px solid #C0C0C0; border-right: 1px solid #C0C0C0; padding: 6px 0px 6px 0px; color: #3D3D3D; font-weight: bold; text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); /* opera specific markup */ box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); border-top-right-radius: 8px; border-top-left-radius: 8px; /* firefox specific markup */ -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; -moz-border-radius-topright: 8px; -moz-border-radius-topleft: 8px; /* webkit specific markup */ -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); -webkit-border-top-right-radius: 8px; -webkit-border-top-left-radius: 8px; background-image:url('nav_f.png'); background-repeat:repeat-x; background-color: #EAEAEA; } .memdoc, dl.reflist dd { border-bottom: 1px solid #C0C0C0; border-left: 1px solid #C0C0C0; border-right: 1px solid #C0C0C0; padding: 2px 5px; background-color: #FCFCFC; border-top-width: 0; /* opera specific markup */ border-bottom-left-radius: 8px; border-bottom-right-radius: 8px; box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); /* firefox specific markup */ -moz-border-radius-bottomleft: 8px; -moz-border-radius-bottomright: 8px; -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F9F9F9 95%, #F2F2F2); /* webkit specific markup */ -webkit-border-bottom-left-radius: 8px; -webkit-border-bottom-right-radius: 8px; -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F9F9F9), to(#F2F2F2)); } dl.reflist dt { padding: 5px; } dl.reflist dd { margin: 0px 0px 10px 0px; padding: 5px; } .paramkey { text-align: right; } .paramtype { white-space: nowrap; } .paramname { color: #602020; white-space: nowrap; } .paramname em { font-style: normal; } .params, .retval, .exception, .tparams { border-spacing: 6px 2px; } .params .paramname, .retval .paramname { font-weight: bold; vertical-align: top; } .params .paramtype { font-style: italic; vertical-align: top; } .params .paramdir { font-family: "courier new",courier,monospace; vertical-align: top; } /* @end */ /* @group Directory (tree) */ /* for the tree view */ .ftvtree { font-family: sans-serif; margin: 0px; } /* these are for tree view when used as main index */ .directory { font-size: 9pt; font-weight: bold; margin: 5px; } .directory h3 { margin: 0px; margin-top: 1em; font-size: 11pt; } /* The following two styles can be used to replace the root node title with an image of your choice. Simply uncomment the next two styles, specify the name of your image and be sure to set 'height' to the proper pixel height of your image. */ /* .directory h3.swap { height: 61px; background-repeat: no-repeat; background-image: url("yourimage.gif"); } .directory h3.swap span { display: none; } */ .directory > h3 { margin-top: 0; } .directory p { margin: 0px; white-space: nowrap; } .directory div { display: none; margin: 0px; } .directory img { vertical-align: -30%; } /* these are for tree view when not used as main index */ .directory-alt { font-size: 100%; font-weight: bold; } .directory-alt h3 { margin: 0px; margin-top: 1em; font-size: 11pt; } .directory-alt > h3 { margin-top: 0; } .directory-alt p { margin: 0px; white-space: nowrap; } .directory-alt div { display: none; margin: 0px; } .directory-alt img { vertical-align: -30%; } /* @end */ div.dynheader { margin-top: 8px; } address { font-style: normal; color: #464646; } table.doxtable { border-collapse:collapse; margin-top: 4px; margin-bottom: 4px; } table.doxtable td, table.doxtable th { border: 1px solid #4A4A4A; padding: 3px 7px 2px; } table.doxtable th { background-color: #5B5B5B; color: #FFFFFF; font-size: 110%; padding-bottom: 4px; padding-top: 5px; } table.fieldtable { width: 100%; margin-bottom: 10px; border: 1px solid #C0C0C0; border-spacing: 0px; -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); } .fieldtable td, .fieldtable th { padding: 3px 7px 2px; } .fieldtable td.fieldtype, .fieldtable td.fieldname { white-space: nowrap; border-right: 1px solid #C0C0C0; border-bottom: 1px solid #C0C0C0; vertical-align: top; } .fieldtable td.fielddoc { border-bottom: 1px solid #C0C0C0; width: 100%; } .fieldtable tr:last-child td { border-bottom: none; } .fieldtable th { background-image:url('nav_f.png'); background-repeat:repeat-x; background-color: #EAEAEA; font-size: 90%; color: #3D3D3D; padding-bottom: 4px; padding-top: 5px; text-align:left; -moz-border-radius-topleft: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-left-radius: 4px; -webkit-border-top-right-radius: 4px; border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom: 1px solid #C0C0C0; } .tabsearch { top: 0px; left: 10px; height: 36px; background-image: url('tab_b.png'); z-index: 101; overflow: hidden; font-size: 13px; } .navpath ul { font-size: 11px; background-image:url('tab_b.png'); background-repeat:repeat-x; height:30px; line-height:30px; color:#ABABAB; border:solid 1px #D3D3D3; overflow:hidden; margin:0px; padding:0px; } .navpath li { list-style-type:none; float:left; padding-left:10px; padding-right:15px; background-image:url('bc_s.png'); background-repeat:no-repeat; background-position:right; color:#595959; } .navpath li.navelem a { height:32px; display:block; text-decoration: none; outline: none; } .navpath li.navelem a:hover { color:#929292; } .navpath li.footer { list-style-type:none; float:right; padding-left:10px; padding-right:15px; background-image:none; background-repeat:no-repeat; background-position:right; color:#595959; font-size: 8pt; } div.summary { float: right; font-size: 8pt; padding-right: 5px; width: 50%; text-align: right; } div.summary a { white-space: nowrap; } div.ingroups { margin-left: 5px; font-size: 8pt; padding-left: 5px; width: 50%; text-align: left; } div.ingroups a { white-space: nowrap; } div.header { background-image:url('nav_h.png'); background-repeat:repeat-x; background-color: #FAFAFA; margin: 0px; border-bottom: 1px solid #D5D5D5; } div.headertitle { padding: 5px 5px 5px 7px; } dl { padding: 0 0 0 10px; } /* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */ dl.section { border-left:4px solid; padding: 0 0 0 6px; } dl.note { border-color: #D0C000; } dl.warning, dl.attention { border-color: #FF0000; } dl.pre, dl.post, dl.invariant { border-color: #00D000; } dl.deprecated { border-color: #505050; } dl.todo { border-color: #00C0E0; } dl.test { border-color: #3030E0; } dl.bug { border-color: #C08050; } dl.section dd { margin-bottom: 6px; } #projectlogo { text-align: center; vertical-align: bottom; border-collapse: separate; } #projectlogo img { border: 0px none; } #projectname { font: 300% Tahoma, Arial,sans-serif; margin: 0px; padding: 2px 0px; } #projectbrief { font: 120% Tahoma, Arial,sans-serif; margin: 0px; padding: 0px; } #projectnumber { font: 100% Tahoma, Arial,sans-serif; margin: 0px; padding: 0px; } #titlearea { padding: 0px; margin: 0px; width: 100%; border-bottom: 1px solid #848484; } .image { text-align: center; } .dotgraph { text-align: center; } .mscgraph { text-align: center; } .caption { font-weight: bold; } div.zoom { border: 1px solid #AFAFAF; } dl.citelist { margin-bottom:50px; } dl.citelist dt { color:#545454; float:left; font-weight:bold; margin-right:10px; padding:5px; } dl.citelist dd { margin:2px 0; padding:5px 0; } div.toc { padding: 14px 25px; background-color: #F7F7F7; border: 1px solid #E3E3E3; border-radius: 7px 7px 7px 7px; float: right; height: auto; margin: 0 20px 10px 10px; width: 200px; } div.toc li { background: url("bdwn.png") no-repeat scroll 0 5px transparent; font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif; margin-top: 5px; padding-left: 10px; padding-top: 2px; } div.toc h3 { font: bold 12px/1.2 Arial,FreeSans,sans-serif; color: #747474; border-bottom: 0 none; margin: 0; } div.toc ul { list-style: none outside none; border: medium none; padding: 0px; } div.toc li.level1 { margin-left: 0px; } div.toc li.level2 { margin-left: 15px; } div.toc li.level3 { margin-left: 30px; } div.toc li.level4 { margin-left: 45px; } @media print { #top { display: none; } #side-nav { display: none; } #nav-path { display: none; } body { overflow:visible; } h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } .summary { display: none; } .memitem { page-break-inside: avoid; } #doc-content { margin-left:0 !important; height:auto !important; width:auto !important; overflow:inherit; display:inline; } pre.fragment { overflow: visible; text-wrap: unrestricted; white-space: -moz-pre-wrap; /* Moz */ white-space: -pre-wrap; /* Opera 4-6 */ white-space: -o-pre-wrap; /* Opera 7 */ white-space: pre-wrap; /* CSS3 */ word-wrap: break-word; /* IE 5.5+ */ } } ================================================ FILE: deps/pjsip/third_party/opus/doc/footer.html ================================================
For more information visit the Opus Website.
================================================ FILE: deps/pjsip/third_party/opus/doc/header.html ================================================ $projectname: $title $title $treeview $search $mathjax
Opus
$projectbrief
$projectnumber
$projectbrief
$searchbox
================================================ FILE: deps/pjsip/third_party/opus/doc/trivial_example.c ================================================ /* Copyright (c) 2013 Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This is meant to be a simple example of encoding and decoding audio using Opus. It should make it easy to understand how the Opus API works. For more information, see the full API documentation at: http://www.opus-codec.org/docs/ */ #include #include #include #include #include /*The frame size is hardcoded for this sample code but it doesn't have to be*/ #define FRAME_SIZE 960 #define SAMPLE_RATE 48000 #define CHANNELS 2 #define APPLICATION OPUS_APPLICATION_AUDIO #define BITRATE 64000 #define MAX_FRAME_SIZE 6*960 #define MAX_PACKET_SIZE (3*1276) int main(int argc, char **argv) { char *inFile; FILE *fin; char *outFile; FILE *fout; opus_int16 in[FRAME_SIZE*CHANNELS]; opus_int16 out[MAX_FRAME_SIZE*CHANNELS]; unsigned char cbits[MAX_PACKET_SIZE]; int nbBytes; /*Holds the state of the encoder and decoder */ OpusEncoder *encoder; OpusDecoder *decoder; int err; if (argc != 3) { fprintf(stderr, "usage: trivial_example input.pcm output.pcm\n"); fprintf(stderr, "input and output are 16-bit little-endian raw files\n"); return EXIT_FAILURE; } /*Create a new encoder state */ encoder = opus_encoder_create(SAMPLE_RATE, CHANNELS, APPLICATION, &err); if (err<0) { fprintf(stderr, "failed to create an encoder: %s\n", opus_strerror(err)); return EXIT_FAILURE; } /* Set the desired bit-rate. You can also set other parameters if needed. The Opus library is designed to have good defaults, so only set parameters you know you need. Doing otherwise is likely to result in worse quality, but better. */ err = opus_encoder_ctl(encoder, OPUS_SET_BITRATE(BITRATE)); if (err<0) { fprintf(stderr, "failed to set bitrate: %s\n", opus_strerror(err)); return EXIT_FAILURE; } inFile = argv[1]; fin = fopen(inFile, "r"); if (fin==NULL) { fprintf(stderr, "failed to open input file: %s\n", strerror(errno)); return EXIT_FAILURE; } /* Create a new decoder state. */ decoder = opus_decoder_create(SAMPLE_RATE, CHANNELS, &err); if (err<0) { fprintf(stderr, "failed to create decoder: %s\n", opus_strerror(err)); return EXIT_FAILURE; } outFile = argv[2]; fout = fopen(outFile, "w"); if (fout==NULL) { fprintf(stderr, "failed to open output file: %s\n", strerror(errno)); return EXIT_FAILURE; } while (1) { int i; unsigned char pcm_bytes[MAX_FRAME_SIZE*CHANNELS*2]; int frame_size; /* Read a 16 bits/sample audio frame. */ fread(pcm_bytes, sizeof(short)*CHANNELS, FRAME_SIZE, fin); if (feof(fin)) break; /* Convert from little-endian ordering. */ for (i=0;i>8)&0xFF; } /* Write the decoded audio to file. */ fwrite(pcm_bytes, sizeof(short), frame_size*CHANNELS, fout); } /*Destroy the encoder state*/ opus_encoder_destroy(encoder); opus_decoder_destroy(decoder); fclose(fin); fclose(fout); return EXIT_SUCCESS; } ================================================ FILE: deps/pjsip/third_party/opus/include/opus.h ================================================ /* Copyright (c) 2010-2011 Xiph.Org Foundation, Skype Limited Written by Jean-Marc Valin and Koen Vos */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file opus.h * @brief Opus reference implementation API */ #ifndef OPUS_H #define OPUS_H #include "opus_types.h" #include "opus_defines.h" #ifdef __cplusplus extern "C" { #endif /** * @mainpage Opus * * The Opus codec is designed for interactive speech and audio transmission over the Internet. * It is designed by the IETF Codec Working Group and incorporates technology from * Skype's SILK codec and Xiph.Org's CELT codec. * * The Opus codec is designed to handle a wide range of interactive audio applications, * including Voice over IP, videoconferencing, in-game chat, and even remote live music * performances. It can scale from low bit-rate narrowband speech to very high quality * stereo music. Its main features are: * @li Sampling rates from 8 to 48 kHz * @li Bit-rates from 6 kb/s to 510 kb/s * @li Support for both constant bit-rate (CBR) and variable bit-rate (VBR) * @li Audio bandwidth from narrowband to full-band * @li Support for speech and music * @li Support for mono and stereo * @li Support for multichannel (up to 255 channels) * @li Frame sizes from 2.5 ms to 60 ms * @li Good loss robustness and packet loss concealment (PLC) * @li Floating point and fixed-point implementation * * Documentation sections: * @li @ref opus_encoder * @li @ref opus_decoder * @li @ref opus_repacketizer * @li @ref opus_multistream * @li @ref opus_libinfo * @li @ref opus_custom */ /** @defgroup opus_encoder Opus Encoder * @{ * * @brief This page describes the process and functions used to encode Opus. * * Since Opus is a stateful codec, the encoding process starts with creating an encoder * state. This can be done with: * * @code * int error; * OpusEncoder *enc; * enc = opus_encoder_create(Fs, channels, application, &error); * @endcode * * From this point, @c enc can be used for encoding an audio stream. An encoder state * @b must @b not be used for more than one stream at the same time. Similarly, the encoder * state @b must @b not be re-initialized for each frame. * * While opus_encoder_create() allocates memory for the state, it's also possible * to initialize pre-allocated memory: * * @code * int size; * int error; * OpusEncoder *enc; * size = opus_encoder_get_size(channels); * enc = malloc(size); * error = opus_encoder_init(enc, Fs, channels, application); * @endcode * * where opus_encoder_get_size() returns the required size for the encoder state. Note that * future versions of this code may change the size, so no assuptions should be made about it. * * The encoder state is always continuous in memory and only a shallow copy is sufficient * to copy it (e.g. memcpy()) * * It is possible to change some of the encoder's settings using the opus_encoder_ctl() * interface. All these settings already default to the recommended value, so they should * only be changed when necessary. The most common settings one may want to change are: * * @code * opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate)); * opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity)); * opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal_type)); * @endcode * * where * * @arg bitrate is in bits per second (b/s) * @arg complexity is a value from 1 to 10, where 1 is the lowest complexity and 10 is the highest * @arg signal_type is either OPUS_AUTO (default), OPUS_SIGNAL_VOICE, or OPUS_SIGNAL_MUSIC * * See @ref opus_encoderctls and @ref opus_genericctls for a complete list of parameters that can be set or queried. Most parameters can be set or changed at any time during a stream. * * To encode a frame, opus_encode() or opus_encode_float() must be called with exactly one frame (2.5, 5, 10, 20, 40 or 60 ms) of audio data: * @code * len = opus_encode(enc, audio_frame, frame_size, packet, max_packet); * @endcode * * where *
    *
  • audio_frame is the audio data in opus_int16 (or float for opus_encode_float())
  • *
  • frame_size is the duration of the frame in samples (per channel)
  • *
  • packet is the byte array to which the compressed data is written
  • *
  • max_packet is the maximum number of bytes that can be written in the packet (4000 bytes is recommended). * Do not use max_packet to control VBR target bitrate, instead use the #OPUS_SET_BITRATE CTL.
  • *
* * opus_encode() and opus_encode_float() return the number of bytes actually written to the packet. * The return value can be negative, which indicates that an error has occurred. If the return value * is 1 byte, then the packet does not need to be transmitted (DTX). * * Once the encoder state if no longer needed, it can be destroyed with * * @code * opus_encoder_destroy(enc); * @endcode * * If the encoder was created with opus_encoder_init() rather than opus_encoder_create(), * then no action is required aside from potentially freeing the memory that was manually * allocated for it (calling free(enc) for the example above) * */ /** Opus encoder state. * This contains the complete state of an Opus encoder. * It is position independent and can be freely copied. * @see opus_encoder_create,opus_encoder_init */ typedef struct OpusEncoder OpusEncoder; /** Gets the size of an OpusEncoder structure. * @param[in] channels int: Number of channels. * This must be 1 or 2. * @returns The size in bytes. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_encoder_get_size(int channels); /** */ /** Allocates and initializes an encoder state. * There are three coding modes: * * @ref OPUS_APPLICATION_VOIP gives best quality at a given bitrate for voice * signals. It enhances the input signal by high-pass filtering and * emphasizing formants and harmonics. Optionally it includes in-band * forward error correction to protect against packet loss. Use this * mode for typical VoIP applications. Because of the enhancement, * even at high bitrates the output may sound different from the input. * * @ref OPUS_APPLICATION_AUDIO gives best quality at a given bitrate for most * non-voice signals like music. Use this mode for music and mixed * (music/voice) content, broadcast, and applications requiring less * than 15 ms of coding delay. * * @ref OPUS_APPLICATION_RESTRICTED_LOWDELAY configures low-delay mode that * disables the speech-optimized mode in exchange for slightly reduced delay. * This mode can only be set on an newly initialized or freshly reset encoder * because it changes the codec delay. * * This is useful when the caller knows that the speech-optimized modes will not be needed (use with caution). * @param [in] Fs opus_int32: Sampling rate of input signal (Hz) * This must be one of 8000, 12000, 16000, * 24000, or 48000. * @param [in] channels int: Number of channels (1 or 2) in input signal * @param [in] application int: Coding mode (@ref OPUS_APPLICATION_VOIP/@ref OPUS_APPLICATION_AUDIO/@ref OPUS_APPLICATION_RESTRICTED_LOWDELAY) * @param [out] error int*: @ref opus_errorcodes * @note Regardless of the sampling rate and number channels selected, the Opus encoder * can switch to a lower audio bandwidth or number of channels if the bitrate * selected is too low. This also means that it is safe to always use 48 kHz stereo input * and let the encoder optimize the encoding. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusEncoder *opus_encoder_create( opus_int32 Fs, int channels, int application, int *error ); /** Initializes a previously allocated encoder state * The memory pointed to by st must be at least the size returned by opus_encoder_get_size(). * This is intended for applications which use their own allocator instead of malloc. * @see opus_encoder_create(),opus_encoder_get_size() * To reset a previously initialized state, use the #OPUS_RESET_STATE CTL. * @param [in] st OpusEncoder*: Encoder state * @param [in] Fs opus_int32: Sampling rate of input signal (Hz) * This must be one of 8000, 12000, 16000, * 24000, or 48000. * @param [in] channels int: Number of channels (1 or 2) in input signal * @param [in] application int: Coding mode (OPUS_APPLICATION_VOIP/OPUS_APPLICATION_AUDIO/OPUS_APPLICATION_RESTRICTED_LOWDELAY) * @retval #OPUS_OK Success or @ref opus_errorcodes */ OPUS_EXPORT int opus_encoder_init( OpusEncoder *st, opus_int32 Fs, int channels, int application ) OPUS_ARG_NONNULL(1); /** Encodes an Opus frame. * @param [in] st OpusEncoder*: Encoder state * @param [in] pcm opus_int16*: Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(opus_int16) * @param [in] frame_size int: Number of samples per channel in the * input signal. * This must be an Opus frame size for * the encoder's sampling rate. * For example, at 48 kHz the permitted * values are 120, 240, 480, 960, 1920, * and 2880. * Passing in a duration of less than * 10 ms (480 samples at 48 kHz) will * prevent the encoder from using the LPC * or hybrid modes. * @param [out] data unsigned char*: Output payload. * This must contain storage for at * least \a max_data_bytes. * @param [in] max_data_bytes opus_int32: Size of the allocated * memory for the output * payload. This may be * used to impose an upper limit on * the instant bitrate, but should * not be used as the only bitrate * control. Use #OPUS_SET_BITRATE to * control the bitrate. * @returns The length of the encoded packet (in bytes) on success or a * negative error code (see @ref opus_errorcodes) on failure. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_encode( OpusEncoder *st, const opus_int16 *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4); /** Encodes an Opus frame from floating point input. * @param [in] st OpusEncoder*: Encoder state * @param [in] pcm float*: Input in float format (interleaved if 2 channels), with a normal range of +/-1.0. * Samples with a range beyond +/-1.0 are supported but will * be clipped by decoders using the integer API and should * only be used if it is known that the far end supports * extended dynamic range. * length is frame_size*channels*sizeof(float) * @param [in] frame_size int: Number of samples per channel in the * input signal. * This must be an Opus frame size for * the encoder's sampling rate. * For example, at 48 kHz the permitted * values are 120, 240, 480, 960, 1920, * and 2880. * Passing in a duration of less than * 10 ms (480 samples at 48 kHz) will * prevent the encoder from using the LPC * or hybrid modes. * @param [out] data unsigned char*: Output payload. * This must contain storage for at * least \a max_data_bytes. * @param [in] max_data_bytes opus_int32: Size of the allocated * memory for the output * payload. This may be * used to impose an upper limit on * the instant bitrate, but should * not be used as the only bitrate * control. Use #OPUS_SET_BITRATE to * control the bitrate. * @returns The length of the encoded packet (in bytes) on success or a * negative error code (see @ref opus_errorcodes) on failure. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_encode_float( OpusEncoder *st, const float *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4); /** Frees an OpusEncoder allocated by opus_encoder_create(). * @param[in] st OpusEncoder*: State to be freed. */ OPUS_EXPORT void opus_encoder_destroy(OpusEncoder *st); /** Perform a CTL function on an Opus encoder. * * Generally the request and subsequent arguments are generated * by a convenience macro. * @param st OpusEncoder*: Encoder state. * @param request This and all remaining parameters should be replaced by one * of the convenience macros in @ref opus_genericctls or * @ref opus_encoderctls. * @see opus_genericctls * @see opus_encoderctls */ OPUS_EXPORT int opus_encoder_ctl(OpusEncoder *st, int request, ...) OPUS_ARG_NONNULL(1); /**@}*/ /** @defgroup opus_decoder Opus Decoder * @{ * * @brief This page describes the process and functions used to decode Opus. * * The decoding process also starts with creating a decoder * state. This can be done with: * @code * int error; * OpusDecoder *dec; * dec = opus_decoder_create(Fs, channels, &error); * @endcode * where * @li Fs is the sampling rate and must be 8000, 12000, 16000, 24000, or 48000 * @li channels is the number of channels (1 or 2) * @li error will hold the error code in case of failure (or #OPUS_OK on success) * @li the return value is a newly created decoder state to be used for decoding * * While opus_decoder_create() allocates memory for the state, it's also possible * to initialize pre-allocated memory: * @code * int size; * int error; * OpusDecoder *dec; * size = opus_decoder_get_size(channels); * dec = malloc(size); * error = opus_decoder_init(dec, Fs, channels); * @endcode * where opus_decoder_get_size() returns the required size for the decoder state. Note that * future versions of this code may change the size, so no assuptions should be made about it. * * The decoder state is always continuous in memory and only a shallow copy is sufficient * to copy it (e.g. memcpy()) * * To decode a frame, opus_decode() or opus_decode_float() must be called with a packet of compressed audio data: * @code * frame_size = opus_decode(dec, packet, len, decoded, max_size, 0); * @endcode * where * * @li packet is the byte array containing the compressed data * @li len is the exact number of bytes contained in the packet * @li decoded is the decoded audio data in opus_int16 (or float for opus_decode_float()) * @li max_size is the max duration of the frame in samples (per channel) that can fit into the decoded_frame array * * opus_decode() and opus_decode_float() return the number of samples (per channel) decoded from the packet. * If that value is negative, then an error has occurred. This can occur if the packet is corrupted or if the audio * buffer is too small to hold the decoded audio. * * Opus is a stateful codec with overlapping blocks and as a result Opus * packets are not coded independently of each other. Packets must be * passed into the decoder serially and in the correct order for a correct * decode. Lost packets can be replaced with loss concealment by calling * the decoder with a null pointer and zero length for the missing packet. * * A single codec state may only be accessed from a single thread at * a time and any required locking must be performed by the caller. Separate * streams must be decoded with separate decoder states and can be decoded * in parallel unless the library was compiled with NONTHREADSAFE_PSEUDOSTACK * defined. * */ /** Opus decoder state. * This contains the complete state of an Opus decoder. * It is position independent and can be freely copied. * @see opus_decoder_create,opus_decoder_init */ typedef struct OpusDecoder OpusDecoder; /** Gets the size of an OpusDecoder structure. * @param [in] channels int: Number of channels. * This must be 1 or 2. * @returns The size in bytes. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decoder_get_size(int channels); /** Allocates and initializes a decoder state. * @param [in] Fs opus_int32: Sample rate to decode at (Hz). * This must be one of 8000, 12000, 16000, * 24000, or 48000. * @param [in] channels int: Number of channels (1 or 2) to decode * @param [out] error int*: #OPUS_OK Success or @ref opus_errorcodes * * Internally Opus stores data at 48000 Hz, so that should be the default * value for Fs. However, the decoder can efficiently decode to buffers * at 8, 12, 16, and 24 kHz so if for some reason the caller cannot use * data at the full sample rate, or knows the compressed data doesn't * use the full frequency range, it can request decoding at a reduced * rate. Likewise, the decoder is capable of filling in either mono or * interleaved stereo pcm buffers, at the caller's request. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusDecoder *opus_decoder_create( opus_int32 Fs, int channels, int *error ); /** Initializes a previously allocated decoder state. * The state must be at least the size returned by opus_decoder_get_size(). * This is intended for applications which use their own allocator instead of malloc. @see opus_decoder_create,opus_decoder_get_size * To reset a previously initialized state, use the #OPUS_RESET_STATE CTL. * @param [in] st OpusDecoder*: Decoder state. * @param [in] Fs opus_int32: Sampling rate to decode to (Hz). * This must be one of 8000, 12000, 16000, * 24000, or 48000. * @param [in] channels int: Number of channels (1 or 2) to decode * @retval #OPUS_OK Success or @ref opus_errorcodes */ OPUS_EXPORT int opus_decoder_init( OpusDecoder *st, opus_int32 Fs, int channels ) OPUS_ARG_NONNULL(1); /** Decode an Opus packet. * @param [in] st OpusDecoder*: Decoder state * @param [in] data char*: Input payload. Use a NULL pointer to indicate packet loss * @param [in] len opus_int32: Number of bytes in payload* * @param [out] pcm opus_int16*: Output signal (interleaved if 2 channels). length * is frame_size*channels*sizeof(opus_int16) * @param [in] frame_size Number of samples per channel of available space in \a pcm. * If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will * not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=1), * then frame_size needs to be exactly the duration of audio that is missing, otherwise the * decoder will not be in the optimal state to decode the next incoming packet. For the PLC and * FEC cases, frame_size must be a multiple of 2.5 ms. * @param [in] decode_fec int: Flag (0 or 1) to request that any in-band forward error correction data be * decoded. If no such data is available, the frame is decoded as if it were lost. * @returns Number of decoded samples or @ref opus_errorcodes */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decode( OpusDecoder *st, const unsigned char *data, opus_int32 len, opus_int16 *pcm, int frame_size, int decode_fec ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4); /** Decode an Opus packet with floating point output. * @param [in] st OpusDecoder*: Decoder state * @param [in] data char*: Input payload. Use a NULL pointer to indicate packet loss * @param [in] len opus_int32: Number of bytes in payload * @param [out] pcm float*: Output signal (interleaved if 2 channels). length * is frame_size*channels*sizeof(float) * @param [in] frame_size Number of samples per channel of available space in \a pcm. * If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will * not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=1), * then frame_size needs to be exactly the duration of audio that is missing, otherwise the * decoder will not be in the optimal state to decode the next incoming packet. For the PLC and * FEC cases, frame_size must be a multiple of 2.5 ms. * @param [in] decode_fec int: Flag (0 or 1) to request that any in-band forward error correction data be * decoded. If no such data is available the frame is decoded as if it were lost. * @returns Number of decoded samples or @ref opus_errorcodes */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decode_float( OpusDecoder *st, const unsigned char *data, opus_int32 len, float *pcm, int frame_size, int decode_fec ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4); /** Perform a CTL function on an Opus decoder. * * Generally the request and subsequent arguments are generated * by a convenience macro. * @param st OpusDecoder*: Decoder state. * @param request This and all remaining parameters should be replaced by one * of the convenience macros in @ref opus_genericctls or * @ref opus_decoderctls. * @see opus_genericctls * @see opus_decoderctls */ OPUS_EXPORT int opus_decoder_ctl(OpusDecoder *st, int request, ...) OPUS_ARG_NONNULL(1); /** Frees an OpusDecoder allocated by opus_decoder_create(). * @param[in] st OpusDecoder*: State to be freed. */ OPUS_EXPORT void opus_decoder_destroy(OpusDecoder *st); /** Parse an opus packet into one or more frames. * Opus_decode will perform this operation internally so most applications do * not need to use this function. * This function does not copy the frames, the returned pointers are pointers into * the input packet. * @param [in] data char*: Opus packet to be parsed * @param [in] len opus_int32: size of data * @param [out] out_toc char*: TOC pointer * @param [out] frames char*[48] encapsulated frames * @param [out] size opus_int16[48] sizes of the encapsulated frames * @param [out] payload_offset int*: returns the position of the payload within the packet (in bytes) * @returns number of frames */ OPUS_EXPORT int opus_packet_parse( const unsigned char *data, opus_int32 len, unsigned char *out_toc, const unsigned char *frames[48], opus_int16 size[48], int *payload_offset ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4); /** Gets the bandwidth of an Opus packet. * @param [in] data char*: Opus packet * @retval OPUS_BANDWIDTH_NARROWBAND Narrowband (4kHz bandpass) * @retval OPUS_BANDWIDTH_MEDIUMBAND Mediumband (6kHz bandpass) * @retval OPUS_BANDWIDTH_WIDEBAND Wideband (8kHz bandpass) * @retval OPUS_BANDWIDTH_SUPERWIDEBAND Superwideband (12kHz bandpass) * @retval OPUS_BANDWIDTH_FULLBAND Fullband (20kHz bandpass) * @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_bandwidth(const unsigned char *data) OPUS_ARG_NONNULL(1); /** Gets the number of samples per frame from an Opus packet. * @param [in] data char*: Opus packet. * This must contain at least one byte of * data. * @param [in] Fs opus_int32: Sampling rate in Hz. * This must be a multiple of 400, or * inaccurate results will be returned. * @returns Number of samples per frame. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_samples_per_frame(const unsigned char *data, opus_int32 Fs) OPUS_ARG_NONNULL(1); /** Gets the number of channels from an Opus packet. * @param [in] data char*: Opus packet * @returns Number of channels * @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_channels(const unsigned char *data) OPUS_ARG_NONNULL(1); /** Gets the number of frames in an Opus packet. * @param [in] packet char*: Opus packet * @param [in] len opus_int32: Length of packet * @returns Number of frames * @retval OPUS_BAD_ARG Insufficient data was passed to the function * @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_frames(const unsigned char packet[], opus_int32 len) OPUS_ARG_NONNULL(1); /** Gets the number of samples of an Opus packet. * @param [in] packet char*: Opus packet * @param [in] len opus_int32: Length of packet * @param [in] Fs opus_int32: Sampling rate in Hz. * This must be a multiple of 400, or * inaccurate results will be returned. * @returns Number of samples * @retval OPUS_BAD_ARG Insufficient data was passed to the function * @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_samples(const unsigned char packet[], opus_int32 len, opus_int32 Fs) OPUS_ARG_NONNULL(1); /** Gets the number of samples of an Opus packet. * @param [in] dec OpusDecoder*: Decoder state * @param [in] packet char*: Opus packet * @param [in] len opus_int32: Length of packet * @returns Number of samples * @retval OPUS_BAD_ARG Insufficient data was passed to the function * @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decoder_get_nb_samples(const OpusDecoder *dec, const unsigned char packet[], opus_int32 len) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2); /** Applies soft-clipping to bring a float signal within the [-1,1] range. If * the signal is already in that range, nothing is done. If there are values * outside of [-1,1], then the signal is clipped as smoothly as possible to * both fit in the range and avoid creating excessive distortion in the * process. * @param [in,out] pcm float*: Input PCM and modified PCM * @param [in] frame_size int Number of samples per channel to process * @param [in] channels int: Number of channels * @param [in,out] softclip_mem float*: State memory for the soft clipping process (one float per channel, initialized to zero) */ OPUS_EXPORT void opus_pcm_soft_clip(float *pcm, int frame_size, int channels, float *softclip_mem); /**@}*/ /** @defgroup opus_repacketizer Repacketizer * @{ * * The repacketizer can be used to merge multiple Opus packets into a single * packet or alternatively to split Opus packets that have previously been * merged. Splitting valid Opus packets is always guaranteed to succeed, * whereas merging valid packets only succeeds if all frames have the same * mode, bandwidth, and frame size, and when the total duration of the merged * packet is no more than 120 ms. The 120 ms limit comes from the * specification and limits decoder memory requirements at a point where * framing overhead becomes negligible. * * The repacketizer currently only operates on elementary Opus * streams. It will not manipualte multistream packets successfully, except in * the degenerate case where they consist of data from a single stream. * * The repacketizing process starts with creating a repacketizer state, either * by calling opus_repacketizer_create() or by allocating the memory yourself, * e.g., * @code * OpusRepacketizer *rp; * rp = (OpusRepacketizer*)malloc(opus_repacketizer_get_size()); * if (rp != NULL) * opus_repacketizer_init(rp); * @endcode * * Then the application should submit packets with opus_repacketizer_cat(), * extract new packets with opus_repacketizer_out() or * opus_repacketizer_out_range(), and then reset the state for the next set of * input packets via opus_repacketizer_init(). * * For example, to split a sequence of packets into individual frames: * @code * unsigned char *data; * int len; * while (get_next_packet(&data, &len)) * { * unsigned char out[1276]; * opus_int32 out_len; * int nb_frames; * int err; * int i; * err = opus_repacketizer_cat(rp, data, len); * if (err != OPUS_OK) * { * release_packet(data); * return err; * } * nb_frames = opus_repacketizer_get_nb_frames(rp); * for (i = 0; i < nb_frames; i++) * { * out_len = opus_repacketizer_out_range(rp, i, i+1, out, sizeof(out)); * if (out_len < 0) * { * release_packet(data); * return (int)out_len; * } * output_next_packet(out, out_len); * } * opus_repacketizer_init(rp); * release_packet(data); * } * @endcode * * Alternatively, to combine a sequence of frames into packets that each * contain up to TARGET_DURATION_MS milliseconds of data: * @code * // The maximum number of packets with duration TARGET_DURATION_MS occurs * // when the frame size is 2.5 ms, for a total of (TARGET_DURATION_MS*2/5) * // packets. * unsigned char *data[(TARGET_DURATION_MS*2/5)+1]; * opus_int32 len[(TARGET_DURATION_MS*2/5)+1]; * int nb_packets; * unsigned char out[1277*(TARGET_DURATION_MS*2/2)]; * opus_int32 out_len; * int prev_toc; * nb_packets = 0; * while (get_next_packet(data+nb_packets, len+nb_packets)) * { * int nb_frames; * int err; * nb_frames = opus_packet_get_nb_frames(data[nb_packets], len[nb_packets]); * if (nb_frames < 1) * { * release_packets(data, nb_packets+1); * return nb_frames; * } * nb_frames += opus_repacketizer_get_nb_frames(rp); * // If adding the next packet would exceed our target, or it has an * // incompatible TOC sequence, output the packets we already have before * // submitting it. * // N.B., The nb_packets > 0 check ensures we've submitted at least one * // packet since the last call to opus_repacketizer_init(). Otherwise a * // single packet longer than TARGET_DURATION_MS would cause us to try to * // output an (invalid) empty packet. It also ensures that prev_toc has * // been set to a valid value. Additionally, len[nb_packets] > 0 is * // guaranteed by the call to opus_packet_get_nb_frames() above, so the * // reference to data[nb_packets][0] should be valid. * if (nb_packets > 0 && ( * ((prev_toc & 0xFC) != (data[nb_packets][0] & 0xFC)) || * opus_packet_get_samples_per_frame(data[nb_packets], 48000)*nb_frames > * TARGET_DURATION_MS*48)) * { * out_len = opus_repacketizer_out(rp, out, sizeof(out)); * if (out_len < 0) * { * release_packets(data, nb_packets+1); * return (int)out_len; * } * output_next_packet(out, out_len); * opus_repacketizer_init(rp); * release_packets(data, nb_packets); * data[0] = data[nb_packets]; * len[0] = len[nb_packets]; * nb_packets = 0; * } * err = opus_repacketizer_cat(rp, data[nb_packets], len[nb_packets]); * if (err != OPUS_OK) * { * release_packets(data, nb_packets+1); * return err; * } * prev_toc = data[nb_packets][0]; * nb_packets++; * } * // Output the final, partial packet. * if (nb_packets > 0) * { * out_len = opus_repacketizer_out(rp, out, sizeof(out)); * release_packets(data, nb_packets); * if (out_len < 0) * return (int)out_len; * output_next_packet(out, out_len); * } * @endcode * * An alternate way of merging packets is to simply call opus_repacketizer_cat() * unconditionally until it fails. At that point, the merged packet can be * obtained with opus_repacketizer_out() and the input packet for which * opus_repacketizer_cat() needs to be re-added to a newly reinitialized * repacketizer state. */ typedef struct OpusRepacketizer OpusRepacketizer; /** Gets the size of an OpusRepacketizer structure. * @returns The size in bytes. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_repacketizer_get_size(void); /** (Re)initializes a previously allocated repacketizer state. * The state must be at least the size returned by opus_repacketizer_get_size(). * This can be used for applications which use their own allocator instead of * malloc(). * It must also be called to reset the queue of packets waiting to be * repacketized, which is necessary if the maximum packet duration of 120 ms * is reached or if you wish to submit packets with a different Opus * configuration (coding mode, audio bandwidth, frame size, or channel count). * Failure to do so will prevent a new packet from being added with * opus_repacketizer_cat(). * @see opus_repacketizer_create * @see opus_repacketizer_get_size * @see opus_repacketizer_cat * @param rp OpusRepacketizer*: The repacketizer state to * (re)initialize. * @returns A pointer to the same repacketizer state that was passed in. */ OPUS_EXPORT OpusRepacketizer *opus_repacketizer_init(OpusRepacketizer *rp) OPUS_ARG_NONNULL(1); /** Allocates memory and initializes the new repacketizer with * opus_repacketizer_init(). */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusRepacketizer *opus_repacketizer_create(void); /** Frees an OpusRepacketizer allocated by * opus_repacketizer_create(). * @param[in] rp OpusRepacketizer*: State to be freed. */ OPUS_EXPORT void opus_repacketizer_destroy(OpusRepacketizer *rp); /** Add a packet to the current repacketizer state. * This packet must match the configuration of any packets already submitted * for repacketization since the last call to opus_repacketizer_init(). * This means that it must have the same coding mode, audio bandwidth, frame * size, and channel count. * This can be checked in advance by examining the top 6 bits of the first * byte of the packet, and ensuring they match the top 6 bits of the first * byte of any previously submitted packet. * The total duration of audio in the repacketizer state also must not exceed * 120 ms, the maximum duration of a single packet, after adding this packet. * * The contents of the current repacketizer state can be extracted into new * packets using opus_repacketizer_out() or opus_repacketizer_out_range(). * * In order to add a packet with a different configuration or to add more * audio beyond 120 ms, you must clear the repacketizer state by calling * opus_repacketizer_init(). * If a packet is too large to add to the current repacketizer state, no part * of it is added, even if it contains multiple frames, some of which might * fit. * If you wish to be able to add parts of such packets, you should first use * another repacketizer to split the packet into pieces and add them * individually. * @see opus_repacketizer_out_range * @see opus_repacketizer_out * @see opus_repacketizer_init * @param rp OpusRepacketizer*: The repacketizer state to which to * add the packet. * @param[in] data const unsigned char*: The packet data. * The application must ensure * this pointer remains valid * until the next call to * opus_repacketizer_init() or * opus_repacketizer_destroy(). * @param len opus_int32: The number of bytes in the packet data. * @returns An error code indicating whether or not the operation succeeded. * @retval #OPUS_OK The packet's contents have been added to the repacketizer * state. * @retval #OPUS_INVALID_PACKET The packet did not have a valid TOC sequence, * the packet's TOC sequence was not compatible * with previously submitted packets (because * the coding mode, audio bandwidth, frame size, * or channel count did not match), or adding * this packet would increase the total amount of * audio stored in the repacketizer state to more * than 120 ms. */ OPUS_EXPORT int opus_repacketizer_cat(OpusRepacketizer *rp, const unsigned char *data, opus_int32 len) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2); /** Construct a new packet from data previously submitted to the repacketizer * state via opus_repacketizer_cat(). * @param rp OpusRepacketizer*: The repacketizer state from which to * construct the new packet. * @param begin int: The index of the first frame in the current * repacketizer state to include in the output. * @param end int: One past the index of the last frame in the * current repacketizer state to include in the * output. * @param[out] data const unsigned char*: The buffer in which to * store the output packet. * @param maxlen opus_int32: The maximum number of bytes to store in * the output buffer. In order to guarantee * success, this should be at least * 1276 for a single frame, * or for multiple frames, * 1277*(end-begin). * However, 1*(end-begin) plus * the size of all packet data submitted to * the repacketizer since the last call to * opus_repacketizer_init() or * opus_repacketizer_create() is also * sufficient, and possibly much smaller. * @returns The total size of the output packet on success, or an error code * on failure. * @retval #OPUS_BAD_ARG [begin,end) was an invalid range of * frames (begin < 0, begin >= end, or end > * opus_repacketizer_get_nb_frames()). * @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the * complete output packet. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_repacketizer_out_range(OpusRepacketizer *rp, int begin, int end, unsigned char *data, opus_int32 maxlen) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4); /** Return the total number of frames contained in packet data submitted to * the repacketizer state so far via opus_repacketizer_cat() since the last * call to opus_repacketizer_init() or opus_repacketizer_create(). * This defines the valid range of packets that can be extracted with * opus_repacketizer_out_range() or opus_repacketizer_out(). * @param rp OpusRepacketizer*: The repacketizer state containing the * frames. * @returns The total number of frames contained in the packet data submitted * to the repacketizer state. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_repacketizer_get_nb_frames(OpusRepacketizer *rp) OPUS_ARG_NONNULL(1); /** Construct a new packet from data previously submitted to the repacketizer * state via opus_repacketizer_cat(). * This is a convenience routine that returns all the data submitted so far * in a single packet. * It is equivalent to calling * @code * opus_repacketizer_out_range(rp, 0, opus_repacketizer_get_nb_frames(rp), * data, maxlen) * @endcode * @param rp OpusRepacketizer*: The repacketizer state from which to * construct the new packet. * @param[out] data const unsigned char*: The buffer in which to * store the output packet. * @param maxlen opus_int32: The maximum number of bytes to store in * the output buffer. In order to guarantee * success, this should be at least * 1277*opus_repacketizer_get_nb_frames(rp). * However, * 1*opus_repacketizer_get_nb_frames(rp) * plus the size of all packet data * submitted to the repacketizer since the * last call to opus_repacketizer_init() or * opus_repacketizer_create() is also * sufficient, and possibly much smaller. * @returns The total size of the output packet on success, or an error code * on failure. * @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the * complete output packet. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_repacketizer_out(OpusRepacketizer *rp, unsigned char *data, opus_int32 maxlen) OPUS_ARG_NONNULL(1); /** Pads a given Opus packet to a larger size (possibly changing the TOC sequence). * @param[in,out] data const unsigned char*: The buffer containing the * packet to pad. * @param len opus_int32: The size of the packet. * This must be at least 1. * @param new_len opus_int32: The desired size of the packet after padding. * This must be at least as large as len. * @returns an error code * @retval #OPUS_OK \a on success. * @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than len. * @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. */ OPUS_EXPORT int opus_packet_pad(unsigned char *data, opus_int32 len, opus_int32 new_len); /** Remove all padding from a given Opus packet and rewrite the TOC sequence to * minimize space usage. * @param[in,out] data const unsigned char*: The buffer containing the * packet to strip. * @param len opus_int32: The size of the packet. * This must be at least 1. * @returns The new size of the output packet on success, or an error code * on failure. * @retval #OPUS_BAD_ARG \a len was less than 1. * @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_packet_unpad(unsigned char *data, opus_int32 len); /** Pads a given Opus multi-stream packet to a larger size (possibly changing the TOC sequence). * @param[in,out] data const unsigned char*: The buffer containing the * packet to pad. * @param len opus_int32: The size of the packet. * This must be at least 1. * @param new_len opus_int32: The desired size of the packet after padding. * This must be at least 1. * @param nb_streams opus_int32: The number of streams (not channels) in the packet. * This must be at least as large as len. * @returns an error code * @retval #OPUS_OK \a on success. * @retval #OPUS_BAD_ARG \a len was less than 1. * @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. */ OPUS_EXPORT int opus_multistream_packet_pad(unsigned char *data, opus_int32 len, opus_int32 new_len, int nb_streams); /** Remove all padding from a given Opus multi-stream packet and rewrite the TOC sequence to * minimize space usage. * @param[in,out] data const unsigned char*: The buffer containing the * packet to strip. * @param len opus_int32: The size of the packet. * This must be at least 1. * @param nb_streams opus_int32: The number of streams (not channels) in the packet. * This must be at least 1. * @returns The new size of the output packet on success, or an error code * on failure. * @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than len. * @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_multistream_packet_unpad(unsigned char *data, opus_int32 len, int nb_streams); /**@}*/ #ifdef __cplusplus } #endif #endif /* OPUS_H */ ================================================ FILE: deps/pjsip/third_party/opus/include/opus_custom.h ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Copyright (c) 2008-2012 Gregory Maxwell Written by Jean-Marc Valin and Gregory Maxwell */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file opus_custom.h @brief Opus-Custom reference implementation API */ #ifndef OPUS_CUSTOM_H #define OPUS_CUSTOM_H #include "opus_defines.h" #ifdef __cplusplus extern "C" { #endif #ifdef CUSTOM_MODES # define OPUS_CUSTOM_EXPORT OPUS_EXPORT # define OPUS_CUSTOM_EXPORT_STATIC OPUS_EXPORT #else # define OPUS_CUSTOM_EXPORT # ifdef OPUS_BUILD # define OPUS_CUSTOM_EXPORT_STATIC static OPUS_INLINE # else # define OPUS_CUSTOM_EXPORT_STATIC # endif #endif /** @defgroup opus_custom Opus Custom * @{ * Opus Custom is an optional part of the Opus specification and * reference implementation which uses a distinct API from the regular * API and supports frame sizes that are not normally supported.\ Use * of Opus Custom is discouraged for all but very special applications * for which a frame size different from 2.5, 5, 10, or 20 ms is needed * (for either complexity or latency reasons) and where interoperability * is less important. * * In addition to the interoperability limitations the use of Opus custom * disables a substantial chunk of the codec and generally lowers the * quality available at a given bitrate. Normally when an application needs * a different frame size from the codec it should buffer to match the * sizes but this adds a small amount of delay which may be important * in some very low latency applications. Some transports (especially * constant rate RF transports) may also work best with frames of * particular durations. * * Libopus only supports custom modes if they are enabled at compile time. * * The Opus Custom API is similar to the regular API but the * @ref opus_encoder_create and @ref opus_decoder_create calls take * an additional mode parameter which is a structure produced by * a call to @ref opus_custom_mode_create. Both the encoder and decoder * must create a mode using the same sample rate (fs) and frame size * (frame size) so these parameters must either be signaled out of band * or fixed in a particular implementation. * * Similar to regular Opus the custom modes support on the fly frame size * switching, but the sizes available depend on the particular frame size in * use. For some initial frame sizes on a single on the fly size is available. */ /** Contains the state of an encoder. One encoder state is needed for each stream. It is initialized once at the beginning of the stream. Do *not* re-initialize the state for every frame. @brief Encoder state */ typedef struct OpusCustomEncoder OpusCustomEncoder; /** State of the decoder. One decoder state is needed for each stream. It is initialized once at the beginning of the stream. Do *not* re-initialize the state for every frame. @brief Decoder state */ typedef struct OpusCustomDecoder OpusCustomDecoder; /** The mode contains all the information necessary to create an encoder. Both the encoder and decoder need to be initialized with exactly the same mode, otherwise the output will be corrupted. @brief Mode configuration */ typedef struct OpusCustomMode OpusCustomMode; /** Creates a new mode struct. This will be passed to an encoder or * decoder. The mode MUST NOT BE DESTROYED until the encoders and * decoders that use it are destroyed as well. * @param [in] Fs int: Sampling rate (8000 to 96000 Hz) * @param [in] frame_size int: Number of samples (per channel) to encode in each * packet (64 - 1024, prime factorization must contain zero or more 2s, 3s, or 5s and no other primes) * @param [out] error int*: Returned error code (if NULL, no error will be returned) * @return A newly created mode */ OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT OpusCustomMode *opus_custom_mode_create(opus_int32 Fs, int frame_size, int *error); /** Destroys a mode struct. Only call this after all encoders and * decoders using this mode are destroyed as well. * @param [in] mode OpusCustomMode*: Mode to be freed. */ OPUS_CUSTOM_EXPORT void opus_custom_mode_destroy(OpusCustomMode *mode); #if !defined(OPUS_BUILD) || defined(CELT_ENCODER_C) /* Encoder */ /** Gets the size of an OpusCustomEncoder structure. * @param [in] mode OpusCustomMode *: Mode configuration * @param [in] channels int: Number of channels * @returns size */ OPUS_CUSTOM_EXPORT_STATIC OPUS_WARN_UNUSED_RESULT int opus_custom_encoder_get_size( const OpusCustomMode *mode, int channels ) OPUS_ARG_NONNULL(1); # ifdef CUSTOM_MODES /** Initializes a previously allocated encoder state * The memory pointed to by st must be the size returned by opus_custom_encoder_get_size. * This is intended for applications which use their own allocator instead of malloc. * @see opus_custom_encoder_create(),opus_custom_encoder_get_size() * To reset a previously initialized state use the OPUS_RESET_STATE CTL. * @param [in] st OpusCustomEncoder*: Encoder state * @param [in] mode OpusCustomMode *: Contains all the information about the characteristics of * the stream (must be the same characteristics as used for the * decoder) * @param [in] channels int: Number of channels * @return OPUS_OK Success or @ref opus_errorcodes */ OPUS_CUSTOM_EXPORT int opus_custom_encoder_init( OpusCustomEncoder *st, const OpusCustomMode *mode, int channels ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2); # endif #endif /** Creates a new encoder state. Each stream needs its own encoder * state (can't be shared across simultaneous streams). * @param [in] mode OpusCustomMode*: Contains all the information about the characteristics of * the stream (must be the same characteristics as used for the * decoder) * @param [in] channels int: Number of channels * @param [out] error int*: Returns an error code * @return Newly created encoder state. */ OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT OpusCustomEncoder *opus_custom_encoder_create( const OpusCustomMode *mode, int channels, int *error ) OPUS_ARG_NONNULL(1); /** Destroys a an encoder state. * @param[in] st OpusCustomEncoder*: State to be freed. */ OPUS_CUSTOM_EXPORT void opus_custom_encoder_destroy(OpusCustomEncoder *st); /** Encodes a frame of audio. * @param [in] st OpusCustomEncoder*: Encoder state * @param [in] pcm float*: PCM audio in float format, with a normal range of +/-1.0. * Samples with a range beyond +/-1.0 are supported but will * be clipped by decoders using the integer API and should * only be used if it is known that the far end supports * extended dynamic range. There must be exactly * frame_size samples per channel. * @param [in] frame_size int: Number of samples per frame of input signal * @param [out] compressed char *: The compressed data is written here. This may not alias pcm and must be at least maxCompressedBytes long. * @param [in] maxCompressedBytes int: Maximum number of bytes to use for compressing the frame * (can change from one frame to another) * @return Number of bytes written to "compressed". * If negative, an error has occurred (see error codes). It is IMPORTANT that * the length returned be somehow transmitted to the decoder. Otherwise, no * decoding is possible. */ OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_encode_float( OpusCustomEncoder *st, const float *pcm, int frame_size, unsigned char *compressed, int maxCompressedBytes ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4); /** Encodes a frame of audio. * @param [in] st OpusCustomEncoder*: Encoder state * @param [in] pcm opus_int16*: PCM audio in signed 16-bit format (native endian). * There must be exactly frame_size samples per channel. * @param [in] frame_size int: Number of samples per frame of input signal * @param [out] compressed char *: The compressed data is written here. This may not alias pcm and must be at least maxCompressedBytes long. * @param [in] maxCompressedBytes int: Maximum number of bytes to use for compressing the frame * (can change from one frame to another) * @return Number of bytes written to "compressed". * If negative, an error has occurred (see error codes). It is IMPORTANT that * the length returned be somehow transmitted to the decoder. Otherwise, no * decoding is possible. */ OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_encode( OpusCustomEncoder *st, const opus_int16 *pcm, int frame_size, unsigned char *compressed, int maxCompressedBytes ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4); /** Perform a CTL function on an Opus custom encoder. * * Generally the request and subsequent arguments are generated * by a convenience macro. * @see opus_encoderctls */ OPUS_CUSTOM_EXPORT int opus_custom_encoder_ctl(OpusCustomEncoder * OPUS_RESTRICT st, int request, ...) OPUS_ARG_NONNULL(1); #if !defined(OPUS_BUILD) || defined(CELT_DECODER_C) /* Decoder */ /** Gets the size of an OpusCustomDecoder structure. * @param [in] mode OpusCustomMode *: Mode configuration * @param [in] channels int: Number of channels * @returns size */ OPUS_CUSTOM_EXPORT_STATIC OPUS_WARN_UNUSED_RESULT int opus_custom_decoder_get_size( const OpusCustomMode *mode, int channels ) OPUS_ARG_NONNULL(1); /** Initializes a previously allocated decoder state * The memory pointed to by st must be the size returned by opus_custom_decoder_get_size. * This is intended for applications which use their own allocator instead of malloc. * @see opus_custom_decoder_create(),opus_custom_decoder_get_size() * To reset a previously initialized state use the OPUS_RESET_STATE CTL. * @param [in] st OpusCustomDecoder*: Decoder state * @param [in] mode OpusCustomMode *: Contains all the information about the characteristics of * the stream (must be the same characteristics as used for the * encoder) * @param [in] channels int: Number of channels * @return OPUS_OK Success or @ref opus_errorcodes */ OPUS_CUSTOM_EXPORT_STATIC int opus_custom_decoder_init( OpusCustomDecoder *st, const OpusCustomMode *mode, int channels ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2); #endif /** Creates a new decoder state. Each stream needs its own decoder state (can't * be shared across simultaneous streams). * @param [in] mode OpusCustomMode: Contains all the information about the characteristics of the * stream (must be the same characteristics as used for the encoder) * @param [in] channels int: Number of channels * @param [out] error int*: Returns an error code * @return Newly created decoder state. */ OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT OpusCustomDecoder *opus_custom_decoder_create( const OpusCustomMode *mode, int channels, int *error ) OPUS_ARG_NONNULL(1); /** Destroys a an decoder state. * @param[in] st OpusCustomDecoder*: State to be freed. */ OPUS_CUSTOM_EXPORT void opus_custom_decoder_destroy(OpusCustomDecoder *st); /** Decode an opus custom frame with floating point output * @param [in] st OpusCustomDecoder*: Decoder state * @param [in] data char*: Input payload. Use a NULL pointer to indicate packet loss * @param [in] len int: Number of bytes in payload * @param [out] pcm float*: Output signal (interleaved if 2 channels). length * is frame_size*channels*sizeof(float) * @param [in] frame_size Number of samples per channel of available space in *pcm. * @returns Number of decoded samples or @ref opus_errorcodes */ OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_decode_float( OpusCustomDecoder *st, const unsigned char *data, int len, float *pcm, int frame_size ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4); /** Decode an opus custom frame * @param [in] st OpusCustomDecoder*: Decoder state * @param [in] data char*: Input payload. Use a NULL pointer to indicate packet loss * @param [in] len int: Number of bytes in payload * @param [out] pcm opus_int16*: Output signal (interleaved if 2 channels). length * is frame_size*channels*sizeof(opus_int16) * @param [in] frame_size Number of samples per channel of available space in *pcm. * @returns Number of decoded samples or @ref opus_errorcodes */ OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_decode( OpusCustomDecoder *st, const unsigned char *data, int len, opus_int16 *pcm, int frame_size ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4); /** Perform a CTL function on an Opus custom decoder. * * Generally the request and subsequent arguments are generated * by a convenience macro. * @see opus_genericctls */ OPUS_CUSTOM_EXPORT int opus_custom_decoder_ctl(OpusCustomDecoder * OPUS_RESTRICT st, int request, ...) OPUS_ARG_NONNULL(1); /**@}*/ #ifdef __cplusplus } #endif #endif /* OPUS_CUSTOM_H */ ================================================ FILE: deps/pjsip/third_party/opus/include/opus_defines.h ================================================ /* Copyright (c) 2010-2011 Xiph.Org Foundation, Skype Limited Written by Jean-Marc Valin and Koen Vos */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file opus_defines.h * @brief Opus reference implementation constants */ #ifndef OPUS_DEFINES_H #define OPUS_DEFINES_H #include "opus_types.h" #ifdef __cplusplus extern "C" { #endif /** @defgroup opus_errorcodes Error codes * @{ */ /** No error @hideinitializer*/ #define OPUS_OK 0 /** One or more invalid/out of range arguments @hideinitializer*/ #define OPUS_BAD_ARG -1 /** Not enough bytes allocated in the buffer @hideinitializer*/ #define OPUS_BUFFER_TOO_SMALL -2 /** An internal error was detected @hideinitializer*/ #define OPUS_INTERNAL_ERROR -3 /** The compressed data passed is corrupted @hideinitializer*/ #define OPUS_INVALID_PACKET -4 /** Invalid/unsupported request number @hideinitializer*/ #define OPUS_UNIMPLEMENTED -5 /** An encoder or decoder structure is invalid or already freed @hideinitializer*/ #define OPUS_INVALID_STATE -6 /** Memory allocation has failed @hideinitializer*/ #define OPUS_ALLOC_FAIL -7 /**@}*/ /** @cond OPUS_INTERNAL_DOC */ /**Export control for opus functions */ #ifndef OPUS_EXPORT # if defined(WIN32) # ifdef OPUS_BUILD # define OPUS_EXPORT __declspec(dllexport) # else # define OPUS_EXPORT # endif # elif defined(__GNUC__) && defined(OPUS_BUILD) # define OPUS_EXPORT __attribute__ ((visibility ("default"))) # else # define OPUS_EXPORT # endif #endif # if !defined(OPUS_GNUC_PREREQ) # if defined(__GNUC__)&&defined(__GNUC_MINOR__) # define OPUS_GNUC_PREREQ(_maj,_min) \ ((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min)) # else # define OPUS_GNUC_PREREQ(_maj,_min) 0 # endif # endif #if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) # if OPUS_GNUC_PREREQ(3,0) # define OPUS_RESTRICT __restrict__ # elif (defined(_MSC_VER) && _MSC_VER >= 1400) # define OPUS_RESTRICT __restrict # else # define OPUS_RESTRICT # endif #else # define OPUS_RESTRICT restrict #endif #if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) # if OPUS_GNUC_PREREQ(2,7) # define OPUS_INLINE __inline__ # elif (defined(_MSC_VER)) # define OPUS_INLINE __inline # else # define OPUS_INLINE # endif #else # define OPUS_INLINE inline #endif /**Warning attributes for opus functions * NONNULL is not used in OPUS_BUILD to avoid the compiler optimizing out * some paranoid null checks. */ #if defined(__GNUC__) && OPUS_GNUC_PREREQ(3, 4) # define OPUS_WARN_UNUSED_RESULT __attribute__ ((__warn_unused_result__)) #else # define OPUS_WARN_UNUSED_RESULT #endif #if !defined(OPUS_BUILD) && defined(__GNUC__) && OPUS_GNUC_PREREQ(3, 4) # define OPUS_ARG_NONNULL(_x) __attribute__ ((__nonnull__(_x))) #else # define OPUS_ARG_NONNULL(_x) #endif /** These are the actual Encoder CTL ID numbers. * They should not be used directly by applications. * In general, SETs should be even and GETs should be odd.*/ #define OPUS_SET_APPLICATION_REQUEST 4000 #define OPUS_GET_APPLICATION_REQUEST 4001 #define OPUS_SET_BITRATE_REQUEST 4002 #define OPUS_GET_BITRATE_REQUEST 4003 #define OPUS_SET_MAX_BANDWIDTH_REQUEST 4004 #define OPUS_GET_MAX_BANDWIDTH_REQUEST 4005 #define OPUS_SET_VBR_REQUEST 4006 #define OPUS_GET_VBR_REQUEST 4007 #define OPUS_SET_BANDWIDTH_REQUEST 4008 #define OPUS_GET_BANDWIDTH_REQUEST 4009 #define OPUS_SET_COMPLEXITY_REQUEST 4010 #define OPUS_GET_COMPLEXITY_REQUEST 4011 #define OPUS_SET_INBAND_FEC_REQUEST 4012 #define OPUS_GET_INBAND_FEC_REQUEST 4013 #define OPUS_SET_PACKET_LOSS_PERC_REQUEST 4014 #define OPUS_GET_PACKET_LOSS_PERC_REQUEST 4015 #define OPUS_SET_DTX_REQUEST 4016 #define OPUS_GET_DTX_REQUEST 4017 #define OPUS_SET_VBR_CONSTRAINT_REQUEST 4020 #define OPUS_GET_VBR_CONSTRAINT_REQUEST 4021 #define OPUS_SET_FORCE_CHANNELS_REQUEST 4022 #define OPUS_GET_FORCE_CHANNELS_REQUEST 4023 #define OPUS_SET_SIGNAL_REQUEST 4024 #define OPUS_GET_SIGNAL_REQUEST 4025 #define OPUS_GET_LOOKAHEAD_REQUEST 4027 /* #define OPUS_RESET_STATE 4028 */ #define OPUS_GET_SAMPLE_RATE_REQUEST 4029 #define OPUS_GET_FINAL_RANGE_REQUEST 4031 #define OPUS_GET_PITCH_REQUEST 4033 #define OPUS_SET_GAIN_REQUEST 4034 #define OPUS_GET_GAIN_REQUEST 4045 /* Should have been 4035 */ #define OPUS_SET_LSB_DEPTH_REQUEST 4036 #define OPUS_GET_LSB_DEPTH_REQUEST 4037 #define OPUS_GET_LAST_PACKET_DURATION_REQUEST 4039 #define OPUS_SET_EXPERT_FRAME_DURATION_REQUEST 4040 #define OPUS_GET_EXPERT_FRAME_DURATION_REQUEST 4041 #define OPUS_SET_PREDICTION_DISABLED_REQUEST 4042 #define OPUS_GET_PREDICTION_DISABLED_REQUEST 4043 /* Don't use 4045, it's already taken by OPUS_GET_GAIN_REQUEST */ /* Macros to trigger compilation errors when the wrong types are provided to a CTL */ #define __opus_check_int(x) (((void)((x) == (opus_int32)0)), (opus_int32)(x)) #define __opus_check_int_ptr(ptr) ((ptr) + ((ptr) - (opus_int32*)(ptr))) #define __opus_check_uint_ptr(ptr) ((ptr) + ((ptr) - (opus_uint32*)(ptr))) #define __opus_check_val16_ptr(ptr) ((ptr) + ((ptr) - (opus_val16*)(ptr))) /** @endcond */ /** @defgroup opus_ctlvalues Pre-defined values for CTL interface * @see opus_genericctls, opus_encoderctls * @{ */ /* Values for the various encoder CTLs */ #define OPUS_AUTO -1000 /**opus_int32: Allowed values: 0-10, inclusive. * * @hideinitializer */ #define OPUS_SET_COMPLEXITY(x) OPUS_SET_COMPLEXITY_REQUEST, __opus_check_int(x) /** Gets the encoder's complexity configuration. * @see OPUS_SET_COMPLEXITY * @param[out] x opus_int32 *: Returns a value in the range 0-10, * inclusive. * @hideinitializer */ #define OPUS_GET_COMPLEXITY(x) OPUS_GET_COMPLEXITY_REQUEST, __opus_check_int_ptr(x) /** Configures the bitrate in the encoder. * Rates from 500 to 512000 bits per second are meaningful, as well as the * special values #OPUS_AUTO and #OPUS_BITRATE_MAX. * The value #OPUS_BITRATE_MAX can be used to cause the codec to use as much * rate as it can, which is useful for controlling the rate by adjusting the * output buffer size. * @see OPUS_GET_BITRATE * @param[in] x opus_int32: Bitrate in bits per second. The default * is determined based on the number of * channels and the input sampling rate. * @hideinitializer */ #define OPUS_SET_BITRATE(x) OPUS_SET_BITRATE_REQUEST, __opus_check_int(x) /** Gets the encoder's bitrate configuration. * @see OPUS_SET_BITRATE * @param[out] x opus_int32 *: Returns the bitrate in bits per second. * The default is determined based on the * number of channels and the input * sampling rate. * @hideinitializer */ #define OPUS_GET_BITRATE(x) OPUS_GET_BITRATE_REQUEST, __opus_check_int_ptr(x) /** Enables or disables variable bitrate (VBR) in the encoder. * The configured bitrate may not be met exactly because frames must * be an integer number of bytes in length. * @see OPUS_GET_VBR * @see OPUS_SET_VBR_CONSTRAINT * @param[in] x opus_int32: Allowed values: *
*
0
Hard CBR. For LPC/hybrid modes at very low bit-rate, this can * cause noticeable quality degradation.
*
1
VBR (default). The exact type of VBR is controlled by * #OPUS_SET_VBR_CONSTRAINT.
*
* @hideinitializer */ #define OPUS_SET_VBR(x) OPUS_SET_VBR_REQUEST, __opus_check_int(x) /** Determine if variable bitrate (VBR) is enabled in the encoder. * @see OPUS_SET_VBR * @see OPUS_GET_VBR_CONSTRAINT * @param[out] x opus_int32 *: Returns one of the following values: *
*
0
Hard CBR.
*
1
VBR (default). The exact type of VBR may be retrieved via * #OPUS_GET_VBR_CONSTRAINT.
*
* @hideinitializer */ #define OPUS_GET_VBR(x) OPUS_GET_VBR_REQUEST, __opus_check_int_ptr(x) /** Enables or disables constrained VBR in the encoder. * This setting is ignored when the encoder is in CBR mode. * @warning Only the MDCT mode of Opus currently heeds the constraint. * Speech mode ignores it completely, hybrid mode may fail to obey it * if the LPC layer uses more bitrate than the constraint would have * permitted. * @see OPUS_GET_VBR_CONSTRAINT * @see OPUS_SET_VBR * @param[in] x opus_int32: Allowed values: *
*
0
Unconstrained VBR.
*
1
Constrained VBR (default). This creates a maximum of one * frame of buffering delay assuming a transport with a * serialization speed of the nominal bitrate.
*
* @hideinitializer */ #define OPUS_SET_VBR_CONSTRAINT(x) OPUS_SET_VBR_CONSTRAINT_REQUEST, __opus_check_int(x) /** Determine if constrained VBR is enabled in the encoder. * @see OPUS_SET_VBR_CONSTRAINT * @see OPUS_GET_VBR * @param[out] x opus_int32 *: Returns one of the following values: *
*
0
Unconstrained VBR.
*
1
Constrained VBR (default).
*
* @hideinitializer */ #define OPUS_GET_VBR_CONSTRAINT(x) OPUS_GET_VBR_CONSTRAINT_REQUEST, __opus_check_int_ptr(x) /** Configures mono/stereo forcing in the encoder. * This can force the encoder to produce packets encoded as either mono or * stereo, regardless of the format of the input audio. This is useful when * the caller knows that the input signal is currently a mono source embedded * in a stereo stream. * @see OPUS_GET_FORCE_CHANNELS * @param[in] x opus_int32: Allowed values: *
*
#OPUS_AUTO
Not forced (default)
*
1
Forced mono
*
2
Forced stereo
*
* @hideinitializer */ #define OPUS_SET_FORCE_CHANNELS(x) OPUS_SET_FORCE_CHANNELS_REQUEST, __opus_check_int(x) /** Gets the encoder's forced channel configuration. * @see OPUS_SET_FORCE_CHANNELS * @param[out] x opus_int32 *: *
*
#OPUS_AUTO
Not forced (default)
*
1
Forced mono
*
2
Forced stereo
*
* @hideinitializer */ #define OPUS_GET_FORCE_CHANNELS(x) OPUS_GET_FORCE_CHANNELS_REQUEST, __opus_check_int_ptr(x) /** Configures the maximum bandpass that the encoder will select automatically. * Applications should normally use this instead of #OPUS_SET_BANDWIDTH * (leaving that set to the default, #OPUS_AUTO). This allows the * application to set an upper bound based on the type of input it is * providing, but still gives the encoder the freedom to reduce the bandpass * when the bitrate becomes too low, for better overall quality. * @see OPUS_GET_MAX_BANDWIDTH * @param[in] x opus_int32: Allowed values: *
*
OPUS_BANDWIDTH_NARROWBAND
4 kHz passband
*
OPUS_BANDWIDTH_MEDIUMBAND
6 kHz passband
*
OPUS_BANDWIDTH_WIDEBAND
8 kHz passband
*
OPUS_BANDWIDTH_SUPERWIDEBAND
12 kHz passband
*
OPUS_BANDWIDTH_FULLBAND
20 kHz passband (default)
*
* @hideinitializer */ #define OPUS_SET_MAX_BANDWIDTH(x) OPUS_SET_MAX_BANDWIDTH_REQUEST, __opus_check_int(x) /** Gets the encoder's configured maximum allowed bandpass. * @see OPUS_SET_MAX_BANDWIDTH * @param[out] x opus_int32 *: Allowed values: *
*
#OPUS_BANDWIDTH_NARROWBAND
4 kHz passband
*
#OPUS_BANDWIDTH_MEDIUMBAND
6 kHz passband
*
#OPUS_BANDWIDTH_WIDEBAND
8 kHz passband
*
#OPUS_BANDWIDTH_SUPERWIDEBAND
12 kHz passband
*
#OPUS_BANDWIDTH_FULLBAND
20 kHz passband (default)
*
* @hideinitializer */ #define OPUS_GET_MAX_BANDWIDTH(x) OPUS_GET_MAX_BANDWIDTH_REQUEST, __opus_check_int_ptr(x) /** Sets the encoder's bandpass to a specific value. * This prevents the encoder from automatically selecting the bandpass based * on the available bitrate. If an application knows the bandpass of the input * audio it is providing, it should normally use #OPUS_SET_MAX_BANDWIDTH * instead, which still gives the encoder the freedom to reduce the bandpass * when the bitrate becomes too low, for better overall quality. * @see OPUS_GET_BANDWIDTH * @param[in] x opus_int32: Allowed values: *
*
#OPUS_AUTO
(default)
*
#OPUS_BANDWIDTH_NARROWBAND
4 kHz passband
*
#OPUS_BANDWIDTH_MEDIUMBAND
6 kHz passband
*
#OPUS_BANDWIDTH_WIDEBAND
8 kHz passband
*
#OPUS_BANDWIDTH_SUPERWIDEBAND
12 kHz passband
*
#OPUS_BANDWIDTH_FULLBAND
20 kHz passband
*
* @hideinitializer */ #define OPUS_SET_BANDWIDTH(x) OPUS_SET_BANDWIDTH_REQUEST, __opus_check_int(x) /** Configures the type of signal being encoded. * This is a hint which helps the encoder's mode selection. * @see OPUS_GET_SIGNAL * @param[in] x opus_int32: Allowed values: *
*
#OPUS_AUTO
(default)
*
#OPUS_SIGNAL_VOICE
Bias thresholds towards choosing LPC or Hybrid modes.
*
#OPUS_SIGNAL_MUSIC
Bias thresholds towards choosing MDCT modes.
*
* @hideinitializer */ #define OPUS_SET_SIGNAL(x) OPUS_SET_SIGNAL_REQUEST, __opus_check_int(x) /** Gets the encoder's configured signal type. * @see OPUS_SET_SIGNAL * @param[out] x opus_int32 *: Returns one of the following values: *
*
#OPUS_AUTO
(default)
*
#OPUS_SIGNAL_VOICE
Bias thresholds towards choosing LPC or Hybrid modes.
*
#OPUS_SIGNAL_MUSIC
Bias thresholds towards choosing MDCT modes.
*
* @hideinitializer */ #define OPUS_GET_SIGNAL(x) OPUS_GET_SIGNAL_REQUEST, __opus_check_int_ptr(x) /** Configures the encoder's intended application. * The initial value is a mandatory argument to the encoder_create function. * @see OPUS_GET_APPLICATION * @param[in] x opus_int32: Returns one of the following values: *
*
#OPUS_APPLICATION_VOIP
*
Process signal for improved speech intelligibility.
*
#OPUS_APPLICATION_AUDIO
*
Favor faithfulness to the original input.
*
#OPUS_APPLICATION_RESTRICTED_LOWDELAY
*
Configure the minimum possible coding delay by disabling certain modes * of operation.
*
* @hideinitializer */ #define OPUS_SET_APPLICATION(x) OPUS_SET_APPLICATION_REQUEST, __opus_check_int(x) /** Gets the encoder's configured application. * @see OPUS_SET_APPLICATION * @param[out] x opus_int32 *: Returns one of the following values: *
*
#OPUS_APPLICATION_VOIP
*
Process signal for improved speech intelligibility.
*
#OPUS_APPLICATION_AUDIO
*
Favor faithfulness to the original input.
*
#OPUS_APPLICATION_RESTRICTED_LOWDELAY
*
Configure the minimum possible coding delay by disabling certain modes * of operation.
*
* @hideinitializer */ #define OPUS_GET_APPLICATION(x) OPUS_GET_APPLICATION_REQUEST, __opus_check_int_ptr(x) /** Gets the total samples of delay added by the entire codec. * This can be queried by the encoder and then the provided number of samples can be * skipped on from the start of the decoder's output to provide time aligned input * and output. From the perspective of a decoding application the real data begins this many * samples late. * * The decoder contribution to this delay is identical for all decoders, but the * encoder portion of the delay may vary from implementation to implementation, * version to version, or even depend on the encoder's initial configuration. * Applications needing delay compensation should call this CTL rather than * hard-coding a value. * @param[out] x opus_int32 *: Number of lookahead samples * @hideinitializer */ #define OPUS_GET_LOOKAHEAD(x) OPUS_GET_LOOKAHEAD_REQUEST, __opus_check_int_ptr(x) /** Configures the encoder's use of inband forward error correction (FEC). * @note This is only applicable to the LPC layer * @see OPUS_GET_INBAND_FEC * @param[in] x opus_int32: Allowed values: *
*
0
Disable inband FEC (default).
*
1
Enable inband FEC.
*
* @hideinitializer */ #define OPUS_SET_INBAND_FEC(x) OPUS_SET_INBAND_FEC_REQUEST, __opus_check_int(x) /** Gets encoder's configured use of inband forward error correction. * @see OPUS_SET_INBAND_FEC * @param[out] x opus_int32 *: Returns one of the following values: *
*
0
Inband FEC disabled (default).
*
1
Inband FEC enabled.
*
* @hideinitializer */ #define OPUS_GET_INBAND_FEC(x) OPUS_GET_INBAND_FEC_REQUEST, __opus_check_int_ptr(x) /** Configures the encoder's expected packet loss percentage. * Higher values trigger progressively more loss resistant behavior in the encoder * at the expense of quality at a given bitrate in the absence of packet loss, but * greater quality under loss. * @see OPUS_GET_PACKET_LOSS_PERC * @param[in] x opus_int32: Loss percentage in the range 0-100, inclusive (default: 0). * @hideinitializer */ #define OPUS_SET_PACKET_LOSS_PERC(x) OPUS_SET_PACKET_LOSS_PERC_REQUEST, __opus_check_int(x) /** Gets the encoder's configured packet loss percentage. * @see OPUS_SET_PACKET_LOSS_PERC * @param[out] x opus_int32 *: Returns the configured loss percentage * in the range 0-100, inclusive (default: 0). * @hideinitializer */ #define OPUS_GET_PACKET_LOSS_PERC(x) OPUS_GET_PACKET_LOSS_PERC_REQUEST, __opus_check_int_ptr(x) /** Configures the encoder's use of discontinuous transmission (DTX). * @note This is only applicable to the LPC layer * @see OPUS_GET_DTX * @param[in] x opus_int32: Allowed values: *
*
0
Disable DTX (default).
*
1
Enabled DTX.
*
* @hideinitializer */ #define OPUS_SET_DTX(x) OPUS_SET_DTX_REQUEST, __opus_check_int(x) /** Gets encoder's configured use of discontinuous transmission. * @see OPUS_SET_DTX * @param[out] x opus_int32 *: Returns one of the following values: *
*
0
DTX disabled (default).
*
1
DTX enabled.
*
* @hideinitializer */ #define OPUS_GET_DTX(x) OPUS_GET_DTX_REQUEST, __opus_check_int_ptr(x) /** Configures the depth of signal being encoded. * * This is a hint which helps the encoder identify silence and near-silence. * It represents the number of significant bits of linear intensity below * which the signal contains ignorable quantization or other noise. * * For example, OPUS_SET_LSB_DEPTH(14) would be an appropriate setting * for G.711 u-law input. OPUS_SET_LSB_DEPTH(16) would be appropriate * for 16-bit linear pcm input with opus_encode_float(). * * When using opus_encode() instead of opus_encode_float(), or when libopus * is compiled for fixed-point, the encoder uses the minimum of the value * set here and the value 16. * * @see OPUS_GET_LSB_DEPTH * @param[in] x opus_int32: Input precision in bits, between 8 and 24 * (default: 24). * @hideinitializer */ #define OPUS_SET_LSB_DEPTH(x) OPUS_SET_LSB_DEPTH_REQUEST, __opus_check_int(x) /** Gets the encoder's configured signal depth. * @see OPUS_SET_LSB_DEPTH * @param[out] x opus_int32 *: Input precision in bits, between 8 and * 24 (default: 24). * @hideinitializer */ #define OPUS_GET_LSB_DEPTH(x) OPUS_GET_LSB_DEPTH_REQUEST, __opus_check_int_ptr(x) /** Configures the encoder's use of variable duration frames. * When variable duration is enabled, the encoder is free to use a shorter frame * size than the one requested in the opus_encode*() call. * It is then the user's responsibility * to verify how much audio was encoded by checking the ToC byte of the encoded * packet. The part of the audio that was not encoded needs to be resent to the * encoder for the next call. Do not use this option unless you really * know what you are doing. * @see OPUS_GET_EXPERT_FRAME_DURATION * @param[in] x opus_int32: Allowed values: *
*
OPUS_FRAMESIZE_ARG
Select frame size from the argument (default).
*
OPUS_FRAMESIZE_2_5_MS
Use 2.5 ms frames.
*
OPUS_FRAMESIZE_5_MS
Use 5 ms frames.
*
OPUS_FRAMESIZE_10_MS
Use 10 ms frames.
*
OPUS_FRAMESIZE_20_MS
Use 20 ms frames.
*
OPUS_FRAMESIZE_40_MS
Use 40 ms frames.
*
OPUS_FRAMESIZE_60_MS
Use 60 ms frames.
*
OPUS_FRAMESIZE_VARIABLE
Optimize the frame size dynamically.
*
* @hideinitializer */ #define OPUS_SET_EXPERT_FRAME_DURATION(x) OPUS_SET_EXPERT_FRAME_DURATION_REQUEST, __opus_check_int(x) /** Gets the encoder's configured use of variable duration frames. * @see OPUS_SET_EXPERT_FRAME_DURATION * @param[out] x opus_int32 *: Returns one of the following values: *
*
OPUS_FRAMESIZE_ARG
Select frame size from the argument (default).
*
OPUS_FRAMESIZE_2_5_MS
Use 2.5 ms frames.
*
OPUS_FRAMESIZE_5_MS
Use 5 ms frames.
*
OPUS_FRAMESIZE_10_MS
Use 10 ms frames.
*
OPUS_FRAMESIZE_20_MS
Use 20 ms frames.
*
OPUS_FRAMESIZE_40_MS
Use 40 ms frames.
*
OPUS_FRAMESIZE_60_MS
Use 60 ms frames.
*
OPUS_FRAMESIZE_VARIABLE
Optimize the frame size dynamically.
*
* @hideinitializer */ #define OPUS_GET_EXPERT_FRAME_DURATION(x) OPUS_GET_EXPERT_FRAME_DURATION_REQUEST, __opus_check_int_ptr(x) /** If set to 1, disables almost all use of prediction, making frames almost * completely independent. This reduces quality. * @see OPUS_GET_PREDICTION_DISABLED * @param[in] x opus_int32: Allowed values: *
*
0
Enable prediction (default).
*
1
Disable prediction.
*
* @hideinitializer */ #define OPUS_SET_PREDICTION_DISABLED(x) OPUS_SET_PREDICTION_DISABLED_REQUEST, __opus_check_int(x) /** Gets the encoder's configured prediction status. * @see OPUS_SET_PREDICTION_DISABLED * @param[out] x opus_int32 *: Returns one of the following values: *
*
0
Prediction enabled (default).
*
1
Prediction disabled.
*
* @hideinitializer */ #define OPUS_GET_PREDICTION_DISABLED(x) OPUS_GET_PREDICTION_DISABLED_REQUEST, __opus_check_int_ptr(x) /**@}*/ /** @defgroup opus_genericctls Generic CTLs * * These macros are used with the \c opus_decoder_ctl and * \c opus_encoder_ctl calls to generate a particular * request. * * When called on an \c OpusDecoder they apply to that * particular decoder instance. When called on an * \c OpusEncoder they apply to the corresponding setting * on that encoder instance, if present. * * Some usage examples: * * @code * int ret; * opus_int32 pitch; * ret = opus_decoder_ctl(dec_ctx, OPUS_GET_PITCH(&pitch)); * if (ret == OPUS_OK) return ret; * * opus_encoder_ctl(enc_ctx, OPUS_RESET_STATE); * opus_decoder_ctl(dec_ctx, OPUS_RESET_STATE); * * opus_int32 enc_bw, dec_bw; * opus_encoder_ctl(enc_ctx, OPUS_GET_BANDWIDTH(&enc_bw)); * opus_decoder_ctl(dec_ctx, OPUS_GET_BANDWIDTH(&dec_bw)); * if (enc_bw != dec_bw) { * printf("packet bandwidth mismatch!\n"); * } * @endcode * * @see opus_encoder, opus_decoder_ctl, opus_encoder_ctl, opus_decoderctls, opus_encoderctls * @{ */ /** Resets the codec state to be equivalent to a freshly initialized state. * This should be called when switching streams in order to prevent * the back to back decoding from giving different results from * one at a time decoding. * @hideinitializer */ #define OPUS_RESET_STATE 4028 /** Gets the final state of the codec's entropy coder. * This is used for testing purposes, * The encoder and decoder state should be identical after coding a payload * (assuming no data corruption or software bugs) * * @param[out] x opus_uint32 *: Entropy coder state * * @hideinitializer */ #define OPUS_GET_FINAL_RANGE(x) OPUS_GET_FINAL_RANGE_REQUEST, __opus_check_uint_ptr(x) /** Gets the encoder's configured bandpass or the decoder's last bandpass. * @see OPUS_SET_BANDWIDTH * @param[out] x opus_int32 *: Returns one of the following values: *
*
#OPUS_AUTO
(default)
*
#OPUS_BANDWIDTH_NARROWBAND
4 kHz passband
*
#OPUS_BANDWIDTH_MEDIUMBAND
6 kHz passband
*
#OPUS_BANDWIDTH_WIDEBAND
8 kHz passband
*
#OPUS_BANDWIDTH_SUPERWIDEBAND
12 kHz passband
*
#OPUS_BANDWIDTH_FULLBAND
20 kHz passband
*
* @hideinitializer */ #define OPUS_GET_BANDWIDTH(x) OPUS_GET_BANDWIDTH_REQUEST, __opus_check_int_ptr(x) /** Gets the sampling rate the encoder or decoder was initialized with. * This simply returns the Fs value passed to opus_encoder_init() * or opus_decoder_init(). * @param[out] x opus_int32 *: Sampling rate of encoder or decoder. * @hideinitializer */ #define OPUS_GET_SAMPLE_RATE(x) OPUS_GET_SAMPLE_RATE_REQUEST, __opus_check_int_ptr(x) /**@}*/ /** @defgroup opus_decoderctls Decoder related CTLs * @see opus_genericctls, opus_encoderctls, opus_decoder * @{ */ /** Configures decoder gain adjustment. * Scales the decoded output by a factor specified in Q8 dB units. * This has a maximum range of -32768 to 32767 inclusive, and returns * OPUS_BAD_ARG otherwise. The default is zero indicating no adjustment. * This setting survives decoder reset. * * gain = pow(10, x/(20.0*256)) * * @param[in] x opus_int32: Amount to scale PCM signal by in Q8 dB units. * @hideinitializer */ #define OPUS_SET_GAIN(x) OPUS_SET_GAIN_REQUEST, __opus_check_int(x) /** Gets the decoder's configured gain adjustment. @see OPUS_SET_GAIN * * @param[out] x opus_int32 *: Amount to scale PCM signal by in Q8 dB units. * @hideinitializer */ #define OPUS_GET_GAIN(x) OPUS_GET_GAIN_REQUEST, __opus_check_int_ptr(x) /** Gets the duration (in samples) of the last packet successfully decoded or concealed. * @param[out] x opus_int32 *: Number of samples (at current sampling rate). * @hideinitializer */ #define OPUS_GET_LAST_PACKET_DURATION(x) OPUS_GET_LAST_PACKET_DURATION_REQUEST, __opus_check_int_ptr(x) /** Gets the pitch of the last decoded frame, if available. * This can be used for any post-processing algorithm requiring the use of pitch, * e.g. time stretching/shortening. If the last frame was not voiced, or if the * pitch was not coded in the frame, then zero is returned. * * This CTL is only implemented for decoder instances. * * @param[out] x opus_int32 *: pitch period at 48 kHz (or 0 if not available) * * @hideinitializer */ #define OPUS_GET_PITCH(x) OPUS_GET_PITCH_REQUEST, __opus_check_int_ptr(x) /**@}*/ /** @defgroup opus_libinfo Opus library information functions * @{ */ /** Converts an opus error code into a human readable string. * * @param[in] error int: Error number * @returns Error string */ OPUS_EXPORT const char *opus_strerror(int error); /** Gets the libopus version string. * * Applications may look for the substring "-fixed" in the version string to * determine whether they have a fixed-point or floating-point build at * runtime. * * @returns Version string */ OPUS_EXPORT const char *opus_get_version_string(void); /**@}*/ #ifdef __cplusplus } #endif #endif /* OPUS_DEFINES_H */ ================================================ FILE: deps/pjsip/third_party/opus/include/opus_multistream.h ================================================ /* Copyright (c) 2011 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file opus_multistream.h * @brief Opus reference implementation multistream API */ #ifndef OPUS_MULTISTREAM_H #define OPUS_MULTISTREAM_H #include "opus.h" #ifdef __cplusplus extern "C" { #endif /** @cond OPUS_INTERNAL_DOC */ /** Macros to trigger compilation errors when the wrong types are provided to a * CTL. */ /**@{*/ #define __opus_check_encstate_ptr(ptr) ((ptr) + ((ptr) - (OpusEncoder**)(ptr))) #define __opus_check_decstate_ptr(ptr) ((ptr) + ((ptr) - (OpusDecoder**)(ptr))) /**@}*/ /** These are the actual encoder and decoder CTL ID numbers. * They should not be used directly by applications. * In general, SETs should be even and GETs should be odd.*/ /**@{*/ #define OPUS_MULTISTREAM_GET_ENCODER_STATE_REQUEST 5120 #define OPUS_MULTISTREAM_GET_DECODER_STATE_REQUEST 5122 /**@}*/ /** @endcond */ /** @defgroup opus_multistream_ctls Multistream specific encoder and decoder CTLs * * These are convenience macros that are specific to the * opus_multistream_encoder_ctl() and opus_multistream_decoder_ctl() * interface. * The CTLs from @ref opus_genericctls, @ref opus_encoderctls, and * @ref opus_decoderctls may be applied to a multistream encoder or decoder as * well. * In addition, you may retrieve the encoder or decoder state for an specific * stream via #OPUS_MULTISTREAM_GET_ENCODER_STATE or * #OPUS_MULTISTREAM_GET_DECODER_STATE and apply CTLs to it individually. */ /**@{*/ /** Gets the encoder state for an individual stream of a multistream encoder. * @param[in] x opus_int32: The index of the stream whose encoder you * wish to retrieve. * This must be non-negative and less than * the streams parameter used * to initialize the encoder. * @param[out] y OpusEncoder**: Returns a pointer to the given * encoder state. * @retval OPUS_BAD_ARG The index of the requested stream was out of range. * @hideinitializer */ #define OPUS_MULTISTREAM_GET_ENCODER_STATE(x,y) OPUS_MULTISTREAM_GET_ENCODER_STATE_REQUEST, __opus_check_int(x), __opus_check_encstate_ptr(y) /** Gets the decoder state for an individual stream of a multistream decoder. * @param[in] x opus_int32: The index of the stream whose decoder you * wish to retrieve. * This must be non-negative and less than * the streams parameter used * to initialize the decoder. * @param[out] y OpusDecoder**: Returns a pointer to the given * decoder state. * @retval OPUS_BAD_ARG The index of the requested stream was out of range. * @hideinitializer */ #define OPUS_MULTISTREAM_GET_DECODER_STATE(x,y) OPUS_MULTISTREAM_GET_DECODER_STATE_REQUEST, __opus_check_int(x), __opus_check_decstate_ptr(y) /**@}*/ /** @defgroup opus_multistream Opus Multistream API * @{ * * The multistream API allows individual Opus streams to be combined into a * single packet, enabling support for up to 255 channels. Unlike an * elementary Opus stream, the encoder and decoder must negotiate the channel * configuration before the decoder can successfully interpret the data in the * packets produced by the encoder. Some basic information, such as packet * duration, can be computed without any special negotiation. * * The format for multistream Opus packets is defined in the * Ogg * encapsulation specification and is based on the self-delimited Opus * framing described in Appendix B of RFC 6716. * Normal Opus packets are just a degenerate case of multistream Opus packets, * and can be encoded or decoded with the multistream API by setting * streams to 1 when initializing the encoder or * decoder. * * Multistream Opus streams can contain up to 255 elementary Opus streams. * These may be either "uncoupled" or "coupled", indicating that the decoder * is configured to decode them to either 1 or 2 channels, respectively. * The streams are ordered so that all coupled streams appear at the * beginning. * * A mapping table defines which decoded channel i * should be used for each input/output (I/O) channel j. This table is * typically provided as an unsigned char array. * Let i = mapping[j] be the index for I/O channel j. * If i < 2*coupled_streams, then I/O channel j is * encoded as the left channel of stream (i/2) if i * is even, or as the right channel of stream (i/2) if * i is odd. Otherwise, I/O channel j is encoded as * mono in stream (i - coupled_streams), unless it has the special * value 255, in which case it is omitted from the encoding entirely (the * decoder will reproduce it as silence). Each value i must either * be the special value 255 or be less than streams + coupled_streams. * * The output channels specified by the encoder * should use the * Vorbis * channel ordering. A decoder may wish to apply an additional permutation * to the mapping the encoder used to achieve a different output channel * order (e.g. for outputing in WAV order). * * Each multistream packet contains an Opus packet for each stream, and all of * the Opus packets in a single multistream packet must have the same * duration. Therefore the duration of a multistream packet can be extracted * from the TOC sequence of the first stream, which is located at the * beginning of the packet, just like an elementary Opus stream: * * @code * int nb_samples; * int nb_frames; * nb_frames = opus_packet_get_nb_frames(data, len); * if (nb_frames < 1) * return nb_frames; * nb_samples = opus_packet_get_samples_per_frame(data, 48000) * nb_frames; * @endcode * * The general encoding and decoding process proceeds exactly the same as in * the normal @ref opus_encoder and @ref opus_decoder APIs. * See their documentation for an overview of how to use the corresponding * multistream functions. */ /** Opus multistream encoder state. * This contains the complete state of a multistream Opus encoder. * It is position independent and can be freely copied. * @see opus_multistream_encoder_create * @see opus_multistream_encoder_init */ typedef struct OpusMSEncoder OpusMSEncoder; /** Opus multistream decoder state. * This contains the complete state of a multistream Opus decoder. * It is position independent and can be freely copied. * @see opus_multistream_decoder_create * @see opus_multistream_decoder_init */ typedef struct OpusMSDecoder OpusMSDecoder; /**\name Multistream encoder functions */ /**@{*/ /** Gets the size of an OpusMSEncoder structure. * @param streams int: The total number of streams to encode from the * input. * This must be no more than 255. * @param coupled_streams int: Number of coupled (2 channel) streams * to encode. * This must be no larger than the total * number of streams. * Additionally, The total number of * encoded channels (streams + * coupled_streams) must be no * more than 255. * @returns The size in bytes on success, or a negative error code * (see @ref opus_errorcodes) on error. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_multistream_encoder_get_size( int streams, int coupled_streams ); OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_multistream_surround_encoder_get_size( int channels, int mapping_family ); /** Allocates and initializes a multistream encoder state. * Call opus_multistream_encoder_destroy() to release * this object when finished. * @param Fs opus_int32: Sampling rate of the input signal (in Hz). * This must be one of 8000, 12000, 16000, * 24000, or 48000. * @param channels int: Number of channels in the input signal. * This must be at most 255. * It may be greater than the number of * coded channels (streams + * coupled_streams). * @param streams int: The total number of streams to encode from the * input. * This must be no more than the number of channels. * @param coupled_streams int: Number of coupled (2 channel) streams * to encode. * This must be no larger than the total * number of streams. * Additionally, The total number of * encoded channels (streams + * coupled_streams) must be no * more than the number of input channels. * @param[in] mapping const unsigned char[channels]: Mapping from * encoded channels to input channels, as described in * @ref opus_multistream. As an extra constraint, the * multistream encoder does not allow encoding coupled * streams for which one channel is unused since this * is never a good idea. * @param application int: The target encoder application. * This must be one of the following: *
*
#OPUS_APPLICATION_VOIP
*
Process signal for improved speech intelligibility.
*
#OPUS_APPLICATION_AUDIO
*
Favor faithfulness to the original input.
*
#OPUS_APPLICATION_RESTRICTED_LOWDELAY
*
Configure the minimum possible coding delay by disabling certain modes * of operation.
*
* @param[out] error int *: Returns #OPUS_OK on success, or an error * code (see @ref opus_errorcodes) on * failure. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusMSEncoder *opus_multistream_encoder_create( opus_int32 Fs, int channels, int streams, int coupled_streams, const unsigned char *mapping, int application, int *error ) OPUS_ARG_NONNULL(5); OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusMSEncoder *opus_multistream_surround_encoder_create( opus_int32 Fs, int channels, int mapping_family, int *streams, int *coupled_streams, unsigned char *mapping, int application, int *error ) OPUS_ARG_NONNULL(5); /** Initialize a previously allocated multistream encoder state. * The memory pointed to by \a st must be at least the size returned by * opus_multistream_encoder_get_size(). * This is intended for applications which use their own allocator instead of * malloc. * To reset a previously initialized state, use the #OPUS_RESET_STATE CTL. * @see opus_multistream_encoder_create * @see opus_multistream_encoder_get_size * @param st OpusMSEncoder*: Multistream encoder state to initialize. * @param Fs opus_int32: Sampling rate of the input signal (in Hz). * This must be one of 8000, 12000, 16000, * 24000, or 48000. * @param channels int: Number of channels in the input signal. * This must be at most 255. * It may be greater than the number of * coded channels (streams + * coupled_streams). * @param streams int: The total number of streams to encode from the * input. * This must be no more than the number of channels. * @param coupled_streams int: Number of coupled (2 channel) streams * to encode. * This must be no larger than the total * number of streams. * Additionally, The total number of * encoded channels (streams + * coupled_streams) must be no * more than the number of input channels. * @param[in] mapping const unsigned char[channels]: Mapping from * encoded channels to input channels, as described in * @ref opus_multistream. As an extra constraint, the * multistream encoder does not allow encoding coupled * streams for which one channel is unused since this * is never a good idea. * @param application int: The target encoder application. * This must be one of the following: *
*
#OPUS_APPLICATION_VOIP
*
Process signal for improved speech intelligibility.
*
#OPUS_APPLICATION_AUDIO
*
Favor faithfulness to the original input.
*
#OPUS_APPLICATION_RESTRICTED_LOWDELAY
*
Configure the minimum possible coding delay by disabling certain modes * of operation.
*
* @returns #OPUS_OK on success, or an error code (see @ref opus_errorcodes) * on failure. */ OPUS_EXPORT int opus_multistream_encoder_init( OpusMSEncoder *st, opus_int32 Fs, int channels, int streams, int coupled_streams, const unsigned char *mapping, int application ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(6); OPUS_EXPORT int opus_multistream_surround_encoder_init( OpusMSEncoder *st, opus_int32 Fs, int channels, int mapping_family, int *streams, int *coupled_streams, unsigned char *mapping, int application ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(6); /** Encodes a multistream Opus frame. * @param st OpusMSEncoder*: Multistream encoder state. * @param[in] pcm const opus_int16*: The input signal as interleaved * samples. * This must contain * frame_size*channels * samples. * @param frame_size int: Number of samples per channel in the input * signal. * This must be an Opus frame size for the * encoder's sampling rate. * For example, at 48 kHz the permitted values * are 120, 240, 480, 960, 1920, and 2880. * Passing in a duration of less than 10 ms * (480 samples at 48 kHz) will prevent the * encoder from using the LPC or hybrid modes. * @param[out] data unsigned char*: Output payload. * This must contain storage for at * least \a max_data_bytes. * @param [in] max_data_bytes opus_int32: Size of the allocated * memory for the output * payload. This may be * used to impose an upper limit on * the instant bitrate, but should * not be used as the only bitrate * control. Use #OPUS_SET_BITRATE to * control the bitrate. * @returns The length of the encoded packet (in bytes) on success or a * negative error code (see @ref opus_errorcodes) on failure. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_multistream_encode( OpusMSEncoder *st, const opus_int16 *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4); /** Encodes a multistream Opus frame from floating point input. * @param st OpusMSEncoder*: Multistream encoder state. * @param[in] pcm const float*: The input signal as interleaved * samples with a normal range of * +/-1.0. * Samples with a range beyond +/-1.0 * are supported but will be clipped by * decoders using the integer API and * should only be used if it is known * that the far end supports extended * dynamic range. * This must contain * frame_size*channels * samples. * @param frame_size int: Number of samples per channel in the input * signal. * This must be an Opus frame size for the * encoder's sampling rate. * For example, at 48 kHz the permitted values * are 120, 240, 480, 960, 1920, and 2880. * Passing in a duration of less than 10 ms * (480 samples at 48 kHz) will prevent the * encoder from using the LPC or hybrid modes. * @param[out] data unsigned char*: Output payload. * This must contain storage for at * least \a max_data_bytes. * @param [in] max_data_bytes opus_int32: Size of the allocated * memory for the output * payload. This may be * used to impose an upper limit on * the instant bitrate, but should * not be used as the only bitrate * control. Use #OPUS_SET_BITRATE to * control the bitrate. * @returns The length of the encoded packet (in bytes) on success or a * negative error code (see @ref opus_errorcodes) on failure. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_multistream_encode_float( OpusMSEncoder *st, const float *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4); /** Frees an OpusMSEncoder allocated by * opus_multistream_encoder_create(). * @param st OpusMSEncoder*: Multistream encoder state to be freed. */ OPUS_EXPORT void opus_multistream_encoder_destroy(OpusMSEncoder *st); /** Perform a CTL function on a multistream Opus encoder. * * Generally the request and subsequent arguments are generated by a * convenience macro. * @param st OpusMSEncoder*: Multistream encoder state. * @param request This and all remaining parameters should be replaced by one * of the convenience macros in @ref opus_genericctls, * @ref opus_encoderctls, or @ref opus_multistream_ctls. * @see opus_genericctls * @see opus_encoderctls * @see opus_multistream_ctls */ OPUS_EXPORT int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...) OPUS_ARG_NONNULL(1); /**@}*/ /**\name Multistream decoder functions */ /**@{*/ /** Gets the size of an OpusMSDecoder structure. * @param streams int: The total number of streams coded in the * input. * This must be no more than 255. * @param coupled_streams int: Number streams to decode as coupled * (2 channel) streams. * This must be no larger than the total * number of streams. * Additionally, The total number of * coded channels (streams + * coupled_streams) must be no * more than 255. * @returns The size in bytes on success, or a negative error code * (see @ref opus_errorcodes) on error. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_multistream_decoder_get_size( int streams, int coupled_streams ); /** Allocates and initializes a multistream decoder state. * Call opus_multistream_decoder_destroy() to release * this object when finished. * @param Fs opus_int32: Sampling rate to decode at (in Hz). * This must be one of 8000, 12000, 16000, * 24000, or 48000. * @param channels int: Number of channels to output. * This must be at most 255. * It may be different from the number of coded * channels (streams + * coupled_streams). * @param streams int: The total number of streams coded in the * input. * This must be no more than 255. * @param coupled_streams int: Number of streams to decode as coupled * (2 channel) streams. * This must be no larger than the total * number of streams. * Additionally, The total number of * coded channels (streams + * coupled_streams) must be no * more than 255. * @param[in] mapping const unsigned char[channels]: Mapping from * coded channels to output channels, as described in * @ref opus_multistream. * @param[out] error int *: Returns #OPUS_OK on success, or an error * code (see @ref opus_errorcodes) on * failure. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusMSDecoder *opus_multistream_decoder_create( opus_int32 Fs, int channels, int streams, int coupled_streams, const unsigned char *mapping, int *error ) OPUS_ARG_NONNULL(5); /** Intialize a previously allocated decoder state object. * The memory pointed to by \a st must be at least the size returned by * opus_multistream_encoder_get_size(). * This is intended for applications which use their own allocator instead of * malloc. * To reset a previously initialized state, use the #OPUS_RESET_STATE CTL. * @see opus_multistream_decoder_create * @see opus_multistream_deocder_get_size * @param st OpusMSEncoder*: Multistream encoder state to initialize. * @param Fs opus_int32: Sampling rate to decode at (in Hz). * This must be one of 8000, 12000, 16000, * 24000, or 48000. * @param channels int: Number of channels to output. * This must be at most 255. * It may be different from the number of coded * channels (streams + * coupled_streams). * @param streams int: The total number of streams coded in the * input. * This must be no more than 255. * @param coupled_streams int: Number of streams to decode as coupled * (2 channel) streams. * This must be no larger than the total * number of streams. * Additionally, The total number of * coded channels (streams + * coupled_streams) must be no * more than 255. * @param[in] mapping const unsigned char[channels]: Mapping from * coded channels to output channels, as described in * @ref opus_multistream. * @returns #OPUS_OK on success, or an error code (see @ref opus_errorcodes) * on failure. */ OPUS_EXPORT int opus_multistream_decoder_init( OpusMSDecoder *st, opus_int32 Fs, int channels, int streams, int coupled_streams, const unsigned char *mapping ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(6); /** Decode a multistream Opus packet. * @param st OpusMSDecoder*: Multistream decoder state. * @param[in] data const unsigned char*: Input payload. * Use a NULL * pointer to indicate packet * loss. * @param len opus_int32: Number of bytes in payload. * @param[out] pcm opus_int16*: Output signal, with interleaved * samples. * This must contain room for * frame_size*channels * samples. * @param frame_size int: The number of samples per channel of * available space in \a pcm. * If this is less than the maximum packet duration * (120 ms; 5760 for 48kHz), this function will not be capable * of decoding some packets. In the case of PLC (data==NULL) * or FEC (decode_fec=1), then frame_size needs to be exactly * the duration of audio that is missing, otherwise the * decoder will not be in the optimal state to decode the * next incoming packet. For the PLC and FEC cases, frame_size * must be a multiple of 2.5 ms. * @param decode_fec int: Flag (0 or 1) to request that any in-band * forward error correction data be decoded. * If no such data is available, the frame is * decoded as if it were lost. * @returns Number of samples decoded on success or a negative error code * (see @ref opus_errorcodes) on failure. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_multistream_decode( OpusMSDecoder *st, const unsigned char *data, opus_int32 len, opus_int16 *pcm, int frame_size, int decode_fec ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4); /** Decode a multistream Opus packet with floating point output. * @param st OpusMSDecoder*: Multistream decoder state. * @param[in] data const unsigned char*: Input payload. * Use a NULL * pointer to indicate packet * loss. * @param len opus_int32: Number of bytes in payload. * @param[out] pcm opus_int16*: Output signal, with interleaved * samples. * This must contain room for * frame_size*channels * samples. * @param frame_size int: The number of samples per channel of * available space in \a pcm. * If this is less than the maximum packet duration * (120 ms; 5760 for 48kHz), this function will not be capable * of decoding some packets. In the case of PLC (data==NULL) * or FEC (decode_fec=1), then frame_size needs to be exactly * the duration of audio that is missing, otherwise the * decoder will not be in the optimal state to decode the * next incoming packet. For the PLC and FEC cases, frame_size * must be a multiple of 2.5 ms. * @param decode_fec int: Flag (0 or 1) to request that any in-band * forward error correction data be decoded. * If no such data is available, the frame is * decoded as if it were lost. * @returns Number of samples decoded on success or a negative error code * (see @ref opus_errorcodes) on failure. */ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_multistream_decode_float( OpusMSDecoder *st, const unsigned char *data, opus_int32 len, float *pcm, int frame_size, int decode_fec ) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4); /** Perform a CTL function on a multistream Opus decoder. * * Generally the request and subsequent arguments are generated by a * convenience macro. * @param st OpusMSDecoder*: Multistream decoder state. * @param request This and all remaining parameters should be replaced by one * of the convenience macros in @ref opus_genericctls, * @ref opus_decoderctls, or @ref opus_multistream_ctls. * @see opus_genericctls * @see opus_decoderctls * @see opus_multistream_ctls */ OPUS_EXPORT int opus_multistream_decoder_ctl(OpusMSDecoder *st, int request, ...) OPUS_ARG_NONNULL(1); /** Frees an OpusMSDecoder allocated by * opus_multistream_decoder_create(). * @param st OpusMSDecoder: Multistream decoder state to be freed. */ OPUS_EXPORT void opus_multistream_decoder_destroy(OpusMSDecoder *st); /**@}*/ /**@}*/ #ifdef __cplusplus } #endif #endif /* OPUS_MULTISTREAM_H */ ================================================ FILE: deps/pjsip/third_party/opus/include/opus_types.h ================================================ /* (C) COPYRIGHT 1994-2002 Xiph.Org Foundation */ /* Modified by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* opus_types.h based on ogg_types.h from libogg */ /** @file opus_types.h @brief Opus reference implementation types */ #ifndef OPUS_TYPES_H #define OPUS_TYPES_H /* Use the real stdint.h if it's there (taken from Paul Hsieh's pstdint.h) */ #if (defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) || defined (HAVE_STDINT_H)) #include typedef int16_t opus_int16; typedef uint16_t opus_uint16; typedef int32_t opus_int32; typedef uint32_t opus_uint32; #elif defined(_WIN32) # if defined(__CYGWIN__) # include <_G_config.h> typedef _G_int32_t opus_int32; typedef _G_uint32_t opus_uint32; typedef _G_int16 opus_int16; typedef _G_uint16 opus_uint16; # elif defined(__MINGW32__) typedef short opus_int16; typedef unsigned short opus_uint16; typedef int opus_int32; typedef unsigned int opus_uint32; # elif defined(__MWERKS__) typedef int opus_int32; typedef unsigned int opus_uint32; typedef short opus_int16; typedef unsigned short opus_uint16; # else /* MSVC/Borland */ typedef __int32 opus_int32; typedef unsigned __int32 opus_uint32; typedef __int16 opus_int16; typedef unsigned __int16 opus_uint16; # endif #elif defined(__MACOS__) # include typedef SInt16 opus_int16; typedef UInt16 opus_uint16; typedef SInt32 opus_int32; typedef UInt32 opus_uint32; #elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */ # include typedef int16_t opus_int16; typedef u_int16_t opus_uint16; typedef int32_t opus_int32; typedef u_int32_t opus_uint32; #elif defined(__BEOS__) /* Be */ # include typedef int16 opus_int16; typedef u_int16 opus_uint16; typedef int32_t opus_int32; typedef u_int32_t opus_uint32; #elif defined (__EMX__) /* OS/2 GCC */ typedef short opus_int16; typedef unsigned short opus_uint16; typedef int opus_int32; typedef unsigned int opus_uint32; #elif defined (DJGPP) /* DJGPP */ typedef short opus_int16; typedef unsigned short opus_uint16; typedef int opus_int32; typedef unsigned int opus_uint32; #elif defined(R5900) /* PS2 EE */ typedef int opus_int32; typedef unsigned opus_uint32; typedef short opus_int16; typedef unsigned short opus_uint16; #elif defined(__SYMBIAN32__) /* Symbian GCC */ typedef signed short opus_int16; typedef unsigned short opus_uint16; typedef signed int opus_int32; typedef unsigned int opus_uint32; #elif defined(CONFIG_TI_C54X) || defined (CONFIG_TI_C55X) typedef short opus_int16; typedef unsigned short opus_uint16; typedef long opus_int32; typedef unsigned long opus_uint32; #elif defined(CONFIG_TI_C6X) typedef short opus_int16; typedef unsigned short opus_uint16; typedef int opus_int32; typedef unsigned int opus_uint32; #else /* Give up, take a reasonable guess */ typedef short opus_int16; typedef unsigned short opus_uint16; typedef int opus_int32; typedef unsigned int opus_uint32; #endif #define opus_int int /* used for counters etc; at least 16 bits */ #define opus_int64 long long #define opus_int8 signed char #define opus_uint unsigned int /* used for counters etc; at least 16 bits */ #define opus_uint64 unsigned long long #define opus_uint8 unsigned char #endif /* OPUS_TYPES_H */ ================================================ FILE: deps/pjsip/third_party/opus/m4/as-gcc-inline-assembly.m4 ================================================ dnl as-gcc-inline-assembly.m4 0.1.0 dnl autostars m4 macro for detection of gcc inline assembly dnl David Schleef dnl AS_COMPILER_FLAG(ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) dnl Tries to compile with the given CFLAGS. dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags, dnl and ACTION-IF-NOT-ACCEPTED otherwise. AC_DEFUN([AS_GCC_INLINE_ASSEMBLY], [ AC_MSG_CHECKING([if compiler supports gcc-style inline assembly]) AC_TRY_COMPILE([], [ #ifdef __GNUC_MINOR__ #if (__GNUC__ * 1000 + __GNUC_MINOR__) < 3004 #error GCC before 3.4 has critical bugs compiling inline assembly #endif #endif __asm__ (""::) ], [flag_ok=yes], [flag_ok=no]) if test "X$flag_ok" = Xyes ; then $1 true else $2 true fi AC_MSG_RESULT([$flag_ok]) ]) AC_DEFUN([AS_ASM_ARM_NEON], [ AC_MSG_CHECKING([if assembler supports NEON instructions on ARM]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[__asm__("vorr d0,d0,d0")])], [AC_MSG_RESULT([yes]) $1], [AC_MSG_RESULT([no]) $2]) ]) AC_DEFUN([AS_ASM_ARM_NEON_FORCE], [ AC_MSG_CHECKING([if assembler supports NEON instructions on ARM]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[__asm__(".arch armv7-a\n.fpu neon\n.object_arch armv4t\nvorr d0,d0,d0")])], [AC_MSG_RESULT([yes]) $1], [AC_MSG_RESULT([no]) $2]) ]) AC_DEFUN([AS_ASM_ARM_MEDIA], [ AC_MSG_CHECKING([if assembler supports ARMv6 media instructions on ARM]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[__asm__("shadd8 r3,r3,r3")])], [AC_MSG_RESULT([yes]) $1], [AC_MSG_RESULT([no]) $2]) ]) AC_DEFUN([AS_ASM_ARM_MEDIA_FORCE], [ AC_MSG_CHECKING([if assembler supports ARMv6 media instructions on ARM]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[__asm__(".arch armv6\n.object_arch armv4t\nshadd8 r3,r3,r3")])], [AC_MSG_RESULT([yes]) $1], [AC_MSG_RESULT([no]) $2]) ]) AC_DEFUN([AS_ASM_ARM_EDSP], [ AC_MSG_CHECKING([if assembler supports EDSP instructions on ARM]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[__asm__("qadd r3,r3,r3")])], [AC_MSG_RESULT([yes]) $1], [AC_MSG_RESULT([no]) $2]) ]) AC_DEFUN([AS_ASM_ARM_EDSP_FORCE], [ AC_MSG_CHECKING([if assembler supports EDSP instructions on ARM]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[__asm__(".arch armv5te\n.object_arch armv4t\nqadd r3,r3,r3")])], [AC_MSG_RESULT([yes]) $1], [AC_MSG_RESULT([no]) $2]) ]) ================================================ FILE: deps/pjsip/third_party/opus/m4/libtool.m4 ================================================ # libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Free Software # Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. m4_define([_LT_COPYING], [dnl # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008, 2009, 2010, 2011 Free Software # Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is part of GNU Libtool. # # GNU Libtool is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, or # obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ]) # serial 57 LT_INIT # LT_PREREQ(VERSION) # ------------------ # Complain and exit if this libtool version is less that VERSION. m4_defun([LT_PREREQ], [m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, [m4_default([$3], [m4_fatal([Libtool version $1 or higher is required], 63)])], [$2])]) # _LT_CHECK_BUILDDIR # ------------------ # Complain if the absolute build directory name contains unusual characters m4_defun([_LT_CHECK_BUILDDIR], [case `pwd` in *\ * | *\ *) AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; esac ]) # LT_INIT([OPTIONS]) # ------------------ AC_DEFUN([LT_INIT], [AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl AC_BEFORE([$0], [LTDL_INIT])dnl m4_require([_LT_CHECK_BUILDDIR])dnl dnl Autoconf doesn't catch unexpanded LT_ macros by default: m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 dnl unless we require an AC_DEFUNed macro: AC_REQUIRE([LTOPTIONS_VERSION])dnl AC_REQUIRE([LTSUGAR_VERSION])dnl AC_REQUIRE([LTVERSION_VERSION])dnl AC_REQUIRE([LTOBSOLETE_VERSION])dnl m4_require([_LT_PROG_LTMAIN])dnl _LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed LIBTOOL_DEPS="$ltmain" # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' AC_SUBST(LIBTOOL)dnl _LT_SETUP # Only expand once: m4_define([LT_INIT]) ])# LT_INIT # Old names: AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PROG_LIBTOOL], []) dnl AC_DEFUN([AM_PROG_LIBTOOL], []) # _LT_CC_BASENAME(CC) # ------------------- # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. m4_defun([_LT_CC_BASENAME], [for cc_temp in $1""; do case $cc_temp in compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` ]) # _LT_FILEUTILS_DEFAULTS # ---------------------- # It is okay to use these file commands and assume they have been set # sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. m4_defun([_LT_FILEUTILS_DEFAULTS], [: ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} ])# _LT_FILEUTILS_DEFAULTS # _LT_SETUP # --------- m4_defun([_LT_SETUP], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl _LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl dnl _LT_DECL([], [host_alias], [0], [The host system])dnl _LT_DECL([], [host], [0])dnl _LT_DECL([], [host_os], [0])dnl dnl _LT_DECL([], [build_alias], [0], [The build system])dnl _LT_DECL([], [build], [0])dnl _LT_DECL([], [build_os], [0])dnl dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl dnl AC_REQUIRE([AC_PROG_LN_S])dnl test -z "$LN_S" && LN_S="ln -s" _LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl dnl AC_REQUIRE([LT_CMD_MAX_LEN])dnl _LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl _LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl m4_require([_LT_CMD_RELOAD])dnl m4_require([_LT_CHECK_MAGIC_METHOD])dnl m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_WITH_SYSROOT])dnl _LT_CONFIG_LIBTOOL_INIT([ # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi ]) if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi _LT_CHECK_OBJDIR m4_require([_LT_TAG_COMPILER])dnl case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld="$lt_cv_prog_gnu_ld" old_CC="$CC" old_CFLAGS="$CFLAGS" # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o _LT_CC_BASENAME([$compiler]) # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then _LT_PATH_MAGIC fi ;; esac # Use C for the default configuration in the libtool script LT_SUPPORTED_TAG([CC]) _LT_LANG_C_CONFIG _LT_LANG_DEFAULT_CONFIG _LT_CONFIG_COMMANDS ])# _LT_SETUP # _LT_PREPARE_SED_QUOTE_VARS # -------------------------- # Define a few sed substitution that help us do robust quoting. m4_defun([_LT_PREPARE_SED_QUOTE_VARS], [# Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\([["`\\]]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ]) # _LT_PROG_LTMAIN # --------------- # Note that this code is called both from `configure', and `config.status' # now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, # `config.status' has no value for ac_aux_dir unless we are using Automake, # so we pass a copy along to make sure it has a sensible value anyway. m4_defun([_LT_PROG_LTMAIN], [m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl _LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) ltmain="$ac_aux_dir/ltmain.sh" ])# _LT_PROG_LTMAIN ## ------------------------------------- ## ## Accumulate code for creating libtool. ## ## ------------------------------------- ## # So that we can recreate a full libtool script including additional # tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS # in macros and then make a single call at the end using the `libtool' # label. # _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) # ---------------------------------------- # Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL_INIT], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_INIT], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_INIT]) # _LT_CONFIG_LIBTOOL([COMMANDS]) # ------------------------------ # Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) # _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) # ----------------------------------------------------- m4_defun([_LT_CONFIG_SAVE_COMMANDS], [_LT_CONFIG_LIBTOOL([$1]) _LT_CONFIG_LIBTOOL_INIT([$2]) ]) # _LT_FORMAT_COMMENT([COMMENT]) # ----------------------------- # Add leading comment marks to the start of each line, and a trailing # full-stop to the whole comment if one is not present already. m4_define([_LT_FORMAT_COMMENT], [m4_ifval([$1], [ m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) )]) ## ------------------------ ## ## FIXME: Eliminate VARNAME ## ## ------------------------ ## # _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) # ------------------------------------------------------------------- # CONFIGNAME is the name given to the value in the libtool script. # VARNAME is the (base) name used in the configure script. # VALUE may be 0, 1 or 2 for a computed quote escaped value based on # VARNAME. Any other value will be used directly. m4_define([_LT_DECL], [lt_if_append_uniq([lt_decl_varnames], [$2], [, ], [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], [m4_ifval([$1], [$1], [$2])]) lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) m4_ifval([$4], [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) lt_dict_add_subkey([lt_decl_dict], [$2], [tagged?], [m4_ifval([$5], [yes], [no])])]) ]) # _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) # -------------------------------------------------------- m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) # lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_tag_varnames], [_lt_decl_filter([tagged?], [yes], $@)]) # _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) # --------------------------------------------------------- m4_define([_lt_decl_filter], [m4_case([$#], [0], [m4_fatal([$0: too few arguments: $#])], [1], [m4_fatal([$0: too few arguments: $#: $1])], [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], [lt_dict_filter([lt_decl_dict], $@)])[]dnl ]) # lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) # -------------------------------------------------- m4_define([lt_decl_quote_varnames], [_lt_decl_filter([value], [1], $@)]) # lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_dquote_varnames], [_lt_decl_filter([value], [2], $@)]) # lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_varnames_tagged], [m4_assert([$# <= 2])dnl _$0(m4_quote(m4_default([$1], [[, ]])), m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) m4_define([_lt_decl_varnames_tagged], [m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) # lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_all_varnames], [_$0(m4_quote(m4_default([$1], [[, ]])), m4_if([$2], [], m4_quote(lt_decl_varnames), m4_quote(m4_shift($@))))[]dnl ]) m4_define([_lt_decl_all_varnames], [lt_join($@, lt_decl_varnames_tagged([$1], lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl ]) # _LT_CONFIG_STATUS_DECLARE([VARNAME]) # ------------------------------------ # Quote a variable value, and forward it to `config.status' so that its # declaration there will have the same value as in `configure'. VARNAME # must have a single quote delimited value for this to work. m4_define([_LT_CONFIG_STATUS_DECLARE], [$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) # _LT_CONFIG_STATUS_DECLARATIONS # ------------------------------ # We delimit libtool config variables with single quotes, so when # we write them to config.status, we have to be sure to quote all # embedded single quotes properly. In configure, this macro expands # each variable declared with _LT_DECL (and _LT_TAGDECL) into: # # ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], [m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAGS # ---------------- # Output comment and list of tags supported by the script m4_defun([_LT_LIBTOOL_TAGS], [_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl available_tags="_LT_TAGS"dnl ]) # _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) # ----------------------------------- # Extract the dictionary values for VARNAME (optionally with TAG) and # expand to a commented shell variable setting: # # # Some comment about what VAR is for. # visible_name=$lt_internal_name m4_define([_LT_LIBTOOL_DECLARE], [_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [description])))[]dnl m4_pushdef([_libtool_name], m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), [0], [_libtool_name=[$]$1], [1], [_libtool_name=$lt_[]$1], [2], [_libtool_name=$lt_[]$1], [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl ]) # _LT_LIBTOOL_CONFIG_VARS # ----------------------- # Produce commented declarations of non-tagged libtool config variables # suitable for insertion in the LIBTOOL CONFIG section of the `libtool' # script. Tagged libtool config variables (even for the LIBTOOL CONFIG # section) are produced by _LT_LIBTOOL_TAG_VARS. m4_defun([_LT_LIBTOOL_CONFIG_VARS], [m4_foreach([_lt_var], m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAG_VARS(TAG) # ------------------------- m4_define([_LT_LIBTOOL_TAG_VARS], [m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) # _LT_TAGVAR(VARNAME, [TAGNAME]) # ------------------------------ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) # _LT_CONFIG_COMMANDS # ------------------- # Send accumulated output to $CONFIG_STATUS. Thanks to the lists of # variables for single and double quote escaping we saved from calls # to _LT_DECL, we can put quote escaped variables declarations # into `config.status', and then the shell code to quote escape them in # for loops in `config.status'. Finally, any additional code accumulated # from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. m4_defun([_LT_CONFIG_COMMANDS], [AC_PROVIDE_IFELSE([LT_OUTPUT], dnl If the libtool generation code has been placed in $CONFIG_LT, dnl instead of duplicating it all over again into config.status, dnl then we will have config.status run $CONFIG_LT later, so it dnl needs to know what name is stored there: [AC_CONFIG_COMMANDS([libtool], [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], dnl If the libtool generation code is destined for config.status, dnl expand the accumulated commands and init code now: [AC_CONFIG_COMMANDS([libtool], [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) ])#_LT_CONFIG_COMMANDS # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], [ # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' _LT_CONFIG_STATUS_DECLARATIONS LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$[]1 _LTECHO_EOF' } # Quote evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_quote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_dquote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done _LT_OUTPUT_LIBTOOL_INIT ]) # _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) # ------------------------------------ # Generate a child script FILE with all initialization necessary to # reuse the environment learned by the parent script, and make the # file executable. If COMMENT is supplied, it is inserted after the # `#!' sequence but before initialization text begins. After this # macro, additional text can be appended to FILE to form the body of # the child script. The macro ends with non-zero status if the # file could not be fully written (such as if the disk is full). m4_ifdef([AS_INIT_GENERATED], [m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], [m4_defun([_LT_GENERATED_FILE_INIT], [m4_require([AS_PREPARE])]dnl [m4_pushdef([AS_MESSAGE_LOG_FD])]dnl [lt_write_fail=0 cat >$1 <<_ASEOF || lt_write_fail=1 #! $SHELL # Generated by $as_me. $2 SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$1 <<\_ASEOF || lt_write_fail=1 AS_SHELL_SANITIZE _AS_PREPARE exec AS_MESSAGE_FD>&1 _ASEOF test $lt_write_fail = 0 && chmod +x $1[]dnl m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT # LT_OUTPUT # --------- # This macro allows early generation of the libtool script (before # AC_OUTPUT is called), incase it is used in configure for compilation # tests. AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} AC_MSG_NOTICE([creating $CONFIG_LT]) _LT_GENERATED_FILE_INIT(["$CONFIG_LT"], [# Run this file to recreate a libtool stub with the current configuration.]) cat >>"$CONFIG_LT" <<\_LTEOF lt_cl_silent=false exec AS_MESSAGE_LOG_FD>>config.log { echo AS_BOX([Running $as_me.]) } >&AS_MESSAGE_LOG_FD lt_cl_help="\ \`$as_me' creates a local libtool stub from the current configuration, for use in further configure time tests before the real libtool is generated. Usage: $[0] [[OPTIONS]] -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files Report bugs to ." lt_cl_version="\ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) configured by $[0], generated by m4_PACKAGE_STRING. Copyright (C) 2011 Free Software Foundation, Inc. This config.lt script is free software; the Free Software Foundation gives unlimited permision to copy, distribute and modify it." while test $[#] != 0 do case $[1] in --version | --v* | -V ) echo "$lt_cl_version"; exit 0 ;; --help | --h* | -h ) echo "$lt_cl_help"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --quiet | --q* | --silent | --s* | -q ) lt_cl_silent=: ;; -*) AC_MSG_ERROR([unrecognized option: $[1] Try \`$[0] --help' for more information.]) ;; *) AC_MSG_ERROR([unrecognized argument: $[1] Try \`$[0] --help' for more information.]) ;; esac shift done if $lt_cl_silent; then exec AS_MESSAGE_FD>/dev/null fi _LTEOF cat >>"$CONFIG_LT" <<_LTEOF _LT_OUTPUT_LIBTOOL_COMMANDS_INIT _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AC_MSG_NOTICE([creating $ofile]) _LT_OUTPUT_LIBTOOL_COMMANDS AS_EXIT(0) _LTEOF chmod +x "$CONFIG_LT" # configure is writing to config.log, but config.lt does its own redirection, # appending to config.log, which fails on DOS, as config.log is still kept # open by configure. Here we exec the FD to /dev/null, effectively closing # config.log, so it can be properly (re)opened and appended to by config.lt. lt_cl_success=: test "$silent" = yes && lt_config_lt_args="$lt_config_lt_args --quiet" exec AS_MESSAGE_LOG_FD>/dev/null $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false exec AS_MESSAGE_LOG_FD>>config.log $lt_cl_success || AS_EXIT(1) ])# LT_OUTPUT # _LT_CONFIG(TAG) # --------------- # If TAG is the built-in tag, create an initial libtool script with a # default configuration from the untagged config vars. Otherwise add code # to config.status for appending the configuration named by TAG from the # matching tagged config vars. m4_defun([_LT_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_CONFIG_SAVE_COMMANDS([ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl m4_if(_LT_TAG, [C], [ # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi cfgfile="${ofile}T" trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. # Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION # Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # _LT_COPYING _LT_LIBTOOL_TAGS # ### BEGIN LIBTOOL CONFIG _LT_LIBTOOL_CONFIG_VARS _LT_LIBTOOL_TAG_VARS # ### END LIBTOOL CONFIG _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac _LT_PROG_LTMAIN # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) _LT_PROG_REPLACE_SHELLFNS mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ], [cat <<_LT_EOF >> "$ofile" dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded dnl in a comment (ie after a #). # ### BEGIN LIBTOOL TAG CONFIG: $1 _LT_LIBTOOL_TAG_VARS(_LT_TAG) # ### END LIBTOOL TAG CONFIG: $1 _LT_EOF ])dnl /m4_if ], [m4_if([$1], [], [ PACKAGE='$PACKAGE' VERSION='$VERSION' TIMESTAMP='$TIMESTAMP' RM='$RM' ofile='$ofile'], []) ])dnl /_LT_CONFIG_SAVE_COMMANDS ])# _LT_CONFIG # LT_SUPPORTED_TAG(TAG) # --------------------- # Trace this macro to discover what tags are supported by the libtool # --tag option, using: # autoconf --trace 'LT_SUPPORTED_TAG:$1' AC_DEFUN([LT_SUPPORTED_TAG], []) # C support is built-in for now m4_define([_LT_LANG_C_enabled], []) m4_define([_LT_TAGS], []) # LT_LANG(LANG) # ------------- # Enable libtool support for the given language if not already enabled. AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], [Go], [_LT_LANG(GO)], [Java], [_LT_LANG(GCJ)], [Fortran 77], [_LT_LANG(F77)], [Fortran], [_LT_LANG(FC)], [Windows Resource], [_LT_LANG(RC)], [m4_ifdef([_LT_LANG_]$1[_CONFIG], [_LT_LANG($1)], [m4_fatal([$0: unsupported language: "$1"])])])dnl ])# LT_LANG # _LT_LANG(LANGNAME) # ------------------ m4_defun([_LT_LANG], [m4_ifdef([_LT_LANG_]$1[_enabled], [], [LT_SUPPORTED_TAG([$1])dnl m4_append([_LT_TAGS], [$1 ])dnl m4_define([_LT_LANG_]$1[_enabled], [])dnl _LT_LANG_$1_CONFIG($1)])dnl ])# _LT_LANG m4_ifndef([AC_PROG_GO], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_GO. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_GO], [AC_LANG_PUSH(Go)dnl AC_ARG_VAR([GOC], [Go compiler command])dnl AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl _AC_ARG_VAR_LDFLAGS()dnl AC_CHECK_TOOL(GOC, gccgo) if test -z "$GOC"; then if test -n "$ac_tool_prefix"; then AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) fi fi if test -z "$GOC"; then AC_CHECK_PROG(GOC, gccgo, gccgo, false) fi ])#m4_defun ])#m4_ifndef # _LT_LANG_DEFAULT_CONFIG # ----------------------- m4_defun([_LT_LANG_DEFAULT_CONFIG], [AC_PROVIDE_IFELSE([AC_PROG_CXX], [LT_LANG(CXX)], [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) AC_PROVIDE_IFELSE([AC_PROG_F77], [LT_LANG(F77)], [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) AC_PROVIDE_IFELSE([AC_PROG_FC], [LT_LANG(FC)], [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal dnl pulling things in needlessly. AC_PROVIDE_IFELSE([AC_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([LT_PROG_GCJ], [LT_LANG(GCJ)], [m4_ifdef([AC_PROG_GCJ], [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([A][M_PROG_GCJ], [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([LT_PROG_GCJ], [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) AC_PROVIDE_IFELSE([AC_PROG_GO], [LT_LANG(GO)], [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) AC_PROVIDE_IFELSE([LT_PROG_RC], [LT_LANG(RC)], [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) ])# _LT_LANG_DEFAULT_CONFIG # Obsolete macros: AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_CXX], []) dnl AC_DEFUN([AC_LIBTOOL_F77], []) dnl AC_DEFUN([AC_LIBTOOL_FC], []) dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) dnl AC_DEFUN([AC_LIBTOOL_RC], []) # _LT_TAG_COMPILER # ---------------- m4_defun([_LT_TAG_COMPILER], [AC_REQUIRE([AC_PROG_CC])dnl _LT_DECL([LTCC], [CC], [1], [A C compiler])dnl _LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl _LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl _LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC ])# _LT_TAG_COMPILER # _LT_COMPILER_BOILERPLATE # ------------------------ # Check for compiler boilerplate output or warnings with # the simple compiler test code. m4_defun([_LT_COMPILER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ])# _LT_COMPILER_BOILERPLATE # _LT_LINKER_BOILERPLATE # ---------------------- # Check for linker boilerplate output or warnings with # the simple link test code. m4_defun([_LT_LINKER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ])# _LT_LINKER_BOILERPLATE # _LT_REQUIRED_DARWIN_CHECKS # ------------------------- m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ case $host_os in rhapsody* | darwin*) AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) AC_CHECK_TOOL([LIPO], [lipo], [:]) AC_CHECK_TOOL([OTOOL], [otool], [:]) AC_CHECK_TOOL([OTOOL64], [otool64], [:]) _LT_DECL([], [DSYMUTIL], [1], [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) _LT_DECL([], [NMEDIT], [1], [Tool to change global to local symbols on Mac OS X]) _LT_DECL([], [LIPO], [1], [Tool to manipulate fat objects and archives on Mac OS X]) _LT_DECL([], [OTOOL], [1], [ldd/readelf like tool for Mach-O binaries on Mac OS X]) _LT_DECL([], [OTOOL64], [1], [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], [lt_cv_apple_cc_single_mod=no if test -z "${LT_MULTI_MODULE}"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test $_lt_result -eq 0; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -rf libconftest.dylib* rm -f conftest.* fi]) AC_CACHE_CHECK([for -exported_symbols_list linker flag], [lt_cv_ld_exported_symbols_list], [lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [lt_cv_ld_exported_symbols_list=yes], [lt_cv_ld_exported_symbols_list=no]) LDFLAGS="$save_LDFLAGS" ]) AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], [lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then lt_cv_ld_force_load=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM ]) case $host_os in rhapsody* | darwin1.[[012]]) _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; 10.[[012]]*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test "$lt_cv_apple_cc_single_mod" = "yes"; then _lt_dar_single_mod='$single_module' fi if test "$lt_cv_ld_exported_symbols_list" = "yes"; then _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' fi if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac ]) # _LT_DARWIN_LINKER_FEATURES([TAG]) # --------------------------------- # Checks for linker and compiler features on darwin m4_defun([_LT_DARWIN_LINKER_FEATURES], [ m4_require([_LT_REQUIRED_DARWIN_CHECKS]) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported if test "$lt_cv_ld_force_load" = "yes"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) else _LT_TAGVAR(whole_archive_flag_spec, $1)='' fi _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" case $cc_basename in ifort*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test "$_lt_dar_can_shared" = "yes"; then output_verbose_link_cmd=func_echo_all _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" m4_if([$1], [CXX], [ if test "$lt_cv_apple_cc_single_mod" != "yes"; then _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" fi ],[]) else _LT_TAGVAR(ld_shlibs, $1)=no fi ]) # _LT_SYS_MODULE_PATH_AIX([TAGNAME]) # ---------------------------------- # Links a minimal program and checks the executable # for the system default hardcoded library path. In most cases, # this is /usr/lib:/lib, but when the MPI compilers are used # the location of the communication and MPI libs are included too. # If we don't find anything, use the default library path according # to the aix ld manual. # Store the results from the different compilers for each TAGNAME. # Allow to override them for all tags through lt_cv_aix_libpath. m4_defun([_LT_SYS_MODULE_PATH_AIX], [m4_require([_LT_DECL_SED])dnl if test "${lt_cv_aix_libpath+set}" = set; then aix_libpath=$lt_cv_aix_libpath else AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ lt_aix_libpath_sed='[ /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }]' _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi],[]) if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])="/usr/lib:/lib" fi ]) aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) fi ])# _LT_SYS_MODULE_PATH_AIX # _LT_SHELL_INIT(ARG) # ------------------- m4_define([_LT_SHELL_INIT], [m4_divert_text([M4SH-INIT], [$1 ])])# _LT_SHELL_INIT # _LT_PROG_ECHO_BACKSLASH # ----------------------- # Find how we can fake an echo command that does not interpret backslash. # In particular, with Autoconf 2.60 or later we add some code to the start # of the generated configure script which will find a shell with a builtin # printf (which we can use as an echo command). m4_defun([_LT_PROG_ECHO_BACKSLASH], [ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO AC_MSG_CHECKING([how to print strings]) # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $[]1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } case "$ECHO" in printf*) AC_MSG_RESULT([printf]) ;; print*) AC_MSG_RESULT([print -r]) ;; *) AC_MSG_RESULT([cat]) ;; esac m4_ifdef([_AS_DETECT_SUGGESTED], [_AS_DETECT_SUGGESTED([ test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test "X`printf %s $ECHO`" = "X$ECHO" \ || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) _LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) _LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) ])# _LT_PROG_ECHO_BACKSLASH # _LT_WITH_SYSROOT # ---------------- AC_DEFUN([_LT_WITH_SYSROOT], [AC_MSG_CHECKING([for sysroot]) AC_ARG_WITH([sysroot], [ --with-sysroot[=DIR] Search for dependent libraries within DIR (or the compiler's sysroot if not specified).], [], [with_sysroot=no]) dnl lt_sysroot will always be passed unquoted. We quote it here dnl in case the user passed a directory name. lt_sysroot= case ${with_sysroot} in #( yes) if test "$GCC" = yes; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) AC_MSG_RESULT([${with_sysroot}]) AC_MSG_ERROR([The sysroot must be an absolute path.]) ;; esac AC_MSG_RESULT([${lt_sysroot:-no}]) _LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl [dependent libraries, and in which our libraries should be installed.])]) # _LT_ENABLE_LOCK # --------------- m4_defun([_LT_ENABLE_LOCK], [AC_ARG_ENABLE([libtool-lock], [AS_HELP_STRING([--disable-libtool-lock], [avoid locking (might break parallel builds)])]) test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE="32" ;; *ELF-64*) HPUX_IA64_MODE="64" ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out which ABI we are using. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then if test "$lt_cv_prog_gnu_ld" = yes; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_i386" ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -belf" AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, [AC_LANG_PUSH(C) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) AC_LANG_POP]) if test x"$lt_cv_cc_needs_belf" != x"yes"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS="$SAVE_CFLAGS" fi ;; *-*solaris*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD="${LD-ld}_sol2" fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks="$enable_libtool_lock" ])# _LT_ENABLE_LOCK # _LT_PROG_AR # ----------- m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} : ${AR_FLAGS=cru} _LT_DECL([], [AR], [1], [The archiver]) _LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], [lt_cv_ar_at_file=no AC_COMPILE_IFELSE([AC_LANG_PROGRAM], [echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([lt_ar_try]) if test "$ac_status" -eq 0; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a AC_TRY_EVAL([lt_ar_try]) if test "$ac_status" -ne 0; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a ]) ]) if test "x$lt_cv_ar_at_file" = xno; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi _LT_DECL([], [archiver_list_spec], [1], [How to feed a file listing to the archiver]) ])# _LT_PROG_AR # _LT_CMD_OLD_ARCHIVE # ------------------- m4_defun([_LT_CMD_OLD_ARCHIVE], [_LT_PROG_AR AC_CHECK_TOOL(STRIP, strip, :) test -z "$STRIP" && STRIP=: _LT_DECL([], [STRIP], [1], [A symbol stripping program]) AC_CHECK_TOOL(RANLIB, ranlib, :) test -z "$RANLIB" && RANLIB=: _LT_DECL([], [RANLIB], [1], [Commands used to install an old-style archive]) # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac _LT_DECL([], [old_postinstall_cmds], [2]) _LT_DECL([], [old_postuninstall_cmds], [2]) _LT_TAGDECL([], [old_archive_cmds], [2], [Commands used to build an old-style archive]) _LT_DECL([], [lock_old_archive_extraction], [0], [Whether to use a lock for old archive extraction]) ])# _LT_CMD_OLD_ARCHIVE # _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------------------- # Check whether the given compiler option works AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$3" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi fi $RM conftest* ]) if test x"[$]$2" = xyes; then m4_if([$5], , :, [$5]) else m4_if([$6], , :, [$6]) fi ])# _LT_COMPILER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) # _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------- # Check whether the given linker option works AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $3" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&AS_MESSAGE_LOG_FD $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi else $2=yes fi fi $RM -r conftest* LDFLAGS="$save_LDFLAGS" ]) if test x"[$]$2" = xyes; then m4_if([$4], , :, [$4]) else m4_if([$5], , :, [$5]) fi ])# _LT_LINKER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) # LT_CMD_MAX_LEN #--------------- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl # find the maximum length of command line arguments AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl i=0 teststring="ABCD" case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8 ; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test $i != 17 # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac ]) if test -n $lt_cv_sys_max_cmd_len ; then AC_MSG_RESULT($lt_cv_sys_max_cmd_len) else AC_MSG_RESULT(none) fi max_cmd_len=$lt_cv_sys_max_cmd_len _LT_DECL([], [max_cmd_len], [0], [What is the maximum length of a command?]) ])# LT_CMD_MAX_LEN # Old name: AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) # _LT_HEADER_DLFCN # ---------------- m4_defun([_LT_HEADER_DLFCN], [AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl ])# _LT_HEADER_DLFCN # _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, # ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) # ---------------------------------------------------------------- m4_defun([_LT_TRY_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test "$cross_compiling" = yes; then : [$4] else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF [#line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisbility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; }] _LT_EOF if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) $1 ;; x$lt_dlneed_uscore) $2 ;; x$lt_dlunknown|x*) $3 ;; esac else : # compilation failed $3 fi fi rm -fr conftest* ])# _LT_TRY_DLOPEN_SELF # LT_SYS_DLOPEN_SELF # ------------------ AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test "x$enable_dlopen" != xyes; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen="load_add_on" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen="LoadLibrary" lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen="dlopen" lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ lt_cv_dlopen="dyld" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ]) ;; *) AC_CHECK_FUNC([shl_load], [lt_cv_dlopen="shl_load"], [AC_CHECK_LIB([dld], [shl_load], [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], [AC_CHECK_FUNC([dlopen], [lt_cv_dlopen="dlopen"], [AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], [AC_CHECK_LIB([svld], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], [AC_CHECK_LIB([dld], [dld_link], [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) ]) ]) ]) ]) ]) ;; esac if test "x$lt_cv_dlopen" != xno; then enable_dlopen=yes else enable_dlopen=no fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS="$CPPFLAGS" test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS="$LDFLAGS" wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS="$LIBS" LIBS="$lt_cv_dlopen_libs $LIBS" AC_CACHE_CHECK([whether a program can dlopen itself], lt_cv_dlopen_self, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) ]) if test "x$lt_cv_dlopen_self" = xyes; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" AC_CACHE_CHECK([whether a statically linked program can dlopen itself], lt_cv_dlopen_self_static, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) ]) fi CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi _LT_DECL([dlopen_support], [enable_dlopen], [0], [Whether dlopen is supported]) _LT_DECL([dlopen_self], [enable_dlopen_self], [0], [Whether dlopen of programs is supported]) _LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], [Whether dlopen of statically linked programs is supported]) ])# LT_SYS_DLOPEN_SELF # Old name: AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) # _LT_COMPILER_C_O([TAGNAME]) # --------------------------- # Check to see if options -c and -o are simultaneously supported by compiler. # This macro does not hard code the compiler like AC_PROG_CC_C_O. m4_defun([_LT_COMPILER_C_O], [m4_require([_LT_DECL_SED])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes fi fi chmod u+w . 2>&AS_MESSAGE_LOG_FD $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ]) _LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], [Does compiler simultaneously support -c and -o options?]) ])# _LT_COMPILER_C_O # _LT_COMPILER_FILE_LOCKS([TAGNAME]) # ---------------------------------- # Check to see if we can do hard links to lock some files if needed m4_defun([_LT_COMPILER_FILE_LOCKS], [m4_require([_LT_ENABLE_LOCK])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_COMPILER_C_O([$1]) hard_links="nottested" if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then # do not overwrite the value of need_locks provided by the user AC_MSG_CHECKING([if we can lock with hard links]) hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no AC_MSG_RESULT([$hard_links]) if test "$hard_links" = no; then AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) need_locks=warn fi else need_locks=no fi _LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) ])# _LT_COMPILER_FILE_LOCKS # _LT_CHECK_OBJDIR # ---------------- m4_defun([_LT_CHECK_OBJDIR], [AC_CACHE_CHECK([for objdir], [lt_cv_objdir], [rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null]) objdir=$lt_cv_objdir _LT_DECL([], [objdir], [0], [The name of the directory that contains temporary libtool files])dnl m4_pattern_allow([LT_OBJDIR])dnl AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", [Define to the sub-directory in which libtool stores uninstalled libraries.]) ])# _LT_CHECK_OBJDIR # _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) # -------------------------------------- # Check hardcoding attributes. m4_defun([_LT_LINKER_HARDCODE_LIBPATH], [AC_MSG_CHECKING([how to hardcode library paths into programs]) _LT_TAGVAR(hardcode_action, $1)= if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || test -n "$_LT_TAGVAR(runpath_var, $1)" || test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then # We can hardcode non-existent directories. if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then # Linking always hardcodes the temporary library directory. _LT_TAGVAR(hardcode_action, $1)=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. _LT_TAGVAR(hardcode_action, $1)=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. _LT_TAGVAR(hardcode_action, $1)=unsupported fi AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then # Fast installation is not supported enable_fast_install=no elif test "$shlibpath_overrides_runpath" = yes || test "$enable_shared" = no; then # Fast installation is not necessary enable_fast_install=needless fi _LT_TAGDECL([], [hardcode_action], [0], [How to hardcode a shared library path into an executable]) ])# _LT_LINKER_HARDCODE_LIBPATH # _LT_CMD_STRIPLIB # ---------------- m4_defun([_LT_CMD_STRIPLIB], [m4_require([_LT_DECL_EGREP]) striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP" ; then striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ;; *) AC_MSG_RESULT([no]) ;; esac fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) ])# _LT_CMD_STRIPLIB # _LT_SYS_DYNAMIC_LINKER([TAG]) # ----------------------------- # PORTME Fill in your ld.so characteristics m4_defun([_LT_SYS_DYNAMIC_LINKER], [AC_REQUIRE([AC_CANONICAL_HOST])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_OBJDUMP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl AC_MSG_CHECKING([dynamic linker characteristics]) m4_if([$1], [], [ if test "$GCC" = yes; then case $host_os in darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; *) lt_awk_arg="/^libraries:/" ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq="s,=\([[A-Za-z]]:\),\1,g" ;; *) lt_sed_strip_eq="s,=/,/,g" ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary. lt_tmp_lt_search_path_spec= lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path/$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" else test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS=" "; FS="/|\n";} { lt_foo=""; lt_count=0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo="/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[[lt_foo]]++; } if (lt_freq[[lt_foo]] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's,/\([[A-Za-z]]:\),\1,g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi]) library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=".so" postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='${libname}${release}${shared_ext}$major' ;; aix[[4-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test "$host_cpu" = ia64; then # AIX 5 supports IA64 library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line `#! .'. This would cause the generated library to # depend on `.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[[01]] | aix4.[[01]].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # AIX (on Power*) has no versioning support, so currently we can not hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. if test "$aix_use_runtimelinking" = yes; then # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' else # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='${libname}${release}.a $libname.a' soname_spec='${libname}${release}${shared_ext}$major' fi shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='${libname}${shared_ext}' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[[45]]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=".dll" need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' library_names_spec='${libname}.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec="$LIB" if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' soname_spec='${libname}${release}${major}$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[[23]].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[[01]]* | freebsdelf3.[[01]]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=yes sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' if test "X$HPUX_IA64_MODE" = X32; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" fi sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[[3-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test "$lt_cv_prog_gnu_ld" = yes; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], [lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], [lt_cv_shlibpath_overrides_runpath=yes])]) LDFLAGS=$save_LDFLAGS libdir=$save_libdir ]) shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Add ABI-specific directories to the system library path. sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib" # Append ld.so.conf contents to the search path if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd*) version_type=sunos sys_lib_dlsearch_path_spec="/usr/lib" need_lib_prefix=no # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. case $host_os in openbsd3.3 | openbsd3.3.*) need_version=yes ;; *) need_version=no ;; esac library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then case $host_os in openbsd2.[[89]] | openbsd2.[[89]].*) shlibpath_overrides_runpath=no ;; *) shlibpath_overrides_runpath=yes ;; esac else shlibpath_overrides_runpath=yes fi ;; os2*) libname_spec='$name' shrext_cmds=".dll" need_lib_prefix=no library_names_spec='$libname${shared_ext} $libname.a' dynamic_linker='OS/2 ld.exe' shlibpath_var=LIBPATH ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test "$with_gnu_ld" = yes; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec ;then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' soname_spec='$libname${shared_ext}.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=freebsd-elf need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test "$with_gnu_ld" = yes; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac AC_MSG_RESULT([$dynamic_linker]) test "$dynamic_linker" = no && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test "$GCC" = yes; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" fi if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" fi _LT_DECL([], [variables_saved_for_relink], [1], [Variables whose values should be saved in libtool wrapper scripts and restored at link time]) _LT_DECL([], [need_lib_prefix], [0], [Do we need the "lib" prefix for modules?]) _LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) _LT_DECL([], [version_type], [0], [Library versioning type]) _LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) _LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) _LT_DECL([], [shlibpath_overrides_runpath], [0], [Is shlibpath searched before the hard-coded library search path?]) _LT_DECL([], [libname_spec], [1], [Format of library name prefix]) _LT_DECL([], [library_names_spec], [1], [[List of archive names. First name is the real one, the rest are links. The last name is the one that the linker finds with -lNAME]]) _LT_DECL([], [soname_spec], [1], [[The coded name of the library, if different from the real name]]) _LT_DECL([], [install_override_mode], [1], [Permission mode override for installation of shared libraries]) _LT_DECL([], [postinstall_cmds], [2], [Command to use after installation of a shared archive]) _LT_DECL([], [postuninstall_cmds], [2], [Command to use after uninstallation of a shared archive]) _LT_DECL([], [finish_cmds], [2], [Commands used to finish a libtool library installation in a directory]) _LT_DECL([], [finish_eval], [1], [[As "finish_cmds", except a single script fragment to be evaled but not shown]]) _LT_DECL([], [hardcode_into_libs], [0], [Whether we should hardcode library paths into libraries]) _LT_DECL([], [sys_lib_search_path_spec], [2], [Compile-time system search path for libraries]) _LT_DECL([], [sys_lib_dlsearch_path_spec], [2], [Run-time system search path for libraries]) ])# _LT_SYS_DYNAMIC_LINKER # _LT_PATH_TOOL_PREFIX(TOOL) # -------------------------- # find a file program which can recognize shared library AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in [[\\/*] | ?:[\\/]*]) lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD="$MAGIC_CMD" lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR dnl $ac_dummy forces splitting on constant user-supplied paths. dnl POSIX.2 word splitting is done only on the output of word expansions, dnl not every word. This closes a longstanding sh security hole. ac_dummy="m4_if([$2], , $PATH, [$2])" for ac_dir in $ac_dummy; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$1; then lt_cv_path_MAGIC_CMD="$ac_dir/$1" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS="$lt_save_ifs" MAGIC_CMD="$lt_save_MAGIC_CMD" ;; esac]) MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if test -n "$MAGIC_CMD"; then AC_MSG_RESULT($MAGIC_CMD) else AC_MSG_RESULT(no) fi _LT_DECL([], [MAGIC_CMD], [0], [Used to examine libraries when file_magic_cmd begins with "file"])dnl ])# _LT_PATH_TOOL_PREFIX # Old name: AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) # _LT_PATH_MAGIC # -------------- # find a file program which can recognize a shared library m4_defun([_LT_PATH_MAGIC], [_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) else MAGIC_CMD=: fi fi ])# _LT_PATH_MAGIC # LT_PATH_LD # ---------- # find the pathname to the GNU or non-GNU linker AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PROG_ECHO_BACKSLASH])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld @<:@default=no@:>@])], [test "$withval" = no || with_gnu_ld=yes], [with_gnu_ld=no])dnl ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(lt_cv_path_LD, [if test -z "$LD"; then lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; gnu*) lt_cv_deplibs_check_method=pass_all ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[[3-9]]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu) lt_cv_deplibs_check_method=pass_all ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; esac ]) file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown _LT_DECL([], [deplibs_check_method], [1], [Method to check whether dependent libraries are shared objects]) _LT_DECL([], [file_magic_cmd], [1], [Command to use when deplibs_check_method = "file_magic"]) _LT_DECL([], [file_magic_glob], [1], [How to find potential files when deplibs_check_method = "file_magic"]) _LT_DECL([], [want_nocaseglob], [1], [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) ])# _LT_CHECK_MAGIC_METHOD # LT_PATH_NM # ---------- # find the pathname to a BSD- or MS-compatible name lister AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM="$NM" else lt_nm_to_check="${ac_tool_prefix}nm" if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. tmp_nm="$ac_dir/$lt_tmp_nm" if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then # Check to see if the nm accepts a BSD-compat flag. # Adding the `sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in */dev/null* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS="$lt_save_ifs" done : ${lt_cv_path_NM=no} fi]) if test "$lt_cv_path_NM" != "no"; then NM="$lt_cv_path_NM" else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols" ;; *) DUMPBIN=: ;; esac fi AC_SUBST([DUMPBIN]) if test "$DUMPBIN" != ":"; then NM="$DUMPBIN" fi fi test -z "$NM" && NM=nm AC_SUBST([NM]) _LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], [lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) cat conftest.out >&AS_MESSAGE_LOG_FD if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest*]) ])# LT_PATH_NM # Old names: AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_PROG_NM], []) dnl AC_DEFUN([AC_PROG_NM], []) # _LT_CHECK_SHAREDLIB_FROM_LINKLIB # -------------------------------- # how to determine the name of the shared library # associated with a specific link library. # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) m4_require([_LT_DECL_DLLTOOL]) AC_CACHE_CHECK([how to associate runtime and link libraries], lt_cv_sharedlib_from_linklib_cmd, [lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh # decide which to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd="$ECHO" ;; esac ]) sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO _LT_DECL([], [sharedlib_from_linklib_cmd], [1], [Command to associate shared and link libraries]) ])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB # _LT_PATH_MANIFEST_TOOL # ---------------------- # locate the manifest tool m4_defun([_LT_PATH_MANIFEST_TOOL], [AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], [lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&AS_MESSAGE_LOG_FD if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest*]) if test "x$lt_cv_path_mainfest_tool" != xyes; then MANIFEST_TOOL=: fi _LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl ])# _LT_PATH_MANIFEST_TOOL # LT_LIB_M # -------- # check for math library AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl LIBM= case $host in *-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") ;; *) AC_CHECK_LIB(m, cos, LIBM="-lm") ;; esac AC_SUBST([LIBM]) ])# LT_LIB_M # Old name: AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_CHECK_LIBM], []) # _LT_COMPILER_NO_RTTI([TAGNAME]) # ------------------------------- m4_defun([_LT_COMPILER_NO_RTTI], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= if test "$GCC" = yes; then case $cc_basename in nvcc*) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; *) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; esac _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], lt_cv_prog_compiler_rtti_exceptions, [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) fi _LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], [Compiler flag to turn off builtin functions]) ])# _LT_COMPILER_NO_RTTI # _LT_CMD_GLOBAL_SYMBOLS # ---------------------- m4_defun([_LT_CMD_GLOBAL_SYMBOLS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([LT_PATH_NM])dnl AC_REQUIRE([LT_PATH_LD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_TAG_COMPILER])dnl # Check for command to grab the raw symbol name followed by C symbol from nm. AC_MSG_CHECKING([command to parse $NM output from $compiler object]) AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], [ # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[[BCDEGRST]]' # Regexp to match symbols that can be accessed directly from C. sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[[BCDT]]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[[ABCDGISTW]]' ;; hpux*) if test "$host_cpu" = ia64; then symcode='[[ABCDEGRST]]' fi ;; irix* | nonstopux*) symcode='[[BCDEGRST]]' ;; osf*) symcode='[[BCDEGQRST]]' ;; solaris*) symcode='[[BDRT]]' ;; sco3.2v5*) symcode='[[DT]]' ;; sysv4.2uw2*) symcode='[[DT]]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[[ABDT]]' ;; sysv4) symcode='[[DFNSTU]]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[[ABCDGIRSTW]]' ;; esac # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function # and D for any global variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ " {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ " s[1]~/^[@?]/{print s[1], s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) /* DATA imports from DLLs on WIN32 con't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT@&t@_DLSYM_CONST #elif defined(__osf__) /* This system does not cope well with relocations in const data. */ # define LT@&t@_DLSYM_CONST #else # define LT@&t@_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT@&t@_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[[]] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS="conftstm.$ac_objext" CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD fi else echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test "$pipe_works" = yes; then break else lt_cv_sys_global_symbol_pipe= fi done ]) if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then AC_MSG_RESULT(failed) else AC_MSG_RESULT(ok) fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then nm_file_list_spec='@' fi _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], [Take the output of nm and produce a listing of raw symbols and C names]) _LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], [Transform the output of nm in a proper C declaration]) _LT_DECL([global_symbol_to_c_name_address], [lt_cv_sys_global_symbol_to_c_name_address], [1], [Transform the output of nm in a C name address pair]) _LT_DECL([global_symbol_to_c_name_address_lib_prefix], [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], [Transform the output of nm in a C name address pair when lib prefix is needed]) _LT_DECL([], [nm_file_list_spec], [1], [Specify filename containing input files for $NM]) ]) # _LT_CMD_GLOBAL_SYMBOLS # _LT_COMPILER_PIC([TAGNAME]) # --------------------------- m4_defun([_LT_COMPILER_PIC], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_wl, $1)= _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)= m4_if([$1], [CXX], [ # C++ specific cases for pic, static, wl, etc. if test "$GXX" = yes; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else case $host_os in aix[[4-9]]*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; dgux*) case $cc_basename in ec++*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; ghcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' if test "$host_cpu" != ia64; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' fi ;; aCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in KCC*) # KAI C++ Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64 which still supported -KPIC. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL 8.0, 9.0 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' ;; *) ;; esac ;; netbsd*) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; cxx*) # Digital/Compaq C++ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; lcc*) # Lucid _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ], [ if test "$GCC" = yes; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; hpux9* | hpux10* | hpux11*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC (with -KPIC) is the default. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in # old Intel for x86_64 which still supported -KPIC. ecc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # Lahey Fortran 8.1. lf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' ;; nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; ccc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All Alpha code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='' ;; *Sun\ F* | *Sun*Fortran*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ;; *Intel*\ [[CF]]*Compiler*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; *Portland\ Group*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; esac ;; newsos6) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All OSF/1 code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; rdos*) _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; solaris*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; *) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; esac ;; sunos4*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec ;then _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; unicos*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; uts4*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ]) case $host_os in # For platforms which do not support PIC, -DPIC is meaningless: *djgpp*) _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ;; esac AC_CACHE_CHECK([for $compiler option to produce PIC], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) _LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) # # Check to make sure the PIC flag actually works. # if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in "" | " "*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; esac], [_LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) fi _LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], [Additional compiler flags for building library objects]) _LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], [How to pass a linker flag through the compiler]) # # Check to make sure the static flag actually works. # wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), $lt_tmp_static_flag, [], [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) _LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], [Compiler flag to prevent dynamic linking]) ])# _LT_COMPILER_PIC # _LT_LINKER_SHLIBS([TAGNAME]) # ---------------------------- # See if the linker supports building shared libraries. m4_defun([_LT_LINKER_SHLIBS], [AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) m4_if([$1], [CXX], [ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm # Also, AIX nm treats weak defined symbols like other global defined # symbols, whereas GNU nm marks them as "W". if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi ;; pw32*) _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl*) _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] ;; esac ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac ], [ runpath_var= _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_cmds, $1)= _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(old_archive_from_new_cmds, $1)= _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= _LT_TAGVAR(thread_safe_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list _LT_TAGVAR(include_expsyms, $1)= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ` (' and `)$', so one must not match beginning or # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', # as well as any symbol that contains `d'. _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. dnl Note also adjust exclude_expsyms for C++ above. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; esac _LT_TAGVAR(ld_shlibs, $1)=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test "$with_gnu_ld" = yes; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test "$lt_use_gnu_ld_interface" = yes; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='${wl}' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no case `$LD -v 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[[3-9]]*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test "$host_os" = linux-dietlibc; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test "$tmp_diet" = no then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 _LT_TAGVAR(whole_archive_flag_spec, $1)= tmp_sharedflag='--shared' ;; xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi case $cc_basename in xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; sunos4*) _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then runpath_var= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. _LT_TAGVAR(hardcode_minus_L, $1)=yes if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. _LT_TAGVAR(hardcode_direct, $1)=unsupported fi ;; aix[[4-9]]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm # Also, AIX nm treats weak defined symbols like other global # defined symbols, whereas GNU nm marks them as "W". if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' if test "$GCC" = yes; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi ;; esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. _LT_TAGVAR(always_export_symbols, $1)=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' if test "$with_gnu_ld" = yes; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes # This is similar to how AIX traditionally builds its shared libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; bsdi[[45]]*) _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; else sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile="$lt_outputfile.exe" lt_tool_outputfile="$lt_tool_outputfile.exe" ;; esac~ if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' # FIXME: Should let the user specify the lib program. _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; hpux9*) if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' ;; hpux10*) if test "$GCC" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test "$with_gnu_ld" = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes fi ;; hpux11*) if test "$GCC" = yes && test "$with_gnu_ld" = no; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) m4_if($1, [], [ # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) _LT_LINKER_OPTION([if $CC understands -b], _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) ;; esac fi if test "$with_gnu_ld" = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], [lt_cv_irix_exported_symbol], [save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" AC_LINK_IFELSE( [AC_LANG_SOURCE( [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], [C++], [[int foo (void) { return 0; }]], [Fortran 77], [[ subroutine foo end]], [Fortran], [[ subroutine foo end]])])], [lt_cv_irix_exported_symbol=yes], [lt_cv_irix_exported_symbol=no]) LDFLAGS="$save_LDFLAGS"]) if test "$lt_cv_irix_exported_symbol" = yes; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' fi else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes _LT_TAGVAR(link_all_deplibs, $1)=yes ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; newsos6) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *nto* | *qnx*) ;; openbsd*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' else case $host_os in openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' ;; esac fi else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' ;; osf3*) if test "$GCC" = yes; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test "$GCC" = yes; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; solaris*) _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' if test "$GCC" = yes; then wlarc='${wl}' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='${wl}' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. GCC discards it without `$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test "$GCC" = yes; then _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' else _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' fi ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes ;; sunos4*) if test "x$host_vendor" = xsequent; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4) case $host_vendor in sni) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' _LT_TAGVAR(hardcode_direct, $1)=no ;; motorola) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4.3*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes _LT_TAGVAR(ld_shlibs, $1)=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(ld_shlibs, $1)=no ;; esac if test x$host_vendor = xsni; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' ;; esac fi fi ]) AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no _LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld _LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl _LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl _LT_DECL([], [extract_expsyms_cmds], [2], [The commands to extract the exported symbol list from a shared archive]) # # Do we need to explicitly link libc? # case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in x|xyes) # Assume -lc should be added _LT_TAGVAR(archive_cmds_need_lc, $1)=yes if test "$enable_shared" = yes && test "$GCC" = yes; then case $_LT_TAGVAR(archive_cmds, $1) in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. AC_CACHE_CHECK([whether -lc should be explicitly linked in], [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), [$RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if AC_TRY_EVAL(ac_compile) 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) _LT_TAGVAR(allow_undefined_flag, $1)= if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) then lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no else lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes fi _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* ]) _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) ;; esac fi ;; esac _LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], [Whether or not to add -lc for building shared libraries]) _LT_TAGDECL([allow_libtool_libs_with_static_runtimes], [enable_shared_with_static_runtimes], [0], [Whether or not to disallow shared libs when runtime libs are static]) _LT_TAGDECL([], [export_dynamic_flag_spec], [1], [Compiler flag to allow reflexive dlopens]) _LT_TAGDECL([], [whole_archive_flag_spec], [1], [Compiler flag to generate shared objects directly from archives]) _LT_TAGDECL([], [compiler_needs_object], [1], [Whether the compiler copes with passing no objects directly]) _LT_TAGDECL([], [old_archive_from_new_cmds], [2], [Create an old-style archive from a shared archive]) _LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], [Create a temporary old-style archive to link instead of a shared archive]) _LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) _LT_TAGDECL([], [archive_expsym_cmds], [2]) _LT_TAGDECL([], [module_cmds], [2], [Commands used to build a loadable module if different from building a shared archive.]) _LT_TAGDECL([], [module_expsym_cmds], [2]) _LT_TAGDECL([], [with_gnu_ld], [1], [Whether we are building with GNU ld or not]) _LT_TAGDECL([], [allow_undefined_flag], [1], [Flag that allows shared libraries with undefined symbols to be built]) _LT_TAGDECL([], [no_undefined_flag], [1], [Flag that enforces no undefined symbols]) _LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], [Flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]) _LT_TAGDECL([], [hardcode_libdir_separator], [1], [Whether we need a single "-rpath" flag with a separated argument]) _LT_TAGDECL([], [hardcode_direct], [0], [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_direct_absolute], [0], [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the resulting binary and the resulting library dependency is "absolute", i.e impossible to change by setting ${shlibpath_var} if the library is relocated]) _LT_TAGDECL([], [hardcode_minus_L], [0], [Set to "yes" if using the -LDIR flag during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_shlibpath_var], [0], [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_automatic], [0], [Set to "yes" if building a shared library automatically hardcodes DIR into the library and all subsequent libraries and executables linked against it]) _LT_TAGDECL([], [inherit_rpath], [0], [Set to yes if linker adds runtime paths of dependent libraries to runtime path list]) _LT_TAGDECL([], [link_all_deplibs], [0], [Whether libtool must link a program against all its dependency libraries]) _LT_TAGDECL([], [always_export_symbols], [0], [Set to "yes" if exported symbols are required]) _LT_TAGDECL([], [export_symbols_cmds], [2], [The commands to list exported symbols]) _LT_TAGDECL([], [exclude_expsyms], [1], [Symbols that should not be listed in the preloaded symbols]) _LT_TAGDECL([], [include_expsyms], [1], [Symbols that must always be exported]) _LT_TAGDECL([], [prelink_cmds], [2], [Commands necessary for linking programs (against libraries) with templates]) _LT_TAGDECL([], [postlink_cmds], [2], [Commands necessary for finishing linking programs]) _LT_TAGDECL([], [file_list_spec], [1], [Specify filename containing input files]) dnl FIXME: Not yet implemented dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], dnl [Compiler flag to generate thread safe objects]) ])# _LT_LINKER_SHLIBS # _LT_LANG_C_CONFIG([TAG]) # ------------------------ # Ensure that the configuration variables for a C compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to `libtool'. m4_defun([_LT_LANG_C_CONFIG], [m4_require([_LT_DECL_EGREP])dnl lt_save_CC="$CC" AC_LANG_PUSH(C) # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' _LT_TAG_COMPILER # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) LT_SYS_DLOPEN_SELF _LT_CMD_STRIPLIB # Report which library types will actually be built AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_CONFIG($1) fi AC_LANG_POP CC="$lt_save_CC" ])# _LT_LANG_C_CONFIG # _LT_LANG_CXX_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a C++ compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to `libtool'. m4_defun([_LT_LANG_CXX_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl if test -n "$CXX" && ( test "X$CXX" != "Xno" && ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || (test "X$CXX" != "Xg++"))) ; then AC_PROG_CXXCPP else _lt_caught_CXX_error=yes fi AC_LANG_PUSH(C++) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_caught_CXX_error" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test "$GXX" = yes; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' else _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= fi if test "$GXX" = yes; then # Set up default GNU C++ configuration LT_PATH_LD # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test "$with_gnu_ld" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='${wl}' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) _LT_TAGVAR(ld_shlibs, $1)=yes case $host_os in aix3*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aix[[4-9]]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' if test "$GXX" = yes; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. _LT_TAGVAR(always_export_symbols, $1)=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an empty # executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' if test "$with_gnu_ld" = yes; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes # This is similar to how AIX traditionally builds its shared # libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl*) # Native MSVC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; else $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile="$lt_outputfile.exe" lt_tool_outputfile="$lt_tool_outputfile.exe" ;; esac~ func_to_tool_file "$lt_outputfile"~ if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF _LT_TAGVAR(ld_shlibs, $1)=no ;; freebsd-elf*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions _LT_TAGVAR(ld_shlibs, $1)=yes ;; gnu*) ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; hpux9*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test "$GXX" = yes; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; hpux10*|hpux11*) if test $with_gnu_ld = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) ;; *) _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test "$GXX" = yes; then if test $with_gnu_ld = no; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test "$GXX" = yes; then if test "$with_gnu_ld" = no; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' fi fi _LT_TAGVAR(link_all_deplibs, $1)=yes ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' ;; cxx*) # Compaq C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; m88k*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) _LT_TAGVAR(ld_shlibs, $1)=yes ;; openbsd2*) # C++ shared libraries are fairly broken _LT_TAGVAR(ld_shlibs, $1)=no ;; openbsd*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; cxx*) case $host in osf3*) _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' ;; *) _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ $RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' ;; esac _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test "$GXX" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' case $host in osf3*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test "$GXX" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # g++ 2.7 appears to require `-G' NOT `-shared' on this # platform. _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ '"$_LT_TAGVAR(old_archive_cmds, $1)" _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ '"$_LT_TAGVAR(reload_cmds, $1)" ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no _LT_TAGVAR(GCC, $1)="$GXX" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test "$_lt_caught_CXX_error" != yes AC_LANG_POP ])# _LT_LANG_CXX_CONFIG # _LT_FUNC_STRIPNAME_CNF # ---------------------- # func_stripname_cnf prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # # This function is identical to the (non-XSI) version of func_stripname, # except this one can be used by m4 code that may be executed by configure, # rather than the libtool script. m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl AC_REQUIRE([_LT_DECL_SED]) AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) func_stripname_cnf () { case ${2} in .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; esac } # func_stripname_cnf ])# _LT_FUNC_STRIPNAME_CNF # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) # --------------------------------- # Figure out "hidden" library dependencies from verbose # compiler output when linking a shared library. # Parse the compiler output and extract the necessary # objects, libraries and library flags. m4_defun([_LT_SYS_HIDDEN_LIBDEPS], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl # Dependencies to place before and after the object being linked: _LT_TAGVAR(predep_objects, $1)= _LT_TAGVAR(postdep_objects, $1)= _LT_TAGVAR(predeps, $1)= _LT_TAGVAR(postdeps, $1)= _LT_TAGVAR(compiler_lib_search_path, $1)= dnl we can't use the lt_simple_compile_test_code here, dnl because it contains code intended for an executable, dnl not a library. It's possible we should let each dnl tag define a new lt_????_link_test_code variable, dnl but it's only used here... m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF int a; void foo (void) { a = 0; } _LT_EOF ], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF ], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer*4 a a=0 return end _LT_EOF ], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer a a=0 return end _LT_EOF ], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF public class foo { private int a; public void bar (void) { a = 0; } }; _LT_EOF ], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF package foo func foo() { } _LT_EOF ]) _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac dnl Parse the compiler output and extract the necessary dnl objects, libraries and library flags. if AC_TRY_EVAL(ac_compile); then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case ${prev}${p} in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test $p = "-L" || test $p = "-R"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test "$pre_test_object_deps_done" = no; then case ${prev} in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" else _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$_LT_TAGVAR(postdeps, $1)"; then _LT_TAGVAR(postdeps, $1)="${prev}${p}" else _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test "$pre_test_object_deps_done" = no; then if test -z "$_LT_TAGVAR(predep_objects, $1)"; then _LT_TAGVAR(predep_objects, $1)="$p" else _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" fi else if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then _LT_TAGVAR(postdep_objects, $1)="$p" else _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling $1 test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken m4_if([$1], [CXX], [case $host_os in interix[[3-9]]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. _LT_TAGVAR(predep_objects,$1)= _LT_TAGVAR(postdep_objects,$1)= _LT_TAGVAR(postdeps,$1)= ;; linux*) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac if test "$solaris_use_stlport4" != yes; then _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' fi ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac # Adding this requires a known-good setup of shared libraries for # Sun compiler versions before 5.6, else PIC objects from an old # archive will be linked into the output, leading to subtle bugs. if test "$solaris_use_stlport4" != yes; then _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' fi ;; esac ;; esac ]) case " $_LT_TAGVAR(postdeps, $1) " in *" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; esac _LT_TAGVAR(compiler_lib_search_dirs, $1)= if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` fi _LT_TAGDECL([], [compiler_lib_search_dirs], [1], [The directories searched by this compiler when creating a shared library]) _LT_TAGDECL([], [predep_objects], [1], [Dependencies to place before and after the objects being linked to create a shared library]) _LT_TAGDECL([], [postdep_objects], [1]) _LT_TAGDECL([], [predeps], [1]) _LT_TAGDECL([], [postdeps], [1]) _LT_TAGDECL([], [compiler_lib_search_path], [1], [The library search path used internally by the compiler when linking a shared library]) ])# _LT_SYS_HIDDEN_LIBDEPS # _LT_LANG_F77_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a Fortran 77 compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_F77_CONFIG], [AC_LANG_PUSH(Fortran 77) if test -z "$F77" || test "X$F77" = "Xno"; then _lt_disable_F77=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for f77 test sources. ac_ext=f # Object file extension for compiled f77 test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the F77 compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_disable_F77" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${F77-"f77"} CFLAGS=$FFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) GCC=$G77 if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)="$G77" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC="$lt_save_CC" CFLAGS="$lt_save_CFLAGS" fi # test "$_lt_disable_F77" != yes AC_LANG_POP ])# _LT_LANG_F77_CONFIG # _LT_LANG_FC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for a Fortran compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_FC_CONFIG], [AC_LANG_PUSH(Fortran) if test -z "$FC" || test "X$FC" = "Xno"; then _lt_disable_FC=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for fc test sources. ac_ext=${ac_fc_srcext-f} # Object file extension for compiled fc test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the FC compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_disable_FC" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${FC-"f95"} CFLAGS=$FCFLAGS compiler=$CC GCC=$ac_cv_fc_compiler_gnu _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test "$_lt_disable_FC" != yes AC_LANG_POP ])# _LT_LANG_FC_CONFIG # _LT_LANG_GCJ_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Java Compiler compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_GCJ_CONFIG], [AC_REQUIRE([LT_PROG_GCJ])dnl AC_LANG_SAVE # Source file extension for Java test sources. ac_ext=java # Object file extension for compiled Java test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="class foo {}" # Code to be used in simple link tests lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GCJ-"gcj"} CFLAGS=$GCJFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)="$LD" _LT_CC_BASENAME([$compiler]) # GCJ did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GCJ_CONFIG # _LT_LANG_GO_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Go compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_GO_CONFIG], [AC_REQUIRE([LT_PROG_GO])dnl AC_LANG_SAVE # Source file extension for Go test sources. ac_ext=go # Object file extension for compiled Go test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="package main; func main() { }" # Code to be used in simple link tests lt_simple_link_test_code='package main; func main() { }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GOC-"gccgo"} CFLAGS=$GOFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)="$LD" _LT_CC_BASENAME([$compiler]) # Go did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GO_CONFIG # _LT_LANG_RC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for the Windows resource compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_RC_CONFIG], [AC_REQUIRE([LT_PROG_RC])dnl AC_LANG_SAVE # Source file extension for RC test sources. ac_ext=rc # Object file extension for compiled RC test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests lt_simple_link_test_code="$lt_simple_compile_test_code" # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC= CC=${RC-"windres"} CFLAGS= compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes if test -n "$compiler"; then : _LT_CONFIG($1) fi GCC=$lt_save_GCC AC_LANG_RESTORE CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_RC_CONFIG # LT_PROG_GCJ # ----------- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) # Old name: AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_GCJ], []) # LT_PROG_GO # ---------- AC_DEFUN([LT_PROG_GO], [AC_CHECK_TOOL(GOC, gccgo,) ]) # LT_PROG_RC # ---------- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) ]) # Old name: AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_RC], []) # _LT_DECL_EGREP # -------------- # If we don't have a new enough Autoconf to choose the best grep # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_EGREP], [AC_REQUIRE([AC_PROG_EGREP])dnl AC_REQUIRE([AC_PROG_FGREP])dnl test -z "$GREP" && GREP=grep _LT_DECL([], [GREP], [1], [A grep program that handles long lines]) _LT_DECL([], [EGREP], [1], [An ERE matcher]) _LT_DECL([], [FGREP], [1], [A literal string matcher]) dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too AC_SUBST([GREP]) ]) # _LT_DECL_OBJDUMP # -------------- # If we don't have a new enough Autoconf to choose the best objdump # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_OBJDUMP], [AC_CHECK_TOOL(OBJDUMP, objdump, false) test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) AC_SUBST([OBJDUMP]) ]) # _LT_DECL_DLLTOOL # ---------------- # Ensure DLLTOOL variable is set. m4_defun([_LT_DECL_DLLTOOL], [AC_CHECK_TOOL(DLLTOOL, dlltool, false) test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program]) AC_SUBST([DLLTOOL]) ]) # _LT_DECL_SED # ------------ # Check for a fully-functional sed program, that truncates # as few characters as possible. Prefer GNU sed if found. m4_defun([_LT_DECL_SED], [AC_PROG_SED test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" _LT_DECL([], [SED], [1], [A sed program that does not truncate output]) _LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], [Sed that helps us avoid accidentally triggering echo(1) options like -n]) ])# _LT_DECL_SED m4_ifndef([AC_PROG_SED], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_SED. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_SED], [AC_MSG_CHECKING([for a sed that does not truncate output]) AC_CACHE_VAL(lt_cv_path_SED, [# Loop through the user's path and test for sed and gsed. # Then use that list of sed's as ones to test for truncation. as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for lt_ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" fi done done done IFS=$as_save_IFS lt_ac_max=0 lt_ac_count=0 # Add /usr/xpg4/bin/sed as it is typically found on Solaris # along with /bin/sed that truncates output. for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do test ! -f $lt_ac_sed && continue cat /dev/null > conftest.in lt_ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >conftest.in # Check for GNU sed and select it if it is found. if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then lt_cv_path_SED=$lt_ac_sed break fi while true; do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo >>conftest.nl $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break cmp -s conftest.out conftest.nl || break # 10000 chars as input seems more than enough test $lt_ac_count -gt 10 && break lt_ac_count=`expr $lt_ac_count + 1` if test $lt_ac_count -gt $lt_ac_max; then lt_ac_max=$lt_ac_count lt_cv_path_SED=$lt_ac_sed fi done done ]) SED=$lt_cv_path_SED AC_SUBST([SED]) AC_MSG_RESULT([$SED]) ])#AC_PROG_SED ])#m4_ifndef # Old name: AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_SED], []) # _LT_CHECK_SHELL_FEATURES # ------------------------ # Find out whether the shell is Bourne or XSI compatible, # or has some other useful features. m4_defun([_LT_CHECK_SHELL_FEATURES], [AC_MSG_CHECKING([whether the shell understands some XSI constructs]) # Try some XSI features xsi_shell=no ( _lt_dummy="a/b/c" test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ = c,a/b,b/c, \ && eval 'test $(( 1 + 1 )) -eq 2 \ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ && xsi_shell=yes AC_MSG_RESULT([$xsi_shell]) _LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) AC_MSG_CHECKING([whether the shell understands "+="]) lt_shell_append=no ( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ >/dev/null 2>&1 \ && lt_shell_append=yes AC_MSG_RESULT([$lt_shell_append]) _LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi _LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac _LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl ])# _LT_CHECK_SHELL_FEATURES # _LT_PROG_FUNCTION_REPLACE (FUNCNAME, REPLACEMENT-BODY) # ------------------------------------------------------ # In `$cfgfile', look for function FUNCNAME delimited by `^FUNCNAME ()$' and # '^} FUNCNAME ', and replace its body with REPLACEMENT-BODY. m4_defun([_LT_PROG_FUNCTION_REPLACE], [dnl { sed -e '/^$1 ()$/,/^} # $1 /c\ $1 ()\ {\ m4_bpatsubsts([$2], [$], [\\], [^\([ ]\)], [\\\1]) } # Extended-shell $1 implementation' "$cfgfile" > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: ]) # _LT_PROG_REPLACE_SHELLFNS # ------------------------- # Replace existing portable implementations of several shell functions with # equivalent extended shell implementations where those features are available.. m4_defun([_LT_PROG_REPLACE_SHELLFNS], [if test x"$xsi_shell" = xyes; then _LT_PROG_FUNCTION_REPLACE([func_dirname], [dnl case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac]) _LT_PROG_FUNCTION_REPLACE([func_basename], [dnl func_basename_result="${1##*/}"]) _LT_PROG_FUNCTION_REPLACE([func_dirname_and_basename], [dnl case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac func_basename_result="${1##*/}"]) _LT_PROG_FUNCTION_REPLACE([func_stripname], [dnl # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary parameter first. func_stripname_result=${3} func_stripname_result=${func_stripname_result#"${1}"} func_stripname_result=${func_stripname_result%"${2}"}]) _LT_PROG_FUNCTION_REPLACE([func_split_long_opt], [dnl func_split_long_opt_name=${1%%=*} func_split_long_opt_arg=${1#*=}]) _LT_PROG_FUNCTION_REPLACE([func_split_short_opt], [dnl func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"}]) _LT_PROG_FUNCTION_REPLACE([func_lo2o], [dnl case ${1} in *.lo) func_lo2o_result=${1%.lo}.${objext} ;; *) func_lo2o_result=${1} ;; esac]) _LT_PROG_FUNCTION_REPLACE([func_xform], [ func_xform_result=${1%.*}.lo]) _LT_PROG_FUNCTION_REPLACE([func_arith], [ func_arith_result=$(( $[*] ))]) _LT_PROG_FUNCTION_REPLACE([func_len], [ func_len_result=${#1}]) fi if test x"$lt_shell_append" = xyes; then _LT_PROG_FUNCTION_REPLACE([func_append], [ eval "${1}+=\\${2}"]) _LT_PROG_FUNCTION_REPLACE([func_append_quoted], [dnl func_quote_for_eval "${2}" dnl m4 expansion turns \\\\ into \\, and then the shell eval turns that into \ eval "${1}+=\\\\ \\$func_quote_for_eval_result"]) # Save a `func_append' function call where possible by direct use of '+=' sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: else # Save a `func_append' function call even when '+=' is not available sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ && mv -f "$cfgfile.tmp" "$cfgfile" \ || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") test 0 -eq $? || _lt_function_replace_fail=: fi if test x"$_lt_function_replace_fail" = x":"; then AC_MSG_WARN([Unable to substitute extended shell functions in $ofile]) fi ]) # _LT_PATH_CONVERSION_FUNCTIONS # ----------------------------- # Determine which file name conversion functions should be used by # func_to_host_file (and, implicitly, by func_to_host_path). These are needed # for certain cross-compile configurations and native mingw. m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_MSG_CHECKING([how to convert $build file names to $host format]) AC_CACHE_VAL(lt_cv_to_host_file_cmd, [case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac ]) to_host_file_cmd=$lt_cv_to_host_file_cmd AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) _LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], [0], [convert $build file names to $host format])dnl AC_MSG_CHECKING([how to convert $build file names to toolchain format]) AC_CACHE_VAL(lt_cv_to_tool_file_cmd, [#assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac ]) to_tool_file_cmd=$lt_cv_to_tool_file_cmd AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) _LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], [0], [convert $build files to toolchain format])dnl ])# _LT_PATH_CONVERSION_FUNCTIONS ================================================ FILE: deps/pjsip/third_party/opus/m4/ltoptions.m4 ================================================ # Helper functions for option handling. -*- Autoconf -*- # # Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation, # Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 7 ltoptions.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) # ------------------------------------------ m4_define([_LT_MANGLE_OPTION], [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) # --------------------------------------- # Set option OPTION-NAME for macro MACRO-NAME, and if there is a # matching handler defined, dispatch to it. Other OPTION-NAMEs are # saved as a flag. m4_define([_LT_SET_OPTION], [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), _LT_MANGLE_DEFUN([$1], [$2]), [m4_warning([Unknown $1 option `$2'])])[]dnl ]) # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) # ------------------------------------------------------------ # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. m4_define([_LT_IF_OPTION], [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) # ------------------------------------------------------- # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME # are set. m4_define([_LT_UNLESS_OPTIONS], [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), [m4_define([$0_found])])])[]dnl m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 ])[]dnl ]) # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) # ---------------------------------------- # OPTION-LIST is a space-separated list of Libtool options associated # with MACRO-NAME. If any OPTION has a matching handler declared with # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about # the unknown option and exit. m4_defun([_LT_SET_OPTIONS], [# Set options m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [_LT_SET_OPTION([$1], _LT_Option)]) m4_if([$1],[LT_INIT],[ dnl dnl Simply set some default values (i.e off) if boolean options were not dnl specified: _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no ]) _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no ]) dnl dnl If no reference was made to various pairs of opposing options, then dnl we run the default mode handler for the pair. For example, if neither dnl `shared' nor `disable-shared' was passed, we enable building of shared dnl archives by default: _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], [_LT_ENABLE_FAST_INSTALL]) ]) ])# _LT_SET_OPTIONS ## --------------------------------- ## ## Macros to handle LT_INIT options. ## ## --------------------------------- ## # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) # ----------------------------------------- m4_define([_LT_MANGLE_DEFUN], [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) # ----------------------------------------------- m4_define([LT_OPTION_DEFINE], [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl ])# LT_OPTION_DEFINE # dlopen # ------ LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes ]) AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `dlopen' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) # win32-dll # --------- # Declare package support for building win32 dll's. LT_OPTION_DEFINE([LT_INIT], [win32-dll], [enable_win32_dll=yes case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) AC_CHECK_TOOL(AS, as, false) AC_CHECK_TOOL(DLLTOOL, dlltool, false) AC_CHECK_TOOL(OBJDUMP, objdump, false) ;; esac test -z "$AS" && AS=as _LT_DECL([], [AS], [1], [Assembler program])dnl test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl ])# win32-dll AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl _LT_SET_OPTION([LT_INIT], [win32-dll]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `win32-dll' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) # _LT_ENABLE_SHARED([DEFAULT]) # ---------------------------- # implement the --enable-shared flag, and supports the `shared' and # `disable-shared' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_SHARED], [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([shared], [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) _LT_DECL([build_libtool_libs], [enable_shared], [0], [Whether or not to build shared libraries]) ])# _LT_ENABLE_SHARED LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) # Old names: AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) ]) AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared]) ]) AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_SHARED], []) dnl AC_DEFUN([AM_DISABLE_SHARED], []) # _LT_ENABLE_STATIC([DEFAULT]) # ---------------------------- # implement the --enable-static flag, and support the `static' and # `disable-static' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_STATIC], [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([static], [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_static=]_LT_ENABLE_STATIC_DEFAULT) _LT_DECL([build_old_libs], [enable_static], [0], [Whether or not to build static libraries]) ])# _LT_ENABLE_STATIC LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) # Old names: AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) ]) AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static]) ]) AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_STATIC], []) dnl AC_DEFUN([AM_DISABLE_STATIC], []) # _LT_ENABLE_FAST_INSTALL([DEFAULT]) # ---------------------------------- # implement the --enable-fast-install flag, and support the `fast-install' # and `disable-fast-install' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_FAST_INSTALL], [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([fast-install], [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) _LT_DECL([fast_install], [enable_fast_install], [0], [Whether or not to optimize for fast installation])dnl ])# _LT_ENABLE_FAST_INSTALL LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) # Old names: AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `fast-install' option into LT_INIT's first parameter.]) ]) AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `disable-fast-install' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) # _LT_WITH_PIC([MODE]) # -------------------- # implement the --with-pic flag, and support the `pic-only' and `no-pic' # LT_INIT options. # MODE is either `yes' or `no'. If omitted, it defaults to `both'. m4_define([_LT_WITH_PIC], [AC_ARG_WITH([pic], [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], [lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for lt_pkg in $withval; do IFS="$lt_save_ifs" if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS="$lt_save_ifs" ;; esac], [pic_mode=default]) test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl ])# _LT_WITH_PIC LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) # Old name: AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `pic-only' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) ## ----------------- ## ## LTDL_INIT Options ## ## ----------------- ## m4_define([_LTDL_MODE], []) LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], [m4_define([_LTDL_MODE], [nonrecursive])]) LT_OPTION_DEFINE([LTDL_INIT], [recursive], [m4_define([_LTDL_MODE], [recursive])]) LT_OPTION_DEFINE([LTDL_INIT], [subproject], [m4_define([_LTDL_MODE], [subproject])]) m4_define([_LTDL_TYPE], []) LT_OPTION_DEFINE([LTDL_INIT], [installable], [m4_define([_LTDL_TYPE], [installable])]) LT_OPTION_DEFINE([LTDL_INIT], [convenience], [m4_define([_LTDL_TYPE], [convenience])]) ================================================ FILE: deps/pjsip/third_party/opus/m4/ltsugar.m4 ================================================ # ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # # Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 6 ltsugar.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) # lt_join(SEP, ARG1, [ARG2...]) # ----------------------------- # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their # associated separator. # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier # versions in m4sugar had bugs. m4_define([lt_join], [m4_if([$#], [1], [], [$#], [2], [[$2]], [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) m4_define([_lt_join], [m4_if([$#$2], [2], [], [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) # lt_car(LIST) # lt_cdr(LIST) # ------------ # Manipulate m4 lists. # These macros are necessary as long as will still need to support # Autoconf-2.59 which quotes differently. m4_define([lt_car], [[$1]]) m4_define([lt_cdr], [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], [$#], 1, [], [m4_dquote(m4_shift($@))])]) m4_define([lt_unquote], $1) # lt_append(MACRO-NAME, STRING, [SEPARATOR]) # ------------------------------------------ # Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. # Note that neither SEPARATOR nor STRING are expanded; they are appended # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). # No SEPARATOR is output if MACRO-NAME was previously undefined (different # than defined and empty). # # This macro is needed until we can rely on Autoconf 2.62, since earlier # versions of m4sugar mistakenly expanded SEPARATOR but not STRING. m4_define([lt_append], [m4_define([$1], m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) # ---------------------------------------------------------- # Produce a SEP delimited list of all paired combinations of elements of # PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list # has the form PREFIXmINFIXSUFFIXn. # Needed until we can rely on m4_combine added in Autoconf 2.62. m4_define([lt_combine], [m4_if(m4_eval([$# > 3]), [1], [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl [[m4_foreach([_Lt_prefix], [$2], [m4_foreach([_Lt_suffix], ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) # ----------------------------------------------------------------------- # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. m4_define([lt_if_append_uniq], [m4_ifdef([$1], [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], [lt_append([$1], [$2], [$3])$4], [$5])], [lt_append([$1], [$2], [$3])$4])]) # lt_dict_add(DICT, KEY, VALUE) # ----------------------------- m4_define([lt_dict_add], [m4_define([$1($2)], [$3])]) # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) # -------------------------------------------- m4_define([lt_dict_add_subkey], [m4_define([$1($2:$3)], [$4])]) # lt_dict_fetch(DICT, KEY, [SUBKEY]) # ---------------------------------- m4_define([lt_dict_fetch], [m4_ifval([$3], m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) # ----------------------------------------------------------------- m4_define([lt_if_dict_fetch], [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], [$5], [$6])]) # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) # -------------------------------------------------------------- m4_define([lt_dict_filter], [m4_if([$5], [], [], [lt_join(m4_quote(m4_default([$4], [[, ]])), lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl ]) ================================================ FILE: deps/pjsip/third_party/opus/m4/ltversion.m4 ================================================ # ltversion.m4 -- version numbers -*- Autoconf -*- # # Copyright (C) 2004 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # @configure_input@ # serial 3337 ltversion.m4 # This file is part of GNU Libtool m4_define([LT_PACKAGE_VERSION], [2.4.2]) m4_define([LT_PACKAGE_REVISION], [1.3337]) AC_DEFUN([LTVERSION_VERSION], [macro_version='2.4.2' macro_revision='1.3337' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) ================================================ FILE: deps/pjsip/third_party/opus/m4/lt~obsolete.m4 ================================================ # lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # # Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004. # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 5 lt~obsolete.m4 # These exist entirely to fool aclocal when bootstrapping libtool. # # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) # which have later been changed to m4_define as they aren't part of the # exported API, or moved to Autoconf or Automake where they belong. # # The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us # using a macro with the same name in our local m4/libtool.m4 it'll # pull the old libtool.m4 in (it doesn't see our shiny new m4_define # and doesn't know about Autoconf macros at all.) # # So we provide this file, which has a silly filename so it's always # included after everything else. This provides aclocal with the # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything # because those macros already exist, or will be overwritten later. # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. # # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. # Yes, that means every name once taken will need to remain here until # we give up compatibility with versions before 1.7, at which point # we need to keep only those names which we still refer to. # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) ================================================ FILE: deps/pjsip/third_party/opus/m4/opus-intrinsics.m4 ================================================ dnl opus-intrinsics.m4 dnl macro for testing for support for compiler intrinsics, either by default or with a compiler flag dnl OPUS_CHECK_INTRINSICS(NAME-OF-INTRINSICS, COMPILER-FLAG-FOR-INTRINSICS, VAR-IF-PRESENT, VAR-IF-DEFAULT, TEST-PROGRAM-HEADER, TEST-PROGRAM-BODY) AC_DEFUN([OPUS_CHECK_INTRINSICS], [ AC_MSG_CHECKING([if compiler supports $1 intrinsics]) AC_LINK_IFELSE( [AC_LANG_PROGRAM($5, $6)], [ $3=1 $4=1 AC_MSG_RESULT([yes]) ],[ $4=0 AC_MSG_RESULT([no]) AC_MSG_CHECKING([if compiler supports $1 intrinsics with $2]) save_CFLAGS="$CFLAGS"; CFLAGS="$2 $CFLAGS" AC_LINK_IFELSE([AC_LANG_PROGRAM($5, $6)], [ AC_MSG_RESULT([yes]) $3=1 ],[ AC_MSG_RESULT([no]) $3=0 ]) CFLAGS="$save_CFLAGS" ]) ]) ================================================ FILE: deps/pjsip/third_party/opus/opus-uninstalled.pc.in ================================================ # Opus codec reference implementation uninstalled pkg-config file libdir=${pcfiledir}/.libs includedir=${pcfiledir} Name: opus uninstalled Description: Opus IETF audio codec (not installed, @PC_BUILD@) Version: @VERSION@ Requires: Conflicts: Libs: ${libdir}/libopus.a @LIBM@ Cflags: -I${pcfiledir}/@top_srcdir@/include ================================================ FILE: deps/pjsip/third_party/opus/opus.m4 ================================================ # Configure paths for libopus # Gregory Maxwell 08-30-2012 # Shamelessly stolen from Jack Moffitt (libogg) who # Shamelessly stole from Owen Taylor and Manish Singh dnl XIPH_PATH_OPUS([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) dnl Test for libopus, and define OPUS_CFLAGS and OPUS_LIBS dnl AC_DEFUN([XIPH_PATH_OPUS], [dnl dnl Get the cflags and libraries dnl AC_ARG_WITH(opus,AC_HELP_STRING([--with-opus=PFX],[Prefix where opus is installed (optional)]), opus_prefix="$withval", opus_prefix="") AC_ARG_WITH(opus-libraries,AC_HELP_STRING([--with-opus-libraries=DIR],[Directory where the opus library is installed (optional)]), opus_libraries="$withval", opus_libraries="") AC_ARG_WITH(opus-includes,AC_HELP_STRING([--with-opus-includes=DIR],[Directory where the opus header files are installed (optional)]), opus_includes="$withval", opus_includes="") AC_ARG_ENABLE(opustest,AC_HELP_STRING([--disable-opustest],[Do not try to compile and run a test opus program]),, enable_opustest=yes) if test "x$opus_libraries" != "x" ; then OPUS_LIBS="-L$opus_libraries" elif test "x$opus_prefix" = "xno" || test "x$opus_prefix" = "xyes" ; then OPUS_LIBS="" elif test "x$opus_prefix" != "x" ; then OPUS_LIBS="-L$opus_prefix/lib" elif test "x$prefix" != "xNONE" ; then OPUS_LIBS="-L$prefix/lib" fi if test "x$opus_prefix" != "xno" ; then OPUS_LIBS="$OPUS_LIBS -lopus" fi if test "x$opus_includes" != "x" ; then OPUS_CFLAGS="-I$opus_includes" elif test "x$opus_prefix" = "xno" || test "x$opus_prefix" = "xyes" ; then OPUS_CFLAGS="" elif test "x$opus_prefix" != "x" ; then OPUS_CFLAGS="-I$opus_prefix/include" elif test "x$prefix" != "xNONE"; then OPUS_CFLAGS="-I$prefix/include" fi AC_MSG_CHECKING(for Opus) if test "x$opus_prefix" = "xno" ; then no_opus="disabled" enable_opustest="no" else no_opus="" fi if test "x$enable_opustest" = "xyes" ; then ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $OPUS_CFLAGS" LIBS="$LIBS $OPUS_LIBS" dnl dnl Now check if the installed Opus is sufficiently new. dnl rm -f conf.opustest AC_TRY_RUN([ #include #include #include #include int main () { system("touch conf.opustest"); return 0; } ],, no_opus=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi if test "x$no_opus" = "xdisabled" ; then AC_MSG_RESULT(no) ifelse([$2], , :, [$2]) elif test "x$no_opus" = "x" ; then AC_MSG_RESULT(yes) ifelse([$1], , :, [$1]) else AC_MSG_RESULT(no) if test -f conf.opustest ; then : else echo "*** Could not run Opus test program, checking why..." CFLAGS="$CFLAGS $OPUS_CFLAGS" LIBS="$LIBS $OPUS_LIBS" AC_TRY_LINK([ #include #include ], [ return 0; ], [ echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding Opus or finding the wrong" echo "*** version of Opus. If it is not finding Opus, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" echo "*** to the installed location Also, make sure you have run ldconfig if that" echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], [ echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occurred. This usually means Opus was incorrectly installed" echo "*** or that you have moved Opus since it was installed." ]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi OPUS_CFLAGS="" OPUS_LIBS="" ifelse([$2], , :, [$2]) fi AC_SUBST(OPUS_CFLAGS) AC_SUBST(OPUS_LIBS) rm -f conf.opustest ]) ================================================ FILE: deps/pjsip/third_party/opus/opus.pc.in ================================================ # Opus codec reference implementation pkg-config file prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: Opus Description: Opus IETF audio codec (@PC_BUILD@ build) URL: http://opus-codec.org/ Version: @VERSION@ Requires: Conflicts: Libs: -L${libdir} -lopus Libs.private: @LIBM@ Cflags: -I${includedir}/opus ================================================ FILE: deps/pjsip/third_party/opus/opus_headers.mk ================================================ OPUS_HEAD = \ include/opus.h \ include/opus_multistream.h \ src/opus_private.h \ src/analysis.h \ src/mlp.h \ src/tansig_table.h ================================================ FILE: deps/pjsip/third_party/opus/opus_sources.mk ================================================ OPUS_SOURCES = src/opus.c \ src/opus_decoder.c \ src/opus_encoder.c \ src/opus_multistream.c \ src/opus_multistream_encoder.c \ src/opus_multistream_decoder.c \ src/repacketizer.c OPUS_SOURCES_FLOAT = \ src/analysis.c \ src/mlp.c \ src/mlp_data.c ================================================ FILE: deps/pjsip/third_party/opus/package_version ================================================ PACKAGE_VERSION="1.1.2" ================================================ FILE: deps/pjsip/third_party/opus/silk/A2NLSF.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ /* Conversion between prediction filter coefficients and NLSFs */ /* Requires the order to be an even number */ /* A piecewise linear approximation maps LSF <-> cos(LSF) */ /* Therefore the result is not accurate NLSFs, but the two */ /* functions are accurate inverses of each other */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" #include "tables.h" /* Number of binary divisions, when not in low complexity mode */ #define BIN_DIV_STEPS_A2NLSF_FIX 3 /* must be no higher than 16 - log2( LSF_COS_TAB_SZ_FIX ) */ #define MAX_ITERATIONS_A2NLSF_FIX 30 /* Helper function for A2NLSF(..) */ /* Transforms polynomials from cos(n*f) to cos(f)^n */ static OPUS_INLINE void silk_A2NLSF_trans_poly( opus_int32 *p, /* I/O Polynomial */ const opus_int dd /* I Polynomial order (= filter order / 2 ) */ ) { opus_int k, n; for( k = 2; k <= dd; k++ ) { for( n = dd; n > k; n-- ) { p[ n - 2 ] -= p[ n ]; } p[ k - 2 ] -= silk_LSHIFT( p[ k ], 1 ); } } /* Helper function for A2NLSF(..) */ /* Polynomial evaluation */ static OPUS_INLINE opus_int32 silk_A2NLSF_eval_poly( /* return the polynomial evaluation, in Q16 */ opus_int32 *p, /* I Polynomial, Q16 */ const opus_int32 x, /* I Evaluation point, Q12 */ const opus_int dd /* I Order */ ) { opus_int n; opus_int32 x_Q16, y32; y32 = p[ dd ]; /* Q16 */ x_Q16 = silk_LSHIFT( x, 4 ); if ( opus_likely( 8 == dd ) ) { y32 = silk_SMLAWW( p[ 7 ], y32, x_Q16 ); y32 = silk_SMLAWW( p[ 6 ], y32, x_Q16 ); y32 = silk_SMLAWW( p[ 5 ], y32, x_Q16 ); y32 = silk_SMLAWW( p[ 4 ], y32, x_Q16 ); y32 = silk_SMLAWW( p[ 3 ], y32, x_Q16 ); y32 = silk_SMLAWW( p[ 2 ], y32, x_Q16 ); y32 = silk_SMLAWW( p[ 1 ], y32, x_Q16 ); y32 = silk_SMLAWW( p[ 0 ], y32, x_Q16 ); } else { for( n = dd - 1; n >= 0; n-- ) { y32 = silk_SMLAWW( p[ n ], y32, x_Q16 ); /* Q16 */ } } return y32; } static OPUS_INLINE void silk_A2NLSF_init( const opus_int32 *a_Q16, opus_int32 *P, opus_int32 *Q, const opus_int dd ) { opus_int k; /* Convert filter coefs to even and odd polynomials */ P[dd] = silk_LSHIFT( 1, 16 ); Q[dd] = silk_LSHIFT( 1, 16 ); for( k = 0; k < dd; k++ ) { P[ k ] = -a_Q16[ dd - k - 1 ] - a_Q16[ dd + k ]; /* Q16 */ Q[ k ] = -a_Q16[ dd - k - 1 ] + a_Q16[ dd + k ]; /* Q16 */ } /* Divide out zeros as we have that for even filter orders, */ /* z = 1 is always a root in Q, and */ /* z = -1 is always a root in P */ for( k = dd; k > 0; k-- ) { P[ k - 1 ] -= P[ k ]; Q[ k - 1 ] += Q[ k ]; } /* Transform polynomials from cos(n*f) to cos(f)^n */ silk_A2NLSF_trans_poly( P, dd ); silk_A2NLSF_trans_poly( Q, dd ); } /* Compute Normalized Line Spectral Frequencies (NLSFs) from whitening filter coefficients */ /* If not all roots are found, the a_Q16 coefficients are bandwidth expanded until convergence. */ void silk_A2NLSF( opus_int16 *NLSF, /* O Normalized Line Spectral Frequencies in Q15 (0..2^15-1) [d] */ opus_int32 *a_Q16, /* I/O Monic whitening filter coefficients in Q16 [d] */ const opus_int d /* I Filter order (must be even) */ ) { opus_int i, k, m, dd, root_ix, ffrac; opus_int32 xlo, xhi, xmid; opus_int32 ylo, yhi, ymid, thr; opus_int32 nom, den; opus_int32 P[ SILK_MAX_ORDER_LPC / 2 + 1 ]; opus_int32 Q[ SILK_MAX_ORDER_LPC / 2 + 1 ]; opus_int32 *PQ[ 2 ]; opus_int32 *p; /* Store pointers to array */ PQ[ 0 ] = P; PQ[ 1 ] = Q; dd = silk_RSHIFT( d, 1 ); silk_A2NLSF_init( a_Q16, P, Q, dd ); /* Find roots, alternating between P and Q */ p = P; /* Pointer to polynomial */ xlo = silk_LSFCosTab_FIX_Q12[ 0 ]; /* Q12*/ ylo = silk_A2NLSF_eval_poly( p, xlo, dd ); if( ylo < 0 ) { /* Set the first NLSF to zero and move on to the next */ NLSF[ 0 ] = 0; p = Q; /* Pointer to polynomial */ ylo = silk_A2NLSF_eval_poly( p, xlo, dd ); root_ix = 1; /* Index of current root */ } else { root_ix = 0; /* Index of current root */ } k = 1; /* Loop counter */ i = 0; /* Counter for bandwidth expansions applied */ thr = 0; while( 1 ) { /* Evaluate polynomial */ xhi = silk_LSFCosTab_FIX_Q12[ k ]; /* Q12 */ yhi = silk_A2NLSF_eval_poly( p, xhi, dd ); /* Detect zero crossing */ if( ( ylo <= 0 && yhi >= thr ) || ( ylo >= 0 && yhi <= -thr ) ) { if( yhi == 0 ) { /* If the root lies exactly at the end of the current */ /* interval, look for the next root in the next interval */ thr = 1; } else { thr = 0; } /* Binary division */ ffrac = -256; for( m = 0; m < BIN_DIV_STEPS_A2NLSF_FIX; m++ ) { /* Evaluate polynomial */ xmid = silk_RSHIFT_ROUND( xlo + xhi, 1 ); ymid = silk_A2NLSF_eval_poly( p, xmid, dd ); /* Detect zero crossing */ if( ( ylo <= 0 && ymid >= 0 ) || ( ylo >= 0 && ymid <= 0 ) ) { /* Reduce frequency */ xhi = xmid; yhi = ymid; } else { /* Increase frequency */ xlo = xmid; ylo = ymid; ffrac = silk_ADD_RSHIFT( ffrac, 128, m ); } } /* Interpolate */ if( silk_abs( ylo ) < 65536 ) { /* Avoid dividing by zero */ den = ylo - yhi; nom = silk_LSHIFT( ylo, 8 - BIN_DIV_STEPS_A2NLSF_FIX ) + silk_RSHIFT( den, 1 ); if( den != 0 ) { ffrac += silk_DIV32( nom, den ); } } else { /* No risk of dividing by zero because abs(ylo - yhi) >= abs(ylo) >= 65536 */ ffrac += silk_DIV32( ylo, silk_RSHIFT( ylo - yhi, 8 - BIN_DIV_STEPS_A2NLSF_FIX ) ); } NLSF[ root_ix ] = (opus_int16)silk_min_32( silk_LSHIFT( (opus_int32)k, 8 ) + ffrac, silk_int16_MAX ); silk_assert( NLSF[ root_ix ] >= 0 ); root_ix++; /* Next root */ if( root_ix >= d ) { /* Found all roots */ break; } /* Alternate pointer to polynomial */ p = PQ[ root_ix & 1 ]; /* Evaluate polynomial */ xlo = silk_LSFCosTab_FIX_Q12[ k - 1 ]; /* Q12*/ ylo = silk_LSHIFT( 1 - ( root_ix & 2 ), 12 ); } else { /* Increment loop counter */ k++; xlo = xhi; ylo = yhi; thr = 0; if( k > LSF_COS_TAB_SZ_FIX ) { i++; if( i > MAX_ITERATIONS_A2NLSF_FIX ) { /* Set NLSFs to white spectrum and exit */ NLSF[ 0 ] = (opus_int16)silk_DIV32_16( 1 << 15, d + 1 ); for( k = 1; k < d; k++ ) { NLSF[ k ] = (opus_int16)silk_SMULBB( k + 1, NLSF[ 0 ] ); } return; } /* Error: Apply progressively more bandwidth expansion and run again */ silk_bwexpander_32( a_Q16, d, 65536 - silk_SMULBB( 10 + i, i ) ); /* 10_Q16 = 0.00015*/ silk_A2NLSF_init( a_Q16, P, Q, dd ); p = P; /* Pointer to polynomial */ xlo = silk_LSFCosTab_FIX_Q12[ 0 ]; /* Q12*/ ylo = silk_A2NLSF_eval_poly( p, xlo, dd ); if( ylo < 0 ) { /* Set the first NLSF to zero and move on to the next */ NLSF[ 0 ] = 0; p = Q; /* Pointer to polynomial */ ylo = silk_A2NLSF_eval_poly( p, xlo, dd ); root_ix = 1; /* Index of current root */ } else { root_ix = 0; /* Index of current root */ } k = 1; /* Reset loop counter */ } } } } ================================================ FILE: deps/pjsip/third_party/opus/silk/API.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_API_H #define SILK_API_H #include "control.h" #include "typedef.h" #include "errors.h" #include "entenc.h" #include "entdec.h" #ifdef __cplusplus extern "C" { #endif #define SILK_MAX_FRAMES_PER_PACKET 3 /* Struct for TOC (Table of Contents) */ typedef struct { opus_int VADFlag; /* Voice activity for packet */ opus_int VADFlags[ SILK_MAX_FRAMES_PER_PACKET ]; /* Voice activity for each frame in packet */ opus_int inbandFECFlag; /* Flag indicating if packet contains in-band FEC */ } silk_TOC_struct; /****************************************/ /* Encoder functions */ /****************************************/ /***********************************************/ /* Get size in bytes of the Silk encoder state */ /***********************************************/ opus_int silk_Get_Encoder_Size( /* O Returns error code */ opus_int *encSizeBytes /* O Number of bytes in SILK encoder state */ ); /*************************/ /* Init or reset encoder */ /*************************/ opus_int silk_InitEncoder( /* O Returns error code */ void *encState, /* I/O State */ int arch, /* I Run-time architecture */ silk_EncControlStruct *encStatus /* O Encoder Status */ ); /**************************/ /* Encode frame with Silk */ /**************************/ /* Note: if prefillFlag is set, the input must contain 10 ms of audio, irrespective of what */ /* encControl->payloadSize_ms is set to */ opus_int silk_Encode( /* O Returns error code */ void *encState, /* I/O State */ silk_EncControlStruct *encControl, /* I Control status */ const opus_int16 *samplesIn, /* I Speech sample input vector */ opus_int nSamplesIn, /* I Number of samples in input vector */ ec_enc *psRangeEnc, /* I/O Compressor data structure */ opus_int32 *nBytesOut, /* I/O Number of bytes in payload (input: Max bytes) */ const opus_int prefillFlag /* I Flag to indicate prefilling buffers no coding */ ); /****************************************/ /* Decoder functions */ /****************************************/ /***********************************************/ /* Get size in bytes of the Silk decoder state */ /***********************************************/ opus_int silk_Get_Decoder_Size( /* O Returns error code */ opus_int *decSizeBytes /* O Number of bytes in SILK decoder state */ ); /*************************/ /* Init or Reset decoder */ /*************************/ opus_int silk_InitDecoder( /* O Returns error code */ void *decState /* I/O State */ ); /******************/ /* Decode a frame */ /******************/ opus_int silk_Decode( /* O Returns error code */ void* decState, /* I/O State */ silk_DecControlStruct* decControl, /* I/O Control Structure */ opus_int lostFlag, /* I 0: no loss, 1 loss, 2 decode fec */ opus_int newPacketFlag, /* I Indicates first decoder call for this packet */ ec_dec *psRangeDec, /* I/O Compressor data structure */ opus_int16 *samplesOut, /* O Decoded output speech vector */ opus_int32 *nSamplesOut, /* O Number of samples decoded */ int arch /* I Run-time architecture */ ); #if 0 /**************************************/ /* Get table of contents for a packet */ /**************************************/ opus_int silk_get_TOC( const opus_uint8 *payload, /* I Payload data */ const opus_int nBytesIn, /* I Number of input bytes */ const opus_int nFramesPerPayload, /* I Number of SILK frames per payload */ silk_TOC_struct *Silk_TOC /* O Type of content */ ); #endif #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/CNG.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #include "stack_alloc.h" /* Generates excitation for CNG LPC synthesis */ static OPUS_INLINE void silk_CNG_exc( opus_int32 exc_Q10[], /* O CNG excitation signal Q10 */ opus_int32 exc_buf_Q14[], /* I Random samples buffer Q10 */ opus_int32 Gain_Q16, /* I Gain to apply */ opus_int length, /* I Length */ opus_int32 *rand_seed /* I/O Seed to random index generator */ ) { opus_int32 seed; opus_int i, idx, exc_mask; exc_mask = CNG_BUF_MASK_MAX; while( exc_mask > length ) { exc_mask = silk_RSHIFT( exc_mask, 1 ); } seed = *rand_seed; for( i = 0; i < length; i++ ) { seed = silk_RAND( seed ); idx = (opus_int)( silk_RSHIFT( seed, 24 ) & exc_mask ); silk_assert( idx >= 0 ); silk_assert( idx <= CNG_BUF_MASK_MAX ); exc_Q10[ i ] = (opus_int16)silk_SAT16( silk_SMULWW( exc_buf_Q14[ idx ], Gain_Q16 >> 4 ) ); } *rand_seed = seed; } void silk_CNG_Reset( silk_decoder_state *psDec /* I/O Decoder state */ ) { opus_int i, NLSF_step_Q15, NLSF_acc_Q15; NLSF_step_Q15 = silk_DIV32_16( silk_int16_MAX, psDec->LPC_order + 1 ); NLSF_acc_Q15 = 0; for( i = 0; i < psDec->LPC_order; i++ ) { NLSF_acc_Q15 += NLSF_step_Q15; psDec->sCNG.CNG_smth_NLSF_Q15[ i ] = NLSF_acc_Q15; } psDec->sCNG.CNG_smth_Gain_Q16 = 0; psDec->sCNG.rand_seed = 3176576; } /* Updates CNG estimate, and applies the CNG when packet was lost */ void silk_CNG( silk_decoder_state *psDec, /* I/O Decoder state */ silk_decoder_control *psDecCtrl, /* I/O Decoder control */ opus_int16 frame[], /* I/O Signal */ opus_int length /* I Length of residual */ ) { opus_int i, subfr; opus_int32 sum_Q6, max_Gain_Q16, gain_Q16; opus_int16 A_Q12[ MAX_LPC_ORDER ]; silk_CNG_struct *psCNG = &psDec->sCNG; SAVE_STACK; if( psDec->fs_kHz != psCNG->fs_kHz ) { /* Reset state */ silk_CNG_Reset( psDec ); psCNG->fs_kHz = psDec->fs_kHz; } if( psDec->lossCnt == 0 && psDec->prevSignalType == TYPE_NO_VOICE_ACTIVITY ) { /* Update CNG parameters */ /* Smoothing of LSF's */ for( i = 0; i < psDec->LPC_order; i++ ) { psCNG->CNG_smth_NLSF_Q15[ i ] += silk_SMULWB( (opus_int32)psDec->prevNLSF_Q15[ i ] - (opus_int32)psCNG->CNG_smth_NLSF_Q15[ i ], CNG_NLSF_SMTH_Q16 ); } /* Find the subframe with the highest gain */ max_Gain_Q16 = 0; subfr = 0; for( i = 0; i < psDec->nb_subfr; i++ ) { if( psDecCtrl->Gains_Q16[ i ] > max_Gain_Q16 ) { max_Gain_Q16 = psDecCtrl->Gains_Q16[ i ]; subfr = i; } } /* Update CNG excitation buffer with excitation from this subframe */ silk_memmove( &psCNG->CNG_exc_buf_Q14[ psDec->subfr_length ], psCNG->CNG_exc_buf_Q14, ( psDec->nb_subfr - 1 ) * psDec->subfr_length * sizeof( opus_int32 ) ); silk_memcpy( psCNG->CNG_exc_buf_Q14, &psDec->exc_Q14[ subfr * psDec->subfr_length ], psDec->subfr_length * sizeof( opus_int32 ) ); /* Smooth gains */ for( i = 0; i < psDec->nb_subfr; i++ ) { psCNG->CNG_smth_Gain_Q16 += silk_SMULWB( psDecCtrl->Gains_Q16[ i ] - psCNG->CNG_smth_Gain_Q16, CNG_GAIN_SMTH_Q16 ); } } /* Add CNG when packet is lost or during DTX */ if( psDec->lossCnt ) { VARDECL( opus_int32, CNG_sig_Q10 ); ALLOC( CNG_sig_Q10, length + MAX_LPC_ORDER, opus_int32 ); /* Generate CNG excitation */ gain_Q16 = silk_SMULWW( psDec->sPLC.randScale_Q14, psDec->sPLC.prevGain_Q16[1] ); if( gain_Q16 >= (1 << 21) || psCNG->CNG_smth_Gain_Q16 > (1 << 23) ) { gain_Q16 = silk_SMULTT( gain_Q16, gain_Q16 ); gain_Q16 = silk_SUB_LSHIFT32(silk_SMULTT( psCNG->CNG_smth_Gain_Q16, psCNG->CNG_smth_Gain_Q16 ), gain_Q16, 5 ); gain_Q16 = silk_LSHIFT32( silk_SQRT_APPROX( gain_Q16 ), 16 ); } else { gain_Q16 = silk_SMULWW( gain_Q16, gain_Q16 ); gain_Q16 = silk_SUB_LSHIFT32(silk_SMULWW( psCNG->CNG_smth_Gain_Q16, psCNG->CNG_smth_Gain_Q16 ), gain_Q16, 5 ); gain_Q16 = silk_LSHIFT32( silk_SQRT_APPROX( gain_Q16 ), 8 ); } silk_CNG_exc( CNG_sig_Q10 + MAX_LPC_ORDER, psCNG->CNG_exc_buf_Q14, gain_Q16, length, &psCNG->rand_seed ); /* Convert CNG NLSF to filter representation */ silk_NLSF2A( A_Q12, psCNG->CNG_smth_NLSF_Q15, psDec->LPC_order ); /* Generate CNG signal, by synthesis filtering */ silk_memcpy( CNG_sig_Q10, psCNG->CNG_synth_state, MAX_LPC_ORDER * sizeof( opus_int32 ) ); for( i = 0; i < length; i++ ) { silk_assert( psDec->LPC_order == 10 || psDec->LPC_order == 16 ); /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ sum_Q6 = silk_RSHIFT( psDec->LPC_order, 1 ); sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 1 ], A_Q12[ 0 ] ); sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 2 ], A_Q12[ 1 ] ); sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 3 ], A_Q12[ 2 ] ); sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 4 ], A_Q12[ 3 ] ); sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 5 ], A_Q12[ 4 ] ); sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 6 ], A_Q12[ 5 ] ); sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 7 ], A_Q12[ 6 ] ); sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 8 ], A_Q12[ 7 ] ); sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 9 ], A_Q12[ 8 ] ); sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 10 ], A_Q12[ 9 ] ); if( psDec->LPC_order == 16 ) { sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 11 ], A_Q12[ 10 ] ); sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 12 ], A_Q12[ 11 ] ); sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 13 ], A_Q12[ 12 ] ); sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 14 ], A_Q12[ 13 ] ); sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 15 ], A_Q12[ 14 ] ); sum_Q6 = silk_SMLAWB( sum_Q6, CNG_sig_Q10[ MAX_LPC_ORDER + i - 16 ], A_Q12[ 15 ] ); } /* Update states */ CNG_sig_Q10[ MAX_LPC_ORDER + i ] = silk_ADD_LSHIFT( CNG_sig_Q10[ MAX_LPC_ORDER + i ], sum_Q6, 4 ); frame[ i ] = silk_ADD_SAT16( frame[ i ], silk_RSHIFT_ROUND( CNG_sig_Q10[ MAX_LPC_ORDER + i ], 10 ) ); } silk_memcpy( psCNG->CNG_synth_state, &CNG_sig_Q10[ length ], MAX_LPC_ORDER * sizeof( opus_int32 ) ); } else { silk_memset( psCNG->CNG_synth_state, 0, psDec->LPC_order * sizeof( opus_int32 ) ); } RESTORE_STACK; } ================================================ FILE: deps/pjsip/third_party/opus/silk/HP_variable_cutoff.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef FIXED_POINT #include "main_FIX.h" #else #include "main_FLP.h" #endif #include "tuning_parameters.h" /* High-pass filter with cutoff frequency adaptation based on pitch lag statistics */ void silk_HP_variable_cutoff( silk_encoder_state_Fxx state_Fxx[] /* I/O Encoder states */ ) { opus_int quality_Q15; opus_int32 pitch_freq_Hz_Q16, pitch_freq_log_Q7, delta_freq_Q7; silk_encoder_state *psEncC1 = &state_Fxx[ 0 ].sCmn; /* Adaptive cutoff frequency: estimate low end of pitch frequency range */ if( psEncC1->prevSignalType == TYPE_VOICED ) { /* difference, in log domain */ pitch_freq_Hz_Q16 = silk_DIV32_16( silk_LSHIFT( silk_MUL( psEncC1->fs_kHz, 1000 ), 16 ), psEncC1->prevLag ); pitch_freq_log_Q7 = silk_lin2log( pitch_freq_Hz_Q16 ) - ( 16 << 7 ); /* adjustment based on quality */ quality_Q15 = psEncC1->input_quality_bands_Q15[ 0 ]; pitch_freq_log_Q7 = silk_SMLAWB( pitch_freq_log_Q7, silk_SMULWB( silk_LSHIFT( -quality_Q15, 2 ), quality_Q15 ), pitch_freq_log_Q7 - ( silk_lin2log( SILK_FIX_CONST( VARIABLE_HP_MIN_CUTOFF_HZ, 16 ) ) - ( 16 << 7 ) ) ); /* delta_freq = pitch_freq_log - psEnc->variable_HP_smth1; */ delta_freq_Q7 = pitch_freq_log_Q7 - silk_RSHIFT( psEncC1->variable_HP_smth1_Q15, 8 ); if( delta_freq_Q7 < 0 ) { /* less smoothing for decreasing pitch frequency, to track something close to the minimum */ delta_freq_Q7 = silk_MUL( delta_freq_Q7, 3 ); } /* limit delta, to reduce impact of outliers in pitch estimation */ delta_freq_Q7 = silk_LIMIT_32( delta_freq_Q7, -SILK_FIX_CONST( VARIABLE_HP_MAX_DELTA_FREQ, 7 ), SILK_FIX_CONST( VARIABLE_HP_MAX_DELTA_FREQ, 7 ) ); /* update smoother */ psEncC1->variable_HP_smth1_Q15 = silk_SMLAWB( psEncC1->variable_HP_smth1_Q15, silk_SMULBB( psEncC1->speech_activity_Q8, delta_freq_Q7 ), SILK_FIX_CONST( VARIABLE_HP_SMTH_COEF1, 16 ) ); /* limit frequency range */ psEncC1->variable_HP_smth1_Q15 = silk_LIMIT_32( psEncC1->variable_HP_smth1_Q15, silk_LSHIFT( silk_lin2log( VARIABLE_HP_MIN_CUTOFF_HZ ), 8 ), silk_LSHIFT( silk_lin2log( VARIABLE_HP_MAX_CUTOFF_HZ ), 8 ) ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/Inlines.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ /*! \file silk_Inlines.h * \brief silk_Inlines.h defines OPUS_INLINE signal processing functions. */ #ifndef SILK_FIX_INLINES_H #define SILK_FIX_INLINES_H #ifdef __cplusplus extern "C" { #endif /* count leading zeros of opus_int64 */ static OPUS_INLINE opus_int32 silk_CLZ64( opus_int64 in ) { opus_int32 in_upper; in_upper = (opus_int32)silk_RSHIFT64(in, 32); if (in_upper == 0) { /* Search in the lower 32 bits */ return 32 + silk_CLZ32( (opus_int32) in ); } else { /* Search in the upper 32 bits */ return silk_CLZ32( in_upper ); } } /* get number of leading zeros and fractional part (the bits right after the leading one */ static OPUS_INLINE void silk_CLZ_FRAC( opus_int32 in, /* I input */ opus_int32 *lz, /* O number of leading zeros */ opus_int32 *frac_Q7 /* O the 7 bits right after the leading one */ ) { opus_int32 lzeros = silk_CLZ32(in); * lz = lzeros; * frac_Q7 = silk_ROR32(in, 24 - lzeros) & 0x7f; } /* Approximation of square root */ /* Accuracy: < +/- 10% for output values > 15 */ /* < +/- 2.5% for output values > 120 */ static OPUS_INLINE opus_int32 silk_SQRT_APPROX( opus_int32 x ) { opus_int32 y, lz, frac_Q7; if( x <= 0 ) { return 0; } silk_CLZ_FRAC(x, &lz, &frac_Q7); if( lz & 1 ) { y = 32768; } else { y = 46214; /* 46214 = sqrt(2) * 32768 */ } /* get scaling right */ y >>= silk_RSHIFT(lz, 1); /* increment using fractional part of input */ y = silk_SMLAWB(y, y, silk_SMULBB(213, frac_Q7)); return y; } /* Divide two int32 values and return result as int32 in a given Q-domain */ static OPUS_INLINE opus_int32 silk_DIV32_varQ( /* O returns a good approximation of "(a32 << Qres) / b32" */ const opus_int32 a32, /* I numerator (Q0) */ const opus_int32 b32, /* I denominator (Q0) */ const opus_int Qres /* I Q-domain of result (>= 0) */ ) { opus_int a_headrm, b_headrm, lshift; opus_int32 b32_inv, a32_nrm, b32_nrm, result; silk_assert( b32 != 0 ); silk_assert( Qres >= 0 ); /* Compute number of bits head room and normalize inputs */ a_headrm = silk_CLZ32( silk_abs(a32) ) - 1; a32_nrm = silk_LSHIFT(a32, a_headrm); /* Q: a_headrm */ b_headrm = silk_CLZ32( silk_abs(b32) ) - 1; b32_nrm = silk_LSHIFT(b32, b_headrm); /* Q: b_headrm */ /* Inverse of b32, with 14 bits of precision */ b32_inv = silk_DIV32_16( silk_int32_MAX >> 2, silk_RSHIFT(b32_nrm, 16) ); /* Q: 29 + 16 - b_headrm */ /* First approximation */ result = silk_SMULWB(a32_nrm, b32_inv); /* Q: 29 + a_headrm - b_headrm */ /* Compute residual by subtracting product of denominator and first approximation */ /* It's OK to overflow because the final value of a32_nrm should always be small */ a32_nrm = silk_SUB32_ovflw(a32_nrm, silk_LSHIFT_ovflw( silk_SMMUL(b32_nrm, result), 3 )); /* Q: a_headrm */ /* Refinement */ result = silk_SMLAWB(result, a32_nrm, b32_inv); /* Q: 29 + a_headrm - b_headrm */ /* Convert to Qres domain */ lshift = 29 + a_headrm - b_headrm - Qres; if( lshift < 0 ) { return silk_LSHIFT_SAT32(result, -lshift); } else { if( lshift < 32){ return silk_RSHIFT(result, lshift); } else { /* Avoid undefined result */ return 0; } } } /* Invert int32 value and return result as int32 in a given Q-domain */ static OPUS_INLINE opus_int32 silk_INVERSE32_varQ( /* O returns a good approximation of "(1 << Qres) / b32" */ const opus_int32 b32, /* I denominator (Q0) */ const opus_int Qres /* I Q-domain of result (> 0) */ ) { opus_int b_headrm, lshift; opus_int32 b32_inv, b32_nrm, err_Q32, result; silk_assert( b32 != 0 ); silk_assert( Qres > 0 ); /* Compute number of bits head room and normalize input */ b_headrm = silk_CLZ32( silk_abs(b32) ) - 1; b32_nrm = silk_LSHIFT(b32, b_headrm); /* Q: b_headrm */ /* Inverse of b32, with 14 bits of precision */ b32_inv = silk_DIV32_16( silk_int32_MAX >> 2, silk_RSHIFT(b32_nrm, 16) ); /* Q: 29 + 16 - b_headrm */ /* First approximation */ result = silk_LSHIFT(b32_inv, 16); /* Q: 61 - b_headrm */ /* Compute residual by subtracting product of denominator and first approximation from one */ err_Q32 = silk_LSHIFT( ((opus_int32)1<<29) - silk_SMULWB(b32_nrm, b32_inv), 3 ); /* Q32 */ /* Refinement */ result = silk_SMLAWW(result, err_Q32, b32_inv); /* Q: 61 - b_headrm */ /* Convert to Qres domain */ lshift = 61 - b_headrm - Qres; if( lshift <= 0 ) { return silk_LSHIFT_SAT32(result, -lshift); } else { if( lshift < 32){ return silk_RSHIFT(result, lshift); }else{ /* Avoid undefined result */ return 0; } } } #ifdef __cplusplus } #endif #endif /* SILK_FIX_INLINES_H */ ================================================ FILE: deps/pjsip/third_party/opus/silk/LPC_analysis_filter.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" #include "celt_lpc.h" /*******************************************/ /* LPC analysis filter */ /* NB! State is kept internally and the */ /* filter always starts with zero state */ /* first d output samples are set to zero */ /*******************************************/ void silk_LPC_analysis_filter( opus_int16 *out, /* O Output signal */ const opus_int16 *in, /* I Input signal */ const opus_int16 *B, /* I MA prediction coefficients, Q12 [order] */ const opus_int32 len, /* I Signal length */ const opus_int32 d, /* I Filter order */ int arch /* I Run-time architecture */ ) { opus_int j; #ifdef FIXED_POINT opus_int16 mem[SILK_MAX_ORDER_LPC]; opus_int16 num[SILK_MAX_ORDER_LPC]; #else int ix; opus_int32 out32_Q12, out32; const opus_int16 *in_ptr; #endif silk_assert( d >= 6 ); silk_assert( (d & 1) == 0 ); silk_assert( d <= len ); #ifdef FIXED_POINT silk_assert( d <= SILK_MAX_ORDER_LPC ); for ( j = 0; j < d; j++ ) { num[ j ] = -B[ j ]; } for (j=0;j 0; k-- ) { /* Check for stability */ if( ( Anew_QA[ k ] > A_LIMIT ) || ( Anew_QA[ k ] < -A_LIMIT ) ) { return 0; } /* Set RC equal to negated AR coef */ rc_Q31 = -silk_LSHIFT( Anew_QA[ k ], 31 - QA ); /* rc_mult1_Q30 range: [ 1 : 2^30 ] */ rc_mult1_Q30 = ( (opus_int32)1 << 30 ) - silk_SMMUL( rc_Q31, rc_Q31 ); silk_assert( rc_mult1_Q30 > ( 1 << 15 ) ); /* reduce A_LIMIT if fails */ silk_assert( rc_mult1_Q30 <= ( 1 << 30 ) ); /* rc_mult2 range: [ 2^30 : silk_int32_MAX ] */ mult2Q = 32 - silk_CLZ32( silk_abs( rc_mult1_Q30 ) ); rc_mult2 = silk_INVERSE32_varQ( rc_mult1_Q30, mult2Q + 30 ); /* Update inverse gain */ /* invGain_Q30 range: [ 0 : 2^30 ] */ invGain_Q30 = silk_LSHIFT( silk_SMMUL( invGain_Q30, rc_mult1_Q30 ), 2 ); silk_assert( invGain_Q30 >= 0 ); silk_assert( invGain_Q30 <= ( 1 << 30 ) ); /* Swap pointers */ Aold_QA = Anew_QA; Anew_QA = A_QA[ k & 1 ]; /* Update AR coefficient */ for( n = 0; n < k; n++ ) { tmp_QA = Aold_QA[ n ] - MUL32_FRAC_Q( Aold_QA[ k - n - 1 ], rc_Q31, 31 ); Anew_QA[ n ] = MUL32_FRAC_Q( tmp_QA, rc_mult2 , mult2Q ); } } /* Check for stability */ if( ( Anew_QA[ 0 ] > A_LIMIT ) || ( Anew_QA[ 0 ] < -A_LIMIT ) ) { return 0; } /* Set RC equal to negated AR coef */ rc_Q31 = -silk_LSHIFT( Anew_QA[ 0 ], 31 - QA ); /* Range: [ 1 : 2^30 ] */ rc_mult1_Q30 = ( (opus_int32)1 << 30 ) - silk_SMMUL( rc_Q31, rc_Q31 ); /* Update inverse gain */ /* Range: [ 0 : 2^30 ] */ invGain_Q30 = silk_LSHIFT( silk_SMMUL( invGain_Q30, rc_mult1_Q30 ), 2 ); silk_assert( invGain_Q30 >= 0 ); silk_assert( invGain_Q30 <= 1<<30 ); return invGain_Q30; } /* For input in Q12 domain */ opus_int32 silk_LPC_inverse_pred_gain( /* O Returns inverse prediction gain in energy domain, Q30 */ const opus_int16 *A_Q12, /* I Prediction coefficients, Q12 [order] */ const opus_int order /* I Prediction order */ ) { opus_int k; opus_int32 Atmp_QA[ 2 ][ SILK_MAX_ORDER_LPC ]; opus_int32 *Anew_QA; opus_int32 DC_resp = 0; Anew_QA = Atmp_QA[ order & 1 ]; /* Increase Q domain of the AR coefficients */ for( k = 0; k < order; k++ ) { DC_resp += (opus_int32)A_Q12[ k ]; Anew_QA[ k ] = silk_LSHIFT32( (opus_int32)A_Q12[ k ], QA - 12 ); } /* If the DC is unstable, we don't even need to do the full calculations */ if( DC_resp >= 4096 ) { return 0; } return LPC_inverse_pred_gain_QA( Atmp_QA, order ); } #ifdef FIXED_POINT /* For input in Q24 domain */ opus_int32 silk_LPC_inverse_pred_gain_Q24( /* O Returns inverse prediction gain in energy domain, Q30 */ const opus_int32 *A_Q24, /* I Prediction coefficients [order] */ const opus_int order /* I Prediction order */ ) { opus_int k; opus_int32 Atmp_QA[ 2 ][ SILK_MAX_ORDER_LPC ]; opus_int32 *Anew_QA; Anew_QA = Atmp_QA[ order & 1 ]; /* Increase Q domain of the AR coefficients */ for( k = 0; k < order; k++ ) { Anew_QA[ k ] = silk_RSHIFT32( A_Q24[ k ], 24 - QA ); } return LPC_inverse_pred_gain_QA( Atmp_QA, order ); } #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/LP_variable_cutoff.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* Elliptic/Cauer filters designed with 0.1 dB passband ripple, 80 dB minimum stopband attenuation, and [0.95 : 0.15 : 0.35] normalized cut off frequencies. */ #include "main.h" /* Helper function, interpolates the filter taps */ static OPUS_INLINE void silk_LP_interpolate_filter_taps( opus_int32 B_Q28[ TRANSITION_NB ], opus_int32 A_Q28[ TRANSITION_NA ], const opus_int ind, const opus_int32 fac_Q16 ) { opus_int nb, na; if( ind < TRANSITION_INT_NUM - 1 ) { if( fac_Q16 > 0 ) { if( fac_Q16 < 32768 ) { /* fac_Q16 is in range of a 16-bit int */ /* Piece-wise linear interpolation of B and A */ for( nb = 0; nb < TRANSITION_NB; nb++ ) { B_Q28[ nb ] = silk_SMLAWB( silk_Transition_LP_B_Q28[ ind ][ nb ], silk_Transition_LP_B_Q28[ ind + 1 ][ nb ] - silk_Transition_LP_B_Q28[ ind ][ nb ], fac_Q16 ); } for( na = 0; na < TRANSITION_NA; na++ ) { A_Q28[ na ] = silk_SMLAWB( silk_Transition_LP_A_Q28[ ind ][ na ], silk_Transition_LP_A_Q28[ ind + 1 ][ na ] - silk_Transition_LP_A_Q28[ ind ][ na ], fac_Q16 ); } } else { /* ( fac_Q16 - ( 1 << 16 ) ) is in range of a 16-bit int */ silk_assert( fac_Q16 - ( 1 << 16 ) == silk_SAT16( fac_Q16 - ( 1 << 16 ) ) ); /* Piece-wise linear interpolation of B and A */ for( nb = 0; nb < TRANSITION_NB; nb++ ) { B_Q28[ nb ] = silk_SMLAWB( silk_Transition_LP_B_Q28[ ind + 1 ][ nb ], silk_Transition_LP_B_Q28[ ind + 1 ][ nb ] - silk_Transition_LP_B_Q28[ ind ][ nb ], fac_Q16 - ( (opus_int32)1 << 16 ) ); } for( na = 0; na < TRANSITION_NA; na++ ) { A_Q28[ na ] = silk_SMLAWB( silk_Transition_LP_A_Q28[ ind + 1 ][ na ], silk_Transition_LP_A_Q28[ ind + 1 ][ na ] - silk_Transition_LP_A_Q28[ ind ][ na ], fac_Q16 - ( (opus_int32)1 << 16 ) ); } } } else { silk_memcpy( B_Q28, silk_Transition_LP_B_Q28[ ind ], TRANSITION_NB * sizeof( opus_int32 ) ); silk_memcpy( A_Q28, silk_Transition_LP_A_Q28[ ind ], TRANSITION_NA * sizeof( opus_int32 ) ); } } else { silk_memcpy( B_Q28, silk_Transition_LP_B_Q28[ TRANSITION_INT_NUM - 1 ], TRANSITION_NB * sizeof( opus_int32 ) ); silk_memcpy( A_Q28, silk_Transition_LP_A_Q28[ TRANSITION_INT_NUM - 1 ], TRANSITION_NA * sizeof( opus_int32 ) ); } } /* Low-pass filter with variable cutoff frequency based on */ /* piece-wise linear interpolation between elliptic filters */ /* Start by setting psEncC->mode <> 0; */ /* Deactivate by setting psEncC->mode = 0; */ void silk_LP_variable_cutoff( silk_LP_state *psLP, /* I/O LP filter state */ opus_int16 *frame, /* I/O Low-pass filtered output signal */ const opus_int frame_length /* I Frame length */ ) { opus_int32 B_Q28[ TRANSITION_NB ], A_Q28[ TRANSITION_NA ], fac_Q16 = 0; opus_int ind = 0; silk_assert( psLP->transition_frame_no >= 0 && psLP->transition_frame_no <= TRANSITION_FRAMES ); /* Run filter if needed */ if( psLP->mode != 0 ) { /* Calculate index and interpolation factor for interpolation */ #if( TRANSITION_INT_STEPS == 64 ) fac_Q16 = silk_LSHIFT( TRANSITION_FRAMES - psLP->transition_frame_no, 16 - 6 ); #else fac_Q16 = silk_DIV32_16( silk_LSHIFT( TRANSITION_FRAMES - psLP->transition_frame_no, 16 ), TRANSITION_FRAMES ); #endif ind = silk_RSHIFT( fac_Q16, 16 ); fac_Q16 -= silk_LSHIFT( ind, 16 ); silk_assert( ind >= 0 ); silk_assert( ind < TRANSITION_INT_NUM ); /* Interpolate filter coefficients */ silk_LP_interpolate_filter_taps( B_Q28, A_Q28, ind, fac_Q16 ); /* Update transition frame number for next frame */ psLP->transition_frame_no = silk_LIMIT( psLP->transition_frame_no + psLP->mode, 0, TRANSITION_FRAMES ); /* ARMA low-pass filtering */ silk_assert( TRANSITION_NB == 3 && TRANSITION_NA == 2 ); silk_biquad_alt( frame, B_Q28, A_Q28, psLP->In_LP_State, frame, frame_length, 1); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/MacroCount.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SIGPROCFIX_API_MACROCOUNT_H #define SIGPROCFIX_API_MACROCOUNT_H #include #ifdef silk_MACRO_COUNT #define varDefine opus_int64 ops_count = 0; extern opus_int64 ops_count; static OPUS_INLINE opus_int64 silk_SaveCount(){ return(ops_count); } static OPUS_INLINE opus_int64 silk_SaveResetCount(){ opus_int64 ret; ret = ops_count; ops_count = 0; return(ret); } static OPUS_INLINE silk_PrintCount(){ printf("ops_count = %d \n ", (opus_int32)ops_count); } #undef silk_MUL static OPUS_INLINE opus_int32 silk_MUL(opus_int32 a32, opus_int32 b32){ opus_int32 ret; ops_count += 4; ret = a32 * b32; return ret; } #undef silk_MUL_uint static OPUS_INLINE opus_uint32 silk_MUL_uint(opus_uint32 a32, opus_uint32 b32){ opus_uint32 ret; ops_count += 4; ret = a32 * b32; return ret; } #undef silk_MLA static OPUS_INLINE opus_int32 silk_MLA(opus_int32 a32, opus_int32 b32, opus_int32 c32){ opus_int32 ret; ops_count += 4; ret = a32 + b32 * c32; return ret; } #undef silk_MLA_uint static OPUS_INLINE opus_int32 silk_MLA_uint(opus_uint32 a32, opus_uint32 b32, opus_uint32 c32){ opus_uint32 ret; ops_count += 4; ret = a32 + b32 * c32; return ret; } #undef silk_SMULWB static OPUS_INLINE opus_int32 silk_SMULWB(opus_int32 a32, opus_int32 b32){ opus_int32 ret; ops_count += 5; ret = (a32 >> 16) * (opus_int32)((opus_int16)b32) + (((a32 & 0x0000FFFF) * (opus_int32)((opus_int16)b32)) >> 16); return ret; } #undef silk_SMLAWB static OPUS_INLINE opus_int32 silk_SMLAWB(opus_int32 a32, opus_int32 b32, opus_int32 c32){ opus_int32 ret; ops_count += 5; ret = ((a32) + ((((b32) >> 16) * (opus_int32)((opus_int16)(c32))) + ((((b32) & 0x0000FFFF) * (opus_int32)((opus_int16)(c32))) >> 16))); return ret; } #undef silk_SMULWT static OPUS_INLINE opus_int32 silk_SMULWT(opus_int32 a32, opus_int32 b32){ opus_int32 ret; ops_count += 4; ret = (a32 >> 16) * (b32 >> 16) + (((a32 & 0x0000FFFF) * (b32 >> 16)) >> 16); return ret; } #undef silk_SMLAWT static OPUS_INLINE opus_int32 silk_SMLAWT(opus_int32 a32, opus_int32 b32, opus_int32 c32){ opus_int32 ret; ops_count += 4; ret = a32 + ((b32 >> 16) * (c32 >> 16)) + (((b32 & 0x0000FFFF) * ((c32 >> 16)) >> 16)); return ret; } #undef silk_SMULBB static OPUS_INLINE opus_int32 silk_SMULBB(opus_int32 a32, opus_int32 b32){ opus_int32 ret; ops_count += 1; ret = (opus_int32)((opus_int16)a32) * (opus_int32)((opus_int16)b32); return ret; } #undef silk_SMLABB static OPUS_INLINE opus_int32 silk_SMLABB(opus_int32 a32, opus_int32 b32, opus_int32 c32){ opus_int32 ret; ops_count += 1; ret = a32 + (opus_int32)((opus_int16)b32) * (opus_int32)((opus_int16)c32); return ret; } #undef silk_SMULBT static OPUS_INLINE opus_int32 silk_SMULBT(opus_int32 a32, opus_int32 b32 ){ opus_int32 ret; ops_count += 4; ret = ((opus_int32)((opus_int16)a32)) * (b32 >> 16); return ret; } #undef silk_SMLABT static OPUS_INLINE opus_int32 silk_SMLABT(opus_int32 a32, opus_int32 b32, opus_int32 c32){ opus_int32 ret; ops_count += 1; ret = a32 + ((opus_int32)((opus_int16)b32)) * (c32 >> 16); return ret; } #undef silk_SMULTT static OPUS_INLINE opus_int32 silk_SMULTT(opus_int32 a32, opus_int32 b32){ opus_int32 ret; ops_count += 1; ret = (a32 >> 16) * (b32 >> 16); return ret; } #undef silk_SMLATT static OPUS_INLINE opus_int32 silk_SMLATT(opus_int32 a32, opus_int32 b32, opus_int32 c32){ opus_int32 ret; ops_count += 1; ret = a32 + (b32 >> 16) * (c32 >> 16); return ret; } /* multiply-accumulate macros that allow overflow in the addition (ie, no asserts in debug mode)*/ #undef silk_MLA_ovflw #define silk_MLA_ovflw silk_MLA #undef silk_SMLABB_ovflw #define silk_SMLABB_ovflw silk_SMLABB #undef silk_SMLABT_ovflw #define silk_SMLABT_ovflw silk_SMLABT #undef silk_SMLATT_ovflw #define silk_SMLATT_ovflw silk_SMLATT #undef silk_SMLAWB_ovflw #define silk_SMLAWB_ovflw silk_SMLAWB #undef silk_SMLAWT_ovflw #define silk_SMLAWT_ovflw silk_SMLAWT #undef silk_SMULL static OPUS_INLINE opus_int64 silk_SMULL(opus_int32 a32, opus_int32 b32){ opus_int64 ret; ops_count += 8; ret = ((opus_int64)(a32) * /*(opus_int64)*/(b32)); return ret; } #undef silk_SMLAL static OPUS_INLINE opus_int64 silk_SMLAL(opus_int64 a64, opus_int32 b32, opus_int32 c32){ opus_int64 ret; ops_count += 8; ret = a64 + ((opus_int64)(b32) * /*(opus_int64)*/(c32)); return ret; } #undef silk_SMLALBB static OPUS_INLINE opus_int64 silk_SMLALBB(opus_int64 a64, opus_int16 b16, opus_int16 c16){ opus_int64 ret; ops_count += 4; ret = a64 + ((opus_int64)(b16) * /*(opus_int64)*/(c16)); return ret; } #undef SigProcFIX_CLZ16 static OPUS_INLINE opus_int32 SigProcFIX_CLZ16(opus_int16 in16) { opus_int32 out32 = 0; ops_count += 10; if( in16 == 0 ) { return 16; } /* test nibbles */ if( in16 & 0xFF00 ) { if( in16 & 0xF000 ) { in16 >>= 12; } else { out32 += 4; in16 >>= 8; } } else { if( in16 & 0xFFF0 ) { out32 += 8; in16 >>= 4; } else { out32 += 12; } } /* test bits and return */ if( in16 & 0xC ) { if( in16 & 0x8 ) return out32 + 0; else return out32 + 1; } else { if( in16 & 0xE ) return out32 + 2; else return out32 + 3; } } #undef SigProcFIX_CLZ32 static OPUS_INLINE opus_int32 SigProcFIX_CLZ32(opus_int32 in32) { /* test highest 16 bits and convert to opus_int16 */ ops_count += 2; if( in32 & 0xFFFF0000 ) { return SigProcFIX_CLZ16((opus_int16)(in32 >> 16)); } else { return SigProcFIX_CLZ16((opus_int16)in32) + 16; } } #undef silk_DIV32 static OPUS_INLINE opus_int32 silk_DIV32(opus_int32 a32, opus_int32 b32){ ops_count += 64; return a32 / b32; } #undef silk_DIV32_16 static OPUS_INLINE opus_int32 silk_DIV32_16(opus_int32 a32, opus_int32 b32){ ops_count += 32; return a32 / b32; } #undef silk_SAT8 static OPUS_INLINE opus_int8 silk_SAT8(opus_int64 a){ opus_int8 tmp; ops_count += 1; tmp = (opus_int8)((a) > silk_int8_MAX ? silk_int8_MAX : \ ((a) < silk_int8_MIN ? silk_int8_MIN : (a))); return(tmp); } #undef silk_SAT16 static OPUS_INLINE opus_int16 silk_SAT16(opus_int64 a){ opus_int16 tmp; ops_count += 1; tmp = (opus_int16)((a) > silk_int16_MAX ? silk_int16_MAX : \ ((a) < silk_int16_MIN ? silk_int16_MIN : (a))); return(tmp); } #undef silk_SAT32 static OPUS_INLINE opus_int32 silk_SAT32(opus_int64 a){ opus_int32 tmp; ops_count += 1; tmp = (opus_int32)((a) > silk_int32_MAX ? silk_int32_MAX : \ ((a) < silk_int32_MIN ? silk_int32_MIN : (a))); return(tmp); } #undef silk_POS_SAT32 static OPUS_INLINE opus_int32 silk_POS_SAT32(opus_int64 a){ opus_int32 tmp; ops_count += 1; tmp = (opus_int32)((a) > silk_int32_MAX ? silk_int32_MAX : (a)); return(tmp); } #undef silk_ADD_POS_SAT8 static OPUS_INLINE opus_int8 silk_ADD_POS_SAT8(opus_int64 a, opus_int64 b){ opus_int8 tmp; ops_count += 1; tmp = (opus_int8)((((a)+(b)) & 0x80) ? silk_int8_MAX : ((a)+(b))); return(tmp); } #undef silk_ADD_POS_SAT16 static OPUS_INLINE opus_int16 silk_ADD_POS_SAT16(opus_int64 a, opus_int64 b){ opus_int16 tmp; ops_count += 1; tmp = (opus_int16)((((a)+(b)) & 0x8000) ? silk_int16_MAX : ((a)+(b))); return(tmp); } #undef silk_ADD_POS_SAT32 static OPUS_INLINE opus_int32 silk_ADD_POS_SAT32(opus_int64 a, opus_int64 b){ opus_int32 tmp; ops_count += 1; tmp = (opus_int32)((((a)+(b)) & 0x80000000) ? silk_int32_MAX : ((a)+(b))); return(tmp); } #undef silk_ADD_POS_SAT64 static OPUS_INLINE opus_int64 silk_ADD_POS_SAT64(opus_int64 a, opus_int64 b){ opus_int64 tmp; ops_count += 1; tmp = ((((a)+(b)) & 0x8000000000000000LL) ? silk_int64_MAX : ((a)+(b))); return(tmp); } #undef silk_LSHIFT8 static OPUS_INLINE opus_int8 silk_LSHIFT8(opus_int8 a, opus_int32 shift){ opus_int8 ret; ops_count += 1; ret = a << shift; return ret; } #undef silk_LSHIFT16 static OPUS_INLINE opus_int16 silk_LSHIFT16(opus_int16 a, opus_int32 shift){ opus_int16 ret; ops_count += 1; ret = a << shift; return ret; } #undef silk_LSHIFT32 static OPUS_INLINE opus_int32 silk_LSHIFT32(opus_int32 a, opus_int32 shift){ opus_int32 ret; ops_count += 1; ret = a << shift; return ret; } #undef silk_LSHIFT64 static OPUS_INLINE opus_int64 silk_LSHIFT64(opus_int64 a, opus_int shift){ ops_count += 1; return a << shift; } #undef silk_LSHIFT_ovflw static OPUS_INLINE opus_int32 silk_LSHIFT_ovflw(opus_int32 a, opus_int32 shift){ ops_count += 1; return a << shift; } #undef silk_LSHIFT_uint static OPUS_INLINE opus_uint32 silk_LSHIFT_uint(opus_uint32 a, opus_int32 shift){ opus_uint32 ret; ops_count += 1; ret = a << shift; return ret; } #undef silk_RSHIFT8 static OPUS_INLINE opus_int8 silk_RSHIFT8(opus_int8 a, opus_int32 shift){ ops_count += 1; return a >> shift; } #undef silk_RSHIFT16 static OPUS_INLINE opus_int16 silk_RSHIFT16(opus_int16 a, opus_int32 shift){ ops_count += 1; return a >> shift; } #undef silk_RSHIFT32 static OPUS_INLINE opus_int32 silk_RSHIFT32(opus_int32 a, opus_int32 shift){ ops_count += 1; return a >> shift; } #undef silk_RSHIFT64 static OPUS_INLINE opus_int64 silk_RSHIFT64(opus_int64 a, opus_int64 shift){ ops_count += 1; return a >> shift; } #undef silk_RSHIFT_uint static OPUS_INLINE opus_uint32 silk_RSHIFT_uint(opus_uint32 a, opus_int32 shift){ ops_count += 1; return a >> shift; } #undef silk_ADD_LSHIFT static OPUS_INLINE opus_int32 silk_ADD_LSHIFT(opus_int32 a, opus_int32 b, opus_int32 shift){ opus_int32 ret; ops_count += 1; ret = a + (b << shift); return ret; /* shift >= 0*/ } #undef silk_ADD_LSHIFT32 static OPUS_INLINE opus_int32 silk_ADD_LSHIFT32(opus_int32 a, opus_int32 b, opus_int32 shift){ opus_int32 ret; ops_count += 1; ret = a + (b << shift); return ret; /* shift >= 0*/ } #undef silk_ADD_LSHIFT_uint static OPUS_INLINE opus_uint32 silk_ADD_LSHIFT_uint(opus_uint32 a, opus_uint32 b, opus_int32 shift){ opus_uint32 ret; ops_count += 1; ret = a + (b << shift); return ret; /* shift >= 0*/ } #undef silk_ADD_RSHIFT static OPUS_INLINE opus_int32 silk_ADD_RSHIFT(opus_int32 a, opus_int32 b, opus_int32 shift){ opus_int32 ret; ops_count += 1; ret = a + (b >> shift); return ret; /* shift > 0*/ } #undef silk_ADD_RSHIFT32 static OPUS_INLINE opus_int32 silk_ADD_RSHIFT32(opus_int32 a, opus_int32 b, opus_int32 shift){ opus_int32 ret; ops_count += 1; ret = a + (b >> shift); return ret; /* shift > 0*/ } #undef silk_ADD_RSHIFT_uint static OPUS_INLINE opus_uint32 silk_ADD_RSHIFT_uint(opus_uint32 a, opus_uint32 b, opus_int32 shift){ opus_uint32 ret; ops_count += 1; ret = a + (b >> shift); return ret; /* shift > 0*/ } #undef silk_SUB_LSHIFT32 static OPUS_INLINE opus_int32 silk_SUB_LSHIFT32(opus_int32 a, opus_int32 b, opus_int32 shift){ opus_int32 ret; ops_count += 1; ret = a - (b << shift); return ret; /* shift >= 0*/ } #undef silk_SUB_RSHIFT32 static OPUS_INLINE opus_int32 silk_SUB_RSHIFT32(opus_int32 a, opus_int32 b, opus_int32 shift){ opus_int32 ret; ops_count += 1; ret = a - (b >> shift); return ret; /* shift > 0*/ } #undef silk_RSHIFT_ROUND static OPUS_INLINE opus_int32 silk_RSHIFT_ROUND(opus_int32 a, opus_int32 shift){ opus_int32 ret; ops_count += 3; ret = shift == 1 ? (a >> 1) + (a & 1) : ((a >> (shift - 1)) + 1) >> 1; return ret; } #undef silk_RSHIFT_ROUND64 static OPUS_INLINE opus_int64 silk_RSHIFT_ROUND64(opus_int64 a, opus_int32 shift){ opus_int64 ret; ops_count += 6; ret = shift == 1 ? (a >> 1) + (a & 1) : ((a >> (shift - 1)) + 1) >> 1; return ret; } #undef silk_abs_int64 static OPUS_INLINE opus_int64 silk_abs_int64(opus_int64 a){ ops_count += 1; return (((a) > 0) ? (a) : -(a)); /* Be careful, silk_abs returns wrong when input equals to silk_intXX_MIN*/ } #undef silk_abs_int32 static OPUS_INLINE opus_int32 silk_abs_int32(opus_int32 a){ ops_count += 1; return silk_abs(a); } #undef silk_min static silk_min(a, b){ ops_count += 1; return (((a) < (b)) ? (a) : (b)); } #undef silk_max static silk_max(a, b){ ops_count += 1; return (((a) > (b)) ? (a) : (b)); } #undef silk_sign static silk_sign(a){ ops_count += 1; return ((a) > 0 ? 1 : ( (a) < 0 ? -1 : 0 )); } #undef silk_ADD16 static OPUS_INLINE opus_int16 silk_ADD16(opus_int16 a, opus_int16 b){ opus_int16 ret; ops_count += 1; ret = a + b; return ret; } #undef silk_ADD32 static OPUS_INLINE opus_int32 silk_ADD32(opus_int32 a, opus_int32 b){ opus_int32 ret; ops_count += 1; ret = a + b; return ret; } #undef silk_ADD64 static OPUS_INLINE opus_int64 silk_ADD64(opus_int64 a, opus_int64 b){ opus_int64 ret; ops_count += 2; ret = a + b; return ret; } #undef silk_SUB16 static OPUS_INLINE opus_int16 silk_SUB16(opus_int16 a, opus_int16 b){ opus_int16 ret; ops_count += 1; ret = a - b; return ret; } #undef silk_SUB32 static OPUS_INLINE opus_int32 silk_SUB32(opus_int32 a, opus_int32 b){ opus_int32 ret; ops_count += 1; ret = a - b; return ret; } #undef silk_SUB64 static OPUS_INLINE opus_int64 silk_SUB64(opus_int64 a, opus_int64 b){ opus_int64 ret; ops_count += 2; ret = a - b; return ret; } #undef silk_ADD_SAT16 static OPUS_INLINE opus_int16 silk_ADD_SAT16( opus_int16 a16, opus_int16 b16 ) { opus_int16 res; /* Nb will be counted in AKP_add32 and silk_SAT16*/ res = (opus_int16)silk_SAT16( silk_ADD32( (opus_int32)(a16), (b16) ) ); return res; } #undef silk_ADD_SAT32 static OPUS_INLINE opus_int32 silk_ADD_SAT32(opus_int32 a32, opus_int32 b32){ opus_int32 res; ops_count += 1; res = ((((a32) + (b32)) & 0x80000000) == 0 ? \ ((((a32) & (b32)) & 0x80000000) != 0 ? silk_int32_MIN : (a32)+(b32)) : \ ((((a32) | (b32)) & 0x80000000) == 0 ? silk_int32_MAX : (a32)+(b32)) ); return res; } #undef silk_ADD_SAT64 static OPUS_INLINE opus_int64 silk_ADD_SAT64( opus_int64 a64, opus_int64 b64 ) { opus_int64 res; ops_count += 1; res = ((((a64) + (b64)) & 0x8000000000000000LL) == 0 ? \ ((((a64) & (b64)) & 0x8000000000000000LL) != 0 ? silk_int64_MIN : (a64)+(b64)) : \ ((((a64) | (b64)) & 0x8000000000000000LL) == 0 ? silk_int64_MAX : (a64)+(b64)) ); return res; } #undef silk_SUB_SAT16 static OPUS_INLINE opus_int16 silk_SUB_SAT16( opus_int16 a16, opus_int16 b16 ) { opus_int16 res; silk_assert(0); /* Nb will be counted in sub-macros*/ res = (opus_int16)silk_SAT16( silk_SUB32( (opus_int32)(a16), (b16) ) ); return res; } #undef silk_SUB_SAT32 static OPUS_INLINE opus_int32 silk_SUB_SAT32( opus_int32 a32, opus_int32 b32 ) { opus_int32 res; ops_count += 1; res = ((((a32)-(b32)) & 0x80000000) == 0 ? \ (( (a32) & ((b32)^0x80000000) & 0x80000000) ? silk_int32_MIN : (a32)-(b32)) : \ ((((a32)^0x80000000) & (b32) & 0x80000000) ? silk_int32_MAX : (a32)-(b32)) ); return res; } #undef silk_SUB_SAT64 static OPUS_INLINE opus_int64 silk_SUB_SAT64( opus_int64 a64, opus_int64 b64 ) { opus_int64 res; ops_count += 1; res = ((((a64)-(b64)) & 0x8000000000000000LL) == 0 ? \ (( (a64) & ((b64)^0x8000000000000000LL) & 0x8000000000000000LL) ? silk_int64_MIN : (a64)-(b64)) : \ ((((a64)^0x8000000000000000LL) & (b64) & 0x8000000000000000LL) ? silk_int64_MAX : (a64)-(b64)) ); return res; } #undef silk_SMULWW static OPUS_INLINE opus_int32 silk_SMULWW(opus_int32 a32, opus_int32 b32){ opus_int32 ret; /* Nb will be counted in sub-macros*/ ret = silk_MLA(silk_SMULWB((a32), (b32)), (a32), silk_RSHIFT_ROUND((b32), 16)); return ret; } #undef silk_SMLAWW static OPUS_INLINE opus_int32 silk_SMLAWW(opus_int32 a32, opus_int32 b32, opus_int32 c32){ opus_int32 ret; /* Nb will be counted in sub-macros*/ ret = silk_MLA(silk_SMLAWB((a32), (b32), (c32)), (b32), silk_RSHIFT_ROUND((c32), 16)); return ret; } #undef silk_min_int static OPUS_INLINE opus_int silk_min_int(opus_int a, opus_int b) { ops_count += 1; return (((a) < (b)) ? (a) : (b)); } #undef silk_min_16 static OPUS_INLINE opus_int16 silk_min_16(opus_int16 a, opus_int16 b) { ops_count += 1; return (((a) < (b)) ? (a) : (b)); } #undef silk_min_32 static OPUS_INLINE opus_int32 silk_min_32(opus_int32 a, opus_int32 b) { ops_count += 1; return (((a) < (b)) ? (a) : (b)); } #undef silk_min_64 static OPUS_INLINE opus_int64 silk_min_64(opus_int64 a, opus_int64 b) { ops_count += 1; return (((a) < (b)) ? (a) : (b)); } /* silk_min() versions with typecast in the function call */ #undef silk_max_int static OPUS_INLINE opus_int silk_max_int(opus_int a, opus_int b) { ops_count += 1; return (((a) > (b)) ? (a) : (b)); } #undef silk_max_16 static OPUS_INLINE opus_int16 silk_max_16(opus_int16 a, opus_int16 b) { ops_count += 1; return (((a) > (b)) ? (a) : (b)); } #undef silk_max_32 static OPUS_INLINE opus_int32 silk_max_32(opus_int32 a, opus_int32 b) { ops_count += 1; return (((a) > (b)) ? (a) : (b)); } #undef silk_max_64 static OPUS_INLINE opus_int64 silk_max_64(opus_int64 a, opus_int64 b) { ops_count += 1; return (((a) > (b)) ? (a) : (b)); } #undef silk_LIMIT_int static OPUS_INLINE opus_int silk_LIMIT_int(opus_int a, opus_int limit1, opus_int limit2) { opus_int ret; ops_count += 6; ret = ((limit1) > (limit2) ? ((a) > (limit1) ? (limit1) : ((a) < (limit2) ? (limit2) : (a))) \ : ((a) > (limit2) ? (limit2) : ((a) < (limit1) ? (limit1) : (a)))); return(ret); } #undef silk_LIMIT_16 static OPUS_INLINE opus_int16 silk_LIMIT_16(opus_int16 a, opus_int16 limit1, opus_int16 limit2) { opus_int16 ret; ops_count += 6; ret = ((limit1) > (limit2) ? ((a) > (limit1) ? (limit1) : ((a) < (limit2) ? (limit2) : (a))) \ : ((a) > (limit2) ? (limit2) : ((a) < (limit1) ? (limit1) : (a)))); return(ret); } #undef silk_LIMIT_32 static OPUS_INLINE opus_int silk_LIMIT_32(opus_int32 a, opus_int32 limit1, opus_int32 limit2) { opus_int32 ret; ops_count += 6; ret = ((limit1) > (limit2) ? ((a) > (limit1) ? (limit1) : ((a) < (limit2) ? (limit2) : (a))) \ : ((a) > (limit2) ? (limit2) : ((a) < (limit1) ? (limit1) : (a)))); return(ret); } #else #define varDefine #define silk_SaveCount() #endif #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/MacroDebug.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Copyright (C) 2012 Xiph.Org Foundation Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef MACRO_DEBUG_H #define MACRO_DEBUG_H /* Redefine macro functions with extensive assertion in DEBUG mode. As functions can't be undefined, this file can't work with SigProcFIX_MacroCount.h */ #if ( defined (FIXED_DEBUG) || ( 0 && defined (_DEBUG) ) ) && !defined (silk_MACRO_COUNT) #undef silk_ADD16 #define silk_ADD16(a,b) silk_ADD16_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int16 silk_ADD16_(opus_int16 a, opus_int16 b, char *file, int line){ opus_int16 ret; ret = a + b; if ( ret != silk_ADD_SAT16( a, b ) ) { fprintf (stderr, "silk_ADD16(%d, %d) in %s: line %d\n", a, b, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_ADD32 #define silk_ADD32(a,b) silk_ADD32_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_ADD32_(opus_int32 a, opus_int32 b, char *file, int line){ opus_int32 ret; ret = a + b; if ( ret != silk_ADD_SAT32( a, b ) ) { fprintf (stderr, "silk_ADD32(%d, %d) in %s: line %d\n", a, b, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_ADD64 #define silk_ADD64(a,b) silk_ADD64_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int64 silk_ADD64_(opus_int64 a, opus_int64 b, char *file, int line){ opus_int64 ret; ret = a + b; if ( ret != silk_ADD_SAT64( a, b ) ) { fprintf (stderr, "silk_ADD64(%lld, %lld) in %s: line %d\n", (long long)a, (long long)b, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_SUB16 #define silk_SUB16(a,b) silk_SUB16_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int16 silk_SUB16_(opus_int16 a, opus_int16 b, char *file, int line){ opus_int16 ret; ret = a - b; if ( ret != silk_SUB_SAT16( a, b ) ) { fprintf (stderr, "silk_SUB16(%d, %d) in %s: line %d\n", a, b, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_SUB32 #define silk_SUB32(a,b) silk_SUB32_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_SUB32_(opus_int32 a, opus_int32 b, char *file, int line){ opus_int32 ret; ret = a - b; if ( ret != silk_SUB_SAT32( a, b ) ) { fprintf (stderr, "silk_SUB32(%d, %d) in %s: line %d\n", a, b, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_SUB64 #define silk_SUB64(a,b) silk_SUB64_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int64 silk_SUB64_(opus_int64 a, opus_int64 b, char *file, int line){ opus_int64 ret; ret = a - b; if ( ret != silk_SUB_SAT64( a, b ) ) { fprintf (stderr, "silk_SUB64(%lld, %lld) in %s: line %d\n", (long long)a, (long long)b, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_ADD_SAT16 #define silk_ADD_SAT16(a,b) silk_ADD_SAT16_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int16 silk_ADD_SAT16_( opus_int16 a16, opus_int16 b16, char *file, int line) { opus_int16 res; res = (opus_int16)silk_SAT16( silk_ADD32( (opus_int32)(a16), (b16) ) ); if ( res != silk_SAT16( (opus_int32)a16 + (opus_int32)b16 ) ) { fprintf (stderr, "silk_ADD_SAT16(%d, %d) in %s: line %d\n", a16, b16, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return res; } #undef silk_ADD_SAT32 #define silk_ADD_SAT32(a,b) silk_ADD_SAT32_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_ADD_SAT32_(opus_int32 a32, opus_int32 b32, char *file, int line){ opus_int32 res; res = ((((opus_uint32)(a32) + (opus_uint32)(b32)) & 0x80000000) == 0 ? \ ((((a32) & (b32)) & 0x80000000) != 0 ? silk_int32_MIN : (a32)+(b32)) : \ ((((a32) | (b32)) & 0x80000000) == 0 ? silk_int32_MAX : (a32)+(b32)) ); if ( res != silk_SAT32( (opus_int64)a32 + (opus_int64)b32 ) ) { fprintf (stderr, "silk_ADD_SAT32(%d, %d) in %s: line %d\n", a32, b32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return res; } #undef silk_ADD_SAT64 #define silk_ADD_SAT64(a,b) silk_ADD_SAT64_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int64 silk_ADD_SAT64_( opus_int64 a64, opus_int64 b64, char *file, int line) { opus_int64 res; int fail = 0; res = ((((a64) + (b64)) & 0x8000000000000000LL) == 0 ? \ ((((a64) & (b64)) & 0x8000000000000000LL) != 0 ? silk_int64_MIN : (a64)+(b64)) : \ ((((a64) | (b64)) & 0x8000000000000000LL) == 0 ? silk_int64_MAX : (a64)+(b64)) ); if( res != a64 + b64 ) { /* Check that we saturated to the correct extreme value */ if ( !(( res == silk_int64_MAX && ( ( a64 >> 1 ) + ( b64 >> 1 ) > ( silk_int64_MAX >> 3 ) ) ) || ( res == silk_int64_MIN && ( ( a64 >> 1 ) + ( b64 >> 1 ) < ( silk_int64_MIN >> 3 ) ) ) ) ) { fail = 1; } } else { /* Saturation not necessary */ fail = res != a64 + b64; } if ( fail ) { fprintf (stderr, "silk_ADD_SAT64(%lld, %lld) in %s: line %d\n", (long long)a64, (long long)b64, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return res; } #undef silk_SUB_SAT16 #define silk_SUB_SAT16(a,b) silk_SUB_SAT16_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int16 silk_SUB_SAT16_( opus_int16 a16, opus_int16 b16, char *file, int line ) { opus_int16 res; res = (opus_int16)silk_SAT16( silk_SUB32( (opus_int32)(a16), (b16) ) ); if ( res != silk_SAT16( (opus_int32)a16 - (opus_int32)b16 ) ) { fprintf (stderr, "silk_SUB_SAT16(%d, %d) in %s: line %d\n", a16, b16, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return res; } #undef silk_SUB_SAT32 #define silk_SUB_SAT32(a,b) silk_SUB_SAT32_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_SUB_SAT32_( opus_int32 a32, opus_int32 b32, char *file, int line ) { opus_int32 res; res = ((((opus_uint32)(a32)-(opus_uint32)(b32)) & 0x80000000) == 0 ? \ (( (a32) & ((b32)^0x80000000) & 0x80000000) ? silk_int32_MIN : (a32)-(b32)) : \ ((((a32)^0x80000000) & (b32) & 0x80000000) ? silk_int32_MAX : (a32)-(b32)) ); if ( res != silk_SAT32( (opus_int64)a32 - (opus_int64)b32 ) ) { fprintf (stderr, "silk_SUB_SAT32(%d, %d) in %s: line %d\n", a32, b32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return res; } #undef silk_SUB_SAT64 #define silk_SUB_SAT64(a,b) silk_SUB_SAT64_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int64 silk_SUB_SAT64_( opus_int64 a64, opus_int64 b64, char *file, int line ) { opus_int64 res; int fail = 0; res = ((((a64)-(b64)) & 0x8000000000000000LL) == 0 ? \ (( (a64) & ((b64)^0x8000000000000000LL) & 0x8000000000000000LL) ? silk_int64_MIN : (a64)-(b64)) : \ ((((a64)^0x8000000000000000LL) & (b64) & 0x8000000000000000LL) ? silk_int64_MAX : (a64)-(b64)) ); if( res != a64 - b64 ) { /* Check that we saturated to the correct extreme value */ if( !(( res == silk_int64_MAX && ( ( a64 >> 1 ) + ( b64 >> 1 ) > ( silk_int64_MAX >> 3 ) ) ) || ( res == silk_int64_MIN && ( ( a64 >> 1 ) + ( b64 >> 1 ) < ( silk_int64_MIN >> 3 ) ) ) )) { fail = 1; } } else { /* Saturation not necessary */ fail = res != a64 - b64; } if ( fail ) { fprintf (stderr, "silk_SUB_SAT64(%lld, %lld) in %s: line %d\n", (long long)a64, (long long)b64, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return res; } #undef silk_MUL #define silk_MUL(a,b) silk_MUL_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_MUL_(opus_int32 a32, opus_int32 b32, char *file, int line){ opus_int32 ret; opus_int64 ret64; ret = a32 * b32; ret64 = (opus_int64)a32 * (opus_int64)b32; if ( (opus_int64)ret != ret64 ) { fprintf (stderr, "silk_MUL(%d, %d) in %s: line %d\n", a32, b32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_MUL_uint #define silk_MUL_uint(a,b) silk_MUL_uint_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_uint32 silk_MUL_uint_(opus_uint32 a32, opus_uint32 b32, char *file, int line){ opus_uint32 ret; ret = a32 * b32; if ( (opus_uint64)ret != (opus_uint64)a32 * (opus_uint64)b32 ) { fprintf (stderr, "silk_MUL_uint(%u, %u) in %s: line %d\n", a32, b32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_MLA #define silk_MLA(a,b,c) silk_MLA_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_MLA_(opus_int32 a32, opus_int32 b32, opus_int32 c32, char *file, int line){ opus_int32 ret; ret = a32 + b32 * c32; if ( (opus_int64)ret != (opus_int64)a32 + (opus_int64)b32 * (opus_int64)c32 ) { fprintf (stderr, "silk_MLA(%d, %d, %d) in %s: line %d\n", a32, b32, c32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_MLA_uint #define silk_MLA_uint(a,b,c) silk_MLA_uint_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_MLA_uint_(opus_uint32 a32, opus_uint32 b32, opus_uint32 c32, char *file, int line){ opus_uint32 ret; ret = a32 + b32 * c32; if ( (opus_int64)ret != (opus_int64)a32 + (opus_int64)b32 * (opus_int64)c32 ) { fprintf (stderr, "silk_MLA_uint(%d, %d, %d) in %s: line %d\n", a32, b32, c32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_SMULWB #define silk_SMULWB(a,b) silk_SMULWB_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_SMULWB_(opus_int32 a32, opus_int32 b32, char *file, int line){ opus_int32 ret; ret = (a32 >> 16) * (opus_int32)((opus_int16)b32) + (((a32 & 0x0000FFFF) * (opus_int32)((opus_int16)b32)) >> 16); if ( (opus_int64)ret != ((opus_int64)a32 * (opus_int16)b32) >> 16 ) { fprintf (stderr, "silk_SMULWB(%d, %d) in %s: line %d\n", a32, b32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_SMLAWB #define silk_SMLAWB(a,b,c) silk_SMLAWB_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_SMLAWB_(opus_int32 a32, opus_int32 b32, opus_int32 c32, char *file, int line){ opus_int32 ret; ret = silk_ADD32( a32, silk_SMULWB( b32, c32 ) ); if ( silk_ADD32( a32, silk_SMULWB( b32, c32 ) ) != silk_ADD_SAT32( a32, silk_SMULWB( b32, c32 ) ) ) { fprintf (stderr, "silk_SMLAWB(%d, %d, %d) in %s: line %d\n", a32, b32, c32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_SMULWT #define silk_SMULWT(a,b) silk_SMULWT_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_SMULWT_(opus_int32 a32, opus_int32 b32, char *file, int line){ opus_int32 ret; ret = (a32 >> 16) * (b32 >> 16) + (((a32 & 0x0000FFFF) * (b32 >> 16)) >> 16); if ( (opus_int64)ret != ((opus_int64)a32 * (b32 >> 16)) >> 16 ) { fprintf (stderr, "silk_SMULWT(%d, %d) in %s: line %d\n", a32, b32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_SMLAWT #define silk_SMLAWT(a,b,c) silk_SMLAWT_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_SMLAWT_(opus_int32 a32, opus_int32 b32, opus_int32 c32, char *file, int line){ opus_int32 ret; ret = a32 + ((b32 >> 16) * (c32 >> 16)) + (((b32 & 0x0000FFFF) * ((c32 >> 16)) >> 16)); if ( (opus_int64)ret != (opus_int64)a32 + (((opus_int64)b32 * (c32 >> 16)) >> 16) ) { fprintf (stderr, "silk_SMLAWT(%d, %d, %d) in %s: line %d\n", a32, b32, c32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_SMULL #define silk_SMULL(a,b) silk_SMULL_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int64 silk_SMULL_(opus_int64 a64, opus_int64 b64, char *file, int line){ opus_int64 ret64; int fail = 0; ret64 = a64 * b64; if( b64 != 0 ) { fail = a64 != (ret64 / b64); } else if( a64 != 0 ) { fail = b64 != (ret64 / a64); } if ( fail ) { fprintf (stderr, "silk_SMULL(%lld, %lld) in %s: line %d\n", (long long)a64, (long long)b64, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret64; } /* no checking needed for silk_SMULBB */ #undef silk_SMLABB #define silk_SMLABB(a,b,c) silk_SMLABB_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_SMLABB_(opus_int32 a32, opus_int32 b32, opus_int32 c32, char *file, int line){ opus_int32 ret; ret = a32 + (opus_int32)((opus_int16)b32) * (opus_int32)((opus_int16)c32); if ( (opus_int64)ret != (opus_int64)a32 + (opus_int64)b32 * (opus_int16)c32 ) { fprintf (stderr, "silk_SMLABB(%d, %d, %d) in %s: line %d\n", a32, b32, c32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } /* no checking needed for silk_SMULBT */ #undef silk_SMLABT #define silk_SMLABT(a,b,c) silk_SMLABT_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_SMLABT_(opus_int32 a32, opus_int32 b32, opus_int32 c32, char *file, int line){ opus_int32 ret; ret = a32 + ((opus_int32)((opus_int16)b32)) * (c32 >> 16); if ( (opus_int64)ret != (opus_int64)a32 + (opus_int64)b32 * (c32 >> 16) ) { fprintf (stderr, "silk_SMLABT(%d, %d, %d) in %s: line %d\n", a32, b32, c32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } /* no checking needed for silk_SMULTT */ #undef silk_SMLATT #define silk_SMLATT(a,b,c) silk_SMLATT_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_SMLATT_(opus_int32 a32, opus_int32 b32, opus_int32 c32, char *file, int line){ opus_int32 ret; ret = a32 + (b32 >> 16) * (c32 >> 16); if ( (opus_int64)ret != (opus_int64)a32 + (b32 >> 16) * (c32 >> 16) ) { fprintf (stderr, "silk_SMLATT(%d, %d, %d) in %s: line %d\n", a32, b32, c32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_SMULWW #define silk_SMULWW(a,b) silk_SMULWW_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_SMULWW_(opus_int32 a32, opus_int32 b32, char *file, int line){ opus_int32 ret, tmp1, tmp2; opus_int64 ret64; int fail = 0; ret = silk_SMULWB( a32, b32 ); tmp1 = silk_RSHIFT_ROUND( b32, 16 ); tmp2 = silk_MUL( a32, tmp1 ); fail |= (opus_int64)tmp2 != (opus_int64) a32 * (opus_int64) tmp1; tmp1 = ret; ret = silk_ADD32( tmp1, tmp2 ); fail |= silk_ADD32( tmp1, tmp2 ) != silk_ADD_SAT32( tmp1, tmp2 ); ret64 = silk_RSHIFT64( silk_SMULL( a32, b32 ), 16 ); fail |= (opus_int64)ret != ret64; if ( fail ) { fprintf (stderr, "silk_SMULWT(%d, %d) in %s: line %d\n", a32, b32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_SMLAWW #define silk_SMLAWW(a,b,c) silk_SMLAWW_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_SMLAWW_(opus_int32 a32, opus_int32 b32, opus_int32 c32, char *file, int line){ opus_int32 ret, tmp; tmp = silk_SMULWW( b32, c32 ); ret = silk_ADD32( a32, tmp ); if ( ret != silk_ADD_SAT32( a32, tmp ) ) { fprintf (stderr, "silk_SMLAWW(%d, %d, %d) in %s: line %d\n", a32, b32, c32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } /* Multiply-accumulate macros that allow overflow in the addition (ie, no asserts in debug mode) */ #undef silk_MLA_ovflw #define silk_MLA_ovflw(a32, b32, c32) ((a32) + ((b32) * (c32))) #undef silk_SMLABB_ovflw #define silk_SMLABB_ovflw(a32, b32, c32) ((a32) + ((opus_int32)((opus_int16)(b32))) * (opus_int32)((opus_int16)(c32))) /* no checking needed for silk_SMULL no checking needed for silk_SMLAL no checking needed for silk_SMLALBB no checking needed for SigProcFIX_CLZ16 no checking needed for SigProcFIX_CLZ32*/ #undef silk_DIV32 #define silk_DIV32(a,b) silk_DIV32_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_DIV32_(opus_int32 a32, opus_int32 b32, char *file, int line){ if ( b32 == 0 ) { fprintf (stderr, "silk_DIV32(%d, %d) in %s: line %d\n", a32, b32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return a32 / b32; } #undef silk_DIV32_16 #define silk_DIV32_16(a,b) silk_DIV32_16_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_DIV32_16_(opus_int32 a32, opus_int32 b32, char *file, int line){ int fail = 0; fail |= b32 == 0; fail |= b32 > silk_int16_MAX; fail |= b32 < silk_int16_MIN; if ( fail ) { fprintf (stderr, "silk_DIV32_16(%d, %d) in %s: line %d\n", a32, b32, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return a32 / b32; } /* no checking needed for silk_SAT8 no checking needed for silk_SAT16 no checking needed for silk_SAT32 no checking needed for silk_POS_SAT32 no checking needed for silk_ADD_POS_SAT8 no checking needed for silk_ADD_POS_SAT16 no checking needed for silk_ADD_POS_SAT32 no checking needed for silk_ADD_POS_SAT64 */ #undef silk_LSHIFT8 #define silk_LSHIFT8(a,b) silk_LSHIFT8_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int8 silk_LSHIFT8_(opus_int8 a, opus_int32 shift, char *file, int line){ opus_int8 ret; int fail = 0; ret = a << shift; fail |= shift < 0; fail |= shift >= 8; fail |= (opus_int64)ret != ((opus_int64)a) << shift; if ( fail ) { fprintf (stderr, "silk_LSHIFT8(%d, %d) in %s: line %d\n", a, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_LSHIFT16 #define silk_LSHIFT16(a,b) silk_LSHIFT16_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int16 silk_LSHIFT16_(opus_int16 a, opus_int32 shift, char *file, int line){ opus_int16 ret; int fail = 0; ret = a << shift; fail |= shift < 0; fail |= shift >= 16; fail |= (opus_int64)ret != ((opus_int64)a) << shift; if ( fail ) { fprintf (stderr, "silk_LSHIFT16(%d, %d) in %s: line %d\n", a, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_LSHIFT32 #define silk_LSHIFT32(a,b) silk_LSHIFT32_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_LSHIFT32_(opus_int32 a, opus_int32 shift, char *file, int line){ opus_int32 ret; int fail = 0; ret = a << shift; fail |= shift < 0; fail |= shift >= 32; fail |= (opus_int64)ret != ((opus_int64)a) << shift; if ( fail ) { fprintf (stderr, "silk_LSHIFT32(%d, %d) in %s: line %d\n", a, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_LSHIFT64 #define silk_LSHIFT64(a,b) silk_LSHIFT64_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int64 silk_LSHIFT64_(opus_int64 a, opus_int shift, char *file, int line){ opus_int64 ret; int fail = 0; ret = a << shift; fail |= shift < 0; fail |= shift >= 64; fail |= (ret>>shift) != ((opus_int64)a); if ( fail ) { fprintf (stderr, "silk_LSHIFT64(%lld, %d) in %s: line %d\n", (long long)a, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_LSHIFT_ovflw #define silk_LSHIFT_ovflw(a,b) silk_LSHIFT_ovflw_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_LSHIFT_ovflw_(opus_int32 a, opus_int32 shift, char *file, int line){ if ( (shift < 0) || (shift >= 32) ) /* no check for overflow */ { fprintf (stderr, "silk_LSHIFT_ovflw(%d, %d) in %s: line %d\n", a, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return a << shift; } #undef silk_LSHIFT_uint #define silk_LSHIFT_uint(a,b) silk_LSHIFT_uint_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_uint32 silk_LSHIFT_uint_(opus_uint32 a, opus_int32 shift, char *file, int line){ opus_uint32 ret; ret = a << shift; if ( (shift < 0) || ((opus_int64)ret != ((opus_int64)a) << shift)) { fprintf (stderr, "silk_LSHIFT_uint(%u, %d) in %s: line %d\n", a, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_RSHIFT8 #define silk_RSHITF8(a,b) silk_RSHIFT8_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int8 silk_RSHIFT8_(opus_int8 a, opus_int32 shift, char *file, int line){ if ( (shift < 0) || (shift>=8) ) { fprintf (stderr, "silk_RSHITF8(%d, %d) in %s: line %d\n", a, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return a >> shift; } #undef silk_RSHIFT16 #define silk_RSHITF16(a,b) silk_RSHIFT16_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int16 silk_RSHIFT16_(opus_int16 a, opus_int32 shift, char *file, int line){ if ( (shift < 0) || (shift>=16) ) { fprintf (stderr, "silk_RSHITF16(%d, %d) in %s: line %d\n", a, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return a >> shift; } #undef silk_RSHIFT32 #define silk_RSHIFT32(a,b) silk_RSHIFT32_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_RSHIFT32_(opus_int32 a, opus_int32 shift, char *file, int line){ if ( (shift < 0) || (shift>=32) ) { fprintf (stderr, "silk_RSHITF32(%d, %d) in %s: line %d\n", a, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return a >> shift; } #undef silk_RSHIFT64 #define silk_RSHIFT64(a,b) silk_RSHIFT64_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int64 silk_RSHIFT64_(opus_int64 a, opus_int64 shift, char *file, int line){ if ( (shift < 0) || (shift>=64) ) { fprintf (stderr, "silk_RSHITF64(%lld, %lld) in %s: line %d\n", (long long)a, (long long)shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return a >> shift; } #undef silk_RSHIFT_uint #define silk_RSHIFT_uint(a,b) silk_RSHIFT_uint_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_uint32 silk_RSHIFT_uint_(opus_uint32 a, opus_int32 shift, char *file, int line){ if ( (shift < 0) || (shift>32) ) { fprintf (stderr, "silk_RSHIFT_uint(%u, %d) in %s: line %d\n", a, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return a >> shift; } #undef silk_ADD_LSHIFT #define silk_ADD_LSHIFT(a,b,c) silk_ADD_LSHIFT_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE int silk_ADD_LSHIFT_(int a, int b, int shift, char *file, int line){ opus_int16 ret; ret = a + (b << shift); if ( (shift < 0) || (shift>15) || ((opus_int64)ret != (opus_int64)a + (((opus_int64)b) << shift)) ) { fprintf (stderr, "silk_ADD_LSHIFT(%d, %d, %d) in %s: line %d\n", a, b, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; /* shift >= 0 */ } #undef silk_ADD_LSHIFT32 #define silk_ADD_LSHIFT32(a,b,c) silk_ADD_LSHIFT32_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_ADD_LSHIFT32_(opus_int32 a, opus_int32 b, opus_int32 shift, char *file, int line){ opus_int32 ret; ret = a + (b << shift); if ( (shift < 0) || (shift>31) || ((opus_int64)ret != (opus_int64)a + (((opus_int64)b) << shift)) ) { fprintf (stderr, "silk_ADD_LSHIFT32(%d, %d, %d) in %s: line %d\n", a, b, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; /* shift >= 0 */ } #undef silk_ADD_LSHIFT_uint #define silk_ADD_LSHIFT_uint(a,b,c) silk_ADD_LSHIFT_uint_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE opus_uint32 silk_ADD_LSHIFT_uint_(opus_uint32 a, opus_uint32 b, opus_int32 shift, char *file, int line){ opus_uint32 ret; ret = a + (b << shift); if ( (shift < 0) || (shift>32) || ((opus_int64)ret != (opus_int64)a + (((opus_int64)b) << shift)) ) { fprintf (stderr, "silk_ADD_LSHIFT_uint(%u, %u, %d) in %s: line %d\n", a, b, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; /* shift >= 0 */ } #undef silk_ADD_RSHIFT #define silk_ADD_RSHIFT(a,b,c) silk_ADD_RSHIFT_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE int silk_ADD_RSHIFT_(int a, int b, int shift, char *file, int line){ opus_int16 ret; ret = a + (b >> shift); if ( (shift < 0) || (shift>15) || ((opus_int64)ret != (opus_int64)a + (((opus_int64)b) >> shift)) ) { fprintf (stderr, "silk_ADD_RSHIFT(%d, %d, %d) in %s: line %d\n", a, b, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; /* shift > 0 */ } #undef silk_ADD_RSHIFT32 #define silk_ADD_RSHIFT32(a,b,c) silk_ADD_RSHIFT32_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_ADD_RSHIFT32_(opus_int32 a, opus_int32 b, opus_int32 shift, char *file, int line){ opus_int32 ret; ret = a + (b >> shift); if ( (shift < 0) || (shift>31) || ((opus_int64)ret != (opus_int64)a + (((opus_int64)b) >> shift)) ) { fprintf (stderr, "silk_ADD_RSHIFT32(%d, %d, %d) in %s: line %d\n", a, b, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; /* shift > 0 */ } #undef silk_ADD_RSHIFT_uint #define silk_ADD_RSHIFT_uint(a,b,c) silk_ADD_RSHIFT_uint_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE opus_uint32 silk_ADD_RSHIFT_uint_(opus_uint32 a, opus_uint32 b, opus_int32 shift, char *file, int line){ opus_uint32 ret; ret = a + (b >> shift); if ( (shift < 0) || (shift>32) || ((opus_int64)ret != (opus_int64)a + (((opus_int64)b) >> shift)) ) { fprintf (stderr, "silk_ADD_RSHIFT_uint(%u, %u, %d) in %s: line %d\n", a, b, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; /* shift > 0 */ } #undef silk_SUB_LSHIFT32 #define silk_SUB_LSHIFT32(a,b,c) silk_SUB_LSHIFT32_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_SUB_LSHIFT32_(opus_int32 a, opus_int32 b, opus_int32 shift, char *file, int line){ opus_int32 ret; ret = a - (b << shift); if ( (shift < 0) || (shift>31) || ((opus_int64)ret != (opus_int64)a - (((opus_int64)b) << shift)) ) { fprintf (stderr, "silk_SUB_LSHIFT32(%d, %d, %d) in %s: line %d\n", a, b, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; /* shift >= 0 */ } #undef silk_SUB_RSHIFT32 #define silk_SUB_RSHIFT32(a,b,c) silk_SUB_RSHIFT32_((a), (b), (c), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_SUB_RSHIFT32_(opus_int32 a, opus_int32 b, opus_int32 shift, char *file, int line){ opus_int32 ret; ret = a - (b >> shift); if ( (shift < 0) || (shift>31) || ((opus_int64)ret != (opus_int64)a - (((opus_int64)b) >> shift)) ) { fprintf (stderr, "silk_SUB_RSHIFT32(%d, %d, %d) in %s: line %d\n", a, b, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; /* shift > 0 */ } #undef silk_RSHIFT_ROUND #define silk_RSHIFT_ROUND(a,b) silk_RSHIFT_ROUND_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_RSHIFT_ROUND_(opus_int32 a, opus_int32 shift, char *file, int line){ opus_int32 ret; ret = shift == 1 ? (a >> 1) + (a & 1) : ((a >> (shift - 1)) + 1) >> 1; /* the marco definition can't handle a shift of zero */ if ( (shift <= 0) || (shift>31) || ((opus_int64)ret != ((opus_int64)a + ((opus_int64)1 << (shift - 1))) >> shift) ) { fprintf (stderr, "silk_RSHIFT_ROUND(%d, %d) in %s: line %d\n", a, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return ret; } #undef silk_RSHIFT_ROUND64 #define silk_RSHIFT_ROUND64(a,b) silk_RSHIFT_ROUND64_((a), (b), __FILE__, __LINE__) static OPUS_INLINE opus_int64 silk_RSHIFT_ROUND64_(opus_int64 a, opus_int32 shift, char *file, int line){ opus_int64 ret; /* the marco definition can't handle a shift of zero */ if ( (shift <= 0) || (shift>=64) ) { fprintf (stderr, "silk_RSHIFT_ROUND64(%lld, %d) in %s: line %d\n", (long long)a, shift, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } ret = shift == 1 ? (a >> 1) + (a & 1) : ((a >> (shift - 1)) + 1) >> 1; return ret; } /* silk_abs is used on floats also, so doesn't work... */ /*#undef silk_abs static OPUS_INLINE opus_int32 silk_abs(opus_int32 a){ silk_assert(a != 0x80000000); return (((a) > 0) ? (a) : -(a)); // Be careful, silk_abs returns wrong when input equals to silk_intXX_MIN }*/ #undef silk_abs_int64 #define silk_abs_int64(a) silk_abs_int64_((a), __FILE__, __LINE__) static OPUS_INLINE opus_int64 silk_abs_int64_(opus_int64 a, char *file, int line){ if ( a == silk_int64_MIN ) { fprintf (stderr, "silk_abs_int64(%lld) in %s: line %d\n", (long long)a, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return (((a) > 0) ? (a) : -(a)); /* Be careful, silk_abs returns wrong when input equals to silk_intXX_MIN */ } #undef silk_abs_int32 #define silk_abs_int32(a) silk_abs_int32_((a), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_abs_int32_(opus_int32 a, char *file, int line){ if ( a == silk_int32_MIN ) { fprintf (stderr, "silk_abs_int32(%d) in %s: line %d\n", a, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return silk_abs(a); } #undef silk_CHECK_FIT8 #define silk_CHECK_FIT8(a) silk_CHECK_FIT8_((a), __FILE__, __LINE__) static OPUS_INLINE opus_int8 silk_CHECK_FIT8_( opus_int64 a, char *file, int line ){ opus_int8 ret; ret = (opus_int8)a; if ( (opus_int64)ret != a ) { fprintf (stderr, "silk_CHECK_FIT8(%lld) in %s: line %d\n", (long long)a, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return( ret ); } #undef silk_CHECK_FIT16 #define silk_CHECK_FIT16(a) silk_CHECK_FIT16_((a), __FILE__, __LINE__) static OPUS_INLINE opus_int16 silk_CHECK_FIT16_( opus_int64 a, char *file, int line ){ opus_int16 ret; ret = (opus_int16)a; if ( (opus_int64)ret != a ) { fprintf (stderr, "silk_CHECK_FIT16(%lld) in %s: line %d\n", (long long)a, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return( ret ); } #undef silk_CHECK_FIT32 #define silk_CHECK_FIT32(a) silk_CHECK_FIT32_((a), __FILE__, __LINE__) static OPUS_INLINE opus_int32 silk_CHECK_FIT32_( opus_int64 a, char *file, int line ){ opus_int32 ret; ret = (opus_int32)a; if ( (opus_int64)ret != a ) { fprintf (stderr, "silk_CHECK_FIT32(%lld) in %s: line %d\n", (long long)a, file, line); #ifdef FIXED_DEBUG_ASSERT silk_assert( 0 ); #endif } return( ret ); } /* no checking for silk_NSHIFT_MUL_32_32 no checking for silk_NSHIFT_MUL_16_16 no checking needed for silk_min no checking needed for silk_max no checking needed for silk_sign */ #endif #endif /* MACRO_DEBUG_H */ ================================================ FILE: deps/pjsip/third_party/opus/silk/NLSF2A.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* conversion between prediction filter coefficients and LSFs */ /* order should be even */ /* a piecewise linear approximation maps LSF <-> cos(LSF) */ /* therefore the result is not accurate LSFs, but the two */ /* functions are accurate inverses of each other */ #include "SigProc_FIX.h" #include "tables.h" #define QA 16 /* helper function for NLSF2A(..) */ static OPUS_INLINE void silk_NLSF2A_find_poly( opus_int32 *out, /* O intermediate polynomial, QA [dd+1] */ const opus_int32 *cLSF, /* I vector of interleaved 2*cos(LSFs), QA [d] */ opus_int dd /* I polynomial order (= 1/2 * filter order) */ ) { opus_int k, n; opus_int32 ftmp; out[0] = silk_LSHIFT( 1, QA ); out[1] = -cLSF[0]; for( k = 1; k < dd; k++ ) { ftmp = cLSF[2*k]; /* QA*/ out[k+1] = silk_LSHIFT( out[k-1], 1 ) - (opus_int32)silk_RSHIFT_ROUND64( silk_SMULL( ftmp, out[k] ), QA ); for( n = k; n > 1; n-- ) { out[n] += out[n-2] - (opus_int32)silk_RSHIFT_ROUND64( silk_SMULL( ftmp, out[n-1] ), QA ); } out[1] -= ftmp; } } /* compute whitening filter coefficients from normalized line spectral frequencies */ void silk_NLSF2A( opus_int16 *a_Q12, /* O monic whitening filter coefficients in Q12, [ d ] */ const opus_int16 *NLSF, /* I normalized line spectral frequencies in Q15, [ d ] */ const opus_int d /* I filter order (should be even) */ ) { /* This ordering was found to maximize quality. It improves numerical accuracy of silk_NLSF2A_find_poly() compared to "standard" ordering. */ static const unsigned char ordering16[16] = { 0, 15, 8, 7, 4, 11, 12, 3, 2, 13, 10, 5, 6, 9, 14, 1 }; static const unsigned char ordering10[10] = { 0, 9, 6, 3, 4, 5, 8, 1, 2, 7 }; const unsigned char *ordering; opus_int k, i, dd; opus_int32 cos_LSF_QA[ SILK_MAX_ORDER_LPC ]; opus_int32 P[ SILK_MAX_ORDER_LPC / 2 + 1 ], Q[ SILK_MAX_ORDER_LPC / 2 + 1 ]; opus_int32 Ptmp, Qtmp, f_int, f_frac, cos_val, delta; opus_int32 a32_QA1[ SILK_MAX_ORDER_LPC ]; opus_int32 maxabs, absval, idx=0, sc_Q16; silk_assert( LSF_COS_TAB_SZ_FIX == 128 ); silk_assert( d==10||d==16 ); /* convert LSFs to 2*cos(LSF), using piecewise linear curve from table */ ordering = d == 16 ? ordering16 : ordering10; for( k = 0; k < d; k++ ) { silk_assert(NLSF[k] >= 0 ); /* f_int on a scale 0-127 (rounded down) */ f_int = silk_RSHIFT( NLSF[k], 15 - 7 ); /* f_frac, range: 0..255 */ f_frac = NLSF[k] - silk_LSHIFT( f_int, 15 - 7 ); silk_assert(f_int >= 0); silk_assert(f_int < LSF_COS_TAB_SZ_FIX ); /* Read start and end value from table */ cos_val = silk_LSFCosTab_FIX_Q12[ f_int ]; /* Q12 */ delta = silk_LSFCosTab_FIX_Q12[ f_int + 1 ] - cos_val; /* Q12, with a range of 0..200 */ /* Linear interpolation */ cos_LSF_QA[ordering[k]] = silk_RSHIFT_ROUND( silk_LSHIFT( cos_val, 8 ) + silk_MUL( delta, f_frac ), 20 - QA ); /* QA */ } dd = silk_RSHIFT( d, 1 ); /* generate even and odd polynomials using convolution */ silk_NLSF2A_find_poly( P, &cos_LSF_QA[ 0 ], dd ); silk_NLSF2A_find_poly( Q, &cos_LSF_QA[ 1 ], dd ); /* convert even and odd polynomials to opus_int32 Q12 filter coefs */ for( k = 0; k < dd; k++ ) { Ptmp = P[ k+1 ] + P[ k ]; Qtmp = Q[ k+1 ] - Q[ k ]; /* the Ptmp and Qtmp values at this stage need to fit in int32 */ a32_QA1[ k ] = -Qtmp - Ptmp; /* QA+1 */ a32_QA1[ d-k-1 ] = Qtmp - Ptmp; /* QA+1 */ } /* Limit the maximum absolute value of the prediction coefficients, so that they'll fit in int16 */ for( i = 0; i < 10; i++ ) { /* Find maximum absolute value and its index */ maxabs = 0; for( k = 0; k < d; k++ ) { absval = silk_abs( a32_QA1[k] ); if( absval > maxabs ) { maxabs = absval; idx = k; } } maxabs = silk_RSHIFT_ROUND( maxabs, QA + 1 - 12 ); /* QA+1 -> Q12 */ if( maxabs > silk_int16_MAX ) { /* Reduce magnitude of prediction coefficients */ maxabs = silk_min( maxabs, 163838 ); /* ( silk_int32_MAX >> 14 ) + silk_int16_MAX = 163838 */ sc_Q16 = SILK_FIX_CONST( 0.999, 16 ) - silk_DIV32( silk_LSHIFT( maxabs - silk_int16_MAX, 14 ), silk_RSHIFT32( silk_MUL( maxabs, idx + 1), 2 ) ); silk_bwexpander_32( a32_QA1, d, sc_Q16 ); } else { break; } } if( i == 10 ) { /* Reached the last iteration, clip the coefficients */ for( k = 0; k < d; k++ ) { a_Q12[ k ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( a32_QA1[ k ], QA + 1 - 12 ) ); /* QA+1 -> Q12 */ a32_QA1[ k ] = silk_LSHIFT( (opus_int32)a_Q12[ k ], QA + 1 - 12 ); } } else { for( k = 0; k < d; k++ ) { a_Q12[ k ] = (opus_int16)silk_RSHIFT_ROUND( a32_QA1[ k ], QA + 1 - 12 ); /* QA+1 -> Q12 */ } } for( i = 0; i < MAX_LPC_STABILIZE_ITERATIONS; i++ ) { if( silk_LPC_inverse_pred_gain( a_Q12, d ) < SILK_FIX_CONST( 1.0 / MAX_PREDICTION_POWER_GAIN, 30 ) ) { /* Prediction coefficients are (too close to) unstable; apply bandwidth expansion */ /* on the unscaled coefficients, convert to Q12 and measure again */ silk_bwexpander_32( a32_QA1, d, 65536 - silk_LSHIFT( 2, i ) ); for( k = 0; k < d; k++ ) { a_Q12[ k ] = (opus_int16)silk_RSHIFT_ROUND( a32_QA1[ k ], QA + 1 - 12 ); /* QA+1 -> Q12 */ } } else { break; } } } ================================================ FILE: deps/pjsip/third_party/opus/silk/NLSF_VQ.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Compute quantization errors for an LPC_order element input vector for a VQ codebook */ void silk_NLSF_VQ( opus_int32 err_Q26[], /* O Quantization errors [K] */ const opus_int16 in_Q15[], /* I Input vectors to be quantized [LPC_order] */ const opus_uint8 pCB_Q8[], /* I Codebook vectors [K*LPC_order] */ const opus_int K, /* I Number of codebook vectors */ const opus_int LPC_order /* I Number of LPCs */ ) { opus_int i, m; opus_int32 diff_Q15, sum_error_Q30, sum_error_Q26; silk_assert( LPC_order <= 16 ); silk_assert( ( LPC_order & 1 ) == 0 ); /* Loop over codebook */ for( i = 0; i < K; i++ ) { sum_error_Q26 = 0; for( m = 0; m < LPC_order; m += 2 ) { /* Compute weighted squared quantization error for index m */ diff_Q15 = silk_SUB_LSHIFT32( in_Q15[ m ], (opus_int32)*pCB_Q8++, 7 ); /* range: [ -32767 : 32767 ]*/ sum_error_Q30 = silk_SMULBB( diff_Q15, diff_Q15 ); /* Compute weighted squared quantization error for index m + 1 */ diff_Q15 = silk_SUB_LSHIFT32( in_Q15[m + 1], (opus_int32)*pCB_Q8++, 7 ); /* range: [ -32767 : 32767 ]*/ sum_error_Q30 = silk_SMLABB( sum_error_Q30, diff_Q15, diff_Q15 ); sum_error_Q26 = silk_ADD_RSHIFT32( sum_error_Q26, sum_error_Q30, 4 ); silk_assert( sum_error_Q26 >= 0 ); silk_assert( sum_error_Q30 >= 0 ); } err_Q26[ i ] = sum_error_Q26; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/NLSF_VQ_weights_laroia.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "define.h" #include "SigProc_FIX.h" /* R. Laroia, N. Phamdo and N. Farvardin, "Robust and Efficient Quantization of Speech LSP Parameters Using Structured Vector Quantization", Proc. IEEE Int. Conf. Acoust., Speech, Signal Processing, pp. 641-644, 1991. */ /* Laroia low complexity NLSF weights */ void silk_NLSF_VQ_weights_laroia( opus_int16 *pNLSFW_Q_OUT, /* O Pointer to input vector weights [D] */ const opus_int16 *pNLSF_Q15, /* I Pointer to input vector [D] */ const opus_int D /* I Input vector dimension (even) */ ) { opus_int k; opus_int32 tmp1_int, tmp2_int; silk_assert( D > 0 ); silk_assert( ( D & 1 ) == 0 ); /* First value */ tmp1_int = silk_max_int( pNLSF_Q15[ 0 ], 1 ); tmp1_int = silk_DIV32_16( (opus_int32)1 << ( 15 + NLSF_W_Q ), tmp1_int ); tmp2_int = silk_max_int( pNLSF_Q15[ 1 ] - pNLSF_Q15[ 0 ], 1 ); tmp2_int = silk_DIV32_16( (opus_int32)1 << ( 15 + NLSF_W_Q ), tmp2_int ); pNLSFW_Q_OUT[ 0 ] = (opus_int16)silk_min_int( tmp1_int + tmp2_int, silk_int16_MAX ); silk_assert( pNLSFW_Q_OUT[ 0 ] > 0 ); /* Main loop */ for( k = 1; k < D - 1; k += 2 ) { tmp1_int = silk_max_int( pNLSF_Q15[ k + 1 ] - pNLSF_Q15[ k ], 1 ); tmp1_int = silk_DIV32_16( (opus_int32)1 << ( 15 + NLSF_W_Q ), tmp1_int ); pNLSFW_Q_OUT[ k ] = (opus_int16)silk_min_int( tmp1_int + tmp2_int, silk_int16_MAX ); silk_assert( pNLSFW_Q_OUT[ k ] > 0 ); tmp2_int = silk_max_int( pNLSF_Q15[ k + 2 ] - pNLSF_Q15[ k + 1 ], 1 ); tmp2_int = silk_DIV32_16( (opus_int32)1 << ( 15 + NLSF_W_Q ), tmp2_int ); pNLSFW_Q_OUT[ k + 1 ] = (opus_int16)silk_min_int( tmp1_int + tmp2_int, silk_int16_MAX ); silk_assert( pNLSFW_Q_OUT[ k + 1 ] > 0 ); } /* Last value */ tmp1_int = silk_max_int( ( 1 << 15 ) - pNLSF_Q15[ D - 1 ], 1 ); tmp1_int = silk_DIV32_16( (opus_int32)1 << ( 15 + NLSF_W_Q ), tmp1_int ); pNLSFW_Q_OUT[ D - 1 ] = (opus_int16)silk_min_int( tmp1_int + tmp2_int, silk_int16_MAX ); silk_assert( pNLSFW_Q_OUT[ D - 1 ] > 0 ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/NLSF_decode.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Predictive dequantizer for NLSF residuals */ static OPUS_INLINE void silk_NLSF_residual_dequant( /* O Returns RD value in Q30 */ opus_int16 x_Q10[], /* O Output [ order ] */ const opus_int8 indices[], /* I Quantization indices [ order ] */ const opus_uint8 pred_coef_Q8[], /* I Backward predictor coefs [ order ] */ const opus_int quant_step_size_Q16, /* I Quantization step size */ const opus_int16 order /* I Number of input values */ ) { opus_int i, out_Q10, pred_Q10; out_Q10 = 0; for( i = order-1; i >= 0; i-- ) { pred_Q10 = silk_RSHIFT( silk_SMULBB( out_Q10, (opus_int16)pred_coef_Q8[ i ] ), 8 ); out_Q10 = silk_LSHIFT( indices[ i ], 10 ); if( out_Q10 > 0 ) { out_Q10 = silk_SUB16( out_Q10, SILK_FIX_CONST( NLSF_QUANT_LEVEL_ADJ, 10 ) ); } else if( out_Q10 < 0 ) { out_Q10 = silk_ADD16( out_Q10, SILK_FIX_CONST( NLSF_QUANT_LEVEL_ADJ, 10 ) ); } out_Q10 = silk_SMLAWB( pred_Q10, (opus_int32)out_Q10, quant_step_size_Q16 ); x_Q10[ i ] = out_Q10; } } /***********************/ /* NLSF vector decoder */ /***********************/ void silk_NLSF_decode( opus_int16 *pNLSF_Q15, /* O Quantized NLSF vector [ LPC_ORDER ] */ opus_int8 *NLSFIndices, /* I Codebook path vector [ LPC_ORDER + 1 ] */ const silk_NLSF_CB_struct *psNLSF_CB /* I Codebook object */ ) { opus_int i; opus_uint8 pred_Q8[ MAX_LPC_ORDER ]; opus_int16 ec_ix[ MAX_LPC_ORDER ]; opus_int16 res_Q10[ MAX_LPC_ORDER ]; opus_int16 W_tmp_QW[ MAX_LPC_ORDER ]; opus_int32 W_tmp_Q9, NLSF_Q15_tmp; const opus_uint8 *pCB_element; /* Decode first stage */ pCB_element = &psNLSF_CB->CB1_NLSF_Q8[ NLSFIndices[ 0 ] * psNLSF_CB->order ]; for( i = 0; i < psNLSF_CB->order; i++ ) { pNLSF_Q15[ i ] = silk_LSHIFT( (opus_int16)pCB_element[ i ], 7 ); } /* Unpack entropy table indices and predictor for current CB1 index */ silk_NLSF_unpack( ec_ix, pred_Q8, psNLSF_CB, NLSFIndices[ 0 ] ); /* Predictive residual dequantizer */ silk_NLSF_residual_dequant( res_Q10, &NLSFIndices[ 1 ], pred_Q8, psNLSF_CB->quantStepSize_Q16, psNLSF_CB->order ); /* Weights from codebook vector */ silk_NLSF_VQ_weights_laroia( W_tmp_QW, pNLSF_Q15, psNLSF_CB->order ); /* Apply inverse square-rooted weights and add to output */ for( i = 0; i < psNLSF_CB->order; i++ ) { W_tmp_Q9 = silk_SQRT_APPROX( silk_LSHIFT( (opus_int32)W_tmp_QW[ i ], 18 - NLSF_W_Q ) ); NLSF_Q15_tmp = silk_ADD32( pNLSF_Q15[ i ], silk_DIV32_16( silk_LSHIFT( (opus_int32)res_Q10[ i ], 14 ), W_tmp_Q9 ) ); pNLSF_Q15[ i ] = (opus_int16)silk_LIMIT( NLSF_Q15_tmp, 0, 32767 ); } /* NLSF stabilization */ silk_NLSF_stabilize( pNLSF_Q15, psNLSF_CB->deltaMin_Q15, psNLSF_CB->order ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/NLSF_del_dec_quant.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Delayed-decision quantizer for NLSF residuals */ opus_int32 silk_NLSF_del_dec_quant( /* O Returns RD value in Q25 */ opus_int8 indices[], /* O Quantization indices [ order ] */ const opus_int16 x_Q10[], /* I Input [ order ] */ const opus_int16 w_Q5[], /* I Weights [ order ] */ const opus_uint8 pred_coef_Q8[], /* I Backward predictor coefs [ order ] */ const opus_int16 ec_ix[], /* I Indices to entropy coding tables [ order ] */ const opus_uint8 ec_rates_Q5[], /* I Rates [] */ const opus_int quant_step_size_Q16, /* I Quantization step size */ const opus_int16 inv_quant_step_size_Q6, /* I Inverse quantization step size */ const opus_int32 mu_Q20, /* I R/D tradeoff */ const opus_int16 order /* I Number of input values */ ) { opus_int i, j, nStates, ind_tmp, ind_min_max, ind_max_min, in_Q10, res_Q10; opus_int pred_Q10, diff_Q10, out0_Q10, out1_Q10, rate0_Q5, rate1_Q5; opus_int32 RD_tmp_Q25, min_Q25, min_max_Q25, max_min_Q25, pred_coef_Q16; opus_int ind_sort[ NLSF_QUANT_DEL_DEC_STATES ]; opus_int8 ind[ NLSF_QUANT_DEL_DEC_STATES ][ MAX_LPC_ORDER ]; opus_int16 prev_out_Q10[ 2 * NLSF_QUANT_DEL_DEC_STATES ]; opus_int32 RD_Q25[ 2 * NLSF_QUANT_DEL_DEC_STATES ]; opus_int32 RD_min_Q25[ NLSF_QUANT_DEL_DEC_STATES ]; opus_int32 RD_max_Q25[ NLSF_QUANT_DEL_DEC_STATES ]; const opus_uint8 *rates_Q5; opus_int out0_Q10_table[2 * NLSF_QUANT_MAX_AMPLITUDE_EXT]; opus_int out1_Q10_table[2 * NLSF_QUANT_MAX_AMPLITUDE_EXT]; for (i = -NLSF_QUANT_MAX_AMPLITUDE_EXT; i <= NLSF_QUANT_MAX_AMPLITUDE_EXT-1; i++) { out0_Q10 = silk_LSHIFT( i, 10 ); out1_Q10 = silk_ADD16( out0_Q10, 1024 ); if( i > 0 ) { out0_Q10 = silk_SUB16( out0_Q10, SILK_FIX_CONST( NLSF_QUANT_LEVEL_ADJ, 10 ) ); out1_Q10 = silk_SUB16( out1_Q10, SILK_FIX_CONST( NLSF_QUANT_LEVEL_ADJ, 10 ) ); } else if( i == 0 ) { out1_Q10 = silk_SUB16( out1_Q10, SILK_FIX_CONST( NLSF_QUANT_LEVEL_ADJ, 10 ) ); } else if( i == -1 ) { out0_Q10 = silk_ADD16( out0_Q10, SILK_FIX_CONST( NLSF_QUANT_LEVEL_ADJ, 10 ) ); } else { out0_Q10 = silk_ADD16( out0_Q10, SILK_FIX_CONST( NLSF_QUANT_LEVEL_ADJ, 10 ) ); out1_Q10 = silk_ADD16( out1_Q10, SILK_FIX_CONST( NLSF_QUANT_LEVEL_ADJ, 10 ) ); } out0_Q10_table[ i + NLSF_QUANT_MAX_AMPLITUDE_EXT ] = silk_SMULWB( (opus_int32)out0_Q10, quant_step_size_Q16 ); out1_Q10_table[ i + NLSF_QUANT_MAX_AMPLITUDE_EXT ] = silk_SMULWB( (opus_int32)out1_Q10, quant_step_size_Q16 ); } silk_assert( (NLSF_QUANT_DEL_DEC_STATES & (NLSF_QUANT_DEL_DEC_STATES-1)) == 0 ); /* must be power of two */ nStates = 1; RD_Q25[ 0 ] = 0; prev_out_Q10[ 0 ] = 0; for( i = order - 1; ; i-- ) { rates_Q5 = &ec_rates_Q5[ ec_ix[ i ] ]; pred_coef_Q16 = silk_LSHIFT( (opus_int32)pred_coef_Q8[ i ], 8 ); in_Q10 = x_Q10[ i ]; for( j = 0; j < nStates; j++ ) { pred_Q10 = silk_SMULWB( pred_coef_Q16, prev_out_Q10[ j ] ); res_Q10 = silk_SUB16( in_Q10, pred_Q10 ); ind_tmp = silk_SMULWB( (opus_int32)inv_quant_step_size_Q6, res_Q10 ); ind_tmp = silk_LIMIT( ind_tmp, -NLSF_QUANT_MAX_AMPLITUDE_EXT, NLSF_QUANT_MAX_AMPLITUDE_EXT-1 ); ind[ j ][ i ] = (opus_int8)ind_tmp; /* compute outputs for ind_tmp and ind_tmp + 1 */ out0_Q10 = out0_Q10_table[ ind_tmp + NLSF_QUANT_MAX_AMPLITUDE_EXT ]; out1_Q10 = out1_Q10_table[ ind_tmp + NLSF_QUANT_MAX_AMPLITUDE_EXT ]; out0_Q10 = silk_ADD16( out0_Q10, pred_Q10 ); out1_Q10 = silk_ADD16( out1_Q10, pred_Q10 ); prev_out_Q10[ j ] = out0_Q10; prev_out_Q10[ j + nStates ] = out1_Q10; /* compute RD for ind_tmp and ind_tmp + 1 */ if( ind_tmp + 1 >= NLSF_QUANT_MAX_AMPLITUDE ) { if( ind_tmp + 1 == NLSF_QUANT_MAX_AMPLITUDE ) { rate0_Q5 = rates_Q5[ ind_tmp + NLSF_QUANT_MAX_AMPLITUDE ]; rate1_Q5 = 280; } else { rate0_Q5 = silk_SMLABB( 280 - 43 * NLSF_QUANT_MAX_AMPLITUDE, 43, ind_tmp ); rate1_Q5 = silk_ADD16( rate0_Q5, 43 ); } } else if( ind_tmp <= -NLSF_QUANT_MAX_AMPLITUDE ) { if( ind_tmp == -NLSF_QUANT_MAX_AMPLITUDE ) { rate0_Q5 = 280; rate1_Q5 = rates_Q5[ ind_tmp + 1 + NLSF_QUANT_MAX_AMPLITUDE ]; } else { rate0_Q5 = silk_SMLABB( 280 - 43 * NLSF_QUANT_MAX_AMPLITUDE, -43, ind_tmp ); rate1_Q5 = silk_SUB16( rate0_Q5, 43 ); } } else { rate0_Q5 = rates_Q5[ ind_tmp + NLSF_QUANT_MAX_AMPLITUDE ]; rate1_Q5 = rates_Q5[ ind_tmp + 1 + NLSF_QUANT_MAX_AMPLITUDE ]; } RD_tmp_Q25 = RD_Q25[ j ]; diff_Q10 = silk_SUB16( in_Q10, out0_Q10 ); RD_Q25[ j ] = silk_SMLABB( silk_MLA( RD_tmp_Q25, silk_SMULBB( diff_Q10, diff_Q10 ), w_Q5[ i ] ), mu_Q20, rate0_Q5 ); diff_Q10 = silk_SUB16( in_Q10, out1_Q10 ); RD_Q25[ j + nStates ] = silk_SMLABB( silk_MLA( RD_tmp_Q25, silk_SMULBB( diff_Q10, diff_Q10 ), w_Q5[ i ] ), mu_Q20, rate1_Q5 ); } if( nStates <= ( NLSF_QUANT_DEL_DEC_STATES >> 1 ) ) { /* double number of states and copy */ for( j = 0; j < nStates; j++ ) { ind[ j + nStates ][ i ] = ind[ j ][ i ] + 1; } nStates = silk_LSHIFT( nStates, 1 ); for( j = nStates; j < NLSF_QUANT_DEL_DEC_STATES; j++ ) { ind[ j ][ i ] = ind[ j - nStates ][ i ]; } } else if( i > 0 ) { /* sort lower and upper half of RD_Q25, pairwise */ for( j = 0; j < NLSF_QUANT_DEL_DEC_STATES; j++ ) { if( RD_Q25[ j ] > RD_Q25[ j + NLSF_QUANT_DEL_DEC_STATES ] ) { RD_max_Q25[ j ] = RD_Q25[ j ]; RD_min_Q25[ j ] = RD_Q25[ j + NLSF_QUANT_DEL_DEC_STATES ]; RD_Q25[ j ] = RD_min_Q25[ j ]; RD_Q25[ j + NLSF_QUANT_DEL_DEC_STATES ] = RD_max_Q25[ j ]; /* swap prev_out values */ out0_Q10 = prev_out_Q10[ j ]; prev_out_Q10[ j ] = prev_out_Q10[ j + NLSF_QUANT_DEL_DEC_STATES ]; prev_out_Q10[ j + NLSF_QUANT_DEL_DEC_STATES ] = out0_Q10; ind_sort[ j ] = j + NLSF_QUANT_DEL_DEC_STATES; } else { RD_min_Q25[ j ] = RD_Q25[ j ]; RD_max_Q25[ j ] = RD_Q25[ j + NLSF_QUANT_DEL_DEC_STATES ]; ind_sort[ j ] = j; } } /* compare the highest RD values of the winning half with the lowest one in the losing half, and copy if necessary */ /* afterwards ind_sort[] will contain the indices of the NLSF_QUANT_DEL_DEC_STATES winning RD values */ while( 1 ) { min_max_Q25 = silk_int32_MAX; max_min_Q25 = 0; ind_min_max = 0; ind_max_min = 0; for( j = 0; j < NLSF_QUANT_DEL_DEC_STATES; j++ ) { if( min_max_Q25 > RD_max_Q25[ j ] ) { min_max_Q25 = RD_max_Q25[ j ]; ind_min_max = j; } if( max_min_Q25 < RD_min_Q25[ j ] ) { max_min_Q25 = RD_min_Q25[ j ]; ind_max_min = j; } } if( min_max_Q25 >= max_min_Q25 ) { break; } /* copy ind_min_max to ind_max_min */ ind_sort[ ind_max_min ] = ind_sort[ ind_min_max ] ^ NLSF_QUANT_DEL_DEC_STATES; RD_Q25[ ind_max_min ] = RD_Q25[ ind_min_max + NLSF_QUANT_DEL_DEC_STATES ]; prev_out_Q10[ ind_max_min ] = prev_out_Q10[ ind_min_max + NLSF_QUANT_DEL_DEC_STATES ]; RD_min_Q25[ ind_max_min ] = 0; RD_max_Q25[ ind_min_max ] = silk_int32_MAX; silk_memcpy( ind[ ind_max_min ], ind[ ind_min_max ], MAX_LPC_ORDER * sizeof( opus_int8 ) ); } /* increment index if it comes from the upper half */ for( j = 0; j < NLSF_QUANT_DEL_DEC_STATES; j++ ) { ind[ j ][ i ] += silk_RSHIFT( ind_sort[ j ], NLSF_QUANT_DEL_DEC_STATES_LOG2 ); } } else { /* i == 0 */ break; } } /* last sample: find winner, copy indices and return RD value */ ind_tmp = 0; min_Q25 = silk_int32_MAX; for( j = 0; j < 2 * NLSF_QUANT_DEL_DEC_STATES; j++ ) { if( min_Q25 > RD_Q25[ j ] ) { min_Q25 = RD_Q25[ j ]; ind_tmp = j; } } for( j = 0; j < order; j++ ) { indices[ j ] = ind[ ind_tmp & ( NLSF_QUANT_DEL_DEC_STATES - 1 ) ][ j ]; silk_assert( indices[ j ] >= -NLSF_QUANT_MAX_AMPLITUDE_EXT ); silk_assert( indices[ j ] <= NLSF_QUANT_MAX_AMPLITUDE_EXT ); } indices[ 0 ] += silk_RSHIFT( ind_tmp, NLSF_QUANT_DEL_DEC_STATES_LOG2 ); silk_assert( indices[ 0 ] <= NLSF_QUANT_MAX_AMPLITUDE_EXT ); silk_assert( min_Q25 >= 0 ); return min_Q25; } ================================================ FILE: deps/pjsip/third_party/opus/silk/NLSF_encode.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #include "stack_alloc.h" /***********************/ /* NLSF vector encoder */ /***********************/ opus_int32 silk_NLSF_encode( /* O Returns RD value in Q25 */ opus_int8 *NLSFIndices, /* I Codebook path vector [ LPC_ORDER + 1 ] */ opus_int16 *pNLSF_Q15, /* I/O Quantized NLSF vector [ LPC_ORDER ] */ const silk_NLSF_CB_struct *psNLSF_CB, /* I Codebook object */ const opus_int16 *pW_QW, /* I NLSF weight vector [ LPC_ORDER ] */ const opus_int NLSF_mu_Q20, /* I Rate weight for the RD optimization */ const opus_int nSurvivors, /* I Max survivors after first stage */ const opus_int signalType /* I Signal type: 0/1/2 */ ) { opus_int i, s, ind1, bestIndex, prob_Q8, bits_q7; opus_int32 W_tmp_Q9; VARDECL( opus_int32, err_Q26 ); VARDECL( opus_int32, RD_Q25 ); VARDECL( opus_int, tempIndices1 ); VARDECL( opus_int8, tempIndices2 ); opus_int16 res_Q15[ MAX_LPC_ORDER ]; opus_int16 res_Q10[ MAX_LPC_ORDER ]; opus_int16 NLSF_tmp_Q15[ MAX_LPC_ORDER ]; opus_int16 W_tmp_QW[ MAX_LPC_ORDER ]; opus_int16 W_adj_Q5[ MAX_LPC_ORDER ]; opus_uint8 pred_Q8[ MAX_LPC_ORDER ]; opus_int16 ec_ix[ MAX_LPC_ORDER ]; const opus_uint8 *pCB_element, *iCDF_ptr; SAVE_STACK; silk_assert( nSurvivors <= NLSF_VQ_MAX_SURVIVORS ); silk_assert( signalType >= 0 && signalType <= 2 ); silk_assert( NLSF_mu_Q20 <= 32767 && NLSF_mu_Q20 >= 0 ); /* NLSF stabilization */ silk_NLSF_stabilize( pNLSF_Q15, psNLSF_CB->deltaMin_Q15, psNLSF_CB->order ); /* First stage: VQ */ ALLOC( err_Q26, psNLSF_CB->nVectors, opus_int32 ); silk_NLSF_VQ( err_Q26, pNLSF_Q15, psNLSF_CB->CB1_NLSF_Q8, psNLSF_CB->nVectors, psNLSF_CB->order ); /* Sort the quantization errors */ ALLOC( tempIndices1, nSurvivors, opus_int ); silk_insertion_sort_increasing( err_Q26, tempIndices1, psNLSF_CB->nVectors, nSurvivors ); ALLOC( RD_Q25, nSurvivors, opus_int32 ); ALLOC( tempIndices2, nSurvivors * MAX_LPC_ORDER, opus_int8 ); /* Loop over survivors */ for( s = 0; s < nSurvivors; s++ ) { ind1 = tempIndices1[ s ]; /* Residual after first stage */ pCB_element = &psNLSF_CB->CB1_NLSF_Q8[ ind1 * psNLSF_CB->order ]; for( i = 0; i < psNLSF_CB->order; i++ ) { NLSF_tmp_Q15[ i ] = silk_LSHIFT16( (opus_int16)pCB_element[ i ], 7 ); res_Q15[ i ] = pNLSF_Q15[ i ] - NLSF_tmp_Q15[ i ]; } /* Weights from codebook vector */ silk_NLSF_VQ_weights_laroia( W_tmp_QW, NLSF_tmp_Q15, psNLSF_CB->order ); /* Apply square-rooted weights */ for( i = 0; i < psNLSF_CB->order; i++ ) { W_tmp_Q9 = silk_SQRT_APPROX( silk_LSHIFT( (opus_int32)W_tmp_QW[ i ], 18 - NLSF_W_Q ) ); res_Q10[ i ] = (opus_int16)silk_RSHIFT( silk_SMULBB( res_Q15[ i ], W_tmp_Q9 ), 14 ); } /* Modify input weights accordingly */ for( i = 0; i < psNLSF_CB->order; i++ ) { W_adj_Q5[ i ] = silk_DIV32_16( silk_LSHIFT( (opus_int32)pW_QW[ i ], 5 ), W_tmp_QW[ i ] ); } /* Unpack entropy table indices and predictor for current CB1 index */ silk_NLSF_unpack( ec_ix, pred_Q8, psNLSF_CB, ind1 ); /* Trellis quantizer */ RD_Q25[ s ] = silk_NLSF_del_dec_quant( &tempIndices2[ s * MAX_LPC_ORDER ], res_Q10, W_adj_Q5, pred_Q8, ec_ix, psNLSF_CB->ec_Rates_Q5, psNLSF_CB->quantStepSize_Q16, psNLSF_CB->invQuantStepSize_Q6, NLSF_mu_Q20, psNLSF_CB->order ); /* Add rate for first stage */ iCDF_ptr = &psNLSF_CB->CB1_iCDF[ ( signalType >> 1 ) * psNLSF_CB->nVectors ]; if( ind1 == 0 ) { prob_Q8 = 256 - iCDF_ptr[ ind1 ]; } else { prob_Q8 = iCDF_ptr[ ind1 - 1 ] - iCDF_ptr[ ind1 ]; } bits_q7 = ( 8 << 7 ) - silk_lin2log( prob_Q8 ); RD_Q25[ s ] = silk_SMLABB( RD_Q25[ s ], bits_q7, silk_RSHIFT( NLSF_mu_Q20, 2 ) ); } /* Find the lowest rate-distortion error */ silk_insertion_sort_increasing( RD_Q25, &bestIndex, nSurvivors, 1 ); NLSFIndices[ 0 ] = (opus_int8)tempIndices1[ bestIndex ]; silk_memcpy( &NLSFIndices[ 1 ], &tempIndices2[ bestIndex * MAX_LPC_ORDER ], psNLSF_CB->order * sizeof( opus_int8 ) ); /* Decode */ silk_NLSF_decode( pNLSF_Q15, NLSFIndices, psNLSF_CB ); RESTORE_STACK; return RD_Q25[ 0 ]; } ================================================ FILE: deps/pjsip/third_party/opus/silk/NLSF_stabilize.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* NLSF stabilizer: */ /* */ /* - Moves NLSFs further apart if they are too close */ /* - Moves NLSFs away from borders if they are too close */ /* - High effort to achieve a modification with minimum */ /* Euclidean distance to input vector */ /* - Output are sorted NLSF coefficients */ /* */ #include "SigProc_FIX.h" /* Constant Definitions */ #define MAX_LOOPS 20 /* NLSF stabilizer, for a single input data vector */ void silk_NLSF_stabilize( opus_int16 *NLSF_Q15, /* I/O Unstable/stabilized normalized LSF vector in Q15 [L] */ const opus_int16 *NDeltaMin_Q15, /* I Min distance vector, NDeltaMin_Q15[L] must be >= 1 [L+1] */ const opus_int L /* I Number of NLSF parameters in the input vector */ ) { opus_int i, I=0, k, loops; opus_int16 center_freq_Q15; opus_int32 diff_Q15, min_diff_Q15, min_center_Q15, max_center_Q15; /* This is necessary to ensure an output within range of a opus_int16 */ silk_assert( NDeltaMin_Q15[L] >= 1 ); for( loops = 0; loops < MAX_LOOPS; loops++ ) { /**************************/ /* Find smallest distance */ /**************************/ /* First element */ min_diff_Q15 = NLSF_Q15[0] - NDeltaMin_Q15[0]; I = 0; /* Middle elements */ for( i = 1; i <= L-1; i++ ) { diff_Q15 = NLSF_Q15[i] - ( NLSF_Q15[i-1] + NDeltaMin_Q15[i] ); if( diff_Q15 < min_diff_Q15 ) { min_diff_Q15 = diff_Q15; I = i; } } /* Last element */ diff_Q15 = ( 1 << 15 ) - ( NLSF_Q15[L-1] + NDeltaMin_Q15[L] ); if( diff_Q15 < min_diff_Q15 ) { min_diff_Q15 = diff_Q15; I = L; } /***************************************************/ /* Now check if the smallest distance non-negative */ /***************************************************/ if( min_diff_Q15 >= 0 ) { return; } if( I == 0 ) { /* Move away from lower limit */ NLSF_Q15[0] = NDeltaMin_Q15[0]; } else if( I == L) { /* Move away from higher limit */ NLSF_Q15[L-1] = ( 1 << 15 ) - NDeltaMin_Q15[L]; } else { /* Find the lower extreme for the location of the current center frequency */ min_center_Q15 = 0; for( k = 0; k < I; k++ ) { min_center_Q15 += NDeltaMin_Q15[k]; } min_center_Q15 += silk_RSHIFT( NDeltaMin_Q15[I], 1 ); /* Find the upper extreme for the location of the current center frequency */ max_center_Q15 = 1 << 15; for( k = L; k > I; k-- ) { max_center_Q15 -= NDeltaMin_Q15[k]; } max_center_Q15 -= silk_RSHIFT( NDeltaMin_Q15[I], 1 ); /* Move apart, sorted by value, keeping the same center frequency */ center_freq_Q15 = (opus_int16)silk_LIMIT_32( silk_RSHIFT_ROUND( (opus_int32)NLSF_Q15[I-1] + (opus_int32)NLSF_Q15[I], 1 ), min_center_Q15, max_center_Q15 ); NLSF_Q15[I-1] = center_freq_Q15 - silk_RSHIFT( NDeltaMin_Q15[I], 1 ); NLSF_Q15[I] = NLSF_Q15[I-1] + NDeltaMin_Q15[I]; } } /* Safe and simple fall back method, which is less ideal than the above */ if( loops == MAX_LOOPS ) { /* Insertion sort (fast for already almost sorted arrays): */ /* Best case: O(n) for an already sorted array */ /* Worst case: O(n^2) for an inversely sorted array */ silk_insertion_sort_increasing_all_values_int16( &NLSF_Q15[0], L ); /* First NLSF should be no less than NDeltaMin[0] */ NLSF_Q15[0] = silk_max_int( NLSF_Q15[0], NDeltaMin_Q15[0] ); /* Keep delta_min distance between the NLSFs */ for( i = 1; i < L; i++ ) NLSF_Q15[i] = silk_max_int( NLSF_Q15[i], NLSF_Q15[i-1] + NDeltaMin_Q15[i] ); /* Last NLSF should be no higher than 1 - NDeltaMin[L] */ NLSF_Q15[L-1] = silk_min_int( NLSF_Q15[L-1], (1<<15) - NDeltaMin_Q15[L] ); /* Keep NDeltaMin distance between the NLSFs */ for( i = L-2; i >= 0; i-- ) NLSF_Q15[i] = silk_min_int( NLSF_Q15[i], NLSF_Q15[i+1] - NDeltaMin_Q15[i+1] ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/NLSF_unpack.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Unpack predictor values and indices for entropy coding tables */ void silk_NLSF_unpack( opus_int16 ec_ix[], /* O Indices to entropy tables [ LPC_ORDER ] */ opus_uint8 pred_Q8[], /* O LSF predictor [ LPC_ORDER ] */ const silk_NLSF_CB_struct *psNLSF_CB, /* I Codebook object */ const opus_int CB1_index /* I Index of vector in first LSF codebook */ ) { opus_int i; opus_uint8 entry; const opus_uint8 *ec_sel_ptr; ec_sel_ptr = &psNLSF_CB->ec_sel[ CB1_index * psNLSF_CB->order / 2 ]; for( i = 0; i < psNLSF_CB->order; i += 2 ) { entry = *ec_sel_ptr++; ec_ix [ i ] = silk_SMULBB( silk_RSHIFT( entry, 1 ) & 7, 2 * NLSF_QUANT_MAX_AMPLITUDE + 1 ); pred_Q8[ i ] = psNLSF_CB->pred_Q8[ i + ( entry & 1 ) * ( psNLSF_CB->order - 1 ) ]; ec_ix [ i + 1 ] = silk_SMULBB( silk_RSHIFT( entry, 5 ) & 7, 2 * NLSF_QUANT_MAX_AMPLITUDE + 1 ); pred_Q8[ i + 1 ] = psNLSF_CB->pred_Q8[ i + ( silk_RSHIFT( entry, 4 ) & 1 ) * ( psNLSF_CB->order - 1 ) + 1 ]; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/NSQ.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #include "stack_alloc.h" static OPUS_INLINE void silk_nsq_scale_states( const silk_encoder_state *psEncC, /* I Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ const opus_int32 x_Q3[], /* I input in Q3 */ opus_int32 x_sc_Q10[], /* O input scaled with 1/Gain */ const opus_int16 sLTP[], /* I re-whitened LTP state in Q0 */ opus_int32 sLTP_Q15[], /* O LTP state matching scaled input */ opus_int subfr, /* I subframe number */ const opus_int LTP_scale_Q14, /* I */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lag */ const opus_int signal_type /* I Signal type */ ); #if !defined(OPUS_X86_MAY_HAVE_SSE4_1) static OPUS_INLINE void silk_noise_shape_quantizer( silk_nsq_state *NSQ, /* I/O NSQ state */ opus_int signalType, /* I Signal type */ const opus_int32 x_sc_Q10[], /* I */ opus_int8 pulses[], /* O */ opus_int16 xq[], /* O */ opus_int32 sLTP_Q15[], /* I/O LTP state */ const opus_int16 a_Q12[], /* I Short term prediction coefs */ const opus_int16 b_Q14[], /* I Long term prediction coefs */ const opus_int16 AR_shp_Q13[], /* I Noise shaping AR coefs */ opus_int lag, /* I Pitch lag */ opus_int32 HarmShapeFIRPacked_Q14, /* I */ opus_int Tilt_Q14, /* I Spectral tilt */ opus_int32 LF_shp_Q14, /* I */ opus_int32 Gain_Q16, /* I */ opus_int Lambda_Q10, /* I */ opus_int offset_Q10, /* I */ opus_int length, /* I Input length */ opus_int shapingLPCOrder, /* I Noise shaping AR filter order */ opus_int predictLPCOrder /* I Prediction filter order */ ); #endif void silk_NSQ_c ( const silk_encoder_state *psEncC, /* I/O Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ SideInfoIndices *psIndices, /* I/O Quantization Indices */ const opus_int32 x_Q3[], /* I Prefiltered input signal */ opus_int8 pulses[], /* O Quantized pulse signal */ const opus_int16 PredCoef_Q12[ 2 * MAX_LPC_ORDER ], /* I Short term prediction coefs */ const opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ], /* I Long term prediction coefs */ const opus_int16 AR2_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ], /* I Noise shaping coefs */ const opus_int HarmShapeGain_Q14[ MAX_NB_SUBFR ], /* I Long term shaping coefs */ const opus_int Tilt_Q14[ MAX_NB_SUBFR ], /* I Spectral tilt */ const opus_int32 LF_shp_Q14[ MAX_NB_SUBFR ], /* I Low frequency shaping coefs */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I Quantization step sizes */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lags */ const opus_int Lambda_Q10, /* I Rate/distortion tradeoff */ const opus_int LTP_scale_Q14 /* I LTP state scaling */ ) { opus_int k, lag, start_idx, LSF_interpolation_flag; const opus_int16 *A_Q12, *B_Q14, *AR_shp_Q13; opus_int16 *pxq; VARDECL( opus_int32, sLTP_Q15 ); VARDECL( opus_int16, sLTP ); opus_int32 HarmShapeFIRPacked_Q14; opus_int offset_Q10; VARDECL( opus_int32, x_sc_Q10 ); SAVE_STACK; NSQ->rand_seed = psIndices->Seed; /* Set unvoiced lag to the previous one, overwrite later for voiced */ lag = NSQ->lagPrev; silk_assert( NSQ->prev_gain_Q16 != 0 ); offset_Q10 = silk_Quantization_Offsets_Q10[ psIndices->signalType >> 1 ][ psIndices->quantOffsetType ]; if( psIndices->NLSFInterpCoef_Q2 == 4 ) { LSF_interpolation_flag = 0; } else { LSF_interpolation_flag = 1; } ALLOC( sLTP_Q15, psEncC->ltp_mem_length + psEncC->frame_length, opus_int32 ); ALLOC( sLTP, psEncC->ltp_mem_length + psEncC->frame_length, opus_int16 ); ALLOC( x_sc_Q10, psEncC->subfr_length, opus_int32 ); /* Set up pointers to start of sub frame */ NSQ->sLTP_shp_buf_idx = psEncC->ltp_mem_length; NSQ->sLTP_buf_idx = psEncC->ltp_mem_length; pxq = &NSQ->xq[ psEncC->ltp_mem_length ]; for( k = 0; k < psEncC->nb_subfr; k++ ) { A_Q12 = &PredCoef_Q12[ (( k >> 1 ) | ( 1 - LSF_interpolation_flag )) * MAX_LPC_ORDER ]; B_Q14 = <PCoef_Q14[ k * LTP_ORDER ]; AR_shp_Q13 = &AR2_Q13[ k * MAX_SHAPE_LPC_ORDER ]; /* Noise shape parameters */ silk_assert( HarmShapeGain_Q14[ k ] >= 0 ); HarmShapeFIRPacked_Q14 = silk_RSHIFT( HarmShapeGain_Q14[ k ], 2 ); HarmShapeFIRPacked_Q14 |= silk_LSHIFT( (opus_int32)silk_RSHIFT( HarmShapeGain_Q14[ k ], 1 ), 16 ); NSQ->rewhite_flag = 0; if( psIndices->signalType == TYPE_VOICED ) { /* Voiced */ lag = pitchL[ k ]; /* Re-whitening */ if( ( k & ( 3 - silk_LSHIFT( LSF_interpolation_flag, 1 ) ) ) == 0 ) { /* Rewhiten with new A coefs */ start_idx = psEncC->ltp_mem_length - lag - psEncC->predictLPCOrder - LTP_ORDER / 2; silk_assert( start_idx > 0 ); silk_LPC_analysis_filter( &sLTP[ start_idx ], &NSQ->xq[ start_idx + k * psEncC->subfr_length ], A_Q12, psEncC->ltp_mem_length - start_idx, psEncC->predictLPCOrder, psEncC->arch ); NSQ->rewhite_flag = 1; NSQ->sLTP_buf_idx = psEncC->ltp_mem_length; } } silk_nsq_scale_states( psEncC, NSQ, x_Q3, x_sc_Q10, sLTP, sLTP_Q15, k, LTP_scale_Q14, Gains_Q16, pitchL, psIndices->signalType ); silk_noise_shape_quantizer( NSQ, psIndices->signalType, x_sc_Q10, pulses, pxq, sLTP_Q15, A_Q12, B_Q14, AR_shp_Q13, lag, HarmShapeFIRPacked_Q14, Tilt_Q14[ k ], LF_shp_Q14[ k ], Gains_Q16[ k ], Lambda_Q10, offset_Q10, psEncC->subfr_length, psEncC->shapingLPCOrder, psEncC->predictLPCOrder ); x_Q3 += psEncC->subfr_length; pulses += psEncC->subfr_length; pxq += psEncC->subfr_length; } /* Update lagPrev for next frame */ NSQ->lagPrev = pitchL[ psEncC->nb_subfr - 1 ]; /* Save quantized speech and noise shaping signals */ /* DEBUG_STORE_DATA( enc.pcm, &NSQ->xq[ psEncC->ltp_mem_length ], psEncC->frame_length * sizeof( opus_int16 ) ) */ silk_memmove( NSQ->xq, &NSQ->xq[ psEncC->frame_length ], psEncC->ltp_mem_length * sizeof( opus_int16 ) ); silk_memmove( NSQ->sLTP_shp_Q14, &NSQ->sLTP_shp_Q14[ psEncC->frame_length ], psEncC->ltp_mem_length * sizeof( opus_int32 ) ); RESTORE_STACK; } /***********************************/ /* silk_noise_shape_quantizer */ /***********************************/ #if !defined(OPUS_X86_MAY_HAVE_SSE4_1) static OPUS_INLINE #endif void silk_noise_shape_quantizer( silk_nsq_state *NSQ, /* I/O NSQ state */ opus_int signalType, /* I Signal type */ const opus_int32 x_sc_Q10[], /* I */ opus_int8 pulses[], /* O */ opus_int16 xq[], /* O */ opus_int32 sLTP_Q15[], /* I/O LTP state */ const opus_int16 a_Q12[], /* I Short term prediction coefs */ const opus_int16 b_Q14[], /* I Long term prediction coefs */ const opus_int16 AR_shp_Q13[], /* I Noise shaping AR coefs */ opus_int lag, /* I Pitch lag */ opus_int32 HarmShapeFIRPacked_Q14, /* I */ opus_int Tilt_Q14, /* I Spectral tilt */ opus_int32 LF_shp_Q14, /* I */ opus_int32 Gain_Q16, /* I */ opus_int Lambda_Q10, /* I */ opus_int offset_Q10, /* I */ opus_int length, /* I Input length */ opus_int shapingLPCOrder, /* I Noise shaping AR filter order */ opus_int predictLPCOrder /* I Prediction filter order */ ) { opus_int i, j; opus_int32 LTP_pred_Q13, LPC_pred_Q10, n_AR_Q12, n_LTP_Q13; opus_int32 n_LF_Q12, r_Q10, rr_Q10, q1_Q0, q1_Q10, q2_Q10, rd1_Q20, rd2_Q20; opus_int32 exc_Q14, LPC_exc_Q14, xq_Q14, Gain_Q10; opus_int32 tmp1, tmp2, sLF_AR_shp_Q14; opus_int32 *psLPC_Q14, *shp_lag_ptr, *pred_lag_ptr; shp_lag_ptr = &NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - lag + HARM_SHAPE_FIR_TAPS / 2 ]; pred_lag_ptr = &sLTP_Q15[ NSQ->sLTP_buf_idx - lag + LTP_ORDER / 2 ]; Gain_Q10 = silk_RSHIFT( Gain_Q16, 6 ); /* Set up short term AR state */ psLPC_Q14 = &NSQ->sLPC_Q14[ NSQ_LPC_BUF_LENGTH - 1 ]; for( i = 0; i < length; i++ ) { /* Generate dither */ NSQ->rand_seed = silk_RAND( NSQ->rand_seed ); /* Short-term prediction */ silk_assert( predictLPCOrder == 10 || predictLPCOrder == 16 ); /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ LPC_pred_Q10 = silk_RSHIFT( predictLPCOrder, 1 ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ 0 ], a_Q12[ 0 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ -1 ], a_Q12[ 1 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ -2 ], a_Q12[ 2 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ -3 ], a_Q12[ 3 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ -4 ], a_Q12[ 4 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ -5 ], a_Q12[ 5 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ -6 ], a_Q12[ 6 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ -7 ], a_Q12[ 7 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ -8 ], a_Q12[ 8 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ -9 ], a_Q12[ 9 ] ); if( predictLPCOrder == 16 ) { LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ -10 ], a_Q12[ 10 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ -11 ], a_Q12[ 11 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ -12 ], a_Q12[ 12 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ -13 ], a_Q12[ 13 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ -14 ], a_Q12[ 14 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, psLPC_Q14[ -15 ], a_Q12[ 15 ] ); } /* Long-term prediction */ if( signalType == TYPE_VOICED ) { /* Unrolled loop */ /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ LTP_pred_Q13 = 2; LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[ 0 ], b_Q14[ 0 ] ); LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[ -1 ], b_Q14[ 1 ] ); LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[ -2 ], b_Q14[ 2 ] ); LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[ -3 ], b_Q14[ 3 ] ); LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[ -4 ], b_Q14[ 4 ] ); pred_lag_ptr++; } else { LTP_pred_Q13 = 0; } /* Noise shape feedback */ silk_assert( ( shapingLPCOrder & 1 ) == 0 ); /* check that order is even */ tmp2 = psLPC_Q14[ 0 ]; tmp1 = NSQ->sAR2_Q14[ 0 ]; NSQ->sAR2_Q14[ 0 ] = tmp2; n_AR_Q12 = silk_RSHIFT( shapingLPCOrder, 1 ); n_AR_Q12 = silk_SMLAWB( n_AR_Q12, tmp2, AR_shp_Q13[ 0 ] ); for( j = 2; j < shapingLPCOrder; j += 2 ) { tmp2 = NSQ->sAR2_Q14[ j - 1 ]; NSQ->sAR2_Q14[ j - 1 ] = tmp1; n_AR_Q12 = silk_SMLAWB( n_AR_Q12, tmp1, AR_shp_Q13[ j - 1 ] ); tmp1 = NSQ->sAR2_Q14[ j + 0 ]; NSQ->sAR2_Q14[ j + 0 ] = tmp2; n_AR_Q12 = silk_SMLAWB( n_AR_Q12, tmp2, AR_shp_Q13[ j ] ); } NSQ->sAR2_Q14[ shapingLPCOrder - 1 ] = tmp1; n_AR_Q12 = silk_SMLAWB( n_AR_Q12, tmp1, AR_shp_Q13[ shapingLPCOrder - 1 ] ); n_AR_Q12 = silk_LSHIFT32( n_AR_Q12, 1 ); /* Q11 -> Q12 */ n_AR_Q12 = silk_SMLAWB( n_AR_Q12, NSQ->sLF_AR_shp_Q14, Tilt_Q14 ); n_LF_Q12 = silk_SMULWB( NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - 1 ], LF_shp_Q14 ); n_LF_Q12 = silk_SMLAWT( n_LF_Q12, NSQ->sLF_AR_shp_Q14, LF_shp_Q14 ); silk_assert( lag > 0 || signalType != TYPE_VOICED ); /* Combine prediction and noise shaping signals */ tmp1 = silk_SUB32( silk_LSHIFT32( LPC_pred_Q10, 2 ), n_AR_Q12 ); /* Q12 */ tmp1 = silk_SUB32( tmp1, n_LF_Q12 ); /* Q12 */ if( lag > 0 ) { /* Symmetric, packed FIR coefficients */ n_LTP_Q13 = silk_SMULWB( silk_ADD32( shp_lag_ptr[ 0 ], shp_lag_ptr[ -2 ] ), HarmShapeFIRPacked_Q14 ); n_LTP_Q13 = silk_SMLAWT( n_LTP_Q13, shp_lag_ptr[ -1 ], HarmShapeFIRPacked_Q14 ); n_LTP_Q13 = silk_LSHIFT( n_LTP_Q13, 1 ); shp_lag_ptr++; tmp2 = silk_SUB32( LTP_pred_Q13, n_LTP_Q13 ); /* Q13 */ tmp1 = silk_ADD_LSHIFT32( tmp2, tmp1, 1 ); /* Q13 */ tmp1 = silk_RSHIFT_ROUND( tmp1, 3 ); /* Q10 */ } else { tmp1 = silk_RSHIFT_ROUND( tmp1, 2 ); /* Q10 */ } r_Q10 = silk_SUB32( x_sc_Q10[ i ], tmp1 ); /* residual error Q10 */ /* Flip sign depending on dither */ if ( NSQ->rand_seed < 0 ) { r_Q10 = -r_Q10; } r_Q10 = silk_LIMIT_32( r_Q10, -(31 << 10), 30 << 10 ); /* Find two quantization level candidates and measure their rate-distortion */ q1_Q10 = silk_SUB32( r_Q10, offset_Q10 ); q1_Q0 = silk_RSHIFT( q1_Q10, 10 ); if( q1_Q0 > 0 ) { q1_Q10 = silk_SUB32( silk_LSHIFT( q1_Q0, 10 ), QUANT_LEVEL_ADJUST_Q10 ); q1_Q10 = silk_ADD32( q1_Q10, offset_Q10 ); q2_Q10 = silk_ADD32( q1_Q10, 1024 ); rd1_Q20 = silk_SMULBB( q1_Q10, Lambda_Q10 ); rd2_Q20 = silk_SMULBB( q2_Q10, Lambda_Q10 ); } else if( q1_Q0 == 0 ) { q1_Q10 = offset_Q10; q2_Q10 = silk_ADD32( q1_Q10, 1024 - QUANT_LEVEL_ADJUST_Q10 ); rd1_Q20 = silk_SMULBB( q1_Q10, Lambda_Q10 ); rd2_Q20 = silk_SMULBB( q2_Q10, Lambda_Q10 ); } else if( q1_Q0 == -1 ) { q2_Q10 = offset_Q10; q1_Q10 = silk_SUB32( q2_Q10, 1024 - QUANT_LEVEL_ADJUST_Q10 ); rd1_Q20 = silk_SMULBB( -q1_Q10, Lambda_Q10 ); rd2_Q20 = silk_SMULBB( q2_Q10, Lambda_Q10 ); } else { /* Q1_Q0 < -1 */ q1_Q10 = silk_ADD32( silk_LSHIFT( q1_Q0, 10 ), QUANT_LEVEL_ADJUST_Q10 ); q1_Q10 = silk_ADD32( q1_Q10, offset_Q10 ); q2_Q10 = silk_ADD32( q1_Q10, 1024 ); rd1_Q20 = silk_SMULBB( -q1_Q10, Lambda_Q10 ); rd2_Q20 = silk_SMULBB( -q2_Q10, Lambda_Q10 ); } rr_Q10 = silk_SUB32( r_Q10, q1_Q10 ); rd1_Q20 = silk_SMLABB( rd1_Q20, rr_Q10, rr_Q10 ); rr_Q10 = silk_SUB32( r_Q10, q2_Q10 ); rd2_Q20 = silk_SMLABB( rd2_Q20, rr_Q10, rr_Q10 ); if( rd2_Q20 < rd1_Q20 ) { q1_Q10 = q2_Q10; } pulses[ i ] = (opus_int8)silk_RSHIFT_ROUND( q1_Q10, 10 ); /* Excitation */ exc_Q14 = silk_LSHIFT( q1_Q10, 4 ); if ( NSQ->rand_seed < 0 ) { exc_Q14 = -exc_Q14; } /* Add predictions */ LPC_exc_Q14 = silk_ADD_LSHIFT32( exc_Q14, LTP_pred_Q13, 1 ); xq_Q14 = silk_ADD_LSHIFT32( LPC_exc_Q14, LPC_pred_Q10, 4 ); /* Scale XQ back to normal level before saving */ xq[ i ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_SMULWW( xq_Q14, Gain_Q10 ), 8 ) ); /* Update states */ psLPC_Q14++; *psLPC_Q14 = xq_Q14; sLF_AR_shp_Q14 = silk_SUB_LSHIFT32( xq_Q14, n_AR_Q12, 2 ); NSQ->sLF_AR_shp_Q14 = sLF_AR_shp_Q14; NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx ] = silk_SUB_LSHIFT32( sLF_AR_shp_Q14, n_LF_Q12, 2 ); sLTP_Q15[ NSQ->sLTP_buf_idx ] = silk_LSHIFT( LPC_exc_Q14, 1 ); NSQ->sLTP_shp_buf_idx++; NSQ->sLTP_buf_idx++; /* Make dither dependent on quantized signal */ NSQ->rand_seed = silk_ADD32_ovflw( NSQ->rand_seed, pulses[ i ] ); } /* Update LPC synth buffer */ silk_memcpy( NSQ->sLPC_Q14, &NSQ->sLPC_Q14[ length ], NSQ_LPC_BUF_LENGTH * sizeof( opus_int32 ) ); } static OPUS_INLINE void silk_nsq_scale_states( const silk_encoder_state *psEncC, /* I Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ const opus_int32 x_Q3[], /* I input in Q3 */ opus_int32 x_sc_Q10[], /* O input scaled with 1/Gain */ const opus_int16 sLTP[], /* I re-whitened LTP state in Q0 */ opus_int32 sLTP_Q15[], /* O LTP state matching scaled input */ opus_int subfr, /* I subframe number */ const opus_int LTP_scale_Q14, /* I */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lag */ const opus_int signal_type /* I Signal type */ ) { opus_int i, lag; opus_int32 gain_adj_Q16, inv_gain_Q31, inv_gain_Q23; lag = pitchL[ subfr ]; inv_gain_Q31 = silk_INVERSE32_varQ( silk_max( Gains_Q16[ subfr ], 1 ), 47 ); silk_assert( inv_gain_Q31 != 0 ); /* Calculate gain adjustment factor */ if( Gains_Q16[ subfr ] != NSQ->prev_gain_Q16 ) { gain_adj_Q16 = silk_DIV32_varQ( NSQ->prev_gain_Q16, Gains_Q16[ subfr ], 16 ); } else { gain_adj_Q16 = (opus_int32)1 << 16; } /* Scale input */ inv_gain_Q23 = silk_RSHIFT_ROUND( inv_gain_Q31, 8 ); for( i = 0; i < psEncC->subfr_length; i++ ) { x_sc_Q10[ i ] = silk_SMULWW( x_Q3[ i ], inv_gain_Q23 ); } /* Save inverse gain */ NSQ->prev_gain_Q16 = Gains_Q16[ subfr ]; /* After rewhitening the LTP state is un-scaled, so scale with inv_gain_Q16 */ if( NSQ->rewhite_flag ) { if( subfr == 0 ) { /* Do LTP downscaling */ inv_gain_Q31 = silk_LSHIFT( silk_SMULWB( inv_gain_Q31, LTP_scale_Q14 ), 2 ); } for( i = NSQ->sLTP_buf_idx - lag - LTP_ORDER / 2; i < NSQ->sLTP_buf_idx; i++ ) { silk_assert( i < MAX_FRAME_LENGTH ); sLTP_Q15[ i ] = silk_SMULWB( inv_gain_Q31, sLTP[ i ] ); } } /* Adjust for changing gain */ if( gain_adj_Q16 != (opus_int32)1 << 16 ) { /* Scale long-term shaping state */ for( i = NSQ->sLTP_shp_buf_idx - psEncC->ltp_mem_length; i < NSQ->sLTP_shp_buf_idx; i++ ) { NSQ->sLTP_shp_Q14[ i ] = silk_SMULWW( gain_adj_Q16, NSQ->sLTP_shp_Q14[ i ] ); } /* Scale long-term prediction state */ if( signal_type == TYPE_VOICED && NSQ->rewhite_flag == 0 ) { for( i = NSQ->sLTP_buf_idx - lag - LTP_ORDER / 2; i < NSQ->sLTP_buf_idx; i++ ) { sLTP_Q15[ i ] = silk_SMULWW( gain_adj_Q16, sLTP_Q15[ i ] ); } } NSQ->sLF_AR_shp_Q14 = silk_SMULWW( gain_adj_Q16, NSQ->sLF_AR_shp_Q14 ); /* Scale short-term prediction and shaping states */ for( i = 0; i < NSQ_LPC_BUF_LENGTH; i++ ) { NSQ->sLPC_Q14[ i ] = silk_SMULWW( gain_adj_Q16, NSQ->sLPC_Q14[ i ] ); } for( i = 0; i < MAX_SHAPE_LPC_ORDER; i++ ) { NSQ->sAR2_Q14[ i ] = silk_SMULWW( gain_adj_Q16, NSQ->sAR2_Q14[ i ] ); } } } ================================================ FILE: deps/pjsip/third_party/opus/silk/NSQ_del_dec.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #include "stack_alloc.h" typedef struct { opus_int32 sLPC_Q14[ MAX_SUB_FRAME_LENGTH + NSQ_LPC_BUF_LENGTH ]; opus_int32 RandState[ DECISION_DELAY ]; opus_int32 Q_Q10[ DECISION_DELAY ]; opus_int32 Xq_Q14[ DECISION_DELAY ]; opus_int32 Pred_Q15[ DECISION_DELAY ]; opus_int32 Shape_Q14[ DECISION_DELAY ]; opus_int32 sAR2_Q14[ MAX_SHAPE_LPC_ORDER ]; opus_int32 LF_AR_Q14; opus_int32 Seed; opus_int32 SeedInit; opus_int32 RD_Q10; } NSQ_del_dec_struct; typedef struct { opus_int32 Q_Q10; opus_int32 RD_Q10; opus_int32 xq_Q14; opus_int32 LF_AR_Q14; opus_int32 sLTP_shp_Q14; opus_int32 LPC_exc_Q14; } NSQ_sample_struct; typedef NSQ_sample_struct NSQ_sample_pair[ 2 ]; #if defined(MIPSr1_ASM) #include "mips/NSQ_del_dec_mipsr1.h" #endif static OPUS_INLINE void silk_nsq_del_dec_scale_states( const silk_encoder_state *psEncC, /* I Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ NSQ_del_dec_struct psDelDec[], /* I/O Delayed decision states */ const opus_int32 x_Q3[], /* I Input in Q3 */ opus_int32 x_sc_Q10[], /* O Input scaled with 1/Gain in Q10 */ const opus_int16 sLTP[], /* I Re-whitened LTP state in Q0 */ opus_int32 sLTP_Q15[], /* O LTP state matching scaled input */ opus_int subfr, /* I Subframe number */ opus_int nStatesDelayedDecision, /* I Number of del dec states */ const opus_int LTP_scale_Q14, /* I LTP state scaling */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lag */ const opus_int signal_type, /* I Signal type */ const opus_int decisionDelay /* I Decision delay */ ); /******************************************/ /* Noise shape quantizer for one subframe */ /******************************************/ static OPUS_INLINE void silk_noise_shape_quantizer_del_dec( silk_nsq_state *NSQ, /* I/O NSQ state */ NSQ_del_dec_struct psDelDec[], /* I/O Delayed decision states */ opus_int signalType, /* I Signal type */ const opus_int32 x_Q10[], /* I */ opus_int8 pulses[], /* O */ opus_int16 xq[], /* O */ opus_int32 sLTP_Q15[], /* I/O LTP filter state */ opus_int32 delayedGain_Q10[], /* I/O Gain delay buffer */ const opus_int16 a_Q12[], /* I Short term prediction coefs */ const opus_int16 b_Q14[], /* I Long term prediction coefs */ const opus_int16 AR_shp_Q13[], /* I Noise shaping coefs */ opus_int lag, /* I Pitch lag */ opus_int32 HarmShapeFIRPacked_Q14, /* I */ opus_int Tilt_Q14, /* I Spectral tilt */ opus_int32 LF_shp_Q14, /* I */ opus_int32 Gain_Q16, /* I */ opus_int Lambda_Q10, /* I */ opus_int offset_Q10, /* I */ opus_int length, /* I Input length */ opus_int subfr, /* I Subframe number */ opus_int shapingLPCOrder, /* I Shaping LPC filter order */ opus_int predictLPCOrder, /* I Prediction filter order */ opus_int warping_Q16, /* I */ opus_int nStatesDelayedDecision, /* I Number of states in decision tree */ opus_int *smpl_buf_idx, /* I Index to newest samples in buffers */ opus_int decisionDelay /* I */ ); void silk_NSQ_del_dec_c( const silk_encoder_state *psEncC, /* I/O Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ SideInfoIndices *psIndices, /* I/O Quantization Indices */ const opus_int32 x_Q3[], /* I Prefiltered input signal */ opus_int8 pulses[], /* O Quantized pulse signal */ const opus_int16 PredCoef_Q12[ 2 * MAX_LPC_ORDER ], /* I Short term prediction coefs */ const opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ], /* I Long term prediction coefs */ const opus_int16 AR2_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ], /* I Noise shaping coefs */ const opus_int HarmShapeGain_Q14[ MAX_NB_SUBFR ], /* I Long term shaping coefs */ const opus_int Tilt_Q14[ MAX_NB_SUBFR ], /* I Spectral tilt */ const opus_int32 LF_shp_Q14[ MAX_NB_SUBFR ], /* I Low frequency shaping coefs */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I Quantization step sizes */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lags */ const opus_int Lambda_Q10, /* I Rate/distortion tradeoff */ const opus_int LTP_scale_Q14 /* I LTP state scaling */ ) { opus_int i, k, lag, start_idx, LSF_interpolation_flag, Winner_ind, subfr; opus_int last_smple_idx, smpl_buf_idx, decisionDelay; const opus_int16 *A_Q12, *B_Q14, *AR_shp_Q13; opus_int16 *pxq; VARDECL( opus_int32, sLTP_Q15 ); VARDECL( opus_int16, sLTP ); opus_int32 HarmShapeFIRPacked_Q14; opus_int offset_Q10; opus_int32 RDmin_Q10, Gain_Q10; VARDECL( opus_int32, x_sc_Q10 ); VARDECL( opus_int32, delayedGain_Q10 ); VARDECL( NSQ_del_dec_struct, psDelDec ); NSQ_del_dec_struct *psDD; SAVE_STACK; /* Set unvoiced lag to the previous one, overwrite later for voiced */ lag = NSQ->lagPrev; silk_assert( NSQ->prev_gain_Q16 != 0 ); /* Initialize delayed decision states */ ALLOC( psDelDec, psEncC->nStatesDelayedDecision, NSQ_del_dec_struct ); silk_memset( psDelDec, 0, psEncC->nStatesDelayedDecision * sizeof( NSQ_del_dec_struct ) ); for( k = 0; k < psEncC->nStatesDelayedDecision; k++ ) { psDD = &psDelDec[ k ]; psDD->Seed = ( k + psIndices->Seed ) & 3; psDD->SeedInit = psDD->Seed; psDD->RD_Q10 = 0; psDD->LF_AR_Q14 = NSQ->sLF_AR_shp_Q14; psDD->Shape_Q14[ 0 ] = NSQ->sLTP_shp_Q14[ psEncC->ltp_mem_length - 1 ]; silk_memcpy( psDD->sLPC_Q14, NSQ->sLPC_Q14, NSQ_LPC_BUF_LENGTH * sizeof( opus_int32 ) ); silk_memcpy( psDD->sAR2_Q14, NSQ->sAR2_Q14, sizeof( NSQ->sAR2_Q14 ) ); } offset_Q10 = silk_Quantization_Offsets_Q10[ psIndices->signalType >> 1 ][ psIndices->quantOffsetType ]; smpl_buf_idx = 0; /* index of oldest samples */ decisionDelay = silk_min_int( DECISION_DELAY, psEncC->subfr_length ); /* For voiced frames limit the decision delay to lower than the pitch lag */ if( psIndices->signalType == TYPE_VOICED ) { for( k = 0; k < psEncC->nb_subfr; k++ ) { decisionDelay = silk_min_int( decisionDelay, pitchL[ k ] - LTP_ORDER / 2 - 1 ); } } else { if( lag > 0 ) { decisionDelay = silk_min_int( decisionDelay, lag - LTP_ORDER / 2 - 1 ); } } if( psIndices->NLSFInterpCoef_Q2 == 4 ) { LSF_interpolation_flag = 0; } else { LSF_interpolation_flag = 1; } ALLOC( sLTP_Q15, psEncC->ltp_mem_length + psEncC->frame_length, opus_int32 ); ALLOC( sLTP, psEncC->ltp_mem_length + psEncC->frame_length, opus_int16 ); ALLOC( x_sc_Q10, psEncC->subfr_length, opus_int32 ); ALLOC( delayedGain_Q10, DECISION_DELAY, opus_int32 ); /* Set up pointers to start of sub frame */ pxq = &NSQ->xq[ psEncC->ltp_mem_length ]; NSQ->sLTP_shp_buf_idx = psEncC->ltp_mem_length; NSQ->sLTP_buf_idx = psEncC->ltp_mem_length; subfr = 0; for( k = 0; k < psEncC->nb_subfr; k++ ) { A_Q12 = &PredCoef_Q12[ ( ( k >> 1 ) | ( 1 - LSF_interpolation_flag ) ) * MAX_LPC_ORDER ]; B_Q14 = <PCoef_Q14[ k * LTP_ORDER ]; AR_shp_Q13 = &AR2_Q13[ k * MAX_SHAPE_LPC_ORDER ]; /* Noise shape parameters */ silk_assert( HarmShapeGain_Q14[ k ] >= 0 ); HarmShapeFIRPacked_Q14 = silk_RSHIFT( HarmShapeGain_Q14[ k ], 2 ); HarmShapeFIRPacked_Q14 |= silk_LSHIFT( (opus_int32)silk_RSHIFT( HarmShapeGain_Q14[ k ], 1 ), 16 ); NSQ->rewhite_flag = 0; if( psIndices->signalType == TYPE_VOICED ) { /* Voiced */ lag = pitchL[ k ]; /* Re-whitening */ if( ( k & ( 3 - silk_LSHIFT( LSF_interpolation_flag, 1 ) ) ) == 0 ) { if( k == 2 ) { /* RESET DELAYED DECISIONS */ /* Find winner */ RDmin_Q10 = psDelDec[ 0 ].RD_Q10; Winner_ind = 0; for( i = 1; i < psEncC->nStatesDelayedDecision; i++ ) { if( psDelDec[ i ].RD_Q10 < RDmin_Q10 ) { RDmin_Q10 = psDelDec[ i ].RD_Q10; Winner_ind = i; } } for( i = 0; i < psEncC->nStatesDelayedDecision; i++ ) { if( i != Winner_ind ) { psDelDec[ i ].RD_Q10 += ( silk_int32_MAX >> 4 ); silk_assert( psDelDec[ i ].RD_Q10 >= 0 ); } } /* Copy final part of signals from winner state to output and long-term filter states */ psDD = &psDelDec[ Winner_ind ]; last_smple_idx = smpl_buf_idx + decisionDelay; for( i = 0; i < decisionDelay; i++ ) { last_smple_idx = ( last_smple_idx - 1 ) & DECISION_DELAY_MASK; pulses[ i - decisionDelay ] = (opus_int8)silk_RSHIFT_ROUND( psDD->Q_Q10[ last_smple_idx ], 10 ); pxq[ i - decisionDelay ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_SMULWW( psDD->Xq_Q14[ last_smple_idx ], Gains_Q16[ 1 ] ), 14 ) ); NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - decisionDelay + i ] = psDD->Shape_Q14[ last_smple_idx ]; } subfr = 0; } /* Rewhiten with new A coefs */ start_idx = psEncC->ltp_mem_length - lag - psEncC->predictLPCOrder - LTP_ORDER / 2; silk_assert( start_idx > 0 ); silk_LPC_analysis_filter( &sLTP[ start_idx ], &NSQ->xq[ start_idx + k * psEncC->subfr_length ], A_Q12, psEncC->ltp_mem_length - start_idx, psEncC->predictLPCOrder, psEncC->arch ); NSQ->sLTP_buf_idx = psEncC->ltp_mem_length; NSQ->rewhite_flag = 1; } } silk_nsq_del_dec_scale_states( psEncC, NSQ, psDelDec, x_Q3, x_sc_Q10, sLTP, sLTP_Q15, k, psEncC->nStatesDelayedDecision, LTP_scale_Q14, Gains_Q16, pitchL, psIndices->signalType, decisionDelay ); silk_noise_shape_quantizer_del_dec( NSQ, psDelDec, psIndices->signalType, x_sc_Q10, pulses, pxq, sLTP_Q15, delayedGain_Q10, A_Q12, B_Q14, AR_shp_Q13, lag, HarmShapeFIRPacked_Q14, Tilt_Q14[ k ], LF_shp_Q14[ k ], Gains_Q16[ k ], Lambda_Q10, offset_Q10, psEncC->subfr_length, subfr++, psEncC->shapingLPCOrder, psEncC->predictLPCOrder, psEncC->warping_Q16, psEncC->nStatesDelayedDecision, &smpl_buf_idx, decisionDelay ); x_Q3 += psEncC->subfr_length; pulses += psEncC->subfr_length; pxq += psEncC->subfr_length; } /* Find winner */ RDmin_Q10 = psDelDec[ 0 ].RD_Q10; Winner_ind = 0; for( k = 1; k < psEncC->nStatesDelayedDecision; k++ ) { if( psDelDec[ k ].RD_Q10 < RDmin_Q10 ) { RDmin_Q10 = psDelDec[ k ].RD_Q10; Winner_ind = k; } } /* Copy final part of signals from winner state to output and long-term filter states */ psDD = &psDelDec[ Winner_ind ]; psIndices->Seed = psDD->SeedInit; last_smple_idx = smpl_buf_idx + decisionDelay; Gain_Q10 = silk_RSHIFT32( Gains_Q16[ psEncC->nb_subfr - 1 ], 6 ); for( i = 0; i < decisionDelay; i++ ) { last_smple_idx = ( last_smple_idx - 1 ) & DECISION_DELAY_MASK; pulses[ i - decisionDelay ] = (opus_int8)silk_RSHIFT_ROUND( psDD->Q_Q10[ last_smple_idx ], 10 ); pxq[ i - decisionDelay ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_SMULWW( psDD->Xq_Q14[ last_smple_idx ], Gain_Q10 ), 8 ) ); NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - decisionDelay + i ] = psDD->Shape_Q14[ last_smple_idx ]; } silk_memcpy( NSQ->sLPC_Q14, &psDD->sLPC_Q14[ psEncC->subfr_length ], NSQ_LPC_BUF_LENGTH * sizeof( opus_int32 ) ); silk_memcpy( NSQ->sAR2_Q14, psDD->sAR2_Q14, sizeof( psDD->sAR2_Q14 ) ); /* Update states */ NSQ->sLF_AR_shp_Q14 = psDD->LF_AR_Q14; NSQ->lagPrev = pitchL[ psEncC->nb_subfr - 1 ]; /* Save quantized speech signal */ /* DEBUG_STORE_DATA( enc.pcm, &NSQ->xq[psEncC->ltp_mem_length], psEncC->frame_length * sizeof( opus_int16 ) ) */ silk_memmove( NSQ->xq, &NSQ->xq[ psEncC->frame_length ], psEncC->ltp_mem_length * sizeof( opus_int16 ) ); silk_memmove( NSQ->sLTP_shp_Q14, &NSQ->sLTP_shp_Q14[ psEncC->frame_length ], psEncC->ltp_mem_length * sizeof( opus_int32 ) ); RESTORE_STACK; } /******************************************/ /* Noise shape quantizer for one subframe */ /******************************************/ #ifndef OVERRIDE_silk_noise_shape_quantizer_del_dec static OPUS_INLINE void silk_noise_shape_quantizer_del_dec( silk_nsq_state *NSQ, /* I/O NSQ state */ NSQ_del_dec_struct psDelDec[], /* I/O Delayed decision states */ opus_int signalType, /* I Signal type */ const opus_int32 x_Q10[], /* I */ opus_int8 pulses[], /* O */ opus_int16 xq[], /* O */ opus_int32 sLTP_Q15[], /* I/O LTP filter state */ opus_int32 delayedGain_Q10[], /* I/O Gain delay buffer */ const opus_int16 a_Q12[], /* I Short term prediction coefs */ const opus_int16 b_Q14[], /* I Long term prediction coefs */ const opus_int16 AR_shp_Q13[], /* I Noise shaping coefs */ opus_int lag, /* I Pitch lag */ opus_int32 HarmShapeFIRPacked_Q14, /* I */ opus_int Tilt_Q14, /* I Spectral tilt */ opus_int32 LF_shp_Q14, /* I */ opus_int32 Gain_Q16, /* I */ opus_int Lambda_Q10, /* I */ opus_int offset_Q10, /* I */ opus_int length, /* I Input length */ opus_int subfr, /* I Subframe number */ opus_int shapingLPCOrder, /* I Shaping LPC filter order */ opus_int predictLPCOrder, /* I Prediction filter order */ opus_int warping_Q16, /* I */ opus_int nStatesDelayedDecision, /* I Number of states in decision tree */ opus_int *smpl_buf_idx, /* I Index to newest samples in buffers */ opus_int decisionDelay /* I */ ) { opus_int i, j, k, Winner_ind, RDmin_ind, RDmax_ind, last_smple_idx; opus_int32 Winner_rand_state; opus_int32 LTP_pred_Q14, LPC_pred_Q14, n_AR_Q14, n_LTP_Q14; opus_int32 n_LF_Q14, r_Q10, rr_Q10, rd1_Q10, rd2_Q10, RDmin_Q10, RDmax_Q10; opus_int32 q1_Q0, q1_Q10, q2_Q10, exc_Q14, LPC_exc_Q14, xq_Q14, Gain_Q10; opus_int32 tmp1, tmp2, sLF_AR_shp_Q14; opus_int32 *pred_lag_ptr, *shp_lag_ptr, *psLPC_Q14; VARDECL( NSQ_sample_pair, psSampleState ); NSQ_del_dec_struct *psDD; NSQ_sample_struct *psSS; SAVE_STACK; silk_assert( nStatesDelayedDecision > 0 ); ALLOC( psSampleState, nStatesDelayedDecision, NSQ_sample_pair ); shp_lag_ptr = &NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - lag + HARM_SHAPE_FIR_TAPS / 2 ]; pred_lag_ptr = &sLTP_Q15[ NSQ->sLTP_buf_idx - lag + LTP_ORDER / 2 ]; Gain_Q10 = silk_RSHIFT( Gain_Q16, 6 ); for( i = 0; i < length; i++ ) { /* Perform common calculations used in all states */ /* Long-term prediction */ if( signalType == TYPE_VOICED ) { /* Unrolled loop */ /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ LTP_pred_Q14 = 2; LTP_pred_Q14 = silk_SMLAWB( LTP_pred_Q14, pred_lag_ptr[ 0 ], b_Q14[ 0 ] ); LTP_pred_Q14 = silk_SMLAWB( LTP_pred_Q14, pred_lag_ptr[ -1 ], b_Q14[ 1 ] ); LTP_pred_Q14 = silk_SMLAWB( LTP_pred_Q14, pred_lag_ptr[ -2 ], b_Q14[ 2 ] ); LTP_pred_Q14 = silk_SMLAWB( LTP_pred_Q14, pred_lag_ptr[ -3 ], b_Q14[ 3 ] ); LTP_pred_Q14 = silk_SMLAWB( LTP_pred_Q14, pred_lag_ptr[ -4 ], b_Q14[ 4 ] ); LTP_pred_Q14 = silk_LSHIFT( LTP_pred_Q14, 1 ); /* Q13 -> Q14 */ pred_lag_ptr++; } else { LTP_pred_Q14 = 0; } /* Long-term shaping */ if( lag > 0 ) { /* Symmetric, packed FIR coefficients */ n_LTP_Q14 = silk_SMULWB( silk_ADD32( shp_lag_ptr[ 0 ], shp_lag_ptr[ -2 ] ), HarmShapeFIRPacked_Q14 ); n_LTP_Q14 = silk_SMLAWT( n_LTP_Q14, shp_lag_ptr[ -1 ], HarmShapeFIRPacked_Q14 ); n_LTP_Q14 = silk_SUB_LSHIFT32( LTP_pred_Q14, n_LTP_Q14, 2 ); /* Q12 -> Q14 */ shp_lag_ptr++; } else { n_LTP_Q14 = 0; } for( k = 0; k < nStatesDelayedDecision; k++ ) { /* Delayed decision state */ psDD = &psDelDec[ k ]; /* Sample state */ psSS = psSampleState[ k ]; /* Generate dither */ psDD->Seed = silk_RAND( psDD->Seed ); /* Pointer used in short term prediction and shaping */ psLPC_Q14 = &psDD->sLPC_Q14[ NSQ_LPC_BUF_LENGTH - 1 + i ]; /* Short-term prediction */ silk_assert( predictLPCOrder == 10 || predictLPCOrder == 16 ); /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ LPC_pred_Q14 = silk_RSHIFT( predictLPCOrder, 1 ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ 0 ], a_Q12[ 0 ] ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -1 ], a_Q12[ 1 ] ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -2 ], a_Q12[ 2 ] ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -3 ], a_Q12[ 3 ] ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -4 ], a_Q12[ 4 ] ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -5 ], a_Q12[ 5 ] ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -6 ], a_Q12[ 6 ] ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -7 ], a_Q12[ 7 ] ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -8 ], a_Q12[ 8 ] ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -9 ], a_Q12[ 9 ] ); if( predictLPCOrder == 16 ) { LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -10 ], a_Q12[ 10 ] ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -11 ], a_Q12[ 11 ] ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -12 ], a_Q12[ 12 ] ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -13 ], a_Q12[ 13 ] ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -14 ], a_Q12[ 14 ] ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -15 ], a_Q12[ 15 ] ); } LPC_pred_Q14 = silk_LSHIFT( LPC_pred_Q14, 4 ); /* Q10 -> Q14 */ /* Noise shape feedback */ silk_assert( ( shapingLPCOrder & 1 ) == 0 ); /* check that order is even */ /* Output of lowpass section */ tmp2 = silk_SMLAWB( psLPC_Q14[ 0 ], psDD->sAR2_Q14[ 0 ], warping_Q16 ); /* Output of allpass section */ tmp1 = silk_SMLAWB( psDD->sAR2_Q14[ 0 ], psDD->sAR2_Q14[ 1 ] - tmp2, warping_Q16 ); psDD->sAR2_Q14[ 0 ] = tmp2; n_AR_Q14 = silk_RSHIFT( shapingLPCOrder, 1 ); n_AR_Q14 = silk_SMLAWB( n_AR_Q14, tmp2, AR_shp_Q13[ 0 ] ); /* Loop over allpass sections */ for( j = 2; j < shapingLPCOrder; j += 2 ) { /* Output of allpass section */ tmp2 = silk_SMLAWB( psDD->sAR2_Q14[ j - 1 ], psDD->sAR2_Q14[ j + 0 ] - tmp1, warping_Q16 ); psDD->sAR2_Q14[ j - 1 ] = tmp1; n_AR_Q14 = silk_SMLAWB( n_AR_Q14, tmp1, AR_shp_Q13[ j - 1 ] ); /* Output of allpass section */ tmp1 = silk_SMLAWB( psDD->sAR2_Q14[ j + 0 ], psDD->sAR2_Q14[ j + 1 ] - tmp2, warping_Q16 ); psDD->sAR2_Q14[ j + 0 ] = tmp2; n_AR_Q14 = silk_SMLAWB( n_AR_Q14, tmp2, AR_shp_Q13[ j ] ); } psDD->sAR2_Q14[ shapingLPCOrder - 1 ] = tmp1; n_AR_Q14 = silk_SMLAWB( n_AR_Q14, tmp1, AR_shp_Q13[ shapingLPCOrder - 1 ] ); n_AR_Q14 = silk_LSHIFT( n_AR_Q14, 1 ); /* Q11 -> Q12 */ n_AR_Q14 = silk_SMLAWB( n_AR_Q14, psDD->LF_AR_Q14, Tilt_Q14 ); /* Q12 */ n_AR_Q14 = silk_LSHIFT( n_AR_Q14, 2 ); /* Q12 -> Q14 */ n_LF_Q14 = silk_SMULWB( psDD->Shape_Q14[ *smpl_buf_idx ], LF_shp_Q14 ); /* Q12 */ n_LF_Q14 = silk_SMLAWT( n_LF_Q14, psDD->LF_AR_Q14, LF_shp_Q14 ); /* Q12 */ n_LF_Q14 = silk_LSHIFT( n_LF_Q14, 2 ); /* Q12 -> Q14 */ /* Input minus prediction plus noise feedback */ /* r = x[ i ] - LTP_pred - LPC_pred + n_AR + n_Tilt + n_LF + n_LTP */ tmp1 = silk_ADD32( n_AR_Q14, n_LF_Q14 ); /* Q14 */ tmp2 = silk_ADD32( n_LTP_Q14, LPC_pred_Q14 ); /* Q13 */ tmp1 = silk_SUB32( tmp2, tmp1 ); /* Q13 */ tmp1 = silk_RSHIFT_ROUND( tmp1, 4 ); /* Q10 */ r_Q10 = silk_SUB32( x_Q10[ i ], tmp1 ); /* residual error Q10 */ /* Flip sign depending on dither */ if ( psDD->Seed < 0 ) { r_Q10 = -r_Q10; } r_Q10 = silk_LIMIT_32( r_Q10, -(31 << 10), 30 << 10 ); /* Find two quantization level candidates and measure their rate-distortion */ q1_Q10 = silk_SUB32( r_Q10, offset_Q10 ); q1_Q0 = silk_RSHIFT( q1_Q10, 10 ); if( q1_Q0 > 0 ) { q1_Q10 = silk_SUB32( silk_LSHIFT( q1_Q0, 10 ), QUANT_LEVEL_ADJUST_Q10 ); q1_Q10 = silk_ADD32( q1_Q10, offset_Q10 ); q2_Q10 = silk_ADD32( q1_Q10, 1024 ); rd1_Q10 = silk_SMULBB( q1_Q10, Lambda_Q10 ); rd2_Q10 = silk_SMULBB( q2_Q10, Lambda_Q10 ); } else if( q1_Q0 == 0 ) { q1_Q10 = offset_Q10; q2_Q10 = silk_ADD32( q1_Q10, 1024 - QUANT_LEVEL_ADJUST_Q10 ); rd1_Q10 = silk_SMULBB( q1_Q10, Lambda_Q10 ); rd2_Q10 = silk_SMULBB( q2_Q10, Lambda_Q10 ); } else if( q1_Q0 == -1 ) { q2_Q10 = offset_Q10; q1_Q10 = silk_SUB32( q2_Q10, 1024 - QUANT_LEVEL_ADJUST_Q10 ); rd1_Q10 = silk_SMULBB( -q1_Q10, Lambda_Q10 ); rd2_Q10 = silk_SMULBB( q2_Q10, Lambda_Q10 ); } else { /* q1_Q0 < -1 */ q1_Q10 = silk_ADD32( silk_LSHIFT( q1_Q0, 10 ), QUANT_LEVEL_ADJUST_Q10 ); q1_Q10 = silk_ADD32( q1_Q10, offset_Q10 ); q2_Q10 = silk_ADD32( q1_Q10, 1024 ); rd1_Q10 = silk_SMULBB( -q1_Q10, Lambda_Q10 ); rd2_Q10 = silk_SMULBB( -q2_Q10, Lambda_Q10 ); } rr_Q10 = silk_SUB32( r_Q10, q1_Q10 ); rd1_Q10 = silk_RSHIFT( silk_SMLABB( rd1_Q10, rr_Q10, rr_Q10 ), 10 ); rr_Q10 = silk_SUB32( r_Q10, q2_Q10 ); rd2_Q10 = silk_RSHIFT( silk_SMLABB( rd2_Q10, rr_Q10, rr_Q10 ), 10 ); if( rd1_Q10 < rd2_Q10 ) { psSS[ 0 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd1_Q10 ); psSS[ 1 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd2_Q10 ); psSS[ 0 ].Q_Q10 = q1_Q10; psSS[ 1 ].Q_Q10 = q2_Q10; } else { psSS[ 0 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd2_Q10 ); psSS[ 1 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd1_Q10 ); psSS[ 0 ].Q_Q10 = q2_Q10; psSS[ 1 ].Q_Q10 = q1_Q10; } /* Update states for best quantization */ /* Quantized excitation */ exc_Q14 = silk_LSHIFT32( psSS[ 0 ].Q_Q10, 4 ); if ( psDD->Seed < 0 ) { exc_Q14 = -exc_Q14; } /* Add predictions */ LPC_exc_Q14 = silk_ADD32( exc_Q14, LTP_pred_Q14 ); xq_Q14 = silk_ADD32( LPC_exc_Q14, LPC_pred_Q14 ); /* Update states */ sLF_AR_shp_Q14 = silk_SUB32( xq_Q14, n_AR_Q14 ); psSS[ 0 ].sLTP_shp_Q14 = silk_SUB32( sLF_AR_shp_Q14, n_LF_Q14 ); psSS[ 0 ].LF_AR_Q14 = sLF_AR_shp_Q14; psSS[ 0 ].LPC_exc_Q14 = LPC_exc_Q14; psSS[ 0 ].xq_Q14 = xq_Q14; /* Update states for second best quantization */ /* Quantized excitation */ exc_Q14 = silk_LSHIFT32( psSS[ 1 ].Q_Q10, 4 ); if ( psDD->Seed < 0 ) { exc_Q14 = -exc_Q14; } /* Add predictions */ LPC_exc_Q14 = silk_ADD32( exc_Q14, LTP_pred_Q14 ); xq_Q14 = silk_ADD32( LPC_exc_Q14, LPC_pred_Q14 ); /* Update states */ sLF_AR_shp_Q14 = silk_SUB32( xq_Q14, n_AR_Q14 ); psSS[ 1 ].sLTP_shp_Q14 = silk_SUB32( sLF_AR_shp_Q14, n_LF_Q14 ); psSS[ 1 ].LF_AR_Q14 = sLF_AR_shp_Q14; psSS[ 1 ].LPC_exc_Q14 = LPC_exc_Q14; psSS[ 1 ].xq_Q14 = xq_Q14; } *smpl_buf_idx = ( *smpl_buf_idx - 1 ) & DECISION_DELAY_MASK; /* Index to newest samples */ last_smple_idx = ( *smpl_buf_idx + decisionDelay ) & DECISION_DELAY_MASK; /* Index to decisionDelay old samples */ /* Find winner */ RDmin_Q10 = psSampleState[ 0 ][ 0 ].RD_Q10; Winner_ind = 0; for( k = 1; k < nStatesDelayedDecision; k++ ) { if( psSampleState[ k ][ 0 ].RD_Q10 < RDmin_Q10 ) { RDmin_Q10 = psSampleState[ k ][ 0 ].RD_Q10; Winner_ind = k; } } /* Increase RD values of expired states */ Winner_rand_state = psDelDec[ Winner_ind ].RandState[ last_smple_idx ]; for( k = 0; k < nStatesDelayedDecision; k++ ) { if( psDelDec[ k ].RandState[ last_smple_idx ] != Winner_rand_state ) { psSampleState[ k ][ 0 ].RD_Q10 = silk_ADD32( psSampleState[ k ][ 0 ].RD_Q10, silk_int32_MAX >> 4 ); psSampleState[ k ][ 1 ].RD_Q10 = silk_ADD32( psSampleState[ k ][ 1 ].RD_Q10, silk_int32_MAX >> 4 ); silk_assert( psSampleState[ k ][ 0 ].RD_Q10 >= 0 ); } } /* Find worst in first set and best in second set */ RDmax_Q10 = psSampleState[ 0 ][ 0 ].RD_Q10; RDmin_Q10 = psSampleState[ 0 ][ 1 ].RD_Q10; RDmax_ind = 0; RDmin_ind = 0; for( k = 1; k < nStatesDelayedDecision; k++ ) { /* find worst in first set */ if( psSampleState[ k ][ 0 ].RD_Q10 > RDmax_Q10 ) { RDmax_Q10 = psSampleState[ k ][ 0 ].RD_Q10; RDmax_ind = k; } /* find best in second set */ if( psSampleState[ k ][ 1 ].RD_Q10 < RDmin_Q10 ) { RDmin_Q10 = psSampleState[ k ][ 1 ].RD_Q10; RDmin_ind = k; } } /* Replace a state if best from second set outperforms worst in first set */ if( RDmin_Q10 < RDmax_Q10 ) { silk_memcpy( ( (opus_int32 *)&psDelDec[ RDmax_ind ] ) + i, ( (opus_int32 *)&psDelDec[ RDmin_ind ] ) + i, sizeof( NSQ_del_dec_struct ) - i * sizeof( opus_int32) ); silk_memcpy( &psSampleState[ RDmax_ind ][ 0 ], &psSampleState[ RDmin_ind ][ 1 ], sizeof( NSQ_sample_struct ) ); } /* Write samples from winner to output and long-term filter states */ psDD = &psDelDec[ Winner_ind ]; if( subfr > 0 || i >= decisionDelay ) { pulses[ i - decisionDelay ] = (opus_int8)silk_RSHIFT_ROUND( psDD->Q_Q10[ last_smple_idx ], 10 ); xq[ i - decisionDelay ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_SMULWW( psDD->Xq_Q14[ last_smple_idx ], delayedGain_Q10[ last_smple_idx ] ), 8 ) ); NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - decisionDelay ] = psDD->Shape_Q14[ last_smple_idx ]; sLTP_Q15[ NSQ->sLTP_buf_idx - decisionDelay ] = psDD->Pred_Q15[ last_smple_idx ]; } NSQ->sLTP_shp_buf_idx++; NSQ->sLTP_buf_idx++; /* Update states */ for( k = 0; k < nStatesDelayedDecision; k++ ) { psDD = &psDelDec[ k ]; psSS = &psSampleState[ k ][ 0 ]; psDD->LF_AR_Q14 = psSS->LF_AR_Q14; psDD->sLPC_Q14[ NSQ_LPC_BUF_LENGTH + i ] = psSS->xq_Q14; psDD->Xq_Q14[ *smpl_buf_idx ] = psSS->xq_Q14; psDD->Q_Q10[ *smpl_buf_idx ] = psSS->Q_Q10; psDD->Pred_Q15[ *smpl_buf_idx ] = silk_LSHIFT32( psSS->LPC_exc_Q14, 1 ); psDD->Shape_Q14[ *smpl_buf_idx ] = psSS->sLTP_shp_Q14; psDD->Seed = silk_ADD32_ovflw( psDD->Seed, silk_RSHIFT_ROUND( psSS->Q_Q10, 10 ) ); psDD->RandState[ *smpl_buf_idx ] = psDD->Seed; psDD->RD_Q10 = psSS->RD_Q10; } delayedGain_Q10[ *smpl_buf_idx ] = Gain_Q10; } /* Update LPC states */ for( k = 0; k < nStatesDelayedDecision; k++ ) { psDD = &psDelDec[ k ]; silk_memcpy( psDD->sLPC_Q14, &psDD->sLPC_Q14[ length ], NSQ_LPC_BUF_LENGTH * sizeof( opus_int32 ) ); } RESTORE_STACK; } #endif /* OVERRIDE_silk_noise_shape_quantizer_del_dec */ static OPUS_INLINE void silk_nsq_del_dec_scale_states( const silk_encoder_state *psEncC, /* I Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ NSQ_del_dec_struct psDelDec[], /* I/O Delayed decision states */ const opus_int32 x_Q3[], /* I Input in Q3 */ opus_int32 x_sc_Q10[], /* O Input scaled with 1/Gain in Q10 */ const opus_int16 sLTP[], /* I Re-whitened LTP state in Q0 */ opus_int32 sLTP_Q15[], /* O LTP state matching scaled input */ opus_int subfr, /* I Subframe number */ opus_int nStatesDelayedDecision, /* I Number of del dec states */ const opus_int LTP_scale_Q14, /* I LTP state scaling */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lag */ const opus_int signal_type, /* I Signal type */ const opus_int decisionDelay /* I Decision delay */ ) { opus_int i, k, lag; opus_int32 gain_adj_Q16, inv_gain_Q31, inv_gain_Q23; NSQ_del_dec_struct *psDD; lag = pitchL[ subfr ]; inv_gain_Q31 = silk_INVERSE32_varQ( silk_max( Gains_Q16[ subfr ], 1 ), 47 ); silk_assert( inv_gain_Q31 != 0 ); /* Calculate gain adjustment factor */ if( Gains_Q16[ subfr ] != NSQ->prev_gain_Q16 ) { gain_adj_Q16 = silk_DIV32_varQ( NSQ->prev_gain_Q16, Gains_Q16[ subfr ], 16 ); } else { gain_adj_Q16 = (opus_int32)1 << 16; } /* Scale input */ inv_gain_Q23 = silk_RSHIFT_ROUND( inv_gain_Q31, 8 ); for( i = 0; i < psEncC->subfr_length; i++ ) { x_sc_Q10[ i ] = silk_SMULWW( x_Q3[ i ], inv_gain_Q23 ); } /* Save inverse gain */ NSQ->prev_gain_Q16 = Gains_Q16[ subfr ]; /* After rewhitening the LTP state is un-scaled, so scale with inv_gain_Q16 */ if( NSQ->rewhite_flag ) { if( subfr == 0 ) { /* Do LTP downscaling */ inv_gain_Q31 = silk_LSHIFT( silk_SMULWB( inv_gain_Q31, LTP_scale_Q14 ), 2 ); } for( i = NSQ->sLTP_buf_idx - lag - LTP_ORDER / 2; i < NSQ->sLTP_buf_idx; i++ ) { silk_assert( i < MAX_FRAME_LENGTH ); sLTP_Q15[ i ] = silk_SMULWB( inv_gain_Q31, sLTP[ i ] ); } } /* Adjust for changing gain */ if( gain_adj_Q16 != (opus_int32)1 << 16 ) { /* Scale long-term shaping state */ for( i = NSQ->sLTP_shp_buf_idx - psEncC->ltp_mem_length; i < NSQ->sLTP_shp_buf_idx; i++ ) { NSQ->sLTP_shp_Q14[ i ] = silk_SMULWW( gain_adj_Q16, NSQ->sLTP_shp_Q14[ i ] ); } /* Scale long-term prediction state */ if( signal_type == TYPE_VOICED && NSQ->rewhite_flag == 0 ) { for( i = NSQ->sLTP_buf_idx - lag - LTP_ORDER / 2; i < NSQ->sLTP_buf_idx - decisionDelay; i++ ) { sLTP_Q15[ i ] = silk_SMULWW( gain_adj_Q16, sLTP_Q15[ i ] ); } } for( k = 0; k < nStatesDelayedDecision; k++ ) { psDD = &psDelDec[ k ]; /* Scale scalar states */ psDD->LF_AR_Q14 = silk_SMULWW( gain_adj_Q16, psDD->LF_AR_Q14 ); /* Scale short-term prediction and shaping states */ for( i = 0; i < NSQ_LPC_BUF_LENGTH; i++ ) { psDD->sLPC_Q14[ i ] = silk_SMULWW( gain_adj_Q16, psDD->sLPC_Q14[ i ] ); } for( i = 0; i < MAX_SHAPE_LPC_ORDER; i++ ) { psDD->sAR2_Q14[ i ] = silk_SMULWW( gain_adj_Q16, psDD->sAR2_Q14[ i ] ); } for( i = 0; i < DECISION_DELAY; i++ ) { psDD->Pred_Q15[ i ] = silk_SMULWW( gain_adj_Q16, psDD->Pred_Q15[ i ] ); psDD->Shape_Q14[ i ] = silk_SMULWW( gain_adj_Q16, psDD->Shape_Q14[ i ] ); } } } } ================================================ FILE: deps/pjsip/third_party/opus/silk/PLC.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #include "stack_alloc.h" #include "PLC.h" #define NB_ATT 2 static const opus_int16 HARM_ATT_Q15[NB_ATT] = { 32440, 31130 }; /* 0.99, 0.95 */ static const opus_int16 PLC_RAND_ATTENUATE_V_Q15[NB_ATT] = { 31130, 26214 }; /* 0.95, 0.8 */ static const opus_int16 PLC_RAND_ATTENUATE_UV_Q15[NB_ATT] = { 32440, 29491 }; /* 0.99, 0.9 */ static OPUS_INLINE void silk_PLC_update( silk_decoder_state *psDec, /* I/O Decoder state */ silk_decoder_control *psDecCtrl /* I/O Decoder control */ ); static OPUS_INLINE void silk_PLC_conceal( silk_decoder_state *psDec, /* I/O Decoder state */ silk_decoder_control *psDecCtrl, /* I/O Decoder control */ opus_int16 frame[], /* O LPC residual signal */ int arch /* I Run-time architecture */ ); void silk_PLC_Reset( silk_decoder_state *psDec /* I/O Decoder state */ ) { psDec->sPLC.pitchL_Q8 = silk_LSHIFT( psDec->frame_length, 8 - 1 ); psDec->sPLC.prevGain_Q16[ 0 ] = SILK_FIX_CONST( 1, 16 ); psDec->sPLC.prevGain_Q16[ 1 ] = SILK_FIX_CONST( 1, 16 ); psDec->sPLC.subfr_length = 20; psDec->sPLC.nb_subfr = 2; } void silk_PLC( silk_decoder_state *psDec, /* I/O Decoder state */ silk_decoder_control *psDecCtrl, /* I/O Decoder control */ opus_int16 frame[], /* I/O signal */ opus_int lost, /* I Loss flag */ int arch /* I Run-time architecture */ ) { /* PLC control function */ if( psDec->fs_kHz != psDec->sPLC.fs_kHz ) { silk_PLC_Reset( psDec ); psDec->sPLC.fs_kHz = psDec->fs_kHz; } if( lost ) { /****************************/ /* Generate Signal */ /****************************/ silk_PLC_conceal( psDec, psDecCtrl, frame, arch ); psDec->lossCnt++; } else { /****************************/ /* Update state */ /****************************/ silk_PLC_update( psDec, psDecCtrl ); } } /**************************************************/ /* Update state of PLC */ /**************************************************/ static OPUS_INLINE void silk_PLC_update( silk_decoder_state *psDec, /* I/O Decoder state */ silk_decoder_control *psDecCtrl /* I/O Decoder control */ ) { opus_int32 LTP_Gain_Q14, temp_LTP_Gain_Q14; opus_int i, j; silk_PLC_struct *psPLC; psPLC = &psDec->sPLC; /* Update parameters used in case of packet loss */ psDec->prevSignalType = psDec->indices.signalType; LTP_Gain_Q14 = 0; if( psDec->indices.signalType == TYPE_VOICED ) { /* Find the parameters for the last subframe which contains a pitch pulse */ for( j = 0; j * psDec->subfr_length < psDecCtrl->pitchL[ psDec->nb_subfr - 1 ]; j++ ) { if( j == psDec->nb_subfr ) { break; } temp_LTP_Gain_Q14 = 0; for( i = 0; i < LTP_ORDER; i++ ) { temp_LTP_Gain_Q14 += psDecCtrl->LTPCoef_Q14[ ( psDec->nb_subfr - 1 - j ) * LTP_ORDER + i ]; } if( temp_LTP_Gain_Q14 > LTP_Gain_Q14 ) { LTP_Gain_Q14 = temp_LTP_Gain_Q14; silk_memcpy( psPLC->LTPCoef_Q14, &psDecCtrl->LTPCoef_Q14[ silk_SMULBB( psDec->nb_subfr - 1 - j, LTP_ORDER ) ], LTP_ORDER * sizeof( opus_int16 ) ); psPLC->pitchL_Q8 = silk_LSHIFT( psDecCtrl->pitchL[ psDec->nb_subfr - 1 - j ], 8 ); } } silk_memset( psPLC->LTPCoef_Q14, 0, LTP_ORDER * sizeof( opus_int16 ) ); psPLC->LTPCoef_Q14[ LTP_ORDER / 2 ] = LTP_Gain_Q14; /* Limit LT coefs */ if( LTP_Gain_Q14 < V_PITCH_GAIN_START_MIN_Q14 ) { opus_int scale_Q10; opus_int32 tmp; tmp = silk_LSHIFT( V_PITCH_GAIN_START_MIN_Q14, 10 ); scale_Q10 = silk_DIV32( tmp, silk_max( LTP_Gain_Q14, 1 ) ); for( i = 0; i < LTP_ORDER; i++ ) { psPLC->LTPCoef_Q14[ i ] = silk_RSHIFT( silk_SMULBB( psPLC->LTPCoef_Q14[ i ], scale_Q10 ), 10 ); } } else if( LTP_Gain_Q14 > V_PITCH_GAIN_START_MAX_Q14 ) { opus_int scale_Q14; opus_int32 tmp; tmp = silk_LSHIFT( V_PITCH_GAIN_START_MAX_Q14, 14 ); scale_Q14 = silk_DIV32( tmp, silk_max( LTP_Gain_Q14, 1 ) ); for( i = 0; i < LTP_ORDER; i++ ) { psPLC->LTPCoef_Q14[ i ] = silk_RSHIFT( silk_SMULBB( psPLC->LTPCoef_Q14[ i ], scale_Q14 ), 14 ); } } } else { psPLC->pitchL_Q8 = silk_LSHIFT( silk_SMULBB( psDec->fs_kHz, 18 ), 8 ); silk_memset( psPLC->LTPCoef_Q14, 0, LTP_ORDER * sizeof( opus_int16 )); } /* Save LPC coeficients */ silk_memcpy( psPLC->prevLPC_Q12, psDecCtrl->PredCoef_Q12[ 1 ], psDec->LPC_order * sizeof( opus_int16 ) ); psPLC->prevLTP_scale_Q14 = psDecCtrl->LTP_scale_Q14; /* Save last two gains */ silk_memcpy( psPLC->prevGain_Q16, &psDecCtrl->Gains_Q16[ psDec->nb_subfr - 2 ], 2 * sizeof( opus_int32 ) ); psPLC->subfr_length = psDec->subfr_length; psPLC->nb_subfr = psDec->nb_subfr; } static OPUS_INLINE void silk_PLC_energy(opus_int32 *energy1, opus_int *shift1, opus_int32 *energy2, opus_int *shift2, const opus_int32 *exc_Q14, const opus_int32 *prevGain_Q10, int subfr_length, int nb_subfr) { int i, k; VARDECL( opus_int16, exc_buf ); opus_int16 *exc_buf_ptr; SAVE_STACK; ALLOC( exc_buf, 2*subfr_length, opus_int16 ); /* Find random noise component */ /* Scale previous excitation signal */ exc_buf_ptr = exc_buf; for( k = 0; k < 2; k++ ) { for( i = 0; i < subfr_length; i++ ) { exc_buf_ptr[ i ] = (opus_int16)silk_SAT16( silk_RSHIFT( silk_SMULWW( exc_Q14[ i + ( k + nb_subfr - 2 ) * subfr_length ], prevGain_Q10[ k ] ), 8 ) ); } exc_buf_ptr += subfr_length; } /* Find the subframe with lowest energy of the last two and use that as random noise generator */ silk_sum_sqr_shift( energy1, shift1, exc_buf, subfr_length ); silk_sum_sqr_shift( energy2, shift2, &exc_buf[ subfr_length ], subfr_length ); RESTORE_STACK; } static OPUS_INLINE void silk_PLC_conceal( silk_decoder_state *psDec, /* I/O Decoder state */ silk_decoder_control *psDecCtrl, /* I/O Decoder control */ opus_int16 frame[], /* O LPC residual signal */ int arch /* I Run-time architecture */ ) { opus_int i, j, k; opus_int lag, idx, sLTP_buf_idx, shift1, shift2; opus_int32 rand_seed, harm_Gain_Q15, rand_Gain_Q15, inv_gain_Q30; opus_int32 energy1, energy2, *rand_ptr, *pred_lag_ptr; opus_int32 LPC_pred_Q10, LTP_pred_Q12; opus_int16 rand_scale_Q14; opus_int16 *B_Q14; opus_int32 *sLPC_Q14_ptr; opus_int16 A_Q12[ MAX_LPC_ORDER ]; #ifdef SMALL_FOOTPRINT opus_int16 *sLTP; #else VARDECL( opus_int16, sLTP ); #endif VARDECL( opus_int32, sLTP_Q14 ); silk_PLC_struct *psPLC = &psDec->sPLC; opus_int32 prevGain_Q10[2]; SAVE_STACK; ALLOC( sLTP_Q14, psDec->ltp_mem_length + psDec->frame_length, opus_int32 ); #ifdef SMALL_FOOTPRINT /* Ugly hack that breaks aliasing rules to save stack: put sLTP at the very end of sLTP_Q14. */ sLTP = ((opus_int16*)&sLTP_Q14[psDec->ltp_mem_length + psDec->frame_length])-psDec->ltp_mem_length; #else ALLOC( sLTP, psDec->ltp_mem_length, opus_int16 ); #endif prevGain_Q10[0] = silk_RSHIFT( psPLC->prevGain_Q16[ 0 ], 6); prevGain_Q10[1] = silk_RSHIFT( psPLC->prevGain_Q16[ 1 ], 6); if( psDec->first_frame_after_reset ) { silk_memset( psPLC->prevLPC_Q12, 0, sizeof( psPLC->prevLPC_Q12 ) ); } silk_PLC_energy(&energy1, &shift1, &energy2, &shift2, psDec->exc_Q14, prevGain_Q10, psDec->subfr_length, psDec->nb_subfr); if( silk_RSHIFT( energy1, shift2 ) < silk_RSHIFT( energy2, shift1 ) ) { /* First sub-frame has lowest energy */ rand_ptr = &psDec->exc_Q14[ silk_max_int( 0, ( psPLC->nb_subfr - 1 ) * psPLC->subfr_length - RAND_BUF_SIZE ) ]; } else { /* Second sub-frame has lowest energy */ rand_ptr = &psDec->exc_Q14[ silk_max_int( 0, psPLC->nb_subfr * psPLC->subfr_length - RAND_BUF_SIZE ) ]; } /* Set up Gain to random noise component */ B_Q14 = psPLC->LTPCoef_Q14; rand_scale_Q14 = psPLC->randScale_Q14; /* Set up attenuation gains */ harm_Gain_Q15 = HARM_ATT_Q15[ silk_min_int( NB_ATT - 1, psDec->lossCnt ) ]; if( psDec->prevSignalType == TYPE_VOICED ) { rand_Gain_Q15 = PLC_RAND_ATTENUATE_V_Q15[ silk_min_int( NB_ATT - 1, psDec->lossCnt ) ]; } else { rand_Gain_Q15 = PLC_RAND_ATTENUATE_UV_Q15[ silk_min_int( NB_ATT - 1, psDec->lossCnt ) ]; } /* LPC concealment. Apply BWE to previous LPC */ silk_bwexpander( psPLC->prevLPC_Q12, psDec->LPC_order, SILK_FIX_CONST( BWE_COEF, 16 ) ); /* Preload LPC coeficients to array on stack. Gives small performance gain */ silk_memcpy( A_Q12, psPLC->prevLPC_Q12, psDec->LPC_order * sizeof( opus_int16 ) ); /* First Lost frame */ if( psDec->lossCnt == 0 ) { rand_scale_Q14 = 1 << 14; /* Reduce random noise Gain for voiced frames */ if( psDec->prevSignalType == TYPE_VOICED ) { for( i = 0; i < LTP_ORDER; i++ ) { rand_scale_Q14 -= B_Q14[ i ]; } rand_scale_Q14 = silk_max_16( 3277, rand_scale_Q14 ); /* 0.2 */ rand_scale_Q14 = (opus_int16)silk_RSHIFT( silk_SMULBB( rand_scale_Q14, psPLC->prevLTP_scale_Q14 ), 14 ); } else { /* Reduce random noise for unvoiced frames with high LPC gain */ opus_int32 invGain_Q30, down_scale_Q30; invGain_Q30 = silk_LPC_inverse_pred_gain( psPLC->prevLPC_Q12, psDec->LPC_order ); down_scale_Q30 = silk_min_32( silk_RSHIFT( (opus_int32)1 << 30, LOG2_INV_LPC_GAIN_HIGH_THRES ), invGain_Q30 ); down_scale_Q30 = silk_max_32( silk_RSHIFT( (opus_int32)1 << 30, LOG2_INV_LPC_GAIN_LOW_THRES ), down_scale_Q30 ); down_scale_Q30 = silk_LSHIFT( down_scale_Q30, LOG2_INV_LPC_GAIN_HIGH_THRES ); rand_Gain_Q15 = silk_RSHIFT( silk_SMULWB( down_scale_Q30, rand_Gain_Q15 ), 14 ); } } rand_seed = psPLC->rand_seed; lag = silk_RSHIFT_ROUND( psPLC->pitchL_Q8, 8 ); sLTP_buf_idx = psDec->ltp_mem_length; /* Rewhiten LTP state */ idx = psDec->ltp_mem_length - lag - psDec->LPC_order - LTP_ORDER / 2; silk_assert( idx > 0 ); silk_LPC_analysis_filter( &sLTP[ idx ], &psDec->outBuf[ idx ], A_Q12, psDec->ltp_mem_length - idx, psDec->LPC_order, arch ); /* Scale LTP state */ inv_gain_Q30 = silk_INVERSE32_varQ( psPLC->prevGain_Q16[ 1 ], 46 ); inv_gain_Q30 = silk_min( inv_gain_Q30, silk_int32_MAX >> 1 ); for( i = idx + psDec->LPC_order; i < psDec->ltp_mem_length; i++ ) { sLTP_Q14[ i ] = silk_SMULWB( inv_gain_Q30, sLTP[ i ] ); } /***************************/ /* LTP synthesis filtering */ /***************************/ for( k = 0; k < psDec->nb_subfr; k++ ) { /* Set up pointer */ pred_lag_ptr = &sLTP_Q14[ sLTP_buf_idx - lag + LTP_ORDER / 2 ]; for( i = 0; i < psDec->subfr_length; i++ ) { /* Unrolled loop */ /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ LTP_pred_Q12 = 2; LTP_pred_Q12 = silk_SMLAWB( LTP_pred_Q12, pred_lag_ptr[ 0 ], B_Q14[ 0 ] ); LTP_pred_Q12 = silk_SMLAWB( LTP_pred_Q12, pred_lag_ptr[ -1 ], B_Q14[ 1 ] ); LTP_pred_Q12 = silk_SMLAWB( LTP_pred_Q12, pred_lag_ptr[ -2 ], B_Q14[ 2 ] ); LTP_pred_Q12 = silk_SMLAWB( LTP_pred_Q12, pred_lag_ptr[ -3 ], B_Q14[ 3 ] ); LTP_pred_Q12 = silk_SMLAWB( LTP_pred_Q12, pred_lag_ptr[ -4 ], B_Q14[ 4 ] ); pred_lag_ptr++; /* Generate LPC excitation */ rand_seed = silk_RAND( rand_seed ); idx = silk_RSHIFT( rand_seed, 25 ) & RAND_BUF_MASK; sLTP_Q14[ sLTP_buf_idx ] = silk_LSHIFT32( silk_SMLAWB( LTP_pred_Q12, rand_ptr[ idx ], rand_scale_Q14 ), 2 ); sLTP_buf_idx++; } /* Gradually reduce LTP gain */ for( j = 0; j < LTP_ORDER; j++ ) { B_Q14[ j ] = silk_RSHIFT( silk_SMULBB( harm_Gain_Q15, B_Q14[ j ] ), 15 ); } /* Gradually reduce excitation gain */ rand_scale_Q14 = silk_RSHIFT( silk_SMULBB( rand_scale_Q14, rand_Gain_Q15 ), 15 ); /* Slowly increase pitch lag */ psPLC->pitchL_Q8 = silk_SMLAWB( psPLC->pitchL_Q8, psPLC->pitchL_Q8, PITCH_DRIFT_FAC_Q16 ); psPLC->pitchL_Q8 = silk_min_32( psPLC->pitchL_Q8, silk_LSHIFT( silk_SMULBB( MAX_PITCH_LAG_MS, psDec->fs_kHz ), 8 ) ); lag = silk_RSHIFT_ROUND( psPLC->pitchL_Q8, 8 ); } /***************************/ /* LPC synthesis filtering */ /***************************/ sLPC_Q14_ptr = &sLTP_Q14[ psDec->ltp_mem_length - MAX_LPC_ORDER ]; /* Copy LPC state */ silk_memcpy( sLPC_Q14_ptr, psDec->sLPC_Q14_buf, MAX_LPC_ORDER * sizeof( opus_int32 ) ); silk_assert( psDec->LPC_order >= 10 ); /* check that unrolling works */ for( i = 0; i < psDec->frame_length; i++ ) { /* partly unrolled */ /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ LPC_pred_Q10 = silk_RSHIFT( psDec->LPC_order, 1 ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 1 ], A_Q12[ 0 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 2 ], A_Q12[ 1 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 3 ], A_Q12[ 2 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 4 ], A_Q12[ 3 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 5 ], A_Q12[ 4 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 6 ], A_Q12[ 5 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 7 ], A_Q12[ 6 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 8 ], A_Q12[ 7 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 9 ], A_Q12[ 8 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 10 ], A_Q12[ 9 ] ); for( j = 10; j < psDec->LPC_order; j++ ) { LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - j - 1 ], A_Q12[ j ] ); } /* Add prediction to LPC excitation */ sLPC_Q14_ptr[ MAX_LPC_ORDER + i ] = silk_ADD_LSHIFT32( sLPC_Q14_ptr[ MAX_LPC_ORDER + i ], LPC_pred_Q10, 4 ); /* Scale with Gain */ frame[ i ] = (opus_int16)silk_SAT16( silk_SAT16( silk_RSHIFT_ROUND( silk_SMULWW( sLPC_Q14_ptr[ MAX_LPC_ORDER + i ], prevGain_Q10[ 1 ] ), 8 ) ) ); } /* Save LPC state */ silk_memcpy( psDec->sLPC_Q14_buf, &sLPC_Q14_ptr[ psDec->frame_length ], MAX_LPC_ORDER * sizeof( opus_int32 ) ); /**************************************/ /* Update states */ /**************************************/ psPLC->rand_seed = rand_seed; psPLC->randScale_Q14 = rand_scale_Q14; for( i = 0; i < MAX_NB_SUBFR; i++ ) { psDecCtrl->pitchL[ i ] = lag; } RESTORE_STACK; } /* Glues concealed frames with new good received frames */ void silk_PLC_glue_frames( silk_decoder_state *psDec, /* I/O decoder state */ opus_int16 frame[], /* I/O signal */ opus_int length /* I length of signal */ ) { opus_int i, energy_shift; opus_int32 energy; silk_PLC_struct *psPLC; psPLC = &psDec->sPLC; if( psDec->lossCnt ) { /* Calculate energy in concealed residual */ silk_sum_sqr_shift( &psPLC->conc_energy, &psPLC->conc_energy_shift, frame, length ); psPLC->last_frame_lost = 1; } else { if( psDec->sPLC.last_frame_lost ) { /* Calculate residual in decoded signal if last frame was lost */ silk_sum_sqr_shift( &energy, &energy_shift, frame, length ); /* Normalize energies */ if( energy_shift > psPLC->conc_energy_shift ) { psPLC->conc_energy = silk_RSHIFT( psPLC->conc_energy, energy_shift - psPLC->conc_energy_shift ); } else if( energy_shift < psPLC->conc_energy_shift ) { energy = silk_RSHIFT( energy, psPLC->conc_energy_shift - energy_shift ); } /* Fade in the energy difference */ if( energy > psPLC->conc_energy ) { opus_int32 frac_Q24, LZ; opus_int32 gain_Q16, slope_Q16; LZ = silk_CLZ32( psPLC->conc_energy ); LZ = LZ - 1; psPLC->conc_energy = silk_LSHIFT( psPLC->conc_energy, LZ ); energy = silk_RSHIFT( energy, silk_max_32( 24 - LZ, 0 ) ); frac_Q24 = silk_DIV32( psPLC->conc_energy, silk_max( energy, 1 ) ); gain_Q16 = silk_LSHIFT( silk_SQRT_APPROX( frac_Q24 ), 4 ); slope_Q16 = silk_DIV32_16( ( (opus_int32)1 << 16 ) - gain_Q16, length ); /* Make slope 4x steeper to avoid missing onsets after DTX */ slope_Q16 = silk_LSHIFT( slope_Q16, 2 ); for( i = 0; i < length; i++ ) { frame[ i ] = silk_SMULWB( gain_Q16, frame[ i ] ); gain_Q16 += slope_Q16; if( gain_Q16 > (opus_int32)1 << 16 ) { break; } } } } psPLC->last_frame_lost = 0; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/PLC.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_PLC_H #define SILK_PLC_H #include "main.h" #define BWE_COEF 0.99 #define V_PITCH_GAIN_START_MIN_Q14 11469 /* 0.7 in Q14 */ #define V_PITCH_GAIN_START_MAX_Q14 15565 /* 0.95 in Q14 */ #define MAX_PITCH_LAG_MS 18 #define RAND_BUF_SIZE 128 #define RAND_BUF_MASK ( RAND_BUF_SIZE - 1 ) #define LOG2_INV_LPC_GAIN_HIGH_THRES 3 /* 2^3 = 8 dB LPC gain */ #define LOG2_INV_LPC_GAIN_LOW_THRES 8 /* 2^8 = 24 dB LPC gain */ #define PITCH_DRIFT_FAC_Q16 655 /* 0.01 in Q16 */ void silk_PLC_Reset( silk_decoder_state *psDec /* I/O Decoder state */ ); void silk_PLC( silk_decoder_state *psDec, /* I/O Decoder state */ silk_decoder_control *psDecCtrl, /* I/O Decoder control */ opus_int16 frame[], /* I/O signal */ opus_int lost, /* I Loss flag */ int arch /* I Run-time architecture */ ); void silk_PLC_glue_frames( silk_decoder_state *psDec, /* I/O decoder state */ opus_int16 frame[], /* I/O signal */ opus_int length /* I length of signal */ ); #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/SigProc_FIX.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_SIGPROC_FIX_H #define SILK_SIGPROC_FIX_H #ifdef __cplusplus extern "C" { #endif /*#define silk_MACRO_COUNT */ /* Used to enable WMOPS counting */ #define SILK_MAX_ORDER_LPC 16 /* max order of the LPC analysis in schur() and k2a() */ #include /* for memset(), memcpy(), memmove() */ #include "typedef.h" #include "resampler_structs.h" #include "macros.h" #include "cpu_support.h" #if defined(OPUS_X86_MAY_HAVE_SSE4_1) #include "x86/SigProc_FIX_sse.h" #endif /********************************************************************/ /* SIGNAL PROCESSING FUNCTIONS */ /********************************************************************/ /*! * Initialize/reset the resampler state for a given pair of input/output sampling rates */ opus_int silk_resampler_init( silk_resampler_state_struct *S, /* I/O Resampler state */ opus_int32 Fs_Hz_in, /* I Input sampling rate (Hz) */ opus_int32 Fs_Hz_out, /* I Output sampling rate (Hz) */ opus_int forEnc /* I If 1: encoder; if 0: decoder */ ); /*! * Resampler: convert from one sampling rate to another */ opus_int silk_resampler( silk_resampler_state_struct *S, /* I/O Resampler state */ opus_int16 out[], /* O Output signal */ const opus_int16 in[], /* I Input signal */ opus_int32 inLen /* I Number of input samples */ ); /*! * Downsample 2x, mediocre quality */ void silk_resampler_down2( opus_int32 *S, /* I/O State vector [ 2 ] */ opus_int16 *out, /* O Output signal [ len ] */ const opus_int16 *in, /* I Input signal [ floor(len/2) ] */ opus_int32 inLen /* I Number of input samples */ ); /*! * Downsample by a factor 2/3, low quality */ void silk_resampler_down2_3( opus_int32 *S, /* I/O State vector [ 6 ] */ opus_int16 *out, /* O Output signal [ floor(2*inLen/3) ] */ const opus_int16 *in, /* I Input signal [ inLen ] */ opus_int32 inLen /* I Number of input samples */ ); /*! * second order ARMA filter; * slower than biquad() but uses more precise coefficients * can handle (slowly) varying coefficients */ void silk_biquad_alt( const opus_int16 *in, /* I input signal */ const opus_int32 *B_Q28, /* I MA coefficients [3] */ const opus_int32 *A_Q28, /* I AR coefficients [2] */ opus_int32 *S, /* I/O State vector [2] */ opus_int16 *out, /* O output signal */ const opus_int32 len, /* I signal length (must be even) */ opus_int stride /* I Operate on interleaved signal if > 1 */ ); /* Variable order MA prediction error filter. */ void silk_LPC_analysis_filter( opus_int16 *out, /* O Output signal */ const opus_int16 *in, /* I Input signal */ const opus_int16 *B, /* I MA prediction coefficients, Q12 [order] */ const opus_int32 len, /* I Signal length */ const opus_int32 d, /* I Filter order */ int arch /* I Run-time architecture */ ); /* Chirp (bandwidth expand) LP AR filter */ void silk_bwexpander( opus_int16 *ar, /* I/O AR filter to be expanded (without leading 1) */ const opus_int d, /* I Length of ar */ opus_int32 chirp_Q16 /* I Chirp factor (typically in the range 0 to 1) */ ); /* Chirp (bandwidth expand) LP AR filter */ void silk_bwexpander_32( opus_int32 *ar, /* I/O AR filter to be expanded (without leading 1) */ const opus_int d, /* I Length of ar */ opus_int32 chirp_Q16 /* I Chirp factor in Q16 */ ); /* Compute inverse of LPC prediction gain, and */ /* test if LPC coefficients are stable (all poles within unit circle) */ opus_int32 silk_LPC_inverse_pred_gain( /* O Returns inverse prediction gain in energy domain, Q30 */ const opus_int16 *A_Q12, /* I Prediction coefficients, Q12 [order] */ const opus_int order /* I Prediction order */ ); /* For input in Q24 domain */ opus_int32 silk_LPC_inverse_pred_gain_Q24( /* O Returns inverse prediction gain in energy domain, Q30 */ const opus_int32 *A_Q24, /* I Prediction coefficients [order] */ const opus_int order /* I Prediction order */ ); /* Split signal in two decimated bands using first-order allpass filters */ void silk_ana_filt_bank_1( const opus_int16 *in, /* I Input signal [N] */ opus_int32 *S, /* I/O State vector [2] */ opus_int16 *outL, /* O Low band [N/2] */ opus_int16 *outH, /* O High band [N/2] */ const opus_int32 N /* I Number of input samples */ ); /********************************************************************/ /* SCALAR FUNCTIONS */ /********************************************************************/ /* Approximation of 128 * log2() (exact inverse of approx 2^() below) */ /* Convert input to a log scale */ opus_int32 silk_lin2log( const opus_int32 inLin /* I input in linear scale */ ); /* Approximation of a sigmoid function */ opus_int silk_sigm_Q15( opus_int in_Q5 /* I */ ); /* Approximation of 2^() (exact inverse of approx log2() above) */ /* Convert input to a linear scale */ opus_int32 silk_log2lin( const opus_int32 inLog_Q7 /* I input on log scale */ ); /* Compute number of bits to right shift the sum of squares of a vector */ /* of int16s to make it fit in an int32 */ void silk_sum_sqr_shift( opus_int32 *energy, /* O Energy of x, after shifting to the right */ opus_int *shift, /* O Number of bits right shift applied to energy */ const opus_int16 *x, /* I Input vector */ opus_int len /* I Length of input vector */ ); /* Calculates the reflection coefficients from the correlation sequence */ /* Faster than schur64(), but much less accurate. */ /* uses SMLAWB(), requiring armv5E and higher. */ opus_int32 silk_schur( /* O Returns residual energy */ opus_int16 *rc_Q15, /* O reflection coefficients [order] Q15 */ const opus_int32 *c, /* I correlations [order+1] */ const opus_int32 order /* I prediction order */ ); /* Calculates the reflection coefficients from the correlation sequence */ /* Slower than schur(), but more accurate. */ /* Uses SMULL(), available on armv4 */ opus_int32 silk_schur64( /* O returns residual energy */ opus_int32 rc_Q16[], /* O Reflection coefficients [order] Q16 */ const opus_int32 c[], /* I Correlations [order+1] */ opus_int32 order /* I Prediction order */ ); /* Step up function, converts reflection coefficients to prediction coefficients */ void silk_k2a( opus_int32 *A_Q24, /* O Prediction coefficients [order] Q24 */ const opus_int16 *rc_Q15, /* I Reflection coefficients [order] Q15 */ const opus_int32 order /* I Prediction order */ ); /* Step up function, converts reflection coefficients to prediction coefficients */ void silk_k2a_Q16( opus_int32 *A_Q24, /* O Prediction coefficients [order] Q24 */ const opus_int32 *rc_Q16, /* I Reflection coefficients [order] Q16 */ const opus_int32 order /* I Prediction order */ ); /* Apply sine window to signal vector. */ /* Window types: */ /* 1 -> sine window from 0 to pi/2 */ /* 2 -> sine window from pi/2 to pi */ /* every other sample of window is linearly interpolated, for speed */ void silk_apply_sine_window( opus_int16 px_win[], /* O Pointer to windowed signal */ const opus_int16 px[], /* I Pointer to input signal */ const opus_int win_type, /* I Selects a window type */ const opus_int length /* I Window length, multiple of 4 */ ); /* Compute autocorrelation */ void silk_autocorr( opus_int32 *results, /* O Result (length correlationCount) */ opus_int *scale, /* O Scaling of the correlation vector */ const opus_int16 *inputData, /* I Input data to correlate */ const opus_int inputDataSize, /* I Length of input */ const opus_int correlationCount, /* I Number of correlation taps to compute */ int arch /* I Run-time architecture */ ); void silk_decode_pitch( opus_int16 lagIndex, /* I */ opus_int8 contourIndex, /* O */ opus_int pitch_lags[], /* O 4 pitch values */ const opus_int Fs_kHz, /* I sampling frequency (kHz) */ const opus_int nb_subfr /* I number of sub frames */ ); opus_int silk_pitch_analysis_core( /* O Voicing estimate: 0 voiced, 1 unvoiced */ const opus_int16 *frame, /* I Signal of length PE_FRAME_LENGTH_MS*Fs_kHz */ opus_int *pitch_out, /* O 4 pitch lag values */ opus_int16 *lagIndex, /* O Lag Index */ opus_int8 *contourIndex, /* O Pitch contour Index */ opus_int *LTPCorr_Q15, /* I/O Normalized correlation; input: value from previous frame */ opus_int prevLag, /* I Last lag of previous frame; set to zero is unvoiced */ const opus_int32 search_thres1_Q16, /* I First stage threshold for lag candidates 0 - 1 */ const opus_int search_thres2_Q13, /* I Final threshold for lag candidates 0 - 1 */ const opus_int Fs_kHz, /* I Sample frequency (kHz) */ const opus_int complexity, /* I Complexity setting, 0-2, where 2 is highest */ const opus_int nb_subfr, /* I number of 5 ms subframes */ int arch /* I Run-time architecture */ ); /* Compute Normalized Line Spectral Frequencies (NLSFs) from whitening filter coefficients */ /* If not all roots are found, the a_Q16 coefficients are bandwidth expanded until convergence. */ void silk_A2NLSF( opus_int16 *NLSF, /* O Normalized Line Spectral Frequencies in Q15 (0..2^15-1) [d] */ opus_int32 *a_Q16, /* I/O Monic whitening filter coefficients in Q16 [d] */ const opus_int d /* I Filter order (must be even) */ ); /* compute whitening filter coefficients from normalized line spectral frequencies */ void silk_NLSF2A( opus_int16 *a_Q12, /* O monic whitening filter coefficients in Q12, [ d ] */ const opus_int16 *NLSF, /* I normalized line spectral frequencies in Q15, [ d ] */ const opus_int d /* I filter order (should be even) */ ); void silk_insertion_sort_increasing( opus_int32 *a, /* I/O Unsorted / Sorted vector */ opus_int *idx, /* O Index vector for the sorted elements */ const opus_int L, /* I Vector length */ const opus_int K /* I Number of correctly sorted positions */ ); void silk_insertion_sort_decreasing_int16( opus_int16 *a, /* I/O Unsorted / Sorted vector */ opus_int *idx, /* O Index vector for the sorted elements */ const opus_int L, /* I Vector length */ const opus_int K /* I Number of correctly sorted positions */ ); void silk_insertion_sort_increasing_all_values_int16( opus_int16 *a, /* I/O Unsorted / Sorted vector */ const opus_int L /* I Vector length */ ); /* NLSF stabilizer, for a single input data vector */ void silk_NLSF_stabilize( opus_int16 *NLSF_Q15, /* I/O Unstable/stabilized normalized LSF vector in Q15 [L] */ const opus_int16 *NDeltaMin_Q15, /* I Min distance vector, NDeltaMin_Q15[L] must be >= 1 [L+1] */ const opus_int L /* I Number of NLSF parameters in the input vector */ ); /* Laroia low complexity NLSF weights */ void silk_NLSF_VQ_weights_laroia( opus_int16 *pNLSFW_Q_OUT, /* O Pointer to input vector weights [D] */ const opus_int16 *pNLSF_Q15, /* I Pointer to input vector [D] */ const opus_int D /* I Input vector dimension (even) */ ); /* Compute reflection coefficients from input signal */ void silk_burg_modified_c( opus_int32 *res_nrg, /* O Residual energy */ opus_int *res_nrg_Q, /* O Residual energy Q value */ opus_int32 A_Q16[], /* O Prediction coefficients (length order) */ const opus_int16 x[], /* I Input signal, length: nb_subfr * ( D + subfr_length ) */ const opus_int32 minInvGain_Q30, /* I Inverse of max prediction gain */ const opus_int subfr_length, /* I Input signal subframe length (incl. D preceding samples) */ const opus_int nb_subfr, /* I Number of subframes stacked in x */ const opus_int D, /* I Order */ int arch /* I Run-time architecture */ ); /* Copy and multiply a vector by a constant */ void silk_scale_copy_vector16( opus_int16 *data_out, const opus_int16 *data_in, opus_int32 gain_Q16, /* I Gain in Q16 */ const opus_int dataSize /* I Length */ ); /* Some for the LTP related function requires Q26 to work.*/ void silk_scale_vector32_Q26_lshift_18( opus_int32 *data1, /* I/O Q0/Q18 */ opus_int32 gain_Q26, /* I Q26 */ opus_int dataSize /* I length */ ); /********************************************************************/ /* INLINE ARM MATH */ /********************************************************************/ /* return sum( inVec1[i] * inVec2[i] ) */ opus_int32 silk_inner_prod_aligned( const opus_int16 *const inVec1, /* I input vector 1 */ const opus_int16 *const inVec2, /* I input vector 2 */ const opus_int len, /* I vector lengths */ int arch /* I Run-time architecture */ ); opus_int32 silk_inner_prod_aligned_scale( const opus_int16 *const inVec1, /* I input vector 1 */ const opus_int16 *const inVec2, /* I input vector 2 */ const opus_int scale, /* I number of bits to shift */ const opus_int len /* I vector lengths */ ); opus_int64 silk_inner_prod16_aligned_64_c( const opus_int16 *inVec1, /* I input vector 1 */ const opus_int16 *inVec2, /* I input vector 2 */ const opus_int len /* I vector lengths */ ); /********************************************************************/ /* MACROS */ /********************************************************************/ /* Rotate a32 right by 'rot' bits. Negative rot values result in rotating left. Output is 32bit int. Note: contemporary compilers recognize the C expression below and compile it into a 'ror' instruction if available. No need for OPUS_INLINE ASM! */ static OPUS_INLINE opus_int32 silk_ROR32( opus_int32 a32, opus_int rot ) { opus_uint32 x = (opus_uint32) a32; opus_uint32 r = (opus_uint32) rot; opus_uint32 m = (opus_uint32) -rot; if( rot == 0 ) { return a32; } else if( rot < 0 ) { return (opus_int32) ((x << m) | (x >> (32 - m))); } else { return (opus_int32) ((x << (32 - r)) | (x >> r)); } } /* Allocate opus_int16 aligned to 4-byte memory address */ #if EMBEDDED_ARM #define silk_DWORD_ALIGN __attribute__((aligned(4))) #else #define silk_DWORD_ALIGN #endif /* Useful Macros that can be adjusted to other platforms */ #define silk_memcpy(dest, src, size) memcpy((dest), (src), (size)) #define silk_memset(dest, src, size) memset((dest), (src), (size)) #define silk_memmove(dest, src, size) memmove((dest), (src), (size)) /* Fixed point macros */ /* (a32 * b32) output have to be 32bit int */ #define silk_MUL(a32, b32) ((a32) * (b32)) /* (a32 * b32) output have to be 32bit uint */ #define silk_MUL_uint(a32, b32) silk_MUL(a32, b32) /* a32 + (b32 * c32) output have to be 32bit int */ #define silk_MLA(a32, b32, c32) silk_ADD32((a32),((b32) * (c32))) /* a32 + (b32 * c32) output have to be 32bit uint */ #define silk_MLA_uint(a32, b32, c32) silk_MLA(a32, b32, c32) /* ((a32 >> 16) * (b32 >> 16)) output have to be 32bit int */ #define silk_SMULTT(a32, b32) (((a32) >> 16) * ((b32) >> 16)) /* a32 + ((a32 >> 16) * (b32 >> 16)) output have to be 32bit int */ #define silk_SMLATT(a32, b32, c32) silk_ADD32((a32),((b32) >> 16) * ((c32) >> 16)) #define silk_SMLALBB(a64, b16, c16) silk_ADD64((a64),(opus_int64)((opus_int32)(b16) * (opus_int32)(c16))) /* (a32 * b32) */ #define silk_SMULL(a32, b32) ((opus_int64)(a32) * /*(opus_int64)*/(b32)) /* Adds two signed 32-bit values in a way that can overflow, while not relying on undefined behaviour (just standard two's complement implementation-specific behaviour) */ #define silk_ADD32_ovflw(a, b) ((opus_int32)((opus_uint32)(a) + (opus_uint32)(b))) /* Subtractss two signed 32-bit values in a way that can overflow, while not relying on undefined behaviour (just standard two's complement implementation-specific behaviour) */ #define silk_SUB32_ovflw(a, b) ((opus_int32)((opus_uint32)(a) - (opus_uint32)(b))) /* Multiply-accumulate macros that allow overflow in the addition (ie, no asserts in debug mode) */ #define silk_MLA_ovflw(a32, b32, c32) silk_ADD32_ovflw((a32), (opus_uint32)(b32) * (opus_uint32)(c32)) #define silk_SMLABB_ovflw(a32, b32, c32) (silk_ADD32_ovflw((a32) , ((opus_int32)((opus_int16)(b32))) * (opus_int32)((opus_int16)(c32)))) #define silk_DIV32_16(a32, b16) ((opus_int32)((a32) / (b16))) #define silk_DIV32(a32, b32) ((opus_int32)((a32) / (b32))) /* These macros enables checking for overflow in silk_API_Debug.h*/ #define silk_ADD16(a, b) ((a) + (b)) #define silk_ADD32(a, b) ((a) + (b)) #define silk_ADD64(a, b) ((a) + (b)) #define silk_SUB16(a, b) ((a) - (b)) #define silk_SUB32(a, b) ((a) - (b)) #define silk_SUB64(a, b) ((a) - (b)) #define silk_SAT8(a) ((a) > silk_int8_MAX ? silk_int8_MAX : \ ((a) < silk_int8_MIN ? silk_int8_MIN : (a))) #define silk_SAT16(a) ((a) > silk_int16_MAX ? silk_int16_MAX : \ ((a) < silk_int16_MIN ? silk_int16_MIN : (a))) #define silk_SAT32(a) ((a) > silk_int32_MAX ? silk_int32_MAX : \ ((a) < silk_int32_MIN ? silk_int32_MIN : (a))) #define silk_CHECK_FIT8(a) (a) #define silk_CHECK_FIT16(a) (a) #define silk_CHECK_FIT32(a) (a) #define silk_ADD_SAT16(a, b) (opus_int16)silk_SAT16( silk_ADD32( (opus_int32)(a), (b) ) ) #define silk_ADD_SAT64(a, b) ((((a) + (b)) & 0x8000000000000000LL) == 0 ? \ ((((a) & (b)) & 0x8000000000000000LL) != 0 ? silk_int64_MIN : (a)+(b)) : \ ((((a) | (b)) & 0x8000000000000000LL) == 0 ? silk_int64_MAX : (a)+(b)) ) #define silk_SUB_SAT16(a, b) (opus_int16)silk_SAT16( silk_SUB32( (opus_int32)(a), (b) ) ) #define silk_SUB_SAT64(a, b) ((((a)-(b)) & 0x8000000000000000LL) == 0 ? \ (( (a) & ((b)^0x8000000000000000LL) & 0x8000000000000000LL) ? silk_int64_MIN : (a)-(b)) : \ ((((a)^0x8000000000000000LL) & (b) & 0x8000000000000000LL) ? silk_int64_MAX : (a)-(b)) ) /* Saturation for positive input values */ #define silk_POS_SAT32(a) ((a) > silk_int32_MAX ? silk_int32_MAX : (a)) /* Add with saturation for positive input values */ #define silk_ADD_POS_SAT8(a, b) ((((a)+(b)) & 0x80) ? silk_int8_MAX : ((a)+(b))) #define silk_ADD_POS_SAT16(a, b) ((((a)+(b)) & 0x8000) ? silk_int16_MAX : ((a)+(b))) #define silk_ADD_POS_SAT32(a, b) ((((a)+(b)) & 0x80000000) ? silk_int32_MAX : ((a)+(b))) #define silk_ADD_POS_SAT64(a, b) ((((a)+(b)) & 0x8000000000000000LL) ? silk_int64_MAX : ((a)+(b))) #define silk_LSHIFT8(a, shift) ((opus_int8)((opus_uint8)(a)<<(shift))) /* shift >= 0, shift < 8 */ #define silk_LSHIFT16(a, shift) ((opus_int16)((opus_uint16)(a)<<(shift))) /* shift >= 0, shift < 16 */ #define silk_LSHIFT32(a, shift) ((opus_int32)((opus_uint32)(a)<<(shift))) /* shift >= 0, shift < 32 */ #define silk_LSHIFT64(a, shift) ((opus_int64)((opus_uint64)(a)<<(shift))) /* shift >= 0, shift < 64 */ #define silk_LSHIFT(a, shift) silk_LSHIFT32(a, shift) /* shift >= 0, shift < 32 */ #define silk_RSHIFT8(a, shift) ((a)>>(shift)) /* shift >= 0, shift < 8 */ #define silk_RSHIFT16(a, shift) ((a)>>(shift)) /* shift >= 0, shift < 16 */ #define silk_RSHIFT32(a, shift) ((a)>>(shift)) /* shift >= 0, shift < 32 */ #define silk_RSHIFT64(a, shift) ((a)>>(shift)) /* shift >= 0, shift < 64 */ #define silk_RSHIFT(a, shift) silk_RSHIFT32(a, shift) /* shift >= 0, shift < 32 */ /* saturates before shifting */ #define silk_LSHIFT_SAT32(a, shift) (silk_LSHIFT32( silk_LIMIT( (a), silk_RSHIFT32( silk_int32_MIN, (shift) ), \ silk_RSHIFT32( silk_int32_MAX, (shift) ) ), (shift) )) #define silk_LSHIFT_ovflw(a, shift) ((opus_int32)((opus_uint32)(a) << (shift))) /* shift >= 0, allowed to overflow */ #define silk_LSHIFT_uint(a, shift) ((a) << (shift)) /* shift >= 0 */ #define silk_RSHIFT_uint(a, shift) ((a) >> (shift)) /* shift >= 0 */ #define silk_ADD_LSHIFT(a, b, shift) ((a) + silk_LSHIFT((b), (shift))) /* shift >= 0 */ #define silk_ADD_LSHIFT32(a, b, shift) silk_ADD32((a), silk_LSHIFT32((b), (shift))) /* shift >= 0 */ #define silk_ADD_LSHIFT_uint(a, b, shift) ((a) + silk_LSHIFT_uint((b), (shift))) /* shift >= 0 */ #define silk_ADD_RSHIFT(a, b, shift) ((a) + silk_RSHIFT((b), (shift))) /* shift >= 0 */ #define silk_ADD_RSHIFT32(a, b, shift) silk_ADD32((a), silk_RSHIFT32((b), (shift))) /* shift >= 0 */ #define silk_ADD_RSHIFT_uint(a, b, shift) ((a) + silk_RSHIFT_uint((b), (shift))) /* shift >= 0 */ #define silk_SUB_LSHIFT32(a, b, shift) silk_SUB32((a), silk_LSHIFT32((b), (shift))) /* shift >= 0 */ #define silk_SUB_RSHIFT32(a, b, shift) silk_SUB32((a), silk_RSHIFT32((b), (shift))) /* shift >= 0 */ /* Requires that shift > 0 */ #define silk_RSHIFT_ROUND(a, shift) ((shift) == 1 ? ((a) >> 1) + ((a) & 1) : (((a) >> ((shift) - 1)) + 1) >> 1) #define silk_RSHIFT_ROUND64(a, shift) ((shift) == 1 ? ((a) >> 1) + ((a) & 1) : (((a) >> ((shift) - 1)) + 1) >> 1) /* Number of rightshift required to fit the multiplication */ #define silk_NSHIFT_MUL_32_32(a, b) ( -(31- (32-silk_CLZ32(silk_abs(a)) + (32-silk_CLZ32(silk_abs(b))))) ) #define silk_NSHIFT_MUL_16_16(a, b) ( -(15- (16-silk_CLZ16(silk_abs(a)) + (16-silk_CLZ16(silk_abs(b))))) ) #define silk_min(a, b) (((a) < (b)) ? (a) : (b)) #define silk_max(a, b) (((a) > (b)) ? (a) : (b)) /* Macro to convert floating-point constants to fixed-point */ #define SILK_FIX_CONST( C, Q ) ((opus_int32)((C) * ((opus_int64)1 << (Q)) + 0.5)) /* silk_min() versions with typecast in the function call */ static OPUS_INLINE opus_int silk_min_int(opus_int a, opus_int b) { return (((a) < (b)) ? (a) : (b)); } static OPUS_INLINE opus_int16 silk_min_16(opus_int16 a, opus_int16 b) { return (((a) < (b)) ? (a) : (b)); } static OPUS_INLINE opus_int32 silk_min_32(opus_int32 a, opus_int32 b) { return (((a) < (b)) ? (a) : (b)); } static OPUS_INLINE opus_int64 silk_min_64(opus_int64 a, opus_int64 b) { return (((a) < (b)) ? (a) : (b)); } /* silk_min() versions with typecast in the function call */ static OPUS_INLINE opus_int silk_max_int(opus_int a, opus_int b) { return (((a) > (b)) ? (a) : (b)); } static OPUS_INLINE opus_int16 silk_max_16(opus_int16 a, opus_int16 b) { return (((a) > (b)) ? (a) : (b)); } static OPUS_INLINE opus_int32 silk_max_32(opus_int32 a, opus_int32 b) { return (((a) > (b)) ? (a) : (b)); } static OPUS_INLINE opus_int64 silk_max_64(opus_int64 a, opus_int64 b) { return (((a) > (b)) ? (a) : (b)); } #define silk_LIMIT( a, limit1, limit2) ((limit1) > (limit2) ? ((a) > (limit1) ? (limit1) : ((a) < (limit2) ? (limit2) : (a))) \ : ((a) > (limit2) ? (limit2) : ((a) < (limit1) ? (limit1) : (a)))) #define silk_LIMIT_int silk_LIMIT #define silk_LIMIT_16 silk_LIMIT #define silk_LIMIT_32 silk_LIMIT #define silk_abs(a) (((a) > 0) ? (a) : -(a)) /* Be careful, silk_abs returns wrong when input equals to silk_intXX_MIN */ #define silk_abs_int(a) (((a) ^ ((a) >> (8 * sizeof(a) - 1))) - ((a) >> (8 * sizeof(a) - 1))) #define silk_abs_int32(a) (((a) ^ ((a) >> 31)) - ((a) >> 31)) #define silk_abs_int64(a) (((a) > 0) ? (a) : -(a)) #define silk_sign(a) ((a) > 0 ? 1 : ( (a) < 0 ? -1 : 0 )) /* PSEUDO-RANDOM GENERATOR */ /* Make sure to store the result as the seed for the next call (also in between */ /* frames), otherwise result won't be random at all. When only using some of the */ /* bits, take the most significant bits by right-shifting. */ #define silk_RAND(seed) (silk_MLA_ovflw(907633515, (seed), 196314165)) /* Add some multiplication functions that can be easily mapped to ARM. */ /* silk_SMMUL: Signed top word multiply. ARMv6 2 instruction cycles. ARMv3M+ 3 instruction cycles. use SMULL and ignore LSB registers.(except xM)*/ /*#define silk_SMMUL(a32, b32) (opus_int32)silk_RSHIFT(silk_SMLAL(silk_SMULWB((a32), (b32)), (a32), silk_RSHIFT_ROUND((b32), 16)), 16)*/ /* the following seems faster on x86 */ #define silk_SMMUL(a32, b32) (opus_int32)silk_RSHIFT64(silk_SMULL((a32), (b32)), 32) #if !defined(OPUS_X86_MAY_HAVE_SSE4_1) #define silk_burg_modified(res_nrg, res_nrg_Q, A_Q16, x, minInvGain_Q30, subfr_length, nb_subfr, D, arch) \ ((void)(arch), silk_burg_modified_c(res_nrg, res_nrg_Q, A_Q16, x, minInvGain_Q30, subfr_length, nb_subfr, D, arch)) #define silk_inner_prod16_aligned_64(inVec1, inVec2, len, arch) \ ((void)(arch),silk_inner_prod16_aligned_64_c(inVec1, inVec2, len)) #endif #include "Inlines.h" #include "MacroCount.h" #include "MacroDebug.h" #ifdef OPUS_ARM_INLINE_ASM #include "arm/SigProc_FIX_armv4.h" #endif #ifdef OPUS_ARM_INLINE_EDSP #include "arm/SigProc_FIX_armv5e.h" #endif #if defined(MIPSr1_ASM) #include "mips/sigproc_fix_mipsr1.h" #endif #ifdef __cplusplus } #endif #endif /* SILK_SIGPROC_FIX_H */ ================================================ FILE: deps/pjsip/third_party/opus/silk/VAD.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #include "stack_alloc.h" /* Silk VAD noise level estimation */ # if !defined(OPUS_X86_MAY_HAVE_SSE4_1) static OPUS_INLINE void silk_VAD_GetNoiseLevels( const opus_int32 pX[ VAD_N_BANDS ], /* I subband energies */ silk_VAD_state *psSilk_VAD /* I/O Pointer to Silk VAD state */ ); #endif /**********************************/ /* Initialization of the Silk VAD */ /**********************************/ opus_int silk_VAD_Init( /* O Return value, 0 if success */ silk_VAD_state *psSilk_VAD /* I/O Pointer to Silk VAD state */ ) { opus_int b, ret = 0; /* reset state memory */ silk_memset( psSilk_VAD, 0, sizeof( silk_VAD_state ) ); /* init noise levels */ /* Initialize array with approx pink noise levels (psd proportional to inverse of frequency) */ for( b = 0; b < VAD_N_BANDS; b++ ) { psSilk_VAD->NoiseLevelBias[ b ] = silk_max_32( silk_DIV32_16( VAD_NOISE_LEVELS_BIAS, b + 1 ), 1 ); } /* Initialize state */ for( b = 0; b < VAD_N_BANDS; b++ ) { psSilk_VAD->NL[ b ] = silk_MUL( 100, psSilk_VAD->NoiseLevelBias[ b ] ); psSilk_VAD->inv_NL[ b ] = silk_DIV32( silk_int32_MAX, psSilk_VAD->NL[ b ] ); } psSilk_VAD->counter = 15; /* init smoothed energy-to-noise ratio*/ for( b = 0; b < VAD_N_BANDS; b++ ) { psSilk_VAD->NrgRatioSmth_Q8[ b ] = 100 * 256; /* 100 * 256 --> 20 dB SNR */ } return( ret ); } /* Weighting factors for tilt measure */ static const opus_int32 tiltWeights[ VAD_N_BANDS ] = { 30000, 6000, -12000, -12000 }; /***************************************/ /* Get the speech activity level in Q8 */ /***************************************/ opus_int silk_VAD_GetSA_Q8_c( /* O Return value, 0 if success */ silk_encoder_state *psEncC, /* I/O Encoder state */ const opus_int16 pIn[] /* I PCM input */ ) { opus_int SA_Q15, pSNR_dB_Q7, input_tilt; opus_int decimated_framelength1, decimated_framelength2; opus_int decimated_framelength; opus_int dec_subframe_length, dec_subframe_offset, SNR_Q7, i, b, s; opus_int32 sumSquared, smooth_coef_Q16; opus_int16 HPstateTmp; VARDECL( opus_int16, X ); opus_int32 Xnrg[ VAD_N_BANDS ]; opus_int32 NrgToNoiseRatio_Q8[ VAD_N_BANDS ]; opus_int32 speech_nrg, x_tmp; opus_int X_offset[ VAD_N_BANDS ]; opus_int ret = 0; silk_VAD_state *psSilk_VAD = &psEncC->sVAD; SAVE_STACK; /* Safety checks */ silk_assert( VAD_N_BANDS == 4 ); silk_assert( MAX_FRAME_LENGTH >= psEncC->frame_length ); silk_assert( psEncC->frame_length <= 512 ); silk_assert( psEncC->frame_length == 8 * silk_RSHIFT( psEncC->frame_length, 3 ) ); /***********************/ /* Filter and Decimate */ /***********************/ decimated_framelength1 = silk_RSHIFT( psEncC->frame_length, 1 ); decimated_framelength2 = silk_RSHIFT( psEncC->frame_length, 2 ); decimated_framelength = silk_RSHIFT( psEncC->frame_length, 3 ); /* Decimate into 4 bands: 0 L 3L L 3L 5L - -- - -- -- 8 8 2 4 4 [0-1 kHz| temp. |1-2 kHz| 2-4 kHz | 4-8 kHz | They're arranged to allow the minimal ( frame_length / 4 ) extra scratch space during the downsampling process */ X_offset[ 0 ] = 0; X_offset[ 1 ] = decimated_framelength + decimated_framelength2; X_offset[ 2 ] = X_offset[ 1 ] + decimated_framelength; X_offset[ 3 ] = X_offset[ 2 ] + decimated_framelength2; ALLOC( X, X_offset[ 3 ] + decimated_framelength1, opus_int16 ); /* 0-8 kHz to 0-4 kHz and 4-8 kHz */ silk_ana_filt_bank_1( pIn, &psSilk_VAD->AnaState[ 0 ], X, &X[ X_offset[ 3 ] ], psEncC->frame_length ); /* 0-4 kHz to 0-2 kHz and 2-4 kHz */ silk_ana_filt_bank_1( X, &psSilk_VAD->AnaState1[ 0 ], X, &X[ X_offset[ 2 ] ], decimated_framelength1 ); /* 0-2 kHz to 0-1 kHz and 1-2 kHz */ silk_ana_filt_bank_1( X, &psSilk_VAD->AnaState2[ 0 ], X, &X[ X_offset[ 1 ] ], decimated_framelength2 ); /*********************************************/ /* HP filter on lowest band (differentiator) */ /*********************************************/ X[ decimated_framelength - 1 ] = silk_RSHIFT( X[ decimated_framelength - 1 ], 1 ); HPstateTmp = X[ decimated_framelength - 1 ]; for( i = decimated_framelength - 1; i > 0; i-- ) { X[ i - 1 ] = silk_RSHIFT( X[ i - 1 ], 1 ); X[ i ] -= X[ i - 1 ]; } X[ 0 ] -= psSilk_VAD->HPstate; psSilk_VAD->HPstate = HPstateTmp; /*************************************/ /* Calculate the energy in each band */ /*************************************/ for( b = 0; b < VAD_N_BANDS; b++ ) { /* Find the decimated framelength in the non-uniformly divided bands */ decimated_framelength = silk_RSHIFT( psEncC->frame_length, silk_min_int( VAD_N_BANDS - b, VAD_N_BANDS - 1 ) ); /* Split length into subframe lengths */ dec_subframe_length = silk_RSHIFT( decimated_framelength, VAD_INTERNAL_SUBFRAMES_LOG2 ); dec_subframe_offset = 0; /* Compute energy per sub-frame */ /* initialize with summed energy of last subframe */ Xnrg[ b ] = psSilk_VAD->XnrgSubfr[ b ]; for( s = 0; s < VAD_INTERNAL_SUBFRAMES; s++ ) { sumSquared = 0; for( i = 0; i < dec_subframe_length; i++ ) { /* The energy will be less than dec_subframe_length * ( silk_int16_MIN / 8 ) ^ 2. */ /* Therefore we can accumulate with no risk of overflow (unless dec_subframe_length > 128) */ x_tmp = silk_RSHIFT( X[ X_offset[ b ] + i + dec_subframe_offset ], 3 ); sumSquared = silk_SMLABB( sumSquared, x_tmp, x_tmp ); /* Safety check */ silk_assert( sumSquared >= 0 ); } /* Add/saturate summed energy of current subframe */ if( s < VAD_INTERNAL_SUBFRAMES - 1 ) { Xnrg[ b ] = silk_ADD_POS_SAT32( Xnrg[ b ], sumSquared ); } else { /* Look-ahead subframe */ Xnrg[ b ] = silk_ADD_POS_SAT32( Xnrg[ b ], silk_RSHIFT( sumSquared, 1 ) ); } dec_subframe_offset += dec_subframe_length; } psSilk_VAD->XnrgSubfr[ b ] = sumSquared; } /********************/ /* Noise estimation */ /********************/ silk_VAD_GetNoiseLevels( &Xnrg[ 0 ], psSilk_VAD ); /***********************************************/ /* Signal-plus-noise to noise ratio estimation */ /***********************************************/ sumSquared = 0; input_tilt = 0; for( b = 0; b < VAD_N_BANDS; b++ ) { speech_nrg = Xnrg[ b ] - psSilk_VAD->NL[ b ]; if( speech_nrg > 0 ) { /* Divide, with sufficient resolution */ if( ( Xnrg[ b ] & 0xFF800000 ) == 0 ) { NrgToNoiseRatio_Q8[ b ] = silk_DIV32( silk_LSHIFT( Xnrg[ b ], 8 ), psSilk_VAD->NL[ b ] + 1 ); } else { NrgToNoiseRatio_Q8[ b ] = silk_DIV32( Xnrg[ b ], silk_RSHIFT( psSilk_VAD->NL[ b ], 8 ) + 1 ); } /* Convert to log domain */ SNR_Q7 = silk_lin2log( NrgToNoiseRatio_Q8[ b ] ) - 8 * 128; /* Sum-of-squares */ sumSquared = silk_SMLABB( sumSquared, SNR_Q7, SNR_Q7 ); /* Q14 */ /* Tilt measure */ if( speech_nrg < ( (opus_int32)1 << 20 ) ) { /* Scale down SNR value for small subband speech energies */ SNR_Q7 = silk_SMULWB( silk_LSHIFT( silk_SQRT_APPROX( speech_nrg ), 6 ), SNR_Q7 ); } input_tilt = silk_SMLAWB( input_tilt, tiltWeights[ b ], SNR_Q7 ); } else { NrgToNoiseRatio_Q8[ b ] = 256; } } /* Mean-of-squares */ sumSquared = silk_DIV32_16( sumSquared, VAD_N_BANDS ); /* Q14 */ /* Root-mean-square approximation, scale to dBs, and write to output pointer */ pSNR_dB_Q7 = (opus_int16)( 3 * silk_SQRT_APPROX( sumSquared ) ); /* Q7 */ /*********************************/ /* Speech Probability Estimation */ /*********************************/ SA_Q15 = silk_sigm_Q15( silk_SMULWB( VAD_SNR_FACTOR_Q16, pSNR_dB_Q7 ) - VAD_NEGATIVE_OFFSET_Q5 ); /**************************/ /* Frequency Tilt Measure */ /**************************/ psEncC->input_tilt_Q15 = silk_LSHIFT( silk_sigm_Q15( input_tilt ) - 16384, 1 ); /**************************************************/ /* Scale the sigmoid output based on power levels */ /**************************************************/ speech_nrg = 0; for( b = 0; b < VAD_N_BANDS; b++ ) { /* Accumulate signal-without-noise energies, higher frequency bands have more weight */ speech_nrg += ( b + 1 ) * silk_RSHIFT( Xnrg[ b ] - psSilk_VAD->NL[ b ], 4 ); } /* Power scaling */ if( speech_nrg <= 0 ) { SA_Q15 = silk_RSHIFT( SA_Q15, 1 ); } else if( speech_nrg < 32768 ) { if( psEncC->frame_length == 10 * psEncC->fs_kHz ) { speech_nrg = silk_LSHIFT_SAT32( speech_nrg, 16 ); } else { speech_nrg = silk_LSHIFT_SAT32( speech_nrg, 15 ); } /* square-root */ speech_nrg = silk_SQRT_APPROX( speech_nrg ); SA_Q15 = silk_SMULWB( 32768 + speech_nrg, SA_Q15 ); } /* Copy the resulting speech activity in Q8 */ psEncC->speech_activity_Q8 = silk_min_int( silk_RSHIFT( SA_Q15, 7 ), silk_uint8_MAX ); /***********************************/ /* Energy Level and SNR estimation */ /***********************************/ /* Smoothing coefficient */ smooth_coef_Q16 = silk_SMULWB( VAD_SNR_SMOOTH_COEF_Q18, silk_SMULWB( (opus_int32)SA_Q15, SA_Q15 ) ); if( psEncC->frame_length == 10 * psEncC->fs_kHz ) { smooth_coef_Q16 >>= 1; } for( b = 0; b < VAD_N_BANDS; b++ ) { /* compute smoothed energy-to-noise ratio per band */ psSilk_VAD->NrgRatioSmth_Q8[ b ] = silk_SMLAWB( psSilk_VAD->NrgRatioSmth_Q8[ b ], NrgToNoiseRatio_Q8[ b ] - psSilk_VAD->NrgRatioSmth_Q8[ b ], smooth_coef_Q16 ); /* signal to noise ratio in dB per band */ SNR_Q7 = 3 * ( silk_lin2log( psSilk_VAD->NrgRatioSmth_Q8[b] ) - 8 * 128 ); /* quality = sigmoid( 0.25 * ( SNR_dB - 16 ) ); */ psEncC->input_quality_bands_Q15[ b ] = silk_sigm_Q15( silk_RSHIFT( SNR_Q7 - 16 * 128, 4 ) ); } RESTORE_STACK; return( ret ); } /**************************/ /* Noise level estimation */ /**************************/ # if !defined(OPUS_X86_MAY_HAVE_SSE4_1) static OPUS_INLINE #endif void silk_VAD_GetNoiseLevels( const opus_int32 pX[ VAD_N_BANDS ], /* I subband energies */ silk_VAD_state *psSilk_VAD /* I/O Pointer to Silk VAD state */ ) { opus_int k; opus_int32 nl, nrg, inv_nrg; opus_int coef, min_coef; /* Initially faster smoothing */ if( psSilk_VAD->counter < 1000 ) { /* 1000 = 20 sec */ min_coef = silk_DIV32_16( silk_int16_MAX, silk_RSHIFT( psSilk_VAD->counter, 4 ) + 1 ); } else { min_coef = 0; } for( k = 0; k < VAD_N_BANDS; k++ ) { /* Get old noise level estimate for current band */ nl = psSilk_VAD->NL[ k ]; silk_assert( nl >= 0 ); /* Add bias */ nrg = silk_ADD_POS_SAT32( pX[ k ], psSilk_VAD->NoiseLevelBias[ k ] ); silk_assert( nrg > 0 ); /* Invert energies */ inv_nrg = silk_DIV32( silk_int32_MAX, nrg ); silk_assert( inv_nrg >= 0 ); /* Less update when subband energy is high */ if( nrg > silk_LSHIFT( nl, 3 ) ) { coef = VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 >> 3; } else if( nrg < nl ) { coef = VAD_NOISE_LEVEL_SMOOTH_COEF_Q16; } else { coef = silk_SMULWB( silk_SMULWW( inv_nrg, nl ), VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 << 1 ); } /* Initially faster smoothing */ coef = silk_max_int( coef, min_coef ); /* Smooth inverse energies */ psSilk_VAD->inv_NL[ k ] = silk_SMLAWB( psSilk_VAD->inv_NL[ k ], inv_nrg - psSilk_VAD->inv_NL[ k ], coef ); silk_assert( psSilk_VAD->inv_NL[ k ] >= 0 ); /* Compute noise level by inverting again */ nl = silk_DIV32( silk_int32_MAX, psSilk_VAD->inv_NL[ k ] ); silk_assert( nl >= 0 ); /* Limit noise levels (guarantee 7 bits of head room) */ nl = silk_min( nl, 0x00FFFFFF ); /* Store as part of state */ psSilk_VAD->NL[ k ] = nl; } /* Increment frame counter */ psSilk_VAD->counter++; } ================================================ FILE: deps/pjsip/third_party/opus/silk/VQ_WMat_EC.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Entropy constrained matrix-weighted VQ, hard-coded to 5-element vectors, for a single input data vector */ void silk_VQ_WMat_EC_c( opus_int8 *ind, /* O index of best codebook vector */ opus_int32 *rate_dist_Q14, /* O best weighted quant error + mu * rate */ opus_int *gain_Q7, /* O sum of absolute LTP coefficients */ const opus_int16 *in_Q14, /* I input vector to be quantized */ const opus_int32 *W_Q18, /* I weighting matrix */ const opus_int8 *cb_Q7, /* I codebook */ const opus_uint8 *cb_gain_Q7, /* I codebook effective gain */ const opus_uint8 *cl_Q5, /* I code length for each codebook vector */ const opus_int mu_Q9, /* I tradeoff betw. weighted error and rate */ const opus_int32 max_gain_Q7, /* I maximum sum of absolute LTP coefficients */ opus_int L /* I number of vectors in codebook */ ) { opus_int k, gain_tmp_Q7; const opus_int8 *cb_row_Q7; opus_int16 diff_Q14[ 5 ]; opus_int32 sum1_Q14, sum2_Q16; /* Loop over codebook */ *rate_dist_Q14 = silk_int32_MAX; cb_row_Q7 = cb_Q7; for( k = 0; k < L; k++ ) { gain_tmp_Q7 = cb_gain_Q7[k]; diff_Q14[ 0 ] = in_Q14[ 0 ] - silk_LSHIFT( cb_row_Q7[ 0 ], 7 ); diff_Q14[ 1 ] = in_Q14[ 1 ] - silk_LSHIFT( cb_row_Q7[ 1 ], 7 ); diff_Q14[ 2 ] = in_Q14[ 2 ] - silk_LSHIFT( cb_row_Q7[ 2 ], 7 ); diff_Q14[ 3 ] = in_Q14[ 3 ] - silk_LSHIFT( cb_row_Q7[ 3 ], 7 ); diff_Q14[ 4 ] = in_Q14[ 4 ] - silk_LSHIFT( cb_row_Q7[ 4 ], 7 ); /* Weighted rate */ sum1_Q14 = silk_SMULBB( mu_Q9, cl_Q5[ k ] ); /* Penalty for too large gain */ sum1_Q14 = silk_ADD_LSHIFT32( sum1_Q14, silk_max( silk_SUB32( gain_tmp_Q7, max_gain_Q7 ), 0 ), 10 ); silk_assert( sum1_Q14 >= 0 ); /* first row of W_Q18 */ sum2_Q16 = silk_SMULWB( W_Q18[ 1 ], diff_Q14[ 1 ] ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 2 ], diff_Q14[ 2 ] ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 3 ], diff_Q14[ 3 ] ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 4 ], diff_Q14[ 4 ] ); sum2_Q16 = silk_LSHIFT( sum2_Q16, 1 ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 0 ], diff_Q14[ 0 ] ); sum1_Q14 = silk_SMLAWB( sum1_Q14, sum2_Q16, diff_Q14[ 0 ] ); /* second row of W_Q18 */ sum2_Q16 = silk_SMULWB( W_Q18[ 7 ], diff_Q14[ 2 ] ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 8 ], diff_Q14[ 3 ] ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 9 ], diff_Q14[ 4 ] ); sum2_Q16 = silk_LSHIFT( sum2_Q16, 1 ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 6 ], diff_Q14[ 1 ] ); sum1_Q14 = silk_SMLAWB( sum1_Q14, sum2_Q16, diff_Q14[ 1 ] ); /* third row of W_Q18 */ sum2_Q16 = silk_SMULWB( W_Q18[ 13 ], diff_Q14[ 3 ] ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 14 ], diff_Q14[ 4 ] ); sum2_Q16 = silk_LSHIFT( sum2_Q16, 1 ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 12 ], diff_Q14[ 2 ] ); sum1_Q14 = silk_SMLAWB( sum1_Q14, sum2_Q16, diff_Q14[ 2 ] ); /* fourth row of W_Q18 */ sum2_Q16 = silk_SMULWB( W_Q18[ 19 ], diff_Q14[ 4 ] ); sum2_Q16 = silk_LSHIFT( sum2_Q16, 1 ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 18 ], diff_Q14[ 3 ] ); sum1_Q14 = silk_SMLAWB( sum1_Q14, sum2_Q16, diff_Q14[ 3 ] ); /* last row of W_Q18 */ sum2_Q16 = silk_SMULWB( W_Q18[ 24 ], diff_Q14[ 4 ] ); sum1_Q14 = silk_SMLAWB( sum1_Q14, sum2_Q16, diff_Q14[ 4 ] ); silk_assert( sum1_Q14 >= 0 ); /* find best */ if( sum1_Q14 < *rate_dist_Q14 ) { *rate_dist_Q14 = sum1_Q14; *ind = (opus_int8)k; *gain_Q7 = gain_tmp_Q7; } /* Go to next cbk vector */ cb_row_Q7 += LTP_ORDER; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/ana_filt_bank_1.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" /* Coefficients for 2-band filter bank based on first-order allpass filters */ static opus_int16 A_fb1_20 = 5394 << 1; static opus_int16 A_fb1_21 = -24290; /* (opus_int16)(20623 << 1) */ /* Split signal into two decimated bands using first-order allpass filters */ void silk_ana_filt_bank_1( const opus_int16 *in, /* I Input signal [N] */ opus_int32 *S, /* I/O State vector [2] */ opus_int16 *outL, /* O Low band [N/2] */ opus_int16 *outH, /* O High band [N/2] */ const opus_int32 N /* I Number of input samples */ ) { opus_int k, N2 = silk_RSHIFT( N, 1 ); opus_int32 in32, X, Y, out_1, out_2; /* Internal variables and state are in Q10 format */ for( k = 0; k < N2; k++ ) { /* Convert to Q10 */ in32 = silk_LSHIFT( (opus_int32)in[ 2 * k ], 10 ); /* All-pass section for even input sample */ Y = silk_SUB32( in32, S[ 0 ] ); X = silk_SMLAWB( Y, Y, A_fb1_21 ); out_1 = silk_ADD32( S[ 0 ], X ); S[ 0 ] = silk_ADD32( in32, X ); /* Convert to Q10 */ in32 = silk_LSHIFT( (opus_int32)in[ 2 * k + 1 ], 10 ); /* All-pass section for odd input sample, and add to output of previous section */ Y = silk_SUB32( in32, S[ 1 ] ); X = silk_SMULWB( Y, A_fb1_20 ); out_2 = silk_ADD32( S[ 1 ], X ); S[ 1 ] = silk_ADD32( in32, X ); /* Add/subtract, convert back to int16 and store to output */ outL[ k ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_ADD32( out_2, out_1 ), 11 ) ); outH[ k ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_SUB32( out_2, out_1 ), 11 ) ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/arm/SigProc_FIX_armv4.h ================================================ /*********************************************************************** Copyright (C) 2013 Xiph.Org Foundation and contributors Copyright (c) 2013 Parrot Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_SIGPROC_FIX_ARMv4_H #define SILK_SIGPROC_FIX_ARMv4_H #undef silk_MLA static OPUS_INLINE opus_int32 silk_MLA_armv4(opus_int32 a, opus_int32 b, opus_int32 c) { opus_int32 res; __asm__( "#silk_MLA\n\t" "mla %0, %1, %2, %3\n\t" : "=&r"(res) : "r"(b), "r"(c), "r"(a) ); return res; } #define silk_MLA(a, b, c) (silk_MLA_armv4(a, b, c)) #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/arm/SigProc_FIX_armv5e.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Copyright (c) 2013 Parrot Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_SIGPROC_FIX_ARMv5E_H #define SILK_SIGPROC_FIX_ARMv5E_H #undef silk_SMULTT static OPUS_INLINE opus_int32 silk_SMULTT_armv5e(opus_int32 a, opus_int32 b) { opus_int32 res; __asm__( "#silk_SMULTT\n\t" "smultt %0, %1, %2\n\t" : "=r"(res) : "%r"(a), "r"(b) ); return res; } #define silk_SMULTT(a, b) (silk_SMULTT_armv5e(a, b)) #undef silk_SMLATT static OPUS_INLINE opus_int32 silk_SMLATT_armv5e(opus_int32 a, opus_int32 b, opus_int32 c) { opus_int32 res; __asm__( "#silk_SMLATT\n\t" "smlatt %0, %1, %2, %3\n\t" : "=r"(res) : "%r"(b), "r"(c), "r"(a) ); return res; } #define silk_SMLATT(a, b, c) (silk_SMLATT_armv5e(a, b, c)) #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/arm/macros_armv4.h ================================================ /*********************************************************************** Copyright (C) 2013 Xiph.Org Foundation and contributors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_MACROS_ARMv4_H #define SILK_MACROS_ARMv4_H /* (a32 * (opus_int32)((opus_int16)(b32))) >> 16 output have to be 32bit int */ #undef silk_SMULWB static OPUS_INLINE opus_int32 silk_SMULWB_armv4(opus_int32 a, opus_int16 b) { unsigned rd_lo; int rd_hi; __asm__( "#silk_SMULWB\n\t" "smull %0, %1, %2, %3\n\t" : "=&r"(rd_lo), "=&r"(rd_hi) : "%r"(a), "r"(b<<16) ); return rd_hi; } #define silk_SMULWB(a, b) (silk_SMULWB_armv4(a, b)) /* a32 + (b32 * (opus_int32)((opus_int16)(c32))) >> 16 output have to be 32bit int */ #undef silk_SMLAWB #define silk_SMLAWB(a, b, c) ((a) + silk_SMULWB(b, c)) /* (a32 * (b32 >> 16)) >> 16 */ #undef silk_SMULWT static OPUS_INLINE opus_int32 silk_SMULWT_armv4(opus_int32 a, opus_int32 b) { unsigned rd_lo; int rd_hi; __asm__( "#silk_SMULWT\n\t" "smull %0, %1, %2, %3\n\t" : "=&r"(rd_lo), "=&r"(rd_hi) : "%r"(a), "r"(b&~0xFFFF) ); return rd_hi; } #define silk_SMULWT(a, b) (silk_SMULWT_armv4(a, b)) /* a32 + (b32 * (c32 >> 16)) >> 16 */ #undef silk_SMLAWT #define silk_SMLAWT(a, b, c) ((a) + silk_SMULWT(b, c)) /* (a32 * b32) >> 16 */ #undef silk_SMULWW static OPUS_INLINE opus_int32 silk_SMULWW_armv4(opus_int32 a, opus_int32 b) { unsigned rd_lo; int rd_hi; __asm__( "#silk_SMULWW\n\t" "smull %0, %1, %2, %3\n\t" : "=&r"(rd_lo), "=&r"(rd_hi) : "%r"(a), "r"(b) ); return (rd_hi<<16)+(rd_lo>>16); } #define silk_SMULWW(a, b) (silk_SMULWW_armv4(a, b)) #undef silk_SMLAWW static OPUS_INLINE opus_int32 silk_SMLAWW_armv4(opus_int32 a, opus_int32 b, opus_int32 c) { unsigned rd_lo; int rd_hi; __asm__( "#silk_SMLAWW\n\t" "smull %0, %1, %2, %3\n\t" : "=&r"(rd_lo), "=&r"(rd_hi) : "%r"(b), "r"(c) ); return a+(rd_hi<<16)+(rd_lo>>16); } #define silk_SMLAWW(a, b, c) (silk_SMLAWW_armv4(a, b, c)) #endif /* SILK_MACROS_ARMv4_H */ ================================================ FILE: deps/pjsip/third_party/opus/silk/arm/macros_armv5e.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Copyright (c) 2013 Parrot Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_MACROS_ARMv5E_H #define SILK_MACROS_ARMv5E_H /* (a32 * (opus_int32)((opus_int16)(b32))) >> 16 output have to be 32bit int */ #undef silk_SMULWB static OPUS_INLINE opus_int32 silk_SMULWB_armv5e(opus_int32 a, opus_int16 b) { int res; __asm__( "#silk_SMULWB\n\t" "smulwb %0, %1, %2\n\t" : "=r"(res) : "r"(a), "r"(b) ); return res; } #define silk_SMULWB(a, b) (silk_SMULWB_armv5e(a, b)) /* a32 + (b32 * (opus_int32)((opus_int16)(c32))) >> 16 output have to be 32bit int */ #undef silk_SMLAWB static OPUS_INLINE opus_int32 silk_SMLAWB_armv5e(opus_int32 a, opus_int32 b, opus_int16 c) { int res; __asm__( "#silk_SMLAWB\n\t" "smlawb %0, %1, %2, %3\n\t" : "=r"(res) : "r"(b), "r"(c), "r"(a) ); return res; } #define silk_SMLAWB(a, b, c) (silk_SMLAWB_armv5e(a, b, c)) /* (a32 * (b32 >> 16)) >> 16 */ #undef silk_SMULWT static OPUS_INLINE opus_int32 silk_SMULWT_armv5e(opus_int32 a, opus_int32 b) { int res; __asm__( "#silk_SMULWT\n\t" "smulwt %0, %1, %2\n\t" : "=r"(res) : "r"(a), "r"(b) ); return res; } #define silk_SMULWT(a, b) (silk_SMULWT_armv5e(a, b)) /* a32 + (b32 * (c32 >> 16)) >> 16 */ #undef silk_SMLAWT static OPUS_INLINE opus_int32 silk_SMLAWT_armv5e(opus_int32 a, opus_int32 b, opus_int32 c) { int res; __asm__( "#silk_SMLAWT\n\t" "smlawt %0, %1, %2, %3\n\t" : "=r"(res) : "r"(b), "r"(c), "r"(a) ); return res; } #define silk_SMLAWT(a, b, c) (silk_SMLAWT_armv5e(a, b, c)) /* (opus_int32)((opus_int16)(a3))) * (opus_int32)((opus_int16)(b32)) output have to be 32bit int */ #undef silk_SMULBB static OPUS_INLINE opus_int32 silk_SMULBB_armv5e(opus_int32 a, opus_int32 b) { int res; __asm__( "#silk_SMULBB\n\t" "smulbb %0, %1, %2\n\t" : "=r"(res) : "%r"(a), "r"(b) ); return res; } #define silk_SMULBB(a, b) (silk_SMULBB_armv5e(a, b)) /* a32 + (opus_int32)((opus_int16)(b32)) * (opus_int32)((opus_int16)(c32)) output have to be 32bit int */ #undef silk_SMLABB static OPUS_INLINE opus_int32 silk_SMLABB_armv5e(opus_int32 a, opus_int32 b, opus_int32 c) { int res; __asm__( "#silk_SMLABB\n\t" "smlabb %0, %1, %2, %3\n\t" : "=r"(res) : "%r"(b), "r"(c), "r"(a) ); return res; } #define silk_SMLABB(a, b, c) (silk_SMLABB_armv5e(a, b, c)) /* (opus_int32)((opus_int16)(a32)) * (b32 >> 16) */ #undef silk_SMULBT static OPUS_INLINE opus_int32 silk_SMULBT_armv5e(opus_int32 a, opus_int32 b) { int res; __asm__( "#silk_SMULBT\n\t" "smulbt %0, %1, %2\n\t" : "=r"(res) : "r"(a), "r"(b) ); return res; } #define silk_SMULBT(a, b) (silk_SMULBT_armv5e(a, b)) /* a32 + (opus_int32)((opus_int16)(b32)) * (c32 >> 16) */ #undef silk_SMLABT static OPUS_INLINE opus_int32 silk_SMLABT_armv5e(opus_int32 a, opus_int32 b, opus_int32 c) { int res; __asm__( "#silk_SMLABT\n\t" "smlabt %0, %1, %2, %3\n\t" : "=r"(res) : "r"(b), "r"(c), "r"(a) ); return res; } #define silk_SMLABT(a, b, c) (silk_SMLABT_armv5e(a, b, c)) /* add/subtract with output saturated */ #undef silk_ADD_SAT32 static OPUS_INLINE opus_int32 silk_ADD_SAT32_armv5e(opus_int32 a, opus_int32 b) { int res; __asm__( "#silk_ADD_SAT32\n\t" "qadd %0, %1, %2\n\t" : "=r"(res) : "%r"(a), "r"(b) ); return res; } #define silk_ADD_SAT32(a, b) (silk_ADD_SAT32_armv5e(a, b)) #undef silk_SUB_SAT32 static OPUS_INLINE opus_int32 silk_SUB_SAT32_armv5e(opus_int32 a, opus_int32 b) { int res; __asm__( "#silk_SUB_SAT32\n\t" "qsub %0, %1, %2\n\t" : "=r"(res) : "r"(a), "r"(b) ); return res; } #define silk_SUB_SAT32(a, b) (silk_SUB_SAT32_armv5e(a, b)) #undef silk_CLZ16 static OPUS_INLINE opus_int32 silk_CLZ16_armv5(opus_int16 in16) { int res; __asm__( "#silk_CLZ16\n\t" "clz %0, %1;\n" : "=r"(res) : "r"(in16<<16|0x8000) ); return res; } #define silk_CLZ16(in16) (silk_CLZ16_armv5(in16)) #undef silk_CLZ32 static OPUS_INLINE opus_int32 silk_CLZ32_armv5(opus_int32 in32) { int res; __asm__( "#silk_CLZ32\n\t" "clz %0, %1\n\t" : "=r"(res) : "r"(in32) ); return res; } #define silk_CLZ32(in32) (silk_CLZ32_armv5(in32)) #endif /* SILK_MACROS_ARMv5E_H */ ================================================ FILE: deps/pjsip/third_party/opus/silk/biquad_alt.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ /* * * silk_biquad_alt.c * * * * Second order ARMA filter * * Can handle slowly varying filter coefficients * * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" /* Second order ARMA filter, alternative implementation */ void silk_biquad_alt( const opus_int16 *in, /* I input signal */ const opus_int32 *B_Q28, /* I MA coefficients [3] */ const opus_int32 *A_Q28, /* I AR coefficients [2] */ opus_int32 *S, /* I/O State vector [2] */ opus_int16 *out, /* O output signal */ const opus_int32 len, /* I signal length (must be even) */ opus_int stride /* I Operate on interleaved signal if > 1 */ ) { /* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */ opus_int k; opus_int32 inval, A0_U_Q28, A0_L_Q28, A1_U_Q28, A1_L_Q28, out32_Q14; /* Negate A_Q28 values and split in two parts */ A0_L_Q28 = ( -A_Q28[ 0 ] ) & 0x00003FFF; /* lower part */ A0_U_Q28 = silk_RSHIFT( -A_Q28[ 0 ], 14 ); /* upper part */ A1_L_Q28 = ( -A_Q28[ 1 ] ) & 0x00003FFF; /* lower part */ A1_U_Q28 = silk_RSHIFT( -A_Q28[ 1 ], 14 ); /* upper part */ for( k = 0; k < len; k++ ) { /* S[ 0 ], S[ 1 ]: Q12 */ inval = in[ k * stride ]; out32_Q14 = silk_LSHIFT( silk_SMLAWB( S[ 0 ], B_Q28[ 0 ], inval ), 2 ); S[ 0 ] = S[1] + silk_RSHIFT_ROUND( silk_SMULWB( out32_Q14, A0_L_Q28 ), 14 ); S[ 0 ] = silk_SMLAWB( S[ 0 ], out32_Q14, A0_U_Q28 ); S[ 0 ] = silk_SMLAWB( S[ 0 ], B_Q28[ 1 ], inval); S[ 1 ] = silk_RSHIFT_ROUND( silk_SMULWB( out32_Q14, A1_L_Q28 ), 14 ); S[ 1 ] = silk_SMLAWB( S[ 1 ], out32_Q14, A1_U_Q28 ); S[ 1 ] = silk_SMLAWB( S[ 1 ], B_Q28[ 2 ], inval ); /* Scale back to Q0 and saturate */ out[ k * stride ] = (opus_int16)silk_SAT16( silk_RSHIFT( out32_Q14 + (1<<14) - 1, 14 ) ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/bwexpander.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" /* Chirp (bandwidth expand) LP AR filter */ void silk_bwexpander( opus_int16 *ar, /* I/O AR filter to be expanded (without leading 1) */ const opus_int d, /* I Length of ar */ opus_int32 chirp_Q16 /* I Chirp factor (typically in the range 0 to 1) */ ) { opus_int i; opus_int32 chirp_minus_one_Q16 = chirp_Q16 - 65536; /* NB: Dont use silk_SMULWB, instead of silk_RSHIFT_ROUND( silk_MUL(), 16 ), below. */ /* Bias in silk_SMULWB can lead to unstable filters */ for( i = 0; i < d - 1; i++ ) { ar[ i ] = (opus_int16)silk_RSHIFT_ROUND( silk_MUL( chirp_Q16, ar[ i ] ), 16 ); chirp_Q16 += silk_RSHIFT_ROUND( silk_MUL( chirp_Q16, chirp_minus_one_Q16 ), 16 ); } ar[ d - 1 ] = (opus_int16)silk_RSHIFT_ROUND( silk_MUL( chirp_Q16, ar[ d - 1 ] ), 16 ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/bwexpander_32.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" /* Chirp (bandwidth expand) LP AR filter */ void silk_bwexpander_32( opus_int32 *ar, /* I/O AR filter to be expanded (without leading 1) */ const opus_int d, /* I Length of ar */ opus_int32 chirp_Q16 /* I Chirp factor in Q16 */ ) { opus_int i; opus_int32 chirp_minus_one_Q16 = chirp_Q16 - 65536; for( i = 0; i < d - 1; i++ ) { ar[ i ] = silk_SMULWW( chirp_Q16, ar[ i ] ); chirp_Q16 += silk_RSHIFT_ROUND( silk_MUL( chirp_Q16, chirp_minus_one_Q16 ), 16 ); } ar[ d - 1 ] = silk_SMULWW( chirp_Q16, ar[ d - 1 ] ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/check_control_input.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #include "control.h" #include "errors.h" /* Check encoder control struct */ opus_int check_control_input( silk_EncControlStruct *encControl /* I Control structure */ ) { silk_assert( encControl != NULL ); if( ( ( encControl->API_sampleRate != 8000 ) && ( encControl->API_sampleRate != 12000 ) && ( encControl->API_sampleRate != 16000 ) && ( encControl->API_sampleRate != 24000 ) && ( encControl->API_sampleRate != 32000 ) && ( encControl->API_sampleRate != 44100 ) && ( encControl->API_sampleRate != 48000 ) ) || ( ( encControl->desiredInternalSampleRate != 8000 ) && ( encControl->desiredInternalSampleRate != 12000 ) && ( encControl->desiredInternalSampleRate != 16000 ) ) || ( ( encControl->maxInternalSampleRate != 8000 ) && ( encControl->maxInternalSampleRate != 12000 ) && ( encControl->maxInternalSampleRate != 16000 ) ) || ( ( encControl->minInternalSampleRate != 8000 ) && ( encControl->minInternalSampleRate != 12000 ) && ( encControl->minInternalSampleRate != 16000 ) ) || ( encControl->minInternalSampleRate > encControl->desiredInternalSampleRate ) || ( encControl->maxInternalSampleRate < encControl->desiredInternalSampleRate ) || ( encControl->minInternalSampleRate > encControl->maxInternalSampleRate ) ) { silk_assert( 0 ); return SILK_ENC_FS_NOT_SUPPORTED; } if( encControl->payloadSize_ms != 10 && encControl->payloadSize_ms != 20 && encControl->payloadSize_ms != 40 && encControl->payloadSize_ms != 60 ) { silk_assert( 0 ); return SILK_ENC_PACKET_SIZE_NOT_SUPPORTED; } if( encControl->packetLossPercentage < 0 || encControl->packetLossPercentage > 100 ) { silk_assert( 0 ); return SILK_ENC_INVALID_LOSS_RATE; } if( encControl->useDTX < 0 || encControl->useDTX > 1 ) { silk_assert( 0 ); return SILK_ENC_INVALID_DTX_SETTING; } if( encControl->useCBR < 0 || encControl->useCBR > 1 ) { silk_assert( 0 ); return SILK_ENC_INVALID_CBR_SETTING; } if( encControl->useInBandFEC < 0 || encControl->useInBandFEC > 1 ) { silk_assert( 0 ); return SILK_ENC_INVALID_INBAND_FEC_SETTING; } if( encControl->nChannelsAPI < 1 || encControl->nChannelsAPI > ENCODER_NUM_CHANNELS ) { silk_assert( 0 ); return SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR; } if( encControl->nChannelsInternal < 1 || encControl->nChannelsInternal > ENCODER_NUM_CHANNELS ) { silk_assert( 0 ); return SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR; } if( encControl->nChannelsInternal > encControl->nChannelsAPI ) { silk_assert( 0 ); return SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR; } if( encControl->complexity < 0 || encControl->complexity > 10 ) { silk_assert( 0 ); return SILK_ENC_INVALID_COMPLEXITY_SETTING; } return SILK_NO_ERROR; } ================================================ FILE: deps/pjsip/third_party/opus/silk/code_signs.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /*#define silk_enc_map(a) ((a) > 0 ? 1 : 0)*/ /*#define silk_dec_map(a) ((a) > 0 ? 1 : -1)*/ /* shifting avoids if-statement */ #define silk_enc_map(a) ( silk_RSHIFT( (a), 15 ) + 1 ) #define silk_dec_map(a) ( silk_LSHIFT( (a), 1 ) - 1 ) /* Encodes signs of excitation */ void silk_encode_signs( ec_enc *psRangeEnc, /* I/O Compressor data structure */ const opus_int8 pulses[], /* I pulse signal */ opus_int length, /* I length of input */ const opus_int signalType, /* I Signal type */ const opus_int quantOffsetType, /* I Quantization offset type */ const opus_int sum_pulses[ MAX_NB_SHELL_BLOCKS ] /* I Sum of absolute pulses per block */ ) { opus_int i, j, p; opus_uint8 icdf[ 2 ]; const opus_int8 *q_ptr; const opus_uint8 *icdf_ptr; icdf[ 1 ] = 0; q_ptr = pulses; i = silk_SMULBB( 7, silk_ADD_LSHIFT( quantOffsetType, signalType, 1 ) ); icdf_ptr = &silk_sign_iCDF[ i ]; length = silk_RSHIFT( length + SHELL_CODEC_FRAME_LENGTH/2, LOG2_SHELL_CODEC_FRAME_LENGTH ); for( i = 0; i < length; i++ ) { p = sum_pulses[ i ]; if( p > 0 ) { icdf[ 0 ] = icdf_ptr[ silk_min( p & 0x1F, 6 ) ]; for( j = 0; j < SHELL_CODEC_FRAME_LENGTH; j++ ) { if( q_ptr[ j ] != 0 ) { ec_enc_icdf( psRangeEnc, silk_enc_map( q_ptr[ j ]), icdf, 8 ); } } } q_ptr += SHELL_CODEC_FRAME_LENGTH; } } /* Decodes signs of excitation */ void silk_decode_signs( ec_dec *psRangeDec, /* I/O Compressor data structure */ opus_int16 pulses[], /* I/O pulse signal */ opus_int length, /* I length of input */ const opus_int signalType, /* I Signal type */ const opus_int quantOffsetType, /* I Quantization offset type */ const opus_int sum_pulses[ MAX_NB_SHELL_BLOCKS ] /* I Sum of absolute pulses per block */ ) { opus_int i, j, p; opus_uint8 icdf[ 2 ]; opus_int16 *q_ptr; const opus_uint8 *icdf_ptr; icdf[ 1 ] = 0; q_ptr = pulses; i = silk_SMULBB( 7, silk_ADD_LSHIFT( quantOffsetType, signalType, 1 ) ); icdf_ptr = &silk_sign_iCDF[ i ]; length = silk_RSHIFT( length + SHELL_CODEC_FRAME_LENGTH/2, LOG2_SHELL_CODEC_FRAME_LENGTH ); for( i = 0; i < length; i++ ) { p = sum_pulses[ i ]; if( p > 0 ) { icdf[ 0 ] = icdf_ptr[ silk_min( p & 0x1F, 6 ) ]; for( j = 0; j < SHELL_CODEC_FRAME_LENGTH; j++ ) { if( q_ptr[ j ] > 0 ) { /* attach sign */ #if 0 /* conditional implementation */ if( ec_dec_icdf( psRangeDec, icdf, 8 ) == 0 ) { q_ptr[ j ] = -q_ptr[ j ]; } #else /* implementation with shift, subtraction, multiplication */ q_ptr[ j ] *= silk_dec_map( ec_dec_icdf( psRangeDec, icdf, 8 ) ); #endif } } } q_ptr += SHELL_CODEC_FRAME_LENGTH; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/control.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_CONTROL_H #define SILK_CONTROL_H #include "typedef.h" #ifdef __cplusplus extern "C" { #endif /* Decoder API flags */ #define FLAG_DECODE_NORMAL 0 #define FLAG_PACKET_LOST 1 #define FLAG_DECODE_LBRR 2 /***********************************************/ /* Structure for controlling encoder operation */ /***********************************************/ typedef struct { /* I: Number of channels; 1/2 */ opus_int32 nChannelsAPI; /* I: Number of channels; 1/2 */ opus_int32 nChannelsInternal; /* I: Input signal sampling rate in Hertz; 8000/12000/16000/24000/32000/44100/48000 */ opus_int32 API_sampleRate; /* I: Maximum internal sampling rate in Hertz; 8000/12000/16000 */ opus_int32 maxInternalSampleRate; /* I: Minimum internal sampling rate in Hertz; 8000/12000/16000 */ opus_int32 minInternalSampleRate; /* I: Soft request for internal sampling rate in Hertz; 8000/12000/16000 */ opus_int32 desiredInternalSampleRate; /* I: Number of samples per packet in milliseconds; 10/20/40/60 */ opus_int payloadSize_ms; /* I: Bitrate during active speech in bits/second; internally limited */ opus_int32 bitRate; /* I: Uplink packet loss in percent (0-100) */ opus_int packetLossPercentage; /* I: Complexity mode; 0 is lowest, 10 is highest complexity */ opus_int complexity; /* I: Flag to enable in-band Forward Error Correction (FEC); 0/1 */ opus_int useInBandFEC; /* I: Flag to enable discontinuous transmission (DTX); 0/1 */ opus_int useDTX; /* I: Flag to use constant bitrate */ opus_int useCBR; /* I: Maximum number of bits allowed for the frame */ opus_int maxBits; /* I: Causes a smooth downmix to mono */ opus_int toMono; /* I: Opus encoder is allowing us to switch bandwidth */ opus_int opusCanSwitch; /* I: Make frames as independent as possible (but still use LPC) */ opus_int reducedDependency; /* O: Internal sampling rate used, in Hertz; 8000/12000/16000 */ opus_int32 internalSampleRate; /* O: Flag that bandwidth switching is allowed (because low voice activity) */ opus_int allowBandwidthSwitch; /* O: Flag that SILK runs in WB mode without variable LP filter (use for switching between WB/SWB/FB) */ opus_int inWBmodeWithoutVariableLP; /* O: Stereo width */ opus_int stereoWidth_Q14; /* O: Tells the Opus encoder we're ready to switch */ opus_int switchReady; } silk_EncControlStruct; /**************************************************************************/ /* Structure for controlling decoder operation and reading decoder status */ /**************************************************************************/ typedef struct { /* I: Number of channels; 1/2 */ opus_int32 nChannelsAPI; /* I: Number of channels; 1/2 */ opus_int32 nChannelsInternal; /* I: Output signal sampling rate in Hertz; 8000/12000/16000/24000/32000/44100/48000 */ opus_int32 API_sampleRate; /* I: Internal sampling rate used, in Hertz; 8000/12000/16000 */ opus_int32 internalSampleRate; /* I: Number of samples per packet in milliseconds; 10/20/40/60 */ opus_int payloadSize_ms; /* O: Pitch lag of previous frame (0 if unvoiced), measured in samples at 48 kHz */ opus_int prevPitchLag; } silk_DecControlStruct; #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/control_SNR.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #include "tuning_parameters.h" /* Control SNR of redidual quantizer */ opus_int silk_control_SNR( silk_encoder_state *psEncC, /* I/O Pointer to Silk encoder state */ opus_int32 TargetRate_bps /* I Target max bitrate (bps) */ ) { opus_int k, ret = SILK_NO_ERROR; opus_int32 frac_Q6; const opus_int32 *rateTable; /* Set bitrate/coding quality */ TargetRate_bps = silk_LIMIT( TargetRate_bps, MIN_TARGET_RATE_BPS, MAX_TARGET_RATE_BPS ); if( TargetRate_bps != psEncC->TargetRate_bps ) { psEncC->TargetRate_bps = TargetRate_bps; /* If new TargetRate_bps, translate to SNR_dB value */ if( psEncC->fs_kHz == 8 ) { rateTable = silk_TargetRate_table_NB; } else if( psEncC->fs_kHz == 12 ) { rateTable = silk_TargetRate_table_MB; } else { rateTable = silk_TargetRate_table_WB; } /* Reduce bitrate for 10 ms modes in these calculations */ if( psEncC->nb_subfr == 2 ) { TargetRate_bps -= REDUCE_BITRATE_10_MS_BPS; } /* Find bitrate interval in table and interpolate */ for( k = 1; k < TARGET_RATE_TAB_SZ; k++ ) { if( TargetRate_bps <= rateTable[ k ] ) { frac_Q6 = silk_DIV32( silk_LSHIFT( TargetRate_bps - rateTable[ k - 1 ], 6 ), rateTable[ k ] - rateTable[ k - 1 ] ); psEncC->SNR_dB_Q7 = silk_LSHIFT( silk_SNR_table_Q1[ k - 1 ], 6 ) + silk_MUL( frac_Q6, silk_SNR_table_Q1[ k ] - silk_SNR_table_Q1[ k - 1 ] ); break; } } } return ret; } ================================================ FILE: deps/pjsip/third_party/opus/silk/control_audio_bandwidth.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #include "tuning_parameters.h" /* Control internal sampling rate */ opus_int silk_control_audio_bandwidth( silk_encoder_state *psEncC, /* I/O Pointer to Silk encoder state */ silk_EncControlStruct *encControl /* I Control structure */ ) { opus_int fs_kHz; opus_int32 fs_Hz; fs_kHz = psEncC->fs_kHz; fs_Hz = silk_SMULBB( fs_kHz, 1000 ); if( fs_Hz == 0 ) { /* Encoder has just been initialized */ fs_Hz = silk_min( psEncC->desiredInternal_fs_Hz, psEncC->API_fs_Hz ); fs_kHz = silk_DIV32_16( fs_Hz, 1000 ); } else if( fs_Hz > psEncC->API_fs_Hz || fs_Hz > psEncC->maxInternal_fs_Hz || fs_Hz < psEncC->minInternal_fs_Hz ) { /* Make sure internal rate is not higher than external rate or maximum allowed, or lower than minimum allowed */ fs_Hz = psEncC->API_fs_Hz; fs_Hz = silk_min( fs_Hz, psEncC->maxInternal_fs_Hz ); fs_Hz = silk_max( fs_Hz, psEncC->minInternal_fs_Hz ); fs_kHz = silk_DIV32_16( fs_Hz, 1000 ); } else { /* State machine for the internal sampling rate switching */ if( psEncC->sLP.transition_frame_no >= TRANSITION_FRAMES ) { /* Stop transition phase */ psEncC->sLP.mode = 0; } if( psEncC->allow_bandwidth_switch || encControl->opusCanSwitch ) { /* Check if we should switch down */ if( silk_SMULBB( psEncC->fs_kHz, 1000 ) > psEncC->desiredInternal_fs_Hz ) { /* Switch down */ if( psEncC->sLP.mode == 0 ) { /* New transition */ psEncC->sLP.transition_frame_no = TRANSITION_FRAMES; /* Reset transition filter state */ silk_memset( psEncC->sLP.In_LP_State, 0, sizeof( psEncC->sLP.In_LP_State ) ); } if( encControl->opusCanSwitch ) { /* Stop transition phase */ psEncC->sLP.mode = 0; /* Switch to a lower sample frequency */ fs_kHz = psEncC->fs_kHz == 16 ? 12 : 8; } else { if( psEncC->sLP.transition_frame_no <= 0 ) { encControl->switchReady = 1; /* Make room for redundancy */ encControl->maxBits -= encControl->maxBits * 5 / ( encControl->payloadSize_ms + 5 ); } else { /* Direction: down (at double speed) */ psEncC->sLP.mode = -2; } } } else /* Check if we should switch up */ if( silk_SMULBB( psEncC->fs_kHz, 1000 ) < psEncC->desiredInternal_fs_Hz ) { /* Switch up */ if( encControl->opusCanSwitch ) { /* Switch to a higher sample frequency */ fs_kHz = psEncC->fs_kHz == 8 ? 12 : 16; /* New transition */ psEncC->sLP.transition_frame_no = 0; /* Reset transition filter state */ silk_memset( psEncC->sLP.In_LP_State, 0, sizeof( psEncC->sLP.In_LP_State ) ); /* Direction: up */ psEncC->sLP.mode = 1; } else { if( psEncC->sLP.mode == 0 ) { encControl->switchReady = 1; /* Make room for redundancy */ encControl->maxBits -= encControl->maxBits * 5 / ( encControl->payloadSize_ms + 5 ); } else { /* Direction: up */ psEncC->sLP.mode = 1; } } } else { if (psEncC->sLP.mode<0) psEncC->sLP.mode = 1; } } } return fs_kHz; } ================================================ FILE: deps/pjsip/third_party/opus/silk/control_codec.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef FIXED_POINT #include "main_FIX.h" #define silk_encoder_state_Fxx silk_encoder_state_FIX #else #include "main_FLP.h" #define silk_encoder_state_Fxx silk_encoder_state_FLP #endif #include "stack_alloc.h" #include "tuning_parameters.h" #include "pitch_est_defines.h" static opus_int silk_setup_resamplers( silk_encoder_state_Fxx *psEnc, /* I/O */ opus_int fs_kHz /* I */ ); static opus_int silk_setup_fs( silk_encoder_state_Fxx *psEnc, /* I/O */ opus_int fs_kHz, /* I */ opus_int PacketSize_ms /* I */ ); static opus_int silk_setup_complexity( silk_encoder_state *psEncC, /* I/O */ opus_int Complexity /* I */ ); static OPUS_INLINE opus_int silk_setup_LBRR( silk_encoder_state *psEncC, /* I/O */ const opus_int32 TargetRate_bps /* I */ ); /* Control encoder */ opus_int silk_control_encoder( silk_encoder_state_Fxx *psEnc, /* I/O Pointer to Silk encoder state */ silk_EncControlStruct *encControl, /* I Control structure */ const opus_int32 TargetRate_bps, /* I Target max bitrate (bps) */ const opus_int allow_bw_switch, /* I Flag to allow switching audio bandwidth */ const opus_int channelNb, /* I Channel number */ const opus_int force_fs_kHz ) { opus_int fs_kHz, ret = 0; psEnc->sCmn.useDTX = encControl->useDTX; psEnc->sCmn.useCBR = encControl->useCBR; psEnc->sCmn.API_fs_Hz = encControl->API_sampleRate; psEnc->sCmn.maxInternal_fs_Hz = encControl->maxInternalSampleRate; psEnc->sCmn.minInternal_fs_Hz = encControl->minInternalSampleRate; psEnc->sCmn.desiredInternal_fs_Hz = encControl->desiredInternalSampleRate; psEnc->sCmn.useInBandFEC = encControl->useInBandFEC; psEnc->sCmn.nChannelsAPI = encControl->nChannelsAPI; psEnc->sCmn.nChannelsInternal = encControl->nChannelsInternal; psEnc->sCmn.allow_bandwidth_switch = allow_bw_switch; psEnc->sCmn.channelNb = channelNb; if( psEnc->sCmn.controlled_since_last_payload != 0 && psEnc->sCmn.prefillFlag == 0 ) { if( psEnc->sCmn.API_fs_Hz != psEnc->sCmn.prev_API_fs_Hz && psEnc->sCmn.fs_kHz > 0 ) { /* Change in API sampling rate in the middle of encoding a packet */ ret += silk_setup_resamplers( psEnc, psEnc->sCmn.fs_kHz ); } return ret; } /* Beyond this point we know that there are no previously coded frames in the payload buffer */ /********************************************/ /* Determine internal sampling rate */ /********************************************/ fs_kHz = silk_control_audio_bandwidth( &psEnc->sCmn, encControl ); if( force_fs_kHz ) { fs_kHz = force_fs_kHz; } /********************************************/ /* Prepare resampler and buffered data */ /********************************************/ ret += silk_setup_resamplers( psEnc, fs_kHz ); /********************************************/ /* Set internal sampling frequency */ /********************************************/ ret += silk_setup_fs( psEnc, fs_kHz, encControl->payloadSize_ms ); /********************************************/ /* Set encoding complexity */ /********************************************/ ret += silk_setup_complexity( &psEnc->sCmn, encControl->complexity ); /********************************************/ /* Set packet loss rate measured by farend */ /********************************************/ psEnc->sCmn.PacketLoss_perc = encControl->packetLossPercentage; /********************************************/ /* Set LBRR usage */ /********************************************/ ret += silk_setup_LBRR( &psEnc->sCmn, TargetRate_bps ); psEnc->sCmn.controlled_since_last_payload = 1; return ret; } static opus_int silk_setup_resamplers( silk_encoder_state_Fxx *psEnc, /* I/O */ opus_int fs_kHz /* I */ ) { opus_int ret = SILK_NO_ERROR; SAVE_STACK; if( psEnc->sCmn.fs_kHz != fs_kHz || psEnc->sCmn.prev_API_fs_Hz != psEnc->sCmn.API_fs_Hz ) { if( psEnc->sCmn.fs_kHz == 0 ) { /* Initialize the resampler for enc_API.c preparing resampling from API_fs_Hz to fs_kHz */ ret += silk_resampler_init( &psEnc->sCmn.resampler_state, psEnc->sCmn.API_fs_Hz, fs_kHz * 1000, 1 ); } else { VARDECL( opus_int16, x_buf_API_fs_Hz ); VARDECL( silk_resampler_state_struct, temp_resampler_state ); #ifdef FIXED_POINT opus_int16 *x_bufFIX = psEnc->x_buf; #else VARDECL( opus_int16, x_bufFIX ); opus_int32 new_buf_samples; #endif opus_int32 api_buf_samples; opus_int32 old_buf_samples; opus_int32 buf_length_ms; buf_length_ms = silk_LSHIFT( psEnc->sCmn.nb_subfr * 5, 1 ) + LA_SHAPE_MS; old_buf_samples = buf_length_ms * psEnc->sCmn.fs_kHz; #ifndef FIXED_POINT new_buf_samples = buf_length_ms * fs_kHz; ALLOC( x_bufFIX, silk_max( old_buf_samples, new_buf_samples ), opus_int16 ); silk_float2short_array( x_bufFIX, psEnc->x_buf, old_buf_samples ); #endif /* Initialize resampler for temporary resampling of x_buf data to API_fs_Hz */ ALLOC( temp_resampler_state, 1, silk_resampler_state_struct ); ret += silk_resampler_init( temp_resampler_state, silk_SMULBB( psEnc->sCmn.fs_kHz, 1000 ), psEnc->sCmn.API_fs_Hz, 0 ); /* Calculate number of samples to temporarily upsample */ api_buf_samples = buf_length_ms * silk_DIV32_16( psEnc->sCmn.API_fs_Hz, 1000 ); /* Temporary resampling of x_buf data to API_fs_Hz */ ALLOC( x_buf_API_fs_Hz, api_buf_samples, opus_int16 ); ret += silk_resampler( temp_resampler_state, x_buf_API_fs_Hz, x_bufFIX, old_buf_samples ); /* Initialize the resampler for enc_API.c preparing resampling from API_fs_Hz to fs_kHz */ ret += silk_resampler_init( &psEnc->sCmn.resampler_state, psEnc->sCmn.API_fs_Hz, silk_SMULBB( fs_kHz, 1000 ), 1 ); /* Correct resampler state by resampling buffered data from API_fs_Hz to fs_kHz */ ret += silk_resampler( &psEnc->sCmn.resampler_state, x_bufFIX, x_buf_API_fs_Hz, api_buf_samples ); #ifndef FIXED_POINT silk_short2float_array( psEnc->x_buf, x_bufFIX, new_buf_samples); #endif } } psEnc->sCmn.prev_API_fs_Hz = psEnc->sCmn.API_fs_Hz; RESTORE_STACK; return ret; } static opus_int silk_setup_fs( silk_encoder_state_Fxx *psEnc, /* I/O */ opus_int fs_kHz, /* I */ opus_int PacketSize_ms /* I */ ) { opus_int ret = SILK_NO_ERROR; /* Set packet size */ if( PacketSize_ms != psEnc->sCmn.PacketSize_ms ) { if( ( PacketSize_ms != 10 ) && ( PacketSize_ms != 20 ) && ( PacketSize_ms != 40 ) && ( PacketSize_ms != 60 ) ) { ret = SILK_ENC_PACKET_SIZE_NOT_SUPPORTED; } if( PacketSize_ms <= 10 ) { psEnc->sCmn.nFramesPerPacket = 1; psEnc->sCmn.nb_subfr = PacketSize_ms == 10 ? 2 : 1; psEnc->sCmn.frame_length = silk_SMULBB( PacketSize_ms, fs_kHz ); psEnc->sCmn.pitch_LPC_win_length = silk_SMULBB( FIND_PITCH_LPC_WIN_MS_2_SF, fs_kHz ); if( psEnc->sCmn.fs_kHz == 8 ) { psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_10_ms_NB_iCDF; } else { psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_10_ms_iCDF; } } else { psEnc->sCmn.nFramesPerPacket = silk_DIV32_16( PacketSize_ms, MAX_FRAME_LENGTH_MS ); psEnc->sCmn.nb_subfr = MAX_NB_SUBFR; psEnc->sCmn.frame_length = silk_SMULBB( 20, fs_kHz ); psEnc->sCmn.pitch_LPC_win_length = silk_SMULBB( FIND_PITCH_LPC_WIN_MS, fs_kHz ); if( psEnc->sCmn.fs_kHz == 8 ) { psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_NB_iCDF; } else { psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_iCDF; } } psEnc->sCmn.PacketSize_ms = PacketSize_ms; psEnc->sCmn.TargetRate_bps = 0; /* trigger new SNR computation */ } /* Set internal sampling frequency */ silk_assert( fs_kHz == 8 || fs_kHz == 12 || fs_kHz == 16 ); silk_assert( psEnc->sCmn.nb_subfr == 2 || psEnc->sCmn.nb_subfr == 4 ); if( psEnc->sCmn.fs_kHz != fs_kHz ) { /* reset part of the state */ silk_memset( &psEnc->sShape, 0, sizeof( psEnc->sShape ) ); silk_memset( &psEnc->sPrefilt, 0, sizeof( psEnc->sPrefilt ) ); silk_memset( &psEnc->sCmn.sNSQ, 0, sizeof( psEnc->sCmn.sNSQ ) ); silk_memset( psEnc->sCmn.prev_NLSFq_Q15, 0, sizeof( psEnc->sCmn.prev_NLSFq_Q15 ) ); silk_memset( &psEnc->sCmn.sLP.In_LP_State, 0, sizeof( psEnc->sCmn.sLP.In_LP_State ) ); psEnc->sCmn.inputBufIx = 0; psEnc->sCmn.nFramesEncoded = 0; psEnc->sCmn.TargetRate_bps = 0; /* trigger new SNR computation */ /* Initialize non-zero parameters */ psEnc->sCmn.prevLag = 100; psEnc->sCmn.first_frame_after_reset = 1; psEnc->sPrefilt.lagPrev = 100; psEnc->sShape.LastGainIndex = 10; psEnc->sCmn.sNSQ.lagPrev = 100; psEnc->sCmn.sNSQ.prev_gain_Q16 = 65536; psEnc->sCmn.prevSignalType = TYPE_NO_VOICE_ACTIVITY; psEnc->sCmn.fs_kHz = fs_kHz; if( psEnc->sCmn.fs_kHz == 8 ) { if( psEnc->sCmn.nb_subfr == MAX_NB_SUBFR ) { psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_NB_iCDF; } else { psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_10_ms_NB_iCDF; } } else { if( psEnc->sCmn.nb_subfr == MAX_NB_SUBFR ) { psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_iCDF; } else { psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_10_ms_iCDF; } } if( psEnc->sCmn.fs_kHz == 8 || psEnc->sCmn.fs_kHz == 12 ) { psEnc->sCmn.predictLPCOrder = MIN_LPC_ORDER; psEnc->sCmn.psNLSF_CB = &silk_NLSF_CB_NB_MB; } else { psEnc->sCmn.predictLPCOrder = MAX_LPC_ORDER; psEnc->sCmn.psNLSF_CB = &silk_NLSF_CB_WB; } psEnc->sCmn.subfr_length = SUB_FRAME_LENGTH_MS * fs_kHz; psEnc->sCmn.frame_length = silk_SMULBB( psEnc->sCmn.subfr_length, psEnc->sCmn.nb_subfr ); psEnc->sCmn.ltp_mem_length = silk_SMULBB( LTP_MEM_LENGTH_MS, fs_kHz ); psEnc->sCmn.la_pitch = silk_SMULBB( LA_PITCH_MS, fs_kHz ); psEnc->sCmn.max_pitch_lag = silk_SMULBB( 18, fs_kHz ); if( psEnc->sCmn.nb_subfr == MAX_NB_SUBFR ) { psEnc->sCmn.pitch_LPC_win_length = silk_SMULBB( FIND_PITCH_LPC_WIN_MS, fs_kHz ); } else { psEnc->sCmn.pitch_LPC_win_length = silk_SMULBB( FIND_PITCH_LPC_WIN_MS_2_SF, fs_kHz ); } if( psEnc->sCmn.fs_kHz == 16 ) { psEnc->sCmn.mu_LTP_Q9 = SILK_FIX_CONST( MU_LTP_QUANT_WB, 9 ); psEnc->sCmn.pitch_lag_low_bits_iCDF = silk_uniform8_iCDF; } else if( psEnc->sCmn.fs_kHz == 12 ) { psEnc->sCmn.mu_LTP_Q9 = SILK_FIX_CONST( MU_LTP_QUANT_MB, 9 ); psEnc->sCmn.pitch_lag_low_bits_iCDF = silk_uniform6_iCDF; } else { psEnc->sCmn.mu_LTP_Q9 = SILK_FIX_CONST( MU_LTP_QUANT_NB, 9 ); psEnc->sCmn.pitch_lag_low_bits_iCDF = silk_uniform4_iCDF; } } /* Check that settings are valid */ silk_assert( ( psEnc->sCmn.subfr_length * psEnc->sCmn.nb_subfr ) == psEnc->sCmn.frame_length ); return ret; } static opus_int silk_setup_complexity( silk_encoder_state *psEncC, /* I/O */ opus_int Complexity /* I */ ) { opus_int ret = 0; /* Set encoding complexity */ silk_assert( Complexity >= 0 && Complexity <= 10 ); if( Complexity < 2 ) { psEncC->pitchEstimationComplexity = SILK_PE_MIN_COMPLEX; psEncC->pitchEstimationThreshold_Q16 = SILK_FIX_CONST( 0.8, 16 ); psEncC->pitchEstimationLPCOrder = 6; psEncC->shapingLPCOrder = 8; psEncC->la_shape = 3 * psEncC->fs_kHz; psEncC->nStatesDelayedDecision = 1; psEncC->useInterpolatedNLSFs = 0; psEncC->LTPQuantLowComplexity = 1; psEncC->NLSF_MSVQ_Survivors = 2; psEncC->warping_Q16 = 0; } else if( Complexity < 4 ) { psEncC->pitchEstimationComplexity = SILK_PE_MID_COMPLEX; psEncC->pitchEstimationThreshold_Q16 = SILK_FIX_CONST( 0.76, 16 ); psEncC->pitchEstimationLPCOrder = 8; psEncC->shapingLPCOrder = 10; psEncC->la_shape = 5 * psEncC->fs_kHz; psEncC->nStatesDelayedDecision = 1; psEncC->useInterpolatedNLSFs = 0; psEncC->LTPQuantLowComplexity = 0; psEncC->NLSF_MSVQ_Survivors = 4; psEncC->warping_Q16 = 0; } else if( Complexity < 6 ) { psEncC->pitchEstimationComplexity = SILK_PE_MID_COMPLEX; psEncC->pitchEstimationThreshold_Q16 = SILK_FIX_CONST( 0.74, 16 ); psEncC->pitchEstimationLPCOrder = 10; psEncC->shapingLPCOrder = 12; psEncC->la_shape = 5 * psEncC->fs_kHz; psEncC->nStatesDelayedDecision = 2; psEncC->useInterpolatedNLSFs = 1; psEncC->LTPQuantLowComplexity = 0; psEncC->NLSF_MSVQ_Survivors = 8; psEncC->warping_Q16 = psEncC->fs_kHz * SILK_FIX_CONST( WARPING_MULTIPLIER, 16 ); } else if( Complexity < 8 ) { psEncC->pitchEstimationComplexity = SILK_PE_MID_COMPLEX; psEncC->pitchEstimationThreshold_Q16 = SILK_FIX_CONST( 0.72, 16 ); psEncC->pitchEstimationLPCOrder = 12; psEncC->shapingLPCOrder = 14; psEncC->la_shape = 5 * psEncC->fs_kHz; psEncC->nStatesDelayedDecision = 3; psEncC->useInterpolatedNLSFs = 1; psEncC->LTPQuantLowComplexity = 0; psEncC->NLSF_MSVQ_Survivors = 16; psEncC->warping_Q16 = psEncC->fs_kHz * SILK_FIX_CONST( WARPING_MULTIPLIER, 16 ); } else { psEncC->pitchEstimationComplexity = SILK_PE_MAX_COMPLEX; psEncC->pitchEstimationThreshold_Q16 = SILK_FIX_CONST( 0.7, 16 ); psEncC->pitchEstimationLPCOrder = 16; psEncC->shapingLPCOrder = 16; psEncC->la_shape = 5 * psEncC->fs_kHz; psEncC->nStatesDelayedDecision = MAX_DEL_DEC_STATES; psEncC->useInterpolatedNLSFs = 1; psEncC->LTPQuantLowComplexity = 0; psEncC->NLSF_MSVQ_Survivors = 32; psEncC->warping_Q16 = psEncC->fs_kHz * SILK_FIX_CONST( WARPING_MULTIPLIER, 16 ); } /* Do not allow higher pitch estimation LPC order than predict LPC order */ psEncC->pitchEstimationLPCOrder = silk_min_int( psEncC->pitchEstimationLPCOrder, psEncC->predictLPCOrder ); psEncC->shapeWinLength = SUB_FRAME_LENGTH_MS * psEncC->fs_kHz + 2 * psEncC->la_shape; psEncC->Complexity = Complexity; silk_assert( psEncC->pitchEstimationLPCOrder <= MAX_FIND_PITCH_LPC_ORDER ); silk_assert( psEncC->shapingLPCOrder <= MAX_SHAPE_LPC_ORDER ); silk_assert( psEncC->nStatesDelayedDecision <= MAX_DEL_DEC_STATES ); silk_assert( psEncC->warping_Q16 <= 32767 ); silk_assert( psEncC->la_shape <= LA_SHAPE_MAX ); silk_assert( psEncC->shapeWinLength <= SHAPE_LPC_WIN_MAX ); silk_assert( psEncC->NLSF_MSVQ_Survivors <= NLSF_VQ_MAX_SURVIVORS ); return ret; } static OPUS_INLINE opus_int silk_setup_LBRR( silk_encoder_state *psEncC, /* I/O */ const opus_int32 TargetRate_bps /* I */ ) { opus_int LBRR_in_previous_packet, ret = SILK_NO_ERROR; opus_int32 LBRR_rate_thres_bps; LBRR_in_previous_packet = psEncC->LBRR_enabled; psEncC->LBRR_enabled = 0; if( psEncC->useInBandFEC && psEncC->PacketLoss_perc > 0 ) { if( psEncC->fs_kHz == 8 ) { LBRR_rate_thres_bps = LBRR_NB_MIN_RATE_BPS; } else if( psEncC->fs_kHz == 12 ) { LBRR_rate_thres_bps = LBRR_MB_MIN_RATE_BPS; } else { LBRR_rate_thres_bps = LBRR_WB_MIN_RATE_BPS; } LBRR_rate_thres_bps = silk_SMULWB( silk_MUL( LBRR_rate_thres_bps, 125 - silk_min( psEncC->PacketLoss_perc, 25 ) ), SILK_FIX_CONST( 0.01, 16 ) ); if( TargetRate_bps > LBRR_rate_thres_bps ) { /* Set gain increase for coding LBRR excitation */ if( LBRR_in_previous_packet == 0 ) { /* Previous packet did not have LBRR, and was therefore coded at a higher bitrate */ psEncC->LBRR_GainIncreases = 7; } else { psEncC->LBRR_GainIncreases = silk_max_int( 7 - silk_SMULWB( (opus_int32)psEncC->PacketLoss_perc, SILK_FIX_CONST( 0.4, 16 ) ), 2 ); } psEncC->LBRR_enabled = 1; } } return ret; } ================================================ FILE: deps/pjsip/third_party/opus/silk/debug.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "debug.h" #include "SigProc_FIX.h" #if SILK_TIC_TOC #ifdef _WIN32 #if (defined(_WIN32) || defined(_WINCE)) #include /* timer */ #else /* Linux or Mac*/ #include #endif unsigned long silk_GetHighResolutionTime(void) /* O time in usec*/ { /* Returns a time counter in microsec */ /* the resolution is platform dependent */ /* but is typically 1.62 us resolution */ LARGE_INTEGER lpPerformanceCount; LARGE_INTEGER lpFrequency; QueryPerformanceCounter(&lpPerformanceCount); QueryPerformanceFrequency(&lpFrequency); return (unsigned long)((1000000*(lpPerformanceCount.QuadPart)) / lpFrequency.QuadPart); } #else /* Linux or Mac*/ unsigned long GetHighResolutionTime(void) /* O time in usec*/ { struct timeval tv; gettimeofday(&tv, 0); return((tv.tv_sec*1000000)+(tv.tv_usec)); } #endif int silk_Timer_nTimers = 0; int silk_Timer_depth_ctr = 0; char silk_Timer_tags[silk_NUM_TIMERS_MAX][silk_NUM_TIMERS_MAX_TAG_LEN]; #ifdef WIN32 LARGE_INTEGER silk_Timer_start[silk_NUM_TIMERS_MAX]; #else unsigned long silk_Timer_start[silk_NUM_TIMERS_MAX]; #endif unsigned int silk_Timer_cnt[silk_NUM_TIMERS_MAX]; opus_int64 silk_Timer_min[silk_NUM_TIMERS_MAX]; opus_int64 silk_Timer_sum[silk_NUM_TIMERS_MAX]; opus_int64 silk_Timer_max[silk_NUM_TIMERS_MAX]; opus_int64 silk_Timer_depth[silk_NUM_TIMERS_MAX]; #ifdef WIN32 void silk_TimerSave(char *file_name) { if( silk_Timer_nTimers > 0 ) { int k; FILE *fp; LARGE_INTEGER lpFrequency; LARGE_INTEGER lpPerformanceCount1, lpPerformanceCount2; int del = 0x7FFFFFFF; double avg, sum_avg; /* estimate overhead of calling performance counters */ for( k = 0; k < 1000; k++ ) { QueryPerformanceCounter(&lpPerformanceCount1); QueryPerformanceCounter(&lpPerformanceCount2); lpPerformanceCount2.QuadPart -= lpPerformanceCount1.QuadPart; if( (int)lpPerformanceCount2.LowPart < del ) del = lpPerformanceCount2.LowPart; } QueryPerformanceFrequency(&lpFrequency); /* print results to file */ sum_avg = 0.0f; for( k = 0; k < silk_Timer_nTimers; k++ ) { if (silk_Timer_depth[k] == 0) { sum_avg += (1e6 * silk_Timer_sum[k] / silk_Timer_cnt[k] - del) / lpFrequency.QuadPart * silk_Timer_cnt[k]; } } fp = fopen(file_name, "w"); fprintf(fp, " min avg %% max count\n"); for( k = 0; k < silk_Timer_nTimers; k++ ) { if (silk_Timer_depth[k] == 0) { fprintf(fp, "%-28s", silk_Timer_tags[k]); } else if (silk_Timer_depth[k] == 1) { fprintf(fp, " %-27s", silk_Timer_tags[k]); } else if (silk_Timer_depth[k] == 2) { fprintf(fp, " %-26s", silk_Timer_tags[k]); } else if (silk_Timer_depth[k] == 3) { fprintf(fp, " %-25s", silk_Timer_tags[k]); } else { fprintf(fp, " %-24s", silk_Timer_tags[k]); } avg = (1e6 * silk_Timer_sum[k] / silk_Timer_cnt[k] - del) / lpFrequency.QuadPart; fprintf(fp, "%8.2f", (1e6 * (silk_max_64(silk_Timer_min[k] - del, 0))) / lpFrequency.QuadPart); fprintf(fp, "%12.2f %6.2f", avg, 100.0 * avg / sum_avg * silk_Timer_cnt[k]); fprintf(fp, "%12.2f", (1e6 * (silk_max_64(silk_Timer_max[k] - del, 0))) / lpFrequency.QuadPart); fprintf(fp, "%10d\n", silk_Timer_cnt[k]); } fprintf(fp, " microseconds\n"); fclose(fp); } } #else void silk_TimerSave(char *file_name) { if( silk_Timer_nTimers > 0 ) { int k; FILE *fp; /* print results to file */ fp = fopen(file_name, "w"); fprintf(fp, " min avg max count\n"); for( k = 0; k < silk_Timer_nTimers; k++ ) { if (silk_Timer_depth[k] == 0) { fprintf(fp, "%-28s", silk_Timer_tags[k]); } else if (silk_Timer_depth[k] == 1) { fprintf(fp, " %-27s", silk_Timer_tags[k]); } else if (silk_Timer_depth[k] == 2) { fprintf(fp, " %-26s", silk_Timer_tags[k]); } else if (silk_Timer_depth[k] == 3) { fprintf(fp, " %-25s", silk_Timer_tags[k]); } else { fprintf(fp, " %-24s", silk_Timer_tags[k]); } fprintf(fp, "%d ", silk_Timer_min[k]); fprintf(fp, "%f ", (double)silk_Timer_sum[k] / (double)silk_Timer_cnt[k]); fprintf(fp, "%d ", silk_Timer_max[k]); fprintf(fp, "%10d\n", silk_Timer_cnt[k]); } fprintf(fp, " microseconds\n"); fclose(fp); } } #endif #endif /* SILK_TIC_TOC */ #if SILK_DEBUG FILE *silk_debug_store_fp[ silk_NUM_STORES_MAX ]; int silk_debug_store_count = 0; #endif /* SILK_DEBUG */ ================================================ FILE: deps/pjsip/third_party/opus/silk/debug.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_DEBUG_H #define SILK_DEBUG_H #include "typedef.h" #include /* file writing */ #include /* strcpy, strcmp */ #ifdef __cplusplus extern "C" { #endif unsigned long GetHighResolutionTime(void); /* O time in usec*/ /* make SILK_DEBUG dependent on compiler's _DEBUG */ #if defined _WIN32 #ifdef _DEBUG #define SILK_DEBUG 1 #else #define SILK_DEBUG 0 #endif /* overrule the above */ #if 0 /* #define NO_ASSERTS*/ #undef SILK_DEBUG #define SILK_DEBUG 1 #endif #else #define SILK_DEBUG 0 #endif /* Flag for using timers */ #define SILK_TIC_TOC 0 #if SILK_TIC_TOC #if (defined(_WIN32) || defined(_WINCE)) #include /* timer */ #else /* Linux or Mac*/ #include #endif /*********************************/ /* timer functions for profiling */ /*********************************/ /* example: */ /* */ /* TIC(LPC) */ /* do_LPC(in_vec, order, acoef); // do LPC analysis */ /* TOC(LPC) */ /* */ /* and call the following just before exiting (from main) */ /* */ /* silk_TimerSave("silk_TimingData.txt"); */ /* */ /* results are now in silk_TimingData.txt */ void silk_TimerSave(char *file_name); /* max number of timers (in different locations) */ #define silk_NUM_TIMERS_MAX 50 /* max length of name tags in TIC(..), TOC(..) */ #define silk_NUM_TIMERS_MAX_TAG_LEN 30 extern int silk_Timer_nTimers; extern int silk_Timer_depth_ctr; extern char silk_Timer_tags[silk_NUM_TIMERS_MAX][silk_NUM_TIMERS_MAX_TAG_LEN]; #ifdef _WIN32 extern LARGE_INTEGER silk_Timer_start[silk_NUM_TIMERS_MAX]; #else extern unsigned long silk_Timer_start[silk_NUM_TIMERS_MAX]; #endif extern unsigned int silk_Timer_cnt[silk_NUM_TIMERS_MAX]; extern opus_int64 silk_Timer_sum[silk_NUM_TIMERS_MAX]; extern opus_int64 silk_Timer_max[silk_NUM_TIMERS_MAX]; extern opus_int64 silk_Timer_min[silk_NUM_TIMERS_MAX]; extern opus_int64 silk_Timer_depth[silk_NUM_TIMERS_MAX]; /* WARNING: TIC()/TOC can measure only up to 0.1 seconds at a time */ #ifdef _WIN32 #define TIC(TAG_NAME) { \ static int init = 0; \ static int ID = -1; \ if( init == 0 ) \ { \ int k; \ init = 1; \ for( k = 0; k < silk_Timer_nTimers; k++ ) { \ if( strcmp(silk_Timer_tags[k], #TAG_NAME) == 0 ) { \ ID = k; \ break; \ } \ } \ if (ID == -1) { \ ID = silk_Timer_nTimers; \ silk_Timer_nTimers++; \ silk_Timer_depth[ID] = silk_Timer_depth_ctr; \ strcpy(silk_Timer_tags[ID], #TAG_NAME); \ silk_Timer_cnt[ID] = 0; \ silk_Timer_sum[ID] = 0; \ silk_Timer_min[ID] = 0xFFFFFFFF; \ silk_Timer_max[ID] = 0; \ } \ } \ silk_Timer_depth_ctr++; \ QueryPerformanceCounter(&silk_Timer_start[ID]); \ } #else #define TIC(TAG_NAME) { \ static int init = 0; \ static int ID = -1; \ if( init == 0 ) \ { \ int k; \ init = 1; \ for( k = 0; k < silk_Timer_nTimers; k++ ) { \ if( strcmp(silk_Timer_tags[k], #TAG_NAME) == 0 ) { \ ID = k; \ break; \ } \ } \ if (ID == -1) { \ ID = silk_Timer_nTimers; \ silk_Timer_nTimers++; \ silk_Timer_depth[ID] = silk_Timer_depth_ctr; \ strcpy(silk_Timer_tags[ID], #TAG_NAME); \ silk_Timer_cnt[ID] = 0; \ silk_Timer_sum[ID] = 0; \ silk_Timer_min[ID] = 0xFFFFFFFF; \ silk_Timer_max[ID] = 0; \ } \ } \ silk_Timer_depth_ctr++; \ silk_Timer_start[ID] = GetHighResolutionTime(); \ } #endif #ifdef _WIN32 #define TOC(TAG_NAME) { \ LARGE_INTEGER lpPerformanceCount; \ static int init = 0; \ static int ID = 0; \ if( init == 0 ) \ { \ int k; \ init = 1; \ for( k = 0; k < silk_Timer_nTimers; k++ ) { \ if( strcmp(silk_Timer_tags[k], #TAG_NAME) == 0 ) { \ ID = k; \ break; \ } \ } \ } \ QueryPerformanceCounter(&lpPerformanceCount); \ lpPerformanceCount.QuadPart -= silk_Timer_start[ID].QuadPart; \ if((lpPerformanceCount.QuadPart < 100000000) && \ (lpPerformanceCount.QuadPart >= 0)) { \ silk_Timer_cnt[ID]++; \ silk_Timer_sum[ID] += lpPerformanceCount.QuadPart; \ if( lpPerformanceCount.QuadPart > silk_Timer_max[ID] ) \ silk_Timer_max[ID] = lpPerformanceCount.QuadPart; \ if( lpPerformanceCount.QuadPart < silk_Timer_min[ID] ) \ silk_Timer_min[ID] = lpPerformanceCount.QuadPart; \ } \ silk_Timer_depth_ctr--; \ } #else #define TOC(TAG_NAME) { \ unsigned long endTime; \ static int init = 0; \ static int ID = 0; \ if( init == 0 ) \ { \ int k; \ init = 1; \ for( k = 0; k < silk_Timer_nTimers; k++ ) { \ if( strcmp(silk_Timer_tags[k], #TAG_NAME) == 0 ) { \ ID = k; \ break; \ } \ } \ } \ endTime = GetHighResolutionTime(); \ endTime -= silk_Timer_start[ID]; \ if((endTime < 100000000) && \ (endTime >= 0)) { \ silk_Timer_cnt[ID]++; \ silk_Timer_sum[ID] += endTime; \ if( endTime > silk_Timer_max[ID] ) \ silk_Timer_max[ID] = endTime; \ if( endTime < silk_Timer_min[ID] ) \ silk_Timer_min[ID] = endTime; \ } \ silk_Timer_depth_ctr--; \ } #endif #else /* SILK_TIC_TOC */ /* define macros as empty strings */ #define TIC(TAG_NAME) #define TOC(TAG_NAME) #define silk_TimerSave(FILE_NAME) #endif /* SILK_TIC_TOC */ #if SILK_DEBUG /************************************/ /* write data to file for debugging */ /************************************/ /* Example: DEBUG_STORE_DATA(testfile.pcm, &RIN[0], 160*sizeof(opus_int16)); */ #define silk_NUM_STORES_MAX 100 extern FILE *silk_debug_store_fp[ silk_NUM_STORES_MAX ]; extern int silk_debug_store_count; /* Faster way of storing the data */ #define DEBUG_STORE_DATA( FILE_NAME, DATA_PTR, N_BYTES ) { \ static opus_int init = 0, cnt = 0; \ static FILE **fp; \ if (init == 0) { \ init = 1; \ cnt = silk_debug_store_count++; \ silk_debug_store_fp[ cnt ] = fopen(#FILE_NAME, "wb"); \ } \ fwrite((DATA_PTR), (N_BYTES), 1, silk_debug_store_fp[ cnt ]); \ } /* Call this at the end of main() */ #define SILK_DEBUG_STORE_CLOSE_FILES { \ opus_int i; \ for( i = 0; i < silk_debug_store_count; i++ ) { \ fclose( silk_debug_store_fp[ i ] ); \ } \ } #else /* SILK_DEBUG */ /* define macros as empty strings */ #define DEBUG_STORE_DATA(FILE_NAME, DATA_PTR, N_BYTES) #define SILK_DEBUG_STORE_CLOSE_FILES #endif /* SILK_DEBUG */ #ifdef __cplusplus } #endif #endif /* SILK_DEBUG_H */ ================================================ FILE: deps/pjsip/third_party/opus/silk/dec_API.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "API.h" #include "main.h" #include "stack_alloc.h" #include "os_support.h" /************************/ /* Decoder Super Struct */ /************************/ typedef struct { silk_decoder_state channel_state[ DECODER_NUM_CHANNELS ]; stereo_dec_state sStereo; opus_int nChannelsAPI; opus_int nChannelsInternal; opus_int prev_decode_only_middle; } silk_decoder; /*********************/ /* Decoder functions */ /*********************/ opus_int silk_Get_Decoder_Size( /* O Returns error code */ opus_int *decSizeBytes /* O Number of bytes in SILK decoder state */ ) { opus_int ret = SILK_NO_ERROR; *decSizeBytes = sizeof( silk_decoder ); return ret; } /* Reset decoder state */ opus_int silk_InitDecoder( /* O Returns error code */ void *decState /* I/O State */ ) { opus_int n, ret = SILK_NO_ERROR; silk_decoder_state *channel_state = ((silk_decoder *)decState)->channel_state; for( n = 0; n < DECODER_NUM_CHANNELS; n++ ) { ret = silk_init_decoder( &channel_state[ n ] ); } silk_memset(&((silk_decoder *)decState)->sStereo, 0, sizeof(((silk_decoder *)decState)->sStereo)); /* Not strictly needed, but it's cleaner that way */ ((silk_decoder *)decState)->prev_decode_only_middle = 0; return ret; } /* Decode a frame */ opus_int silk_Decode( /* O Returns error code */ void* decState, /* I/O State */ silk_DecControlStruct* decControl, /* I/O Control Structure */ opus_int lostFlag, /* I 0: no loss, 1 loss, 2 decode fec */ opus_int newPacketFlag, /* I Indicates first decoder call for this packet */ ec_dec *psRangeDec, /* I/O Compressor data structure */ opus_int16 *samplesOut, /* O Decoded output speech vector */ opus_int32 *nSamplesOut, /* O Number of samples decoded */ int arch /* I Run-time architecture */ ) { opus_int i, n, decode_only_middle = 0, ret = SILK_NO_ERROR; opus_int32 nSamplesOutDec, LBRR_symbol; opus_int16 *samplesOut1_tmp[ 2 ]; VARDECL( opus_int16, samplesOut1_tmp_storage1 ); VARDECL( opus_int16, samplesOut1_tmp_storage2 ); VARDECL( opus_int16, samplesOut2_tmp ); opus_int32 MS_pred_Q13[ 2 ] = { 0 }; opus_int16 *resample_out_ptr; silk_decoder *psDec = ( silk_decoder * )decState; silk_decoder_state *channel_state = psDec->channel_state; opus_int has_side; opus_int stereo_to_mono; int delay_stack_alloc; SAVE_STACK; silk_assert( decControl->nChannelsInternal == 1 || decControl->nChannelsInternal == 2 ); /**********************************/ /* Test if first frame in payload */ /**********************************/ if( newPacketFlag ) { for( n = 0; n < decControl->nChannelsInternal; n++ ) { channel_state[ n ].nFramesDecoded = 0; /* Used to count frames in packet */ } } /* If Mono -> Stereo transition in bitstream: init state of second channel */ if( decControl->nChannelsInternal > psDec->nChannelsInternal ) { ret += silk_init_decoder( &channel_state[ 1 ] ); } stereo_to_mono = decControl->nChannelsInternal == 1 && psDec->nChannelsInternal == 2 && ( decControl->internalSampleRate == 1000*channel_state[ 0 ].fs_kHz ); if( channel_state[ 0 ].nFramesDecoded == 0 ) { for( n = 0; n < decControl->nChannelsInternal; n++ ) { opus_int fs_kHz_dec; if( decControl->payloadSize_ms == 0 ) { /* Assuming packet loss, use 10 ms */ channel_state[ n ].nFramesPerPacket = 1; channel_state[ n ].nb_subfr = 2; } else if( decControl->payloadSize_ms == 10 ) { channel_state[ n ].nFramesPerPacket = 1; channel_state[ n ].nb_subfr = 2; } else if( decControl->payloadSize_ms == 20 ) { channel_state[ n ].nFramesPerPacket = 1; channel_state[ n ].nb_subfr = 4; } else if( decControl->payloadSize_ms == 40 ) { channel_state[ n ].nFramesPerPacket = 2; channel_state[ n ].nb_subfr = 4; } else if( decControl->payloadSize_ms == 60 ) { channel_state[ n ].nFramesPerPacket = 3; channel_state[ n ].nb_subfr = 4; } else { silk_assert( 0 ); RESTORE_STACK; return SILK_DEC_INVALID_FRAME_SIZE; } fs_kHz_dec = ( decControl->internalSampleRate >> 10 ) + 1; if( fs_kHz_dec != 8 && fs_kHz_dec != 12 && fs_kHz_dec != 16 ) { silk_assert( 0 ); RESTORE_STACK; return SILK_DEC_INVALID_SAMPLING_FREQUENCY; } ret += silk_decoder_set_fs( &channel_state[ n ], fs_kHz_dec, decControl->API_sampleRate ); } } if( decControl->nChannelsAPI == 2 && decControl->nChannelsInternal == 2 && ( psDec->nChannelsAPI == 1 || psDec->nChannelsInternal == 1 ) ) { silk_memset( psDec->sStereo.pred_prev_Q13, 0, sizeof( psDec->sStereo.pred_prev_Q13 ) ); silk_memset( psDec->sStereo.sSide, 0, sizeof( psDec->sStereo.sSide ) ); silk_memcpy( &channel_state[ 1 ].resampler_state, &channel_state[ 0 ].resampler_state, sizeof( silk_resampler_state_struct ) ); } psDec->nChannelsAPI = decControl->nChannelsAPI; psDec->nChannelsInternal = decControl->nChannelsInternal; if( decControl->API_sampleRate > (opus_int32)MAX_API_FS_KHZ * 1000 || decControl->API_sampleRate < 8000 ) { ret = SILK_DEC_INVALID_SAMPLING_FREQUENCY; RESTORE_STACK; return( ret ); } if( lostFlag != FLAG_PACKET_LOST && channel_state[ 0 ].nFramesDecoded == 0 ) { /* First decoder call for this payload */ /* Decode VAD flags and LBRR flag */ for( n = 0; n < decControl->nChannelsInternal; n++ ) { for( i = 0; i < channel_state[ n ].nFramesPerPacket; i++ ) { channel_state[ n ].VAD_flags[ i ] = ec_dec_bit_logp(psRangeDec, 1); } channel_state[ n ].LBRR_flag = ec_dec_bit_logp(psRangeDec, 1); } /* Decode LBRR flags */ for( n = 0; n < decControl->nChannelsInternal; n++ ) { silk_memset( channel_state[ n ].LBRR_flags, 0, sizeof( channel_state[ n ].LBRR_flags ) ); if( channel_state[ n ].LBRR_flag ) { if( channel_state[ n ].nFramesPerPacket == 1 ) { channel_state[ n ].LBRR_flags[ 0 ] = 1; } else { LBRR_symbol = ec_dec_icdf( psRangeDec, silk_LBRR_flags_iCDF_ptr[ channel_state[ n ].nFramesPerPacket - 2 ], 8 ) + 1; for( i = 0; i < channel_state[ n ].nFramesPerPacket; i++ ) { channel_state[ n ].LBRR_flags[ i ] = silk_RSHIFT( LBRR_symbol, i ) & 1; } } } } if( lostFlag == FLAG_DECODE_NORMAL ) { /* Regular decoding: skip all LBRR data */ for( i = 0; i < channel_state[ 0 ].nFramesPerPacket; i++ ) { for( n = 0; n < decControl->nChannelsInternal; n++ ) { if( channel_state[ n ].LBRR_flags[ i ] ) { opus_int16 pulses[ MAX_FRAME_LENGTH ]; opus_int condCoding; if( decControl->nChannelsInternal == 2 && n == 0 ) { silk_stereo_decode_pred( psRangeDec, MS_pred_Q13 ); if( channel_state[ 1 ].LBRR_flags[ i ] == 0 ) { silk_stereo_decode_mid_only( psRangeDec, &decode_only_middle ); } } /* Use conditional coding if previous frame available */ if( i > 0 && channel_state[ n ].LBRR_flags[ i - 1 ] ) { condCoding = CODE_CONDITIONALLY; } else { condCoding = CODE_INDEPENDENTLY; } silk_decode_indices( &channel_state[ n ], psRangeDec, i, 1, condCoding ); silk_decode_pulses( psRangeDec, pulses, channel_state[ n ].indices.signalType, channel_state[ n ].indices.quantOffsetType, channel_state[ n ].frame_length ); } } } } } /* Get MS predictor index */ if( decControl->nChannelsInternal == 2 ) { if( lostFlag == FLAG_DECODE_NORMAL || ( lostFlag == FLAG_DECODE_LBRR && channel_state[ 0 ].LBRR_flags[ channel_state[ 0 ].nFramesDecoded ] == 1 ) ) { silk_stereo_decode_pred( psRangeDec, MS_pred_Q13 ); /* For LBRR data, decode mid-only flag only if side-channel's LBRR flag is false */ if( ( lostFlag == FLAG_DECODE_NORMAL && channel_state[ 1 ].VAD_flags[ channel_state[ 0 ].nFramesDecoded ] == 0 ) || ( lostFlag == FLAG_DECODE_LBRR && channel_state[ 1 ].LBRR_flags[ channel_state[ 0 ].nFramesDecoded ] == 0 ) ) { silk_stereo_decode_mid_only( psRangeDec, &decode_only_middle ); } else { decode_only_middle = 0; } } else { for( n = 0; n < 2; n++ ) { MS_pred_Q13[ n ] = psDec->sStereo.pred_prev_Q13[ n ]; } } } /* Reset side channel decoder prediction memory for first frame with side coding */ if( decControl->nChannelsInternal == 2 && decode_only_middle == 0 && psDec->prev_decode_only_middle == 1 ) { silk_memset( psDec->channel_state[ 1 ].outBuf, 0, sizeof(psDec->channel_state[ 1 ].outBuf) ); silk_memset( psDec->channel_state[ 1 ].sLPC_Q14_buf, 0, sizeof(psDec->channel_state[ 1 ].sLPC_Q14_buf) ); psDec->channel_state[ 1 ].lagPrev = 100; psDec->channel_state[ 1 ].LastGainIndex = 10; psDec->channel_state[ 1 ].prevSignalType = TYPE_NO_VOICE_ACTIVITY; psDec->channel_state[ 1 ].first_frame_after_reset = 1; } /* Check if the temp buffer fits into the output PCM buffer. If it fits, we can delay allocating the temp buffer until after the SILK peak stack usage. We need to use a < and not a <= because of the two extra samples. */ delay_stack_alloc = decControl->internalSampleRate*decControl->nChannelsInternal < decControl->API_sampleRate*decControl->nChannelsAPI; ALLOC( samplesOut1_tmp_storage1, delay_stack_alloc ? ALLOC_NONE : decControl->nChannelsInternal*(channel_state[ 0 ].frame_length + 2 ), opus_int16 ); if ( delay_stack_alloc ) { samplesOut1_tmp[ 0 ] = samplesOut; samplesOut1_tmp[ 1 ] = samplesOut + channel_state[ 0 ].frame_length + 2; } else { samplesOut1_tmp[ 0 ] = samplesOut1_tmp_storage1; samplesOut1_tmp[ 1 ] = samplesOut1_tmp_storage1 + channel_state[ 0 ].frame_length + 2; } if( lostFlag == FLAG_DECODE_NORMAL ) { has_side = !decode_only_middle; } else { has_side = !psDec->prev_decode_only_middle || (decControl->nChannelsInternal == 2 && lostFlag == FLAG_DECODE_LBRR && channel_state[1].LBRR_flags[ channel_state[1].nFramesDecoded ] == 1 ); } /* Call decoder for one frame */ for( n = 0; n < decControl->nChannelsInternal; n++ ) { if( n == 0 || has_side ) { opus_int FrameIndex; opus_int condCoding; FrameIndex = channel_state[ 0 ].nFramesDecoded - n; /* Use independent coding if no previous frame available */ if( FrameIndex <= 0 ) { condCoding = CODE_INDEPENDENTLY; } else if( lostFlag == FLAG_DECODE_LBRR ) { condCoding = channel_state[ n ].LBRR_flags[ FrameIndex - 1 ] ? CODE_CONDITIONALLY : CODE_INDEPENDENTLY; } else if( n > 0 && psDec->prev_decode_only_middle ) { /* If we skipped a side frame in this packet, we don't need LTP scaling; the LTP state is well-defined. */ condCoding = CODE_INDEPENDENTLY_NO_LTP_SCALING; } else { condCoding = CODE_CONDITIONALLY; } ret += silk_decode_frame( &channel_state[ n ], psRangeDec, &samplesOut1_tmp[ n ][ 2 ], &nSamplesOutDec, lostFlag, condCoding, arch); } else { silk_memset( &samplesOut1_tmp[ n ][ 2 ], 0, nSamplesOutDec * sizeof( opus_int16 ) ); } channel_state[ n ].nFramesDecoded++; } if( decControl->nChannelsAPI == 2 && decControl->nChannelsInternal == 2 ) { /* Convert Mid/Side to Left/Right */ silk_stereo_MS_to_LR( &psDec->sStereo, samplesOut1_tmp[ 0 ], samplesOut1_tmp[ 1 ], MS_pred_Q13, channel_state[ 0 ].fs_kHz, nSamplesOutDec ); } else { /* Buffering */ silk_memcpy( samplesOut1_tmp[ 0 ], psDec->sStereo.sMid, 2 * sizeof( opus_int16 ) ); silk_memcpy( psDec->sStereo.sMid, &samplesOut1_tmp[ 0 ][ nSamplesOutDec ], 2 * sizeof( opus_int16 ) ); } /* Number of output samples */ *nSamplesOut = silk_DIV32( nSamplesOutDec * decControl->API_sampleRate, silk_SMULBB( channel_state[ 0 ].fs_kHz, 1000 ) ); /* Set up pointers to temp buffers */ ALLOC( samplesOut2_tmp, decControl->nChannelsAPI == 2 ? *nSamplesOut : ALLOC_NONE, opus_int16 ); if( decControl->nChannelsAPI == 2 ) { resample_out_ptr = samplesOut2_tmp; } else { resample_out_ptr = samplesOut; } ALLOC( samplesOut1_tmp_storage2, delay_stack_alloc ? decControl->nChannelsInternal*(channel_state[ 0 ].frame_length + 2 ) : ALLOC_NONE, opus_int16 ); if ( delay_stack_alloc ) { OPUS_COPY(samplesOut1_tmp_storage2, samplesOut, decControl->nChannelsInternal*(channel_state[ 0 ].frame_length + 2)); samplesOut1_tmp[ 0 ] = samplesOut1_tmp_storage2; samplesOut1_tmp[ 1 ] = samplesOut1_tmp_storage2 + channel_state[ 0 ].frame_length + 2; } for( n = 0; n < silk_min( decControl->nChannelsAPI, decControl->nChannelsInternal ); n++ ) { /* Resample decoded signal to API_sampleRate */ ret += silk_resampler( &channel_state[ n ].resampler_state, resample_out_ptr, &samplesOut1_tmp[ n ][ 1 ], nSamplesOutDec ); /* Interleave if stereo output and stereo stream */ if( decControl->nChannelsAPI == 2 ) { for( i = 0; i < *nSamplesOut; i++ ) { samplesOut[ n + 2 * i ] = resample_out_ptr[ i ]; } } } /* Create two channel output from mono stream */ if( decControl->nChannelsAPI == 2 && decControl->nChannelsInternal == 1 ) { if ( stereo_to_mono ){ /* Resample right channel for newly collapsed stereo just in case we weren't doing collapsing when switching to mono */ ret += silk_resampler( &channel_state[ 1 ].resampler_state, resample_out_ptr, &samplesOut1_tmp[ 0 ][ 1 ], nSamplesOutDec ); for( i = 0; i < *nSamplesOut; i++ ) { samplesOut[ 1 + 2 * i ] = resample_out_ptr[ i ]; } } else { for( i = 0; i < *nSamplesOut; i++ ) { samplesOut[ 1 + 2 * i ] = samplesOut[ 0 + 2 * i ]; } } } /* Export pitch lag, measured at 48 kHz sampling rate */ if( channel_state[ 0 ].prevSignalType == TYPE_VOICED ) { int mult_tab[ 3 ] = { 6, 4, 3 }; decControl->prevPitchLag = channel_state[ 0 ].lagPrev * mult_tab[ ( channel_state[ 0 ].fs_kHz - 8 ) >> 2 ]; } else { decControl->prevPitchLag = 0; } if( lostFlag == FLAG_PACKET_LOST ) { /* On packet loss, remove the gain clamping to prevent having the energy "bounce back" if we lose packets when the energy is going down */ for ( i = 0; i < psDec->nChannelsInternal; i++ ) psDec->channel_state[ i ].LastGainIndex = 10; } else { psDec->prev_decode_only_middle = decode_only_middle; } RESTORE_STACK; return ret; } #if 0 /* Getting table of contents for a packet */ opus_int silk_get_TOC( const opus_uint8 *payload, /* I Payload data */ const opus_int nBytesIn, /* I Number of input bytes */ const opus_int nFramesPerPayload, /* I Number of SILK frames per payload */ silk_TOC_struct *Silk_TOC /* O Type of content */ ) { opus_int i, flags, ret = SILK_NO_ERROR; if( nBytesIn < 1 ) { return -1; } if( nFramesPerPayload < 0 || nFramesPerPayload > 3 ) { return -1; } silk_memset( Silk_TOC, 0, sizeof( *Silk_TOC ) ); /* For stereo, extract the flags for the mid channel */ flags = silk_RSHIFT( payload[ 0 ], 7 - nFramesPerPayload ) & ( silk_LSHIFT( 1, nFramesPerPayload + 1 ) - 1 ); Silk_TOC->inbandFECFlag = flags & 1; for( i = nFramesPerPayload - 1; i >= 0 ; i-- ) { flags = silk_RSHIFT( flags, 1 ); Silk_TOC->VADFlags[ i ] = flags & 1; Silk_TOC->VADFlag |= flags & 1; } return ret; } #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/decode_core.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #include "stack_alloc.h" /**********************************************************/ /* Core decoder. Performs inverse NSQ operation LTP + LPC */ /**********************************************************/ void silk_decode_core( silk_decoder_state *psDec, /* I/O Decoder state */ silk_decoder_control *psDecCtrl, /* I Decoder control */ opus_int16 xq[], /* O Decoded speech */ const opus_int16 pulses[ MAX_FRAME_LENGTH ], /* I Pulse signal */ int arch /* I Run-time architecture */ ) { opus_int i, k, lag = 0, start_idx, sLTP_buf_idx, NLSF_interpolation_flag, signalType; opus_int16 *A_Q12, *B_Q14, *pxq, A_Q12_tmp[ MAX_LPC_ORDER ]; VARDECL( opus_int16, sLTP ); VARDECL( opus_int32, sLTP_Q15 ); opus_int32 LTP_pred_Q13, LPC_pred_Q10, Gain_Q10, inv_gain_Q31, gain_adj_Q16, rand_seed, offset_Q10; opus_int32 *pred_lag_ptr, *pexc_Q14, *pres_Q14; VARDECL( opus_int32, res_Q14 ); VARDECL( opus_int32, sLPC_Q14 ); SAVE_STACK; silk_assert( psDec->prev_gain_Q16 != 0 ); ALLOC( sLTP, psDec->ltp_mem_length, opus_int16 ); ALLOC( sLTP_Q15, psDec->ltp_mem_length + psDec->frame_length, opus_int32 ); ALLOC( res_Q14, psDec->subfr_length, opus_int32 ); ALLOC( sLPC_Q14, psDec->subfr_length + MAX_LPC_ORDER, opus_int32 ); offset_Q10 = silk_Quantization_Offsets_Q10[ psDec->indices.signalType >> 1 ][ psDec->indices.quantOffsetType ]; if( psDec->indices.NLSFInterpCoef_Q2 < 1 << 2 ) { NLSF_interpolation_flag = 1; } else { NLSF_interpolation_flag = 0; } /* Decode excitation */ rand_seed = psDec->indices.Seed; for( i = 0; i < psDec->frame_length; i++ ) { rand_seed = silk_RAND( rand_seed ); psDec->exc_Q14[ i ] = silk_LSHIFT( (opus_int32)pulses[ i ], 14 ); if( psDec->exc_Q14[ i ] > 0 ) { psDec->exc_Q14[ i ] -= QUANT_LEVEL_ADJUST_Q10 << 4; } else if( psDec->exc_Q14[ i ] < 0 ) { psDec->exc_Q14[ i ] += QUANT_LEVEL_ADJUST_Q10 << 4; } psDec->exc_Q14[ i ] += offset_Q10 << 4; if( rand_seed < 0 ) { psDec->exc_Q14[ i ] = -psDec->exc_Q14[ i ]; } rand_seed = silk_ADD32_ovflw( rand_seed, pulses[ i ] ); } /* Copy LPC state */ silk_memcpy( sLPC_Q14, psDec->sLPC_Q14_buf, MAX_LPC_ORDER * sizeof( opus_int32 ) ); pexc_Q14 = psDec->exc_Q14; pxq = xq; sLTP_buf_idx = psDec->ltp_mem_length; /* Loop over subframes */ for( k = 0; k < psDec->nb_subfr; k++ ) { pres_Q14 = res_Q14; A_Q12 = psDecCtrl->PredCoef_Q12[ k >> 1 ]; /* Preload LPC coeficients to array on stack. Gives small performance gain */ silk_memcpy( A_Q12_tmp, A_Q12, psDec->LPC_order * sizeof( opus_int16 ) ); B_Q14 = &psDecCtrl->LTPCoef_Q14[ k * LTP_ORDER ]; signalType = psDec->indices.signalType; Gain_Q10 = silk_RSHIFT( psDecCtrl->Gains_Q16[ k ], 6 ); inv_gain_Q31 = silk_INVERSE32_varQ( psDecCtrl->Gains_Q16[ k ], 47 ); /* Calculate gain adjustment factor */ if( psDecCtrl->Gains_Q16[ k ] != psDec->prev_gain_Q16 ) { gain_adj_Q16 = silk_DIV32_varQ( psDec->prev_gain_Q16, psDecCtrl->Gains_Q16[ k ], 16 ); /* Scale short term state */ for( i = 0; i < MAX_LPC_ORDER; i++ ) { sLPC_Q14[ i ] = silk_SMULWW( gain_adj_Q16, sLPC_Q14[ i ] ); } } else { gain_adj_Q16 = (opus_int32)1 << 16; } /* Save inv_gain */ silk_assert( inv_gain_Q31 != 0 ); psDec->prev_gain_Q16 = psDecCtrl->Gains_Q16[ k ]; /* Avoid abrupt transition from voiced PLC to unvoiced normal decoding */ if( psDec->lossCnt && psDec->prevSignalType == TYPE_VOICED && psDec->indices.signalType != TYPE_VOICED && k < MAX_NB_SUBFR/2 ) { silk_memset( B_Q14, 0, LTP_ORDER * sizeof( opus_int16 ) ); B_Q14[ LTP_ORDER/2 ] = SILK_FIX_CONST( 0.25, 14 ); signalType = TYPE_VOICED; psDecCtrl->pitchL[ k ] = psDec->lagPrev; } if( signalType == TYPE_VOICED ) { /* Voiced */ lag = psDecCtrl->pitchL[ k ]; /* Re-whitening */ if( k == 0 || ( k == 2 && NLSF_interpolation_flag ) ) { /* Rewhiten with new A coefs */ start_idx = psDec->ltp_mem_length - lag - psDec->LPC_order - LTP_ORDER / 2; silk_assert( start_idx > 0 ); if( k == 2 ) { silk_memcpy( &psDec->outBuf[ psDec->ltp_mem_length ], xq, 2 * psDec->subfr_length * sizeof( opus_int16 ) ); } silk_LPC_analysis_filter( &sLTP[ start_idx ], &psDec->outBuf[ start_idx + k * psDec->subfr_length ], A_Q12, psDec->ltp_mem_length - start_idx, psDec->LPC_order, arch ); /* After rewhitening the LTP state is unscaled */ if( k == 0 ) { /* Do LTP downscaling to reduce inter-packet dependency */ inv_gain_Q31 = silk_LSHIFT( silk_SMULWB( inv_gain_Q31, psDecCtrl->LTP_scale_Q14 ), 2 ); } for( i = 0; i < lag + LTP_ORDER/2; i++ ) { sLTP_Q15[ sLTP_buf_idx - i - 1 ] = silk_SMULWB( inv_gain_Q31, sLTP[ psDec->ltp_mem_length - i - 1 ] ); } } else { /* Update LTP state when Gain changes */ if( gain_adj_Q16 != (opus_int32)1 << 16 ) { for( i = 0; i < lag + LTP_ORDER/2; i++ ) { sLTP_Q15[ sLTP_buf_idx - i - 1 ] = silk_SMULWW( gain_adj_Q16, sLTP_Q15[ sLTP_buf_idx - i - 1 ] ); } } } } /* Long-term prediction */ if( signalType == TYPE_VOICED ) { /* Set up pointer */ pred_lag_ptr = &sLTP_Q15[ sLTP_buf_idx - lag + LTP_ORDER / 2 ]; for( i = 0; i < psDec->subfr_length; i++ ) { /* Unrolled loop */ /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ LTP_pred_Q13 = 2; LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[ 0 ], B_Q14[ 0 ] ); LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[ -1 ], B_Q14[ 1 ] ); LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[ -2 ], B_Q14[ 2 ] ); LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[ -3 ], B_Q14[ 3 ] ); LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[ -4 ], B_Q14[ 4 ] ); pred_lag_ptr++; /* Generate LPC excitation */ pres_Q14[ i ] = silk_ADD_LSHIFT32( pexc_Q14[ i ], LTP_pred_Q13, 1 ); /* Update states */ sLTP_Q15[ sLTP_buf_idx ] = silk_LSHIFT( pres_Q14[ i ], 1 ); sLTP_buf_idx++; } } else { pres_Q14 = pexc_Q14; } for( i = 0; i < psDec->subfr_length; i++ ) { /* Short-term prediction */ silk_assert( psDec->LPC_order == 10 || psDec->LPC_order == 16 ); /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ LPC_pred_Q10 = silk_RSHIFT( psDec->LPC_order, 1 ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 1 ], A_Q12_tmp[ 0 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 2 ], A_Q12_tmp[ 1 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 3 ], A_Q12_tmp[ 2 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 4 ], A_Q12_tmp[ 3 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 5 ], A_Q12_tmp[ 4 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 6 ], A_Q12_tmp[ 5 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 7 ], A_Q12_tmp[ 6 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 8 ], A_Q12_tmp[ 7 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 9 ], A_Q12_tmp[ 8 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 10 ], A_Q12_tmp[ 9 ] ); if( psDec->LPC_order == 16 ) { LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 11 ], A_Q12_tmp[ 10 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 12 ], A_Q12_tmp[ 11 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 13 ], A_Q12_tmp[ 12 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 14 ], A_Q12_tmp[ 13 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 15 ], A_Q12_tmp[ 14 ] ); LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14[ MAX_LPC_ORDER + i - 16 ], A_Q12_tmp[ 15 ] ); } /* Add prediction to LPC excitation */ sLPC_Q14[ MAX_LPC_ORDER + i ] = silk_ADD_LSHIFT32( pres_Q14[ i ], LPC_pred_Q10, 4 ); /* Scale with gain */ pxq[ i ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_SMULWW( sLPC_Q14[ MAX_LPC_ORDER + i ], Gain_Q10 ), 8 ) ); } /* DEBUG_STORE_DATA( dec.pcm, pxq, psDec->subfr_length * sizeof( opus_int16 ) ) */ /* Update LPC filter state */ silk_memcpy( sLPC_Q14, &sLPC_Q14[ psDec->subfr_length ], MAX_LPC_ORDER * sizeof( opus_int32 ) ); pexc_Q14 += psDec->subfr_length; pxq += psDec->subfr_length; } /* Save LPC state */ silk_memcpy( psDec->sLPC_Q14_buf, sLPC_Q14, MAX_LPC_ORDER * sizeof( opus_int32 ) ); RESTORE_STACK; } ================================================ FILE: deps/pjsip/third_party/opus/silk/decode_frame.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #include "stack_alloc.h" #include "PLC.h" /****************/ /* Decode frame */ /****************/ opus_int silk_decode_frame( silk_decoder_state *psDec, /* I/O Pointer to Silk decoder state */ ec_dec *psRangeDec, /* I/O Compressor data structure */ opus_int16 pOut[], /* O Pointer to output speech frame */ opus_int32 *pN, /* O Pointer to size of output frame */ opus_int lostFlag, /* I 0: no loss, 1 loss, 2 decode fec */ opus_int condCoding, /* I The type of conditional coding to use */ int arch /* I Run-time architecture */ ) { VARDECL( silk_decoder_control, psDecCtrl ); opus_int L, mv_len, ret = 0; SAVE_STACK; L = psDec->frame_length; ALLOC( psDecCtrl, 1, silk_decoder_control ); psDecCtrl->LTP_scale_Q14 = 0; /* Safety checks */ silk_assert( L > 0 && L <= MAX_FRAME_LENGTH ); if( lostFlag == FLAG_DECODE_NORMAL || ( lostFlag == FLAG_DECODE_LBRR && psDec->LBRR_flags[ psDec->nFramesDecoded ] == 1 ) ) { VARDECL( opus_int16, pulses ); ALLOC( pulses, (L + SHELL_CODEC_FRAME_LENGTH - 1) & ~(SHELL_CODEC_FRAME_LENGTH - 1), opus_int16 ); /*********************************************/ /* Decode quantization indices of side info */ /*********************************************/ silk_decode_indices( psDec, psRangeDec, psDec->nFramesDecoded, lostFlag, condCoding ); /*********************************************/ /* Decode quantization indices of excitation */ /*********************************************/ silk_decode_pulses( psRangeDec, pulses, psDec->indices.signalType, psDec->indices.quantOffsetType, psDec->frame_length ); /********************************************/ /* Decode parameters and pulse signal */ /********************************************/ silk_decode_parameters( psDec, psDecCtrl, condCoding ); /********************************************************/ /* Run inverse NSQ */ /********************************************************/ silk_decode_core( psDec, psDecCtrl, pOut, pulses, arch ); /********************************************************/ /* Update PLC state */ /********************************************************/ silk_PLC( psDec, psDecCtrl, pOut, 0, arch ); psDec->lossCnt = 0; psDec->prevSignalType = psDec->indices.signalType; silk_assert( psDec->prevSignalType >= 0 && psDec->prevSignalType <= 2 ); /* A frame has been decoded without errors */ psDec->first_frame_after_reset = 0; } else { /* Handle packet loss by extrapolation */ silk_PLC( psDec, psDecCtrl, pOut, 1, arch ); } /*************************/ /* Update output buffer. */ /*************************/ silk_assert( psDec->ltp_mem_length >= psDec->frame_length ); mv_len = psDec->ltp_mem_length - psDec->frame_length; silk_memmove( psDec->outBuf, &psDec->outBuf[ psDec->frame_length ], mv_len * sizeof(opus_int16) ); silk_memcpy( &psDec->outBuf[ mv_len ], pOut, psDec->frame_length * sizeof( opus_int16 ) ); /************************************************/ /* Comfort noise generation / estimation */ /************************************************/ silk_CNG( psDec, psDecCtrl, pOut, L ); /****************************************************************/ /* Ensure smooth connection of extrapolated and good frames */ /****************************************************************/ silk_PLC_glue_frames( psDec, pOut, L ); /* Update some decoder state variables */ psDec->lagPrev = psDecCtrl->pitchL[ psDec->nb_subfr - 1 ]; /* Set output frame length */ *pN = L; RESTORE_STACK; return ret; } ================================================ FILE: deps/pjsip/third_party/opus/silk/decode_indices.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Decode side-information parameters from payload */ void silk_decode_indices( silk_decoder_state *psDec, /* I/O State */ ec_dec *psRangeDec, /* I/O Compressor data structure */ opus_int FrameIndex, /* I Frame number */ opus_int decode_LBRR, /* I Flag indicating LBRR data is being decoded */ opus_int condCoding /* I The type of conditional coding to use */ ) { opus_int i, k, Ix; opus_int decode_absolute_lagIndex, delta_lagIndex; opus_int16 ec_ix[ MAX_LPC_ORDER ]; opus_uint8 pred_Q8[ MAX_LPC_ORDER ]; /*******************************************/ /* Decode signal type and quantizer offset */ /*******************************************/ if( decode_LBRR || psDec->VAD_flags[ FrameIndex ] ) { Ix = ec_dec_icdf( psRangeDec, silk_type_offset_VAD_iCDF, 8 ) + 2; } else { Ix = ec_dec_icdf( psRangeDec, silk_type_offset_no_VAD_iCDF, 8 ); } psDec->indices.signalType = (opus_int8)silk_RSHIFT( Ix, 1 ); psDec->indices.quantOffsetType = (opus_int8)( Ix & 1 ); /****************/ /* Decode gains */ /****************/ /* First subframe */ if( condCoding == CODE_CONDITIONALLY ) { /* Conditional coding */ psDec->indices.GainsIndices[ 0 ] = (opus_int8)ec_dec_icdf( psRangeDec, silk_delta_gain_iCDF, 8 ); } else { /* Independent coding, in two stages: MSB bits followed by 3 LSBs */ psDec->indices.GainsIndices[ 0 ] = (opus_int8)silk_LSHIFT( ec_dec_icdf( psRangeDec, silk_gain_iCDF[ psDec->indices.signalType ], 8 ), 3 ); psDec->indices.GainsIndices[ 0 ] += (opus_int8)ec_dec_icdf( psRangeDec, silk_uniform8_iCDF, 8 ); } /* Remaining subframes */ for( i = 1; i < psDec->nb_subfr; i++ ) { psDec->indices.GainsIndices[ i ] = (opus_int8)ec_dec_icdf( psRangeDec, silk_delta_gain_iCDF, 8 ); } /**********************/ /* Decode LSF Indices */ /**********************/ psDec->indices.NLSFIndices[ 0 ] = (opus_int8)ec_dec_icdf( psRangeDec, &psDec->psNLSF_CB->CB1_iCDF[ ( psDec->indices.signalType >> 1 ) * psDec->psNLSF_CB->nVectors ], 8 ); silk_NLSF_unpack( ec_ix, pred_Q8, psDec->psNLSF_CB, psDec->indices.NLSFIndices[ 0 ] ); silk_assert( psDec->psNLSF_CB->order == psDec->LPC_order ); for( i = 0; i < psDec->psNLSF_CB->order; i++ ) { Ix = ec_dec_icdf( psRangeDec, &psDec->psNLSF_CB->ec_iCDF[ ec_ix[ i ] ], 8 ); if( Ix == 0 ) { Ix -= ec_dec_icdf( psRangeDec, silk_NLSF_EXT_iCDF, 8 ); } else if( Ix == 2 * NLSF_QUANT_MAX_AMPLITUDE ) { Ix += ec_dec_icdf( psRangeDec, silk_NLSF_EXT_iCDF, 8 ); } psDec->indices.NLSFIndices[ i+1 ] = (opus_int8)( Ix - NLSF_QUANT_MAX_AMPLITUDE ); } /* Decode LSF interpolation factor */ if( psDec->nb_subfr == MAX_NB_SUBFR ) { psDec->indices.NLSFInterpCoef_Q2 = (opus_int8)ec_dec_icdf( psRangeDec, silk_NLSF_interpolation_factor_iCDF, 8 ); } else { psDec->indices.NLSFInterpCoef_Q2 = 4; } if( psDec->indices.signalType == TYPE_VOICED ) { /*********************/ /* Decode pitch lags */ /*********************/ /* Get lag index */ decode_absolute_lagIndex = 1; if( condCoding == CODE_CONDITIONALLY && psDec->ec_prevSignalType == TYPE_VOICED ) { /* Decode Delta index */ delta_lagIndex = (opus_int16)ec_dec_icdf( psRangeDec, silk_pitch_delta_iCDF, 8 ); if( delta_lagIndex > 0 ) { delta_lagIndex = delta_lagIndex - 9; psDec->indices.lagIndex = (opus_int16)( psDec->ec_prevLagIndex + delta_lagIndex ); decode_absolute_lagIndex = 0; } } if( decode_absolute_lagIndex ) { /* Absolute decoding */ psDec->indices.lagIndex = (opus_int16)ec_dec_icdf( psRangeDec, silk_pitch_lag_iCDF, 8 ) * silk_RSHIFT( psDec->fs_kHz, 1 ); psDec->indices.lagIndex += (opus_int16)ec_dec_icdf( psRangeDec, psDec->pitch_lag_low_bits_iCDF, 8 ); } psDec->ec_prevLagIndex = psDec->indices.lagIndex; /* Get countour index */ psDec->indices.contourIndex = (opus_int8)ec_dec_icdf( psRangeDec, psDec->pitch_contour_iCDF, 8 ); /********************/ /* Decode LTP gains */ /********************/ /* Decode PERIndex value */ psDec->indices.PERIndex = (opus_int8)ec_dec_icdf( psRangeDec, silk_LTP_per_index_iCDF, 8 ); for( k = 0; k < psDec->nb_subfr; k++ ) { psDec->indices.LTPIndex[ k ] = (opus_int8)ec_dec_icdf( psRangeDec, silk_LTP_gain_iCDF_ptrs[ psDec->indices.PERIndex ], 8 ); } /**********************/ /* Decode LTP scaling */ /**********************/ if( condCoding == CODE_INDEPENDENTLY ) { psDec->indices.LTP_scaleIndex = (opus_int8)ec_dec_icdf( psRangeDec, silk_LTPscale_iCDF, 8 ); } else { psDec->indices.LTP_scaleIndex = 0; } } psDec->ec_prevSignalType = psDec->indices.signalType; /***************/ /* Decode seed */ /***************/ psDec->indices.Seed = (opus_int8)ec_dec_icdf( psRangeDec, silk_uniform4_iCDF, 8 ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/decode_parameters.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Decode parameters from payload */ void silk_decode_parameters( silk_decoder_state *psDec, /* I/O State */ silk_decoder_control *psDecCtrl, /* I/O Decoder control */ opus_int condCoding /* I The type of conditional coding to use */ ) { opus_int i, k, Ix; opus_int16 pNLSF_Q15[ MAX_LPC_ORDER ], pNLSF0_Q15[ MAX_LPC_ORDER ]; const opus_int8 *cbk_ptr_Q7; /* Dequant Gains */ silk_gains_dequant( psDecCtrl->Gains_Q16, psDec->indices.GainsIndices, &psDec->LastGainIndex, condCoding == CODE_CONDITIONALLY, psDec->nb_subfr ); /****************/ /* Decode NLSFs */ /****************/ silk_NLSF_decode( pNLSF_Q15, psDec->indices.NLSFIndices, psDec->psNLSF_CB ); /* Convert NLSF parameters to AR prediction filter coefficients */ silk_NLSF2A( psDecCtrl->PredCoef_Q12[ 1 ], pNLSF_Q15, psDec->LPC_order ); /* If just reset, e.g., because internal Fs changed, do not allow interpolation */ /* improves the case of packet loss in the first frame after a switch */ if( psDec->first_frame_after_reset == 1 ) { psDec->indices.NLSFInterpCoef_Q2 = 4; } if( psDec->indices.NLSFInterpCoef_Q2 < 4 ) { /* Calculation of the interpolated NLSF0 vector from the interpolation factor, */ /* the previous NLSF1, and the current NLSF1 */ for( i = 0; i < psDec->LPC_order; i++ ) { pNLSF0_Q15[ i ] = psDec->prevNLSF_Q15[ i ] + silk_RSHIFT( silk_MUL( psDec->indices.NLSFInterpCoef_Q2, pNLSF_Q15[ i ] - psDec->prevNLSF_Q15[ i ] ), 2 ); } /* Convert NLSF parameters to AR prediction filter coefficients */ silk_NLSF2A( psDecCtrl->PredCoef_Q12[ 0 ], pNLSF0_Q15, psDec->LPC_order ); } else { /* Copy LPC coefficients for first half from second half */ silk_memcpy( psDecCtrl->PredCoef_Q12[ 0 ], psDecCtrl->PredCoef_Q12[ 1 ], psDec->LPC_order * sizeof( opus_int16 ) ); } silk_memcpy( psDec->prevNLSF_Q15, pNLSF_Q15, psDec->LPC_order * sizeof( opus_int16 ) ); /* After a packet loss do BWE of LPC coefs */ if( psDec->lossCnt ) { silk_bwexpander( psDecCtrl->PredCoef_Q12[ 0 ], psDec->LPC_order, BWE_AFTER_LOSS_Q16 ); silk_bwexpander( psDecCtrl->PredCoef_Q12[ 1 ], psDec->LPC_order, BWE_AFTER_LOSS_Q16 ); } if( psDec->indices.signalType == TYPE_VOICED ) { /*********************/ /* Decode pitch lags */ /*********************/ /* Decode pitch values */ silk_decode_pitch( psDec->indices.lagIndex, psDec->indices.contourIndex, psDecCtrl->pitchL, psDec->fs_kHz, psDec->nb_subfr ); /* Decode Codebook Index */ cbk_ptr_Q7 = silk_LTP_vq_ptrs_Q7[ psDec->indices.PERIndex ]; /* set pointer to start of codebook */ for( k = 0; k < psDec->nb_subfr; k++ ) { Ix = psDec->indices.LTPIndex[ k ]; for( i = 0; i < LTP_ORDER; i++ ) { psDecCtrl->LTPCoef_Q14[ k * LTP_ORDER + i ] = silk_LSHIFT( cbk_ptr_Q7[ Ix * LTP_ORDER + i ], 7 ); } } /**********************/ /* Decode LTP scaling */ /**********************/ Ix = psDec->indices.LTP_scaleIndex; psDecCtrl->LTP_scale_Q14 = silk_LTPScales_table_Q14[ Ix ]; } else { silk_memset( psDecCtrl->pitchL, 0, psDec->nb_subfr * sizeof( opus_int ) ); silk_memset( psDecCtrl->LTPCoef_Q14, 0, LTP_ORDER * psDec->nb_subfr * sizeof( opus_int16 ) ); psDec->indices.PERIndex = 0; psDecCtrl->LTP_scale_Q14 = 0; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/decode_pitch.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*********************************************************** * Pitch analyser function ********************************************************** */ #include "SigProc_FIX.h" #include "pitch_est_defines.h" void silk_decode_pitch( opus_int16 lagIndex, /* I */ opus_int8 contourIndex, /* O */ opus_int pitch_lags[], /* O 4 pitch values */ const opus_int Fs_kHz, /* I sampling frequency (kHz) */ const opus_int nb_subfr /* I number of sub frames */ ) { opus_int lag, k, min_lag, max_lag, cbk_size; const opus_int8 *Lag_CB_ptr; if( Fs_kHz == 8 ) { if( nb_subfr == PE_MAX_NB_SUBFR ) { Lag_CB_ptr = &silk_CB_lags_stage2[ 0 ][ 0 ]; cbk_size = PE_NB_CBKS_STAGE2_EXT; } else { silk_assert( nb_subfr == PE_MAX_NB_SUBFR >> 1 ); Lag_CB_ptr = &silk_CB_lags_stage2_10_ms[ 0 ][ 0 ]; cbk_size = PE_NB_CBKS_STAGE2_10MS; } } else { if( nb_subfr == PE_MAX_NB_SUBFR ) { Lag_CB_ptr = &silk_CB_lags_stage3[ 0 ][ 0 ]; cbk_size = PE_NB_CBKS_STAGE3_MAX; } else { silk_assert( nb_subfr == PE_MAX_NB_SUBFR >> 1 ); Lag_CB_ptr = &silk_CB_lags_stage3_10_ms[ 0 ][ 0 ]; cbk_size = PE_NB_CBKS_STAGE3_10MS; } } min_lag = silk_SMULBB( PE_MIN_LAG_MS, Fs_kHz ); max_lag = silk_SMULBB( PE_MAX_LAG_MS, Fs_kHz ); lag = min_lag + lagIndex; for( k = 0; k < nb_subfr; k++ ) { pitch_lags[ k ] = lag + matrix_ptr( Lag_CB_ptr, k, contourIndex, cbk_size ); pitch_lags[ k ] = silk_LIMIT( pitch_lags[ k ], min_lag, max_lag ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/decode_pulses.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /*********************************************/ /* Decode quantization indices of excitation */ /*********************************************/ void silk_decode_pulses( ec_dec *psRangeDec, /* I/O Compressor data structure */ opus_int16 pulses[], /* O Excitation signal */ const opus_int signalType, /* I Sigtype */ const opus_int quantOffsetType, /* I quantOffsetType */ const opus_int frame_length /* I Frame length */ ) { opus_int i, j, k, iter, abs_q, nLS, RateLevelIndex; opus_int sum_pulses[ MAX_NB_SHELL_BLOCKS ], nLshifts[ MAX_NB_SHELL_BLOCKS ]; opus_int16 *pulses_ptr; const opus_uint8 *cdf_ptr; /*********************/ /* Decode rate level */ /*********************/ RateLevelIndex = ec_dec_icdf( psRangeDec, silk_rate_levels_iCDF[ signalType >> 1 ], 8 ); /* Calculate number of shell blocks */ silk_assert( 1 << LOG2_SHELL_CODEC_FRAME_LENGTH == SHELL_CODEC_FRAME_LENGTH ); iter = silk_RSHIFT( frame_length, LOG2_SHELL_CODEC_FRAME_LENGTH ); if( iter * SHELL_CODEC_FRAME_LENGTH < frame_length ) { silk_assert( frame_length == 12 * 10 ); /* Make sure only happens for 10 ms @ 12 kHz */ iter++; } /***************************************************/ /* Sum-Weighted-Pulses Decoding */ /***************************************************/ cdf_ptr = silk_pulses_per_block_iCDF[ RateLevelIndex ]; for( i = 0; i < iter; i++ ) { nLshifts[ i ] = 0; sum_pulses[ i ] = ec_dec_icdf( psRangeDec, cdf_ptr, 8 ); /* LSB indication */ while( sum_pulses[ i ] == SILK_MAX_PULSES + 1 ) { nLshifts[ i ]++; /* When we've already got 10 LSBs, we shift the table to not allow (SILK_MAX_PULSES + 1) */ sum_pulses[ i ] = ec_dec_icdf( psRangeDec, silk_pulses_per_block_iCDF[ N_RATE_LEVELS - 1] + ( nLshifts[ i ] == 10 ), 8 ); } } /***************************************************/ /* Shell decoding */ /***************************************************/ for( i = 0; i < iter; i++ ) { if( sum_pulses[ i ] > 0 ) { silk_shell_decoder( &pulses[ silk_SMULBB( i, SHELL_CODEC_FRAME_LENGTH ) ], psRangeDec, sum_pulses[ i ] ); } else { silk_memset( &pulses[ silk_SMULBB( i, SHELL_CODEC_FRAME_LENGTH ) ], 0, SHELL_CODEC_FRAME_LENGTH * sizeof( pulses[0] ) ); } } /***************************************************/ /* LSB Decoding */ /***************************************************/ for( i = 0; i < iter; i++ ) { if( nLshifts[ i ] > 0 ) { nLS = nLshifts[ i ]; pulses_ptr = &pulses[ silk_SMULBB( i, SHELL_CODEC_FRAME_LENGTH ) ]; for( k = 0; k < SHELL_CODEC_FRAME_LENGTH; k++ ) { abs_q = pulses_ptr[ k ]; for( j = 0; j < nLS; j++ ) { abs_q = silk_LSHIFT( abs_q, 1 ); abs_q += ec_dec_icdf( psRangeDec, silk_lsb_iCDF, 8 ); } pulses_ptr[ k ] = abs_q; } /* Mark the number of pulses non-zero for sign decoding. */ sum_pulses[ i ] |= nLS << 5; } } /****************************************/ /* Decode and add signs to pulse signal */ /****************************************/ silk_decode_signs( psRangeDec, pulses, frame_length, signalType, quantOffsetType, sum_pulses ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/decoder_set_fs.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Set decoder sampling rate */ opus_int silk_decoder_set_fs( silk_decoder_state *psDec, /* I/O Decoder state pointer */ opus_int fs_kHz, /* I Sampling frequency (kHz) */ opus_int32 fs_API_Hz /* I API Sampling frequency (Hz) */ ) { opus_int frame_length, ret = 0; silk_assert( fs_kHz == 8 || fs_kHz == 12 || fs_kHz == 16 ); silk_assert( psDec->nb_subfr == MAX_NB_SUBFR || psDec->nb_subfr == MAX_NB_SUBFR/2 ); /* New (sub)frame length */ psDec->subfr_length = silk_SMULBB( SUB_FRAME_LENGTH_MS, fs_kHz ); frame_length = silk_SMULBB( psDec->nb_subfr, psDec->subfr_length ); /* Initialize resampler when switching internal or external sampling frequency */ if( psDec->fs_kHz != fs_kHz || psDec->fs_API_hz != fs_API_Hz ) { /* Initialize the resampler for dec_API.c preparing resampling from fs_kHz to API_fs_Hz */ ret += silk_resampler_init( &psDec->resampler_state, silk_SMULBB( fs_kHz, 1000 ), fs_API_Hz, 0 ); psDec->fs_API_hz = fs_API_Hz; } if( psDec->fs_kHz != fs_kHz || frame_length != psDec->frame_length ) { if( fs_kHz == 8 ) { if( psDec->nb_subfr == MAX_NB_SUBFR ) { psDec->pitch_contour_iCDF = silk_pitch_contour_NB_iCDF; } else { psDec->pitch_contour_iCDF = silk_pitch_contour_10_ms_NB_iCDF; } } else { if( psDec->nb_subfr == MAX_NB_SUBFR ) { psDec->pitch_contour_iCDF = silk_pitch_contour_iCDF; } else { psDec->pitch_contour_iCDF = silk_pitch_contour_10_ms_iCDF; } } if( psDec->fs_kHz != fs_kHz ) { psDec->ltp_mem_length = silk_SMULBB( LTP_MEM_LENGTH_MS, fs_kHz ); if( fs_kHz == 8 || fs_kHz == 12 ) { psDec->LPC_order = MIN_LPC_ORDER; psDec->psNLSF_CB = &silk_NLSF_CB_NB_MB; } else { psDec->LPC_order = MAX_LPC_ORDER; psDec->psNLSF_CB = &silk_NLSF_CB_WB; } if( fs_kHz == 16 ) { psDec->pitch_lag_low_bits_iCDF = silk_uniform8_iCDF; } else if( fs_kHz == 12 ) { psDec->pitch_lag_low_bits_iCDF = silk_uniform6_iCDF; } else if( fs_kHz == 8 ) { psDec->pitch_lag_low_bits_iCDF = silk_uniform4_iCDF; } else { /* unsupported sampling rate */ silk_assert( 0 ); } psDec->first_frame_after_reset = 1; psDec->lagPrev = 100; psDec->LastGainIndex = 10; psDec->prevSignalType = TYPE_NO_VOICE_ACTIVITY; silk_memset( psDec->outBuf, 0, sizeof(psDec->outBuf)); silk_memset( psDec->sLPC_Q14_buf, 0, sizeof(psDec->sLPC_Q14_buf) ); } psDec->fs_kHz = fs_kHz; psDec->frame_length = frame_length; } /* Check that settings are valid */ silk_assert( psDec->frame_length > 0 && psDec->frame_length <= MAX_FRAME_LENGTH ); return ret; } ================================================ FILE: deps/pjsip/third_party/opus/silk/define.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_DEFINE_H #define SILK_DEFINE_H #include "errors.h" #include "typedef.h" #ifdef __cplusplus extern "C" { #endif /* Max number of encoder channels (1/2) */ #define ENCODER_NUM_CHANNELS 2 /* Number of decoder channels (1/2) */ #define DECODER_NUM_CHANNELS 2 #define MAX_FRAMES_PER_PACKET 3 /* Limits on bitrate */ #define MIN_TARGET_RATE_BPS 5000 #define MAX_TARGET_RATE_BPS 80000 #define TARGET_RATE_TAB_SZ 8 /* LBRR thresholds */ #define LBRR_NB_MIN_RATE_BPS 12000 #define LBRR_MB_MIN_RATE_BPS 14000 #define LBRR_WB_MIN_RATE_BPS 16000 /* DTX settings */ #define NB_SPEECH_FRAMES_BEFORE_DTX 10 /* eq 200 ms */ #define MAX_CONSECUTIVE_DTX 20 /* eq 400 ms */ /* Maximum sampling frequency */ #define MAX_FS_KHZ 16 #define MAX_API_FS_KHZ 48 /* Signal types */ #define TYPE_NO_VOICE_ACTIVITY 0 #define TYPE_UNVOICED 1 #define TYPE_VOICED 2 /* Conditional coding types */ #define CODE_INDEPENDENTLY 0 #define CODE_INDEPENDENTLY_NO_LTP_SCALING 1 #define CODE_CONDITIONALLY 2 /* Settings for stereo processing */ #define STEREO_QUANT_TAB_SIZE 16 #define STEREO_QUANT_SUB_STEPS 5 #define STEREO_INTERP_LEN_MS 8 /* must be even */ #define STEREO_RATIO_SMOOTH_COEF 0.01 /* smoothing coef for signal norms and stereo width */ /* Range of pitch lag estimates */ #define PITCH_EST_MIN_LAG_MS 2 /* 2 ms -> 500 Hz */ #define PITCH_EST_MAX_LAG_MS 18 /* 18 ms -> 56 Hz */ /* Maximum number of subframes */ #define MAX_NB_SUBFR 4 /* Number of samples per frame */ #define LTP_MEM_LENGTH_MS 20 #define SUB_FRAME_LENGTH_MS 5 #define MAX_SUB_FRAME_LENGTH ( SUB_FRAME_LENGTH_MS * MAX_FS_KHZ ) #define MAX_FRAME_LENGTH_MS ( SUB_FRAME_LENGTH_MS * MAX_NB_SUBFR ) #define MAX_FRAME_LENGTH ( MAX_FRAME_LENGTH_MS * MAX_FS_KHZ ) /* Milliseconds of lookahead for pitch analysis */ #define LA_PITCH_MS 2 #define LA_PITCH_MAX ( LA_PITCH_MS * MAX_FS_KHZ ) /* Order of LPC used in find pitch */ #define MAX_FIND_PITCH_LPC_ORDER 16 /* Length of LPC window used in find pitch */ #define FIND_PITCH_LPC_WIN_MS ( 20 + (LA_PITCH_MS << 1) ) #define FIND_PITCH_LPC_WIN_MS_2_SF ( 10 + (LA_PITCH_MS << 1) ) #define FIND_PITCH_LPC_WIN_MAX ( FIND_PITCH_LPC_WIN_MS * MAX_FS_KHZ ) /* Milliseconds of lookahead for noise shape analysis */ #define LA_SHAPE_MS 5 #define LA_SHAPE_MAX ( LA_SHAPE_MS * MAX_FS_KHZ ) /* Maximum length of LPC window used in noise shape analysis */ #define SHAPE_LPC_WIN_MAX ( 15 * MAX_FS_KHZ ) /* dB level of lowest gain quantization level */ #define MIN_QGAIN_DB 2 /* dB level of highest gain quantization level */ #define MAX_QGAIN_DB 88 /* Number of gain quantization levels */ #define N_LEVELS_QGAIN 64 /* Max increase in gain quantization index */ #define MAX_DELTA_GAIN_QUANT 36 /* Max decrease in gain quantization index */ #define MIN_DELTA_GAIN_QUANT -4 /* Quantization offsets (multiples of 4) */ #define OFFSET_VL_Q10 32 #define OFFSET_VH_Q10 100 #define OFFSET_UVL_Q10 100 #define OFFSET_UVH_Q10 240 #define QUANT_LEVEL_ADJUST_Q10 80 /* Maximum numbers of iterations used to stabilize an LPC vector */ #define MAX_LPC_STABILIZE_ITERATIONS 16 #define MAX_PREDICTION_POWER_GAIN 1e4f #define MAX_PREDICTION_POWER_GAIN_AFTER_RESET 1e2f #define MAX_LPC_ORDER 16 #define MIN_LPC_ORDER 10 /* Find Pred Coef defines */ #define LTP_ORDER 5 /* LTP quantization settings */ #define NB_LTP_CBKS 3 /* Flag to use harmonic noise shaping */ #define USE_HARM_SHAPING 1 /* Max LPC order of noise shaping filters */ #define MAX_SHAPE_LPC_ORDER 16 #define HARM_SHAPE_FIR_TAPS 3 /* Maximum number of delayed decision states */ #define MAX_DEL_DEC_STATES 4 #define LTP_BUF_LENGTH 512 #define LTP_MASK ( LTP_BUF_LENGTH - 1 ) #define DECISION_DELAY 32 #define DECISION_DELAY_MASK ( DECISION_DELAY - 1 ) /* Number of subframes for excitation entropy coding */ #define SHELL_CODEC_FRAME_LENGTH 16 #define LOG2_SHELL_CODEC_FRAME_LENGTH 4 #define MAX_NB_SHELL_BLOCKS ( MAX_FRAME_LENGTH / SHELL_CODEC_FRAME_LENGTH ) /* Number of rate levels, for entropy coding of excitation */ #define N_RATE_LEVELS 10 /* Maximum sum of pulses per shell coding frame */ #define SILK_MAX_PULSES 16 #define MAX_MATRIX_SIZE MAX_LPC_ORDER /* Max of LPC Order and LTP order */ #if( MAX_LPC_ORDER > DECISION_DELAY ) # define NSQ_LPC_BUF_LENGTH MAX_LPC_ORDER #else # define NSQ_LPC_BUF_LENGTH DECISION_DELAY #endif /***************************/ /* Voice activity detector */ /***************************/ #define VAD_N_BANDS 4 #define VAD_INTERNAL_SUBFRAMES_LOG2 2 #define VAD_INTERNAL_SUBFRAMES ( 1 << VAD_INTERNAL_SUBFRAMES_LOG2 ) #define VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 1024 /* Must be < 4096 */ #define VAD_NOISE_LEVELS_BIAS 50 /* Sigmoid settings */ #define VAD_NEGATIVE_OFFSET_Q5 128 /* sigmoid is 0 at -128 */ #define VAD_SNR_FACTOR_Q16 45000 /* smoothing for SNR measurement */ #define VAD_SNR_SMOOTH_COEF_Q18 4096 /* Size of the piecewise linear cosine approximation table for the LSFs */ #define LSF_COS_TAB_SZ_FIX 128 /******************/ /* NLSF quantizer */ /******************/ #define NLSF_W_Q 2 #define NLSF_VQ_MAX_VECTORS 32 #define NLSF_VQ_MAX_SURVIVORS 32 #define NLSF_QUANT_MAX_AMPLITUDE 4 #define NLSF_QUANT_MAX_AMPLITUDE_EXT 10 #define NLSF_QUANT_LEVEL_ADJ 0.1 #define NLSF_QUANT_DEL_DEC_STATES_LOG2 2 #define NLSF_QUANT_DEL_DEC_STATES ( 1 << NLSF_QUANT_DEL_DEC_STATES_LOG2 ) /* Transition filtering for mode switching */ #define TRANSITION_TIME_MS 5120 /* 5120 = 64 * FRAME_LENGTH_MS * ( TRANSITION_INT_NUM - 1 ) = 64*(20*4)*/ #define TRANSITION_NB 3 /* Hardcoded in tables */ #define TRANSITION_NA 2 /* Hardcoded in tables */ #define TRANSITION_INT_NUM 5 /* Hardcoded in tables */ #define TRANSITION_FRAMES ( TRANSITION_TIME_MS / MAX_FRAME_LENGTH_MS ) #define TRANSITION_INT_STEPS ( TRANSITION_FRAMES / ( TRANSITION_INT_NUM - 1 ) ) /* BWE factors to apply after packet loss */ #define BWE_AFTER_LOSS_Q16 63570 /* Defines for CN generation */ #define CNG_BUF_MASK_MAX 255 /* 2^floor(log2(MAX_FRAME_LENGTH))-1 */ #define CNG_GAIN_SMTH_Q16 4634 /* 0.25^(1/4) */ #define CNG_NLSF_SMTH_Q16 16348 /* 0.25 */ #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/enc_API.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "define.h" #include "API.h" #include "control.h" #include "typedef.h" #include "stack_alloc.h" #include "structs.h" #include "tuning_parameters.h" #ifdef FIXED_POINT #include "main_FIX.h" #else #include "main_FLP.h" #endif /***************************************/ /* Read control structure from encoder */ /***************************************/ static opus_int silk_QueryEncoder( /* O Returns error code */ const void *encState, /* I State */ silk_EncControlStruct *encStatus /* O Encoder Status */ ); /****************************************/ /* Encoder functions */ /****************************************/ opus_int silk_Get_Encoder_Size( /* O Returns error code */ opus_int *encSizeBytes /* O Number of bytes in SILK encoder state */ ) { opus_int ret = SILK_NO_ERROR; *encSizeBytes = sizeof( silk_encoder ); return ret; } /*************************/ /* Init or Reset encoder */ /*************************/ opus_int silk_InitEncoder( /* O Returns error code */ void *encState, /* I/O State */ int arch, /* I Run-time architecture */ silk_EncControlStruct *encStatus /* O Encoder Status */ ) { silk_encoder *psEnc; opus_int n, ret = SILK_NO_ERROR; psEnc = (silk_encoder *)encState; /* Reset encoder */ silk_memset( psEnc, 0, sizeof( silk_encoder ) ); for( n = 0; n < ENCODER_NUM_CHANNELS; n++ ) { if( ret += silk_init_encoder( &psEnc->state_Fxx[ n ], arch ) ) { silk_assert( 0 ); } } psEnc->nChannelsAPI = 1; psEnc->nChannelsInternal = 1; /* Read control structure */ if( ret += silk_QueryEncoder( encState, encStatus ) ) { silk_assert( 0 ); } return ret; } /***************************************/ /* Read control structure from encoder */ /***************************************/ static opus_int silk_QueryEncoder( /* O Returns error code */ const void *encState, /* I State */ silk_EncControlStruct *encStatus /* O Encoder Status */ ) { opus_int ret = SILK_NO_ERROR; silk_encoder_state_Fxx *state_Fxx; silk_encoder *psEnc = (silk_encoder *)encState; state_Fxx = psEnc->state_Fxx; encStatus->nChannelsAPI = psEnc->nChannelsAPI; encStatus->nChannelsInternal = psEnc->nChannelsInternal; encStatus->API_sampleRate = state_Fxx[ 0 ].sCmn.API_fs_Hz; encStatus->maxInternalSampleRate = state_Fxx[ 0 ].sCmn.maxInternal_fs_Hz; encStatus->minInternalSampleRate = state_Fxx[ 0 ].sCmn.minInternal_fs_Hz; encStatus->desiredInternalSampleRate = state_Fxx[ 0 ].sCmn.desiredInternal_fs_Hz; encStatus->payloadSize_ms = state_Fxx[ 0 ].sCmn.PacketSize_ms; encStatus->bitRate = state_Fxx[ 0 ].sCmn.TargetRate_bps; encStatus->packetLossPercentage = state_Fxx[ 0 ].sCmn.PacketLoss_perc; encStatus->complexity = state_Fxx[ 0 ].sCmn.Complexity; encStatus->useInBandFEC = state_Fxx[ 0 ].sCmn.useInBandFEC; encStatus->useDTX = state_Fxx[ 0 ].sCmn.useDTX; encStatus->useCBR = state_Fxx[ 0 ].sCmn.useCBR; encStatus->internalSampleRate = silk_SMULBB( state_Fxx[ 0 ].sCmn.fs_kHz, 1000 ); encStatus->allowBandwidthSwitch = state_Fxx[ 0 ].sCmn.allow_bandwidth_switch; encStatus->inWBmodeWithoutVariableLP = state_Fxx[ 0 ].sCmn.fs_kHz == 16 && state_Fxx[ 0 ].sCmn.sLP.mode == 0; return ret; } /**************************/ /* Encode frame with Silk */ /**************************/ /* Note: if prefillFlag is set, the input must contain 10 ms of audio, irrespective of what */ /* encControl->payloadSize_ms is set to */ opus_int silk_Encode( /* O Returns error code */ void *encState, /* I/O State */ silk_EncControlStruct *encControl, /* I Control status */ const opus_int16 *samplesIn, /* I Speech sample input vector */ opus_int nSamplesIn, /* I Number of samples in input vector */ ec_enc *psRangeEnc, /* I/O Compressor data structure */ opus_int32 *nBytesOut, /* I/O Number of bytes in payload (input: Max bytes) */ const opus_int prefillFlag /* I Flag to indicate prefilling buffers no coding */ ) { opus_int n, i, nBits, flags, tmp_payloadSize_ms = 0, tmp_complexity = 0, ret = 0; opus_int nSamplesToBuffer, nSamplesToBufferMax, nBlocksOf10ms; opus_int nSamplesFromInput = 0, nSamplesFromInputMax; opus_int speech_act_thr_for_switch_Q8; opus_int32 TargetRate_bps, MStargetRates_bps[ 2 ], channelRate_bps, LBRR_symbol, sum; silk_encoder *psEnc = ( silk_encoder * )encState; VARDECL( opus_int16, buf ); opus_int transition, curr_block, tot_blocks; SAVE_STACK; if (encControl->reducedDependency) { psEnc->state_Fxx[0].sCmn.first_frame_after_reset = 1; psEnc->state_Fxx[1].sCmn.first_frame_after_reset = 1; } psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded = psEnc->state_Fxx[ 1 ].sCmn.nFramesEncoded = 0; /* Check values in encoder control structure */ if( ( ret = check_control_input( encControl ) ) != 0 ) { silk_assert( 0 ); RESTORE_STACK; return ret; } encControl->switchReady = 0; if( encControl->nChannelsInternal > psEnc->nChannelsInternal ) { /* Mono -> Stereo transition: init state of second channel and stereo state */ ret += silk_init_encoder( &psEnc->state_Fxx[ 1 ], psEnc->state_Fxx[ 0 ].sCmn.arch ); silk_memset( psEnc->sStereo.pred_prev_Q13, 0, sizeof( psEnc->sStereo.pred_prev_Q13 ) ); silk_memset( psEnc->sStereo.sSide, 0, sizeof( psEnc->sStereo.sSide ) ); psEnc->sStereo.mid_side_amp_Q0[ 0 ] = 0; psEnc->sStereo.mid_side_amp_Q0[ 1 ] = 1; psEnc->sStereo.mid_side_amp_Q0[ 2 ] = 0; psEnc->sStereo.mid_side_amp_Q0[ 3 ] = 1; psEnc->sStereo.width_prev_Q14 = 0; psEnc->sStereo.smth_width_Q14 = SILK_FIX_CONST( 1, 14 ); if( psEnc->nChannelsAPI == 2 ) { silk_memcpy( &psEnc->state_Fxx[ 1 ].sCmn.resampler_state, &psEnc->state_Fxx[ 0 ].sCmn.resampler_state, sizeof( silk_resampler_state_struct ) ); silk_memcpy( &psEnc->state_Fxx[ 1 ].sCmn.In_HP_State, &psEnc->state_Fxx[ 0 ].sCmn.In_HP_State, sizeof( psEnc->state_Fxx[ 1 ].sCmn.In_HP_State ) ); } } transition = (encControl->payloadSize_ms != psEnc->state_Fxx[ 0 ].sCmn.PacketSize_ms) || (psEnc->nChannelsInternal != encControl->nChannelsInternal); psEnc->nChannelsAPI = encControl->nChannelsAPI; psEnc->nChannelsInternal = encControl->nChannelsInternal; nBlocksOf10ms = silk_DIV32( 100 * nSamplesIn, encControl->API_sampleRate ); tot_blocks = ( nBlocksOf10ms > 1 ) ? nBlocksOf10ms >> 1 : 1; curr_block = 0; if( prefillFlag ) { /* Only accept input length of 10 ms */ if( nBlocksOf10ms != 1 ) { silk_assert( 0 ); RESTORE_STACK; return SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES; } /* Reset Encoder */ for( n = 0; n < encControl->nChannelsInternal; n++ ) { ret = silk_init_encoder( &psEnc->state_Fxx[ n ], psEnc->state_Fxx[ n ].sCmn.arch ); silk_assert( !ret ); } tmp_payloadSize_ms = encControl->payloadSize_ms; encControl->payloadSize_ms = 10; tmp_complexity = encControl->complexity; encControl->complexity = 0; for( n = 0; n < encControl->nChannelsInternal; n++ ) { psEnc->state_Fxx[ n ].sCmn.controlled_since_last_payload = 0; psEnc->state_Fxx[ n ].sCmn.prefillFlag = 1; } } else { /* Only accept input lengths that are a multiple of 10 ms */ if( nBlocksOf10ms * encControl->API_sampleRate != 100 * nSamplesIn || nSamplesIn < 0 ) { silk_assert( 0 ); RESTORE_STACK; return SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES; } /* Make sure no more than one packet can be produced */ if( 1000 * (opus_int32)nSamplesIn > encControl->payloadSize_ms * encControl->API_sampleRate ) { silk_assert( 0 ); RESTORE_STACK; return SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES; } } TargetRate_bps = silk_RSHIFT32( encControl->bitRate, encControl->nChannelsInternal - 1 ); for( n = 0; n < encControl->nChannelsInternal; n++ ) { /* Force the side channel to the same rate as the mid */ opus_int force_fs_kHz = (n==1) ? psEnc->state_Fxx[0].sCmn.fs_kHz : 0; if( ( ret = silk_control_encoder( &psEnc->state_Fxx[ n ], encControl, TargetRate_bps, psEnc->allowBandwidthSwitch, n, force_fs_kHz ) ) != 0 ) { silk_assert( 0 ); RESTORE_STACK; return ret; } if( psEnc->state_Fxx[n].sCmn.first_frame_after_reset || transition ) { for( i = 0; i < psEnc->state_Fxx[ 0 ].sCmn.nFramesPerPacket; i++ ) { psEnc->state_Fxx[ n ].sCmn.LBRR_flags[ i ] = 0; } } psEnc->state_Fxx[ n ].sCmn.inDTX = psEnc->state_Fxx[ n ].sCmn.useDTX; } silk_assert( encControl->nChannelsInternal == 1 || psEnc->state_Fxx[ 0 ].sCmn.fs_kHz == psEnc->state_Fxx[ 1 ].sCmn.fs_kHz ); /* Input buffering/resampling and encoding */ nSamplesToBufferMax = 10 * nBlocksOf10ms * psEnc->state_Fxx[ 0 ].sCmn.fs_kHz; nSamplesFromInputMax = silk_DIV32_16( nSamplesToBufferMax * psEnc->state_Fxx[ 0 ].sCmn.API_fs_Hz, psEnc->state_Fxx[ 0 ].sCmn.fs_kHz * 1000 ); ALLOC( buf, nSamplesFromInputMax, opus_int16 ); while( 1 ) { nSamplesToBuffer = psEnc->state_Fxx[ 0 ].sCmn.frame_length - psEnc->state_Fxx[ 0 ].sCmn.inputBufIx; nSamplesToBuffer = silk_min( nSamplesToBuffer, nSamplesToBufferMax ); nSamplesFromInput = silk_DIV32_16( nSamplesToBuffer * psEnc->state_Fxx[ 0 ].sCmn.API_fs_Hz, psEnc->state_Fxx[ 0 ].sCmn.fs_kHz * 1000 ); /* Resample and write to buffer */ if( encControl->nChannelsAPI == 2 && encControl->nChannelsInternal == 2 ) { opus_int id = psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded; for( n = 0; n < nSamplesFromInput; n++ ) { buf[ n ] = samplesIn[ 2 * n ]; } /* Making sure to start both resamplers from the same state when switching from mono to stereo */ if( psEnc->nPrevChannelsInternal == 1 && id==0 ) { silk_memcpy( &psEnc->state_Fxx[ 1 ].sCmn.resampler_state, &psEnc->state_Fxx[ 0 ].sCmn.resampler_state, sizeof(psEnc->state_Fxx[ 1 ].sCmn.resampler_state)); } ret += silk_resampler( &psEnc->state_Fxx[ 0 ].sCmn.resampler_state, &psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ psEnc->state_Fxx[ 0 ].sCmn.inputBufIx + 2 ], buf, nSamplesFromInput ); psEnc->state_Fxx[ 0 ].sCmn.inputBufIx += nSamplesToBuffer; nSamplesToBuffer = psEnc->state_Fxx[ 1 ].sCmn.frame_length - psEnc->state_Fxx[ 1 ].sCmn.inputBufIx; nSamplesToBuffer = silk_min( nSamplesToBuffer, 10 * nBlocksOf10ms * psEnc->state_Fxx[ 1 ].sCmn.fs_kHz ); for( n = 0; n < nSamplesFromInput; n++ ) { buf[ n ] = samplesIn[ 2 * n + 1 ]; } ret += silk_resampler( &psEnc->state_Fxx[ 1 ].sCmn.resampler_state, &psEnc->state_Fxx[ 1 ].sCmn.inputBuf[ psEnc->state_Fxx[ 1 ].sCmn.inputBufIx + 2 ], buf, nSamplesFromInput ); psEnc->state_Fxx[ 1 ].sCmn.inputBufIx += nSamplesToBuffer; } else if( encControl->nChannelsAPI == 2 && encControl->nChannelsInternal == 1 ) { /* Combine left and right channels before resampling */ for( n = 0; n < nSamplesFromInput; n++ ) { sum = samplesIn[ 2 * n ] + samplesIn[ 2 * n + 1 ]; buf[ n ] = (opus_int16)silk_RSHIFT_ROUND( sum, 1 ); } ret += silk_resampler( &psEnc->state_Fxx[ 0 ].sCmn.resampler_state, &psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ psEnc->state_Fxx[ 0 ].sCmn.inputBufIx + 2 ], buf, nSamplesFromInput ); /* On the first mono frame, average the results for the two resampler states */ if( psEnc->nPrevChannelsInternal == 2 && psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded == 0 ) { ret += silk_resampler( &psEnc->state_Fxx[ 1 ].sCmn.resampler_state, &psEnc->state_Fxx[ 1 ].sCmn.inputBuf[ psEnc->state_Fxx[ 1 ].sCmn.inputBufIx + 2 ], buf, nSamplesFromInput ); for( n = 0; n < psEnc->state_Fxx[ 0 ].sCmn.frame_length; n++ ) { psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ psEnc->state_Fxx[ 0 ].sCmn.inputBufIx+n+2 ] = silk_RSHIFT(psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ psEnc->state_Fxx[ 0 ].sCmn.inputBufIx+n+2 ] + psEnc->state_Fxx[ 1 ].sCmn.inputBuf[ psEnc->state_Fxx[ 1 ].sCmn.inputBufIx+n+2 ], 1); } } psEnc->state_Fxx[ 0 ].sCmn.inputBufIx += nSamplesToBuffer; } else { silk_assert( encControl->nChannelsAPI == 1 && encControl->nChannelsInternal == 1 ); silk_memcpy(buf, samplesIn, nSamplesFromInput*sizeof(opus_int16)); ret += silk_resampler( &psEnc->state_Fxx[ 0 ].sCmn.resampler_state, &psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ psEnc->state_Fxx[ 0 ].sCmn.inputBufIx + 2 ], buf, nSamplesFromInput ); psEnc->state_Fxx[ 0 ].sCmn.inputBufIx += nSamplesToBuffer; } samplesIn += nSamplesFromInput * encControl->nChannelsAPI; nSamplesIn -= nSamplesFromInput; /* Default */ psEnc->allowBandwidthSwitch = 0; /* Silk encoder */ if( psEnc->state_Fxx[ 0 ].sCmn.inputBufIx >= psEnc->state_Fxx[ 0 ].sCmn.frame_length ) { /* Enough data in input buffer, so encode */ silk_assert( psEnc->state_Fxx[ 0 ].sCmn.inputBufIx == psEnc->state_Fxx[ 0 ].sCmn.frame_length ); silk_assert( encControl->nChannelsInternal == 1 || psEnc->state_Fxx[ 1 ].sCmn.inputBufIx == psEnc->state_Fxx[ 1 ].sCmn.frame_length ); /* Deal with LBRR data */ if( psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded == 0 && !prefillFlag ) { /* Create space at start of payload for VAD and FEC flags */ opus_uint8 iCDF[ 2 ] = { 0, 0 }; iCDF[ 0 ] = 256 - silk_RSHIFT( 256, ( psEnc->state_Fxx[ 0 ].sCmn.nFramesPerPacket + 1 ) * encControl->nChannelsInternal ); ec_enc_icdf( psRangeEnc, 0, iCDF, 8 ); /* Encode any LBRR data from previous packet */ /* Encode LBRR flags */ for( n = 0; n < encControl->nChannelsInternal; n++ ) { LBRR_symbol = 0; for( i = 0; i < psEnc->state_Fxx[ n ].sCmn.nFramesPerPacket; i++ ) { LBRR_symbol |= silk_LSHIFT( psEnc->state_Fxx[ n ].sCmn.LBRR_flags[ i ], i ); } psEnc->state_Fxx[ n ].sCmn.LBRR_flag = LBRR_symbol > 0 ? 1 : 0; if( LBRR_symbol && psEnc->state_Fxx[ n ].sCmn.nFramesPerPacket > 1 ) { ec_enc_icdf( psRangeEnc, LBRR_symbol - 1, silk_LBRR_flags_iCDF_ptr[ psEnc->state_Fxx[ n ].sCmn.nFramesPerPacket - 2 ], 8 ); } } /* Code LBRR indices and excitation signals */ for( i = 0; i < psEnc->state_Fxx[ 0 ].sCmn.nFramesPerPacket; i++ ) { for( n = 0; n < encControl->nChannelsInternal; n++ ) { if( psEnc->state_Fxx[ n ].sCmn.LBRR_flags[ i ] ) { opus_int condCoding; if( encControl->nChannelsInternal == 2 && n == 0 ) { silk_stereo_encode_pred( psRangeEnc, psEnc->sStereo.predIx[ i ] ); /* For LBRR data there's no need to code the mid-only flag if the side-channel LBRR flag is set */ if( psEnc->state_Fxx[ 1 ].sCmn.LBRR_flags[ i ] == 0 ) { silk_stereo_encode_mid_only( psRangeEnc, psEnc->sStereo.mid_only_flags[ i ] ); } } /* Use conditional coding if previous frame available */ if( i > 0 && psEnc->state_Fxx[ n ].sCmn.LBRR_flags[ i - 1 ] ) { condCoding = CODE_CONDITIONALLY; } else { condCoding = CODE_INDEPENDENTLY; } silk_encode_indices( &psEnc->state_Fxx[ n ].sCmn, psRangeEnc, i, 1, condCoding ); silk_encode_pulses( psRangeEnc, psEnc->state_Fxx[ n ].sCmn.indices_LBRR[i].signalType, psEnc->state_Fxx[ n ].sCmn.indices_LBRR[i].quantOffsetType, psEnc->state_Fxx[ n ].sCmn.pulses_LBRR[ i ], psEnc->state_Fxx[ n ].sCmn.frame_length ); } } } /* Reset LBRR flags */ for( n = 0; n < encControl->nChannelsInternal; n++ ) { silk_memset( psEnc->state_Fxx[ n ].sCmn.LBRR_flags, 0, sizeof( psEnc->state_Fxx[ n ].sCmn.LBRR_flags ) ); } psEnc->nBitsUsedLBRR = ec_tell( psRangeEnc ); } silk_HP_variable_cutoff( psEnc->state_Fxx ); /* Total target bits for packet */ nBits = silk_DIV32_16( silk_MUL( encControl->bitRate, encControl->payloadSize_ms ), 1000 ); /* Subtract bits used for LBRR */ if( !prefillFlag ) { nBits -= psEnc->nBitsUsedLBRR; } /* Divide by number of uncoded frames left in packet */ nBits = silk_DIV32_16( nBits, psEnc->state_Fxx[ 0 ].sCmn.nFramesPerPacket ); /* Convert to bits/second */ if( encControl->payloadSize_ms == 10 ) { TargetRate_bps = silk_SMULBB( nBits, 100 ); } else { TargetRate_bps = silk_SMULBB( nBits, 50 ); } /* Subtract fraction of bits in excess of target in previous frames and packets */ TargetRate_bps -= silk_DIV32_16( silk_MUL( psEnc->nBitsExceeded, 1000 ), BITRESERVOIR_DECAY_TIME_MS ); if( !prefillFlag && psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded > 0 ) { /* Compare actual vs target bits so far in this packet */ opus_int32 bitsBalance = ec_tell( psRangeEnc ) - psEnc->nBitsUsedLBRR - nBits * psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded; TargetRate_bps -= silk_DIV32_16( silk_MUL( bitsBalance, 1000 ), BITRESERVOIR_DECAY_TIME_MS ); } /* Never exceed input bitrate */ TargetRate_bps = silk_LIMIT( TargetRate_bps, encControl->bitRate, 5000 ); /* Convert Left/Right to Mid/Side */ if( encControl->nChannelsInternal == 2 ) { silk_stereo_LR_to_MS( &psEnc->sStereo, &psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ 2 ], &psEnc->state_Fxx[ 1 ].sCmn.inputBuf[ 2 ], psEnc->sStereo.predIx[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ], &psEnc->sStereo.mid_only_flags[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ], MStargetRates_bps, TargetRate_bps, psEnc->state_Fxx[ 0 ].sCmn.speech_activity_Q8, encControl->toMono, psEnc->state_Fxx[ 0 ].sCmn.fs_kHz, psEnc->state_Fxx[ 0 ].sCmn.frame_length ); if( psEnc->sStereo.mid_only_flags[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ] == 0 ) { /* Reset side channel encoder memory for first frame with side coding */ if( psEnc->prev_decode_only_middle == 1 ) { silk_memset( &psEnc->state_Fxx[ 1 ].sShape, 0, sizeof( psEnc->state_Fxx[ 1 ].sShape ) ); silk_memset( &psEnc->state_Fxx[ 1 ].sPrefilt, 0, sizeof( psEnc->state_Fxx[ 1 ].sPrefilt ) ); silk_memset( &psEnc->state_Fxx[ 1 ].sCmn.sNSQ, 0, sizeof( psEnc->state_Fxx[ 1 ].sCmn.sNSQ ) ); silk_memset( psEnc->state_Fxx[ 1 ].sCmn.prev_NLSFq_Q15, 0, sizeof( psEnc->state_Fxx[ 1 ].sCmn.prev_NLSFq_Q15 ) ); silk_memset( &psEnc->state_Fxx[ 1 ].sCmn.sLP.In_LP_State, 0, sizeof( psEnc->state_Fxx[ 1 ].sCmn.sLP.In_LP_State ) ); psEnc->state_Fxx[ 1 ].sCmn.prevLag = 100; psEnc->state_Fxx[ 1 ].sCmn.sNSQ.lagPrev = 100; psEnc->state_Fxx[ 1 ].sShape.LastGainIndex = 10; psEnc->state_Fxx[ 1 ].sCmn.prevSignalType = TYPE_NO_VOICE_ACTIVITY; psEnc->state_Fxx[ 1 ].sCmn.sNSQ.prev_gain_Q16 = 65536; psEnc->state_Fxx[ 1 ].sCmn.first_frame_after_reset = 1; } silk_encode_do_VAD_Fxx( &psEnc->state_Fxx[ 1 ] ); } else { psEnc->state_Fxx[ 1 ].sCmn.VAD_flags[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ] = 0; } if( !prefillFlag ) { silk_stereo_encode_pred( psRangeEnc, psEnc->sStereo.predIx[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ] ); if( psEnc->state_Fxx[ 1 ].sCmn.VAD_flags[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ] == 0 ) { silk_stereo_encode_mid_only( psRangeEnc, psEnc->sStereo.mid_only_flags[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ] ); } } } else { /* Buffering */ silk_memcpy( psEnc->state_Fxx[ 0 ].sCmn.inputBuf, psEnc->sStereo.sMid, 2 * sizeof( opus_int16 ) ); silk_memcpy( psEnc->sStereo.sMid, &psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ psEnc->state_Fxx[ 0 ].sCmn.frame_length ], 2 * sizeof( opus_int16 ) ); } silk_encode_do_VAD_Fxx( &psEnc->state_Fxx[ 0 ] ); /* Encode */ for( n = 0; n < encControl->nChannelsInternal; n++ ) { opus_int maxBits, useCBR; /* Handling rate constraints */ maxBits = encControl->maxBits; if( tot_blocks == 2 && curr_block == 0 ) { maxBits = maxBits * 3 / 5; } else if( tot_blocks == 3 ) { if( curr_block == 0 ) { maxBits = maxBits * 2 / 5; } else if( curr_block == 1 ) { maxBits = maxBits * 3 / 4; } } useCBR = encControl->useCBR && curr_block == tot_blocks - 1; if( encControl->nChannelsInternal == 1 ) { channelRate_bps = TargetRate_bps; } else { channelRate_bps = MStargetRates_bps[ n ]; if( n == 0 && MStargetRates_bps[ 1 ] > 0 ) { useCBR = 0; /* Give mid up to 1/2 of the max bits for that frame */ maxBits -= encControl->maxBits / ( tot_blocks * 2 ); } } if( channelRate_bps > 0 ) { opus_int condCoding; silk_control_SNR( &psEnc->state_Fxx[ n ].sCmn, channelRate_bps ); /* Use independent coding if no previous frame available */ if( psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded - n <= 0 ) { condCoding = CODE_INDEPENDENTLY; } else if( n > 0 && psEnc->prev_decode_only_middle ) { /* If we skipped a side frame in this packet, we don't need LTP scaling; the LTP state is well-defined. */ condCoding = CODE_INDEPENDENTLY_NO_LTP_SCALING; } else { condCoding = CODE_CONDITIONALLY; } if( ( ret = silk_encode_frame_Fxx( &psEnc->state_Fxx[ n ], nBytesOut, psRangeEnc, condCoding, maxBits, useCBR ) ) != 0 ) { silk_assert( 0 ); } } psEnc->state_Fxx[ n ].sCmn.controlled_since_last_payload = 0; psEnc->state_Fxx[ n ].sCmn.inputBufIx = 0; psEnc->state_Fxx[ n ].sCmn.nFramesEncoded++; } psEnc->prev_decode_only_middle = psEnc->sStereo.mid_only_flags[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded - 1 ]; /* Insert VAD and FEC flags at beginning of bitstream */ if( *nBytesOut > 0 && psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded == psEnc->state_Fxx[ 0 ].sCmn.nFramesPerPacket) { flags = 0; for( n = 0; n < encControl->nChannelsInternal; n++ ) { for( i = 0; i < psEnc->state_Fxx[ n ].sCmn.nFramesPerPacket; i++ ) { flags = silk_LSHIFT( flags, 1 ); flags |= psEnc->state_Fxx[ n ].sCmn.VAD_flags[ i ]; } flags = silk_LSHIFT( flags, 1 ); flags |= psEnc->state_Fxx[ n ].sCmn.LBRR_flag; } if( !prefillFlag ) { ec_enc_patch_initial_bits( psRangeEnc, flags, ( psEnc->state_Fxx[ 0 ].sCmn.nFramesPerPacket + 1 ) * encControl->nChannelsInternal ); } /* Return zero bytes if all channels DTXed */ if( psEnc->state_Fxx[ 0 ].sCmn.inDTX && ( encControl->nChannelsInternal == 1 || psEnc->state_Fxx[ 1 ].sCmn.inDTX ) ) { *nBytesOut = 0; } psEnc->nBitsExceeded += *nBytesOut * 8; psEnc->nBitsExceeded -= silk_DIV32_16( silk_MUL( encControl->bitRate, encControl->payloadSize_ms ), 1000 ); psEnc->nBitsExceeded = silk_LIMIT( psEnc->nBitsExceeded, 0, 10000 ); /* Update flag indicating if bandwidth switching is allowed */ speech_act_thr_for_switch_Q8 = silk_SMLAWB( SILK_FIX_CONST( SPEECH_ACTIVITY_DTX_THRES, 8 ), SILK_FIX_CONST( ( 1 - SPEECH_ACTIVITY_DTX_THRES ) / MAX_BANDWIDTH_SWITCH_DELAY_MS, 16 + 8 ), psEnc->timeSinceSwitchAllowed_ms ); if( psEnc->state_Fxx[ 0 ].sCmn.speech_activity_Q8 < speech_act_thr_for_switch_Q8 ) { psEnc->allowBandwidthSwitch = 1; psEnc->timeSinceSwitchAllowed_ms = 0; } else { psEnc->allowBandwidthSwitch = 0; psEnc->timeSinceSwitchAllowed_ms += encControl->payloadSize_ms; } } if( nSamplesIn == 0 ) { break; } } else { break; } curr_block++; } psEnc->nPrevChannelsInternal = encControl->nChannelsInternal; encControl->allowBandwidthSwitch = psEnc->allowBandwidthSwitch; encControl->inWBmodeWithoutVariableLP = psEnc->state_Fxx[ 0 ].sCmn.fs_kHz == 16 && psEnc->state_Fxx[ 0 ].sCmn.sLP.mode == 0; encControl->internalSampleRate = silk_SMULBB( psEnc->state_Fxx[ 0 ].sCmn.fs_kHz, 1000 ); encControl->stereoWidth_Q14 = encControl->toMono ? 0 : psEnc->sStereo.smth_width_Q14; if( prefillFlag ) { encControl->payloadSize_ms = tmp_payloadSize_ms; encControl->complexity = tmp_complexity; for( n = 0; n < encControl->nChannelsInternal; n++ ) { psEnc->state_Fxx[ n ].sCmn.controlled_since_last_payload = 0; psEnc->state_Fxx[ n ].sCmn.prefillFlag = 0; } } RESTORE_STACK; return ret; } ================================================ FILE: deps/pjsip/third_party/opus/silk/encode_indices.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Encode side-information parameters to payload */ void silk_encode_indices( silk_encoder_state *psEncC, /* I/O Encoder state */ ec_enc *psRangeEnc, /* I/O Compressor data structure */ opus_int FrameIndex, /* I Frame number */ opus_int encode_LBRR, /* I Flag indicating LBRR data is being encoded */ opus_int condCoding /* I The type of conditional coding to use */ ) { opus_int i, k, typeOffset; opus_int encode_absolute_lagIndex, delta_lagIndex; opus_int16 ec_ix[ MAX_LPC_ORDER ]; opus_uint8 pred_Q8[ MAX_LPC_ORDER ]; const SideInfoIndices *psIndices; if( encode_LBRR ) { psIndices = &psEncC->indices_LBRR[ FrameIndex ]; } else { psIndices = &psEncC->indices; } /*******************************************/ /* Encode signal type and quantizer offset */ /*******************************************/ typeOffset = 2 * psIndices->signalType + psIndices->quantOffsetType; silk_assert( typeOffset >= 0 && typeOffset < 6 ); silk_assert( encode_LBRR == 0 || typeOffset >= 2 ); if( encode_LBRR || typeOffset >= 2 ) { ec_enc_icdf( psRangeEnc, typeOffset - 2, silk_type_offset_VAD_iCDF, 8 ); } else { ec_enc_icdf( psRangeEnc, typeOffset, silk_type_offset_no_VAD_iCDF, 8 ); } /****************/ /* Encode gains */ /****************/ /* first subframe */ if( condCoding == CODE_CONDITIONALLY ) { /* conditional coding */ silk_assert( psIndices->GainsIndices[ 0 ] >= 0 && psIndices->GainsIndices[ 0 ] < MAX_DELTA_GAIN_QUANT - MIN_DELTA_GAIN_QUANT + 1 ); ec_enc_icdf( psRangeEnc, psIndices->GainsIndices[ 0 ], silk_delta_gain_iCDF, 8 ); } else { /* independent coding, in two stages: MSB bits followed by 3 LSBs */ silk_assert( psIndices->GainsIndices[ 0 ] >= 0 && psIndices->GainsIndices[ 0 ] < N_LEVELS_QGAIN ); ec_enc_icdf( psRangeEnc, silk_RSHIFT( psIndices->GainsIndices[ 0 ], 3 ), silk_gain_iCDF[ psIndices->signalType ], 8 ); ec_enc_icdf( psRangeEnc, psIndices->GainsIndices[ 0 ] & 7, silk_uniform8_iCDF, 8 ); } /* remaining subframes */ for( i = 1; i < psEncC->nb_subfr; i++ ) { silk_assert( psIndices->GainsIndices[ i ] >= 0 && psIndices->GainsIndices[ i ] < MAX_DELTA_GAIN_QUANT - MIN_DELTA_GAIN_QUANT + 1 ); ec_enc_icdf( psRangeEnc, psIndices->GainsIndices[ i ], silk_delta_gain_iCDF, 8 ); } /****************/ /* Encode NLSFs */ /****************/ ec_enc_icdf( psRangeEnc, psIndices->NLSFIndices[ 0 ], &psEncC->psNLSF_CB->CB1_iCDF[ ( psIndices->signalType >> 1 ) * psEncC->psNLSF_CB->nVectors ], 8 ); silk_NLSF_unpack( ec_ix, pred_Q8, psEncC->psNLSF_CB, psIndices->NLSFIndices[ 0 ] ); silk_assert( psEncC->psNLSF_CB->order == psEncC->predictLPCOrder ); for( i = 0; i < psEncC->psNLSF_CB->order; i++ ) { if( psIndices->NLSFIndices[ i+1 ] >= NLSF_QUANT_MAX_AMPLITUDE ) { ec_enc_icdf( psRangeEnc, 2 * NLSF_QUANT_MAX_AMPLITUDE, &psEncC->psNLSF_CB->ec_iCDF[ ec_ix[ i ] ], 8 ); ec_enc_icdf( psRangeEnc, psIndices->NLSFIndices[ i+1 ] - NLSF_QUANT_MAX_AMPLITUDE, silk_NLSF_EXT_iCDF, 8 ); } else if( psIndices->NLSFIndices[ i+1 ] <= -NLSF_QUANT_MAX_AMPLITUDE ) { ec_enc_icdf( psRangeEnc, 0, &psEncC->psNLSF_CB->ec_iCDF[ ec_ix[ i ] ], 8 ); ec_enc_icdf( psRangeEnc, -psIndices->NLSFIndices[ i+1 ] - NLSF_QUANT_MAX_AMPLITUDE, silk_NLSF_EXT_iCDF, 8 ); } else { ec_enc_icdf( psRangeEnc, psIndices->NLSFIndices[ i+1 ] + NLSF_QUANT_MAX_AMPLITUDE, &psEncC->psNLSF_CB->ec_iCDF[ ec_ix[ i ] ], 8 ); } } /* Encode NLSF interpolation factor */ if( psEncC->nb_subfr == MAX_NB_SUBFR ) { silk_assert( psIndices->NLSFInterpCoef_Q2 >= 0 && psIndices->NLSFInterpCoef_Q2 < 5 ); ec_enc_icdf( psRangeEnc, psIndices->NLSFInterpCoef_Q2, silk_NLSF_interpolation_factor_iCDF, 8 ); } if( psIndices->signalType == TYPE_VOICED ) { /*********************/ /* Encode pitch lags */ /*********************/ /* lag index */ encode_absolute_lagIndex = 1; if( condCoding == CODE_CONDITIONALLY && psEncC->ec_prevSignalType == TYPE_VOICED ) { /* Delta Encoding */ delta_lagIndex = psIndices->lagIndex - psEncC->ec_prevLagIndex; if( delta_lagIndex < -8 || delta_lagIndex > 11 ) { delta_lagIndex = 0; } else { delta_lagIndex = delta_lagIndex + 9; encode_absolute_lagIndex = 0; /* Only use delta */ } silk_assert( delta_lagIndex >= 0 && delta_lagIndex < 21 ); ec_enc_icdf( psRangeEnc, delta_lagIndex, silk_pitch_delta_iCDF, 8 ); } if( encode_absolute_lagIndex ) { /* Absolute encoding */ opus_int32 pitch_high_bits, pitch_low_bits; pitch_high_bits = silk_DIV32_16( psIndices->lagIndex, silk_RSHIFT( psEncC->fs_kHz, 1 ) ); pitch_low_bits = psIndices->lagIndex - silk_SMULBB( pitch_high_bits, silk_RSHIFT( psEncC->fs_kHz, 1 ) ); silk_assert( pitch_low_bits < psEncC->fs_kHz / 2 ); silk_assert( pitch_high_bits < 32 ); ec_enc_icdf( psRangeEnc, pitch_high_bits, silk_pitch_lag_iCDF, 8 ); ec_enc_icdf( psRangeEnc, pitch_low_bits, psEncC->pitch_lag_low_bits_iCDF, 8 ); } psEncC->ec_prevLagIndex = psIndices->lagIndex; /* Countour index */ silk_assert( psIndices->contourIndex >= 0 ); silk_assert( ( psIndices->contourIndex < 34 && psEncC->fs_kHz > 8 && psEncC->nb_subfr == 4 ) || ( psIndices->contourIndex < 11 && psEncC->fs_kHz == 8 && psEncC->nb_subfr == 4 ) || ( psIndices->contourIndex < 12 && psEncC->fs_kHz > 8 && psEncC->nb_subfr == 2 ) || ( psIndices->contourIndex < 3 && psEncC->fs_kHz == 8 && psEncC->nb_subfr == 2 ) ); ec_enc_icdf( psRangeEnc, psIndices->contourIndex, psEncC->pitch_contour_iCDF, 8 ); /********************/ /* Encode LTP gains */ /********************/ /* PERIndex value */ silk_assert( psIndices->PERIndex >= 0 && psIndices->PERIndex < 3 ); ec_enc_icdf( psRangeEnc, psIndices->PERIndex, silk_LTP_per_index_iCDF, 8 ); /* Codebook Indices */ for( k = 0; k < psEncC->nb_subfr; k++ ) { silk_assert( psIndices->LTPIndex[ k ] >= 0 && psIndices->LTPIndex[ k ] < ( 8 << psIndices->PERIndex ) ); ec_enc_icdf( psRangeEnc, psIndices->LTPIndex[ k ], silk_LTP_gain_iCDF_ptrs[ psIndices->PERIndex ], 8 ); } /**********************/ /* Encode LTP scaling */ /**********************/ if( condCoding == CODE_INDEPENDENTLY ) { silk_assert( psIndices->LTP_scaleIndex >= 0 && psIndices->LTP_scaleIndex < 3 ); ec_enc_icdf( psRangeEnc, psIndices->LTP_scaleIndex, silk_LTPscale_iCDF, 8 ); } silk_assert( !condCoding || psIndices->LTP_scaleIndex == 0 ); } psEncC->ec_prevSignalType = psIndices->signalType; /***************/ /* Encode seed */ /***************/ silk_assert( psIndices->Seed >= 0 && psIndices->Seed < 4 ); ec_enc_icdf( psRangeEnc, psIndices->Seed, silk_uniform4_iCDF, 8 ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/encode_pulses.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #include "stack_alloc.h" /*********************************************/ /* Encode quantization indices of excitation */ /*********************************************/ static OPUS_INLINE opus_int combine_and_check( /* return ok */ opus_int *pulses_comb, /* O */ const opus_int *pulses_in, /* I */ opus_int max_pulses, /* I max value for sum of pulses */ opus_int len /* I number of output values */ ) { opus_int k, sum; for( k = 0; k < len; k++ ) { sum = pulses_in[ 2 * k ] + pulses_in[ 2 * k + 1 ]; if( sum > max_pulses ) { return 1; } pulses_comb[ k ] = sum; } return 0; } /* Encode quantization indices of excitation */ void silk_encode_pulses( ec_enc *psRangeEnc, /* I/O compressor data structure */ const opus_int signalType, /* I Signal type */ const opus_int quantOffsetType, /* I quantOffsetType */ opus_int8 pulses[], /* I quantization indices */ const opus_int frame_length /* I Frame length */ ) { opus_int i, k, j, iter, bit, nLS, scale_down, RateLevelIndex = 0; opus_int32 abs_q, minSumBits_Q5, sumBits_Q5; VARDECL( opus_int, abs_pulses ); VARDECL( opus_int, sum_pulses ); VARDECL( opus_int, nRshifts ); opus_int pulses_comb[ 8 ]; opus_int *abs_pulses_ptr; const opus_int8 *pulses_ptr; const opus_uint8 *cdf_ptr; const opus_uint8 *nBits_ptr; SAVE_STACK; silk_memset( pulses_comb, 0, 8 * sizeof( opus_int ) ); /* Fixing Valgrind reported problem*/ /****************************/ /* Prepare for shell coding */ /****************************/ /* Calculate number of shell blocks */ silk_assert( 1 << LOG2_SHELL_CODEC_FRAME_LENGTH == SHELL_CODEC_FRAME_LENGTH ); iter = silk_RSHIFT( frame_length, LOG2_SHELL_CODEC_FRAME_LENGTH ); if( iter * SHELL_CODEC_FRAME_LENGTH < frame_length ) { silk_assert( frame_length == 12 * 10 ); /* Make sure only happens for 10 ms @ 12 kHz */ iter++; silk_memset( &pulses[ frame_length ], 0, SHELL_CODEC_FRAME_LENGTH * sizeof(opus_int8)); } /* Take the absolute value of the pulses */ ALLOC( abs_pulses, iter * SHELL_CODEC_FRAME_LENGTH, opus_int ); silk_assert( !( SHELL_CODEC_FRAME_LENGTH & 3 ) ); for( i = 0; i < iter * SHELL_CODEC_FRAME_LENGTH; i+=4 ) { abs_pulses[i+0] = ( opus_int )silk_abs( pulses[ i + 0 ] ); abs_pulses[i+1] = ( opus_int )silk_abs( pulses[ i + 1 ] ); abs_pulses[i+2] = ( opus_int )silk_abs( pulses[ i + 2 ] ); abs_pulses[i+3] = ( opus_int )silk_abs( pulses[ i + 3 ] ); } /* Calc sum pulses per shell code frame */ ALLOC( sum_pulses, iter, opus_int ); ALLOC( nRshifts, iter, opus_int ); abs_pulses_ptr = abs_pulses; for( i = 0; i < iter; i++ ) { nRshifts[ i ] = 0; while( 1 ) { /* 1+1 -> 2 */ scale_down = combine_and_check( pulses_comb, abs_pulses_ptr, silk_max_pulses_table[ 0 ], 8 ); /* 2+2 -> 4 */ scale_down += combine_and_check( pulses_comb, pulses_comb, silk_max_pulses_table[ 1 ], 4 ); /* 4+4 -> 8 */ scale_down += combine_and_check( pulses_comb, pulses_comb, silk_max_pulses_table[ 2 ], 2 ); /* 8+8 -> 16 */ scale_down += combine_and_check( &sum_pulses[ i ], pulses_comb, silk_max_pulses_table[ 3 ], 1 ); if( scale_down ) { /* We need to downscale the quantization signal */ nRshifts[ i ]++; for( k = 0; k < SHELL_CODEC_FRAME_LENGTH; k++ ) { abs_pulses_ptr[ k ] = silk_RSHIFT( abs_pulses_ptr[ k ], 1 ); } } else { /* Jump out of while(1) loop and go to next shell coding frame */ break; } } abs_pulses_ptr += SHELL_CODEC_FRAME_LENGTH; } /**************/ /* Rate level */ /**************/ /* find rate level that leads to fewest bits for coding of pulses per block info */ minSumBits_Q5 = silk_int32_MAX; for( k = 0; k < N_RATE_LEVELS - 1; k++ ) { nBits_ptr = silk_pulses_per_block_BITS_Q5[ k ]; sumBits_Q5 = silk_rate_levels_BITS_Q5[ signalType >> 1 ][ k ]; for( i = 0; i < iter; i++ ) { if( nRshifts[ i ] > 0 ) { sumBits_Q5 += nBits_ptr[ SILK_MAX_PULSES + 1 ]; } else { sumBits_Q5 += nBits_ptr[ sum_pulses[ i ] ]; } } if( sumBits_Q5 < minSumBits_Q5 ) { minSumBits_Q5 = sumBits_Q5; RateLevelIndex = k; } } ec_enc_icdf( psRangeEnc, RateLevelIndex, silk_rate_levels_iCDF[ signalType >> 1 ], 8 ); /***************************************************/ /* Sum-Weighted-Pulses Encoding */ /***************************************************/ cdf_ptr = silk_pulses_per_block_iCDF[ RateLevelIndex ]; for( i = 0; i < iter; i++ ) { if( nRshifts[ i ] == 0 ) { ec_enc_icdf( psRangeEnc, sum_pulses[ i ], cdf_ptr, 8 ); } else { ec_enc_icdf( psRangeEnc, SILK_MAX_PULSES + 1, cdf_ptr, 8 ); for( k = 0; k < nRshifts[ i ] - 1; k++ ) { ec_enc_icdf( psRangeEnc, SILK_MAX_PULSES + 1, silk_pulses_per_block_iCDF[ N_RATE_LEVELS - 1 ], 8 ); } ec_enc_icdf( psRangeEnc, sum_pulses[ i ], silk_pulses_per_block_iCDF[ N_RATE_LEVELS - 1 ], 8 ); } } /******************/ /* Shell Encoding */ /******************/ for( i = 0; i < iter; i++ ) { if( sum_pulses[ i ] > 0 ) { silk_shell_encoder( psRangeEnc, &abs_pulses[ i * SHELL_CODEC_FRAME_LENGTH ] ); } } /****************/ /* LSB Encoding */ /****************/ for( i = 0; i < iter; i++ ) { if( nRshifts[ i ] > 0 ) { pulses_ptr = &pulses[ i * SHELL_CODEC_FRAME_LENGTH ]; nLS = nRshifts[ i ] - 1; for( k = 0; k < SHELL_CODEC_FRAME_LENGTH; k++ ) { abs_q = (opus_int8)silk_abs( pulses_ptr[ k ] ); for( j = nLS; j > 0; j-- ) { bit = silk_RSHIFT( abs_q, j ) & 1; ec_enc_icdf( psRangeEnc, bit, silk_lsb_iCDF, 8 ); } bit = abs_q & 1; ec_enc_icdf( psRangeEnc, bit, silk_lsb_iCDF, 8 ); } } } /****************/ /* Encode signs */ /****************/ silk_encode_signs( psRangeEnc, pulses, frame_length, signalType, quantOffsetType, sum_pulses ); RESTORE_STACK; } ================================================ FILE: deps/pjsip/third_party/opus/silk/errors.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_ERRORS_H #define SILK_ERRORS_H #ifdef __cplusplus extern "C" { #endif /******************/ /* Error messages */ /******************/ #define SILK_NO_ERROR 0 /**************************/ /* Encoder error messages */ /**************************/ /* Input length is not a multiple of 10 ms, or length is longer than the packet length */ #define SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES -101 /* Sampling frequency not 8000, 12000 or 16000 Hertz */ #define SILK_ENC_FS_NOT_SUPPORTED -102 /* Packet size not 10, 20, 40, or 60 ms */ #define SILK_ENC_PACKET_SIZE_NOT_SUPPORTED -103 /* Allocated payload buffer too short */ #define SILK_ENC_PAYLOAD_BUF_TOO_SHORT -104 /* Loss rate not between 0 and 100 percent */ #define SILK_ENC_INVALID_LOSS_RATE -105 /* Complexity setting not valid, use 0...10 */ #define SILK_ENC_INVALID_COMPLEXITY_SETTING -106 /* Inband FEC setting not valid, use 0 or 1 */ #define SILK_ENC_INVALID_INBAND_FEC_SETTING -107 /* DTX setting not valid, use 0 or 1 */ #define SILK_ENC_INVALID_DTX_SETTING -108 /* CBR setting not valid, use 0 or 1 */ #define SILK_ENC_INVALID_CBR_SETTING -109 /* Internal encoder error */ #define SILK_ENC_INTERNAL_ERROR -110 /* Internal encoder error */ #define SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR -111 /**************************/ /* Decoder error messages */ /**************************/ /* Output sampling frequency lower than internal decoded sampling frequency */ #define SILK_DEC_INVALID_SAMPLING_FREQUENCY -200 /* Payload size exceeded the maximum allowed 1024 bytes */ #define SILK_DEC_PAYLOAD_TOO_LARGE -201 /* Payload has bit errors */ #define SILK_DEC_PAYLOAD_ERROR -202 /* Payload has bit errors */ #define SILK_DEC_INVALID_FRAME_SIZE -203 #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/LTP_analysis_filter_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" void silk_LTP_analysis_filter_FIX( opus_int16 *LTP_res, /* O LTP residual signal of length MAX_NB_SUBFR * ( pre_length + subfr_length ) */ const opus_int16 *x, /* I Pointer to input signal with at least max( pitchL ) preceding samples */ const opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ],/* I LTP_ORDER LTP coefficients for each MAX_NB_SUBFR subframe */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lag, one for each subframe */ const opus_int32 invGains_Q16[ MAX_NB_SUBFR ], /* I Inverse quantization gains, one for each subframe */ const opus_int subfr_length, /* I Length of each subframe */ const opus_int nb_subfr, /* I Number of subframes */ const opus_int pre_length /* I Length of the preceding samples starting at &x[0] for each subframe */ ) { const opus_int16 *x_ptr, *x_lag_ptr; opus_int16 Btmp_Q14[ LTP_ORDER ]; opus_int16 *LTP_res_ptr; opus_int k, i; opus_int32 LTP_est; x_ptr = x; LTP_res_ptr = LTP_res; for( k = 0; k < nb_subfr; k++ ) { x_lag_ptr = x_ptr - pitchL[ k ]; Btmp_Q14[ 0 ] = LTPCoef_Q14[ k * LTP_ORDER ]; Btmp_Q14[ 1 ] = LTPCoef_Q14[ k * LTP_ORDER + 1 ]; Btmp_Q14[ 2 ] = LTPCoef_Q14[ k * LTP_ORDER + 2 ]; Btmp_Q14[ 3 ] = LTPCoef_Q14[ k * LTP_ORDER + 3 ]; Btmp_Q14[ 4 ] = LTPCoef_Q14[ k * LTP_ORDER + 4 ]; /* LTP analysis FIR filter */ for( i = 0; i < subfr_length + pre_length; i++ ) { LTP_res_ptr[ i ] = x_ptr[ i ]; /* Long-term prediction */ LTP_est = silk_SMULBB( x_lag_ptr[ LTP_ORDER / 2 ], Btmp_Q14[ 0 ] ); LTP_est = silk_SMLABB_ovflw( LTP_est, x_lag_ptr[ 1 ], Btmp_Q14[ 1 ] ); LTP_est = silk_SMLABB_ovflw( LTP_est, x_lag_ptr[ 0 ], Btmp_Q14[ 2 ] ); LTP_est = silk_SMLABB_ovflw( LTP_est, x_lag_ptr[ -1 ], Btmp_Q14[ 3 ] ); LTP_est = silk_SMLABB_ovflw( LTP_est, x_lag_ptr[ -2 ], Btmp_Q14[ 4 ] ); LTP_est = silk_RSHIFT_ROUND( LTP_est, 14 ); /* round and -> Q0*/ /* Subtract long-term prediction */ LTP_res_ptr[ i ] = (opus_int16)silk_SAT16( (opus_int32)x_ptr[ i ] - LTP_est ); /* Scale residual */ LTP_res_ptr[ i ] = silk_SMULWB( invGains_Q16[ k ], LTP_res_ptr[ i ] ); x_lag_ptr++; } /* Update pointers */ LTP_res_ptr += subfr_length + pre_length; x_ptr += subfr_length; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/LTP_scale_ctrl_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" /* Calculation of LTP state scaling */ void silk_LTP_scale_ctrl_FIX( silk_encoder_state_FIX *psEnc, /* I/O encoder state */ silk_encoder_control_FIX *psEncCtrl, /* I/O encoder control */ opus_int condCoding /* I The type of conditional coding to use */ ) { opus_int round_loss; if( condCoding == CODE_INDEPENDENTLY ) { /* Only scale if first frame in packet */ round_loss = psEnc->sCmn.PacketLoss_perc + psEnc->sCmn.nFramesPerPacket; psEnc->sCmn.indices.LTP_scaleIndex = (opus_int8)silk_LIMIT( silk_SMULWB( silk_SMULBB( round_loss, psEncCtrl->LTPredCodGain_Q7 ), SILK_FIX_CONST( 0.1, 9 ) ), 0, 2 ); } else { /* Default is minimum scaling */ psEnc->sCmn.indices.LTP_scaleIndex = 0; } psEncCtrl->LTP_scale_Q14 = silk_LTPScales_table_Q14[ psEnc->sCmn.indices.LTP_scaleIndex ]; } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/apply_sine_window_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" /* Apply sine window to signal vector. */ /* Window types: */ /* 1 -> sine window from 0 to pi/2 */ /* 2 -> sine window from pi/2 to pi */ /* Every other sample is linearly interpolated, for speed. */ /* Window length must be between 16 and 120 (incl) and a multiple of 4. */ /* Matlab code for table: for k=16:9*4:16+2*9*4, fprintf(' %7.d,', -round(65536*pi ./ (k:4:k+8*4))); fprintf('\n'); end */ static const opus_int16 freq_table_Q16[ 27 ] = { 12111, 9804, 8235, 7100, 6239, 5565, 5022, 4575, 4202, 3885, 3612, 3375, 3167, 2984, 2820, 2674, 2542, 2422, 2313, 2214, 2123, 2038, 1961, 1889, 1822, 1760, 1702, }; void silk_apply_sine_window( opus_int16 px_win[], /* O Pointer to windowed signal */ const opus_int16 px[], /* I Pointer to input signal */ const opus_int win_type, /* I Selects a window type */ const opus_int length /* I Window length, multiple of 4 */ ) { opus_int k, f_Q16, c_Q16; opus_int32 S0_Q16, S1_Q16; silk_assert( win_type == 1 || win_type == 2 ); /* Length must be in a range from 16 to 120 and a multiple of 4 */ silk_assert( length >= 16 && length <= 120 ); silk_assert( ( length & 3 ) == 0 ); /* Frequency */ k = ( length >> 2 ) - 4; silk_assert( k >= 0 && k <= 26 ); f_Q16 = (opus_int)freq_table_Q16[ k ]; /* Factor used for cosine approximation */ c_Q16 = silk_SMULWB( (opus_int32)f_Q16, -f_Q16 ); silk_assert( c_Q16 >= -32768 ); /* initialize state */ if( win_type == 1 ) { /* start from 0 */ S0_Q16 = 0; /* approximation of sin(f) */ S1_Q16 = f_Q16 + silk_RSHIFT( length, 3 ); } else { /* start from 1 */ S0_Q16 = ( (opus_int32)1 << 16 ); /* approximation of cos(f) */ S1_Q16 = ( (opus_int32)1 << 16 ) + silk_RSHIFT( c_Q16, 1 ) + silk_RSHIFT( length, 4 ); } /* Uses the recursive equation: sin(n*f) = 2 * cos(f) * sin((n-1)*f) - sin((n-2)*f) */ /* 4 samples at a time */ for( k = 0; k < length; k += 4 ) { px_win[ k ] = (opus_int16)silk_SMULWB( silk_RSHIFT( S0_Q16 + S1_Q16, 1 ), px[ k ] ); px_win[ k + 1 ] = (opus_int16)silk_SMULWB( S1_Q16, px[ k + 1] ); S0_Q16 = silk_SMULWB( S1_Q16, c_Q16 ) + silk_LSHIFT( S1_Q16, 1 ) - S0_Q16 + 1; S0_Q16 = silk_min( S0_Q16, ( (opus_int32)1 << 16 ) ); px_win[ k + 2 ] = (opus_int16)silk_SMULWB( silk_RSHIFT( S0_Q16 + S1_Q16, 1 ), px[ k + 2] ); px_win[ k + 3 ] = (opus_int16)silk_SMULWB( S0_Q16, px[ k + 3 ] ); S1_Q16 = silk_SMULWB( S0_Q16, c_Q16 ) + silk_LSHIFT( S0_Q16, 1 ) - S1_Q16; S1_Q16 = silk_min( S1_Q16, ( (opus_int32)1 << 16 ) ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/autocorr_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" #include "celt_lpc.h" /* Compute autocorrelation */ void silk_autocorr( opus_int32 *results, /* O Result (length correlationCount) */ opus_int *scale, /* O Scaling of the correlation vector */ const opus_int16 *inputData, /* I Input data to correlate */ const opus_int inputDataSize, /* I Length of input */ const opus_int correlationCount, /* I Number of correlation taps to compute */ int arch /* I Run-time architecture */ ) { opus_int corrCount; corrCount = silk_min_int( inputDataSize, correlationCount ); *scale = _celt_autocorr(inputData, results, NULL, 0, corrCount-1, inputDataSize, arch); } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/burg_modified_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" #include "define.h" #include "tuning_parameters.h" #include "pitch.h" #define MAX_FRAME_SIZE 384 /* subfr_length * nb_subfr = ( 0.005 * 16000 + 16 ) * 4 = 384 */ #define QA 25 #define N_BITS_HEAD_ROOM 2 #define MIN_RSHIFTS -16 #define MAX_RSHIFTS (32 - QA) /* Compute reflection coefficients from input signal */ void silk_burg_modified_c( opus_int32 *res_nrg, /* O Residual energy */ opus_int *res_nrg_Q, /* O Residual energy Q value */ opus_int32 A_Q16[], /* O Prediction coefficients (length order) */ const opus_int16 x[], /* I Input signal, length: nb_subfr * ( D + subfr_length ) */ const opus_int32 minInvGain_Q30, /* I Inverse of max prediction gain */ const opus_int subfr_length, /* I Input signal subframe length (incl. D preceding samples) */ const opus_int nb_subfr, /* I Number of subframes stacked in x */ const opus_int D, /* I Order */ int arch /* I Run-time architecture */ ) { opus_int k, n, s, lz, rshifts, reached_max_gain; opus_int32 C0, num, nrg, rc_Q31, invGain_Q30, Atmp_QA, Atmp1, tmp1, tmp2, x1, x2; const opus_int16 *x_ptr; opus_int32 C_first_row[ SILK_MAX_ORDER_LPC ]; opus_int32 C_last_row[ SILK_MAX_ORDER_LPC ]; opus_int32 Af_QA[ SILK_MAX_ORDER_LPC ]; opus_int32 CAf[ SILK_MAX_ORDER_LPC + 1 ]; opus_int32 CAb[ SILK_MAX_ORDER_LPC + 1 ]; opus_int32 xcorr[ SILK_MAX_ORDER_LPC ]; opus_int64 C0_64; silk_assert( subfr_length * nb_subfr <= MAX_FRAME_SIZE ); /* Compute autocorrelations, added over subframes */ C0_64 = silk_inner_prod16_aligned_64( x, x, subfr_length*nb_subfr, arch ); lz = silk_CLZ64(C0_64); rshifts = 32 + 1 + N_BITS_HEAD_ROOM - lz; if (rshifts > MAX_RSHIFTS) rshifts = MAX_RSHIFTS; if (rshifts < MIN_RSHIFTS) rshifts = MIN_RSHIFTS; if (rshifts > 0) { C0 = (opus_int32)silk_RSHIFT64(C0_64, rshifts ); } else { C0 = silk_LSHIFT32((opus_int32)C0_64, -rshifts ); } CAb[ 0 ] = CAf[ 0 ] = C0 + silk_SMMUL( SILK_FIX_CONST( FIND_LPC_COND_FAC, 32 ), C0 ) + 1; /* Q(-rshifts) */ silk_memset( C_first_row, 0, SILK_MAX_ORDER_LPC * sizeof( opus_int32 ) ); if( rshifts > 0 ) { for( s = 0; s < nb_subfr; s++ ) { x_ptr = x + s * subfr_length; for( n = 1; n < D + 1; n++ ) { C_first_row[ n - 1 ] += (opus_int32)silk_RSHIFT64( silk_inner_prod16_aligned_64( x_ptr, x_ptr + n, subfr_length - n, arch ), rshifts ); } } } else { for( s = 0; s < nb_subfr; s++ ) { int i; opus_int32 d; x_ptr = x + s * subfr_length; celt_pitch_xcorr(x_ptr, x_ptr + 1, xcorr, subfr_length - D, D, arch ); for( n = 1; n < D + 1; n++ ) { for ( i = n + subfr_length - D, d = 0; i < subfr_length; i++ ) d = MAC16_16( d, x_ptr[ i ], x_ptr[ i - n ] ); xcorr[ n - 1 ] += d; } for( n = 1; n < D + 1; n++ ) { C_first_row[ n - 1 ] += silk_LSHIFT32( xcorr[ n - 1 ], -rshifts ); } } } silk_memcpy( C_last_row, C_first_row, SILK_MAX_ORDER_LPC * sizeof( opus_int32 ) ); /* Initialize */ CAb[ 0 ] = CAf[ 0 ] = C0 + silk_SMMUL( SILK_FIX_CONST( FIND_LPC_COND_FAC, 32 ), C0 ) + 1; /* Q(-rshifts) */ invGain_Q30 = (opus_int32)1 << 30; reached_max_gain = 0; for( n = 0; n < D; n++ ) { /* Update first row of correlation matrix (without first element) */ /* Update last row of correlation matrix (without last element, stored in reversed order) */ /* Update C * Af */ /* Update C * flipud(Af) (stored in reversed order) */ if( rshifts > -2 ) { for( s = 0; s < nb_subfr; s++ ) { x_ptr = x + s * subfr_length; x1 = -silk_LSHIFT32( (opus_int32)x_ptr[ n ], 16 - rshifts ); /* Q(16-rshifts) */ x2 = -silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], 16 - rshifts ); /* Q(16-rshifts) */ tmp1 = silk_LSHIFT32( (opus_int32)x_ptr[ n ], QA - 16 ); /* Q(QA-16) */ tmp2 = silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], QA - 16 ); /* Q(QA-16) */ for( k = 0; k < n; k++ ) { C_first_row[ k ] = silk_SMLAWB( C_first_row[ k ], x1, x_ptr[ n - k - 1 ] ); /* Q( -rshifts ) */ C_last_row[ k ] = silk_SMLAWB( C_last_row[ k ], x2, x_ptr[ subfr_length - n + k ] ); /* Q( -rshifts ) */ Atmp_QA = Af_QA[ k ]; tmp1 = silk_SMLAWB( tmp1, Atmp_QA, x_ptr[ n - k - 1 ] ); /* Q(QA-16) */ tmp2 = silk_SMLAWB( tmp2, Atmp_QA, x_ptr[ subfr_length - n + k ] ); /* Q(QA-16) */ } tmp1 = silk_LSHIFT32( -tmp1, 32 - QA - rshifts ); /* Q(16-rshifts) */ tmp2 = silk_LSHIFT32( -tmp2, 32 - QA - rshifts ); /* Q(16-rshifts) */ for( k = 0; k <= n; k++ ) { CAf[ k ] = silk_SMLAWB( CAf[ k ], tmp1, x_ptr[ n - k ] ); /* Q( -rshift ) */ CAb[ k ] = silk_SMLAWB( CAb[ k ], tmp2, x_ptr[ subfr_length - n + k - 1 ] ); /* Q( -rshift ) */ } } } else { for( s = 0; s < nb_subfr; s++ ) { x_ptr = x + s * subfr_length; x1 = -silk_LSHIFT32( (opus_int32)x_ptr[ n ], -rshifts ); /* Q( -rshifts ) */ x2 = -silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], -rshifts ); /* Q( -rshifts ) */ tmp1 = silk_LSHIFT32( (opus_int32)x_ptr[ n ], 17 ); /* Q17 */ tmp2 = silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], 17 ); /* Q17 */ for( k = 0; k < n; k++ ) { C_first_row[ k ] = silk_MLA( C_first_row[ k ], x1, x_ptr[ n - k - 1 ] ); /* Q( -rshifts ) */ C_last_row[ k ] = silk_MLA( C_last_row[ k ], x2, x_ptr[ subfr_length - n + k ] ); /* Q( -rshifts ) */ Atmp1 = silk_RSHIFT_ROUND( Af_QA[ k ], QA - 17 ); /* Q17 */ tmp1 = silk_MLA( tmp1, x_ptr[ n - k - 1 ], Atmp1 ); /* Q17 */ tmp2 = silk_MLA( tmp2, x_ptr[ subfr_length - n + k ], Atmp1 ); /* Q17 */ } tmp1 = -tmp1; /* Q17 */ tmp2 = -tmp2; /* Q17 */ for( k = 0; k <= n; k++ ) { CAf[ k ] = silk_SMLAWW( CAf[ k ], tmp1, silk_LSHIFT32( (opus_int32)x_ptr[ n - k ], -rshifts - 1 ) ); /* Q( -rshift ) */ CAb[ k ] = silk_SMLAWW( CAb[ k ], tmp2, silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n + k - 1 ], -rshifts - 1 ) ); /* Q( -rshift ) */ } } } /* Calculate nominator and denominator for the next order reflection (parcor) coefficient */ tmp1 = C_first_row[ n ]; /* Q( -rshifts ) */ tmp2 = C_last_row[ n ]; /* Q( -rshifts ) */ num = 0; /* Q( -rshifts ) */ nrg = silk_ADD32( CAb[ 0 ], CAf[ 0 ] ); /* Q( 1-rshifts ) */ for( k = 0; k < n; k++ ) { Atmp_QA = Af_QA[ k ]; lz = silk_CLZ32( silk_abs( Atmp_QA ) ) - 1; lz = silk_min( 32 - QA, lz ); Atmp1 = silk_LSHIFT32( Atmp_QA, lz ); /* Q( QA + lz ) */ tmp1 = silk_ADD_LSHIFT32( tmp1, silk_SMMUL( C_last_row[ n - k - 1 ], Atmp1 ), 32 - QA - lz ); /* Q( -rshifts ) */ tmp2 = silk_ADD_LSHIFT32( tmp2, silk_SMMUL( C_first_row[ n - k - 1 ], Atmp1 ), 32 - QA - lz ); /* Q( -rshifts ) */ num = silk_ADD_LSHIFT32( num, silk_SMMUL( CAb[ n - k ], Atmp1 ), 32 - QA - lz ); /* Q( -rshifts ) */ nrg = silk_ADD_LSHIFT32( nrg, silk_SMMUL( silk_ADD32( CAb[ k + 1 ], CAf[ k + 1 ] ), Atmp1 ), 32 - QA - lz ); /* Q( 1-rshifts ) */ } CAf[ n + 1 ] = tmp1; /* Q( -rshifts ) */ CAb[ n + 1 ] = tmp2; /* Q( -rshifts ) */ num = silk_ADD32( num, tmp2 ); /* Q( -rshifts ) */ num = silk_LSHIFT32( -num, 1 ); /* Q( 1-rshifts ) */ /* Calculate the next order reflection (parcor) coefficient */ if( silk_abs( num ) < nrg ) { rc_Q31 = silk_DIV32_varQ( num, nrg, 31 ); } else { rc_Q31 = ( num > 0 ) ? silk_int32_MAX : silk_int32_MIN; } /* Update inverse prediction gain */ tmp1 = ( (opus_int32)1 << 30 ) - silk_SMMUL( rc_Q31, rc_Q31 ); tmp1 = silk_LSHIFT( silk_SMMUL( invGain_Q30, tmp1 ), 2 ); if( tmp1 <= minInvGain_Q30 ) { /* Max prediction gain exceeded; set reflection coefficient such that max prediction gain is exactly hit */ tmp2 = ( (opus_int32)1 << 30 ) - silk_DIV32_varQ( minInvGain_Q30, invGain_Q30, 30 ); /* Q30 */ rc_Q31 = silk_SQRT_APPROX( tmp2 ); /* Q15 */ /* Newton-Raphson iteration */ rc_Q31 = silk_RSHIFT32( rc_Q31 + silk_DIV32( tmp2, rc_Q31 ), 1 ); /* Q15 */ rc_Q31 = silk_LSHIFT32( rc_Q31, 16 ); /* Q31 */ if( num < 0 ) { /* Ensure adjusted reflection coefficients has the original sign */ rc_Q31 = -rc_Q31; } invGain_Q30 = minInvGain_Q30; reached_max_gain = 1; } else { invGain_Q30 = tmp1; } /* Update the AR coefficients */ for( k = 0; k < (n + 1) >> 1; k++ ) { tmp1 = Af_QA[ k ]; /* QA */ tmp2 = Af_QA[ n - k - 1 ]; /* QA */ Af_QA[ k ] = silk_ADD_LSHIFT32( tmp1, silk_SMMUL( tmp2, rc_Q31 ), 1 ); /* QA */ Af_QA[ n - k - 1 ] = silk_ADD_LSHIFT32( tmp2, silk_SMMUL( tmp1, rc_Q31 ), 1 ); /* QA */ } Af_QA[ n ] = silk_RSHIFT32( rc_Q31, 31 - QA ); /* QA */ if( reached_max_gain ) { /* Reached max prediction gain; set remaining coefficients to zero and exit loop */ for( k = n + 1; k < D; k++ ) { Af_QA[ k ] = 0; } break; } /* Update C * Af and C * Ab */ for( k = 0; k <= n + 1; k++ ) { tmp1 = CAf[ k ]; /* Q( -rshifts ) */ tmp2 = CAb[ n - k + 1 ]; /* Q( -rshifts ) */ CAf[ k ] = silk_ADD_LSHIFT32( tmp1, silk_SMMUL( tmp2, rc_Q31 ), 1 ); /* Q( -rshifts ) */ CAb[ n - k + 1 ] = silk_ADD_LSHIFT32( tmp2, silk_SMMUL( tmp1, rc_Q31 ), 1 ); /* Q( -rshifts ) */ } } if( reached_max_gain ) { for( k = 0; k < D; k++ ) { /* Scale coefficients */ A_Q16[ k ] = -silk_RSHIFT_ROUND( Af_QA[ k ], QA - 16 ); } /* Subtract energy of preceding samples from C0 */ if( rshifts > 0 ) { for( s = 0; s < nb_subfr; s++ ) { x_ptr = x + s * subfr_length; C0 -= (opus_int32)silk_RSHIFT64( silk_inner_prod16_aligned_64( x_ptr, x_ptr, D, arch ), rshifts ); } } else { for( s = 0; s < nb_subfr; s++ ) { x_ptr = x + s * subfr_length; C0 -= silk_LSHIFT32( silk_inner_prod_aligned( x_ptr, x_ptr, D, arch), -rshifts); } } /* Approximate residual energy */ *res_nrg = silk_LSHIFT( silk_SMMUL( invGain_Q30, C0 ), 2 ); *res_nrg_Q = -rshifts; } else { /* Return residual energy */ nrg = CAf[ 0 ]; /* Q( -rshifts ) */ tmp1 = (opus_int32)1 << 16; /* Q16 */ for( k = 0; k < D; k++ ) { Atmp1 = silk_RSHIFT_ROUND( Af_QA[ k ], QA - 16 ); /* Q16 */ nrg = silk_SMLAWW( nrg, CAf[ k + 1 ], Atmp1 ); /* Q( -rshifts ) */ tmp1 = silk_SMLAWW( tmp1, Atmp1, Atmp1 ); /* Q16 */ A_Q16[ k ] = -Atmp1; } *res_nrg = silk_SMLAWW( nrg, silk_SMMUL( SILK_FIX_CONST( FIND_LPC_COND_FAC, 32 ), C0 ), -tmp1 );/* Q( -rshifts ) */ *res_nrg_Q = -rshifts; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/corrMatrix_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /********************************************************************** * Correlation Matrix Computations for LS estimate. **********************************************************************/ #include "main_FIX.h" /* Calculates correlation vector X'*t */ void silk_corrVector_FIX( const opus_int16 *x, /* I x vector [L + order - 1] used to form data matrix X */ const opus_int16 *t, /* I Target vector [L] */ const opus_int L, /* I Length of vectors */ const opus_int order, /* I Max lag for correlation */ opus_int32 *Xt, /* O Pointer to X'*t correlation vector [order] */ const opus_int rshifts, /* I Right shifts of correlations */ int arch /* I Run-time architecture */ ) { opus_int lag, i; const opus_int16 *ptr1, *ptr2; opus_int32 inner_prod; ptr1 = &x[ order - 1 ]; /* Points to first sample of column 0 of X: X[:,0] */ ptr2 = t; /* Calculate X'*t */ if( rshifts > 0 ) { /* Right shifting used */ for( lag = 0; lag < order; lag++ ) { inner_prod = 0; for( i = 0; i < L; i++ ) { inner_prod += silk_RSHIFT32( silk_SMULBB( ptr1[ i ], ptr2[i] ), rshifts ); } Xt[ lag ] = inner_prod; /* X[:,lag]'*t */ ptr1--; /* Go to next column of X */ } } else { silk_assert( rshifts == 0 ); for( lag = 0; lag < order; lag++ ) { Xt[ lag ] = silk_inner_prod_aligned( ptr1, ptr2, L, arch ); /* X[:,lag]'*t */ ptr1--; /* Go to next column of X */ } } } /* Calculates correlation matrix X'*X */ void silk_corrMatrix_FIX( const opus_int16 *x, /* I x vector [L + order - 1] used to form data matrix X */ const opus_int L, /* I Length of vectors */ const opus_int order, /* I Max lag for correlation */ const opus_int head_room, /* I Desired headroom */ opus_int32 *XX, /* O Pointer to X'*X correlation matrix [ order x order ] */ opus_int *rshifts, /* I/O Right shifts of correlations */ int arch /* I Run-time architecture */ ) { opus_int i, j, lag, rshifts_local, head_room_rshifts; opus_int32 energy; const opus_int16 *ptr1, *ptr2; /* Calculate energy to find shift used to fit in 32 bits */ silk_sum_sqr_shift( &energy, &rshifts_local, x, L + order - 1 ); /* Add shifts to get the desired head room */ head_room_rshifts = silk_max( head_room - silk_CLZ32( energy ), 0 ); energy = silk_RSHIFT32( energy, head_room_rshifts ); rshifts_local += head_room_rshifts; /* Calculate energy of first column (0) of X: X[:,0]'*X[:,0] */ /* Remove contribution of first order - 1 samples */ for( i = 0; i < order - 1; i++ ) { energy -= silk_RSHIFT32( silk_SMULBB( x[ i ], x[ i ] ), rshifts_local ); } if( rshifts_local < *rshifts ) { /* Adjust energy */ energy = silk_RSHIFT32( energy, *rshifts - rshifts_local ); rshifts_local = *rshifts; } /* Calculate energy of remaining columns of X: X[:,j]'*X[:,j] */ /* Fill out the diagonal of the correlation matrix */ matrix_ptr( XX, 0, 0, order ) = energy; ptr1 = &x[ order - 1 ]; /* First sample of column 0 of X */ for( j = 1; j < order; j++ ) { energy = silk_SUB32( energy, silk_RSHIFT32( silk_SMULBB( ptr1[ L - j ], ptr1[ L - j ] ), rshifts_local ) ); energy = silk_ADD32( energy, silk_RSHIFT32( silk_SMULBB( ptr1[ -j ], ptr1[ -j ] ), rshifts_local ) ); matrix_ptr( XX, j, j, order ) = energy; } ptr2 = &x[ order - 2 ]; /* First sample of column 1 of X */ /* Calculate the remaining elements of the correlation matrix */ if( rshifts_local > 0 ) { /* Right shifting used */ for( lag = 1; lag < order; lag++ ) { /* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */ energy = 0; for( i = 0; i < L; i++ ) { energy += silk_RSHIFT32( silk_SMULBB( ptr1[ i ], ptr2[i] ), rshifts_local ); } /* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */ matrix_ptr( XX, lag, 0, order ) = energy; matrix_ptr( XX, 0, lag, order ) = energy; for( j = 1; j < ( order - lag ); j++ ) { energy = silk_SUB32( energy, silk_RSHIFT32( silk_SMULBB( ptr1[ L - j ], ptr2[ L - j ] ), rshifts_local ) ); energy = silk_ADD32( energy, silk_RSHIFT32( silk_SMULBB( ptr1[ -j ], ptr2[ -j ] ), rshifts_local ) ); matrix_ptr( XX, lag + j, j, order ) = energy; matrix_ptr( XX, j, lag + j, order ) = energy; } ptr2--; /* Update pointer to first sample of next column (lag) in X */ } } else { for( lag = 1; lag < order; lag++ ) { /* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */ energy = silk_inner_prod_aligned( ptr1, ptr2, L, arch ); matrix_ptr( XX, lag, 0, order ) = energy; matrix_ptr( XX, 0, lag, order ) = energy; /* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */ for( j = 1; j < ( order - lag ); j++ ) { energy = silk_SUB32( energy, silk_SMULBB( ptr1[ L - j ], ptr2[ L - j ] ) ); energy = silk_SMLABB( energy, ptr1[ -j ], ptr2[ -j ] ); matrix_ptr( XX, lag + j, j, order ) = energy; matrix_ptr( XX, j, lag + j, order ) = energy; } ptr2--;/* Update pointer to first sample of next column (lag) in X */ } } *rshifts = rshifts_local; } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/encode_frame_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" #include "stack_alloc.h" #include "tuning_parameters.h" /* Low Bitrate Redundancy (LBRR) encoding. Reuse all parameters but encode with lower bitrate */ static OPUS_INLINE void silk_LBRR_encode_FIX( silk_encoder_state_FIX *psEnc, /* I/O Pointer to Silk FIX encoder state */ silk_encoder_control_FIX *psEncCtrl, /* I/O Pointer to Silk FIX encoder control struct */ const opus_int32 xfw_Q3[], /* I Input signal */ opus_int condCoding /* I The type of conditional coding used so far for this frame */ ); void silk_encode_do_VAD_FIX( silk_encoder_state_FIX *psEnc /* I/O Pointer to Silk FIX encoder state */ ) { /****************************/ /* Voice Activity Detection */ /****************************/ silk_VAD_GetSA_Q8( &psEnc->sCmn, psEnc->sCmn.inputBuf + 1, psEnc->sCmn.arch ); /**************************************************/ /* Convert speech activity into VAD and DTX flags */ /**************************************************/ if( psEnc->sCmn.speech_activity_Q8 < SILK_FIX_CONST( SPEECH_ACTIVITY_DTX_THRES, 8 ) ) { psEnc->sCmn.indices.signalType = TYPE_NO_VOICE_ACTIVITY; psEnc->sCmn.noSpeechCounter++; if( psEnc->sCmn.noSpeechCounter < NB_SPEECH_FRAMES_BEFORE_DTX ) { psEnc->sCmn.inDTX = 0; } else if( psEnc->sCmn.noSpeechCounter > MAX_CONSECUTIVE_DTX + NB_SPEECH_FRAMES_BEFORE_DTX ) { psEnc->sCmn.noSpeechCounter = NB_SPEECH_FRAMES_BEFORE_DTX; psEnc->sCmn.inDTX = 0; } psEnc->sCmn.VAD_flags[ psEnc->sCmn.nFramesEncoded ] = 0; } else { psEnc->sCmn.noSpeechCounter = 0; psEnc->sCmn.inDTX = 0; psEnc->sCmn.indices.signalType = TYPE_UNVOICED; psEnc->sCmn.VAD_flags[ psEnc->sCmn.nFramesEncoded ] = 1; } } /****************/ /* Encode frame */ /****************/ opus_int silk_encode_frame_FIX( silk_encoder_state_FIX *psEnc, /* I/O Pointer to Silk FIX encoder state */ opus_int32 *pnBytesOut, /* O Pointer to number of payload bytes; */ ec_enc *psRangeEnc, /* I/O compressor data structure */ opus_int condCoding, /* I The type of conditional coding to use */ opus_int maxBits, /* I If > 0: maximum number of output bits */ opus_int useCBR /* I Flag to force constant-bitrate operation */ ) { silk_encoder_control_FIX sEncCtrl; opus_int i, iter, maxIter, found_upper, found_lower, ret = 0; opus_int16 *x_frame; ec_enc sRangeEnc_copy, sRangeEnc_copy2; silk_nsq_state sNSQ_copy, sNSQ_copy2; opus_int32 seed_copy, nBits, nBits_lower, nBits_upper, gainMult_lower, gainMult_upper; opus_int32 gainsID, gainsID_lower, gainsID_upper; opus_int16 gainMult_Q8; opus_int16 ec_prevLagIndex_copy; opus_int ec_prevSignalType_copy; opus_int8 LastGainIndex_copy2; SAVE_STACK; /* This is totally unnecessary but many compilers (including gcc) are too dumb to realise it */ LastGainIndex_copy2 = nBits_lower = nBits_upper = gainMult_lower = gainMult_upper = 0; psEnc->sCmn.indices.Seed = psEnc->sCmn.frameCounter++ & 3; /**************************************************************/ /* Set up Input Pointers, and insert frame in input buffer */ /*************************************************************/ /* start of frame to encode */ x_frame = psEnc->x_buf + psEnc->sCmn.ltp_mem_length; /***************************************/ /* Ensure smooth bandwidth transitions */ /***************************************/ silk_LP_variable_cutoff( &psEnc->sCmn.sLP, psEnc->sCmn.inputBuf + 1, psEnc->sCmn.frame_length ); /*******************************************/ /* Copy new frame to front of input buffer */ /*******************************************/ silk_memcpy( x_frame + LA_SHAPE_MS * psEnc->sCmn.fs_kHz, psEnc->sCmn.inputBuf + 1, psEnc->sCmn.frame_length * sizeof( opus_int16 ) ); if( !psEnc->sCmn.prefillFlag ) { VARDECL( opus_int32, xfw_Q3 ); VARDECL( opus_int16, res_pitch ); VARDECL( opus_uint8, ec_buf_copy ); opus_int16 *res_pitch_frame; ALLOC( res_pitch, psEnc->sCmn.la_pitch + psEnc->sCmn.frame_length + psEnc->sCmn.ltp_mem_length, opus_int16 ); /* start of pitch LPC residual frame */ res_pitch_frame = res_pitch + psEnc->sCmn.ltp_mem_length; /*****************************************/ /* Find pitch lags, initial LPC analysis */ /*****************************************/ silk_find_pitch_lags_FIX( psEnc, &sEncCtrl, res_pitch, x_frame, psEnc->sCmn.arch ); /************************/ /* Noise shape analysis */ /************************/ silk_noise_shape_analysis_FIX( psEnc, &sEncCtrl, res_pitch_frame, x_frame, psEnc->sCmn.arch ); /***************************************************/ /* Find linear prediction coefficients (LPC + LTP) */ /***************************************************/ silk_find_pred_coefs_FIX( psEnc, &sEncCtrl, res_pitch, x_frame, condCoding ); /****************************************/ /* Process gains */ /****************************************/ silk_process_gains_FIX( psEnc, &sEncCtrl, condCoding ); /*****************************************/ /* Prefiltering for noise shaper */ /*****************************************/ ALLOC( xfw_Q3, psEnc->sCmn.frame_length, opus_int32 ); silk_prefilter_FIX( psEnc, &sEncCtrl, xfw_Q3, x_frame ); /****************************************/ /* Low Bitrate Redundant Encoding */ /****************************************/ silk_LBRR_encode_FIX( psEnc, &sEncCtrl, xfw_Q3, condCoding ); /* Loop over quantizer and entropy coding to control bitrate */ maxIter = 6; gainMult_Q8 = SILK_FIX_CONST( 1, 8 ); found_lower = 0; found_upper = 0; gainsID = silk_gains_ID( psEnc->sCmn.indices.GainsIndices, psEnc->sCmn.nb_subfr ); gainsID_lower = -1; gainsID_upper = -1; /* Copy part of the input state */ silk_memcpy( &sRangeEnc_copy, psRangeEnc, sizeof( ec_enc ) ); silk_memcpy( &sNSQ_copy, &psEnc->sCmn.sNSQ, sizeof( silk_nsq_state ) ); seed_copy = psEnc->sCmn.indices.Seed; ec_prevLagIndex_copy = psEnc->sCmn.ec_prevLagIndex; ec_prevSignalType_copy = psEnc->sCmn.ec_prevSignalType; ALLOC( ec_buf_copy, 1275, opus_uint8 ); for( iter = 0; ; iter++ ) { if( gainsID == gainsID_lower ) { nBits = nBits_lower; } else if( gainsID == gainsID_upper ) { nBits = nBits_upper; } else { /* Restore part of the input state */ if( iter > 0 ) { silk_memcpy( psRangeEnc, &sRangeEnc_copy, sizeof( ec_enc ) ); silk_memcpy( &psEnc->sCmn.sNSQ, &sNSQ_copy, sizeof( silk_nsq_state ) ); psEnc->sCmn.indices.Seed = seed_copy; psEnc->sCmn.ec_prevLagIndex = ec_prevLagIndex_copy; psEnc->sCmn.ec_prevSignalType = ec_prevSignalType_copy; } /*****************************************/ /* Noise shaping quantization */ /*****************************************/ if( psEnc->sCmn.nStatesDelayedDecision > 1 || psEnc->sCmn.warping_Q16 > 0 ) { silk_NSQ_del_dec( &psEnc->sCmn, &psEnc->sCmn.sNSQ, &psEnc->sCmn.indices, xfw_Q3, psEnc->sCmn.pulses, sEncCtrl.PredCoef_Q12[ 0 ], sEncCtrl.LTPCoef_Q14, sEncCtrl.AR2_Q13, sEncCtrl.HarmShapeGain_Q14, sEncCtrl.Tilt_Q14, sEncCtrl.LF_shp_Q14, sEncCtrl.Gains_Q16, sEncCtrl.pitchL, sEncCtrl.Lambda_Q10, sEncCtrl.LTP_scale_Q14, psEnc->sCmn.arch ); } else { silk_NSQ( &psEnc->sCmn, &psEnc->sCmn.sNSQ, &psEnc->sCmn.indices, xfw_Q3, psEnc->sCmn.pulses, sEncCtrl.PredCoef_Q12[ 0 ], sEncCtrl.LTPCoef_Q14, sEncCtrl.AR2_Q13, sEncCtrl.HarmShapeGain_Q14, sEncCtrl.Tilt_Q14, sEncCtrl.LF_shp_Q14, sEncCtrl.Gains_Q16, sEncCtrl.pitchL, sEncCtrl.Lambda_Q10, sEncCtrl.LTP_scale_Q14, psEnc->sCmn.arch); } /****************************************/ /* Encode Parameters */ /****************************************/ silk_encode_indices( &psEnc->sCmn, psRangeEnc, psEnc->sCmn.nFramesEncoded, 0, condCoding ); /****************************************/ /* Encode Excitation Signal */ /****************************************/ silk_encode_pulses( psRangeEnc, psEnc->sCmn.indices.signalType, psEnc->sCmn.indices.quantOffsetType, psEnc->sCmn.pulses, psEnc->sCmn.frame_length ); nBits = ec_tell( psRangeEnc ); if( useCBR == 0 && iter == 0 && nBits <= maxBits ) { break; } } if( iter == maxIter ) { if( found_lower && ( gainsID == gainsID_lower || nBits > maxBits ) ) { /* Restore output state from earlier iteration that did meet the bitrate budget */ silk_memcpy( psRangeEnc, &sRangeEnc_copy2, sizeof( ec_enc ) ); silk_assert( sRangeEnc_copy2.offs <= 1275 ); silk_memcpy( psRangeEnc->buf, ec_buf_copy, sRangeEnc_copy2.offs ); silk_memcpy( &psEnc->sCmn.sNSQ, &sNSQ_copy2, sizeof( silk_nsq_state ) ); psEnc->sShape.LastGainIndex = LastGainIndex_copy2; } break; } if( nBits > maxBits ) { if( found_lower == 0 && iter >= 2 ) { /* Adjust the quantizer's rate/distortion tradeoff and discard previous "upper" results */ sEncCtrl.Lambda_Q10 = silk_ADD_RSHIFT32( sEncCtrl.Lambda_Q10, sEncCtrl.Lambda_Q10, 1 ); found_upper = 0; gainsID_upper = -1; } else { found_upper = 1; nBits_upper = nBits; gainMult_upper = gainMult_Q8; gainsID_upper = gainsID; } } else if( nBits < maxBits - 5 ) { found_lower = 1; nBits_lower = nBits; gainMult_lower = gainMult_Q8; if( gainsID != gainsID_lower ) { gainsID_lower = gainsID; /* Copy part of the output state */ silk_memcpy( &sRangeEnc_copy2, psRangeEnc, sizeof( ec_enc ) ); silk_assert( psRangeEnc->offs <= 1275 ); silk_memcpy( ec_buf_copy, psRangeEnc->buf, psRangeEnc->offs ); silk_memcpy( &sNSQ_copy2, &psEnc->sCmn.sNSQ, sizeof( silk_nsq_state ) ); LastGainIndex_copy2 = psEnc->sShape.LastGainIndex; } } else { /* Within 5 bits of budget: close enough */ break; } if( ( found_lower & found_upper ) == 0 ) { /* Adjust gain according to high-rate rate/distortion curve */ opus_int32 gain_factor_Q16; gain_factor_Q16 = silk_log2lin( silk_LSHIFT( nBits - maxBits, 7 ) / psEnc->sCmn.frame_length + SILK_FIX_CONST( 16, 7 ) ); gain_factor_Q16 = silk_min_32( gain_factor_Q16, SILK_FIX_CONST( 2, 16 ) ); if( nBits > maxBits ) { gain_factor_Q16 = silk_max_32( gain_factor_Q16, SILK_FIX_CONST( 1.3, 16 ) ); } gainMult_Q8 = silk_SMULWB( gain_factor_Q16, gainMult_Q8 ); } else { /* Adjust gain by interpolating */ gainMult_Q8 = gainMult_lower + silk_DIV32_16( silk_MUL( gainMult_upper - gainMult_lower, maxBits - nBits_lower ), nBits_upper - nBits_lower ); /* New gain multplier must be between 25% and 75% of old range (note that gainMult_upper < gainMult_lower) */ if( gainMult_Q8 > silk_ADD_RSHIFT32( gainMult_lower, gainMult_upper - gainMult_lower, 2 ) ) { gainMult_Q8 = silk_ADD_RSHIFT32( gainMult_lower, gainMult_upper - gainMult_lower, 2 ); } else if( gainMult_Q8 < silk_SUB_RSHIFT32( gainMult_upper, gainMult_upper - gainMult_lower, 2 ) ) { gainMult_Q8 = silk_SUB_RSHIFT32( gainMult_upper, gainMult_upper - gainMult_lower, 2 ); } } for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) { sEncCtrl.Gains_Q16[ i ] = silk_LSHIFT_SAT32( silk_SMULWB( sEncCtrl.GainsUnq_Q16[ i ], gainMult_Q8 ), 8 ); } /* Quantize gains */ psEnc->sShape.LastGainIndex = sEncCtrl.lastGainIndexPrev; silk_gains_quant( psEnc->sCmn.indices.GainsIndices, sEncCtrl.Gains_Q16, &psEnc->sShape.LastGainIndex, condCoding == CODE_CONDITIONALLY, psEnc->sCmn.nb_subfr ); /* Unique identifier of gains vector */ gainsID = silk_gains_ID( psEnc->sCmn.indices.GainsIndices, psEnc->sCmn.nb_subfr ); } } /* Update input buffer */ silk_memmove( psEnc->x_buf, &psEnc->x_buf[ psEnc->sCmn.frame_length ], ( psEnc->sCmn.ltp_mem_length + LA_SHAPE_MS * psEnc->sCmn.fs_kHz ) * sizeof( opus_int16 ) ); /* Exit without entropy coding */ if( psEnc->sCmn.prefillFlag ) { /* No payload */ *pnBytesOut = 0; RESTORE_STACK; return ret; } /* Parameters needed for next frame */ psEnc->sCmn.prevLag = sEncCtrl.pitchL[ psEnc->sCmn.nb_subfr - 1 ]; psEnc->sCmn.prevSignalType = psEnc->sCmn.indices.signalType; /****************************************/ /* Finalize payload */ /****************************************/ psEnc->sCmn.first_frame_after_reset = 0; /* Payload size */ *pnBytesOut = silk_RSHIFT( ec_tell( psRangeEnc ) + 7, 3 ); RESTORE_STACK; return ret; } /* Low-Bitrate Redundancy (LBRR) encoding. Reuse all parameters but encode excitation at lower bitrate */ static OPUS_INLINE void silk_LBRR_encode_FIX( silk_encoder_state_FIX *psEnc, /* I/O Pointer to Silk FIX encoder state */ silk_encoder_control_FIX *psEncCtrl, /* I/O Pointer to Silk FIX encoder control struct */ const opus_int32 xfw_Q3[], /* I Input signal */ opus_int condCoding /* I The type of conditional coding used so far for this frame */ ) { opus_int32 TempGains_Q16[ MAX_NB_SUBFR ]; SideInfoIndices *psIndices_LBRR = &psEnc->sCmn.indices_LBRR[ psEnc->sCmn.nFramesEncoded ]; silk_nsq_state sNSQ_LBRR; /*******************************************/ /* Control use of inband LBRR */ /*******************************************/ if( psEnc->sCmn.LBRR_enabled && psEnc->sCmn.speech_activity_Q8 > SILK_FIX_CONST( LBRR_SPEECH_ACTIVITY_THRES, 8 ) ) { psEnc->sCmn.LBRR_flags[ psEnc->sCmn.nFramesEncoded ] = 1; /* Copy noise shaping quantizer state and quantization indices from regular encoding */ silk_memcpy( &sNSQ_LBRR, &psEnc->sCmn.sNSQ, sizeof( silk_nsq_state ) ); silk_memcpy( psIndices_LBRR, &psEnc->sCmn.indices, sizeof( SideInfoIndices ) ); /* Save original gains */ silk_memcpy( TempGains_Q16, psEncCtrl->Gains_Q16, psEnc->sCmn.nb_subfr * sizeof( opus_int32 ) ); if( psEnc->sCmn.nFramesEncoded == 0 || psEnc->sCmn.LBRR_flags[ psEnc->sCmn.nFramesEncoded - 1 ] == 0 ) { /* First frame in packet or previous frame not LBRR coded */ psEnc->sCmn.LBRRprevLastGainIndex = psEnc->sShape.LastGainIndex; /* Increase Gains to get target LBRR rate */ psIndices_LBRR->GainsIndices[ 0 ] = psIndices_LBRR->GainsIndices[ 0 ] + psEnc->sCmn.LBRR_GainIncreases; psIndices_LBRR->GainsIndices[ 0 ] = silk_min_int( psIndices_LBRR->GainsIndices[ 0 ], N_LEVELS_QGAIN - 1 ); } /* Decode to get gains in sync with decoder */ /* Overwrite unquantized gains with quantized gains */ silk_gains_dequant( psEncCtrl->Gains_Q16, psIndices_LBRR->GainsIndices, &psEnc->sCmn.LBRRprevLastGainIndex, condCoding == CODE_CONDITIONALLY, psEnc->sCmn.nb_subfr ); /*****************************************/ /* Noise shaping quantization */ /*****************************************/ if( psEnc->sCmn.nStatesDelayedDecision > 1 || psEnc->sCmn.warping_Q16 > 0 ) { silk_NSQ_del_dec( &psEnc->sCmn, &sNSQ_LBRR, psIndices_LBRR, xfw_Q3, psEnc->sCmn.pulses_LBRR[ psEnc->sCmn.nFramesEncoded ], psEncCtrl->PredCoef_Q12[ 0 ], psEncCtrl->LTPCoef_Q14, psEncCtrl->AR2_Q13, psEncCtrl->HarmShapeGain_Q14, psEncCtrl->Tilt_Q14, psEncCtrl->LF_shp_Q14, psEncCtrl->Gains_Q16, psEncCtrl->pitchL, psEncCtrl->Lambda_Q10, psEncCtrl->LTP_scale_Q14, psEnc->sCmn.arch ); } else { silk_NSQ( &psEnc->sCmn, &sNSQ_LBRR, psIndices_LBRR, xfw_Q3, psEnc->sCmn.pulses_LBRR[ psEnc->sCmn.nFramesEncoded ], psEncCtrl->PredCoef_Q12[ 0 ], psEncCtrl->LTPCoef_Q14, psEncCtrl->AR2_Q13, psEncCtrl->HarmShapeGain_Q14, psEncCtrl->Tilt_Q14, psEncCtrl->LF_shp_Q14, psEncCtrl->Gains_Q16, psEncCtrl->pitchL, psEncCtrl->Lambda_Q10, psEncCtrl->LTP_scale_Q14, psEnc->sCmn.arch ); } /* Restore original gains */ silk_memcpy( psEncCtrl->Gains_Q16, TempGains_Q16, psEnc->sCmn.nb_subfr * sizeof( opus_int32 ) ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/find_LPC_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" #include "stack_alloc.h" #include "tuning_parameters.h" /* Finds LPC vector from correlations, and converts to NLSF */ void silk_find_LPC_FIX( silk_encoder_state *psEncC, /* I/O Encoder state */ opus_int16 NLSF_Q15[], /* O NLSFs */ const opus_int16 x[], /* I Input signal */ const opus_int32 minInvGain_Q30 /* I Inverse of max prediction gain */ ) { opus_int k, subfr_length; opus_int32 a_Q16[ MAX_LPC_ORDER ]; opus_int isInterpLower, shift; opus_int32 res_nrg0, res_nrg1; opus_int rshift0, rshift1; /* Used only for LSF interpolation */ opus_int32 a_tmp_Q16[ MAX_LPC_ORDER ], res_nrg_interp, res_nrg, res_tmp_nrg; opus_int res_nrg_interp_Q, res_nrg_Q, res_tmp_nrg_Q; opus_int16 a_tmp_Q12[ MAX_LPC_ORDER ]; opus_int16 NLSF0_Q15[ MAX_LPC_ORDER ]; SAVE_STACK; subfr_length = psEncC->subfr_length + psEncC->predictLPCOrder; /* Default: no interpolation */ psEncC->indices.NLSFInterpCoef_Q2 = 4; /* Burg AR analysis for the full frame */ silk_burg_modified( &res_nrg, &res_nrg_Q, a_Q16, x, minInvGain_Q30, subfr_length, psEncC->nb_subfr, psEncC->predictLPCOrder, psEncC->arch ); if( psEncC->useInterpolatedNLSFs && !psEncC->first_frame_after_reset && psEncC->nb_subfr == MAX_NB_SUBFR ) { VARDECL( opus_int16, LPC_res ); /* Optimal solution for last 10 ms */ silk_burg_modified( &res_tmp_nrg, &res_tmp_nrg_Q, a_tmp_Q16, x + 2 * subfr_length, minInvGain_Q30, subfr_length, 2, psEncC->predictLPCOrder, psEncC->arch ); /* subtract residual energy here, as that's easier than adding it to the */ /* residual energy of the first 10 ms in each iteration of the search below */ shift = res_tmp_nrg_Q - res_nrg_Q; if( shift >= 0 ) { if( shift < 32 ) { res_nrg = res_nrg - silk_RSHIFT( res_tmp_nrg, shift ); } } else { silk_assert( shift > -32 ); res_nrg = silk_RSHIFT( res_nrg, -shift ) - res_tmp_nrg; res_nrg_Q = res_tmp_nrg_Q; } /* Convert to NLSFs */ silk_A2NLSF( NLSF_Q15, a_tmp_Q16, psEncC->predictLPCOrder ); ALLOC( LPC_res, 2 * subfr_length, opus_int16 ); /* Search over interpolation indices to find the one with lowest residual energy */ for( k = 3; k >= 0; k-- ) { /* Interpolate NLSFs for first half */ silk_interpolate( NLSF0_Q15, psEncC->prev_NLSFq_Q15, NLSF_Q15, k, psEncC->predictLPCOrder ); /* Convert to LPC for residual energy evaluation */ silk_NLSF2A( a_tmp_Q12, NLSF0_Q15, psEncC->predictLPCOrder ); /* Calculate residual energy with NLSF interpolation */ silk_LPC_analysis_filter( LPC_res, x, a_tmp_Q12, 2 * subfr_length, psEncC->predictLPCOrder, psEncC->arch ); silk_sum_sqr_shift( &res_nrg0, &rshift0, LPC_res + psEncC->predictLPCOrder, subfr_length - psEncC->predictLPCOrder ); silk_sum_sqr_shift( &res_nrg1, &rshift1, LPC_res + psEncC->predictLPCOrder + subfr_length, subfr_length - psEncC->predictLPCOrder ); /* Add subframe energies from first half frame */ shift = rshift0 - rshift1; if( shift >= 0 ) { res_nrg1 = silk_RSHIFT( res_nrg1, shift ); res_nrg_interp_Q = -rshift0; } else { res_nrg0 = silk_RSHIFT( res_nrg0, -shift ); res_nrg_interp_Q = -rshift1; } res_nrg_interp = silk_ADD32( res_nrg0, res_nrg1 ); /* Compare with first half energy without NLSF interpolation, or best interpolated value so far */ shift = res_nrg_interp_Q - res_nrg_Q; if( shift >= 0 ) { if( silk_RSHIFT( res_nrg_interp, shift ) < res_nrg ) { isInterpLower = silk_TRUE; } else { isInterpLower = silk_FALSE; } } else { if( -shift < 32 ) { if( res_nrg_interp < silk_RSHIFT( res_nrg, -shift ) ) { isInterpLower = silk_TRUE; } else { isInterpLower = silk_FALSE; } } else { isInterpLower = silk_FALSE; } } /* Determine whether current interpolated NLSFs are best so far */ if( isInterpLower == silk_TRUE ) { /* Interpolation has lower residual energy */ res_nrg = res_nrg_interp; res_nrg_Q = res_nrg_interp_Q; psEncC->indices.NLSFInterpCoef_Q2 = (opus_int8)k; } } } if( psEncC->indices.NLSFInterpCoef_Q2 == 4 ) { /* NLSF interpolation is currently inactive, calculate NLSFs from full frame AR coefficients */ silk_A2NLSF( NLSF_Q15, a_Q16, psEncC->predictLPCOrder ); } silk_assert( psEncC->indices.NLSFInterpCoef_Q2 == 4 || ( psEncC->useInterpolatedNLSFs && !psEncC->first_frame_after_reset && psEncC->nb_subfr == MAX_NB_SUBFR ) ); RESTORE_STACK; } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/find_LTP_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" #include "tuning_parameters.h" /* Head room for correlations */ #define LTP_CORRS_HEAD_ROOM 2 void silk_fit_LTP( opus_int32 LTP_coefs_Q16[ LTP_ORDER ], opus_int16 LTP_coefs_Q14[ LTP_ORDER ] ); void silk_find_LTP_FIX( opus_int16 b_Q14[ MAX_NB_SUBFR * LTP_ORDER ], /* O LTP coefs */ opus_int32 WLTP[ MAX_NB_SUBFR * LTP_ORDER * LTP_ORDER ], /* O Weight for LTP quantization */ opus_int *LTPredCodGain_Q7, /* O LTP coding gain */ const opus_int16 r_lpc[], /* I residual signal after LPC signal + state for first 10 ms */ const opus_int lag[ MAX_NB_SUBFR ], /* I LTP lags */ const opus_int32 Wght_Q15[ MAX_NB_SUBFR ], /* I weights */ const opus_int subfr_length, /* I subframe length */ const opus_int nb_subfr, /* I number of subframes */ const opus_int mem_offset, /* I number of samples in LTP memory */ opus_int corr_rshifts[ MAX_NB_SUBFR ], /* O right shifts applied to correlations */ int arch /* I Run-time architecture */ ) { opus_int i, k, lshift; const opus_int16 *r_ptr, *lag_ptr; opus_int16 *b_Q14_ptr; opus_int32 regu; opus_int32 *WLTP_ptr; opus_int32 b_Q16[ LTP_ORDER ], delta_b_Q14[ LTP_ORDER ], d_Q14[ MAX_NB_SUBFR ], nrg[ MAX_NB_SUBFR ], g_Q26; opus_int32 w[ MAX_NB_SUBFR ], WLTP_max, max_abs_d_Q14, max_w_bits; opus_int32 temp32, denom32; opus_int extra_shifts; opus_int rr_shifts, maxRshifts, maxRshifts_wxtra, LZs; opus_int32 LPC_res_nrg, LPC_LTP_res_nrg, div_Q16; opus_int32 Rr[ LTP_ORDER ], rr[ MAX_NB_SUBFR ]; opus_int32 wd, m_Q12; b_Q14_ptr = b_Q14; WLTP_ptr = WLTP; r_ptr = &r_lpc[ mem_offset ]; for( k = 0; k < nb_subfr; k++ ) { lag_ptr = r_ptr - ( lag[ k ] + LTP_ORDER / 2 ); silk_sum_sqr_shift( &rr[ k ], &rr_shifts, r_ptr, subfr_length ); /* rr[ k ] in Q( -rr_shifts ) */ /* Assure headroom */ LZs = silk_CLZ32( rr[k] ); if( LZs < LTP_CORRS_HEAD_ROOM ) { rr[ k ] = silk_RSHIFT_ROUND( rr[ k ], LTP_CORRS_HEAD_ROOM - LZs ); rr_shifts += ( LTP_CORRS_HEAD_ROOM - LZs ); } corr_rshifts[ k ] = rr_shifts; silk_corrMatrix_FIX( lag_ptr, subfr_length, LTP_ORDER, LTP_CORRS_HEAD_ROOM, WLTP_ptr, &corr_rshifts[ k ], arch ); /* WLTP_fix_ptr in Q( -corr_rshifts[ k ] ) */ /* The correlation vector always has lower max abs value than rr and/or RR so head room is assured */ silk_corrVector_FIX( lag_ptr, r_ptr, subfr_length, LTP_ORDER, Rr, corr_rshifts[ k ], arch ); /* Rr_fix_ptr in Q( -corr_rshifts[ k ] ) */ if( corr_rshifts[ k ] > rr_shifts ) { rr[ k ] = silk_RSHIFT( rr[ k ], corr_rshifts[ k ] - rr_shifts ); /* rr[ k ] in Q( -corr_rshifts[ k ] ) */ } silk_assert( rr[ k ] >= 0 ); regu = 1; regu = silk_SMLAWB( regu, rr[ k ], SILK_FIX_CONST( LTP_DAMPING/3, 16 ) ); regu = silk_SMLAWB( regu, matrix_ptr( WLTP_ptr, 0, 0, LTP_ORDER ), SILK_FIX_CONST( LTP_DAMPING/3, 16 ) ); regu = silk_SMLAWB( regu, matrix_ptr( WLTP_ptr, LTP_ORDER-1, LTP_ORDER-1, LTP_ORDER ), SILK_FIX_CONST( LTP_DAMPING/3, 16 ) ); silk_regularize_correlations_FIX( WLTP_ptr, &rr[k], regu, LTP_ORDER ); silk_solve_LDL_FIX( WLTP_ptr, LTP_ORDER, Rr, b_Q16 ); /* WLTP_fix_ptr and Rr_fix_ptr both in Q(-corr_rshifts[k]) */ /* Limit and store in Q14 */ silk_fit_LTP( b_Q16, b_Q14_ptr ); /* Calculate residual energy */ nrg[ k ] = silk_residual_energy16_covar_FIX( b_Q14_ptr, WLTP_ptr, Rr, rr[ k ], LTP_ORDER, 14 ); /* nrg_fix in Q( -corr_rshifts[ k ] ) */ /* temp = Wght[ k ] / ( nrg[ k ] * Wght[ k ] + 0.01f * subfr_length ); */ extra_shifts = silk_min_int( corr_rshifts[ k ], LTP_CORRS_HEAD_ROOM ); denom32 = silk_LSHIFT_SAT32( silk_SMULWB( nrg[ k ], Wght_Q15[ k ] ), 1 + extra_shifts ) + /* Q( -corr_rshifts[ k ] + extra_shifts ) */ silk_RSHIFT( silk_SMULWB( (opus_int32)subfr_length, 655 ), corr_rshifts[ k ] - extra_shifts ); /* Q( -corr_rshifts[ k ] + extra_shifts ) */ denom32 = silk_max( denom32, 1 ); silk_assert( ((opus_int64)Wght_Q15[ k ] << 16 ) < silk_int32_MAX ); /* Wght always < 0.5 in Q0 */ temp32 = silk_DIV32( silk_LSHIFT( (opus_int32)Wght_Q15[ k ], 16 ), denom32 ); /* Q( 15 + 16 + corr_rshifts[k] - extra_shifts ) */ temp32 = silk_RSHIFT( temp32, 31 + corr_rshifts[ k ] - extra_shifts - 26 ); /* Q26 */ /* Limit temp such that the below scaling never wraps around */ WLTP_max = 0; for( i = 0; i < LTP_ORDER * LTP_ORDER; i++ ) { WLTP_max = silk_max( WLTP_ptr[ i ], WLTP_max ); } lshift = silk_CLZ32( WLTP_max ) - 1 - 3; /* keep 3 bits free for vq_nearest_neighbor_fix */ silk_assert( 26 - 18 + lshift >= 0 ); if( 26 - 18 + lshift < 31 ) { temp32 = silk_min_32( temp32, silk_LSHIFT( (opus_int32)1, 26 - 18 + lshift ) ); } silk_scale_vector32_Q26_lshift_18( WLTP_ptr, temp32, LTP_ORDER * LTP_ORDER ); /* WLTP_ptr in Q( 18 - corr_rshifts[ k ] ) */ w[ k ] = matrix_ptr( WLTP_ptr, LTP_ORDER/2, LTP_ORDER/2, LTP_ORDER ); /* w in Q( 18 - corr_rshifts[ k ] ) */ silk_assert( w[k] >= 0 ); r_ptr += subfr_length; b_Q14_ptr += LTP_ORDER; WLTP_ptr += LTP_ORDER * LTP_ORDER; } maxRshifts = 0; for( k = 0; k < nb_subfr; k++ ) { maxRshifts = silk_max_int( corr_rshifts[ k ], maxRshifts ); } /* Compute LTP coding gain */ if( LTPredCodGain_Q7 != NULL ) { LPC_LTP_res_nrg = 0; LPC_res_nrg = 0; silk_assert( LTP_CORRS_HEAD_ROOM >= 2 ); /* Check that no overflow will happen when adding */ for( k = 0; k < nb_subfr; k++ ) { LPC_res_nrg = silk_ADD32( LPC_res_nrg, silk_RSHIFT( silk_ADD32( silk_SMULWB( rr[ k ], Wght_Q15[ k ] ), 1 ), 1 + ( maxRshifts - corr_rshifts[ k ] ) ) ); /* Q( -maxRshifts ) */ LPC_LTP_res_nrg = silk_ADD32( LPC_LTP_res_nrg, silk_RSHIFT( silk_ADD32( silk_SMULWB( nrg[ k ], Wght_Q15[ k ] ), 1 ), 1 + ( maxRshifts - corr_rshifts[ k ] ) ) ); /* Q( -maxRshifts ) */ } LPC_LTP_res_nrg = silk_max( LPC_LTP_res_nrg, 1 ); /* avoid division by zero */ div_Q16 = silk_DIV32_varQ( LPC_res_nrg, LPC_LTP_res_nrg, 16 ); *LTPredCodGain_Q7 = ( opus_int )silk_SMULBB( 3, silk_lin2log( div_Q16 ) - ( 16 << 7 ) ); silk_assert( *LTPredCodGain_Q7 == ( opus_int )silk_SAT16( silk_MUL( 3, silk_lin2log( div_Q16 ) - ( 16 << 7 ) ) ) ); } /* smoothing */ /* d = sum( B, 1 ); */ b_Q14_ptr = b_Q14; for( k = 0; k < nb_subfr; k++ ) { d_Q14[ k ] = 0; for( i = 0; i < LTP_ORDER; i++ ) { d_Q14[ k ] += b_Q14_ptr[ i ]; } b_Q14_ptr += LTP_ORDER; } /* m = ( w * d' ) / ( sum( w ) + 1e-3 ); */ /* Find maximum absolute value of d_Q14 and the bits used by w in Q0 */ max_abs_d_Q14 = 0; max_w_bits = 0; for( k = 0; k < nb_subfr; k++ ) { max_abs_d_Q14 = silk_max_32( max_abs_d_Q14, silk_abs( d_Q14[ k ] ) ); /* w[ k ] is in Q( 18 - corr_rshifts[ k ] ) */ /* Find bits needed in Q( 18 - maxRshifts ) */ max_w_bits = silk_max_32( max_w_bits, 32 - silk_CLZ32( w[ k ] ) + corr_rshifts[ k ] - maxRshifts ); } /* max_abs_d_Q14 = (5 << 15); worst case, i.e. LTP_ORDER * -silk_int16_MIN */ silk_assert( max_abs_d_Q14 <= ( 5 << 15 ) ); /* How many bits is needed for w*d' in Q( 18 - maxRshifts ) in the worst case, of all d_Q14's being equal to max_abs_d_Q14 */ extra_shifts = max_w_bits + 32 - silk_CLZ32( max_abs_d_Q14 ) - 14; /* Subtract what we got available; bits in output var plus maxRshifts */ extra_shifts -= ( 32 - 1 - 2 + maxRshifts ); /* Keep sign bit free as well as 2 bits for accumulation */ extra_shifts = silk_max_int( extra_shifts, 0 ); maxRshifts_wxtra = maxRshifts + extra_shifts; temp32 = silk_RSHIFT( 262, maxRshifts + extra_shifts ) + 1; /* 1e-3f in Q( 18 - (maxRshifts + extra_shifts) ) */ wd = 0; for( k = 0; k < nb_subfr; k++ ) { /* w has at least 2 bits of headroom so no overflow should happen */ temp32 = silk_ADD32( temp32, silk_RSHIFT( w[ k ], maxRshifts_wxtra - corr_rshifts[ k ] ) ); /* Q( 18 - maxRshifts_wxtra ) */ wd = silk_ADD32( wd, silk_LSHIFT( silk_SMULWW( silk_RSHIFT( w[ k ], maxRshifts_wxtra - corr_rshifts[ k ] ), d_Q14[ k ] ), 2 ) ); /* Q( 18 - maxRshifts_wxtra ) */ } m_Q12 = silk_DIV32_varQ( wd, temp32, 12 ); b_Q14_ptr = b_Q14; for( k = 0; k < nb_subfr; k++ ) { /* w_fix[ k ] from Q( 18 - corr_rshifts[ k ] ) to Q( 16 ) */ if( 2 - corr_rshifts[k] > 0 ) { temp32 = silk_RSHIFT( w[ k ], 2 - corr_rshifts[ k ] ); } else { temp32 = silk_LSHIFT_SAT32( w[ k ], corr_rshifts[ k ] - 2 ); } g_Q26 = silk_MUL( silk_DIV32( SILK_FIX_CONST( LTP_SMOOTHING, 26 ), silk_RSHIFT( SILK_FIX_CONST( LTP_SMOOTHING, 26 ), 10 ) + temp32 ), /* Q10 */ silk_LSHIFT_SAT32( silk_SUB_SAT32( (opus_int32)m_Q12, silk_RSHIFT( d_Q14[ k ], 2 ) ), 4 ) ); /* Q16 */ temp32 = 0; for( i = 0; i < LTP_ORDER; i++ ) { delta_b_Q14[ i ] = silk_max_16( b_Q14_ptr[ i ], 1638 ); /* 1638_Q14 = 0.1_Q0 */ temp32 += delta_b_Q14[ i ]; /* Q14 */ } temp32 = silk_DIV32( g_Q26, temp32 ); /* Q14 -> Q12 */ for( i = 0; i < LTP_ORDER; i++ ) { b_Q14_ptr[ i ] = silk_LIMIT_32( (opus_int32)b_Q14_ptr[ i ] + silk_SMULWB( silk_LSHIFT_SAT32( temp32, 4 ), delta_b_Q14[ i ] ), -16000, 28000 ); } b_Q14_ptr += LTP_ORDER; } } void silk_fit_LTP( opus_int32 LTP_coefs_Q16[ LTP_ORDER ], opus_int16 LTP_coefs_Q14[ LTP_ORDER ] ) { opus_int i; for( i = 0; i < LTP_ORDER; i++ ) { LTP_coefs_Q14[ i ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( LTP_coefs_Q16[ i ], 2 ) ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/find_pitch_lags_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" #include "stack_alloc.h" #include "tuning_parameters.h" /* Find pitch lags */ void silk_find_pitch_lags_FIX( silk_encoder_state_FIX *psEnc, /* I/O encoder state */ silk_encoder_control_FIX *psEncCtrl, /* I/O encoder control */ opus_int16 res[], /* O residual */ const opus_int16 x[], /* I Speech signal */ int arch /* I Run-time architecture */ ) { opus_int buf_len, i, scale; opus_int32 thrhld_Q13, res_nrg; const opus_int16 *x_buf, *x_buf_ptr; VARDECL( opus_int16, Wsig ); opus_int16 *Wsig_ptr; opus_int32 auto_corr[ MAX_FIND_PITCH_LPC_ORDER + 1 ]; opus_int16 rc_Q15[ MAX_FIND_PITCH_LPC_ORDER ]; opus_int32 A_Q24[ MAX_FIND_PITCH_LPC_ORDER ]; opus_int16 A_Q12[ MAX_FIND_PITCH_LPC_ORDER ]; SAVE_STACK; /******************************************/ /* Set up buffer lengths etc based on Fs */ /******************************************/ buf_len = psEnc->sCmn.la_pitch + psEnc->sCmn.frame_length + psEnc->sCmn.ltp_mem_length; /* Safety check */ silk_assert( buf_len >= psEnc->sCmn.pitch_LPC_win_length ); x_buf = x - psEnc->sCmn.ltp_mem_length; /*************************************/ /* Estimate LPC AR coefficients */ /*************************************/ /* Calculate windowed signal */ ALLOC( Wsig, psEnc->sCmn.pitch_LPC_win_length, opus_int16 ); /* First LA_LTP samples */ x_buf_ptr = x_buf + buf_len - psEnc->sCmn.pitch_LPC_win_length; Wsig_ptr = Wsig; silk_apply_sine_window( Wsig_ptr, x_buf_ptr, 1, psEnc->sCmn.la_pitch ); /* Middle un - windowed samples */ Wsig_ptr += psEnc->sCmn.la_pitch; x_buf_ptr += psEnc->sCmn.la_pitch; silk_memcpy( Wsig_ptr, x_buf_ptr, ( psEnc->sCmn.pitch_LPC_win_length - silk_LSHIFT( psEnc->sCmn.la_pitch, 1 ) ) * sizeof( opus_int16 ) ); /* Last LA_LTP samples */ Wsig_ptr += psEnc->sCmn.pitch_LPC_win_length - silk_LSHIFT( psEnc->sCmn.la_pitch, 1 ); x_buf_ptr += psEnc->sCmn.pitch_LPC_win_length - silk_LSHIFT( psEnc->sCmn.la_pitch, 1 ); silk_apply_sine_window( Wsig_ptr, x_buf_ptr, 2, psEnc->sCmn.la_pitch ); /* Calculate autocorrelation sequence */ silk_autocorr( auto_corr, &scale, Wsig, psEnc->sCmn.pitch_LPC_win_length, psEnc->sCmn.pitchEstimationLPCOrder + 1, arch ); /* Add white noise, as fraction of energy */ auto_corr[ 0 ] = silk_SMLAWB( auto_corr[ 0 ], auto_corr[ 0 ], SILK_FIX_CONST( FIND_PITCH_WHITE_NOISE_FRACTION, 16 ) ) + 1; /* Calculate the reflection coefficients using schur */ res_nrg = silk_schur( rc_Q15, auto_corr, psEnc->sCmn.pitchEstimationLPCOrder ); /* Prediction gain */ psEncCtrl->predGain_Q16 = silk_DIV32_varQ( auto_corr[ 0 ], silk_max_int( res_nrg, 1 ), 16 ); /* Convert reflection coefficients to prediction coefficients */ silk_k2a( A_Q24, rc_Q15, psEnc->sCmn.pitchEstimationLPCOrder ); /* Convert From 32 bit Q24 to 16 bit Q12 coefs */ for( i = 0; i < psEnc->sCmn.pitchEstimationLPCOrder; i++ ) { A_Q12[ i ] = (opus_int16)silk_SAT16( silk_RSHIFT( A_Q24[ i ], 12 ) ); } /* Do BWE */ silk_bwexpander( A_Q12, psEnc->sCmn.pitchEstimationLPCOrder, SILK_FIX_CONST( FIND_PITCH_BANDWIDTH_EXPANSION, 16 ) ); /*****************************************/ /* LPC analysis filtering */ /*****************************************/ silk_LPC_analysis_filter( res, x_buf, A_Q12, buf_len, psEnc->sCmn.pitchEstimationLPCOrder, psEnc->sCmn.arch ); if( psEnc->sCmn.indices.signalType != TYPE_NO_VOICE_ACTIVITY && psEnc->sCmn.first_frame_after_reset == 0 ) { /* Threshold for pitch estimator */ thrhld_Q13 = SILK_FIX_CONST( 0.6, 13 ); thrhld_Q13 = silk_SMLABB( thrhld_Q13, SILK_FIX_CONST( -0.004, 13 ), psEnc->sCmn.pitchEstimationLPCOrder ); thrhld_Q13 = silk_SMLAWB( thrhld_Q13, SILK_FIX_CONST( -0.1, 21 ), psEnc->sCmn.speech_activity_Q8 ); thrhld_Q13 = silk_SMLABB( thrhld_Q13, SILK_FIX_CONST( -0.15, 13 ), silk_RSHIFT( psEnc->sCmn.prevSignalType, 1 ) ); thrhld_Q13 = silk_SMLAWB( thrhld_Q13, SILK_FIX_CONST( -0.1, 14 ), psEnc->sCmn.input_tilt_Q15 ); thrhld_Q13 = silk_SAT16( thrhld_Q13 ); /*****************************************/ /* Call pitch estimator */ /*****************************************/ if( silk_pitch_analysis_core( res, psEncCtrl->pitchL, &psEnc->sCmn.indices.lagIndex, &psEnc->sCmn.indices.contourIndex, &psEnc->LTPCorr_Q15, psEnc->sCmn.prevLag, psEnc->sCmn.pitchEstimationThreshold_Q16, (opus_int)thrhld_Q13, psEnc->sCmn.fs_kHz, psEnc->sCmn.pitchEstimationComplexity, psEnc->sCmn.nb_subfr, psEnc->sCmn.arch) == 0 ) { psEnc->sCmn.indices.signalType = TYPE_VOICED; } else { psEnc->sCmn.indices.signalType = TYPE_UNVOICED; } } else { silk_memset( psEncCtrl->pitchL, 0, sizeof( psEncCtrl->pitchL ) ); psEnc->sCmn.indices.lagIndex = 0; psEnc->sCmn.indices.contourIndex = 0; psEnc->LTPCorr_Q15 = 0; } RESTORE_STACK; } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/find_pred_coefs_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" #include "stack_alloc.h" void silk_find_pred_coefs_FIX( silk_encoder_state_FIX *psEnc, /* I/O encoder state */ silk_encoder_control_FIX *psEncCtrl, /* I/O encoder control */ const opus_int16 res_pitch[], /* I Residual from pitch analysis */ const opus_int16 x[], /* I Speech signal */ opus_int condCoding /* I The type of conditional coding to use */ ) { opus_int i; opus_int32 invGains_Q16[ MAX_NB_SUBFR ], local_gains[ MAX_NB_SUBFR ], Wght_Q15[ MAX_NB_SUBFR ]; opus_int16 NLSF_Q15[ MAX_LPC_ORDER ]; const opus_int16 *x_ptr; opus_int16 *x_pre_ptr; VARDECL( opus_int16, LPC_in_pre ); opus_int32 tmp, min_gain_Q16, minInvGain_Q30; opus_int LTP_corrs_rshift[ MAX_NB_SUBFR ]; SAVE_STACK; /* weighting for weighted least squares */ min_gain_Q16 = silk_int32_MAX >> 6; for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) { min_gain_Q16 = silk_min( min_gain_Q16, psEncCtrl->Gains_Q16[ i ] ); } for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) { /* Divide to Q16 */ silk_assert( psEncCtrl->Gains_Q16[ i ] > 0 ); /* Invert and normalize gains, and ensure that maximum invGains_Q16 is within range of a 16 bit int */ invGains_Q16[ i ] = silk_DIV32_varQ( min_gain_Q16, psEncCtrl->Gains_Q16[ i ], 16 - 2 ); /* Ensure Wght_Q15 a minimum value 1 */ invGains_Q16[ i ] = silk_max( invGains_Q16[ i ], 363 ); /* Square the inverted gains */ silk_assert( invGains_Q16[ i ] == silk_SAT16( invGains_Q16[ i ] ) ); tmp = silk_SMULWB( invGains_Q16[ i ], invGains_Q16[ i ] ); Wght_Q15[ i ] = silk_RSHIFT( tmp, 1 ); /* Invert the inverted and normalized gains */ local_gains[ i ] = silk_DIV32( ( (opus_int32)1 << 16 ), invGains_Q16[ i ] ); } ALLOC( LPC_in_pre, psEnc->sCmn.nb_subfr * psEnc->sCmn.predictLPCOrder + psEnc->sCmn.frame_length, opus_int16 ); if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { VARDECL( opus_int32, WLTP ); /**********/ /* VOICED */ /**********/ silk_assert( psEnc->sCmn.ltp_mem_length - psEnc->sCmn.predictLPCOrder >= psEncCtrl->pitchL[ 0 ] + LTP_ORDER / 2 ); ALLOC( WLTP, psEnc->sCmn.nb_subfr * LTP_ORDER * LTP_ORDER, opus_int32 ); /* LTP analysis */ silk_find_LTP_FIX( psEncCtrl->LTPCoef_Q14, WLTP, &psEncCtrl->LTPredCodGain_Q7, res_pitch, psEncCtrl->pitchL, Wght_Q15, psEnc->sCmn.subfr_length, psEnc->sCmn.nb_subfr, psEnc->sCmn.ltp_mem_length, LTP_corrs_rshift, psEnc->sCmn.arch ); /* Quantize LTP gain parameters */ silk_quant_LTP_gains( psEncCtrl->LTPCoef_Q14, psEnc->sCmn.indices.LTPIndex, &psEnc->sCmn.indices.PERIndex, &psEnc->sCmn.sum_log_gain_Q7, WLTP, psEnc->sCmn.mu_LTP_Q9, psEnc->sCmn.LTPQuantLowComplexity, psEnc->sCmn.nb_subfr, psEnc->sCmn.arch); /* Control LTP scaling */ silk_LTP_scale_ctrl_FIX( psEnc, psEncCtrl, condCoding ); /* Create LTP residual */ silk_LTP_analysis_filter_FIX( LPC_in_pre, x - psEnc->sCmn.predictLPCOrder, psEncCtrl->LTPCoef_Q14, psEncCtrl->pitchL, invGains_Q16, psEnc->sCmn.subfr_length, psEnc->sCmn.nb_subfr, psEnc->sCmn.predictLPCOrder ); } else { /************/ /* UNVOICED */ /************/ /* Create signal with prepended subframes, scaled by inverse gains */ x_ptr = x - psEnc->sCmn.predictLPCOrder; x_pre_ptr = LPC_in_pre; for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) { silk_scale_copy_vector16( x_pre_ptr, x_ptr, invGains_Q16[ i ], psEnc->sCmn.subfr_length + psEnc->sCmn.predictLPCOrder ); x_pre_ptr += psEnc->sCmn.subfr_length + psEnc->sCmn.predictLPCOrder; x_ptr += psEnc->sCmn.subfr_length; } silk_memset( psEncCtrl->LTPCoef_Q14, 0, psEnc->sCmn.nb_subfr * LTP_ORDER * sizeof( opus_int16 ) ); psEncCtrl->LTPredCodGain_Q7 = 0; psEnc->sCmn.sum_log_gain_Q7 = 0; } /* Limit on total predictive coding gain */ if( psEnc->sCmn.first_frame_after_reset ) { minInvGain_Q30 = SILK_FIX_CONST( 1.0f / MAX_PREDICTION_POWER_GAIN_AFTER_RESET, 30 ); } else { minInvGain_Q30 = silk_log2lin( silk_SMLAWB( 16 << 7, (opus_int32)psEncCtrl->LTPredCodGain_Q7, SILK_FIX_CONST( 1.0 / 3, 16 ) ) ); /* Q16 */ minInvGain_Q30 = silk_DIV32_varQ( minInvGain_Q30, silk_SMULWW( SILK_FIX_CONST( MAX_PREDICTION_POWER_GAIN, 0 ), silk_SMLAWB( SILK_FIX_CONST( 0.25, 18 ), SILK_FIX_CONST( 0.75, 18 ), psEncCtrl->coding_quality_Q14 ) ), 14 ); } /* LPC_in_pre contains the LTP-filtered input for voiced, and the unfiltered input for unvoiced */ silk_find_LPC_FIX( &psEnc->sCmn, NLSF_Q15, LPC_in_pre, minInvGain_Q30 ); /* Quantize LSFs */ silk_process_NLSFs( &psEnc->sCmn, psEncCtrl->PredCoef_Q12, NLSF_Q15, psEnc->sCmn.prev_NLSFq_Q15 ); /* Calculate residual energy using quantized LPC coefficients */ silk_residual_energy_FIX( psEncCtrl->ResNrg, psEncCtrl->ResNrgQ, LPC_in_pre, psEncCtrl->PredCoef_Q12, local_gains, psEnc->sCmn.subfr_length, psEnc->sCmn.nb_subfr, psEnc->sCmn.predictLPCOrder, psEnc->sCmn.arch ); /* Copy to prediction struct for use in next frame for interpolation */ silk_memcpy( psEnc->sCmn.prev_NLSFq_Q15, NLSF_Q15, sizeof( psEnc->sCmn.prev_NLSFq_Q15 ) ); RESTORE_STACK; } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/k2a_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" /* Step up function, converts reflection coefficients to prediction coefficients */ void silk_k2a( opus_int32 *A_Q24, /* O Prediction coefficients [order] Q24 */ const opus_int16 *rc_Q15, /* I Reflection coefficients [order] Q15 */ const opus_int32 order /* I Prediction order */ ) { opus_int k, n; opus_int32 Atmp[ SILK_MAX_ORDER_LPC ]; for( k = 0; k < order; k++ ) { for( n = 0; n < k; n++ ) { Atmp[ n ] = A_Q24[ n ]; } for( n = 0; n < k; n++ ) { A_Q24[ n ] = silk_SMLAWB( A_Q24[ n ], silk_LSHIFT( Atmp[ k - n - 1 ], 1 ), rc_Q15[ k ] ); } A_Q24[ k ] = -silk_LSHIFT( (opus_int32)rc_Q15[ k ], 9 ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/k2a_Q16_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" /* Step up function, converts reflection coefficients to prediction coefficients */ void silk_k2a_Q16( opus_int32 *A_Q24, /* O Prediction coefficients [order] Q24 */ const opus_int32 *rc_Q16, /* I Reflection coefficients [order] Q16 */ const opus_int32 order /* I Prediction order */ ) { opus_int k, n; opus_int32 Atmp[ SILK_MAX_ORDER_LPC ]; for( k = 0; k < order; k++ ) { for( n = 0; n < k; n++ ) { Atmp[ n ] = A_Q24[ n ]; } for( n = 0; n < k; n++ ) { A_Q24[ n ] = silk_SMLAWW( A_Q24[ n ], Atmp[ k - n - 1 ], rc_Q16[ k ] ); } A_Q24[ k ] = -silk_LSHIFT( rc_Q16[ k ], 8 ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/main_FIX.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_MAIN_FIX_H #define SILK_MAIN_FIX_H #include "SigProc_FIX.h" #include "structs_FIX.h" #include "control.h" #include "main.h" #include "PLC.h" #include "debug.h" #include "entenc.h" #ifndef FORCE_CPP_BUILD #ifdef __cplusplus extern "C" { #endif #endif #define silk_encoder_state_Fxx silk_encoder_state_FIX #define silk_encode_do_VAD_Fxx silk_encode_do_VAD_FIX #define silk_encode_frame_Fxx silk_encode_frame_FIX /*********************/ /* Encoder Functions */ /*********************/ /* High-pass filter with cutoff frequency adaptation based on pitch lag statistics */ void silk_HP_variable_cutoff( silk_encoder_state_Fxx state_Fxx[] /* I/O Encoder states */ ); /* Encoder main function */ void silk_encode_do_VAD_FIX( silk_encoder_state_FIX *psEnc /* I/O Pointer to Silk FIX encoder state */ ); /* Encoder main function */ opus_int silk_encode_frame_FIX( silk_encoder_state_FIX *psEnc, /* I/O Pointer to Silk FIX encoder state */ opus_int32 *pnBytesOut, /* O Pointer to number of payload bytes; */ ec_enc *psRangeEnc, /* I/O compressor data structure */ opus_int condCoding, /* I The type of conditional coding to use */ opus_int maxBits, /* I If > 0: maximum number of output bits */ opus_int useCBR /* I Flag to force constant-bitrate operation */ ); /* Initializes the Silk encoder state */ opus_int silk_init_encoder( silk_encoder_state_Fxx *psEnc, /* I/O Pointer to Silk FIX encoder state */ int arch /* I Run-time architecture */ ); /* Control the Silk encoder */ opus_int silk_control_encoder( silk_encoder_state_Fxx *psEnc, /* I/O Pointer to Silk encoder state */ silk_EncControlStruct *encControl, /* I Control structure */ const opus_int32 TargetRate_bps, /* I Target max bitrate (bps) */ const opus_int allow_bw_switch, /* I Flag to allow switching audio bandwidth */ const opus_int channelNb, /* I Channel number */ const opus_int force_fs_kHz ); /****************/ /* Prefiltering */ /****************/ void silk_prefilter_FIX( silk_encoder_state_FIX *psEnc, /* I/O Encoder state */ const silk_encoder_control_FIX *psEncCtrl, /* I Encoder control */ opus_int32 xw_Q10[], /* O Weighted signal */ const opus_int16 x[] /* I Speech signal */ ); void silk_warped_LPC_analysis_filter_FIX_c( opus_int32 state[], /* I/O State [order + 1] */ opus_int32 res_Q2[], /* O Residual signal [length] */ const opus_int16 coef_Q13[], /* I Coefficients [order] */ const opus_int16 input[], /* I Input signal [length] */ const opus_int16 lambda_Q16, /* I Warping factor */ const opus_int length, /* I Length of input signal */ const opus_int order /* I Filter order (even) */ ); /**************************/ /* Noise shaping analysis */ /**************************/ /* Compute noise shaping coefficients and initial gain values */ void silk_noise_shape_analysis_FIX( silk_encoder_state_FIX *psEnc, /* I/O Encoder state FIX */ silk_encoder_control_FIX *psEncCtrl, /* I/O Encoder control FIX */ const opus_int16 *pitch_res, /* I LPC residual from pitch analysis */ const opus_int16 *x, /* I Input signal [ frame_length + la_shape ] */ int arch /* I Run-time architecture */ ); /* Autocorrelations for a warped frequency axis */ void silk_warped_autocorrelation_FIX( opus_int32 *corr, /* O Result [order + 1] */ opus_int *scale, /* O Scaling of the correlation vector */ const opus_int16 *input, /* I Input data to correlate */ const opus_int warping_Q16, /* I Warping coefficient */ const opus_int length, /* I Length of input */ const opus_int order /* I Correlation order (even) */ ); /* Calculation of LTP state scaling */ void silk_LTP_scale_ctrl_FIX( silk_encoder_state_FIX *psEnc, /* I/O encoder state */ silk_encoder_control_FIX *psEncCtrl, /* I/O encoder control */ opus_int condCoding /* I The type of conditional coding to use */ ); /**********************************************/ /* Prediction Analysis */ /**********************************************/ /* Find pitch lags */ void silk_find_pitch_lags_FIX( silk_encoder_state_FIX *psEnc, /* I/O encoder state */ silk_encoder_control_FIX *psEncCtrl, /* I/O encoder control */ opus_int16 res[], /* O residual */ const opus_int16 x[], /* I Speech signal */ int arch /* I Run-time architecture */ ); /* Find LPC and LTP coefficients */ void silk_find_pred_coefs_FIX( silk_encoder_state_FIX *psEnc, /* I/O encoder state */ silk_encoder_control_FIX *psEncCtrl, /* I/O encoder control */ const opus_int16 res_pitch[], /* I Residual from pitch analysis */ const opus_int16 x[], /* I Speech signal */ opus_int condCoding /* I The type of conditional coding to use */ ); /* LPC analysis */ void silk_find_LPC_FIX( silk_encoder_state *psEncC, /* I/O Encoder state */ opus_int16 NLSF_Q15[], /* O NLSFs */ const opus_int16 x[], /* I Input signal */ const opus_int32 minInvGain_Q30 /* I Inverse of max prediction gain */ ); /* LTP analysis */ void silk_find_LTP_FIX( opus_int16 b_Q14[ MAX_NB_SUBFR * LTP_ORDER ], /* O LTP coefs */ opus_int32 WLTP[ MAX_NB_SUBFR * LTP_ORDER * LTP_ORDER ], /* O Weight for LTP quantization */ opus_int *LTPredCodGain_Q7, /* O LTP coding gain */ const opus_int16 r_lpc[], /* I residual signal after LPC signal + state for first 10 ms */ const opus_int lag[ MAX_NB_SUBFR ], /* I LTP lags */ const opus_int32 Wght_Q15[ MAX_NB_SUBFR ], /* I weights */ const opus_int subfr_length, /* I subframe length */ const opus_int nb_subfr, /* I number of subframes */ const opus_int mem_offset, /* I number of samples in LTP memory */ opus_int corr_rshifts[ MAX_NB_SUBFR ], /* O right shifts applied to correlations */ int arch /* I Run-time architecture */ ); void silk_LTP_analysis_filter_FIX( opus_int16 *LTP_res, /* O LTP residual signal of length MAX_NB_SUBFR * ( pre_length + subfr_length ) */ const opus_int16 *x, /* I Pointer to input signal with at least max( pitchL ) preceding samples */ const opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ],/* I LTP_ORDER LTP coefficients for each MAX_NB_SUBFR subframe */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lag, one for each subframe */ const opus_int32 invGains_Q16[ MAX_NB_SUBFR ], /* I Inverse quantization gains, one for each subframe */ const opus_int subfr_length, /* I Length of each subframe */ const opus_int nb_subfr, /* I Number of subframes */ const opus_int pre_length /* I Length of the preceding samples starting at &x[0] for each subframe */ ); /* Calculates residual energies of input subframes where all subframes have LPC_order */ /* of preceding samples */ void silk_residual_energy_FIX( opus_int32 nrgs[ MAX_NB_SUBFR ], /* O Residual energy per subframe */ opus_int nrgsQ[ MAX_NB_SUBFR ], /* O Q value per subframe */ const opus_int16 x[], /* I Input signal */ opus_int16 a_Q12[ 2 ][ MAX_LPC_ORDER ], /* I AR coefs for each frame half */ const opus_int32 gains[ MAX_NB_SUBFR ], /* I Quantization gains */ const opus_int subfr_length, /* I Subframe length */ const opus_int nb_subfr, /* I Number of subframes */ const opus_int LPC_order, /* I LPC order */ int arch /* I Run-time architecture */ ); /* Residual energy: nrg = wxx - 2 * wXx * c + c' * wXX * c */ opus_int32 silk_residual_energy16_covar_FIX( const opus_int16 *c, /* I Prediction vector */ const opus_int32 *wXX, /* I Correlation matrix */ const opus_int32 *wXx, /* I Correlation vector */ opus_int32 wxx, /* I Signal energy */ opus_int D, /* I Dimension */ opus_int cQ /* I Q value for c vector 0 - 15 */ ); /* Processing of gains */ void silk_process_gains_FIX( silk_encoder_state_FIX *psEnc, /* I/O Encoder state */ silk_encoder_control_FIX *psEncCtrl, /* I/O Encoder control */ opus_int condCoding /* I The type of conditional coding to use */ ); /******************/ /* Linear Algebra */ /******************/ /* Calculates correlation matrix X'*X */ void silk_corrMatrix_FIX( const opus_int16 *x, /* I x vector [L + order - 1] used to form data matrix X */ const opus_int L, /* I Length of vectors */ const opus_int order, /* I Max lag for correlation */ const opus_int head_room, /* I Desired headroom */ opus_int32 *XX, /* O Pointer to X'*X correlation matrix [ order x order ] */ opus_int *rshifts, /* I/O Right shifts of correlations */ int arch /* I Run-time architecture */ ); /* Calculates correlation vector X'*t */ void silk_corrVector_FIX( const opus_int16 *x, /* I x vector [L + order - 1] used to form data matrix X */ const opus_int16 *t, /* I Target vector [L] */ const opus_int L, /* I Length of vectors */ const opus_int order, /* I Max lag for correlation */ opus_int32 *Xt, /* O Pointer to X'*t correlation vector [order] */ const opus_int rshifts, /* I Right shifts of correlations */ int arch /* I Run-time architecture */ ); /* Add noise to matrix diagonal */ void silk_regularize_correlations_FIX( opus_int32 *XX, /* I/O Correlation matrices */ opus_int32 *xx, /* I/O Correlation values */ opus_int32 noise, /* I Noise to add */ opus_int D /* I Dimension of XX */ ); /* Solves Ax = b, assuming A is symmetric */ void silk_solve_LDL_FIX( opus_int32 *A, /* I Pointer to symetric square matrix A */ opus_int M, /* I Size of matrix */ const opus_int32 *b, /* I Pointer to b vector */ opus_int32 *x_Q16 /* O Pointer to x solution vector */ ); #ifndef FORCE_CPP_BUILD #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* FORCE_CPP_BUILD */ #endif /* SILK_MAIN_FIX_H */ ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/mips/noise_shape_analysis_FIX_mipsr1.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ /**************************************************************/ /* Compute noise shaping coefficients and initial gain values */ /**************************************************************/ #define OVERRIDE_silk_noise_shape_analysis_FIX void silk_noise_shape_analysis_FIX( silk_encoder_state_FIX *psEnc, /* I/O Encoder state FIX */ silk_encoder_control_FIX *psEncCtrl, /* I/O Encoder control FIX */ const opus_int16 *pitch_res, /* I LPC residual from pitch analysis */ const opus_int16 *x, /* I Input signal [ frame_length + la_shape ] */ int arch /* I Run-time architecture */ ) { silk_shape_state_FIX *psShapeSt = &psEnc->sShape; opus_int k, i, nSamples, Qnrg, b_Q14, warping_Q16, scale = 0; opus_int32 SNR_adj_dB_Q7, HarmBoost_Q16, HarmShapeGain_Q16, Tilt_Q16, tmp32; opus_int32 nrg, pre_nrg_Q30, log_energy_Q7, log_energy_prev_Q7, energy_variation_Q7; opus_int32 delta_Q16, BWExp1_Q16, BWExp2_Q16, gain_mult_Q16, gain_add_Q16, strength_Q16, b_Q8; opus_int32 auto_corr[ MAX_SHAPE_LPC_ORDER + 1 ]; opus_int32 refl_coef_Q16[ MAX_SHAPE_LPC_ORDER ]; opus_int32 AR1_Q24[ MAX_SHAPE_LPC_ORDER ]; opus_int32 AR2_Q24[ MAX_SHAPE_LPC_ORDER ]; VARDECL( opus_int16, x_windowed ); const opus_int16 *x_ptr, *pitch_res_ptr; SAVE_STACK; /* Point to start of first LPC analysis block */ x_ptr = x - psEnc->sCmn.la_shape; /****************/ /* GAIN CONTROL */ /****************/ SNR_adj_dB_Q7 = psEnc->sCmn.SNR_dB_Q7; /* Input quality is the average of the quality in the lowest two VAD bands */ psEncCtrl->input_quality_Q14 = ( opus_int )silk_RSHIFT( (opus_int32)psEnc->sCmn.input_quality_bands_Q15[ 0 ] + psEnc->sCmn.input_quality_bands_Q15[ 1 ], 2 ); /* Coding quality level, between 0.0_Q0 and 1.0_Q0, but in Q14 */ psEncCtrl->coding_quality_Q14 = silk_RSHIFT( silk_sigm_Q15( silk_RSHIFT_ROUND( SNR_adj_dB_Q7 - SILK_FIX_CONST( 20.0, 7 ), 4 ) ), 1 ); /* Reduce coding SNR during low speech activity */ if( psEnc->sCmn.useCBR == 0 ) { b_Q8 = SILK_FIX_CONST( 1.0, 8 ) - psEnc->sCmn.speech_activity_Q8; b_Q8 = silk_SMULWB( silk_LSHIFT( b_Q8, 8 ), b_Q8 ); SNR_adj_dB_Q7 = silk_SMLAWB( SNR_adj_dB_Q7, silk_SMULBB( SILK_FIX_CONST( -BG_SNR_DECR_dB, 7 ) >> ( 4 + 1 ), b_Q8 ), /* Q11*/ silk_SMULWB( SILK_FIX_CONST( 1.0, 14 ) + psEncCtrl->input_quality_Q14, psEncCtrl->coding_quality_Q14 ) ); /* Q12*/ } if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { /* Reduce gains for periodic signals */ SNR_adj_dB_Q7 = silk_SMLAWB( SNR_adj_dB_Q7, SILK_FIX_CONST( HARM_SNR_INCR_dB, 8 ), psEnc->LTPCorr_Q15 ); } else { /* For unvoiced signals and low-quality input, adjust the quality slower than SNR_dB setting */ SNR_adj_dB_Q7 = silk_SMLAWB( SNR_adj_dB_Q7, silk_SMLAWB( SILK_FIX_CONST( 6.0, 9 ), -SILK_FIX_CONST( 0.4, 18 ), psEnc->sCmn.SNR_dB_Q7 ), SILK_FIX_CONST( 1.0, 14 ) - psEncCtrl->input_quality_Q14 ); } /*************************/ /* SPARSENESS PROCESSING */ /*************************/ /* Set quantizer offset */ if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { /* Initially set to 0; may be overruled in process_gains(..) */ psEnc->sCmn.indices.quantOffsetType = 0; psEncCtrl->sparseness_Q8 = 0; } else { /* Sparseness measure, based on relative fluctuations of energy per 2 milliseconds */ nSamples = silk_LSHIFT( psEnc->sCmn.fs_kHz, 1 ); energy_variation_Q7 = 0; log_energy_prev_Q7 = 0; pitch_res_ptr = pitch_res; for( k = 0; k < silk_SMULBB( SUB_FRAME_LENGTH_MS, psEnc->sCmn.nb_subfr ) / 2; k++ ) { silk_sum_sqr_shift( &nrg, &scale, pitch_res_ptr, nSamples ); nrg += silk_RSHIFT( nSamples, scale ); /* Q(-scale)*/ log_energy_Q7 = silk_lin2log( nrg ); if( k > 0 ) { energy_variation_Q7 += silk_abs( log_energy_Q7 - log_energy_prev_Q7 ); } log_energy_prev_Q7 = log_energy_Q7; pitch_res_ptr += nSamples; } psEncCtrl->sparseness_Q8 = silk_RSHIFT( silk_sigm_Q15( silk_SMULWB( energy_variation_Q7 - SILK_FIX_CONST( 5.0, 7 ), SILK_FIX_CONST( 0.1, 16 ) ) ), 7 ); /* Set quantization offset depending on sparseness measure */ if( psEncCtrl->sparseness_Q8 > SILK_FIX_CONST( SPARSENESS_THRESHOLD_QNT_OFFSET, 8 ) ) { psEnc->sCmn.indices.quantOffsetType = 0; } else { psEnc->sCmn.indices.quantOffsetType = 1; } /* Increase coding SNR for sparse signals */ SNR_adj_dB_Q7 = silk_SMLAWB( SNR_adj_dB_Q7, SILK_FIX_CONST( SPARSE_SNR_INCR_dB, 15 ), psEncCtrl->sparseness_Q8 - SILK_FIX_CONST( 0.5, 8 ) ); } /*******************************/ /* Control bandwidth expansion */ /*******************************/ /* More BWE for signals with high prediction gain */ strength_Q16 = silk_SMULWB( psEncCtrl->predGain_Q16, SILK_FIX_CONST( FIND_PITCH_WHITE_NOISE_FRACTION, 16 ) ); BWExp1_Q16 = BWExp2_Q16 = silk_DIV32_varQ( SILK_FIX_CONST( BANDWIDTH_EXPANSION, 16 ), silk_SMLAWW( SILK_FIX_CONST( 1.0, 16 ), strength_Q16, strength_Q16 ), 16 ); delta_Q16 = silk_SMULWB( SILK_FIX_CONST( 1.0, 16 ) - silk_SMULBB( 3, psEncCtrl->coding_quality_Q14 ), SILK_FIX_CONST( LOW_RATE_BANDWIDTH_EXPANSION_DELTA, 16 ) ); BWExp1_Q16 = silk_SUB32( BWExp1_Q16, delta_Q16 ); BWExp2_Q16 = silk_ADD32( BWExp2_Q16, delta_Q16 ); /* BWExp1 will be applied after BWExp2, so make it relative */ BWExp1_Q16 = silk_DIV32_16( silk_LSHIFT( BWExp1_Q16, 14 ), silk_RSHIFT( BWExp2_Q16, 2 ) ); if( psEnc->sCmn.warping_Q16 > 0 ) { /* Slightly more warping in analysis will move quantization noise up in frequency, where it's better masked */ warping_Q16 = silk_SMLAWB( psEnc->sCmn.warping_Q16, (opus_int32)psEncCtrl->coding_quality_Q14, SILK_FIX_CONST( 0.01, 18 ) ); } else { warping_Q16 = 0; } /********************************************/ /* Compute noise shaping AR coefs and gains */ /********************************************/ ALLOC( x_windowed, psEnc->sCmn.shapeWinLength, opus_int16 ); for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { /* Apply window: sine slope followed by flat part followed by cosine slope */ opus_int shift, slope_part, flat_part; flat_part = psEnc->sCmn.fs_kHz * 3; slope_part = silk_RSHIFT( psEnc->sCmn.shapeWinLength - flat_part, 1 ); silk_apply_sine_window( x_windowed, x_ptr, 1, slope_part ); shift = slope_part; silk_memcpy( x_windowed + shift, x_ptr + shift, flat_part * sizeof(opus_int16) ); shift += flat_part; silk_apply_sine_window( x_windowed + shift, x_ptr + shift, 2, slope_part ); /* Update pointer: next LPC analysis block */ x_ptr += psEnc->sCmn.subfr_length; if( psEnc->sCmn.warping_Q16 > 0 ) { /* Calculate warped auto correlation */ silk_warped_autocorrelation_FIX( auto_corr, &scale, x_windowed, warping_Q16, psEnc->sCmn.shapeWinLength, psEnc->sCmn.shapingLPCOrder ); } else { /* Calculate regular auto correlation */ silk_autocorr( auto_corr, &scale, x_windowed, psEnc->sCmn.shapeWinLength, psEnc->sCmn.shapingLPCOrder + 1, arch ); } /* Add white noise, as a fraction of energy */ auto_corr[0] = silk_ADD32( auto_corr[0], silk_max_32( silk_SMULWB( silk_RSHIFT( auto_corr[ 0 ], 4 ), SILK_FIX_CONST( SHAPE_WHITE_NOISE_FRACTION, 20 ) ), 1 ) ); /* Calculate the reflection coefficients using schur */ nrg = silk_schur64( refl_coef_Q16, auto_corr, psEnc->sCmn.shapingLPCOrder ); silk_assert( nrg >= 0 ); /* Convert reflection coefficients to prediction coefficients */ silk_k2a_Q16( AR2_Q24, refl_coef_Q16, psEnc->sCmn.shapingLPCOrder ); Qnrg = -scale; /* range: -12...30*/ silk_assert( Qnrg >= -12 ); silk_assert( Qnrg <= 30 ); /* Make sure that Qnrg is an even number */ if( Qnrg & 1 ) { Qnrg -= 1; nrg >>= 1; } tmp32 = silk_SQRT_APPROX( nrg ); Qnrg >>= 1; /* range: -6...15*/ psEncCtrl->Gains_Q16[ k ] = (silk_LSHIFT32( silk_LIMIT( (tmp32), silk_RSHIFT32( silk_int32_MIN, (16 - Qnrg) ), \ silk_RSHIFT32( silk_int32_MAX, (16 - Qnrg) ) ), (16 - Qnrg) )); if( psEnc->sCmn.warping_Q16 > 0 ) { /* Adjust gain for warping */ gain_mult_Q16 = warped_gain( AR2_Q24, warping_Q16, psEnc->sCmn.shapingLPCOrder ); silk_assert( psEncCtrl->Gains_Q16[ k ] >= 0 ); if ( silk_SMULWW( silk_RSHIFT_ROUND( psEncCtrl->Gains_Q16[ k ], 1 ), gain_mult_Q16 ) >= ( silk_int32_MAX >> 1 ) ) { psEncCtrl->Gains_Q16[ k ] = silk_int32_MAX; } else { psEncCtrl->Gains_Q16[ k ] = silk_SMULWW( psEncCtrl->Gains_Q16[ k ], gain_mult_Q16 ); } } /* Bandwidth expansion for synthesis filter shaping */ silk_bwexpander_32( AR2_Q24, psEnc->sCmn.shapingLPCOrder, BWExp2_Q16 ); /* Compute noise shaping filter coefficients */ silk_memcpy( AR1_Q24, AR2_Q24, psEnc->sCmn.shapingLPCOrder * sizeof( opus_int32 ) ); /* Bandwidth expansion for analysis filter shaping */ silk_assert( BWExp1_Q16 <= SILK_FIX_CONST( 1.0, 16 ) ); silk_bwexpander_32( AR1_Q24, psEnc->sCmn.shapingLPCOrder, BWExp1_Q16 ); /* Ratio of prediction gains, in energy domain */ pre_nrg_Q30 = silk_LPC_inverse_pred_gain_Q24( AR2_Q24, psEnc->sCmn.shapingLPCOrder ); nrg = silk_LPC_inverse_pred_gain_Q24( AR1_Q24, psEnc->sCmn.shapingLPCOrder ); /*psEncCtrl->GainsPre[ k ] = 1.0f - 0.7f * ( 1.0f - pre_nrg / nrg ) = 0.3f + 0.7f * pre_nrg / nrg;*/ pre_nrg_Q30 = silk_LSHIFT32( silk_SMULWB( pre_nrg_Q30, SILK_FIX_CONST( 0.7, 15 ) ), 1 ); psEncCtrl->GainsPre_Q14[ k ] = ( opus_int ) SILK_FIX_CONST( 0.3, 14 ) + silk_DIV32_varQ( pre_nrg_Q30, nrg, 14 ); /* Convert to monic warped prediction coefficients and limit absolute values */ limit_warped_coefs( AR2_Q24, AR1_Q24, warping_Q16, SILK_FIX_CONST( 3.999, 24 ), psEnc->sCmn.shapingLPCOrder ); /* Convert from Q24 to Q13 and store in int16 */ for( i = 0; i < psEnc->sCmn.shapingLPCOrder; i++ ) { psEncCtrl->AR1_Q13[ k * MAX_SHAPE_LPC_ORDER + i ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( AR1_Q24[ i ], 11 ) ); psEncCtrl->AR2_Q13[ k * MAX_SHAPE_LPC_ORDER + i ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( AR2_Q24[ i ], 11 ) ); } } /*****************/ /* Gain tweaking */ /*****************/ /* Increase gains during low speech activity and put lower limit on gains */ gain_mult_Q16 = silk_log2lin( -silk_SMLAWB( -SILK_FIX_CONST( 16.0, 7 ), SNR_adj_dB_Q7, SILK_FIX_CONST( 0.16, 16 ) ) ); gain_add_Q16 = silk_log2lin( silk_SMLAWB( SILK_FIX_CONST( 16.0, 7 ), SILK_FIX_CONST( MIN_QGAIN_DB, 7 ), SILK_FIX_CONST( 0.16, 16 ) ) ); silk_assert( gain_mult_Q16 > 0 ); for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { psEncCtrl->Gains_Q16[ k ] = silk_SMULWW( psEncCtrl->Gains_Q16[ k ], gain_mult_Q16 ); silk_assert( psEncCtrl->Gains_Q16[ k ] >= 0 ); psEncCtrl->Gains_Q16[ k ] = silk_ADD_POS_SAT32( psEncCtrl->Gains_Q16[ k ], gain_add_Q16 ); } gain_mult_Q16 = SILK_FIX_CONST( 1.0, 16 ) + silk_RSHIFT_ROUND( silk_MLA( SILK_FIX_CONST( INPUT_TILT, 26 ), psEncCtrl->coding_quality_Q14, SILK_FIX_CONST( HIGH_RATE_INPUT_TILT, 12 ) ), 10 ); for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { psEncCtrl->GainsPre_Q14[ k ] = silk_SMULWB( gain_mult_Q16, psEncCtrl->GainsPre_Q14[ k ] ); } /************************************************/ /* Control low-frequency shaping and noise tilt */ /************************************************/ /* Less low frequency shaping for noisy inputs */ strength_Q16 = silk_MUL( SILK_FIX_CONST( LOW_FREQ_SHAPING, 4 ), silk_SMLAWB( SILK_FIX_CONST( 1.0, 12 ), SILK_FIX_CONST( LOW_QUALITY_LOW_FREQ_SHAPING_DECR, 13 ), psEnc->sCmn.input_quality_bands_Q15[ 0 ] - SILK_FIX_CONST( 1.0, 15 ) ) ); strength_Q16 = silk_RSHIFT( silk_MUL( strength_Q16, psEnc->sCmn.speech_activity_Q8 ), 8 ); if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { /* Reduce low frequencies quantization noise for periodic signals, depending on pitch lag */ /*f = 400; freqz([1, -0.98 + 2e-4 * f], [1, -0.97 + 7e-4 * f], 2^12, Fs); axis([0, 1000, -10, 1])*/ opus_int fs_kHz_inv = silk_DIV32_16( SILK_FIX_CONST( 0.2, 14 ), psEnc->sCmn.fs_kHz ); for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { b_Q14 = fs_kHz_inv + silk_DIV32_16( SILK_FIX_CONST( 3.0, 14 ), psEncCtrl->pitchL[ k ] ); /* Pack two coefficients in one int32 */ psEncCtrl->LF_shp_Q14[ k ] = silk_LSHIFT( SILK_FIX_CONST( 1.0, 14 ) - b_Q14 - silk_SMULWB( strength_Q16, b_Q14 ), 16 ); psEncCtrl->LF_shp_Q14[ k ] |= (opus_uint16)( b_Q14 - SILK_FIX_CONST( 1.0, 14 ) ); } silk_assert( SILK_FIX_CONST( HARM_HP_NOISE_COEF, 24 ) < SILK_FIX_CONST( 0.5, 24 ) ); /* Guarantees that second argument to SMULWB() is within range of an opus_int16*/ Tilt_Q16 = - SILK_FIX_CONST( HP_NOISE_COEF, 16 ) - silk_SMULWB( SILK_FIX_CONST( 1.0, 16 ) - SILK_FIX_CONST( HP_NOISE_COEF, 16 ), silk_SMULWB( SILK_FIX_CONST( HARM_HP_NOISE_COEF, 24 ), psEnc->sCmn.speech_activity_Q8 ) ); } else { b_Q14 = silk_DIV32_16( 21299, psEnc->sCmn.fs_kHz ); /* 1.3_Q0 = 21299_Q14*/ /* Pack two coefficients in one int32 */ psEncCtrl->LF_shp_Q14[ 0 ] = silk_LSHIFT( SILK_FIX_CONST( 1.0, 14 ) - b_Q14 - silk_SMULWB( strength_Q16, silk_SMULWB( SILK_FIX_CONST( 0.6, 16 ), b_Q14 ) ), 16 ); psEncCtrl->LF_shp_Q14[ 0 ] |= (opus_uint16)( b_Q14 - SILK_FIX_CONST( 1.0, 14 ) ); for( k = 1; k < psEnc->sCmn.nb_subfr; k++ ) { psEncCtrl->LF_shp_Q14[ k ] = psEncCtrl->LF_shp_Q14[ 0 ]; } Tilt_Q16 = -SILK_FIX_CONST( HP_NOISE_COEF, 16 ); } /****************************/ /* HARMONIC SHAPING CONTROL */ /****************************/ /* Control boosting of harmonic frequencies */ HarmBoost_Q16 = silk_SMULWB( silk_SMULWB( SILK_FIX_CONST( 1.0, 17 ) - silk_LSHIFT( psEncCtrl->coding_quality_Q14, 3 ), psEnc->LTPCorr_Q15 ), SILK_FIX_CONST( LOW_RATE_HARMONIC_BOOST, 16 ) ); /* More harmonic boost for noisy input signals */ HarmBoost_Q16 = silk_SMLAWB( HarmBoost_Q16, SILK_FIX_CONST( 1.0, 16 ) - silk_LSHIFT( psEncCtrl->input_quality_Q14, 2 ), SILK_FIX_CONST( LOW_INPUT_QUALITY_HARMONIC_BOOST, 16 ) ); if( USE_HARM_SHAPING && psEnc->sCmn.indices.signalType == TYPE_VOICED ) { /* More harmonic noise shaping for high bitrates or noisy input */ HarmShapeGain_Q16 = silk_SMLAWB( SILK_FIX_CONST( HARMONIC_SHAPING, 16 ), SILK_FIX_CONST( 1.0, 16 ) - silk_SMULWB( SILK_FIX_CONST( 1.0, 18 ) - silk_LSHIFT( psEncCtrl->coding_quality_Q14, 4 ), psEncCtrl->input_quality_Q14 ), SILK_FIX_CONST( HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING, 16 ) ); /* Less harmonic noise shaping for less periodic signals */ HarmShapeGain_Q16 = silk_SMULWB( silk_LSHIFT( HarmShapeGain_Q16, 1 ), silk_SQRT_APPROX( silk_LSHIFT( psEnc->LTPCorr_Q15, 15 ) ) ); } else { HarmShapeGain_Q16 = 0; } /*************************/ /* Smooth over subframes */ /*************************/ for( k = 0; k < MAX_NB_SUBFR; k++ ) { psShapeSt->HarmBoost_smth_Q16 = silk_SMLAWB( psShapeSt->HarmBoost_smth_Q16, HarmBoost_Q16 - psShapeSt->HarmBoost_smth_Q16, SILK_FIX_CONST( SUBFR_SMTH_COEF, 16 ) ); psShapeSt->HarmShapeGain_smth_Q16 = silk_SMLAWB( psShapeSt->HarmShapeGain_smth_Q16, HarmShapeGain_Q16 - psShapeSt->HarmShapeGain_smth_Q16, SILK_FIX_CONST( SUBFR_SMTH_COEF, 16 ) ); psShapeSt->Tilt_smth_Q16 = silk_SMLAWB( psShapeSt->Tilt_smth_Q16, Tilt_Q16 - psShapeSt->Tilt_smth_Q16, SILK_FIX_CONST( SUBFR_SMTH_COEF, 16 ) ); psEncCtrl->HarmBoost_Q14[ k ] = ( opus_int )silk_RSHIFT_ROUND( psShapeSt->HarmBoost_smth_Q16, 2 ); psEncCtrl->HarmShapeGain_Q14[ k ] = ( opus_int )silk_RSHIFT_ROUND( psShapeSt->HarmShapeGain_smth_Q16, 2 ); psEncCtrl->Tilt_Q14[ k ] = ( opus_int )silk_RSHIFT_ROUND( psShapeSt->Tilt_smth_Q16, 2 ); } RESTORE_STACK; } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/mips/prefilter_FIX_mipsr1.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef __PREFILTER_FIX_MIPSR1_H__ #define __PREFILTER_FIX_MIPSR1_H__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" #include "stack_alloc.h" #include "tuning_parameters.h" #define OVERRIDE_silk_warped_LPC_analysis_filter_FIX void silk_warped_LPC_analysis_filter_FIX( opus_int32 state[], /* I/O State [order + 1] */ opus_int32 res_Q2[], /* O Residual signal [length] */ const opus_int16 coef_Q13[], /* I Coefficients [order] */ const opus_int16 input[], /* I Input signal [length] */ const opus_int16 lambda_Q16, /* I Warping factor */ const opus_int length, /* I Length of input signal */ const opus_int order, /* I Filter order (even) */ int arch ) { opus_int n, i; opus_int32 acc_Q11, acc_Q22, tmp1, tmp2, tmp3, tmp4; opus_int32 state_cur, state_next; (void)arch; /* Order must be even */ /* Length must be even */ silk_assert( ( order & 1 ) == 0 ); silk_assert( ( length & 1 ) == 0 ); for( n = 0; n < length; n+=2 ) { /* Output of lowpass section */ tmp2 = silk_SMLAWB( state[ 0 ], state[ 1 ], lambda_Q16 ); state_cur = silk_LSHIFT( input[ n ], 14 ); /* Output of allpass section */ tmp1 = silk_SMLAWB( state[ 1 ], state[ 2 ] - tmp2, lambda_Q16 ); state_next = tmp2; acc_Q11 = silk_RSHIFT( order, 1 ); acc_Q11 = silk_SMLAWB( acc_Q11, tmp2, coef_Q13[ 0 ] ); /* Output of lowpass section */ tmp4 = silk_SMLAWB( state_cur, state_next, lambda_Q16 ); state[ 0 ] = silk_LSHIFT( input[ n+1 ], 14 ); /* Output of allpass section */ tmp3 = silk_SMLAWB( state_next, tmp1 - tmp4, lambda_Q16 ); state[ 1 ] = tmp4; acc_Q22 = silk_RSHIFT( order, 1 ); acc_Q22 = silk_SMLAWB( acc_Q22, tmp4, coef_Q13[ 0 ] ); /* Loop over allpass sections */ for( i = 2; i < order; i += 2 ) { /* Output of allpass section */ tmp2 = silk_SMLAWB( state[ i ], state[ i + 1 ] - tmp1, lambda_Q16 ); state_cur = tmp1; acc_Q11 = silk_SMLAWB( acc_Q11, tmp1, coef_Q13[ i - 1 ] ); /* Output of allpass section */ tmp1 = silk_SMLAWB( state[ i + 1 ], state[ i + 2 ] - tmp2, lambda_Q16 ); state_next = tmp2; acc_Q11 = silk_SMLAWB( acc_Q11, tmp2, coef_Q13[ i ] ); /* Output of allpass section */ tmp4 = silk_SMLAWB( state_cur, state_next - tmp3, lambda_Q16 ); state[ i ] = tmp3; acc_Q22 = silk_SMLAWB( acc_Q22, tmp3, coef_Q13[ i - 1 ] ); /* Output of allpass section */ tmp3 = silk_SMLAWB( state_next, tmp1 - tmp4, lambda_Q16 ); state[ i + 1 ] = tmp4; acc_Q22 = silk_SMLAWB( acc_Q22, tmp4, coef_Q13[ i ] ); } acc_Q11 = silk_SMLAWB( acc_Q11, tmp1, coef_Q13[ order - 1 ] ); res_Q2[ n ] = silk_LSHIFT( (opus_int32)input[ n ], 2 ) - silk_RSHIFT_ROUND( acc_Q11, 9 ); state[ order ] = tmp3; acc_Q22 = silk_SMLAWB( acc_Q22, tmp3, coef_Q13[ order - 1 ] ); res_Q2[ n+1 ] = silk_LSHIFT( (opus_int32)input[ n+1 ], 2 ) - silk_RSHIFT_ROUND( acc_Q22, 9 ); } } /* Prefilter for finding Quantizer input signal */ #define OVERRIDE_silk_prefilt_FIX static inline void silk_prefilt_FIX( silk_prefilter_state_FIX *P, /* I/O state */ opus_int32 st_res_Q12[], /* I short term residual signal */ opus_int32 xw_Q3[], /* O prefiltered signal */ opus_int32 HarmShapeFIRPacked_Q12, /* I Harmonic shaping coeficients */ opus_int Tilt_Q14, /* I Tilt shaping coeficient */ opus_int32 LF_shp_Q14, /* I Low-frequancy shaping coeficients */ opus_int lag, /* I Lag for harmonic shaping */ opus_int length /* I Length of signals */ ) { opus_int i, idx, LTP_shp_buf_idx; opus_int32 n_LTP_Q12, n_Tilt_Q10, n_LF_Q10; opus_int32 sLF_MA_shp_Q12, sLF_AR_shp_Q12; opus_int16 *LTP_shp_buf; /* To speed up use temp variables instead of using the struct */ LTP_shp_buf = P->sLTP_shp; LTP_shp_buf_idx = P->sLTP_shp_buf_idx; sLF_AR_shp_Q12 = P->sLF_AR_shp_Q12; sLF_MA_shp_Q12 = P->sLF_MA_shp_Q12; if( lag > 0 ) { for( i = 0; i < length; i++ ) { /* unrolled loop */ silk_assert( HARM_SHAPE_FIR_TAPS == 3 ); idx = lag + LTP_shp_buf_idx; n_LTP_Q12 = silk_SMULBB( LTP_shp_buf[ ( idx - HARM_SHAPE_FIR_TAPS / 2 - 1) & LTP_MASK ], HarmShapeFIRPacked_Q12 ); n_LTP_Q12 = silk_SMLABT( n_LTP_Q12, LTP_shp_buf[ ( idx - HARM_SHAPE_FIR_TAPS / 2 ) & LTP_MASK ], HarmShapeFIRPacked_Q12 ); n_LTP_Q12 = silk_SMLABB( n_LTP_Q12, LTP_shp_buf[ ( idx - HARM_SHAPE_FIR_TAPS / 2 + 1) & LTP_MASK ], HarmShapeFIRPacked_Q12 ); n_Tilt_Q10 = silk_SMULWB( sLF_AR_shp_Q12, Tilt_Q14 ); n_LF_Q10 = silk_SMLAWB( silk_SMULWT( sLF_AR_shp_Q12, LF_shp_Q14 ), sLF_MA_shp_Q12, LF_shp_Q14 ); sLF_AR_shp_Q12 = silk_SUB32( st_res_Q12[ i ], silk_LSHIFT( n_Tilt_Q10, 2 ) ); sLF_MA_shp_Q12 = silk_SUB32( sLF_AR_shp_Q12, silk_LSHIFT( n_LF_Q10, 2 ) ); LTP_shp_buf_idx = ( LTP_shp_buf_idx - 1 ) & LTP_MASK; LTP_shp_buf[ LTP_shp_buf_idx ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( sLF_MA_shp_Q12, 12 ) ); xw_Q3[i] = silk_RSHIFT_ROUND( silk_SUB32( sLF_MA_shp_Q12, n_LTP_Q12 ), 9 ); } } else { for( i = 0; i < length; i++ ) { n_LTP_Q12 = 0; n_Tilt_Q10 = silk_SMULWB( sLF_AR_shp_Q12, Tilt_Q14 ); n_LF_Q10 = silk_SMLAWB( silk_SMULWT( sLF_AR_shp_Q12, LF_shp_Q14 ), sLF_MA_shp_Q12, LF_shp_Q14 ); sLF_AR_shp_Q12 = silk_SUB32( st_res_Q12[ i ], silk_LSHIFT( n_Tilt_Q10, 2 ) ); sLF_MA_shp_Q12 = silk_SUB32( sLF_AR_shp_Q12, silk_LSHIFT( n_LF_Q10, 2 ) ); LTP_shp_buf_idx = ( LTP_shp_buf_idx - 1 ) & LTP_MASK; LTP_shp_buf[ LTP_shp_buf_idx ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( sLF_MA_shp_Q12, 12 ) ); xw_Q3[i] = silk_RSHIFT_ROUND( sLF_MA_shp_Q12, 9 ); } } /* Copy temp variable back to state */ P->sLF_AR_shp_Q12 = sLF_AR_shp_Q12; P->sLF_MA_shp_Q12 = sLF_MA_shp_Q12; P->sLTP_shp_buf_idx = LTP_shp_buf_idx; } #endif /* __PREFILTER_FIX_MIPSR1_H__ */ ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/mips/warped_autocorrelation_FIX_mipsr1.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef __WARPED_AUTOCORRELATION_FIX_MIPSR1_H__ #define __WARPED_AUTOCORRELATION_FIX_MIPSR1_H__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" #undef QC #define QC 10 #undef QS #define QS 14 /* Autocorrelations for a warped frequency axis */ #define OVERRIDE_silk_warped_autocorrelation_FIX void silk_warped_autocorrelation_FIX( opus_int32 *corr, /* O Result [order + 1] */ opus_int *scale, /* O Scaling of the correlation vector */ const opus_int16 *input, /* I Input data to correlate */ const opus_int warping_Q16, /* I Warping coefficient */ const opus_int length, /* I Length of input */ const opus_int order /* I Correlation order (even) */ ) { opus_int n, i, lsh; opus_int32 tmp1_QS=0, tmp2_QS=0, tmp3_QS=0, tmp4_QS=0, tmp5_QS=0, tmp6_QS=0, tmp7_QS=0, tmp8_QS=0, start_1=0, start_2=0, start_3=0; opus_int32 state_QS[ MAX_SHAPE_LPC_ORDER + 1 ] = { 0 }; opus_int64 corr_QC[ MAX_SHAPE_LPC_ORDER + 1 ] = { 0 }; opus_int64 temp64; opus_int32 val; val = 2 * QS - QC; /* Order must be even */ silk_assert( ( order & 1 ) == 0 ); silk_assert( 2 * QS - QC >= 0 ); /* Loop over samples */ for( n = 0; n < length; n=n+4 ) { tmp1_QS = silk_LSHIFT32( (opus_int32)input[ n ], QS ); start_1 = tmp1_QS; tmp3_QS = silk_LSHIFT32( (opus_int32)input[ n+1], QS ); start_2 = tmp3_QS; tmp5_QS = silk_LSHIFT32( (opus_int32)input[ n+2], QS ); start_3 = tmp5_QS; tmp7_QS = silk_LSHIFT32( (opus_int32)input[ n+3], QS ); /* Loop over allpass sections */ for( i = 0; i < order; i += 2 ) { /* Output of allpass section */ tmp2_QS = silk_SMLAWB( state_QS[ i ], state_QS[ i + 1 ] - tmp1_QS, warping_Q16 ); corr_QC[ i ] = __builtin_mips_madd( corr_QC[ i ], tmp1_QS, start_1); tmp4_QS = silk_SMLAWB( tmp1_QS, tmp2_QS - tmp3_QS, warping_Q16 ); corr_QC[ i ] = __builtin_mips_madd( corr_QC[ i ], tmp3_QS, start_2); tmp6_QS = silk_SMLAWB( tmp3_QS, tmp4_QS - tmp5_QS, warping_Q16 ); corr_QC[ i ] = __builtin_mips_madd( corr_QC[ i ], tmp5_QS, start_3); tmp8_QS = silk_SMLAWB( tmp5_QS, tmp6_QS - tmp7_QS, warping_Q16 ); state_QS[ i ] = tmp7_QS; corr_QC[ i ] = __builtin_mips_madd( corr_QC[ i ], tmp7_QS, state_QS[0]); /* Output of allpass section */ tmp1_QS = silk_SMLAWB( state_QS[ i + 1 ], state_QS[ i + 2 ] - tmp2_QS, warping_Q16 ); corr_QC[ i+1 ] = __builtin_mips_madd( corr_QC[ i+1 ], tmp2_QS, start_1); tmp3_QS = silk_SMLAWB( tmp2_QS, tmp1_QS - tmp4_QS, warping_Q16 ); corr_QC[ i+1 ] = __builtin_mips_madd( corr_QC[ i+1 ], tmp4_QS, start_2); tmp5_QS = silk_SMLAWB( tmp4_QS, tmp3_QS - tmp6_QS, warping_Q16 ); corr_QC[ i+1 ] = __builtin_mips_madd( corr_QC[ i+1 ], tmp6_QS, start_3); tmp7_QS = silk_SMLAWB( tmp6_QS, tmp5_QS - tmp8_QS, warping_Q16 ); state_QS[ i + 1 ] = tmp8_QS; corr_QC[ i+1 ] = __builtin_mips_madd( corr_QC[ i+1 ], tmp8_QS, state_QS[ 0 ]); } state_QS[ order ] = tmp7_QS; corr_QC[ order ] = __builtin_mips_madd( corr_QC[ order ], tmp1_QS, start_1); corr_QC[ order ] = __builtin_mips_madd( corr_QC[ order ], tmp3_QS, start_2); corr_QC[ order ] = __builtin_mips_madd( corr_QC[ order ], tmp5_QS, start_3); corr_QC[ order ] = __builtin_mips_madd( corr_QC[ order ], tmp7_QS, state_QS[ 0 ]); } for(;n< length; n++ ) { tmp1_QS = silk_LSHIFT32( (opus_int32)input[ n ], QS ); /* Loop over allpass sections */ for( i = 0; i < order; i += 2 ) { /* Output of allpass section */ tmp2_QS = silk_SMLAWB( state_QS[ i ], state_QS[ i + 1 ] - tmp1_QS, warping_Q16 ); state_QS[ i ] = tmp1_QS; corr_QC[ i ] = __builtin_mips_madd( corr_QC[ i ], tmp1_QS, state_QS[ 0 ]); /* Output of allpass section */ tmp1_QS = silk_SMLAWB( state_QS[ i + 1 ], state_QS[ i + 2 ] - tmp2_QS, warping_Q16 ); state_QS[ i + 1 ] = tmp2_QS; corr_QC[ i+1 ] = __builtin_mips_madd( corr_QC[ i+1 ], tmp2_QS, state_QS[ 0 ]); } state_QS[ order ] = tmp1_QS; corr_QC[ order ] = __builtin_mips_madd( corr_QC[ order ], tmp1_QS, state_QS[ 0 ]); } temp64 = corr_QC[ 0 ]; temp64 = __builtin_mips_shilo(temp64, val); lsh = silk_CLZ64( temp64 ) - 35; lsh = silk_LIMIT( lsh, -12 - QC, 30 - QC ); *scale = -( QC + lsh ); silk_assert( *scale >= -30 && *scale <= 12 ); if( lsh >= 0 ) { for( i = 0; i < order + 1; i++ ) { temp64 = corr_QC[ i ]; //temp64 = __builtin_mips_shilo(temp64, val); temp64 = (val >= 0) ? (temp64 >> val) : (temp64 << -val); corr[ i ] = (opus_int32)silk_CHECK_FIT32( __builtin_mips_shilo( temp64, -lsh ) ); } } else { for( i = 0; i < order + 1; i++ ) { temp64 = corr_QC[ i ]; //temp64 = __builtin_mips_shilo(temp64, val); temp64 = (val >= 0) ? (temp64 >> val) : (temp64 << -val); corr[ i ] = (opus_int32)silk_CHECK_FIT32( __builtin_mips_shilo( temp64, -lsh ) ); } } corr_QC[ 0 ] = __builtin_mips_shilo(corr_QC[ 0 ], val); silk_assert( corr_QC[ 0 ] >= 0 ); /* If breaking, decrease QC*/ } #endif /* __WARPED_AUTOCORRELATION_FIX_MIPSR1_H__ */ ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/noise_shape_analysis_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" #include "stack_alloc.h" #include "tuning_parameters.h" /* Compute gain to make warped filter coefficients have a zero mean log frequency response on a */ /* non-warped frequency scale. (So that it can be implemented with a minimum-phase monic filter.) */ /* Note: A monic filter is one with the first coefficient equal to 1.0. In Silk we omit the first */ /* coefficient in an array of coefficients, for monic filters. */ static OPUS_INLINE opus_int32 warped_gain( /* gain in Q16*/ const opus_int32 *coefs_Q24, opus_int lambda_Q16, opus_int order ) { opus_int i; opus_int32 gain_Q24; lambda_Q16 = -lambda_Q16; gain_Q24 = coefs_Q24[ order - 1 ]; for( i = order - 2; i >= 0; i-- ) { gain_Q24 = silk_SMLAWB( coefs_Q24[ i ], gain_Q24, lambda_Q16 ); } gain_Q24 = silk_SMLAWB( SILK_FIX_CONST( 1.0, 24 ), gain_Q24, -lambda_Q16 ); return silk_INVERSE32_varQ( gain_Q24, 40 ); } /* Convert warped filter coefficients to monic pseudo-warped coefficients and limit maximum */ /* amplitude of monic warped coefficients by using bandwidth expansion on the true coefficients */ static OPUS_INLINE void limit_warped_coefs( opus_int32 *coefs_syn_Q24, opus_int32 *coefs_ana_Q24, opus_int lambda_Q16, opus_int32 limit_Q24, opus_int order ) { opus_int i, iter, ind = 0; opus_int32 tmp, maxabs_Q24, chirp_Q16, gain_syn_Q16, gain_ana_Q16; opus_int32 nom_Q16, den_Q24; /* Convert to monic coefficients */ lambda_Q16 = -lambda_Q16; for( i = order - 1; i > 0; i-- ) { coefs_syn_Q24[ i - 1 ] = silk_SMLAWB( coefs_syn_Q24[ i - 1 ], coefs_syn_Q24[ i ], lambda_Q16 ); coefs_ana_Q24[ i - 1 ] = silk_SMLAWB( coefs_ana_Q24[ i - 1 ], coefs_ana_Q24[ i ], lambda_Q16 ); } lambda_Q16 = -lambda_Q16; nom_Q16 = silk_SMLAWB( SILK_FIX_CONST( 1.0, 16 ), -(opus_int32)lambda_Q16, lambda_Q16 ); den_Q24 = silk_SMLAWB( SILK_FIX_CONST( 1.0, 24 ), coefs_syn_Q24[ 0 ], lambda_Q16 ); gain_syn_Q16 = silk_DIV32_varQ( nom_Q16, den_Q24, 24 ); den_Q24 = silk_SMLAWB( SILK_FIX_CONST( 1.0, 24 ), coefs_ana_Q24[ 0 ], lambda_Q16 ); gain_ana_Q16 = silk_DIV32_varQ( nom_Q16, den_Q24, 24 ); for( i = 0; i < order; i++ ) { coefs_syn_Q24[ i ] = silk_SMULWW( gain_syn_Q16, coefs_syn_Q24[ i ] ); coefs_ana_Q24[ i ] = silk_SMULWW( gain_ana_Q16, coefs_ana_Q24[ i ] ); } for( iter = 0; iter < 10; iter++ ) { /* Find maximum absolute value */ maxabs_Q24 = -1; for( i = 0; i < order; i++ ) { tmp = silk_max( silk_abs_int32( coefs_syn_Q24[ i ] ), silk_abs_int32( coefs_ana_Q24[ i ] ) ); if( tmp > maxabs_Q24 ) { maxabs_Q24 = tmp; ind = i; } } if( maxabs_Q24 <= limit_Q24 ) { /* Coefficients are within range - done */ return; } /* Convert back to true warped coefficients */ for( i = 1; i < order; i++ ) { coefs_syn_Q24[ i - 1 ] = silk_SMLAWB( coefs_syn_Q24[ i - 1 ], coefs_syn_Q24[ i ], lambda_Q16 ); coefs_ana_Q24[ i - 1 ] = silk_SMLAWB( coefs_ana_Q24[ i - 1 ], coefs_ana_Q24[ i ], lambda_Q16 ); } gain_syn_Q16 = silk_INVERSE32_varQ( gain_syn_Q16, 32 ); gain_ana_Q16 = silk_INVERSE32_varQ( gain_ana_Q16, 32 ); for( i = 0; i < order; i++ ) { coefs_syn_Q24[ i ] = silk_SMULWW( gain_syn_Q16, coefs_syn_Q24[ i ] ); coefs_ana_Q24[ i ] = silk_SMULWW( gain_ana_Q16, coefs_ana_Q24[ i ] ); } /* Apply bandwidth expansion */ chirp_Q16 = SILK_FIX_CONST( 0.99, 16 ) - silk_DIV32_varQ( silk_SMULWB( maxabs_Q24 - limit_Q24, silk_SMLABB( SILK_FIX_CONST( 0.8, 10 ), SILK_FIX_CONST( 0.1, 10 ), iter ) ), silk_MUL( maxabs_Q24, ind + 1 ), 22 ); silk_bwexpander_32( coefs_syn_Q24, order, chirp_Q16 ); silk_bwexpander_32( coefs_ana_Q24, order, chirp_Q16 ); /* Convert to monic warped coefficients */ lambda_Q16 = -lambda_Q16; for( i = order - 1; i > 0; i-- ) { coefs_syn_Q24[ i - 1 ] = silk_SMLAWB( coefs_syn_Q24[ i - 1 ], coefs_syn_Q24[ i ], lambda_Q16 ); coefs_ana_Q24[ i - 1 ] = silk_SMLAWB( coefs_ana_Q24[ i - 1 ], coefs_ana_Q24[ i ], lambda_Q16 ); } lambda_Q16 = -lambda_Q16; nom_Q16 = silk_SMLAWB( SILK_FIX_CONST( 1.0, 16 ), -(opus_int32)lambda_Q16, lambda_Q16 ); den_Q24 = silk_SMLAWB( SILK_FIX_CONST( 1.0, 24 ), coefs_syn_Q24[ 0 ], lambda_Q16 ); gain_syn_Q16 = silk_DIV32_varQ( nom_Q16, den_Q24, 24 ); den_Q24 = silk_SMLAWB( SILK_FIX_CONST( 1.0, 24 ), coefs_ana_Q24[ 0 ], lambda_Q16 ); gain_ana_Q16 = silk_DIV32_varQ( nom_Q16, den_Q24, 24 ); for( i = 0; i < order; i++ ) { coefs_syn_Q24[ i ] = silk_SMULWW( gain_syn_Q16, coefs_syn_Q24[ i ] ); coefs_ana_Q24[ i ] = silk_SMULWW( gain_ana_Q16, coefs_ana_Q24[ i ] ); } } silk_assert( 0 ); } #if defined(MIPSr1_ASM) #include "mips/noise_shape_analysis_FIX_mipsr1.h" #endif /**************************************************************/ /* Compute noise shaping coefficients and initial gain values */ /**************************************************************/ #ifndef OVERRIDE_silk_noise_shape_analysis_FIX void silk_noise_shape_analysis_FIX( silk_encoder_state_FIX *psEnc, /* I/O Encoder state FIX */ silk_encoder_control_FIX *psEncCtrl, /* I/O Encoder control FIX */ const opus_int16 *pitch_res, /* I LPC residual from pitch analysis */ const opus_int16 *x, /* I Input signal [ frame_length + la_shape ] */ int arch /* I Run-time architecture */ ) { silk_shape_state_FIX *psShapeSt = &psEnc->sShape; opus_int k, i, nSamples, Qnrg, b_Q14, warping_Q16, scale = 0; opus_int32 SNR_adj_dB_Q7, HarmBoost_Q16, HarmShapeGain_Q16, Tilt_Q16, tmp32; opus_int32 nrg, pre_nrg_Q30, log_energy_Q7, log_energy_prev_Q7, energy_variation_Q7; opus_int32 delta_Q16, BWExp1_Q16, BWExp2_Q16, gain_mult_Q16, gain_add_Q16, strength_Q16, b_Q8; opus_int32 auto_corr[ MAX_SHAPE_LPC_ORDER + 1 ]; opus_int32 refl_coef_Q16[ MAX_SHAPE_LPC_ORDER ]; opus_int32 AR1_Q24[ MAX_SHAPE_LPC_ORDER ]; opus_int32 AR2_Q24[ MAX_SHAPE_LPC_ORDER ]; VARDECL( opus_int16, x_windowed ); const opus_int16 *x_ptr, *pitch_res_ptr; SAVE_STACK; /* Point to start of first LPC analysis block */ x_ptr = x - psEnc->sCmn.la_shape; /****************/ /* GAIN CONTROL */ /****************/ SNR_adj_dB_Q7 = psEnc->sCmn.SNR_dB_Q7; /* Input quality is the average of the quality in the lowest two VAD bands */ psEncCtrl->input_quality_Q14 = ( opus_int )silk_RSHIFT( (opus_int32)psEnc->sCmn.input_quality_bands_Q15[ 0 ] + psEnc->sCmn.input_quality_bands_Q15[ 1 ], 2 ); /* Coding quality level, between 0.0_Q0 and 1.0_Q0, but in Q14 */ psEncCtrl->coding_quality_Q14 = silk_RSHIFT( silk_sigm_Q15( silk_RSHIFT_ROUND( SNR_adj_dB_Q7 - SILK_FIX_CONST( 20.0, 7 ), 4 ) ), 1 ); /* Reduce coding SNR during low speech activity */ if( psEnc->sCmn.useCBR == 0 ) { b_Q8 = SILK_FIX_CONST( 1.0, 8 ) - psEnc->sCmn.speech_activity_Q8; b_Q8 = silk_SMULWB( silk_LSHIFT( b_Q8, 8 ), b_Q8 ); SNR_adj_dB_Q7 = silk_SMLAWB( SNR_adj_dB_Q7, silk_SMULBB( SILK_FIX_CONST( -BG_SNR_DECR_dB, 7 ) >> ( 4 + 1 ), b_Q8 ), /* Q11*/ silk_SMULWB( SILK_FIX_CONST( 1.0, 14 ) + psEncCtrl->input_quality_Q14, psEncCtrl->coding_quality_Q14 ) ); /* Q12*/ } if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { /* Reduce gains for periodic signals */ SNR_adj_dB_Q7 = silk_SMLAWB( SNR_adj_dB_Q7, SILK_FIX_CONST( HARM_SNR_INCR_dB, 8 ), psEnc->LTPCorr_Q15 ); } else { /* For unvoiced signals and low-quality input, adjust the quality slower than SNR_dB setting */ SNR_adj_dB_Q7 = silk_SMLAWB( SNR_adj_dB_Q7, silk_SMLAWB( SILK_FIX_CONST( 6.0, 9 ), -SILK_FIX_CONST( 0.4, 18 ), psEnc->sCmn.SNR_dB_Q7 ), SILK_FIX_CONST( 1.0, 14 ) - psEncCtrl->input_quality_Q14 ); } /*************************/ /* SPARSENESS PROCESSING */ /*************************/ /* Set quantizer offset */ if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { /* Initially set to 0; may be overruled in process_gains(..) */ psEnc->sCmn.indices.quantOffsetType = 0; psEncCtrl->sparseness_Q8 = 0; } else { /* Sparseness measure, based on relative fluctuations of energy per 2 milliseconds */ nSamples = silk_LSHIFT( psEnc->sCmn.fs_kHz, 1 ); energy_variation_Q7 = 0; log_energy_prev_Q7 = 0; pitch_res_ptr = pitch_res; for( k = 0; k < silk_SMULBB( SUB_FRAME_LENGTH_MS, psEnc->sCmn.nb_subfr ) / 2; k++ ) { silk_sum_sqr_shift( &nrg, &scale, pitch_res_ptr, nSamples ); nrg += silk_RSHIFT( nSamples, scale ); /* Q(-scale)*/ log_energy_Q7 = silk_lin2log( nrg ); if( k > 0 ) { energy_variation_Q7 += silk_abs( log_energy_Q7 - log_energy_prev_Q7 ); } log_energy_prev_Q7 = log_energy_Q7; pitch_res_ptr += nSamples; } psEncCtrl->sparseness_Q8 = silk_RSHIFT( silk_sigm_Q15( silk_SMULWB( energy_variation_Q7 - SILK_FIX_CONST( 5.0, 7 ), SILK_FIX_CONST( 0.1, 16 ) ) ), 7 ); /* Set quantization offset depending on sparseness measure */ if( psEncCtrl->sparseness_Q8 > SILK_FIX_CONST( SPARSENESS_THRESHOLD_QNT_OFFSET, 8 ) ) { psEnc->sCmn.indices.quantOffsetType = 0; } else { psEnc->sCmn.indices.quantOffsetType = 1; } /* Increase coding SNR for sparse signals */ SNR_adj_dB_Q7 = silk_SMLAWB( SNR_adj_dB_Q7, SILK_FIX_CONST( SPARSE_SNR_INCR_dB, 15 ), psEncCtrl->sparseness_Q8 - SILK_FIX_CONST( 0.5, 8 ) ); } /*******************************/ /* Control bandwidth expansion */ /*******************************/ /* More BWE for signals with high prediction gain */ strength_Q16 = silk_SMULWB( psEncCtrl->predGain_Q16, SILK_FIX_CONST( FIND_PITCH_WHITE_NOISE_FRACTION, 16 ) ); BWExp1_Q16 = BWExp2_Q16 = silk_DIV32_varQ( SILK_FIX_CONST( BANDWIDTH_EXPANSION, 16 ), silk_SMLAWW( SILK_FIX_CONST( 1.0, 16 ), strength_Q16, strength_Q16 ), 16 ); delta_Q16 = silk_SMULWB( SILK_FIX_CONST( 1.0, 16 ) - silk_SMULBB( 3, psEncCtrl->coding_quality_Q14 ), SILK_FIX_CONST( LOW_RATE_BANDWIDTH_EXPANSION_DELTA, 16 ) ); BWExp1_Q16 = silk_SUB32( BWExp1_Q16, delta_Q16 ); BWExp2_Q16 = silk_ADD32( BWExp2_Q16, delta_Q16 ); /* BWExp1 will be applied after BWExp2, so make it relative */ BWExp1_Q16 = silk_DIV32_16( silk_LSHIFT( BWExp1_Q16, 14 ), silk_RSHIFT( BWExp2_Q16, 2 ) ); if( psEnc->sCmn.warping_Q16 > 0 ) { /* Slightly more warping in analysis will move quantization noise up in frequency, where it's better masked */ warping_Q16 = silk_SMLAWB( psEnc->sCmn.warping_Q16, (opus_int32)psEncCtrl->coding_quality_Q14, SILK_FIX_CONST( 0.01, 18 ) ); } else { warping_Q16 = 0; } /********************************************/ /* Compute noise shaping AR coefs and gains */ /********************************************/ ALLOC( x_windowed, psEnc->sCmn.shapeWinLength, opus_int16 ); for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { /* Apply window: sine slope followed by flat part followed by cosine slope */ opus_int shift, slope_part, flat_part; flat_part = psEnc->sCmn.fs_kHz * 3; slope_part = silk_RSHIFT( psEnc->sCmn.shapeWinLength - flat_part, 1 ); silk_apply_sine_window( x_windowed, x_ptr, 1, slope_part ); shift = slope_part; silk_memcpy( x_windowed + shift, x_ptr + shift, flat_part * sizeof(opus_int16) ); shift += flat_part; silk_apply_sine_window( x_windowed + shift, x_ptr + shift, 2, slope_part ); /* Update pointer: next LPC analysis block */ x_ptr += psEnc->sCmn.subfr_length; if( psEnc->sCmn.warping_Q16 > 0 ) { /* Calculate warped auto correlation */ silk_warped_autocorrelation_FIX( auto_corr, &scale, x_windowed, warping_Q16, psEnc->sCmn.shapeWinLength, psEnc->sCmn.shapingLPCOrder ); } else { /* Calculate regular auto correlation */ silk_autocorr( auto_corr, &scale, x_windowed, psEnc->sCmn.shapeWinLength, psEnc->sCmn.shapingLPCOrder + 1, arch ); } /* Add white noise, as a fraction of energy */ auto_corr[0] = silk_ADD32( auto_corr[0], silk_max_32( silk_SMULWB( silk_RSHIFT( auto_corr[ 0 ], 4 ), SILK_FIX_CONST( SHAPE_WHITE_NOISE_FRACTION, 20 ) ), 1 ) ); /* Calculate the reflection coefficients using schur */ nrg = silk_schur64( refl_coef_Q16, auto_corr, psEnc->sCmn.shapingLPCOrder ); silk_assert( nrg >= 0 ); /* Convert reflection coefficients to prediction coefficients */ silk_k2a_Q16( AR2_Q24, refl_coef_Q16, psEnc->sCmn.shapingLPCOrder ); Qnrg = -scale; /* range: -12...30*/ silk_assert( Qnrg >= -12 ); silk_assert( Qnrg <= 30 ); /* Make sure that Qnrg is an even number */ if( Qnrg & 1 ) { Qnrg -= 1; nrg >>= 1; } tmp32 = silk_SQRT_APPROX( nrg ); Qnrg >>= 1; /* range: -6...15*/ psEncCtrl->Gains_Q16[ k ] = silk_LSHIFT_SAT32( tmp32, 16 - Qnrg ); if( psEnc->sCmn.warping_Q16 > 0 ) { /* Adjust gain for warping */ gain_mult_Q16 = warped_gain( AR2_Q24, warping_Q16, psEnc->sCmn.shapingLPCOrder ); silk_assert( psEncCtrl->Gains_Q16[ k ] >= 0 ); if ( silk_SMULWW( silk_RSHIFT_ROUND( psEncCtrl->Gains_Q16[ k ], 1 ), gain_mult_Q16 ) >= ( silk_int32_MAX >> 1 ) ) { psEncCtrl->Gains_Q16[ k ] = silk_int32_MAX; } else { psEncCtrl->Gains_Q16[ k ] = silk_SMULWW( psEncCtrl->Gains_Q16[ k ], gain_mult_Q16 ); } } /* Bandwidth expansion for synthesis filter shaping */ silk_bwexpander_32( AR2_Q24, psEnc->sCmn.shapingLPCOrder, BWExp2_Q16 ); /* Compute noise shaping filter coefficients */ silk_memcpy( AR1_Q24, AR2_Q24, psEnc->sCmn.shapingLPCOrder * sizeof( opus_int32 ) ); /* Bandwidth expansion for analysis filter shaping */ silk_assert( BWExp1_Q16 <= SILK_FIX_CONST( 1.0, 16 ) ); silk_bwexpander_32( AR1_Q24, psEnc->sCmn.shapingLPCOrder, BWExp1_Q16 ); /* Ratio of prediction gains, in energy domain */ pre_nrg_Q30 = silk_LPC_inverse_pred_gain_Q24( AR2_Q24, psEnc->sCmn.shapingLPCOrder ); nrg = silk_LPC_inverse_pred_gain_Q24( AR1_Q24, psEnc->sCmn.shapingLPCOrder ); /*psEncCtrl->GainsPre[ k ] = 1.0f - 0.7f * ( 1.0f - pre_nrg / nrg ) = 0.3f + 0.7f * pre_nrg / nrg;*/ pre_nrg_Q30 = silk_LSHIFT32( silk_SMULWB( pre_nrg_Q30, SILK_FIX_CONST( 0.7, 15 ) ), 1 ); psEncCtrl->GainsPre_Q14[ k ] = ( opus_int ) SILK_FIX_CONST( 0.3, 14 ) + silk_DIV32_varQ( pre_nrg_Q30, nrg, 14 ); /* Convert to monic warped prediction coefficients and limit absolute values */ limit_warped_coefs( AR2_Q24, AR1_Q24, warping_Q16, SILK_FIX_CONST( 3.999, 24 ), psEnc->sCmn.shapingLPCOrder ); /* Convert from Q24 to Q13 and store in int16 */ for( i = 0; i < psEnc->sCmn.shapingLPCOrder; i++ ) { psEncCtrl->AR1_Q13[ k * MAX_SHAPE_LPC_ORDER + i ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( AR1_Q24[ i ], 11 ) ); psEncCtrl->AR2_Q13[ k * MAX_SHAPE_LPC_ORDER + i ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( AR2_Q24[ i ], 11 ) ); } } /*****************/ /* Gain tweaking */ /*****************/ /* Increase gains during low speech activity and put lower limit on gains */ gain_mult_Q16 = silk_log2lin( -silk_SMLAWB( -SILK_FIX_CONST( 16.0, 7 ), SNR_adj_dB_Q7, SILK_FIX_CONST( 0.16, 16 ) ) ); gain_add_Q16 = silk_log2lin( silk_SMLAWB( SILK_FIX_CONST( 16.0, 7 ), SILK_FIX_CONST( MIN_QGAIN_DB, 7 ), SILK_FIX_CONST( 0.16, 16 ) ) ); silk_assert( gain_mult_Q16 > 0 ); for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { psEncCtrl->Gains_Q16[ k ] = silk_SMULWW( psEncCtrl->Gains_Q16[ k ], gain_mult_Q16 ); silk_assert( psEncCtrl->Gains_Q16[ k ] >= 0 ); psEncCtrl->Gains_Q16[ k ] = silk_ADD_POS_SAT32( psEncCtrl->Gains_Q16[ k ], gain_add_Q16 ); } gain_mult_Q16 = SILK_FIX_CONST( 1.0, 16 ) + silk_RSHIFT_ROUND( silk_MLA( SILK_FIX_CONST( INPUT_TILT, 26 ), psEncCtrl->coding_quality_Q14, SILK_FIX_CONST( HIGH_RATE_INPUT_TILT, 12 ) ), 10 ); for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { psEncCtrl->GainsPre_Q14[ k ] = silk_SMULWB( gain_mult_Q16, psEncCtrl->GainsPre_Q14[ k ] ); } /************************************************/ /* Control low-frequency shaping and noise tilt */ /************************************************/ /* Less low frequency shaping for noisy inputs */ strength_Q16 = silk_MUL( SILK_FIX_CONST( LOW_FREQ_SHAPING, 4 ), silk_SMLAWB( SILK_FIX_CONST( 1.0, 12 ), SILK_FIX_CONST( LOW_QUALITY_LOW_FREQ_SHAPING_DECR, 13 ), psEnc->sCmn.input_quality_bands_Q15[ 0 ] - SILK_FIX_CONST( 1.0, 15 ) ) ); strength_Q16 = silk_RSHIFT( silk_MUL( strength_Q16, psEnc->sCmn.speech_activity_Q8 ), 8 ); if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { /* Reduce low frequencies quantization noise for periodic signals, depending on pitch lag */ /*f = 400; freqz([1, -0.98 + 2e-4 * f], [1, -0.97 + 7e-4 * f], 2^12, Fs); axis([0, 1000, -10, 1])*/ opus_int fs_kHz_inv = silk_DIV32_16( SILK_FIX_CONST( 0.2, 14 ), psEnc->sCmn.fs_kHz ); for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { b_Q14 = fs_kHz_inv + silk_DIV32_16( SILK_FIX_CONST( 3.0, 14 ), psEncCtrl->pitchL[ k ] ); /* Pack two coefficients in one int32 */ psEncCtrl->LF_shp_Q14[ k ] = silk_LSHIFT( SILK_FIX_CONST( 1.0, 14 ) - b_Q14 - silk_SMULWB( strength_Q16, b_Q14 ), 16 ); psEncCtrl->LF_shp_Q14[ k ] |= (opus_uint16)( b_Q14 - SILK_FIX_CONST( 1.0, 14 ) ); } silk_assert( SILK_FIX_CONST( HARM_HP_NOISE_COEF, 24 ) < SILK_FIX_CONST( 0.5, 24 ) ); /* Guarantees that second argument to SMULWB() is within range of an opus_int16*/ Tilt_Q16 = - SILK_FIX_CONST( HP_NOISE_COEF, 16 ) - silk_SMULWB( SILK_FIX_CONST( 1.0, 16 ) - SILK_FIX_CONST( HP_NOISE_COEF, 16 ), silk_SMULWB( SILK_FIX_CONST( HARM_HP_NOISE_COEF, 24 ), psEnc->sCmn.speech_activity_Q8 ) ); } else { b_Q14 = silk_DIV32_16( 21299, psEnc->sCmn.fs_kHz ); /* 1.3_Q0 = 21299_Q14*/ /* Pack two coefficients in one int32 */ psEncCtrl->LF_shp_Q14[ 0 ] = silk_LSHIFT( SILK_FIX_CONST( 1.0, 14 ) - b_Q14 - silk_SMULWB( strength_Q16, silk_SMULWB( SILK_FIX_CONST( 0.6, 16 ), b_Q14 ) ), 16 ); psEncCtrl->LF_shp_Q14[ 0 ] |= (opus_uint16)( b_Q14 - SILK_FIX_CONST( 1.0, 14 ) ); for( k = 1; k < psEnc->sCmn.nb_subfr; k++ ) { psEncCtrl->LF_shp_Q14[ k ] = psEncCtrl->LF_shp_Q14[ 0 ]; } Tilt_Q16 = -SILK_FIX_CONST( HP_NOISE_COEF, 16 ); } /****************************/ /* HARMONIC SHAPING CONTROL */ /****************************/ /* Control boosting of harmonic frequencies */ HarmBoost_Q16 = silk_SMULWB( silk_SMULWB( SILK_FIX_CONST( 1.0, 17 ) - silk_LSHIFT( psEncCtrl->coding_quality_Q14, 3 ), psEnc->LTPCorr_Q15 ), SILK_FIX_CONST( LOW_RATE_HARMONIC_BOOST, 16 ) ); /* More harmonic boost for noisy input signals */ HarmBoost_Q16 = silk_SMLAWB( HarmBoost_Q16, SILK_FIX_CONST( 1.0, 16 ) - silk_LSHIFT( psEncCtrl->input_quality_Q14, 2 ), SILK_FIX_CONST( LOW_INPUT_QUALITY_HARMONIC_BOOST, 16 ) ); if( USE_HARM_SHAPING && psEnc->sCmn.indices.signalType == TYPE_VOICED ) { /* More harmonic noise shaping for high bitrates or noisy input */ HarmShapeGain_Q16 = silk_SMLAWB( SILK_FIX_CONST( HARMONIC_SHAPING, 16 ), SILK_FIX_CONST( 1.0, 16 ) - silk_SMULWB( SILK_FIX_CONST( 1.0, 18 ) - silk_LSHIFT( psEncCtrl->coding_quality_Q14, 4 ), psEncCtrl->input_quality_Q14 ), SILK_FIX_CONST( HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING, 16 ) ); /* Less harmonic noise shaping for less periodic signals */ HarmShapeGain_Q16 = silk_SMULWB( silk_LSHIFT( HarmShapeGain_Q16, 1 ), silk_SQRT_APPROX( silk_LSHIFT( psEnc->LTPCorr_Q15, 15 ) ) ); } else { HarmShapeGain_Q16 = 0; } /*************************/ /* Smooth over subframes */ /*************************/ for( k = 0; k < MAX_NB_SUBFR; k++ ) { psShapeSt->HarmBoost_smth_Q16 = silk_SMLAWB( psShapeSt->HarmBoost_smth_Q16, HarmBoost_Q16 - psShapeSt->HarmBoost_smth_Q16, SILK_FIX_CONST( SUBFR_SMTH_COEF, 16 ) ); psShapeSt->HarmShapeGain_smth_Q16 = silk_SMLAWB( psShapeSt->HarmShapeGain_smth_Q16, HarmShapeGain_Q16 - psShapeSt->HarmShapeGain_smth_Q16, SILK_FIX_CONST( SUBFR_SMTH_COEF, 16 ) ); psShapeSt->Tilt_smth_Q16 = silk_SMLAWB( psShapeSt->Tilt_smth_Q16, Tilt_Q16 - psShapeSt->Tilt_smth_Q16, SILK_FIX_CONST( SUBFR_SMTH_COEF, 16 ) ); psEncCtrl->HarmBoost_Q14[ k ] = ( opus_int )silk_RSHIFT_ROUND( psShapeSt->HarmBoost_smth_Q16, 2 ); psEncCtrl->HarmShapeGain_Q14[ k ] = ( opus_int )silk_RSHIFT_ROUND( psShapeSt->HarmShapeGain_smth_Q16, 2 ); psEncCtrl->Tilt_Q14[ k ] = ( opus_int )silk_RSHIFT_ROUND( psShapeSt->Tilt_smth_Q16, 2 ); } RESTORE_STACK; } #endif /* OVERRIDE_silk_noise_shape_analysis_FIX */ ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/pitch_analysis_core_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*********************************************************** * Pitch analyser function ********************************************************** */ #include "SigProc_FIX.h" #include "pitch_est_defines.h" #include "stack_alloc.h" #include "debug.h" #include "pitch.h" #define SCRATCH_SIZE 22 #define SF_LENGTH_4KHZ ( PE_SUBFR_LENGTH_MS * 4 ) #define SF_LENGTH_8KHZ ( PE_SUBFR_LENGTH_MS * 8 ) #define MIN_LAG_4KHZ ( PE_MIN_LAG_MS * 4 ) #define MIN_LAG_8KHZ ( PE_MIN_LAG_MS * 8 ) #define MAX_LAG_4KHZ ( PE_MAX_LAG_MS * 4 ) #define MAX_LAG_8KHZ ( PE_MAX_LAG_MS * 8 - 1 ) #define CSTRIDE_4KHZ ( MAX_LAG_4KHZ + 1 - MIN_LAG_4KHZ ) #define CSTRIDE_8KHZ ( MAX_LAG_8KHZ + 3 - ( MIN_LAG_8KHZ - 2 ) ) #define D_COMP_MIN ( MIN_LAG_8KHZ - 3 ) #define D_COMP_MAX ( MAX_LAG_8KHZ + 4 ) #define D_COMP_STRIDE ( D_COMP_MAX - D_COMP_MIN ) typedef opus_int32 silk_pe_stage3_vals[ PE_NB_STAGE3_LAGS ]; /************************************************************/ /* Internally used functions */ /************************************************************/ static void silk_P_Ana_calc_corr_st3( silk_pe_stage3_vals cross_corr_st3[], /* O 3 DIM correlation array */ const opus_int16 frame[], /* I vector to correlate */ opus_int start_lag, /* I lag offset to search around */ opus_int sf_length, /* I length of a 5 ms subframe */ opus_int nb_subfr, /* I number of subframes */ opus_int complexity, /* I Complexity setting */ int arch /* I Run-time architecture */ ); static void silk_P_Ana_calc_energy_st3( silk_pe_stage3_vals energies_st3[], /* O 3 DIM energy array */ const opus_int16 frame[], /* I vector to calc energy in */ opus_int start_lag, /* I lag offset to search around */ opus_int sf_length, /* I length of one 5 ms subframe */ opus_int nb_subfr, /* I number of subframes */ opus_int complexity, /* I Complexity setting */ int arch /* I Run-time architecture */ ); /*************************************************************/ /* FIXED POINT CORE PITCH ANALYSIS FUNCTION */ /*************************************************************/ opus_int silk_pitch_analysis_core( /* O Voicing estimate: 0 voiced, 1 unvoiced */ const opus_int16 *frame, /* I Signal of length PE_FRAME_LENGTH_MS*Fs_kHz */ opus_int *pitch_out, /* O 4 pitch lag values */ opus_int16 *lagIndex, /* O Lag Index */ opus_int8 *contourIndex, /* O Pitch contour Index */ opus_int *LTPCorr_Q15, /* I/O Normalized correlation; input: value from previous frame */ opus_int prevLag, /* I Last lag of previous frame; set to zero is unvoiced */ const opus_int32 search_thres1_Q16, /* I First stage threshold for lag candidates 0 - 1 */ const opus_int search_thres2_Q13, /* I Final threshold for lag candidates 0 - 1 */ const opus_int Fs_kHz, /* I Sample frequency (kHz) */ const opus_int complexity, /* I Complexity setting, 0-2, where 2 is highest */ const opus_int nb_subfr, /* I number of 5 ms subframes */ int arch /* I Run-time architecture */ ) { VARDECL( opus_int16, frame_8kHz ); VARDECL( opus_int16, frame_4kHz ); opus_int32 filt_state[ 6 ]; const opus_int16 *input_frame_ptr; opus_int i, k, d, j; VARDECL( opus_int16, C ); VARDECL( opus_int32, xcorr32 ); const opus_int16 *target_ptr, *basis_ptr; opus_int32 cross_corr, normalizer, energy, shift, energy_basis, energy_target; opus_int d_srch[ PE_D_SRCH_LENGTH ], Cmax, length_d_srch, length_d_comp; VARDECL( opus_int16, d_comp ); opus_int32 sum, threshold, lag_counter; opus_int CBimax, CBimax_new, CBimax_old, lag, start_lag, end_lag, lag_new; opus_int32 CC[ PE_NB_CBKS_STAGE2_EXT ], CCmax, CCmax_b, CCmax_new_b, CCmax_new; VARDECL( silk_pe_stage3_vals, energies_st3 ); VARDECL( silk_pe_stage3_vals, cross_corr_st3 ); opus_int frame_length, frame_length_8kHz, frame_length_4kHz; opus_int sf_length; opus_int min_lag; opus_int max_lag; opus_int32 contour_bias_Q15, diff; opus_int nb_cbk_search, cbk_size; opus_int32 delta_lag_log2_sqr_Q7, lag_log2_Q7, prevLag_log2_Q7, prev_lag_bias_Q13; const opus_int8 *Lag_CB_ptr; SAVE_STACK; /* Check for valid sampling frequency */ silk_assert( Fs_kHz == 8 || Fs_kHz == 12 || Fs_kHz == 16 ); /* Check for valid complexity setting */ silk_assert( complexity >= SILK_PE_MIN_COMPLEX ); silk_assert( complexity <= SILK_PE_MAX_COMPLEX ); silk_assert( search_thres1_Q16 >= 0 && search_thres1_Q16 <= (1<<16) ); silk_assert( search_thres2_Q13 >= 0 && search_thres2_Q13 <= (1<<13) ); /* Set up frame lengths max / min lag for the sampling frequency */ frame_length = ( PE_LTP_MEM_LENGTH_MS + nb_subfr * PE_SUBFR_LENGTH_MS ) * Fs_kHz; frame_length_4kHz = ( PE_LTP_MEM_LENGTH_MS + nb_subfr * PE_SUBFR_LENGTH_MS ) * 4; frame_length_8kHz = ( PE_LTP_MEM_LENGTH_MS + nb_subfr * PE_SUBFR_LENGTH_MS ) * 8; sf_length = PE_SUBFR_LENGTH_MS * Fs_kHz; min_lag = PE_MIN_LAG_MS * Fs_kHz; max_lag = PE_MAX_LAG_MS * Fs_kHz - 1; /* Resample from input sampled at Fs_kHz to 8 kHz */ ALLOC( frame_8kHz, frame_length_8kHz, opus_int16 ); if( Fs_kHz == 16 ) { silk_memset( filt_state, 0, 2 * sizeof( opus_int32 ) ); silk_resampler_down2( filt_state, frame_8kHz, frame, frame_length ); } else if( Fs_kHz == 12 ) { silk_memset( filt_state, 0, 6 * sizeof( opus_int32 ) ); silk_resampler_down2_3( filt_state, frame_8kHz, frame, frame_length ); } else { silk_assert( Fs_kHz == 8 ); silk_memcpy( frame_8kHz, frame, frame_length_8kHz * sizeof(opus_int16) ); } /* Decimate again to 4 kHz */ silk_memset( filt_state, 0, 2 * sizeof( opus_int32 ) );/* Set state to zero */ ALLOC( frame_4kHz, frame_length_4kHz, opus_int16 ); silk_resampler_down2( filt_state, frame_4kHz, frame_8kHz, frame_length_8kHz ); /* Low-pass filter */ for( i = frame_length_4kHz - 1; i > 0; i-- ) { frame_4kHz[ i ] = silk_ADD_SAT16( frame_4kHz[ i ], frame_4kHz[ i - 1 ] ); } /******************************************************************************* ** Scale 4 kHz signal down to prevent correlations measures from overflowing ** find scaling as max scaling for each 8kHz(?) subframe *******************************************************************************/ /* Inner product is calculated with different lengths, so scale for the worst case */ silk_sum_sqr_shift( &energy, &shift, frame_4kHz, frame_length_4kHz ); if( shift > 0 ) { shift = silk_RSHIFT( shift, 1 ); for( i = 0; i < frame_length_4kHz; i++ ) { frame_4kHz[ i ] = silk_RSHIFT( frame_4kHz[ i ], shift ); } } /****************************************************************************** * FIRST STAGE, operating in 4 khz ******************************************************************************/ ALLOC( C, nb_subfr * CSTRIDE_8KHZ, opus_int16 ); ALLOC( xcorr32, MAX_LAG_4KHZ-MIN_LAG_4KHZ+1, opus_int32 ); silk_memset( C, 0, (nb_subfr >> 1) * CSTRIDE_4KHZ * sizeof( opus_int16 ) ); target_ptr = &frame_4kHz[ silk_LSHIFT( SF_LENGTH_4KHZ, 2 ) ]; for( k = 0; k < nb_subfr >> 1; k++ ) { /* Check that we are within range of the array */ silk_assert( target_ptr >= frame_4kHz ); silk_assert( target_ptr + SF_LENGTH_8KHZ <= frame_4kHz + frame_length_4kHz ); basis_ptr = target_ptr - MIN_LAG_4KHZ; /* Check that we are within range of the array */ silk_assert( basis_ptr >= frame_4kHz ); silk_assert( basis_ptr + SF_LENGTH_8KHZ <= frame_4kHz + frame_length_4kHz ); celt_pitch_xcorr( target_ptr, target_ptr - MAX_LAG_4KHZ, xcorr32, SF_LENGTH_8KHZ, MAX_LAG_4KHZ - MIN_LAG_4KHZ + 1, arch ); /* Calculate first vector products before loop */ cross_corr = xcorr32[ MAX_LAG_4KHZ - MIN_LAG_4KHZ ]; normalizer = silk_inner_prod_aligned( target_ptr, target_ptr, SF_LENGTH_8KHZ, arch ); normalizer = silk_ADD32( normalizer, silk_inner_prod_aligned( basis_ptr, basis_ptr, SF_LENGTH_8KHZ, arch ) ); normalizer = silk_ADD32( normalizer, silk_SMULBB( SF_LENGTH_8KHZ, 4000 ) ); matrix_ptr( C, k, 0, CSTRIDE_4KHZ ) = (opus_int16)silk_DIV32_varQ( cross_corr, normalizer, 13 + 1 ); /* Q13 */ /* From now on normalizer is computed recursively */ for( d = MIN_LAG_4KHZ + 1; d <= MAX_LAG_4KHZ; d++ ) { basis_ptr--; /* Check that we are within range of the array */ silk_assert( basis_ptr >= frame_4kHz ); silk_assert( basis_ptr + SF_LENGTH_8KHZ <= frame_4kHz + frame_length_4kHz ); cross_corr = xcorr32[ MAX_LAG_4KHZ - d ]; /* Add contribution of new sample and remove contribution from oldest sample */ normalizer = silk_ADD32( normalizer, silk_SMULBB( basis_ptr[ 0 ], basis_ptr[ 0 ] ) - silk_SMULBB( basis_ptr[ SF_LENGTH_8KHZ ], basis_ptr[ SF_LENGTH_8KHZ ] ) ); matrix_ptr( C, k, d - MIN_LAG_4KHZ, CSTRIDE_4KHZ) = (opus_int16)silk_DIV32_varQ( cross_corr, normalizer, 13 + 1 ); /* Q13 */ } /* Update target pointer */ target_ptr += SF_LENGTH_8KHZ; } /* Combine two subframes into single correlation measure and apply short-lag bias */ if( nb_subfr == PE_MAX_NB_SUBFR ) { for( i = MAX_LAG_4KHZ; i >= MIN_LAG_4KHZ; i-- ) { sum = (opus_int32)matrix_ptr( C, 0, i - MIN_LAG_4KHZ, CSTRIDE_4KHZ ) + (opus_int32)matrix_ptr( C, 1, i - MIN_LAG_4KHZ, CSTRIDE_4KHZ ); /* Q14 */ sum = silk_SMLAWB( sum, sum, silk_LSHIFT( -i, 4 ) ); /* Q14 */ C[ i - MIN_LAG_4KHZ ] = (opus_int16)sum; /* Q14 */ } } else { /* Only short-lag bias */ for( i = MAX_LAG_4KHZ; i >= MIN_LAG_4KHZ; i-- ) { sum = silk_LSHIFT( (opus_int32)C[ i - MIN_LAG_4KHZ ], 1 ); /* Q14 */ sum = silk_SMLAWB( sum, sum, silk_LSHIFT( -i, 4 ) ); /* Q14 */ C[ i - MIN_LAG_4KHZ ] = (opus_int16)sum; /* Q14 */ } } /* Sort */ length_d_srch = silk_ADD_LSHIFT32( 4, complexity, 1 ); silk_assert( 3 * length_d_srch <= PE_D_SRCH_LENGTH ); silk_insertion_sort_decreasing_int16( C, d_srch, CSTRIDE_4KHZ, length_d_srch ); /* Escape if correlation is very low already here */ Cmax = (opus_int)C[ 0 ]; /* Q14 */ if( Cmax < SILK_FIX_CONST( 0.2, 14 ) ) { silk_memset( pitch_out, 0, nb_subfr * sizeof( opus_int ) ); *LTPCorr_Q15 = 0; *lagIndex = 0; *contourIndex = 0; RESTORE_STACK; return 1; } threshold = silk_SMULWB( search_thres1_Q16, Cmax ); for( i = 0; i < length_d_srch; i++ ) { /* Convert to 8 kHz indices for the sorted correlation that exceeds the threshold */ if( C[ i ] > threshold ) { d_srch[ i ] = silk_LSHIFT( d_srch[ i ] + MIN_LAG_4KHZ, 1 ); } else { length_d_srch = i; break; } } silk_assert( length_d_srch > 0 ); ALLOC( d_comp, D_COMP_STRIDE, opus_int16 ); for( i = D_COMP_MIN; i < D_COMP_MAX; i++ ) { d_comp[ i - D_COMP_MIN ] = 0; } for( i = 0; i < length_d_srch; i++ ) { d_comp[ d_srch[ i ] - D_COMP_MIN ] = 1; } /* Convolution */ for( i = D_COMP_MAX - 1; i >= MIN_LAG_8KHZ; i-- ) { d_comp[ i - D_COMP_MIN ] += d_comp[ i - 1 - D_COMP_MIN ] + d_comp[ i - 2 - D_COMP_MIN ]; } length_d_srch = 0; for( i = MIN_LAG_8KHZ; i < MAX_LAG_8KHZ + 1; i++ ) { if( d_comp[ i + 1 - D_COMP_MIN ] > 0 ) { d_srch[ length_d_srch ] = i; length_d_srch++; } } /* Convolution */ for( i = D_COMP_MAX - 1; i >= MIN_LAG_8KHZ; i-- ) { d_comp[ i - D_COMP_MIN ] += d_comp[ i - 1 - D_COMP_MIN ] + d_comp[ i - 2 - D_COMP_MIN ] + d_comp[ i - 3 - D_COMP_MIN ]; } length_d_comp = 0; for( i = MIN_LAG_8KHZ; i < D_COMP_MAX; i++ ) { if( d_comp[ i - D_COMP_MIN ] > 0 ) { d_comp[ length_d_comp ] = i - 2; length_d_comp++; } } /********************************************************************************** ** SECOND STAGE, operating at 8 kHz, on lag sections with high correlation *************************************************************************************/ /****************************************************************************** ** Scale signal down to avoid correlations measures from overflowing *******************************************************************************/ /* find scaling as max scaling for each subframe */ silk_sum_sqr_shift( &energy, &shift, frame_8kHz, frame_length_8kHz ); if( shift > 0 ) { shift = silk_RSHIFT( shift, 1 ); for( i = 0; i < frame_length_8kHz; i++ ) { frame_8kHz[ i ] = silk_RSHIFT( frame_8kHz[ i ], shift ); } } /********************************************************************************* * Find energy of each subframe projected onto its history, for a range of delays *********************************************************************************/ silk_memset( C, 0, nb_subfr * CSTRIDE_8KHZ * sizeof( opus_int16 ) ); target_ptr = &frame_8kHz[ PE_LTP_MEM_LENGTH_MS * 8 ]; for( k = 0; k < nb_subfr; k++ ) { /* Check that we are within range of the array */ silk_assert( target_ptr >= frame_8kHz ); silk_assert( target_ptr + SF_LENGTH_8KHZ <= frame_8kHz + frame_length_8kHz ); energy_target = silk_ADD32( silk_inner_prod_aligned( target_ptr, target_ptr, SF_LENGTH_8KHZ, arch ), 1 ); for( j = 0; j < length_d_comp; j++ ) { d = d_comp[ j ]; basis_ptr = target_ptr - d; /* Check that we are within range of the array */ silk_assert( basis_ptr >= frame_8kHz ); silk_assert( basis_ptr + SF_LENGTH_8KHZ <= frame_8kHz + frame_length_8kHz ); cross_corr = silk_inner_prod_aligned( target_ptr, basis_ptr, SF_LENGTH_8KHZ, arch ); if( cross_corr > 0 ) { energy_basis = silk_inner_prod_aligned( basis_ptr, basis_ptr, SF_LENGTH_8KHZ, arch ); matrix_ptr( C, k, d - ( MIN_LAG_8KHZ - 2 ), CSTRIDE_8KHZ ) = (opus_int16)silk_DIV32_varQ( cross_corr, silk_ADD32( energy_target, energy_basis ), 13 + 1 ); /* Q13 */ } else { matrix_ptr( C, k, d - ( MIN_LAG_8KHZ - 2 ), CSTRIDE_8KHZ ) = 0; } } target_ptr += SF_LENGTH_8KHZ; } /* search over lag range and lags codebook */ /* scale factor for lag codebook, as a function of center lag */ CCmax = silk_int32_MIN; CCmax_b = silk_int32_MIN; CBimax = 0; /* To avoid returning undefined lag values */ lag = -1; /* To check if lag with strong enough correlation has been found */ if( prevLag > 0 ) { if( Fs_kHz == 12 ) { prevLag = silk_DIV32_16( silk_LSHIFT( prevLag, 1 ), 3 ); } else if( Fs_kHz == 16 ) { prevLag = silk_RSHIFT( prevLag, 1 ); } prevLag_log2_Q7 = silk_lin2log( (opus_int32)prevLag ); } else { prevLag_log2_Q7 = 0; } silk_assert( search_thres2_Q13 == silk_SAT16( search_thres2_Q13 ) ); /* Set up stage 2 codebook based on number of subframes */ if( nb_subfr == PE_MAX_NB_SUBFR ) { cbk_size = PE_NB_CBKS_STAGE2_EXT; Lag_CB_ptr = &silk_CB_lags_stage2[ 0 ][ 0 ]; if( Fs_kHz == 8 && complexity > SILK_PE_MIN_COMPLEX ) { /* If input is 8 khz use a larger codebook here because it is last stage */ nb_cbk_search = PE_NB_CBKS_STAGE2_EXT; } else { nb_cbk_search = PE_NB_CBKS_STAGE2; } } else { cbk_size = PE_NB_CBKS_STAGE2_10MS; Lag_CB_ptr = &silk_CB_lags_stage2_10_ms[ 0 ][ 0 ]; nb_cbk_search = PE_NB_CBKS_STAGE2_10MS; } for( k = 0; k < length_d_srch; k++ ) { d = d_srch[ k ]; for( j = 0; j < nb_cbk_search; j++ ) { CC[ j ] = 0; for( i = 0; i < nb_subfr; i++ ) { opus_int d_subfr; /* Try all codebooks */ d_subfr = d + matrix_ptr( Lag_CB_ptr, i, j, cbk_size ); CC[ j ] = CC[ j ] + (opus_int32)matrix_ptr( C, i, d_subfr - ( MIN_LAG_8KHZ - 2 ), CSTRIDE_8KHZ ); } } /* Find best codebook */ CCmax_new = silk_int32_MIN; CBimax_new = 0; for( i = 0; i < nb_cbk_search; i++ ) { if( CC[ i ] > CCmax_new ) { CCmax_new = CC[ i ]; CBimax_new = i; } } /* Bias towards shorter lags */ lag_log2_Q7 = silk_lin2log( d ); /* Q7 */ silk_assert( lag_log2_Q7 == silk_SAT16( lag_log2_Q7 ) ); silk_assert( nb_subfr * SILK_FIX_CONST( PE_SHORTLAG_BIAS, 13 ) == silk_SAT16( nb_subfr * SILK_FIX_CONST( PE_SHORTLAG_BIAS, 13 ) ) ); CCmax_new_b = CCmax_new - silk_RSHIFT( silk_SMULBB( nb_subfr * SILK_FIX_CONST( PE_SHORTLAG_BIAS, 13 ), lag_log2_Q7 ), 7 ); /* Q13 */ /* Bias towards previous lag */ silk_assert( nb_subfr * SILK_FIX_CONST( PE_PREVLAG_BIAS, 13 ) == silk_SAT16( nb_subfr * SILK_FIX_CONST( PE_PREVLAG_BIAS, 13 ) ) ); if( prevLag > 0 ) { delta_lag_log2_sqr_Q7 = lag_log2_Q7 - prevLag_log2_Q7; silk_assert( delta_lag_log2_sqr_Q7 == silk_SAT16( delta_lag_log2_sqr_Q7 ) ); delta_lag_log2_sqr_Q7 = silk_RSHIFT( silk_SMULBB( delta_lag_log2_sqr_Q7, delta_lag_log2_sqr_Q7 ), 7 ); prev_lag_bias_Q13 = silk_RSHIFT( silk_SMULBB( nb_subfr * SILK_FIX_CONST( PE_PREVLAG_BIAS, 13 ), *LTPCorr_Q15 ), 15 ); /* Q13 */ prev_lag_bias_Q13 = silk_DIV32( silk_MUL( prev_lag_bias_Q13, delta_lag_log2_sqr_Q7 ), delta_lag_log2_sqr_Q7 + SILK_FIX_CONST( 0.5, 7 ) ); CCmax_new_b -= prev_lag_bias_Q13; /* Q13 */ } if( CCmax_new_b > CCmax_b && /* Find maximum biased correlation */ CCmax_new > silk_SMULBB( nb_subfr, search_thres2_Q13 ) && /* Correlation needs to be high enough to be voiced */ silk_CB_lags_stage2[ 0 ][ CBimax_new ] <= MIN_LAG_8KHZ /* Lag must be in range */ ) { CCmax_b = CCmax_new_b; CCmax = CCmax_new; lag = d; CBimax = CBimax_new; } } if( lag == -1 ) { /* No suitable candidate found */ silk_memset( pitch_out, 0, nb_subfr * sizeof( opus_int ) ); *LTPCorr_Q15 = 0; *lagIndex = 0; *contourIndex = 0; RESTORE_STACK; return 1; } /* Output normalized correlation */ *LTPCorr_Q15 = (opus_int)silk_LSHIFT( silk_DIV32_16( CCmax, nb_subfr ), 2 ); silk_assert( *LTPCorr_Q15 >= 0 ); if( Fs_kHz > 8 ) { VARDECL( opus_int16, scratch_mem ); /***************************************************************************/ /* Scale input signal down to avoid correlations measures from overflowing */ /***************************************************************************/ /* find scaling as max scaling for each subframe */ silk_sum_sqr_shift( &energy, &shift, frame, frame_length ); ALLOC( scratch_mem, shift > 0 ? frame_length : ALLOC_NONE, opus_int16 ); if( shift > 0 ) { /* Move signal to scratch mem because the input signal should be unchanged */ shift = silk_RSHIFT( shift, 1 ); for( i = 0; i < frame_length; i++ ) { scratch_mem[ i ] = silk_RSHIFT( frame[ i ], shift ); } input_frame_ptr = scratch_mem; } else { input_frame_ptr = frame; } /* Search in original signal */ CBimax_old = CBimax; /* Compensate for decimation */ silk_assert( lag == silk_SAT16( lag ) ); if( Fs_kHz == 12 ) { lag = silk_RSHIFT( silk_SMULBB( lag, 3 ), 1 ); } else if( Fs_kHz == 16 ) { lag = silk_LSHIFT( lag, 1 ); } else { lag = silk_SMULBB( lag, 3 ); } lag = silk_LIMIT_int( lag, min_lag, max_lag ); start_lag = silk_max_int( lag - 2, min_lag ); end_lag = silk_min_int( lag + 2, max_lag ); lag_new = lag; /* to avoid undefined lag */ CBimax = 0; /* to avoid undefined lag */ CCmax = silk_int32_MIN; /* pitch lags according to second stage */ for( k = 0; k < nb_subfr; k++ ) { pitch_out[ k ] = lag + 2 * silk_CB_lags_stage2[ k ][ CBimax_old ]; } /* Set up codebook parameters according to complexity setting and frame length */ if( nb_subfr == PE_MAX_NB_SUBFR ) { nb_cbk_search = (opus_int)silk_nb_cbk_searchs_stage3[ complexity ]; cbk_size = PE_NB_CBKS_STAGE3_MAX; Lag_CB_ptr = &silk_CB_lags_stage3[ 0 ][ 0 ]; } else { nb_cbk_search = PE_NB_CBKS_STAGE3_10MS; cbk_size = PE_NB_CBKS_STAGE3_10MS; Lag_CB_ptr = &silk_CB_lags_stage3_10_ms[ 0 ][ 0 ]; } /* Calculate the correlations and energies needed in stage 3 */ ALLOC( energies_st3, nb_subfr * nb_cbk_search, silk_pe_stage3_vals ); ALLOC( cross_corr_st3, nb_subfr * nb_cbk_search, silk_pe_stage3_vals ); silk_P_Ana_calc_corr_st3( cross_corr_st3, input_frame_ptr, start_lag, sf_length, nb_subfr, complexity, arch ); silk_P_Ana_calc_energy_st3( energies_st3, input_frame_ptr, start_lag, sf_length, nb_subfr, complexity, arch ); lag_counter = 0; silk_assert( lag == silk_SAT16( lag ) ); contour_bias_Q15 = silk_DIV32_16( SILK_FIX_CONST( PE_FLATCONTOUR_BIAS, 15 ), lag ); target_ptr = &input_frame_ptr[ PE_LTP_MEM_LENGTH_MS * Fs_kHz ]; energy_target = silk_ADD32( silk_inner_prod_aligned( target_ptr, target_ptr, nb_subfr * sf_length, arch ), 1 ); for( d = start_lag; d <= end_lag; d++ ) { for( j = 0; j < nb_cbk_search; j++ ) { cross_corr = 0; energy = energy_target; for( k = 0; k < nb_subfr; k++ ) { cross_corr = silk_ADD32( cross_corr, matrix_ptr( cross_corr_st3, k, j, nb_cbk_search )[ lag_counter ] ); energy = silk_ADD32( energy, matrix_ptr( energies_st3, k, j, nb_cbk_search )[ lag_counter ] ); silk_assert( energy >= 0 ); } if( cross_corr > 0 ) { CCmax_new = silk_DIV32_varQ( cross_corr, energy, 13 + 1 ); /* Q13 */ /* Reduce depending on flatness of contour */ diff = silk_int16_MAX - silk_MUL( contour_bias_Q15, j ); /* Q15 */ silk_assert( diff == silk_SAT16( diff ) ); CCmax_new = silk_SMULWB( CCmax_new, diff ); /* Q14 */ } else { CCmax_new = 0; } if( CCmax_new > CCmax && ( d + silk_CB_lags_stage3[ 0 ][ j ] ) <= max_lag ) { CCmax = CCmax_new; lag_new = d; CBimax = j; } } lag_counter++; } for( k = 0; k < nb_subfr; k++ ) { pitch_out[ k ] = lag_new + matrix_ptr( Lag_CB_ptr, k, CBimax, cbk_size ); pitch_out[ k ] = silk_LIMIT( pitch_out[ k ], min_lag, PE_MAX_LAG_MS * Fs_kHz ); } *lagIndex = (opus_int16)( lag_new - min_lag); *contourIndex = (opus_int8)CBimax; } else { /* Fs_kHz == 8 */ /* Save Lags */ for( k = 0; k < nb_subfr; k++ ) { pitch_out[ k ] = lag + matrix_ptr( Lag_CB_ptr, k, CBimax, cbk_size ); pitch_out[ k ] = silk_LIMIT( pitch_out[ k ], MIN_LAG_8KHZ, PE_MAX_LAG_MS * 8 ); } *lagIndex = (opus_int16)( lag - MIN_LAG_8KHZ ); *contourIndex = (opus_int8)CBimax; } silk_assert( *lagIndex >= 0 ); /* return as voiced */ RESTORE_STACK; return 0; } /*********************************************************************** * Calculates the correlations used in stage 3 search. In order to cover * the whole lag codebook for all the searched offset lags (lag +- 2), * the following correlations are needed in each sub frame: * * sf1: lag range [-8,...,7] total 16 correlations * sf2: lag range [-4,...,4] total 9 correlations * sf3: lag range [-3,....4] total 8 correltions * sf4: lag range [-6,....8] total 15 correlations * * In total 48 correlations. The direct implementation computed in worst * case 4*12*5 = 240 correlations, but more likely around 120. ***********************************************************************/ static void silk_P_Ana_calc_corr_st3( silk_pe_stage3_vals cross_corr_st3[], /* O 3 DIM correlation array */ const opus_int16 frame[], /* I vector to correlate */ opus_int start_lag, /* I lag offset to search around */ opus_int sf_length, /* I length of a 5 ms subframe */ opus_int nb_subfr, /* I number of subframes */ opus_int complexity, /* I Complexity setting */ int arch /* I Run-time architecture */ ) { const opus_int16 *target_ptr; opus_int i, j, k, lag_counter, lag_low, lag_high; opus_int nb_cbk_search, delta, idx, cbk_size; VARDECL( opus_int32, scratch_mem ); VARDECL( opus_int32, xcorr32 ); const opus_int8 *Lag_range_ptr, *Lag_CB_ptr; SAVE_STACK; silk_assert( complexity >= SILK_PE_MIN_COMPLEX ); silk_assert( complexity <= SILK_PE_MAX_COMPLEX ); if( nb_subfr == PE_MAX_NB_SUBFR ) { Lag_range_ptr = &silk_Lag_range_stage3[ complexity ][ 0 ][ 0 ]; Lag_CB_ptr = &silk_CB_lags_stage3[ 0 ][ 0 ]; nb_cbk_search = silk_nb_cbk_searchs_stage3[ complexity ]; cbk_size = PE_NB_CBKS_STAGE3_MAX; } else { silk_assert( nb_subfr == PE_MAX_NB_SUBFR >> 1); Lag_range_ptr = &silk_Lag_range_stage3_10_ms[ 0 ][ 0 ]; Lag_CB_ptr = &silk_CB_lags_stage3_10_ms[ 0 ][ 0 ]; nb_cbk_search = PE_NB_CBKS_STAGE3_10MS; cbk_size = PE_NB_CBKS_STAGE3_10MS; } ALLOC( scratch_mem, SCRATCH_SIZE, opus_int32 ); ALLOC( xcorr32, SCRATCH_SIZE, opus_int32 ); target_ptr = &frame[ silk_LSHIFT( sf_length, 2 ) ]; /* Pointer to middle of frame */ for( k = 0; k < nb_subfr; k++ ) { lag_counter = 0; /* Calculate the correlations for each subframe */ lag_low = matrix_ptr( Lag_range_ptr, k, 0, 2 ); lag_high = matrix_ptr( Lag_range_ptr, k, 1, 2 ); silk_assert(lag_high-lag_low+1 <= SCRATCH_SIZE); celt_pitch_xcorr( target_ptr, target_ptr - start_lag - lag_high, xcorr32, sf_length, lag_high - lag_low + 1, arch ); for( j = lag_low; j <= lag_high; j++ ) { silk_assert( lag_counter < SCRATCH_SIZE ); scratch_mem[ lag_counter ] = xcorr32[ lag_high - j ]; lag_counter++; } delta = matrix_ptr( Lag_range_ptr, k, 0, 2 ); for( i = 0; i < nb_cbk_search; i++ ) { /* Fill out the 3 dim array that stores the correlations for */ /* each code_book vector for each start lag */ idx = matrix_ptr( Lag_CB_ptr, k, i, cbk_size ) - delta; for( j = 0; j < PE_NB_STAGE3_LAGS; j++ ) { silk_assert( idx + j < SCRATCH_SIZE ); silk_assert( idx + j < lag_counter ); matrix_ptr( cross_corr_st3, k, i, nb_cbk_search )[ j ] = scratch_mem[ idx + j ]; } } target_ptr += sf_length; } RESTORE_STACK; } /********************************************************************/ /* Calculate the energies for first two subframes. The energies are */ /* calculated recursively. */ /********************************************************************/ static void silk_P_Ana_calc_energy_st3( silk_pe_stage3_vals energies_st3[], /* O 3 DIM energy array */ const opus_int16 frame[], /* I vector to calc energy in */ opus_int start_lag, /* I lag offset to search around */ opus_int sf_length, /* I length of one 5 ms subframe */ opus_int nb_subfr, /* I number of subframes */ opus_int complexity, /* I Complexity setting */ int arch /* I Run-time architecture */ ) { const opus_int16 *target_ptr, *basis_ptr; opus_int32 energy; opus_int k, i, j, lag_counter; opus_int nb_cbk_search, delta, idx, cbk_size, lag_diff; VARDECL( opus_int32, scratch_mem ); const opus_int8 *Lag_range_ptr, *Lag_CB_ptr; SAVE_STACK; silk_assert( complexity >= SILK_PE_MIN_COMPLEX ); silk_assert( complexity <= SILK_PE_MAX_COMPLEX ); if( nb_subfr == PE_MAX_NB_SUBFR ) { Lag_range_ptr = &silk_Lag_range_stage3[ complexity ][ 0 ][ 0 ]; Lag_CB_ptr = &silk_CB_lags_stage3[ 0 ][ 0 ]; nb_cbk_search = silk_nb_cbk_searchs_stage3[ complexity ]; cbk_size = PE_NB_CBKS_STAGE3_MAX; } else { silk_assert( nb_subfr == PE_MAX_NB_SUBFR >> 1); Lag_range_ptr = &silk_Lag_range_stage3_10_ms[ 0 ][ 0 ]; Lag_CB_ptr = &silk_CB_lags_stage3_10_ms[ 0 ][ 0 ]; nb_cbk_search = PE_NB_CBKS_STAGE3_10MS; cbk_size = PE_NB_CBKS_STAGE3_10MS; } ALLOC( scratch_mem, SCRATCH_SIZE, opus_int32 ); target_ptr = &frame[ silk_LSHIFT( sf_length, 2 ) ]; for( k = 0; k < nb_subfr; k++ ) { lag_counter = 0; /* Calculate the energy for first lag */ basis_ptr = target_ptr - ( start_lag + matrix_ptr( Lag_range_ptr, k, 0, 2 ) ); energy = silk_inner_prod_aligned( basis_ptr, basis_ptr, sf_length, arch ); silk_assert( energy >= 0 ); scratch_mem[ lag_counter ] = energy; lag_counter++; lag_diff = ( matrix_ptr( Lag_range_ptr, k, 1, 2 ) - matrix_ptr( Lag_range_ptr, k, 0, 2 ) + 1 ); for( i = 1; i < lag_diff; i++ ) { /* remove part outside new window */ energy -= silk_SMULBB( basis_ptr[ sf_length - i ], basis_ptr[ sf_length - i ] ); silk_assert( energy >= 0 ); /* add part that comes into window */ energy = silk_ADD_SAT32( energy, silk_SMULBB( basis_ptr[ -i ], basis_ptr[ -i ] ) ); silk_assert( energy >= 0 ); silk_assert( lag_counter < SCRATCH_SIZE ); scratch_mem[ lag_counter ] = energy; lag_counter++; } delta = matrix_ptr( Lag_range_ptr, k, 0, 2 ); for( i = 0; i < nb_cbk_search; i++ ) { /* Fill out the 3 dim array that stores the correlations for */ /* each code_book vector for each start lag */ idx = matrix_ptr( Lag_CB_ptr, k, i, cbk_size ) - delta; for( j = 0; j < PE_NB_STAGE3_LAGS; j++ ) { silk_assert( idx + j < SCRATCH_SIZE ); silk_assert( idx + j < lag_counter ); matrix_ptr( energies_st3, k, i, nb_cbk_search )[ j ] = scratch_mem[ idx + j ]; silk_assert( matrix_ptr( energies_st3, k, i, nb_cbk_search )[ j ] >= 0 ); } } target_ptr += sf_length; } RESTORE_STACK; } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/prefilter_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" #include "stack_alloc.h" #include "tuning_parameters.h" #if defined(MIPSr1_ASM) #include "mips/prefilter_FIX_mipsr1.h" #endif #if !defined(OVERRIDE_silk_warped_LPC_analysis_filter_FIX) #define silk_warped_LPC_analysis_filter_FIX(state, res_Q2, coef_Q13, input, lambda_Q16, length, order, arch) \ ((void)(arch),silk_warped_LPC_analysis_filter_FIX_c(state, res_Q2, coef_Q13, input, lambda_Q16, length, order)) #endif /* Prefilter for finding Quantizer input signal */ static OPUS_INLINE void silk_prefilt_FIX( silk_prefilter_state_FIX *P, /* I/O state */ opus_int32 st_res_Q12[], /* I short term residual signal */ opus_int32 xw_Q3[], /* O prefiltered signal */ opus_int32 HarmShapeFIRPacked_Q12, /* I Harmonic shaping coeficients */ opus_int Tilt_Q14, /* I Tilt shaping coeficient */ opus_int32 LF_shp_Q14, /* I Low-frequancy shaping coeficients */ opus_int lag, /* I Lag for harmonic shaping */ opus_int length /* I Length of signals */ ); void silk_warped_LPC_analysis_filter_FIX_c( opus_int32 state[], /* I/O State [order + 1] */ opus_int32 res_Q2[], /* O Residual signal [length] */ const opus_int16 coef_Q13[], /* I Coefficients [order] */ const opus_int16 input[], /* I Input signal [length] */ const opus_int16 lambda_Q16, /* I Warping factor */ const opus_int length, /* I Length of input signal */ const opus_int order /* I Filter order (even) */ ) { opus_int n, i; opus_int32 acc_Q11, tmp1, tmp2; /* Order must be even */ silk_assert( ( order & 1 ) == 0 ); for( n = 0; n < length; n++ ) { /* Output of lowpass section */ tmp2 = silk_SMLAWB( state[ 0 ], state[ 1 ], lambda_Q16 ); state[ 0 ] = silk_LSHIFT( input[ n ], 14 ); /* Output of allpass section */ tmp1 = silk_SMLAWB( state[ 1 ], state[ 2 ] - tmp2, lambda_Q16 ); state[ 1 ] = tmp2; acc_Q11 = silk_RSHIFT( order, 1 ); acc_Q11 = silk_SMLAWB( acc_Q11, tmp2, coef_Q13[ 0 ] ); /* Loop over allpass sections */ for( i = 2; i < order; i += 2 ) { /* Output of allpass section */ tmp2 = silk_SMLAWB( state[ i ], state[ i + 1 ] - tmp1, lambda_Q16 ); state[ i ] = tmp1; acc_Q11 = silk_SMLAWB( acc_Q11, tmp1, coef_Q13[ i - 1 ] ); /* Output of allpass section */ tmp1 = silk_SMLAWB( state[ i + 1 ], state[ i + 2 ] - tmp2, lambda_Q16 ); state[ i + 1 ] = tmp2; acc_Q11 = silk_SMLAWB( acc_Q11, tmp2, coef_Q13[ i ] ); } state[ order ] = tmp1; acc_Q11 = silk_SMLAWB( acc_Q11, tmp1, coef_Q13[ order - 1 ] ); res_Q2[ n ] = silk_LSHIFT( (opus_int32)input[ n ], 2 ) - silk_RSHIFT_ROUND( acc_Q11, 9 ); } } void silk_prefilter_FIX( silk_encoder_state_FIX *psEnc, /* I/O Encoder state */ const silk_encoder_control_FIX *psEncCtrl, /* I Encoder control */ opus_int32 xw_Q3[], /* O Weighted signal */ const opus_int16 x[] /* I Speech signal */ ) { silk_prefilter_state_FIX *P = &psEnc->sPrefilt; opus_int j, k, lag; opus_int32 tmp_32; const opus_int16 *AR1_shp_Q13; const opus_int16 *px; opus_int32 *pxw_Q3; opus_int HarmShapeGain_Q12, Tilt_Q14; opus_int32 HarmShapeFIRPacked_Q12, LF_shp_Q14; VARDECL( opus_int32, x_filt_Q12 ); VARDECL( opus_int32, st_res_Q2 ); opus_int16 B_Q10[ 2 ]; SAVE_STACK; /* Set up pointers */ px = x; pxw_Q3 = xw_Q3; lag = P->lagPrev; ALLOC( x_filt_Q12, psEnc->sCmn.subfr_length, opus_int32 ); ALLOC( st_res_Q2, psEnc->sCmn.subfr_length, opus_int32 ); for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { /* Update Variables that change per sub frame */ if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { lag = psEncCtrl->pitchL[ k ]; } /* Noise shape parameters */ HarmShapeGain_Q12 = silk_SMULWB( (opus_int32)psEncCtrl->HarmShapeGain_Q14[ k ], 16384 - psEncCtrl->HarmBoost_Q14[ k ] ); silk_assert( HarmShapeGain_Q12 >= 0 ); HarmShapeFIRPacked_Q12 = silk_RSHIFT( HarmShapeGain_Q12, 2 ); HarmShapeFIRPacked_Q12 |= silk_LSHIFT( (opus_int32)silk_RSHIFT( HarmShapeGain_Q12, 1 ), 16 ); Tilt_Q14 = psEncCtrl->Tilt_Q14[ k ]; LF_shp_Q14 = psEncCtrl->LF_shp_Q14[ k ]; AR1_shp_Q13 = &psEncCtrl->AR1_Q13[ k * MAX_SHAPE_LPC_ORDER ]; /* Short term FIR filtering*/ silk_warped_LPC_analysis_filter_FIX( P->sAR_shp, st_res_Q2, AR1_shp_Q13, px, psEnc->sCmn.warping_Q16, psEnc->sCmn.subfr_length, psEnc->sCmn.shapingLPCOrder, psEnc->sCmn.arch ); /* Reduce (mainly) low frequencies during harmonic emphasis */ B_Q10[ 0 ] = silk_RSHIFT_ROUND( psEncCtrl->GainsPre_Q14[ k ], 4 ); tmp_32 = silk_SMLABB( SILK_FIX_CONST( INPUT_TILT, 26 ), psEncCtrl->HarmBoost_Q14[ k ], HarmShapeGain_Q12 ); /* Q26 */ tmp_32 = silk_SMLABB( tmp_32, psEncCtrl->coding_quality_Q14, SILK_FIX_CONST( HIGH_RATE_INPUT_TILT, 12 ) ); /* Q26 */ tmp_32 = silk_SMULWB( tmp_32, -psEncCtrl->GainsPre_Q14[ k ] ); /* Q24 */ tmp_32 = silk_RSHIFT_ROUND( tmp_32, 14 ); /* Q10 */ B_Q10[ 1 ]= silk_SAT16( tmp_32 ); x_filt_Q12[ 0 ] = silk_MLA( silk_MUL( st_res_Q2[ 0 ], B_Q10[ 0 ] ), P->sHarmHP_Q2, B_Q10[ 1 ] ); for( j = 1; j < psEnc->sCmn.subfr_length; j++ ) { x_filt_Q12[ j ] = silk_MLA( silk_MUL( st_res_Q2[ j ], B_Q10[ 0 ] ), st_res_Q2[ j - 1 ], B_Q10[ 1 ] ); } P->sHarmHP_Q2 = st_res_Q2[ psEnc->sCmn.subfr_length - 1 ]; silk_prefilt_FIX( P, x_filt_Q12, pxw_Q3, HarmShapeFIRPacked_Q12, Tilt_Q14, LF_shp_Q14, lag, psEnc->sCmn.subfr_length ); px += psEnc->sCmn.subfr_length; pxw_Q3 += psEnc->sCmn.subfr_length; } P->lagPrev = psEncCtrl->pitchL[ psEnc->sCmn.nb_subfr - 1 ]; RESTORE_STACK; } #ifndef OVERRIDE_silk_prefilt_FIX /* Prefilter for finding Quantizer input signal */ static OPUS_INLINE void silk_prefilt_FIX( silk_prefilter_state_FIX *P, /* I/O state */ opus_int32 st_res_Q12[], /* I short term residual signal */ opus_int32 xw_Q3[], /* O prefiltered signal */ opus_int32 HarmShapeFIRPacked_Q12, /* I Harmonic shaping coeficients */ opus_int Tilt_Q14, /* I Tilt shaping coeficient */ opus_int32 LF_shp_Q14, /* I Low-frequancy shaping coeficients */ opus_int lag, /* I Lag for harmonic shaping */ opus_int length /* I Length of signals */ ) { opus_int i, idx, LTP_shp_buf_idx; opus_int32 n_LTP_Q12, n_Tilt_Q10, n_LF_Q10; opus_int32 sLF_MA_shp_Q12, sLF_AR_shp_Q12; opus_int16 *LTP_shp_buf; /* To speed up use temp variables instead of using the struct */ LTP_shp_buf = P->sLTP_shp; LTP_shp_buf_idx = P->sLTP_shp_buf_idx; sLF_AR_shp_Q12 = P->sLF_AR_shp_Q12; sLF_MA_shp_Q12 = P->sLF_MA_shp_Q12; for( i = 0; i < length; i++ ) { if( lag > 0 ) { /* unrolled loop */ silk_assert( HARM_SHAPE_FIR_TAPS == 3 ); idx = lag + LTP_shp_buf_idx; n_LTP_Q12 = silk_SMULBB( LTP_shp_buf[ ( idx - HARM_SHAPE_FIR_TAPS / 2 - 1) & LTP_MASK ], HarmShapeFIRPacked_Q12 ); n_LTP_Q12 = silk_SMLABT( n_LTP_Q12, LTP_shp_buf[ ( idx - HARM_SHAPE_FIR_TAPS / 2 ) & LTP_MASK ], HarmShapeFIRPacked_Q12 ); n_LTP_Q12 = silk_SMLABB( n_LTP_Q12, LTP_shp_buf[ ( idx - HARM_SHAPE_FIR_TAPS / 2 + 1) & LTP_MASK ], HarmShapeFIRPacked_Q12 ); } else { n_LTP_Q12 = 0; } n_Tilt_Q10 = silk_SMULWB( sLF_AR_shp_Q12, Tilt_Q14 ); n_LF_Q10 = silk_SMLAWB( silk_SMULWT( sLF_AR_shp_Q12, LF_shp_Q14 ), sLF_MA_shp_Q12, LF_shp_Q14 ); sLF_AR_shp_Q12 = silk_SUB32( st_res_Q12[ i ], silk_LSHIFT( n_Tilt_Q10, 2 ) ); sLF_MA_shp_Q12 = silk_SUB32( sLF_AR_shp_Q12, silk_LSHIFT( n_LF_Q10, 2 ) ); LTP_shp_buf_idx = ( LTP_shp_buf_idx - 1 ) & LTP_MASK; LTP_shp_buf[ LTP_shp_buf_idx ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( sLF_MA_shp_Q12, 12 ) ); xw_Q3[i] = silk_RSHIFT_ROUND( silk_SUB32( sLF_MA_shp_Q12, n_LTP_Q12 ), 9 ); } /* Copy temp variable back to state */ P->sLF_AR_shp_Q12 = sLF_AR_shp_Q12; P->sLF_MA_shp_Q12 = sLF_MA_shp_Q12; P->sLTP_shp_buf_idx = LTP_shp_buf_idx; } #endif /* OVERRIDE_silk_prefilt_FIX */ ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/process_gains_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" #include "tuning_parameters.h" /* Processing of gains */ void silk_process_gains_FIX( silk_encoder_state_FIX *psEnc, /* I/O Encoder state */ silk_encoder_control_FIX *psEncCtrl, /* I/O Encoder control */ opus_int condCoding /* I The type of conditional coding to use */ ) { silk_shape_state_FIX *psShapeSt = &psEnc->sShape; opus_int k; opus_int32 s_Q16, InvMaxSqrVal_Q16, gain, gain_squared, ResNrg, ResNrgPart, quant_offset_Q10; /* Gain reduction when LTP coding gain is high */ if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { /*s = -0.5f * silk_sigmoid( 0.25f * ( psEncCtrl->LTPredCodGain - 12.0f ) ); */ s_Q16 = -silk_sigm_Q15( silk_RSHIFT_ROUND( psEncCtrl->LTPredCodGain_Q7 - SILK_FIX_CONST( 12.0, 7 ), 4 ) ); for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { psEncCtrl->Gains_Q16[ k ] = silk_SMLAWB( psEncCtrl->Gains_Q16[ k ], psEncCtrl->Gains_Q16[ k ], s_Q16 ); } } /* Limit the quantized signal */ /* InvMaxSqrVal = pow( 2.0f, 0.33f * ( 21.0f - SNR_dB ) ) / subfr_length; */ InvMaxSqrVal_Q16 = silk_DIV32_16( silk_log2lin( silk_SMULWB( SILK_FIX_CONST( 21 + 16 / 0.33, 7 ) - psEnc->sCmn.SNR_dB_Q7, SILK_FIX_CONST( 0.33, 16 ) ) ), psEnc->sCmn.subfr_length ); for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { /* Soft limit on ratio residual energy and squared gains */ ResNrg = psEncCtrl->ResNrg[ k ]; ResNrgPart = silk_SMULWW( ResNrg, InvMaxSqrVal_Q16 ); if( psEncCtrl->ResNrgQ[ k ] > 0 ) { ResNrgPart = silk_RSHIFT_ROUND( ResNrgPart, psEncCtrl->ResNrgQ[ k ] ); } else { if( ResNrgPart >= silk_RSHIFT( silk_int32_MAX, -psEncCtrl->ResNrgQ[ k ] ) ) { ResNrgPart = silk_int32_MAX; } else { ResNrgPart = silk_LSHIFT( ResNrgPart, -psEncCtrl->ResNrgQ[ k ] ); } } gain = psEncCtrl->Gains_Q16[ k ]; gain_squared = silk_ADD_SAT32( ResNrgPart, silk_SMMUL( gain, gain ) ); if( gain_squared < silk_int16_MAX ) { /* recalculate with higher precision */ gain_squared = silk_SMLAWW( silk_LSHIFT( ResNrgPart, 16 ), gain, gain ); silk_assert( gain_squared > 0 ); gain = silk_SQRT_APPROX( gain_squared ); /* Q8 */ gain = silk_min( gain, silk_int32_MAX >> 8 ); psEncCtrl->Gains_Q16[ k ] = silk_LSHIFT_SAT32( gain, 8 ); /* Q16 */ } else { gain = silk_SQRT_APPROX( gain_squared ); /* Q0 */ gain = silk_min( gain, silk_int32_MAX >> 16 ); psEncCtrl->Gains_Q16[ k ] = silk_LSHIFT_SAT32( gain, 16 ); /* Q16 */ } } /* Save unquantized gains and gain Index */ silk_memcpy( psEncCtrl->GainsUnq_Q16, psEncCtrl->Gains_Q16, psEnc->sCmn.nb_subfr * sizeof( opus_int32 ) ); psEncCtrl->lastGainIndexPrev = psShapeSt->LastGainIndex; /* Quantize gains */ silk_gains_quant( psEnc->sCmn.indices.GainsIndices, psEncCtrl->Gains_Q16, &psShapeSt->LastGainIndex, condCoding == CODE_CONDITIONALLY, psEnc->sCmn.nb_subfr ); /* Set quantizer offset for voiced signals. Larger offset when LTP coding gain is low or tilt is high (ie low-pass) */ if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { if( psEncCtrl->LTPredCodGain_Q7 + silk_RSHIFT( psEnc->sCmn.input_tilt_Q15, 8 ) > SILK_FIX_CONST( 1.0, 7 ) ) { psEnc->sCmn.indices.quantOffsetType = 0; } else { psEnc->sCmn.indices.quantOffsetType = 1; } } /* Quantizer boundary adjustment */ quant_offset_Q10 = silk_Quantization_Offsets_Q10[ psEnc->sCmn.indices.signalType >> 1 ][ psEnc->sCmn.indices.quantOffsetType ]; psEncCtrl->Lambda_Q10 = SILK_FIX_CONST( LAMBDA_OFFSET, 10 ) + silk_SMULBB( SILK_FIX_CONST( LAMBDA_DELAYED_DECISIONS, 10 ), psEnc->sCmn.nStatesDelayedDecision ) + silk_SMULWB( SILK_FIX_CONST( LAMBDA_SPEECH_ACT, 18 ), psEnc->sCmn.speech_activity_Q8 ) + silk_SMULWB( SILK_FIX_CONST( LAMBDA_INPUT_QUALITY, 12 ), psEncCtrl->input_quality_Q14 ) + silk_SMULWB( SILK_FIX_CONST( LAMBDA_CODING_QUALITY, 12 ), psEncCtrl->coding_quality_Q14 ) + silk_SMULWB( SILK_FIX_CONST( LAMBDA_QUANT_OFFSET, 16 ), quant_offset_Q10 ); silk_assert( psEncCtrl->Lambda_Q10 > 0 ); silk_assert( psEncCtrl->Lambda_Q10 < SILK_FIX_CONST( 2, 10 ) ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/regularize_correlations_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" /* Add noise to matrix diagonal */ void silk_regularize_correlations_FIX( opus_int32 *XX, /* I/O Correlation matrices */ opus_int32 *xx, /* I/O Correlation values */ opus_int32 noise, /* I Noise to add */ opus_int D /* I Dimension of XX */ ) { opus_int i; for( i = 0; i < D; i++ ) { matrix_ptr( &XX[ 0 ], i, i, D ) = silk_ADD32( matrix_ptr( &XX[ 0 ], i, i, D ), noise ); } xx[ 0 ] += noise; } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/residual_energy16_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" /* Residual energy: nrg = wxx - 2 * wXx * c + c' * wXX * c */ opus_int32 silk_residual_energy16_covar_FIX( const opus_int16 *c, /* I Prediction vector */ const opus_int32 *wXX, /* I Correlation matrix */ const opus_int32 *wXx, /* I Correlation vector */ opus_int32 wxx, /* I Signal energy */ opus_int D, /* I Dimension */ opus_int cQ /* I Q value for c vector 0 - 15 */ ) { opus_int i, j, lshifts, Qxtra; opus_int32 c_max, w_max, tmp, tmp2, nrg; opus_int cn[ MAX_MATRIX_SIZE ]; const opus_int32 *pRow; /* Safety checks */ silk_assert( D >= 0 ); silk_assert( D <= 16 ); silk_assert( cQ > 0 ); silk_assert( cQ < 16 ); lshifts = 16 - cQ; Qxtra = lshifts; c_max = 0; for( i = 0; i < D; i++ ) { c_max = silk_max_32( c_max, silk_abs( (opus_int32)c[ i ] ) ); } Qxtra = silk_min_int( Qxtra, silk_CLZ32( c_max ) - 17 ); w_max = silk_max_32( wXX[ 0 ], wXX[ D * D - 1 ] ); Qxtra = silk_min_int( Qxtra, silk_CLZ32( silk_MUL( D, silk_RSHIFT( silk_SMULWB( w_max, c_max ), 4 ) ) ) - 5 ); Qxtra = silk_max_int( Qxtra, 0 ); for( i = 0; i < D; i++ ) { cn[ i ] = silk_LSHIFT( ( opus_int )c[ i ], Qxtra ); silk_assert( silk_abs(cn[i]) <= ( silk_int16_MAX + 1 ) ); /* Check that silk_SMLAWB can be used */ } lshifts -= Qxtra; /* Compute wxx - 2 * wXx * c */ tmp = 0; for( i = 0; i < D; i++ ) { tmp = silk_SMLAWB( tmp, wXx[ i ], cn[ i ] ); } nrg = silk_RSHIFT( wxx, 1 + lshifts ) - tmp; /* Q: -lshifts - 1 */ /* Add c' * wXX * c, assuming wXX is symmetric */ tmp2 = 0; for( i = 0; i < D; i++ ) { tmp = 0; pRow = &wXX[ i * D ]; for( j = i + 1; j < D; j++ ) { tmp = silk_SMLAWB( tmp, pRow[ j ], cn[ j ] ); } tmp = silk_SMLAWB( tmp, silk_RSHIFT( pRow[ i ], 1 ), cn[ i ] ); tmp2 = silk_SMLAWB( tmp2, tmp, cn[ i ] ); } nrg = silk_ADD_LSHIFT32( nrg, tmp2, lshifts ); /* Q: -lshifts - 1 */ /* Keep one bit free always, because we add them for LSF interpolation */ if( nrg < 1 ) { nrg = 1; } else if( nrg > silk_RSHIFT( silk_int32_MAX, lshifts + 2 ) ) { nrg = silk_int32_MAX >> 1; } else { nrg = silk_LSHIFT( nrg, lshifts + 1 ); /* Q0 */ } return nrg; } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/residual_energy_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" #include "stack_alloc.h" /* Calculates residual energies of input subframes where all subframes have LPC_order */ /* of preceding samples */ void silk_residual_energy_FIX( opus_int32 nrgs[ MAX_NB_SUBFR ], /* O Residual energy per subframe */ opus_int nrgsQ[ MAX_NB_SUBFR ], /* O Q value per subframe */ const opus_int16 x[], /* I Input signal */ opus_int16 a_Q12[ 2 ][ MAX_LPC_ORDER ], /* I AR coefs for each frame half */ const opus_int32 gains[ MAX_NB_SUBFR ], /* I Quantization gains */ const opus_int subfr_length, /* I Subframe length */ const opus_int nb_subfr, /* I Number of subframes */ const opus_int LPC_order, /* I LPC order */ int arch /* I Run-time architecture */ ) { opus_int offset, i, j, rshift, lz1, lz2; opus_int16 *LPC_res_ptr; VARDECL( opus_int16, LPC_res ); const opus_int16 *x_ptr; opus_int32 tmp32; SAVE_STACK; x_ptr = x; offset = LPC_order + subfr_length; /* Filter input to create the LPC residual for each frame half, and measure subframe energies */ ALLOC( LPC_res, ( MAX_NB_SUBFR >> 1 ) * offset, opus_int16 ); silk_assert( ( nb_subfr >> 1 ) * ( MAX_NB_SUBFR >> 1 ) == nb_subfr ); for( i = 0; i < nb_subfr >> 1; i++ ) { /* Calculate half frame LPC residual signal including preceding samples */ silk_LPC_analysis_filter( LPC_res, x_ptr, a_Q12[ i ], ( MAX_NB_SUBFR >> 1 ) * offset, LPC_order, arch ); /* Point to first subframe of the just calculated LPC residual signal */ LPC_res_ptr = LPC_res + LPC_order; for( j = 0; j < ( MAX_NB_SUBFR >> 1 ); j++ ) { /* Measure subframe energy */ silk_sum_sqr_shift( &nrgs[ i * ( MAX_NB_SUBFR >> 1 ) + j ], &rshift, LPC_res_ptr, subfr_length ); /* Set Q values for the measured energy */ nrgsQ[ i * ( MAX_NB_SUBFR >> 1 ) + j ] = -rshift; /* Move to next subframe */ LPC_res_ptr += offset; } /* Move to next frame half */ x_ptr += ( MAX_NB_SUBFR >> 1 ) * offset; } /* Apply the squared subframe gains */ for( i = 0; i < nb_subfr; i++ ) { /* Fully upscale gains and energies */ lz1 = silk_CLZ32( nrgs[ i ] ) - 1; lz2 = silk_CLZ32( gains[ i ] ) - 1; tmp32 = silk_LSHIFT32( gains[ i ], lz2 ); /* Find squared gains */ tmp32 = silk_SMMUL( tmp32, tmp32 ); /* Q( 2 * lz2 - 32 )*/ /* Scale energies */ nrgs[ i ] = silk_SMMUL( tmp32, silk_LSHIFT32( nrgs[ i ], lz1 ) ); /* Q( nrgsQ[ i ] + lz1 + 2 * lz2 - 32 - 32 )*/ nrgsQ[ i ] += lz1 + 2 * lz2 - 32 - 32; } RESTORE_STACK; } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/schur64_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" /* Slower than schur(), but more accurate. */ /* Uses SMULL(), available on armv4 */ opus_int32 silk_schur64( /* O returns residual energy */ opus_int32 rc_Q16[], /* O Reflection coefficients [order] Q16 */ const opus_int32 c[], /* I Correlations [order+1] */ opus_int32 order /* I Prediction order */ ) { opus_int k, n; opus_int32 C[ SILK_MAX_ORDER_LPC + 1 ][ 2 ]; opus_int32 Ctmp1_Q30, Ctmp2_Q30, rc_tmp_Q31; silk_assert( order==6||order==8||order==10||order==12||order==14||order==16 ); /* Check for invalid input */ if( c[ 0 ] <= 0 ) { silk_memset( rc_Q16, 0, order * sizeof( opus_int32 ) ); return 0; } for( k = 0; k < order + 1; k++ ) { C[ k ][ 0 ] = C[ k ][ 1 ] = c[ k ]; } for( k = 0; k < order; k++ ) { /* Check that we won't be getting an unstable rc, otherwise stop here. */ if (silk_abs_int32(C[ k + 1 ][ 0 ]) >= C[ 0 ][ 1 ]) { if ( C[ k + 1 ][ 0 ] > 0 ) { rc_Q16[ k ] = -SILK_FIX_CONST( .99f, 16 ); } else { rc_Q16[ k ] = SILK_FIX_CONST( .99f, 16 ); } k++; break; } /* Get reflection coefficient: divide two Q30 values and get result in Q31 */ rc_tmp_Q31 = silk_DIV32_varQ( -C[ k + 1 ][ 0 ], C[ 0 ][ 1 ], 31 ); /* Save the output */ rc_Q16[ k ] = silk_RSHIFT_ROUND( rc_tmp_Q31, 15 ); /* Update correlations */ for( n = 0; n < order - k; n++ ) { Ctmp1_Q30 = C[ n + k + 1 ][ 0 ]; Ctmp2_Q30 = C[ n ][ 1 ]; /* Multiply and add the highest int32 */ C[ n + k + 1 ][ 0 ] = Ctmp1_Q30 + silk_SMMUL( silk_LSHIFT( Ctmp2_Q30, 1 ), rc_tmp_Q31 ); C[ n ][ 1 ] = Ctmp2_Q30 + silk_SMMUL( silk_LSHIFT( Ctmp1_Q30, 1 ), rc_tmp_Q31 ); } } for(; k < order; k++ ) { rc_Q16[ k ] = 0; } return silk_max_32( 1, C[ 0 ][ 1 ] ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/schur_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" /* Faster than schur64(), but much less accurate. */ /* uses SMLAWB(), requiring armv5E and higher. */ opus_int32 silk_schur( /* O Returns residual energy */ opus_int16 *rc_Q15, /* O reflection coefficients [order] Q15 */ const opus_int32 *c, /* I correlations [order+1] */ const opus_int32 order /* I prediction order */ ) { opus_int k, n, lz; opus_int32 C[ SILK_MAX_ORDER_LPC + 1 ][ 2 ]; opus_int32 Ctmp1, Ctmp2, rc_tmp_Q15; silk_assert( order==6||order==8||order==10||order==12||order==14||order==16 ); /* Get number of leading zeros */ lz = silk_CLZ32( c[ 0 ] ); /* Copy correlations and adjust level to Q30 */ if( lz < 2 ) { /* lz must be 1, so shift one to the right */ for( k = 0; k < order + 1; k++ ) { C[ k ][ 0 ] = C[ k ][ 1 ] = silk_RSHIFT( c[ k ], 1 ); } } else if( lz > 2 ) { /* Shift to the left */ lz -= 2; for( k = 0; k < order + 1; k++ ) { C[ k ][ 0 ] = C[ k ][ 1 ] = silk_LSHIFT( c[ k ], lz ); } } else { /* No need to shift */ for( k = 0; k < order + 1; k++ ) { C[ k ][ 0 ] = C[ k ][ 1 ] = c[ k ]; } } for( k = 0; k < order; k++ ) { /* Check that we won't be getting an unstable rc, otherwise stop here. */ if (silk_abs_int32(C[ k + 1 ][ 0 ]) >= C[ 0 ][ 1 ]) { if ( C[ k + 1 ][ 0 ] > 0 ) { rc_Q15[ k ] = -SILK_FIX_CONST( .99f, 15 ); } else { rc_Q15[ k ] = SILK_FIX_CONST( .99f, 15 ); } k++; break; } /* Get reflection coefficient */ rc_tmp_Q15 = -silk_DIV32_16( C[ k + 1 ][ 0 ], silk_max_32( silk_RSHIFT( C[ 0 ][ 1 ], 15 ), 1 ) ); /* Clip (shouldn't happen for properly conditioned inputs) */ rc_tmp_Q15 = silk_SAT16( rc_tmp_Q15 ); /* Store */ rc_Q15[ k ] = (opus_int16)rc_tmp_Q15; /* Update correlations */ for( n = 0; n < order - k; n++ ) { Ctmp1 = C[ n + k + 1 ][ 0 ]; Ctmp2 = C[ n ][ 1 ]; C[ n + k + 1 ][ 0 ] = silk_SMLAWB( Ctmp1, silk_LSHIFT( Ctmp2, 1 ), rc_tmp_Q15 ); C[ n ][ 1 ] = silk_SMLAWB( Ctmp2, silk_LSHIFT( Ctmp1, 1 ), rc_tmp_Q15 ); } } for(; k < order; k++ ) { rc_Q15[ k ] = 0; } /* return residual energy */ return silk_max_32( 1, C[ 0 ][ 1 ] ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/solve_LS_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" #include "stack_alloc.h" #include "tuning_parameters.h" /*****************************/ /* Internal function headers */ /*****************************/ typedef struct { opus_int32 Q36_part; opus_int32 Q48_part; } inv_D_t; /* Factorize square matrix A into LDL form */ static OPUS_INLINE void silk_LDL_factorize_FIX( opus_int32 *A, /* I/O Pointer to Symetric Square Matrix */ opus_int M, /* I Size of Matrix */ opus_int32 *L_Q16, /* I/O Pointer to Square Upper triangular Matrix */ inv_D_t *inv_D /* I/O Pointer to vector holding inverted diagonal elements of D */ ); /* Solve Lx = b, when L is lower triangular and has ones on the diagonal */ static OPUS_INLINE void silk_LS_SolveFirst_FIX( const opus_int32 *L_Q16, /* I Pointer to Lower Triangular Matrix */ opus_int M, /* I Dim of Matrix equation */ const opus_int32 *b, /* I b Vector */ opus_int32 *x_Q16 /* O x Vector */ ); /* Solve L^t*x = b, where L is lower triangular with ones on the diagonal */ static OPUS_INLINE void silk_LS_SolveLast_FIX( const opus_int32 *L_Q16, /* I Pointer to Lower Triangular Matrix */ const opus_int M, /* I Dim of Matrix equation */ const opus_int32 *b, /* I b Vector */ opus_int32 *x_Q16 /* O x Vector */ ); static OPUS_INLINE void silk_LS_divide_Q16_FIX( opus_int32 T[], /* I/O Numenator vector */ inv_D_t *inv_D, /* I 1 / D vector */ opus_int M /* I dimension */ ); /* Solves Ax = b, assuming A is symmetric */ void silk_solve_LDL_FIX( opus_int32 *A, /* I Pointer to symetric square matrix A */ opus_int M, /* I Size of matrix */ const opus_int32 *b, /* I Pointer to b vector */ opus_int32 *x_Q16 /* O Pointer to x solution vector */ ) { VARDECL( opus_int32, L_Q16 ); opus_int32 Y[ MAX_MATRIX_SIZE ]; inv_D_t inv_D[ MAX_MATRIX_SIZE ]; SAVE_STACK; silk_assert( M <= MAX_MATRIX_SIZE ); ALLOC( L_Q16, M * M, opus_int32 ); /*************************************************** Factorize A by LDL such that A = L*D*L', where L is lower triangular with ones on diagonal ****************************************************/ silk_LDL_factorize_FIX( A, M, L_Q16, inv_D ); /**************************************************** * substitute D*L'*x = Y. ie: L*D*L'*x = b => L*Y = b <=> Y = inv(L)*b ******************************************************/ silk_LS_SolveFirst_FIX( L_Q16, M, b, Y ); /**************************************************** D*L'*x = Y <=> L'*x = inv(D)*Y, because D is diagonal just multiply with 1/d_i ****************************************************/ silk_LS_divide_Q16_FIX( Y, inv_D, M ); /**************************************************** x = inv(L') * inv(D) * Y *****************************************************/ silk_LS_SolveLast_FIX( L_Q16, M, Y, x_Q16 ); RESTORE_STACK; } static OPUS_INLINE void silk_LDL_factorize_FIX( opus_int32 *A, /* I/O Pointer to Symetric Square Matrix */ opus_int M, /* I Size of Matrix */ opus_int32 *L_Q16, /* I/O Pointer to Square Upper triangular Matrix */ inv_D_t *inv_D /* I/O Pointer to vector holding inverted diagonal elements of D */ ) { opus_int i, j, k, status, loop_count; const opus_int32 *ptr1, *ptr2; opus_int32 diag_min_value, tmp_32, err; opus_int32 v_Q0[ MAX_MATRIX_SIZE ], D_Q0[ MAX_MATRIX_SIZE ]; opus_int32 one_div_diag_Q36, one_div_diag_Q40, one_div_diag_Q48; silk_assert( M <= MAX_MATRIX_SIZE ); status = 1; diag_min_value = silk_max_32( silk_SMMUL( silk_ADD_SAT32( A[ 0 ], A[ silk_SMULBB( M, M ) - 1 ] ), SILK_FIX_CONST( FIND_LTP_COND_FAC, 31 ) ), 1 << 9 ); for( loop_count = 0; loop_count < M && status == 1; loop_count++ ) { status = 0; for( j = 0; j < M; j++ ) { ptr1 = matrix_adr( L_Q16, j, 0, M ); tmp_32 = 0; for( i = 0; i < j; i++ ) { v_Q0[ i ] = silk_SMULWW( D_Q0[ i ], ptr1[ i ] ); /* Q0 */ tmp_32 = silk_SMLAWW( tmp_32, v_Q0[ i ], ptr1[ i ] ); /* Q0 */ } tmp_32 = silk_SUB32( matrix_ptr( A, j, j, M ), tmp_32 ); if( tmp_32 < diag_min_value ) { tmp_32 = silk_SUB32( silk_SMULBB( loop_count + 1, diag_min_value ), tmp_32 ); /* Matrix not positive semi-definite, or ill conditioned */ for( i = 0; i < M; i++ ) { matrix_ptr( A, i, i, M ) = silk_ADD32( matrix_ptr( A, i, i, M ), tmp_32 ); } status = 1; break; } D_Q0[ j ] = tmp_32; /* always < max(Correlation) */ /* two-step division */ one_div_diag_Q36 = silk_INVERSE32_varQ( tmp_32, 36 ); /* Q36 */ one_div_diag_Q40 = silk_LSHIFT( one_div_diag_Q36, 4 ); /* Q40 */ err = silk_SUB32( (opus_int32)1 << 24, silk_SMULWW( tmp_32, one_div_diag_Q40 ) ); /* Q24 */ one_div_diag_Q48 = silk_SMULWW( err, one_div_diag_Q40 ); /* Q48 */ /* Save 1/Ds */ inv_D[ j ].Q36_part = one_div_diag_Q36; inv_D[ j ].Q48_part = one_div_diag_Q48; matrix_ptr( L_Q16, j, j, M ) = 65536; /* 1.0 in Q16 */ ptr1 = matrix_adr( A, j, 0, M ); ptr2 = matrix_adr( L_Q16, j + 1, 0, M ); for( i = j + 1; i < M; i++ ) { tmp_32 = 0; for( k = 0; k < j; k++ ) { tmp_32 = silk_SMLAWW( tmp_32, v_Q0[ k ], ptr2[ k ] ); /* Q0 */ } tmp_32 = silk_SUB32( ptr1[ i ], tmp_32 ); /* always < max(Correlation) */ /* tmp_32 / D_Q0[j] : Divide to Q16 */ matrix_ptr( L_Q16, i, j, M ) = silk_ADD32( silk_SMMUL( tmp_32, one_div_diag_Q48 ), silk_RSHIFT( silk_SMULWW( tmp_32, one_div_diag_Q36 ), 4 ) ); /* go to next column */ ptr2 += M; } } } silk_assert( status == 0 ); } static OPUS_INLINE void silk_LS_divide_Q16_FIX( opus_int32 T[], /* I/O Numenator vector */ inv_D_t *inv_D, /* I 1 / D vector */ opus_int M /* I dimension */ ) { opus_int i; opus_int32 tmp_32; opus_int32 one_div_diag_Q36, one_div_diag_Q48; for( i = 0; i < M; i++ ) { one_div_diag_Q36 = inv_D[ i ].Q36_part; one_div_diag_Q48 = inv_D[ i ].Q48_part; tmp_32 = T[ i ]; T[ i ] = silk_ADD32( silk_SMMUL( tmp_32, one_div_diag_Q48 ), silk_RSHIFT( silk_SMULWW( tmp_32, one_div_diag_Q36 ), 4 ) ); } } /* Solve Lx = b, when L is lower triangular and has ones on the diagonal */ static OPUS_INLINE void silk_LS_SolveFirst_FIX( const opus_int32 *L_Q16, /* I Pointer to Lower Triangular Matrix */ opus_int M, /* I Dim of Matrix equation */ const opus_int32 *b, /* I b Vector */ opus_int32 *x_Q16 /* O x Vector */ ) { opus_int i, j; const opus_int32 *ptr32; opus_int32 tmp_32; for( i = 0; i < M; i++ ) { ptr32 = matrix_adr( L_Q16, i, 0, M ); tmp_32 = 0; for( j = 0; j < i; j++ ) { tmp_32 = silk_SMLAWW( tmp_32, ptr32[ j ], x_Q16[ j ] ); } x_Q16[ i ] = silk_SUB32( b[ i ], tmp_32 ); } } /* Solve L^t*x = b, where L is lower triangular with ones on the diagonal */ static OPUS_INLINE void silk_LS_SolveLast_FIX( const opus_int32 *L_Q16, /* I Pointer to Lower Triangular Matrix */ const opus_int M, /* I Dim of Matrix equation */ const opus_int32 *b, /* I b Vector */ opus_int32 *x_Q16 /* O x Vector */ ) { opus_int i, j; const opus_int32 *ptr32; opus_int32 tmp_32; for( i = M - 1; i >= 0; i-- ) { ptr32 = matrix_adr( L_Q16, 0, i, M ); tmp_32 = 0; for( j = M - 1; j > i; j-- ) { tmp_32 = silk_SMLAWW( tmp_32, ptr32[ silk_SMULBB( j, M ) ], x_Q16[ j ] ); } x_Q16[ i ] = silk_SUB32( b[ i ], tmp_32 ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/structs_FIX.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_STRUCTS_FIX_H #define SILK_STRUCTS_FIX_H #include "typedef.h" #include "main.h" #include "structs.h" #ifdef __cplusplus extern "C" { #endif /********************************/ /* Noise shaping analysis state */ /********************************/ typedef struct { opus_int8 LastGainIndex; opus_int32 HarmBoost_smth_Q16; opus_int32 HarmShapeGain_smth_Q16; opus_int32 Tilt_smth_Q16; } silk_shape_state_FIX; /********************************/ /* Prefilter state */ /********************************/ typedef struct { opus_int16 sLTP_shp[ LTP_BUF_LENGTH ]; opus_int32 sAR_shp[ MAX_SHAPE_LPC_ORDER + 1 ]; opus_int sLTP_shp_buf_idx; opus_int32 sLF_AR_shp_Q12; opus_int32 sLF_MA_shp_Q12; opus_int32 sHarmHP_Q2; opus_int32 rand_seed; opus_int lagPrev; } silk_prefilter_state_FIX; /********************************/ /* Encoder state FIX */ /********************************/ typedef struct { silk_encoder_state sCmn; /* Common struct, shared with floating-point code */ silk_shape_state_FIX sShape; /* Shape state */ silk_prefilter_state_FIX sPrefilt; /* Prefilter State */ /* Buffer for find pitch and noise shape analysis */ silk_DWORD_ALIGN opus_int16 x_buf[ 2 * MAX_FRAME_LENGTH + LA_SHAPE_MAX ];/* Buffer for find pitch and noise shape analysis */ opus_int LTPCorr_Q15; /* Normalized correlation from pitch lag estimator */ } silk_encoder_state_FIX; /************************/ /* Encoder control FIX */ /************************/ typedef struct { /* Prediction and coding parameters */ opus_int32 Gains_Q16[ MAX_NB_SUBFR ]; silk_DWORD_ALIGN opus_int16 PredCoef_Q12[ 2 ][ MAX_LPC_ORDER ]; opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ]; opus_int LTP_scale_Q14; opus_int pitchL[ MAX_NB_SUBFR ]; /* Noise shaping parameters */ /* Testing */ silk_DWORD_ALIGN opus_int16 AR1_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ]; silk_DWORD_ALIGN opus_int16 AR2_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ]; opus_int32 LF_shp_Q14[ MAX_NB_SUBFR ]; /* Packs two int16 coefficients per int32 value */ opus_int GainsPre_Q14[ MAX_NB_SUBFR ]; opus_int HarmBoost_Q14[ MAX_NB_SUBFR ]; opus_int Tilt_Q14[ MAX_NB_SUBFR ]; opus_int HarmShapeGain_Q14[ MAX_NB_SUBFR ]; opus_int Lambda_Q10; opus_int input_quality_Q14; opus_int coding_quality_Q14; /* measures */ opus_int sparseness_Q8; opus_int32 predGain_Q16; opus_int LTPredCodGain_Q7; opus_int32 ResNrg[ MAX_NB_SUBFR ]; /* Residual energy per subframe */ opus_int ResNrgQ[ MAX_NB_SUBFR ]; /* Q domain for the residual energy > 0 */ /* Parameters for CBR mode */ opus_int32 GainsUnq_Q16[ MAX_NB_SUBFR ]; opus_int8 lastGainIndexPrev; } silk_encoder_control_FIX; /************************/ /* Encoder Super Struct */ /************************/ typedef struct { silk_encoder_state_FIX state_Fxx[ ENCODER_NUM_CHANNELS ]; stereo_enc_state sStereo; opus_int32 nBitsUsedLBRR; opus_int32 nBitsExceeded; opus_int nChannelsAPI; opus_int nChannelsInternal; opus_int nPrevChannelsInternal; opus_int timeSinceSwitchAllowed_ms; opus_int allowBandwidthSwitch; opus_int prev_decode_only_middle; } silk_encoder; #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/vector_ops_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" #include "pitch.h" /* Copy and multiply a vector by a constant */ void silk_scale_copy_vector16( opus_int16 *data_out, const opus_int16 *data_in, opus_int32 gain_Q16, /* I Gain in Q16 */ const opus_int dataSize /* I Length */ ) { opus_int i; opus_int32 tmp32; for( i = 0; i < dataSize; i++ ) { tmp32 = silk_SMULWB( gain_Q16, data_in[ i ] ); data_out[ i ] = (opus_int16)silk_CHECK_FIT16( tmp32 ); } } /* Multiply a vector by a constant */ void silk_scale_vector32_Q26_lshift_18( opus_int32 *data1, /* I/O Q0/Q18 */ opus_int32 gain_Q26, /* I Q26 */ opus_int dataSize /* I length */ ) { opus_int i; for( i = 0; i < dataSize; i++ ) { data1[ i ] = (opus_int32)silk_CHECK_FIT32( silk_RSHIFT64( silk_SMULL( data1[ i ], gain_Q26 ), 8 ) ); /* OUTPUT: Q18 */ } } /* sum = for(i=0;i6, memory access can be reduced by half. */ opus_int32 silk_inner_prod_aligned( const opus_int16 *const inVec1, /* I input vector 1 */ const opus_int16 *const inVec2, /* I input vector 2 */ const opus_int len, /* I vector lengths */ int arch /* I Run-time architecture */ ) { #ifdef FIXED_POINT return celt_inner_prod(inVec1, inVec2, len, arch); #else opus_int i; opus_int32 sum = 0; for( i = 0; i < len; i++ ) { sum = silk_SMLABB( sum, inVec1[ i ], inVec2[ i ] ); } return sum; #endif } opus_int64 silk_inner_prod16_aligned_64_c( const opus_int16 *inVec1, /* I input vector 1 */ const opus_int16 *inVec2, /* I input vector 2 */ const opus_int len /* I vector lengths */ ) { opus_int i; opus_int64 sum = 0; for( i = 0; i < len; i++ ) { sum = silk_SMLALBB( sum, inVec1[ i ], inVec2[ i ] ); } return sum; } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/warped_autocorrelation_FIX.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FIX.h" #define QC 10 #define QS 14 #if defined(MIPSr1_ASM) #include "mips/warped_autocorrelation_FIX_mipsr1.h" #endif #ifndef OVERRIDE_silk_warped_autocorrelation_FIX /* Autocorrelations for a warped frequency axis */ void silk_warped_autocorrelation_FIX( opus_int32 *corr, /* O Result [order + 1] */ opus_int *scale, /* O Scaling of the correlation vector */ const opus_int16 *input, /* I Input data to correlate */ const opus_int warping_Q16, /* I Warping coefficient */ const opus_int length, /* I Length of input */ const opus_int order /* I Correlation order (even) */ ) { opus_int n, i, lsh; opus_int32 tmp1_QS, tmp2_QS; opus_int32 state_QS[ MAX_SHAPE_LPC_ORDER + 1 ] = { 0 }; opus_int64 corr_QC[ MAX_SHAPE_LPC_ORDER + 1 ] = { 0 }; /* Order must be even */ silk_assert( ( order & 1 ) == 0 ); silk_assert( 2 * QS - QC >= 0 ); /* Loop over samples */ for( n = 0; n < length; n++ ) { tmp1_QS = silk_LSHIFT32( (opus_int32)input[ n ], QS ); /* Loop over allpass sections */ for( i = 0; i < order; i += 2 ) { /* Output of allpass section */ tmp2_QS = silk_SMLAWB( state_QS[ i ], state_QS[ i + 1 ] - tmp1_QS, warping_Q16 ); state_QS[ i ] = tmp1_QS; corr_QC[ i ] += silk_RSHIFT64( silk_SMULL( tmp1_QS, state_QS[ 0 ] ), 2 * QS - QC ); /* Output of allpass section */ tmp1_QS = silk_SMLAWB( state_QS[ i + 1 ], state_QS[ i + 2 ] - tmp2_QS, warping_Q16 ); state_QS[ i + 1 ] = tmp2_QS; corr_QC[ i + 1 ] += silk_RSHIFT64( silk_SMULL( tmp2_QS, state_QS[ 0 ] ), 2 * QS - QC ); } state_QS[ order ] = tmp1_QS; corr_QC[ order ] += silk_RSHIFT64( silk_SMULL( tmp1_QS, state_QS[ 0 ] ), 2 * QS - QC ); } lsh = silk_CLZ64( corr_QC[ 0 ] ) - 35; lsh = silk_LIMIT( lsh, -12 - QC, 30 - QC ); *scale = -( QC + lsh ); silk_assert( *scale >= -30 && *scale <= 12 ); if( lsh >= 0 ) { for( i = 0; i < order + 1; i++ ) { corr[ i ] = (opus_int32)silk_CHECK_FIT32( silk_LSHIFT64( corr_QC[ i ], lsh ) ); } } else { for( i = 0; i < order + 1; i++ ) { corr[ i ] = (opus_int32)silk_CHECK_FIT32( silk_RSHIFT64( corr_QC[ i ], -lsh ) ); } } silk_assert( corr_QC[ 0 ] >= 0 ); /* If breaking, decrease QC*/ } #endif /* OVERRIDE_silk_warped_autocorrelation_FIX */ ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/x86/burg_modified_FIX_sse.c ================================================ /* Copyright (c) 2014, Cisco Systems, INC Written by XiangMingZhu WeiZhou MinPeng YanWang Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "SigProc_FIX.h" #include "define.h" #include "tuning_parameters.h" #include "pitch.h" #include "celt/x86/x86cpu.h" #define MAX_FRAME_SIZE 384 /* subfr_length * nb_subfr = ( 0.005 * 16000 + 16 ) * 4 = 384 */ #define QA 25 #define N_BITS_HEAD_ROOM 2 #define MIN_RSHIFTS -16 #define MAX_RSHIFTS (32 - QA) /* Compute reflection coefficients from input signal */ void silk_burg_modified_sse4_1( opus_int32 *res_nrg, /* O Residual energy */ opus_int *res_nrg_Q, /* O Residual energy Q value */ opus_int32 A_Q16[], /* O Prediction coefficients (length order) */ const opus_int16 x[], /* I Input signal, length: nb_subfr * ( D + subfr_length ) */ const opus_int32 minInvGain_Q30, /* I Inverse of max prediction gain */ const opus_int subfr_length, /* I Input signal subframe length (incl. D preceding samples) */ const opus_int nb_subfr, /* I Number of subframes stacked in x */ const opus_int D, /* I Order */ int arch /* I Run-time architecture */ ) { opus_int k, n, s, lz, rshifts, rshifts_extra, reached_max_gain; opus_int32 C0, num, nrg, rc_Q31, invGain_Q30, Atmp_QA, Atmp1, tmp1, tmp2, x1, x2; const opus_int16 *x_ptr; opus_int32 C_first_row[ SILK_MAX_ORDER_LPC ]; opus_int32 C_last_row[ SILK_MAX_ORDER_LPC ]; opus_int32 Af_QA[ SILK_MAX_ORDER_LPC ]; opus_int32 CAf[ SILK_MAX_ORDER_LPC + 1 ]; opus_int32 CAb[ SILK_MAX_ORDER_LPC + 1 ]; opus_int32 xcorr[ SILK_MAX_ORDER_LPC ]; __m128i FIRST_3210, LAST_3210, ATMP_3210, TMP1_3210, TMP2_3210, T1_3210, T2_3210, PTR_3210, SUBFR_3210, X1_3210, X2_3210; __m128i CONST1 = _mm_set1_epi32(1); silk_assert( subfr_length * nb_subfr <= MAX_FRAME_SIZE ); /* Compute autocorrelations, added over subframes */ silk_sum_sqr_shift( &C0, &rshifts, x, nb_subfr * subfr_length ); if( rshifts > MAX_RSHIFTS ) { C0 = silk_LSHIFT32( C0, rshifts - MAX_RSHIFTS ); silk_assert( C0 > 0 ); rshifts = MAX_RSHIFTS; } else { lz = silk_CLZ32( C0 ) - 1; rshifts_extra = N_BITS_HEAD_ROOM - lz; if( rshifts_extra > 0 ) { rshifts_extra = silk_min( rshifts_extra, MAX_RSHIFTS - rshifts ); C0 = silk_RSHIFT32( C0, rshifts_extra ); } else { rshifts_extra = silk_max( rshifts_extra, MIN_RSHIFTS - rshifts ); C0 = silk_LSHIFT32( C0, -rshifts_extra ); } rshifts += rshifts_extra; } CAb[ 0 ] = CAf[ 0 ] = C0 + silk_SMMUL( SILK_FIX_CONST( FIND_LPC_COND_FAC, 32 ), C0 ) + 1; /* Q(-rshifts) */ silk_memset( C_first_row, 0, SILK_MAX_ORDER_LPC * sizeof( opus_int32 ) ); if( rshifts > 0 ) { for( s = 0; s < nb_subfr; s++ ) { x_ptr = x + s * subfr_length; for( n = 1; n < D + 1; n++ ) { C_first_row[ n - 1 ] += (opus_int32)silk_RSHIFT64( silk_inner_prod16_aligned_64( x_ptr, x_ptr + n, subfr_length - n, arch ), rshifts ); } } } else { for( s = 0; s < nb_subfr; s++ ) { int i; opus_int32 d; x_ptr = x + s * subfr_length; celt_pitch_xcorr(x_ptr, x_ptr + 1, xcorr, subfr_length - D, D, arch ); for( n = 1; n < D + 1; n++ ) { for ( i = n + subfr_length - D, d = 0; i < subfr_length; i++ ) d = MAC16_16( d, x_ptr[ i ], x_ptr[ i - n ] ); xcorr[ n - 1 ] += d; } for( n = 1; n < D + 1; n++ ) { C_first_row[ n - 1 ] += silk_LSHIFT32( xcorr[ n - 1 ], -rshifts ); } } } silk_memcpy( C_last_row, C_first_row, SILK_MAX_ORDER_LPC * sizeof( opus_int32 ) ); /* Initialize */ CAb[ 0 ] = CAf[ 0 ] = C0 + silk_SMMUL( SILK_FIX_CONST( FIND_LPC_COND_FAC, 32 ), C0 ) + 1; /* Q(-rshifts) */ invGain_Q30 = (opus_int32)1 << 30; reached_max_gain = 0; for( n = 0; n < D; n++ ) { /* Update first row of correlation matrix (without first element) */ /* Update last row of correlation matrix (without last element, stored in reversed order) */ /* Update C * Af */ /* Update C * flipud(Af) (stored in reversed order) */ if( rshifts > -2 ) { for( s = 0; s < nb_subfr; s++ ) { x_ptr = x + s * subfr_length; x1 = -silk_LSHIFT32( (opus_int32)x_ptr[ n ], 16 - rshifts ); /* Q(16-rshifts) */ x2 = -silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], 16 - rshifts ); /* Q(16-rshifts) */ tmp1 = silk_LSHIFT32( (opus_int32)x_ptr[ n ], QA - 16 ); /* Q(QA-16) */ tmp2 = silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], QA - 16 ); /* Q(QA-16) */ for( k = 0; k < n; k++ ) { C_first_row[ k ] = silk_SMLAWB( C_first_row[ k ], x1, x_ptr[ n - k - 1 ] ); /* Q( -rshifts ) */ C_last_row[ k ] = silk_SMLAWB( C_last_row[ k ], x2, x_ptr[ subfr_length - n + k ] ); /* Q( -rshifts ) */ Atmp_QA = Af_QA[ k ]; tmp1 = silk_SMLAWB( tmp1, Atmp_QA, x_ptr[ n - k - 1 ] ); /* Q(QA-16) */ tmp2 = silk_SMLAWB( tmp2, Atmp_QA, x_ptr[ subfr_length - n + k ] ); /* Q(QA-16) */ } tmp1 = silk_LSHIFT32( -tmp1, 32 - QA - rshifts ); /* Q(16-rshifts) */ tmp2 = silk_LSHIFT32( -tmp2, 32 - QA - rshifts ); /* Q(16-rshifts) */ for( k = 0; k <= n; k++ ) { CAf[ k ] = silk_SMLAWB( CAf[ k ], tmp1, x_ptr[ n - k ] ); /* Q( -rshift ) */ CAb[ k ] = silk_SMLAWB( CAb[ k ], tmp2, x_ptr[ subfr_length - n + k - 1 ] ); /* Q( -rshift ) */ } } } else { for( s = 0; s < nb_subfr; s++ ) { x_ptr = x + s * subfr_length; x1 = -silk_LSHIFT32( (opus_int32)x_ptr[ n ], -rshifts ); /* Q( -rshifts ) */ x2 = -silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], -rshifts ); /* Q( -rshifts ) */ tmp1 = silk_LSHIFT32( (opus_int32)x_ptr[ n ], 17 ); /* Q17 */ tmp2 = silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], 17 ); /* Q17 */ X1_3210 = _mm_set1_epi32( x1 ); X2_3210 = _mm_set1_epi32( x2 ); TMP1_3210 = _mm_setzero_si128(); TMP2_3210 = _mm_setzero_si128(); for( k = 0; k < n - 3; k += 4 ) { PTR_3210 = OP_CVTEPI16_EPI32_M64( &x_ptr[ n - k - 1 - 3 ] ); SUBFR_3210 = OP_CVTEPI16_EPI32_M64( &x_ptr[ subfr_length - n + k ] ); FIRST_3210 = _mm_loadu_si128( (__m128i *)&C_first_row[ k ] ); PTR_3210 = _mm_shuffle_epi32( PTR_3210, _MM_SHUFFLE( 0, 1, 2, 3 ) ); LAST_3210 = _mm_loadu_si128( (__m128i *)&C_last_row[ k ] ); ATMP_3210 = _mm_loadu_si128( (__m128i *)&Af_QA[ k ] ); T1_3210 = _mm_mullo_epi32( PTR_3210, X1_3210 ); T2_3210 = _mm_mullo_epi32( SUBFR_3210, X2_3210 ); ATMP_3210 = _mm_srai_epi32( ATMP_3210, 7 ); ATMP_3210 = _mm_add_epi32( ATMP_3210, CONST1 ); ATMP_3210 = _mm_srai_epi32( ATMP_3210, 1 ); FIRST_3210 = _mm_add_epi32( FIRST_3210, T1_3210 ); LAST_3210 = _mm_add_epi32( LAST_3210, T2_3210 ); PTR_3210 = _mm_mullo_epi32( ATMP_3210, PTR_3210 ); SUBFR_3210 = _mm_mullo_epi32( ATMP_3210, SUBFR_3210 ); _mm_storeu_si128( (__m128i *)&C_first_row[ k ], FIRST_3210 ); _mm_storeu_si128( (__m128i *)&C_last_row[ k ], LAST_3210 ); TMP1_3210 = _mm_add_epi32( TMP1_3210, PTR_3210 ); TMP2_3210 = _mm_add_epi32( TMP2_3210, SUBFR_3210 ); } TMP1_3210 = _mm_add_epi32( TMP1_3210, _mm_unpackhi_epi64(TMP1_3210, TMP1_3210 ) ); TMP2_3210 = _mm_add_epi32( TMP2_3210, _mm_unpackhi_epi64(TMP2_3210, TMP2_3210 ) ); TMP1_3210 = _mm_add_epi32( TMP1_3210, _mm_shufflelo_epi16(TMP1_3210, 0x0E ) ); TMP2_3210 = _mm_add_epi32( TMP2_3210, _mm_shufflelo_epi16(TMP2_3210, 0x0E ) ); tmp1 += _mm_cvtsi128_si32( TMP1_3210 ); tmp2 += _mm_cvtsi128_si32( TMP2_3210 ); for( ; k < n; k++ ) { C_first_row[ k ] = silk_MLA( C_first_row[ k ], x1, x_ptr[ n - k - 1 ] ); /* Q( -rshifts ) */ C_last_row[ k ] = silk_MLA( C_last_row[ k ], x2, x_ptr[ subfr_length - n + k ] ); /* Q( -rshifts ) */ Atmp1 = silk_RSHIFT_ROUND( Af_QA[ k ], QA - 17 ); /* Q17 */ tmp1 = silk_MLA( tmp1, x_ptr[ n - k - 1 ], Atmp1 ); /* Q17 */ tmp2 = silk_MLA( tmp2, x_ptr[ subfr_length - n + k ], Atmp1 ); /* Q17 */ } tmp1 = -tmp1; /* Q17 */ tmp2 = -tmp2; /* Q17 */ { __m128i xmm_tmp1, xmm_tmp2; __m128i xmm_x_ptr_n_k_x2x0, xmm_x_ptr_n_k_x3x1; __m128i xmm_x_ptr_sub_x2x0, xmm_x_ptr_sub_x3x1; xmm_tmp1 = _mm_set1_epi32( tmp1 ); xmm_tmp2 = _mm_set1_epi32( tmp2 ); for( k = 0; k <= n - 3; k += 4 ) { xmm_x_ptr_n_k_x2x0 = OP_CVTEPI16_EPI32_M64( &x_ptr[ n - k - 3 ] ); xmm_x_ptr_sub_x2x0 = OP_CVTEPI16_EPI32_M64( &x_ptr[ subfr_length - n + k - 1 ] ); xmm_x_ptr_n_k_x2x0 = _mm_shuffle_epi32( xmm_x_ptr_n_k_x2x0, _MM_SHUFFLE( 0, 1, 2, 3 ) ); xmm_x_ptr_n_k_x2x0 = _mm_slli_epi32( xmm_x_ptr_n_k_x2x0, -rshifts - 1 ); xmm_x_ptr_sub_x2x0 = _mm_slli_epi32( xmm_x_ptr_sub_x2x0, -rshifts - 1 ); /* equal shift right 4 bytes, xmm_x_ptr_n_k_x3x1 = _mm_srli_si128(xmm_x_ptr_n_k_x2x0, 4)*/ xmm_x_ptr_n_k_x3x1 = _mm_shuffle_epi32( xmm_x_ptr_n_k_x2x0, _MM_SHUFFLE( 0, 3, 2, 1 ) ); xmm_x_ptr_sub_x3x1 = _mm_shuffle_epi32( xmm_x_ptr_sub_x2x0, _MM_SHUFFLE( 0, 3, 2, 1 ) ); xmm_x_ptr_n_k_x2x0 = _mm_mul_epi32( xmm_x_ptr_n_k_x2x0, xmm_tmp1 ); xmm_x_ptr_n_k_x3x1 = _mm_mul_epi32( xmm_x_ptr_n_k_x3x1, xmm_tmp1 ); xmm_x_ptr_sub_x2x0 = _mm_mul_epi32( xmm_x_ptr_sub_x2x0, xmm_tmp2 ); xmm_x_ptr_sub_x3x1 = _mm_mul_epi32( xmm_x_ptr_sub_x3x1, xmm_tmp2 ); xmm_x_ptr_n_k_x2x0 = _mm_srli_epi64( xmm_x_ptr_n_k_x2x0, 16 ); xmm_x_ptr_n_k_x3x1 = _mm_slli_epi64( xmm_x_ptr_n_k_x3x1, 16 ); xmm_x_ptr_sub_x2x0 = _mm_srli_epi64( xmm_x_ptr_sub_x2x0, 16 ); xmm_x_ptr_sub_x3x1 = _mm_slli_epi64( xmm_x_ptr_sub_x3x1, 16 ); xmm_x_ptr_n_k_x2x0 = _mm_blend_epi16( xmm_x_ptr_n_k_x2x0, xmm_x_ptr_n_k_x3x1, 0xCC ); xmm_x_ptr_sub_x2x0 = _mm_blend_epi16( xmm_x_ptr_sub_x2x0, xmm_x_ptr_sub_x3x1, 0xCC ); X1_3210 = _mm_loadu_si128( (__m128i *)&CAf[ k ] ); PTR_3210 = _mm_loadu_si128( (__m128i *)&CAb[ k ] ); X1_3210 = _mm_add_epi32( X1_3210, xmm_x_ptr_n_k_x2x0 ); PTR_3210 = _mm_add_epi32( PTR_3210, xmm_x_ptr_sub_x2x0 ); _mm_storeu_si128( (__m128i *)&CAf[ k ], X1_3210 ); _mm_storeu_si128( (__m128i *)&CAb[ k ], PTR_3210 ); } for( ; k <= n; k++ ) { CAf[ k ] = silk_SMLAWW( CAf[ k ], tmp1, silk_LSHIFT32( (opus_int32)x_ptr[ n - k ], -rshifts - 1 ) ); /* Q( -rshift ) */ CAb[ k ] = silk_SMLAWW( CAb[ k ], tmp2, silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n + k - 1 ], -rshifts - 1 ) ); /* Q( -rshift ) */ } } } } /* Calculate nominator and denominator for the next order reflection (parcor) coefficient */ tmp1 = C_first_row[ n ]; /* Q( -rshifts ) */ tmp2 = C_last_row[ n ]; /* Q( -rshifts ) */ num = 0; /* Q( -rshifts ) */ nrg = silk_ADD32( CAb[ 0 ], CAf[ 0 ] ); /* Q( 1-rshifts ) */ for( k = 0; k < n; k++ ) { Atmp_QA = Af_QA[ k ]; lz = silk_CLZ32( silk_abs( Atmp_QA ) ) - 1; lz = silk_min( 32 - QA, lz ); Atmp1 = silk_LSHIFT32( Atmp_QA, lz ); /* Q( QA + lz ) */ tmp1 = silk_ADD_LSHIFT32( tmp1, silk_SMMUL( C_last_row[ n - k - 1 ], Atmp1 ), 32 - QA - lz ); /* Q( -rshifts ) */ tmp2 = silk_ADD_LSHIFT32( tmp2, silk_SMMUL( C_first_row[ n - k - 1 ], Atmp1 ), 32 - QA - lz ); /* Q( -rshifts ) */ num = silk_ADD_LSHIFT32( num, silk_SMMUL( CAb[ n - k ], Atmp1 ), 32 - QA - lz ); /* Q( -rshifts ) */ nrg = silk_ADD_LSHIFT32( nrg, silk_SMMUL( silk_ADD32( CAb[ k + 1 ], CAf[ k + 1 ] ), Atmp1 ), 32 - QA - lz ); /* Q( 1-rshifts ) */ } CAf[ n + 1 ] = tmp1; /* Q( -rshifts ) */ CAb[ n + 1 ] = tmp2; /* Q( -rshifts ) */ num = silk_ADD32( num, tmp2 ); /* Q( -rshifts ) */ num = silk_LSHIFT32( -num, 1 ); /* Q( 1-rshifts ) */ /* Calculate the next order reflection (parcor) coefficient */ if( silk_abs( num ) < nrg ) { rc_Q31 = silk_DIV32_varQ( num, nrg, 31 ); } else { rc_Q31 = ( num > 0 ) ? silk_int32_MAX : silk_int32_MIN; } /* Update inverse prediction gain */ tmp1 = ( (opus_int32)1 << 30 ) - silk_SMMUL( rc_Q31, rc_Q31 ); tmp1 = silk_LSHIFT( silk_SMMUL( invGain_Q30, tmp1 ), 2 ); if( tmp1 <= minInvGain_Q30 ) { /* Max prediction gain exceeded; set reflection coefficient such that max prediction gain is exactly hit */ tmp2 = ( (opus_int32)1 << 30 ) - silk_DIV32_varQ( minInvGain_Q30, invGain_Q30, 30 ); /* Q30 */ rc_Q31 = silk_SQRT_APPROX( tmp2 ); /* Q15 */ /* Newton-Raphson iteration */ rc_Q31 = silk_RSHIFT32( rc_Q31 + silk_DIV32( tmp2, rc_Q31 ), 1 ); /* Q15 */ rc_Q31 = silk_LSHIFT32( rc_Q31, 16 ); /* Q31 */ if( num < 0 ) { /* Ensure adjusted reflection coefficients has the original sign */ rc_Q31 = -rc_Q31; } invGain_Q30 = minInvGain_Q30; reached_max_gain = 1; } else { invGain_Q30 = tmp1; } /* Update the AR coefficients */ for( k = 0; k < (n + 1) >> 1; k++ ) { tmp1 = Af_QA[ k ]; /* QA */ tmp2 = Af_QA[ n - k - 1 ]; /* QA */ Af_QA[ k ] = silk_ADD_LSHIFT32( tmp1, silk_SMMUL( tmp2, rc_Q31 ), 1 ); /* QA */ Af_QA[ n - k - 1 ] = silk_ADD_LSHIFT32( tmp2, silk_SMMUL( tmp1, rc_Q31 ), 1 ); /* QA */ } Af_QA[ n ] = silk_RSHIFT32( rc_Q31, 31 - QA ); /* QA */ if( reached_max_gain ) { /* Reached max prediction gain; set remaining coefficients to zero and exit loop */ for( k = n + 1; k < D; k++ ) { Af_QA[ k ] = 0; } break; } /* Update C * Af and C * Ab */ for( k = 0; k <= n + 1; k++ ) { tmp1 = CAf[ k ]; /* Q( -rshifts ) */ tmp2 = CAb[ n - k + 1 ]; /* Q( -rshifts ) */ CAf[ k ] = silk_ADD_LSHIFT32( tmp1, silk_SMMUL( tmp2, rc_Q31 ), 1 ); /* Q( -rshifts ) */ CAb[ n - k + 1 ] = silk_ADD_LSHIFT32( tmp2, silk_SMMUL( tmp1, rc_Q31 ), 1 ); /* Q( -rshifts ) */ } } if( reached_max_gain ) { for( k = 0; k < D; k++ ) { /* Scale coefficients */ A_Q16[ k ] = -silk_RSHIFT_ROUND( Af_QA[ k ], QA - 16 ); } /* Subtract energy of preceding samples from C0 */ if( rshifts > 0 ) { for( s = 0; s < nb_subfr; s++ ) { x_ptr = x + s * subfr_length; C0 -= (opus_int32)silk_RSHIFT64( silk_inner_prod16_aligned_64( x_ptr, x_ptr, D, arch ), rshifts ); } } else { for( s = 0; s < nb_subfr; s++ ) { x_ptr = x + s * subfr_length; C0 -= silk_LSHIFT32( silk_inner_prod_aligned( x_ptr, x_ptr, D, arch ), -rshifts ); } } /* Approximate residual energy */ *res_nrg = silk_LSHIFT( silk_SMMUL( invGain_Q30, C0 ), 2 ); *res_nrg_Q = -rshifts; } else { /* Return residual energy */ nrg = CAf[ 0 ]; /* Q( -rshifts ) */ tmp1 = (opus_int32)1 << 16; /* Q16 */ for( k = 0; k < D; k++ ) { Atmp1 = silk_RSHIFT_ROUND( Af_QA[ k ], QA - 16 ); /* Q16 */ nrg = silk_SMLAWW( nrg, CAf[ k + 1 ], Atmp1 ); /* Q( -rshifts ) */ tmp1 = silk_SMLAWW( tmp1, Atmp1, Atmp1 ); /* Q16 */ A_Q16[ k ] = -Atmp1; } *res_nrg = silk_SMLAWW( nrg, silk_SMMUL( SILK_FIX_CONST( FIND_LPC_COND_FAC, 32 ), C0 ), -tmp1 );/* Q( -rshifts ) */ *res_nrg_Q = -rshifts; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/x86/prefilter_FIX_sse.c ================================================ /* Copyright (c) 2014, Cisco Systems, INC Written by XiangMingZhu WeiZhou MinPeng YanWang Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "main.h" #include "celt/x86/x86cpu.h" void silk_warped_LPC_analysis_filter_FIX_sse4_1( opus_int32 state[], /* I/O State [order + 1] */ opus_int32 res_Q2[], /* O Residual signal [length] */ const opus_int16 coef_Q13[], /* I Coefficients [order] */ const opus_int16 input[], /* I Input signal [length] */ const opus_int16 lambda_Q16, /* I Warping factor */ const opus_int length, /* I Length of input signal */ const opus_int order /* I Filter order (even) */ ) { opus_int n, i; opus_int32 acc_Q11, tmp1, tmp2; /* Order must be even */ silk_assert( ( order & 1 ) == 0 ); if (order == 10) { if (0 == lambda_Q16) { __m128i coef_Q13_3210, coef_Q13_7654; __m128i coef_Q13_0123, coef_Q13_4567; __m128i state_0123, state_4567; __m128i xmm_product1, xmm_product2; __m128i xmm_tempa, xmm_tempb; register opus_int32 sum; register opus_int32 state_8, state_9, state_a; register opus_int64 coef_Q13_8, coef_Q13_9; silk_assert( length > 0 ); coef_Q13_3210 = OP_CVTEPI16_EPI32_M64( &coef_Q13[ 0 ] ); coef_Q13_7654 = OP_CVTEPI16_EPI32_M64( &coef_Q13[ 4 ] ); coef_Q13_0123 = _mm_shuffle_epi32( coef_Q13_3210, _MM_SHUFFLE( 0, 1, 2, 3 ) ); coef_Q13_4567 = _mm_shuffle_epi32( coef_Q13_7654, _MM_SHUFFLE( 0, 1, 2, 3 ) ); coef_Q13_8 = (opus_int64) coef_Q13[ 8 ]; coef_Q13_9 = (opus_int64) coef_Q13[ 9 ]; state_0123 = _mm_loadu_si128( (__m128i *)(&state[ 0 ] ) ); state_4567 = _mm_loadu_si128( (__m128i *)(&state[ 4 ] ) ); state_0123 = _mm_shuffle_epi32( state_0123, _MM_SHUFFLE( 0, 1, 2, 3 ) ); state_4567 = _mm_shuffle_epi32( state_4567, _MM_SHUFFLE( 0, 1, 2, 3 ) ); state_8 = state[ 8 ]; state_9 = state[ 9 ]; state_a = 0; for( n = 0; n < length; n++ ) { xmm_product1 = _mm_mul_epi32( coef_Q13_0123, state_0123 ); /* 64-bit multiply, only 2 pairs */ xmm_product2 = _mm_mul_epi32( coef_Q13_4567, state_4567 ); xmm_tempa = _mm_shuffle_epi32( state_0123, _MM_SHUFFLE( 0, 1, 2, 3 ) ); xmm_tempb = _mm_shuffle_epi32( state_4567, _MM_SHUFFLE( 0, 1, 2, 3 ) ); xmm_product1 = _mm_srli_epi64( xmm_product1, 16 ); /* >> 16, zero extending works */ xmm_product2 = _mm_srli_epi64( xmm_product2, 16 ); xmm_tempa = _mm_mul_epi32( coef_Q13_3210, xmm_tempa ); xmm_tempb = _mm_mul_epi32( coef_Q13_7654, xmm_tempb ); xmm_tempa = _mm_srli_epi64( xmm_tempa, 16 ); xmm_tempb = _mm_srli_epi64( xmm_tempb, 16 ); xmm_tempa = _mm_add_epi32( xmm_tempa, xmm_product1 ); xmm_tempb = _mm_add_epi32( xmm_tempb, xmm_product2 ); xmm_tempa = _mm_add_epi32( xmm_tempa, xmm_tempb ); sum = (coef_Q13_8 * state_8) >> 16; sum += (coef_Q13_9 * state_9) >> 16; xmm_tempa = _mm_add_epi32( xmm_tempa, _mm_shuffle_epi32( xmm_tempa, _MM_SHUFFLE( 0, 0, 0, 2 ) ) ); sum += _mm_cvtsi128_si32( xmm_tempa); res_Q2[ n ] = silk_LSHIFT( (opus_int32)input[ n ], 2 ) - silk_RSHIFT_ROUND( ( 5 + sum ), 9); /* move right */ state_a = state_9; state_9 = state_8; state_8 = _mm_cvtsi128_si32( state_4567 ); state_4567 = _mm_alignr_epi8( state_0123, state_4567, 4 ); state_0123 = _mm_alignr_epi8( _mm_cvtsi32_si128( silk_LSHIFT( input[ n ], 14 ) ), state_0123, 4 ); } _mm_storeu_si128( (__m128i *)( &state[ 0 ] ), _mm_shuffle_epi32( state_0123, _MM_SHUFFLE( 0, 1, 2, 3 ) ) ); _mm_storeu_si128( (__m128i *)( &state[ 4 ] ), _mm_shuffle_epi32( state_4567, _MM_SHUFFLE( 0, 1, 2, 3 ) ) ); state[ 8 ] = state_8; state[ 9 ] = state_9; state[ 10 ] = state_a; return; } } for( n = 0; n < length; n++ ) { /* Output of lowpass section */ tmp2 = silk_SMLAWB( state[ 0 ], state[ 1 ], lambda_Q16 ); state[ 0 ] = silk_LSHIFT( input[ n ], 14 ); /* Output of allpass section */ tmp1 = silk_SMLAWB( state[ 1 ], state[ 2 ] - tmp2, lambda_Q16 ); state[ 1 ] = tmp2; acc_Q11 = silk_RSHIFT( order, 1 ); acc_Q11 = silk_SMLAWB( acc_Q11, tmp2, coef_Q13[ 0 ] ); /* Loop over allpass sections */ for( i = 2; i < order; i += 2 ) { /* Output of allpass section */ tmp2 = silk_SMLAWB( state[ i ], state[ i + 1 ] - tmp1, lambda_Q16 ); state[ i ] = tmp1; acc_Q11 = silk_SMLAWB( acc_Q11, tmp1, coef_Q13[ i - 1 ] ); /* Output of allpass section */ tmp1 = silk_SMLAWB( state[ i + 1 ], state[ i + 2 ] - tmp2, lambda_Q16 ); state[ i + 1 ] = tmp2; acc_Q11 = silk_SMLAWB( acc_Q11, tmp2, coef_Q13[ i ] ); } state[ order ] = tmp1; acc_Q11 = silk_SMLAWB( acc_Q11, tmp1, coef_Q13[ order - 1 ] ); res_Q2[ n ] = silk_LSHIFT( (opus_int32)input[ n ], 2 ) - silk_RSHIFT_ROUND( acc_Q11, 9 ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/fixed/x86/vector_ops_FIX_sse.c ================================================ /* Copyright (c) 2014, Cisco Systems, INC Written by XiangMingZhu WeiZhou MinPeng YanWang Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "main.h" #include "SigProc_FIX.h" #include "pitch.h" opus_int64 silk_inner_prod16_aligned_64_sse4_1( const opus_int16 *inVec1, /* I input vector 1 */ const opus_int16 *inVec2, /* I input vector 2 */ const opus_int len /* I vector lengths */ ) { opus_int i, dataSize8; opus_int64 sum; __m128i xmm_tempa; __m128i inVec1_76543210, acc1; __m128i inVec2_76543210, acc2; sum = 0; dataSize8 = len & ~7; acc1 = _mm_setzero_si128(); acc2 = _mm_setzero_si128(); for( i = 0; i < dataSize8; i += 8 ) { inVec1_76543210 = _mm_loadu_si128( (__m128i *)(&inVec1[i + 0] ) ); inVec2_76543210 = _mm_loadu_si128( (__m128i *)(&inVec2[i + 0] ) ); /* only when all 4 operands are -32768 (0x8000), this results in wrap around */ inVec1_76543210 = _mm_madd_epi16( inVec1_76543210, inVec2_76543210 ); xmm_tempa = _mm_cvtepi32_epi64( inVec1_76543210 ); /* equal shift right 8 bytes */ inVec1_76543210 = _mm_shuffle_epi32( inVec1_76543210, _MM_SHUFFLE( 0, 0, 3, 2 ) ); inVec1_76543210 = _mm_cvtepi32_epi64( inVec1_76543210 ); acc1 = _mm_add_epi64( acc1, xmm_tempa ); acc2 = _mm_add_epi64( acc2, inVec1_76543210 ); } acc1 = _mm_add_epi64( acc1, acc2 ); /* equal shift right 8 bytes */ acc2 = _mm_shuffle_epi32( acc1, _MM_SHUFFLE( 0, 0, 3, 2 ) ); acc1 = _mm_add_epi64( acc1, acc2 ); _mm_storel_epi64( (__m128i *)&sum, acc1 ); for( ; i < len; i++ ) { sum = silk_SMLABB( sum, inVec1[ i ], inVec2[ i ] ); } return sum; } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/LPC_analysis_filter_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "main_FLP.h" /************************************************/ /* LPC analysis filter */ /* NB! State is kept internally and the */ /* filter always starts with zero state */ /* first Order output samples are set to zero */ /************************************************/ /* 16th order LPC analysis filter, does not write first 16 samples */ static OPUS_INLINE void silk_LPC_analysis_filter16_FLP( silk_float r_LPC[], /* O LPC residual signal */ const silk_float PredCoef[], /* I LPC coefficients */ const silk_float s[], /* I Input signal */ const opus_int length /* I Length of input signal */ ) { opus_int ix; silk_float LPC_pred; const silk_float *s_ptr; for( ix = 16; ix < length; ix++ ) { s_ptr = &s[ix - 1]; /* short-term prediction */ LPC_pred = s_ptr[ 0 ] * PredCoef[ 0 ] + s_ptr[ -1 ] * PredCoef[ 1 ] + s_ptr[ -2 ] * PredCoef[ 2 ] + s_ptr[ -3 ] * PredCoef[ 3 ] + s_ptr[ -4 ] * PredCoef[ 4 ] + s_ptr[ -5 ] * PredCoef[ 5 ] + s_ptr[ -6 ] * PredCoef[ 6 ] + s_ptr[ -7 ] * PredCoef[ 7 ] + s_ptr[ -8 ] * PredCoef[ 8 ] + s_ptr[ -9 ] * PredCoef[ 9 ] + s_ptr[ -10 ] * PredCoef[ 10 ] + s_ptr[ -11 ] * PredCoef[ 11 ] + s_ptr[ -12 ] * PredCoef[ 12 ] + s_ptr[ -13 ] * PredCoef[ 13 ] + s_ptr[ -14 ] * PredCoef[ 14 ] + s_ptr[ -15 ] * PredCoef[ 15 ]; /* prediction error */ r_LPC[ix] = s_ptr[ 1 ] - LPC_pred; } } /* 12th order LPC analysis filter, does not write first 12 samples */ static OPUS_INLINE void silk_LPC_analysis_filter12_FLP( silk_float r_LPC[], /* O LPC residual signal */ const silk_float PredCoef[], /* I LPC coefficients */ const silk_float s[], /* I Input signal */ const opus_int length /* I Length of input signal */ ) { opus_int ix; silk_float LPC_pred; const silk_float *s_ptr; for( ix = 12; ix < length; ix++ ) { s_ptr = &s[ix - 1]; /* short-term prediction */ LPC_pred = s_ptr[ 0 ] * PredCoef[ 0 ] + s_ptr[ -1 ] * PredCoef[ 1 ] + s_ptr[ -2 ] * PredCoef[ 2 ] + s_ptr[ -3 ] * PredCoef[ 3 ] + s_ptr[ -4 ] * PredCoef[ 4 ] + s_ptr[ -5 ] * PredCoef[ 5 ] + s_ptr[ -6 ] * PredCoef[ 6 ] + s_ptr[ -7 ] * PredCoef[ 7 ] + s_ptr[ -8 ] * PredCoef[ 8 ] + s_ptr[ -9 ] * PredCoef[ 9 ] + s_ptr[ -10 ] * PredCoef[ 10 ] + s_ptr[ -11 ] * PredCoef[ 11 ]; /* prediction error */ r_LPC[ix] = s_ptr[ 1 ] - LPC_pred; } } /* 10th order LPC analysis filter, does not write first 10 samples */ static OPUS_INLINE void silk_LPC_analysis_filter10_FLP( silk_float r_LPC[], /* O LPC residual signal */ const silk_float PredCoef[], /* I LPC coefficients */ const silk_float s[], /* I Input signal */ const opus_int length /* I Length of input signal */ ) { opus_int ix; silk_float LPC_pred; const silk_float *s_ptr; for( ix = 10; ix < length; ix++ ) { s_ptr = &s[ix - 1]; /* short-term prediction */ LPC_pred = s_ptr[ 0 ] * PredCoef[ 0 ] + s_ptr[ -1 ] * PredCoef[ 1 ] + s_ptr[ -2 ] * PredCoef[ 2 ] + s_ptr[ -3 ] * PredCoef[ 3 ] + s_ptr[ -4 ] * PredCoef[ 4 ] + s_ptr[ -5 ] * PredCoef[ 5 ] + s_ptr[ -6 ] * PredCoef[ 6 ] + s_ptr[ -7 ] * PredCoef[ 7 ] + s_ptr[ -8 ] * PredCoef[ 8 ] + s_ptr[ -9 ] * PredCoef[ 9 ]; /* prediction error */ r_LPC[ix] = s_ptr[ 1 ] - LPC_pred; } } /* 8th order LPC analysis filter, does not write first 8 samples */ static OPUS_INLINE void silk_LPC_analysis_filter8_FLP( silk_float r_LPC[], /* O LPC residual signal */ const silk_float PredCoef[], /* I LPC coefficients */ const silk_float s[], /* I Input signal */ const opus_int length /* I Length of input signal */ ) { opus_int ix; silk_float LPC_pred; const silk_float *s_ptr; for( ix = 8; ix < length; ix++ ) { s_ptr = &s[ix - 1]; /* short-term prediction */ LPC_pred = s_ptr[ 0 ] * PredCoef[ 0 ] + s_ptr[ -1 ] * PredCoef[ 1 ] + s_ptr[ -2 ] * PredCoef[ 2 ] + s_ptr[ -3 ] * PredCoef[ 3 ] + s_ptr[ -4 ] * PredCoef[ 4 ] + s_ptr[ -5 ] * PredCoef[ 5 ] + s_ptr[ -6 ] * PredCoef[ 6 ] + s_ptr[ -7 ] * PredCoef[ 7 ]; /* prediction error */ r_LPC[ix] = s_ptr[ 1 ] - LPC_pred; } } /* 6th order LPC analysis filter, does not write first 6 samples */ static OPUS_INLINE void silk_LPC_analysis_filter6_FLP( silk_float r_LPC[], /* O LPC residual signal */ const silk_float PredCoef[], /* I LPC coefficients */ const silk_float s[], /* I Input signal */ const opus_int length /* I Length of input signal */ ) { opus_int ix; silk_float LPC_pred; const silk_float *s_ptr; for( ix = 6; ix < length; ix++ ) { s_ptr = &s[ix - 1]; /* short-term prediction */ LPC_pred = s_ptr[ 0 ] * PredCoef[ 0 ] + s_ptr[ -1 ] * PredCoef[ 1 ] + s_ptr[ -2 ] * PredCoef[ 2 ] + s_ptr[ -3 ] * PredCoef[ 3 ] + s_ptr[ -4 ] * PredCoef[ 4 ] + s_ptr[ -5 ] * PredCoef[ 5 ]; /* prediction error */ r_LPC[ix] = s_ptr[ 1 ] - LPC_pred; } } /************************************************/ /* LPC analysis filter */ /* NB! State is kept internally and the */ /* filter always starts with zero state */ /* first Order output samples are set to zero */ /************************************************/ void silk_LPC_analysis_filter_FLP( silk_float r_LPC[], /* O LPC residual signal */ const silk_float PredCoef[], /* I LPC coefficients */ const silk_float s[], /* I Input signal */ const opus_int length, /* I Length of input signal */ const opus_int Order /* I LPC order */ ) { silk_assert( Order <= length ); switch( Order ) { case 6: silk_LPC_analysis_filter6_FLP( r_LPC, PredCoef, s, length ); break; case 8: silk_LPC_analysis_filter8_FLP( r_LPC, PredCoef, s, length ); break; case 10: silk_LPC_analysis_filter10_FLP( r_LPC, PredCoef, s, length ); break; case 12: silk_LPC_analysis_filter12_FLP( r_LPC, PredCoef, s, length ); break; case 16: silk_LPC_analysis_filter16_FLP( r_LPC, PredCoef, s, length ); break; default: silk_assert( 0 ); break; } /* Set first Order output samples to zero */ silk_memset( r_LPC, 0, Order * sizeof( silk_float ) ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/LPC_inv_pred_gain_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" #include "SigProc_FLP.h" #define RC_THRESHOLD 0.9999f /* compute inverse of LPC prediction gain, and */ /* test if LPC coefficients are stable (all poles within unit circle) */ /* this code is based on silk_a2k_FLP() */ silk_float silk_LPC_inverse_pred_gain_FLP( /* O return inverse prediction gain, energy domain */ const silk_float *A, /* I prediction coefficients [order] */ opus_int32 order /* I prediction order */ ) { opus_int k, n; double invGain, rc, rc_mult1, rc_mult2; silk_float Atmp[ 2 ][ SILK_MAX_ORDER_LPC ]; silk_float *Aold, *Anew; Anew = Atmp[ order & 1 ]; silk_memcpy( Anew, A, order * sizeof(silk_float) ); invGain = 1.0; for( k = order - 1; k > 0; k-- ) { rc = -Anew[ k ]; if( rc > RC_THRESHOLD || rc < -RC_THRESHOLD ) { return 0.0f; } rc_mult1 = 1.0f - rc * rc; rc_mult2 = 1.0f / rc_mult1; invGain *= rc_mult1; /* swap pointers */ Aold = Anew; Anew = Atmp[ k & 1 ]; for( n = 0; n < k; n++ ) { Anew[ n ] = (silk_float)( ( Aold[ n ] - Aold[ k - n - 1 ] * rc ) * rc_mult2 ); } } rc = -Anew[ 0 ]; if( rc > RC_THRESHOLD || rc < -RC_THRESHOLD ) { return 0.0f; } rc_mult1 = 1.0f - rc * rc; invGain *= rc_mult1; return (silk_float)invGain; } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/LTP_analysis_filter_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FLP.h" void silk_LTP_analysis_filter_FLP( silk_float *LTP_res, /* O LTP res MAX_NB_SUBFR*(pre_lgth+subfr_lngth) */ const silk_float *x, /* I Input signal, with preceding samples */ const silk_float B[ LTP_ORDER * MAX_NB_SUBFR ], /* I LTP coefficients for each subframe */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lags */ const silk_float invGains[ MAX_NB_SUBFR ], /* I Inverse quantization gains */ const opus_int subfr_length, /* I Length of each subframe */ const opus_int nb_subfr, /* I number of subframes */ const opus_int pre_length /* I Preceding samples for each subframe */ ) { const silk_float *x_ptr, *x_lag_ptr; silk_float Btmp[ LTP_ORDER ]; silk_float *LTP_res_ptr; silk_float inv_gain; opus_int k, i, j; x_ptr = x; LTP_res_ptr = LTP_res; for( k = 0; k < nb_subfr; k++ ) { x_lag_ptr = x_ptr - pitchL[ k ]; inv_gain = invGains[ k ]; for( i = 0; i < LTP_ORDER; i++ ) { Btmp[ i ] = B[ k * LTP_ORDER + i ]; } /* LTP analysis FIR filter */ for( i = 0; i < subfr_length + pre_length; i++ ) { LTP_res_ptr[ i ] = x_ptr[ i ]; /* Subtract long-term prediction */ for( j = 0; j < LTP_ORDER; j++ ) { LTP_res_ptr[ i ] -= Btmp[ j ] * x_lag_ptr[ LTP_ORDER / 2 - j ]; } LTP_res_ptr[ i ] *= inv_gain; x_lag_ptr++; } /* Update pointers */ LTP_res_ptr += subfr_length + pre_length; x_ptr += subfr_length; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/LTP_scale_ctrl_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FLP.h" void silk_LTP_scale_ctrl_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */ opus_int condCoding /* I The type of conditional coding to use */ ) { opus_int round_loss; if( condCoding == CODE_INDEPENDENTLY ) { /* Only scale if first frame in packet */ round_loss = psEnc->sCmn.PacketLoss_perc + psEnc->sCmn.nFramesPerPacket; psEnc->sCmn.indices.LTP_scaleIndex = (opus_int8)silk_LIMIT( round_loss * psEncCtrl->LTPredCodGain * 0.1f, 0.0f, 2.0f ); } else { /* Default is minimum scaling */ psEnc->sCmn.indices.LTP_scaleIndex = 0; } psEncCtrl->LTP_scale = (silk_float)silk_LTPScales_table_Q14[ psEnc->sCmn.indices.LTP_scaleIndex ] / 16384.0f; } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/SigProc_FLP.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_SIGPROC_FLP_H #define SILK_SIGPROC_FLP_H #include "SigProc_FIX.h" #include "float_cast.h" #include #ifdef __cplusplus extern "C" { #endif /********************************************************************/ /* SIGNAL PROCESSING FUNCTIONS */ /********************************************************************/ /* Chirp (bw expand) LP AR filter */ void silk_bwexpander_FLP( silk_float *ar, /* I/O AR filter to be expanded (without leading 1) */ const opus_int d, /* I length of ar */ const silk_float chirp /* I chirp factor (typically in range (0..1) ) */ ); /* compute inverse of LPC prediction gain, and */ /* test if LPC coefficients are stable (all poles within unit circle) */ /* this code is based on silk_FLP_a2k() */ silk_float silk_LPC_inverse_pred_gain_FLP( /* O return inverse prediction gain, energy domain */ const silk_float *A, /* I prediction coefficients [order] */ opus_int32 order /* I prediction order */ ); silk_float silk_schur_FLP( /* O returns residual energy */ silk_float refl_coef[], /* O reflection coefficients (length order) */ const silk_float auto_corr[], /* I autocorrelation sequence (length order+1) */ opus_int order /* I order */ ); void silk_k2a_FLP( silk_float *A, /* O prediction coefficients [order] */ const silk_float *rc, /* I reflection coefficients [order] */ opus_int32 order /* I prediction order */ ); /* Solve the normal equations using the Levinson-Durbin recursion */ silk_float silk_levinsondurbin_FLP( /* O prediction error energy */ silk_float A[], /* O prediction coefficients [order] */ const silk_float corr[], /* I input auto-correlations [order + 1] */ const opus_int order /* I prediction order */ ); /* compute autocorrelation */ void silk_autocorrelation_FLP( silk_float *results, /* O result (length correlationCount) */ const silk_float *inputData, /* I input data to correlate */ opus_int inputDataSize, /* I length of input */ opus_int correlationCount /* I number of correlation taps to compute */ ); opus_int silk_pitch_analysis_core_FLP( /* O Voicing estimate: 0 voiced, 1 unvoiced */ const silk_float *frame, /* I Signal of length PE_FRAME_LENGTH_MS*Fs_kHz */ opus_int *pitch_out, /* O Pitch lag values [nb_subfr] */ opus_int16 *lagIndex, /* O Lag Index */ opus_int8 *contourIndex, /* O Pitch contour Index */ silk_float *LTPCorr, /* I/O Normalized correlation; input: value from previous frame */ opus_int prevLag, /* I Last lag of previous frame; set to zero is unvoiced */ const silk_float search_thres1, /* I First stage threshold for lag candidates 0 - 1 */ const silk_float search_thres2, /* I Final threshold for lag candidates 0 - 1 */ const opus_int Fs_kHz, /* I sample frequency (kHz) */ const opus_int complexity, /* I Complexity setting, 0-2, where 2 is highest */ const opus_int nb_subfr, /* I Number of 5 ms subframes */ int arch /* I Run-time architecture */ ); void silk_insertion_sort_decreasing_FLP( silk_float *a, /* I/O Unsorted / Sorted vector */ opus_int *idx, /* O Index vector for the sorted elements */ const opus_int L, /* I Vector length */ const opus_int K /* I Number of correctly sorted positions */ ); /* Compute reflection coefficients from input signal */ silk_float silk_burg_modified_FLP( /* O returns residual energy */ silk_float A[], /* O prediction coefficients (length order) */ const silk_float x[], /* I input signal, length: nb_subfr*(D+L_sub) */ const silk_float minInvGain, /* I minimum inverse prediction gain */ const opus_int subfr_length, /* I input signal subframe length (incl. D preceding samples) */ const opus_int nb_subfr, /* I number of subframes stacked in x */ const opus_int D /* I order */ ); /* multiply a vector by a constant */ void silk_scale_vector_FLP( silk_float *data1, silk_float gain, opus_int dataSize ); /* copy and multiply a vector by a constant */ void silk_scale_copy_vector_FLP( silk_float *data_out, const silk_float *data_in, silk_float gain, opus_int dataSize ); /* inner product of two silk_float arrays, with result as double */ double silk_inner_product_FLP( const silk_float *data1, const silk_float *data2, opus_int dataSize ); /* sum of squares of a silk_float array, with result as double */ double silk_energy_FLP( const silk_float *data, opus_int dataSize ); /********************************************************************/ /* MACROS */ /********************************************************************/ #define PI (3.1415926536f) #define silk_min_float( a, b ) (((a) < (b)) ? (a) : (b)) #define silk_max_float( a, b ) (((a) > (b)) ? (a) : (b)) #define silk_abs_float( a ) ((silk_float)fabs(a)) /* sigmoid function */ static OPUS_INLINE silk_float silk_sigmoid( silk_float x ) { return (silk_float)(1.0 / (1.0 + exp(-x))); } /* floating-point to integer conversion (rounding) */ static OPUS_INLINE opus_int32 silk_float2int( silk_float x ) { return (opus_int32)float2int( x ); } /* floating-point to integer conversion (rounding) */ static OPUS_INLINE void silk_float2short_array( opus_int16 *out, const silk_float *in, opus_int32 length ) { opus_int32 k; for( k = length - 1; k >= 0; k-- ) { out[k] = silk_SAT16( (opus_int32)float2int( in[k] ) ); } } /* integer to floating-point conversion */ static OPUS_INLINE void silk_short2float_array( silk_float *out, const opus_int16 *in, opus_int32 length ) { opus_int32 k; for( k = length - 1; k >= 0; k-- ) { out[k] = (silk_float)in[k]; } } /* using log2() helps the fixed-point conversion */ static OPUS_INLINE silk_float silk_log2( double x ) { return ( silk_float )( 3.32192809488736 * log10( x ) ); } #ifdef __cplusplus } #endif #endif /* SILK_SIGPROC_FLP_H */ ================================================ FILE: deps/pjsip/third_party/opus/silk/float/apply_sine_window_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FLP.h" /* Apply sine window to signal vector */ /* Window types: */ /* 1 -> sine window from 0 to pi/2 */ /* 2 -> sine window from pi/2 to pi */ void silk_apply_sine_window_FLP( silk_float px_win[], /* O Pointer to windowed signal */ const silk_float px[], /* I Pointer to input signal */ const opus_int win_type, /* I Selects a window type */ const opus_int length /* I Window length, multiple of 4 */ ) { opus_int k; silk_float freq, c, S0, S1; silk_assert( win_type == 1 || win_type == 2 ); /* Length must be multiple of 4 */ silk_assert( ( length & 3 ) == 0 ); freq = PI / ( length + 1 ); /* Approximation of 2 * cos(f) */ c = 2.0f - freq * freq; /* Initialize state */ if( win_type < 2 ) { /* Start from 0 */ S0 = 0.0f; /* Approximation of sin(f) */ S1 = freq; } else { /* Start from 1 */ S0 = 1.0f; /* Approximation of cos(f) */ S1 = 0.5f * c; } /* Uses the recursive equation: sin(n*f) = 2 * cos(f) * sin((n-1)*f) - sin((n-2)*f) */ /* 4 samples at a time */ for( k = 0; k < length; k += 4 ) { px_win[ k + 0 ] = px[ k + 0 ] * 0.5f * ( S0 + S1 ); px_win[ k + 1 ] = px[ k + 1 ] * S1; S0 = c * S1 - S0; px_win[ k + 2 ] = px[ k + 2 ] * 0.5f * ( S1 + S0 ); px_win[ k + 3 ] = px[ k + 3 ] * S0; S1 = c * S0 - S1; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/autocorrelation_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "typedef.h" #include "SigProc_FLP.h" /* compute autocorrelation */ void silk_autocorrelation_FLP( silk_float *results, /* O result (length correlationCount) */ const silk_float *inputData, /* I input data to correlate */ opus_int inputDataSize, /* I length of input */ opus_int correlationCount /* I number of correlation taps to compute */ ) { opus_int i; if( correlationCount > inputDataSize ) { correlationCount = inputDataSize; } for( i = 0; i < correlationCount; i++ ) { results[ i ] = (silk_float)silk_inner_product_FLP( inputData, inputData + i, inputDataSize - i ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/burg_modified_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FLP.h" #include "tuning_parameters.h" #include "define.h" #define MAX_FRAME_SIZE 384 /* subfr_length * nb_subfr = ( 0.005 * 16000 + 16 ) * 4 = 384*/ /* Compute reflection coefficients from input signal */ silk_float silk_burg_modified_FLP( /* O returns residual energy */ silk_float A[], /* O prediction coefficients (length order) */ const silk_float x[], /* I input signal, length: nb_subfr*(D+L_sub) */ const silk_float minInvGain, /* I minimum inverse prediction gain */ const opus_int subfr_length, /* I input signal subframe length (incl. D preceding samples) */ const opus_int nb_subfr, /* I number of subframes stacked in x */ const opus_int D /* I order */ ) { opus_int k, n, s, reached_max_gain; double C0, invGain, num, nrg_f, nrg_b, rc, Atmp, tmp1, tmp2; const silk_float *x_ptr; double C_first_row[ SILK_MAX_ORDER_LPC ], C_last_row[ SILK_MAX_ORDER_LPC ]; double CAf[ SILK_MAX_ORDER_LPC + 1 ], CAb[ SILK_MAX_ORDER_LPC + 1 ]; double Af[ SILK_MAX_ORDER_LPC ]; silk_assert( subfr_length * nb_subfr <= MAX_FRAME_SIZE ); /* Compute autocorrelations, added over subframes */ C0 = silk_energy_FLP( x, nb_subfr * subfr_length ); silk_memset( C_first_row, 0, SILK_MAX_ORDER_LPC * sizeof( double ) ); for( s = 0; s < nb_subfr; s++ ) { x_ptr = x + s * subfr_length; for( n = 1; n < D + 1; n++ ) { C_first_row[ n - 1 ] += silk_inner_product_FLP( x_ptr, x_ptr + n, subfr_length - n ); } } silk_memcpy( C_last_row, C_first_row, SILK_MAX_ORDER_LPC * sizeof( double ) ); /* Initialize */ CAb[ 0 ] = CAf[ 0 ] = C0 + FIND_LPC_COND_FAC * C0 + 1e-9f; invGain = 1.0f; reached_max_gain = 0; for( n = 0; n < D; n++ ) { /* Update first row of correlation matrix (without first element) */ /* Update last row of correlation matrix (without last element, stored in reversed order) */ /* Update C * Af */ /* Update C * flipud(Af) (stored in reversed order) */ for( s = 0; s < nb_subfr; s++ ) { x_ptr = x + s * subfr_length; tmp1 = x_ptr[ n ]; tmp2 = x_ptr[ subfr_length - n - 1 ]; for( k = 0; k < n; k++ ) { C_first_row[ k ] -= x_ptr[ n ] * x_ptr[ n - k - 1 ]; C_last_row[ k ] -= x_ptr[ subfr_length - n - 1 ] * x_ptr[ subfr_length - n + k ]; Atmp = Af[ k ]; tmp1 += x_ptr[ n - k - 1 ] * Atmp; tmp2 += x_ptr[ subfr_length - n + k ] * Atmp; } for( k = 0; k <= n; k++ ) { CAf[ k ] -= tmp1 * x_ptr[ n - k ]; CAb[ k ] -= tmp2 * x_ptr[ subfr_length - n + k - 1 ]; } } tmp1 = C_first_row[ n ]; tmp2 = C_last_row[ n ]; for( k = 0; k < n; k++ ) { Atmp = Af[ k ]; tmp1 += C_last_row[ n - k - 1 ] * Atmp; tmp2 += C_first_row[ n - k - 1 ] * Atmp; } CAf[ n + 1 ] = tmp1; CAb[ n + 1 ] = tmp2; /* Calculate nominator and denominator for the next order reflection (parcor) coefficient */ num = CAb[ n + 1 ]; nrg_b = CAb[ 0 ]; nrg_f = CAf[ 0 ]; for( k = 0; k < n; k++ ) { Atmp = Af[ k ]; num += CAb[ n - k ] * Atmp; nrg_b += CAb[ k + 1 ] * Atmp; nrg_f += CAf[ k + 1 ] * Atmp; } silk_assert( nrg_f > 0.0 ); silk_assert( nrg_b > 0.0 ); /* Calculate the next order reflection (parcor) coefficient */ rc = -2.0 * num / ( nrg_f + nrg_b ); silk_assert( rc > -1.0 && rc < 1.0 ); /* Update inverse prediction gain */ tmp1 = invGain * ( 1.0 - rc * rc ); if( tmp1 <= minInvGain ) { /* Max prediction gain exceeded; set reflection coefficient such that max prediction gain is exactly hit */ rc = sqrt( 1.0 - minInvGain / invGain ); if( num > 0 ) { /* Ensure adjusted reflection coefficients has the original sign */ rc = -rc; } invGain = minInvGain; reached_max_gain = 1; } else { invGain = tmp1; } /* Update the AR coefficients */ for( k = 0; k < (n + 1) >> 1; k++ ) { tmp1 = Af[ k ]; tmp2 = Af[ n - k - 1 ]; Af[ k ] = tmp1 + rc * tmp2; Af[ n - k - 1 ] = tmp2 + rc * tmp1; } Af[ n ] = rc; if( reached_max_gain ) { /* Reached max prediction gain; set remaining coefficients to zero and exit loop */ for( k = n + 1; k < D; k++ ) { Af[ k ] = 0.0; } break; } /* Update C * Af and C * Ab */ for( k = 0; k <= n + 1; k++ ) { tmp1 = CAf[ k ]; CAf[ k ] += rc * CAb[ n - k + 1 ]; CAb[ n - k + 1 ] += rc * tmp1; } } if( reached_max_gain ) { /* Convert to silk_float */ for( k = 0; k < D; k++ ) { A[ k ] = (silk_float)( -Af[ k ] ); } /* Subtract energy of preceding samples from C0 */ for( s = 0; s < nb_subfr; s++ ) { C0 -= silk_energy_FLP( x + s * subfr_length, D ); } /* Approximate residual energy */ nrg_f = C0 * invGain; } else { /* Compute residual energy and store coefficients as silk_float */ nrg_f = CAf[ 0 ]; tmp1 = 1.0; for( k = 0; k < D; k++ ) { Atmp = Af[ k ]; nrg_f += CAf[ k + 1 ] * Atmp; tmp1 += Atmp * Atmp; A[ k ] = (silk_float)(-Atmp); } nrg_f -= FIND_LPC_COND_FAC * C0 * tmp1; } /* Return residual energy */ return (silk_float)nrg_f; } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/bwexpander_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FLP.h" /* Chirp (bw expand) LP AR filter */ void silk_bwexpander_FLP( silk_float *ar, /* I/O AR filter to be expanded (without leading 1) */ const opus_int d, /* I length of ar */ const silk_float chirp /* I chirp factor (typically in range (0..1) ) */ ) { opus_int i; silk_float cfac = chirp; for( i = 0; i < d - 1; i++ ) { ar[ i ] *= cfac; cfac *= chirp; } ar[ d - 1 ] *= cfac; } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/corrMatrix_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /********************************************************************** * Correlation matrix computations for LS estimate. **********************************************************************/ #include "main_FLP.h" /* Calculates correlation vector X'*t */ void silk_corrVector_FLP( const silk_float *x, /* I x vector [L+order-1] used to create X */ const silk_float *t, /* I Target vector [L] */ const opus_int L, /* I Length of vecors */ const opus_int Order, /* I Max lag for correlation */ silk_float *Xt /* O X'*t correlation vector [order] */ ) { opus_int lag; const silk_float *ptr1; ptr1 = &x[ Order - 1 ]; /* Points to first sample of column 0 of X: X[:,0] */ for( lag = 0; lag < Order; lag++ ) { /* Calculate X[:,lag]'*t */ Xt[ lag ] = (silk_float)silk_inner_product_FLP( ptr1, t, L ); ptr1--; /* Next column of X */ } } /* Calculates correlation matrix X'*X */ void silk_corrMatrix_FLP( const silk_float *x, /* I x vector [ L+order-1 ] used to create X */ const opus_int L, /* I Length of vectors */ const opus_int Order, /* I Max lag for correlation */ silk_float *XX /* O X'*X correlation matrix [order x order] */ ) { opus_int j, lag; double energy; const silk_float *ptr1, *ptr2; ptr1 = &x[ Order - 1 ]; /* First sample of column 0 of X */ energy = silk_energy_FLP( ptr1, L ); /* X[:,0]'*X[:,0] */ matrix_ptr( XX, 0, 0, Order ) = ( silk_float )energy; for( j = 1; j < Order; j++ ) { /* Calculate X[:,j]'*X[:,j] */ energy += ptr1[ -j ] * ptr1[ -j ] - ptr1[ L - j ] * ptr1[ L - j ]; matrix_ptr( XX, j, j, Order ) = ( silk_float )energy; } ptr2 = &x[ Order - 2 ]; /* First sample of column 1 of X */ for( lag = 1; lag < Order; lag++ ) { /* Calculate X[:,0]'*X[:,lag] */ energy = silk_inner_product_FLP( ptr1, ptr2, L ); matrix_ptr( XX, lag, 0, Order ) = ( silk_float )energy; matrix_ptr( XX, 0, lag, Order ) = ( silk_float )energy; /* Calculate X[:,j]'*X[:,j + lag] */ for( j = 1; j < ( Order - lag ); j++ ) { energy += ptr1[ -j ] * ptr2[ -j ] - ptr1[ L - j ] * ptr2[ L - j ]; matrix_ptr( XX, lag + j, j, Order ) = ( silk_float )energy; matrix_ptr( XX, j, lag + j, Order ) = ( silk_float )energy; } ptr2--; /* Next column of X */ } } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/encode_frame_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FLP.h" #include "tuning_parameters.h" /* Low Bitrate Redundancy (LBRR) encoding. Reuse all parameters but encode with lower bitrate */ static OPUS_INLINE void silk_LBRR_encode_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */ const silk_float xfw[], /* I Input signal */ opus_int condCoding /* I The type of conditional coding used so far for this frame */ ); void silk_encode_do_VAD_FLP( silk_encoder_state_FLP *psEnc /* I/O Encoder state FLP */ ) { /****************************/ /* Voice Activity Detection */ /****************************/ silk_VAD_GetSA_Q8( &psEnc->sCmn, psEnc->sCmn.inputBuf + 1, psEnc->sCmn.arch ); /**************************************************/ /* Convert speech activity into VAD and DTX flags */ /**************************************************/ if( psEnc->sCmn.speech_activity_Q8 < SILK_FIX_CONST( SPEECH_ACTIVITY_DTX_THRES, 8 ) ) { psEnc->sCmn.indices.signalType = TYPE_NO_VOICE_ACTIVITY; psEnc->sCmn.noSpeechCounter++; if( psEnc->sCmn.noSpeechCounter < NB_SPEECH_FRAMES_BEFORE_DTX ) { psEnc->sCmn.inDTX = 0; } else if( psEnc->sCmn.noSpeechCounter > MAX_CONSECUTIVE_DTX + NB_SPEECH_FRAMES_BEFORE_DTX ) { psEnc->sCmn.noSpeechCounter = NB_SPEECH_FRAMES_BEFORE_DTX; psEnc->sCmn.inDTX = 0; } psEnc->sCmn.VAD_flags[ psEnc->sCmn.nFramesEncoded ] = 0; } else { psEnc->sCmn.noSpeechCounter = 0; psEnc->sCmn.inDTX = 0; psEnc->sCmn.indices.signalType = TYPE_UNVOICED; psEnc->sCmn.VAD_flags[ psEnc->sCmn.nFramesEncoded ] = 1; } } /****************/ /* Encode frame */ /****************/ opus_int silk_encode_frame_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ opus_int32 *pnBytesOut, /* O Number of payload bytes; */ ec_enc *psRangeEnc, /* I/O compressor data structure */ opus_int condCoding, /* I The type of conditional coding to use */ opus_int maxBits, /* I If > 0: maximum number of output bits */ opus_int useCBR /* I Flag to force constant-bitrate operation */ ) { silk_encoder_control_FLP sEncCtrl; opus_int i, iter, maxIter, found_upper, found_lower, ret = 0; silk_float *x_frame, *res_pitch_frame; silk_float xfw[ MAX_FRAME_LENGTH ]; silk_float res_pitch[ 2 * MAX_FRAME_LENGTH + LA_PITCH_MAX ]; ec_enc sRangeEnc_copy, sRangeEnc_copy2; silk_nsq_state sNSQ_copy, sNSQ_copy2; opus_int32 seed_copy, nBits, nBits_lower, nBits_upper, gainMult_lower, gainMult_upper; opus_int32 gainsID, gainsID_lower, gainsID_upper; opus_int16 gainMult_Q8; opus_int16 ec_prevLagIndex_copy; opus_int ec_prevSignalType_copy; opus_int8 LastGainIndex_copy2; opus_int32 pGains_Q16[ MAX_NB_SUBFR ]; opus_uint8 ec_buf_copy[ 1275 ]; /* This is totally unnecessary but many compilers (including gcc) are too dumb to realise it */ LastGainIndex_copy2 = nBits_lower = nBits_upper = gainMult_lower = gainMult_upper = 0; psEnc->sCmn.indices.Seed = psEnc->sCmn.frameCounter++ & 3; /**************************************************************/ /* Set up Input Pointers, and insert frame in input buffer */ /**************************************************************/ /* pointers aligned with start of frame to encode */ x_frame = psEnc->x_buf + psEnc->sCmn.ltp_mem_length; /* start of frame to encode */ res_pitch_frame = res_pitch + psEnc->sCmn.ltp_mem_length; /* start of pitch LPC residual frame */ /***************************************/ /* Ensure smooth bandwidth transitions */ /***************************************/ silk_LP_variable_cutoff( &psEnc->sCmn.sLP, psEnc->sCmn.inputBuf + 1, psEnc->sCmn.frame_length ); /*******************************************/ /* Copy new frame to front of input buffer */ /*******************************************/ silk_short2float_array( x_frame + LA_SHAPE_MS * psEnc->sCmn.fs_kHz, psEnc->sCmn.inputBuf + 1, psEnc->sCmn.frame_length ); /* Add tiny signal to avoid high CPU load from denormalized floating point numbers */ for( i = 0; i < 8; i++ ) { x_frame[ LA_SHAPE_MS * psEnc->sCmn.fs_kHz + i * ( psEnc->sCmn.frame_length >> 3 ) ] += ( 1 - ( i & 2 ) ) * 1e-6f; } if( !psEnc->sCmn.prefillFlag ) { /*****************************************/ /* Find pitch lags, initial LPC analysis */ /*****************************************/ silk_find_pitch_lags_FLP( psEnc, &sEncCtrl, res_pitch, x_frame, psEnc->sCmn.arch ); /************************/ /* Noise shape analysis */ /************************/ silk_noise_shape_analysis_FLP( psEnc, &sEncCtrl, res_pitch_frame, x_frame ); /***************************************************/ /* Find linear prediction coefficients (LPC + LTP) */ /***************************************************/ silk_find_pred_coefs_FLP( psEnc, &sEncCtrl, res_pitch, x_frame, condCoding ); /****************************************/ /* Process gains */ /****************************************/ silk_process_gains_FLP( psEnc, &sEncCtrl, condCoding ); /*****************************************/ /* Prefiltering for noise shaper */ /*****************************************/ silk_prefilter_FLP( psEnc, &sEncCtrl, xfw, x_frame ); /****************************************/ /* Low Bitrate Redundant Encoding */ /****************************************/ silk_LBRR_encode_FLP( psEnc, &sEncCtrl, xfw, condCoding ); /* Loop over quantizer and entroy coding to control bitrate */ maxIter = 6; gainMult_Q8 = SILK_FIX_CONST( 1, 8 ); found_lower = 0; found_upper = 0; gainsID = silk_gains_ID( psEnc->sCmn.indices.GainsIndices, psEnc->sCmn.nb_subfr ); gainsID_lower = -1; gainsID_upper = -1; /* Copy part of the input state */ silk_memcpy( &sRangeEnc_copy, psRangeEnc, sizeof( ec_enc ) ); silk_memcpy( &sNSQ_copy, &psEnc->sCmn.sNSQ, sizeof( silk_nsq_state ) ); seed_copy = psEnc->sCmn.indices.Seed; ec_prevLagIndex_copy = psEnc->sCmn.ec_prevLagIndex; ec_prevSignalType_copy = psEnc->sCmn.ec_prevSignalType; for( iter = 0; ; iter++ ) { if( gainsID == gainsID_lower ) { nBits = nBits_lower; } else if( gainsID == gainsID_upper ) { nBits = nBits_upper; } else { /* Restore part of the input state */ if( iter > 0 ) { silk_memcpy( psRangeEnc, &sRangeEnc_copy, sizeof( ec_enc ) ); silk_memcpy( &psEnc->sCmn.sNSQ, &sNSQ_copy, sizeof( silk_nsq_state ) ); psEnc->sCmn.indices.Seed = seed_copy; psEnc->sCmn.ec_prevLagIndex = ec_prevLagIndex_copy; psEnc->sCmn.ec_prevSignalType = ec_prevSignalType_copy; } /*****************************************/ /* Noise shaping quantization */ /*****************************************/ silk_NSQ_wrapper_FLP( psEnc, &sEncCtrl, &psEnc->sCmn.indices, &psEnc->sCmn.sNSQ, psEnc->sCmn.pulses, xfw ); /****************************************/ /* Encode Parameters */ /****************************************/ silk_encode_indices( &psEnc->sCmn, psRangeEnc, psEnc->sCmn.nFramesEncoded, 0, condCoding ); /****************************************/ /* Encode Excitation Signal */ /****************************************/ silk_encode_pulses( psRangeEnc, psEnc->sCmn.indices.signalType, psEnc->sCmn.indices.quantOffsetType, psEnc->sCmn.pulses, psEnc->sCmn.frame_length ); nBits = ec_tell( psRangeEnc ); if( useCBR == 0 && iter == 0 && nBits <= maxBits ) { break; } } if( iter == maxIter ) { if( found_lower && ( gainsID == gainsID_lower || nBits > maxBits ) ) { /* Restore output state from earlier iteration that did meet the bitrate budget */ silk_memcpy( psRangeEnc, &sRangeEnc_copy2, sizeof( ec_enc ) ); silk_assert( sRangeEnc_copy2.offs <= 1275 ); silk_memcpy( psRangeEnc->buf, ec_buf_copy, sRangeEnc_copy2.offs ); silk_memcpy( &psEnc->sCmn.sNSQ, &sNSQ_copy2, sizeof( silk_nsq_state ) ); psEnc->sShape.LastGainIndex = LastGainIndex_copy2; } break; } if( nBits > maxBits ) { if( found_lower == 0 && iter >= 2 ) { /* Adjust the quantizer's rate/distortion tradeoff and discard previous "upper" results */ sEncCtrl.Lambda *= 1.5f; found_upper = 0; gainsID_upper = -1; } else { found_upper = 1; nBits_upper = nBits; gainMult_upper = gainMult_Q8; gainsID_upper = gainsID; } } else if( nBits < maxBits - 5 ) { found_lower = 1; nBits_lower = nBits; gainMult_lower = gainMult_Q8; if( gainsID != gainsID_lower ) { gainsID_lower = gainsID; /* Copy part of the output state */ silk_memcpy( &sRangeEnc_copy2, psRangeEnc, sizeof( ec_enc ) ); silk_assert( psRangeEnc->offs <= 1275 ); silk_memcpy( ec_buf_copy, psRangeEnc->buf, psRangeEnc->offs ); silk_memcpy( &sNSQ_copy2, &psEnc->sCmn.sNSQ, sizeof( silk_nsq_state ) ); LastGainIndex_copy2 = psEnc->sShape.LastGainIndex; } } else { /* Within 5 bits of budget: close enough */ break; } if( ( found_lower & found_upper ) == 0 ) { /* Adjust gain according to high-rate rate/distortion curve */ opus_int32 gain_factor_Q16; gain_factor_Q16 = silk_log2lin( silk_LSHIFT( nBits - maxBits, 7 ) / psEnc->sCmn.frame_length + SILK_FIX_CONST( 16, 7 ) ); gain_factor_Q16 = silk_min_32( gain_factor_Q16, SILK_FIX_CONST( 2, 16 ) ); if( nBits > maxBits ) { gain_factor_Q16 = silk_max_32( gain_factor_Q16, SILK_FIX_CONST( 1.3, 16 ) ); } gainMult_Q8 = silk_SMULWB( gain_factor_Q16, gainMult_Q8 ); } else { /* Adjust gain by interpolating */ gainMult_Q8 = gainMult_lower + ( ( gainMult_upper - gainMult_lower ) * ( maxBits - nBits_lower ) ) / ( nBits_upper - nBits_lower ); /* New gain multplier must be between 25% and 75% of old range (note that gainMult_upper < gainMult_lower) */ if( gainMult_Q8 > silk_ADD_RSHIFT32( gainMult_lower, gainMult_upper - gainMult_lower, 2 ) ) { gainMult_Q8 = silk_ADD_RSHIFT32( gainMult_lower, gainMult_upper - gainMult_lower, 2 ); } else if( gainMult_Q8 < silk_SUB_RSHIFT32( gainMult_upper, gainMult_upper - gainMult_lower, 2 ) ) { gainMult_Q8 = silk_SUB_RSHIFT32( gainMult_upper, gainMult_upper - gainMult_lower, 2 ); } } for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) { pGains_Q16[ i ] = silk_LSHIFT_SAT32( silk_SMULWB( sEncCtrl.GainsUnq_Q16[ i ], gainMult_Q8 ), 8 ); } /* Quantize gains */ psEnc->sShape.LastGainIndex = sEncCtrl.lastGainIndexPrev; silk_gains_quant( psEnc->sCmn.indices.GainsIndices, pGains_Q16, &psEnc->sShape.LastGainIndex, condCoding == CODE_CONDITIONALLY, psEnc->sCmn.nb_subfr ); /* Unique identifier of gains vector */ gainsID = silk_gains_ID( psEnc->sCmn.indices.GainsIndices, psEnc->sCmn.nb_subfr ); /* Overwrite unquantized gains with quantized gains and convert back to Q0 from Q16 */ for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) { sEncCtrl.Gains[ i ] = pGains_Q16[ i ] / 65536.0f; } } } /* Update input buffer */ silk_memmove( psEnc->x_buf, &psEnc->x_buf[ psEnc->sCmn.frame_length ], ( psEnc->sCmn.ltp_mem_length + LA_SHAPE_MS * psEnc->sCmn.fs_kHz ) * sizeof( silk_float ) ); /* Exit without entropy coding */ if( psEnc->sCmn.prefillFlag ) { /* No payload */ *pnBytesOut = 0; return ret; } /* Parameters needed for next frame */ psEnc->sCmn.prevLag = sEncCtrl.pitchL[ psEnc->sCmn.nb_subfr - 1 ]; psEnc->sCmn.prevSignalType = psEnc->sCmn.indices.signalType; /****************************************/ /* Finalize payload */ /****************************************/ psEnc->sCmn.first_frame_after_reset = 0; /* Payload size */ *pnBytesOut = silk_RSHIFT( ec_tell( psRangeEnc ) + 7, 3 ); return ret; } /* Low-Bitrate Redundancy (LBRR) encoding. Reuse all parameters but encode excitation at lower bitrate */ static OPUS_INLINE void silk_LBRR_encode_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */ const silk_float xfw[], /* I Input signal */ opus_int condCoding /* I The type of conditional coding used so far for this frame */ ) { opus_int k; opus_int32 Gains_Q16[ MAX_NB_SUBFR ]; silk_float TempGains[ MAX_NB_SUBFR ]; SideInfoIndices *psIndices_LBRR = &psEnc->sCmn.indices_LBRR[ psEnc->sCmn.nFramesEncoded ]; silk_nsq_state sNSQ_LBRR; /*******************************************/ /* Control use of inband LBRR */ /*******************************************/ if( psEnc->sCmn.LBRR_enabled && psEnc->sCmn.speech_activity_Q8 > SILK_FIX_CONST( LBRR_SPEECH_ACTIVITY_THRES, 8 ) ) { psEnc->sCmn.LBRR_flags[ psEnc->sCmn.nFramesEncoded ] = 1; /* Copy noise shaping quantizer state and quantization indices from regular encoding */ silk_memcpy( &sNSQ_LBRR, &psEnc->sCmn.sNSQ, sizeof( silk_nsq_state ) ); silk_memcpy( psIndices_LBRR, &psEnc->sCmn.indices, sizeof( SideInfoIndices ) ); /* Save original gains */ silk_memcpy( TempGains, psEncCtrl->Gains, psEnc->sCmn.nb_subfr * sizeof( silk_float ) ); if( psEnc->sCmn.nFramesEncoded == 0 || psEnc->sCmn.LBRR_flags[ psEnc->sCmn.nFramesEncoded - 1 ] == 0 ) { /* First frame in packet or previous frame not LBRR coded */ psEnc->sCmn.LBRRprevLastGainIndex = psEnc->sShape.LastGainIndex; /* Increase Gains to get target LBRR rate */ psIndices_LBRR->GainsIndices[ 0 ] += psEnc->sCmn.LBRR_GainIncreases; psIndices_LBRR->GainsIndices[ 0 ] = silk_min_int( psIndices_LBRR->GainsIndices[ 0 ], N_LEVELS_QGAIN - 1 ); } /* Decode to get gains in sync with decoder */ silk_gains_dequant( Gains_Q16, psIndices_LBRR->GainsIndices, &psEnc->sCmn.LBRRprevLastGainIndex, condCoding == CODE_CONDITIONALLY, psEnc->sCmn.nb_subfr ); /* Overwrite unquantized gains with quantized gains and convert back to Q0 from Q16 */ for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { psEncCtrl->Gains[ k ] = Gains_Q16[ k ] * ( 1.0f / 65536.0f ); } /*****************************************/ /* Noise shaping quantization */ /*****************************************/ silk_NSQ_wrapper_FLP( psEnc, psEncCtrl, psIndices_LBRR, &sNSQ_LBRR, psEnc->sCmn.pulses_LBRR[ psEnc->sCmn.nFramesEncoded ], xfw ); /* Restore original gains */ silk_memcpy( psEncCtrl->Gains, TempGains, psEnc->sCmn.nb_subfr * sizeof( silk_float ) ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/energy_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FLP.h" /* sum of squares of a silk_float array, with result as double */ double silk_energy_FLP( const silk_float *data, opus_int dataSize ) { opus_int i, dataSize4; double result; /* 4x unrolled loop */ result = 0.0; dataSize4 = dataSize & 0xFFFC; for( i = 0; i < dataSize4; i += 4 ) { result += data[ i + 0 ] * (double)data[ i + 0 ] + data[ i + 1 ] * (double)data[ i + 1 ] + data[ i + 2 ] * (double)data[ i + 2 ] + data[ i + 3 ] * (double)data[ i + 3 ]; } /* add any remaining products */ for( ; i < dataSize; i++ ) { result += data[ i ] * (double)data[ i ]; } silk_assert( result >= 0.0 ); return result; } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/find_LPC_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "define.h" #include "main_FLP.h" #include "tuning_parameters.h" /* LPC analysis */ void silk_find_LPC_FLP( silk_encoder_state *psEncC, /* I/O Encoder state */ opus_int16 NLSF_Q15[], /* O NLSFs */ const silk_float x[], /* I Input signal */ const silk_float minInvGain /* I Inverse of max prediction gain */ ) { opus_int k, subfr_length; silk_float a[ MAX_LPC_ORDER ]; /* Used only for NLSF interpolation */ silk_float res_nrg, res_nrg_2nd, res_nrg_interp; opus_int16 NLSF0_Q15[ MAX_LPC_ORDER ]; silk_float a_tmp[ MAX_LPC_ORDER ]; silk_float LPC_res[ MAX_FRAME_LENGTH + MAX_NB_SUBFR * MAX_LPC_ORDER ]; subfr_length = psEncC->subfr_length + psEncC->predictLPCOrder; /* Default: No interpolation */ psEncC->indices.NLSFInterpCoef_Q2 = 4; /* Burg AR analysis for the full frame */ res_nrg = silk_burg_modified_FLP( a, x, minInvGain, subfr_length, psEncC->nb_subfr, psEncC->predictLPCOrder ); if( psEncC->useInterpolatedNLSFs && !psEncC->first_frame_after_reset && psEncC->nb_subfr == MAX_NB_SUBFR ) { /* Optimal solution for last 10 ms; subtract residual energy here, as that's easier than */ /* adding it to the residual energy of the first 10 ms in each iteration of the search below */ res_nrg -= silk_burg_modified_FLP( a_tmp, x + ( MAX_NB_SUBFR / 2 ) * subfr_length, minInvGain, subfr_length, MAX_NB_SUBFR / 2, psEncC->predictLPCOrder ); /* Convert to NLSFs */ silk_A2NLSF_FLP( NLSF_Q15, a_tmp, psEncC->predictLPCOrder ); /* Search over interpolation indices to find the one with lowest residual energy */ res_nrg_2nd = silk_float_MAX; for( k = 3; k >= 0; k-- ) { /* Interpolate NLSFs for first half */ silk_interpolate( NLSF0_Q15, psEncC->prev_NLSFq_Q15, NLSF_Q15, k, psEncC->predictLPCOrder ); /* Convert to LPC for residual energy evaluation */ silk_NLSF2A_FLP( a_tmp, NLSF0_Q15, psEncC->predictLPCOrder ); /* Calculate residual energy with LSF interpolation */ silk_LPC_analysis_filter_FLP( LPC_res, a_tmp, x, 2 * subfr_length, psEncC->predictLPCOrder ); res_nrg_interp = (silk_float)( silk_energy_FLP( LPC_res + psEncC->predictLPCOrder, subfr_length - psEncC->predictLPCOrder ) + silk_energy_FLP( LPC_res + psEncC->predictLPCOrder + subfr_length, subfr_length - psEncC->predictLPCOrder ) ); /* Determine whether current interpolated NLSFs are best so far */ if( res_nrg_interp < res_nrg ) { /* Interpolation has lower residual energy */ res_nrg = res_nrg_interp; psEncC->indices.NLSFInterpCoef_Q2 = (opus_int8)k; } else if( res_nrg_interp > res_nrg_2nd ) { /* No reason to continue iterating - residual energies will continue to climb */ break; } res_nrg_2nd = res_nrg_interp; } } if( psEncC->indices.NLSFInterpCoef_Q2 == 4 ) { /* NLSF interpolation is currently inactive, calculate NLSFs from full frame AR coefficients */ silk_A2NLSF_FLP( NLSF_Q15, a, psEncC->predictLPCOrder ); } silk_assert( psEncC->indices.NLSFInterpCoef_Q2 == 4 || ( psEncC->useInterpolatedNLSFs && !psEncC->first_frame_after_reset && psEncC->nb_subfr == MAX_NB_SUBFR ) ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/find_LTP_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FLP.h" #include "tuning_parameters.h" void silk_find_LTP_FLP( silk_float b[ MAX_NB_SUBFR * LTP_ORDER ], /* O LTP coefs */ silk_float WLTP[ MAX_NB_SUBFR * LTP_ORDER * LTP_ORDER ], /* O Weight for LTP quantization */ silk_float *LTPredCodGain, /* O LTP coding gain */ const silk_float r_lpc[], /* I LPC residual */ const opus_int lag[ MAX_NB_SUBFR ], /* I LTP lags */ const silk_float Wght[ MAX_NB_SUBFR ], /* I Weights */ const opus_int subfr_length, /* I Subframe length */ const opus_int nb_subfr, /* I number of subframes */ const opus_int mem_offset /* I Number of samples in LTP memory */ ) { opus_int i, k; silk_float *b_ptr, temp, *WLTP_ptr; silk_float LPC_res_nrg, LPC_LTP_res_nrg; silk_float d[ MAX_NB_SUBFR ], m, g, delta_b[ LTP_ORDER ]; silk_float w[ MAX_NB_SUBFR ], nrg[ MAX_NB_SUBFR ], regu; silk_float Rr[ LTP_ORDER ], rr[ MAX_NB_SUBFR ]; const silk_float *r_ptr, *lag_ptr; b_ptr = b; WLTP_ptr = WLTP; r_ptr = &r_lpc[ mem_offset ]; for( k = 0; k < nb_subfr; k++ ) { lag_ptr = r_ptr - ( lag[ k ] + LTP_ORDER / 2 ); silk_corrMatrix_FLP( lag_ptr, subfr_length, LTP_ORDER, WLTP_ptr ); silk_corrVector_FLP( lag_ptr, r_ptr, subfr_length, LTP_ORDER, Rr ); rr[ k ] = ( silk_float )silk_energy_FLP( r_ptr, subfr_length ); regu = 1.0f + rr[ k ] + matrix_ptr( WLTP_ptr, 0, 0, LTP_ORDER ) + matrix_ptr( WLTP_ptr, LTP_ORDER-1, LTP_ORDER-1, LTP_ORDER ); regu *= LTP_DAMPING / 3; silk_regularize_correlations_FLP( WLTP_ptr, &rr[ k ], regu, LTP_ORDER ); silk_solve_LDL_FLP( WLTP_ptr, LTP_ORDER, Rr, b_ptr ); /* Calculate residual energy */ nrg[ k ] = silk_residual_energy_covar_FLP( b_ptr, WLTP_ptr, Rr, rr[ k ], LTP_ORDER ); temp = Wght[ k ] / ( nrg[ k ] * Wght[ k ] + 0.01f * subfr_length ); silk_scale_vector_FLP( WLTP_ptr, temp, LTP_ORDER * LTP_ORDER ); w[ k ] = matrix_ptr( WLTP_ptr, LTP_ORDER / 2, LTP_ORDER / 2, LTP_ORDER ); r_ptr += subfr_length; b_ptr += LTP_ORDER; WLTP_ptr += LTP_ORDER * LTP_ORDER; } /* Compute LTP coding gain */ if( LTPredCodGain != NULL ) { LPC_LTP_res_nrg = 1e-6f; LPC_res_nrg = 0.0f; for( k = 0; k < nb_subfr; k++ ) { LPC_res_nrg += rr[ k ] * Wght[ k ]; LPC_LTP_res_nrg += nrg[ k ] * Wght[ k ]; } silk_assert( LPC_LTP_res_nrg > 0 ); *LTPredCodGain = 3.0f * silk_log2( LPC_res_nrg / LPC_LTP_res_nrg ); } /* Smoothing */ /* d = sum( B, 1 ); */ b_ptr = b; for( k = 0; k < nb_subfr; k++ ) { d[ k ] = 0; for( i = 0; i < LTP_ORDER; i++ ) { d[ k ] += b_ptr[ i ]; } b_ptr += LTP_ORDER; } /* m = ( w * d' ) / ( sum( w ) + 1e-3 ); */ temp = 1e-3f; for( k = 0; k < nb_subfr; k++ ) { temp += w[ k ]; } m = 0; for( k = 0; k < nb_subfr; k++ ) { m += d[ k ] * w[ k ]; } m = m / temp; b_ptr = b; for( k = 0; k < nb_subfr; k++ ) { g = LTP_SMOOTHING / ( LTP_SMOOTHING + w[ k ] ) * ( m - d[ k ] ); temp = 0; for( i = 0; i < LTP_ORDER; i++ ) { delta_b[ i ] = silk_max_float( b_ptr[ i ], 0.1f ); temp += delta_b[ i ]; } temp = g / temp; for( i = 0; i < LTP_ORDER; i++ ) { b_ptr[ i ] = b_ptr[ i ] + delta_b[ i ] * temp; } b_ptr += LTP_ORDER; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/find_pitch_lags_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "main_FLP.h" #include "tuning_parameters.h" void silk_find_pitch_lags_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */ silk_float res[], /* O Residual */ const silk_float x[], /* I Speech signal */ int arch /* I Run-time architecture */ ) { opus_int buf_len; silk_float thrhld, res_nrg; const silk_float *x_buf_ptr, *x_buf; silk_float auto_corr[ MAX_FIND_PITCH_LPC_ORDER + 1 ]; silk_float A[ MAX_FIND_PITCH_LPC_ORDER ]; silk_float refl_coef[ MAX_FIND_PITCH_LPC_ORDER ]; silk_float Wsig[ FIND_PITCH_LPC_WIN_MAX ]; silk_float *Wsig_ptr; /******************************************/ /* Set up buffer lengths etc based on Fs */ /******************************************/ buf_len = psEnc->sCmn.la_pitch + psEnc->sCmn.frame_length + psEnc->sCmn.ltp_mem_length; /* Safety check */ silk_assert( buf_len >= psEnc->sCmn.pitch_LPC_win_length ); x_buf = x - psEnc->sCmn.ltp_mem_length; /******************************************/ /* Estimate LPC AR coeficients */ /******************************************/ /* Calculate windowed signal */ /* First LA_LTP samples */ x_buf_ptr = x_buf + buf_len - psEnc->sCmn.pitch_LPC_win_length; Wsig_ptr = Wsig; silk_apply_sine_window_FLP( Wsig_ptr, x_buf_ptr, 1, psEnc->sCmn.la_pitch ); /* Middle non-windowed samples */ Wsig_ptr += psEnc->sCmn.la_pitch; x_buf_ptr += psEnc->sCmn.la_pitch; silk_memcpy( Wsig_ptr, x_buf_ptr, ( psEnc->sCmn.pitch_LPC_win_length - ( psEnc->sCmn.la_pitch << 1 ) ) * sizeof( silk_float ) ); /* Last LA_LTP samples */ Wsig_ptr += psEnc->sCmn.pitch_LPC_win_length - ( psEnc->sCmn.la_pitch << 1 ); x_buf_ptr += psEnc->sCmn.pitch_LPC_win_length - ( psEnc->sCmn.la_pitch << 1 ); silk_apply_sine_window_FLP( Wsig_ptr, x_buf_ptr, 2, psEnc->sCmn.la_pitch ); /* Calculate autocorrelation sequence */ silk_autocorrelation_FLP( auto_corr, Wsig, psEnc->sCmn.pitch_LPC_win_length, psEnc->sCmn.pitchEstimationLPCOrder + 1 ); /* Add white noise, as a fraction of the energy */ auto_corr[ 0 ] += auto_corr[ 0 ] * FIND_PITCH_WHITE_NOISE_FRACTION + 1; /* Calculate the reflection coefficients using Schur */ res_nrg = silk_schur_FLP( refl_coef, auto_corr, psEnc->sCmn.pitchEstimationLPCOrder ); /* Prediction gain */ psEncCtrl->predGain = auto_corr[ 0 ] / silk_max_float( res_nrg, 1.0f ); /* Convert reflection coefficients to prediction coefficients */ silk_k2a_FLP( A, refl_coef, psEnc->sCmn.pitchEstimationLPCOrder ); /* Bandwidth expansion */ silk_bwexpander_FLP( A, psEnc->sCmn.pitchEstimationLPCOrder, FIND_PITCH_BANDWIDTH_EXPANSION ); /*****************************************/ /* LPC analysis filtering */ /*****************************************/ silk_LPC_analysis_filter_FLP( res, A, x_buf, buf_len, psEnc->sCmn.pitchEstimationLPCOrder ); if( psEnc->sCmn.indices.signalType != TYPE_NO_VOICE_ACTIVITY && psEnc->sCmn.first_frame_after_reset == 0 ) { /* Threshold for pitch estimator */ thrhld = 0.6f; thrhld -= 0.004f * psEnc->sCmn.pitchEstimationLPCOrder; thrhld -= 0.1f * psEnc->sCmn.speech_activity_Q8 * ( 1.0f / 256.0f ); thrhld -= 0.15f * (psEnc->sCmn.prevSignalType >> 1); thrhld -= 0.1f * psEnc->sCmn.input_tilt_Q15 * ( 1.0f / 32768.0f ); /*****************************************/ /* Call Pitch estimator */ /*****************************************/ if( silk_pitch_analysis_core_FLP( res, psEncCtrl->pitchL, &psEnc->sCmn.indices.lagIndex, &psEnc->sCmn.indices.contourIndex, &psEnc->LTPCorr, psEnc->sCmn.prevLag, psEnc->sCmn.pitchEstimationThreshold_Q16 / 65536.0f, thrhld, psEnc->sCmn.fs_kHz, psEnc->sCmn.pitchEstimationComplexity, psEnc->sCmn.nb_subfr, arch ) == 0 ) { psEnc->sCmn.indices.signalType = TYPE_VOICED; } else { psEnc->sCmn.indices.signalType = TYPE_UNVOICED; } } else { silk_memset( psEncCtrl->pitchL, 0, sizeof( psEncCtrl->pitchL ) ); psEnc->sCmn.indices.lagIndex = 0; psEnc->sCmn.indices.contourIndex = 0; psEnc->LTPCorr = 0; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/find_pred_coefs_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FLP.h" /* Find LPC and LTP coefficients */ void silk_find_pred_coefs_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */ const silk_float res_pitch[], /* I Residual from pitch analysis */ const silk_float x[], /* I Speech signal */ opus_int condCoding /* I The type of conditional coding to use */ ) { opus_int i; silk_float WLTP[ MAX_NB_SUBFR * LTP_ORDER * LTP_ORDER ]; silk_float invGains[ MAX_NB_SUBFR ], Wght[ MAX_NB_SUBFR ]; opus_int16 NLSF_Q15[ MAX_LPC_ORDER ]; const silk_float *x_ptr; silk_float *x_pre_ptr, LPC_in_pre[ MAX_NB_SUBFR * MAX_LPC_ORDER + MAX_FRAME_LENGTH ]; silk_float minInvGain; /* Weighting for weighted least squares */ for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) { silk_assert( psEncCtrl->Gains[ i ] > 0.0f ); invGains[ i ] = 1.0f / psEncCtrl->Gains[ i ]; Wght[ i ] = invGains[ i ] * invGains[ i ]; } if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { /**********/ /* VOICED */ /**********/ silk_assert( psEnc->sCmn.ltp_mem_length - psEnc->sCmn.predictLPCOrder >= psEncCtrl->pitchL[ 0 ] + LTP_ORDER / 2 ); /* LTP analysis */ silk_find_LTP_FLP( psEncCtrl->LTPCoef, WLTP, &psEncCtrl->LTPredCodGain, res_pitch, psEncCtrl->pitchL, Wght, psEnc->sCmn.subfr_length, psEnc->sCmn.nb_subfr, psEnc->sCmn.ltp_mem_length ); /* Quantize LTP gain parameters */ silk_quant_LTP_gains_FLP( psEncCtrl->LTPCoef, psEnc->sCmn.indices.LTPIndex, &psEnc->sCmn.indices.PERIndex, &psEnc->sCmn.sum_log_gain_Q7, WLTP, psEnc->sCmn.mu_LTP_Q9, psEnc->sCmn.LTPQuantLowComplexity, psEnc->sCmn.nb_subfr, psEnc->sCmn.arch ); /* Control LTP scaling */ silk_LTP_scale_ctrl_FLP( psEnc, psEncCtrl, condCoding ); /* Create LTP residual */ silk_LTP_analysis_filter_FLP( LPC_in_pre, x - psEnc->sCmn.predictLPCOrder, psEncCtrl->LTPCoef, psEncCtrl->pitchL, invGains, psEnc->sCmn.subfr_length, psEnc->sCmn.nb_subfr, psEnc->sCmn.predictLPCOrder ); } else { /************/ /* UNVOICED */ /************/ /* Create signal with prepended subframes, scaled by inverse gains */ x_ptr = x - psEnc->sCmn.predictLPCOrder; x_pre_ptr = LPC_in_pre; for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) { silk_scale_copy_vector_FLP( x_pre_ptr, x_ptr, invGains[ i ], psEnc->sCmn.subfr_length + psEnc->sCmn.predictLPCOrder ); x_pre_ptr += psEnc->sCmn.subfr_length + psEnc->sCmn.predictLPCOrder; x_ptr += psEnc->sCmn.subfr_length; } silk_memset( psEncCtrl->LTPCoef, 0, psEnc->sCmn.nb_subfr * LTP_ORDER * sizeof( silk_float ) ); psEncCtrl->LTPredCodGain = 0.0f; psEnc->sCmn.sum_log_gain_Q7 = 0; } /* Limit on total predictive coding gain */ if( psEnc->sCmn.first_frame_after_reset ) { minInvGain = 1.0f / MAX_PREDICTION_POWER_GAIN_AFTER_RESET; } else { minInvGain = (silk_float)pow( 2, psEncCtrl->LTPredCodGain / 3 ) / MAX_PREDICTION_POWER_GAIN; minInvGain /= 0.25f + 0.75f * psEncCtrl->coding_quality; } /* LPC_in_pre contains the LTP-filtered input for voiced, and the unfiltered input for unvoiced */ silk_find_LPC_FLP( &psEnc->sCmn, NLSF_Q15, LPC_in_pre, minInvGain ); /* Quantize LSFs */ silk_process_NLSFs_FLP( &psEnc->sCmn, psEncCtrl->PredCoef, NLSF_Q15, psEnc->sCmn.prev_NLSFq_Q15 ); /* Calculate residual energy using quantized LPC coefficients */ silk_residual_energy_FLP( psEncCtrl->ResNrg, LPC_in_pre, psEncCtrl->PredCoef, psEncCtrl->Gains, psEnc->sCmn.subfr_length, psEnc->sCmn.nb_subfr, psEnc->sCmn.predictLPCOrder ); /* Copy to prediction struct for use in next frame for interpolation */ silk_memcpy( psEnc->sCmn.prev_NLSFq_Q15, NLSF_Q15, sizeof( psEnc->sCmn.prev_NLSFq_Q15 ) ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/inner_product_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FLP.h" /* inner product of two silk_float arrays, with result as double */ double silk_inner_product_FLP( const silk_float *data1, const silk_float *data2, opus_int dataSize ) { opus_int i, dataSize4; double result; /* 4x unrolled loop */ result = 0.0; dataSize4 = dataSize & 0xFFFC; for( i = 0; i < dataSize4; i += 4 ) { result += data1[ i + 0 ] * (double)data2[ i + 0 ] + data1[ i + 1 ] * (double)data2[ i + 1 ] + data1[ i + 2 ] * (double)data2[ i + 2 ] + data1[ i + 3 ] * (double)data2[ i + 3 ]; } /* add any remaining products */ for( ; i < dataSize; i++ ) { result += data1[ i ] * (double)data2[ i ]; } return result; } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/k2a_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FLP.h" /* step up function, converts reflection coefficients to prediction coefficients */ void silk_k2a_FLP( silk_float *A, /* O prediction coefficients [order] */ const silk_float *rc, /* I reflection coefficients [order] */ opus_int32 order /* I prediction order */ ) { opus_int k, n; silk_float Atmp[ SILK_MAX_ORDER_LPC ]; for( k = 0; k < order; k++ ) { for( n = 0; n < k; n++ ) { Atmp[ n ] = A[ n ]; } for( n = 0; n < k; n++ ) { A[ n ] += Atmp[ k - n - 1 ] * rc[ k ]; } A[ k ] = -rc[ k ]; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/levinsondurbin_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FLP.h" /* Solve the normal equations using the Levinson-Durbin recursion */ silk_float silk_levinsondurbin_FLP( /* O prediction error energy */ silk_float A[], /* O prediction coefficients [order] */ const silk_float corr[], /* I input auto-correlations [order + 1] */ const opus_int order /* I prediction order */ ) { opus_int i, mHalf, m; silk_float min_nrg, nrg, t, km, Atmp1, Atmp2; min_nrg = 1e-12f * corr[ 0 ] + 1e-9f; nrg = corr[ 0 ]; nrg = silk_max_float(min_nrg, nrg); A[ 0 ] = corr[ 1 ] / nrg; nrg -= A[ 0 ] * corr[ 1 ]; nrg = silk_max_float(min_nrg, nrg); for( m = 1; m < order; m++ ) { t = corr[ m + 1 ]; for( i = 0; i < m; i++ ) { t -= A[ i ] * corr[ m - i ]; } /* reflection coefficient */ km = t / nrg; /* residual energy */ nrg -= km * t; nrg = silk_max_float(min_nrg, nrg); mHalf = m >> 1; for( i = 0; i < mHalf; i++ ) { Atmp1 = A[ i ]; Atmp2 = A[ m - i - 1 ]; A[ m - i - 1 ] -= km * Atmp1; A[ i ] -= km * Atmp2; } if( m & 1 ) { A[ mHalf ] -= km * A[ mHalf ]; } A[ m ] = km; } /* return the residual energy */ return nrg; } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/main_FLP.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_MAIN_FLP_H #define SILK_MAIN_FLP_H #include "SigProc_FLP.h" #include "SigProc_FIX.h" #include "structs_FLP.h" #include "main.h" #include "define.h" #include "debug.h" #include "entenc.h" #ifdef __cplusplus extern "C" { #endif #define silk_encoder_state_Fxx silk_encoder_state_FLP #define silk_encode_do_VAD_Fxx silk_encode_do_VAD_FLP #define silk_encode_frame_Fxx silk_encode_frame_FLP /*********************/ /* Encoder Functions */ /*********************/ /* High-pass filter with cutoff frequency adaptation based on pitch lag statistics */ void silk_HP_variable_cutoff( silk_encoder_state_Fxx state_Fxx[] /* I/O Encoder states */ ); /* Encoder main function */ void silk_encode_do_VAD_FLP( silk_encoder_state_FLP *psEnc /* I/O Encoder state FLP */ ); /* Encoder main function */ opus_int silk_encode_frame_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ opus_int32 *pnBytesOut, /* O Number of payload bytes; */ ec_enc *psRangeEnc, /* I/O compressor data structure */ opus_int condCoding, /* I The type of conditional coding to use */ opus_int maxBits, /* I If > 0: maximum number of output bits */ opus_int useCBR /* I Flag to force constant-bitrate operation */ ); /* Initializes the Silk encoder state */ opus_int silk_init_encoder( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ int arch /* I Run-tim architecture */ ); /* Control the Silk encoder */ opus_int silk_control_encoder( silk_encoder_state_FLP *psEnc, /* I/O Pointer to Silk encoder state FLP */ silk_EncControlStruct *encControl, /* I Control structure */ const opus_int32 TargetRate_bps, /* I Target max bitrate (bps) */ const opus_int allow_bw_switch, /* I Flag to allow switching audio bandwidth */ const opus_int channelNb, /* I Channel number */ const opus_int force_fs_kHz ); /****************/ /* Prefiltering */ /****************/ void silk_prefilter_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ const silk_encoder_control_FLP *psEncCtrl, /* I Encoder control FLP */ silk_float xw[], /* O Weighted signal */ const silk_float x[] /* I Speech signal */ ); /**************************/ /* Noise shaping analysis */ /**************************/ /* Compute noise shaping coefficients and initial gain values */ void silk_noise_shape_analysis_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */ const silk_float *pitch_res, /* I LPC residual from pitch analysis */ const silk_float *x /* I Input signal [frame_length + la_shape] */ ); /* Autocorrelations for a warped frequency axis */ void silk_warped_autocorrelation_FLP( silk_float *corr, /* O Result [order + 1] */ const silk_float *input, /* I Input data to correlate */ const silk_float warping, /* I Warping coefficient */ const opus_int length, /* I Length of input */ const opus_int order /* I Correlation order (even) */ ); /* Calculation of LTP state scaling */ void silk_LTP_scale_ctrl_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */ opus_int condCoding /* I The type of conditional coding to use */ ); /**********************************************/ /* Prediction Analysis */ /**********************************************/ /* Find pitch lags */ void silk_find_pitch_lags_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */ silk_float res[], /* O Residual */ const silk_float x[], /* I Speech signal */ int arch /* I Run-time architecture */ ); /* Find LPC and LTP coefficients */ void silk_find_pred_coefs_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */ const silk_float res_pitch[], /* I Residual from pitch analysis */ const silk_float x[], /* I Speech signal */ opus_int condCoding /* I The type of conditional coding to use */ ); /* LPC analysis */ void silk_find_LPC_FLP( silk_encoder_state *psEncC, /* I/O Encoder state */ opus_int16 NLSF_Q15[], /* O NLSFs */ const silk_float x[], /* I Input signal */ const silk_float minInvGain /* I Prediction gain from LTP (dB) */ ); /* LTP analysis */ void silk_find_LTP_FLP( silk_float b[ MAX_NB_SUBFR * LTP_ORDER ], /* O LTP coefs */ silk_float WLTP[ MAX_NB_SUBFR * LTP_ORDER * LTP_ORDER ], /* O Weight for LTP quantization */ silk_float *LTPredCodGain, /* O LTP coding gain */ const silk_float r_lpc[], /* I LPC residual */ const opus_int lag[ MAX_NB_SUBFR ], /* I LTP lags */ const silk_float Wght[ MAX_NB_SUBFR ], /* I Weights */ const opus_int subfr_length, /* I Subframe length */ const opus_int nb_subfr, /* I number of subframes */ const opus_int mem_offset /* I Number of samples in LTP memory */ ); void silk_LTP_analysis_filter_FLP( silk_float *LTP_res, /* O LTP res MAX_NB_SUBFR*(pre_lgth+subfr_lngth) */ const silk_float *x, /* I Input signal, with preceding samples */ const silk_float B[ LTP_ORDER * MAX_NB_SUBFR ], /* I LTP coefficients for each subframe */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lags */ const silk_float invGains[ MAX_NB_SUBFR ], /* I Inverse quantization gains */ const opus_int subfr_length, /* I Length of each subframe */ const opus_int nb_subfr, /* I number of subframes */ const opus_int pre_length /* I Preceding samples for each subframe */ ); /* Calculates residual energies of input subframes where all subframes have LPC_order */ /* of preceding samples */ void silk_residual_energy_FLP( silk_float nrgs[ MAX_NB_SUBFR ], /* O Residual energy per subframe */ const silk_float x[], /* I Input signal */ silk_float a[ 2 ][ MAX_LPC_ORDER ], /* I AR coefs for each frame half */ const silk_float gains[], /* I Quantization gains */ const opus_int subfr_length, /* I Subframe length */ const opus_int nb_subfr, /* I number of subframes */ const opus_int LPC_order /* I LPC order */ ); /* 16th order LPC analysis filter */ void silk_LPC_analysis_filter_FLP( silk_float r_LPC[], /* O LPC residual signal */ const silk_float PredCoef[], /* I LPC coefficients */ const silk_float s[], /* I Input signal */ const opus_int length, /* I Length of input signal */ const opus_int Order /* I LPC order */ ); /* LTP tap quantizer */ void silk_quant_LTP_gains_FLP( silk_float B[ MAX_NB_SUBFR * LTP_ORDER ], /* I/O (Un-)quantized LTP gains */ opus_int8 cbk_index[ MAX_NB_SUBFR ], /* O Codebook index */ opus_int8 *periodicity_index, /* O Periodicity index */ opus_int32 *sum_log_gain_Q7, /* I/O Cumulative max prediction gain */ const silk_float W[ MAX_NB_SUBFR * LTP_ORDER * LTP_ORDER ], /* I Error weights */ const opus_int mu_Q10, /* I Mu value (R/D tradeoff) */ const opus_int lowComplexity, /* I Flag for low complexity */ const opus_int nb_subfr, /* I number of subframes */ int arch /* I Run-time architecture */ ); /* Residual energy: nrg = wxx - 2 * wXx * c + c' * wXX * c */ silk_float silk_residual_energy_covar_FLP( /* O Weighted residual energy */ const silk_float *c, /* I Filter coefficients */ silk_float *wXX, /* I/O Weighted correlation matrix, reg. out */ const silk_float *wXx, /* I Weighted correlation vector */ const silk_float wxx, /* I Weighted correlation value */ const opus_int D /* I Dimension */ ); /* Processing of gains */ void silk_process_gains_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */ opus_int condCoding /* I The type of conditional coding to use */ ); /******************/ /* Linear Algebra */ /******************/ /* Calculates correlation matrix X'*X */ void silk_corrMatrix_FLP( const silk_float *x, /* I x vector [ L+order-1 ] used to create X */ const opus_int L, /* I Length of vectors */ const opus_int Order, /* I Max lag for correlation */ silk_float *XX /* O X'*X correlation matrix [order x order] */ ); /* Calculates correlation vector X'*t */ void silk_corrVector_FLP( const silk_float *x, /* I x vector [L+order-1] used to create X */ const silk_float *t, /* I Target vector [L] */ const opus_int L, /* I Length of vecors */ const opus_int Order, /* I Max lag for correlation */ silk_float *Xt /* O X'*t correlation vector [order] */ ); /* Add noise to matrix diagonal */ void silk_regularize_correlations_FLP( silk_float *XX, /* I/O Correlation matrices */ silk_float *xx, /* I/O Correlation values */ const silk_float noise, /* I Noise energy to add */ const opus_int D /* I Dimension of XX */ ); /* Function to solve linear equation Ax = b, where A is an MxM symmetric matrix */ void silk_solve_LDL_FLP( silk_float *A, /* I/O Symmetric square matrix, out: reg. */ const opus_int M, /* I Size of matrix */ const silk_float *b, /* I Pointer to b vector */ silk_float *x /* O Pointer to x solution vector */ ); /* Apply sine window to signal vector. */ /* Window types: */ /* 1 -> sine window from 0 to pi/2 */ /* 2 -> sine window from pi/2 to pi */ void silk_apply_sine_window_FLP( silk_float px_win[], /* O Pointer to windowed signal */ const silk_float px[], /* I Pointer to input signal */ const opus_int win_type, /* I Selects a window type */ const opus_int length /* I Window length, multiple of 4 */ ); /* Wrapper functions. Call flp / fix code */ /* Convert AR filter coefficients to NLSF parameters */ void silk_A2NLSF_FLP( opus_int16 *NLSF_Q15, /* O NLSF vector [ LPC_order ] */ const silk_float *pAR, /* I LPC coefficients [ LPC_order ] */ const opus_int LPC_order /* I LPC order */ ); /* Convert NLSF parameters to AR prediction filter coefficients */ void silk_NLSF2A_FLP( silk_float *pAR, /* O LPC coefficients [ LPC_order ] */ const opus_int16 *NLSF_Q15, /* I NLSF vector [ LPC_order ] */ const opus_int LPC_order /* I LPC order */ ); /* Limit, stabilize, and quantize NLSFs */ void silk_process_NLSFs_FLP( silk_encoder_state *psEncC, /* I/O Encoder state */ silk_float PredCoef[ 2 ][ MAX_LPC_ORDER ], /* O Prediction coefficients */ opus_int16 NLSF_Q15[ MAX_LPC_ORDER ], /* I/O Normalized LSFs (quant out) (0 - (2^15-1)) */ const opus_int16 prev_NLSF_Q15[ MAX_LPC_ORDER ] /* I Previous Normalized LSFs (0 - (2^15-1)) */ ); /* Floating-point Silk NSQ wrapper */ void silk_NSQ_wrapper_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */ SideInfoIndices *psIndices, /* I/O Quantization indices */ silk_nsq_state *psNSQ, /* I/O Noise Shaping Quantzation state */ opus_int8 pulses[], /* O Quantized pulse signal */ const silk_float x[] /* I Prefiltered input signal */ ); #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/float/noise_shape_analysis_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FLP.h" #include "tuning_parameters.h" /* Compute gain to make warped filter coefficients have a zero mean log frequency response on a */ /* non-warped frequency scale. (So that it can be implemented with a minimum-phase monic filter.) */ /* Note: A monic filter is one with the first coefficient equal to 1.0. In Silk we omit the first */ /* coefficient in an array of coefficients, for monic filters. */ static OPUS_INLINE silk_float warped_gain( const silk_float *coefs, silk_float lambda, opus_int order ) { opus_int i; silk_float gain; lambda = -lambda; gain = coefs[ order - 1 ]; for( i = order - 2; i >= 0; i-- ) { gain = lambda * gain + coefs[ i ]; } return (silk_float)( 1.0f / ( 1.0f - lambda * gain ) ); } /* Convert warped filter coefficients to monic pseudo-warped coefficients and limit maximum */ /* amplitude of monic warped coefficients by using bandwidth expansion on the true coefficients */ static OPUS_INLINE void warped_true2monic_coefs( silk_float *coefs_syn, silk_float *coefs_ana, silk_float lambda, silk_float limit, opus_int order ) { opus_int i, iter, ind = 0; silk_float tmp, maxabs, chirp, gain_syn, gain_ana; /* Convert to monic coefficients */ for( i = order - 1; i > 0; i-- ) { coefs_syn[ i - 1 ] -= lambda * coefs_syn[ i ]; coefs_ana[ i - 1 ] -= lambda * coefs_ana[ i ]; } gain_syn = ( 1.0f - lambda * lambda ) / ( 1.0f + lambda * coefs_syn[ 0 ] ); gain_ana = ( 1.0f - lambda * lambda ) / ( 1.0f + lambda * coefs_ana[ 0 ] ); for( i = 0; i < order; i++ ) { coefs_syn[ i ] *= gain_syn; coefs_ana[ i ] *= gain_ana; } /* Limit */ for( iter = 0; iter < 10; iter++ ) { /* Find maximum absolute value */ maxabs = -1.0f; for( i = 0; i < order; i++ ) { tmp = silk_max( silk_abs_float( coefs_syn[ i ] ), silk_abs_float( coefs_ana[ i ] ) ); if( tmp > maxabs ) { maxabs = tmp; ind = i; } } if( maxabs <= limit ) { /* Coefficients are within range - done */ return; } /* Convert back to true warped coefficients */ for( i = 1; i < order; i++ ) { coefs_syn[ i - 1 ] += lambda * coefs_syn[ i ]; coefs_ana[ i - 1 ] += lambda * coefs_ana[ i ]; } gain_syn = 1.0f / gain_syn; gain_ana = 1.0f / gain_ana; for( i = 0; i < order; i++ ) { coefs_syn[ i ] *= gain_syn; coefs_ana[ i ] *= gain_ana; } /* Apply bandwidth expansion */ chirp = 0.99f - ( 0.8f + 0.1f * iter ) * ( maxabs - limit ) / ( maxabs * ( ind + 1 ) ); silk_bwexpander_FLP( coefs_syn, order, chirp ); silk_bwexpander_FLP( coefs_ana, order, chirp ); /* Convert to monic warped coefficients */ for( i = order - 1; i > 0; i-- ) { coefs_syn[ i - 1 ] -= lambda * coefs_syn[ i ]; coefs_ana[ i - 1 ] -= lambda * coefs_ana[ i ]; } gain_syn = ( 1.0f - lambda * lambda ) / ( 1.0f + lambda * coefs_syn[ 0 ] ); gain_ana = ( 1.0f - lambda * lambda ) / ( 1.0f + lambda * coefs_ana[ 0 ] ); for( i = 0; i < order; i++ ) { coefs_syn[ i ] *= gain_syn; coefs_ana[ i ] *= gain_ana; } } silk_assert( 0 ); } /* Compute noise shaping coefficients and initial gain values */ void silk_noise_shape_analysis_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */ const silk_float *pitch_res, /* I LPC residual from pitch analysis */ const silk_float *x /* I Input signal [frame_length + la_shape] */ ) { silk_shape_state_FLP *psShapeSt = &psEnc->sShape; opus_int k, nSamples; silk_float SNR_adj_dB, HarmBoost, HarmShapeGain, Tilt; silk_float nrg, pre_nrg, log_energy, log_energy_prev, energy_variation; silk_float delta, BWExp1, BWExp2, gain_mult, gain_add, strength, b, warping; silk_float x_windowed[ SHAPE_LPC_WIN_MAX ]; silk_float auto_corr[ MAX_SHAPE_LPC_ORDER + 1 ]; const silk_float *x_ptr, *pitch_res_ptr; /* Point to start of first LPC analysis block */ x_ptr = x - psEnc->sCmn.la_shape; /****************/ /* GAIN CONTROL */ /****************/ SNR_adj_dB = psEnc->sCmn.SNR_dB_Q7 * ( 1 / 128.0f ); /* Input quality is the average of the quality in the lowest two VAD bands */ psEncCtrl->input_quality = 0.5f * ( psEnc->sCmn.input_quality_bands_Q15[ 0 ] + psEnc->sCmn.input_quality_bands_Q15[ 1 ] ) * ( 1.0f / 32768.0f ); /* Coding quality level, between 0.0 and 1.0 */ psEncCtrl->coding_quality = silk_sigmoid( 0.25f * ( SNR_adj_dB - 20.0f ) ); if( psEnc->sCmn.useCBR == 0 ) { /* Reduce coding SNR during low speech activity */ b = 1.0f - psEnc->sCmn.speech_activity_Q8 * ( 1.0f / 256.0f ); SNR_adj_dB -= BG_SNR_DECR_dB * psEncCtrl->coding_quality * ( 0.5f + 0.5f * psEncCtrl->input_quality ) * b * b; } if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { /* Reduce gains for periodic signals */ SNR_adj_dB += HARM_SNR_INCR_dB * psEnc->LTPCorr; } else { /* For unvoiced signals and low-quality input, adjust the quality slower than SNR_dB setting */ SNR_adj_dB += ( -0.4f * psEnc->sCmn.SNR_dB_Q7 * ( 1 / 128.0f ) + 6.0f ) * ( 1.0f - psEncCtrl->input_quality ); } /*************************/ /* SPARSENESS PROCESSING */ /*************************/ /* Set quantizer offset */ if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { /* Initially set to 0; may be overruled in process_gains(..) */ psEnc->sCmn.indices.quantOffsetType = 0; psEncCtrl->sparseness = 0.0f; } else { /* Sparseness measure, based on relative fluctuations of energy per 2 milliseconds */ nSamples = 2 * psEnc->sCmn.fs_kHz; energy_variation = 0.0f; log_energy_prev = 0.0f; pitch_res_ptr = pitch_res; for( k = 0; k < silk_SMULBB( SUB_FRAME_LENGTH_MS, psEnc->sCmn.nb_subfr ) / 2; k++ ) { nrg = ( silk_float )nSamples + ( silk_float )silk_energy_FLP( pitch_res_ptr, nSamples ); log_energy = silk_log2( nrg ); if( k > 0 ) { energy_variation += silk_abs_float( log_energy - log_energy_prev ); } log_energy_prev = log_energy; pitch_res_ptr += nSamples; } psEncCtrl->sparseness = silk_sigmoid( 0.4f * ( energy_variation - 5.0f ) ); /* Set quantization offset depending on sparseness measure */ if( psEncCtrl->sparseness > SPARSENESS_THRESHOLD_QNT_OFFSET ) { psEnc->sCmn.indices.quantOffsetType = 0; } else { psEnc->sCmn.indices.quantOffsetType = 1; } /* Increase coding SNR for sparse signals */ SNR_adj_dB += SPARSE_SNR_INCR_dB * ( psEncCtrl->sparseness - 0.5f ); } /*******************************/ /* Control bandwidth expansion */ /*******************************/ /* More BWE for signals with high prediction gain */ strength = FIND_PITCH_WHITE_NOISE_FRACTION * psEncCtrl->predGain; /* between 0.0 and 1.0 */ BWExp1 = BWExp2 = BANDWIDTH_EXPANSION / ( 1.0f + strength * strength ); delta = LOW_RATE_BANDWIDTH_EXPANSION_DELTA * ( 1.0f - 0.75f * psEncCtrl->coding_quality ); BWExp1 -= delta; BWExp2 += delta; /* BWExp1 will be applied after BWExp2, so make it relative */ BWExp1 /= BWExp2; if( psEnc->sCmn.warping_Q16 > 0 ) { /* Slightly more warping in analysis will move quantization noise up in frequency, where it's better masked */ warping = (silk_float)psEnc->sCmn.warping_Q16 / 65536.0f + 0.01f * psEncCtrl->coding_quality; } else { warping = 0.0f; } /********************************************/ /* Compute noise shaping AR coefs and gains */ /********************************************/ for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { /* Apply window: sine slope followed by flat part followed by cosine slope */ opus_int shift, slope_part, flat_part; flat_part = psEnc->sCmn.fs_kHz * 3; slope_part = ( psEnc->sCmn.shapeWinLength - flat_part ) / 2; silk_apply_sine_window_FLP( x_windowed, x_ptr, 1, slope_part ); shift = slope_part; silk_memcpy( x_windowed + shift, x_ptr + shift, flat_part * sizeof(silk_float) ); shift += flat_part; silk_apply_sine_window_FLP( x_windowed + shift, x_ptr + shift, 2, slope_part ); /* Update pointer: next LPC analysis block */ x_ptr += psEnc->sCmn.subfr_length; if( psEnc->sCmn.warping_Q16 > 0 ) { /* Calculate warped auto correlation */ silk_warped_autocorrelation_FLP( auto_corr, x_windowed, warping, psEnc->sCmn.shapeWinLength, psEnc->sCmn.shapingLPCOrder ); } else { /* Calculate regular auto correlation */ silk_autocorrelation_FLP( auto_corr, x_windowed, psEnc->sCmn.shapeWinLength, psEnc->sCmn.shapingLPCOrder + 1 ); } /* Add white noise, as a fraction of energy */ auto_corr[ 0 ] += auto_corr[ 0 ] * SHAPE_WHITE_NOISE_FRACTION; /* Convert correlations to prediction coefficients, and compute residual energy */ nrg = silk_levinsondurbin_FLP( &psEncCtrl->AR2[ k * MAX_SHAPE_LPC_ORDER ], auto_corr, psEnc->sCmn.shapingLPCOrder ); psEncCtrl->Gains[ k ] = ( silk_float )sqrt( nrg ); if( psEnc->sCmn.warping_Q16 > 0 ) { /* Adjust gain for warping */ psEncCtrl->Gains[ k ] *= warped_gain( &psEncCtrl->AR2[ k * MAX_SHAPE_LPC_ORDER ], warping, psEnc->sCmn.shapingLPCOrder ); } /* Bandwidth expansion for synthesis filter shaping */ silk_bwexpander_FLP( &psEncCtrl->AR2[ k * MAX_SHAPE_LPC_ORDER ], psEnc->sCmn.shapingLPCOrder, BWExp2 ); /* Compute noise shaping filter coefficients */ silk_memcpy( &psEncCtrl->AR1[ k * MAX_SHAPE_LPC_ORDER ], &psEncCtrl->AR2[ k * MAX_SHAPE_LPC_ORDER ], psEnc->sCmn.shapingLPCOrder * sizeof( silk_float ) ); /* Bandwidth expansion for analysis filter shaping */ silk_bwexpander_FLP( &psEncCtrl->AR1[ k * MAX_SHAPE_LPC_ORDER ], psEnc->sCmn.shapingLPCOrder, BWExp1 ); /* Ratio of prediction gains, in energy domain */ pre_nrg = silk_LPC_inverse_pred_gain_FLP( &psEncCtrl->AR2[ k * MAX_SHAPE_LPC_ORDER ], psEnc->sCmn.shapingLPCOrder ); nrg = silk_LPC_inverse_pred_gain_FLP( &psEncCtrl->AR1[ k * MAX_SHAPE_LPC_ORDER ], psEnc->sCmn.shapingLPCOrder ); psEncCtrl->GainsPre[ k ] = 1.0f - 0.7f * ( 1.0f - pre_nrg / nrg ); /* Convert to monic warped prediction coefficients and limit absolute values */ warped_true2monic_coefs( &psEncCtrl->AR2[ k * MAX_SHAPE_LPC_ORDER ], &psEncCtrl->AR1[ k * MAX_SHAPE_LPC_ORDER ], warping, 3.999f, psEnc->sCmn.shapingLPCOrder ); } /*****************/ /* Gain tweaking */ /*****************/ /* Increase gains during low speech activity */ gain_mult = (silk_float)pow( 2.0f, -0.16f * SNR_adj_dB ); gain_add = (silk_float)pow( 2.0f, 0.16f * MIN_QGAIN_DB ); for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { psEncCtrl->Gains[ k ] *= gain_mult; psEncCtrl->Gains[ k ] += gain_add; } gain_mult = 1.0f + INPUT_TILT + psEncCtrl->coding_quality * HIGH_RATE_INPUT_TILT; for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { psEncCtrl->GainsPre[ k ] *= gain_mult; } /************************************************/ /* Control low-frequency shaping and noise tilt */ /************************************************/ /* Less low frequency shaping for noisy inputs */ strength = LOW_FREQ_SHAPING * ( 1.0f + LOW_QUALITY_LOW_FREQ_SHAPING_DECR * ( psEnc->sCmn.input_quality_bands_Q15[ 0 ] * ( 1.0f / 32768.0f ) - 1.0f ) ); strength *= psEnc->sCmn.speech_activity_Q8 * ( 1.0f / 256.0f ); if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { /* Reduce low frequencies quantization noise for periodic signals, depending on pitch lag */ /*f = 400; freqz([1, -0.98 + 2e-4 * f], [1, -0.97 + 7e-4 * f], 2^12, Fs); axis([0, 1000, -10, 1])*/ for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { b = 0.2f / psEnc->sCmn.fs_kHz + 3.0f / psEncCtrl->pitchL[ k ]; psEncCtrl->LF_MA_shp[ k ] = -1.0f + b; psEncCtrl->LF_AR_shp[ k ] = 1.0f - b - b * strength; } Tilt = - HP_NOISE_COEF - (1 - HP_NOISE_COEF) * HARM_HP_NOISE_COEF * psEnc->sCmn.speech_activity_Q8 * ( 1.0f / 256.0f ); } else { b = 1.3f / psEnc->sCmn.fs_kHz; psEncCtrl->LF_MA_shp[ 0 ] = -1.0f + b; psEncCtrl->LF_AR_shp[ 0 ] = 1.0f - b - b * strength * 0.6f; for( k = 1; k < psEnc->sCmn.nb_subfr; k++ ) { psEncCtrl->LF_MA_shp[ k ] = psEncCtrl->LF_MA_shp[ 0 ]; psEncCtrl->LF_AR_shp[ k ] = psEncCtrl->LF_AR_shp[ 0 ]; } Tilt = -HP_NOISE_COEF; } /****************************/ /* HARMONIC SHAPING CONTROL */ /****************************/ /* Control boosting of harmonic frequencies */ HarmBoost = LOW_RATE_HARMONIC_BOOST * ( 1.0f - psEncCtrl->coding_quality ) * psEnc->LTPCorr; /* More harmonic boost for noisy input signals */ HarmBoost += LOW_INPUT_QUALITY_HARMONIC_BOOST * ( 1.0f - psEncCtrl->input_quality ); if( USE_HARM_SHAPING && psEnc->sCmn.indices.signalType == TYPE_VOICED ) { /* Harmonic noise shaping */ HarmShapeGain = HARMONIC_SHAPING; /* More harmonic noise shaping for high bitrates or noisy input */ HarmShapeGain += HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING * ( 1.0f - ( 1.0f - psEncCtrl->coding_quality ) * psEncCtrl->input_quality ); /* Less harmonic noise shaping for less periodic signals */ HarmShapeGain *= ( silk_float )sqrt( psEnc->LTPCorr ); } else { HarmShapeGain = 0.0f; } /*************************/ /* Smooth over subframes */ /*************************/ for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { psShapeSt->HarmBoost_smth += SUBFR_SMTH_COEF * ( HarmBoost - psShapeSt->HarmBoost_smth ); psEncCtrl->HarmBoost[ k ] = psShapeSt->HarmBoost_smth; psShapeSt->HarmShapeGain_smth += SUBFR_SMTH_COEF * ( HarmShapeGain - psShapeSt->HarmShapeGain_smth ); psEncCtrl->HarmShapeGain[ k ] = psShapeSt->HarmShapeGain_smth; psShapeSt->Tilt_smth += SUBFR_SMTH_COEF * ( Tilt - psShapeSt->Tilt_smth ); psEncCtrl->Tilt[ k ] = psShapeSt->Tilt_smth; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/pitch_analysis_core_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /***************************************************************************** * Pitch analyser function ******************************************************************************/ #include "SigProc_FLP.h" #include "SigProc_FIX.h" #include "pitch_est_defines.h" #include "pitch.h" #define SCRATCH_SIZE 22 /************************************************************/ /* Internally used functions */ /************************************************************/ static void silk_P_Ana_calc_corr_st3( silk_float cross_corr_st3[ PE_MAX_NB_SUBFR ][ PE_NB_CBKS_STAGE3_MAX ][ PE_NB_STAGE3_LAGS ], /* O 3 DIM correlation array */ const silk_float frame[], /* I vector to correlate */ opus_int start_lag, /* I start lag */ opus_int sf_length, /* I sub frame length */ opus_int nb_subfr, /* I number of subframes */ opus_int complexity, /* I Complexity setting */ int arch /* I Run-time architecture */ ); static void silk_P_Ana_calc_energy_st3( silk_float energies_st3[ PE_MAX_NB_SUBFR ][ PE_NB_CBKS_STAGE3_MAX ][ PE_NB_STAGE3_LAGS ], /* O 3 DIM correlation array */ const silk_float frame[], /* I vector to correlate */ opus_int start_lag, /* I start lag */ opus_int sf_length, /* I sub frame length */ opus_int nb_subfr, /* I number of subframes */ opus_int complexity /* I Complexity setting */ ); /************************************************************/ /* CORE PITCH ANALYSIS FUNCTION */ /************************************************************/ opus_int silk_pitch_analysis_core_FLP( /* O Voicing estimate: 0 voiced, 1 unvoiced */ const silk_float *frame, /* I Signal of length PE_FRAME_LENGTH_MS*Fs_kHz */ opus_int *pitch_out, /* O Pitch lag values [nb_subfr] */ opus_int16 *lagIndex, /* O Lag Index */ opus_int8 *contourIndex, /* O Pitch contour Index */ silk_float *LTPCorr, /* I/O Normalized correlation; input: value from previous frame */ opus_int prevLag, /* I Last lag of previous frame; set to zero is unvoiced */ const silk_float search_thres1, /* I First stage threshold for lag candidates 0 - 1 */ const silk_float search_thres2, /* I Final threshold for lag candidates 0 - 1 */ const opus_int Fs_kHz, /* I sample frequency (kHz) */ const opus_int complexity, /* I Complexity setting, 0-2, where 2 is highest */ const opus_int nb_subfr, /* I Number of 5 ms subframes */ int arch /* I Run-time architecture */ ) { opus_int i, k, d, j; silk_float frame_8kHz[ PE_MAX_FRAME_LENGTH_MS * 8 ]; silk_float frame_4kHz[ PE_MAX_FRAME_LENGTH_MS * 4 ]; opus_int16 frame_8_FIX[ PE_MAX_FRAME_LENGTH_MS * 8 ]; opus_int16 frame_4_FIX[ PE_MAX_FRAME_LENGTH_MS * 4 ]; opus_int32 filt_state[ 6 ]; silk_float threshold, contour_bias; silk_float C[ PE_MAX_NB_SUBFR][ (PE_MAX_LAG >> 1) + 5 ]; opus_val32 xcorr[ PE_MAX_LAG_MS * 4 - PE_MIN_LAG_MS * 4 + 1 ]; silk_float CC[ PE_NB_CBKS_STAGE2_EXT ]; const silk_float *target_ptr, *basis_ptr; double cross_corr, normalizer, energy, energy_tmp; opus_int d_srch[ PE_D_SRCH_LENGTH ]; opus_int16 d_comp[ (PE_MAX_LAG >> 1) + 5 ]; opus_int length_d_srch, length_d_comp; silk_float Cmax, CCmax, CCmax_b, CCmax_new_b, CCmax_new; opus_int CBimax, CBimax_new, lag, start_lag, end_lag, lag_new; opus_int cbk_size; silk_float lag_log2, prevLag_log2, delta_lag_log2_sqr; silk_float energies_st3[ PE_MAX_NB_SUBFR ][ PE_NB_CBKS_STAGE3_MAX ][ PE_NB_STAGE3_LAGS ]; silk_float cross_corr_st3[ PE_MAX_NB_SUBFR ][ PE_NB_CBKS_STAGE3_MAX ][ PE_NB_STAGE3_LAGS ]; opus_int lag_counter; opus_int frame_length, frame_length_8kHz, frame_length_4kHz; opus_int sf_length, sf_length_8kHz, sf_length_4kHz; opus_int min_lag, min_lag_8kHz, min_lag_4kHz; opus_int max_lag, max_lag_8kHz, max_lag_4kHz; opus_int nb_cbk_search; const opus_int8 *Lag_CB_ptr; /* Check for valid sampling frequency */ silk_assert( Fs_kHz == 8 || Fs_kHz == 12 || Fs_kHz == 16 ); /* Check for valid complexity setting */ silk_assert( complexity >= SILK_PE_MIN_COMPLEX ); silk_assert( complexity <= SILK_PE_MAX_COMPLEX ); silk_assert( search_thres1 >= 0.0f && search_thres1 <= 1.0f ); silk_assert( search_thres2 >= 0.0f && search_thres2 <= 1.0f ); /* Set up frame lengths max / min lag for the sampling frequency */ frame_length = ( PE_LTP_MEM_LENGTH_MS + nb_subfr * PE_SUBFR_LENGTH_MS ) * Fs_kHz; frame_length_4kHz = ( PE_LTP_MEM_LENGTH_MS + nb_subfr * PE_SUBFR_LENGTH_MS ) * 4; frame_length_8kHz = ( PE_LTP_MEM_LENGTH_MS + nb_subfr * PE_SUBFR_LENGTH_MS ) * 8; sf_length = PE_SUBFR_LENGTH_MS * Fs_kHz; sf_length_4kHz = PE_SUBFR_LENGTH_MS * 4; sf_length_8kHz = PE_SUBFR_LENGTH_MS * 8; min_lag = PE_MIN_LAG_MS * Fs_kHz; min_lag_4kHz = PE_MIN_LAG_MS * 4; min_lag_8kHz = PE_MIN_LAG_MS * 8; max_lag = PE_MAX_LAG_MS * Fs_kHz - 1; max_lag_4kHz = PE_MAX_LAG_MS * 4; max_lag_8kHz = PE_MAX_LAG_MS * 8 - 1; /* Resample from input sampled at Fs_kHz to 8 kHz */ if( Fs_kHz == 16 ) { /* Resample to 16 -> 8 khz */ opus_int16 frame_16_FIX[ 16 * PE_MAX_FRAME_LENGTH_MS ]; silk_float2short_array( frame_16_FIX, frame, frame_length ); silk_memset( filt_state, 0, 2 * sizeof( opus_int32 ) ); silk_resampler_down2( filt_state, frame_8_FIX, frame_16_FIX, frame_length ); silk_short2float_array( frame_8kHz, frame_8_FIX, frame_length_8kHz ); } else if( Fs_kHz == 12 ) { /* Resample to 12 -> 8 khz */ opus_int16 frame_12_FIX[ 12 * PE_MAX_FRAME_LENGTH_MS ]; silk_float2short_array( frame_12_FIX, frame, frame_length ); silk_memset( filt_state, 0, 6 * sizeof( opus_int32 ) ); silk_resampler_down2_3( filt_state, frame_8_FIX, frame_12_FIX, frame_length ); silk_short2float_array( frame_8kHz, frame_8_FIX, frame_length_8kHz ); } else { silk_assert( Fs_kHz == 8 ); silk_float2short_array( frame_8_FIX, frame, frame_length_8kHz ); } /* Decimate again to 4 kHz */ silk_memset( filt_state, 0, 2 * sizeof( opus_int32 ) ); silk_resampler_down2( filt_state, frame_4_FIX, frame_8_FIX, frame_length_8kHz ); silk_short2float_array( frame_4kHz, frame_4_FIX, frame_length_4kHz ); /* Low-pass filter */ for( i = frame_length_4kHz - 1; i > 0; i-- ) { frame_4kHz[ i ] += frame_4kHz[ i - 1 ]; } /****************************************************************************** * FIRST STAGE, operating in 4 khz ******************************************************************************/ silk_memset(C, 0, sizeof(silk_float) * nb_subfr * ((PE_MAX_LAG >> 1) + 5)); target_ptr = &frame_4kHz[ silk_LSHIFT( sf_length_4kHz, 2 ) ]; for( k = 0; k < nb_subfr >> 1; k++ ) { /* Check that we are within range of the array */ silk_assert( target_ptr >= frame_4kHz ); silk_assert( target_ptr + sf_length_8kHz <= frame_4kHz + frame_length_4kHz ); basis_ptr = target_ptr - min_lag_4kHz; /* Check that we are within range of the array */ silk_assert( basis_ptr >= frame_4kHz ); silk_assert( basis_ptr + sf_length_8kHz <= frame_4kHz + frame_length_4kHz ); celt_pitch_xcorr( target_ptr, target_ptr-max_lag_4kHz, xcorr, sf_length_8kHz, max_lag_4kHz - min_lag_4kHz + 1, arch ); /* Calculate first vector products before loop */ cross_corr = xcorr[ max_lag_4kHz - min_lag_4kHz ]; normalizer = silk_energy_FLP( target_ptr, sf_length_8kHz ) + silk_energy_FLP( basis_ptr, sf_length_8kHz ) + sf_length_8kHz * 4000.0f; C[ 0 ][ min_lag_4kHz ] += (silk_float)( 2 * cross_corr / normalizer ); /* From now on normalizer is computed recursively */ for( d = min_lag_4kHz + 1; d <= max_lag_4kHz; d++ ) { basis_ptr--; /* Check that we are within range of the array */ silk_assert( basis_ptr >= frame_4kHz ); silk_assert( basis_ptr + sf_length_8kHz <= frame_4kHz + frame_length_4kHz ); cross_corr = xcorr[ max_lag_4kHz - d ]; /* Add contribution of new sample and remove contribution from oldest sample */ normalizer += basis_ptr[ 0 ] * (double)basis_ptr[ 0 ] - basis_ptr[ sf_length_8kHz ] * (double)basis_ptr[ sf_length_8kHz ]; C[ 0 ][ d ] += (silk_float)( 2 * cross_corr / normalizer ); } /* Update target pointer */ target_ptr += sf_length_8kHz; } /* Apply short-lag bias */ for( i = max_lag_4kHz; i >= min_lag_4kHz; i-- ) { C[ 0 ][ i ] -= C[ 0 ][ i ] * i / 4096.0f; } /* Sort */ length_d_srch = 4 + 2 * complexity; silk_assert( 3 * length_d_srch <= PE_D_SRCH_LENGTH ); silk_insertion_sort_decreasing_FLP( &C[ 0 ][ min_lag_4kHz ], d_srch, max_lag_4kHz - min_lag_4kHz + 1, length_d_srch ); /* Escape if correlation is very low already here */ Cmax = C[ 0 ][ min_lag_4kHz ]; if( Cmax < 0.2f ) { silk_memset( pitch_out, 0, nb_subfr * sizeof( opus_int ) ); *LTPCorr = 0.0f; *lagIndex = 0; *contourIndex = 0; return 1; } threshold = search_thres1 * Cmax; for( i = 0; i < length_d_srch; i++ ) { /* Convert to 8 kHz indices for the sorted correlation that exceeds the threshold */ if( C[ 0 ][ min_lag_4kHz + i ] > threshold ) { d_srch[ i ] = silk_LSHIFT( d_srch[ i ] + min_lag_4kHz, 1 ); } else { length_d_srch = i; break; } } silk_assert( length_d_srch > 0 ); for( i = min_lag_8kHz - 5; i < max_lag_8kHz + 5; i++ ) { d_comp[ i ] = 0; } for( i = 0; i < length_d_srch; i++ ) { d_comp[ d_srch[ i ] ] = 1; } /* Convolution */ for( i = max_lag_8kHz + 3; i >= min_lag_8kHz; i-- ) { d_comp[ i ] += d_comp[ i - 1 ] + d_comp[ i - 2 ]; } length_d_srch = 0; for( i = min_lag_8kHz; i < max_lag_8kHz + 1; i++ ) { if( d_comp[ i + 1 ] > 0 ) { d_srch[ length_d_srch ] = i; length_d_srch++; } } /* Convolution */ for( i = max_lag_8kHz + 3; i >= min_lag_8kHz; i-- ) { d_comp[ i ] += d_comp[ i - 1 ] + d_comp[ i - 2 ] + d_comp[ i - 3 ]; } length_d_comp = 0; for( i = min_lag_8kHz; i < max_lag_8kHz + 4; i++ ) { if( d_comp[ i ] > 0 ) { d_comp[ length_d_comp ] = (opus_int16)( i - 2 ); length_d_comp++; } } /********************************************************************************** ** SECOND STAGE, operating at 8 kHz, on lag sections with high correlation *************************************************************************************/ /********************************************************************************* * Find energy of each subframe projected onto its history, for a range of delays *********************************************************************************/ silk_memset( C, 0, PE_MAX_NB_SUBFR*((PE_MAX_LAG >> 1) + 5) * sizeof(silk_float)); if( Fs_kHz == 8 ) { target_ptr = &frame[ PE_LTP_MEM_LENGTH_MS * 8 ]; } else { target_ptr = &frame_8kHz[ PE_LTP_MEM_LENGTH_MS * 8 ]; } for( k = 0; k < nb_subfr; k++ ) { energy_tmp = silk_energy_FLP( target_ptr, sf_length_8kHz ) + 1.0; for( j = 0; j < length_d_comp; j++ ) { d = d_comp[ j ]; basis_ptr = target_ptr - d; cross_corr = silk_inner_product_FLP( basis_ptr, target_ptr, sf_length_8kHz ); if( cross_corr > 0.0f ) { energy = silk_energy_FLP( basis_ptr, sf_length_8kHz ); C[ k ][ d ] = (silk_float)( 2 * cross_corr / ( energy + energy_tmp ) ); } else { C[ k ][ d ] = 0.0f; } } target_ptr += sf_length_8kHz; } /* search over lag range and lags codebook */ /* scale factor for lag codebook, as a function of center lag */ CCmax = 0.0f; /* This value doesn't matter */ CCmax_b = -1000.0f; CBimax = 0; /* To avoid returning undefined lag values */ lag = -1; /* To check if lag with strong enough correlation has been found */ if( prevLag > 0 ) { if( Fs_kHz == 12 ) { prevLag = silk_LSHIFT( prevLag, 1 ) / 3; } else if( Fs_kHz == 16 ) { prevLag = silk_RSHIFT( prevLag, 1 ); } prevLag_log2 = silk_log2( (silk_float)prevLag ); } else { prevLag_log2 = 0; } /* Set up stage 2 codebook based on number of subframes */ if( nb_subfr == PE_MAX_NB_SUBFR ) { cbk_size = PE_NB_CBKS_STAGE2_EXT; Lag_CB_ptr = &silk_CB_lags_stage2[ 0 ][ 0 ]; if( Fs_kHz == 8 && complexity > SILK_PE_MIN_COMPLEX ) { /* If input is 8 khz use a larger codebook here because it is last stage */ nb_cbk_search = PE_NB_CBKS_STAGE2_EXT; } else { nb_cbk_search = PE_NB_CBKS_STAGE2; } } else { cbk_size = PE_NB_CBKS_STAGE2_10MS; Lag_CB_ptr = &silk_CB_lags_stage2_10_ms[ 0 ][ 0 ]; nb_cbk_search = PE_NB_CBKS_STAGE2_10MS; } for( k = 0; k < length_d_srch; k++ ) { d = d_srch[ k ]; for( j = 0; j < nb_cbk_search; j++ ) { CC[j] = 0.0f; for( i = 0; i < nb_subfr; i++ ) { /* Try all codebooks */ CC[ j ] += C[ i ][ d + matrix_ptr( Lag_CB_ptr, i, j, cbk_size )]; } } /* Find best codebook */ CCmax_new = -1000.0f; CBimax_new = 0; for( i = 0; i < nb_cbk_search; i++ ) { if( CC[ i ] > CCmax_new ) { CCmax_new = CC[ i ]; CBimax_new = i; } } /* Bias towards shorter lags */ lag_log2 = silk_log2( (silk_float)d ); CCmax_new_b = CCmax_new - PE_SHORTLAG_BIAS * nb_subfr * lag_log2; /* Bias towards previous lag */ if( prevLag > 0 ) { delta_lag_log2_sqr = lag_log2 - prevLag_log2; delta_lag_log2_sqr *= delta_lag_log2_sqr; CCmax_new_b -= PE_PREVLAG_BIAS * nb_subfr * (*LTPCorr) * delta_lag_log2_sqr / ( delta_lag_log2_sqr + 0.5f ); } if( CCmax_new_b > CCmax_b && /* Find maximum biased correlation */ CCmax_new > nb_subfr * search_thres2 /* Correlation needs to be high enough to be voiced */ ) { CCmax_b = CCmax_new_b; CCmax = CCmax_new; lag = d; CBimax = CBimax_new; } } if( lag == -1 ) { /* No suitable candidate found */ silk_memset( pitch_out, 0, PE_MAX_NB_SUBFR * sizeof(opus_int) ); *LTPCorr = 0.0f; *lagIndex = 0; *contourIndex = 0; return 1; } /* Output normalized correlation */ *LTPCorr = (silk_float)( CCmax / nb_subfr ); silk_assert( *LTPCorr >= 0.0f ); if( Fs_kHz > 8 ) { /* Search in original signal */ /* Compensate for decimation */ silk_assert( lag == silk_SAT16( lag ) ); if( Fs_kHz == 12 ) { lag = silk_RSHIFT_ROUND( silk_SMULBB( lag, 3 ), 1 ); } else { /* Fs_kHz == 16 */ lag = silk_LSHIFT( lag, 1 ); } lag = silk_LIMIT_int( lag, min_lag, max_lag ); start_lag = silk_max_int( lag - 2, min_lag ); end_lag = silk_min_int( lag + 2, max_lag ); lag_new = lag; /* to avoid undefined lag */ CBimax = 0; /* to avoid undefined lag */ CCmax = -1000.0f; /* Calculate the correlations and energies needed in stage 3 */ silk_P_Ana_calc_corr_st3( cross_corr_st3, frame, start_lag, sf_length, nb_subfr, complexity, arch ); silk_P_Ana_calc_energy_st3( energies_st3, frame, start_lag, sf_length, nb_subfr, complexity ); lag_counter = 0; silk_assert( lag == silk_SAT16( lag ) ); contour_bias = PE_FLATCONTOUR_BIAS / lag; /* Set up cbk parameters according to complexity setting and frame length */ if( nb_subfr == PE_MAX_NB_SUBFR ) { nb_cbk_search = (opus_int)silk_nb_cbk_searchs_stage3[ complexity ]; cbk_size = PE_NB_CBKS_STAGE3_MAX; Lag_CB_ptr = &silk_CB_lags_stage3[ 0 ][ 0 ]; } else { nb_cbk_search = PE_NB_CBKS_STAGE3_10MS; cbk_size = PE_NB_CBKS_STAGE3_10MS; Lag_CB_ptr = &silk_CB_lags_stage3_10_ms[ 0 ][ 0 ]; } target_ptr = &frame[ PE_LTP_MEM_LENGTH_MS * Fs_kHz ]; energy_tmp = silk_energy_FLP( target_ptr, nb_subfr * sf_length ) + 1.0; for( d = start_lag; d <= end_lag; d++ ) { for( j = 0; j < nb_cbk_search; j++ ) { cross_corr = 0.0; energy = energy_tmp; for( k = 0; k < nb_subfr; k++ ) { cross_corr += cross_corr_st3[ k ][ j ][ lag_counter ]; energy += energies_st3[ k ][ j ][ lag_counter ]; } if( cross_corr > 0.0 ) { CCmax_new = (silk_float)( 2 * cross_corr / energy ); /* Reduce depending on flatness of contour */ CCmax_new *= 1.0f - contour_bias * j; } else { CCmax_new = 0.0f; } if( CCmax_new > CCmax && ( d + (opus_int)silk_CB_lags_stage3[ 0 ][ j ] ) <= max_lag ) { CCmax = CCmax_new; lag_new = d; CBimax = j; } } lag_counter++; } for( k = 0; k < nb_subfr; k++ ) { pitch_out[ k ] = lag_new + matrix_ptr( Lag_CB_ptr, k, CBimax, cbk_size ); pitch_out[ k ] = silk_LIMIT( pitch_out[ k ], min_lag, PE_MAX_LAG_MS * Fs_kHz ); } *lagIndex = (opus_int16)( lag_new - min_lag ); *contourIndex = (opus_int8)CBimax; } else { /* Fs_kHz == 8 */ /* Save Lags */ for( k = 0; k < nb_subfr; k++ ) { pitch_out[ k ] = lag + matrix_ptr( Lag_CB_ptr, k, CBimax, cbk_size ); pitch_out[ k ] = silk_LIMIT( pitch_out[ k ], min_lag_8kHz, PE_MAX_LAG_MS * 8 ); } *lagIndex = (opus_int16)( lag - min_lag_8kHz ); *contourIndex = (opus_int8)CBimax; } silk_assert( *lagIndex >= 0 ); /* return as voiced */ return 0; } /*********************************************************************** * Calculates the correlations used in stage 3 search. In order to cover * the whole lag codebook for all the searched offset lags (lag +- 2), * the following correlations are needed in each sub frame: * * sf1: lag range [-8,...,7] total 16 correlations * sf2: lag range [-4,...,4] total 9 correlations * sf3: lag range [-3,....4] total 8 correltions * sf4: lag range [-6,....8] total 15 correlations * * In total 48 correlations. The direct implementation computed in worst * case 4*12*5 = 240 correlations, but more likely around 120. ***********************************************************************/ static void silk_P_Ana_calc_corr_st3( silk_float cross_corr_st3[ PE_MAX_NB_SUBFR ][ PE_NB_CBKS_STAGE3_MAX ][ PE_NB_STAGE3_LAGS ], /* O 3 DIM correlation array */ const silk_float frame[], /* I vector to correlate */ opus_int start_lag, /* I start lag */ opus_int sf_length, /* I sub frame length */ opus_int nb_subfr, /* I number of subframes */ opus_int complexity, /* I Complexity setting */ int arch /* I Run-time architecture */ ) { const silk_float *target_ptr; opus_int i, j, k, lag_counter, lag_low, lag_high; opus_int nb_cbk_search, delta, idx, cbk_size; silk_float scratch_mem[ SCRATCH_SIZE ]; opus_val32 xcorr[ SCRATCH_SIZE ]; const opus_int8 *Lag_range_ptr, *Lag_CB_ptr; silk_assert( complexity >= SILK_PE_MIN_COMPLEX ); silk_assert( complexity <= SILK_PE_MAX_COMPLEX ); if( nb_subfr == PE_MAX_NB_SUBFR ) { Lag_range_ptr = &silk_Lag_range_stage3[ complexity ][ 0 ][ 0 ]; Lag_CB_ptr = &silk_CB_lags_stage3[ 0 ][ 0 ]; nb_cbk_search = silk_nb_cbk_searchs_stage3[ complexity ]; cbk_size = PE_NB_CBKS_STAGE3_MAX; } else { silk_assert( nb_subfr == PE_MAX_NB_SUBFR >> 1); Lag_range_ptr = &silk_Lag_range_stage3_10_ms[ 0 ][ 0 ]; Lag_CB_ptr = &silk_CB_lags_stage3_10_ms[ 0 ][ 0 ]; nb_cbk_search = PE_NB_CBKS_STAGE3_10MS; cbk_size = PE_NB_CBKS_STAGE3_10MS; } target_ptr = &frame[ silk_LSHIFT( sf_length, 2 ) ]; /* Pointer to middle of frame */ for( k = 0; k < nb_subfr; k++ ) { lag_counter = 0; /* Calculate the correlations for each subframe */ lag_low = matrix_ptr( Lag_range_ptr, k, 0, 2 ); lag_high = matrix_ptr( Lag_range_ptr, k, 1, 2 ); silk_assert(lag_high-lag_low+1 <= SCRATCH_SIZE); celt_pitch_xcorr( target_ptr, target_ptr - start_lag - lag_high, xcorr, sf_length, lag_high - lag_low + 1, arch ); for( j = lag_low; j <= lag_high; j++ ) { silk_assert( lag_counter < SCRATCH_SIZE ); scratch_mem[ lag_counter ] = xcorr[ lag_high - j ]; lag_counter++; } delta = matrix_ptr( Lag_range_ptr, k, 0, 2 ); for( i = 0; i < nb_cbk_search; i++ ) { /* Fill out the 3 dim array that stores the correlations for */ /* each code_book vector for each start lag */ idx = matrix_ptr( Lag_CB_ptr, k, i, cbk_size ) - delta; for( j = 0; j < PE_NB_STAGE3_LAGS; j++ ) { silk_assert( idx + j < SCRATCH_SIZE ); silk_assert( idx + j < lag_counter ); cross_corr_st3[ k ][ i ][ j ] = scratch_mem[ idx + j ]; } } target_ptr += sf_length; } } /********************************************************************/ /* Calculate the energies for first two subframes. The energies are */ /* calculated recursively. */ /********************************************************************/ static void silk_P_Ana_calc_energy_st3( silk_float energies_st3[ PE_MAX_NB_SUBFR ][ PE_NB_CBKS_STAGE3_MAX ][ PE_NB_STAGE3_LAGS ], /* O 3 DIM correlation array */ const silk_float frame[], /* I vector to correlate */ opus_int start_lag, /* I start lag */ opus_int sf_length, /* I sub frame length */ opus_int nb_subfr, /* I number of subframes */ opus_int complexity /* I Complexity setting */ ) { const silk_float *target_ptr, *basis_ptr; double energy; opus_int k, i, j, lag_counter; opus_int nb_cbk_search, delta, idx, cbk_size, lag_diff; silk_float scratch_mem[ SCRATCH_SIZE ]; const opus_int8 *Lag_range_ptr, *Lag_CB_ptr; silk_assert( complexity >= SILK_PE_MIN_COMPLEX ); silk_assert( complexity <= SILK_PE_MAX_COMPLEX ); if( nb_subfr == PE_MAX_NB_SUBFR ) { Lag_range_ptr = &silk_Lag_range_stage3[ complexity ][ 0 ][ 0 ]; Lag_CB_ptr = &silk_CB_lags_stage3[ 0 ][ 0 ]; nb_cbk_search = silk_nb_cbk_searchs_stage3[ complexity ]; cbk_size = PE_NB_CBKS_STAGE3_MAX; } else { silk_assert( nb_subfr == PE_MAX_NB_SUBFR >> 1); Lag_range_ptr = &silk_Lag_range_stage3_10_ms[ 0 ][ 0 ]; Lag_CB_ptr = &silk_CB_lags_stage3_10_ms[ 0 ][ 0 ]; nb_cbk_search = PE_NB_CBKS_STAGE3_10MS; cbk_size = PE_NB_CBKS_STAGE3_10MS; } target_ptr = &frame[ silk_LSHIFT( sf_length, 2 ) ]; for( k = 0; k < nb_subfr; k++ ) { lag_counter = 0; /* Calculate the energy for first lag */ basis_ptr = target_ptr - ( start_lag + matrix_ptr( Lag_range_ptr, k, 0, 2 ) ); energy = silk_energy_FLP( basis_ptr, sf_length ) + 1e-3; silk_assert( energy >= 0.0 ); scratch_mem[lag_counter] = (silk_float)energy; lag_counter++; lag_diff = ( matrix_ptr( Lag_range_ptr, k, 1, 2 ) - matrix_ptr( Lag_range_ptr, k, 0, 2 ) + 1 ); for( i = 1; i < lag_diff; i++ ) { /* remove part outside new window */ energy -= basis_ptr[sf_length - i] * (double)basis_ptr[sf_length - i]; silk_assert( energy >= 0.0 ); /* add part that comes into window */ energy += basis_ptr[ -i ] * (double)basis_ptr[ -i ]; silk_assert( energy >= 0.0 ); silk_assert( lag_counter < SCRATCH_SIZE ); scratch_mem[lag_counter] = (silk_float)energy; lag_counter++; } delta = matrix_ptr( Lag_range_ptr, k, 0, 2 ); for( i = 0; i < nb_cbk_search; i++ ) { /* Fill out the 3 dim array that stores the correlations for */ /* each code_book vector for each start lag */ idx = matrix_ptr( Lag_CB_ptr, k, i, cbk_size ) - delta; for( j = 0; j < PE_NB_STAGE3_LAGS; j++ ) { silk_assert( idx + j < SCRATCH_SIZE ); silk_assert( idx + j < lag_counter ); energies_st3[ k ][ i ][ j ] = scratch_mem[ idx + j ]; silk_assert( energies_st3[ k ][ i ][ j ] >= 0.0f ); } } target_ptr += sf_length; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/prefilter_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FLP.h" #include "tuning_parameters.h" /* * Prefilter for finding Quantizer input signal */ static OPUS_INLINE void silk_prefilt_FLP( silk_prefilter_state_FLP *P, /* I/O state */ silk_float st_res[], /* I */ silk_float xw[], /* O */ silk_float *HarmShapeFIR, /* I */ silk_float Tilt, /* I */ silk_float LF_MA_shp, /* I */ silk_float LF_AR_shp, /* I */ opus_int lag, /* I */ opus_int length /* I */ ); static void silk_warped_LPC_analysis_filter_FLP( silk_float state[], /* I/O State [order + 1] */ silk_float res[], /* O Residual signal [length] */ const silk_float coef[], /* I Coefficients [order] */ const silk_float input[], /* I Input signal [length] */ const silk_float lambda, /* I Warping factor */ const opus_int length, /* I Length of input signal */ const opus_int order /* I Filter order (even) */ ) { opus_int n, i; silk_float acc, tmp1, tmp2; /* Order must be even */ silk_assert( ( order & 1 ) == 0 ); for( n = 0; n < length; n++ ) { /* Output of lowpass section */ tmp2 = state[ 0 ] + lambda * state[ 1 ]; state[ 0 ] = input[ n ]; /* Output of allpass section */ tmp1 = state[ 1 ] + lambda * ( state[ 2 ] - tmp2 ); state[ 1 ] = tmp2; acc = coef[ 0 ] * tmp2; /* Loop over allpass sections */ for( i = 2; i < order; i += 2 ) { /* Output of allpass section */ tmp2 = state[ i ] + lambda * ( state[ i + 1 ] - tmp1 ); state[ i ] = tmp1; acc += coef[ i - 1 ] * tmp1; /* Output of allpass section */ tmp1 = state[ i + 1 ] + lambda * ( state[ i + 2 ] - tmp2 ); state[ i + 1 ] = tmp2; acc += coef[ i ] * tmp2; } state[ order ] = tmp1; acc += coef[ order - 1 ] * tmp1; res[ n ] = input[ n ] - acc; } } /* * silk_prefilter. Main prefilter function */ void silk_prefilter_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ const silk_encoder_control_FLP *psEncCtrl, /* I Encoder control FLP */ silk_float xw[], /* O Weighted signal */ const silk_float x[] /* I Speech signal */ ) { silk_prefilter_state_FLP *P = &psEnc->sPrefilt; opus_int j, k, lag; silk_float HarmShapeGain, Tilt, LF_MA_shp, LF_AR_shp; silk_float B[ 2 ]; const silk_float *AR1_shp; const silk_float *px; silk_float *pxw; silk_float HarmShapeFIR[ 3 ]; silk_float st_res[ MAX_SUB_FRAME_LENGTH + MAX_LPC_ORDER ]; /* Set up pointers */ px = x; pxw = xw; lag = P->lagPrev; for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { /* Update Variables that change per sub frame */ if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { lag = psEncCtrl->pitchL[ k ]; } /* Noise shape parameters */ HarmShapeGain = psEncCtrl->HarmShapeGain[ k ] * ( 1.0f - psEncCtrl->HarmBoost[ k ] ); HarmShapeFIR[ 0 ] = 0.25f * HarmShapeGain; HarmShapeFIR[ 1 ] = 32767.0f / 65536.0f * HarmShapeGain; HarmShapeFIR[ 2 ] = 0.25f * HarmShapeGain; Tilt = psEncCtrl->Tilt[ k ]; LF_MA_shp = psEncCtrl->LF_MA_shp[ k ]; LF_AR_shp = psEncCtrl->LF_AR_shp[ k ]; AR1_shp = &psEncCtrl->AR1[ k * MAX_SHAPE_LPC_ORDER ]; /* Short term FIR filtering */ silk_warped_LPC_analysis_filter_FLP( P->sAR_shp, st_res, AR1_shp, px, (silk_float)psEnc->sCmn.warping_Q16 / 65536.0f, psEnc->sCmn.subfr_length, psEnc->sCmn.shapingLPCOrder ); /* Reduce (mainly) low frequencies during harmonic emphasis */ B[ 0 ] = psEncCtrl->GainsPre[ k ]; B[ 1 ] = -psEncCtrl->GainsPre[ k ] * ( psEncCtrl->HarmBoost[ k ] * HarmShapeGain + INPUT_TILT + psEncCtrl->coding_quality * HIGH_RATE_INPUT_TILT ); pxw[ 0 ] = B[ 0 ] * st_res[ 0 ] + B[ 1 ] * P->sHarmHP; for( j = 1; j < psEnc->sCmn.subfr_length; j++ ) { pxw[ j ] = B[ 0 ] * st_res[ j ] + B[ 1 ] * st_res[ j - 1 ]; } P->sHarmHP = st_res[ psEnc->sCmn.subfr_length - 1 ]; silk_prefilt_FLP( P, pxw, pxw, HarmShapeFIR, Tilt, LF_MA_shp, LF_AR_shp, lag, psEnc->sCmn.subfr_length ); px += psEnc->sCmn.subfr_length; pxw += psEnc->sCmn.subfr_length; } P->lagPrev = psEncCtrl->pitchL[ psEnc->sCmn.nb_subfr - 1 ]; } /* * Prefilter for finding Quantizer input signal */ static OPUS_INLINE void silk_prefilt_FLP( silk_prefilter_state_FLP *P, /* I/O state */ silk_float st_res[], /* I */ silk_float xw[], /* O */ silk_float *HarmShapeFIR, /* I */ silk_float Tilt, /* I */ silk_float LF_MA_shp, /* I */ silk_float LF_AR_shp, /* I */ opus_int lag, /* I */ opus_int length /* I */ ) { opus_int i; opus_int idx, LTP_shp_buf_idx; silk_float n_Tilt, n_LF, n_LTP; silk_float sLF_AR_shp, sLF_MA_shp; silk_float *LTP_shp_buf; /* To speed up use temp variables instead of using the struct */ LTP_shp_buf = P->sLTP_shp; LTP_shp_buf_idx = P->sLTP_shp_buf_idx; sLF_AR_shp = P->sLF_AR_shp; sLF_MA_shp = P->sLF_MA_shp; for( i = 0; i < length; i++ ) { if( lag > 0 ) { silk_assert( HARM_SHAPE_FIR_TAPS == 3 ); idx = lag + LTP_shp_buf_idx; n_LTP = LTP_shp_buf[ ( idx - HARM_SHAPE_FIR_TAPS / 2 - 1) & LTP_MASK ] * HarmShapeFIR[ 0 ]; n_LTP += LTP_shp_buf[ ( idx - HARM_SHAPE_FIR_TAPS / 2 ) & LTP_MASK ] * HarmShapeFIR[ 1 ]; n_LTP += LTP_shp_buf[ ( idx - HARM_SHAPE_FIR_TAPS / 2 + 1) & LTP_MASK ] * HarmShapeFIR[ 2 ]; } else { n_LTP = 0; } n_Tilt = sLF_AR_shp * Tilt; n_LF = sLF_AR_shp * LF_AR_shp + sLF_MA_shp * LF_MA_shp; sLF_AR_shp = st_res[ i ] - n_Tilt; sLF_MA_shp = sLF_AR_shp - n_LF; LTP_shp_buf_idx = ( LTP_shp_buf_idx - 1 ) & LTP_MASK; LTP_shp_buf[ LTP_shp_buf_idx ] = sLF_MA_shp; xw[ i ] = sLF_MA_shp - n_LTP; } /* Copy temp variable back to state */ P->sLF_AR_shp = sLF_AR_shp; P->sLF_MA_shp = sLF_MA_shp; P->sLTP_shp_buf_idx = LTP_shp_buf_idx; } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/process_gains_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FLP.h" #include "tuning_parameters.h" /* Processing of gains */ void silk_process_gains_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */ opus_int condCoding /* I The type of conditional coding to use */ ) { silk_shape_state_FLP *psShapeSt = &psEnc->sShape; opus_int k; opus_int32 pGains_Q16[ MAX_NB_SUBFR ]; silk_float s, InvMaxSqrVal, gain, quant_offset; /* Gain reduction when LTP coding gain is high */ if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { s = 1.0f - 0.5f * silk_sigmoid( 0.25f * ( psEncCtrl->LTPredCodGain - 12.0f ) ); for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { psEncCtrl->Gains[ k ] *= s; } } /* Limit the quantized signal */ InvMaxSqrVal = ( silk_float )( pow( 2.0f, 0.33f * ( 21.0f - psEnc->sCmn.SNR_dB_Q7 * ( 1 / 128.0f ) ) ) / psEnc->sCmn.subfr_length ); for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { /* Soft limit on ratio residual energy and squared gains */ gain = psEncCtrl->Gains[ k ]; gain = ( silk_float )sqrt( gain * gain + psEncCtrl->ResNrg[ k ] * InvMaxSqrVal ); psEncCtrl->Gains[ k ] = silk_min_float( gain, 32767.0f ); } /* Prepare gains for noise shaping quantization */ for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { pGains_Q16[ k ] = (opus_int32)( psEncCtrl->Gains[ k ] * 65536.0f ); } /* Save unquantized gains and gain Index */ silk_memcpy( psEncCtrl->GainsUnq_Q16, pGains_Q16, psEnc->sCmn.nb_subfr * sizeof( opus_int32 ) ); psEncCtrl->lastGainIndexPrev = psShapeSt->LastGainIndex; /* Quantize gains */ silk_gains_quant( psEnc->sCmn.indices.GainsIndices, pGains_Q16, &psShapeSt->LastGainIndex, condCoding == CODE_CONDITIONALLY, psEnc->sCmn.nb_subfr ); /* Overwrite unquantized gains with quantized gains and convert back to Q0 from Q16 */ for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) { psEncCtrl->Gains[ k ] = pGains_Q16[ k ] / 65536.0f; } /* Set quantizer offset for voiced signals. Larger offset when LTP coding gain is low or tilt is high (ie low-pass) */ if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) { if( psEncCtrl->LTPredCodGain + psEnc->sCmn.input_tilt_Q15 * ( 1.0f / 32768.0f ) > 1.0f ) { psEnc->sCmn.indices.quantOffsetType = 0; } else { psEnc->sCmn.indices.quantOffsetType = 1; } } /* Quantizer boundary adjustment */ quant_offset = silk_Quantization_Offsets_Q10[ psEnc->sCmn.indices.signalType >> 1 ][ psEnc->sCmn.indices.quantOffsetType ] / 1024.0f; psEncCtrl->Lambda = LAMBDA_OFFSET + LAMBDA_DELAYED_DECISIONS * psEnc->sCmn.nStatesDelayedDecision + LAMBDA_SPEECH_ACT * psEnc->sCmn.speech_activity_Q8 * ( 1.0f / 256.0f ) + LAMBDA_INPUT_QUALITY * psEncCtrl->input_quality + LAMBDA_CODING_QUALITY * psEncCtrl->coding_quality + LAMBDA_QUANT_OFFSET * quant_offset; silk_assert( psEncCtrl->Lambda > 0.0f ); silk_assert( psEncCtrl->Lambda < 2.0f ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/regularize_correlations_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FLP.h" /* Add noise to matrix diagonal */ void silk_regularize_correlations_FLP( silk_float *XX, /* I/O Correlation matrices */ silk_float *xx, /* I/O Correlation values */ const silk_float noise, /* I Noise energy to add */ const opus_int D /* I Dimension of XX */ ) { opus_int i; for( i = 0; i < D; i++ ) { matrix_ptr( &XX[ 0 ], i, i, D ) += noise; } xx[ 0 ] += noise; } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/residual_energy_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FLP.h" #define MAX_ITERATIONS_RESIDUAL_NRG 10 #define REGULARIZATION_FACTOR 1e-8f /* Residual energy: nrg = wxx - 2 * wXx * c + c' * wXX * c */ silk_float silk_residual_energy_covar_FLP( /* O Weighted residual energy */ const silk_float *c, /* I Filter coefficients */ silk_float *wXX, /* I/O Weighted correlation matrix, reg. out */ const silk_float *wXx, /* I Weighted correlation vector */ const silk_float wxx, /* I Weighted correlation value */ const opus_int D /* I Dimension */ ) { opus_int i, j, k; silk_float tmp, nrg = 0.0f, regularization; /* Safety checks */ silk_assert( D >= 0 ); regularization = REGULARIZATION_FACTOR * ( wXX[ 0 ] + wXX[ D * D - 1 ] ); for( k = 0; k < MAX_ITERATIONS_RESIDUAL_NRG; k++ ) { nrg = wxx; tmp = 0.0f; for( i = 0; i < D; i++ ) { tmp += wXx[ i ] * c[ i ]; } nrg -= 2.0f * tmp; /* compute c' * wXX * c, assuming wXX is symmetric */ for( i = 0; i < D; i++ ) { tmp = 0.0f; for( j = i + 1; j < D; j++ ) { tmp += matrix_c_ptr( wXX, i, j, D ) * c[ j ]; } nrg += c[ i ] * ( 2.0f * tmp + matrix_c_ptr( wXX, i, i, D ) * c[ i ] ); } if( nrg > 0 ) { break; } else { /* Add white noise */ for( i = 0; i < D; i++ ) { matrix_c_ptr( wXX, i, i, D ) += regularization; } /* Increase noise for next run */ regularization *= 2.0f; } } if( k == MAX_ITERATIONS_RESIDUAL_NRG ) { silk_assert( nrg == 0 ); nrg = 1.0f; } return nrg; } /* Calculates residual energies of input subframes where all subframes have LPC_order */ /* of preceding samples */ void silk_residual_energy_FLP( silk_float nrgs[ MAX_NB_SUBFR ], /* O Residual energy per subframe */ const silk_float x[], /* I Input signal */ silk_float a[ 2 ][ MAX_LPC_ORDER ], /* I AR coefs for each frame half */ const silk_float gains[], /* I Quantization gains */ const opus_int subfr_length, /* I Subframe length */ const opus_int nb_subfr, /* I number of subframes */ const opus_int LPC_order /* I LPC order */ ) { opus_int shift; silk_float *LPC_res_ptr, LPC_res[ ( MAX_FRAME_LENGTH + MAX_NB_SUBFR * MAX_LPC_ORDER ) / 2 ]; LPC_res_ptr = LPC_res + LPC_order; shift = LPC_order + subfr_length; /* Filter input to create the LPC residual for each frame half, and measure subframe energies */ silk_LPC_analysis_filter_FLP( LPC_res, a[ 0 ], x + 0 * shift, 2 * shift, LPC_order ); nrgs[ 0 ] = ( silk_float )( gains[ 0 ] * gains[ 0 ] * silk_energy_FLP( LPC_res_ptr + 0 * shift, subfr_length ) ); nrgs[ 1 ] = ( silk_float )( gains[ 1 ] * gains[ 1 ] * silk_energy_FLP( LPC_res_ptr + 1 * shift, subfr_length ) ); if( nb_subfr == MAX_NB_SUBFR ) { silk_LPC_analysis_filter_FLP( LPC_res, a[ 1 ], x + 2 * shift, 2 * shift, LPC_order ); nrgs[ 2 ] = ( silk_float )( gains[ 2 ] * gains[ 2 ] * silk_energy_FLP( LPC_res_ptr + 0 * shift, subfr_length ) ); nrgs[ 3 ] = ( silk_float )( gains[ 3 ] * gains[ 3 ] * silk_energy_FLP( LPC_res_ptr + 1 * shift, subfr_length ) ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/scale_copy_vector_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FLP.h" /* copy and multiply a vector by a constant */ void silk_scale_copy_vector_FLP( silk_float *data_out, const silk_float *data_in, silk_float gain, opus_int dataSize ) { opus_int i, dataSize4; /* 4x unrolled loop */ dataSize4 = dataSize & 0xFFFC; for( i = 0; i < dataSize4; i += 4 ) { data_out[ i + 0 ] = gain * data_in[ i + 0 ]; data_out[ i + 1 ] = gain * data_in[ i + 1 ]; data_out[ i + 2 ] = gain * data_in[ i + 2 ]; data_out[ i + 3 ] = gain * data_in[ i + 3 ]; } /* any remaining elements */ for( ; i < dataSize; i++ ) { data_out[ i ] = gain * data_in[ i ]; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/scale_vector_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FLP.h" /* multiply a vector by a constant */ void silk_scale_vector_FLP( silk_float *data1, silk_float gain, opus_int dataSize ) { opus_int i, dataSize4; /* 4x unrolled loop */ dataSize4 = dataSize & 0xFFFC; for( i = 0; i < dataSize4; i += 4 ) { data1[ i + 0 ] *= gain; data1[ i + 1 ] *= gain; data1[ i + 2 ] *= gain; data1[ i + 3 ] *= gain; } /* any remaining elements */ for( ; i < dataSize; i++ ) { data1[ i ] *= gain; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/schur_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FLP.h" silk_float silk_schur_FLP( /* O returns residual energy */ silk_float refl_coef[], /* O reflection coefficients (length order) */ const silk_float auto_corr[], /* I autocorrelation sequence (length order+1) */ opus_int order /* I order */ ) { opus_int k, n; silk_float C[ SILK_MAX_ORDER_LPC + 1 ][ 2 ]; silk_float Ctmp1, Ctmp2, rc_tmp; silk_assert( order==6||order==8||order==10||order==12||order==14||order==16 ); /* Copy correlations */ for( k = 0; k < order+1; k++ ) { C[ k ][ 0 ] = C[ k ][ 1 ] = auto_corr[ k ]; } for( k = 0; k < order; k++ ) { /* Get reflection coefficient */ rc_tmp = -C[ k + 1 ][ 0 ] / silk_max_float( C[ 0 ][ 1 ], 1e-9f ); /* Save the output */ refl_coef[ k ] = rc_tmp; /* Update correlations */ for( n = 0; n < order - k; n++ ) { Ctmp1 = C[ n + k + 1 ][ 0 ]; Ctmp2 = C[ n ][ 1 ]; C[ n + k + 1 ][ 0 ] = Ctmp1 + Ctmp2 * rc_tmp; C[ n ][ 1 ] = Ctmp2 + Ctmp1 * rc_tmp; } } /* Return residual energy */ return C[ 0 ][ 1 ]; } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/solve_LS_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FLP.h" #include "tuning_parameters.h" /********************************************************************** * LDL Factorisation. Finds the upper triangular matrix L and the diagonal * Matrix D (only the diagonal elements returned in a vector)such that * the symmetric matric A is given by A = L*D*L'. **********************************************************************/ static OPUS_INLINE void silk_LDL_FLP( silk_float *A, /* I/O Pointer to Symetric Square Matrix */ opus_int M, /* I Size of Matrix */ silk_float *L, /* I/O Pointer to Square Upper triangular Matrix */ silk_float *Dinv /* I/O Pointer to vector holding the inverse diagonal elements of D */ ); /********************************************************************** * Function to solve linear equation Ax = b, when A is a MxM lower * triangular matrix, with ones on the diagonal. **********************************************************************/ static OPUS_INLINE void silk_SolveWithLowerTriangularWdiagOnes_FLP( const silk_float *L, /* I Pointer to Lower Triangular Matrix */ opus_int M, /* I Dim of Matrix equation */ const silk_float *b, /* I b Vector */ silk_float *x /* O x Vector */ ); /********************************************************************** * Function to solve linear equation (A^T)x = b, when A is a MxM lower * triangular, with ones on the diagonal. (ie then A^T is upper triangular) **********************************************************************/ static OPUS_INLINE void silk_SolveWithUpperTriangularFromLowerWdiagOnes_FLP( const silk_float *L, /* I Pointer to Lower Triangular Matrix */ opus_int M, /* I Dim of Matrix equation */ const silk_float *b, /* I b Vector */ silk_float *x /* O x Vector */ ); /********************************************************************** * Function to solve linear equation Ax = b, when A is a MxM * symmetric square matrix - using LDL factorisation **********************************************************************/ void silk_solve_LDL_FLP( silk_float *A, /* I/O Symmetric square matrix, out: reg. */ const opus_int M, /* I Size of matrix */ const silk_float *b, /* I Pointer to b vector */ silk_float *x /* O Pointer to x solution vector */ ) { opus_int i; silk_float L[ MAX_MATRIX_SIZE ][ MAX_MATRIX_SIZE ]; silk_float T[ MAX_MATRIX_SIZE ]; silk_float Dinv[ MAX_MATRIX_SIZE ]; /* inverse diagonal elements of D*/ silk_assert( M <= MAX_MATRIX_SIZE ); /*************************************************** Factorize A by LDL such that A = L*D*(L^T), where L is lower triangular with ones on diagonal ****************************************************/ silk_LDL_FLP( A, M, &L[ 0 ][ 0 ], Dinv ); /**************************************************** * substitute D*(L^T) = T. ie: L*D*(L^T)*x = b => L*T = b <=> T = inv(L)*b ******************************************************/ silk_SolveWithLowerTriangularWdiagOnes_FLP( &L[ 0 ][ 0 ], M, b, T ); /**************************************************** D*(L^T)*x = T <=> (L^T)*x = inv(D)*T, because D is diagonal just multiply with 1/d_i ****************************************************/ for( i = 0; i < M; i++ ) { T[ i ] = T[ i ] * Dinv[ i ]; } /**************************************************** x = inv(L') * inv(D) * T *****************************************************/ silk_SolveWithUpperTriangularFromLowerWdiagOnes_FLP( &L[ 0 ][ 0 ], M, T, x ); } static OPUS_INLINE void silk_SolveWithUpperTriangularFromLowerWdiagOnes_FLP( const silk_float *L, /* I Pointer to Lower Triangular Matrix */ opus_int M, /* I Dim of Matrix equation */ const silk_float *b, /* I b Vector */ silk_float *x /* O x Vector */ ) { opus_int i, j; silk_float temp; const silk_float *ptr1; for( i = M - 1; i >= 0; i-- ) { ptr1 = matrix_adr( L, 0, i, M ); temp = 0; for( j = M - 1; j > i ; j-- ) { temp += ptr1[ j * M ] * x[ j ]; } temp = b[ i ] - temp; x[ i ] = temp; } } static OPUS_INLINE void silk_SolveWithLowerTriangularWdiagOnes_FLP( const silk_float *L, /* I Pointer to Lower Triangular Matrix */ opus_int M, /* I Dim of Matrix equation */ const silk_float *b, /* I b Vector */ silk_float *x /* O x Vector */ ) { opus_int i, j; silk_float temp; const silk_float *ptr1; for( i = 0; i < M; i++ ) { ptr1 = matrix_adr( L, i, 0, M ); temp = 0; for( j = 0; j < i; j++ ) { temp += ptr1[ j ] * x[ j ]; } temp = b[ i ] - temp; x[ i ] = temp; } } static OPUS_INLINE void silk_LDL_FLP( silk_float *A, /* I/O Pointer to Symetric Square Matrix */ opus_int M, /* I Size of Matrix */ silk_float *L, /* I/O Pointer to Square Upper triangular Matrix */ silk_float *Dinv /* I/O Pointer to vector holding the inverse diagonal elements of D */ ) { opus_int i, j, k, loop_count, err = 1; silk_float *ptr1, *ptr2; double temp, diag_min_value; silk_float v[ MAX_MATRIX_SIZE ], D[ MAX_MATRIX_SIZE ]; /* temp arrays*/ silk_assert( M <= MAX_MATRIX_SIZE ); diag_min_value = FIND_LTP_COND_FAC * 0.5f * ( A[ 0 ] + A[ M * M - 1 ] ); for( loop_count = 0; loop_count < M && err == 1; loop_count++ ) { err = 0; for( j = 0; j < M; j++ ) { ptr1 = matrix_adr( L, j, 0, M ); temp = matrix_ptr( A, j, j, M ); /* element in row j column j*/ for( i = 0; i < j; i++ ) { v[ i ] = ptr1[ i ] * D[ i ]; temp -= ptr1[ i ] * v[ i ]; } if( temp < diag_min_value ) { /* Badly conditioned matrix: add white noise and run again */ temp = ( loop_count + 1 ) * diag_min_value - temp; for( i = 0; i < M; i++ ) { matrix_ptr( A, i, i, M ) += ( silk_float )temp; } err = 1; break; } D[ j ] = ( silk_float )temp; Dinv[ j ] = ( silk_float )( 1.0f / temp ); matrix_ptr( L, j, j, M ) = 1.0f; ptr1 = matrix_adr( A, j, 0, M ); ptr2 = matrix_adr( L, j + 1, 0, M); for( i = j + 1; i < M; i++ ) { temp = 0.0; for( k = 0; k < j; k++ ) { temp += ptr2[ k ] * v[ k ]; } matrix_ptr( L, i, j, M ) = ( silk_float )( ( ptr1[ i ] - temp ) * Dinv[ j ] ); ptr2 += M; /* go to next column*/ } } } silk_assert( err == 0 ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/sort_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* Insertion sort (fast for already almost sorted arrays): */ /* Best case: O(n) for an already sorted array */ /* Worst case: O(n^2) for an inversely sorted array */ #include "typedef.h" #include "SigProc_FLP.h" void silk_insertion_sort_decreasing_FLP( silk_float *a, /* I/O Unsorted / Sorted vector */ opus_int *idx, /* O Index vector for the sorted elements */ const opus_int L, /* I Vector length */ const opus_int K /* I Number of correctly sorted positions */ ) { silk_float value; opus_int i, j; /* Safety checks */ silk_assert( K > 0 ); silk_assert( L > 0 ); silk_assert( L >= K ); /* Write start indices in index vector */ for( i = 0; i < K; i++ ) { idx[ i ] = i; } /* Sort vector elements by value, decreasing order */ for( i = 1; i < K; i++ ) { value = a[ i ]; for( j = i - 1; ( j >= 0 ) && ( value > a[ j ] ); j-- ) { a[ j + 1 ] = a[ j ]; /* Shift value */ idx[ j + 1 ] = idx[ j ]; /* Shift index */ } a[ j + 1 ] = value; /* Write value */ idx[ j + 1 ] = i; /* Write index */ } /* If less than L values are asked check the remaining values, */ /* but only spend CPU to ensure that the K first values are correct */ for( i = K; i < L; i++ ) { value = a[ i ]; if( value > a[ K - 1 ] ) { for( j = K - 2; ( j >= 0 ) && ( value > a[ j ] ); j-- ) { a[ j + 1 ] = a[ j ]; /* Shift value */ idx[ j + 1 ] = idx[ j ]; /* Shift index */ } a[ j + 1 ] = value; /* Write value */ idx[ j + 1 ] = i; /* Write index */ } } } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/structs_FLP.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_STRUCTS_FLP_H #define SILK_STRUCTS_FLP_H #include "typedef.h" #include "main.h" #include "structs.h" #ifdef __cplusplus extern "C" { #endif /********************************/ /* Noise shaping analysis state */ /********************************/ typedef struct { opus_int8 LastGainIndex; silk_float HarmBoost_smth; silk_float HarmShapeGain_smth; silk_float Tilt_smth; } silk_shape_state_FLP; /********************************/ /* Prefilter state */ /********************************/ typedef struct { silk_float sLTP_shp[ LTP_BUF_LENGTH ]; silk_float sAR_shp[ MAX_SHAPE_LPC_ORDER + 1 ]; opus_int sLTP_shp_buf_idx; silk_float sLF_AR_shp; silk_float sLF_MA_shp; silk_float sHarmHP; opus_int32 rand_seed; opus_int lagPrev; } silk_prefilter_state_FLP; /********************************/ /* Encoder state FLP */ /********************************/ typedef struct { silk_encoder_state sCmn; /* Common struct, shared with fixed-point code */ silk_shape_state_FLP sShape; /* Noise shaping state */ silk_prefilter_state_FLP sPrefilt; /* Prefilter State */ /* Buffer for find pitch and noise shape analysis */ silk_float x_buf[ 2 * MAX_FRAME_LENGTH + LA_SHAPE_MAX ];/* Buffer for find pitch and noise shape analysis */ silk_float LTPCorr; /* Normalized correlation from pitch lag estimator */ } silk_encoder_state_FLP; /************************/ /* Encoder control FLP */ /************************/ typedef struct { /* Prediction and coding parameters */ silk_float Gains[ MAX_NB_SUBFR ]; silk_float PredCoef[ 2 ][ MAX_LPC_ORDER ]; /* holds interpolated and final coefficients */ silk_float LTPCoef[LTP_ORDER * MAX_NB_SUBFR]; silk_float LTP_scale; opus_int pitchL[ MAX_NB_SUBFR ]; /* Noise shaping parameters */ silk_float AR1[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ]; silk_float AR2[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ]; silk_float LF_MA_shp[ MAX_NB_SUBFR ]; silk_float LF_AR_shp[ MAX_NB_SUBFR ]; silk_float GainsPre[ MAX_NB_SUBFR ]; silk_float HarmBoost[ MAX_NB_SUBFR ]; silk_float Tilt[ MAX_NB_SUBFR ]; silk_float HarmShapeGain[ MAX_NB_SUBFR ]; silk_float Lambda; silk_float input_quality; silk_float coding_quality; /* Measures */ silk_float sparseness; silk_float predGain; silk_float LTPredCodGain; silk_float ResNrg[ MAX_NB_SUBFR ]; /* Residual energy per subframe */ /* Parameters for CBR mode */ opus_int32 GainsUnq_Q16[ MAX_NB_SUBFR ]; opus_int8 lastGainIndexPrev; } silk_encoder_control_FLP; /************************/ /* Encoder Super Struct */ /************************/ typedef struct { silk_encoder_state_FLP state_Fxx[ ENCODER_NUM_CHANNELS ]; stereo_enc_state sStereo; opus_int32 nBitsUsedLBRR; opus_int32 nBitsExceeded; opus_int nChannelsAPI; opus_int nChannelsInternal; opus_int nPrevChannelsInternal; opus_int timeSinceSwitchAllowed_ms; opus_int allowBandwidthSwitch; opus_int prev_decode_only_middle; } silk_encoder; #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/float/warped_autocorrelation_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FLP.h" /* Autocorrelations for a warped frequency axis */ void silk_warped_autocorrelation_FLP( silk_float *corr, /* O Result [order + 1] */ const silk_float *input, /* I Input data to correlate */ const silk_float warping, /* I Warping coefficient */ const opus_int length, /* I Length of input */ const opus_int order /* I Correlation order (even) */ ) { opus_int n, i; double tmp1, tmp2; double state[ MAX_SHAPE_LPC_ORDER + 1 ] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; double C[ MAX_SHAPE_LPC_ORDER + 1 ] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; /* Order must be even */ silk_assert( ( order & 1 ) == 0 ); /* Loop over samples */ for( n = 0; n < length; n++ ) { tmp1 = input[ n ]; /* Loop over allpass sections */ for( i = 0; i < order; i += 2 ) { /* Output of allpass section */ tmp2 = state[ i ] + warping * ( state[ i + 1 ] - tmp1 ); state[ i ] = tmp1; C[ i ] += state[ 0 ] * tmp1; /* Output of allpass section */ tmp1 = state[ i + 1 ] + warping * ( state[ i + 2 ] - tmp2 ); state[ i + 1 ] = tmp2; C[ i + 1 ] += state[ 0 ] * tmp2; } state[ order ] = tmp1; C[ order ] += state[ 0 ] * tmp1; } /* Copy correlations in silk_float output format */ for( i = 0; i < order + 1; i++ ) { corr[ i ] = ( silk_float )C[ i ]; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/float/wrappers_FLP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main_FLP.h" /* Wrappers. Calls flp / fix code */ /* Convert AR filter coefficients to NLSF parameters */ void silk_A2NLSF_FLP( opus_int16 *NLSF_Q15, /* O NLSF vector [ LPC_order ] */ const silk_float *pAR, /* I LPC coefficients [ LPC_order ] */ const opus_int LPC_order /* I LPC order */ ) { opus_int i; opus_int32 a_fix_Q16[ MAX_LPC_ORDER ]; for( i = 0; i < LPC_order; i++ ) { a_fix_Q16[ i ] = silk_float2int( pAR[ i ] * 65536.0f ); } silk_A2NLSF( NLSF_Q15, a_fix_Q16, LPC_order ); } /* Convert LSF parameters to AR prediction filter coefficients */ void silk_NLSF2A_FLP( silk_float *pAR, /* O LPC coefficients [ LPC_order ] */ const opus_int16 *NLSF_Q15, /* I NLSF vector [ LPC_order ] */ const opus_int LPC_order /* I LPC order */ ) { opus_int i; opus_int16 a_fix_Q12[ MAX_LPC_ORDER ]; silk_NLSF2A( a_fix_Q12, NLSF_Q15, LPC_order ); for( i = 0; i < LPC_order; i++ ) { pAR[ i ] = ( silk_float )a_fix_Q12[ i ] * ( 1.0f / 4096.0f ); } } /******************************************/ /* Floating-point NLSF processing wrapper */ /******************************************/ void silk_process_NLSFs_FLP( silk_encoder_state *psEncC, /* I/O Encoder state */ silk_float PredCoef[ 2 ][ MAX_LPC_ORDER ], /* O Prediction coefficients */ opus_int16 NLSF_Q15[ MAX_LPC_ORDER ], /* I/O Normalized LSFs (quant out) (0 - (2^15-1)) */ const opus_int16 prev_NLSF_Q15[ MAX_LPC_ORDER ] /* I Previous Normalized LSFs (0 - (2^15-1)) */ ) { opus_int i, j; opus_int16 PredCoef_Q12[ 2 ][ MAX_LPC_ORDER ]; silk_process_NLSFs( psEncC, PredCoef_Q12, NLSF_Q15, prev_NLSF_Q15); for( j = 0; j < 2; j++ ) { for( i = 0; i < psEncC->predictLPCOrder; i++ ) { PredCoef[ j ][ i ] = ( silk_float )PredCoef_Q12[ j ][ i ] * ( 1.0f / 4096.0f ); } } } /****************************************/ /* Floating-point Silk NSQ wrapper */ /****************************************/ void silk_NSQ_wrapper_FLP( silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */ SideInfoIndices *psIndices, /* I/O Quantization indices */ silk_nsq_state *psNSQ, /* I/O Noise Shaping Quantzation state */ opus_int8 pulses[], /* O Quantized pulse signal */ const silk_float x[] /* I Prefiltered input signal */ ) { opus_int i, j; opus_int32 x_Q3[ MAX_FRAME_LENGTH ]; opus_int32 Gains_Q16[ MAX_NB_SUBFR ]; silk_DWORD_ALIGN opus_int16 PredCoef_Q12[ 2 ][ MAX_LPC_ORDER ]; opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ]; opus_int LTP_scale_Q14; /* Noise shaping parameters */ opus_int16 AR2_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ]; opus_int32 LF_shp_Q14[ MAX_NB_SUBFR ]; /* Packs two int16 coefficients per int32 value */ opus_int Lambda_Q10; opus_int Tilt_Q14[ MAX_NB_SUBFR ]; opus_int HarmShapeGain_Q14[ MAX_NB_SUBFR ]; /* Convert control struct to fix control struct */ /* Noise shape parameters */ for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) { for( j = 0; j < psEnc->sCmn.shapingLPCOrder; j++ ) { AR2_Q13[ i * MAX_SHAPE_LPC_ORDER + j ] = silk_float2int( psEncCtrl->AR2[ i * MAX_SHAPE_LPC_ORDER + j ] * 8192.0f ); } } for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) { LF_shp_Q14[ i ] = silk_LSHIFT32( silk_float2int( psEncCtrl->LF_AR_shp[ i ] * 16384.0f ), 16 ) | (opus_uint16)silk_float2int( psEncCtrl->LF_MA_shp[ i ] * 16384.0f ); Tilt_Q14[ i ] = (opus_int)silk_float2int( psEncCtrl->Tilt[ i ] * 16384.0f ); HarmShapeGain_Q14[ i ] = (opus_int)silk_float2int( psEncCtrl->HarmShapeGain[ i ] * 16384.0f ); } Lambda_Q10 = ( opus_int )silk_float2int( psEncCtrl->Lambda * 1024.0f ); /* prediction and coding parameters */ for( i = 0; i < psEnc->sCmn.nb_subfr * LTP_ORDER; i++ ) { LTPCoef_Q14[ i ] = (opus_int16)silk_float2int( psEncCtrl->LTPCoef[ i ] * 16384.0f ); } for( j = 0; j < 2; j++ ) { for( i = 0; i < psEnc->sCmn.predictLPCOrder; i++ ) { PredCoef_Q12[ j ][ i ] = (opus_int16)silk_float2int( psEncCtrl->PredCoef[ j ][ i ] * 4096.0f ); } } for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) { Gains_Q16[ i ] = silk_float2int( psEncCtrl->Gains[ i ] * 65536.0f ); silk_assert( Gains_Q16[ i ] > 0 ); } if( psIndices->signalType == TYPE_VOICED ) { LTP_scale_Q14 = silk_LTPScales_table_Q14[ psIndices->LTP_scaleIndex ]; } else { LTP_scale_Q14 = 0; } /* Convert input to fix */ for( i = 0; i < psEnc->sCmn.frame_length; i++ ) { x_Q3[ i ] = silk_float2int( 8.0f * x[ i ] ); } /* Call NSQ */ if( psEnc->sCmn.nStatesDelayedDecision > 1 || psEnc->sCmn.warping_Q16 > 0 ) { silk_NSQ_del_dec( &psEnc->sCmn, psNSQ, psIndices, x_Q3, pulses, PredCoef_Q12[ 0 ], LTPCoef_Q14, AR2_Q13, HarmShapeGain_Q14, Tilt_Q14, LF_shp_Q14, Gains_Q16, psEncCtrl->pitchL, Lambda_Q10, LTP_scale_Q14, psEnc->sCmn.arch ); } else { silk_NSQ( &psEnc->sCmn, psNSQ, psIndices, x_Q3, pulses, PredCoef_Q12[ 0 ], LTPCoef_Q14, AR2_Q13, HarmShapeGain_Q14, Tilt_Q14, LF_shp_Q14, Gains_Q16, psEncCtrl->pitchL, Lambda_Q10, LTP_scale_Q14, psEnc->sCmn.arch ); } } /***********************************************/ /* Floating-point Silk LTP quantiation wrapper */ /***********************************************/ void silk_quant_LTP_gains_FLP( silk_float B[ MAX_NB_SUBFR * LTP_ORDER ], /* I/O (Un-)quantized LTP gains */ opus_int8 cbk_index[ MAX_NB_SUBFR ], /* O Codebook index */ opus_int8 *periodicity_index, /* O Periodicity index */ opus_int32 *sum_log_gain_Q7, /* I/O Cumulative max prediction gain */ const silk_float W[ MAX_NB_SUBFR * LTP_ORDER * LTP_ORDER ], /* I Error weights */ const opus_int mu_Q10, /* I Mu value (R/D tradeoff) */ const opus_int lowComplexity, /* I Flag for low complexity */ const opus_int nb_subfr, /* I number of subframes */ int arch /* I Run-time architecture */ ) { opus_int i; opus_int16 B_Q14[ MAX_NB_SUBFR * LTP_ORDER ]; opus_int32 W_Q18[ MAX_NB_SUBFR*LTP_ORDER*LTP_ORDER ]; for( i = 0; i < nb_subfr * LTP_ORDER; i++ ) { B_Q14[ i ] = (opus_int16)silk_float2int( B[ i ] * 16384.0f ); } for( i = 0; i < nb_subfr * LTP_ORDER * LTP_ORDER; i++ ) { W_Q18[ i ] = (opus_int32)silk_float2int( W[ i ] * 262144.0f ); } silk_quant_LTP_gains( B_Q14, cbk_index, periodicity_index, sum_log_gain_Q7, W_Q18, mu_Q10, lowComplexity, nb_subfr, arch ); for( i = 0; i < nb_subfr * LTP_ORDER; i++ ) { B[ i ] = (silk_float)B_Q14[ i ] * ( 1.0f / 16384.0f ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/gain_quant.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #define OFFSET ( ( MIN_QGAIN_DB * 128 ) / 6 + 16 * 128 ) #define SCALE_Q16 ( ( 65536 * ( N_LEVELS_QGAIN - 1 ) ) / ( ( ( MAX_QGAIN_DB - MIN_QGAIN_DB ) * 128 ) / 6 ) ) #define INV_SCALE_Q16 ( ( 65536 * ( ( ( MAX_QGAIN_DB - MIN_QGAIN_DB ) * 128 ) / 6 ) ) / ( N_LEVELS_QGAIN - 1 ) ) /* Gain scalar quantization with hysteresis, uniform on log scale */ void silk_gains_quant( opus_int8 ind[ MAX_NB_SUBFR ], /* O gain indices */ opus_int32 gain_Q16[ MAX_NB_SUBFR ], /* I/O gains (quantized out) */ opus_int8 *prev_ind, /* I/O last index in previous frame */ const opus_int conditional, /* I first gain is delta coded if 1 */ const opus_int nb_subfr /* I number of subframes */ ) { opus_int k, double_step_size_threshold; for( k = 0; k < nb_subfr; k++ ) { /* Convert to log scale, scale, floor() */ ind[ k ] = silk_SMULWB( SCALE_Q16, silk_lin2log( gain_Q16[ k ] ) - OFFSET ); /* Round towards previous quantized gain (hysteresis) */ if( ind[ k ] < *prev_ind ) { ind[ k ]++; } ind[ k ] = silk_LIMIT_int( ind[ k ], 0, N_LEVELS_QGAIN - 1 ); /* Compute delta indices and limit */ if( k == 0 && conditional == 0 ) { /* Full index */ ind[ k ] = silk_LIMIT_int( ind[ k ], *prev_ind + MIN_DELTA_GAIN_QUANT, N_LEVELS_QGAIN - 1 ); *prev_ind = ind[ k ]; } else { /* Delta index */ ind[ k ] = ind[ k ] - *prev_ind; /* Double the quantization step size for large gain increases, so that the max gain level can be reached */ double_step_size_threshold = 2 * MAX_DELTA_GAIN_QUANT - N_LEVELS_QGAIN + *prev_ind; if( ind[ k ] > double_step_size_threshold ) { ind[ k ] = double_step_size_threshold + silk_RSHIFT( ind[ k ] - double_step_size_threshold + 1, 1 ); } ind[ k ] = silk_LIMIT_int( ind[ k ], MIN_DELTA_GAIN_QUANT, MAX_DELTA_GAIN_QUANT ); /* Accumulate deltas */ if( ind[ k ] > double_step_size_threshold ) { *prev_ind += silk_LSHIFT( ind[ k ], 1 ) - double_step_size_threshold; } else { *prev_ind += ind[ k ]; } /* Shift to make non-negative */ ind[ k ] -= MIN_DELTA_GAIN_QUANT; } /* Scale and convert to linear scale */ gain_Q16[ k ] = silk_log2lin( silk_min_32( silk_SMULWB( INV_SCALE_Q16, *prev_ind ) + OFFSET, 3967 ) ); /* 3967 = 31 in Q7 */ } } /* Gains scalar dequantization, uniform on log scale */ void silk_gains_dequant( opus_int32 gain_Q16[ MAX_NB_SUBFR ], /* O quantized gains */ const opus_int8 ind[ MAX_NB_SUBFR ], /* I gain indices */ opus_int8 *prev_ind, /* I/O last index in previous frame */ const opus_int conditional, /* I first gain is delta coded if 1 */ const opus_int nb_subfr /* I number of subframes */ ) { opus_int k, ind_tmp, double_step_size_threshold; for( k = 0; k < nb_subfr; k++ ) { if( k == 0 && conditional == 0 ) { /* Gain index is not allowed to go down more than 16 steps (~21.8 dB) */ *prev_ind = silk_max_int( ind[ k ], *prev_ind - 16 ); } else { /* Delta index */ ind_tmp = ind[ k ] + MIN_DELTA_GAIN_QUANT; /* Accumulate deltas */ double_step_size_threshold = 2 * MAX_DELTA_GAIN_QUANT - N_LEVELS_QGAIN + *prev_ind; if( ind_tmp > double_step_size_threshold ) { *prev_ind += silk_LSHIFT( ind_tmp, 1 ) - double_step_size_threshold; } else { *prev_ind += ind_tmp; } } *prev_ind = silk_LIMIT_int( *prev_ind, 0, N_LEVELS_QGAIN - 1 ); /* Scale and convert to linear scale */ gain_Q16[ k ] = silk_log2lin( silk_min_32( silk_SMULWB( INV_SCALE_Q16, *prev_ind ) + OFFSET, 3967 ) ); /* 3967 = 31 in Q7 */ } } /* Compute unique identifier of gain indices vector */ opus_int32 silk_gains_ID( /* O returns unique identifier of gains */ const opus_int8 ind[ MAX_NB_SUBFR ], /* I gain indices */ const opus_int nb_subfr /* I number of subframes */ ) { opus_int k; opus_int32 gainsID; gainsID = 0; for( k = 0; k < nb_subfr; k++ ) { gainsID = silk_ADD_LSHIFT32( ind[ k ], gainsID, 8 ); } return gainsID; } ================================================ FILE: deps/pjsip/third_party/opus/silk/init_decoder.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /************************/ /* Init Decoder State */ /************************/ opus_int silk_init_decoder( silk_decoder_state *psDec /* I/O Decoder state pointer */ ) { /* Clear the entire encoder state, except anything copied */ silk_memset( psDec, 0, sizeof( silk_decoder_state ) ); /* Used to deactivate LSF interpolation */ psDec->first_frame_after_reset = 1; psDec->prev_gain_Q16 = 65536; /* Reset CNG state */ silk_CNG_Reset( psDec ); /* Reset PLC state */ silk_PLC_Reset( psDec ); return(0); } ================================================ FILE: deps/pjsip/third_party/opus/silk/init_encoder.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef FIXED_POINT #include "main_FIX.h" #else #include "main_FLP.h" #endif #include "tuning_parameters.h" #include "cpu_support.h" /*********************************/ /* Initialize Silk Encoder state */ /*********************************/ opus_int silk_init_encoder( silk_encoder_state_Fxx *psEnc, /* I/O Pointer to Silk FIX encoder state */ int arch /* I Run-time architecture */ ) { opus_int ret = 0; /* Clear the entire encoder state */ silk_memset( psEnc, 0, sizeof( silk_encoder_state_Fxx ) ); psEnc->sCmn.arch = arch; psEnc->sCmn.variable_HP_smth1_Q15 = silk_LSHIFT( silk_lin2log( SILK_FIX_CONST( VARIABLE_HP_MIN_CUTOFF_HZ, 16 ) ) - ( 16 << 7 ), 8 ); psEnc->sCmn.variable_HP_smth2_Q15 = psEnc->sCmn.variable_HP_smth1_Q15; /* Used to deactivate LSF interpolation, pitch prediction */ psEnc->sCmn.first_frame_after_reset = 1; /* Initialize Silk VAD */ ret += silk_VAD_Init( &psEnc->sCmn.sVAD ); return ret; } ================================================ FILE: deps/pjsip/third_party/opus/silk/inner_prod_aligned.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" opus_int32 silk_inner_prod_aligned_scale( const opus_int16 *const inVec1, /* I input vector 1 */ const opus_int16 *const inVec2, /* I input vector 2 */ const opus_int scale, /* I number of bits to shift */ const opus_int len /* I vector lengths */ ) { opus_int i; opus_int32 sum = 0; for( i = 0; i < len; i++ ) { sum = silk_ADD_RSHIFT32( sum, silk_SMULBB( inVec1[ i ], inVec2[ i ] ), scale ); } return sum; } ================================================ FILE: deps/pjsip/third_party/opus/silk/interpolate.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Interpolate two vectors */ void silk_interpolate( opus_int16 xi[ MAX_LPC_ORDER ], /* O interpolated vector */ const opus_int16 x0[ MAX_LPC_ORDER ], /* I first vector */ const opus_int16 x1[ MAX_LPC_ORDER ], /* I second vector */ const opus_int ifact_Q2, /* I interp. factor, weight on 2nd vector */ const opus_int d /* I number of parameters */ ) { opus_int i; silk_assert( ifact_Q2 >= 0 ); silk_assert( ifact_Q2 <= 4 ); for( i = 0; i < d; i++ ) { xi[ i ] = (opus_int16)silk_ADD_RSHIFT( x0[ i ], silk_SMULBB( x1[ i ] - x0[ i ], ifact_Q2 ), 2 ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/lin2log.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" /* Approximation of 128 * log2() (very close inverse of silk_log2lin()) */ /* Convert input to a log scale */ opus_int32 silk_lin2log( const opus_int32 inLin /* I input in linear scale */ ) { opus_int32 lz, frac_Q7; silk_CLZ_FRAC( inLin, &lz, &frac_Q7 ); /* Piece-wise parabolic approximation */ return silk_LSHIFT( 31 - lz, 7 ) + silk_SMLAWB( frac_Q7, silk_MUL( frac_Q7, 128 - frac_Q7 ), 179 ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/log2lin.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" /* Approximation of 2^() (very close inverse of silk_lin2log()) */ /* Convert input to a linear scale */ opus_int32 silk_log2lin( const opus_int32 inLog_Q7 /* I input on log scale */ ) { opus_int32 out, frac_Q7; if( inLog_Q7 < 0 ) { return 0; } else if ( inLog_Q7 >= 3967 ) { return silk_int32_MAX; } out = silk_LSHIFT( 1, silk_RSHIFT( inLog_Q7, 7 ) ); frac_Q7 = inLog_Q7 & 0x7F; if( inLog_Q7 < 2048 ) { /* Piece-wise parabolic approximation */ out = silk_ADD_RSHIFT32( out, silk_MUL( out, silk_SMLAWB( frac_Q7, silk_SMULBB( frac_Q7, 128 - frac_Q7 ), -174 ) ), 7 ); } else { /* Piece-wise parabolic approximation */ out = silk_MLA( out, silk_RSHIFT( out, 7 ), silk_SMLAWB( frac_Q7, silk_SMULBB( frac_Q7, 128 - frac_Q7 ), -174 ) ); } return out; } ================================================ FILE: deps/pjsip/third_party/opus/silk/macros.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_MACROS_H #define SILK_MACROS_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "opus_types.h" #include "opus_defines.h" #if OPUS_GNUC_PREREQ(3, 0) #define opus_likely(x) (__builtin_expect(!!(x), 1)) #define opus_unlikely(x) (__builtin_expect(!!(x), 0)) #else #define opus_likely(x) (!!(x)) #define opus_unlikely(x) (!!(x)) #endif /* Set this if opus_int64 is a native type of the CPU. */ #define OPUS_FAST_INT64 (defined(__x86_64__) || defined(__LP64__) || defined(_WIN64)) /* This is an OPUS_INLINE header file for general platform. */ /* (a32 * (opus_int32)((opus_int16)(b32))) >> 16 output have to be 32bit int */ #if OPUS_FAST_INT64 #define silk_SMULWB(a32, b32) (((a32) * (opus_int64)((opus_int16)(b32))) >> 16) #else #define silk_SMULWB(a32, b32) ((((a32) >> 16) * (opus_int32)((opus_int16)(b32))) + ((((a32) & 0x0000FFFF) * (opus_int32)((opus_int16)(b32))) >> 16)) #endif /* a32 + (b32 * (opus_int32)((opus_int16)(c32))) >> 16 output have to be 32bit int */ #if OPUS_FAST_INT64 #define silk_SMLAWB(a32, b32, c32) ((a32) + (((b32) * (opus_int64)((opus_int16)(c32))) >> 16)) #else #define silk_SMLAWB(a32, b32, c32) ((a32) + ((((b32) >> 16) * (opus_int32)((opus_int16)(c32))) + ((((b32) & 0x0000FFFF) * (opus_int32)((opus_int16)(c32))) >> 16))) #endif /* (a32 * (b32 >> 16)) >> 16 */ #define silk_SMULWT(a32, b32) (((a32) >> 16) * ((b32) >> 16) + ((((a32) & 0x0000FFFF) * ((b32) >> 16)) >> 16)) /* a32 + (b32 * (c32 >> 16)) >> 16 */ #if OPUS_FAST_INT64 #define silk_SMLAWT(a32, b32, c32) ((a32) + (((b32) * ((opus_int64)(c32) >> 16)) >> 16)) #else #define silk_SMLAWT(a32, b32, c32) ((a32) + (((b32) >> 16) * ((c32) >> 16)) + ((((b32) & 0x0000FFFF) * ((c32) >> 16)) >> 16)) #endif /* (opus_int32)((opus_int16)(a3))) * (opus_int32)((opus_int16)(b32)) output have to be 32bit int */ #define silk_SMULBB(a32, b32) ((opus_int32)((opus_int16)(a32)) * (opus_int32)((opus_int16)(b32))) /* a32 + (opus_int32)((opus_int16)(b32)) * (opus_int32)((opus_int16)(c32)) output have to be 32bit int */ #define silk_SMLABB(a32, b32, c32) ((a32) + ((opus_int32)((opus_int16)(b32))) * (opus_int32)((opus_int16)(c32))) /* (opus_int32)((opus_int16)(a32)) * (b32 >> 16) */ #define silk_SMULBT(a32, b32) ((opus_int32)((opus_int16)(a32)) * ((b32) >> 16)) /* a32 + (opus_int32)((opus_int16)(b32)) * (c32 >> 16) */ #define silk_SMLABT(a32, b32, c32) ((a32) + ((opus_int32)((opus_int16)(b32))) * ((c32) >> 16)) /* a64 + (b32 * c32) */ #define silk_SMLAL(a64, b32, c32) (silk_ADD64((a64), ((opus_int64)(b32) * (opus_int64)(c32)))) /* (a32 * b32) >> 16 */ #if OPUS_FAST_INT64 #define silk_SMULWW(a32, b32) (((opus_int64)(a32) * (b32)) >> 16) #else #define silk_SMULWW(a32, b32) silk_MLA(silk_SMULWB((a32), (b32)), (a32), silk_RSHIFT_ROUND((b32), 16)) #endif /* a32 + ((b32 * c32) >> 16) */ #if OPUS_FAST_INT64 #define silk_SMLAWW(a32, b32, c32) ((a32) + (((opus_int64)(b32) * (c32)) >> 16)) #else #define silk_SMLAWW(a32, b32, c32) silk_MLA(silk_SMLAWB((a32), (b32), (c32)), (b32), silk_RSHIFT_ROUND((c32), 16)) #endif /* add/subtract with output saturated */ #define silk_ADD_SAT32(a, b) ((((opus_uint32)(a) + (opus_uint32)(b)) & 0x80000000) == 0 ? \ ((((a) & (b)) & 0x80000000) != 0 ? silk_int32_MIN : (a)+(b)) : \ ((((a) | (b)) & 0x80000000) == 0 ? silk_int32_MAX : (a)+(b)) ) #define silk_SUB_SAT32(a, b) ((((opus_uint32)(a)-(opus_uint32)(b)) & 0x80000000) == 0 ? \ (( (a) & ((b)^0x80000000) & 0x80000000) ? silk_int32_MIN : (a)-(b)) : \ ((((a)^0x80000000) & (b) & 0x80000000) ? silk_int32_MAX : (a)-(b)) ) #if defined(MIPSr1_ASM) #include "mips/macros_mipsr1.h" #endif #include "ecintrin.h" #ifndef OVERRIDE_silk_CLZ16 static OPUS_INLINE opus_int32 silk_CLZ16(opus_int16 in16) { return 32 - EC_ILOG(in16<<16|0x8000); } #endif #ifndef OVERRIDE_silk_CLZ32 static OPUS_INLINE opus_int32 silk_CLZ32(opus_int32 in32) { return in32 ? 32 - EC_ILOG(in32) : 32; } #endif /* Row based */ #define matrix_ptr(Matrix_base_adr, row, column, N) \ (*((Matrix_base_adr) + ((row)*(N)+(column)))) #define matrix_adr(Matrix_base_adr, row, column, N) \ ((Matrix_base_adr) + ((row)*(N)+(column))) /* Column based */ #ifndef matrix_c_ptr # define matrix_c_ptr(Matrix_base_adr, row, column, M) \ (*((Matrix_base_adr) + ((row)+(M)*(column)))) #endif #ifdef OPUS_ARM_INLINE_ASM #include "arm/macros_armv4.h" #endif #ifdef OPUS_ARM_INLINE_EDSP #include "arm/macros_armv5e.h" #endif #endif /* SILK_MACROS_H */ ================================================ FILE: deps/pjsip/third_party/opus/silk/main.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_MAIN_H #define SILK_MAIN_H #include "SigProc_FIX.h" #include "define.h" #include "structs.h" #include "tables.h" #include "PLC.h" #include "control.h" #include "debug.h" #include "entenc.h" #include "entdec.h" #if defined(OPUS_X86_MAY_HAVE_SSE4_1) #include "x86/main_sse.h" #endif /* Convert Left/Right stereo signal to adaptive Mid/Side representation */ void silk_stereo_LR_to_MS( stereo_enc_state *state, /* I/O State */ opus_int16 x1[], /* I/O Left input signal, becomes mid signal */ opus_int16 x2[], /* I/O Right input signal, becomes side signal */ opus_int8 ix[ 2 ][ 3 ], /* O Quantization indices */ opus_int8 *mid_only_flag, /* O Flag: only mid signal coded */ opus_int32 mid_side_rates_bps[], /* O Bitrates for mid and side signals */ opus_int32 total_rate_bps, /* I Total bitrate */ opus_int prev_speech_act_Q8, /* I Speech activity level in previous frame */ opus_int toMono, /* I Last frame before a stereo->mono transition */ opus_int fs_kHz, /* I Sample rate (kHz) */ opus_int frame_length /* I Number of samples */ ); /* Convert adaptive Mid/Side representation to Left/Right stereo signal */ void silk_stereo_MS_to_LR( stereo_dec_state *state, /* I/O State */ opus_int16 x1[], /* I/O Left input signal, becomes mid signal */ opus_int16 x2[], /* I/O Right input signal, becomes side signal */ const opus_int32 pred_Q13[], /* I Predictors */ opus_int fs_kHz, /* I Samples rate (kHz) */ opus_int frame_length /* I Number of samples */ ); /* Find least-squares prediction gain for one signal based on another and quantize it */ opus_int32 silk_stereo_find_predictor( /* O Returns predictor in Q13 */ opus_int32 *ratio_Q14, /* O Ratio of residual and mid energies */ const opus_int16 x[], /* I Basis signal */ const opus_int16 y[], /* I Target signal */ opus_int32 mid_res_amp_Q0[], /* I/O Smoothed mid, residual norms */ opus_int length, /* I Number of samples */ opus_int smooth_coef_Q16 /* I Smoothing coefficient */ ); /* Quantize mid/side predictors */ void silk_stereo_quant_pred( opus_int32 pred_Q13[], /* I/O Predictors (out: quantized) */ opus_int8 ix[ 2 ][ 3 ] /* O Quantization indices */ ); /* Entropy code the mid/side quantization indices */ void silk_stereo_encode_pred( ec_enc *psRangeEnc, /* I/O Compressor data structure */ opus_int8 ix[ 2 ][ 3 ] /* I Quantization indices */ ); /* Entropy code the mid-only flag */ void silk_stereo_encode_mid_only( ec_enc *psRangeEnc, /* I/O Compressor data structure */ opus_int8 mid_only_flag ); /* Decode mid/side predictors */ void silk_stereo_decode_pred( ec_dec *psRangeDec, /* I/O Compressor data structure */ opus_int32 pred_Q13[] /* O Predictors */ ); /* Decode mid-only flag */ void silk_stereo_decode_mid_only( ec_dec *psRangeDec, /* I/O Compressor data structure */ opus_int *decode_only_mid /* O Flag that only mid channel has been coded */ ); /* Encodes signs of excitation */ void silk_encode_signs( ec_enc *psRangeEnc, /* I/O Compressor data structure */ const opus_int8 pulses[], /* I pulse signal */ opus_int length, /* I length of input */ const opus_int signalType, /* I Signal type */ const opus_int quantOffsetType, /* I Quantization offset type */ const opus_int sum_pulses[ MAX_NB_SHELL_BLOCKS ] /* I Sum of absolute pulses per block */ ); /* Decodes signs of excitation */ void silk_decode_signs( ec_dec *psRangeDec, /* I/O Compressor data structure */ opus_int16 pulses[], /* I/O pulse signal */ opus_int length, /* I length of input */ const opus_int signalType, /* I Signal type */ const opus_int quantOffsetType, /* I Quantization offset type */ const opus_int sum_pulses[ MAX_NB_SHELL_BLOCKS ] /* I Sum of absolute pulses per block */ ); /* Check encoder control struct */ opus_int check_control_input( silk_EncControlStruct *encControl /* I Control structure */ ); /* Control internal sampling rate */ opus_int silk_control_audio_bandwidth( silk_encoder_state *psEncC, /* I/O Pointer to Silk encoder state */ silk_EncControlStruct *encControl /* I Control structure */ ); /* Control SNR of redidual quantizer */ opus_int silk_control_SNR( silk_encoder_state *psEncC, /* I/O Pointer to Silk encoder state */ opus_int32 TargetRate_bps /* I Target max bitrate (bps) */ ); /***************/ /* Shell coder */ /***************/ /* Encode quantization indices of excitation */ void silk_encode_pulses( ec_enc *psRangeEnc, /* I/O compressor data structure */ const opus_int signalType, /* I Signal type */ const opus_int quantOffsetType, /* I quantOffsetType */ opus_int8 pulses[], /* I quantization indices */ const opus_int frame_length /* I Frame length */ ); /* Shell encoder, operates on one shell code frame of 16 pulses */ void silk_shell_encoder( ec_enc *psRangeEnc, /* I/O compressor data structure */ const opus_int *pulses0 /* I data: nonnegative pulse amplitudes */ ); /* Shell decoder, operates on one shell code frame of 16 pulses */ void silk_shell_decoder( opus_int16 *pulses0, /* O data: nonnegative pulse amplitudes */ ec_dec *psRangeDec, /* I/O Compressor data structure */ const opus_int pulses4 /* I number of pulses per pulse-subframe */ ); /* Gain scalar quantization with hysteresis, uniform on log scale */ void silk_gains_quant( opus_int8 ind[ MAX_NB_SUBFR ], /* O gain indices */ opus_int32 gain_Q16[ MAX_NB_SUBFR ], /* I/O gains (quantized out) */ opus_int8 *prev_ind, /* I/O last index in previous frame */ const opus_int conditional, /* I first gain is delta coded if 1 */ const opus_int nb_subfr /* I number of subframes */ ); /* Gains scalar dequantization, uniform on log scale */ void silk_gains_dequant( opus_int32 gain_Q16[ MAX_NB_SUBFR ], /* O quantized gains */ const opus_int8 ind[ MAX_NB_SUBFR ], /* I gain indices */ opus_int8 *prev_ind, /* I/O last index in previous frame */ const opus_int conditional, /* I first gain is delta coded if 1 */ const opus_int nb_subfr /* I number of subframes */ ); /* Compute unique identifier of gain indices vector */ opus_int32 silk_gains_ID( /* O returns unique identifier of gains */ const opus_int8 ind[ MAX_NB_SUBFR ], /* I gain indices */ const opus_int nb_subfr /* I number of subframes */ ); /* Interpolate two vectors */ void silk_interpolate( opus_int16 xi[ MAX_LPC_ORDER ], /* O interpolated vector */ const opus_int16 x0[ MAX_LPC_ORDER ], /* I first vector */ const opus_int16 x1[ MAX_LPC_ORDER ], /* I second vector */ const opus_int ifact_Q2, /* I interp. factor, weight on 2nd vector */ const opus_int d /* I number of parameters */ ); /* LTP tap quantizer */ void silk_quant_LTP_gains( opus_int16 B_Q14[ MAX_NB_SUBFR * LTP_ORDER ], /* I/O (un)quantized LTP gains */ opus_int8 cbk_index[ MAX_NB_SUBFR ], /* O Codebook Index */ opus_int8 *periodicity_index, /* O Periodicity Index */ opus_int32 *sum_gain_dB_Q7, /* I/O Cumulative max prediction gain */ const opus_int32 W_Q18[ MAX_NB_SUBFR*LTP_ORDER*LTP_ORDER ], /* I Error Weights in Q18 */ opus_int mu_Q9, /* I Mu value (R/D tradeoff) */ opus_int lowComplexity, /* I Flag for low complexity */ const opus_int nb_subfr, /* I number of subframes */ int arch /* I Run-time architecture */ ); /* Entropy constrained matrix-weighted VQ, for a single input data vector */ void silk_VQ_WMat_EC_c( opus_int8 *ind, /* O index of best codebook vector */ opus_int32 *rate_dist_Q14, /* O best weighted quant error + mu * rate */ opus_int *gain_Q7, /* O sum of absolute LTP coefficients */ const opus_int16 *in_Q14, /* I input vector to be quantized */ const opus_int32 *W_Q18, /* I weighting matrix */ const opus_int8 *cb_Q7, /* I codebook */ const opus_uint8 *cb_gain_Q7, /* I codebook effective gain */ const opus_uint8 *cl_Q5, /* I code length for each codebook vector */ const opus_int mu_Q9, /* I tradeoff betw. weighted error and rate */ const opus_int32 max_gain_Q7, /* I maximum sum of absolute LTP coefficients */ opus_int L /* I number of vectors in codebook */ ); #if !defined(OVERRIDE_silk_VQ_WMat_EC) #define silk_VQ_WMat_EC(ind, rate_dist_Q14, gain_Q7, in_Q14, W_Q18, cb_Q7, cb_gain_Q7, cl_Q5, \ mu_Q9, max_gain_Q7, L, arch) \ ((void)(arch),silk_VQ_WMat_EC_c(ind, rate_dist_Q14, gain_Q7, in_Q14, W_Q18, cb_Q7, cb_gain_Q7, cl_Q5, \ mu_Q9, max_gain_Q7, L)) #endif /************************************/ /* Noise shaping quantization (NSQ) */ /************************************/ void silk_NSQ_c( const silk_encoder_state *psEncC, /* I/O Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ SideInfoIndices *psIndices, /* I/O Quantization Indices */ const opus_int32 x_Q3[], /* I Prefiltered input signal */ opus_int8 pulses[], /* O Quantized pulse signal */ const opus_int16 PredCoef_Q12[ 2 * MAX_LPC_ORDER ], /* I Short term prediction coefs */ const opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ], /* I Long term prediction coefs */ const opus_int16 AR2_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ], /* I Noise shaping coefs */ const opus_int HarmShapeGain_Q14[ MAX_NB_SUBFR ], /* I Long term shaping coefs */ const opus_int Tilt_Q14[ MAX_NB_SUBFR ], /* I Spectral tilt */ const opus_int32 LF_shp_Q14[ MAX_NB_SUBFR ], /* I Low frequency shaping coefs */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I Quantization step sizes */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lags */ const opus_int Lambda_Q10, /* I Rate/distortion tradeoff */ const opus_int LTP_scale_Q14 /* I LTP state scaling */ ); #if !defined(OVERRIDE_silk_NSQ) #define silk_NSQ(psEncC, NSQ, psIndices, x_Q3, pulses, PredCoef_Q12, LTPCoef_Q14, AR2_Q13, \ HarmShapeGain_Q14, Tilt_Q14, LF_shp_Q14, Gains_Q16, pitchL, Lambda_Q10, LTP_scale_Q14, arch) \ ((void)(arch),silk_NSQ_c(psEncC, NSQ, psIndices, x_Q3, pulses, PredCoef_Q12, LTPCoef_Q14, AR2_Q13, \ HarmShapeGain_Q14, Tilt_Q14, LF_shp_Q14, Gains_Q16, pitchL, Lambda_Q10, LTP_scale_Q14)) #endif /* Noise shaping using delayed decision */ void silk_NSQ_del_dec_c( const silk_encoder_state *psEncC, /* I/O Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ SideInfoIndices *psIndices, /* I/O Quantization Indices */ const opus_int32 x_Q3[], /* I Prefiltered input signal */ opus_int8 pulses[], /* O Quantized pulse signal */ const opus_int16 PredCoef_Q12[ 2 * MAX_LPC_ORDER ], /* I Short term prediction coefs */ const opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ], /* I Long term prediction coefs */ const opus_int16 AR2_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ], /* I Noise shaping coefs */ const opus_int HarmShapeGain_Q14[ MAX_NB_SUBFR ], /* I Long term shaping coefs */ const opus_int Tilt_Q14[ MAX_NB_SUBFR ], /* I Spectral tilt */ const opus_int32 LF_shp_Q14[ MAX_NB_SUBFR ], /* I Low frequency shaping coefs */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I Quantization step sizes */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lags */ const opus_int Lambda_Q10, /* I Rate/distortion tradeoff */ const opus_int LTP_scale_Q14 /* I LTP state scaling */ ); #if !defined(OVERRIDE_silk_NSQ_del_dec) #define silk_NSQ_del_dec(psEncC, NSQ, psIndices, x_Q3, pulses, PredCoef_Q12, LTPCoef_Q14, AR2_Q13, \ HarmShapeGain_Q14, Tilt_Q14, LF_shp_Q14, Gains_Q16, pitchL, Lambda_Q10, LTP_scale_Q14, arch) \ ((void)(arch),silk_NSQ_del_dec_c(psEncC, NSQ, psIndices, x_Q3, pulses, PredCoef_Q12, LTPCoef_Q14, AR2_Q13, \ HarmShapeGain_Q14, Tilt_Q14, LF_shp_Q14, Gains_Q16, pitchL, Lambda_Q10, LTP_scale_Q14)) #endif /************/ /* Silk VAD */ /************/ /* Initialize the Silk VAD */ opus_int silk_VAD_Init( /* O Return value, 0 if success */ silk_VAD_state *psSilk_VAD /* I/O Pointer to Silk VAD state */ ); /* Get speech activity level in Q8 */ opus_int silk_VAD_GetSA_Q8_c( /* O Return value, 0 if success */ silk_encoder_state *psEncC, /* I/O Encoder state */ const opus_int16 pIn[] /* I PCM input */ ); #if !defined(OVERRIDE_silk_VAD_GetSA_Q8) #define silk_VAD_GetSA_Q8(psEnC, pIn, arch) ((void)(arch),silk_VAD_GetSA_Q8_c(psEnC, pIn)) #endif /* Low-pass filter with variable cutoff frequency based on */ /* piece-wise linear interpolation between elliptic filters */ /* Start by setting transition_frame_no = 1; */ void silk_LP_variable_cutoff( silk_LP_state *psLP, /* I/O LP filter state */ opus_int16 *frame, /* I/O Low-pass filtered output signal */ const opus_int frame_length /* I Frame length */ ); /******************/ /* NLSF Quantizer */ /******************/ /* Limit, stabilize, convert and quantize NLSFs */ void silk_process_NLSFs( silk_encoder_state *psEncC, /* I/O Encoder state */ opus_int16 PredCoef_Q12[ 2 ][ MAX_LPC_ORDER ], /* O Prediction coefficients */ opus_int16 pNLSF_Q15[ MAX_LPC_ORDER ], /* I/O Normalized LSFs (quant out) (0 - (2^15-1)) */ const opus_int16 prev_NLSFq_Q15[ MAX_LPC_ORDER ] /* I Previous Normalized LSFs (0 - (2^15-1)) */ ); opus_int32 silk_NLSF_encode( /* O Returns RD value in Q25 */ opus_int8 *NLSFIndices, /* I Codebook path vector [ LPC_ORDER + 1 ] */ opus_int16 *pNLSF_Q15, /* I/O Quantized NLSF vector [ LPC_ORDER ] */ const silk_NLSF_CB_struct *psNLSF_CB, /* I Codebook object */ const opus_int16 *pW_QW, /* I NLSF weight vector [ LPC_ORDER ] */ const opus_int NLSF_mu_Q20, /* I Rate weight for the RD optimization */ const opus_int nSurvivors, /* I Max survivors after first stage */ const opus_int signalType /* I Signal type: 0/1/2 */ ); /* Compute quantization errors for an LPC_order element input vector for a VQ codebook */ void silk_NLSF_VQ( opus_int32 err_Q26[], /* O Quantization errors [K] */ const opus_int16 in_Q15[], /* I Input vectors to be quantized [LPC_order] */ const opus_uint8 pCB_Q8[], /* I Codebook vectors [K*LPC_order] */ const opus_int K, /* I Number of codebook vectors */ const opus_int LPC_order /* I Number of LPCs */ ); /* Delayed-decision quantizer for NLSF residuals */ opus_int32 silk_NLSF_del_dec_quant( /* O Returns RD value in Q25 */ opus_int8 indices[], /* O Quantization indices [ order ] */ const opus_int16 x_Q10[], /* I Input [ order ] */ const opus_int16 w_Q5[], /* I Weights [ order ] */ const opus_uint8 pred_coef_Q8[], /* I Backward predictor coefs [ order ] */ const opus_int16 ec_ix[], /* I Indices to entropy coding tables [ order ] */ const opus_uint8 ec_rates_Q5[], /* I Rates [] */ const opus_int quant_step_size_Q16, /* I Quantization step size */ const opus_int16 inv_quant_step_size_Q6, /* I Inverse quantization step size */ const opus_int32 mu_Q20, /* I R/D tradeoff */ const opus_int16 order /* I Number of input values */ ); /* Unpack predictor values and indices for entropy coding tables */ void silk_NLSF_unpack( opus_int16 ec_ix[], /* O Indices to entropy tables [ LPC_ORDER ] */ opus_uint8 pred_Q8[], /* O LSF predictor [ LPC_ORDER ] */ const silk_NLSF_CB_struct *psNLSF_CB, /* I Codebook object */ const opus_int CB1_index /* I Index of vector in first LSF codebook */ ); /***********************/ /* NLSF vector decoder */ /***********************/ void silk_NLSF_decode( opus_int16 *pNLSF_Q15, /* O Quantized NLSF vector [ LPC_ORDER ] */ opus_int8 *NLSFIndices, /* I Codebook path vector [ LPC_ORDER + 1 ] */ const silk_NLSF_CB_struct *psNLSF_CB /* I Codebook object */ ); /****************************************************/ /* Decoder Functions */ /****************************************************/ opus_int silk_init_decoder( silk_decoder_state *psDec /* I/O Decoder state pointer */ ); /* Set decoder sampling rate */ opus_int silk_decoder_set_fs( silk_decoder_state *psDec, /* I/O Decoder state pointer */ opus_int fs_kHz, /* I Sampling frequency (kHz) */ opus_int32 fs_API_Hz /* I API Sampling frequency (Hz) */ ); /****************/ /* Decode frame */ /****************/ opus_int silk_decode_frame( silk_decoder_state *psDec, /* I/O Pointer to Silk decoder state */ ec_dec *psRangeDec, /* I/O Compressor data structure */ opus_int16 pOut[], /* O Pointer to output speech frame */ opus_int32 *pN, /* O Pointer to size of output frame */ opus_int lostFlag, /* I 0: no loss, 1 loss, 2 decode fec */ opus_int condCoding, /* I The type of conditional coding to use */ int arch /* I Run-time architecture */ ); /* Decode indices from bitstream */ void silk_decode_indices( silk_decoder_state *psDec, /* I/O State */ ec_dec *psRangeDec, /* I/O Compressor data structure */ opus_int FrameIndex, /* I Frame number */ opus_int decode_LBRR, /* I Flag indicating LBRR data is being decoded */ opus_int condCoding /* I The type of conditional coding to use */ ); /* Decode parameters from payload */ void silk_decode_parameters( silk_decoder_state *psDec, /* I/O State */ silk_decoder_control *psDecCtrl, /* I/O Decoder control */ opus_int condCoding /* I The type of conditional coding to use */ ); /* Core decoder. Performs inverse NSQ operation LTP + LPC */ void silk_decode_core( silk_decoder_state *psDec, /* I/O Decoder state */ silk_decoder_control *psDecCtrl, /* I Decoder control */ opus_int16 xq[], /* O Decoded speech */ const opus_int16 pulses[ MAX_FRAME_LENGTH ], /* I Pulse signal */ int arch /* I Run-time architecture */ ); /* Decode quantization indices of excitation (Shell coding) */ void silk_decode_pulses( ec_dec *psRangeDec, /* I/O Compressor data structure */ opus_int16 pulses[], /* O Excitation signal */ const opus_int signalType, /* I Sigtype */ const opus_int quantOffsetType, /* I quantOffsetType */ const opus_int frame_length /* I Frame length */ ); /******************/ /* CNG */ /******************/ /* Reset CNG */ void silk_CNG_Reset( silk_decoder_state *psDec /* I/O Decoder state */ ); /* Updates CNG estimate, and applies the CNG when packet was lost */ void silk_CNG( silk_decoder_state *psDec, /* I/O Decoder state */ silk_decoder_control *psDecCtrl, /* I/O Decoder control */ opus_int16 frame[], /* I/O Signal */ opus_int length /* I Length of residual */ ); /* Encoding of various parameters */ void silk_encode_indices( silk_encoder_state *psEncC, /* I/O Encoder state */ ec_enc *psRangeEnc, /* I/O Compressor data structure */ opus_int FrameIndex, /* I Frame number */ opus_int encode_LBRR, /* I Flag indicating LBRR data is being encoded */ opus_int condCoding /* I The type of conditional coding to use */ ); #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/mips/NSQ_del_dec_mipsr1.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef __NSQ_DEL_DEC_MIPSR1_H__ #define __NSQ_DEL_DEC_MIPSR1_H__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #include "stack_alloc.h" #define OVERRIDE_silk_noise_shape_quantizer_del_dec static inline void silk_noise_shape_quantizer_del_dec( silk_nsq_state *NSQ, /* I/O NSQ state */ NSQ_del_dec_struct psDelDec[], /* I/O Delayed decision states */ opus_int signalType, /* I Signal type */ const opus_int32 x_Q10[], /* I */ opus_int8 pulses[], /* O */ opus_int16 xq[], /* O */ opus_int32 sLTP_Q15[], /* I/O LTP filter state */ opus_int32 delayedGain_Q10[], /* I/O Gain delay buffer */ const opus_int16 a_Q12[], /* I Short term prediction coefs */ const opus_int16 b_Q14[], /* I Long term prediction coefs */ const opus_int16 AR_shp_Q13[], /* I Noise shaping coefs */ opus_int lag, /* I Pitch lag */ opus_int32 HarmShapeFIRPacked_Q14, /* I */ opus_int Tilt_Q14, /* I Spectral tilt */ opus_int32 LF_shp_Q14, /* I */ opus_int32 Gain_Q16, /* I */ opus_int Lambda_Q10, /* I */ opus_int offset_Q10, /* I */ opus_int length, /* I Input length */ opus_int subfr, /* I Subframe number */ opus_int shapingLPCOrder, /* I Shaping LPC filter order */ opus_int predictLPCOrder, /* I Prediction filter order */ opus_int warping_Q16, /* I */ opus_int nStatesDelayedDecision, /* I Number of states in decision tree */ opus_int *smpl_buf_idx, /* I Index to newest samples in buffers */ opus_int decisionDelay /* I */ ) { opus_int i, j, k, Winner_ind, RDmin_ind, RDmax_ind, last_smple_idx; opus_int32 Winner_rand_state; opus_int32 LTP_pred_Q14, LPC_pred_Q14, n_AR_Q14, n_LTP_Q14; opus_int32 n_LF_Q14, r_Q10, rr_Q10, rd1_Q10, rd2_Q10, RDmin_Q10, RDmax_Q10; opus_int32 q1_Q0, q1_Q10, q2_Q10, exc_Q14, LPC_exc_Q14, xq_Q14, Gain_Q10; opus_int32 tmp1, tmp2, sLF_AR_shp_Q14; opus_int32 *pred_lag_ptr, *shp_lag_ptr, *psLPC_Q14; NSQ_sample_struct psSampleState[ MAX_DEL_DEC_STATES ][ 2 ]; NSQ_del_dec_struct *psDD; NSQ_sample_struct *psSS; opus_int16 b_Q14_0, b_Q14_1, b_Q14_2, b_Q14_3, b_Q14_4; opus_int16 a_Q12_0, a_Q12_1, a_Q12_2, a_Q12_3, a_Q12_4, a_Q12_5, a_Q12_6; opus_int16 a_Q12_7, a_Q12_8, a_Q12_9, a_Q12_10, a_Q12_11, a_Q12_12, a_Q12_13; opus_int16 a_Q12_14, a_Q12_15; opus_int32 cur, prev, next; //Intialize b_Q14 variables b_Q14_0 = b_Q14[ 0 ]; b_Q14_1 = b_Q14[ 1 ]; b_Q14_2 = b_Q14[ 2 ]; b_Q14_3 = b_Q14[ 3 ]; b_Q14_4 = b_Q14[ 4 ]; //Intialize a_Q12 variables a_Q12_0 = a_Q12[0]; a_Q12_1 = a_Q12[1]; a_Q12_2 = a_Q12[2]; a_Q12_3 = a_Q12[3]; a_Q12_4 = a_Q12[4]; a_Q12_5 = a_Q12[5]; a_Q12_6 = a_Q12[6]; a_Q12_7 = a_Q12[7]; a_Q12_8 = a_Q12[8]; a_Q12_9 = a_Q12[9]; a_Q12_10 = a_Q12[10]; a_Q12_11 = a_Q12[11]; a_Q12_12 = a_Q12[12]; a_Q12_13 = a_Q12[13]; a_Q12_14 = a_Q12[14]; a_Q12_15 = a_Q12[15]; long long temp64; silk_assert( nStatesDelayedDecision > 0 ); shp_lag_ptr = &NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - lag + HARM_SHAPE_FIR_TAPS / 2 ]; pred_lag_ptr = &sLTP_Q15[ NSQ->sLTP_buf_idx - lag + LTP_ORDER / 2 ]; Gain_Q10 = silk_RSHIFT( Gain_Q16, 6 ); for( i = 0; i < length; i++ ) { /* Perform common calculations used in all states */ /* Long-term prediction */ if( signalType == TYPE_VOICED ) { /* Unrolled loop */ /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ temp64 = __builtin_mips_mult(pred_lag_ptr[ 0 ], b_Q14_0 ); temp64 = __builtin_mips_madd( temp64, pred_lag_ptr[ -1 ], b_Q14_1 ); temp64 = __builtin_mips_madd( temp64, pred_lag_ptr[ -2 ], b_Q14_2 ); temp64 = __builtin_mips_madd( temp64, pred_lag_ptr[ -3 ], b_Q14_3 ); temp64 = __builtin_mips_madd( temp64, pred_lag_ptr[ -4 ], b_Q14_4 ); temp64 += 32768; LTP_pred_Q14 = __builtin_mips_extr_w(temp64, 16); LTP_pred_Q14 = silk_LSHIFT( LTP_pred_Q14, 1 ); /* Q13 -> Q14 */ pred_lag_ptr++; } else { LTP_pred_Q14 = 0; } /* Long-term shaping */ if( lag > 0 ) { /* Symmetric, packed FIR coefficients */ n_LTP_Q14 = silk_SMULWB( silk_ADD32( shp_lag_ptr[ 0 ], shp_lag_ptr[ -2 ] ), HarmShapeFIRPacked_Q14 ); n_LTP_Q14 = silk_SMLAWT( n_LTP_Q14, shp_lag_ptr[ -1 ], HarmShapeFIRPacked_Q14 ); n_LTP_Q14 = silk_SUB_LSHIFT32( LTP_pred_Q14, n_LTP_Q14, 2 ); /* Q12 -> Q14 */ shp_lag_ptr++; } else { n_LTP_Q14 = 0; } for( k = 0; k < nStatesDelayedDecision; k++ ) { /* Delayed decision state */ psDD = &psDelDec[ k ]; /* Sample state */ psSS = psSampleState[ k ]; /* Generate dither */ psDD->Seed = silk_RAND( psDD->Seed ); /* Pointer used in short term prediction and shaping */ psLPC_Q14 = &psDD->sLPC_Q14[ NSQ_LPC_BUF_LENGTH - 1 + i ]; /* Short-term prediction */ silk_assert( predictLPCOrder == 10 || predictLPCOrder == 16 ); temp64 = __builtin_mips_mult(psLPC_Q14[ 0 ], a_Q12_0 ); temp64 = __builtin_mips_madd( temp64, psLPC_Q14[ -1 ], a_Q12_1 ); temp64 = __builtin_mips_madd( temp64, psLPC_Q14[ -2 ], a_Q12_2 ); temp64 = __builtin_mips_madd( temp64, psLPC_Q14[ -3 ], a_Q12_3 ); temp64 = __builtin_mips_madd( temp64, psLPC_Q14[ -4 ], a_Q12_4 ); temp64 = __builtin_mips_madd( temp64, psLPC_Q14[ -5 ], a_Q12_5 ); temp64 = __builtin_mips_madd( temp64, psLPC_Q14[ -6 ], a_Q12_6 ); temp64 = __builtin_mips_madd( temp64, psLPC_Q14[ -7 ], a_Q12_7 ); temp64 = __builtin_mips_madd( temp64, psLPC_Q14[ -8 ], a_Q12_8 ); temp64 = __builtin_mips_madd( temp64, psLPC_Q14[ -9 ], a_Q12_9 ); if( predictLPCOrder == 16 ) { temp64 = __builtin_mips_madd( temp64, psLPC_Q14[ -10 ], a_Q12_10 ); temp64 = __builtin_mips_madd( temp64, psLPC_Q14[ -11 ], a_Q12_11 ); temp64 = __builtin_mips_madd( temp64, psLPC_Q14[ -12 ], a_Q12_12 ); temp64 = __builtin_mips_madd( temp64, psLPC_Q14[ -13 ], a_Q12_13 ); temp64 = __builtin_mips_madd( temp64, psLPC_Q14[ -14 ], a_Q12_14 ); temp64 = __builtin_mips_madd( temp64, psLPC_Q14[ -15 ], a_Q12_15 ); } temp64 += 32768; LPC_pred_Q14 = __builtin_mips_extr_w(temp64, 16); LPC_pred_Q14 = silk_LSHIFT( LPC_pred_Q14, 4 ); /* Q10 -> Q14 */ /* Noise shape feedback */ silk_assert( ( shapingLPCOrder & 1 ) == 0 ); /* check that order is even */ /* Output of lowpass section */ tmp2 = silk_SMLAWB( psLPC_Q14[ 0 ], psDD->sAR2_Q14[ 0 ], warping_Q16 ); /* Output of allpass section */ tmp1 = silk_SMLAWB( psDD->sAR2_Q14[ 0 ], psDD->sAR2_Q14[ 1 ] - tmp2, warping_Q16 ); psDD->sAR2_Q14[ 0 ] = tmp2; temp64 = __builtin_mips_mult(tmp2, AR_shp_Q13[ 0 ] ); prev = psDD->sAR2_Q14[ 1 ]; /* Loop over allpass sections */ for( j = 2; j < shapingLPCOrder; j += 2 ) { cur = psDD->sAR2_Q14[ j ]; next = psDD->sAR2_Q14[ j+1 ]; /* Output of allpass section */ tmp2 = silk_SMLAWB( prev, cur - tmp1, warping_Q16 ); psDD->sAR2_Q14[ j - 1 ] = tmp1; temp64 = __builtin_mips_madd( temp64, tmp1, AR_shp_Q13[ j - 1 ] ); temp64 = __builtin_mips_madd( temp64, tmp2, AR_shp_Q13[ j ] ); /* Output of allpass section */ tmp1 = silk_SMLAWB( cur, next - tmp2, warping_Q16 ); psDD->sAR2_Q14[ j + 0 ] = tmp2; prev = next; } psDD->sAR2_Q14[ shapingLPCOrder - 1 ] = tmp1; temp64 = __builtin_mips_madd( temp64, tmp1, AR_shp_Q13[ shapingLPCOrder - 1 ] ); temp64 += 32768; n_AR_Q14 = __builtin_mips_extr_w(temp64, 16); n_AR_Q14 = silk_LSHIFT( n_AR_Q14, 1 ); /* Q11 -> Q12 */ n_AR_Q14 = silk_SMLAWB( n_AR_Q14, psDD->LF_AR_Q14, Tilt_Q14 ); /* Q12 */ n_AR_Q14 = silk_LSHIFT( n_AR_Q14, 2 ); /* Q12 -> Q14 */ n_LF_Q14 = silk_SMULWB( psDD->Shape_Q14[ *smpl_buf_idx ], LF_shp_Q14 ); /* Q12 */ n_LF_Q14 = silk_SMLAWT( n_LF_Q14, psDD->LF_AR_Q14, LF_shp_Q14 ); /* Q12 */ n_LF_Q14 = silk_LSHIFT( n_LF_Q14, 2 ); /* Q12 -> Q14 */ /* Input minus prediction plus noise feedback */ /* r = x[ i ] - LTP_pred - LPC_pred + n_AR + n_Tilt + n_LF + n_LTP */ tmp1 = silk_ADD32( n_AR_Q14, n_LF_Q14 ); /* Q14 */ tmp2 = silk_ADD32( n_LTP_Q14, LPC_pred_Q14 ); /* Q13 */ tmp1 = silk_SUB32( tmp2, tmp1 ); /* Q13 */ tmp1 = silk_RSHIFT_ROUND( tmp1, 4 ); /* Q10 */ r_Q10 = silk_SUB32( x_Q10[ i ], tmp1 ); /* residual error Q10 */ /* Flip sign depending on dither */ if ( psDD->Seed < 0 ) { r_Q10 = -r_Q10; } r_Q10 = silk_LIMIT_32( r_Q10, -(31 << 10), 30 << 10 ); /* Find two quantization level candidates and measure their rate-distortion */ q1_Q10 = silk_SUB32( r_Q10, offset_Q10 ); q1_Q0 = silk_RSHIFT( q1_Q10, 10 ); if( q1_Q0 > 0 ) { q1_Q10 = silk_SUB32( silk_LSHIFT( q1_Q0, 10 ), QUANT_LEVEL_ADJUST_Q10 ); q1_Q10 = silk_ADD32( q1_Q10, offset_Q10 ); q2_Q10 = silk_ADD32( q1_Q10, 1024 ); rd1_Q10 = silk_SMULBB( q1_Q10, Lambda_Q10 ); rd2_Q10 = silk_SMULBB( q2_Q10, Lambda_Q10 ); } else if( q1_Q0 == 0 ) { q1_Q10 = offset_Q10; q2_Q10 = silk_ADD32( q1_Q10, 1024 - QUANT_LEVEL_ADJUST_Q10 ); rd1_Q10 = silk_SMULBB( q1_Q10, Lambda_Q10 ); rd2_Q10 = silk_SMULBB( q2_Q10, Lambda_Q10 ); } else if( q1_Q0 == -1 ) { q2_Q10 = offset_Q10; q1_Q10 = silk_SUB32( q2_Q10, 1024 - QUANT_LEVEL_ADJUST_Q10 ); rd1_Q10 = silk_SMULBB( -q1_Q10, Lambda_Q10 ); rd2_Q10 = silk_SMULBB( q2_Q10, Lambda_Q10 ); } else { /* q1_Q0 < -1 */ q1_Q10 = silk_ADD32( silk_LSHIFT( q1_Q0, 10 ), QUANT_LEVEL_ADJUST_Q10 ); q1_Q10 = silk_ADD32( q1_Q10, offset_Q10 ); q2_Q10 = silk_ADD32( q1_Q10, 1024 ); rd1_Q10 = silk_SMULBB( -q1_Q10, Lambda_Q10 ); rd2_Q10 = silk_SMULBB( -q2_Q10, Lambda_Q10 ); } rr_Q10 = silk_SUB32( r_Q10, q1_Q10 ); rd1_Q10 = silk_RSHIFT( silk_SMLABB( rd1_Q10, rr_Q10, rr_Q10 ), 10 ); rr_Q10 = silk_SUB32( r_Q10, q2_Q10 ); rd2_Q10 = silk_RSHIFT( silk_SMLABB( rd2_Q10, rr_Q10, rr_Q10 ), 10 ); if( rd1_Q10 < rd2_Q10 ) { psSS[ 0 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd1_Q10 ); psSS[ 1 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd2_Q10 ); psSS[ 0 ].Q_Q10 = q1_Q10; psSS[ 1 ].Q_Q10 = q2_Q10; } else { psSS[ 0 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd2_Q10 ); psSS[ 1 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd1_Q10 ); psSS[ 0 ].Q_Q10 = q2_Q10; psSS[ 1 ].Q_Q10 = q1_Q10; } /* Update states for best quantization */ /* Quantized excitation */ exc_Q14 = silk_LSHIFT32( psSS[ 0 ].Q_Q10, 4 ); if ( psDD->Seed < 0 ) { exc_Q14 = -exc_Q14; } /* Add predictions */ LPC_exc_Q14 = silk_ADD32( exc_Q14, LTP_pred_Q14 ); xq_Q14 = silk_ADD32( LPC_exc_Q14, LPC_pred_Q14 ); /* Update states */ sLF_AR_shp_Q14 = silk_SUB32( xq_Q14, n_AR_Q14 ); psSS[ 0 ].sLTP_shp_Q14 = silk_SUB32( sLF_AR_shp_Q14, n_LF_Q14 ); psSS[ 0 ].LF_AR_Q14 = sLF_AR_shp_Q14; psSS[ 0 ].LPC_exc_Q14 = LPC_exc_Q14; psSS[ 0 ].xq_Q14 = xq_Q14; /* Update states for second best quantization */ /* Quantized excitation */ exc_Q14 = silk_LSHIFT32( psSS[ 1 ].Q_Q10, 4 ); if ( psDD->Seed < 0 ) { exc_Q14 = -exc_Q14; } /* Add predictions */ LPC_exc_Q14 = silk_ADD32( exc_Q14, LTP_pred_Q14 ); xq_Q14 = silk_ADD32( LPC_exc_Q14, LPC_pred_Q14 ); /* Update states */ sLF_AR_shp_Q14 = silk_SUB32( xq_Q14, n_AR_Q14 ); psSS[ 1 ].sLTP_shp_Q14 = silk_SUB32( sLF_AR_shp_Q14, n_LF_Q14 ); psSS[ 1 ].LF_AR_Q14 = sLF_AR_shp_Q14; psSS[ 1 ].LPC_exc_Q14 = LPC_exc_Q14; psSS[ 1 ].xq_Q14 = xq_Q14; } *smpl_buf_idx = ( *smpl_buf_idx - 1 ) & DECISION_DELAY_MASK; /* Index to newest samples */ last_smple_idx = ( *smpl_buf_idx + decisionDelay ) & DECISION_DELAY_MASK; /* Index to decisionDelay old samples */ /* Find winner */ RDmin_Q10 = psSampleState[ 0 ][ 0 ].RD_Q10; Winner_ind = 0; for( k = 1; k < nStatesDelayedDecision; k++ ) { if( psSampleState[ k ][ 0 ].RD_Q10 < RDmin_Q10 ) { RDmin_Q10 = psSampleState[ k ][ 0 ].RD_Q10; Winner_ind = k; } } /* Increase RD values of expired states */ Winner_rand_state = psDelDec[ Winner_ind ].RandState[ last_smple_idx ]; for( k = 0; k < nStatesDelayedDecision; k++ ) { if( psDelDec[ k ].RandState[ last_smple_idx ] != Winner_rand_state ) { psSampleState[ k ][ 0 ].RD_Q10 = silk_ADD32( psSampleState[ k ][ 0 ].RD_Q10, silk_int32_MAX >> 4 ); psSampleState[ k ][ 1 ].RD_Q10 = silk_ADD32( psSampleState[ k ][ 1 ].RD_Q10, silk_int32_MAX >> 4 ); silk_assert( psSampleState[ k ][ 0 ].RD_Q10 >= 0 ); } } /* Find worst in first set and best in second set */ RDmax_Q10 = psSampleState[ 0 ][ 0 ].RD_Q10; RDmin_Q10 = psSampleState[ 0 ][ 1 ].RD_Q10; RDmax_ind = 0; RDmin_ind = 0; for( k = 1; k < nStatesDelayedDecision; k++ ) { /* find worst in first set */ if( psSampleState[ k ][ 0 ].RD_Q10 > RDmax_Q10 ) { RDmax_Q10 = psSampleState[ k ][ 0 ].RD_Q10; RDmax_ind = k; } /* find best in second set */ if( psSampleState[ k ][ 1 ].RD_Q10 < RDmin_Q10 ) { RDmin_Q10 = psSampleState[ k ][ 1 ].RD_Q10; RDmin_ind = k; } } /* Replace a state if best from second set outperforms worst in first set */ if( RDmin_Q10 < RDmax_Q10 ) { silk_memcpy( ( (opus_int32 *)&psDelDec[ RDmax_ind ] ) + i, ( (opus_int32 *)&psDelDec[ RDmin_ind ] ) + i, sizeof( NSQ_del_dec_struct ) - i * sizeof( opus_int32) ); silk_memcpy( &psSampleState[ RDmax_ind ][ 0 ], &psSampleState[ RDmin_ind ][ 1 ], sizeof( NSQ_sample_struct ) ); } /* Write samples from winner to output and long-term filter states */ psDD = &psDelDec[ Winner_ind ]; if( subfr > 0 || i >= decisionDelay ) { pulses[ i - decisionDelay ] = (opus_int8)silk_RSHIFT_ROUND( psDD->Q_Q10[ last_smple_idx ], 10 ); xq[ i - decisionDelay ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_SMULWW( psDD->Xq_Q14[ last_smple_idx ], delayedGain_Q10[ last_smple_idx ] ), 8 ) ); NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - decisionDelay ] = psDD->Shape_Q14[ last_smple_idx ]; sLTP_Q15[ NSQ->sLTP_buf_idx - decisionDelay ] = psDD->Pred_Q15[ last_smple_idx ]; } NSQ->sLTP_shp_buf_idx++; NSQ->sLTP_buf_idx++; /* Update states */ for( k = 0; k < nStatesDelayedDecision; k++ ) { psDD = &psDelDec[ k ]; psSS = &psSampleState[ k ][ 0 ]; psDD->LF_AR_Q14 = psSS->LF_AR_Q14; psDD->sLPC_Q14[ NSQ_LPC_BUF_LENGTH + i ] = psSS->xq_Q14; psDD->Xq_Q14[ *smpl_buf_idx ] = psSS->xq_Q14; psDD->Q_Q10[ *smpl_buf_idx ] = psSS->Q_Q10; psDD->Pred_Q15[ *smpl_buf_idx ] = silk_LSHIFT32( psSS->LPC_exc_Q14, 1 ); psDD->Shape_Q14[ *smpl_buf_idx ] = psSS->sLTP_shp_Q14; psDD->Seed = silk_ADD32_ovflw( psDD->Seed, silk_RSHIFT_ROUND( psSS->Q_Q10, 10 ) ); psDD->RandState[ *smpl_buf_idx ] = psDD->Seed; psDD->RD_Q10 = psSS->RD_Q10; } delayedGain_Q10[ *smpl_buf_idx ] = Gain_Q10; } /* Update LPC states */ for( k = 0; k < nStatesDelayedDecision; k++ ) { psDD = &psDelDec[ k ]; silk_memcpy( psDD->sLPC_Q14, &psDD->sLPC_Q14[ length ], NSQ_LPC_BUF_LENGTH * sizeof( opus_int32 ) ); } } #endif /* __NSQ_DEL_DEC_MIPSR1_H__ */ ================================================ FILE: deps/pjsip/third_party/opus/silk/mips/macros_mipsr1.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef __SILK_MACROS_MIPSR1_H__ #define __SILK_MACROS_MIPSR1_H__ #define mips_clz(x) __builtin_clz(x) #undef silk_SMULWB static inline int silk_SMULWB(int a, int b) { long long ac; int c; ac = __builtin_mips_mult(a, (opus_int32)(opus_int16)b); c = __builtin_mips_extr_w(ac, 16); return c; } #undef silk_SMLAWB #define silk_SMLAWB(a32, b32, c32) ((a32) + silk_SMULWB(b32, c32)) #undef silk_SMULWW static inline int silk_SMULWW(int a, int b) { long long ac; int c; ac = __builtin_mips_mult(a, b); c = __builtin_mips_extr_w(ac, 16); return c; } #undef silk_SMLAWW static inline int silk_SMLAWW(int a, int b, int c) { long long ac; int res; ac = __builtin_mips_mult(b, c); res = __builtin_mips_extr_w(ac, 16); res += a; return res; } #define OVERRIDE_silk_CLZ16 static inline opus_int32 silk_CLZ16(opus_int16 in16) { int re32; opus_int32 in32 = (opus_int32 )in16; re32 = mips_clz(in32); re32-=16; return re32; } #define OVERRIDE_silk_CLZ32 static inline opus_int32 silk_CLZ32(opus_int32 in32) { int re32; re32 = mips_clz(in32); return re32; } #endif /* __SILK_MACROS_MIPSR1_H__ */ ================================================ FILE: deps/pjsip/third_party/opus/silk/mips/sigproc_fix_mipsr1.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_SIGPROC_FIX_MIPSR1_H #define SILK_SIGPROC_FIX_MIPSR1_H #ifdef __cplusplus extern "C" { #endif #undef silk_SAT16 static inline short int silk_SAT16(int a) { int c; c = __builtin_mips_shll_s_w(a, 16); c = c>>16; return c; } #undef silk_LSHIFT_SAT32 static inline int silk_LSHIFT_SAT32(int a, int shift) { int r; r = __builtin_mips_shll_s_w(a, shift); return r; } #undef silk_RSHIFT_ROUND static inline int silk_RSHIFT_ROUND(int a, int shift) { int r; r = __builtin_mips_shra_r_w(a, shift); return r; } #endif /* SILK_SIGPROC_FIX_MIPSR1_H */ ================================================ FILE: deps/pjsip/third_party/opus/silk/pitch_est_defines.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_PE_DEFINES_H #define SILK_PE_DEFINES_H #include "SigProc_FIX.h" /********************************************************/ /* Definitions for pitch estimator */ /********************************************************/ #define PE_MAX_FS_KHZ 16 /* Maximum sampling frequency used */ #define PE_MAX_NB_SUBFR 4 #define PE_SUBFR_LENGTH_MS 5 /* 5 ms */ #define PE_LTP_MEM_LENGTH_MS ( 4 * PE_SUBFR_LENGTH_MS ) #define PE_MAX_FRAME_LENGTH_MS ( PE_LTP_MEM_LENGTH_MS + PE_MAX_NB_SUBFR * PE_SUBFR_LENGTH_MS ) #define PE_MAX_FRAME_LENGTH ( PE_MAX_FRAME_LENGTH_MS * PE_MAX_FS_KHZ ) #define PE_MAX_FRAME_LENGTH_ST_1 ( PE_MAX_FRAME_LENGTH >> 2 ) #define PE_MAX_FRAME_LENGTH_ST_2 ( PE_MAX_FRAME_LENGTH >> 1 ) #define PE_MAX_LAG_MS 18 /* 18 ms -> 56 Hz */ #define PE_MIN_LAG_MS 2 /* 2 ms -> 500 Hz */ #define PE_MAX_LAG ( PE_MAX_LAG_MS * PE_MAX_FS_KHZ ) #define PE_MIN_LAG ( PE_MIN_LAG_MS * PE_MAX_FS_KHZ ) #define PE_D_SRCH_LENGTH 24 #define PE_NB_STAGE3_LAGS 5 #define PE_NB_CBKS_STAGE2 3 #define PE_NB_CBKS_STAGE2_EXT 11 #define PE_NB_CBKS_STAGE3_MAX 34 #define PE_NB_CBKS_STAGE3_MID 24 #define PE_NB_CBKS_STAGE3_MIN 16 #define PE_NB_CBKS_STAGE3_10MS 12 #define PE_NB_CBKS_STAGE2_10MS 3 #define PE_SHORTLAG_BIAS 0.2f /* for logarithmic weighting */ #define PE_PREVLAG_BIAS 0.2f /* for logarithmic weighting */ #define PE_FLATCONTOUR_BIAS 0.05f #define SILK_PE_MIN_COMPLEX 0 #define SILK_PE_MID_COMPLEX 1 #define SILK_PE_MAX_COMPLEX 2 /* Tables for 20 ms frames */ extern const opus_int8 silk_CB_lags_stage2[ PE_MAX_NB_SUBFR ][ PE_NB_CBKS_STAGE2_EXT ]; extern const opus_int8 silk_CB_lags_stage3[ PE_MAX_NB_SUBFR ][ PE_NB_CBKS_STAGE3_MAX ]; extern const opus_int8 silk_Lag_range_stage3[ SILK_PE_MAX_COMPLEX + 1 ] [ PE_MAX_NB_SUBFR ][ 2 ]; extern const opus_int8 silk_nb_cbk_searchs_stage3[ SILK_PE_MAX_COMPLEX + 1 ]; /* Tables for 10 ms frames */ extern const opus_int8 silk_CB_lags_stage2_10_ms[ PE_MAX_NB_SUBFR >> 1][ 3 ]; extern const opus_int8 silk_CB_lags_stage3_10_ms[ PE_MAX_NB_SUBFR >> 1 ][ 12 ]; extern const opus_int8 silk_Lag_range_stage3_10_ms[ PE_MAX_NB_SUBFR >> 1 ][ 2 ]; #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/pitch_est_tables.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "typedef.h" #include "pitch_est_defines.h" const opus_int8 silk_CB_lags_stage2_10_ms[ PE_MAX_NB_SUBFR >> 1][ PE_NB_CBKS_STAGE2_10MS ] = { {0, 1, 0}, {0, 0, 1} }; const opus_int8 silk_CB_lags_stage3_10_ms[ PE_MAX_NB_SUBFR >> 1 ][ PE_NB_CBKS_STAGE3_10MS ] = { { 0, 0, 1,-1, 1,-1, 2,-2, 2,-2, 3,-3}, { 0, 1, 0, 1,-1, 2,-1, 2,-2, 3,-2, 3} }; const opus_int8 silk_Lag_range_stage3_10_ms[ PE_MAX_NB_SUBFR >> 1 ][ 2 ] = { {-3, 7}, {-2, 7} }; const opus_int8 silk_CB_lags_stage2[ PE_MAX_NB_SUBFR ][ PE_NB_CBKS_STAGE2_EXT ] = { {0, 2,-1,-1,-1, 0, 0, 1, 1, 0, 1}, {0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0}, {0,-1, 2, 1, 0, 1, 1, 0, 0,-1,-1} }; const opus_int8 silk_CB_lags_stage3[ PE_MAX_NB_SUBFR ][ PE_NB_CBKS_STAGE3_MAX ] = { {0, 0, 1,-1, 0, 1,-1, 0,-1, 1,-2, 2,-2,-2, 2,-3, 2, 3,-3,-4, 3,-4, 4, 4,-5, 5,-6,-5, 6,-7, 6, 5, 8,-9}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0,-1, 1, 0, 0, 1,-1, 0, 1,-1,-1, 1,-1, 2, 1,-1, 2,-2,-2, 2,-2, 2, 2, 3,-3}, {0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1,-1, 1, 0, 0, 2, 1,-1, 2,-1,-1, 2,-1, 2, 2,-1, 3,-2,-2,-2, 3}, {0, 1, 0, 0, 1, 0, 1,-1, 2,-1, 2,-1, 2, 3,-2, 3,-2,-2, 4, 4,-3, 5,-3,-4, 6,-4, 6, 5,-5, 8,-6,-5,-7, 9} }; const opus_int8 silk_Lag_range_stage3[ SILK_PE_MAX_COMPLEX + 1 ] [ PE_MAX_NB_SUBFR ][ 2 ] = { /* Lags to search for low number of stage3 cbks */ { {-5,8}, {-1,6}, {-1,6}, {-4,10} }, /* Lags to search for middle number of stage3 cbks */ { {-6,10}, {-2,6}, {-1,6}, {-5,10} }, /* Lags to search for max number of stage3 cbks */ { {-9,12}, {-3,7}, {-2,7}, {-7,13} } }; const opus_int8 silk_nb_cbk_searchs_stage3[ SILK_PE_MAX_COMPLEX + 1 ] = { PE_NB_CBKS_STAGE3_MIN, PE_NB_CBKS_STAGE3_MID, PE_NB_CBKS_STAGE3_MAX }; ================================================ FILE: deps/pjsip/third_party/opus/silk/process_NLSFs.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Limit, stabilize, convert and quantize NLSFs */ void silk_process_NLSFs( silk_encoder_state *psEncC, /* I/O Encoder state */ opus_int16 PredCoef_Q12[ 2 ][ MAX_LPC_ORDER ], /* O Prediction coefficients */ opus_int16 pNLSF_Q15[ MAX_LPC_ORDER ], /* I/O Normalized LSFs (quant out) (0 - (2^15-1)) */ const opus_int16 prev_NLSFq_Q15[ MAX_LPC_ORDER ] /* I Previous Normalized LSFs (0 - (2^15-1)) */ ) { opus_int i, doInterpolate; opus_int NLSF_mu_Q20; opus_int32 i_sqr_Q15; opus_int16 pNLSF0_temp_Q15[ MAX_LPC_ORDER ]; opus_int16 pNLSFW_QW[ MAX_LPC_ORDER ]; opus_int16 pNLSFW0_temp_QW[ MAX_LPC_ORDER ]; silk_assert( psEncC->speech_activity_Q8 >= 0 ); silk_assert( psEncC->speech_activity_Q8 <= SILK_FIX_CONST( 1.0, 8 ) ); silk_assert( psEncC->useInterpolatedNLSFs == 1 || psEncC->indices.NLSFInterpCoef_Q2 == ( 1 << 2 ) ); /***********************/ /* Calculate mu values */ /***********************/ /* NLSF_mu = 0.003 - 0.0015 * psEnc->speech_activity; */ NLSF_mu_Q20 = silk_SMLAWB( SILK_FIX_CONST( 0.003, 20 ), SILK_FIX_CONST( -0.001, 28 ), psEncC->speech_activity_Q8 ); if( psEncC->nb_subfr == 2 ) { /* Multiply by 1.5 for 10 ms packets */ NLSF_mu_Q20 = silk_ADD_RSHIFT( NLSF_mu_Q20, NLSF_mu_Q20, 1 ); } silk_assert( NLSF_mu_Q20 > 0 ); silk_assert( NLSF_mu_Q20 <= SILK_FIX_CONST( 0.005, 20 ) ); /* Calculate NLSF weights */ silk_NLSF_VQ_weights_laroia( pNLSFW_QW, pNLSF_Q15, psEncC->predictLPCOrder ); /* Update NLSF weights for interpolated NLSFs */ doInterpolate = ( psEncC->useInterpolatedNLSFs == 1 ) && ( psEncC->indices.NLSFInterpCoef_Q2 < 4 ); if( doInterpolate ) { /* Calculate the interpolated NLSF vector for the first half */ silk_interpolate( pNLSF0_temp_Q15, prev_NLSFq_Q15, pNLSF_Q15, psEncC->indices.NLSFInterpCoef_Q2, psEncC->predictLPCOrder ); /* Calculate first half NLSF weights for the interpolated NLSFs */ silk_NLSF_VQ_weights_laroia( pNLSFW0_temp_QW, pNLSF0_temp_Q15, psEncC->predictLPCOrder ); /* Update NLSF weights with contribution from first half */ i_sqr_Q15 = silk_LSHIFT( silk_SMULBB( psEncC->indices.NLSFInterpCoef_Q2, psEncC->indices.NLSFInterpCoef_Q2 ), 11 ); for( i = 0; i < psEncC->predictLPCOrder; i++ ) { pNLSFW_QW[ i ] = silk_SMLAWB( silk_RSHIFT( pNLSFW_QW[ i ], 1 ), (opus_int32)pNLSFW0_temp_QW[ i ], i_sqr_Q15 ); silk_assert( pNLSFW_QW[ i ] >= 1 ); } } silk_NLSF_encode( psEncC->indices.NLSFIndices, pNLSF_Q15, psEncC->psNLSF_CB, pNLSFW_QW, NLSF_mu_Q20, psEncC->NLSF_MSVQ_Survivors, psEncC->indices.signalType ); /* Convert quantized NLSFs back to LPC coefficients */ silk_NLSF2A( PredCoef_Q12[ 1 ], pNLSF_Q15, psEncC->predictLPCOrder ); if( doInterpolate ) { /* Calculate the interpolated, quantized LSF vector for the first half */ silk_interpolate( pNLSF0_temp_Q15, prev_NLSFq_Q15, pNLSF_Q15, psEncC->indices.NLSFInterpCoef_Q2, psEncC->predictLPCOrder ); /* Convert back to LPC coefficients */ silk_NLSF2A( PredCoef_Q12[ 0 ], pNLSF0_temp_Q15, psEncC->predictLPCOrder ); } else { /* Copy LPC coefficients for first half from second half */ silk_memcpy( PredCoef_Q12[ 0 ], PredCoef_Q12[ 1 ], psEncC->predictLPCOrder * sizeof( opus_int16 ) ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/quant_LTP_gains.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #include "tuning_parameters.h" void silk_quant_LTP_gains( opus_int16 B_Q14[ MAX_NB_SUBFR * LTP_ORDER ], /* I/O (un)quantized LTP gains */ opus_int8 cbk_index[ MAX_NB_SUBFR ], /* O Codebook Index */ opus_int8 *periodicity_index, /* O Periodicity Index */ opus_int32 *sum_log_gain_Q7, /* I/O Cumulative max prediction gain */ const opus_int32 W_Q18[ MAX_NB_SUBFR*LTP_ORDER*LTP_ORDER ], /* I Error Weights in Q18 */ opus_int mu_Q9, /* I Mu value (R/D tradeoff) */ opus_int lowComplexity, /* I Flag for low complexity */ const opus_int nb_subfr, /* I number of subframes */ int arch /* I Run-time architecture */ ) { opus_int j, k, cbk_size; opus_int8 temp_idx[ MAX_NB_SUBFR ]; const opus_uint8 *cl_ptr_Q5; const opus_int8 *cbk_ptr_Q7; const opus_uint8 *cbk_gain_ptr_Q7; const opus_int16 *b_Q14_ptr; const opus_int32 *W_Q18_ptr; opus_int32 rate_dist_Q14_subfr, rate_dist_Q14, min_rate_dist_Q14; opus_int32 sum_log_gain_tmp_Q7, best_sum_log_gain_Q7, max_gain_Q7, gain_Q7; /***************************************************/ /* iterate over different codebooks with different */ /* rates/distortions, and choose best */ /***************************************************/ min_rate_dist_Q14 = silk_int32_MAX; best_sum_log_gain_Q7 = 0; for( k = 0; k < 3; k++ ) { /* Safety margin for pitch gain control, to take into account factors such as state rescaling/rewhitening. */ opus_int32 gain_safety = SILK_FIX_CONST( 0.4, 7 ); cl_ptr_Q5 = silk_LTP_gain_BITS_Q5_ptrs[ k ]; cbk_ptr_Q7 = silk_LTP_vq_ptrs_Q7[ k ]; cbk_gain_ptr_Q7 = silk_LTP_vq_gain_ptrs_Q7[ k ]; cbk_size = silk_LTP_vq_sizes[ k ]; /* Set up pointer to first subframe */ W_Q18_ptr = W_Q18; b_Q14_ptr = B_Q14; rate_dist_Q14 = 0; sum_log_gain_tmp_Q7 = *sum_log_gain_Q7; for( j = 0; j < nb_subfr; j++ ) { max_gain_Q7 = silk_log2lin( ( SILK_FIX_CONST( MAX_SUM_LOG_GAIN_DB / 6.0, 7 ) - sum_log_gain_tmp_Q7 ) + SILK_FIX_CONST( 7, 7 ) ) - gain_safety; silk_VQ_WMat_EC( &temp_idx[ j ], /* O index of best codebook vector */ &rate_dist_Q14_subfr, /* O best weighted quantization error + mu * rate */ &gain_Q7, /* O sum of absolute LTP coefficients */ b_Q14_ptr, /* I input vector to be quantized */ W_Q18_ptr, /* I weighting matrix */ cbk_ptr_Q7, /* I codebook */ cbk_gain_ptr_Q7, /* I codebook effective gains */ cl_ptr_Q5, /* I code length for each codebook vector */ mu_Q9, /* I tradeoff between weighted error and rate */ max_gain_Q7, /* I maximum sum of absolute LTP coefficients */ cbk_size, /* I number of vectors in codebook */ arch /* I Run-time architecture */ ); rate_dist_Q14 = silk_ADD_POS_SAT32( rate_dist_Q14, rate_dist_Q14_subfr ); sum_log_gain_tmp_Q7 = silk_max(0, sum_log_gain_tmp_Q7 + silk_lin2log( gain_safety + gain_Q7 ) - SILK_FIX_CONST( 7, 7 )); b_Q14_ptr += LTP_ORDER; W_Q18_ptr += LTP_ORDER * LTP_ORDER; } /* Avoid never finding a codebook */ rate_dist_Q14 = silk_min( silk_int32_MAX - 1, rate_dist_Q14 ); if( rate_dist_Q14 < min_rate_dist_Q14 ) { min_rate_dist_Q14 = rate_dist_Q14; *periodicity_index = (opus_int8)k; silk_memcpy( cbk_index, temp_idx, nb_subfr * sizeof( opus_int8 ) ); best_sum_log_gain_Q7 = sum_log_gain_tmp_Q7; } /* Break early in low-complexity mode if rate distortion is below threshold */ if( lowComplexity && ( rate_dist_Q14 < silk_LTP_gain_middle_avg_RD_Q14 ) ) { break; } } cbk_ptr_Q7 = silk_LTP_vq_ptrs_Q7[ *periodicity_index ]; for( j = 0; j < nb_subfr; j++ ) { for( k = 0; k < LTP_ORDER; k++ ) { B_Q14[ j * LTP_ORDER + k ] = silk_LSHIFT( cbk_ptr_Q7[ cbk_index[ j ] * LTP_ORDER + k ], 7 ); } } *sum_log_gain_Q7 = best_sum_log_gain_Q7; } ================================================ FILE: deps/pjsip/third_party/opus/silk/resampler.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* * Matrix of resampling methods used: * Fs_out (kHz) * 8 12 16 24 48 * * 8 C UF U UF UF * 12 AF C UF U UF * Fs_in (kHz) 16 D AF C UF UF * 24 AF D AF C U * 48 AF AF AF D C * * C -> Copy (no resampling) * D -> Allpass-based 2x downsampling * U -> Allpass-based 2x upsampling * UF -> Allpass-based 2x upsampling followed by FIR interpolation * AF -> AR2 filter followed by FIR interpolation */ #include "resampler_private.h" /* Tables with delay compensation values to equalize total delay for different modes */ static const opus_int8 delay_matrix_enc[ 5 ][ 3 ] = { /* in \ out 8 12 16 */ /* 8 */ { 6, 0, 3 }, /* 12 */ { 0, 7, 3 }, /* 16 */ { 0, 1, 10 }, /* 24 */ { 0, 2, 6 }, /* 48 */ { 18, 10, 12 } }; static const opus_int8 delay_matrix_dec[ 3 ][ 5 ] = { /* in \ out 8 12 16 24 48 */ /* 8 */ { 4, 0, 2, 0, 0 }, /* 12 */ { 0, 9, 4, 7, 4 }, /* 16 */ { 0, 3, 12, 7, 7 } }; /* Simple way to make [8000, 12000, 16000, 24000, 48000] to [0, 1, 2, 3, 4] */ #define rateID(R) ( ( ( ((R)>>12) - ((R)>16000) ) >> ((R)>24000) ) - 1 ) #define USE_silk_resampler_copy (0) #define USE_silk_resampler_private_up2_HQ_wrapper (1) #define USE_silk_resampler_private_IIR_FIR (2) #define USE_silk_resampler_private_down_FIR (3) /* Initialize/reset the resampler state for a given pair of input/output sampling rates */ opus_int silk_resampler_init( silk_resampler_state_struct *S, /* I/O Resampler state */ opus_int32 Fs_Hz_in, /* I Input sampling rate (Hz) */ opus_int32 Fs_Hz_out, /* I Output sampling rate (Hz) */ opus_int forEnc /* I If 1: encoder; if 0: decoder */ ) { opus_int up2x; /* Clear state */ silk_memset( S, 0, sizeof( silk_resampler_state_struct ) ); /* Input checking */ if( forEnc ) { if( ( Fs_Hz_in != 8000 && Fs_Hz_in != 12000 && Fs_Hz_in != 16000 && Fs_Hz_in != 24000 && Fs_Hz_in != 48000 ) || ( Fs_Hz_out != 8000 && Fs_Hz_out != 12000 && Fs_Hz_out != 16000 ) ) { silk_assert( 0 ); return -1; } S->inputDelay = delay_matrix_enc[ rateID( Fs_Hz_in ) ][ rateID( Fs_Hz_out ) ]; } else { if( ( Fs_Hz_in != 8000 && Fs_Hz_in != 12000 && Fs_Hz_in != 16000 ) || ( Fs_Hz_out != 8000 && Fs_Hz_out != 12000 && Fs_Hz_out != 16000 && Fs_Hz_out != 24000 && Fs_Hz_out != 48000 ) ) { silk_assert( 0 ); return -1; } S->inputDelay = delay_matrix_dec[ rateID( Fs_Hz_in ) ][ rateID( Fs_Hz_out ) ]; } S->Fs_in_kHz = silk_DIV32_16( Fs_Hz_in, 1000 ); S->Fs_out_kHz = silk_DIV32_16( Fs_Hz_out, 1000 ); /* Number of samples processed per batch */ S->batchSize = S->Fs_in_kHz * RESAMPLER_MAX_BATCH_SIZE_MS; /* Find resampler with the right sampling ratio */ up2x = 0; if( Fs_Hz_out > Fs_Hz_in ) { /* Upsample */ if( Fs_Hz_out == silk_MUL( Fs_Hz_in, 2 ) ) { /* Fs_out : Fs_in = 2 : 1 */ /* Special case: directly use 2x upsampler */ S->resampler_function = USE_silk_resampler_private_up2_HQ_wrapper; } else { /* Default resampler */ S->resampler_function = USE_silk_resampler_private_IIR_FIR; up2x = 1; } } else if ( Fs_Hz_out < Fs_Hz_in ) { /* Downsample */ S->resampler_function = USE_silk_resampler_private_down_FIR; if( silk_MUL( Fs_Hz_out, 4 ) == silk_MUL( Fs_Hz_in, 3 ) ) { /* Fs_out : Fs_in = 3 : 4 */ S->FIR_Fracs = 3; S->FIR_Order = RESAMPLER_DOWN_ORDER_FIR0; S->Coefs = silk_Resampler_3_4_COEFS; } else if( silk_MUL( Fs_Hz_out, 3 ) == silk_MUL( Fs_Hz_in, 2 ) ) { /* Fs_out : Fs_in = 2 : 3 */ S->FIR_Fracs = 2; S->FIR_Order = RESAMPLER_DOWN_ORDER_FIR0; S->Coefs = silk_Resampler_2_3_COEFS; } else if( silk_MUL( Fs_Hz_out, 2 ) == Fs_Hz_in ) { /* Fs_out : Fs_in = 1 : 2 */ S->FIR_Fracs = 1; S->FIR_Order = RESAMPLER_DOWN_ORDER_FIR1; S->Coefs = silk_Resampler_1_2_COEFS; } else if( silk_MUL( Fs_Hz_out, 3 ) == Fs_Hz_in ) { /* Fs_out : Fs_in = 1 : 3 */ S->FIR_Fracs = 1; S->FIR_Order = RESAMPLER_DOWN_ORDER_FIR2; S->Coefs = silk_Resampler_1_3_COEFS; } else if( silk_MUL( Fs_Hz_out, 4 ) == Fs_Hz_in ) { /* Fs_out : Fs_in = 1 : 4 */ S->FIR_Fracs = 1; S->FIR_Order = RESAMPLER_DOWN_ORDER_FIR2; S->Coefs = silk_Resampler_1_4_COEFS; } else if( silk_MUL( Fs_Hz_out, 6 ) == Fs_Hz_in ) { /* Fs_out : Fs_in = 1 : 6 */ S->FIR_Fracs = 1; S->FIR_Order = RESAMPLER_DOWN_ORDER_FIR2; S->Coefs = silk_Resampler_1_6_COEFS; } else { /* None available */ silk_assert( 0 ); return -1; } } else { /* Input and output sampling rates are equal: copy */ S->resampler_function = USE_silk_resampler_copy; } /* Ratio of input/output samples */ S->invRatio_Q16 = silk_LSHIFT32( silk_DIV32( silk_LSHIFT32( Fs_Hz_in, 14 + up2x ), Fs_Hz_out ), 2 ); /* Make sure the ratio is rounded up */ while( silk_SMULWW( S->invRatio_Q16, Fs_Hz_out ) < silk_LSHIFT32( Fs_Hz_in, up2x ) ) { S->invRatio_Q16++; } return 0; } /* Resampler: convert from one sampling rate to another */ /* Input and output sampling rate are at most 48000 Hz */ opus_int silk_resampler( silk_resampler_state_struct *S, /* I/O Resampler state */ opus_int16 out[], /* O Output signal */ const opus_int16 in[], /* I Input signal */ opus_int32 inLen /* I Number of input samples */ ) { opus_int nSamples; /* Need at least 1 ms of input data */ silk_assert( inLen >= S->Fs_in_kHz ); /* Delay can't exceed the 1 ms of buffering */ silk_assert( S->inputDelay <= S->Fs_in_kHz ); nSamples = S->Fs_in_kHz - S->inputDelay; /* Copy to delay buffer */ silk_memcpy( &S->delayBuf[ S->inputDelay ], in, nSamples * sizeof( opus_int16 ) ); switch( S->resampler_function ) { case USE_silk_resampler_private_up2_HQ_wrapper: silk_resampler_private_up2_HQ_wrapper( S, out, S->delayBuf, S->Fs_in_kHz ); silk_resampler_private_up2_HQ_wrapper( S, &out[ S->Fs_out_kHz ], &in[ nSamples ], inLen - S->Fs_in_kHz ); break; case USE_silk_resampler_private_IIR_FIR: silk_resampler_private_IIR_FIR( S, out, S->delayBuf, S->Fs_in_kHz ); silk_resampler_private_IIR_FIR( S, &out[ S->Fs_out_kHz ], &in[ nSamples ], inLen - S->Fs_in_kHz ); break; case USE_silk_resampler_private_down_FIR: silk_resampler_private_down_FIR( S, out, S->delayBuf, S->Fs_in_kHz ); silk_resampler_private_down_FIR( S, &out[ S->Fs_out_kHz ], &in[ nSamples ], inLen - S->Fs_in_kHz ); break; default: silk_memcpy( out, S->delayBuf, S->Fs_in_kHz * sizeof( opus_int16 ) ); silk_memcpy( &out[ S->Fs_out_kHz ], &in[ nSamples ], ( inLen - S->Fs_in_kHz ) * sizeof( opus_int16 ) ); } /* Copy to delay buffer */ silk_memcpy( S->delayBuf, &in[ inLen - S->inputDelay ], S->inputDelay * sizeof( opus_int16 ) ); return 0; } ================================================ FILE: deps/pjsip/third_party/opus/silk/resampler_down2.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" #include "resampler_rom.h" /* Downsample by a factor 2 */ void silk_resampler_down2( opus_int32 *S, /* I/O State vector [ 2 ] */ opus_int16 *out, /* O Output signal [ floor(len/2) ] */ const opus_int16 *in, /* I Input signal [ len ] */ opus_int32 inLen /* I Number of input samples */ ) { opus_int32 k, len2 = silk_RSHIFT32( inLen, 1 ); opus_int32 in32, out32, Y, X; silk_assert( silk_resampler_down2_0 > 0 ); silk_assert( silk_resampler_down2_1 < 0 ); /* Internal variables and state are in Q10 format */ for( k = 0; k < len2; k++ ) { /* Convert to Q10 */ in32 = silk_LSHIFT( (opus_int32)in[ 2 * k ], 10 ); /* All-pass section for even input sample */ Y = silk_SUB32( in32, S[ 0 ] ); X = silk_SMLAWB( Y, Y, silk_resampler_down2_1 ); out32 = silk_ADD32( S[ 0 ], X ); S[ 0 ] = silk_ADD32( in32, X ); /* Convert to Q10 */ in32 = silk_LSHIFT( (opus_int32)in[ 2 * k + 1 ], 10 ); /* All-pass section for odd input sample, and add to output of previous section */ Y = silk_SUB32( in32, S[ 1 ] ); X = silk_SMULWB( Y, silk_resampler_down2_0 ); out32 = silk_ADD32( out32, S[ 1 ] ); out32 = silk_ADD32( out32, X ); S[ 1 ] = silk_ADD32( in32, X ); /* Add, convert back to int16 and store to output */ out[ k ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( out32, 11 ) ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/resampler_down2_3.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" #include "resampler_private.h" #include "stack_alloc.h" #define ORDER_FIR 4 /* Downsample by a factor 2/3, low quality */ void silk_resampler_down2_3( opus_int32 *S, /* I/O State vector [ 6 ] */ opus_int16 *out, /* O Output signal [ floor(2*inLen/3) ] */ const opus_int16 *in, /* I Input signal [ inLen ] */ opus_int32 inLen /* I Number of input samples */ ) { opus_int32 nSamplesIn, counter, res_Q6; VARDECL( opus_int32, buf ); opus_int32 *buf_ptr; SAVE_STACK; ALLOC( buf, RESAMPLER_MAX_BATCH_SIZE_IN + ORDER_FIR, opus_int32 ); /* Copy buffered samples to start of buffer */ silk_memcpy( buf, S, ORDER_FIR * sizeof( opus_int32 ) ); /* Iterate over blocks of frameSizeIn input samples */ while( 1 ) { nSamplesIn = silk_min( inLen, RESAMPLER_MAX_BATCH_SIZE_IN ); /* Second-order AR filter (output in Q8) */ silk_resampler_private_AR2( &S[ ORDER_FIR ], &buf[ ORDER_FIR ], in, silk_Resampler_2_3_COEFS_LQ, nSamplesIn ); /* Interpolate filtered signal */ buf_ptr = buf; counter = nSamplesIn; while( counter > 2 ) { /* Inner product */ res_Q6 = silk_SMULWB( buf_ptr[ 0 ], silk_Resampler_2_3_COEFS_LQ[ 2 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 1 ], silk_Resampler_2_3_COEFS_LQ[ 3 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 2 ], silk_Resampler_2_3_COEFS_LQ[ 5 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 3 ], silk_Resampler_2_3_COEFS_LQ[ 4 ] ); /* Scale down, saturate and store in output array */ *out++ = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( res_Q6, 6 ) ); res_Q6 = silk_SMULWB( buf_ptr[ 1 ], silk_Resampler_2_3_COEFS_LQ[ 4 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 2 ], silk_Resampler_2_3_COEFS_LQ[ 5 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 3 ], silk_Resampler_2_3_COEFS_LQ[ 3 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 4 ], silk_Resampler_2_3_COEFS_LQ[ 2 ] ); /* Scale down, saturate and store in output array */ *out++ = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( res_Q6, 6 ) ); buf_ptr += 3; counter -= 3; } in += nSamplesIn; inLen -= nSamplesIn; if( inLen > 0 ) { /* More iterations to do; copy last part of filtered signal to beginning of buffer */ silk_memcpy( buf, &buf[ nSamplesIn ], ORDER_FIR * sizeof( opus_int32 ) ); } else { break; } } /* Copy last part of filtered signal to the state for the next call */ silk_memcpy( S, &buf[ nSamplesIn ], ORDER_FIR * sizeof( opus_int32 ) ); RESTORE_STACK; } ================================================ FILE: deps/pjsip/third_party/opus/silk/resampler_private.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_RESAMPLER_PRIVATE_H #define SILK_RESAMPLER_PRIVATE_H #ifdef __cplusplus extern "C" { #endif #include "SigProc_FIX.h" #include "resampler_structs.h" #include "resampler_rom.h" /* Number of input samples to process in the inner loop */ #define RESAMPLER_MAX_BATCH_SIZE_MS 10 #define RESAMPLER_MAX_FS_KHZ 48 #define RESAMPLER_MAX_BATCH_SIZE_IN ( RESAMPLER_MAX_BATCH_SIZE_MS * RESAMPLER_MAX_FS_KHZ ) /* Description: Hybrid IIR/FIR polyphase implementation of resampling */ void silk_resampler_private_IIR_FIR( void *SS, /* I/O Resampler state */ opus_int16 out[], /* O Output signal */ const opus_int16 in[], /* I Input signal */ opus_int32 inLen /* I Number of input samples */ ); /* Description: Hybrid IIR/FIR polyphase implementation of resampling */ void silk_resampler_private_down_FIR( void *SS, /* I/O Resampler state */ opus_int16 out[], /* O Output signal */ const opus_int16 in[], /* I Input signal */ opus_int32 inLen /* I Number of input samples */ ); /* Upsample by a factor 2, high quality */ void silk_resampler_private_up2_HQ_wrapper( void *SS, /* I/O Resampler state (unused) */ opus_int16 *out, /* O Output signal [ 2 * len ] */ const opus_int16 *in, /* I Input signal [ len ] */ opus_int32 len /* I Number of input samples */ ); /* Upsample by a factor 2, high quality */ void silk_resampler_private_up2_HQ( opus_int32 *S, /* I/O Resampler state [ 6 ] */ opus_int16 *out, /* O Output signal [ 2 * len ] */ const opus_int16 *in, /* I Input signal [ len ] */ opus_int32 len /* I Number of input samples */ ); /* Second order AR filter */ void silk_resampler_private_AR2( opus_int32 S[], /* I/O State vector [ 2 ] */ opus_int32 out_Q8[], /* O Output signal */ const opus_int16 in[], /* I Input signal */ const opus_int16 A_Q14[], /* I AR coefficients, Q14 */ opus_int32 len /* I Signal length */ ); #ifdef __cplusplus } #endif #endif /* SILK_RESAMPLER_PRIVATE_H */ ================================================ FILE: deps/pjsip/third_party/opus/silk/resampler_private_AR2.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" #include "resampler_private.h" /* Second order AR filter with single delay elements */ void silk_resampler_private_AR2( opus_int32 S[], /* I/O State vector [ 2 ] */ opus_int32 out_Q8[], /* O Output signal */ const opus_int16 in[], /* I Input signal */ const opus_int16 A_Q14[], /* I AR coefficients, Q14 */ opus_int32 len /* I Signal length */ ) { opus_int32 k; opus_int32 out32; for( k = 0; k < len; k++ ) { out32 = silk_ADD_LSHIFT32( S[ 0 ], (opus_int32)in[ k ], 8 ); out_Q8[ k ] = out32; out32 = silk_LSHIFT( out32, 2 ); S[ 0 ] = silk_SMLAWB( S[ 1 ], out32, A_Q14[ 0 ] ); S[ 1 ] = silk_SMULWB( out32, A_Q14[ 1 ] ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/resampler_private_IIR_FIR.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" #include "resampler_private.h" #include "stack_alloc.h" static OPUS_INLINE opus_int16 *silk_resampler_private_IIR_FIR_INTERPOL( opus_int16 *out, opus_int16 *buf, opus_int32 max_index_Q16, opus_int32 index_increment_Q16 ) { opus_int32 index_Q16, res_Q15; opus_int16 *buf_ptr; opus_int32 table_index; /* Interpolate upsampled signal and store in output array */ for( index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16 ) { table_index = silk_SMULWB( index_Q16 & 0xFFFF, 12 ); buf_ptr = &buf[ index_Q16 >> 16 ]; res_Q15 = silk_SMULBB( buf_ptr[ 0 ], silk_resampler_frac_FIR_12[ table_index ][ 0 ] ); res_Q15 = silk_SMLABB( res_Q15, buf_ptr[ 1 ], silk_resampler_frac_FIR_12[ table_index ][ 1 ] ); res_Q15 = silk_SMLABB( res_Q15, buf_ptr[ 2 ], silk_resampler_frac_FIR_12[ table_index ][ 2 ] ); res_Q15 = silk_SMLABB( res_Q15, buf_ptr[ 3 ], silk_resampler_frac_FIR_12[ table_index ][ 3 ] ); res_Q15 = silk_SMLABB( res_Q15, buf_ptr[ 4 ], silk_resampler_frac_FIR_12[ 11 - table_index ][ 3 ] ); res_Q15 = silk_SMLABB( res_Q15, buf_ptr[ 5 ], silk_resampler_frac_FIR_12[ 11 - table_index ][ 2 ] ); res_Q15 = silk_SMLABB( res_Q15, buf_ptr[ 6 ], silk_resampler_frac_FIR_12[ 11 - table_index ][ 1 ] ); res_Q15 = silk_SMLABB( res_Q15, buf_ptr[ 7 ], silk_resampler_frac_FIR_12[ 11 - table_index ][ 0 ] ); *out++ = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( res_Q15, 15 ) ); } return out; } /* Upsample using a combination of allpass-based 2x upsampling and FIR interpolation */ void silk_resampler_private_IIR_FIR( void *SS, /* I/O Resampler state */ opus_int16 out[], /* O Output signal */ const opus_int16 in[], /* I Input signal */ opus_int32 inLen /* I Number of input samples */ ) { silk_resampler_state_struct *S = (silk_resampler_state_struct *)SS; opus_int32 nSamplesIn; opus_int32 max_index_Q16, index_increment_Q16; VARDECL( opus_int16, buf ); SAVE_STACK; ALLOC( buf, 2 * S->batchSize + RESAMPLER_ORDER_FIR_12, opus_int16 ); /* Copy buffered samples to start of buffer */ silk_memcpy( buf, S->sFIR.i16, RESAMPLER_ORDER_FIR_12 * sizeof( opus_int16 ) ); /* Iterate over blocks of frameSizeIn input samples */ index_increment_Q16 = S->invRatio_Q16; while( 1 ) { nSamplesIn = silk_min( inLen, S->batchSize ); /* Upsample 2x */ silk_resampler_private_up2_HQ( S->sIIR, &buf[ RESAMPLER_ORDER_FIR_12 ], in, nSamplesIn ); max_index_Q16 = silk_LSHIFT32( nSamplesIn, 16 + 1 ); /* + 1 because 2x upsampling */ out = silk_resampler_private_IIR_FIR_INTERPOL( out, buf, max_index_Q16, index_increment_Q16 ); in += nSamplesIn; inLen -= nSamplesIn; if( inLen > 0 ) { /* More iterations to do; copy last part of filtered signal to beginning of buffer */ silk_memcpy( buf, &buf[ nSamplesIn << 1 ], RESAMPLER_ORDER_FIR_12 * sizeof( opus_int16 ) ); } else { break; } } /* Copy last part of filtered signal to the state for the next call */ silk_memcpy( S->sFIR.i16, &buf[ nSamplesIn << 1 ], RESAMPLER_ORDER_FIR_12 * sizeof( opus_int16 ) ); RESTORE_STACK; } ================================================ FILE: deps/pjsip/third_party/opus/silk/resampler_private_down_FIR.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" #include "resampler_private.h" #include "stack_alloc.h" static OPUS_INLINE opus_int16 *silk_resampler_private_down_FIR_INTERPOL( opus_int16 *out, opus_int32 *buf, const opus_int16 *FIR_Coefs, opus_int FIR_Order, opus_int FIR_Fracs, opus_int32 max_index_Q16, opus_int32 index_increment_Q16 ) { opus_int32 index_Q16, res_Q6; opus_int32 *buf_ptr; opus_int32 interpol_ind; const opus_int16 *interpol_ptr; switch( FIR_Order ) { case RESAMPLER_DOWN_ORDER_FIR0: for( index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16 ) { /* Integer part gives pointer to buffered input */ buf_ptr = buf + silk_RSHIFT( index_Q16, 16 ); /* Fractional part gives interpolation coefficients */ interpol_ind = silk_SMULWB( index_Q16 & 0xFFFF, FIR_Fracs ); /* Inner product */ interpol_ptr = &FIR_Coefs[ RESAMPLER_DOWN_ORDER_FIR0 / 2 * interpol_ind ]; res_Q6 = silk_SMULWB( buf_ptr[ 0 ], interpol_ptr[ 0 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 1 ], interpol_ptr[ 1 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 2 ], interpol_ptr[ 2 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 3 ], interpol_ptr[ 3 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 4 ], interpol_ptr[ 4 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 5 ], interpol_ptr[ 5 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 6 ], interpol_ptr[ 6 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 7 ], interpol_ptr[ 7 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 8 ], interpol_ptr[ 8 ] ); interpol_ptr = &FIR_Coefs[ RESAMPLER_DOWN_ORDER_FIR0 / 2 * ( FIR_Fracs - 1 - interpol_ind ) ]; res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 17 ], interpol_ptr[ 0 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 16 ], interpol_ptr[ 1 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 15 ], interpol_ptr[ 2 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 14 ], interpol_ptr[ 3 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 13 ], interpol_ptr[ 4 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 12 ], interpol_ptr[ 5 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 11 ], interpol_ptr[ 6 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 10 ], interpol_ptr[ 7 ] ); res_Q6 = silk_SMLAWB( res_Q6, buf_ptr[ 9 ], interpol_ptr[ 8 ] ); /* Scale down, saturate and store in output array */ *out++ = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( res_Q6, 6 ) ); } break; case RESAMPLER_DOWN_ORDER_FIR1: for( index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16 ) { /* Integer part gives pointer to buffered input */ buf_ptr = buf + silk_RSHIFT( index_Q16, 16 ); /* Inner product */ res_Q6 = silk_SMULWB( silk_ADD32( buf_ptr[ 0 ], buf_ptr[ 23 ] ), FIR_Coefs[ 0 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 1 ], buf_ptr[ 22 ] ), FIR_Coefs[ 1 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 2 ], buf_ptr[ 21 ] ), FIR_Coefs[ 2 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 3 ], buf_ptr[ 20 ] ), FIR_Coefs[ 3 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 4 ], buf_ptr[ 19 ] ), FIR_Coefs[ 4 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 5 ], buf_ptr[ 18 ] ), FIR_Coefs[ 5 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 6 ], buf_ptr[ 17 ] ), FIR_Coefs[ 6 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 7 ], buf_ptr[ 16 ] ), FIR_Coefs[ 7 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 8 ], buf_ptr[ 15 ] ), FIR_Coefs[ 8 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 9 ], buf_ptr[ 14 ] ), FIR_Coefs[ 9 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 10 ], buf_ptr[ 13 ] ), FIR_Coefs[ 10 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 11 ], buf_ptr[ 12 ] ), FIR_Coefs[ 11 ] ); /* Scale down, saturate and store in output array */ *out++ = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( res_Q6, 6 ) ); } break; case RESAMPLER_DOWN_ORDER_FIR2: for( index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16 ) { /* Integer part gives pointer to buffered input */ buf_ptr = buf + silk_RSHIFT( index_Q16, 16 ); /* Inner product */ res_Q6 = silk_SMULWB( silk_ADD32( buf_ptr[ 0 ], buf_ptr[ 35 ] ), FIR_Coefs[ 0 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 1 ], buf_ptr[ 34 ] ), FIR_Coefs[ 1 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 2 ], buf_ptr[ 33 ] ), FIR_Coefs[ 2 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 3 ], buf_ptr[ 32 ] ), FIR_Coefs[ 3 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 4 ], buf_ptr[ 31 ] ), FIR_Coefs[ 4 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 5 ], buf_ptr[ 30 ] ), FIR_Coefs[ 5 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 6 ], buf_ptr[ 29 ] ), FIR_Coefs[ 6 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 7 ], buf_ptr[ 28 ] ), FIR_Coefs[ 7 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 8 ], buf_ptr[ 27 ] ), FIR_Coefs[ 8 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 9 ], buf_ptr[ 26 ] ), FIR_Coefs[ 9 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 10 ], buf_ptr[ 25 ] ), FIR_Coefs[ 10 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 11 ], buf_ptr[ 24 ] ), FIR_Coefs[ 11 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 12 ], buf_ptr[ 23 ] ), FIR_Coefs[ 12 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 13 ], buf_ptr[ 22 ] ), FIR_Coefs[ 13 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 14 ], buf_ptr[ 21 ] ), FIR_Coefs[ 14 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 15 ], buf_ptr[ 20 ] ), FIR_Coefs[ 15 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 16 ], buf_ptr[ 19 ] ), FIR_Coefs[ 16 ] ); res_Q6 = silk_SMLAWB( res_Q6, silk_ADD32( buf_ptr[ 17 ], buf_ptr[ 18 ] ), FIR_Coefs[ 17 ] ); /* Scale down, saturate and store in output array */ *out++ = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( res_Q6, 6 ) ); } break; default: silk_assert( 0 ); } return out; } /* Resample with a 2nd order AR filter followed by FIR interpolation */ void silk_resampler_private_down_FIR( void *SS, /* I/O Resampler state */ opus_int16 out[], /* O Output signal */ const opus_int16 in[], /* I Input signal */ opus_int32 inLen /* I Number of input samples */ ) { silk_resampler_state_struct *S = (silk_resampler_state_struct *)SS; opus_int32 nSamplesIn; opus_int32 max_index_Q16, index_increment_Q16; VARDECL( opus_int32, buf ); const opus_int16 *FIR_Coefs; SAVE_STACK; ALLOC( buf, S->batchSize + S->FIR_Order, opus_int32 ); /* Copy buffered samples to start of buffer */ silk_memcpy( buf, S->sFIR.i32, S->FIR_Order * sizeof( opus_int32 ) ); FIR_Coefs = &S->Coefs[ 2 ]; /* Iterate over blocks of frameSizeIn input samples */ index_increment_Q16 = S->invRatio_Q16; while( 1 ) { nSamplesIn = silk_min( inLen, S->batchSize ); /* Second-order AR filter (output in Q8) */ silk_resampler_private_AR2( S->sIIR, &buf[ S->FIR_Order ], in, S->Coefs, nSamplesIn ); max_index_Q16 = silk_LSHIFT32( nSamplesIn, 16 ); /* Interpolate filtered signal */ out = silk_resampler_private_down_FIR_INTERPOL( out, buf, FIR_Coefs, S->FIR_Order, S->FIR_Fracs, max_index_Q16, index_increment_Q16 ); in += nSamplesIn; inLen -= nSamplesIn; if( inLen > 1 ) { /* More iterations to do; copy last part of filtered signal to beginning of buffer */ silk_memcpy( buf, &buf[ nSamplesIn ], S->FIR_Order * sizeof( opus_int32 ) ); } else { break; } } /* Copy last part of filtered signal to the state for the next call */ silk_memcpy( S->sFIR.i32, &buf[ nSamplesIn ], S->FIR_Order * sizeof( opus_int32 ) ); RESTORE_STACK; } ================================================ FILE: deps/pjsip/third_party/opus/silk/resampler_private_up2_HQ.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" #include "resampler_private.h" /* Upsample by a factor 2, high quality */ /* Uses 2nd order allpass filters for the 2x upsampling, followed by a */ /* notch filter just above Nyquist. */ void silk_resampler_private_up2_HQ( opus_int32 *S, /* I/O Resampler state [ 6 ] */ opus_int16 *out, /* O Output signal [ 2 * len ] */ const opus_int16 *in, /* I Input signal [ len ] */ opus_int32 len /* I Number of input samples */ ) { opus_int32 k; opus_int32 in32, out32_1, out32_2, Y, X; silk_assert( silk_resampler_up2_hq_0[ 0 ] > 0 ); silk_assert( silk_resampler_up2_hq_0[ 1 ] > 0 ); silk_assert( silk_resampler_up2_hq_0[ 2 ] < 0 ); silk_assert( silk_resampler_up2_hq_1[ 0 ] > 0 ); silk_assert( silk_resampler_up2_hq_1[ 1 ] > 0 ); silk_assert( silk_resampler_up2_hq_1[ 2 ] < 0 ); /* Internal variables and state are in Q10 format */ for( k = 0; k < len; k++ ) { /* Convert to Q10 */ in32 = silk_LSHIFT( (opus_int32)in[ k ], 10 ); /* First all-pass section for even output sample */ Y = silk_SUB32( in32, S[ 0 ] ); X = silk_SMULWB( Y, silk_resampler_up2_hq_0[ 0 ] ); out32_1 = silk_ADD32( S[ 0 ], X ); S[ 0 ] = silk_ADD32( in32, X ); /* Second all-pass section for even output sample */ Y = silk_SUB32( out32_1, S[ 1 ] ); X = silk_SMULWB( Y, silk_resampler_up2_hq_0[ 1 ] ); out32_2 = silk_ADD32( S[ 1 ], X ); S[ 1 ] = silk_ADD32( out32_1, X ); /* Third all-pass section for even output sample */ Y = silk_SUB32( out32_2, S[ 2 ] ); X = silk_SMLAWB( Y, Y, silk_resampler_up2_hq_0[ 2 ] ); out32_1 = silk_ADD32( S[ 2 ], X ); S[ 2 ] = silk_ADD32( out32_2, X ); /* Apply gain in Q15, convert back to int16 and store to output */ out[ 2 * k ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( out32_1, 10 ) ); /* First all-pass section for odd output sample */ Y = silk_SUB32( in32, S[ 3 ] ); X = silk_SMULWB( Y, silk_resampler_up2_hq_1[ 0 ] ); out32_1 = silk_ADD32( S[ 3 ], X ); S[ 3 ] = silk_ADD32( in32, X ); /* Second all-pass section for odd output sample */ Y = silk_SUB32( out32_1, S[ 4 ] ); X = silk_SMULWB( Y, silk_resampler_up2_hq_1[ 1 ] ); out32_2 = silk_ADD32( S[ 4 ], X ); S[ 4 ] = silk_ADD32( out32_1, X ); /* Third all-pass section for odd output sample */ Y = silk_SUB32( out32_2, S[ 5 ] ); X = silk_SMLAWB( Y, Y, silk_resampler_up2_hq_1[ 2 ] ); out32_1 = silk_ADD32( S[ 5 ], X ); S[ 5 ] = silk_ADD32( out32_2, X ); /* Apply gain in Q15, convert back to int16 and store to output */ out[ 2 * k + 1 ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( out32_1, 10 ) ); } } void silk_resampler_private_up2_HQ_wrapper( void *SS, /* I/O Resampler state (unused) */ opus_int16 *out, /* O Output signal [ 2 * len ] */ const opus_int16 *in, /* I Input signal [ len ] */ opus_int32 len /* I Number of input samples */ ) { silk_resampler_state_struct *S = (silk_resampler_state_struct *)SS; silk_resampler_private_up2_HQ( S->sIIR, out, in, len ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/resampler_rom.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* Filter coefficients for IIR/FIR polyphase resampling * * Total size: 179 Words (358 Bytes) */ #include "resampler_private.h" /* Matlab code for the notch filter coefficients: */ /* B = [1, 0.147, 1]; A = [1, 0.107, 0.89]; G = 0.93; freqz(G * B, A, 2^14, 16e3); axis([0, 8000, -10, 1]) */ /* fprintf('\t%6d, %6d, %6d, %6d\n', round(B(2)*2^16), round(-A(2)*2^16), round((1-A(3))*2^16), round(G*2^15)) */ /* const opus_int16 silk_resampler_up2_hq_notch[ 4 ] = { 9634, -7012, 7209, 30474 }; */ /* Tables with IIR and FIR coefficients for fractional downsamplers (123 Words) */ silk_DWORD_ALIGN const opus_int16 silk_Resampler_3_4_COEFS[ 2 + 3 * RESAMPLER_DOWN_ORDER_FIR0 / 2 ] = { -20694, -13867, -49, 64, 17, -157, 353, -496, 163, 11047, 22205, -39, 6, 91, -170, 186, 23, -896, 6336, 19928, -19, -36, 102, -89, -24, 328, -951, 2568, 15909, }; silk_DWORD_ALIGN const opus_int16 silk_Resampler_2_3_COEFS[ 2 + 2 * RESAMPLER_DOWN_ORDER_FIR0 / 2 ] = { -14457, -14019, 64, 128, -122, 36, 310, -768, 584, 9267, 17733, 12, 128, 18, -142, 288, -117, -865, 4123, 14459, }; silk_DWORD_ALIGN const opus_int16 silk_Resampler_1_2_COEFS[ 2 + RESAMPLER_DOWN_ORDER_FIR1 / 2 ] = { 616, -14323, -10, 39, 58, -46, -84, 120, 184, -315, -541, 1284, 5380, 9024, }; silk_DWORD_ALIGN const opus_int16 silk_Resampler_1_3_COEFS[ 2 + RESAMPLER_DOWN_ORDER_FIR2 / 2 ] = { 16102, -15162, -13, 0, 20, 26, 5, -31, -43, -4, 65, 90, 7, -157, -248, -44, 593, 1583, 2612, 3271, }; silk_DWORD_ALIGN const opus_int16 silk_Resampler_1_4_COEFS[ 2 + RESAMPLER_DOWN_ORDER_FIR2 / 2 ] = { 22500, -15099, 3, -14, -20, -15, 2, 25, 37, 25, -16, -71, -107, -79, 50, 292, 623, 982, 1288, 1464, }; silk_DWORD_ALIGN const opus_int16 silk_Resampler_1_6_COEFS[ 2 + RESAMPLER_DOWN_ORDER_FIR2 / 2 ] = { 27540, -15257, 17, 12, 8, 1, -10, -22, -30, -32, -22, 3, 44, 100, 168, 243, 317, 381, 429, 455, }; silk_DWORD_ALIGN const opus_int16 silk_Resampler_2_3_COEFS_LQ[ 2 + 2 * 2 ] = { -2797, -6507, 4697, 10739, 1567, 8276, }; /* Table with interplation fractions of 1/24, 3/24, 5/24, ... , 23/24 : 23/24 (46 Words) */ silk_DWORD_ALIGN const opus_int16 silk_resampler_frac_FIR_12[ 12 ][ RESAMPLER_ORDER_FIR_12 / 2 ] = { { 189, -600, 617, 30567 }, { 117, -159, -1070, 29704 }, { 52, 221, -2392, 28276 }, { -4, 529, -3350, 26341 }, { -48, 758, -3956, 23973 }, { -80, 905, -4235, 21254 }, { -99, 972, -4222, 18278 }, { -107, 967, -3957, 15143 }, { -103, 896, -3487, 11950 }, { -91, 773, -2865, 8798 }, { -71, 611, -2143, 5784 }, { -46, 425, -1375, 2996 }, }; ================================================ FILE: deps/pjsip/third_party/opus/silk/resampler_rom.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_FIX_RESAMPLER_ROM_H #define SILK_FIX_RESAMPLER_ROM_H #ifdef __cplusplus extern "C" { #endif #include "typedef.h" #include "resampler_structs.h" #define RESAMPLER_DOWN_ORDER_FIR0 18 #define RESAMPLER_DOWN_ORDER_FIR1 24 #define RESAMPLER_DOWN_ORDER_FIR2 36 #define RESAMPLER_ORDER_FIR_12 8 /* Tables for 2x downsampler */ static const opus_int16 silk_resampler_down2_0 = 9872; static const opus_int16 silk_resampler_down2_1 = 39809 - 65536; /* Tables for 2x upsampler, high quality */ static const opus_int16 silk_resampler_up2_hq_0[ 3 ] = { 1746, 14986, 39083 - 65536 }; static const opus_int16 silk_resampler_up2_hq_1[ 3 ] = { 6854, 25769, 55542 - 65536 }; /* Tables with IIR and FIR coefficients for fractional downsamplers */ extern const opus_int16 silk_Resampler_3_4_COEFS[ 2 + 3 * RESAMPLER_DOWN_ORDER_FIR0 / 2 ]; extern const opus_int16 silk_Resampler_2_3_COEFS[ 2 + 2 * RESAMPLER_DOWN_ORDER_FIR0 / 2 ]; extern const opus_int16 silk_Resampler_1_2_COEFS[ 2 + RESAMPLER_DOWN_ORDER_FIR1 / 2 ]; extern const opus_int16 silk_Resampler_1_3_COEFS[ 2 + RESAMPLER_DOWN_ORDER_FIR2 / 2 ]; extern const opus_int16 silk_Resampler_1_4_COEFS[ 2 + RESAMPLER_DOWN_ORDER_FIR2 / 2 ]; extern const opus_int16 silk_Resampler_1_6_COEFS[ 2 + RESAMPLER_DOWN_ORDER_FIR2 / 2 ]; extern const opus_int16 silk_Resampler_2_3_COEFS_LQ[ 2 + 2 * 2 ]; /* Table with interplation fractions of 1/24, 3/24, ..., 23/24 */ extern const opus_int16 silk_resampler_frac_FIR_12[ 12 ][ RESAMPLER_ORDER_FIR_12 / 2 ]; #ifdef __cplusplus } #endif #endif /* SILK_FIX_RESAMPLER_ROM_H */ ================================================ FILE: deps/pjsip/third_party/opus/silk/resampler_structs.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_RESAMPLER_STRUCTS_H #define SILK_RESAMPLER_STRUCTS_H #ifdef __cplusplus extern "C" { #endif #define SILK_RESAMPLER_MAX_FIR_ORDER 36 #define SILK_RESAMPLER_MAX_IIR_ORDER 6 typedef struct _silk_resampler_state_struct{ opus_int32 sIIR[ SILK_RESAMPLER_MAX_IIR_ORDER ]; /* this must be the first element of this struct */ union{ opus_int32 i32[ SILK_RESAMPLER_MAX_FIR_ORDER ]; opus_int16 i16[ SILK_RESAMPLER_MAX_FIR_ORDER ]; } sFIR; opus_int16 delayBuf[ 48 ]; opus_int resampler_function; opus_int batchSize; opus_int32 invRatio_Q16; opus_int FIR_Order; opus_int FIR_Fracs; opus_int Fs_in_kHz; opus_int Fs_out_kHz; opus_int inputDelay; const opus_int16 *Coefs; } silk_resampler_state_struct; #ifdef __cplusplus } #endif #endif /* SILK_RESAMPLER_STRUCTS_H */ ================================================ FILE: deps/pjsip/third_party/opus/silk/shell_coder.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* shell coder; pulse-subframe length is hardcoded */ static OPUS_INLINE void combine_pulses( opus_int *out, /* O combined pulses vector [len] */ const opus_int *in, /* I input vector [2 * len] */ const opus_int len /* I number of OUTPUT samples */ ) { opus_int k; for( k = 0; k < len; k++ ) { out[ k ] = in[ 2 * k ] + in[ 2 * k + 1 ]; } } static OPUS_INLINE void encode_split( ec_enc *psRangeEnc, /* I/O compressor data structure */ const opus_int p_child1, /* I pulse amplitude of first child subframe */ const opus_int p, /* I pulse amplitude of current subframe */ const opus_uint8 *shell_table /* I table of shell cdfs */ ) { if( p > 0 ) { ec_enc_icdf( psRangeEnc, p_child1, &shell_table[ silk_shell_code_table_offsets[ p ] ], 8 ); } } static OPUS_INLINE void decode_split( opus_int16 *p_child1, /* O pulse amplitude of first child subframe */ opus_int16 *p_child2, /* O pulse amplitude of second child subframe */ ec_dec *psRangeDec, /* I/O Compressor data structure */ const opus_int p, /* I pulse amplitude of current subframe */ const opus_uint8 *shell_table /* I table of shell cdfs */ ) { if( p > 0 ) { p_child1[ 0 ] = ec_dec_icdf( psRangeDec, &shell_table[ silk_shell_code_table_offsets[ p ] ], 8 ); p_child2[ 0 ] = p - p_child1[ 0 ]; } else { p_child1[ 0 ] = 0; p_child2[ 0 ] = 0; } } /* Shell encoder, operates on one shell code frame of 16 pulses */ void silk_shell_encoder( ec_enc *psRangeEnc, /* I/O compressor data structure */ const opus_int *pulses0 /* I data: nonnegative pulse amplitudes */ ) { opus_int pulses1[ 8 ], pulses2[ 4 ], pulses3[ 2 ], pulses4[ 1 ]; /* this function operates on one shell code frame of 16 pulses */ silk_assert( SHELL_CODEC_FRAME_LENGTH == 16 ); /* tree representation per pulse-subframe */ combine_pulses( pulses1, pulses0, 8 ); combine_pulses( pulses2, pulses1, 4 ); combine_pulses( pulses3, pulses2, 2 ); combine_pulses( pulses4, pulses3, 1 ); encode_split( psRangeEnc, pulses3[ 0 ], pulses4[ 0 ], silk_shell_code_table3 ); encode_split( psRangeEnc, pulses2[ 0 ], pulses3[ 0 ], silk_shell_code_table2 ); encode_split( psRangeEnc, pulses1[ 0 ], pulses2[ 0 ], silk_shell_code_table1 ); encode_split( psRangeEnc, pulses0[ 0 ], pulses1[ 0 ], silk_shell_code_table0 ); encode_split( psRangeEnc, pulses0[ 2 ], pulses1[ 1 ], silk_shell_code_table0 ); encode_split( psRangeEnc, pulses1[ 2 ], pulses2[ 1 ], silk_shell_code_table1 ); encode_split( psRangeEnc, pulses0[ 4 ], pulses1[ 2 ], silk_shell_code_table0 ); encode_split( psRangeEnc, pulses0[ 6 ], pulses1[ 3 ], silk_shell_code_table0 ); encode_split( psRangeEnc, pulses2[ 2 ], pulses3[ 1 ], silk_shell_code_table2 ); encode_split( psRangeEnc, pulses1[ 4 ], pulses2[ 2 ], silk_shell_code_table1 ); encode_split( psRangeEnc, pulses0[ 8 ], pulses1[ 4 ], silk_shell_code_table0 ); encode_split( psRangeEnc, pulses0[ 10 ], pulses1[ 5 ], silk_shell_code_table0 ); encode_split( psRangeEnc, pulses1[ 6 ], pulses2[ 3 ], silk_shell_code_table1 ); encode_split( psRangeEnc, pulses0[ 12 ], pulses1[ 6 ], silk_shell_code_table0 ); encode_split( psRangeEnc, pulses0[ 14 ], pulses1[ 7 ], silk_shell_code_table0 ); } /* Shell decoder, operates on one shell code frame of 16 pulses */ void silk_shell_decoder( opus_int16 *pulses0, /* O data: nonnegative pulse amplitudes */ ec_dec *psRangeDec, /* I/O Compressor data structure */ const opus_int pulses4 /* I number of pulses per pulse-subframe */ ) { opus_int16 pulses3[ 2 ], pulses2[ 4 ], pulses1[ 8 ]; /* this function operates on one shell code frame of 16 pulses */ silk_assert( SHELL_CODEC_FRAME_LENGTH == 16 ); decode_split( &pulses3[ 0 ], &pulses3[ 1 ], psRangeDec, pulses4, silk_shell_code_table3 ); decode_split( &pulses2[ 0 ], &pulses2[ 1 ], psRangeDec, pulses3[ 0 ], silk_shell_code_table2 ); decode_split( &pulses1[ 0 ], &pulses1[ 1 ], psRangeDec, pulses2[ 0 ], silk_shell_code_table1 ); decode_split( &pulses0[ 0 ], &pulses0[ 1 ], psRangeDec, pulses1[ 0 ], silk_shell_code_table0 ); decode_split( &pulses0[ 2 ], &pulses0[ 3 ], psRangeDec, pulses1[ 1 ], silk_shell_code_table0 ); decode_split( &pulses1[ 2 ], &pulses1[ 3 ], psRangeDec, pulses2[ 1 ], silk_shell_code_table1 ); decode_split( &pulses0[ 4 ], &pulses0[ 5 ], psRangeDec, pulses1[ 2 ], silk_shell_code_table0 ); decode_split( &pulses0[ 6 ], &pulses0[ 7 ], psRangeDec, pulses1[ 3 ], silk_shell_code_table0 ); decode_split( &pulses2[ 2 ], &pulses2[ 3 ], psRangeDec, pulses3[ 1 ], silk_shell_code_table2 ); decode_split( &pulses1[ 4 ], &pulses1[ 5 ], psRangeDec, pulses2[ 2 ], silk_shell_code_table1 ); decode_split( &pulses0[ 8 ], &pulses0[ 9 ], psRangeDec, pulses1[ 4 ], silk_shell_code_table0 ); decode_split( &pulses0[ 10 ], &pulses0[ 11 ], psRangeDec, pulses1[ 5 ], silk_shell_code_table0 ); decode_split( &pulses1[ 6 ], &pulses1[ 7 ], psRangeDec, pulses2[ 3 ], silk_shell_code_table1 ); decode_split( &pulses0[ 12 ], &pulses0[ 13 ], psRangeDec, pulses1[ 6 ], silk_shell_code_table0 ); decode_split( &pulses0[ 14 ], &pulses0[ 15 ], psRangeDec, pulses1[ 7 ], silk_shell_code_table0 ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/sigm_Q15.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* Approximate sigmoid function */ #include "SigProc_FIX.h" /* fprintf(1, '%d, ', round(1024 * ([1 ./ (1 + exp(-(1:5))), 1] - 1 ./ (1 + exp(-(0:5)))))); */ static const opus_int32 sigm_LUT_slope_Q10[ 6 ] = { 237, 153, 73, 30, 12, 7 }; /* fprintf(1, '%d, ', round(32767 * 1 ./ (1 + exp(-(0:5))))); */ static const opus_int32 sigm_LUT_pos_Q15[ 6 ] = { 16384, 23955, 28861, 31213, 32178, 32548 }; /* fprintf(1, '%d, ', round(32767 * 1 ./ (1 + exp((0:5))))); */ static const opus_int32 sigm_LUT_neg_Q15[ 6 ] = { 16384, 8812, 3906, 1554, 589, 219 }; opus_int silk_sigm_Q15( opus_int in_Q5 /* I */ ) { opus_int ind; if( in_Q5 < 0 ) { /* Negative input */ in_Q5 = -in_Q5; if( in_Q5 >= 6 * 32 ) { return 0; /* Clip */ } else { /* Linear interpolation of look up table */ ind = silk_RSHIFT( in_Q5, 5 ); return( sigm_LUT_neg_Q15[ ind ] - silk_SMULBB( sigm_LUT_slope_Q10[ ind ], in_Q5 & 0x1F ) ); } } else { /* Positive input */ if( in_Q5 >= 6 * 32 ) { return 32767; /* clip */ } else { /* Linear interpolation of look up table */ ind = silk_RSHIFT( in_Q5, 5 ); return( sigm_LUT_pos_Q15[ ind ] + silk_SMULBB( sigm_LUT_slope_Q10[ ind ], in_Q5 & 0x1F ) ); } } } ================================================ FILE: deps/pjsip/third_party/opus/silk/sort.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* Insertion sort (fast for already almost sorted arrays): */ /* Best case: O(n) for an already sorted array */ /* Worst case: O(n^2) for an inversely sorted array */ /* */ /* Shell short: http://en.wikipedia.org/wiki/Shell_sort */ #include "SigProc_FIX.h" void silk_insertion_sort_increasing( opus_int32 *a, /* I/O Unsorted / Sorted vector */ opus_int *idx, /* O Index vector for the sorted elements */ const opus_int L, /* I Vector length */ const opus_int K /* I Number of correctly sorted positions */ ) { opus_int32 value; opus_int i, j; /* Safety checks */ silk_assert( K > 0 ); silk_assert( L > 0 ); silk_assert( L >= K ); /* Write start indices in index vector */ for( i = 0; i < K; i++ ) { idx[ i ] = i; } /* Sort vector elements by value, increasing order */ for( i = 1; i < K; i++ ) { value = a[ i ]; for( j = i - 1; ( j >= 0 ) && ( value < a[ j ] ); j-- ) { a[ j + 1 ] = a[ j ]; /* Shift value */ idx[ j + 1 ] = idx[ j ]; /* Shift index */ } a[ j + 1 ] = value; /* Write value */ idx[ j + 1 ] = i; /* Write index */ } /* If less than L values are asked for, check the remaining values, */ /* but only spend CPU to ensure that the K first values are correct */ for( i = K; i < L; i++ ) { value = a[ i ]; if( value < a[ K - 1 ] ) { for( j = K - 2; ( j >= 0 ) && ( value < a[ j ] ); j-- ) { a[ j + 1 ] = a[ j ]; /* Shift value */ idx[ j + 1 ] = idx[ j ]; /* Shift index */ } a[ j + 1 ] = value; /* Write value */ idx[ j + 1 ] = i; /* Write index */ } } } #ifdef FIXED_POINT /* This function is only used by the fixed-point build */ void silk_insertion_sort_decreasing_int16( opus_int16 *a, /* I/O Unsorted / Sorted vector */ opus_int *idx, /* O Index vector for the sorted elements */ const opus_int L, /* I Vector length */ const opus_int K /* I Number of correctly sorted positions */ ) { opus_int i, j; opus_int value; /* Safety checks */ silk_assert( K > 0 ); silk_assert( L > 0 ); silk_assert( L >= K ); /* Write start indices in index vector */ for( i = 0; i < K; i++ ) { idx[ i ] = i; } /* Sort vector elements by value, decreasing order */ for( i = 1; i < K; i++ ) { value = a[ i ]; for( j = i - 1; ( j >= 0 ) && ( value > a[ j ] ); j-- ) { a[ j + 1 ] = a[ j ]; /* Shift value */ idx[ j + 1 ] = idx[ j ]; /* Shift index */ } a[ j + 1 ] = value; /* Write value */ idx[ j + 1 ] = i; /* Write index */ } /* If less than L values are asked for, check the remaining values, */ /* but only spend CPU to ensure that the K first values are correct */ for( i = K; i < L; i++ ) { value = a[ i ]; if( value > a[ K - 1 ] ) { for( j = K - 2; ( j >= 0 ) && ( value > a[ j ] ); j-- ) { a[ j + 1 ] = a[ j ]; /* Shift value */ idx[ j + 1 ] = idx[ j ]; /* Shift index */ } a[ j + 1 ] = value; /* Write value */ idx[ j + 1 ] = i; /* Write index */ } } } #endif void silk_insertion_sort_increasing_all_values_int16( opus_int16 *a, /* I/O Unsorted / Sorted vector */ const opus_int L /* I Vector length */ ) { opus_int value; opus_int i, j; /* Safety checks */ silk_assert( L > 0 ); /* Sort vector elements by value, increasing order */ for( i = 1; i < L; i++ ) { value = a[ i ]; for( j = i - 1; ( j >= 0 ) && ( value < a[ j ] ); j-- ) { a[ j + 1 ] = a[ j ]; /* Shift value */ } a[ j + 1 ] = value; /* Write value */ } } ================================================ FILE: deps/pjsip/third_party/opus/silk/stereo_LR_to_MS.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" #include "stack_alloc.h" /* Convert Left/Right stereo signal to adaptive Mid/Side representation */ void silk_stereo_LR_to_MS( stereo_enc_state *state, /* I/O State */ opus_int16 x1[], /* I/O Left input signal, becomes mid signal */ opus_int16 x2[], /* I/O Right input signal, becomes side signal */ opus_int8 ix[ 2 ][ 3 ], /* O Quantization indices */ opus_int8 *mid_only_flag, /* O Flag: only mid signal coded */ opus_int32 mid_side_rates_bps[], /* O Bitrates for mid and side signals */ opus_int32 total_rate_bps, /* I Total bitrate */ opus_int prev_speech_act_Q8, /* I Speech activity level in previous frame */ opus_int toMono, /* I Last frame before a stereo->mono transition */ opus_int fs_kHz, /* I Sample rate (kHz) */ opus_int frame_length /* I Number of samples */ ) { opus_int n, is10msFrame, denom_Q16, delta0_Q13, delta1_Q13; opus_int32 sum, diff, smooth_coef_Q16, pred_Q13[ 2 ], pred0_Q13, pred1_Q13; opus_int32 LP_ratio_Q14, HP_ratio_Q14, frac_Q16, frac_3_Q16, min_mid_rate_bps, width_Q14, w_Q24, deltaw_Q24; VARDECL( opus_int16, side ); VARDECL( opus_int16, LP_mid ); VARDECL( opus_int16, HP_mid ); VARDECL( opus_int16, LP_side ); VARDECL( opus_int16, HP_side ); opus_int16 *mid = &x1[ -2 ]; SAVE_STACK; ALLOC( side, frame_length + 2, opus_int16 ); /* Convert to basic mid/side signals */ for( n = 0; n < frame_length + 2; n++ ) { sum = x1[ n - 2 ] + (opus_int32)x2[ n - 2 ]; diff = x1[ n - 2 ] - (opus_int32)x2[ n - 2 ]; mid[ n ] = (opus_int16)silk_RSHIFT_ROUND( sum, 1 ); side[ n ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( diff, 1 ) ); } /* Buffering */ silk_memcpy( mid, state->sMid, 2 * sizeof( opus_int16 ) ); silk_memcpy( side, state->sSide, 2 * sizeof( opus_int16 ) ); silk_memcpy( state->sMid, &mid[ frame_length ], 2 * sizeof( opus_int16 ) ); silk_memcpy( state->sSide, &side[ frame_length ], 2 * sizeof( opus_int16 ) ); /* LP and HP filter mid signal */ ALLOC( LP_mid, frame_length, opus_int16 ); ALLOC( HP_mid, frame_length, opus_int16 ); for( n = 0; n < frame_length; n++ ) { sum = silk_RSHIFT_ROUND( silk_ADD_LSHIFT( mid[ n ] + mid[ n + 2 ], mid[ n + 1 ], 1 ), 2 ); LP_mid[ n ] = sum; HP_mid[ n ] = mid[ n + 1 ] - sum; } /* LP and HP filter side signal */ ALLOC( LP_side, frame_length, opus_int16 ); ALLOC( HP_side, frame_length, opus_int16 ); for( n = 0; n < frame_length; n++ ) { sum = silk_RSHIFT_ROUND( silk_ADD_LSHIFT( side[ n ] + side[ n + 2 ], side[ n + 1 ], 1 ), 2 ); LP_side[ n ] = sum; HP_side[ n ] = side[ n + 1 ] - sum; } /* Find energies and predictors */ is10msFrame = frame_length == 10 * fs_kHz; smooth_coef_Q16 = is10msFrame ? SILK_FIX_CONST( STEREO_RATIO_SMOOTH_COEF / 2, 16 ) : SILK_FIX_CONST( STEREO_RATIO_SMOOTH_COEF, 16 ); smooth_coef_Q16 = silk_SMULWB( silk_SMULBB( prev_speech_act_Q8, prev_speech_act_Q8 ), smooth_coef_Q16 ); pred_Q13[ 0 ] = silk_stereo_find_predictor( &LP_ratio_Q14, LP_mid, LP_side, &state->mid_side_amp_Q0[ 0 ], frame_length, smooth_coef_Q16 ); pred_Q13[ 1 ] = silk_stereo_find_predictor( &HP_ratio_Q14, HP_mid, HP_side, &state->mid_side_amp_Q0[ 2 ], frame_length, smooth_coef_Q16 ); /* Ratio of the norms of residual and mid signals */ frac_Q16 = silk_SMLABB( HP_ratio_Q14, LP_ratio_Q14, 3 ); frac_Q16 = silk_min( frac_Q16, SILK_FIX_CONST( 1, 16 ) ); /* Determine bitrate distribution between mid and side, and possibly reduce stereo width */ total_rate_bps -= is10msFrame ? 1200 : 600; /* Subtract approximate bitrate for coding stereo parameters */ if( total_rate_bps < 1 ) { total_rate_bps = 1; } min_mid_rate_bps = silk_SMLABB( 2000, fs_kHz, 900 ); silk_assert( min_mid_rate_bps < 32767 ); /* Default bitrate distribution: 8 parts for Mid and (5+3*frac) parts for Side. so: mid_rate = ( 8 / ( 13 + 3 * frac ) ) * total_ rate */ frac_3_Q16 = silk_MUL( 3, frac_Q16 ); mid_side_rates_bps[ 0 ] = silk_DIV32_varQ( total_rate_bps, SILK_FIX_CONST( 8 + 5, 16 ) + frac_3_Q16, 16+3 ); /* If Mid bitrate below minimum, reduce stereo width */ if( mid_side_rates_bps[ 0 ] < min_mid_rate_bps ) { mid_side_rates_bps[ 0 ] = min_mid_rate_bps; mid_side_rates_bps[ 1 ] = total_rate_bps - mid_side_rates_bps[ 0 ]; /* width = 4 * ( 2 * side_rate - min_rate ) / ( ( 1 + 3 * frac ) * min_rate ) */ width_Q14 = silk_DIV32_varQ( silk_LSHIFT( mid_side_rates_bps[ 1 ], 1 ) - min_mid_rate_bps, silk_SMULWB( SILK_FIX_CONST( 1, 16 ) + frac_3_Q16, min_mid_rate_bps ), 14+2 ); width_Q14 = silk_LIMIT( width_Q14, 0, SILK_FIX_CONST( 1, 14 ) ); } else { mid_side_rates_bps[ 1 ] = total_rate_bps - mid_side_rates_bps[ 0 ]; width_Q14 = SILK_FIX_CONST( 1, 14 ); } /* Smoother */ state->smth_width_Q14 = (opus_int16)silk_SMLAWB( state->smth_width_Q14, width_Q14 - state->smth_width_Q14, smooth_coef_Q16 ); /* At very low bitrates or for inputs that are nearly amplitude panned, switch to panned-mono coding */ *mid_only_flag = 0; if( toMono ) { /* Last frame before stereo->mono transition; collapse stereo width */ width_Q14 = 0; pred_Q13[ 0 ] = 0; pred_Q13[ 1 ] = 0; silk_stereo_quant_pred( pred_Q13, ix ); } else if( state->width_prev_Q14 == 0 && ( 8 * total_rate_bps < 13 * min_mid_rate_bps || silk_SMULWB( frac_Q16, state->smth_width_Q14 ) < SILK_FIX_CONST( 0.05, 14 ) ) ) { /* Code as panned-mono; previous frame already had zero width */ /* Scale down and quantize predictors */ pred_Q13[ 0 ] = silk_RSHIFT( silk_SMULBB( state->smth_width_Q14, pred_Q13[ 0 ] ), 14 ); pred_Q13[ 1 ] = silk_RSHIFT( silk_SMULBB( state->smth_width_Q14, pred_Q13[ 1 ] ), 14 ); silk_stereo_quant_pred( pred_Q13, ix ); /* Collapse stereo width */ width_Q14 = 0; pred_Q13[ 0 ] = 0; pred_Q13[ 1 ] = 0; mid_side_rates_bps[ 0 ] = total_rate_bps; mid_side_rates_bps[ 1 ] = 0; *mid_only_flag = 1; } else if( state->width_prev_Q14 != 0 && ( 8 * total_rate_bps < 11 * min_mid_rate_bps || silk_SMULWB( frac_Q16, state->smth_width_Q14 ) < SILK_FIX_CONST( 0.02, 14 ) ) ) { /* Transition to zero-width stereo */ /* Scale down and quantize predictors */ pred_Q13[ 0 ] = silk_RSHIFT( silk_SMULBB( state->smth_width_Q14, pred_Q13[ 0 ] ), 14 ); pred_Q13[ 1 ] = silk_RSHIFT( silk_SMULBB( state->smth_width_Q14, pred_Q13[ 1 ] ), 14 ); silk_stereo_quant_pred( pred_Q13, ix ); /* Collapse stereo width */ width_Q14 = 0; pred_Q13[ 0 ] = 0; pred_Q13[ 1 ] = 0; } else if( state->smth_width_Q14 > SILK_FIX_CONST( 0.95, 14 ) ) { /* Full-width stereo coding */ silk_stereo_quant_pred( pred_Q13, ix ); width_Q14 = SILK_FIX_CONST( 1, 14 ); } else { /* Reduced-width stereo coding; scale down and quantize predictors */ pred_Q13[ 0 ] = silk_RSHIFT( silk_SMULBB( state->smth_width_Q14, pred_Q13[ 0 ] ), 14 ); pred_Q13[ 1 ] = silk_RSHIFT( silk_SMULBB( state->smth_width_Q14, pred_Q13[ 1 ] ), 14 ); silk_stereo_quant_pred( pred_Q13, ix ); width_Q14 = state->smth_width_Q14; } /* Make sure to keep on encoding until the tapered output has been transmitted */ if( *mid_only_flag == 1 ) { state->silent_side_len += frame_length - STEREO_INTERP_LEN_MS * fs_kHz; if( state->silent_side_len < LA_SHAPE_MS * fs_kHz ) { *mid_only_flag = 0; } else { /* Limit to avoid wrapping around */ state->silent_side_len = 10000; } } else { state->silent_side_len = 0; } if( *mid_only_flag == 0 && mid_side_rates_bps[ 1 ] < 1 ) { mid_side_rates_bps[ 1 ] = 1; mid_side_rates_bps[ 0 ] = silk_max_int( 1, total_rate_bps - mid_side_rates_bps[ 1 ]); } /* Interpolate predictors and subtract prediction from side channel */ pred0_Q13 = -state->pred_prev_Q13[ 0 ]; pred1_Q13 = -state->pred_prev_Q13[ 1 ]; w_Q24 = silk_LSHIFT( state->width_prev_Q14, 10 ); denom_Q16 = silk_DIV32_16( (opus_int32)1 << 16, STEREO_INTERP_LEN_MS * fs_kHz ); delta0_Q13 = -silk_RSHIFT_ROUND( silk_SMULBB( pred_Q13[ 0 ] - state->pred_prev_Q13[ 0 ], denom_Q16 ), 16 ); delta1_Q13 = -silk_RSHIFT_ROUND( silk_SMULBB( pred_Q13[ 1 ] - state->pred_prev_Q13[ 1 ], denom_Q16 ), 16 ); deltaw_Q24 = silk_LSHIFT( silk_SMULWB( width_Q14 - state->width_prev_Q14, denom_Q16 ), 10 ); for( n = 0; n < STEREO_INTERP_LEN_MS * fs_kHz; n++ ) { pred0_Q13 += delta0_Q13; pred1_Q13 += delta1_Q13; w_Q24 += deltaw_Q24; sum = silk_LSHIFT( silk_ADD_LSHIFT( mid[ n ] + mid[ n + 2 ], mid[ n + 1 ], 1 ), 9 ); /* Q11 */ sum = silk_SMLAWB( silk_SMULWB( w_Q24, side[ n + 1 ] ), sum, pred0_Q13 ); /* Q8 */ sum = silk_SMLAWB( sum, silk_LSHIFT( (opus_int32)mid[ n + 1 ], 11 ), pred1_Q13 ); /* Q8 */ x2[ n - 1 ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( sum, 8 ) ); } pred0_Q13 = -pred_Q13[ 0 ]; pred1_Q13 = -pred_Q13[ 1 ]; w_Q24 = silk_LSHIFT( width_Q14, 10 ); for( n = STEREO_INTERP_LEN_MS * fs_kHz; n < frame_length; n++ ) { sum = silk_LSHIFT( silk_ADD_LSHIFT( mid[ n ] + mid[ n + 2 ], mid[ n + 1 ], 1 ), 9 ); /* Q11 */ sum = silk_SMLAWB( silk_SMULWB( w_Q24, side[ n + 1 ] ), sum, pred0_Q13 ); /* Q8 */ sum = silk_SMLAWB( sum, silk_LSHIFT( (opus_int32)mid[ n + 1 ], 11 ), pred1_Q13 ); /* Q8 */ x2[ n - 1 ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( sum, 8 ) ); } state->pred_prev_Q13[ 0 ] = (opus_int16)pred_Q13[ 0 ]; state->pred_prev_Q13[ 1 ] = (opus_int16)pred_Q13[ 1 ]; state->width_prev_Q14 = (opus_int16)width_Q14; RESTORE_STACK; } ================================================ FILE: deps/pjsip/third_party/opus/silk/stereo_MS_to_LR.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Convert adaptive Mid/Side representation to Left/Right stereo signal */ void silk_stereo_MS_to_LR( stereo_dec_state *state, /* I/O State */ opus_int16 x1[], /* I/O Left input signal, becomes mid signal */ opus_int16 x2[], /* I/O Right input signal, becomes side signal */ const opus_int32 pred_Q13[], /* I Predictors */ opus_int fs_kHz, /* I Samples rate (kHz) */ opus_int frame_length /* I Number of samples */ ) { opus_int n, denom_Q16, delta0_Q13, delta1_Q13; opus_int32 sum, diff, pred0_Q13, pred1_Q13; /* Buffering */ silk_memcpy( x1, state->sMid, 2 * sizeof( opus_int16 ) ); silk_memcpy( x2, state->sSide, 2 * sizeof( opus_int16 ) ); silk_memcpy( state->sMid, &x1[ frame_length ], 2 * sizeof( opus_int16 ) ); silk_memcpy( state->sSide, &x2[ frame_length ], 2 * sizeof( opus_int16 ) ); /* Interpolate predictors and add prediction to side channel */ pred0_Q13 = state->pred_prev_Q13[ 0 ]; pred1_Q13 = state->pred_prev_Q13[ 1 ]; denom_Q16 = silk_DIV32_16( (opus_int32)1 << 16, STEREO_INTERP_LEN_MS * fs_kHz ); delta0_Q13 = silk_RSHIFT_ROUND( silk_SMULBB( pred_Q13[ 0 ] - state->pred_prev_Q13[ 0 ], denom_Q16 ), 16 ); delta1_Q13 = silk_RSHIFT_ROUND( silk_SMULBB( pred_Q13[ 1 ] - state->pred_prev_Q13[ 1 ], denom_Q16 ), 16 ); for( n = 0; n < STEREO_INTERP_LEN_MS * fs_kHz; n++ ) { pred0_Q13 += delta0_Q13; pred1_Q13 += delta1_Q13; sum = silk_LSHIFT( silk_ADD_LSHIFT( x1[ n ] + x1[ n + 2 ], x1[ n + 1 ], 1 ), 9 ); /* Q11 */ sum = silk_SMLAWB( silk_LSHIFT( (opus_int32)x2[ n + 1 ], 8 ), sum, pred0_Q13 ); /* Q8 */ sum = silk_SMLAWB( sum, silk_LSHIFT( (opus_int32)x1[ n + 1 ], 11 ), pred1_Q13 ); /* Q8 */ x2[ n + 1 ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( sum, 8 ) ); } pred0_Q13 = pred_Q13[ 0 ]; pred1_Q13 = pred_Q13[ 1 ]; for( n = STEREO_INTERP_LEN_MS * fs_kHz; n < frame_length; n++ ) { sum = silk_LSHIFT( silk_ADD_LSHIFT( x1[ n ] + x1[ n + 2 ], x1[ n + 1 ], 1 ), 9 ); /* Q11 */ sum = silk_SMLAWB( silk_LSHIFT( (opus_int32)x2[ n + 1 ], 8 ), sum, pred0_Q13 ); /* Q8 */ sum = silk_SMLAWB( sum, silk_LSHIFT( (opus_int32)x1[ n + 1 ], 11 ), pred1_Q13 ); /* Q8 */ x2[ n + 1 ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( sum, 8 ) ); } state->pred_prev_Q13[ 0 ] = pred_Q13[ 0 ]; state->pred_prev_Q13[ 1 ] = pred_Q13[ 1 ]; /* Convert to left/right signals */ for( n = 0; n < frame_length; n++ ) { sum = x1[ n + 1 ] + (opus_int32)x2[ n + 1 ]; diff = x1[ n + 1 ] - (opus_int32)x2[ n + 1 ]; x1[ n + 1 ] = (opus_int16)silk_SAT16( sum ); x2[ n + 1 ] = (opus_int16)silk_SAT16( diff ); } } ================================================ FILE: deps/pjsip/third_party/opus/silk/stereo_decode_pred.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Decode mid/side predictors */ void silk_stereo_decode_pred( ec_dec *psRangeDec, /* I/O Compressor data structure */ opus_int32 pred_Q13[] /* O Predictors */ ) { opus_int n, ix[ 2 ][ 3 ]; opus_int32 low_Q13, step_Q13; /* Entropy decoding */ n = ec_dec_icdf( psRangeDec, silk_stereo_pred_joint_iCDF, 8 ); ix[ 0 ][ 2 ] = silk_DIV32_16( n, 5 ); ix[ 1 ][ 2 ] = n - 5 * ix[ 0 ][ 2 ]; for( n = 0; n < 2; n++ ) { ix[ n ][ 0 ] = ec_dec_icdf( psRangeDec, silk_uniform3_iCDF, 8 ); ix[ n ][ 1 ] = ec_dec_icdf( psRangeDec, silk_uniform5_iCDF, 8 ); } /* Dequantize */ for( n = 0; n < 2; n++ ) { ix[ n ][ 0 ] += 3 * ix[ n ][ 2 ]; low_Q13 = silk_stereo_pred_quant_Q13[ ix[ n ][ 0 ] ]; step_Q13 = silk_SMULWB( silk_stereo_pred_quant_Q13[ ix[ n ][ 0 ] + 1 ] - low_Q13, SILK_FIX_CONST( 0.5 / STEREO_QUANT_SUB_STEPS, 16 ) ); pred_Q13[ n ] = silk_SMLABB( low_Q13, step_Q13, 2 * ix[ n ][ 1 ] + 1 ); } /* Subtract second from first predictor (helps when actually applying these) */ pred_Q13[ 0 ] -= pred_Q13[ 1 ]; } /* Decode mid-only flag */ void silk_stereo_decode_mid_only( ec_dec *psRangeDec, /* I/O Compressor data structure */ opus_int *decode_only_mid /* O Flag that only mid channel has been coded */ ) { /* Decode flag that only mid channel is coded */ *decode_only_mid = ec_dec_icdf( psRangeDec, silk_stereo_only_code_mid_iCDF, 8 ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/stereo_encode_pred.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Entropy code the mid/side quantization indices */ void silk_stereo_encode_pred( ec_enc *psRangeEnc, /* I/O Compressor data structure */ opus_int8 ix[ 2 ][ 3 ] /* I Quantization indices */ ) { opus_int n; /* Entropy coding */ n = 5 * ix[ 0 ][ 2 ] + ix[ 1 ][ 2 ]; silk_assert( n < 25 ); ec_enc_icdf( psRangeEnc, n, silk_stereo_pred_joint_iCDF, 8 ); for( n = 0; n < 2; n++ ) { silk_assert( ix[ n ][ 0 ] < 3 ); silk_assert( ix[ n ][ 1 ] < STEREO_QUANT_SUB_STEPS ); ec_enc_icdf( psRangeEnc, ix[ n ][ 0 ], silk_uniform3_iCDF, 8 ); ec_enc_icdf( psRangeEnc, ix[ n ][ 1 ], silk_uniform5_iCDF, 8 ); } } /* Entropy code the mid-only flag */ void silk_stereo_encode_mid_only( ec_enc *psRangeEnc, /* I/O Compressor data structure */ opus_int8 mid_only_flag ) { /* Encode flag that only mid channel is coded */ ec_enc_icdf( psRangeEnc, mid_only_flag, silk_stereo_only_code_mid_iCDF, 8 ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/stereo_find_predictor.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Find least-squares prediction gain for one signal based on another and quantize it */ opus_int32 silk_stereo_find_predictor( /* O Returns predictor in Q13 */ opus_int32 *ratio_Q14, /* O Ratio of residual and mid energies */ const opus_int16 x[], /* I Basis signal */ const opus_int16 y[], /* I Target signal */ opus_int32 mid_res_amp_Q0[], /* I/O Smoothed mid, residual norms */ opus_int length, /* I Number of samples */ opus_int smooth_coef_Q16 /* I Smoothing coefficient */ ) { opus_int scale, scale1, scale2; opus_int32 nrgx, nrgy, corr, pred_Q13, pred2_Q10; /* Find predictor */ silk_sum_sqr_shift( &nrgx, &scale1, x, length ); silk_sum_sqr_shift( &nrgy, &scale2, y, length ); scale = silk_max_int( scale1, scale2 ); scale = scale + ( scale & 1 ); /* make even */ nrgy = silk_RSHIFT32( nrgy, scale - scale2 ); nrgx = silk_RSHIFT32( nrgx, scale - scale1 ); nrgx = silk_max_int( nrgx, 1 ); corr = silk_inner_prod_aligned_scale( x, y, scale, length ); pred_Q13 = silk_DIV32_varQ( corr, nrgx, 13 ); pred_Q13 = silk_LIMIT( pred_Q13, -(1 << 14), 1 << 14 ); pred2_Q10 = silk_SMULWB( pred_Q13, pred_Q13 ); /* Faster update for signals with large prediction parameters */ smooth_coef_Q16 = (opus_int)silk_max_int( smooth_coef_Q16, silk_abs( pred2_Q10 ) ); /* Smoothed mid and residual norms */ silk_assert( smooth_coef_Q16 < 32768 ); scale = silk_RSHIFT( scale, 1 ); mid_res_amp_Q0[ 0 ] = silk_SMLAWB( mid_res_amp_Q0[ 0 ], silk_LSHIFT( silk_SQRT_APPROX( nrgx ), scale ) - mid_res_amp_Q0[ 0 ], smooth_coef_Q16 ); /* Residual energy = nrgy - 2 * pred * corr + pred^2 * nrgx */ nrgy = silk_SUB_LSHIFT32( nrgy, silk_SMULWB( corr, pred_Q13 ), 3 + 1 ); nrgy = silk_ADD_LSHIFT32( nrgy, silk_SMULWB( nrgx, pred2_Q10 ), 6 ); mid_res_amp_Q0[ 1 ] = silk_SMLAWB( mid_res_amp_Q0[ 1 ], silk_LSHIFT( silk_SQRT_APPROX( nrgy ), scale ) - mid_res_amp_Q0[ 1 ], smooth_coef_Q16 ); /* Ratio of smoothed residual and mid norms */ *ratio_Q14 = silk_DIV32_varQ( mid_res_amp_Q0[ 1 ], silk_max( mid_res_amp_Q0[ 0 ], 1 ), 14 ); *ratio_Q14 = silk_LIMIT( *ratio_Q14, 0, 32767 ); return pred_Q13; } ================================================ FILE: deps/pjsip/third_party/opus/silk/stereo_quant_pred.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "main.h" /* Quantize mid/side predictors */ void silk_stereo_quant_pred( opus_int32 pred_Q13[], /* I/O Predictors (out: quantized) */ opus_int8 ix[ 2 ][ 3 ] /* O Quantization indices */ ) { opus_int i, j, n; opus_int32 low_Q13, step_Q13, lvl_Q13, err_min_Q13, err_Q13, quant_pred_Q13 = 0; /* Quantize */ for( n = 0; n < 2; n++ ) { /* Brute-force search over quantization levels */ err_min_Q13 = silk_int32_MAX; for( i = 0; i < STEREO_QUANT_TAB_SIZE - 1; i++ ) { low_Q13 = silk_stereo_pred_quant_Q13[ i ]; step_Q13 = silk_SMULWB( silk_stereo_pred_quant_Q13[ i + 1 ] - low_Q13, SILK_FIX_CONST( 0.5 / STEREO_QUANT_SUB_STEPS, 16 ) ); for( j = 0; j < STEREO_QUANT_SUB_STEPS; j++ ) { lvl_Q13 = silk_SMLABB( low_Q13, step_Q13, 2 * j + 1 ); err_Q13 = silk_abs( pred_Q13[ n ] - lvl_Q13 ); if( err_Q13 < err_min_Q13 ) { err_min_Q13 = err_Q13; quant_pred_Q13 = lvl_Q13; ix[ n ][ 0 ] = i; ix[ n ][ 1 ] = j; } else { /* Error increasing, so we're past the optimum */ goto done; } } } done: ix[ n ][ 2 ] = silk_DIV32_16( ix[ n ][ 0 ], 3 ); ix[ n ][ 0 ] -= ix[ n ][ 2 ] * 3; pred_Q13[ n ] = quant_pred_Q13; } /* Subtract second from first predictor (helps when actually applying these) */ pred_Q13[ 0 ] -= pred_Q13[ 1 ]; } ================================================ FILE: deps/pjsip/third_party/opus/silk/structs.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_STRUCTS_H #define SILK_STRUCTS_H #include "typedef.h" #include "SigProc_FIX.h" #include "define.h" #include "entenc.h" #include "entdec.h" #ifdef __cplusplus extern "C" { #endif /************************************/ /* Noise shaping quantization state */ /************************************/ typedef struct { opus_int16 xq[ 2 * MAX_FRAME_LENGTH ]; /* Buffer for quantized output signal */ opus_int32 sLTP_shp_Q14[ 2 * MAX_FRAME_LENGTH ]; opus_int32 sLPC_Q14[ MAX_SUB_FRAME_LENGTH + NSQ_LPC_BUF_LENGTH ]; opus_int32 sAR2_Q14[ MAX_SHAPE_LPC_ORDER ]; opus_int32 sLF_AR_shp_Q14; opus_int lagPrev; opus_int sLTP_buf_idx; opus_int sLTP_shp_buf_idx; opus_int32 rand_seed; opus_int32 prev_gain_Q16; opus_int rewhite_flag; } silk_nsq_state; /********************************/ /* VAD state */ /********************************/ typedef struct { opus_int32 AnaState[ 2 ]; /* Analysis filterbank state: 0-8 kHz */ opus_int32 AnaState1[ 2 ]; /* Analysis filterbank state: 0-4 kHz */ opus_int32 AnaState2[ 2 ]; /* Analysis filterbank state: 0-2 kHz */ opus_int32 XnrgSubfr[ VAD_N_BANDS ]; /* Subframe energies */ opus_int32 NrgRatioSmth_Q8[ VAD_N_BANDS ]; /* Smoothed energy level in each band */ opus_int16 HPstate; /* State of differentiator in the lowest band */ opus_int32 NL[ VAD_N_BANDS ]; /* Noise energy level in each band */ opus_int32 inv_NL[ VAD_N_BANDS ]; /* Inverse noise energy level in each band */ opus_int32 NoiseLevelBias[ VAD_N_BANDS ]; /* Noise level estimator bias/offset */ opus_int32 counter; /* Frame counter used in the initial phase */ } silk_VAD_state; /* Variable cut-off low-pass filter state */ typedef struct { opus_int32 In_LP_State[ 2 ]; /* Low pass filter state */ opus_int32 transition_frame_no; /* Counter which is mapped to a cut-off frequency */ opus_int mode; /* Operating mode, <0: switch down, >0: switch up; 0: do nothing */ } silk_LP_state; /* Structure containing NLSF codebook */ typedef struct { const opus_int16 nVectors; const opus_int16 order; const opus_int16 quantStepSize_Q16; const opus_int16 invQuantStepSize_Q6; const opus_uint8 *CB1_NLSF_Q8; const opus_uint8 *CB1_iCDF; const opus_uint8 *pred_Q8; const opus_uint8 *ec_sel; const opus_uint8 *ec_iCDF; const opus_uint8 *ec_Rates_Q5; const opus_int16 *deltaMin_Q15; } silk_NLSF_CB_struct; typedef struct { opus_int16 pred_prev_Q13[ 2 ]; opus_int16 sMid[ 2 ]; opus_int16 sSide[ 2 ]; opus_int32 mid_side_amp_Q0[ 4 ]; opus_int16 smth_width_Q14; opus_int16 width_prev_Q14; opus_int16 silent_side_len; opus_int8 predIx[ MAX_FRAMES_PER_PACKET ][ 2 ][ 3 ]; opus_int8 mid_only_flags[ MAX_FRAMES_PER_PACKET ]; } stereo_enc_state; typedef struct { opus_int16 pred_prev_Q13[ 2 ]; opus_int16 sMid[ 2 ]; opus_int16 sSide[ 2 ]; } stereo_dec_state; typedef struct { opus_int8 GainsIndices[ MAX_NB_SUBFR ]; opus_int8 LTPIndex[ MAX_NB_SUBFR ]; opus_int8 NLSFIndices[ MAX_LPC_ORDER + 1 ]; opus_int16 lagIndex; opus_int8 contourIndex; opus_int8 signalType; opus_int8 quantOffsetType; opus_int8 NLSFInterpCoef_Q2; opus_int8 PERIndex; opus_int8 LTP_scaleIndex; opus_int8 Seed; } SideInfoIndices; /********************************/ /* Encoder state */ /********************************/ typedef struct { opus_int32 In_HP_State[ 2 ]; /* High pass filter state */ opus_int32 variable_HP_smth1_Q15; /* State of first smoother */ opus_int32 variable_HP_smth2_Q15; /* State of second smoother */ silk_LP_state sLP; /* Low pass filter state */ silk_VAD_state sVAD; /* Voice activity detector state */ silk_nsq_state sNSQ; /* Noise Shape Quantizer State */ opus_int16 prev_NLSFq_Q15[ MAX_LPC_ORDER ]; /* Previously quantized NLSF vector */ opus_int speech_activity_Q8; /* Speech activity */ opus_int allow_bandwidth_switch; /* Flag indicating that switching of internal bandwidth is allowed */ opus_int8 LBRRprevLastGainIndex; opus_int8 prevSignalType; opus_int prevLag; opus_int pitch_LPC_win_length; opus_int max_pitch_lag; /* Highest possible pitch lag (samples) */ opus_int32 API_fs_Hz; /* API sampling frequency (Hz) */ opus_int32 prev_API_fs_Hz; /* Previous API sampling frequency (Hz) */ opus_int maxInternal_fs_Hz; /* Maximum internal sampling frequency (Hz) */ opus_int minInternal_fs_Hz; /* Minimum internal sampling frequency (Hz) */ opus_int desiredInternal_fs_Hz; /* Soft request for internal sampling frequency (Hz) */ opus_int fs_kHz; /* Internal sampling frequency (kHz) */ opus_int nb_subfr; /* Number of 5 ms subframes in a frame */ opus_int frame_length; /* Frame length (samples) */ opus_int subfr_length; /* Subframe length (samples) */ opus_int ltp_mem_length; /* Length of LTP memory */ opus_int la_pitch; /* Look-ahead for pitch analysis (samples) */ opus_int la_shape; /* Look-ahead for noise shape analysis (samples) */ opus_int shapeWinLength; /* Window length for noise shape analysis (samples) */ opus_int32 TargetRate_bps; /* Target bitrate (bps) */ opus_int PacketSize_ms; /* Number of milliseconds to put in each packet */ opus_int PacketLoss_perc; /* Packet loss rate measured by farend */ opus_int32 frameCounter; opus_int Complexity; /* Complexity setting */ opus_int nStatesDelayedDecision; /* Number of states in delayed decision quantization */ opus_int useInterpolatedNLSFs; /* Flag for using NLSF interpolation */ opus_int shapingLPCOrder; /* Filter order for noise shaping filters */ opus_int predictLPCOrder; /* Filter order for prediction filters */ opus_int pitchEstimationComplexity; /* Complexity level for pitch estimator */ opus_int pitchEstimationLPCOrder; /* Whitening filter order for pitch estimator */ opus_int32 pitchEstimationThreshold_Q16; /* Threshold for pitch estimator */ opus_int LTPQuantLowComplexity; /* Flag for low complexity LTP quantization */ opus_int mu_LTP_Q9; /* Rate-distortion tradeoff in LTP quantization */ opus_int32 sum_log_gain_Q7; /* Cumulative max prediction gain */ opus_int NLSF_MSVQ_Survivors; /* Number of survivors in NLSF MSVQ */ opus_int first_frame_after_reset; /* Flag for deactivating NLSF interpolation, pitch prediction */ opus_int controlled_since_last_payload; /* Flag for ensuring codec_control only runs once per packet */ opus_int warping_Q16; /* Warping parameter for warped noise shaping */ opus_int useCBR; /* Flag to enable constant bitrate */ opus_int prefillFlag; /* Flag to indicate that only buffers are prefilled, no coding */ const opus_uint8 *pitch_lag_low_bits_iCDF; /* Pointer to iCDF table for low bits of pitch lag index */ const opus_uint8 *pitch_contour_iCDF; /* Pointer to iCDF table for pitch contour index */ const silk_NLSF_CB_struct *psNLSF_CB; /* Pointer to NLSF codebook */ opus_int input_quality_bands_Q15[ VAD_N_BANDS ]; opus_int input_tilt_Q15; opus_int SNR_dB_Q7; /* Quality setting */ opus_int8 VAD_flags[ MAX_FRAMES_PER_PACKET ]; opus_int8 LBRR_flag; opus_int LBRR_flags[ MAX_FRAMES_PER_PACKET ]; SideInfoIndices indices; opus_int8 pulses[ MAX_FRAME_LENGTH ]; int arch; /* Input/output buffering */ opus_int16 inputBuf[ MAX_FRAME_LENGTH + 2 ]; /* Buffer containing input signal */ opus_int inputBufIx; opus_int nFramesPerPacket; opus_int nFramesEncoded; /* Number of frames analyzed in current packet */ opus_int nChannelsAPI; opus_int nChannelsInternal; opus_int channelNb; /* Parameters For LTP scaling Control */ opus_int frames_since_onset; /* Specifically for entropy coding */ opus_int ec_prevSignalType; opus_int16 ec_prevLagIndex; silk_resampler_state_struct resampler_state; /* DTX */ opus_int useDTX; /* Flag to enable DTX */ opus_int inDTX; /* Flag to signal DTX period */ opus_int noSpeechCounter; /* Counts concecutive nonactive frames, used by DTX */ /* Inband Low Bitrate Redundancy (LBRR) data */ opus_int useInBandFEC; /* Saves the API setting for query */ opus_int LBRR_enabled; /* Depends on useInBandFRC, bitrate and packet loss rate */ opus_int LBRR_GainIncreases; /* Gains increment for coding LBRR frames */ SideInfoIndices indices_LBRR[ MAX_FRAMES_PER_PACKET ]; opus_int8 pulses_LBRR[ MAX_FRAMES_PER_PACKET ][ MAX_FRAME_LENGTH ]; } silk_encoder_state; /* Struct for Packet Loss Concealment */ typedef struct { opus_int32 pitchL_Q8; /* Pitch lag to use for voiced concealment */ opus_int16 LTPCoef_Q14[ LTP_ORDER ]; /* LTP coeficients to use for voiced concealment */ opus_int16 prevLPC_Q12[ MAX_LPC_ORDER ]; opus_int last_frame_lost; /* Was previous frame lost */ opus_int32 rand_seed; /* Seed for unvoiced signal generation */ opus_int16 randScale_Q14; /* Scaling of unvoiced random signal */ opus_int32 conc_energy; opus_int conc_energy_shift; opus_int16 prevLTP_scale_Q14; opus_int32 prevGain_Q16[ 2 ]; opus_int fs_kHz; opus_int nb_subfr; opus_int subfr_length; } silk_PLC_struct; /* Struct for CNG */ typedef struct { opus_int32 CNG_exc_buf_Q14[ MAX_FRAME_LENGTH ]; opus_int16 CNG_smth_NLSF_Q15[ MAX_LPC_ORDER ]; opus_int32 CNG_synth_state[ MAX_LPC_ORDER ]; opus_int32 CNG_smth_Gain_Q16; opus_int32 rand_seed; opus_int fs_kHz; } silk_CNG_struct; /********************************/ /* Decoder state */ /********************************/ typedef struct { opus_int32 prev_gain_Q16; opus_int32 exc_Q14[ MAX_FRAME_LENGTH ]; opus_int32 sLPC_Q14_buf[ MAX_LPC_ORDER ]; opus_int16 outBuf[ MAX_FRAME_LENGTH + 2 * MAX_SUB_FRAME_LENGTH ]; /* Buffer for output signal */ opus_int lagPrev; /* Previous Lag */ opus_int8 LastGainIndex; /* Previous gain index */ opus_int fs_kHz; /* Sampling frequency in kHz */ opus_int32 fs_API_hz; /* API sample frequency (Hz) */ opus_int nb_subfr; /* Number of 5 ms subframes in a frame */ opus_int frame_length; /* Frame length (samples) */ opus_int subfr_length; /* Subframe length (samples) */ opus_int ltp_mem_length; /* Length of LTP memory */ opus_int LPC_order; /* LPC order */ opus_int16 prevNLSF_Q15[ MAX_LPC_ORDER ]; /* Used to interpolate LSFs */ opus_int first_frame_after_reset; /* Flag for deactivating NLSF interpolation */ const opus_uint8 *pitch_lag_low_bits_iCDF; /* Pointer to iCDF table for low bits of pitch lag index */ const opus_uint8 *pitch_contour_iCDF; /* Pointer to iCDF table for pitch contour index */ /* For buffering payload in case of more frames per packet */ opus_int nFramesDecoded; opus_int nFramesPerPacket; /* Specifically for entropy coding */ opus_int ec_prevSignalType; opus_int16 ec_prevLagIndex; opus_int VAD_flags[ MAX_FRAMES_PER_PACKET ]; opus_int LBRR_flag; opus_int LBRR_flags[ MAX_FRAMES_PER_PACKET ]; silk_resampler_state_struct resampler_state; const silk_NLSF_CB_struct *psNLSF_CB; /* Pointer to NLSF codebook */ /* Quantization indices */ SideInfoIndices indices; /* CNG state */ silk_CNG_struct sCNG; /* Stuff used for PLC */ opus_int lossCnt; opus_int prevSignalType; silk_PLC_struct sPLC; } silk_decoder_state; /************************/ /* Decoder control */ /************************/ typedef struct { /* Prediction and coding parameters */ opus_int pitchL[ MAX_NB_SUBFR ]; opus_int32 Gains_Q16[ MAX_NB_SUBFR ]; /* Holds interpolated and final coefficients, 4-byte aligned */ silk_DWORD_ALIGN opus_int16 PredCoef_Q12[ 2 ][ MAX_LPC_ORDER ]; opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ]; opus_int LTP_scale_Q14; } silk_decoder_control; #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/sum_sqr_shift.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SigProc_FIX.h" /* Compute number of bits to right shift the sum of squares of a vector */ /* of int16s to make it fit in an int32 */ void silk_sum_sqr_shift( opus_int32 *energy, /* O Energy of x, after shifting to the right */ opus_int *shift, /* O Number of bits right shift applied to energy */ const opus_int16 *x, /* I Input vector */ opus_int len /* I Length of input vector */ ) { opus_int i, shft; opus_int32 nrg_tmp, nrg; nrg = 0; shft = 0; len--; for( i = 0; i < len; i += 2 ) { nrg = silk_SMLABB_ovflw( nrg, x[ i ], x[ i ] ); nrg = silk_SMLABB_ovflw( nrg, x[ i + 1 ], x[ i + 1 ] ); if( nrg < 0 ) { /* Scale down */ nrg = (opus_int32)silk_RSHIFT_uint( (opus_uint32)nrg, 2 ); shft = 2; i+=2; break; } } for( ; i < len; i += 2 ) { nrg_tmp = silk_SMULBB( x[ i ], x[ i ] ); nrg_tmp = silk_SMLABB_ovflw( nrg_tmp, x[ i + 1 ], x[ i + 1 ] ); nrg = (opus_int32)silk_ADD_RSHIFT_uint( nrg, (opus_uint32)nrg_tmp, shft ); if( nrg < 0 ) { /* Scale down */ nrg = (opus_int32)silk_RSHIFT_uint( (opus_uint32)nrg, 2 ); shft += 2; } } if( i == len ) { /* One sample left to process */ nrg_tmp = silk_SMULBB( x[ i ], x[ i ] ); nrg = (opus_int32)silk_ADD_RSHIFT_uint( nrg, nrg_tmp, shft ); } /* Make sure to have at least one extra leading zero (two leading zeros in total) */ if( nrg & 0xC0000000 ) { nrg = silk_RSHIFT_uint( (opus_uint32)nrg, 2 ); shft += 2; } /* Output arguments */ *shift = shft; *energy = nrg; } ================================================ FILE: deps/pjsip/third_party/opus/silk/table_LSF_cos.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "tables.h" /* Cosine approximation table for LSF conversion */ /* Q12 values (even) */ const opus_int16 silk_LSFCosTab_FIX_Q12[ LSF_COS_TAB_SZ_FIX + 1 ] = { 8192, 8190, 8182, 8170, 8152, 8130, 8104, 8072, 8034, 7994, 7946, 7896, 7840, 7778, 7714, 7644, 7568, 7490, 7406, 7318, 7226, 7128, 7026, 6922, 6812, 6698, 6580, 6458, 6332, 6204, 6070, 5934, 5792, 5648, 5502, 5352, 5198, 5040, 4880, 4718, 4552, 4382, 4212, 4038, 3862, 3684, 3502, 3320, 3136, 2948, 2760, 2570, 2378, 2186, 1990, 1794, 1598, 1400, 1202, 1002, 802, 602, 402, 202, 0, -202, -402, -602, -802, -1002, -1202, -1400, -1598, -1794, -1990, -2186, -2378, -2570, -2760, -2948, -3136, -3320, -3502, -3684, -3862, -4038, -4212, -4382, -4552, -4718, -4880, -5040, -5198, -5352, -5502, -5648, -5792, -5934, -6070, -6204, -6332, -6458, -6580, -6698, -6812, -6922, -7026, -7128, -7226, -7318, -7406, -7490, -7568, -7644, -7714, -7778, -7840, -7896, -7946, -7994, -8034, -8072, -8104, -8130, -8152, -8170, -8182, -8190, -8192 }; ================================================ FILE: deps/pjsip/third_party/opus/silk/tables.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_TABLES_H #define SILK_TABLES_H #include "define.h" #include "structs.h" #ifdef __cplusplus extern "C" { #endif /* Entropy coding tables (with size in bytes indicated) */ extern const opus_uint8 silk_gain_iCDF[ 3 ][ N_LEVELS_QGAIN / 8 ]; /* 24 */ extern const opus_uint8 silk_delta_gain_iCDF[ MAX_DELTA_GAIN_QUANT - MIN_DELTA_GAIN_QUANT + 1 ]; /* 41 */ extern const opus_uint8 silk_pitch_lag_iCDF[ 2 * ( PITCH_EST_MAX_LAG_MS - PITCH_EST_MIN_LAG_MS ) ];/* 32 */ extern const opus_uint8 silk_pitch_delta_iCDF[ 21 ]; /* 21 */ extern const opus_uint8 silk_pitch_contour_iCDF[ 34 ]; /* 34 */ extern const opus_uint8 silk_pitch_contour_NB_iCDF[ 11 ]; /* 11 */ extern const opus_uint8 silk_pitch_contour_10_ms_iCDF[ 12 ]; /* 12 */ extern const opus_uint8 silk_pitch_contour_10_ms_NB_iCDF[ 3 ]; /* 3 */ extern const opus_uint8 silk_pulses_per_block_iCDF[ N_RATE_LEVELS ][ SILK_MAX_PULSES + 2 ]; /* 180 */ extern const opus_uint8 silk_pulses_per_block_BITS_Q5[ N_RATE_LEVELS - 1 ][ SILK_MAX_PULSES + 2 ]; /* 162 */ extern const opus_uint8 silk_rate_levels_iCDF[ 2 ][ N_RATE_LEVELS - 1 ]; /* 18 */ extern const opus_uint8 silk_rate_levels_BITS_Q5[ 2 ][ N_RATE_LEVELS - 1 ]; /* 18 */ extern const opus_uint8 silk_max_pulses_table[ 4 ]; /* 4 */ extern const opus_uint8 silk_shell_code_table0[ 152 ]; /* 152 */ extern const opus_uint8 silk_shell_code_table1[ 152 ]; /* 152 */ extern const opus_uint8 silk_shell_code_table2[ 152 ]; /* 152 */ extern const opus_uint8 silk_shell_code_table3[ 152 ]; /* 152 */ extern const opus_uint8 silk_shell_code_table_offsets[ SILK_MAX_PULSES + 1 ]; /* 17 */ extern const opus_uint8 silk_lsb_iCDF[ 2 ]; /* 2 */ extern const opus_uint8 silk_sign_iCDF[ 42 ]; /* 42 */ extern const opus_uint8 silk_uniform3_iCDF[ 3 ]; /* 3 */ extern const opus_uint8 silk_uniform4_iCDF[ 4 ]; /* 4 */ extern const opus_uint8 silk_uniform5_iCDF[ 5 ]; /* 5 */ extern const opus_uint8 silk_uniform6_iCDF[ 6 ]; /* 6 */ extern const opus_uint8 silk_uniform8_iCDF[ 8 ]; /* 8 */ extern const opus_uint8 silk_NLSF_EXT_iCDF[ 7 ]; /* 7 */ extern const opus_uint8 silk_LTP_per_index_iCDF[ 3 ]; /* 3 */ extern const opus_uint8 * const silk_LTP_gain_iCDF_ptrs[ NB_LTP_CBKS ]; /* 3 */ extern const opus_uint8 * const silk_LTP_gain_BITS_Q5_ptrs[ NB_LTP_CBKS ]; /* 3 */ extern const opus_int16 silk_LTP_gain_middle_avg_RD_Q14; extern const opus_int8 * const silk_LTP_vq_ptrs_Q7[ NB_LTP_CBKS ]; /* 168 */ extern const opus_uint8 * const silk_LTP_vq_gain_ptrs_Q7[NB_LTP_CBKS]; extern const opus_int8 silk_LTP_vq_sizes[ NB_LTP_CBKS ]; /* 3 */ extern const opus_uint8 silk_LTPscale_iCDF[ 3 ]; /* 4 */ extern const opus_int16 silk_LTPScales_table_Q14[ 3 ]; /* 6 */ extern const opus_uint8 silk_type_offset_VAD_iCDF[ 4 ]; /* 4 */ extern const opus_uint8 silk_type_offset_no_VAD_iCDF[ 2 ]; /* 2 */ extern const opus_int16 silk_stereo_pred_quant_Q13[ STEREO_QUANT_TAB_SIZE ]; /* 32 */ extern const opus_uint8 silk_stereo_pred_joint_iCDF[ 25 ]; /* 25 */ extern const opus_uint8 silk_stereo_only_code_mid_iCDF[ 2 ]; /* 2 */ extern const opus_uint8 * const silk_LBRR_flags_iCDF_ptr[ 2 ]; /* 10 */ extern const opus_uint8 silk_NLSF_interpolation_factor_iCDF[ 5 ]; /* 5 */ extern const silk_NLSF_CB_struct silk_NLSF_CB_WB; /* 1040 */ extern const silk_NLSF_CB_struct silk_NLSF_CB_NB_MB; /* 728 */ /* Piece-wise linear mapping from bitrate in kbps to coding quality in dB SNR */ extern const opus_int32 silk_TargetRate_table_NB[ TARGET_RATE_TAB_SZ ]; /* 32 */ extern const opus_int32 silk_TargetRate_table_MB[ TARGET_RATE_TAB_SZ ]; /* 32 */ extern const opus_int32 silk_TargetRate_table_WB[ TARGET_RATE_TAB_SZ ]; /* 32 */ extern const opus_int16 silk_SNR_table_Q1[ TARGET_RATE_TAB_SZ ]; /* 32 */ /* Quantization offsets */ extern const opus_int16 silk_Quantization_Offsets_Q10[ 2 ][ 2 ]; /* 8 */ /* Interpolation points for filter coefficients used in the bandwidth transition smoother */ extern const opus_int32 silk_Transition_LP_B_Q28[ TRANSITION_INT_NUM ][ TRANSITION_NB ]; /* 60 */ extern const opus_int32 silk_Transition_LP_A_Q28[ TRANSITION_INT_NUM ][ TRANSITION_NA ]; /* 60 */ /* Rom table with cosine values */ extern const opus_int16 silk_LSFCosTab_FIX_Q12[ LSF_COS_TAB_SZ_FIX + 1 ]; /* 258 */ #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/tables_LTP.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "tables.h" const opus_uint8 silk_LTP_per_index_iCDF[3] = { 179, 99, 0 }; static const opus_uint8 silk_LTP_gain_iCDF_0[8] = { 71, 56, 43, 30, 21, 12, 6, 0 }; static const opus_uint8 silk_LTP_gain_iCDF_1[16] = { 199, 165, 144, 124, 109, 96, 84, 71, 61, 51, 42, 32, 23, 15, 8, 0 }; static const opus_uint8 silk_LTP_gain_iCDF_2[32] = { 241, 225, 211, 199, 187, 175, 164, 153, 142, 132, 123, 114, 105, 96, 88, 80, 72, 64, 57, 50, 44, 38, 33, 29, 24, 20, 16, 12, 9, 5, 2, 0 }; const opus_int16 silk_LTP_gain_middle_avg_RD_Q14 = 12304; static const opus_uint8 silk_LTP_gain_BITS_Q5_0[8] = { 15, 131, 138, 138, 155, 155, 173, 173 }; static const opus_uint8 silk_LTP_gain_BITS_Q5_1[16] = { 69, 93, 115, 118, 131, 138, 141, 138, 150, 150, 155, 150, 155, 160, 166, 160 }; static const opus_uint8 silk_LTP_gain_BITS_Q5_2[32] = { 131, 128, 134, 141, 141, 141, 145, 145, 145, 150, 155, 155, 155, 155, 160, 160, 160, 160, 166, 166, 173, 173, 182, 192, 182, 192, 192, 192, 205, 192, 205, 224 }; const opus_uint8 * const silk_LTP_gain_iCDF_ptrs[NB_LTP_CBKS] = { silk_LTP_gain_iCDF_0, silk_LTP_gain_iCDF_1, silk_LTP_gain_iCDF_2 }; const opus_uint8 * const silk_LTP_gain_BITS_Q5_ptrs[NB_LTP_CBKS] = { silk_LTP_gain_BITS_Q5_0, silk_LTP_gain_BITS_Q5_1, silk_LTP_gain_BITS_Q5_2 }; static const opus_int8 silk_LTP_gain_vq_0[8][5] = { { 4, 6, 24, 7, 5 }, { 0, 0, 2, 0, 0 }, { 12, 28, 41, 13, -4 }, { -9, 15, 42, 25, 14 }, { 1, -2, 62, 41, -9 }, { -10, 37, 65, -4, 3 }, { -6, 4, 66, 7, -8 }, { 16, 14, 38, -3, 33 } }; static const opus_int8 silk_LTP_gain_vq_1[16][5] = { { 13, 22, 39, 23, 12 }, { -1, 36, 64, 27, -6 }, { -7, 10, 55, 43, 17 }, { 1, 1, 8, 1, 1 }, { 6, -11, 74, 53, -9 }, { -12, 55, 76, -12, 8 }, { -3, 3, 93, 27, -4 }, { 26, 39, 59, 3, -8 }, { 2, 0, 77, 11, 9 }, { -8, 22, 44, -6, 7 }, { 40, 9, 26, 3, 9 }, { -7, 20, 101, -7, 4 }, { 3, -8, 42, 26, 0 }, { -15, 33, 68, 2, 23 }, { -2, 55, 46, -2, 15 }, { 3, -1, 21, 16, 41 } }; static const opus_int8 silk_LTP_gain_vq_2[32][5] = { { -6, 27, 61, 39, 5 }, { -11, 42, 88, 4, 1 }, { -2, 60, 65, 6, -4 }, { -1, -5, 73, 56, 1 }, { -9, 19, 94, 29, -9 }, { 0, 12, 99, 6, 4 }, { 8, -19, 102, 46, -13 }, { 3, 2, 13, 3, 2 }, { 9, -21, 84, 72, -18 }, { -11, 46, 104, -22, 8 }, { 18, 38, 48, 23, 0 }, { -16, 70, 83, -21, 11 }, { 5, -11, 117, 22, -8 }, { -6, 23, 117, -12, 3 }, { 3, -8, 95, 28, 4 }, { -10, 15, 77, 60, -15 }, { -1, 4, 124, 2, -4 }, { 3, 38, 84, 24, -25 }, { 2, 13, 42, 13, 31 }, { 21, -4, 56, 46, -1 }, { -1, 35, 79, -13, 19 }, { -7, 65, 88, -9, -14 }, { 20, 4, 81, 49, -29 }, { 20, 0, 75, 3, -17 }, { 5, -9, 44, 92, -8 }, { 1, -3, 22, 69, 31 }, { -6, 95, 41, -12, 5 }, { 39, 67, 16, -4, 1 }, { 0, -6, 120, 55, -36 }, { -13, 44, 122, 4, -24 }, { 81, 5, 11, 3, 7 }, { 2, 0, 9, 10, 88 } }; const opus_int8 * const silk_LTP_vq_ptrs_Q7[NB_LTP_CBKS] = { (opus_int8 *)&silk_LTP_gain_vq_0[0][0], (opus_int8 *)&silk_LTP_gain_vq_1[0][0], (opus_int8 *)&silk_LTP_gain_vq_2[0][0] }; /* Maximum frequency-dependent response of the pitch taps above, computed as max(abs(freqz(taps))) */ static const opus_uint8 silk_LTP_gain_vq_0_gain[8] = { 46, 2, 90, 87, 93, 91, 82, 98 }; static const opus_uint8 silk_LTP_gain_vq_1_gain[16] = { 109, 120, 118, 12, 113, 115, 117, 119, 99, 59, 87, 111, 63, 111, 112, 80 }; static const opus_uint8 silk_LTP_gain_vq_2_gain[32] = { 126, 124, 125, 124, 129, 121, 126, 23, 132, 127, 127, 127, 126, 127, 122, 133, 130, 134, 101, 118, 119, 145, 126, 86, 124, 120, 123, 119, 170, 173, 107, 109 }; const opus_uint8 * const silk_LTP_vq_gain_ptrs_Q7[NB_LTP_CBKS] = { &silk_LTP_gain_vq_0_gain[0], &silk_LTP_gain_vq_1_gain[0], &silk_LTP_gain_vq_2_gain[0] }; const opus_int8 silk_LTP_vq_sizes[NB_LTP_CBKS] = { 8, 16, 32 }; ================================================ FILE: deps/pjsip/third_party/opus/silk/tables_NLSF_CB_NB_MB.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "tables.h" static const opus_uint8 silk_NLSF_CB1_NB_MB_Q8[ 320 ] = { 12, 35, 60, 83, 108, 132, 157, 180, 206, 228, 15, 32, 55, 77, 101, 125, 151, 175, 201, 225, 19, 42, 66, 89, 114, 137, 162, 184, 209, 230, 12, 25, 50, 72, 97, 120, 147, 172, 200, 223, 26, 44, 69, 90, 114, 135, 159, 180, 205, 225, 13, 22, 53, 80, 106, 130, 156, 180, 205, 228, 15, 25, 44, 64, 90, 115, 142, 168, 196, 222, 19, 24, 62, 82, 100, 120, 145, 168, 190, 214, 22, 31, 50, 79, 103, 120, 151, 170, 203, 227, 21, 29, 45, 65, 106, 124, 150, 171, 196, 224, 30, 49, 75, 97, 121, 142, 165, 186, 209, 229, 19, 25, 52, 70, 93, 116, 143, 166, 192, 219, 26, 34, 62, 75, 97, 118, 145, 167, 194, 217, 25, 33, 56, 70, 91, 113, 143, 165, 196, 223, 21, 34, 51, 72, 97, 117, 145, 171, 196, 222, 20, 29, 50, 67, 90, 117, 144, 168, 197, 221, 22, 31, 48, 66, 95, 117, 146, 168, 196, 222, 24, 33, 51, 77, 116, 134, 158, 180, 200, 224, 21, 28, 70, 87, 106, 124, 149, 170, 194, 217, 26, 33, 53, 64, 83, 117, 152, 173, 204, 225, 27, 34, 65, 95, 108, 129, 155, 174, 210, 225, 20, 26, 72, 99, 113, 131, 154, 176, 200, 219, 34, 43, 61, 78, 93, 114, 155, 177, 205, 229, 23, 29, 54, 97, 124, 138, 163, 179, 209, 229, 30, 38, 56, 89, 118, 129, 158, 178, 200, 231, 21, 29, 49, 63, 85, 111, 142, 163, 193, 222, 27, 48, 77, 103, 133, 158, 179, 196, 215, 232, 29, 47, 74, 99, 124, 151, 176, 198, 220, 237, 33, 42, 61, 76, 93, 121, 155, 174, 207, 225, 29, 53, 87, 112, 136, 154, 170, 188, 208, 227, 24, 30, 52, 84, 131, 150, 166, 186, 203, 229, 37, 48, 64, 84, 104, 118, 156, 177, 201, 230 }; static const opus_uint8 silk_NLSF_CB1_iCDF_NB_MB[ 64 ] = { 212, 178, 148, 129, 108, 96, 85, 82, 79, 77, 61, 59, 57, 56, 51, 49, 48, 45, 42, 41, 40, 38, 36, 34, 31, 30, 21, 12, 10, 3, 1, 0, 255, 245, 244, 236, 233, 225, 217, 203, 190, 176, 175, 161, 149, 136, 125, 114, 102, 91, 81, 71, 60, 52, 43, 35, 28, 20, 19, 18, 12, 11, 5, 0 }; static const opus_uint8 silk_NLSF_CB2_SELECT_NB_MB[ 160 ] = { 16, 0, 0, 0, 0, 99, 66, 36, 36, 34, 36, 34, 34, 34, 34, 83, 69, 36, 52, 34, 116, 102, 70, 68, 68, 176, 102, 68, 68, 34, 65, 85, 68, 84, 36, 116, 141, 152, 139, 170, 132, 187, 184, 216, 137, 132, 249, 168, 185, 139, 104, 102, 100, 68, 68, 178, 218, 185, 185, 170, 244, 216, 187, 187, 170, 244, 187, 187, 219, 138, 103, 155, 184, 185, 137, 116, 183, 155, 152, 136, 132, 217, 184, 184, 170, 164, 217, 171, 155, 139, 244, 169, 184, 185, 170, 164, 216, 223, 218, 138, 214, 143, 188, 218, 168, 244, 141, 136, 155, 170, 168, 138, 220, 219, 139, 164, 219, 202, 216, 137, 168, 186, 246, 185, 139, 116, 185, 219, 185, 138, 100, 100, 134, 100, 102, 34, 68, 68, 100, 68, 168, 203, 221, 218, 168, 167, 154, 136, 104, 70, 164, 246, 171, 137, 139, 137, 155, 218, 219, 139 }; static const opus_uint8 silk_NLSF_CB2_iCDF_NB_MB[ 72 ] = { 255, 254, 253, 238, 14, 3, 2, 1, 0, 255, 254, 252, 218, 35, 3, 2, 1, 0, 255, 254, 250, 208, 59, 4, 2, 1, 0, 255, 254, 246, 194, 71, 10, 2, 1, 0, 255, 252, 236, 183, 82, 8, 2, 1, 0, 255, 252, 235, 180, 90, 17, 2, 1, 0, 255, 248, 224, 171, 97, 30, 4, 1, 0, 255, 254, 236, 173, 95, 37, 7, 1, 0 }; static const opus_uint8 silk_NLSF_CB2_BITS_NB_MB_Q5[ 72 ] = { 255, 255, 255, 131, 6, 145, 255, 255, 255, 255, 255, 236, 93, 15, 96, 255, 255, 255, 255, 255, 194, 83, 25, 71, 221, 255, 255, 255, 255, 162, 73, 34, 66, 162, 255, 255, 255, 210, 126, 73, 43, 57, 173, 255, 255, 255, 201, 125, 71, 48, 58, 130, 255, 255, 255, 166, 110, 73, 57, 62, 104, 210, 255, 255, 251, 123, 65, 55, 68, 100, 171, 255 }; static const opus_uint8 silk_NLSF_PRED_NB_MB_Q8[ 18 ] = { 179, 138, 140, 148, 151, 149, 153, 151, 163, 116, 67, 82, 59, 92, 72, 100, 89, 92 }; static const opus_int16 silk_NLSF_DELTA_MIN_NB_MB_Q15[ 11 ] = { 250, 3, 6, 3, 3, 3, 4, 3, 3, 3, 461 }; const silk_NLSF_CB_struct silk_NLSF_CB_NB_MB = { 32, 10, SILK_FIX_CONST( 0.18, 16 ), SILK_FIX_CONST( 1.0 / 0.18, 6 ), silk_NLSF_CB1_NB_MB_Q8, silk_NLSF_CB1_iCDF_NB_MB, silk_NLSF_PRED_NB_MB_Q8, silk_NLSF_CB2_SELECT_NB_MB, silk_NLSF_CB2_iCDF_NB_MB, silk_NLSF_CB2_BITS_NB_MB_Q5, silk_NLSF_DELTA_MIN_NB_MB_Q15, }; ================================================ FILE: deps/pjsip/third_party/opus/silk/tables_NLSF_CB_WB.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "tables.h" static const opus_uint8 silk_NLSF_CB1_WB_Q8[ 512 ] = { 7, 23, 38, 54, 69, 85, 100, 116, 131, 147, 162, 178, 193, 208, 223, 239, 13, 25, 41, 55, 69, 83, 98, 112, 127, 142, 157, 171, 187, 203, 220, 236, 15, 21, 34, 51, 61, 78, 92, 106, 126, 136, 152, 167, 185, 205, 225, 240, 10, 21, 36, 50, 63, 79, 95, 110, 126, 141, 157, 173, 189, 205, 221, 237, 17, 20, 37, 51, 59, 78, 89, 107, 123, 134, 150, 164, 184, 205, 224, 240, 10, 15, 32, 51, 67, 81, 96, 112, 129, 142, 158, 173, 189, 204, 220, 236, 8, 21, 37, 51, 65, 79, 98, 113, 126, 138, 155, 168, 179, 192, 209, 218, 12, 15, 34, 55, 63, 78, 87, 108, 118, 131, 148, 167, 185, 203, 219, 236, 16, 19, 32, 36, 56, 79, 91, 108, 118, 136, 154, 171, 186, 204, 220, 237, 11, 28, 43, 58, 74, 89, 105, 120, 135, 150, 165, 180, 196, 211, 226, 241, 6, 16, 33, 46, 60, 75, 92, 107, 123, 137, 156, 169, 185, 199, 214, 225, 11, 19, 30, 44, 57, 74, 89, 105, 121, 135, 152, 169, 186, 202, 218, 234, 12, 19, 29, 46, 57, 71, 88, 100, 120, 132, 148, 165, 182, 199, 216, 233, 17, 23, 35, 46, 56, 77, 92, 106, 123, 134, 152, 167, 185, 204, 222, 237, 14, 17, 45, 53, 63, 75, 89, 107, 115, 132, 151, 171, 188, 206, 221, 240, 9, 16, 29, 40, 56, 71, 88, 103, 119, 137, 154, 171, 189, 205, 222, 237, 16, 19, 36, 48, 57, 76, 87, 105, 118, 132, 150, 167, 185, 202, 218, 236, 12, 17, 29, 54, 71, 81, 94, 104, 126, 136, 149, 164, 182, 201, 221, 237, 15, 28, 47, 62, 79, 97, 115, 129, 142, 155, 168, 180, 194, 208, 223, 238, 8, 14, 30, 45, 62, 78, 94, 111, 127, 143, 159, 175, 192, 207, 223, 239, 17, 30, 49, 62, 79, 92, 107, 119, 132, 145, 160, 174, 190, 204, 220, 235, 14, 19, 36, 45, 61, 76, 91, 108, 121, 138, 154, 172, 189, 205, 222, 238, 12, 18, 31, 45, 60, 76, 91, 107, 123, 138, 154, 171, 187, 204, 221, 236, 13, 17, 31, 43, 53, 70, 83, 103, 114, 131, 149, 167, 185, 203, 220, 237, 17, 22, 35, 42, 58, 78, 93, 110, 125, 139, 155, 170, 188, 206, 224, 240, 8, 15, 34, 50, 67, 83, 99, 115, 131, 146, 162, 178, 193, 209, 224, 239, 13, 16, 41, 66, 73, 86, 95, 111, 128, 137, 150, 163, 183, 206, 225, 241, 17, 25, 37, 52, 63, 75, 92, 102, 119, 132, 144, 160, 175, 191, 212, 231, 19, 31, 49, 65, 83, 100, 117, 133, 147, 161, 174, 187, 200, 213, 227, 242, 18, 31, 52, 68, 88, 103, 117, 126, 138, 149, 163, 177, 192, 207, 223, 239, 16, 29, 47, 61, 76, 90, 106, 119, 133, 147, 161, 176, 193, 209, 224, 240, 15, 21, 35, 50, 61, 73, 86, 97, 110, 119, 129, 141, 175, 198, 218, 237 }; static const opus_uint8 silk_NLSF_CB1_iCDF_WB[ 64 ] = { 225, 204, 201, 184, 183, 175, 158, 154, 153, 135, 119, 115, 113, 110, 109, 99, 98, 95, 79, 68, 52, 50, 48, 45, 43, 32, 31, 27, 18, 10, 3, 0, 255, 251, 235, 230, 212, 201, 196, 182, 167, 166, 163, 151, 138, 124, 110, 104, 90, 78, 76, 70, 69, 57, 45, 34, 24, 21, 11, 6, 5, 4, 3, 0 }; static const opus_uint8 silk_NLSF_CB2_SELECT_WB[ 256 ] = { 0, 0, 0, 0, 0, 0, 0, 1, 100, 102, 102, 68, 68, 36, 34, 96, 164, 107, 158, 185, 180, 185, 139, 102, 64, 66, 36, 34, 34, 0, 1, 32, 208, 139, 141, 191, 152, 185, 155, 104, 96, 171, 104, 166, 102, 102, 102, 132, 1, 0, 0, 0, 0, 16, 16, 0, 80, 109, 78, 107, 185, 139, 103, 101, 208, 212, 141, 139, 173, 153, 123, 103, 36, 0, 0, 0, 0, 0, 0, 1, 48, 0, 0, 0, 0, 0, 0, 32, 68, 135, 123, 119, 119, 103, 69, 98, 68, 103, 120, 118, 118, 102, 71, 98, 134, 136, 157, 184, 182, 153, 139, 134, 208, 168, 248, 75, 189, 143, 121, 107, 32, 49, 34, 34, 34, 0, 17, 2, 210, 235, 139, 123, 185, 137, 105, 134, 98, 135, 104, 182, 100, 183, 171, 134, 100, 70, 68, 70, 66, 66, 34, 131, 64, 166, 102, 68, 36, 2, 1, 0, 134, 166, 102, 68, 34, 34, 66, 132, 212, 246, 158, 139, 107, 107, 87, 102, 100, 219, 125, 122, 137, 118, 103, 132, 114, 135, 137, 105, 171, 106, 50, 34, 164, 214, 141, 143, 185, 151, 121, 103, 192, 34, 0, 0, 0, 0, 0, 1, 208, 109, 74, 187, 134, 249, 159, 137, 102, 110, 154, 118, 87, 101, 119, 101, 0, 2, 0, 36, 36, 66, 68, 35, 96, 164, 102, 100, 36, 0, 2, 33, 167, 138, 174, 102, 100, 84, 2, 2, 100, 107, 120, 119, 36, 197, 24, 0 }; static const opus_uint8 silk_NLSF_CB2_iCDF_WB[ 72 ] = { 255, 254, 253, 244, 12, 3, 2, 1, 0, 255, 254, 252, 224, 38, 3, 2, 1, 0, 255, 254, 251, 209, 57, 4, 2, 1, 0, 255, 254, 244, 195, 69, 4, 2, 1, 0, 255, 251, 232, 184, 84, 7, 2, 1, 0, 255, 254, 240, 186, 86, 14, 2, 1, 0, 255, 254, 239, 178, 91, 30, 5, 1, 0, 255, 248, 227, 177, 100, 19, 2, 1, 0 }; static const opus_uint8 silk_NLSF_CB2_BITS_WB_Q5[ 72 ] = { 255, 255, 255, 156, 4, 154, 255, 255, 255, 255, 255, 227, 102, 15, 92, 255, 255, 255, 255, 255, 213, 83, 24, 72, 236, 255, 255, 255, 255, 150, 76, 33, 63, 214, 255, 255, 255, 190, 121, 77, 43, 55, 185, 255, 255, 255, 245, 137, 71, 43, 59, 139, 255, 255, 255, 255, 131, 66, 50, 66, 107, 194, 255, 255, 166, 116, 76, 55, 53, 125, 255, 255 }; static const opus_uint8 silk_NLSF_PRED_WB_Q8[ 30 ] = { 175, 148, 160, 176, 178, 173, 174, 164, 177, 174, 196, 182, 198, 192, 182, 68, 62, 66, 60, 72, 117, 85, 90, 118, 136, 151, 142, 160, 142, 155 }; static const opus_int16 silk_NLSF_DELTA_MIN_WB_Q15[ 17 ] = { 100, 3, 40, 3, 3, 3, 5, 14, 14, 10, 11, 3, 8, 9, 7, 3, 347 }; const silk_NLSF_CB_struct silk_NLSF_CB_WB = { 32, 16, SILK_FIX_CONST( 0.15, 16 ), SILK_FIX_CONST( 1.0 / 0.15, 6 ), silk_NLSF_CB1_WB_Q8, silk_NLSF_CB1_iCDF_WB, silk_NLSF_PRED_WB_Q8, silk_NLSF_CB2_SELECT_WB, silk_NLSF_CB2_iCDF_WB, silk_NLSF_CB2_BITS_WB_Q5, silk_NLSF_DELTA_MIN_WB_Q15, }; ================================================ FILE: deps/pjsip/third_party/opus/silk/tables_gain.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "tables.h" #ifdef __cplusplus extern "C" { #endif const opus_uint8 silk_gain_iCDF[ 3 ][ N_LEVELS_QGAIN / 8 ] = { { 224, 112, 44, 15, 3, 2, 1, 0 }, { 254, 237, 192, 132, 70, 23, 4, 0 }, { 255, 252, 226, 155, 61, 11, 2, 0 } }; const opus_uint8 silk_delta_gain_iCDF[ MAX_DELTA_GAIN_QUANT - MIN_DELTA_GAIN_QUANT + 1 ] = { 250, 245, 234, 203, 71, 50, 42, 38, 35, 33, 31, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; #ifdef __cplusplus } #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/tables_other.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "structs.h" #include "define.h" #include "tables.h" #ifdef __cplusplus extern "C" { #endif /* Piece-wise linear mapping from bitrate in kbps to coding quality in dB SNR */ const opus_int32 silk_TargetRate_table_NB[ TARGET_RATE_TAB_SZ ] = { 0, 8000, 9400, 11500, 13500, 17500, 25000, MAX_TARGET_RATE_BPS }; const opus_int32 silk_TargetRate_table_MB[ TARGET_RATE_TAB_SZ ] = { 0, 9000, 12000, 14500, 18500, 24500, 35500, MAX_TARGET_RATE_BPS }; const opus_int32 silk_TargetRate_table_WB[ TARGET_RATE_TAB_SZ ] = { 0, 10500, 14000, 17000, 21500, 28500, 42000, MAX_TARGET_RATE_BPS }; const opus_int16 silk_SNR_table_Q1[ TARGET_RATE_TAB_SZ ] = { 18, 29, 38, 40, 46, 52, 62, 84 }; /* Tables for stereo predictor coding */ const opus_int16 silk_stereo_pred_quant_Q13[ STEREO_QUANT_TAB_SIZE ] = { -13732, -10050, -8266, -7526, -6500, -5000, -2950, -820, 820, 2950, 5000, 6500, 7526, 8266, 10050, 13732 }; const opus_uint8 silk_stereo_pred_joint_iCDF[ 25 ] = { 249, 247, 246, 245, 244, 234, 210, 202, 201, 200, 197, 174, 82, 59, 56, 55, 54, 46, 22, 12, 11, 10, 9, 7, 0 }; const opus_uint8 silk_stereo_only_code_mid_iCDF[ 2 ] = { 64, 0 }; /* Tables for LBRR flags */ static const opus_uint8 silk_LBRR_flags_2_iCDF[ 3 ] = { 203, 150, 0 }; static const opus_uint8 silk_LBRR_flags_3_iCDF[ 7 ] = { 215, 195, 166, 125, 110, 82, 0 }; const opus_uint8 * const silk_LBRR_flags_iCDF_ptr[ 2 ] = { silk_LBRR_flags_2_iCDF, silk_LBRR_flags_3_iCDF }; /* Table for LSB coding */ const opus_uint8 silk_lsb_iCDF[ 2 ] = { 120, 0 }; /* Tables for LTPScale */ const opus_uint8 silk_LTPscale_iCDF[ 3 ] = { 128, 64, 0 }; /* Tables for signal type and offset coding */ const opus_uint8 silk_type_offset_VAD_iCDF[ 4 ] = { 232, 158, 10, 0 }; const opus_uint8 silk_type_offset_no_VAD_iCDF[ 2 ] = { 230, 0 }; /* Tables for NLSF interpolation factor */ const opus_uint8 silk_NLSF_interpolation_factor_iCDF[ 5 ] = { 243, 221, 192, 181, 0 }; /* Quantization offsets */ const opus_int16 silk_Quantization_Offsets_Q10[ 2 ][ 2 ] = { { OFFSET_UVL_Q10, OFFSET_UVH_Q10 }, { OFFSET_VL_Q10, OFFSET_VH_Q10 } }; /* Table for LTPScale */ const opus_int16 silk_LTPScales_table_Q14[ 3 ] = { 15565, 12288, 8192 }; /* Uniform entropy tables */ const opus_uint8 silk_uniform3_iCDF[ 3 ] = { 171, 85, 0 }; const opus_uint8 silk_uniform4_iCDF[ 4 ] = { 192, 128, 64, 0 }; const opus_uint8 silk_uniform5_iCDF[ 5 ] = { 205, 154, 102, 51, 0 }; const opus_uint8 silk_uniform6_iCDF[ 6 ] = { 213, 171, 128, 85, 43, 0 }; const opus_uint8 silk_uniform8_iCDF[ 8 ] = { 224, 192, 160, 128, 96, 64, 32, 0 }; const opus_uint8 silk_NLSF_EXT_iCDF[ 7 ] = { 100, 40, 16, 7, 3, 1, 0 }; /* Elliptic/Cauer filters designed with 0.1 dB passband ripple, 80 dB minimum stopband attenuation, and [0.95 : 0.15 : 0.35] normalized cut off frequencies. */ /* Interpolation points for filter coefficients used in the bandwidth transition smoother */ const opus_int32 silk_Transition_LP_B_Q28[ TRANSITION_INT_NUM ][ TRANSITION_NB ] = { { 250767114, 501534038, 250767114 }, { 209867381, 419732057, 209867381 }, { 170987846, 341967853, 170987846 }, { 131531482, 263046905, 131531482 }, { 89306658, 178584282, 89306658 } }; /* Interpolation points for filter coefficients used in the bandwidth transition smoother */ const opus_int32 silk_Transition_LP_A_Q28[ TRANSITION_INT_NUM ][ TRANSITION_NA ] = { { 506393414, 239854379 }, { 411067935, 169683996 }, { 306733530, 116694253 }, { 185807084, 77959395 }, { 35497197, 57401098 } }; #ifdef __cplusplus } #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/tables_pitch_lag.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "tables.h" const opus_uint8 silk_pitch_lag_iCDF[ 2 * ( PITCH_EST_MAX_LAG_MS - PITCH_EST_MIN_LAG_MS ) ] = { 253, 250, 244, 233, 212, 182, 150, 131, 120, 110, 98, 85, 72, 60, 49, 40, 32, 25, 19, 15, 13, 11, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; const opus_uint8 silk_pitch_delta_iCDF[21] = { 210, 208, 206, 203, 199, 193, 183, 168, 142, 104, 74, 52, 37, 27, 20, 14, 10, 6, 4, 2, 0 }; const opus_uint8 silk_pitch_contour_iCDF[34] = { 223, 201, 183, 167, 152, 138, 124, 111, 98, 88, 79, 70, 62, 56, 50, 44, 39, 35, 31, 27, 24, 21, 18, 16, 14, 12, 10, 8, 6, 4, 3, 2, 1, 0 }; const opus_uint8 silk_pitch_contour_NB_iCDF[11] = { 188, 176, 155, 138, 119, 97, 67, 43, 26, 10, 0 }; const opus_uint8 silk_pitch_contour_10_ms_iCDF[12] = { 165, 119, 80, 61, 47, 35, 27, 20, 14, 9, 4, 0 }; const opus_uint8 silk_pitch_contour_10_ms_NB_iCDF[3] = { 113, 63, 0 }; ================================================ FILE: deps/pjsip/third_party/opus/silk/tables_pulses_per_block.c ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "tables.h" const opus_uint8 silk_max_pulses_table[ 4 ] = { 8, 10, 12, 16 }; const opus_uint8 silk_pulses_per_block_iCDF[ 10 ][ 18 ] = { { 125, 51, 26, 18, 15, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, { 198, 105, 45, 22, 15, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, { 213, 162, 116, 83, 59, 43, 32, 24, 18, 15, 12, 9, 7, 6, 5, 3, 2, 0 }, { 239, 187, 116, 59, 28, 16, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, { 250, 229, 188, 135, 86, 51, 30, 19, 13, 10, 8, 6, 5, 4, 3, 2, 1, 0 }, { 249, 235, 213, 185, 156, 128, 103, 83, 66, 53, 42, 33, 26, 21, 17, 13, 10, 0 }, { 254, 249, 235, 206, 164, 118, 77, 46, 27, 16, 10, 7, 5, 4, 3, 2, 1, 0 }, { 255, 253, 249, 239, 220, 191, 156, 119, 85, 57, 37, 23, 15, 10, 6, 4, 2, 0 }, { 255, 253, 251, 246, 237, 223, 203, 179, 152, 124, 98, 75, 55, 40, 29, 21, 15, 0 }, { 255, 254, 253, 247, 220, 162, 106, 67, 42, 28, 18, 12, 9, 6, 4, 3, 2, 0 } }; const opus_uint8 silk_pulses_per_block_BITS_Q5[ 9 ][ 18 ] = { { 31, 57, 107, 160, 205, 205, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 69, 47, 67, 111, 166, 205, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 82, 74, 79, 95, 109, 128, 145, 160, 173, 205, 205, 205, 224, 255, 255, 224, 255, 224 }, { 125, 74, 59, 69, 97, 141, 182, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 173, 115, 85, 73, 76, 92, 115, 145, 173, 205, 224, 224, 255, 255, 255, 255, 255, 255 }, { 166, 134, 113, 102, 101, 102, 107, 118, 125, 138, 145, 155, 166, 182, 192, 192, 205, 150 }, { 224, 182, 134, 101, 83, 79, 85, 97, 120, 145, 173, 205, 224, 255, 255, 255, 255, 255 }, { 255, 224, 192, 150, 120, 101, 92, 89, 93, 102, 118, 134, 160, 182, 192, 224, 224, 224 }, { 255, 224, 224, 182, 155, 134, 118, 109, 104, 102, 106, 111, 118, 131, 145, 160, 173, 131 } }; const opus_uint8 silk_rate_levels_iCDF[ 2 ][ 9 ] = { { 241, 190, 178, 132, 87, 74, 41, 14, 0 }, { 223, 193, 157, 140, 106, 57, 39, 18, 0 } }; const opus_uint8 silk_rate_levels_BITS_Q5[ 2 ][ 9 ] = { { 131, 74, 141, 79, 80, 138, 95, 104, 134 }, { 95, 99, 91, 125, 93, 76, 123, 115, 123 } }; const opus_uint8 silk_shell_code_table0[ 152 ] = { 128, 0, 214, 42, 0, 235, 128, 21, 0, 244, 184, 72, 11, 0, 248, 214, 128, 42, 7, 0, 248, 225, 170, 80, 25, 5, 0, 251, 236, 198, 126, 54, 18, 3, 0, 250, 238, 211, 159, 82, 35, 15, 5, 0, 250, 231, 203, 168, 128, 88, 53, 25, 6, 0, 252, 238, 216, 185, 148, 108, 71, 40, 18, 4, 0, 253, 243, 225, 199, 166, 128, 90, 57, 31, 13, 3, 0, 254, 246, 233, 212, 183, 147, 109, 73, 44, 23, 10, 2, 0, 255, 250, 240, 223, 198, 166, 128, 90, 58, 33, 16, 6, 1, 0, 255, 251, 244, 231, 210, 181, 146, 110, 75, 46, 25, 12, 5, 1, 0, 255, 253, 248, 238, 221, 196, 164, 128, 92, 60, 35, 18, 8, 3, 1, 0, 255, 253, 249, 242, 229, 208, 180, 146, 110, 76, 48, 27, 14, 7, 3, 1, 0 }; const opus_uint8 silk_shell_code_table1[ 152 ] = { 129, 0, 207, 50, 0, 236, 129, 20, 0, 245, 185, 72, 10, 0, 249, 213, 129, 42, 6, 0, 250, 226, 169, 87, 27, 4, 0, 251, 233, 194, 130, 62, 20, 4, 0, 250, 236, 207, 160, 99, 47, 17, 3, 0, 255, 240, 217, 182, 131, 81, 41, 11, 1, 0, 255, 254, 233, 201, 159, 107, 61, 20, 2, 1, 0, 255, 249, 233, 206, 170, 128, 86, 50, 23, 7, 1, 0, 255, 250, 238, 217, 186, 148, 108, 70, 39, 18, 6, 1, 0, 255, 252, 243, 226, 200, 166, 128, 90, 56, 30, 13, 4, 1, 0, 255, 252, 245, 231, 209, 180, 146, 110, 76, 47, 25, 11, 4, 1, 0, 255, 253, 248, 237, 219, 194, 163, 128, 93, 62, 37, 19, 8, 3, 1, 0, 255, 254, 250, 241, 226, 205, 177, 145, 111, 79, 51, 30, 15, 6, 2, 1, 0 }; const opus_uint8 silk_shell_code_table2[ 152 ] = { 129, 0, 203, 54, 0, 234, 129, 23, 0, 245, 184, 73, 10, 0, 250, 215, 129, 41, 5, 0, 252, 232, 173, 86, 24, 3, 0, 253, 240, 200, 129, 56, 15, 2, 0, 253, 244, 217, 164, 94, 38, 10, 1, 0, 253, 245, 226, 189, 132, 71, 27, 7, 1, 0, 253, 246, 231, 203, 159, 105, 56, 23, 6, 1, 0, 255, 248, 235, 213, 179, 133, 85, 47, 19, 5, 1, 0, 255, 254, 243, 221, 194, 159, 117, 70, 37, 12, 2, 1, 0, 255, 254, 248, 234, 208, 171, 128, 85, 48, 22, 8, 2, 1, 0, 255, 254, 250, 240, 220, 189, 149, 107, 67, 36, 16, 6, 2, 1, 0, 255, 254, 251, 243, 227, 201, 166, 128, 90, 55, 29, 13, 5, 2, 1, 0, 255, 254, 252, 246, 234, 213, 183, 147, 109, 73, 43, 22, 10, 4, 2, 1, 0 }; const opus_uint8 silk_shell_code_table3[ 152 ] = { 130, 0, 200, 58, 0, 231, 130, 26, 0, 244, 184, 76, 12, 0, 249, 214, 130, 43, 6, 0, 252, 232, 173, 87, 24, 3, 0, 253, 241, 203, 131, 56, 14, 2, 0, 254, 246, 221, 167, 94, 35, 8, 1, 0, 254, 249, 232, 193, 130, 65, 23, 5, 1, 0, 255, 251, 239, 211, 162, 99, 45, 15, 4, 1, 0, 255, 251, 243, 223, 186, 131, 74, 33, 11, 3, 1, 0, 255, 252, 245, 230, 202, 158, 105, 57, 24, 8, 2, 1, 0, 255, 253, 247, 235, 214, 179, 132, 84, 44, 19, 7, 2, 1, 0, 255, 254, 250, 240, 223, 196, 159, 112, 69, 36, 15, 6, 2, 1, 0, 255, 254, 253, 245, 231, 209, 176, 136, 93, 55, 27, 11, 3, 2, 1, 0, 255, 254, 253, 252, 239, 221, 194, 158, 117, 76, 42, 18, 4, 3, 2, 1, 0 }; const opus_uint8 silk_shell_code_table_offsets[ 17 ] = { 0, 0, 2, 5, 9, 14, 20, 27, 35, 44, 54, 65, 77, 90, 104, 119, 135 }; const opus_uint8 silk_sign_iCDF[ 42 ] = { 254, 49, 67, 77, 82, 93, 99, 198, 11, 18, 24, 31, 36, 45, 255, 46, 66, 78, 87, 94, 104, 208, 14, 21, 32, 42, 51, 66, 255, 94, 104, 109, 112, 115, 118, 248, 53, 69, 80, 88, 95, 102 }; ================================================ FILE: deps/pjsip/third_party/opus/silk/tuning_parameters.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_TUNING_PARAMETERS_H #define SILK_TUNING_PARAMETERS_H #ifdef __cplusplus extern "C" { #endif /* Decay time for bitreservoir */ #define BITRESERVOIR_DECAY_TIME_MS 500 /*******************/ /* Pitch estimator */ /*******************/ /* Level of noise floor for whitening filter LPC analysis in pitch analysis */ #define FIND_PITCH_WHITE_NOISE_FRACTION 1e-3f /* Bandwidth expansion for whitening filter in pitch analysis */ #define FIND_PITCH_BANDWIDTH_EXPANSION 0.99f /*********************/ /* Linear prediction */ /*********************/ /* LPC analysis regularization */ #define FIND_LPC_COND_FAC 1e-5f /* LTP analysis defines */ #define FIND_LTP_COND_FAC 1e-5f #define LTP_DAMPING 0.05f #define LTP_SMOOTHING 0.1f /* LTP quantization settings */ #define MU_LTP_QUANT_NB 0.03f #define MU_LTP_QUANT_MB 0.025f #define MU_LTP_QUANT_WB 0.02f /* Max cumulative LTP gain */ #define MAX_SUM_LOG_GAIN_DB 250.0f /***********************/ /* High pass filtering */ /***********************/ /* Smoothing parameters for low end of pitch frequency range estimation */ #define VARIABLE_HP_SMTH_COEF1 0.1f #define VARIABLE_HP_SMTH_COEF2 0.015f #define VARIABLE_HP_MAX_DELTA_FREQ 0.4f /* Min and max cut-off frequency values (-3 dB points) */ #define VARIABLE_HP_MIN_CUTOFF_HZ 60 #define VARIABLE_HP_MAX_CUTOFF_HZ 100 /***********/ /* Various */ /***********/ /* VAD threshold */ #define SPEECH_ACTIVITY_DTX_THRES 0.05f /* Speech Activity LBRR enable threshold */ #define LBRR_SPEECH_ACTIVITY_THRES 0.3f /*************************/ /* Perceptual parameters */ /*************************/ /* reduction in coding SNR during low speech activity */ #define BG_SNR_DECR_dB 2.0f /* factor for reducing quantization noise during voiced speech */ #define HARM_SNR_INCR_dB 2.0f /* factor for reducing quantization noise for unvoiced sparse signals */ #define SPARSE_SNR_INCR_dB 2.0f /* threshold for sparseness measure above which to use lower quantization offset during unvoiced */ #define SPARSENESS_THRESHOLD_QNT_OFFSET 0.75f /* warping control */ #define WARPING_MULTIPLIER 0.015f /* fraction added to first autocorrelation value */ #define SHAPE_WHITE_NOISE_FRACTION 5e-5f /* noise shaping filter chirp factor */ #define BANDWIDTH_EXPANSION 0.95f /* difference between chirp factors for analysis and synthesis noise shaping filters at low bitrates */ #define LOW_RATE_BANDWIDTH_EXPANSION_DELTA 0.01f /* extra harmonic boosting (signal shaping) at low bitrates */ #define LOW_RATE_HARMONIC_BOOST 0.1f /* extra harmonic boosting (signal shaping) for noisy input signals */ #define LOW_INPUT_QUALITY_HARMONIC_BOOST 0.1f /* harmonic noise shaping */ #define HARMONIC_SHAPING 0.3f /* extra harmonic noise shaping for high bitrates or noisy input */ #define HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING 0.2f /* parameter for shaping noise towards higher frequencies */ #define HP_NOISE_COEF 0.25f /* parameter for shaping noise even more towards higher frequencies during voiced speech */ #define HARM_HP_NOISE_COEF 0.35f /* parameter for applying a high-pass tilt to the input signal */ #define INPUT_TILT 0.05f /* parameter for extra high-pass tilt to the input signal at high rates */ #define HIGH_RATE_INPUT_TILT 0.1f /* parameter for reducing noise at the very low frequencies */ #define LOW_FREQ_SHAPING 4.0f /* less reduction of noise at the very low frequencies for signals with low SNR at low frequencies */ #define LOW_QUALITY_LOW_FREQ_SHAPING_DECR 0.5f /* subframe smoothing coefficient for HarmBoost, HarmShapeGain, Tilt (lower -> more smoothing) */ #define SUBFR_SMTH_COEF 0.4f /* parameters defining the R/D tradeoff in the residual quantizer */ #define LAMBDA_OFFSET 1.2f #define LAMBDA_SPEECH_ACT -0.2f #define LAMBDA_DELAYED_DECISIONS -0.05f #define LAMBDA_INPUT_QUALITY -0.1f #define LAMBDA_CODING_QUALITY -0.2f #define LAMBDA_QUANT_OFFSET 0.8f /* Compensation in bitrate calculations for 10 ms modes */ #define REDUCE_BITRATE_10_MS_BPS 2200 /* Maximum time before allowing a bandwidth transition */ #define MAX_BANDWIDTH_SWITCH_DELAY_MS 5000 #ifdef __cplusplus } #endif #endif /* SILK_TUNING_PARAMETERS_H */ ================================================ FILE: deps/pjsip/third_party/opus/silk/typedef.h ================================================ /*********************************************************************** Copyright (c) 2006-2011, Skype Limited. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ #ifndef SILK_TYPEDEF_H #define SILK_TYPEDEF_H #include "opus_types.h" #include "opus_defines.h" #ifndef FIXED_POINT # include # define silk_float float # define silk_float_MAX FLT_MAX #endif #define silk_int64_MAX ((opus_int64)0x7FFFFFFFFFFFFFFFLL) /* 2^63 - 1 */ #define silk_int64_MIN ((opus_int64)0x8000000000000000LL) /* -2^63 */ #define silk_int32_MAX 0x7FFFFFFF /* 2^31 - 1 = 2147483647 */ #define silk_int32_MIN ((opus_int32)0x80000000) /* -2^31 = -2147483648 */ #define silk_int16_MAX 0x7FFF /* 2^15 - 1 = 32767 */ #define silk_int16_MIN ((opus_int16)0x8000) /* -2^15 = -32768 */ #define silk_int8_MAX 0x7F /* 2^7 - 1 = 127 */ #define silk_int8_MIN ((opus_int8)0x80) /* -2^7 = -128 */ #define silk_uint8_MAX 0xFF /* 2^8 - 1 = 255 */ #define silk_TRUE 1 #define silk_FALSE 0 /* assertions */ #if (defined _WIN32 && !defined _WINCE && !defined(__GNUC__) && !defined(NO_ASSERTS)) # ifndef silk_assert # include /* ASSERTE() */ # define silk_assert(COND) _ASSERTE(COND) # endif #else # ifdef ENABLE_ASSERTIONS # include # include #define silk_fatal(str) _silk_fatal(str, __FILE__, __LINE__); #ifdef __GNUC__ __attribute__((noreturn)) #endif static OPUS_INLINE void _silk_fatal(const char *str, const char *file, int line) { fprintf (stderr, "Fatal (internal) error in %s, line %d: %s\n", file, line, str); abort(); } # define silk_assert(COND) {if (!(COND)) {silk_fatal("assertion failed: " #COND);}} # else # define silk_assert(COND) # endif #endif #endif /* SILK_TYPEDEF_H */ ================================================ FILE: deps/pjsip/third_party/opus/silk/x86/NSQ_del_dec_sse.c ================================================ /* Copyright (c) 2014, Cisco Systems, INC Written by XiangMingZhu WeiZhou MinPeng YanWang Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "main.h" #include "celt/x86/x86cpu.h" #include "stack_alloc.h" typedef struct { opus_int32 sLPC_Q14[ MAX_SUB_FRAME_LENGTH + NSQ_LPC_BUF_LENGTH ]; opus_int32 RandState[ DECISION_DELAY ]; opus_int32 Q_Q10[ DECISION_DELAY ]; opus_int32 Xq_Q14[ DECISION_DELAY ]; opus_int32 Pred_Q15[ DECISION_DELAY ]; opus_int32 Shape_Q14[ DECISION_DELAY ]; opus_int32 sAR2_Q14[ MAX_SHAPE_LPC_ORDER ]; opus_int32 LF_AR_Q14; opus_int32 Seed; opus_int32 SeedInit; opus_int32 RD_Q10; } NSQ_del_dec_struct; typedef struct { opus_int32 Q_Q10; opus_int32 RD_Q10; opus_int32 xq_Q14; opus_int32 LF_AR_Q14; opus_int32 sLTP_shp_Q14; opus_int32 LPC_exc_Q14; } NSQ_sample_struct; typedef NSQ_sample_struct NSQ_sample_pair[ 2 ]; static OPUS_INLINE void silk_nsq_del_dec_scale_states_sse4_1( const silk_encoder_state *psEncC, /* I Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ NSQ_del_dec_struct psDelDec[], /* I/O Delayed decision states */ const opus_int32 x_Q3[], /* I Input in Q3 */ opus_int32 x_sc_Q10[], /* O Input scaled with 1/Gain in Q10 */ const opus_int16 sLTP[], /* I Re-whitened LTP state in Q0 */ opus_int32 sLTP_Q15[], /* O LTP state matching scaled input */ opus_int subfr, /* I Subframe number */ opus_int nStatesDelayedDecision, /* I Number of del dec states */ const opus_int LTP_scale_Q14, /* I LTP state scaling */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lag */ const opus_int signal_type, /* I Signal type */ const opus_int decisionDelay /* I Decision delay */ ); /******************************************/ /* Noise shape quantizer for one subframe */ /******************************************/ static OPUS_INLINE void silk_noise_shape_quantizer_del_dec_sse4_1( silk_nsq_state *NSQ, /* I/O NSQ state */ NSQ_del_dec_struct psDelDec[], /* I/O Delayed decision states */ opus_int signalType, /* I Signal type */ const opus_int32 x_Q10[], /* I */ opus_int8 pulses[], /* O */ opus_int16 xq[], /* O */ opus_int32 sLTP_Q15[], /* I/O LTP filter state */ opus_int32 delayedGain_Q10[], /* I/O Gain delay buffer */ const opus_int16 a_Q12[], /* I Short term prediction coefs */ const opus_int16 b_Q14[], /* I Long term prediction coefs */ const opus_int16 AR_shp_Q13[], /* I Noise shaping coefs */ opus_int lag, /* I Pitch lag */ opus_int32 HarmShapeFIRPacked_Q14, /* I */ opus_int Tilt_Q14, /* I Spectral tilt */ opus_int32 LF_shp_Q14, /* I */ opus_int32 Gain_Q16, /* I */ opus_int Lambda_Q10, /* I */ opus_int offset_Q10, /* I */ opus_int length, /* I Input length */ opus_int subfr, /* I Subframe number */ opus_int shapingLPCOrder, /* I Shaping LPC filter order */ opus_int predictLPCOrder, /* I Prediction filter order */ opus_int warping_Q16, /* I */ opus_int nStatesDelayedDecision, /* I Number of states in decision tree */ opus_int *smpl_buf_idx, /* I Index to newest samples in buffers */ opus_int decisionDelay /* I */ ); void silk_NSQ_del_dec_sse4_1( const silk_encoder_state *psEncC, /* I/O Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ SideInfoIndices *psIndices, /* I/O Quantization Indices */ const opus_int32 x_Q3[], /* I Prefiltered input signal */ opus_int8 pulses[], /* O Quantized pulse signal */ const opus_int16 PredCoef_Q12[ 2 * MAX_LPC_ORDER ], /* I Short term prediction coefs */ const opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ], /* I Long term prediction coefs */ const opus_int16 AR2_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ], /* I Noise shaping coefs */ const opus_int HarmShapeGain_Q14[ MAX_NB_SUBFR ], /* I Long term shaping coefs */ const opus_int Tilt_Q14[ MAX_NB_SUBFR ], /* I Spectral tilt */ const opus_int32 LF_shp_Q14[ MAX_NB_SUBFR ], /* I Low frequency shaping coefs */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I Quantization step sizes */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lags */ const opus_int Lambda_Q10, /* I Rate/distortion tradeoff */ const opus_int LTP_scale_Q14 /* I LTP state scaling */ ) { opus_int i, k, lag, start_idx, LSF_interpolation_flag, Winner_ind, subfr; opus_int last_smple_idx, smpl_buf_idx, decisionDelay; const opus_int16 *A_Q12, *B_Q14, *AR_shp_Q13; opus_int16 *pxq; VARDECL( opus_int32, sLTP_Q15 ); VARDECL( opus_int16, sLTP ); opus_int32 HarmShapeFIRPacked_Q14; opus_int offset_Q10; opus_int32 RDmin_Q10, Gain_Q10; VARDECL( opus_int32, x_sc_Q10 ); VARDECL( opus_int32, delayedGain_Q10 ); VARDECL( NSQ_del_dec_struct, psDelDec ); NSQ_del_dec_struct *psDD; SAVE_STACK; /* Set unvoiced lag to the previous one, overwrite later for voiced */ lag = NSQ->lagPrev; silk_assert( NSQ->prev_gain_Q16 != 0 ); /* Initialize delayed decision states */ ALLOC( psDelDec, psEncC->nStatesDelayedDecision, NSQ_del_dec_struct ); silk_memset( psDelDec, 0, psEncC->nStatesDelayedDecision * sizeof( NSQ_del_dec_struct ) ); for( k = 0; k < psEncC->nStatesDelayedDecision; k++ ) { psDD = &psDelDec[ k ]; psDD->Seed = ( k + psIndices->Seed ) & 3; psDD->SeedInit = psDD->Seed; psDD->RD_Q10 = 0; psDD->LF_AR_Q14 = NSQ->sLF_AR_shp_Q14; psDD->Shape_Q14[ 0 ] = NSQ->sLTP_shp_Q14[ psEncC->ltp_mem_length - 1 ]; silk_memcpy( psDD->sLPC_Q14, NSQ->sLPC_Q14, NSQ_LPC_BUF_LENGTH * sizeof( opus_int32 ) ); silk_memcpy( psDD->sAR2_Q14, NSQ->sAR2_Q14, sizeof( NSQ->sAR2_Q14 ) ); } offset_Q10 = silk_Quantization_Offsets_Q10[ psIndices->signalType >> 1 ][ psIndices->quantOffsetType ]; smpl_buf_idx = 0; /* index of oldest samples */ decisionDelay = silk_min_int( DECISION_DELAY, psEncC->subfr_length ); /* For voiced frames limit the decision delay to lower than the pitch lag */ if( psIndices->signalType == TYPE_VOICED ) { for( k = 0; k < psEncC->nb_subfr; k++ ) { decisionDelay = silk_min_int( decisionDelay, pitchL[ k ] - LTP_ORDER / 2 - 1 ); } } else { if( lag > 0 ) { decisionDelay = silk_min_int( decisionDelay, lag - LTP_ORDER / 2 - 1 ); } } if( psIndices->NLSFInterpCoef_Q2 == 4 ) { LSF_interpolation_flag = 0; } else { LSF_interpolation_flag = 1; } ALLOC( sLTP_Q15, psEncC->ltp_mem_length + psEncC->frame_length, opus_int32 ); ALLOC( sLTP, psEncC->ltp_mem_length + psEncC->frame_length, opus_int16 ); ALLOC( x_sc_Q10, psEncC->subfr_length, opus_int32 ); ALLOC( delayedGain_Q10, DECISION_DELAY, opus_int32 ); /* Set up pointers to start of sub frame */ pxq = &NSQ->xq[ psEncC->ltp_mem_length ]; NSQ->sLTP_shp_buf_idx = psEncC->ltp_mem_length; NSQ->sLTP_buf_idx = psEncC->ltp_mem_length; subfr = 0; for( k = 0; k < psEncC->nb_subfr; k++ ) { A_Q12 = &PredCoef_Q12[ ( ( k >> 1 ) | ( 1 - LSF_interpolation_flag ) ) * MAX_LPC_ORDER ]; B_Q14 = <PCoef_Q14[ k * LTP_ORDER ]; AR_shp_Q13 = &AR2_Q13[ k * MAX_SHAPE_LPC_ORDER ]; /* Noise shape parameters */ silk_assert( HarmShapeGain_Q14[ k ] >= 0 ); HarmShapeFIRPacked_Q14 = silk_RSHIFT( HarmShapeGain_Q14[ k ], 2 ); HarmShapeFIRPacked_Q14 |= silk_LSHIFT( (opus_int32)silk_RSHIFT( HarmShapeGain_Q14[ k ], 1 ), 16 ); NSQ->rewhite_flag = 0; if( psIndices->signalType == TYPE_VOICED ) { /* Voiced */ lag = pitchL[ k ]; /* Re-whitening */ if( ( k & ( 3 - silk_LSHIFT( LSF_interpolation_flag, 1 ) ) ) == 0 ) { if( k == 2 ) { /* RESET DELAYED DECISIONS */ /* Find winner */ RDmin_Q10 = psDelDec[ 0 ].RD_Q10; Winner_ind = 0; for( i = 1; i < psEncC->nStatesDelayedDecision; i++ ) { if( psDelDec[ i ].RD_Q10 < RDmin_Q10 ) { RDmin_Q10 = psDelDec[ i ].RD_Q10; Winner_ind = i; } } for( i = 0; i < psEncC->nStatesDelayedDecision; i++ ) { if( i != Winner_ind ) { psDelDec[ i ].RD_Q10 += ( silk_int32_MAX >> 4 ); silk_assert( psDelDec[ i ].RD_Q10 >= 0 ); } } /* Copy final part of signals from winner state to output and long-term filter states */ psDD = &psDelDec[ Winner_ind ]; last_smple_idx = smpl_buf_idx + decisionDelay; for( i = 0; i < decisionDelay; i++ ) { last_smple_idx = ( last_smple_idx - 1 ) & DECISION_DELAY_MASK; pulses[ i - decisionDelay ] = (opus_int8)silk_RSHIFT_ROUND( psDD->Q_Q10[ last_smple_idx ], 10 ); pxq[ i - decisionDelay ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_SMULWW( psDD->Xq_Q14[ last_smple_idx ], Gains_Q16[ 1 ] ), 14 ) ); NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - decisionDelay + i ] = psDD->Shape_Q14[ last_smple_idx ]; } subfr = 0; } /* Rewhiten with new A coefs */ start_idx = psEncC->ltp_mem_length - lag - psEncC->predictLPCOrder - LTP_ORDER / 2; silk_assert( start_idx > 0 ); silk_LPC_analysis_filter( &sLTP[ start_idx ], &NSQ->xq[ start_idx + k * psEncC->subfr_length ], A_Q12, psEncC->ltp_mem_length - start_idx, psEncC->predictLPCOrder, psEncC->arch ); NSQ->sLTP_buf_idx = psEncC->ltp_mem_length; NSQ->rewhite_flag = 1; } } silk_nsq_del_dec_scale_states_sse4_1( psEncC, NSQ, psDelDec, x_Q3, x_sc_Q10, sLTP, sLTP_Q15, k, psEncC->nStatesDelayedDecision, LTP_scale_Q14, Gains_Q16, pitchL, psIndices->signalType, decisionDelay ); silk_noise_shape_quantizer_del_dec_sse4_1( NSQ, psDelDec, psIndices->signalType, x_sc_Q10, pulses, pxq, sLTP_Q15, delayedGain_Q10, A_Q12, B_Q14, AR_shp_Q13, lag, HarmShapeFIRPacked_Q14, Tilt_Q14[ k ], LF_shp_Q14[ k ], Gains_Q16[ k ], Lambda_Q10, offset_Q10, psEncC->subfr_length, subfr++, psEncC->shapingLPCOrder, psEncC->predictLPCOrder, psEncC->warping_Q16, psEncC->nStatesDelayedDecision, &smpl_buf_idx, decisionDelay ); x_Q3 += psEncC->subfr_length; pulses += psEncC->subfr_length; pxq += psEncC->subfr_length; } /* Find winner */ RDmin_Q10 = psDelDec[ 0 ].RD_Q10; Winner_ind = 0; for( k = 1; k < psEncC->nStatesDelayedDecision; k++ ) { if( psDelDec[ k ].RD_Q10 < RDmin_Q10 ) { RDmin_Q10 = psDelDec[ k ].RD_Q10; Winner_ind = k; } } /* Copy final part of signals from winner state to output and long-term filter states */ psDD = &psDelDec[ Winner_ind ]; psIndices->Seed = psDD->SeedInit; last_smple_idx = smpl_buf_idx + decisionDelay; Gain_Q10 = silk_RSHIFT32( Gains_Q16[ psEncC->nb_subfr - 1 ], 6 ); for( i = 0; i < decisionDelay; i++ ) { last_smple_idx = ( last_smple_idx - 1 ) & DECISION_DELAY_MASK; pulses[ i - decisionDelay ] = (opus_int8)silk_RSHIFT_ROUND( psDD->Q_Q10[ last_smple_idx ], 10 ); pxq[ i - decisionDelay ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_SMULWW( psDD->Xq_Q14[ last_smple_idx ], Gain_Q10 ), 8 ) ); NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - decisionDelay + i ] = psDD->Shape_Q14[ last_smple_idx ]; } silk_memcpy( NSQ->sLPC_Q14, &psDD->sLPC_Q14[ psEncC->subfr_length ], NSQ_LPC_BUF_LENGTH * sizeof( opus_int32 ) ); silk_memcpy( NSQ->sAR2_Q14, psDD->sAR2_Q14, sizeof( psDD->sAR2_Q14 ) ); /* Update states */ NSQ->sLF_AR_shp_Q14 = psDD->LF_AR_Q14; NSQ->lagPrev = pitchL[ psEncC->nb_subfr - 1 ]; /* Save quantized speech signal */ /* DEBUG_STORE_DATA( enc.pcm, &NSQ->xq[psEncC->ltp_mem_length], psEncC->frame_length * sizeof( opus_int16 ) ) */ silk_memmove( NSQ->xq, &NSQ->xq[ psEncC->frame_length ], psEncC->ltp_mem_length * sizeof( opus_int16 ) ); silk_memmove( NSQ->sLTP_shp_Q14, &NSQ->sLTP_shp_Q14[ psEncC->frame_length ], psEncC->ltp_mem_length * sizeof( opus_int32 ) ); RESTORE_STACK; } /******************************************/ /* Noise shape quantizer for one subframe */ /******************************************/ static OPUS_INLINE void silk_noise_shape_quantizer_del_dec_sse4_1( silk_nsq_state *NSQ, /* I/O NSQ state */ NSQ_del_dec_struct psDelDec[], /* I/O Delayed decision states */ opus_int signalType, /* I Signal type */ const opus_int32 x_Q10[], /* I */ opus_int8 pulses[], /* O */ opus_int16 xq[], /* O */ opus_int32 sLTP_Q15[], /* I/O LTP filter state */ opus_int32 delayedGain_Q10[], /* I/O Gain delay buffer */ const opus_int16 a_Q12[], /* I Short term prediction coefs */ const opus_int16 b_Q14[], /* I Long term prediction coefs */ const opus_int16 AR_shp_Q13[], /* I Noise shaping coefs */ opus_int lag, /* I Pitch lag */ opus_int32 HarmShapeFIRPacked_Q14, /* I */ opus_int Tilt_Q14, /* I Spectral tilt */ opus_int32 LF_shp_Q14, /* I */ opus_int32 Gain_Q16, /* I */ opus_int Lambda_Q10, /* I */ opus_int offset_Q10, /* I */ opus_int length, /* I Input length */ opus_int subfr, /* I Subframe number */ opus_int shapingLPCOrder, /* I Shaping LPC filter order */ opus_int predictLPCOrder, /* I Prediction filter order */ opus_int warping_Q16, /* I */ opus_int nStatesDelayedDecision, /* I Number of states in decision tree */ opus_int *smpl_buf_idx, /* I Index to newest samples in buffers */ opus_int decisionDelay /* I */ ) { opus_int i, j, k, Winner_ind, RDmin_ind, RDmax_ind, last_smple_idx; opus_int32 Winner_rand_state; opus_int32 LTP_pred_Q14, LPC_pred_Q14, n_AR_Q14, n_LTP_Q14; opus_int32 n_LF_Q14, r_Q10, rr_Q10, rd1_Q10, rd2_Q10, RDmin_Q10, RDmax_Q10; opus_int32 q1_Q0, q1_Q10, q2_Q10, exc_Q14, LPC_exc_Q14, xq_Q14, Gain_Q10; opus_int32 tmp1, tmp2, sLF_AR_shp_Q14; opus_int32 *pred_lag_ptr, *shp_lag_ptr, *psLPC_Q14; VARDECL( NSQ_sample_pair, psSampleState ); NSQ_del_dec_struct *psDD; NSQ_sample_struct *psSS; __m128i a_Q12_0123, a_Q12_4567, a_Q12_89AB, a_Q12_CDEF; __m128i b_Q12_0123, b_sr_Q12_0123; SAVE_STACK; silk_assert( nStatesDelayedDecision > 0 ); ALLOC( psSampleState, nStatesDelayedDecision, NSQ_sample_pair ); shp_lag_ptr = &NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - lag + HARM_SHAPE_FIR_TAPS / 2 ]; pred_lag_ptr = &sLTP_Q15[ NSQ->sLTP_buf_idx - lag + LTP_ORDER / 2 ]; Gain_Q10 = silk_RSHIFT( Gain_Q16, 6 ); a_Q12_0123 = OP_CVTEPI16_EPI32_M64( a_Q12 ); a_Q12_4567 = OP_CVTEPI16_EPI32_M64( a_Q12 + 4 ); if( opus_likely( predictLPCOrder == 16 ) ) { a_Q12_89AB = OP_CVTEPI16_EPI32_M64( a_Q12 + 8 ); a_Q12_CDEF = OP_CVTEPI16_EPI32_M64( a_Q12 + 12 ); } if( signalType == TYPE_VOICED ){ b_Q12_0123 = OP_CVTEPI16_EPI32_M64( b_Q14 ); b_sr_Q12_0123 = _mm_shuffle_epi32( b_Q12_0123, _MM_SHUFFLE( 0, 3, 2, 1 ) ); /* equal shift right 4 bytes */ } for( i = 0; i < length; i++ ) { /* Perform common calculations used in all states */ /* Long-term prediction */ if( signalType == TYPE_VOICED ) { /* Unrolled loop */ /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ LTP_pred_Q14 = 2; { __m128i tmpa, tmpb, pred_lag_ptr_tmp; pred_lag_ptr_tmp = _mm_loadu_si128( (__m128i *)(&pred_lag_ptr[ -3 ] ) ); pred_lag_ptr_tmp = _mm_shuffle_epi32( pred_lag_ptr_tmp, 0x1B ); tmpa = _mm_mul_epi32( pred_lag_ptr_tmp, b_Q12_0123 ); tmpa = _mm_srli_si128( tmpa, 2 ); pred_lag_ptr_tmp = _mm_shuffle_epi32( pred_lag_ptr_tmp, _MM_SHUFFLE( 0, 3, 2, 1 ) );/* equal shift right 4 bytes */ pred_lag_ptr_tmp = _mm_mul_epi32( pred_lag_ptr_tmp, b_sr_Q12_0123 ); pred_lag_ptr_tmp = _mm_srli_si128( pred_lag_ptr_tmp, 2 ); pred_lag_ptr_tmp = _mm_add_epi32( pred_lag_ptr_tmp, tmpa ); tmpb = _mm_shuffle_epi32( pred_lag_ptr_tmp, _MM_SHUFFLE( 0, 0, 3, 2 ) );/* equal shift right 8 bytes */ pred_lag_ptr_tmp = _mm_add_epi32( pred_lag_ptr_tmp, tmpb ); LTP_pred_Q14 += _mm_cvtsi128_si32( pred_lag_ptr_tmp ); LTP_pred_Q14 = silk_SMLAWB( LTP_pred_Q14, pred_lag_ptr[ -4 ], b_Q14[ 4 ] ); LTP_pred_Q14 = silk_LSHIFT( LTP_pred_Q14, 1 ); /* Q13 -> Q14 */ pred_lag_ptr++; } } else { LTP_pred_Q14 = 0; } /* Long-term shaping */ if( lag > 0 ) { /* Symmetric, packed FIR coefficients */ n_LTP_Q14 = silk_SMULWB( silk_ADD32( shp_lag_ptr[ 0 ], shp_lag_ptr[ -2 ] ), HarmShapeFIRPacked_Q14 ); n_LTP_Q14 = silk_SMLAWT( n_LTP_Q14, shp_lag_ptr[ -1 ], HarmShapeFIRPacked_Q14 ); n_LTP_Q14 = silk_SUB_LSHIFT32( LTP_pred_Q14, n_LTP_Q14, 2 ); /* Q12 -> Q14 */ shp_lag_ptr++; } else { n_LTP_Q14 = 0; } { __m128i tmpa, tmpb, psLPC_Q14_tmp, a_Q12_tmp; for( k = 0; k < nStatesDelayedDecision; k++ ) { /* Delayed decision state */ psDD = &psDelDec[ k ]; /* Sample state */ psSS = psSampleState[ k ]; /* Generate dither */ psDD->Seed = silk_RAND( psDD->Seed ); /* Pointer used in short term prediction and shaping */ psLPC_Q14 = &psDD->sLPC_Q14[ NSQ_LPC_BUF_LENGTH - 1 + i ]; /* Short-term prediction */ silk_assert( predictLPCOrder == 10 || predictLPCOrder == 16 ); /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ LPC_pred_Q14 = silk_RSHIFT( predictLPCOrder, 1 ); tmpb = _mm_setzero_si128(); /* step 1 */ psLPC_Q14_tmp = _mm_loadu_si128( (__m128i *)(&psLPC_Q14[ -3 ] ) ); /* -3, -2 , -1, 0 */ psLPC_Q14_tmp = _mm_shuffle_epi32( psLPC_Q14_tmp, 0x1B ); /* 0, -1, -2, -3 */ tmpa = _mm_mul_epi32( psLPC_Q14_tmp, a_Q12_0123 ); /* 0, -1, -2, -3 * 0123 -> 0*0, 2*-2 */ tmpa = _mm_srli_epi64( tmpa, 16 ); tmpb = _mm_add_epi32( tmpb, tmpa ); psLPC_Q14_tmp = _mm_shuffle_epi32( psLPC_Q14_tmp, _MM_SHUFFLE( 0, 3, 2, 1 ) ); /* equal shift right 4 bytes */ a_Q12_tmp = _mm_shuffle_epi32( a_Q12_0123, _MM_SHUFFLE(0, 3, 2, 1 ) ); /* equal shift right 4 bytes */ psLPC_Q14_tmp = _mm_mul_epi32( psLPC_Q14_tmp, a_Q12_tmp ); /* 1*-1, 3*-3 */ psLPC_Q14_tmp = _mm_srli_epi64( psLPC_Q14_tmp, 16 ); tmpb = _mm_add_epi32( tmpb, psLPC_Q14_tmp ); /* step 2 */ psLPC_Q14_tmp = _mm_loadu_si128( (__m128i *)(&psLPC_Q14[ -7 ] ) ); psLPC_Q14_tmp = _mm_shuffle_epi32( psLPC_Q14_tmp, 0x1B ); tmpa = _mm_mul_epi32( psLPC_Q14_tmp, a_Q12_4567 ); tmpa = _mm_srli_epi64( tmpa, 16 ); tmpb = _mm_add_epi32( tmpb, tmpa ); psLPC_Q14_tmp = _mm_shuffle_epi32( psLPC_Q14_tmp, _MM_SHUFFLE( 0, 3, 2, 1 ) ); /* equal shift right 4 bytes */ a_Q12_tmp = _mm_shuffle_epi32( a_Q12_4567, _MM_SHUFFLE(0, 3, 2, 1 ) ); /* equal shift right 4 bytes */ psLPC_Q14_tmp = _mm_mul_epi32( psLPC_Q14_tmp, a_Q12_tmp ); psLPC_Q14_tmp = _mm_srli_epi64( psLPC_Q14_tmp, 16 ); tmpb = _mm_add_epi32( tmpb, psLPC_Q14_tmp ); if ( opus_likely( predictLPCOrder == 16 ) ) { /* step 3 */ psLPC_Q14_tmp = _mm_loadu_si128( (__m128i *)(&psLPC_Q14[ -11 ] ) ); psLPC_Q14_tmp = _mm_shuffle_epi32( psLPC_Q14_tmp, 0x1B ); tmpa = _mm_mul_epi32( psLPC_Q14_tmp, a_Q12_89AB ); tmpa = _mm_srli_epi64( tmpa, 16 ); tmpb = _mm_add_epi32( tmpb, tmpa ); psLPC_Q14_tmp = _mm_shuffle_epi32( psLPC_Q14_tmp, _MM_SHUFFLE( 0, 3, 2, 1 ) ); /* equal shift right 4 bytes */ a_Q12_tmp = _mm_shuffle_epi32( a_Q12_89AB, _MM_SHUFFLE(0, 3, 2, 1 ) );/* equal shift right 4 bytes */ psLPC_Q14_tmp = _mm_mul_epi32( psLPC_Q14_tmp, a_Q12_tmp ); psLPC_Q14_tmp = _mm_srli_epi64( psLPC_Q14_tmp, 16 ); tmpb = _mm_add_epi32( tmpb, psLPC_Q14_tmp ); /* setp 4 */ psLPC_Q14_tmp = _mm_loadu_si128( (__m128i *)(&psLPC_Q14[ -15 ] ) ); psLPC_Q14_tmp = _mm_shuffle_epi32( psLPC_Q14_tmp, 0x1B ); tmpa = _mm_mul_epi32( psLPC_Q14_tmp, a_Q12_CDEF ); tmpa = _mm_srli_epi64( tmpa, 16 ); tmpb = _mm_add_epi32( tmpb, tmpa ); psLPC_Q14_tmp = _mm_shuffle_epi32( psLPC_Q14_tmp, _MM_SHUFFLE( 0, 3, 2, 1 ) ); /* equal shift right 4 bytes */ a_Q12_tmp = _mm_shuffle_epi32( a_Q12_CDEF, _MM_SHUFFLE(0, 3, 2, 1 ) ); /* equal shift right 4 bytes */ psLPC_Q14_tmp = _mm_mul_epi32( psLPC_Q14_tmp, a_Q12_tmp ); psLPC_Q14_tmp = _mm_srli_epi64( psLPC_Q14_tmp, 16 ); tmpb = _mm_add_epi32( tmpb, psLPC_Q14_tmp ); /* add at last */ /* equal shift right 8 bytes*/ tmpa = _mm_shuffle_epi32( tmpb, _MM_SHUFFLE( 0, 0, 3, 2 ) ); tmpb = _mm_add_epi32( tmpb, tmpa ); LPC_pred_Q14 += _mm_cvtsi128_si32( tmpb ); } else { /* add at last */ tmpa = _mm_shuffle_epi32( tmpb, _MM_SHUFFLE( 0, 0, 3, 2 ) ); /* equal shift right 8 bytes*/ tmpb = _mm_add_epi32( tmpb, tmpa ); LPC_pred_Q14 += _mm_cvtsi128_si32( tmpb ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -8 ], a_Q12[ 8 ] ); LPC_pred_Q14 = silk_SMLAWB( LPC_pred_Q14, psLPC_Q14[ -9 ], a_Q12[ 9 ] ); } LPC_pred_Q14 = silk_LSHIFT( LPC_pred_Q14, 4 ); /* Q10 -> Q14 */ /* Noise shape feedback */ silk_assert( ( shapingLPCOrder & 1 ) == 0 ); /* check that order is even */ /* Output of lowpass section */ tmp2 = silk_SMLAWB( psLPC_Q14[ 0 ], psDD->sAR2_Q14[ 0 ], warping_Q16 ); /* Output of allpass section */ tmp1 = silk_SMLAWB( psDD->sAR2_Q14[ 0 ], psDD->sAR2_Q14[ 1 ] - tmp2, warping_Q16 ); psDD->sAR2_Q14[ 0 ] = tmp2; n_AR_Q14 = silk_RSHIFT( shapingLPCOrder, 1 ); n_AR_Q14 = silk_SMLAWB( n_AR_Q14, tmp2, AR_shp_Q13[ 0 ] ); /* Loop over allpass sections */ for( j = 2; j < shapingLPCOrder; j += 2 ) { /* Output of allpass section */ tmp2 = silk_SMLAWB( psDD->sAR2_Q14[ j - 1 ], psDD->sAR2_Q14[ j + 0 ] - tmp1, warping_Q16 ); psDD->sAR2_Q14[ j - 1 ] = tmp1; n_AR_Q14 = silk_SMLAWB( n_AR_Q14, tmp1, AR_shp_Q13[ j - 1 ] ); /* Output of allpass section */ tmp1 = silk_SMLAWB( psDD->sAR2_Q14[ j + 0 ], psDD->sAR2_Q14[ j + 1 ] - tmp2, warping_Q16 ); psDD->sAR2_Q14[ j + 0 ] = tmp2; n_AR_Q14 = silk_SMLAWB( n_AR_Q14, tmp2, AR_shp_Q13[ j ] ); } psDD->sAR2_Q14[ shapingLPCOrder - 1 ] = tmp1; n_AR_Q14 = silk_SMLAWB( n_AR_Q14, tmp1, AR_shp_Q13[ shapingLPCOrder - 1 ] ); n_AR_Q14 = silk_LSHIFT( n_AR_Q14, 1 ); /* Q11 -> Q12 */ n_AR_Q14 = silk_SMLAWB( n_AR_Q14, psDD->LF_AR_Q14, Tilt_Q14 ); /* Q12 */ n_AR_Q14 = silk_LSHIFT( n_AR_Q14, 2 ); /* Q12 -> Q14 */ n_LF_Q14 = silk_SMULWB( psDD->Shape_Q14[ *smpl_buf_idx ], LF_shp_Q14 ); /* Q12 */ n_LF_Q14 = silk_SMLAWT( n_LF_Q14, psDD->LF_AR_Q14, LF_shp_Q14 ); /* Q12 */ n_LF_Q14 = silk_LSHIFT( n_LF_Q14, 2 ); /* Q12 -> Q14 */ /* Input minus prediction plus noise feedback */ /* r = x[ i ] - LTP_pred - LPC_pred + n_AR + n_Tilt + n_LF + n_LTP */ tmp1 = silk_ADD32( n_AR_Q14, n_LF_Q14 ); /* Q14 */ tmp2 = silk_ADD32( n_LTP_Q14, LPC_pred_Q14 ); /* Q13 */ tmp1 = silk_SUB32( tmp2, tmp1 ); /* Q13 */ tmp1 = silk_RSHIFT_ROUND( tmp1, 4 ); /* Q10 */ r_Q10 = silk_SUB32( x_Q10[ i ], tmp1 ); /* residual error Q10 */ /* Flip sign depending on dither */ if ( psDD->Seed < 0 ) { r_Q10 = -r_Q10; } r_Q10 = silk_LIMIT_32( r_Q10, -(31 << 10), 30 << 10 ); /* Find two quantization level candidates and measure their rate-distortion */ q1_Q10 = silk_SUB32( r_Q10, offset_Q10 ); q1_Q0 = silk_RSHIFT( q1_Q10, 10 ); if( q1_Q0 > 0 ) { q1_Q10 = silk_SUB32( silk_LSHIFT( q1_Q0, 10 ), QUANT_LEVEL_ADJUST_Q10 ); q1_Q10 = silk_ADD32( q1_Q10, offset_Q10 ); q2_Q10 = silk_ADD32( q1_Q10, 1024 ); rd1_Q10 = silk_SMULBB( q1_Q10, Lambda_Q10 ); rd2_Q10 = silk_SMULBB( q2_Q10, Lambda_Q10 ); } else if( q1_Q0 == 0 ) { q1_Q10 = offset_Q10; q2_Q10 = silk_ADD32( q1_Q10, 1024 - QUANT_LEVEL_ADJUST_Q10 ); rd1_Q10 = silk_SMULBB( q1_Q10, Lambda_Q10 ); rd2_Q10 = silk_SMULBB( q2_Q10, Lambda_Q10 ); } else if( q1_Q0 == -1 ) { q2_Q10 = offset_Q10; q1_Q10 = silk_SUB32( q2_Q10, 1024 - QUANT_LEVEL_ADJUST_Q10 ); rd1_Q10 = silk_SMULBB( -q1_Q10, Lambda_Q10 ); rd2_Q10 = silk_SMULBB( q2_Q10, Lambda_Q10 ); } else { /* q1_Q0 < -1 */ q1_Q10 = silk_ADD32( silk_LSHIFT( q1_Q0, 10 ), QUANT_LEVEL_ADJUST_Q10 ); q1_Q10 = silk_ADD32( q1_Q10, offset_Q10 ); q2_Q10 = silk_ADD32( q1_Q10, 1024 ); rd1_Q10 = silk_SMULBB( -q1_Q10, Lambda_Q10 ); rd2_Q10 = silk_SMULBB( -q2_Q10, Lambda_Q10 ); } rr_Q10 = silk_SUB32( r_Q10, q1_Q10 ); rd1_Q10 = silk_RSHIFT( silk_SMLABB( rd1_Q10, rr_Q10, rr_Q10 ), 10 ); rr_Q10 = silk_SUB32( r_Q10, q2_Q10 ); rd2_Q10 = silk_RSHIFT( silk_SMLABB( rd2_Q10, rr_Q10, rr_Q10 ), 10 ); if( rd1_Q10 < rd2_Q10 ) { psSS[ 0 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd1_Q10 ); psSS[ 1 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd2_Q10 ); psSS[ 0 ].Q_Q10 = q1_Q10; psSS[ 1 ].Q_Q10 = q2_Q10; } else { psSS[ 0 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd2_Q10 ); psSS[ 1 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd1_Q10 ); psSS[ 0 ].Q_Q10 = q2_Q10; psSS[ 1 ].Q_Q10 = q1_Q10; } /* Update states for best quantization */ /* Quantized excitation */ exc_Q14 = silk_LSHIFT32( psSS[ 0 ].Q_Q10, 4 ); if ( psDD->Seed < 0 ) { exc_Q14 = -exc_Q14; } /* Add predictions */ LPC_exc_Q14 = silk_ADD32( exc_Q14, LTP_pred_Q14 ); xq_Q14 = silk_ADD32( LPC_exc_Q14, LPC_pred_Q14 ); /* Update states */ sLF_AR_shp_Q14 = silk_SUB32( xq_Q14, n_AR_Q14 ); psSS[ 0 ].sLTP_shp_Q14 = silk_SUB32( sLF_AR_shp_Q14, n_LF_Q14 ); psSS[ 0 ].LF_AR_Q14 = sLF_AR_shp_Q14; psSS[ 0 ].LPC_exc_Q14 = LPC_exc_Q14; psSS[ 0 ].xq_Q14 = xq_Q14; /* Update states for second best quantization */ /* Quantized excitation */ exc_Q14 = silk_LSHIFT32( psSS[ 1 ].Q_Q10, 4 ); if ( psDD->Seed < 0 ) { exc_Q14 = -exc_Q14; } /* Add predictions */ LPC_exc_Q14 = silk_ADD32( exc_Q14, LTP_pred_Q14 ); xq_Q14 = silk_ADD32( LPC_exc_Q14, LPC_pred_Q14 ); /* Update states */ sLF_AR_shp_Q14 = silk_SUB32( xq_Q14, n_AR_Q14 ); psSS[ 1 ].sLTP_shp_Q14 = silk_SUB32( sLF_AR_shp_Q14, n_LF_Q14 ); psSS[ 1 ].LF_AR_Q14 = sLF_AR_shp_Q14; psSS[ 1 ].LPC_exc_Q14 = LPC_exc_Q14; psSS[ 1 ].xq_Q14 = xq_Q14; } } *smpl_buf_idx = ( *smpl_buf_idx - 1 ) & DECISION_DELAY_MASK; /* Index to newest samples */ last_smple_idx = ( *smpl_buf_idx + decisionDelay ) & DECISION_DELAY_MASK; /* Index to decisionDelay old samples */ /* Find winner */ RDmin_Q10 = psSampleState[ 0 ][ 0 ].RD_Q10; Winner_ind = 0; for( k = 1; k < nStatesDelayedDecision; k++ ) { if( psSampleState[ k ][ 0 ].RD_Q10 < RDmin_Q10 ) { RDmin_Q10 = psSampleState[ k ][ 0 ].RD_Q10; Winner_ind = k; } } /* Increase RD values of expired states */ Winner_rand_state = psDelDec[ Winner_ind ].RandState[ last_smple_idx ]; for( k = 0; k < nStatesDelayedDecision; k++ ) { if( psDelDec[ k ].RandState[ last_smple_idx ] != Winner_rand_state ) { psSampleState[ k ][ 0 ].RD_Q10 = silk_ADD32( psSampleState[ k ][ 0 ].RD_Q10, silk_int32_MAX >> 4 ); psSampleState[ k ][ 1 ].RD_Q10 = silk_ADD32( psSampleState[ k ][ 1 ].RD_Q10, silk_int32_MAX >> 4 ); silk_assert( psSampleState[ k ][ 0 ].RD_Q10 >= 0 ); } } /* Find worst in first set and best in second set */ RDmax_Q10 = psSampleState[ 0 ][ 0 ].RD_Q10; RDmin_Q10 = psSampleState[ 0 ][ 1 ].RD_Q10; RDmax_ind = 0; RDmin_ind = 0; for( k = 1; k < nStatesDelayedDecision; k++ ) { /* find worst in first set */ if( psSampleState[ k ][ 0 ].RD_Q10 > RDmax_Q10 ) { RDmax_Q10 = psSampleState[ k ][ 0 ].RD_Q10; RDmax_ind = k; } /* find best in second set */ if( psSampleState[ k ][ 1 ].RD_Q10 < RDmin_Q10 ) { RDmin_Q10 = psSampleState[ k ][ 1 ].RD_Q10; RDmin_ind = k; } } /* Replace a state if best from second set outperforms worst in first set */ if( RDmin_Q10 < RDmax_Q10 ) { silk_memcpy( ( (opus_int32 *)&psDelDec[ RDmax_ind ] ) + i, ( (opus_int32 *)&psDelDec[ RDmin_ind ] ) + i, sizeof( NSQ_del_dec_struct ) - i * sizeof( opus_int32) ); silk_memcpy( &psSampleState[ RDmax_ind ][ 0 ], &psSampleState[ RDmin_ind ][ 1 ], sizeof( NSQ_sample_struct ) ); } /* Write samples from winner to output and long-term filter states */ psDD = &psDelDec[ Winner_ind ]; if( subfr > 0 || i >= decisionDelay ) { pulses[ i - decisionDelay ] = (opus_int8)silk_RSHIFT_ROUND( psDD->Q_Q10[ last_smple_idx ], 10 ); xq[ i - decisionDelay ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_SMULWW( psDD->Xq_Q14[ last_smple_idx ], delayedGain_Q10[ last_smple_idx ] ), 8 ) ); NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - decisionDelay ] = psDD->Shape_Q14[ last_smple_idx ]; sLTP_Q15[ NSQ->sLTP_buf_idx - decisionDelay ] = psDD->Pred_Q15[ last_smple_idx ]; } NSQ->sLTP_shp_buf_idx++; NSQ->sLTP_buf_idx++; /* Update states */ for( k = 0; k < nStatesDelayedDecision; k++ ) { psDD = &psDelDec[ k ]; psSS = &psSampleState[ k ][ 0 ]; psDD->LF_AR_Q14 = psSS->LF_AR_Q14; psDD->sLPC_Q14[ NSQ_LPC_BUF_LENGTH + i ] = psSS->xq_Q14; psDD->Xq_Q14[ *smpl_buf_idx ] = psSS->xq_Q14; psDD->Q_Q10[ *smpl_buf_idx ] = psSS->Q_Q10; psDD->Pred_Q15[ *smpl_buf_idx ] = silk_LSHIFT32( psSS->LPC_exc_Q14, 1 ); psDD->Shape_Q14[ *smpl_buf_idx ] = psSS->sLTP_shp_Q14; psDD->Seed = silk_ADD32_ovflw( psDD->Seed, silk_RSHIFT_ROUND( psSS->Q_Q10, 10 ) ); psDD->RandState[ *smpl_buf_idx ] = psDD->Seed; psDD->RD_Q10 = psSS->RD_Q10; } delayedGain_Q10[ *smpl_buf_idx ] = Gain_Q10; } /* Update LPC states */ for( k = 0; k < nStatesDelayedDecision; k++ ) { psDD = &psDelDec[ k ]; silk_memcpy( psDD->sLPC_Q14, &psDD->sLPC_Q14[ length ], NSQ_LPC_BUF_LENGTH * sizeof( opus_int32 ) ); } RESTORE_STACK; } static OPUS_INLINE void silk_nsq_del_dec_scale_states_sse4_1( const silk_encoder_state *psEncC, /* I Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ NSQ_del_dec_struct psDelDec[], /* I/O Delayed decision states */ const opus_int32 x_Q3[], /* I Input in Q3 */ opus_int32 x_sc_Q10[], /* O Input scaled with 1/Gain in Q10 */ const opus_int16 sLTP[], /* I Re-whitened LTP state in Q0 */ opus_int32 sLTP_Q15[], /* O LTP state matching scaled input */ opus_int subfr, /* I Subframe number */ opus_int nStatesDelayedDecision, /* I Number of del dec states */ const opus_int LTP_scale_Q14, /* I LTP state scaling */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lag */ const opus_int signal_type, /* I Signal type */ const opus_int decisionDelay /* I Decision delay */ ) { opus_int i, k, lag; opus_int32 gain_adj_Q16, inv_gain_Q31, inv_gain_Q23; NSQ_del_dec_struct *psDD; __m128i xmm_inv_gain_Q23, xmm_x_Q3_x2x0, xmm_x_Q3_x3x1; lag = pitchL[ subfr ]; inv_gain_Q31 = silk_INVERSE32_varQ( silk_max( Gains_Q16[ subfr ], 1 ), 47 ); silk_assert( inv_gain_Q31 != 0 ); /* Calculate gain adjustment factor */ if( Gains_Q16[ subfr ] != NSQ->prev_gain_Q16 ) { gain_adj_Q16 = silk_DIV32_varQ( NSQ->prev_gain_Q16, Gains_Q16[ subfr ], 16 ); } else { gain_adj_Q16 = (opus_int32)1 << 16; } /* Scale input */ inv_gain_Q23 = silk_RSHIFT_ROUND( inv_gain_Q31, 8 ); /* prepare inv_gain_Q23 in packed 4 32-bits */ xmm_inv_gain_Q23 = _mm_set1_epi32(inv_gain_Q23); for( i = 0; i < psEncC->subfr_length - 3; i += 4 ) { xmm_x_Q3_x2x0 = _mm_loadu_si128( (__m128i *)(&(x_Q3[ i ] ) ) ); /* equal shift right 4 bytes*/ xmm_x_Q3_x3x1 = _mm_shuffle_epi32( xmm_x_Q3_x2x0, _MM_SHUFFLE( 0, 3, 2, 1 ) ); xmm_x_Q3_x2x0 = _mm_mul_epi32( xmm_x_Q3_x2x0, xmm_inv_gain_Q23 ); xmm_x_Q3_x3x1 = _mm_mul_epi32( xmm_x_Q3_x3x1, xmm_inv_gain_Q23 ); xmm_x_Q3_x2x0 = _mm_srli_epi64( xmm_x_Q3_x2x0, 16 ); xmm_x_Q3_x3x1 = _mm_slli_epi64( xmm_x_Q3_x3x1, 16 ); xmm_x_Q3_x2x0 = _mm_blend_epi16( xmm_x_Q3_x2x0, xmm_x_Q3_x3x1, 0xCC ); _mm_storeu_si128( (__m128i *)(&(x_sc_Q10[ i ])), xmm_x_Q3_x2x0 ); } for( ; i < psEncC->subfr_length; i++ ) { x_sc_Q10[ i ] = silk_SMULWW( x_Q3[ i ], inv_gain_Q23 ); } /* Save inverse gain */ NSQ->prev_gain_Q16 = Gains_Q16[ subfr ]; /* After rewhitening the LTP state is un-scaled, so scale with inv_gain_Q16 */ if( NSQ->rewhite_flag ) { if( subfr == 0 ) { /* Do LTP downscaling */ inv_gain_Q31 = silk_LSHIFT( silk_SMULWB( inv_gain_Q31, LTP_scale_Q14 ), 2 ); } for( i = NSQ->sLTP_buf_idx - lag - LTP_ORDER / 2; i < NSQ->sLTP_buf_idx; i++ ) { silk_assert( i < MAX_FRAME_LENGTH ); sLTP_Q15[ i ] = silk_SMULWB( inv_gain_Q31, sLTP[ i ] ); } } /* Adjust for changing gain */ if( gain_adj_Q16 != (opus_int32)1 << 16 ) { /* Scale long-term shaping state */ { __m128i xmm_gain_adj_Q16, xmm_sLTP_shp_Q14_x2x0, xmm_sLTP_shp_Q14_x3x1; /* prepare gain_adj_Q16 in packed 4 32-bits */ xmm_gain_adj_Q16 = _mm_set1_epi32( gain_adj_Q16 ); for( i = NSQ->sLTP_shp_buf_idx - psEncC->ltp_mem_length; i < NSQ->sLTP_shp_buf_idx - 3; i += 4 ) { xmm_sLTP_shp_Q14_x2x0 = _mm_loadu_si128( (__m128i *)(&(NSQ->sLTP_shp_Q14[ i ] ) ) ); /* equal shift right 4 bytes*/ xmm_sLTP_shp_Q14_x3x1 = _mm_shuffle_epi32( xmm_sLTP_shp_Q14_x2x0, _MM_SHUFFLE( 0, 3, 2, 1 ) ); xmm_sLTP_shp_Q14_x2x0 = _mm_mul_epi32( xmm_sLTP_shp_Q14_x2x0, xmm_gain_adj_Q16 ); xmm_sLTP_shp_Q14_x3x1 = _mm_mul_epi32( xmm_sLTP_shp_Q14_x3x1, xmm_gain_adj_Q16 ); xmm_sLTP_shp_Q14_x2x0 = _mm_srli_epi64( xmm_sLTP_shp_Q14_x2x0, 16 ); xmm_sLTP_shp_Q14_x3x1 = _mm_slli_epi64( xmm_sLTP_shp_Q14_x3x1, 16 ); xmm_sLTP_shp_Q14_x2x0 = _mm_blend_epi16( xmm_sLTP_shp_Q14_x2x0, xmm_sLTP_shp_Q14_x3x1, 0xCC ); _mm_storeu_si128( (__m128i *)(&(NSQ->sLTP_shp_Q14[ i ] ) ), xmm_sLTP_shp_Q14_x2x0 ); } for( ; i < NSQ->sLTP_shp_buf_idx; i++ ) { NSQ->sLTP_shp_Q14[ i ] = silk_SMULWW( gain_adj_Q16, NSQ->sLTP_shp_Q14[ i ] ); } /* Scale long-term prediction state */ if( signal_type == TYPE_VOICED && NSQ->rewhite_flag == 0 ) { for( i = NSQ->sLTP_buf_idx - lag - LTP_ORDER / 2; i < NSQ->sLTP_buf_idx - decisionDelay; i++ ) { sLTP_Q15[ i ] = silk_SMULWW( gain_adj_Q16, sLTP_Q15[ i ] ); } } for( k = 0; k < nStatesDelayedDecision; k++ ) { psDD = &psDelDec[ k ]; /* Scale scalar states */ psDD->LF_AR_Q14 = silk_SMULWW( gain_adj_Q16, psDD->LF_AR_Q14 ); /* Scale short-term prediction and shaping states */ for( i = 0; i < NSQ_LPC_BUF_LENGTH; i++ ) { psDD->sLPC_Q14[ i ] = silk_SMULWW( gain_adj_Q16, psDD->sLPC_Q14[ i ] ); } for( i = 0; i < MAX_SHAPE_LPC_ORDER; i++ ) { psDD->sAR2_Q14[ i ] = silk_SMULWW( gain_adj_Q16, psDD->sAR2_Q14[ i ] ); } for( i = 0; i < DECISION_DELAY; i++ ) { psDD->Pred_Q15[ i ] = silk_SMULWW( gain_adj_Q16, psDD->Pred_Q15[ i ] ); psDD->Shape_Q14[ i ] = silk_SMULWW( gain_adj_Q16, psDD->Shape_Q14[ i ] ); } } } } } ================================================ FILE: deps/pjsip/third_party/opus/silk/x86/NSQ_sse.c ================================================ /* Copyright (c) 2014, Cisco Systems, INC Written by XiangMingZhu WeiZhou MinPeng YanWang Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "main.h" #include "celt/x86/x86cpu.h" #include "stack_alloc.h" static OPUS_INLINE void silk_nsq_scale_states_sse4_1( const silk_encoder_state *psEncC, /* I Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ const opus_int32 x_Q3[], /* I input in Q3 */ opus_int32 x_sc_Q10[], /* O input scaled with 1/Gain */ const opus_int16 sLTP[], /* I re-whitened LTP state in Q0 */ opus_int32 sLTP_Q15[], /* O LTP state matching scaled input */ opus_int subfr, /* I subframe number */ const opus_int LTP_scale_Q14, /* I */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lag */ const opus_int signal_type /* I Signal type */ ); static OPUS_INLINE void silk_noise_shape_quantizer_10_16_sse4_1( silk_nsq_state *NSQ, /* I/O NSQ state */ opus_int signalType, /* I Signal type */ const opus_int32 x_sc_Q10[], /* I */ opus_int8 pulses[], /* O */ opus_int16 xq[], /* O */ opus_int32 sLTP_Q15[], /* I/O LTP state */ const opus_int16 a_Q12[], /* I Short term prediction coefs */ const opus_int16 b_Q14[], /* I Long term prediction coefs */ const opus_int16 AR_shp_Q13[], /* I Noise shaping AR coefs */ opus_int lag, /* I Pitch lag */ opus_int32 HarmShapeFIRPacked_Q14, /* I */ opus_int Tilt_Q14, /* I Spectral tilt */ opus_int32 LF_shp_Q14, /* I */ opus_int32 Gain_Q16, /* I */ opus_int offset_Q10, /* I */ opus_int length, /* I Input length */ opus_int32 table[][4] /* I */ ); void silk_NSQ_sse4_1( const silk_encoder_state *psEncC, /* I/O Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ SideInfoIndices *psIndices, /* I/O Quantization Indices */ const opus_int32 x_Q3[], /* I Prefiltered input signal */ opus_int8 pulses[], /* O Quantized pulse signal */ const opus_int16 PredCoef_Q12[ 2 * MAX_LPC_ORDER ], /* I Short term prediction coefs */ const opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ], /* I Long term prediction coefs */ const opus_int16 AR2_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ], /* I Noise shaping coefs */ const opus_int HarmShapeGain_Q14[ MAX_NB_SUBFR ], /* I Long term shaping coefs */ const opus_int Tilt_Q14[ MAX_NB_SUBFR ], /* I Spectral tilt */ const opus_int32 LF_shp_Q14[ MAX_NB_SUBFR ], /* I Low frequency shaping coefs */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I Quantization step sizes */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lags */ const opus_int Lambda_Q10, /* I Rate/distortion tradeoff */ const opus_int LTP_scale_Q14 /* I LTP state scaling */ ) { opus_int k, lag, start_idx, LSF_interpolation_flag; const opus_int16 *A_Q12, *B_Q14, *AR_shp_Q13; opus_int16 *pxq; VARDECL( opus_int32, sLTP_Q15 ); VARDECL( opus_int16, sLTP ); opus_int32 HarmShapeFIRPacked_Q14; opus_int offset_Q10; VARDECL( opus_int32, x_sc_Q10 ); opus_int32 table[ 64 ][ 4 ]; opus_int32 tmp1; opus_int32 q1_Q10, q2_Q10, rd1_Q20, rd2_Q20; SAVE_STACK; NSQ->rand_seed = psIndices->Seed; /* Set unvoiced lag to the previous one, overwrite later for voiced */ lag = NSQ->lagPrev; silk_assert( NSQ->prev_gain_Q16 != 0 ); offset_Q10 = silk_Quantization_Offsets_Q10[ psIndices->signalType >> 1 ][ psIndices->quantOffsetType ]; /* 0 */ q1_Q10 = offset_Q10; q2_Q10 = offset_Q10 + ( 1024 - QUANT_LEVEL_ADJUST_Q10 ); rd1_Q20 = q1_Q10 * Lambda_Q10; rd2_Q20 = q2_Q10 * Lambda_Q10; table[ 32 ][ 0 ] = q1_Q10; table[ 32 ][ 1 ] = q2_Q10; table[ 32 ][ 2 ] = 2 * (q1_Q10 - q2_Q10); table[ 32 ][ 3 ] = (rd1_Q20 - rd2_Q20) + (q1_Q10 * q1_Q10 - q2_Q10 * q2_Q10); /* -1 */ q1_Q10 = offset_Q10 - ( 1024 - QUANT_LEVEL_ADJUST_Q10 ); q2_Q10 = offset_Q10; rd1_Q20 = - q1_Q10 * Lambda_Q10; rd2_Q20 = q2_Q10 * Lambda_Q10; table[ 31 ][ 0 ] = q1_Q10; table[ 31 ][ 1 ] = q2_Q10; table[ 31 ][ 2 ] = 2 * (q1_Q10 - q2_Q10); table[ 31 ][ 3 ] = (rd1_Q20 - rd2_Q20) + (q1_Q10 * q1_Q10 - q2_Q10 * q2_Q10); /* > 0 */ for (k = 1; k <= 31; k++) { tmp1 = offset_Q10 + silk_LSHIFT( k, 10 ); q1_Q10 = tmp1 - QUANT_LEVEL_ADJUST_Q10; q2_Q10 = tmp1 - QUANT_LEVEL_ADJUST_Q10 + 1024; rd1_Q20 = q1_Q10 * Lambda_Q10; rd2_Q20 = q2_Q10 * Lambda_Q10; table[ 32 + k ][ 0 ] = q1_Q10; table[ 32 + k ][ 1 ] = q2_Q10; table[ 32 + k ][ 2 ] = 2 * (q1_Q10 - q2_Q10); table[ 32 + k ][ 3 ] = (rd1_Q20 - rd2_Q20) + (q1_Q10 * q1_Q10 - q2_Q10 * q2_Q10); } /* < -1 */ for (k = -32; k <= -2; k++) { tmp1 = offset_Q10 + silk_LSHIFT( k, 10 ); q1_Q10 = tmp1 + QUANT_LEVEL_ADJUST_Q10; q2_Q10 = tmp1 + QUANT_LEVEL_ADJUST_Q10 + 1024; rd1_Q20 = - q1_Q10 * Lambda_Q10; rd2_Q20 = - q2_Q10 * Lambda_Q10; table[ 32 + k ][ 0 ] = q1_Q10; table[ 32 + k ][ 1 ] = q2_Q10; table[ 32 + k ][ 2 ] = 2 * (q1_Q10 - q2_Q10); table[ 32 + k ][ 3 ] = (rd1_Q20 - rd2_Q20) + (q1_Q10 * q1_Q10 - q2_Q10 * q2_Q10); } if( psIndices->NLSFInterpCoef_Q2 == 4 ) { LSF_interpolation_flag = 0; } else { LSF_interpolation_flag = 1; } ALLOC( sLTP_Q15, psEncC->ltp_mem_length + psEncC->frame_length, opus_int32 ); ALLOC( sLTP, psEncC->ltp_mem_length + psEncC->frame_length, opus_int16 ); ALLOC( x_sc_Q10, psEncC->subfr_length, opus_int32 ); /* Set up pointers to start of sub frame */ NSQ->sLTP_shp_buf_idx = psEncC->ltp_mem_length; NSQ->sLTP_buf_idx = psEncC->ltp_mem_length; pxq = &NSQ->xq[ psEncC->ltp_mem_length ]; for( k = 0; k < psEncC->nb_subfr; k++ ) { A_Q12 = &PredCoef_Q12[ (( k >> 1 ) | ( 1 - LSF_interpolation_flag )) * MAX_LPC_ORDER ]; B_Q14 = <PCoef_Q14[ k * LTP_ORDER ]; AR_shp_Q13 = &AR2_Q13[ k * MAX_SHAPE_LPC_ORDER ]; /* Noise shape parameters */ silk_assert( HarmShapeGain_Q14[ k ] >= 0 ); HarmShapeFIRPacked_Q14 = silk_RSHIFT( HarmShapeGain_Q14[ k ], 2 ); HarmShapeFIRPacked_Q14 |= silk_LSHIFT( (opus_int32)silk_RSHIFT( HarmShapeGain_Q14[ k ], 1 ), 16 ); NSQ->rewhite_flag = 0; if( psIndices->signalType == TYPE_VOICED ) { /* Voiced */ lag = pitchL[ k ]; /* Re-whitening */ if( ( k & ( 3 - silk_LSHIFT( LSF_interpolation_flag, 1 ) ) ) == 0 ) { /* Rewhiten with new A coefs */ start_idx = psEncC->ltp_mem_length - lag - psEncC->predictLPCOrder - LTP_ORDER / 2; silk_assert( start_idx > 0 ); silk_LPC_analysis_filter( &sLTP[ start_idx ], &NSQ->xq[ start_idx + k * psEncC->subfr_length ], A_Q12, psEncC->ltp_mem_length - start_idx, psEncC->predictLPCOrder, psEncC->arch ); NSQ->rewhite_flag = 1; NSQ->sLTP_buf_idx = psEncC->ltp_mem_length; } } silk_nsq_scale_states_sse4_1( psEncC, NSQ, x_Q3, x_sc_Q10, sLTP, sLTP_Q15, k, LTP_scale_Q14, Gains_Q16, pitchL, psIndices->signalType ); if ( opus_likely( ( 10 == psEncC->shapingLPCOrder ) && ( 16 == psEncC->predictLPCOrder) ) ) { silk_noise_shape_quantizer_10_16_sse4_1( NSQ, psIndices->signalType, x_sc_Q10, pulses, pxq, sLTP_Q15, A_Q12, B_Q14, AR_shp_Q13, lag, HarmShapeFIRPacked_Q14, Tilt_Q14[ k ], LF_shp_Q14[ k ], Gains_Q16[ k ], offset_Q10, psEncC->subfr_length, &(table[32]) ); } else { silk_noise_shape_quantizer( NSQ, psIndices->signalType, x_sc_Q10, pulses, pxq, sLTP_Q15, A_Q12, B_Q14, AR_shp_Q13, lag, HarmShapeFIRPacked_Q14, Tilt_Q14[ k ], LF_shp_Q14[ k ], Gains_Q16[ k ], Lambda_Q10, offset_Q10, psEncC->subfr_length, psEncC->shapingLPCOrder, psEncC->predictLPCOrder ); } x_Q3 += psEncC->subfr_length; pulses += psEncC->subfr_length; pxq += psEncC->subfr_length; } /* Update lagPrev for next frame */ NSQ->lagPrev = pitchL[ psEncC->nb_subfr - 1 ]; /* Save quantized speech and noise shaping signals */ /* DEBUG_STORE_DATA( enc.pcm, &NSQ->xq[ psEncC->ltp_mem_length ], psEncC->frame_length * sizeof( opus_int16 ) ) */ silk_memmove( NSQ->xq, &NSQ->xq[ psEncC->frame_length ], psEncC->ltp_mem_length * sizeof( opus_int16 ) ); silk_memmove( NSQ->sLTP_shp_Q14, &NSQ->sLTP_shp_Q14[ psEncC->frame_length ], psEncC->ltp_mem_length * sizeof( opus_int32 ) ); RESTORE_STACK; } /***********************************/ /* silk_noise_shape_quantizer_10_16 */ /***********************************/ static OPUS_INLINE void silk_noise_shape_quantizer_10_16_sse4_1( silk_nsq_state *NSQ, /* I/O NSQ state */ opus_int signalType, /* I Signal type */ const opus_int32 x_sc_Q10[], /* I */ opus_int8 pulses[], /* O */ opus_int16 xq[], /* O */ opus_int32 sLTP_Q15[], /* I/O LTP state */ const opus_int16 a_Q12[], /* I Short term prediction coefs */ const opus_int16 b_Q14[], /* I Long term prediction coefs */ const opus_int16 AR_shp_Q13[], /* I Noise shaping AR coefs */ opus_int lag, /* I Pitch lag */ opus_int32 HarmShapeFIRPacked_Q14, /* I */ opus_int Tilt_Q14, /* I Spectral tilt */ opus_int32 LF_shp_Q14, /* I */ opus_int32 Gain_Q16, /* I */ opus_int offset_Q10, /* I */ opus_int length, /* I Input length */ opus_int32 table[][4] /* I */ ) { opus_int i; opus_int32 LTP_pred_Q13, LPC_pred_Q10, n_AR_Q12, n_LTP_Q13; opus_int32 n_LF_Q12, r_Q10, q1_Q0, q1_Q10, q2_Q10; opus_int32 exc_Q14, LPC_exc_Q14, xq_Q14, Gain_Q10; opus_int32 tmp1, tmp2, sLF_AR_shp_Q14; opus_int32 *psLPC_Q14, *shp_lag_ptr, *pred_lag_ptr; __m128i xmm_tempa, xmm_tempb; __m128i xmm_one; __m128i psLPC_Q14_hi_01234567, psLPC_Q14_hi_89ABCDEF; __m128i psLPC_Q14_lo_01234567, psLPC_Q14_lo_89ABCDEF; __m128i a_Q12_01234567, a_Q12_89ABCDEF; __m128i sAR2_Q14_hi_76543210, sAR2_Q14_lo_76543210; __m128i AR_shp_Q13_76543210; shp_lag_ptr = &NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - lag + HARM_SHAPE_FIR_TAPS / 2 ]; pred_lag_ptr = &sLTP_Q15[ NSQ->sLTP_buf_idx - lag + LTP_ORDER / 2 ]; Gain_Q10 = silk_RSHIFT( Gain_Q16, 6 ); /* Set up short term AR state */ psLPC_Q14 = &NSQ->sLPC_Q14[ NSQ_LPC_BUF_LENGTH - 1 ]; sLF_AR_shp_Q14 = NSQ->sLF_AR_shp_Q14; xq_Q14 = psLPC_Q14[ 0 ]; LTP_pred_Q13 = 0; /* load a_Q12 */ xmm_one = _mm_set_epi8( 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14 ); /* load a_Q12[0] - a_Q12[7] */ a_Q12_01234567 = _mm_loadu_si128( (__m128i *)(&a_Q12[ 0 ] ) ); /* load a_Q12[ 8 ] - a_Q12[ 15 ] */ a_Q12_89ABCDEF = _mm_loadu_si128( (__m128i *)(&a_Q12[ 8 ] ) ); a_Q12_01234567 = _mm_shuffle_epi8( a_Q12_01234567, xmm_one ); a_Q12_89ABCDEF = _mm_shuffle_epi8( a_Q12_89ABCDEF, xmm_one ); /* load AR_shp_Q13 */ AR_shp_Q13_76543210 = _mm_loadu_si128( (__m128i *)(&AR_shp_Q13[0] ) ); /* load psLPC_Q14 */ xmm_one = _mm_set_epi8(15, 14, 11, 10, 7, 6, 3, 2, 13, 12, 9, 8, 5, 4, 1, 0 ); xmm_tempa = _mm_loadu_si128( (__m128i *)(&psLPC_Q14[-16]) ); xmm_tempb = _mm_loadu_si128( (__m128i *)(&psLPC_Q14[-12]) ); xmm_tempa = _mm_shuffle_epi8( xmm_tempa, xmm_one ); xmm_tempb = _mm_shuffle_epi8( xmm_tempb, xmm_one ); psLPC_Q14_hi_89ABCDEF = _mm_unpackhi_epi64( xmm_tempa, xmm_tempb ); psLPC_Q14_lo_89ABCDEF = _mm_unpacklo_epi64( xmm_tempa, xmm_tempb ); xmm_tempa = _mm_loadu_si128( (__m128i *)(&psLPC_Q14[ -8 ]) ); xmm_tempb = _mm_loadu_si128( (__m128i *)(&psLPC_Q14[ -4 ]) ); xmm_tempa = _mm_shuffle_epi8( xmm_tempa, xmm_one ); xmm_tempb = _mm_shuffle_epi8( xmm_tempb, xmm_one ); psLPC_Q14_hi_01234567 = _mm_unpackhi_epi64( xmm_tempa, xmm_tempb ); psLPC_Q14_lo_01234567 = _mm_unpacklo_epi64( xmm_tempa, xmm_tempb ); /* load sAR2_Q14 */ xmm_tempa = _mm_loadu_si128( (__m128i *)(&(NSQ->sAR2_Q14[ 0 ]) ) ); xmm_tempb = _mm_loadu_si128( (__m128i *)(&(NSQ->sAR2_Q14[ 4 ]) ) ); xmm_tempa = _mm_shuffle_epi8( xmm_tempa, xmm_one ); xmm_tempb = _mm_shuffle_epi8( xmm_tempb, xmm_one ); sAR2_Q14_hi_76543210 = _mm_unpackhi_epi64( xmm_tempa, xmm_tempb ); sAR2_Q14_lo_76543210 = _mm_unpacklo_epi64( xmm_tempa, xmm_tempb ); /* prepare 1 in 8 * 16bit */ xmm_one = _mm_set1_epi16(1); for( i = 0; i < length; i++ ) { /* Short-term prediction */ __m128i xmm_hi_07, xmm_hi_8F, xmm_lo_07, xmm_lo_8F; /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ LPC_pred_Q10 = 8; /* silk_RSHIFT( predictLPCOrder, 1 ); */ /* shift psLPC_Q14 */ psLPC_Q14_hi_89ABCDEF = _mm_alignr_epi8( psLPC_Q14_hi_01234567, psLPC_Q14_hi_89ABCDEF, 2 ); psLPC_Q14_lo_89ABCDEF = _mm_alignr_epi8( psLPC_Q14_lo_01234567, psLPC_Q14_lo_89ABCDEF, 2 ); psLPC_Q14_hi_01234567 = _mm_srli_si128( psLPC_Q14_hi_01234567, 2 ); psLPC_Q14_lo_01234567 = _mm_srli_si128( psLPC_Q14_lo_01234567, 2 ); psLPC_Q14_hi_01234567 = _mm_insert_epi16( psLPC_Q14_hi_01234567, (xq_Q14 >> 16), 7 ); psLPC_Q14_lo_01234567 = _mm_insert_epi16( psLPC_Q14_lo_01234567, (xq_Q14), 7 ); /* high part, use pmaddwd, results in 4 32-bit */ xmm_hi_07 = _mm_madd_epi16( psLPC_Q14_hi_01234567, a_Q12_01234567 ); xmm_hi_8F = _mm_madd_epi16( psLPC_Q14_hi_89ABCDEF, a_Q12_89ABCDEF ); /* low part, use pmulhw, results in 8 16-bit, note we need simulate unsigned * signed, _mm_srai_epi16(psLPC_Q14_lo_01234567, 15) */ xmm_tempa = _mm_cmpgt_epi16( _mm_setzero_si128(), psLPC_Q14_lo_01234567 ); xmm_tempb = _mm_cmpgt_epi16( _mm_setzero_si128(), psLPC_Q14_lo_89ABCDEF ); xmm_tempa = _mm_and_si128( xmm_tempa, a_Q12_01234567 ); xmm_tempb = _mm_and_si128( xmm_tempb, a_Q12_89ABCDEF ); xmm_lo_07 = _mm_mulhi_epi16( psLPC_Q14_lo_01234567, a_Q12_01234567 ); xmm_lo_8F = _mm_mulhi_epi16( psLPC_Q14_lo_89ABCDEF, a_Q12_89ABCDEF ); xmm_lo_07 = _mm_add_epi16( xmm_lo_07, xmm_tempa ); xmm_lo_8F = _mm_add_epi16( xmm_lo_8F, xmm_tempb ); xmm_lo_07 = _mm_madd_epi16( xmm_lo_07, xmm_one ); xmm_lo_8F = _mm_madd_epi16( xmm_lo_8F, xmm_one ); /* accumulate */ xmm_hi_07 = _mm_add_epi32( xmm_hi_07, xmm_hi_8F ); xmm_lo_07 = _mm_add_epi32( xmm_lo_07, xmm_lo_8F ); xmm_hi_07 = _mm_add_epi32( xmm_hi_07, xmm_lo_07 ); xmm_hi_07 = _mm_add_epi32( xmm_hi_07, _mm_unpackhi_epi64(xmm_hi_07, xmm_hi_07 ) ); xmm_hi_07 = _mm_add_epi32( xmm_hi_07, _mm_shufflelo_epi16(xmm_hi_07, 0x0E ) ); LPC_pred_Q10 += _mm_cvtsi128_si32( xmm_hi_07 ); /* Long-term prediction */ if ( opus_likely( signalType == TYPE_VOICED ) ) { /* Unrolled loop */ /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */ LTP_pred_Q13 = 2; { __m128i b_Q14_3210, b_Q14_0123, pred_lag_ptr_0123; b_Q14_3210 = OP_CVTEPI16_EPI32_M64( b_Q14 ); b_Q14_0123 = _mm_shuffle_epi32( b_Q14_3210, 0x1B ); /* loaded: [0] [-1] [-2] [-3] */ pred_lag_ptr_0123 = _mm_loadu_si128( (__m128i *)(&pred_lag_ptr[ -3 ] ) ); /* shuffle to [-3] [-2] [-1] [0] and to new xmm */ xmm_tempa = _mm_shuffle_epi32( pred_lag_ptr_0123, 0x1B ); /*64-bit multiply, a[2] * b[-2], a[0] * b[0] */ xmm_tempa = _mm_mul_epi32( xmm_tempa, b_Q14_3210 ); /* right shift 2 bytes (16 bits), zero extended */ xmm_tempa = _mm_srli_si128( xmm_tempa, 2 ); /* a[1] * b[-1], a[3] * b[-3] */ pred_lag_ptr_0123 = _mm_mul_epi32( pred_lag_ptr_0123, b_Q14_0123 ); pred_lag_ptr_0123 = _mm_srli_si128( pred_lag_ptr_0123, 2 ); pred_lag_ptr_0123 = _mm_add_epi32( pred_lag_ptr_0123, xmm_tempa ); /* equal shift right 8 bytes*/ xmm_tempa = _mm_shuffle_epi32( pred_lag_ptr_0123, _MM_SHUFFLE( 0, 0, 3, 2 ) ); xmm_tempa = _mm_add_epi32( xmm_tempa, pred_lag_ptr_0123 ); LTP_pred_Q13 += _mm_cvtsi128_si32( xmm_tempa ); LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[ -4 ], b_Q14[ 4 ] ); pred_lag_ptr++; } } /* Noise shape feedback */ NSQ->sAR2_Q14[ 9 ] = NSQ->sAR2_Q14[ 8 ]; NSQ->sAR2_Q14[ 8 ] = _mm_cvtsi128_si32( _mm_srli_si128(_mm_unpackhi_epi16( sAR2_Q14_lo_76543210, sAR2_Q14_hi_76543210 ), 12 ) ); sAR2_Q14_hi_76543210 = _mm_slli_si128( sAR2_Q14_hi_76543210, 2 ); sAR2_Q14_lo_76543210 = _mm_slli_si128( sAR2_Q14_lo_76543210, 2 ); sAR2_Q14_hi_76543210 = _mm_insert_epi16( sAR2_Q14_hi_76543210, (xq_Q14 >> 16), 0 ); sAR2_Q14_lo_76543210 = _mm_insert_epi16( sAR2_Q14_lo_76543210, (xq_Q14), 0 ); /* high part, use pmaddwd, results in 4 32-bit */ xmm_hi_07 = _mm_madd_epi16( sAR2_Q14_hi_76543210, AR_shp_Q13_76543210 ); /* low part, use pmulhw, results in 8 16-bit, note we need simulate unsigned * signed,_mm_srai_epi16(sAR2_Q14_lo_76543210, 15) */ xmm_tempa = _mm_cmpgt_epi16( _mm_setzero_si128(), sAR2_Q14_lo_76543210 ); xmm_tempa = _mm_and_si128( xmm_tempa, AR_shp_Q13_76543210 ); xmm_lo_07 = _mm_mulhi_epi16( sAR2_Q14_lo_76543210, AR_shp_Q13_76543210 ); xmm_lo_07 = _mm_add_epi16( xmm_lo_07, xmm_tempa ); xmm_lo_07 = _mm_madd_epi16( xmm_lo_07, xmm_one ); /* accumulate */ xmm_hi_07 = _mm_add_epi32( xmm_hi_07, xmm_lo_07 ); xmm_hi_07 = _mm_add_epi32( xmm_hi_07, _mm_unpackhi_epi64(xmm_hi_07, xmm_hi_07 ) ); xmm_hi_07 = _mm_add_epi32( xmm_hi_07, _mm_shufflelo_epi16(xmm_hi_07, 0x0E ) ); n_AR_Q12 = 5 + _mm_cvtsi128_si32( xmm_hi_07 ); n_AR_Q12 = silk_SMLAWB( n_AR_Q12, NSQ->sAR2_Q14[ 8 ], AR_shp_Q13[ 8 ] ); n_AR_Q12 = silk_SMLAWB( n_AR_Q12, NSQ->sAR2_Q14[ 9 ], AR_shp_Q13[ 9 ] ); n_AR_Q12 = silk_LSHIFT32( n_AR_Q12, 1 ); /* Q11 -> Q12 */ n_AR_Q12 = silk_SMLAWB( n_AR_Q12, sLF_AR_shp_Q14, Tilt_Q14 ); n_LF_Q12 = silk_SMULWB( NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - 1 ], LF_shp_Q14 ); n_LF_Q12 = silk_SMLAWT( n_LF_Q12, sLF_AR_shp_Q14, LF_shp_Q14 ); silk_assert( lag > 0 || signalType != TYPE_VOICED ); /* Combine prediction and noise shaping signals */ tmp1 = silk_SUB32( silk_LSHIFT32( LPC_pred_Q10, 2 ), n_AR_Q12 ); /* Q12 */ tmp1 = silk_SUB32( tmp1, n_LF_Q12 ); /* Q12 */ if( lag > 0 ) { /* Symmetric, packed FIR coefficients */ n_LTP_Q13 = silk_SMULWB( silk_ADD32( shp_lag_ptr[ 0 ], shp_lag_ptr[ -2 ] ), HarmShapeFIRPacked_Q14 ); n_LTP_Q13 = silk_SMLAWT( n_LTP_Q13, shp_lag_ptr[ -1 ], HarmShapeFIRPacked_Q14 ); n_LTP_Q13 = silk_LSHIFT( n_LTP_Q13, 1 ); shp_lag_ptr++; tmp2 = silk_SUB32( LTP_pred_Q13, n_LTP_Q13 ); /* Q13 */ tmp1 = silk_ADD_LSHIFT32( tmp2, tmp1, 1 ); /* Q13 */ tmp1 = silk_RSHIFT_ROUND( tmp1, 3 ); /* Q10 */ } else { tmp1 = silk_RSHIFT_ROUND( tmp1, 2 ); /* Q10 */ } r_Q10 = silk_SUB32( x_sc_Q10[ i ], tmp1 ); /* residual error Q10 */ /* Generate dither */ NSQ->rand_seed = silk_RAND( NSQ->rand_seed ); /* Flip sign depending on dither */ tmp2 = -r_Q10; if ( NSQ->rand_seed < 0 ) r_Q10 = tmp2; r_Q10 = silk_LIMIT_32( r_Q10, -(31 << 10), 30 << 10 ); /* Find two quantization level candidates and measure their rate-distortion */ q1_Q10 = silk_SUB32( r_Q10, offset_Q10 ); q1_Q0 = silk_RSHIFT( q1_Q10, 10 ); q1_Q10 = table[q1_Q0][0]; q2_Q10 = table[q1_Q0][1]; if (r_Q10 * table[q1_Q0][2] - table[q1_Q0][3] < 0) { q1_Q10 = q2_Q10; } pulses[ i ] = (opus_int8)silk_RSHIFT_ROUND( q1_Q10, 10 ); /* Excitation */ exc_Q14 = silk_LSHIFT( q1_Q10, 4 ); tmp2 = -exc_Q14; if ( NSQ->rand_seed < 0 ) exc_Q14 = tmp2; /* Add predictions */ LPC_exc_Q14 = silk_ADD_LSHIFT32( exc_Q14, LTP_pred_Q13, 1 ); xq_Q14 = silk_ADD_LSHIFT32( LPC_exc_Q14, LPC_pred_Q10, 4 ); /* Update states */ psLPC_Q14++; *psLPC_Q14 = xq_Q14; sLF_AR_shp_Q14 = silk_SUB_LSHIFT32( xq_Q14, n_AR_Q12, 2 ); NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx ] = silk_SUB_LSHIFT32( sLF_AR_shp_Q14, n_LF_Q12, 2 ); sLTP_Q15[ NSQ->sLTP_buf_idx ] = silk_LSHIFT( LPC_exc_Q14, 1 ); NSQ->sLTP_shp_buf_idx++; NSQ->sLTP_buf_idx++; /* Make dither dependent on quantized signal */ NSQ->rand_seed = silk_ADD32_ovflw( NSQ->rand_seed, pulses[ i ] ); } NSQ->sLF_AR_shp_Q14 = sLF_AR_shp_Q14; /* Scale XQ back to normal level before saving */ psLPC_Q14 = &NSQ->sLPC_Q14[ NSQ_LPC_BUF_LENGTH ]; /* write back sAR2_Q14 */ xmm_tempa = _mm_unpackhi_epi16( sAR2_Q14_lo_76543210, sAR2_Q14_hi_76543210 ); xmm_tempb = _mm_unpacklo_epi16( sAR2_Q14_lo_76543210, sAR2_Q14_hi_76543210 ); _mm_storeu_si128( (__m128i *)(&NSQ->sAR2_Q14[ 4 ]), xmm_tempa ); _mm_storeu_si128( (__m128i *)(&NSQ->sAR2_Q14[ 0 ]), xmm_tempb ); /* xq[ i ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_SMULWW( psLPC_Q14[ i ], Gain_Q10 ), 8 ) ); */ { __m128i xmm_Gain_Q10; __m128i xmm_xq_Q14_3210, xmm_xq_Q14_x3x1, xmm_xq_Q14_7654, xmm_xq_Q14_x7x5; /* prepare (1 << 7) in packed 4 32-bits */ xmm_tempa = _mm_set1_epi32( (1 << 7) ); /* prepare Gain_Q10 in packed 4 32-bits */ xmm_Gain_Q10 = _mm_set1_epi32( Gain_Q10 ); /* process xq */ for (i = 0; i < length - 7; i += 8) { xmm_xq_Q14_3210 = _mm_loadu_si128( (__m128i *)(&(psLPC_Q14[ i + 0 ] ) ) ); xmm_xq_Q14_7654 = _mm_loadu_si128( (__m128i *)(&(psLPC_Q14[ i + 4 ] ) ) ); /* equal shift right 4 bytes*/ xmm_xq_Q14_x3x1 = _mm_shuffle_epi32( xmm_xq_Q14_3210, _MM_SHUFFLE( 0, 3, 2, 1 ) ); /* equal shift right 4 bytes*/ xmm_xq_Q14_x7x5 = _mm_shuffle_epi32( xmm_xq_Q14_7654, _MM_SHUFFLE( 0, 3, 2, 1 ) ); xmm_xq_Q14_3210 = _mm_mul_epi32( xmm_xq_Q14_3210, xmm_Gain_Q10 ); xmm_xq_Q14_x3x1 = _mm_mul_epi32( xmm_xq_Q14_x3x1, xmm_Gain_Q10 ); xmm_xq_Q14_7654 = _mm_mul_epi32( xmm_xq_Q14_7654, xmm_Gain_Q10 ); xmm_xq_Q14_x7x5 = _mm_mul_epi32( xmm_xq_Q14_x7x5, xmm_Gain_Q10 ); xmm_xq_Q14_3210 = _mm_srli_epi64( xmm_xq_Q14_3210, 16 ); xmm_xq_Q14_x3x1 = _mm_slli_epi64( xmm_xq_Q14_x3x1, 16 ); xmm_xq_Q14_7654 = _mm_srli_epi64( xmm_xq_Q14_7654, 16 ); xmm_xq_Q14_x7x5 = _mm_slli_epi64( xmm_xq_Q14_x7x5, 16 ); xmm_xq_Q14_3210 = _mm_blend_epi16( xmm_xq_Q14_3210, xmm_xq_Q14_x3x1, 0xCC ); xmm_xq_Q14_7654 = _mm_blend_epi16( xmm_xq_Q14_7654, xmm_xq_Q14_x7x5, 0xCC ); /* silk_RSHIFT_ROUND(xq, 8) */ xmm_xq_Q14_3210 = _mm_add_epi32( xmm_xq_Q14_3210, xmm_tempa ); xmm_xq_Q14_7654 = _mm_add_epi32( xmm_xq_Q14_7654, xmm_tempa ); xmm_xq_Q14_3210 = _mm_srai_epi32( xmm_xq_Q14_3210, 8 ); xmm_xq_Q14_7654 = _mm_srai_epi32( xmm_xq_Q14_7654, 8 ); /* silk_SAT16 */ xmm_xq_Q14_3210 = _mm_packs_epi32( xmm_xq_Q14_3210, xmm_xq_Q14_7654 ); /* save to xq */ _mm_storeu_si128( (__m128i *)(&xq[ i ] ), xmm_xq_Q14_3210 ); } } for ( ; i < length; i++) { xq[i] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_SMULWW( psLPC_Q14[ i ], Gain_Q10 ), 8 ) ); } /* Update LPC synth buffer */ silk_memcpy( NSQ->sLPC_Q14, &NSQ->sLPC_Q14[ length ], NSQ_LPC_BUF_LENGTH * sizeof( opus_int32 ) ); } static OPUS_INLINE void silk_nsq_scale_states_sse4_1( const silk_encoder_state *psEncC, /* I Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ const opus_int32 x_Q3[], /* I input in Q3 */ opus_int32 x_sc_Q10[], /* O input scaled with 1/Gain */ const opus_int16 sLTP[], /* I re-whitened LTP state in Q0 */ opus_int32 sLTP_Q15[], /* O LTP state matching scaled input */ opus_int subfr, /* I subframe number */ const opus_int LTP_scale_Q14, /* I */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lag */ const opus_int signal_type /* I Signal type */ ) { opus_int i, lag; opus_int32 gain_adj_Q16, inv_gain_Q31, inv_gain_Q23; __m128i xmm_inv_gain_Q23, xmm_x_Q3_x2x0, xmm_x_Q3_x3x1; lag = pitchL[ subfr ]; inv_gain_Q31 = silk_INVERSE32_varQ( silk_max( Gains_Q16[ subfr ], 1 ), 47 ); silk_assert( inv_gain_Q31 != 0 ); /* Calculate gain adjustment factor */ if( Gains_Q16[ subfr ] != NSQ->prev_gain_Q16 ) { gain_adj_Q16 = silk_DIV32_varQ( NSQ->prev_gain_Q16, Gains_Q16[ subfr ], 16 ); } else { gain_adj_Q16 = (opus_int32)1 << 16; } /* Scale input */ inv_gain_Q23 = silk_RSHIFT_ROUND( inv_gain_Q31, 8 ); /* prepare inv_gain_Q23 in packed 4 32-bits */ xmm_inv_gain_Q23 = _mm_set1_epi32(inv_gain_Q23); for( i = 0; i < psEncC->subfr_length - 3; i += 4 ) { xmm_x_Q3_x2x0 = _mm_loadu_si128( (__m128i *)(&(x_Q3[ i ] ) ) ); /* equal shift right 4 bytes*/ xmm_x_Q3_x3x1 = _mm_shuffle_epi32( xmm_x_Q3_x2x0, _MM_SHUFFLE( 0, 3, 2, 1 ) ); xmm_x_Q3_x2x0 = _mm_mul_epi32( xmm_x_Q3_x2x0, xmm_inv_gain_Q23 ); xmm_x_Q3_x3x1 = _mm_mul_epi32( xmm_x_Q3_x3x1, xmm_inv_gain_Q23 ); xmm_x_Q3_x2x0 = _mm_srli_epi64( xmm_x_Q3_x2x0, 16 ); xmm_x_Q3_x3x1 = _mm_slli_epi64( xmm_x_Q3_x3x1, 16 ); xmm_x_Q3_x2x0 = _mm_blend_epi16( xmm_x_Q3_x2x0, xmm_x_Q3_x3x1, 0xCC ); _mm_storeu_si128( (__m128i *)(&(x_sc_Q10[ i ] ) ), xmm_x_Q3_x2x0 ); } for( ; i < psEncC->subfr_length; i++ ) { x_sc_Q10[ i ] = silk_SMULWW( x_Q3[ i ], inv_gain_Q23 ); } /* Save inverse gain */ NSQ->prev_gain_Q16 = Gains_Q16[ subfr ]; /* After rewhitening the LTP state is un-scaled, so scale with inv_gain_Q16 */ if( NSQ->rewhite_flag ) { if( subfr == 0 ) { /* Do LTP downscaling */ inv_gain_Q31 = silk_LSHIFT( silk_SMULWB( inv_gain_Q31, LTP_scale_Q14 ), 2 ); } for( i = NSQ->sLTP_buf_idx - lag - LTP_ORDER / 2; i < NSQ->sLTP_buf_idx; i++ ) { silk_assert( i < MAX_FRAME_LENGTH ); sLTP_Q15[ i ] = silk_SMULWB( inv_gain_Q31, sLTP[ i ] ); } } /* Adjust for changing gain */ if( gain_adj_Q16 != (opus_int32)1 << 16 ) { /* Scale long-term shaping state */ __m128i xmm_gain_adj_Q16, xmm_sLTP_shp_Q14_x2x0, xmm_sLTP_shp_Q14_x3x1; /* prepare gain_adj_Q16 in packed 4 32-bits */ xmm_gain_adj_Q16 = _mm_set1_epi32(gain_adj_Q16); for( i = NSQ->sLTP_shp_buf_idx - psEncC->ltp_mem_length; i < NSQ->sLTP_shp_buf_idx - 3; i += 4 ) { xmm_sLTP_shp_Q14_x2x0 = _mm_loadu_si128( (__m128i *)(&(NSQ->sLTP_shp_Q14[ i ] ) ) ); /* equal shift right 4 bytes*/ xmm_sLTP_shp_Q14_x3x1 = _mm_shuffle_epi32( xmm_sLTP_shp_Q14_x2x0, _MM_SHUFFLE( 0, 3, 2, 1 ) ); xmm_sLTP_shp_Q14_x2x0 = _mm_mul_epi32( xmm_sLTP_shp_Q14_x2x0, xmm_gain_adj_Q16 ); xmm_sLTP_shp_Q14_x3x1 = _mm_mul_epi32( xmm_sLTP_shp_Q14_x3x1, xmm_gain_adj_Q16 ); xmm_sLTP_shp_Q14_x2x0 = _mm_srli_epi64( xmm_sLTP_shp_Q14_x2x0, 16 ); xmm_sLTP_shp_Q14_x3x1 = _mm_slli_epi64( xmm_sLTP_shp_Q14_x3x1, 16 ); xmm_sLTP_shp_Q14_x2x0 = _mm_blend_epi16( xmm_sLTP_shp_Q14_x2x0, xmm_sLTP_shp_Q14_x3x1, 0xCC ); _mm_storeu_si128( (__m128i *)(&(NSQ->sLTP_shp_Q14[ i ] ) ), xmm_sLTP_shp_Q14_x2x0 ); } for( ; i < NSQ->sLTP_shp_buf_idx; i++ ) { NSQ->sLTP_shp_Q14[ i ] = silk_SMULWW( gain_adj_Q16, NSQ->sLTP_shp_Q14[ i ] ); } /* Scale long-term prediction state */ if( signal_type == TYPE_VOICED && NSQ->rewhite_flag == 0 ) { for( i = NSQ->sLTP_buf_idx - lag - LTP_ORDER / 2; i < NSQ->sLTP_buf_idx; i++ ) { sLTP_Q15[ i ] = silk_SMULWW( gain_adj_Q16, sLTP_Q15[ i ] ); } } NSQ->sLF_AR_shp_Q14 = silk_SMULWW( gain_adj_Q16, NSQ->sLF_AR_shp_Q14 ); /* Scale short-term prediction and shaping states */ for( i = 0; i < NSQ_LPC_BUF_LENGTH; i++ ) { NSQ->sLPC_Q14[ i ] = silk_SMULWW( gain_adj_Q16, NSQ->sLPC_Q14[ i ] ); } for( i = 0; i < MAX_SHAPE_LPC_ORDER; i++ ) { NSQ->sAR2_Q14[ i ] = silk_SMULWW( gain_adj_Q16, NSQ->sAR2_Q14[ i ] ); } } } ================================================ FILE: deps/pjsip/third_party/opus/silk/x86/SigProc_FIX_sse.h ================================================ /* Copyright (c) 2014, Cisco Systems, INC Written by XiangMingZhu WeiZhou MinPeng YanWang Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SIGPROC_FIX_SSE_H #define SIGPROC_FIX_SSE_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #if defined(OPUS_X86_MAY_HAVE_SSE4_1) void silk_burg_modified_sse4_1( opus_int32 *res_nrg, /* O Residual energy */ opus_int *res_nrg_Q, /* O Residual energy Q value */ opus_int32 A_Q16[], /* O Prediction coefficients (length order) */ const opus_int16 x[], /* I Input signal, length: nb_subfr * ( D + subfr_length ) */ const opus_int32 minInvGain_Q30, /* I Inverse of max prediction gain */ const opus_int subfr_length, /* I Input signal subframe length (incl. D preceding samples) */ const opus_int nb_subfr, /* I Number of subframes stacked in x */ const opus_int D, /* I Order */ int arch /* I Run-time architecture */ ); #if defined(OPUS_X86_PRESUME_SSE4_1) #define silk_burg_modified(res_nrg, res_nrg_Q, A_Q16, x, minInvGain_Q30, subfr_length, nb_subfr, D, arch) \ ((void)(arch), silk_burg_modified_sse4_1(res_nrg, res_nrg_Q, A_Q16, x, minInvGain_Q30, subfr_length, nb_subfr, D, arch)) #else extern void (*const SILK_BURG_MODIFIED_IMPL[OPUS_ARCHMASK + 1])( opus_int32 *res_nrg, /* O Residual energy */ opus_int *res_nrg_Q, /* O Residual energy Q value */ opus_int32 A_Q16[], /* O Prediction coefficients (length order) */ const opus_int16 x[], /* I Input signal, length: nb_subfr * ( D + subfr_length ) */ const opus_int32 minInvGain_Q30, /* I Inverse of max prediction gain */ const opus_int subfr_length, /* I Input signal subframe length (incl. D preceding samples) */ const opus_int nb_subfr, /* I Number of subframes stacked in x */ const opus_int D, /* I Order */ int arch /* I Run-time architecture */); # define silk_burg_modified(res_nrg, res_nrg_Q, A_Q16, x, minInvGain_Q30, subfr_length, nb_subfr, D, arch) \ ((*SILK_BURG_MODIFIED_IMPL[(arch) & OPUS_ARCHMASK])(res_nrg, res_nrg_Q, A_Q16, x, minInvGain_Q30, subfr_length, nb_subfr, D, arch)) #endif opus_int64 silk_inner_prod16_aligned_64_sse4_1( const opus_int16 *inVec1, const opus_int16 *inVec2, const opus_int len ); #if defined(OPUS_X86_PRESUME_SSE4_1) #define silk_inner_prod16_aligned_64(inVec1, inVec2, len, arch) \ ((void)(arch),silk_inner_prod16_aligned_64_sse4_1(inVec1, inVec2, len)) #else extern opus_int64 (*const SILK_INNER_PROD16_ALIGNED_64_IMPL[OPUS_ARCHMASK + 1])( const opus_int16 *inVec1, const opus_int16 *inVec2, const opus_int len); # define silk_inner_prod16_aligned_64(inVec1, inVec2, len, arch) \ ((*SILK_INNER_PROD16_ALIGNED_64_IMPL[(arch) & OPUS_ARCHMASK])(inVec1, inVec2, len)) #endif #endif #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/x86/VAD_sse.c ================================================ /* Copyright (c) 2014, Cisco Systems, INC Written by XiangMingZhu WeiZhou MinPeng YanWang Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "main.h" #include "stack_alloc.h" /* Weighting factors for tilt measure */ static const opus_int32 tiltWeights[ VAD_N_BANDS ] = { 30000, 6000, -12000, -12000 }; /***************************************/ /* Get the speech activity level in Q8 */ /***************************************/ opus_int silk_VAD_GetSA_Q8_sse4_1( /* O Return value, 0 if success */ silk_encoder_state *psEncC, /* I/O Encoder state */ const opus_int16 pIn[] /* I PCM input */ ) { opus_int SA_Q15, pSNR_dB_Q7, input_tilt; opus_int decimated_framelength1, decimated_framelength2; opus_int decimated_framelength; opus_int dec_subframe_length, dec_subframe_offset, SNR_Q7, i, b, s; opus_int32 sumSquared, smooth_coef_Q16; opus_int16 HPstateTmp; VARDECL( opus_int16, X ); opus_int32 Xnrg[ VAD_N_BANDS ]; opus_int32 NrgToNoiseRatio_Q8[ VAD_N_BANDS ]; opus_int32 speech_nrg, x_tmp; opus_int X_offset[ VAD_N_BANDS ]; opus_int ret = 0; silk_VAD_state *psSilk_VAD = &psEncC->sVAD; SAVE_STACK; /* Safety checks */ silk_assert( VAD_N_BANDS == 4 ); silk_assert( MAX_FRAME_LENGTH >= psEncC->frame_length ); silk_assert( psEncC->frame_length <= 512 ); silk_assert( psEncC->frame_length == 8 * silk_RSHIFT( psEncC->frame_length, 3 ) ); /***********************/ /* Filter and Decimate */ /***********************/ decimated_framelength1 = silk_RSHIFT( psEncC->frame_length, 1 ); decimated_framelength2 = silk_RSHIFT( psEncC->frame_length, 2 ); decimated_framelength = silk_RSHIFT( psEncC->frame_length, 3 ); /* Decimate into 4 bands: 0 L 3L L 3L 5L - -- - -- -- 8 8 2 4 4 [0-1 kHz| temp. |1-2 kHz| 2-4 kHz | 4-8 kHz | They're arranged to allow the minimal ( frame_length / 4 ) extra scratch space during the downsampling process */ X_offset[ 0 ] = 0; X_offset[ 1 ] = decimated_framelength + decimated_framelength2; X_offset[ 2 ] = X_offset[ 1 ] + decimated_framelength; X_offset[ 3 ] = X_offset[ 2 ] + decimated_framelength2; ALLOC( X, X_offset[ 3 ] + decimated_framelength1, opus_int16 ); /* 0-8 kHz to 0-4 kHz and 4-8 kHz */ silk_ana_filt_bank_1( pIn, &psSilk_VAD->AnaState[ 0 ], X, &X[ X_offset[ 3 ] ], psEncC->frame_length ); /* 0-4 kHz to 0-2 kHz and 2-4 kHz */ silk_ana_filt_bank_1( X, &psSilk_VAD->AnaState1[ 0 ], X, &X[ X_offset[ 2 ] ], decimated_framelength1 ); /* 0-2 kHz to 0-1 kHz and 1-2 kHz */ silk_ana_filt_bank_1( X, &psSilk_VAD->AnaState2[ 0 ], X, &X[ X_offset[ 1 ] ], decimated_framelength2 ); /*********************************************/ /* HP filter on lowest band (differentiator) */ /*********************************************/ X[ decimated_framelength - 1 ] = silk_RSHIFT( X[ decimated_framelength - 1 ], 1 ); HPstateTmp = X[ decimated_framelength - 1 ]; for( i = decimated_framelength - 1; i > 0; i-- ) { X[ i - 1 ] = silk_RSHIFT( X[ i - 1 ], 1 ); X[ i ] -= X[ i - 1 ]; } X[ 0 ] -= psSilk_VAD->HPstate; psSilk_VAD->HPstate = HPstateTmp; /*************************************/ /* Calculate the energy in each band */ /*************************************/ for( b = 0; b < VAD_N_BANDS; b++ ) { /* Find the decimated framelength in the non-uniformly divided bands */ decimated_framelength = silk_RSHIFT( psEncC->frame_length, silk_min_int( VAD_N_BANDS - b, VAD_N_BANDS - 1 ) ); /* Split length into subframe lengths */ dec_subframe_length = silk_RSHIFT( decimated_framelength, VAD_INTERNAL_SUBFRAMES_LOG2 ); dec_subframe_offset = 0; /* Compute energy per sub-frame */ /* initialize with summed energy of last subframe */ Xnrg[ b ] = psSilk_VAD->XnrgSubfr[ b ]; for( s = 0; s < VAD_INTERNAL_SUBFRAMES; s++ ) { __m128i xmm_X, xmm_acc; sumSquared = 0; xmm_acc = _mm_setzero_si128(); for( i = 0; i < dec_subframe_length - 7; i += 8 ) { xmm_X = _mm_loadu_si128( (__m128i *)&(X[ X_offset[ b ] + i + dec_subframe_offset ] ) ); xmm_X = _mm_srai_epi16( xmm_X, 3 ); xmm_X = _mm_madd_epi16( xmm_X, xmm_X ); xmm_acc = _mm_add_epi32( xmm_acc, xmm_X ); } xmm_acc = _mm_add_epi32( xmm_acc, _mm_unpackhi_epi64( xmm_acc, xmm_acc ) ); xmm_acc = _mm_add_epi32( xmm_acc, _mm_shufflelo_epi16( xmm_acc, 0x0E ) ); sumSquared += _mm_cvtsi128_si32( xmm_acc ); for( ; i < dec_subframe_length; i++ ) { /* The energy will be less than dec_subframe_length * ( silk_int16_MIN / 8 ) ^ 2. */ /* Therefore we can accumulate with no risk of overflow (unless dec_subframe_length > 128) */ x_tmp = silk_RSHIFT( X[ X_offset[ b ] + i + dec_subframe_offset ], 3 ); sumSquared = silk_SMLABB( sumSquared, x_tmp, x_tmp ); /* Safety check */ silk_assert( sumSquared >= 0 ); } /* Add/saturate summed energy of current subframe */ if( s < VAD_INTERNAL_SUBFRAMES - 1 ) { Xnrg[ b ] = silk_ADD_POS_SAT32( Xnrg[ b ], sumSquared ); } else { /* Look-ahead subframe */ Xnrg[ b ] = silk_ADD_POS_SAT32( Xnrg[ b ], silk_RSHIFT( sumSquared, 1 ) ); } dec_subframe_offset += dec_subframe_length; } psSilk_VAD->XnrgSubfr[ b ] = sumSquared; } /********************/ /* Noise estimation */ /********************/ silk_VAD_GetNoiseLevels( &Xnrg[ 0 ], psSilk_VAD ); /***********************************************/ /* Signal-plus-noise to noise ratio estimation */ /***********************************************/ sumSquared = 0; input_tilt = 0; for( b = 0; b < VAD_N_BANDS; b++ ) { speech_nrg = Xnrg[ b ] - psSilk_VAD->NL[ b ]; if( speech_nrg > 0 ) { /* Divide, with sufficient resolution */ if( ( Xnrg[ b ] & 0xFF800000 ) == 0 ) { NrgToNoiseRatio_Q8[ b ] = silk_DIV32( silk_LSHIFT( Xnrg[ b ], 8 ), psSilk_VAD->NL[ b ] + 1 ); } else { NrgToNoiseRatio_Q8[ b ] = silk_DIV32( Xnrg[ b ], silk_RSHIFT( psSilk_VAD->NL[ b ], 8 ) + 1 ); } /* Convert to log domain */ SNR_Q7 = silk_lin2log( NrgToNoiseRatio_Q8[ b ] ) - 8 * 128; /* Sum-of-squares */ sumSquared = silk_SMLABB( sumSquared, SNR_Q7, SNR_Q7 ); /* Q14 */ /* Tilt measure */ if( speech_nrg < ( (opus_int32)1 << 20 ) ) { /* Scale down SNR value for small subband speech energies */ SNR_Q7 = silk_SMULWB( silk_LSHIFT( silk_SQRT_APPROX( speech_nrg ), 6 ), SNR_Q7 ); } input_tilt = silk_SMLAWB( input_tilt, tiltWeights[ b ], SNR_Q7 ); } else { NrgToNoiseRatio_Q8[ b ] = 256; } } /* Mean-of-squares */ sumSquared = silk_DIV32_16( sumSquared, VAD_N_BANDS ); /* Q14 */ /* Root-mean-square approximation, scale to dBs, and write to output pointer */ pSNR_dB_Q7 = (opus_int16)( 3 * silk_SQRT_APPROX( sumSquared ) ); /* Q7 */ /*********************************/ /* Speech Probability Estimation */ /*********************************/ SA_Q15 = silk_sigm_Q15( silk_SMULWB( VAD_SNR_FACTOR_Q16, pSNR_dB_Q7 ) - VAD_NEGATIVE_OFFSET_Q5 ); /**************************/ /* Frequency Tilt Measure */ /**************************/ psEncC->input_tilt_Q15 = silk_LSHIFT( silk_sigm_Q15( input_tilt ) - 16384, 1 ); /**************************************************/ /* Scale the sigmoid output based on power levels */ /**************************************************/ speech_nrg = 0; for( b = 0; b < VAD_N_BANDS; b++ ) { /* Accumulate signal-without-noise energies, higher frequency bands have more weight */ speech_nrg += ( b + 1 ) * silk_RSHIFT( Xnrg[ b ] - psSilk_VAD->NL[ b ], 4 ); } /* Power scaling */ if( speech_nrg <= 0 ) { SA_Q15 = silk_RSHIFT( SA_Q15, 1 ); } else if( speech_nrg < 32768 ) { if( psEncC->frame_length == 10 * psEncC->fs_kHz ) { speech_nrg = silk_LSHIFT_SAT32( speech_nrg, 16 ); } else { speech_nrg = silk_LSHIFT_SAT32( speech_nrg, 15 ); } /* square-root */ speech_nrg = silk_SQRT_APPROX( speech_nrg ); SA_Q15 = silk_SMULWB( 32768 + speech_nrg, SA_Q15 ); } /* Copy the resulting speech activity in Q8 */ psEncC->speech_activity_Q8 = silk_min_int( silk_RSHIFT( SA_Q15, 7 ), silk_uint8_MAX ); /***********************************/ /* Energy Level and SNR estimation */ /***********************************/ /* Smoothing coefficient */ smooth_coef_Q16 = silk_SMULWB( VAD_SNR_SMOOTH_COEF_Q18, silk_SMULWB( (opus_int32)SA_Q15, SA_Q15 ) ); if( psEncC->frame_length == 10 * psEncC->fs_kHz ) { smooth_coef_Q16 >>= 1; } for( b = 0; b < VAD_N_BANDS; b++ ) { /* compute smoothed energy-to-noise ratio per band */ psSilk_VAD->NrgRatioSmth_Q8[ b ] = silk_SMLAWB( psSilk_VAD->NrgRatioSmth_Q8[ b ], NrgToNoiseRatio_Q8[ b ] - psSilk_VAD->NrgRatioSmth_Q8[ b ], smooth_coef_Q16 ); /* signal to noise ratio in dB per band */ SNR_Q7 = 3 * ( silk_lin2log( psSilk_VAD->NrgRatioSmth_Q8[b] ) - 8 * 128 ); /* quality = sigmoid( 0.25 * ( SNR_dB - 16 ) ); */ psEncC->input_quality_bands_Q15[ b ] = silk_sigm_Q15( silk_RSHIFT( SNR_Q7 - 16 * 128, 4 ) ); } RESTORE_STACK; return( ret ); } ================================================ FILE: deps/pjsip/third_party/opus/silk/x86/VQ_WMat_EC_sse.c ================================================ /* Copyright (c) 2014, Cisco Systems, INC Written by XiangMingZhu WeiZhou MinPeng YanWang Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "main.h" #include "celt/x86/x86cpu.h" /* Entropy constrained matrix-weighted VQ, hard-coded to 5-element vectors, for a single input data vector */ void silk_VQ_WMat_EC_sse4_1( opus_int8 *ind, /* O index of best codebook vector */ opus_int32 *rate_dist_Q14, /* O best weighted quant error + mu * rate */ opus_int *gain_Q7, /* O sum of absolute LTP coefficients */ const opus_int16 *in_Q14, /* I input vector to be quantized */ const opus_int32 *W_Q18, /* I weighting matrix */ const opus_int8 *cb_Q7, /* I codebook */ const opus_uint8 *cb_gain_Q7, /* I codebook effective gain */ const opus_uint8 *cl_Q5, /* I code length for each codebook vector */ const opus_int mu_Q9, /* I tradeoff betw. weighted error and rate */ const opus_int32 max_gain_Q7, /* I maximum sum of absolute LTP coefficients */ opus_int L /* I number of vectors in codebook */ ) { opus_int k, gain_tmp_Q7; const opus_int8 *cb_row_Q7; opus_int16 diff_Q14[ 5 ]; opus_int32 sum1_Q14, sum2_Q16; __m128i C_tmp1, C_tmp2, C_tmp3, C_tmp4, C_tmp5; /* Loop over codebook */ *rate_dist_Q14 = silk_int32_MAX; cb_row_Q7 = cb_Q7; for( k = 0; k < L; k++ ) { gain_tmp_Q7 = cb_gain_Q7[k]; diff_Q14[ 0 ] = in_Q14[ 0 ] - silk_LSHIFT( cb_row_Q7[ 0 ], 7 ); C_tmp1 = OP_CVTEPI16_EPI32_M64( &in_Q14[ 1 ] ); C_tmp2 = OP_CVTEPI8_EPI32_M32( &cb_row_Q7[ 1 ] ); C_tmp2 = _mm_slli_epi32( C_tmp2, 7 ); C_tmp1 = _mm_sub_epi32( C_tmp1, C_tmp2 ); diff_Q14[ 1 ] = _mm_extract_epi16( C_tmp1, 0 ); diff_Q14[ 2 ] = _mm_extract_epi16( C_tmp1, 2 ); diff_Q14[ 3 ] = _mm_extract_epi16( C_tmp1, 4 ); diff_Q14[ 4 ] = _mm_extract_epi16( C_tmp1, 6 ); /* Weighted rate */ sum1_Q14 = silk_SMULBB( mu_Q9, cl_Q5[ k ] ); /* Penalty for too large gain */ sum1_Q14 = silk_ADD_LSHIFT32( sum1_Q14, silk_max( silk_SUB32( gain_tmp_Q7, max_gain_Q7 ), 0 ), 10 ); silk_assert( sum1_Q14 >= 0 ); /* first row of W_Q18 */ C_tmp3 = _mm_loadu_si128( (__m128i *)(&W_Q18[ 1 ] ) ); C_tmp4 = _mm_mul_epi32( C_tmp3, C_tmp1 ); C_tmp4 = _mm_srli_si128( C_tmp4, 2 ); C_tmp1 = _mm_shuffle_epi32( C_tmp1, _MM_SHUFFLE( 0, 3, 2, 1 ) ); /* shift right 4 bytes */ C_tmp3 = _mm_shuffle_epi32( C_tmp3, _MM_SHUFFLE( 0, 3, 2, 1 ) ); /* shift right 4 bytes */ C_tmp5 = _mm_mul_epi32( C_tmp3, C_tmp1 ); C_tmp5 = _mm_srli_si128( C_tmp5, 2 ); C_tmp5 = _mm_add_epi32( C_tmp4, C_tmp5 ); C_tmp5 = _mm_slli_epi32( C_tmp5, 1 ); C_tmp5 = _mm_add_epi32( C_tmp5, _mm_shuffle_epi32( C_tmp5, _MM_SHUFFLE( 0, 0, 0, 2 ) ) ); sum2_Q16 = _mm_cvtsi128_si32( C_tmp5 ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 0 ], diff_Q14[ 0 ] ); sum1_Q14 = silk_SMLAWB( sum1_Q14, sum2_Q16, diff_Q14[ 0 ] ); /* second row of W_Q18 */ sum2_Q16 = silk_SMULWB( W_Q18[ 7 ], diff_Q14[ 2 ] ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 8 ], diff_Q14[ 3 ] ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 9 ], diff_Q14[ 4 ] ); sum2_Q16 = silk_LSHIFT( sum2_Q16, 1 ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 6 ], diff_Q14[ 1 ] ); sum1_Q14 = silk_SMLAWB( sum1_Q14, sum2_Q16, diff_Q14[ 1 ] ); /* third row of W_Q18 */ sum2_Q16 = silk_SMULWB( W_Q18[ 13 ], diff_Q14[ 3 ] ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 14 ], diff_Q14[ 4 ] ); sum2_Q16 = silk_LSHIFT( sum2_Q16, 1 ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 12 ], diff_Q14[ 2 ] ); sum1_Q14 = silk_SMLAWB( sum1_Q14, sum2_Q16, diff_Q14[ 2 ] ); /* fourth row of W_Q18 */ sum2_Q16 = silk_SMULWB( W_Q18[ 19 ], diff_Q14[ 4 ] ); sum2_Q16 = silk_LSHIFT( sum2_Q16, 1 ); sum2_Q16 = silk_SMLAWB( sum2_Q16, W_Q18[ 18 ], diff_Q14[ 3 ] ); sum1_Q14 = silk_SMLAWB( sum1_Q14, sum2_Q16, diff_Q14[ 3 ] ); /* last row of W_Q18 */ sum2_Q16 = silk_SMULWB( W_Q18[ 24 ], diff_Q14[ 4 ] ); sum1_Q14 = silk_SMLAWB( sum1_Q14, sum2_Q16, diff_Q14[ 4 ] ); silk_assert( sum1_Q14 >= 0 ); /* find best */ if( sum1_Q14 < *rate_dist_Q14 ) { *rate_dist_Q14 = sum1_Q14; *ind = (opus_int8)k; *gain_Q7 = gain_tmp_Q7; } /* Go to next cbk vector */ cb_row_Q7 += LTP_ORDER; } } ================================================ FILE: deps/pjsip/third_party/opus/silk/x86/main_sse.h ================================================ /* Copyright (c) 2014, Cisco Systems, INC Written by XiangMingZhu WeiZhou MinPeng YanWang Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MAIN_SSE_H #define MAIN_SSE_H #ifdef HAVE_CONFIG_H #include "config.h" #endif # if defined(OPUS_X86_MAY_HAVE_SSE4_1) # define OVERRIDE_silk_VQ_WMat_EC void silk_VQ_WMat_EC_sse4_1( opus_int8 *ind, /* O index of best codebook vector */ opus_int32 *rate_dist_Q14, /* O best weighted quant error + mu * rate */ opus_int *gain_Q7, /* O sum of absolute LTP coefficients */ const opus_int16 *in_Q14, /* I input vector to be quantized */ const opus_int32 *W_Q18, /* I weighting matrix */ const opus_int8 *cb_Q7, /* I codebook */ const opus_uint8 *cb_gain_Q7, /* I codebook effective gain */ const opus_uint8 *cl_Q5, /* I code length for each codebook vector */ const opus_int mu_Q9, /* I tradeoff betw. weighted error and rate */ const opus_int32 max_gain_Q7, /* I maximum sum of absolute LTP coefficients */ opus_int L /* I number of vectors in codebook */ ); #if defined OPUS_X86_PRESUME_SSE4_1 #define silk_VQ_WMat_EC(ind, rate_dist_Q14, gain_Q7, in_Q14, W_Q18, cb_Q7, cb_gain_Q7, cl_Q5, \ mu_Q9, max_gain_Q7, L, arch) \ ((void)(arch),silk_VQ_WMat_EC_sse4_1(ind, rate_dist_Q14, gain_Q7, in_Q14, W_Q18, cb_Q7, cb_gain_Q7, cl_Q5, \ mu_Q9, max_gain_Q7, L)) #else extern void (*const SILK_VQ_WMAT_EC_IMPL[OPUS_ARCHMASK + 1])( opus_int8 *ind, /* O index of best codebook vector */ opus_int32 *rate_dist_Q14, /* O best weighted quant error + mu * rate */ opus_int *gain_Q7, /* O sum of absolute LTP coefficients */ const opus_int16 *in_Q14, /* I input vector to be quantized */ const opus_int32 *W_Q18, /* I weighting matrix */ const opus_int8 *cb_Q7, /* I codebook */ const opus_uint8 *cb_gain_Q7, /* I codebook effective gain */ const opus_uint8 *cl_Q5, /* I code length for each codebook vector */ const opus_int mu_Q9, /* I tradeoff betw. weighted error and rate */ const opus_int32 max_gain_Q7, /* I maximum sum of absolute LTP coefficients */ opus_int L /* I number of vectors in codebook */ ); # define silk_VQ_WMat_EC(ind, rate_dist_Q14, gain_Q7, in_Q14, W_Q18, cb_Q7, cb_gain_Q7, cl_Q5, \ mu_Q9, max_gain_Q7, L, arch) \ ((*SILK_VQ_WMAT_EC_IMPL[(arch) & OPUS_ARCHMASK])(ind, rate_dist_Q14, gain_Q7, in_Q14, W_Q18, cb_Q7, cb_gain_Q7, cl_Q5, \ mu_Q9, max_gain_Q7, L)) #endif # define OVERRIDE_silk_NSQ void silk_NSQ_sse4_1( const silk_encoder_state *psEncC, /* I/O Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ SideInfoIndices *psIndices, /* I/O Quantization Indices */ const opus_int32 x_Q3[], /* I Prefiltered input signal */ opus_int8 pulses[], /* O Quantized pulse signal */ const opus_int16 PredCoef_Q12[ 2 * MAX_LPC_ORDER ], /* I Short term prediction coefs */ const opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ], /* I Long term prediction coefs */ const opus_int16 AR2_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ], /* I Noise shaping coefs */ const opus_int HarmShapeGain_Q14[ MAX_NB_SUBFR ], /* I Long term shaping coefs */ const opus_int Tilt_Q14[ MAX_NB_SUBFR ], /* I Spectral tilt */ const opus_int32 LF_shp_Q14[ MAX_NB_SUBFR ], /* I Low frequency shaping coefs */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I Quantization step sizes */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lags */ const opus_int Lambda_Q10, /* I Rate/distortion tradeoff */ const opus_int LTP_scale_Q14 /* I LTP state scaling */ ); #if defined OPUS_X86_PRESUME_SSE4_1 #define silk_NSQ(psEncC, NSQ, psIndices, x_Q3, pulses, PredCoef_Q12, LTPCoef_Q14, AR2_Q13, \ HarmShapeGain_Q14, Tilt_Q14, LF_shp_Q14, Gains_Q16, pitchL, Lambda_Q10, LTP_scale_Q14, arch) \ ((void)(arch),silk_NSQ_sse4_1(psEncC, NSQ, psIndices, x_Q3, pulses, PredCoef_Q12, LTPCoef_Q14, AR2_Q13, \ HarmShapeGain_Q14, Tilt_Q14, LF_shp_Q14, Gains_Q16, pitchL, Lambda_Q10, LTP_scale_Q14)) #else extern void (*const SILK_NSQ_IMPL[OPUS_ARCHMASK + 1])( const silk_encoder_state *psEncC, /* I/O Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ SideInfoIndices *psIndices, /* I/O Quantization Indices */ const opus_int32 x_Q3[], /* I Prefiltered input signal */ opus_int8 pulses[], /* O Quantized pulse signal */ const opus_int16 PredCoef_Q12[ 2 * MAX_LPC_ORDER ], /* I Short term prediction coefs */ const opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ], /* I Long term prediction coefs */ const opus_int16 AR2_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ], /* I Noise shaping coefs */ const opus_int HarmShapeGain_Q14[ MAX_NB_SUBFR ], /* I Long term shaping coefs */ const opus_int Tilt_Q14[ MAX_NB_SUBFR ], /* I Spectral tilt */ const opus_int32 LF_shp_Q14[ MAX_NB_SUBFR ], /* I Low frequency shaping coefs */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I Quantization step sizes */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lags */ const opus_int Lambda_Q10, /* I Rate/distortion tradeoff */ const opus_int LTP_scale_Q14 /* I LTP state scaling */ ); # define silk_NSQ(psEncC, NSQ, psIndices, x_Q3, pulses, PredCoef_Q12, LTPCoef_Q14, AR2_Q13, \ HarmShapeGain_Q14, Tilt_Q14, LF_shp_Q14, Gains_Q16, pitchL, Lambda_Q10, LTP_scale_Q14, arch) \ ((*SILK_NSQ_IMPL[(arch) & OPUS_ARCHMASK])(psEncC, NSQ, psIndices, x_Q3, pulses, PredCoef_Q12, LTPCoef_Q14, AR2_Q13, \ HarmShapeGain_Q14, Tilt_Q14, LF_shp_Q14, Gains_Q16, pitchL, Lambda_Q10, LTP_scale_Q14)) #endif # define OVERRIDE_silk_NSQ_del_dec void silk_NSQ_del_dec_sse4_1( const silk_encoder_state *psEncC, /* I/O Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ SideInfoIndices *psIndices, /* I/O Quantization Indices */ const opus_int32 x_Q3[], /* I Prefiltered input signal */ opus_int8 pulses[], /* O Quantized pulse signal */ const opus_int16 PredCoef_Q12[ 2 * MAX_LPC_ORDER ], /* I Short term prediction coefs */ const opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ], /* I Long term prediction coefs */ const opus_int16 AR2_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ], /* I Noise shaping coefs */ const opus_int HarmShapeGain_Q14[ MAX_NB_SUBFR ], /* I Long term shaping coefs */ const opus_int Tilt_Q14[ MAX_NB_SUBFR ], /* I Spectral tilt */ const opus_int32 LF_shp_Q14[ MAX_NB_SUBFR ], /* I Low frequency shaping coefs */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I Quantization step sizes */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lags */ const opus_int Lambda_Q10, /* I Rate/distortion tradeoff */ const opus_int LTP_scale_Q14 /* I LTP state scaling */ ); #if defined OPUS_X86_PRESUME_SSE4_1 #define silk_NSQ_del_dec(psEncC, NSQ, psIndices, x_Q3, pulses, PredCoef_Q12, LTPCoef_Q14, AR2_Q13, \ HarmShapeGain_Q14, Tilt_Q14, LF_shp_Q14, Gains_Q16, pitchL, Lambda_Q10, LTP_scale_Q14, arch) \ ((void)(arch),silk_NSQ_del_dec_sse4_1(psEncC, NSQ, psIndices, x_Q3, pulses, PredCoef_Q12, LTPCoef_Q14, AR2_Q13, \ HarmShapeGain_Q14, Tilt_Q14, LF_shp_Q14, Gains_Q16, pitchL, Lambda_Q10, LTP_scale_Q14)) #else extern void (*const SILK_NSQ_DEL_DEC_IMPL[OPUS_ARCHMASK + 1])( const silk_encoder_state *psEncC, /* I/O Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ SideInfoIndices *psIndices, /* I/O Quantization Indices */ const opus_int32 x_Q3[], /* I Prefiltered input signal */ opus_int8 pulses[], /* O Quantized pulse signal */ const opus_int16 PredCoef_Q12[ 2 * MAX_LPC_ORDER ], /* I Short term prediction coefs */ const opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ], /* I Long term prediction coefs */ const opus_int16 AR2_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ], /* I Noise shaping coefs */ const opus_int HarmShapeGain_Q14[ MAX_NB_SUBFR ], /* I Long term shaping coefs */ const opus_int Tilt_Q14[ MAX_NB_SUBFR ], /* I Spectral tilt */ const opus_int32 LF_shp_Q14[ MAX_NB_SUBFR ], /* I Low frequency shaping coefs */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I Quantization step sizes */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lags */ const opus_int Lambda_Q10, /* I Rate/distortion tradeoff */ const opus_int LTP_scale_Q14 /* I LTP state scaling */ ); # define silk_NSQ_del_dec(psEncC, NSQ, psIndices, x_Q3, pulses, PredCoef_Q12, LTPCoef_Q14, AR2_Q13, \ HarmShapeGain_Q14, Tilt_Q14, LF_shp_Q14, Gains_Q16, pitchL, Lambda_Q10, LTP_scale_Q14, arch) \ ((*SILK_NSQ_DEL_DEC_IMPL[(arch) & OPUS_ARCHMASK])(psEncC, NSQ, psIndices, x_Q3, pulses, PredCoef_Q12, LTPCoef_Q14, AR2_Q13, \ HarmShapeGain_Q14, Tilt_Q14, LF_shp_Q14, Gains_Q16, pitchL, Lambda_Q10, LTP_scale_Q14)) #endif void silk_noise_shape_quantizer( silk_nsq_state *NSQ, /* I/O NSQ state */ opus_int signalType, /* I Signal type */ const opus_int32 x_sc_Q10[], /* I */ opus_int8 pulses[], /* O */ opus_int16 xq[], /* O */ opus_int32 sLTP_Q15[], /* I/O LTP state */ const opus_int16 a_Q12[], /* I Short term prediction coefs */ const opus_int16 b_Q14[], /* I Long term prediction coefs */ const opus_int16 AR_shp_Q13[], /* I Noise shaping AR coefs */ opus_int lag, /* I Pitch lag */ opus_int32 HarmShapeFIRPacked_Q14, /* I */ opus_int Tilt_Q14, /* I Spectral tilt */ opus_int32 LF_shp_Q14, /* I */ opus_int32 Gain_Q16, /* I */ opus_int Lambda_Q10, /* I */ opus_int offset_Q10, /* I */ opus_int length, /* I Input length */ opus_int shapingLPCOrder, /* I Noise shaping AR filter order */ opus_int predictLPCOrder /* I Prediction filter order */ ); /**************************/ /* Noise level estimation */ /**************************/ void silk_VAD_GetNoiseLevels( const opus_int32 pX[ VAD_N_BANDS ], /* I subband energies */ silk_VAD_state *psSilk_VAD /* I/O Pointer to Silk VAD state */ ); # define OVERRIDE_silk_VAD_GetSA_Q8 opus_int silk_VAD_GetSA_Q8_sse4_1( silk_encoder_state *psEnC, const opus_int16 pIn[] ); #if defined(OPUS_X86_PRESUME_SSE4_1) #define silk_VAD_GetSA_Q8(psEnC, pIn, arch) ((void)(arch),silk_VAD_GetSA_Q8_sse4_1(psEnC, pIn)) #else # define silk_VAD_GetSA_Q8(psEnC, pIn, arch) \ ((*SILK_VAD_GETSA_Q8_IMPL[(arch) & OPUS_ARCHMASK])(psEnC, pIn)) extern opus_int (*const SILK_VAD_GETSA_Q8_IMPL[OPUS_ARCHMASK + 1])( silk_encoder_state *psEnC, const opus_int16 pIn[]); # define OVERRIDE_silk_warped_LPC_analysis_filter_FIX #endif void silk_warped_LPC_analysis_filter_FIX_sse4_1( opus_int32 state[], /* I/O State [order + 1] */ opus_int32 res_Q2[], /* O Residual signal [length] */ const opus_int16 coef_Q13[], /* I Coefficients [order] */ const opus_int16 input[], /* I Input signal [length] */ const opus_int16 lambda_Q16, /* I Warping factor */ const opus_int length, /* I Length of input signal */ const opus_int order /* I Filter order (even) */ ); #if defined(OPUS_X86_PRESUME_SSE4_1) #define silk_warped_LPC_analysis_filter_FIX(state, res_Q2, coef_Q13, input, lambda_Q16, length, order, arch) \ ((void)(arch),silk_warped_LPC_analysis_filter_FIX_c(state, res_Q2, coef_Q13, input, lambda_Q16, length, order)) #else extern void (*const SILK_WARPED_LPC_ANALYSIS_FILTER_FIX_IMPL[OPUS_ARCHMASK + 1])( opus_int32 state[], /* I/O State [order + 1] */ opus_int32 res_Q2[], /* O Residual signal [length] */ const opus_int16 coef_Q13[], /* I Coefficients [order] */ const opus_int16 input[], /* I Input signal [length] */ const opus_int16 lambda_Q16, /* I Warping factor */ const opus_int length, /* I Length of input signal */ const opus_int order /* I Filter order (even) */ ); # define silk_warped_LPC_analysis_filter_FIX(state, res_Q2, coef_Q13, input, lambda_Q16, length, order, arch) \ ((*SILK_WARPED_LPC_ANALYSIS_FILTER_FIX_IMPL[(arch) & OPUS_ARCHMASK])(state, res_Q2, coef_Q13, input, lambda_Q16, length, order)) #endif # endif #endif ================================================ FILE: deps/pjsip/third_party/opus/silk/x86/x86_silk_map.c ================================================ /* Copyright (c) 2014, Cisco Systems, INC Written by XiangMingZhu WeiZhou MinPeng YanWang Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #include "celt/x86/x86cpu.h" #include "structs.h" #include "SigProc_FIX.h" #include "pitch.h" #include "main.h" #if !defined(OPUS_X86_PRESUME_SSE4_1) #if defined(FIXED_POINT) #include "fixed/main_FIX.h" opus_int64 (*const SILK_INNER_PROD16_ALIGNED_64_IMPL[ OPUS_ARCHMASK + 1 ] )( const opus_int16 *inVec1, const opus_int16 *inVec2, const opus_int len ) = { silk_inner_prod16_aligned_64_c, /* non-sse */ silk_inner_prod16_aligned_64_c, silk_inner_prod16_aligned_64_c, MAY_HAVE_SSE4_1( silk_inner_prod16_aligned_64 ), /* sse4.1 */ MAY_HAVE_SSE4_1( silk_inner_prod16_aligned_64 ) /* avx */ }; #endif opus_int (*const SILK_VAD_GETSA_Q8_IMPL[ OPUS_ARCHMASK + 1 ] )( silk_encoder_state *psEncC, const opus_int16 pIn[] ) = { silk_VAD_GetSA_Q8_c, /* non-sse */ silk_VAD_GetSA_Q8_c, silk_VAD_GetSA_Q8_c, MAY_HAVE_SSE4_1( silk_VAD_GetSA_Q8 ), /* sse4.1 */ MAY_HAVE_SSE4_1( silk_VAD_GetSA_Q8 ) /* avx */ }; void (*const SILK_NSQ_IMPL[ OPUS_ARCHMASK + 1 ] )( const silk_encoder_state *psEncC, /* I/O Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ SideInfoIndices *psIndices, /* I/O Quantization Indices */ const opus_int32 x_Q3[], /* I Prefiltered input signal */ opus_int8 pulses[], /* O Quantized pulse signal */ const opus_int16 PredCoef_Q12[ 2 * MAX_LPC_ORDER ], /* I Short term prediction coefs */ const opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ], /* I Long term prediction coefs */ const opus_int16 AR2_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ], /* I Noise shaping coefs */ const opus_int HarmShapeGain_Q14[ MAX_NB_SUBFR ], /* I Long term shaping coefs */ const opus_int Tilt_Q14[ MAX_NB_SUBFR ], /* I Spectral tilt */ const opus_int32 LF_shp_Q14[ MAX_NB_SUBFR ], /* I Low frequency shaping coefs */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I Quantization step sizes */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lags */ const opus_int Lambda_Q10, /* I Rate/distortion tradeoff */ const opus_int LTP_scale_Q14 /* I LTP state scaling */ ) = { silk_NSQ_c, /* non-sse */ silk_NSQ_c, silk_NSQ_c, MAY_HAVE_SSE4_1( silk_NSQ ), /* sse4.1 */ MAY_HAVE_SSE4_1( silk_NSQ ) /* avx */ }; void (*const SILK_VQ_WMAT_EC_IMPL[ OPUS_ARCHMASK + 1 ] )( opus_int8 *ind, /* O index of best codebook vector */ opus_int32 *rate_dist_Q14, /* O best weighted quant error + mu * rate */ opus_int *gain_Q7, /* O sum of absolute LTP coefficients */ const opus_int16 *in_Q14, /* I input vector to be quantized */ const opus_int32 *W_Q18, /* I weighting matrix */ const opus_int8 *cb_Q7, /* I codebook */ const opus_uint8 *cb_gain_Q7, /* I codebook effective gain */ const opus_uint8 *cl_Q5, /* I code length for each codebook vector */ const opus_int mu_Q9, /* I tradeoff betw. weighted error and rate */ const opus_int32 max_gain_Q7, /* I maximum sum of absolute LTP coefficients */ opus_int L /* I number of vectors in codebook */ ) = { silk_VQ_WMat_EC_c, /* non-sse */ silk_VQ_WMat_EC_c, silk_VQ_WMat_EC_c, MAY_HAVE_SSE4_1( silk_VQ_WMat_EC ), /* sse4.1 */ MAY_HAVE_SSE4_1( silk_VQ_WMat_EC ) /* avx */ }; void (*const SILK_NSQ_DEL_DEC_IMPL[ OPUS_ARCHMASK + 1 ] )( const silk_encoder_state *psEncC, /* I/O Encoder State */ silk_nsq_state *NSQ, /* I/O NSQ state */ SideInfoIndices *psIndices, /* I/O Quantization Indices */ const opus_int32 x_Q3[], /* I Prefiltered input signal */ opus_int8 pulses[], /* O Quantized pulse signal */ const opus_int16 PredCoef_Q12[ 2 * MAX_LPC_ORDER ], /* I Short term prediction coefs */ const opus_int16 LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ], /* I Long term prediction coefs */ const opus_int16 AR2_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ], /* I Noise shaping coefs */ const opus_int HarmShapeGain_Q14[ MAX_NB_SUBFR ], /* I Long term shaping coefs */ const opus_int Tilt_Q14[ MAX_NB_SUBFR ], /* I Spectral tilt */ const opus_int32 LF_shp_Q14[ MAX_NB_SUBFR ], /* I Low frequency shaping coefs */ const opus_int32 Gains_Q16[ MAX_NB_SUBFR ], /* I Quantization step sizes */ const opus_int pitchL[ MAX_NB_SUBFR ], /* I Pitch lags */ const opus_int Lambda_Q10, /* I Rate/distortion tradeoff */ const opus_int LTP_scale_Q14 /* I LTP state scaling */ ) = { silk_NSQ_del_dec_c, /* non-sse */ silk_NSQ_del_dec_c, silk_NSQ_del_dec_c, MAY_HAVE_SSE4_1( silk_NSQ_del_dec ), /* sse4.1 */ MAY_HAVE_SSE4_1( silk_NSQ_del_dec ) /* avx */ }; #if defined(FIXED_POINT) void (*const SILK_WARPED_LPC_ANALYSIS_FILTER_FIX_IMPL[ OPUS_ARCHMASK + 1 ] )( opus_int32 state[], /* I/O State [order + 1] */ opus_int32 res_Q2[], /* O Residual signal [length] */ const opus_int16 coef_Q13[], /* I Coefficients [order] */ const opus_int16 input[], /* I Input signal [length] */ const opus_int16 lambda_Q16, /* I Warping factor */ const opus_int length, /* I Length of input signal */ const opus_int order /* I Filter order (even) */ ) = { silk_warped_LPC_analysis_filter_FIX_c, /* non-sse */ silk_warped_LPC_analysis_filter_FIX_c, silk_warped_LPC_analysis_filter_FIX_c, MAY_HAVE_SSE4_1( silk_warped_LPC_analysis_filter_FIX ), /* sse4.1 */ MAY_HAVE_SSE4_1( silk_warped_LPC_analysis_filter_FIX ) /* avx */ }; void (*const SILK_BURG_MODIFIED_IMPL[ OPUS_ARCHMASK + 1 ] )( opus_int32 *res_nrg, /* O Residual energy */ opus_int *res_nrg_Q, /* O Residual energy Q value */ opus_int32 A_Q16[], /* O Prediction coefficients (length order) */ const opus_int16 x[], /* I Input signal, length: nb_subfr * ( D + subfr_length ) */ const opus_int32 minInvGain_Q30, /* I Inverse of max prediction gain */ const opus_int subfr_length, /* I Input signal subframe length (incl. D preceding samples) */ const opus_int nb_subfr, /* I Number of subframes stacked in x */ const opus_int D, /* I Order */ int arch /* I Run-time architecture */ ) = { silk_burg_modified_c, /* non-sse */ silk_burg_modified_c, silk_burg_modified_c, MAY_HAVE_SSE4_1( silk_burg_modified ), /* sse4.1 */ MAY_HAVE_SSE4_1( silk_burg_modified ) /* avx */ }; #endif #endif ================================================ FILE: deps/pjsip/third_party/opus/silk_headers.mk ================================================ SILK_HEAD = \ silk/debug.h \ silk/control.h \ silk/errors.h \ silk/API.h \ silk/typedef.h \ silk/define.h \ silk/main.h \ silk/x86/main_sse.h \ silk/PLC.h \ silk/structs.h \ silk/tables.h \ silk/tuning_parameters.h \ silk/Inlines.h \ silk/MacroCount.h \ silk/MacroDebug.h \ silk/macros.h \ silk/pitch_est_defines.h \ silk/resampler_private.h \ silk/resampler_rom.h \ silk/resampler_structs.h \ silk/SigProc_FIX.h \ silk/x86/SigProc_FIX_sse.h \ silk/arm/macros_armv4.h \ silk/arm/macros_armv5e.h \ silk/arm/SigProc_FIX_armv4.h \ silk/arm/SigProc_FIX_armv5e.h \ silk/fixed/main_FIX.h \ silk/fixed/structs_FIX.h \ silk/fixed/mips/noise_shape_analysis_FIX_mipsr1.h \ silk/fixed/mips/prefilter_FIX_mipsr1.h \ silk/fixed/mips/warped_autocorrelation_FIX_mipsr1.h \ silk/float/main_FLP.h \ silk/float/structs_FLP.h \ silk/float/SigProc_FLP.h \ silk/mips/macros_mipsr1.h \ silk/mips/NSQ_del_dec_mipsr1.h \ silk/mips/sigproc_fix_mipsr1.h ================================================ FILE: deps/pjsip/third_party/opus/silk_sources.mk ================================================ SILK_SOURCES = \ silk/CNG.c \ silk/code_signs.c \ silk/init_decoder.c \ silk/decode_core.c \ silk/decode_frame.c \ silk/decode_parameters.c \ silk/decode_indices.c \ silk/decode_pulses.c \ silk/decoder_set_fs.c \ silk/dec_API.c \ silk/enc_API.c \ silk/encode_indices.c \ silk/encode_pulses.c \ silk/gain_quant.c \ silk/interpolate.c \ silk/LP_variable_cutoff.c \ silk/NLSF_decode.c \ silk/NSQ.c \ silk/NSQ_del_dec.c \ silk/PLC.c \ silk/shell_coder.c \ silk/tables_gain.c \ silk/tables_LTP.c \ silk/tables_NLSF_CB_NB_MB.c \ silk/tables_NLSF_CB_WB.c \ silk/tables_other.c \ silk/tables_pitch_lag.c \ silk/tables_pulses_per_block.c \ silk/VAD.c \ silk/control_audio_bandwidth.c \ silk/quant_LTP_gains.c \ silk/VQ_WMat_EC.c \ silk/HP_variable_cutoff.c \ silk/NLSF_encode.c \ silk/NLSF_VQ.c \ silk/NLSF_unpack.c \ silk/NLSF_del_dec_quant.c \ silk/process_NLSFs.c \ silk/stereo_LR_to_MS.c \ silk/stereo_MS_to_LR.c \ silk/check_control_input.c \ silk/control_SNR.c \ silk/init_encoder.c \ silk/control_codec.c \ silk/A2NLSF.c \ silk/ana_filt_bank_1.c \ silk/biquad_alt.c \ silk/bwexpander_32.c \ silk/bwexpander.c \ silk/debug.c \ silk/decode_pitch.c \ silk/inner_prod_aligned.c \ silk/lin2log.c \ silk/log2lin.c \ silk/LPC_analysis_filter.c \ silk/LPC_inv_pred_gain.c \ silk/table_LSF_cos.c \ silk/NLSF2A.c \ silk/NLSF_stabilize.c \ silk/NLSF_VQ_weights_laroia.c \ silk/pitch_est_tables.c \ silk/resampler.c \ silk/resampler_down2_3.c \ silk/resampler_down2.c \ silk/resampler_private_AR2.c \ silk/resampler_private_down_FIR.c \ silk/resampler_private_IIR_FIR.c \ silk/resampler_private_up2_HQ.c \ silk/resampler_rom.c \ silk/sigm_Q15.c \ silk/sort.c \ silk/sum_sqr_shift.c \ silk/stereo_decode_pred.c \ silk/stereo_encode_pred.c \ silk/stereo_find_predictor.c \ silk/stereo_quant_pred.c SILK_SOURCES_SSE4_1 = silk/x86/NSQ_sse.c \ silk/x86/NSQ_del_dec_sse.c \ silk/x86/x86_silk_map.c \ silk/x86/VAD_sse.c \ silk/x86/VQ_WMat_EC_sse.c SILK_SOURCES_FIXED = \ silk/fixed/LTP_analysis_filter_FIX.c \ silk/fixed/LTP_scale_ctrl_FIX.c \ silk/fixed/corrMatrix_FIX.c \ silk/fixed/encode_frame_FIX.c \ silk/fixed/find_LPC_FIX.c \ silk/fixed/find_LTP_FIX.c \ silk/fixed/find_pitch_lags_FIX.c \ silk/fixed/find_pred_coefs_FIX.c \ silk/fixed/noise_shape_analysis_FIX.c \ silk/fixed/prefilter_FIX.c \ silk/fixed/process_gains_FIX.c \ silk/fixed/regularize_correlations_FIX.c \ silk/fixed/residual_energy16_FIX.c \ silk/fixed/residual_energy_FIX.c \ silk/fixed/solve_LS_FIX.c \ silk/fixed/warped_autocorrelation_FIX.c \ silk/fixed/apply_sine_window_FIX.c \ silk/fixed/autocorr_FIX.c \ silk/fixed/burg_modified_FIX.c \ silk/fixed/k2a_FIX.c \ silk/fixed/k2a_Q16_FIX.c \ silk/fixed/pitch_analysis_core_FIX.c \ silk/fixed/vector_ops_FIX.c \ silk/fixed/schur64_FIX.c \ silk/fixed/schur_FIX.c SILK_SOURCES_FIXED_SSE4_1 = silk/fixed/x86/vector_ops_FIX_sse.c \ silk/fixed/x86/burg_modified_FIX_sse.c \ silk/fixed/x86/prefilter_FIX_sse.c SILK_SOURCES_FLOAT = \ silk/float/apply_sine_window_FLP.c \ silk/float/corrMatrix_FLP.c \ silk/float/encode_frame_FLP.c \ silk/float/find_LPC_FLP.c \ silk/float/find_LTP_FLP.c \ silk/float/find_pitch_lags_FLP.c \ silk/float/find_pred_coefs_FLP.c \ silk/float/LPC_analysis_filter_FLP.c \ silk/float/LTP_analysis_filter_FLP.c \ silk/float/LTP_scale_ctrl_FLP.c \ silk/float/noise_shape_analysis_FLP.c \ silk/float/prefilter_FLP.c \ silk/float/process_gains_FLP.c \ silk/float/regularize_correlations_FLP.c \ silk/float/residual_energy_FLP.c \ silk/float/solve_LS_FLP.c \ silk/float/warped_autocorrelation_FLP.c \ silk/float/wrappers_FLP.c \ silk/float/autocorrelation_FLP.c \ silk/float/burg_modified_FLP.c \ silk/float/bwexpander_FLP.c \ silk/float/energy_FLP.c \ silk/float/inner_product_FLP.c \ silk/float/k2a_FLP.c \ silk/float/levinsondurbin_FLP.c \ silk/float/LPC_inv_pred_gain_FLP.c \ silk/float/pitch_analysis_core_FLP.c \ silk/float/scale_copy_vector_FLP.c \ silk/float/scale_vector_FLP.c \ silk/float/schur_FLP.c \ silk/float/sort_FLP.c ================================================ FILE: deps/pjsip/third_party/opus/src/analysis.c ================================================ /* Copyright (c) 2011 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "kiss_fft.h" #include "celt.h" #include "modes.h" #include "arch.h" #include "quant_bands.h" #include #include "analysis.h" #include "mlp.h" #include "stack_alloc.h" #ifndef M_PI #define M_PI 3.141592653 #endif static const float dct_table[128] = { 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.351851f, 0.338330f, 0.311806f, 0.273300f, 0.224292f, 0.166664f, 0.102631f, 0.034654f, -0.034654f,-0.102631f,-0.166664f,-0.224292f,-0.273300f,-0.311806f,-0.338330f,-0.351851f, 0.346760f, 0.293969f, 0.196424f, 0.068975f,-0.068975f,-0.196424f,-0.293969f,-0.346760f, -0.346760f,-0.293969f,-0.196424f,-0.068975f, 0.068975f, 0.196424f, 0.293969f, 0.346760f, 0.338330f, 0.224292f, 0.034654f,-0.166664f,-0.311806f,-0.351851f,-0.273300f,-0.102631f, 0.102631f, 0.273300f, 0.351851f, 0.311806f, 0.166664f,-0.034654f,-0.224292f,-0.338330f, 0.326641f, 0.135299f,-0.135299f,-0.326641f,-0.326641f,-0.135299f, 0.135299f, 0.326641f, 0.326641f, 0.135299f,-0.135299f,-0.326641f,-0.326641f,-0.135299f, 0.135299f, 0.326641f, 0.311806f, 0.034654f,-0.273300f,-0.338330f,-0.102631f, 0.224292f, 0.351851f, 0.166664f, -0.166664f,-0.351851f,-0.224292f, 0.102631f, 0.338330f, 0.273300f,-0.034654f,-0.311806f, 0.293969f,-0.068975f,-0.346760f,-0.196424f, 0.196424f, 0.346760f, 0.068975f,-0.293969f, -0.293969f, 0.068975f, 0.346760f, 0.196424f,-0.196424f,-0.346760f,-0.068975f, 0.293969f, 0.273300f,-0.166664f,-0.338330f, 0.034654f, 0.351851f, 0.102631f,-0.311806f,-0.224292f, 0.224292f, 0.311806f,-0.102631f,-0.351851f,-0.034654f, 0.338330f, 0.166664f,-0.273300f, }; static const float analysis_window[240] = { 0.000043f, 0.000171f, 0.000385f, 0.000685f, 0.001071f, 0.001541f, 0.002098f, 0.002739f, 0.003466f, 0.004278f, 0.005174f, 0.006156f, 0.007222f, 0.008373f, 0.009607f, 0.010926f, 0.012329f, 0.013815f, 0.015385f, 0.017037f, 0.018772f, 0.020590f, 0.022490f, 0.024472f, 0.026535f, 0.028679f, 0.030904f, 0.033210f, 0.035595f, 0.038060f, 0.040604f, 0.043227f, 0.045928f, 0.048707f, 0.051564f, 0.054497f, 0.057506f, 0.060591f, 0.063752f, 0.066987f, 0.070297f, 0.073680f, 0.077136f, 0.080665f, 0.084265f, 0.087937f, 0.091679f, 0.095492f, 0.099373f, 0.103323f, 0.107342f, 0.111427f, 0.115579f, 0.119797f, 0.124080f, 0.128428f, 0.132839f, 0.137313f, 0.141849f, 0.146447f, 0.151105f, 0.155823f, 0.160600f, 0.165435f, 0.170327f, 0.175276f, 0.180280f, 0.185340f, 0.190453f, 0.195619f, 0.200838f, 0.206107f, 0.211427f, 0.216797f, 0.222215f, 0.227680f, 0.233193f, 0.238751f, 0.244353f, 0.250000f, 0.255689f, 0.261421f, 0.267193f, 0.273005f, 0.278856f, 0.284744f, 0.290670f, 0.296632f, 0.302628f, 0.308658f, 0.314721f, 0.320816f, 0.326941f, 0.333097f, 0.339280f, 0.345492f, 0.351729f, 0.357992f, 0.364280f, 0.370590f, 0.376923f, 0.383277f, 0.389651f, 0.396044f, 0.402455f, 0.408882f, 0.415325f, 0.421783f, 0.428254f, 0.434737f, 0.441231f, 0.447736f, 0.454249f, 0.460770f, 0.467298f, 0.473832f, 0.480370f, 0.486912f, 0.493455f, 0.500000f, 0.506545f, 0.513088f, 0.519630f, 0.526168f, 0.532702f, 0.539230f, 0.545751f, 0.552264f, 0.558769f, 0.565263f, 0.571746f, 0.578217f, 0.584675f, 0.591118f, 0.597545f, 0.603956f, 0.610349f, 0.616723f, 0.623077f, 0.629410f, 0.635720f, 0.642008f, 0.648271f, 0.654508f, 0.660720f, 0.666903f, 0.673059f, 0.679184f, 0.685279f, 0.691342f, 0.697372f, 0.703368f, 0.709330f, 0.715256f, 0.721144f, 0.726995f, 0.732807f, 0.738579f, 0.744311f, 0.750000f, 0.755647f, 0.761249f, 0.766807f, 0.772320f, 0.777785f, 0.783203f, 0.788573f, 0.793893f, 0.799162f, 0.804381f, 0.809547f, 0.814660f, 0.819720f, 0.824724f, 0.829673f, 0.834565f, 0.839400f, 0.844177f, 0.848895f, 0.853553f, 0.858151f, 0.862687f, 0.867161f, 0.871572f, 0.875920f, 0.880203f, 0.884421f, 0.888573f, 0.892658f, 0.896677f, 0.900627f, 0.904508f, 0.908321f, 0.912063f, 0.915735f, 0.919335f, 0.922864f, 0.926320f, 0.929703f, 0.933013f, 0.936248f, 0.939409f, 0.942494f, 0.945503f, 0.948436f, 0.951293f, 0.954072f, 0.956773f, 0.959396f, 0.961940f, 0.964405f, 0.966790f, 0.969096f, 0.971321f, 0.973465f, 0.975528f, 0.977510f, 0.979410f, 0.981228f, 0.982963f, 0.984615f, 0.986185f, 0.987671f, 0.989074f, 0.990393f, 0.991627f, 0.992778f, 0.993844f, 0.994826f, 0.995722f, 0.996534f, 0.997261f, 0.997902f, 0.998459f, 0.998929f, 0.999315f, 0.999615f, 0.999829f, 0.999957f, 1.000000f, }; static const int tbands[NB_TBANDS+1] = { 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 68, 80, 96, 120 }; static const int extra_bands[NB_TOT_BANDS+1] = { 1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 68, 80, 96, 120, 160, 200 }; /*static const float tweight[NB_TBANDS+1] = { .3, .4, .5, .6, .7, .8, .9, 1., 1., 1., 1., 1., 1., 1., .8, .7, .6, .5 };*/ #define NB_TONAL_SKIP_BANDS 9 #define cA 0.43157974f #define cB 0.67848403f #define cC 0.08595542f #define cE ((float)M_PI/2) static OPUS_INLINE float fast_atan2f(float y, float x) { float x2, y2; /* Should avoid underflow on the values we'll get */ if (ABS16(x)+ABS16(y)<1e-9f) { x*=1e12f; y*=1e12f; } x2 = x*x; y2 = y*y; if(x2arch = opus_select_arch(); /* Clear remaining fields. */ tonality_analysis_reset(tonal); } void tonality_analysis_reset(TonalityAnalysisState *tonal) { /* Clear non-reusable fields. */ char *start = (char*)&tonal->TONALITY_ANALYSIS_RESET_START; OPUS_CLEAR(start, sizeof(TonalityAnalysisState) - (start - (char*)tonal)); } void tonality_get_info(TonalityAnalysisState *tonal, AnalysisInfo *info_out, int len) { int pos; int curr_lookahead; float psum; int i; pos = tonal->read_pos; curr_lookahead = tonal->write_pos-tonal->read_pos; if (curr_lookahead<0) curr_lookahead += DETECT_SIZE; if (len > 480 && pos != tonal->write_pos) { pos++; if (pos==DETECT_SIZE) pos=0; } if (pos == tonal->write_pos) pos--; if (pos<0) pos = DETECT_SIZE-1; OPUS_COPY(info_out, &tonal->info[pos], 1); tonal->read_subframe += len/120; while (tonal->read_subframe>=4) { tonal->read_subframe -= 4; tonal->read_pos++; } if (tonal->read_pos>=DETECT_SIZE) tonal->read_pos-=DETECT_SIZE; /* Compensate for the delay in the features themselves. FIXME: Need a better estimate the 10 I just made up */ curr_lookahead = IMAX(curr_lookahead-10, 0); psum=0; /* Summing the probability of transition patterns that involve music at time (DETECT_SIZE-curr_lookahead-1) */ for (i=0;ipmusic[i]; for (;ipspeech[i]; psum = psum*tonal->music_confidence + (1-psum)*tonal->speech_confidence; /*printf("%f %f %f\n", psum, info_out->music_prob, info_out->tonality);*/ info_out->music_prob = psum; } static void tonality_analysis(TonalityAnalysisState *tonal, const CELTMode *celt_mode, const void *x, int len, int offset, int c1, int c2, int C, int lsb_depth, downmix_func downmix) { int i, b; const kiss_fft_state *kfft; VARDECL(kiss_fft_cpx, in); VARDECL(kiss_fft_cpx, out); int N = 480, N2=240; float * OPUS_RESTRICT A = tonal->angle; float * OPUS_RESTRICT dA = tonal->d_angle; float * OPUS_RESTRICT d2A = tonal->d2_angle; VARDECL(float, tonality); VARDECL(float, noisiness); float band_tonality[NB_TBANDS]; float logE[NB_TBANDS]; float BFCC[8]; float features[25]; float frame_tonality; float max_frame_tonality; /*float tw_sum=0;*/ float frame_noisiness; const float pi4 = (float)(M_PI*M_PI*M_PI*M_PI); float slope=0; float frame_stationarity; float relativeE; float frame_probs[2]; float alpha, alphaE, alphaE2; float frame_loudness; float bandwidth_mask; int bandwidth=0; float maxE = 0; float noise_floor; int remaining; AnalysisInfo *info; SAVE_STACK; tonal->last_transition++; alpha = 1.f/IMIN(20, 1+tonal->count); alphaE = 1.f/IMIN(50, 1+tonal->count); alphaE2 = 1.f/IMIN(1000, 1+tonal->count); if (tonal->count<4) tonal->music_prob = .5; kfft = celt_mode->mdct.kfft[0]; if (tonal->count==0) tonal->mem_fill = 240; downmix(x, &tonal->inmem[tonal->mem_fill], IMIN(len, ANALYSIS_BUF_SIZE-tonal->mem_fill), offset, c1, c2, C); if (tonal->mem_fill+len < ANALYSIS_BUF_SIZE) { tonal->mem_fill += len; /* Don't have enough to update the analysis */ RESTORE_STACK; return; } info = &tonal->info[tonal->write_pos++]; if (tonal->write_pos>=DETECT_SIZE) tonal->write_pos-=DETECT_SIZE; ALLOC(in, 480, kiss_fft_cpx); ALLOC(out, 480, kiss_fft_cpx); ALLOC(tonality, 240, float); ALLOC(noisiness, 240, float); for (i=0;iinmem[i]); in[i].i = (kiss_fft_scalar)(w*tonal->inmem[N2+i]); in[N-i-1].r = (kiss_fft_scalar)(w*tonal->inmem[N-i-1]); in[N-i-1].i = (kiss_fft_scalar)(w*tonal->inmem[N+N2-i-1]); } OPUS_MOVE(tonal->inmem, tonal->inmem+ANALYSIS_BUF_SIZE-240, 240); remaining = len - (ANALYSIS_BUF_SIZE-tonal->mem_fill); downmix(x, &tonal->inmem[240], remaining, offset+ANALYSIS_BUF_SIZE-tonal->mem_fill, c1, c2, C); tonal->mem_fill = 240 + remaining; opus_fft(kfft, in, out, tonal->arch); #ifndef FIXED_POINT /* If there's any NaN on the input, the entire output will be NaN, so we only need to check one value. */ if (celt_isnan(out[0].r)) { info->valid = 0; RESTORE_STACK; return; } #endif for (i=1;iactivity = 0; frame_noisiness = 0; frame_stationarity = 0; if (!tonal->count) { for (b=0;blowE[b] = 1e10; tonal->highE[b] = -1e10; } } relativeE = 0; frame_loudness = 0; for (b=0;bvalid = 0; RESTORE_STACK; return; } #endif tonal->E[tonal->E_count][b] = E; frame_noisiness += nE/(1e-15f+E); frame_loudness += (float)sqrt(E+1e-10f); logE[b] = (float)log(E+1e-10f); tonal->lowE[b] = MIN32(logE[b], tonal->lowE[b]+.01f); tonal->highE[b] = MAX32(logE[b], tonal->highE[b]-.1f); if (tonal->highE[b] < tonal->lowE[b]+1.f) { tonal->highE[b]+=.5f; tonal->lowE[b]-=.5f; } relativeE += (logE[b]-tonal->lowE[b])/(1e-15f+tonal->highE[b]-tonal->lowE[b]); L1=L2=0; for (i=0;iE[i][b]); L2 += tonal->E[i][b]; } stationarity = MIN16(0.99f,L1/(float)sqrt(1e-15+NB_FRAMES*L2)); stationarity *= stationarity; stationarity *= stationarity; frame_stationarity += stationarity; /*band_tonality[b] = tE/(1e-15+E)*/; band_tonality[b] = MAX16(tE/(1e-15f+E), stationarity*tonal->prev_band_tonality[b]); #if 0 if (b>=NB_TONAL_SKIP_BANDS) { frame_tonality += tweight[b]*band_tonality[b]; tw_sum += tweight[b]; } #else frame_tonality += band_tonality[b]; if (b>=NB_TBANDS-NB_TONAL_SKIP_BANDS) frame_tonality -= band_tonality[b-NB_TBANDS+NB_TONAL_SKIP_BANDS]; #endif max_frame_tonality = MAX16(max_frame_tonality, (1.f+.03f*(b-NB_TBANDS))*frame_tonality); slope += band_tonality[b]*(b-8); /*printf("%f %f ", band_tonality[b], stationarity);*/ tonal->prev_band_tonality[b] = band_tonality[b]; } bandwidth_mask = 0; bandwidth = 0; maxE = 0; noise_floor = 5.7e-4f/(1<<(IMAX(0,lsb_depth-8))); #ifdef FIXED_POINT noise_floor *= 1<<(15+SIG_SHIFT); #endif noise_floor *= noise_floor; for (b=0;bmeanE[b] = MAX32((1-alphaE2)*tonal->meanE[b], E); E = MAX32(E, tonal->meanE[b]); /* Use a simple follower with 13 dB/Bark slope for spreading function */ bandwidth_mask = MAX32(.05f*bandwidth_mask, E); /* Consider the band "active" only if all these conditions are met: 1) less than 10 dB below the simple follower 2) less than 90 dB below the peak band (maximal masking possible considering both the ATH and the loudness-dependent slope of the spreading function) 3) above the PCM quantization noise floor */ if (E>.1*bandwidth_mask && E*1e9f > maxE && E > noise_floor*(band_end-band_start)) bandwidth = b; } if (tonal->count<=2) bandwidth = 20; frame_loudness = 20*(float)log10(frame_loudness); tonal->Etracker = MAX32(tonal->Etracker-.03f, frame_loudness); tonal->lowECount *= (1-alphaE); if (frame_loudness < tonal->Etracker-30) tonal->lowECount += alphaE; for (i=0;i<8;i++) { float sum=0; for (b=0;b<16;b++) sum += dct_table[i*16+b]*logE[b]; BFCC[i] = sum; } frame_stationarity /= NB_TBANDS; relativeE /= NB_TBANDS; if (tonal->count<10) relativeE = .5; frame_noisiness /= NB_TBANDS; #if 1 info->activity = frame_noisiness + (1-frame_noisiness)*relativeE; #else info->activity = .5*(1+frame_noisiness-frame_stationarity); #endif frame_tonality = (max_frame_tonality/(NB_TBANDS-NB_TONAL_SKIP_BANDS)); frame_tonality = MAX16(frame_tonality, tonal->prev_tonality*.8f); tonal->prev_tonality = frame_tonality; slope /= 8*8; info->tonality_slope = slope; tonal->E_count = (tonal->E_count+1)%NB_FRAMES; tonal->count++; info->tonality = frame_tonality; for (i=0;i<4;i++) features[i] = -0.12299f*(BFCC[i]+tonal->mem[i+24]) + 0.49195f*(tonal->mem[i]+tonal->mem[i+16]) + 0.69693f*tonal->mem[i+8] - 1.4349f*tonal->cmean[i]; for (i=0;i<4;i++) tonal->cmean[i] = (1-alpha)*tonal->cmean[i] + alpha*BFCC[i]; for (i=0;i<4;i++) features[4+i] = 0.63246f*(BFCC[i]-tonal->mem[i+24]) + 0.31623f*(tonal->mem[i]-tonal->mem[i+16]); for (i=0;i<3;i++) features[8+i] = 0.53452f*(BFCC[i]+tonal->mem[i+24]) - 0.26726f*(tonal->mem[i]+tonal->mem[i+16]) -0.53452f*tonal->mem[i+8]; if (tonal->count > 5) { for (i=0;i<9;i++) tonal->std[i] = (1-alpha)*tonal->std[i] + alpha*features[i]*features[i]; } for (i=0;i<8;i++) { tonal->mem[i+24] = tonal->mem[i+16]; tonal->mem[i+16] = tonal->mem[i+8]; tonal->mem[i+8] = tonal->mem[i]; tonal->mem[i] = BFCC[i]; } for (i=0;i<9;i++) features[11+i] = (float)sqrt(tonal->std[i]); features[20] = info->tonality; features[21] = info->activity; features[22] = frame_stationarity; features[23] = info->tonality_slope; features[24] = tonal->lowECount; #ifndef DISABLE_FLOAT_API mlp_process(&net, features, frame_probs); frame_probs[0] = .5f*(frame_probs[0]+1); /* Curve fitting between the MLP probability and the actual probability */ frame_probs[0] = .01f + 1.21f*frame_probs[0]*frame_probs[0] - .23f*(float)pow(frame_probs[0], 10); /* Probability of active audio (as opposed to silence) */ frame_probs[1] = .5f*frame_probs[1]+.5f; /* Consider that silence has a 50-50 probability. */ frame_probs[0] = frame_probs[1]*frame_probs[0] + (1-frame_probs[1])*.5f; /*printf("%f %f ", frame_probs[0], frame_probs[1]);*/ { /* Probability of state transition */ float tau; /* Represents independence of the MLP probabilities, where beta=1 means fully independent. */ float beta; /* Denormalized probability of speech (p0) and music (p1) after update */ float p0, p1; /* Probabilities for "all speech" and "all music" */ float s0, m0; /* Probability sum for renormalisation */ float psum; /* Instantaneous probability of speech and music, with beta pre-applied. */ float speech0; float music0; /* One transition every 3 minutes of active audio */ tau = .00005f*frame_probs[1]; beta = .05f; if (1) { /* Adapt beta based on how "unexpected" the new prob is */ float p, q; p = MAX16(.05f,MIN16(.95f,frame_probs[0])); q = MAX16(.05f,MIN16(.95f,tonal->music_prob)); beta = .01f+.05f*ABS16(p-q)/(p*(1-q)+q*(1-p)); } /* p0 and p1 are the probabilities of speech and music at this frame using only information from previous frame and applying the state transition model */ p0 = (1-tonal->music_prob)*(1-tau) + tonal->music_prob *tau; p1 = tonal->music_prob *(1-tau) + (1-tonal->music_prob)*tau; /* We apply the current probability with exponent beta to work around the fact that the probability estimates aren't independent. */ p0 *= (float)pow(1-frame_probs[0], beta); p1 *= (float)pow(frame_probs[0], beta); /* Normalise the probabilities to get the Marokv probability of music. */ tonal->music_prob = p1/(p0+p1); info->music_prob = tonal->music_prob; /* This chunk of code deals with delayed decision. */ psum=1e-20f; /* Instantaneous probability of speech and music, with beta pre-applied. */ speech0 = (float)pow(1-frame_probs[0], beta); music0 = (float)pow(frame_probs[0], beta); if (tonal->count==1) { tonal->pspeech[0]=.5; tonal->pmusic [0]=.5; } /* Updated probability of having only speech (s0) or only music (m0), before considering the new observation. */ s0 = tonal->pspeech[0] + tonal->pspeech[1]; m0 = tonal->pmusic [0] + tonal->pmusic [1]; /* Updates s0 and m0 with instantaneous probability. */ tonal->pspeech[0] = s0*(1-tau)*speech0; tonal->pmusic [0] = m0*(1-tau)*music0; /* Propagate the transition probabilities */ for (i=1;ipspeech[i] = tonal->pspeech[i+1]*speech0; tonal->pmusic [i] = tonal->pmusic [i+1]*music0; } /* Probability that the latest frame is speech, when all the previous ones were music. */ tonal->pspeech[DETECT_SIZE-1] = m0*tau*speech0; /* Probability that the latest frame is music, when all the previous ones were speech. */ tonal->pmusic [DETECT_SIZE-1] = s0*tau*music0; /* Renormalise probabilities to 1 */ for (i=0;ipspeech[i] + tonal->pmusic[i]; psum = 1.f/psum; for (i=0;ipspeech[i] *= psum; tonal->pmusic [i] *= psum; } psum = tonal->pmusic[0]; for (i=1;ipspeech[i]; /* Estimate our confidence in the speech/music decisions */ if (frame_probs[1]>.75) { if (tonal->music_prob>.9) { float adapt; adapt = 1.f/(++tonal->music_confidence_count); tonal->music_confidence_count = IMIN(tonal->music_confidence_count, 500); tonal->music_confidence += adapt*MAX16(-.2f,frame_probs[0]-tonal->music_confidence); } if (tonal->music_prob<.1) { float adapt; adapt = 1.f/(++tonal->speech_confidence_count); tonal->speech_confidence_count = IMIN(tonal->speech_confidence_count, 500); tonal->speech_confidence += adapt*MIN16(.2f,frame_probs[0]-tonal->speech_confidence); } } else { if (tonal->music_confidence_count==0) tonal->music_confidence = .9f; if (tonal->speech_confidence_count==0) tonal->speech_confidence = .1f; } } if (tonal->last_music != (tonal->music_prob>.5f)) tonal->last_transition=0; tonal->last_music = tonal->music_prob>.5f; #else info->music_prob = 0; #endif /*for (i=0;i<25;i++) printf("%f ", features[i]); printf("\n");*/ info->bandwidth = bandwidth; /*printf("%d %d\n", info->bandwidth, info->opus_bandwidth);*/ info->noisiness = frame_noisiness; info->valid = 1; RESTORE_STACK; } void run_analysis(TonalityAnalysisState *analysis, const CELTMode *celt_mode, const void *analysis_pcm, int analysis_frame_size, int frame_size, int c1, int c2, int C, opus_int32 Fs, int lsb_depth, downmix_func downmix, AnalysisInfo *analysis_info) { int offset; int pcm_len; if (analysis_pcm != NULL) { /* Avoid overflow/wrap-around of the analysis buffer */ analysis_frame_size = IMIN((DETECT_SIZE-5)*Fs/100, analysis_frame_size); pcm_len = analysis_frame_size - analysis->analysis_offset; offset = analysis->analysis_offset; do { tonality_analysis(analysis, celt_mode, analysis_pcm, IMIN(480, pcm_len), offset, c1, c2, C, lsb_depth, downmix); offset += 480; pcm_len -= 480; } while (pcm_len>0); analysis->analysis_offset = analysis_frame_size; analysis->analysis_offset -= frame_size; } analysis_info->valid = 0; tonality_get_info(analysis, analysis_info, frame_size); } ================================================ FILE: deps/pjsip/third_party/opus/src/analysis.h ================================================ /* Copyright (c) 2011 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef ANALYSIS_H #define ANALYSIS_H #include "celt.h" #include "opus_private.h" #define NB_FRAMES 8 #define NB_TBANDS 18 #define NB_TOT_BANDS 21 #define ANALYSIS_BUF_SIZE 720 /* 15 ms at 48 kHz */ #define DETECT_SIZE 200 typedef struct { int arch; #define TONALITY_ANALYSIS_RESET_START angle float angle[240]; float d_angle[240]; float d2_angle[240]; opus_val32 inmem[ANALYSIS_BUF_SIZE]; int mem_fill; /* number of usable samples in the buffer */ float prev_band_tonality[NB_TBANDS]; float prev_tonality; float E[NB_FRAMES][NB_TBANDS]; float lowE[NB_TBANDS]; float highE[NB_TBANDS]; float meanE[NB_TOT_BANDS]; float mem[32]; float cmean[8]; float std[9]; float music_prob; float Etracker; float lowECount; int E_count; int last_music; int last_transition; int count; float subframe_mem[3]; int analysis_offset; /** Probability of having speech for time i to DETECT_SIZE-1 (and music before). pspeech[0] is the probability that all frames in the window are speech. */ float pspeech[DETECT_SIZE]; /** Probability of having music for time i to DETECT_SIZE-1 (and speech before). pmusic[0] is the probability that all frames in the window are music. */ float pmusic[DETECT_SIZE]; float speech_confidence; float music_confidence; int speech_confidence_count; int music_confidence_count; int write_pos; int read_pos; int read_subframe; AnalysisInfo info[DETECT_SIZE]; } TonalityAnalysisState; /** Initialize a TonalityAnalysisState struct. * * This performs some possibly slow initialization steps which should * not be repeated every analysis step. No allocated memory is retained * by the state struct, so no cleanup call is required. */ void tonality_analysis_init(TonalityAnalysisState *analysis); /** Reset a TonalityAnalysisState stuct. * * Call this when there's a discontinuity in the data. */ void tonality_analysis_reset(TonalityAnalysisState *analysis); void tonality_get_info(TonalityAnalysisState *tonal, AnalysisInfo *info_out, int len); void run_analysis(TonalityAnalysisState *analysis, const CELTMode *celt_mode, const void *analysis_pcm, int analysis_frame_size, int frame_size, int c1, int c2, int C, opus_int32 Fs, int lsb_depth, downmix_func downmix, AnalysisInfo *analysis_info); #endif ================================================ FILE: deps/pjsip/third_party/opus/src/mlp.c ================================================ /* Copyright (c) 2008-2011 Octasic Inc. Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "opus_types.h" #include "opus_defines.h" #include #include "mlp.h" #include "arch.h" #include "tansig_table.h" #define MAX_NEURONS 100 #if 0 static OPUS_INLINE opus_val16 tansig_approx(opus_val32 _x) /* Q19 */ { int i; opus_val16 xx; /* Q11 */ /*double x, y;*/ opus_val16 dy, yy; /* Q14 */ /*x = 1.9073e-06*_x;*/ if (_x>=QCONST32(8,19)) return QCONST32(1.,14); if (_x<=-QCONST32(8,19)) return -QCONST32(1.,14); xx = EXTRACT16(SHR32(_x, 8)); /*i = lrint(25*x);*/ i = SHR32(ADD32(1024,MULT16_16(25, xx)),11); /*x -= .04*i;*/ xx -= EXTRACT16(SHR32(MULT16_16(20972,i),8)); /*x = xx*(1./2048);*/ /*y = tansig_table[250+i];*/ yy = tansig_table[250+i]; /*y = yy*(1./16384);*/ dy = 16384-MULT16_16_Q14(yy,yy); yy = yy + MULT16_16_Q14(MULT16_16_Q11(xx,dy),(16384 - MULT16_16_Q11(yy,xx))); return yy; } #else /*extern const float tansig_table[501];*/ static OPUS_INLINE float tansig_approx(float x) { int i; float y, dy; float sign=1; /* Tests are reversed to catch NaNs */ if (!(x<8)) return 1; if (!(x>-8)) return -1; #ifndef FIXED_POINT /* Another check in case of -ffast-math */ if (celt_isnan(x)) return 0; #endif if (x<0) { x=-x; sign=-1; } i = (int)floor(.5f+25*x); x -= .04f*i; y = tansig_table[i]; dy = 1-y*y; y = y + x*dy*(1 - y*x); return sign*y; } #endif #if 0 void mlp_process(const MLP *m, const opus_val16 *in, opus_val16 *out) { int j; opus_val16 hidden[MAX_NEURONS]; const opus_val16 *W = m->weights; /* Copy to tmp_in */ for (j=0;jtopo[1];j++) { int k; opus_val32 sum = SHL32(EXTEND32(*W++),8); for (k=0;ktopo[0];k++) sum = MAC16_16(sum, in[k],*W++); hidden[j] = tansig_approx(sum); } for (j=0;jtopo[2];j++) { int k; opus_val32 sum = SHL32(EXTEND32(*W++),14); for (k=0;ktopo[1];k++) sum = MAC16_16(sum, hidden[k], *W++); out[j] = tansig_approx(EXTRACT16(PSHR32(sum,17))); } } #else void mlp_process(const MLP *m, const float *in, float *out) { int j; float hidden[MAX_NEURONS]; const float *W = m->weights; /* Copy to tmp_in */ for (j=0;jtopo[1];j++) { int k; float sum = *W++; for (k=0;ktopo[0];k++) sum = sum + in[k]**W++; hidden[j] = tansig_approx(sum); } for (j=0;jtopo[2];j++) { int k; float sum = *W++; for (k=0;ktopo[1];k++) sum = sum + hidden[k]**W++; out[j] = tansig_approx(sum); } } #endif ================================================ FILE: deps/pjsip/third_party/opus/src/mlp.h ================================================ /* Copyright (c) 2008-2011 Octasic Inc. Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _MLP_H_ #define _MLP_H_ #include "arch.h" typedef struct { int layers; const int *topo; const float *weights; } MLP; extern const MLP net; void mlp_process(const MLP *m, const float *in, float *out); #endif /* _MLP_H_ */ ================================================ FILE: deps/pjsip/third_party/opus/src/mlp_data.c ================================================ /* The contents of this file was automatically generated by mlp_train.c It contains multi-layer perceptron (MLP) weights. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mlp.h" /* RMS error was 0.138320, seed was 1361535663 */ static const float weights[422] = { /* hidden layer */ -0.0941125f, -0.302976f, -0.603555f, -0.19393f, -0.185983f, -0.601617f, -0.0465317f, -0.114563f, -0.103599f, -0.618938f, -0.317859f, -0.169949f, -0.0702885f, 0.148065f, 0.409524f, 0.548432f, 0.367649f, -0.494393f, 0.764306f, -1.83957f, 0.170849f, 12.786f, -1.08848f, -1.27284f, -16.2606f, 24.1773f, -5.57454f, -0.17276f, -0.163388f, -0.224421f, -0.0948944f, -0.0728695f, -0.26557f, -0.100283f, -0.0515459f, -0.146142f, -0.120674f, -0.180655f, 0.12857f, 0.442138f, -0.493735f, 0.167767f, 0.206699f, -0.197567f, 0.417999f, 1.50364f, -0.773341f, -10.0401f, 0.401872f, 2.97966f, 15.2165f, -1.88905f, -1.19254f, 0.0285397f, -0.00405139f, 0.0707565f, 0.00825699f, -0.0927269f, -0.010393f, -0.00428882f, -0.00489743f, -0.0709731f, -0.00255992f, 0.0395619f, 0.226424f, 0.0325231f, 0.162175f, -0.100118f, 0.485789f, 0.12697f, 0.285937f, 0.0155637f, 0.10546f, 3.05558f, 1.15059f, -1.00904f, -1.83088f, 3.31766f, -3.42516f, -0.119135f, -0.0405654f, 0.00690068f, 0.0179877f, -0.0382487f, 0.00597941f, -0.0183611f, 0.00190395f, -0.144322f, -0.0435671f, 0.000990594f, 0.221087f, 0.142405f, 0.484066f, 0.404395f, 0.511955f, -0.237255f, 0.241742f, 0.35045f, -0.699428f, 10.3993f, 2.6507f, -2.43459f, -4.18838f, 1.05928f, 1.71067f, 0.00667811f, -0.0721335f, -0.0397346f, 0.0362704f, -0.11496f, -0.0235776f, 0.0082161f, -0.0141741f, -0.0329699f, -0.0354253f, 0.00277404f, -0.290654f, -1.14767f, -0.319157f, -0.686544f, 0.36897f, 0.478899f, 0.182579f, -0.411069f, 0.881104f, -4.60683f, 1.4697f, 0.335845f, -1.81905f, -30.1699f, 5.55225f, 0.0019508f, -0.123576f, -0.0727332f, -0.0641597f, -0.0534458f, -0.108166f, -0.0937368f, -0.0697883f, -0.0275475f, -0.192309f, -0.110074f, 0.285375f, -0.405597f, 0.0926724f, -0.287881f, -0.851193f, -0.099493f, -0.233764f, -1.2852f, 1.13611f, 3.12168f, -0.0699f, -1.86216f, 2.65292f, -7.31036f, 2.44776f, -0.00111802f, -0.0632786f, -0.0376296f, -0.149851f, 0.142963f, 0.184368f, 0.123433f, 0.0756158f, 0.117312f, 0.0933395f, 0.0692163f, 0.0842592f, 0.0704683f, 0.0589963f, 0.0942205f, -0.448862f, 0.0262677f, 0.270352f, -0.262317f, 0.172586f, 2.00227f, -0.159216f, 0.038422f, 10.2073f, 4.15536f, -2.3407f, -0.0550265f, 0.00964792f, -0.141336f, 0.0274501f, 0.0343921f, -0.0487428f, 0.0950172f, -0.00775017f, -0.0372492f, -0.00548121f, -0.0663695f, 0.0960506f, -0.200008f, -0.0412827f, 0.58728f, 0.0515787f, 0.337254f, 0.855024f, 0.668371f, -0.114904f, -3.62962f, -0.467477f, -0.215472f, 2.61537f, 0.406117f, -1.36373f, 0.0425394f, 0.12208f, 0.0934502f, 0.123055f, 0.0340935f, -0.142466f, 0.035037f, -0.0490666f, 0.0733208f, 0.0576672f, 0.123984f, -0.0517194f, -0.253018f, 0.590565f, 0.145849f, 0.315185f, 0.221534f, -0.149081f, 0.216161f, -0.349575f, 24.5664f, -0.994196f, 0.614289f, -18.7905f, -2.83277f, -0.716801f, -0.347201f, 0.479515f, -0.246027f, 0.0758683f, 0.137293f, -0.17781f, 0.118751f, -0.00108329f, -0.237334f, 0.355732f, -0.12991f, -0.0547627f, -0.318576f, -0.325524f, 0.180494f, -0.0625604f, 0.141219f, 0.344064f, 0.37658f, -0.591772f, 5.8427f, -0.38075f, 0.221894f, -1.41934f, -1.87943e+06f, 1.34114f, 0.0283355f, -0.0447856f, -0.0211466f, -0.0256927f, 0.0139618f, 0.0207934f, -0.0107666f, 0.0110969f, 0.0586069f, -0.0253545f, -0.0328433f, 0.11872f, -0.216943f, 0.145748f, 0.119808f, -0.0915211f, -0.120647f, -0.0787719f, -0.143644f, -0.595116f, -1.152f, -1.25335f, -1.17092f, 4.34023f, -975268.f, -1.37033f, -0.0401123f, 0.210602f, -0.136656f, 0.135962f, -0.0523293f, 0.0444604f, 0.0143928f, 0.00412666f, -0.0193003f, 0.218452f, -0.110204f, -2.02563f, 0.918238f, -2.45362f, 1.19542f, -0.061362f, -1.92243f, 0.308111f, 0.49764f, 0.912356f, 0.209272f, -2.34525f, 2.19326f, -6.47121f, 1.69771f, -0.725123f, 0.0118929f, 0.0377944f, 0.0554003f, 0.0226452f, -0.0704421f, -0.0300309f, 0.0122978f, -0.0041782f, -0.0686612f, 0.0313115f, 0.039111f, 0.364111f, -0.0945548f, 0.0229876f, -0.17414f, 0.329795f, 0.114714f, 0.30022f, 0.106997f, 0.132355f, 5.79932f, 0.908058f, -0.905324f, -3.3561f, 0.190647f, 0.184211f, -0.673648f, 0.231807f, -0.0586222f, 0.230752f, -0.438277f, 0.245857f, -0.17215f, 0.0876383f, -0.720512f, 0.162515f, 0.0170571f, 0.101781f, 0.388477f, 1.32931f, 1.08548f, -0.936301f, -2.36958f, -6.71988f, -3.44376f, 2.13818f, 14.2318f, 4.91459f, -3.09052f, -9.69191f, -0.768234f, 1.79604f, 0.0549653f, 0.163399f, 0.0797025f, 0.0343933f, -0.0555876f, -0.00505673f, 0.0187258f, 0.0326628f, 0.0231486f, 0.15573f, 0.0476223f, -0.254824f, 1.60155f, -0.801221f, 2.55496f, 0.737629f, -1.36249f, -0.695463f, -2.44301f, -1.73188f, 3.95279f, 1.89068f, 0.486087f, -11.3343f, 3.9416e+06f, /* output layer */ -0.381439f, 0.12115f, -0.906927f, 2.93878f, 1.6388f, 0.882811f, 0.874344f, 1.21726f, -0.874545f, 0.321706f, 0.785055f, 0.946558f, -0.575066f, -3.46553f, 0.884905f, 0.0924047f, -9.90712f, 0.391338f, 0.160103f, -2.04954f, 4.1455f, 0.0684029f, -0.144761f, -0.285282f, 0.379244f, -1.1584f, -0.0277241f, -9.85f, -4.82386f, 3.71333f, 3.87308f, 3.52558f}; static const int topo[3] = {25, 15, 2}; const MLP net = { 3, topo, weights }; ================================================ FILE: deps/pjsip/third_party/opus/src/opus.c ================================================ /* Copyright (c) 2011 Xiph.Org Foundation, Skype Limited Written by Jean-Marc Valin and Koen Vos */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "opus.h" #include "opus_private.h" #ifndef DISABLE_FLOAT_API OPUS_EXPORT void opus_pcm_soft_clip(float *_x, int N, int C, float *declip_mem) { int c; int i; float *x; if (C<1 || N<1 || !_x || !declip_mem) return; /* First thing: saturate everything to +/- 2 which is the highest level our non-linearity can handle. At the point where the signal reaches +/-2, the derivative will be zero anyway, so this doesn't introduce any discontinuity in the derivative. */ for (i=0;i=0) break; x[i*C] = x[i*C]+a*x[i*C]*x[i*C]; } curr=0; x0 = x[0]; while(1) { int start, end; float maxval; int special=0; int peak_pos; for (i=curr;i1 || x[i*C]<-1) break; } if (i==N) { a=0; break; } peak_pos = i; start=end=i; maxval=ABS16(x[i*C]); /* Look for first zero crossing before clipping */ while (start>0 && x[i*C]*x[(start-1)*C]>=0) start--; /* Look for first zero crossing after clipping */ while (end=0) { /* Look for other peaks until the next zero-crossing. */ if (ABS16(x[end*C])>maxval) { maxval = ABS16(x[end*C]); peak_pos = end; } end++; } /* Detect the special case where we clip before the first zero crossing */ special = (start==0 && x[i*C]*x[0]>=0); /* Compute a such that maxval + a*maxval^2 = 1 */ a=(maxval-1)/(maxval*maxval); if (x[i*C]>0) a = -a; /* Apply soft clipping */ for (i=start;i=2) { /* Add a linear ramp from the first sample to the signal peak. This avoids a discontinuity at the beginning of the frame. */ float delta; float offset = x0-x[0]; delta = offset / peak_pos; for (i=curr;i>2; return 2; } } static int parse_size(const unsigned char *data, opus_int32 len, opus_int16 *size) { if (len<1) { *size = -1; return -1; } else if (data[0]<252) { *size = data[0]; return 1; } else if (len<2) { *size = -1; return -1; } else { *size = 4*data[1] + data[0]; return 2; } } int opus_packet_get_samples_per_frame(const unsigned char *data, opus_int32 Fs) { int audiosize; if (data[0]&0x80) { audiosize = ((data[0]>>3)&0x3); audiosize = (Fs<>3)&0x3); if (audiosize == 3) audiosize = Fs*60/1000; else audiosize = (Fs< len) return OPUS_INVALID_PACKET; data += bytes; last_size = len-size[0]; break; /* Multiple CBR/VBR frames (from 0 to 120 ms) */ default: /*case 3:*/ if (len<1) return OPUS_INVALID_PACKET; /* Number of frames encoded in bits 0 to 5 */ ch = *data++; count = ch&0x3F; if (count <= 0 || framesize*count > 5760) return OPUS_INVALID_PACKET; len--; /* Padding flag is bit 6 */ if (ch&0x40) { int p; do { int tmp; if (len<=0) return OPUS_INVALID_PACKET; p = *data++; len--; tmp = p==255 ? 254: p; len -= tmp; pad += tmp; } while (p==255); } if (len<0) return OPUS_INVALID_PACKET; /* VBR flag is bit 7 */ cbr = !(ch&0x80); if (!cbr) { /* VBR case */ last_size = len; for (i=0;i len) return OPUS_INVALID_PACKET; data += bytes; last_size -= bytes+size[i]; } if (last_size<0) return OPUS_INVALID_PACKET; } else if (!self_delimited) { /* CBR case */ last_size = len/count; if (last_size*count!=len) return OPUS_INVALID_PACKET; for (i=0;i len) return OPUS_INVALID_PACKET; data += bytes; /* For CBR packets, apply the size to all the frames. */ if (cbr) { if (size[count-1]*count > len) return OPUS_INVALID_PACKET; for (i=0;i last_size) return OPUS_INVALID_PACKET; } else { /* Because it's not encoded explicitly, it's possible the size of the last packet (or all the packets, for the CBR case) is larger than 1275. Reject them here.*/ if (last_size > 1275) return OPUS_INVALID_PACKET; size[count-1] = (opus_int16)last_size; } if (payload_offset) *payload_offset = (int)(data-data0); for (i=0;i #include #include #include #define OPUS_PI (3.14159265F) #define OPUS_COSF(_x) ((float)cos(_x)) #define OPUS_SINF(_x) ((float)sin(_x)) static void *check_alloc(void *_ptr){ if(_ptr==NULL){ fprintf(stderr,"Out of memory.\n"); exit(EXIT_FAILURE); } return _ptr; } static void *opus_malloc(size_t _size){ return check_alloc(malloc(_size)); } static void *opus_realloc(void *_ptr,size_t _size){ return check_alloc(realloc(_ptr,_size)); } static size_t read_pcm16(float **_samples,FILE *_fin,int _nchannels){ unsigned char buf[1024]; float *samples; size_t nsamples; size_t csamples; size_t xi; size_t nread; samples=NULL; nsamples=csamples=0; for(;;){ nread=fread(buf,2*_nchannels,1024/(2*_nchannels),_fin); if(nread<=0)break; if(nsamples+nread>csamples){ do csamples=csamples<<1|1; while(nsamples+nread>csamples); samples=(float *)opus_realloc(samples, _nchannels*csamples*sizeof(*samples)); } for(xi=0;xi=_window_sz)ti-=_window_sz; } re*=_downsample; im*=_downsample; _ps[(xi*ps_sz+xj)*_nchannels+ci]=re*re+im*im+100000; p[ci]+=_ps[(xi*ps_sz+xj)*_nchannels+ci]; } } if(_out){ _out[(xi*_nbands+bi)*_nchannels]=p[0]/(_bands[bi+1]-_bands[bi]); if(_nchannels==2){ _out[(xi*_nbands+bi)*_nchannels+1]=p[1]/(_bands[bi+1]-_bands[bi]); } } } } free(window); } #define NBANDS (21) #define NFREQS (240) /*Bands on which we compute the pseudo-NMR (Bark-derived CELT bands).*/ static const int BANDS[NBANDS+1]={ 0,2,4,6,8,10,12,14,16,20,24,28,32,40,48,56,68,80,96,120,156,200 }; #define TEST_WIN_SIZE (480) #define TEST_WIN_STEP (120) int main(int _argc,const char **_argv){ FILE *fin1; FILE *fin2; float *x; float *y; float *xb; float *X; float *Y; double err; float Q; size_t xlength; size_t ylength; size_t nframes; size_t xi; int ci; int xj; int bi; int nchannels; unsigned rate; int downsample; int ybands; int yfreqs; int max_compare; if(_argc<3||_argc>6){ fprintf(stderr,"Usage: %s [-s] [-r rate2] \n", _argv[0]); return EXIT_FAILURE; } nchannels=1; if(strcmp(_argv[1],"-s")==0){ nchannels=2; _argv++; } rate=48000; ybands=NBANDS; yfreqs=NFREQS; downsample=1; if(strcmp(_argv[1],"-r")==0){ rate=atoi(_argv[2]); if(rate!=8000&&rate!=12000&&rate!=16000&&rate!=24000&&rate!=48000){ fprintf(stderr, "Sampling rate must be 8000, 12000, 16000, 24000, or 48000\n"); return EXIT_FAILURE; } downsample=48000/rate; switch(rate){ case 8000:ybands=13;break; case 12000:ybands=15;break; case 16000:ybands=17;break; case 24000:ybands=19;break; } yfreqs=NFREQS/downsample; _argv+=2; } fin1=fopen(_argv[1],"rb"); if(fin1==NULL){ fprintf(stderr,"Error opening '%s'.\n",_argv[1]); return EXIT_FAILURE; } fin2=fopen(_argv[2],"rb"); if(fin2==NULL){ fprintf(stderr,"Error opening '%s'.\n",_argv[2]); fclose(fin1); return EXIT_FAILURE; } /*Read in the data and allocate scratch space.*/ xlength=read_pcm16(&x,fin1,2); if(nchannels==1){ for(xi=0;xi0;){ for(ci=0;ci0){ /*Temporal masking: -3 dB/2.5ms slope.*/ for(bi=0;bi=79&&xj<=81)im*=0.1F; if(xj==80)im*=0.1F; Eb+=im; } } Eb /= (BANDS[bi+1]-BANDS[bi])*nchannels; Ef += Eb*Eb; } /*Using a fixed normalization value means we're willing to accept slightly lower quality for lower sampling rates.*/ Ef/=NBANDS; Ef*=Ef; err+=Ef*Ef; } err=pow(err/nframes,1.0/16); Q=100*(1-0.5*log(1+err)/log(1.13)); if(Q<0){ fprintf(stderr,"Test vector FAILS\n"); fprintf(stderr,"Internal weighted error is %f\n",err); return EXIT_FAILURE; } else{ fprintf(stderr,"Test vector PASSES\n"); fprintf(stderr, "Opus quality metric: %.1f %% (internal weighted error is %f)\n",Q,err); return EXIT_SUCCESS; } } ================================================ FILE: deps/pjsip/third_party/opus/src/opus_decoder.c ================================================ /* Copyright (c) 2010 Xiph.Org Foundation, Skype Limited Written by Jean-Marc Valin and Koen Vos */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifndef OPUS_BUILD # error "OPUS_BUILD _MUST_ be defined to build Opus. This probably means you need other defines as well, as in a config.h. See the included build files for details." #endif #if defined(__GNUC__) && (__GNUC__ >= 2) && !defined(__OPTIMIZE__) && !defined(OPUS_WILL_BE_SLOW) # pragma message "You appear to be compiling without optimization, if so opus will be very slow." #endif #include #include "celt.h" #include "opus.h" #include "entdec.h" #include "modes.h" #include "API.h" #include "stack_alloc.h" #include "float_cast.h" #include "opus_private.h" #include "os_support.h" #include "structs.h" #include "define.h" #include "mathops.h" #include "cpu_support.h" struct OpusDecoder { int celt_dec_offset; int silk_dec_offset; int channels; opus_int32 Fs; /** Sampling rate (at the API level) */ silk_DecControlStruct DecControl; int decode_gain; int arch; /* Everything beyond this point gets cleared on a reset */ #define OPUS_DECODER_RESET_START stream_channels int stream_channels; int bandwidth; int mode; int prev_mode; int frame_size; int prev_redundancy; int last_packet_duration; #ifndef FIXED_POINT opus_val16 softclip_mem[2]; #endif opus_uint32 rangeFinal; }; int opus_decoder_get_size(int channels) { int silkDecSizeBytes, celtDecSizeBytes; int ret; if (channels<1 || channels > 2) return 0; ret = silk_Get_Decoder_Size( &silkDecSizeBytes ); if(ret) return 0; silkDecSizeBytes = align(silkDecSizeBytes); celtDecSizeBytes = celt_decoder_get_size(channels); return align(sizeof(OpusDecoder))+silkDecSizeBytes+celtDecSizeBytes; } int opus_decoder_init(OpusDecoder *st, opus_int32 Fs, int channels) { void *silk_dec; CELTDecoder *celt_dec; int ret, silkDecSizeBytes; if ((Fs!=48000&&Fs!=24000&&Fs!=16000&&Fs!=12000&&Fs!=8000) || (channels!=1&&channels!=2)) return OPUS_BAD_ARG; OPUS_CLEAR((char*)st, opus_decoder_get_size(channels)); /* Initialize SILK encoder */ ret = silk_Get_Decoder_Size(&silkDecSizeBytes); if (ret) return OPUS_INTERNAL_ERROR; silkDecSizeBytes = align(silkDecSizeBytes); st->silk_dec_offset = align(sizeof(OpusDecoder)); st->celt_dec_offset = st->silk_dec_offset+silkDecSizeBytes; silk_dec = (char*)st+st->silk_dec_offset; celt_dec = (CELTDecoder*)((char*)st+st->celt_dec_offset); st->stream_channels = st->channels = channels; st->Fs = Fs; st->DecControl.API_sampleRate = st->Fs; st->DecControl.nChannelsAPI = st->channels; /* Reset decoder */ ret = silk_InitDecoder( silk_dec ); if(ret)return OPUS_INTERNAL_ERROR; /* Initialize CELT decoder */ ret = celt_decoder_init(celt_dec, Fs, channels); if(ret!=OPUS_OK)return OPUS_INTERNAL_ERROR; celt_decoder_ctl(celt_dec, CELT_SET_SIGNALLING(0)); st->prev_mode = 0; st->frame_size = Fs/400; st->arch = opus_select_arch(); return OPUS_OK; } OpusDecoder *opus_decoder_create(opus_int32 Fs, int channels, int *error) { int ret; OpusDecoder *st; if ((Fs!=48000&&Fs!=24000&&Fs!=16000&&Fs!=12000&&Fs!=8000) || (channels!=1&&channels!=2)) { if (error) *error = OPUS_BAD_ARG; return NULL; } st = (OpusDecoder *)opus_alloc(opus_decoder_get_size(channels)); if (st == NULL) { if (error) *error = OPUS_ALLOC_FAIL; return NULL; } ret = opus_decoder_init(st, Fs, channels); if (error) *error = ret; if (ret != OPUS_OK) { opus_free(st); st = NULL; } return st; } static void smooth_fade(const opus_val16 *in1, const opus_val16 *in2, opus_val16 *out, int overlap, int channels, const opus_val16 *window, opus_int32 Fs) { int i, c; int inc = 48000/Fs; for (c=0;csilk_dec_offset; celt_dec = (CELTDecoder*)((char*)st+st->celt_dec_offset); F20 = st->Fs/50; F10 = F20>>1; F5 = F10>>1; F2_5 = F5>>1; if (frame_size < F2_5) { RESTORE_STACK; return OPUS_BUFFER_TOO_SMALL; } /* Limit frame_size to avoid excessive stack allocations. */ frame_size = IMIN(frame_size, st->Fs/25*3); /* Payloads of 1 (2 including ToC) or 0 trigger the PLC/DTX */ if (len<=1) { data = NULL; /* In that case, don't conceal more than what the ToC says */ frame_size = IMIN(frame_size, st->frame_size); } if (data != NULL) { audiosize = st->frame_size; mode = st->mode; ec_dec_init(&dec,(unsigned char*)data,len); } else { audiosize = frame_size; mode = st->prev_mode; if (mode == 0) { /* If we haven't got any packet yet, all we can do is return zeros */ for (i=0;ichannels;i++) pcm[i] = 0; RESTORE_STACK; return audiosize; } /* Avoids trying to run the PLC on sizes other than 2.5 (CELT), 5 (CELT), 10, or 20 (e.g. 12.5 or 30 ms). */ if (audiosize > F20) { do { int ret = opus_decode_frame(st, NULL, 0, pcm, IMIN(audiosize, F20), 0); if (ret<0) { RESTORE_STACK; return ret; } pcm += ret*st->channels; audiosize -= ret; } while (audiosize > 0); RESTORE_STACK; return frame_size; } else if (audiosize < F20) { if (audiosize > F10) audiosize = F10; else if (mode != MODE_SILK_ONLY && audiosize > F5 && audiosize < F10) audiosize = F5; } } /* In fixed-point, we can tell CELT to do the accumulation on top of the SILK PCM buffer. This saves some stack space. */ #ifdef FIXED_POINT celt_accum = (mode != MODE_CELT_ONLY) && (frame_size >= F10); #else celt_accum = 0; #endif pcm_transition_silk_size = ALLOC_NONE; pcm_transition_celt_size = ALLOC_NONE; if (data!=NULL && st->prev_mode > 0 && ( (mode == MODE_CELT_ONLY && st->prev_mode != MODE_CELT_ONLY && !st->prev_redundancy) || (mode != MODE_CELT_ONLY && st->prev_mode == MODE_CELT_ONLY) ) ) { transition = 1; /* Decide where to allocate the stack memory for pcm_transition */ if (mode == MODE_CELT_ONLY) pcm_transition_celt_size = F5*st->channels; else pcm_transition_silk_size = F5*st->channels; } ALLOC(pcm_transition_celt, pcm_transition_celt_size, opus_val16); if (transition && mode == MODE_CELT_ONLY) { pcm_transition = pcm_transition_celt; opus_decode_frame(st, NULL, 0, pcm_transition, IMIN(F5, audiosize), 0); } if (audiosize > frame_size) { /*fprintf(stderr, "PCM buffer too small: %d vs %d (mode = %d)\n", audiosize, frame_size, mode);*/ RESTORE_STACK; return OPUS_BAD_ARG; } else { frame_size = audiosize; } /* Don't allocate any memory when in CELT-only mode */ pcm_silk_size = (mode != MODE_CELT_ONLY && !celt_accum) ? IMAX(F10, frame_size)*st->channels : ALLOC_NONE; ALLOC(pcm_silk, pcm_silk_size, opus_int16); /* SILK processing */ if (mode != MODE_CELT_ONLY) { int lost_flag, decoded_samples; opus_int16 *pcm_ptr; #ifdef FIXED_POINT if (celt_accum) pcm_ptr = pcm; else #endif pcm_ptr = pcm_silk; if (st->prev_mode==MODE_CELT_ONLY) silk_InitDecoder( silk_dec ); /* The SILK PLC cannot produce frames of less than 10 ms */ st->DecControl.payloadSize_ms = IMAX(10, 1000 * audiosize / st->Fs); if (data != NULL) { st->DecControl.nChannelsInternal = st->stream_channels; if( mode == MODE_SILK_ONLY ) { if( st->bandwidth == OPUS_BANDWIDTH_NARROWBAND ) { st->DecControl.internalSampleRate = 8000; } else if( st->bandwidth == OPUS_BANDWIDTH_MEDIUMBAND ) { st->DecControl.internalSampleRate = 12000; } else if( st->bandwidth == OPUS_BANDWIDTH_WIDEBAND ) { st->DecControl.internalSampleRate = 16000; } else { st->DecControl.internalSampleRate = 16000; silk_assert( 0 ); } } else { /* Hybrid mode */ st->DecControl.internalSampleRate = 16000; } } lost_flag = data == NULL ? 1 : 2 * decode_fec; decoded_samples = 0; do { /* Call SILK decoder */ int first_frame = decoded_samples == 0; silk_ret = silk_Decode( silk_dec, &st->DecControl, lost_flag, first_frame, &dec, pcm_ptr, &silk_frame_size, st->arch ); if( silk_ret ) { if (lost_flag) { /* PLC failure should not be fatal */ silk_frame_size = frame_size; for (i=0;ichannels;i++) pcm_ptr[i] = 0; } else { RESTORE_STACK; return OPUS_INTERNAL_ERROR; } } pcm_ptr += silk_frame_size * st->channels; decoded_samples += silk_frame_size; } while( decoded_samples < frame_size ); } start_band = 0; if (!decode_fec && mode != MODE_CELT_ONLY && data != NULL && ec_tell(&dec)+17+20*(st->mode == MODE_HYBRID) <= 8*len) { /* Check if we have a redundant 0-8 kHz band */ if (mode == MODE_HYBRID) redundancy = ec_dec_bit_logp(&dec, 12); else redundancy = 1; if (redundancy) { celt_to_silk = ec_dec_bit_logp(&dec, 1); /* redundancy_bytes will be at least two, in the non-hybrid case due to the ec_tell() check above */ redundancy_bytes = mode==MODE_HYBRID ? (opus_int32)ec_dec_uint(&dec, 256)+2 : len-((ec_tell(&dec)+7)>>3); len -= redundancy_bytes; /* This is a sanity check. It should never happen for a valid packet, so the exact behaviour is not normative. */ if (len*8 < ec_tell(&dec)) { len = 0; redundancy_bytes = 0; redundancy = 0; } /* Shrink decoder because of raw bits */ dec.storage -= redundancy_bytes; } } if (mode != MODE_CELT_ONLY) start_band = 17; { int endband=21; switch(st->bandwidth) { case OPUS_BANDWIDTH_NARROWBAND: endband = 13; break; case OPUS_BANDWIDTH_MEDIUMBAND: case OPUS_BANDWIDTH_WIDEBAND: endband = 17; break; case OPUS_BANDWIDTH_SUPERWIDEBAND: endband = 19; break; case OPUS_BANDWIDTH_FULLBAND: endband = 21; break; } celt_decoder_ctl(celt_dec, CELT_SET_END_BAND(endband)); celt_decoder_ctl(celt_dec, CELT_SET_CHANNELS(st->stream_channels)); } if (redundancy) { transition = 0; pcm_transition_silk_size=ALLOC_NONE; } ALLOC(pcm_transition_silk, pcm_transition_silk_size, opus_val16); if (transition && mode != MODE_CELT_ONLY) { pcm_transition = pcm_transition_silk; opus_decode_frame(st, NULL, 0, pcm_transition, IMIN(F5, audiosize), 0); } /* Only allocation memory for redundancy if/when needed */ redundant_audio_size = redundancy ? F5*st->channels : ALLOC_NONE; ALLOC(redundant_audio, redundant_audio_size, opus_val16); /* 5 ms redundant frame for CELT->SILK*/ if (redundancy && celt_to_silk) { celt_decoder_ctl(celt_dec, CELT_SET_START_BAND(0)); celt_decode_with_ec(celt_dec, data+len, redundancy_bytes, redundant_audio, F5, NULL, 0); celt_decoder_ctl(celt_dec, OPUS_GET_FINAL_RANGE(&redundant_rng)); } /* MUST be after PLC */ celt_decoder_ctl(celt_dec, CELT_SET_START_BAND(start_band)); if (mode != MODE_SILK_ONLY) { int celt_frame_size = IMIN(F20, frame_size); /* Make sure to discard any previous CELT state */ if (mode != st->prev_mode && st->prev_mode > 0 && !st->prev_redundancy) celt_decoder_ctl(celt_dec, OPUS_RESET_STATE); /* Decode CELT */ celt_ret = celt_decode_with_ec(celt_dec, decode_fec ? NULL : data, len, pcm, celt_frame_size, &dec, celt_accum); } else { unsigned char silence[2] = {0xFF, 0xFF}; if (!celt_accum) { for (i=0;ichannels;i++) pcm[i] = 0; } /* For hybrid -> SILK transitions, we let the CELT MDCT do a fade-out by decoding a silence frame */ if (st->prev_mode == MODE_HYBRID && !(redundancy && celt_to_silk && st->prev_redundancy) ) { celt_decoder_ctl(celt_dec, CELT_SET_START_BAND(0)); celt_decode_with_ec(celt_dec, silence, 2, pcm, F2_5, NULL, celt_accum); } } if (mode != MODE_CELT_ONLY && !celt_accum) { #ifdef FIXED_POINT for (i=0;ichannels;i++) pcm[i] = SAT16(ADD32(pcm[i], pcm_silk[i])); #else for (i=0;ichannels;i++) pcm[i] = pcm[i] + (opus_val16)((1.f/32768.f)*pcm_silk[i]); #endif } { const CELTMode *celt_mode; celt_decoder_ctl(celt_dec, CELT_GET_MODE(&celt_mode)); window = celt_mode->window; } /* 5 ms redundant frame for SILK->CELT */ if (redundancy && !celt_to_silk) { celt_decoder_ctl(celt_dec, OPUS_RESET_STATE); celt_decoder_ctl(celt_dec, CELT_SET_START_BAND(0)); celt_decode_with_ec(celt_dec, data+len, redundancy_bytes, redundant_audio, F5, NULL, 0); celt_decoder_ctl(celt_dec, OPUS_GET_FINAL_RANGE(&redundant_rng)); smooth_fade(pcm+st->channels*(frame_size-F2_5), redundant_audio+st->channels*F2_5, pcm+st->channels*(frame_size-F2_5), F2_5, st->channels, window, st->Fs); } if (redundancy && celt_to_silk) { for (c=0;cchannels;c++) { for (i=0;ichannels*i+c] = redundant_audio[st->channels*i+c]; } smooth_fade(redundant_audio+st->channels*F2_5, pcm+st->channels*F2_5, pcm+st->channels*F2_5, F2_5, st->channels, window, st->Fs); } if (transition) { if (audiosize >= F5) { for (i=0;ichannels*F2_5;i++) pcm[i] = pcm_transition[i]; smooth_fade(pcm_transition+st->channels*F2_5, pcm+st->channels*F2_5, pcm+st->channels*F2_5, F2_5, st->channels, window, st->Fs); } else { /* Not enough time to do a clean transition, but we do it anyway This will not preserve amplitude perfectly and may introduce a bit of temporal aliasing, but it shouldn't be too bad and that's pretty much the best we can do. In any case, generating this transition it pretty silly in the first place */ smooth_fade(pcm_transition, pcm, pcm, F2_5, st->channels, window, st->Fs); } } if(st->decode_gain) { opus_val32 gain; gain = celt_exp2(MULT16_16_P15(QCONST16(6.48814081e-4f, 25), st->decode_gain)); for (i=0;ichannels;i++) { opus_val32 x; x = MULT16_32_P16(pcm[i],gain); pcm[i] = SATURATE(x, 32767); } } if (len <= 1) st->rangeFinal = 0; else st->rangeFinal = dec.rng ^ redundant_rng; st->prev_mode = mode; st->prev_redundancy = redundancy && !celt_to_silk; if (celt_ret>=0) { if (OPUS_CHECK_ARRAY(pcm, audiosize*st->channels)) OPUS_PRINT_INT(audiosize); } RESTORE_STACK; return celt_ret < 0 ? celt_ret : audiosize; } int opus_decode_native(OpusDecoder *st, const unsigned char *data, opus_int32 len, opus_val16 *pcm, int frame_size, int decode_fec, int self_delimited, opus_int32 *packet_offset, int soft_clip) { int i, nb_samples; int count, offset; unsigned char toc; int packet_frame_size, packet_bandwidth, packet_mode, packet_stream_channels; /* 48 x 2.5 ms = 120 ms */ opus_int16 size[48]; if (decode_fec<0 || decode_fec>1) return OPUS_BAD_ARG; /* For FEC/PLC, frame_size has to be to have a multiple of 2.5 ms */ if ((decode_fec || len==0 || data==NULL) && frame_size%(st->Fs/400)!=0) return OPUS_BAD_ARG; if (len==0 || data==NULL) { int pcm_count=0; do { int ret; ret = opus_decode_frame(st, NULL, 0, pcm+pcm_count*st->channels, frame_size-pcm_count, 0); if (ret<0) return ret; pcm_count += ret; } while (pcm_count < frame_size); celt_assert(pcm_count == frame_size); if (OPUS_CHECK_ARRAY(pcm, pcm_count*st->channels)) OPUS_PRINT_INT(pcm_count); st->last_packet_duration = pcm_count; return pcm_count; } else if (len<0) return OPUS_BAD_ARG; packet_mode = opus_packet_get_mode(data); packet_bandwidth = opus_packet_get_bandwidth(data); packet_frame_size = opus_packet_get_samples_per_frame(data, st->Fs); packet_stream_channels = opus_packet_get_nb_channels(data); count = opus_packet_parse_impl(data, len, self_delimited, &toc, NULL, size, &offset, packet_offset); if (count<0) return count; data += offset; if (decode_fec) { int duration_copy; int ret; /* If no FEC can be present, run the PLC (recursive call) */ if (frame_size < packet_frame_size || packet_mode == MODE_CELT_ONLY || st->mode == MODE_CELT_ONLY) return opus_decode_native(st, NULL, 0, pcm, frame_size, 0, 0, NULL, soft_clip); /* Otherwise, run the PLC on everything except the size for which we might have FEC */ duration_copy = st->last_packet_duration; if (frame_size-packet_frame_size!=0) { ret = opus_decode_native(st, NULL, 0, pcm, frame_size-packet_frame_size, 0, 0, NULL, soft_clip); if (ret<0) { st->last_packet_duration = duration_copy; return ret; } celt_assert(ret==frame_size-packet_frame_size); } /* Complete with FEC */ st->mode = packet_mode; st->bandwidth = packet_bandwidth; st->frame_size = packet_frame_size; st->stream_channels = packet_stream_channels; ret = opus_decode_frame(st, data, size[0], pcm+st->channels*(frame_size-packet_frame_size), packet_frame_size, 1); if (ret<0) return ret; else { if (OPUS_CHECK_ARRAY(pcm, frame_size*st->channels)) OPUS_PRINT_INT(frame_size); st->last_packet_duration = frame_size; return frame_size; } } if (count*packet_frame_size > frame_size) return OPUS_BUFFER_TOO_SMALL; /* Update the state as the last step to avoid updating it on an invalid packet */ st->mode = packet_mode; st->bandwidth = packet_bandwidth; st->frame_size = packet_frame_size; st->stream_channels = packet_stream_channels; nb_samples=0; for (i=0;ichannels, frame_size-nb_samples, 0); if (ret<0) return ret; celt_assert(ret==packet_frame_size); data += size[i]; nb_samples += ret; } st->last_packet_duration = nb_samples; if (OPUS_CHECK_ARRAY(pcm, nb_samples*st->channels)) OPUS_PRINT_INT(nb_samples); #ifndef FIXED_POINT if (soft_clip) opus_pcm_soft_clip(pcm, nb_samples, st->channels, st->softclip_mem); else st->softclip_mem[0]=st->softclip_mem[1]=0; #endif return nb_samples; } #ifdef FIXED_POINT int opus_decode(OpusDecoder *st, const unsigned char *data, opus_int32 len, opus_val16 *pcm, int frame_size, int decode_fec) { if(frame_size<=0) return OPUS_BAD_ARG; return opus_decode_native(st, data, len, pcm, frame_size, decode_fec, 0, NULL, 0); } #ifndef DISABLE_FLOAT_API int opus_decode_float(OpusDecoder *st, const unsigned char *data, opus_int32 len, float *pcm, int frame_size, int decode_fec) { VARDECL(opus_int16, out); int ret, i; int nb_samples; ALLOC_STACK; if(frame_size<=0) { RESTORE_STACK; return OPUS_BAD_ARG; } if (data != NULL && len > 0 && !decode_fec) { nb_samples = opus_decoder_get_nb_samples(st, data, len); if (nb_samples>0) frame_size = IMIN(frame_size, nb_samples); else return OPUS_INVALID_PACKET; } ALLOC(out, frame_size*st->channels, opus_int16); ret = opus_decode_native(st, data, len, out, frame_size, decode_fec, 0, NULL, 0); if (ret > 0) { for (i=0;ichannels;i++) pcm[i] = (1.f/32768.f)*(out[i]); } RESTORE_STACK; return ret; } #endif #else int opus_decode(OpusDecoder *st, const unsigned char *data, opus_int32 len, opus_int16 *pcm, int frame_size, int decode_fec) { VARDECL(float, out); int ret, i; int nb_samples; ALLOC_STACK; if(frame_size<=0) { RESTORE_STACK; return OPUS_BAD_ARG; } if (data != NULL && len > 0 && !decode_fec) { nb_samples = opus_decoder_get_nb_samples(st, data, len); if (nb_samples>0) frame_size = IMIN(frame_size, nb_samples); else return OPUS_INVALID_PACKET; } ALLOC(out, frame_size*st->channels, float); ret = opus_decode_native(st, data, len, out, frame_size, decode_fec, 0, NULL, 1); if (ret > 0) { for (i=0;ichannels;i++) pcm[i] = FLOAT2INT16(out[i]); } RESTORE_STACK; return ret; } int opus_decode_float(OpusDecoder *st, const unsigned char *data, opus_int32 len, opus_val16 *pcm, int frame_size, int decode_fec) { if(frame_size<=0) return OPUS_BAD_ARG; return opus_decode_native(st, data, len, pcm, frame_size, decode_fec, 0, NULL, 0); } #endif int opus_decoder_ctl(OpusDecoder *st, int request, ...) { int ret = OPUS_OK; va_list ap; void *silk_dec; CELTDecoder *celt_dec; silk_dec = (char*)st+st->silk_dec_offset; celt_dec = (CELTDecoder*)((char*)st+st->celt_dec_offset); va_start(ap, request); switch (request) { case OPUS_GET_BANDWIDTH_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->bandwidth; } break; case OPUS_GET_FINAL_RANGE_REQUEST: { opus_uint32 *value = va_arg(ap, opus_uint32*); if (!value) { goto bad_arg; } *value = st->rangeFinal; } break; case OPUS_RESET_STATE: { OPUS_CLEAR((char*)&st->OPUS_DECODER_RESET_START, sizeof(OpusDecoder)- ((char*)&st->OPUS_DECODER_RESET_START - (char*)st)); celt_decoder_ctl(celt_dec, OPUS_RESET_STATE); silk_InitDecoder( silk_dec ); st->stream_channels = st->channels; st->frame_size = st->Fs/400; } break; case OPUS_GET_SAMPLE_RATE_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->Fs; } break; case OPUS_GET_PITCH_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } if (st->prev_mode == MODE_CELT_ONLY) celt_decoder_ctl(celt_dec, OPUS_GET_PITCH(value)); else *value = st->DecControl.prevPitchLag; } break; case OPUS_GET_GAIN_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->decode_gain; } break; case OPUS_SET_GAIN_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if (value<-32768 || value>32767) { goto bad_arg; } st->decode_gain = value; } break; case OPUS_GET_LAST_PACKET_DURATION_REQUEST: { opus_uint32 *value = va_arg(ap, opus_uint32*); if (!value) { goto bad_arg; } *value = st->last_packet_duration; } break; default: /*fprintf(stderr, "unknown opus_decoder_ctl() request: %d", request);*/ ret = OPUS_UNIMPLEMENTED; break; } va_end(ap); return ret; bad_arg: va_end(ap); return OPUS_BAD_ARG; } void opus_decoder_destroy(OpusDecoder *st) { opus_free(st); } int opus_packet_get_bandwidth(const unsigned char *data) { int bandwidth; if (data[0]&0x80) { bandwidth = OPUS_BANDWIDTH_MEDIUMBAND + ((data[0]>>5)&0x3); if (bandwidth == OPUS_BANDWIDTH_MEDIUMBAND) bandwidth = OPUS_BANDWIDTH_NARROWBAND; } else if ((data[0]&0x60) == 0x60) { bandwidth = (data[0]&0x10) ? OPUS_BANDWIDTH_FULLBAND : OPUS_BANDWIDTH_SUPERWIDEBAND; } else { bandwidth = OPUS_BANDWIDTH_NARROWBAND + ((data[0]>>5)&0x3); } return bandwidth; } int opus_packet_get_nb_channels(const unsigned char *data) { return (data[0]&0x4) ? 2 : 1; } int opus_packet_get_nb_frames(const unsigned char packet[], opus_int32 len) { int count; if (len<1) return OPUS_BAD_ARG; count = packet[0]&0x3; if (count==0) return 1; else if (count!=3) return 2; else if (len<2) return OPUS_INVALID_PACKET; else return packet[1]&0x3F; } int opus_packet_get_nb_samples(const unsigned char packet[], opus_int32 len, opus_int32 Fs) { int samples; int count = opus_packet_get_nb_frames(packet, len); if (count<0) return count; samples = count*opus_packet_get_samples_per_frame(packet, Fs); /* Can't have more than 120 ms */ if (samples*25 > Fs*3) return OPUS_INVALID_PACKET; else return samples; } int opus_decoder_get_nb_samples(const OpusDecoder *dec, const unsigned char packet[], opus_int32 len) { return opus_packet_get_nb_samples(packet, len, dec->Fs); } ================================================ FILE: deps/pjsip/third_party/opus/src/opus_demo.c ================================================ /* Copyright (c) 2007-2008 CSIRO Copyright (c) 2007-2009 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "opus.h" #include "debug.h" #include "opus_types.h" #include "opus_private.h" #include "opus_multistream.h" #define MAX_PACKET 1500 void print_usage( char* argv[] ) { fprintf(stderr, "Usage: %s [-e] " " [options] \n", argv[0]); fprintf(stderr, " %s -d " "[options] \n\n", argv[0]); fprintf(stderr, "application: voip | audio | restricted-lowdelay\n" ); fprintf(stderr, "options:\n" ); fprintf(stderr, "-e : only runs the encoder (output the bit-stream)\n" ); fprintf(stderr, "-d : only runs the decoder (reads the bit-stream as input)\n" ); fprintf(stderr, "-cbr : enable constant bitrate; default: variable bitrate\n" ); fprintf(stderr, "-cvbr : enable constrained variable bitrate; default: unconstrained\n" ); fprintf(stderr, "-variable-duration : enable frames of variable duration (experts only); default: disabled\n" ); fprintf(stderr, "-bandwidth : audio bandwidth (from narrowband to fullband); default: sampling rate\n" ); fprintf(stderr, "-framesize <2.5|5|10|20|40|60> : frame size in ms; default: 20 \n" ); fprintf(stderr, "-max_payload : maximum payload size in bytes, default: 1024\n" ); fprintf(stderr, "-complexity : complexity, 0 (lowest) ... 10 (highest); default: 10\n" ); fprintf(stderr, "-inbandfec : enable SILK inband FEC\n" ); fprintf(stderr, "-forcemono : force mono encoding, even for stereo input\n" ); fprintf(stderr, "-dtx : enable SILK DTX\n" ); fprintf(stderr, "-loss : simulate packet loss, in percent (0-100); default: 0\n" ); } static void int_to_char(opus_uint32 i, unsigned char ch[4]) { ch[0] = i>>24; ch[1] = (i>>16)&0xFF; ch[2] = (i>>8)&0xFF; ch[3] = i&0xFF; } static opus_uint32 char_to_int(unsigned char ch[4]) { return ((opus_uint32)ch[0]<<24) | ((opus_uint32)ch[1]<<16) | ((opus_uint32)ch[2]<< 8) | (opus_uint32)ch[3]; } static void check_encoder_option(int decode_only, const char *opt) { if (decode_only) { fprintf(stderr, "option %s is only for encoding\n", opt); exit(EXIT_FAILURE); } } static const int silk8_test[][4] = { {MODE_SILK_ONLY, OPUS_BANDWIDTH_NARROWBAND, 960*3, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_NARROWBAND, 960*2, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_NARROWBAND, 960, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_NARROWBAND, 480, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_NARROWBAND, 960*3, 2}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_NARROWBAND, 960*2, 2}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_NARROWBAND, 960, 2}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_NARROWBAND, 480, 2} }; static const int silk12_test[][4] = { {MODE_SILK_ONLY, OPUS_BANDWIDTH_MEDIUMBAND, 960*3, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_MEDIUMBAND, 960*2, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_MEDIUMBAND, 960, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_MEDIUMBAND, 480, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_MEDIUMBAND, 960*3, 2}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_MEDIUMBAND, 960*2, 2}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_MEDIUMBAND, 960, 2}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_MEDIUMBAND, 480, 2} }; static const int silk16_test[][4] = { {MODE_SILK_ONLY, OPUS_BANDWIDTH_WIDEBAND, 960*3, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_WIDEBAND, 960*2, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_WIDEBAND, 960, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_WIDEBAND, 480, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_WIDEBAND, 960*3, 2}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_WIDEBAND, 960*2, 2}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_WIDEBAND, 960, 2}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_WIDEBAND, 480, 2} }; static const int hybrid24_test[][4] = { {MODE_SILK_ONLY, OPUS_BANDWIDTH_SUPERWIDEBAND, 960, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_SUPERWIDEBAND, 480, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_SUPERWIDEBAND, 960, 2}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_SUPERWIDEBAND, 480, 2} }; static const int hybrid48_test[][4] = { {MODE_SILK_ONLY, OPUS_BANDWIDTH_FULLBAND, 960, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_FULLBAND, 480, 1}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_FULLBAND, 960, 2}, {MODE_SILK_ONLY, OPUS_BANDWIDTH_FULLBAND, 480, 2} }; static const int celt_test[][4] = { {MODE_CELT_ONLY, OPUS_BANDWIDTH_FULLBAND, 960, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_SUPERWIDEBAND, 960, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_WIDEBAND, 960, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_NARROWBAND, 960, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_FULLBAND, 480, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_SUPERWIDEBAND, 480, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_WIDEBAND, 480, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_NARROWBAND, 480, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_FULLBAND, 240, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_SUPERWIDEBAND, 240, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_WIDEBAND, 240, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_NARROWBAND, 240, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_FULLBAND, 120, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_SUPERWIDEBAND, 120, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_WIDEBAND, 120, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_NARROWBAND, 120, 1}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_FULLBAND, 960, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_SUPERWIDEBAND, 960, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_WIDEBAND, 960, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_NARROWBAND, 960, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_FULLBAND, 480, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_SUPERWIDEBAND, 480, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_WIDEBAND, 480, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_NARROWBAND, 480, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_FULLBAND, 240, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_SUPERWIDEBAND, 240, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_WIDEBAND, 240, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_NARROWBAND, 240, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_FULLBAND, 120, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_SUPERWIDEBAND, 120, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_WIDEBAND, 120, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_NARROWBAND, 120, 2}, }; static const int celt_hq_test[][4] = { {MODE_CELT_ONLY, OPUS_BANDWIDTH_FULLBAND, 960, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_FULLBAND, 480, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_FULLBAND, 240, 2}, {MODE_CELT_ONLY, OPUS_BANDWIDTH_FULLBAND, 120, 2}, }; #if 0 /* This is a hack that replaces the normal encoder/decoder with the multistream version */ #define OpusEncoder OpusMSEncoder #define OpusDecoder OpusMSDecoder #define opus_encode opus_multistream_encode #define opus_decode opus_multistream_decode #define opus_encoder_ctl opus_multistream_encoder_ctl #define opus_decoder_ctl opus_multistream_decoder_ctl #define opus_encoder_create ms_opus_encoder_create #define opus_decoder_create ms_opus_decoder_create #define opus_encoder_destroy opus_multistream_encoder_destroy #define opus_decoder_destroy opus_multistream_decoder_destroy static OpusEncoder *ms_opus_encoder_create(opus_int32 Fs, int channels, int application, int *error) { int streams, coupled_streams; unsigned char mapping[256]; return (OpusEncoder *)opus_multistream_surround_encoder_create(Fs, channels, 1, &streams, &coupled_streams, mapping, application, error); } static OpusDecoder *ms_opus_decoder_create(opus_int32 Fs, int channels, int *error) { int streams; int coupled_streams; unsigned char mapping[256]={0,1}; streams = 1; coupled_streams = channels==2; return (OpusDecoder *)opus_multistream_decoder_create(Fs, channels, streams, coupled_streams, mapping, error); } #endif int main(int argc, char *argv[]) { int err; char *inFile, *outFile; FILE *fin, *fout; OpusEncoder *enc=NULL; OpusDecoder *dec=NULL; int args; int len[2]; int frame_size, channels; opus_int32 bitrate_bps=0; unsigned char *data[2]; unsigned char *fbytes; opus_int32 sampling_rate; int use_vbr; int max_payload_bytes; int complexity; int use_inbandfec; int use_dtx; int forcechannels; int cvbr = 0; int packet_loss_perc; opus_int32 count=0, count_act=0; int k; opus_int32 skip=0; int stop=0; short *in, *out; int application=OPUS_APPLICATION_AUDIO; double bits=0.0, bits_max=0.0, bits_act=0.0, bits2=0.0, nrg; double tot_samples=0; opus_uint64 tot_in, tot_out; int bandwidth=OPUS_AUTO; const char *bandwidth_string; int lost = 0, lost_prev = 1; int toggle = 0; opus_uint32 enc_final_range[2]; opus_uint32 dec_final_range; int encode_only=0, decode_only=0; int max_frame_size = 48000*2; int curr_read=0; int sweep_bps = 0; int random_framesize=0, newsize=0, delayed_celt=0; int sweep_max=0, sweep_min=0; int random_fec=0; const int (*mode_list)[4]=NULL; int nb_modes_in_list=0; int curr_mode=0; int curr_mode_count=0; int mode_switch_time = 48000; int nb_encoded=0; int remaining=0; int variable_duration=OPUS_FRAMESIZE_ARG; int delayed_decision=0; if (argc < 5 ) { print_usage( argv ); return EXIT_FAILURE; } tot_in=tot_out=0; fprintf(stderr, "%s\n", opus_get_version_string()); args = 1; if (strcmp(argv[args], "-e")==0) { encode_only = 1; args++; } else if (strcmp(argv[args], "-d")==0) { decode_only = 1; args++; } if (!decode_only && argc < 7 ) { print_usage( argv ); return EXIT_FAILURE; } if (!decode_only) { if (strcmp(argv[args], "voip")==0) application = OPUS_APPLICATION_VOIP; else if (strcmp(argv[args], "restricted-lowdelay")==0) application = OPUS_APPLICATION_RESTRICTED_LOWDELAY; else if (strcmp(argv[args], "audio")!=0) { fprintf(stderr, "unknown application: %s\n", argv[args]); print_usage(argv); return EXIT_FAILURE; } args++; } sampling_rate = (opus_int32)atol(argv[args]); args++; if (sampling_rate != 8000 && sampling_rate != 12000 && sampling_rate != 16000 && sampling_rate != 24000 && sampling_rate != 48000) { fprintf(stderr, "Supported sampling rates are 8000, 12000, " "16000, 24000 and 48000.\n"); return EXIT_FAILURE; } frame_size = sampling_rate/50; channels = atoi(argv[args]); args++; if (channels < 1 || channels > 2) { fprintf(stderr, "Opus_demo supports only 1 or 2 channels.\n"); return EXIT_FAILURE; } if (!decode_only) { bitrate_bps = (opus_int32)atol(argv[args]); args++; } /* defaults: */ use_vbr = 1; max_payload_bytes = MAX_PACKET; complexity = 10; use_inbandfec = 0; forcechannels = OPUS_AUTO; use_dtx = 0; packet_loss_perc = 0; while( args < argc - 2 ) { /* process command line options */ if( strcmp( argv[ args ], "-cbr" ) == 0 ) { check_encoder_option(decode_only, "-cbr"); use_vbr = 0; args++; } else if( strcmp( argv[ args ], "-bandwidth" ) == 0 ) { check_encoder_option(decode_only, "-bandwidth"); if (strcmp(argv[ args + 1 ], "NB")==0) bandwidth = OPUS_BANDWIDTH_NARROWBAND; else if (strcmp(argv[ args + 1 ], "MB")==0) bandwidth = OPUS_BANDWIDTH_MEDIUMBAND; else if (strcmp(argv[ args + 1 ], "WB")==0) bandwidth = OPUS_BANDWIDTH_WIDEBAND; else if (strcmp(argv[ args + 1 ], "SWB")==0) bandwidth = OPUS_BANDWIDTH_SUPERWIDEBAND; else if (strcmp(argv[ args + 1 ], "FB")==0) bandwidth = OPUS_BANDWIDTH_FULLBAND; else { fprintf(stderr, "Unknown bandwidth %s. " "Supported are NB, MB, WB, SWB, FB.\n", argv[ args + 1 ]); return EXIT_FAILURE; } args += 2; } else if( strcmp( argv[ args ], "-framesize" ) == 0 ) { check_encoder_option(decode_only, "-framesize"); if (strcmp(argv[ args + 1 ], "2.5")==0) frame_size = sampling_rate/400; else if (strcmp(argv[ args + 1 ], "5")==0) frame_size = sampling_rate/200; else if (strcmp(argv[ args + 1 ], "10")==0) frame_size = sampling_rate/100; else if (strcmp(argv[ args + 1 ], "20")==0) frame_size = sampling_rate/50; else if (strcmp(argv[ args + 1 ], "40")==0) frame_size = sampling_rate/25; else if (strcmp(argv[ args + 1 ], "60")==0) frame_size = 3*sampling_rate/50; else { fprintf(stderr, "Unsupported frame size: %s ms. " "Supported are 2.5, 5, 10, 20, 40, 60.\n", argv[ args + 1 ]); return EXIT_FAILURE; } args += 2; } else if( strcmp( argv[ args ], "-max_payload" ) == 0 ) { check_encoder_option(decode_only, "-max_payload"); max_payload_bytes = atoi( argv[ args + 1 ] ); args += 2; } else if( strcmp( argv[ args ], "-complexity" ) == 0 ) { check_encoder_option(decode_only, "-complexity"); complexity = atoi( argv[ args + 1 ] ); args += 2; } else if( strcmp( argv[ args ], "-inbandfec" ) == 0 ) { use_inbandfec = 1; args++; } else if( strcmp( argv[ args ], "-forcemono" ) == 0 ) { check_encoder_option(decode_only, "-forcemono"); forcechannels = 1; args++; } else if( strcmp( argv[ args ], "-cvbr" ) == 0 ) { check_encoder_option(decode_only, "-cvbr"); cvbr = 1; args++; } else if( strcmp( argv[ args ], "-variable-duration" ) == 0 ) { check_encoder_option(decode_only, "-variable-duration"); variable_duration = OPUS_FRAMESIZE_VARIABLE; args++; } else if( strcmp( argv[ args ], "-delayed-decision" ) == 0 ) { check_encoder_option(decode_only, "-delayed-decision"); delayed_decision = 1; args++; } else if( strcmp( argv[ args ], "-dtx") == 0 ) { check_encoder_option(decode_only, "-dtx"); use_dtx = 1; args++; } else if( strcmp( argv[ args ], "-loss" ) == 0 ) { packet_loss_perc = atoi( argv[ args + 1 ] ); args += 2; } else if( strcmp( argv[ args ], "-sweep" ) == 0 ) { check_encoder_option(decode_only, "-sweep"); sweep_bps = atoi( argv[ args + 1 ] ); args += 2; } else if( strcmp( argv[ args ], "-random_framesize" ) == 0 ) { check_encoder_option(decode_only, "-random_framesize"); random_framesize = 1; args++; } else if( strcmp( argv[ args ], "-sweep_max" ) == 0 ) { check_encoder_option(decode_only, "-sweep_max"); sweep_max = atoi( argv[ args + 1 ] ); args += 2; } else if( strcmp( argv[ args ], "-random_fec" ) == 0 ) { check_encoder_option(decode_only, "-random_fec"); random_fec = 1; args++; } else if( strcmp( argv[ args ], "-silk8k_test" ) == 0 ) { check_encoder_option(decode_only, "-silk8k_test"); mode_list = silk8_test; nb_modes_in_list = 8; args++; } else if( strcmp( argv[ args ], "-silk12k_test" ) == 0 ) { check_encoder_option(decode_only, "-silk12k_test"); mode_list = silk12_test; nb_modes_in_list = 8; args++; } else if( strcmp( argv[ args ], "-silk16k_test" ) == 0 ) { check_encoder_option(decode_only, "-silk16k_test"); mode_list = silk16_test; nb_modes_in_list = 8; args++; } else if( strcmp( argv[ args ], "-hybrid24k_test" ) == 0 ) { check_encoder_option(decode_only, "-hybrid24k_test"); mode_list = hybrid24_test; nb_modes_in_list = 4; args++; } else if( strcmp( argv[ args ], "-hybrid48k_test" ) == 0 ) { check_encoder_option(decode_only, "-hybrid48k_test"); mode_list = hybrid48_test; nb_modes_in_list = 4; args++; } else if( strcmp( argv[ args ], "-celt_test" ) == 0 ) { check_encoder_option(decode_only, "-celt_test"); mode_list = celt_test; nb_modes_in_list = 32; args++; } else if( strcmp( argv[ args ], "-celt_hq_test" ) == 0 ) { check_encoder_option(decode_only, "-celt_hq_test"); mode_list = celt_hq_test; nb_modes_in_list = 4; args++; } else { printf( "Error: unrecognized setting: %s\n\n", argv[ args ] ); print_usage( argv ); return EXIT_FAILURE; } } if (sweep_max) sweep_min = bitrate_bps; if (max_payload_bytes < 0 || max_payload_bytes > MAX_PACKET) { fprintf (stderr, "max_payload_bytes must be between 0 and %d\n", MAX_PACKET); return EXIT_FAILURE; } inFile = argv[argc-2]; fin = fopen(inFile, "rb"); if (!fin) { fprintf (stderr, "Could not open input file %s\n", argv[argc-2]); return EXIT_FAILURE; } if (mode_list) { int size; fseek(fin, 0, SEEK_END); size = ftell(fin); fprintf(stderr, "File size is %d bytes\n", size); fseek(fin, 0, SEEK_SET); mode_switch_time = size/sizeof(short)/channels/nb_modes_in_list; fprintf(stderr, "Switching mode every %d samples\n", mode_switch_time); } outFile = argv[argc-1]; fout = fopen(outFile, "wb+"); if (!fout) { fprintf (stderr, "Could not open output file %s\n", argv[argc-1]); fclose(fin); return EXIT_FAILURE; } if (!decode_only) { enc = opus_encoder_create(sampling_rate, channels, application, &err); if (err != OPUS_OK) { fprintf(stderr, "Cannot create encoder: %s\n", opus_strerror(err)); fclose(fin); fclose(fout); return EXIT_FAILURE; } opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate_bps)); opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(bandwidth)); opus_encoder_ctl(enc, OPUS_SET_VBR(use_vbr)); opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(cvbr)); opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity)); opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(use_inbandfec)); opus_encoder_ctl(enc, OPUS_SET_FORCE_CHANNELS(forcechannels)); opus_encoder_ctl(enc, OPUS_SET_DTX(use_dtx)); opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc)); opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&skip)); opus_encoder_ctl(enc, OPUS_SET_LSB_DEPTH(16)); opus_encoder_ctl(enc, OPUS_SET_EXPERT_FRAME_DURATION(variable_duration)); } if (!encode_only) { dec = opus_decoder_create(sampling_rate, channels, &err); if (err != OPUS_OK) { fprintf(stderr, "Cannot create decoder: %s\n", opus_strerror(err)); fclose(fin); fclose(fout); return EXIT_FAILURE; } } switch(bandwidth) { case OPUS_BANDWIDTH_NARROWBAND: bandwidth_string = "narrowband"; break; case OPUS_BANDWIDTH_MEDIUMBAND: bandwidth_string = "mediumband"; break; case OPUS_BANDWIDTH_WIDEBAND: bandwidth_string = "wideband"; break; case OPUS_BANDWIDTH_SUPERWIDEBAND: bandwidth_string = "superwideband"; break; case OPUS_BANDWIDTH_FULLBAND: bandwidth_string = "fullband"; break; case OPUS_AUTO: bandwidth_string = "auto bandwidth"; break; default: bandwidth_string = "unknown"; break; } if (decode_only) fprintf(stderr, "Decoding with %ld Hz output (%d channels)\n", (long)sampling_rate, channels); else fprintf(stderr, "Encoding %ld Hz input at %.3f kb/s " "in %s with %d-sample frames.\n", (long)sampling_rate, bitrate_bps*0.001, bandwidth_string, frame_size); in = (short*)malloc(max_frame_size*channels*sizeof(short)); out = (short*)malloc(max_frame_size*channels*sizeof(short)); fbytes = (unsigned char*)malloc(max_frame_size*channels*sizeof(short)); data[0] = (unsigned char*)calloc(max_payload_bytes,sizeof(char)); if ( use_inbandfec ) { data[1] = (unsigned char*)calloc(max_payload_bytes,sizeof(char)); } if(delayed_decision) { if (variable_duration!=OPUS_FRAMESIZE_VARIABLE) { if (frame_size==sampling_rate/400) variable_duration = OPUS_FRAMESIZE_2_5_MS; else if (frame_size==sampling_rate/200) variable_duration = OPUS_FRAMESIZE_5_MS; else if (frame_size==sampling_rate/100) variable_duration = OPUS_FRAMESIZE_10_MS; else if (frame_size==sampling_rate/50) variable_duration = OPUS_FRAMESIZE_20_MS; else if (frame_size==sampling_rate/25) variable_duration = OPUS_FRAMESIZE_40_MS; else variable_duration = OPUS_FRAMESIZE_60_MS; opus_encoder_ctl(enc, OPUS_SET_EXPERT_FRAME_DURATION(variable_duration)); } frame_size = 2*48000; } while (!stop) { if (delayed_celt) { frame_size = newsize; delayed_celt = 0; } else if (random_framesize && rand()%20==0) { newsize = rand()%6; switch(newsize) { case 0: newsize=sampling_rate/400; break; case 1: newsize=sampling_rate/200; break; case 2: newsize=sampling_rate/100; break; case 3: newsize=sampling_rate/50; break; case 4: newsize=sampling_rate/25; break; case 5: newsize=3*sampling_rate/50; break; } while (newsize < sampling_rate/25 && bitrate_bps-abs(sweep_bps) <= 3*12*sampling_rate/newsize) newsize*=2; if (newsize < sampling_rate/100 && frame_size >= sampling_rate/100) { opus_encoder_ctl(enc, OPUS_SET_FORCE_MODE(MODE_CELT_ONLY)); delayed_celt=1; } else { frame_size = newsize; } } if (random_fec && rand()%30==0) { opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(rand()%4==0)); } if (decode_only) { unsigned char ch[4]; err = fread(ch, 1, 4, fin); if (feof(fin)) break; len[toggle] = char_to_int(ch); if (len[toggle]>max_payload_bytes || len[toggle]<0) { fprintf(stderr, "Invalid payload length: %d\n",len[toggle]); break; } err = fread(ch, 1, 4, fin); enc_final_range[toggle] = char_to_int(ch); err = fread(data[toggle], 1, len[toggle], fin); if (err sweep_max) sweep_bps = -sweep_bps; else if (bitrate_bps < sweep_min) sweep_bps = -sweep_bps; } /* safety */ if (bitrate_bps<1000) bitrate_bps = 1000; opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate_bps)); } opus_encoder_ctl(enc, OPUS_GET_FINAL_RANGE(&enc_final_range[toggle])); if (len[toggle] < 0) { fprintf (stderr, "opus_encode() returned %d\n", len[toggle]); fclose(fin); fclose(fout); return EXIT_FAILURE; } curr_mode_count += frame_size; if (curr_mode_count > mode_switch_time && curr_mode < nb_modes_in_list-1) { curr_mode++; curr_mode_count = 0; } } #if 0 /* This is for testing the padding code, do not enable by default */ if (len[toggle]<1275) { int new_len = len[toggle]+rand()%(max_payload_bytes-len[toggle]); if ((err = opus_packet_pad(data[toggle], len[toggle], new_len)) != OPUS_OK) { fprintf(stderr, "padding failed: %s\n", opus_strerror(err)); return EXIT_FAILURE; } len[toggle] = new_len; } #endif if (encode_only) { unsigned char int_field[4]; int_to_char(len[toggle], int_field); if (fwrite(int_field, 1, 4, fout) != 4) { fprintf(stderr, "Error writing.\n"); return EXIT_FAILURE; } int_to_char(enc_final_range[toggle], int_field); if (fwrite(int_field, 1, 4, fout) != 4) { fprintf(stderr, "Error writing.\n"); return EXIT_FAILURE; } if (fwrite(data[toggle], 1, len[toggle], fout) != (unsigned)len[toggle]) { fprintf(stderr, "Error writing.\n"); return EXIT_FAILURE; } tot_samples += nb_encoded; } else { int output_samples; lost = len[toggle]==0 || (packet_loss_perc>0 && rand()%100 < packet_loss_perc); if (lost) opus_decoder_ctl(dec, OPUS_GET_LAST_PACKET_DURATION(&output_samples)); else output_samples = max_frame_size; if( count >= use_inbandfec ) { /* delay by one packet when using in-band FEC */ if( use_inbandfec ) { if( lost_prev ) { /* attempt to decode with in-band FEC from next packet */ opus_decoder_ctl(dec, OPUS_GET_LAST_PACKET_DURATION(&output_samples)); output_samples = opus_decode(dec, lost ? NULL : data[toggle], len[toggle], out, output_samples, 1); } else { /* regular decode */ output_samples = max_frame_size; output_samples = opus_decode(dec, data[1-toggle], len[1-toggle], out, output_samples, 0); } } else { output_samples = opus_decode(dec, lost ? NULL : data[toggle], len[toggle], out, output_samples, 0); } if (output_samples>0) { if (!decode_only && tot_out + output_samples > tot_in) { stop=1; output_samples = tot_in-tot_out; } if (output_samples>skip) { int i; for(i=0;i<(output_samples-skip)*channels;i++) { short s; s=out[i+(skip*channels)]; fbytes[2*i]=s&0xFF; fbytes[2*i+1]=(s>>8)&0xFF; } if (fwrite(fbytes, sizeof(short)*channels, output_samples-skip, fout) != (unsigned)(output_samples-skip)){ fprintf(stderr, "Error writing.\n"); return EXIT_FAILURE; } tot_out += output_samples-skip; } if (output_samples= use_inbandfec ) { /* count bits */ bits += len[toggle]*8; bits_max = ( len[toggle]*8 > bits_max ) ? len[toggle]*8 : bits_max; bits2 += len[toggle]*len[toggle]*64; if (!decode_only) { nrg = 0.0; for ( k = 0; k < frame_size * channels; k++ ) { nrg += in[ k ] * (double)in[ k ]; } nrg /= frame_size * channels; if( nrg > 1e5 ) { bits_act += len[toggle]*8; count_act++; } } } count++; toggle = (toggle + use_inbandfec) & 1; } /* Print out bitrate statistics */ if(decode_only) frame_size = (int)(tot_samples / count); count -= use_inbandfec; fprintf (stderr, "average bitrate: %7.3f kb/s\n", 1e-3*bits*sampling_rate/tot_samples); fprintf (stderr, "maximum bitrate: %7.3f kb/s\n", 1e-3*bits_max*sampling_rate/frame_size); if (!decode_only) fprintf (stderr, "active bitrate: %7.3f kb/s\n", 1e-3*bits_act*sampling_rate/(1e-15+frame_size*(double)count_act)); fprintf (stderr, "bitrate standard deviation: %7.3f kb/s\n", 1e-3*sqrt(bits2/count - bits*bits/(count*(double)count))*sampling_rate/frame_size); /* Close any files to which intermediate results were stored */ SILK_DEBUG_STORE_CLOSE_FILES silk_TimerSave("opus_timing.txt"); opus_encoder_destroy(enc); opus_decoder_destroy(dec); free(data[0]); if (use_inbandfec) free(data[1]); fclose(fin); fclose(fout); free(in); free(out); free(fbytes); return EXIT_SUCCESS; } ================================================ FILE: deps/pjsip/third_party/opus/src/opus_encoder.c ================================================ /* Copyright (c) 2010-2011 Xiph.Org Foundation, Skype Limited Written by Jean-Marc Valin and Koen Vos */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "celt.h" #include "entenc.h" #include "modes.h" #include "API.h" #include "stack_alloc.h" #include "float_cast.h" #include "opus.h" #include "arch.h" #include "pitch.h" #include "opus_private.h" #include "os_support.h" #include "cpu_support.h" #include "analysis.h" #include "mathops.h" #include "tuning_parameters.h" #ifdef FIXED_POINT #include "fixed/structs_FIX.h" #else #include "float/structs_FLP.h" #endif #define MAX_ENCODER_BUFFER 480 typedef struct { opus_val32 XX, XY, YY; opus_val16 smoothed_width; opus_val16 max_follower; } StereoWidthState; struct OpusEncoder { int celt_enc_offset; int silk_enc_offset; silk_EncControlStruct silk_mode; int application; int channels; int delay_compensation; int force_channels; int signal_type; int user_bandwidth; int max_bandwidth; int user_forced_mode; int voice_ratio; opus_int32 Fs; int use_vbr; int vbr_constraint; int variable_duration; opus_int32 bitrate_bps; opus_int32 user_bitrate_bps; int lsb_depth; int encoder_buffer; int lfe; int arch; #ifndef DISABLE_FLOAT_API TonalityAnalysisState analysis; #endif #define OPUS_ENCODER_RESET_START stream_channels int stream_channels; opus_int16 hybrid_stereo_width_Q14; opus_int32 variable_HP_smth2_Q15; opus_val16 prev_HB_gain; opus_val32 hp_mem[4]; int mode; int prev_mode; int prev_channels; int prev_framesize; int bandwidth; int silk_bw_switch; /* Sampling rate (at the API level) */ int first; opus_val16 * energy_masking; StereoWidthState width_mem; opus_val16 delay_buffer[MAX_ENCODER_BUFFER*2]; #ifndef DISABLE_FLOAT_API int detected_bandwidth; #endif opus_uint32 rangeFinal; }; /* Transition tables for the voice and music. First column is the middle (memoriless) threshold. The second column is the hysteresis (difference with the middle) */ static const opus_int32 mono_voice_bandwidth_thresholds[8] = { 11000, 1000, /* NB<->MB */ 14000, 1000, /* MB<->WB */ 17000, 1000, /* WB<->SWB */ 21000, 2000, /* SWB<->FB */ }; static const opus_int32 mono_music_bandwidth_thresholds[8] = { 12000, 1000, /* NB<->MB */ 15000, 1000, /* MB<->WB */ 18000, 2000, /* WB<->SWB */ 22000, 2000, /* SWB<->FB */ }; static const opus_int32 stereo_voice_bandwidth_thresholds[8] = { 11000, 1000, /* NB<->MB */ 14000, 1000, /* MB<->WB */ 21000, 2000, /* WB<->SWB */ 28000, 2000, /* SWB<->FB */ }; static const opus_int32 stereo_music_bandwidth_thresholds[8] = { 12000, 1000, /* NB<->MB */ 18000, 2000, /* MB<->WB */ 21000, 2000, /* WB<->SWB */ 30000, 2000, /* SWB<->FB */ }; /* Threshold bit-rates for switching between mono and stereo */ static const opus_int32 stereo_voice_threshold = 30000; static const opus_int32 stereo_music_threshold = 30000; /* Threshold bit-rate for switching between SILK/hybrid and CELT-only */ static const opus_int32 mode_thresholds[2][2] = { /* voice */ /* music */ { 64000, 16000}, /* mono */ { 36000, 16000}, /* stereo */ }; int opus_encoder_get_size(int channels) { int silkEncSizeBytes, celtEncSizeBytes; int ret; if (channels<1 || channels > 2) return 0; ret = silk_Get_Encoder_Size( &silkEncSizeBytes ); if (ret) return 0; silkEncSizeBytes = align(silkEncSizeBytes); celtEncSizeBytes = celt_encoder_get_size(channels); return align(sizeof(OpusEncoder))+silkEncSizeBytes+celtEncSizeBytes; } int opus_encoder_init(OpusEncoder* st, opus_int32 Fs, int channels, int application) { void *silk_enc; CELTEncoder *celt_enc; int err; int ret, silkEncSizeBytes; if((Fs!=48000&&Fs!=24000&&Fs!=16000&&Fs!=12000&&Fs!=8000)||(channels!=1&&channels!=2)|| (application != OPUS_APPLICATION_VOIP && application != OPUS_APPLICATION_AUDIO && application != OPUS_APPLICATION_RESTRICTED_LOWDELAY)) return OPUS_BAD_ARG; OPUS_CLEAR((char*)st, opus_encoder_get_size(channels)); /* Create SILK encoder */ ret = silk_Get_Encoder_Size( &silkEncSizeBytes ); if (ret) return OPUS_BAD_ARG; silkEncSizeBytes = align(silkEncSizeBytes); st->silk_enc_offset = align(sizeof(OpusEncoder)); st->celt_enc_offset = st->silk_enc_offset+silkEncSizeBytes; silk_enc = (char*)st+st->silk_enc_offset; celt_enc = (CELTEncoder*)((char*)st+st->celt_enc_offset); st->stream_channels = st->channels = channels; st->Fs = Fs; st->arch = opus_select_arch(); ret = silk_InitEncoder( silk_enc, st->arch, &st->silk_mode ); if(ret)return OPUS_INTERNAL_ERROR; /* default SILK parameters */ st->silk_mode.nChannelsAPI = channels; st->silk_mode.nChannelsInternal = channels; st->silk_mode.API_sampleRate = st->Fs; st->silk_mode.maxInternalSampleRate = 16000; st->silk_mode.minInternalSampleRate = 8000; st->silk_mode.desiredInternalSampleRate = 16000; st->silk_mode.payloadSize_ms = 20; st->silk_mode.bitRate = 25000; st->silk_mode.packetLossPercentage = 0; st->silk_mode.complexity = 9; st->silk_mode.useInBandFEC = 0; st->silk_mode.useDTX = 0; st->silk_mode.useCBR = 0; st->silk_mode.reducedDependency = 0; /* Create CELT encoder */ /* Initialize CELT encoder */ err = celt_encoder_init(celt_enc, Fs, channels, st->arch); if(err!=OPUS_OK)return OPUS_INTERNAL_ERROR; celt_encoder_ctl(celt_enc, CELT_SET_SIGNALLING(0)); celt_encoder_ctl(celt_enc, OPUS_SET_COMPLEXITY(st->silk_mode.complexity)); st->use_vbr = 1; /* Makes constrained VBR the default (safer for real-time use) */ st->vbr_constraint = 1; st->user_bitrate_bps = OPUS_AUTO; st->bitrate_bps = 3000+Fs*channels; st->application = application; st->signal_type = OPUS_AUTO; st->user_bandwidth = OPUS_AUTO; st->max_bandwidth = OPUS_BANDWIDTH_FULLBAND; st->force_channels = OPUS_AUTO; st->user_forced_mode = OPUS_AUTO; st->voice_ratio = -1; st->encoder_buffer = st->Fs/100; st->lsb_depth = 24; st->variable_duration = OPUS_FRAMESIZE_ARG; /* Delay compensation of 4 ms (2.5 ms for SILK's extra look-ahead + 1.5 ms for SILK resamplers and stereo prediction) */ st->delay_compensation = st->Fs/250; st->hybrid_stereo_width_Q14 = 1 << 14; st->prev_HB_gain = Q15ONE; st->variable_HP_smth2_Q15 = silk_LSHIFT( silk_lin2log( VARIABLE_HP_MIN_CUTOFF_HZ ), 8 ); st->first = 1; st->mode = MODE_HYBRID; st->bandwidth = OPUS_BANDWIDTH_FULLBAND; #ifndef DISABLE_FLOAT_API tonality_analysis_init(&st->analysis); #endif return OPUS_OK; } static unsigned char gen_toc(int mode, int framerate, int bandwidth, int channels) { int period; unsigned char toc; period = 0; while (framerate < 400) { framerate <<= 1; period++; } if (mode == MODE_SILK_ONLY) { toc = (bandwidth-OPUS_BANDWIDTH_NARROWBAND)<<5; toc |= (period-2)<<3; } else if (mode == MODE_CELT_ONLY) { int tmp = bandwidth-OPUS_BANDWIDTH_MEDIUMBAND; if (tmp < 0) tmp = 0; toc = 0x80; toc |= tmp << 5; toc |= period<<3; } else /* Hybrid */ { toc = 0x60; toc |= (bandwidth-OPUS_BANDWIDTH_SUPERWIDEBAND)<<4; toc |= (period-2)<<3; } toc |= (channels==2)<<2; return toc; } #ifndef FIXED_POINT static void silk_biquad_float( const opus_val16 *in, /* I: Input signal */ const opus_int32 *B_Q28, /* I: MA coefficients [3] */ const opus_int32 *A_Q28, /* I: AR coefficients [2] */ opus_val32 *S, /* I/O: State vector [2] */ opus_val16 *out, /* O: Output signal */ const opus_int32 len, /* I: Signal length (must be even) */ int stride ) { /* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */ opus_int k; opus_val32 vout; opus_val32 inval; opus_val32 A[2], B[3]; A[0] = (opus_val32)(A_Q28[0] * (1.f/((opus_int32)1<<28))); A[1] = (opus_val32)(A_Q28[1] * (1.f/((opus_int32)1<<28))); B[0] = (opus_val32)(B_Q28[0] * (1.f/((opus_int32)1<<28))); B[1] = (opus_val32)(B_Q28[1] * (1.f/((opus_int32)1<<28))); B[2] = (opus_val32)(B_Q28[2] * (1.f/((opus_int32)1<<28))); /* Negate A_Q28 values and split in two parts */ for( k = 0; k < len; k++ ) { /* S[ 0 ], S[ 1 ]: Q12 */ inval = in[ k*stride ]; vout = S[ 0 ] + B[0]*inval; S[ 0 ] = S[1] - vout*A[0] + B[1]*inval; S[ 1 ] = - vout*A[1] + B[2]*inval + VERY_SMALL; /* Scale back to Q0 and saturate */ out[ k*stride ] = vout; } } #endif static void hp_cutoff(const opus_val16 *in, opus_int32 cutoff_Hz, opus_val16 *out, opus_val32 *hp_mem, int len, int channels, opus_int32 Fs) { opus_int32 B_Q28[ 3 ], A_Q28[ 2 ]; opus_int32 Fc_Q19, r_Q28, r_Q22; silk_assert( cutoff_Hz <= silk_int32_MAX / SILK_FIX_CONST( 1.5 * 3.14159 / 1000, 19 ) ); Fc_Q19 = silk_DIV32_16( silk_SMULBB( SILK_FIX_CONST( 1.5 * 3.14159 / 1000, 19 ), cutoff_Hz ), Fs/1000 ); silk_assert( Fc_Q19 > 0 && Fc_Q19 < 32768 ); r_Q28 = SILK_FIX_CONST( 1.0, 28 ) - silk_MUL( SILK_FIX_CONST( 0.92, 9 ), Fc_Q19 ); /* b = r * [ 1; -2; 1 ]; */ /* a = [ 1; -2 * r * ( 1 - 0.5 * Fc^2 ); r^2 ]; */ B_Q28[ 0 ] = r_Q28; B_Q28[ 1 ] = silk_LSHIFT( -r_Q28, 1 ); B_Q28[ 2 ] = r_Q28; /* -r * ( 2 - Fc * Fc ); */ r_Q22 = silk_RSHIFT( r_Q28, 6 ); A_Q28[ 0 ] = silk_SMULWW( r_Q22, silk_SMULWW( Fc_Q19, Fc_Q19 ) - SILK_FIX_CONST( 2.0, 22 ) ); A_Q28[ 1 ] = silk_SMULWW( r_Q22, r_Q22 ); #ifdef FIXED_POINT silk_biquad_alt( in, B_Q28, A_Q28, hp_mem, out, len, channels ); if( channels == 2 ) { silk_biquad_alt( in+1, B_Q28, A_Q28, hp_mem+2, out+1, len, channels ); } #else silk_biquad_float( in, B_Q28, A_Q28, hp_mem, out, len, channels ); if( channels == 2 ) { silk_biquad_float( in+1, B_Q28, A_Q28, hp_mem+2, out+1, len, channels ); } #endif } #ifdef FIXED_POINT static void dc_reject(const opus_val16 *in, opus_int32 cutoff_Hz, opus_val16 *out, opus_val32 *hp_mem, int len, int channels, opus_int32 Fs) { int c, i; int shift; /* Approximates -round(log2(4.*cutoff_Hz/Fs)) */ shift=celt_ilog2(Fs/(cutoff_Hz*3)); for (c=0;cFs/400; if (st->user_bitrate_bps==OPUS_AUTO) return 60*st->Fs/frame_size + st->Fs*st->channels; else if (st->user_bitrate_bps==OPUS_BITRATE_MAX) return max_data_bytes*8*st->Fs/frame_size; else return st->user_bitrate_bps; } #ifndef DISABLE_FLOAT_API /* Don't use more than 60 ms for the frame size analysis */ #define MAX_DYNAMIC_FRAMESIZE 24 /* Estimates how much the bitrate will be boosted based on the sub-frame energy */ static float transient_boost(const float *E, const float *E_1, int LM, int maxM) { int i; int M; float sumE=0, sumE_1=0; float metric; M = IMIN(maxM, (1<10 ? 1 : 0;*/ /*return MAX16(0,1-exp(-.25*(metric-2.)));*/ return MIN16(1,(float)sqrt(MAX16(0,.05f*(metric-2)))); } /* Viterbi decoding trying to find the best frame size combination using look-ahead State numbering: 0: unused 1: 2.5 ms 2: 5 ms (#1) 3: 5 ms (#2) 4: 10 ms (#1) 5: 10 ms (#2) 6: 10 ms (#3) 7: 10 ms (#4) 8: 20 ms (#1) 9: 20 ms (#2) 10: 20 ms (#3) 11: 20 ms (#4) 12: 20 ms (#5) 13: 20 ms (#6) 14: 20 ms (#7) 15: 20 ms (#8) */ static int transient_viterbi(const float *E, const float *E_1, int N, int frame_cost, int rate) { int i; float cost[MAX_DYNAMIC_FRAMESIZE][16]; int states[MAX_DYNAMIC_FRAMESIZE][16]; float best_cost; int best_state; float factor; /* Take into account that we damp VBR in the 32 kb/s to 64 kb/s range. */ if (rate<80) factor=0; else if (rate>160) factor=1; else factor = (rate-80.f)/80.f; /* Makes variable framesize less aggressive at lower bitrates, but I can't find any valid theoretical justification for this (other than it seems to help) */ for (i=0;i<16;i++) { /* Impossible state */ states[0][i] = -1; cost[0][i] = 1e10; } for (i=0;i<4;i++) { cost[0][1<=0;i--) { /*printf("%d ", best_state);*/ best_state = states[i][best_state]; } /*printf("%d\n", best_state);*/ return best_state; } static int optimize_framesize(const void *x, int len, int C, opus_int32 Fs, int bitrate, opus_val16 tonality, float *mem, int buffering, downmix_func downmix) { int N; int i; float e[MAX_DYNAMIC_FRAMESIZE+4]; float e_1[MAX_DYNAMIC_FRAMESIZE+3]; opus_val32 memx; int bestLM=0; int subframe; int pos; int offset; VARDECL(opus_val32, sub); subframe = Fs/400; ALLOC(sub, subframe, opus_val32); e[0]=mem[0]; e_1[0]=1.f/(EPSILON+mem[0]); if (buffering) { /* Consider the CELT delay when not in restricted-lowdelay */ /* We assume the buffering is between 2.5 and 5 ms */ offset = 2*subframe - buffering; celt_assert(offset>=0 && offset <= subframe); len -= offset; e[1]=mem[1]; e_1[1]=1.f/(EPSILON+mem[1]); e[2]=mem[2]; e_1[2]=1.f/(EPSILON+mem[2]); pos = 3; } else { pos=1; offset=0; } N=IMIN(len/subframe, MAX_DYNAMIC_FRAMESIZE); /* Just silencing a warning, it's really initialized later */ memx = 0; for (i=0;i-1) { for (j=0;j-1) { for (j=0;j= OPUS_FRAMESIZE_2_5_MS && variable_duration <= OPUS_FRAMESIZE_60_MS) new_size = IMIN(3*Fs/50, (Fs/400)<<(variable_duration-OPUS_FRAMESIZE_2_5_MS)); else return -1; if (new_size>frame_size) return -1; if (400*new_size!=Fs && 200*new_size!=Fs && 100*new_size!=Fs && 50*new_size!=Fs && 25*new_size!=Fs && 50*new_size!=3*Fs) return -1; return new_size; } opus_int32 compute_frame_size(const void *analysis_pcm, int frame_size, int variable_duration, int C, opus_int32 Fs, int bitrate_bps, int delay_compensation, downmix_func downmix #ifndef DISABLE_FLOAT_API , float *subframe_mem #endif ) { #ifndef DISABLE_FLOAT_API if (variable_duration == OPUS_FRAMESIZE_VARIABLE && frame_size >= Fs/200) { int LM = 3; LM = optimize_framesize(analysis_pcm, frame_size, C, Fs, bitrate_bps, 0, subframe_mem, delay_compensation, downmix); while ((Fs/400<frame_size) LM--; frame_size = (Fs/400<XX += MULT16_32_Q15(short_alpha, xx-mem->XX); mem->XY += MULT16_32_Q15(short_alpha, xy-mem->XY); mem->YY += MULT16_32_Q15(short_alpha, yy-mem->YY); mem->XX = MAX32(0, mem->XX); mem->XY = MAX32(0, mem->XY); mem->YY = MAX32(0, mem->YY); if (MAX32(mem->XX, mem->YY)>QCONST16(8e-4f, 18)) { sqrt_xx = celt_sqrt(mem->XX); sqrt_yy = celt_sqrt(mem->YY); qrrt_xx = celt_sqrt(sqrt_xx); qrrt_yy = celt_sqrt(sqrt_yy); /* Inter-channel correlation */ mem->XY = MIN32(mem->XY, sqrt_xx*sqrt_yy); corr = SHR32(frac_div32(mem->XY,EPSILON+MULT16_16(sqrt_xx,sqrt_yy)),16); /* Approximate loudness difference */ ldiff = Q15ONE*ABS16(qrrt_xx-qrrt_yy)/(EPSILON+qrrt_xx+qrrt_yy); width = MULT16_16_Q15(celt_sqrt(QCONST32(1.f,30)-MULT16_16(corr,corr)), ldiff); /* Smoothing over one second */ mem->smoothed_width += (width-mem->smoothed_width)/frame_rate; /* Peak follower */ mem->max_follower = MAX16(mem->max_follower-QCONST16(.02f,15)/frame_rate, mem->smoothed_width); } else { width = 0; corr=Q15ONE; ldiff=0; } /*printf("%f %f %f %f %f ", corr/(float)Q15ONE, ldiff/(float)Q15ONE, width/(float)Q15ONE, mem->smoothed_width/(float)Q15ONE, mem->max_follower/(float)Q15ONE);*/ return EXTRACT16(MIN32(Q15ONE,20*mem->max_follower)); } opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_size, unsigned char *data, opus_int32 out_data_bytes, int lsb_depth, const void *analysis_pcm, opus_int32 analysis_size, int c1, int c2, int analysis_channels, downmix_func downmix, int float_api) { void *silk_enc; CELTEncoder *celt_enc; int i; int ret=0; opus_int32 nBytes; ec_enc enc; int bytes_target; int prefill=0; int start_band = 0; int redundancy = 0; int redundancy_bytes = 0; /* Number of bytes to use for redundancy frame */ int celt_to_silk = 0; VARDECL(opus_val16, pcm_buf); int nb_compr_bytes; int to_celt = 0; opus_uint32 redundant_rng = 0; int cutoff_Hz, hp_freq_smth1; int voice_est; /* Probability of voice in Q7 */ opus_int32 equiv_rate; int delay_compensation; int frame_rate; opus_int32 max_rate; /* Max bitrate we're allowed to use */ int curr_bandwidth; opus_val16 HB_gain; opus_int32 max_data_bytes; /* Max number of bytes we're allowed to use */ int total_buffer; opus_val16 stereo_width; const CELTMode *celt_mode; #ifndef DISABLE_FLOAT_API AnalysisInfo analysis_info; int analysis_read_pos_bak=-1; int analysis_read_subframe_bak=-1; #endif VARDECL(opus_val16, tmp_prefill); ALLOC_STACK; max_data_bytes = IMIN(1276, out_data_bytes); st->rangeFinal = 0; if ((!st->variable_duration && 400*frame_size != st->Fs && 200*frame_size != st->Fs && 100*frame_size != st->Fs && 50*frame_size != st->Fs && 25*frame_size != st->Fs && 50*frame_size != 3*st->Fs) || (400*frame_size < st->Fs) || max_data_bytes<=0 ) { RESTORE_STACK; return OPUS_BAD_ARG; } silk_enc = (char*)st+st->silk_enc_offset; celt_enc = (CELTEncoder*)((char*)st+st->celt_enc_offset); if (st->application == OPUS_APPLICATION_RESTRICTED_LOWDELAY) delay_compensation = 0; else delay_compensation = st->delay_compensation; lsb_depth = IMIN(lsb_depth, st->lsb_depth); celt_encoder_ctl(celt_enc, CELT_GET_MODE(&celt_mode)); #ifndef DISABLE_FLOAT_API analysis_info.valid = 0; #ifdef FIXED_POINT if (st->silk_mode.complexity >= 10 && st->Fs==48000) #else if (st->silk_mode.complexity >= 7 && st->Fs==48000) #endif { analysis_read_pos_bak = st->analysis.read_pos; analysis_read_subframe_bak = st->analysis.read_subframe; run_analysis(&st->analysis, celt_mode, analysis_pcm, analysis_size, frame_size, c1, c2, analysis_channels, st->Fs, lsb_depth, downmix, &analysis_info); } #else (void)analysis_pcm; (void)analysis_size; #endif st->voice_ratio = -1; #ifndef DISABLE_FLOAT_API st->detected_bandwidth = 0; if (analysis_info.valid) { int analysis_bandwidth; if (st->signal_type == OPUS_AUTO) st->voice_ratio = (int)floor(.5+100*(1-analysis_info.music_prob)); analysis_bandwidth = analysis_info.bandwidth; if (analysis_bandwidth<=12) st->detected_bandwidth = OPUS_BANDWIDTH_NARROWBAND; else if (analysis_bandwidth<=14) st->detected_bandwidth = OPUS_BANDWIDTH_MEDIUMBAND; else if (analysis_bandwidth<=16) st->detected_bandwidth = OPUS_BANDWIDTH_WIDEBAND; else if (analysis_bandwidth<=18) st->detected_bandwidth = OPUS_BANDWIDTH_SUPERWIDEBAND; else st->detected_bandwidth = OPUS_BANDWIDTH_FULLBAND; } #endif if (st->channels==2 && st->force_channels!=1) stereo_width = compute_stereo_width(pcm, frame_size, st->Fs, &st->width_mem); else stereo_width = 0; total_buffer = delay_compensation; st->bitrate_bps = user_bitrate_to_bitrate(st, frame_size, max_data_bytes); frame_rate = st->Fs/frame_size; if (max_data_bytes<3 || st->bitrate_bps < 3*frame_rate*8 || (frame_rate<50 && (max_data_bytes*frame_rate<300 || st->bitrate_bps < 2400))) { /*If the space is too low to do something useful, emit 'PLC' frames.*/ int tocmode = st->mode; int bw = st->bandwidth == 0 ? OPUS_BANDWIDTH_NARROWBAND : st->bandwidth; if (tocmode==0) tocmode = MODE_SILK_ONLY; if (frame_rate>100) tocmode = MODE_CELT_ONLY; if (frame_rate < 50) tocmode = MODE_SILK_ONLY; if(tocmode==MODE_SILK_ONLY&&bw>OPUS_BANDWIDTH_WIDEBAND) bw=OPUS_BANDWIDTH_WIDEBAND; else if (tocmode==MODE_CELT_ONLY&&bw==OPUS_BANDWIDTH_MEDIUMBAND) bw=OPUS_BANDWIDTH_NARROWBAND; else if (bw<=OPUS_BANDWIDTH_SUPERWIDEBAND) bw=OPUS_BANDWIDTH_SUPERWIDEBAND; data[0] = gen_toc(tocmode, frame_rate, bw, st->stream_channels); RESTORE_STACK; return 1; } if (!st->use_vbr) { int cbrBytes; cbrBytes = IMIN( (st->bitrate_bps + 4*frame_rate)/(8*frame_rate) , max_data_bytes); st->bitrate_bps = cbrBytes * (8*frame_rate); max_data_bytes = cbrBytes; } max_rate = frame_rate*max_data_bytes*8; /* Equivalent 20-ms rate for mode/channel/bandwidth decisions */ equiv_rate = st->bitrate_bps - (40*st->channels+20)*(st->Fs/frame_size - 50); if (st->signal_type == OPUS_SIGNAL_VOICE) voice_est = 127; else if (st->signal_type == OPUS_SIGNAL_MUSIC) voice_est = 0; else if (st->voice_ratio >= 0) { voice_est = st->voice_ratio*327>>8; /* For AUDIO, never be more than 90% confident of having speech */ if (st->application == OPUS_APPLICATION_AUDIO) voice_est = IMIN(voice_est, 115); } else if (st->application == OPUS_APPLICATION_VOIP) voice_est = 115; else voice_est = 48; if (st->force_channels!=OPUS_AUTO && st->channels == 2) { st->stream_channels = st->force_channels; } else { #ifdef FUZZING /* Random mono/stereo decision */ if (st->channels == 2 && (rand()&0x1F)==0) st->stream_channels = 3-st->stream_channels; #else /* Rate-dependent mono-stereo decision */ if (st->channels == 2) { opus_int32 stereo_threshold; stereo_threshold = stereo_music_threshold + ((voice_est*voice_est*(stereo_voice_threshold-stereo_music_threshold))>>14); if (st->stream_channels == 2) stereo_threshold -= 1000; else stereo_threshold += 1000; st->stream_channels = (equiv_rate > stereo_threshold) ? 2 : 1; } else { st->stream_channels = st->channels; } #endif } equiv_rate = st->bitrate_bps - (40*st->stream_channels+20)*(st->Fs/frame_size - 50); /* Mode selection depending on application and signal type */ if (st->application == OPUS_APPLICATION_RESTRICTED_LOWDELAY) { st->mode = MODE_CELT_ONLY; } else if (st->user_forced_mode == OPUS_AUTO) { #ifdef FUZZING /* Random mode switching */ if ((rand()&0xF)==0) { if ((rand()&0x1)==0) st->mode = MODE_CELT_ONLY; else st->mode = MODE_SILK_ONLY; } else { if (st->prev_mode==MODE_CELT_ONLY) st->mode = MODE_CELT_ONLY; else st->mode = MODE_SILK_ONLY; } #else opus_int32 mode_voice, mode_music; opus_int32 threshold; /* Interpolate based on stereo width */ mode_voice = (opus_int32)(MULT16_32_Q15(Q15ONE-stereo_width,mode_thresholds[0][0]) + MULT16_32_Q15(stereo_width,mode_thresholds[1][0])); mode_music = (opus_int32)(MULT16_32_Q15(Q15ONE-stereo_width,mode_thresholds[1][1]) + MULT16_32_Q15(stereo_width,mode_thresholds[1][1])); /* Interpolate based on speech/music probability */ threshold = mode_music + ((voice_est*voice_est*(mode_voice-mode_music))>>14); /* Bias towards SILK for VoIP because of some useful features */ if (st->application == OPUS_APPLICATION_VOIP) threshold += 8000; /*printf("%f %d\n", stereo_width/(float)Q15ONE, threshold);*/ /* Hysteresis */ if (st->prev_mode == MODE_CELT_ONLY) threshold -= 4000; else if (st->prev_mode>0) threshold += 4000; st->mode = (equiv_rate >= threshold) ? MODE_CELT_ONLY: MODE_SILK_ONLY; /* When FEC is enabled and there's enough packet loss, use SILK */ if (st->silk_mode.useInBandFEC && st->silk_mode.packetLossPercentage > (128-voice_est)>>4) st->mode = MODE_SILK_ONLY; /* When encoding voice and DTX is enabled, set the encoder to SILK mode (at least for now) */ if (st->silk_mode.useDTX && voice_est > 100) st->mode = MODE_SILK_ONLY; #endif } else { st->mode = st->user_forced_mode; } /* Override the chosen mode to make sure we meet the requested frame size */ if (st->mode != MODE_CELT_ONLY && frame_size < st->Fs/100) st->mode = MODE_CELT_ONLY; if (st->lfe) st->mode = MODE_CELT_ONLY; /* If max_data_bytes represents less than 8 kb/s, switch to CELT-only mode */ if (max_data_bytes < (frame_rate > 50 ? 12000 : 8000)*frame_size / (st->Fs * 8)) st->mode = MODE_CELT_ONLY; if (st->stream_channels == 1 && st->prev_channels ==2 && st->silk_mode.toMono==0 && st->mode != MODE_CELT_ONLY && st->prev_mode != MODE_CELT_ONLY) { /* Delay stereo->mono transition by two frames so that SILK can do a smooth downmix */ st->silk_mode.toMono = 1; st->stream_channels = 2; } else { st->silk_mode.toMono = 0; } if (st->prev_mode > 0 && ((st->mode != MODE_CELT_ONLY && st->prev_mode == MODE_CELT_ONLY) || (st->mode == MODE_CELT_ONLY && st->prev_mode != MODE_CELT_ONLY))) { redundancy = 1; celt_to_silk = (st->mode != MODE_CELT_ONLY); if (!celt_to_silk) { /* Switch to SILK/hybrid if frame size is 10 ms or more*/ if (frame_size >= st->Fs/100) { st->mode = st->prev_mode; to_celt = 1; } else { redundancy=0; } } } /* For the first frame at a new SILK bandwidth */ if (st->silk_bw_switch) { redundancy = 1; celt_to_silk = 1; st->silk_bw_switch = 0; prefill=1; } if (redundancy) { /* Fair share of the max size allowed */ redundancy_bytes = IMIN(257, max_data_bytes*(opus_int32)(st->Fs/200)/(frame_size+st->Fs/200)); /* For VBR, target the actual bitrate (subject to the limit above) */ if (st->use_vbr) redundancy_bytes = IMIN(redundancy_bytes, st->bitrate_bps/1600); } if (st->mode != MODE_CELT_ONLY && st->prev_mode == MODE_CELT_ONLY) { silk_EncControlStruct dummy; silk_InitEncoder( silk_enc, st->arch, &dummy); prefill=1; } /* Automatic (rate-dependent) bandwidth selection */ if (st->mode == MODE_CELT_ONLY || st->first || st->silk_mode.allowBandwidthSwitch) { const opus_int32 *voice_bandwidth_thresholds, *music_bandwidth_thresholds; opus_int32 bandwidth_thresholds[8]; int bandwidth = OPUS_BANDWIDTH_FULLBAND; opus_int32 equiv_rate2; equiv_rate2 = equiv_rate; if (st->mode != MODE_CELT_ONLY) { /* Adjust the threshold +/- 10% depending on complexity */ equiv_rate2 = equiv_rate2 * (45+st->silk_mode.complexity)/50; /* CBR is less efficient by ~1 kb/s */ if (!st->use_vbr) equiv_rate2 -= 1000; } if (st->channels==2 && st->force_channels!=1) { voice_bandwidth_thresholds = stereo_voice_bandwidth_thresholds; music_bandwidth_thresholds = stereo_music_bandwidth_thresholds; } else { voice_bandwidth_thresholds = mono_voice_bandwidth_thresholds; music_bandwidth_thresholds = mono_music_bandwidth_thresholds; } /* Interpolate bandwidth thresholds depending on voice estimation */ for (i=0;i<8;i++) { bandwidth_thresholds[i] = music_bandwidth_thresholds[i] + ((voice_est*voice_est*(voice_bandwidth_thresholds[i]-music_bandwidth_thresholds[i]))>>14); } do { int threshold, hysteresis; threshold = bandwidth_thresholds[2*(bandwidth-OPUS_BANDWIDTH_MEDIUMBAND)]; hysteresis = bandwidth_thresholds[2*(bandwidth-OPUS_BANDWIDTH_MEDIUMBAND)+1]; if (!st->first) { if (st->bandwidth >= bandwidth) threshold -= hysteresis; else threshold += hysteresis; } if (equiv_rate2 >= threshold) break; } while (--bandwidth>OPUS_BANDWIDTH_NARROWBAND); st->bandwidth = bandwidth; /* Prevents any transition to SWB/FB until the SILK layer has fully switched to WB mode and turned the variable LP filter off */ if (!st->first && st->mode != MODE_CELT_ONLY && !st->silk_mode.inWBmodeWithoutVariableLP && st->bandwidth > OPUS_BANDWIDTH_WIDEBAND) st->bandwidth = OPUS_BANDWIDTH_WIDEBAND; } if (st->bandwidth>st->max_bandwidth) st->bandwidth = st->max_bandwidth; if (st->user_bandwidth != OPUS_AUTO) st->bandwidth = st->user_bandwidth; /* This prevents us from using hybrid at unsafe CBR/max rates */ if (st->mode != MODE_CELT_ONLY && max_rate < 15000) { st->bandwidth = IMIN(st->bandwidth, OPUS_BANDWIDTH_WIDEBAND); } /* Prevents Opus from wasting bits on frequencies that are above the Nyquist rate of the input signal */ if (st->Fs <= 24000 && st->bandwidth > OPUS_BANDWIDTH_SUPERWIDEBAND) st->bandwidth = OPUS_BANDWIDTH_SUPERWIDEBAND; if (st->Fs <= 16000 && st->bandwidth > OPUS_BANDWIDTH_WIDEBAND) st->bandwidth = OPUS_BANDWIDTH_WIDEBAND; if (st->Fs <= 12000 && st->bandwidth > OPUS_BANDWIDTH_MEDIUMBAND) st->bandwidth = OPUS_BANDWIDTH_MEDIUMBAND; if (st->Fs <= 8000 && st->bandwidth > OPUS_BANDWIDTH_NARROWBAND) st->bandwidth = OPUS_BANDWIDTH_NARROWBAND; #ifndef DISABLE_FLOAT_API /* Use detected bandwidth to reduce the encoded bandwidth. */ if (st->detected_bandwidth && st->user_bandwidth == OPUS_AUTO) { int min_detected_bandwidth; /* Makes bandwidth detection more conservative just in case the detector gets it wrong when we could have coded a high bandwidth transparently. When operating in SILK/hybrid mode, we don't go below wideband to avoid more complicated switches that require redundancy. */ if (equiv_rate <= 18000*st->stream_channels && st->mode == MODE_CELT_ONLY) min_detected_bandwidth = OPUS_BANDWIDTH_NARROWBAND; else if (equiv_rate <= 24000*st->stream_channels && st->mode == MODE_CELT_ONLY) min_detected_bandwidth = OPUS_BANDWIDTH_MEDIUMBAND; else if (equiv_rate <= 30000*st->stream_channels) min_detected_bandwidth = OPUS_BANDWIDTH_WIDEBAND; else if (equiv_rate <= 44000*st->stream_channels) min_detected_bandwidth = OPUS_BANDWIDTH_SUPERWIDEBAND; else min_detected_bandwidth = OPUS_BANDWIDTH_FULLBAND; st->detected_bandwidth = IMAX(st->detected_bandwidth, min_detected_bandwidth); st->bandwidth = IMIN(st->bandwidth, st->detected_bandwidth); } #endif celt_encoder_ctl(celt_enc, OPUS_SET_LSB_DEPTH(lsb_depth)); /* CELT mode doesn't support mediumband, use wideband instead */ if (st->mode == MODE_CELT_ONLY && st->bandwidth == OPUS_BANDWIDTH_MEDIUMBAND) st->bandwidth = OPUS_BANDWIDTH_WIDEBAND; if (st->lfe) st->bandwidth = OPUS_BANDWIDTH_NARROWBAND; /* Can't support higher than wideband for >20 ms frames */ if (frame_size > st->Fs/50 && (st->mode == MODE_CELT_ONLY || st->bandwidth > OPUS_BANDWIDTH_WIDEBAND)) { VARDECL(unsigned char, tmp_data); int nb_frames; int bak_mode, bak_bandwidth, bak_channels, bak_to_mono; VARDECL(OpusRepacketizer, rp); opus_int32 bytes_per_frame; opus_int32 repacketize_len; #ifndef DISABLE_FLOAT_API if (analysis_read_pos_bak!= -1) { st->analysis.read_pos = analysis_read_pos_bak; st->analysis.read_subframe = analysis_read_subframe_bak; } #endif nb_frames = frame_size > st->Fs/25 ? 3 : 2; bytes_per_frame = IMIN(1276,(out_data_bytes-3)/nb_frames); ALLOC(tmp_data, nb_frames*bytes_per_frame, unsigned char); ALLOC(rp, 1, OpusRepacketizer); opus_repacketizer_init(rp); bak_mode = st->user_forced_mode; bak_bandwidth = st->user_bandwidth; bak_channels = st->force_channels; st->user_forced_mode = st->mode; st->user_bandwidth = st->bandwidth; st->force_channels = st->stream_channels; bak_to_mono = st->silk_mode.toMono; if (bak_to_mono) st->force_channels = 1; else st->prev_channels = st->stream_channels; for (i=0;isilk_mode.toMono = 0; /* When switching from SILK/Hybrid to CELT, only ask for a switch at the last frame */ if (to_celt && i==nb_frames-1) st->user_forced_mode = MODE_CELT_ONLY; tmp_len = opus_encode_native(st, pcm+i*(st->channels*st->Fs/50), st->Fs/50, tmp_data+i*bytes_per_frame, bytes_per_frame, lsb_depth, NULL, 0, c1, c2, analysis_channels, downmix, float_api); if (tmp_len<0) { RESTORE_STACK; return OPUS_INTERNAL_ERROR; } ret = opus_repacketizer_cat(rp, tmp_data+i*bytes_per_frame, tmp_len); if (ret<0) { RESTORE_STACK; return OPUS_INTERNAL_ERROR; } } if (st->use_vbr) repacketize_len = out_data_bytes; else repacketize_len = IMIN(3*st->bitrate_bps/(3*8*50/nb_frames), out_data_bytes); ret = opus_repacketizer_out_range_impl(rp, 0, nb_frames, data, repacketize_len, 0, !st->use_vbr); if (ret<0) { RESTORE_STACK; return OPUS_INTERNAL_ERROR; } st->user_forced_mode = bak_mode; st->user_bandwidth = bak_bandwidth; st->force_channels = bak_channels; st->silk_mode.toMono = bak_to_mono; RESTORE_STACK; return ret; } curr_bandwidth = st->bandwidth; /* Chooses the appropriate mode for speech *NEVER* switch to/from CELT-only mode here as this will invalidate some assumptions */ if (st->mode == MODE_SILK_ONLY && curr_bandwidth > OPUS_BANDWIDTH_WIDEBAND) st->mode = MODE_HYBRID; if (st->mode == MODE_HYBRID && curr_bandwidth <= OPUS_BANDWIDTH_WIDEBAND) st->mode = MODE_SILK_ONLY; /* printf("%d %d %d %d\n", st->bitrate_bps, st->stream_channels, st->mode, curr_bandwidth); */ bytes_target = IMIN(max_data_bytes-redundancy_bytes, st->bitrate_bps * frame_size / (st->Fs * 8)) - 1; data += 1; ec_enc_init(&enc, data, max_data_bytes-1); ALLOC(pcm_buf, (total_buffer+frame_size)*st->channels, opus_val16); OPUS_COPY(pcm_buf, &st->delay_buffer[(st->encoder_buffer-total_buffer)*st->channels], total_buffer*st->channels); if (st->mode == MODE_CELT_ONLY) hp_freq_smth1 = silk_LSHIFT( silk_lin2log( VARIABLE_HP_MIN_CUTOFF_HZ ), 8 ); else hp_freq_smth1 = ((silk_encoder*)silk_enc)->state_Fxx[0].sCmn.variable_HP_smth1_Q15; st->variable_HP_smth2_Q15 = silk_SMLAWB( st->variable_HP_smth2_Q15, hp_freq_smth1 - st->variable_HP_smth2_Q15, SILK_FIX_CONST( VARIABLE_HP_SMTH_COEF2, 16 ) ); /* convert from log scale to Hertz */ cutoff_Hz = silk_log2lin( silk_RSHIFT( st->variable_HP_smth2_Q15, 8 ) ); if (st->application == OPUS_APPLICATION_VOIP) { hp_cutoff(pcm, cutoff_Hz, &pcm_buf[total_buffer*st->channels], st->hp_mem, frame_size, st->channels, st->Fs); } else { dc_reject(pcm, 3, &pcm_buf[total_buffer*st->channels], st->hp_mem, frame_size, st->channels, st->Fs); } #ifndef FIXED_POINT if (float_api) { opus_val32 sum; sum = celt_inner_prod(&pcm_buf[total_buffer*st->channels], &pcm_buf[total_buffer*st->channels], frame_size*st->channels, st->arch); /* This should filter out both NaNs and ridiculous signals that could cause NaNs further down. */ if (!(sum < 1e9f) || celt_isnan(sum)) { OPUS_CLEAR(&pcm_buf[total_buffer*st->channels], frame_size*st->channels); st->hp_mem[0] = st->hp_mem[1] = st->hp_mem[2] = st->hp_mem[3] = 0; } } #endif /* SILK processing */ HB_gain = Q15ONE; if (st->mode != MODE_CELT_ONLY) { opus_int32 total_bitRate, celt_rate; #ifdef FIXED_POINT const opus_int16 *pcm_silk; #else VARDECL(opus_int16, pcm_silk); ALLOC(pcm_silk, st->channels*frame_size, opus_int16); #endif /* Distribute bits between SILK and CELT */ total_bitRate = 8 * bytes_target * frame_rate; if( st->mode == MODE_HYBRID ) { int HB_gain_ref; /* Base rate for SILK */ st->silk_mode.bitRate = st->stream_channels * ( 5000 + 1000 * ( st->Fs == 100 * frame_size ) ); if( curr_bandwidth == OPUS_BANDWIDTH_SUPERWIDEBAND ) { /* SILK gets 2/3 of the remaining bits */ st->silk_mode.bitRate += ( total_bitRate - st->silk_mode.bitRate ) * 2 / 3; } else { /* FULLBAND */ /* SILK gets 3/5 of the remaining bits */ st->silk_mode.bitRate += ( total_bitRate - st->silk_mode.bitRate ) * 3 / 5; } /* Don't let SILK use more than 80% */ if( st->silk_mode.bitRate > total_bitRate * 4/5 ) { st->silk_mode.bitRate = total_bitRate * 4/5; } if (!st->energy_masking) { /* Increasingly attenuate high band when it gets allocated fewer bits */ celt_rate = total_bitRate - st->silk_mode.bitRate; HB_gain_ref = (curr_bandwidth == OPUS_BANDWIDTH_SUPERWIDEBAND) ? 3000 : 3600; HB_gain = SHL32((opus_val32)celt_rate, 9) / SHR32((opus_val32)celt_rate + st->stream_channels * HB_gain_ref, 6); HB_gain = HB_gain < Q15ONE*6/7 ? HB_gain + Q15ONE/7 : Q15ONE; } } else { /* SILK gets all bits */ st->silk_mode.bitRate = total_bitRate; } /* Surround masking for SILK */ if (st->energy_masking && st->use_vbr && !st->lfe) { opus_val32 mask_sum=0; opus_val16 masking_depth; opus_int32 rate_offset; int c; int end = 17; opus_int16 srate = 16000; if (st->bandwidth == OPUS_BANDWIDTH_NARROWBAND) { end = 13; srate = 8000; } else if (st->bandwidth == OPUS_BANDWIDTH_MEDIUMBAND) { end = 15; srate = 12000; } for (c=0;cchannels;c++) { for(i=0;ienergy_masking[21*c+i], QCONST16(.5f, DB_SHIFT)), -QCONST16(2.0f, DB_SHIFT)); if (mask > 0) mask = HALF16(mask); mask_sum += mask; } } /* Conservative rate reduction, we cut the masking in half */ masking_depth = mask_sum / end*st->channels; masking_depth += QCONST16(.2f, DB_SHIFT); rate_offset = (opus_int32)PSHR32(MULT16_16(srate, masking_depth), DB_SHIFT); rate_offset = MAX32(rate_offset, -2*st->silk_mode.bitRate/3); /* Split the rate change between the SILK and CELT part for hybrid. */ if (st->bandwidth==OPUS_BANDWIDTH_SUPERWIDEBAND || st->bandwidth==OPUS_BANDWIDTH_FULLBAND) st->silk_mode.bitRate += 3*rate_offset/5; else st->silk_mode.bitRate += rate_offset; bytes_target += rate_offset * frame_size / (8 * st->Fs); } st->silk_mode.payloadSize_ms = 1000 * frame_size / st->Fs; st->silk_mode.nChannelsAPI = st->channels; st->silk_mode.nChannelsInternal = st->stream_channels; if (curr_bandwidth == OPUS_BANDWIDTH_NARROWBAND) { st->silk_mode.desiredInternalSampleRate = 8000; } else if (curr_bandwidth == OPUS_BANDWIDTH_MEDIUMBAND) { st->silk_mode.desiredInternalSampleRate = 12000; } else { silk_assert( st->mode == MODE_HYBRID || curr_bandwidth == OPUS_BANDWIDTH_WIDEBAND ); st->silk_mode.desiredInternalSampleRate = 16000; } if( st->mode == MODE_HYBRID ) { /* Don't allow bandwidth reduction at lowest bitrates in hybrid mode */ st->silk_mode.minInternalSampleRate = 16000; } else { st->silk_mode.minInternalSampleRate = 8000; } if (st->mode == MODE_SILK_ONLY) { opus_int32 effective_max_rate = max_rate; st->silk_mode.maxInternalSampleRate = 16000; if (frame_rate > 50) effective_max_rate = effective_max_rate*2/3; if (effective_max_rate < 13000) { st->silk_mode.maxInternalSampleRate = 12000; st->silk_mode.desiredInternalSampleRate = IMIN(12000, st->silk_mode.desiredInternalSampleRate); } if (effective_max_rate < 9600) { st->silk_mode.maxInternalSampleRate = 8000; st->silk_mode.desiredInternalSampleRate = IMIN(8000, st->silk_mode.desiredInternalSampleRate); } } else { st->silk_mode.maxInternalSampleRate = 16000; } st->silk_mode.useCBR = !st->use_vbr; /* Call SILK encoder for the low band */ nBytes = IMIN(1275, max_data_bytes-1-redundancy_bytes); st->silk_mode.maxBits = nBytes*8; /* Only allow up to 90% of the bits for hybrid mode*/ if (st->mode == MODE_HYBRID) st->silk_mode.maxBits = (opus_int32)st->silk_mode.maxBits*9/10; if (st->silk_mode.useCBR) { st->silk_mode.maxBits = (st->silk_mode.bitRate * frame_size / (st->Fs * 8))*8; /* Reduce the initial target to make it easier to reach the CBR rate */ st->silk_mode.bitRate = IMAX(1, st->silk_mode.bitRate-2000); } if (prefill) { opus_int32 zero=0; int prefill_offset; /* Use a smooth onset for the SILK prefill to avoid the encoder trying to encode a discontinuity. The exact location is what we need to avoid leaving any "gap" in the audio when mixing with the redundant CELT frame. Here we can afford to overwrite st->delay_buffer because the only thing that uses it before it gets rewritten is tmp_prefill[] and even then only the part after the ramp really gets used (rather than sent to the encoder and discarded) */ prefill_offset = st->channels*(st->encoder_buffer-st->delay_compensation-st->Fs/400); gain_fade(st->delay_buffer+prefill_offset, st->delay_buffer+prefill_offset, 0, Q15ONE, celt_mode->overlap, st->Fs/400, st->channels, celt_mode->window, st->Fs); OPUS_CLEAR(st->delay_buffer, prefill_offset); #ifdef FIXED_POINT pcm_silk = st->delay_buffer; #else for (i=0;iencoder_buffer*st->channels;i++) pcm_silk[i] = FLOAT2INT16(st->delay_buffer[i]); #endif silk_Encode( silk_enc, &st->silk_mode, pcm_silk, st->encoder_buffer, NULL, &zero, 1 ); } #ifdef FIXED_POINT pcm_silk = pcm_buf+total_buffer*st->channels; #else for (i=0;ichannels;i++) pcm_silk[i] = FLOAT2INT16(pcm_buf[total_buffer*st->channels + i]); #endif ret = silk_Encode( silk_enc, &st->silk_mode, pcm_silk, frame_size, &enc, &nBytes, 0 ); if( ret ) { /*fprintf (stderr, "SILK encode error: %d\n", ret);*/ /* Handle error */ RESTORE_STACK; return OPUS_INTERNAL_ERROR; } if (nBytes==0) { st->rangeFinal = 0; data[-1] = gen_toc(st->mode, st->Fs/frame_size, curr_bandwidth, st->stream_channels); RESTORE_STACK; return 1; } /* Extract SILK internal bandwidth for signaling in first byte */ if( st->mode == MODE_SILK_ONLY ) { if( st->silk_mode.internalSampleRate == 8000 ) { curr_bandwidth = OPUS_BANDWIDTH_NARROWBAND; } else if( st->silk_mode.internalSampleRate == 12000 ) { curr_bandwidth = OPUS_BANDWIDTH_MEDIUMBAND; } else if( st->silk_mode.internalSampleRate == 16000 ) { curr_bandwidth = OPUS_BANDWIDTH_WIDEBAND; } } else { silk_assert( st->silk_mode.internalSampleRate == 16000 ); } st->silk_mode.opusCanSwitch = st->silk_mode.switchReady; /* FIXME: How do we allocate the redundancy for CBR? */ if (st->silk_mode.opusCanSwitch) { redundancy = 1; celt_to_silk = 0; st->silk_bw_switch = 1; } } /* CELT processing */ { int endband=21; switch(curr_bandwidth) { case OPUS_BANDWIDTH_NARROWBAND: endband = 13; break; case OPUS_BANDWIDTH_MEDIUMBAND: case OPUS_BANDWIDTH_WIDEBAND: endband = 17; break; case OPUS_BANDWIDTH_SUPERWIDEBAND: endband = 19; break; case OPUS_BANDWIDTH_FULLBAND: endband = 21; break; } celt_encoder_ctl(celt_enc, CELT_SET_END_BAND(endband)); celt_encoder_ctl(celt_enc, CELT_SET_CHANNELS(st->stream_channels)); } celt_encoder_ctl(celt_enc, OPUS_SET_BITRATE(OPUS_BITRATE_MAX)); if (st->mode != MODE_SILK_ONLY) { opus_val32 celt_pred=2; celt_encoder_ctl(celt_enc, OPUS_SET_VBR(0)); /* We may still decide to disable prediction later */ if (st->silk_mode.reducedDependency) celt_pred = 0; celt_encoder_ctl(celt_enc, CELT_SET_PREDICTION(celt_pred)); if (st->mode == MODE_HYBRID) { int len; len = (ec_tell(&enc)+7)>>3; if (redundancy) len += st->mode == MODE_HYBRID ? 3 : 1; if( st->use_vbr ) { nb_compr_bytes = len + bytes_target - (st->silk_mode.bitRate * frame_size) / (8 * st->Fs); } else { /* check if SILK used up too much */ nb_compr_bytes = len > bytes_target ? len : bytes_target; } } else { if (st->use_vbr) { opus_int32 bonus=0; #ifndef DISABLE_FLOAT_API if (st->variable_duration==OPUS_FRAMESIZE_VARIABLE && frame_size != st->Fs/50) { bonus = (60*st->stream_channels+40)*(st->Fs/frame_size-50); if (analysis_info.valid) bonus = (opus_int32)(bonus*(1.f+.5f*analysis_info.tonality)); } #endif celt_encoder_ctl(celt_enc, OPUS_SET_VBR(1)); celt_encoder_ctl(celt_enc, OPUS_SET_VBR_CONSTRAINT(st->vbr_constraint)); celt_encoder_ctl(celt_enc, OPUS_SET_BITRATE(st->bitrate_bps+bonus)); nb_compr_bytes = max_data_bytes-1-redundancy_bytes; } else { nb_compr_bytes = bytes_target; } } } else { nb_compr_bytes = 0; } ALLOC(tmp_prefill, st->channels*st->Fs/400, opus_val16); if (st->mode != MODE_SILK_ONLY && st->mode != st->prev_mode && st->prev_mode > 0) { OPUS_COPY(tmp_prefill, &st->delay_buffer[(st->encoder_buffer-total_buffer-st->Fs/400)*st->channels], st->channels*st->Fs/400); } if (st->channels*(st->encoder_buffer-(frame_size+total_buffer)) > 0) { OPUS_MOVE(st->delay_buffer, &st->delay_buffer[st->channels*frame_size], st->channels*(st->encoder_buffer-frame_size-total_buffer)); OPUS_COPY(&st->delay_buffer[st->channels*(st->encoder_buffer-frame_size-total_buffer)], &pcm_buf[0], (frame_size+total_buffer)*st->channels); } else { OPUS_COPY(st->delay_buffer, &pcm_buf[(frame_size+total_buffer-st->encoder_buffer)*st->channels], st->encoder_buffer*st->channels); } /* gain_fade() and stereo_fade() need to be after the buffer copying because we don't want any of this to affect the SILK part */ if( st->prev_HB_gain < Q15ONE || HB_gain < Q15ONE ) { gain_fade(pcm_buf, pcm_buf, st->prev_HB_gain, HB_gain, celt_mode->overlap, frame_size, st->channels, celt_mode->window, st->Fs); } st->prev_HB_gain = HB_gain; if (st->mode != MODE_HYBRID || st->stream_channels==1) st->silk_mode.stereoWidth_Q14 = IMIN((1<<14),2*IMAX(0,equiv_rate-30000)); if( !st->energy_masking && st->channels == 2 ) { /* Apply stereo width reduction (at low bitrates) */ if( st->hybrid_stereo_width_Q14 < (1 << 14) || st->silk_mode.stereoWidth_Q14 < (1 << 14) ) { opus_val16 g1, g2; g1 = st->hybrid_stereo_width_Q14; g2 = (opus_val16)(st->silk_mode.stereoWidth_Q14); #ifdef FIXED_POINT g1 = g1==16384 ? Q15ONE : SHL16(g1,1); g2 = g2==16384 ? Q15ONE : SHL16(g2,1); #else g1 *= (1.f/16384); g2 *= (1.f/16384); #endif stereo_fade(pcm_buf, pcm_buf, g1, g2, celt_mode->overlap, frame_size, st->channels, celt_mode->window, st->Fs); st->hybrid_stereo_width_Q14 = st->silk_mode.stereoWidth_Q14; } } if ( st->mode != MODE_CELT_ONLY && ec_tell(&enc)+17+20*(st->mode == MODE_HYBRID) <= 8*(max_data_bytes-1)) { /* For SILK mode, the redundancy is inferred from the length */ if (st->mode == MODE_HYBRID && (redundancy || ec_tell(&enc)+37 <= 8*nb_compr_bytes)) ec_enc_bit_logp(&enc, redundancy, 12); if (redundancy) { int max_redundancy; ec_enc_bit_logp(&enc, celt_to_silk, 1); if (st->mode == MODE_HYBRID) max_redundancy = (max_data_bytes-1)-nb_compr_bytes; else max_redundancy = (max_data_bytes-1)-((ec_tell(&enc)+7)>>3); /* Target the same bit-rate for redundancy as for the rest, up to a max of 257 bytes */ redundancy_bytes = IMIN(max_redundancy, st->bitrate_bps/1600); redundancy_bytes = IMIN(257, IMAX(2, redundancy_bytes)); if (st->mode == MODE_HYBRID) ec_enc_uint(&enc, redundancy_bytes-2, 256); } } else { redundancy = 0; } if (!redundancy) { st->silk_bw_switch = 0; redundancy_bytes = 0; } if (st->mode != MODE_CELT_ONLY)start_band=17; if (st->mode == MODE_SILK_ONLY) { ret = (ec_tell(&enc)+7)>>3; ec_enc_done(&enc); nb_compr_bytes = ret; } else { nb_compr_bytes = IMIN((max_data_bytes-1)-redundancy_bytes, nb_compr_bytes); ec_enc_shrink(&enc, nb_compr_bytes); } #ifndef DISABLE_FLOAT_API if (redundancy || st->mode != MODE_SILK_ONLY) celt_encoder_ctl(celt_enc, CELT_SET_ANALYSIS(&analysis_info)); #endif /* 5 ms redundant frame for CELT->SILK */ if (redundancy && celt_to_silk) { int err; celt_encoder_ctl(celt_enc, CELT_SET_START_BAND(0)); celt_encoder_ctl(celt_enc, OPUS_SET_VBR(0)); err = celt_encode_with_ec(celt_enc, pcm_buf, st->Fs/200, data+nb_compr_bytes, redundancy_bytes, NULL); if (err < 0) { RESTORE_STACK; return OPUS_INTERNAL_ERROR; } celt_encoder_ctl(celt_enc, OPUS_GET_FINAL_RANGE(&redundant_rng)); celt_encoder_ctl(celt_enc, OPUS_RESET_STATE); } celt_encoder_ctl(celt_enc, CELT_SET_START_BAND(start_band)); if (st->mode != MODE_SILK_ONLY) { if (st->mode != st->prev_mode && st->prev_mode > 0) { unsigned char dummy[2]; celt_encoder_ctl(celt_enc, OPUS_RESET_STATE); /* Prefilling */ celt_encode_with_ec(celt_enc, tmp_prefill, st->Fs/400, dummy, 2, NULL); celt_encoder_ctl(celt_enc, CELT_SET_PREDICTION(0)); } /* If false, we already busted the budget and we'll end up with a "PLC packet" */ if (ec_tell(&enc) <= 8*nb_compr_bytes) { ret = celt_encode_with_ec(celt_enc, pcm_buf, frame_size, NULL, nb_compr_bytes, &enc); if (ret < 0) { RESTORE_STACK; return OPUS_INTERNAL_ERROR; } } } /* 5 ms redundant frame for SILK->CELT */ if (redundancy && !celt_to_silk) { int err; unsigned char dummy[2]; int N2, N4; N2 = st->Fs/200; N4 = st->Fs/400; celt_encoder_ctl(celt_enc, OPUS_RESET_STATE); celt_encoder_ctl(celt_enc, CELT_SET_START_BAND(0)); celt_encoder_ctl(celt_enc, CELT_SET_PREDICTION(0)); /* NOTE: We could speed this up slightly (at the expense of code size) by just adding a function that prefills the buffer */ celt_encode_with_ec(celt_enc, pcm_buf+st->channels*(frame_size-N2-N4), N4, dummy, 2, NULL); err = celt_encode_with_ec(celt_enc, pcm_buf+st->channels*(frame_size-N2), N2, data+nb_compr_bytes, redundancy_bytes, NULL); if (err < 0) { RESTORE_STACK; return OPUS_INTERNAL_ERROR; } celt_encoder_ctl(celt_enc, OPUS_GET_FINAL_RANGE(&redundant_rng)); } /* Signalling the mode in the first byte */ data--; data[0] = gen_toc(st->mode, st->Fs/frame_size, curr_bandwidth, st->stream_channels); st->rangeFinal = enc.rng ^ redundant_rng; if (to_celt) st->prev_mode = MODE_CELT_ONLY; else st->prev_mode = st->mode; st->prev_channels = st->stream_channels; st->prev_framesize = frame_size; st->first = 0; /* In the unlikely case that the SILK encoder busted its target, tell the decoder to call the PLC */ if (ec_tell(&enc) > (max_data_bytes-1)*8) { if (max_data_bytes < 2) { RESTORE_STACK; return OPUS_BUFFER_TOO_SMALL; } data[1] = 0; ret = 1; st->rangeFinal = 0; } else if (st->mode==MODE_SILK_ONLY&&!redundancy) { /*When in LPC only mode it's perfectly reasonable to strip off trailing zero bytes as the required range decoder behavior is to fill these in. This can't be done when the MDCT modes are used because the decoder needs to know the actual length for allocation purposes.*/ while(ret>2&&data[ret]==0)ret--; } /* Count ToC and redundancy */ ret += 1+redundancy_bytes; if (!st->use_vbr) { if (opus_packet_pad(data, ret, max_data_bytes) != OPUS_OK) { RESTORE_STACK; return OPUS_INTERNAL_ERROR; } ret = max_data_bytes; } RESTORE_STACK; return ret; } #ifdef FIXED_POINT #ifndef DISABLE_FLOAT_API opus_int32 opus_encode_float(OpusEncoder *st, const float *pcm, int analysis_frame_size, unsigned char *data, opus_int32 max_data_bytes) { int i, ret; int frame_size; int delay_compensation; VARDECL(opus_int16, in); ALLOC_STACK; if (st->application == OPUS_APPLICATION_RESTRICTED_LOWDELAY) delay_compensation = 0; else delay_compensation = st->delay_compensation; frame_size = compute_frame_size(pcm, analysis_frame_size, st->variable_duration, st->channels, st->Fs, st->bitrate_bps, delay_compensation, downmix_float, st->analysis.subframe_mem); ALLOC(in, frame_size*st->channels, opus_int16); for (i=0;ichannels;i++) in[i] = FLOAT2INT16(pcm[i]); ret = opus_encode_native(st, in, frame_size, data, max_data_bytes, 16, pcm, analysis_frame_size, 0, -2, st->channels, downmix_float, 1); RESTORE_STACK; return ret; } #endif opus_int32 opus_encode(OpusEncoder *st, const opus_int16 *pcm, int analysis_frame_size, unsigned char *data, opus_int32 out_data_bytes) { int frame_size; int delay_compensation; if (st->application == OPUS_APPLICATION_RESTRICTED_LOWDELAY) delay_compensation = 0; else delay_compensation = st->delay_compensation; frame_size = compute_frame_size(pcm, analysis_frame_size, st->variable_duration, st->channels, st->Fs, st->bitrate_bps, delay_compensation, downmix_int #ifndef DISABLE_FLOAT_API , st->analysis.subframe_mem #endif ); return opus_encode_native(st, pcm, frame_size, data, out_data_bytes, 16, pcm, analysis_frame_size, 0, -2, st->channels, downmix_int, 0); } #else opus_int32 opus_encode(OpusEncoder *st, const opus_int16 *pcm, int analysis_frame_size, unsigned char *data, opus_int32 max_data_bytes) { int i, ret; int frame_size; int delay_compensation; VARDECL(float, in); ALLOC_STACK; if (st->application == OPUS_APPLICATION_RESTRICTED_LOWDELAY) delay_compensation = 0; else delay_compensation = st->delay_compensation; frame_size = compute_frame_size(pcm, analysis_frame_size, st->variable_duration, st->channels, st->Fs, st->bitrate_bps, delay_compensation, downmix_int, st->analysis.subframe_mem); ALLOC(in, frame_size*st->channels, float); for (i=0;ichannels;i++) in[i] = (1.0f/32768)*pcm[i]; ret = opus_encode_native(st, in, frame_size, data, max_data_bytes, 16, pcm, analysis_frame_size, 0, -2, st->channels, downmix_int, 0); RESTORE_STACK; return ret; } opus_int32 opus_encode_float(OpusEncoder *st, const float *pcm, int analysis_frame_size, unsigned char *data, opus_int32 out_data_bytes) { int frame_size; int delay_compensation; if (st->application == OPUS_APPLICATION_RESTRICTED_LOWDELAY) delay_compensation = 0; else delay_compensation = st->delay_compensation; frame_size = compute_frame_size(pcm, analysis_frame_size, st->variable_duration, st->channels, st->Fs, st->bitrate_bps, delay_compensation, downmix_float, st->analysis.subframe_mem); return opus_encode_native(st, pcm, frame_size, data, out_data_bytes, 24, pcm, analysis_frame_size, 0, -2, st->channels, downmix_float, 1); } #endif int opus_encoder_ctl(OpusEncoder *st, int request, ...) { int ret; CELTEncoder *celt_enc; va_list ap; ret = OPUS_OK; va_start(ap, request); celt_enc = (CELTEncoder*)((char*)st+st->celt_enc_offset); switch (request) { case OPUS_SET_APPLICATION_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if ( (value != OPUS_APPLICATION_VOIP && value != OPUS_APPLICATION_AUDIO && value != OPUS_APPLICATION_RESTRICTED_LOWDELAY) || (!st->first && st->application != value)) { ret = OPUS_BAD_ARG; break; } st->application = value; } break; case OPUS_GET_APPLICATION_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->application; } break; case OPUS_SET_BITRATE_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if (value != OPUS_AUTO && value != OPUS_BITRATE_MAX) { if (value <= 0) goto bad_arg; else if (value <= 500) value = 500; else if (value > (opus_int32)300000*st->channels) value = (opus_int32)300000*st->channels; } st->user_bitrate_bps = value; } break; case OPUS_GET_BITRATE_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = user_bitrate_to_bitrate(st, st->prev_framesize, 1276); } break; case OPUS_SET_FORCE_CHANNELS_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if((value<1 || value>st->channels) && value != OPUS_AUTO) { goto bad_arg; } st->force_channels = value; } break; case OPUS_GET_FORCE_CHANNELS_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->force_channels; } break; case OPUS_SET_MAX_BANDWIDTH_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if (value < OPUS_BANDWIDTH_NARROWBAND || value > OPUS_BANDWIDTH_FULLBAND) { goto bad_arg; } st->max_bandwidth = value; if (st->max_bandwidth == OPUS_BANDWIDTH_NARROWBAND) { st->silk_mode.maxInternalSampleRate = 8000; } else if (st->max_bandwidth == OPUS_BANDWIDTH_MEDIUMBAND) { st->silk_mode.maxInternalSampleRate = 12000; } else { st->silk_mode.maxInternalSampleRate = 16000; } } break; case OPUS_GET_MAX_BANDWIDTH_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->max_bandwidth; } break; case OPUS_SET_BANDWIDTH_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if ((value < OPUS_BANDWIDTH_NARROWBAND || value > OPUS_BANDWIDTH_FULLBAND) && value != OPUS_AUTO) { goto bad_arg; } st->user_bandwidth = value; if (st->user_bandwidth == OPUS_BANDWIDTH_NARROWBAND) { st->silk_mode.maxInternalSampleRate = 8000; } else if (st->user_bandwidth == OPUS_BANDWIDTH_MEDIUMBAND) { st->silk_mode.maxInternalSampleRate = 12000; } else { st->silk_mode.maxInternalSampleRate = 16000; } } break; case OPUS_GET_BANDWIDTH_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->bandwidth; } break; case OPUS_SET_DTX_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if(value<0 || value>1) { goto bad_arg; } st->silk_mode.useDTX = value; } break; case OPUS_GET_DTX_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->silk_mode.useDTX; } break; case OPUS_SET_COMPLEXITY_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if(value<0 || value>10) { goto bad_arg; } st->silk_mode.complexity = value; celt_encoder_ctl(celt_enc, OPUS_SET_COMPLEXITY(value)); } break; case OPUS_GET_COMPLEXITY_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->silk_mode.complexity; } break; case OPUS_SET_INBAND_FEC_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if(value<0 || value>1) { goto bad_arg; } st->silk_mode.useInBandFEC = value; } break; case OPUS_GET_INBAND_FEC_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->silk_mode.useInBandFEC; } break; case OPUS_SET_PACKET_LOSS_PERC_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if (value < 0 || value > 100) { goto bad_arg; } st->silk_mode.packetLossPercentage = value; celt_encoder_ctl(celt_enc, OPUS_SET_PACKET_LOSS_PERC(value)); } break; case OPUS_GET_PACKET_LOSS_PERC_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->silk_mode.packetLossPercentage; } break; case OPUS_SET_VBR_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if(value<0 || value>1) { goto bad_arg; } st->use_vbr = value; st->silk_mode.useCBR = 1-value; } break; case OPUS_GET_VBR_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->use_vbr; } break; case OPUS_SET_VOICE_RATIO_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if (value<-1 || value>100) { goto bad_arg; } st->voice_ratio = value; } break; case OPUS_GET_VOICE_RATIO_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->voice_ratio; } break; case OPUS_SET_VBR_CONSTRAINT_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if(value<0 || value>1) { goto bad_arg; } st->vbr_constraint = value; } break; case OPUS_GET_VBR_CONSTRAINT_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->vbr_constraint; } break; case OPUS_SET_SIGNAL_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if(value!=OPUS_AUTO && value!=OPUS_SIGNAL_VOICE && value!=OPUS_SIGNAL_MUSIC) { goto bad_arg; } st->signal_type = value; } break; case OPUS_GET_SIGNAL_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->signal_type; } break; case OPUS_GET_LOOKAHEAD_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->Fs/400; if (st->application != OPUS_APPLICATION_RESTRICTED_LOWDELAY) *value += st->delay_compensation; } break; case OPUS_GET_SAMPLE_RATE_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->Fs; } break; case OPUS_GET_FINAL_RANGE_REQUEST: { opus_uint32 *value = va_arg(ap, opus_uint32*); if (!value) { goto bad_arg; } *value = st->rangeFinal; } break; case OPUS_SET_LSB_DEPTH_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if (value<8 || value>24) { goto bad_arg; } st->lsb_depth=value; } break; case OPUS_GET_LSB_DEPTH_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->lsb_depth; } break; case OPUS_SET_EXPERT_FRAME_DURATION_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if (value != OPUS_FRAMESIZE_ARG && value != OPUS_FRAMESIZE_2_5_MS && value != OPUS_FRAMESIZE_5_MS && value != OPUS_FRAMESIZE_10_MS && value != OPUS_FRAMESIZE_20_MS && value != OPUS_FRAMESIZE_40_MS && value != OPUS_FRAMESIZE_60_MS && value != OPUS_FRAMESIZE_VARIABLE) { goto bad_arg; } st->variable_duration = value; celt_encoder_ctl(celt_enc, OPUS_SET_EXPERT_FRAME_DURATION(value)); } break; case OPUS_GET_EXPERT_FRAME_DURATION_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->variable_duration; } break; case OPUS_SET_PREDICTION_DISABLED_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if (value > 1 || value < 0) goto bad_arg; st->silk_mode.reducedDependency = value; } break; case OPUS_GET_PREDICTION_DISABLED_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) goto bad_arg; *value = st->silk_mode.reducedDependency; } break; case OPUS_RESET_STATE: { void *silk_enc; silk_EncControlStruct dummy; char *start; silk_enc = (char*)st+st->silk_enc_offset; #ifndef DISABLE_FLOAT_API tonality_analysis_reset(&st->analysis); #endif start = (char*)&st->OPUS_ENCODER_RESET_START; OPUS_CLEAR(start, sizeof(OpusEncoder) - (start - (char*)st)); celt_encoder_ctl(celt_enc, OPUS_RESET_STATE); silk_InitEncoder( silk_enc, st->arch, &dummy ); st->stream_channels = st->channels; st->hybrid_stereo_width_Q14 = 1 << 14; st->prev_HB_gain = Q15ONE; st->first = 1; st->mode = MODE_HYBRID; st->bandwidth = OPUS_BANDWIDTH_FULLBAND; st->variable_HP_smth2_Q15 = silk_LSHIFT( silk_lin2log( VARIABLE_HP_MIN_CUTOFF_HZ ), 8 ); } break; case OPUS_SET_FORCE_MODE_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); if ((value < MODE_SILK_ONLY || value > MODE_CELT_ONLY) && value != OPUS_AUTO) { goto bad_arg; } st->user_forced_mode = value; } break; case OPUS_SET_LFE_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); st->lfe = value; ret = celt_encoder_ctl(celt_enc, OPUS_SET_LFE(value)); } break; case OPUS_SET_ENERGY_MASK_REQUEST: { opus_val16 *value = va_arg(ap, opus_val16*); st->energy_masking = value; ret = celt_encoder_ctl(celt_enc, OPUS_SET_ENERGY_MASK(value)); } break; case CELT_GET_MODE_REQUEST: { const CELTMode ** value = va_arg(ap, const CELTMode**); if (!value) { goto bad_arg; } ret = celt_encoder_ctl(celt_enc, CELT_GET_MODE(value)); } break; default: /* fprintf(stderr, "unknown opus_encoder_ctl() request: %d", request);*/ ret = OPUS_UNIMPLEMENTED; break; } va_end(ap); return ret; bad_arg: va_end(ap); return OPUS_BAD_ARG; } void opus_encoder_destroy(OpusEncoder *st) { opus_free(st); } ================================================ FILE: deps/pjsip/third_party/opus/src/opus_multistream.c ================================================ /* Copyright (c) 2011 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "opus_multistream.h" #include "opus.h" #include "opus_private.h" #include "stack_alloc.h" #include #include "float_cast.h" #include "os_support.h" int validate_layout(const ChannelLayout *layout) { int i, max_channel; max_channel = layout->nb_streams+layout->nb_coupled_streams; if (max_channel>255) return 0; for (i=0;inb_channels;i++) { if (layout->mapping[i] >= max_channel && layout->mapping[i] != 255) return 0; } return 1; } int get_left_channel(const ChannelLayout *layout, int stream_id, int prev) { int i; i = (prev<0) ? 0 : prev+1; for (;inb_channels;i++) { if (layout->mapping[i]==stream_id*2) return i; } return -1; } int get_right_channel(const ChannelLayout *layout, int stream_id, int prev) { int i; i = (prev<0) ? 0 : prev+1; for (;inb_channels;i++) { if (layout->mapping[i]==stream_id*2+1) return i; } return -1; } int get_mono_channel(const ChannelLayout *layout, int stream_id, int prev) { int i; i = (prev<0) ? 0 : prev+1; for (;inb_channels;i++) { if (layout->mapping[i]==stream_id+layout->nb_coupled_streams) return i; } return -1; } ================================================ FILE: deps/pjsip/third_party/opus/src/opus_multistream_decoder.c ================================================ /* Copyright (c) 2011 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "opus_multistream.h" #include "opus.h" #include "opus_private.h" #include "stack_alloc.h" #include #include "float_cast.h" #include "os_support.h" struct OpusMSDecoder { ChannelLayout layout; /* Decoder states go here */ }; /* DECODER */ opus_int32 opus_multistream_decoder_get_size(int nb_streams, int nb_coupled_streams) { int coupled_size; int mono_size; if(nb_streams<1||nb_coupled_streams>nb_streams||nb_coupled_streams<0)return 0; coupled_size = opus_decoder_get_size(2); mono_size = opus_decoder_get_size(1); return align(sizeof(OpusMSDecoder)) + nb_coupled_streams * align(coupled_size) + (nb_streams-nb_coupled_streams) * align(mono_size); } int opus_multistream_decoder_init( OpusMSDecoder *st, opus_int32 Fs, int channels, int streams, int coupled_streams, const unsigned char *mapping ) { int coupled_size; int mono_size; int i, ret; char *ptr; if ((channels>255) || (channels<1) || (coupled_streams>streams) || (streams<1) || (coupled_streams<0) || (streams>255-coupled_streams)) return OPUS_BAD_ARG; st->layout.nb_channels = channels; st->layout.nb_streams = streams; st->layout.nb_coupled_streams = coupled_streams; for (i=0;ilayout.nb_channels;i++) st->layout.mapping[i] = mapping[i]; if (!validate_layout(&st->layout)) return OPUS_BAD_ARG; ptr = (char*)st + align(sizeof(OpusMSDecoder)); coupled_size = opus_decoder_get_size(2); mono_size = opus_decoder_get_size(1); for (i=0;ilayout.nb_coupled_streams;i++) { ret=opus_decoder_init((OpusDecoder*)ptr, Fs, 2); if(ret!=OPUS_OK)return ret; ptr += align(coupled_size); } for (;ilayout.nb_streams;i++) { ret=opus_decoder_init((OpusDecoder*)ptr, Fs, 1); if(ret!=OPUS_OK)return ret; ptr += align(mono_size); } return OPUS_OK; } OpusMSDecoder *opus_multistream_decoder_create( opus_int32 Fs, int channels, int streams, int coupled_streams, const unsigned char *mapping, int *error ) { int ret; OpusMSDecoder *st; if ((channels>255) || (channels<1) || (coupled_streams>streams) || (streams<1) || (coupled_streams<0) || (streams>255-coupled_streams)) { if (error) *error = OPUS_BAD_ARG; return NULL; } st = (OpusMSDecoder *)opus_alloc(opus_multistream_decoder_get_size(streams, coupled_streams)); if (st==NULL) { if (error) *error = OPUS_ALLOC_FAIL; return NULL; } ret = opus_multistream_decoder_init(st, Fs, channels, streams, coupled_streams, mapping); if (error) *error = ret; if (ret != OPUS_OK) { opus_free(st); st = NULL; } return st; } typedef void (*opus_copy_channel_out_func)( void *dst, int dst_stride, int dst_channel, const opus_val16 *src, int src_stride, int frame_size ); static int opus_multistream_packet_validate(const unsigned char *data, opus_int32 len, int nb_streams, opus_int32 Fs) { int s; int count; unsigned char toc; opus_int16 size[48]; int samples=0; opus_int32 packet_offset; for (s=0;slayout.nb_streams-1) { RESTORE_STACK; return OPUS_INVALID_PACKET; } if (!do_plc) { int ret = opus_multistream_packet_validate(data, len, st->layout.nb_streams, Fs); if (ret < 0) { RESTORE_STACK; return ret; } else if (ret > frame_size) { RESTORE_STACK; return OPUS_BUFFER_TOO_SMALL; } } for (s=0;slayout.nb_streams;s++) { OpusDecoder *dec; int packet_offset, ret; dec = (OpusDecoder*)ptr; ptr += (s < st->layout.nb_coupled_streams) ? align(coupled_size) : align(mono_size); if (!do_plc && len<=0) { RESTORE_STACK; return OPUS_INTERNAL_ERROR; } packet_offset = 0; ret = opus_decode_native(dec, data, len, buf, frame_size, decode_fec, s!=st->layout.nb_streams-1, &packet_offset, soft_clip); data += packet_offset; len -= packet_offset; if (ret <= 0) { RESTORE_STACK; return ret; } frame_size = ret; if (s < st->layout.nb_coupled_streams) { int chan, prev; prev = -1; /* Copy "left" audio to the channel(s) where it belongs */ while ( (chan = get_left_channel(&st->layout, s, prev)) != -1) { (*copy_channel_out)(pcm, st->layout.nb_channels, chan, buf, 2, frame_size); prev = chan; } prev = -1; /* Copy "right" audio to the channel(s) where it belongs */ while ( (chan = get_right_channel(&st->layout, s, prev)) != -1) { (*copy_channel_out)(pcm, st->layout.nb_channels, chan, buf+1, 2, frame_size); prev = chan; } } else { int chan, prev; prev = -1; /* Copy audio to the channel(s) where it belongs */ while ( (chan = get_mono_channel(&st->layout, s, prev)) != -1) { (*copy_channel_out)(pcm, st->layout.nb_channels, chan, buf, 1, frame_size); prev = chan; } } } /* Handle muted channels */ for (c=0;clayout.nb_channels;c++) { if (st->layout.mapping[c] == 255) { (*copy_channel_out)(pcm, st->layout.nb_channels, c, NULL, 0, frame_size); } } RESTORE_STACK; return frame_size; } #if !defined(DISABLE_FLOAT_API) static void opus_copy_channel_out_float( void *dst, int dst_stride, int dst_channel, const opus_val16 *src, int src_stride, int frame_size ) { float *float_dst; opus_int32 i; float_dst = (float*)dst; if (src != NULL) { for (i=0;ilayout.nb_streams;s++) { OpusDecoder *dec; dec = (OpusDecoder*)ptr; if (s < st->layout.nb_coupled_streams) ptr += align(coupled_size); else ptr += align(mono_size); ret = opus_decoder_ctl(dec, request, &tmp); if (ret != OPUS_OK) break; *value ^= tmp; } } break; case OPUS_RESET_STATE: { int s; for (s=0;slayout.nb_streams;s++) { OpusDecoder *dec; dec = (OpusDecoder*)ptr; if (s < st->layout.nb_coupled_streams) ptr += align(coupled_size); else ptr += align(mono_size); ret = opus_decoder_ctl(dec, OPUS_RESET_STATE); if (ret != OPUS_OK) break; } } break; case OPUS_MULTISTREAM_GET_DECODER_STATE_REQUEST: { int s; opus_int32 stream_id; OpusDecoder **value; stream_id = va_arg(ap, opus_int32); if (stream_id<0 || stream_id >= st->layout.nb_streams) ret = OPUS_BAD_ARG; value = va_arg(ap, OpusDecoder**); if (!value) { goto bad_arg; } for (s=0;slayout.nb_coupled_streams) ptr += align(coupled_size); else ptr += align(mono_size); } *value = (OpusDecoder*)ptr; } break; case OPUS_SET_GAIN_REQUEST: { int s; /* This works for int32 params */ opus_int32 value = va_arg(ap, opus_int32); for (s=0;slayout.nb_streams;s++) { OpusDecoder *dec; dec = (OpusDecoder*)ptr; if (s < st->layout.nb_coupled_streams) ptr += align(coupled_size); else ptr += align(mono_size); ret = opus_decoder_ctl(dec, request, value); if (ret != OPUS_OK) break; } } break; default: ret = OPUS_UNIMPLEMENTED; break; } va_end(ap); return ret; bad_arg: va_end(ap); return OPUS_BAD_ARG; } void opus_multistream_decoder_destroy(OpusMSDecoder *st) { opus_free(st); } ================================================ FILE: deps/pjsip/third_party/opus/src/opus_multistream_encoder.c ================================================ /* Copyright (c) 2011 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "opus_multistream.h" #include "opus.h" #include "opus_private.h" #include "stack_alloc.h" #include #include "float_cast.h" #include "os_support.h" #include "mathops.h" #include "mdct.h" #include "modes.h" #include "bands.h" #include "quant_bands.h" #include "pitch.h" typedef struct { int nb_streams; int nb_coupled_streams; unsigned char mapping[8]; } VorbisLayout; /* Index is nb_channel-1*/ static const VorbisLayout vorbis_mappings[8] = { {1, 0, {0}}, /* 1: mono */ {1, 1, {0, 1}}, /* 2: stereo */ {2, 1, {0, 2, 1}}, /* 3: 1-d surround */ {2, 2, {0, 1, 2, 3}}, /* 4: quadraphonic surround */ {3, 2, {0, 4, 1, 2, 3}}, /* 5: 5-channel surround */ {4, 2, {0, 4, 1, 2, 3, 5}}, /* 6: 5.1 surround */ {4, 3, {0, 4, 1, 2, 3, 5, 6}}, /* 7: 6.1 surround */ {5, 3, {0, 6, 1, 2, 3, 4, 5, 7}}, /* 8: 7.1 surround */ }; typedef void (*opus_copy_channel_in_func)( opus_val16 *dst, int dst_stride, const void *src, int src_stride, int src_channel, int frame_size ); struct OpusMSEncoder { ChannelLayout layout; int arch; int lfe_stream; int application; int variable_duration; int surround; opus_int32 bitrate_bps; float subframe_mem[3]; /* Encoder states go here */ /* then opus_val32 window_mem[channels*120]; */ /* then opus_val32 preemph_mem[channels]; */ }; static opus_val32 *ms_get_preemph_mem(OpusMSEncoder *st) { int s; char *ptr; int coupled_size, mono_size; coupled_size = opus_encoder_get_size(2); mono_size = opus_encoder_get_size(1); ptr = (char*)st + align(sizeof(OpusMSEncoder)); for (s=0;slayout.nb_streams;s++) { if (s < st->layout.nb_coupled_streams) ptr += align(coupled_size); else ptr += align(mono_size); } /* void* cast avoids clang -Wcast-align warning */ return (opus_val32*)(void*)(ptr+st->layout.nb_channels*120*sizeof(opus_val32)); } static opus_val32 *ms_get_window_mem(OpusMSEncoder *st) { int s; char *ptr; int coupled_size, mono_size; coupled_size = opus_encoder_get_size(2); mono_size = opus_encoder_get_size(1); ptr = (char*)st + align(sizeof(OpusMSEncoder)); for (s=0;slayout.nb_streams;s++) { if (s < st->layout.nb_coupled_streams) ptr += align(coupled_size); else ptr += align(mono_size); } /* void* cast avoids clang -Wcast-align warning */ return (opus_val32*)(void*)ptr; } static int validate_encoder_layout(const ChannelLayout *layout) { int s; for (s=0;snb_streams;s++) { if (s < layout->nb_coupled_streams) { if (get_left_channel(layout, s, -1)==-1) return 0; if (get_right_channel(layout, s, -1)==-1) return 0; } else { if (get_mono_channel(layout, s, -1)==-1) return 0; } } return 1; } static void channel_pos(int channels, int pos[8]) { /* Position in the mix: 0 don't mix, 1: left, 2: center, 3:right */ if (channels==4) { pos[0]=1; pos[1]=3; pos[2]=1; pos[3]=3; } else if (channels==3||channels==5||channels==6) { pos[0]=1; pos[1]=2; pos[2]=3; pos[3]=1; pos[4]=3; pos[5]=0; } else if (channels==7) { pos[0]=1; pos[1]=2; pos[2]=3; pos[3]=1; pos[4]=3; pos[5]=2; pos[6]=0; } else if (channels==8) { pos[0]=1; pos[1]=2; pos[2]=3; pos[3]=1; pos[4]=3; pos[5]=1; pos[6]=3; pos[7]=0; } } #if 1 /* Computes a rough approximation of log2(2^a + 2^b) */ static opus_val16 logSum(opus_val16 a, opus_val16 b) { opus_val16 max; opus_val32 diff; opus_val16 frac; static const opus_val16 diff_table[17] = { QCONST16(0.5000000f, DB_SHIFT), QCONST16(0.2924813f, DB_SHIFT), QCONST16(0.1609640f, DB_SHIFT), QCONST16(0.0849625f, DB_SHIFT), QCONST16(0.0437314f, DB_SHIFT), QCONST16(0.0221971f, DB_SHIFT), QCONST16(0.0111839f, DB_SHIFT), QCONST16(0.0056136f, DB_SHIFT), QCONST16(0.0028123f, DB_SHIFT) }; int low; if (a>b) { max = a; diff = SUB32(EXTEND32(a),EXTEND32(b)); } else { max = b; diff = SUB32(EXTEND32(b),EXTEND32(a)); } if (!(diff < QCONST16(8.f, DB_SHIFT))) /* inverted to catch NaNs */ return max; #ifdef FIXED_POINT low = SHR32(diff, DB_SHIFT-1); frac = SHL16(diff - SHL16(low, DB_SHIFT-1), 16-DB_SHIFT); #else low = (int)floor(2*diff); frac = 2*diff - low; #endif return max + diff_table[low] + MULT16_16_Q15(frac, SUB16(diff_table[low+1], diff_table[low])); } #else opus_val16 logSum(opus_val16 a, opus_val16 b) { return log2(pow(4, a)+ pow(4, b))/2; } #endif void surround_analysis(const CELTMode *celt_mode, const void *pcm, opus_val16 *bandLogE, opus_val32 *mem, opus_val32 *preemph_mem, int len, int overlap, int channels, int rate, opus_copy_channel_in_func copy_channel_in, int arch ) { int c; int i; int LM; int pos[8] = {0}; int upsample; int frame_size; opus_val16 channel_offset; opus_val32 bandE[21]; opus_val16 maskLogE[3][21]; VARDECL(opus_val32, in); VARDECL(opus_val16, x); VARDECL(opus_val32, freq); SAVE_STACK; upsample = resampling_factor(rate); frame_size = len*upsample; for (LM=0;LMmaxLM;LM++) if (celt_mode->shortMdctSize<preemph, preemph_mem+c, 0); #ifndef FIXED_POINT { opus_val32 sum; sum = celt_inner_prod(in, in, frame_size+overlap, 0); /* This should filter out both NaNs and ridiculous signals that could cause NaNs further down. */ if (!(sum < 1e9f) || celt_isnan(sum)) { OPUS_CLEAR(in, frame_size+overlap); preemph_mem[c] = 0; } } #endif clt_mdct_forward(&celt_mode->mdct, in, freq, celt_mode->window, overlap, celt_mode->maxLM-LM, 1, arch); if (upsample != 1) { int bound = len; for (i=0;i=0;i--) bandLogE[21*c+i] = MAX16(bandLogE[21*c+i], bandLogE[21*c+i+1]-QCONST16(2.f, DB_SHIFT)); if (pos[c]==1) { for (i=0;i<21;i++) maskLogE[0][i] = logSum(maskLogE[0][i], bandLogE[21*c+i]); } else if (pos[c]==3) { for (i=0;i<21;i++) maskLogE[2][i] = logSum(maskLogE[2][i], bandLogE[21*c+i]); } else if (pos[c]==2) { for (i=0;i<21;i++) { maskLogE[0][i] = logSum(maskLogE[0][i], bandLogE[21*c+i]-QCONST16(.5f, DB_SHIFT)); maskLogE[2][i] = logSum(maskLogE[2][i], bandLogE[21*c+i]-QCONST16(.5f, DB_SHIFT)); } } #if 0 for (i=0;i<21;i++) printf("%f ", bandLogE[21*c+i]); float sum=0; for (i=0;i<21;i++) sum += bandLogE[21*c+i]; printf("%f ", sum/21); #endif OPUS_COPY(mem+c*overlap, in+frame_size, overlap); } for (i=0;i<21;i++) maskLogE[1][i] = MIN32(maskLogE[0][i],maskLogE[2][i]); channel_offset = HALF16(celt_log2(QCONST32(2.f,14)/(channels-1))); for (c=0;c<3;c++) for (i=0;i<21;i++) maskLogE[c][i] += channel_offset; #if 0 for (c=0;c<3;c++) { for (i=0;i<21;i++) printf("%f ", maskLogE[c][i]); } #endif for (c=0;cnb_streams||nb_coupled_streams<0)return 0; coupled_size = opus_encoder_get_size(2); mono_size = opus_encoder_get_size(1); return align(sizeof(OpusMSEncoder)) + nb_coupled_streams * align(coupled_size) + (nb_streams-nb_coupled_streams) * align(mono_size); } opus_int32 opus_multistream_surround_encoder_get_size(int channels, int mapping_family) { int nb_streams; int nb_coupled_streams; opus_int32 size; if (mapping_family==0) { if (channels==1) { nb_streams=1; nb_coupled_streams=0; } else if (channels==2) { nb_streams=1; nb_coupled_streams=1; } else return 0; } else if (mapping_family==1 && channels<=8 && channels>=1) { nb_streams=vorbis_mappings[channels-1].nb_streams; nb_coupled_streams=vorbis_mappings[channels-1].nb_coupled_streams; } else if (mapping_family==255) { nb_streams=channels; nb_coupled_streams=0; } else return 0; size = opus_multistream_encoder_get_size(nb_streams, nb_coupled_streams); if (channels>2) { size += channels*(120*sizeof(opus_val32) + sizeof(opus_val32)); } return size; } static int opus_multistream_encoder_init_impl( OpusMSEncoder *st, opus_int32 Fs, int channels, int streams, int coupled_streams, const unsigned char *mapping, int application, int surround ) { int coupled_size; int mono_size; int i, ret; char *ptr; if ((channels>255) || (channels<1) || (coupled_streams>streams) || (streams<1) || (coupled_streams<0) || (streams>255-coupled_streams)) return OPUS_BAD_ARG; st->arch = opus_select_arch(); st->layout.nb_channels = channels; st->layout.nb_streams = streams; st->layout.nb_coupled_streams = coupled_streams; st->subframe_mem[0]=st->subframe_mem[1]=st->subframe_mem[2]=0; if (!surround) st->lfe_stream = -1; st->bitrate_bps = OPUS_AUTO; st->application = application; st->variable_duration = OPUS_FRAMESIZE_ARG; for (i=0;ilayout.nb_channels;i++) st->layout.mapping[i] = mapping[i]; if (!validate_layout(&st->layout) || !validate_encoder_layout(&st->layout)) return OPUS_BAD_ARG; ptr = (char*)st + align(sizeof(OpusMSEncoder)); coupled_size = opus_encoder_get_size(2); mono_size = opus_encoder_get_size(1); for (i=0;ilayout.nb_coupled_streams;i++) { ret = opus_encoder_init((OpusEncoder*)ptr, Fs, 2, application); if(ret!=OPUS_OK)return ret; if (i==st->lfe_stream) opus_encoder_ctl((OpusEncoder*)ptr, OPUS_SET_LFE(1)); ptr += align(coupled_size); } for (;ilayout.nb_streams;i++) { ret = opus_encoder_init((OpusEncoder*)ptr, Fs, 1, application); if (i==st->lfe_stream) opus_encoder_ctl((OpusEncoder*)ptr, OPUS_SET_LFE(1)); if(ret!=OPUS_OK)return ret; ptr += align(mono_size); } if (surround) { OPUS_CLEAR(ms_get_preemph_mem(st), channels); OPUS_CLEAR(ms_get_window_mem(st), channels*120); } st->surround = surround; return OPUS_OK; } int opus_multistream_encoder_init( OpusMSEncoder *st, opus_int32 Fs, int channels, int streams, int coupled_streams, const unsigned char *mapping, int application ) { return opus_multistream_encoder_init_impl(st, Fs, channels, streams, coupled_streams, mapping, application, 0); } int opus_multistream_surround_encoder_init( OpusMSEncoder *st, opus_int32 Fs, int channels, int mapping_family, int *streams, int *coupled_streams, unsigned char *mapping, int application ) { if ((channels>255) || (channels<1)) return OPUS_BAD_ARG; st->lfe_stream = -1; if (mapping_family==0) { if (channels==1) { *streams=1; *coupled_streams=0; mapping[0]=0; } else if (channels==2) { *streams=1; *coupled_streams=1; mapping[0]=0; mapping[1]=1; } else return OPUS_UNIMPLEMENTED; } else if (mapping_family==1 && channels<=8 && channels>=1) { int i; *streams=vorbis_mappings[channels-1].nb_streams; *coupled_streams=vorbis_mappings[channels-1].nb_coupled_streams; for (i=0;i=6) st->lfe_stream = *streams-1; } else if (mapping_family==255) { int i; *streams=channels; *coupled_streams=0; for(i=0;i2&&mapping_family==1); } OpusMSEncoder *opus_multistream_encoder_create( opus_int32 Fs, int channels, int streams, int coupled_streams, const unsigned char *mapping, int application, int *error ) { int ret; OpusMSEncoder *st; if ((channels>255) || (channels<1) || (coupled_streams>streams) || (streams<1) || (coupled_streams<0) || (streams>255-coupled_streams)) { if (error) *error = OPUS_BAD_ARG; return NULL; } st = (OpusMSEncoder *)opus_alloc(opus_multistream_encoder_get_size(streams, coupled_streams)); if (st==NULL) { if (error) *error = OPUS_ALLOC_FAIL; return NULL; } ret = opus_multistream_encoder_init(st, Fs, channels, streams, coupled_streams, mapping, application); if (ret != OPUS_OK) { opus_free(st); st = NULL; } if (error) *error = ret; return st; } OpusMSEncoder *opus_multistream_surround_encoder_create( opus_int32 Fs, int channels, int mapping_family, int *streams, int *coupled_streams, unsigned char *mapping, int application, int *error ) { int ret; opus_int32 size; OpusMSEncoder *st; if ((channels>255) || (channels<1)) { if (error) *error = OPUS_BAD_ARG; return NULL; } size = opus_multistream_surround_encoder_get_size(channels, mapping_family); if (!size) { if (error) *error = OPUS_UNIMPLEMENTED; return NULL; } st = (OpusMSEncoder *)opus_alloc(size); if (st==NULL) { if (error) *error = OPUS_ALLOC_FAIL; return NULL; } ret = opus_multistream_surround_encoder_init(st, Fs, channels, mapping_family, streams, coupled_streams, mapping, application); if (ret != OPUS_OK) { opus_free(st); st = NULL; } if (error) *error = ret; return st; } static opus_int32 surround_rate_allocation( OpusMSEncoder *st, opus_int32 *rate, int frame_size ) { int i; opus_int32 channel_rate; opus_int32 Fs; char *ptr; int stream_offset; int lfe_offset; int coupled_ratio; /* Q8 */ int lfe_ratio; /* Q8 */ opus_int32 rate_sum=0; ptr = (char*)st + align(sizeof(OpusMSEncoder)); opus_encoder_ctl((OpusEncoder*)ptr, OPUS_GET_SAMPLE_RATE(&Fs)); if (st->bitrate_bps > st->layout.nb_channels*40000) stream_offset = 20000; else stream_offset = st->bitrate_bps/st->layout.nb_channels/2; stream_offset += 60*(Fs/frame_size-50); /* We start by giving each stream (coupled or uncoupled) the same bitrate. This models the main saving of coupled channels over uncoupled. */ /* The LFE stream is an exception to the above and gets fewer bits. */ lfe_offset = 3500 + 60*(Fs/frame_size-50); /* Coupled streams get twice the mono rate after the first 20 kb/s. */ coupled_ratio = 512; /* Should depend on the bitrate, for now we assume LFE gets 1/8 the bits of mono */ lfe_ratio = 32; /* Compute bitrate allocation between streams */ if (st->bitrate_bps==OPUS_AUTO) { channel_rate = Fs+60*Fs/frame_size; } else if (st->bitrate_bps==OPUS_BITRATE_MAX) { channel_rate = 300000; } else { int nb_lfe; int nb_uncoupled; int nb_coupled; int total; nb_lfe = (st->lfe_stream!=-1); nb_coupled = st->layout.nb_coupled_streams; nb_uncoupled = st->layout.nb_streams-nb_coupled-nb_lfe; total = (nb_uncoupled<<8) /* mono */ + coupled_ratio*nb_coupled /* stereo */ + nb_lfe*lfe_ratio; channel_rate = 256*(st->bitrate_bps-lfe_offset*nb_lfe-stream_offset*(nb_coupled+nb_uncoupled))/total; } #ifndef FIXED_POINT if (st->variable_duration==OPUS_FRAMESIZE_VARIABLE && frame_size != Fs/50) { opus_int32 bonus; bonus = 60*(Fs/frame_size-50); channel_rate += bonus; } #endif for (i=0;ilayout.nb_streams;i++) { if (ilayout.nb_coupled_streams) rate[i] = stream_offset+(channel_rate*coupled_ratio>>8); else if (i!=st->lfe_stream) rate[i] = stream_offset+channel_rate; else rate[i] = lfe_offset+(channel_rate*lfe_ratio>>8); rate[i] = IMAX(rate[i], 500); rate_sum += rate[i]; } return rate_sum; } /* Max size in case the encoder decides to return three frames */ #define MS_FRAME_TMP (3*1275+7) static int opus_multistream_encode_native ( OpusMSEncoder *st, opus_copy_channel_in_func copy_channel_in, const void *pcm, int analysis_frame_size, unsigned char *data, opus_int32 max_data_bytes, int lsb_depth, downmix_func downmix, int float_api ) { opus_int32 Fs; int coupled_size; int mono_size; int s; char *ptr; int tot_size; VARDECL(opus_val16, buf); VARDECL(opus_val16, bandSMR); unsigned char tmp_data[MS_FRAME_TMP]; OpusRepacketizer rp; opus_int32 vbr; const CELTMode *celt_mode; opus_int32 bitrates[256]; opus_val16 bandLogE[42]; opus_val32 *mem = NULL; opus_val32 *preemph_mem=NULL; int frame_size; opus_int32 rate_sum; opus_int32 smallest_packet; ALLOC_STACK; if (st->surround) { preemph_mem = ms_get_preemph_mem(st); mem = ms_get_window_mem(st); } ptr = (char*)st + align(sizeof(OpusMSEncoder)); opus_encoder_ctl((OpusEncoder*)ptr, OPUS_GET_SAMPLE_RATE(&Fs)); opus_encoder_ctl((OpusEncoder*)ptr, OPUS_GET_VBR(&vbr)); opus_encoder_ctl((OpusEncoder*)ptr, CELT_GET_MODE(&celt_mode)); { opus_int32 delay_compensation; int channels; channels = st->layout.nb_streams + st->layout.nb_coupled_streams; opus_encoder_ctl((OpusEncoder*)ptr, OPUS_GET_LOOKAHEAD(&delay_compensation)); delay_compensation -= Fs/400; frame_size = compute_frame_size(pcm, analysis_frame_size, st->variable_duration, channels, Fs, st->bitrate_bps, delay_compensation, downmix #ifndef DISABLE_FLOAT_API , st->subframe_mem #endif ); } if (400*frame_size < Fs) { RESTORE_STACK; return OPUS_BAD_ARG; } /* Validate frame_size before using it to allocate stack space. This mirrors the checks in opus_encode[_float](). */ if (400*frame_size != Fs && 200*frame_size != Fs && 100*frame_size != Fs && 50*frame_size != Fs && 25*frame_size != Fs && 50*frame_size != 3*Fs) { RESTORE_STACK; return OPUS_BAD_ARG; } /* Smallest packet the encoder can produce. */ smallest_packet = st->layout.nb_streams*2-1; if (max_data_bytes < smallest_packet) { RESTORE_STACK; return OPUS_BUFFER_TOO_SMALL; } ALLOC(buf, 2*frame_size, opus_val16); coupled_size = opus_encoder_get_size(2); mono_size = opus_encoder_get_size(1); ALLOC(bandSMR, 21*st->layout.nb_channels, opus_val16); if (st->surround) { surround_analysis(celt_mode, pcm, bandSMR, mem, preemph_mem, frame_size, 120, st->layout.nb_channels, Fs, copy_channel_in, st->arch); } /* Compute bitrate allocation between streams (this could be a lot better) */ rate_sum = surround_rate_allocation(st, bitrates, frame_size); if (!vbr) { if (st->bitrate_bps == OPUS_AUTO) { max_data_bytes = IMIN(max_data_bytes, 3*rate_sum/(3*8*Fs/frame_size)); } else if (st->bitrate_bps != OPUS_BITRATE_MAX) { max_data_bytes = IMIN(max_data_bytes, IMAX(smallest_packet, 3*st->bitrate_bps/(3*8*Fs/frame_size))); } } ptr = (char*)st + align(sizeof(OpusMSEncoder)); for (s=0;slayout.nb_streams;s++) { OpusEncoder *enc; enc = (OpusEncoder*)ptr; if (s < st->layout.nb_coupled_streams) ptr += align(coupled_size); else ptr += align(mono_size); opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrates[s])); if (st->surround) { opus_int32 equiv_rate; equiv_rate = st->bitrate_bps; if (frame_size*50 < Fs) equiv_rate -= 60*(Fs/frame_size - 50)*st->layout.nb_channels; if (equiv_rate > 10000*st->layout.nb_channels) opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)); else if (equiv_rate > 7000*st->layout.nb_channels) opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND)); else if (equiv_rate > 5000*st->layout.nb_channels) opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND)); else opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND)); if (s < st->layout.nb_coupled_streams) { /* To preserve the spatial image, force stereo CELT on coupled streams */ opus_encoder_ctl(enc, OPUS_SET_FORCE_MODE(MODE_CELT_ONLY)); opus_encoder_ctl(enc, OPUS_SET_FORCE_CHANNELS(2)); } } } ptr = (char*)st + align(sizeof(OpusMSEncoder)); /* Counting ToC */ tot_size = 0; for (s=0;slayout.nb_streams;s++) { OpusEncoder *enc; int len; int curr_max; int c1, c2; opus_repacketizer_init(&rp); enc = (OpusEncoder*)ptr; if (s < st->layout.nb_coupled_streams) { int i; int left, right; left = get_left_channel(&st->layout, s, -1); right = get_right_channel(&st->layout, s, -1); (*copy_channel_in)(buf, 2, pcm, st->layout.nb_channels, left, frame_size); (*copy_channel_in)(buf+1, 2, pcm, st->layout.nb_channels, right, frame_size); ptr += align(coupled_size); if (st->surround) { for (i=0;i<21;i++) { bandLogE[i] = bandSMR[21*left+i]; bandLogE[21+i] = bandSMR[21*right+i]; } } c1 = left; c2 = right; } else { int i; int chan = get_mono_channel(&st->layout, s, -1); (*copy_channel_in)(buf, 1, pcm, st->layout.nb_channels, chan, frame_size); ptr += align(mono_size); if (st->surround) { for (i=0;i<21;i++) bandLogE[i] = bandSMR[21*chan+i]; } c1 = chan; c2 = -1; } if (st->surround) opus_encoder_ctl(enc, OPUS_SET_ENERGY_MASK(bandLogE)); /* number of bytes left (+Toc) */ curr_max = max_data_bytes - tot_size; /* Reserve one byte for the last stream and two for the others */ curr_max -= IMAX(0,2*(st->layout.nb_streams-s-1)-1); curr_max = IMIN(curr_max,MS_FRAME_TMP); /* Repacketizer will add one or two bytes for self-delimited frames */ if (s != st->layout.nb_streams-1) curr_max -= curr_max>253 ? 2 : 1; if (!vbr && s == st->layout.nb_streams-1) opus_encoder_ctl(enc, OPUS_SET_BITRATE(curr_max*(8*Fs/frame_size))); len = opus_encode_native(enc, buf, frame_size, tmp_data, curr_max, lsb_depth, pcm, analysis_frame_size, c1, c2, st->layout.nb_channels, downmix, float_api); if (len<0) { RESTORE_STACK; return len; } /* We need to use the repacketizer to add the self-delimiting lengths while taking into account the fact that the encoder can now return more than one frame at a time (e.g. 60 ms CELT-only) */ opus_repacketizer_cat(&rp, tmp_data, len); len = opus_repacketizer_out_range_impl(&rp, 0, opus_repacketizer_get_nb_frames(&rp), data, max_data_bytes-tot_size, s != st->layout.nb_streams-1, !vbr && s == st->layout.nb_streams-1); data += len; tot_size += len; } /*printf("\n");*/ RESTORE_STACK; return tot_size; } #if !defined(DISABLE_FLOAT_API) static void opus_copy_channel_in_float( opus_val16 *dst, int dst_stride, const void *src, int src_stride, int src_channel, int frame_size ) { const float *float_src; opus_int32 i; float_src = (const float *)src; for (i=0;ibitrate_bps = value; } break; case OPUS_GET_BITRATE_REQUEST: { int s; opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = 0; for (s=0;slayout.nb_streams;s++) { opus_int32 rate; OpusEncoder *enc; enc = (OpusEncoder*)ptr; if (s < st->layout.nb_coupled_streams) ptr += align(coupled_size); else ptr += align(mono_size); opus_encoder_ctl(enc, request, &rate); *value += rate; } } break; case OPUS_GET_LSB_DEPTH_REQUEST: case OPUS_GET_VBR_REQUEST: case OPUS_GET_APPLICATION_REQUEST: case OPUS_GET_BANDWIDTH_REQUEST: case OPUS_GET_COMPLEXITY_REQUEST: case OPUS_GET_PACKET_LOSS_PERC_REQUEST: case OPUS_GET_DTX_REQUEST: case OPUS_GET_VOICE_RATIO_REQUEST: case OPUS_GET_VBR_CONSTRAINT_REQUEST: case OPUS_GET_SIGNAL_REQUEST: case OPUS_GET_LOOKAHEAD_REQUEST: case OPUS_GET_SAMPLE_RATE_REQUEST: case OPUS_GET_INBAND_FEC_REQUEST: case OPUS_GET_FORCE_CHANNELS_REQUEST: case OPUS_GET_PREDICTION_DISABLED_REQUEST: { OpusEncoder *enc; /* For int32* GET params, just query the first stream */ opus_int32 *value = va_arg(ap, opus_int32*); enc = (OpusEncoder*)ptr; ret = opus_encoder_ctl(enc, request, value); } break; case OPUS_GET_FINAL_RANGE_REQUEST: { int s; opus_uint32 *value = va_arg(ap, opus_uint32*); opus_uint32 tmp; if (!value) { goto bad_arg; } *value=0; for (s=0;slayout.nb_streams;s++) { OpusEncoder *enc; enc = (OpusEncoder*)ptr; if (s < st->layout.nb_coupled_streams) ptr += align(coupled_size); else ptr += align(mono_size); ret = opus_encoder_ctl(enc, request, &tmp); if (ret != OPUS_OK) break; *value ^= tmp; } } break; case OPUS_SET_LSB_DEPTH_REQUEST: case OPUS_SET_COMPLEXITY_REQUEST: case OPUS_SET_VBR_REQUEST: case OPUS_SET_VBR_CONSTRAINT_REQUEST: case OPUS_SET_MAX_BANDWIDTH_REQUEST: case OPUS_SET_BANDWIDTH_REQUEST: case OPUS_SET_SIGNAL_REQUEST: case OPUS_SET_APPLICATION_REQUEST: case OPUS_SET_INBAND_FEC_REQUEST: case OPUS_SET_PACKET_LOSS_PERC_REQUEST: case OPUS_SET_DTX_REQUEST: case OPUS_SET_FORCE_MODE_REQUEST: case OPUS_SET_FORCE_CHANNELS_REQUEST: case OPUS_SET_PREDICTION_DISABLED_REQUEST: { int s; /* This works for int32 params */ opus_int32 value = va_arg(ap, opus_int32); for (s=0;slayout.nb_streams;s++) { OpusEncoder *enc; enc = (OpusEncoder*)ptr; if (s < st->layout.nb_coupled_streams) ptr += align(coupled_size); else ptr += align(mono_size); ret = opus_encoder_ctl(enc, request, value); if (ret != OPUS_OK) break; } } break; case OPUS_MULTISTREAM_GET_ENCODER_STATE_REQUEST: { int s; opus_int32 stream_id; OpusEncoder **value; stream_id = va_arg(ap, opus_int32); if (stream_id<0 || stream_id >= st->layout.nb_streams) ret = OPUS_BAD_ARG; value = va_arg(ap, OpusEncoder**); if (!value) { goto bad_arg; } for (s=0;slayout.nb_coupled_streams) ptr += align(coupled_size); else ptr += align(mono_size); } *value = (OpusEncoder*)ptr; } break; case OPUS_SET_EXPERT_FRAME_DURATION_REQUEST: { opus_int32 value = va_arg(ap, opus_int32); st->variable_duration = value; } break; case OPUS_GET_EXPERT_FRAME_DURATION_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); if (!value) { goto bad_arg; } *value = st->variable_duration; } break; case OPUS_RESET_STATE: { int s; st->subframe_mem[0] = st->subframe_mem[1] = st->subframe_mem[2] = 0; if (st->surround) { OPUS_CLEAR(ms_get_preemph_mem(st), st->layout.nb_channels); OPUS_CLEAR(ms_get_window_mem(st), st->layout.nb_channels*120); } for (s=0;slayout.nb_streams;s++) { OpusEncoder *enc; enc = (OpusEncoder*)ptr; if (s < st->layout.nb_coupled_streams) ptr += align(coupled_size); else ptr += align(mono_size); ret = opus_encoder_ctl(enc, OPUS_RESET_STATE); if (ret != OPUS_OK) break; } } break; default: ret = OPUS_UNIMPLEMENTED; break; } va_end(ap); return ret; bad_arg: va_end(ap); return OPUS_BAD_ARG; } void opus_multistream_encoder_destroy(OpusMSEncoder *st) { opus_free(st); } ================================================ FILE: deps/pjsip/third_party/opus/src/opus_private.h ================================================ /* Copyright (c) 2012 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef OPUS_PRIVATE_H #define OPUS_PRIVATE_H #include "arch.h" #include "opus.h" #include "celt.h" #include /* offsetof */ struct OpusRepacketizer { unsigned char toc; int nb_frames; const unsigned char *frames[48]; opus_int16 len[48]; int framesize; }; typedef struct ChannelLayout { int nb_channels; int nb_streams; int nb_coupled_streams; unsigned char mapping[256]; } ChannelLayout; int validate_layout(const ChannelLayout *layout); int get_left_channel(const ChannelLayout *layout, int stream_id, int prev); int get_right_channel(const ChannelLayout *layout, int stream_id, int prev); int get_mono_channel(const ChannelLayout *layout, int stream_id, int prev); #define MODE_SILK_ONLY 1000 #define MODE_HYBRID 1001 #define MODE_CELT_ONLY 1002 #define OPUS_SET_VOICE_RATIO_REQUEST 11018 #define OPUS_GET_VOICE_RATIO_REQUEST 11019 /** Configures the encoder's expected percentage of voice * opposed to music or other signals. * * @note This interface is currently more aspiration than actuality. It's * ultimately expected to bias an automatic signal classifier, but it currently * just shifts the static bitrate to mode mapping around a little bit. * * @param[in] x int: Voice percentage in the range 0-100, inclusive. * @hideinitializer */ #define OPUS_SET_VOICE_RATIO(x) OPUS_SET_VOICE_RATIO_REQUEST, __opus_check_int(x) /** Gets the encoder's configured voice ratio value, @see OPUS_SET_VOICE_RATIO * * @param[out] x int*: Voice percentage in the range 0-100, inclusive. * @hideinitializer */ #define OPUS_GET_VOICE_RATIO(x) OPUS_GET_VOICE_RATIO_REQUEST, __opus_check_int_ptr(x) #define OPUS_SET_FORCE_MODE_REQUEST 11002 #define OPUS_SET_FORCE_MODE(x) OPUS_SET_FORCE_MODE_REQUEST, __opus_check_int(x) typedef void (*downmix_func)(const void *, opus_val32 *, int, int, int, int, int); void downmix_float(const void *_x, opus_val32 *sub, int subframe, int offset, int c1, int c2, int C); void downmix_int(const void *_x, opus_val32 *sub, int subframe, int offset, int c1, int c2, int C); int encode_size(int size, unsigned char *data); opus_int32 frame_size_select(opus_int32 frame_size, int variable_duration, opus_int32 Fs); opus_int32 compute_frame_size(const void *analysis_pcm, int frame_size, int variable_duration, int C, opus_int32 Fs, int bitrate_bps, int delay_compensation, downmix_func downmix #ifndef DISABLE_FLOAT_API , float *subframe_mem #endif ); opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_size, unsigned char *data, opus_int32 out_data_bytes, int lsb_depth, const void *analysis_pcm, opus_int32 analysis_size, int c1, int c2, int analysis_channels, downmix_func downmix, int float_api); int opus_decode_native(OpusDecoder *st, const unsigned char *data, opus_int32 len, opus_val16 *pcm, int frame_size, int decode_fec, int self_delimited, opus_int32 *packet_offset, int soft_clip); /* Make sure everything is properly aligned. */ static OPUS_INLINE int align(int i) { struct foo {char c; union { void* p; opus_int32 i; opus_val32 v; } u;}; unsigned int alignment = offsetof(struct foo, u); /* Optimizing compilers should optimize div and multiply into and for all sensible alignment values. */ return ((i + alignment - 1) / alignment) * alignment; } int opus_packet_parse_impl(const unsigned char *data, opus_int32 len, int self_delimited, unsigned char *out_toc, const unsigned char *frames[48], opus_int16 size[48], int *payload_offset, opus_int32 *packet_offset); opus_int32 opus_repacketizer_out_range_impl(OpusRepacketizer *rp, int begin, int end, unsigned char *data, opus_int32 maxlen, int self_delimited, int pad); int pad_frame(unsigned char *data, opus_int32 len, opus_int32 new_len); #endif /* OPUS_PRIVATE_H */ ================================================ FILE: deps/pjsip/third_party/opus/src/repacketizer.c ================================================ /* Copyright (c) 2011 Xiph.Org Foundation Written by Jean-Marc Valin */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "opus.h" #include "opus_private.h" #include "os_support.h" int opus_repacketizer_get_size(void) { return sizeof(OpusRepacketizer); } OpusRepacketizer *opus_repacketizer_init(OpusRepacketizer *rp) { rp->nb_frames = 0; return rp; } OpusRepacketizer *opus_repacketizer_create(void) { OpusRepacketizer *rp; rp=(OpusRepacketizer *)opus_alloc(opus_repacketizer_get_size()); if(rp==NULL)return NULL; return opus_repacketizer_init(rp); } void opus_repacketizer_destroy(OpusRepacketizer *rp) { opus_free(rp); } static int opus_repacketizer_cat_impl(OpusRepacketizer *rp, const unsigned char *data, opus_int32 len, int self_delimited) { unsigned char tmp_toc; int curr_nb_frames,ret; /* Set of check ToC */ if (len<1) return OPUS_INVALID_PACKET; if (rp->nb_frames == 0) { rp->toc = data[0]; rp->framesize = opus_packet_get_samples_per_frame(data, 8000); } else if ((rp->toc&0xFC) != (data[0]&0xFC)) { /*fprintf(stderr, "toc mismatch: 0x%x vs 0x%x\n", rp->toc, data[0]);*/ return OPUS_INVALID_PACKET; } curr_nb_frames = opus_packet_get_nb_frames(data, len); if(curr_nb_frames<1) return OPUS_INVALID_PACKET; /* Check the 120 ms maximum packet size */ if ((curr_nb_frames+rp->nb_frames)*rp->framesize > 960) { return OPUS_INVALID_PACKET; } ret=opus_packet_parse_impl(data, len, self_delimited, &tmp_toc, &rp->frames[rp->nb_frames], &rp->len[rp->nb_frames], NULL, NULL); if(ret<1)return ret; rp->nb_frames += curr_nb_frames; return OPUS_OK; } int opus_repacketizer_cat(OpusRepacketizer *rp, const unsigned char *data, opus_int32 len) { return opus_repacketizer_cat_impl(rp, data, len, 0); } int opus_repacketizer_get_nb_frames(OpusRepacketizer *rp) { return rp->nb_frames; } opus_int32 opus_repacketizer_out_range_impl(OpusRepacketizer *rp, int begin, int end, unsigned char *data, opus_int32 maxlen, int self_delimited, int pad) { int i, count; opus_int32 tot_size; opus_int16 *len; const unsigned char **frames; unsigned char * ptr; if (begin<0 || begin>=end || end>rp->nb_frames) { /*fprintf(stderr, "%d %d %d\n", begin, end, rp->nb_frames);*/ return OPUS_BAD_ARG; } count = end-begin; len = rp->len+begin; frames = rp->frames+begin; if (self_delimited) tot_size = 1 + (len[count-1]>=252); else tot_size = 0; ptr = data; if (count==1) { /* Code 0 */ tot_size += len[0]+1; if (tot_size > maxlen) return OPUS_BUFFER_TOO_SMALL; *ptr++ = rp->toc&0xFC; } else if (count==2) { if (len[1] == len[0]) { /* Code 1 */ tot_size += 2*len[0]+1; if (tot_size > maxlen) return OPUS_BUFFER_TOO_SMALL; *ptr++ = (rp->toc&0xFC) | 0x1; } else { /* Code 2 */ tot_size += len[0]+len[1]+2+(len[0]>=252); if (tot_size > maxlen) return OPUS_BUFFER_TOO_SMALL; *ptr++ = (rp->toc&0xFC) | 0x2; ptr += encode_size(len[0], ptr); } } if (count > 2 || (pad && tot_size < maxlen)) { /* Code 3 */ int vbr; int pad_amount=0; /* Restart the process for the padding case */ ptr = data; if (self_delimited) tot_size = 1 + (len[count-1]>=252); else tot_size = 0; vbr = 0; for (i=1;i=252) + len[i]; tot_size += len[count-1]; if (tot_size > maxlen) return OPUS_BUFFER_TOO_SMALL; *ptr++ = (rp->toc&0xFC) | 0x3; *ptr++ = count | 0x80; } else { tot_size += count*len[0]+2; if (tot_size > maxlen) return OPUS_BUFFER_TOO_SMALL; *ptr++ = (rp->toc&0xFC) | 0x3; *ptr++ = count; } pad_amount = pad ? (maxlen-tot_size) : 0; if (pad_amount != 0) { int nb_255s; data[1] |= 0x40; nb_255s = (pad_amount-1)/255; for (i=0;inb_frames, data, maxlen, 0, 0); } int opus_packet_pad(unsigned char *data, opus_int32 len, opus_int32 new_len) { OpusRepacketizer rp; opus_int32 ret; if (len < 1) return OPUS_BAD_ARG; if (len==new_len) return OPUS_OK; else if (len > new_len) return OPUS_BAD_ARG; opus_repacketizer_init(&rp); /* Moving payload to the end of the packet so we can do in-place padding */ OPUS_MOVE(data+new_len-len, data, len); opus_repacketizer_cat(&rp, data+new_len-len, len); ret = opus_repacketizer_out_range_impl(&rp, 0, rp.nb_frames, data, new_len, 0, 1); if (ret > 0) return OPUS_OK; else return ret; } opus_int32 opus_packet_unpad(unsigned char *data, opus_int32 len) { OpusRepacketizer rp; opus_int32 ret; if (len < 1) return OPUS_BAD_ARG; opus_repacketizer_init(&rp); ret = opus_repacketizer_cat(&rp, data, len); if (ret < 0) return ret; ret = opus_repacketizer_out_range_impl(&rp, 0, rp.nb_frames, data, len, 0, 0); celt_assert(ret > 0 && ret <= len); return ret; } int opus_multistream_packet_pad(unsigned char *data, opus_int32 len, opus_int32 new_len, int nb_streams) { int s; int count; unsigned char toc; opus_int16 size[48]; opus_int32 packet_offset; opus_int32 amount; if (len < 1) return OPUS_BAD_ARG; if (len==new_len) return OPUS_OK; else if (len > new_len) return OPUS_BAD_ARG; amount = new_len - len; /* Seek to last stream */ for (s=0;s #include #include #define MAX_PACKETOUT 32000 void usage(char *argv0) { fprintf(stderr, "usage: %s [options] input_file output_file\n", argv0); } static void int_to_char(opus_uint32 i, unsigned char ch[4]) { ch[0] = i>>24; ch[1] = (i>>16)&0xFF; ch[2] = (i>>8)&0xFF; ch[3] = i&0xFF; } static opus_uint32 char_to_int(unsigned char ch[4]) { return ((opus_uint32)ch[0]<<24) | ((opus_uint32)ch[1]<<16) | ((opus_uint32)ch[2]<< 8) | (opus_uint32)ch[3]; } int main(int argc, char *argv[]) { int i, eof=0; FILE *fin, *fout; unsigned char packets[48][1500]; int len[48]; int rng[48]; OpusRepacketizer *rp; unsigned char output_packet[MAX_PACKETOUT]; int merge = 1, split=0; if (argc < 3) { usage(argv[0]); return EXIT_FAILURE; } for (i=1;i48) { fprintf(stderr, "-merge parameter must be less than 48.\n"); return EXIT_FAILURE; } i++; } else if (strcmp(argv[i], "-split")==0) split = 1; else { fprintf(stderr, "Unknown option: %s\n", argv[i]); usage(argv[0]); return EXIT_FAILURE; } } fin = fopen(argv[argc-2], "r"); if(fin==NULL) { fprintf(stderr, "Error opening input file: %s\n", argv[argc-2]); return EXIT_FAILURE; } fout = fopen(argv[argc-1], "w"); if(fout==NULL) { fprintf(stderr, "Error opening output file: %s\n", argv[argc-1]); fclose(fin); return EXIT_FAILURE; } rp = opus_repacketizer_create(); while (!eof) { int err; int nb_packets=merge; opus_repacketizer_init(rp); for (i=0;i1500 || len[i]<0) { if (feof(fin)) { eof = 1; } else { fprintf(stderr, "Invalid payload length\n"); fclose(fin); fclose(fout); return EXIT_FAILURE; } break; } err = fread(ch, 1, 4, fin); rng[i] = char_to_int(ch); err = fread(packets[i], 1, len[i], fin); if (feof(fin)) { eof = 1; break; } err = opus_repacketizer_cat(rp, packets[i], len[i]); if (err!=OPUS_OK) { fprintf(stderr, "opus_repacketizer_cat() failed: %s\n", opus_strerror(err)); break; } } nb_packets = i; if (eof) break; if (!split) { err = opus_repacketizer_out(rp, output_packet, MAX_PACKETOUT); if (err>0) { unsigned char int_field[4]; int_to_char(err, int_field); if(fwrite(int_field, 1, 4, fout)!=4){ fprintf(stderr, "Error writing.\n"); return EXIT_FAILURE; } int_to_char(rng[nb_packets-1], int_field); if (fwrite(int_field, 1, 4, fout)!=4) { fprintf(stderr, "Error writing.\n"); return EXIT_FAILURE; } if (fwrite(output_packet, 1, err, fout)!=(unsigned)err) { fprintf(stderr, "Error writing.\n"); return EXIT_FAILURE; } /*fprintf(stderr, "out len = %d\n", err);*/ } else { fprintf(stderr, "opus_repacketizer_out() failed: %s\n", opus_strerror(err)); } } else { int nb_frames = opus_repacketizer_get_nb_frames(rp); for (i=0;i0) { unsigned char int_field[4]; int_to_char(err, int_field); if (fwrite(int_field, 1, 4, fout)!=4) { fprintf(stderr, "Error writing.\n"); return EXIT_FAILURE; } if (i==nb_frames-1) int_to_char(rng[nb_packets-1], int_field); else int_to_char(0, int_field); if (fwrite(int_field, 1, 4, fout)!=4) { fprintf(stderr, "Error writing.\n"); return EXIT_FAILURE; } if (fwrite(output_packet, 1, err, fout)!=(unsigned)err) { fprintf(stderr, "Error writing.\n"); return EXIT_FAILURE; } /*fprintf(stderr, "out len = %d\n", err);*/ } else { fprintf(stderr, "opus_repacketizer_out() failed: %s\n", opus_strerror(err)); } } } } fclose(fin); fclose(fout); return EXIT_SUCCESS; } ================================================ FILE: deps/pjsip/third_party/opus/src/tansig_table.h ================================================ /* This file is auto-generated by gen_tables */ static const float tansig_table[201] = { 0.000000f, 0.039979f, 0.079830f, 0.119427f, 0.158649f, 0.197375f, 0.235496f, 0.272905f, 0.309507f, 0.345214f, 0.379949f, 0.413644f, 0.446244f, 0.477700f, 0.507977f, 0.537050f, 0.564900f, 0.591519f, 0.616909f, 0.641077f, 0.664037f, 0.685809f, 0.706419f, 0.725897f, 0.744277f, 0.761594f, 0.777888f, 0.793199f, 0.807569f, 0.821040f, 0.833655f, 0.845456f, 0.856485f, 0.866784f, 0.876393f, 0.885352f, 0.893698f, 0.901468f, 0.908698f, 0.915420f, 0.921669f, 0.927473f, 0.932862f, 0.937863f, 0.942503f, 0.946806f, 0.950795f, 0.954492f, 0.957917f, 0.961090f, 0.964028f, 0.966747f, 0.969265f, 0.971594f, 0.973749f, 0.975743f, 0.977587f, 0.979293f, 0.980869f, 0.982327f, 0.983675f, 0.984921f, 0.986072f, 0.987136f, 0.988119f, 0.989027f, 0.989867f, 0.990642f, 0.991359f, 0.992020f, 0.992631f, 0.993196f, 0.993718f, 0.994199f, 0.994644f, 0.995055f, 0.995434f, 0.995784f, 0.996108f, 0.996407f, 0.996682f, 0.996937f, 0.997172f, 0.997389f, 0.997590f, 0.997775f, 0.997946f, 0.998104f, 0.998249f, 0.998384f, 0.998508f, 0.998623f, 0.998728f, 0.998826f, 0.998916f, 0.999000f, 0.999076f, 0.999147f, 0.999213f, 0.999273f, 0.999329f, 0.999381f, 0.999428f, 0.999472f, 0.999513f, 0.999550f, 0.999585f, 0.999617f, 0.999646f, 0.999673f, 0.999699f, 0.999722f, 0.999743f, 0.999763f, 0.999781f, 0.999798f, 0.999813f, 0.999828f, 0.999841f, 0.999853f, 0.999865f, 0.999875f, 0.999885f, 0.999893f, 0.999902f, 0.999909f, 0.999916f, 0.999923f, 0.999929f, 0.999934f, 0.999939f, 0.999944f, 0.999948f, 0.999952f, 0.999956f, 0.999959f, 0.999962f, 0.999965f, 0.999968f, 0.999970f, 0.999973f, 0.999975f, 0.999977f, 0.999978f, 0.999980f, 0.999982f, 0.999983f, 0.999984f, 0.999986f, 0.999987f, 0.999988f, 0.999989f, 0.999990f, 0.999990f, 0.999991f, 0.999992f, 0.999992f, 0.999993f, 0.999994f, 0.999994f, 0.999994f, 0.999995f, 0.999995f, 0.999996f, 0.999996f, 0.999996f, 0.999997f, 0.999997f, 0.999997f, 0.999997f, 0.999997f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, }; ================================================ FILE: deps/pjsip/third_party/opus/test-driver ================================================ #!/bin/sh # test-driver - basic testsuite driver script. scriptversion=2013-07-13.22; # UTC # Copyright (C) 2011-2014 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . # Make unconditional expansion of undefined variables an error. This # helps a lot in preventing typo-related bugs. set -u usage_error () { echo "$0: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat <$log_file 2>&1 estatus=$? if test $enable_hard_errors = no && test $estatus -eq 99; then tweaked_estatus=1 else tweaked_estatus=$estatus fi case $tweaked_estatus:$expect_failure in 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; 0:*) col=$grn res=PASS recheck=no gcopy=no;; 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; *:*) col=$red res=FAIL recheck=yes gcopy=yes;; esac # Report the test outcome and exit status in the logs, so that one can # know whether the test passed or failed simply by looking at the '.log' # file, without the need of also peaking into the corresponding '.trs' # file (automake bug#11814). echo "$res $test_name (exit status: $estatus)" >>$log_file # Report outcome to console. echo "${col}${res}${std}: $test_name" # Register the test result, and other relevant metadata. echo ":test-result: $res" > $trs_file echo ":global-test-result: $res" >> $trs_file echo ":recheck: $recheck" >> $trs_file echo ":copy-in-global-log: $gcopy" >> $trs_file # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: ================================================ FILE: deps/pjsip/third_party/opus/tests/run_vectors.sh ================================================ #!/bin/sh # Copyright (c) 2011-2012 Jean-Marc Valin # # This file is extracted from RFC6716. Please see that RFC for additional # information. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # - Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # - Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # - Neither the name of Internet Society, IETF or IETF Trust, nor the # names of specific contributors, may be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER # OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. rm logs_mono.txt rm logs_stereo.txt if [ "$#" -ne "3" ]; then echo "usage: run_vectors.sh " exit 1 fi CMD_PATH=$1 VECTOR_PATH=$2 RATE=$3 : ${OPUS_DEMO:=$CMD_PATH/opus_demo} : ${OPUS_COMPARE:=$CMD_PATH/opus_compare} if [ -d $VECTOR_PATH ]; then echo Test vectors found in $VECTOR_PATH else echo No test vectors found #Don't make the test fail here because the test vectors #will be distributed separately exit 0 fi if [ ! -x $OPUS_COMPARE ]; then echo ERROR: Compare program not found: $OPUS_COMPARE exit 1 fi if [ -x $OPUS_DEMO ]; then echo Decoding with $OPUS_DEMO else echo ERROR: Decoder not found: $OPUS_DEMO exit 1 fi echo "==============" echo Testing mono echo "==============" echo for file in 01 02 03 04 05 06 07 08 09 10 11 12 do if [ -e $VECTOR_PATH/testvector$file.bit ]; then echo Testing testvector$file else echo Bitstream file not found: testvector$file.bit fi if $OPUS_DEMO -d $RATE 1 $VECTOR_PATH/testvector$file.bit tmp.out >> logs_mono.txt 2>&1; then echo successfully decoded else echo ERROR: decoding failed exit 1 fi $OPUS_COMPARE -r $RATE $VECTOR_PATH/testvector$file.dec tmp.out >> logs_mono.txt 2>&1 float_ret=$? if [ "$float_ret" -eq "0" ]; then echo output matches reference else echo ERROR: output does not match reference exit 1 fi echo done echo "==============" echo Testing stereo echo "==============" echo for file in 01 02 03 04 05 06 07 08 09 10 11 12 do if [ -e $VECTOR_PATH/testvector$file.bit ]; then echo Testing testvector$file else echo Bitstream file not found: testvector$file fi if $OPUS_DEMO -d $RATE 2 $VECTOR_PATH/testvector$file.bit tmp.out >> logs_stereo.txt 2>&1; then echo successfully decoded else echo ERROR: decoding failed exit 1 fi $OPUS_COMPARE -s -r $RATE $VECTOR_PATH/testvector$file.dec tmp.out >> logs_stereo.txt 2>&1 float_ret=$? if [ "$float_ret" -eq "0" ]; then echo output matches reference else echo ERROR: output does not match reference exit 1 fi echo done echo All tests have passed successfully grep quality logs_mono.txt | awk '{sum+=$4}END{print "Average mono quality is", sum/NR, "%"}' grep quality logs_stereo.txt | awk '{sum+=$4}END{print "Average stereo quality is", sum/NR, "%"}' ================================================ FILE: deps/pjsip/third_party/opus/tests/test_opus_api.c ================================================ /* Copyright (c) 2011-2013 Xiph.Org Foundation Written by Gregory Maxwell */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This tests the API presented by the libopus system. It does not attempt to extensively exercise the codec internals. The strategy here is to simply the API interface invariants: That sane options are accepted, insane options are rejected, and that nothing blows up. In particular we don't actually test that settings are heeded by the codec (though we do check that get after set returns a sane value when it should). Other tests check the actual codec behavior. In cases where its reasonable to do so we test exhaustively, but its not reasonable to do so in all cases. Although these tests are simple they found several library bugs when they were initially developed. */ /* These tests are more sensitive if compiled with -DVALGRIND and run inside valgrind. Malloc failure testing requires glibc. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "arch.h" #include "opus_multistream.h" #include "opus.h" #include "test_opus_common.h" #ifdef VALGRIND #include #define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y)) #define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y)) #else #define VG_UNDEF(x,y) #define VG_CHECK(x,y) #endif #if defined(HAVE___MALLOC_HOOK) #define MALLOC_FAIL #include "os_support.h" #include static const opus_int32 opus_apps[3] = {OPUS_APPLICATION_VOIP, OPUS_APPLICATION_AUDIO,OPUS_APPLICATION_RESTRICTED_LOWDELAY}; void *malloc_hook(__attribute__((unused)) size_t size, __attribute__((unused)) const void *caller) { return 0; } #endif static const opus_int32 opus_rates[5] = {48000,24000,16000,12000,8000}; opus_int32 test_dec_api(void) { opus_uint32 dec_final_range; OpusDecoder *dec; OpusDecoder *dec2; opus_int32 i,j,cfgs; unsigned char packet[1276]; #ifndef DISABLE_FLOAT_API float fbuf[960*2]; #endif short sbuf[960*2]; int c,err; opus_int32 *nullvalue; nullvalue=0; cfgs=0; /*First test invalid configurations which should fail*/ fprintf(stdout,"\n Decoder basic API tests\n"); fprintf(stdout," ---------------------------------------------------\n"); for(c=0;c<4;c++) { i=opus_decoder_get_size(c); if(((c==1||c==2)&&(i<=2048||i>1<<16))||((c!=1&&c!=2)&&i!=0))test_failed(); fprintf(stdout," opus_decoder_get_size(%d)=%d ...............%s OK.\n",c,i,i>0?"":"...."); cfgs++; } /*Test with unsupported sample rates*/ for(c=0;c<4;c++) { for(i=-7;i<=96000;i++) { int fs; if((i==8000||i==12000||i==16000||i==24000||i==48000)&&(c==1||c==2))continue; switch(i) { case(-5):fs=-8000;break; case(-6):fs=INT32_MAX;break; case(-7):fs=INT32_MIN;break; default:fs=i; } err = OPUS_OK; VG_UNDEF(&err,sizeof(err)); dec = opus_decoder_create(fs, c, &err); if(err!=OPUS_BAD_ARG || dec!=NULL)test_failed(); cfgs++; dec = opus_decoder_create(fs, c, 0); if(dec!=NULL)test_failed(); cfgs++; dec=malloc(opus_decoder_get_size(2)); if(dec==NULL)test_failed(); err = opus_decoder_init(dec,fs,c); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; free(dec); } } VG_UNDEF(&err,sizeof(err)); dec = opus_decoder_create(48000, 2, &err); if(err!=OPUS_OK || dec==NULL)test_failed(); VG_CHECK(dec,opus_decoder_get_size(2)); cfgs++; fprintf(stdout," opus_decoder_create() ........................ OK.\n"); fprintf(stdout," opus_decoder_init() .......................... OK.\n"); err=opus_decoder_ctl(dec, OPUS_GET_FINAL_RANGE((opus_uint32 *)NULL)); if(err != OPUS_BAD_ARG)test_failed(); VG_UNDEF(&dec_final_range,sizeof(dec_final_range)); err=opus_decoder_ctl(dec, OPUS_GET_FINAL_RANGE(&dec_final_range)); if(err!=OPUS_OK)test_failed(); VG_CHECK(&dec_final_range,sizeof(dec_final_range)); fprintf(stdout," OPUS_GET_FINAL_RANGE ......................... OK.\n"); cfgs++; err=opus_decoder_ctl(dec,OPUS_UNIMPLEMENTED); if(err!=OPUS_UNIMPLEMENTED)test_failed(); fprintf(stdout," OPUS_UNIMPLEMENTED ........................... OK.\n"); cfgs++; err=opus_decoder_ctl(dec, OPUS_GET_BANDWIDTH((opus_int32 *)NULL)); if(err != OPUS_BAD_ARG)test_failed(); VG_UNDEF(&i,sizeof(i)); err=opus_decoder_ctl(dec, OPUS_GET_BANDWIDTH(&i)); if(err != OPUS_OK || i!=0)test_failed(); fprintf(stdout," OPUS_GET_BANDWIDTH ........................... OK.\n"); cfgs++; err=opus_decoder_ctl(dec, OPUS_GET_SAMPLE_RATE((opus_int32 *)NULL)); if(err != OPUS_BAD_ARG)test_failed(); VG_UNDEF(&i,sizeof(i)); err=opus_decoder_ctl(dec, OPUS_GET_SAMPLE_RATE(&i)); if(err != OPUS_OK || i!=48000)test_failed(); fprintf(stdout," OPUS_GET_SAMPLE_RATE ......................... OK.\n"); cfgs++; /*GET_PITCH has different execution paths depending on the previously decoded frame.*/ err=opus_decoder_ctl(dec, OPUS_GET_PITCH(nullvalue)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; VG_UNDEF(&i,sizeof(i)); err=opus_decoder_ctl(dec, OPUS_GET_PITCH(&i)); if(err != OPUS_OK || i>0 || i<-1)test_failed(); cfgs++; VG_UNDEF(packet,sizeof(packet)); packet[0]=63<<2;packet[1]=packet[2]=0; if(opus_decode(dec, packet, 3, sbuf, 960, 0)!=960)test_failed(); cfgs++; VG_UNDEF(&i,sizeof(i)); err=opus_decoder_ctl(dec, OPUS_GET_PITCH(&i)); if(err != OPUS_OK || i>0 || i<-1)test_failed(); cfgs++; packet[0]=1; if(opus_decode(dec, packet, 1, sbuf, 960, 0)!=960)test_failed(); cfgs++; VG_UNDEF(&i,sizeof(i)); err=opus_decoder_ctl(dec, OPUS_GET_PITCH(&i)); if(err != OPUS_OK || i>0 || i<-1)test_failed(); cfgs++; fprintf(stdout," OPUS_GET_PITCH ............................... OK.\n"); err=opus_decoder_ctl(dec, OPUS_GET_LAST_PACKET_DURATION((opus_int32 *)NULL)); if(err != OPUS_BAD_ARG)test_failed(); VG_UNDEF(&i,sizeof(i)); err=opus_decoder_ctl(dec, OPUS_GET_LAST_PACKET_DURATION(&i)); if(err != OPUS_OK || i!=960)test_failed(); cfgs++; fprintf(stdout," OPUS_GET_LAST_PACKET_DURATION ................ OK.\n"); VG_UNDEF(&i,sizeof(i)); err=opus_decoder_ctl(dec, OPUS_GET_GAIN(&i)); VG_CHECK(&i,sizeof(i)); if(err != OPUS_OK || i!=0)test_failed(); cfgs++; err=opus_decoder_ctl(dec, OPUS_GET_GAIN(nullvalue)); if(err != OPUS_BAD_ARG)test_failed(); cfgs++; err=opus_decoder_ctl(dec, OPUS_SET_GAIN(-32769)); if(err != OPUS_BAD_ARG)test_failed(); cfgs++; err=opus_decoder_ctl(dec, OPUS_SET_GAIN(32768)); if(err != OPUS_BAD_ARG)test_failed(); cfgs++; err=opus_decoder_ctl(dec, OPUS_SET_GAIN(-15)); if(err != OPUS_OK)test_failed(); cfgs++; VG_UNDEF(&i,sizeof(i)); err=opus_decoder_ctl(dec, OPUS_GET_GAIN(&i)); VG_CHECK(&i,sizeof(i)); if(err != OPUS_OK || i!=-15)test_failed(); cfgs++; fprintf(stdout," OPUS_SET_GAIN ................................ OK.\n"); fprintf(stdout," OPUS_GET_GAIN ................................ OK.\n"); /*Reset the decoder*/ dec2=malloc(opus_decoder_get_size(2)); memcpy(dec2,dec,opus_decoder_get_size(2)); if(opus_decoder_ctl(dec, OPUS_RESET_STATE)!=OPUS_OK)test_failed(); if(memcmp(dec2,dec,opus_decoder_get_size(2))==0)test_failed(); free(dec2); fprintf(stdout," OPUS_RESET_STATE ............................. OK.\n"); cfgs++; VG_UNDEF(packet,sizeof(packet)); packet[0]=0; if(opus_decoder_get_nb_samples(dec,packet,1)!=480)test_failed(); if(opus_packet_get_nb_samples(packet,1,48000)!=480)test_failed(); if(opus_packet_get_nb_samples(packet,1,96000)!=960)test_failed(); if(opus_packet_get_nb_samples(packet,1,32000)!=320)test_failed(); if(opus_packet_get_nb_samples(packet,1,8000)!=80)test_failed(); packet[0]=3; if(opus_packet_get_nb_samples(packet,1,24000)!=OPUS_INVALID_PACKET)test_failed(); packet[0]=(63<<2)|3; packet[1]=63; if(opus_packet_get_nb_samples(packet,0,24000)!=OPUS_BAD_ARG)test_failed(); if(opus_packet_get_nb_samples(packet,2,48000)!=OPUS_INVALID_PACKET)test_failed(); if(opus_decoder_get_nb_samples(dec,packet,2)!=OPUS_INVALID_PACKET)test_failed(); fprintf(stdout," opus_{packet,decoder}_get_nb_samples() ....... OK.\n"); cfgs+=9; if(OPUS_BAD_ARG!=opus_packet_get_nb_frames(packet,0))test_failed(); for(i=0;i<256;i++) { int l1res[4]={1,2,2,OPUS_INVALID_PACKET}; packet[0]=i; if(l1res[packet[0]&3]!=opus_packet_get_nb_frames(packet,1))test_failed(); cfgs++; for(j=0;j<256;j++) { packet[1]=j; if(((packet[0]&3)!=3?l1res[packet[0]&3]:packet[1]&63)!=opus_packet_get_nb_frames(packet,2))test_failed(); cfgs++; } } fprintf(stdout," opus_packet_get_nb_frames() .................. OK.\n"); for(i=0;i<256;i++) { int bw; packet[0]=i; bw=packet[0]>>4; bw=OPUS_BANDWIDTH_NARROWBAND+(((((bw&7)*9)&(63-(bw&8)))+2+12*((bw&8)!=0))>>4); if(bw!=opus_packet_get_bandwidth(packet))test_failed(); cfgs++; } fprintf(stdout," opus_packet_get_bandwidth() .................. OK.\n"); for(i=0;i<256;i++) { int fp3s,rate; packet[0]=i; fp3s=packet[0]>>3; fp3s=((((3-(fp3s&3))*13&119)+9)>>2)*((fp3s>13)*(3-((fp3s&3)==3))+1)*25; for(rate=0;rate<5;rate++) { if((opus_rates[rate]*3/fp3s)!=opus_packet_get_samples_per_frame(packet,opus_rates[rate]))test_failed(); cfgs++; } } fprintf(stdout," opus_packet_get_samples_per_frame() .......... OK.\n"); packet[0]=(63<<2)+3; packet[1]=49; for(j=2;j<51;j++)packet[j]=0; VG_UNDEF(sbuf,sizeof(sbuf)); if(opus_decode(dec, packet, 51, sbuf, 960, 0)!=OPUS_INVALID_PACKET)test_failed(); cfgs++; packet[0]=(63<<2); packet[1]=packet[2]=0; if(opus_decode(dec, packet, -1, sbuf, 960, 0)!=OPUS_BAD_ARG)test_failed(); cfgs++; if(opus_decode(dec, packet, 3, sbuf, 60, 0)!=OPUS_BUFFER_TOO_SMALL)test_failed(); cfgs++; if(opus_decode(dec, packet, 3, sbuf, 480, 0)!=OPUS_BUFFER_TOO_SMALL)test_failed(); cfgs++; if(opus_decode(dec, packet, 3, sbuf, 960, 0)!=960)test_failed(); cfgs++; fprintf(stdout," opus_decode() ................................ OK.\n"); #ifndef DISABLE_FLOAT_API VG_UNDEF(fbuf,sizeof(fbuf)); if(opus_decode_float(dec, packet, 3, fbuf, 960, 0)!=960)test_failed(); cfgs++; fprintf(stdout," opus_decode_float() .......................... OK.\n"); #endif #if 0 /*These tests are disabled because the library crashes with null states*/ if(opus_decoder_ctl(0,OPUS_RESET_STATE) !=OPUS_INVALID_STATE)test_failed(); if(opus_decoder_init(0,48000,1) !=OPUS_INVALID_STATE)test_failed(); if(opus_decode(0,packet,1,outbuf,2880,0) !=OPUS_INVALID_STATE)test_failed(); if(opus_decode_float(0,packet,1,0,2880,0) !=OPUS_INVALID_STATE)test_failed(); if(opus_decoder_get_nb_samples(0,packet,1) !=OPUS_INVALID_STATE)test_failed(); if(opus_packet_get_nb_frames(NULL,1) !=OPUS_BAD_ARG)test_failed(); if(opus_packet_get_bandwidth(NULL) !=OPUS_BAD_ARG)test_failed(); if(opus_packet_get_samples_per_frame(NULL,48000)!=OPUS_BAD_ARG)test_failed(); #endif opus_decoder_destroy(dec); cfgs++; fprintf(stdout," All decoder interface tests passed\n"); fprintf(stdout," (%6d API invocations)\n",cfgs); return cfgs; } opus_int32 test_msdec_api(void) { opus_uint32 dec_final_range; OpusMSDecoder *dec; OpusDecoder *streamdec; opus_int32 i,j,cfgs; unsigned char packet[1276]; unsigned char mapping[256]; #ifndef DISABLE_FLOAT_API float fbuf[960*2]; #endif short sbuf[960*2]; int a,b,c,err; #if 0 /*Relevant test not enabled for multistream*/ int *nullvalue; nullvalue=0; #endif mapping[0]=0; mapping[1]=1; for(i=2;i<256;i++)VG_UNDEF(&mapping[i],sizeof(unsigned char)); cfgs=0; /*First test invalid configurations which should fail*/ fprintf(stdout,"\n Multistream decoder basic API tests\n"); fprintf(stdout," ---------------------------------------------------\n"); for(a=-1;a<4;a++) { for(b=-1;b<4;b++) { i=opus_multistream_decoder_get_size(a,b); if(((a>0&&b<=a&&b>=0)&&(i<=2048||i>((1<<16)*a)))||((a<1||b>a||b<0)&&i!=0))test_failed(); fprintf(stdout," opus_multistream_decoder_get_size(%2d,%2d)=%d %sOK.\n",a,b,i,i>0?"":"... "); cfgs++; } } /*Test with unsupported sample rates*/ for(c=1;c<3;c++) { for(i=-7;i<=96000;i++) { int fs; if((i==8000||i==12000||i==16000||i==24000||i==48000)&&(c==1||c==2))continue; switch(i) { case(-5):fs=-8000;break; case(-6):fs=INT32_MAX;break; case(-7):fs=INT32_MIN;break; default:fs=i; } err = OPUS_OK; VG_UNDEF(&err,sizeof(err)); dec = opus_multistream_decoder_create(fs, c, 1, c-1, mapping, &err); if(err!=OPUS_BAD_ARG || dec!=NULL)test_failed(); cfgs++; dec = opus_multistream_decoder_create(fs, c, 1, c-1, mapping, 0); if(dec!=NULL)test_failed(); cfgs++; dec=malloc(opus_multistream_decoder_get_size(1,1)); if(dec==NULL)test_failed(); err = opus_multistream_decoder_init(dec,fs,c,1,c-1, mapping); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; free(dec); } } for(c=0;c<2;c++) { int *ret_err; ret_err = c?0:&err; mapping[0]=0; mapping[1]=1; for(i=2;i<256;i++)VG_UNDEF(&mapping[i],sizeof(unsigned char)); VG_UNDEF(ret_err,sizeof(*ret_err)); dec = opus_multistream_decoder_create(48000, 2, 1, 0, mapping, ret_err); if(ret_err){VG_CHECK(ret_err,sizeof(*ret_err));} if((ret_err && *ret_err!=OPUS_BAD_ARG) || dec!=NULL)test_failed(); cfgs++; VG_UNDEF(ret_err,sizeof(*ret_err)); mapping[0]=mapping[1]=0; dec = opus_multistream_decoder_create(48000, 2, 1, 0, mapping, ret_err); if(ret_err){VG_CHECK(ret_err,sizeof(*ret_err));} if((ret_err && *ret_err!=OPUS_OK) || dec==NULL)test_failed(); cfgs++; opus_multistream_decoder_destroy(dec); cfgs++; VG_UNDEF(ret_err,sizeof(*ret_err)); dec = opus_multistream_decoder_create(48000, 1, 4, 1, mapping, ret_err); if(ret_err){VG_CHECK(ret_err,sizeof(*ret_err));} if((ret_err && *ret_err!=OPUS_OK) || dec==NULL)test_failed(); cfgs++; err = opus_multistream_decoder_init(dec,48000, 1, 0, 0, mapping); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; err = opus_multistream_decoder_init(dec,48000, 1, 1, -1, mapping); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; opus_multistream_decoder_destroy(dec); cfgs++; VG_UNDEF(ret_err,sizeof(*ret_err)); dec = opus_multistream_decoder_create(48000, 2, 1, 1, mapping, ret_err); if(ret_err){VG_CHECK(ret_err,sizeof(*ret_err));} if((ret_err && *ret_err!=OPUS_OK) || dec==NULL)test_failed(); cfgs++; opus_multistream_decoder_destroy(dec); cfgs++; VG_UNDEF(ret_err,sizeof(*ret_err)); dec = opus_multistream_decoder_create(48000, 255, 255, 1, mapping, ret_err); if(ret_err){VG_CHECK(ret_err,sizeof(*ret_err));} if((ret_err && *ret_err!=OPUS_BAD_ARG) || dec!=NULL)test_failed(); cfgs++; VG_UNDEF(ret_err,sizeof(*ret_err)); dec = opus_multistream_decoder_create(48000, -1, 1, 1, mapping, ret_err); if(ret_err){VG_CHECK(ret_err,sizeof(*ret_err));} if((ret_err && *ret_err!=OPUS_BAD_ARG) || dec!=NULL)test_failed(); cfgs++; VG_UNDEF(ret_err,sizeof(*ret_err)); dec = opus_multistream_decoder_create(48000, 0, 1, 1, mapping, ret_err); if(ret_err){VG_CHECK(ret_err,sizeof(*ret_err));} if((ret_err && *ret_err!=OPUS_BAD_ARG) || dec!=NULL)test_failed(); cfgs++; VG_UNDEF(ret_err,sizeof(*ret_err)); dec = opus_multistream_decoder_create(48000, 1, -1, 2, mapping, ret_err); if(ret_err){VG_CHECK(ret_err,sizeof(*ret_err));} if((ret_err && *ret_err!=OPUS_BAD_ARG) || dec!=NULL)test_failed(); cfgs++; VG_UNDEF(ret_err,sizeof(*ret_err)); dec = opus_multistream_decoder_create(48000, 1, -1, -1, mapping, ret_err); if(ret_err){VG_CHECK(ret_err,sizeof(*ret_err));} if((ret_err && *ret_err!=OPUS_BAD_ARG) || dec!=NULL)test_failed(); cfgs++; VG_UNDEF(ret_err,sizeof(*ret_err)); dec = opus_multistream_decoder_create(48000, 256, 255, 1, mapping, ret_err); if(ret_err){VG_CHECK(ret_err,sizeof(*ret_err));} if((ret_err && *ret_err!=OPUS_BAD_ARG) || dec!=NULL)test_failed(); cfgs++; VG_UNDEF(ret_err,sizeof(*ret_err)); dec = opus_multistream_decoder_create(48000, 256, 255, 0, mapping, ret_err); if(ret_err){VG_CHECK(ret_err,sizeof(*ret_err));} if((ret_err && *ret_err!=OPUS_BAD_ARG) || dec!=NULL)test_failed(); cfgs++; VG_UNDEF(ret_err,sizeof(*ret_err)); mapping[0]=255; mapping[1]=1; mapping[2]=2; dec = opus_multistream_decoder_create(48000, 3, 2, 0, mapping, ret_err); if(ret_err){VG_CHECK(ret_err,sizeof(*ret_err));} if((ret_err && *ret_err!=OPUS_BAD_ARG) || dec!=NULL)test_failed(); cfgs++; VG_UNDEF(ret_err,sizeof(*ret_err)); mapping[0]=0; mapping[1]=0; mapping[2]=0; dec = opus_multistream_decoder_create(48000, 3, 2, 1, mapping, ret_err); if(ret_err){VG_CHECK(ret_err,sizeof(*ret_err));} if((ret_err && *ret_err!=OPUS_OK) || dec==NULL)test_failed(); cfgs++; opus_multistream_decoder_destroy(dec); cfgs++; VG_UNDEF(ret_err,sizeof(*ret_err)); mapping[0]=0; mapping[1]=255; mapping[2]=1; mapping[3]=2; mapping[4]=3; dec = opus_multistream_decoder_create(48001, 5, 4, 1, mapping, ret_err); if(ret_err){VG_CHECK(ret_err,sizeof(*ret_err));} if((ret_err && *ret_err!=OPUS_BAD_ARG) || dec!=NULL)test_failed(); cfgs++; } VG_UNDEF(&err,sizeof(err)); mapping[0]=0; mapping[1]=255; mapping[2]=1; mapping[3]=2; dec = opus_multistream_decoder_create(48000, 4, 2, 1, mapping, &err); VG_CHECK(&err,sizeof(err)); if(err!=OPUS_OK || dec==NULL)test_failed(); cfgs++; fprintf(stdout," opus_multistream_decoder_create() ............ OK.\n"); fprintf(stdout," opus_multistream_decoder_init() .............. OK.\n"); VG_UNDEF(&dec_final_range,sizeof(dec_final_range)); err=opus_multistream_decoder_ctl(dec, OPUS_GET_FINAL_RANGE(&dec_final_range)); if(err!=OPUS_OK)test_failed(); VG_CHECK(&dec_final_range,sizeof(dec_final_range)); fprintf(stdout," OPUS_GET_FINAL_RANGE ......................... OK.\n"); cfgs++; streamdec=0; VG_UNDEF(&streamdec,sizeof(streamdec)); err=opus_multistream_decoder_ctl(dec, OPUS_MULTISTREAM_GET_DECODER_STATE(-1,&streamdec)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; err=opus_multistream_decoder_ctl(dec, OPUS_MULTISTREAM_GET_DECODER_STATE(1,&streamdec)); if(err!=OPUS_OK||streamdec==NULL)test_failed(); VG_CHECK(streamdec,opus_decoder_get_size(1)); cfgs++; err=opus_multistream_decoder_ctl(dec, OPUS_MULTISTREAM_GET_DECODER_STATE(2,&streamdec)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; err=opus_multistream_decoder_ctl(dec, OPUS_MULTISTREAM_GET_DECODER_STATE(0,&streamdec)); if(err!=OPUS_OK||streamdec==NULL)test_failed(); VG_CHECK(streamdec,opus_decoder_get_size(1)); fprintf(stdout," OPUS_MULTISTREAM_GET_DECODER_STATE ........... OK.\n"); cfgs++; for(j=0;j<2;j++) { OpusDecoder *od; err=opus_multistream_decoder_ctl(dec,OPUS_MULTISTREAM_GET_DECODER_STATE(j,&od)); if(err != OPUS_OK)test_failed(); VG_UNDEF(&i,sizeof(i)); err=opus_decoder_ctl(od, OPUS_GET_GAIN(&i)); VG_CHECK(&i,sizeof(i)); if(err != OPUS_OK || i!=0)test_failed(); cfgs++; } err=opus_multistream_decoder_ctl(dec,OPUS_SET_GAIN(15)); if(err!=OPUS_OK)test_failed(); fprintf(stdout," OPUS_SET_GAIN ................................ OK.\n"); for(j=0;j<2;j++) { OpusDecoder *od; err=opus_multistream_decoder_ctl(dec,OPUS_MULTISTREAM_GET_DECODER_STATE(j,&od)); if(err != OPUS_OK)test_failed(); VG_UNDEF(&i,sizeof(i)); err=opus_decoder_ctl(od, OPUS_GET_GAIN(&i)); VG_CHECK(&i,sizeof(i)); if(err != OPUS_OK || i!=15)test_failed(); cfgs++; } fprintf(stdout," OPUS_GET_GAIN ................................ OK.\n"); VG_UNDEF(&i,sizeof(i)); err=opus_multistream_decoder_ctl(dec, OPUS_GET_BANDWIDTH(&i)); if(err != OPUS_OK || i!=0)test_failed(); fprintf(stdout," OPUS_GET_BANDWIDTH ........................... OK.\n"); cfgs++; err=opus_multistream_decoder_ctl(dec,OPUS_UNIMPLEMENTED); if(err!=OPUS_UNIMPLEMENTED)test_failed(); fprintf(stdout," OPUS_UNIMPLEMENTED ........................... OK.\n"); cfgs++; #if 0 /*Currently unimplemented for multistream*/ /*GET_PITCH has different execution paths depending on the previously decoded frame.*/ err=opus_multistream_decoder_ctl(dec, OPUS_GET_PITCH(nullvalue)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; VG_UNDEF(&i,sizeof(i)); err=opus_multistream_decoder_ctl(dec, OPUS_GET_PITCH(&i)); if(err != OPUS_OK || i>0 || i<-1)test_failed(); cfgs++; VG_UNDEF(packet,sizeof(packet)); packet[0]=63<<2;packet[1]=packet[2]=0; if(opus_multistream_decode(dec, packet, 3, sbuf, 960, 0)!=960)test_failed(); cfgs++; VG_UNDEF(&i,sizeof(i)); err=opus_multistream_decoder_ctl(dec, OPUS_GET_PITCH(&i)); if(err != OPUS_OK || i>0 || i<-1)test_failed(); cfgs++; packet[0]=1; if(opus_multistream_decode(dec, packet, 1, sbuf, 960, 0)!=960)test_failed(); cfgs++; VG_UNDEF(&i,sizeof(i)); err=opus_multistream_decoder_ctl(dec, OPUS_GET_PITCH(&i)); if(err != OPUS_OK || i>0 || i<-1)test_failed(); cfgs++; fprintf(stdout," OPUS_GET_PITCH ............................... OK.\n"); #endif /*Reset the decoder*/ if(opus_multistream_decoder_ctl(dec, OPUS_RESET_STATE)!=OPUS_OK)test_failed(); fprintf(stdout," OPUS_RESET_STATE ............................. OK.\n"); cfgs++; opus_multistream_decoder_destroy(dec); cfgs++; VG_UNDEF(&err,sizeof(err)); dec = opus_multistream_decoder_create(48000, 2, 1, 1, mapping, &err); if(err!=OPUS_OK || dec==NULL)test_failed(); cfgs++; packet[0]=(63<<2)+3; packet[1]=49; for(j=2;j<51;j++)packet[j]=0; VG_UNDEF(sbuf,sizeof(sbuf)); if(opus_multistream_decode(dec, packet, 51, sbuf, 960, 0)!=OPUS_INVALID_PACKET)test_failed(); cfgs++; packet[0]=(63<<2); packet[1]=packet[2]=0; if(opus_multistream_decode(dec, packet, -1, sbuf, 960, 0)!=OPUS_BAD_ARG){printf("%d\n",opus_multistream_decode(dec, packet, -1, sbuf, 960, 0));test_failed();} cfgs++; if(opus_multistream_decode(dec, packet, 3, sbuf, 60, 0)!=OPUS_BUFFER_TOO_SMALL)test_failed(); cfgs++; if(opus_multistream_decode(dec, packet, 3, sbuf, 480, 0)!=OPUS_BUFFER_TOO_SMALL)test_failed(); cfgs++; if(opus_multistream_decode(dec, packet, 3, sbuf, 960, 0)!=960)test_failed(); cfgs++; fprintf(stdout," opus_multistream_decode() .................... OK.\n"); #ifndef DISABLE_FLOAT_API VG_UNDEF(fbuf,sizeof(fbuf)); if(opus_multistream_decode_float(dec, packet, 3, fbuf, 960, 0)!=960)test_failed(); cfgs++; fprintf(stdout," opus_multistream_decode_float() .............. OK.\n"); #endif #if 0 /*These tests are disabled because the library crashes with null states*/ if(opus_multistream_decoder_ctl(0,OPUS_RESET_STATE) !=OPUS_INVALID_STATE)test_failed(); if(opus_multistream_decoder_init(0,48000,1) !=OPUS_INVALID_STATE)test_failed(); if(opus_multistream_decode(0,packet,1,outbuf,2880,0) !=OPUS_INVALID_STATE)test_failed(); if(opus_multistream_decode_float(0,packet,1,0,2880,0) !=OPUS_INVALID_STATE)test_failed(); if(opus_multistream_decoder_get_nb_samples(0,packet,1) !=OPUS_INVALID_STATE)test_failed(); #endif opus_multistream_decoder_destroy(dec); cfgs++; fprintf(stdout," All multistream decoder interface tests passed\n"); fprintf(stdout," (%6d API invocations)\n",cfgs); return cfgs; } #ifdef VALGRIND #define UNDEFINE_FOR_PARSE toc=-1; \ frames[0]=(unsigned char *)0; \ frames[1]=(unsigned char *)0; \ payload_offset=-1; \ VG_UNDEF(&toc,sizeof(toc)); \ VG_UNDEF(frames,sizeof(frames));\ VG_UNDEF(&payload_offset,sizeof(payload_offset)); #else #define UNDEFINE_FOR_PARSE toc=-1; \ frames[0]=(unsigned char *)0; \ frames[1]=(unsigned char *)0; \ payload_offset=-1; #endif /* This test exercises the heck out of the libopus parser. It is much larger than the parser itself in part because it tries to hit a lot of corner cases that could never fail with the libopus code, but might be problematic for other implementations. */ opus_int32 test_parse(void) { opus_int32 i,j,jj,sz; unsigned char packet[1276]; opus_int32 cfgs,cfgs_total; unsigned char toc; const unsigned char *frames[48]; short size[48]; int payload_offset, ret; fprintf(stdout,"\n Packet header parsing tests\n"); fprintf(stdout," ---------------------------------------------------\n"); memset(packet,0,sizeof(char)*1276); packet[0]=63<<2; if(opus_packet_parse(packet,1,&toc,frames,0,&payload_offset)!=OPUS_BAD_ARG)test_failed(); cfgs_total=cfgs=1; /*code 0*/ for(i=0;i<64;i++) { packet[0]=i<<2; UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,4,&toc,frames,size,&payload_offset); cfgs++; if(ret!=1)test_failed(); if(size[0]!=3)test_failed(); if(frames[0]!=packet+1)test_failed(); } fprintf(stdout," code 0 (%2d cases) ............................ OK.\n",cfgs); cfgs_total+=cfgs;cfgs=0; /*code 1, two frames of the same size*/ for(i=0;i<64;i++) { packet[0]=(i<<2)+1; for(jj=0;jj<=1275*2+3;jj++) { UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,jj,&toc,frames,size,&payload_offset); cfgs++; if((jj&1)==1 && jj<=2551) { /* Must pass if payload length even (packet length odd) and size<=2551, must fail otherwise. */ if(ret!=2)test_failed(); if(size[0]!=size[1] || size[0]!=((jj-1)>>1))test_failed(); if(frames[0]!=packet+1)test_failed(); if(frames[1]!=frames[0]+size[0])test_failed(); if((toc>>2)!=i)test_failed(); } else if(ret!=OPUS_INVALID_PACKET)test_failed(); } } fprintf(stdout," code 1 (%6d cases) ........................ OK.\n",cfgs); cfgs_total+=cfgs;cfgs=0; for(i=0;i<64;i++) { /*code 2, length code overflow*/ packet[0]=(i<<2)+2; UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,1,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); packet[1]=252; UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,2,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); for(j=0;j<1275;j++) { if(j<252)packet[1]=j; else{packet[1]=252+(j&3);packet[2]=(j-252)>>2;} /*Code 2, one too short*/ UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,j+(j<252?2:3)-1,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); /*Code 2, one too long*/ UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,j+(j<252?2:3)+1276,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); /*Code 2, second zero*/ UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,j+(j<252?2:3),&toc,frames,size,&payload_offset); cfgs++; if(ret!=2)test_failed(); if(size[0]!=j||size[1]!=0)test_failed(); if(frames[1]!=frames[0]+size[0])test_failed(); if((toc>>2)!=i)test_failed(); /*Code 2, normal*/ UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,(j<<1)+4,&toc,frames,size,&payload_offset); cfgs++; if(ret!=2)test_failed(); if(size[0]!=j||size[1]!=(j<<1)+3-j-(j<252?1:2))test_failed(); if(frames[1]!=frames[0]+size[0])test_failed(); if((toc>>2)!=i)test_failed(); } } fprintf(stdout," code 2 (%6d cases) ........................ OK.\n",cfgs); cfgs_total+=cfgs;cfgs=0; for(i=0;i<64;i++) { packet[0]=(i<<2)+3; /*code 3, length code overflow*/ UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,1,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); } fprintf(stdout," code 3 m-truncation (%2d cases) ............... OK.\n",cfgs); cfgs_total+=cfgs;cfgs=0; for(i=0;i<64;i++) { /*code 3, m is zero or 49-63*/ packet[0]=(i<<2)+3; for(jj=49;jj<=64;jj++) { packet[1]=0+(jj&63); /*CBR, no padding*/ UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,1275,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); packet[1]=128+(jj&63); /*VBR, no padding*/ UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,1275,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); packet[1]=64+(jj&63); /*CBR, padding*/ UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,1275,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); packet[1]=128+64+(jj&63); /*VBR, padding*/ UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,1275,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); } } fprintf(stdout," code 3 m=0,49-64 (%2d cases) ................ OK.\n",cfgs); cfgs_total+=cfgs;cfgs=0; for(i=0;i<64;i++) { packet[0]=(i<<2)+3; /*code 3, m is one, cbr*/ packet[1]=1; for(j=0;j<1276;j++) { UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,j+2,&toc,frames,size,&payload_offset); cfgs++; if(ret!=1)test_failed(); if(size[0]!=j)test_failed(); if((toc>>2)!=i)test_failed(); } UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,1276+2,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); } fprintf(stdout," code 3 m=1 CBR (%2d cases) ................. OK.\n",cfgs); cfgs_total+=cfgs;cfgs=0; for(i=0;i<64;i++) { int frame_samp; /*code 3, m>1 CBR*/ packet[0]=(i<<2)+3; frame_samp=opus_packet_get_samples_per_frame(packet,48000); for(j=2;j<49;j++) { packet[1]=j; for(sz=2;sz<((j+2)*1275);sz++) { UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,sz,&toc,frames,size,&payload_offset); cfgs++; /*Must be <=120ms, must be evenly divisible, can't have frames>1275 bytes*/ if(frame_samp*j<=5760 && (sz-2)%j==0 && (sz-2)/j<1276) { if(ret!=j)test_failed(); for(jj=1;jj>2)!=i)test_failed(); } else if(ret!=OPUS_INVALID_PACKET)test_failed(); } } /*Super jumbo packets*/ packet[1]=5760/frame_samp; UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,1275*packet[1]+2,&toc,frames,size,&payload_offset); cfgs++; if(ret!=packet[1])test_failed(); for(jj=0;jj>2)!=i)test_failed(); } UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,2+1276,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); for(j=2;j<49;j++) { packet[1]=128+j; /*Length code overflow*/ UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,2+j-2,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); packet[2]=252; packet[3]=0; for(jj=4;jj<2+j;jj++)packet[jj]=0; UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,2+j,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); /*One byte too short*/ for(jj=2;jj<2+j;jj++)packet[jj]=0; UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,2+j-2,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); /*One byte too short thanks to length coding*/ packet[2]=252; packet[3]=0; for(jj=4;jj<2+j;jj++)packet[jj]=0; UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,2+j+252-1,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); /*Most expensive way of coding zeros*/ for(jj=2;jj<2+j;jj++)packet[jj]=0; UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,2+j-1,&toc,frames,size,&payload_offset); cfgs++; if(frame_samp*j<=5760){ if(ret!=j)test_failed(); for(jj=0;jj>2)!=i)test_failed(); } else if(ret!=OPUS_INVALID_PACKET)test_failed(); /*Quasi-CBR use of mode 3*/ for(sz=0;sz<8;sz++) { const int tsz[8]={50,201,403,700,1472,5110,20400,61298}; int pos=0; int as=(tsz[sz]+i-j-2)/j; for(jj=0;jj>2;pos+=2;} } UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,tsz[sz]+i,&toc,frames,size,&payload_offset); cfgs++; if(frame_samp*j<=5760 && as<1276 && (tsz[sz]+i-2-pos-as*(j-1))<1276){ if(ret!=j)test_failed(); for(jj=0;jj>2)!=i)test_failed(); } else if(ret!=OPUS_INVALID_PACKET)test_failed(); } } } fprintf(stdout," code 3 m=1-48 VBR (%2d cases) ............. OK.\n",cfgs); cfgs_total+=cfgs;cfgs=0; for(i=0;i<64;i++) { packet[0]=(i<<2)+3; /*Padding*/ packet[1]=128+1+64; /*Overflow the length coding*/ for(jj=2;jj<127;jj++)packet[jj]=255; UNDEFINE_FOR_PARSE ret=opus_packet_parse(packet,127,&toc,frames,size,&payload_offset); cfgs++; if(ret!=OPUS_INVALID_PACKET)test_failed(); for(sz=0;sz<4;sz++) { const int tsz[4]={0,72,512,1275}; for(jj=sz;jj<65025;jj+=11) { int pos; for(pos=0;pos>2)!=i)test_failed(); } else if (ret!=OPUS_INVALID_PACKET)test_failed(); } } } fprintf(stdout," code 3 padding (%2d cases) ............... OK.\n",cfgs); cfgs_total+=cfgs; fprintf(stdout," opus_packet_parse ............................ OK.\n"); fprintf(stdout," All packet parsing tests passed\n"); fprintf(stdout," (%d API invocations)\n",cfgs_total); return cfgs_total; } /* This is a helper macro for the encoder tests. The encoder api tests all have a pattern of set-must-fail, set-must-fail, set-must-pass, get-and-compare, set-must-pass, get-and-compare. */ #define CHECK_SETGET(setcall,getcall,badv,badv2,goodv,goodv2,sok,gok) \ i=(badv);\ if(opus_encoder_ctl(enc,setcall)==OPUS_OK)test_failed();\ i=(badv2);\ if(opus_encoder_ctl(enc,setcall)==OPUS_OK)test_failed();\ j=i=(goodv);\ if(opus_encoder_ctl(enc,setcall)!=OPUS_OK)test_failed();\ i=-12345;\ VG_UNDEF(&i,sizeof(i)); \ err=opus_encoder_ctl(enc,getcall);\ if(err!=OPUS_OK || i!=j)test_failed();\ j=i=(goodv2);\ if(opus_encoder_ctl(enc,setcall)!=OPUS_OK)test_failed();\ fprintf(stdout,sok);\ i=-12345;\ VG_UNDEF(&i,sizeof(i)); \ err=opus_encoder_ctl(enc,getcall);\ if(err!=OPUS_OK || i!=j)test_failed();\ fprintf(stdout,gok);\ cfgs+=6; opus_int32 test_enc_api(void) { opus_uint32 enc_final_range; OpusEncoder *enc; opus_int32 i,j; unsigned char packet[1276]; #ifndef DISABLE_FLOAT_API float fbuf[960*2]; #endif short sbuf[960*2]; int c,err,cfgs; cfgs=0; /*First test invalid configurations which should fail*/ fprintf(stdout,"\n Encoder basic API tests\n"); fprintf(stdout," ---------------------------------------------------\n"); for(c=0;c<4;c++) { i=opus_encoder_get_size(c); if(((c==1||c==2)&&(i<=2048||i>1<<17))||((c!=1&&c!=2)&&i!=0))test_failed(); fprintf(stdout," opus_encoder_get_size(%d)=%d ...............%s OK.\n",c,i,i>0?"":"...."); cfgs++; } /*Test with unsupported sample rates, channel counts*/ for(c=0;c<4;c++) { for(i=-7;i<=96000;i++) { int fs; if((i==8000||i==12000||i==16000||i==24000||i==48000)&&(c==1||c==2))continue; switch(i) { case(-5):fs=-8000;break; case(-6):fs=INT32_MAX;break; case(-7):fs=INT32_MIN;break; default:fs=i; } err = OPUS_OK; VG_UNDEF(&err,sizeof(err)); enc = opus_encoder_create(fs, c, OPUS_APPLICATION_VOIP, &err); if(err!=OPUS_BAD_ARG || enc!=NULL)test_failed(); cfgs++; enc = opus_encoder_create(fs, c, OPUS_APPLICATION_VOIP, 0); if(enc!=NULL)test_failed(); cfgs++; opus_encoder_destroy(enc); enc=malloc(opus_encoder_get_size(2)); if(enc==NULL)test_failed(); err = opus_encoder_init(enc, fs, c, OPUS_APPLICATION_VOIP); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; free(enc); } } enc = opus_encoder_create(48000, 2, OPUS_AUTO, NULL); if(enc!=NULL)test_failed(); cfgs++; VG_UNDEF(&err,sizeof(err)); enc = opus_encoder_create(48000, 2, OPUS_AUTO, &err); if(err!=OPUS_BAD_ARG || enc!=NULL)test_failed(); cfgs++; VG_UNDEF(&err,sizeof(err)); enc = opus_encoder_create(48000, 2, OPUS_APPLICATION_VOIP, NULL); if(enc==NULL)test_failed(); opus_encoder_destroy(enc); cfgs++; VG_UNDEF(&err,sizeof(err)); enc = opus_encoder_create(48000, 2, OPUS_APPLICATION_RESTRICTED_LOWDELAY, &err); if(err!=OPUS_OK || enc==NULL)test_failed(); cfgs++; err=opus_encoder_ctl(enc,OPUS_GET_LOOKAHEAD(&i)); if(err!=OPUS_OK || i<0 || i>32766)test_failed(); cfgs++; opus_encoder_destroy(enc); VG_UNDEF(&err,sizeof(err)); enc = opus_encoder_create(48000, 2, OPUS_APPLICATION_AUDIO, &err); if(err!=OPUS_OK || enc==NULL)test_failed(); cfgs++; err=opus_encoder_ctl(enc,OPUS_GET_LOOKAHEAD(&i)); if(err!=OPUS_OK || i<0 || i>32766)test_failed(); opus_encoder_destroy(enc); cfgs++; VG_UNDEF(&err,sizeof(err)); enc = opus_encoder_create(48000, 2, OPUS_APPLICATION_VOIP, &err); if(err!=OPUS_OK || enc==NULL)test_failed(); cfgs++; fprintf(stdout," opus_encoder_create() ........................ OK.\n"); fprintf(stdout," opus_encoder_init() .......................... OK.\n"); i=-12345; VG_UNDEF(&i,sizeof(i)); err=opus_encoder_ctl(enc,OPUS_GET_LOOKAHEAD(&i)); if(err!=OPUS_OK || i<0 || i>32766)test_failed(); cfgs++; err=opus_encoder_ctl(enc,OPUS_GET_LOOKAHEAD((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; fprintf(stdout," OPUS_GET_LOOKAHEAD ........................... OK.\n"); err=opus_encoder_ctl(enc,OPUS_GET_SAMPLE_RATE(&i)); if(err!=OPUS_OK || i!=48000)test_failed(); cfgs++; err=opus_encoder_ctl(enc,OPUS_GET_SAMPLE_RATE((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; fprintf(stdout," OPUS_GET_SAMPLE_RATE ......................... OK.\n"); if(opus_encoder_ctl(enc,OPUS_UNIMPLEMENTED)!=OPUS_UNIMPLEMENTED)test_failed(); fprintf(stdout," OPUS_UNIMPLEMENTED ........................... OK.\n"); cfgs++; err=opus_encoder_ctl(enc,OPUS_GET_APPLICATION((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; CHECK_SETGET(OPUS_SET_APPLICATION(i),OPUS_GET_APPLICATION(&i),-1,OPUS_AUTO, OPUS_APPLICATION_AUDIO,OPUS_APPLICATION_RESTRICTED_LOWDELAY, " OPUS_SET_APPLICATION ......................... OK.\n", " OPUS_GET_APPLICATION ......................... OK.\n") err=opus_encoder_ctl(enc,OPUS_GET_BITRATE((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; if(opus_encoder_ctl(enc,OPUS_SET_BITRATE(1073741832))!=OPUS_OK)test_failed(); cfgs++; VG_UNDEF(&i,sizeof(i)); if(opus_encoder_ctl(enc,OPUS_GET_BITRATE(&i))!=OPUS_OK)test_failed(); if(i>700000||i<256000)test_failed(); cfgs++; CHECK_SETGET(OPUS_SET_BITRATE(i),OPUS_GET_BITRATE(&i),-12345,0, 500,256000, " OPUS_SET_BITRATE ............................. OK.\n", " OPUS_GET_BITRATE ............................. OK.\n") err=opus_encoder_ctl(enc,OPUS_GET_FORCE_CHANNELS((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; CHECK_SETGET(OPUS_SET_FORCE_CHANNELS(i),OPUS_GET_FORCE_CHANNELS(&i),-1,3, 1,OPUS_AUTO, " OPUS_SET_FORCE_CHANNELS ...................... OK.\n", " OPUS_GET_FORCE_CHANNELS ...................... OK.\n") i=-2; if(opus_encoder_ctl(enc,OPUS_SET_BANDWIDTH(i))==OPUS_OK)test_failed(); cfgs++; i=OPUS_BANDWIDTH_FULLBAND+1; if(opus_encoder_ctl(enc,OPUS_SET_BANDWIDTH(i))==OPUS_OK)test_failed(); cfgs++; i=OPUS_BANDWIDTH_NARROWBAND; if(opus_encoder_ctl(enc,OPUS_SET_BANDWIDTH(i))!=OPUS_OK)test_failed(); cfgs++; i=OPUS_BANDWIDTH_FULLBAND; if(opus_encoder_ctl(enc,OPUS_SET_BANDWIDTH(i))!=OPUS_OK)test_failed(); cfgs++; i=OPUS_BANDWIDTH_WIDEBAND; if(opus_encoder_ctl(enc,OPUS_SET_BANDWIDTH(i))!=OPUS_OK)test_failed(); cfgs++; i=OPUS_BANDWIDTH_MEDIUMBAND; if(opus_encoder_ctl(enc,OPUS_SET_BANDWIDTH(i))!=OPUS_OK)test_failed(); cfgs++; fprintf(stdout," OPUS_SET_BANDWIDTH ........................... OK.\n"); /*We don't test if the bandwidth has actually changed. because the change may be delayed until the encoder is advanced.*/ i=-12345; VG_UNDEF(&i,sizeof(i)); err=opus_encoder_ctl(enc,OPUS_GET_BANDWIDTH(&i)); if(err!=OPUS_OK || (i!=OPUS_BANDWIDTH_NARROWBAND&& i!=OPUS_BANDWIDTH_MEDIUMBAND&&i!=OPUS_BANDWIDTH_WIDEBAND&& i!=OPUS_BANDWIDTH_FULLBAND&&i!=OPUS_AUTO))test_failed(); cfgs++; if(opus_encoder_ctl(enc,OPUS_SET_BANDWIDTH(OPUS_AUTO))!=OPUS_OK)test_failed(); cfgs++; err=opus_encoder_ctl(enc,OPUS_GET_BANDWIDTH((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; fprintf(stdout," OPUS_GET_BANDWIDTH ........................... OK.\n"); i=-2; if(opus_encoder_ctl(enc,OPUS_SET_MAX_BANDWIDTH(i))==OPUS_OK)test_failed(); cfgs++; i=OPUS_BANDWIDTH_FULLBAND+1; if(opus_encoder_ctl(enc,OPUS_SET_MAX_BANDWIDTH(i))==OPUS_OK)test_failed(); cfgs++; i=OPUS_BANDWIDTH_NARROWBAND; if(opus_encoder_ctl(enc,OPUS_SET_MAX_BANDWIDTH(i))!=OPUS_OK)test_failed(); cfgs++; i=OPUS_BANDWIDTH_FULLBAND; if(opus_encoder_ctl(enc,OPUS_SET_MAX_BANDWIDTH(i))!=OPUS_OK)test_failed(); cfgs++; i=OPUS_BANDWIDTH_WIDEBAND; if(opus_encoder_ctl(enc,OPUS_SET_MAX_BANDWIDTH(i))!=OPUS_OK)test_failed(); cfgs++; i=OPUS_BANDWIDTH_MEDIUMBAND; if(opus_encoder_ctl(enc,OPUS_SET_MAX_BANDWIDTH(i))!=OPUS_OK)test_failed(); cfgs++; fprintf(stdout," OPUS_SET_MAX_BANDWIDTH ....................... OK.\n"); /*We don't test if the bandwidth has actually changed. because the change may be delayed until the encoder is advanced.*/ i=-12345; VG_UNDEF(&i,sizeof(i)); err=opus_encoder_ctl(enc,OPUS_GET_MAX_BANDWIDTH(&i)); if(err!=OPUS_OK || (i!=OPUS_BANDWIDTH_NARROWBAND&& i!=OPUS_BANDWIDTH_MEDIUMBAND&&i!=OPUS_BANDWIDTH_WIDEBAND&& i!=OPUS_BANDWIDTH_FULLBAND))test_failed(); cfgs++; err=opus_encoder_ctl(enc,OPUS_GET_MAX_BANDWIDTH((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; fprintf(stdout," OPUS_GET_MAX_BANDWIDTH ....................... OK.\n"); err=opus_encoder_ctl(enc,OPUS_GET_DTX((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; CHECK_SETGET(OPUS_SET_DTX(i),OPUS_GET_DTX(&i),-1,2, 1,0, " OPUS_SET_DTX ................................. OK.\n", " OPUS_GET_DTX ................................. OK.\n") err=opus_encoder_ctl(enc,OPUS_GET_COMPLEXITY((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; CHECK_SETGET(OPUS_SET_COMPLEXITY(i),OPUS_GET_COMPLEXITY(&i),-1,11, 0,10, " OPUS_SET_COMPLEXITY .......................... OK.\n", " OPUS_GET_COMPLEXITY .......................... OK.\n") err=opus_encoder_ctl(enc,OPUS_GET_INBAND_FEC((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; CHECK_SETGET(OPUS_SET_INBAND_FEC(i),OPUS_GET_INBAND_FEC(&i),-1,2, 1,0, " OPUS_SET_INBAND_FEC .......................... OK.\n", " OPUS_GET_INBAND_FEC .......................... OK.\n") err=opus_encoder_ctl(enc,OPUS_GET_PACKET_LOSS_PERC((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; CHECK_SETGET(OPUS_SET_PACKET_LOSS_PERC(i),OPUS_GET_PACKET_LOSS_PERC(&i),-1,101, 100,0, " OPUS_SET_PACKET_LOSS_PERC .................... OK.\n", " OPUS_GET_PACKET_LOSS_PERC .................... OK.\n") err=opus_encoder_ctl(enc,OPUS_GET_VBR((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; CHECK_SETGET(OPUS_SET_VBR(i),OPUS_GET_VBR(&i),-1,2, 1,0, " OPUS_SET_VBR ................................. OK.\n", " OPUS_GET_VBR ................................. OK.\n") /* err=opus_encoder_ctl(enc,OPUS_GET_VOICE_RATIO((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; CHECK_SETGET(OPUS_SET_VOICE_RATIO(i),OPUS_GET_VOICE_RATIO(&i),-2,101, 0,50, " OPUS_SET_VOICE_RATIO ......................... OK.\n", " OPUS_GET_VOICE_RATIO ......................... OK.\n")*/ err=opus_encoder_ctl(enc,OPUS_GET_VBR_CONSTRAINT((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; CHECK_SETGET(OPUS_SET_VBR_CONSTRAINT(i),OPUS_GET_VBR_CONSTRAINT(&i),-1,2, 1,0, " OPUS_SET_VBR_CONSTRAINT ...................... OK.\n", " OPUS_GET_VBR_CONSTRAINT ...................... OK.\n") err=opus_encoder_ctl(enc,OPUS_GET_SIGNAL((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; CHECK_SETGET(OPUS_SET_SIGNAL(i),OPUS_GET_SIGNAL(&i),-12345,0x7FFFFFFF, OPUS_SIGNAL_MUSIC,OPUS_AUTO, " OPUS_SET_SIGNAL .............................. OK.\n", " OPUS_GET_SIGNAL .............................. OK.\n") err=opus_encoder_ctl(enc,OPUS_GET_LSB_DEPTH((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; CHECK_SETGET(OPUS_SET_LSB_DEPTH(i),OPUS_GET_LSB_DEPTH(&i),7,25,16,24, " OPUS_SET_LSB_DEPTH ........................... OK.\n", " OPUS_GET_LSB_DEPTH ........................... OK.\n") err=opus_encoder_ctl(enc,OPUS_GET_PREDICTION_DISABLED(&i)); if(i!=0)test_failed(); cfgs++; err=opus_encoder_ctl(enc,OPUS_GET_PREDICTION_DISABLED((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; CHECK_SETGET(OPUS_SET_PREDICTION_DISABLED(i),OPUS_GET_PREDICTION_DISABLED(&i),-1,2,1,0, " OPUS_SET_PREDICTION_DISABLED ................. OK.\n", " OPUS_GET_PREDICTION_DISABLED ................. OK.\n") err=opus_encoder_ctl(enc,OPUS_GET_EXPERT_FRAME_DURATION((opus_int32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; err=opus_encoder_ctl(enc,OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_2_5_MS)); if(err!=OPUS_OK)test_failed(); cfgs++; err=opus_encoder_ctl(enc,OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_5_MS)); if(err!=OPUS_OK)test_failed(); cfgs++; err=opus_encoder_ctl(enc,OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_10_MS)); if(err!=OPUS_OK)test_failed(); cfgs++; err=opus_encoder_ctl(enc,OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_20_MS)); if(err!=OPUS_OK)test_failed(); cfgs++; err=opus_encoder_ctl(enc,OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_40_MS)); if(err!=OPUS_OK)test_failed(); cfgs++; err=opus_encoder_ctl(enc,OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_60_MS)); if(err!=OPUS_OK)test_failed(); cfgs++; CHECK_SETGET(OPUS_SET_EXPERT_FRAME_DURATION(i),OPUS_GET_EXPERT_FRAME_DURATION(&i),0,-1, OPUS_FRAMESIZE_60_MS,OPUS_FRAMESIZE_ARG, " OPUS_SET_EXPERT_FRAME_DURATION ............... OK.\n", " OPUS_GET_EXPERT_FRAME_DURATION ............... OK.\n") /*OPUS_SET_FORCE_MODE is not tested here because it's not a public API, however the encoder tests use it*/ err=opus_encoder_ctl(enc,OPUS_GET_FINAL_RANGE((opus_uint32 *)NULL)); if(err!=OPUS_BAD_ARG)test_failed(); cfgs++; if(opus_encoder_ctl(enc,OPUS_GET_FINAL_RANGE(&enc_final_range))!=OPUS_OK)test_failed(); cfgs++; fprintf(stdout," OPUS_GET_FINAL_RANGE ......................... OK.\n"); /*Reset the encoder*/ if(opus_encoder_ctl(enc, OPUS_RESET_STATE)!=OPUS_OK)test_failed(); cfgs++; fprintf(stdout," OPUS_RESET_STATE ............................. OK.\n"); memset(sbuf,0,sizeof(short)*2*960); VG_UNDEF(packet,sizeof(packet)); i=opus_encode(enc, sbuf, 960, packet, sizeof(packet)); if(i<1 || (i>(opus_int32)sizeof(packet)))test_failed(); VG_CHECK(packet,i); cfgs++; fprintf(stdout," opus_encode() ................................ OK.\n"); #ifndef DISABLE_FLOAT_API memset(fbuf,0,sizeof(float)*2*960); VG_UNDEF(packet,sizeof(packet)); i=opus_encode_float(enc, fbuf, 960, packet, sizeof(packet)); if(i<1 || (i>(opus_int32)sizeof(packet)))test_failed(); VG_CHECK(packet,i); cfgs++; fprintf(stdout," opus_encode_float() .......................... OK.\n"); #endif #if 0 /*These tests are disabled because the library crashes with null states*/ if(opus_encoder_ctl(0,OPUS_RESET_STATE) !=OPUS_INVALID_STATE)test_failed(); if(opus_encoder_init(0,48000,1,OPUS_APPLICATION_VOIP) !=OPUS_INVALID_STATE)test_failed(); if(opus_encode(0,sbuf,960,packet,sizeof(packet)) !=OPUS_INVALID_STATE)test_failed(); if(opus_encode_float(0,fbuf,960,packet,sizeof(packet))!=OPUS_INVALID_STATE)test_failed(); #endif opus_encoder_destroy(enc); cfgs++; fprintf(stdout," All encoder interface tests passed\n"); fprintf(stdout," (%d API invocations)\n",cfgs); return cfgs; } #define max_out (1276*48+48*2+2) int test_repacketizer_api(void) { int ret,cfgs,i,j,k; OpusRepacketizer *rp; unsigned char *packet; unsigned char *po; cfgs=0; fprintf(stdout,"\n Repacketizer tests\n"); fprintf(stdout," ---------------------------------------------------\n"); packet=malloc(max_out); if(packet==NULL)test_failed(); memset(packet,0,max_out); po=malloc(max_out+256); if(po==NULL)test_failed(); i=opus_repacketizer_get_size(); if(i<=0)test_failed(); cfgs++; fprintf(stdout," opus_repacketizer_get_size()=%d ............. OK.\n",i); rp=malloc(i); rp=opus_repacketizer_init(rp); if(rp==NULL)test_failed(); cfgs++; free(rp); fprintf(stdout," opus_repacketizer_init ....................... OK.\n"); rp=opus_repacketizer_create(); if(rp==NULL)test_failed(); cfgs++; fprintf(stdout," opus_repacketizer_create ..................... OK.\n"); if(opus_repacketizer_get_nb_frames(rp)!=0)test_failed(); cfgs++; fprintf(stdout," opus_repacketizer_get_nb_frames .............. OK.\n"); /*Length overflows*/ VG_UNDEF(packet,4); if(opus_repacketizer_cat(rp,packet,0)!=OPUS_INVALID_PACKET)test_failed(); /* Zero len */ cfgs++; packet[0]=1; if(opus_repacketizer_cat(rp,packet,2)!=OPUS_INVALID_PACKET)test_failed(); /* Odd payload code 1 */ cfgs++; packet[0]=2; if(opus_repacketizer_cat(rp,packet,1)!=OPUS_INVALID_PACKET)test_failed(); /* Code 2 overflow one */ cfgs++; packet[0]=3; if(opus_repacketizer_cat(rp,packet,1)!=OPUS_INVALID_PACKET)test_failed(); /* Code 3 no count */ cfgs++; packet[0]=2; packet[1]=255; if(opus_repacketizer_cat(rp,packet,2)!=OPUS_INVALID_PACKET)test_failed(); /* Code 2 overflow two */ cfgs++; packet[0]=2; packet[1]=250; if(opus_repacketizer_cat(rp,packet,251)!=OPUS_INVALID_PACKET)test_failed(); /* Code 2 overflow three */ cfgs++; packet[0]=3; packet[1]=0; if(opus_repacketizer_cat(rp,packet,2)!=OPUS_INVALID_PACKET)test_failed(); /* Code 3 m=0 */ cfgs++; packet[1]=49; if(opus_repacketizer_cat(rp,packet,100)!=OPUS_INVALID_PACKET)test_failed(); /* Code 3 m=49 */ cfgs++; packet[0]=0; if(opus_repacketizer_cat(rp,packet,3)!=OPUS_OK)test_failed(); cfgs++; packet[0]=1<<2; if(opus_repacketizer_cat(rp,packet,3)!=OPUS_INVALID_PACKET)test_failed(); /* Change in TOC */ cfgs++; /* Code 0,1,3 CBR -> Code 0,1,3 CBR */ opus_repacketizer_init(rp); for(j=0;j<32;j++) { /* TOC types, test half with stereo */ int maxi; packet[0]=((j<<1)+(j&1))<<2; maxi=960/opus_packet_get_samples_per_frame(packet,8000); for(i=1;i<=maxi;i++) { /* Number of CBR frames in the input packets */ int maxp; packet[0]=((j<<1)+(j&1))<<2; if(i>1)packet[0]+=i==2?1:3; packet[1]=i>2?i:0; maxp=960/(i*opus_packet_get_samples_per_frame(packet,8000)); for(k=0;k<=(1275+75);k+=3) { /*Payload size*/ opus_int32 cnt,rcnt; if(k%i!=0)continue; /* Only testing CBR here, payload must be a multiple of the count */ for(cnt=0;cnt0) { ret=opus_repacketizer_cat(rp,packet,k+(i>2?2:1)); if((cnt<=maxp&&k<=(1275*i))?ret!=OPUS_OK:ret!=OPUS_INVALID_PACKET)test_failed(); cfgs++; } rcnt=k<=(1275*i)?(cnt0) { int len; len=k*rcnt+((rcnt*i)>2?2:1); if(ret!=len)test_failed(); if((rcnt*i)<2&&(po[0]&3)!=0)test_failed(); /* Code 0 */ if((rcnt*i)==2&&(po[0]&3)!=1)test_failed(); /* Code 1 */ if((rcnt*i)>2&&(((po[0]&3)!=3)||(po[1]!=rcnt*i)))test_failed(); /* Code 3 CBR */ cfgs++; if(opus_repacketizer_out(rp,po,len)!=len)test_failed(); cfgs++; if(opus_packet_unpad(po,len)!=len)test_failed(); cfgs++; if(opus_packet_pad(po,len,len+1)!=OPUS_OK)test_failed(); cfgs++; if(opus_packet_pad(po,len+1,len+256)!=OPUS_OK)test_failed(); cfgs++; if(opus_packet_unpad(po,len+256)!=len)test_failed(); cfgs++; if(opus_multistream_packet_unpad(po,len,1)!=len)test_failed(); cfgs++; if(opus_multistream_packet_pad(po,len,len+1,1)!=OPUS_OK)test_failed(); cfgs++; if(opus_multistream_packet_pad(po,len+1,len+256,1)!=OPUS_OK)test_failed(); cfgs++; if(opus_multistream_packet_unpad(po,len+256,1)!=len)test_failed(); cfgs++; if(opus_repacketizer_out(rp,po,len-1)!=OPUS_BUFFER_TOO_SMALL)test_failed(); cfgs++; if(len>1) { if(opus_repacketizer_out(rp,po,1)!=OPUS_BUFFER_TOO_SMALL)test_failed(); cfgs++; } if(opus_repacketizer_out(rp,po,0)!=OPUS_BUFFER_TOO_SMALL)test_failed(); cfgs++; } else if (ret!=OPUS_BAD_ARG)test_failed(); /* M must not be 0 */ } opus_repacketizer_init(rp); } } } /*Change in input count code, CBR out*/ opus_repacketizer_init(rp); packet[0]=0; if(opus_repacketizer_cat(rp,packet,5)!=OPUS_OK)test_failed(); cfgs++; packet[0]+=1; if(opus_repacketizer_cat(rp,packet,9)!=OPUS_OK)test_failed(); cfgs++; i=opus_repacketizer_out(rp,po,max_out); if((i!=(4+8+2))||((po[0]&3)!=3)||((po[1]&63)!=3)||((po[1]>>7)!=0))test_failed(); cfgs++; i=opus_repacketizer_out_range(rp,0,1,po,max_out); if(i!=5||(po[0]&3)!=0)test_failed(); cfgs++; i=opus_repacketizer_out_range(rp,1,2,po,max_out); if(i!=5||(po[0]&3)!=0)test_failed(); cfgs++; /*Change in input count code, VBR out*/ opus_repacketizer_init(rp); packet[0]=1; if(opus_repacketizer_cat(rp,packet,9)!=OPUS_OK)test_failed(); cfgs++; packet[0]=0; if(opus_repacketizer_cat(rp,packet,3)!=OPUS_OK)test_failed(); cfgs++; i=opus_repacketizer_out(rp,po,max_out); if((i!=(2+8+2+2))||((po[0]&3)!=3)||((po[1]&63)!=3)||((po[1]>>7)!=1))test_failed(); cfgs++; /*VBR in, VBR out*/ opus_repacketizer_init(rp); packet[0]=2; packet[1]=4; if(opus_repacketizer_cat(rp,packet,8)!=OPUS_OK)test_failed(); cfgs++; if(opus_repacketizer_cat(rp,packet,8)!=OPUS_OK)test_failed(); cfgs++; i=opus_repacketizer_out(rp,po,max_out); if((i!=(2+1+1+1+4+2+4+2))||((po[0]&3)!=3)||((po[1]&63)!=4)||((po[1]>>7)!=1))test_failed(); cfgs++; /*VBR in, CBR out*/ opus_repacketizer_init(rp); packet[0]=2; packet[1]=4; if(opus_repacketizer_cat(rp,packet,10)!=OPUS_OK)test_failed(); cfgs++; if(opus_repacketizer_cat(rp,packet,10)!=OPUS_OK)test_failed(); cfgs++; i=opus_repacketizer_out(rp,po,max_out); if((i!=(2+4+4+4+4))||((po[0]&3)!=3)||((po[1]&63)!=4)||((po[1]>>7)!=0))test_failed(); cfgs++; /*Count 0 in, VBR out*/ for(j=0;j<32;j++) { /* TOC types, test half with stereo */ int maxi,sum,rcnt; packet[0]=((j<<1)+(j&1))<<2; maxi=960/opus_packet_get_samples_per_frame(packet,8000); sum=0; rcnt=0; opus_repacketizer_init(rp); for(i=1;i<=maxi+2;i++) { int len; ret=opus_repacketizer_cat(rp,packet,i); if(rcnt2&&(po[1]&63)!=rcnt)test_failed(); if(rcnt==2&&(po[0]&3)!=2)test_failed(); if(rcnt==1&&(po[0]&3)!=0)test_failed(); cfgs++; if(opus_repacketizer_out(rp,po,len)!=len)test_failed(); cfgs++; if(opus_packet_unpad(po,len)!=len)test_failed(); cfgs++; if(opus_packet_pad(po,len,len+1)!=OPUS_OK)test_failed(); cfgs++; if(opus_packet_pad(po,len+1,len+256)!=OPUS_OK)test_failed(); cfgs++; if(opus_packet_unpad(po,len+256)!=len)test_failed(); cfgs++; if(opus_multistream_packet_unpad(po,len,1)!=len)test_failed(); cfgs++; if(opus_multistream_packet_pad(po,len,len+1,1)!=OPUS_OK)test_failed(); cfgs++; if(opus_multistream_packet_pad(po,len+1,len+256,1)!=OPUS_OK)test_failed(); cfgs++; if(opus_multistream_packet_unpad(po,len+256,1)!=len)test_failed(); cfgs++; if(opus_repacketizer_out(rp,po,len-1)!=OPUS_BUFFER_TOO_SMALL)test_failed(); cfgs++; if(len>1) { if(opus_repacketizer_out(rp,po,1)!=OPUS_BUFFER_TOO_SMALL)test_failed(); cfgs++; } if(opus_repacketizer_out(rp,po,0)!=OPUS_BUFFER_TOO_SMALL)test_failed(); cfgs++; } } po[0]='O'; po[1]='p'; if(opus_packet_pad(po,4,4)!=OPUS_OK)test_failed(); cfgs++; if(opus_multistream_packet_pad(po,4,4,1)!=OPUS_OK)test_failed(); cfgs++; if(opus_packet_pad(po,4,5)!=OPUS_BAD_ARG)test_failed(); cfgs++; if(opus_multistream_packet_pad(po,4,5,1)!=OPUS_BAD_ARG)test_failed(); cfgs++; if(opus_packet_pad(po,0,5)!=OPUS_BAD_ARG)test_failed(); cfgs++; if(opus_multistream_packet_pad(po,0,5,1)!=OPUS_BAD_ARG)test_failed(); cfgs++; if(opus_packet_unpad(po,0)!=OPUS_BAD_ARG)test_failed(); cfgs++; if(opus_multistream_packet_unpad(po,0,1)!=OPUS_BAD_ARG)test_failed(); cfgs++; if(opus_packet_unpad(po,4)!=OPUS_INVALID_PACKET)test_failed(); cfgs++; if(opus_multistream_packet_unpad(po,4,1)!=OPUS_INVALID_PACKET)test_failed(); cfgs++; po[0]=0; po[1]=0; po[2]=0; if(opus_packet_pad(po,5,4)!=OPUS_BAD_ARG)test_failed(); cfgs++; if(opus_multistream_packet_pad(po,5,4,1)!=OPUS_BAD_ARG)test_failed(); cfgs++; fprintf(stdout," opus_repacketizer_cat ........................ OK.\n"); fprintf(stdout," opus_repacketizer_out ........................ OK.\n"); fprintf(stdout," opus_repacketizer_out_range .................. OK.\n"); fprintf(stdout," opus_packet_pad .............................. OK.\n"); fprintf(stdout," opus_packet_unpad ............................ OK.\n"); fprintf(stdout," opus_multistream_packet_pad .................. OK.\n"); fprintf(stdout," opus_multistream_packet_unpad ................ OK.\n"); opus_repacketizer_destroy(rp); cfgs++; free(packet); free(po); fprintf(stdout," All repacketizer tests passed\n"); fprintf(stdout," (%7d API invocations)\n",cfgs); return cfgs; } #ifdef MALLOC_FAIL /* GLIBC 2.14 declares __malloc_hook as deprecated, generating a warning * under GCC. However, this is the cleanest way to test malloc failure * handling in our codebase, and the lack of thread safety isn't an * issue here. We therefore disable the warning for this function. */ #if OPUS_GNUC_PREREQ(4,6) /* Save the current warning settings */ #pragma GCC diagnostic push #endif #pragma GCC diagnostic ignored "-Wdeprecated-declarations" typedef void *(*mhook)(size_t __size, __const void *); #endif int test_malloc_fail(void) { #ifdef MALLOC_FAIL OpusDecoder *dec; OpusEncoder *enc; OpusRepacketizer *rp; unsigned char mapping[256] = {0,1}; OpusMSDecoder *msdec; OpusMSEncoder *msenc; int rate,c,app,cfgs,err,useerr; int *ep; mhook orig_malloc; cfgs=0; #endif fprintf(stdout,"\n malloc() failure tests\n"); fprintf(stdout," ---------------------------------------------------\n"); #ifdef MALLOC_FAIL orig_malloc=__malloc_hook; __malloc_hook=malloc_hook; ep=(int *)opus_alloc(sizeof(int)); if(ep!=NULL) { if(ep)free(ep); __malloc_hook=orig_malloc; #endif fprintf(stdout," opus_decoder_create() ................... SKIPPED.\n"); fprintf(stdout," opus_encoder_create() ................... SKIPPED.\n"); fprintf(stdout," opus_repacketizer_create() .............. SKIPPED.\n"); fprintf(stdout," opus_multistream_decoder_create() ....... SKIPPED.\n"); fprintf(stdout," opus_multistream_encoder_create() ....... SKIPPED.\n"); fprintf(stdout,"(Test only supported with GLIBC and without valgrind)\n"); return 0; #ifdef MALLOC_FAIL } for(useerr=0;useerr<2;useerr++) { ep=useerr?&err:0; for(rate=0;rate<5;rate++) { for(c=1;c<3;c++) { err=1; if(useerr) { VG_UNDEF(&err,sizeof(err)); } dec=opus_decoder_create(opus_rates[rate], c, ep); if(dec!=NULL||(useerr&&err!=OPUS_ALLOC_FAIL)) { __malloc_hook=orig_malloc; test_failed(); } cfgs++; msdec=opus_multistream_decoder_create(opus_rates[rate], c, 1, c-1, mapping, ep); if(msdec!=NULL||(useerr&&err!=OPUS_ALLOC_FAIL)) { __malloc_hook=orig_malloc; test_failed(); } cfgs++; for(app=0;app<3;app++) { if(useerr) { VG_UNDEF(&err,sizeof(err)); } enc=opus_encoder_create(opus_rates[rate], c, opus_apps[app],ep); if(enc!=NULL||(useerr&&err!=OPUS_ALLOC_FAIL)) { __malloc_hook=orig_malloc; test_failed(); } cfgs++; msenc=opus_multistream_encoder_create(opus_rates[rate], c, 1, c-1, mapping, opus_apps[app],ep); if(msenc!=NULL||(useerr&&err!=OPUS_ALLOC_FAIL)) { __malloc_hook=orig_malloc; test_failed(); } cfgs++; } } } } rp=opus_repacketizer_create(); if(rp!=NULL) { __malloc_hook=orig_malloc; test_failed(); } cfgs++; __malloc_hook=orig_malloc; fprintf(stdout," opus_decoder_create() ........................ OK.\n"); fprintf(stdout," opus_encoder_create() ........................ OK.\n"); fprintf(stdout," opus_repacketizer_create() ................... OK.\n"); fprintf(stdout," opus_multistream_decoder_create() ............ OK.\n"); fprintf(stdout," opus_multistream_encoder_create() ............ OK.\n"); fprintf(stdout," All malloc failure tests passed\n"); fprintf(stdout," (%2d API invocations)\n",cfgs); return cfgs; #endif } #ifdef MALLOC_FAIL #if __GNUC_PREREQ(4,6) #pragma GCC diagnostic pop /* restore -Wdeprecated-declarations */ #endif #endif int main(int _argc, char **_argv) { opus_int32 total; const char * oversion; if(_argc>1) { fprintf(stderr,"Usage: %s\n",_argv[0]); return 1; } iseed=0; oversion=opus_get_version_string(); if(!oversion)test_failed(); fprintf(stderr,"Testing the %s API deterministically\n", oversion); if(opus_strerror(-32768)==NULL)test_failed(); if(opus_strerror(32767)==NULL)test_failed(); if(strlen(opus_strerror(0))<1)test_failed(); total=4; total+=test_dec_api(); total+=test_msdec_api(); total+=test_parse(); total+=test_enc_api(); total+=test_repacketizer_api(); total+=test_malloc_fail(); fprintf(stderr,"\nAll API tests passed.\nThe libopus API was invoked %d times.\n",total); return 0; } ================================================ FILE: deps/pjsip/third_party/opus/tests/test_opus_common.h ================================================ /* Copyright (c) 2011 Xiph.Org Foundation Written by Gregory Maxwell */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ static OPUS_INLINE void deb2_impl(unsigned char *_t,unsigned char **_p,int _k,int _x,int _y) { int i; if(_x>2){ if(_y<3)for(i=0;i<_y;i++)*(--*_p)=_t[i+1]; }else{ _t[_x]=_t[_x-_y]; deb2_impl(_t,_p,_k,_x+1,_y); for(i=_t[_x-_y]+1;i<_k;i++){ _t[_x]=i; deb2_impl(_t,_p,_k,_x+1,_x); } } } /*Generates a De Bruijn sequence (k,2) with length k^2*/ static OPUS_INLINE void debruijn2(int _k, unsigned char *_res) { unsigned char *p; unsigned char *t; t=malloc(sizeof(unsigned char)*_k*2); memset(t,0,sizeof(unsigned char)*_k*2); p=&_res[_k*_k]; deb2_impl(t,&p,_k,1,1); free(t); } /*MWC RNG of George Marsaglia*/ static opus_uint32 Rz, Rw; static OPUS_INLINE opus_uint32 fast_rand(void) { Rz=36969*(Rz&65535)+(Rz>>16); Rw=18000*(Rw&65535)+(Rw>>16); return (Rz<<16)+Rw; } static opus_uint32 iseed; #ifdef __GNUC__ __attribute__((noreturn)) #endif static OPUS_INLINE void _test_failed(const char *file, int line) { fprintf(stderr,"\n ***************************************************\n"); fprintf(stderr," *** A fatal error was detected. ***\n"); fprintf(stderr," ***************************************************\n"); fprintf(stderr,"Please report this failure and include\n"); fprintf(stderr,"'make check SEED=%u fails %s at line %d for %s'\n",iseed,file,line,opus_get_version_string()); fprintf(stderr,"and any relevant details about your system.\n\n"); abort(); } #define test_failed() _test_failed(__FILE__, __LINE__); ================================================ FILE: deps/pjsip/third_party/opus/tests/test_opus_decode.c ================================================ /* Copyright (c) 2011-2013 Xiph.Org Foundation Written by Gregory Maxwell */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #if (!defined WIN32 && !defined _WIN32) || defined(__MINGW32__) #include #else #include #define getpid _getpid #endif #include "opus.h" #include "test_opus_common.h" #define MAX_PACKET (1500) #define MAX_FRAME_SAMP (5760) int test_decoder_code0(int no_fuzz) { static const opus_int32 fsv[5]={48000,24000,16000,12000,8000}; int err,skip,plen; int out_samples,fec; int t; opus_int32 i; OpusDecoder *dec[5*2]; opus_int32 decsize; OpusDecoder *decbak; opus_uint32 dec_final_range1,dec_final_range2,dec_final_acc; unsigned char *packet; unsigned char modes[4096]; short *outbuf_int; short *outbuf; dec_final_range1=dec_final_range2=2; packet=malloc(sizeof(unsigned char)*MAX_PACKET); if(packet==NULL)test_failed(); outbuf_int=malloc(sizeof(short)*(MAX_FRAME_SAMP+16)*2); for(i=0;i<(MAX_FRAME_SAMP+16)*2;i++)outbuf_int[i]=32749; outbuf=&outbuf_int[8*2]; fprintf(stdout," Starting %d decoders...\n",5*2); for(t=0;t<5*2;t++) { int fs=fsv[t>>1]; int c=(t&1)+1; err=OPUS_INTERNAL_ERROR; dec[t] = opus_decoder_create(fs, c, &err); if(err!=OPUS_OK || dec[t]==NULL)test_failed(); fprintf(stdout," opus_decoder_create(%5d,%d) OK. Copy ",fs,c); { OpusDecoder *dec2; /*The opus state structures contain no pointers and can be freely copied*/ dec2=(OpusDecoder *)malloc(opus_decoder_get_size(c)); if(dec2==NULL)test_failed(); memcpy(dec2,dec[t],opus_decoder_get_size(c)); memset(dec[t],255,opus_decoder_get_size(c)); opus_decoder_destroy(dec[t]); printf("OK.\n"); dec[t]=dec2; } } decsize=opus_decoder_get_size(1); decbak=(OpusDecoder *)malloc(decsize); if(decbak==NULL)test_failed(); for(t=0;t<5*2;t++) { int factor=48000/fsv[t>>1]; for(fec=0;fec<2;fec++) { int dur; /*Test PLC on a fresh decoder*/ out_samples = opus_decode(dec[t], 0, 0, outbuf, 120/factor, fec); if(out_samples!=120/factor)test_failed(); if(opus_decoder_ctl(dec[t], OPUS_GET_LAST_PACKET_DURATION(&dur))!=OPUS_OK)test_failed(); if(dur!=120/factor)test_failed(); /*Test on a size which isn't a multiple of 2.5ms*/ out_samples = opus_decode(dec[t], 0, 0, outbuf, 120/factor+2, fec); if(out_samples!=OPUS_BAD_ARG)test_failed(); /*Test null pointer input*/ out_samples = opus_decode(dec[t], 0, -1, outbuf, 120/factor, fec); if(out_samples!=120/factor)test_failed(); out_samples = opus_decode(dec[t], 0, 1, outbuf, 120/factor, fec); if(out_samples!=120/factor)test_failed(); out_samples = opus_decode(dec[t], 0, 10, outbuf, 120/factor, fec); if(out_samples!=120/factor)test_failed(); out_samples = opus_decode(dec[t], 0, fast_rand(), outbuf, 120/factor, fec); if(out_samples!=120/factor)test_failed(); if(opus_decoder_ctl(dec[t], OPUS_GET_LAST_PACKET_DURATION(&dur))!=OPUS_OK)test_failed(); if(dur!=120/factor)test_failed(); /*Zero lengths*/ out_samples = opus_decode(dec[t], packet, 0, outbuf, 120/factor, fec); if(out_samples!=120/factor)test_failed(); /*Zero buffer*/ outbuf[0]=32749; out_samples = opus_decode(dec[t], packet, 0, outbuf, 0, fec); if(out_samples>0)test_failed(); out_samples = opus_decode(dec[t], packet, 0, 0, 0, fec); if(out_samples>0)test_failed(); if(outbuf[0]!=32749)test_failed(); /*Invalid lengths*/ out_samples = opus_decode(dec[t], packet, -1, outbuf, MAX_FRAME_SAMP, fec); if(out_samples>=0)test_failed(); out_samples = opus_decode(dec[t], packet, INT_MIN, outbuf, MAX_FRAME_SAMP, fec); if(out_samples>=0)test_failed(); out_samples = opus_decode(dec[t], packet, -1, outbuf, -1, fec); if(out_samples>=0)test_failed(); /*Crazy FEC values*/ out_samples = opus_decode(dec[t], packet, 1, outbuf, MAX_FRAME_SAMP, fec?-1:2); if(out_samples>=0)test_failed(); /*Reset the decoder*/ if(opus_decoder_ctl(dec[t], OPUS_RESET_STATE)!=OPUS_OK)test_failed(); } } fprintf(stdout," dec[all] initial frame PLC OK.\n"); /*Count code 0 tests*/ for(i=0;i<64;i++) { int dur; int j,expected[5*2]; packet[0]=i<<2; packet[1]=255; packet[2]=255; err=opus_packet_get_nb_channels(packet); if(err!=(i&1)+1)test_failed(); for(t=0;t<5*2;t++){ expected[t]=opus_decoder_get_nb_samples(dec[t],packet,1); if(expected[t]>2880)test_failed(); } for(j=0;j<256;j++) { packet[1]=j; for(t=0;t<5*2;t++) { out_samples = opus_decode(dec[t], packet, 3, outbuf, MAX_FRAME_SAMP, 0); if(out_samples!=expected[t])test_failed(); if(opus_decoder_ctl(dec[t], OPUS_GET_LAST_PACKET_DURATION(&dur))!=OPUS_OK)test_failed(); if(dur!=out_samples)test_failed(); opus_decoder_ctl(dec[t], OPUS_GET_FINAL_RANGE(&dec_final_range1)); if(t==0)dec_final_range2=dec_final_range1; else if(dec_final_range1!=dec_final_range2)test_failed(); } } for(t=0;t<5*2;t++){ int factor=48000/fsv[t>>1]; /* The PLC is run for 6 frames in order to get better PLC coverage. */ for(j=0;j<6;j++) { out_samples = opus_decode(dec[t], 0, 0, outbuf, expected[t], 0); if(out_samples!=expected[t])test_failed(); if(opus_decoder_ctl(dec[t], OPUS_GET_LAST_PACKET_DURATION(&dur))!=OPUS_OK)test_failed(); if(dur!=out_samples)test_failed(); } /* Run the PLC once at 2.5ms, as a simulation of someone trying to do small drift corrections. */ if(expected[t]!=120/factor) { out_samples = opus_decode(dec[t], 0, 0, outbuf, 120/factor, 0); if(out_samples!=120/factor)test_failed(); if(opus_decoder_ctl(dec[t], OPUS_GET_LAST_PACKET_DURATION(&dur))!=OPUS_OK)test_failed(); if(dur!=out_samples)test_failed(); } out_samples = opus_decode(dec[t], packet, 2, outbuf, expected[t]-1, 0); if(out_samples>0)test_failed(); } } fprintf(stdout," dec[all] all 2-byte prefix for length 3 and PLC, all modes (64) OK.\n"); if(no_fuzz) { fprintf(stdout," Skipping many tests which fuzz the decoder as requested.\n"); free(decbak); for(t=0;t<5*2;t++)opus_decoder_destroy(dec[t]); printf(" Decoders stopped.\n"); err=0; for(i=0;i<8*2;i++)err|=outbuf_int[i]!=32749; for(i=MAX_FRAME_SAMP*2;i<(MAX_FRAME_SAMP+8)*2;i++)err|=outbuf[i]!=32749; if(err)test_failed(); free(outbuf_int); free(packet); return 0; } { /*We only test a subset of the modes here simply because the longer durations end up taking a long time.*/ static const int cmodes[4]={16,20,24,28}; static const opus_uint32 cres[4]={116290185,2172123586u,2172123586u,2172123586u}; static const opus_uint32 lres[3]={3285687739u,1481572662,694350475}; static const int lmodes[3]={0,4,8}; int mode=fast_rand()%4; packet[0]=cmodes[mode]<<3; dec_final_acc=0; t=fast_rand()%10; for(i=0;i<65536;i++) { int factor=48000/fsv[t>>1]; packet[1]=i>>8; packet[2]=i&255; packet[3]=255; out_samples = opus_decode(dec[t], packet, 4, outbuf, MAX_FRAME_SAMP, 0); if(out_samples!=120/factor)test_failed(); opus_decoder_ctl(dec[t], OPUS_GET_FINAL_RANGE(&dec_final_range1)); dec_final_acc+=dec_final_range1; } if(dec_final_acc!=cres[mode])test_failed(); fprintf(stdout," dec[%3d] all 3-byte prefix for length 4, mode %2d OK.\n",t,cmodes[mode]); mode=fast_rand()%3; packet[0]=lmodes[mode]<<3; dec_final_acc=0; t=fast_rand()%10; for(i=0;i<65536;i++) { int factor=48000/fsv[t>>1]; packet[1]=i>>8; packet[2]=i&255; packet[3]=255; out_samples = opus_decode(dec[t], packet, 4, outbuf, MAX_FRAME_SAMP, 0); if(out_samples!=480/factor)test_failed(); opus_decoder_ctl(dec[t], OPUS_GET_FINAL_RANGE(&dec_final_range1)); dec_final_acc+=dec_final_range1; } if(dec_final_acc!=lres[mode])test_failed(); fprintf(stdout," dec[%3d] all 3-byte prefix for length 4, mode %2d OK.\n",t,lmodes[mode]); } skip=fast_rand()%7; for(i=0;i<64;i++) { int j,expected[5*2]; packet[0]=i<<2; for(t=0;t<5*2;t++)expected[t]=opus_decoder_get_nb_samples(dec[t],packet,1); for(j=2+skip;j<1275;j+=4) { int jj; for(jj=0;jj1.f)test_failed(); if(x[i]<-1.f)test_failed(); } } for(i=1;i<9;i++) { for (j=0;j<1024;j++) { x[j]=(i&255)*(1/32.f)-4.f; } opus_pcm_soft_clip(x,1024/i,i,s); for (j=0;j<(1024/i)*i;j++) { if(x[i]>1.f)test_failed(); if(x[i]<-1.f)test_failed(); } } opus_pcm_soft_clip(x,0,1,s); opus_pcm_soft_clip(x,1,0,s); opus_pcm_soft_clip(x,1,1,0); opus_pcm_soft_clip(x,1,-1,s); opus_pcm_soft_clip(x,-1,1,s); opus_pcm_soft_clip(0,1,1,s); printf("OK.\n"); } #endif int main(int _argc, char **_argv) { const char * oversion; const char * env_seed; int env_used; if(_argc>2) { fprintf(stderr,"Usage: %s []\n",_argv[0]); return 1; } env_used=0; env_seed=getenv("SEED"); if(_argc>1)iseed=atoi(_argv[1]); else if(env_seed) { iseed=atoi(env_seed); env_used=1; } else iseed=(opus_uint32)time(NULL)^((getpid()&65535)<<16); Rw=Rz=iseed; oversion=opus_get_version_string(); if(!oversion)test_failed(); fprintf(stderr,"Testing %s decoder. Random seed: %u (%.4X)\n", oversion, iseed, fast_rand() % 65535); if(env_used)fprintf(stderr," Random seed set from the environment (SEED=%s).\n", env_seed); /*Setting TEST_OPUS_NOFUZZ tells the tool not to send garbage data into the decoders. This is helpful because garbage data may cause the decoders to clip, which angers CLANG IOC.*/ test_decoder_code0(getenv("TEST_OPUS_NOFUZZ")!=NULL); #ifndef DISABLE_FLOAT_API test_soft_clip(); #endif return 0; } ================================================ FILE: deps/pjsip/third_party/opus/tests/test_opus_encode.c ================================================ /* Copyright (c) 2011-2013 Xiph.Org Foundation Written by Gregory Maxwell */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #if (!defined WIN32 && !defined _WIN32) || defined(__MINGW32__) #include #else #include #define getpid _getpid #endif #include "opus_multistream.h" #include "opus.h" #include "../src/opus_private.h" #include "test_opus_common.h" #define MAX_PACKET (1500) #define SAMPLES (48000*30) #define SSAMPLES (SAMPLES/3) #define MAX_FRAME_SAMP (5760) #define PI (3.141592653589793238462643f) void generate_music(short *buf, opus_int32 len) { opus_int32 a1,b1,a2,b2; opus_int32 c1,c2,d1,d2; opus_int32 i,j; a1=b1=a2=b2=0; c1=c2=d1=d2=0; j=0; /*60ms silence*/ for(i=0;i<2880;i++)buf[i*2]=buf[i*2+1]=0; for(i=2880;i>12)^((j>>10|j>>12)&26&j>>7)))&128)+128)<<15; r=fast_rand();v1+=r&65535;v1-=r>>16; r=fast_rand();v2+=r&65535;v2-=r>>16; b1=v1-a1+((b1*61+32)>>6);a1=v1; b2=v2-a2+((b2*61+32)>>6);a2=v2; c1=(30*(c1+b1+d1)+32)>>6;d1=b1; c2=(30*(c2+b2+d2)+32)>>6;d2=b2; v1=(c1+128)>>8; v2=(c2+128)>>8; buf[i*2]=v1>32767?32767:(v1<-32768?-32768:v1); buf[i*2+1]=v2>32767?32767:(v2<-32768?-32768:v2); if(i%6==0)j++; } } #if 0 static int save_ctr = 0; static void int_to_char(opus_uint32 i, unsigned char ch[4]) { ch[0] = i>>24; ch[1] = (i>>16)&0xFF; ch[2] = (i>>8)&0xFF; ch[3] = i&0xFF; } static OPUS_INLINE void save_packet(unsigned char* p, int len, opus_uint32 rng) { FILE *fout; unsigned char int_field[4]; char name[256]; snprintf(name,255,"test_opus_encode.%llu.%d.bit",(unsigned long long)iseed,save_ctr); fprintf(stdout,"writing %d byte packet to %s\n",len,name); fout=fopen(name, "wb+"); if(fout==NULL)test_failed(); int_to_char(len, int_field); fwrite(int_field, 1, 4, fout); int_to_char(rng, int_field); fwrite(int_field, 1, 4, fout); fwrite(p, 1, len, fout); fclose(fout); save_ctr++; } #endif int run_test1(int no_fuzz) { static const int fsizes[6]={960*3,960*2,120,240,480,960}; static const char *mstrings[3] = {" LP","Hybrid"," MDCT"}; unsigned char mapping[256] = {0,1,255}; unsigned char db62[36]; opus_int32 i; int rc,j,err; OpusEncoder *enc; OpusMSEncoder *MSenc; OpusDecoder *dec; OpusMSDecoder *MSdec; OpusMSDecoder *MSdec_err; OpusDecoder *dec_err[10]; short *inbuf; short *outbuf; short *out2buf; opus_int32 bitrate_bps; unsigned char packet[MAX_PACKET+257]; opus_uint32 enc_final_range; opus_uint32 dec_final_range; int fswitch; int fsize; int count; /*FIXME: encoder api tests, fs!=48k, mono, VBR*/ fprintf(stdout," Encode+Decode tests.\n"); enc = opus_encoder_create(48000, 2, OPUS_APPLICATION_VOIP, &err); if(err != OPUS_OK || enc==NULL)test_failed(); for(i=0;i<2;i++) { int *ret_err; ret_err = i?0:&err; MSenc = opus_multistream_encoder_create(8000, 2, 2, 0, mapping, OPUS_UNIMPLEMENTED, ret_err); if((ret_err && *ret_err != OPUS_BAD_ARG) || MSenc!=NULL)test_failed(); MSenc = opus_multistream_encoder_create(8000, 0, 1, 0, mapping, OPUS_APPLICATION_VOIP, ret_err); if((ret_err && *ret_err != OPUS_BAD_ARG) || MSenc!=NULL)test_failed(); MSenc = opus_multistream_encoder_create(44100, 2, 2, 0, mapping, OPUS_APPLICATION_VOIP, ret_err); if((ret_err && *ret_err != OPUS_BAD_ARG) || MSenc!=NULL)test_failed(); MSenc = opus_multistream_encoder_create(8000, 2, 2, 3, mapping, OPUS_APPLICATION_VOIP, ret_err); if((ret_err && *ret_err != OPUS_BAD_ARG) || MSenc!=NULL)test_failed(); MSenc = opus_multistream_encoder_create(8000, 2, -1, 0, mapping, OPUS_APPLICATION_VOIP, ret_err); if((ret_err && *ret_err != OPUS_BAD_ARG) || MSenc!=NULL)test_failed(); MSenc = opus_multistream_encoder_create(8000, 256, 2, 0, mapping, OPUS_APPLICATION_VOIP, ret_err); if((ret_err && *ret_err != OPUS_BAD_ARG) || MSenc!=NULL)test_failed(); } MSenc = opus_multistream_encoder_create(8000, 2, 2, 0, mapping, OPUS_APPLICATION_AUDIO, &err); if(err != OPUS_OK || MSenc==NULL)test_failed(); /*Some multistream encoder API tests*/ if(opus_multistream_encoder_ctl(MSenc, OPUS_GET_BITRATE(&i))!=OPUS_OK)test_failed(); if(opus_multistream_encoder_ctl(MSenc, OPUS_GET_LSB_DEPTH(&i))!=OPUS_OK)test_failed(); if(i<16)test_failed(); { OpusEncoder *tmp_enc; if(opus_multistream_encoder_ctl(MSenc, OPUS_MULTISTREAM_GET_ENCODER_STATE(1,&tmp_enc))!=OPUS_OK)test_failed(); if(opus_encoder_ctl(tmp_enc, OPUS_GET_LSB_DEPTH(&j))!=OPUS_OK)test_failed(); if(i!=j)test_failed(); if(opus_multistream_encoder_ctl(MSenc, OPUS_MULTISTREAM_GET_ENCODER_STATE(2,&tmp_enc))!=OPUS_BAD_ARG)test_failed(); } dec = opus_decoder_create(48000, 2, &err); if(err != OPUS_OK || dec==NULL)test_failed(); MSdec = opus_multistream_decoder_create(48000, 2, 2, 0, mapping, &err); if(err != OPUS_OK || MSdec==NULL)test_failed(); MSdec_err = opus_multistream_decoder_create(48000, 3, 2, 0, mapping, &err); if(err != OPUS_OK || MSdec_err==NULL)test_failed(); dec_err[0]=(OpusDecoder *)malloc(opus_decoder_get_size(2)); memcpy(dec_err[0],dec,opus_decoder_get_size(2)); dec_err[1] = opus_decoder_create(48000, 1, &err); dec_err[2] = opus_decoder_create(24000, 2, &err); dec_err[3] = opus_decoder_create(24000, 1, &err); dec_err[4] = opus_decoder_create(16000, 2, &err); dec_err[5] = opus_decoder_create(16000, 1, &err); dec_err[6] = opus_decoder_create(12000, 2, &err); dec_err[7] = opus_decoder_create(12000, 1, &err); dec_err[8] = opus_decoder_create(8000, 2, &err); dec_err[9] = opus_decoder_create(8000, 1, &err); for(i=0;i<10;i++)if(dec_err[i]==NULL)test_failed(); { OpusEncoder *enccpy; /*The opus state structures contain no pointers and can be freely copied*/ enccpy=(OpusEncoder *)malloc(opus_encoder_get_size(2)); memcpy(enccpy,enc,opus_encoder_get_size(2)); memset(enc,255,opus_encoder_get_size(2)); opus_encoder_destroy(enc); enc=enccpy; } inbuf=(short *)malloc(sizeof(short)*SAMPLES*2); outbuf=(short *)malloc(sizeof(short)*SAMPLES*2); out2buf=(short *)malloc(sizeof(short)*MAX_FRAME_SAMP*3); if(inbuf==NULL || outbuf==NULL || out2buf==NULL)test_failed(); generate_music(inbuf,SAMPLES); /* FILE *foo; foo = fopen("foo.sw", "wb+"); fwrite(inbuf, 1, SAMPLES*2*2, foo); fclose(foo);*/ if(opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_AUTO))!=OPUS_OK)test_failed(); if(opus_encoder_ctl(enc, OPUS_SET_FORCE_MODE(-2))!=OPUS_BAD_ARG)test_failed(); for(rc=0;rc<3;rc++) { if(opus_encoder_ctl(enc, OPUS_SET_VBR(rc<2))!=OPUS_OK)test_failed(); if(opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(rc==1))!=OPUS_OK)test_failed(); if(opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(rc==1))!=OPUS_OK)test_failed(); if(opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(rc==0))!=OPUS_OK)test_failed(); for(j=0;j<13;j++) { int rate; int modes[13]={0,0,0,1,1,1,1,2,2,2,2,2,2}; int rates[13]={6000,12000,48000,16000,32000,48000,64000,512000,13000,24000,48000,64000,96000}; int frame[13]={960*2,960,480,960,960,960,480,960*3,960*3,960,480,240,120}; rate=rates[j]+fast_rand()%rates[j]; count=i=0; do { int bw,len,out_samples,frame_size; frame_size=frame[j]; if((fast_rand()&255)==0) { if(opus_encoder_ctl(enc, OPUS_RESET_STATE)!=OPUS_OK)test_failed(); if(opus_decoder_ctl(dec, OPUS_RESET_STATE)!=OPUS_OK)test_failed(); if((fast_rand()&1)!=0) { if(opus_decoder_ctl(dec_err[fast_rand()&1], OPUS_RESET_STATE)!=OPUS_OK)test_failed(); } } if((fast_rand()&127)==0) { if(opus_decoder_ctl(dec_err[fast_rand()&1], OPUS_RESET_STATE)!=OPUS_OK)test_failed(); } if(fast_rand()%10==0){ int complex=fast_rand()%11; if(opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complex))!=OPUS_OK)test_failed(); } if(fast_rand()%50==0)opus_decoder_ctl(dec, OPUS_RESET_STATE); if(opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(rc==0))!=OPUS_OK)test_failed(); if(opus_encoder_ctl(enc, OPUS_SET_FORCE_MODE(MODE_SILK_ONLY+modes[j]))!=OPUS_OK)test_failed(); if(opus_encoder_ctl(enc, OPUS_SET_DTX(fast_rand()&1))!=OPUS_OK)test_failed(); if(opus_encoder_ctl(enc, OPUS_SET_BITRATE(rate))!=OPUS_OK)test_failed(); if(opus_encoder_ctl(enc, OPUS_SET_FORCE_CHANNELS((rates[j]>=64000?2:1)))!=OPUS_OK)test_failed(); if(opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY((count>>2)%11))!=OPUS_OK)test_failed(); if(opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC((fast_rand()&15)&(fast_rand()%15)))!=OPUS_OK)test_failed(); bw=modes[j]==0?OPUS_BANDWIDTH_NARROWBAND+(fast_rand()%3): modes[j]==1?OPUS_BANDWIDTH_SUPERWIDEBAND+(fast_rand()&1): OPUS_BANDWIDTH_NARROWBAND+(fast_rand()%5); if(modes[j]==2&&bw==OPUS_BANDWIDTH_MEDIUMBAND)bw+=3; if(opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(bw))!=OPUS_OK)test_failed(); len = opus_encode(enc, &inbuf[i<<1], frame_size, packet, MAX_PACKET); if(len<0 || len>MAX_PACKET)test_failed(); if(opus_encoder_ctl(enc, OPUS_GET_FINAL_RANGE(&enc_final_range))!=OPUS_OK)test_failed(); if((fast_rand()&3)==0) { if(opus_packet_pad(packet,len,len+1)!=OPUS_OK)test_failed(); len++; } if((fast_rand()&7)==0) { if(opus_packet_pad(packet,len,len+256)!=OPUS_OK)test_failed(); len+=256; } if((fast_rand()&3)==0) { len=opus_packet_unpad(packet,len); if(len<1)test_failed(); } out_samples = opus_decode(dec, packet, len, &outbuf[i<<1], MAX_FRAME_SAMP, 0); if(out_samples!=frame_size)test_failed(); if(opus_decoder_ctl(dec, OPUS_GET_FINAL_RANGE(&dec_final_range))!=OPUS_OK)test_failed(); if(enc_final_range!=dec_final_range)test_failed(); /*LBRR decode*/ out_samples = opus_decode(dec_err[0], packet, len, out2buf, frame_size, (fast_rand()&3)!=0); if(out_samples!=frame_size)test_failed(); out_samples = opus_decode(dec_err[1], packet, (fast_rand()&3)==0?0:len, out2buf, MAX_FRAME_SAMP, (fast_rand()&7)!=0); if(out_samples<120)test_failed(); i+=frame_size; count++; }while(i<(SSAMPLES-MAX_FRAME_SAMP)); fprintf(stdout," Mode %s FB encode %s, %6d bps OK.\n",mstrings[modes[j]],rc==0?" VBR":rc==1?"CVBR":" CBR",rate); } } if(opus_encoder_ctl(enc, OPUS_SET_FORCE_MODE(OPUS_AUTO))!=OPUS_OK)test_failed(); if(opus_encoder_ctl(enc, OPUS_SET_FORCE_CHANNELS(OPUS_AUTO))!=OPUS_OK)test_failed(); if(opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(0))!=OPUS_OK)test_failed(); if(opus_encoder_ctl(enc, OPUS_SET_DTX(0))!=OPUS_OK)test_failed(); for(rc=0;rc<3;rc++) { if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_VBR(rc<2))!=OPUS_OK)test_failed(); if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_VBR_CONSTRAINT(rc==1))!=OPUS_OK)test_failed(); if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_VBR_CONSTRAINT(rc==1))!=OPUS_OK)test_failed(); if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_INBAND_FEC(rc==0))!=OPUS_OK)test_failed(); for(j=0;j<16;j++) { int rate; int modes[16]={0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2}; int rates[16]={4000,12000,32000,8000,16000,32000,48000,88000,4000,12000,32000,8000,16000,32000,48000,88000}; int frame[16]={160*1,160,80,160,160,80,40,20,160*1,160,80,160,160,80,40,20}; if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_INBAND_FEC(rc==0&&j==1))!=OPUS_OK)test_failed(); if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_FORCE_MODE(MODE_SILK_ONLY+modes[j]))!=OPUS_OK)test_failed(); rate=rates[j]+fast_rand()%rates[j]; if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_DTX(fast_rand()&1))!=OPUS_OK)test_failed(); if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_BITRATE(rate))!=OPUS_OK)test_failed(); count=i=0; do { int pred,len,out_samples,frame_size,loss; if(opus_multistream_encoder_ctl(MSenc, OPUS_GET_PREDICTION_DISABLED(&pred))!=OPUS_OK)test_failed(); if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_PREDICTION_DISABLED((int)(fast_rand()&15)<(pred?11:4)))!=OPUS_OK)test_failed(); frame_size=frame[j]; if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_COMPLEXITY((count>>2)%11))!=OPUS_OK)test_failed(); if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_PACKET_LOSS_PERC((fast_rand()&15)&(fast_rand()%15)))!=OPUS_OK)test_failed(); if((fast_rand()&255)==0) { if(opus_multistream_encoder_ctl(MSenc, OPUS_RESET_STATE)!=OPUS_OK)test_failed(); if(opus_multistream_decoder_ctl(MSdec, OPUS_RESET_STATE)!=OPUS_OK)test_failed(); if((fast_rand()&3)!=0) { if(opus_multistream_decoder_ctl(MSdec_err, OPUS_RESET_STATE)!=OPUS_OK)test_failed(); } } if((fast_rand()&255)==0) { if(opus_multistream_decoder_ctl(MSdec_err, OPUS_RESET_STATE)!=OPUS_OK)test_failed(); } len = opus_multistream_encode(MSenc, &inbuf[i<<1], frame_size, packet, MAX_PACKET); if(len<0 || len>MAX_PACKET)test_failed(); if(opus_multistream_encoder_ctl(MSenc, OPUS_GET_FINAL_RANGE(&enc_final_range))!=OPUS_OK)test_failed(); if((fast_rand()&3)==0) { if(opus_multistream_packet_pad(packet,len,len+1,2)!=OPUS_OK)test_failed(); len++; } if((fast_rand()&7)==0) { if(opus_multistream_packet_pad(packet,len,len+256,2)!=OPUS_OK)test_failed(); len+=256; } if((fast_rand()&3)==0) { len=opus_multistream_packet_unpad(packet,len,2); if(len<1)test_failed(); } out_samples = opus_multistream_decode(MSdec, packet, len, out2buf, MAX_FRAME_SAMP, 0); if(out_samples!=frame_size*6)test_failed(); if(opus_multistream_decoder_ctl(MSdec, OPUS_GET_FINAL_RANGE(&dec_final_range))!=OPUS_OK)test_failed(); if(enc_final_range!=dec_final_range)test_failed(); /*LBRR decode*/ loss=(fast_rand()&63)==0; out_samples = opus_multistream_decode(MSdec_err, packet, loss?0:len, out2buf, frame_size*6, (fast_rand()&3)!=0); if(out_samples!=(frame_size*6))test_failed(); i+=frame_size; count++; }while(i<(SSAMPLES/12-MAX_FRAME_SAMP)); fprintf(stdout," Mode %s NB dual-mono MS encode %s, %6d bps OK.\n",mstrings[modes[j]],rc==0?" VBR":rc==1?"CVBR":" CBR",rate); } } bitrate_bps=512000; fsize=fast_rand()%31; fswitch=100; debruijn2(6,db62); count=i=0; do { unsigned char toc; const unsigned char *frames[48]; short size[48]; int payload_offset; opus_uint32 dec_final_range2; int jj,dec2; int len,out_samples; int frame_size=fsizes[db62[fsize]]; opus_int32 offset=i%(SAMPLES-MAX_FRAME_SAMP); opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate_bps)); len = opus_encode(enc, &inbuf[offset<<1], frame_size, packet, MAX_PACKET); if(len<0 || len>MAX_PACKET)test_failed(); count++; opus_encoder_ctl(enc, OPUS_GET_FINAL_RANGE(&enc_final_range)); out_samples = opus_decode(dec, packet, len, &outbuf[offset<<1], MAX_FRAME_SAMP, 0); if(out_samples!=frame_size)test_failed(); opus_decoder_ctl(dec, OPUS_GET_FINAL_RANGE(&dec_final_range)); /* compare final range encoder rng values of encoder and decoder */ if(dec_final_range!=enc_final_range)test_failed(); /* We fuzz the packet, but take care not to only corrupt the payload Corrupted headers are tested elsewhere and we need to actually run the decoders in order to compare them. */ if(opus_packet_parse(packet,len,&toc,frames,size,&payload_offset)<=0)test_failed(); if((fast_rand()&1023)==0)len=0; for(j=(frames[0]-packet);j0?packet:NULL, len, out2buf, MAX_FRAME_SAMP, 0); if(out_samples<0||out_samples>MAX_FRAME_SAMP)test_failed(); if((len>0&&out_samples!=frame_size))test_failed(); /*FIXME use lastframe*/ opus_decoder_ctl(dec_err[0], OPUS_GET_FINAL_RANGE(&dec_final_range)); /*randomly select one of the decoders to compare with*/ dec2=fast_rand()%9+1; out_samples = opus_decode(dec_err[dec2], len>0?packet:NULL, len, out2buf, MAX_FRAME_SAMP, 0); if(out_samples<0||out_samples>MAX_FRAME_SAMP)test_failed(); /*FIXME, use factor, lastframe for loss*/ opus_decoder_ctl(dec_err[dec2], OPUS_GET_FINAL_RANGE(&dec_final_range2)); if(len>0&&dec_final_range!=dec_final_range2)test_failed(); fswitch--; if(fswitch<1) { int new_size; fsize=(fsize+1)%36; new_size=fsizes[db62[fsize]]; if(new_size==960||new_size==480)fswitch=2880/new_size*(fast_rand()%19+1); else fswitch=(fast_rand()%(2880/new_size))+1; } bitrate_bps=((fast_rand()%508000+4000)+bitrate_bps)>>1; i+=frame_size; }while(i2) { fprintf(stderr,"Usage: %s []\n",_argv[0]); return 1; } env_used=0; env_seed=getenv("SEED"); if(_argc>1)iseed=atoi(_argv[1]); else if(env_seed) { iseed=atoi(env_seed); env_used=1; } else iseed=(opus_uint32)time(NULL)^((getpid()&65535)<<16); Rw=Rz=iseed; oversion=opus_get_version_string(); if(!oversion)test_failed(); fprintf(stderr,"Testing %s encoder. Random seed: %u (%.4X)\n", oversion, iseed, fast_rand() % 65535); if(env_used)fprintf(stderr," Random seed set from the environment (SEED=%s).\n", env_seed); /*Setting TEST_OPUS_NOFUZZ tells the tool not to send garbage data into the decoders. This is helpful because garbage data may cause the decoders to clip, which angers CLANG IOC.*/ run_test1(getenv("TEST_OPUS_NOFUZZ")!=NULL); fprintf(stderr,"Tests completed successfully.\n"); return 0; } ================================================ FILE: deps/pjsip/third_party/opus/tests/test_opus_padding.c ================================================ /* Copyright (c) 2012 Xiph.Org Foundation Written by Jüri Aedla and Ralph Giles */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Check for overflow in reading the padding length. * http://lists.xiph.org/pipermail/opus/2012-November/001834.html */ #include #include #include #include "opus.h" #include "test_opus_common.h" #define PACKETSIZE 16909318 #define CHANNELS 2 #define FRAMESIZE 5760 int test_overflow(void) { OpusDecoder *decoder; int result; int error; unsigned char *in = malloc(PACKETSIZE); opus_int16 *out = malloc(FRAMESIZE*CHANNELS*sizeof(*out)); fprintf(stderr, " Checking for padding overflow... "); if (!in || !out) { fprintf(stderr, "FAIL (out of memory)\n"); return -1; } in[0] = 0xff; in[1] = 0x41; memset(in + 2, 0xff, PACKETSIZE - 3); in[PACKETSIZE-1] = 0x0b; decoder = opus_decoder_create(48000, CHANNELS, &error); result = opus_decode(decoder, in, PACKETSIZE, out, FRAMESIZE, 0); opus_decoder_destroy(decoder); free(in); free(out); if (result != OPUS_INVALID_PACKET) { fprintf(stderr, "FAIL!\n"); test_failed(); } fprintf(stderr, "OK.\n"); return 1; } int main(void) { const char *oversion; int tests = 0;; iseed = 0; oversion = opus_get_version_string(); if (!oversion) test_failed(); fprintf(stderr, "Testing %s padding.\n", oversion); tests += test_overflow(); fprintf(stderr, "All padding tests passed.\n"); return 0; } ================================================ FILE: deps/pjsip/third_party/opus/version.mk ================================================ # static version string; update manually every release. PACKAGE_VERSION = "1.1.2" ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/celt.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 {245603E3-F580-41A5-9632-B25FE3372CBF} Win32Proj celt StaticLibrary true Unicode StaticLibrary true Unicode StaticLibrary false true Unicode StaticLibrary false true Unicode $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ Level3 Disabled HAVE_CONFIG_H;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) ..\;..\..\include;..\..\celt;..\..\silk;..\..\silk\float;..\..\silk\fixed;%(AdditionalIncludeDirectories) MultiThreadedDebug Windows true "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h Level3 Disabled HAVE_CONFIG_H;WIN32;WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions) ..\;..\..\include;..\..\celt;..\..\silk;..\..\silk\float;..\..\silk\fixed;%(AdditionalIncludeDirectories) MultiThreadedDebug Windows true "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h Level3 true true HAVE_CONFIG_H;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) ..\;..\..\include;..\..\celt;..\..\silk;..\..\silk\float;..\..\silk\fixed;%(AdditionalIncludeDirectories) MultiThreaded Windows true true true "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h Level3 MaxSpeed true true HAVE_CONFIG_H;WIN32;WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) ..\;..\..\include;..\..\celt;..\..\silk;..\..\silk\float;..\..\silk\fixed;%(AdditionalIncludeDirectories) MultiThreaded Windows true true true "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/celt.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/opus.sln ================================================  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opus", "opus.vcxproj", "{219EC965-228A-1824-174D-96449D05F88A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "celt", "celt.vcxproj", "{245603E3-F580-41A5-9632-B25FE3372CBF}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opus_demo", "opus_demo.vcxproj", "{016C739D-6389-43BF-8D88-24B2BF6F620F}" ProjectSection(ProjectDependencies) = postProject {219EC965-228A-1824-174D-96449D05F88A} = {219EC965-228A-1824-174D-96449D05F88A} {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782} = {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782} {245603E3-F580-41A5-9632-B25FE3372CBF} = {245603E3-F580-41A5-9632-B25FE3372CBF} {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16} = {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_opus_api", "test_opus_api.vcxproj", "{1D257A17-D254-42E5-82D6-1C87A6EC775A}" ProjectSection(ProjectDependencies) = postProject {219EC965-228A-1824-174D-96449D05F88A} = {219EC965-228A-1824-174D-96449D05F88A} {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782} = {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782} {245603E3-F580-41A5-9632-B25FE3372CBF} = {245603E3-F580-41A5-9632-B25FE3372CBF} {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16} = {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_opus_decode", "test_opus_decode.vcxproj", "{8578322A-1883-486B-B6FA-E0094B65C9F2}" ProjectSection(ProjectDependencies) = postProject {219EC965-228A-1824-174D-96449D05F88A} = {219EC965-228A-1824-174D-96449D05F88A} {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782} = {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782} {245603E3-F580-41A5-9632-B25FE3372CBF} = {245603E3-F580-41A5-9632-B25FE3372CBF} {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16} = {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_opus_encode", "test_opus_encode.vcxproj", "{84DAA768-1A38-4312-BB61-4C78BB59E5B8}" ProjectSection(ProjectDependencies) = postProject {219EC965-228A-1824-174D-96449D05F88A} = {219EC965-228A-1824-174D-96449D05F88A} {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782} = {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782} {245603E3-F580-41A5-9632-B25FE3372CBF} = {245603E3-F580-41A5-9632-B25FE3372CBF} {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16} = {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "silk_float", "silk_float.vcxproj", "{9C4961D2-5DDB-40C7-9BE8-CA918DC4E782}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "silk_common", "silk_common.vcxproj", "{C303D2FC-FF97-49B8-9DDD-467B4C9A0B16}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "silk_fixed", "silk_fixed.vcxproj", "{8484C90D-1561-402F-A91D-2DB10F8C5171}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {219EC965-228A-1824-174D-96449D05F88A}.Debug|Win32.ActiveCfg = Debug|Win32 {219EC965-228A-1824-174D-96449D05F88A}.Debug|Win32.Build.0 = Debug|Win32 {219EC965-228A-1824-174D-96449D05F88A}.Debug|x64.ActiveCfg = Debug|x64 {219EC965-228A-1824-174D-96449D05F88A}.Debug|x64.Build.0 = Debug|x64 {219EC965-228A-1824-174D-96449D05F88A}.Release|Win32.ActiveCfg = Release|Win32 {219EC965-228A-1824-174D-96449D05F88A}.Release|Win32.Build.0 = Release|Win32 {219EC965-228A-1824-174D-96449D05F88A}.Release|x64.ActiveCfg = Release|x64 {219EC965-228A-1824-174D-96449D05F88A}.Release|x64.Build.0 = Release|x64 {245603E3-F580-41A5-9632-B25FE3372CBF}.Debug|Win32.ActiveCfg = Debug|Win32 {245603E3-F580-41A5-9632-B25FE3372CBF}.Debug|Win32.Build.0 = Debug|Win32 {245603E3-F580-41A5-9632-B25FE3372CBF}.Debug|x64.ActiveCfg = Debug|x64 {245603E3-F580-41A5-9632-B25FE3372CBF}.Debug|x64.Build.0 = Debug|x64 {245603E3-F580-41A5-9632-B25FE3372CBF}.Release|Win32.ActiveCfg = Release|Win32 {245603E3-F580-41A5-9632-B25FE3372CBF}.Release|Win32.Build.0 = Release|Win32 {245603E3-F580-41A5-9632-B25FE3372CBF}.Release|x64.ActiveCfg = Release|x64 {245603E3-F580-41A5-9632-B25FE3372CBF}.Release|x64.Build.0 = Release|x64 {016C739D-6389-43BF-8D88-24B2BF6F620F}.Debug|Win32.ActiveCfg = Debug|Win32 {016C739D-6389-43BF-8D88-24B2BF6F620F}.Debug|Win32.Build.0 = Debug|Win32 {016C739D-6389-43BF-8D88-24B2BF6F620F}.Debug|x64.ActiveCfg = Debug|x64 {016C739D-6389-43BF-8D88-24B2BF6F620F}.Debug|x64.Build.0 = Debug|x64 {016C739D-6389-43BF-8D88-24B2BF6F620F}.Release|Win32.ActiveCfg = Release|Win32 {016C739D-6389-43BF-8D88-24B2BF6F620F}.Release|Win32.Build.0 = Release|Win32 {016C739D-6389-43BF-8D88-24B2BF6F620F}.Release|x64.ActiveCfg = Release|x64 {016C739D-6389-43BF-8D88-24B2BF6F620F}.Release|x64.Build.0 = Release|x64 {1D257A17-D254-42E5-82D6-1C87A6EC775A}.Debug|Win32.ActiveCfg = Debug|Win32 {1D257A17-D254-42E5-82D6-1C87A6EC775A}.Debug|Win32.Build.0 = Debug|Win32 {1D257A17-D254-42E5-82D6-1C87A6EC775A}.Debug|x64.ActiveCfg = Debug|x64 {1D257A17-D254-42E5-82D6-1C87A6EC775A}.Debug|x64.Build.0 = Debug|x64 {1D257A17-D254-42E5-82D6-1C87A6EC775A}.Release|Win32.ActiveCfg = Release|Win32 {1D257A17-D254-42E5-82D6-1C87A6EC775A}.Release|Win32.Build.0 = Release|Win32 {1D257A17-D254-42E5-82D6-1C87A6EC775A}.Release|x64.ActiveCfg = Release|x64 {1D257A17-D254-42E5-82D6-1C87A6EC775A}.Release|x64.Build.0 = Release|x64 {8578322A-1883-486B-B6FA-E0094B65C9F2}.Debug|Win32.ActiveCfg = Debug|Win32 {8578322A-1883-486B-B6FA-E0094B65C9F2}.Debug|Win32.Build.0 = Debug|Win32 {8578322A-1883-486B-B6FA-E0094B65C9F2}.Debug|x64.ActiveCfg = Debug|x64 {8578322A-1883-486B-B6FA-E0094B65C9F2}.Debug|x64.Build.0 = Debug|x64 {8578322A-1883-486B-B6FA-E0094B65C9F2}.Release|Win32.ActiveCfg = Release|Win32 {8578322A-1883-486B-B6FA-E0094B65C9F2}.Release|Win32.Build.0 = Release|Win32 {8578322A-1883-486B-B6FA-E0094B65C9F2}.Release|x64.ActiveCfg = Release|x64 {8578322A-1883-486B-B6FA-E0094B65C9F2}.Release|x64.Build.0 = Release|x64 {84DAA768-1A38-4312-BB61-4C78BB59E5B8}.Debug|Win32.ActiveCfg = Debug|Win32 {84DAA768-1A38-4312-BB61-4C78BB59E5B8}.Debug|Win32.Build.0 = Debug|Win32 {84DAA768-1A38-4312-BB61-4C78BB59E5B8}.Debug|x64.ActiveCfg = Debug|x64 {84DAA768-1A38-4312-BB61-4C78BB59E5B8}.Debug|x64.Build.0 = Debug|x64 {84DAA768-1A38-4312-BB61-4C78BB59E5B8}.Release|Win32.ActiveCfg = Release|Win32 {84DAA768-1A38-4312-BB61-4C78BB59E5B8}.Release|Win32.Build.0 = Release|Win32 {84DAA768-1A38-4312-BB61-4C78BB59E5B8}.Release|x64.ActiveCfg = Release|x64 {84DAA768-1A38-4312-BB61-4C78BB59E5B8}.Release|x64.Build.0 = Release|x64 {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782}.Debug|Win32.ActiveCfg = Debug|Win32 {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782}.Debug|Win32.Build.0 = Debug|Win32 {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782}.Debug|x64.ActiveCfg = Debug|x64 {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782}.Debug|x64.Build.0 = Debug|x64 {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782}.Release|Win32.ActiveCfg = Release|Win32 {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782}.Release|Win32.Build.0 = Release|Win32 {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782}.Release|x64.ActiveCfg = Release|x64 {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782}.Release|x64.Build.0 = Release|x64 {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16}.Debug|Win32.ActiveCfg = Debug|Win32 {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16}.Debug|Win32.Build.0 = Debug|Win32 {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16}.Debug|x64.ActiveCfg = Debug|x64 {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16}.Debug|x64.Build.0 = Debug|x64 {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16}.Release|Win32.ActiveCfg = Release|Win32 {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16}.Release|Win32.Build.0 = Release|Win32 {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16}.Release|x64.ActiveCfg = Release|x64 {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16}.Release|x64.Build.0 = Release|x64 {8484C90D-1561-402F-A91D-2DB10F8C5171}.Debug|Win32.ActiveCfg = Debug|Win32 {8484C90D-1561-402F-A91D-2DB10F8C5171}.Debug|Win32.Build.0 = Debug|Win32 {8484C90D-1561-402F-A91D-2DB10F8C5171}.Debug|x64.ActiveCfg = Debug|x64 {8484C90D-1561-402F-A91D-2DB10F8C5171}.Debug|x64.Build.0 = Debug|x64 {8484C90D-1561-402F-A91D-2DB10F8C5171}.Release|Win32.ActiveCfg = Release|Win32 {8484C90D-1561-402F-A91D-2DB10F8C5171}.Release|Win32.Build.0 = Release|Win32 {8484C90D-1561-402F-A91D-2DB10F8C5171}.Release|x64.ActiveCfg = Release|x64 {8484C90D-1561-402F-A91D-2DB10F8C5171}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/opus.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 Win32Proj opus {219EC965-228A-1824-174D-96449D05F88A} StaticLibrary true StaticLibrary true false StaticLibrary true StaticLibrary false true $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ true $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ false $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ false $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ HAVE_CONFIG_H;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../silk;../../celt;../../win32;../../include;%(AdditionalIncludeDirectories) MultiThreadedDebug Level3 ProgramDatabase Disabled 4996 MachineX86 true Console $(SolutionDir)$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) celt.lib;silk_common.lib;silk_fixed.lib;silk_float.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h HAVE_CONFIG_H;WIN32;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../silk;../../celt;../../win32;../../include;%(AdditionalIncludeDirectories) MultiThreadedDebug Level3 ProgramDatabase Disabled 4996 true Console $(SolutionDir)$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) celt.lib;silk_common.lib;silk_fixed.lib;silk_float.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h HAVE_CONFIG_H;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../silk;../../celt;../../win32;../../include;%(AdditionalIncludeDirectories) MultiThreaded Level3 ProgramDatabase true 4996 MachineX86 false Console true true celt.lib;silk_common.lib;silk_fixed.lib;silk_float.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) $(SolutionDir)$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) false "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h HAVE_CONFIG_H;WIN32;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../silk;../../celt;../../win32;../../include;%(AdditionalIncludeDirectories) MultiThreaded Level3 ProgramDatabase 4996 false Console true true celt.lib;silk_common.lib;silk_fixed.lib;silk_float.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) $(SolutionDir)$(Configuration)$(PlatformArchitecture);%(AdditionalLibraryDirectories) false "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/opus.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/opus_demo.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 {245603e3-f580-41a5-9632-b25fe3372cbf} {219ec965-228a-1824-174d-96449d05f88a} {c303d2fc-ff97-49b8-9ddd-467b4c9a0b16} {9c4961d2-5ddb-40c7-9be8-ca918dc4e782} {016C739D-6389-43BF-8D88-24B2BF6F620F} Win32Proj opus_demo Application true Unicode Application true Unicode Application false true Unicode Application false true Unicode $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ Level3 Disabled HAVE_CONFIG_H;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\silk;..\..\celt;..\;..\..\include; MultiThreadedDebug Console true Level3 Disabled HAVE_CONFIG_H;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\silk;..\..\celt;..\;..\..\include; MultiThreadedDebug Console true Level3 MaxSpeed true true HAVE_CONFIG_H;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded ..\..\silk;..\..\celt;..\;..\..\include; true Console true true Level3 MaxSpeed true true HAVE_CONFIG_H;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded ..\..\silk;..\..\celt;..\;..\..\include; true Console true true ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/opus_demo.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/silk_common.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 {C303D2FC-FF97-49B8-9DDD-467B4C9A0B16} Win32Proj src_common silk_common StaticLibrary true Unicode StaticLibrary true Unicode StaticLibrary false true Unicode StaticLibrary false true Unicode $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ Level3 Disabled HAVE_CONFIG_H;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) ../..;../../silk/fixed;../../silk/float;../../silk;../../win32;../../celt;../../include MultiThreadedDebug Windows true $(OutDir)$(TargetName)$(TargetExt) "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h Level3 Disabled HAVE_CONFIG_H;WIN32;WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions) ../..;../../silk/fixed;../../silk/float;../../silk;../../win32;../../celt;../../include MultiThreadedDebug Windows true $(OutDir)$(TargetName)$(TargetExt) "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h Level3 true true HAVE_CONFIG_H;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) ../..;../../silk/fixed;../../silk/float;../../silk;../../win32;../../celt;../../include MultiThreaded Fast Windows true true true $(OutDir)$(TargetName)$(TargetExt) "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h Level3 MaxSpeed true true HAVE_CONFIG_H;WIN32;WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) ../..;../../silk/fixed;../../silk/float;../../silk;../../win32;../../celt;../../include MultiThreaded Fast Windows true true true $(OutDir)$(TargetName)$(TargetExt) "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/silk_common.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/silk_fixed.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 {8484C90D-1561-402F-A91D-2DB10F8C5171} Win32Proj src_FIX silk_fixed StaticLibrary true Unicode StaticLibrary true Unicode StaticLibrary false true Unicode StaticLibrary false true Unicode $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ Level3 Disabled HAVE_CONFIG_H;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) ../..;../../silk/fixed;../../silk;../../win32;../../celt;../../include;../win32 MultiThreadedDebug Windows true $(OutDir)$(TargetName)$(TargetExt) Level3 Disabled HAVE_CONFIG_H;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) ../..;../../silk/fixed;../../silk;../../win32;../../celt;../../include;../win32 MultiThreadedDebug Windows true $(OutDir)$(TargetName)$(TargetExt) Level3 true true HAVE_CONFIG_H;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) ../..;../../silk/fixed;../../silk;../../win32;../../celt;../../include;../win32 MultiThreaded Windows true true true $(OutDir)$(TargetName)$(TargetExt) Level3 MaxSpeed true true HAVE_CONFIG_H;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) ../..;../../silk/fixed;../../silk;../../win32;../../celt;../../include;../win32 MultiThreaded Windows true true true $(OutDir)$(TargetName)$(TargetExt) ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/silk_fixed.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/silk_float.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 {9C4961D2-5DDB-40C7-9BE8-CA918DC4E782} Win32Proj src_FLP silk_float StaticLibrary true Unicode StaticLibrary true Unicode StaticLibrary false true Unicode StaticLibrary false true Unicode $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ Compile $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ Level3 Disabled HAVE_CONFIG_H;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) ../../silk/float;../../silk;../../win32;../../celt;../../include;../win32 MultiThreadedDebug Windows true $(OutDir)$(TargetName)$(TargetExt) "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h Level3 Disabled HAVE_CONFIG_H;WIN32;WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions) ../../silk/float;../../silk;../../win32;../../celt;../../include;../win32 MultiThreadedDebug Windows true $(OutDir)$(TargetName)$(TargetExt) "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h Level3 true true HAVE_CONFIG_H;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) ../../silk/float;../../silk;../../win32;../../celt;../../include;../win32 MultiThreaded Windows true true true $(OutDir)$(TargetName)$(TargetExt) "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h Level3 MaxSpeed true true HAVE_CONFIG_H;WIN32;WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) ../../silk/float;../../silk;../../win32;../../celt;../../include;../win32 MultiThreaded Windows true true true $(OutDir)$(TargetName)$(TargetExt) "$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION Generating version.h ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/silk_float.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/test_opus_api.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 {245603e3-f580-41a5-9632-b25fe3372cbf} {219ec965-228a-1824-174d-96449d05f88a} {c303d2fc-ff97-49b8-9ddd-467b4c9a0b16} {9c4961d2-5ddb-40c7-9be8-ca918dc4e782} {1D257A17-D254-42E5-82D6-1C87A6EC775A} Win32Proj test_opus_api Application true Unicode Application true Unicode Application false true Unicode Application false true Unicode $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ Level3 Disabled HAVE_CONFIG_H;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\silk;..\..\celt;..\;..\..\include; MultiThreadedDebug Console true Level3 Disabled HAVE_CONFIG_H;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\silk;..\..\celt;..\;..\..\include; MultiThreadedDebug Console true Level3 MaxSpeed true true HAVE_CONFIG_H;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded ..\..\silk;..\..\celt;..\;..\..\include; Console true true Level3 MaxSpeed true true HAVE_CONFIG_H;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded ..\..\silk;..\..\celt;..\;..\..\include; Console true true ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/test_opus_api.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx Source Files ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/test_opus_decode.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 {245603e3-f580-41a5-9632-b25fe3372cbf} {219ec965-228a-1824-174d-96449d05f88a} {c303d2fc-ff97-49b8-9ddd-467b4c9a0b16} {9c4961d2-5ddb-40c7-9be8-ca918dc4e782} {8578322A-1883-486B-B6FA-E0094B65C9F2} Win32Proj test_opus_api Application true Unicode Application true Unicode Application false true Unicode Application false true Unicode $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ Level3 Disabled HAVE_CONFIG_H;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\silk;..\..\celt;..\;..\..\include; MultiThreadedDebug 4996 Console true Level3 Disabled HAVE_CONFIG_H;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\silk;..\..\celt;..\;..\..\include; MultiThreadedDebug 4996 Console true Level3 MaxSpeed true true HAVE_CONFIG_H;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded ..\..\silk;..\..\celt;..\;..\..\include; 4996 Console true true Level3 MaxSpeed true true HAVE_CONFIG_H;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded ..\..\silk;..\..\celt;..\;..\..\include; 4996 Console true true ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/test_opus_decode.vcxproj.filters ================================================  {4a0dd677-931f-4728-afe5-b761149fc7eb} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx Source Files ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/test_opus_encode.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 {245603e3-f580-41a5-9632-b25fe3372cbf} {219ec965-228a-1824-174d-96449d05f88a} {c303d2fc-ff97-49b8-9ddd-467b4c9a0b16} {9c4961d2-5ddb-40c7-9be8-ca918dc4e782} {84DAA768-1A38-4312-BB61-4C78BB59E5B8} Win32Proj test_opus_api Application true Unicode Application true Unicode Application false true Unicode Application false true Unicode $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ $(Platform)\$(Configuration)\ Level3 Disabled HAVE_CONFIG_H;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\silk;..\..\celt;..\;..\..\include; MultiThreadedDebug 4996 Console true Level3 Disabled HAVE_CONFIG_H;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\..\silk;..\..\celt;..\;..\..\include; MultiThreadedDebug 4996 Console true Level3 MaxSpeed true true HAVE_CONFIG_H;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded ..\..\silk;..\..\celt;..\;..\..\include; 4996 Console true true Level3 MaxSpeed true true HAVE_CONFIG_H;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded ..\..\silk;..\..\celt;..\;..\..\include; 4996 Console true true ================================================ FILE: deps/pjsip/third_party/opus/win32/VS2010/test_opus_encode.vcxproj.filters ================================================  {546c8d9a-103e-4f78-972b-b44e8d3c8aba} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx Source Files ================================================ FILE: deps/pjsip/third_party/opus/win32/genversion.bat ================================================ @echo off setlocal enableextensions enabledelayedexpansion for /f %%v in ('git --git-dir="%~dp0..\.git" describe --tags --match "v*"') do set version=%%v if not "%version%"=="" goto :gotversion if exist "%~dp0..\version.mk" goto :getversion echo Git cannot be found, nor can version.mk. Generating unknown version. set version=unknown goto :gotversion :getversion for /f "delims== tokens=2" %%v in (%~dps0..\version.mk) do set version=%%v set version=!version:^"=! set version=!version: =! :gotversion set version_out=#define %~2 "%version%" set version_mk=%~2 = "%version%" echo %version_out%> "%~1_temp" if %version%==unknown goto :skipgenerate echo # static version string; update manually every release.> "%~dp0..\version.mk" echo %version_mk%>> "%~dp0..\version.mk" :skipgenerate echo n | comp "%~1_temp" "%~1" > NUL 2> NUL if not errorlevel 1 goto exit copy /y "%~1_temp" "%~1" :exit del "%~1_temp" ================================================ FILE: deps/pjsip/third_party/resample/COPYING ================================================ GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS ================================================ FILE: deps/pjsip/third_party/resample/README.resample ================================================ README file for resample-1.x.tgz from the Digital Audio Resampling Home Page located at http://ccrma.stanford.edu/~jos/resample/. SOFTWARE FOR SAMPLING-RATE CONVERSION AND FIR DIGITAL FILTER DESIGN For installation instructions, read the INSTALL file in this directory. The resample program "resamples" a soundfile to change its sampling rate. For example, it can be used to convert the sampling rate from 48 kHz (used by DAT machines) to 44.1 kHz (the standard sampling rate for Compact Discs). The command line for this operation would look something like resample -by 0.91875 dat.snd cd.snd or, more simply, resample -to 44100 dat.snd cd.snd Any reasonable sampling rate can be converted to any other. The windowfilter program designs Finite-Impulse-Response (FIR) digital filters by the so-called "window method." In this method, the ideal impulse response (a sinc function) is "windowed" by a Kaiser window (a popular window used in spectrum analysis). The resample program uses 32-bit fixed-point arithmetic: 16-bits data and 16-bits coefficients. The input soundfile must be 16-bit mono or stereo (interleaved) audio data. SNDLIB The program uses elements of Bill Schottstaedt's sndlib sound file library. This means resample can read many different kinds of sound file header (AIFF, WAV, NeXT, IRCAM, etc.). The sndlib files used by resample are included in this directory to ensure stability. The latest version of sndlib should be available as ftp://ccrma-ftp.stanford.edu/pub/Lisp/sndlib.tar.gz See sndlib.html in the sndlib distribution for documentation of SNDLIB. CONTENTS of ./src directory resample.c Sampling-rate conversion program. resample.1 Manual page for resample. Try "nroff -man resample.1". resamplesubs.c Subroutines used by resample. resample.h Configuration constants for the sampling rate converter. stdefs.h Machine-dependent definitions, useful constants and macros. windowfilter.c Program for designing FIR digital filters used by resample. windowfilter.1 Manual page for windowfilter. filterkit.c Library for filter design, application, and file management. filterkit.h Declarations (procedure prototypes) for the filterkit library. README This file. README.deemph A word about deemphasis filtering. LGPL GNU Lesser General Public License (LGPL) SNDLIB files: io.c audio.c headers.c sound.c sndlib.h sndlib-strings.h COPYING SNDLIB files are Copyright 2000 by Bill Schottstaedt . The remaining files in this package, unless otherwise noted, are Copyright 1994-2006 by Julius O. Smith III , all rights reserved. Permission to use and copy is granted subject to the terms of the "GNU Lesser General Public License" (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or any later version. In addition, we request that a copy of any modified files be sent by email to jos@ccrma.stanford.edu so that we may incorporate them into the CCRMA version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. FILTERKIT CONTENTS LpFilter() - Calculates the filter coeffs for a Kaiser-windowed low-pass filter with a given roll-off frequency. These coeffs are stored into a array of doubles. writeFilter() - Writes a filter to a file. makeFilter() - A section of the original SAIL program. Calls LpFilter() to create a filter, then scales the double coeffs into a array of half words. readFilter() - Reads a filter from a file. FilterUp() - Applies a filter to a given sample when up-converting. FilterUD() - Applies a filter to a given sample when up- or down- converting. Both are repoductions of the original SAIL program. initZerox() - Initialization routine for the zerox() function. Must be called before zerox() is called. This routine loads the correct filter so zerox() can use it. zerox() - Given a pointer into a sample, finds a zero-crossing on the interval [pointer-1:pointer+2] by iteration. Query() - Ask the user for a yes/no question with prompt, default, and optional help. GetUShort() - Ask the user for a unsigned short with prompt, default, and optional help. GetDouble() - Ask the user for a double with prompt, default, and optional help. GetString() - Ask the user for a string with prompt, default, and optional help. FILTER FILE FORMAT File Name: "F" Nmult "T" Nhc ".filter" example: "F13T8.filter" and "F27T8.filter" Structure of File: "ScaleFactor" LpScl "Length" Nwing "Coeffs:" Imp[0] Imp[1] : Imp[Nwing-1] "Differences:" ImpD[0] ImpD[1] : ImpD[Nwing-1] EOF where: Something enclosed in "" indicates specific characters in the file. Nmult, Nwing, Imp[], and ImpD[] are variables (HWORD) Npc is a conversion constant. EOF is the end of the file. See writeFilter() and readFilter() in "filterkit.c" for more details. ================================================ FILE: deps/pjsip/third_party/resample/include/resamplesubs.h ================================================ #ifndef __RESAMPLESUBS_H__ #define __RESAMPLESUBS_H__ typedef char RES_BOOL; typedef short RES_HWORD; typedef int RES_WORD; typedef unsigned short RES_UHWORD; typedef unsigned int RES_UWORD; #ifdef _USRDLL # define DECL(T) __declspec(dllexport) T #else # define DECL(T) T #endif #ifdef __cplusplus extern "C" { #endif DECL(int) res_SrcLinear(const RES_HWORD X[], RES_HWORD Y[], double pFactor, RES_UHWORD nx); DECL(int) res_Resample(const RES_HWORD X[], RES_HWORD Y[], double pFactor, RES_UHWORD nx, RES_BOOL LargeF, RES_BOOL Interp); DECL(int) res_GetXOFF(double pFactor, RES_BOOL LargeF); #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/resample/src/largefilter.h ================================================ /* Included by resamplesubs.c */ #define LARGE_FILTER_NMULT ((RES_HWORD)65) #define LARGE_FILTER_SCALE 14746 /* Unity-gain scale factor */ #define LARGE_FILTER_NWING 8192 /* Filter table length */ static const RES_HWORD LARGE_FILTER_IMP[] /* Impulse response */ = { 32767, 32766, 32764, 32761, 32756, 32750, 32743, 32734, 32724, 32713, 32700, 32686, 32671, 32654, 32636, 32617, 32596, 32574, 32551, 32526, 32500, 32473, 32445, 32415, 32383, 32351, 32317, 32282, 32246, 32208, 32169, 32129, 32087, 32044, 32000, 31955, 31908, 31860, 31811, 31760, 31708, 31655, 31601, 31545, 31489, 31431, 31371, 31311, 31249, 31186, 31122, 31056, 30990, 30922, 30853, 30783, 30711, 30639, 30565, 30490, 30414, 30337, 30258, 30179, 30098, 30016, 29933, 29849, 29764, 29677, 29590, 29501, 29411, 29321, 29229, 29136, 29042, 28947, 28851, 28753, 28655, 28556, 28456, 28354, 28252, 28149, 28044, 27939, 27833, 27725, 27617, 27508, 27398, 27287, 27175, 27062, 26948, 26833, 26717, 26601, 26483, 26365, 26246, 26125, 26005, 25883, 25760, 25637, 25512, 25387, 25261, 25135, 25007, 24879, 24750, 24620, 24490, 24358, 24226, 24094, 23960, 23826, 23691, 23556, 23420, 23283, 23146, 23008, 22869, 22730, 22590, 22449, 22308, 22166, 22024, 21881, 21738, 21594, 21449, 21304, 21159, 21013, 20866, 20719, 20572, 20424, 20275, 20127, 19977, 19828, 19678, 19527, 19376, 19225, 19073, 18921, 18769, 18616, 18463, 18310, 18157, 18003, 17849, 17694, 17539, 17384, 17229, 17074, 16918, 16762, 16606, 16450, 16294, 16137, 15980, 15823, 15666, 15509, 15352, 15195, 15037, 14880, 14722, 14564, 14407, 14249, 14091, 13933, 13775, 13618, 13460, 13302, 13144, 12987, 12829, 12671, 12514, 12356, 12199, 12042, 11885, 11728, 11571, 11414, 11257, 11101, 10945, 10789, 10633, 10477, 10322, 10167, 10012, 9857, 9702, 9548, 9394, 9241, 9087, 8934, 8781, 8629, 8477, 8325, 8174, 8023, 7872, 7722, 7572, 7422, 7273, 7124, 6976, 6828, 6681, 6534, 6387, 6241, 6096, 5951, 5806, 5662, 5518, 5375, 5233, 5091, 4949, 4808, 4668, 4528, 4389, 4250, 4112, 3975, 3838, 3702, 3566, 3431, 3297, 3163, 3030, 2898, 2766, 2635, 2505, 2375, 2246, 2118, 1990, 1864, 1738, 1612, 1487, 1364, 1240, 1118, 996, 875, 755, 636, 517, 400, 283, 166, 51, -63, -176, -289, -401, -513, -623, -733, -841, -949, -1056, -1162, -1268, -1372, -1476, -1578, -1680, -1781, -1881, -1980, -2078, -2176, -2272, -2367, -2462, -2556, -2648, -2740, -2831, -2921, -3010, -3098, -3185, -3271, -3356, -3441, -3524, -3606, -3688, -3768, -3848, -3926, -4004, -4080, -4156, -4231, -4304, -4377, -4449, -4519, -4589, -4658, -4726, -4792, -4858, -4923, -4987, -5050, -5111, -5172, -5232, -5291, -5349, -5406, -5462, -5517, -5571, -5624, -5675, -5726, -5776, -5825, -5873, -5920, -5966, -6011, -6055, -6098, -6140, -6181, -6222, -6261, -6299, -6336, -6372, -6407, -6441, -6475, -6507, -6538, -6569, -6598, -6626, -6654, -6680, -6706, -6730, -6754, -6777, -6798, -6819, -6839, -6858, -6876, -6893, -6909, -6924, -6938, -6951, -6964, -6975, -6986, -6995, -7004, -7012, -7019, -7025, -7030, -7035, -7038, -7040, -7042, -7043, -7043, -7042, -7040, -7038, -7034, -7030, -7025, -7019, -7012, -7004, -6996, -6986, -6976, -6965, -6954, -6941, -6928, -6914, -6899, -6884, -6867, -6850, -6832, -6814, -6794, -6774, -6753, -6732, -6709, -6686, -6663, -6638, -6613, -6587, -6561, -6534, -6506, -6478, -6448, -6419, -6388, -6357, -6325, -6293, -6260, -6226, -6192, -6157, -6122, -6086, -6049, -6012, -5975, -5936, -5897, -5858, -5818, -5778, -5737, -5695, -5653, -5611, -5568, -5524, -5480, -5436, -5391, -5345, -5300, -5253, -5207, -5159, -5112, -5064, -5015, -4966, -4917, -4868, -4818, -4767, -4716, -4665, -4614, -4562, -4510, -4457, -4404, -4351, -4298, -4244, -4190, -4136, -4081, -4026, -3971, -3916, -3860, -3804, -3748, -3692, -3635, -3578, -3521, -3464, -3406, -3349, -3291, -3233, -3175, -3117, -3058, -3000, -2941, -2882, -2823, -2764, -2705, -2646, -2587, -2527, -2468, -2408, -2349, -2289, -2229, -2169, -2110, -2050, -1990, -1930, -1870, -1811, -1751, -1691, -1631, -1571, -1512, -1452, -1392, -1333, -1273, -1214, -1154, -1095, -1036, -977, -918, -859, -800, -741, -683, -624, -566, -508, -450, -392, -335, -277, -220, -163, -106, -49, 6, 63, 119, 175, 230, 286, 341, 396, 450, 505, 559, 613, 667, 720, 773, 826, 878, 931, 983, 1034, 1086, 1137, 1187, 1238, 1288, 1337, 1387, 1436, 1484, 1533, 1581, 1628, 1675, 1722, 1769, 1815, 1861, 1906, 1951, 1996, 2040, 2084, 2127, 2170, 2212, 2255, 2296, 2338, 2378, 2419, 2459, 2498, 2538, 2576, 2615, 2652, 2690, 2727, 2763, 2799, 2834, 2870, 2904, 2938, 2972, 3005, 3038, 3070, 3102, 3133, 3164, 3194, 3224, 3253, 3282, 3310, 3338, 3365, 3392, 3418, 3444, 3469, 3494, 3518, 3542, 3566, 3588, 3611, 3632, 3653, 3674, 3694, 3714, 3733, 3752, 3770, 3788, 3805, 3821, 3837, 3853, 3868, 3882, 3896, 3910, 3923, 3935, 3947, 3958, 3969, 3980, 3989, 3999, 4007, 4016, 4023, 4031, 4037, 4044, 4049, 4054, 4059, 4063, 4067, 4070, 4073, 4075, 4076, 4077, 4078, 4078, 4078, 4077, 4076, 4074, 4071, 4068, 4065, 4061, 4057, 4052, 4047, 4041, 4035, 4028, 4021, 4013, 4005, 3997, 3988, 3978, 3968, 3958, 3947, 3936, 3924, 3912, 3899, 3886, 3872, 3858, 3844, 3829, 3814, 3798, 3782, 3766, 3749, 3731, 3714, 3696, 3677, 3658, 3639, 3619, 3599, 3578, 3558, 3536, 3515, 3493, 3470, 3448, 3425, 3401, 3378, 3353, 3329, 3304, 3279, 3254, 3228, 3202, 3175, 3149, 3122, 3094, 3067, 3039, 3011, 2982, 2953, 2924, 2895, 2865, 2835, 2805, 2775, 2744, 2713, 2682, 2651, 2619, 2587, 2555, 2523, 2490, 2457, 2424, 2391, 2358, 2324, 2290, 2256, 2222, 2188, 2153, 2119, 2084, 2049, 2014, 1978, 1943, 1907, 1872, 1836, 1800, 1764, 1727, 1691, 1655, 1618, 1581, 1545, 1508, 1471, 1434, 1397, 1360, 1322, 1285, 1248, 1210, 1173, 1135, 1098, 1060, 1023, 985, 947, 910, 872, 834, 797, 759, 721, 684, 646, 608, 571, 533, 496, 458, 421, 383, 346, 308, 271, 234, 197, 160, 123, 86, 49, 12, -23, -60, -96, -133, -169, -205, -241, -277, -313, -348, -384, -419, -455, -490, -525, -559, -594, -628, -663, -697, -731, -765, -798, -832, -865, -898, -931, -963, -996, -1028, -1060, -1092, -1124, -1155, -1186, -1217, -1248, -1279, -1309, -1339, -1369, -1398, -1428, -1457, -1486, -1514, -1542, -1571, -1598, -1626, -1653, -1680, -1707, -1733, -1760, -1785, -1811, -1836, -1862, -1886, -1911, -1935, -1959, -1982, -2006, -2029, -2051, -2074, -2096, -2118, -2139, -2160, -2181, -2202, -2222, -2242, -2261, -2280, -2299, -2318, -2336, -2354, -2372, -2389, -2406, -2423, -2439, -2455, -2470, -2486, -2500, -2515, -2529, -2543, -2557, -2570, -2583, -2595, -2607, -2619, -2631, -2642, -2652, -2663, -2673, -2683, -2692, -2701, -2710, -2718, -2726, -2734, -2741, -2748, -2754, -2760, -2766, -2772, -2777, -2782, -2786, -2790, -2794, -2797, -2800, -2803, -2805, -2807, -2809, -2810, -2811, -2812, -2812, -2812, -2812, -2811, -2810, -2808, -2807, -2804, -2802, -2799, -2796, -2792, -2789, -2785, -2780, -2775, -2770, -2765, -2759, -2753, -2746, -2740, -2732, -2725, -2717, -2709, -2701, -2692, -2683, -2674, -2664, -2655, -2644, -2634, -2623, -2612, -2601, -2589, -2577, -2565, -2552, -2539, -2526, -2513, -2499, -2485, -2471, -2457, -2442, -2427, -2412, -2396, -2380, -2364, -2348, -2331, -2315, -2297, -2280, -2263, -2245, -2227, -2209, -2190, -2171, -2152, -2133, -2114, -2094, -2075, -2055, -2034, -2014, -1993, -1972, -1951, -1930, -1909, -1887, -1865, -1843, -1821, -1799, -1776, -1754, -1731, -1708, -1685, -1662, -1638, -1614, -1591, -1567, -1543, -1519, -1494, -1470, -1445, -1421, -1396, -1371, -1346, -1321, -1295, -1270, -1244, -1219, -1193, -1167, -1142, -1116, -1090, -1064, -1037, -1011, -985, -958, -932, -905, -879, -852, -826, -799, -772, -745, -719, -692, -665, -638, -611, -584, -557, -530, -503, -476, -449, -422, -395, -368, -341, -314, -287, -260, -234, -207, -180, -153, -126, -100, -73, -46, -20, 6, 32, 59, 85, 111, 138, 164, 190, 216, 242, 268, 294, 319, 345, 370, 396, 421, 446, 471, 496, 521, 546, 571, 595, 619, 644, 668, 692, 716, 739, 763, 787, 810, 833, 856, 879, 902, 924, 947, 969, 991, 1013, 1035, 1056, 1078, 1099, 1120, 1141, 1162, 1182, 1202, 1223, 1243, 1262, 1282, 1301, 1320, 1339, 1358, 1377, 1395, 1413, 1431, 1449, 1467, 1484, 1501, 1518, 1535, 1551, 1567, 1583, 1599, 1615, 1630, 1645, 1660, 1674, 1689, 1703, 1717, 1731, 1744, 1757, 1770, 1783, 1795, 1808, 1820, 1831, 1843, 1854, 1865, 1876, 1886, 1897, 1907, 1916, 1926, 1935, 1944, 1953, 1961, 1970, 1978, 1985, 1993, 2000, 2007, 2014, 2020, 2026, 2032, 2038, 2043, 2048, 2053, 2058, 2062, 2066, 2070, 2073, 2077, 2080, 2083, 2085, 2087, 2089, 2091, 2093, 2094, 2095, 2095, 2096, 2096, 2096, 2096, 2095, 2094, 2093, 2092, 2090, 2088, 2086, 2084, 2081, 2079, 2075, 2072, 2069, 2065, 2061, 2056, 2052, 2047, 2042, 2037, 2031, 2025, 2019, 2013, 2006, 2000, 1993, 1986, 1978, 1971, 1963, 1955, 1946, 1938, 1929, 1920, 1911, 1901, 1892, 1882, 1872, 1862, 1851, 1841, 1830, 1819, 1807, 1796, 1784, 1772, 1760, 1748, 1735, 1723, 1710, 1697, 1684, 1670, 1657, 1643, 1629, 1615, 1601, 1586, 1572, 1557, 1542, 1527, 1512, 1496, 1481, 1465, 1449, 1433, 1417, 1401, 1384, 1368, 1351, 1334, 1317, 1300, 1283, 1265, 1248, 1230, 1212, 1195, 1177, 1159, 1140, 1122, 1104, 1085, 1067, 1048, 1029, 1010, 991, 972, 953, 934, 915, 895, 876, 856, 837, 817, 797, 777, 758, 738, 718, 698, 678, 658, 637, 617, 597, 577, 556, 536, 516, 495, 475, 454, 434, 414, 393, 373, 352, 332, 311, 291, 270, 250, 229, 208, 188, 168, 147, 127, 106, 86, 65, 45, 25, 5, -15, -35, -55, -75, -95, -115, -135, -155, -175, -195, -215, -234, -254, -274, -293, -313, -332, -351, -370, -390, -409, -428, -446, -465, -484, -503, -521, -539, -558, -576, -594, -612, -630, -648, -666, -683, -701, -718, -735, -752, -769, -786, -803, -819, -836, -852, -868, -885, -900, -916, -932, -947, -963, -978, -993, -1008, -1023, -1038, -1052, -1066, -1081, -1095, -1108, -1122, -1136, -1149, -1162, -1175, -1188, -1201, -1214, -1226, -1238, -1250, -1262, -1274, -1285, -1297, -1308, -1319, -1330, -1340, -1351, -1361, -1371, -1381, -1390, -1400, -1409, -1418, -1427, -1436, -1445, -1453, -1461, -1469, -1477, -1485, -1492, -1499, -1506, -1513, -1520, -1526, -1532, -1538, -1544, -1550, -1555, -1560, -1566, -1570, -1575, -1579, -1584, -1588, -1592, -1595, -1599, -1602, -1605, -1608, -1610, -1613, -1615, -1617, -1619, -1620, -1622, -1623, -1624, -1625, -1625, -1626, -1626, -1626, -1626, -1625, -1625, -1624, -1623, -1622, -1621, -1619, -1617, -1615, -1613, -1611, -1608, -1605, -1603, -1599, -1596, -1593, -1589, -1585, -1581, -1577, -1572, -1568, -1563, -1558, -1553, -1547, -1542, -1536, -1530, -1524, -1518, -1511, -1505, -1498, -1491, -1484, -1477, -1469, -1462, -1454, -1446, -1438, -1430, -1421, -1413, -1404, -1395, -1386, -1377, -1367, -1358, -1348, -1338, -1328, -1318, -1308, -1297, -1287, -1276, -1265, -1254, -1243, -1232, -1221, -1209, -1198, -1186, -1174, -1162, -1150, -1138, -1125, -1113, -1100, -1087, -1075, -1062, -1049, -1035, -1022, -1009, -995, -982, -968, -954, -941, -927, -913, -898, -884, -870, -856, -841, -827, -812, -797, -783, -768, -753, -738, -723, -708, -692, -677, -662, -647, -631, -616, -600, -585, -569, -554, -538, -522, -506, -491, -475, -459, -443, -427, -411, -395, -379, -363, -347, -331, -315, -299, -283, -267, -251, -235, -218, -202, -186, -170, -154, -138, -122, -106, -90, -74, -58, -42, -26, -10, 5, 21, 37, 53, 69, 85, 100, 116, 132, 147, 163, 179, 194, 209, 225, 240, 256, 271, 286, 301, 316, 331, 346, 361, 376, 391, 405, 420, 434, 449, 463, 477, 492, 506, 520, 534, 548, 561, 575, 589, 602, 615, 629, 642, 655, 668, 681, 694, 706, 719, 731, 744, 756, 768, 780, 792, 804, 816, 827, 839, 850, 861, 872, 883, 894, 905, 915, 926, 936, 946, 956, 966, 976, 986, 995, 1005, 1014, 1023, 1032, 1041, 1049, 1058, 1066, 1075, 1083, 1091, 1099, 1106, 1114, 1121, 1128, 1135, 1142, 1149, 1156, 1162, 1169, 1175, 1181, 1187, 1192, 1198, 1203, 1208, 1214, 1218, 1223, 1228, 1232, 1237, 1241, 1245, 1249, 1252, 1256, 1259, 1262, 1265, 1268, 1271, 1273, 1276, 1278, 1280, 1282, 1284, 1285, 1287, 1288, 1289, 1290, 1291, 1292, 1292, 1292, 1293, 1293, 1292, 1292, 1292, 1291, 1290, 1289, 1288, 1287, 1285, 1284, 1282, 1280, 1278, 1276, 1274, 1271, 1269, 1266, 1263, 1260, 1257, 1253, 1250, 1246, 1242, 1238, 1234, 1230, 1225, 1221, 1216, 1211, 1206, 1201, 1196, 1190, 1185, 1179, 1173, 1167, 1161, 1155, 1149, 1142, 1136, 1129, 1122, 1115, 1108, 1101, 1094, 1086, 1078, 1071, 1063, 1055, 1047, 1039, 1030, 1022, 1013, 1005, 996, 987, 978, 969, 960, 951, 941, 932, 922, 913, 903, 893, 883, 873, 863, 853, 842, 832, 821, 811, 800, 789, 778, 768, 757, 746, 734, 723, 712, 701, 689, 678, 666, 654, 643, 631, 619, 607, 596, 584, 572, 559, 547, 535, 523, 511, 498, 486, 474, 461, 449, 436, 424, 411, 399, 386, 373, 361, 348, 335, 322, 310, 297, 284, 271, 258, 246, 233, 220, 207, 194, 181, 168, 156, 143, 130, 117, 104, 91, 78, 65, 53, 40, 27, 14, 1, -10, -23, -36, -48, -61, -74, -86, -99, -111, -124, -136, -149, -161, -174, -186, -198, -211, -223, -235, -247, -259, -271, -283, -295, -307, -319, -330, -342, -354, -365, -377, -388, -399, -411, -422, -433, -444, -455, -466, -477, -488, -499, -509, -520, -530, -541, -551, -561, -571, -581, -591, -601, -611, -621, -631, -640, -650, -659, -668, -677, -686, -695, -704, -713, -722, -730, -739, -747, -755, -763, -771, -779, -787, -795, -803, -810, -817, -825, -832, -839, -846, -853, -859, -866, -873, -879, -885, -891, -897, -903, -909, -915, -920, -926, -931, -936, -941, -946, -951, -955, -960, -964, -969, -973, -977, -981, -985, -988, -992, -995, -999, -1002, -1005, -1008, -1011, -1013, -1016, -1018, -1020, -1023, -1025, -1026, -1028, -1030, -1031, -1033, -1034, -1035, -1036, -1037, -1038, -1038, -1039, -1039, -1039, -1039, -1039, -1039, -1039, -1038, -1038, -1037, -1036, -1036, -1034, -1033, -1032, -1031, -1029, -1027, -1026, -1024, -1022, -1019, -1017, -1015, -1012, -1010, -1007, -1004, -1001, -998, -995, -991, -988, -984, -980, -977, -973, -969, -965, -960, -956, -951, -947, -942, -937, -932, -927, -922, -917, -912, -906, -901, -895, -889, -883, -877, -871, -865, -859, -853, -846, -840, -833, -826, -819, -812, -805, -798, -791, -784, -777, -769, -762, -754, -746, -739, -731, -723, -715, -707, -699, -691, -682, -674, -665, -657, -648, -640, -631, -622, -614, -605, -596, -587, -578, -569, -559, -550, -541, -532, -522, -513, -503, -494, -484, -474, -465, -455, -445, -436, -426, -416, -406, -396, -386, -376, -366, -356, -346, -336, -325, -315, -305, -295, -285, -274, -264, -254, -243, -233, -223, -212, -202, -192, -181, -171, -161, -150, -140, -129, -119, -108, -98, -88, -77, -67, -57, -46, -36, -25, -15, -5, 5, 15, 25, 35, 46, 56, 66, 76, 86, 97, 107, 117, 127, 137, 147, 157, 167, 177, 186, 196, 206, 216, 225, 235, 245, 254, 264, 273, 283, 292, 301, 311, 320, 329, 338, 347, 356, 365, 374, 383, 392, 400, 409, 418, 426, 435, 443, 451, 460, 468, 476, 484, 492, 500, 508, 515, 523, 531, 538, 546, 553, 560, 568, 575, 582, 589, 596, 602, 609, 616, 622, 629, 635, 642, 648, 654, 660, 666, 672, 678, 683, 689, 694, 700, 705, 710, 715, 720, 725, 730, 735, 739, 744, 748, 753, 757, 761, 765, 769, 773, 777, 780, 784, 787, 791, 794, 797, 800, 803, 806, 809, 811, 814, 816, 818, 821, 823, 825, 827, 828, 830, 832, 833, 835, 836, 837, 838, 839, 840, 841, 841, 842, 842, 843, 843, 843, 843, 843, 843, 843, 842, 842, 841, 840, 840, 839, 838, 837, 835, 834, 833, 831, 830, 828, 826, 824, 822, 820, 818, 816, 813, 811, 808, 806, 803, 800, 797, 794, 791, 788, 784, 781, 777, 774, 770, 766, 763, 759, 755, 750, 746, 742, 738, 733, 729, 724, 719, 714, 710, 705, 700, 694, 689, 684, 679, 673, 668, 662, 657, 651, 645, 639, 633, 627, 621, 615, 609, 603, 596, 590, 584, 577, 571, 564, 557, 551, 544, 537, 530, 523, 516, 509, 502, 495, 487, 480, 473, 465, 458, 450, 443, 435, 428, 420, 413, 405, 397, 389, 381, 374, 366, 358, 350, 342, 334, 326, 318, 310, 301, 293, 285, 277, 269, 260, 252, 244, 236, 227, 219, 211, 202, 194, 185, 177, 169, 160, 152, 143, 135, 126, 118, 110, 101, 93, 84, 76, 67, 59, 50, 42, 34, 25, 17, 8, 0, -7, -16, -24, -32, -41, -49, -57, -66, -74, -82, -90, -98, -106, -115, -123, -131, -139, -147, -155, -163, -171, -179, -186, -194, -202, -210, -217, -225, -233, -240, -248, -255, -263, -270, -278, -285, -292, -299, -307, -314, -321, -328, -335, -342, -349, -356, -362, -369, -376, -382, -389, -396, -402, -408, -415, -421, -427, -433, -439, -445, -451, -457, -463, -469, -475, -480, -486, -491, -497, -502, -507, -513, -518, -523, -528, -533, -538, -542, -547, -552, -556, -561, -565, -570, -574, -578, -582, -586, -590, -594, -598, -602, -605, -609, -612, -616, -619, -622, -625, -629, -632, -634, -637, -640, -643, -645, -648, -650, -653, -655, -657, -659, -661, -663, -665, -667, -668, -670, -671, -673, -674, -675, -676, -678, -679, -679, -680, -681, -682, -682, -683, -683, -683, -684, -684, -684, -684, -684, -684, -683, -683, -682, -682, -681, -681, -680, -679, -678, -677, -676, -675, -674, -672, -671, -669, -668, -666, -664, -662, -661, -659, -657, -654, -652, -650, -648, -645, -643, -640, -637, -635, -632, -629, -626, -623, -620, -617, -613, -610, -607, -603, -600, -596, -593, -589, -585, -581, -577, -573, -569, -565, -561, -557, -552, -548, -544, -539, -534, -530, -525, -520, -516, -511, -506, -501, -496, -491, -486, -481, -475, -470, -465, -460, -454, -449, -443, -438, -432, -426, -421, -415, -409, -403, -398, -392, -386, -380, -374, -368, -362, -355, -349, -343, -337, -331, -324, -318, -312, -305, -299, -292, -286, -280, -273, -266, -260, -253, -247, -240, -234, -227, -220, -213, -207, -200, -193, -187, -180, -173, -166, -159, -153, -146, -139, -132, -125, -118, -112, -105, -98, -91, -84, -77, -70, -64, -57, -50, -43, -36, -29, -22, -16, -9, -2, 4, 11, 17, 24, 31, 38, 44, 51, 58, 64, 71, 78, 84, 91, 98, 104, 111, 117, 124, 130, 137, 143, 149, 156, 162, 168, 175, 181, 187, 193, 199, 205, 212, 218, 224, 230, 236, 241, 247, 253, 259, 265, 270, 276, 282, 287, 293, 298, 304, 309, 314, 320, 325, 330, 335, 340, 345, 350, 355, 360, 365, 370, 375, 380, 384, 389, 393, 398, 402, 407, 411, 415, 419, 424, 428, 432, 436, 440, 443, 447, 451, 455, 458, 462, 465, 469, 472, 475, 479, 482, 485, 488, 491, 494, 497, 499, 502, 505, 507, 510, 512, 515, 517, 519, 522, 524, 526, 528, 530, 532, 533, 535, 537, 538, 540, 541, 543, 544, 545, 546, 548, 549, 550, 550, 551, 552, 553, 553, 554, 554, 555, 555, 555, 556, 556, 556, 556, 556, 556, 555, 555, 555, 554, 554, 553, 553, 552, 551, 551, 550, 549, 548, 547, 546, 545, 543, 542, 541, 539, 538, 536, 534, 533, 531, 529, 527, 525, 523, 521, 519, 517, 514, 512, 510, 507, 505, 502, 500, 497, 494, 492, 489, 486, 483, 480, 477, 474, 471, 467, 464, 461, 457, 454, 450, 447, 443, 440, 436, 432, 429, 425, 421, 417, 413, 409, 405, 401, 397, 393, 388, 384, 380, 375, 371, 367, 362, 358, 353, 349, 344, 339, 335, 330, 325, 321, 316, 311, 306, 301, 296, 291, 286, 281, 276, 271, 266, 261, 256, 251, 245, 240, 235, 230, 225, 219, 214, 209, 203, 198, 193, 187, 182, 176, 171, 165, 160, 154, 149, 144, 138, 132, 127, 121, 116, 110, 105, 99, 94, 88, 83, 77, 71, 66, 60, 55, 49, 44, 38, 33, 27, 21, 16, 10, 5, 0, -5, -11, -16, -22, -27, -33, -38, -43, -49, -54, -60, -65, -70, -76, -81, -86, -92, -97, -102, -107, -113, -118, -123, -128, -133, -138, -143, -148, -153, -158, -163, -168, -173, -178, -183, -188, -192, -197, -202, -207, -211, -216, -221, -225, -230, -234, -239, -243, -247, -252, -256, -260, -265, -269, -273, -277, -281, -285, -289, -293, -297, -301, -305, -308, -312, -316, -319, -323, -327, -330, -334, -337, -340, -344, -347, -350, -353, -357, -360, -363, -366, -369, -372, -374, -377, -380, -383, -385, -388, -390, -393, -395, -398, -400, -402, -405, -407, -409, -411, -413, -415, -417, -419, -420, -422, -424, -425, -427, -429, -430, -431, -433, -434, -435, -436, -438, -439, -440, -441, -442, -442, -443, -444, -445, -445, -446, -446, -447, -447, -448, -448, -448, -448, -449, -449, -449, -449, -449, -448, -448, -448, -448, -447, -447, -446, -446, -445, -445, -444, -443, -442, -442, -441, -440, -439, -438, -437, -435, -434, -433, -432, -430, -429, -427, -426, -424, -423, -421, -419, -418, -416, -414, -412, -410, -408, -406, -404, -402, -400, -397, -395, -393, -390, -388, -385, -383, -380, -378, -375, -373, -370, -367, -364, -361, -359, -356, -353, -350, -347, -344, -340, -337, -334, -331, -328, -324, -321, -318, -314, -311, -307, -304, -300, -297, -293, -290, -286, -282, -279, -275, -271, -267, -263, -260, -256, -252, -248, -244, -240, -236, -232, -228, -224, -220, -216, -212, -207, -203, -199, -195, -191, -186, -182, -178, -174, -169, -165, -161, -156, -152, -148, -143, -139, -134, -130, -126, -121, -117, -112, -108, -103, -99, -95, -90, -86, -81, -77, -72, -68, -63, -59, -54, -50, -45, -41, -36, -32, -27, -23, -18, -14, -9, -5, 0, 3, 7, 12, 16, 21, 25, 29, 34, 38, 43, 47, 51, 56, 60, 64, 69, 73, 77, 81, 85, 90, 94, 98, 102, 106, 110, 115, 119, 123, 127, 131, 135, 139, 143, 146, 150, 154, 158, 162, 166, 169, 173, 177, 181, 184, 188, 192, 195, 199, 202, 206, 209, 212, 216, 219, 223, 226, 229, 232, 236, 239, 242, 245, 248, 251, 254, 257, 260, 263, 266, 268, 271, 274, 277, 279, 282, 284, 287, 289, 292, 294, 297, 299, 301, 304, 306, 308, 310, 312, 314, 316, 318, 320, 322, 324, 326, 328, 329, 331, 333, 334, 336, 337, 339, 340, 341, 343, 344, 345, 346, 348, 349, 350, 351, 352, 353, 354, 354, 355, 356, 357, 357, 358, 358, 359, 359, 360, 360, 361, 361, 361, 361, 362, 362, 362, 362, 362, 362, 362, 361, 361, 361, 361, 360, 360, 360, 359, 359, 358, 358, 357, 356, 356, 355, 354, 353, 352, 351, 350, 349, 348, 347, 346, 345, 344, 343, 341, 340, 339, 337, 336, 334, 333, 331, 330, 328, 326, 324, 323, 321, 319, 317, 315, 313, 311, 309, 307, 305, 303, 301, 299, 297, 294, 292, 290, 288, 285, 283, 280, 278, 275, 273, 270, 268, 265, 263, 260, 257, 254, 252, 249, 246, 243, 240, 238, 235, 232, 229, 226, 223, 220, 217, 214, 211, 208, 204, 201, 198, 195, 192, 188, 185, 182, 179, 175, 172, 169, 165, 162, 159, 155, 152, 149, 145, 142, 138, 135, 131, 128, 124, 121, 117, 114, 110, 107, 103, 100, 96, 93, 89, 85, 82, 78, 75, 71, 67, 64, 60, 57, 53, 49, 46, 42, 39, 35, 31, 28, 24, 21, 17, 14, 10, 6, 3, 0, -3, -7, -10, -14, -18, -21, -25, -28, -32, -35, -39, -42, -45, -49, -52, -56, -59, -63, -66, -69, -73, -76, -79, -83, -86, -89, -93, -96, -99, -102, -105, -109, -112, -115, -118, -121, -124, -127, -130, -133, -136, -139, -142, -145, -148, -151, -154, -157, -160, -162, -165, -168, -171, -173, -176, -179, -181, -184, -186, -189, -191, -194, -196, -199, -201, -204, -206, -208, -211, -213, -215, -217, -219, -221, -224, -226, -228, -230, -232, -234, -236, -237, -239, -241, -243, -245, -246, -248, -250, -251, -253, -254, -256, -257, -259, -260, -262, -263, -264, -266, -267, -268, -269, -270, -271, -273, -274, -275, -275, -276, -277, -278, -279, -280, -281, -281, -282, -283, -283, -284, -284, -285, -285, -286, -286, -286, -287, -287, -287, -287, -288, -288, -288, -288, -288, -288, -288, -288, -288, -288, -287, -287, -287, -287, -286, -286, -286, -285, -285, -284, -284, -283, -283, -282, -281, -281, -280, -279, -278, -277, -277, -276, -275, -274, -273, -272, -271, -270, -269, -267, -266, -265, -264, -262, -261, -260, -258, -257, -256, -254, -253, -251, -250, -248, -247, -245, -243, -242, -240, -238, -236, -235, -233, -231, -229, -227, -225, -223, -221, -219, -217, -215, -213, -211, -209, -207, -205, -203, -201, -198, -196, -194, -192, -189, -187, -185, -182, -180, -178, -175, -173, -170, -168, -165, -163, -160, -158, -155, -153, -150, -148, -145, -142, -140, -137, -135, -132, -129, -127, -124, -121, -118, -116, -113, -110, -108, -105, -102, -99, -96, -94, -91, -88, -85, -82, -80, -77, -74, -71, -68, -65, -63, -60, -57, -54, -51, -48, -45, -42, -40, -37, -34, -31, -28, -25, -22, -20, -17, -14, -11, -8, -5, -2, 0, 2, 5, 8, 11, 13, 16, 19, 22, 25, 27, 30, 33, 36, 38, 41, 44, 47, 49, 52, 55, 57, 60, 63, 65, 68, 71, 73, 76, 78, 81, 83, 86, 88, 91, 93, 96, 98, 101, 103, 106, 108, 110, 113, 115, 117, 120, 122, 124, 127, 129, 131, 133, 135, 137, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 161, 163, 165, 167, 169, 171, 172, 174, 176, 177, 179, 181, 182, 184, 185, 187, 188, 190, 191, 193, 194, 195, 197, 198, 199, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 216, 217, 218, 219, 219, 220, 221, 222, 222, 223, 223, 224, 224, 225, 225, 226, 226, 227, 227, 227, 227, 228, 228, 228, 228, 228, 229, 229, 229, 229, 229, 229, 229, 229, 228, 228, 228, 228, 228, 228, 227, 227, 227, 226, 226, 225, 225, 225, 224, 224, 223, 222, 222, 221, 221, 220, 219, 218, 218, 217, 216, 215, 214, 214, 213, 212, 211, 210, 209, 208, 207, 206, 205, 203, 202, 201, 200, 199, 197, 196, 195, 194, 192, 191, 190, 188, 187, 185, 184, 182, 181, 179, 178, 176, 175, 173, 172, 170, 168, 167, 165, 163, 162, 160, 158, 156, 155, 153, 151, 149, 147, 146, 144, 142, 140, 138, 136, 134, 132, 130, 128, 126, 124, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 99, 97, 95, 93, 91, 89, 87, 84, 82, 80, 78, 76, 73, 71, 69, 67, 64, 62, 60, 58, 56, 53, 51, 49, 47, 44, 42, 40, 38, 35, 33, 31, 28, 26, 24, 22, 19, 17, 15, 13, 10, 8, 6, 4, 1, 0, -2, -4, -6, -9, -11, -13, -15, -17, -20, -22, -24, -26, -28, -30, -33, -35, -37, -39, -41, -43, -45, -47, -49, -52, -54, -56, -58, -60, -62, -64, -66, -68, -70, -72, -74, -76, -77, -79, -81, -83, -85, -87, -89, -90, -92, -94, -96, -98, -99, -101, -103, -105, -106, -108, -110, -111, -113, -114, -116, -118, -119, -121, -122, -124, -125, -127, -128, -130, -131, -132, -134, -135, -136, -138, -139, -140, -142, -143, -144, -145, -146, -148, -149, -150, -151, -152, -153, -154, -155, -156, -157, -158, -159, -160, -161, -162, -162, -163, -164, -165, -166, -166, -167, -168, -168, -169, -170, -170, -171, -171, -172, -172, -173, -173, -174, -174, -175, -175, -175, -176, -176, -176, -177, -177, -177, -177, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -178, -177, -177, -177, -177, -177, -176, -176, -176, -175, -175, -175, -174, -174, -173, -173, -172, -172, -171, -171, -170, -170, -169, -168, -168, -167, -166, -166, -165, -164, -163, -163, -162, -161, -160, -159, -158, -158, -157, -156, -155, -154, -153, -152, -151, -150, -149, -148, -147, -145, -144, -143, -142, -141, -140, -139, -137, -136, -135, -134, -132, -131, -130, -128, -127, -126, -124, -123, -122, -120, -119, -118, -116, -115, -113, -112, -110, -109, -107, -106, -104, -103, -101, -100, -98, -97, -95, -94, -92, -90, -89, -87, -86, -84, -82, -81, -79, -77, -76, -74, -72, -71, -69, -67, -66, -64, -62, -60, -59, -57, -55, -54, -52, -50, -48, -47, -45, -43, -41, -40, -38, -36, -34, -33, -31, -29, -27, -25, -24, -22, -20, -18, -17, -15, -13, -11, -10, -8, -6, -4, -3, -1, 0, 2, 3, 5, 7, 8, 10, 12, 14, 15, 17, 19, 20, 22, 24, 25, 27, 29, 30, 32, 34, 35, 37, 39, 40, 42, 43, 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 65, 66, 68, 69, 70, 72, 73, 75, 76, 77, 79, 80, 81, 83, 84, 85, 87, 88, 89, 90, 92, 93, 94, 95, 96, 97, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 117, 118, 119, 120, 121, 121, 122, 123, 124, 124, 125, 126, 126, 127, 128, 128, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, 134, 134, 135, 135, 135, 136, 136, 136, 136, 137, 137, 137, 137, 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139, 139, 139, 139, 138, 138, 138, 138, 138, 138, 138, 138, 137, 137, 137, 137, 136, 136, 136, 136, 135, 135, 134, 134, 134, 133, 133, 132, 132, 132, 131, 131, 130, 129, 129, 128, 128, 127, 127, 126, 125, 125, 124, 123, 123, 122, 121, 121, 120, 119, 118, 117, 117, 116, 115, 114, 113, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 87, 86, 85, 84, 83, 82, 81, 79, 78, 77, 76, 75, 73, 72, 71, 70, 68, 67, 66, 65, 63, 62, 61, 60, 58, 57, 56, 54, 53, 52, 50, 49, 48, 47, 45, 44, 43, 41, 40, 39, 37, 36, 34, 33, 32, 30, 29, 28, 26, 25, 24, 22, 21, 20, 18, 17, 16, 14, 13, 11, 10, 9, 7, 6, 5, 3, 2, 1, 0, -1, -2, -4, -5, -6, -8, -9, -10, -11, -13, -14, -15, -17, -18, -19, -20, -22, -23, -24, -26, -27, -28, -29, -30, -32, -33, -34, -35, -37, -38, -39, -40, -41, -42, -44, -45, -46, -47, -48, -49, -50, -51, -53, -54, -55, -56, -57, -58, -59, -60, -61, -62, -63, -64, -65, -66, -67, -68, -69, -70, -71, -71, -72, -73, -74, -75, -76, -77, -77, -78, -79, -80, -81, -81, -82, -83, -84, -84, -85, -86, -87, -87, -88, -89, -89, -90, -90, -91, -92, -92, -93, -93, -94, -94, -95, -95, -96, -96, -97, -97, -98, -98, -99, -99, -99, -100, -100, -100, -101, -101, -101, -102, -102, -102, -102, -103, -103, -103, -103, -103, -104, -104, -104, -104, -104, -104, -104, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -104, -104, -104, -104, -104, -104, -104, -104, -103, -103, -103, -103, -103, -102, -102, -102, -102, -101, -101, -101, -100, -100, -100, -99, -99, -99, -98, -98, -97, -97, -97, -96, -96, -95, -95, -94, -94, -93, -93, -92, -92, -91, -90, -90, -89, -89, -88, -87, -87, -86, -86, -85, -84, -84, -83, -82, -81, -81, -80, -79, -79, -78, -77, -76, -76, -75, -74, -73, -72, -72, -71, -70, -69, -68, -67, -67, -66, -65, -64, -63, -62, -61, -61, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 46, 47, 48, 49, 49, 50, 51, 52, 52, 53, 54, 54, 55, 56, 56, 57, 58, 58, 59, 59, 60, 61, 61, 62, 62, 63, 64, 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, 69, 70, 70, 71, 71, 72, 72, 72, 73, 73, 73, 74, 74, 74, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 78, 78, 78, 78, 78, 78, 77, 77, 77, 77, 77, 76, 76, 76, 76, 75, 75, 75, 74, 74, 74, 74, 73, 73, 72, 72, 72, 71, 71, 71, 70, 70, 69, 69, 69, 68, 68, 67, 67, 66, 66, 65, 65, 64, 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 57, 57, 56, 56, 55, 55, 54, 53, 53, 52, 51, 51, 50, 50, 49, 48, 48, 47, 46, 46, 45, 44, 44, 43, 42, 41, 41, 40, 39, 39, 38, 37, 37, 36, 35, 34, 34, 33, 32, 31, 31, 30, 29, 28, 28, 27, 26, 25, 25, 24, 23, 22, 22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 14, 13, 12, 12, 11, 10, 9, 9, 8, 7, 6, 6, 5, 4, 3, 3, 2, 1, 0, 0, 0, -1, -2, -2, -3, -4, -5, -5, -6, -7, -8, -8, -9, -10, -10, -11, -12, -13, -13, -14, -15, -15, -16, -17, -17, -18, -19, -19, -20, -21, -21, -22, -23, -23, -24, -25, -25, -26, -27, -27, -28, -28, -29, -30, -30, -31, -31, -32, -33, -33, -34, -34, -35, -35, -36, -36, -37, -37, -38, -38, -39, -39, -40, -40, -41, -41, -42, -42, -43, -43, -44, -44, -45, -45, -45, -46, -46, -47, -47, -47, -48, -48, -49, -49, -49, -50, -50, -50, -51, -51, -51, -51, -52, -52, -52, -53, -53, -53, -53, -54, -54, -54, -54, -54, -55, -55, -55, -55, -55, -56, -56, -56, -56, -56, -56, -56, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -56, -56, -56, -56, -56, -56, -56, -55, -55, -55, -55, -55, -54, -54, -54, -54, -54, -53, -53, -53, -53, -52, -52, -52, -52, -51, -51, -51, -50, -50, -50, -50, -49, -49, -49, -48, -48, -48, -47, -47, -46, -46, -46, -45, -45, -45, -44, -44, -43, -43, -43, -42, -42, -41, -41, -40, -40, -40, -39, -39, -38, -38, -37, -37, -36, -36, -35, -35, -35, -34, -34, -33, -33, -32, -32, -31, -31, -30, -30, -29, -29, -28, -28, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -19, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -13, -13, -12, -12, -11, -11, -10, -9, -9, -8, -8, -7, -7, -6, -6, -5, -4, -4, -3, -3, -2, -2, -1, -1, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26, 26, 27, 27, 27, 28, 28, 28, 29, 29, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40, 40, 40, 39, 39, 39, 39, 39, 38, 38, 38, 38, 38, 37, 37, 37, 37, 37, 36, 36, 36, 36, 35, 35, 35, 35, 34, 34, 34, 34, 33, 33, 33, 33, 32, 32, 32, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 28, 28, 28, 27, 27, 27, 26, 26, 26, 25, 25, 25, 24, 24, 23, 23, 23, 22, 22, 22, 21, 21, 21, 20, 20, 19, 19, 19, 18, 18, 18, 17, 17, 16, 16, 16, 15, 15, 14, 14, 14, 13, 13, 12, 12, 12, 11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 7, 7, 6, 6, 6, 5, 5, 4, 4, 4, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, -1, -1, -2, -2, -2, -3, -3, -3, -4, -4, -5, -5, -5, -6, -6, -6, -7, -7, -7, -8, -8, -9, -9, -9, -10, -10, -10, -11, -11, -11, -12, -12, -12, -13, -13, -13, -14, -14, -14, -14, -15, -15, -15, -16, -16, -16, -17, -17, -17, -17, -18, -18, -18, -19, -19, -19, -19, -20, -20, -20, -20, -21, -21, -21, -21, -21, -22, -22, -22, -22, -23, -23, -23, -23, -23, -24, -24, -24, -24, -24, -25, -25, -25, -25, -25, -25, -26, -26, -26, -26, -26, -26, -26, -27, -27, -27, -27, -27, -27, -27, -27, -27, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -26, -26, -26, -26, -26, -26, -26, -25, -25, -25, -25, -25, -25, -25, -24, -24, -24, -24, -24, -24, -23, -23, -23, -23, -23, -22, -22, -22, -22, -22, -21, -21, -21, -21, -21, -20, -20, -20, -20, -20, -19, -19, -19, -19, -18, -18, -18, -18, -17, -17, -17, -17, -16, -16, -16, -16, -15, -15, -15, -15, -14, -14, -14, -14, -13, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -11, -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -2, -2, -2, -2, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 17, 17, 17, 17, 17, 17, 17, 17, 16, 16, 16, 16, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -4, -4, -4, -4, -4, -4, -5, -5, -5, -5, -5, -5, -5, -6, -6, -6, -6, -6, -6, -6, -7, -7, -7, -7, -7, -7, -7, -7, -8, -8, -8, -8, -8, -8, -8, -8, -9, -9, -9, -9, -9, -9, -9, -9, -9, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; static const RES_HWORD LARGE_FILTER_IMPD[] /* Impulse response differences */ = { -1, -2, -3, -5, -6, -7, -9, -10, -11, -13, -14, -15, -17, -18, -19, -21, -22, -23, -25, -26, -27, -28, -30, -32, -32, -34, -35, -36, -38, -39, -40, -42, -43, -44, -45, -47, -48, -49, -51, -52, -53, -54, -56, -56, -58, -60, -60, -62, -63, -64, -66, -66, -68, -69, -70, -72, -72, -74, -75, -76, -77, -79, -79, -81, -82, -83, -84, -85, -87, -87, -89, -90, -90, -92, -93, -94, -95, -96, -98, -98, -99, -100, -102, -102, -103, -105, -105, -106, -108, -108, -109, -110, -111, -112, -113, -114, -115, -116, -116, -118, -118, -119, -121, -120, -122, -123, -123, -125, -125, -126, -126, -128, -128, -129, -130, -130, -132, -132, -132, -134, -134, -135, -135, -136, -137, -137, -138, -139, -139, -140, -141, -141, -142, -142, -143, -143, -144, -145, -145, -145, -146, -147, -147, -147, -148, -149, -148, -150, -149, -150, -151, -151, -151, -152, -152, -152, -153, -153, -153, -153, -154, -154, -155, -155, -155, -155, -155, -156, -156, -156, -156, -156, -157, -157, -157, -157, -157, -157, -157, -158, -157, -158, -158, -157, -158, -158, -158, -158, -157, -158, -158, -158, -157, -158, -158, -157, -158, -157, -157, -157, -157, -157, -157, -157, -156, -156, -156, -156, -156, -155, -155, -155, -155, -155, -154, -154, -153, -154, -153, -153, -152, -152, -152, -151, -151, -151, -150, -150, -150, -149, -149, -148, -148, -147, -147, -147, -146, -145, -145, -145, -144, -144, -143, -142, -142, -142, -141, -140, -140, -139, -139, -138, -137, -137, -136, -136, -135, -134, -134, -133, -132, -132, -131, -130, -130, -129, -128, -128, -126, -126, -126, -125, -123, -124, -122, -122, -121, -120, -119, -119, -117, -117, -117, -115, -114, -113, -113, -112, -112, -110, -110, -108, -108, -107, -106, -106, -104, -104, -102, -102, -101, -100, -99, -98, -98, -96, -95, -95, -94, -92, -92, -91, -90, -89, -88, -87, -86, -85, -85, -83, -82, -82, -80, -80, -78, -78, -76, -76, -75, -73, -73, -72, -70, -70, -69, -68, -66, -66, -65, -64, -63, -61, -61, -60, -59, -58, -57, -56, -55, -54, -53, -51, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -41, -39, -38, -37, -36, -35, -34, -34, -32, -31, -31, -29, -28, -28, -26, -26, -24, -24, -23, -21, -21, -20, -19, -18, -17, -16, -15, -14, -13, -13, -11, -11, -9, -9, -8, -7, -6, -5, -5, -3, -2, -2, -1, 0, 1, 2, 2, 4, 4, 5, 6, 7, 8, 8, 10, 10, 11, 11, 13, 13, 14, 15, 15, 17, 17, 18, 18, 20, 20, 21, 21, 23, 23, 23, 25, 25, 26, 26, 27, 28, 28, 30, 29, 31, 31, 32, 32, 33, 34, 34, 35, 35, 36, 37, 37, 37, 39, 39, 39, 40, 40, 41, 42, 42, 42, 43, 44, 44, 44, 45, 46, 45, 47, 46, 48, 47, 48, 49, 49, 49, 49, 50, 51, 51, 51, 51, 52, 52, 53, 53, 53, 53, 54, 54, 54, 55, 55, 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 58, 57, 58, 58, 58, 58, 59, 58, 59, 59, 59, 59, 59, 59, 59, 60, 59, 60, 59, 60, 60, 60, 59, 60, 60, 60, 60, 59, 60, 60, 60, 60, 59, 60, 60, 59, 60, 59, 60, 59, 59, 59, 59, 59, 59, 59, 58, 59, 58, 58, 58, 58, 57, 58, 57, 57, 57, 57, 55, 57, 56, 56, 55, 56, 55, 55, 54, 55, 54, 54, 54, 53, 53, 53, 52, 53, 52, 51, 52, 51, 50, 51, 50, 49, 50, 49, 48, 49, 48, 47, 47, 47, 47, 46, 46, 45, 45, 45, 44, 44, 43, 43, 42, 43, 41, 42, 40, 41, 40, 39, 40, 38, 39, 37, 38, 37, 36, 36, 35, 36, 34, 34, 34, 33, 33, 32, 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24, 24, 24, 22, 23, 21, 21, 21, 20, 20, 19, 19, 18, 18, 17, 16, 16, 16, 15, 14, 14, 14, 13, 12, 12, 11, 11, 11, 9, 10, 8, 9, 7, 8, 6, 7, 5, 5, 5, 4, 4, 3, 3, 2, 1, 1, 1, 0, 0, -1, -1, -2, -3, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8, -8, -8, -9, -10, -10, -10, -11, -11, -12, -12, -13, -13, -14, -14, -14, -15, -15, -16, -16, -16, -17, -18, -17, -18, -19, -19, -19, -20, -20, -21, -20, -22, -21, -22, -23, -22, -23, -24, -23, -25, -24, -25, -25, -25, -26, -26, -27, -26, -27, -28, -27, -28, -28, -29, -29, -29, -29, -30, -30, -30, -30, -31, -31, -31, -31, -32, -32, -32, -32, -33, -33, -33, -33, -33, -34, -34, -34, -34, -34, -35, -34, -35, -35, -35, -36, -35, -36, -35, -36, -36, -36, -37, -36, -36, -37, -37, -36, -37, -37, -37, -37, -37, -38, -37, -37, -38, -37, -38, -37, -38, -37, -38, -38, -37, -38, -38, -37, -38, -38, -37, -38, -38, -37, -38, -37, -38, -37, -38, -37, -38, -37, -37, -37, -37, -37, -37, -37, -37, -35, -37, -36, -37, -36, -36, -36, -36, -36, -35, -36, -35, -36, -35, -35, -34, -35, -34, -35, -34, -34, -34, -33, -34, -33, -33, -33, -32, -33, -32, -32, -32, -32, -31, -31, -31, -31, -31, -30, -30, -30, -29, -30, -29, -29, -28, -28, -29, -27, -28, -27, -27, -27, -26, -27, -25, -26, -25, -26, -24, -25, -24, -24, -23, -24, -23, -22, -23, -22, -22, -21, -21, -21, -21, -20, -20, -19, -19, -19, -19, -18, -18, -18, -17, -17, -17, -16, -16, -15, -16, -14, -15, -14, -14, -14, -13, -13, -12, -12, -12, -12, -11, -10, -11, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -6, -6, -6, -6, -5, -5, -4, -4, -4, -3, -3, -3, -2, -2, -2, -1, -1, -1, 0, 0, 0, 1, 1, 2, 1, 3, 2, 3, 3, 4, 3, 4, 5, 5, 5, 5, 6, 6, 7, 6, 8, 7, 8, 8, 8, 9, 9, 9, 10, 9, 11, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 16, 16, 16, 16, 17, 16, 18, 17, 17, 18, 18, 18, 19, 19, 19, 19, 19, 20, 19, 20, 21, 20, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 23, 22, 23, 23, 23, 23, 24, 24, 23, 24, 24, 24, 25, 24, 25, 24, 25, 25, 25, 25, 26, 25, 26, 25, 26, 26, 25, 26, 26, 26, 27, 26, 26, 27, 26, 27, 26, 27, 26, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 26, 27, 27, 26, 26, 26, 27, 26, 26, 27, 26, 26, 26, 26, 26, 26, 25, 26, 25, 26, 25, 25, 25, 25, 25, 25, 25, 24, 24, 25, 24, 24, 24, 23, 24, 24, 23, 23, 23, 23, 23, 22, 23, 22, 22, 22, 22, 21, 22, 21, 21, 21, 21, 20, 20, 21, 20, 19, 20, 19, 19, 19, 19, 19, 18, 18, 18, 18, 18, 17, 17, 17, 17, 16, 16, 16, 16, 16, 15, 15, 15, 14, 15, 14, 14, 14, 13, 13, 13, 13, 12, 13, 12, 11, 12, 11, 11, 11, 10, 11, 10, 9, 10, 9, 9, 9, 8, 9, 8, 7, 8, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 3, 4, 3, 3, 2, 2, 2, 2, 2, 1, 1, 0, 1, 0, 0, 0, -1, -1, -1, -1, -2, -2, -2, -2, -3, -2, -4, -3, -3, -4, -4, -5, -4, -5, -5, -5, -6, -6, -6, -6, -7, -6, -7, -7, -8, -7, -8, -8, -9, -8, -9, -9, -9, -10, -9, -10, -10, -10, -11, -10, -11, -11, -12, -11, -12, -12, -12, -12, -13, -12, -13, -13, -13, -14, -13, -14, -14, -14, -14, -15, -14, -15, -15, -15, -15, -16, -15, -16, -16, -16, -16, -16, -17, -16, -17, -17, -17, -17, -17, -18, -17, -18, -18, -17, -18, -18, -19, -18, -18, -19, -18, -19, -19, -19, -19, -19, -19, -19, -19, -20, -19, -20, -19, -20, -20, -20, -19, -20, -20, -20, -20, -20, -21, -20, -20, -20, -21, -20, -20, -21, -20, -21, -20, -20, -21, -20, -21, -20, -21, -20, -21, -20, -21, -21, -20, -20, -21, -20, -21, -20, -21, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -19, -20, -20, -19, -20, -19, -19, -19, -20, -19, -19, -18, -19, -19, -19, -18, -18, -19, -18, -18, -18, -18, -18, -18, -17, -18, -17, -17, -17, -17, -17, -17, -16, -17, -16, -16, -17, -15, -16, -16, -15, -16, -15, -15, -15, -15, -15, -14, -14, -15, -14, -13, -14, -14, -13, -13, -13, -13, -13, -13, -12, -12, -12, -12, -12, -11, -12, -11, -11, -11, -10, -11, -10, -10, -10, -9, -10, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -6, -4, -5, -4, -5, -4, -4, -3, -4, -3, -3, -3, -2, -3, -2, -2, -2, -1, -2, -1, -1, -1, 0, -1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 2, 4, 3, 3, 4, 4, 4, 4, 5, 4, 5, 5, 5, 6, 5, 6, 6, 6, 6, 7, 6, 7, 7, 7, 7, 8, 7, 8, 8, 8, 8, 9, 8, 9, 9, 9, 9, 10, 9, 10, 10, 10, 10, 10, 11, 10, 11, 11, 11, 11, 11, 11, 12, 11, 12, 12, 12, 12, 12, 13, 12, 13, 13, 12, 13, 13, 14, 13, 13, 14, 13, 14, 14, 13, 14, 14, 15, 14, 14, 14, 15, 14, 15, 15, 14, 15, 15, 15, 15, 15, 16, 15, 15, 15, 16, 15, 16, 15, 16, 15, 16, 16, 16, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 15, 16, 16, 16, 16, 16, 15, 16, 16, 15, 16, 16, 15, 15, 16, 15, 16, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 15, 14, 15, 14, 14, 15, 14, 14, 14, 14, 13, 14, 14, 13, 13, 14, 13, 13, 13, 13, 13, 12, 13, 12, 13, 12, 12, 12, 12, 12, 12, 11, 12, 11, 11, 11, 11, 11, 11, 10, 11, 10, 10, 10, 10, 10, 10, 9, 10, 9, 9, 9, 9, 8, 9, 8, 9, 8, 8, 8, 7, 8, 7, 7, 7, 7, 7, 7, 6, 7, 6, 6, 6, 5, 6, 5, 5, 6, 4, 5, 5, 4, 5, 4, 4, 4, 3, 4, 3, 3, 3, 3, 3, 2, 3, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 0, 0, 1, 0, -1, 0, 0, -1, -1, -1, -1, -1, -2, -1, -2, -2, -2, -2, -2, -3, -2, -3, -3, -3, -3, -4, -3, -4, -4, -4, -4, -4, -5, -4, -5, -5, -5, -5, -5, -6, -5, -6, -6, -6, -6, -6, -6, -7, -6, -7, -7, -7, -7, -7, -7, -8, -8, -7, -8, -8, -8, -8, -9, -8, -9, -8, -9, -9, -9, -9, -9, -9, -10, -9, -10, -9, -10, -10, -10, -10, -10, -10, -11, -10, -11, -10, -11, -11, -11, -10, -11, -11, -12, -11, -11, -11, -12, -11, -12, -12, -11, -12, -12, -12, -11, -12, -12, -13, -12, -12, -12, -12, -13, -12, -12, -13, -12, -13, -12, -13, -12, -13, -13, -12, -13, -13, -13, -12, -13, -13, -13, -13, -12, -13, -13, -13, -13, -13, -13, -12, -13, -13, -13, -13, -13, -13, -13, -12, -13, -13, -13, -13, -11, -13, -13, -12, -13, -13, -12, -13, -12, -13, -12, -13, -12, -13, -12, -12, -13, -12, -12, -12, -12, -12, -12, -12, -12, -12, -11, -12, -12, -11, -12, -11, -11, -12, -11, -11, -11, -11, -11, -11, -11, -11, -10, -11, -10, -11, -10, -10, -10, -10, -10, -10, -10, -10, -10, -9, -10, -9, -9, -9, -9, -9, -9, -9, -9, -8, -9, -8, -8, -8, -8, -8, -8, -8, -8, -7, -7, -8, -7, -7, -7, -7, -6, -7, -7, -6, -6, -6, -6, -6, -6, -6, -5, -6, -5, -5, -5, -5, -5, -4, -5, -4, -5, -4, -4, -4, -4, -3, -4, -3, -4, -3, -3, -3, -3, -2, -3, -2, -2, -3, -2, -1, -2, -2, -1, -2, -1, -1, -1, -1, -1, 0, -1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 2, 1, 1, 1, 2, 2, 1, 2, 2, 3, 2, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 4, 3, 4, 4, 4, 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 8, 7, 8, 8, 8, 8, 8, 8, 9, 8, 9, 8, 9, 8, 9, 9, 8, 9, 9, 9, 9, 9, 10, 9, 9, 9, 10, 9, 10, 9, 10, 10, 9, 10, 10, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 10, 10, 10, 10, 11, 10, 10, 11, 10, 10, 11, 10, 10, 11, 10, 10, 11, 10, 11, 10, 11, 10, 10, 11, 10, 10, 11, 10, 11, 10, 10, 10, 10, 10, 10, 11, 10, 10, 10, 10, 11, 10, 10, 10, 10, 10, 10, 10, 10, 9, 10, 10, 10, 9, 10, 10, 9, 10, 9, 10, 9, 9, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 9, 9, 8, 9, 8, 8, 9, 8, 8, 8, 8, 8, 8, 7, 8, 8, 7, 8, 7, 7, 8, 7, 7, 7, 7, 6, 7, 7, 6, 7, 6, 7, 6, 6, 6, 6, 6, 6, 5, 6, 5, 6, 5, 5, 5, 5, 5, 5, 5, 4, 5, 4, 5, 4, 4, 4, 4, 4, 4, 3, 4, 3, 4, 3, 3, 3, 3, 3, 3, 2, 3, 2, 2, 3, 2, 2, 2, 1, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, -1, 0, -1, -1, 0, -1, -1, -1, -2, -1, -1, -2, -1, -2, -2, -2, -2, -2, -2, -2, -3, -2, -3, -2, -3, -3, -3, -3, -3, -3, -4, -3, -4, -3, -4, -4, -3, -4, -4, -5, -4, -4, -4, -5, -4, -5, -5, -5, -4, -5, -5, -6, -5, -5, -5, -6, -5, -6, -5, -6, -6, -6, -6, -6, -6, -6, -6, -6, -7, -6, -6, -7, -6, -7, -7, -6, -7, -7, -7, -7, -7, -7, -7, -7, -8, -7, -7, -8, -7, -8, -7, -8, -7, -8, -7, -8, -8, -8, -8, -7, -8, -8, -8, -8, -8, -8, -8, -8, -9, -8, -8, -8, -8, -9, -8, -8, -8, -9, -8, -8, -9, -8, -9, -8, -8, -9, -8, -9, -8, -9, -8, -8, -9, -8, -9, -8, -9, -8, -9, -8, -8, -9, -8, -9, -8, -7, -9, -8, -8, -9, -8, -8, -9, -8, -8, -8, -8, -8, -9, -8, -8, -8, -8, -8, -8, -8, -8, -7, -8, -8, -8, -7, -8, -8, -7, -8, -7, -8, -7, -8, -7, -7, -7, -8, -7, -7, -7, -7, -7, -7, -7, -6, -7, -7, -6, -7, -7, -6, -6, -7, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -5, -6, -5, -6, -5, -5, -6, -5, -5, -5, -5, -5, -4, -5, -5, -4, -5, -4, -5, -4, -4, -4, -4, -4, -4, -4, -4, -3, -4, -3, -4, -3, -3, -3, -4, -3, -2, -3, -3, -3, -2, -3, -2, -3, -2, -2, -2, -2, -2, -2, -2, -1, -2, -1, -2, -1, -1, -1, -2, -1, 0, -1, -1, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 2, 2, 1, 2, 2, 3, 2, 2, 2, 3, 2, 3, 3, 2, 3, 3, 3, 3, 3, 3, 4, 3, 3, 4, 3, 4, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 5, 5, 4, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 6, 5, 6, 5, 6, 6, 5, 6, 6, 6, 5, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 7, 6, 6, 7, 6, 7, 6, 6, 7, 7, 6, 7, 6, 7, 6, 7, 7, 7, 6, 7, 7, 6, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 6, 7, 7, 6, 7, 6, 7, 7, 7, 6, 7, 7, 6, 7, 7, 6, 7, 7, 6, 7, 6, 7, 6, 7, 6, 6, 7, 6, 6, 7, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 5, 6, 6, 6, 6, 5, 6, 6, 5, 6, 5, 6, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, 4, 5, 4, 5, 4, 4, 4, 5, 4, 4, 4, 4, 3, 4, 4, 4, 3, 4, 3, 4, 3, 3, 4, 3, 3, 3, 3, 3, 3, 2, 3, 3, 2, 3, 2, 3, 2, 2, 3, 2, 2, 2, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, -1, 0, -1, -1, 0, -1, -1, -1, -1, -1, -1, -2, -1, -1, -2, -1, -2, -2, -1, -2, -2, -2, -2, -2, -2, -2, -2, -3, -2, -2, -3, -2, -3, -2, -3, -3, -2, -3, -3, -3, -3, -3, -3, -3, -4, -3, -3, -4, -3, -4, -3, -4, -3, -4, -4, -3, -4, -4, -4, -4, -4, -4, -4, -4, -4, -5, -4, -4, -5, -4, -4, -5, -4, -5, -4, -5, -5, -4, -5, -5, -4, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -6, -5, -5, -5, -5, -6, -5, -5, -6, -5, -5, -6, -5, -6, -5, -6, -5, -6, -5, -5, -6, -6, -5, -6, -5, -6, -5, -6, -5, -6, -5, -6, -6, -5, -6, -5, -6, -5, -6, -5, -6, -6, -5, -6, -5, -5, -5, -6, -5, -6, -5, -6, -5, -5, -6, -5, -6, -5, -5, -6, -5, -5, -6, -5, -5, -5, -6, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -4, -5, -5, -5, -4, -5, -5, -4, -5, -4, -5, -4, -4, -5, -4, -4, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -4, -4, -3, -4, -4, -3, -4, -3, -3, -4, -3, -3, -3, -4, -3, -3, -3, -3, -3, -2, -3, -3, -3, -2, -3, -2, -3, -2, -3, -2, -2, -3, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -1, -2, -2, -1, -1, -2, -1, -1, -1, -2, -1, -1, -1, -1, 0, -1, -1, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 4, 3, 4, 4, 4, 4, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 5, 4, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 5, 4, 4, 5, 4, 5, 4, 5, 4, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 3, 4, 5, 4, 5, 4, 4, 5, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 3, 4, 3, 4, 3, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1, 0, -1, 0, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -2, -1, -2, -1, -2, -1, -2, -2, -2, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -3, -2, -2, -2, -3, -2, -3, -2, -3, -2, -3, -2, -3, -2, -3, -3, -3, -2, -3, -3, -3, -3, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -4, -3, -3, -3, -3, -4, -3, -3, -3, -4, -3, -3, -4, -3, -3, -4, -3, -3, -4, -3, -4, -3, -4, -3, -4, -3, -4, -3, -4, -3, -4, -3, -4, -3, -4, -4, -3, -4, -3, -4, -4, -3, -4, -3, -4, -4, -3, -4, -3, -4, -4, -3, -4, -3, -4, -3, -4, -4, -3, -3, -3, -4, -3, -4, -4, -3, -4, -3, -4, -3, -4, -3, -3, -4, -3, -4, -3, -4, -3, -3, -4, -3, -3, -4, -3, -3, -4, -3, -3, -3, -3, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -3, -3, -3, -2, -3, -3, -2, -3, -2, -3, -2, -3, -2, -3, -2, -3, -2, -2, -3, -2, -2, -2, -2, -2, -3, -2, -2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -1, -2, -2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -1, -2, -1, -1, -1, -1, -1, -2, -1, -1, 0, -1, -1, -1, -1, -1, -1, 0, -1, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 3, 2, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 3, 2, 3, 2, 3, 3, 2, 3, 3, 3, 2, 3, 3, 2, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 2, 2, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 2, 3, 3, 2, 3, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 2, 3, 2, 2, 3, 2, 2, 3, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, -1, 0, -1, 0, -1, -1, -1, 0, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -1, -1, -2, -1, -1, -1, -2, -1, -1, -2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -2, -2, -1, -2, -2, -1, -2, -2, -2, -1, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -3, -2, -2, -2, -2, -2, -2, -3, -2, -2, -2, -2, -3, -2, -2, -2, -3, -2, -2, -2, -2, -3, -2, -2, -2, -3, -2, -2, -2, -3, -2, -2, -3, -2, -2, -2, -3, -2, -2, -2, -3, -2, -2, -2, -3, -1, -2, -2, -2, -3, -2, -2, -2, -2, -3, -2, -2, -2, -2, -2, -3, -2, -2, -2, -2, -2, -2, -2, -2, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -1, -2, -2, -2, -1, -2, -2, -1, -2, -1, -2, -2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -1, -2, -1, -1, -2, -1, -1, -2, -1, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, 0, -1, -1, 0, -1, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 2, 1, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, -1, 0, -1, 0, -1, 0, -1, -1, 0, -1, -1, 0, -1, -1, 0, -1, -1, -1, -1, 0, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -2, -1, -1, -1, -1, -2, -1, -1, -1, -2, -1, -1, -1, -2, -1, -1, -1, -2, -1, -1, -2, -1, -1, -2, -1, -1, -1, -2, -1, -1, -2, -1, -1, -2, -1, -2, -1, -1, -2, -1, -1, -2, -1, -1, -2, -1, -1, -2, -1, -1, -2, -1, -2, -1, -1, -2, -1, -1, -2, -1, -1, -1, -1, -1, -2, -1, -1, -2, -1, -1, -1, -2, -1, -1, -2, -1, -1, -1, -2, -1, -1, -2, -1, -1, -1, -1, -2, -1, -1, -1, -2, -1, -1, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, 0, -1, 0, -1, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -1, 0, -1, 0, -1, 0, -1, -1, 0, -1, -1, 0, -1, 0, -1, -1, 0, -1, -1, 0, -1, -1, 0, -1, -1, -1, 0, -1, -1, 0, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, 0, 0, -1, -1, 0, -1, -1, -1, 0, -1, -1, -1, 0, -1, -1, 0, -1, -1, -1, 0, -1, -1, 0, -1, -1, 0, -1, -1, 0, -1, -1, 0, -1, -1, 0, -1, -1, 0, -1, -1, 0, -1, 0, -1, -1, 0, -1, 0, -1, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; ================================================ FILE: deps/pjsip/third_party/resample/src/libresample_dll.c ================================================ // libresample_dll.cpp : Defines the entry point for the DLL application. // #include BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { return TRUE; } ================================================ FILE: deps/pjsip/third_party/resample/src/resample.h ================================================ /* * The configuration constants below govern * the number of bits in the input sample and filter coefficients, the * number of bits to the right of the binary-point for fixed-point math, etc. * */ /* Conversion constants */ #define Nhc 8 #define Na 7 #define Np (Nhc+Na) #define Npc (1< at CCRMA in * 1981. It was called SRCONV and was written in SAIL for PDP-10 * compatible machines. The algorithm was first published in * * Smith, Julius O. and Phil Gossett. ``A Flexible Sampling-Rate * Conversion Method,'' Proceedings (2): 19.4.1-19.4.4, IEEE Conference * on Acoustics, Speech, and Signal Processing, San Diego, March 1984. * * An expanded tutorial based on this paper is available at the Digital * Audio Resampling Home Page given above. * * Circa 1988, the SRCONV program was translated from SAIL to C by * Christopher Lee Fraley working with Roger Dannenberg at CMU. * * Since then, the C version has been maintained by jos. * * Sndlib support was added 6/99 by John Gibson . * * The resample program is free software distributed in accordance * with the Lesser GNU Public License (LGPL). There is NO warranty; not * even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ /* PJMEDIA modification: * - remove resample(), just use SrcUp, SrcUD, and SrcLinear directly. * - move FilterUp() and FilterUD() from filterkit.c * - move stddefs.h and resample.h to this file. * - const correctness. */ #include #include "config.h" #include "stddefs.h" #include "resample.h" #ifdef _MSC_VER # pragma warning(push, 3) //# pragma warning(disable: 4245) // Conversion from uint to ushort # pragma warning(disable: 4244) // Conversion from double to uint # pragma warning(disable: 4146) // unary minus operator applied to unsigned type, result still unsigned # pragma warning(disable: 4761) // integral size mismatch in argument; conversion supplied #endif #if defined(RESAMPLE_HAS_SMALL_FILTER) && RESAMPLE_HAS_SMALL_FILTER!=0 # include "smallfilter.h" #else # define SMALL_FILTER_NMULT 0 # define SMALL_FILTER_SCALE 0 # define SMALL_FILTER_NWING 0 # define SMALL_FILTER_IMP NULL # define SMALL_FILTER_IMPD NULL #endif #if defined(RESAMPLE_HAS_LARGE_FILTER) && RESAMPLE_HAS_LARGE_FILTER!=0 # include "largefilter.h" #else # define LARGE_FILTER_NMULT 0 # define LARGE_FILTER_SCALE 0 # define LARGE_FILTER_NWING 0 # define LARGE_FILTER_IMP NULL # define LARGE_FILTER_IMPD NULL #endif #undef INLINE #define INLINE #define HAVE_FILTER 0 #ifndef NULL # define NULL 0 #endif static INLINE RES_HWORD WordToHword(RES_WORD v, int scl) { RES_HWORD out; RES_WORD llsb = (1<<(scl-1)); v += llsb; /* round */ v >>= scl; if (v>MAX_HWORD) { v = MAX_HWORD; } else if (v < MIN_HWORD) { v = MIN_HWORD; } out = (RES_HWORD) v; return out; } /* Sampling rate conversion using linear interpolation for maximum speed. */ static int SrcLinear(const RES_HWORD X[], RES_HWORD Y[], double pFactor, RES_UHWORD nx) { RES_HWORD iconst; RES_UWORD time = 0; const RES_HWORD *xp; RES_HWORD *Ystart, *Yend; RES_WORD v,x1,x2; double dt; /* Step through input signal */ RES_UWORD dtb; /* Fixed-point version of Dt */ //RES_UWORD endTime; /* When time reaches EndTime, return to user */ dt = 1.0/pFactor; /* Output sampling period */ dtb = dt*(1< 0), // so it may cause resample write pass the output buffer (Y >= Yend). // while (time < endTime) while (Y < Yend) { iconst = (time) & Pmask; xp = &X[(time)>>Np]; /* Ptr to current input sample */ x1 = *xp++; x2 = *xp; x1 *= ((1<>Na]; End = &Imp[Nwing]; if (Interp) { Hdp = &ImpD[Ph>>Na]; a = Ph & Amask; } if (Inc == 1) /* If doing right wing... */ { /* ...drop extra coeff, so when Ph is */ End--; /* 0.5, we don't do too many mult's */ if (Ph == 0) /* If the phase is zero... */ { /* ...then we've already skipped the */ Hp += Npc; /* first sample, so we must also */ Hdp += Npc; /* skip ahead in Imp[] and ImpD[] */ } } if (Interp) while (Hp < End) { t = *Hp; /* Get filter coeff */ t += (((RES_WORD)*Hdp)*a)>>Na; /* t is now interp'd filter coeff */ Hdp += Npc; /* Filter coeff differences step */ t *= *Xp; /* Mult coeff by input sample */ if (t & (1<<(Nhxn-1))) /* Round, if needed */ t += (1<<(Nhxn-1)); t >>= Nhxn; /* Leave some guard bits, but come back some */ v += t; /* The filter output */ Hp += Npc; /* Filter coeff step */ Xp += Inc; /* Input signal step. NO CHECK ON BOUNDS */ } else while (Hp < End) { t = *Hp; /* Get filter coeff */ t *= *Xp; /* Mult coeff by input sample */ if (t & (1<<(Nhxn-1))) /* Round, if needed */ t += (1<<(Nhxn-1)); t >>= Nhxn; /* Leave some guard bits, but come back some */ v += t; /* The filter output */ Hp += Npc; /* Filter coeff step */ Xp += Inc; /* Input signal step. NO CHECK ON BOUNDS */ } return(v); } static RES_WORD FilterUD(const RES_HWORD Imp[], const RES_HWORD ImpD[], RES_UHWORD Nwing, RES_BOOL Interp, const RES_HWORD *Xp, RES_HWORD Ph, RES_HWORD Inc, RES_UHWORD dhb) { RES_HWORD a; const RES_HWORD *Hp, *Hdp, *End; RES_WORD v, t; RES_UWORD Ho; v=0; Ho = (Ph*(RES_UWORD)dhb)>>Np; End = &Imp[Nwing]; if (Inc == 1) /* If doing right wing... */ { /* ...drop extra coeff, so when Ph is */ End--; /* 0.5, we don't do too many mult's */ if (Ph == 0) /* If the phase is zero... */ Ho += dhb; /* ...then we've already skipped the */ } /* first sample, so we must also */ /* skip ahead in Imp[] and ImpD[] */ if (Interp) while ((Hp = &Imp[Ho>>Na]) < End) { t = *Hp; /* Get IR sample */ Hdp = &ImpD[Ho>>Na]; /* get interp (lower Na) bits from diff table*/ a = Ho & Amask; /* a is logically between 0 and 1 */ t += (((RES_WORD)*Hdp)*a)>>Na; /* t is now interp'd filter coeff */ t *= *Xp; /* Mult coeff by input sample */ if (t & 1<<(Nhxn-1)) /* Round, if needed */ t += 1<<(Nhxn-1); t >>= Nhxn; /* Leave some guard bits, but come back some */ v += t; /* The filter output */ Ho += dhb; /* IR step */ Xp += Inc; /* Input signal step. NO CHECK ON BOUNDS */ } else while ((Hp = &Imp[Ho>>Na]) < End) { t = *Hp; /* Get IR sample */ t *= *Xp; /* Mult coeff by input sample */ if (t & 1<<(Nhxn-1)) /* Round, if needed */ t += 1<<(Nhxn-1); t >>= Nhxn; /* Leave some guard bits, but come back some */ v += t; /* The filter output */ Ho += dhb; /* IR step */ Xp += Inc; /* Input signal step. NO CHECK ON BOUNDS */ } return(v); } /* Sampling rate up-conversion only subroutine; * Slightly faster than down-conversion; */ static int SrcUp(const RES_HWORD X[], RES_HWORD Y[], double pFactor, RES_UHWORD nx, RES_UHWORD pNwing, RES_UHWORD pLpScl, const RES_HWORD pImp[], const RES_HWORD pImpD[], RES_BOOL Interp) { const RES_HWORD *xp; RES_HWORD *Ystart, *Yend; RES_WORD v; double dt; /* Step through input signal */ RES_UWORD dtb; /* Fixed-point version of Dt */ RES_UWORD time = 0; //RES_UWORD endTime; /* When time reaches EndTime, return to user */ dt = 1.0/pFactor; /* Output sampling period */ dtb = dt*(1< 0), // so it may cause resample write pass the output buffer (Y >= Yend). // while (time < endTime) while (Y < Yend) { xp = &X[time>>Np]; /* Ptr to current input sample */ /* Perform left-wing inner product */ v = 0; v = FilterUp(pImp, pImpD, pNwing, Interp, xp, (RES_HWORD)(time&Pmask),-1); /* Perform right-wing inner product */ v += FilterUp(pImp, pImpD, pNwing, Interp, xp+1, (RES_HWORD)((-time)&Pmask),1); v >>= Nhg; /* Make guard bits */ v *= pLpScl; /* Normalize for unity filter gain */ *Y++ = WordToHword(v,NLpScl); /* strip guard bits, deposit output */ time += dtb; /* Move to next sample by time increment */ } return (Y - Ystart); /* Return the number of output samples */ } /* Sampling rate conversion subroutine */ static int SrcUD(const RES_HWORD X[], RES_HWORD Y[], double pFactor, RES_UHWORD nx, RES_UHWORD pNwing, RES_UHWORD pLpScl, const RES_HWORD pImp[], const RES_HWORD pImpD[], RES_BOOL Interp) { const RES_HWORD *xp; RES_HWORD *Ystart, *Yend; RES_WORD v; double dh; /* Step through filter impulse response */ double dt; /* Step through input signal */ RES_UWORD time = 0; //RES_UWORD endTime; /* When time reaches EndTime, return to user */ RES_UWORD dhb, dtb; /* Fixed-point versions of Dh,Dt */ dt = 1.0/pFactor; /* Output sampling period */ dtb = dt*(1< 0), // so it may cause resample write pass the output buffer (Y >= Yend). // while (time < endTime) while (Y < Yend) { xp = &X[time>>Np]; /* Ptr to current input sample */ v = FilterUD(pImp, pImpD, pNwing, Interp, xp, (RES_HWORD)(time&Pmask), -1, dhb); /* Perform left-wing inner product */ v += FilterUD(pImp, pImpD, pNwing, Interp, xp+1, (RES_HWORD)((-time)&Pmask), 1, dhb); /* Perform right-wing inner product */ v >>= Nhg; /* Make guard bits */ v *= pLpScl; /* Normalize for unity filter gain */ *Y++ = WordToHword(v,NLpScl); /* strip guard bits, deposit output */ time += dtb; /* Move to next sample by time increment */ } return (Y - Ystart); /* Return the number of output samples */ } DECL(int) res_SrcLinear(const RES_HWORD X[], RES_HWORD Y[], double pFactor, RES_UHWORD nx) { return SrcLinear(X, Y, pFactor, nx); } DECL(int) res_Resample(const RES_HWORD X[], RES_HWORD Y[], double pFactor, RES_UHWORD nx, RES_BOOL LargeF, RES_BOOL Interp) { if (pFactor >= 1) { if (LargeF) return SrcUp(X, Y, pFactor, nx, LARGE_FILTER_NWING, LARGE_FILTER_SCALE, LARGE_FILTER_IMP, LARGE_FILTER_IMPD, Interp); else return SrcUp(X, Y, pFactor, nx, SMALL_FILTER_NWING, SMALL_FILTER_SCALE, SMALL_FILTER_IMP, SMALL_FILTER_IMPD, Interp); } else { if (LargeF) return SrcUD(X, Y, pFactor, nx, LARGE_FILTER_NWING, LARGE_FILTER_SCALE * pFactor + 0.5, LARGE_FILTER_IMP, LARGE_FILTER_IMPD, Interp); else return SrcUD(X, Y, pFactor, nx, SMALL_FILTER_NWING, SMALL_FILTER_SCALE * pFactor + 0.5, SMALL_FILTER_IMP, SMALL_FILTER_IMPD, Interp); } } DECL(int) res_GetXOFF(double pFactor, RES_BOOL LargeF) { if (LargeF) return (LARGE_FILTER_NMULT + 1) / 2.0 * MAX(1.0, 1.0/pFactor); else return (SMALL_FILTER_NMULT + 1) / 2.0 * MAX(1.0, 1.0/pFactor); } ================================================ FILE: deps/pjsip/third_party/resample/src/smallfilter.h ================================================ /* Included by resamplesubs.c */ #define SMALL_FILTER_NMULT ((RES_HWORD)13) #define SMALL_FILTER_SCALE 13128 /* Unity-gain scale factor */ #define SMALL_FILTER_NWING 1536 /* Filter table length */ static const RES_HWORD SMALL_FILTER_IMP[] /* Impulse response */ = { 32767, 32766, 32764, 32760, 32755, 32749, 32741, 32731, 32721, 32708, 32695, 32679, 32663, 32645, 32625, 32604, 32582, 32558, 32533, 32506, 32478, 32448, 32417, 32385, 32351, 32316, 32279, 32241, 32202, 32161, 32119, 32075, 32030, 31984, 31936, 31887, 31836, 31784, 31731, 31676, 31620, 31563, 31504, 31444, 31383, 31320, 31256, 31191, 31124, 31056, 30987, 30916, 30845, 30771, 30697, 30621, 30544, 30466, 30387, 30306, 30224, 30141, 30057, 29971, 29884, 29796, 29707, 29617, 29525, 29433, 29339, 29244, 29148, 29050, 28952, 28852, 28752, 28650, 28547, 28443, 28338, 28232, 28125, 28017, 27908, 27797, 27686, 27574, 27461, 27346, 27231, 27115, 26998, 26879, 26760, 26640, 26519, 26398, 26275, 26151, 26027, 25901, 25775, 25648, 25520, 25391, 25262, 25131, 25000, 24868, 24735, 24602, 24467, 24332, 24197, 24060, 23923, 23785, 23647, 23507, 23368, 23227, 23086, 22944, 22802, 22659, 22515, 22371, 22226, 22081, 21935, 21789, 21642, 21494, 21346, 21198, 21049, 20900, 20750, 20600, 20449, 20298, 20146, 19995, 19842, 19690, 19537, 19383, 19230, 19076, 18922, 18767, 18612, 18457, 18302, 18146, 17990, 17834, 17678, 17521, 17365, 17208, 17051, 16894, 16737, 16579, 16422, 16264, 16106, 15949, 15791, 15633, 15475, 15317, 15159, 15001, 14843, 14685, 14527, 14369, 14212, 14054, 13896, 13739, 13581, 13424, 13266, 13109, 12952, 12795, 12639, 12482, 12326, 12170, 12014, 11858, 11703, 11548, 11393, 11238, 11084, 10929, 10776, 10622, 10469, 10316, 10164, 10011, 9860, 9708, 9557, 9407, 9256, 9106, 8957, 8808, 8659, 8511, 8364, 8216, 8070, 7924, 7778, 7633, 7488, 7344, 7200, 7057, 6914, 6773, 6631, 6490, 6350, 6210, 6071, 5933, 5795, 5658, 5521, 5385, 5250, 5115, 4981, 4848, 4716, 4584, 4452, 4322, 4192, 4063, 3935, 3807, 3680, 3554, 3429, 3304, 3180, 3057, 2935, 2813, 2692, 2572, 2453, 2335, 2217, 2101, 1985, 1870, 1755, 1642, 1529, 1418, 1307, 1197, 1088, 979, 872, 765, 660, 555, 451, 348, 246, 145, 44, -54, -153, -250, -347, -443, -537, -631, -724, -816, -908, -998, -1087, -1175, -1263, -1349, -1435, -1519, -1603, -1685, -1767, -1848, -1928, -2006, -2084, -2161, -2237, -2312, -2386, -2459, -2531, -2603, -2673, -2742, -2810, -2878, -2944, -3009, -3074, -3137, -3200, -3261, -3322, -3381, -3440, -3498, -3554, -3610, -3665, -3719, -3772, -3824, -3875, -3925, -3974, -4022, -4069, -4116, -4161, -4205, -4249, -4291, -4333, -4374, -4413, -4452, -4490, -4527, -4563, -4599, -4633, -4666, -4699, -4730, -4761, -4791, -4820, -4848, -4875, -4901, -4926, -4951, -4974, -4997, -5019, -5040, -5060, -5080, -5098, -5116, -5133, -5149, -5164, -5178, -5192, -5205, -5217, -5228, -5238, -5248, -5257, -5265, -5272, -5278, -5284, -5289, -5293, -5297, -5299, -5301, -5303, -5303, -5303, -5302, -5300, -5298, -5295, -5291, -5287, -5282, -5276, -5270, -5263, -5255, -5246, -5237, -5228, -5217, -5206, -5195, -5183, -5170, -5157, -5143, -5128, -5113, -5097, -5081, -5064, -5047, -5029, -5010, -4991, -4972, -4952, -4931, -4910, -4889, -4867, -4844, -4821, -4797, -4774, -4749, -4724, -4699, -4673, -4647, -4620, -4593, -4566, -4538, -4510, -4481, -4452, -4422, -4393, -4363, -4332, -4301, -4270, -4238, -4206, -4174, -4142, -4109, -4076, -4042, -4009, -3975, -3940, -3906, -3871, -3836, -3801, -3765, -3729, -3693, -3657, -3620, -3584, -3547, -3510, -3472, -3435, -3397, -3360, -3322, -3283, -3245, -3207, -3168, -3129, -3091, -3052, -3013, -2973, -2934, -2895, -2855, -2816, -2776, -2736, -2697, -2657, -2617, -2577, -2537, -2497, -2457, -2417, -2377, -2337, -2297, -2256, -2216, -2176, -2136, -2096, -2056, -2016, -1976, -1936, -1896, -1856, -1817, -1777, -1737, -1698, -1658, -1619, -1579, -1540, -1501, -1462, -1423, -1384, -1345, -1306, -1268, -1230, -1191, -1153, -1115, -1077, -1040, -1002, -965, -927, -890, -854, -817, -780, -744, -708, -672, -636, -600, -565, -530, -494, -460, -425, -391, -356, -322, -289, -255, -222, -189, -156, -123, -91, -59, -27, 4, 35, 66, 97, 127, 158, 188, 218, 247, 277, 306, 334, 363, 391, 419, 447, 474, 501, 528, 554, 581, 606, 632, 657, 683, 707, 732, 756, 780, 803, 827, 850, 872, 895, 917, 939, 960, 981, 1002, 1023, 1043, 1063, 1082, 1102, 1121, 1139, 1158, 1176, 1194, 1211, 1228, 1245, 1262, 1278, 1294, 1309, 1325, 1340, 1354, 1369, 1383, 1397, 1410, 1423, 1436, 1448, 1461, 1473, 1484, 1496, 1507, 1517, 1528, 1538, 1548, 1557, 1566, 1575, 1584, 1592, 1600, 1608, 1616, 1623, 1630, 1636, 1643, 1649, 1654, 1660, 1665, 1670, 1675, 1679, 1683, 1687, 1690, 1694, 1697, 1700, 1702, 1704, 1706, 1708, 1709, 1711, 1712, 1712, 1713, 1713, 1713, 1713, 1712, 1711, 1710, 1709, 1708, 1706, 1704, 1702, 1700, 1697, 1694, 1691, 1688, 1685, 1681, 1677, 1673, 1669, 1664, 1660, 1655, 1650, 1644, 1639, 1633, 1627, 1621, 1615, 1609, 1602, 1596, 1589, 1582, 1575, 1567, 1560, 1552, 1544, 1536, 1528, 1520, 1511, 1503, 1494, 1485, 1476, 1467, 1458, 1448, 1439, 1429, 1419, 1409, 1399, 1389, 1379, 1368, 1358, 1347, 1337, 1326, 1315, 1304, 1293, 1282, 1271, 1260, 1248, 1237, 1225, 1213, 1202, 1190, 1178, 1166, 1154, 1142, 1130, 1118, 1106, 1094, 1081, 1069, 1057, 1044, 1032, 1019, 1007, 994, 981, 969, 956, 943, 931, 918, 905, 892, 879, 867, 854, 841, 828, 815, 802, 790, 777, 764, 751, 738, 725, 713, 700, 687, 674, 662, 649, 636, 623, 611, 598, 585, 573, 560, 548, 535, 523, 510, 498, 486, 473, 461, 449, 437, 425, 413, 401, 389, 377, 365, 353, 341, 330, 318, 307, 295, 284, 272, 261, 250, 239, 228, 217, 206, 195, 184, 173, 163, 152, 141, 131, 121, 110, 100, 90, 80, 70, 60, 51, 41, 31, 22, 12, 3, -5, -14, -23, -32, -41, -50, -59, -67, -76, -84, -93, -101, -109, -117, -125, -133, -140, -148, -156, -163, -170, -178, -185, -192, -199, -206, -212, -219, -226, -232, -239, -245, -251, -257, -263, -269, -275, -280, -286, -291, -297, -302, -307, -312, -317, -322, -327, -332, -336, -341, -345, -349, -354, -358, -362, -366, -369, -373, -377, -380, -384, -387, -390, -394, -397, -400, -402, -405, -408, -411, -413, -416, -418, -420, -422, -424, -426, -428, -430, -432, -433, -435, -436, -438, -439, -440, -442, -443, -444, -445, -445, -446, -447, -447, -448, -448, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -449, -448, -448, -447, -447, -446, -445, -444, -443, -443, -442, -441, -440, -438, -437, -436, -435, -433, -432, -430, -429, -427, -426, -424, -422, -420, -419, -417, -415, -413, -411, -409, -407, -405, -403, -400, -398, -396, -393, -391, -389, -386, -384, -381, -379, -376, -374, -371, -368, -366, -363, -360, -357, -355, -352, -349, -346, -343, -340, -337, -334, -331, -328, -325, -322, -319, -316, -313, -310, -307, -304, -301, -298, -294, -291, -288, -285, -282, -278, -275, -272, -269, -265, -262, -259, -256, -252, -249, -246, -243, -239, -236, -233, -230, -226, -223, -220, -217, -213, -210, -207, -204, -200, -197, -194, -191, -187, -184, -181, -178, -175, -172, -168, -165, -162, -159, -156, -153, -150, -147, -143, -140, -137, -134, -131, -128, -125, -122, -120, -117, -114, -111, -108, -105, -102, -99, -97, -94, -91, -88, -86, -83, -80, -78, -75, -72, -70, -67, -65, -62, -59, -57, -55, -52, -50, -47, -45, -43, -40, -38, -36, -33, -31, -29, -27, -25, -22, -20, -18, -16, -14, -12, -10, -8, -6, -4, -2, 0, 0, 2, 4, 6, 8, 9, 11, 13, 14, 16, 17, 19, 21, 22, 24, 25, 27, 28, 29, 31, 32, 33, 35, 36, 37, 38, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, 59, 60, 61, 62, 62, 63, 63, 64, 64, 65, 66, 66, 66, 67, 67, 68, 68, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 71, 71, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 67, 67, 67, 66, 66, 66, 65, 65, 64, 64, 64, 63, 63, 62, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 58, 57, 57, 56, 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 48, 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32, 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24, 24, 23, 23, 23, 22, 22, 21, 21, 20, 20, 20, 19, 19, 18, 18, 17, 17, 17, 16, 16, 15, 15, 15, 14, 14, 14, 13, 13, 12, 12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 9, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; static const RES_HWORD SMALL_FILTER_IMPD[] = { -1, -2, -4, -5, -6, -8, -10, -10, -13, -13, -16, -16, -18, -20, -21, -22, -24, -25, -27, -28, -30, -31, -32, -34, -35, -37, -38, -39, -41, -42, -44, -45, -46, -48, -49, -51, -52, -53, -55, -56, -57, -59, -60, -61, -63, -64, -65, -67, -68, -69, -71, -71, -74, -74, -76, -77, -78, -79, -81, -82, -83, -84, -86, -87, -88, -89, -90, -92, -92, -94, -95, -96, -98, -98, -100, -100, -102, -103, -104, -105, -106, -107, -108, -109, -111, -111, -112, -113, -115, -115, -116, -117, -119, -119, -120, -121, -121, -123, -124, -124, -126, -126, -127, -128, -129, -129, -131, -131, -132, -133, -133, -135, -135, -135, -137, -137, -138, -138, -140, -139, -141, -141, -142, -142, -143, -144, -144, -145, -145, -146, -146, -147, -148, -148, -148, -149, -149, -150, -150, -151, -151, -152, -151, -153, -152, -153, -154, -153, -154, -154, -155, -155, -155, -155, -156, -156, -156, -156, -157, -156, -157, -157, -157, -157, -158, -157, -158, -158, -157, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -157, -158, -158, -157, -158, -157, -158, -157, -157, -157, -156, -157, -156, -156, -156, -156, -155, -155, -155, -155, -154, -155, -153, -154, -153, -153, -152, -153, -151, -152, -151, -150, -151, -150, -149, -149, -149, -148, -147, -148, -146, -146, -146, -145, -145, -144, -144, -143, -143, -141, -142, -141, -140, -140, -139, -138, -138, -137, -137, -136, -135, -135, -134, -133, -132, -132, -132, -130, -130, -129, -128, -128, -127, -126, -125, -125, -124, -123, -122, -122, -121, -120, -119, -118, -118, -116, -116, -115, -115, -113, -113, -111, -111, -110, -109, -109, -107, -107, -105, -105, -104, -103, -102, -101, -101, -98, -99, -97, -97, -96, -94, -94, -93, -92, -92, -90, -89, -88, -88, -86, -86, -84, -84, -82, -82, -81, -80, -78, -78, -77, -76, -75, -74, -73, -72, -72, -70, -69, -68, -68, -66, -65, -65, -63, -63, -61, -61, -59, -59, -58, -56, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -47, -45, -44, -44, -42, -42, -41, -39, -39, -38, -37, -36, -36, -34, -33, -33, -31, -31, -30, -29, -28, -27, -26, -25, -25, -23, -23, -22, -21, -20, -20, -18, -18, -17, -16, -15, -14, -14, -13, -12, -11, -10, -10, -9, -8, -7, -6, -6, -5, -4, -4, -2, -2, -2, 0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 9, 9, 9, 11, 11, 11, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 19, 19, 19, 20, 21, 21, 21, 22, 23, 23, 24, 23, 25, 25, 25, 26, 26, 27, 27, 27, 28, 28, 29, 29, 30, 29, 30, 31, 31, 31, 32, 32, 32, 32, 33, 33, 34, 33, 34, 35, 34, 35, 35, 35, 36, 36, 36, 36, 37, 36, 37, 37, 38, 37, 38, 37, 38, 39, 38, 38, 39, 39, 38, 39, 39, 40, 39, 39, 40, 39, 40, 40, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 39, 40, 40, 39, 40, 39, 40, 39, 39, 39, 39, 39, 39, 39, 38, 38, 39, 38, 38, 38, 37, 38, 37, 38, 37, 36, 37, 37, 36, 36, 36, 36, 36, 35, 35, 36, 34, 35, 34, 35, 34, 33, 34, 33, 33, 33, 33, 32, 32, 32, 31, 31, 31, 31, 30, 31, 30, 30, 29, 30, 29, 28, 29, 28, 28, 28, 27, 27, 27, 26, 27, 25, 26, 25, 26, 24, 25, 24, 24, 23, 24, 23, 22, 23, 22, 22, 21, 21, 21, 21, 20, 20, 19, 20, 19, 18, 19, 18, 18, 17, 17, 17, 17, 16, 16, 15, 16, 15, 14, 15, 14, 14, 13, 13, 13, 12, 13, 12, 11, 12, 11, 10, 11, 10, 10, 9, 9, 9, 9, 8, 8, 8, 8, 7, 7, 6, 7, 6, 5, 6, 5, 5, 5, 4, 4, 4, 3, 4, 3, 3, 2, 2, 2, 2, 1, 2, 1, 0, 1, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2, -2, -2, -3, -3, -3, -3, -3, -4, -4, -4, -4, -5, -4, -5, -5, -6, -5, -6, -6, -6, -6, -6, -7, -6, -7, -7, -7, -8, -7, -8, -8, -8, -8, -8, -9, -8, -9, -9, -9, -9, -9, -10, -9, -10, -10, -10, -10, -10, -10, -11, -10, -11, -10, -11, -11, -11, -11, -11, -11, -11, -12, -11, -12, -12, -11, -12, -12, -12, -12, -12, -12, -12, -12, -12, -13, -12, -12, -13, -12, -13, -12, -13, -13, -12, -13, -13, -12, -13, -13, -13, -13, -12, -13, -13, -13, -13, -13, -12, -13, -13, -13, -13, -13, -12, -13, -13, -13, -12, -13, -13, -13, -12, -13, -13, -12, -13, -12, -13, -12, -13, -12, -12, -13, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -11, -12, -11, -12, -11, -12, -11, -11, -11, -11, -11, -11, -11, -11, -11, -10, -11, -11, -10, -10, -11, -10, -10, -10, -10, -10, -9, -10, -10, -9, -10, -9, -8, -9, -9, -9, -9, -9, -9, -8, -9, -8, -9, -8, -8, -8, -8, -8, -7, -8, -8, -7, -7, -8, -7, -7, -7, -7, -6, -7, -7, -6, -7, -6, -6, -6, -6, -6, -6, -5, -6, -5, -6, -5, -5, -5, -5, -5, -5, -5, -4, -5, -4, -4, -5, -4, -4, -4, -3, -4, -4, -3, -4, -3, -3, -4, -3, -3, -2, -3, -3, -3, -2, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -1, -2, -1, -1, -2, -1, -1, -1, 0, -1, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 3, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 2, 3, 3, 2, 3, 2, 3, 3, 2, 2, 3, 2, 3, 2, 2, 3, 2, 2, 3, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 1, 2, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, -2, -4, -5, -6, -8, -10, -10, -13, -13, -16, -16, -18, -20, -21, -22, -24, -25, -27, -28, -30, -31, -32, -34, -35, -37, -38, -39, -41, -42, -44, -45, -46, -48, -49, -51, -52, -53, -55, -56, -57, -59, -60, -61, -63, -64, -65, -67, -68, -69, -71, -71, -74, -74, -76, -77, -78, -79, -81, -82, -83, -84, -86, -87, -88, -89, -90, -92, -92, -94, -95, -96, -98, -98, -100, -100, -102, -103, -104, -105, -106, -107, -108, -109, -111, -111, -112, -113, -115, -115, -116, -117, -119, -119, -120, -121, -121, -123, -124, -124, -126, -126, -127, -128, -129, -129, -131, -131, -132, -133, -133, -135, -135, -135, -137, -137, -138, -138, -140, -139, -141, -141, -142, -142, -143, -144, -144, -145, -145, -146, -146, -147, -148, -148, -148, -149, -149, -150, -150, -151, -151, -152, -151, -153, -152, -153, -154, -153, -154, -154, -155, -155, -155, -155, -156, -156, -156, -156, -157, -156, -157, -157, -157, -157, -158, -157, -158, -158, -157, -158, -158, -158, -158, -158, -158, -158, -158, -158, -158, -157, -158, -158, -157, -158, -157, -158, -157, -157, -157, -156, -157, -156, -156, -156, -156, -155, -155, -155, -155, -154, -155, -153, -154, -153, -153, -152, -153, -151, -152, -151, -150, -151, -150, -149, -149, -149, -148, -147, -148, -146, -146, -146, -145, -145, -144, -144, -143, -143, -141, -142, -141, -140, -140, -139, -138, -138, -137, -137, -136, -135, -135, -134, -133, -132, -132, -132, -130, -130, -129, -128, -128, -127, -126, -125, -125, -124, -123, -122, -122, -121, -120, -119, -118, -118, -116, -116, -115, -115, -113, -113, -111, -111, -110, -109, -109, -107, -107, -105, -105, -104, -103, -102, -101, -101, -98, -99, -97, -97, -96, -94, -94, -93, -92, -92, -90, -89, -88, -88, -86, -86, -84, -84, -82, -82, -81, -80, -78, -78, -77, -76, -75, -74, -73, -72, -72, -70, -69, -68, -68, -66, -65, -65, -63, -63, -61, -61, -59, -59, -58, -56, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -47, -45, -44, -44, -42, -42, -41, -39, -39, -38, -37, -36, -36, -34, -33, -33, -31, -31, -30, -29, -28, -27, -26, -25, -25, -23, -23, -22, -21, -20, -20, -18, -18, -17, -16, -15, -14, -14, -13, -12, -11, -10, -10, -9, -8, -7, -6, -6, -5, -4, -4, -2, -2, -2, 0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 9, 9, 9, 11, 11, 11, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 19, 19, 19, 20, 21, 21, 21, 22, 23, 23, 24, 23, 25, 25, 25, 26, 26, 27, 27, 27, 28, 28, 29, 29, 30, 29, 30, 31, 31, 31, 32, 32, 32, 32, 33, 33, 34, 33, 34, 35, 34, 35, 35, 35, 36, 36, 36, 36, 37, 36, 37, 37, 38, 37, 38, 37, 38, 39, 38, 38, 39, 39, 38, 39, 39, 40, 39, 39, 40, 39, 40, 40, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 39, 40, 40, 39, 40, 39, 40, 39, 39, 39, 39, 39, 39, 39, 38, 38, 39, 38, 38, 38, 37, 38, 37, 38, 37, 36, 37, 37, 36, 36, 36, 36, 36, 35, 35, 36, 34, 35, 34, 35, 34, 33, 34, 33, 33, 33, 33, 32, 32, 32, 31, 31, 31, 31, 30, 31, 30, 30, 29, 30, 29, 28, 29, 28, 28, 28, 27, 27, 27, 26, 27, 25, 26, 25, 26, 24, 25, 24, 24, 23, 24, 23, 22, 23, 22, 22, 21, 21, 21, 21, 20, 20, 19, 20, 19, 18, 19, 18, 18, 17, 17, 17, 17, 16, 16, 15, 16, 15, 14, 15, 14, 14, 13, 13, 13, 12, 13, 12, 11, 12, 11, 10, 11, 10, 10, 9, 9, 9, 9, 8, 8, 8, 8, 7, 7, 6, 7, 6, 5, 6, 5, 5, 5, 4, 4, 4, 3, 4, 3, 3, 2, 2, 2, 2, 1, 2, 1, 0, 1, 0, 0, 0, -1, -1, -1, -1, -1, -2, -2, -2, -2, -3, -3, -3, -3, -3, -4, -4, -4, -4, -5, -4, -5, -5, -6, -5, -6, -6, -6, -6, -6, -7, -6, -7, -7, -7, -8, -7, -8, -8, -8, -8, -8, -9, -8, -9, -9, -9, -9, -9, -10, -9, -10, -10, -10, -10, -10, -10, -11, -10, -11, -10, -11, -11, -11, -11, -11, -11, -11, -12, -11, -12, -12, -11, -12, -12, -12, -12, -12, -12, -12, -12, -12, -13, -12, -12, -13, -12, -13, -12, -13, -13, -12, -13, -13, -12, -13, -13, -13, -13, -12, -13, -13, -13, -13, -13, -12, -13, -13, -13, -13, -13, -12, -13, -13, -13, -12, -13, -13, -13, -12, -13, -13, -12, -13, -12, -13, -12, -13, -12, -12, -13, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -11, -12, -11, -12, -11, -12, -11, -11, -11, -11, -11, -11, -11, -11, -11, -10, -11, -11, -10, -10, -11, -10, -10, -10, -10, -10, -9, -10, -10, -9, -10, -9, -8, -9, -9, -9, -9, -9, -9, -8, -9, -8, -9, -8, -8, -8, -8, -8, -7, -8, -8, -7, -7, -8, -7, -7, -7, -7, -6, -7, -7, -6, -7, -6, -6, -6, -6, -6, -6, -5, -6, -5, -6, -5, -5, -5, -5, -5, -5, -5, -4, -5, -4, -4, -5, -4, -4, -4, -3, -4, -4, -3, -4, -3, -3, -4, -3, -3, -2, -3, -3, -3, -2, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -1, -2, -1, -1, -2, -1, -1, -1, 0, -1, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 3, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 2, 3, 3, 2, 3, 2, 3, 3, 2, 2, 3, 2, 3, 2, 2, 3, 2, 2, 3, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 1, 2, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; ================================================ FILE: deps/pjsip/third_party/resample/src/stddefs.h ================================================ #ifndef PI #define PI (3.14159265358979232846) #endif #ifndef PI2 #define PI2 (6.28318530717958465692) #endif #define D2R (0.01745329348) /* (2*pi)/360 */ #define R2D (57.29577951) /* 360/(2*pi) */ #ifndef MAX #define MAX(x,y) ((x)>(y) ?(x):(y)) #endif #ifndef MIN #define MIN(x,y) ((x)<(y) ?(x):(y)) #endif #ifndef ABS #define ABS(x) ((x)<0 ?(-(x)):(x)) #endif #ifndef SGN #define SGN(x) ((x)<0 ?(-1):((x)==0?(0):(1))) #endif #define MAX_HWORD (32767) #define MIN_HWORD (-32768) ================================================ FILE: deps/pjsip/third_party/speex/AUTHORS ================================================ Jean-Marc Valin All the code except the following David Rowe lsp.c lsp.h Also ideas and feedback John Francis Edwards wave_out.[ch], some #ifdefs for windows port and MSVC project files Segher Boessenkool Misc. optimizations (for QMF in particular) Atsuhiko Yamanaka : Patch to speexenc.c to add Vorbis comment format Radim Kolar : Patch to speexenc.c for supporting more input formats ================================================ FILE: deps/pjsip/third_party/speex/COPYING ================================================ Copyright 2002-2008 Xiph.org Foundation Copyright 2002-2008 Jean-Marc Valin Copyright 2005-2007 Analog Devices Inc. Copyright 2005-2008 Commonwealth Scientific and Industrial Research Organisation (CSIRO) Copyright 1993, 2002, 2006 David Rowe Copyright 2003 EpicGames Copyright 1992-1994 Jutta Degener, Carsten Bormann Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: deps/pjsip/third_party/speex/include/speex/speex.h ================================================ /* Copyright (C) 2002-2006 Jean-Marc Valin*/ /** @file speex.h @brief Describes the different modes of the codec */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SPEEX_H #define SPEEX_H /** @defgroup Codec Speex encoder and decoder * This is the Speex codec itself. * @{ */ #include "speex/speex_bits.h" #include "speex/speex_types.h" #ifdef __cplusplus extern "C" { #endif /* Values allowed for *ctl() requests */ /** Set enhancement on/off (decoder only) */ #define SPEEX_SET_ENH 0 /** Get enhancement state (decoder only) */ #define SPEEX_GET_ENH 1 /*Would be SPEEX_SET_FRAME_SIZE, but it's (currently) invalid*/ /** Obtain frame size used by encoder/decoder */ #define SPEEX_GET_FRAME_SIZE 3 /** Set quality value */ #define SPEEX_SET_QUALITY 4 /** Get current quality setting */ /* #define SPEEX_GET_QUALITY 5 -- Doesn't make much sense, does it? */ /** Set sub-mode to use */ #define SPEEX_SET_MODE 6 /** Get current sub-mode in use */ #define SPEEX_GET_MODE 7 /** Set low-band sub-mode to use (wideband only)*/ #define SPEEX_SET_LOW_MODE 8 /** Get current low-band mode in use (wideband only)*/ #define SPEEX_GET_LOW_MODE 9 /** Set high-band sub-mode to use (wideband only)*/ #define SPEEX_SET_HIGH_MODE 10 /** Get current high-band mode in use (wideband only)*/ #define SPEEX_GET_HIGH_MODE 11 /** Set VBR on (1) or off (0) */ #define SPEEX_SET_VBR 12 /** Get VBR status (1 for on, 0 for off) */ #define SPEEX_GET_VBR 13 /** Set quality value for VBR encoding (0-10) */ #define SPEEX_SET_VBR_QUALITY 14 /** Get current quality value for VBR encoding (0-10) */ #define SPEEX_GET_VBR_QUALITY 15 /** Set complexity of the encoder (0-10) */ #define SPEEX_SET_COMPLEXITY 16 /** Get current complexity of the encoder (0-10) */ #define SPEEX_GET_COMPLEXITY 17 /** Set bit-rate used by the encoder (or lower) */ #define SPEEX_SET_BITRATE 18 /** Get current bit-rate used by the encoder or decoder */ #define SPEEX_GET_BITRATE 19 /** Define a handler function for in-band Speex request*/ #define SPEEX_SET_HANDLER 20 /** Define a handler function for in-band user-defined request*/ #define SPEEX_SET_USER_HANDLER 22 /** Set sampling rate used in bit-rate computation */ #define SPEEX_SET_SAMPLING_RATE 24 /** Get sampling rate used in bit-rate computation */ #define SPEEX_GET_SAMPLING_RATE 25 /** Reset the encoder/decoder memories to zero*/ #define SPEEX_RESET_STATE 26 /** Get VBR info (mostly used internally) */ #define SPEEX_GET_RELATIVE_QUALITY 29 /** Set VAD status (1 for on, 0 for off) */ #define SPEEX_SET_VAD 30 /** Get VAD status (1 for on, 0 for off) */ #define SPEEX_GET_VAD 31 /** Set Average Bit-Rate (ABR) to n bits per seconds */ #define SPEEX_SET_ABR 32 /** Get Average Bit-Rate (ABR) setting (in bps) */ #define SPEEX_GET_ABR 33 /** Set DTX status (1 for on, 0 for off) */ #define SPEEX_SET_DTX 34 /** Get DTX status (1 for on, 0 for off) */ #define SPEEX_GET_DTX 35 /** Set submode encoding in each frame (1 for yes, 0 for no, setting to no breaks the standard) */ #define SPEEX_SET_SUBMODE_ENCODING 36 /** Get submode encoding in each frame */ #define SPEEX_GET_SUBMODE_ENCODING 37 /*#define SPEEX_SET_LOOKAHEAD 38*/ /** Returns the lookahead used by Speex */ #define SPEEX_GET_LOOKAHEAD 39 /** Sets tuning for packet-loss concealment (expected loss rate) */ #define SPEEX_SET_PLC_TUNING 40 /** Gets tuning for PLC */ #define SPEEX_GET_PLC_TUNING 41 /** Sets the max bit-rate allowed in VBR mode */ #define SPEEX_SET_VBR_MAX_BITRATE 42 /** Gets the max bit-rate allowed in VBR mode */ #define SPEEX_GET_VBR_MAX_BITRATE 43 /** Turn on/off input/output high-pass filtering */ #define SPEEX_SET_HIGHPASS 44 /** Get status of input/output high-pass filtering */ #define SPEEX_GET_HIGHPASS 45 /** Get "activity level" of the last decoded frame, i.e. how much damage we cause if we remove the frame */ #define SPEEX_GET_ACTIVITY 47 /* Preserving compatibility:*/ /** Equivalent to SPEEX_SET_ENH */ #define SPEEX_SET_PF 0 /** Equivalent to SPEEX_GET_ENH */ #define SPEEX_GET_PF 1 /* Values allowed for mode queries */ /** Query the frame size of a mode */ #define SPEEX_MODE_FRAME_SIZE 0 /** Query the size of an encoded frame for a particular sub-mode */ #define SPEEX_SUBMODE_BITS_PER_FRAME 1 /** Get major Speex version */ #define SPEEX_LIB_GET_MAJOR_VERSION 1 /** Get minor Speex version */ #define SPEEX_LIB_GET_MINOR_VERSION 3 /** Get micro Speex version */ #define SPEEX_LIB_GET_MICRO_VERSION 5 /** Get extra Speex version */ #define SPEEX_LIB_GET_EXTRA_VERSION 7 /** Get Speex version string */ #define SPEEX_LIB_GET_VERSION_STRING 9 /*#define SPEEX_LIB_SET_ALLOC_FUNC 10 #define SPEEX_LIB_GET_ALLOC_FUNC 11 #define SPEEX_LIB_SET_FREE_FUNC 12 #define SPEEX_LIB_GET_FREE_FUNC 13 #define SPEEX_LIB_SET_WARNING_FUNC 14 #define SPEEX_LIB_GET_WARNING_FUNC 15 #define SPEEX_LIB_SET_ERROR_FUNC 16 #define SPEEX_LIB_GET_ERROR_FUNC 17 */ /** Number of defined modes in Speex */ #define SPEEX_NB_MODES 3 /** modeID for the defined narrowband mode */ #define SPEEX_MODEID_NB 0 /** modeID for the defined wideband mode */ #define SPEEX_MODEID_WB 1 /** modeID for the defined ultra-wideband mode */ #define SPEEX_MODEID_UWB 2 struct SpeexMode; /* Prototypes for mode function pointers */ /** Encoder state initialization function */ typedef void *(*encoder_init_func)(const struct SpeexMode *mode); /** Encoder state destruction function */ typedef void (*encoder_destroy_func)(void *st); /** Main encoding function */ typedef int (*encode_func)(void *state, void *in, SpeexBits *bits); /** Function for controlling the encoder options */ typedef int (*encoder_ctl_func)(void *state, int request, void *ptr); /** Decoder state initialization function */ typedef void *(*decoder_init_func)(const struct SpeexMode *mode); /** Decoder state destruction function */ typedef void (*decoder_destroy_func)(void *st); /** Main decoding function */ typedef int (*decode_func)(void *state, SpeexBits *bits, void *out); /** Function for controlling the decoder options */ typedef int (*decoder_ctl_func)(void *state, int request, void *ptr); /** Query function for a mode */ typedef int (*mode_query_func)(const void *mode, int request, void *ptr); /** Struct defining a Speex mode */ typedef struct SpeexMode { /** Pointer to the low-level mode data */ const void *mode; /** Pointer to the mode query function */ mode_query_func query; /** The name of the mode (you should not rely on this to identify the mode)*/ const char *modeName; /**ID of the mode*/ int modeID; /**Version number of the bitstream (incremented every time we break bitstream compatibility*/ int bitstream_version; /** Pointer to encoder initialization function */ encoder_init_func enc_init; /** Pointer to encoder destruction function */ encoder_destroy_func enc_destroy; /** Pointer to frame encoding function */ encode_func enc; /** Pointer to decoder initialization function */ decoder_init_func dec_init; /** Pointer to decoder destruction function */ decoder_destroy_func dec_destroy; /** Pointer to frame decoding function */ decode_func dec; /** ioctl-like requests for encoder */ encoder_ctl_func enc_ctl; /** ioctl-like requests for decoder */ decoder_ctl_func dec_ctl; } SpeexMode; /** * Returns a handle to a newly created Speex encoder state structure. For now, * the "mode" argument can be &nb_mode or &wb_mode . In the future, more modes * may be added. Note that for now if you have more than one channels to * encode, you need one state per channel. * * @param mode The mode to use (either speex_nb_mode or speex_wb.mode) * @return A newly created encoder state or NULL if state allocation fails */ void *speex_encoder_init(const SpeexMode *mode); /** Frees all resources associated to an existing Speex encoder state. * @param state Encoder state to be destroyed */ void speex_encoder_destroy(void *state); /** Uses an existing encoder state to encode one frame of speech pointed to by "in". The encoded bit-stream is saved in "bits". @param state Encoder state @param in Frame that will be encoded with a +-2^15 range. This data MAY be overwritten by the encoder and should be considered uninitialised after the call. @param bits Bit-stream where the data will be written @return 0 if frame needs not be transmitted (DTX only), 1 otherwise */ int speex_encode(void *state, float *in, SpeexBits *bits); /** Uses an existing encoder state to encode one frame of speech pointed to by "in". The encoded bit-stream is saved in "bits". @param state Encoder state @param in Frame that will be encoded with a +-2^15 range @param bits Bit-stream where the data will be written @return 0 if frame needs not be transmitted (DTX only), 1 otherwise */ int speex_encode_int(void *state, spx_int16_t *in, SpeexBits *bits); /** Used like the ioctl function to control the encoder parameters * * @param state Encoder state * @param request ioctl-type request (one of the SPEEX_* macros) * @param ptr Data exchanged to-from function * @return 0 if no error, -1 if request in unknown, -2 for invalid parameter */ int speex_encoder_ctl(void *state, int request, void *ptr); /** Returns a handle to a newly created decoder state structure. For now, * the mode argument can be &nb_mode or &wb_mode . In the future, more modes * may be added. Note that for now if you have more than one channels to * decode, you need one state per channel. * * @param mode Speex mode (one of speex_nb_mode or speex_wb_mode) * @return A newly created decoder state or NULL if state allocation fails */ void *speex_decoder_init(const SpeexMode *mode); /** Frees all resources associated to an existing decoder state. * * @param state State to be destroyed */ void speex_decoder_destroy(void *state); /** Uses an existing decoder state to decode one frame of speech from * bit-stream bits. The output speech is saved written to out. * * @param state Decoder state * @param bits Bit-stream from which to decode the frame (NULL if the packet was lost) * @param out Where to write the decoded frame * @return return status (0 for no error, -1 for end of stream, -2 corrupt stream) */ int speex_decode(void *state, SpeexBits *bits, float *out); /** Uses an existing decoder state to decode one frame of speech from * bit-stream bits. The output speech is saved written to out. * * @param state Decoder state * @param bits Bit-stream from which to decode the frame (NULL if the packet was lost) * @param out Where to write the decoded frame * @return return status (0 for no error, -1 for end of stream, -2 corrupt stream) */ int speex_decode_int(void *state, SpeexBits *bits, spx_int16_t *out); /** Used like the ioctl function to control the encoder parameters * * @param state Decoder state * @param request ioctl-type request (one of the SPEEX_* macros) * @param ptr Data exchanged to-from function * @return 0 if no error, -1 if request in unknown, -2 for invalid parameter */ int speex_decoder_ctl(void *state, int request, void *ptr); /** Query function for mode information * * @param mode Speex mode * @param request ioctl-type request (one of the SPEEX_* macros) * @param ptr Data exchanged to-from function * @return 0 if no error, -1 if request in unknown, -2 for invalid parameter */ int speex_mode_query(const SpeexMode *mode, int request, void *ptr); /** Functions for controlling the behavior of libspeex * @param request ioctl-type request (one of the SPEEX_LIB_* macros) * @param ptr Data exchanged to-from function * @return 0 if no error, -1 if request in unknown, -2 for invalid parameter */ int speex_lib_ctl(int request, void *ptr); /** Default narrowband mode */ extern const SpeexMode speex_nb_mode; /** Default wideband mode */ extern const SpeexMode speex_wb_mode; /** Default "ultra-wideband" mode */ extern const SpeexMode speex_uwb_mode; /** List of all modes available */ extern const SpeexMode * const speex_mode_list[SPEEX_NB_MODES]; /** Obtain one of the modes available */ const SpeexMode * speex_lib_get_mode (int mode); #if (!defined(WIN32) && !defined(WIN64)) /* We actually override the function in the narrowband case so that we can avoid linking in the wideband stuff */ #define speex_lib_get_mode(mode) ((mode)==SPEEX_MODEID_NB ? &speex_nb_mode : speex_lib_get_mode (mode)) #endif #ifdef __cplusplus } #endif /** @}*/ #endif ================================================ FILE: deps/pjsip/third_party/speex/include/speex/speex_bits.h ================================================ /* Copyright (C) 2002 Jean-Marc Valin */ /** @file speex_bits.h @brief Handles bit packing/unpacking */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef BITS_H #define BITS_H /** @defgroup SpeexBits SpeexBits: Bit-stream manipulations * This is the structure that holds the bit-stream when encoding or decoding * with Speex. It allows some manipulations as well. * @{ */ #ifdef __cplusplus extern "C" { #endif /** Bit-packing data structure representing (part of) a bit-stream. */ typedef struct SpeexBits { char *chars; /**< "raw" data */ int nbBits; /**< Total number of bits stored in the stream*/ int charPtr; /**< Position of the byte "cursor" */ int bitPtr; /**< Position of the bit "cursor" within the current char */ int owner; /**< Does the struct "own" the "raw" buffer (member "chars") */ int overflow;/**< Set to one if we try to read past the valid data */ int buf_size;/**< Allocated size for buffer */ int reserved1; /**< Reserved for future use */ void *reserved2; /**< Reserved for future use */ } SpeexBits; /** Initializes and allocates resources for a SpeexBits struct */ void speex_bits_init(SpeexBits *bits); /** Initializes SpeexBits struct using a pre-allocated buffer*/ void speex_bits_init_buffer(SpeexBits *bits, void *buff, int buf_size); /** Sets the bits in a SpeexBits struct to use data from an existing buffer (for decoding without copying data) */ void speex_bits_set_bit_buffer(SpeexBits *bits, void *buff, int buf_size); /** Frees all resources associated to a SpeexBits struct. Right now this does nothing since no resources are allocated, but this could change in the future.*/ void speex_bits_destroy(SpeexBits *bits); /** Resets bits to initial value (just after initialization, erasing content)*/ void speex_bits_reset(SpeexBits *bits); /** Rewind the bit-stream to the beginning (ready for read) without erasing the content */ void speex_bits_rewind(SpeexBits *bits); /** Initializes the bit-stream from the data in an area of memory */ void speex_bits_read_from(SpeexBits *bits, char *bytes, int len); /** Append bytes to the bit-stream * * @param bits Bit-stream to operate on * @param bytes pointer to the bytes what will be appended * @param len Number of bytes of append */ void speex_bits_read_whole_bytes(SpeexBits *bits, char *bytes, int len); /** Write the content of a bit-stream to an area of memory * * @param bits Bit-stream to operate on * @param bytes Memory location where to write the bits * @param max_len Maximum number of bytes to write (i.e. size of the "bytes" buffer) * @return Number of bytes written to the "bytes" buffer */ int speex_bits_write(SpeexBits *bits, char *bytes, int max_len); /** Like speex_bits_write, but writes only the complete bytes in the stream. Also removes the written bytes from the stream */ int speex_bits_write_whole_bytes(SpeexBits *bits, char *bytes, int max_len); /** Append bits to the bit-stream * @param bits Bit-stream to operate on * @param data Value to append as integer * @param nbBits number of bits to consider in "data" */ void speex_bits_pack(SpeexBits *bits, int data, int nbBits); /** Interpret the next bits in the bit-stream as a signed integer * * @param bits Bit-stream to operate on * @param nbBits Number of bits to interpret * @return A signed integer represented by the bits read */ int speex_bits_unpack_signed(SpeexBits *bits, int nbBits); /** Interpret the next bits in the bit-stream as an unsigned integer * * @param bits Bit-stream to operate on * @param nbBits Number of bits to interpret * @return An unsigned integer represented by the bits read */ unsigned int speex_bits_unpack_unsigned(SpeexBits *bits, int nbBits); /** Returns the number of bytes in the bit-stream, including the last one even if it is not "full" * * @param bits Bit-stream to operate on * @return Number of bytes in the stream */ int speex_bits_nbytes(SpeexBits *bits); /** Same as speex_bits_unpack_unsigned, but without modifying the cursor position * * @param bits Bit-stream to operate on * @param nbBits Number of bits to look for * @return Value of the bits peeked, interpreted as unsigned */ unsigned int speex_bits_peek_unsigned(SpeexBits *bits, int nbBits); /** Get the value of the next bit in the stream, without modifying the * "cursor" position * * @param bits Bit-stream to operate on * @return Value of the bit peeked (one bit only) */ int speex_bits_peek(SpeexBits *bits); /** Advances the position of the "bit cursor" in the stream * * @param bits Bit-stream to operate on * @param n Number of bits to advance */ void speex_bits_advance(SpeexBits *bits, int n); /** Returns the number of bits remaining to be read in a stream * * @param bits Bit-stream to operate on * @return Number of bits that can still be read from the stream */ int speex_bits_remaining(SpeexBits *bits); /** Insert a terminator so that the data can be sent as a packet while auto-detecting * the number of frames in each packet * * @param bits Bit-stream to operate on */ void speex_bits_insert_terminator(SpeexBits *bits); #ifdef __cplusplus } #endif /* @} */ #endif ================================================ FILE: deps/pjsip/third_party/speex/include/speex/speex_buffer.h ================================================ /* Copyright (C) 2007 Jean-Marc Valin File: speex_buffer.h This is a very simple ring buffer implementation. It is not thread-safe so you need to do your own locking. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SPEEX_BUFFER_H #define SPEEX_BUFFER_H #include "speex/speex_types.h" #ifdef __cplusplus extern "C" { #endif struct SpeexBuffer_; typedef struct SpeexBuffer_ SpeexBuffer; SpeexBuffer *speex_buffer_init(int size); void speex_buffer_destroy(SpeexBuffer *st); int speex_buffer_write(SpeexBuffer *st, void *data, int len); int speex_buffer_writezeros(SpeexBuffer *st, int len); int speex_buffer_read(SpeexBuffer *st, void *data, int len); int speex_buffer_get_available(SpeexBuffer *st); int speex_buffer_resize(SpeexBuffer *st, int len); #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/speex/include/speex/speex_callbacks.h ================================================ /* Copyright (C) 2002 Jean-Marc Valin*/ /** @file speex_callbacks.h @brief Describes callback handling and in-band signalling */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SPEEX_CALLBACKS_H #define SPEEX_CALLBACKS_H /** @defgroup SpeexCallbacks Various definitions for Speex callbacks supported by the decoder. * @{ */ #include "speex.h" #ifdef __cplusplus extern "C" { #endif /** Total number of callbacks */ #define SPEEX_MAX_CALLBACKS 16 /* Describes all the in-band requests */ /*These are 1-bit requests*/ /** Request for perceptual enhancement (1 for on, 0 for off) */ #define SPEEX_INBAND_ENH_REQUEST 0 /** Reserved */ #define SPEEX_INBAND_RESERVED1 1 /*These are 4-bit requests*/ /** Request for a mode change */ #define SPEEX_INBAND_MODE_REQUEST 2 /** Request for a low mode change */ #define SPEEX_INBAND_LOW_MODE_REQUEST 3 /** Request for a high mode change */ #define SPEEX_INBAND_HIGH_MODE_REQUEST 4 /** Request for VBR (1 on, 0 off) */ #define SPEEX_INBAND_VBR_QUALITY_REQUEST 5 /** Request to be sent acknowledge */ #define SPEEX_INBAND_ACKNOWLEDGE_REQUEST 6 /** Request for VBR (1 for on, 0 for off) */ #define SPEEX_INBAND_VBR_REQUEST 7 /*These are 8-bit requests*/ /** Send a character in-band */ #define SPEEX_INBAND_CHAR 8 /** Intensity stereo information */ #define SPEEX_INBAND_STEREO 9 /*These are 16-bit requests*/ /** Transmit max bit-rate allowed */ #define SPEEX_INBAND_MAX_BITRATE 10 /*These are 32-bit requests*/ /** Acknowledge packet reception */ #define SPEEX_INBAND_ACKNOWLEDGE 12 /** Callback function type */ typedef int (*speex_callback_func)(SpeexBits *bits, void *state, void *data); /** Callback information */ typedef struct SpeexCallback { int callback_id; /**< ID associated to the callback */ speex_callback_func func; /**< Callback handler function */ void *data; /**< Data that will be sent to the handler */ void *reserved1; /**< Reserved for future use */ int reserved2; /**< Reserved for future use */ } SpeexCallback; /** Handle in-band request */ int speex_inband_handler(SpeexBits *bits, SpeexCallback *callback_list, void *state); /** Standard handler for mode request (change mode, no questions asked) */ int speex_std_mode_request_handler(SpeexBits *bits, void *state, void *data); /** Standard handler for high mode request (change high mode, no questions asked) */ int speex_std_high_mode_request_handler(SpeexBits *bits, void *state, void *data); /** Standard handler for in-band characters (write to stderr) */ int speex_std_char_handler(SpeexBits *bits, void *state, void *data); /** Default handler for user-defined requests: in this case, just ignore */ int speex_default_user_handler(SpeexBits *bits, void *state, void *data); /** Standard handler for low mode request (change low mode, no questions asked) */ int speex_std_low_mode_request_handler(SpeexBits *bits, void *state, void *data); /** Standard handler for VBR request (Set VBR, no questions asked) */ int speex_std_vbr_request_handler(SpeexBits *bits, void *state, void *data); /** Standard handler for enhancer request (Turn enhancer on/off, no questions asked) */ int speex_std_enh_request_handler(SpeexBits *bits, void *state, void *data); /** Standard handler for VBR quality request (Set VBR quality, no questions asked) */ int speex_std_vbr_quality_request_handler(SpeexBits *bits, void *state, void *data); #ifdef __cplusplus } #endif /** @} */ #endif ================================================ FILE: deps/pjsip/third_party/speex/include/speex/speex_config_types.h.in ================================================ #ifndef __SPEEX_TYPES_H__ #define __SPEEX_TYPES_H__ /* these are filled in by configure */ typedef @SIZE16@ spx_int16_t; typedef unsigned @SIZE16@ spx_uint16_t; typedef @SIZE32@ spx_int32_t; typedef unsigned @SIZE32@ spx_uint32_t; #endif ================================================ FILE: deps/pjsip/third_party/speex/include/speex/speex_echo.h ================================================ /* Copyright (C) Jean-Marc Valin */ /** @file speex_echo.h @brief Echo cancellation */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SPEEX_ECHO_H #define SPEEX_ECHO_H /** @defgroup SpeexEchoState SpeexEchoState: Acoustic echo canceller * This is the acoustic echo canceller module. * @{ */ #include "speex/speex_types.h" #ifdef __cplusplus extern "C" { #endif /** Obtain frame size used by the AEC */ #define SPEEX_ECHO_GET_FRAME_SIZE 3 /** Set sampling rate */ #define SPEEX_ECHO_SET_SAMPLING_RATE 24 /** Get sampling rate */ #define SPEEX_ECHO_GET_SAMPLING_RATE 25 /* Can't set window sizes */ /** Get size of impulse response (int32) */ #define SPEEX_ECHO_GET_IMPULSE_RESPONSE_SIZE 27 /* Can't set window content */ /** Get impulse response (int32[]) */ #define SPEEX_ECHO_GET_IMPULSE_RESPONSE 29 /** Internal echo canceller state. Should never be accessed directly. */ struct SpeexEchoState_; /** @class SpeexEchoState * This holds the state of the echo canceller. You need one per channel. */ /** Internal echo canceller state. Should never be accessed directly. */ typedef struct SpeexEchoState_ SpeexEchoState; /** Creates a new echo canceller state * @param frame_size Number of samples to process at one time (should correspond to 10-20 ms) * @param filter_length Number of samples of echo to cancel (should generally correspond to 100-500 ms) * @return Newly-created echo canceller state */ SpeexEchoState *speex_echo_state_init(int frame_size, int filter_length); /** Creates a new multi-channel echo canceller state * @param frame_size Number of samples to process at one time (should correspond to 10-20 ms) * @param filter_length Number of samples of echo to cancel (should generally correspond to 100-500 ms) * @param nb_mic Number of microphone channels * @param nb_speakers Number of speaker channels * @return Newly-created echo canceller state */ SpeexEchoState *speex_echo_state_init_mc(int frame_size, int filter_length, int nb_mic, int nb_speakers); /** Destroys an echo canceller state * @param st Echo canceller state */ void speex_echo_state_destroy(SpeexEchoState *st); /** Performs echo cancellation a frame, based on the audio sent to the speaker (no delay is added * to playback in this form) * * @param st Echo canceller state * @param rec Signal from the microphone (near end + far end echo) * @param play Signal played to the speaker (received from far end) * @param out Returns near-end signal with echo removed */ void speex_echo_cancellation(SpeexEchoState *st, const spx_int16_t *rec, const spx_int16_t *play, spx_int16_t *out); /** Performs echo cancellation a frame (deprecated) */ void speex_echo_cancel(SpeexEchoState *st, const spx_int16_t *rec, const spx_int16_t *play, spx_int16_t *out, spx_int32_t *Yout); /** Perform echo cancellation using internal playback buffer, which is delayed by two frames * to account for the delay introduced by most soundcards (but it could be off!) * @param st Echo canceller state * @param rec Signal from the microphone (near end + far end echo) * @param out Returns near-end signal with echo removed */ void speex_echo_capture(SpeexEchoState *st, const spx_int16_t *rec, spx_int16_t *out); /** Let the echo canceller know that a frame was just queued to the soundcard * @param st Echo canceller state * @param play Signal played to the speaker (received from far end) */ void speex_echo_playback(SpeexEchoState *st, const spx_int16_t *play); /** Reset the echo canceller to its original state * @param st Echo canceller state */ void speex_echo_state_reset(SpeexEchoState *st); /** Used like the ioctl function to control the echo canceller parameters * * @param st Echo canceller state * @param request ioctl-type request (one of the SPEEX_ECHO_* macros) * @param ptr Data exchanged to-from function * @return 0 if no error, -1 if request in unknown */ int speex_echo_ctl(SpeexEchoState *st, int request, void *ptr); struct SpeexDecorrState_; typedef struct SpeexDecorrState_ SpeexDecorrState; /** Create a state for the channel decorrelation algorithm This is useful for multi-channel echo cancellation only * @param rate Sampling rate * @param channels Number of channels (it's a bit pointless if you don't have at least 2) * @param frame_size Size of the frame to process at ones (counting samples *per* channel) */ SpeexDecorrState *speex_decorrelate_new(int rate, int channels, int frame_size); /** Remove correlation between the channels by modifying the phase and possibly adding noise in a way that is not (or little) perceptible. * @param st Decorrelator state * @param in Input audio in interleaved format * @param out Result of the decorrelation (out *may* alias in) * @param strength How much alteration of the audio to apply from 0 to 100. */ void speex_decorrelate(SpeexDecorrState *st, const spx_int16_t *in, spx_int16_t *out, int strength); /** Destroy a Decorrelation state * @param st State to destroy */ void speex_decorrelate_destroy(SpeexDecorrState *st); #ifdef __cplusplus } #endif /** @}*/ #endif ================================================ FILE: deps/pjsip/third_party/speex/include/speex/speex_header.h ================================================ /* Copyright (C) 2002 Jean-Marc Valin */ /** @file speex_header.h @brief Describes the Speex header */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SPEEX_HEADER_H #define SPEEX_HEADER_H /** @defgroup SpeexHeader SpeexHeader: Makes it easy to write/parse an Ogg/Speex header * This is the Speex header for the Ogg encapsulation. You don't need that if you just use RTP. * @{ */ #include "speex/speex_types.h" #ifdef __cplusplus extern "C" { #endif struct SpeexMode; /** Length of the Speex header identifier */ #define SPEEX_HEADER_STRING_LENGTH 8 /** Maximum number of characters for encoding the Speex version number in the header */ #define SPEEX_HEADER_VERSION_LENGTH 20 /** Speex header info for file-based formats */ typedef struct SpeexHeader { char speex_string[SPEEX_HEADER_STRING_LENGTH]; /**< Identifies a Speex bit-stream, always set to "Speex " */ char speex_version[SPEEX_HEADER_VERSION_LENGTH]; /**< Speex version */ spx_int32_t speex_version_id; /**< Version for Speex (for checking compatibility) */ spx_int32_t header_size; /**< Total size of the header ( sizeof(SpeexHeader) ) */ spx_int32_t rate; /**< Sampling rate used */ spx_int32_t mode; /**< Mode used (0 for narrowband, 1 for wideband) */ spx_int32_t mode_bitstream_version; /**< Version ID of the bit-stream */ spx_int32_t nb_channels; /**< Number of channels encoded */ spx_int32_t bitrate; /**< Bit-rate used */ spx_int32_t frame_size; /**< Size of frames */ spx_int32_t vbr; /**< 1 for a VBR encoding, 0 otherwise */ spx_int32_t frames_per_packet; /**< Number of frames stored per Ogg packet */ spx_int32_t extra_headers; /**< Number of additional headers after the comments */ spx_int32_t reserved1; /**< Reserved for future use, must be zero */ spx_int32_t reserved2; /**< Reserved for future use, must be zero */ } SpeexHeader; /** Initializes a SpeexHeader using basic information */ void speex_init_header(SpeexHeader *header, int rate, int nb_channels, const struct SpeexMode *m); /** Creates the header packet from the header itself (mostly involves endianness conversion) */ char *speex_header_to_packet(SpeexHeader *header, int *size); /** Creates a SpeexHeader from a packet */ SpeexHeader *speex_packet_to_header(char *packet, int size); /** Frees the memory allocated by either speex_header_to_packet() or speex_packet_to_header() */ void speex_header_free(void *ptr); #ifdef __cplusplus } #endif /** @} */ #endif ================================================ FILE: deps/pjsip/third_party/speex/include/speex/speex_jitter.h ================================================ /* Copyright (C) 2002 Jean-Marc Valin */ /** @file speex_jitter.h @brief Adaptive jitter buffer for Speex */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SPEEX_JITTER_H #define SPEEX_JITTER_H /** @defgroup JitterBuffer JitterBuffer: Adaptive jitter buffer * This is the jitter buffer that reorders UDP/RTP packets and adjusts the buffer size * to maintain good quality and low latency. * @{ */ #include "speex/speex_types.h" #ifdef __cplusplus extern "C" { #endif /** Generic adaptive jitter buffer state */ struct JitterBuffer_; /** Generic adaptive jitter buffer state */ typedef struct JitterBuffer_ JitterBuffer; /** Definition of an incoming packet */ typedef struct _JitterBufferPacket JitterBufferPacket; /** Definition of an incoming packet */ struct _JitterBufferPacket { char *data; /**< Data bytes contained in the packet */ spx_uint32_t len; /**< Length of the packet in bytes */ spx_uint32_t timestamp; /**< Timestamp for the packet */ spx_uint32_t span; /**< Time covered by the packet (same units as timestamp) */ spx_uint16_t sequence; /**< RTP Sequence number if available (0 otherwise) */ spx_uint32_t user_data; /**< Put whatever data you like here (it's ignored by the jitter buffer) */ }; /** Packet has been retrieved */ #define JITTER_BUFFER_OK 0 /** Packet is lost or is late */ #define JITTER_BUFFER_MISSING 1 /** A "fake" packet is meant to be inserted here to increase buffering */ #define JITTER_BUFFER_INSERTION 2 /** There was an error in the jitter buffer */ #define JITTER_BUFFER_INTERNAL_ERROR -1 /** Invalid argument */ #define JITTER_BUFFER_BAD_ARGUMENT -2 /** Set minimum amount of extra buffering required (margin) */ #define JITTER_BUFFER_SET_MARGIN 0 /** Get minimum amount of extra buffering required (margin) */ #define JITTER_BUFFER_GET_MARGIN 1 /* JITTER_BUFFER_SET_AVAILABLE_COUNT wouldn't make sense */ /** Get the amount of available packets currently buffered */ #define JITTER_BUFFER_GET_AVAILABLE_COUNT 3 /** Included because of an early misspelling (will remove in next release) */ #define JITTER_BUFFER_GET_AVALIABLE_COUNT 3 /** Assign a function to destroy unused packet. When setting that, the jitter buffer no longer copies packet data. */ #define JITTER_BUFFER_SET_DESTROY_CALLBACK 4 /** */ #define JITTER_BUFFER_GET_DESTROY_CALLBACK 5 /** Tell the jitter buffer to only adjust the delay in multiples of the step parameter provided */ #define JITTER_BUFFER_SET_DELAY_STEP 6 /** */ #define JITTER_BUFFER_GET_DELAY_STEP 7 /** Tell the jitter buffer to only do concealment in multiples of the size parameter provided */ #define JITTER_BUFFER_SET_CONCEALMENT_SIZE 8 #define JITTER_BUFFER_GET_CONCEALMENT_SIZE 9 /** Absolute max amount of loss that can be tolerated regardless of the delay. Typical loss should be half of that or less. */ #define JITTER_BUFFER_SET_MAX_LATE_RATE 10 #define JITTER_BUFFER_GET_MAX_LATE_RATE 11 /** Equivalent cost of one percent late packet in timestamp units */ #define JITTER_BUFFER_SET_LATE_COST 12 #define JITTER_BUFFER_GET_LATE_COST 13 /** Initialises jitter buffer * * @param step_size Starting value for the size of concleanment packets and delay adjustment steps. Can be changed at any time using JITTER_BUFFER_SET_DELAY_STEP and JITTER_BUFFER_GET_CONCEALMENT_SIZE. * @return Newly created jitter buffer state */ JitterBuffer *jitter_buffer_init(int step_size); /** Restores jitter buffer to its original state * * @param jitter Jitter buffer state */ void jitter_buffer_reset(JitterBuffer *jitter); /** Destroys jitter buffer * * @param jitter Jitter buffer state */ void jitter_buffer_destroy(JitterBuffer *jitter); /** Put one packet into the jitter buffer * * @param jitter Jitter buffer state * @param packet Incoming packet */ void jitter_buffer_put(JitterBuffer *jitter, const JitterBufferPacket *packet); /** Get one packet from the jitter buffer * * @param jitter Jitter buffer state * @param packet Returned packet * @param desired_span Number of samples (or units) we wish to get from the buffer (no guarantee) * @param current_timestamp Timestamp for the returned packet */ int jitter_buffer_get(JitterBuffer *jitter, JitterBufferPacket *packet, spx_int32_t desired_span, spx_int32_t *start_offset); /** Used right after jitter_buffer_get() to obtain another packet that would have the same timestamp. * This is mainly useful for media where a single "frame" can be split into several packets. * * @param jitter Jitter buffer state * @param packet Returned packet */ int jitter_buffer_get_another(JitterBuffer *jitter, JitterBufferPacket *packet); /** Get pointer timestamp of jitter buffer * * @param jitter Jitter buffer state */ int jitter_buffer_get_pointer_timestamp(JitterBuffer *jitter); /** Advance by one tick * * @param jitter Jitter buffer state */ void jitter_buffer_tick(JitterBuffer *jitter); /** Telling the jitter buffer about the remaining data in the application buffer * @param jitter Jitter buffer state * @param rem Amount of data buffered by the application (timestamp units) */ void jitter_buffer_remaining_span(JitterBuffer *jitter, spx_uint32_t rem); /** Used like the ioctl function to control the jitter buffer parameters * * @param jitter Jitter buffer state * @param request ioctl-type request (one of the JITTER_BUFFER_* macros) * @param ptr Data exchanged to-from function * @return 0 if no error, -1 if request in unknown */ int jitter_buffer_ctl(JitterBuffer *jitter, int request, void *ptr); int jitter_buffer_update_delay(JitterBuffer *jitter, JitterBufferPacket *packet, spx_int32_t *start_offset); /* @} */ #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/speex/include/speex/speex_preprocess.h ================================================ /* Copyright (C) 2003 Epic Games Written by Jean-Marc Valin */ /** * @file speex_preprocess.h * @brief Speex preprocessor. The preprocess can do noise suppression, * residual echo suppression (after using the echo canceller), automatic * gain control (AGC) and voice activity detection (VAD). */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SPEEX_PREPROCESS_H #define SPEEX_PREPROCESS_H /** @defgroup SpeexPreprocessState SpeexPreprocessState: The Speex preprocessor * This is the Speex preprocessor. The preprocess can do noise suppression, * residual echo suppression (after using the echo canceller), automatic * gain control (AGC) and voice activity detection (VAD). * @{ */ #include "speex/speex_types.h" #ifdef __cplusplus extern "C" { #endif /** State of the preprocessor (one per channel). Should never be accessed directly. */ struct SpeexPreprocessState_; /** State of the preprocessor (one per channel). Should never be accessed directly. */ typedef struct SpeexPreprocessState_ SpeexPreprocessState; /** Creates a new preprocessing state. You MUST create one state per channel processed. * @param frame_size Number of samples to process at one time (should correspond to 10-20 ms). Must be * the same value as that used for the echo canceller for residual echo cancellation to work. * @param sampling_rate Sampling rate used for the input. * @return Newly created preprocessor state */ SpeexPreprocessState *speex_preprocess_state_init(int frame_size, int sampling_rate); /** Destroys a preprocessor state * @param st Preprocessor state to destroy */ void speex_preprocess_state_destroy(SpeexPreprocessState *st); /** Preprocess a frame * @param st Preprocessor state * @param x Audio sample vector (in and out). Must be same size as specified in speex_preprocess_state_init(). * @return Bool value for voice activity (1 for speech, 0 for noise/silence), ONLY if VAD turned on. */ int speex_preprocess_run(SpeexPreprocessState *st, spx_int16_t *x); /** Preprocess a frame (deprecated, use speex_preprocess_run() instead)*/ int speex_preprocess(SpeexPreprocessState *st, spx_int16_t *x, spx_int32_t *echo); /** Update preprocessor state, but do not compute the output * @param st Preprocessor state * @param x Audio sample vector (in only). Must be same size as specified in speex_preprocess_state_init(). */ void speex_preprocess_estimate_update(SpeexPreprocessState *st, spx_int16_t *x); /** Used like the ioctl function to control the preprocessor parameters * @param st Preprocessor state * @param request ioctl-type request (one of the SPEEX_PREPROCESS_* macros) * @param ptr Data exchanged to-from function * @return 0 if no error, -1 if request in unknown */ int speex_preprocess_ctl(SpeexPreprocessState *st, int request, void *ptr); /** Set preprocessor denoiser state */ #define SPEEX_PREPROCESS_SET_DENOISE 0 /** Get preprocessor denoiser state */ #define SPEEX_PREPROCESS_GET_DENOISE 1 /** Set preprocessor Automatic Gain Control state */ #define SPEEX_PREPROCESS_SET_AGC 2 /** Get preprocessor Automatic Gain Control state */ #define SPEEX_PREPROCESS_GET_AGC 3 /** Set preprocessor Voice Activity Detection state */ #define SPEEX_PREPROCESS_SET_VAD 4 /** Get preprocessor Voice Activity Detection state */ #define SPEEX_PREPROCESS_GET_VAD 5 /** Set preprocessor Automatic Gain Control level (float) */ #define SPEEX_PREPROCESS_SET_AGC_LEVEL 6 /** Get preprocessor Automatic Gain Control level (float) */ #define SPEEX_PREPROCESS_GET_AGC_LEVEL 7 /** Set preprocessor dereverb state */ #define SPEEX_PREPROCESS_SET_DEREVERB 8 /** Get preprocessor dereverb state */ #define SPEEX_PREPROCESS_GET_DEREVERB 9 /** Set preprocessor dereverb level */ #define SPEEX_PREPROCESS_SET_DEREVERB_LEVEL 10 /** Get preprocessor dereverb level */ #define SPEEX_PREPROCESS_GET_DEREVERB_LEVEL 11 /** Set preprocessor dereverb decay */ #define SPEEX_PREPROCESS_SET_DEREVERB_DECAY 12 /** Get preprocessor dereverb decay */ #define SPEEX_PREPROCESS_GET_DEREVERB_DECAY 13 /** Set probability required for the VAD to go from silence to voice */ #define SPEEX_PREPROCESS_SET_PROB_START 14 /** Get probability required for the VAD to go from silence to voice */ #define SPEEX_PREPROCESS_GET_PROB_START 15 /** Set probability required for the VAD to stay in the voice state (integer percent) */ #define SPEEX_PREPROCESS_SET_PROB_CONTINUE 16 /** Get probability required for the VAD to stay in the voice state (integer percent) */ #define SPEEX_PREPROCESS_GET_PROB_CONTINUE 17 /** Set maximum attenuation of the noise in dB (negative number) */ #define SPEEX_PREPROCESS_SET_NOISE_SUPPRESS 18 /** Get maximum attenuation of the noise in dB (negative number) */ #define SPEEX_PREPROCESS_GET_NOISE_SUPPRESS 19 /** Set maximum attenuation of the residual echo in dB (negative number) */ #define SPEEX_PREPROCESS_SET_ECHO_SUPPRESS 20 /** Get maximum attenuation of the residual echo in dB (negative number) */ #define SPEEX_PREPROCESS_GET_ECHO_SUPPRESS 21 /** Set maximum attenuation of the residual echo in dB when near end is active (negative number) */ #define SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE 22 /** Get maximum attenuation of the residual echo in dB when near end is active (negative number) */ #define SPEEX_PREPROCESS_GET_ECHO_SUPPRESS_ACTIVE 23 /** Set the corresponding echo canceller state so that residual echo suppression can be performed (NULL for no residual echo suppression) */ #define SPEEX_PREPROCESS_SET_ECHO_STATE 24 /** Get the corresponding echo canceller state */ #define SPEEX_PREPROCESS_GET_ECHO_STATE 25 /** Set maximal gain increase in dB/second (int32) */ #define SPEEX_PREPROCESS_SET_AGC_INCREMENT 26 /** Get maximal gain increase in dB/second (int32) */ #define SPEEX_PREPROCESS_GET_AGC_INCREMENT 27 /** Set maximal gain decrease in dB/second (int32) */ #define SPEEX_PREPROCESS_SET_AGC_DECREMENT 28 /** Get maximal gain decrease in dB/second (int32) */ #define SPEEX_PREPROCESS_GET_AGC_DECREMENT 29 /** Set maximal gain in dB (int32) */ #define SPEEX_PREPROCESS_SET_AGC_MAX_GAIN 30 /** Get maximal gain in dB (int32) */ #define SPEEX_PREPROCESS_GET_AGC_MAX_GAIN 31 /* Can't set loudness */ /** Get loudness */ #define SPEEX_PREPROCESS_GET_AGC_LOUDNESS 33 /* Can't set gain */ /** Get current gain (int32 percent) */ #define SPEEX_PREPROCESS_GET_AGC_GAIN 35 /* Can't set spectrum size */ /** Get spectrum size for power spectrum (int32) */ #define SPEEX_PREPROCESS_GET_PSD_SIZE 37 /* Can't set power spectrum */ /** Get power spectrum (int32[] of squared values) */ #define SPEEX_PREPROCESS_GET_PSD 39 /* Can't set noise size */ /** Get spectrum size for noise estimate (int32) */ #define SPEEX_PREPROCESS_GET_NOISE_PSD_SIZE 41 /* Can't set noise estimate */ /** Get noise estimate (int32[] of squared values) */ #define SPEEX_PREPROCESS_GET_NOISE_PSD 43 /* Can't set speech probability */ /** Get speech probability in last frame (int32). */ #define SPEEX_PREPROCESS_GET_PROB 45 /** Set preprocessor Automatic Gain Control level (int32) */ #define SPEEX_PREPROCESS_SET_AGC_TARGET 46 /** Get preprocessor Automatic Gain Control level (int32) */ #define SPEEX_PREPROCESS_GET_AGC_TARGET 47 #ifdef __cplusplus } #endif /** @}*/ #endif ================================================ FILE: deps/pjsip/third_party/speex/include/speex/speex_resampler.h ================================================ /* Copyright (C) 2007 Jean-Marc Valin File: speex_resampler.h Resampling code The design goals of this code are: - Very fast algorithm - Low memory requirement - Good *perceptual* quality (and not best SNR) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SPEEX_RESAMPLER_H #define SPEEX_RESAMPLER_H #ifdef OUTSIDE_SPEEX /********* WARNING: MENTAL SANITY ENDS HERE *************/ /* If the resampler is defined outside of Speex, we change the symbol names so that there won't be any clash if linking with Speex later on. */ /* #define RANDOM_PREFIX your software name here */ #ifndef RANDOM_PREFIX #error "Please define RANDOM_PREFIX (above) to something specific to your project to prevent symbol name clashes" #endif #define CAT_PREFIX2(a,b) a ## b #define CAT_PREFIX(a,b) CAT_PREFIX2(a, b) #define speex_resampler_init CAT_PREFIX(RANDOM_PREFIX,_resampler_init) #define speex_resampler_init_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_init_frac) #define speex_resampler_destroy CAT_PREFIX(RANDOM_PREFIX,_resampler_destroy) #define speex_resampler_process_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_float) #define speex_resampler_process_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_int) #define speex_resampler_process_interleaved_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_float) #define speex_resampler_process_interleaved_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_int) #define speex_resampler_set_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate) #define speex_resampler_get_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_get_rate) #define speex_resampler_set_rate_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate_frac) #define speex_resampler_get_ratio CAT_PREFIX(RANDOM_PREFIX,_resampler_get_ratio) #define speex_resampler_set_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_set_quality) #define speex_resampler_get_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_get_quality) #define speex_resampler_set_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_input_stride) #define speex_resampler_get_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_stride) #define speex_resampler_set_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_output_stride) #define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride) #define speex_resampler_get_input_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_latency) #define speex_resampler_get_output_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_latency) #define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros) #define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem) #define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror) #define spx_int16_t short #define spx_int32_t int #define spx_uint16_t unsigned short #define spx_uint32_t unsigned int #else /* OUTSIDE_SPEEX */ #include "speex/speex_types.h" #endif /* OUTSIDE_SPEEX */ #ifdef __cplusplus extern "C" { #endif #define SPEEX_RESAMPLER_QUALITY_MAX 10 #define SPEEX_RESAMPLER_QUALITY_MIN 0 #define SPEEX_RESAMPLER_QUALITY_DEFAULT 4 #define SPEEX_RESAMPLER_QUALITY_VOIP 3 #define SPEEX_RESAMPLER_QUALITY_DESKTOP 5 enum { RESAMPLER_ERR_SUCCESS = 0, RESAMPLER_ERR_ALLOC_FAILED = 1, RESAMPLER_ERR_BAD_STATE = 2, RESAMPLER_ERR_INVALID_ARG = 3, RESAMPLER_ERR_PTR_OVERLAP = 4, RESAMPLER_ERR_MAX_ERROR }; struct SpeexResamplerState_; typedef struct SpeexResamplerState_ SpeexResamplerState; /** Create a new resampler with integer input and output rates. * @param nb_channels Number of channels to be processed * @param in_rate Input sampling rate (integer number of Hz). * @param out_rate Output sampling rate (integer number of Hz). * @param quality Resampling quality between 0 and 10, where 0 has poor quality * and 10 has very high quality. * @return Newly created resampler state * @retval NULL Error: not enough memory */ SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err); /** Create a new resampler with fractional input/output rates. The sampling * rate ratio is an arbitrary rational number with both the numerator and * denominator being 32-bit integers. * @param nb_channels Number of channels to be processed * @param ratio_num Numerator of the sampling rate ratio * @param ratio_den Denominator of the sampling rate ratio * @param in_rate Input sampling rate rounded to the nearest integer (in Hz). * @param out_rate Output sampling rate rounded to the nearest integer (in Hz). * @param quality Resampling quality between 0 and 10, where 0 has poor quality * and 10 has very high quality. * @return Newly created resampler state * @retval NULL Error: not enough memory */ SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err); /** Destroy a resampler state. * @param st Resampler state */ void speex_resampler_destroy(SpeexResamplerState *st); /** Resample a float array. The input and output buffers must *not* overlap. * @param st Resampler state * @param channel_index Index of the channel to process for the multi-channel * base (0 otherwise) * @param in Input buffer * @param in_len Number of input samples in the input buffer. Returns the * number of samples processed * @param out Output buffer * @param out_len Size of the output buffer. Returns the number of samples written */ int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len); /** Resample an int array. The input and output buffers must *not* overlap. * @param st Resampler state * @param channel_index Index of the channel to process for the multi-channel * base (0 otherwise) * @param in Input buffer * @param in_len Number of input samples in the input buffer. Returns the number * of samples processed * @param out Output buffer * @param out_len Size of the output buffer. Returns the number of samples written */ int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len); /** Resample an interleaved float array. The input and output buffers must *not* overlap. * @param st Resampler state * @param in Input buffer * @param in_len Number of input samples in the input buffer. Returns the number * of samples processed. This is all per-channel. * @param out Output buffer * @param out_len Size of the output buffer. Returns the number of samples written. * This is all per-channel. */ int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len); /** Resample an interleaved int array. The input and output buffers must *not* overlap. * @param st Resampler state * @param in Input buffer * @param in_len Number of input samples in the input buffer. Returns the number * of samples processed. This is all per-channel. * @param out Output buffer * @param out_len Size of the output buffer. Returns the number of samples written. * This is all per-channel. */ int speex_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len); /** Set (change) the input/output sampling rates (integer value). * @param st Resampler state * @param in_rate Input sampling rate (integer number of Hz). * @param out_rate Output sampling rate (integer number of Hz). */ int speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate); /** Get the current input/output sampling rates (integer value). * @param st Resampler state * @param in_rate Input sampling rate (integer number of Hz) copied. * @param out_rate Output sampling rate (integer number of Hz) copied. */ void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, spx_uint32_t *out_rate); /** Set (change) the input/output sampling rates and resampling ratio * (fractional values in Hz supported). * @param st Resampler state * @param ratio_num Numerator of the sampling rate ratio * @param ratio_den Denominator of the sampling rate ratio * @param in_rate Input sampling rate rounded to the nearest integer (in Hz). * @param out_rate Output sampling rate rounded to the nearest integer (in Hz). */ int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate); /** Get the current resampling ratio. This will be reduced to the least * common denominator. * @param st Resampler state * @param ratio_num Numerator of the sampling rate ratio copied * @param ratio_den Denominator of the sampling rate ratio copied */ void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, spx_uint32_t *ratio_den); /** Set (change) the conversion quality. * @param st Resampler state * @param quality Resampling quality between 0 and 10, where 0 has poor * quality and 10 has very high quality. */ int speex_resampler_set_quality(SpeexResamplerState *st, int quality); /** Get the conversion quality. * @param st Resampler state * @param quality Resampling quality between 0 and 10, where 0 has poor * quality and 10 has very high quality. */ void speex_resampler_get_quality(SpeexResamplerState *st, int *quality); /** Set (change) the input stride. * @param st Resampler state * @param stride Input stride */ void speex_resampler_set_input_stride(SpeexResamplerState *st, spx_uint32_t stride); /** Get the input stride. * @param st Resampler state * @param stride Input stride copied */ void speex_resampler_get_input_stride(SpeexResamplerState *st, spx_uint32_t *stride); /** Set (change) the output stride. * @param st Resampler state * @param stride Output stride */ void speex_resampler_set_output_stride(SpeexResamplerState *st, spx_uint32_t stride); /** Get the output stride. * @param st Resampler state copied * @param stride Output stride */ void speex_resampler_get_output_stride(SpeexResamplerState *st, spx_uint32_t *stride); /** Get the latency in input samples introduced by the resampler. * @param st Resampler state */ int speex_resampler_get_input_latency(SpeexResamplerState *st); /** Get the latency in output samples introduced by the resampler. * @param st Resampler state */ int speex_resampler_get_output_latency(SpeexResamplerState *st); /** Make sure that the first samples to go out of the resamplers don't have * leading zeros. This is only useful before starting to use a newly created * resampler. It is recommended to use that when resampling an audio file, as * it will generate a file with the same length. For real-time processing, * it is probably easier not to use this call (so that the output duration * is the same for the first frame). * @param st Resampler state */ int speex_resampler_skip_zeros(SpeexResamplerState *st); /** Reset a resampler so a new (unrelated) stream can be processed. * @param st Resampler state */ int speex_resampler_reset_mem(SpeexResamplerState *st); /** Returns the English meaning for an error code * @param err Error code * @return English string */ const char *speex_resampler_strerror(int err); #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/speex/include/speex/speex_stereo.h ================================================ /* Copyright (C) 2002 Jean-Marc Valin*/ /** @file speex_stereo.h @brief Describes the handling for intensity stereo */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef STEREO_H #define STEREO_H /** @defgroup SpeexStereoState SpeexStereoState: Handling Speex stereo files * This describes the Speex intensity stereo encoding/decoding * @{ */ #include "speex/speex_types.h" #include "speex/speex_bits.h" #ifdef __cplusplus extern "C" { #endif /** If you access any of these fields directly, I'll personally come and bite you */ typedef struct SpeexStereoState { float balance; /**< Left/right balance info */ float e_ratio; /**< Ratio of energies: E(left+right)/[E(left)+E(right)] */ float smooth_left; /**< Smoothed left channel gain */ float smooth_right; /**< Smoothed right channel gain */ float reserved1; /**< Reserved for future use */ float reserved2; /**< Reserved for future use */ } SpeexStereoState; /** Deprecated. Use speex_stereo_state_init() instead. */ #define SPEEX_STEREO_STATE_INIT {1,.5,1,1,0,0} /** Initialise/create a stereo stereo state */ SpeexStereoState *speex_stereo_state_init(); /** Reset/re-initialise an already allocated stereo state */ void speex_stereo_state_reset(SpeexStereoState *stereo); /** Destroy a stereo stereo state */ void speex_stereo_state_destroy(SpeexStereoState *stereo); /** Transforms a stereo frame into a mono frame and stores intensity stereo info in 'bits' */ void speex_encode_stereo(float *data, int frame_size, SpeexBits *bits); /** Transforms a stereo frame into a mono frame and stores intensity stereo info in 'bits' */ void speex_encode_stereo_int(spx_int16_t *data, int frame_size, SpeexBits *bits); /** Transforms a mono frame into a stereo frame using intensity stereo info */ void speex_decode_stereo(float *data, int frame_size, SpeexStereoState *stereo); /** Transforms a mono frame into a stereo frame using intensity stereo info */ void speex_decode_stereo_int(spx_int16_t *data, int frame_size, SpeexStereoState *stereo); /** Callback handler for intensity stereo info */ int speex_std_stereo_request_handler(SpeexBits *bits, void *state, void *data); #ifdef __cplusplus } #endif /** @} */ #endif ================================================ FILE: deps/pjsip/third_party/speex/include/speex/speex_types.h ================================================ /* speex_types.h taken from libogg */ /******************************************************************** * * * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * * by the Xiph.Org Foundation http://www.xiph.org/ * * * ******************************************************************** function: #ifdef jail to whip a few platforms into the UNIX ideal. last mod: $Id: speex_types.h 2002 2008-06-10 14:09:37Z nanang $ ********************************************************************/ /** @file speex_types.h @brief Speex types */ #ifndef _SPEEX_TYPES_H #define _SPEEX_TYPES_H #if defined(_WIN32) # if defined(__CYGWIN__) # include <_G_config.h> typedef _G_int32_t spx_int32_t; typedef _G_uint32_t spx_uint32_t; typedef _G_int16_t spx_int16_t; typedef _G_uint16_t spx_uint16_t; # elif defined(__MINGW32__) typedef short spx_int16_t; typedef unsigned short spx_uint16_t; typedef int spx_int32_t; typedef unsigned int spx_uint32_t; # elif defined(__MWERKS__) typedef int spx_int32_t; typedef unsigned int spx_uint32_t; typedef short spx_int16_t; typedef unsigned short spx_uint16_t; # else /* MSVC/Borland */ typedef __int32 spx_int32_t; typedef unsigned __int32 spx_uint32_t; typedef __int16 spx_int16_t; typedef unsigned __int16 spx_uint16_t; # endif #elif defined(__MACOS__) # include typedef SInt16 spx_int16_t; typedef UInt16 spx_uint16_t; typedef SInt32 spx_int32_t; typedef UInt32 spx_uint32_t; #elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */ # include typedef int16_t spx_int16_t; typedef u_int16_t spx_uint16_t; typedef int32_t spx_int32_t; typedef u_int32_t spx_uint32_t; #elif defined(__BEOS__) /* Be */ # include typedef int16_t spx_int16_t; typedef u_int16_t spx_uint16_t; typedef int32_t spx_int32_t; typedef u_int32_t spx_uint32_t; #elif defined (__EMX__) /* OS/2 GCC */ typedef short spx_int16_t; typedef unsigned short spx_uint16_t; typedef int spx_int32_t; typedef unsigned int spx_uint32_t; #elif defined (DJGPP) /* DJGPP */ typedef short spx_int16_t; typedef int spx_int32_t; typedef unsigned int spx_uint32_t; #elif defined(R5900) /* PS2 EE */ typedef int spx_int32_t; typedef unsigned spx_uint32_t; typedef short spx_int16_t; #elif defined(__SYMBIAN32__) /* Symbian GCC */ typedef signed short spx_int16_t; typedef unsigned short spx_uint16_t; typedef signed int spx_int32_t; typedef unsigned int spx_uint32_t; #elif defined(CONFIG_TI_C54X) || defined (CONFIG_TI_C55X) typedef short spx_int16_t; typedef unsigned short spx_uint16_t; typedef long spx_int32_t; typedef unsigned long spx_uint32_t; #elif defined(CONFIG_TI_C6X) typedef short spx_int16_t; typedef unsigned short spx_uint16_t; typedef int spx_int32_t; typedef unsigned int spx_uint32_t; #else # include #endif #endif /* _SPEEX_TYPES_H */ ================================================ FILE: deps/pjsip/third_party/speex/libspeex/_kiss_fft_guts.h ================================================ /* Copyright (c) 2003-2004, Mark Borgerding All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define MIN(a,b) ((a)<(b) ? (a):(b)) #define MAX(a,b) ((a)>(b) ? (a):(b)) /* kiss_fft.h defines kiss_fft_scalar as either short or a float type and defines typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */ #include "kiss_fft.h" #include "math_approx.h" #define MAXFACTORS 32 /* e.g. an fft of length 128 has 4 factors as far as kissfft is concerned 4*4*4*2 */ struct kiss_fft_state{ int nfft; int inverse; int factors[2*MAXFACTORS]; kiss_fft_cpx twiddles[1]; }; /* Explanation of macros dealing with complex math: C_MUL(m,a,b) : m = a*b C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise C_SUB( res, a,b) : res = a - b C_SUBFROM( res , a) : res -= a C_ADDTO( res , a) : res += a * */ #ifdef FIXED_POINT #include "arch.h" # define FRACBITS 15 # define SAMPPROD spx_int32_t #define SAMP_MAX 32767 #define SAMP_MIN -SAMP_MAX #if defined(CHECK_OVERFLOW) # define CHECK_OVERFLOW_OP(a,op,b) \ if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \ fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); } #endif # define smul(a,b) ( (SAMPPROD)(a)*(b) ) # define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS ) # define S_MUL(a,b) sround( smul(a,b) ) # define C_MUL(m,a,b) \ do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \ (m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0) # define C_MUL4(m,a,b) \ do{ (m).r = PSHR32( smul((a).r,(b).r) - smul((a).i,(b).i),17 ); \ (m).i = PSHR32( smul((a).r,(b).i) + smul((a).i,(b).r),17 ); }while(0) # define DIVSCALAR(x,k) \ (x) = sround( smul( x, SAMP_MAX/k ) ) # define C_FIXDIV(c,div) \ do { DIVSCALAR( (c).r , div); \ DIVSCALAR( (c).i , div); }while (0) # define C_MULBYSCALAR( c, s ) \ do{ (c).r = sround( smul( (c).r , s ) ) ;\ (c).i = sround( smul( (c).i , s ) ) ; }while(0) #else /* not FIXED_POINT*/ # define S_MUL(a,b) ( (a)*(b) ) #define C_MUL(m,a,b) \ do{ (m).r = (a).r*(b).r - (a).i*(b).i;\ (m).i = (a).r*(b).i + (a).i*(b).r; }while(0) #define C_MUL4(m,a,b) C_MUL(m,a,b) # define C_FIXDIV(c,div) /* NOOP */ # define C_MULBYSCALAR( c, s ) \ do{ (c).r *= (s);\ (c).i *= (s); }while(0) #endif #ifndef CHECK_OVERFLOW_OP # define CHECK_OVERFLOW_OP(a,op,b) /* noop */ #endif #define C_ADD( res, a,b)\ do { \ CHECK_OVERFLOW_OP((a).r,+,(b).r)\ CHECK_OVERFLOW_OP((a).i,+,(b).i)\ (res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \ }while(0) #define C_SUB( res, a,b)\ do { \ CHECK_OVERFLOW_OP((a).r,-,(b).r)\ CHECK_OVERFLOW_OP((a).i,-,(b).i)\ (res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \ }while(0) #define C_ADDTO( res , a)\ do { \ CHECK_OVERFLOW_OP((res).r,+,(a).r)\ CHECK_OVERFLOW_OP((res).i,+,(a).i)\ (res).r += (a).r; (res).i += (a).i;\ }while(0) #define C_SUBFROM( res , a)\ do {\ CHECK_OVERFLOW_OP((res).r,-,(a).r)\ CHECK_OVERFLOW_OP((res).i,-,(a).i)\ (res).r -= (a).r; (res).i -= (a).i; \ }while(0) #ifdef FIXED_POINT # define KISS_FFT_COS(phase) floor(MIN(32767,MAX(-32767,.5+32768 * cos (phase)))) # define KISS_FFT_SIN(phase) floor(MIN(32767,MAX(-32767,.5+32768 * sin (phase)))) # define HALF_OF(x) ((x)>>1) #elif defined(USE_SIMD) # define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) ) # define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) ) # define HALF_OF(x) ((x)*_mm_set1_ps(.5)) #else # define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase) # define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase) # define HALF_OF(x) ((x)*.5) #endif #define kf_cexp(x,phase) \ do{ \ (x)->r = KISS_FFT_COS(phase);\ (x)->i = KISS_FFT_SIN(phase);\ }while(0) #define kf_cexp2(x,phase) \ do{ \ (x)->r = spx_cos_norm((phase));\ (x)->i = spx_cos_norm((phase)-32768);\ }while(0) /* a debugging function */ #define pcpx(c)\ fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) ) ================================================ FILE: deps/pjsip/third_party/speex/libspeex/arch.h ================================================ /* Copyright (C) 2003 Jean-Marc Valin */ /** @file arch.h @brief Various architecture definitions Speex */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef ARCH_H #define ARCH_H #ifndef SPEEX_VERSION #define SPEEX_MAJOR_VERSION 1 /**< Major Speex version. */ #define SPEEX_MINOR_VERSION 1 /**< Minor Speex version. */ #define SPEEX_MICRO_VERSION 15 /**< Micro Speex version. */ #define SPEEX_EXTRA_VERSION "" /**< Extra Speex version. */ #define SPEEX_VERSION "speex-1.2beta3" /**< Speex version string. */ #endif /* A couple test to catch stupid option combinations */ #ifdef FIXED_POINT #ifdef FLOATING_POINT #error You cannot compile as floating point and fixed point at the same time #endif #ifdef _USE_SSE #error SSE is only for floating-point #endif #if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM)) #error Make up your mind. What CPU do you have? #endif #ifdef VORBIS_PSYCHO #error Vorbis-psy model currently not implemented in fixed-point #endif #else #ifndef FLOATING_POINT #error You now need to define either FIXED_POINT or FLOATING_POINT #endif #if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM) #error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions? #endif #ifdef FIXED_POINT_DEBUG #error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?" #endif #endif #ifndef OUTSIDE_SPEEX #include "speex/speex_types.h" #endif #define ABS(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute integer value. */ #define ABS16(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 16-bit value. */ #define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 16-bit value. */ #define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */ #define ABS32(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 32-bit value. */ #define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 32-bit value. */ #define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */ #ifdef FIXED_POINT typedef spx_int16_t spx_word16_t; typedef spx_int32_t spx_word32_t; typedef spx_word32_t spx_mem_t; typedef spx_word16_t spx_coef_t; typedef spx_word16_t spx_lsp_t; typedef spx_word32_t spx_sig_t; #define Q15ONE 32767 #define LPC_SCALING 8192 #define SIG_SCALING 16384 #define LSP_SCALING 8192. #define GAMMA_SCALING 32768. #define GAIN_SCALING 64 #define GAIN_SCALING_1 0.015625 #define LPC_SHIFT 13 #define LSP_SHIFT 13 #define SIG_SHIFT 14 #define GAIN_SHIFT 6 #define VERY_SMALL 0 #define VERY_LARGE32 ((spx_word32_t)2147483647) #define VERY_LARGE16 ((spx_word16_t)32767) #define Q15_ONE ((spx_word16_t)32767) #ifdef FIXED_DEBUG #include "fixed_debug.h" #else #include "fixed_generic.h" #ifdef ARM5E_ASM #include "fixed_arm5e.h" #elif defined (ARM4_ASM) #include "fixed_arm4.h" #elif defined (BFIN_ASM) #include "fixed_bfin.h" #endif #endif #else typedef float spx_mem_t; typedef float spx_coef_t; typedef float spx_lsp_t; typedef float spx_sig_t; typedef float spx_word16_t; typedef float spx_word32_t; #define Q15ONE 1.0f #define LPC_SCALING 1.f #define SIG_SCALING 1.f #define LSP_SCALING 1.f #define GAMMA_SCALING 1.f #define GAIN_SCALING 1.f #define GAIN_SCALING_1 1.f #define VERY_SMALL 1e-15f #define VERY_LARGE32 1e15f #define VERY_LARGE16 1e15f #define Q15_ONE ((spx_word16_t)1.f) #define QCONST16(x,bits) (x) #define QCONST32(x,bits) (x) #define NEG16(x) (-(x)) #define NEG32(x) (-(x)) #define EXTRACT16(x) (x) #define EXTEND32(x) (x) #define SHR16(a,shift) (a) #define SHL16(a,shift) (a) #define SHR32(a,shift) (a) #define SHL32(a,shift) (a) #define PSHR16(a,shift) (a) #define PSHR32(a,shift) (a) #define VSHR32(a,shift) (a) #define SATURATE16(x,a) (x) #define SATURATE32(x,a) (x) #define PSHR(a,shift) (a) #define SHR(a,shift) (a) #define SHL(a,shift) (a) #define SATURATE(x,a) (x) #define ADD16(a,b) ((a)+(b)) #define SUB16(a,b) ((a)-(b)) #define ADD32(a,b) ((a)+(b)) #define SUB32(a,b) ((a)-(b)) #define MULT16_16_16(a,b) ((a)*(b)) #define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b)) #define MAC16_16(c,a,b) ((c)+(spx_word32_t)(a)*(spx_word32_t)(b)) #define MULT16_32_Q11(a,b) ((a)*(b)) #define MULT16_32_Q13(a,b) ((a)*(b)) #define MULT16_32_Q14(a,b) ((a)*(b)) #define MULT16_32_Q15(a,b) ((a)*(b)) #define MULT16_32_P15(a,b) ((a)*(b)) #define MAC16_32_Q11(c,a,b) ((c)+(a)*(b)) #define MAC16_32_Q15(c,a,b) ((c)+(a)*(b)) #define MAC16_16_Q11(c,a,b) ((c)+(a)*(b)) #define MAC16_16_Q13(c,a,b) ((c)+(a)*(b)) #define MAC16_16_P13(c,a,b) ((c)+(a)*(b)) #define MULT16_16_Q11_32(a,b) ((a)*(b)) #define MULT16_16_Q13(a,b) ((a)*(b)) #define MULT16_16_Q14(a,b) ((a)*(b)) #define MULT16_16_Q15(a,b) ((a)*(b)) #define MULT16_16_P15(a,b) ((a)*(b)) #define MULT16_16_P13(a,b) ((a)*(b)) #define MULT16_16_P14(a,b) ((a)*(b)) #define DIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b)) #define PDIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b)) #define DIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b)) #define PDIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b)) #endif #if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X) /* 2 on TI C5x DSP */ #define BYTES_PER_CHAR 2 #define BITS_PER_CHAR 16 #define LOG2_BITS_PER_CHAR 4 #else #define BYTES_PER_CHAR 1 #define BITS_PER_CHAR 8 #define LOG2_BITS_PER_CHAR 3 #endif #ifdef FIXED_DEBUG extern long long spx_mips; #endif #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/bits.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: speex_bits.c Handles bit packing/unpacking Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "arch.h" #include "os_support.h" /* Maximum size of the bit-stream (for fixed-size allocation) */ #ifndef MAX_CHARS_PER_FRAME #define MAX_CHARS_PER_FRAME (2000/BYTES_PER_CHAR) #endif EXPORT void speex_bits_init(SpeexBits *bits) { bits->chars = (char*)speex_alloc(MAX_CHARS_PER_FRAME); if (!bits->chars) return; bits->buf_size = MAX_CHARS_PER_FRAME; bits->owner=1; speex_bits_reset(bits); } EXPORT void speex_bits_init_buffer(SpeexBits *bits, void *buff, int buf_size) { bits->chars = (char*)buff; bits->buf_size = buf_size; bits->owner=0; speex_bits_reset(bits); } EXPORT void speex_bits_set_bit_buffer(SpeexBits *bits, void *buff, int buf_size) { bits->chars = (char*)buff; bits->buf_size = buf_size; bits->owner=0; bits->nbBits=buf_size<charPtr=0; bits->bitPtr=0; bits->overflow=0; } EXPORT void speex_bits_destroy(SpeexBits *bits) { if (bits->owner) speex_free(bits->chars); /* Will do something once the allocation is dynamic */ } EXPORT void speex_bits_reset(SpeexBits *bits) { /* We only need to clear the first byte now */ bits->chars[0]=0; bits->nbBits=0; bits->charPtr=0; bits->bitPtr=0; bits->overflow=0; } EXPORT void speex_bits_rewind(SpeexBits *bits) { bits->charPtr=0; bits->bitPtr=0; bits->overflow=0; } EXPORT void speex_bits_read_from(SpeexBits *bits, char *chars, int len) { int i; int nchars = len / BYTES_PER_CHAR; if (nchars > bits->buf_size) { speex_notify("Packet is larger than allocated buffer"); if (bits->owner) { char *tmp = (char*)speex_realloc(bits->chars, nchars); if (tmp) { bits->buf_size=nchars; bits->chars=tmp; } else { nchars=bits->buf_size; speex_warning("Could not resize input buffer: truncating input"); } } else { speex_warning("Do not own input buffer: truncating oversize input"); nchars=bits->buf_size; } } #if (BYTES_PER_CHAR==2) /* Swap bytes to proper endian order (could be done externally) */ #define HTOLS(A) ((((A) >> 8)&0xff)|(((A) & 0xff)<<8)) #else #define HTOLS(A) (A) #endif for (i=0;ichars[i]=HTOLS(chars[i]); bits->nbBits=nchars<charPtr=0; bits->bitPtr=0; bits->overflow=0; } static void speex_bits_flush(SpeexBits *bits) { int nchars = ((bits->nbBits+BITS_PER_CHAR-1)>>LOG2_BITS_PER_CHAR); if (bits->charPtr>0) SPEEX_MOVE(bits->chars, &bits->chars[bits->charPtr], nchars-bits->charPtr); bits->nbBits -= bits->charPtr<charPtr=0; } EXPORT void speex_bits_read_whole_bytes(SpeexBits *bits, char *chars, int nbytes) { int i,pos; int nchars = nbytes/BYTES_PER_CHAR; if (((bits->nbBits+BITS_PER_CHAR-1)>>LOG2_BITS_PER_CHAR)+nchars > bits->buf_size) { /* Packet is larger than allocated buffer */ if (bits->owner) { char *tmp = (char*)speex_realloc(bits->chars, (bits->nbBits>>LOG2_BITS_PER_CHAR)+nchars+1); if (tmp) { bits->buf_size=(bits->nbBits>>LOG2_BITS_PER_CHAR)+nchars+1; bits->chars=tmp; } else { nchars=bits->buf_size-(bits->nbBits>>LOG2_BITS_PER_CHAR)-1; speex_warning("Could not resize input buffer: truncating oversize input"); } } else { speex_warning("Do not own input buffer: truncating oversize input"); nchars=bits->buf_size; } } speex_bits_flush(bits); pos=bits->nbBits>>LOG2_BITS_PER_CHAR; for (i=0;ichars[pos+i]=HTOLS(chars[i]); bits->nbBits+=nchars<bitPtr; charPtr=bits->charPtr; nbBits=bits->nbBits; speex_bits_insert_terminator(bits); bits->bitPtr=bitPtr; bits->charPtr=charPtr; bits->nbBits=nbBits; if (max_nchars > ((bits->nbBits+BITS_PER_CHAR-1)>>LOG2_BITS_PER_CHAR)) max_nchars = ((bits->nbBits+BITS_PER_CHAR-1)>>LOG2_BITS_PER_CHAR); for (i=0;ichars[i]); return max_nchars*BYTES_PER_CHAR; } EXPORT int speex_bits_write_whole_bytes(SpeexBits *bits, char *chars, int max_nbytes) { int max_nchars = max_nbytes/BYTES_PER_CHAR; int i; if (max_nchars > ((bits->nbBits)>>LOG2_BITS_PER_CHAR)) max_nchars = ((bits->nbBits)>>LOG2_BITS_PER_CHAR); for (i=0;ichars[i]); if (bits->bitPtr>0) bits->chars[0]=bits->chars[max_nchars]; else bits->chars[0]=0; bits->charPtr=0; bits->nbBits &= (BITS_PER_CHAR-1); return max_nchars*BYTES_PER_CHAR; } EXPORT void speex_bits_pack(SpeexBits *bits, int data, int nbBits) { unsigned int d=data; if (bits->charPtr+((nbBits+bits->bitPtr)>>LOG2_BITS_PER_CHAR) >= bits->buf_size) { speex_notify("Buffer too small to pack bits"); if (bits->owner) { int new_nchars = ((bits->buf_size+5)*3)>>1; char *tmp = (char*)speex_realloc(bits->chars, new_nchars); if (tmp) { bits->buf_size=new_nchars; bits->chars=tmp; } else { speex_warning("Could not resize input buffer: not packing"); return; } } else { speex_warning("Do not own input buffer: not packing"); return; } } while(nbBits) { int bit; bit = (d>>(nbBits-1))&1; bits->chars[bits->charPtr] |= bit<<(BITS_PER_CHAR-1-bits->bitPtr); bits->bitPtr++; if (bits->bitPtr==BITS_PER_CHAR) { bits->bitPtr=0; bits->charPtr++; bits->chars[bits->charPtr] = 0; } bits->nbBits++; nbBits--; } } EXPORT int speex_bits_unpack_signed(SpeexBits *bits, int nbBits) { unsigned int d=speex_bits_unpack_unsigned(bits,nbBits); /* If number is negative */ if (d>>(nbBits-1)) { d |= (-1)<charPtr<bitPtr+nbBits>bits->nbBits) bits->overflow=1; if (bits->overflow) return 0; while(nbBits) { d<<=1; d |= (bits->chars[bits->charPtr]>>(BITS_PER_CHAR-1 - bits->bitPtr))&1; bits->bitPtr++; if (bits->bitPtr==BITS_PER_CHAR) { bits->bitPtr=0; bits->charPtr++; } nbBits--; } return d; } EXPORT unsigned int speex_bits_peek_unsigned(SpeexBits *bits, int nbBits) { unsigned int d=0; int bitPtr, charPtr; char *chars; if ((bits->charPtr<bitPtr+nbBits>bits->nbBits) bits->overflow=1; if (bits->overflow) return 0; bitPtr=bits->bitPtr; charPtr=bits->charPtr; chars = bits->chars; while(nbBits) { d<<=1; d |= (chars[charPtr]>>(BITS_PER_CHAR-1 - bitPtr))&1; bitPtr++; if (bitPtr==BITS_PER_CHAR) { bitPtr=0; charPtr++; } nbBits--; } return d; } EXPORT int speex_bits_peek(SpeexBits *bits) { if ((bits->charPtr<bitPtr+1>bits->nbBits) bits->overflow=1; if (bits->overflow) return 0; return (bits->chars[bits->charPtr]>>(BITS_PER_CHAR-1 - bits->bitPtr))&1; } EXPORT void speex_bits_advance(SpeexBits *bits, int n) { if (((bits->charPtr<bitPtr+n>bits->nbBits) || bits->overflow){ bits->overflow=1; return; } bits->charPtr += (bits->bitPtr+n) >> LOG2_BITS_PER_CHAR; /* divide by BITS_PER_CHAR */ bits->bitPtr = (bits->bitPtr+n) & (BITS_PER_CHAR-1); /* modulo by BITS_PER_CHAR */ } EXPORT int speex_bits_remaining(SpeexBits *bits) { if (bits->overflow) return -1; else return bits->nbBits-((bits->charPtr<bitPtr); } EXPORT int speex_bits_nbytes(SpeexBits *bits) { return ((bits->nbBits+BITS_PER_CHAR-1)>>LOG2_BITS_PER_CHAR); } EXPORT void speex_bits_insert_terminator(SpeexBits *bits) { if (bits->bitPtr) speex_bits_pack(bits, 0, 1); while (bits->bitPtr) speex_bits_pack(bits, 1, 1); } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/buffer.c ================================================ /* Copyright (C) 2007 Jean-Marc Valin File: buffer.c This is a very simple ring buffer implementation. It is not thread-safe so you need to do your own locking. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_support.h" #include "arch.h" #include struct SpeexBuffer_ { char *data; int size; int read_ptr; int write_ptr; int available; }; EXPORT SpeexBuffer *speex_buffer_init(int size) { SpeexBuffer *st = speex_alloc(sizeof(SpeexBuffer)); st->data = speex_alloc(size); st->size = size; st->read_ptr = 0; st->write_ptr = 0; st->available = 0; return st; } EXPORT void speex_buffer_destroy(SpeexBuffer *st) { speex_free(st->data); speex_free(st); } EXPORT int speex_buffer_write(SpeexBuffer *st, void *_data, int len) { int end; int end1; char *data = _data; if (len > st->size) { data += len-st->size; len = st->size; } end = st->write_ptr + len; end1 = end; if (end1 > st->size) end1 = st->size; SPEEX_COPY(st->data + st->write_ptr, data, end1 - st->write_ptr); if (end > st->size) { end -= st->size; SPEEX_COPY(st->data, data+end1 - st->write_ptr, end); } st->available += len; if (st->available > st->size) { st->available = st->size; st->read_ptr = st->write_ptr; } st->write_ptr += len; if (st->write_ptr > st->size) st->write_ptr -= st->size; return len; } EXPORT int speex_buffer_writezeros(SpeexBuffer *st, int len) { /* This is almost the same as for speex_buffer_write() but using SPEEX_MEMSET() instead of SPEEX_COPY(). Update accordingly. */ int end; int end1; if (len > st->size) { len = st->size; } end = st->write_ptr + len; end1 = end; if (end1 > st->size) end1 = st->size; SPEEX_MEMSET(st->data + st->write_ptr, 0, end1 - st->write_ptr); if (end > st->size) { end -= st->size; SPEEX_MEMSET(st->data, 0, end); } st->available += len; if (st->available > st->size) { st->available = st->size; st->read_ptr = st->write_ptr; } st->write_ptr += len; if (st->write_ptr > st->size) st->write_ptr -= st->size; return len; } EXPORT int speex_buffer_read(SpeexBuffer *st, void *_data, int len) { int end, end1; char *data = _data; if (len > st->available) { SPEEX_MEMSET(data+st->available, 0, st->size-st->available); len = st->available; } end = st->read_ptr + len; end1 = end; if (end1 > st->size) end1 = st->size; SPEEX_COPY(data, st->data + st->read_ptr, end1 - st->read_ptr); if (end > st->size) { end -= st->size; SPEEX_COPY(data+end1 - st->read_ptr, st->data, end); } st->available -= len; st->read_ptr += len; if (st->read_ptr > st->size) st->read_ptr -= st->size; return len; } EXPORT int speex_buffer_get_available(SpeexBuffer *st) { return st->available; } EXPORT int speex_buffer_resize(SpeexBuffer *st, int len) { int old_len = st->size; if (len > old_len) { st->data = speex_realloc(st->data, len); /* FIXME: move data/pointers properly for growing the buffer */ } else { /* FIXME: move data/pointers properly for shrinking the buffer */ st->data = speex_realloc(st->data, len); } return len; } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/cb_search.c ================================================ /* Copyright (C) 2002-2006 Jean-Marc Valin File: cb_search.c Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "cb_search.h" #include "filters.h" #include "stack_alloc.h" #include "vq.h" #include "arch.h" #include "math_approx.h" #include "os_support.h" #ifdef _USE_SSE #include "cb_search_sse.h" #elif defined(ARM4_ASM) || defined(ARM5E_ASM) #include "cb_search_arm4.h" #elif defined(BFIN_ASM) #include "cb_search_bfin.h" #endif #ifndef OVERRIDE_COMPUTE_WEIGHTED_CODEBOOK static void compute_weighted_codebook(const signed char *shape_cb, const spx_word16_t *r, spx_word16_t *resp, spx_word16_t *resp2, spx_word32_t *E, int shape_cb_size, int subvect_size, char *stack) { int i, j, k; VARDECL(spx_word16_t *shape); ALLOC(shape, subvect_size, spx_word16_t); for (i=0;isubvect_size; nb_subvect = params->nb_subvect; shape_cb_size = 1<shape_bits; shape_cb = params->shape_cb; have_sign = params->have_sign; ALLOC(resp, shape_cb_size*subvect_size, spx_word16_t); #ifdef _USE_SSE ALLOC(resp2, (shape_cb_size*subvect_size)>>2, __m128); ALLOC(E, shape_cb_size>>2, __m128); #else resp2 = resp; ALLOC(E, shape_cb_size, spx_word32_t); #endif ALLOC(t, nsf, spx_word16_t); ALLOC(e, nsf, spx_sig_t); /* FIXME: Do we still need to copy the target? */ SPEEX_COPY(t, target, nsf); compute_weighted_codebook(shape_cb, r, resp, resp2, E, shape_cb_size, subvect_size, stack); for (i=0;ishape_bits+have_sign); { int rind; spx_word16_t *res; spx_word16_t sign=1; rind = best_index; if (rind>=shape_cb_size) { sign=-1; rind-=shape_cb_size; } res = resp+rind*subvect_size; if (sign>0) for (m=0;m=shape_cb_size) { sign=-1; rind-=shape_cb_size; } q=subvect_size-m; #ifdef FIXED_POINT g=sign*shape_cb[rind*subvect_size+m]; #else g=sign*0.03125*shape_cb[rind*subvect_size+m]; #endif target_update(t+subvect_size*(i+1), g, r+q, nsf-subvect_size*(i+1)); } } /* Update excitation */ /* FIXME: We could update the excitation directly above */ for (j=0;j10) N=10; /* Complexity isn't as important for the codebooks as it is for the pitch */ N=(2*N)/3; if (N<1) N=1; if (N==1) { split_cb_search_shape_sign_N1(target,ak,awk1,awk2,par,p,nsf,exc,r,bits,stack,update_target); return; } ALLOC(ot2, N, spx_word16_t*); ALLOC(nt2, N, spx_word16_t*); ALLOC(oind, N, int*); ALLOC(nind, N, int*); params = (const split_cb_params *) par; subvect_size = params->subvect_size; nb_subvect = params->nb_subvect; shape_cb_size = 1<shape_bits; shape_cb = params->shape_cb; have_sign = params->have_sign; ALLOC(resp, shape_cb_size*subvect_size, spx_word16_t); #ifdef _USE_SSE ALLOC(resp2, (shape_cb_size*subvect_size)>>2, __m128); ALLOC(E, shape_cb_size>>2, __m128); #else resp2 = resp; ALLOC(E, shape_cb_size, spx_word32_t); #endif ALLOC(t, nsf, spx_word16_t); ALLOC(e, nsf, spx_sig_t); ALLOC(ind, nb_subvect, int); ALLOC(tmp, 2*N*nsf, spx_word16_t); for (i=0;im;n--) { ndist[n] = ndist[n-1]; best_nind[n] = best_nind[n-1]; best_ntarget[n] = best_ntarget[n-1]; } /* n is equal to m here, so they're interchangeable */ ndist[m] = err; best_nind[n] = best_index[k]; best_ntarget[n] = j; break; } } } } if (i==0) break; } for (j=0;j=shape_cb_size) { sign=-1; rind-=shape_cb_size; } q=subvect_size-m; #ifdef FIXED_POINT g=sign*shape_cb[rind*subvect_size+m]; #else g=sign*0.03125*shape_cb[rind*subvect_size+m]; #endif target_update(nt[j]+subvect_size*(i+1), g, r+q, nsf-subvect_size*(i+1)); } for (q=0;qshape_bits+have_sign); } /* Put everything back together */ for (i=0;i=shape_cb_size) { sign=-1; rind-=shape_cb_size; } #ifdef FIXED_POINT if (sign==1) { for (j=0;jsubvect_size; nb_subvect = params->nb_subvect; shape_cb_size = 1<shape_bits; shape_cb = params->shape_cb; have_sign = params->have_sign; ALLOC(ind, nb_subvect, int); ALLOC(signs, nb_subvect, int); /* Decode codewords and gains */ for (i=0;ishape_bits); } /* Compute decoded excitation */ for (i=0;i #include "arch.h" /** Split codebook parameters. */ typedef struct split_cb_params { int subvect_size; int nb_subvect; const signed char *shape_cb; int shape_bits; int have_sign; } split_cb_params; void split_cb_search_shape_sign( spx_word16_t target[], /* target vector */ spx_coef_t ak[], /* LPCs for this subframe */ spx_coef_t awk1[], /* Weighted LPCs for this subframe */ spx_coef_t awk2[], /* Weighted LPCs for this subframe */ const void *par, /* Codebook/search parameters */ int p, /* number of LPC coeffs */ int nsf, /* number of samples in subframe */ spx_sig_t *exc, spx_word16_t *r, SpeexBits *bits, char *stack, int complexity, int update_target ); void split_cb_shape_sign_unquant( spx_sig_t *exc, const void *par, /* non-overlapping codebook */ int nsf, /* number of samples in subframe */ SpeexBits *bits, char *stack, spx_int32_t *seed ); void noise_codebook_quant( spx_word16_t target[], /* target vector */ spx_coef_t ak[], /* LPCs for this subframe */ spx_coef_t awk1[], /* Weighted LPCs for this subframe */ spx_coef_t awk2[], /* Weighted LPCs for this subframe */ const void *par, /* Codebook/search parameters */ int p, /* number of LPC coeffs */ int nsf, /* number of samples in subframe */ spx_sig_t *exc, spx_word16_t *r, SpeexBits *bits, char *stack, int complexity, int update_target ); void noise_codebook_unquant( spx_sig_t *exc, const void *par, /* non-overlapping codebook */ int nsf, /* number of samples in subframe */ SpeexBits *bits, char *stack, spx_int32_t *seed ); #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/cb_search_arm4.h ================================================ /* Copyright (C) 2004 Jean-Marc Valin */ /** @file cb_search_arm4.h @brief Fixed codebook functions (ARM4 version) */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This optimization is temporaly disabled until it is fixed to account for the fact that "r" is now a 16-bit array */ #if 0 #define OVERRIDE_COMPUTE_WEIGHTED_CODEBOOK static void compute_weighted_codebook(const signed char *shape_cb, const spx_word16_t *r, spx_word16_t *resp, spx_word16_t *resp2, spx_word32_t *E, int shape_cb_size, int subvect_size, char *stack) { int i, j, k; //const signed char *shape; for (i=0;i>>= 13;\n\t" "A1 += R0.L*R0.L (IS);\n\t" "W[P3++] = R0;\n\t" "P0 += 1;\n\t" "P2 += 2;\n\t" "LOOP_END outter%=;\n\t" "P4 = %4;\n\t" "R1 = A1;\n\t" "[P4] = R1;\n\t" : : "m" (subvect_size), "m" (shape_cb), "m" (r), "m" (resp), "m" (E) : "A0", "P0", "P1", "P2", "P3", "P4", "R0", "R1", "R2", "I0", "I1", "L0", "L1", "A0", "A1", "memory" #if !(__GNUC__ == 3) , "LC0", "LC1" /* gcc 3.4 doesn't know about LC registers */ #endif ); shape_cb += subvect_size; resp += subvect_size; E++; } } #define OVERRIDE_TARGET_UPDATE static inline void target_update(spx_word16_t *t, spx_word16_t g, spx_word16_t *r, int len) { if (!len) return; __asm__ __volatile__ ( "I0 = %0;\n\t" "I1 = %1;\n\t" "L0 = 0;\n\t" "L1 = 0;\n\t" "R2 = 4096;\n\t" "LOOP tupdate%= LC0 = %3;\n\t" "LOOP_BEGIN tupdate%=;\n\t" "R0.L = W[I0] || R1.L = W[I1++];\n\t" "R1 = (A1 = R1.L*%2.L) (IS);\n\t" "R1 = R1 + R2;\n\t" "R1 >>>= 13;\n\t" "R0.L = R0.L - R1.L;\n\t" "W[I0++] = R0.L;\n\t" "LOOP_END tupdate%=;\n\t" : : "a" (t), "a" (r), "d" (g), "a" (len) : "R0", "R1", "R2", "A1", "I0", "I1", "L0", "L1" ); } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/cb_search_sse.h ================================================ /* Copyright (C) 2004 Jean-Marc Valin */ /** @file cb_search_sse.h @brief Fixed codebook functions (SSE version) */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include static inline void _spx_mm_getr_ps (__m128 U, float *__Z, float *__Y, float *__X, float *__W) { union { float __a[4]; __m128 __v; } __u; __u.__v = U; *__Z = __u.__a[0]; *__Y = __u.__a[1]; *__X = __u.__a[2]; *__W = __u.__a[3]; } #define OVERRIDE_COMPUTE_WEIGHTED_CODEBOOK static void compute_weighted_codebook(const signed char *shape_cb, const spx_sig_t *_r, float *resp, __m128 *resp2, __m128 *E, int shape_cb_size, int subvect_size, char *stack) { int i, j, k; __m128 resj, EE; VARDECL(__m128 *r); VARDECL(__m128 *shape); ALLOC(r, subvect_size, __m128); ALLOC(shape, subvect_size, __m128); for(j=0;j>2] = EE; } } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/echo_diagnostic.m ================================================ % Attempts to diagnose AEC problems from recorded samples % % out = echo_diagnostic(rec_file, play_file, out_file, tail_length) % % Computes the full matrix inversion to cancel echo from the % recording 'rec_file' using the far end signal 'play_file' using % a filter length of 'tail_length'. The output is saved to 'out_file'. function out = echo_diagnostic(rec_file, play_file, out_file, tail_length) F=fopen(rec_file,'rb'); rec=fread(F,Inf,'short'); fclose (F); F=fopen(play_file,'rb'); play=fread(F,Inf,'short'); fclose (F); rec = [rec; zeros(1024,1)]; play = [play; zeros(1024,1)]; N = length(rec); corr = real(ifft(fft(rec).*conj(fft(play)))); acorr = real(ifft(fft(play).*conj(fft(play)))); [a,b] = max(corr); if b > N/2 b = b-N; end printf ("Far end to near end delay is %d samples\n", b); if (b > .3*tail_length) printf ('This is too much delay, try delaying the far-end signal a bit\n'); else if (b < 0) printf ('You have a negative delay, the echo canceller has no chance to cancel anything!\n'); else printf ('Delay looks OK.\n'); end end end N2 = round(N/2); corr1 = real(ifft(fft(rec(1:N2)).*conj(fft(play(1:N2))))); corr2 = real(ifft(fft(rec(N2+1:end)).*conj(fft(play(N2+1:end))))); [a,b1] = max(corr1); if b1 > N2/2 b1 = b1-N2; end [a,b2] = max(corr2); if b2 > N2/2 b2 = b2-N2; end drift = (b1-b2)/N2; printf ('Drift estimate is %f%% (%d samples)\n', 100*drift, b1-b2); if abs(b1-b2) < 10 printf ('A drift of a few (+-10) samples is normal.\n'); else if abs(b1-b2) < 30 printf ('There may be (not sure) excessive clock drift. Is the capture and playback done on the same soundcard?\n'); else printf ('Your clock is drifting! No way the AEC will be able to do anything with that. Most likely, you''re doing capture and playback from two different cards.\n'); end end end acorr(1) = .001+1.00001*acorr(1); AtA = toeplitz(acorr(1:tail_length)); bb = corr(1:tail_length); h = AtA\bb; out = (rec - filter(h, 1, play)); F=fopen(out_file,'w'); fwrite(F,out,'short'); fclose (F); ================================================ FILE: deps/pjsip/third_party/speex/libspeex/exc_10_16_table.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: exc_10_16_table.c Codebook for excitation in narrowband CELP mode (3200 bps) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ const signed char exc_10_16_table[160] = { 22,39,14,44,11,35,-2,23,-4,6, 46,-28,13,-27,-23,12,4,20,-5,9, 37,-18,-23,23,0,9,-6,-20,4,-1, -17,-5,-4,17,0,1,9,-2,1,2, 2,-12,8,-25,39,15,9,16,-55,-11, 9,11,5,10,-2,-60,8,13,-6,11, -16,27,-47,-12,11,1,16,-7,9,-3, -29,9,-14,25,-19,34,36,12,40,-10, -3,-24,-14,-37,-21,-35,-2,-36,3,-6, 67,28,6,-17,-3,-12,-16,-15,-17,-7, -59,-36,-13,1,7,1,2,10,2,11, 13,10,8,-2,7,3,5,4,2,2, -3,-8,4,-5,6,7,-42,15,35,-2, -46,38,28,-20,-9,1,7,-3,0,-2, 0,0,0,0,0,0,0,0,0,0, -15,-28,52,32,5,-5,-17,-20,-10,-1}; ================================================ FILE: deps/pjsip/third_party/speex/libspeex/exc_10_32_table.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: exc_10_32_table.c Codebook for excitation in narrowband CELP mode (4000 bps) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ const signed char exc_10_32_table[320] = { 7,17,17,27,25,22,12,4,-3,0, 28,-36,39,-24,-15,3,-9,15,-5,10, 31,-28,11,31,-21,9,-11,-11,-2,-7, -25,14,-22,31,4,-14,19,-12,14,-5, 4,-7,4,-5,9,0,-2,42,-47,-16, 1,8,0,9,23,-57,0,28,-11,6, -31,55,-45,3,-5,4,2,-2,4,-7, -3,6,-2,7,-3,12,5,8,54,-10, 8,-7,-8,-24,-25,-27,-14,-5,8,5, 44,23,5,-9,-11,-11,-13,-9,-12,-8, -29,-8,-22,6,-15,3,-12,-1,-5,-3, 34,-1,29,-16,17,-4,12,2,1,4, -2,-4,2,-1,11,-3,-52,28,30,-9, -32,25,44,-20,-24,4,6,-1,0,0, 0,0,0,0,0,0,0,0,0,0, -25,-10,22,29,13,-13,-22,-13,-4,0, -4,-16,10,15,-36,-24,28,25,-1,-3, 66,-33,-11,-15,6,0,3,4,-2,5, 24,-20,-47,29,19,-2,-4,-1,0,-1, -2,3,1,8,-11,5,5,-57,28,28, 0,-16,4,-4,12,-6,-1,2,-20,61, -9,24,-22,-42,29,6,17,8,4,2, -65,15,8,10,5,6,5,3,2,-2, -3,5,-9,4,-5,23,13,23,-3,-63, 3,-5,-4,-6,0,-3,23,-36,-46,9, 5,5,8,4,9,-5,1,-3,10,1, -6,10,-11,24,-47,31,22,-12,14,-10, 6,11,-7,-7,7,-31,51,-12,-6,7, 6,-17,9,-11,-20,52,-19,3,-6,-6, -8,-5,23,-41,37,1,-21,10,-14,8, 7,5,-15,-15,23,39,-26,-33,7,2, -32,-30,-21,-8,4,12,17,15,14,11}; ================================================ FILE: deps/pjsip/third_party/speex/libspeex/exc_20_32_table.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: exc_20_32_table.c Codebook for excitation in narrowband CELP mode (2000 bps) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ const signed char exc_20_32_table[640] = { 12,32,25,46,36,33,9,14,-3,6,1,-8,0,-10,-5,-7,-7,-7,-5,-5, 31,-27,24,-32,-4,10,-11,21,-3,19,23,-9,22,24,-10,-1,-10,-13,-7,-11, 42,-33,31,19,-8,0,-10,-16,1,-21,-17,10,-8,14,8,4,11,-2,5,-2, -33,11,-16,33,11,-4,9,-4,11,2,6,-5,8,-5,11,-4,-6,26,-36,-16, 0,4,-2,-8,12,6,-1,34,-46,-22,9,9,21,9,5,-66,-5,26,2,10, 13,2,19,9,12,-81,3,13,13,0,-14,22,-35,6,-7,-4,6,-6,10,-6, -31,38,-33,0,-10,-11,5,-12,12,-17,5,0,-6,13,-9,10,8,25,33,2, -12,8,-6,10,-2,21,7,17,43,5,11,-7,-9,-20,-36,-20,-23,-4,-4,-3, 27,-9,-9,-49,-39,-38,-11,-9,6,5,23,25,5,3,3,4,1,2,-3,-1, 87,39,17,-21,-9,-19,-9,-15,-13,-14,-17,-11,-10,-11,-8,-6,-1,-3,-3,-1, -54,-34,-27,-8,-11,-4,-5,0,0,4,8,6,9,7,9,7,6,5,5,5, 48,10,19,-10,12,-1,9,-3,2,5,-3,2,-2,-2,0,-2,-26,6,9,-7, -16,-9,2,7,7,-5,-43,11,22,-11,-9,34,37,-15,-13,-6,1,-1,1,1, -64,56,52,-11,-27,5,4,3,1,2,1,3,-1,-4,-4,-10,-7,-4,-4,2, -1,-7,-7,-12,-10,-15,-9,-5,-5,-11,-16,-13,6,16,4,-13,-16,-10,-4,2, -47,-13,25,47,19,-14,-20,-8,-17,0,-3,-13,1,6,-17,-14,15,1,10,6, -24,0,-10,19,-69,-8,14,49,17,-5,33,-29,3,-4,0,2,-8,5,-6,2, 120,-56,-12,-47,23,-9,6,-5,1,2,-5,1,-10,4,-1,-1,4,-1,0,-3, 30,-52,-67,30,22,11,-1,-4,3,0,7,2,0,1,-10,-4,-8,-13,5,1, 1,-1,5,13,-9,-3,-10,-62,22,48,-4,-6,2,3,5,1,1,4,1,13, 3,-20,10,-9,13,-2,-4,9,-20,44,-1,20,-32,-67,19,0,28,11,8,2, -11,15,-19,-53,31,2,34,10,6,-4,-58,8,10,13,14,1,12,2,0,0, -128,37,-8,44,-9,26,-3,18,2,6,11,-1,9,1,5,3,0,1,1,2, 12,3,-2,-3,7,25,9,18,-6,-37,3,-8,-16,3,-10,-7,17,-34,-44,11, 17,-15,-3,-16,-1,-13,11,-46,-65,-2,8,13,2,4,4,5,15,5,9,6, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -9,19,-12,12,-28,38,29,-1,12,2,5,23,-10,3,4,-15,21,-4,3,3, 6,17,-9,-4,-8,-20,26,5,-10,6,1,-19,18,-15,-12,47,-6,-2,-7,-9, -1,-17,-2,-2,-14,30,-14,2,-7,-4,-1,-12,11,-25,16,-3,-12,11,-7,7, -17,1,19,-28,31,-7,-10,7,-10,3,12,5,-16,6,24,41,-29,-54,0,1, 7,-1,5,-6,13,10,-4,-8,8,-9,-27,-53,-38,-1,10,19,17,16,12,12, 0,3,-7,-4,13,12,-31,-14,6,-5,3,5,17,43,50,25,10,1,-6,-2}; ================================================ FILE: deps/pjsip/third_party/speex/libspeex/exc_5_256_table.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: exc_5_256_table.c Codebook for excitation in narrowband CELP mode (12800 bps) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ const signed char exc_5_256_table[1280] = { -8,-37,5,-43,5, 73,61,39,12,-3, -61,-32,2,42,30, -3,17,-27,9,34, 20,-1,-5,2,23, -7,-46,26,53,-47, 20,-2,-33,-89,-51, -64,27,11,15,-34, -5,-56,25,-9,-1, -29,1,40,67,-23, -16,16,33,19,7, 14,85,22,-10,-10, -12,-7,-1,52,89, 29,11,-20,-37,-46, -15,17,-24,-28,24, 2,1,0,23,-101, 23,14,-1,-23,-18, 9,5,-13,38,1, -28,-28,4,27,51, -26,34,-40,35,47, 54,38,-54,-26,-6, 42,-25,13,-30,-36, 18,41,-4,-33,23, -32,-7,-4,51,-3, 17,-52,56,-47,36, -2,-21,36,10,8, -33,31,19,9,-5, -40,10,-9,-21,19, 18,-78,-18,-5,0, -26,-36,-47,-51,-44, 18,40,27,-2,29, 49,-26,2,32,-54, 30,-73,54,3,-5, 36,22,53,10,-1, -84,-53,-29,-5,3, -44,53,-51,4,22, 71,-35,-1,33,-5, -27,-7,36,17,-23, -39,16,-9,-55,-15, -20,39,-35,6,-39, -14,18,48,-64,-17, -15,9,39,81,37, -68,37,47,-21,-6, -104,13,6,9,-2, 35,8,-23,18,42, 45,21,33,-5,-49, 9,-6,-43,-56,39, 2,-16,-25,87,1, -3,-9,17,-25,-11, -9,-1,10,2,-14, -14,4,-1,-10,28, -23,40,-32,26,-9, 26,4,-27,-23,3, 42,-60,1,49,-3, 27,10,-52,-40,-2, 18,45,-23,17,-44, 3,-3,17,-46,52, -40,-47,25,75,31, -49,53,30,-30,-32, -36,38,-6,-15,-16, 54,-27,-48,3,38, -29,-32,-22,-14,-4, -23,-13,32,-39,9, 8,-45,-13,34,-16, 49,40,32,31,28, 23,23,32,47,59, -68,8,62,44,25, -14,-24,-65,-16,36, 67,-25,-38,-21,4, -33,-2,42,5,-63, 40,11,26,-42,-23, -61,79,-31,23,-20, 10,-32,53,-25,-36, 10,-26,-5,3,0, -71,5,-10,-37,1, -24,21,-54,-17,1, -29,-25,-15,-27,32, 68,45,-16,-37,-18, -5,1,0,-77,71, -6,3,-20,71,-67, 29,-35,10,-30,19, 4,16,17,5,0, -14,19,2,28,26, 59,3,2,24,39, 55,-50,-45,-18,-17, 33,-35,14,-1,1, 8,87,-35,-29,0, -27,13,-7,23,-13, 37,-40,50,-35,14, 19,-7,-14,49,54, -5,22,-2,-29,-8, -27,38,13,27,48, 12,-41,-21,-15,28, 7,-16,-24,-19,-20, 11,-20,9,2,13, 23,-20,11,27,-27, 71,-69,8,2,-6, 22,12,16,16,9, -16,-8,-17,1,25, 1,40,-37,-33,66, 94,53,4,-22,-25, -41,-42,25,35,-16, -15,57,31,-29,-32, 21,16,-60,45,15, -1,7,57,-26,-47, -29,11,8,15,19, -105,-8,54,27,10, -17,6,-12,-1,-10, 4,0,23,-10,31, 13,11,10,12,-64, 23,-3,-8,-19,16, 52,24,-40,16,10, 40,5,9,0,-13, -7,-21,-8,-6,-7, -21,59,16,-53,18, -60,11,-47,14,-18, 25,-13,-24,4,-39, 16,-28,54,26,-67, 30,27,-20,-52,20, -12,55,12,18,-16, 39,-14,-6,-26,56, -88,-55,12,25,26, -37,6,75,0,-34, -81,54,-30,1,-7, 49,-23,-14,21,10, -62,-58,-57,-47,-34, 15,-4,34,-78,31, 25,-11,7,50,-10, 42,-63,14,-36,-4, 57,55,57,53,42, -42,-1,15,40,37, 15,25,-11,6,1, 31,-2,-6,-1,-7, -64,34,28,30,-1, 3,21,0,-88,-12, -56,25,-28,40,8, -28,-14,9,12,2, -6,-17,22,49,-6, -26,14,28,-20,4, -12,50,35,40,13, -38,-58,-29,17,30, 22,60,26,-54,-39, -12,58,-28,-63,10, -21,-8,-12,26,-62, 6,-10,-11,-22,-6, -7,4,1,18,2, -70,11,14,4,13, 19,-24,-34,24,67, 17,51,-21,13,23, 54,-30,48,1,-13, 80,26,-16,-2,13, -4,6,-30,29,-24, 73,-58,30,-27,20, -2,-21,41,45,30, -27,-3,-5,-18,-20, -49,-3,-35,10,42, -19,-67,-53,-11,9, 13,-15,-33,-51,-30, 15,7,25,-30,4, 28,-22,-34,54,-29, 39,-46,20,16,34, -4,47,75,1,-44, -55,-24,7,-1,9, -42,50,-8,-36,41, 68,0,-4,-10,-23, -15,-50,64,36,-9, -27,12,25,-38,-47, -37,32,-49,51,-36, 2,-4,69,-26,19, 7,45,67,46,13, -63,46,15,-47,4, -41,13,-6,5,-21, 37,26,-55,-7,33, -1,-28,10,-17,-64, -14,0,-36,-17,93, -3,-9,-66,44,-21, 3,-12,38,-6,-13, -12,19,13,43,-43, -10,-12,6,-5,9, -49,32,-5,2,4, 5,15,-16,10,-21, 8,-62,-8,64,8, 79,-1,-66,-49,-18, 5,40,-5,-30,-45, 1,-6,21,-32,93, -18,-30,-21,32,21, -18,22,8,5,-41, -54,80,22,-10,-7, -8,-23,-64,66,56, -14,-30,-41,-46,-14, -29,-37,27,-14,42, -2,-9,-29,34,14, 33,-14,22,4,10, 26,26,28,32,23, -72,-32,3,0,-14, 35,-42,-78,-32,6, 29,-18,-45,-5,7, -33,-45,-3,-22,-34, 8,-8,4,-51,-25, -9,59,-78,21,-5, -25,-48,66,-15,-17, -24,-49,-13,25,-23, -64,-6,40,-24,-19, -11,57,-33,-8,1, 10,-52,-54,28,39, 49,34,-11,-61,-41, -43,10,15,-15,51, 30,15,-51,32,-34, -2,-34,14,18,16, 1,1,-3,-3,1, 1,-18,6,16,48, 12,-5,-42,7,36, 48,7,-20,-10,7, 12,2,54,39,-38, 37,54,4,-11,-8, -46,-10,5,-10,-34, 46,-12,29,-37,39, 36,-11,24,56,17, 14,20,25,0,-25, -28,55,-7,-5,27, 3,9,-26,-8,6, -24,-10,-30,-31,-34, 18,4,22,21,40, -1,-29,-37,-8,-21, 92,-29,11,-3,11, 73,23,22,7,4, -44,-9,-11,21,-13, 11,9,-78,-1,47, 114,-12,-37,-19,-5, -11,-22,19,12,-30, 7,38,45,-21,-8, -9,55,-45,56,-21, 7,17,46,-57,-87, -6,27,31,31,7, -56,-12,46,21,-5, -12,36,3,3,-21, 43,19,12,-7,9, -14,0,-9,-33,-91, 7,26,3,-11,64, 83,-31,-46,25,2, 9,5,2,2,-1, 20,-17,10,-5,-27, -8,20,8,-19,16, -21,-13,-31,5,5, 42,24,9,34,-20, 28,-61,22,11,-39, 64,-20,-1,-30,-9, -20,24,-25,-24,-29, 22,-60,6,-5,41, -9,-87,14,34,15, -57,52,69,15,-3, -102,58,16,3,6, 60,-75,-32,26,7, -57,-27,-32,-24,-21, -29,-16,62,-46,31, 30,-27,-15,7,15}; ================================================ FILE: deps/pjsip/third_party/speex/libspeex/exc_5_64_table.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: exc_5_64_table.c Codebook for excitation in narrowband CELP mode (9600 bps) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ const signed char exc_5_64_table[320]={ 1,5,-15,49,-66, -48,-4,50,-44,7, 37,16,-18,25,-26, -26,-15,19,19,-27, -47,28,57,5,-17, -32,-41,68,21,-2, 64,56,8,-16,-13, -26,-9,-16,11,6, -39,25,-19,22,-31, 20,-45,55,-43,10, -16,47,-40,40,-20, -51,3,-17,-14,-15, -24,53,-20,-46,46, 27,-68,32,3,-18, -5,9,-31,16,-9, -10,-1,-23,48,95, 47,25,-41,-32,-3, 15,-25,-55,36,41, -27,20,5,13,14, -22,5,2,-23,18, 46,-15,17,-18,-34, -5,-8,27,-55,73, 16,2,-1,-17,40, -78,33,0,2,19, 4,53,-16,-15,-16, -28,-3,-13,49,8, -7,-29,27,-13,32, 20,32,-61,16,14, 41,44,40,24,20, 7,4,48,-60,-77, 17,-6,-48,65,-15, 32,-30,-71,-10,-3, -6,10,-2,-7,-29, -56,67,-30,7,-5, 86,-6,-10,0,5, -31,60,34,-38,-3, 24,10,-2,30,23, 24,-41,12,70,-43, 15,-17,6,13,16, -13,8,30,-15,-8, 5,23,-34,-98,-4, -13,13,-48,-31,70, 12,31,25,24,-24, 26,-7,33,-16,8, 5,-11,-14,-8,-65, 13,10,-2,-9,0, -3,-68,5,35,7, 0,-31,-1,-17,-9, -9,16,-37,-18,-1, 69,-48,-28,22,-21, -11,5,49,55,23, -86,-36,16,2,13, 63,-51,30,-11,13, 24,-18,-6,14,-19, 1,41,9,-5,27, -36,-44,-34,-37,-21, -26,31,-39,15,43, 5,-8,29,20,-8, -20,-52,-28,-1,13, 26,-34,-10,-9,27, -8,8,27,-66,4, 12,-22,49,10,-77, 32,-18,3,-38,12, -3,-1,2,2,0}; ================================================ FILE: deps/pjsip/third_party/speex/libspeex/exc_8_128_table.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: exc_8_128_table.c Codebook for excitation in narrowband CELP mode (7000 bps) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ const signed char exc_8_128_table[1024] = { -14,9,13,-32,2,-10,31,-10, -8,-8,6,-4,-1,10,-64,23, 6,20,13,6,8,-22,16,34, 7,42,-49,-28,5,26,4,-15, 41,34,41,32,33,24,23,14, 8,40,34,4,-24,-41,-19,-15, 13,-13,33,-54,24,27,-44,33, 27,-15,-15,24,-19,14,-36,14, -9,24,-12,-4,37,-5,16,-34, 5,10,33,-15,-54,-16,12,25, 12,1,2,0,3,-1,-4,-4, 11,2,-56,54,27,-20,13,-6, -46,-41,-33,-11,-5,7,12,14, -14,-5,8,20,6,3,4,-8, -5,-42,11,8,-14,25,-2,2, 13,11,-22,39,-9,9,5,-45, -9,7,-9,12,-7,34,-17,-102, 7,2,-42,18,35,-9,-34,11, -5,-2,3,22,46,-52,-25,-9, -94,8,11,-5,-5,-5,4,-7, -35,-7,54,5,-32,3,24,-9, -22,8,65,37,-1,-12,-23,-6, -9,-28,55,-33,14,-3,2,18, -60,41,-17,8,-16,17,-11,0, -11,29,-28,37,9,-53,33,-14, -9,7,-25,-7,-11,26,-32,-8, 24,-21,22,-19,19,-10,29,-14, 0,0,0,0,0,0,0,0, -5,-52,10,41,6,-30,-4,16, 32,22,-27,-22,32,-3,-28,-3, 3,-35,6,17,23,21,8,2, 4,-45,-17,14,23,-4,-31,-11, -3,14,1,19,-11,2,61,-8, 9,-12,7,-10,12,-3,-24,99, -48,23,50,-37,-5,-23,0,8, -14,35,-64,-5,46,-25,13,-1, -49,-19,-15,9,34,50,25,11, -6,-9,-16,-20,-32,-33,-32,-27, 10,-8,12,-15,56,-14,-32,33, 3,-9,1,65,-9,-9,-10,-2, -6,-23,9,17,3,-28,13,-32, 4,-2,-10,4,-16,76,12,-52, 6,13,33,-6,4,-14,-9,-3, 1,-15,-16,28,1,-15,11,16, 9,4,-21,-37,-40,-6,22,12, -15,-23,-14,-17,-16,-9,-10,-9, 13,-39,41,5,-9,16,-38,25, 46,-47,4,49,-14,17,-2,6, 18,5,-6,-33,-22,44,50,-2, 1,3,-6,7,7,-3,-21,38, -18,34,-14,-41,60,-13,6,16, -24,35,19,-13,-36,24,3,-17, -14,-10,36,44,-44,-29,-3,3, -54,-8,12,55,26,4,-2,-5, 2,-11,22,-23,2,22,1,-25, -39,66,-49,21,-8,-2,10,-14, -60,25,6,10,27,-25,16,5, -2,-9,26,-13,-20,58,-2,7, 52,-9,2,5,-4,-15,23,-1, -38,23,8,27,-6,0,-27,-7, 39,-10,-14,26,11,-45,-12,9, -5,34,4,-35,10,43,-22,-11, 56,-7,20,1,10,1,-26,9, 94,11,-27,-14,-13,1,-11,0, 14,-5,-6,-10,-4,-15,-8,-41, 21,-5,1,-28,-8,22,-9,33, -23,-4,-4,-12,39,4,-7,3, -60,80,8,-17,2,-6,12,-5, 1,9,15,27,31,30,27,23, 61,47,26,10,-5,-8,-12,-13, 5,-18,25,-15,-4,-15,-11,12, -2,-2,-16,-2,-6,24,12,11, -4,9,1,-9,14,-45,57,12, 20,-35,26,11,-64,32,-10,-10, 42,-4,-9,-16,32,24,7,10, 52,-11,-57,29,0,8,0,-6, 17,-17,-56,-40,7,20,18,12, -6,16,5,7,-1,9,1,10, 29,12,16,13,-2,23,7,9, -3,-4,-5,18,-64,13,55,-25, 9,-9,24,14,-25,15,-11,-40, -30,37,1,-19,22,-5,-31,13, -2,0,7,-4,16,-67,12,66, -36,24,-8,18,-15,-23,19,0, -45,-7,4,3,-13,13,35,5, 13,33,10,27,23,0,-7,-11, 43,-74,36,-12,2,5,-8,6, -33,11,-16,-14,-5,-7,-3,17, -34,27,-16,11,-9,15,33,-31, 8,-16,7,-6,-7,63,-55,-17, 11,-1,20,-46,34,-30,6,9, 19,28,-9,5,-24,-8,-23,-2, 31,-19,-16,-5,-15,-18,0,26, 18,37,-5,-15,-2,17,5,-27, 21,-33,44,12,-27,-9,17,11, 25,-21,-31,-7,13,33,-8,-25, -7,7,-10,4,-6,-9,48,-82, -23,-8,6,11,-23,3,-3,49, -29,25,31,4,14,16,9,-4, -18,10,-26,3,5,-44,-9,9, -47,-55,15,9,28,1,4,-3, 46,6,-6,-38,-29,-31,-15,-6, 3,0,14,-6,8,-54,-50,33, -5,1,-14,33,-48,26,-4,-5, -3,-5,-3,-5,-28,-22,77,55, -1,2,10,10,-9,-14,-66,-49, 11,-36,-6,-20,10,-10,16,12, 4,-1,-16,45,-44,-50,31,-2, 25,42,23,-32,-22,0,11,20, -40,-35,-40,-36,-32,-26,-21,-13, 52,-22,6,-24,-20,17,-5,-8, 36,-25,-11,21,-26,6,34,-8, 7,20,-3,5,-25,-8,18,-5, -9,-4,1,-9,20,20,39,48, -24,9,5,-65,22,29,4,3, -43,-11,32,-6,9,19,-27,-10, -47,-14,24,10,-7,-36,-7,-1, -4,-5,-5,16,53,25,-26,-29, -4,-12,45,-58,-34,33,-5,2, -1,27,-48,31,-15,22,-5,4, 7,7,-25,-3,11,-22,16,-12, 8,-3,7,-11,45,14,-73,-19, 56,-46,24,-20,28,-12,-2,-1, -36,-3,-33,19,-6,7,2,-15, 5,-31,-45,8,35,13,20,0, -9,48,-13,-43,-3,-13,2,-5, 72,-68,-27,2,1,-2,-7,5, 36,33,-40,-12,-4,-5,23,19}; ================================================ FILE: deps/pjsip/third_party/speex/libspeex/fftwrap.c ================================================ /* Copyright (C) 2005-2006 Jean-Marc Valin File: fftwrap.c Wrapper for various FFTs Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "arch.h" #include "os_support.h" #define MAX_FFT_SIZE 2048 #ifdef FIXED_POINT static int maximize_range(spx_word16_t *in, spx_word16_t *out, spx_word16_t bound, int len) { int i, shift; spx_word16_t max_val = 0; for (i=0;imax_val) max_val = in[i]; if (-in[i]>max_val) max_val = -in[i]; } shift=0; while (max_val <= (bound>>1) && max_val != 0) { max_val <<= 1; shift++; } for (i=0;i void *spx_fft_init(int size) { struct drft_lookup *table; table = speex_alloc(sizeof(struct drft_lookup)); spx_drft_init((struct drft_lookup *)table, size); return (void*)table; } void spx_fft_destroy(void *table) { spx_drft_clear(table); speex_free(table); } void spx_fft(void *table, float *in, float *out) { if (in==out) { int i; float scale = 1./((struct drft_lookup *)table)->n; speex_warning("FFT should not be done in-place"); for (i=0;i<((struct drft_lookup *)table)->n;i++) out[i] = scale*in[i]; } else { int i; float scale = 1./((struct drft_lookup *)table)->n; for (i=0;i<((struct drft_lookup *)table)->n;i++) out[i] = scale*in[i]; } spx_drft_forward((struct drft_lookup *)table, out); } void spx_ifft(void *table, float *in, float *out) { if (in==out) { speex_warning("FFT should not be done in-place"); } else { int i; for (i=0;i<((struct drft_lookup *)table)->n;i++) out[i] = in[i]; } spx_drft_backward((struct drft_lookup *)table, out); } #elif defined(USE_INTEL_MKL) #include struct mkl_config { DFTI_DESCRIPTOR_HANDLE desc; int N; }; void *spx_fft_init(int size) { struct mkl_config *table = (struct mkl_config *) speex_alloc(sizeof(struct mkl_config)); table->N = size; DftiCreateDescriptor(&table->desc, DFTI_SINGLE, DFTI_REAL, 1, size); DftiSetValue(table->desc, DFTI_PACKED_FORMAT, DFTI_PACK_FORMAT); DftiSetValue(table->desc, DFTI_PLACEMENT, DFTI_NOT_INPLACE); DftiSetValue(table->desc, DFTI_FORWARD_SCALE, 1.0f / size); DftiCommitDescriptor(table->desc); return table; } void spx_fft_destroy(void *table) { struct mkl_config *t = (struct mkl_config *) table; DftiFreeDescriptor(t->desc); speex_free(table); } void spx_fft(void *table, spx_word16_t *in, spx_word16_t *out) { struct mkl_config *t = (struct mkl_config *) table; DftiComputeForward(t->desc, in, out); } void spx_ifft(void *table, spx_word16_t *in, spx_word16_t *out) { struct mkl_config *t = (struct mkl_config *) table; DftiComputeBackward(t->desc, in, out); } #elif defined(USE_GPL_FFTW3) #include struct fftw_config { float *in; float *out; fftwf_plan fft; fftwf_plan ifft; int N; }; void *spx_fft_init(int size) { struct fftw_config *table = (struct fftw_config *) speex_alloc(sizeof(struct fftw_config)); table->in = fftwf_malloc(sizeof(float) * (size+2)); table->out = fftwf_malloc(sizeof(float) * (size+2)); table->fft = fftwf_plan_dft_r2c_1d(size, table->in, (fftwf_complex *) table->out, FFTW_PATIENT); table->ifft = fftwf_plan_dft_c2r_1d(size, (fftwf_complex *) table->in, table->out, FFTW_PATIENT); table->N = size; return table; } void spx_fft_destroy(void *table) { struct fftw_config *t = (struct fftw_config *) table; fftwf_destroy_plan(t->fft); fftwf_destroy_plan(t->ifft); fftwf_free(t->in); fftwf_free(t->out); speex_free(table); } void spx_fft(void *table, spx_word16_t *in, spx_word16_t *out) { int i; struct fftw_config *t = (struct fftw_config *) table; const int N = t->N; float *iptr = t->in; float *optr = t->out; const float m = 1.0 / N; for(i=0;ifft); out[0] = optr[0]; for(i=1;iN; float *iptr = t->in; float *optr = t->out; iptr[0] = in[0]; iptr[1] = 0.0f; for(i=1;iifft); for(i=0;iforward = kiss_fftr_alloc(size,0,NULL,NULL); table->backward = kiss_fftr_alloc(size,1,NULL,NULL); table->N = size; return table; } void spx_fft_destroy(void *table) { struct kiss_config *t = (struct kiss_config *)table; kiss_fftr_free(t->forward); kiss_fftr_free(t->backward); speex_free(table); } #ifdef FIXED_POINT void spx_fft(void *table, spx_word16_t *in, spx_word16_t *out) { int shift; struct kiss_config *t = (struct kiss_config *)table; shift = maximize_range(in, in, 32000, t->N); kiss_fftr2(t->forward, in, out); renorm_range(in, in, shift, t->N); renorm_range(out, out, shift, t->N); } #else void spx_fft(void *table, spx_word16_t *in, spx_word16_t *out) { int i; float scale; struct kiss_config *t = (struct kiss_config *)table; scale = 1./t->N; kiss_fftr2(t->forward, in, out); for (i=0;iN;i++) out[i] *= scale; } #endif void spx_ifft(void *table, spx_word16_t *in, spx_word16_t *out) { struct kiss_config *t = (struct kiss_config *)table; kiss_fftri2(t->backward, in, out); } #else #error No other FFT implemented #endif #ifdef FIXED_POINT /*#include "smallft.h"*/ void spx_fft_float(void *table, float *in, float *out) { int i; #ifdef USE_SMALLFT int N = ((struct drft_lookup *)table)->n; #elif defined(USE_KISS_FFT) int N = ((struct kiss_config *)table)->N; #else #endif #ifdef VAR_ARRAYS spx_word16_t _in[N]; spx_word16_t _out[N]; #else spx_word16_t _in[MAX_FFT_SIZE]; spx_word16_t _out[MAX_FFT_SIZE]; #endif for (i=0;iN); scale = 1./((struct kiss_config *)table)->N; for (i=0;i<((struct kiss_config *)table)->N;i++) out[i] = scale*in[i]; spx_drft_forward(&t, out); spx_drft_clear(&t); } #endif } void spx_ifft_float(void *table, float *in, float *out) { int i; #ifdef USE_SMALLFT int N = ((struct drft_lookup *)table)->n; #elif defined(USE_KISS_FFT) int N = ((struct kiss_config *)table)->N; #else #endif #ifdef VAR_ARRAYS spx_word16_t _in[N]; spx_word16_t _out[N]; #else spx_word16_t _in[MAX_FFT_SIZE]; spx_word16_t _out[MAX_FFT_SIZE]; #endif for (i=0;iN); for (i=0;i<((struct kiss_config *)table)->N;i++) out[i] = in[i]; spx_drft_backward(&t, out); spx_drft_clear(&t); } #endif } #else void spx_fft_float(void *table, float *in, float *out) { spx_fft(table, in, out); } void spx_ifft_float(void *table, float *in, float *out) { spx_ifft(table, in, out); } #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/fftwrap.h ================================================ /* Copyright (C) 2005 Jean-Marc Valin File: fftwrap.h Wrapper for various FFTs Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FFTWRAP_H #define FFTWRAP_H #include "arch.h" /** Compute tables for an FFT */ void *spx_fft_init(int size); /** Destroy tables for an FFT */ void spx_fft_destroy(void *table); /** Forward (real to half-complex) transform */ void spx_fft(void *table, spx_word16_t *in, spx_word16_t *out); /** Backward (half-complex to real) transform */ void spx_ifft(void *table, spx_word16_t *in, spx_word16_t *out); /** Forward (real to half-complex) transform of float data */ void spx_fft_float(void *table, float *in, float *out); /** Backward (half-complex to real) transform of float data */ void spx_ifft_float(void *table, float *in, float *out); #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/filterbank.c ================================================ /* Copyright (C) 2006 Jean-Marc Valin */ /** @file filterbank.c @brief Converting between psd and filterbank */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "filterbank.h" #include "arch.h" #include #include "math_approx.h" #include "os_support.h" #ifdef FIXED_POINT #define toBARK(n) (MULT16_16(26829,spx_atan(SHR32(MULT16_16(97,n),2))) + MULT16_16(4588,spx_atan(MULT16_32_Q15(20,MULT16_16(n,n)))) + MULT16_16(3355,n)) #else #define toBARK(n) (13.1f*atan(.00074f*(n))+2.24f*atan((n)*(n)*1.85e-8f)+1e-4f*(n)) #endif #define toMEL(n) (2595.f*log10(1.f+(n)/700.f)) FilterBank *filterbank_new(int banks, spx_word32_t sampling, int len, int type) { FilterBank *bank; spx_word32_t df; spx_word32_t max_mel, mel_interval; int i; int id1; int id2; df = DIV32(SHL32(sampling,15),MULT16_16(2,len)); max_mel = toBARK(EXTRACT16(sampling/2)); mel_interval = PDIV32(max_mel,banks-1); bank = (FilterBank*)speex_alloc(sizeof(FilterBank)); bank->nb_banks = banks; bank->len = len; bank->bank_left = (int*)speex_alloc(len*sizeof(int)); bank->bank_right = (int*)speex_alloc(len*sizeof(int)); bank->filter_left = (spx_word16_t*)speex_alloc(len*sizeof(spx_word16_t)); bank->filter_right = (spx_word16_t*)speex_alloc(len*sizeof(spx_word16_t)); /* Think I can safely disable normalisation that for fixed-point (and probably float as well) */ #ifndef FIXED_POINT bank->scaling = (float*)speex_alloc(banks*sizeof(float)); #endif for (i=0;i max_mel) break; #ifdef FIXED_POINT id1 = DIV32(mel,mel_interval); #else id1 = (int)(floor(mel/mel_interval)); #endif if (id1>banks-2) { id1 = banks-2; val = Q15_ONE; } else { val = DIV32_16(mel - id1*mel_interval,EXTRACT16(PSHR32(mel_interval,15))); } id2 = id1+1; bank->bank_left[i] = id1; bank->filter_left[i] = SUB16(Q15_ONE,val); bank->bank_right[i] = id2; bank->filter_right[i] = val; } /* Think I can safely disable normalisation for fixed-point (and probably float as well) */ #ifndef FIXED_POINT for (i=0;inb_banks;i++) bank->scaling[i] = 0; for (i=0;ilen;i++) { int id = bank->bank_left[i]; bank->scaling[id] += bank->filter_left[i]; id = bank->bank_right[i]; bank->scaling[id] += bank->filter_right[i]; } for (i=0;inb_banks;i++) bank->scaling[i] = Q15_ONE/(bank->scaling[i]); #endif return bank; } void filterbank_destroy(FilterBank *bank) { speex_free(bank->bank_left); speex_free(bank->bank_right); speex_free(bank->filter_left); speex_free(bank->filter_right); #ifndef FIXED_POINT speex_free(bank->scaling); #endif speex_free(bank); } void filterbank_compute_bank32(FilterBank *bank, spx_word32_t *ps, spx_word32_t *mel) { int i; for (i=0;inb_banks;i++) mel[i] = 0; for (i=0;ilen;i++) { int id; id = bank->bank_left[i]; mel[id] += MULT16_32_P15(bank->filter_left[i],ps[i]); id = bank->bank_right[i]; mel[id] += MULT16_32_P15(bank->filter_right[i],ps[i]); } /* Think I can safely disable normalisation that for fixed-point (and probably float as well) */ #ifndef FIXED_POINT /*for (i=0;inb_banks;i++) mel[i] = MULT16_32_P15(Q15(bank->scaling[i]),mel[i]); */ #endif } void filterbank_compute_psd16(FilterBank *bank, spx_word16_t *mel, spx_word16_t *ps) { int i; for (i=0;ilen;i++) { spx_word32_t tmp; int id1, id2; id1 = bank->bank_left[i]; id2 = bank->bank_right[i]; tmp = MULT16_16(mel[id1],bank->filter_left[i]); tmp += MULT16_16(mel[id2],bank->filter_right[i]); ps[i] = EXTRACT16(PSHR32(tmp,15)); } } #ifndef FIXED_POINT void filterbank_compute_bank(FilterBank *bank, float *ps, float *mel) { int i; for (i=0;inb_banks;i++) mel[i] = 0; for (i=0;ilen;i++) { int id = bank->bank_left[i]; mel[id] += bank->filter_left[i]*ps[i]; id = bank->bank_right[i]; mel[id] += bank->filter_right[i]*ps[i]; } for (i=0;inb_banks;i++) mel[i] *= bank->scaling[i]; } void filterbank_compute_psd(FilterBank *bank, float *mel, float *ps) { int i; for (i=0;ilen;i++) { int id = bank->bank_left[i]; ps[i] = mel[id]*bank->filter_left[i]; id = bank->bank_right[i]; ps[i] += mel[id]*bank->filter_right[i]; } } void filterbank_psy_smooth(FilterBank *bank, float *ps, float *mask) { /* Low freq slope: 14 dB/Bark*/ /* High freq slope: 9 dB/Bark*/ /* Noise vs tone: 5 dB difference */ /* FIXME: Temporary kludge */ float bark[100]; int i; /* Assumes 1/3 Bark resolution */ float decay_low = 0.34145f; float decay_high = 0.50119f; filterbank_compute_bank(bank, ps, bark); for (i=1;inb_banks;i++) { /*float decay_high = 13-1.6*log10(bark[i-1]); decay_high = pow(10,(-decay_high/30.f));*/ bark[i] = bark[i] + decay_high*bark[i-1]; } for (i=bank->nb_banks-2;i>=0;i--) { bark[i] = bark[i] + decay_low*bark[i+1]; } filterbank_compute_psd(bank, bark, mask); } #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/filterbank.h ================================================ /* Copyright (C) 2006 Jean-Marc Valin */ /** @file filterbank.h @brief Converting between psd and filterbank */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FILTERBANK_H #define FILTERBANK_H #include "arch.h" typedef struct { int *bank_left; int *bank_right; spx_word16_t *filter_left; spx_word16_t *filter_right; #ifndef FIXED_POINT float *scaling; #endif int nb_banks; int len; } FilterBank; FilterBank *filterbank_new(int banks, spx_word32_t sampling, int len, int type); void filterbank_destroy(FilterBank *bank); void filterbank_compute_bank32(FilterBank *bank, spx_word32_t *ps, spx_word32_t *mel); void filterbank_compute_psd16(FilterBank *bank, spx_word16_t *mel, spx_word16_t *psd); #ifndef FIXED_POINT void filterbank_compute_bank(FilterBank *bank, float *psd, float *mel); void filterbank_compute_psd(FilterBank *bank, float *mel, float *psd); #endif #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/filters.c ================================================ /* Copyright (C) 2002-2006 Jean-Marc Valin File: filters.c Various analysis/synthesis filters Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "filters.h" #include "stack_alloc.h" #include "arch.h" #include "math_approx.h" #include "ltp.h" #include #ifdef _USE_SSE #include "filters_sse.h" #elif defined (ARM4_ASM) || defined(ARM5E_ASM) #include "filters_arm4.h" #elif defined (BFIN_ASM) #include "filters_bfin.h" #endif void bw_lpc(spx_word16_t gamma, const spx_coef_t *lpc_in, spx_coef_t *lpc_out, int order) { int i; spx_word16_t tmp=gamma; for (i=0;i=min_val && vec[i] <= max_val)) { if (vec[i] < min_val) vec[i] = min_val; else if (vec[i] > max_val) vec[i] = max_val; else /* Has to be NaN */ vec[i] = 0; } } } void highpass(const spx_word16_t *x, spx_word16_t *y, int len, int filtID, spx_mem_t *mem) { int i; #ifdef FIXED_POINT const spx_word16_t Pcoef[5][3] = {{16384, -31313, 14991}, {16384, -31569, 15249}, {16384, -31677, 15328}, {16384, -32313, 15947}, {16384, -22446, 6537}}; const spx_word16_t Zcoef[5][3] = {{15672, -31344, 15672}, {15802, -31601, 15802}, {15847, -31694, 15847}, {16162, -32322, 16162}, {14418, -28836, 14418}}; #else const spx_word16_t Pcoef[5][3] = {{1.00000f, -1.91120f, 0.91498f}, {1.00000f, -1.92683f, 0.93071f}, {1.00000f, -1.93338f, 0.93553f}, {1.00000f, -1.97226f, 0.97332f}, {1.00000f, -1.37000f, 0.39900f}}; const spx_word16_t Zcoef[5][3] = {{0.95654f, -1.91309f, 0.95654f}, {0.96446f, -1.92879f, 0.96446f}, {0.96723f, -1.93445f, 0.96723f}, {0.98645f, -1.97277f, 0.98645f}, {0.88000f, -1.76000f, 0.88000f}}; #endif const spx_word16_t *den, *num; if (filtID>4) filtID=4; den = Pcoef[filtID]; num = Zcoef[filtID]; /*return;*/ for (i=0;i SHL32(EXTEND32(SIG_SCALING), 8)) { spx_word16_t scale_1; scale = PSHR32(scale, SIG_SHIFT); scale_1 = EXTRACT16(PDIV32_16(SHL32(EXTEND32(SIG_SCALING),7),scale)); for (i=0;i SHR32(EXTEND32(SIG_SCALING), 2)) { spx_word16_t scale_1; scale = PSHR32(scale, SIG_SHIFT-5); scale_1 = DIV32_16(SHL32(EXTEND32(SIG_SCALING),3),scale); for (i=0;i max_val) max_val = tmp; } sig_shift=0; while (max_val>16383) { sig_shift++; max_val >>= 1; } for (i=0;i max_val) max_val = tmp; } if (max_val>16383) { spx_word32_t sum=0; for (i=0;i= max_val) max_val = tmp; } sig_shift=0; while (max_val>max_scale) { sig_shift++; max_val >>= 1; } for (i=0;i>1; for (i=0;i>1; N2 = N>>1; ALLOC(xx1, M2+N2, spx_word16_t); ALLOC(xx2, M2+N2, spx_word16_t); for (i = 0; i < N2; i++) xx1[i] = x1[N2-1-i]; for (i = 0; i < M2; i++) xx1[N2+i] = mem1[2*i+1]; for (i = 0; i < N2; i++) xx2[i] = x2[N2-1-i]; for (i = 0; i < M2; i++) xx2[N2+i] = mem2[2*i+1]; for (i = 0; i < N2; i += 2) { spx_sig_t y0, y1, y2, y3; spx_word16_t x10, x20; y0 = y1 = y2 = y3 = 0; x10 = xx1[N2-2-i]; x20 = xx2[N2-2-i]; for (j = 0; j < M2; j += 2) { spx_word16_t x11, x21; spx_word16_t a0, a1; a0 = a[2*j]; a1 = a[2*j+1]; x11 = xx1[N2-1+j-i]; x21 = xx2[N2-1+j-i]; #ifdef FIXED_POINT /* We multiply twice by the same coef to avoid overflows */ y0 = MAC16_16(MAC16_16(y0, a0, x11), NEG16(a0), x21); y1 = MAC16_16(MAC16_16(y1, a1, x11), a1, x21); y2 = MAC16_16(MAC16_16(y2, a0, x10), NEG16(a0), x20); y3 = MAC16_16(MAC16_16(y3, a1, x10), a1, x20); #else y0 = ADD32(y0,MULT16_16(a0, x11-x21)); y1 = ADD32(y1,MULT16_16(a1, x11+x21)); y2 = ADD32(y2,MULT16_16(a0, x10-x20)); y3 = ADD32(y3,MULT16_16(a1, x10+x20)); #endif a0 = a[2*j+2]; a1 = a[2*j+3]; x10 = xx1[N2+j-i]; x20 = xx2[N2+j-i]; #ifdef FIXED_POINT /* We multiply twice by the same coef to avoid overflows */ y0 = MAC16_16(MAC16_16(y0, a0, x10), NEG16(a0), x20); y1 = MAC16_16(MAC16_16(y1, a1, x10), a1, x20); y2 = MAC16_16(MAC16_16(y2, a0, x11), NEG16(a0), x21); y3 = MAC16_16(MAC16_16(y3, a1, x11), a1, x21); #else y0 = ADD32(y0,MULT16_16(a0, x10-x20)); y1 = ADD32(y1,MULT16_16(a1, x10+x20)); y2 = ADD32(y2,MULT16_16(a0, x11-x21)); y3 = ADD32(y3,MULT16_16(a1, x11+x21)); #endif } #ifdef FIXED_POINT y[2*i] = EXTRACT16(SATURATE32(PSHR32(y0,15),32767)); y[2*i+1] = EXTRACT16(SATURATE32(PSHR32(y1,15),32767)); y[2*i+2] = EXTRACT16(SATURATE32(PSHR32(y2,15),32767)); y[2*i+3] = EXTRACT16(SATURATE32(PSHR32(y3,15),32767)); #else /* Normalize up explicitly if we're in float */ y[2*i] = 2.f*y0; y[2*i+1] = 2.f*y1; y[2*i+2] = 2.f*y2; y[2*i+3] = 2.f*y3; #endif } for (i = 0; i < M2; i++) mem1[2*i+1] = xx1[i]; for (i = 0; i < M2; i++) mem2[2*i+1] = xx2[i]; } #ifdef FIXED_POINT #if 0 const spx_word16_t shift_filt[3][7] = {{-33, 1043, -4551, 19959, 19959, -4551, 1043}, {-98, 1133, -4425, 29179, 8895, -2328, 444}, {444, -2328, 8895, 29179, -4425, 1133, -98}}; #else const spx_word16_t shift_filt[3][7] = {{-390, 1540, -4993, 20123, 20123, -4993, 1540}, {-1064, 2817, -6694, 31589, 6837, -990, -209}, {-209, -990, 6837, 31589, -6694, 2817, -1064}}; #endif #else #if 0 const float shift_filt[3][7] = {{-9.9369e-04, 3.1831e-02, -1.3889e-01, 6.0910e-01, 6.0910e-01, -1.3889e-01, 3.1831e-02}, {-0.0029937, 0.0345613, -0.1350474, 0.8904793, 0.2714479, -0.0710304, 0.0135403}, {0.0135403, -0.0710304, 0.2714479, 0.8904793, -0.1350474, 0.0345613, -0.0029937}}; #else const float shift_filt[3][7] = {{-0.011915f, 0.046995f, -0.152373f, 0.614108f, 0.614108f, -0.152373f, 0.046995f}, {-0.0324855f, 0.0859768f, -0.2042986f, 0.9640297f, 0.2086420f, -0.0302054f, -0.0063646f}, {-0.0063646f, -0.0302054f, 0.2086420f, 0.9640297f, -0.2042986f, 0.0859768f, -0.0324855f}}; #endif #endif int interp_pitch( spx_word16_t *exc, /*decoded excitation*/ spx_word16_t *interp, /*decoded excitation*/ int pitch, /*pitch period*/ int len ) { int i,j,k; spx_word32_t corr[4][7]; spx_word32_t maxcorr; int maxi, maxj; for (i=0;i<7;i++) { corr[0][i] = inner_prod(exc, exc-pitch-3+i, len); } for (i=0;i<3;i++) { for (j=0;j<7;j++) { int i1, i2; spx_word32_t tmp=0; i1 = 3-j; if (i1<0) i1 = 0; i2 = 10-j; if (i2>7) i2 = 7; for (k=i1;k maxcorr) { maxcorr = corr[i][j]; maxi=i; maxj=j; } } } for (i=0;i0) { for (k=0;k<7;k++) { tmp += MULT16_16(exc[i-(pitch-maxj+3)+k-3],shift_filt[maxi-1][k]); } } else { tmp = SHL32(exc[i-(pitch-maxj+3)],15); } interp[i] = PSHR32(tmp,15); } return pitch-maxj+3; } void multicomb( spx_word16_t *exc, /*decoded excitation*/ spx_word16_t *new_exc, /*enhanced excitation*/ spx_coef_t *ak, /*LPC filter coefs*/ int p, /*LPC order*/ int nsf, /*sub-frame size*/ int pitch, /*pitch period*/ int max_pitch, spx_word16_t comb_gain, /*gain of comb filter*/ char *stack ) { int i; VARDECL(spx_word16_t *iexc); spx_word16_t old_ener, new_ener; int corr_pitch; spx_word16_t iexc0_mag, iexc1_mag, exc_mag; spx_word32_t corr0, corr1; spx_word16_t gain0, gain1; spx_word16_t pgain1, pgain2; spx_word16_t c1, c2; spx_word16_t g1, g2; spx_word16_t ngain; spx_word16_t gg1, gg2; #ifdef FIXED_POINT int scaledown=0; #endif #if 0 /* Set to 1 to enable full pitch search */ int nol_pitch[6]; spx_word16_t nol_pitch_coef[6]; spx_word16_t ol_pitch_coef; open_loop_nbest_pitch(exc, 20, 120, nsf, nol_pitch, nol_pitch_coef, 6, stack); corr_pitch=nol_pitch[0]; ol_pitch_coef = nol_pitch_coef[0]; /*Try to remove pitch multiples*/ for (i=1;i<6;i++) { #ifdef FIXED_POINT if ((nol_pitch_coef[i]>MULT16_16_Q15(nol_pitch_coef[0],19661)) && #else if ((nol_pitch_coef[i]>.6*nol_pitch_coef[0]) && #endif (ABS(2*nol_pitch[i]-corr_pitch)<=2 || ABS(3*nol_pitch[i]-corr_pitch)<=3 || ABS(4*nol_pitch[i]-corr_pitch)<=4 || ABS(5*nol_pitch[i]-corr_pitch)<=5)) { corr_pitch = nol_pitch[i]; } } #else corr_pitch = pitch; #endif ALLOC(iexc, 2*nsf, spx_word16_t); interp_pitch(exc, iexc, corr_pitch, 80); if (corr_pitch>max_pitch) interp_pitch(exc, iexc+nsf, 2*corr_pitch, 80); else interp_pitch(exc, iexc+nsf, -corr_pitch, 80); #ifdef FIXED_POINT for (i=0;i16383) { scaledown = 1; break; } } if (scaledown) { for (i=0;i MULT16_16(iexc0_mag,exc_mag)) pgain1 = QCONST16(1., 14); else pgain1 = PDIV32_16(SHL32(PDIV32(corr0, exc_mag),14),iexc0_mag); if (corr1 > MULT16_16(iexc1_mag,exc_mag)) pgain2 = QCONST16(1., 14); else pgain2 = PDIV32_16(SHL32(PDIV32(corr1, exc_mag),14),iexc1_mag); gg1 = PDIV32_16(SHL32(EXTEND32(exc_mag),8), iexc0_mag); gg2 = PDIV32_16(SHL32(EXTEND32(exc_mag),8), iexc1_mag); if (comb_gain>0) { #ifdef FIXED_POINT c1 = (MULT16_16_Q15(QCONST16(.4,15),comb_gain)+QCONST16(.07,15)); c2 = QCONST16(.5,15)+MULT16_16_Q14(QCONST16(1.72,14),(c1-QCONST16(.07,15))); #else c1 = .4*comb_gain+.07; c2 = .5+1.72*(c1-.07); #endif } else { c1=c2=0; } #ifdef FIXED_POINT g1 = 32767 - MULT16_16_Q13(MULT16_16_Q15(c2, pgain1),pgain1); g2 = 32767 - MULT16_16_Q13(MULT16_16_Q15(c2, pgain2),pgain2); #else g1 = 1-c2*pgain1*pgain1; g2 = 1-c2*pgain2*pgain2; #endif if (g1max_pitch) { gain0 = MULT16_16_Q15(QCONST16(.7,15),MULT16_16_Q14(g1,gg1)); gain1 = MULT16_16_Q15(QCONST16(.3,15),MULT16_16_Q14(g2,gg2)); } else { gain0 = MULT16_16_Q15(QCONST16(.6,15),MULT16_16_Q14(g1,gg1)); gain1 = MULT16_16_Q15(QCONST16(.6,15),MULT16_16_Q14(g2,gg2)); } for (i=0;i new_ener) old_ener = new_ener; ngain = PDIV32_16(SHL32(EXTEND32(old_ener),14),new_ener); for (i=0;imax_scale) { sig_shift++; max_val >>= 1; } __asm__ __volatile__ ( ".normalize16loop%=: \n" "\tldr %4, [%0], #4 \n" "\tldr %5, [%0], #4 \n" "\tmov %4, %4, asr %3 \n" "\tstrh %4, [%1], #2 \n" "\tldr %4, [%0], #4 \n" "\tmov %5, %5, asr %3 \n" "\tstrh %5, [%1], #2 \n" "\tldr %5, [%0], #4 \n" "\tmov %4, %4, asr %3 \n" "\tstrh %4, [%1], #2 \n" "\tsubs %2, %2, #1 \n" "\tmov %5, %5, asr %3 \n" "\tstrh %5, [%1], #2 \n" "\tbgt .normalize16loop%=\n" : "=r" (dead1), "=r" (dead2), "=r" (dead3), "=r" (dead4), "=r" (dead5), "=r" (dead6) : "0" (x), "1" (y), "2" (len>>2), "3" (sig_shift) : "cc", "memory"); return sig_shift; } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/filters_bfin.h ================================================ /* Copyright (C) 2005 Analog Devices */ /** @file filters_bfin.h @brief Various analysis/synthesis filters (Blackfin version) */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define OVERRIDE_NORMALIZE16 int normalize16(const spx_sig_t *x, spx_word16_t *y, spx_sig_t max_scale, int len) { spx_sig_t max_val=1; int sig_shift; __asm__ ( "%0 = 0;\n\t" "I0 = %1;\n\t" "L0 = 0;\n\t" "R1 = [I0++];\n\t" "LOOP norm_max%= LC0 = %2;\n\t" "LOOP_BEGIN norm_max%=;\n\t" "R2 = ABS R1 || R1 = [I0++];\n\t" "%0 = MAX(%0, R2);\n\t" "LOOP_END norm_max%=;\n\t" : "=&d" (max_val) : "a" (x), "a" (len) : "R1", "R2" ); sig_shift=0; while (max_val>max_scale) { sig_shift++; max_val >>= 1; } __asm__ __volatile__ ( "I0 = %0;\n\t" "L0 = 0;\n\t" "P1 = %1;\n\t" "R0 = [I0++];\n\t" "LOOP norm_shift%= LC0 = %3;\n\t" "LOOP_BEGIN norm_shift%=;\n\t" "R1 = ASHIFT R0 by %2.L || R0 = [I0++];\n\t" "W[P1++] = R1;\n\t" "LOOP_END norm_shift%=;\n\t" "R1 = ASHIFT R0 by %2.L;\n\t" "W[P1++] = R1;\n\t" : : "a" (x), "a" (y), "d" (-sig_shift), "a" (len-1) : "I0", "L0", "P1", "R0", "R1", "memory" ); return sig_shift; } #define OVERRIDE_FILTER_MEM16 void filter_mem16(const spx_word16_t *_x, const spx_coef_t *num, const spx_coef_t *den, spx_word16_t *_y, int N, int ord, spx_mem_t *mem, char *stack) { VARDECL(spx_word32_t *xy2); VARDECL(spx_word32_t *numden_a); spx_word32_t *xy; spx_word16_t *numden; int i; ALLOC(xy2, (N+1), spx_word32_t); ALLOC(numden_a, (2*ord+2), spx_word32_t); xy = xy2+1; numden = (spx_word16_t*) numden_a; for (i=0;i>> 13;\n\t" "W[%0] = R3.L;\n\t" "R0 <<= 1;\n\t" "R1 = R1 + R0;\n\t" "R1 >>>= 13;\n\t" "W[%1] = R1.L;\n\t" "LOOP_END samples%=;\n\t" : "=a" (ytmp2), "=a" (y) : "a" (awk2), "a" (ak), "d" (ord), "m" (N), "0" (ytmp2), "1" (y) : "A0", "A1", "R0", "R1", "R2", "R3", "I0", "I1", "I2", "I3", "L0", "L1", "L2", "L3", "A0", "A1" ); } #if 0 /* Equivalent C function for filter_mem2 and compute_impulse_response */ #define min(a,b) ((a)<(b) ? (a):(b)) void compute_impulse_response(const spx_coef_t *ak, const spx_coef_t *awk1, const spx_coef_t *awk2, spx_word16_t *y, int N, int ord, char *stack) { int i,j; VARDECL(spx_word16_t *ytmp); ALLOC(ytmp, N, spx_word16_t); y[0] = LPC_SCALING; for (i=0;i void filter_mem16_10(const float *x, const float *_num, const float *_den, float *y, int N, int ord, float *_mem) { __m128 num[3], den[3], mem[3]; int i; /* Copy numerator, denominator and memory to aligned xmm */ for (i=0;i<2;i++) { mem[i] = _mm_loadu_ps(_mem+4*i); num[i] = _mm_loadu_ps(_num+4*i); den[i] = _mm_loadu_ps(_den+4*i); } mem[2] = _mm_setr_ps(_mem[8], _mem[9], 0, 0); num[2] = _mm_setr_ps(_num[8], _num[9], 0, 0); den[2] = _mm_setr_ps(_den[8], _den[9], 0, 0); for (i=0;i>1; __asm__ ( "P0 = 15;\n\t" "R0 = %1;\n\t" "R1 = %2;\n\t" //"R0 = R0 + R1;\n\t" "R0 <<= 1;\n\t" "DIVS (R0, R1);\n\t" "LOOP divide%= LC0 = P0;\n\t" "LOOP_BEGIN divide%=;\n\t" "DIVQ (R0, R1);\n\t" "LOOP_END divide%=;\n\t" "R0 = R0.L;\n\t" "%0 = R0;\n\t" : "=m" (res) : "m" (a), "m" (bb) : "P0", "R0", "R1", "cc"); return res; } #undef DIV32_16 static inline spx_word16_t DIV32_16(spx_word32_t a, spx_word16_t b) { spx_word32_t res, bb; bb = b; /* Make the roundinf consistent with the C version (do we need to do that?)*/ if (a<0) a += (b-1); __asm__ ( "P0 = 15;\n\t" "R0 = %1;\n\t" "R1 = %2;\n\t" "R0 <<= 1;\n\t" "DIVS (R0, R1);\n\t" "LOOP divide%= LC0 = P0;\n\t" "LOOP_BEGIN divide%=;\n\t" "DIVQ (R0, R1);\n\t" "LOOP_END divide%=;\n\t" "R0 = R0.L;\n\t" "%0 = R0;\n\t" : "=m" (res) : "m" (a), "m" (bb) : "P0", "R0", "R1", "cc"); return res; } #undef MAX16 static inline spx_word16_t MAX16(spx_word16_t a, spx_word16_t b) { spx_word32_t res; __asm__ ( "%1 = %1.L (X);\n\t" "%2 = %2.L (X);\n\t" "%0 = MAX(%1,%2);" : "=d" (res) : "%d" (a), "d" (b) ); return res; } #undef MULT16_32_Q15 static inline spx_word32_t MULT16_32_Q15(spx_word16_t a, spx_word32_t b) { spx_word32_t res; __asm__ ( "A1 = %2.L*%1.L (M);\n\t" "A1 = A1 >>> 15;\n\t" "%0 = (A1 += %2.L*%1.H) ;\n\t" : "=&W" (res), "=&d" (b) : "d" (a), "1" (b) : "A1" ); return res; } #undef MAC16_32_Q15 static inline spx_word32_t MAC16_32_Q15(spx_word32_t c, spx_word16_t a, spx_word32_t b) { spx_word32_t res; __asm__ ( "A1 = %2.L*%1.L (M);\n\t" "A1 = A1 >>> 15;\n\t" "%0 = (A1 += %2.L*%1.H);\n\t" "%0 = %0 + %4;\n\t" : "=&W" (res), "=&d" (b) : "d" (a), "1" (b), "d" (c) : "A1" ); return res; } #undef MULT16_32_Q14 static inline spx_word32_t MULT16_32_Q14(spx_word16_t a, spx_word32_t b) { spx_word32_t res; __asm__ ( "%2 <<= 1;\n\t" "A1 = %1.L*%2.L (M);\n\t" "A1 = A1 >>> 15;\n\t" "%0 = (A1 += %1.L*%2.H);\n\t" : "=W" (res), "=d" (a), "=d" (b) : "1" (a), "2" (b) : "A1" ); return res; } #undef MAC16_32_Q14 static inline spx_word32_t MAC16_32_Q14(spx_word32_t c, spx_word16_t a, spx_word32_t b) { spx_word32_t res; __asm__ ( "%1 <<= 1;\n\t" "A1 = %2.L*%1.L (M);\n\t" "A1 = A1 >>> 15;\n\t" "%0 = (A1 += %2.L*%1.H);\n\t" "%0 = %0 + %4;\n\t" : "=&W" (res), "=&d" (b) : "d" (a), "1" (b), "d" (c) : "A1" ); return res; } #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/fixed_debug.h ================================================ /* Copyright (C) 2003 Jean-Marc Valin */ /** @file fixed_debug.h @brief Fixed-point operations with debugging */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FIXED_DEBUG_H #define FIXED_DEBUG_H #include extern long long spx_mips; #define MIPS_INC spx_mips++, #define QCONST16(x,bits) ((spx_word16_t)(.5+(x)*(((spx_word32_t)1)<<(bits)))) #define QCONST32(x,bits) ((spx_word32_t)(.5+(x)*(((spx_word32_t)1)<<(bits)))) #define VERIFY_SHORT(x) ((x)<=32767&&(x)>=-32768) #define VERIFY_INT(x) ((x)<=2147483647LL&&(x)>=-2147483648LL) static inline short NEG16(int x) { int res; if (!VERIFY_SHORT(x)) { fprintf (stderr, "NEG16: input is not short: %d\n", (int)x); } res = -x; if (!VERIFY_SHORT(res)) fprintf (stderr, "NEG16: output is not short: %d\n", (int)res); spx_mips++; return res; } static inline int NEG32(long long x) { long long res; if (!VERIFY_INT(x)) { fprintf (stderr, "NEG16: input is not int: %d\n", (int)x); } res = -x; if (!VERIFY_INT(res)) fprintf (stderr, "NEG16: output is not int: %d\n", (int)res); spx_mips++; return res; } #define EXTRACT16(x) _EXTRACT16(x, __FILE__, __LINE__) static inline short _EXTRACT16(int x, char *file, int line) { int res; if (!VERIFY_SHORT(x)) { fprintf (stderr, "EXTRACT16: input is not short: %d in %s: line %d\n", x, file, line); } res = x; spx_mips++; return res; } #define EXTEND32(x) _EXTEND32(x, __FILE__, __LINE__) static inline int _EXTEND32(int x, char *file, int line) { int res; if (!VERIFY_SHORT(x)) { fprintf (stderr, "EXTEND32: input is not short: %d in %s: line %d\n", x, file, line); } res = x; spx_mips++; return res; } #define SHR16(a, shift) _SHR16(a, shift, __FILE__, __LINE__) static inline short _SHR16(int a, int shift, char *file, int line) { int res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(shift)) { fprintf (stderr, "SHR16: inputs are not short: %d >> %d in %s: line %d\n", a, shift, file, line); } res = a>>shift; if (!VERIFY_SHORT(res)) fprintf (stderr, "SHR16: output is not short: %d in %s: line %d\n", res, file, line); spx_mips++; return res; } #define SHL16(a, shift) _SHL16(a, shift, __FILE__, __LINE__) static inline short _SHL16(int a, int shift, char *file, int line) { int res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(shift)) { fprintf (stderr, "SHL16: inputs are not short: %d %d in %s: line %d\n", a, shift, file, line); } res = a<>shift; if (!VERIFY_INT(res)) { fprintf (stderr, "SHR32: output is not int: %d\n", (int)res); } spx_mips++; return res; } static inline int SHL32(long long a, int shift) { long long res; if (!VERIFY_INT(a) || !VERIFY_SHORT(shift)) { fprintf (stderr, "SHL32: inputs are not int: %d %d\n", (int)a, shift); } res = a<>1))),shift)) #define PSHR32(a,shift) (SHR32(ADD32((a),((EXTEND32(1)<<((shift))>>1))),shift)) #define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift))) #define SATURATE16(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) #define SATURATE32(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) //#define SHR(a,shift) ((a) >> (shift)) //#define SHL(a,shift) ((a) << (shift)) #define ADD16(a, b) _ADD16(a, b, __FILE__, __LINE__) static inline short _ADD16(int a, int b, char *file, int line) { int res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "ADD16: inputs are not short: %d %d in %s: line %d\n", a, b, file, line); } res = a+b; if (!VERIFY_SHORT(res)) { fprintf (stderr, "ADD16: output is not short: %d+%d=%d in %s: line %d\n", a,b,res, file, line); } spx_mips++; return res; } #define SUB16(a, b) _SUB16(a, b, __FILE__, __LINE__) static inline short _SUB16(int a, int b, char *file, int line) { int res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "SUB16: inputs are not short: %d %d in %s: line %d\n", a, b, file, line); } res = a-b; if (!VERIFY_SHORT(res)) fprintf (stderr, "SUB16: output is not short: %d in %s: line %d\n", res, file, line); spx_mips++; return res; } #define ADD32(a, b) _ADD32(a, b, __FILE__, __LINE__) static inline int _ADD32(long long a, long long b, char *file, int line) { long long res; if (!VERIFY_INT(a) || !VERIFY_INT(b)) { fprintf (stderr, "ADD32: inputs are not int: %d %d in %s: line %d\n", (int)a, (int)b, file, line); } res = a+b; if (!VERIFY_INT(res)) { fprintf (stderr, "ADD32: output is not int: %d in %s: line %d\n", (int)res, file, line); } spx_mips++; return res; } static inline int SUB32(long long a, long long b) { long long res; if (!VERIFY_INT(a) || !VERIFY_INT(b)) { fprintf (stderr, "SUB32: inputs are not int: %d %d\n", (int)a, (int)b); } res = a-b; if (!VERIFY_INT(res)) fprintf (stderr, "SUB32: output is not int: %d\n", (int)res); spx_mips++; return res; } #define ADD64(a,b) (MIPS_INC(a)+(b)) /* result fits in 16 bits */ static inline short MULT16_16_16(int a, int b) { int res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_16: inputs are not short: %d %d\n", a, b); } res = a*b; if (!VERIFY_SHORT(res)) fprintf (stderr, "MULT16_16_16: output is not short: %d\n", res); spx_mips++; return res; } #define MULT16_16(a, b) _MULT16_16(a, b, __FILE__, __LINE__) static inline int _MULT16_16(int a, int b, char *file, int line) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16: inputs are not short: %d %d in %s: line %d\n", a, b, file, line); } res = ((long long)a)*b; if (!VERIFY_INT(res)) fprintf (stderr, "MULT16_16: output is not int: %d in %s: line %d\n", (int)res, file, line); spx_mips++; return res; } #define MAC16_16(c,a,b) (spx_mips--,ADD32((c),MULT16_16((a),(b)))) #define MAC16_16_Q11(c,a,b) (EXTRACT16(ADD16((c),EXTRACT16(SHR32(MULT16_16((a),(b)),11))))) #define MAC16_16_Q13(c,a,b) (EXTRACT16(ADD16((c),EXTRACT16(SHR32(MULT16_16((a),(b)),13))))) #define MAC16_16_P13(c,a,b) (EXTRACT16(ADD32((c),SHR32(ADD32(4096,MULT16_16((a),(b))),13)))) #define MULT16_32_QX(a, b, Q) _MULT16_32_QX(a, b, Q, __FILE__, __LINE__) static inline int _MULT16_32_QX(int a, long long b, int Q, char *file, int line) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_INT(b)) { fprintf (stderr, "MULT16_32_Q%d: inputs are not short+int: %d %d in %s: line %d\n", Q, (int)a, (int)b, file, line); } if (ABS32(b)>=(EXTEND32(1)<<(15+Q))) fprintf (stderr, "MULT16_32_Q%d: second operand too large: %d %d in %s: line %d\n", Q, (int)a, (int)b, file, line); res = (((long long)a)*(long long)b) >> Q; if (!VERIFY_INT(res)) fprintf (stderr, "MULT16_32_Q%d: output is not int: %d*%d=%d in %s: line %d\n", Q, (int)a, (int)b,(int)res, file, line); spx_mips+=5; return res; } static inline int MULT16_32_PX(int a, long long b, int Q) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_INT(b)) { fprintf (stderr, "MULT16_32_P%d: inputs are not short+int: %d %d\n", Q, (int)a, (int)b); } if (ABS32(b)>=(EXTEND32(1)<<(15+Q))) fprintf (stderr, "MULT16_32_Q%d: second operand too large: %d %d\n", Q, (int)a, (int)b); res = ((((long long)a)*(long long)b) + ((EXTEND32(1)<>1))>> Q; if (!VERIFY_INT(res)) fprintf (stderr, "MULT16_32_P%d: output is not int: %d*%d=%d\n", Q, (int)a, (int)b,(int)res); spx_mips+=5; return res; } #define MULT16_32_Q11(a,b) MULT16_32_QX(a,b,11) #define MAC16_32_Q11(c,a,b) ADD32((c),MULT16_32_Q11((a),(b))) #define MULT16_32_Q12(a,b) MULT16_32_QX(a,b,12) #define MULT16_32_Q13(a,b) MULT16_32_QX(a,b,13) #define MULT16_32_Q14(a,b) MULT16_32_QX(a,b,14) #define MULT16_32_Q15(a,b) MULT16_32_QX(a,b,15) #define MULT16_32_P15(a,b) MULT16_32_PX(a,b,15) #define MAC16_32_Q15(c,a,b) ADD32((c),MULT16_32_Q15((a),(b))) static inline int SATURATE(int a, int b) { if (a>b) a=b; if (a<-b) a = -b; return a; } static inline int MULT16_16_Q11_32(int a, int b) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_Q11: inputs are not short: %d %d\n", a, b); } res = ((long long)a)*b; res >>= 11; if (!VERIFY_INT(res)) fprintf (stderr, "MULT16_16_Q11: output is not short: %d*%d=%d\n", (int)a, (int)b, (int)res); spx_mips+=3; return res; } static inline short MULT16_16_Q13(int a, int b) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_Q13: inputs are not short: %d %d\n", a, b); } res = ((long long)a)*b; res >>= 13; if (!VERIFY_SHORT(res)) fprintf (stderr, "MULT16_16_Q13: output is not short: %d*%d=%d\n", a, b, (int)res); spx_mips+=3; return res; } static inline short MULT16_16_Q14(int a, int b) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_Q14: inputs are not short: %d %d\n", a, b); } res = ((long long)a)*b; res >>= 14; if (!VERIFY_SHORT(res)) fprintf (stderr, "MULT16_16_Q14: output is not short: %d\n", (int)res); spx_mips+=3; return res; } static inline short MULT16_16_Q15(int a, int b) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_Q15: inputs are not short: %d %d\n", a, b); } res = ((long long)a)*b; res >>= 15; if (!VERIFY_SHORT(res)) { fprintf (stderr, "MULT16_16_Q15: output is not short: %d\n", (int)res); } spx_mips+=3; return res; } static inline short MULT16_16_P13(int a, int b) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_P13: inputs are not short: %d %d\n", a, b); } res = ((long long)a)*b; res += 4096; if (!VERIFY_INT(res)) fprintf (stderr, "MULT16_16_P13: overflow: %d*%d=%d\n", a, b, (int)res); res >>= 13; if (!VERIFY_SHORT(res)) fprintf (stderr, "MULT16_16_P13: output is not short: %d*%d=%d\n", a, b, (int)res); spx_mips+=4; return res; } static inline short MULT16_16_P14(int a, int b) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_P14: inputs are not short: %d %d\n", a, b); } res = ((long long)a)*b; res += 8192; if (!VERIFY_INT(res)) fprintf (stderr, "MULT16_16_P14: overflow: %d*%d=%d\n", a, b, (int)res); res >>= 14; if (!VERIFY_SHORT(res)) fprintf (stderr, "MULT16_16_P14: output is not short: %d*%d=%d\n", a, b, (int)res); spx_mips+=4; return res; } static inline short MULT16_16_P15(int a, int b) { long long res; if (!VERIFY_SHORT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "MULT16_16_P15: inputs are not short: %d %d\n", a, b); } res = ((long long)a)*b; res += 16384; if (!VERIFY_INT(res)) fprintf (stderr, "MULT16_16_P15: overflow: %d*%d=%d\n", a, b, (int)res); res >>= 15; if (!VERIFY_SHORT(res)) fprintf (stderr, "MULT16_16_P15: output is not short: %d*%d=%d\n", a, b, (int)res); spx_mips+=4; return res; } #define DIV32_16(a, b) _DIV32_16(a, b, __FILE__, __LINE__) static inline int _DIV32_16(long long a, long long b, char *file, int line) { long long res; if (b==0) { fprintf(stderr, "DIV32_16: divide by zero: %d/%d in %s: line %d\n", (int)a, (int)b, file, line); return 0; } if (!VERIFY_INT(a) || !VERIFY_SHORT(b)) { fprintf (stderr, "DIV32_16: inputs are not int/short: %d %d in %s: line %d\n", (int)a, (int)b, file, line); } res = a/b; if (!VERIFY_SHORT(res)) { fprintf (stderr, "DIV32_16: output is not short: %d / %d = %d in %s: line %d\n", (int)a,(int)b,(int)res, file, line); if (res>32767) res = 32767; if (res<-32768) res = -32768; } spx_mips+=20; return res; } #define DIV32(a, b) _DIV32(a, b, __FILE__, __LINE__) static inline int _DIV32(long long a, long long b, char *file, int line) { long long res; if (b==0) { fprintf(stderr, "DIV32: divide by zero: %d/%d in %s: line %d\n", (int)a, (int)b, file, line); return 0; } if (!VERIFY_INT(a) || !VERIFY_INT(b)) { fprintf (stderr, "DIV32: inputs are not int/short: %d %d in %s: line %d\n", (int)a, (int)b, file, line); } res = a/b; if (!VERIFY_INT(res)) fprintf (stderr, "DIV32: output is not int: %d in %s: line %d\n", (int)res, file, line); spx_mips+=36; return res; } #define PDIV32(a,b) DIV32(ADD32((a),(b)>>1),b) #define PDIV32_16(a,b) DIV32_16(ADD32((a),(b)>>1),b) #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/fixed_generic.h ================================================ /* Copyright (C) 2003 Jean-Marc Valin */ /** @file fixed_generic.h @brief Generic fixed-point operations */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FIXED_GENERIC_H #define FIXED_GENERIC_H #define QCONST16(x,bits) ((spx_word16_t)(.5+(x)*(((spx_word32_t)1)<<(bits)))) #define QCONST32(x,bits) ((spx_word32_t)(.5+(x)*(((spx_word32_t)1)<<(bits)))) #define NEG16(x) (-(x)) #define NEG32(x) (-(x)) #define EXTRACT16(x) ((spx_word16_t)(x)) #define EXTEND32(x) ((spx_word32_t)(x)) #define SHR16(a,shift) ((a) >> (shift)) #define SHL16(a,shift) ((a) << (shift)) #define SHR32(a,shift) ((a) >> (shift)) #define SHL32(a,shift) ((a) << (shift)) #define PSHR16(a,shift) (SHR16((a)+((1<<((shift))>>1)),shift)) #define PSHR32(a,shift) (SHR32((a)+((EXTEND32(1)<<((shift))>>1)),shift)) #define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift))) #define SATURATE16(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) #define SATURATE32(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) #define SHR(a,shift) ((a) >> (shift)) #define SHL(a,shift) ((spx_word32_t)(a) << (shift)) #define PSHR(a,shift) (SHR((a)+((EXTEND32(1)<<((shift))>>1)),shift)) #define SATURATE(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) #define ADD16(a,b) ((spx_word16_t)((spx_word16_t)(a)+(spx_word16_t)(b))) #define SUB16(a,b) ((spx_word16_t)(a)-(spx_word16_t)(b)) #define ADD32(a,b) ((spx_word32_t)(a)+(spx_word32_t)(b)) #define SUB32(a,b) ((spx_word32_t)(a)-(spx_word32_t)(b)) /* result fits in 16 bits */ #define MULT16_16_16(a,b) ((((spx_word16_t)(a))*((spx_word16_t)(b)))) /* (spx_word32_t)(spx_word16_t) gives TI compiler a hint that it's 16x16->32 multiply */ #define MULT16_16(a,b) (((spx_word32_t)(spx_word16_t)(a))*((spx_word32_t)(spx_word16_t)(b))) #define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b)))) #define MULT16_32_Q12(a,b) ADD32(MULT16_16((a),SHR((b),12)), SHR(MULT16_16((a),((b)&0x00000fff)),12)) #define MULT16_32_Q13(a,b) ADD32(MULT16_16((a),SHR((b),13)), SHR(MULT16_16((a),((b)&0x00001fff)),13)) #define MULT16_32_Q14(a,b) ADD32(MULT16_16((a),SHR((b),14)), SHR(MULT16_16((a),((b)&0x00003fff)),14)) #define MULT16_32_Q11(a,b) ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11)) #define MAC16_32_Q11(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11))) #define MULT16_32_P15(a,b) ADD32(MULT16_16((a),SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15)) #define MULT16_32_Q15(a,b) ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15)) #define MAC16_32_Q15(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))) #define MAC16_16_Q11(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),11))) #define MAC16_16_Q13(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),13))) #define MAC16_16_P13(c,a,b) (ADD32((c),SHR(ADD32(4096,MULT16_16((a),(b))),13))) #define MULT16_16_Q11_32(a,b) (SHR(MULT16_16((a),(b)),11)) #define MULT16_16_Q13(a,b) (SHR(MULT16_16((a),(b)),13)) #define MULT16_16_Q14(a,b) (SHR(MULT16_16((a),(b)),14)) #define MULT16_16_Q15(a,b) (SHR(MULT16_16((a),(b)),15)) #define MULT16_16_P13(a,b) (SHR(ADD32(4096,MULT16_16((a),(b))),13)) #define MULT16_16_P14(a,b) (SHR(ADD32(8192,MULT16_16((a),(b))),14)) #define MULT16_16_P15(a,b) (SHR(ADD32(16384,MULT16_16((a),(b))),15)) #define MUL_16_32_R15(a,bh,bl) ADD32(MULT16_16((a),(bh)), SHR(MULT16_16((a),(bl)),15)) #define DIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a))/((spx_word16_t)(b)))) #define PDIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word16_t)(b)))) #define DIV32(a,b) (((spx_word32_t)(a))/((spx_word32_t)(b))) #define PDIV32(a,b) (((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word32_t)(b))) #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/gain_table.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: gain_table.c Codebook for 3-tap pitch prediction gain (128 entries) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ const signed char gain_cdbk_nb[512] = { -32, -32, -32, 0, -28, -67, -5, 33, -42, -6, -32, 18, -57, -10, -54, 35, -16, 27, -41, 42, 19, -19, -40, 36, -45, 24, -21, 40, -8, -14, -18, 28, 1, 14, -58, 53, -18, -88, -39, 39, -38, 21, -18, 37, -19, 20, -43, 38, 10, 17, -48, 54, -52, -58, -13, 33, -44, -1, -11, 32, -12, -11, -34, 22, 14, 0, -46, 46, -37, -35, -34, 5, -25, 44, -30, 43, 6, -4, -63, 49, -31, 43, -41, 43, -23, 30, -43, 41, -43, 26, -14, 44, -33, 1, -13, 27, -13, 18, -37, 37, -46, -73, -45, 34, -36, 24, -25, 34, -36, -11, -20, 19, -25, 12, -18, 33, -36, -69, -59, 34, -45, 6, 8, 46, -22, -14, -24, 18, -1, 13, -44, 44, -39, -48, -26, 15, -32, 31, -37, 34, -33, 15, -46, 31, -24, 30, -36, 37, -41, 31, -23, 41, -50, 22, -4, 50, -22, 2, -21, 28, -17, 30, -34, 40, -7, -60, -28, 29, -38, 42, -28, 42, -44, -11, 21, 43, -16, 8, -44, 34, -39, -55, -43, 21, -11, -35, 26, 41, -9, 0, -34, 29, -8, 121, -81, 113, 7, -16, -22, 33, -37, 33, -31, 36, -27, -7, -36, 17, -34, 70, -57, 65, -37, -11, -48, 21, -40, 17, -1, 44, -33, 6, -6, 33, -9, 0, -20, 34, -21, 69, -33, 57, -29, 33, -31, 35, -55, 12, -1, 49, -33, 27, -22, 35, -50, -33, -47, 17, -50, 54, 51, 94, -1, -5, -44, 35, -4, 22, -40, 45, -39, -66, -25, 24, -33, 1, -26, 20, -24, -23, -25, 12, -11, 21, -45, 44, -25, -45, -19, 17, -43, 105, -16, 82, 5, -21, 1, 41, -16, 11, -33, 30, -13, -99, -4, 57, -37, 33, -15, 44, -25, 37, -63, 54, -36, 24, -31, 31, -53, -56, -38, 26, -41, -4, 4, 37, -33, 13, -30, 24, 49, 52, -94, 114, -5, -30, -15, 23, 1, 38, -40, 56, -23, 12, -36, 29, -17, 40, -47, 51, -37, -41, -39, 11, -49, 34, 0, 58, -18, -7, -4, 34, -16, 17, -27, 35, 30, 5, -62, 65, 4, 48, -68, 76, -43, 11, -11, 38, -18, 19, -15, 41, -23, -62, -39, 23, -42, 10, -2, 41, -21, -13, -13, 25, -9, 13, -47, 42, -23, -62, -24, 24, -44, 60, -21, 58, -18, -3, -52, 32, -22, 22, -36, 34, -75, 57, 16, 90, -19, 3, 10, 45, -29, 23, -38, 32, -5, -62, -51, 38, -51, 40, -18, 53, -42, 13, -24, 32, -34, 14, -20, 30, -56, -75, -26, 37, -26, 32, 15, 59, -26, 17, -29, 29, -7, 28, -52, 53, -12, -30, 5, 30, -5, -48, -5, 35, 2, 2, -43, 40, 21, 16, 16, 75, -25, -45, -32, 10, -43, 18, -10, 42, 9, 0, -1, 52, -1, 7, -30, 36, 19, -48, -4, 48, -28, 25, -29, 32, -22, 0, -31, 22, -32, 17, -10, 36, -64, -41, -62, 36, -52, 15, 16, 58, -30, -22, -32, 6, -7, 9, -38, 36}; ================================================ FILE: deps/pjsip/third_party/speex/libspeex/gain_table_lbr.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: gain_table_lbr.c Codebook for 3-tap pitch prediction gain (32 entries) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ const signed char gain_cdbk_lbr[128] = { -32, -32, -32, 0, -31, -58, -16, 22, -41, -24, -43, 14, -56, -22, -55, 29, -13, 33, -41, 47, -4, -39, -9, 29, -41, 15, -12, 38, -8, -15, -12, 31, 1, 2, -44, 40, -22, -66, -42, 27, -38, 28, -23, 38, -21, 14, -37, 31, 0, 21, -50, 52, -53, -71, -27, 33, -37, -1, -19, 25, -19, -5, -28, 22, 6, 65, -44, 74, -33, -48, -33, 9, -40, 57, -14, 58, -17, 4, -45, 32, -31, 38, -33, 36, -23, 28, -40, 39, -43, 29, -12, 46, -34, 13, -23, 28, -16, 15, -27, 34, -14, -82, -15, 43, -31, 25, -32, 29, -21, 5, -5, 38, -47, -63, -51, 33, -46, 12, 3, 47, -28, -17, -29, 11, -10, 14, -40, 38}; ================================================ FILE: deps/pjsip/third_party/speex/libspeex/hexc_10_32_table.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: hexc_10_32_table.c Codebook for high-band excitation in SB-CELP mode (4000 bps) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ const signed char hexc_10_32_table[320] = { -3, -2, -1, 0, -4, 5, 35, -40, -9, 13, -44, 5, -27, -1, -7, 6, -11, 7, -8, 7, 19, -14, 15, -4, 9, -10, 10, -8, 10, -9, -1, 1, 0, 0, 2, 5, -18, 22, -53, 50, 1, -23, 50, -36, 15, 3, -13, 14, -10, 6, 1, 5, -3, 4, -2, 5, -32, 25, 5, -2, -1, -4, 1, 11, -29, 26, -6, -15, 30, -18, 0, 15, -17, 40, -41, 3, 9, -2, -2, 3, -3, -1, -5, 2, 21, -6, -16, -21, 23, 2, 60, 15, 16, -16, -9, 14, 9, -1, 7, -9, 0, 1, 1, 0, -1, -6, 17, -28, 54, -45, -1, 1, -1, -6, -6, 2, 11, 26, -29, -2, 46, -21, 34, 12, -23, 32, -23, 16, -10, 3, 66, 19, -20, 24, 7, 11, -3, 0, -3, -1, -50, -46, 2, -18, -3, 4, -1, -2, 3, -3, -19, 41, -36, 9, 11, -24, 21, -16, 9, -3, -25, -3, 10, 18, -9, -2, -5, -1, -5, 6, -4, -3, 2, -26, 21, -19, 35, -15, 7, -13, 17, -19, 39, -43, 48, -31, 16, -9, 7, -2, -5, 3, -4, 9, -19, 27, -55, 63, -35, 10, 26, -44, -2, 9, 4, 1, -6, 8, -9, 5, -8, -1, -3, -16, 45, -42, 5, 15, -16, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -16, 24, -55, 47, -38, 27, -19, 7, -3, 1, 16, 27, 20, -19, 18, 5, -7, 1, -5, 2, -6, 8, -22, 0, -3, -3, 8, -1, 7, -8, 1, -3, 5, 0, 17, -48, 58, -52, 29, -7, -2, 3, -10, 6, -26, 58, -31, 1, -6, 3, 93, -29, 39, 3, 17, 5, 6, -1, -1, -1, 27, 13, 10, 19, -7, -34, 12, 10, -4, 9, -76, 9, 8, -28, -2, -11, 2, -1, 3, 1, -83, 38, -39, 4, -16, -6, -2, -5, 5, -2, }; ================================================ FILE: deps/pjsip/third_party/speex/libspeex/hexc_table.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: hexc_table.c Codebook for high-band excitation in SB-CELP mode (8000 bps with sign) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ const signed char hexc_table[1024] = { -24, 21, -20, 5, -5, -7, 14, -10, 2, -27, 16, -20, 0, -32, 26, 19, 8, -11, -41, 31, 28, -27, -32, 34, 42, 34, -17, 22, -10, 13, -29, 18, -12, -26, -24, 11, 22, 5, -5, -5, 54, -68, -43, 57, -25, 24, 4, 4, 26, -8, -12, -17, 54, 30, -45, 1, 10, -15, 18, -41, 11, 68, -67, 37, -16, -24, -16, 38, -22, 6, -29, 30, 66, -27, 5, 7, -16, 13, 2, -12, -7, -3, -20, 36, 4, -28, 9, 3, 32, 48, 26, 39, 3, 0, 7, -21, -13, 5, -82, -7, 73, -20, 34, -9, -5, 1, -1, 10, -5, -10, -1, 9, 1, -9, 10, 0, -14, 11, -1, -2, -1, 11, 20, 96, -81, -22, -12, -9, -58, 9, 24, -30, 26, -35, 27, -12, 13, -18, 56, -59, 15, -7, 23, -15, -1, 6, -25, 14, -22, -20, 47, -11, 16, 2, 38, -23, -19, -30, -9, 40, -11, 5, 4, -6, 8, 26, -21, -11, 127, 4, 1, 6, -9, 2, -7, -2, -3, 7, -5, 10, -19, 7, -106, 91, -3, 9, -4, 21, -8, 26, -80, 8, 1, -2, -10, -17, -17, -27, 32, 71, 6, -29, 11, -23, 54, -38, 29, -22, 39, 87, -31, -12, -20, 3, -2, -2, 2, 20, 0, -1, -35, 27, 9, -6, -12, 3, -12, -6, 13, 1, 14, -22, -59, -15, -17, -25, 13, -7, 7, 3, 0, 1, -7, 6, -3, 61, -37, -23, -23, -29, 38, -31, 27, 1, -8, 2, -27, 23, -26, 36, -34, 5, 24, -24, -6, 7, 3, -59, 78, -62, 44, -16, 1, 6, 0, 17, 8, 45, 0, -110, 6, 14, -2, 32, -77, -56, 62, -3, 3, -13, 4, -16, 102, -15, -36, -1, 9, -113, 6, 23, 0, 9, 9, 5, -8, -1, -14, 5, -12, 121, -53, -27, -8, -9, 22, -13, 3, 2, -3, 1, -2, -71, 95, 38, -19, 15, -16, -5, 71, 10, 2, -32, -13, -5, 15, -1, -2, -14, -85, 30, 29, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, -65, -56, -9, 18, 18, 23, -14, -2, 0, 12, -29, 26, -12, 1, 2, -12, -64, 90, -6, 4, 1, 5, -5, -110, -3, -31, 22, -29, 9, 0, 8, -40, -5, 21, -5, -5, 13, 10, -18, 40, 1, 35, -20, 30, -28, 11, -6, 19, 7, 14, 18, -64, 9, -6, 16, 51, 68, 8, 16, 12, -8, 0, -9, 20, -22, 25, 7, -4, -13, 41, -35, 93, -18, -54, 11, -1, 1, -9, 4, -66, 66, -31, 20, -22, 25, -23, 11, 10, 9, 19, 15, 11, -5, -31, -10, -23, -28, -6, -6, -3, -4, 5, 3, -28, 22, -11, -42, 25, -25, -16, 41, 34, 47, -6, 2, 42, -19, -22, 5, -39, 32, 6, -35, 22, 17, -30, 8, -26, -11, -11, 3, -12, 33, 33, -37, 21, -1, 6, -4, 3, 0, -5, 5, 12, -12, 57, 27, -61, -3, 20, -17, 2, 0, 4, 0, -2, -33, -58, 81, -23, 39, -10, -5, 2, 6, -7, 5, 4, -3, -2, -13, -23, -72, 107, 15, -5, 0, -7, -3, -6, 5, -4, 15, 47, 12, -31, 25, -16, 8, 22, -25, -62, -56, -18, 14, 28, 12, 2, -11, 74, -66, 41, -20, -7, 16, -20, 16, -8, 0, -16, 4, -19, 92, 12, -59, -14, -39, 49, -25, -16, 23, -27, 19, -3, -33, 19, 85, -29, 6, -7, -10, 16, -7, -12, 1, -6, 2, 4, -2, 64, 10, -25, 41, -2, -31, 15, 0, 110, 50, 69, 35, 28, 19, -10, 2, -43, -49, -56, -15, -16, 10, 3, 12, -1, -8, 1, 26, -12, -1, 7, -11, -27, 41, 25, 1, -11, -18, 22, -7, -1, -47, -8, 23, -3, -17, -7, 18, -125, 59, -5, 3, 18, 1, 2, 3, 27, -35, 65, -53, 50, -46, 37, -21, -28, 7, 14, -37, -5, -5, 12, 5, -8, 78, -19, 21, -6, -16, 8, -7, 5, 2, 7, 2, 10, -6, 12, -60, 44, 11, -36, -32, 31, 0, 2, -2, 2, 1, -3, 7, -10, 17, -21, 10, 6, -2, 19, -2, 59, -38, -86, 38, 8, -41, -30, -45, -33, 7, 15, 28, 29, -7, 24, -40, 7, 7, 5, -2, 9, 24, -23, -18, 6, -29, 30, 2, 28, 49, -11, -46, 10, 43, -13, -9, -1, -3, -7, -7, -17, -6, 97, -33, -21, 3, 5, 1, 12, -43, -8, 28, 7, -43, -7, 17, -20, 19, -1, 2, -13, 9, 54, 34, 9, -28, -11, -9, -17, 110, -59, 44, -26, 0, 3, -12, -47, 73, -34, -43, 38, -33, 16, -5, -46, -4, -6, -2, -25, 19, -29, 28, -13, 5, 14, 27, -40, -43, 4, 32, -13, -2, -35, -4, 112, -42, 9, -12, 37, -28, 17, 14, -19, 35, -39, 23, 3, -14, -1, -57, -5, 94, -9, 3, -39, 5, 30, -10, -32, 42, -13, -14, -97, -63, 30, -9, 1, -7, 12, 5, 20, 17, -9, -36, -30, 25, 47, -9, -15, 12, -22, 98, -8, -50, 15, -27, 21, -16, -11, 2, 12, -10, 10, -3, 33, 36, -96, 0, -17, 31, -9, 9, 3, -20, 13, -11, 8, -4, 10, -10, 9, 1, 112, -70, -27, 5, -21, 2, -57, -3, -29, 10, 19, -21, 21, -10, -66, -3, 91, -35, 30, -12, 0, -7, 59, -28, 26, 2, 14, -18, 1, 1, 11, 17, 20, -54, -59, 27, 4, 29, 32, 5, 19, 12, -4, 1, 7, -10, 5, -2, 10, 0, 23, -5, 28, -104, 46, 11, 16, 3, 29, 1, -8, -14, 1, 7, -50, 88, -62, 26, 8, -17, -14, 50, 0, 32, -12, -3, -27, 18, -8, -5, 8, 3, -20, -11, 37, -12, 9, 33, 46, -101, -1, -4, 1, 6, -1, 28, -42, -15, 16, 5, -1, -2, -55, 85, 38, -9, -4, 11, -2, -9, -6, 3, -20, -10, -77, 89, 24, -3, -104, -57, -26, -31, -20, -6, -9, 14, 20, -23, 46, -15, -31, 28, 1, -15, -2, 6, -2, 31, 45, -76, 23, -25, }; ================================================ FILE: deps/pjsip/third_party/speex/libspeex/high_lsp_tables.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: high_lsp_tables.c Codebooks for high-band LSPs in SB-CELP mode Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ const signed char high_lsp_cdbk[512]={ 39,12,-14,-20,-29,-61,-67,-76, -32,-71,-67,68,77,46,34,5, -13,-48,-46,-72,-81,-84,-60,-58, -40,-28,82,93,68,45,29,3, -19,-47,-28,-43,-35,-30,-8,-13, -39,-91,-91,-123,-96,10,10,-6, -18,-55,-60,-91,-56,-36,-27,-16, -48,-75,40,28,-10,-28,35,9, 37,19,1,-20,-31,-41,-18,-25, -35,-68,-80,45,27,-1,47,13, 0,-29,-35,-57,-50,-79,-73,-38, -19,5,35,14,-10,-23,16,-8, 5,-24,-40,-62,-23,-27,-22,-16, -18,-46,-72,-77,43,21,33,1, -80,-70,-70,-64,-56,-52,-39,-33, -31,-38,-19,-19,-15,32,33,-2, 7,-15,-15,-24,-23,-33,-41,-56, -24,-57,5,89,64,41,27,5, -9,-47,-60,-97,-97,-124,-20,-9, -44,-73,31,29,-4,64,48,7, -35,-57,0,-3,-26,-47,-3,-6, -40,-76,-79,-48,12,81,55,10, 9,-24,-43,-73,-57,-69,16,5, -28,-53,18,29,20,0,-4,-11, 6,-13,23,7,-17,-35,-37,-37, -30,-68,-63,6,24,-9,-14,3, 21,-13,-27,-57,-49,-80,-24,-41, -5,-16,-5,1,45,25,12,-7, 3,-15,-6,-16,-15,-8,6,-13, -42,-81,-80,-87,14,1,-10,-3, -43,-69,-46,-24,-28,-29,36,6, -43,-56,-12,12,54,79,43,9, 54,22,2,8,-12,-43,-46,-52, -38,-69,-89,-5,75,38,33,5, -13,-53,-62,-87,-89,-113,-99,-55, -34,-37,62,55,33,16,21,-2, -17,-46,-29,-38,-38,-48,-39,-42, -36,-75,-72,-88,-48,-30,21,2, -15,-57,-64,-98,-84,-76,25,1, -46,-80,-12,18,-7,3,34,6, 38,31,23,4,-1,20,14,-15, -43,-78,-91,-24,14,-3,54,16, 0,-27,-28,-44,-56,-83,-92,-89, -3,34,56,41,36,22,20,-8, -7,-35,-42,-62,-49,3,12,-10, -50,-87,-96,-66,92,70,38,9, -70,-71,-62,-42,-39,-43,-11,-7, -50,-79,-58,-50,-31,32,31,-6, -4,-25,7,-17,-38,-70,-58,-27, -43,-83,-28,59,36,20,31,2, -27,-71,-80,-109,-98,-75,-33,-32, -31,-2,33,15,-6,43,33,-5, 0,-22,-10,-27,-34,-49,-11,-20, -41,-91,-100,-121,-39,57,41,10, -19,-50,-38,-59,-60,-70,-18,-20, -8,-31,-8,-15,1,-14,-26,-25, 33,21,32,17,1,-19,-19,-26, -58,-81,-35,-22,45,30,11,-11, 3,-26,-48,-87,-67,-83,-58,3, -1,-26,-20,44,10,25,39,5, -9,-35,-27,-38,7,10,4,-9, -42,-85,-102,-127,52,44,28,10, -47,-61,-40,-39,-17,-1,-10,-33, -42,-74,-48,21,-4,70,52,10}; const signed char high_lsp_cdbk2[512]={ -36,-62,6,-9,-10,-14,-56,23, 1,-26,23,-48,-17,12,8,-7, 23,29,-36,-28,-6,-29,-17,-5, 40,23,10,10,-46,-13,36,6, 4,-30,-29,62,32,-32,-1,22, -14,1,-4,-22,-45,2,54,4, -30,-57,-59,-12,27,-3,-31,8, -9,5,10,-14,32,66,19,9, 2,-25,-37,23,-15,18,-38,-31, 5,-9,-21,15,0,22,62,30, 15,-12,-14,-46,77,21,33,3, 34,29,-19,50,2,11,9,-38, -12,-37,62,1,-15,54,32,6, 2,-24,20,35,-21,2,19,24, -13,55,4,9,39,-19,30,-1, -21,73,54,33,8,18,3,15, 6,-19,-47,6,-3,-48,-50,1, 26,20,8,-23,-50,65,-14,-55, -17,-31,-37,-28,53,-1,-17,-53, 1,57,11,-8,-25,-30,-37,64, 5,-52,-45,15,23,31,15,14, -25,24,33,-2,-44,-56,-18,6, -21,-43,4,-12,17,-37,20,-10, 34,15,2,15,55,21,-11,-31, -6,46,25,16,-9,-25,-8,-62, 28,17,20,-32,-29,26,30,25, -19,2,-16,-17,26,-51,2,50, 42,19,-66,23,29,-2,3,19, -19,-37,32,15,6,30,-34,13, 11,-5,40,31,10,-42,4,-9, 26,-9,-70,17,-2,-23,20,-22, -55,51,-24,-31,22,-22,15,-13, 3,-10,-28,-16,56,4,-63,11, -18,-15,-18,-38,-35,16,-7,34, -1,-21,-49,-47,9,-37,7,8, 69,55,20,6,-33,-45,-10,-9, 6,-9,12,71,15,-3,-42,-7, -24,32,-35,-2,-42,-17,-5,0, -2,-33,-54,13,-12,-34,47,23, 19,55,7,-8,74,31,14,16, -23,-26,19,12,-18,-49,-28,-31, -20,2,-14,-20,-47,78,40,13, -23,-11,21,-6,18,1,47,5, 38,35,32,46,22,8,13,16, -14,18,51,19,40,39,11,-26, -1,-17,47,2,-53,-15,31,-22, 38,21,-15,-16,5,-33,53,15, -38,86,11,-3,-24,49,13,-4, -11,-18,28,20,-12,-27,-26,35, -25,-35,-3,-20,-61,30,10,-55, -12,-22,-52,-54,-14,19,-32,-12, 45,15,-8,-48,-9,11,-32,8, -16,-34,-13,51,18,38,-2,-32, -17,22,-2,-18,-28,-70,59,27, -28,-19,-10,-20,-9,-9,-8,-21, 21,-8,35,-2,45,-3,-9,12, 0,30,7,-39,43,27,-38,-91, 30,26,19,-55,-4,63,14,-17, 13,9,13,2,7,4,6,61, 72,-1,-17,29,-1,-22,-17,8, -28,-37,63,44,41,3,2,14, 9,-6,75,-8,-7,-12,-15,-12, 13,9,-4,30,-22,-65,15,0, -45,4,-4,1,5,22,11,23}; ================================================ FILE: deps/pjsip/third_party/speex/libspeex/jitter.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: speex_jitter.h Adaptive jitter buffer for Speex Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* TODO: - Add short-term estimate - Defensive programming + warn when last returned < last desired (begative buffering) + warn if update_delay not called between get() and tick() or is called twice in a row - Linked list structure for holding the packets instead of the current fixed-size array + return memory to a pool + allow pre-allocation of the pool + optional max number of elements - Statistics + drift + loss + late + jitter + buffering delay */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "arch.h" #include #include #include #include "os_support.h" #ifndef NULL #define NULL 0 #endif #define SPEEX_JITTER_MAX_BUFFER_SIZE 200 /**< Maximum number of packets in jitter buffer */ #define TSUB(a,b) ((spx_int32_t)((a)-(b))) #define GT32(a,b) (((spx_int32_t)((a)-(b)))>0) #define GE32(a,b) (((spx_int32_t)((a)-(b)))>=0) #define LT32(a,b) (((spx_int32_t)((a)-(b)))<0) #define LE32(a,b) (((spx_int32_t)((a)-(b)))<=0) #define ROUND_DOWN(x, step) ((x)<0 ? ((x)-(step)+1)/(step)*(step) : (x)/(step)*(step)) #define MAX_TIMINGS 40 #define MAX_BUFFERS 3 #define TOP_DELAY 40 /** Buffer that keeps the time of arrival of the latest packets */ struct TimingBuffer { int filled; /**< Number of entries occupied in "timing" and "counts"*/ int curr_count; /**< Number of packet timings we got (including those we discarded) */ spx_int32_t timing[MAX_TIMINGS]; /**< Sorted list of all timings ("latest" packets first) */ spx_int16_t counts[MAX_TIMINGS]; /**< Order the packets were put in (will be used for short-term estimate) */ }; static void tb_init(struct TimingBuffer *tb) { tb->filled = 0; tb->curr_count = 0; } /* Add the timing of a new packet to the TimingBuffer */ static void tb_add(struct TimingBuffer *tb, spx_int16_t timing) { int pos; /* Discard packet that won't make it into the list because they're too early */ if (tb->filled >= MAX_TIMINGS && timing >= tb->timing[tb->filled-1]) { tb->curr_count++; return; } /* Find where the timing info goes in the sorted list */ pos = 0; /* FIXME: Do bisection instead of linear search */ while (posfilled && timing >= tb->timing[pos]) { pos++; } speex_assert(pos <= tb->filled && pos < MAX_TIMINGS); /* Shift everything so we can perform the insertion */ if (pos < tb->filled) { int move_size = tb->filled-pos; if (tb->filled == MAX_TIMINGS) move_size -= 1; SPEEX_MOVE(&tb->timing[pos+1], &tb->timing[pos], move_size); SPEEX_MOVE(&tb->counts[pos+1], &tb->counts[pos], move_size); } /* Insert */ tb->timing[pos] = timing; tb->counts[pos] = tb->curr_count; tb->curr_count++; if (tb->filledfilled++; } /** Jitter buffer structure */ struct JitterBuffer_ { spx_uint32_t pointer_timestamp; /**< Timestamp of what we will *get* next */ spx_uint32_t last_returned_timestamp; /**< Useful for getting the next packet with the same timestamp (for fragmented media) */ spx_uint32_t next_stop; /**< Estimated time the next get() will be called */ spx_int32_t buffered; /**< Amount of data we think is still buffered by the application (timestamp units)*/ JitterBufferPacket packets[SPEEX_JITTER_MAX_BUFFER_SIZE]; /**< Packets stored in the buffer */ spx_uint32_t arrival[SPEEX_JITTER_MAX_BUFFER_SIZE]; /**< Packet arrival time (0 means it was late, even though it's a valid timestamp) */ void (*destroy) (void *); /**< Callback for destroying a packet */ spx_int32_t delay_step; /**< Size of the steps when adjusting buffering (timestamp units) */ spx_int32_t concealment_size; /**< Size of the packet loss concealment "units" */ int reset_state; /**< True if state was just reset */ int buffer_margin; /**< How many frames we want to keep in the buffer (lower bound) */ int late_cutoff; /**< How late must a packet be for it not to be considered at all */ int interp_requested; /**< An interpolation is requested by speex_jitter_update_delay() */ int auto_adjust; /**< Whether to automatically adjust the delay at any time */ struct TimingBuffer _tb[MAX_BUFFERS]; /**< Don't use those directly */ struct TimingBuffer *timeBuffers[MAX_BUFFERS]; /**< Storing arrival time of latest frames so we can compute some stats */ int window_size; /**< Total window over which the late frames are counted */ int subwindow_size; /**< Sub-window size for faster computation */ int max_late_rate; /**< Absolute maximum amount of late packets tolerable (in percent) */ int latency_tradeoff; /**< Latency equivalent of losing one percent of packets */ int auto_tradeoff; /**< Latency equivalent of losing one percent of packets (automatic default) */ int lost_count; /**< Number of consecutive lost packets */ }; /** Based on available data, this computes the optimal delay for the jitter buffer. The optimised function is in timestamp units and is: cost = delay + late_factor*[number of frames that would be late if we used that delay] @param tb Array of buffers @param late_factor Equivalent cost of a late frame (in timestamp units) */ static spx_int16_t compute_opt_delay(JitterBuffer *jitter) { int i; spx_int16_t opt=0; spx_int32_t best_cost=0x7fffffff; int late = 0; int pos[MAX_BUFFERS]; int tot_count; float late_factor; int penalty_taken = 0; int best = 0; int worst = 0; spx_int32_t deltaT; struct TimingBuffer *tb; tb = jitter->_tb; /* Number of packet timings we have received (including those we didn't keep) */ tot_count = 0; for (i=0;ilatency_tradeoff != 0) late_factor = jitter->latency_tradeoff * 100.0f / tot_count; else late_factor = jitter->auto_tradeoff * jitter->window_size/tot_count; /*fprintf(stderr, "late_factor = %f\n", late_factor);*/ for (i=0;idelay_step); pos[next]++; /* Actual cost function that tells us how bad using this delay would be */ cost = -latest + late_factor*late; /*fprintf(stderr, "cost %d = %d + %f * %d\n", cost, -latest, late_factor, late);*/ if (cost < best_cost) { best_cost = cost; opt = latest; } } else { break; } /* For the next timing we will consider, there will be one more late packet to count */ late++; /* Two-frame penalty if we're going to increase the amount of late frames (hysteresis) */ if (latest >= 0 && !penalty_taken) { penalty_taken = 1; late+=4; } } deltaT = best-worst; /* This is a default "automatic latency tradeoff" when none is provided */ jitter->auto_tradeoff = 1 + deltaT/TOP_DELAY; /*fprintf(stderr, "auto_tradeoff = %d (%d %d %d)\n", jitter->auto_tradeoff, best, worst, i);*/ /* FIXME: Compute a short-term estimate too and combine with the long-term one */ /* Prevents reducing the buffer size when we haven't really had much data */ if (tot_count < TOP_DELAY && opt > 0) return 0; return opt; } /** Initialise jitter buffer */ EXPORT JitterBuffer *jitter_buffer_init(int step_size) { JitterBuffer *jitter = (JitterBuffer*)speex_alloc(sizeof(JitterBuffer)); if (jitter) { int i; spx_int32_t tmp; for (i=0;ipackets[i].data=NULL; jitter->delay_step = step_size; jitter->concealment_size = step_size; /*FIXME: Should this be 0 or 1?*/ jitter->buffer_margin = 0; jitter->late_cutoff = 50; jitter->destroy = NULL; jitter->latency_tradeoff = 0; jitter->auto_adjust = 1; tmp = 4; jitter_buffer_ctl(jitter, JITTER_BUFFER_SET_MAX_LATE_RATE, &tmp); jitter_buffer_reset(jitter); } return jitter; } /** Reset jitter buffer */ EXPORT void jitter_buffer_reset(JitterBuffer *jitter) { int i; for (i=0;ipackets[i].data) { if (jitter->destroy) jitter->destroy(jitter->packets[i].data); else speex_free(jitter->packets[i].data); jitter->packets[i].data = NULL; } } /* Timestamp is actually undefined at this point */ jitter->pointer_timestamp = 0; jitter->next_stop = 0; jitter->reset_state = 1; jitter->lost_count = 0; jitter->buffered = 0; jitter->auto_tradeoff = 32000; for (i=0;i_tb[i]); jitter->timeBuffers[i] = &jitter->_tb[i]; } /*fprintf (stderr, "reset\n");*/ } /** Destroy jitter buffer */ EXPORT void jitter_buffer_destroy(JitterBuffer *jitter) { jitter_buffer_reset(jitter); speex_free(jitter); } /** Take the following timing into consideration for future calculations */ static void update_timings(JitterBuffer *jitter, spx_int32_t timing) { if (timing < -32767) timing = -32767; if (timing > 32767) timing = 32767; /* If the current sub-window is full, perform a rotation and discard oldest sub-widow */ if (jitter->timeBuffers[0]->curr_count >= jitter->subwindow_size) { int i; /*fprintf(stderr, "Rotate buffer\n");*/ struct TimingBuffer *tmp = jitter->timeBuffers[MAX_BUFFERS-1]; for (i=MAX_BUFFERS-1;i>=1;i--) jitter->timeBuffers[i] = jitter->timeBuffers[i-1]; jitter->timeBuffers[0] = tmp; tb_init(jitter->timeBuffers[0]); } tb_add(jitter->timeBuffers[0], timing); } /** Compensate all timings when we do an adjustment of the buffering */ static void shift_timings(JitterBuffer *jitter, spx_int16_t amount) { int i, j; for (i=0;itimeBuffers[i]->filled;j++) jitter->timeBuffers[i]->timing[j] += amount; } } /** Put one packet into the jitter buffer */ EXPORT void jitter_buffer_put(JitterBuffer *jitter, const JitterBufferPacket *packet) { int i,j; int late; /*fprintf (stderr, "put packet %d %d\n", timestamp, span);*/ /* Cleanup buffer (remove old packets that weren't played) */ if (!jitter->reset_state) { for (i=0;ipackets[i].data && LE32(jitter->packets[i].timestamp + jitter->packets[i].span, jitter->pointer_timestamp)) { /*fprintf (stderr, "cleaned (not played)\n");*/ if (jitter->destroy) jitter->destroy(jitter->packets[i].data); else speex_free(jitter->packets[i].data); jitter->packets[i].data = NULL; } } } /*fprintf(stderr, "arrival: %d %d %d\n", packet->timestamp, jitter->next_stop, jitter->pointer_timestamp);*/ /* Check if packet is late (could still be useful though) */ if (!jitter->reset_state && LT32(packet->timestamp, jitter->next_stop)) { update_timings(jitter, ((spx_int32_t)packet->timestamp) - ((spx_int32_t)jitter->next_stop) - jitter->buffer_margin); late = 1; } else { late = 0; } /* For some reason, the consumer has failed the last 20 fetches. Make sure this packet is * used to resync. */ if (jitter->lost_count>20) { jitter_buffer_reset(jitter); } /* Only insert the packet if it's not hopelessly late (i.e. totally useless) */ if (jitter->reset_state || GE32(packet->timestamp+packet->span+jitter->delay_step, jitter->pointer_timestamp)) { /*Find an empty slot in the buffer*/ for (i=0;ipackets[i].data==NULL) break; } /*No place left in the buffer, need to make room for it by discarding the oldest packet */ if (i==SPEEX_JITTER_MAX_BUFFER_SIZE) { int earliest=jitter->packets[0].timestamp; i=0; for (j=1;jpackets[i].data || LT32(jitter->packets[j].timestamp,earliest)) { earliest = jitter->packets[j].timestamp; i=j; } } if (jitter->destroy) jitter->destroy(jitter->packets[i].data); else speex_free(jitter->packets[i].data); jitter->packets[i].data=NULL; /*fprintf (stderr, "Buffer is full, discarding earliest frame %d (currently at %d)\n", timestamp, jitter->pointer_timestamp);*/ } /* Copy packet in buffer */ if (jitter->destroy) { jitter->packets[i].data = packet->data; } else { jitter->packets[i].data=(char*)speex_alloc(packet->len); for (j=0;jlen;j++) jitter->packets[i].data[j]=packet->data[j]; } jitter->packets[i].timestamp=packet->timestamp; jitter->packets[i].span=packet->span; jitter->packets[i].len=packet->len; jitter->packets[i].sequence=packet->sequence; jitter->packets[i].user_data=packet->user_data; if (jitter->reset_state || late) jitter->arrival[i] = 0; else jitter->arrival[i] = jitter->next_stop; } } /** Get one packet from the jitter buffer */ EXPORT int jitter_buffer_get(JitterBuffer *jitter, JitterBufferPacket *packet, spx_int32_t desired_span, spx_int32_t *start_offset) { int i; unsigned int j; int incomplete = 0; spx_int16_t opt; if (start_offset != NULL) *start_offset = 0; /* Syncing on the first call */ if (jitter->reset_state) { int found = 0; /* Find the oldest packet */ spx_uint32_t oldest=0; for (i=0;ipackets[i].data && (!found || LT32(jitter->packets[i].timestamp,oldest))) { oldest = jitter->packets[i].timestamp; found = 1; } } if (found) { jitter->reset_state=0; jitter->pointer_timestamp = oldest; jitter->next_stop = oldest; } else { packet->timestamp = 0; packet->span = jitter->interp_requested; return JITTER_BUFFER_MISSING; } } jitter->last_returned_timestamp = jitter->pointer_timestamp; if (jitter->interp_requested != 0) { packet->timestamp = jitter->pointer_timestamp; packet->span = jitter->interp_requested; /* Increment the pointer because it got decremented in the delay update */ jitter->pointer_timestamp += jitter->interp_requested; packet->len = 0; /*fprintf (stderr, "Deferred interpolate\n");*/ jitter->interp_requested = 0; jitter->buffered = packet->span - desired_span; return JITTER_BUFFER_INSERTION; } /* Searching for the packet that fits best */ /* Search the buffer for a packet with the right timestamp and spanning the whole current chunk */ for (i=0;ipackets[i].data && jitter->packets[i].timestamp==jitter->pointer_timestamp && GE32(jitter->packets[i].timestamp+jitter->packets[i].span,jitter->pointer_timestamp+desired_span)) break; } /* If no match, try for an "older" packet that still spans (fully) the current chunk */ if (i==SPEEX_JITTER_MAX_BUFFER_SIZE) { for (i=0;ipackets[i].data && LE32(jitter->packets[i].timestamp, jitter->pointer_timestamp) && GE32(jitter->packets[i].timestamp+jitter->packets[i].span,jitter->pointer_timestamp+desired_span)) break; } } /* If still no match, try for an "older" packet that spans part of the current chunk */ if (i==SPEEX_JITTER_MAX_BUFFER_SIZE) { for (i=0;ipackets[i].data && LE32(jitter->packets[i].timestamp, jitter->pointer_timestamp) && GT32(jitter->packets[i].timestamp+jitter->packets[i].span,jitter->pointer_timestamp)) break; } } /* If still no match, try for earliest packet possible */ if (i==SPEEX_JITTER_MAX_BUFFER_SIZE) { int found = 0; spx_uint32_t best_time=0; int best_span=0; int besti=0; for (i=0;ipackets[i].data && LT32(jitter->packets[i].timestamp,jitter->pointer_timestamp+desired_span) && GE32(jitter->packets[i].timestamp,jitter->pointer_timestamp)) { if (!found || LT32(jitter->packets[i].timestamp,best_time) || (jitter->packets[i].timestamp==best_time && GT32(jitter->packets[i].span,best_span))) { best_time = jitter->packets[i].timestamp; best_span = jitter->packets[i].span; besti = i; found = 1; } } } if (found) { i=besti; incomplete = 1; /*fprintf (stderr, "incomplete: %d %d %d %d\n", jitter->packets[i].timestamp, jitter->pointer_timestamp, chunk_size, jitter->packets[i].span);*/ } } /* If we find something */ if (i!=SPEEX_JITTER_MAX_BUFFER_SIZE) { spx_int32_t offset; /* We (obviously) haven't lost this packet */ jitter->lost_count = 0; /* In this case, 0 isn't as a valid timestamp */ if (jitter->arrival[i] != 0) { update_timings(jitter, ((spx_int32_t)jitter->packets[i].timestamp) - ((spx_int32_t)jitter->arrival[i]) - jitter->buffer_margin); } /* Copy packet */ if (jitter->destroy) { packet->data = jitter->packets[i].data; packet->len = jitter->packets[i].len; } else { if (jitter->packets[i].len > packet->len) { speex_warning_int("jitter_buffer_get(): packet too large to fit. Size is", jitter->packets[i].len); } else { packet->len = jitter->packets[i].len; } for (j=0;jlen;j++) packet->data[j] = jitter->packets[i].data[j]; /* Remove packet */ speex_free(jitter->packets[i].data); } jitter->packets[i].data = NULL; /* Set timestamp and span (if requested) */ offset = (spx_int32_t)jitter->packets[i].timestamp-(spx_int32_t)jitter->pointer_timestamp; if (start_offset != NULL) *start_offset = offset; else if (offset != 0) speex_warning_int("jitter_buffer_get() discarding non-zero start_offset", offset); packet->timestamp = jitter->packets[i].timestamp; jitter->last_returned_timestamp = packet->timestamp; packet->span = jitter->packets[i].span; packet->sequence = jitter->packets[i].sequence; packet->user_data = jitter->packets[i].user_data; /* Point to the end of the current packet */ jitter->pointer_timestamp = jitter->packets[i].timestamp+jitter->packets[i].span; jitter->buffered = packet->span - desired_span; if (start_offset != NULL) jitter->buffered += *start_offset; return JITTER_BUFFER_OK; } /* If we haven't found anything worth returning */ /*fprintf (stderr, "not found\n");*/ jitter->lost_count++; /*fprintf (stderr, "m");*/ /*fprintf (stderr, "lost_count = %d\n", jitter->lost_count);*/ opt = compute_opt_delay(jitter); /* Should we force an increase in the buffer or just do normal interpolation? */ if (opt < 0) { /* Need to increase buffering */ /* Shift histogram to compensate */ shift_timings(jitter, -opt); packet->timestamp = jitter->pointer_timestamp; packet->span = -opt; /* Don't move the pointer_timestamp forward */ packet->len = 0; jitter->buffered = packet->span - desired_span; return JITTER_BUFFER_INSERTION; /*jitter->pointer_timestamp -= jitter->delay_step;*/ /*fprintf (stderr, "Forced to interpolate\n");*/ } else { /* Normal packet loss */ packet->timestamp = jitter->pointer_timestamp; desired_span = ROUND_DOWN(desired_span, jitter->concealment_size); packet->span = desired_span; jitter->pointer_timestamp += desired_span; packet->len = 0; jitter->buffered = packet->span - desired_span; return JITTER_BUFFER_MISSING; /*fprintf (stderr, "Normal loss\n");*/ } } EXPORT int jitter_buffer_get_another(JitterBuffer *jitter, JitterBufferPacket *packet) { int i, j; for (i=0;ipackets[i].data && jitter->packets[i].timestamp==jitter->last_returned_timestamp) break; } if (i!=SPEEX_JITTER_MAX_BUFFER_SIZE) { /* Copy packet */ packet->len = jitter->packets[i].len; if (jitter->destroy) { packet->data = jitter->packets[i].data; } else { for (j=0;jlen;j++) packet->data[j] = jitter->packets[i].data[j]; /* Remove packet */ speex_free(jitter->packets[i].data); } jitter->packets[i].data = NULL; packet->timestamp = jitter->packets[i].timestamp; packet->span = jitter->packets[i].span; packet->sequence = jitter->packets[i].sequence; packet->user_data = jitter->packets[i].user_data; return JITTER_BUFFER_OK; } else { packet->data = NULL; packet->len = 0; packet->span = 0; return JITTER_BUFFER_MISSING; } } /* Let the jitter buffer know it's the right time to adjust the buffering delay to the network conditions */ static int _jitter_buffer_update_delay(JitterBuffer *jitter, JitterBufferPacket *packet, spx_int32_t *start_offset) { spx_int16_t opt = compute_opt_delay(jitter); /*fprintf(stderr, "opt adjustment is %d ", opt);*/ if (opt < 0) { shift_timings(jitter, -opt); jitter->pointer_timestamp += opt; jitter->interp_requested = -opt; /*fprintf (stderr, "Decision to interpolate %d samples\n", -opt);*/ } else if (opt > 0) { shift_timings(jitter, -opt); jitter->pointer_timestamp += opt; /*fprintf (stderr, "Decision to drop %d samples\n", opt);*/ } return opt; } /* Let the jitter buffer know it's the right time to adjust the buffering delay to the network conditions */ EXPORT int jitter_buffer_update_delay(JitterBuffer *jitter, JitterBufferPacket *packet, spx_int32_t *start_offset) { /* If the programmer calls jitter_buffer_update_delay() directly, automatically disable auto-adjustment */ jitter->auto_adjust = 0; return _jitter_buffer_update_delay(jitter, packet, start_offset); } /** Get pointer timestamp of jitter buffer */ EXPORT int jitter_buffer_get_pointer_timestamp(JitterBuffer *jitter) { return jitter->pointer_timestamp; } EXPORT void jitter_buffer_tick(JitterBuffer *jitter) { /* Automatically-adjust the buffering delay if requested */ if (jitter->auto_adjust) _jitter_buffer_update_delay(jitter, NULL, NULL); if (jitter->buffered >= 0) { jitter->next_stop = jitter->pointer_timestamp - jitter->buffered; } else { jitter->next_stop = jitter->pointer_timestamp; speex_warning_int("jitter buffer sees negative buffering, your code might be broken. Value is ", jitter->buffered); } jitter->buffered = 0; } EXPORT void jitter_buffer_remaining_span(JitterBuffer *jitter, spx_uint32_t rem) { /* Automatically-adjust the buffering delay if requested */ if (jitter->auto_adjust) _jitter_buffer_update_delay(jitter, NULL, NULL); if (jitter->buffered < 0) speex_warning_int("jitter buffer sees negative buffering, your code might be broken. Value is ", jitter->buffered); jitter->next_stop = jitter->pointer_timestamp - rem; } /* Used like the ioctl function to control the jitter buffer parameters */ EXPORT int jitter_buffer_ctl(JitterBuffer *jitter, int request, void *ptr) { int count, i; switch(request) { case JITTER_BUFFER_SET_MARGIN: jitter->buffer_margin = *(spx_int32_t*)ptr; break; case JITTER_BUFFER_GET_MARGIN: *(spx_int32_t*)ptr = jitter->buffer_margin; break; case JITTER_BUFFER_GET_AVALIABLE_COUNT: count = 0; for (i=0;ipackets[i].data && LE32(jitter->pointer_timestamp, jitter->packets[i].timestamp)) { count++; } } *(spx_int32_t*)ptr = count; break; case JITTER_BUFFER_SET_DESTROY_CALLBACK: jitter->destroy = (void (*) (void *))ptr; break; case JITTER_BUFFER_GET_DESTROY_CALLBACK: *(void (**) (void *))ptr = jitter->destroy; break; case JITTER_BUFFER_SET_DELAY_STEP: jitter->delay_step = *(spx_int32_t*)ptr; break; case JITTER_BUFFER_GET_DELAY_STEP: *(spx_int32_t*)ptr = jitter->delay_step; break; case JITTER_BUFFER_SET_CONCEALMENT_SIZE: jitter->concealment_size = *(spx_int32_t*)ptr; break; case JITTER_BUFFER_GET_CONCEALMENT_SIZE: *(spx_int32_t*)ptr = jitter->concealment_size; break; case JITTER_BUFFER_SET_MAX_LATE_RATE: jitter->max_late_rate = *(spx_int32_t*)ptr; jitter->window_size = 100*TOP_DELAY/jitter->max_late_rate; jitter->subwindow_size = jitter->window_size/MAX_BUFFERS; break; case JITTER_BUFFER_GET_MAX_LATE_RATE: *(spx_int32_t*)ptr = jitter->max_late_rate; break; case JITTER_BUFFER_SET_LATE_COST: jitter->latency_tradeoff = *(spx_int32_t*)ptr; break; case JITTER_BUFFER_GET_LATE_COST: *(spx_int32_t*)ptr = jitter->latency_tradeoff; break; default: speex_warning_int("Unknown jitter_buffer_ctl request: ", request); return -1; } return 0; } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/kiss_fft.c ================================================ /* Copyright (c) 2003-2004, Mark Borgerding Copyright (c) 2005-2007, Jean-Marc Valin All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "_kiss_fft_guts.h" #include "arch.h" #include "os_support.h" /* The guts header contains all the multiplication and addition macros that are defined for fixed or floating point complex numbers. It also delares the kf_ internal functions. */ static void kf_bfly2( kiss_fft_cpx * Fout, const size_t fstride, const kiss_fft_cfg st, int m, int N, int mm ) { kiss_fft_cpx * Fout2; kiss_fft_cpx * tw1; kiss_fft_cpx t; if (!st->inverse) { int i,j; kiss_fft_cpx * Fout_beg = Fout; for (i=0;itwiddles; for(j=0;jr , tw1->r),MULT16_16(Fout2->i , tw1->i)), 1); ti = SHR32(ADD32(MULT16_16(Fout2->i , tw1->r),MULT16_16(Fout2->r , tw1->i)), 1); tw1 += fstride; Fout2->r = PSHR32(SUB32(SHL32(EXTEND32(Fout->r), 14), tr), 15); Fout2->i = PSHR32(SUB32(SHL32(EXTEND32(Fout->i), 14), ti), 15); Fout->r = PSHR32(ADD32(SHL32(EXTEND32(Fout->r), 14), tr), 15); Fout->i = PSHR32(ADD32(SHL32(EXTEND32(Fout->i), 14), ti), 15); ++Fout2; ++Fout; } } } else { int i,j; kiss_fft_cpx * Fout_beg = Fout; for (i=0;itwiddles; for(j=0;jinverse) { kiss_fft_cpx * Fout_beg = Fout; for (i=0;itwiddles; for (j=0;jtwiddles; for (j=0;jr = PSHR16(Fout->r, 2); Fout->i = PSHR16(Fout->i, 2); C_SUB( scratch[5] , *Fout, scratch[1] ); C_ADDTO(*Fout, scratch[1]); C_ADD( scratch[3] , scratch[0] , scratch[2] ); C_SUB( scratch[4] , scratch[0] , scratch[2] ); Fout[m2].r = PSHR16(Fout[m2].r, 2); Fout[m2].i = PSHR16(Fout[m2].i, 2); C_SUB( Fout[m2], *Fout, scratch[3] ); tw1 += fstride; tw2 += fstride*2; tw3 += fstride*3; C_ADDTO( *Fout , scratch[3] ); Fout[m].r = scratch[5].r + scratch[4].i; Fout[m].i = scratch[5].i - scratch[4].r; Fout[m3].r = scratch[5].r - scratch[4].i; Fout[m3].i = scratch[5].i + scratch[4].r; ++Fout; } } } } static void kf_bfly3( kiss_fft_cpx * Fout, const size_t fstride, const kiss_fft_cfg st, size_t m ) { size_t k=m; const size_t m2 = 2*m; kiss_fft_cpx *tw1,*tw2; kiss_fft_cpx scratch[5]; kiss_fft_cpx epi3; epi3 = st->twiddles[fstride*m]; tw1=tw2=st->twiddles; do{ if (!st->inverse) { C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3); } C_MUL(scratch[1],Fout[m] , *tw1); C_MUL(scratch[2],Fout[m2] , *tw2); C_ADD(scratch[3],scratch[1],scratch[2]); C_SUB(scratch[0],scratch[1],scratch[2]); tw1 += fstride; tw2 += fstride*2; Fout[m].r = Fout->r - HALF_OF(scratch[3].r); Fout[m].i = Fout->i - HALF_OF(scratch[3].i); C_MULBYSCALAR( scratch[0] , epi3.i ); C_ADDTO(*Fout,scratch[3]); Fout[m2].r = Fout[m].r + scratch[0].i; Fout[m2].i = Fout[m].i - scratch[0].r; Fout[m].r -= scratch[0].i; Fout[m].i += scratch[0].r; ++Fout; }while(--k); } static void kf_bfly5( kiss_fft_cpx * Fout, const size_t fstride, const kiss_fft_cfg st, int m ) { kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4; int u; kiss_fft_cpx scratch[13]; kiss_fft_cpx * twiddles = st->twiddles; kiss_fft_cpx *tw; kiss_fft_cpx ya,yb; ya = twiddles[fstride*m]; yb = twiddles[fstride*2*m]; Fout0=Fout; Fout1=Fout0+m; Fout2=Fout0+2*m; Fout3=Fout0+3*m; Fout4=Fout0+4*m; tw=st->twiddles; for ( u=0; uinverse) { C_FIXDIV( *Fout0,5); C_FIXDIV( *Fout1,5); C_FIXDIV( *Fout2,5); C_FIXDIV( *Fout3,5); C_FIXDIV( *Fout4,5); } scratch[0] = *Fout0; C_MUL(scratch[1] ,*Fout1, tw[u*fstride]); C_MUL(scratch[2] ,*Fout2, tw[2*u*fstride]); C_MUL(scratch[3] ,*Fout3, tw[3*u*fstride]); C_MUL(scratch[4] ,*Fout4, tw[4*u*fstride]); C_ADD( scratch[7],scratch[1],scratch[4]); C_SUB( scratch[10],scratch[1],scratch[4]); C_ADD( scratch[8],scratch[2],scratch[3]); C_SUB( scratch[9],scratch[2],scratch[3]); Fout0->r += scratch[7].r + scratch[8].r; Fout0->i += scratch[7].i + scratch[8].i; scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r); scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r); scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i); scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i); C_SUB(*Fout1,scratch[5],scratch[6]); C_ADD(*Fout4,scratch[5],scratch[6]); scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r); scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r); scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i); scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i); C_ADD(*Fout2,scratch[11],scratch[12]); C_SUB(*Fout3,scratch[11],scratch[12]); ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4; } } /* perform the butterfly for one stage of a mixed radix FFT */ static void kf_bfly_generic( kiss_fft_cpx * Fout, const size_t fstride, const kiss_fft_cfg st, int m, int p ) { int u,k,q1,q; kiss_fft_cpx * twiddles = st->twiddles; kiss_fft_cpx t; kiss_fft_cpx scratchbuf[17]; int Norig = st->nfft; /*CHECKBUF(scratchbuf,nscratchbuf,p);*/ if (p>17) speex_fatal("KissFFT: max radix supported is 17"); for ( u=0; uinverse) { C_FIXDIV(scratchbuf[q1],p); } k += m; } k=u; for ( q1=0 ; q1

=Norig) twidx-=Norig; C_MUL(t,scratchbuf[q] , twiddles[twidx] ); C_ADDTO( Fout[ k ] ,t); } k += m; } } } static void kf_shuffle( kiss_fft_cpx * Fout, const kiss_fft_cpx * f, const size_t fstride, int in_stride, int * factors, const kiss_fft_cfg st ) { const int p=*factors++; /* the radix */ const int m=*factors++; /* stage's fft length/p */ /*printf ("fft %d %d %d %d %d %d\n", p*m, m, p, s2, fstride*in_stride, N);*/ if (m==1) { int j; for (j=0;j32000 || (spx_int32_t)p*(spx_int32_t)p > n) p = n; /* no more factors, skip to end */ } n /= p; *facbuf++ = p; *facbuf++ = n; } while (n > 1); } /* * * User-callable function to allocate all necessary storage space for the fft. * * The return value is a contiguous block of memory, allocated with malloc. As such, * It can be freed with free(), rather than a kiss_fft-specific function. * */ kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem ) { kiss_fft_cfg st=NULL; size_t memneeded = sizeof(struct kiss_fft_state) + sizeof(kiss_fft_cpx)*(nfft-1); /* twiddle factors*/ if ( lenmem==NULL ) { st = ( kiss_fft_cfg)KISS_FFT_MALLOC( memneeded ); }else{ if (mem != NULL && *lenmem >= memneeded) st = (kiss_fft_cfg)mem; *lenmem = memneeded; } if (st) { int i; st->nfft=nfft; st->inverse = inverse_fft; #ifdef FIXED_POINT for (i=0;iinverse) phase = -phase; kf_cexp2(st->twiddles+i, DIV32(SHL32(phase,17),nfft)); } #else for (i=0;iinverse) phase *= -1; kf_cexp(st->twiddles+i, phase ); } #endif kf_factor(nfft,st->factors); } return st; } void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride) { if (fin == fout) { speex_fatal("In-place FFT not supported"); /*CHECKBUF(tmpbuf,ntmpbuf,st->nfft); kf_work(tmpbuf,fin,1,in_stride, st->factors,st); SPEEX_MOVE(fout,tmpbuf,st->nfft);*/ } else { kf_shuffle( fout, fin, 1,in_stride, st->factors,st); kf_work( fout, fin, 1,in_stride, st->factors,st, 1, in_stride, 1); } } void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout) { kiss_fft_stride(cfg,fin,fout,1); } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/kiss_fft.h ================================================ #ifndef KISS_FFT_H #define KISS_FFT_H #include #include #include "arch.h" #ifdef __cplusplus extern "C" { #endif /* ATTENTION! If you would like a : -- a utility that will handle the caching of fft objects -- real-only (no imaginary time component ) FFT -- a multi-dimensional FFT -- a command-line utility to perform ffts -- a command-line utility to perform fast-convolution filtering Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c in the tools/ directory. */ #ifdef USE_SIMD # include # define kiss_fft_scalar __m128 #define KISS_FFT_MALLOC(nbytes) memalign(16,nbytes) #else #define KISS_FFT_MALLOC speex_alloc #endif #ifdef FIXED_POINT #include "arch.h" # define kiss_fft_scalar spx_int16_t #else # ifndef kiss_fft_scalar /* default is float */ # define kiss_fft_scalar float # endif #endif typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; typedef struct kiss_fft_state* kiss_fft_cfg; /* * kiss_fft_alloc * * Initialize a FFT (or IFFT) algorithm's cfg/state buffer. * * typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL); * * The return value from fft_alloc is a cfg buffer used internally * by the fft routine or NULL. * * If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc. * The returned value should be free()d when done to avoid memory leaks. * * The state can be placed in a user supplied buffer 'mem': * If lenmem is not NULL and mem is not NULL and *lenmem is large enough, * then the function places the cfg in mem and the size used in *lenmem * and returns mem. * * If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough), * then the function returns NULL and places the minimum cfg * buffer size in *lenmem. * */ kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); /* * kiss_fft(cfg,in_out_buf) * * Perform an FFT on a complex input buffer. * for a forward FFT, * fin should be f[0] , f[1] , ... ,f[nfft-1] * fout will be F[0] , F[1] , ... ,F[nfft-1] * Note that each element is complex and can be accessed like f[k].r and f[k].i * */ void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); /* A more generic version of the above function. It reads its input from every Nth sample. * */ void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); /* If kiss_fft_alloc allocated a buffer, it is one contiguous buffer and can be simply free()d when no longer needed*/ #define kiss_fft_free speex_free /* Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up your compiler output to call this before you exit. */ void kiss_fft_cleanup(void); #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/kiss_fftr.c ================================================ /* Copyright (c) 2003-2004, Mark Borgerding All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_support.h" #include "kiss_fftr.h" #include "_kiss_fft_guts.h" struct kiss_fftr_state{ kiss_fft_cfg substate; kiss_fft_cpx * tmpbuf; kiss_fft_cpx * super_twiddles; #ifdef USE_SIMD long pad; #endif }; kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem) { int i; kiss_fftr_cfg st = NULL; size_t subsize, memneeded; if (nfft & 1) { speex_warning("Real FFT optimization must be even.\n"); return NULL; } nfft >>= 1; kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize); memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 2); if (lenmem == NULL) { st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded); } else { if (*lenmem >= memneeded) st = (kiss_fftr_cfg) mem; *lenmem = memneeded; } if (!st) return NULL; st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */ st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize); st->super_twiddles = st->tmpbuf + nfft; kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize); #ifdef FIXED_POINT for (i=0;i>1); if (!inverse_fft) phase = -phase; kf_cexp2(st->super_twiddles+i, DIV32(SHL32(phase,16),nfft)); } #else for (i=0;isuper_twiddles+i, phase ); } #endif return st; } void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata) { /* input buffer timedata is stored row-wise */ int k,ncfft; kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc; if ( st->substate->inverse) { speex_fatal("kiss fft usage error: improper alloc\n"); } ncfft = st->substate->nfft; /*perform the parallel fft of two real signals packed in real,imag*/ kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf ); /* The real part of the DC element of the frequency spectrum in st->tmpbuf * contains the sum of the even-numbered elements of the input time sequence * The imag part is the sum of the odd-numbered elements * * The sum of tdc.r and tdc.i is the sum of the input time sequence. * yielding DC of input time sequence * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1... * yielding Nyquist bin of input time sequence */ tdc.r = st->tmpbuf[0].r; tdc.i = st->tmpbuf[0].i; C_FIXDIV(tdc,2); CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i); CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i); freqdata[0].r = tdc.r + tdc.i; freqdata[ncfft].r = tdc.r - tdc.i; #ifdef USE_SIMD freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0); #else freqdata[ncfft].i = freqdata[0].i = 0; #endif for ( k=1;k <= ncfft/2 ; ++k ) { fpk = st->tmpbuf[k]; fpnk.r = st->tmpbuf[ncfft-k].r; fpnk.i = - st->tmpbuf[ncfft-k].i; C_FIXDIV(fpk,2); C_FIXDIV(fpnk,2); C_ADD( f1k, fpk , fpnk ); C_SUB( f2k, fpk , fpnk ); C_MUL( tw , f2k , st->super_twiddles[k]); freqdata[k].r = HALF_OF(f1k.r + tw.r); freqdata[k].i = HALF_OF(f1k.i + tw.i); freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r); freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i); } } void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata, kiss_fft_scalar *timedata) { /* input buffer timedata is stored row-wise */ int k, ncfft; if (st->substate->inverse == 0) { speex_fatal("kiss fft usage error: improper alloc\n"); } ncfft = st->substate->nfft; st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r; st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r; /*C_FIXDIV(st->tmpbuf[0],2);*/ for (k = 1; k <= ncfft / 2; ++k) { kiss_fft_cpx fk, fnkc, fek, fok, tmp; fk = freqdata[k]; fnkc.r = freqdata[ncfft - k].r; fnkc.i = -freqdata[ncfft - k].i; /*C_FIXDIV( fk , 2 ); C_FIXDIV( fnkc , 2 );*/ C_ADD (fek, fk, fnkc); C_SUB (tmp, fk, fnkc); C_MUL (fok, tmp, st->super_twiddles[k]); C_ADD (st->tmpbuf[k], fek, fok); C_SUB (st->tmpbuf[ncfft - k], fek, fok); #ifdef USE_SIMD st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0); #else st->tmpbuf[ncfft - k].i *= -1; #endif } kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata); } void kiss_fftr2(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_scalar *freqdata) { /* input buffer timedata is stored row-wise */ int k,ncfft; kiss_fft_cpx f2k,tdc; spx_word32_t f1kr, f1ki, twr, twi; if ( st->substate->inverse) { speex_fatal("kiss fft usage error: improper alloc\n"); } ncfft = st->substate->nfft; /*perform the parallel fft of two real signals packed in real,imag*/ kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf ); /* The real part of the DC element of the frequency spectrum in st->tmpbuf * contains the sum of the even-numbered elements of the input time sequence * The imag part is the sum of the odd-numbered elements * * The sum of tdc.r and tdc.i is the sum of the input time sequence. * yielding DC of input time sequence * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1... * yielding Nyquist bin of input time sequence */ tdc.r = st->tmpbuf[0].r; tdc.i = st->tmpbuf[0].i; C_FIXDIV(tdc,2); CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i); CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i); freqdata[0] = tdc.r + tdc.i; freqdata[2*ncfft-1] = tdc.r - tdc.i; for ( k=1;k <= ncfft/2 ; ++k ) { /*fpk = st->tmpbuf[k]; fpnk.r = st->tmpbuf[ncfft-k].r; fpnk.i = - st->tmpbuf[ncfft-k].i; C_FIXDIV(fpk,2); C_FIXDIV(fpnk,2); C_ADD( f1k, fpk , fpnk ); C_SUB( f2k, fpk , fpnk ); C_MUL( tw , f2k , st->super_twiddles[k]); freqdata[2*k-1] = HALF_OF(f1k.r + tw.r); freqdata[2*k] = HALF_OF(f1k.i + tw.i); freqdata[2*(ncfft-k)-1] = HALF_OF(f1k.r - tw.r); freqdata[2*(ncfft-k)] = HALF_OF(tw.i - f1k.i); */ /*f1k.r = PSHR32(ADD32(EXTEND32(st->tmpbuf[k].r), EXTEND32(st->tmpbuf[ncfft-k].r)),1); f1k.i = PSHR32(SUB32(EXTEND32(st->tmpbuf[k].i), EXTEND32(st->tmpbuf[ncfft-k].i)),1); f2k.r = PSHR32(SUB32(EXTEND32(st->tmpbuf[k].r), EXTEND32(st->tmpbuf[ncfft-k].r)),1); f2k.i = SHR32(ADD32(EXTEND32(st->tmpbuf[k].i), EXTEND32(st->tmpbuf[ncfft-k].i)),1); C_MUL( tw , f2k , st->super_twiddles[k]); freqdata[2*k-1] = HALF_OF(f1k.r + tw.r); freqdata[2*k] = HALF_OF(f1k.i + tw.i); freqdata[2*(ncfft-k)-1] = HALF_OF(f1k.r - tw.r); freqdata[2*(ncfft-k)] = HALF_OF(tw.i - f1k.i); */ f2k.r = SHR32(SUB32(EXTEND32(st->tmpbuf[k].r), EXTEND32(st->tmpbuf[ncfft-k].r)),1); f2k.i = PSHR32(ADD32(EXTEND32(st->tmpbuf[k].i), EXTEND32(st->tmpbuf[ncfft-k].i)),1); f1kr = SHL32(ADD32(EXTEND32(st->tmpbuf[k].r), EXTEND32(st->tmpbuf[ncfft-k].r)),13); f1ki = SHL32(SUB32(EXTEND32(st->tmpbuf[k].i), EXTEND32(st->tmpbuf[ncfft-k].i)),13); twr = SHR32(SUB32(MULT16_16(f2k.r,st->super_twiddles[k].r),MULT16_16(f2k.i,st->super_twiddles[k].i)), 1); twi = SHR32(ADD32(MULT16_16(f2k.i,st->super_twiddles[k].r),MULT16_16(f2k.r,st->super_twiddles[k].i)), 1); #ifdef FIXED_POINT freqdata[2*k-1] = PSHR32(f1kr + twr, 15); freqdata[2*k] = PSHR32(f1ki + twi, 15); freqdata[2*(ncfft-k)-1] = PSHR32(f1kr - twr, 15); freqdata[2*(ncfft-k)] = PSHR32(twi - f1ki, 15); #else freqdata[2*k-1] = .5f*(f1kr + twr); freqdata[2*k] = .5f*(f1ki + twi); freqdata[2*(ncfft-k)-1] = .5f*(f1kr - twr); freqdata[2*(ncfft-k)] = .5f*(twi - f1ki); #endif } } void kiss_fftri2(kiss_fftr_cfg st,const kiss_fft_scalar *freqdata,kiss_fft_scalar *timedata) { /* input buffer timedata is stored row-wise */ int k, ncfft; if (st->substate->inverse == 0) { speex_fatal ("kiss fft usage error: improper alloc\n"); } ncfft = st->substate->nfft; st->tmpbuf[0].r = freqdata[0] + freqdata[2*ncfft-1]; st->tmpbuf[0].i = freqdata[0] - freqdata[2*ncfft-1]; /*C_FIXDIV(st->tmpbuf[0],2);*/ for (k = 1; k <= ncfft / 2; ++k) { kiss_fft_cpx fk, fnkc, fek, fok, tmp; fk.r = freqdata[2*k-1]; fk.i = freqdata[2*k]; fnkc.r = freqdata[2*(ncfft - k)-1]; fnkc.i = -freqdata[2*(ncfft - k)]; /*C_FIXDIV( fk , 2 ); C_FIXDIV( fnkc , 2 );*/ C_ADD (fek, fk, fnkc); C_SUB (tmp, fk, fnkc); C_MUL (fok, tmp, st->super_twiddles[k]); C_ADD (st->tmpbuf[k], fek, fok); C_SUB (st->tmpbuf[ncfft - k], fek, fok); #ifdef USE_SIMD st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0); #else st->tmpbuf[ncfft - k].i *= -1; #endif } kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata); } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/kiss_fftr.h ================================================ #ifndef KISS_FTR_H #define KISS_FTR_H #include "kiss_fft.h" #ifdef __cplusplus extern "C" { #endif /* Real optimized version can save about 45% cpu time vs. complex fft of a real seq. */ typedef struct kiss_fftr_state *kiss_fftr_cfg; kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem); /* nfft must be even If you don't care to allocate space, use mem = lenmem = NULL */ void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata); /* input timedata has nfft scalar points output freqdata has nfft/2+1 complex points */ void kiss_fftr2(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_scalar *freqdata); void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata); void kiss_fftri2(kiss_fftr_cfg st,const kiss_fft_scalar *freqdata, kiss_fft_scalar *timedata); /* input freqdata has nfft/2+1 complex points output timedata has nfft scalar points */ #define kiss_fftr_free speex_free #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/lpc.c ================================================ /* Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann, Technische Universitaet Berlin Any use of this software is permitted provided that this notice is not removed and that neither the authors nor the Technische Universitaet Berlin are deemed to have made any representations as to the suitability of this software for any purpose nor are held responsible for any defects of this software. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. As a matter of courtesy, the authors request to be informed about uses this software has found, about bugs in this software, and about any improvements that may be of general interest. Berlin, 28.11.1994 Jutta Degener Carsten Bormann Code modified by Jean-Marc Valin Speex License: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "lpc.h" #ifdef BFIN_ASM #include "lpc_bfin.h" #endif /* LPC analysis * * The next two functions calculate linear prediction coefficients * and/or the related reflection coefficients from the first P_MAX+1 * values of the autocorrelation function. */ /* Invented by N. Levinson in 1947, modified by J. Durbin in 1959. */ /* returns minimum mean square error */ spx_word32_t _spx_lpc( spx_coef_t *lpc, /* out: [0...p-1] LPC coefficients */ const spx_word16_t *ac, /* in: [0...p] autocorrelation values */ int p ) { int i, j; spx_word16_t r; spx_word16_t error = ac[0]; if (ac[0] == 0) { for (i = 0; i < p; i++) lpc[i] = 0; return 0; } for (i = 0; i < p; i++) { /* Sum up this iteration's reflection coefficient */ spx_word32_t rr = NEG32(SHL32(EXTEND32(ac[i + 1]),13)); for (j = 0; j < i; j++) rr = SUB32(rr,MULT16_16(lpc[j],ac[i - j])); #ifdef FIXED_POINT r = DIV32_16(rr+PSHR32(error,1),ADD16(error,8)); #else r = rr/(error+.003*ac[0]); #endif /* Update LPC coefficients and total error */ lpc[i] = r; for (j = 0; j < i>>1; j++) { spx_word16_t tmp = lpc[j]; lpc[j] = MAC16_16_P13(lpc[j],r,lpc[i-1-j]); lpc[i-1-j] = MAC16_16_P13(lpc[i-1-j],r,tmp); } if (i & 1) lpc[j] = MAC16_16_P13(lpc[j],lpc[j],r); error = SUB16(error,MULT16_16_Q13(r,MULT16_16_Q13(error,r))); } return error; } #ifdef FIXED_POINT /* Compute the autocorrelation * ,--, * ac(i) = > x(n) * x(n-i) for all n * `--' * for lags between 0 and lag-1, and x == 0 outside 0...n-1 */ #ifndef OVERRIDE_SPEEX_AUTOCORR void _spx_autocorr( const spx_word16_t *x, /* in: [0...n-1] samples x */ spx_word16_t *ac, /* out: [0...lag-1] ac values */ int lag, int n ) { spx_word32_t d; int i, j; spx_word32_t ac0=1; int shift, ac_shift; for (j=0;j x(n) * x(n-i) for all n * `--' * for lags between 0 and lag-1, and x == 0 outside 0...n-1 */ void _spx_autocorr( const spx_word16_t *x, /* in: [0...n-1] samples x */ float *ac, /* out: [0...lag-1] ac values */ int lag, int n ) { float d; int i; while (lag--) { for (i = lag, d = 0; i < n; i++) d += x[i] * x[i-lag]; ac[lag] = d; } ac[0] += 10; } #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/lpc.h ================================================ /* Copyright (C) 2002 Jean-Marc Valin */ /** @file lpc.h @brief Functions for LPC (Linear Prediction Coefficients) analysis */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LPC_H #define LPC_H #include "arch.h" void _spx_autocorr( const spx_word16_t * x, /* in: [0...n-1] samples x */ spx_word16_t *ac, /* out: [0...lag-1] ac values */ int lag, int n); spx_word32_t /* returns minimum mean square error */ _spx_lpc( spx_coef_t * lpc, /* [0...p-1] LPC coefficients */ const spx_word16_t * ac, /* in: [0...p] autocorrelation values */ int p ); #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/lpc_bfin.h ================================================ /* Copyright (C) 2005 Analog Devices */ /** @file lpc_bfin.h @author Jean-Marc Valin @brief Functions for LPC (Linear Prediction Coefficients) analysis (Blackfin version) */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define OVERRIDE_SPEEX_AUTOCORR void _spx_autocorr( const spx_word16_t *x, /* in: [0...n-1] samples x */ spx_word16_t *ac, /* out: [0...lag-1] ac values */ int lag, int n ) { spx_word32_t d; const spx_word16_t *xs; int i, j; spx_word32_t ac0=1; spx_word32_t ac32[11], *ac32top; int shift, ac_shift; ac32top = ac32+lag-1; int lag_1, N_lag; int nshift; lag_1 = lag-1; N_lag = n-lag_1; for (j=0;j> 1;\n\t" "LOOP_BEGIN pitch%=;\n\t" "I1 = P0;\n\t" "A1 = A0 = 0;\n\t" "R1 = [I1++];\n\t" "LOOP inner_prod%= LC1 = P3 >> 1;\n\t" "LOOP_BEGIN inner_prod%=;\n\t" "A1 += R0.L*R1.H, A0 += R0.L*R1.L (IS) || R1.L = W[I1++];\n\t" "A1 += R0.H*R1.L, A0 += R0.H*R1.H (IS) || R1.H = W[I1++] || R0 = [I0++];\n\t" "LOOP_END inner_prod%=;\n\t" "A0 = ASHIFT A0 by R4.L;\n\t" "A1 = ASHIFT A1 by R4.L;\n\t" "R2 = A0, R3 = A1;\n\t" "[P1--] = R2;\n\t" "[P1--] = R3;\n\t" "P0 += 4;\n\t" "LOOP_END pitch%=;\n\t" : : "m" (xs), "m" (x), "m" (ac32top), "m" (N_lag), "m" (lag_1), "m" (nshift) : "A0", "A1", "P0", "P1", "P2", "P3", "P4", "R0", "R1", "R2", "R3", "R4", "I0", "I1", "L0", "L1", "B0", "B1", "memory" ); d=0; for (j=0;j #include "lsp.h" #include "stack_alloc.h" #include "math_approx.h" #ifndef M_PI #define M_PI 3.14159265358979323846 /* pi */ #endif #ifndef NULL #define NULL 0 #endif #ifdef FIXED_POINT #define FREQ_SCALE 16384 /*#define ANGLE2X(a) (32768*cos(((a)/8192.)))*/ #define ANGLE2X(a) (SHL16(spx_cos(a),2)) /*#define X2ANGLE(x) (acos(.00006103515625*(x))*LSP_SCALING)*/ #define X2ANGLE(x) (spx_acos(x)) #ifdef BFIN_ASM #include "lsp_bfin.h" #endif #else /*#define C1 0.99940307 #define C2 -0.49558072 #define C3 0.03679168*/ #define FREQ_SCALE 1. #define ANGLE2X(a) (spx_cos(a)) #define X2ANGLE(x) (acos(x)) #endif /*---------------------------------------------------------------------------*\ FUNCTION....: cheb_poly_eva() AUTHOR......: David Rowe DATE CREATED: 24/2/93 This function evaluates a series of Chebyshev polynomials \*---------------------------------------------------------------------------*/ #ifdef FIXED_POINT #ifndef OVERRIDE_CHEB_POLY_EVA static inline spx_word32_t cheb_poly_eva( spx_word16_t *coef, /* P or Q coefs in Q13 format */ spx_word16_t x, /* cos of freq (-1.0 to 1.0) in Q14 format */ int m, /* LPC order/2 */ char *stack ) { int i; spx_word16_t b0, b1; spx_word32_t sum; /*Prevents overflows*/ if (x>16383) x = 16383; if (x<-16383) x = -16383; /* Initialise values */ b1=16384; b0=x; /* Evaluate Chebyshev series formulation usin g iterative approach */ sum = ADD32(EXTEND32(coef[m]), EXTEND32(MULT16_16_P14(coef[m-1],x))); for(i=2;i<=m;i++) { spx_word16_t tmp=b0; b0 = SUB16(MULT16_16_Q13(x,b0), b1); b1 = tmp; sum = ADD32(sum, EXTEND32(MULT16_16_P14(coef[m-i],b0))); } return sum; } #endif #else static float cheb_poly_eva(spx_word32_t *coef, spx_word16_t x, int m, char *stack) { int k; float b0, b1, tmp; /* Initial conditions */ b0=0; /* b_(m+1) */ b1=0; /* b_(m+2) */ x*=2; /* Calculate the b_(k) */ for(k=m;k>0;k--) { tmp=b0; /* tmp holds the previous value of b0 */ b0=x*b0-b1+coef[m-k]; /* b0 holds its new value based on b0 and b1 */ b1=tmp; /* b1 holds the previous value of b0 */ } return(-b1+.5*x*b0+coef[m]); } #endif /*---------------------------------------------------------------------------*\ FUNCTION....: lpc_to_lsp() AUTHOR......: David Rowe DATE CREATED: 24/2/93 This function converts LPC coefficients to LSP coefficients. \*---------------------------------------------------------------------------*/ #ifdef FIXED_POINT #define SIGN_CHANGE(a,b) (((a)&0x70000000)^((b)&0x70000000)||(b==0)) #else #define SIGN_CHANGE(a,b) (((a)*(b))<0.0) #endif int lpc_to_lsp (spx_coef_t *a,int lpcrdr,spx_lsp_t *freq,int nb,spx_word16_t delta, char *stack) /* float *a lpc coefficients */ /* int lpcrdr order of LPC coefficients (10) */ /* float *freq LSP frequencies in the x domain */ /* int nb number of sub-intervals (4) */ /* float delta grid spacing interval (0.02) */ { spx_word16_t temp_xr,xl,xr,xm=0; spx_word32_t psuml,psumr,psumm,temp_psumr/*,temp_qsumr*/; int i,j,m,flag,k; VARDECL(spx_word32_t *Q); /* ptrs for memory allocation */ VARDECL(spx_word32_t *P); VARDECL(spx_word16_t *Q16); /* ptrs for memory allocation */ VARDECL(spx_word16_t *P16); spx_word32_t *px; /* ptrs of respective P'(z) & Q'(z) */ spx_word32_t *qx; spx_word32_t *p; spx_word32_t *q; spx_word16_t *pt; /* ptr used for cheb_poly_eval() whether P' or Q' */ int roots=0; /* DR 8/2/94: number of roots found */ flag = 1; /* program is searching for a root when, 1 else has found one */ m = lpcrdr/2; /* order of P'(z) & Q'(z) polynomials */ /* Allocate memory space for polynomials */ ALLOC(Q, (m+1), spx_word32_t); ALLOC(P, (m+1), spx_word32_t); /* determine P'(z)'s and Q'(z)'s coefficients where P'(z) = P(z)/(1 + z^(-1)) and Q'(z) = Q(z)/(1-z^(-1)) */ px = P; /* initialise ptrs */ qx = Q; p = px; q = qx; #ifdef FIXED_POINT *px++ = LPC_SCALING; *qx++ = LPC_SCALING; for(i=0;i=32768) speex_warning_int("px", *px); if (fabs(*qx)>=32768) speex_warning_int("qx", *qx);*/ *px = PSHR32(*px,2); *qx = PSHR32(*qx,2); px++; qx++; } /* The reason for this lies in the way cheb_poly_eva() is implemented for fixed-point */ P[m] = PSHR32(P[m],3); Q[m] = PSHR32(Q[m],3); #else *px++ = LPC_SCALING; *qx++ = LPC_SCALING; for(i=0;i= -FREQ_SCALE)){ spx_word16_t dd; /* Modified by JMV to provide smaller steps around x=+-1 */ #ifdef FIXED_POINT dd = MULT16_16_Q15(delta,SUB16(FREQ_SCALE, MULT16_16_Q14(MULT16_16_Q14(xl,xl),14000))); if (psuml<512 && psuml>-512) dd = PSHR16(dd,1); #else dd=delta*(1-.9*xl*xl); if (fabs(psuml)<.2) dd *= .5; #endif xr = SUB16(xl, dd); /* interval spacing */ psumr = cheb_poly_eva(pt,xr,m,stack);/* poly(xl-delta_x) */ temp_psumr = psumr; temp_xr = xr; /* if no sign change increment xr and re-evaluate poly(xr). Repeat til sign change. if a sign change has occurred the interval is bisected and then checked again for a sign change which determines in which interval the zero lies in. If there is no sign change between poly(xm) and poly(xl) set interval between xm and xr else set interval between xl and xr and repeat till root is located within the specified limits */ if(SIGN_CHANGE(psumr,psuml)) { roots++; psumm=psuml; for(k=0;k<=nb;k++){ #ifdef FIXED_POINT xm = ADD16(PSHR16(xl,1),PSHR16(xr,1)); /* bisect the interval */ #else xm = .5*(xl+xr); /* bisect the interval */ #endif psumm=cheb_poly_eva(pt,xm,m,stack); /*if(psumm*psuml>0.)*/ if(!SIGN_CHANGE(psumm,psuml)) { psuml=psumm; xl=xm; } else { psumr=psumm; xr=xm; } } /* once zero is found, reset initial interval to xr */ freq[j] = X2ANGLE(xm); xl = xm; flag = 0; /* reset flag for next search */ } else{ psuml=temp_psumr; xl=temp_xr; } } } return(roots); } /*---------------------------------------------------------------------------*\ FUNCTION....: lsp_to_lpc() AUTHOR......: David Rowe DATE CREATED: 24/2/93 Converts LSP coefficients to LPC coefficients. \*---------------------------------------------------------------------------*/ #ifdef FIXED_POINT void lsp_to_lpc(spx_lsp_t *freq,spx_coef_t *ak,int lpcrdr, char *stack) /* float *freq array of LSP frequencies in the x domain */ /* float *ak array of LPC coefficients */ /* int lpcrdr order of LPC coefficients */ { int i,j; spx_word32_t xout1,xout2,xin; spx_word32_t mult, a; VARDECL(spx_word16_t *freqn); VARDECL(spx_word32_t **xp); VARDECL(spx_word32_t *xpmem); VARDECL(spx_word32_t **xq); VARDECL(spx_word32_t *xqmem); int m = lpcrdr>>1; /* Reconstruct P(z) and Q(z) by cascading second order polynomials in form 1 - 2cos(w)z(-1) + z(-2), where w is the LSP frequency. In the time domain this is: y(n) = x(n) - 2cos(w)x(n-1) + x(n-2) This is what the ALLOCS below are trying to do: int xp[m+1][lpcrdr+1+2]; // P matrix in QIMP int xq[m+1][lpcrdr+1+2]; // Q matrix in QIMP These matrices store the output of each stage on each row. The final (m-th) row has the output of the final (m-th) cascaded 2nd order filter. The first row is the impulse input to the system (not written as it is known). The version below takes advantage of the fact that a lot of the outputs are zero or known, for example if we put an inpulse into the first section the "clock" it 10 times only the first 3 outputs samples are non-zero (it's an FIR filter). */ ALLOC(xp, (m+1), spx_word32_t*); ALLOC(xpmem, (m+1)*(lpcrdr+1+2), spx_word32_t); ALLOC(xq, (m+1), spx_word32_t*); ALLOC(xqmem, (m+1)*(lpcrdr+1+2), spx_word32_t); for(i=0; i<=m; i++) { xp[i] = xpmem + i*(lpcrdr+1+2); xq[i] = xqmem + i*(lpcrdr+1+2); } /* work out 2cos terms in Q14 */ ALLOC(freqn, lpcrdr, spx_word16_t); for (i=0;i 32767) a = 32767; ak[j-1] = (short)a; } } #else void lsp_to_lpc(spx_lsp_t *freq,spx_coef_t *ak,int lpcrdr, char *stack) /* float *freq array of LSP frequencies in the x domain */ /* float *ak array of LPC coefficients */ /* int lpcrdr order of LPC coefficients */ { int i,j; float xout1,xout2,xin1,xin2; VARDECL(float *Wp); float *pw,*n1,*n2,*n3,*n4=NULL; VARDECL(float *x_freq); int m = lpcrdr>>1; ALLOC(Wp, 4*m+2, float); pw = Wp; /* initialise contents of array */ for(i=0;i<=4*m+1;i++){ /* set contents of buffer to 0 */ *pw++ = 0.0; } /* Set pointers up */ pw = Wp; xin1 = 1.0; xin2 = 1.0; ALLOC(x_freq, lpcrdr, float); for (i=0;i0) ak[j-1] = (xout1 + xout2)*0.5f; *(n4+1) = xin1; *(n4+2) = xin2; xin1 = 0.0; xin2 = 0.0; } } #endif #ifdef FIXED_POINT /*Makes sure the LSPs are stable*/ void lsp_enforce_margin(spx_lsp_t *lsp, int len, spx_word16_t margin) { int i; spx_word16_t m = margin; spx_word16_t m2 = 25736-margin; if (lsp[0]m2) lsp[len-1]=m2; for (i=1;ilsp[i+1]-m) lsp[i]= SHR16(lsp[i],1) + SHR16(lsp[i+1]-m,1); } } void lsp_interpolate(spx_lsp_t *old_lsp, spx_lsp_t *new_lsp, spx_lsp_t *interp_lsp, int len, int subframe, int nb_subframes) { int i; spx_word16_t tmp = DIV32_16(SHL32(EXTEND32(1 + subframe),14),nb_subframes); spx_word16_t tmp2 = 16384-tmp; for (i=0;iLSP_SCALING*(M_PI-margin)) lsp[len-1]=LSP_SCALING*(M_PI-margin); for (i=1;ilsp[i+1]-LSP_SCALING*margin) lsp[i]= .5f* (lsp[i] + lsp[i+1]-LSP_SCALING*margin); } } void lsp_interpolate(spx_lsp_t *old_lsp, spx_lsp_t *new_lsp, spx_lsp_t *interp_lsp, int len, int subframe, int nb_subframes) { int i; float tmp = (1.0f + subframe)/nb_subframes; for (i=0;i>>= 14;\n\t" "R3 = R3 + R5;\n\t" "R0 = R2;\n\t" /* R0: b0 */ "R1 = 16384;\n\t" /* R1: b1 */ "LOOP cpe%= LC0 = %3;\n\t" "LOOP_BEGIN cpe%=;\n\t" "P1 = R0;\n\t" "R0 = R2.L * R0.L (IS) || R5 = W[P0--] (X);\n\t" "R0 >>>= 13;\n\t" "R0 = R0 - R1;\n\t" "R1 = P1;\n\t" "R5 = R5.L * R0.L (IS);\n\t" "R5 = R5 + R4;\n\t" "R5 >>>= 14;\n\t" "R3 = R3 + R5;\n\t" "LOOP_END cpe%=;\n\t" "%0 = R3;\n\t" : "=&d" (sum) : "a" (x), "a" (&coef[m]), "a" (m-1) : "R0", "R1", "R3", "R2", "R4", "R5", "P0", "P1" ); return sum; } #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/lsp_tables_nb.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: lsp_tables_nb.c Codebooks for LSPs in narrowband CELP mode Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ const signed char cdbk_nb[640]={ 30,19,38,34,40,32,46,43,58,43, 5,-18,-25,-40,-33,-55,-52,20,34,28, -20,-63,-97,-92,61,53,47,49,53,75, -14,-53,-77,-79,0,-3,-5,19,22,26, -9,-53,-55,66,90,72,85,68,74,52, -4,-41,-58,-31,-18,-31,27,32,30,18, 24,3,8,5,-12,-3,26,28,74,63, -2,-39,-67,-77,-106,-74,59,59,73,65, 44,40,71,72,82,83,98,88,89,60, -6,-31,-47,-48,-13,-39,-9,7,2,79, -1,-39,-60,-17,87,81,65,50,45,19, -21,-67,-91,-87,-41,-50,7,18,39,74, 10,-31,-28,39,24,13,23,5,56,45, 29,10,-5,-13,-11,-35,-18,-8,-10,-8, -25,-71,-77,-21,2,16,50,63,87,87, 5,-32,-40,-51,-68,0,12,6,54,34, 5,-12,32,52,68,64,69,59,65,45, 14,-16,-31,-40,-65,-67,41,49,47,37, -11,-52,-75,-84,-4,57,48,42,42,33, -11,-51,-68,-6,13,0,8,-8,26,32, -23,-53,0,36,56,76,97,105,111,97, -1,-28,-39,-40,-43,-54,-44,-40,-18,35, 16,-20,-19,-28,-42,29,47,38,74,45, 3,-29,-48,-62,-80,-104,-33,56,59,59, 10,17,46,72,84,101,117,123,123,106, -7,-33,-49,-51,-70,-67,-27,-31,70,67, -16,-62,-85,-20,82,71,86,80,85,74, -19,-58,-75,-45,-29,-33,-18,-25,45,57, -12,-42,-5,12,28,36,52,64,81,82, 13,-9,-27,-28,22,3,2,22,26,6, -6,-44,-51,2,15,10,48,43,49,34, -19,-62,-84,-89,-102,-24,8,17,61,68, 39,24,23,19,16,-5,12,15,27,15, -8,-44,-49,-60,-18,-32,-28,52,54,62, -8,-48,-77,-70,66,101,83,63,61,37, -12,-50,-75,-64,33,17,13,25,15,77, 1,-42,-29,72,64,46,49,31,61,44, -8,-47,-54,-46,-30,19,20,-1,-16,0, 16,-12,-18,-9,-26,-27,-10,-22,53,45, -10,-47,-75,-82,-105,-109,8,25,49,77, 50,65,114,117,124,118,115,96,90,61, -9,-45,-63,-60,-75,-57,8,11,20,29, 0,-35,-49,-43,40,47,35,40,55,38, -24,-76,-103,-112,-27,3,23,34,52,75, 8,-29,-43,12,63,38,35,29,24,8, 25,11,1,-15,-18,-43,-7,37,40,21, -20,-56,-19,-19,-4,-2,11,29,51,63, -2,-44,-62,-75,-89,30,57,51,74,51, 50,46,68,64,65,52,63,55,65,43, 18,-9,-26,-35,-55,-69,3,6,8,17, -15,-61,-86,-97,1,86,93,74,78,67, -1,-38,-66,-48,48,39,29,25,17,-1, 13,13,29,39,50,51,69,82,97,98, -2,-36,-46,-27,-16,-30,-13,-4,-7,-4, 25,-5,-11,-6,-25,-21,33,12,31,29, -8,-38,-52,-63,-68,-89,-33,-1,10,74, -2,-15,59,91,105,105,101,87,84,62, -7,-33,-50,-35,-54,-47,25,17,82,81, -13,-56,-83,21,58,31,42,25,72,65, -24,-66,-91,-56,9,-2,21,10,69,75, 2,-24,11,22,25,28,38,34,48,33, 7,-29,-26,17,15,-1,14,0,-2,0, -6,-41,-67,6,-2,-9,19,2,85,74, -22,-67,-84,-71,-50,3,11,-9,2,62}; const signed char cdbk_nb_low1[320]={ -34,-52,-15,45,2, 23,21,52,24,-33, -9,-1,9,-44,-41, -13,-17,44,22,-17, -6,-4,-1,22,38, 26,16,2,50,27, -35,-34,-9,-41,6, 0,-16,-34,51,8, -14,-31,-49,15,-33, 45,49,33,-11,-37, -62,-54,45,11,-5, -72,11,-1,-12,-11, 24,27,-11,-43,46, 43,33,-12,-9,-1, 1,-4,-23,-57,-71, 11,8,16,17,-8, -20,-31,-41,53,48, -16,3,65,-24,-8, -23,-32,-37,-32,-49, -10,-17,6,38,5, -9,-17,-46,8,52, 3,6,45,40,39, -7,-6,-34,-74,31, 8,1,-16,43,68, -11,-19,-31,4,6, 0,-6,-17,-16,-38, -16,-30,2,9,-39, -16,-1,43,-10,48, 3,3,-16,-31,-3, 62,68,43,13,3, -10,8,20,-56,12, 12,-2,-18,22,-15, -40,-36,1,7,41, 0,1,46,-6,-62, -4,-12,-2,-11,-83, -13,-2,91,33,-10, 0,4,-11,-16,79, 32,37,14,9,51, -21,-28,-56,-34,0, 21,9,-26,11,28, -42,-54,-23,-2,-15, 31,30,8,-39,-66, -39,-36,31,-28,-40, -46,35,40,22,24, 33,48,23,-34,14, 40,32,17,27,-3, 25,26,-13,-61,-17, 11,4,31,60,-6, -26,-41,-64,13,16, -26,54,31,-11,-23, -9,-11,-34,-71,-21, -34,-35,55,50,29, -22,-27,-50,-38,57, 33,42,57,48,26, 11,0,-49,-31,26, -4,-14,5,78,37, 17,0,-49,-12,-23, 26,14,2,2,-43, -17,-12,10,-8,-4, 8,18,12,-6,20, -12,-6,-13,-25,34, 15,40,49,7,8, 13,20,20,-19,-22, -2,-8,2,51,-51}; const signed char cdbk_nb_low2[320]={ -6,53,-21,-24,4, 26,17,-4,-37,25, 17,-36,-13,31,3, -6,27,15,-10,31, 28,26,-10,-10,-40, 16,-7,15,13,41, -9,0,-4,50,-6, -7,14,38,22,0, -48,2,1,-13,-19, 32,-3,-60,11,-17, -1,-24,-34,-1,35, -5,-27,28,44,13, 25,15,42,-11,15, 51,35,-36,20,8, -4,-12,-29,19,-47, 49,-15,-4,16,-29, -39,14,-30,4,25, -9,-5,-51,-14,-3, -40,-32,38,5,-9, -8,-4,-1,-22,71, -3,14,26,-18,-22, 24,-41,-25,-24,6, 23,19,-10,39,-26, -27,65,45,2,-7, -26,-8,22,-12,16, 15,16,-35,-5,33, -21,-8,0,23,33, 34,6,21,36,6, -7,-22,8,-37,-14, 31,38,11,-4,-3, -39,-32,-8,32,-23, -6,-12,16,20,-28, -4,23,13,-52,-1, 22,6,-33,-40,-6, 4,-62,13,5,-26, 35,39,11,2,57, -11,9,-20,-28,-33, 52,-5,-6,-2,22, -14,-16,-48,35,1, -58,20,13,33,-1, -74,56,-18,-22,-31, 12,6,-14,4,-2, -9,-47,10,-3,29, -17,-5,61,14,47, -12,2,72,-39,-17, 92,64,-53,-51,-15, -30,-38,-41,-29,-28, 27,9,36,9,-35, -42,81,-21,20,25, -16,-5,-17,-35,21, 15,-28,48,2,-2, 9,-19,29,-40,30, -18,-18,18,-16,-57, 15,-20,-12,-15,-37, -15,33,-39,21,-22, -13,35,11,13,-38, -63,29,23,-27,32, 18,3,-26,42,33, -64,-66,-17,16,56, 2,36,3,31,21, -41,-39,8,-57,14, 37,-2,19,-36,-19, -23,-29,-16,1,-3, -8,-10,31,64,-65}; const signed char cdbk_nb_high1[320]={ -26,-8,29,21,4, 19,-39,33,-7,-36, 56,54,48,40,29, -4,-24,-42,-66,-43, -60,19,-2,37,41, -10,-37,-60,-64,18, -22,77,73,40,25, 4,19,-19,-66,-2, 11,5,21,14,26, -25,-86,-4,18,1, 26,-37,10,37,-1, 24,-12,-59,-11,20, -6,34,-16,-16,42, 19,-28,-51,53,32, 4,10,62,21,-12, -34,27,4,-48,-48, -50,-49,31,-7,-21, -42,-25,-4,-43,-22, 59,2,27,12,-9, -6,-16,-8,-32,-58, -16,-29,-5,41,23, -30,-33,-46,-13,-10, -38,52,52,1,-17, -9,10,26,-25,-6, 33,-20,53,55,25, -32,-5,-42,23,21, 66,5,-28,20,9, 75,29,-7,-42,-39, 15,3,-23,21,6, 11,1,-29,14,63, 10,54,26,-24,-51, -49,7,-23,-51,15, -66,1,60,25,10, 0,-30,-4,-15,17, 19,59,40,4,-5, 33,6,-22,-58,-70, -5,23,-6,60,44, -29,-16,-47,-29,52, -19,50,28,16,35, 31,36,0,-21,6, 21,27,22,42,7, -66,-40,-8,7,19, 46,0,-4,60,36, 45,-7,-29,-6,-32, -39,2,6,-9,33, 20,-51,-34,18,-6, 19,6,11,5,-19, -29,-2,42,-11,-45, -21,-55,57,37,2, -14,-67,-16,-27,-38, 69,48,19,2,-17, 20,-20,-16,-34,-17, -25,-61,10,73,45, 16,-40,-64,-17,-29, -22,56,17,-39,8, -11,8,-25,-18,-13, -19,8,54,57,36, -17,-26,-4,6,-21, 40,42,-4,20,31, 53,10,-34,-53,31, -17,35,0,15,-6, -20,-63,-73,22,25, 29,17,8,-29,-39, -69,18,15,-15,-5}; const signed char cdbk_nb_high2[320]={ 11,47,16,-9,-46, -32,26,-64,34,-5, 38,-7,47,20,2, -73,-99,-3,-45,20, 70,-52,15,-6,-7, -82,31,21,47,51, 39,-3,9,0,-41, -7,-15,-54,2,0, 27,-31,9,-45,-22, -38,-24,-24,8,-33, 23,5,50,-36,-17, -18,-51,-2,13,19, 43,12,-15,-12,61, 38,38,7,13,0, 6,-1,3,62,9, 27,22,-33,38,-35, -9,30,-43,-9,-32, -1,4,-4,1,-5, -11,-8,38,31,11, -10,-42,-21,-37,1, 43,15,-13,-35,-19, -18,15,23,-26,59, 1,-21,53,8,-41, -50,-14,-28,4,21, 25,-28,-40,5,-40, -41,4,51,-33,-8, -8,1,17,-60,12, 25,-41,17,34,43, 19,45,7,-37,24, -15,56,-2,35,-10, 48,4,-47,-2,5, -5,-54,5,-3,-33, -10,30,-2,-44,-24, -38,9,-9,42,4, 6,-56,44,-16,9, -40,-26,18,-20,10, 28,-41,-21,-4,13, -18,32,-30,-3,37, 15,22,28,50,-40, 3,-29,-64,7,51, -19,-11,17,-27,-40, -64,24,-12,-7,-27, 3,37,48,-1,2, -9,-38,-34,46,1, 27,-6,19,-13,26, 10,34,20,25,40, 50,-6,-7,30,9, -24,0,-23,71,-61, 22,58,-34,-4,2, -49,-33,25,30,-8, -6,-16,77,2,38, -8,-35,-6,-30,56, 78,31,33,-20,13, -39,20,22,4,21, -8,4,-6,10,-83, -41,9,-25,-43,15, -7,-12,-34,-39,-37, -33,19,30,16,-33, 42,-25,25,-68,44, -15,-11,-4,23,50, 14,4,-39,-43,20, -30,60,9,-20,7, 16,19,-33,37,29, 16,-35,7,38,-27}; ================================================ FILE: deps/pjsip/third_party/speex/libspeex/ltp.c ================================================ /* Copyright (C) 2002-2006 Jean-Marc Valin File: ltp.c Long-Term Prediction functions Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "ltp.h" #include "stack_alloc.h" #include "filters.h" #include #include "math_approx.h" #include "os_support.h" #ifndef NULL #define NULL 0 #endif #ifdef _USE_SSE #include "ltp_sse.h" #elif defined (ARM4_ASM) || defined(ARM5E_ASM) #include "ltp_arm4.h" #elif defined (BFIN_ASM) #include "ltp_bfin.h" #endif #ifndef OVERRIDE_INNER_PROD spx_word32_t inner_prod(const spx_word16_t *x, const spx_word16_t *y, int len) { spx_word32_t sum=0; len >>= 2; while(len--) { spx_word32_t part=0; part = MAC16_16(part,*x++,*y++); part = MAC16_16(part,*x++,*y++); part = MAC16_16(part,*x++,*y++); part = MAC16_16(part,*x++,*y++); /* HINT: If you had a 40-bit accumulator, you could shift only at the end */ sum = ADD32(sum,SHR32(part,6)); } return sum; } #endif #ifndef OVERRIDE_PITCH_XCORR #if 0 /* HINT: Enable this for machines with enough registers (i.e. not x86) */ void pitch_xcorr(const spx_word16_t *_x, const spx_word16_t *_y, spx_word32_t *corr, int len, int nb_pitch, char *stack) { int i,j; for (i=0;i16383) { scaledown=1; break; } } /* If the weighted input is close to saturation, then we scale it down */ if (scaledown) { for (i=-end;iMULT16_16(best_score[N-1],ADD16(1,ener16[i-start]))) { /* We can safely put it last and then check */ best_score[N-1]=tmp; best_ener[N-1]=ener16[i-start]+1; pitch[N-1]=i; /* Check if it comes in front of others */ for (j=0;jMULT16_16(best_score[j],ADD16(1,ener16[i-start]))) { for (k=N-1;k>j;k--) { best_score[k]=best_score[k-1]; best_ener[k]=best_ener[k-1]; pitch[k]=pitch[k-1]; } best_score[j]=tmp; best_ener[j]=ener16[i-start]+1; pitch[j]=i; break; } } } } /* Compute open-loop gain if necessary */ if (gain) { for (j=0;jbest_sum && gain_sum<=max_gain) { best_sum=sum; best_cdbk=i; } } return best_cdbk; } #endif /** Finds the best quantized 3-tap pitch predictor by analysis by synthesis */ static spx_word32_t pitch_gain_search_3tap( const spx_word16_t target[], /* Target vector */ const spx_coef_t ak[], /* LPCs for this subframe */ const spx_coef_t awk1[], /* Weighted LPCs #1 for this subframe */ const spx_coef_t awk2[], /* Weighted LPCs #2 for this subframe */ spx_sig_t exc[], /* Excitation */ const signed char *gain_cdbk, int gain_cdbk_size, int pitch, /* Pitch value */ int p, /* Number of LPC coeffs */ int nsf, /* Number of samples in subframe */ SpeexBits *bits, char *stack, const spx_word16_t *exc2, const spx_word16_t *r, spx_word16_t *new_target, int *cdbk_index, int plc_tuning, spx_word32_t cumul_gain, int scaledown ) { int i,j; VARDECL(spx_word16_t *tmp1); VARDECL(spx_word16_t *e); spx_word16_t *x[3]; spx_word32_t corr[3]; spx_word32_t A[3][3]; spx_word16_t gain[3]; spx_word32_t err; spx_word16_t max_gain=128; int best_cdbk=0; ALLOC(tmp1, 3*nsf, spx_word16_t); ALLOC(e, nsf, spx_word16_t); if (cumul_gain > 262144) max_gain = 31; x[0]=tmp1; x[1]=tmp1+nsf; x[2]=tmp1+2*nsf; for (j=0;j=0;i--) { spx_word16_t e0=exc2[-pitch-1+i]; #ifdef FIXED_POINT /* Scale excitation down if needed (avoiding overflow) */ if (scaledown) e0 = SHR16(e0,1); #endif x[i][0]=MULT16_16_Q14(r[0], e0); for (j=0;j30) plc_tuning=30; #ifdef FIXED_POINT C[0] = SHL32(C[0],1); C[1] = SHL32(C[1],1); C[2] = SHL32(C[2],1); C[3] = SHL32(C[3],1); C[4] = SHL32(C[4],1); C[5] = SHL32(C[5],1); C[6] = MAC16_32_Q15(C[6],MULT16_16_16(plc_tuning,655),C[6]); C[7] = MAC16_32_Q15(C[7],MULT16_16_16(plc_tuning,655),C[7]); C[8] = MAC16_32_Q15(C[8],MULT16_16_16(plc_tuning,655),C[8]); normalize16(C, C16, 32767, 9); #else C[6]*=.5*(1+.02*plc_tuning); C[7]*=.5*(1+.02*plc_tuning); C[8]*=.5*(1+.02*plc_tuning); #endif best_cdbk = pitch_gain_search_3tap_vq(gain_cdbk, gain_cdbk_size, C16, max_gain); #ifdef FIXED_POINT gain[0] = ADD16(32,(spx_word16_t)gain_cdbk[best_cdbk*4]); gain[1] = ADD16(32,(spx_word16_t)gain_cdbk[best_cdbk*4+1]); gain[2] = ADD16(32,(spx_word16_t)gain_cdbk[best_cdbk*4+2]); /*printf ("%d %d %d %d\n",gain[0],gain[1],gain[2], best_cdbk);*/ #else gain[0] = 0.015625*gain_cdbk[best_cdbk*4] + .5; gain[1] = 0.015625*gain_cdbk[best_cdbk*4+1]+ .5; gain[2] = 0.015625*gain_cdbk[best_cdbk*4+2]+ .5; #endif *cdbk_index=best_cdbk; } SPEEX_MEMSET(exc, 0, nsf); for (i=0;i<3;i++) { int j; int tmp1, tmp3; int pp=pitch+1-i; tmp1=nsf; if (tmp1>pp) tmp1=pp; for (j=0;jpp+pitch) tmp3=pp+pitch; for (j=tmp1;jgain_bits; gain_cdbk = params->gain_cdbk + 4*gain_cdbk_size*cdbk_offset; N=complexity; if (N>10) N=10; if (N<1) N=1; ALLOC(nbest, N, int); params = (const ltp_params*) par; if (endpitch_bits); speex_bits_pack(bits, 0, params->gain_bits); SPEEX_MEMSET(exc, 0, nsf); return start; } #ifdef FIXED_POINT /* Check if we need to scale everything down in the pitch search to avoid overflows */ for (i=0;i16383) { scaledown=1; break; } } for (i=-end;i16383) { scaledown=1; break; } } #endif if (N>end-start+1) N=end-start+1; if (end != start) open_loop_nbest_pitch(sw, start, end, nsf, nbest, NULL, N, stack); else nbest[0] = start; ALLOC(best_exc, nsf, spx_sig_t); ALLOC(new_target, nsf, spx_word16_t); ALLOC(best_target, nsf, spx_word16_t); for (i=0;ipitch_bits); speex_bits_pack(bits, best_gain_index, params->gain_bits); #ifdef FIXED_POINT *cumul_gain = MULT16_32_Q13(SHL16(params->gain_cdbk[4*best_gain_index+3],8), MAX32(1024,*cumul_gain)); #else *cumul_gain = 0.03125*MAX32(1024,*cumul_gain)*params->gain_cdbk[4*best_gain_index+3]; #endif /*printf ("%f\n", cumul_gain);*/ /*printf ("encode pitch: %d %d\n", best_pitch, best_gain_index);*/ SPEEX_COPY(exc, best_exc, nsf); SPEEX_COPY(target, best_target, nsf); #ifdef FIXED_POINT /* Scale target back up if needed */ if (scaledown) { for (i=0;igain_bits; gain_cdbk = params->gain_cdbk + 4*gain_cdbk_size*cdbk_offset; pitch = speex_bits_unpack_unsigned(bits, params->pitch_bits); pitch += start; gain_index = speex_bits_unpack_unsigned(bits, params->gain_bits); /*printf ("decode pitch: %d %d\n", pitch, gain_index);*/ #ifdef FIXED_POINT gain[0] = ADD16(32,(spx_word16_t)gain_cdbk[gain_index*4]); gain[1] = ADD16(32,(spx_word16_t)gain_cdbk[gain_index*4+1]); gain[2] = ADD16(32,(spx_word16_t)gain_cdbk[gain_index*4+2]); #else gain[0] = 0.015625*gain_cdbk[gain_index*4]+.5; gain[1] = 0.015625*gain_cdbk[gain_index*4+1]+.5; gain[2] = 0.015625*gain_cdbk[gain_index*4+2]+.5; #endif if (count_lost && pitch > subframe_offset) { spx_word16_t gain_sum; if (1) { #ifdef FIXED_POINT spx_word16_t tmp = count_lost < 4 ? last_pitch_gain : SHR16(last_pitch_gain,1); if (tmp>62) tmp=62; #else spx_word16_t tmp = count_lost < 4 ? last_pitch_gain : 0.5 * last_pitch_gain; if (tmp>.95) tmp=.95; #endif gain_sum = gain_3tap_to_1tap(gain); if (gain_sum > tmp) { spx_word16_t fact = DIV32_16(SHL32(EXTEND32(tmp),14),gain_sum); for (i=0;i<3;i++) gain[i]=MULT16_16_Q14(fact,gain[i]); } } } *pitch_val = pitch; gain_val[0]=gain[0]; gain_val[1]=gain[1]; gain_val[2]=gain[2]; gain[0] = SHL16(gain[0],7); gain[1] = SHL16(gain[1],7); gain[2] = SHL16(gain[2],7); SPEEX_MEMSET(exc_out, 0, nsf); for (i=0;i<3;i++) { int j; int tmp1, tmp3; int pp=pitch+1-i; tmp1=nsf; if (tmp1>pp) tmp1=pp; for (j=0;jpp+pitch) tmp3=pp+pitch; for (j=tmp1;j63) pitch_coef=63; #else if (pitch_coef>.99) pitch_coef=.99; #endif for (i=0;i63) pitch_coef=63; #else if (pitch_coef>.99) pitch_coef=.99; #endif for (i=0;i #include "arch.h" /** LTP parameters. */ typedef struct { const signed char *gain_cdbk; int gain_bits; int pitch_bits; } ltp_params; #ifdef FIXED_POINT #define gain_3tap_to_1tap(g) (ABS(g[1]) + (g[0]>0 ? g[0] : -SHR16(g[0],1)) + (g[2]>0 ? g[2] : -SHR16(g[2],1))) #else #define gain_3tap_to_1tap(g) (ABS(g[1]) + (g[0]>0 ? g[0] : -.5*g[0]) + (g[2]>0 ? g[2] : -.5*g[2])) #endif spx_word32_t inner_prod(const spx_word16_t *x, const spx_word16_t *y, int len); void pitch_xcorr(const spx_word16_t *_x, const spx_word16_t *_y, spx_word32_t *corr, int len, int nb_pitch, char *stack); void open_loop_nbest_pitch(spx_word16_t *sw, int start, int end, int len, int *pitch, spx_word16_t *gain, int N, char *stack); /** Finds the best quantized 3-tap pitch predictor by analysis by synthesis */ int pitch_search_3tap( spx_word16_t target[], /* Target vector */ spx_word16_t *sw, spx_coef_t ak[], /* LPCs for this subframe */ spx_coef_t awk1[], /* Weighted LPCs #1 for this subframe */ spx_coef_t awk2[], /* Weighted LPCs #2 for this subframe */ spx_sig_t exc[], /* Overlapping codebook */ const void *par, int start, /* Smallest pitch value allowed */ int end, /* Largest pitch value allowed */ spx_word16_t pitch_coef, /* Voicing (pitch) coefficient */ int p, /* Number of LPC coeffs */ int nsf, /* Number of samples in subframe */ SpeexBits *bits, char *stack, spx_word16_t *exc2, spx_word16_t *r, int complexity, int cdbk_offset, int plc_tuning, spx_word32_t *cumul_gain ); /*Unquantize adaptive codebook and update pitch contribution*/ void pitch_unquant_3tap( spx_word16_t exc[], /* Input excitation */ spx_word32_t exc_out[], /* Output excitation */ int start, /* Smallest pitch value allowed */ int end, /* Largest pitch value allowed */ spx_word16_t pitch_coef, /* Voicing (pitch) coefficient */ const void *par, int nsf, /* Number of samples in subframe */ int *pitch_val, spx_word16_t *gain_val, SpeexBits *bits, char *stack, int lost, int subframe_offset, spx_word16_t last_pitch_gain, int cdbk_offset ); /** Forced pitch delay and gain */ int forced_pitch_quant( spx_word16_t target[], /* Target vector */ spx_word16_t *sw, spx_coef_t ak[], /* LPCs for this subframe */ spx_coef_t awk1[], /* Weighted LPCs #1 for this subframe */ spx_coef_t awk2[], /* Weighted LPCs #2 for this subframe */ spx_sig_t exc[], /* Excitation */ const void *par, int start, /* Smallest pitch value allowed */ int end, /* Largest pitch value allowed */ spx_word16_t pitch_coef, /* Voicing (pitch) coefficient */ int p, /* Number of LPC coeffs */ int nsf, /* Number of samples in subframe */ SpeexBits *bits, char *stack, spx_word16_t *exc2, spx_word16_t *r, int complexity, int cdbk_offset, int plc_tuning, spx_word32_t *cumul_gain ); /** Unquantize forced pitch delay and gain */ void forced_pitch_unquant( spx_word16_t exc[], /* Input excitation */ spx_word32_t exc_out[], /* Output excitation */ int start, /* Smallest pitch value allowed */ int end, /* Largest pitch value allowed */ spx_word16_t pitch_coef, /* Voicing (pitch) coefficient */ const void *par, int nsf, /* Number of samples in subframe */ int *pitch_val, spx_word16_t *gain_val, SpeexBits *bits, char *stack, int lost, int subframe_offset, spx_word16_t last_pitch_gain, int cdbk_offset ); ================================================ FILE: deps/pjsip/third_party/speex/libspeex/ltp_arm4.h ================================================ /* Copyright (C) 2004 Jean-Marc Valin */ /** @file ltp_arm4.h @brief Long-Term Prediction functions (ARM4 version) */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define OVERRIDE_INNER_PROD spx_word32_t inner_prod(const spx_word16_t *x, const spx_word16_t *y, int len) { spx_word32_t sum1=0,sum2=0; spx_word16_t *deadx, *deady; int deadlen, dead1, dead2, dead3, dead4, dead5, dead6; __asm__ __volatile__ ( "\tldrsh %5, [%0], #2 \n" "\tldrsh %6, [%1], #2 \n" ".inner_prod_loop%=:\n" "\tsub %7, %7, %7\n" "\tsub %10, %10, %10\n" "\tldrsh %8, [%0], #2 \n" "\tldrsh %9, [%1], #2 \n" "\tmla %7, %5, %6, %7\n" "\tldrsh %5, [%0], #2 \n" "\tldrsh %6, [%1], #2 \n" "\tmla %10, %8, %9, %10\n" "\tldrsh %8, [%0], #2 \n" "\tldrsh %9, [%1], #2 \n" "\tmla %7, %5, %6, %7\n" "\tldrsh %5, [%0], #2 \n" "\tldrsh %6, [%1], #2 \n" "\tmla %10, %8, %9, %10\n" "\tldrsh %8, [%0], #2 \n" "\tldrsh %9, [%1], #2 \n" "\tmla %7, %5, %6, %7\n" "\tldrsh %5, [%0], #2 \n" "\tldrsh %6, [%1], #2 \n" "\tmla %10, %8, %9, %10\n" "\tldrsh %8, [%0], #2 \n" "\tldrsh %9, [%1], #2 \n" "\tmla %7, %5, %6, %7\n" "\tldrsh %5, [%0], #2 \n" "\tldrsh %6, [%1], #2 \n" "\tmla %10, %8, %9, %10\n" "\tsubs %4, %4, #1\n" "\tadd %2, %2, %7, asr #5\n" "\tadd %3, %3, %10, asr #5\n" "\tbne .inner_prod_loop%=\n" : "=r" (deadx), "=r" (deady), "+r" (sum1), "+r" (sum2), "=r" (deadlen), "=r" (dead1), "=r" (dead2), "=r" (dead3), "=r" (dead4), "=r" (dead5), "=r" (dead6) : "0" (x), "1" (y), "4" (len>>3) : "cc" ); return (sum1+sum2)>>1; } #define OVERRIDE_PITCH_XCORR void pitch_xcorr(const spx_word16_t *_x, const spx_word16_t *_y, spx_word32_t *corr, int len, int nb_pitch, char *stack) { int i,j; for (i=0;i>> 6;\n\t" "R0 = A0;\n\t" "%0 = R0;\n\t" : "=m" (sum) : "m" (x), "m" (y), "d" (len-1) : "P0", "P1", "P2", "R0", "R1", "A0", "I0", "I1", "L0", "L1", "R3" ); return sum; } #define OVERRIDE_PITCH_XCORR void pitch_xcorr(const spx_word16_t *_x, const spx_word16_t *_y, spx_word32_t *corr, int len, int nb_pitch, char *stack) { corr += nb_pitch - 1; __asm__ __volatile__ ( "P2 = %0;\n\t" "I0 = P2;\n\t" /* x in I0 */ "B0 = P2;\n\t" /* x in B0 */ "R0 = %3;\n\t" /* len in R0 */ "P3 = %3;\n\t" "P3 += -2;\n\t" /* len in R0 */ "P4 = %4;\n\t" /* nb_pitch in R0 */ "R1 = R0 << 1;\n\t" /* number of bytes in x */ "L0 = R1;\n\t" "P0 = %1;\n\t" "P1 = %2;\n\t" "B1 = P1;\n\t" "L1 = 0;\n\t" /*Disable looping on I1*/ "r0 = [I0++];\n\t" "LOOP pitch%= LC0 = P4 >> 1;\n\t" "LOOP_BEGIN pitch%=;\n\t" "I1 = P0;\n\t" "A1 = A0 = 0;\n\t" "R1 = [I1++];\n\t" "LOOP inner_prod%= LC1 = P3 >> 1;\n\t" "LOOP_BEGIN inner_prod%=;\n\t" "A1 += R0.L*R1.H, A0 += R0.L*R1.L (IS) || R1.L = W[I1++];\n\t" "A1 += R0.H*R1.L, A0 += R0.H*R1.H (IS) || R1.H = W[I1++] || R0 = [I0++];\n\t" "LOOP_END inner_prod%=;\n\t" "A1 += R0.L*R1.H, A0 += R0.L*R1.L (IS) || R1.L = W[I1++];\n\t" "A1 += R0.H*R1.L, A0 += R0.H*R1.H (IS) || R0 = [I0++];\n\t" "A0 = A0 >>> 6;\n\t" "A1 = A1 >>> 6;\n\t" "R2 = A0, R3 = A1;\n\t" "[P1--] = r2;\n\t" "[P1--] = r3;\n\t" "P0 += 4;\n\t" "LOOP_END pitch%=;\n\t" "L0 = 0;\n\t" : : "m" (_x), "m" (_y), "m" (corr), "m" (len), "m" (nb_pitch) : "A0", "A1", "P0", "P1", "P2", "P3", "P4", "R0", "R1", "R2", "R3", "I0", "I1", "L0", "L1", "B0", "B1", "memory" ); } #define OVERRIDE_COMPUTE_PITCH_ERROR static inline spx_word32_t compute_pitch_error(spx_word16_t *C, spx_word16_t *g, spx_word16_t pitch_control) { spx_word32_t sum; __asm__ __volatile__ ( "A0 = 0;\n\t" "R0 = W[%1++];\n\t" "R1.L = %2.L*%5.L (IS);\n\t" "A0 += R1.L*R0.L (IS) || R0 = W[%1++];\n\t" "R1.L = %3.L*%5.L (IS);\n\t" "A0 += R1.L*R0.L (IS) || R0 = W[%1++];\n\t" "R1.L = %4.L*%5.L (IS);\n\t" "A0 += R1.L*R0.L (IS) || R0 = W[%1++];\n\t" "R1.L = %2.L*%3.L (IS);\n\t" "A0 -= R1.L*R0.L (IS) || R0 = W[%1++];\n\t" "R1.L = %4.L*%3.L (IS);\n\t" "A0 -= R1.L*R0.L (IS) || R0 = W[%1++];\n\t" "R1.L = %4.L*%2.L (IS);\n\t" "A0 -= R1.L*R0.L (IS) || R0 = W[%1++];\n\t" "R1.L = %2.L*%2.L (IS);\n\t" "A0 -= R1.L*R0.L (IS) || R0 = W[%1++];\n\t" "R1.L = %3.L*%3.L (IS);\n\t" "A0 -= R1.L*R0.L (IS) || R0 = W[%1++];\n\t" "R1.L = %4.L*%4.L (IS);\n\t" "A0 -= R1.L*R0.L (IS);\n\t" "%0 = A0;\n\t" : "=&D" (sum), "=a" (C) : "d" (g[0]), "d" (g[1]), "d" (g[2]), "d" (pitch_control), "1" (C) : "R0", "R1", "R2", "A0" ); return sum; } #define OVERRIDE_OPEN_LOOP_NBEST_PITCH #ifdef OVERRIDE_OPEN_LOOP_NBEST_PITCH void open_loop_nbest_pitch(spx_word16_t *sw, int start, int end, int len, int *pitch, spx_word16_t *gain, int N, char *stack) { int i,j,k; VARDECL(spx_word32_t *best_score); VARDECL(spx_word32_t *best_ener); spx_word32_t e0; VARDECL(spx_word32_t *corr); VARDECL(spx_word32_t *energy); ALLOC(best_score, N, spx_word32_t); ALLOC(best_ener, N, spx_word32_t); ALLOC(corr, end-start+1, spx_word32_t); ALLOC(energy, end-start+2, spx_word32_t); for (i=0;i>>= 6;\n\t" " R1 = R1 + R2;\n\t" " R0 >>>= 6;\n\t" " R1 = R1 - R0;\n\t" " R2 = MAX(R1,R3);\n\t" "eu2: [P0++] = R2;\n\t" : : "d" (energy), "d" (&sw[-start-1]), "d" (&sw[-start+len-1]), "a" (end-start) : "P0", "I1", "I2", "R0", "R1", "R2", "R3" #if (__GNUC__ == 4) , "LC1" #endif ); pitch_xcorr(sw, sw-end, corr, len, end-start+1, stack); /* FIXME: Fixed-point and floating-point code should be merged */ { VARDECL(spx_word16_t *corr16); VARDECL(spx_word16_t *ener16); ALLOC(corr16, end-start+1, spx_word16_t); ALLOC(ener16, end-start+1, spx_word16_t); /* Normalize to 180 so we can square it and it still fits in 16 bits */ normalize16(corr, corr16, 180, end-start+1); normalize16(energy, ener16, 180, end-start+1); if (N == 1) { /* optimised asm to handle N==1 case */ __asm__ __volatile__ ( " I0 = %1;\n\t" /* I0: corr16[] */ " L0 = 0;\n\t" " I1 = %2;\n\t" /* I1: energy */ " L1 = 0;\n\t" " R2 = -1;\n\t" /* R2: best score */ " R3 = 0;\n\t" /* R3: best energy */ " P0 = %4;\n\t" /* P0: best pitch */ " P1 = %4;\n\t" /* P1: counter */ " LSETUP (sl1, sl2) LC1 = %3;\n\t" "sl1: R0.L = W [I0++] || R1.L = W [I1++];\n\t" " R0 = R0.L * R0.L (IS);\n\t" " R1 += 1;\n\t" " R4 = R0.L * R3.L;\n\t" " R5 = R2.L * R1.L;\n\t" " cc = R5 < R4;\n\t" " if cc R2 = R0;\n\t" " if cc R3 = R1;\n\t" " if cc P0 = P1;\n\t" "sl2: P1 += 1;\n\t" " %0 = P0;\n\t" : "=&d" (pitch[0]) : "a" (corr16), "a" (ener16), "a" (end+1-start), "d" (start) : "P0", "P1", "I0", "I1", "R0", "R1", "R2", "R3", "R4", "R5" #if (__GNUC__ == 4) , "LC1" #endif ); } else { for (i=start;i<=end;i++) { spx_word16_t tmp = MULT16_16_16(corr16[i-start],corr16[i-start]); /* Instead of dividing the tmp by the energy, we multiply on the other side */ if (MULT16_16(tmp,best_ener[N-1])>MULT16_16(best_score[N-1],ADD16(1,ener16[i-start]))) { /* We can safely put it last and then check */ best_score[N-1]=tmp; best_ener[N-1]=ener16[i-start]+1; pitch[N-1]=i; /* Check if it comes in front of others */ for (j=0;jMULT16_16(best_score[j],ADD16(1,ener16[i-start]))) { for (k=N-1;k>j;k--) { best_score[k]=best_score[k-1]; best_ener[k]=best_ener[k-1]; pitch[k]=pitch[k-1]; } best_score[j]=tmp; best_ener[j]=ener16[i-start]+1; pitch[j]=i; break; } } } } } } /* Compute open-loop gain */ if (gain) { for (j=0;jbest_sum && gain_sum<=max_gain) ------ (1) if (sum>best_sum && !(gain_sum>max_gain)) ------ (2) if (max_gain<=gain_sum) { ------ (3) sum = -VERY_LARGE32; } if (best_sum<=sum) The blackin cc instructions are all of the form: cc = x < y (or cc = x <= y) */ " R1 = B0\n\t" " R2 = %5\n\t" " R3 = %6\n\t" " cc = R2 <= R1;\n\t" " if cc R0 = R3;\n\t" " cc = %0 <= R0;\n\t" " if cc %0 = R0;\n\t" " if cc %1 = P1;\n\t" "pgs2: P1 += 1;\n\t" : "=&d" (best_sum), "=&d" (best_cdbk) : "a" (gain_cdbk), "a" (C16), "a" (gain_cdbk_size), "a" (max_gain), "b" (-VERY_LARGE32) : "R0", "R1", "R2", "R3", "R4", "P0", "P1", "I1", "L1", "A0", "B0" #if (__GNUC__ == 4) , "LC1" #endif ); return best_cdbk; } #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/ltp_sse.h ================================================ /* Copyright (C) 2002 Jean-Marc Valin */ /** @file ltp_sse.h @brief Long-Term Prediction functions (SSE version) */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #define OVERRIDE_INNER_PROD float inner_prod(const float *a, const float *b, int len) { int i; float ret; __m128 sum = _mm_setzero_ps(); for (i=0;i<(len>>2);i+=2) { sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+0), _mm_loadu_ps(b+0))); sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+4), _mm_loadu_ps(b+4))); a += 8; b += 8; } sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum)); sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55)); _mm_store_ss(&ret, sum); return ret; } #define OVERRIDE_PITCH_XCORR void pitch_xcorr(const float *_x, const float *_y, float *corr, int len, int nb_pitch, char *stack) { int i, offset; VARDECL(__m128 *x); VARDECL(__m128 *y); int N, L; N = len>>2; L = nb_pitch>>2; ALLOC(x, N, __m128); ALLOC(y, N+L, __m128); for (i=0;i=(spx_int32_t)65536) { x >>= 16; r += 16; } if (x>=256) { x >>= 8; r += 8; } if (x>=16) { x >>= 4; r += 4; } if (x>=4) { x >>= 2; r += 2; } if (x>=2) { r += 1; } return r; } static inline spx_int16_t spx_ilog4(spx_uint32_t x) { int r=0; if (x>=(spx_int32_t)65536) { x >>= 16; r += 8; } if (x>=256) { x >>= 8; r += 4; } if (x>=16) { x >>= 4; r += 2; } if (x>=4) { r += 1; } return r; } #ifdef FIXED_POINT /** Generate a pseudo-random number */ static inline spx_word16_t speex_rand(spx_word16_t std, spx_int32_t *seed) { spx_word32_t res; *seed = 1664525 * *seed + 1013904223; res = MULT16_16(EXTRACT16(SHR32(*seed,16)),std); return EXTRACT16(PSHR32(SUB32(res, SHR32(res, 3)),14)); } /* sqrt(x) ~= 0.22178 + 1.29227*x - 0.77070*x^2 + 0.25723*x^3 (for .25 < x < 1) */ /*#define C0 3634 #define C1 21173 #define C2 -12627 #define C3 4215*/ /* sqrt(x) ~= 0.22178 + 1.29227*x - 0.77070*x^2 + 0.25659*x^3 (for .25 < x < 1) */ #define C0 3634 #define C1 21173 #define C2 -12627 #define C3 4204 static inline spx_word16_t spx_sqrt(spx_word32_t x) { int k; spx_word32_t rt; k = spx_ilog4(x)-6; x = VSHR32(x, (k<<1)); rt = ADD16(C0, MULT16_16_Q14(x, ADD16(C1, MULT16_16_Q14(x, ADD16(C2, MULT16_16_Q14(x, (C3))))))); rt = VSHR32(rt,7-k); return rt; } /* log(x) ~= -2.18151 + 4.20592*x - 2.88938*x^2 + 0.86535*x^3 (for .5 < x < 1) */ #define A1 16469 #define A2 2242 #define A3 1486 static inline spx_word16_t spx_acos(spx_word16_t x) { int s=0; spx_word16_t ret; spx_word16_t sq; if (x<0) { s=1; x = NEG16(x); } x = SUB16(16384,x); x = x >> 1; sq = MULT16_16_Q13(x, ADD16(A1, MULT16_16_Q13(x, ADD16(A2, MULT16_16_Q13(x, (A3)))))); ret = spx_sqrt(SHL32(EXTEND32(sq),13)); /*ret = spx_sqrt(67108864*(-1.6129e-04 + 2.0104e+00*f + 2.7373e-01*f*f + 1.8136e-01*f*f*f));*/ if (s) ret = SUB16(25736,ret); return ret; } #define K1 8192 #define K2 -4096 #define K3 340 #define K4 -10 static inline spx_word16_t spx_cos(spx_word16_t x) { spx_word16_t x2; if (x<12868) { x2 = MULT16_16_P13(x,x); return ADD32(K1, MULT16_16_P13(x2, ADD32(K2, MULT16_16_P13(x2, ADD32(K3, MULT16_16_P13(K4, x2)))))); } else { x = SUB16(25736,x); x2 = MULT16_16_P13(x,x); return SUB32(-K1, MULT16_16_P13(x2, ADD32(K2, MULT16_16_P13(x2, ADD32(K3, MULT16_16_P13(K4, x2)))))); } } #define L1 32767 #define L2 -7651 #define L3 8277 #define L4 -626 static inline spx_word16_t _spx_cos_pi_2(spx_word16_t x) { spx_word16_t x2; x2 = MULT16_16_P15(x,x); return ADD16(1,MIN16(32766,ADD32(SUB16(L1,x2), MULT16_16_P15(x2, ADD32(L2, MULT16_16_P15(x2, ADD32(L3, MULT16_16_P15(L4, x2)))))))); } static inline spx_word16_t spx_cos_norm(spx_word32_t x) { x = x&0x0001ffff; if (x>SHL32(EXTEND32(1), 16)) x = SUB32(SHL32(EXTEND32(1), 17),x); if (x&0x00007fff) { if (x14) return 0x7fffffff; else if (integer < -15) return 0; frac = SHL16(x-SHL16(integer,11),3); frac = ADD16(D0, MULT16_16_Q14(frac, ADD16(D1, MULT16_16_Q14(frac, ADD16(D2 , MULT16_16_Q14(D3,frac)))))); return VSHR32(EXTEND32(frac), -integer-2); } /* Input in Q11 format, output in Q16 */ static inline spx_word32_t spx_exp(spx_word16_t x) { if (x>21290) return 0x7fffffff; else if (x<-21290) return 0; else return spx_exp2(MULT16_16_P14(23637,x)); } #define M1 32767 #define M2 -21 #define M3 -11943 #define M4 4936 static inline spx_word16_t spx_atan01(spx_word16_t x) { return MULT16_16_P15(x, ADD32(M1, MULT16_16_P15(x, ADD32(M2, MULT16_16_P15(x, ADD32(M3, MULT16_16_P15(M4, x))))))); } #undef M1 #undef M2 #undef M3 #undef M4 /* Input in Q15, output in Q14 */ static inline spx_word16_t spx_atan(spx_word32_t x) { if (x <= 32767) { return SHR16(spx_atan01(x),1); } else { int e = spx_ilog2(x); if (e>=29) return 25736; x = DIV32_16(SHL32(EXTEND32(32767),29-e), EXTRACT16(SHR32(x, e-14))); return SUB16(25736, SHR16(spx_atan01(x),1)); } } #else #ifndef M_PI #define M_PI 3.14159265358979323846 /* pi */ #endif #define C1 0.9999932946f #define C2 -0.4999124376f #define C3 0.0414877472f #define C4 -0.0012712095f #define SPX_PI_2 1.5707963268 static inline spx_word16_t spx_cos(spx_word16_t x) { if (x>16) #else static const spx_float_t MIN_LEAK = .005f; /* Constants for the two-path filter */ static const spx_float_t VAR1_SMOOTH = .36f; static const spx_float_t VAR2_SMOOTH = .7225f; static const spx_float_t VAR1_UPDATE = .5f; static const spx_float_t VAR2_UPDATE = .25f; static const spx_float_t VAR_BACKTRACK = 4.f; #define TOP16(x) (x) #endif #define PLAYBACK_DELAY 2 void speex_echo_get_residual(SpeexEchoState *st, spx_word32_t *Yout, int len); /** Speex echo cancellation state. */ struct SpeexEchoState_ { int frame_size; /**< Number of samples processed each time */ int window_size; int M; int cancel_count; int adapted; int saturated; int screwed_up; spx_int32_t sampling_rate; spx_word16_t spec_average; spx_word16_t beta0; spx_word16_t beta_max; spx_word32_t sum_adapt; spx_word16_t leak_estimate; spx_word16_t *e; /* scratch */ spx_word16_t *x; /* Far-end input buffer (2N) */ spx_word16_t *X; /* Far-end buffer (M+1 frames) in frequency domain */ spx_word16_t *input; /* scratch */ spx_word16_t *y; /* scratch */ spx_word16_t *last_y; spx_word16_t *Y; /* scratch */ spx_word16_t *E; spx_word32_t *PHI; /* scratch */ spx_word32_t *W; /* (Background) filter weights */ #ifdef TWO_PATH spx_word16_t *foreground; /* Foreground filter weights */ spx_word32_t Davg1; /* 1st recursive average of the residual power difference */ spx_word32_t Davg2; /* 2nd recursive average of the residual power difference */ spx_float_t Dvar1; /* Estimated variance of 1st estimator */ spx_float_t Dvar2; /* Estimated variance of 2nd estimator */ #endif spx_word32_t *power; /* Power of the far-end signal */ spx_float_t *power_1;/* Inverse power of far-end */ spx_word16_t *wtmp; /* scratch */ #ifdef FIXED_POINT spx_word16_t *wtmp2; /* scratch */ #endif spx_word32_t *Rf; /* scratch */ spx_word32_t *Yf; /* scratch */ spx_word32_t *Xf; /* scratch */ spx_word32_t *Eh; spx_word32_t *Yh; spx_float_t Pey; spx_float_t Pyy; spx_word16_t *window; spx_word16_t *prop; void *fft_table; spx_word16_t memX, memD, memE; spx_word16_t preemph; spx_word16_t notch_radius; spx_mem_t notch_mem[2]; /* NOTE: If you only use speex_echo_cancel() and want to save some memory, remove this */ spx_int16_t *play_buf; int play_buf_pos; int play_buf_started; }; static inline void filter_dc_notch16(const spx_int16_t *in, spx_word16_t radius, spx_word16_t *out, int len, spx_mem_t *mem) { int i; spx_word16_t den2; #ifdef FIXED_POINT den2 = MULT16_16_Q15(radius,radius) + MULT16_16_Q15(QCONST16(.7,15),MULT16_16_Q15(32767-radius,32767-radius)); #else den2 = radius*radius + .7*(1-radius)*(1-radius); #endif /*printf ("%d %d %d %d %d %d\n", num[0], num[1], num[2], den[0], den[1], den[2]);*/ for (i=0;i>= 1; while(len--) { spx_word32_t part=0; part = MAC16_16(part,*x++,*y++); part = MAC16_16(part,*x++,*y++); /* HINT: If you had a 40-bit accumulator, you could shift only at the end */ sum = ADD32(sum,SHR32(part,6)); } return sum; } /** Compute power spectrum of a half-complex (packed) vector */ static inline void power_spectrum(const spx_word16_t *X, spx_word32_t *ps, int N) { int i, j; ps[0]=MULT16_16(X[0],X[0]); for (i=1,j=1;i max_sum) max_sum = prop[i]; } for (i=0;i static FILE *rFile=NULL, *pFile=NULL, *oFile=NULL; static void dump_audio(const spx_int16_t *rec, const spx_int16_t *play, const spx_int16_t *out, int len) { if (!(rFile && pFile && oFile)) { speex_fatal("Dump files not open"); } fwrite(rec, sizeof(spx_int16_t), len, rFile); fwrite(play, sizeof(spx_int16_t), len, pFile); fwrite(out, sizeof(spx_int16_t), len, oFile); } #endif /** Creates a new echo canceller state */ SpeexEchoState *speex_echo_state_init(int frame_size, int filter_length) { int i,N,M; SpeexEchoState *st = (SpeexEchoState *)speex_alloc(sizeof(SpeexEchoState)); #ifdef DUMP_ECHO_CANCEL_DATA if (rFile || pFile || oFile) speex_fatal("Opening dump files twice"); rFile = fopen("aec_rec.sw", "wb"); pFile = fopen("aec_play.sw", "wb"); oFile = fopen("aec_out.sw", "wb"); #endif st->frame_size = frame_size; st->window_size = 2*frame_size; N = st->window_size; M = st->M = (filter_length+st->frame_size-1)/frame_size; st->cancel_count=0; st->sum_adapt = 0; st->saturated = 0; st->screwed_up = 0; /* This is the default sampling rate */ st->sampling_rate = 8000; st->spec_average = DIV32_16(SHL32(EXTEND32(st->frame_size), 15), st->sampling_rate); #ifdef FIXED_POINT st->beta0 = DIV32_16(SHL32(EXTEND32(st->frame_size), 16), st->sampling_rate); st->beta_max = DIV32_16(SHL32(EXTEND32(st->frame_size), 14), st->sampling_rate); #else st->beta0 = (2.0f*st->frame_size)/st->sampling_rate; st->beta_max = (.5f*st->frame_size)/st->sampling_rate; #endif st->leak_estimate = 0; st->fft_table = spx_fft_init(N); st->e = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t)); st->x = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t)); st->input = (spx_word16_t*)speex_alloc(st->frame_size*sizeof(spx_word16_t)); st->y = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t)); st->last_y = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t)); st->Yf = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t)); st->Rf = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t)); st->Xf = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t)); st->Yh = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t)); st->Eh = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t)); st->X = (spx_word16_t*)speex_alloc((M+1)*N*sizeof(spx_word16_t)); st->Y = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t)); st->E = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t)); st->W = (spx_word32_t*)speex_alloc(M*N*sizeof(spx_word32_t)); #ifdef TWO_PATH st->foreground = (spx_word16_t*)speex_alloc(M*N*sizeof(spx_word16_t)); #endif st->PHI = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t)); st->power = (spx_word32_t*)speex_alloc((frame_size+1)*sizeof(spx_word32_t)); st->power_1 = (spx_float_t*)speex_alloc((frame_size+1)*sizeof(spx_float_t)); st->window = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t)); st->prop = (spx_word16_t*)speex_alloc(M*sizeof(spx_word16_t)); st->wtmp = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t)); #ifdef FIXED_POINT st->wtmp2 = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t)); for (i=0;i>1;i++) { st->window[i] = (16383-SHL16(spx_cos(DIV32_16(MULT16_16(25736,i<<1),N)),1)); st->window[N-i-1] = st->window[i]; } #else for (i=0;iwindow[i] = .5-.5*cos(2*M_PI*i/N); #endif for (i=0;i<=st->frame_size;i++) st->power_1[i] = FLOAT_ONE; for (i=0;iW[i] = 0; { spx_word32_t sum = 0; /* Ratio of ~10 between adaptation rate of first and last block */ spx_word16_t decay = SHR32(spx_exp(NEG16(DIV32_16(QCONST16(2.4,11),M))),1); st->prop[0] = QCONST16(.7, 15); sum = EXTEND32(st->prop[0]); for (i=1;iprop[i] = MULT16_16_Q15(st->prop[i-1], decay); sum = ADD32(sum, EXTEND32(st->prop[i])); } for (i=M-1;i>=0;i--) { st->prop[i] = DIV32(MULT16_16(QCONST16(.8,15), st->prop[i]),sum); } } st->memX=st->memD=st->memE=0; st->preemph = QCONST16(.9,15); if (st->sampling_rate<12000) st->notch_radius = QCONST16(.9, 15); else if (st->sampling_rate<24000) st->notch_radius = QCONST16(.982, 15); else st->notch_radius = QCONST16(.992, 15); st->notch_mem[0] = st->notch_mem[1] = 0; st->adapted = 0; st->Pey = st->Pyy = FLOAT_ONE; #ifdef TWO_PATH st->Davg1 = st->Davg2 = 0; st->Dvar1 = st->Dvar2 = FLOAT_ZERO; #endif st->play_buf = (spx_int16_t*)speex_alloc((PLAYBACK_DELAY+1)*st->frame_size*sizeof(spx_int16_t)); st->play_buf_pos = PLAYBACK_DELAY*st->frame_size; st->play_buf_started = 0; return st; } /** Resets echo canceller state */ void speex_echo_state_reset(SpeexEchoState *st) { int i, M, N; st->cancel_count=0; st->screwed_up = 0; N = st->window_size; M = st->M; for (i=0;iW[i] = 0; #ifdef TWO_PATH for (i=0;iforeground[i] = 0; #endif for (i=0;iX[i] = 0; for (i=0;i<=st->frame_size;i++) { st->power[i] = 0; st->power_1[i] = FLOAT_ONE; st->Eh[i] = 0; st->Yh[i] = 0; } for (i=0;iframe_size;i++) { st->last_y[i] = 0; } for (i=0;iE[i] = 0; st->x[i] = 0; } st->notch_mem[0] = st->notch_mem[1] = 0; st->memX=st->memD=st->memE=0; st->saturated = 0; st->adapted = 0; st->sum_adapt = 0; st->Pey = st->Pyy = FLOAT_ONE; #ifdef TWO_PATH st->Davg1 = st->Davg2 = 0; st->Dvar1 = st->Dvar2 = FLOAT_ZERO; #endif for (i=0;i<3*st->frame_size;i++) st->play_buf[i] = 0; st->play_buf_pos = PLAYBACK_DELAY*st->frame_size; st->play_buf_started = 0; } /** Destroys an echo canceller state */ void speex_echo_state_destroy(SpeexEchoState *st) { spx_fft_destroy(st->fft_table); speex_free(st->e); speex_free(st->x); speex_free(st->input); speex_free(st->y); speex_free(st->last_y); speex_free(st->Yf); speex_free(st->Rf); speex_free(st->Xf); speex_free(st->Yh); speex_free(st->Eh); speex_free(st->X); speex_free(st->Y); speex_free(st->E); speex_free(st->W); #ifdef TWO_PATH speex_free(st->foreground); #endif speex_free(st->PHI); speex_free(st->power); speex_free(st->power_1); speex_free(st->window); speex_free(st->prop); speex_free(st->wtmp); #ifdef FIXED_POINT speex_free(st->wtmp2); #endif speex_free(st->play_buf); speex_free(st); #ifdef DUMP_ECHO_CANCEL_DATA fclose(rFile); fclose(pFile); fclose(oFile); rFile = pFile = oFile = NULL; #endif } void speex_echo_capture(SpeexEchoState *st, const spx_int16_t *rec, spx_int16_t *out) { int i; /*speex_warning_int("capture with fill level ", st->play_buf_pos/st->frame_size);*/ st->play_buf_started = 1; if (st->play_buf_pos>=st->frame_size) { speex_echo_cancellation(st, rec, st->play_buf, out); st->play_buf_pos -= st->frame_size; for (i=0;iplay_buf_pos;i++) st->play_buf[i] = st->play_buf[i+st->frame_size]; } else { speex_warning("No playback frame available (your application is buggy and/or got xruns)"); if (st->play_buf_pos!=0) { speex_warning("internal playback buffer corruption?"); st->play_buf_pos = 0; } for (i=0;iframe_size;i++) out[i] = rec[i]; } } void speex_echo_playback(SpeexEchoState *st, const spx_int16_t *play) { /*speex_warning_int("playback with fill level ", st->play_buf_pos/st->frame_size);*/ if (!st->play_buf_started) { speex_warning("discarded first playback frame"); return; } if (st->play_buf_pos<=PLAYBACK_DELAY*st->frame_size) { int i; for (i=0;iframe_size;i++) st->play_buf[st->play_buf_pos+i] = play[i]; st->play_buf_pos += st->frame_size; if (st->play_buf_pos <= (PLAYBACK_DELAY-1)*st->frame_size) { speex_warning("Auto-filling the buffer (your application is buggy and/or got xruns)"); for (i=0;iframe_size;i++) st->play_buf[st->play_buf_pos+i] = play[i]; st->play_buf_pos += st->frame_size; } } else { speex_warning("Had to discard a playback frame (your application is buggy and/or got xruns)"); } } /** Performs echo cancellation on a frame (deprecated, last arg now ignored) */ void speex_echo_cancel(SpeexEchoState *st, const spx_int16_t *in, const spx_int16_t *far_end, spx_int16_t *out, spx_int32_t *Yout) { speex_echo_cancellation(st, in, far_end, out); } /** Performs echo cancellation on a frame */ void speex_echo_cancellation(SpeexEchoState *st, const spx_int16_t *in, const spx_int16_t *far_end, spx_int16_t *out) { int i,j; int N,M; spx_word32_t Syy,See,Sxx,Sdd, Sff; #ifdef TWO_PATH spx_word32_t Dbf; int update_foreground; #endif spx_word32_t Sey; spx_word16_t ss, ss_1; spx_float_t Pey = FLOAT_ONE, Pyy=FLOAT_ONE; spx_float_t alpha, alpha_1; spx_word16_t RER; spx_word32_t tmp32; N = st->window_size; M = st->M; st->cancel_count++; #ifdef FIXED_POINT ss=DIV32_16(11469,M); ss_1 = SUB16(32767,ss); #else ss=.35/M; ss_1 = 1-ss; #endif /* Apply a notch filter to make sure DC doesn't end up causing problems */ filter_dc_notch16(in, st->notch_radius, st->input, st->frame_size, st->notch_mem); /* Copy input data to buffer and apply pre-emphasis */ for (i=0;iframe_size;i++) { spx_word32_t tmp32; tmp32 = SUB32(EXTEND32(far_end[i]), EXTEND32(MULT16_16_P15(st->preemph, st->memX))); #ifdef FIXED_POINT /* If saturation occurs here, we need to freeze adaptation for M+1 frames (not just one) */ if (tmp32 > 32767) { tmp32 = 32767; st->saturated = M+1; } if (tmp32 < -32767) { tmp32 = -32767; st->saturated = M+1; } #endif st->x[i+st->frame_size] = EXTRACT16(tmp32); st->memX = far_end[i]; tmp32 = SUB32(EXTEND32(st->input[i]), EXTEND32(MULT16_16_P15(st->preemph, st->memD))); #ifdef FIXED_POINT if (tmp32 > 32767) { tmp32 = 32767; if (st->saturated == 0) st->saturated = 1; } if (tmp32 < -32767) { tmp32 = -32767; if (st->saturated == 0) st->saturated = 1; } #endif st->memD = st->input[i]; st->input[i] = tmp32; } /* Shift memory: this could be optimized eventually*/ for (j=M-1;j>=0;j--) { for (i=0;iX[(j+1)*N+i] = st->X[j*N+i]; } /* Convert x (far end) to frequency domain */ spx_fft(st->fft_table, st->x, &st->X[0]); for (i=0;ilast_y[i] = st->x[i]; Sxx = mdf_inner_prod(st->x+st->frame_size, st->x+st->frame_size, st->frame_size); for (i=0;iframe_size;i++) st->x[i] = st->x[i+st->frame_size]; /* From here on, the top part of x is used as scratch space */ #ifdef TWO_PATH /* Compute foreground filter */ spectral_mul_accum16(st->X, st->foreground, st->Y, N, M); spx_ifft(st->fft_table, st->Y, st->e); for (i=0;iframe_size;i++) st->e[i] = SUB16(st->input[i], st->e[i+st->frame_size]); Sff = mdf_inner_prod(st->e, st->e, st->frame_size); #endif /* Adjust proportional adaption rate */ mdf_adjust_prop (st->W, N, M, st->prop); /* Compute weight gradient */ if (st->saturated == 0) { for (j=M-1;j>=0;j--) { weighted_spectral_mul_conj(st->power_1, FLOAT_SHL(PSEUDOFLOAT(st->prop[j]),-15), &st->X[(j+1)*N], st->E, st->PHI, N); for (i=0;iW[j*N+i] = ADD32(st->W[j*N+i], st->PHI[i]); } } else { st->saturated--; } /* Update weight to prevent circular convolution (MDF / AUMDF) */ for (j=0;jcancel_count%(M-1) == j-1) { #ifdef FIXED_POINT for (i=0;iwtmp2[i] = EXTRACT16(PSHR32(st->W[j*N+i],NORMALIZE_SCALEDOWN+16)); spx_ifft(st->fft_table, st->wtmp2, st->wtmp); for (i=0;iframe_size;i++) { st->wtmp[i]=0; } for (i=st->frame_size;iwtmp[i]=SHL16(st->wtmp[i],NORMALIZE_SCALEUP); } spx_fft(st->fft_table, st->wtmp, st->wtmp2); /* The "-1" in the shift is a sort of kludge that trades less efficient update speed for decrease noise */ for (i=0;iW[j*N+i] -= SHL32(EXTEND32(st->wtmp2[i]),16+NORMALIZE_SCALEDOWN-NORMALIZE_SCALEUP-1); #else spx_ifft(st->fft_table, &st->W[j*N], st->wtmp); for (i=st->frame_size;iwtmp[i]=0; } spx_fft(st->fft_table, st->wtmp, &st->W[j*N]); #endif } } /* Compute filter response Y */ spectral_mul_accum(st->X, st->W, st->Y, N, M); spx_ifft(st->fft_table, st->Y, st->y); #ifdef TWO_PATH /* Difference in response, this is used to estimate the variance of our residual power estimate */ for (i=0;iframe_size;i++) st->e[i] = SUB16(st->e[i+st->frame_size], st->y[i+st->frame_size]); Dbf = 10+mdf_inner_prod(st->e, st->e, st->frame_size); #endif for (i=0;iframe_size;i++) st->e[i] = SUB16(st->input[i], st->y[i+st->frame_size]); See = mdf_inner_prod(st->e, st->e, st->frame_size); #ifndef TWO_PATH Sff = See; #endif #ifdef TWO_PATH /* Logic for updating the foreground filter */ /* For two time windows, compute the mean of the energy difference, as well as the variance */ st->Davg1 = ADD32(MULT16_32_Q15(QCONST16(.6f,15),st->Davg1), MULT16_32_Q15(QCONST16(.4f,15),SUB32(Sff,See))); st->Davg2 = ADD32(MULT16_32_Q15(QCONST16(.85f,15),st->Davg2), MULT16_32_Q15(QCONST16(.15f,15),SUB32(Sff,See))); st->Dvar1 = FLOAT_ADD(FLOAT_MULT(VAR1_SMOOTH, st->Dvar1), FLOAT_MUL32U(MULT16_32_Q15(QCONST16(.4f,15),Sff), MULT16_32_Q15(QCONST16(.4f,15),Dbf))); st->Dvar2 = FLOAT_ADD(FLOAT_MULT(VAR2_SMOOTH, st->Dvar2), FLOAT_MUL32U(MULT16_32_Q15(QCONST16(.15f,15),Sff), MULT16_32_Q15(QCONST16(.15f,15),Dbf))); /* Equivalent float code: st->Davg1 = .6*st->Davg1 + .4*(Sff-See); st->Davg2 = .85*st->Davg2 + .15*(Sff-See); st->Dvar1 = .36*st->Dvar1 + .16*Sff*Dbf; st->Dvar2 = .7225*st->Dvar2 + .0225*Sff*Dbf; */ update_foreground = 0; /* Check if we have a statistically significant reduction in the residual echo */ /* Note that this is *not* Gaussian, so we need to be careful about the longer tail */ if (FLOAT_GT(FLOAT_MUL32U(SUB32(Sff,See),ABS32(SUB32(Sff,See))), FLOAT_MUL32U(Sff,Dbf))) update_foreground = 1; else if (FLOAT_GT(FLOAT_MUL32U(st->Davg1, ABS32(st->Davg1)), FLOAT_MULT(VAR1_UPDATE,(st->Dvar1)))) update_foreground = 1; else if (FLOAT_GT(FLOAT_MUL32U(st->Davg2, ABS32(st->Davg2)), FLOAT_MULT(VAR2_UPDATE,(st->Dvar2)))) update_foreground = 1; /* Do we update? */ if (update_foreground) { st->Davg1 = st->Davg2 = 0; st->Dvar1 = st->Dvar2 = FLOAT_ZERO; /* Copy background filter to foreground filter */ for (i=0;iforeground[i] = EXTRACT16(PSHR32(st->W[i],16)); /* Apply a smooth transition so as to not introduce blocking artifacts */ for (i=0;iframe_size;i++) st->e[i+st->frame_size] = MULT16_16_Q15(st->window[i+st->frame_size],st->e[i+st->frame_size]) + MULT16_16_Q15(st->window[i],st->y[i+st->frame_size]); } else { int reset_background=0; /* Otherwise, check if the background filter is significantly worse */ if (FLOAT_GT(FLOAT_MUL32U(NEG32(SUB32(Sff,See)),ABS32(SUB32(Sff,See))), FLOAT_MULT(VAR_BACKTRACK,FLOAT_MUL32U(Sff,Dbf)))) reset_background = 1; if (FLOAT_GT(FLOAT_MUL32U(NEG32(st->Davg1), ABS32(st->Davg1)), FLOAT_MULT(VAR_BACKTRACK,st->Dvar1))) reset_background = 1; if (FLOAT_GT(FLOAT_MUL32U(NEG32(st->Davg2), ABS32(st->Davg2)), FLOAT_MULT(VAR_BACKTRACK,st->Dvar2))) reset_background = 1; if (reset_background) { /* Copy foreground filter to background filter */ for (i=0;iW[i] = SHL32(EXTEND32(st->foreground[i]),16); /* We also need to copy the output so as to get correct adaptation */ for (i=0;iframe_size;i++) st->y[i+st->frame_size] = st->e[i+st->frame_size]; for (i=0;iframe_size;i++) st->e[i] = SUB16(st->input[i], st->y[i+st->frame_size]); See = Sff; st->Davg1 = st->Davg2 = 0; st->Dvar1 = st->Dvar2 = FLOAT_ZERO; } } #endif /* Compute error signal (for the output with de-emphasis) */ for (i=0;iframe_size;i++) { spx_word32_t tmp_out; #ifdef TWO_PATH tmp_out = SUB32(EXTEND32(st->input[i]), EXTEND32(st->e[i+st->frame_size])); #else tmp_out = SUB32(EXTEND32(st->input[i]), EXTEND32(st->y[i+st->frame_size])); #endif /* Saturation */ if (tmp_out>32767) tmp_out = 32767; else if (tmp_out<-32768) tmp_out = -32768; tmp_out = ADD32(tmp_out, EXTEND32(MULT16_16_P15(st->preemph, st->memE))); /* This is an arbitrary test for saturation in the microphone signal */ if (in[i] <= -32000 || in[i] >= 32000) { tmp_out = 0; if (st->saturated == 0) st->saturated = 1; } out[i] = (spx_int16_t)tmp_out; st->memE = tmp_out; } #ifdef DUMP_ECHO_CANCEL_DATA dump_audio(in, far_end, out, st->frame_size); #endif /* Compute error signal (filter update version) */ for (i=0;iframe_size;i++) { st->e[i+st->frame_size] = st->e[i]; st->e[i] = 0; } /* Compute a bunch of correlations */ Sey = mdf_inner_prod(st->e+st->frame_size, st->y+st->frame_size, st->frame_size); Syy = mdf_inner_prod(st->y+st->frame_size, st->y+st->frame_size, st->frame_size); Sdd = mdf_inner_prod(st->input, st->input, st->frame_size); /*printf ("%f %f %f %f\n", Sff, See, Syy, Sdd, st->update_cond);*/ /* Do some sanity check */ if (!(Syy>=0 && Sxx>=0 && See >= 0) #ifndef FIXED_POINT || !(Sff < N*1e9 && Syy < N*1e9 && Sxx < N*1e9) #endif ) { /* Things have gone really bad */ st->screwed_up += 50; for (i=0;iframe_size;i++) out[i] = 0; } else if (SHR32(Sff, 2) > ADD32(Sdd, SHR32(MULT16_16(N, 10000),6))) { /* AEC seems to add lots of echo instead of removing it, let's see if it will improve */ st->screwed_up++; } else { /* Everything's fine */ st->screwed_up=0; } if (st->screwed_up>=50) { speex_warning("The echo canceller started acting funny and got slapped (reset). It swears it will behave now."); speex_echo_state_reset(st); return; } /* Add a small noise floor to make sure not to have problems when dividing */ See = MAX32(See, SHR32(MULT16_16(N, 100),6)); /* Convert error to frequency domain */ spx_fft(st->fft_table, st->e, st->E); for (i=0;iframe_size;i++) st->y[i] = 0; spx_fft(st->fft_table, st->y, st->Y); /* Compute power spectrum of far end (X), error (E) and filter response (Y) */ power_spectrum(st->E, st->Rf, N); power_spectrum(st->Y, st->Yf, N); power_spectrum(st->X, st->Xf, N); /* Smooth far end energy estimate over time */ for (j=0;j<=st->frame_size;j++) st->power[j] = MULT16_32_Q15(ss_1,st->power[j]) + 1 + MULT16_32_Q15(ss,st->Xf[j]); /* Enable this to compute the power based only on the tail (would need to compute more efficiently to make this really useful */ if (0) { float scale2 = .5f/M; for (j=0;j<=st->frame_size;j++) st->power[j] = 100; for (i=0;iX[i*N], st->Xf, N); for (j=0;j<=st->frame_size;j++) st->power[j] += scale2*st->Xf[j]; } } /* Compute filtered spectra and (cross-)correlations */ for (j=st->frame_size;j>=0;j--) { spx_float_t Eh, Yh; Eh = PSEUDOFLOAT(st->Rf[j] - st->Eh[j]); Yh = PSEUDOFLOAT(st->Yf[j] - st->Yh[j]); Pey = FLOAT_ADD(Pey,FLOAT_MULT(Eh,Yh)); Pyy = FLOAT_ADD(Pyy,FLOAT_MULT(Yh,Yh)); #ifdef FIXED_POINT st->Eh[j] = MAC16_32_Q15(MULT16_32_Q15(SUB16(32767,st->spec_average),st->Eh[j]), st->spec_average, st->Rf[j]); st->Yh[j] = MAC16_32_Q15(MULT16_32_Q15(SUB16(32767,st->spec_average),st->Yh[j]), st->spec_average, st->Yf[j]); #else st->Eh[j] = (1-st->spec_average)*st->Eh[j] + st->spec_average*st->Rf[j]; st->Yh[j] = (1-st->spec_average)*st->Yh[j] + st->spec_average*st->Yf[j]; #endif } Pyy = FLOAT_SQRT(Pyy); Pey = FLOAT_DIVU(Pey,Pyy); /* Compute correlation updatete rate */ tmp32 = MULT16_32_Q15(st->beta0,Syy); if (tmp32 > MULT16_32_Q15(st->beta_max,See)) tmp32 = MULT16_32_Q15(st->beta_max,See); alpha = FLOAT_DIV32(tmp32, See); alpha_1 = FLOAT_SUB(FLOAT_ONE, alpha); /* Update correlations (recursive average) */ st->Pey = FLOAT_ADD(FLOAT_MULT(alpha_1,st->Pey) , FLOAT_MULT(alpha,Pey)); st->Pyy = FLOAT_ADD(FLOAT_MULT(alpha_1,st->Pyy) , FLOAT_MULT(alpha,Pyy)); if (FLOAT_LT(st->Pyy, FLOAT_ONE)) st->Pyy = FLOAT_ONE; /* We don't really hope to get better than 33 dB (MIN_LEAK-3dB) attenuation anyway */ if (FLOAT_LT(st->Pey, FLOAT_MULT(MIN_LEAK,st->Pyy))) st->Pey = FLOAT_MULT(MIN_LEAK,st->Pyy); if (FLOAT_GT(st->Pey, st->Pyy)) st->Pey = st->Pyy; /* leak_estimate is the linear regression result */ st->leak_estimate = FLOAT_EXTRACT16(FLOAT_SHL(FLOAT_DIVU(st->Pey, st->Pyy),14)); /* This looks like a stupid bug, but it's right (because we convert from Q14 to Q15) */ if (st->leak_estimate > 16383) st->leak_estimate = 32767; else st->leak_estimate = SHL16(st->leak_estimate,1); /*printf ("%f\n", st->leak_estimate);*/ /* Compute Residual to Error Ratio */ #ifdef FIXED_POINT tmp32 = MULT16_32_Q15(st->leak_estimate,Syy); tmp32 = ADD32(SHR32(Sxx,13), ADD32(tmp32, SHL32(tmp32,1))); /* Check for y in e (lower bound on RER) */ { spx_float_t bound = PSEUDOFLOAT(Sey); bound = FLOAT_DIVU(FLOAT_MULT(bound, bound), PSEUDOFLOAT(ADD32(1,Syy))); if (FLOAT_GT(bound, PSEUDOFLOAT(See))) tmp32 = See; else if (tmp32 < FLOAT_EXTRACT32(bound)) tmp32 = FLOAT_EXTRACT32(bound); } if (tmp32 > SHR32(See,1)) tmp32 = SHR32(See,1); RER = FLOAT_EXTRACT16(FLOAT_SHL(FLOAT_DIV32(tmp32,See),15)); #else RER = (.0001*Sxx + 3.*MULT16_32_Q15(st->leak_estimate,Syy)) / See; /* Check for y in e (lower bound on RER) */ if (RER < Sey*Sey/(1+See*Syy)) RER = Sey*Sey/(1+See*Syy); if (RER > .5) RER = .5; #endif /* We consider that the filter has had minimal adaptation if the following is true*/ if (!st->adapted && st->sum_adapt > SHL32(EXTEND32(M),15) && MULT16_32_Q15(st->leak_estimate,Syy) > MULT16_32_Q15(QCONST16(.03f,15),Syy)) { st->adapted = 1; } if (st->adapted) { /* Normal learning rate calculation once we're past the minimal adaptation phase */ for (i=0;i<=st->frame_size;i++) { spx_word32_t r, e; /* Compute frequency-domain adaptation mask */ r = MULT16_32_Q15(st->leak_estimate,SHL32(st->Yf[i],3)); e = SHL32(st->Rf[i],3)+1; #ifdef FIXED_POINT if (r>SHR32(e,1)) r = SHR32(e,1); #else if (r>.5*e) r = .5*e; #endif r = MULT16_32_Q15(QCONST16(.7,15),r) + MULT16_32_Q15(QCONST16(.3,15),(spx_word32_t)(MULT16_32_Q15(RER,e))); /*st->power_1[i] = adapt_rate*r/(e*(1+st->power[i]));*/ st->power_1[i] = FLOAT_SHL(FLOAT_DIV32_FLOAT(r,FLOAT_MUL32U(e,st->power[i]+10)),WEIGHT_SHIFT+16); } } else { /* Temporary adaption rate if filter is not yet adapted enough */ spx_word16_t adapt_rate=0; if (Sxx > SHR32(MULT16_16(N, 1000),6)) { tmp32 = MULT16_32_Q15(QCONST16(.25f, 15), Sxx); #ifdef FIXED_POINT if (tmp32 > SHR32(See,2)) tmp32 = SHR32(See,2); #else if (tmp32 > .25*See) tmp32 = .25*See; #endif adapt_rate = FLOAT_EXTRACT16(FLOAT_SHL(FLOAT_DIV32(tmp32, See),15)); } for (i=0;i<=st->frame_size;i++) st->power_1[i] = FLOAT_SHL(FLOAT_DIV32(EXTEND32(adapt_rate),ADD32(st->power[i],10)),WEIGHT_SHIFT+1); /* How much have we adapted so far? */ st->sum_adapt = ADD32(st->sum_adapt,adapt_rate); } /* Save residual echo so it can be used by the nonlinear processor */ if (st->adapted) { /* If the filter is adapted, take the filtered echo */ for (i=0;iframe_size;i++) st->last_y[i] = st->last_y[st->frame_size+i]; for (i=0;iframe_size;i++) st->last_y[st->frame_size+i] = in[i]-out[i]; } else { /* If filter isn't adapted yet, all we can do is take the far end signal directly */ /* moved earlier: for (i=0;ilast_y[i] = st->x[i];*/ } } /* Compute spectrum of estimated echo for use in an echo post-filter */ void speex_echo_get_residual(SpeexEchoState *st, spx_word32_t *residual_echo, int len) { int i; spx_word16_t leak2; int N; N = st->window_size; /* Apply hanning window (should pre-compute it)*/ for (i=0;iy[i] = MULT16_16_Q15(st->window[i],st->last_y[i]); /* Compute power spectrum of the echo */ spx_fft(st->fft_table, st->y, st->Y); power_spectrum(st->Y, residual_echo, N); #ifdef FIXED_POINT if (st->leak_estimate > 16383) leak2 = 32767; else leak2 = SHL16(st->leak_estimate, 1); #else if (st->leak_estimate>.5) leak2 = 1; else leak2 = 2*st->leak_estimate; #endif /* Estimate residual echo */ for (i=0;i<=st->frame_size;i++) residual_echo[i] = (spx_int32_t)MULT16_32_Q15(leak2,residual_echo[i]); } int speex_echo_ctl(SpeexEchoState *st, int request, void *ptr) { switch(request) { case SPEEX_ECHO_GET_FRAME_SIZE: (*(int*)ptr) = st->frame_size; break; case SPEEX_ECHO_SET_SAMPLING_RATE: st->sampling_rate = (*(int*)ptr); st->spec_average = DIV32_16(SHL32(EXTEND32(st->frame_size), 15), st->sampling_rate); #ifdef FIXED_POINT st->beta0 = DIV32_16(SHL32(EXTEND32(st->frame_size), 16), st->sampling_rate); st->beta_max = DIV32_16(SHL32(EXTEND32(st->frame_size), 14), st->sampling_rate); #else st->beta0 = (2.0f*st->frame_size)/st->sampling_rate; st->beta_max = (.5f*st->frame_size)/st->sampling_rate; #endif if (st->sampling_rate<12000) st->notch_radius = QCONST16(.9, 15); else if (st->sampling_rate<24000) st->notch_radius = QCONST16(.982, 15); else st->notch_radius = QCONST16(.992, 15); break; case SPEEX_ECHO_GET_SAMPLING_RATE: (*(int*)ptr) = st->sampling_rate; break; default: speex_warning_int("Unknown speex_echo_ctl request: ", request); return -1; } return 0; } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/misc_bfin.h ================================================ /* Copyright (C) 2005 Analog Devices */ /** @file misc_bfin.h @author Jean-Marc Valin @brief Various compatibility routines for Speex (Blackfin version) */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define OVERRIDE_SPEEX_MOVE void *speex_move (void *dest, void *src, int n) { __asm__ __volatile__ ( "L0 = 0;\n\t" "I0 = %0;\n\t" "R0 = [I0++];\n\t" "LOOP move%= LC0 = %2;\n\t" "LOOP_BEGIN move%=;\n\t" "[%1++] = R0 || R0 = [I0++];\n\t" "LOOP_END move%=;\n\t" "[%1++] = R0;\n\t" : "=a" (src), "=a" (dest) : "a" ((n>>2)-1), "0" (src), "1" (dest) : "R0", "I0", "L0", "memory" ); return dest; } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/modes.c ================================================ /* Copyright (C) 2002-2006 Jean-Marc Valin File: modes.c Describes the different modes of the codec Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "modes.h" #include "ltp.h" #include "quant_lsp.h" #include "cb_search.h" #include "sb_celp.h" #include "nb_celp.h" #include "vbr.h" #include "arch.h" #include #ifndef NULL #define NULL 0 #endif /* Extern declarations for all codebooks we use here */ extern const signed char gain_cdbk_nb[]; extern const signed char gain_cdbk_lbr[]; extern const signed char exc_5_256_table[]; extern const signed char exc_5_64_table[]; extern const signed char exc_8_128_table[]; extern const signed char exc_10_32_table[]; extern const signed char exc_10_16_table[]; extern const signed char exc_20_32_table[]; /* Parameters for Long-Term Prediction (LTP)*/ static const ltp_params ltp_params_nb = { gain_cdbk_nb, 7, 7 }; /* Parameters for Long-Term Prediction (LTP)*/ static const ltp_params ltp_params_vlbr = { gain_cdbk_lbr, 5, 0 }; /* Parameters for Long-Term Prediction (LTP)*/ static const ltp_params ltp_params_lbr = { gain_cdbk_lbr, 5, 7 }; /* Parameters for Long-Term Prediction (LTP)*/ static const ltp_params ltp_params_med = { gain_cdbk_lbr, 5, 7 }; /* Split-VQ innovation parameters for very low bit-rate narrowband */ static const split_cb_params split_cb_nb_vlbr = { 10, /*subvect_size*/ 4, /*nb_subvect*/ exc_10_16_table, /*shape_cb*/ 4, /*shape_bits*/ 0, }; /* Split-VQ innovation parameters for very low bit-rate narrowband */ static const split_cb_params split_cb_nb_ulbr = { 20, /*subvect_size*/ 2, /*nb_subvect*/ exc_20_32_table, /*shape_cb*/ 5, /*shape_bits*/ 0, }; /* Split-VQ innovation parameters for low bit-rate narrowband */ static const split_cb_params split_cb_nb_lbr = { 10, /*subvect_size*/ 4, /*nb_subvect*/ exc_10_32_table, /*shape_cb*/ 5, /*shape_bits*/ 0, }; /* Split-VQ innovation parameters narrowband */ static const split_cb_params split_cb_nb = { 5, /*subvect_size*/ 8, /*nb_subvect*/ exc_5_64_table, /*shape_cb*/ 6, /*shape_bits*/ 0, }; /* Split-VQ innovation parameters narrowband */ static const split_cb_params split_cb_nb_med = { 8, /*subvect_size*/ 5, /*nb_subvect*/ exc_8_128_table, /*shape_cb*/ 7, /*shape_bits*/ 0, }; /* Split-VQ innovation for low-band wideband */ static const split_cb_params split_cb_sb = { 5, /*subvect_size*/ 8, /*nb_subvect*/ exc_5_256_table, /*shape_cb*/ 8, /*shape_bits*/ 0, }; /* 2150 bps "vocoder-like" mode for comfort noise */ static const SpeexSubmode nb_submode1 = { 0, 1, 0, 0, /* LSP quantization */ lsp_quant_lbr, lsp_unquant_lbr, /* No pitch quantization */ forced_pitch_quant, forced_pitch_unquant, NULL, /* No innovation quantization (noise only) */ noise_codebook_quant, noise_codebook_unquant, NULL, -1, 43 }; /* 3.95 kbps very low bit-rate mode */ static const SpeexSubmode nb_submode8 = { 0, 1, 0, 0, /*LSP quantization*/ lsp_quant_lbr, lsp_unquant_lbr, /*No pitch quantization*/ forced_pitch_quant, forced_pitch_unquant, NULL, /*Innovation quantization*/ split_cb_search_shape_sign, split_cb_shape_sign_unquant, &split_cb_nb_ulbr, QCONST16(.5,15), 79 }; /* 5.95 kbps very low bit-rate mode */ static const SpeexSubmode nb_submode2 = { 0, 0, 0, 0, /*LSP quantization*/ lsp_quant_lbr, lsp_unquant_lbr, /*No pitch quantization*/ pitch_search_3tap, pitch_unquant_3tap, <p_params_vlbr, /*Innovation quantization*/ split_cb_search_shape_sign, split_cb_shape_sign_unquant, &split_cb_nb_vlbr, QCONST16(.6,15), 119 }; /* 8 kbps low bit-rate mode */ static const SpeexSubmode nb_submode3 = { -1, 0, 1, 0, /*LSP quantization*/ lsp_quant_lbr, lsp_unquant_lbr, /*Pitch quantization*/ pitch_search_3tap, pitch_unquant_3tap, <p_params_lbr, /*Innovation quantization*/ split_cb_search_shape_sign, split_cb_shape_sign_unquant, &split_cb_nb_lbr, QCONST16(.55,15), 160 }; /* 11 kbps medium bit-rate mode */ static const SpeexSubmode nb_submode4 = { -1, 0, 1, 0, /*LSP quantization*/ lsp_quant_lbr, lsp_unquant_lbr, /*Pitch quantization*/ pitch_search_3tap, pitch_unquant_3tap, <p_params_med, /*Innovation quantization*/ split_cb_search_shape_sign, split_cb_shape_sign_unquant, &split_cb_nb_med, QCONST16(.45,15), 220 }; /* 15 kbps high bit-rate mode */ static const SpeexSubmode nb_submode5 = { -1, 0, 3, 0, /*LSP quantization*/ lsp_quant_nb, lsp_unquant_nb, /*Pitch quantization*/ pitch_search_3tap, pitch_unquant_3tap, <p_params_nb, /*Innovation quantization*/ split_cb_search_shape_sign, split_cb_shape_sign_unquant, &split_cb_nb, QCONST16(.3,15), 300 }; /* 18.2 high bit-rate mode */ static const SpeexSubmode nb_submode6 = { -1, 0, 3, 0, /*LSP quantization*/ lsp_quant_nb, lsp_unquant_nb, /*Pitch quantization*/ pitch_search_3tap, pitch_unquant_3tap, <p_params_nb, /*Innovation quantization*/ split_cb_search_shape_sign, split_cb_shape_sign_unquant, &split_cb_sb, QCONST16(.2,15), 364 }; /* 24.6 kbps high bit-rate mode */ static const SpeexSubmode nb_submode7 = { -1, 0, 3, 1, /*LSP quantization*/ lsp_quant_nb, lsp_unquant_nb, /*Pitch quantization*/ pitch_search_3tap, pitch_unquant_3tap, <p_params_nb, /*Innovation quantization*/ split_cb_search_shape_sign, split_cb_shape_sign_unquant, &split_cb_nb, QCONST16(.1,15), 492 }; /* Default mode for narrowband */ static const SpeexNBMode nb_mode = { 160, /*frameSize*/ 40, /*subframeSize*/ 10, /*lpcSize*/ 17, /*pitchStart*/ 144, /*pitchEnd*/ #ifdef FIXED_POINT 29491, 19661, /* gamma1, gamma2 */ #else 0.9, 0.6, /* gamma1, gamma2 */ #endif QCONST16(.0002,15), /*lpc_floor*/ {NULL, &nb_submode1, &nb_submode2, &nb_submode3, &nb_submode4, &nb_submode5, &nb_submode6, &nb_submode7, &nb_submode8, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, 5, {1, 8, 2, 3, 3, 4, 4, 5, 5, 6, 7} }; /* Default mode for narrowband */ EXPORT const SpeexMode speex_nb_mode = { &nb_mode, nb_mode_query, "narrowband", 0, 4, &nb_encoder_init, &nb_encoder_destroy, &nb_encode, &nb_decoder_init, &nb_decoder_destroy, &nb_decode, &nb_encoder_ctl, &nb_decoder_ctl, }; EXPORT int speex_mode_query(const SpeexMode *mode, int request, void *ptr) { return mode->query(mode->mode, request, ptr); } #ifdef FIXED_DEBUG long long spx_mips=0; #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/modes.h ================================================ /* Copyright (C) 2002-2006 Jean-Marc Valin */ /** @file modes.h @brief Describes the different modes of the codec */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MODES_H #define MODES_H #include #include #include "arch.h" #define NB_SUBMODES 16 #define NB_SUBMODE_BITS 4 #define SB_SUBMODES 8 #define SB_SUBMODE_BITS 3 /* Used internally, NOT TO BE USED in applications */ /** Used internally*/ #define SPEEX_GET_PI_GAIN 100 /** Used internally*/ #define SPEEX_GET_EXC 101 /** Used internally*/ #define SPEEX_GET_INNOV 102 /** Used internally*/ #define SPEEX_GET_DTX_STATUS 103 /** Used internally*/ #define SPEEX_SET_INNOVATION_SAVE 104 /** Used internally*/ #define SPEEX_SET_WIDEBAND 105 /** Used internally*/ #define SPEEX_GET_STACK 106 /** Quantizes LSPs */ typedef void (*lsp_quant_func)(spx_lsp_t *, spx_lsp_t *, int, SpeexBits *); /** Decodes quantized LSPs */ typedef void (*lsp_unquant_func)(spx_lsp_t *, int, SpeexBits *); /** Long-term predictor quantization */ typedef int (*ltp_quant_func)(spx_word16_t *, spx_word16_t *, spx_coef_t *, spx_coef_t *, spx_coef_t *, spx_sig_t *, const void *, int, int, spx_word16_t, int, int, SpeexBits*, char *, spx_word16_t *, spx_word16_t *, int, int, int, spx_word32_t *); /** Long-term un-quantize */ typedef void (*ltp_unquant_func)(spx_word16_t *, spx_word32_t *, int, int, spx_word16_t, const void *, int, int *, spx_word16_t *, SpeexBits*, char*, int, int, spx_word16_t, int); /** Innovation quantization function */ typedef void (*innovation_quant_func)(spx_word16_t *, spx_coef_t *, spx_coef_t *, spx_coef_t *, const void *, int, int, spx_sig_t *, spx_word16_t *, SpeexBits *, char *, int, int); /** Innovation unquantization function */ typedef void (*innovation_unquant_func)(spx_sig_t *, const void *, int, SpeexBits*, char *, spx_int32_t *); /** Description of a Speex sub-mode (wither narrowband or wideband */ typedef struct SpeexSubmode { int lbr_pitch; /**< Set to -1 for "normal" modes, otherwise encode pitch using a global pitch and allowing a +- lbr_pitch variation (for low not-rates)*/ int forced_pitch_gain; /**< Use the same (forced) pitch gain for all sub-frames */ int have_subframe_gain; /**< Number of bits to use as sub-frame innovation gain */ int double_codebook; /**< Apply innovation quantization twice for higher quality (and higher bit-rate)*/ /*LSP functions*/ lsp_quant_func lsp_quant; /**< LSP quantization function */ lsp_unquant_func lsp_unquant; /**< LSP unquantization function */ /*Long-term predictor functions*/ ltp_quant_func ltp_quant; /**< Long-term predictor (pitch) quantizer */ ltp_unquant_func ltp_unquant; /**< Long-term predictor (pitch) un-quantizer */ const void *ltp_params; /**< Pitch parameters (options) */ /*Quantization of innovation*/ innovation_quant_func innovation_quant; /**< Innovation quantization */ innovation_unquant_func innovation_unquant; /**< Innovation un-quantization */ const void *innovation_params; /**< Innovation quantization parameters*/ spx_word16_t comb_gain; /**< Gain of enhancer comb filter */ int bits_per_frame; /**< Number of bits per frame after encoding*/ } SpeexSubmode; /** Struct defining the encoding/decoding mode*/ typedef struct SpeexNBMode { int frameSize; /**< Size of frames used for encoding */ int subframeSize; /**< Size of sub-frames used for encoding */ int lpcSize; /**< Order of LPC filter */ int pitchStart; /**< Smallest pitch value allowed */ int pitchEnd; /**< Largest pitch value allowed */ spx_word16_t gamma1; /**< Perceptual filter parameter #1 */ spx_word16_t gamma2; /**< Perceptual filter parameter #2 */ spx_word16_t lpc_floor; /**< Noise floor for LPC analysis */ const SpeexSubmode *submodes[NB_SUBMODES]; /**< Sub-mode data for the mode */ int defaultSubmode; /**< Default sub-mode to use when encoding */ int quality_map[11]; /**< Mode corresponding to each quality setting */ } SpeexNBMode; /** Struct defining the encoding/decoding mode for SB-CELP (wideband) */ typedef struct SpeexSBMode { const SpeexMode *nb_mode; /**< Embedded narrowband mode */ int frameSize; /**< Size of frames used for encoding */ int subframeSize; /**< Size of sub-frames used for encoding */ int lpcSize; /**< Order of LPC filter */ spx_word16_t gamma1; /**< Perceptual filter parameter #1 */ spx_word16_t gamma2; /**< Perceptual filter parameter #1 */ spx_word16_t lpc_floor; /**< Noise floor for LPC analysis */ spx_word16_t folding_gain; const SpeexSubmode *submodes[SB_SUBMODES]; /**< Sub-mode data for the mode */ int defaultSubmode; /**< Default sub-mode to use when encoding */ int low_quality_map[11]; /**< Mode corresponding to each quality setting */ int quality_map[11]; /**< Mode corresponding to each quality setting */ #ifndef DISABLE_VBR const float (*vbr_thresh)[11]; #endif int nb_modes; } SpeexSBMode; int speex_encode_native(void *state, spx_word16_t *in, SpeexBits *bits); int speex_decode_native(void *state, SpeexBits *bits, spx_word16_t *out); int nb_mode_query(const void *mode, int request, void *ptr); int wb_mode_query(const void *mode, int request, void *ptr); #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/modes_wb.c ================================================ /* Copyright (C) 2002-2007 Jean-Marc Valin File: modes.c Describes the wideband modes of the codec Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "modes.h" #include "ltp.h" #include "quant_lsp.h" #include "cb_search.h" #include "sb_celp.h" #include "nb_celp.h" #include "vbr.h" #include "arch.h" #include #include "os_support.h" #ifndef NULL #define NULL 0 #endif EXPORT const SpeexMode * const speex_mode_list[SPEEX_NB_MODES] = {&speex_nb_mode, &speex_wb_mode, &speex_uwb_mode}; extern const signed char hexc_table[]; extern const signed char hexc_10_32_table[]; #ifndef DISABLE_WIDEBAND /* Split-VQ innovation for high-band wideband */ static const split_cb_params split_cb_high = { 8, /*subvect_size*/ 5, /*nb_subvect*/ hexc_table, /*shape_cb*/ 7, /*shape_bits*/ 1, }; /* Split-VQ innovation for high-band wideband */ static const split_cb_params split_cb_high_lbr = { 10, /*subvect_size*/ 4, /*nb_subvect*/ hexc_10_32_table, /*shape_cb*/ 5, /*shape_bits*/ 0, }; #endif static const SpeexSubmode wb_submode1 = { 0, 0, 1, 0, /*LSP quantization*/ lsp_quant_high, lsp_unquant_high, /*Pitch quantization*/ NULL, NULL, NULL, /*No innovation quantization*/ NULL, NULL, NULL, -1, 36 }; static const SpeexSubmode wb_submode2 = { 0, 0, 1, 0, /*LSP quantization*/ lsp_quant_high, lsp_unquant_high, /*Pitch quantization*/ NULL, NULL, NULL, /*Innovation quantization*/ split_cb_search_shape_sign, split_cb_shape_sign_unquant, #ifdef DISABLE_WIDEBAND NULL, #else &split_cb_high_lbr, #endif -1, 112 }; static const SpeexSubmode wb_submode3 = { 0, 0, 1, 0, /*LSP quantization*/ lsp_quant_high, lsp_unquant_high, /*Pitch quantization*/ NULL, NULL, NULL, /*Innovation quantization*/ split_cb_search_shape_sign, split_cb_shape_sign_unquant, #ifdef DISABLE_WIDEBAND NULL, #else &split_cb_high, #endif -1, 192 }; static const SpeexSubmode wb_submode4 = { 0, 0, 1, 1, /*LSP quantization*/ lsp_quant_high, lsp_unquant_high, /*Pitch quantization*/ NULL, NULL, NULL, /*Innovation quantization*/ split_cb_search_shape_sign, split_cb_shape_sign_unquant, #ifdef DISABLE_WIDEBAND NULL, #else &split_cb_high, #endif -1, 352 }; /* Split-band wideband CELP mode*/ static const SpeexSBMode sb_wb_mode = { &speex_nb_mode, 160, /*frameSize*/ 40, /*subframeSize*/ 8, /*lpcSize*/ #ifdef FIXED_POINT 29491, 19661, /* gamma1, gamma2 */ #else 0.9, 0.6, /* gamma1, gamma2 */ #endif QCONST16(.0002,15), /*lpc_floor*/ QCONST16(0.9f,15), {NULL, &wb_submode1, &wb_submode2, &wb_submode3, &wb_submode4, NULL, NULL, NULL}, 3, {1, 8, 2, 3, 4, 5, 5, 6, 6, 7, 7}, {1, 1, 1, 1, 1, 1, 2, 2, 3, 3, 4}, #ifndef DISABLE_VBR vbr_hb_thresh, #endif 5 }; EXPORT const SpeexMode speex_wb_mode = { &sb_wb_mode, wb_mode_query, "wideband (sub-band CELP)", 1, 4, &sb_encoder_init, &sb_encoder_destroy, &sb_encode, &sb_decoder_init, &sb_decoder_destroy, &sb_decode, &sb_encoder_ctl, &sb_decoder_ctl, }; /* "Ultra-wideband" mode stuff */ /* Split-band "ultra-wideband" (32 kbps) CELP mode*/ static const SpeexSBMode sb_uwb_mode = { &speex_wb_mode, 320, /*frameSize*/ 80, /*subframeSize*/ 8, /*lpcSize*/ #ifdef FIXED_POINT 29491, 19661, /* gamma1, gamma2 */ #else 0.9, 0.6, /* gamma1, gamma2 */ #endif QCONST16(.0002,15), /*lpc_floor*/ QCONST16(0.7f,15), {NULL, &wb_submode1, NULL, NULL, NULL, NULL, NULL, NULL}, 1, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, #ifndef DISABLE_VBR vbr_uhb_thresh, #endif 2 }; int wb_mode_query(const void *mode, int request, void *ptr) { const SpeexSBMode *m = (const SpeexSBMode*)mode; switch (request) { case SPEEX_MODE_FRAME_SIZE: *((int*)ptr)=2*m->frameSize; break; case SPEEX_SUBMODE_BITS_PER_FRAME: if (*((int*)ptr)==0) *((int*)ptr) = SB_SUBMODE_BITS+1; else if (m->submodes[*((int*)ptr)]==NULL) *((int*)ptr) = -1; else *((int*)ptr) = m->submodes[*((int*)ptr)]->bits_per_frame; break; default: speex_warning_int("Unknown wb_mode_query request: ", request); return -1; } return 0; } EXPORT const SpeexMode speex_uwb_mode = { &sb_uwb_mode, wb_mode_query, "ultra-wideband (sub-band CELP)", 2, 4, &sb_encoder_init, &sb_encoder_destroy, &sb_encode, &sb_decoder_init, &sb_decoder_destroy, &sb_decode, &sb_encoder_ctl, &sb_decoder_ctl, }; /* We have defined speex_lib_get_mode() as a macro in speex.h */ #undef speex_lib_get_mode EXPORT const SpeexMode * speex_lib_get_mode (int mode) { if (mode < 0 || mode >= SPEEX_NB_MODES) return NULL; return speex_mode_list[mode]; } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/nb_celp.c ================================================ /* Copyright (C) 2002-2006 Jean-Marc Valin File: nb_celp.c Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "nb_celp.h" #include "lpc.h" #include "lsp.h" #include "ltp.h" #include "quant_lsp.h" #include "cb_search.h" #include "filters.h" #include "stack_alloc.h" #include "vq.h" #include #include "vbr.h" #include "arch.h" #include "math_approx.h" #include "os_support.h" #include #ifdef VORBIS_PSYCHO #include "vorbis_psy.h" #endif #ifndef M_PI #define M_PI 3.14159265358979323846 /* pi */ #endif #ifndef NULL #define NULL 0 #endif #define SUBMODE(x) st->submodes[st->submodeID]->x /* Default size for the encoder and decoder stack (can be changed at compile time). This does not apply when using variable-size arrays or alloca. */ #ifndef NB_ENC_STACK #define NB_ENC_STACK (8000*sizeof(spx_sig_t)) #endif #ifndef NB_DEC_STACK #define NB_DEC_STACK (4000*sizeof(spx_sig_t)) #endif #ifdef FIXED_POINT const spx_word32_t ol_gain_table[32]={18900, 25150, 33468, 44536, 59265, 78865, 104946, 139653, 185838, 247297, 329081, 437913, 582736, 775454, 1031906, 1373169, 1827293, 2431601, 3235761, 4305867, 5729870, 7624808, 10146425, 13501971, 17967238, 23909222, 31816294, 42338330, 56340132, 74972501, 99766822, 132760927}; const spx_word16_t exc_gain_quant_scal3_bound[7]={1841, 3883, 6051, 8062, 10444, 13580, 18560}; const spx_word16_t exc_gain_quant_scal3[8]={1002, 2680, 5086, 7016, 9108, 11781, 15380, 21740}; const spx_word16_t exc_gain_quant_scal1_bound[1]={14385}; const spx_word16_t exc_gain_quant_scal1[2]={11546, 17224}; #define LSP_MARGIN 16 #define LSP_DELTA1 6553 #define LSP_DELTA2 1638 #else const float exc_gain_quant_scal3_bound[7]={0.112338f, 0.236980f, 0.369316f, 0.492054f, 0.637471f, 0.828874f, 1.132784f}; const float exc_gain_quant_scal3[8]={0.061130f, 0.163546f, 0.310413f, 0.428220f, 0.555887f, 0.719055f, 0.938694f, 1.326874f}; const float exc_gain_quant_scal1_bound[1]={0.87798f}; const float exc_gain_quant_scal1[2]={0.70469f, 1.05127f}; #define LSP_MARGIN .002f #define LSP_DELTA1 .2f #define LSP_DELTA2 .05f #endif #ifdef VORBIS_PSYCHO #define EXTRA_BUFFER 100 #else #define EXTRA_BUFFER 0 #endif #define sqr(x) ((x)*(x)) extern const spx_word16_t lag_window[]; extern const spx_word16_t lpc_window[]; void *nb_encoder_init(const SpeexMode *m) { EncState *st; const SpeexNBMode *mode; int i; mode=(const SpeexNBMode *)m->mode; st = (EncState*)speex_alloc(sizeof(EncState)); if (!st) return NULL; #if defined(VAR_ARRAYS) || defined (USE_ALLOCA) st->stack = NULL; #else st->stack = (char*)speex_alloc_scratch(NB_ENC_STACK); #endif st->mode=m; st->frameSize = mode->frameSize; st->nbSubframes=mode->frameSize/mode->subframeSize; st->subframeSize=mode->subframeSize; st->windowSize = st->frameSize+st->subframeSize; st->lpcSize = mode->lpcSize; st->gamma1=mode->gamma1; st->gamma2=mode->gamma2; st->min_pitch=mode->pitchStart; st->max_pitch=mode->pitchEnd; st->lpc_floor = mode->lpc_floor; st->submodes=mode->submodes; st->submodeID=st->submodeSelect=mode->defaultSubmode; st->bounded_pitch = 1; st->encode_submode = 1; #ifdef VORBIS_PSYCHO st->psy = vorbis_psy_init(8000, 256); st->curve = (float*)speex_alloc(128*sizeof(float)); st->old_curve = (float*)speex_alloc(128*sizeof(float)); st->psy_window = (float*)speex_alloc(256*sizeof(float)); #endif st->cumul_gain = 1024; /* Allocating input buffer */ st->winBuf = (spx_word16_t*)speex_alloc((st->windowSize-st->frameSize)*sizeof(spx_word16_t)); /* Allocating excitation buffer */ st->excBuf = (spx_word16_t*)speex_alloc((mode->frameSize+mode->pitchEnd+2)*sizeof(spx_word16_t)); st->exc = st->excBuf + mode->pitchEnd + 2; st->swBuf = (spx_word16_t*)speex_alloc((mode->frameSize+mode->pitchEnd+2)*sizeof(spx_word16_t)); st->sw = st->swBuf + mode->pitchEnd + 2; st->window= lpc_window; /* Create the window for autocorrelation (lag-windowing) */ st->lagWindow = lag_window; st->old_lsp = (spx_lsp_t*)speex_alloc((st->lpcSize)*sizeof(spx_lsp_t)); st->old_qlsp = (spx_lsp_t*)speex_alloc((st->lpcSize)*sizeof(spx_lsp_t)); st->first = 1; for (i=0;ilpcSize;i++) st->old_lsp[i]= DIV32(MULT16_16(QCONST16(3.1415927f, LSP_SHIFT), i+1), st->lpcSize+1); st->mem_sp = (spx_mem_t*)speex_alloc((st->lpcSize)*sizeof(spx_mem_t)); st->mem_sw = (spx_mem_t*)speex_alloc((st->lpcSize)*sizeof(spx_mem_t)); st->mem_sw_whole = (spx_mem_t*)speex_alloc((st->lpcSize)*sizeof(spx_mem_t)); st->mem_exc = (spx_mem_t*)speex_alloc((st->lpcSize)*sizeof(spx_mem_t)); st->mem_exc2 = (spx_mem_t*)speex_alloc((st->lpcSize)*sizeof(spx_mem_t)); st->pi_gain = (spx_word32_t*)speex_alloc((st->nbSubframes)*sizeof(spx_word32_t)); st->innov_rms_save = NULL; st->pitch = (int*)speex_alloc((st->nbSubframes)*sizeof(int)); #ifndef DISABLE_VBR st->vbr = (VBRState*)speex_alloc(sizeof(VBRState)); vbr_init(st->vbr); st->vbr_quality = 8; st->vbr_enabled = 0; st->vbr_max = 0; st->vad_enabled = 0; st->dtx_enabled = 0; st->dtx_count=0; st->abr_enabled = 0; st->abr_drift = 0; st->abr_drift2 = 0; #endif /* #ifndef DISABLE_VBR */ st->plc_tuning = 2; st->complexity=2; st->sampling_rate=8000; st->isWideband = 0; st->highpass_enabled = 1; #ifdef ENABLE_VALGRIND VALGRIND_MAKE_READABLE(st, NB_ENC_STACK); #endif return st; } void nb_encoder_destroy(void *state) { EncState *st=(EncState *)state; /* Free all allocated memory */ #if !(defined(VAR_ARRAYS) || defined (USE_ALLOCA)) speex_free_scratch(st->stack); #endif speex_free (st->winBuf); speex_free (st->excBuf); speex_free (st->old_qlsp); speex_free (st->swBuf); speex_free (st->old_lsp); speex_free (st->mem_sp); speex_free (st->mem_sw); speex_free (st->mem_sw_whole); speex_free (st->mem_exc); speex_free (st->mem_exc2); speex_free (st->pi_gain); speex_free (st->pitch); #ifndef DISABLE_VBR vbr_destroy(st->vbr); speex_free (st->vbr); #endif /* #ifndef DISABLE_VBR */ #ifdef VORBIS_PSYCHO vorbis_psy_destroy(st->psy); speex_free (st->curve); speex_free (st->old_curve); speex_free (st->psy_window); #endif /*Free state memory... should be last*/ speex_free(st); } int nb_encode(void *state, void *vin, SpeexBits *bits) { EncState *st; int i, sub, roots; int ol_pitch; spx_word16_t ol_pitch_coef; spx_word32_t ol_gain; VARDECL(spx_word16_t *ringing); VARDECL(spx_word16_t *target); VARDECL(spx_sig_t *innov); VARDECL(spx_word32_t *exc32); VARDECL(spx_mem_t *mem); VARDECL(spx_coef_t *bw_lpc1); VARDECL(spx_coef_t *bw_lpc2); VARDECL(spx_coef_t *lpc); VARDECL(spx_lsp_t *lsp); VARDECL(spx_lsp_t *qlsp); VARDECL(spx_lsp_t *interp_lsp); VARDECL(spx_lsp_t *interp_qlsp); VARDECL(spx_coef_t *interp_lpc); VARDECL(spx_coef_t *interp_qlpc); char *stack; VARDECL(spx_word16_t *syn_resp); VARDECL(spx_word16_t *real_exc); spx_word32_t ener=0; spx_word16_t fine_gain; spx_word16_t *in = (spx_word16_t*)vin; st=(EncState *)state; stack=st->stack; ALLOC(lpc, st->lpcSize, spx_coef_t); ALLOC(bw_lpc1, st->lpcSize, spx_coef_t); ALLOC(bw_lpc2, st->lpcSize, spx_coef_t); ALLOC(lsp, st->lpcSize, spx_lsp_t); ALLOC(qlsp, st->lpcSize, spx_lsp_t); ALLOC(interp_lsp, st->lpcSize, spx_lsp_t); ALLOC(interp_qlsp, st->lpcSize, spx_lsp_t); ALLOC(interp_lpc, st->lpcSize, spx_coef_t); ALLOC(interp_qlpc, st->lpcSize, spx_coef_t); /* Move signals 1 frame towards the past */ SPEEX_MOVE(st->excBuf, st->excBuf+st->frameSize, st->max_pitch+2); SPEEX_MOVE(st->swBuf, st->swBuf+st->frameSize, st->max_pitch+2); if (st->highpass_enabled) highpass(in, in, st->frameSize, (st->isWideband?HIGHPASS_WIDEBAND:HIGHPASS_NARROWBAND)|HIGHPASS_INPUT, st->mem_hp); { VARDECL(spx_word16_t *w_sig); VARDECL(spx_word16_t *autocorr); ALLOC(w_sig, st->windowSize, spx_word16_t); ALLOC(autocorr, st->lpcSize+1, spx_word16_t); /* Window for analysis */ for (i=0;iwindowSize-st->frameSize;i++) w_sig[i] = EXTRACT16(SHR32(MULT16_16(st->winBuf[i],st->window[i]),SIG_SHIFT)); for (;iwindowSize;i++) w_sig[i] = EXTRACT16(SHR32(MULT16_16(in[i-st->windowSize+st->frameSize],st->window[i]),SIG_SHIFT)); /* Compute auto-correlation */ _spx_autocorr(w_sig, autocorr, st->lpcSize+1, st->windowSize); autocorr[0] = ADD16(autocorr[0],MULT16_16_Q15(autocorr[0],st->lpc_floor)); /* Noise floor in auto-correlation domain */ /* Lag windowing: equivalent to filtering in the power-spectrum domain */ for (i=0;ilpcSize+1;i++) autocorr[i] = MULT16_16_Q14(autocorr[i],st->lagWindow[i]); /* Levinson-Durbin */ _spx_lpc(lpc, autocorr, st->lpcSize); /* LPC to LSPs (x-domain) transform */ roots=lpc_to_lsp (lpc, st->lpcSize, lsp, 10, LSP_DELTA1, stack); /* Check if we found all the roots */ if (roots!=st->lpcSize) { /*If we can't find all LSP's, do some damage control and use previous filter*/ for (i=0;ilpcSize;i++) { lsp[i]=st->old_lsp[i]; } } } /* Whole frame analysis (open-loop estimation of pitch and excitation gain) */ { int diff = st->windowSize-st->frameSize; if (st->first) for (i=0;ilpcSize;i++) interp_lsp[i] = lsp[i]; else lsp_interpolate(st->old_lsp, lsp, interp_lsp, st->lpcSize, st->nbSubframes, st->nbSubframes<<1); lsp_enforce_margin(interp_lsp, st->lpcSize, LSP_MARGIN); /* Compute interpolated LPCs (unquantized) for whole frame*/ lsp_to_lpc(interp_lsp, interp_lpc, st->lpcSize,stack); /*Open-loop pitch*/ if (!st->submodes[st->submodeID] || (st->complexity>2 && SUBMODE(have_subframe_gain)<3) || SUBMODE(forced_pitch_gain) || SUBMODE(lbr_pitch) != -1 #ifndef DISABLE_VBR || st->vbr_enabled || st->vad_enabled #endif ) { int nol_pitch[6]; spx_word16_t nol_pitch_coef[6]; bw_lpc(st->gamma1, interp_lpc, bw_lpc1, st->lpcSize); bw_lpc(st->gamma2, interp_lpc, bw_lpc2, st->lpcSize); SPEEX_COPY(st->sw, st->winBuf, diff); SPEEX_COPY(st->sw+diff, in, st->frameSize-diff); filter_mem16(st->sw, bw_lpc1, bw_lpc2, st->sw, st->frameSize, st->lpcSize, st->mem_sw_whole, stack); open_loop_nbest_pitch(st->sw, st->min_pitch, st->max_pitch, st->frameSize, nol_pitch, nol_pitch_coef, 6, stack); ol_pitch=nol_pitch[0]; ol_pitch_coef = nol_pitch_coef[0]; /*Try to remove pitch multiples*/ for (i=1;i<6;i++) { #ifdef FIXED_POINT if ((nol_pitch_coef[i]>MULT16_16_Q15(nol_pitch_coef[0],27853)) && #else if ((nol_pitch_coef[i]>.85*nol_pitch_coef[0]) && #endif (ABS(2*nol_pitch[i]-ol_pitch)<=2 || ABS(3*nol_pitch[i]-ol_pitch)<=3 || ABS(4*nol_pitch[i]-ol_pitch)<=4 || ABS(5*nol_pitch[i]-ol_pitch)<=5)) { /*ol_pitch_coef=nol_pitch_coef[i];*/ ol_pitch = nol_pitch[i]; } } /*if (ol_pitch>50) ol_pitch/=2;*/ /*ol_pitch_coef = sqrt(ol_pitch_coef);*/ } else { ol_pitch=0; ol_pitch_coef=0; } /*Compute "real" excitation*/ SPEEX_COPY(st->exc, st->winBuf, diff); SPEEX_COPY(st->exc+diff, in, st->frameSize-diff); fir_mem16(st->exc, interp_lpc, st->exc, st->frameSize, st->lpcSize, st->mem_exc, stack); /* Compute open-loop excitation gain */ { spx_word16_t g = compute_rms16(st->exc, st->frameSize); if (st->submodeID!=1 && ol_pitch>0) ol_gain = MULT16_16(g, MULT16_16_Q14(QCONST16(1.1,14), spx_sqrt(QCONST32(1.,28)-MULT16_32_Q15(QCONST16(.8,15),SHL32(MULT16_16(ol_pitch_coef,ol_pitch_coef),16))))); else ol_gain = SHL32(EXTEND32(g),SIG_SHIFT); } } #ifdef VORBIS_PSYCHO SPEEX_MOVE(st->psy_window, st->psy_window+st->frameSize, 256-st->frameSize); SPEEX_COPY(&st->psy_window[256-st->frameSize], in, st->frameSize); compute_curve(st->psy, st->psy_window, st->curve); /*print_vec(st->curve, 128, "curve");*/ if (st->first) SPEEX_COPY(st->old_curve, st->curve, 128); #endif /*VBR stuff*/ #ifndef DISABLE_VBR if (st->vbr && (st->vbr_enabled||st->vad_enabled)) { float lsp_dist=0; for (i=0;ilpcSize;i++) lsp_dist += (st->old_lsp[i] - lsp[i])*(st->old_lsp[i] - lsp[i]); lsp_dist /= LSP_SCALING*LSP_SCALING; if (st->abr_enabled) { float qual_change=0; if (st->abr_drift2 * st->abr_drift > 0) { /* Only adapt if long-term and short-term drift are the same sign */ qual_change = -.00001*st->abr_drift/(1+st->abr_count); if (qual_change>.05) qual_change=.05; if (qual_change<-.05) qual_change=-.05; } st->vbr_quality += qual_change; if (st->vbr_quality>10) st->vbr_quality=10; if (st->vbr_quality<0) st->vbr_quality=0; } st->relative_quality = vbr_analysis(st->vbr, in, st->frameSize, ol_pitch, GAIN_SCALING_1*ol_pitch_coef); /*if (delta_qual<0)*/ /* delta_qual*=.1*(3+st->vbr_quality);*/ if (st->vbr_enabled) { spx_int32_t mode; int choice=0; float min_diff=100; mode = 8; while (mode) { int v1; float thresh; v1=(int)floor(st->vbr_quality); if (v1==10) thresh = vbr_nb_thresh[mode][v1]; else thresh = (st->vbr_quality-v1)*vbr_nb_thresh[mode][v1+1] + (1+v1-st->vbr_quality)*vbr_nb_thresh[mode][v1]; if (st->relative_quality > thresh && st->relative_quality-threshrelative_quality-thresh; } mode--; } mode=choice; if (mode==0) { if (st->dtx_count==0 || lsp_dist>.05 || !st->dtx_enabled || st->dtx_count>20) { mode=1; st->dtx_count=1; } else { mode=0; st->dtx_count++; } } else { st->dtx_count=0; } speex_encoder_ctl(state, SPEEX_SET_MODE, &mode); if (st->vbr_max>0) { spx_int32_t rate; speex_encoder_ctl(state, SPEEX_GET_BITRATE, &rate); if (rate > st->vbr_max) { rate = st->vbr_max; speex_encoder_ctl(state, SPEEX_SET_BITRATE, &rate); } } if (st->abr_enabled) { spx_int32_t bitrate; speex_encoder_ctl(state, SPEEX_GET_BITRATE, &bitrate); st->abr_drift+=(bitrate-st->abr_enabled); st->abr_drift2 = .95*st->abr_drift2 + .05*(bitrate-st->abr_enabled); st->abr_count += 1.0; } } else { /*VAD only case*/ int mode; if (st->relative_quality<2) { if (st->dtx_count==0 || lsp_dist>.05 || !st->dtx_enabled || st->dtx_count>20) { st->dtx_count=1; mode=1; } else { mode=0; st->dtx_count++; } } else { st->dtx_count = 0; mode=st->submodeSelect; } /*speex_encoder_ctl(state, SPEEX_SET_MODE, &mode);*/ st->submodeID=mode; } } else { st->relative_quality = -1; } #endif /* #ifndef DISABLE_VBR */ if (st->encode_submode) { /* First, transmit a zero for narrowband */ speex_bits_pack(bits, 0, 1); /* Transmit the sub-mode we use for this frame */ speex_bits_pack(bits, st->submodeID, NB_SUBMODE_BITS); } /* If null mode (no transmission), just set a couple things to zero*/ if (st->submodes[st->submodeID] == NULL) { for (i=0;iframeSize;i++) st->exc[i]=st->sw[i]=VERY_SMALL; for (i=0;ilpcSize;i++) st->mem_sw[i]=0; st->first=1; st->bounded_pitch = 1; SPEEX_COPY(st->winBuf, in+2*st->frameSize-st->windowSize, st->windowSize-st->frameSize); /* Clear memory (no need to really compute it) */ for (i=0;ilpcSize;i++) st->mem_sp[i] = 0; return 0; } /* LSP Quantization */ if (st->first) { for (i=0;ilpcSize;i++) st->old_lsp[i] = lsp[i]; } /*Quantize LSPs*/ #if 1 /*0 for unquantized*/ SUBMODE(lsp_quant)(lsp, qlsp, st->lpcSize, bits); #else for (i=0;ilpcSize;i++) qlsp[i]=lsp[i]; #endif /*If we use low bit-rate pitch mode, transmit open-loop pitch*/ if (SUBMODE(lbr_pitch)!=-1) { speex_bits_pack(bits, ol_pitch-st->min_pitch, 7); } if (SUBMODE(forced_pitch_gain)) { int quant; /* This just damps the pitch a bit, because it tends to be too aggressive when forced */ ol_pitch_coef = MULT16_16_Q15(QCONST16(.9,15), ol_pitch_coef); #ifdef FIXED_POINT quant = PSHR16(MULT16_16_16(15, ol_pitch_coef),GAIN_SHIFT); #else quant = (int)floor(.5+15*ol_pitch_coef*GAIN_SCALING_1); #endif if (quant>15) quant=15; if (quant<0) quant=0; speex_bits_pack(bits, quant, 4); ol_pitch_coef=MULT16_16_P15(QCONST16(0.066667,15),SHL16(quant,GAIN_SHIFT)); } /*Quantize and transmit open-loop excitation gain*/ #ifdef FIXED_POINT { int qe = scal_quant32(ol_gain, ol_gain_table, 32); /*ol_gain = exp(qe/3.5)*SIG_SCALING;*/ ol_gain = MULT16_32_Q15(28406,ol_gain_table[qe]); speex_bits_pack(bits, qe, 5); } #else { int qe = (int)(floor(.5+3.5*log(ol_gain*1.0/SIG_SCALING))); if (qe<0) qe=0; if (qe>31) qe=31; ol_gain = exp(qe/3.5)*SIG_SCALING; speex_bits_pack(bits, qe, 5); } #endif /* Special case for first frame */ if (st->first) { for (i=0;ilpcSize;i++) st->old_qlsp[i] = qlsp[i]; } /* Target signal */ ALLOC(target, st->subframeSize, spx_word16_t); ALLOC(innov, st->subframeSize, spx_sig_t); ALLOC(exc32, st->subframeSize, spx_word32_t); ALLOC(ringing, st->subframeSize, spx_word16_t); ALLOC(syn_resp, st->subframeSize, spx_word16_t); ALLOC(real_exc, st->subframeSize, spx_word16_t); ALLOC(mem, st->lpcSize, spx_mem_t); /* Loop on sub-frames */ for (sub=0;subnbSubframes;sub++) { int offset; spx_word16_t *sw; spx_word16_t *exc; int pitch; int response_bound = st->subframeSize; /* Offset relative to start of frame */ offset = st->subframeSize*sub; /* Excitation */ exc=st->exc+offset; /* Weighted signal */ sw=st->sw+offset; /* LSP interpolation (quantized and unquantized) */ lsp_interpolate(st->old_lsp, lsp, interp_lsp, st->lpcSize, sub, st->nbSubframes); lsp_interpolate(st->old_qlsp, qlsp, interp_qlsp, st->lpcSize, sub, st->nbSubframes); /* Make sure the filters are stable */ lsp_enforce_margin(interp_lsp, st->lpcSize, LSP_MARGIN); lsp_enforce_margin(interp_qlsp, st->lpcSize, LSP_MARGIN); /* Compute interpolated LPCs (quantized and unquantized) */ lsp_to_lpc(interp_lsp, interp_lpc, st->lpcSize,stack); lsp_to_lpc(interp_qlsp, interp_qlpc, st->lpcSize, stack); /* Compute analysis filter gain at w=pi (for use in SB-CELP) */ { spx_word32_t pi_g=LPC_SCALING; for (i=0;ilpcSize;i+=2) { /*pi_g += -st->interp_qlpc[i] + st->interp_qlpc[i+1];*/ pi_g = ADD32(pi_g, SUB32(EXTEND32(interp_qlpc[i+1]),EXTEND32(interp_qlpc[i]))); } st->pi_gain[sub] = pi_g; } #ifdef VORBIS_PSYCHO { float curr_curve[128]; float fact = ((float)sub+1.0f)/st->nbSubframes; for (i=0;i<128;i++) curr_curve[i] = (1.0f-fact)*st->old_curve[i] + fact*st->curve[i]; curve_to_lpc(st->psy, curr_curve, bw_lpc1, bw_lpc2, 10); } #else /* Compute bandwidth-expanded (unquantized) LPCs for perceptual weighting */ bw_lpc(st->gamma1, interp_lpc, bw_lpc1, st->lpcSize); if (st->gamma2>=0) bw_lpc(st->gamma2, interp_lpc, bw_lpc2, st->lpcSize); else { for (i=0;ilpcSize;i++) bw_lpc2[i]=0; } /*print_vec(st->bw_lpc1, 10, "bw_lpc");*/ #endif /*FIXME: This will break if we change the window size */ speex_assert(st->windowSize-st->frameSize == st->subframeSize); if (sub==0) { for (i=0;isubframeSize;i++) real_exc[i] = sw[i] = st->winBuf[i]; } else { for (i=0;isubframeSize;i++) real_exc[i] = sw[i] = in[i+((sub-1)*st->subframeSize)]; } fir_mem16(real_exc, interp_qlpc, real_exc, st->subframeSize, st->lpcSize, st->mem_exc2, stack); if (st->complexity==0) response_bound >>= 1; compute_impulse_response(interp_qlpc, bw_lpc1, bw_lpc2, syn_resp, response_bound, st->lpcSize, stack); for (i=response_bound;isubframeSize;i++) syn_resp[i]=VERY_SMALL; /* Compute zero response of A(z/g1) / ( A(z/g2) * A(z) ) */ for (i=0;ilpcSize;i++) mem[i]=SHL32(st->mem_sp[i],1); for (i=0;isubframeSize;i++) ringing[i] = VERY_SMALL; #ifdef SHORTCUTS2 iir_mem16(ringing, interp_qlpc, ringing, response_bound, st->lpcSize, mem, stack); for (i=0;ilpcSize;i++) mem[i]=SHL32(st->mem_sw[i],1); filter_mem16(ringing, st->bw_lpc1, st->bw_lpc2, ringing, response_bound, st->lpcSize, mem, stack); SPEEX_MEMSET(&ringing[response_bound], 0, st->subframeSize-response_bound); #else iir_mem16(ringing, interp_qlpc, ringing, st->subframeSize, st->lpcSize, mem, stack); for (i=0;ilpcSize;i++) mem[i]=SHL32(st->mem_sw[i],1); filter_mem16(ringing, bw_lpc1, bw_lpc2, ringing, st->subframeSize, st->lpcSize, mem, stack); #endif /* Compute weighted signal */ for (i=0;ilpcSize;i++) mem[i]=st->mem_sw[i]; filter_mem16(sw, bw_lpc1, bw_lpc2, sw, st->subframeSize, st->lpcSize, mem, stack); if (st->complexity==0) for (i=0;ilpcSize;i++) st->mem_sw[i]=mem[i]; /* Compute target signal (saturation prevents overflows on clipped input speech) */ for (i=0;isubframeSize;i++) target[i]=EXTRACT16(SATURATE(SUB32(sw[i],PSHR32(ringing[i],1)),32767)); /* Reset excitation */ SPEEX_MEMSET(exc, 0, st->subframeSize); /* If we have a long-term predictor (otherwise, something's wrong) */ speex_assert (SUBMODE(ltp_quant)); { int pit_min, pit_max; /* Long-term prediction */ if (SUBMODE(lbr_pitch) != -1) { /* Low bit-rate pitch handling */ int margin; margin = SUBMODE(lbr_pitch); if (margin) { if (ol_pitch < st->min_pitch+margin-1) ol_pitch=st->min_pitch+margin-1; if (ol_pitch > st->max_pitch-margin) ol_pitch=st->max_pitch-margin; pit_min = ol_pitch-margin+1; pit_max = ol_pitch+margin; } else { pit_min=pit_max=ol_pitch; } } else { pit_min = st->min_pitch; pit_max = st->max_pitch; } /* Force pitch to use only the current frame if needed */ if (st->bounded_pitch && pit_max>offset) pit_max=offset; /* Perform pitch search */ pitch = SUBMODE(ltp_quant)(target, sw, interp_qlpc, bw_lpc1, bw_lpc2, exc32, SUBMODE(ltp_params), pit_min, pit_max, ol_pitch_coef, st->lpcSize, st->subframeSize, bits, stack, exc, syn_resp, st->complexity, 0, st->plc_tuning, &st->cumul_gain); st->pitch[sub]=pitch; } /* Quantization of innovation */ SPEEX_MEMSET(innov, 0, st->subframeSize); /* FIXME: Make sure this is save from overflows (so far so good) */ for (i=0;isubframeSize;i++) real_exc[i] = EXTRACT16(SUB32(EXTEND32(real_exc[i]), PSHR32(exc32[i],SIG_SHIFT-1))); ener = SHL32(EXTEND32(compute_rms16(real_exc, st->subframeSize)),SIG_SHIFT); /*FIXME: Should use DIV32_16 and make sure result fits in 16 bits */ #ifdef FIXED_POINT { spx_word32_t f = PDIV32(ener,PSHR32(ol_gain,SIG_SHIFT)); if (f<=32767) fine_gain = f; else fine_gain = 32767; } #else fine_gain = PDIV32_16(ener,PSHR32(ol_gain,SIG_SHIFT)); #endif /* Calculate gain correction for the sub-frame (if any) */ if (SUBMODE(have_subframe_gain)) { int qe; if (SUBMODE(have_subframe_gain)==3) { qe = scal_quant(fine_gain, exc_gain_quant_scal3_bound, 8); speex_bits_pack(bits, qe, 3); ener=MULT16_32_Q14(exc_gain_quant_scal3[qe],ol_gain); } else { qe = scal_quant(fine_gain, exc_gain_quant_scal1_bound, 2); speex_bits_pack(bits, qe, 1); ener=MULT16_32_Q14(exc_gain_quant_scal1[qe],ol_gain); } } else { ener=ol_gain; } /*printf ("%f %f\n", ener, ol_gain);*/ /* Normalize innovation */ signal_div(target, target, ener, st->subframeSize); /* Quantize innovation */ speex_assert (SUBMODE(innovation_quant)); { /* Codebook search */ SUBMODE(innovation_quant)(target, interp_qlpc, bw_lpc1, bw_lpc2, SUBMODE(innovation_params), st->lpcSize, st->subframeSize, innov, syn_resp, bits, stack, st->complexity, SUBMODE(double_codebook)); /* De-normalize innovation and update excitation */ signal_mul(innov, innov, ener, st->subframeSize); for (i=0;isubframeSize;i++) exc[i] = EXTRACT16(SATURATE32(PSHR32(ADD32(SHL32(exc32[i],1),innov[i]),SIG_SHIFT),32767)); /* In some (rare) modes, we do a second search (more bits) to reduce noise even more */ if (SUBMODE(double_codebook)) { char *tmp_stack=stack; VARDECL(spx_sig_t *innov2); ALLOC(innov2, st->subframeSize, spx_sig_t); SPEEX_MEMSET(innov2, 0, st->subframeSize); for (i=0;isubframeSize;i++) target[i]=MULT16_16_P13(QCONST16(2.2f,13), target[i]); SUBMODE(innovation_quant)(target, interp_qlpc, bw_lpc1, bw_lpc2, SUBMODE(innovation_params), st->lpcSize, st->subframeSize, innov2, syn_resp, bits, stack, st->complexity, 0); signal_mul(innov2, innov2, MULT16_32_Q15(QCONST16(0.454545f,15),ener), st->subframeSize); for (i=0;isubframeSize;i++) innov[i] = ADD32(innov[i],innov2[i]); stack = tmp_stack; } for (i=0;isubframeSize;i++) exc[i] = EXTRACT16(SATURATE32(PSHR32(ADD32(SHL32(exc32[i],1),innov[i]),SIG_SHIFT),32767)); if (st->innov_rms_save) { st->innov_rms_save[sub] = compute_rms(innov, st->subframeSize); } } /* Final signal synthesis from excitation */ iir_mem16(exc, interp_qlpc, sw, st->subframeSize, st->lpcSize, st->mem_sp, stack); /* Compute weighted signal again, from synthesized speech (not sure it's the right thing) */ if (st->complexity!=0) filter_mem16(sw, bw_lpc1, bw_lpc2, sw, st->subframeSize, st->lpcSize, st->mem_sw, stack); } /* Store the LSPs for interpolation in the next frame */ if (st->submodeID>=1) { for (i=0;ilpcSize;i++) st->old_lsp[i] = lsp[i]; for (i=0;ilpcSize;i++) st->old_qlsp[i] = qlsp[i]; } #ifdef VORBIS_PSYCHO if (st->submodeID>=1) SPEEX_COPY(st->old_curve, st->curve, 128); #endif if (st->submodeID==1) { #ifndef DISABLE_VBR if (st->dtx_count) speex_bits_pack(bits, 15, 4); else #endif speex_bits_pack(bits, 0, 4); } /* The next frame will not be the first (Duh!) */ st->first = 0; SPEEX_COPY(st->winBuf, in+2*st->frameSize-st->windowSize, st->windowSize-st->frameSize); if (SUBMODE(innovation_quant) == noise_codebook_quant || st->submodeID==0) st->bounded_pitch = 1; else st->bounded_pitch = 0; return 1; } void *nb_decoder_init(const SpeexMode *m) { DecState *st; const SpeexNBMode *mode; int i; mode=(const SpeexNBMode*)m->mode; st = (DecState *)speex_alloc(sizeof(DecState)); if (!st) return NULL; #if defined(VAR_ARRAYS) || defined (USE_ALLOCA) st->stack = NULL; #else st->stack = (char*)speex_alloc_scratch(NB_DEC_STACK); #endif st->mode=m; st->encode_submode = 1; st->first=1; /* Codec parameters, should eventually have several "modes"*/ st->frameSize = mode->frameSize; st->nbSubframes=mode->frameSize/mode->subframeSize; st->subframeSize=mode->subframeSize; st->lpcSize = mode->lpcSize; st->min_pitch=mode->pitchStart; st->max_pitch=mode->pitchEnd; st->submodes=mode->submodes; st->submodeID=mode->defaultSubmode; st->lpc_enh_enabled=1; st->excBuf = (spx_word16_t*)speex_alloc((st->frameSize + 2*st->max_pitch + st->subframeSize + 12)*sizeof(spx_word16_t)); st->exc = st->excBuf + 2*st->max_pitch + st->subframeSize + 6; SPEEX_MEMSET(st->excBuf, 0, st->frameSize + st->max_pitch); st->interp_qlpc = (spx_coef_t*)speex_alloc(st->lpcSize*sizeof(spx_coef_t)); st->old_qlsp = (spx_lsp_t*)speex_alloc(st->lpcSize*sizeof(spx_lsp_t)); st->mem_sp = (spx_mem_t*)speex_alloc(st->lpcSize*sizeof(spx_mem_t)); st->pi_gain = (spx_word32_t*)speex_alloc((st->nbSubframes)*sizeof(spx_word32_t)); st->last_pitch = 40; st->count_lost=0; st->pitch_gain_buf[0] = st->pitch_gain_buf[1] = st->pitch_gain_buf[2] = 0; st->pitch_gain_buf_idx = 0; st->seed = 1000; st->sampling_rate=8000; st->last_ol_gain = 0; st->user_callback.func = &speex_default_user_handler; st->user_callback.data = NULL; for (i=0;i<16;i++) st->speex_callbacks[i].func = NULL; st->voc_m1=st->voc_m2=st->voc_mean=0; st->voc_offset=0; st->dtx_enabled=0; st->isWideband = 0; st->highpass_enabled = 1; #ifdef ENABLE_VALGRIND VALGRIND_MAKE_READABLE(st, NB_DEC_STACK); #endif return st; } void nb_decoder_destroy(void *state) { DecState *st; st=(DecState*)state; #if !(defined(VAR_ARRAYS) || defined (USE_ALLOCA)) speex_free_scratch(st->stack); #endif speex_free (st->excBuf); speex_free (st->interp_qlpc); speex_free (st->old_qlsp); speex_free (st->mem_sp); speex_free (st->pi_gain); speex_free(state); } #define median3(a, b, c) ((a) < (b) ? ((b) < (c) ? (b) : ((a) < (c) ? (c) : (a))) : ((c) < (b) ? (b) : ((c) < (a) ? (c) : (a)))) #ifdef FIXED_POINT const spx_word16_t attenuation[10] = {32767, 31483, 27923, 22861, 17278, 12055, 7764, 4616, 2533, 1283}; #else const spx_word16_t attenuation[10] = {1., 0.961, 0.852, 0.698, 0.527, 0.368, 0.237, 0.141, 0.077, 0.039}; #endif static void nb_decode_lost(DecState *st, spx_word16_t *out, char *stack) { int i; int pitch_val; spx_word16_t pitch_gain; spx_word16_t fact; spx_word16_t gain_med; spx_word16_t innov_gain; spx_word16_t noise_gain; if (st->count_lost<10) fact = attenuation[st->count_lost]; else fact = 0; gain_med = median3(st->pitch_gain_buf[0], st->pitch_gain_buf[1], st->pitch_gain_buf[2]); if (gain_med < st->last_pitch_gain) st->last_pitch_gain = gain_med; #ifdef FIXED_POINT pitch_gain = st->last_pitch_gain; if (pitch_gain>54) pitch_gain = 54; pitch_gain = SHL16(pitch_gain, 9); #else pitch_gain = GAIN_SCALING_1*st->last_pitch_gain; if (pitch_gain>.85) pitch_gain=.85; #endif pitch_gain = MULT16_16_Q15(fact,pitch_gain) + VERY_SMALL; /* FIXME: This was rms of innovation (not exc) */ innov_gain = compute_rms16(st->exc, st->frameSize); noise_gain = MULT16_16_Q15(innov_gain, MULT16_16_Q15(fact, SUB16(Q15ONE,MULT16_16_Q15(pitch_gain,pitch_gain)))); /* Shift all buffers by one frame */ SPEEX_MOVE(st->excBuf, st->excBuf+st->frameSize, 2*st->max_pitch + st->subframeSize + 12); pitch_val = st->last_pitch + SHR32((spx_int32_t)speex_rand(1+st->count_lost, &st->seed),SIG_SHIFT); if (pitch_val > st->max_pitch) pitch_val = st->max_pitch; if (pitch_val < st->min_pitch) pitch_val = st->min_pitch; for (i=0;iframeSize;i++) { st->exc[i]= MULT16_16_Q15(pitch_gain, (st->exc[i-pitch_val]+VERY_SMALL)) + speex_rand(noise_gain, &st->seed); } bw_lpc(QCONST16(.98,15), st->interp_qlpc, st->interp_qlpc, st->lpcSize); iir_mem16(&st->exc[-st->subframeSize], st->interp_qlpc, out, st->frameSize, st->lpcSize, st->mem_sp, stack); highpass(out, out, st->frameSize, HIGHPASS_NARROWBAND|HIGHPASS_OUTPUT, st->mem_hp); st->first = 0; st->count_lost++; st->pitch_gain_buf[st->pitch_gain_buf_idx++] = PSHR16(pitch_gain,9); if (st->pitch_gain_buf_idx > 2) /* rollover */ st->pitch_gain_buf_idx = 0; } /* Just so we don't need to carry the complete wideband mode information */ static const int wb_skip_table[8] = {0, 36, 112, 192, 352, 0, 0, 0}; int nb_decode(void *state, SpeexBits *bits, void *vout) { DecState *st; int i, sub; int pitch; spx_word16_t pitch_gain[3]; spx_word32_t ol_gain=0; int ol_pitch=0; spx_word16_t ol_pitch_coef=0; int best_pitch=40; spx_word16_t best_pitch_gain=0; int wideband; int m; char *stack; VARDECL(spx_sig_t *innov); VARDECL(spx_word32_t *exc32); VARDECL(spx_coef_t *ak); VARDECL(spx_lsp_t *qlsp); spx_word16_t pitch_average=0; spx_word16_t *out = (spx_word16_t*)vout; VARDECL(spx_lsp_t *interp_qlsp); st=(DecState*)state; stack=st->stack; /* Check if we're in DTX mode*/ if (!bits && st->dtx_enabled) { st->submodeID=0; } else { /* If bits is NULL, consider the packet to be lost (what could we do anyway) */ if (!bits) { nb_decode_lost(st, out, stack); return 0; } if (st->encode_submode) { /* Search for next narrowband block (handle requests, skip wideband blocks) */ do { if (speex_bits_remaining(bits)<5) return -1; wideband = speex_bits_unpack_unsigned(bits, 1); if (wideband) /* Skip wideband block (for compatibility) */ { int submode; int advance; advance = submode = speex_bits_unpack_unsigned(bits, SB_SUBMODE_BITS); /*speex_mode_query(&speex_wb_mode, SPEEX_SUBMODE_BITS_PER_FRAME, &advance);*/ advance = wb_skip_table[submode]; if (advance < 0) { speex_notify("Invalid mode encountered. The stream is corrupted."); return -2; } advance -= (SB_SUBMODE_BITS+1); speex_bits_advance(bits, advance); if (speex_bits_remaining(bits)<5) return -1; wideband = speex_bits_unpack_unsigned(bits, 1); if (wideband) { advance = submode = speex_bits_unpack_unsigned(bits, SB_SUBMODE_BITS); /*speex_mode_query(&speex_wb_mode, SPEEX_SUBMODE_BITS_PER_FRAME, &advance);*/ advance = wb_skip_table[submode]; if (advance < 0) { speex_notify("Invalid mode encountered. The stream is corrupted."); return -2; } advance -= (SB_SUBMODE_BITS+1); speex_bits_advance(bits, advance); wideband = speex_bits_unpack_unsigned(bits, 1); if (wideband) { speex_notify("More than two wideband layers found. The stream is corrupted."); return -2; } } } if (speex_bits_remaining(bits)<4) return -1; /* FIXME: Check for overflow */ m = speex_bits_unpack_unsigned(bits, 4); if (m==15) /* We found a terminator */ { return -1; } else if (m==14) /* Speex in-band request */ { int ret = speex_inband_handler(bits, st->speex_callbacks, state); if (ret) return ret; } else if (m==13) /* User in-band request */ { int ret = st->user_callback.func(bits, state, st->user_callback.data); if (ret) return ret; } else if (m>8) /* Invalid mode */ { speex_notify("Invalid mode encountered. The stream is corrupted."); return -2; } } while (m>8); /* Get the sub-mode that was used */ st->submodeID = m; } } /* Shift all buffers by one frame */ SPEEX_MOVE(st->excBuf, st->excBuf+st->frameSize, 2*st->max_pitch + st->subframeSize + 12); /* If null mode (no transmission), just set a couple things to zero*/ if (st->submodes[st->submodeID] == NULL) { VARDECL(spx_coef_t *lpc); ALLOC(lpc, st->lpcSize, spx_coef_t); bw_lpc(QCONST16(0.93f,15), st->interp_qlpc, lpc, st->lpcSize); { spx_word16_t innov_gain=0; /* FIXME: This was innov, not exc */ innov_gain = compute_rms16(st->exc, st->frameSize); for (i=0;iframeSize;i++) st->exc[i]=speex_rand(innov_gain, &st->seed); } st->first=1; /* Final signal synthesis from excitation */ iir_mem16(st->exc, lpc, out, st->frameSize, st->lpcSize, st->mem_sp, stack); st->count_lost=0; return 0; } ALLOC(qlsp, st->lpcSize, spx_lsp_t); /* Unquantize LSPs */ SUBMODE(lsp_unquant)(qlsp, st->lpcSize, bits); /*Damp memory if a frame was lost and the LSP changed too much*/ if (st->count_lost) { spx_word16_t fact; spx_word32_t lsp_dist=0; for (i=0;ilpcSize;i++) lsp_dist = ADD32(lsp_dist, EXTEND32(ABS(st->old_qlsp[i] - qlsp[i]))); #ifdef FIXED_POINT fact = SHR16(19661,SHR32(lsp_dist,LSP_SHIFT+2)); #else fact = .6*exp(-.2*lsp_dist); #endif for (i=0;ilpcSize;i++) st->mem_sp[i] = MULT16_32_Q15(fact,st->mem_sp[i]); } /* Handle first frame and lost-packet case */ if (st->first || st->count_lost) { for (i=0;ilpcSize;i++) st->old_qlsp[i] = qlsp[i]; } /* Get open-loop pitch estimation for low bit-rate pitch coding */ if (SUBMODE(lbr_pitch)!=-1) { ol_pitch = st->min_pitch+speex_bits_unpack_unsigned(bits, 7); } if (SUBMODE(forced_pitch_gain)) { int quant; quant = speex_bits_unpack_unsigned(bits, 4); ol_pitch_coef=MULT16_16_P15(QCONST16(0.066667,15),SHL16(quant,GAIN_SHIFT)); } /* Get global excitation gain */ { int qe; qe = speex_bits_unpack_unsigned(bits, 5); #ifdef FIXED_POINT /* FIXME: Perhaps we could slightly lower the gain here when the output is going to saturate? */ ol_gain = MULT16_32_Q15(28406,ol_gain_table[qe]); #else ol_gain = SIG_SCALING*exp(qe/3.5); #endif } ALLOC(ak, st->lpcSize, spx_coef_t); ALLOC(innov, st->subframeSize, spx_sig_t); ALLOC(exc32, st->subframeSize, spx_word32_t); if (st->submodeID==1) { int extra; extra = speex_bits_unpack_unsigned(bits, 4); if (extra==15) st->dtx_enabled=1; else st->dtx_enabled=0; } if (st->submodeID>1) st->dtx_enabled=0; /*Loop on subframes */ for (sub=0;subnbSubframes;sub++) { int offset; spx_word16_t *exc; spx_word16_t *sp; spx_word16_t *innov_save = NULL; spx_word16_t tmp; /* Offset relative to start of frame */ offset = st->subframeSize*sub; /* Excitation */ exc=st->exc+offset; /* Original signal */ sp=out+offset; if (st->innov_save) innov_save = st->innov_save+offset; /* Reset excitation */ SPEEX_MEMSET(exc, 0, st->subframeSize); /*Adaptive codebook contribution*/ speex_assert (SUBMODE(ltp_unquant)); { int pit_min, pit_max; /* Handle pitch constraints if any */ if (SUBMODE(lbr_pitch) != -1) { int margin; margin = SUBMODE(lbr_pitch); if (margin) { /* GT - need optimization? if (ol_pitch < st->min_pitch+margin-1) ol_pitch=st->min_pitch+margin-1; if (ol_pitch > st->max_pitch-margin) ol_pitch=st->max_pitch-margin; pit_min = ol_pitch-margin+1; pit_max = ol_pitch+margin; */ pit_min = ol_pitch-margin+1; if (pit_min < st->min_pitch) pit_min = st->min_pitch; pit_max = ol_pitch+margin; if (pit_max > st->max_pitch) pit_max = st->max_pitch; } else { pit_min = pit_max = ol_pitch; } } else { pit_min = st->min_pitch; pit_max = st->max_pitch; } SUBMODE(ltp_unquant)(exc, exc32, pit_min, pit_max, ol_pitch_coef, SUBMODE(ltp_params), st->subframeSize, &pitch, &pitch_gain[0], bits, stack, st->count_lost, offset, st->last_pitch_gain, 0); /* Ensuring that things aren't blowing up as would happen if e.g. an encoder is crafting packets to make us produce NaNs and slow down the decoder (vague DoS threat). We can probably be even more aggressive and limit to 15000 or so. */ sanitize_values32(exc32, NEG32(QCONST32(32000,SIG_SHIFT-1)), QCONST32(32000,SIG_SHIFT-1), st->subframeSize); tmp = gain_3tap_to_1tap(pitch_gain); pitch_average += tmp; if ((tmp>best_pitch_gain&&ABS(2*best_pitch-pitch)>=3&&ABS(3*best_pitch-pitch)>=4&&ABS(4*best_pitch-pitch)>=5) || (tmp>MULT16_16_Q15(QCONST16(.6,15),best_pitch_gain)&&(ABS(best_pitch-2*pitch)<3||ABS(best_pitch-3*pitch)<4||ABS(best_pitch-4*pitch)<5)) || (MULT16_16_Q15(QCONST16(.67,15),tmp)>best_pitch_gain&&(ABS(2*best_pitch-pitch)<3||ABS(3*best_pitch-pitch)<4||ABS(4*best_pitch-pitch)<5)) ) { best_pitch = pitch; if (tmp > best_pitch_gain) best_pitch_gain = tmp; } } /* Unquantize the innovation */ { int q_energy; spx_word32_t ener; SPEEX_MEMSET(innov, 0, st->subframeSize); /* Decode sub-frame gain correction */ if (SUBMODE(have_subframe_gain)==3) { q_energy = speex_bits_unpack_unsigned(bits, 3); ener = MULT16_32_Q14(exc_gain_quant_scal3[q_energy],ol_gain); } else if (SUBMODE(have_subframe_gain)==1) { q_energy = speex_bits_unpack_unsigned(bits, 1); ener = MULT16_32_Q14(exc_gain_quant_scal1[q_energy],ol_gain); } else { ener = ol_gain; } speex_assert (SUBMODE(innovation_unquant)); { /*Fixed codebook contribution*/ SUBMODE(innovation_unquant)(innov, SUBMODE(innovation_params), st->subframeSize, bits, stack, &st->seed); /* De-normalize innovation and update excitation */ signal_mul(innov, innov, ener, st->subframeSize); /* Decode second codebook (only for some modes) */ if (SUBMODE(double_codebook)) { char *tmp_stack=stack; VARDECL(spx_sig_t *innov2); ALLOC(innov2, st->subframeSize, spx_sig_t); SPEEX_MEMSET(innov2, 0, st->subframeSize); SUBMODE(innovation_unquant)(innov2, SUBMODE(innovation_params), st->subframeSize, bits, stack, &st->seed); signal_mul(innov2, innov2, MULT16_32_Q15(QCONST16(0.454545f,15),ener), st->subframeSize); for (i=0;isubframeSize;i++) innov[i] = ADD32(innov[i], innov2[i]); stack = tmp_stack; } for (i=0;isubframeSize;i++) exc[i]=EXTRACT16(SATURATE32(PSHR32(ADD32(SHL32(exc32[i],1),innov[i]),SIG_SHIFT),32767)); /*print_vec(exc, 40, "innov");*/ if (innov_save) { for (i=0;isubframeSize;i++) innov_save[i] = EXTRACT16(PSHR32(innov[i], SIG_SHIFT)); } } /*Vocoder mode*/ if (st->submodeID==1) { spx_word16_t g=ol_pitch_coef; g=MULT16_16_P14(QCONST16(1.5f,14),(g-QCONST16(.2f,6))); if (g<0) g=0; if (g>GAIN_SCALING) g=GAIN_SCALING; SPEEX_MEMSET(exc, 0, st->subframeSize); while (st->voc_offsetsubframeSize) { /* exc[st->voc_offset]= g*sqrt(2*ol_pitch)*ol_gain; Not quite sure why we need the factor of two in the sqrt */ if (st->voc_offset>=0) exc[st->voc_offset]=MULT16_16(spx_sqrt(MULT16_16_16(2,ol_pitch)),EXTRACT16(PSHR32(MULT16_16(g,PSHR32(ol_gain,SIG_SHIFT)),6))); st->voc_offset+=ol_pitch; } st->voc_offset -= st->subframeSize; for (i=0;isubframeSize;i++) { spx_word16_t exci=exc[i]; exc[i]= ADD16(ADD16(MULT16_16_Q15(QCONST16(.7f,15),exc[i]) , MULT16_16_Q15(QCONST16(.3f,15),st->voc_m1)), SUB16(MULT16_16_Q15(Q15_ONE-MULT16_16_16(QCONST16(.85f,9),g),EXTRACT16(PSHR32(innov[i],SIG_SHIFT))), MULT16_16_Q15(MULT16_16_16(QCONST16(.15f,9),g),EXTRACT16(PSHR32(st->voc_m2,SIG_SHIFT))) )); st->voc_m1 = exci; st->voc_m2=innov[i]; st->voc_mean = EXTRACT16(PSHR32(ADD32(MULT16_16(QCONST16(.8f,15),st->voc_mean), MULT16_16(QCONST16(.2f,15),exc[i])), 15)); exc[i]-=st->voc_mean; } } } } ALLOC(interp_qlsp, st->lpcSize, spx_lsp_t); if (st->lpc_enh_enabled && SUBMODE(comb_gain)>0 && !st->count_lost) { multicomb(st->exc-st->subframeSize, out, st->interp_qlpc, st->lpcSize, 2*st->subframeSize, best_pitch, 40, SUBMODE(comb_gain), stack); multicomb(st->exc+st->subframeSize, out+2*st->subframeSize, st->interp_qlpc, st->lpcSize, 2*st->subframeSize, best_pitch, 40, SUBMODE(comb_gain), stack); } else { SPEEX_COPY(out, &st->exc[-st->subframeSize], st->frameSize); } /* If the last packet was lost, re-scale the excitation to obtain the same energy as encoded in ol_gain */ if (st->count_lost) { spx_word16_t exc_ener; spx_word32_t gain32; spx_word16_t gain; exc_ener = compute_rms16 (st->exc, st->frameSize); gain32 = PDIV32(ol_gain, ADD16(exc_ener,1)); #ifdef FIXED_POINT if (gain32 > 32767) gain32 = 32767; gain = EXTRACT16(gain32); #else if (gain32 > 2) gain32=2; gain = gain32; #endif for (i=0;iframeSize;i++) { st->exc[i] = MULT16_16_Q14(gain, st->exc[i]); out[i]=st->exc[i-st->subframeSize]; } } /*Loop on subframes */ for (sub=0;subnbSubframes;sub++) { int offset; spx_word16_t *sp; spx_word16_t *exc; /* Offset relative to start of frame */ offset = st->subframeSize*sub; /* Original signal */ sp=out+offset; /* Excitation */ exc=st->exc+offset; /* LSP interpolation (quantized and unquantized) */ lsp_interpolate(st->old_qlsp, qlsp, interp_qlsp, st->lpcSize, sub, st->nbSubframes); /* Make sure the LSP's are stable */ lsp_enforce_margin(interp_qlsp, st->lpcSize, LSP_MARGIN); /* Compute interpolated LPCs (unquantized) */ lsp_to_lpc(interp_qlsp, ak, st->lpcSize, stack); /* Compute analysis filter at w=pi */ { spx_word32_t pi_g=LPC_SCALING; for (i=0;ilpcSize;i+=2) { /*pi_g += -st->interp_qlpc[i] + st->interp_qlpc[i+1];*/ pi_g = ADD32(pi_g, SUB32(EXTEND32(ak[i+1]),EXTEND32(ak[i]))); } st->pi_gain[sub] = pi_g; } iir_mem16(sp, st->interp_qlpc, sp, st->subframeSize, st->lpcSize, st->mem_sp, stack); for (i=0;ilpcSize;i++) st->interp_qlpc[i] = ak[i]; } if (st->highpass_enabled) highpass(out, out, st->frameSize, (st->isWideband?HIGHPASS_WIDEBAND:HIGHPASS_NARROWBAND)|HIGHPASS_OUTPUT, st->mem_hp); /*for (i=0;iframeSize;i++) printf ("%d\n", (int)st->frame[i]);*/ /* Tracking output level */ st->level = 1+PSHR32(ol_gain,SIG_SHIFT); st->max_level = MAX16(MULT16_16_Q15(QCONST16(.99f,15), st->max_level), st->level); st->min_level = MIN16(ADD16(1,MULT16_16_Q14(QCONST16(1.01f,14), st->min_level)), st->level); if (st->max_level < st->min_level+1) st->max_level = st->min_level+1; /*printf ("%f %f %f %d\n", og, st->min_level, st->max_level, update);*/ /* Store the LSPs for interpolation in the next frame */ for (i=0;ilpcSize;i++) st->old_qlsp[i] = qlsp[i]; /* The next frame will not be the first (Duh!) */ st->first = 0; st->count_lost=0; st->last_pitch = best_pitch; #ifdef FIXED_POINT st->last_pitch_gain = PSHR16(pitch_average,2); #else st->last_pitch_gain = .25*pitch_average; #endif st->pitch_gain_buf[st->pitch_gain_buf_idx++] = st->last_pitch_gain; if (st->pitch_gain_buf_idx > 2) /* rollover */ st->pitch_gain_buf_idx = 0; st->last_ol_gain = ol_gain; return 0; } int nb_encoder_ctl(void *state, int request, void *ptr) { EncState *st; st=(EncState*)state; switch(request) { case SPEEX_GET_FRAME_SIZE: (*(spx_int32_t*)ptr) = st->frameSize; break; case SPEEX_SET_LOW_MODE: case SPEEX_SET_MODE: st->submodeSelect = st->submodeID = (*(spx_int32_t*)ptr); break; case SPEEX_GET_LOW_MODE: case SPEEX_GET_MODE: (*(spx_int32_t*)ptr) = st->submodeID; break; #ifndef DISABLE_VBR case SPEEX_SET_VBR: st->vbr_enabled = (*(spx_int32_t*)ptr); break; case SPEEX_GET_VBR: (*(spx_int32_t*)ptr) = st->vbr_enabled; break; case SPEEX_SET_VAD: st->vad_enabled = (*(spx_int32_t*)ptr); break; case SPEEX_GET_VAD: (*(spx_int32_t*)ptr) = st->vad_enabled; break; case SPEEX_SET_DTX: st->dtx_enabled = (*(spx_int32_t*)ptr); break; case SPEEX_GET_DTX: (*(spx_int32_t*)ptr) = st->dtx_enabled; break; case SPEEX_SET_ABR: st->abr_enabled = (*(spx_int32_t*)ptr); st->vbr_enabled = st->abr_enabled!=0; if (st->vbr_enabled) { spx_int32_t i=10; spx_int32_t rate, target; float vbr_qual; target = (*(spx_int32_t*)ptr); while (i>=0) { speex_encoder_ctl(st, SPEEX_SET_QUALITY, &i); speex_encoder_ctl(st, SPEEX_GET_BITRATE, &rate); if (rate <= target) break; i--; } vbr_qual=i; if (vbr_qual<0) vbr_qual=0; speex_encoder_ctl(st, SPEEX_SET_VBR_QUALITY, &vbr_qual); st->abr_count=0; st->abr_drift=0; st->abr_drift2=0; } break; case SPEEX_GET_ABR: (*(spx_int32_t*)ptr) = st->abr_enabled; break; #endif /* #ifndef DISABLE_VBR */ #if !defined(DISABLE_VBR) && !defined(DISABLE_FLOAT_API) case SPEEX_SET_VBR_QUALITY: st->vbr_quality = (*(float*)ptr); break; case SPEEX_GET_VBR_QUALITY: (*(float*)ptr) = st->vbr_quality; break; #endif /* !defined(DISABLE_VBR) && !defined(DISABLE_FLOAT_API) */ case SPEEX_SET_QUALITY: { int quality = (*(spx_int32_t*)ptr); if (quality < 0) quality = 0; if (quality > 10) quality = 10; st->submodeSelect = st->submodeID = ((const SpeexNBMode*)(st->mode->mode))->quality_map[quality]; } break; case SPEEX_SET_COMPLEXITY: st->complexity = (*(spx_int32_t*)ptr); if (st->complexity<0) st->complexity=0; break; case SPEEX_GET_COMPLEXITY: (*(spx_int32_t*)ptr) = st->complexity; break; case SPEEX_SET_BITRATE: { spx_int32_t i=10; spx_int32_t rate, target; target = (*(spx_int32_t*)ptr); while (i>=0) { speex_encoder_ctl(st, SPEEX_SET_QUALITY, &i); speex_encoder_ctl(st, SPEEX_GET_BITRATE, &rate); if (rate <= target) break; i--; } } break; case SPEEX_GET_BITRATE: if (st->submodes[st->submodeID]) (*(spx_int32_t*)ptr) = st->sampling_rate*SUBMODE(bits_per_frame)/st->frameSize; else (*(spx_int32_t*)ptr) = st->sampling_rate*(NB_SUBMODE_BITS+1)/st->frameSize; break; case SPEEX_SET_SAMPLING_RATE: st->sampling_rate = (*(spx_int32_t*)ptr); break; case SPEEX_GET_SAMPLING_RATE: (*(spx_int32_t*)ptr)=st->sampling_rate; break; case SPEEX_RESET_STATE: { int i; st->bounded_pitch = 1; st->first = 1; for (i=0;ilpcSize;i++) st->old_lsp[i]= DIV32(MULT16_16(QCONST16(3.1415927f, LSP_SHIFT), i+1), st->lpcSize+1); for (i=0;ilpcSize;i++) st->mem_sw[i]=st->mem_sw_whole[i]=st->mem_sp[i]=st->mem_exc[i]=0; for (i=0;iframeSize+st->max_pitch+1;i++) st->excBuf[i]=st->swBuf[i]=0; for (i=0;iwindowSize-st->frameSize;i++) st->winBuf[i]=0; } break; case SPEEX_SET_SUBMODE_ENCODING: st->encode_submode = (*(spx_int32_t*)ptr); break; case SPEEX_GET_SUBMODE_ENCODING: (*(spx_int32_t*)ptr) = st->encode_submode; break; case SPEEX_GET_LOOKAHEAD: (*(spx_int32_t*)ptr)=(st->windowSize-st->frameSize); break; case SPEEX_SET_PLC_TUNING: st->plc_tuning = (*(spx_int32_t*)ptr); if (st->plc_tuning>100) st->plc_tuning=100; break; case SPEEX_GET_PLC_TUNING: (*(spx_int32_t*)ptr)=(st->plc_tuning); break; #ifndef DISABLE_VBR case SPEEX_SET_VBR_MAX_BITRATE: st->vbr_max = (*(spx_int32_t*)ptr); break; case SPEEX_GET_VBR_MAX_BITRATE: (*(spx_int32_t*)ptr) = st->vbr_max; break; #endif /* #ifndef DISABLE_VBR */ case SPEEX_SET_HIGHPASS: st->highpass_enabled = (*(spx_int32_t*)ptr); break; case SPEEX_GET_HIGHPASS: (*(spx_int32_t*)ptr) = st->highpass_enabled; break; /* This is all internal stuff past this point */ case SPEEX_GET_PI_GAIN: { int i; spx_word32_t *g = (spx_word32_t*)ptr; for (i=0;inbSubframes;i++) g[i]=st->pi_gain[i]; } break; case SPEEX_GET_EXC: { int i; for (i=0;inbSubframes;i++) ((spx_word16_t*)ptr)[i] = compute_rms16(st->exc+i*st->subframeSize, st->subframeSize); } break; #ifndef DISABLE_VBR case SPEEX_GET_RELATIVE_QUALITY: (*(float*)ptr)=st->relative_quality; break; #endif /* #ifndef DISABLE_VBR */ case SPEEX_SET_INNOVATION_SAVE: st->innov_rms_save = (spx_word16_t*)ptr; break; case SPEEX_SET_WIDEBAND: st->isWideband = *((spx_int32_t*)ptr); break; case SPEEX_GET_STACK: *((char**)ptr) = st->stack; break; default: speex_warning_int("Unknown nb_ctl request: ", request); return -1; } return 0; } int nb_decoder_ctl(void *state, int request, void *ptr) { DecState *st; st=(DecState*)state; switch(request) { case SPEEX_SET_LOW_MODE: case SPEEX_SET_MODE: st->submodeID = (*(spx_int32_t*)ptr); break; case SPEEX_GET_LOW_MODE: case SPEEX_GET_MODE: (*(spx_int32_t*)ptr) = st->submodeID; break; case SPEEX_SET_ENH: st->lpc_enh_enabled = *((spx_int32_t*)ptr); break; case SPEEX_GET_ENH: *((spx_int32_t*)ptr) = st->lpc_enh_enabled; break; case SPEEX_GET_FRAME_SIZE: (*(spx_int32_t*)ptr) = st->frameSize; break; case SPEEX_GET_BITRATE: if (st->submodes[st->submodeID]) (*(spx_int32_t*)ptr) = st->sampling_rate*SUBMODE(bits_per_frame)/st->frameSize; else (*(spx_int32_t*)ptr) = st->sampling_rate*(NB_SUBMODE_BITS+1)/st->frameSize; break; case SPEEX_SET_SAMPLING_RATE: st->sampling_rate = (*(spx_int32_t*)ptr); break; case SPEEX_GET_SAMPLING_RATE: (*(spx_int32_t*)ptr)=st->sampling_rate; break; case SPEEX_SET_HANDLER: { SpeexCallback *c = (SpeexCallback*)ptr; st->speex_callbacks[c->callback_id].func=c->func; st->speex_callbacks[c->callback_id].data=c->data; st->speex_callbacks[c->callback_id].callback_id=c->callback_id; } break; case SPEEX_SET_USER_HANDLER: { SpeexCallback *c = (SpeexCallback*)ptr; st->user_callback.func=c->func; st->user_callback.data=c->data; st->user_callback.callback_id=c->callback_id; } break; case SPEEX_RESET_STATE: { int i; for (i=0;ilpcSize;i++) st->mem_sp[i]=0; for (i=0;iframeSize + st->max_pitch + 1;i++) st->excBuf[i]=0; } break; case SPEEX_SET_SUBMODE_ENCODING: st->encode_submode = (*(spx_int32_t*)ptr); break; case SPEEX_GET_SUBMODE_ENCODING: (*(spx_int32_t*)ptr) = st->encode_submode; break; case SPEEX_GET_LOOKAHEAD: (*(spx_int32_t*)ptr)=st->subframeSize; break; case SPEEX_SET_HIGHPASS: st->highpass_enabled = (*(spx_int32_t*)ptr); break; case SPEEX_GET_HIGHPASS: (*(spx_int32_t*)ptr) = st->highpass_enabled; break; /* FIXME: Convert to fixed-point and re-enable even when float API is disabled */ #ifndef DISABLE_FLOAT_API case SPEEX_GET_ACTIVITY: { float ret; ret = log(st->level/st->min_level)/log(st->max_level/st->min_level); if (ret>1) ret = 1; /* Done in a strange way to catch NaNs as well */ if (!(ret > 0)) ret = 0; /*printf ("%f %f %f %f\n", st->level, st->min_level, st->max_level, ret);*/ (*(spx_int32_t*)ptr) = (int)(100*ret); } break; #endif case SPEEX_GET_PI_GAIN: { int i; spx_word32_t *g = (spx_word32_t*)ptr; for (i=0;inbSubframes;i++) g[i]=st->pi_gain[i]; } break; case SPEEX_GET_EXC: { int i; for (i=0;inbSubframes;i++) ((spx_word16_t*)ptr)[i] = compute_rms16(st->exc+i*st->subframeSize, st->subframeSize); } break; case SPEEX_GET_DTX_STATUS: *((spx_int32_t*)ptr) = st->dtx_enabled; break; case SPEEX_SET_INNOVATION_SAVE: st->innov_save = (spx_word16_t*)ptr; break; case SPEEX_SET_WIDEBAND: st->isWideband = *((spx_int32_t*)ptr); break; case SPEEX_GET_STACK: *((char**)ptr) = st->stack; break; default: speex_warning_int("Unknown nb_ctl request: ", request); return -1; } return 0; } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/nb_celp.h ================================================ /* Copyright (C) 2002-2006 Jean-Marc Valin */ /** @file nb_celp.h @brief Narrowband CELP encoder/decoder */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef NB_CELP_H #define NB_CELP_H #include "modes.h" #include #include #include "vbr.h" #include "filters.h" #ifdef VORBIS_PSYCHO #include "vorbis_psy.h" #endif /**Structure representing the full state of the narrowband encoder*/ typedef struct EncState { const SpeexMode *mode; /**< Mode corresponding to the state */ int first; /**< Is this the first frame? */ int frameSize; /**< Size of frames */ int subframeSize; /**< Size of sub-frames */ int nbSubframes; /**< Number of sub-frames */ int windowSize; /**< Analysis (LPC) window length */ int lpcSize; /**< LPC order */ int min_pitch; /**< Minimum pitch value allowed */ int max_pitch; /**< Maximum pitch value allowed */ spx_word32_t cumul_gain; /**< Product of previously used pitch gains (Q10) */ int bounded_pitch; /**< Next frame should not rely on previous frames for pitch */ int ol_pitch; /**< Open-loop pitch */ int ol_voiced; /**< Open-loop voiced/non-voiced decision */ int *pitch; #ifdef VORBIS_PSYCHO VorbisPsy *psy; float *psy_window; float *curve; float *old_curve; #endif spx_word16_t gamma1; /**< Perceptual filter: A(z/gamma1) */ spx_word16_t gamma2; /**< Perceptual filter: A(z/gamma2) */ spx_word16_t lpc_floor; /**< Noise floor multiplier for A[0] in LPC analysis*/ char *stack; /**< Pseudo-stack allocation for temporary memory */ spx_word16_t *winBuf; /**< Input buffer (original signal) */ spx_word16_t *excBuf; /**< Excitation buffer */ spx_word16_t *exc; /**< Start of excitation frame */ spx_word16_t *swBuf; /**< Weighted signal buffer */ spx_word16_t *sw; /**< Start of weighted signal frame */ const spx_word16_t *window; /**< Temporary (Hanning) window */ const spx_word16_t *lagWindow; /**< Window applied to auto-correlation */ spx_lsp_t *old_lsp; /**< LSPs for previous frame */ spx_lsp_t *old_qlsp; /**< Quantized LSPs for previous frame */ spx_mem_t *mem_sp; /**< Filter memory for signal synthesis */ spx_mem_t *mem_sw; /**< Filter memory for perceptually-weighted signal */ spx_mem_t *mem_sw_whole; /**< Filter memory for perceptually-weighted signal (whole frame)*/ spx_mem_t *mem_exc; /**< Filter memory for excitation (whole frame) */ spx_mem_t *mem_exc2; /**< Filter memory for excitation (whole frame) */ spx_mem_t mem_hp[2]; /**< High-pass filter memory */ spx_word32_t *pi_gain; /**< Gain of LPC filter at theta=pi (fe/2) */ spx_word16_t *innov_rms_save; /**< If non-NULL, innovation RMS is copied here */ #ifndef DISABLE_VBR VBRState *vbr; /**< State of the VBR data */ float vbr_quality; /**< Quality setting for VBR encoding */ float relative_quality; /**< Relative quality that will be needed by VBR */ spx_int32_t vbr_enabled; /**< 1 for enabling VBR, 0 otherwise */ spx_int32_t vbr_max; /**< Max bit-rate allowed in VBR mode */ int vad_enabled; /**< 1 for enabling VAD, 0 otherwise */ int dtx_enabled; /**< 1 for enabling DTX, 0 otherwise */ int dtx_count; /**< Number of consecutive DTX frames */ spx_int32_t abr_enabled; /**< ABR setting (in bps), 0 if off */ float abr_drift; float abr_drift2; float abr_count; #endif /* #ifndef DISABLE_VBR */ int complexity; /**< Complexity setting (0-10 from least complex to most complex) */ spx_int32_t sampling_rate; int plc_tuning; int encode_submode; const SpeexSubmode * const *submodes; /**< Sub-mode data */ int submodeID; /**< Activated sub-mode */ int submodeSelect; /**< Mode chosen by the user (may differ from submodeID if VAD is on) */ int isWideband; /**< Is this used as part of the embedded wideband codec */ int highpass_enabled; /**< Is the input filter enabled */ } EncState; /**Structure representing the full state of the narrowband decoder*/ typedef struct DecState { const SpeexMode *mode; /**< Mode corresponding to the state */ int first; /**< Is this the first frame? */ int count_lost; /**< Was the last frame lost? */ int frameSize; /**< Size of frames */ int subframeSize; /**< Size of sub-frames */ int nbSubframes; /**< Number of sub-frames */ int lpcSize; /**< LPC order */ int min_pitch; /**< Minimum pitch value allowed */ int max_pitch; /**< Maximum pitch value allowed */ spx_int32_t sampling_rate; spx_word16_t last_ol_gain; /**< Open-loop gain for previous frame */ char *stack; /**< Pseudo-stack allocation for temporary memory */ spx_word16_t *excBuf; /**< Excitation buffer */ spx_word16_t *exc; /**< Start of excitation frame */ spx_lsp_t *old_qlsp; /**< Quantized LSPs for previous frame */ spx_coef_t *interp_qlpc; /**< Interpolated quantized LPCs */ spx_mem_t *mem_sp; /**< Filter memory for synthesis signal */ spx_mem_t mem_hp[2]; /**< High-pass filter memory */ spx_word32_t *pi_gain; /**< Gain of LPC filter at theta=pi (fe/2) */ spx_word16_t *innov_save; /** If non-NULL, innovation is copied here */ spx_word16_t level; spx_word16_t max_level; spx_word16_t min_level; /* This is used in packet loss concealment */ int last_pitch; /**< Pitch of last correctly decoded frame */ spx_word16_t last_pitch_gain; /**< Pitch gain of last correctly decoded frame */ spx_word16_t pitch_gain_buf[3]; /**< Pitch gain of last decoded frames */ int pitch_gain_buf_idx; /**< Tail of the buffer */ spx_int32_t seed; /** Seed used for random number generation */ int encode_submode; const SpeexSubmode * const *submodes; /**< Sub-mode data */ int submodeID; /**< Activated sub-mode */ int lpc_enh_enabled; /**< 1 when LPC enhancer is on, 0 otherwise */ SpeexCallback speex_callbacks[SPEEX_MAX_CALLBACKS]; SpeexCallback user_callback; /*Vocoder data*/ spx_word16_t voc_m1; spx_word32_t voc_m2; spx_word16_t voc_mean; int voc_offset; int dtx_enabled; int isWideband; /**< Is this used as part of the embedded wideband codec */ int highpass_enabled; /**< Is the input filter enabled */ } DecState; /** Initializes encoder state*/ void *nb_encoder_init(const SpeexMode *m); /** De-allocates encoder state resources*/ void nb_encoder_destroy(void *state); /** Encodes one frame*/ int nb_encode(void *state, void *in, SpeexBits *bits); /** Initializes decoder state*/ void *nb_decoder_init(const SpeexMode *m); /** De-allocates decoder state resources*/ void nb_decoder_destroy(void *state); /** Decodes one frame*/ int nb_decode(void *state, SpeexBits *bits, void *out); /** ioctl-like function for controlling a narrowband encoder */ int nb_encoder_ctl(void *state, int request, void *ptr); /** ioctl-like function for controlling a narrowband decoder */ int nb_decoder_ctl(void *state, int request, void *ptr); #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/os_support.h ================================================ /* Copyright (C) 2007 Jean-Marc Valin File: os_support.h This is the (tiny) OS abstraction layer. Aside from math.h, this is the only place where system headers are allowed. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef OS_SUPPORT_H #define OS_SUPPORT_H #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef OS_SUPPORT_CUSTOM #include "os_support_custom.h" #endif /** Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, speex_realloc and speex_free NOTE: speex_alloc needs to CLEAR THE MEMORY */ #ifndef OVERRIDE_SPEEX_ALLOC static inline void *speex_alloc (int size) { /* WARNING: this is not equivalent to malloc(). If you want to use malloc() or your own allocator, YOU NEED TO CLEAR THE MEMORY ALLOCATED. Otherwise you will experience strange bugs */ return calloc(size,1); } #endif /** Same as speex_alloc, except that the area is only needed inside a Speex call (might cause problem with wideband though) */ #ifndef OVERRIDE_SPEEX_ALLOC_SCRATCH static inline void *speex_alloc_scratch (int size) { /* Scratch space doesn't need to be cleared */ return calloc(size,1); } #endif /** Speex wrapper for realloc. To do your own dynamic allocation, all you need to do is replace this function, speex_alloc and speex_free */ #ifndef OVERRIDE_SPEEX_REALLOC static inline void *speex_realloc (void *ptr, int size) { return realloc(ptr, size); } #endif /** Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, speex_realloc and speex_alloc */ #ifndef OVERRIDE_SPEEX_FREE static inline void speex_free (void *ptr) { free(ptr); } #endif /** Same as speex_free, except that the area is only needed inside a Speex call (might cause problem with wideband though) */ #ifndef OVERRIDE_SPEEX_FREE_SCRATCH static inline void speex_free_scratch (void *ptr) { free(ptr); } #endif /** Copy n bytes of memory from src to dst. The 0* term provides compile-time type checking */ #ifndef OVERRIDE_SPEEX_COPY #define SPEEX_COPY(dst, src, n) (memcpy((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) )) #endif /** Copy n bytes of memory from src to dst, allowing overlapping regions. The 0* term provides compile-time type checking */ #ifndef OVERRIDE_SPEEX_MOVE #define SPEEX_MOVE(dst, src, n) (memmove((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) )) #endif /** Set n bytes of memory to value of c, starting at address s */ #ifndef OVERRIDE_SPEEX_MEMSET #define SPEEX_MEMSET(dst, c, n) (memset((dst), (c), (n)*sizeof(*(dst)))) #endif #ifndef OVERRIDE_SPEEX_FATAL static inline void _speex_fatal(const char *str, const char *file, int line) { fprintf (stderr, "Fatal (internal) error in %s, line %d: %s\n", file, line, str); exit(1); } #endif #ifndef OVERRIDE_SPEEX_WARNING static inline void speex_warning(const char *str) { #ifndef DISABLE_WARNINGS fprintf (stderr, "warning: %s\n", str); #endif } #endif #ifndef OVERRIDE_SPEEX_WARNING_INT static inline void speex_warning_int(const char *str, int val) { #ifndef DISABLE_WARNINGS fprintf (stderr, "warning: %s %d\n", str, val); #endif } #endif #ifndef OVERRIDE_SPEEX_NOTIFY static inline void speex_notify(const char *str) { #ifndef DISABLE_NOTIFICATIONS fprintf (stderr, "notification: %s\n", str); #endif } #endif #ifndef OVERRIDE_SPEEX_PUTC /** Speex wrapper for putc */ static inline void _speex_putc(int ch, void *file) { FILE *f = (FILE *)file; fprintf(f, "%c", ch); } #endif #define speex_fatal(str) _speex_fatal(str, __FILE__, __LINE__); #define speex_assert(cond) {if (!(cond)) {speex_fatal("assertion failed: " #cond);}} #ifndef RELEASE static inline void print_vec(float *vec, int len, char *name) { int i; printf ("%s ", name); for (i=0;i #include "speex/speex_preprocess.h" #include "speex/speex_echo.h" #include "arch.h" #include "fftwrap.h" #include "filterbank.h" #include "math_approx.h" #include "os_support.h" #ifndef M_PI #define M_PI 3.14159263 #endif #define LOUDNESS_EXP 5.f #define AMP_SCALE .001f #define AMP_SCALE_1 1000.f #define NB_BANDS 24 #define SPEECH_PROB_START_DEFAULT QCONST16(0.35f,15) #define SPEECH_PROB_CONTINUE_DEFAULT QCONST16(0.20f,15) #define NOISE_SUPPRESS_DEFAULT -15 #define ECHO_SUPPRESS_DEFAULT -40 #define ECHO_SUPPRESS_ACTIVE_DEFAULT -15 #ifndef NULL #define NULL 0 #endif #define SQR(x) ((x)*(x)) #define SQR16(x) (MULT16_16((x),(x))) #define SQR16_Q15(x) (MULT16_16_Q15((x),(x))) #ifdef FIXED_POINT static inline spx_word16_t DIV32_16_Q8(spx_word32_t a, spx_word32_t b) { if (SHR32(a,7) >= b) { return 32767; } else { if (b>=QCONST32(1,23)) { a = SHR32(a,8); b = SHR32(b,8); } if (b>=QCONST32(1,19)) { a = SHR32(a,4); b = SHR32(b,4); } if (b>=QCONST32(1,15)) { a = SHR32(a,4); b = SHR32(b,4); } a = SHL32(a,8); return PDIV32_16(a,b); } } static inline spx_word16_t DIV32_16_Q15(spx_word32_t a, spx_word32_t b) { if (SHR32(a,15) >= b) { return 32767; } else { if (b>=QCONST32(1,23)) { a = SHR32(a,8); b = SHR32(b,8); } if (b>=QCONST32(1,19)) { a = SHR32(a,4); b = SHR32(b,4); } if (b>=QCONST32(1,15)) { a = SHR32(a,4); b = SHR32(b,4); } a = SHL32(a,15)-a; return DIV32_16(a,b); } } #define SNR_SCALING 256.f #define SNR_SCALING_1 0.0039062f #define SNR_SHIFT 8 #define FRAC_SCALING 32767.f #define FRAC_SCALING_1 3.0518e-05 #define FRAC_SHIFT 1 #define EXPIN_SCALING 2048.f #define EXPIN_SCALING_1 0.00048828f #define EXPIN_SHIFT 11 #define EXPOUT_SCALING_1 1.5259e-05 #define NOISE_SHIFT 7 #else #define DIV32_16_Q8(a,b) ((a)/(b)) #define DIV32_16_Q15(a,b) ((a)/(b)) #define SNR_SCALING 1.f #define SNR_SCALING_1 1.f #define SNR_SHIFT 0 #define FRAC_SCALING 1.f #define FRAC_SCALING_1 1.f #define FRAC_SHIFT 0 #define NOISE_SHIFT 0 #define EXPIN_SCALING 1.f #define EXPIN_SCALING_1 1.f #define EXPOUT_SCALING_1 1.f #endif /** Speex pre-processor state. */ struct SpeexPreprocessState_ { /* Basic info */ int frame_size; /**< Number of samples processed each time */ int ps_size; /**< Number of points in the power spectrum */ int sampling_rate; /**< Sampling rate of the input/output */ int nbands; FilterBank *bank; /* Parameters */ int denoise_enabled; int vad_enabled; int dereverb_enabled; spx_word16_t reverb_decay; spx_word16_t reverb_level; spx_word16_t speech_prob_start; spx_word16_t speech_prob_continue; int noise_suppress; int echo_suppress; int echo_suppress_active; SpeexEchoState *echo_state; spx_word16_t speech_prob; /**< Probability last frame was speech */ /* DSP-related arrays */ spx_word16_t *frame; /**< Processing frame (2*ps_size) */ spx_word16_t *ft; /**< Processing frame in freq domain (2*ps_size) */ spx_word32_t *ps; /**< Current power spectrum */ spx_word16_t *gain2; /**< Adjusted gains */ spx_word16_t *gain_floor; /**< Minimum gain allowed */ spx_word16_t *window; /**< Analysis/Synthesis window */ spx_word32_t *noise; /**< Noise estimate */ spx_word32_t *reverb_estimate; /**< Estimate of reverb energy */ spx_word32_t *old_ps; /**< Power spectrum for last frame */ spx_word16_t *gain; /**< Ephraim Malah gain */ spx_word16_t *prior; /**< A-priori SNR */ spx_word16_t *post; /**< A-posteriori SNR */ spx_word32_t *S; /**< Smoothed power spectrum */ spx_word32_t *Smin; /**< See Cohen paper */ spx_word32_t *Stmp; /**< See Cohen paper */ int *update_prob; /**< Probability of speech presence for noise update */ spx_word16_t *zeta; /**< Smoothed a priori SNR */ spx_word32_t *echo_noise; spx_word32_t *residual_echo; /* Misc */ spx_word16_t *inbuf; /**< Input buffer (overlapped analysis) */ spx_word16_t *outbuf; /**< Output buffer (for overlap and add) */ /* AGC stuff, only for floating point for now */ #ifndef FIXED_POINT int agc_enabled; float agc_level; float loudness_accum; float *loudness_weight; /**< Perceptual loudness curve */ float loudness; /**< Loudness estimate */ float agc_gain; /**< Current AGC gain */ float max_gain; /**< Maximum gain allowed */ float max_increase_step; /**< Maximum increase in gain from one frame to another */ float max_decrease_step; /**< Maximum decrease in gain from one frame to another */ float prev_loudness; /**< Loudness of previous frame */ float init_max; /**< Current gain limit during initialisation */ #endif int nb_adapt; /**< Number of frames used for adaptation so far */ int was_speech; int min_count; /**< Number of frames processed so far */ void *fft_lookup; /**< Lookup table for the FFT */ #ifdef FIXED_POINT int frame_shift; #endif }; static void conj_window(spx_word16_t *w, int len) { int i; for (i=0;i19) return ADD32(EXTEND32(Q15_ONE),EXTEND32(DIV32_16(QCONST32(.1296,23), SHR32(xx,EXPIN_SHIFT-SNR_SHIFT)))); frac = SHL32(xx-SHL32(ind,10),5); return SHL32(DIV32_16(PSHR32(MULT16_16(Q15_ONE-frac,table[ind]) + MULT16_16(frac,table[ind+1]),7),(spx_sqrt(SHL32(xx,15)+6711))),7); } static inline spx_word16_t qcurve(spx_word16_t x) { x = MAX16(x, 1); return DIV32_16(SHL32(EXTEND32(32767),9),ADD16(512,MULT16_16_Q15(QCONST16(.60f,15),DIV32_16(32767,x)))); } /* Compute the gain floor based on different floors for the background noise and residual echo */ static void compute_gain_floor(int noise_suppress, int effective_echo_suppress, spx_word32_t *noise, spx_word32_t *echo, spx_word16_t *gain_floor, int len) { int i; if (noise_suppress > effective_echo_suppress) { spx_word16_t noise_gain, gain_ratio; noise_gain = EXTRACT16(MIN32(Q15_ONE,SHR32(spx_exp(MULT16_16(QCONST16(0.11513,11),noise_suppress)),1))); gain_ratio = EXTRACT16(MIN32(Q15_ONE,SHR32(spx_exp(MULT16_16(QCONST16(.2302585f,11),effective_echo_suppress-noise_suppress)),1))); /* gain_floor = sqrt [ (noise*noise_floor + echo*echo_floor) / (noise+echo) ] */ for (i=0;i19) return FRAC_SCALING*(1+.1296/x); frac = 2*x-integer; return FRAC_SCALING*((1-frac)*table[ind] + frac*table[ind+1])/sqrt(x+.0001f); } static inline spx_word16_t qcurve(spx_word16_t x) { return 1.f/(1.f+.15f/(SNR_SCALING_1*x)); } static void compute_gain_floor(int noise_suppress, int effective_echo_suppress, spx_word32_t *noise, spx_word32_t *echo, spx_word16_t *gain_floor, int len) { int i; float echo_floor; float noise_floor; noise_floor = exp(.2302585f*noise_suppress); echo_floor = exp(.2302585f*effective_echo_suppress); /* Compute the gain floor based on different floors for the background noise and residual echo */ for (i=0;iframe_size = frame_size; /* Round ps_size down to the nearest power of two */ #if 0 i=1; st->ps_size = st->frame_size; while(1) { if (st->ps_size & ~i) { st->ps_size &= ~i; i<<=1; } else { break; } } if (st->ps_size < 3*st->frame_size/4) st->ps_size = st->ps_size * 3 / 2; #else st->ps_size = st->frame_size; #endif N = st->ps_size; N3 = 2*N - st->frame_size; N4 = st->frame_size - N3; st->sampling_rate = sampling_rate; st->denoise_enabled = 1; st->vad_enabled = 0; st->dereverb_enabled = 0; st->reverb_decay = 0; st->reverb_level = 0; st->noise_suppress = NOISE_SUPPRESS_DEFAULT; st->echo_suppress = ECHO_SUPPRESS_DEFAULT; st->echo_suppress_active = ECHO_SUPPRESS_ACTIVE_DEFAULT; st->speech_prob_start = SPEECH_PROB_START_DEFAULT; st->speech_prob_continue = SPEECH_PROB_CONTINUE_DEFAULT; st->echo_state = NULL; st->nbands = NB_BANDS; M = st->nbands; st->bank = filterbank_new(M, sampling_rate, N, 1); st->frame = (spx_word16_t*)speex_alloc(2*N*sizeof(spx_word16_t)); st->window = (spx_word16_t*)speex_alloc(2*N*sizeof(spx_word16_t)); st->ft = (spx_word16_t*)speex_alloc(2*N*sizeof(spx_word16_t)); st->ps = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t)); st->noise = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t)); st->echo_noise = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t)); st->residual_echo = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t)); st->reverb_estimate = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t)); st->old_ps = (spx_word32_t*)speex_alloc((N+M)*sizeof(spx_word32_t)); st->prior = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t)); st->post = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t)); st->gain = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t)); st->gain2 = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t)); st->gain_floor = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t)); st->zeta = (spx_word16_t*)speex_alloc((N+M)*sizeof(spx_word16_t)); st->S = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t)); st->Smin = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t)); st->Stmp = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t)); st->update_prob = (int*)speex_alloc(N*sizeof(int)); st->inbuf = (spx_word16_t*)speex_alloc(N3*sizeof(spx_word16_t)); st->outbuf = (spx_word16_t*)speex_alloc(N3*sizeof(spx_word16_t)); conj_window(st->window, 2*N3); for (i=2*N3;i<2*st->ps_size;i++) st->window[i]=Q15_ONE; if (N4>0) { for (i=N3-1;i>=0;i--) { st->window[i+N3+N4]=st->window[i+N3]; st->window[i+N3]=1; } } for (i=0;inoise[i]=QCONST32(1.f,NOISE_SHIFT); st->reverb_estimate[i]=0; st->old_ps[i]=1; st->gain[i]=Q15_ONE; st->post[i]=SHL16(1, SNR_SHIFT); st->prior[i]=SHL16(1, SNR_SHIFT); } for (i=0;iupdate_prob[i] = 1; for (i=0;iinbuf[i]=0; st->outbuf[i]=0; } #ifndef FIXED_POINT st->agc_enabled = 0; st->agc_level = 8000; st->loudness_weight = (float*)speex_alloc(N*sizeof(float)); for (i=0;iloudness_weight[i] = .5f*(1.f/(1.f+ff/8000.f))+1.f*exp(-.5f*(ff-3800.f)*(ff-3800.f)/9e5f);*/ st->loudness_weight[i] = .35f-.35f*ff/16000.f+.73f*exp(-.5f*(ff-3800)*(ff-3800)/9e5f); if (st->loudness_weight[i]<.01f) st->loudness_weight[i]=.01f; st->loudness_weight[i] *= st->loudness_weight[i]; } /*st->loudness = pow(AMP_SCALE*st->agc_level,LOUDNESS_EXP);*/ st->loudness = 1e-15; st->agc_gain = 1; st->max_gain = 30; st->max_increase_step = exp(0.11513f * 12.*st->frame_size / st->sampling_rate); st->max_decrease_step = exp(-0.11513f * 40.*st->frame_size / st->sampling_rate); st->prev_loudness = 1; st->init_max = 1; #endif st->was_speech = 0; st->fft_lookup = spx_fft_init(2*N); st->nb_adapt=0; st->min_count=0; return st; } EXPORT void speex_preprocess_state_destroy(SpeexPreprocessState *st) { speex_free(st->frame); speex_free(st->ft); speex_free(st->ps); speex_free(st->gain2); speex_free(st->gain_floor); speex_free(st->window); speex_free(st->noise); speex_free(st->reverb_estimate); speex_free(st->old_ps); speex_free(st->gain); speex_free(st->prior); speex_free(st->post); #ifndef FIXED_POINT speex_free(st->loudness_weight); #endif speex_free(st->echo_noise); speex_free(st->residual_echo); speex_free(st->S); speex_free(st->Smin); speex_free(st->Stmp); speex_free(st->update_prob); speex_free(st->zeta); speex_free(st->inbuf); speex_free(st->outbuf); spx_fft_destroy(st->fft_lookup); filterbank_destroy(st->bank); speex_free(st); } /* FIXME: The AGC doesn't work yet with fixed-point*/ #ifndef FIXED_POINT static void speex_compute_agc(SpeexPreprocessState *st, spx_word16_t Pframe, spx_word16_t *ft) { int i; int N = st->ps_size; float target_gain; float loudness=1.f; float rate; for (i=2;ips[i]* st->loudness_weight[i]; } loudness=sqrt(loudness); /*if (loudness < 2*pow(st->loudness, 1.0/LOUDNESS_EXP) && loudness*2 > pow(st->loudness, 1.0/LOUDNESS_EXP))*/ if (Pframe>.3f) { /*rate=2.0f*Pframe*Pframe/(1+st->nb_loudness_adapt);*/ rate = .03*Pframe*Pframe; st->loudness = (1-rate)*st->loudness + (rate)*pow(AMP_SCALE*loudness, LOUDNESS_EXP); st->loudness_accum = (1-rate)*st->loudness_accum + rate; if (st->init_max < st->max_gain && st->nb_adapt > 20) st->init_max *= 1.f + .1f*Pframe*Pframe; } /*printf ("%f %f %f %f\n", Pframe, loudness, pow(st->loudness, 1.0f/LOUDNESS_EXP), st->loudness2);*/ target_gain = AMP_SCALE*st->agc_level*pow(st->loudness/(1e-4+st->loudness_accum), -1.0f/LOUDNESS_EXP); if ((Pframe>.5 && st->nb_adapt > 20) || target_gain < st->agc_gain) { if (target_gain > st->max_increase_step*st->agc_gain) target_gain = st->max_increase_step*st->agc_gain; if (target_gain < st->max_decrease_step*st->agc_gain && loudness < 10*st->prev_loudness) target_gain = st->max_decrease_step*st->agc_gain; if (target_gain > st->max_gain) target_gain = st->max_gain; if (target_gain > st->init_max) target_gain = st->init_max; st->agc_gain = target_gain; } /*fprintf (stderr, "%f %f %f\n", loudness, (float)AMP_SCALE_1*pow(st->loudness, 1.0f/LOUDNESS_EXP), st->agc_gain);*/ for (i=0;i<2*N;i++) ft[i] *= st->agc_gain; st->prev_loudness = loudness; } #endif static void preprocess_analysis(SpeexPreprocessState *st, spx_int16_t *x) { int i; int N = st->ps_size; int N3 = 2*N - st->frame_size; int N4 = st->frame_size - N3; spx_word32_t *ps=st->ps; /* 'Build' input frame */ for (i=0;iframe[i]=st->inbuf[i]; for (i=0;iframe_size;i++) st->frame[N3+i]=x[i]; /* Update inbuf */ for (i=0;iinbuf[i]=x[N4+i]; /* Windowing */ for (i=0;i<2*N;i++) st->frame[i] = MULT16_16_Q15(st->frame[i], st->window[i]); #ifdef FIXED_POINT { spx_word16_t max_val=0; for (i=0;i<2*N;i++) max_val = MAX16(max_val, ABS16(st->frame[i])); st->frame_shift = 14-spx_ilog2(EXTEND32(max_val)); for (i=0;i<2*N;i++) st->frame[i] = SHL16(st->frame[i], st->frame_shift); } #endif /* Perform FFT */ spx_fft(st->fft_lookup, st->frame, st->ft); /* Power spectrum */ ps[0]=MULT16_16(st->ft[0],st->ft[0]); for (i=1;ift[2*i-1],st->ft[2*i-1]) + MULT16_16(st->ft[2*i],st->ft[2*i]); for (i=0;ips[i] = PSHR32(st->ps[i], 2*st->frame_shift); filterbank_compute_bank32(st->bank, ps, ps+N); } static void update_noise_prob(SpeexPreprocessState *st) { int i; int min_range; int N = st->ps_size; for (i=1;iS[i] = MULT16_32_Q15(QCONST16(.8f,15),st->S[i]) + MULT16_32_Q15(QCONST16(.05f,15),st->ps[i-1]) + MULT16_32_Q15(QCONST16(.1f,15),st->ps[i]) + MULT16_32_Q15(QCONST16(.05f,15),st->ps[i+1]); st->S[0] = MULT16_32_Q15(QCONST16(.8f,15),st->S[0]) + MULT16_32_Q15(QCONST16(.2f,15),st->ps[0]); st->S[N-1] = MULT16_32_Q15(QCONST16(.8f,15),st->S[N-1]) + MULT16_32_Q15(QCONST16(.2f,15),st->ps[N-1]); if (st->nb_adapt==1) { for (i=0;iSmin[i] = st->Stmp[i] = 0; } if (st->nb_adapt < 100) min_range = 15; else if (st->nb_adapt < 1000) min_range = 50; else if (st->nb_adapt < 10000) min_range = 150; else min_range = 300; if (st->min_count > min_range) { st->min_count = 0; for (i=0;iSmin[i] = MIN32(st->Stmp[i], st->S[i]); st->Stmp[i] = st->S[i]; } } else { for (i=0;iSmin[i] = MIN32(st->Smin[i], st->S[i]); st->Stmp[i] = MIN32(st->Stmp[i], st->S[i]); } } for (i=0;iS[i]) > st->Smin[i]) st->update_prob[i] = 1; else st->update_prob[i] = 0; /*fprintf (stderr, "%f ", st->S[i]/st->Smin[i]);*/ /*fprintf (stderr, "%f ", st->update_prob[i]);*/ } } #define NOISE_OVERCOMPENS 1. void speex_echo_get_residual(SpeexEchoState *st, spx_word32_t *Yout, int len); EXPORT int speex_preprocess(SpeexPreprocessState *st, spx_int16_t *x, spx_int32_t *echo) { return speex_preprocess_run(st, x); } EXPORT int speex_preprocess_run(SpeexPreprocessState *st, spx_int16_t *x) { int i; int M; int N = st->ps_size; int N3 = 2*N - st->frame_size; int N4 = st->frame_size - N3; spx_word32_t *ps=st->ps; spx_word32_t Zframe; spx_word16_t Pframe; spx_word16_t beta, beta_1; spx_word16_t effective_echo_suppress; st->nb_adapt++; if (st->nb_adapt>20000) st->nb_adapt = 20000; st->min_count++; beta = MAX16(QCONST16(.03,15),DIV32_16(Q15_ONE,st->nb_adapt)); beta_1 = Q15_ONE-beta; M = st->nbands; /* Deal with residual echo if provided */ if (st->echo_state) { speex_echo_get_residual(st->echo_state, st->residual_echo, N); #ifndef FIXED_POINT /* If there are NaNs or ridiculous values, it'll show up in the DC and we just reset everything to zero */ if (!(st->residual_echo[0] >=0 && st->residual_echo[0]residual_echo[i] = 0; } #endif for (i=0;iecho_noise[i] = MAX32(MULT16_32_Q15(QCONST16(.6f,15),st->echo_noise[i]), st->residual_echo[i]); filterbank_compute_bank32(st->bank, st->echo_noise, st->echo_noise+N); } else { for (i=0;iecho_noise[i] = 0; } preprocess_analysis(st, x); update_noise_prob(st); /* Noise estimation always updated for the 10 first frames */ /*if (st->nb_adapt<10) { for (i=1;iupdate_prob[i] = 0; } */ /* Update the noise estimate for the frequencies where it can be */ for (i=0;iupdate_prob[i] || st->ps[i] < PSHR32(st->noise[i], NOISE_SHIFT)) st->noise[i] = MAX32(EXTEND32(0),MULT16_32_Q15(beta_1,st->noise[i]) + MULT16_32_Q15(beta,SHL32(st->ps[i],NOISE_SHIFT))); } filterbank_compute_bank32(st->bank, st->noise, st->noise+N); /* Special case for first frame */ if (st->nb_adapt==1) for (i=0;iold_ps[i] = ps[i]; /* Compute a posteriori SNR */ for (i=0;inoise[i],NOISE_SHIFT)) , st->echo_noise[i]) , st->reverb_estimate[i]); /* A posteriori SNR = ps/noise - 1*/ st->post[i] = SUB16(DIV32_16_Q8(ps[i],tot_noise), QCONST16(1.f,SNR_SHIFT)); st->post[i]=MIN16(st->post[i], QCONST16(100.f,SNR_SHIFT)); /* Computing update gamma = .1 + .9*(old/(old+noise))^2 */ gamma = QCONST16(.1f,15)+MULT16_16_Q15(QCONST16(.89f,15),SQR16_Q15(DIV32_16_Q15(st->old_ps[i],ADD32(st->old_ps[i],tot_noise)))); /* A priori SNR update = gamma*max(0,post) + (1-gamma)*old/noise */ st->prior[i] = EXTRACT16(PSHR32(ADD32(MULT16_16(gamma,MAX16(0,st->post[i])), MULT16_16(Q15_ONE-gamma,DIV32_16_Q8(st->old_ps[i],tot_noise))), 15)); st->prior[i]=MIN16(st->prior[i], QCONST16(100.f,SNR_SHIFT)); } /*print_vec(st->post, N+M, "");*/ /* Recursive average of the a priori SNR. A bit smoothed for the psd components */ st->zeta[0] = PSHR32(ADD32(MULT16_16(QCONST16(.7f,15),st->zeta[0]), MULT16_16(QCONST16(.3f,15),st->prior[0])),15); for (i=1;izeta[i] = PSHR32(ADD32(ADD32(ADD32(MULT16_16(QCONST16(.7f,15),st->zeta[i]), MULT16_16(QCONST16(.15f,15),st->prior[i])), MULT16_16(QCONST16(.075f,15),st->prior[i-1])), MULT16_16(QCONST16(.075f,15),st->prior[i+1])),15); for (i=N-1;izeta[i] = PSHR32(ADD32(MULT16_16(QCONST16(.7f,15),st->zeta[i]), MULT16_16(QCONST16(.3f,15),st->prior[i])),15); /* Speech probability of presence for the entire frame is based on the average filterbank a priori SNR */ Zframe = 0; for (i=N;izeta[i])); Pframe = QCONST16(.1f,15)+MULT16_16_Q15(QCONST16(.899f,15),qcurve(DIV32_16(Zframe,st->nbands))); effective_echo_suppress = EXTRACT16(PSHR32(ADD32(MULT16_16(SUB16(Q15_ONE,Pframe), st->echo_suppress), MULT16_16(Pframe, st->echo_suppress_active)),15)); compute_gain_floor(st->noise_suppress, effective_echo_suppress, st->noise+N, st->echo_noise+N, st->gain_floor+N, M); /* Compute Ephraim & Malah gain speech probability of presence for each critical band (Bark scale) Technically this is actually wrong because the EM gaim assumes a slightly different probability distribution */ for (i=N;iprior[i]), 15), ADD16(st->prior[i], SHL32(1,SNR_SHIFT))); theta = MULT16_32_P15(prior_ratio, QCONST32(1.f,EXPIN_SHIFT)+SHL32(EXTEND32(st->post[i]),EXPIN_SHIFT-SNR_SHIFT)); MM = hypergeom_gain(theta); /* Gain with bound */ st->gain[i] = EXTRACT16(MIN32(Q15_ONE, MULT16_32_Q15(prior_ratio, MM))); /* Save old Bark power spectrum */ st->old_ps[i] = MULT16_32_P15(QCONST16(.2f,15),st->old_ps[i]) + MULT16_32_P15(MULT16_16_P15(QCONST16(.8f,15),SQR16_Q15(st->gain[i])),ps[i]); P1 = QCONST16(.199f,15)+MULT16_16_Q15(QCONST16(.8f,15),qcurve (st->zeta[i])); q = Q15_ONE-MULT16_16_Q15(Pframe,P1); #ifdef FIXED_POINT theta = MIN32(theta, EXTEND32(32767)); /*Q8*/tmp = MULT16_16_Q15((SHL32(1,SNR_SHIFT)+st->prior[i]),EXTRACT16(MIN32(Q15ONE,SHR32(spx_exp(-EXTRACT16(theta)),1)))); tmp = MIN16(QCONST16(3.,SNR_SHIFT), tmp); /* Prevent overflows in the next line*/ /*Q8*/tmp = EXTRACT16(PSHR32(MULT16_16(PDIV32_16(SHL32(EXTEND32(q),8),(Q15_ONE-q)),tmp),8)); st->gain2[i]=DIV32_16(SHL32(EXTEND32(32767),SNR_SHIFT), ADD16(256,tmp)); #else st->gain2[i]=1/(1.f + (q/(1.f-q))*(1+st->prior[i])*exp(-theta)); #endif } /* Convert the EM gains and speech prob to linear frequency */ filterbank_compute_psd16(st->bank,st->gain2+N, st->gain2); filterbank_compute_psd16(st->bank,st->gain+N, st->gain); /* Use 1 for linear gain resolution (best) or 0 for Bark gain resolution (faster) */ if (1) { filterbank_compute_psd16(st->bank,st->gain_floor+N, st->gain_floor); /* Compute gain according to the Ephraim-Malah algorithm -- linear frequency */ for (i=0;iprior[i]), 15), ADD16(st->prior[i], SHL32(1,SNR_SHIFT))); theta = MULT16_32_P15(prior_ratio, QCONST32(1.f,EXPIN_SHIFT)+SHL32(EXTEND32(st->post[i]),EXPIN_SHIFT-SNR_SHIFT)); /* Optimal estimator for loudness domain */ MM = hypergeom_gain(theta); /* EM gain with bound */ g = EXTRACT16(MIN32(Q15_ONE, MULT16_32_Q15(prior_ratio, MM))); /* Interpolated speech probability of presence */ p = st->gain2[i]; /* Constrain the gain to be close to the Bark scale gain */ if (MULT16_16_Q15(QCONST16(.333f,15),g) > st->gain[i]) g = MULT16_16(3,st->gain[i]); st->gain[i] = g; /* Save old power spectrum */ st->old_ps[i] = MULT16_32_P15(QCONST16(.2f,15),st->old_ps[i]) + MULT16_32_P15(MULT16_16_P15(QCONST16(.8f,15),SQR16_Q15(st->gain[i])),ps[i]); /* Apply gain floor */ if (st->gain[i] < st->gain_floor[i]) st->gain[i] = st->gain_floor[i]; /* Exponential decay model for reverberation (unused) */ /*st->reverb_estimate[i] = st->reverb_decay*st->reverb_estimate[i] + st->reverb_decay*st->reverb_level*st->gain[i]*st->gain[i]*st->ps[i];*/ /* Take into account speech probability of presence (loudness domain MMSE estimator) */ /* gain2 = [p*sqrt(gain)+(1-p)*sqrt(gain _floor) ]^2 */ tmp = MULT16_16_P15(p,spx_sqrt(SHL32(EXTEND32(st->gain[i]),15))) + MULT16_16_P15(SUB16(Q15_ONE,p),spx_sqrt(SHL32(EXTEND32(st->gain_floor[i]),15))); st->gain2[i]=SQR16_Q15(tmp); /* Use this if you want a log-domain MMSE estimator instead */ /*st->gain2[i] = pow(st->gain[i], p) * pow(st->gain_floor[i],1.f-p);*/ } } else { for (i=N;igain2[i]; st->gain[i] = MAX16(st->gain[i], st->gain_floor[i]); tmp = MULT16_16_P15(p,spx_sqrt(SHL32(EXTEND32(st->gain[i]),15))) + MULT16_16_P15(SUB16(Q15_ONE,p),spx_sqrt(SHL32(EXTEND32(st->gain_floor[i]),15))); st->gain2[i]=SQR16_Q15(tmp); } filterbank_compute_psd16(st->bank,st->gain2+N, st->gain2); } /* If noise suppression is off, don't apply the gain (but then why call this in the first place!) */ if (!st->denoise_enabled) { for (i=0;igain2[i]=Q15_ONE; } /* Apply computed gain */ for (i=1;ift[2*i-1] = MULT16_16_P15(st->gain2[i],st->ft[2*i-1]); st->ft[2*i] = MULT16_16_P15(st->gain2[i],st->ft[2*i]); } st->ft[0] = MULT16_16_P15(st->gain2[0],st->ft[0]); st->ft[2*N-1] = MULT16_16_P15(st->gain2[N-1],st->ft[2*N-1]); /*FIXME: This *will* not work for fixed-point */ #ifndef FIXED_POINT if (st->agc_enabled) speex_compute_agc(st, Pframe, st->ft); #endif /* Inverse FFT with 1/N scaling */ spx_ifft(st->fft_lookup, st->ft, st->frame); /* Scale back to original (lower) amplitude */ for (i=0;i<2*N;i++) st->frame[i] = PSHR16(st->frame[i], st->frame_shift); /*FIXME: This *will* not work for fixed-point */ #ifndef FIXED_POINT if (st->agc_enabled) { float max_sample=0; for (i=0;i<2*N;i++) if (fabs(st->frame[i])>max_sample) max_sample = fabs(st->frame[i]); if (max_sample>28000.f) { float damp = 28000.f/max_sample; for (i=0;i<2*N;i++) st->frame[i] *= damp; } } #endif /* Synthesis window (for WOLA) */ for (i=0;i<2*N;i++) st->frame[i] = MULT16_16_Q15(st->frame[i], st->window[i]); /* Perform overlap and add */ for (i=0;ioutbuf[i] + st->frame[i]; for (i=0;iframe[N3+i]; /* Update outbuf */ for (i=0;ioutbuf[i] = st->frame[st->frame_size+i]; /* FIXME: This VAD is a kludge */ st->speech_prob = Pframe; if (st->vad_enabled) { if (st->speech_prob > st->speech_prob_start || (st->was_speech && st->speech_prob > st->speech_prob_continue)) { st->was_speech=1; return 1; } else { st->was_speech=0; return 0; } } else { return 1; } } EXPORT void speex_preprocess_estimate_update(SpeexPreprocessState *st, spx_int16_t *x) { int i; int N = st->ps_size; int N3 = 2*N - st->frame_size; int M; spx_word32_t *ps=st->ps; M = st->nbands; st->min_count++; preprocess_analysis(st, x); update_noise_prob(st); for (i=1;iupdate_prob[i] || st->ps[i] < PSHR32(st->noise[i],NOISE_SHIFT)) { st->noise[i] = MULT16_32_Q15(QCONST16(.95f,15),st->noise[i]) + MULT16_32_Q15(QCONST16(.05f,15),SHL32(st->ps[i],NOISE_SHIFT)); } } for (i=0;ioutbuf[i] = MULT16_16_Q15(x[st->frame_size-N3+i],st->window[st->frame_size+i]); /* Save old power spectrum */ for (i=0;iold_ps[i] = ps[i]; for (i=0;ireverb_estimate[i] = MULT16_32_Q15(st->reverb_decay, st->reverb_estimate[i]); } EXPORT int speex_preprocess_ctl(SpeexPreprocessState *state, int request, void *ptr) { int i; SpeexPreprocessState *st; st=(SpeexPreprocessState*)state; switch(request) { case SPEEX_PREPROCESS_SET_DENOISE: st->denoise_enabled = (*(spx_int32_t*)ptr); break; case SPEEX_PREPROCESS_GET_DENOISE: (*(spx_int32_t*)ptr) = st->denoise_enabled; break; #ifndef FIXED_POINT case SPEEX_PREPROCESS_SET_AGC: st->agc_enabled = (*(spx_int32_t*)ptr); break; case SPEEX_PREPROCESS_GET_AGC: (*(spx_int32_t*)ptr) = st->agc_enabled; break; #ifndef DISABLE_FLOAT_API case SPEEX_PREPROCESS_SET_AGC_LEVEL: st->agc_level = (*(float*)ptr); if (st->agc_level<1) st->agc_level=1; if (st->agc_level>32768) st->agc_level=32768; break; case SPEEX_PREPROCESS_GET_AGC_LEVEL: (*(float*)ptr) = st->agc_level; break; #endif /* #ifndef DISABLE_FLOAT_API */ case SPEEX_PREPROCESS_SET_AGC_INCREMENT: st->max_increase_step = exp(0.11513f * (*(spx_int32_t*)ptr)*st->frame_size / st->sampling_rate); break; case SPEEX_PREPROCESS_GET_AGC_INCREMENT: (*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->max_increase_step)*st->sampling_rate/st->frame_size); break; case SPEEX_PREPROCESS_SET_AGC_DECREMENT: st->max_decrease_step = exp(0.11513f * (*(spx_int32_t*)ptr)*st->frame_size / st->sampling_rate); break; case SPEEX_PREPROCESS_GET_AGC_DECREMENT: (*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->max_decrease_step)*st->sampling_rate/st->frame_size); break; case SPEEX_PREPROCESS_SET_AGC_MAX_GAIN: st->max_gain = exp(0.11513f * (*(spx_int32_t*)ptr)); break; case SPEEX_PREPROCESS_GET_AGC_MAX_GAIN: (*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->max_gain)); break; #endif case SPEEX_PREPROCESS_SET_VAD: speex_warning("The VAD has been replaced by a hack pending a complete rewrite"); st->vad_enabled = (*(spx_int32_t*)ptr); break; case SPEEX_PREPROCESS_GET_VAD: (*(spx_int32_t*)ptr) = st->vad_enabled; break; case SPEEX_PREPROCESS_SET_DEREVERB: st->dereverb_enabled = (*(spx_int32_t*)ptr); for (i=0;ips_size;i++) st->reverb_estimate[i]=0; break; case SPEEX_PREPROCESS_GET_DEREVERB: (*(spx_int32_t*)ptr) = st->dereverb_enabled; break; case SPEEX_PREPROCESS_SET_DEREVERB_LEVEL: /* FIXME: Re-enable when de-reverberation is actually enabled again */ /*st->reverb_level = (*(float*)ptr);*/ break; case SPEEX_PREPROCESS_GET_DEREVERB_LEVEL: /* FIXME: Re-enable when de-reverberation is actually enabled again */ /*(*(float*)ptr) = st->reverb_level;*/ break; case SPEEX_PREPROCESS_SET_DEREVERB_DECAY: /* FIXME: Re-enable when de-reverberation is actually enabled again */ /*st->reverb_decay = (*(float*)ptr);*/ break; case SPEEX_PREPROCESS_GET_DEREVERB_DECAY: /* FIXME: Re-enable when de-reverberation is actually enabled again */ /*(*(float*)ptr) = st->reverb_decay;*/ break; case SPEEX_PREPROCESS_SET_PROB_START: *(spx_int32_t*)ptr = MIN32(100,MAX32(0, *(spx_int32_t*)ptr)); st->speech_prob_start = DIV32_16(MULT16_16(Q15ONE,*(spx_int32_t*)ptr), 100); break; case SPEEX_PREPROCESS_GET_PROB_START: (*(spx_int32_t*)ptr) = MULT16_16_Q15(st->speech_prob_start, 100); break; case SPEEX_PREPROCESS_SET_PROB_CONTINUE: *(spx_int32_t*)ptr = MIN32(100,MAX32(0, *(spx_int32_t*)ptr)); st->speech_prob_continue = DIV32_16(MULT16_16(Q15ONE,*(spx_int32_t*)ptr), 100); break; case SPEEX_PREPROCESS_GET_PROB_CONTINUE: (*(spx_int32_t*)ptr) = MULT16_16_Q15(st->speech_prob_continue, 100); break; case SPEEX_PREPROCESS_SET_NOISE_SUPPRESS: st->noise_suppress = -ABS(*(spx_int32_t*)ptr); break; case SPEEX_PREPROCESS_GET_NOISE_SUPPRESS: (*(spx_int32_t*)ptr) = st->noise_suppress; break; case SPEEX_PREPROCESS_SET_ECHO_SUPPRESS: st->echo_suppress = -ABS(*(spx_int32_t*)ptr); break; case SPEEX_PREPROCESS_GET_ECHO_SUPPRESS: (*(spx_int32_t*)ptr) = st->echo_suppress; break; case SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE: st->echo_suppress_active = -ABS(*(spx_int32_t*)ptr); break; case SPEEX_PREPROCESS_GET_ECHO_SUPPRESS_ACTIVE: (*(spx_int32_t*)ptr) = st->echo_suppress_active; break; case SPEEX_PREPROCESS_SET_ECHO_STATE: st->echo_state = (SpeexEchoState*)ptr; break; case SPEEX_PREPROCESS_GET_ECHO_STATE: (*(SpeexEchoState**)ptr) = (SpeexEchoState*)st->echo_state; break; #ifndef FIXED_POINT case SPEEX_PREPROCESS_GET_AGC_LOUDNESS: (*(spx_int32_t*)ptr) = pow(st->loudness, 1.0/LOUDNESS_EXP); break; case SPEEX_PREPROCESS_GET_AGC_GAIN: (*(spx_int32_t*)ptr) = floor(.5+8.6858*log(st->agc_gain)); break; #endif case SPEEX_PREPROCESS_GET_PSD_SIZE: case SPEEX_PREPROCESS_GET_NOISE_PSD_SIZE: (*(spx_int32_t*)ptr) = st->ps_size; break; case SPEEX_PREPROCESS_GET_PSD: for(i=0;ips_size;i++) ((spx_int32_t *)ptr)[i] = (spx_int32_t) st->ps[i]; break; case SPEEX_PREPROCESS_GET_NOISE_PSD: for(i=0;ips_size;i++) ((spx_int32_t *)ptr)[i] = (spx_int32_t) PSHR32(st->noise[i], NOISE_SHIFT); break; case SPEEX_PREPROCESS_GET_PROB: (*(spx_int32_t*)ptr) = MULT16_16_Q15(st->speech_prob, 100); break; #ifndef FIXED_POINT case SPEEX_PREPROCESS_SET_AGC_TARGET: st->agc_level = (*(spx_int32_t*)ptr); if (st->agc_level<1) st->agc_level=1; if (st->agc_level>32768) st->agc_level=32768; break; case SPEEX_PREPROCESS_GET_AGC_TARGET: (*(spx_int32_t*)ptr) = st->agc_level; break; #endif default: speex_warning_int("Unknown speex_preprocess_ctl request: ", request); return -1; } return 0; } #ifdef FIXED_DEBUG long long spx_mips=0; #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/pseudofloat.h ================================================ /* Copyright (C) 2005 Jean-Marc Valin */ /** @file pseudofloat.h @brief Pseudo-floating point * This header file provides a lightweight floating point type for * use on fixed-point platforms when a large dynamic range is * required. The new type is not compatible with the 32-bit IEEE format, * it is not even remotely as accurate as 32-bit floats, and is not * even guaranteed to produce even remotely correct results for code * other than Speex. It makes all kinds of shortcuts that are acceptable * for Speex, but may not be acceptable for your application. You're * quite welcome to reuse this code and improve it, but don't assume * it works out of the box. Most likely, it doesn't. */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef PSEUDOFLOAT_H #define PSEUDOFLOAT_H #include "arch.h" #include "os_support.h" #include "math_approx.h" #include #ifdef FIXED_POINT typedef struct { spx_int16_t m; spx_int16_t e; } spx_float_t; static const spx_float_t FLOAT_ZERO = {0,0}; static const spx_float_t FLOAT_ONE = {16384,-14}; static const spx_float_t FLOAT_HALF = {16384,-15}; #define MIN(a,b) ((a)<(b)?(a):(b)) static inline spx_float_t PSEUDOFLOAT(spx_int32_t x) { int e=0; int sign=0; if (x<0) { sign = 1; x = -x; } if (x==0) { spx_float_t r = {0,0}; return r; } e = spx_ilog2(ABS32(x))-14; x = VSHR32(x, e); if (sign) { spx_float_t r; r.m = -x; r.e = e; return r; } else { spx_float_t r; r.m = x; r.e = e; return r; } } static inline spx_float_t FLOAT_ADD(spx_float_t a, spx_float_t b) { spx_float_t r; if (a.m==0) return b; else if (b.m==0) return a; if ((a).e > (b).e) { r.m = ((a).m>>1) + ((b).m>>MIN(15,(a).e-(b).e+1)); r.e = (a).e+1; } else { r.m = ((b).m>>1) + ((a).m>>MIN(15,(b).e-(a).e+1)); r.e = (b).e+1; } if (r.m>0) { if (r.m<16384) { r.m<<=1; r.e-=1; } } else { if (r.m>-16384) { r.m<<=1; r.e-=1; } } /*printf ("%f + %f = %f\n", REALFLOAT(a), REALFLOAT(b), REALFLOAT(r));*/ return r; } static inline spx_float_t FLOAT_SUB(spx_float_t a, spx_float_t b) { spx_float_t r; if (a.m==0) return b; else if (b.m==0) return a; if ((a).e > (b).e) { r.m = ((a).m>>1) - ((b).m>>MIN(15,(a).e-(b).e+1)); r.e = (a).e+1; } else { r.m = ((a).m>>MIN(15,(b).e-(a).e+1)) - ((b).m>>1); r.e = (b).e+1; } if (r.m>0) { if (r.m<16384) { r.m<<=1; r.e-=1; } } else { if (r.m>-16384) { r.m<<=1; r.e-=1; } } /*printf ("%f + %f = %f\n", REALFLOAT(a), REALFLOAT(b), REALFLOAT(r));*/ return r; } static inline int FLOAT_LT(spx_float_t a, spx_float_t b) { if (a.m==0) return b.m>0; else if (b.m==0) return a.m<0; if ((a).e > (b).e) return ((a).m>>1) < ((b).m>>MIN(15,(a).e-(b).e+1)); else return ((b).m>>1) > ((a).m>>MIN(15,(b).e-(a).e+1)); } static inline int FLOAT_GT(spx_float_t a, spx_float_t b) { return FLOAT_LT(b,a); } static inline spx_float_t FLOAT_MULT(spx_float_t a, spx_float_t b) { spx_float_t r; r.m = (spx_int16_t)((spx_int32_t)(a).m*(b).m>>15); r.e = (a).e+(b).e+15; if (r.m>0) { if (r.m<16384) { r.m<<=1; r.e-=1; } } else { if (r.m>-16384) { r.m<<=1; r.e-=1; } } /*printf ("%f * %f = %f\n", REALFLOAT(a), REALFLOAT(b), REALFLOAT(r));*/ return r; } static inline spx_float_t FLOAT_AMULT(spx_float_t a, spx_float_t b) { spx_float_t r; r.m = (spx_int16_t)((spx_int32_t)(a).m*(b).m>>15); r.e = (a).e+(b).e+15; return r; } static inline spx_float_t FLOAT_SHL(spx_float_t a, int b) { spx_float_t r; r.m = a.m; r.e = a.e+b; return r; } static inline spx_int16_t FLOAT_EXTRACT16(spx_float_t a) { if (a.e<0) return EXTRACT16((EXTEND32(a.m)+(EXTEND32(1)<<(-a.e-1)))>>-a.e); else return a.m<>-a.e; else return EXTEND32(a.m)<=SHL32(EXTEND32(b.m-1),15)) { a >>= 1; e++; } r.m = DIV32_16(a,b.m); r.e = e-b.e; return r; } /* Do NOT attempt to divide by a negative number */ static inline spx_float_t FLOAT_DIV32(spx_word32_t a, spx_word32_t b) { int e0=0,e=0; spx_float_t r; if (a==0) { return FLOAT_ZERO; } if (b>32767) { e0 = spx_ilog2(b)-14; b = VSHR32(b, e0); e0 = -e0; } e = spx_ilog2(ABS32(a))-spx_ilog2(b-1)-15; a = VSHR32(a, e); if (ABS32(a)>=SHL32(EXTEND32(b-1),15)) { a >>= 1; e++; } e += e0; r.m = DIV32_16(a,b); r.e = e; return r; } /* Do NOT attempt to divide by a negative number */ static inline spx_float_t FLOAT_DIVU(spx_float_t a, spx_float_t b) { int e=0; spx_int32_t num; spx_float_t r; if (b.m<=0) { speex_warning_int("Attempted to divide by", b.m); return FLOAT_ONE; } num = a.m; a.m = ABS16(a.m); while (a.m >= b.m) { e++; a.m >>= 1; } num = num << (15-e); r.m = DIV32_16(num,b.m); r.e = a.e-b.e-15+e; return r; } static inline spx_float_t FLOAT_SQRT(spx_float_t a) { spx_float_t r; spx_int32_t m; m = SHL32(EXTEND32(a.m), 14); r.e = a.e - 14; if (r.e & 1) { r.e -= 1; m <<= 1; } r.e >>= 1; r.m = spx_sqrt(m); return r; } #else #define spx_float_t float #define FLOAT_ZERO 0.f #define FLOAT_ONE 1.f #define FLOAT_HALF 0.5f #define PSEUDOFLOAT(x) (x) #define FLOAT_MULT(a,b) ((a)*(b)) #define FLOAT_AMULT(a,b) ((a)*(b)) #define FLOAT_MUL32(a,b) ((a)*(b)) #define FLOAT_DIV32(a,b) ((a)/(b)) #define FLOAT_EXTRACT16(a) (a) #define FLOAT_EXTRACT32(a) (a) #define FLOAT_ADD(a,b) ((a)+(b)) #define FLOAT_SUB(a,b) ((a)-(b)) #define REALFLOAT(x) (x) #define FLOAT_DIV32_FLOAT(a,b) ((a)/(b)) #define FLOAT_MUL32U(a,b) ((a)*(b)) #define FLOAT_SHL(a,b) (a) #define FLOAT_LT(a,b) ((a)<(b)) #define FLOAT_GT(a,b) ((a)>(b)) #define FLOAT_DIVU(a,b) ((a)/(b)) #define FLOAT_SQRT(a) (spx_sqrt(a)) #endif #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/quant_lsp.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: quant_lsp.c LSP vector quantization Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "quant_lsp.h" #include "os_support.h" #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #include "arch.h" #ifdef BFIN_ASM #include "quant_lsp_bfin.h" #endif #ifdef FIXED_POINT #define LSP_LINEAR(i) (SHL16(i+1,11)) #define LSP_LINEAR_HIGH(i) (ADD16(MULT16_16_16(i,2560),6144)) #define LSP_DIV_256(x) (SHL16((spx_word16_t)x, 5)) #define LSP_DIV_512(x) (SHL16((spx_word16_t)x, 4)) #define LSP_DIV_1024(x) (SHL16((spx_word16_t)x, 3)) #define LSP_PI 25736 #else #define LSP_LINEAR(i) (.25*(i)+.25) #define LSP_LINEAR_HIGH(i) (.3125*(i)+.75) #define LSP_SCALE 256. #define LSP_DIV_256(x) (0.0039062*(x)) #define LSP_DIV_512(x) (0.0019531*(x)) #define LSP_DIV_1024(x) (0.00097656*(x)) #define LSP_PI M_PI #endif static void compute_quant_weights(spx_lsp_t *qlsp, spx_word16_t *quant_weight, int order) { int i; spx_word16_t tmp1, tmp2; for (i=0;i tmp2 ? tmp1 : tmp2; }*/ for (i=0;i #include "arch.h" #define MAX_LSP_SIZE 20 #define NB_CDBK_SIZE 64 #define NB_CDBK_SIZE_LOW1 64 #define NB_CDBK_SIZE_LOW2 64 #define NB_CDBK_SIZE_HIGH1 64 #define NB_CDBK_SIZE_HIGH2 64 /*Narrowband codebooks*/ extern const signed char cdbk_nb[]; extern const signed char cdbk_nb_low1[]; extern const signed char cdbk_nb_low2[]; extern const signed char cdbk_nb_high1[]; extern const signed char cdbk_nb_high2[]; /* Quantizes narrowband LSPs with 30 bits */ void lsp_quant_nb(spx_lsp_t *lsp, spx_lsp_t *qlsp, int order, SpeexBits *bits); /* Decodes quantized narrowband LSPs */ void lsp_unquant_nb(spx_lsp_t *lsp, int order, SpeexBits *bits); /* Quantizes low bit-rate narrowband LSPs with 18 bits */ void lsp_quant_lbr(spx_lsp_t *lsp, spx_lsp_t *qlsp, int order, SpeexBits *bits); /* Decodes quantized low bit-rate narrowband LSPs */ void lsp_unquant_lbr(spx_lsp_t *lsp, int order, SpeexBits *bits); /* Quantizes high-band LSPs with 12 bits */ void lsp_quant_high(spx_lsp_t *lsp, spx_lsp_t *qlsp, int order, SpeexBits *bits); /* Decodes high-band LSPs */ void lsp_unquant_high(spx_lsp_t *lsp, int order, SpeexBits *bits); #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/quant_lsp_bfin.h ================================================ /* Copyright (C) 2006 David Rowe */ /** @file quant_lsp_bfin.h @author David Rowe @brief Various compatibility routines for Speex (Blackfin version) */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define OVERRIDE_LSP_QUANT #ifdef OVERRIDE_LSP_QUANT /* Note http://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html well tell you all the magic resgister constraints used below for gcc in-line asm. */ static int lsp_quant( spx_word16_t *x, const signed char *cdbk, int nbVec, int nbDim ) { int j; spx_word32_t best_dist=1<<30; int best_id=0; __asm__ __volatile__ ( " %0 = 1 (X);\n\t" /* %0: best_dist */ " %0 <<= 30;\n\t" " %1 = 0 (X);\n\t" /* %1: best_i */ " P2 = %3\n\t" /* P2: ptr to cdbk */ " R5 = 0;\n\t" /* R5: best cb entry */ " R0 = %5;\n\t" /* set up circ addr */ " R0 <<= 1;\n\t" " L0 = R0;\n\t" " I0 = %2;\n\t" /* %2: &x[0] */ " B0 = %2;\n\t" " R2.L = W [I0++];\n\t" " LSETUP (1f, 2f) LC0 = %4;\n\t" "1: R3 = 0;\n\t" /* R3: dist */ " LSETUP (3f, 4f) LC1 = %5;\n\t" "3: R1 = B [P2++] (X);\n\t" " R1 <<= 5;\n\t" " R0.L = R2.L - R1.L || R2.L = W [I0++];\n\t" " R0 = R0.L*R0.L;\n\t" "4: R3 = R3 + R0;\n\t" " cc =R3<%0;\n\t" " if cc %0=R3;\n\t" " if cc %1=R5;\n\t" "2: R5 += 1;\n\t" " L0 = 0;\n\t" : "=&d" (best_dist), "=&d" (best_id) : "a" (x), "b" (cdbk), "a" (nbVec), "a" (nbDim) : "I0", "P2", "R0", "R1", "R2", "R3", "R5", "L0", "B0", "A0" ); for (j=0;j>> 16;\n\t" " R1 = (A1 += R2.L*R0.H) (IS);\n\t" "4: R3 = R3 + R1;\n\t" " cc =R3<%0;\n\t" " if cc %0=R3;\n\t" " if cc %1=R5;\n\t" "2: R5 += 1;\n\t" " L0 = 0;\n\t" " L1 = 0;\n\t" : "=&d" (best_dist), "=&d" (best_id) : "a" (x), "a" (weight), "b" (cdbk), "a" (nbVec), "a" (nbDim) : "I0", "I1", "P2", "R0", "R1", "R2", "R3", "R5", "A1", "L0", "L1", "B0", "B1" ); for (j=0;j static void *speex_alloc (int size) {return calloc(size,1);} static void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);} static void speex_free (void *ptr) {free(ptr);} #include "speex_resampler.h" #include "arch.h" #else /* OUTSIDE_SPEEX */ #include "speex/speex_resampler.h" #include "arch.h" #include "os_support.h" #endif /* OUTSIDE_SPEEX */ #include "stack_alloc.h" #include #ifndef M_PI #define M_PI 3.14159263 #endif #ifdef FIXED_POINT #define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x))) #else #define WORD2INT(x) ((x) < -32767.5f ? -32768 : ((x) > 32766.5f ? 32767 : floor(.5+(x)))) #endif #define IMAX(a,b) ((a) > (b) ? (a) : (b)) #define IMIN(a,b) ((a) < (b) ? (a) : (b)) #ifndef NULL #define NULL 0 #endif #ifdef _USE_SSE #include "resample_sse.h" #endif /* Numer of elements to allocate on the stack */ #ifdef VAR_ARRAYS #define FIXED_STACK_ALLOC 8192 #else #define FIXED_STACK_ALLOC 1024 #endif typedef int (*resampler_basic_func)(SpeexResamplerState *, spx_uint32_t , const spx_word16_t *, spx_uint32_t *, spx_word16_t *, spx_uint32_t *); struct SpeexResamplerState_ { spx_uint32_t in_rate; spx_uint32_t out_rate; spx_uint32_t num_rate; spx_uint32_t den_rate; int quality; spx_uint32_t nb_channels; spx_uint32_t filt_len; spx_uint32_t mem_alloc_size; spx_uint32_t buffer_size; int int_advance; int frac_advance; float cutoff; spx_uint32_t oversample; int initialised; int started; /* These are per-channel */ spx_int32_t *last_sample; spx_uint32_t *samp_frac_num; spx_uint32_t *magic_samples; spx_word16_t *mem; spx_word16_t *sinc_table; spx_uint32_t sinc_table_length; resampler_basic_func resampler_ptr; int in_stride; int out_stride; } ; static double kaiser12_table[68] = { 0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076, 0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014, 0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601, 0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014, 0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490, 0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546, 0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178, 0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947, 0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058, 0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438, 0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734, 0.00001000, 0.00000000}; /* static double kaiser12_table[36] = { 0.99440475, 1.00000000, 0.99440475, 0.97779076, 0.95066529, 0.91384741, 0.86843014, 0.81573067, 0.75723148, 0.69451601, 0.62920216, 0.56287762, 0.49704014, 0.43304576, 0.37206735, 0.31506490, 0.26276832, 0.21567274, 0.17404546, 0.13794294, 0.10723616, 0.08164178, 0.06075685, 0.04409466, 0.03111947, 0.02127838, 0.01402878, 0.00886058, 0.00531256, 0.00298291, 0.00153438, 0.00069463, 0.00025272, 0.0000527734, 0.00000500, 0.00000000}; */ static double kaiser10_table[36] = { 0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446, 0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347, 0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962, 0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451, 0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739, 0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000}; static double kaiser8_table[36] = { 0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200, 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126, 0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272, 0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758, 0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490, 0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000}; static double kaiser6_table[36] = { 0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003, 0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565, 0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561, 0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058, 0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600, 0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000}; struct FuncDef { double *table; int oversample; }; static struct FuncDef _KAISER12 = {kaiser12_table, 64}; #define KAISER12 (&_KAISER12) /*static struct FuncDef _KAISER12 = {kaiser12_table, 32}; #define KAISER12 (&_KAISER12)*/ static struct FuncDef _KAISER10 = {kaiser10_table, 32}; #define KAISER10 (&_KAISER10) static struct FuncDef _KAISER8 = {kaiser8_table, 32}; #define KAISER8 (&_KAISER8) static struct FuncDef _KAISER6 = {kaiser6_table, 32}; #define KAISER6 (&_KAISER6) struct QualityMapping { int base_length; int oversample; float downsample_bandwidth; float upsample_bandwidth; struct FuncDef *window_func; }; /* This table maps conversion quality to internal parameters. There are two reasons that explain why the up-sampling bandwidth is larger than the down-sampling bandwidth: 1) When up-sampling, we can assume that the spectrum is already attenuated close to the Nyquist rate (from an A/D or a previous resampling filter) 2) Any aliasing that occurs very close to the Nyquist rate will be masked by the sinusoids/noise just below the Nyquist rate (guaranteed only for up-sampling). */ static const struct QualityMapping quality_map[11] = { { 8, 4, 0.830f, 0.860f, KAISER6 }, /* Q0 */ { 16, 4, 0.850f, 0.880f, KAISER6 }, /* Q1 */ { 32, 4, 0.882f, 0.910f, KAISER6 }, /* Q2 */ /* 82.3% cutoff ( ~60 dB stop) 6 */ { 48, 8, 0.895f, 0.917f, KAISER8 }, /* Q3 */ /* 84.9% cutoff ( ~80 dB stop) 8 */ { 64, 8, 0.921f, 0.940f, KAISER8 }, /* Q4 */ /* 88.7% cutoff ( ~80 dB stop) 8 */ { 80, 16, 0.922f, 0.940f, KAISER10}, /* Q5 */ /* 89.1% cutoff (~100 dB stop) 10 */ { 96, 16, 0.940f, 0.945f, KAISER10}, /* Q6 */ /* 91.5% cutoff (~100 dB stop) 10 */ {128, 16, 0.950f, 0.950f, KAISER10}, /* Q7 */ /* 93.1% cutoff (~100 dB stop) 10 */ {160, 16, 0.960f, 0.960f, KAISER10}, /* Q8 */ /* 94.5% cutoff (~100 dB stop) 10 */ {192, 32, 0.968f, 0.968f, KAISER12}, /* Q9 */ /* 95.5% cutoff (~100 dB stop) 10 */ {256, 32, 0.975f, 0.975f, KAISER12}, /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */ }; /*8,24,40,56,80,104,128,160,200,256,320*/ static double compute_func(float x, struct FuncDef *func) { float y, frac; double interp[4]; int ind; y = x*func->oversample; ind = (int)floor(y); frac = (y-ind); /* CSE with handle the repeated powers */ interp[3] = -0.1666666667*frac + 0.1666666667*(frac*frac*frac); interp[2] = frac + 0.5*(frac*frac) - 0.5*(frac*frac*frac); /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/ interp[0] = -0.3333333333*frac + 0.5*(frac*frac) - 0.1666666667*(frac*frac*frac); /* Just to make sure we don't have rounding problems */ interp[1] = 1.f-interp[3]-interp[2]-interp[0]; /*sum = frac*accum[1] + (1-frac)*accum[2];*/ return interp[0]*func->table[ind] + interp[1]*func->table[ind+1] + interp[2]*func->table[ind+2] + interp[3]*func->table[ind+3]; } #if 0 #include int main(int argc, char **argv) { int i; for (i=0;i<256;i++) { printf ("%f\n", compute_func(i/256., KAISER12)); } return 0; } #endif #ifdef FIXED_POINT /* The slow way of computing a sinc for the table. Should improve that some day */ static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func) { /*fprintf (stderr, "%f ", x);*/ float xx = x * cutoff; if (fabs(x)<1e-6f) return WORD2INT(32768.*cutoff); else if (fabs(x) > .5f*N) return 0; /*FIXME: Can it really be any slower than this? */ return WORD2INT(32768.*cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func)); } #else /* The slow way of computing a sinc for the table. Should improve that some day */ static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func) { /*fprintf (stderr, "%f ", x);*/ float xx = x * cutoff; if (fabs(x)<1e-6) return cutoff; else if (fabs(x) > .5*N) return 0; /*FIXME: Can it really be any slower than this? */ return cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func); } #endif #ifdef FIXED_POINT static void cubic_coef(spx_word16_t x, spx_word16_t interp[4]) { /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation but I know it's MMSE-optimal on a sinc */ spx_word16_t x2, x3; x2 = MULT16_16_P15(x, x); x3 = MULT16_16_P15(x, x2); interp[0] = PSHR32(MULT16_16(QCONST16(-0.16667f, 15),x) + MULT16_16(QCONST16(0.16667f, 15),x3),15); interp[1] = EXTRACT16(EXTEND32(x) + SHR32(SUB32(EXTEND32(x2),EXTEND32(x3)),1)); interp[3] = PSHR32(MULT16_16(QCONST16(-0.33333f, 15),x) + MULT16_16(QCONST16(.5f,15),x2) - MULT16_16(QCONST16(0.16667f, 15),x3),15); /* Just to make sure we don't have rounding problems */ interp[2] = Q15_ONE-interp[0]-interp[1]-interp[3]; if (interp[2]<32767) interp[2]+=1; } #else static void cubic_coef(spx_word16_t frac, spx_word16_t interp[4]) { /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation but I know it's MMSE-optimal on a sinc */ interp[0] = -0.16667f*frac + 0.16667f*frac*frac*frac; interp[1] = frac + 0.5f*frac*frac - 0.5f*frac*frac*frac; /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/ interp[3] = -0.33333f*frac + 0.5f*frac*frac - 0.16667f*frac*frac*frac; /* Just to make sure we don't have rounding problems */ interp[2] = 1.-interp[0]-interp[1]-interp[3]; } #endif static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) { const int N = st->filt_len; int out_sample = 0; int last_sample = st->last_sample[channel_index]; spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; const spx_word16_t *sinc_table = st->sinc_table; const int out_stride = st->out_stride; const int int_advance = st->int_advance; const int frac_advance = st->frac_advance; const spx_uint32_t den_rate = st->den_rate; spx_word32_t sum; int j; while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) { const spx_word16_t *sinc = & sinc_table[samp_frac_num*N]; const spx_word16_t *iptr = & in[last_sample]; #ifndef OVERRIDE_INNER_PRODUCT_SINGLE float accum[4] = {0,0,0,0}; for(j=0;j= den_rate) { samp_frac_num -= den_rate; last_sample++; } } st->last_sample[channel_index] = last_sample; st->samp_frac_num[channel_index] = samp_frac_num; return out_sample; } #ifdef FIXED_POINT #else /* This is the same as the previous function, except with a double-precision accumulator */ static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) { const int N = st->filt_len; int out_sample = 0; int last_sample = st->last_sample[channel_index]; spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; const spx_word16_t *sinc_table = st->sinc_table; const int out_stride = st->out_stride; const int int_advance = st->int_advance; const int frac_advance = st->frac_advance; const spx_uint32_t den_rate = st->den_rate; double sum; int j; while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) { const spx_word16_t *sinc = & sinc_table[samp_frac_num*N]; const spx_word16_t *iptr = & in[last_sample]; #ifndef OVERRIDE_INNER_PRODUCT_DOUBLE double accum[4] = {0,0,0,0}; for(j=0;j= den_rate) { samp_frac_num -= den_rate; last_sample++; } } st->last_sample[channel_index] = last_sample; st->samp_frac_num[channel_index] = samp_frac_num; return out_sample; } #endif static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) { const int N = st->filt_len; int out_sample = 0; int last_sample = st->last_sample[channel_index]; spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; const int out_stride = st->out_stride; const int int_advance = st->int_advance; const int frac_advance = st->frac_advance; const spx_uint32_t den_rate = st->den_rate; int j; spx_word32_t sum; while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) { const spx_word16_t *iptr = & in[last_sample]; const int offset = samp_frac_num*st->oversample/st->den_rate; #ifdef FIXED_POINT const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate); #else const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate; #endif spx_word16_t interp[4]; #ifndef OVERRIDE_INTERPOLATE_PRODUCT_SINGLE spx_word32_t accum[4] = {0,0,0,0}; for(j=0;jsinc_table[4+(j+1)*st->oversample-offset-2]); accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]); accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]); accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]); } cubic_coef(frac, interp); sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]); #else cubic_coef(frac, interp); sum = interpolate_product_single(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp); #endif out[out_stride * out_sample++] = PSHR32(sum,15); last_sample += int_advance; samp_frac_num += frac_advance; if (samp_frac_num >= den_rate) { samp_frac_num -= den_rate; last_sample++; } } st->last_sample[channel_index] = last_sample; st->samp_frac_num[channel_index] = samp_frac_num; return out_sample; } #ifdef FIXED_POINT #else /* This is the same as the previous function, except with a double-precision accumulator */ static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) { const int N = st->filt_len; int out_sample = 0; int last_sample = st->last_sample[channel_index]; spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; const int out_stride = st->out_stride; const int int_advance = st->int_advance; const int frac_advance = st->frac_advance; const spx_uint32_t den_rate = st->den_rate; int j; spx_word32_t sum; while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) { const spx_word16_t *iptr = & in[last_sample]; const int offset = samp_frac_num*st->oversample/st->den_rate; #ifdef FIXED_POINT const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate); #else const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate; #endif spx_word16_t interp[4]; #ifndef OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE double accum[4] = {0,0,0,0}; for(j=0;jsinc_table[4+(j+1)*st->oversample-offset-2]); accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]); accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]); accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]); } cubic_coef(frac, interp); sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]); #else cubic_coef(frac, interp); sum = interpolate_product_double(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp); #endif out[out_stride * out_sample++] = PSHR32(sum,15); last_sample += int_advance; samp_frac_num += frac_advance; if (samp_frac_num >= den_rate) { samp_frac_num -= den_rate; last_sample++; } } st->last_sample[channel_index] = last_sample; st->samp_frac_num[channel_index] = samp_frac_num; return out_sample; } #endif static void update_filter(SpeexResamplerState *st) { spx_uint32_t old_length; old_length = st->filt_len; st->oversample = quality_map[st->quality].oversample; st->filt_len = quality_map[st->quality].base_length; if (st->num_rate > st->den_rate) { /* down-sampling */ st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate; /* FIXME: divide the numerator and denominator by a certain amount if they're too large */ st->filt_len = st->filt_len*st->num_rate / st->den_rate; /* Round down to make sure we have a multiple of 4 */ st->filt_len &= (~0x3); if (2*st->den_rate < st->num_rate) st->oversample >>= 1; if (4*st->den_rate < st->num_rate) st->oversample >>= 1; if (8*st->den_rate < st->num_rate) st->oversample >>= 1; if (16*st->den_rate < st->num_rate) st->oversample >>= 1; if (st->oversample < 1) st->oversample = 1; } else { /* up-sampling */ st->cutoff = quality_map[st->quality].upsample_bandwidth; } /* Choose the resampling type that requires the least amount of memory */ if (st->den_rate <= st->oversample) { spx_uint32_t i; if (!st->sinc_table) st->sinc_table = (spx_word16_t *)speex_alloc(st->filt_len*st->den_rate*sizeof(spx_word16_t)); else if (st->sinc_table_length < st->filt_len*st->den_rate) { st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,st->filt_len*st->den_rate*sizeof(spx_word16_t)); st->sinc_table_length = st->filt_len*st->den_rate; } for (i=0;iden_rate;i++) { spx_int32_t j; for (j=0;jfilt_len;j++) { st->sinc_table[i*st->filt_len+j] = sinc(st->cutoff,((j-(spx_int32_t)st->filt_len/2+1)-((float)i)/st->den_rate), st->filt_len, quality_map[st->quality].window_func); } } #ifdef FIXED_POINT st->resampler_ptr = resampler_basic_direct_single; #else if (st->quality>8) st->resampler_ptr = resampler_basic_direct_double; else st->resampler_ptr = resampler_basic_direct_single; #endif /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/ } else { spx_int32_t i; if (!st->sinc_table) st->sinc_table = (spx_word16_t *)speex_alloc((st->filt_len*st->oversample+8)*sizeof(spx_word16_t)); else if (st->sinc_table_length < st->filt_len*st->oversample+8) { st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,(st->filt_len*st->oversample+8)*sizeof(spx_word16_t)); st->sinc_table_length = st->filt_len*st->oversample+8; } for (i=-4;i<(spx_int32_t)(st->oversample*st->filt_len+4);i++) st->sinc_table[i+4] = sinc(st->cutoff,(i/(float)st->oversample - st->filt_len/2), st->filt_len, quality_map[st->quality].window_func); #ifdef FIXED_POINT st->resampler_ptr = resampler_basic_interpolate_single; #else if (st->quality>8) st->resampler_ptr = resampler_basic_interpolate_double; else st->resampler_ptr = resampler_basic_interpolate_single; #endif /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/ } st->int_advance = st->num_rate/st->den_rate; st->frac_advance = st->num_rate%st->den_rate; /* Here's the place where we update the filter memory to take into account the change in filter length. It's probably the messiest part of the code due to handling of lots of corner cases. */ if (!st->mem) { spx_uint32_t i; st->mem_alloc_size = st->filt_len-1 + st->buffer_size; st->mem = (spx_word16_t*)speex_alloc(st->nb_channels*st->mem_alloc_size * sizeof(spx_word16_t)); for (i=0;inb_channels*st->mem_alloc_size;i++) st->mem[i] = 0; /*speex_warning("init filter");*/ } else if (!st->started) { spx_uint32_t i; st->mem_alloc_size = st->filt_len-1 + st->buffer_size; st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*st->mem_alloc_size * sizeof(spx_word16_t)); for (i=0;inb_channels*st->mem_alloc_size;i++) st->mem[i] = 0; /*speex_warning("reinit filter");*/ } else if (st->filt_len > old_length) { spx_int32_t i; /* Increase the filter length */ /*speex_warning("increase filter size");*/ int old_alloc_size = st->mem_alloc_size; if ((st->filt_len-1 + st->buffer_size) > st->mem_alloc_size) { st->mem_alloc_size = st->filt_len-1 + st->buffer_size; st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*st->mem_alloc_size * sizeof(spx_word16_t)); } for (i=st->nb_channels-1;i>=0;i--) { spx_int32_t j; spx_uint32_t olen = old_length; /*if (st->magic_samples[i])*/ { /* Try and remove the magic samples as if nothing had happened */ /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */ olen = old_length + 2*st->magic_samples[i]; for (j=old_length-2+st->magic_samples[i];j>=0;j--) st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j]; for (j=0;jmagic_samples[i];j++) st->mem[i*st->mem_alloc_size+j] = 0; st->magic_samples[i] = 0; } if (st->filt_len > olen) { /* If the new filter length is still bigger than the "augmented" length */ /* Copy data going backward */ for (j=0;jmem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)]; /* Then put zeros for lack of anything better */ for (;jfilt_len-1;j++) st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0; /* Adjust last_sample */ st->last_sample[i] += (st->filt_len - olen)/2; } else { /* Put back some of the magic! */ st->magic_samples[i] = (olen - st->filt_len)/2; for (j=0;jfilt_len-1+st->magic_samples[i];j++) st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; } } } else if (st->filt_len < old_length) { spx_uint32_t i; /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic" samples so they can be used directly as input the next time(s) */ for (i=0;inb_channels;i++) { spx_uint32_t j; spx_uint32_t old_magic = st->magic_samples[i]; st->magic_samples[i] = (old_length - st->filt_len)/2; /* We must copy some of the memory that's no longer used */ /* Copy data going backward */ for (j=0;jfilt_len-1+st->magic_samples[i]+old_magic;j++) st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; st->magic_samples[i] += old_magic; } } } EXPORT SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err) { return speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err); } EXPORT SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err) { spx_uint32_t i; SpeexResamplerState *st; if (quality > 10 || quality < 0) { if (err) *err = RESAMPLER_ERR_INVALID_ARG; return NULL; } st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState)); st->initialised = 0; st->started = 0; st->in_rate = 0; st->out_rate = 0; st->num_rate = 0; st->den_rate = 0; st->quality = -1; st->sinc_table_length = 0; st->mem_alloc_size = 0; st->filt_len = 0; st->mem = 0; st->resampler_ptr = 0; st->cutoff = 1.f; st->nb_channels = nb_channels; st->in_stride = 1; st->out_stride = 1; #ifdef FIXED_POINT st->buffer_size = 160; #else st->buffer_size = 160; #endif /* Per channel data */ st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(int)); st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(int)); st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(int)); for (i=0;ilast_sample[i] = 0; st->magic_samples[i] = 0; st->samp_frac_num[i] = 0; } speex_resampler_set_quality(st, quality); speex_resampler_set_rate_frac(st, ratio_num, ratio_den, in_rate, out_rate); update_filter(st); st->initialised = 1; if (err) *err = RESAMPLER_ERR_SUCCESS; return st; } EXPORT void speex_resampler_destroy(SpeexResamplerState *st) { speex_free(st->mem); speex_free(st->sinc_table); speex_free(st->last_sample); speex_free(st->magic_samples); speex_free(st->samp_frac_num); speex_free(st); } static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t channel_index, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) { int j=0; const int N = st->filt_len; int out_sample = 0; spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size; spx_uint32_t ilen; st->started = 1; /* Call the right resampler through the function ptr */ out_sample = st->resampler_ptr(st, channel_index, mem, in_len, out, out_len); if (st->last_sample[channel_index] < (spx_int32_t)*in_len) *in_len = st->last_sample[channel_index]; *out_len = out_sample; st->last_sample[channel_index] -= *in_len; ilen = *in_len; for(j=0;jmagic_samples[channel_index]; spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size; const int N = st->filt_len; speex_resampler_process_native(st, channel_index, &tmp_in_len, *out, &out_len); st->magic_samples[channel_index] -= tmp_in_len; /* If we couldn't process all "magic" input samples, save the rest for next time */ if (st->magic_samples[channel_index]) { spx_uint32_t i; for (i=0;imagic_samples[channel_index];i++) mem[N-1+i]=mem[N-1+i+tmp_in_len]; } *out += out_len*st->out_stride; return out_len; } #ifdef FIXED_POINT EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) #else EXPORT int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) #endif { int j; spx_uint32_t ilen = *in_len; spx_uint32_t olen = *out_len; spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size; const int filt_offs = st->filt_len - 1; const spx_uint32_t xlen = st->mem_alloc_size - filt_offs; const int istride = st->in_stride; if (st->magic_samples[channel_index]) olen -= speex_resampler_magic(st, channel_index, &out, olen); if (! st->magic_samples[channel_index]) { while (ilen && olen) { spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen; spx_uint32_t ochunk = olen; if (in) { for(j=0;jout_stride; if (in) in += ichunk * istride; } } *in_len -= ilen; *out_len -= olen; return RESAMPLER_ERR_SUCCESS; } #ifdef FIXED_POINT EXPORT int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) #else EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) #endif { int j; const int istride_save = st->in_stride; const int ostride_save = st->out_stride; spx_uint32_t ilen = *in_len; spx_uint32_t olen = *out_len; spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size; const spx_uint32_t xlen = st->mem_alloc_size - (st->filt_len - 1); #ifdef VAR_ARRAYS const unsigned int ylen = (olen < FIXED_STACK_ALLOC) ? olen : FIXED_STACK_ALLOC; VARDECL(spx_word16_t *ystack); ALLOC(ystack, ylen, spx_word16_t); #else const unsigned int ylen = FIXED_STACK_ALLOC; spx_word16_t ystack[FIXED_STACK_ALLOC]; #endif st->out_stride = 1; while (ilen && olen) { spx_word16_t *y = ystack; spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen; spx_uint32_t ochunk = (olen > ylen) ? ylen : olen; spx_uint32_t omagic = 0; if (st->magic_samples[channel_index]) { omagic = speex_resampler_magic(st, channel_index, &y, ochunk); ochunk -= omagic; olen -= omagic; } if (! st->magic_samples[channel_index]) { if (in) { for(j=0;jfilt_len-1]=WORD2INT(in[j*istride_save]); #else x[j+st->filt_len-1]=in[j*istride_save]; #endif } else { for(j=0;jfilt_len-1]=0; } speex_resampler_process_native(st, channel_index, &ichunk, y, &ochunk); } else { ichunk = 0; ochunk = 0; } for (j=0;jout_stride = ostride_save; *in_len -= ilen; *out_len -= olen; return RESAMPLER_ERR_SUCCESS; } EXPORT int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) { spx_uint32_t i; int istride_save, ostride_save; spx_uint32_t bak_len = *out_len; istride_save = st->in_stride; ostride_save = st->out_stride; st->in_stride = st->out_stride = st->nb_channels; for (i=0;inb_channels;i++) { *out_len = bak_len; if (in != NULL) speex_resampler_process_float(st, i, in+i, in_len, out+i, out_len); else speex_resampler_process_float(st, i, NULL, in_len, out+i, out_len); } st->in_stride = istride_save; st->out_stride = ostride_save; return RESAMPLER_ERR_SUCCESS; } EXPORT int speex_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) { spx_uint32_t i; int istride_save, ostride_save; spx_uint32_t bak_len = *out_len; istride_save = st->in_stride; ostride_save = st->out_stride; st->in_stride = st->out_stride = st->nb_channels; for (i=0;inb_channels;i++) { *out_len = bak_len; if (in != NULL) speex_resampler_process_int(st, i, in+i, in_len, out+i, out_len); else speex_resampler_process_int(st, i, NULL, in_len, out+i, out_len); } st->in_stride = istride_save; st->out_stride = ostride_save; return RESAMPLER_ERR_SUCCESS; } EXPORT int speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate) { return speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate); } EXPORT void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, spx_uint32_t *out_rate) { *in_rate = st->in_rate; *out_rate = st->out_rate; } EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate) { spx_uint32_t fact; spx_uint32_t old_den; spx_uint32_t i; if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den) return RESAMPLER_ERR_SUCCESS; old_den = st->den_rate; st->in_rate = in_rate; st->out_rate = out_rate; st->num_rate = ratio_num; st->den_rate = ratio_den; /* FIXME: This is terribly inefficient, but who cares (at least for now)? */ for (fact=2;fact<=IMIN(st->num_rate, st->den_rate);fact++) { while ((st->num_rate % fact == 0) && (st->den_rate % fact == 0)) { st->num_rate /= fact; st->den_rate /= fact; } } if (old_den > 0) { for (i=0;inb_channels;i++) { st->samp_frac_num[i]=st->samp_frac_num[i]*st->den_rate/old_den; /* Safety net */ if (st->samp_frac_num[i] >= st->den_rate) st->samp_frac_num[i] = st->den_rate-1; } } if (st->initialised) update_filter(st); return RESAMPLER_ERR_SUCCESS; } EXPORT void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, spx_uint32_t *ratio_den) { *ratio_num = st->num_rate; *ratio_den = st->den_rate; } EXPORT int speex_resampler_set_quality(SpeexResamplerState *st, int quality) { if (quality > 10 || quality < 0) return RESAMPLER_ERR_INVALID_ARG; if (st->quality == quality) return RESAMPLER_ERR_SUCCESS; st->quality = quality; if (st->initialised) update_filter(st); return RESAMPLER_ERR_SUCCESS; } EXPORT void speex_resampler_get_quality(SpeexResamplerState *st, int *quality) { *quality = st->quality; } EXPORT void speex_resampler_set_input_stride(SpeexResamplerState *st, spx_uint32_t stride) { st->in_stride = stride; } EXPORT void speex_resampler_get_input_stride(SpeexResamplerState *st, spx_uint32_t *stride) { *stride = st->in_stride; } EXPORT void speex_resampler_set_output_stride(SpeexResamplerState *st, spx_uint32_t stride) { st->out_stride = stride; } EXPORT void speex_resampler_get_output_stride(SpeexResamplerState *st, spx_uint32_t *stride) { *stride = st->out_stride; } EXPORT int speex_resampler_get_input_latency(SpeexResamplerState *st) { return st->filt_len / 2; } EXPORT int speex_resampler_get_output_latency(SpeexResamplerState *st) { return ((st->filt_len / 2) * st->den_rate + (st->num_rate >> 1)) / st->num_rate; } EXPORT int speex_resampler_skip_zeros(SpeexResamplerState *st) { spx_uint32_t i; for (i=0;inb_channels;i++) st->last_sample[i] = st->filt_len/2; return RESAMPLER_ERR_SUCCESS; } EXPORT int speex_resampler_reset_mem(SpeexResamplerState *st) { spx_uint32_t i; for (i=0;inb_channels*(st->filt_len-1);i++) st->mem[i] = 0; return RESAMPLER_ERR_SUCCESS; } EXPORT const char *speex_resampler_strerror(int err) { switch (err) { case RESAMPLER_ERR_SUCCESS: return "Success."; case RESAMPLER_ERR_ALLOC_FAILED: return "Memory allocation failed."; case RESAMPLER_ERR_BAD_STATE: return "Bad resampler state."; case RESAMPLER_ERR_INVALID_ARG: return "Invalid argument."; case RESAMPLER_ERR_PTR_OVERLAP: return "Input and output buffers overlap."; default: return "Unknown error. Bad error code or strange version mismatch."; } } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/resample_sse.h ================================================ /* Copyright (C) 2007-2008 Jean-Marc Valin * Copyright (C) 2008 Thorvald Natvig */ /** @file resample_sse.h @brief Resampler functions (SSE version) */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #define OVERRIDE_INNER_PRODUCT_SINGLE static inline float inner_product_single(const float *a, const float *b, unsigned int len) { int i; float ret; __m128 sum = _mm_setzero_ps(); for (i=0;i #define OVERRIDE_INNER_PRODUCT_DOUBLE static inline double inner_product_double(const float *a, const float *b, unsigned int len) { int i; double ret; __m128d sum = _mm_setzero_pd(); __m128 t; for (i=0;i #include "sb_celp.h" #include "filters.h" #include "lpc.h" #include "lsp.h" #include "stack_alloc.h" #include "cb_search.h" #include "quant_lsp.h" #include "vq.h" #include "ltp.h" #include "arch.h" #include "math_approx.h" #include "os_support.h" #ifndef NULL #define NULL 0 #endif /* Default size for the encoder and decoder stack (can be changed at compile time). This does not apply when using variable-size arrays or alloca. */ #ifndef SB_ENC_STACK #define SB_ENC_STACK (10000*sizeof(spx_sig_t)) #endif #ifndef SB_DEC_STACK #define SB_DEC_STACK (6000*sizeof(spx_sig_t)) #endif #ifdef DISABLE_WIDEBAND void *sb_encoder_init(const SpeexMode *m) { speex_fatal("Wideband and Ultra-wideband are disabled"); return NULL; } void sb_encoder_destroy(void *state) { speex_fatal("Wideband and Ultra-wideband are disabled"); } int sb_encode(void *state, void *vin, SpeexBits *bits) { speex_fatal("Wideband and Ultra-wideband are disabled"); return -2; } void *sb_decoder_init(const SpeexMode *m) { speex_fatal("Wideband and Ultra-wideband are disabled"); return NULL; } void sb_decoder_destroy(void *state) { speex_fatal("Wideband and Ultra-wideband are disabled"); } int sb_decode(void *state, SpeexBits *bits, void *vout) { speex_fatal("Wideband and Ultra-wideband are disabled"); return -2; } int sb_encoder_ctl(void *state, int request, void *ptr) { speex_fatal("Wideband and Ultra-wideband are disabled"); return -2; } int sb_decoder_ctl(void *state, int request, void *ptr) { speex_fatal("Wideband and Ultra-wideband are disabled"); return -2; } #else #ifndef M_PI #define M_PI 3.14159265358979323846 /* pi */ #endif #define sqr(x) ((x)*(x)) #define SUBMODE(x) st->submodes[st->submodeID]->x #ifdef FIXED_POINT static const spx_word16_t gc_quant_bound[16] = {125, 164, 215, 282, 370, 484, 635, 832, 1090, 1428, 1871, 2452, 3213, 4210, 5516, 7228}; static const spx_word16_t fold_quant_bound[32] = { 39, 44, 50, 57, 64, 73, 83, 94, 106, 120, 136, 154, 175, 198, 225, 255, 288, 327, 370, 420, 476, 539, 611, 692, 784, 889, 1007, 1141, 1293, 1465, 1660, 1881}; #define LSP_MARGIN 410 #define LSP_DELTA1 6553 #define LSP_DELTA2 1638 #else static const spx_word16_t gc_quant_bound[16] = { 0.97979, 1.28384, 1.68223, 2.20426, 2.88829, 3.78458, 4.95900, 6.49787, 8.51428, 11.15642, 14.61846, 19.15484, 25.09895, 32.88761, 43.09325, 56.46588}; static const spx_word16_t fold_quant_bound[32] = { 0.30498, 0.34559, 0.39161, 0.44375, 0.50283, 0.56979, 0.64565, 0.73162, 0.82903, 0.93942, 1.06450, 1.20624, 1.36685, 1.54884, 1.75506, 1.98875, 2.25355, 2.55360, 2.89361, 3.27889, 3.71547, 4.21018, 4.77076, 5.40598, 6.12577, 6.94141, 7.86565, 8.91295, 10.09969, 11.44445, 12.96826, 14.69497}; #define LSP_MARGIN .05 #define LSP_DELTA1 .2 #define LSP_DELTA2 .05 #endif #define QMF_ORDER 64 #ifdef FIXED_POINT static const spx_word16_t h0[64] = {2, -7, -7, 18, 15, -39, -25, 75, 35, -130, -41, 212, 38, -327, -17, 483, -32, -689, 124, 956, -283, -1307, 543, 1780, -973, -2467, 1733, 3633, -3339, -6409, 9059, 30153, 30153, 9059, -6409, -3339, 3633, 1733, -2467, -973, 1780, 543, -1307, -283, 956, 124, -689, -32, 483, -17, -327, 38, 212, -41, -130, 35, 75, -25, -39, 15, 18, -7, -7, 2}; #else static const float h0[64] = { 3.596189e-05f, -0.0001123515f, -0.0001104587f, 0.0002790277f, 0.0002298438f, -0.0005953563f, -0.0003823631f, 0.00113826f, 0.0005308539f, -0.001986177f, -0.0006243724f, 0.003235877f, 0.0005743159f, -0.004989147f, -0.0002584767f, 0.007367171f, -0.0004857935f, -0.01050689f, 0.001894714f, 0.01459396f, -0.004313674f, -0.01994365f, 0.00828756f, 0.02716055f, -0.01485397f, -0.03764973f, 0.026447f, 0.05543245f, -0.05095487f, -0.09779096f, 0.1382363f, 0.4600981f, 0.4600981f, 0.1382363f, -0.09779096f, -0.05095487f, 0.05543245f, 0.026447f, -0.03764973f, -0.01485397f, 0.02716055f, 0.00828756f, -0.01994365f, -0.004313674f, 0.01459396f, 0.001894714f, -0.01050689f, -0.0004857935f, 0.007367171f, -0.0002584767f, -0.004989147f, 0.0005743159f, 0.003235877f, -0.0006243724f, -0.001986177f, 0.0005308539f, 0.00113826f, -0.0003823631f, -0.0005953563f, 0.0002298438f, 0.0002790277f, -0.0001104587f, -0.0001123515f, 3.596189e-05f }; #endif extern const spx_word16_t lag_window[]; extern const spx_word16_t lpc_window[]; void *sb_encoder_init(const SpeexMode *m) { int i; spx_int32_t tmp; SBEncState *st; const SpeexSBMode *mode; st = (SBEncState*)speex_alloc(sizeof(SBEncState)); if (!st) return NULL; st->mode = m; mode = (const SpeexSBMode*)m->mode; st->st_low = speex_encoder_init(mode->nb_mode); #if defined(VAR_ARRAYS) || defined (USE_ALLOCA) st->stack = NULL; #else /*st->stack = (char*)speex_alloc_scratch(SB_ENC_STACK);*/ speex_encoder_ctl(st->st_low, SPEEX_GET_STACK, &st->stack); #endif st->full_frame_size = 2*mode->frameSize; st->frame_size = mode->frameSize; st->subframeSize = mode->subframeSize; st->nbSubframes = mode->frameSize/mode->subframeSize; st->windowSize = st->frame_size+st->subframeSize; st->lpcSize=mode->lpcSize; st->encode_submode = 1; st->submodes=mode->submodes; st->submodeSelect = st->submodeID=mode->defaultSubmode; tmp=9; speex_encoder_ctl(st->st_low, SPEEX_SET_QUALITY, &tmp); tmp=1; speex_encoder_ctl(st->st_low, SPEEX_SET_WIDEBAND, &tmp); st->lpc_floor = mode->lpc_floor; st->gamma1=mode->gamma1; st->gamma2=mode->gamma2; st->first=1; st->high=(spx_word16_t*)speex_alloc((st->windowSize-st->frame_size)*sizeof(spx_word16_t)); st->h0_mem=(spx_word16_t*)speex_alloc((QMF_ORDER)*sizeof(spx_word16_t)); st->h1_mem=(spx_word16_t*)speex_alloc((QMF_ORDER)*sizeof(spx_word16_t)); st->window= lpc_window; st->lagWindow = lag_window; st->old_lsp = (spx_lsp_t*)speex_alloc(st->lpcSize*sizeof(spx_lsp_t)); st->old_qlsp = (spx_lsp_t*)speex_alloc(st->lpcSize*sizeof(spx_lsp_t)); st->interp_qlpc = (spx_coef_t*)speex_alloc(st->lpcSize*sizeof(spx_coef_t)); st->pi_gain = (spx_word32_t*)speex_alloc((st->nbSubframes)*sizeof(spx_word32_t)); st->exc_rms = (spx_word16_t*)speex_alloc((st->nbSubframes)*sizeof(spx_word16_t)); st->innov_rms_save = NULL; st->mem_sp = (spx_mem_t*)speex_alloc((st->lpcSize)*sizeof(spx_mem_t)); st->mem_sp2 = (spx_mem_t*)speex_alloc((st->lpcSize)*sizeof(spx_mem_t)); st->mem_sw = (spx_mem_t*)speex_alloc((st->lpcSize)*sizeof(spx_mem_t)); for (i=0;ilpcSize;i++) st->old_lsp[i]= DIV32(MULT16_16(QCONST16(3.1415927f, LSP_SHIFT), i+1), st->lpcSize+1); #ifndef DISABLE_VBR st->vbr_quality = 8; st->vbr_enabled = 0; st->vbr_max = 0; st->vbr_max_high = 20000; /* We just need a big value here */ st->vad_enabled = 0; st->abr_enabled = 0; st->relative_quality=0; #endif /* #ifndef DISABLE_VBR */ st->complexity=2; speex_encoder_ctl(st->st_low, SPEEX_GET_SAMPLING_RATE, &st->sampling_rate); st->sampling_rate*=2; #ifdef ENABLE_VALGRIND VALGRIND_MAKE_READABLE(st, (st->stack-(char*)st)); #endif return st; } void sb_encoder_destroy(void *state) { SBEncState *st=(SBEncState*)state; speex_encoder_destroy(st->st_low); #if !(defined(VAR_ARRAYS) || defined (USE_ALLOCA)) /*speex_free_scratch(st->stack);*/ #endif speex_free(st->high); speex_free(st->h0_mem); speex_free(st->h1_mem); speex_free(st->old_lsp); speex_free(st->old_qlsp); speex_free(st->interp_qlpc); speex_free(st->pi_gain); speex_free(st->exc_rms); speex_free(st->mem_sp); speex_free(st->mem_sp2); speex_free(st->mem_sw); speex_free(st); } int sb_encode(void *state, void *vin, SpeexBits *bits) { SBEncState *st; int i, roots, sub; char *stack; VARDECL(spx_mem_t *mem); VARDECL(spx_sig_t *innov); VARDECL(spx_word16_t *target); VARDECL(spx_word16_t *syn_resp); VARDECL(spx_word32_t *low_pi_gain); spx_word16_t *low; spx_word16_t *high; VARDECL(spx_word16_t *low_exc_rms); VARDECL(spx_word16_t *low_innov_rms); const SpeexSBMode *mode; spx_int32_t dtx; spx_word16_t *in = (spx_word16_t*)vin; spx_word16_t e_low=0, e_high=0; VARDECL(spx_coef_t *lpc); VARDECL(spx_coef_t *interp_lpc); VARDECL(spx_coef_t *bw_lpc1); VARDECL(spx_coef_t *bw_lpc2); VARDECL(spx_lsp_t *lsp); VARDECL(spx_lsp_t *qlsp); VARDECL(spx_lsp_t *interp_lsp); VARDECL(spx_lsp_t *interp_qlsp); st = (SBEncState*)state; stack=st->stack; mode = (const SpeexSBMode*)(st->mode->mode); low = in; high = in+st->frame_size; /* High-band buffering / sync with low band */ /* Compute the two sub-bands by filtering with QMF h0*/ qmf_decomp(in, h0, low, high, st->full_frame_size, QMF_ORDER, st->h0_mem, stack); #ifndef DISABLE_VBR if (st->vbr_enabled || st->vad_enabled) { /* Need to compute things here before the signal is trashed by the encoder */ /*FIXME: Are the two signals (low, high) in sync? */ e_low = compute_rms16(low, st->frame_size); e_high = compute_rms16(high, st->frame_size); } #endif /* #ifndef DISABLE_VBR */ ALLOC(low_innov_rms, st->nbSubframes, spx_word16_t); speex_encoder_ctl(st->st_low, SPEEX_SET_INNOVATION_SAVE, low_innov_rms); /* Encode the narrowband part*/ speex_encode_native(st->st_low, low, bits); high = high - (st->windowSize-st->frame_size); SPEEX_COPY(high, st->high, st->windowSize-st->frame_size); SPEEX_COPY(st->high, &high[st->frame_size], st->windowSize-st->frame_size); ALLOC(low_pi_gain, st->nbSubframes, spx_word32_t); ALLOC(low_exc_rms, st->nbSubframes, spx_word16_t); speex_encoder_ctl(st->st_low, SPEEX_GET_PI_GAIN, low_pi_gain); speex_encoder_ctl(st->st_low, SPEEX_GET_EXC, low_exc_rms); speex_encoder_ctl(st->st_low, SPEEX_GET_LOW_MODE, &dtx); if (dtx==0) dtx=1; else dtx=0; ALLOC(lpc, st->lpcSize, spx_coef_t); ALLOC(interp_lpc, st->lpcSize, spx_coef_t); ALLOC(bw_lpc1, st->lpcSize, spx_coef_t); ALLOC(bw_lpc2, st->lpcSize, spx_coef_t); ALLOC(lsp, st->lpcSize, spx_lsp_t); ALLOC(qlsp, st->lpcSize, spx_lsp_t); ALLOC(interp_lsp, st->lpcSize, spx_lsp_t); ALLOC(interp_qlsp, st->lpcSize, spx_lsp_t); { VARDECL(spx_word16_t *autocorr); VARDECL(spx_word16_t *w_sig); ALLOC(autocorr, st->lpcSize+1, spx_word16_t); ALLOC(w_sig, st->windowSize, spx_word16_t); /* Window for analysis */ /* FIXME: This is a kludge */ if (st->subframeSize==80) { for (i=0;iwindowSize;i++) w_sig[i] = EXTRACT16(SHR32(MULT16_16(high[i],st->window[i>>1]),SIG_SHIFT)); } else { for (i=0;iwindowSize;i++) w_sig[i] = EXTRACT16(SHR32(MULT16_16(high[i],st->window[i]),SIG_SHIFT)); } /* Compute auto-correlation */ _spx_autocorr(w_sig, autocorr, st->lpcSize+1, st->windowSize); autocorr[0] = ADD16(autocorr[0],MULT16_16_Q15(autocorr[0],st->lpc_floor)); /* Noise floor in auto-correlation domain */ /* Lag windowing: equivalent to filtering in the power-spectrum domain */ for (i=0;ilpcSize+1;i++) autocorr[i] = MULT16_16_Q14(autocorr[i],st->lagWindow[i]); /* Levinson-Durbin */ _spx_lpc(lpc, autocorr, st->lpcSize); } /* LPC to LSPs (x-domain) transform */ roots=lpc_to_lsp (lpc, st->lpcSize, lsp, 10, LSP_DELTA1, stack); if (roots!=st->lpcSize) { roots = lpc_to_lsp (lpc, st->lpcSize, lsp, 10, LSP_DELTA2, stack); if (roots!=st->lpcSize) { /*If we can't find all LSP's, do some damage control and use a flat filter*/ for (i=0;ilpcSize;i++) { lsp[i]=st->old_lsp[i]; } } } #ifndef DISABLE_VBR /* VBR code */ if ((st->vbr_enabled || st->vad_enabled) && !dtx) { float ratio; if (st->abr_enabled) { float qual_change=0; if (st->abr_drift2 * st->abr_drift > 0) { /* Only adapt if long-term and short-term drift are the same sign */ qual_change = -.00001*st->abr_drift/(1+st->abr_count); if (qual_change>.1) qual_change=.1; if (qual_change<-.1) qual_change=-.1; } st->vbr_quality += qual_change; if (st->vbr_quality>10) st->vbr_quality=10; if (st->vbr_quality<0) st->vbr_quality=0; } ratio = 2*log((1.f+e_high)/(1.f+e_low)); speex_encoder_ctl(st->st_low, SPEEX_GET_RELATIVE_QUALITY, &st->relative_quality); if (ratio<-4) ratio=-4; if (ratio>2) ratio=2; /*if (ratio>-2)*/ if (st->vbr_enabled) { spx_int32_t modeid; modeid = mode->nb_modes-1; st->relative_quality+=1.0*(ratio+2); if (st->relative_quality<-1) st->relative_quality=-1; while (modeid) { int v1; float thresh; v1=(int)floor(st->vbr_quality); if (v1==10) thresh = mode->vbr_thresh[modeid][v1]; else thresh = (st->vbr_quality-v1) * mode->vbr_thresh[modeid][v1+1] + (1+v1-st->vbr_quality) * mode->vbr_thresh[modeid][v1]; if (st->relative_quality >= thresh && st->sampling_rate*st->submodes[modeid]->bits_per_frame/st->full_frame_size <= st->vbr_max_high) break; modeid--; } speex_encoder_ctl(state, SPEEX_SET_HIGH_MODE, &modeid); if (st->abr_enabled) { spx_int32_t bitrate; speex_encoder_ctl(state, SPEEX_GET_BITRATE, &bitrate); st->abr_drift+=(bitrate-st->abr_enabled); st->abr_drift2 = .95*st->abr_drift2 + .05*(bitrate-st->abr_enabled); st->abr_count += 1.0; } } else { /* VAD only */ int modeid; if (st->relative_quality<2.0) modeid=1; else modeid=st->submodeSelect; /*speex_encoder_ctl(state, SPEEX_SET_MODE, &mode);*/ st->submodeID=modeid; } /*fprintf (stderr, "%f %f\n", ratio, low_qual);*/ } #endif /* #ifndef DISABLE_VBR */ if (st->encode_submode) { speex_bits_pack(bits, 1, 1); if (dtx) speex_bits_pack(bits, 0, SB_SUBMODE_BITS); else speex_bits_pack(bits, st->submodeID, SB_SUBMODE_BITS); } /* If null mode (no transmission), just set a couple things to zero*/ if (dtx || st->submodes[st->submodeID] == NULL) { for (i=0;iframe_size;i++) high[i]=VERY_SMALL; for (i=0;ilpcSize;i++) st->mem_sw[i]=0; st->first=1; /* Final signal synthesis from excitation */ iir_mem16(high, st->interp_qlpc, high, st->frame_size, st->lpcSize, st->mem_sp, stack); if (dtx) return 0; else return 1; } /* LSP quantization */ SUBMODE(lsp_quant)(lsp, qlsp, st->lpcSize, bits); if (st->first) { for (i=0;ilpcSize;i++) st->old_lsp[i] = lsp[i]; for (i=0;ilpcSize;i++) st->old_qlsp[i] = qlsp[i]; } ALLOC(mem, st->lpcSize, spx_mem_t); ALLOC(syn_resp, st->subframeSize, spx_word16_t); ALLOC(innov, st->subframeSize, spx_sig_t); ALLOC(target, st->subframeSize, spx_word16_t); for (sub=0;subnbSubframes;sub++) { VARDECL(spx_word16_t *exc); VARDECL(spx_word16_t *res); VARDECL(spx_word16_t *sw); spx_word16_t *sp; spx_word16_t filter_ratio; /*Q7*/ int offset; spx_word32_t rl, rh; /*Q13*/ spx_word16_t eh=0; offset = st->subframeSize*sub; sp=high+offset; ALLOC(exc, st->subframeSize, spx_word16_t); ALLOC(res, st->subframeSize, spx_word16_t); ALLOC(sw, st->subframeSize, spx_word16_t); /* LSP interpolation (quantized and unquantized) */ lsp_interpolate(st->old_lsp, lsp, interp_lsp, st->lpcSize, sub, st->nbSubframes); lsp_interpolate(st->old_qlsp, qlsp, interp_qlsp, st->lpcSize, sub, st->nbSubframes); lsp_enforce_margin(interp_lsp, st->lpcSize, LSP_MARGIN); lsp_enforce_margin(interp_qlsp, st->lpcSize, LSP_MARGIN); lsp_to_lpc(interp_lsp, interp_lpc, st->lpcSize,stack); lsp_to_lpc(interp_qlsp, st->interp_qlpc, st->lpcSize, stack); bw_lpc(st->gamma1, interp_lpc, bw_lpc1, st->lpcSize); bw_lpc(st->gamma2, interp_lpc, bw_lpc2, st->lpcSize); /* Compute mid-band (4000 Hz for wideband) response of low-band and high-band filters */ st->pi_gain[sub]=LPC_SCALING; rh = LPC_SCALING; for (i=0;ilpcSize;i+=2) { rh += st->interp_qlpc[i+1] - st->interp_qlpc[i]; st->pi_gain[sub] += st->interp_qlpc[i] + st->interp_qlpc[i+1]; } rl = low_pi_gain[sub]; #ifdef FIXED_POINT filter_ratio=EXTRACT16(SATURATE(PDIV32(SHL32(ADD32(rl,82),7),ADD32(82,rh)),32767)); #else filter_ratio=(rl+.01)/(rh+.01); #endif /* Compute "real excitation" */ fir_mem16(sp, st->interp_qlpc, exc, st->subframeSize, st->lpcSize, st->mem_sp2, stack); /* Compute energy of low-band and high-band excitation */ eh = compute_rms16(exc, st->subframeSize); if (!SUBMODE(innovation_quant)) {/* 1 for spectral folding excitation, 0 for stochastic */ spx_word32_t g; /*Q7*/ spx_word16_t el; /*Q0*/ el = low_innov_rms[sub]; /* Gain to use if we want to use the low-band excitation for high-band */ g=PDIV32(MULT16_16(filter_ratio,eh),EXTEND32(ADD16(1,el))); #if 0 { char *tmp_stack=stack; float *tmp_sig; float g2; ALLOC(tmp_sig, st->subframeSize, spx_sig_t); for (i=0;ilpcSize;i++) mem[i]=st->mem_sp[i]; iir_mem2(st->low_innov+offset, st->interp_qlpc, tmp_sig, st->subframeSize, st->lpcSize, mem); g2 = compute_rms(sp, st->subframeSize)/(.01+compute_rms(tmp_sig, st->subframeSize)); /*fprintf (stderr, "gains: %f %f\n", g, g2);*/ g = g2; stack = tmp_stack; } #endif /*print_vec(&g, 1, "gain factor");*/ /* Gain quantization */ { int quant = scal_quant(g, fold_quant_bound, 32); /*speex_warning_int("tata", quant);*/ if (quant<0) quant=0; if (quant>31) quant=31; speex_bits_pack(bits, quant, 5); } if (st->innov_rms_save) { st->innov_rms_save[sub] = eh; } st->exc_rms[sub] = eh; } else { spx_word16_t gc; /*Q7*/ spx_word32_t scale; /*Q14*/ spx_word16_t el; /*Q0*/ el = low_exc_rms[sub]; /*Q0*/ gc = PDIV32_16(MULT16_16(filter_ratio,1+eh),1+el); /* This is a kludge that cleans up a historical bug */ if (st->subframeSize==80) gc = MULT16_16_P15(QCONST16(0.70711f,15),gc); /*printf ("%f %f %f %f\n", el, eh, filter_ratio, gc);*/ { int qgc = scal_quant(gc, gc_quant_bound, 16); speex_bits_pack(bits, qgc, 4); gc = MULT16_16_Q15(QCONST16(0.87360,15),gc_quant_bound[qgc]); } if (st->subframeSize==80) gc = MULT16_16_P14(QCONST16(1.4142f,14), gc); scale = SHL32(MULT16_16(PDIV32_16(SHL32(EXTEND32(gc),SIG_SHIFT-6),filter_ratio),(1+el)),6); compute_impulse_response(st->interp_qlpc, bw_lpc1, bw_lpc2, syn_resp, st->subframeSize, st->lpcSize, stack); /* Reset excitation */ for (i=0;isubframeSize;i++) res[i]=VERY_SMALL; /* Compute zero response (ringing) of A(z/g1) / ( A(z/g2) * Aq(z) ) */ for (i=0;ilpcSize;i++) mem[i]=st->mem_sp[i]; iir_mem16(res, st->interp_qlpc, res, st->subframeSize, st->lpcSize, mem, stack); for (i=0;ilpcSize;i++) mem[i]=st->mem_sw[i]; filter_mem16(res, bw_lpc1, bw_lpc2, res, st->subframeSize, st->lpcSize, mem, stack); /* Compute weighted signal */ for (i=0;ilpcSize;i++) mem[i]=st->mem_sw[i]; filter_mem16(sp, bw_lpc1, bw_lpc2, sw, st->subframeSize, st->lpcSize, mem, stack); /* Compute target signal */ for (i=0;isubframeSize;i++) target[i]=SUB16(sw[i],res[i]); signal_div(target, target, scale, st->subframeSize); /* Reset excitation */ SPEEX_MEMSET(innov, 0, st->subframeSize); /*print_vec(target, st->subframeSize, "\ntarget");*/ SUBMODE(innovation_quant)(target, st->interp_qlpc, bw_lpc1, bw_lpc2, SUBMODE(innovation_params), st->lpcSize, st->subframeSize, innov, syn_resp, bits, stack, st->complexity, SUBMODE(double_codebook)); /*print_vec(target, st->subframeSize, "after");*/ signal_mul(innov, innov, scale, st->subframeSize); if (SUBMODE(double_codebook)) { char *tmp_stack=stack; VARDECL(spx_sig_t *innov2); ALLOC(innov2, st->subframeSize, spx_sig_t); SPEEX_MEMSET(innov2, 0, st->subframeSize); for (i=0;isubframeSize;i++) target[i]=MULT16_16_P13(QCONST16(2.5f,13), target[i]); SUBMODE(innovation_quant)(target, st->interp_qlpc, bw_lpc1, bw_lpc2, SUBMODE(innovation_params), st->lpcSize, st->subframeSize, innov2, syn_resp, bits, stack, st->complexity, 0); signal_mul(innov2, innov2, MULT16_32_P15(QCONST16(0.4f,15),scale), st->subframeSize); for (i=0;isubframeSize;i++) innov[i] = ADD32(innov[i],innov2[i]); stack = tmp_stack; } for (i=0;isubframeSize;i++) exc[i] = PSHR32(innov[i],SIG_SHIFT); if (st->innov_rms_save) { st->innov_rms_save[sub] = MULT16_16_Q15(QCONST16(.70711f, 15), compute_rms(innov, st->subframeSize)); } st->exc_rms[sub] = compute_rms16(exc, st->subframeSize); } /*Keep the previous memory*/ for (i=0;ilpcSize;i++) mem[i]=st->mem_sp[i]; /* Final signal synthesis from excitation */ iir_mem16(exc, st->interp_qlpc, sp, st->subframeSize, st->lpcSize, st->mem_sp, stack); /* Compute weighted signal again, from synthesized speech (not sure it's the right thing) */ filter_mem16(sp, bw_lpc1, bw_lpc2, sw, st->subframeSize, st->lpcSize, st->mem_sw, stack); } for (i=0;ilpcSize;i++) st->old_lsp[i] = lsp[i]; for (i=0;ilpcSize;i++) st->old_qlsp[i] = qlsp[i]; st->first=0; return 1; } void *sb_decoder_init(const SpeexMode *m) { spx_int32_t tmp; SBDecState *st; const SpeexSBMode *mode; st = (SBDecState*)speex_alloc(sizeof(SBDecState)); if (!st) return NULL; st->mode = m; mode=(const SpeexSBMode*)m->mode; st->encode_submode = 1; st->st_low = speex_decoder_init(mode->nb_mode); #if defined(VAR_ARRAYS) || defined (USE_ALLOCA) st->stack = NULL; #else /*st->stack = (char*)speex_alloc_scratch(SB_DEC_STACK);*/ speex_decoder_ctl(st->st_low, SPEEX_GET_STACK, &st->stack); #endif st->full_frame_size = 2*mode->frameSize; st->frame_size = mode->frameSize; st->subframeSize = mode->subframeSize; st->nbSubframes = mode->frameSize/mode->subframeSize; st->lpcSize=mode->lpcSize; speex_decoder_ctl(st->st_low, SPEEX_GET_SAMPLING_RATE, &st->sampling_rate); st->sampling_rate*=2; tmp=1; speex_decoder_ctl(st->st_low, SPEEX_SET_WIDEBAND, &tmp); st->submodes=mode->submodes; st->submodeID=mode->defaultSubmode; st->first=1; st->g0_mem = (spx_word16_t*)speex_alloc((QMF_ORDER)*sizeof(spx_word16_t)); st->g1_mem = (spx_word16_t*)speex_alloc((QMF_ORDER)*sizeof(spx_word16_t)); st->excBuf = (spx_word16_t*)speex_alloc((st->subframeSize)*sizeof(spx_word16_t)); st->old_qlsp = (spx_lsp_t*)speex_alloc((st->lpcSize)*sizeof(spx_lsp_t)); st->interp_qlpc = (spx_coef_t*)speex_alloc(st->lpcSize*sizeof(spx_coef_t)); st->pi_gain = (spx_word32_t*)speex_alloc((st->nbSubframes)*sizeof(spx_word32_t)); st->exc_rms = (spx_word16_t*)speex_alloc((st->nbSubframes)*sizeof(spx_word16_t)); st->mem_sp = (spx_mem_t*)speex_alloc((2*st->lpcSize)*sizeof(spx_mem_t)); st->innov_save = NULL; st->lpc_enh_enabled=0; st->seed = 1000; #ifdef ENABLE_VALGRIND VALGRIND_MAKE_READABLE(st, (st->stack-(char*)st)); #endif return st; } void sb_decoder_destroy(void *state) { SBDecState *st; st = (SBDecState*)state; speex_decoder_destroy(st->st_low); #if !(defined(VAR_ARRAYS) || defined (USE_ALLOCA)) /*speex_free_scratch(st->stack);*/ #endif speex_free(st->g0_mem); speex_free(st->g1_mem); speex_free(st->excBuf); speex_free(st->old_qlsp); speex_free(st->interp_qlpc); speex_free(st->pi_gain); speex_free(st->exc_rms); speex_free(st->mem_sp); speex_free(state); } static void sb_decode_lost(SBDecState *st, spx_word16_t *out, int dtx, char *stack) { int i; int saved_modeid=0; if (dtx) { saved_modeid=st->submodeID; st->submodeID=1; } else { bw_lpc(QCONST16(0.99f,15), st->interp_qlpc, st->interp_qlpc, st->lpcSize); } st->first=1; /* Final signal synthesis from excitation */ if (!dtx) { st->last_ener = MULT16_16_Q15(QCONST16(.9f,15),st->last_ener); } for (i=0;iframe_size;i++) out[i+st->frame_size] = speex_rand(st->last_ener, &st->seed); iir_mem16(out+st->frame_size, st->interp_qlpc, out+st->frame_size, st->frame_size, st->lpcSize, st->mem_sp, stack); /* Reconstruct the original */ qmf_synth(out, out+st->frame_size, h0, out, st->full_frame_size, QMF_ORDER, st->g0_mem, st->g1_mem, stack); if (dtx) { st->submodeID=saved_modeid; } return; } int sb_decode(void *state, SpeexBits *bits, void *vout) { int i, sub; SBDecState *st; int wideband; int ret; char *stack; VARDECL(spx_word32_t *low_pi_gain); VARDECL(spx_word16_t *low_exc_rms); VARDECL(spx_coef_t *ak); VARDECL(spx_lsp_t *qlsp); VARDECL(spx_lsp_t *interp_qlsp); spx_int32_t dtx; const SpeexSBMode *mode; spx_word16_t *out = (spx_word16_t*)vout; spx_word16_t *low_innov_alias; spx_word32_t exc_ener_sum = 0; st = (SBDecState*)state; stack=st->stack; mode = (const SpeexSBMode*)(st->mode->mode); low_innov_alias = out+st->frame_size; speex_decoder_ctl(st->st_low, SPEEX_SET_INNOVATION_SAVE, low_innov_alias); /* Decode the low-band */ ret = speex_decode_native(st->st_low, bits, out); speex_decoder_ctl(st->st_low, SPEEX_GET_DTX_STATUS, &dtx); /* If error decoding the narrowband part, propagate error */ if (ret!=0) { return ret; } if (!bits) { sb_decode_lost(st, out, dtx, stack); return 0; } if (st->encode_submode) { /*Check "wideband bit"*/ if (speex_bits_remaining(bits)>0) wideband = speex_bits_peek(bits); else wideband = 0; if (wideband) { /*Regular wideband frame, read the submode*/ wideband = speex_bits_unpack_unsigned(bits, 1); st->submodeID = speex_bits_unpack_unsigned(bits, SB_SUBMODE_BITS); } else { /*Was a narrowband frame, set "null submode"*/ st->submodeID = 0; } if (st->submodeID != 0 && st->submodes[st->submodeID] == NULL) { speex_notify("Invalid mode encountered. The stream is corrupted."); return -2; } } /* If null mode (no transmission), just set a couple things to zero*/ if (st->submodes[st->submodeID] == NULL) { if (dtx) { sb_decode_lost(st, out, 1, stack); return 0; } for (i=0;iframe_size;i++) out[st->frame_size+i]=VERY_SMALL; st->first=1; /* Final signal synthesis from excitation */ iir_mem16(out+st->frame_size, st->interp_qlpc, out+st->frame_size, st->frame_size, st->lpcSize, st->mem_sp, stack); qmf_synth(out, out+st->frame_size, h0, out, st->full_frame_size, QMF_ORDER, st->g0_mem, st->g1_mem, stack); return 0; } ALLOC(low_pi_gain, st->nbSubframes, spx_word32_t); ALLOC(low_exc_rms, st->nbSubframes, spx_word16_t); speex_decoder_ctl(st->st_low, SPEEX_GET_PI_GAIN, low_pi_gain); speex_decoder_ctl(st->st_low, SPEEX_GET_EXC, low_exc_rms); ALLOC(qlsp, st->lpcSize, spx_lsp_t); ALLOC(interp_qlsp, st->lpcSize, spx_lsp_t); SUBMODE(lsp_unquant)(qlsp, st->lpcSize, bits); if (st->first) { for (i=0;ilpcSize;i++) st->old_qlsp[i] = qlsp[i]; } ALLOC(ak, st->lpcSize, spx_coef_t); for (sub=0;subnbSubframes;sub++) { VARDECL(spx_word32_t *exc); spx_word16_t *innov_save=NULL; spx_word16_t *sp; spx_word16_t filter_ratio; spx_word16_t el=0; int offset; spx_word32_t rl=0,rh=0; offset = st->subframeSize*sub; sp=out+st->frame_size+offset; ALLOC(exc, st->subframeSize, spx_word32_t); /* Pointer for saving innovation */ if (st->innov_save) { innov_save = st->innov_save+2*offset; SPEEX_MEMSET(innov_save, 0, 2*st->subframeSize); } /* LSP interpolation */ lsp_interpolate(st->old_qlsp, qlsp, interp_qlsp, st->lpcSize, sub, st->nbSubframes); lsp_enforce_margin(interp_qlsp, st->lpcSize, LSP_MARGIN); /* LSP to LPC */ lsp_to_lpc(interp_qlsp, ak, st->lpcSize, stack); /* Calculate reponse ratio between the low and high filter in the middle of the band (4000 Hz) */ st->pi_gain[sub]=LPC_SCALING; rh = LPC_SCALING; for (i=0;ilpcSize;i+=2) { rh += ak[i+1] - ak[i]; st->pi_gain[sub] += ak[i] + ak[i+1]; } rl = low_pi_gain[sub]; #ifdef FIXED_POINT filter_ratio=EXTRACT16(SATURATE(PDIV32(SHL32(ADD32(rl,82),7),ADD32(82,rh)),32767)); #else filter_ratio=(rl+.01)/(rh+.01); #endif SPEEX_MEMSET(exc, 0, st->subframeSize); if (!SUBMODE(innovation_unquant)) { spx_word32_t g; int quant; quant = speex_bits_unpack_unsigned(bits, 5); g= spx_exp(MULT16_16(QCONST16(.125f,11),(quant-10))); g = PDIV32(g, filter_ratio); for (i=0;isubframeSize;i+=2) { exc[i]=SHL32(MULT16_32_P15(MULT16_16_Q15(mode->folding_gain,low_innov_alias[offset+i]),SHL32(g,6)),SIG_SHIFT); exc[i+1]=NEG32(SHL32(MULT16_32_P15(MULT16_16_Q15(mode->folding_gain,low_innov_alias[offset+i+1]),SHL32(g,6)),SIG_SHIFT)); } } else { spx_word16_t gc; spx_word32_t scale; int qgc = speex_bits_unpack_unsigned(bits, 4); el = low_exc_rms[sub]; gc = MULT16_16_Q15(QCONST16(0.87360,15),gc_quant_bound[qgc]); if (st->subframeSize==80) gc = MULT16_16_P14(QCONST16(1.4142f,14),gc); scale = SHL32(PDIV32(SHL32(MULT16_16(gc, el),3), filter_ratio),SIG_SHIFT-3); SUBMODE(innovation_unquant)(exc, SUBMODE(innovation_params), st->subframeSize, bits, stack, &st->seed); signal_mul(exc,exc,scale,st->subframeSize); if (SUBMODE(double_codebook)) { char *tmp_stack=stack; VARDECL(spx_sig_t *innov2); ALLOC(innov2, st->subframeSize, spx_sig_t); SPEEX_MEMSET(innov2, 0, st->subframeSize); SUBMODE(innovation_unquant)(innov2, SUBMODE(innovation_params), st->subframeSize, bits, stack, &st->seed); signal_mul(innov2, innov2, MULT16_32_P15(QCONST16(0.4f,15),scale), st->subframeSize); for (i=0;isubframeSize;i++) exc[i] = ADD32(exc[i],innov2[i]); stack = tmp_stack; } } if (st->innov_save) { for (i=0;isubframeSize;i++) innov_save[2*i]=EXTRACT16(PSHR32(exc[i],SIG_SHIFT)); } iir_mem16(st->excBuf, st->interp_qlpc, sp, st->subframeSize, st->lpcSize, st->mem_sp, stack); for (i=0;isubframeSize;i++) st->excBuf[i]=EXTRACT16(PSHR32(exc[i],SIG_SHIFT)); for (i=0;ilpcSize;i++) st->interp_qlpc[i] = ak[i]; st->exc_rms[sub] = compute_rms16(st->excBuf, st->subframeSize); exc_ener_sum = ADD32(exc_ener_sum, DIV32(MULT16_16(st->exc_rms[sub],st->exc_rms[sub]), st->nbSubframes)); } st->last_ener = spx_sqrt(exc_ener_sum); qmf_synth(out, out+st->frame_size, h0, out, st->full_frame_size, QMF_ORDER, st->g0_mem, st->g1_mem, stack); for (i=0;ilpcSize;i++) st->old_qlsp[i] = qlsp[i]; st->first=0; return 0; } int sb_encoder_ctl(void *state, int request, void *ptr) { SBEncState *st; st=(SBEncState*)state; switch(request) { case SPEEX_GET_FRAME_SIZE: (*(spx_int32_t*)ptr) = st->full_frame_size; break; case SPEEX_SET_HIGH_MODE: st->submodeSelect = st->submodeID = (*(spx_int32_t*)ptr); break; case SPEEX_SET_LOW_MODE: speex_encoder_ctl(st->st_low, SPEEX_SET_LOW_MODE, ptr); break; case SPEEX_SET_DTX: speex_encoder_ctl(st->st_low, SPEEX_SET_DTX, ptr); break; case SPEEX_GET_DTX: speex_encoder_ctl(st->st_low, SPEEX_GET_DTX, ptr); break; case SPEEX_GET_LOW_MODE: speex_encoder_ctl(st->st_low, SPEEX_GET_LOW_MODE, ptr); break; case SPEEX_SET_MODE: speex_encoder_ctl(st, SPEEX_SET_QUALITY, ptr); break; #ifndef DISABLE_VBR case SPEEX_SET_VBR: st->vbr_enabled = (*(spx_int32_t*)ptr); speex_encoder_ctl(st->st_low, SPEEX_SET_VBR, ptr); break; case SPEEX_GET_VBR: (*(spx_int32_t*)ptr) = st->vbr_enabled; break; case SPEEX_SET_VAD: st->vad_enabled = (*(spx_int32_t*)ptr); speex_encoder_ctl(st->st_low, SPEEX_SET_VAD, ptr); break; case SPEEX_GET_VAD: (*(spx_int32_t*)ptr) = st->vad_enabled; break; #endif /* #ifndef DISABLE_VBR */ #if !defined(DISABLE_VBR) && !defined(DISABLE_FLOAT_API) case SPEEX_SET_VBR_QUALITY: { spx_int32_t q; float qual = (*(float*)ptr)+.6; st->vbr_quality = (*(float*)ptr); if (qual>10) qual=10; q=(int)floor(.5+*(float*)ptr); if (q>10) q=10; speex_encoder_ctl(st->st_low, SPEEX_SET_VBR_QUALITY, &qual); speex_encoder_ctl(state, SPEEX_SET_QUALITY, &q); break; } case SPEEX_GET_VBR_QUALITY: (*(float*)ptr) = st->vbr_quality; break; #endif /* #if !defined(DISABLE_VBR) && !defined(DISABLE_FLOAT_API) */ #ifndef DISABLE_VBR case SPEEX_SET_ABR: st->abr_enabled = (*(spx_int32_t*)ptr); st->vbr_enabled = st->abr_enabled!=0; speex_encoder_ctl(st->st_low, SPEEX_SET_VBR, &st->vbr_enabled); if (st->vbr_enabled) { spx_int32_t i=10, rate, target; float vbr_qual; target = (*(spx_int32_t*)ptr); while (i>=0) { speex_encoder_ctl(st, SPEEX_SET_QUALITY, &i); speex_encoder_ctl(st, SPEEX_GET_BITRATE, &rate); if (rate <= target) break; i--; } vbr_qual=i; if (vbr_qual<0) vbr_qual=0; speex_encoder_ctl(st, SPEEX_SET_VBR_QUALITY, &vbr_qual); st->abr_count=0; st->abr_drift=0; st->abr_drift2=0; } break; case SPEEX_GET_ABR: (*(spx_int32_t*)ptr) = st->abr_enabled; break; #endif /* #ifndef DISABLE_VBR */ case SPEEX_SET_QUALITY: { spx_int32_t nb_qual; int quality = (*(spx_int32_t*)ptr); if (quality < 0) quality = 0; if (quality > 10) quality = 10; st->submodeSelect = st->submodeID = ((const SpeexSBMode*)(st->mode->mode))->quality_map[quality]; nb_qual = ((const SpeexSBMode*)(st->mode->mode))->low_quality_map[quality]; speex_encoder_ctl(st->st_low, SPEEX_SET_MODE, &nb_qual); } break; case SPEEX_SET_COMPLEXITY: speex_encoder_ctl(st->st_low, SPEEX_SET_COMPLEXITY, ptr); st->complexity = (*(spx_int32_t*)ptr); if (st->complexity<1) st->complexity=1; break; case SPEEX_GET_COMPLEXITY: (*(spx_int32_t*)ptr) = st->complexity; break; case SPEEX_SET_BITRATE: { spx_int32_t i=10; spx_int32_t rate, target; target = (*(spx_int32_t*)ptr); while (i>=0) { speex_encoder_ctl(st, SPEEX_SET_QUALITY, &i); speex_encoder_ctl(st, SPEEX_GET_BITRATE, &rate); if (rate <= target) break; i--; } } break; case SPEEX_GET_BITRATE: speex_encoder_ctl(st->st_low, request, ptr); /*fprintf (stderr, "before: %d\n", (*(int*)ptr));*/ if (st->submodes[st->submodeID]) (*(spx_int32_t*)ptr) += st->sampling_rate*SUBMODE(bits_per_frame)/st->full_frame_size; else (*(spx_int32_t*)ptr) += st->sampling_rate*(SB_SUBMODE_BITS+1)/st->full_frame_size; /*fprintf (stderr, "after: %d\n", (*(int*)ptr));*/ break; case SPEEX_SET_SAMPLING_RATE: { spx_int32_t tmp=(*(spx_int32_t*)ptr); st->sampling_rate = tmp; tmp>>=1; speex_encoder_ctl(st->st_low, SPEEX_SET_SAMPLING_RATE, &tmp); } break; case SPEEX_GET_SAMPLING_RATE: (*(spx_int32_t*)ptr)=st->sampling_rate; break; case SPEEX_RESET_STATE: { int i; st->first = 1; for (i=0;ilpcSize;i++) st->old_lsp[i]= DIV32(MULT16_16(QCONST16(3.1415927f, LSP_SHIFT), i+1), st->lpcSize+1); for (i=0;ilpcSize;i++) st->mem_sw[i]=st->mem_sp[i]=st->mem_sp2[i]=0; for (i=0;ih0_mem[i]=st->h1_mem[i]=0; } break; case SPEEX_SET_SUBMODE_ENCODING: st->encode_submode = (*(spx_int32_t*)ptr); speex_encoder_ctl(st->st_low, SPEEX_SET_SUBMODE_ENCODING, ptr); break; case SPEEX_GET_SUBMODE_ENCODING: (*(spx_int32_t*)ptr) = st->encode_submode; break; case SPEEX_GET_LOOKAHEAD: speex_encoder_ctl(st->st_low, SPEEX_GET_LOOKAHEAD, ptr); (*(spx_int32_t*)ptr) = 2*(*(spx_int32_t*)ptr) + QMF_ORDER - 1; break; case SPEEX_SET_PLC_TUNING: speex_encoder_ctl(st->st_low, SPEEX_SET_PLC_TUNING, ptr); break; case SPEEX_GET_PLC_TUNING: speex_encoder_ctl(st->st_low, SPEEX_GET_PLC_TUNING, ptr); break; #ifndef DISABLE_VBR case SPEEX_SET_VBR_MAX_BITRATE: { st->vbr_max = (*(spx_int32_t*)ptr); if (SPEEX_SET_VBR_MAX_BITRATE<1) { speex_encoder_ctl(st->st_low, SPEEX_SET_VBR_MAX_BITRATE, &st->vbr_max); st->vbr_max_high = 17600; } else { spx_int32_t low_rate; if (st->vbr_max >= 42200) { st->vbr_max_high = 17600; } else if (st->vbr_max >= 27800) { st->vbr_max_high = 9600; } else if (st->vbr_max > 20600) { st->vbr_max_high = 5600; } else { st->vbr_max_high = 1800; } if (st->subframeSize==80) st->vbr_max_high = 1800; low_rate = st->vbr_max - st->vbr_max_high; speex_encoder_ctl(st->st_low, SPEEX_SET_VBR_MAX_BITRATE, &low_rate); } } break; case SPEEX_GET_VBR_MAX_BITRATE: (*(spx_int32_t*)ptr) = st->vbr_max; break; #endif /* #ifndef DISABLE_VBR */ case SPEEX_SET_HIGHPASS: speex_encoder_ctl(st->st_low, SPEEX_SET_HIGHPASS, ptr); break; case SPEEX_GET_HIGHPASS: speex_encoder_ctl(st->st_low, SPEEX_GET_HIGHPASS, ptr); break; /* This is all internal stuff past this point */ case SPEEX_GET_PI_GAIN: { int i; spx_word32_t *g = (spx_word32_t*)ptr; for (i=0;inbSubframes;i++) g[i]=st->pi_gain[i]; } break; case SPEEX_GET_EXC: { int i; for (i=0;inbSubframes;i++) ((spx_word16_t*)ptr)[i] = st->exc_rms[i]; } break; #ifndef DISABLE_VBR case SPEEX_GET_RELATIVE_QUALITY: (*(float*)ptr)=st->relative_quality; break; #endif /* #ifndef DISABLE_VBR */ case SPEEX_SET_INNOVATION_SAVE: st->innov_rms_save = (spx_word16_t*)ptr; break; case SPEEX_SET_WIDEBAND: speex_encoder_ctl(st->st_low, SPEEX_SET_WIDEBAND, ptr); break; case SPEEX_GET_STACK: *((char**)ptr) = st->stack; break; default: speex_warning_int("Unknown nb_ctl request: ", request); return -1; } return 0; } int sb_decoder_ctl(void *state, int request, void *ptr) { SBDecState *st; st=(SBDecState*)state; switch(request) { case SPEEX_SET_HIGH_MODE: st->submodeID = (*(spx_int32_t*)ptr); break; case SPEEX_SET_LOW_MODE: speex_decoder_ctl(st->st_low, SPEEX_SET_LOW_MODE, ptr); break; case SPEEX_GET_LOW_MODE: speex_decoder_ctl(st->st_low, SPEEX_GET_LOW_MODE, ptr); break; case SPEEX_GET_FRAME_SIZE: (*(spx_int32_t*)ptr) = st->full_frame_size; break; case SPEEX_SET_ENH: speex_decoder_ctl(st->st_low, request, ptr); st->lpc_enh_enabled = *((spx_int32_t*)ptr); break; case SPEEX_GET_ENH: *((spx_int32_t*)ptr) = st->lpc_enh_enabled; break; case SPEEX_SET_MODE: case SPEEX_SET_QUALITY: { spx_int32_t nb_qual; int quality = (*(spx_int32_t*)ptr); if (quality < 0) quality = 0; if (quality > 10) quality = 10; st->submodeID = ((const SpeexSBMode*)(st->mode->mode))->quality_map[quality]; nb_qual = ((const SpeexSBMode*)(st->mode->mode))->low_quality_map[quality]; speex_decoder_ctl(st->st_low, SPEEX_SET_MODE, &nb_qual); } break; case SPEEX_GET_BITRATE: speex_decoder_ctl(st->st_low, request, ptr); if (st->submodes[st->submodeID]) (*(spx_int32_t*)ptr) += st->sampling_rate*SUBMODE(bits_per_frame)/st->full_frame_size; else (*(spx_int32_t*)ptr) += st->sampling_rate*(SB_SUBMODE_BITS+1)/st->full_frame_size; break; case SPEEX_SET_SAMPLING_RATE: { spx_int32_t tmp=(*(spx_int32_t*)ptr); st->sampling_rate = tmp; tmp>>=1; speex_decoder_ctl(st->st_low, SPEEX_SET_SAMPLING_RATE, &tmp); } break; case SPEEX_GET_SAMPLING_RATE: (*(spx_int32_t*)ptr)=st->sampling_rate; break; case SPEEX_SET_HANDLER: speex_decoder_ctl(st->st_low, SPEEX_SET_HANDLER, ptr); break; case SPEEX_SET_USER_HANDLER: speex_decoder_ctl(st->st_low, SPEEX_SET_USER_HANDLER, ptr); break; case SPEEX_RESET_STATE: { int i; for (i=0;i<2*st->lpcSize;i++) st->mem_sp[i]=0; for (i=0;ig0_mem[i]=st->g1_mem[i]=0; st->last_ener=0; } break; case SPEEX_SET_SUBMODE_ENCODING: st->encode_submode = (*(spx_int32_t*)ptr); speex_decoder_ctl(st->st_low, SPEEX_SET_SUBMODE_ENCODING, ptr); break; case SPEEX_GET_SUBMODE_ENCODING: (*(spx_int32_t*)ptr) = st->encode_submode; break; case SPEEX_GET_LOOKAHEAD: speex_decoder_ctl(st->st_low, SPEEX_GET_LOOKAHEAD, ptr); (*(spx_int32_t*)ptr) = 2*(*(spx_int32_t*)ptr); break; case SPEEX_SET_HIGHPASS: speex_decoder_ctl(st->st_low, SPEEX_SET_HIGHPASS, ptr); break; case SPEEX_GET_HIGHPASS: speex_decoder_ctl(st->st_low, SPEEX_GET_HIGHPASS, ptr); break; case SPEEX_GET_ACTIVITY: speex_decoder_ctl(st->st_low, SPEEX_GET_ACTIVITY, ptr); break; case SPEEX_GET_PI_GAIN: { int i; spx_word32_t *g = (spx_word32_t*)ptr; for (i=0;inbSubframes;i++) g[i]=st->pi_gain[i]; } break; case SPEEX_GET_EXC: { int i; for (i=0;inbSubframes;i++) ((spx_word16_t*)ptr)[i] = st->exc_rms[i]; } break; case SPEEX_GET_DTX_STATUS: speex_decoder_ctl(st->st_low, SPEEX_GET_DTX_STATUS, ptr); break; case SPEEX_SET_INNOVATION_SAVE: st->innov_save = (spx_word16_t*)ptr; break; case SPEEX_SET_WIDEBAND: speex_decoder_ctl(st->st_low, SPEEX_SET_WIDEBAND, ptr); break; case SPEEX_GET_STACK: *((char**)ptr) = st->stack; break; default: speex_warning_int("Unknown nb_ctl request: ", request); return -1; } return 0; } #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/sb_celp.h ================================================ /* Copyright (C) 2002-2006 Jean-Marc Valin */ /** @file sb_celp.h @brief Sub-band CELP mode used for wideband encoding */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SB_CELP_H #define SB_CELP_H #include "modes.h" #include #include "nb_celp.h" /**Structure representing the full state of the sub-band encoder*/ typedef struct SBEncState { const SpeexMode *mode; /**< Pointer to the mode (containing for vtable info) */ void *st_low; /**< State of the low-band (narrowband) encoder */ int full_frame_size; /**< Length of full-band frames*/ int frame_size; /**< Length of high-band frames*/ int subframeSize; /**< Length of high-band sub-frames*/ int nbSubframes; /**< Number of high-band sub-frames*/ int windowSize; /**< Length of high-band LPC window*/ int lpcSize; /**< Order of high-band LPC analysis */ int first; /**< First frame? */ spx_word16_t lpc_floor; /**< Controls LPC analysis noise floor */ spx_word16_t gamma1; /**< Perceptual weighting coef 1 */ spx_word16_t gamma2; /**< Perceptual weighting coef 2 */ char *stack; /**< Temporary allocation stack */ spx_word16_t *high; /**< High-band signal (buffer) */ spx_word16_t *h0_mem, *h1_mem; const spx_word16_t *window; /**< LPC analysis window */ const spx_word16_t *lagWindow; /**< Auto-correlation window */ spx_lsp_t *old_lsp; /**< LSPs of previous frame */ spx_lsp_t *old_qlsp; /**< Quantized LSPs of previous frame */ spx_coef_t *interp_qlpc; /**< Interpolated quantized LPCs for current sub-frame */ spx_mem_t *mem_sp; /**< Synthesis signal memory */ spx_mem_t *mem_sp2; spx_mem_t *mem_sw; /**< Perceptual signal memory */ spx_word32_t *pi_gain; spx_word16_t *exc_rms; spx_word16_t *innov_rms_save; /**< If non-NULL, innovation is copied here */ #ifndef DISABLE_VBR float vbr_quality; /**< Quality setting for VBR encoding */ int vbr_enabled; /**< 1 for enabling VBR, 0 otherwise */ spx_int32_t vbr_max; /**< Max bit-rate allowed in VBR mode (total) */ spx_int32_t vbr_max_high; /**< Max bit-rate allowed in VBR mode for the high-band */ spx_int32_t abr_enabled; /**< ABR setting (in bps), 0 if off */ float abr_drift; float abr_drift2; float abr_count; int vad_enabled; /**< 1 for enabling VAD, 0 otherwise */ float relative_quality; #endif /* #ifndef DISABLE_VBR */ int encode_submode; const SpeexSubmode * const *submodes; int submodeID; int submodeSelect; int complexity; spx_int32_t sampling_rate; } SBEncState; /**Structure representing the full state of the sub-band decoder*/ typedef struct SBDecState { const SpeexMode *mode; /**< Pointer to the mode (containing for vtable info) */ void *st_low; /**< State of the low-band (narrowband) encoder */ int full_frame_size; int frame_size; int subframeSize; int nbSubframes; int lpcSize; int first; spx_int32_t sampling_rate; int lpc_enh_enabled; char *stack; spx_word16_t *g0_mem, *g1_mem; spx_word16_t *excBuf; spx_lsp_t *old_qlsp; spx_coef_t *interp_qlpc; spx_mem_t *mem_sp; spx_word32_t *pi_gain; spx_word16_t *exc_rms; spx_word16_t *innov_save; /** If non-NULL, innovation is copied here */ spx_word16_t last_ener; spx_int32_t seed; int encode_submode; const SpeexSubmode * const *submodes; int submodeID; } SBDecState; /**Initializes encoder state*/ void *sb_encoder_init(const SpeexMode *m); /**De-allocates encoder state resources*/ void sb_encoder_destroy(void *state); /**Encodes one frame*/ int sb_encode(void *state, void *in, SpeexBits *bits); /**Initializes decoder state*/ void *sb_decoder_init(const SpeexMode *m); /**De-allocates decoder state resources*/ void sb_decoder_destroy(void *state); /**Decodes one frame*/ int sb_decode(void *state, SpeexBits *bits, void *out); int sb_encoder_ctl(void *state, int request, void *ptr); int sb_decoder_ctl(void *state, int request, void *ptr); #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/scal.c ================================================ /* Copyright (C) 2006-2008 CSIRO, Jean-Marc Valin, Xiph.Org Foundation File: scal.c Shaped comb-allpass filter for channel decorrelation Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* The algorithm implemented here is described in: * J.-M. Valin, Perceptually-Motivated Nonlinear Channel Decorrelation For Stereo Acoustic Echo Cancellation, Accepted for Joint Workshop on Hands­free Speech Communication and Microphone Arrays (HSCMA), 2008. http://people.xiph.org/~jm/papers/valin_hscma2008.pdf */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "speex/speex_echo.h" #include "vorbis_psy.h" #include "arch.h" #include "os_support.h" #include "smallft.h" #include #include #define ALLPASS_ORDER 20 struct SpeexDecorrState_ { int rate; int channels; int frame_size; #ifdef VORBIS_PSYCHO VorbisPsy *psy; struct drft_lookup lookup; float *wola_mem; float *curve; #endif float *vorbis_win; int seed; float *y; /* Per-channel stuff */ float *buff; float (*ring)[ALLPASS_ORDER]; int *ringID; int *order; float *alpha; }; EXPORT SpeexDecorrState *speex_decorrelate_new(int rate, int channels, int frame_size) { int i, ch; SpeexDecorrState *st = speex_alloc(sizeof(SpeexDecorrState)); st->rate = rate; st->channels = channels; st->frame_size = frame_size; #ifdef VORBIS_PSYCHO st->psy = vorbis_psy_init(rate, 2*frame_size); spx_drft_init(&st->lookup, 2*frame_size); st->wola_mem = speex_alloc(frame_size*sizeof(float)); st->curve = speex_alloc(frame_size*sizeof(float)); #endif st->y = speex_alloc(frame_size*sizeof(float)); st->buff = speex_alloc(channels*2*frame_size*sizeof(float)); st->ringID = speex_alloc(channels*sizeof(int)); st->order = speex_alloc(channels*sizeof(int)); st->alpha = speex_alloc(channels*sizeof(float)); st->ring = speex_alloc(channels*ALLPASS_ORDER*sizeof(float)); /*FIXME: The +20 is there only as a kludge for ALL_PASS_OLA*/ st->vorbis_win = speex_alloc((2*frame_size+20)*sizeof(float)); for (i=0;i<2*frame_size;i++) st->vorbis_win[i] = sin(.5*M_PI* sin(M_PI*i/(2*frame_size))*sin(M_PI*i/(2*frame_size)) ); st->seed = rand(); for (ch=0;chring[ch][i] = 0; st->ringID[ch] = 0; st->alpha[ch] = 0; st->order[ch] = 10; } return st; } static float uni_rand(int *seed) { const unsigned int jflone = 0x3f800000; const unsigned int jflmsk = 0x007fffff; union {int i; float f;} ran; *seed = 1664525 * *seed + 1013904223; ran.i = jflone | (jflmsk & *seed); ran.f -= 1.5; return 2*ran.f; } static unsigned int irand(int *seed) { *seed = 1664525 * *seed + 1013904223; return ((unsigned int)*seed)>>16; } EXPORT void speex_decorrelate(SpeexDecorrState *st, const spx_int16_t *in, spx_int16_t *out, int strength) { int ch; float amount; if (strength<0) strength = 0; if (strength>100) strength = 100; amount = .01*strength; for (ch=0;chchannels;ch++) { int i; int N=2*st->frame_size; float beta, beta2; float *x; float max_alpha = 0; float *buff; float *ring; int ringID; int order; float alpha; buff = st->buff+ch*2*st->frame_size; ring = st->ring[ch]; ringID = st->ringID[ch]; order = st->order[ch]; alpha = st->alpha[ch]; for (i=0;iframe_size;i++) buff[i] = buff[i+st->frame_size]; for (i=0;iframe_size;i++) buff[i+st->frame_size] = in[i*st->channels+ch]; x = buff+st->frame_size; beta = 1.-.3*amount*amount; if (amount>1) beta = 1-sqrt(.4*amount); else beta = 1-0.63246*amount; if (beta<0) beta = 0; beta2 = beta; for (i=0;iframe_size;i++) { st->y[i] = alpha*(x[i-ALLPASS_ORDER+order]-beta*x[i-ALLPASS_ORDER+order-1])*st->vorbis_win[st->frame_size+i+order] + x[i-ALLPASS_ORDER]*st->vorbis_win[st->frame_size+i] - alpha*(ring[ringID] - beta*ring[ringID+1>=order?0:ringID+1]); ring[ringID++]=st->y[i]; st->y[i] *= st->vorbis_win[st->frame_size+i]; if (ringID>=order) ringID=0; } order = order+(irand(&st->seed)%3)-1; if (order < 5) order = 5; if (order > 10) order = 10; /*order = 5+(irand(&st->seed)%6);*/ max_alpha = pow(.96+.04*(amount-1),order); if (max_alpha > .98/(1.+beta2)) max_alpha = .98/(1.+beta2); alpha = alpha + .4*uni_rand(&st->seed); if (alpha > max_alpha) alpha = max_alpha; if (alpha < -max_alpha) alpha = -max_alpha; for (i=0;iframe_size;i++) { float tmp = alpha*(x[i-ALLPASS_ORDER+order]-beta*x[i-ALLPASS_ORDER+order-1])*st->vorbis_win[i+order] + x[i-ALLPASS_ORDER]*st->vorbis_win[i] - alpha*(ring[ringID] - beta*ring[ringID+1>=order?0:ringID+1]); ring[ringID++]=tmp; tmp *= st->vorbis_win[i]; if (ringID>=order) ringID=0; st->y[i] += tmp; } #ifdef VORBIS_PSYCHO float frame[N]; float scale = 1./N; for (i=0;i<2*st->frame_size;i++) frame[i] = buff[i]; //float coef = .5*0.78130; float coef = M_PI*0.075063 * 0.93763 * amount * .8 * 0.707; compute_curve(st->psy, buff, st->curve); for (i=1;iframe_size;i++) { float x1,x2; float gain; do { x1 = uni_rand(&st->seed); x2 = uni_rand(&st->seed); } while (x1*x1+x2*x2 > 1.); gain = coef*sqrt(.1+st->curve[i]); frame[2*i-1] = gain*x1; frame[2*i] = gain*x2; } frame[0] = coef*uni_rand(&st->seed)*sqrt(.1+st->curve[0]); frame[2*st->frame_size-1] = coef*uni_rand(&st->seed)*sqrt(.1+st->curve[st->frame_size-1]); spx_drft_backward(&st->lookup,frame); for (i=0;i<2*st->frame_size;i++) frame[i] *= st->vorbis_win[i]; #endif for (i=0;iframe_size;i++) { #ifdef VORBIS_PSYCHO float tmp = st->y[i] + frame[i] + st->wola_mem[i]; st->wola_mem[i] = frame[i+st->frame_size]; #else float tmp = st->y[i]; #endif if (tmp>32767) tmp = 32767; if (tmp < -32767) tmp = -32767; out[i*st->channels+ch] = tmp; } st->ringID[ch] = ringID; st->order[ch] = order; st->alpha[ch] = alpha; } } EXPORT void speex_decorrelate_destroy(SpeexDecorrState *st) { #ifdef VORBIS_PSYCHO vorbis_psy_destroy(st->psy); speex_free(st->wola_mem); speex_free(st->curve); #endif speex_free(st->buff); speex_free(st->ring); speex_free(st->ringID); speex_free(st->alpha); speex_free(st->vorbis_win); speex_free(st->order); speex_free(st->y); speex_free(st); } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/smallft.c ================================================ /******************************************************************** * * * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 * * by the XIPHOPHORUS Company http://www.xiph.org/ * * * ******************************************************************** function: *unnormalized* fft transform last mod: $Id: smallft.c 2002 2008-06-10 14:09:37Z nanang $ ********************************************************************/ /* FFT implementation from OggSquish, minus cosine transforms, * minus all but radix 2/4 case. In Vorbis we only need this * cut-down version. * * To do more than just power-of-two sized vectors, see the full * version I wrote for NetLib. * * Note that the packing is a little strange; rather than the FFT r/i * packing following R_0, I_n, R_1, I_1, R_2, I_2 ... R_n-1, I_n-1, * it follows R_0, R_1, I_1, R_2, I_2 ... R_n-1, I_n-1, I_n like the * FORTRAN version */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "smallft.h" #include "arch.h" #include "os_support.h" static void drfti1(int n, float *wa, int *ifac){ static int ntryh[4] = { 4,2,3,5 }; static float tpi = 6.28318530717958648f; float arg,argh,argld,fi; int ntry=0,i,j=-1; int k1, l1, l2, ib; int ld, ii, ip, is, nq, nr; int ido, ipm, nfm1; int nl=n; int nf=0; L101: j++; if (j < 4) ntry=ntryh[j]; else ntry+=2; L104: nq=nl/ntry; nr=nl-ntry*nq; if (nr!=0) goto L101; nf++; ifac[nf+1]=ntry; nl=nq; if(ntry!=2)goto L107; if(nf==1)goto L107; for (i=1;i>1; ipp2=ip; idp2=ido; nbd=(ido-1)>>1; t0=l1*ido; t10=ip*ido; if(ido==1)goto L119; for(ik=0;ikl1){ for(j=1;j>1; ipp2=ip; ipph=(ip+1)>>1; if(idol1)goto L139; is= -ido-1; t1=0; for(j=1;jn==1)return; drftf1(l->n,data,l->trigcache,l->trigcache+l->n,l->splitcache); } void spx_drft_backward(struct drft_lookup *l,float *data){ if (l->n==1)return; drftb1(l->n,data,l->trigcache,l->trigcache+l->n,l->splitcache); } void spx_drft_init(struct drft_lookup *l,int n) { l->n=n; l->trigcache=(float*)speex_alloc(3*n*sizeof(*l->trigcache)); l->splitcache=(int*)speex_alloc(32*sizeof(*l->splitcache)); fdrffti(n, l->trigcache, l->splitcache); } void spx_drft_clear(struct drft_lookup *l) { if(l) { if(l->trigcache) speex_free(l->trigcache); if(l->splitcache) speex_free(l->splitcache); } } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/smallft.h ================================================ /******************************************************************** * * * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 * * by the XIPHOPHORUS Company http://www.xiph.org/ * * * ******************************************************************** function: fft transform last mod: $Id: smallft.h 2002 2008-06-10 14:09:37Z nanang $ ********************************************************************/ /** @file smallft.h @brief Discrete Rotational Fourier Transform (DRFT) */ #ifndef _V_SMFT_H_ #define _V_SMFT_H_ #ifdef __cplusplus extern "C" { #endif /** Discrete Rotational Fourier Transform lookup */ struct drft_lookup{ int n; float *trigcache; int *splitcache; }; extern void spx_drft_forward(struct drft_lookup *l,float *data); extern void spx_drft_backward(struct drft_lookup *l,float *data); extern void spx_drft_init(struct drft_lookup *l,int n); extern void spx_drft_clear(struct drft_lookup *l); #ifdef __cplusplus } #endif #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/speex.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: speex.c Basic Speex functions Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "modes.h" #include #include "os_support.h" #ifndef NULL #define NULL 0 #endif #define MAX_IN_SAMPLES 640 EXPORT void *speex_encoder_init(const SpeexMode *mode) { return mode->enc_init(mode); } EXPORT void *speex_decoder_init(const SpeexMode *mode) { return mode->dec_init(mode); } EXPORT void speex_encoder_destroy(void *state) { (*((SpeexMode**)state))->enc_destroy(state); } EXPORT void speex_decoder_destroy(void *state) { (*((SpeexMode**)state))->dec_destroy(state); } int speex_encode_native(void *state, spx_word16_t *in, SpeexBits *bits) { return (*((SpeexMode**)state))->enc(state, in, bits); } int speex_decode_native(void *state, SpeexBits *bits, spx_word16_t *out) { return (*((SpeexMode**)state))->dec(state, bits, out); } #ifdef FIXED_POINT #ifndef DISABLE_FLOAT_API EXPORT int speex_encode(void *state, float *in, SpeexBits *bits) { int i; spx_int32_t N; spx_int16_t short_in[MAX_IN_SAMPLES]; speex_encoder_ctl(state, SPEEX_GET_FRAME_SIZE, &N); for (i=0;i32767.f) short_in[i] = 32767; else if (in[i]<-32768.f) short_in[i] = -32768; else short_in[i] = (spx_int16_t)floor(.5+in[i]); } return (*((SpeexMode**)state))->enc(state, short_in, bits); } #endif /* #ifndef DISABLE_FLOAT_API */ EXPORT int speex_encode_int(void *state, spx_int16_t *in, SpeexBits *bits) { SpeexMode *mode; mode = *(SpeexMode**)state; return (mode)->enc(state, in, bits); } #ifndef DISABLE_FLOAT_API EXPORT int speex_decode(void *state, SpeexBits *bits, float *out) { int i, ret; spx_int32_t N; spx_int16_t short_out[MAX_IN_SAMPLES]; speex_decoder_ctl(state, SPEEX_GET_FRAME_SIZE, &N); ret = (*((SpeexMode**)state))->dec(state, bits, short_out); for (i=0;idec(state, bits, out); } #else EXPORT int speex_encode(void *state, float *in, SpeexBits *bits) { return (*((SpeexMode**)state))->enc(state, in, bits); } EXPORT int speex_encode_int(void *state, spx_int16_t *in, SpeexBits *bits) { int i; spx_int32_t N; float float_in[MAX_IN_SAMPLES]; speex_encoder_ctl(state, SPEEX_GET_FRAME_SIZE, &N); for (i=0;ienc(state, float_in, bits); } EXPORT int speex_decode(void *state, SpeexBits *bits, float *out) { return (*((SpeexMode**)state))->dec(state, bits, out); } EXPORT int speex_decode_int(void *state, SpeexBits *bits, spx_int16_t *out) { int i; spx_int32_t N; float float_out[MAX_IN_SAMPLES]; int ret; speex_decoder_ctl(state, SPEEX_GET_FRAME_SIZE, &N); ret = (*((SpeexMode**)state))->dec(state, bits, float_out); for (i=0;i32767.f) out[i] = 32767; else if (float_out[i]<-32768.f) out[i] = -32768; else out[i] = (spx_int16_t)floor(.5+float_out[i]); } return ret; } #endif EXPORT int speex_encoder_ctl(void *state, int request, void *ptr) { return (*((SpeexMode**)state))->enc_ctl(state, request, ptr); } EXPORT int speex_decoder_ctl(void *state, int request, void *ptr) { return (*((SpeexMode**)state))->dec_ctl(state, request, ptr); } int nb_mode_query(const void *mode, int request, void *ptr) { const SpeexNBMode *m = (const SpeexNBMode*)mode; switch (request) { case SPEEX_MODE_FRAME_SIZE: *((int*)ptr)=m->frameSize; break; case SPEEX_SUBMODE_BITS_PER_FRAME: if (*((int*)ptr)==0) *((int*)ptr) = NB_SUBMODE_BITS+1; else if (m->submodes[*((int*)ptr)]==NULL) *((int*)ptr) = -1; else *((int*)ptr) = m->submodes[*((int*)ptr)]->bits_per_frame; break; default: speex_warning_int("Unknown nb_mode_query request: ", request); return -1; } return 0; } EXPORT int speex_lib_ctl(int request, void *ptr) { switch (request) { case SPEEX_LIB_GET_MAJOR_VERSION: *((int*)ptr) = SPEEX_MAJOR_VERSION; break; case SPEEX_LIB_GET_MINOR_VERSION: *((int*)ptr) = SPEEX_MINOR_VERSION; break; case SPEEX_LIB_GET_MICRO_VERSION: *((int*)ptr) = SPEEX_MICRO_VERSION; break; case SPEEX_LIB_GET_EXTRA_VERSION: *((const char**)ptr) = SPEEX_EXTRA_VERSION; break; case SPEEX_LIB_GET_VERSION_STRING: *((const char**)ptr) = SPEEX_VERSION; break; /*case SPEEX_LIB_SET_ALLOC_FUNC: break; case SPEEX_LIB_GET_ALLOC_FUNC: break; case SPEEX_LIB_SET_FREE_FUNC: break; case SPEEX_LIB_GET_FREE_FUNC: break;*/ default: speex_warning_int("Unknown wb_mode_query request: ", request); return -1; } return 0; } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/speex_callbacks.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File speex_callbacks.c Callback handling and in-band signalling Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "arch.h" #include "os_support.h" EXPORT int speex_inband_handler(SpeexBits *bits, SpeexCallback *callback_list, void *state) { int id; SpeexCallback *callback; /*speex_bits_advance(bits, 5);*/ id=speex_bits_unpack_unsigned(bits, 4); callback = callback_list+id; if (callback->func) { return callback->func(bits, state, callback->data); } else /*If callback is not registered, skip the right number of bits*/ { int adv; if (id<2) adv = 1; else if (id<8) adv = 4; else if (id<10) adv = 8; else if (id<12) adv = 16; else if (id<14) adv = 32; else adv = 64; speex_bits_advance(bits, adv); } return 0; } EXPORT int speex_std_mode_request_handler(SpeexBits *bits, void *state, void *data) { spx_int32_t m; m = speex_bits_unpack_unsigned(bits, 4); speex_encoder_ctl(data, SPEEX_SET_MODE, &m); return 0; } EXPORT int speex_std_low_mode_request_handler(SpeexBits *bits, void *state, void *data) { spx_int32_t m; m = speex_bits_unpack_unsigned(bits, 4); speex_encoder_ctl(data, SPEEX_SET_LOW_MODE, &m); return 0; } EXPORT int speex_std_high_mode_request_handler(SpeexBits *bits, void *state, void *data) { spx_int32_t m; m = speex_bits_unpack_unsigned(bits, 4); speex_encoder_ctl(data, SPEEX_SET_HIGH_MODE, &m); return 0; } #ifndef DISABLE_VBR EXPORT int speex_std_vbr_request_handler(SpeexBits *bits, void *state, void *data) { spx_int32_t vbr; vbr = speex_bits_unpack_unsigned(bits, 1); speex_encoder_ctl(data, SPEEX_SET_VBR, &vbr); return 0; } #endif /* #ifndef DISABLE_VBR */ EXPORT int speex_std_enh_request_handler(SpeexBits *bits, void *state, void *data) { spx_int32_t enh; enh = speex_bits_unpack_unsigned(bits, 1); speex_decoder_ctl(data, SPEEX_SET_ENH, &enh); return 0; } #ifndef DISABLE_VBR EXPORT int speex_std_vbr_quality_request_handler(SpeexBits *bits, void *state, void *data) { float qual; qual = speex_bits_unpack_unsigned(bits, 4); speex_encoder_ctl(data, SPEEX_SET_VBR_QUALITY, &qual); return 0; } #endif /* #ifndef DISABLE_VBR */ EXPORT int speex_std_char_handler(SpeexBits *bits, void *state, void *data) { unsigned char ch; ch = speex_bits_unpack_unsigned(bits, 8); _speex_putc(ch, data); /*printf("speex_std_char_handler ch=%x\n", ch);*/ return 0; } /* Default handler for user callbacks: skip it */ EXPORT int speex_default_user_handler(SpeexBits *bits, void *state, void *data) { int req_size = speex_bits_unpack_unsigned(bits, 4); speex_bits_advance(bits, 5+8*req_size); return 0; } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/speex_header.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: speex_header.c Describes the Speex header Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "arch.h" #include #include #include "os_support.h" #ifndef NULL #define NULL 0 #endif /** Convert little endian */ static inline spx_int32_t le_int(spx_int32_t i) { #if !defined(__LITTLE_ENDIAN__) && ( defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) ) spx_uint32_t ui, ret; ui = i; ret = ui>>24; ret |= (ui>>8)&0x0000ff00; ret |= (ui<<8)&0x00ff0000; ret |= (ui<<24); return ret; #else return i; #endif } #define ENDIAN_SWITCH(x) {x=le_int(x);} /* typedef struct SpeexHeader { char speex_string[8]; char speex_version[SPEEX_HEADER_VERSION_LENGTH]; int speex_version_id; int header_size; int rate; int mode; int mode_bitstream_version; int nb_channels; int bitrate; int frame_size; int vbr; int frames_per_packet; int extra_headers; int reserved1; int reserved2; } SpeexHeader; */ EXPORT void speex_init_header(SpeexHeader *header, int rate, int nb_channels, const SpeexMode *m) { int i; const char *h="Speex "; /* strncpy(header->speex_string, "Speex ", 8); strncpy(header->speex_version, SPEEX_VERSION, SPEEX_HEADER_VERSION_LENGTH-1); header->speex_version[SPEEX_HEADER_VERSION_LENGTH-1]=0; */ for (i=0;i<8;i++) header->speex_string[i]=h[i]; for (i=0;ispeex_version[i]=SPEEX_VERSION[i]; for (;ispeex_version[i]=0; header->speex_version_id = 1; header->header_size = sizeof(SpeexHeader); header->rate = rate; header->mode = m->modeID; header->mode_bitstream_version = m->bitstream_version; if (m->modeID<0) speex_warning("This mode is meant to be used alone"); header->nb_channels = nb_channels; header->bitrate = -1; speex_mode_query(m, SPEEX_MODE_FRAME_SIZE, &header->frame_size); header->vbr = 0; header->frames_per_packet = 0; header->extra_headers = 0; header->reserved1 = 0; header->reserved2 = 0; } EXPORT char *speex_header_to_packet(SpeexHeader *header, int *size) { SpeexHeader *le_header; le_header = (SpeexHeader*)speex_alloc(sizeof(SpeexHeader)); SPEEX_COPY(le_header, header, 1); /*Make sure everything is now little-endian*/ ENDIAN_SWITCH(le_header->speex_version_id); ENDIAN_SWITCH(le_header->header_size); ENDIAN_SWITCH(le_header->rate); ENDIAN_SWITCH(le_header->mode); ENDIAN_SWITCH(le_header->mode_bitstream_version); ENDIAN_SWITCH(le_header->nb_channels); ENDIAN_SWITCH(le_header->bitrate); ENDIAN_SWITCH(le_header->frame_size); ENDIAN_SWITCH(le_header->vbr); ENDIAN_SWITCH(le_header->frames_per_packet); ENDIAN_SWITCH(le_header->extra_headers); *size = sizeof(SpeexHeader); return (char *)le_header; } EXPORT SpeexHeader *speex_packet_to_header(char *packet, int size) { int i; SpeexHeader *le_header; const char *h = "Speex "; for (i=0;i<8;i++) if (packet[i]!=h[i]) { speex_notify("This doesn't look like a Speex file"); return NULL; } /*FIXME: Do we allow larger headers?*/ if (size < (int)sizeof(SpeexHeader)) { speex_notify("Speex header too small"); return NULL; } le_header = (SpeexHeader*)speex_alloc(sizeof(SpeexHeader)); SPEEX_COPY(le_header, (SpeexHeader*)packet, 1); /*Make sure everything is converted correctly from little-endian*/ ENDIAN_SWITCH(le_header->speex_version_id); ENDIAN_SWITCH(le_header->header_size); ENDIAN_SWITCH(le_header->rate); ENDIAN_SWITCH(le_header->mode); ENDIAN_SWITCH(le_header->mode_bitstream_version); ENDIAN_SWITCH(le_header->nb_channels); ENDIAN_SWITCH(le_header->bitrate); ENDIAN_SWITCH(le_header->frame_size); ENDIAN_SWITCH(le_header->vbr); ENDIAN_SWITCH(le_header->frames_per_packet); ENDIAN_SWITCH(le_header->extra_headers); if (le_header->mode >= SPEEX_NB_MODES || le_header->mode < 0) { speex_notify("Invalid mode specified in Speex header"); speex_free (le_header); return NULL; } if (le_header->nb_channels>2) le_header->nb_channels = 2; if (le_header->nb_channels<1) le_header->nb_channels = 1; return le_header; } EXPORT void speex_header_free(void *ptr) { speex_free(ptr); } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/stack_alloc.h ================================================ /* Copyright (C) 2002 Jean-Marc Valin */ /** @file stack_alloc.h @brief Temporary memory allocation on stack */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef STACK_ALLOC_H #define STACK_ALLOC_H #ifdef USE_ALLOCA # if (defined(WIN32) || defined(WIN64)) # include # else # ifdef HAVE_ALLOCA_H # include # else # include # endif # endif #endif /** * @def ALIGN(stack, size) * * Aligns the stack to a 'size' boundary * * @param stack Stack * @param size New size boundary */ /** * @def PUSH(stack, size, type) * * Allocates 'size' elements of type 'type' on the stack * * @param stack Stack * @param size Number of elements * @param type Type of element */ /** * @def VARDECL(var) * * Declare variable on stack * * @param var Variable to declare */ /** * @def ALLOC(var, size, type) * * Allocate 'size' elements of 'type' on stack * * @param var Name of variable to allocate * @param size Number of elements * @param type Type of element */ #ifdef ENABLE_VALGRIND #include #define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) #define PUSH(stack, size, type) (VALGRIND_MAKE_NOACCESS(stack, 1000),ALIGN((stack),sizeof(type)),VALGRIND_MAKE_WRITABLE(stack, ((size)*sizeof(type))),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type)))) #else #define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) #define PUSH(stack, size, type) (ALIGN((stack),sizeof(type)),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type)))) #endif #if defined(VAR_ARRAYS) #define VARDECL(var) #define ALLOC(var, size, type) type var[size] #elif defined(USE_ALLOCA) #define VARDECL(var) var #define ALLOC(var, size, type) var = alloca(sizeof(type)*(size)) #else #define VARDECL(var) var #define ALLOC(var, size, type) var = PUSH(stack, size, type) #endif #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/stereo.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: stereo.c Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "math_approx.h" #include "vq.h" #include #include "os_support.h" typedef struct RealSpeexStereoState { spx_word32_t balance; /**< Left/right balance info */ spx_word32_t e_ratio; /**< Ratio of energies: E(left+right)/[E(left)+E(right)] */ spx_word32_t smooth_left; /**< Smoothed left channel gain */ spx_word32_t smooth_right; /**< Smoothed right channel gain */ spx_uint32_t reserved1; /**< Reserved for future use */ spx_int32_t reserved2; /**< Reserved for future use */ } RealSpeexStereoState; /*float e_ratio_quant[4] = {1, 1.26, 1.587, 2};*/ #ifndef FIXED_POINT static const float e_ratio_quant[4] = {.25f, .315f, .397f, .5f}; static const float e_ratio_quant_bounds[3] = {0.2825f, 0.356f, 0.4485f}; #else static const spx_word16_t e_ratio_quant[4] = {8192, 10332, 13009, 16384}; static const spx_word16_t e_ratio_quant_bounds[3] = {9257, 11665, 14696}; static const spx_word16_t balance_bounds[31] = {18, 23, 30, 38, 49, 63, 81, 104, 134, 172, 221, 284, 364, 468, 600, 771, 990, 1271, 1632, 2096, 2691, 3455, 4436, 5696, 7314, 9392, 12059, 15484, 19882, 25529, 32766}; #endif /* This is an ugly compatibility hack that properly resets the stereo state In case it it compiled in fixed-point, but initialised with the deprecated floating point static initialiser */ #ifdef FIXED_POINT #define COMPATIBILITY_HACK(s) do {if ((s)->reserved1 != 0xdeadbeef) speex_stereo_state_reset((SpeexStereoState*)s); } while (0); #else #define COMPATIBILITY_HACK(s) #endif EXPORT SpeexStereoState *speex_stereo_state_init() { SpeexStereoState *stereo = speex_alloc(sizeof(SpeexStereoState)); speex_stereo_state_reset(stereo); return stereo; } EXPORT void speex_stereo_state_reset(SpeexStereoState *_stereo) { RealSpeexStereoState *stereo = (RealSpeexStereoState*)_stereo; #ifdef FIXED_POINT stereo->balance = 65536; stereo->e_ratio = 16384; stereo->smooth_left = 16384; stereo->smooth_right = 16384; stereo->reserved1 = 0xdeadbeef; stereo->reserved2 = 0; #else stereo->balance = 1.0f; stereo->e_ratio = .5f; stereo->smooth_left = 1.f; stereo->smooth_right = 1.f; stereo->reserved1 = 0; stereo->reserved2 = 0; #endif } EXPORT void speex_stereo_state_destroy(SpeexStereoState *stereo) { speex_free(stereo); } #ifndef DISABLE_FLOAT_API EXPORT void speex_encode_stereo(float *data, int frame_size, SpeexBits *bits) { int i, tmp; float e_left=0, e_right=0, e_tot=0; float balance, e_ratio; for (i=0;i0) speex_bits_pack(bits, 0, 1); else speex_bits_pack(bits, 1, 1); balance=floor(.5+fabs(balance)); if (balance>30) balance=31; speex_bits_pack(bits, (int)balance, 5); /* FIXME: this is a hack */ tmp=scal_quant(e_ratio*Q15_ONE, e_ratio_quant_bounds, 4); speex_bits_pack(bits, tmp, 2); } #endif /* #ifndef DISABLE_FLOAT_API */ EXPORT void speex_encode_stereo_int(spx_int16_t *data, int frame_size, SpeexBits *bits) { int i, tmp; spx_word32_t e_left=0, e_right=0, e_tot=0; spx_word32_t balance, e_ratio; spx_word32_t largest, smallest; int balance_id; #ifdef FIXED_POINT int shift; #endif /* In band marker */ speex_bits_pack(bits, 14, 5); /* Stereo marker */ speex_bits_pack(bits, SPEEX_INBAND_STEREO, 4); for (i=0;i e_right) { speex_bits_pack(bits, 0, 1); largest = e_left; smallest = e_right; } else { speex_bits_pack(bits, 1, 1); largest = e_right; smallest = e_left; } /* Balance quantization */ #ifdef FIXED_POINT shift = spx_ilog2(largest)-15; largest = VSHR32(largest, shift-4); smallest = VSHR32(smallest, shift); balance = DIV32(largest, ADD32(smallest, 1)); if (balance > 32767) balance = 32767; balance_id = scal_quant(EXTRACT16(balance), balance_bounds, 32); #else balance=(largest+1.)/(smallest+1.); balance=4*log(balance); balance_id=floor(.5+fabs(balance)); if (balance_id>30) balance_id=31; #endif speex_bits_pack(bits, balance_id, 5); /* "coherence" quantisation */ #ifdef FIXED_POINT shift = spx_ilog2(e_tot); e_tot = VSHR32(e_tot, shift-25); e_left = VSHR32(e_left, shift-10); e_right = VSHR32(e_right, shift-10); e_ratio = DIV32(e_tot, e_left+e_right+1); #else e_ratio = e_tot/(1.+e_left+e_right); #endif tmp=scal_quant(EXTRACT16(e_ratio), e_ratio_quant_bounds, 4); /*fprintf (stderr, "%d %d %d %d\n", largest, smallest, balance_id, e_ratio);*/ speex_bits_pack(bits, tmp, 2); } #ifndef DISABLE_FLOAT_API EXPORT void speex_decode_stereo(float *data, int frame_size, SpeexStereoState *_stereo) { int i; spx_word32_t balance; spx_word16_t e_left, e_right, e_ratio; RealSpeexStereoState *stereo = (RealSpeexStereoState*)_stereo; COMPATIBILITY_HACK(stereo); balance=stereo->balance; e_ratio=stereo->e_ratio; /* These two are Q14, with max value just below 2. */ e_right = DIV32(QCONST32(1., 22), spx_sqrt(MULT16_32_Q15(e_ratio, ADD32(QCONST32(1., 16), balance)))); e_left = SHR32(MULT16_16(spx_sqrt(balance), e_right), 8); for (i=frame_size-1;i>=0;i--) { spx_word16_t tmp=data[i]; stereo->smooth_left = EXTRACT16(PSHR32(MAC16_16(MULT16_16(stereo->smooth_left, QCONST16(0.98, 15)), e_left, QCONST16(0.02, 15)), 15)); stereo->smooth_right = EXTRACT16(PSHR32(MAC16_16(MULT16_16(stereo->smooth_right, QCONST16(0.98, 15)), e_right, QCONST16(0.02, 15)), 15)); data[2*i] = (float)MULT16_16_P14(stereo->smooth_left, tmp); data[2*i+1] = (float)MULT16_16_P14(stereo->smooth_right, tmp); } } #endif /* #ifndef DISABLE_FLOAT_API */ EXPORT void speex_decode_stereo_int(spx_int16_t *data, int frame_size, SpeexStereoState *_stereo) { int i; spx_word32_t balance; spx_word16_t e_left, e_right, e_ratio; RealSpeexStereoState *stereo = (RealSpeexStereoState*)_stereo; COMPATIBILITY_HACK(stereo); balance=stereo->balance; e_ratio=stereo->e_ratio; /* These two are Q14, with max value just below 2. */ e_right = DIV32(QCONST32(1., 22), spx_sqrt(MULT16_32_Q15(e_ratio, ADD32(QCONST32(1., 16), balance)))); e_left = SHR32(MULT16_16(spx_sqrt(balance), e_right), 8); for (i=frame_size-1;i>=0;i--) { spx_int16_t tmp=data[i]; stereo->smooth_left = EXTRACT16(PSHR32(MAC16_16(MULT16_16(stereo->smooth_left, QCONST16(0.98, 15)), e_left, QCONST16(0.02, 15)), 15)); stereo->smooth_right = EXTRACT16(PSHR32(MAC16_16(MULT16_16(stereo->smooth_right, QCONST16(0.98, 15)), e_right, QCONST16(0.02, 15)), 15)); data[2*i] = (spx_int16_t)MULT16_16_P14(stereo->smooth_left, tmp); data[2*i+1] = (spx_int16_t)MULT16_16_P14(stereo->smooth_right, tmp); } } EXPORT int speex_std_stereo_request_handler(SpeexBits *bits, void *state, void *data) { RealSpeexStereoState *stereo; spx_word16_t sign=1, dexp; int tmp; stereo = (RealSpeexStereoState*)data; COMPATIBILITY_HACK(stereo); if (speex_bits_unpack_unsigned(bits, 1)) sign=-1; dexp = speex_bits_unpack_unsigned(bits, 5); #ifndef FIXED_POINT stereo->balance = exp(sign*.25*dexp); #else stereo->balance = spx_exp(MULT16_16(sign, SHL16(dexp, 9))); #endif tmp = speex_bits_unpack_unsigned(bits, 2); stereo->e_ratio = e_ratio_quant[tmp]; return 0; } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/testdenoise.c ================================================ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #define NN 160 int main() { short in[NN]; int i; SpeexPreprocessState *st; int count=0; float f; st = speex_preprocess_state_init(NN, 8000); i=1; speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DENOISE, &i); i=0; speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_AGC, &i); i=8000; speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_AGC_LEVEL, &i); i=0; speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DEREVERB, &i); f=.0; speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &f); f=.0; speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &f); while (1) { int vad; fread(in, sizeof(short), NN, stdin); if (feof(stdin)) break; vad = speex_preprocess_run(st, in); /*fprintf (stderr, "%d\n", vad);*/ fwrite(in, sizeof(short), NN, stdout); count++; } speex_preprocess_state_destroy(st); return 0; } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/testecho.c ================================================ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "speex/speex_echo.h" #include "speex/speex_preprocess.h" #define NN 128 #define TAIL 1024 int main(int argc, char **argv) { FILE *echo_fd, *ref_fd, *e_fd; short echo_buf[NN], ref_buf[NN], e_buf[NN]; SpeexEchoState *st; SpeexPreprocessState *den; int sampleRate = 8000; if (argc != 4) { fprintf(stderr, "testecho mic_signal.sw speaker_signal.sw output.sw\n"); exit(1); } echo_fd = fopen(argv[2], "rb"); ref_fd = fopen(argv[1], "rb"); e_fd = fopen(argv[3], "wb"); st = speex_echo_state_init(NN, TAIL); den = speex_preprocess_state_init(NN, sampleRate); speex_echo_ctl(st, SPEEX_ECHO_SET_SAMPLING_RATE, &sampleRate); speex_preprocess_ctl(den, SPEEX_PREPROCESS_SET_ECHO_STATE, st); while (!feof(ref_fd) && !feof(echo_fd)) { fread(ref_buf, sizeof(short), NN, ref_fd); fread(echo_buf, sizeof(short), NN, echo_fd); speex_echo_cancellation(st, ref_buf, echo_buf, e_buf); speex_preprocess_run(den, e_buf); fwrite(e_buf, sizeof(short), NN, e_fd); } speex_echo_state_destroy(st); speex_preprocess_state_destroy(den); fclose(e_fd); fclose(echo_fd); fclose(ref_fd); return 0; } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/testenc.c ================================================ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifdef FIXED_DEBUG extern long long spx_mips; #endif #define FRAME_SIZE 160 #include int main(int argc, char **argv) { char *inFile, *outFile, *bitsFile; FILE *fin, *fout, *fbits=NULL; short in_short[FRAME_SIZE]; short out_short[FRAME_SIZE]; int snr_frames = 0; char cbits[200]; int nbBits; int i; void *st; void *dec; SpeexBits bits; spx_int32_t tmp; int bitCount=0; spx_int32_t skip_group_delay; SpeexCallback callback; st = speex_encoder_init(speex_lib_get_mode(SPEEX_MODEID_NB)); dec = speex_decoder_init(speex_lib_get_mode(SPEEX_MODEID_NB)); /* BEGIN: You probably don't need the following in a real application */ callback.callback_id = SPEEX_INBAND_CHAR; callback.func = speex_std_char_handler; callback.data = stderr; speex_decoder_ctl(dec, SPEEX_SET_HANDLER, &callback); callback.callback_id = SPEEX_INBAND_MODE_REQUEST; callback.func = speex_std_mode_request_handler; callback.data = st; speex_decoder_ctl(dec, SPEEX_SET_HANDLER, &callback); /* END of unnecessary stuff */ tmp=1; speex_decoder_ctl(dec, SPEEX_SET_ENH, &tmp); tmp=0; speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp); tmp=8; speex_encoder_ctl(st, SPEEX_SET_QUALITY, &tmp); tmp=1; speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &tmp); /* Turn this off if you want to measure SNR (on by default) */ tmp=1; speex_encoder_ctl(st, SPEEX_SET_HIGHPASS, &tmp); speex_decoder_ctl(dec, SPEEX_SET_HIGHPASS, &tmp); speex_encoder_ctl(st, SPEEX_GET_LOOKAHEAD, &skip_group_delay); speex_decoder_ctl(dec, SPEEX_GET_LOOKAHEAD, &tmp); skip_group_delay += tmp; if (argc != 4 && argc != 3) { fprintf (stderr, "Usage: encode [in file] [out file] [bits file]\nargc = %d", argc); exit(1); } inFile = argv[1]; fin = fopen(inFile, "rb"); outFile = argv[2]; fout = fopen(outFile, "wb+"); if (argc==4) { bitsFile = argv[3]; fbits = fopen(bitsFile, "wb"); } speex_bits_init(&bits); while (!feof(fin)) { fread(in_short, sizeof(short), FRAME_SIZE, fin); if (feof(fin)) break; speex_bits_reset(&bits); speex_encode_int(st, in_short, &bits); nbBits = speex_bits_write(&bits, cbits, 200); bitCount+=bits.nbBits; if (argc==4) fwrite(cbits, 1, nbBits, fbits); speex_bits_rewind(&bits); speex_decode_int(dec, &bits, out_short); speex_bits_reset(&bits); fwrite(&out_short[skip_group_delay], sizeof(short), FRAME_SIZE-skip_group_delay, fout); skip_group_delay = 0; } fprintf (stderr, "Total encoded size: %d bits\n", bitCount); speex_encoder_destroy(st); speex_decoder_destroy(dec); speex_bits_destroy(&bits); #ifndef DISABLE_FLOAT_API { float sigpow,errpow,snr, seg_snr=0; sigpow = 0; errpow = 0; /* This code just computes SNR, so you don't need it either */ rewind(fin); rewind(fout); while ( FRAME_SIZE == fread(in_short, sizeof(short), FRAME_SIZE, fin) && FRAME_SIZE == fread(out_short, sizeof(short), FRAME_SIZE,fout) ) { float s=0, e=0; for (i=0;i #include #include #include #ifdef FIXED_DEBUG extern long long spx_mips; #endif #define FRAME_SIZE 640 #include int main(int argc, char **argv) { char *inFile, *outFile, *bitsFile; FILE *fin, *fout, *fbits=NULL; short in_short[FRAME_SIZE]; short out_short[FRAME_SIZE]; float in_float[FRAME_SIZE]; float sigpow,errpow,snr, seg_snr=0; int snr_frames = 0; char cbits[200]; int nbBits; int i; void *st; void *dec; SpeexBits bits; spx_int32_t tmp; int bitCount=0; spx_int32_t skip_group_delay; SpeexCallback callback; sigpow = 0; errpow = 0; st = speex_encoder_init(speex_lib_get_mode(SPEEX_MODEID_UWB)); dec = speex_decoder_init(speex_lib_get_mode(SPEEX_MODEID_UWB)); callback.callback_id = SPEEX_INBAND_CHAR; callback.func = speex_std_char_handler; callback.data = stderr; speex_decoder_ctl(dec, SPEEX_SET_HANDLER, &callback); callback.callback_id = SPEEX_INBAND_MODE_REQUEST; callback.func = speex_std_mode_request_handler; callback.data = st; speex_decoder_ctl(dec, SPEEX_SET_HANDLER, &callback); tmp=0; speex_decoder_ctl(dec, SPEEX_SET_ENH, &tmp); tmp=0; speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp); tmp=7; speex_encoder_ctl(st, SPEEX_SET_QUALITY, &tmp); tmp=1; speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &tmp); speex_encoder_ctl(st, SPEEX_GET_LOOKAHEAD, &skip_group_delay); speex_decoder_ctl(dec, SPEEX_GET_LOOKAHEAD, &tmp); skip_group_delay += tmp; if (argc != 4 && argc != 3) { fprintf (stderr, "Usage: encode [in file] [out file] [bits file]\nargc = %d", argc); exit(1); } inFile = argv[1]; fin = fopen(inFile, "rb"); outFile = argv[2]; fout = fopen(outFile, "wb+"); if (argc==4) { bitsFile = argv[3]; fbits = fopen(bitsFile, "wb"); } speex_bits_init(&bits); while (!feof(fin)) { fread(in_short, sizeof(short), FRAME_SIZE, fin); if (feof(fin)) break; for (i=0;i #include #include #include #ifdef FIXED_DEBUG extern long long spx_mips; #endif #define FRAME_SIZE 320 #include int main(int argc, char **argv) { char *inFile, *outFile, *bitsFile; FILE *fin, *fout, *fbits=NULL; short in_short[FRAME_SIZE]; short out_short[FRAME_SIZE]; float sigpow,errpow,snr, seg_snr=0; int snr_frames = 0; char cbits[200]; int nbBits; int i; void *st; void *dec; SpeexBits bits; spx_int32_t tmp; int bitCount=0; spx_int32_t skip_group_delay; SpeexCallback callback; sigpow = 0; errpow = 0; st = speex_encoder_init(speex_lib_get_mode(SPEEX_MODEID_WB)); dec = speex_decoder_init(speex_lib_get_mode(SPEEX_MODEID_WB)); callback.callback_id = SPEEX_INBAND_CHAR; callback.func = speex_std_char_handler; callback.data = stderr; speex_decoder_ctl(dec, SPEEX_SET_HANDLER, &callback); callback.callback_id = SPEEX_INBAND_MODE_REQUEST; callback.func = speex_std_mode_request_handler; callback.data = st; speex_decoder_ctl(dec, SPEEX_SET_HANDLER, &callback); tmp=1; speex_decoder_ctl(dec, SPEEX_SET_ENH, &tmp); tmp=0; speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp); tmp=8; speex_encoder_ctl(st, SPEEX_SET_QUALITY, &tmp); tmp=3; speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &tmp); /*tmp=3; speex_encoder_ctl(st, SPEEX_SET_HIGH_MODE, &tmp); tmp=6; speex_encoder_ctl(st, SPEEX_SET_LOW_MODE, &tmp); */ speex_encoder_ctl(st, SPEEX_GET_LOOKAHEAD, &skip_group_delay); speex_decoder_ctl(dec, SPEEX_GET_LOOKAHEAD, &tmp); skip_group_delay += tmp; if (argc != 4 && argc != 3) { fprintf (stderr, "Usage: encode [in file] [out file] [bits file]\nargc = %d", argc); exit(1); } inFile = argv[1]; fin = fopen(inFile, "rb"); outFile = argv[2]; fout = fopen(outFile, "wb+"); if (argc==4) { bitsFile = argv[3]; fbits = fopen(bitsFile, "wb"); } speex_bits_init(&bits); while (!feof(fin)) { fread(in_short, sizeof(short), FRAME_SIZE, fin); if (feof(fin)) break; speex_bits_reset(&bits); speex_encode_int(st, in_short, &bits); nbBits = speex_bits_write(&bits, cbits, 200); bitCount+=bits.nbBits; if (argc==4) fwrite(cbits, 1, nbBits, fbits); speex_bits_rewind(&bits); speex_decode_int(dec, &bits, out_short); speex_bits_reset(&bits); fwrite(&out_short[skip_group_delay], sizeof(short), FRAME_SIZE-skip_group_delay, fout); skip_group_delay = 0; } fprintf (stderr, "Total encoded size: %d bits\n", bitCount); speex_encoder_destroy(st); speex_decoder_destroy(dec); speex_bits_destroy(&bits); rewind(fin); rewind(fout); while ( FRAME_SIZE == fread(in_short, sizeof(short), FRAME_SIZE, fin) && FRAME_SIZE == fread(out_short, sizeof(short), FRAME_SIZE,fout) ) { float s=0, e=0; for (i=0;i #include union jbpdata { unsigned int idx; unsigned char data[4]; }; void synthIn(JitterBufferPacket *in, int idx, int span) { union jbpdata d; d.idx = idx; in->data = d.data; in->len = sizeof(d); in->timestamp = idx * 10; in->span = span * 10; in->sequence = idx; in->user_data = 0; } void jitterFill(JitterBuffer *jb) { char buffer[65536]; JitterBufferPacket in, out; int i; out.data = buffer; jitter_buffer_reset(jb); for(i=0;i<100;++i) { synthIn(&in, i, 1); jitter_buffer_put(jb, &in); out.len = 65536; if (jitter_buffer_get(jb, &out, 10, NULL) != JITTER_BUFFER_OK) { printf("Fill test failed iteration %d\n", i); } if (out.timestamp != i * 10) { printf("Fill test expected %d got %d\n", i*10, out.timestamp); } jitter_buffer_tick(jb); } } int main() { char buffer[65536]; JitterBufferPacket in, out; int i; JitterBuffer *jb = jitter_buffer_init(10); out.data = buffer; /* Frozen sender case */ jitterFill(jb); for(i=0;i<100;++i) { out.len = 65536; jitter_buffer_get(jb, &out, 10, NULL); jitter_buffer_tick(jb); } synthIn(&in, 100, 1); jitter_buffer_put(jb, &in); out.len = 65536; if (jitter_buffer_get(jb, &out, 10, NULL) != JITTER_BUFFER_OK) { printf("Failed frozen sender resynchronize\n"); } else { printf("Frozen sender: Jitter %d\n", out.timestamp - 100*10); } return 0; } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/testresample.c ================================================ /* Copyright (C) 2007 Jean-Marc Valin File: testresample.c Testing the resampling code Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "speex/speex_resampler.h" #include #include #define NN 256 int main() { spx_uint32_t i; short *in; short *out; float *fin, *fout; int count = 0; SpeexResamplerState *st = speex_resampler_init(1, 8000, 12000, 10, NULL); speex_resampler_set_rate(st, 96000, 44100); speex_resampler_skip_zeros(st); in = malloc(NN*sizeof(short)); out = malloc(2*NN*sizeof(short)); fin = malloc(NN*sizeof(float)); fout = malloc(2*NN*sizeof(float)); while (1) { spx_uint32_t in_len; spx_uint32_t out_len; fread(in, sizeof(short), NN, stdin); if (feof(stdin)) break; for (i=0;i #define sqr(x) ((x)*(x)) #define MIN_ENERGY 6000 #define NOISE_POW .3 #ifndef DISABLE_VBR const float vbr_nb_thresh[9][11]={ {-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f}, /* CNG */ { 4.0f, 2.5f, 2.0f, 1.2f, 0.5f, 0.0f, -0.5f, -0.7f, -0.8f, -0.9f, -1.0f}, /* 2 kbps */ {10.0f, 6.5f, 5.2f, 4.5f, 3.9f, 3.5f, 3.0f, 2.5f, 2.3f, 1.8f, 1.0f}, /* 6 kbps */ {11.0f, 8.8f, 7.5f, 6.5f, 5.0f, 3.9f, 3.9f, 3.9f, 3.5f, 3.0f, 1.0f}, /* 8 kbps */ {11.0f, 11.0f, 9.9f, 8.5f, 7.0f, 6.0f, 4.5f, 4.0f, 4.0f, 4.0f, 2.0f}, /* 11 kbps */ {11.0f, 11.0f, 11.0f, 11.0f, 9.5f, 8.5f, 8.0f, 7.0f, 6.0f, 5.0f, 3.0f}, /* 15 kbps */ {11.0f, 11.0f, 11.0f, 11.0f, 11.0f, 11.0f, 9.5f, 8.5f, 7.0f, 6.0f, 5.0f}, /* 18 kbps */ {11.0f, 11.0f, 11.0f, 11.0f, 11.0f, 11.0f, 11.0f, 11.0f, 9.8f, 9.5f, 7.5f}, /* 24 kbps */ { 7.0f, 4.5f, 3.7f, 3.0f, 2.5f, 2.0f, 1.8f, 1.5f, 1.0f, 0.0f, 0.0f} /* 4 kbps */ }; const float vbr_hb_thresh[5][11]={ {-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f}, /* silence */ {-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f}, /* 2 kbps */ {11.0f, 11.0f, 9.5f, 8.5f, 7.5f, 6.0f, 5.0f, 3.9f, 3.0f, 2.0f, 1.0f}, /* 6 kbps */ {11.0f, 11.0f, 11.0f, 11.0f, 11.0f, 9.5f, 8.7f, 7.8f, 7.0f, 6.5f, 4.0f}, /* 10 kbps */ {11.0f, 11.0f, 11.0f, 11.0f, 11.0f, 11.0f, 11.0f, 11.0f, 9.8f, 7.5f, 5.5f} /* 18 kbps */ }; const float vbr_uhb_thresh[2][11]={ {-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f}, /* silence */ { 3.9f, 2.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f} /* 2 kbps */ }; void vbr_init(VBRState *vbr) { int i; vbr->average_energy=0; vbr->last_energy=1; vbr->accum_sum=0; vbr->energy_alpha=.1; vbr->soft_pitch=0; vbr->last_pitch_coef=0; vbr->last_quality=0; vbr->noise_accum = .05*pow(MIN_ENERGY, NOISE_POW); vbr->noise_accum_count=.05; vbr->noise_level=vbr->noise_accum/vbr->noise_accum_count; vbr->consec_noise=0; for (i=0;ilast_log_energy[i] = log(MIN_ENERGY); } /* This function should analyse the signal and decide how critical the coding error will be perceptually. The following factors should be taken into account: -Attacks (positive energy derivative) should be coded with more bits -Stationary voiced segments should receive more bits -Segments with (very) low absolute energy should receive less bits (maybe only shaped noise?) -DTX for near-zero energy? -Stationary fricative segments should have less bits -Temporal masking: when energy slope is decreasing, decrease the bit-rate -Decrease bit-rate for males (low pitch)? -(wideband only) less bits in the high-band when signal is very non-stationary (harder to notice high-frequency noise)??? */ float vbr_analysis(VBRState *vbr, spx_word16_t *sig, int len, int pitch, float pitch_coef) { int i; float ener=0, ener1=0, ener2=0; float qual=7; int va; float log_energy; float non_st=0; float voicing; float pow_ener; for (i=0;i>1;i++) ener1 += ((float)sig[i])*sig[i]; for (i=len>>1;ilast_log_energy[i]); non_st = non_st/(30*VBR_MEMORY_SIZE); if (non_st>1) non_st=1; voicing = 3*(pitch_coef-.4)*fabs(pitch_coef-.4); vbr->average_energy = (1-vbr->energy_alpha)*vbr->average_energy + vbr->energy_alpha*ener; vbr->noise_level=vbr->noise_accum/vbr->noise_accum_count; pow_ener = pow(ener,NOISE_POW); if (vbr->noise_accum_count<.06 && ener>MIN_ENERGY) vbr->noise_accum = .05*pow_ener; if ((voicing<.3 && non_st < .2 && pow_ener < 1.2*vbr->noise_level) || (voicing<.3 && non_st < .05 && pow_ener < 1.5*vbr->noise_level) || (voicing<.4 && non_st < .05 && pow_ener < 1.2*vbr->noise_level) || (voicing<0 && non_st < .05)) { float tmp; va = 0; vbr->consec_noise++; if (pow_ener > 3*vbr->noise_level) tmp = 3*vbr->noise_level; else tmp = pow_ener; if (vbr->consec_noise>=4) { vbr->noise_accum = .95*vbr->noise_accum + .05*tmp; vbr->noise_accum_count = .95*vbr->noise_accum_count + .05; } } else { va = 1; vbr->consec_noise=0; } if (pow_ener < vbr->noise_level && ener>MIN_ENERGY) { vbr->noise_accum = .95*vbr->noise_accum + .05*pow_ener; vbr->noise_accum_count = .95*vbr->noise_accum_count + .05; } /* Checking for very low absolute energy */ if (ener < 30000) { qual -= .7; if (ener < 10000) qual-=.7; if (ener < 3000) qual-=.7; } else { float short_diff, long_diff; short_diff = log((ener+1)/(1+vbr->last_energy)); long_diff = log((ener+1)/(1+vbr->average_energy)); /*fprintf (stderr, "%f %f\n", short_diff, long_diff);*/ if (long_diff<-5) long_diff=-5; if (long_diff>2) long_diff=2; if (long_diff>0) qual += .6*long_diff; if (long_diff<0) qual += .5*long_diff; if (short_diff>0) { if (short_diff>5) short_diff=5; qual += .5*short_diff; } /* Checking for energy increases */ if (ener2 > 1.6*ener1) qual += .5; } vbr->last_energy = ener; vbr->soft_pitch = .6*vbr->soft_pitch + .4*pitch_coef; qual += 2.2*((pitch_coef-.4) + (vbr->soft_pitch-.4)); if (qual < vbr->last_quality) qual = .5*qual + .5*vbr->last_quality; if (qual<4) qual=4; if (qual>10) qual=10; /* if (vbr->consec_noise>=2) qual-=1.3; if (vbr->consec_noise>=5) qual-=1.3; if (vbr->consec_noise>=12) qual-=1.3; */ if (vbr->consec_noise>=3) qual=4; if (vbr->consec_noise) qual -= 1.0 * (log(3.0 + vbr->consec_noise)-log(3)); if (qual<0) qual=0; if (ener<60000) { if (vbr->consec_noise>2) qual-=0.5*(log(3.0 + vbr->consec_noise)-log(3)); if (ener<10000&&vbr->consec_noise>2) qual-=0.5*(log(3.0 + vbr->consec_noise)-log(3)); if (qual<0) qual=0; qual += .3*log(.0001+ener/60000.0); } if (qual<-1) qual=-1; /*printf ("%f %f %f %f %d\n", qual, voicing, non_st, pow_ener/(.01+vbr->noise_level), va);*/ vbr->last_pitch_coef = pitch_coef; vbr->last_quality = qual; for (i=VBR_MEMORY_SIZE-1;i>0;i--) vbr->last_log_energy[i] = vbr->last_log_energy[i-1]; vbr->last_log_energy[0] = log_energy; /*printf ("VBR: %f %f %f %d %f\n", (float)(log_energy-log(vbr->average_energy+MIN_ENERGY)), non_st, voicing, va, vbr->noise_level);*/ return qual; } void vbr_destroy(VBRState *vbr) { } #endif /* #ifndef DISABLE_VBR */ ================================================ FILE: deps/pjsip/third_party/speex/libspeex/vbr.h ================================================ /* Copyright (C) 2002 Jean-Marc Valin */ /** @file vbr.h @brief Variable Bit-Rate (VBR) related routines */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef VBR_H #define VBR_H #include "arch.h" #define VBR_MEMORY_SIZE 5 extern const float vbr_nb_thresh[9][11]; extern const float vbr_hb_thresh[5][11]; extern const float vbr_uhb_thresh[2][11]; /** VBR state. */ typedef struct VBRState { float energy_alpha; float average_energy; float last_energy; float last_log_energy[VBR_MEMORY_SIZE]; float accum_sum; float last_pitch_coef; float soft_pitch; float last_quality; float noise_level; float noise_accum; float noise_accum_count; int consec_noise; } VBRState; void vbr_init(VBRState *vbr); float vbr_analysis(VBRState *vbr, spx_word16_t *sig, int len, int pitch, float pitch_coef); void vbr_destroy(VBRState *vbr); #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/vorbis_psy.c ================================================ /* Copyright (C) 2005 Jean-Marc Valin, CSIRO, Christopher Montgomery File: vorbis_psy.c Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef VORBIS_PSYCHO #include "arch.h" #include "smallft.h" #include "lpc.h" #include "vorbis_psy.h" #include #include #include /* psychoacoustic setup ********************************************/ static VorbisPsyInfo example_tuning = { .5,.5, 3,3,25, /*63 125 250 500 1k 2k 4k 8k 16k*/ // vorbis mode 4 style //{-32,-32,-32,-32,-28,-24,-22,-20,-20, -20, -20, -8, -6, -6, -6, -6, -6}, { -4, -6, -6, -6, -6, -6, -6, -6, -8, -8,-10,-10, -8, -6, -4, -4, -2}, { 0, 1, 2, 3, 4, 5, 5, 5, /* 7dB */ 6, 6, 6, 5, 4, 4, 4, 4, /* 15dB */ 4, 4, 5, 5, 5, 6, 6, 6, /* 23dB */ 7, 7, 7, 8, 8, 8, 9, 10, /* 31dB */ 11,12,13,14,15,16,17, 18, /* 39dB */ } }; /* there was no great place to put this.... */ #include static void _analysis_output(char *base,int i,float *v,int n,int bark,int dB){ int j; FILE *of; char buffer[80]; sprintf(buffer,"%s_%d.m",base,i); of=fopen(buffer,"w"); if(!of)perror("failed to open data dump file"); for(j=0;j> 16; if( lo>=0 ) break; hi = b[i] & 0xffff; tN = N[hi] + N[-lo]; tX = X[hi] - X[-lo]; tXX = XX[hi] + XX[-lo]; tY = Y[hi] + Y[-lo]; tXY = XY[hi] - XY[-lo]; A = tY * tXX - tX * tXY; B = tN * tXY - tX * tY; D = tN * tXX - tX * tX; R = (A + x * B) / D; if (R < 0.f) R = 0.f; noise[i] = R - offset; } for ( ;; i++, x += 1.f) { lo = b[i] >> 16; hi = b[i] & 0xffff; if(hi>=n)break; tN = N[hi] - N[lo]; tX = X[hi] - X[lo]; tXX = XX[hi] - XX[lo]; tY = Y[hi] - Y[lo]; tXY = XY[hi] - XY[lo]; A = tY * tXX - tX * tXY; B = tN * tXY - tX * tY; D = tN * tXX - tX * tX; R = (A + x * B) / D; if (R < 0.f) R = 0.f; noise[i] = R - offset; } for ( ; i < n; i++, x += 1.f) { R = (A + x * B) / D; if (R < 0.f) R = 0.f; noise[i] = R - offset; } if (fixed <= 0) return; for (i = 0, x = 0.f;; i++, x += 1.f) { hi = i + fixed / 2; lo = hi - fixed; if(lo>=0)break; tN = N[hi] + N[-lo]; tX = X[hi] - X[-lo]; tXX = XX[hi] + XX[-lo]; tY = Y[hi] + Y[-lo]; tXY = XY[hi] - XY[-lo]; A = tY * tXX - tX * tXY; B = tN * tXY - tX * tY; D = tN * tXX - tX * tX; R = (A + x * B) / D; if (R - offset < noise[i]) noise[i] = R - offset; } for ( ;; i++, x += 1.f) { hi = i + fixed / 2; lo = hi - fixed; if(hi>=n)break; tN = N[hi] - N[lo]; tX = X[hi] - X[lo]; tXX = XX[hi] - XX[lo]; tY = Y[hi] - Y[lo]; tXY = XY[hi] - XY[lo]; A = tY * tXX - tX * tXY; B = tN * tXY - tX * tY; D = tN * tXX - tX * tX; R = (A + x * B) / D; if (R - offset < noise[i]) noise[i] = R - offset; } for ( ; i < n; i++, x += 1.f) { R = (A + x * B) / D; if (R - offset < noise[i]) noise[i] = R - offset; } } static void _vp_noisemask(VorbisPsy *p, float *logfreq, float *logmask){ int i,n=p->n/2; float *work=alloca(n*sizeof(*work)); bark_noise_hybridmp(n,p->bark,logfreq,logmask, 140.,-1); for(i=0;ibark,work,logmask,0., p->vi->noisewindowfixed); for(i=0;i=NOISE_COMPAND_LEVELS)dB=NOISE_COMPAND_LEVELS-1; if(dB<0)dB=0; logmask[i]= work[i]+p->vi->noisecompand[dB]+p->noiseoffset[i]; } } VorbisPsy *vorbis_psy_init(int rate, int n) { long i,j,lo=-99,hi=1; VorbisPsy *p = speex_alloc(sizeof(VorbisPsy)); memset(p,0,sizeof(*p)); p->n = n; spx_drft_init(&p->lookup, n); p->bark = speex_alloc(n*sizeof(*p->bark)); p->rate=rate; p->vi = &example_tuning; /* BH4 window */ p->window = speex_alloc(sizeof(*p->window)*n); float a0 = .35875f; float a1 = .48829f; float a2 = .14128f; float a3 = .01168f; for(i=0;iwindow[i] = //a0 - a1*cos(2.*M_PI/n*(i+.5)) + a2*cos(4.*M_PI/n*(i+.5)) - a3*cos(6.*M_PI/n*(i+.5)); sin((i+.5)/n * M_PI)*sin((i+.5)/n * M_PI); /* bark scale lookups */ for(i=0;ivi->noisewindowlominvi->noisewindowlo);lo++); for(;hi<=n && (hivi->noisewindowhimin || toBARK(rate/(2*n)*hi)<(bark+p->vi->noisewindowhi));hi++); p->bark[i]=((lo-1)<<16)+(hi-1); } /* set up rolling noise median */ p->noiseoffset=speex_alloc(n*sizeof(*p->noiseoffset)); for(i=0;i=P_BANDS-1)halfoc=P_BANDS-1; inthalfoc=(int)halfoc; del=halfoc-inthalfoc; p->noiseoffset[i]= p->vi->noiseoff[inthalfoc]*(1.-del) + p->vi->noiseoff[inthalfoc+1]*del; } #if 0 _analysis_output_always("noiseoff0",ls,p->noiseoffset,n,1,0,0); #endif return p; } void vorbis_psy_destroy(VorbisPsy *p) { if(p){ spx_drft_clear(&p->lookup); if(p->bark) speex_free(p->bark); if(p->noiseoffset) speex_free(p->noiseoffset); if(p->window) speex_free(p->window); memset(p,0,sizeof(*p)); speex_free(p); } } void compute_curve(VorbisPsy *psy, float *audio, float *curve) { int i; float work[psy->n]; float scale=4.f/psy->n; float scale_dB; scale_dB=todB(scale); /* window the PCM data; use a BH4 window, not vorbis */ for(i=0;in;i++) work[i]=audio[i] * psy->window[i]; { static int seq=0; //_analysis_output("win",seq,work,psy->n,0,0); seq++; } /* FFT yields more accurate tonal estimation (not phase sensitive) */ spx_drft_forward(&psy->lookup,work); /* magnitudes */ work[0]=scale_dB+todB(work[0]); for(i=1;in-1;i+=2){ float temp = work[i]*work[i] + work[i+1]*work[i+1]; work[(i+1)>>1] = scale_dB+.5f * todB(temp); } /* derive a noise curve */ _vp_noisemask(psy,work,curve); #define SIDEL 12 for (i=0;in>>1)-i-1]=curve[(psy->n>>1)-SIDEH]; } for(i=0;i<((psy->n)>>1);i++) curve[i] = fromdB(1.2*curve[i]+.2*i); //curve[i] = fromdB(0.8*curve[i]+.35*i); //curve[i] = fromdB(0.9*curve[i])*pow(1.0*i+45,1.3); } /* Transform a masking curve (power spectrum) into a pole-zero filter */ void curve_to_lpc(VorbisPsy *psy, float *curve, float *awk1, float *awk2, int ord) { int i; float ac[psy->n]; float tmp; int len = psy->n >> 1; for (i=0;i<2*len;i++) ac[i] = 0; for (i=1;ilookup, ac); _spx_lpc(awk1, ac, ord); tmp = 1.; for (i=0;ilookup, ac); /* Compute (power) response of awk1 (all zero) */ ac[0] *= ac[0]; for (i=1;ilookup, ac); _spx_lpc(awk2, ac, ord); tmp = 1; for (i=0;i #include #define ORDER 10 #define CURVE_SIZE 24 int main() { int i; float curve[CURVE_SIZE]; float awk1[ORDER], awk2[ORDER]; for (i=0;i1e-13?log((x)*(x))*4.34294480f:-30) #define fromdB(x) (exp((x)*.11512925f)) /* The bark scale equations are approximations, since the original table was somewhat hand rolled. The below are chosen to have the best possible fit to the rolled tables, thus their somewhat odd appearance (these are more accurate and over a longer range than the oft-quoted bark equations found in the texts I have). The approximations are valid from 0 - 30kHz (nyquist) or so. all f in Hz, z in Bark */ #define toBARK(n) (13.1f*atan(.00074f*(n))+2.24f*atan((n)*(n)*1.85e-8f)+1e-4f*(n)) #define fromBARK(z) (102.f*(z)-2.f*pow(z,2.f)+.4f*pow(z,3.f)+pow(1.46f,z)-1.f) /* Frequency to octave. We arbitrarily declare 63.5 Hz to be octave 0.0 */ #define toOC(n) (log(n)*1.442695f-5.965784f) #define fromOC(o) (exp(((o)+5.965784f)*.693147f)) typedef struct { float noisewindowlo; float noisewindowhi; int noisewindowlomin; int noisewindowhimin; int noisewindowfixed; float noiseoff[P_BANDS]; float noisecompand[NOISE_COMPAND_LEVELS]; } VorbisPsyInfo; typedef struct { int n; int rate; struct drft_lookup lookup; VorbisPsyInfo *vi; float *window; float *noiseoffset; long *bark; } VorbisPsy; VorbisPsy *vorbis_psy_init(int rate, int size); void vorbis_psy_destroy(VorbisPsy *psy); void compute_curve(VorbisPsy *psy, float *audio, float *curve); void curve_to_lpc(VorbisPsy *psy, float *curve, float *awk1, float *awk2, int ord); #endif #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/vq.c ================================================ /* Copyright (C) 2002 Jean-Marc Valin File: vq.c Vector quantization Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "vq.h" #include "stack_alloc.h" #include "arch.h" #ifdef _USE_SSE #include #include "vq_sse.h" #elif defined(SHORTCUTS) && (defined(ARM4_ASM) || defined(ARM5E_ASM)) #include "vq_arm4.h" #elif defined(BFIN_ASM) #include "vq_bfin.h" #endif int scal_quant(spx_word16_t in, const spx_word16_t *boundary, int entries) { int i=0; while (iboundary[0]) { boundary++; i++; } return i; } int scal_quant32(spx_word32_t in, const spx_word32_t *boundary, int entries) { int i=0; while (iboundary[0]) { boundary++; i++; } return i; } #ifndef OVERRIDE_VQ_NBEST /*Finds the indices of the n-best entries in a codebook*/ void vq_nbest(spx_word16_t *in, const spx_word16_t *codebook, int len, int entries, spx_word32_t *E, int N, int *nbest, spx_word32_t *best_dist, char *stack) { int i,j,k,used; used = 0; for (i=0;i= 1) && (k > used || dist < best_dist[k-1]); k--) { best_dist[k]=best_dist[k-1]; nbest[k] = nbest[k-1]; } best_dist[k]=dist; nbest[k]=i; used++; } } } #endif #ifndef OVERRIDE_VQ_NBEST_SIGN /*Finds the indices of the n-best entries in a codebook with sign*/ void vq_nbest_sign(spx_word16_t *in, const spx_word16_t *codebook, int len, int entries, spx_word32_t *E, int N, int *nbest, spx_word32_t *best_dist, char *stack) { int i,j,k, sign, used; used=0; for (i=0;i0) { sign=0; dist=-dist; } else { sign=1; } #ifdef FIXED_POINT dist = ADD32(dist,SHR32(E[i],1)); #else dist = ADD32(dist,.5f*E[i]); #endif if (i= 1) && (k > used || dist < best_dist[k-1]); k--) { best_dist[k]=best_dist[k-1]; nbest[k] = nbest[k-1]; } best_dist[k]=dist; nbest[k]=i; used++; if (sign) nbest[k]+=entries; } } } #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/vq.h ================================================ /* Copyright (C) 2002 Jean-Marc Valin */ /** @file vq.h @brief Vector quantization */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef VQ_H #define VQ_H #include "arch.h" int scal_quant(spx_word16_t in, const spx_word16_t *boundary, int entries); int scal_quant32(spx_word32_t in, const spx_word32_t *boundary, int entries); #ifdef _USE_SSE #include void vq_nbest(spx_word16_t *in, const __m128 *codebook, int len, int entries, __m128 *E, int N, int *nbest, spx_word32_t *best_dist, char *stack); void vq_nbest_sign(spx_word16_t *in, const __m128 *codebook, int len, int entries, __m128 *E, int N, int *nbest, spx_word32_t *best_dist, char *stack); #else void vq_nbest(spx_word16_t *in, const spx_word16_t *codebook, int len, int entries, spx_word32_t *E, int N, int *nbest, spx_word32_t *best_dist, char *stack); void vq_nbest_sign(spx_word16_t *in, const spx_word16_t *codebook, int len, int entries, spx_word32_t *E, int N, int *nbest, spx_word32_t *best_dist, char *stack); #endif #endif ================================================ FILE: deps/pjsip/third_party/speex/libspeex/vq_arm4.h ================================================ /* Copyright (C) 2004 Jean-Marc Valin */ /** @file vq_arm4.h @brief ARM4-optimized vq routine */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define OVERRIDE_VQ_NBEST void vq_nbest(spx_word16_t *in, const spx_word16_t *codebook, int len, int entries, spx_word32_t *E, int N, int *nbest, spx_word32_t *best_dist, char *stack) { int i,j; for (i=0;i>= 1;\n\t" "A0 = %0;\n\t" "R0.L = W[%1++%7] || R1.L = W[I0++];\n\t" "LOOP vq_loop%= LC1 = %5;\n\t" "LOOP_BEGIN vq_loop%=;\n\t" "%0 = (A0 -= R0.L*R1.L) (IS) || R0.L = W[%1++%7] || R1.L = W[I0++];\n\t" "LOOP_END vq_loop%=;\n\t" "%0 = (A0 -= R0.L*R1.L) (IS);\n\t" "cc = %0 < %2;\n\t" "if cc %2 = %0;\n\t" "if cc %3 = R2;\n\t" "R2 += 1;\n\t" "LOOP_END entries_loop%=;\n\t" : "=&D" (dist), "=&a" (codebook), "=&d" (best_dist[0]), "=&d" (nbest[0]), "=&a" (E) : "a" (len-1), "a" (in), "a" (2), "d" (entries), "d" (len<<1), "1" (codebook), "4" (E), "2" (best_dist[0]), "3" (nbest[0]) : "R0", "R1", "R2", "I0", "L0", "B0", "A0", "cc", "memory" ); } } else { int i,k,used; used = 0; for (i=0;i>= 1;\n\t" "A0 = %0;\n\t" "I0 = %3;\n\t" "L0 = 0;\n\t" "R0.L = W[%1++%4] || R1.L = W[I0++];\n\t" "LOOP vq_loop%= LC0 = %2;\n\t" "LOOP_BEGIN vq_loop%=;\n\t" "%0 = (A0 -= R0.L*R1.L) (IS) || R0.L = W[%1++%4] || R1.L = W[I0++];\n\t" "LOOP_END vq_loop%=;\n\t" "%0 = (A0 -= R0.L*R1.L) (IS);\n\t" : "=D" (dist), "=a" (codebook) : "a" (len-1), "a" (in), "a" (2), "1" (codebook), "0" (E[i]) : "R0", "R1", "I0", "L0", "A0" ); if (i= 1) && (k > used || dist < best_dist[k-1]); k--) { best_dist[k]=best_dist[k-1]; nbest[k] = nbest[k-1]; } best_dist[k]=dist; nbest[k]=i; used++; } } } } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/vq_sse.h ================================================ /* Copyright (C) 2004 Jean-Marc Valin */ /** @file vq_sse.h @brief SSE-optimized vq routine */ /* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define OVERRIDE_VQ_NBEST void vq_nbest(spx_word16_t *_in, const __m128 *codebook, int len, int entries, __m128 *E, int N, int *nbest, spx_word32_t *best_dist, char *stack) { int i,j,k,used; VARDECL(float *dist); VARDECL(__m128 *in); __m128 half; used = 0; ALLOC(dist, entries, float); half = _mm_set_ps1(.5f); ALLOC(in, len, __m128); for (i=0;i>2;i++) { __m128 d = _mm_mul_ps(E[i], half); for (j=0;j= 1) && (k > used || dist[i] < best_dist[k-1]); k--) { best_dist[k]=best_dist[k-1]; nbest[k] = nbest[k-1]; } best_dist[k]=dist[i]; nbest[k]=i; used++; } } } #define OVERRIDE_VQ_NBEST_SIGN void vq_nbest_sign(spx_word16_t *_in, const __m128 *codebook, int len, int entries, __m128 *E, int N, int *nbest, spx_word32_t *best_dist, char *stack) { int i,j,k,used; VARDECL(float *dist); VARDECL(__m128 *in); __m128 half; used = 0; ALLOC(dist, entries, float); half = _mm_set_ps1(.5f); ALLOC(in, len, __m128); for (i=0;i>2;i++) { __m128 d = _mm_setzero_ps(); for (j=0;j0) { sign=0; dist[i]=-dist[i]; } else { sign=1; } dist[i] += .5f*((float*)E)[i]; if (i= 1) && (k > used || dist[i] < best_dist[k-1]); k--) { best_dist[k]=best_dist[k-1]; nbest[k] = nbest[k-1]; } best_dist[k]=dist[i]; nbest[k]=i; used++; if (sign) nbest[k]+=entries; } } } ================================================ FILE: deps/pjsip/third_party/speex/libspeex/window.c ================================================ /* Copyright (C) 2006 Jean-Marc Valin File: window.c Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "arch.h" #ifdef FIXED_POINT const spx_word16_t lag_window[11] = { 16384, 16337, 16199, 15970, 15656, 15260, 14790, 14254, 13659, 13015, 12330 }; const spx_word16_t lpc_window[200] = { 1310, 1313, 1321, 1333, 1352, 1375, 1403, 1436, 1475, 1518, 1567, 1621, 1679, 1743, 1811, 1884, 1962, 2044, 2132, 2224, 2320, 2421, 2526, 2636, 2750, 2868, 2990, 3116, 3246, 3380, 3518, 3659, 3804, 3952, 4104, 4259, 4417, 4578, 4742, 4909, 5079, 5251, 5425, 5602, 5781, 5963, 6146, 6331, 6518, 6706, 6896, 7087, 7280, 7473, 7668, 7863, 8059, 8256, 8452, 8650, 8847, 9044, 9241, 9438, 9635, 9831, 10026, 10220, 10414, 10606, 10797, 10987, 11176, 11363, 11548, 11731, 11912, 12091, 12268, 12443, 12615, 12785, 12952, 13116, 13277, 13435, 13590, 13742, 13890, 14035, 14176, 14314, 14448, 14578, 14704, 14826, 14944, 15058, 15168, 15273, 15374, 15470, 15562, 15649, 15732, 15810, 15883, 15951, 16015, 16073, 16127, 16175, 16219, 16257, 16291, 16319, 16342, 16360, 16373, 16381, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16361, 16294, 16183, 16028, 15830, 15588, 15304, 14979, 14613, 14207, 13763, 13282, 12766, 12215, 11631, 11016, 10373, 9702, 9007, 8289, 7551, 6797, 6028, 5251, 4470, 3695, 2943, 2248, 1696 }; #else const spx_word16_t lag_window[11] = { 1.00000, 0.99716, 0.98869, 0.97474, 0.95554, 0.93140, 0.90273, 0.86998, 0.83367, 0.79434, 0.75258 }; const spx_word16_t lpc_window[200] = { 0.080000f, 0.080158f, 0.080630f, 0.081418f, 0.082520f, 0.083935f, 0.085663f, 0.087703f, 0.090052f, 0.092710f, 0.095674f, 0.098943f, 0.102514f, 0.106385f, 0.110553f, 0.115015f, 0.119769f, 0.124811f, 0.130137f, 0.135744f, 0.141628f, 0.147786f, 0.154212f, 0.160902f, 0.167852f, 0.175057f, 0.182513f, 0.190213f, 0.198153f, 0.206328f, 0.214731f, 0.223357f, 0.232200f, 0.241254f, 0.250513f, 0.259970f, 0.269619f, 0.279453f, 0.289466f, 0.299651f, 0.310000f, 0.320507f, 0.331164f, 0.341965f, 0.352901f, 0.363966f, 0.375151f, 0.386449f, 0.397852f, 0.409353f, 0.420943f, 0.432615f, 0.444361f, 0.456172f, 0.468040f, 0.479958f, 0.491917f, 0.503909f, 0.515925f, 0.527959f, 0.540000f, 0.552041f, 0.564075f, 0.576091f, 0.588083f, 0.600042f, 0.611960f, 0.623828f, 0.635639f, 0.647385f, 0.659057f, 0.670647f, 0.682148f, 0.693551f, 0.704849f, 0.716034f, 0.727099f, 0.738035f, 0.748836f, 0.759493f, 0.770000f, 0.780349f, 0.790534f, 0.800547f, 0.810381f, 0.820030f, 0.829487f, 0.838746f, 0.847800f, 0.856643f, 0.865269f, 0.873672f, 0.881847f, 0.889787f, 0.897487f, 0.904943f, 0.912148f, 0.919098f, 0.925788f, 0.932214f, 0.938372f, 0.944256f, 0.949863f, 0.955189f, 0.960231f, 0.964985f, 0.969447f, 0.973615f, 0.977486f, 0.981057f, 0.984326f, 0.987290f, 0.989948f, 0.992297f, 0.994337f, 0.996065f, 0.997480f, 0.998582f, 0.999370f, 0.999842f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, 0.998640f, 0.994566f, 0.987787f, 0.978324f, 0.966203f, 0.951458f, 0.934131f, 0.914270f, 0.891931f, 0.867179f, 0.840084f, 0.810723f, 0.779182f, 0.745551f, 0.709930f, 0.672424f, 0.633148f, 0.592223f, 0.549781f, 0.505964f, 0.460932f, 0.414863f, 0.367968f, 0.320511f, 0.272858f, 0.225569f, 0.179655f, 0.137254f, 0.103524f }; #endif ================================================ FILE: deps/pjsip/third_party/speex/symbian/config.h ================================================ /* Copyright (C) 2003 Commonwealth Scientific and Industrial Research Organisation (CSIRO) Australia Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of CSIRO Australia nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CONFIG_H #define CONFIG_H /* An inline macro is required for use of the inline keyword as not all C compilers support */ /* inline. It is officially C99 and C++ only */ #ifdef __WINS__ #define inline __inline /* Disable some pointless/stupid warnings */ #pragma warning(disable: 4100) /* unreferenced formal parameter */ #pragma warning(disable: 4127) /* conditional expression is constant */ #pragma warning(disable: 4305) /* truncation from '...' to '...' */ #pragma warning(disable: 4244) /* conversion from '...' to '...', possible loss of data */ #pragma warning(disable: 4701) /* local variable may be be used without having been initialized */ #endif /* ! __WINS__ */ /* Use only fixed point arithmetic */ #define FIXED_POINT 1 #define USE_KISS_FFT #define EXPORT #endif /* ! CONFIG_H */ ================================================ FILE: deps/pjsip/third_party/speex/win32/config.h ================================================ // Microsoft version of 'inline' #define inline __inline // Visual Studio support alloca(), but it always align variables to 16-bit // boundary, while SSE need 128-bit alignment. So we disable alloca() when // SSE is enabled. #ifndef _USE_SSE # define USE_ALLOCA #endif /* Default to floating point */ #ifndef FIXED_POINT # define USE_SMALLFT #else # define USE_KISS_FFT #endif /* We don't support visibility on Win32 */ #define EXPORT ================================================ FILE: deps/pjsip/third_party/srtp/CHANGES ================================================ Changelog 1.3.20 Lots of changes. Thanks to Jeff Chan for catching a memory leak and helping track down the endian issues with the SSRCs. 1.3.8 This is an interim release. Several little-endian bugs were identified and fixed; this means that we can use intel/linux for development again. Cleaned up sha1 and hmac code significantly, got rid of some excess functions and properly documented the fuctions in the .h files. Eliminated some vestigial files. There is a SIGBUS error in the AES encrypt function on sparc (observed on both solaris and openbsd) with gcc 2.95. Was unable to find bad pointer anywhere, so I'm wondering if it isn't a compiler problem (there's a known problem whose profile it fits). It doesn't appear on any other platform, even in the cipher_driver stress tests. Planned changes Change interface to nonces (xtd_seq_num_t) so that it uses network byte ordering, and is consistent with other arguments. 1.3.6 Changed /dev/random (in configure.in and crypto/rng/rand_source.c) to /dev/urandom; the latter is non-blocking on all known platforms (which corrects some programs that seem to hang) and is actually present on Open BSD (unlike /dev/random, which only works in the presence of hardware supported random number generation). Added machine/types.h case in include/integers.h. 1.3.5 Removing srtp_t::template and stream_clone(). Adding a new policy structure, which will reflect a complete SRTP policy (including SRTCP). This version is *incomplete* and will undergo more changes. It is provided only as a basis for discussion. 1.3.4 Removed tmmh.c and tmmh.h, which implemented version one of TMMH. Changed srtp_get_trailer_length() to act on streams rather than sessions, and documented the macro SRTP_MAX_TRAILER_LEN, which should usually be used rather than that function. Removed 'salt' from cipher input. Changed rdbx to use err.h error codes. Changed malloc() and free() to xalloc() and xfree; these functions are defined in crypto/kernel/alloc.c and declared in include/alloc.h. Added 'output' functions to cipher, in addition to 'encrypt' functions. It is no longer necessary to zeroize a buffer before encrypting in order to get keystream. Changed octet_string_hex_string() so that "times two" isn't needed in its input. Added crypto_kernel_init() prior to command-line parsing, so that kernel can be passed command-line arguments, such as "-d debug_module". This was done to for the applications test/srtp-driver, test/kernel-driver, and test/ust-driver. Improved srtp_init_aes_128_prf - wrote key derivation function (srtp_kdf_t). Add the tag_len as an argument to the auth_compute() function, but not the corresponding macro. This change allows the tag length for a given auth func to be set to different values at initialization time. Previously, the structure auth_t contained the output_length, but that value was inaccessible from hmac_compute() and other functions. Re-named files from a-b.c to a_b.c. in order to help portability. Re-named rijndael to aes (or aes_128 as appropriate). 1.2.1 Changes so that 1.2.0 compiles on cygwin-win2k. Added better error reporting system. If syslog is present on the OS, then it is used. 1.2.0 Many improvements and additions, and a fex fixes Fixed endian issues in RTP header construction in the function rtp_sendto() in srtp/rtp.c. Implemented RIJNDAEL decryption operation, adding the functions rijndael_decrypt() and rijndael_expand_decryption_key(). Also re-named rijndael_expand_key() to rijndael_expand_encryption_key() for consistency. Implemented random number source using /dev/random, in the files crypto/rng/rand_source.c and include/rand_source.h. Added index check to SEAL cipher (only values less than 2^32 are allowed) Added test case for null_auth authentication function. Added a timing test which tests the effect of CPU cache thrash on cipher throughput. The test is done by the function cipher_test_throughput_array(); the function cipher_array_alloc_init() creates an array of ciphers for use in this test. This test can be accessed by using the -a flag to the application cipher-driver in the test subdirectory. Added argument processing to ust-driver.c, and added that app to the 'runtest' target in Makefile.in. A minor auth_t API change: last argument of auth_init() eliminated. 1.0.6 A small but important fix Fixed srtp_init_aes_128_prf() by adding octet_string_set_to_zero() after buffer allocation. Eliminated references to no-longer-existing variables in debugging code in srtp/srtp.c. This fixes the compilation failure that occured when using PRINT_DEBUG in that file. Corrected spelling of Richard Priestley's name in credits. Sorry Richard! 1.0.5 Many little fixes Fixed octet_string_set_to_zero(), which was writing one more zero octet than it should. This bug caused srtp_protect() and srtp_unprotect() to overwrite the byte that followed the srtp packet. Changed sizeof(uint32_t) to srtp_get_trailer_length() in srtp-driver.c. This is just defensive coding. Added NULL check to malloc in srtp_alloc(). 1.0.4 Many minor fixes and two big ones (thanks for the bug reports!) Removed 'ssrc' from the srtp_init_aes_128_prf() function argument list. This is so that applications which do not a priori know the ssrc which they will be receiving can still use libsrtp. Now the SSRC value is gleaned from the rtp header and exored into the counter mode offset in the srtp_protect() and srtp_unprotect() functions, if that cipher is used. This change cascaed through many other functions, including srtp_init_from_hex(), srtp_sender_init() and srtp_receiver_init() in rtp.c, and also changing the CLI to test/rtpw. In the future, another function call will be added to the library that enables multiple ssrc/key pairs to be installed into the same srtp session, so that libsrtp works with multiple srtp senders. For now, this functionality is lacking. Removed the GDOI interface to the rtpw demo program. This will be added again at a later date, after the SRTP and GDOI distributions stabilize. For now, I've left in the GDOI #defines and autoconf definitions so that they'll be in place when needed. Updated tmmhv2_compute() so that it didn't assume any particular alginment of the output tag. Changed bit field variables in srtp.h to unsigned char from unsigned int in order to avoid a potential endianness issue. Fixed rdbx_estimate_index() to handle all input cases. This solves the now notorious "abaft" bug in the rtpw demo app on linux/intel, in which spurious replay protection failures happen after that word is received. Added ntohs(hdr->seq) to srtp_protect and srtp_unprotect, removed from rijndael_icm_set_segment(). Added error checking and handling to srtp_sender_init() and srtp_receiver_init(). Changed srtp_alloc() so that it does what you'd expect: allocate an srtp_ctx_t structure. This hides the library internals. 1.0.1 Many minor fixes Added cipher_driver_buffer_test(...) to test/cipher-driver.c. This function checks that the byte-buffering functions used by a cipher are correct. Fixed SunOS/Solaris build problems: added HAVE_SYS_INT_TYPES_H and changed index_t to xtd_seq_num_t (see include/rdbx.h). Fixed SEAL3.0 output byte buffering, added byte-buffering test to cipher/cipher-driver.c. Fixed roc-driver so that the non-sequential insertion test automatically recovers from bad estimates. This was required to prevent spurious failures. Made rdbx_estimate_index(...) function smarter, so that initial RTP sequence numbers greater than 32,768 don't cause it to estimate the rollover counter of 0xffffffff. 1.0.0 Initial release ================================================ FILE: deps/pjsip/third_party/srtp/LICENSE ================================================ /* * * Copyright (c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ ================================================ FILE: deps/pjsip/third_party/srtp/Makefile.in ================================================ # Makefile for secure rtp # # David A. McGrew # Cisco Systems, Inc. # targets: # # runtest runs test applications # test builds test applications # libcrypt.a static library implementing crypto engine # libsrtp.a static library implementing srtp # clean removes objects, libs, and executables # distribution cleans and builds a .tgz # tags builds etags file from all .c and .h files .PHONY: all test build_table_apps all: test runtest: build_table_apps test @echo "running libsrtp test applications..." crypto/test/cipher_driver$(EXE) -v >/dev/null crypto/test/kernel_driver$(EXE) -v >/dev/null test/rdbx_driver$(EXE) -v >/dev/null test/srtp_driver$(EXE) -v >/dev/null test/roc_driver$(EXE) -v >/dev/null test/replay_driver$(EXE) -v >/dev/null test/dtls_srtp_driver$(EXE) >/dev/null cd test; ./rtpw_test.sh >/dev/null @echo "libsrtp test applications passed." $(MAKE) -C crypto runtest # makefile variables CC = @CC@ INCDIR = -Icrypto/include -I$(srcdir)/include -I$(srcdir)/crypto/include DEFS = @DEFS@ CPPFLAGS= @CPPFLAGS@ CFLAGS = @CFLAGS@ LIBS = @LIBS@ LDFLAGS = @LDFLAGS@ -L. COMPILE = $(CC) $(DEFS) $(INCDIR) $(CPPFLAGS) $(CFLAGS) SRTPLIB = -lsrtp RANLIB = @RANLIB@ INSTALL = @INSTALL@ # EXE defines the suffix on executables - it's .exe for Windows, and # null on linux, bsd, and OS X and other OSes. EXE = @EXE@ # gdoi is the group domain of interpretation for isakmp, a group key # management system which can provide keys for srtp gdoi = @GDOI_OBJS@ # Random source. RNG_OBJS = @RNG_OBJS@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ top_builddir = @top_builddir@ VPATH = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ includedir = @includedir@ libdir = @libdir@ # implicit rules for object files and test apps %.o: %.c $(COMPILE) -c $< -o $@ %$(EXE): %.c $(COMPILE) $(LDFLAGS) $< -o $@ $(SRTPLIB) $(LIBS) # libcrypt.a (the crypto engine) ciphers = crypto/cipher/cipher.o crypto/cipher/null_cipher.o \ crypto/cipher/aes.o crypto/cipher/aes_icm.o \ crypto/cipher/aes_cbc.o hashes = crypto/hash/null_auth.o crypto/hash/sha1.o \ crypto/hash/hmac.o crypto/hash/auth.o # crypto/hash/tmmhv2.o replay = crypto/replay/rdb.o crypto/replay/rdbx.o \ crypto/replay/ut_sim.o math = crypto/math/datatypes.o crypto/math/stat.o ust = crypto/ust/ust.o rng = crypto/rng/$(RNG_OBJS) crypto/rng/prng.o crypto/rng/ctr_prng.o err = crypto/kernel/err.o kernel = crypto/kernel/crypto_kernel.o crypto/kernel/alloc.o \ crypto/kernel/key.o $(rng) $(err) # $(ust) cryptobj = $(ciphers) $(hashes) $(math) $(stat) $(kernel) $(replay) # libsrtp.a (implements srtp processing) srtpobj = srtp/srtp.o libsrtp.a: $(srtpobj) $(cryptobj) $(gdoi) ar cr libsrtp.a $^ $(RANLIB) libsrtp.a # libcryptomath.a contains general-purpose routines that are used to # generate tables and verify cryptoalgorithm implementations - this # library is not meant to be included in production code cryptomath = crypto/math/math.o crypto/math/gf2_8.o libcryptomath.a: $(cryptomath) ar cr libcryptomath.a $(cryptomath) $(RANLIB) libcryptomath.a # test applications crypto_testapp = crypto/test/aes_calc$(EXE) crypto/test/cipher_driver$(EXE) \ crypto/test/datatypes_driver$(EXE) crypto/test/kernel_driver$(EXE) \ crypto/test/rand_gen$(EXE) crypto/test/sha1_driver$(EXE) \ crypto/test/stat_driver$(EXE) testapp = $(crypto_testapp) test/srtp_driver$(EXE) test/replay_driver$(EXE) \ test/roc_driver$(EXE) test/rdbx_driver$(EXE) test/rtpw$(EXE) \ test/dtls_srtp_driver$(EXE) $(testapp): libsrtp.a test/rtpw$(EXE): test/rtpw.c test/rtp.c test/getopt_s.c $(COMPILE) $(LDFLAGS) -o $@ $^ $(LIBS) $(SRTPLIB) test/srtp_driver$(EXE): test/srtp_driver.c test/getopt_s.c $(COMPILE) $(LDFLAGS) -o $@ $^ $(LIBS) $(SRTPLIB) test/rdbx_driver$(EXE): test/rdbx_driver.c test/getopt_s.c $(COMPILE) $(LDFLAGS) -o $@ $^ $(LIBS) $(SRTPLIB) test/dtls_srtp_driver$(EXE): test/dtls_srtp_driver.c test/getopt_s.c $(COMPILE) $(LDFLAGS) -o $@ $^ $(LIBS) $(SRTPLIB) test: $(testapp) @echo "Build done. Please run '$(MAKE) runtest' to run self tests." memtest: test/srtp_driver @test/srtp_driver -v -d "alloc" > tmp @grep freed tmp | wc -l > freed @grep allocated tmp | wc -l > allocated @echo "checking for memory leaks (only works with --enable-stdout)" cmp -s allocated freed @echo "passed (same number of alloc() and dealloc() calls found)" @rm freed allocated tmp # tables_apps are used to generate the tables used in the crypto # implementations; these need only be generated during porting, not # for building libsrtp or the test applications table_apps = tables/aes_tables build_table_apps: $(table_apps) # in the tables/ subdirectory, we use libcryptomath instead of libsrtp tables/%: tables/%.c libcryptomath.a $(COMPILE) $(LDFLAGS) $< -o $@ $(LIBS) libcryptomath.a # the target 'plot' runs the timing test (test/srtp_driver -t) then # uses gnuplot to produce plots of the results - see the script file # 'timing' plot: test/srtp_driver test/srtp_driver -t > timing.dat # bookkeeping: tags, clean, and distribution tags: etags */*.[ch] */*/*.[ch] # documentation - the target libsrtpdoc builds a PDF file documenting # libsrtp libsrtpdoc: $(MAKE) -C doc .PHONY: clean superclean install install: @if [ -d $(DESTDIR)$(includedir)/srtp ]; then \ echo "you should run 'make uninstall' first"; exit 1; \ fi $(INSTALL) -d $(DESTDIR)$(includedir)/srtp $(INSTALL) -d $(DESTDIR)$(libdir) cp include/*.h $(DESTDIR)$(includedir)/srtp cp crypto/include/*.h $(DESTDIR)$(includedir)/srtp if [ -f libsrtp.a ]; then cp libsrtp.a $(DESTDIR)$(libdir)/; fi uninstall: rm -rf $(DESTDIR)$(includedir)/srtp rm -rf $(DESTDIR)$(libdir)/libsrtp.a clean: rm -rf $(cryptobj) $(srtpobj) $(cryptomath) TAGS \ libcryptomath.a libsrtp.a core *.core test/core for a in * */* */*/*; do \ if [ -f "$$a~" ] ; then rm -f $$a~; fi; \ done; for a in $(testapp) $(table_apps); do rm -rf $$a$(EXE); done rm -rf *.pict *.jpg *.dat rm -rf freed allocated tmp $(MAKE) -C doc clean $(MAKE) -C crypto clean superclean: clean rm -rf crypto/include/config.h config.log config.cache config.status \ Makefile .gdb_history test/.gdb_history .DS_Store rm -rf autom4te.cache distname = srtp-$(shell cat VERSION) distribution: runtest superclean if ! [ -f VERSION ]; then exit 1; fi if [ -f ../$(distname).tgz ]; then \ mv ../$(distname).tgz ../$(distname).tgz.bak; \ fi cd ..; tar cvzf $(distname).tgz srtp # EOF ================================================ FILE: deps/pjsip/third_party/srtp/README ================================================ Secure RTP (SRTP) Reference Implementation David A. McGrew Cisco Systems, Inc. mcgrew@cisco.com This package provides an implementation of the Secure Real-time Transport Protocol (SRTP), the Universal Security Transform (UST), and a supporting cryptographic kernel. These mechanisms are documented in the Internet Drafts in the doc/ subdirectory. The SRTP API is documented in include/srtp.h, and the library is in libsrtp.a (after compilation). An overview and reference manual is available in doc/libsrtp.pdf. The PDF documentation is more up to date than this file. Installation: ./configure [ options ] # GNU autoconf script make # or gmake if needed; use GNU make The configure script accepts the following options: --help provides a usage summary --disable-debug compile without the runtime debugging system --enable-syslog use syslog for error reporting --disable-stdout use stdout for error reporting --enable-console use /dev/console for error reporting --gdoi use GDOI key management (disabled at present) By default, debbuging is enabled and stdout is used for debugging. You can use the above configure options to have the debugging output sent to syslog or the system console. Alternatively, you can define ERR_REPORTING_FILE in include/conf.h to be any other file that can be opened by libSRTP, and debug messages will be sent to it. This package has been tested on Mac OS X (powerpc-apple-darwin1.4), Cygwin (i686-pc-cygwin), and Sparc (sparc-sun-solaris2.6). Previous versions have been tested on Linux and OpenBSD on both x86 and sparc platforms. A quick tour of this package: Makefile targets: all, clean, ... README this file CHANGES change log VERSION version number of this package LICENSE legal details (it's a BSD-like license) crypto/ciphers/ ciphers (null, aes_icm, ...) crypto/math/ crypto math routines crypto/hash/ crypto hashing (hmac, tmmhv2, ...) crypto/replay/ replay protection doc/ documentation: rfcs, apis, and suchlike include/ include files for all code in distribution srtp/ secure real-time transport protocol implementation tables/ apps for generating tables (useful in porting) test/ test drivers Applications Several test drivers and a simple and portable srtp application are included in the test/ subdirectory. test driver function tested ------------------------------------------------------------- kernel_driver crypto kernel (ciphers, auth funcs, rng) srtp_driver srtp in-memory tests (does not use the network) rdbx_driver rdbx (extended replay database) roc_driver extended sequence number functions replay_driver replay database (n.b. not used in libsrtp) cipher_driver ciphers auth_driver hash functions The app rtpw is a simple rtp application which reads words from /usr/dict/words and then sends them out one at a time using [s]rtp. Manual srtp keying uses the -k option; automated key management using gdoi will be added later. usage: rtpw [-d ]* [-k [-a][-e]] [-s | -r] dest_ip dest_port or rtpw -l Either the -s (sender) or -r (receiver) option must be chosen. The values dest_ip, dest_port are the ip address and udp port to which the dictionary will be sent, respectively. options: -s (s)rtp sender - causes app to send words -r (s)rtp receive - causes app to receve words -k use srtp master key , where the key is a hexadecimal value (without the leading "0x") -e encrypt/decrypt (for data confidentiality) (requires use of -k option as well) -a message authentication (requires use of -k option as well) -l list debug modules -d turn on debugging for module In order to get random 30-byte values for use as key/salt pairs , you can use the following bash function to format the output of /dev/random (where that device is available). function randhex() { cat /dev/random | od --read-bytes=32 --width=32 -x | awk '{ print $2 $3 $4 $5 $6 $7 $8 $9 $10 $11 $12 $13 $14 $15 $16 }' } An example of an SRTP session using two rtpw programs follows: set k=c1eec3717da76195bb878578790af71c4ee9f859e197a414a78d5abc7451 [sh1]$ test/rtpw -s -k $k -ea 0.0.0.0 9999 Security services: confidentiality message authentication set master key/salt to C1EEC3717DA76195BB878578790AF71C/4EE9F859E197A414A78D5ABC7451 setting SSRC to 2078917053 sending word: A sending word: a sending word: aa sending word: aal ... [sh2]$ test/rtpw -r -k $k -ea 0.0.0.0 9999 security services: confidentiality message authentication set master key/salt to C1EEC3717DA76195BB878578790AF71C/4EE9F859E197A414A78D5ABC7451 19 octets received from SSRC 2078917053 word: A 19 octets received from SSRC 2078917053 word: a 20 octets received from SSRC 2078917053 word: aa 21 octets received from SSRC 2078917053 word: aal ... Implementation Notes * The srtp_protect() function assumes that the buffer holding the rtp packet has enough storage allocated that the authentication tag can be written to the end of that packet. If this assumption is not valid, memory corruption will ensue. * Automated tests for the crypto functions are provided through the cipher_type_self_test() and auth_type_self_test() functions. These functions should be used to test each port of this code to a new platform. * Replay protection is contained in the crypto engine, and tests for it are provided. * This implementation provides calls to initialize, protect, and unprotect RTP packets, and makes as few as possible assumptions about how these functions will be called. For example, the caller is not expected to provide packets in order (though if they're called more than 65k out of sequence, synchronization will be lost). * The sequence number in the rtp packet is used as the low 16 bits of the sender's local packet index. Note that RTP will start its sequence number in a random place, and the SRTP layer just jumps forward to that number at its first invocation. An earlier version of this library used initial sequence numbers that are less than 32,768; this trick is no longer required as the rdbx_estimate_index(...) function has been made smarter. * The replay window is 128 bits in length, and is hard-coded to this value for now. ================================================ FILE: deps/pjsip/third_party/srtp/TODO ================================================ TODO List 1.4.1 - document which fields are in NBO/HBO, and check for consistency. - move HAVE_U_LONG_LONG inside of datatypes.c, or some other separate file - re-write configure.in to make cross-compilation easier - eliminate GENERIC_AESICM by generalizing the code a bit Older comments - add tests for key_limit_t datatype - move octet_get_weight() from datatypes.c to math.c (any other funcs?) Changes and additions planned Make cipher and auth dealloc() functions zeroize the key-storage areas before calling free(). Eliminate key_len from auth_init() Doucument internal APIs (cipher, auth, srtp_protect, ...) SRTP options not (yet) included in this libaray: - the aes-f8-mode cipher - the Master Key Index - re-keying using the key derivation function (only the initial use of the PRF has been implemented, as it's sufficient for most uses) (OLD) PLANNED CHANGES strip out test/lfsr.c Write new documentation!!! Fix the x86 assembly code in aes.c. Eliminate /* DAM */ - there's one in srtp.c Change debugging so that it can print more than one line. Or perhaps just change it so that a single check of the debug-enabled flag is needed. Improve interface between cipher and rdbx - perhaps generalize rdbx into 'nonce' datatype. Make rijndael_icm accept variable sized keys. Add rdbx functions that allow different-sized explicit sequence numbers to be used. Write uniform byte-buffering code for PRFs, preferably as macros. Consider eliminating low-level alloc functions in favor of len() functions, so that there need not be multiple allocations within a particular alloc() function. ================================================ FILE: deps/pjsip/third_party/srtp/VERSION ================================================ 1.4.4 ================================================ FILE: deps/pjsip/third_party/srtp/config.h_win32vc7 ================================================ /* Hacked config.h for Windows XP 32-bit & VC7 */ /* Define if building for a CISC machine (e.g. Intel). */ #define CPU_CISC 1 /* Define if building for a RISC machine (assume slow byte access). */ #undef CPU_RISC /* Path to random device */ #undef DEV_URANDOM /* Define to compile in dynamic debugging system. */ #undef ENABLE_DEBUGGING /* Report errors to this file. */ #undef ERR_REPORTING_FILE /* Define to use logging to stdout. */ #undef ERR_REPORTING_STDOUT /* Define this to use ISMAcryp code. */ #undef GENERIC_AESICM /* Define to 1 if you have the header file. */ #undef HAVE_ARPA_INET_H /* Define to 1 if you have the header file. */ #undef HAVE_BYTESWAP_H /* Define to 1 if you have the `inet_aton' function. */ #define HAVE_INET_ATON 1 /* Define to 1 if the system has the type `int16_t'. */ #undef HAVE_INT16_T /* Define to 1 if the system has the type `int32_t'. */ #undef HAVE_INT32_T /* Define to 1 if the system has the type `int8_t'. */ #undef HAVE_INT8_T /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the `socket' library (-lsocket). */ #undef HAVE_LIBSOCKET /* Define to 1 if you have the header file. */ #undef HAVE_MACHINE_TYPES_H /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_IN_H /* Define to 1 if you have the `socket' function. */ #define HAVE_SOCKET 1 /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the header file. */ #undef HAVE_SYSLOG_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_INT_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKET_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UIO_H /* Define to 1 if the system has the type `uint16_t'. */ #undef HAVE_UINT16_T /* Define to 1 if the system has the type `uint32_t'. */ #undef HAVE_UINT32_T /* Define to 1 if the system has the type `uint64_t'. */ #undef HAVE_UINT64_T /* Define to 1 if the system has the type `uint8_t'. */ #undef HAVE_UINT8_T /* Define to 1 if you have the header file. */ #define HAVE_UNISTD_H 1 /* Define to 1 if you have the `usleep' function. */ #define HAVE_USLEEP 1 /* Define to 1 if you have the header file. */ #define HAVE_WINDOWS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_WINSOCK2_H 1 /* Define to use X86 inlined assembly code */ #undef HAVE_X86 /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the version of this package. */ #undef PACKAGE_VERSION /* The size of a `unsigned long', as computed by sizeof. */ #define SIZEOF_UNSIGNED_LONG 4 /* The size of a `unsigned long long', as computed by sizeof. */ #define SIZEOF_UNSIGNED_LONG_LONG 8 /* Define to use GDOI. */ #undef SRTP_GDOI /* Define to compile for kernel contexts. */ #undef SRTP_KERNEL /* Define to compile for Linux kernel context. */ #undef SRTP_KERNEL_LINUX /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Write errors to this file */ #undef USE_ERR_REPORTING_FILE /* Define to use syslog logging. */ #undef USE_SYSLOG /* Define to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX). */ #undef WORDS_BIGENDIAN /* Define to empty if `const' does not conform to ANSI C. */ //#undef const /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ //#ifndef __cplusplus //#undef inline //#endif #define inline __inline /* Define to `unsigned' if does not define. */ //#undef size_t ================================================ FILE: deps/pjsip/third_party/srtp/config.hw ================================================ /* crypto/include/config.h. Generated by configure. */ /* config_in.h. Generated from configure.in by autoheader. */ /* Define if building for a CISC machine (e.g. Intel). */ #define CPU_CISC 1 /* Define if building for a RISC machine (assume slow byte access). */ /* #undef CPU_RISC */ /* Path to random device */ /* #define DEV_URANDOM "/dev/urandom" */ /* Define to compile in dynamic debugging system. */ #define ENABLE_DEBUGGING 1 /* Report errors to this file. */ /* #undef ERR_REPORTING_FILE */ /* Define to use logging to stdout. */ #define ERR_REPORTING_STDOUT 1 /* Define this to use ISMAcryp code. */ /* #undef GENERIC_AESICM */ /* Define to 1 if you have the header file. */ /* #undef HAVE_ARPA_INET_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_BYTESWAP_H */ /* Define to 1 if you have the `inet_aton' function. */ /* #undef HAVE_INET_ATON */ /* Define to 1 if the system has the type `int16_t'. */ #define HAVE_INT16_T 1 /* Define to 1 if the system has the type `int32_t'. */ #define HAVE_INT32_T 1 /* Define to 1 if the system has the type `int8_t'. */ #define HAVE_INT8_T 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_INTTYPES_H */ /* Define to 1 if you have the `socket' library (-lsocket). */ /* #undef HAVE_LIBSOCKET */ /* Define to 1 if you have the header file. */ /* #undef HAVE_MACHINE_TYPES_H */ /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_NETINET_IN_H */ /* Define to 1 if you have the `socket' function. */ /* #undef HAVE_SOCKET */ /* Define to 1 if you have the header file. */ /* #undef HAVE_STDINT_H */ /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_SYSLOG_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_INT_TYPES_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_SOCKET_H */ /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_UIO_H */ /* Define to 1 if the system has the type `uint16_t'. */ #define HAVE_UINT16_T 1 /* Define to 1 if the system has the type `uint32_t'. */ #define HAVE_UINT32_T 1 /* Define to 1 if the system has the type `uint64_t'. */ #define HAVE_UINT64_T 1 /* Define to 1 if the system has the type `uint8_t'. */ #define HAVE_UINT8_T 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_UNISTD_H */ /* Define to 1 if you have the `usleep' function. */ /* #undef HAVE_USLEEP */ /* Define to 1 if you have the header file. */ #define HAVE_WINDOWS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_WINSOCK2_H 1 /* Define to use X86 inlined assembly code */ /* #undef HAVE_X86 */ /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "" /* Define to the full name of this package. */ #define PACKAGE_NAME "" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "" /* Define to the version of this package. */ #define PACKAGE_VERSION "" /* The size of a `unsigned long', as computed by sizeof. */ #define SIZEOF_UNSIGNED_LONG 4 /* The size of a `unsigned long long', as computed by sizeof. */ #define SIZEOF_UNSIGNED_LONG_LONG 8 /* Define to use GDOI. */ /* #undef SRTP_GDOI */ /* Define to compile for kernel contexts. */ /* #undef SRTP_KERNEL */ /* Define to compile for Linux kernel context. */ /* #undef SRTP_KERNEL_LINUX */ /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Write errors to this file */ /* #undef USE_ERR_REPORTING_FILE */ /* Define to use syslog logging. */ /* #undef USE_SYSLOG */ /* Define to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX). */ /* #undef WORDS_BIGENDIAN */ /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ /* Define 'inline' to nothing, since the MSVC compiler doesn't support it. */ #define inline /* Define to `unsigned' if does not define. */ /* #undef size_t */ #if (_MSC_VER >= 1400) // VC8+ #ifndef _CRT_SECURE_NO_DEPRECATE #define _CRT_SECURE_NO_DEPRECATE #endif #ifndef _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE #endif #endif // VC8+ #ifndef uint32_t typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; typedef __int8 int8_t; typedef __int16 int16_t; typedef __int32 int32_t; typedef __int64 int64_t; #endif #ifdef _MSC_VER #pragma warning(disable:4311) #endif ================================================ FILE: deps/pjsip/third_party/srtp/config_in.h ================================================ /* config_in.h. Generated from configure.in by autoheader. */ /* Define if building for a CISC machine (e.g. Intel). */ #undef CPU_CISC /* Define if building for a RISC machine (assume slow byte access). */ #undef CPU_RISC /* Path to random device */ #undef DEV_URANDOM /* Define to compile in dynamic debugging system. */ #undef ENABLE_DEBUGGING /* Report errors to this file. */ #undef ERR_REPORTING_FILE /* Define to use logging to stdout. */ #undef ERR_REPORTING_STDOUT /* Define this to use ISMAcryp code. */ #undef GENERIC_AESICM /* Define to 1 if you have the header file. */ #undef HAVE_ARPA_INET_H /* Define to 1 if you have the header file. */ #undef HAVE_BYTESWAP_H /* Define to 1 if you have the `inet_aton' function. */ #undef HAVE_INET_ATON /* Define to 1 if the system has the type `int16_t'. */ #undef HAVE_INT16_T /* Define to 1 if the system has the type `int32_t'. */ #undef HAVE_INT32_T /* Define to 1 if the system has the type `int8_t'. */ #undef HAVE_INT8_T /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the `socket' library (-lsocket). */ #undef HAVE_LIBSOCKET /* Define to 1 if you have the header file. */ #undef HAVE_MACHINE_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_IN_H /* Define to 1 if you have the `socket' function. */ #undef HAVE_SOCKET /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the header file. */ #undef HAVE_SYSLOG_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_INT_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKET_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UIO_H /* Define to 1 if the system has the type `uint16_t'. */ #undef HAVE_UINT16_T /* Define to 1 if the system has the type `uint32_t'. */ #undef HAVE_UINT32_T /* Define to 1 if the system has the type `uint64_t'. */ #undef HAVE_UINT64_T /* Define to 1 if the system has the type `uint8_t'. */ #undef HAVE_UINT8_T /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `usleep' function. */ #undef HAVE_USLEEP /* Define to 1 if you have the header file. */ #undef HAVE_WINDOWS_H /* Define to 1 if you have the header file. */ #undef HAVE_WINSOCK2_H /* Define to use X86 inlined assembly code */ #undef HAVE_X86 /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the version of this package. */ #undef PACKAGE_VERSION /* The size of a `unsigned long', as computed by sizeof. */ #undef SIZEOF_UNSIGNED_LONG /* The size of a `unsigned long long', as computed by sizeof. */ #undef SIZEOF_UNSIGNED_LONG_LONG /* Define to use GDOI. */ #undef SRTP_GDOI /* Define to compile for kernel contexts. */ #undef SRTP_KERNEL /* Define to compile for Linux kernel context. */ #undef SRTP_KERNEL_LINUX /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Write errors to this file */ #undef USE_ERR_REPORTING_FILE /* Define to use syslog logging. */ #undef USE_SYSLOG /* Define to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX). */ #undef WORDS_BIGENDIAN /* Define to empty if `const' does not conform to ANSI C. */ #undef const /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline #endif /* Define to `unsigned' if does not define. */ #undef size_t ================================================ FILE: deps/pjsip/third_party/srtp/configure ================================================ #!/bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.59. # # Copyright (C) 2003 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi DUALCASE=1; export DUALCASE # for MKS sh # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # Work around bugs in pre-3.0 UWIN ksh. $as_unset ENV MAIL MAILPATH PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)$' \| \ . : '\(.\)' 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } /^X\/\(\/\/\)$/{ s//\1/; q; } /^X\/\(\/\).*/{ s//\1/; q; } s/.*/./; q'` # PATH needs CR, and LINENO needs CR and PATH. # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" || { # Find who we are. Look in the path if we contain no path at all # relative or not. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 { (exit 1); exit 1; }; } fi case $CONFIG_SHELL in '') as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for as_base in sh bash ksh sh5; do case $as_dir in /*) if ("$as_dir/$as_base" -c ' as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } CONFIG_SHELL=$as_dir/$as_base export CONFIG_SHELL exec "$CONFIG_SHELL" "$0" ${1+"$@"} fi;; esac done done ;; esac # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line before each line; the second 'sed' does the real # work. The second script uses 'N' to pair each line-number line # with the numbered line, and appends trailing '-' during # substitution so that $LINENO is not a special case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) sed '=' <$as_myself | sed ' N s,$,-, : loop s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, t loop s,-$,, s,^['$as_cr_digits']*\n,, ' >$as_me.lineno && chmod +x $as_me.lineno || { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensible to this). . ./$as_me.lineno # Exit status is that of the last command. exit } case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then # We could just check for DJGPP; but this test a) works b) is more generic # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). if test -f conf$$.exe; then # Don't use ln at all; we don't have any links as_ln_s='cp -p' else as_ln_s='ln -s' fi elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.file if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_executable_p="test -f" # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" # IFS # We need space, tab and new line, in precisely that order. as_nl=' ' IFS=" $as_nl" # CDPATH. $as_unset CDPATH # Name of the host. # hostname on some systems (SVR3.2, Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` exec 6>&1 # # Initializations. # ac_default_prefix=/usr/local ac_config_libobj_dir=. cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Maximum number of lines to put in a shell here document. # This variable seems obsolete. It should probably be removed, and # only ac_max_sed_lines should be used. : ${ac_max_here_lines=38} # Identity of this package. PACKAGE_NAME= PACKAGE_TARNAME= PACKAGE_VERSION= PACKAGE_STRING= PACKAGE_BUGREPORT= ac_unique_file="srtp" # Factoring default headers for most tests. ac_includes_default="\ #include #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_STAT_H # include #endif #if STDC_HEADERS # include # include #else # if HAVE_STDLIB_H # include # endif #endif #if HAVE_STRING_H # if !STDC_HEADERS && HAVE_MEMORY_H # include # endif # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_INTTYPES_H # include #else # if HAVE_STDINT_H # include # endif #endif #if HAVE_UNISTD_H # include #endif" ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS RANLIB ac_ct_RANLIB CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA RNG_OBJS CPP EGREP build build_cpu build_vendor build_os host host_cpu host_vendor host_os EXE GDOI_OBJS LIBOBJS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. ac_init_help= ac_init_version=false # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datadir='${prefix}/share' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' libdir='${exec_prefix}/lib' includedir='${prefix}/include' oldincludedir='/usr/include' infodir='${prefix}/info' mandir='${prefix}/man' ac_prev= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval "$ac_prev=\$ac_option" ac_prev= continue fi ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_option in -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad | --data | --dat | --da) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ | --da=*) datadir=$ac_optarg ;; -disable-* | --disable-*) ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` eval "enable_$ac_feature=no" ;; -enable-* | --enable-*) ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` case $ac_option in *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; *) ac_optarg=yes ;; esac eval "enable_$ac_feature='$ac_optarg'" ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst \ | --locals | --local | --loca | --loc | --lo) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* \ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package| sed 's/-/_/g'` case $ac_option in *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; *) ac_optarg=yes ;; esac eval "with_$ac_package='$ac_optarg'" ;; -without-* | --without-*) ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package | sed 's/-/_/g'` eval "with_$ac_package=no" ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) { echo "$as_me: error: unrecognized option: $ac_option Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 { (exit 1); exit 1; }; } ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` eval "$ac_envvar='$ac_optarg'" export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` { echo "$as_me: error: missing argument to $ac_option" >&2 { (exit 1); exit 1; }; } fi # Be sure to have absolute paths. for ac_var in exec_prefix prefix do eval ac_val=$`echo $ac_var` case $ac_val in [\\/$]* | ?:[\\/]* | NONE | '' ) ;; *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; };; esac done # Be sure to have absolute paths. for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ localstatedir libdir includedir oldincludedir infodir mandir do eval ac_val=$`echo $ac_var` case $ac_val in [\\/$]* | ?:[\\/]* ) ;; *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; };; esac done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then its parent. ac_confdir=`(dirname "$0") 2>/dev/null || $as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$0" : 'X\(//\)[^/]' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$0" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` srcdir=$ac_confdir if test ! -r $srcdir/$ac_unique_file; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r $srcdir/$ac_unique_file; then if test "$ac_srcdir_defaulted" = yes; then { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 { (exit 1); exit 1; }; } else { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 { (exit 1); exit 1; }; } fi fi (cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 { (exit 1); exit 1; }; } srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` ac_env_build_alias_set=${build_alias+set} ac_env_build_alias_value=$build_alias ac_cv_env_build_alias_set=${build_alias+set} ac_cv_env_build_alias_value=$build_alias ac_env_host_alias_set=${host_alias+set} ac_env_host_alias_value=$host_alias ac_cv_env_host_alias_set=${host_alias+set} ac_cv_env_host_alias_value=$host_alias ac_env_target_alias_set=${target_alias+set} ac_env_target_alias_value=$target_alias ac_cv_env_target_alias_set=${target_alias+set} ac_cv_env_target_alias_value=$target_alias ac_env_CC_set=${CC+set} ac_env_CC_value=$CC ac_cv_env_CC_set=${CC+set} ac_cv_env_CC_value=$CC ac_env_CFLAGS_set=${CFLAGS+set} ac_env_CFLAGS_value=$CFLAGS ac_cv_env_CFLAGS_set=${CFLAGS+set} ac_cv_env_CFLAGS_value=$CFLAGS ac_env_LDFLAGS_set=${LDFLAGS+set} ac_env_LDFLAGS_value=$LDFLAGS ac_cv_env_LDFLAGS_set=${LDFLAGS+set} ac_cv_env_LDFLAGS_value=$LDFLAGS ac_env_CPPFLAGS_set=${CPPFLAGS+set} ac_env_CPPFLAGS_value=$CPPFLAGS ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} ac_cv_env_CPPFLAGS_value=$CPPFLAGS ac_env_CPP_set=${CPP+set} ac_env_CPP_value=$CPP ac_cv_env_CPP_set=${CPP+set} ac_cv_env_CPP_value=$CPP # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures this package to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] _ACEOF cat <<_ACEOF Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --datadir=DIR read-only architecture-independent data [PREFIX/share] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --infodir=DIR info documentation [PREFIX/info] --mandir=DIR man documentation [PREFIX/man] _ACEOF cat <<\_ACEOF System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then cat <<\_ACEOF Optional Features: --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-kernel-linux build library to run in Linux kernel context --disable-debug do not compile in dynamic debugging system --enable-generic-aesicm compile in changes for ISMAcryp --enable-syslog use syslog for error reporting --disable-stdout don't use stdout for error reporting --enable-console use /dev/console for error reporting --enable-gdoi enable GDOI key management Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. _ACEOF fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. ac_popdir=`pwd` for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d $ac_dir || continue ac_builddir=. if test "$ac_dir" != .; then ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A "../" for each directory in $ac_dir_suffix. ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` else ac_dir_suffix= ac_top_builddir= fi case $srcdir in .) # No --srcdir option. We are building in place. ac_srcdir=. if test -z "$ac_top_builddir"; then ac_top_srcdir=. else ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` fi ;; [\\/]* | ?:[\\/]* ) # Absolute path. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ;; *) # Relative path. ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_builddir$srcdir ;; esac # Do not use `cd foo && pwd` to compute absolute paths, because # the directories may not exist. case `pwd` in .) ac_abs_builddir="$ac_dir";; *) case "$ac_dir" in .) ac_abs_builddir=`pwd`;; [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; *) ac_abs_builddir=`pwd`/"$ac_dir";; esac;; esac case $ac_abs_builddir in .) ac_abs_top_builddir=${ac_top_builddir}.;; *) case ${ac_top_builddir}. in .) ac_abs_top_builddir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; esac;; esac case $ac_abs_builddir in .) ac_abs_srcdir=$ac_srcdir;; *) case $ac_srcdir in .) ac_abs_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; esac;; esac case $ac_abs_builddir in .) ac_abs_top_srcdir=$ac_top_srcdir;; *) case $ac_top_srcdir in .) ac_abs_top_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; esac;; esac cd $ac_dir # Check for guested configure; otherwise get Cygnus style configure. if test -f $ac_srcdir/configure.gnu; then echo $SHELL $ac_srcdir/configure.gnu --help=recursive elif test -f $ac_srcdir/configure; then echo $SHELL $ac_srcdir/configure --help=recursive elif test -f $ac_srcdir/configure.ac || test -f $ac_srcdir/configure.in; then echo $ac_configure --help else echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi cd $ac_popdir done fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF Copyright (C) 2003 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit 0 fi exec 5>config.log cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by $as_me, which was generated by GNU Autoconf 2.59. Invocation command line was $ $0 $@ _ACEOF { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` hostinfo = `(hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. echo "PATH: $as_dir" done } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_sep= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; 2) ac_configure_args1="$ac_configure_args1 '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" # Get rid of the leading space. ac_sep=" " ;; esac done done $as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } $as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Be sure not to use single quotes in there, as some shells, # such as our DU 5.0 friend, will then `close' the trap. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo cat <<\_ASBOX ## ---------------- ## ## Cache variables. ## ## ---------------- ## _ASBOX echo # The following way of writing the cache mishandles newlines in values, { (set) 2>&1 | case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in *ac_space=\ *) sed -n \ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" ;; *) sed -n \ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" ;; esac; } echo cat <<\_ASBOX ## ----------------- ## ## Output variables. ## ## ----------------- ## _ASBOX echo for ac_var in $ac_subst_vars do eval ac_val=$`echo $ac_var` echo "$ac_var='"'"'$ac_val'"'"'" done | sort echo if test -n "$ac_subst_files"; then cat <<\_ASBOX ## ------------- ## ## Output files. ## ## ------------- ## _ASBOX echo for ac_var in $ac_subst_files do eval ac_val=$`echo $ac_var` echo "$ac_var='"'"'$ac_val'"'"'" done | sort echo fi if test -s confdefs.h; then cat <<\_ASBOX ## ----------- ## ## confdefs.h. ## ## ----------- ## _ASBOX echo sed "/^$/d" confdefs.h | sort echo fi test "$ac_signal" != 0 && echo "$as_me: caught signal $ac_signal" echo "$as_me: exit $exit_status" } >&5 rm -f core *.core && rm -rf conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -rf conftest* confdefs.h # AIX cpp loses on an empty file, so make sure it contains at least a newline. echo >confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer explicitly selected file to automatically selected ones. if test -z "$CONFIG_SITE"; then if test "x$prefix" != xNONE; then CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" else CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi fi for ac_site_file in $CONFIG_SITE; do if test -r "$ac_site_file"; then { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special # files actually), so we avoid doing that. if test -f "$cache_file"; then { echo "$as_me:$LINENO: loading cache $cache_file" >&5 echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . $cache_file;; *) . ./$cache_file;; esac fi else { echo "$as_me:$LINENO: creating cache $cache_file" >&5 echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in `(set) 2>&1 | sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val="\$ac_cv_env_${ac_var}_value" eval ac_new_val="\$ac_env_${ac_var}_value" case $ac_old_set,$ac_new_set in set,) { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 echo "$as_me: former value: $ac_old_val" >&2;} { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 echo "$as_me: current value: $ac_new_val" >&2;} ac_cache_corrupted=: fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 echo "$as_me: error: changes in the environment can compromise the build" >&2;} { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -z "$CFLAGS"; then CFLAGS="-Wall -O4 -fexpensive-optimizations -funroll-loops" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_RANLIB+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then echo "$as_me:$LINENO: result: $RANLIB" >&5 echo "${ECHO_T}$RANLIB" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":" fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 echo "${ECHO_T}$ac_ct_RANLIB" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi RANLIB=$ac_ct_RANLIB else RANLIB="$ac_cv_prog_RANLIB" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi CC=$ac_ct_CC else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi CC=$ac_ct_CC else CC="$ac_cv_prog_CC" fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$ac_ct_CC" && break done CC=$ac_ct_CC fi fi test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&5 echo "$as_me: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } # Provide some information about the compiler. echo "$as_me:$LINENO:" \ "checking for C compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 (eval $ac_compiler --version &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 (eval $ac_compiler -v &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 (eval $ac_compiler -V &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 (eval $ac_link_default) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # Find the output, starting from the most likely. This scheme is # not robust to junk in `.', hence go to wildcards (a.*) only as a last # resort. # Be careful to initialize this variable, since it used to be cached. # Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. ac_cv_exeext= # b.out is created by i960 compilers. for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; conftest.$ac_ext ) # This is the source file. ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` # FIXME: I believe we export ac_cv_exeext for Libtool, # but it would be cool to find out if it's true. Does anybody # maintain Libtool? --akim. export ac_cv_exeext break;; * ) break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: C compiler cannot create executables See \`config.log' for more details." >&5 echo "$as_me: error: C compiler cannot create executables See \`config.log' for more details." >&2;} { (exit 77); exit 77; }; } fi ac_exeext=$ac_cv_exeext echo "$as_me:$LINENO: result: $ac_file" >&5 echo "${ECHO_T}$ac_file" >&6 # Check the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. echo "$as_me:$LINENO: checking whether the C compiler works" >&5 echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 # FIXME: These cross compiler hacks should be removed for Autoconf 3.0 # If not cross compiling, check that we can run a simple program. if test "$cross_compiling" != yes; then if { ac_try='./$ac_file' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { echo "$as_me:$LINENO: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&5 echo "$as_me: error: cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi fi fi echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 rm -f a.out a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save # Check the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 echo "$as_me:$LINENO: result: $cross_compiling" >&5 echo "${ECHO_T}$cross_compiling" >&6 echo "$as_me:$LINENO: checking for suffix of executables" >&5 echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` export ac_cv_exeext break;; * ) break;; esac done else { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest$ac_cv_exeext echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 echo "${ECHO_T}$ac_cv_exeext" >&6 rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT echo "$as_me:$LINENO: checking for suffix of object files" >&5 echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 if test "${ac_cv_objext+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 echo "${ECHO_T}$ac_cv_objext" >&6 OBJEXT=$ac_cv_objext ac_objext=$OBJEXT echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 if test "${ac_cv_c_compiler_gnu+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_compiler_gnu=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_compiler_gnu=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 GCC=`test $ac_compiler_gnu = yes && echo yes` ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS CFLAGS="-g" echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 if test "${ac_cv_prog_cc_g+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_prog_cc_g=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 if test "${ac_cv_prog_cc_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_prog_cc_stdc=no ac_save_CC=$CC cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std1 is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std1. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF # Don't try gcc -ansi; that turns off useful extensions and # breaks some systems' header files. # AIX -qlanglvl=ansi # Ultrix and OSF/1 -std1 # HP-UX 10.20 and later -Ae # HP-UX older versions -Aa -D_HPUX_SOURCE # SVR4 -Xc -D__EXTENSIONS__ for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_stdc=$ac_arg break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext done rm -f conftest.$ac_ext conftest.$ac_objext CC=$ac_save_CC fi case "x$ac_cv_prog_cc_stdc" in x|xno) echo "$as_me:$LINENO: result: none needed" >&5 echo "${ECHO_T}none needed" >&6 ;; *) echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 CC="$CC $ac_cv_prog_cc_stdc" ;; esac # Some people use a C++ compiler to compile C. Since we use `exit', # in C++ we need to declare it. In case someone uses the same compiler # for both compiling C and C++ we need to have the C++ compiler decide # the declaration of exit, since it's the most demanding environment. cat >conftest.$ac_ext <<_ACEOF #ifndef __cplusplus choke me #endif _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then for ac_declaration in \ '' \ 'extern "C" void std::exit (int) throw (); using std::exit;' \ 'extern "C" void std::exit (int); using std::exit;' \ 'extern "C" void exit (int) throw ();' \ 'extern "C" void exit (int);' \ 'void exit (int);' do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration #include int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 continue fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done rm -f conftest* if test -n "$ac_declaration"; then echo '#ifdef __cplusplus' >>confdefs.h echo $ac_declaration >>confdefs.h echo '#endif' >>confdefs.h fi else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do if test -f $ac_dir/install-sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f $ac_dir/install.sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f $ac_dir/shtool; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} { (exit 1); exit 1; }; } fi ac_config_guess="$SHELL $ac_aux_dir/config.guess" ac_config_sub="$SHELL $ac_aux_dir/config.sub" ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 if test -z "$INSTALL"; then if test "${ac_cv_path_install+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in ./ | .// | /cC/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi done done ;; esac done fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. We don't cache a # path for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the path is relative. INSTALL=$ac_install_sh fi fi echo "$as_me:$LINENO: result: $INSTALL" >&5 echo "${ECHO_T}$INSTALL" >&6 # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' # Check whether --enable-kernel-linux or --disable-kernel-linux was given. if test "${enable_kernel_linux+set}" = set; then enableval="$enable_kernel_linux" else enable_kernel_linux=no fi; echo "$as_me:$LINENO: checking whether to build for Linux kernel context" >&5 echo $ECHO_N "checking whether to build for Linux kernel context... $ECHO_C" >&6 if test "$enable_kernel_linux" = "yes"; then cat >>confdefs.h <<\_ACEOF #define SRTP_KERNEL 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define SRTP_KERNEL_LINUX 1 _ACEOF fi echo "$as_me:$LINENO: result: $enable_kernel_linux" >&5 echo "${ECHO_T}$enable_kernel_linux" >&6 if test "$cross_compiling" != yes; then echo "$as_me:$LINENO: checking for /dev/urandom" >&5 echo $ECHO_N "checking for /dev/urandom... $ECHO_C" >&6 if test "${ac_cv_file__dev_urandom+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else test "$cross_compiling" = yes && { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5 echo "$as_me: error: cannot check for file existence when cross compiling" >&2;} { (exit 1); exit 1; }; } if test -r "/dev/urandom"; then ac_cv_file__dev_urandom=yes else ac_cv_file__dev_urandom=no fi fi echo "$as_me:$LINENO: result: $ac_cv_file__dev_urandom" >&5 echo "${ECHO_T}$ac_cv_file__dev_urandom" >&6 if test $ac_cv_file__dev_urandom = yes; then DEV_URANDOM=/dev/urandom else echo "$as_me:$LINENO: checking for /dev/random" >&5 echo $ECHO_N "checking for /dev/random... $ECHO_C" >&6 if test "${ac_cv_file__dev_random+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else test "$cross_compiling" = yes && { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5 echo "$as_me: error: cannot check for file existence when cross compiling" >&2;} { (exit 1); exit 1; }; } if test -r "/dev/random"; then ac_cv_file__dev_random=yes else ac_cv_file__dev_random=no fi fi echo "$as_me:$LINENO: result: $ac_cv_file__dev_random" >&5 echo "${ECHO_T}$ac_cv_file__dev_random" >&6 if test $ac_cv_file__dev_random = yes; then DEV_URANDOM=/dev/random fi fi fi echo "$as_me:$LINENO: checking which random device to use" >&5 echo $ECHO_N "checking which random device to use... $ECHO_C" >&6 if test "$enable_kernel_linux" = "yes"; then RNG_OBJS=rand_linux_kernel.o echo "$as_me:$LINENO: result: Linux kernel builtin" >&5 echo "${ECHO_T}Linux kernel builtin" >&6 else RNG_OBJS=rand_source.o if test -n "$DEV_URANDOM"; then cat >>confdefs.h <<_ACEOF #define DEV_URANDOM "$DEV_URANDOM" _ACEOF echo "$as_me:$LINENO: result: $DEV_URANDOM" >&5 echo "${ECHO_T}$DEV_URANDOM" >&6 else echo "$as_me:$LINENO: result: standard rand() function..." >&5 echo "${ECHO_T}standard rand() function..." >&6 fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if test "${ac_cv_prog_CPP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether non-existent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi echo "$as_me:$LINENO: result: $CPP" >&5 echo "${ECHO_T}$CPP" >&6 ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether non-existent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&5 echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu echo "$as_me:$LINENO: checking for egrep" >&5 echo $ECHO_N "checking for egrep... $ECHO_C" >&6 if test "${ac_cv_prog_egrep+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if echo a | (grep -E '(a|b)') >/dev/null 2>&1 then ac_cv_prog_egrep='grep -E' else ac_cv_prog_egrep='egrep' fi fi echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 echo "${ECHO_T}$ac_cv_prog_egrep" >&6 EGREP=$ac_cv_prog_egrep echo "$as_me:$LINENO: checking for ANSI C header files" >&5 echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 if test "${ac_cv_header_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_header_stdc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_header_stdc=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_header_stdc=no fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi fi echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 echo "${ECHO_T}$ac_cv_header_stdc" >&6 if test $ac_cv_header_stdc = yes; then cat >>confdefs.h <<\_ACEOF #define STDC_HEADERS 1 _ACEOF fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_Header=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_Header=no" fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in stdlib.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in unistd.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in byteswap.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in stdint.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in sys/uio.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in inttypes.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in sys/types.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in machine/types.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in sys/int_types.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in sys/socket.h netinet/in.h arpa/inet.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in windows.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF for ac_header in winsock2.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done fi done for ac_header in syslog.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 else # Is the header compilable? echo "$as_me:$LINENO: checking $ac_header usability" >&5 echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default #include <$ac_header> _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_header_compiler=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_compiler=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 echo "${ECHO_T}$ac_header_compiler" >&6 # Is the header present? echo "$as_me:$LINENO: checking $ac_header presence" >&5 echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include <$ac_header> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_c_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_c_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then ac_header_preproc=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_header_preproc=no fi rm -f conftest.err conftest.$ac_ext echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 echo "${ECHO_T}$ac_header_preproc" >&6 # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in yes:no: ) { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} ac_header_preproc=yes ;; no:yes:* ) { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} ( cat <<\_ASBOX ## ------------------------------------------ ## ## Report this to the AC_PACKAGE_NAME lists. ## ## ------------------------------------------ ## _ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 if eval "test \"\${$as_ac_Header+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else eval "$as_ac_Header=\$ac_header_preproc" fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 fi if test `eval echo '${'$as_ac_Header'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done echo "$as_me:$LINENO: checking for int8_t" >&5 echo $ECHO_N "checking for int8_t... $ECHO_C" >&6 if test "${ac_cv_type_int8_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((int8_t *) 0) return 0; if (sizeof (int8_t)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_int8_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_int8_t=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_int8_t" >&5 echo "${ECHO_T}$ac_cv_type_int8_t" >&6 if test $ac_cv_type_int8_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_INT8_T 1 _ACEOF fi echo "$as_me:$LINENO: checking for uint8_t" >&5 echo $ECHO_N "checking for uint8_t... $ECHO_C" >&6 if test "${ac_cv_type_uint8_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((uint8_t *) 0) return 0; if (sizeof (uint8_t)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_uint8_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_uint8_t=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_uint8_t" >&5 echo "${ECHO_T}$ac_cv_type_uint8_t" >&6 if test $ac_cv_type_uint8_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_UINT8_T 1 _ACEOF fi echo "$as_me:$LINENO: checking for int16_t" >&5 echo $ECHO_N "checking for int16_t... $ECHO_C" >&6 if test "${ac_cv_type_int16_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((int16_t *) 0) return 0; if (sizeof (int16_t)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_int16_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_int16_t=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_int16_t" >&5 echo "${ECHO_T}$ac_cv_type_int16_t" >&6 if test $ac_cv_type_int16_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_INT16_T 1 _ACEOF fi echo "$as_me:$LINENO: checking for uint16_t" >&5 echo $ECHO_N "checking for uint16_t... $ECHO_C" >&6 if test "${ac_cv_type_uint16_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((uint16_t *) 0) return 0; if (sizeof (uint16_t)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_uint16_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_uint16_t=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_uint16_t" >&5 echo "${ECHO_T}$ac_cv_type_uint16_t" >&6 if test $ac_cv_type_uint16_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_UINT16_T 1 _ACEOF fi echo "$as_me:$LINENO: checking for int32_t" >&5 echo $ECHO_N "checking for int32_t... $ECHO_C" >&6 if test "${ac_cv_type_int32_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((int32_t *) 0) return 0; if (sizeof (int32_t)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_int32_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_int32_t=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_int32_t" >&5 echo "${ECHO_T}$ac_cv_type_int32_t" >&6 if test $ac_cv_type_int32_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_INT32_T 1 _ACEOF fi echo "$as_me:$LINENO: checking for uint32_t" >&5 echo $ECHO_N "checking for uint32_t... $ECHO_C" >&6 if test "${ac_cv_type_uint32_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((uint32_t *) 0) return 0; if (sizeof (uint32_t)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_uint32_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_uint32_t=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_uint32_t" >&5 echo "${ECHO_T}$ac_cv_type_uint32_t" >&6 if test $ac_cv_type_uint32_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_UINT32_T 1 _ACEOF fi echo "$as_me:$LINENO: checking for uint64_t" >&5 echo $ECHO_N "checking for uint64_t... $ECHO_C" >&6 if test "${ac_cv_type_uint64_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((uint64_t *) 0) return 0; if (sizeof (uint64_t)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_uint64_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_uint64_t=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_uint64_t" >&5 echo "${ECHO_T}$ac_cv_type_uint64_t" >&6 if test $ac_cv_type_uint64_t = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_UINT64_T 1 _ACEOF fi echo "$as_me:$LINENO: checking for unsigned long" >&5 echo $ECHO_N "checking for unsigned long... $ECHO_C" >&6 if test "${ac_cv_type_unsigned_long+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((unsigned long *) 0) return 0; if (sizeof (unsigned long)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_unsigned_long=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_unsigned_long=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_unsigned_long" >&5 echo "${ECHO_T}$ac_cv_type_unsigned_long" >&6 echo "$as_me:$LINENO: checking size of unsigned long" >&5 echo $ECHO_N "checking size of unsigned long... $ECHO_C" >&6 if test "${ac_cv_sizeof_unsigned_long+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$ac_cv_type_unsigned_long" = yes; then # The cast to unsigned long works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. if test "$cross_compiling" = yes; then # Depending upon the size, compute the lo and hi bounds. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long))) >= 0)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_lo=0 ac_mid=0 while :; do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long))) <= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_hi=$ac_mid; break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_lo=`expr $ac_mid + 1` if test $ac_lo -le $ac_mid; then ac_lo= ac_hi= break fi ac_mid=`expr 2 '*' $ac_mid + 1` fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long))) < 0)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_hi=-1 ac_mid=-1 while :; do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long))) >= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_lo=$ac_mid; break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_hi=`expr '(' $ac_mid ')' - 1` if test $ac_mid -le $ac_hi; then ac_lo= ac_hi= break fi ac_mid=`expr 2 '*' $ac_mid` fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_lo= ac_hi= fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext # Binary search between lo and hi bounds. while test "x$ac_lo" != "x$ac_hi"; do ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long))) <= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_hi=$ac_mid else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_lo=`expr '(' $ac_mid ')' + 1` fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done case $ac_lo in ?*) ac_cv_sizeof_unsigned_long=$ac_lo;; '') { { echo "$as_me:$LINENO: error: cannot compute sizeof (unsigned long), 77 See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute sizeof (unsigned long), 77 See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } ;; esac else if test "$cross_compiling" = yes; then { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling See \`config.log' for more details." >&5 echo "$as_me: error: cannot run test program while cross compiling See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default long longval () { return (long) (sizeof (unsigned long)); } unsigned long ulongval () { return (long) (sizeof (unsigned long)); } #include #include int main () { FILE *f = fopen ("conftest.val", "w"); if (! f) exit (1); if (((long) (sizeof (unsigned long))) < 0) { long i = longval (); if (i != ((long) (sizeof (unsigned long)))) exit (1); fprintf (f, "%ld\n", i); } else { unsigned long i = ulongval (); if (i != ((long) (sizeof (unsigned long)))) exit (1); fprintf (f, "%lu\n", i); } exit (ferror (f) || fclose (f) != 0); ; return 0; } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_sizeof_unsigned_long=`cat conftest.val` else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) { { echo "$as_me:$LINENO: error: cannot compute sizeof (unsigned long), 77 See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute sizeof (unsigned long), 77 See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi rm -f conftest.val else ac_cv_sizeof_unsigned_long=0 fi fi echo "$as_me:$LINENO: result: $ac_cv_sizeof_unsigned_long" >&5 echo "${ECHO_T}$ac_cv_sizeof_unsigned_long" >&6 cat >>confdefs.h <<_ACEOF #define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long _ACEOF echo "$as_me:$LINENO: checking for unsigned long long" >&5 echo $ECHO_N "checking for unsigned long long... $ECHO_C" >&6 if test "${ac_cv_type_unsigned_long_long+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((unsigned long long *) 0) return 0; if (sizeof (unsigned long long)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_unsigned_long_long=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_unsigned_long_long=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_unsigned_long_long" >&5 echo "${ECHO_T}$ac_cv_type_unsigned_long_long" >&6 echo "$as_me:$LINENO: checking size of unsigned long long" >&5 echo $ECHO_N "checking size of unsigned long long... $ECHO_C" >&6 if test "${ac_cv_sizeof_unsigned_long_long+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test "$ac_cv_type_unsigned_long_long" = yes; then # The cast to unsigned long works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. if test "$cross_compiling" = yes; then # Depending upon the size, compute the lo and hi bounds. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long long))) >= 0)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_lo=0 ac_mid=0 while :; do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long long))) <= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_hi=$ac_mid; break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_lo=`expr $ac_mid + 1` if test $ac_lo -le $ac_mid; then ac_lo= ac_hi= break fi ac_mid=`expr 2 '*' $ac_mid + 1` fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long long))) < 0)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_hi=-1 ac_mid=-1 while :; do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long long))) >= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_lo=$ac_mid; break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_hi=`expr '(' $ac_mid ')' - 1` if test $ac_mid -le $ac_hi; then ac_lo= ac_hi= break fi ac_mid=`expr 2 '*' $ac_mid` fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_lo= ac_hi= fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext # Binary search between lo and hi bounds. while test "x$ac_lo" != "x$ac_hi"; do ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !(((long) (sizeof (unsigned long long))) <= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_hi=$ac_mid else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_lo=`expr '(' $ac_mid ')' + 1` fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done case $ac_lo in ?*) ac_cv_sizeof_unsigned_long_long=$ac_lo;; '') { { echo "$as_me:$LINENO: error: cannot compute sizeof (unsigned long long), 77 See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute sizeof (unsigned long long), 77 See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } ;; esac else if test "$cross_compiling" = yes; then { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling See \`config.log' for more details." >&5 echo "$as_me: error: cannot run test program while cross compiling See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default long longval () { return (long) (sizeof (unsigned long long)); } unsigned long ulongval () { return (long) (sizeof (unsigned long long)); } #include #include int main () { FILE *f = fopen ("conftest.val", "w"); if (! f) exit (1); if (((long) (sizeof (unsigned long long))) < 0) { long i = longval (); if (i != ((long) (sizeof (unsigned long long)))) exit (1); fprintf (f, "%ld\n", i); } else { unsigned long i = ulongval (); if (i != ((long) (sizeof (unsigned long long)))) exit (1); fprintf (f, "%lu\n", i); } exit (ferror (f) || fclose (f) != 0); ; return 0; } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_sizeof_unsigned_long_long=`cat conftest.val` else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) { { echo "$as_me:$LINENO: error: cannot compute sizeof (unsigned long long), 77 See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute sizeof (unsigned long long), 77 See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi rm -f conftest.val else ac_cv_sizeof_unsigned_long_long=0 fi fi echo "$as_me:$LINENO: result: $ac_cv_sizeof_unsigned_long_long" >&5 echo "${ECHO_T}$ac_cv_sizeof_unsigned_long_long" >&6 cat >>confdefs.h <<_ACEOF #define SIZEOF_UNSIGNED_LONG_LONG $ac_cv_sizeof_unsigned_long_long _ACEOF echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 if test "${ac_cv_c_const+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { /* FIXME: Include the comments suggested by Paul. */ #ifndef __cplusplus /* Ultrix mips cc rejects this. */ typedef int charset[2]; const charset x; /* SunOS 4.1.1 cc rejects this. */ char const *const *ccp; char **p; /* NEC SVR4.0.2 mips cc rejects this. */ struct point {int x, y;}; static struct point const zero = {0,0}; /* AIX XL C 1.02.0.0 rejects this. It does not let you subtract one const X* pointer from another in an arm of an if-expression whose if-part is not a constant expression */ const char *g = "string"; ccp = &g + (g ? g-g : 0); /* HPUX 7.0 cc rejects these. */ ++ccp; p = (char**) ccp; ccp = (char const *const *) p; { /* SCO 3.2v4 cc rejects this. */ char *t; char const *s = 0 ? (char *) 0 : (char const *) 0; *t++ = 0; } { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ int x[] = {25, 17}; const int *foo = &x[0]; ++foo; } { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ typedef const int *iptr; iptr p = 0; ++p; } { /* AIX XL C 1.02.0.0 rejects this saying "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ struct s { int j; const int *ap[3]; }; struct s *b; b->j = 5; } { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ const int foo = 10; } #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_c_const=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_c_const=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 echo "${ECHO_T}$ac_cv_c_const" >&6 if test $ac_cv_c_const = no; then cat >>confdefs.h <<\_ACEOF #define const _ACEOF fi echo "$as_me:$LINENO: checking for inline" >&5 echo $ECHO_N "checking for inline... $ECHO_C" >&6 if test "${ac_cv_c_inline+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; static $ac_kw foo_t static_foo () {return 0; } $ac_kw foo_t foo () {return 0; } #endif _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_c_inline=$ac_kw; break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done fi echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5 echo "${ECHO_T}$ac_cv_c_inline" >&6 case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac echo "$as_me:$LINENO: checking for size_t" >&5 echo $ECHO_N "checking for size_t... $ECHO_C" >&6 if test "${ac_cv_type_size_t+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_includes_default int main () { if ((size_t *) 0) return 0; if (sizeof (size_t)) return 0; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_type_size_t=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_type_size_t=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 echo "${ECHO_T}$ac_cv_type_size_t" >&6 if test $ac_cv_type_size_t = yes; then : else cat >>confdefs.h <<_ACEOF #define size_t unsigned _ACEOF fi for ac_func in socket inet_aton usleep do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 if eval "test \"\${$as_ac_var+set}\" = set"; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define $ac_func to an innocuous variant, in case declares $ac_func. For example, HP-UX 11i declares gettimeofday. */ #define $ac_func innocuous_$ac_func /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $ac_func (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $ac_func /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else char (*f) () = $ac_func; #endif #ifdef __cplusplus } #endif int main () { return f != $ac_func; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then eval "$as_ac_var=yes" else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 eval "$as_ac_var=no" fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 if test `eval echo '${'$as_ac_var'}'` = yes; then cat >>confdefs.h <<_ACEOF #define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done if test "x$ac_cv_func_socket" = "xno"; then echo "$as_me:$LINENO: checking for socket in -lsocket" >&5 echo $ECHO_N "checking for socket in -lsocket... $ECHO_C" >&6 if test "${ac_cv_lib_socket_socket+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char socket (); int main () { socket (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_socket_socket=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_socket_socket=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_socket_socket" >&5 echo "${ECHO_T}$ac_cv_lib_socket_socket" >&6 if test $ac_cv_lib_socket_socket = yes; then cat >>confdefs.h <<_ACEOF #define HAVE_LIBSOCKET 1 _ACEOF LIBS="-lsocket $LIBS" fi echo "$as_me:$LINENO: checking for socket in -lwsock32" >&5 echo $ECHO_N "checking for socket in -lwsock32... $ECHO_C" >&6 SAVELIBS="$LIBS" LIBS="$LIBS -lwsock32" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { socket(0, 0, 0); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_socket=yes echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 LIBS="$SAVELIBS" echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5 echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6 if test "${ac_cv_c_bigendian+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # See if sys/param.h defines the BYTE_ORDER macro. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { #if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN bogus endian macros #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then # It does; now see whether it defined to BIG_ENDIAN or not. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_c_bigendian=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_c_bigendian=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # It does not; compile a test program. if test "$cross_compiling" = yes; then # try to guess the endianness by grepping values into an object file ac_cv_c_bigendian=unknown cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; } short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; } int main () { _ascii (); _ebcdic (); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long l; char c[sizeof (long)]; } u; u.l = 1; exit (u.c[sizeof (long) - 1] == 1); } _ACEOF rm -f conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='./conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_c_bigendian=no else echo "$as_me: program exited with status $ac_status" >&5 echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_c_bigendian=yes fi rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 echo "${ECHO_T}$ac_cv_c_bigendian" >&6 case $ac_cv_c_bigendian in yes) cat >>confdefs.h <<\_ACEOF #define WORDS_BIGENDIAN 1 _ACEOF ;; no) ;; *) { { echo "$as_me:$LINENO: error: unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" >&5 echo "$as_me: error: unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" >&2;} { (exit 1); exit 1; }; } ;; esac # Make sure we can run config.sub. $ac_config_sub sun4 >/dev/null 2>&1 || { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5 echo "$as_me: error: cannot run $ac_config_sub" >&2;} { (exit 1); exit 1; }; } echo "$as_me:$LINENO: checking build system type" >&5 echo $ECHO_N "checking build system type... $ECHO_C" >&6 if test "${ac_cv_build+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_build_alias=$build_alias test -z "$ac_cv_build_alias" && ac_cv_build_alias=`$ac_config_guess` test -z "$ac_cv_build_alias" && { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 echo "$as_me: error: cannot guess build type; you must specify one" >&2;} { (exit 1); exit 1; }; } ac_cv_build=`$ac_config_sub $ac_cv_build_alias` || { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5 echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;} { (exit 1); exit 1; }; } fi echo "$as_me:$LINENO: result: $ac_cv_build" >&5 echo "${ECHO_T}$ac_cv_build" >&6 build=$ac_cv_build build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` echo "$as_me:$LINENO: checking host system type" >&5 echo $ECHO_N "checking host system type... $ECHO_C" >&6 if test "${ac_cv_host+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_host_alias=$host_alias test -z "$ac_cv_host_alias" && ac_cv_host_alias=$ac_cv_build_alias ac_cv_host=`$ac_config_sub $ac_cv_host_alias` || { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5 echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;} { (exit 1); exit 1; }; } fi echo "$as_me:$LINENO: result: $ac_cv_host" >&5 echo "${ECHO_T}$ac_cv_host" >&6 host=$ac_cv_host host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` case $host_cpu in i*86 ) cat >>confdefs.h <<\_ACEOF #define CPU_CISC 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define HAVE_X86 1 _ACEOF ;; * ) # CPU_RISC is only supported for big endian machines. if test "$ac_cv_c_bigendian" = "yes"; then cat >>confdefs.h <<\_ACEOF #define CPU_RISC 1 _ACEOF else cat >>confdefs.h <<\_ACEOF #define CPU_CISC 1 _ACEOF fi ;; esac case $host_os in *cygwin*|*mingw* ) EXE=.exe;; * ) EXE="";; esac # define executable suffix; this is needed for `make clean' echo "$as_me:$LINENO: checking whether to compile in debugging" >&5 echo $ECHO_N "checking whether to compile in debugging... $ECHO_C" >&6 # Check whether --enable-debug or --disable-debug was given. if test "${enable_debug+set}" = set; then enableval="$enable_debug" else enable_debug=yes fi; if test "$enable_debug" = "yes"; then cat >>confdefs.h <<\_ACEOF #define ENABLE_DEBUGGING 1 _ACEOF fi echo "$as_me:$LINENO: result: $enable_debug" >&5 echo "${ECHO_T}$enable_debug" >&6 echo "$as_me:$LINENO: checking whether to use ISMAcryp code" >&5 echo $ECHO_N "checking whether to use ISMAcryp code... $ECHO_C" >&6 # Check whether --enable-generic-aesicm or --disable-generic-aesicm was given. if test "${enable_generic_aesicm+set}" = set; then enableval="$enable_generic_aesicm" else enable_generic_aesicm=no fi; if test "$enable_generic_aesicm" = "yes"; then cat >>confdefs.h <<\_ACEOF #define GENERIC_AESICM 1 _ACEOF fi echo "$as_me:$LINENO: result: $enable_generic_aesicm" >&5 echo "${ECHO_T}$enable_generic_aesicm" >&6 echo "$as_me:$LINENO: checking whether to use syslog for error reporting" >&5 echo $ECHO_N "checking whether to use syslog for error reporting... $ECHO_C" >&6 # Check whether --enable-syslog or --disable-syslog was given. if test "${enable_syslog+set}" = set; then enableval="$enable_syslog" else enable_syslog=no fi; if test "$enable_syslog" = "yes"; then cat >>confdefs.h <<\_ACEOF #define USE_SYSLOG 1 _ACEOF fi echo "$as_me:$LINENO: result: $enable_syslog" >&5 echo "${ECHO_T}$enable_syslog" >&6 echo "$as_me:$LINENO: checking whether to use stdout for error reporting" >&5 echo $ECHO_N "checking whether to use stdout for error reporting... $ECHO_C" >&6 # Check whether --enable-stdout or --disable-stdout was given. if test "${enable_stdout+set}" = set; then enableval="$enable_stdout" else enable_stdout=yes fi; if test "$enable_stdout" = "yes"; then cat >>confdefs.h <<\_ACEOF #define ERR_REPORTING_STDOUT 1 _ACEOF fi echo "$as_me:$LINENO: result: $enable_stdout" >&5 echo "${ECHO_T}$enable_stdout" >&6 echo "$as_me:$LINENO: checking whether to use /dev/console for error reporting" >&5 echo $ECHO_N "checking whether to use /dev/console for error reporting... $ECHO_C" >&6 # Check whether --enable-console or --disable-console was given. if test "${enable_console+set}" = set; then enableval="$enable_console" else enable_console=no fi; if test "$enable_console" = "yes"; then cat >>confdefs.h <<\_ACEOF #define USE_ERR_REPORTING_FILE 1 _ACEOF cat >>confdefs.h <<\_ACEOF #define ERR_REPORTING_FILE "/dev/console" _ACEOF fi echo "$as_me:$LINENO: result: $enable_console" >&5 echo "${ECHO_T}$enable_console" >&6 echo "$as_me:$LINENO: checking whether to use GDOI key management" >&5 echo $ECHO_N "checking whether to use GDOI key management... $ECHO_C" >&6 # Check whether --enable-gdoi or --disable-gdoi was given. if test "${enable_gdoi+set}" = set; then enableval="$enable_gdoi" else enable_gdoi=no fi; if test "$enable_gdoi" = "yes"; then cat >>confdefs.h <<\_ACEOF #define SRTP_GDOI 1 _ACEOF GDOI_OBJS=gdoi/srtp+gdoi.o fi echo "$as_me:$LINENO: result: $enable_gdoi" >&5 echo "${ECHO_T}$enable_gdoi" >&6 ac_config_headers="$ac_config_headers crypto/include/config.h:config_in.h" ac_config_files="$ac_config_files Makefile crypto/Makefile doc/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, don't put newlines in cache variables' values. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. { (set) 2>&1 | case `(ac_space=' '; set | grep ac_space) 2>&1` in *ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote # substitution turns \\\\ into \\, and sed turns \\ into \). sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n \ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" ;; esac; } | sed ' t clear : clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ : end' >>confcache if diff $cache_file confcache >/dev/null 2>&1; then :; else if test -w $cache_file; then test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" cat confcache >$cache_file else echo "not updating unwritable cache $cache_file" fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # VPATH may cause trouble with some makes, so we remove $(srcdir), # ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=/{ s/:*\$(srcdir):*/:/; s/:*\${srcdir}:*/:/; s/:*@srcdir@:*/:/; s/^\([^=]*=[ ]*\):*/\1/; s/:*$//; s/^[^=]*=[ ]*$//; }' fi DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_i=`echo "$ac_i" | sed 's/\$U\././;s/\.o$//;s/\.obj$//'` # 2. Add them. ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : ${CONFIG_STATUS=./config.status} ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 echo "$as_me: creating $CONFIG_STATUS" >&6;} cat >$CONFIG_STATUS <<_ACEOF #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi DUALCASE=1; export DUALCASE # for MKS sh # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # Work around bugs in pre-3.0 UWIN ksh. $as_unset ENV MAIL MAILPATH PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)$' \| \ . : '\(.\)' 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } /^X\/\(\/\/\)$/{ s//\1/; q; } /^X\/\(\/\).*/{ s//\1/; q; } s/.*/./; q'` # PATH needs CR, and LINENO needs CR and PATH. # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" || { # Find who we are. Look in the path if we contain no path at all # relative or not. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} { (exit 1); exit 1; }; } fi case $CONFIG_SHELL in '') as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for as_base in sh bash ksh sh5; do case $as_dir in /*) if ("$as_dir/$as_base" -c ' as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } CONFIG_SHELL=$as_dir/$as_base export CONFIG_SHELL exec "$CONFIG_SHELL" "$0" ${1+"$@"} fi;; esac done done ;; esac # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line before each line; the second 'sed' does the real # work. The second script uses 'N' to pair each line-number line # with the numbered line, and appends trailing '-' during # substitution so that $LINENO is not a special case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) sed '=' <$as_myself | sed ' N s,$,-, : loop s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, t loop s,-$,, s,^['$as_cr_digits']*\n,, ' >$as_me.lineno && chmod +x $as_me.lineno || { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensible to this). . ./$as_me.lineno # Exit status is that of the last command. exit } case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then # We could just check for DJGPP; but this test a) works b) is more generic # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). if test -f conf$$.exe; then # Don't use ln at all; we don't have any links as_ln_s='cp -p' else as_ln_s='ln -s' fi elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.file if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_executable_p="test -f" # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" # IFS # We need space, tab and new line, in precisely that order. as_nl=' ' IFS=" $as_nl" # CDPATH. $as_unset CDPATH exec 6>&1 # Open the log real soon, to keep \$[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. Logging --version etc. is OK. exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX } >&5 cat >&5 <<_CSEOF This file was extended by $as_me, which was generated by GNU Autoconf 2.59. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ _CSEOF echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 echo >&5 _ACEOF # Files that config.status was made for. if test -n "$ac_config_files"; then echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS fi if test -n "$ac_config_headers"; then echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS fi if test -n "$ac_config_links"; then echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS fi if test -n "$ac_config_commands"; then echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS fi cat >>$CONFIG_STATUS <<\_ACEOF ac_cs_usage="\ \`$as_me' instantiates files from templates according to the current configuration. Usage: $0 [OPTIONS] [FILE]... -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ config.status configured by $0, generated by GNU Autoconf 2.59, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2003 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." srcdir=$srcdir INSTALL="$INSTALL" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # If no file are specified by the user, then we need to provide default # value. By we need to know if files were specified by the user. ac_need_defaults=: while test $# != 0 do case $1 in --*=*) ac_option=`expr "x$1" : 'x\([^=]*\)='` ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` ac_shift=: ;; -*) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; *) # This is not an option, so the user has probably given explicit # arguments. ac_option=$1 ac_need_defaults=false;; esac case $ac_option in # Handling of the options. _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --vers* | -V ) echo "$ac_cs_version"; exit 0 ;; --he | --h) # Conflict between --help and --header { { echo "$as_me:$LINENO: error: ambiguous option: $1 Try \`$0 --help' for more information." >&5 echo "$as_me: error: ambiguous option: $1 Try \`$0 --help' for more information." >&2;} { (exit 1); exit 1; }; };; --help | --hel | -h ) echo "$ac_cs_usage"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift CONFIG_FILES="$CONFIG_FILES $ac_optarg" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" ac_need_defaults=false;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 Try \`$0 --help' for more information." >&5 echo "$as_me: error: unrecognized option: $1 Try \`$0 --help' for more information." >&2;} { (exit 1); exit 1; }; } ;; *) ac_config_targets="$ac_config_targets $1" ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF if \$ac_cs_recheck; then echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF for ac_config_target in $ac_config_targets do case "$ac_config_target" in # Handling of arguments. "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; "crypto/Makefile" ) CONFIG_FILES="$CONFIG_FILES crypto/Makefile" ;; "doc/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "crypto/include/config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS crypto/include/config.h:config_in.h" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} { (exit 1); exit 1; }; };; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason to put it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Create a temporary directory, and hook for its removal unless debugging. $debug || { trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 trap '{ (exit 1); exit 1; }' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" } || { tmp=./confstat$$-$RANDOM (umask 077 && mkdir $tmp) } || { echo "$me: cannot create a temporary directory in ." >&2 { (exit 1); exit 1; } } _ACEOF cat >>$CONFIG_STATUS <<_ACEOF # # CONFIG_FILES section. # # No need to generate the scripts if there are no CONFIG_FILES. # This happens for instance when ./config.status config.h if test -n "\$CONFIG_FILES"; then # Protect against being on the right side of a sed subst in config.status. sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF s,@SHELL@,$SHELL,;t t s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t s,@exec_prefix@,$exec_prefix,;t t s,@prefix@,$prefix,;t t s,@program_transform_name@,$program_transform_name,;t t s,@bindir@,$bindir,;t t s,@sbindir@,$sbindir,;t t s,@libexecdir@,$libexecdir,;t t s,@datadir@,$datadir,;t t s,@sysconfdir@,$sysconfdir,;t t s,@sharedstatedir@,$sharedstatedir,;t t s,@localstatedir@,$localstatedir,;t t s,@libdir@,$libdir,;t t s,@includedir@,$includedir,;t t s,@oldincludedir@,$oldincludedir,;t t s,@infodir@,$infodir,;t t s,@mandir@,$mandir,;t t s,@build_alias@,$build_alias,;t t s,@host_alias@,$host_alias,;t t s,@target_alias@,$target_alias,;t t s,@DEFS@,$DEFS,;t t s,@ECHO_C@,$ECHO_C,;t t s,@ECHO_N@,$ECHO_N,;t t s,@ECHO_T@,$ECHO_T,;t t s,@LIBS@,$LIBS,;t t s,@RANLIB@,$RANLIB,;t t s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t s,@CC@,$CC,;t t s,@CFLAGS@,$CFLAGS,;t t s,@LDFLAGS@,$LDFLAGS,;t t s,@CPPFLAGS@,$CPPFLAGS,;t t s,@ac_ct_CC@,$ac_ct_CC,;t t s,@EXEEXT@,$EXEEXT,;t t s,@OBJEXT@,$OBJEXT,;t t s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t s,@INSTALL_DATA@,$INSTALL_DATA,;t t s,@RNG_OBJS@,$RNG_OBJS,;t t s,@CPP@,$CPP,;t t s,@EGREP@,$EGREP,;t t s,@build@,$build,;t t s,@build_cpu@,$build_cpu,;t t s,@build_vendor@,$build_vendor,;t t s,@build_os@,$build_os,;t t s,@host@,$host,;t t s,@host_cpu@,$host_cpu,;t t s,@host_vendor@,$host_vendor,;t t s,@host_os@,$host_os,;t t s,@EXE@,$EXE,;t t s,@GDOI_OBJS@,$GDOI_OBJS,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t CEOF _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # Split the substitutions into bite-sized pieces for seds with # small command number limits, like on Digital OSF/1 and HP-UX. ac_max_sed_lines=48 ac_sed_frag=1 # Number of current file. ac_beg=1 # First line for current file. ac_end=$ac_max_sed_lines # Line after last line for current file. ac_more_lines=: ac_sed_cmds= while $ac_more_lines; do if test $ac_beg -gt 1; then sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag else sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag fi if test ! -s $tmp/subs.frag; then ac_more_lines=false else # The purpose of the label and of the branching condition is to # speed up the sed processing (if there are no `@' at all, there # is no need to browse any of the substitutions). # These are the two extra sed commands mentioned above. (echo ':t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed if test -z "$ac_sed_cmds"; then ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" else ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" fi ac_sed_frag=`expr $ac_sed_frag + 1` ac_beg=$ac_end ac_end=`expr $ac_end + $ac_max_sed_lines` fi done if test -z "$ac_sed_cmds"; then ac_sed_cmds=cat fi fi # test -n "$CONFIG_FILES" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case $ac_file in - | *:- | *:-:* ) # input from stdin cat >$tmp/stdin ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; * ) ac_file_in=$ac_file.in ;; esac # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. ac_dir=`(dirname "$ac_file") 2>/dev/null || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` { if $as_mkdir_p; then mkdir -p "$ac_dir" else as_dir="$ac_dir" as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} { (exit 1); exit 1; }; }; } ac_builddir=. if test "$ac_dir" != .; then ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A "../" for each directory in $ac_dir_suffix. ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` else ac_dir_suffix= ac_top_builddir= fi case $srcdir in .) # No --srcdir option. We are building in place. ac_srcdir=. if test -z "$ac_top_builddir"; then ac_top_srcdir=. else ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` fi ;; [\\/]* | ?:[\\/]* ) # Absolute path. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ;; *) # Relative path. ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_builddir$srcdir ;; esac # Do not use `cd foo && pwd` to compute absolute paths, because # the directories may not exist. case `pwd` in .) ac_abs_builddir="$ac_dir";; *) case "$ac_dir" in .) ac_abs_builddir=`pwd`;; [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; *) ac_abs_builddir=`pwd`/"$ac_dir";; esac;; esac case $ac_abs_builddir in .) ac_abs_top_builddir=${ac_top_builddir}.;; *) case ${ac_top_builddir}. in .) ac_abs_top_builddir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; esac;; esac case $ac_abs_builddir in .) ac_abs_srcdir=$ac_srcdir;; *) case $ac_srcdir in .) ac_abs_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; esac;; esac case $ac_abs_builddir in .) ac_abs_top_srcdir=$ac_top_srcdir;; *) case $ac_top_srcdir in .) ac_abs_top_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; esac;; esac case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_builddir$INSTALL ;; esac if test x"$ac_file" != x-; then { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} rm -f "$ac_file" fi # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ if test x"$ac_file" = x-; then configure_input= else configure_input="$ac_file. " fi configure_input=$configure_input"Generated from `echo $ac_file_in | sed 's,.*/,,'` by configure." # First look for the input files in the build tree, otherwise in the # src tree. ac_file_inputs=`IFS=: for f in $ac_file_in; do case $f in -) echo $tmp/stdin ;; [\\/$]*) # Absolute (can't be DOS-style, as IFS=:) test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } echo "$f";; *) # Relative if test -f "$f"; then # Build tree echo "$f" elif test -f "$srcdir/$f"; then # Source tree echo "$srcdir/$f" else # /dev/null tree { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } fi;; esac done` || { (exit 1); exit 1; } _ACEOF cat >>$CONFIG_STATUS <<_ACEOF sed "$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s,@configure_input@,$configure_input,;t t s,@srcdir@,$ac_srcdir,;t t s,@abs_srcdir@,$ac_abs_srcdir,;t t s,@top_srcdir@,$ac_top_srcdir,;t t s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t s,@builddir@,$ac_builddir,;t t s,@abs_builddir@,$ac_abs_builddir,;t t s,@top_builddir@,$ac_top_builddir,;t t s,@abs_top_builddir@,$ac_abs_top_builddir,;t t s,@INSTALL@,$ac_INSTALL,;t t " $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out rm -f $tmp/stdin if test x"$ac_file" != x-; then mv $tmp/out $ac_file else cat $tmp/out rm -f $tmp/out fi done _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # # CONFIG_HEADER section. # # These sed commands are passed to sed as "A NAME B NAME C VALUE D", where # NAME is the cpp macro being defined and VALUE is the value it is being given. # # ac_d sets the value in "#define NAME VALUE" lines. ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' ac_dB='[ ].*$,\1#\2' ac_dC=' ' ac_dD=',;t' # ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' ac_uB='$,\1#\2define\3' ac_uC=' ' ac_uD=',;t' for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case $ac_file in - | *:- | *:-:* ) # input from stdin cat >$tmp/stdin ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; * ) ac_file_in=$ac_file.in ;; esac test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} # First look for the input files in the build tree, otherwise in the # src tree. ac_file_inputs=`IFS=: for f in $ac_file_in; do case $f in -) echo $tmp/stdin ;; [\\/$]*) # Absolute (can't be DOS-style, as IFS=:) test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } # Do quote $f, to prevent DOS paths from being IFS'd. echo "$f";; *) # Relative if test -f "$f"; then # Build tree echo "$f" elif test -f "$srcdir/$f"; then # Source tree echo "$srcdir/$f" else # /dev/null tree { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } fi;; esac done` || { (exit 1); exit 1; } # Remove the trailing spaces. sed 's/[ ]*$//' $ac_file_inputs >$tmp/in _ACEOF # Transform confdefs.h into two sed scripts, `conftest.defines' and # `conftest.undefs', that substitutes the proper values into # config.h.in to produce config.h. The first handles `#define' # templates, and the second `#undef' templates. # And first: Protect against being on the right side of a sed subst in # config.status. Protect against being in an unquoted here document # in config.status. rm -f conftest.defines conftest.undefs # Using a here document instead of a string reduces the quoting nightmare. # Putting comments in sed scripts is not portable. # # `end' is used to avoid that the second main sed command (meant for # 0-ary CPP macros) applies to n-ary macro definitions. # See the Autoconf documentation for `clear'. cat >confdef2sed.sed <<\_ACEOF s/[\\&,]/\\&/g s,[\\$`],\\&,g t clear : clear s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp t end s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp : end _ACEOF # If some macros were called several times there might be several times # the same #defines, which is useless. Nevertheless, we may not want to # sort them, since we want the *last* AC-DEFINE to be honored. uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs rm -f confdef2sed.sed # This sed command replaces #undef with comments. This is necessary, for # example, in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. cat >>conftest.undefs <<\_ACEOF s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, _ACEOF # Break up conftest.defines because some shells have a limit on the size # of here documents, and old seds have small limits too (100 cmds). echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS echo ' :' >>$CONFIG_STATUS rm -f conftest.tail while grep . conftest.defines >/dev/null do # Write a limited-size here document to $tmp/defines.sed. echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS # Speed up: don't consider the non `#define' lines. echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS # Work around the forget-to-reset-the-flag bug. echo 't clr' >>$CONFIG_STATUS echo ': clr' >>$CONFIG_STATUS sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS echo 'CEOF sed -f $tmp/defines.sed $tmp/in >$tmp/out rm -f $tmp/in mv $tmp/out $tmp/in ' >>$CONFIG_STATUS sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail rm -f conftest.defines mv conftest.tail conftest.defines done rm -f conftest.defines echo ' fi # grep' >>$CONFIG_STATUS echo >>$CONFIG_STATUS # Break up conftest.undefs because some shells have a limit on the size # of here documents, and old seds have small limits too (100 cmds). echo ' # Handle all the #undef templates' >>$CONFIG_STATUS rm -f conftest.tail while grep . conftest.undefs >/dev/null do # Write a limited-size here document to $tmp/undefs.sed. echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS # Speed up: don't consider the non `#undef' echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS # Work around the forget-to-reset-the-flag bug. echo 't clr' >>$CONFIG_STATUS echo ': clr' >>$CONFIG_STATUS sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS echo 'CEOF sed -f $tmp/undefs.sed $tmp/in >$tmp/out rm -f $tmp/in mv $tmp/out $tmp/in ' >>$CONFIG_STATUS sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail rm -f conftest.undefs mv conftest.tail conftest.undefs done rm -f conftest.undefs cat >>$CONFIG_STATUS <<\_ACEOF # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ if test x"$ac_file" = x-; then echo "/* Generated by configure. */" >$tmp/config.h else echo "/* $ac_file. Generated by configure. */" >$tmp/config.h fi cat $tmp/in >>$tmp/config.h rm -f $tmp/in if test x"$ac_file" != x-; then if diff $ac_file $tmp/config.h >/dev/null 2>&1; then { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 echo "$as_me: $ac_file is unchanged" >&6;} else ac_dir=`(dirname "$ac_file") 2>/dev/null || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` { if $as_mkdir_p; then mkdir -p "$ac_dir" else as_dir="$ac_dir" as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} { (exit 1); exit 1; }; }; } rm -f $ac_file mv $tmp/config.h $ac_file fi else cat $tmp/config.h rm -f $tmp/config.h fi done _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF { (exit 0); exit 0; } _ACEOF chmod +x $CONFIG_STATUS ac_clean_files=$ac_clean_files_save # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || { (exit 1); exit 1; } fi # This is needed when building outside the source dir. { if $as_mkdir_p; then mkdir -p crypto/ae_xfm else as_dir=crypto/ae_xfm as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory crypto/ae_xfm" >&5 echo "$as_me: error: cannot create directory crypto/ae_xfm" >&2;} { (exit 1); exit 1; }; }; } { if $as_mkdir_p; then mkdir -p crypto/cipher else as_dir=crypto/cipher as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory crypto/cipher" >&5 echo "$as_me: error: cannot create directory crypto/cipher" >&2;} { (exit 1); exit 1; }; }; } { if $as_mkdir_p; then mkdir -p crypto/hash else as_dir=crypto/hash as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory crypto/hash" >&5 echo "$as_me: error: cannot create directory crypto/hash" >&2;} { (exit 1); exit 1; }; }; } { if $as_mkdir_p; then mkdir -p crypto/kernel else as_dir=crypto/kernel as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory crypto/kernel" >&5 echo "$as_me: error: cannot create directory crypto/kernel" >&2;} { (exit 1); exit 1; }; }; } { if $as_mkdir_p; then mkdir -p crypto/math else as_dir=crypto/math as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory crypto/math" >&5 echo "$as_me: error: cannot create directory crypto/math" >&2;} { (exit 1); exit 1; }; }; } { if $as_mkdir_p; then mkdir -p crypto/replay else as_dir=crypto/replay as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory crypto/replay" >&5 echo "$as_me: error: cannot create directory crypto/replay" >&2;} { (exit 1); exit 1; }; }; } { if $as_mkdir_p; then mkdir -p crypto/rng else as_dir=crypto/rng as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory crypto/rng" >&5 echo "$as_me: error: cannot create directory crypto/rng" >&2;} { (exit 1); exit 1; }; }; } { if $as_mkdir_p; then mkdir -p crypto/test else as_dir=crypto/test as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory crypto/test" >&5 echo "$as_me: error: cannot create directory crypto/test" >&2;} { (exit 1); exit 1; }; }; } { if $as_mkdir_p; then mkdir -p doc else as_dir=doc as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory doc" >&5 echo "$as_me: error: cannot create directory doc" >&2;} { (exit 1); exit 1; }; }; } { if $as_mkdir_p; then mkdir -p srtp else as_dir=srtp as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory srtp" >&5 echo "$as_me: error: cannot create directory srtp" >&2;} { (exit 1); exit 1; }; }; } { if $as_mkdir_p; then mkdir -p tables else as_dir=tables as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory tables" >&5 echo "$as_me: error: cannot create directory tables" >&2;} { (exit 1); exit 1; }; }; } { if $as_mkdir_p; then mkdir -p test else as_dir=test as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory test" >&5 echo "$as_me: error: cannot create directory test" >&2;} { (exit 1); exit 1; }; }; } ================================================ FILE: deps/pjsip/third_party/srtp/configure.in ================================================ dnl Process this file with autoconf to produce a configure script. AC_INIT(srtp) dnl Must come before AC_PROG_CC if test -z "$CFLAGS"; then dnl Default value for CFLAGS if not specified. CFLAGS="-Wall -O4 -fexpensive-optimizations -funroll-loops" fi dnl Checks for programs. AC_PROG_RANLIB AC_PROG_CC AC_PROG_INSTALL AC_ARG_ENABLE(kernel-linux, [AS_HELP_STRING([--enable-kernel-linux], [build library to run in Linux kernel context])], [], enable_kernel_linux=no) AC_MSG_CHECKING(whether to build for Linux kernel context) if test "$enable_kernel_linux" = "yes"; then AC_DEFINE(SRTP_KERNEL, 1, [Define to compile for kernel contexts.]) AC_DEFINE(SRTP_KERNEL_LINUX, 1, [Define to compile for Linux kernel context.]) fi AC_MSG_RESULT($enable_kernel_linux) if test "$cross_compiling" != yes; then dnl Check for /dev/urandom AC_CHECK_FILE(/dev/urandom, DEV_URANDOM=/dev/urandom, [AC_CHECK_FILE(/dev/random, DEV_URANDOM=/dev/random)]) fi AC_MSG_CHECKING(which random device to use) if test "$enable_kernel_linux" = "yes"; then RNG_OBJS=rand_linux_kernel.o AC_MSG_RESULT([Linux kernel builtin]) else RNG_OBJS=rand_source.o if test -n "$DEV_URANDOM"; then AC_DEFINE_UNQUOTED(DEV_URANDOM, "$DEV_URANDOM",[Path to random device]) AC_MSG_RESULT([$DEV_URANDOM]) else AC_MSG_RESULT([standard rand() function...]) fi fi AC_SUBST(RNG_OBJS) dnl Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS(stdlib.h) AC_CHECK_HEADERS(unistd.h) AC_CHECK_HEADERS(byteswap.h) AC_CHECK_HEADERS(stdint.h) AC_CHECK_HEADERS(sys/uio.h) AC_CHECK_HEADERS(inttypes.h) AC_CHECK_HEADERS(sys/types.h) AC_CHECK_HEADERS(machine/types.h) AC_CHECK_HEADERS(sys/int_types.h) dnl socket() and friends AC_CHECK_HEADERS(sys/socket.h netinet/in.h arpa/inet.h) AC_CHECK_HEADERS(windows.h, [AC_CHECK_HEADERS(winsock2.h)]) AC_CHECK_HEADERS(syslog.h) AC_CHECK_TYPES([int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,uint64_t]) AC_CHECK_SIZEOF(unsigned long) AC_CHECK_SIZEOF(unsigned long long) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_C_INLINE AC_TYPE_SIZE_T dnl Checks for library functions. AC_CHECK_FUNCS(socket inet_aton usleep) dnl Find socket function if not found yet. if test "x$ac_cv_func_socket" = "xno"; then AC_CHECK_LIB(socket, socket) AC_MSG_CHECKING([for socket in -lwsock32]) SAVELIBS="$LIBS" LIBS="$LIBS -lwsock32" AC_TRY_LINK([ #include ],[ socket(0, 0, 0); ], ac_cv_func_socket=yes AC_MSG_RESULT(yes), LIBS="$SAVELIBS" AC_MSG_RESULT(no)) fi dnl Check the byte order AC_C_BIGENDIAN AC_CANONICAL_HOST dnl check host_cpu type, set defines appropriately case $host_cpu in i*86 ) AC_DEFINE(CPU_CISC, 1, [Define if building for a CISC machine (e.g. Intel).]) AC_DEFINE(HAVE_X86, 1, [Define to use X86 inlined assembly code]);; * ) # CPU_RISC is only supported for big endian machines. if test "$ac_cv_c_bigendian" = "yes"; then AC_DEFINE(CPU_RISC, 1, [Define if building for a RISC machine (assume slow byte access).]) else AC_DEFINE(CPU_CISC, 1) fi ;; esac dnl Check if we're on a Windows platform. case $host_os in *cygwin*|*mingw* ) EXE=.exe;; * ) EXE="";; esac AC_SUBST(EXE) # define executable suffix; this is needed for `make clean' AC_MSG_CHECKING(whether to compile in debugging) AC_ARG_ENABLE(debug, [AS_HELP_STRING([--disable-debug], [do not compile in dynamic debugging system])], [], enable_debug=yes) if test "$enable_debug" = "yes"; then AC_DEFINE(ENABLE_DEBUGGING, 1, [Define to compile in dynamic debugging system.]) fi AC_MSG_RESULT($enable_debug) AC_MSG_CHECKING(whether to use ISMAcryp code) AC_ARG_ENABLE(generic-aesicm, [AS_HELP_STRING([--enable-generic-aesicm], [compile in changes for ISMAcryp])], [], enable_generic_aesicm=no) if test "$enable_generic_aesicm" = "yes"; then AC_DEFINE(GENERIC_AESICM, 1, [Define this to use ISMAcryp code.]) fi AC_MSG_RESULT($enable_generic_aesicm) AC_MSG_CHECKING(whether to use syslog for error reporting) AC_ARG_ENABLE(syslog, [AS_HELP_STRING([--enable-syslog], [use syslog for error reporting])], [], enable_syslog=no) if test "$enable_syslog" = "yes"; then AC_DEFINE(USE_SYSLOG, 1, [Define to use syslog logging.]) fi AC_MSG_RESULT($enable_syslog) AC_MSG_CHECKING(whether to use stdout for error reporting) AC_ARG_ENABLE(stdout, [AS_HELP_STRING([--disable-stdout], [don't use stdout for error reporting])], [], enable_stdout=yes) if test "$enable_stdout" = "yes"; then AC_DEFINE(ERR_REPORTING_STDOUT, 1, [Define to use logging to stdout.]) fi AC_MSG_RESULT($enable_stdout) AC_MSG_CHECKING(whether to use /dev/console for error reporting) AC_ARG_ENABLE(console, [AS_HELP_STRING([--enable-console], [use /dev/console for error reporting])], [], enable_console=no) if test "$enable_console" = "yes"; then AC_DEFINE(USE_ERR_REPORTING_FILE, 1, [Write errors to this file]) AC_DEFINE(ERR_REPORTING_FILE, "/dev/console", [Report errors to this file.]) fi AC_MSG_RESULT($enable_console) AC_MSG_CHECKING(whether to use GDOI key management) AC_ARG_ENABLE(gdoi, [AS_HELP_STRING([--enable-gdoi], [enable GDOI key management])], [], enable_gdoi=no) if test "$enable_gdoi" = "yes"; then AC_DEFINE(SRTP_GDOI, 1, [Define to use GDOI.]) GDOI_OBJS=gdoi/srtp+gdoi.o AC_SUBST(GDOI_OBJS) fi AC_MSG_RESULT($enable_gdoi) AC_CONFIG_HEADER(crypto/include/config.h:config_in.h) AC_OUTPUT(Makefile crypto/Makefile doc/Makefile) # This is needed when building outside the source dir. AS_MKDIR_P(crypto/ae_xfm) AS_MKDIR_P(crypto/cipher) AS_MKDIR_P(crypto/hash) AS_MKDIR_P(crypto/kernel) AS_MKDIR_P(crypto/math) AS_MKDIR_P(crypto/replay) AS_MKDIR_P(crypto/rng) AS_MKDIR_P(crypto/test) AS_MKDIR_P(doc) AS_MKDIR_P(srtp) AS_MKDIR_P(tables) AS_MKDIR_P(test) ================================================ FILE: deps/pjsip/third_party/srtp/crypto/Makefile ================================================ # Makefile for libcryptomodule.a # # David A. McGrew # Cisco Systems, Inc. srcdir = . top_srcdir = .. top_builddir = ../ CC = gcc INCDIR = -Iinclude -I$(srcdir)/include DEFS = -DHAVE_CONFIG_H CPPFLAGS= CFLAGS = -Wall -O4 -fexpensive-optimizations -funroll-loops LIBS = LDFLAGS = -L. COMPILE = $(CC) $(DEFS) $(INCDIR) $(CPPFLAGS) $(CFLAGS) CRYPTOLIB = -lcryptomodule RANLIB = ranlib # EXE defines the suffix on executables - it's .exe for cygwin, and # null on linux, bsd, and OS X and other OSes. we define this so that # `make clean` will work on the cygwin platform EXE = # Random source. RNG_OBJS = rand_source.o ifdef ARCH DEFS += -D$(ARCH)=1 endif ifdef sysname DEFS += -D$(sysname)=1 endif .PHONY: dummy all runtest clean superclean dummy : all runtest # test applications testapp = test/cipher_driver$(EXE) test/datatypes_driver$(EXE) \ test/stat_driver$(EXE) test/sha1_driver$(EXE) \ test/kernel_driver$(EXE) test/aes_calc$(EXE) test/rand_gen$(EXE) \ test/env$(EXE) # data values used to test the aes_calc application k=000102030405060708090a0b0c0d0e0f p=00112233445566778899aabbccddeeff c=69c4e0d86a7b0430d8cdb78070b4c55a runtest: libcryptomodule.a $(testapp) test/env$(EXE) # print out information on the build environment @echo "running libcryptomodule test applications..." test `test/aes_calc $k $p` = $c test/cipher_driver$(EXE) -v >/dev/null test/datatypes_driver$(EXE) -v >/dev/null test/stat_driver$(EXE) >/dev/null test/sha1_driver$(EXE) -v >/dev/null test/kernel_driver$(EXE) -v >/dev/null test/rand_gen$(EXE) -n 256 >/dev/null @echo "libcryptomodule test applications passed." # libcryptomodule.a (the crypto engine) ciphers = cipher/cipher.o cipher/null_cipher.o \ cipher/aes.o cipher/aes_icm.o \ cipher/aes_cbc.o hashes = hash/null_auth.o hash/sha1.o \ hash/hmac.o hash/auth.o math = math/datatypes.o math/stat.o rng = rng/$(RNG_OBJS) rng/rand_source.o rng/prng.o rng/ctr_prng.o err = kernel/err.o kernel = kernel/crypto_kernel.o kernel/alloc.o \ kernel/key.o $(rng) $(err) xfm = ae_xfm/xfm.o cryptobj = $(ciphers) $(hashes) $(math) $(stat) $(kernel) $(xfm) # the rule for making object files and test apps %.o: %.c $(COMPILE) -c $< -o $@ %$(EXE): %.c libcryptomodule.a $(COMPILE) $(LDFLAGS) $< -o $@ $(CRYPTOLIB) $(LIBS) ifndef AR AR=ar endif # and the crypto module library itself libcryptomodule.a: $(cryptobj) $(AR) cr libcryptomodule.a $(cryptobj) $(RANLIB) libcryptomodule.a all: libcryptomodule.a $(testapp) # housekeeping functions clean: rm -f libcryptomodule.a rm -f $(testapp) *.o */*.o for a in * .* */*; do if [ -f "$$a~" ] ; then rm $$a~; fi; done; rm -f `find . -name "*.[ch]~*~"` rm -rf latex superclean: clean rm -f *core TAGS ktrace.out # the target 'package' builds a compressed tar archive of the source code distname = crypto-$(shell cat VERSION) package: superclean cd ..; tar cvzf $(distname).tgz crypto/ # EOF ================================================ FILE: deps/pjsip/third_party/srtp/crypto/Makefile.in ================================================ # Makefile for libcryptomodule.a # # David A. McGrew # Cisco Systems, Inc. srcdir = @srcdir@ top_srcdir = @top_srcdir@ top_builddir = @top_builddir@ VPATH = @srcdir@ CC = @CC@ INCDIR = -Iinclude -I$(srcdir)/include DEFS = @DEFS@ CPPFLAGS= @CPPFLAGS@ CFLAGS = @CFLAGS@ LIBS = @LIBS@ LDFLAGS = @LDFLAGS@ -L. COMPILE = $(CC) $(DEFS) $(INCDIR) $(CPPFLAGS) $(CFLAGS) CRYPTOLIB = -lcryptomodule RANLIB = @RANLIB@ # EXE defines the suffix on executables - it's .exe for cygwin, and # null on linux, bsd, and OS X and other OSes. we define this so that # `make clean` will work on the cygwin platform EXE = @EXE@ # Random source. RNG_OBJS = @RNG_OBJS@ ifdef ARCH DEFS += -D$(ARCH)=1 endif ifdef sysname DEFS += -D$(sysname)=1 endif .PHONY: dummy all runtest clean superclean dummy : all runtest # test applications testapp = test/cipher_driver$(EXE) test/datatypes_driver$(EXE) \ test/stat_driver$(EXE) test/sha1_driver$(EXE) \ test/kernel_driver$(EXE) test/aes_calc$(EXE) test/rand_gen$(EXE) \ test/env$(EXE) # data values used to test the aes_calc application k=000102030405060708090a0b0c0d0e0f p=00112233445566778899aabbccddeeff c=69c4e0d86a7b0430d8cdb78070b4c55a runtest: libcryptomodule.a $(testapp) test/env$(EXE) # print out information on the build environment @echo "running libcryptomodule test applications..." test `test/aes_calc $k $p` = $c test/cipher_driver$(EXE) -v >/dev/null test/datatypes_driver$(EXE) -v >/dev/null test/stat_driver$(EXE) >/dev/null test/sha1_driver$(EXE) -v >/dev/null test/kernel_driver$(EXE) -v >/dev/null test/rand_gen$(EXE) -n 256 >/dev/null @echo "libcryptomodule test applications passed." # libcryptomodule.a (the crypto engine) ciphers = cipher/cipher.o cipher/null_cipher.o \ cipher/aes.o cipher/aes_icm.o \ cipher/aes_cbc.o hashes = hash/null_auth.o hash/sha1.o \ hash/hmac.o hash/auth.o math = math/datatypes.o math/stat.o rng = rng/$(RNG_OBJS) rng/rand_source.o rng/prng.o rng/ctr_prng.o err = kernel/err.o kernel = kernel/crypto_kernel.o kernel/alloc.o \ kernel/key.o $(rng) $(err) xfm = ae_xfm/xfm.o cryptobj = $(ciphers) $(hashes) $(math) $(stat) $(kernel) $(xfm) # the rule for making object files and test apps %.o: %.c $(COMPILE) -c $< -o $@ %$(EXE): %.c libcryptomodule.a $(COMPILE) $(LDFLAGS) $< -o $@ $(CRYPTOLIB) $(LIBS) ifndef AR AR=ar endif # and the crypto module library itself libcryptomodule.a: $(cryptobj) $(AR) cr libcryptomodule.a $(cryptobj) $(RANLIB) libcryptomodule.a all: libcryptomodule.a $(testapp) # housekeeping functions clean: rm -f libcryptomodule.a rm -f $(testapp) *.o */*.o for a in * .* */*; do if [ -f "$$a~" ] ; then rm $$a~; fi; done; rm -f `find . -name "*.[ch]~*~"` rm -rf latex superclean: clean rm -f *core TAGS ktrace.out # the target 'package' builds a compressed tar archive of the source code distname = crypto-$(shell cat VERSION) package: superclean cd ..; tar cvzf $(distname).tgz crypto/ # EOF ================================================ FILE: deps/pjsip/third_party/srtp/crypto/VERSION ================================================ 1.0.0 ================================================ FILE: deps/pjsip/third_party/srtp/crypto/ae_xfm/xfm.c ================================================ /* * xfm.c * * Crypto transform implementation * * David A. McGrew * Cisco Systems, Inc. */ #include "cryptoalg.h" #include "aes_cbc.h" #include "hmac.h" #include "crypto_kernel.h" /* for crypto_get_random() */ #define KEY_LEN 16 #define ENC_KEY_LEN 16 #define MAC_KEY_LEN 16 #define IV_LEN 16 #define TAG_LEN 12 #define MAX_EXPAND 27 err_status_t aes_128_cbc_hmac_sha1_96_func(void *key, void *clear, unsigned clear_len, void *iv, void *opaque, unsigned *opaque_len, void *auth_tag) { aes_cbc_ctx_t aes_ctx; hmac_ctx_t hmac_ctx; unsigned char enc_key[ENC_KEY_LEN]; unsigned char mac_key[MAC_KEY_LEN]; err_status_t status; /* check if we're doing authentication only */ if ((iv == NULL) && (opaque == NULL) && (opaque_len == NULL)) { /* perform authentication only */ } else if ((iv == NULL) || (opaque == NULL) || (opaque_len == NULL)) { /* * bad parameter - we expect either all three pointers to be NULL, * or none of those pointers to be NULL */ return err_status_fail; } else { /* derive encryption and authentication keys from the input key */ status = hmac_init(&hmac_ctx, key, KEY_LEN); if (status) return status; status = hmac_compute(&hmac_ctx, "ENC", 3, ENC_KEY_LEN, enc_key); if (status) return status; status = hmac_init(&hmac_ctx, key, KEY_LEN); if (status) return status; status = hmac_compute(&hmac_ctx, "MAC", 3, MAC_KEY_LEN, mac_key); if (status) return status; /* perform encryption and authentication */ /* set aes key */ status = aes_cbc_context_init(&aes_ctx, key, direction_encrypt); if (status) return status; /* set iv */ status = crypto_get_random(iv, IV_LEN); if (status) return status; status = aes_cbc_set_iv(&aes_ctx, iv); /* encrypt the opaque data */ status = aes_cbc_nist_encrypt(&aes_ctx, opaque, opaque_len); if (status) return status; /* authenticate clear and opaque data */ status = hmac_init(&hmac_ctx, mac_key, MAC_KEY_LEN); if (status) return status; status = hmac_start(&hmac_ctx); if (status) return status; status = hmac_update(&hmac_ctx, clear, clear_len); if (status) return status; status = hmac_compute(&hmac_ctx, opaque, *opaque_len, TAG_LEN, auth_tag); if (status) return status; } return err_status_ok; } err_status_t aes_128_cbc_hmac_sha1_96_inv(void *key, void *clear, unsigned clear_len, void *iv, void *opaque, unsigned *opaque_len, void *auth_tag) { aes_cbc_ctx_t aes_ctx; hmac_ctx_t hmac_ctx; unsigned char enc_key[ENC_KEY_LEN]; unsigned char mac_key[MAC_KEY_LEN]; unsigned char tmp_tag[TAG_LEN]; unsigned char *tag = auth_tag; err_status_t status; int i; /* check if we're doing authentication only */ if ((iv == NULL) && (opaque == NULL) && (opaque_len == NULL)) { /* perform authentication only */ } else if ((iv == NULL) || (opaque == NULL) || (opaque_len == NULL)) { /* * bad parameter - we expect either all three pointers to be NULL, * or none of those pointers to be NULL */ return err_status_fail; } else { /* derive encryption and authentication keys from the input key */ status = hmac_init(&hmac_ctx, key, KEY_LEN); if (status) return status; status = hmac_compute(&hmac_ctx, "ENC", 3, ENC_KEY_LEN, enc_key); if (status) return status; status = hmac_init(&hmac_ctx, key, KEY_LEN); if (status) return status; status = hmac_compute(&hmac_ctx, "MAC", 3, MAC_KEY_LEN, mac_key); if (status) return status; /* perform encryption and authentication */ /* set aes key */ status = aes_cbc_context_init(&aes_ctx, key, direction_decrypt); if (status) return status; /* set iv */ status = rand_source_get_octet_string(iv, IV_LEN); if (status) return status; status = aes_cbc_set_iv(&aes_ctx, iv); /* encrypt the opaque data */ status = aes_cbc_nist_decrypt(&aes_ctx, opaque, opaque_len); if (status) return status; /* authenticate clear and opaque data */ status = hmac_init(&hmac_ctx, mac_key, MAC_KEY_LEN); if (status) return status; status = hmac_start(&hmac_ctx); if (status) return status; status = hmac_update(&hmac_ctx, clear, clear_len); if (status) return status; status = hmac_compute(&hmac_ctx, opaque, *opaque_len, TAG_LEN, tmp_tag); if (status) return status; /* compare the computed tag with the one provided as input */ for (i=0; i < TAG_LEN; i++) if (tmp_tag[i] != tag[i]) return err_status_auth_fail; } return err_status_ok; } #define ENC 1 // eVC4 declares DEBUG #undef DEBUG #define DEBUG 0 err_status_t aes_128_cbc_hmac_sha1_96_enc(void *key, const void *clear, unsigned clear_len, void *iv, void *opaque, unsigned *opaque_len) { aes_cbc_ctx_t aes_ctx; hmac_ctx_t hmac_ctx; unsigned char enc_key[ENC_KEY_LEN]; unsigned char mac_key[MAC_KEY_LEN]; unsigned char *auth_tag; err_status_t status; /* check if we're doing authentication only */ if ((iv == NULL) && (opaque == NULL) && (opaque_len == NULL)) { /* perform authentication only */ } else if ((iv == NULL) || (opaque == NULL) || (opaque_len == NULL)) { /* * bad parameter - we expect either all three pointers to be NULL, * or none of those pointers to be NULL */ return err_status_fail; } else { #if DEBUG printf("ENC using key %s\n", octet_string_hex_string(key, KEY_LEN)); #endif /* derive encryption and authentication keys from the input key */ status = hmac_init(&hmac_ctx, key, KEY_LEN); if (status) return status; status = hmac_compute(&hmac_ctx, "ENC", 3, ENC_KEY_LEN, enc_key); if (status) return status; status = hmac_init(&hmac_ctx, key, KEY_LEN); if (status) return status; status = hmac_compute(&hmac_ctx, "MAC", 3, MAC_KEY_LEN, mac_key); if (status) return status; /* perform encryption and authentication */ /* set aes key */ status = aes_cbc_context_init(&aes_ctx, key, direction_encrypt); if (status) return status; /* set iv */ status = rand_source_get_octet_string(iv, IV_LEN); if (status) return status; status = aes_cbc_set_iv(&aes_ctx, iv); if (status) return status; #if DEBUG printf("plaintext len: %d\n", *opaque_len); printf("iv: %s\n", octet_string_hex_string(iv, IV_LEN)); printf("plaintext: %s\n", octet_string_hex_string(opaque, *opaque_len)); #endif #if ENC /* encrypt the opaque data */ status = aes_cbc_nist_encrypt(&aes_ctx, opaque, opaque_len); if (status) return status; #endif #if DEBUG printf("ciphertext len: %d\n", *opaque_len); printf("ciphertext: %s\n", octet_string_hex_string(opaque, *opaque_len)); #endif /* * authenticate clear and opaque data, then write the * authentication tag to the location immediately following the * ciphertext */ status = hmac_init(&hmac_ctx, mac_key, MAC_KEY_LEN); if (status) return status; status = hmac_start(&hmac_ctx); if (status) return status; status = hmac_update(&hmac_ctx, clear, clear_len); if (status) return status; #if DEBUG printf("hmac input: %s\n", octet_string_hex_string(clear, clear_len)); #endif auth_tag = (unsigned char *)opaque; auth_tag += *opaque_len; status = hmac_compute(&hmac_ctx, opaque, *opaque_len, TAG_LEN, auth_tag); if (status) return status; #if DEBUG printf("hmac input: %s\n", octet_string_hex_string(opaque, *opaque_len)); #endif /* bump up the opaque_len to reflect the authentication tag */ *opaque_len += TAG_LEN; #if DEBUG printf("prot data len: %d\n", *opaque_len); printf("prot data: %s\n", octet_string_hex_string(opaque, *opaque_len)); #endif } return err_status_ok; } err_status_t aes_128_cbc_hmac_sha1_96_dec(void *key, const void *clear, unsigned clear_len, void *iv, void *opaque, unsigned *opaque_len) { aes_cbc_ctx_t aes_ctx; hmac_ctx_t hmac_ctx; unsigned char enc_key[ENC_KEY_LEN]; unsigned char mac_key[MAC_KEY_LEN]; unsigned char tmp_tag[TAG_LEN]; unsigned char *auth_tag; unsigned ciphertext_len; err_status_t status; int i; /* check if we're doing authentication only */ if ((iv == NULL) && (opaque == NULL) && (opaque_len == NULL)) { /* perform authentication only */ } else if ((iv == NULL) || (opaque == NULL) || (opaque_len == NULL)) { /* * bad parameter - we expect either all three pointers to be NULL, * or none of those pointers to be NULL */ return err_status_fail; } else { #if DEBUG printf("DEC using key %s\n", octet_string_hex_string(key, KEY_LEN)); #endif /* derive encryption and authentication keys from the input key */ status = hmac_init(&hmac_ctx, key, KEY_LEN); if (status) return status; status = hmac_compute(&hmac_ctx, "ENC", 3, ENC_KEY_LEN, enc_key); if (status) return status; status = hmac_init(&hmac_ctx, key, KEY_LEN); if (status) return status; status = hmac_compute(&hmac_ctx, "MAC", 3, MAC_KEY_LEN, mac_key); if (status) return status; #if DEBUG printf("prot data len: %d\n", *opaque_len); printf("prot data: %s\n", octet_string_hex_string(opaque, *opaque_len)); #endif /* * set the protected data length to that of the ciphertext, by * subtracting out the length of the authentication tag */ ciphertext_len = *opaque_len - TAG_LEN; #if DEBUG printf("ciphertext len: %d\n", ciphertext_len); #endif /* verify the authentication tag */ /* * compute the authentication tag for the clear and opaque data, * and write it to a temporary location */ status = hmac_init(&hmac_ctx, mac_key, MAC_KEY_LEN); if (status) return status; status = hmac_start(&hmac_ctx); if (status) return status; status = hmac_update(&hmac_ctx, clear, clear_len); if (status) return status; #if DEBUG printf("hmac input: %s\n", octet_string_hex_string(clear, clear_len)); #endif status = hmac_compute(&hmac_ctx, opaque, ciphertext_len, TAG_LEN, tmp_tag); if (status) return status; #if DEBUG printf("hmac input: %s\n", octet_string_hex_string(opaque, ciphertext_len)); #endif /* * compare the computed tag with the one provided as input (which * immediately follows the ciphertext) */ auth_tag = (unsigned char *)opaque; auth_tag += ciphertext_len; #if DEBUG printf("auth_tag: %s\n", octet_string_hex_string(auth_tag, TAG_LEN)); printf("tmp_tag: %s\n", octet_string_hex_string(tmp_tag, TAG_LEN)); #endif for (i=0; i < TAG_LEN; i++) { if (tmp_tag[i] != auth_tag[i]) return err_status_auth_fail; } /* bump down the opaque_len to reflect the authentication tag */ *opaque_len -= TAG_LEN; /* decrypt the confidential data */ status = aes_cbc_context_init(&aes_ctx, key, direction_decrypt); if (status) return status; status = aes_cbc_set_iv(&aes_ctx, iv); if (status) return status; #if DEBUG printf("ciphertext: %s\n", octet_string_hex_string(opaque, *opaque_len)); printf("iv: %s\n", octet_string_hex_string(iv, IV_LEN)); #endif #if ENC status = aes_cbc_nist_decrypt(&aes_ctx, opaque, &ciphertext_len); if (status) return status; #endif #if DEBUG printf("plaintext len: %d\n", ciphertext_len); printf("plaintext: %s\n", octet_string_hex_string(opaque, ciphertext_len)); #endif /* indicate the length of the plaintext */ *opaque_len = ciphertext_len; } return err_status_ok; } cryptoalg_ctx_t cryptoalg_ctx = { aes_128_cbc_hmac_sha1_96_enc, aes_128_cbc_hmac_sha1_96_dec, KEY_LEN, IV_LEN, TAG_LEN, MAX_EXPAND, }; cryptoalg_t cryptoalg = &cryptoalg_ctx; #define NULL_TAG_LEN 12 err_status_t null_enc(void *key, const void *clear, unsigned clear_len, void *iv, void *opaque, unsigned *opaque_len) { int i; unsigned char *auth_tag; unsigned char *init_vec = iv; /* check if we're doing authentication only */ if ((iv == NULL) && (opaque == NULL) && (opaque_len == NULL)) { /* perform authentication only */ } else if ((iv == NULL) || (opaque == NULL) || (opaque_len == NULL)) { /* * bad parameter - we expect either all three pointers to be NULL, * or none of those pointers to be NULL */ return err_status_fail; } else { #if DEBUG printf("NULL ENC using key %s\n", octet_string_hex_string(key, KEY_LEN)); printf("NULL_TAG_LEN: %d\n", NULL_TAG_LEN); printf("plaintext len: %d\n", *opaque_len); #endif for (i=0; i < IV_LEN; i++) init_vec[i] = i + (i * 16); #if DEBUG printf("iv: %s\n", octet_string_hex_string(iv, IV_LEN)); printf("plaintext: %s\n", octet_string_hex_string(opaque, *opaque_len)); #endif auth_tag = opaque; auth_tag += *opaque_len; for (i=0; i < NULL_TAG_LEN; i++) auth_tag[i] = i + (i * 16); *opaque_len += NULL_TAG_LEN; #if DEBUG printf("protected data len: %d\n", *opaque_len); printf("protected data: %s\n", octet_string_hex_string(opaque, *opaque_len)); #endif } return err_status_ok; } err_status_t null_dec(void *key, const void *clear, unsigned clear_len, void *iv, void *opaque, unsigned *opaque_len) { unsigned char *auth_tag; /* check if we're doing authentication only */ if ((iv == NULL) && (opaque == NULL) && (opaque_len == NULL)) { /* perform authentication only */ } else if ((iv == NULL) || (opaque == NULL) || (opaque_len == NULL)) { /* * bad parameter - we expect either all three pointers to be NULL, * or none of those pointers to be NULL */ return err_status_fail; } else { #if DEBUG printf("NULL DEC using key %s\n", octet_string_hex_string(key, KEY_LEN)); printf("protected data len: %d\n", *opaque_len); printf("protected data: %s\n", octet_string_hex_string(opaque, *opaque_len)); #endif auth_tag = opaque; auth_tag += (*opaque_len - NULL_TAG_LEN); #if DEBUG printf("iv: %s\n", octet_string_hex_string(iv, IV_LEN)); #endif *opaque_len -= NULL_TAG_LEN; #if DEBUG printf("plaintext len: %d\n", *opaque_len); printf("plaintext: %s\n", octet_string_hex_string(opaque, *opaque_len)); #endif } return err_status_ok; } cryptoalg_ctx_t null_cryptoalg_ctx = { null_enc, null_dec, KEY_LEN, IV_LEN, NULL_TAG_LEN, MAX_EXPAND, }; cryptoalg_t null_cryptoalg = &null_cryptoalg_ctx; int cryptoalg_get_id(cryptoalg_t c) { if (c == cryptoalg) return 1; return 0; } cryptoalg_t cryptoalg_find_by_id(int id) { switch(id) { case 1: return cryptoalg; default: break; } return 0; } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/cipher/aes.c ================================================ /* * aes.c * * An implemnetation of the AES block cipher. * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "aes.h" #include "err.h" /* * we use the tables T0, T1, T2, T3, and T4 to compute AES, and * the tables U0, U1, U2, and U4 to compute its inverse * * different tables are used on little-endian (Intel, VMS) and * big-endian processors (everything else) * * these tables are computed using the program tables/aes_tables; use * this program to generate different tables for porting or * optimization on a different platform */ #ifndef WORDS_BIGENDIAN static uint32_t T0[256] = { 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0xdf2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x3010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0xbf0f0fb, 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x2f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x8f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0xc040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0xf05050a, 0xb59a9a2f, 0x907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x0, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x6020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x4f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, 0xef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0xa06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac, 0x7f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x5030306, 0x1f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c, }; static uint32_t T1[256] = { 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x1010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x5050a0f, 0x9a9a2fb5, 0x7070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, 0xf9f9e910, 0x2020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0xc0c1814, 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0xb0b161d, 0xdbdbad76, 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0xa0a141e, 0x494992db, 0x6060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x8081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x3030605, 0xf6f6f701, 0xe0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, 0x8c8c038f, 0xa1a159f8, 0x89890980, 0xd0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, 0x2d2d5a77, 0xf0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a, }; static uint32_t T2[256] = { 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x1020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x4080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x50a0f05, 0x9a2fb59a, 0x70e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x9121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x0, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, 0xf9e910f9, 0x2040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0xc18140c, 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0xb161d0b, 0xdbad76db, 0xe0db3be0, 0x32645632, 0x3a744e3a, 0xa141e0a, 0x4992db49, 0x60c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x8101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x3060503, 0xf6f701f6, 0xe1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, 0x8c038f8c, 0xa159f8a1, 0x89098089, 0xd1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, 0x2d5a772d, 0xf1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16, }; static uint32_t T3[256] = { 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x2030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x80c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0xa0f0505, 0x2fb59a9a, 0xe090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x0, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, 0xe910f9f9, 0x4060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x58a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, 0x3bab9090, 0xb838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0xc0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x18c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, 0x61dcbdbd, 0xd868b8b, 0xf858a8a, 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x6050303, 0xf701f6f6, 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x7898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, 0x38f8c8c, 0x59f8a1a1, 0x9808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616, }; static uint32_t U0[256] = { 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x2752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, 0x728ebb2, 0x3c2b52f, 0x9a7bc586, 0xa50837d3, 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x69f715e, 0x51106ebd, 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x55dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x0, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0xfe75793, 0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, 0xaba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0xb0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0xd927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x97826cd, 0xf418596e, 0x1b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x8cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, 0xf7daec41, 0xe50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546, 0x4ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0xc25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0, }; static uint32_t U1[256] = { 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, 0x459d1ff1, 0x58faacab, 0x3e34b93, 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0xeea4598, 0xc0fe5de1, 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b, 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x24b72e2, 0x8f1fe357, 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x837d3a5, 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5, 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51, 0x8a213ef9, 0x6dd963d, 0x53eddae, 0xbde64d46, 0x8d5491b5, 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db, 0xa7ca147, 0xf427ce9, 0x1e84f8c9, 0x0, 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb, 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2, 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0xd090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x775af4c, 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3, 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef, 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0xbd49836, 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x99fead4, 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c, 0xca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x1a83971, 0xb30c08de, 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042, }; static uint32_t U2[256] = { 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, 0xcc889176, 0x2f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e, 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x8f9942b, 0x48705868, 0x458f19fd, 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, 0x2830f287, 0xbf23b2a5, 0x302ba6a, 0x16ed5c82, 0xcf8a2b1c, 0x79a792b4, 0x7f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x506d5be, 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110, 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, 0xc471055d, 0x6046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x0, 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0xefdfbff, 0x850f5638, 0xae3d1ed5, 0x2d362739, 0xf0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, 0xa0cb167, 0x57930fe7, 0xeeb4d296, 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x90e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60, 0x1f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330, 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90, 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312, 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409, 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x4f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0xbfb2e41, 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0xdff4195, 0xa8397101, 0xc08deb3, 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257, }; static uint32_t U3[256] = { 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x3e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58, 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, 0x30f28728, 0x23b2a5bf, 0x2ba6a03, 0xed5c8216, 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x6d5be05, 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x532e18a, 0xa475ebf6, 0xb39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e, 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, 0x71055dc4, 0x46fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x7888b89, 0xe7385b19, 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x0, 0x9838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e, 0xf563885, 0x3d1ed5ae, 0x3627392d, 0xa64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, 0xcb1670a, 0x930fe757, 0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0xe0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, 0xdec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f, 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, 0x467f5165, 0x9d04ea5e, 0x15d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x8deb30c, 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8, }; #else /* assume big endian */ static uint32_t T0[256] = { 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x2010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0xa05050f, 0x2f9a9ab5, 0xe070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x0, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x4020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x58f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0xb888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0xc06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x18d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0xd8b8b86, 0xf8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x6030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x78e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x38c8c8f, 0x59a1a1f8, 0x9898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, }; static uint32_t T1[256] = { 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0xdfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x3020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0xbfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x2f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x8f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0xc080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0xf0a0505, 0xb52f9a9a, 0x90e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x0, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x6040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x4f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0xefdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0xa0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x7f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x5060303, 0x1f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, }; static uint32_t T2[256] = { 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x1030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x40c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x50f0a05, 0x9ab52f9a, 0x7090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x91b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x0, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x2060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0xc14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0xb1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0xa1e140a, 0x49db9249, 0x60a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x8181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x3050603, 0xf601f7f6, 0xe121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0xd171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0xf111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, }; static uint32_t T3[256] = { 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x1010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x4040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x5050f0a, 0x9a9ab52f, 0x707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x9091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x0, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x2020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0xc0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0xb0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0xa0a1e14, 0x4949db92, 0x6060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x8081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x3030506, 0xf6f601f7, 0xe0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0xd0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0xf0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, }; static uint32_t U0[256] = { 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x38f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x2036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x58ae132, 0xa4f6eb75, 0xb83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x7898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x0, 0x9808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0xf853856, 0x3daed51e, 0x362d3927, 0xa0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0xc0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0xe090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0xd8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x18c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x80cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 }; static uint32_t U1[256] = { 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x2c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x7b2eb28, 0x32fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x65e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x0, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0xf9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0xae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0xb0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0xd507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x9cd2678, 0xf46e5918, 0x1ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0xe7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x49d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0xcbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 }; static uint32_t U2[256] = { 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x3934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0xe9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x2e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x8a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x63d96dd, 0x5aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0xa47a17c, 0xfe97c42, 0x1ec9f884, 0x0, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0xd0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x74caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0xb3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x9d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x4dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0xc7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x17139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 }; static uint32_t U3[256] = { 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x24c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x82b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x36aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x7f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x5bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x6d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x0, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0xefffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0xfd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0xa67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x90d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x1269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x4984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0xb412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0xd9541ff, 0xa8017139, 0xcb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 }; #endif /* * the following tables (aes_sbox, aes_inv_sbox, T4, U4) are * endian-neutral */ static uint8_t aes_sbox[256] = { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; #ifndef CPU_RISC static uint8_t aes_inv_sbox[256] = { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; #endif /* ! CPU_RISC */ #ifdef CPU_RISC static uint32_t T4[256] = { 0x63636363, 0x7c7c7c7c, 0x77777777, 0x7b7b7b7b, 0xf2f2f2f2, 0x6b6b6b6b, 0x6f6f6f6f, 0xc5c5c5c5, 0x30303030, 0x01010101, 0x67676767, 0x2b2b2b2b, 0xfefefefe, 0xd7d7d7d7, 0xabababab, 0x76767676, 0xcacacaca, 0x82828282, 0xc9c9c9c9, 0x7d7d7d7d, 0xfafafafa, 0x59595959, 0x47474747, 0xf0f0f0f0, 0xadadadad, 0xd4d4d4d4, 0xa2a2a2a2, 0xafafafaf, 0x9c9c9c9c, 0xa4a4a4a4, 0x72727272, 0xc0c0c0c0, 0xb7b7b7b7, 0xfdfdfdfd, 0x93939393, 0x26262626, 0x36363636, 0x3f3f3f3f, 0xf7f7f7f7, 0xcccccccc, 0x34343434, 0xa5a5a5a5, 0xe5e5e5e5, 0xf1f1f1f1, 0x71717171, 0xd8d8d8d8, 0x31313131, 0x15151515, 0x04040404, 0xc7c7c7c7, 0x23232323, 0xc3c3c3c3, 0x18181818, 0x96969696, 0x05050505, 0x9a9a9a9a, 0x07070707, 0x12121212, 0x80808080, 0xe2e2e2e2, 0xebebebeb, 0x27272727, 0xb2b2b2b2, 0x75757575, 0x09090909, 0x83838383, 0x2c2c2c2c, 0x1a1a1a1a, 0x1b1b1b1b, 0x6e6e6e6e, 0x5a5a5a5a, 0xa0a0a0a0, 0x52525252, 0x3b3b3b3b, 0xd6d6d6d6, 0xb3b3b3b3, 0x29292929, 0xe3e3e3e3, 0x2f2f2f2f, 0x84848484, 0x53535353, 0xd1d1d1d1, 0x00000000, 0xedededed, 0x20202020, 0xfcfcfcfc, 0xb1b1b1b1, 0x5b5b5b5b, 0x6a6a6a6a, 0xcbcbcbcb, 0xbebebebe, 0x39393939, 0x4a4a4a4a, 0x4c4c4c4c, 0x58585858, 0xcfcfcfcf, 0xd0d0d0d0, 0xefefefef, 0xaaaaaaaa, 0xfbfbfbfb, 0x43434343, 0x4d4d4d4d, 0x33333333, 0x85858585, 0x45454545, 0xf9f9f9f9, 0x02020202, 0x7f7f7f7f, 0x50505050, 0x3c3c3c3c, 0x9f9f9f9f, 0xa8a8a8a8, 0x51515151, 0xa3a3a3a3, 0x40404040, 0x8f8f8f8f, 0x92929292, 0x9d9d9d9d, 0x38383838, 0xf5f5f5f5, 0xbcbcbcbc, 0xb6b6b6b6, 0xdadadada, 0x21212121, 0x10101010, 0xffffffff, 0xf3f3f3f3, 0xd2d2d2d2, 0xcdcdcdcd, 0x0c0c0c0c, 0x13131313, 0xecececec, 0x5f5f5f5f, 0x97979797, 0x44444444, 0x17171717, 0xc4c4c4c4, 0xa7a7a7a7, 0x7e7e7e7e, 0x3d3d3d3d, 0x64646464, 0x5d5d5d5d, 0x19191919, 0x73737373, 0x60606060, 0x81818181, 0x4f4f4f4f, 0xdcdcdcdc, 0x22222222, 0x2a2a2a2a, 0x90909090, 0x88888888, 0x46464646, 0xeeeeeeee, 0xb8b8b8b8, 0x14141414, 0xdededede, 0x5e5e5e5e, 0x0b0b0b0b, 0xdbdbdbdb, 0xe0e0e0e0, 0x32323232, 0x3a3a3a3a, 0x0a0a0a0a, 0x49494949, 0x06060606, 0x24242424, 0x5c5c5c5c, 0xc2c2c2c2, 0xd3d3d3d3, 0xacacacac, 0x62626262, 0x91919191, 0x95959595, 0xe4e4e4e4, 0x79797979, 0xe7e7e7e7, 0xc8c8c8c8, 0x37373737, 0x6d6d6d6d, 0x8d8d8d8d, 0xd5d5d5d5, 0x4e4e4e4e, 0xa9a9a9a9, 0x6c6c6c6c, 0x56565656, 0xf4f4f4f4, 0xeaeaeaea, 0x65656565, 0x7a7a7a7a, 0xaeaeaeae, 0x08080808, 0xbabababa, 0x78787878, 0x25252525, 0x2e2e2e2e, 0x1c1c1c1c, 0xa6a6a6a6, 0xb4b4b4b4, 0xc6c6c6c6, 0xe8e8e8e8, 0xdddddddd, 0x74747474, 0x1f1f1f1f, 0x4b4b4b4b, 0xbdbdbdbd, 0x8b8b8b8b, 0x8a8a8a8a, 0x70707070, 0x3e3e3e3e, 0xb5b5b5b5, 0x66666666, 0x48484848, 0x03030303, 0xf6f6f6f6, 0x0e0e0e0e, 0x61616161, 0x35353535, 0x57575757, 0xb9b9b9b9, 0x86868686, 0xc1c1c1c1, 0x1d1d1d1d, 0x9e9e9e9e, 0xe1e1e1e1, 0xf8f8f8f8, 0x98989898, 0x11111111, 0x69696969, 0xd9d9d9d9, 0x8e8e8e8e, 0x94949494, 0x9b9b9b9b, 0x1e1e1e1e, 0x87878787, 0xe9e9e9e9, 0xcececece, 0x55555555, 0x28282828, 0xdfdfdfdf, 0x8c8c8c8c, 0xa1a1a1a1, 0x89898989, 0x0d0d0d0d, 0xbfbfbfbf, 0xe6e6e6e6, 0x42424242, 0x68686868, 0x41414141, 0x99999999, 0x2d2d2d2d, 0x0f0f0f0f, 0xb0b0b0b0, 0x54545454, 0xbbbbbbbb, 0x16161616 }; static uint32_t U4[256] = { 0x52525252, 0x9090909, 0x6a6a6a6a, 0xd5d5d5d5, 0x30303030, 0x36363636, 0xa5a5a5a5, 0x38383838, 0xbfbfbfbf, 0x40404040, 0xa3a3a3a3, 0x9e9e9e9e, 0x81818181, 0xf3f3f3f3, 0xd7d7d7d7, 0xfbfbfbfb, 0x7c7c7c7c, 0xe3e3e3e3, 0x39393939, 0x82828282, 0x9b9b9b9b, 0x2f2f2f2f, 0xffffffff, 0x87878787, 0x34343434, 0x8e8e8e8e, 0x43434343, 0x44444444, 0xc4c4c4c4, 0xdededede, 0xe9e9e9e9, 0xcbcbcbcb, 0x54545454, 0x7b7b7b7b, 0x94949494, 0x32323232, 0xa6a6a6a6, 0xc2c2c2c2, 0x23232323, 0x3d3d3d3d, 0xeeeeeeee, 0x4c4c4c4c, 0x95959595, 0xb0b0b0b, 0x42424242, 0xfafafafa, 0xc3c3c3c3, 0x4e4e4e4e, 0x8080808, 0x2e2e2e2e, 0xa1a1a1a1, 0x66666666, 0x28282828, 0xd9d9d9d9, 0x24242424, 0xb2b2b2b2, 0x76767676, 0x5b5b5b5b, 0xa2a2a2a2, 0x49494949, 0x6d6d6d6d, 0x8b8b8b8b, 0xd1d1d1d1, 0x25252525, 0x72727272, 0xf8f8f8f8, 0xf6f6f6f6, 0x64646464, 0x86868686, 0x68686868, 0x98989898, 0x16161616, 0xd4d4d4d4, 0xa4a4a4a4, 0x5c5c5c5c, 0xcccccccc, 0x5d5d5d5d, 0x65656565, 0xb6b6b6b6, 0x92929292, 0x6c6c6c6c, 0x70707070, 0x48484848, 0x50505050, 0xfdfdfdfd, 0xedededed, 0xb9b9b9b9, 0xdadadada, 0x5e5e5e5e, 0x15151515, 0x46464646, 0x57575757, 0xa7a7a7a7, 0x8d8d8d8d, 0x9d9d9d9d, 0x84848484, 0x90909090, 0xd8d8d8d8, 0xabababab, 0x0, 0x8c8c8c8c, 0xbcbcbcbc, 0xd3d3d3d3, 0xa0a0a0a, 0xf7f7f7f7, 0xe4e4e4e4, 0x58585858, 0x5050505, 0xb8b8b8b8, 0xb3b3b3b3, 0x45454545, 0x6060606, 0xd0d0d0d0, 0x2c2c2c2c, 0x1e1e1e1e, 0x8f8f8f8f, 0xcacacaca, 0x3f3f3f3f, 0xf0f0f0f, 0x2020202, 0xc1c1c1c1, 0xafafafaf, 0xbdbdbdbd, 0x3030303, 0x1010101, 0x13131313, 0x8a8a8a8a, 0x6b6b6b6b, 0x3a3a3a3a, 0x91919191, 0x11111111, 0x41414141, 0x4f4f4f4f, 0x67676767, 0xdcdcdcdc, 0xeaeaeaea, 0x97979797, 0xf2f2f2f2, 0xcfcfcfcf, 0xcececece, 0xf0f0f0f0, 0xb4b4b4b4, 0xe6e6e6e6, 0x73737373, 0x96969696, 0xacacacac, 0x74747474, 0x22222222, 0xe7e7e7e7, 0xadadadad, 0x35353535, 0x85858585, 0xe2e2e2e2, 0xf9f9f9f9, 0x37373737, 0xe8e8e8e8, 0x1c1c1c1c, 0x75757575, 0xdfdfdfdf, 0x6e6e6e6e, 0x47474747, 0xf1f1f1f1, 0x1a1a1a1a, 0x71717171, 0x1d1d1d1d, 0x29292929, 0xc5c5c5c5, 0x89898989, 0x6f6f6f6f, 0xb7b7b7b7, 0x62626262, 0xe0e0e0e, 0xaaaaaaaa, 0x18181818, 0xbebebebe, 0x1b1b1b1b, 0xfcfcfcfc, 0x56565656, 0x3e3e3e3e, 0x4b4b4b4b, 0xc6c6c6c6, 0xd2d2d2d2, 0x79797979, 0x20202020, 0x9a9a9a9a, 0xdbdbdbdb, 0xc0c0c0c0, 0xfefefefe, 0x78787878, 0xcdcdcdcd, 0x5a5a5a5a, 0xf4f4f4f4, 0x1f1f1f1f, 0xdddddddd, 0xa8a8a8a8, 0x33333333, 0x88888888, 0x7070707, 0xc7c7c7c7, 0x31313131, 0xb1b1b1b1, 0x12121212, 0x10101010, 0x59595959, 0x27272727, 0x80808080, 0xecececec, 0x5f5f5f5f, 0x60606060, 0x51515151, 0x7f7f7f7f, 0xa9a9a9a9, 0x19191919, 0xb5b5b5b5, 0x4a4a4a4a, 0xd0d0d0d, 0x2d2d2d2d, 0xe5e5e5e5, 0x7a7a7a7a, 0x9f9f9f9f, 0x93939393, 0xc9c9c9c9, 0x9c9c9c9c, 0xefefefef, 0xa0a0a0a0, 0xe0e0e0e0, 0x3b3b3b3b, 0x4d4d4d4d, 0xaeaeaeae, 0x2a2a2a2a, 0xf5f5f5f5, 0xb0b0b0b0, 0xc8c8c8c8, 0xebebebeb, 0xbbbbbbbb, 0x3c3c3c3c, 0x83838383, 0x53535353, 0x99999999, 0x61616161, 0x17171717, 0x2b2b2b2b, 0x4040404, 0x7e7e7e7e, 0xbabababa, 0x77777777, 0xd6d6d6d6, 0x26262626, 0xe1e1e1e1, 0x69696969, 0x14141414, 0x63636363, 0x55555555, 0x21212121, 0xc0c0c0c, 0x7d7d7d7d }; #endif /* CPU_RISC */ /* aes internals */ extern debug_module_t mod_aes_icm; void aes_expand_encryption_key(const v128_t *key, aes_expanded_key_t expanded_key) { int i; gf2_8 rc; /* initialize round constant */ rc = 1; expanded_key[0].v32[0] = key->v32[0]; expanded_key[0].v32[1] = key->v32[1]; expanded_key[0].v32[2] = key->v32[2]; expanded_key[0].v32[3] = key->v32[3]; #if 0 debug_print(mod_aes_icm, "expanded key[0]: %s", v128_hex_string(&expanded_key[0])); #endif /* loop over round keys */ for (i=1; i < 11; i++) { /* munge first word of round key */ expanded_key[i].v8[0] = aes_sbox[expanded_key[i-1].v8[13]] ^ rc; expanded_key[i].v8[1] = aes_sbox[expanded_key[i-1].v8[14]]; expanded_key[i].v8[2] = aes_sbox[expanded_key[i-1].v8[15]]; expanded_key[i].v8[3] = aes_sbox[expanded_key[i-1].v8[12]]; expanded_key[i].v32[0] ^= expanded_key[i-1].v32[0]; /* set remaining 32 bit words to the exor of the one previous with * the one four words previous */ expanded_key[i].v32[1] = expanded_key[i].v32[0] ^ expanded_key[i-1].v32[1]; expanded_key[i].v32[2] = expanded_key[i].v32[1] ^ expanded_key[i-1].v32[2]; expanded_key[i].v32[3] = expanded_key[i].v32[2] ^ expanded_key[i-1].v32[3]; #if 0 debug_print2(mod_aes_icm, "expanded key[%d]: %s", i,v128_hex_string(&expanded_key[i])); #endif /* modify round constant */ rc = gf2_8_shift(rc); } } void aes_expand_decryption_key(const v128_t *key, aes_expanded_key_t expanded_key) { int i; aes_expand_encryption_key(key, expanded_key); /* invert the order of the round keys */ for (i=0; i < 5; i++) { v128_t tmp; v128_copy(&tmp, &expanded_key[10-i]); v128_copy(&expanded_key[10-i], &expanded_key[i]); v128_copy(&expanded_key[i], &tmp); } /* * apply the inverse mixColumn transform to the round keys (except * for the first and the last) * * mixColumn is implemented by using the tables U0, U1, U2, U3, * followed by the T4 table (which cancels out the use of the sbox * in the U-tables) */ for (i=1; i < 10; i++) { #ifdef CPU_RISC uint32_t tmp; tmp = expanded_key[i].v32[0]; expanded_key[i].v32[0] = U0[T4[(tmp >> 24) ] & 0xff] ^ U1[T4[(tmp >> 16) & 0xff] & 0xff] ^ U2[T4[(tmp >> 8) & 0xff] & 0xff] ^ U3[T4[(tmp) & 0xff] & 0xff]; tmp = expanded_key[i].v32[1]; expanded_key[i].v32[1] = U0[T4[(tmp >> 24) ] & 0xff] ^ U1[T4[(tmp >> 16) & 0xff] & 0xff] ^ U2[T4[(tmp >> 8) & 0xff] & 0xff] ^ U3[T4[(tmp) & 0xff] & 0xff]; tmp = expanded_key[i].v32[2]; expanded_key[i].v32[2] = U0[T4[(tmp >> 24) ] & 0xff] ^ U1[T4[(tmp >> 16) & 0xff] & 0xff] ^ U2[T4[(tmp >> 8) & 0xff] & 0xff] ^ U3[T4[(tmp) & 0xff] & 0xff]; tmp = expanded_key[i].v32[3]; expanded_key[i].v32[3] = U0[T4[(tmp >> 24) ] & 0xff] ^ U1[T4[(tmp >> 16) & 0xff] & 0xff] ^ U2[T4[(tmp >> 8) & 0xff] & 0xff] ^ U3[T4[(tmp) & 0xff] & 0xff]; #else /* assume CPU_CISC */ uint32_t c0, c1, c2, c3; c0 = U0[aes_sbox[expanded_key[i].v8[0]]] ^ U1[aes_sbox[expanded_key[i].v8[1]]] ^ U2[aes_sbox[expanded_key[i].v8[2]]] ^ U3[aes_sbox[expanded_key[i].v8[3]]]; c1 = U0[aes_sbox[expanded_key[i].v8[4]]] ^ U1[aes_sbox[expanded_key[i].v8[5]]] ^ U2[aes_sbox[expanded_key[i].v8[6]]] ^ U3[aes_sbox[expanded_key[i].v8[7]]]; c2 = U0[aes_sbox[expanded_key[i].v8[8]]] ^ U1[aes_sbox[expanded_key[i].v8[9]]] ^ U2[aes_sbox[expanded_key[i].v8[10]]] ^ U3[aes_sbox[expanded_key[i].v8[11]]]; c3 = U0[aes_sbox[expanded_key[i].v8[12]]] ^ U1[aes_sbox[expanded_key[i].v8[13]]] ^ U2[aes_sbox[expanded_key[i].v8[14]]] ^ U3[aes_sbox[expanded_key[i].v8[15]]]; expanded_key[i].v32[0] = c0; expanded_key[i].v32[1] = c1; expanded_key[i].v32[2] = c2; expanded_key[i].v32[3] = c3; #endif } } #ifdef CPU_CISC static inline void aes_round(v128_t *state, const v128_t *round_key) { uint32_t column0, column1, column2, column3; /* compute the columns of the output square in terms of the octets of state, using the tables T0, T1, T2, T3 */ column0 = T0[state->v8[0]] ^ T1[state->v8[5]] ^ T2[state->v8[10]] ^ T3[state->v8[15]]; column1 = T0[state->v8[4]] ^ T1[state->v8[9]] ^ T2[state->v8[14]] ^ T3[state->v8[3]]; column2 = T0[state->v8[8]] ^ T1[state->v8[13]] ^ T2[state->v8[2]] ^ T3[state->v8[7]]; column3 = T0[state->v8[12]] ^ T1[state->v8[1]] ^ T2[state->v8[6]] ^ T3[state->v8[11]]; state->v32[0] = column0 ^ round_key->v32[0]; state->v32[1] = column1 ^ round_key->v32[1]; state->v32[2] = column2 ^ round_key->v32[2]; state->v32[3] = column3 ^ round_key->v32[3]; } static inline void aes_inv_round(v128_t *state, const v128_t *round_key) { uint32_t column0, column1, column2, column3; /* compute the columns of the output square in terms of the octets of state, using the tables U0, U1, U2, U3 */ column0 = U0[state->v8[0]] ^ U1[state->v8[13]] ^ U2[state->v8[10]] ^ U3[state->v8[7]]; column1 = U0[state->v8[4]] ^ U1[state->v8[1]] ^ U2[state->v8[14]] ^ U3[state->v8[11]]; column2 = U0[state->v8[8]] ^ U1[state->v8[5]] ^ U2[state->v8[2]] ^ U3[state->v8[15]]; column3 = U0[state->v8[12]] ^ U1[state->v8[9]] ^ U2[state->v8[6]] ^ U3[state->v8[3]]; state->v32[0] = column0 ^ round_key->v32[0]; state->v32[1] = column1 ^ round_key->v32[1]; state->v32[2] = column2 ^ round_key->v32[2]; state->v32[3] = column3 ^ round_key->v32[3]; } static inline void aes_final_round(v128_t *state, const v128_t *round_key) { uint8_t tmp; /* byte substitutions and row shifts */ /* first row - no shift */ state->v8[0] = aes_sbox[state->v8[0]]; state->v8[4] = aes_sbox[state->v8[4]]; state->v8[8] = aes_sbox[state->v8[8]]; state->v8[12] = aes_sbox[state->v8[12]]; /* second row - shift one left */ tmp = aes_sbox[state->v8[1]]; state->v8[1] = aes_sbox[state->v8[5]]; state->v8[5] = aes_sbox[state->v8[9]]; state->v8[9] = aes_sbox[state->v8[13]]; state->v8[13] = tmp; /* third row - shift two left */ tmp = aes_sbox[state->v8[10]]; state->v8[10] = aes_sbox[state->v8[2]]; state->v8[2] = tmp; tmp = aes_sbox[state->v8[14]]; state->v8[14] = aes_sbox[state->v8[6]]; state->v8[6] = tmp; /* fourth row - shift three left */ tmp = aes_sbox[state->v8[15]]; state->v8[15] = aes_sbox[state->v8[11]]; state->v8[11] = aes_sbox[state->v8[7]]; state->v8[7] = aes_sbox[state->v8[3]]; state->v8[3] = tmp; v128_xor_eq(state, round_key); } static inline void aes_inv_final_round(v128_t *state, const v128_t *round_key) { uint8_t tmp; /* byte substitutions and row shifts */ /* first row - no shift */ state->v8[0] = aes_inv_sbox[state->v8[0]]; state->v8[4] = aes_inv_sbox[state->v8[4]]; state->v8[8] = aes_inv_sbox[state->v8[8]]; state->v8[12] = aes_inv_sbox[state->v8[12]]; /* second row - shift one right */ tmp = aes_inv_sbox[state->v8[13]]; state->v8[13] = aes_inv_sbox[state->v8[9]]; state->v8[9] = aes_inv_sbox[state->v8[5]]; state->v8[5] = aes_inv_sbox[state->v8[1]]; state->v8[1] = tmp; /* third row - shift two right */ tmp = aes_inv_sbox[state->v8[2]]; state->v8[2] = aes_inv_sbox[state->v8[10]]; state->v8[10] = tmp; tmp = aes_inv_sbox[state->v8[6]]; state->v8[6] = aes_inv_sbox[state->v8[14]]; state->v8[14] = tmp; /* fourth row - shift three right */ tmp = aes_inv_sbox[state->v8[3]]; state->v8[3] = aes_inv_sbox[state->v8[7]]; state->v8[7] = aes_inv_sbox[state->v8[11]]; state->v8[11] = aes_inv_sbox[state->v8[15]]; state->v8[15] = tmp; v128_xor_eq(state, round_key); } #elif CPU_RISC static inline void aes_round(v128_t *state, const v128_t *round_key) { uint32_t column0, column1, column2, column3; /* compute the columns of the output square in terms of the octets of state, using the tables T0, T1, T2, T3 */ #ifdef WORDS_BIGENDIAN column0 = T0[state->v32[0] >> 24] ^ T1[(state->v32[1] >> 16) & 0xff] ^ T2[(state->v32[2] >> 8) & 0xff] ^ T3[state->v32[3] & 0xff]; column1 = T0[state->v32[1] >> 24] ^ T1[(state->v32[2] >> 16) & 0xff] ^ T2[(state->v32[3] >> 8) & 0xff] ^ T3[state->v32[0] & 0xff]; column2 = T0[state->v32[2] >> 24] ^ T1[(state->v32[3] >> 16) & 0xff] ^ T2[(state->v32[0] >> 8) & 0xff] ^ T3[state->v32[1] & 0xff]; column3 = T0[state->v32[3] >> 24] ^ T1[(state->v32[0] >> 16) & 0xff] ^ T2[(state->v32[1] >> 8) & 0xff] ^ T3[state->v32[2] & 0xff]; #else column0 = T0[state->v32[0] & 0xff] ^ T1[(state->v32[1] >> 8) & 0xff] ^ T2[(state->v32[2] >> 16) & 0xff] ^ T3[state->v32[3] >> 24]; column1 = T0[state->v32[1] & 0xff] ^ T1[(state->v32[2] >> 8) & 0xff] ^ T2[(state->v32[3] >> 16) & 0xff] ^ T3[state->v32[0] >> 24]; column2 = T0[state->v32[2] & 0xff] ^ T1[(state->v32[3] >> 8) & 0xff] ^ T2[(state->v32[0] >> 16) & 0xff] ^ T3[state->v32[1] >> 24]; column3 = T0[state->v32[3] & 0xff] ^ T1[(state->v32[0] >> 8) & 0xff] ^ T2[(state->v32[1] >> 16) & 0xff] ^ T3[state->v32[2] >> 24]; #endif /* WORDS_BIGENDIAN */ state->v32[0] = column0 ^ round_key->v32[0]; state->v32[1] = column1 ^ round_key->v32[1]; state->v32[2] = column2 ^ round_key->v32[2]; state->v32[3] = column3 ^ round_key->v32[3]; } static inline void aes_inv_round(v128_t *state, const v128_t *round_key) { uint32_t column0, column1, column2, column3; /* compute the columns of the output square in terms of the octets of state, using the tables U0, U1, U2, U3 */ #ifdef WORDS_BIGENDIAN /* FIX! WRong indexes */ column0 = U0[state->v32[0] >> 24] ^ U1[(state->v32[3] >> 16) & 0xff] ^ U2[(state->v32[2] >> 8) & 0xff] ^ U3[state->v32[1] & 0xff]; column1 = U0[state->v32[1] >> 24] ^ U1[(state->v32[0] >> 16) & 0xff] ^ U2[(state->v32[3] >> 8) & 0xff] ^ U3[state->v32[2] & 0xff]; column2 = U0[state->v32[2] >> 24] ^ U1[(state->v32[1] >> 16) & 0xff] ^ U2[(state->v32[0] >> 8) & 0xff] ^ U3[state->v32[3] & 0xff]; column3 = U0[state->v32[3] >> 24] ^ U1[(state->v32[2] >> 16) & 0xff] ^ U2[(state->v32[1] >> 8) & 0xff] ^ U3[state->v32[0] & 0xff]; #else column0 = U0[state->v32[0] & 0xff] ^ U1[(state->v32[1] >> 8) & 0xff] ^ U2[(state->v32[2] >> 16) & 0xff] ^ U3[state->v32[3] >> 24]; column1 = U0[state->v32[1] & 0xff] ^ U1[(state->v32[2] >> 8) & 0xff] ^ U2[(state->v32[3] >> 16) & 0xff] ^ U3[state->v32[0] >> 24]; column2 = U0[state->v32[2] & 0xff] ^ U1[(state->v32[3] >> 8) & 0xff] ^ U2[(state->v32[0] >> 16) & 0xff] ^ U3[state->v32[1] >> 24]; column3 = U0[state->v32[3] & 0xff] ^ U1[(state->v32[0] >> 8) & 0xff] ^ U2[(state->v32[1] >> 16) & 0xff] ^ U3[state->v32[2] >> 24]; #endif /* WORDS_BIGENDIAN */ state->v32[0] = column0 ^ round_key->v32[0]; state->v32[1] = column1 ^ round_key->v32[1]; state->v32[2] = column2 ^ round_key->v32[2]; state->v32[3] = column3 ^ round_key->v32[3]; } static inline void aes_final_round(v128_t *state, const v128_t *round_key) { uint32_t tmp0, tmp1, tmp2, tmp3; tmp0 = (T4[(state->v32[0] >> 24)] & 0xff000000) ^ (T4[(state->v32[1] >> 16) & 0xff] & 0x00ff0000) ^ (T4[(state->v32[2] >> 8) & 0xff] & 0x0000ff00) ^ (T4[(state->v32[3] ) & 0xff] & 0x000000ff) ^ round_key->v32[0]; tmp1 = (T4[(state->v32[1] >> 24)] & 0xff000000) ^ (T4[(state->v32[2] >> 16) & 0xff] & 0x00ff0000) ^ (T4[(state->v32[3] >> 8) & 0xff] & 0x0000ff00) ^ (T4[(state->v32[0] ) & 0xff] & 0x000000ff) ^ round_key->v32[1]; tmp2 = (T4[(state->v32[2] >> 24)] & 0xff000000) ^ (T4[(state->v32[3] >> 16) & 0xff] & 0x00ff0000) ^ (T4[(state->v32[0] >> 8) & 0xff] & 0x0000ff00) ^ (T4[(state->v32[1] ) & 0xff] & 0x000000ff) ^ round_key->v32[2]; tmp3 = (T4[(state->v32[3] >> 24)] & 0xff000000) ^ (T4[(state->v32[0] >> 16) & 0xff] & 0x00ff0000) ^ (T4[(state->v32[1] >> 8) & 0xff] & 0x0000ff00) ^ (T4[(state->v32[2] ) & 0xff] & 0x000000ff) ^ round_key->v32[3]; state->v32[0] = tmp0; state->v32[1] = tmp1; state->v32[2] = tmp2; state->v32[3] = tmp3; } static inline void aes_inv_final_round(v128_t *state, const v128_t *round_key) { uint32_t tmp0, tmp1, tmp2, tmp3; tmp0 = (U4[(state->v32[0] >> 24)] & 0xff000000) ^ (U4[(state->v32[3] >> 16) & 0xff] & 0x00ff0000) ^ (U4[(state->v32[2] >> 8) & 0xff] & 0x0000ff00) ^ (U4[(state->v32[1] ) & 0xff] & 0x000000ff) ^ round_key->v32[0]; tmp1 = (U4[(state->v32[1] >> 24)] & 0xff000000) ^ (U4[(state->v32[0] >> 16) & 0xff] & 0x00ff0000) ^ (U4[(state->v32[3] >> 8) & 0xff] & 0x0000ff00) ^ (U4[(state->v32[2] ) & 0xff] & 0x000000ff) ^ round_key->v32[1]; tmp2 = (U4[(state->v32[2] >> 24)] & 0xff000000) ^ (U4[(state->v32[1] >> 16) & 0xff] & 0x00ff0000) ^ (U4[(state->v32[0] >> 8) & 0xff] & 0x0000ff00) ^ (U4[(state->v32[3] ) & 0xff] & 0x000000ff) ^ round_key->v32[2]; tmp3 = (U4[(state->v32[3] >> 24)] & 0xff000000) ^ (U4[(state->v32[2] >> 16) & 0xff] & 0x00ff0000) ^ (U4[(state->v32[1] >> 8) & 0xff] & 0x0000ff00) ^ (U4[(state->v32[0] ) & 0xff] & 0x000000ff) ^ round_key->v32[3]; state->v32[0] = tmp0; state->v32[1] = tmp1; state->v32[2] = tmp2; state->v32[3] = tmp3; } #elif CPU_16 /* assume 16-bit word size on processor */ static inline void aes_round(v128_t *state, const v128_t *round_key) { uint32_t column0, column1, column2, column3; uint16_t c /* compute the columns of the output square in terms of the octets of state, using the tables T0, T1, T2, T3 */ column0 = T0[state->v8[0]] ^ T1[state->v8[5]] ^ T2[state->v8[10]] ^ T3[state->v8[15]]; column1 = T0[state->v8[4]] ^ T1[state->v8[9]] ^ T2[state->v8[14]] ^ T3[state->v8[3]]; column2 = T0[state->v8[8]] ^ T1[state->v8[13]] ^ T2[state->v8[2]] ^ T3[state->v8[7]]; column3 = T0[state->v8[12]] ^ T1[state->v8[1]] ^ T2[state->v8[6]] ^ T3[state->v8[11]]; state->v32[0] = column0 ^ round_key->v32[0]; state->v32[1] = column1 ^ round_key->v32[1]; state->v32[2] = column2 ^ round_key->v32[2]; state->v32[3] = column3 ^ round_key->v32[3]; } static inline void aes_inv_round(v128_t *state, const v128_t *round_key) { uint32_t column0, column1, column2, column3; /* compute the columns of the output square in terms of the octets of state, using the tables U0, U1, U2, U3 */ column0 = U0[state->v8[0]] ^ U1[state->v8[5]] ^ U2[state->v8[10]] ^ U3[state->v8[15]]; column1 = U0[state->v8[4]] ^ U1[state->v8[9]] ^ U2[state->v8[14]] ^ U3[state->v8[3]]; column2 = U0[state->v8[8]] ^ U1[state->v8[13]] ^ U2[state->v8[2]] ^ U3[state->v8[7]]; column3 = U0[state->v8[12]] ^ U1[state->v8[1]] ^ U2[state->v8[6]] ^ U3[state->v8[11]]; state->v32[0] = column0 ^ round_key->v32[0]; state->v32[1] = column1 ^ round_key->v32[1]; state->v32[2] = column2 ^ round_key->v32[2]; state->v32[3] = column3 ^ round_key->v32[3]; } static inline void aes_final_round(v128_t *state, const v128_t *round_key) { uint8_t tmp; /* byte substitutions and row shifts */ /* first row - no shift */ state->v8[0] = aes_sbox[state->v8[0]]; state->v8[4] = aes_sbox[state->v8[4]]; state->v8[8] = aes_sbox[state->v8[8]]; state->v8[12] = aes_sbox[state->v8[12]]; /* second row - shift one left */ tmp = aes_sbox[state->v8[1]]; state->v8[1] = aes_sbox[state->v8[5]]; state->v8[5] = aes_sbox[state->v8[9]]; state->v8[9] = aes_sbox[state->v8[13]]; state->v8[13] = tmp; /* third row - shift two left */ tmp = aes_sbox[state->v8[10]]; state->v8[10] = aes_sbox[state->v8[2]]; state->v8[2] = tmp; tmp = aes_sbox[state->v8[14]]; state->v8[14] = aes_sbox[state->v8[6]]; state->v8[6] = tmp; /* fourth row - shift three left */ tmp = aes_sbox[state->v8[15]]; state->v8[15] = aes_sbox[state->v8[11]]; state->v8[11] = aes_sbox[state->v8[7]]; state->v8[7] = aes_sbox[state->v8[3]]; state->v8[3] = tmp; v128_xor_eq(state, round_key); } static inline void aes_inv_final_round(v128_t *state, const v128_t *round_key) { uint8_t tmp; /* byte substitutions and row shifts */ /* first row - no shift */ state->v8[0] = aes_inv_sbox[state->v8[0]]; state->v8[4] = aes_inv_sbox[state->v8[4]]; state->v8[8] = aes_inv_sbox[state->v8[8]]; state->v8[12] = aes_inv_sbox[state->v8[12]]; /* second row - shift one left */ tmp = aes_inv_sbox[state->v8[1]]; state->v8[1] = aes_inv_sbox[state->v8[5]]; state->v8[5] = aes_inv_sbox[state->v8[9]]; state->v8[9] = aes_inv_sbox[state->v8[13]]; state->v8[13] = tmp; /* third row - shift two left */ tmp = aes_inv_sbox[state->v8[10]]; state->v8[10] = aes_inv_sbox[state->v8[2]]; state->v8[2] = tmp; tmp = aes_inv_sbox[state->v8[14]]; state->v8[14] = aes_inv_sbox[state->v8[6]]; state->v8[6] = tmp; /* fourth row - shift three left */ tmp = aes_inv_sbox[state->v8[15]]; state->v8[15] = aes_inv_sbox[state->v8[11]]; state->v8[11] = aes_inv_sbox[state->v8[7]]; state->v8[7] = aes_inv_sbox[state->v8[3]]; state->v8[3] = tmp; v128_xor_eq(state, round_key); } #endif /* CPU type */ void aes_encrypt(v128_t *plaintext, const aes_expanded_key_t exp_key) { /* add in the subkey */ v128_xor_eq(plaintext, exp_key + 0); /* now do nine rounds */ aes_round(plaintext, exp_key + 1); aes_round(plaintext, exp_key + 2); aes_round(plaintext, exp_key + 3); aes_round(plaintext, exp_key + 4); aes_round(plaintext, exp_key + 5); aes_round(plaintext, exp_key + 6); aes_round(plaintext, exp_key + 7); aes_round(plaintext, exp_key + 8); aes_round(plaintext, exp_key + 9); /* the last round is different */ aes_final_round(plaintext, exp_key + 10); } void aes_decrypt(v128_t *plaintext, const aes_expanded_key_t exp_key) { /* add in the subkey */ v128_xor_eq(plaintext, exp_key + 0); /* now do nine rounds */ aes_inv_round(plaintext, exp_key + 1); aes_inv_round(plaintext, exp_key + 2); aes_inv_round(plaintext, exp_key + 3); aes_inv_round(plaintext, exp_key + 4); aes_inv_round(plaintext, exp_key + 5); aes_inv_round(plaintext, exp_key + 6); aes_inv_round(plaintext, exp_key + 7); aes_inv_round(plaintext, exp_key + 8); aes_inv_round(plaintext, exp_key + 9); /* the last round is different */ aes_inv_final_round(plaintext, exp_key + 10); } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/cipher/aes_cbc.c ================================================ /* * aes_cbc.c * * AES Cipher Block Chaining Mode * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "aes_cbc.h" #include "alloc.h" debug_module_t mod_aes_cbc = { 0, /* debugging is off by default */ "aes cbc" /* printable module name */ }; err_status_t aes_cbc_alloc(cipher_t **c, int key_len) { extern cipher_type_t aes_cbc; uint8_t *pointer; int tmp; debug_print(mod_aes_cbc, "allocating cipher with key length %d", key_len); if (key_len != 16) return err_status_bad_param; /* allocate memory a cipher of type aes_icm */ tmp = (sizeof(aes_cbc_ctx_t) + sizeof(cipher_t)); pointer = (uint8_t*)crypto_alloc(tmp); if (pointer == NULL) return err_status_alloc_fail; /* set pointers */ *c = (cipher_t *)pointer; (*c)->type = &aes_cbc; (*c)->state = pointer + sizeof(cipher_t); /* increment ref_count */ aes_cbc.ref_count++; /* set key size */ (*c)->key_len = key_len; return err_status_ok; } err_status_t aes_cbc_dealloc(cipher_t *c) { extern cipher_type_t aes_cbc; /* zeroize entire state*/ octet_string_set_to_zero((uint8_t *)c, sizeof(aes_cbc_ctx_t) + sizeof(cipher_t)); /* free memory */ crypto_free(c); /* decrement ref_count */ aes_cbc.ref_count--; return err_status_ok; } err_status_t aes_cbc_context_init(aes_cbc_ctx_t *c, const uint8_t *key, cipher_direction_t dir) { v128_t tmp_key; /* set tmp_key (for alignment) */ v128_copy_octet_string(&tmp_key, key); debug_print(mod_aes_cbc, "key: %s", v128_hex_string(&tmp_key)); /* expand key for the appropriate direction */ switch (dir) { case (direction_encrypt): aes_expand_encryption_key(&tmp_key, c->expanded_key); break; case (direction_decrypt): aes_expand_decryption_key(&tmp_key, c->expanded_key); break; default: return err_status_bad_param; } return err_status_ok; } err_status_t aes_cbc_set_iv(aes_cbc_ctx_t *c, void *iv) { int i; /* v128_t *input = iv; */ uint8_t *input = (uint8_t*) iv; /* set state and 'previous' block to iv */ for (i=0; i < 16; i++) c->previous.v8[i] = c->state.v8[i] = input[i]; debug_print(mod_aes_cbc, "setting iv: %s", v128_hex_string(&c->state)); return err_status_ok; } err_status_t aes_cbc_encrypt(aes_cbc_ctx_t *c, unsigned char *data, unsigned int *bytes_in_data) { int i; unsigned char *input = data; /* pointer to data being read */ unsigned char *output = data; /* pointer to data being written */ int bytes_to_encr = *bytes_in_data; /* * verify that we're 16-octet aligned */ if (*bytes_in_data & 0xf) return err_status_bad_param; /* * note that we assume that the initialization vector has already * been set, e.g. by calling aes_cbc_set_iv() */ debug_print(mod_aes_cbc, "iv: %s", v128_hex_string(&c->state)); /* * loop over plaintext blocks, exoring state into plaintext then * encrypting and writing to output */ while (bytes_to_encr > 0) { /* exor plaintext into state */ for (i=0; i < 16; i++) c->state.v8[i] ^= *input++; debug_print(mod_aes_cbc, "inblock: %s", v128_hex_string(&c->state)); aes_encrypt(&c->state, c->expanded_key); debug_print(mod_aes_cbc, "outblock: %s", v128_hex_string(&c->state)); /* copy ciphertext to output */ for (i=0; i < 16; i++) *output++ = c->state.v8[i]; bytes_to_encr -= 16; } return err_status_ok; } err_status_t aes_cbc_decrypt(aes_cbc_ctx_t *c, unsigned char *data, unsigned int *bytes_in_data) { int i; v128_t state, previous; unsigned char *input = data; /* pointer to data being read */ unsigned char *output = data; /* pointer to data being written */ int bytes_to_encr = *bytes_in_data; uint8_t tmp; /* * verify that we're 16-octet aligned */ if (*bytes_in_data & 0x0f) return err_status_bad_param; /* set 'previous' block to iv*/ for (i=0; i < 16; i++) { previous.v8[i] = c->previous.v8[i]; } debug_print(mod_aes_cbc, "iv: %s", v128_hex_string(&previous)); /* * loop over ciphertext blocks, decrypting then exoring with state * then writing plaintext to output */ while (bytes_to_encr > 0) { /* set state to ciphertext input block */ for (i=0; i < 16; i++) { state.v8[i] = *input++; } debug_print(mod_aes_cbc, "inblock: %s", v128_hex_string(&state)); /* decrypt state */ aes_decrypt(&state, c->expanded_key); debug_print(mod_aes_cbc, "outblock: %s", v128_hex_string(&state)); /* * exor previous ciphertext block out of plaintext, and write new * plaintext block to output, while copying old ciphertext block * to the 'previous' block */ for (i=0; i < 16; i++) { tmp = *output; *output++ = state.v8[i] ^ previous.v8[i]; previous.v8[i] = tmp; } bytes_to_encr -= 16; } return err_status_ok; } err_status_t aes_cbc_nist_encrypt(aes_cbc_ctx_t *c, unsigned char *data, unsigned int *bytes_in_data) { int i; unsigned char *pad_start; int num_pad_bytes; err_status_t status; /* * determine the number of padding bytes that we need to add - * this value is always between 1 and 16, inclusive. */ num_pad_bytes = 16 - (*bytes_in_data & 0xf); pad_start = data; pad_start += *bytes_in_data; *pad_start++ = 0xa0; for (i=0; i < num_pad_bytes; i++) *pad_start++ = 0x00; /* * increment the data size */ *bytes_in_data += num_pad_bytes; /* * now cbc encrypt the padded data */ status = aes_cbc_encrypt(c, data, bytes_in_data); if (status) return status; return err_status_ok; } err_status_t aes_cbc_nist_decrypt(aes_cbc_ctx_t *c, unsigned char *data, unsigned int *bytes_in_data) { unsigned char *pad_end; int num_pad_bytes; err_status_t status; /* * cbc decrypt the padded data */ status = aes_cbc_decrypt(c, data, bytes_in_data); if (status) return status; /* * determine the number of padding bytes in the decrypted plaintext * - this value is always between 1 and 16, inclusive. */ num_pad_bytes = 1; pad_end = data + (*bytes_in_data - 1); while (*pad_end != 0xa0) { /* note: should check padding correctness */ pad_end--; num_pad_bytes++; } /* decrement data size */ *bytes_in_data -= num_pad_bytes; return err_status_ok; } char aes_cbc_description[] = "aes cipher block chaining (cbc) mode"; /* * Test case 0 is derived from FIPS 197 Appendix A; it uses an * all-zero IV, so that the first block encryption matches the test * case in that appendix. This property provides a check of the base * AES encryption and decryption algorithms; if CBC fails on some * particular platform, then you should print out AES intermediate * data and compare with the detailed info provided in that appendix. * */ uint8_t aes_cbc_test_case_0_key[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; uint8_t aes_cbc_test_case_0_plaintext[64] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; uint8_t aes_cbc_test_case_0_ciphertext[80] = { 0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30, 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a, 0x03, 0x35, 0xed, 0x27, 0x67, 0xf2, 0x6d, 0xf1, 0x64, 0x83, 0x2e, 0x23, 0x44, 0x38, 0x70, 0x8b }; uint8_t aes_cbc_test_case_0_iv[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; cipher_test_case_t aes_cbc_test_case_0 = { 16, /* octets in key */ aes_cbc_test_case_0_key, /* key */ aes_cbc_test_case_0_iv, /* initialization vector */ 16, /* octets in plaintext */ aes_cbc_test_case_0_plaintext, /* plaintext */ 32, /* octets in ciphertext */ aes_cbc_test_case_0_ciphertext, /* ciphertext */ NULL /* pointer to next testcase */ }; /* * this test case is taken directly from Appendix F.2 of NIST Special * Publication SP 800-38A */ uint8_t aes_cbc_test_case_1_key[16] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, }; uint8_t aes_cbc_test_case_1_plaintext[64] = { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 }; uint8_t aes_cbc_test_case_1_ciphertext[80] = { 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7, 0x39, 0x34, 0x07, 0x03, 0x36, 0xd0, 0x77, 0x99, 0xe0, 0xc4, 0x2f, 0xdd, 0xa8, 0xdf, 0x4c, 0xa3 }; uint8_t aes_cbc_test_case_1_iv[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; cipher_test_case_t aes_cbc_test_case_1 = { 16, /* octets in key */ aes_cbc_test_case_1_key, /* key */ aes_cbc_test_case_1_iv, /* initialization vector */ 64, /* octets in plaintext */ aes_cbc_test_case_1_plaintext, /* plaintext */ 80, /* octets in ciphertext */ aes_cbc_test_case_1_ciphertext, /* ciphertext */ &aes_cbc_test_case_0 /* pointer to next testcase */ }; cipher_type_t aes_cbc = { (cipher_alloc_func_t) aes_cbc_alloc, (cipher_dealloc_func_t) aes_cbc_dealloc, (cipher_init_func_t) aes_cbc_context_init, (cipher_encrypt_func_t) aes_cbc_nist_encrypt, (cipher_decrypt_func_t) aes_cbc_nist_decrypt, (cipher_set_iv_func_t) aes_cbc_set_iv, (char *) aes_cbc_description, (int) 0, /* instance count */ (cipher_test_case_t *) &aes_cbc_test_case_0, (debug_module_t *) &mod_aes_cbc }; ================================================ FILE: deps/pjsip/third_party/srtp/crypto/cipher/aes_icm.c ================================================ /* * aes_icm.c * * AES Integer Counter Mode * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #define ALIGN_32 0 #include "aes_icm.h" #include "alloc.h" debug_module_t mod_aes_icm = { 0, /* debugging is off by default */ "aes icm" /* printable module name */ }; /* * integer counter mode works as follows: * * 16 bits * <-----> * +------+------+------+------+------+------+------+------+ * | nonce | pakcet index | ctr |---+ * +------+------+------+------+------+------+------+------+ | * | * +------+------+------+------+------+------+------+------+ v * | salt |000000|->(+) * +------+------+------+------+------+------+------+------+ | * | * +---------+ * | encrypt | * +---------+ * | * +------+------+------+------+------+------+------+------+ | * | keystream block |<--+ * +------+------+------+------+------+------+------+------+ * * All fields are big-endian * * ctr is the block counter, which increments from zero for * each packet (16 bits wide) * * packet index is distinct for each packet (48 bits wide) * * nonce can be distinct across many uses of the same key, or * can be a fixed value per key, or can be per-packet randomness * (64 bits) * */ err_status_t aes_icm_alloc_ismacryp(cipher_t **c, int key_len, int forIsmacryp) { extern cipher_type_t aes_icm; uint8_t *pointer; int tmp; debug_print(mod_aes_icm, "allocating cipher with key length %d", key_len); /* * Ismacryp, for example, uses 16 byte key + 8 byte * salt so this function is called with key_len = 24. * The check for key_len = 30 does not apply. Our usage * of aes functions with key_len = values other than 30 * has not broken anything. Don't know what would be the * effect of skipping this check for srtp in general. */ if (!forIsmacryp && key_len != 30) return err_status_bad_param; /* allocate memory a cipher of type aes_icm */ tmp = (sizeof(aes_icm_ctx_t) + sizeof(cipher_t)); pointer = (uint8_t*)crypto_alloc(tmp); if (pointer == NULL) return err_status_alloc_fail; /* set pointers */ *c = (cipher_t *)pointer; (*c)->type = &aes_icm; (*c)->state = pointer + sizeof(cipher_t); /* increment ref_count */ aes_icm.ref_count++; /* set key size */ (*c)->key_len = key_len; return err_status_ok; } err_status_t aes_icm_alloc(cipher_t **c, int key_len, int forIsmacryp) { return aes_icm_alloc_ismacryp(c, key_len, 0); } err_status_t aes_icm_dealloc(cipher_t *c) { extern cipher_type_t aes_icm; /* zeroize entire state*/ octet_string_set_to_zero((uint8_t *)c, sizeof(aes_icm_ctx_t) + sizeof(cipher_t)); /* free memory */ crypto_free(c); /* decrement ref_count */ aes_icm.ref_count--; return err_status_ok; } /* * aes_icm_context_init(...) initializes the aes_icm_context * using the value in key[]. * * the key is the secret key * * the salt is unpredictable (but not necessarily secret) data which * randomizes the starting point in the keystream */ err_status_t aes_icm_context_init(aes_icm_ctx_t *c, const uint8_t *key) { v128_t tmp_key; /* set counter and initial values to 'offset' value */ /* FIX!!! this assumes the salt is at key + 16, and thus that the */ /* FIX!!! cipher key length is 16! Also note this copies past the end of the 'key' array by 2 bytes! */ v128_copy_octet_string(&c->counter, key + 16); v128_copy_octet_string(&c->offset, key + 16); /* force last two octets of the offset to zero (for srtp compatibility) */ c->offset.v8[14] = c->offset.v8[15] = 0; c->counter.v8[14] = c->counter.v8[15] = 0; /* set tmp_key (for alignment) */ v128_copy_octet_string(&tmp_key, key); debug_print(mod_aes_icm, "key: %s", v128_hex_string(&tmp_key)); debug_print(mod_aes_icm, "offset: %s", v128_hex_string(&c->offset)); /* expand key */ aes_expand_encryption_key(&tmp_key, c->expanded_key); /* indicate that the keystream_buffer is empty */ c->bytes_in_buffer = 0; return err_status_ok; } /* * aes_icm_set_octet(c, i) sets the counter of the context which it is * passed so that the next octet of keystream that will be generated * is the ith octet */ err_status_t aes_icm_set_octet(aes_icm_ctx_t *c, uint64_t octet_num) { #ifdef NO_64BIT_MATH int tail_num = low32(octet_num) & 0x0f; /* 64-bit right-shift 4 */ uint64_t block_num = make64(high32(octet_num) >> 4, ((high32(octet_num) & 0x0f)<<(32-4)) | (low32(octet_num) >> 4)); #else int tail_num = octet_num % 16; uint64_t block_num = octet_num / 16; #endif /* set counter value */ /* FIX - There's no way this is correct */ c->counter.v64[0] = c->offset.v64[0]; #ifdef NO_64BIT_MATH c->counter.v64[0] = make64(high32(c->offset.v64[0]) ^ high32(block_num), low32(c->offset.v64[0]) ^ low32(block_num)); #else c->counter.v64[0] = c->offset.v64[0] ^ block_num; #endif debug_print(mod_aes_icm, "set_octet: %s", v128_hex_string(&c->counter)); /* fill keystream buffer, if needed */ if (tail_num) { v128_copy(&c->keystream_buffer, &c->counter); aes_encrypt(&c->keystream_buffer, c->expanded_key); c->bytes_in_buffer = sizeof(v128_t); debug_print(mod_aes_icm, "counter: %s", v128_hex_string(&c->counter)); debug_print(mod_aes_icm, "ciphertext: %s", v128_hex_string(&c->keystream_buffer)); /* indicate number of bytes in keystream_buffer */ c->bytes_in_buffer = sizeof(v128_t) - tail_num; } else { /* indicate that keystream_buffer is empty */ c->bytes_in_buffer = 0; } return err_status_ok; } /* * aes_icm_set_iv(c, iv) sets the counter value to the exor of iv with * the offset */ err_status_t aes_icm_set_iv(aes_icm_ctx_t *c, void *iv) { v128_t *nonce = (v128_t *) iv; debug_print(mod_aes_icm, "setting iv: %s", v128_hex_string(nonce)); v128_xor(&c->counter, &c->offset, nonce); debug_print(mod_aes_icm, "set_counter: %s", v128_hex_string(&c->counter)); /* indicate that the keystream_buffer is empty */ c->bytes_in_buffer = 0; return err_status_ok; } /* * aes_icm_advance(...) refills the keystream_buffer and * advances the block index of the sicm_context forward by one * * this is an internal, hopefully inlined function */ static inline void aes_icm_advance_ismacryp(aes_icm_ctx_t *c, uint8_t forIsmacryp) { /* fill buffer with new keystream */ v128_copy(&c->keystream_buffer, &c->counter); aes_encrypt(&c->keystream_buffer, c->expanded_key); c->bytes_in_buffer = sizeof(v128_t); debug_print(mod_aes_icm, "counter: %s", v128_hex_string(&c->counter)); debug_print(mod_aes_icm, "ciphertext: %s", v128_hex_string(&c->keystream_buffer)); /* clock counter forward */ if (forIsmacryp) { uint32_t temp; //alex's clock counter forward temp = ntohl(c->counter.v32[3]); c->counter.v32[3] = htonl(++temp); } else { if (!++(c->counter.v8[15])) ++(c->counter.v8[14]); } } inline void aes_icm_advance(aes_icm_ctx_t *c) { aes_icm_advance_ismacryp(c, 0); } /*e * icm_encrypt deals with the following cases: * * bytes_to_encr < bytes_in_buffer * - add keystream into data * * bytes_to_encr > bytes_in_buffer * - add keystream into data until keystream_buffer is depleted * - loop over blocks, filling keystream_buffer and then * adding keystream into data * - fill buffer then add in remaining (< 16) bytes of keystream */ err_status_t aes_icm_encrypt_ismacryp(aes_icm_ctx_t *c, unsigned char *buf, unsigned int *enc_len, int forIsmacryp) { unsigned int bytes_to_encr = *enc_len; unsigned int i; uint32_t *b; /* check that there's enough segment left but not for ismacryp*/ if (!forIsmacryp && (bytes_to_encr + htons(c->counter.v16[7])) > 0xffff) return err_status_terminus; debug_print(mod_aes_icm, "block index: %d", htons(c->counter.v16[7])); if (bytes_to_encr <= (unsigned int)c->bytes_in_buffer) { /* deal with odd case of small bytes_to_encr */ for (i = (sizeof(v128_t) - c->bytes_in_buffer); i < (sizeof(v128_t) - c->bytes_in_buffer + bytes_to_encr); i++) { *buf++ ^= c->keystream_buffer.v8[i]; } c->bytes_in_buffer -= bytes_to_encr; /* return now to avoid the main loop */ return err_status_ok; } else { /* encrypt bytes until the remaining data is 16-byte aligned */ for (i=(sizeof(v128_t) - c->bytes_in_buffer); i < sizeof(v128_t); i++) *buf++ ^= c->keystream_buffer.v8[i]; bytes_to_encr -= c->bytes_in_buffer; c->bytes_in_buffer = 0; } /* now loop over entire 16-byte blocks of keystream */ for (i=0; i < (bytes_to_encr/sizeof(v128_t)); i++) { /* fill buffer with new keystream */ aes_icm_advance_ismacryp(c, forIsmacryp); /* * add keystream into the data buffer (this would be a lot faster * if we could assume 32-bit alignment!) */ #if ALIGN_32 b = (uint32_t *)buf; *b++ ^= c->keystream_buffer.v32[0]; *b++ ^= c->keystream_buffer.v32[1]; *b++ ^= c->keystream_buffer.v32[2]; *b++ ^= c->keystream_buffer.v32[3]; buf = (uint8_t *)b; #else if ((((unsigned long) buf) & 0x03) != 0) { *buf++ ^= c->keystream_buffer.v8[0]; *buf++ ^= c->keystream_buffer.v8[1]; *buf++ ^= c->keystream_buffer.v8[2]; *buf++ ^= c->keystream_buffer.v8[3]; *buf++ ^= c->keystream_buffer.v8[4]; *buf++ ^= c->keystream_buffer.v8[5]; *buf++ ^= c->keystream_buffer.v8[6]; *buf++ ^= c->keystream_buffer.v8[7]; *buf++ ^= c->keystream_buffer.v8[8]; *buf++ ^= c->keystream_buffer.v8[9]; *buf++ ^= c->keystream_buffer.v8[10]; *buf++ ^= c->keystream_buffer.v8[11]; *buf++ ^= c->keystream_buffer.v8[12]; *buf++ ^= c->keystream_buffer.v8[13]; *buf++ ^= c->keystream_buffer.v8[14]; *buf++ ^= c->keystream_buffer.v8[15]; } else { b = (uint32_t *)buf; *b++ ^= c->keystream_buffer.v32[0]; *b++ ^= c->keystream_buffer.v32[1]; *b++ ^= c->keystream_buffer.v32[2]; *b++ ^= c->keystream_buffer.v32[3]; buf = (uint8_t *)b; } #endif /* #if ALIGN_32 */ } /* if there is a tail end of the data, process it */ if ((bytes_to_encr & 0xf) != 0) { /* fill buffer with new keystream */ aes_icm_advance_ismacryp(c, forIsmacryp); for (i=0; i < (bytes_to_encr & 0xf); i++) *buf++ ^= c->keystream_buffer.v8[i]; /* reset the keystream buffer size to right value */ c->bytes_in_buffer = sizeof(v128_t) - i; } else { /* no tail, so just reset the keystream buffer size to zero */ c->bytes_in_buffer = 0; } return err_status_ok; } err_status_t aes_icm_encrypt(aes_icm_ctx_t *c, unsigned char *buf, unsigned int *enc_len) { return aes_icm_encrypt_ismacryp(c, buf, enc_len, 0); } err_status_t aes_icm_output(aes_icm_ctx_t *c, uint8_t *buffer, int num_octets_to_output) { unsigned int len = num_octets_to_output; /* zeroize the buffer */ octet_string_set_to_zero(buffer, num_octets_to_output); /* exor keystream into buffer */ return aes_icm_encrypt(c, buffer, &len); } char aes_icm_description[] = "aes integer counter mode"; uint8_t aes_icm_test_case_0_key[30] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd }; uint8_t aes_icm_test_case_0_nonce[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t aes_icm_test_case_0_plaintext[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; uint8_t aes_icm_test_case_0_ciphertext[32] = { 0xe0, 0x3e, 0xad, 0x09, 0x35, 0xc9, 0x5e, 0x80, 0xe1, 0x66, 0xb1, 0x6d, 0xd9, 0x2b, 0x4e, 0xb4, 0xd2, 0x35, 0x13, 0x16, 0x2b, 0x02, 0xd0, 0xf7, 0x2a, 0x43, 0xa2, 0xfe, 0x4a, 0x5f, 0x97, 0xab }; cipher_test_case_t aes_icm_test_case_0 = { 30, /* octets in key */ aes_icm_test_case_0_key, /* key */ aes_icm_test_case_0_nonce, /* packet index */ 32, /* octets in plaintext */ aes_icm_test_case_0_plaintext, /* plaintext */ 32, /* octets in ciphertext */ aes_icm_test_case_0_ciphertext, /* ciphertext */ NULL /* pointer to next testcase */ }; /* * note: the encrypt function is identical to the decrypt function */ cipher_type_t aes_icm = { (cipher_alloc_func_t) aes_icm_alloc, (cipher_dealloc_func_t) aes_icm_dealloc, (cipher_init_func_t) aes_icm_context_init, (cipher_encrypt_func_t) aes_icm_encrypt, (cipher_decrypt_func_t) aes_icm_encrypt, (cipher_set_iv_func_t) aes_icm_set_iv, (char *) aes_icm_description, (int) 0, /* instance count */ (cipher_test_case_t *) &aes_icm_test_case_0, (debug_module_t *) &mod_aes_icm }; ================================================ FILE: deps/pjsip/third_party/srtp/crypto/cipher/cipher.c ================================================ /* * cipher.c * * cipher meta-functions * * David A. McGrew * Cisco Systems, Inc. * */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "cipher.h" #include "rand_source.h" /* used in invertibiltiy tests */ #include "alloc.h" /* for crypto_alloc(), crypto_free() */ debug_module_t mod_cipher = { 0, /* debugging is off by default */ "cipher" /* printable module name */ }; err_status_t cipher_output(cipher_t *c, uint8_t *buffer, int num_octets_to_output) { /* zeroize the buffer */ octet_string_set_to_zero(buffer, num_octets_to_output); /* exor keystream into buffer */ return cipher_encrypt(c, buffer, (unsigned int *) &num_octets_to_output); } /* some bookkeeping functions */ int cipher_get_key_length(const cipher_t *c) { return c->key_len; } /* * cipher_type_self_test(ct) tests a cipher of type ct against test cases * provided in an array of values of key, salt, xtd_seq_num_t, * plaintext, and ciphertext that is known to be good */ #define SELF_TEST_BUF_OCTETS 128 #define NUM_RAND_TESTS 128 #define MAX_KEY_LEN 64 err_status_t cipher_type_self_test(const cipher_type_t *ct) { const cipher_test_case_t *test_case = ct->test_data; cipher_t *c; err_status_t status; uint8_t buffer[SELF_TEST_BUF_OCTETS]; uint8_t buffer2[SELF_TEST_BUF_OCTETS]; unsigned int len; int i, j, case_num = 0; debug_print(mod_cipher, "running self-test for cipher %s", ct->description); /* * check to make sure that we have at least one test case, and * return an error if we don't - we need to be paranoid here */ if (test_case == NULL) return err_status_cant_check; /* * loop over all test cases, perform known-answer tests of both the * encryption and decryption functions */ while (test_case != NULL) { /* allocate cipher */ status = cipher_type_alloc(ct, &c, test_case->key_length_octets); if (status) return status; /* * test the encrypt function */ debug_print(mod_cipher, "testing encryption", NULL); /* initialize cipher */ status = cipher_init(c, test_case->key, direction_encrypt); if (status) { cipher_dealloc(c); return status; } /* copy plaintext into test buffer */ if (test_case->ciphertext_length_octets > SELF_TEST_BUF_OCTETS) { cipher_dealloc(c); return err_status_bad_param; } for (i=0; i < test_case->plaintext_length_octets; i++) buffer[i] = test_case->plaintext[i]; debug_print(mod_cipher, "plaintext: %s", octet_string_hex_string(buffer, test_case->plaintext_length_octets)); /* set the initialization vector */ status = cipher_set_iv(c, test_case->idx); if (status) { cipher_dealloc(c); return status; } /* encrypt */ len = test_case->plaintext_length_octets; status = cipher_encrypt(c, buffer, &len); if (status) { cipher_dealloc(c); return status; } debug_print(mod_cipher, "ciphertext: %s", octet_string_hex_string(buffer, test_case->ciphertext_length_octets)); /* compare the resulting ciphertext with that in the test case */ if (len != test_case->ciphertext_length_octets) return err_status_algo_fail; status = err_status_ok; for (i=0; i < test_case->ciphertext_length_octets; i++) if (buffer[i] != test_case->ciphertext[i]) { status = err_status_algo_fail; debug_print(mod_cipher, "test case %d failed", case_num); debug_print(mod_cipher, "(failure at byte %d)", i); break; } if (status) { debug_print(mod_cipher, "c computed: %s", octet_string_hex_string(buffer, 2*test_case->plaintext_length_octets)); debug_print(mod_cipher, "c expected: %s", octet_string_hex_string(test_case->ciphertext, 2*test_case->plaintext_length_octets)); cipher_dealloc(c); return err_status_algo_fail; } /* * test the decrypt function */ debug_print(mod_cipher, "testing decryption", NULL); /* re-initialize cipher for decryption */ status = cipher_init(c, test_case->key, direction_decrypt); if (status) { cipher_dealloc(c); return status; } /* copy ciphertext into test buffer */ if (test_case->ciphertext_length_octets > SELF_TEST_BUF_OCTETS) { cipher_dealloc(c); return err_status_bad_param; } for (i=0; i < test_case->ciphertext_length_octets; i++) buffer[i] = test_case->ciphertext[i]; debug_print(mod_cipher, "ciphertext: %s", octet_string_hex_string(buffer, test_case->plaintext_length_octets)); /* set the initialization vector */ status = cipher_set_iv(c, test_case->idx); if (status) { cipher_dealloc(c); return status; } /* decrypt */ len = test_case->ciphertext_length_octets; status = cipher_decrypt(c, buffer, &len); if (status) { cipher_dealloc(c); return status; } debug_print(mod_cipher, "plaintext: %s", octet_string_hex_string(buffer, test_case->plaintext_length_octets)); /* compare the resulting plaintext with that in the test case */ if (len != test_case->plaintext_length_octets) return err_status_algo_fail; status = err_status_ok; for (i=0; i < test_case->plaintext_length_octets; i++) if (buffer[i] != test_case->plaintext[i]) { status = err_status_algo_fail; debug_print(mod_cipher, "test case %d failed", case_num); debug_print(mod_cipher, "(failure at byte %d)", i); } if (status) { debug_print(mod_cipher, "p computed: %s", octet_string_hex_string(buffer, 2*test_case->plaintext_length_octets)); debug_print(mod_cipher, "p expected: %s", octet_string_hex_string(test_case->plaintext, 2*test_case->plaintext_length_octets)); cipher_dealloc(c); return err_status_algo_fail; } /* deallocate the cipher */ status = cipher_dealloc(c); if (status) return status; /* * the cipher passed the test case, so move on to the next test * case in the list; if NULL, we'l proceed to the next test */ test_case = test_case->next_test_case; ++case_num; } /* now run some random invertibility tests */ /* allocate cipher, using paramaters from the first test case */ test_case = ct->test_data; status = cipher_type_alloc(ct, &c, test_case->key_length_octets); if (status) return status; rand_source_init(); for (j=0; j < NUM_RAND_TESTS; j++) { unsigned length; unsigned plaintext_len; uint8_t key[MAX_KEY_LEN]; uint8_t iv[MAX_KEY_LEN]; /* choose a length at random (leaving room for IV and padding) */ length = rand() % (SELF_TEST_BUF_OCTETS - 64); debug_print(mod_cipher, "random plaintext length %d\n", length); status = rand_source_get_octet_string(buffer, length); if (status) return status; debug_print(mod_cipher, "plaintext: %s", octet_string_hex_string(buffer, length)); /* copy plaintext into second buffer */ for (i=0; (unsigned int)i < length; i++) buffer2[i] = buffer[i]; /* choose a key at random */ if (test_case->key_length_octets > MAX_KEY_LEN) return err_status_cant_check; status = rand_source_get_octet_string(key, test_case->key_length_octets); if (status) return status; /* chose a random initialization vector */ status = rand_source_get_octet_string(iv, MAX_KEY_LEN); if (status) return status; /* initialize cipher */ status = cipher_init(c, key, direction_encrypt); if (status) { cipher_dealloc(c); return status; } /* set initialization vector */ status = cipher_set_iv(c, test_case->idx); if (status) { cipher_dealloc(c); return status; } /* encrypt buffer with cipher */ plaintext_len = length; status = cipher_encrypt(c, buffer, &length); if (status) { cipher_dealloc(c); return status; } debug_print(mod_cipher, "ciphertext: %s", octet_string_hex_string(buffer, length)); /* * re-initialize cipher for decryption, re-set the iv, then * decrypt the ciphertext */ status = cipher_init(c, key, direction_decrypt); if (status) { cipher_dealloc(c); return status; } status = cipher_set_iv(c, test_case->idx); if (status) { cipher_dealloc(c); return status; } status = cipher_decrypt(c, buffer, &length); if (status) { cipher_dealloc(c); return status; } debug_print(mod_cipher, "plaintext[2]: %s", octet_string_hex_string(buffer, length)); /* compare the resulting plaintext with the original one */ if (length != plaintext_len) return err_status_algo_fail; status = err_status_ok; for (i=0; i < plaintext_len; i++) if (buffer[i] != buffer2[i]) { status = err_status_algo_fail; debug_print(mod_cipher, "random test case %d failed", case_num); debug_print(mod_cipher, "(failure at byte %d)", i); } if (status) { cipher_dealloc(c); return err_status_algo_fail; } } cipher_dealloc(c); return err_status_ok; } /* * cipher_bits_per_second(c, l, t) computes (an estimate of) the * number of bits that a cipher implementation can encrypt in a second * * c is a cipher (which MUST be allocated and initialized already), l * is the length in octets of the test data to be encrypted, and t is * the number of trials * * if an error is encountered, the value 0 is returned */ uint64_t cipher_bits_per_second(cipher_t *c, int octets_in_buffer, int num_trials) { int i; v128_t nonce; clock_t timer; unsigned char *enc_buf; unsigned int len = octets_in_buffer; enc_buf = (unsigned char*) crypto_alloc(octets_in_buffer); if (enc_buf == NULL) return 0; /* indicate bad parameters by returning null */ /* time repeated trials */ v128_set_to_zero(&nonce); timer = clock(); for(i=0; i < num_trials; i++, nonce.v32[3] = i) { cipher_set_iv(c, &nonce); cipher_encrypt(c, enc_buf, &len); } timer = clock() - timer; crypto_free(enc_buf); if (timer == 0) { /* Too fast! */ return 0; } return (uint64_t)CLOCKS_PER_SEC * num_trials * 8 * octets_in_buffer / timer; } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/cipher/null_cipher.c ================================================ /* * null_cipher.c * * A null cipher implementation. This cipher leaves the plaintext * unchanged. * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "datatypes.h" #include "null_cipher.h" #include "alloc.h" /* the null_cipher uses the cipher debug module */ extern debug_module_t mod_cipher; err_status_t null_cipher_alloc(cipher_t **c, int key_len) { extern cipher_type_t null_cipher; uint8_t *pointer; debug_print(mod_cipher, "allocating cipher with key length %d", key_len); /* allocate memory a cipher of type null_cipher */ pointer = (uint8_t*)crypto_alloc(sizeof(null_cipher_ctx_t) + sizeof(cipher_t)); if (pointer == NULL) return err_status_alloc_fail; /* set pointers */ *c = (cipher_t *)pointer; (*c)->type = &null_cipher; (*c)->state = pointer + sizeof(cipher_t); /* set key size */ (*c)->key_len = key_len; /* increment ref_count */ null_cipher.ref_count++; return err_status_ok; } err_status_t null_cipher_dealloc(cipher_t *c) { extern cipher_type_t null_cipher; /* zeroize entire state*/ octet_string_set_to_zero((uint8_t *)c, sizeof(null_cipher_ctx_t) + sizeof(cipher_t)); /* free memory of type null_cipher */ crypto_free(c); /* decrement reference count */ null_cipher.ref_count--; return err_status_ok; } err_status_t null_cipher_init(null_cipher_ctx_t *ctx, const uint8_t *key) { debug_print(mod_cipher, "initializing null cipher", NULL); return err_status_ok; } err_status_t null_cipher_set_iv(null_cipher_ctx_t *c, void *iv) { return err_status_ok; } err_status_t null_cipher_encrypt(null_cipher_ctx_t *c, unsigned char *buf, unsigned int *bytes_to_encr) { return err_status_ok; } char null_cipher_description[] = "null cipher"; cipher_test_case_t null_cipher_test_0 = { 0, /* octets in key */ NULL, /* key */ 0, /* packet index */ 0, /* octets in plaintext */ NULL, /* plaintext */ 0, /* octets in plaintext */ NULL, /* ciphertext */ NULL /* pointer to next testcase */ }; /* * note: the decrypt function is idential to the encrypt function */ cipher_type_t null_cipher = { (cipher_alloc_func_t) null_cipher_alloc, (cipher_dealloc_func_t) null_cipher_dealloc, (cipher_init_func_t) null_cipher_init, (cipher_encrypt_func_t) null_cipher_encrypt, (cipher_decrypt_func_t) null_cipher_encrypt, (cipher_set_iv_func_t) null_cipher_set_iv, (char *) null_cipher_description, (int) 0, (cipher_test_case_t *) &null_cipher_test_0, (debug_module_t *) NULL }; ================================================ FILE: deps/pjsip/third_party/srtp/crypto/hash/auth.c ================================================ /* * auth.c * * some bookkeeping functions for authentication functions * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "auth.h" /* the debug module for authentiation */ debug_module_t mod_auth = { 0, /* debugging is off by default */ "auth func" /* printable name for module */ }; int auth_get_key_length(const auth_t *a) { return a->key_len; } int auth_get_tag_length(const auth_t *a) { return a->out_len; } int auth_get_prefix_length(const auth_t *a) { return a->prefix_len; } int auth_type_get_ref_count(const auth_type_t *at) { return at->ref_count; } /* * auth_type_self_test() tests an auth function of type ct against * test cases provided in an array of values of key, data, and tag * that is known to be good */ /* should be big enough for most occasions */ #define SELF_TEST_TAG_BUF_OCTETS 32 err_status_t auth_type_self_test(const auth_type_t *at) { auth_test_case_t *test_case = at->test_data; auth_t *a; err_status_t status; uint8_t tag[SELF_TEST_TAG_BUF_OCTETS]; int i, case_num = 0; debug_print(mod_auth, "running self-test for auth function %s", at->description); /* * check to make sure that we have at least one test case, and * return an error if we don't - we need to be paranoid here */ if (test_case == NULL) return err_status_cant_check; /* loop over all test cases */ while (test_case != NULL) { /* check test case parameters */ if (test_case->tag_length_octets > SELF_TEST_TAG_BUF_OCTETS) return err_status_bad_param; /* allocate auth */ status = auth_type_alloc(at, &a, test_case->key_length_octets, test_case->tag_length_octets); if (status) return status; /* initialize auth */ status = auth_init(a, test_case->key); if (status) { auth_dealloc(a); return status; } /* zeroize tag then compute */ octet_string_set_to_zero(tag, test_case->tag_length_octets); status = auth_compute(a, test_case->data, test_case->data_length_octets, tag); if (status) { auth_dealloc(a); return status; } debug_print(mod_auth, "key: %s", octet_string_hex_string(test_case->key, test_case->key_length_octets)); debug_print(mod_auth, "data: %s", octet_string_hex_string(test_case->data, test_case->data_length_octets)); debug_print(mod_auth, "tag computed: %s", octet_string_hex_string(tag, test_case->tag_length_octets)); debug_print(mod_auth, "tag expected: %s", octet_string_hex_string(test_case->tag, test_case->tag_length_octets)); /* check the result */ status = err_status_ok; for (i=0; i < test_case->tag_length_octets; i++) if (tag[i] != test_case->tag[i]) { status = err_status_algo_fail; debug_print(mod_auth, "test case %d failed", case_num); debug_print(mod_auth, " (mismatch at octet %d)", i); } if (status) { auth_dealloc(a); return err_status_algo_fail; } /* deallocate the auth function */ status = auth_dealloc(a); if (status) return status; /* * the auth function passed the test case, so move on to the next test * case in the list; if NULL, we'll quit and return an OK */ test_case = test_case->next_test_case; ++case_num; } return err_status_ok; } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/hash/hmac.c ================================================ /* * hmac.c * * implementation of hmac auth_type_t * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright(c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "hmac.h" #include "alloc.h" /* the debug module for authentiation */ debug_module_t mod_hmac = { 0, /* debugging is off by default */ "hmac sha-1" /* printable name for module */ }; err_status_t hmac_alloc(auth_t **a, int key_len, int out_len) { extern auth_type_t hmac; uint8_t *pointer; debug_print(mod_hmac, "allocating auth func with key length %d", key_len); debug_print(mod_hmac, " tag length %d", out_len); /* * check key length - note that we don't support keys larger * than 20 bytes yet */ if (key_len > 20) return err_status_bad_param; /* check output length - should be less than 20 bytes */ if (out_len > 20) return err_status_bad_param; /* allocate memory for auth and hmac_ctx_t structures */ pointer = (uint8_t*)crypto_alloc(sizeof(hmac_ctx_t) + sizeof(auth_t)); if (pointer == NULL) return err_status_alloc_fail; /* set pointers */ *a = (auth_t *)pointer; (*a)->type = &hmac; (*a)->state = pointer + sizeof(auth_t); (*a)->out_len = out_len; (*a)->key_len = key_len; (*a)->prefix_len = 0; /* increment global count of all hmac uses */ hmac.ref_count++; return err_status_ok; } err_status_t hmac_dealloc(auth_t *a) { extern auth_type_t hmac; /* zeroize entire state*/ octet_string_set_to_zero((uint8_t *)a, sizeof(hmac_ctx_t) + sizeof(auth_t)); /* free memory */ crypto_free(a); /* decrement global count of all hmac uses */ hmac.ref_count--; return err_status_ok; } err_status_t hmac_init(hmac_ctx_t *state, const uint8_t *key, int key_len) { int i; uint8_t ipad[64]; /* * check key length - note that we don't support keys larger * than 20 bytes yet */ if (key_len > 20) return err_status_bad_param; /* * set values of ipad and opad by exoring the key into the * appropriate constant values */ for (i=0; i < key_len; i++) { ipad[i] = key[i] ^ 0x36; state->opad[i] = key[i] ^ 0x5c; } /* set the rest of ipad, opad to constant values */ for ( ; i < 64; i++) { ipad[i] = 0x36; ((uint8_t *)state->opad)[i] = 0x5c; } debug_print(mod_hmac, "ipad: %s", octet_string_hex_string(ipad, 64)); /* initialize sha1 context */ sha1_init(&state->init_ctx); /* hash ipad ^ key */ sha1_update(&state->init_ctx, ipad, 64); memcpy(&state->ctx, &state->init_ctx, sizeof(sha1_ctx_t)); return err_status_ok; } err_status_t hmac_start(hmac_ctx_t *state) { memcpy(&state->ctx, &state->init_ctx, sizeof(sha1_ctx_t)); return err_status_ok; } err_status_t hmac_update(hmac_ctx_t *state, const uint8_t *message, int msg_octets) { debug_print(mod_hmac, "input: %s", octet_string_hex_string(message, msg_octets)); /* hash message into sha1 context */ sha1_update(&state->ctx, message, msg_octets); return err_status_ok; } err_status_t hmac_compute(hmac_ctx_t *state, const void *message, int msg_octets, int tag_len, uint8_t *result) { uint32_t hash_value[5]; uint32_t H[5]; int i; /* check tag length, return error if we can't provide the value expected */ if (tag_len > 20) return err_status_bad_param; /* hash message, copy output into H */ hmac_update(state, (const uint8_t*)message, msg_octets); sha1_final(&state->ctx, H); /* * note that we don't need to debug_print() the input, since the * function hmac_update() already did that for us */ debug_print(mod_hmac, "intermediate state: %s", octet_string_hex_string((uint8_t *)H, 20)); /* re-initialize hash context */ sha1_init(&state->ctx); /* hash opad ^ key */ sha1_update(&state->ctx, (uint8_t *)state->opad, 64); /* hash the result of the inner hash */ sha1_update(&state->ctx, (uint8_t *)H, 20); /* the result is returned in the array hash_value[] */ sha1_final(&state->ctx, hash_value); /* copy hash_value to *result */ for (i=0; i < tag_len; i++) result[i] = ((uint8_t *)hash_value)[i]; debug_print(mod_hmac, "output: %s", octet_string_hex_string((uint8_t *)hash_value, tag_len)); return err_status_ok; } /* begin test case 0 */ uint8_t hmac_test_case_0_key[20] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b }; uint8_t hmac_test_case_0_data[8] = { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65 /* "Hi There" */ }; uint8_t hmac_test_case_0_tag[20] = { 0xb6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, 0xe2, 0x8b, 0xc0, 0xb6, 0xfb, 0x37, 0x8c, 0x8e, 0xf1, 0x46, 0xbe, 0x00 }; auth_test_case_t hmac_test_case_0 = { 20, /* octets in key */ hmac_test_case_0_key, /* key */ 8, /* octets in data */ hmac_test_case_0_data, /* data */ 20, /* octets in tag */ hmac_test_case_0_tag, /* tag */ NULL /* pointer to next testcase */ }; /* end test case 0 */ char hmac_description[] = "hmac sha-1 authentication function"; /* * auth_type_t hmac is the hmac metaobject */ auth_type_t hmac = { (auth_alloc_func) hmac_alloc, (auth_dealloc_func) hmac_dealloc, (auth_init_func) hmac_init, (auth_compute_func) hmac_compute, (auth_update_func) hmac_update, (auth_start_func) hmac_start, (char *) hmac_description, (int) 0, /* instance count */ (auth_test_case_t *) &hmac_test_case_0, (debug_module_t *) &mod_hmac }; ================================================ FILE: deps/pjsip/third_party/srtp/crypto/hash/null_auth.c ================================================ /* * null_auth.c * * implements the do-nothing auth algorithm * * David A. McGrew * Cisco Systems, Inc. * */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "null_auth.h" #include "alloc.h" /* null_auth uses the auth debug module */ extern debug_module_t mod_auth; err_status_t null_auth_alloc(auth_t **a, int key_len, int out_len) { extern auth_type_t null_auth; uint8_t *pointer; debug_print(mod_auth, "allocating auth func with key length %d", key_len); debug_print(mod_auth, " tag length %d", out_len); /* allocate memory for auth and null_auth_ctx_t structures */ pointer = (uint8_t*)crypto_alloc(sizeof(null_auth_ctx_t) + sizeof(auth_t)); if (pointer == NULL) return err_status_alloc_fail; /* set pointers */ *a = (auth_t *)pointer; (*a)->type = &null_auth; (*a)->state = pointer + sizeof (auth_t); (*a)->out_len = out_len; (*a)->prefix_len = out_len; (*a)->key_len = key_len; /* increment global count of all null_auth uses */ null_auth.ref_count++; return err_status_ok; } err_status_t null_auth_dealloc(auth_t *a) { extern auth_type_t null_auth; /* zeroize entire state*/ octet_string_set_to_zero((uint8_t *)a, sizeof(null_auth_ctx_t) + sizeof(auth_t)); /* free memory */ crypto_free(a); /* decrement global count of all null_auth uses */ null_auth.ref_count--; return err_status_ok; } err_status_t null_auth_init(null_auth_ctx_t *state, const uint8_t *key, int key_len) { /* accept any length of key, and do nothing */ return err_status_ok; } err_status_t null_auth_compute(null_auth_ctx_t *state, uint8_t *message, int msg_octets, int tag_len, uint8_t *result) { return err_status_ok; } err_status_t null_auth_update(null_auth_ctx_t *state, uint8_t *message, int msg_octets) { return err_status_ok; } err_status_t null_auth_start(null_auth_ctx_t *state) { return err_status_ok; } /* * auth_type_t - defines description, test case, and null_auth * metaobject */ /* begin test case 0 */ auth_test_case_t null_auth_test_case_0 = { 0, /* octets in key */ NULL, /* key */ 0, /* octets in data */ NULL, /* data */ 0, /* octets in tag */ NULL, /* tag */ NULL /* pointer to next testcase */ }; /* end test case 0 */ char null_auth_description[] = "null authentication function"; auth_type_t null_auth = { (auth_alloc_func) null_auth_alloc, (auth_dealloc_func) null_auth_dealloc, (auth_init_func) null_auth_init, (auth_compute_func) null_auth_compute, (auth_update_func) null_auth_update, (auth_start_func) null_auth_start, (char *) null_auth_description, (int) 0, /* instance count */ (auth_test_case_t *) &null_auth_test_case_0 }; ================================================ FILE: deps/pjsip/third_party/srtp/crypto/hash/sha1.c ================================================ /* * sha1.c * * an implementation of the Secure Hash Algorithm v.1 (SHA-1), * specified in FIPS 180-1 * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "sha1.h" debug_module_t mod_sha1 = { 0, /* debugging is off by default */ "sha-1" /* printable module name */ }; /* SN == Rotate left N bits */ #define S1(X) ((X << 1) | (X >> 31)) #define S5(X) ((X << 5) | (X >> 27)) #define S30(X) ((X << 30) | (X >> 2)) #define f0(B,C,D) ((B & C) | (~B & D)) #define f1(B,C,D) (B ^ C ^ D) #define f2(B,C,D) ((B & C) | (B & D) | (C & D)) #define f3(B,C,D) (B ^ C ^ D) /* * nota bene: the variable K0 appears in the curses library, so we * give longer names to these variables to avoid spurious warnings * on systems that uses curses */ uint32_t SHA_K0 = 0x5A827999; /* Kt for 0 <= t <= 19 */ uint32_t SHA_K1 = 0x6ED9EBA1; /* Kt for 20 <= t <= 39 */ uint32_t SHA_K2 = 0x8F1BBCDC; /* Kt for 40 <= t <= 59 */ uint32_t SHA_K3 = 0xCA62C1D6; /* Kt for 60 <= t <= 79 */ void sha1(const uint8_t *msg, int octets_in_msg, uint32_t hash_value[5]) { sha1_ctx_t ctx; sha1_init(&ctx); sha1_update(&ctx, msg, octets_in_msg); sha1_final(&ctx, hash_value); } /* * sha1_core(M, H) computes the core compression function, where M is * the next part of the message (in network byte order) and H is the * intermediate state { H0, H1, ...} (in host byte order) * * this function does not do any of the padding required in the * complete SHA1 function * * this function is used in the SEAL 3.0 key setup routines * (crypto/cipher/seal.c) */ void sha1_core(const uint32_t M[16], uint32_t hash_value[5]) { uint32_t H0; uint32_t H1; uint32_t H2; uint32_t H3; uint32_t H4; uint32_t W[80]; uint32_t A, B, C, D, E, TEMP; int t; /* copy hash_value into H0, H1, H2, H3, H4 */ H0 = hash_value[0]; H1 = hash_value[1]; H2 = hash_value[2]; H3 = hash_value[3]; H4 = hash_value[4]; /* copy/xor message into array */ W[0] = be32_to_cpu(M[0]); W[1] = be32_to_cpu(M[1]); W[2] = be32_to_cpu(M[2]); W[3] = be32_to_cpu(M[3]); W[4] = be32_to_cpu(M[4]); W[5] = be32_to_cpu(M[5]); W[6] = be32_to_cpu(M[6]); W[7] = be32_to_cpu(M[7]); W[8] = be32_to_cpu(M[8]); W[9] = be32_to_cpu(M[9]); W[10] = be32_to_cpu(M[10]); W[11] = be32_to_cpu(M[11]); W[12] = be32_to_cpu(M[12]); W[13] = be32_to_cpu(M[13]); W[14] = be32_to_cpu(M[14]); W[15] = be32_to_cpu(M[15]); TEMP = W[13] ^ W[8] ^ W[2] ^ W[0]; W[16] = S1(TEMP); TEMP = W[14] ^ W[9] ^ W[3] ^ W[1]; W[17] = S1(TEMP); TEMP = W[15] ^ W[10] ^ W[4] ^ W[2]; W[18] = S1(TEMP); TEMP = W[16] ^ W[11] ^ W[5] ^ W[3]; W[19] = S1(TEMP); TEMP = W[17] ^ W[12] ^ W[6] ^ W[4]; W[20] = S1(TEMP); TEMP = W[18] ^ W[13] ^ W[7] ^ W[5]; W[21] = S1(TEMP); TEMP = W[19] ^ W[14] ^ W[8] ^ W[6]; W[22] = S1(TEMP); TEMP = W[20] ^ W[15] ^ W[9] ^ W[7]; W[23] = S1(TEMP); TEMP = W[21] ^ W[16] ^ W[10] ^ W[8]; W[24] = S1(TEMP); TEMP = W[22] ^ W[17] ^ W[11] ^ W[9]; W[25] = S1(TEMP); TEMP = W[23] ^ W[18] ^ W[12] ^ W[10]; W[26] = S1(TEMP); TEMP = W[24] ^ W[19] ^ W[13] ^ W[11]; W[27] = S1(TEMP); TEMP = W[25] ^ W[20] ^ W[14] ^ W[12]; W[28] = S1(TEMP); TEMP = W[26] ^ W[21] ^ W[15] ^ W[13]; W[29] = S1(TEMP); TEMP = W[27] ^ W[22] ^ W[16] ^ W[14]; W[30] = S1(TEMP); TEMP = W[28] ^ W[23] ^ W[17] ^ W[15]; W[31] = S1(TEMP); /* process the remainder of the array */ for (t=32; t < 80; t++) { TEMP = W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]; W[t] = S1(TEMP); } A = H0; B = H1; C = H2; D = H3; E = H4; for (t=0; t < 20; t++) { TEMP = S5(A) + f0(B,C,D) + E + W[t] + SHA_K0; E = D; D = C; C = S30(B); B = A; A = TEMP; } for ( ; t < 40; t++) { TEMP = S5(A) + f1(B,C,D) + E + W[t] + SHA_K1; E = D; D = C; C = S30(B); B = A; A = TEMP; } for ( ; t < 60; t++) { TEMP = S5(A) + f2(B,C,D) + E + W[t] + SHA_K2; E = D; D = C; C = S30(B); B = A; A = TEMP; } for ( ; t < 80; t++) { TEMP = S5(A) + f3(B,C,D) + E + W[t] + SHA_K3; E = D; D = C; C = S30(B); B = A; A = TEMP; } hash_value[0] = H0 + A; hash_value[1] = H1 + B; hash_value[2] = H2 + C; hash_value[3] = H3 + D; hash_value[4] = H4 + E; return; } void sha1_init(sha1_ctx_t *ctx) { /* initialize state vector */ ctx->H[0] = 0x67452301; ctx->H[1] = 0xefcdab89; ctx->H[2] = 0x98badcfe; ctx->H[3] = 0x10325476; ctx->H[4] = 0xc3d2e1f0; /* indicate that message buffer is empty */ ctx->octets_in_buffer = 0; /* reset message bit-count to zero */ ctx->num_bits_in_msg = 0; } void sha1_update(sha1_ctx_t *ctx, const uint8_t *msg, int octets_in_msg) { int i; uint8_t *buf = (uint8_t *)ctx->M; /* update message bit-count */ ctx->num_bits_in_msg += octets_in_msg * 8; /* loop over 16-word blocks of M */ while (octets_in_msg > 0) { if (octets_in_msg + ctx->octets_in_buffer >= 64) { /* * copy words of M into msg buffer until that buffer is full, * converting them into host byte order as needed */ octets_in_msg -= (64 - ctx->octets_in_buffer); for (i=ctx->octets_in_buffer; i < 64; i++) buf[i] = *msg++; ctx->octets_in_buffer = 0; /* process a whole block */ debug_print(mod_sha1, "(update) running sha1_core()", NULL); sha1_core(ctx->M, ctx->H); } else { debug_print(mod_sha1, "(update) not running sha1_core()", NULL); for (i=ctx->octets_in_buffer; i < (ctx->octets_in_buffer + octets_in_msg); i++) buf[i] = *msg++; ctx->octets_in_buffer += octets_in_msg; octets_in_msg = 0; } } } /* * sha1_final(ctx, output) computes the result for ctx and copies it * into the twenty octets located at *output */ void sha1_final(sha1_ctx_t *ctx, uint32_t *output) { uint32_t A, B, C, D, E, TEMP; uint32_t W[80]; int i, t; /* * process the remaining octets_in_buffer, padding and terminating as * necessary */ { int tail = ctx->octets_in_buffer % 4; /* copy/xor message into array */ for (i=0; i < (ctx->octets_in_buffer+3)/4; i++) W[i] = be32_to_cpu(ctx->M[i]); /* set the high bit of the octet immediately following the message */ switch (tail) { case (3): W[i-1] = (be32_to_cpu(ctx->M[i-1]) & 0xffffff00) | 0x80; W[i] = 0x0; break; case (2): W[i-1] = (be32_to_cpu(ctx->M[i-1]) & 0xffff0000) | 0x8000; W[i] = 0x0; break; case (1): W[i-1] = (be32_to_cpu(ctx->M[i-1]) & 0xff000000) | 0x800000; W[i] = 0x0; break; case (0): W[i] = 0x80000000; break; } /* zeroize remaining words */ for (i++ ; i < 15; i++) W[i] = 0x0; /* * if there is room at the end of the word array, then set the * last word to the bit-length of the message; otherwise, set that * word to zero and then we need to do one more run of the * compression algo. */ if (ctx->octets_in_buffer < 56) W[15] = ctx->num_bits_in_msg; else if (ctx->octets_in_buffer < 60) W[15] = 0x0; /* process the word array */ for (t=16; t < 80; t++) { TEMP = W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]; W[t] = S1(TEMP); } A = ctx->H[0]; B = ctx->H[1]; C = ctx->H[2]; D = ctx->H[3]; E = ctx->H[4]; for (t=0; t < 20; t++) { TEMP = S5(A) + f0(B,C,D) + E + W[t] + SHA_K0; E = D; D = C; C = S30(B); B = A; A = TEMP; } for ( ; t < 40; t++) { TEMP = S5(A) + f1(B,C,D) + E + W[t] + SHA_K1; E = D; D = C; C = S30(B); B = A; A = TEMP; } for ( ; t < 60; t++) { TEMP = S5(A) + f2(B,C,D) + E + W[t] + SHA_K2; E = D; D = C; C = S30(B); B = A; A = TEMP; } for ( ; t < 80; t++) { TEMP = S5(A) + f3(B,C,D) + E + W[t] + SHA_K3; E = D; D = C; C = S30(B); B = A; A = TEMP; } ctx->H[0] += A; ctx->H[1] += B; ctx->H[2] += C; ctx->H[3] += D; ctx->H[4] += E; } debug_print(mod_sha1, "(final) running sha1_core()", NULL); if (ctx->octets_in_buffer >= 56) { debug_print(mod_sha1, "(final) running sha1_core() again", NULL); /* we need to do one final run of the compression algo */ /* * set initial part of word array to zeros, and set the * final part to the number of bits in the message */ for (i=0; i < 15; i++) W[i] = 0x0; W[15] = ctx->num_bits_in_msg; /* process the word array */ for (t=16; t < 80; t++) { TEMP = W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]; W[t] = S1(TEMP); } A = ctx->H[0]; B = ctx->H[1]; C = ctx->H[2]; D = ctx->H[3]; E = ctx->H[4]; for (t=0; t < 20; t++) { TEMP = S5(A) + f0(B,C,D) + E + W[t] + SHA_K0; E = D; D = C; C = S30(B); B = A; A = TEMP; } for ( ; t < 40; t++) { TEMP = S5(A) + f1(B,C,D) + E + W[t] + SHA_K1; E = D; D = C; C = S30(B); B = A; A = TEMP; } for ( ; t < 60; t++) { TEMP = S5(A) + f2(B,C,D) + E + W[t] + SHA_K2; E = D; D = C; C = S30(B); B = A; A = TEMP; } for ( ; t < 80; t++) { TEMP = S5(A) + f3(B,C,D) + E + W[t] + SHA_K3; E = D; D = C; C = S30(B); B = A; A = TEMP; } ctx->H[0] += A; ctx->H[1] += B; ctx->H[2] += C; ctx->H[3] += D; ctx->H[4] += E; } /* copy result into output buffer */ output[0] = be32_to_cpu(ctx->H[0]); output[1] = be32_to_cpu(ctx->H[1]); output[2] = be32_to_cpu(ctx->H[2]); output[3] = be32_to_cpu(ctx->H[3]); output[4] = be32_to_cpu(ctx->H[4]); /* indicate that message buffer in context is empty */ ctx->octets_in_buffer = 0; return; } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/aes.h ================================================ /* * aes.h * * header file for the AES block cipher * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef _AES_H #define _AES_H #include "srtp_config.h" #include "datatypes.h" #include "gf2_8.h" /* aes internals */ typedef v128_t aes_expanded_key_t[11]; void aes_expand_encryption_key(const v128_t *key, aes_expanded_key_t expanded_key); void aes_expand_decryption_key(const v128_t *key, aes_expanded_key_t expanded_key); void aes_encrypt(v128_t *plaintext, const aes_expanded_key_t exp_key); void aes_decrypt(v128_t *plaintext, const aes_expanded_key_t exp_key); #if 0 /* * internal functions */ void aes_init_sbox(void); void aes_compute_tables(void); #endif #endif /* _AES_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/aes_cbc.h ================================================ /* * aes_cbc.h * * Header for AES Cipher Blobk Chaining Mode. * * David A. McGrew * Cisco Systems, Inc. * */ #ifndef AES_CBC_H #define AES_CBC_H #include "aes.h" #include "cipher.h" typedef struct { v128_t state; /* cipher chaining state */ v128_t previous; /* previous ciphertext block */ aes_expanded_key_t expanded_key; /* the cipher key */ } aes_cbc_ctx_t; err_status_t aes_cbc_set_key(aes_cbc_ctx_t *c, const unsigned char *key); err_status_t aes_cbc_encrypt(aes_cbc_ctx_t *c, unsigned char *buf, unsigned int *bytes_in_data); err_status_t aes_cbc_context_init(aes_cbc_ctx_t *c, const uint8_t *key, cipher_direction_t dir); err_status_t aes_cbc_set_iv(aes_cbc_ctx_t *c, void *iv); err_status_t aes_cbc_nist_encrypt(aes_cbc_ctx_t *c, unsigned char *data, unsigned int *bytes_in_data); err_status_t aes_cbc_nist_decrypt(aes_cbc_ctx_t *c, unsigned char *data, unsigned int *bytes_in_data); #endif /* AES_CBC_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/aes_icm.h ================================================ /* * aes_icm.h * * Header for AES Integer Counter Mode. * * David A. McGrew * Cisco Systems, Inc. * */ #ifndef AES_ICM_H #define AES_ICM_H #include "aes.h" #include "cipher.h" typedef struct { v128_t counter; /* holds the counter value */ v128_t offset; /* initial offset value */ v128_t keystream_buffer; /* buffers bytes of keystream */ aes_expanded_key_t expanded_key; /* the cipher key */ int bytes_in_buffer; /* number of unused bytes in buffer */ } aes_icm_ctx_t; err_status_t aes_icm_context_init(aes_icm_ctx_t *c, const unsigned char *key); err_status_t aes_icm_set_iv(aes_icm_ctx_t *c, void *iv); err_status_t aes_icm_encrypt(aes_icm_ctx_t *c, unsigned char *buf, unsigned int *bytes_to_encr); err_status_t aes_icm_output(aes_icm_ctx_t *c, unsigned char *buf, int bytes_to_output); err_status_t aes_icm_dealloc(cipher_t *c); err_status_t aes_icm_encrypt_ismacryp(aes_icm_ctx_t *c, unsigned char *buf, unsigned int *enc_len, int forIsmacryp); err_status_t aes_icm_alloc_ismacryp(cipher_t **c, int key_len, int forIsmacryp); #endif /* AES_ICM_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/alloc.h ================================================ /* * alloc.h * * interface to memory allocation and deallocation, with optional debugging * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef CRYPTO_ALLOC_H #define CRYPTO_ALLOC_H #include "datatypes.h" void * crypto_alloc(size_t size); void crypto_free(void *ptr); #endif /* CRYPTO_ALLOC_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/auth.h ================================================ /* * auth.h * * common interface to authentication functions * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef AUTH_H #define AUTH_H #include "datatypes.h" #include "err.h" /* error codes */ typedef struct auth_type_t *auth_type_pointer; typedef struct auth_t *auth_pointer_t; typedef err_status_t (*auth_alloc_func) (auth_pointer_t *ap, int key_len, int out_len); typedef err_status_t (*auth_init_func) (void *state, const uint8_t *key, int key_len); typedef err_status_t (*auth_dealloc_func)(auth_pointer_t ap); typedef err_status_t (*auth_compute_func) (void *state, uint8_t *buffer, int octets_to_auth, int tag_len, uint8_t *tag); typedef err_status_t (*auth_update_func) (void *state, uint8_t *buffer, int octets_to_auth); typedef err_status_t (*auth_start_func)(void *state); /* some syntactic sugar on these function types */ #define auth_type_alloc(at, a, klen, outlen) \ ((at)->alloc((a), (klen), (outlen))) #define auth_init(a, key) \ (((a)->type)->init((a)->state, (key), ((a)->key_len))) #define auth_compute(a, buf, len, res) \ (((a)->type)->compute((a)->state, (buf), (len), (a)->out_len, (res))) #define auth_update(a, buf, len) \ (((a)->type)->update((a)->state, (buf), (len))) #define auth_start(a)(((a)->type)->start((a)->state)) #define auth_dealloc(c) (((c)->type)->dealloc(c)) /* functions to get information about a particular auth_t */ int auth_get_key_length(const struct auth_t *a); int auth_get_tag_length(const struct auth_t *a); int auth_get_prefix_length(const struct auth_t *a); /* * auth_test_case_t is a (list of) key/message/tag values that are * known to be correct for a particular cipher. this data can be used * to test an implementation in an on-the-fly self test of the * correcness of the implementation. (see the auth_type_self_test() * function below) */ typedef struct auth_test_case_t { int key_length_octets; /* octets in key */ uint8_t *key; /* key */ int data_length_octets; /* octets in data */ uint8_t *data; /* data */ int tag_length_octets; /* octets in tag */ uint8_t *tag; /* tag */ struct auth_test_case_t *next_test_case; /* pointer to next testcase */ } auth_test_case_t; /* auth_type_t */ typedef struct auth_type_t { auth_alloc_func alloc; auth_dealloc_func dealloc; auth_init_func init; auth_compute_func compute; auth_update_func update; auth_start_func start; char *description; int ref_count; auth_test_case_t *test_data; debug_module_t *debug; } auth_type_t; typedef struct auth_t { auth_type_t *type; void *state; int out_len; /* length of output tag in octets */ int key_len; /* length of key in octets */ int prefix_len; /* length of keystream prefix */ } auth_t; /* * auth_type_self_test() tests an auth_type against test cases * provided in an array of values of key/message/tag that is known to * be good */ err_status_t auth_type_self_test(const auth_type_t *at); /* * auth_type_get_ref_count(at) returns the reference count (the number * of instantiations) of the auth_type_t at */ int auth_type_get_ref_count(const auth_type_t *at); #endif /* AUTH_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/cipher.h ================================================ /* * cipher.h * * common interface to ciphers * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef CIPHER_H #define CIPHER_H #include "datatypes.h" #include "rdbx.h" /* for xtd_seq_num_t */ #include "err.h" /* for error codes */ /** * @brief cipher_direction_t defines a particular cipher operation. * * A cipher_direction_t is an enum that describes a particular cipher * operation, i.e. encryption or decryption. For some ciphers, this * distinction does not matter, but for others, it is essential. */ typedef enum { direction_encrypt, /**< encryption (convert plaintext to ciphertext) */ direction_decrypt, /**< decryption (convert ciphertext to plaintext) */ direction_any /**< encryption or decryption */ } cipher_direction_t; /* * the cipher_pointer and cipher_type_pointer definitions are needed * as cipher_t and cipher_type_t are not yet defined */ typedef struct cipher_type_t *cipher_type_pointer_t; typedef struct cipher_t *cipher_pointer_t; /* * a cipher_alloc_func_t allocates (but does not initialize) a cipher_t */ typedef err_status_t (*cipher_alloc_func_t) (cipher_pointer_t *cp, int key_len); /* * a cipher_init_func_t [re-]initializes a cipher_t with a given key * and direction (i.e., encrypt or decrypt) */ typedef err_status_t (*cipher_init_func_t) (void *state, const uint8_t *key, cipher_direction_t dir); /* a cipher_dealloc_func_t de-allocates a cipher_t */ typedef err_status_t (*cipher_dealloc_func_t)(cipher_pointer_t cp); /* a cipher_set_segment_func_t sets the segment index of a cipher_t */ typedef err_status_t (*cipher_set_segment_func_t) (void *state, xtd_seq_num_t idx); /* a cipher_encrypt_func_t encrypts data in-place */ typedef err_status_t (*cipher_encrypt_func_t) (void *state, uint8_t *buffer, unsigned int *octets_to_encrypt); /* a cipher_decrypt_func_t decrypts data in-place */ typedef err_status_t (*cipher_decrypt_func_t) (void *state, uint8_t *buffer, unsigned int *octets_to_decrypt); /* * a cipher_set_nonce_seq_func_t function sets both the nonce * and the extended sequence number */ typedef err_status_t (*cipher_set_iv_func_t) (cipher_pointer_t cp, void *iv); /* * cipher_test_case_t is a (list of) key, salt, xtd_seq_num_t, * plaintext, and ciphertext values that are known to be correct for a * particular cipher. this data can be used to test an implementation * in an on-the-fly self test of the correcness of the implementation. * (see the cipher_type_self_test() function below) */ typedef struct cipher_test_case_t { int key_length_octets; /* octets in key */ uint8_t *key; /* key */ uint8_t *idx; /* packet index */ unsigned int plaintext_length_octets; /* octets in plaintext */ uint8_t *plaintext; /* plaintext */ unsigned int ciphertext_length_octets; /* octets in plaintext */ uint8_t *ciphertext; /* ciphertext */ struct cipher_test_case_t *next_test_case; /* pointer to next testcase */ } cipher_test_case_t; /* cipher_type_t defines the 'metadata' for a particular cipher type */ typedef struct cipher_type_t { cipher_alloc_func_t alloc; cipher_dealloc_func_t dealloc; cipher_init_func_t init; cipher_encrypt_func_t encrypt; cipher_encrypt_func_t decrypt; cipher_set_iv_func_t set_iv; char *description; int ref_count; cipher_test_case_t *test_data; debug_module_t *debug; } cipher_type_t; /* * cipher_t defines an instantiation of a particular cipher, with fixed * key length, key and salt values */ typedef struct cipher_t { cipher_type_t *type; void *state; int key_len; #ifdef FORCE_64BIT_ALIGN int pad; #endif } cipher_t; /* some syntactic sugar on these function types */ #define cipher_type_alloc(ct, c, klen) ((ct)->alloc((c), (klen))) #define cipher_dealloc(c) (((c)->type)->dealloc(c)) #define cipher_init(c, k, dir) (((c)->type)->init(((c)->state), (k), (dir))) #define cipher_encrypt(c, buf, len) \ (((c)->type)->encrypt(((c)->state), (buf), (len))) #define cipher_decrypt(c, buf, len) \ (((c)->type)->decrypt(((c)->state), (buf), (len))) #define cipher_set_iv(c, n) \ ((c) ? (((c)->type)->set_iv(((cipher_pointer_t)(c)->state), (n))) : \ err_status_no_such_op) err_status_t cipher_output(cipher_t *c, uint8_t *buffer, int num_octets_to_output); /* some bookkeeping functions */ int cipher_get_key_length(const cipher_t *c); /* * cipher_type_self_test() tests a cipher against test cases provided in * an array of values of key/xtd_seq_num_t/plaintext/ciphertext * that is known to be good */ err_status_t cipher_type_self_test(const cipher_type_t *ct); /* * cipher_bits_per_second(c, l, t) computes (and estimate of) the * number of bits that a cipher implementation can encrypt in a second * * c is a cipher (which MUST be allocated and initialized already), l * is the length in octets of the test data to be encrypted, and t is * the number of trials * * if an error is encountered, then the value 0 is returned */ uint64_t cipher_bits_per_second(cipher_t *c, int octets_in_buffer, int num_trials); #endif /* CIPHER_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/crypto.h ================================================ /* * crypto.h * * API for libcrypto * * David A. McGrew * Cisco Systems, Inc. */ #ifndef CRYPTO_H #define CRYPTO_H /** * @brief A cipher_type_id_t is an identifier for a particular cipher * type. * * A cipher_type_id_t is an integer that represents a particular * cipher type, e.g. the Advanced Encryption Standard (AES). A * NULL_CIPHER is avaliable; this cipher leaves the data unchanged, * and can be selected to indicate that no encryption is to take * place. * * @ingroup Ciphers */ typedef uint32_t cipher_type_id_t; /** * @brief An auth_type_id_t is an identifier for a particular authentication * function. * * An auth_type_id_t is an integer that represents a particular * authentication function type, e.g. HMAC-SHA1. A NULL_AUTH is * avaliable; this authentication function performs no computation, * and can be selected to indicate that no authentication is to take * place. * * @ingroup Authentication */ typedef uint32_t auth_type_id_t; #endif /* CRYPTO_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/crypto_kernel.h ================================================ /* * crypto_kernel.h * * header for the cryptographic kernel * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright(c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef CRYPTO_KERNEL #define CRYPTO_KERNEL #include "rand_source.h" #include "prng.h" #include "cipher.h" #include "auth.h" #include "cryptoalg.h" #include "stat.h" #include "err.h" #include "crypto_types.h" #include "key.h" #include "crypto.h" /* * crypto_kernel_state_t defines the possible states: * * insecure - not yet initialized * secure - initialized and passed self-tests */ typedef enum { crypto_kernel_state_insecure, crypto_kernel_state_secure } crypto_kernel_state_t; /* * linked list of cipher types */ typedef struct kernel_cipher_type { cipher_type_id_t id; cipher_type_t *cipher_type; struct kernel_cipher_type *next; } kernel_cipher_type_t; /* * linked list of auth types */ typedef struct kernel_auth_type { auth_type_id_t id; auth_type_t *auth_type; struct kernel_auth_type *next; } kernel_auth_type_t; /* * linked list of debug modules */ typedef struct kernel_debug_module { debug_module_t *mod; struct kernel_debug_module *next; } kernel_debug_module_t; /* * crypto_kernel_t is the data structure for the crypto kernel * * note that there is *exactly one* instance of this data type, * a global variable defined in crypto_kernel.c */ typedef struct { crypto_kernel_state_t state; /* current state of kernel */ kernel_cipher_type_t *cipher_type_list; /* list of all cipher types */ kernel_auth_type_t *auth_type_list; /* list of all auth func types */ kernel_debug_module_t *debug_module_list; /* list of all debug modules */ } crypto_kernel_t; /* * crypto_kernel_t external api */ /* * The function crypto_kernel_init() initialized the crypto kernel and * runs the self-test operations on the random number generators and * crypto algorithms. Possible return values are: * * err_status_ok initialization successful * init failure * * If any value other than err_status_ok is returned, the * crypto_kernel MUST NOT be used. */ err_status_t crypto_kernel_init(void); /* * The function crypto_kernel_shutdown() de-initializes the * crypto_kernel, zeroizes keys and other cryptographic material, and * deallocates any dynamically allocated memory. Possible return * values are: * * err_status_ok shutdown successful * shutdown failure * */ err_status_t crypto_kernel_shutdown(void); /* * The function crypto_kernel_stats() checks the the crypto_kernel, * running tests on the ciphers, auth funcs, and rng, and prints out a * status report. Possible return values are: * * err_status_ok all tests were passed * a test failed * */ err_status_t crypto_kernel_status(void); /* * crypto_kernel_list_debug_modules() outputs a list of debugging modules * */ err_status_t crypto_kernel_list_debug_modules(void); /* * crypto_kernel_load_cipher_type() * */ err_status_t crypto_kernel_load_cipher_type(cipher_type_t *ct, cipher_type_id_t id); err_status_t crypto_kernel_load_auth_type(auth_type_t *ct, auth_type_id_t id); err_status_t crypto_kernel_load_debug_module(debug_module_t *new_dm); /* * crypto_kernel_alloc_cipher(id, cp, key_len); * * allocates a cipher of type id at location *cp, with key length * key_len octets. Return values are: * * err_status_ok no problems * err_status_alloc_fail an allocation failure occured * err_status_fail couldn't find cipher with identifier 'id' */ err_status_t crypto_kernel_alloc_cipher(cipher_type_id_t id, cipher_pointer_t *cp, int key_len); /* * crypto_kernel_alloc_auth(id, ap, key_len, tag_len); * * allocates an auth function of type id at location *ap, with key * length key_len octets and output tag length of tag_len. Return * values are: * * err_status_ok no problems * err_status_alloc_fail an allocation failure occured * err_status_fail couldn't find auth with identifier 'id' */ err_status_t crypto_kernel_alloc_auth(auth_type_id_t id, auth_pointer_t *ap, int key_len, int tag_len); /* * crypto_kernel_set_debug_module(mod_name, v) * * sets dynamic debugging to the value v (0 for off, 1 for on) for the * debug module with the name mod_name * * returns err_status_ok on success, err_status_fail otherwise */ err_status_t crypto_kernel_set_debug_module(char *mod_name, int v); /** * @brief writes a random octet string. * * The function call crypto_get_random(dest, len) writes len octets of * random data to the location to which dest points, and returns an * error code. This error code @b must be checked, and if a failure is * reported, the data in the buffer @b must @b not be used. * * @warning If the return code is not checked, then non-random * data may be in the buffer. This function will fail * unless it is called after crypto_kernel_init(). * * @return * - err_status_ok if no problems occured. * - [other] a problem occured, and no assumptions should * be made about the contents of the destination * buffer. * * @ingroup SRTP */ err_status_t crypto_get_random(unsigned char *buffer, unsigned int length); #endif /* CRYPTO_KERNEL */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/crypto_math.h ================================================ /* * math.h * * crypto math operations and data types * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef MATH_H #define MATH_H #include "datatypes.h" unsigned char v32_weight(v32_t a); unsigned char v32_distance(v32_t x, v32_t y); unsigned int v32_dot_product(v32_t a, v32_t b); char * v16_bit_string(v16_t x); char * v32_bit_string(v32_t x); char * v64_bit_string(const v64_t *x); char * octet_hex_string(uint8_t x); char * v16_hex_string(v16_t x); char * v32_hex_string(v32_t x); char * v64_hex_string(const v64_t *x); int hex_char_to_nibble(uint8_t c); int is_hex_string(char *s); v16_t hex_string_to_v16(char *s); v32_t hex_string_to_v32(char *s); v64_t hex_string_to_v64(char *s); /* the matrix A[] is stored in column format, i.e., A[i] is the ith column of the matrix */ uint8_t A_times_x_plus_b(uint8_t A[8], uint8_t x, uint8_t b); void v16_copy_octet_string(v16_t *x, const uint8_t s[2]); void v32_copy_octet_string(v32_t *x, const uint8_t s[4]); void v64_copy_octet_string(v64_t *x, const uint8_t s[8]); void v128_add(v128_t *z, v128_t *x, v128_t *y); int octet_string_is_eq(uint8_t *a, uint8_t *b, int len); void octet_string_set_to_zero(uint8_t *s, int len); /* * the matrix A[] is stored in column format, i.e., A[i] is the ith * column of the matrix */ uint8_t A_times_x_plus_b(uint8_t A[8], uint8_t x, uint8_t b); #if 0 #if WORDS_BIGENDIAN #define _v128_add(z, x, y) { \ uint64_t tmp; \ \ tmp = x->v32[3] + y->v32[3]; \ z->v32[3] = (uint32_t) tmp; \ \ tmp = x->v32[2] + y->v32[2] + (tmp >> 32); \ z->v32[2] = (uint32_t) tmp; \ \ tmp = x->v32[1] + y->v32[1] + (tmp >> 32); \ z->v32[1] = (uint32_t) tmp; \ \ tmp = x->v32[0] + y->v32[0] + (tmp >> 32); \ z->v32[0] = (uint32_t) tmp; \ } #else /* assume little endian architecture */ #define _v128_add(z, x, y) { \ uint64_t tmp; \ \ tmp = htonl(x->v32[3]) + htonl(y->v32[3]); \ z->v32[3] = ntohl((uint32_t) tmp); \ \ tmp = htonl(x->v32[2]) + htonl(y->v32[2]) \ + htonl(tmp >> 32); \ z->v32[2] = ntohl((uint32_t) tmp); \ \ tmp = htonl(x->v32[1]) + htonl(y->v32[1]) \ + htonl(tmp >> 32); \ z->v32[1] = ntohl((uint32_t) tmp); \ \ tmp = htonl(x->v32[0]) + htonl(y->v32[0]) \ + htonl(tmp >> 32); \ z->v32[0] = ntohl((uint32_t) tmp); \ } #endif /* WORDS_BIGENDIAN */ #endif #ifdef DATATYPES_USE_MACROS /* little functions are really macros */ #define v128_set_to_zero(z) _v128_set_to_zero(z) #define v128_copy(z, x) _v128_copy(z, x) #define v128_xor(z, x, y) _v128_xor(z, x, y) #define v128_and(z, x, y) _v128_and(z, x, y) #define v128_or(z, x, y) _v128_or(z, x, y) #define v128_complement(x) _v128_complement(x) #define v128_is_eq(x, y) _v128_is_eq(x, y) #define v128_xor_eq(x, y) _v128_xor_eq(x, y) #define v128_get_bit(x, i) _v128_get_bit(x, i) #define v128_set_bit(x, i) _v128_set_bit(x, i) #define v128_clear_bit(x, i) _v128_clear_bit(x, i) #define v128_set_bit_to(x, i, y) _v128_set_bit_to(x, i, y) #else void v128_set_to_zero(v128_t *x); int v128_is_eq(const v128_t *x, const v128_t *y); void v128_copy(v128_t *x, const v128_t *y); void v128_xor(v128_t *z, v128_t *x, v128_t *y); void v128_and(v128_t *z, v128_t *x, v128_t *y); void v128_or(v128_t *z, v128_t *x, v128_t *y); void v128_complement(v128_t *x); int v128_get_bit(const v128_t *x, int i); void v128_set_bit(v128_t *x, int i) ; void v128_clear_bit(v128_t *x, int i); void v128_set_bit_to(v128_t *x, int i, int y); #endif /* DATATYPES_USE_MACROS */ /* * octet_string_is_eq(a,b, len) returns 1 if the length len strings a * and b are not equal, returns 0 otherwise */ int octet_string_is_eq(uint8_t *a, uint8_t *b, int len); void octet_string_set_to_zero(uint8_t *s, int len); /* * functions manipulating bit_vector_t * * A bitvector_t consists of an array of words and an integer * representing the number of significant bits stored in the array. * The bits are packed as follows: the least significant bit is that * of word[0], while the most significant bit is the nth most * significant bit of word[m], where length = bits_per_word * m + n. * */ #define bits_per_word 32 #define bytes_per_word 4 typedef struct { uint32_t length; uint32_t *word; } bitvector_t; int bitvector_alloc(bitvector_t *v, unsigned long length); void bitvector_set_bit(bitvector_t *v, int bit_index); int bitvector_get_bit(const bitvector_t *v, int bit_index); int bitvector_print_hex(const bitvector_t *v, FILE *stream); int bitvector_set_from_hex(bitvector_t *v, char *string); #endif /* MATH_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/crypto_types.h ================================================ /* * crypto_types.h * * constants for cipher types and auth func types * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright(c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef CRYPTO_TYPES_H #define CRYPTO_TYPES_H /** * @defgroup Algos Cryptographic Algorithms * * * This library provides several different cryptographic algorithms, * each of which can be selected by using the cipher_type_id_t and * auth_type_id_t. These algorithms are documented below. * * Authentication functions that use the Universal Security Transform * (UST) must be used in conjunction with a cipher other than the null * cipher. These functions require a per-message pseudorandom input * that is generated by the cipher. * * The identifiers STRONGHOLD_AUTH and STRONGHOLD_CIPHER identify the * strongest available authentication function and cipher, * respectively. They are resolved at compile time to the strongest * available algorithm. The stronghold algorithms can serve as did * the keep of a medieval fortification; they provide the strongest * defense (or the last refuge). * * @{ */ /** * @defgroup Ciphers Cipher Types * * @brief Each cipher type is identified by an unsigned integer. The * cipher types available in this edition of libSRTP are given * by the #defines below. * * A cipher_type_id_t is an identifier for a cipher_type; only values * given by the #defines above (or those present in the file * crypto_types.h) should be used. * * The identifier STRONGHOLD_CIPHER indicates the strongest available * cipher, allowing an application to choose the strongest available * algorithm without any advance knowledge about the avaliable * algorithms. * * @{ */ /** * @brief The null cipher performs no encryption. * * The NULL_CIPHER leaves its inputs unaltered, during both the * encryption and decryption operations. This cipher can be chosen * to indicate that no encryption is to be performed. */ #define NULL_CIPHER 0 /** * @brief AES-128 Integer Counter Mode (AES ICM) * * AES-128 ICM is the variant of counter mode that is used by Secure RTP. * This cipher uses a 16-octet key and a 30-octet offset (or salt) value. */ #define AES_128_ICM 1 /** * @brief SEAL 3.0 * * SEAL is the Software-Optimized Encryption Algorithm of Coppersmith * and Rogaway. Nota bene: this cipher is IBM proprietary. */ #define SEAL 2 /** * @brief AES-128 Integer Counter Mode (AES ICM) * * AES-128 ICM is the variant of counter mode that is used by Secure RTP. * This cipher uses a 16-octet key and a 30-octet offset (or salt) value. */ #define AES_128_CBC 3 /** * @brief Strongest available cipher. * * This identifier resolves to the strongest cipher type available. */ #define STRONGHOLD_CIPHER AES_128_ICM /** * @} */ /** * @defgroup Authentication Authentication Function Types * * @brief Each authentication function type is identified by an * unsigned integer. The authentication function types available in * this edition of libSRTP are given by the #defines below. * * An auth_type_id_t is an identifier for an authentication function type; * only values given by the #defines above (or those present in the * file crypto_types.h) should be used. * * The identifier STRONGHOLD_AUTH indicates the strongest available * authentication function, allowing an application to choose the * strongest available algorithm without any advance knowledge about * the avaliable algorithms. The stronghold algorithms can serve as * did the keep of a medieval fortification; they provide the * strongest defense (or the last refuge). * * @{ */ /** * @brief The null authentication function performs no authentication. * * The NULL_AUTH function does nothing, and can be selected to indicate * that authentication should not be performed. */ #define NULL_AUTH 0 /** * @brief UST with TMMH Version 2 * * UST_TMMHv2 implements the Truncated Multi-Modular Hash using * UST. This function must be used in conjunction with a cipher other * than the null cipher. * with a cipher. */ #define UST_TMMHv2 1 /** * @brief (UST) AES-128 XORMAC * * UST_AES_128_XMAC implements AES-128 XORMAC, using UST. Nota bene: * the XORMAC algorithm is IBM proprietary. */ #define UST_AES_128_XMAC 2 /** * @brief HMAC-SHA1 * * HMAC_SHA1 implements the Hash-based MAC using the NIST Secure * Hash Algorithm version 1 (SHA1). */ #define HMAC_SHA1 3 /** * @brief Strongest available authentication function. * * This identifier resolves to the strongest available authentication * function. */ #define STRONGHOLD_AUTH HMAC_SHA1 /** * @} */ /** * @} */ #endif /* CRYPTO_TYPES_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/cryptoalg.h ================================================ /* * cryptoalg.h * * API for authenticated encryption crypto algorithms * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef CRYPTOALG_H #define CRYPTOALG_H #include "err.h" /** * @defgroup Crypto Cryptography * * Zed uses a simple interface to a cryptographic transform. * * @{ */ /** * @brief applies a crypto algorithm * * The function pointer cryptoalg_func_t points to a function that * implements a crypto transform, and provides a uniform API for * accessing crypto mechanisms. * * @param key location of secret key * * @param clear data to be authenticated but not encrypted * * @param clear_len length of data to be authenticated but not encrypted * * @param iv location to write the Initialization Vector (IV) * * @param protect location of the data to be encrypted and * authenticated (before the function call), and the ciphertext * and authentication tag (after the call) * * @param protected_len location of the length of the data to be * encrypted and authenticated (before the function call), and the * length of the ciphertext (after the call) * */ typedef err_status_t (*cryptoalg_func_t) (void *key, const void *clear, unsigned clear_len, void *iv, void *protect, unsigned *protected_len); typedef err_status_t (*cryptoalg_inv_t) (void *key, /* location of secret key */ const void *clear, /* data to be authenticated only */ unsigned clear_len, /* length of data to be authenticated only */ void *iv, /* location of iv */ void *opaque, /* data to be decrypted and authenticated */ unsigned *opaque_len /* location of the length of data to be * decrypted and authd (before and after) */ ); typedef struct cryptoalg_ctx_t { cryptoalg_func_t enc; cryptoalg_inv_t dec; unsigned key_len; unsigned iv_len; unsigned auth_tag_len; unsigned max_expansion; } cryptoalg_ctx_t; typedef cryptoalg_ctx_t *cryptoalg_t; #define cryptoalg_get_key_len(cryptoalg) ((cryptoalg)->key_len) #define cryptoalg_get_iv_len(cryptoalg) ((cryptoalg)->iv_len) #define cryptoalg_get_auth_tag_len(cryptoalg) ((cryptoalg)->auth_tag_len) int cryptoalg_get_id(cryptoalg_t c); cryptoalg_t cryptoalg_find_by_id(int id); /** * @} */ #endif /* CRYPTOALG_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/datatypes.h ================================================ /* * datatypes.h * * data types for bit vectors and finite fields * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef _DATATYPES_H #define _DATATYPES_H #include "integers.h" /* definitions of uint32_t, et cetera */ #include "alloc.h" #include #ifndef SRTP_KERNEL # include # include # include # ifdef HAVE_NETINET_IN_H # include # elif defined HAVE_WINSOCK2_H # include # endif #endif /* if DATATYPES_USE_MACROS is defined, then little functions are macros */ #define DATATYPES_USE_MACROS typedef union { uint8_t v8[2]; uint16_t value; } v16_t; typedef union { uint8_t v8[4]; uint16_t v16[2]; uint32_t value; } v32_t; typedef union { uint8_t v8[8]; uint16_t v16[4]; uint32_t v32[2]; uint64_t value; } v64_t; typedef union { uint8_t v8[16]; uint16_t v16[8]; uint32_t v32[4]; uint64_t v64[2]; } v128_t; /* some useful and simple math functions */ #define pow_2(X) ( (unsigned int)1 << (X) ) /* 2^X */ #define pow_minus_one(X) ( (X) ? -1 : 1 ) /* (-1)^X */ /* * octet_get_weight(x) returns the hamming weight (number of bits equal to * one) in the octet x */ int octet_get_weight(uint8_t octet); char * octet_bit_string(uint8_t x); #define MAX_PRINT_STRING_LEN 1024 char * octet_string_hex_string(const void *str, int length); char * v128_bit_string(v128_t *x); char * v128_hex_string(v128_t *x); uint8_t nibble_to_hex_char(uint8_t nibble); char * char_to_hex_string(char *x, int num_char); uint8_t hex_string_to_octet(char *s); /* * hex_string_to_octet_string(raw, hex, len) converts the hexadecimal * string at *hex (of length len octets) to the equivalent raw data * and writes it to *raw. * * if a character in the hex string that is not a hexadeciaml digit * (0123456789abcdefABCDEF) is encountered, the function stops writing * data to *raw * * the number of hex digits copied (which is two times the number of * octets in *raw) is returned */ int hex_string_to_octet_string(char *raw, char *hex, int len); v128_t hex_string_to_v128(char *s); void v128_copy_octet_string(v128_t *x, const uint8_t s[16]); void v128_left_shift(v128_t *x, int index); void v128_right_shift(v128_t *x, int index); /* * the following macros define the data manipulation functions * * If DATATYPES_USE_MACROS is defined, then these macros are used * directly (and function call overhead is avoided). Otherwise, * the macros are used through the functions defined in datatypes.c * (and the compiler provides better warnings). */ #define _v128_set_to_zero(x) \ ( \ (x)->v32[0] = 0, \ (x)->v32[1] = 0, \ (x)->v32[2] = 0, \ (x)->v32[3] = 0 \ ) #define _v128_copy(x, y) \ ( \ (x)->v32[0] = (y)->v32[0], \ (x)->v32[1] = (y)->v32[1], \ (x)->v32[2] = (y)->v32[2], \ (x)->v32[3] = (y)->v32[3] \ ) #define _v128_xor(z, x, y) \ ( \ (z)->v32[0] = (x)->v32[0] ^ (y)->v32[0], \ (z)->v32[1] = (x)->v32[1] ^ (y)->v32[1], \ (z)->v32[2] = (x)->v32[2] ^ (y)->v32[2], \ (z)->v32[3] = (x)->v32[3] ^ (y)->v32[3] \ ) #define _v128_and(z, x, y) \ ( \ (z)->v32[0] = (x)->v32[0] & (y)->v32[0], \ (z)->v32[1] = (x)->v32[1] & (y)->v32[1], \ (z)->v32[2] = (x)->v32[2] & (y)->v32[2], \ (z)->v32[3] = (x)->v32[3] & (y)->v32[3] \ ) #define _v128_or(z, x, y) \ ( \ (z)->v32[0] = (x)->v32[0] | (y)->v32[0], \ (z)->v32[1] = (x)->v32[1] | (y)->v32[1], \ (z)->v32[2] = (x)->v32[2] | (y)->v32[2], \ (z)->v32[3] = (x)->v32[3] | (y)->v32[3] \ ) #define _v128_complement(x) \ ( \ (x)->v32[0] = ~(x)->v32[0], \ (x)->v32[1] = ~(x)->v32[1], \ (x)->v32[2] = ~(x)->v32[2], \ (x)->v32[3] = ~(x)->v32[3] \ ) /* ok for NO_64BIT_MATH if it can compare uint64_t's (even as structures) */ #define _v128_is_eq(x, y) \ (((x)->v64[0] == (y)->v64[0]) && ((x)->v64[1] == (y)->v64[1])) #ifdef NO_64BIT_MATH #define _v128_xor_eq(z, x) \ ( \ (z)->v32[0] ^= (x)->v32[0], \ (z)->v32[1] ^= (x)->v32[1], \ (z)->v32[2] ^= (x)->v32[2], \ (z)->v32[3] ^= (x)->v32[3] \ ) #else #define _v128_xor_eq(z, x) \ ( \ (z)->v64[0] ^= (x)->v64[0], \ (z)->v64[1] ^= (x)->v64[1] \ ) #endif /* NOTE! This assumes an odd ordering! */ /* This will not be compatible directly with math on some processors */ /* bit 0 is first 32-bit word, low order bit. in little-endian, that's the first byte of the first 32-bit word. In big-endian, that's the 3rd byte of the first 32-bit word */ /* The get/set bit code is used by the replay code ONLY, and it doesn't really care which bit is which. AES does care which bit is which, but doesn't use the 128-bit get/set or 128-bit shifts */ #define _v128_get_bit(x, bit) \ ( \ ((((x)->v32[(bit) >> 5]) >> ((bit) & 31)) & 1) \ ) #define _v128_set_bit(x, bit) \ ( \ (((x)->v32[(bit) >> 5]) |= ((uint32_t)1 << ((bit) & 31))) \ ) #define _v128_clear_bit(x, bit) \ ( \ (((x)->v32[(bit) >> 5]) &= ~((uint32_t)1 << ((bit) & 31))) \ ) #define _v128_set_bit_to(x, bit, value) \ ( \ (value) ? _v128_set_bit(x, bit) : \ _v128_clear_bit(x, bit) \ ) #if 0 /* nothing uses this */ #ifdef WORDS_BIGENDIAN #define _v128_add(z, x, y) { \ uint64_t tmp; \ \ tmp = x->v32[3] + y->v32[3]; \ z->v32[3] = (uint32_t) tmp; \ \ tmp = x->v32[2] + y->v32[2] + (tmp >> 32); \ z->v32[2] = (uint32_t) tmp; \ \ tmp = x->v32[1] + y->v32[1] + (tmp >> 32); \ z->v32[1] = (uint32_t) tmp; \ \ tmp = x->v32[0] + y->v32[0] + (tmp >> 32); \ z->v32[0] = (uint32_t) tmp; \ } #else /* assume little endian architecture */ #define _v128_add(z, x, y) { \ uint64_t tmp; \ \ tmp = htonl(x->v32[3]) + htonl(y->v32[3]); \ z->v32[3] = ntohl((uint32_t) tmp); \ \ tmp = htonl(x->v32[2]) + htonl(y->v32[2]) \ + htonl(tmp >> 32); \ z->v32[2] = ntohl((uint32_t) tmp); \ \ tmp = htonl(x->v32[1]) + htonl(y->v32[1]) \ + htonl(tmp >> 32); \ z->v32[1] = ntohl((uint32_t) tmp); \ \ tmp = htonl(x->v32[0]) + htonl(y->v32[0]) \ + htonl(tmp >> 32); \ z->v32[0] = ntohl((uint32_t) tmp); \ } #endif /* WORDS_BIGENDIAN */ #endif /* 0 */ #ifdef DATATYPES_USE_MACROS /* little functions are really macros */ #define v128_set_to_zero(z) _v128_set_to_zero(z) #define v128_copy(z, x) _v128_copy(z, x) #define v128_xor(z, x, y) _v128_xor(z, x, y) #define v128_and(z, x, y) _v128_and(z, x, y) #define v128_or(z, x, y) _v128_or(z, x, y) #define v128_complement(x) _v128_complement(x) #define v128_is_eq(x, y) _v128_is_eq(x, y) #define v128_xor_eq(x, y) _v128_xor_eq(x, y) #define v128_get_bit(x, i) _v128_get_bit(x, i) #define v128_set_bit(x, i) _v128_set_bit(x, i) #define v128_clear_bit(x, i) _v128_clear_bit(x, i) #define v128_set_bit_to(x, i, y) _v128_set_bit_to(x, i, y) #else void v128_set_to_zero(v128_t *x); int v128_is_eq(const v128_t *x, const v128_t *y); void v128_copy(v128_t *x, const v128_t *y); void v128_xor(v128_t *z, v128_t *x, v128_t *y); void v128_and(v128_t *z, v128_t *x, v128_t *y); void v128_or(v128_t *z, v128_t *x, v128_t *y); void v128_complement(v128_t *x); int v128_get_bit(const v128_t *x, int i); void v128_set_bit(v128_t *x, int i) ; void v128_clear_bit(v128_t *x, int i); void v128_set_bit_to(v128_t *x, int i, int y); #endif /* DATATYPES_USE_MACROS */ /* * octet_string_is_eq(a,b, len) returns 1 if the length len strings a * and b are not equal, returns 0 otherwise */ int octet_string_is_eq(uint8_t *a, uint8_t *b, int len); void octet_string_set_to_zero(uint8_t *s, int len); #ifndef SRTP_KERNEL_LINUX /* * Convert big endian integers to CPU byte order. */ #ifdef WORDS_BIGENDIAN /* Nothing to do. */ # define be32_to_cpu(x) (x) # define be64_to_cpu(x) (x) #elif defined(HAVE_BYTESWAP_H) /* We have (hopefully) optimized versions in byteswap.h */ # include # define be32_to_cpu(x) bswap_32((x)) # define be64_to_cpu(x) bswap_64((x)) #else #if defined(__GNUC__) && defined(HAVE_X86) /* Fall back. */ static inline uint32_t be32_to_cpu(uint32_t v) { /* optimized for x86. */ asm("bswap %0" : "=r" (v) : "0" (v)); return v; } # else /* HAVE_X86 */ # ifdef HAVE_NETINET_IN_H # include # elif defined HAVE_WINSOCK2_H # include # endif # define be32_to_cpu(x) ntohl((x)) # endif /* HAVE_X86 */ static inline uint64_t be64_to_cpu(uint64_t v) { # ifdef NO_64BIT_MATH /* use the make64 functions to do 64-bit math */ v = make64(htonl(low32(v)),htonl(high32(v))); # else /* use the native 64-bit math */ v= (uint64_t)((be32_to_cpu((uint32_t)(v >> 32))) | (((uint64_t)be32_to_cpu((uint32_t)v)) << 32)); # endif return v; } #endif /* ! SRTP_KERNEL_LINUX */ #endif /* WORDS_BIGENDIAN */ #endif /* _DATATYPES_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/err.h ================================================ /* * err.h * * error status codes * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef ERR_H #define ERR_H #include "datatypes.h" /** * @defgroup Error Error Codes * * Error status codes are represented by the enumeration err_status_t. * * @{ */ /* * @brief err_status_t defines error codes. * * The enumeration err_status_t defines error codes. Note that the * value of err_status_ok is equal to zero, which can simplify error * checking somewhat. * */ typedef enum { err_status_ok = 0, /**< nothing to report */ err_status_fail = 1, /**< unspecified failure */ err_status_bad_param = 2, /**< unsupported parameter */ err_status_alloc_fail = 3, /**< couldn't allocate memory */ err_status_dealloc_fail = 4, /**< couldn't deallocate properly */ err_status_init_fail = 5, /**< couldn't initialize */ err_status_terminus = 6, /**< can't process as much data as requested */ err_status_auth_fail = 7, /**< authentication failure */ err_status_cipher_fail = 8, /**< cipher failure */ err_status_replay_fail = 9, /**< replay check failed (bad index) */ err_status_replay_old = 10, /**< replay check failed (index too old) */ err_status_algo_fail = 11, /**< algorithm failed test routine */ err_status_no_such_op = 12, /**< unsupported operation */ err_status_no_ctx = 13, /**< no appropriate context found */ err_status_cant_check = 14, /**< unable to perform desired validation */ err_status_key_expired = 15, /**< can't use key any more */ err_status_socket_err = 16, /**< error in use of socket */ err_status_signal_err = 17, /**< error in use POSIX signals */ err_status_nonce_bad = 18, /**< nonce check failed */ err_status_read_fail = 19, /**< couldn't read data */ err_status_write_fail = 20, /**< couldn't write data */ err_status_parse_err = 21, /**< error pasring data */ err_status_encode_err = 22, /**< error encoding data */ err_status_semaphore_err = 23,/**< error while using semaphores */ err_status_pfkey_err = 24 /**< error while using pfkey */ } err_status_t; /** * @} */ typedef enum { err_level_emergency = 0, err_level_alert, err_level_critical, err_level_error, err_level_warning, err_level_notice, err_level_info, err_level_debug, err_level_none } err_reporting_level_t; /* * err_reporting_init prepares the error system. If * ERR_REPORTING_SYSLOG is defined, it will open syslog. * * The ident argument is a string that will be prepended to * all syslog messages. It is conventionally argv[0]. */ err_status_t err_reporting_init(char *ident); #ifdef SRTP_KERNEL_LINUX extern err_reporting_level_t err_level; #else /* * keydaemon_report_error reports a 'printf' formatted error * string, followed by a an arg list. The priority argument * is equivalent to that defined for syslog. * * Errors will be reported to ERR_REPORTING_FILE, if defined, and to * syslog, if ERR_REPORTING_SYSLOG is defined. * */ void err_report(int priority, char *format, ...); #endif /* ! SRTP_KERNEL_LINUX */ /* * debug_module_t defines a debug module */ typedef struct { int on; /* 1 if debugging is on, 0 if it is off */ char *name; /* printable name for debug module */ } debug_module_t; #ifdef ENABLE_DEBUGGING #define debug_on(mod) (mod).on = 1 #define debug_off(mod) (mod).on = 0 /* use err_report() to report debug message */ #define debug_print(mod, format, arg) \ if (mod.on) err_report(err_level_debug, ("%s: " format "\n"), mod.name, arg) #define debug_print2(mod, format, arg1,arg2) \ if (mod.on) err_report(err_level_debug, ("%s: " format "\n"), mod.name, arg1,arg2) #else /* define macros to do nothing */ #define debug_print(mod, format, arg) #define debug_on(mod) #define debug_off(mod) #endif #endif /* ERR_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/gf2_8.h ================================================ /* * gf2_8.h * * GF(256) implementation * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef GF2_8_H #define GF2_8_H #include "datatypes.h" /* for uint8_t definition */ typedef uint8_t gf2_8; #define gf2_8_field_polynomial 0x1B /* * gf2_8_shift(x) returns */ /* * gf2_8_shift(z) returns the result of the GF(2^8) 'multiply by x' * operation, using the field representation from AES; that is, the * next gf2_8 value in the cyclic representation of that field. The * value z should be an uint8_t. */ #define gf2_8_shift(z) (((z) & 128) ? \ (((z) << 1) ^ gf2_8_field_polynomial) : ((z) << 1)) gf2_8 gf2_8_compute_inverse(gf2_8 x); void test_gf2_8(void); gf2_8 gf2_8_multiply(gf2_8 x, gf2_8 y); #endif /* GF2_8_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/hmac.h ================================================ /* * hmac.h * * interface to hmac auth_type_t * * David A. McGrew * Cisco Systems, Inc. * */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef HMAC_H #define HMAC_H #include "auth.h" #include "sha1.h" typedef struct { uint8_t opad[64]; sha1_ctx_t ctx; sha1_ctx_t init_ctx; } hmac_ctx_t; err_status_t hmac_alloc(auth_t **a, int key_len, int out_len); err_status_t hmac_dealloc(auth_t *a); err_status_t hmac_init(hmac_ctx_t *state, const uint8_t *key, int key_len); err_status_t hmac_start(hmac_ctx_t *state); err_status_t hmac_update(hmac_ctx_t *state, const uint8_t *message, int msg_octets); err_status_t hmac_compute(hmac_ctx_t *state, const void *message, int msg_octets, int tag_len, uint8_t *result); #endif /* HMAC_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/integers.h ================================================ /* * integers.h * * defines integer types (or refers to their definitions) * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef INTEGERS_H #define INTEGERS_H #include "srtp_config.h" /* configuration file, using autoconf */ #ifdef SRTP_KERNEL #include "kernel_compat.h" #else /* SRTP_KERNEL */ /* use standard integer definitions, if they're available */ #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_INT_TYPES_H # include /* this exists on Sun OS */ #endif #ifdef HAVE_MACHINE_TYPES_H # include #endif /* Can we do 64 bit integers? */ #ifndef HAVE_UINT64_T # if SIZEOF_UNSIGNED_LONG == 8 typedef unsigned long uint64_t; # elif SIZEOF_UNSIGNED_LONG_LONG == 8 typedef unsigned long long uint64_t; # else # define NO_64BIT_MATH 1 # endif #endif /* Reasonable defaults for 32 bit machines - you may need to * edit these definitions for your own machine. */ #ifndef HAVE_UINT8_T typedef unsigned char uint8_t; #endif #ifndef HAVE_UINT16_T typedef unsigned short int uint16_t; #endif #ifndef HAVE_UINT32_T typedef unsigned int uint32_t; #endif #ifdef NO_64BIT_MATH typedef double uint64_t; /* assert that sizeof(double) == 8 */ extern uint64_t make64(uint32_t high, uint32_t low); extern uint32_t high32(uint64_t value); extern uint32_t low32(uint64_t value); #endif #endif /* SRTP_KERNEL */ /* These macros are to load and store 32-bit values from un-aligned addresses. This is required for processors that do not allow unaligned loads. */ #ifdef ALIGNMENT_32BIT_REQUIRED /* Note that if it's in a variable, you can memcpy it */ #ifdef WORDS_BIGENDIAN #define PUT_32(addr,value) \ { \ ((unsigned char *) (addr))[0] = (value >> 24); \ ((unsigned char *) (addr))[1] = (value >> 16) & 0xff; \ ((unsigned char *) (addr))[2] = (value >> 8) & 0xff; \ ((unsigned char *) (addr))[3] = (value) & 0xff; \ } #define GET_32(addr) ((((unsigned char *) (addr))[0] << 24) | \ (((unsigned char *) (addr))[1] << 16) | \ (((unsigned char *) (addr))[2] << 8) | \ (((unsigned char *) (addr))[3])) #else #define PUT_32(addr,value) \ { \ ((unsigned char *) (addr))[3] = (value >> 24); \ ((unsigned char *) (addr))[2] = (value >> 16) & 0xff; \ ((unsigned char *) (addr))[1] = (value >> 8) & 0xff; \ ((unsigned char *) (addr))[0] = (value) & 0xff; \ } #define GET_32(addr) ((((unsigned char *) (addr))[3] << 24) | \ (((unsigned char *) (addr))[2] << 16) | \ (((unsigned char *) (addr))[1] << 8) | \ (((unsigned char *) (addr))[0])) #endif // WORDS_BIGENDIAN #else #define PUT_32(addr,value) *(((uint32_t *) (addr)) = (value) #define GET_32(addr) (*(((uint32_t *) (addr))) #endif #endif /* INTEGERS_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/kernel_compat.h ================================================ /* * kernel_compat.h * * Compatibility stuff for building in kernel context where standard * C headers and library are not available. * * Marcus Sundberg * Ingate Systems AB */ /* * * Copyright(c) 2005 Ingate Systems AB * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the author(s) nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef KERNEL_COMPAT_H #define KERNEL_COMPAT_H #ifdef SRTP_KERNEL_LINUX #include #include #include #include #include #define err_report(priority, ...) \ do {\ if (priority <= err_level) {\ printk(__VA_ARGS__);\ }\ }while(0) #define clock() (jiffies) #define time(x) (jiffies) /* rand() implementation. */ #define RAND_MAX 32767 static inline int rand(void) { uint32_t temp; get_random_bytes(&temp, sizeof(temp)); return temp % (RAND_MAX+1); } /* stdio/stdlib implementation. */ #define printf(...) printk(__VA_ARGS__) #define exit(n) panic("%s:%d: exit(%d)\n", __FILE__, __LINE__, (n)) #endif /* SRTP_KERNEL_LINUX */ #endif /* KERNEL_COMPAT_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/key.h ================================================ /* * key.h * * key usage limits enforcement * * David A. Mcgrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef KEY_H #define KEY_H #include "rdbx.h" /* for xtd_seq_num_t */ #include "err.h" typedef struct key_limit_ctx_t *key_limit_t; typedef enum { key_event_normal, key_event_soft_limit, key_event_hard_limit } key_event_t; err_status_t key_limit_set(key_limit_t key, const xtd_seq_num_t s); err_status_t key_limit_clone(key_limit_t original, key_limit_t *new_key); err_status_t key_limit_check(const key_limit_t key); key_event_t key_limit_update(key_limit_t key); typedef enum { key_state_normal, key_state_past_soft_limit, key_state_expired } key_state_t; typedef struct key_limit_ctx_t { xtd_seq_num_t num_left; key_state_t state; } key_limit_ctx_t; #endif /* KEY_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/null_auth.h ================================================ /* * null-auth.h * * David A. McGrew * Cisco Systems, Inc. * */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef NULL_AUTH_H #define NULL_AUTH_H #include "auth.h" typedef struct { char foo; } null_auth_ctx_t; err_status_t null_auth_alloc(auth_t **a, int key_len, int out_len); err_status_t null_auth_dealloc(auth_t *a); err_status_t null_auth_init(null_auth_ctx_t *state, const uint8_t *key, int key_len); err_status_t null_auth_compute (null_auth_ctx_t *state, uint8_t *message, int msg_octets, int tag_len, uint8_t *result); #endif /* NULL_AUTH_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/null_cipher.h ================================================ /* * null-cipher.h * * header file for the null cipher * * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef NULL_CIPHER_H #define NULL_CIPHER_H #include "datatypes.h" #include "cipher.h" typedef struct { char foo ;/* empty, for now */ } null_cipher_ctx_t; /* * none of these functions do anything (though future versions may keep * track of bytes encrypted, number of instances, and/or other info). */ err_status_t null_cipher_init(null_cipher_ctx_t *c, const uint8_t *key); err_status_t null_cipher_set_segment(null_cipher_ctx_t *c, unsigned long index); err_status_t null_cipher_encrypt(null_cipher_ctx_t *c, unsigned char *buf, unsigned int *bytes_to_encr); err_status_t null_cipher_encrypt_aligned(null_cipher_ctx_t *c, unsigned char *buf, int bytes_to_encr); #endif /* NULL_CIPHER_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/prng.h ================================================ /* * prng.h * * pseudorandom source * * David A. McGrew * Cisco Systems, Inc. */ #ifndef PRNG_H #define PRNG_H #include "rand_source.h" /* for rand_source_func_t definition */ #include "aes.h" /* for aes */ #include "aes_icm.h" /* for aes ctr */ #define MAX_PRNG_OUT_LEN 0xffffffffU /* * x917_prng is an ANSI X9.17-like AES-based PRNG */ typedef struct { v128_t state; /* state data */ aes_expanded_key_t key; /* secret key */ uint32_t octet_count; /* number of octets output since last init */ rand_source_func_t rand; /* random source for re-initialization */ } x917_prng_t; err_status_t x917_prng_init(rand_source_func_t random_source); err_status_t x917_prng_get_octet_string(uint8_t *dest, uint32_t len); /* * ctr_prng is an AES-CTR based PRNG */ typedef struct { uint32_t octet_count; /* number of octets output since last init */ aes_icm_ctx_t state; /* state data */ rand_source_func_t rand; /* random source for re-initialization */ } ctr_prng_t; err_status_t ctr_prng_init(rand_source_func_t random_source); err_status_t ctr_prng_get_octet_string(void *dest, uint32_t len); #endif ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/rand_source.h ================================================ /* * rand_source.h * * implements a random source based on /dev/random * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright(c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef RAND_SOURCE #define RAND_SOURCE #include "err.h" #include "datatypes.h" err_status_t rand_source_init(void); /* * rand_source_get_octet_string() writes a random octet string. * * The function call rand_source_get_octet_string(dest, len) writes * len octets of random data to the location to which dest points, * and returns an error code. This error code should be checked, * and if a failure is reported, the data in the buffer MUST NOT * be used. * * warning: If the return code is not checked, then non-random * data may inadvertently be used. * * returns: * - err_status_ok if no problems occured. * - [other] a problem occured, and no assumptions should * be made about the contents of the destination * buffer. */ err_status_t rand_source_get_octet_string(void *dest, uint32_t length); err_status_t rand_source_deinit(void); /* * function prototype for a random source function * * A rand_source_func_t writes num_octets at the location indicated by * dest and returns err_status_ok. Any other return value indicates * failure. */ typedef err_status_t (*rand_source_func_t) (void *dest, uint32_t num_octets); #endif /* RAND_SOURCE */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/rdb.h ================================================ /* * replay-database.h * * interface for a replay database for packet security * * David A. McGrew * Cisco Systems, Inc. */ #ifndef REPLAY_DB_H #define REPLAY_DB_H #include "integers.h" /* for uint32_t */ #include "datatypes.h" /* for v128_t */ #include "err.h" /* for err_status_t */ /* * if the ith least significant bit is one, then the packet index * window_end-i is in the database */ typedef struct { uint32_t window_start; /* packet index of the first bit in bitmask */ v128_t bitmask; } rdb_t; #define rdb_bits_in_bitmask (8*sizeof(v128_t)) /* * rdb init * * initalizes rdb * * returns err_status_ok on success, err_status_t_fail otherwise */ err_status_t rdb_init(rdb_t *rdb); /* * rdb_check * * checks to see if index appears in rdb * * returns err_status_fail if the index already appears in rdb, * returns err_status_ok otherwise */ err_status_t rdb_check(const rdb_t *rdb, uint32_t index); /* * rdb_add_index * * adds index to rdb_t (and does *not* check if index appears in db) * * returns err_status_ok on success, err_status_fail otherwise * */ err_status_t rdb_add_index(rdb_t *rdb, uint32_t index); /* * the functions rdb_increment() and rdb_get_value() are for use by * senders, not receivers - DO NOT use these functions on the same * rdb_t upon which rdb_add_index is used! */ /* * rdb_increment(db) increments the sequence number in db, if it is * not too high * * return values: * * err_status_ok no problem * err_status_key_expired sequence number too high * */ err_status_t rdb_increment(rdb_t *rdb); /* * rdb_get_value(db) returns the current sequence number of db */ uint32_t rdb_get_value(const rdb_t *rdb); #endif /* REPLAY_DB_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/rdbx.h ================================================ /* * rdbx.h * * replay database with extended packet indices, using a rollover counter * * David A. McGrew * Cisco Systems, Inc. * */ #ifndef RDBX_H #define RDBX_H #include "datatypes.h" #include "err.h" /* #define ROC_TEST */ #ifndef ROC_TEST typedef uint16_t sequence_number_t; /* 16 bit sequence number */ typedef uint32_t rollover_counter_t; /* 32 bit rollover counter */ #else /* use small seq_num and roc datatypes for testing purposes */ typedef unsigned char sequence_number_t; /* 8 bit sequence number */ typedef uint16_t rollover_counter_t; /* 16 bit rollover counter */ #endif #define seq_num_median (1 << (8*sizeof(sequence_number_t) - 1)) #define seq_num_max (1 << (8*sizeof(sequence_number_t))) /* * An xtd_seq_num_t is a 64-bit unsigned integer used as an 'extended' * sequence number. */ typedef uint64_t xtd_seq_num_t; /* * An rdbx_t is a replay database with extended range; it uses an * xtd_seq_num_t and a bitmask of recently received indices. */ typedef struct { xtd_seq_num_t index; v128_t bitmask; } rdbx_t; /* * rdbx_init(rdbx_ptr) * * initializes the rdbx pointed to by its argument, setting the * rollover counter and sequence number to zero */ err_status_t rdbx_init(rdbx_t *rdbx); /* * rdbx_estimate_index(rdbx, guess, s) * * given an rdbx and a sequence number s (from a newly arrived packet), * sets the contents of *guess to contain the best guess of the packet * index to which s corresponds, and returns the difference between * *guess and the locally stored synch info */ int rdbx_estimate_index(const rdbx_t *rdbx, xtd_seq_num_t *guess, sequence_number_t s); /* * rdbx_check(rdbx, delta); * * rdbx_check(&r, delta) checks to see if the xtd_seq_num_t * which is at rdbx->window_start + delta is in the rdb * */ err_status_t rdbx_check(const rdbx_t *rdbx, int difference); /* * replay_add_index(rdbx, delta) * * adds the xtd_seq_num_t at rdbx->window_start + delta to replay_db * (and does *not* check if that xtd_seq_num_t appears in db) * * this function should be called *only* after replay_check has * indicated that the index does not appear in the rdbx, and a mutex * should protect the rdbx between these calls if necessary. */ err_status_t rdbx_add_index(rdbx_t *rdbx, int delta); /* * xtd_seq_num_t functions - these are *internal* functions of rdbx, and * shouldn't be used to manipulate rdbx internal values. use the rdbx * api instead! */ /* index_init(&pi) initializes a packet index pi (sets it to zero) */ void index_init(xtd_seq_num_t *pi); /* index_advance(&pi, s) advances a xtd_seq_num_t forward by s */ void index_advance(xtd_seq_num_t *pi, sequence_number_t s); /* * index_guess(local, guess, s) * * given a xtd_seq_num_t local (which represents the highest * known-to-be-good index) and a sequence number s (from a newly * arrived packet), sets the contents of *guess to contain the best * guess of the packet index to which s corresponds, and returns the * difference between *guess and *local */ int index_guess(const xtd_seq_num_t *local, xtd_seq_num_t *guess, sequence_number_t s); #endif /* RDBX_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/sha1.h ================================================ /* * sha1.h * * interface to the Secure Hash Algorithm v.1 (SHA-1), specified in * FIPS 180-1 * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef SHA1_H #define SHA1_H #include "err.h" #include "datatypes.h" typedef struct { uint32_t H[5]; /* state vector */ uint32_t M[16]; /* message buffer */ int octets_in_buffer; /* octets of message in buffer */ uint32_t num_bits_in_msg; /* total number of bits in message */ } sha1_ctx_t; /* * sha1(&ctx, msg, len, output) hashes the len octets starting at msg * into the SHA1 context, then writes the result to the 20 octets at * output * */ void sha1(const uint8_t *message, int octets_in_msg, uint32_t output[5]); /* * sha1_init(&ctx) initializes the SHA1 context ctx * * sha1_update(&ctx, msg, len) hashes the len octets starting at msg * into the SHA1 context * * sha1_final(&ctx, output) performs the final processing of the SHA1 * context and writes the result to the 20 octets at output * */ void sha1_init(sha1_ctx_t *ctx); void sha1_update(sha1_ctx_t *ctx, const uint8_t *M, int octets_in_msg); void sha1_final(sha1_ctx_t *ctx, uint32_t output[5]); /* * The sha1_core function is INTERNAL to SHA-1, but it is declared * here because it is also used by the cipher SEAL 3.0 in its key * setup algorithm. */ /* * sha1_core(M, H) computes the core sha1 compression function, where M is * the next part of the message and H is the intermediate state {H0, * H1, ...} * * this function does not do any of the padding required in the * complete sha1 function */ void sha1_core(const uint32_t M[16], uint32_t hash_value[5]); #endif /* SHA1_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/stat.h ================================================ /* * stats.h * * interface to statistical test functions * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright(c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef STAT_H #define STAT_H #include "datatypes.h" /* for uint8_t */ #include "err.h" /* for err_status_t */ #include "rand_source.h" /* for rand_source_func_t definition */ err_status_t stat_test_monobit(uint8_t *data); err_status_t stat_test_poker(uint8_t *data); err_status_t stat_test_runs(uint8_t *data); err_status_t stat_test_rand_source(rand_source_func_t rs); err_status_t stat_test_rand_source_with_repetition(rand_source_func_t source, unsigned num_trials); #endif /* STAT_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/include/xfm.h ================================================ /* * xfm.h * * interface for abstract crypto transform * * David A. McGrew * Cisco Systems, Inc. */ #ifndef XFM_H #define XFM_H #include "crypto_kernel.h" #include "err.h" /** * @defgroup Crypto Cryptography * * A simple interface to an abstract cryptographic transform that * provides both confidentiality and message authentication. * * @{ */ /** * @brief applies a crypto transform * * The function pointer xfm_func_t points to a function that * implements a crypto transform, and provides a uniform API for * accessing crypto mechanisms. * * @param key location of secret key * * @param clear data to be authenticated only * * @param clear_len length of data to be authenticated only * * @param iv location to write the Initialization Vector (IV) * * @param protect location of the data to be encrypted and * authenticated (before the function call), and the ciphertext * and authentication tag (after the call) * * @param protected_len location of the length of the data to be * encrypted and authenticated (before the function call), and the * length of the ciphertext (after the call) * * @param auth_tag location to write auth tag */ typedef err_status_t (*xfm_func_t) (void *key, void *clear, unsigned clear_len, void *iv, void *protect, unsigned *protected_len, void *auth_tag ); typedef err_status_t (*xfm_inv_t) (void *key, /* location of secret key */ void *clear, /* data to be authenticated only */ unsigned clear_len, /* length of data to be authenticated only */ void *iv, /* location of iv */ void *opaque, /* data to be decrypted and authenticated */ unsigned *opaque_len, /* location of the length of data to be * decrypted and authd (before and after) */ void *auth_tag /* location of auth tag */ ); typedef struct xfm_ctx_t { xfm_func_t func; xfm_inv_t inv; unsigned key_len; unsigned iv_len; unsigned auth_tag_len; } xfm_ctx_t; typedef xfm_ctx_t *xfm_t; #define xfm_get_key_len(xfm) ((xfm)->key_len) #define xfm_get_iv_len(xfm) ((xfm)->iv_len) #define xfm_get_auth_tag_len(xfm) ((xfm)->auth_tag_len) /* cryptoalgo - 5/28 */ typedef err_status_t (*cryptoalg_func_t) (void *key, void *clear, unsigned clear_len, void *iv, void *opaque, unsigned *opaque_len ); typedef err_status_t (*cryptoalg_inv_t) (void *key, /* location of secret key */ void *clear, /* data to be authenticated only */ unsigned clear_len, /* length of data to be authenticated only */ void *iv, /* location of iv */ void *opaque, /* data to be decrypted and authenticated */ unsigned *opaque_len /* location of the length of data to be * decrypted and authd (before and after) */ ); typedef struct cryptoalg_ctx_t { cryptoalg_func_t enc; cryptoalg_inv_t dec; unsigned key_len; unsigned iv_len; unsigned auth_tag_len; unsigned max_expansion; } cryptoalg_ctx_t; typedef cryptoalg_ctx_t *cryptoalg_t; #define cryptoalg_get_key_len(cryptoalg) ((cryptoalg)->key_len) #define cryptoalg_get_iv_len(cryptoalg) ((cryptoalg)->iv_len) #define cryptoalg_get_auth_tag_len(cryptoalg) ((cryptoalg)->auth_tag_len) /** * @} */ #endif /* XFM_H */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/kernel/alloc.c ================================================ /* * alloc.c * * memory allocation and deallocation * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "alloc.h" #include "crypto_kernel.h" /* the debug module for memory allocation */ debug_module_t mod_alloc = { 0, /* debugging is off by default */ "alloc" /* printable name for module */ }; /* * Nota bene: the debugging statements for crypto_alloc() and * crypto_free() have identical prefixes, which include the addresses * of the memory locations on which they are operating. This fact can * be used to locate memory leaks, by turning on memory debugging, * grepping for 'alloc', then matching alloc and free calls by * address. */ #ifdef SRTP_KERNEL_LINUX #include void * crypto_alloc(size_t size) { void *ptr; ptr = kmalloc(size, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); if (ptr) { debug_print(mod_alloc, "(location: %p) allocated", ptr); } else debug_print(mod_alloc, "allocation failed (asked for %d bytes)\n", size); return ptr; } void crypto_free(void *ptr) { debug_print(mod_alloc, "(location: %p) freed", ptr); kfree(ptr); } #elif defined(HAVE_STDLIB_H) void * crypto_alloc(size_t size) { void *ptr; ptr = malloc(size); if (ptr) { debug_print(mod_alloc, "(location: %p) allocated", ptr); } else debug_print(mod_alloc, "allocation failed (asked for %d bytes)\n", size); return ptr; } void crypto_free(void *ptr) { debug_print(mod_alloc, "(location: %p) freed", ptr); free(ptr); } #else /* we need to define our own memory allocation routines */ #error no memory allocation defined yet #endif ================================================ FILE: deps/pjsip/third_party/srtp/crypto/kernel/crypto_kernel.c ================================================ /* * crypto_kernel.c * * header for the cryptographic kernel * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright(c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "alloc.h" #include "crypto_kernel.h" /* the debug module for the crypto_kernel */ debug_module_t mod_crypto_kernel = { 0, /* debugging is off by default */ "crypto kernel" /* printable name for module */ }; /* * other debug modules that can be included in the kernel */ extern debug_module_t mod_auth; extern debug_module_t mod_cipher; extern debug_module_t mod_stat; extern debug_module_t mod_alloc; /* * cipher types that can be included in the kernel */ extern cipher_type_t null_cipher; extern cipher_type_t aes_icm; extern cipher_type_t aes_cbc; /* * auth func types that can be included in the kernel */ extern auth_type_t null_auth; extern auth_type_t hmac; /* crypto_kernel is a global variable, the only one of its datatype */ crypto_kernel_t crypto_kernel = { crypto_kernel_state_insecure, /* start off in insecure state */ NULL, /* no cipher types yet */ NULL, /* no auth types yet */ NULL /* no debug modules yet */ }; #define MAX_RNG_TRIALS 25 err_status_t crypto_kernel_init() { err_status_t status; /* check the security state */ if (crypto_kernel.state == crypto_kernel_state_secure) { /* * we're already in the secure state, but we've been asked to * re-initialize, so we just re-run the self-tests and then return */ return crypto_kernel_status(); } /* initialize error reporting system */ status = err_reporting_init("crypto"); if (status) return status; /* load debug modules */ status = crypto_kernel_load_debug_module(&mod_crypto_kernel); if (status) return status; status = crypto_kernel_load_debug_module(&mod_auth); if (status) return status; status = crypto_kernel_load_debug_module(&mod_cipher); if (status) return status; status = crypto_kernel_load_debug_module(&mod_stat); if (status) return status; status = crypto_kernel_load_debug_module(&mod_alloc); if (status) return status; /* initialize random number generator */ status = rand_source_init(); if (status) return status; /* run FIPS-140 statistical tests on rand_source */ status = stat_test_rand_source_with_repetition(rand_source_get_octet_string, MAX_RNG_TRIALS); if (status) return status; /* initialize pseudorandom number generator */ status = ctr_prng_init(rand_source_get_octet_string); if (status) return status; /* run FIPS-140 statistical tests on ctr_prng */ status = stat_test_rand_source_with_repetition(ctr_prng_get_octet_string, MAX_RNG_TRIALS); if (status) return status; /* load cipher types */ status = crypto_kernel_load_cipher_type(&null_cipher, NULL_CIPHER); if (status) return status; status = crypto_kernel_load_cipher_type(&aes_icm, AES_128_ICM); if (status) return status; status = crypto_kernel_load_cipher_type(&aes_cbc, AES_128_CBC); if (status) return status; /* load auth func types */ status = crypto_kernel_load_auth_type(&null_auth, NULL_AUTH); if (status) return status; status = crypto_kernel_load_auth_type(&hmac, HMAC_SHA1); if (status) return status; /* change state to secure */ crypto_kernel.state = crypto_kernel_state_secure; return err_status_ok; } err_status_t crypto_kernel_status() { err_status_t status; kernel_cipher_type_t *ctype = crypto_kernel.cipher_type_list; kernel_auth_type_t *atype = crypto_kernel.auth_type_list; kernel_debug_module_t *dm = crypto_kernel.debug_module_list; /* run FIPS-140 statistical tests on rand_source */ printf("testing rand_source..."); status = stat_test_rand_source_with_repetition(rand_source_get_octet_string, MAX_RNG_TRIALS); if (status) { printf("failed\n"); crypto_kernel.state = crypto_kernel_state_insecure; return status; } printf("passed\n"); /* for each cipher type, describe and test */ while(ctype != NULL) { printf("cipher: %s\n", ctype->cipher_type->description); printf(" instance count: %d\n", ctype->cipher_type->ref_count); printf(" self-test: "); status = cipher_type_self_test(ctype->cipher_type); if (status) { printf("failed with error code %d\n", status); exit(status); } printf("passed\n"); ctype = ctype->next; } /* for each auth type, describe and test */ while(atype != NULL) { printf("auth func: %s\n", atype->auth_type->description); printf(" instance count: %d\n", atype->auth_type->ref_count); printf(" self-test: "); status = auth_type_self_test(atype->auth_type); if (status) { printf("failed with error code %d\n", status); exit(status); } printf("passed\n"); atype = atype->next; } /* describe each debug module */ printf("debug modules loaded:\n"); while (dm != NULL) { printf(" %s ", dm->mod->name); if (dm->mod->on) printf("(on)\n"); else printf("(off)\n"); dm = dm->next; } return err_status_ok; } err_status_t crypto_kernel_list_debug_modules() { kernel_debug_module_t *dm = crypto_kernel.debug_module_list; /* describe each debug module */ printf("debug modules loaded:\n"); while (dm != NULL) { printf(" %s ", dm->mod->name); if (dm->mod->on) printf("(on)\n"); else printf("(off)\n"); dm = dm->next; } return err_status_ok; } err_status_t crypto_kernel_shutdown() { err_status_t status; /* * free dynamic memory used in crypto_kernel at present */ /* walk down cipher type list, freeing memory */ while (crypto_kernel.cipher_type_list != NULL) { kernel_cipher_type_t *ctype = crypto_kernel.cipher_type_list; crypto_kernel.cipher_type_list = ctype->next; debug_print(mod_crypto_kernel, "freeing memory for cipher %s", ctype->cipher_type->description); crypto_free(ctype); } /* walk down authetication module list, freeing memory */ while (crypto_kernel.auth_type_list != NULL) { kernel_auth_type_t *atype = crypto_kernel.auth_type_list; crypto_kernel.auth_type_list = atype->next; debug_print(mod_crypto_kernel, "freeing memory for authentication %s", atype->auth_type->description); crypto_free(atype); } /* walk down debug module list, freeing memory */ while (crypto_kernel.debug_module_list != NULL) { kernel_debug_module_t *kdm = crypto_kernel.debug_module_list; crypto_kernel.debug_module_list = kdm->next; debug_print(mod_crypto_kernel, "freeing memory for debug module %s", kdm->mod->name); crypto_free(kdm); } /* de-initialize random number generator */ status = rand_source_deinit(); if (status) return status; /* return to insecure state */ crypto_kernel.state = crypto_kernel_state_insecure; return err_status_ok; } err_status_t crypto_kernel_load_cipher_type(cipher_type_t *new_ct, cipher_type_id_t id) { kernel_cipher_type_t *ctype, *new_ctype; err_status_t status; /* defensive coding */ if (new_ct == NULL) return err_status_bad_param; /* check cipher type by running self-test */ status = cipher_type_self_test(new_ct); if (status) { return status; } /* walk down list, checking if this type is in the list already */ ctype = crypto_kernel.cipher_type_list; while (ctype != NULL) { if ((new_ct == ctype->cipher_type) || (id == ctype->id)) return err_status_bad_param; ctype = ctype->next; } /* put new_ct at the head of the list */ /* allocate memory */ new_ctype = (kernel_cipher_type_t *) crypto_alloc(sizeof(kernel_cipher_type_t)); if (new_ctype == NULL) return err_status_alloc_fail; /* set fields */ new_ctype->cipher_type = new_ct; new_ctype->id = id; new_ctype->next = crypto_kernel.cipher_type_list; /* set head of list to new cipher type */ crypto_kernel.cipher_type_list = new_ctype; /* load debug module, if there is one present */ if (new_ct->debug != NULL) crypto_kernel_load_debug_module(new_ct->debug); /* we could check for errors here */ return err_status_ok; } err_status_t crypto_kernel_load_auth_type(auth_type_t *new_at, auth_type_id_t id) { kernel_auth_type_t *atype, *new_atype; err_status_t status; /* defensive coding */ if (new_at == NULL) return err_status_bad_param; /* check auth type by running self-test */ status = auth_type_self_test(new_at); if (status) { return status; } /* walk down list, checking if this type is in the list already */ atype = crypto_kernel.auth_type_list; while (atype != NULL) { if ((new_at == atype->auth_type) || (id == atype->id)) return err_status_bad_param; atype = atype->next; } /* put new_at at the head of the list */ /* allocate memory */ new_atype = (kernel_auth_type_t *)crypto_alloc(sizeof(kernel_auth_type_t)); if (new_atype == NULL) return err_status_alloc_fail; /* set fields */ new_atype->auth_type = new_at; new_atype->id = id; new_atype->next = crypto_kernel.auth_type_list; /* set head of list to new auth type */ crypto_kernel.auth_type_list = new_atype; /* load debug module, if there is one present */ if (new_at->debug != NULL) crypto_kernel_load_debug_module(new_at->debug); /* we could check for errors here */ return err_status_ok; } cipher_type_t * crypto_kernel_get_cipher_type(cipher_type_id_t id) { kernel_cipher_type_t *ctype; /* walk down list, looking for id */ ctype = crypto_kernel.cipher_type_list; while (ctype != NULL) { if (id == ctype->id) return ctype->cipher_type; ctype = ctype->next; } /* haven't found the right one, indicate failure by returning NULL */ return NULL; } err_status_t crypto_kernel_alloc_cipher(cipher_type_id_t id, cipher_pointer_t *cp, int key_len) { cipher_type_t *ct; /* * if the crypto_kernel is not yet initialized, we refuse to allocate * any ciphers - this is a bit extra-paranoid */ if (crypto_kernel.state != crypto_kernel_state_secure) return err_status_init_fail; ct = crypto_kernel_get_cipher_type(id); if (!ct) return err_status_fail; return ((ct)->alloc(cp, key_len)); } auth_type_t * crypto_kernel_get_auth_type(auth_type_id_t id) { kernel_auth_type_t *atype; /* walk down list, looking for id */ atype = crypto_kernel.auth_type_list; while (atype != NULL) { if (id == atype->id) return atype->auth_type; atype = atype->next; } /* haven't found the right one, indicate failure by returning NULL */ return NULL; } err_status_t crypto_kernel_alloc_auth(auth_type_id_t id, auth_pointer_t *ap, int key_len, int tag_len) { auth_type_t *at; /* * if the crypto_kernel is not yet initialized, we refuse to allocate * any auth functions - this is a bit extra-paranoid */ if (crypto_kernel.state != crypto_kernel_state_secure) return err_status_init_fail; at = crypto_kernel_get_auth_type(id); if (!at) return err_status_fail; return ((at)->alloc(ap, key_len, tag_len)); } err_status_t crypto_kernel_load_debug_module(debug_module_t *new_dm) { kernel_debug_module_t *kdm, *new; /* defensive coding */ if (new_dm == NULL) return err_status_bad_param; /* walk down list, checking if this type is in the list already */ kdm = crypto_kernel.debug_module_list; while (kdm != NULL) { if (strncmp(new_dm->name, kdm->mod->name, 64) == 0) return err_status_bad_param; kdm = kdm->next; } /* put new_dm at the head of the list */ /* allocate memory */ new = (kernel_debug_module_t *)crypto_alloc(sizeof(kernel_debug_module_t)); if (new == NULL) return err_status_alloc_fail; /* set fields */ new->mod = new_dm; new->next = crypto_kernel.debug_module_list; /* set head of list to new cipher type */ crypto_kernel.debug_module_list = new; return err_status_ok; } err_status_t crypto_kernel_set_debug_module(char *name, int on) { kernel_debug_module_t *kdm; /* walk down list, checking if this type is in the list already */ kdm = crypto_kernel.debug_module_list; while (kdm != NULL) { if (strncmp(name, kdm->mod->name, 64) == 0) { kdm->mod->on = on; return err_status_ok; } kdm = kdm->next; } return err_status_fail; } err_status_t crypto_get_random(unsigned char *buffer, unsigned int length) { if (crypto_kernel.state == crypto_kernel_state_secure) return ctr_prng_get_octet_string(buffer, length); else return err_status_fail; } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/kernel/err.c ================================================ /* * err.c * * error status reporting functions * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright(c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "err.h" #ifdef ERR_REPORTING_SYSLOG # ifdef HAVE_SYSLOG_H # include # endif #endif /* err_level reflects the level of errors that are reported */ err_reporting_level_t err_level = err_level_none; #ifdef SRTP_KERNEL_LINUX err_status_t err_reporting_init(char *ident) { return err_status_ok; } #else /* SRTP_KERNEL_LINUX */ /* err_file is the FILE to which errors are reported */ static FILE *err_file = NULL; err_status_t err_reporting_init(char *ident) { #ifdef ERR_REPORTING_SYSLOG openlog(ident, LOG_PID, LOG_AUTHPRIV); #endif /* * Believe it or not, openlog doesn't return an error on failure. * But then, neither does the syslog() call... */ #ifdef ERR_REPORTING_STDOUT err_file = stdout; #elif defined(USE_ERR_REPORTING_FILE) /* open file for error reporting */ err_file = fopen(ERR_REPORTING_FILE, "w"); if (err_file == NULL) return err_status_init_fail; #endif return err_status_ok; } void err_report(int priority, char *format, ...) { va_list args; if (priority <= err_level) { va_start(args, format); if (err_file != NULL) { vfprintf(err_file, format, args); /* fprintf(err_file, "\n"); */ } #ifdef ERR_REPORTING_SYSLOG if (1) { /* FIXME: Make this a runtime option. */ int syslogpri; switch (priority) { case err_level_emergency: syslogpri = LOG_EMERG; break; case err_level_alert: syslogpri = LOG_ALERT; break; case err_level_critical: syslogpri = LOG_CRIT; break; case err_level_error: syslogpri = LOG_ERR; break; case err_level_warning: syslogpri = LOG_WARNING; break; case err_level_notice: syslogpri = LOG_NOTICE; break; case err_level_info: syslogpri = LOG_INFO; break; case err_level_debug: case err_level_none: default: syslogpri = LOG_DEBUG; break; } vsyslog(syslogpri, format, args); #endif va_end(args); } } #endif /* SRTP_KERNEL_LINUX */ void err_reporting_set_level(err_reporting_level_t lvl) { err_level = lvl; } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/kernel/key.c ================================================ /* * key.c * * key usage limits enforcement * * David A. Mcgrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "key.h" #define soft_limit 0x10000 err_status_t key_limit_set(key_limit_t key, const xtd_seq_num_t s) { #ifdef NO_64BIT_MATH if (high32(s) == 0 && low32(s) < soft_limit) return err_status_bad_param; #else if (s < soft_limit) return err_status_bad_param; #endif key->num_left = s; key->state = key_state_normal; return err_status_ok; } err_status_t key_limit_clone(key_limit_t original, key_limit_t *new_key) { if (original == NULL) return err_status_bad_param; *new_key = original; return err_status_ok; } err_status_t key_limit_check(const key_limit_t key) { if (key->state == key_state_expired) return err_status_key_expired; return err_status_ok; } key_event_t key_limit_update(key_limit_t key) { #ifdef NO_64BIT_MATH if (low32(key->num_left) == 0) { // carry key->num_left = make64(high32(key->num_left)-1,low32(key->num_left) - 1); } else { // no carry key->num_left = make64(high32(key->num_left),low32(key->num_left) - 1); } if (high32(key->num_left) != 0 || low32(key->num_left) >= soft_limit) { return key_event_normal; /* we're above the soft limit */ } #else key->num_left--; if (key->num_left >= soft_limit) { return key_event_normal; /* we're above the soft limit */ } #endif if (key->state == key_state_normal) { /* we just passed the soft limit, so change the state */ key->state = key_state_past_soft_limit; } #ifdef NO_64BIT_MATH if (low32(key->num_left) == 0 && high32(key->num_left == 0)) #else if (key->num_left < 1) #endif { /* we just hit the hard limit */ key->state = key_state_expired; return key_event_hard_limit; } return key_event_soft_limit; } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/math/datatypes.c ================================================ /* * datatypes.c * * data types for finite fields and functions for input, output, and * manipulation * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "datatypes.h" int octet_weight[256] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 }; int octet_get_weight(uint8_t octet) { extern int octet_weight[256]; return octet_weight[octet]; } /* * bit_string is a buffer that is used to hold output strings, e.g. * for printing. */ /* the value MAX_PRINT_STRING_LEN is defined in datatypes.h */ char bit_string[MAX_PRINT_STRING_LEN]; uint8_t nibble_to_hex_char(uint8_t nibble) { char buf[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; return buf[nibble & 0xF]; } char * octet_string_hex_string(const void *s, int length) { const uint8_t *str = (const uint8_t *)s; int i; /* double length, since one octet takes two hex characters */ length *= 2; /* truncate string if it would be too long */ if (length >= MAX_PRINT_STRING_LEN-1) length = MAX_PRINT_STRING_LEN-2; for (i=0; i < length; i+=2) { bit_string[i] = nibble_to_hex_char(*str >> 4); bit_string[i+1] = nibble_to_hex_char(*str++ & 0xF); } bit_string[i] = 0; /* null terminate string */ return bit_string; } static inline int hex_char_to_nibble(uint8_t c) { switch(c) { case ('0'): return 0x0; case ('1'): return 0x1; case ('2'): return 0x2; case ('3'): return 0x3; case ('4'): return 0x4; case ('5'): return 0x5; case ('6'): return 0x6; case ('7'): return 0x7; case ('8'): return 0x8; case ('9'): return 0x9; case ('a'): return 0xa; case ('A'): return 0xa; case ('b'): return 0xb; case ('B'): return 0xb; case ('c'): return 0xc; case ('C'): return 0xc; case ('d'): return 0xd; case ('D'): return 0xd; case ('e'): return 0xe; case ('E'): return 0xe; case ('f'): return 0xf; case ('F'): return 0xf; default: break; /* this flags an error */ } return -1; } int is_hex_string(char *s) { while(*s != 0) if (hex_char_to_nibble(*s++) == -1) return 0; return 1; } /* * hex_string_to_octet_string converts a hexadecimal string * of length 2 * len to a raw octet string of length len */ int hex_string_to_octet_string(char *raw, char *hex, int len) { uint8_t x; int tmp; int hex_len; hex_len = 0; while (hex_len < len) { tmp = hex_char_to_nibble(hex[0]); if (tmp == -1) return hex_len; x = (tmp << 4); hex_len++; tmp = hex_char_to_nibble(hex[1]); if (tmp == -1) return hex_len; x |= (tmp & 0xff); hex_len++; *raw++ = x; hex += 2; } return hex_len; } char * v128_hex_string(v128_t *x) { int i, j; for (i=j=0; i < 16; i++) { bit_string[j++] = nibble_to_hex_char(x->v8[i] >> 4); bit_string[j++] = nibble_to_hex_char(x->v8[i] & 0xF); } bit_string[j] = 0; /* null terminate string */ return bit_string; } char * v128_bit_string(v128_t *x) { int j, index; uint32_t mask; for (j=index=0; j < 4; j++) { for (mask=0x80000000; mask > 0; mask >>= 1) { if (x->v32[j] & mask) bit_string[index] = '1'; else bit_string[index] = '0'; ++index; } } bit_string[128] = 0; /* null terminate string */ return bit_string; } void v128_copy_octet_string(v128_t *x, const uint8_t s[16]) { #ifdef ALIGNMENT_32BIT_REQUIRED if ((((uint32_t) &s[0]) & 0x3) != 0) #endif { x->v8[0] = s[0]; x->v8[1] = s[1]; x->v8[2] = s[2]; x->v8[3] = s[3]; x->v8[4] = s[4]; x->v8[5] = s[5]; x->v8[6] = s[6]; x->v8[7] = s[7]; x->v8[8] = s[8]; x->v8[9] = s[9]; x->v8[10] = s[10]; x->v8[11] = s[11]; x->v8[12] = s[12]; x->v8[13] = s[13]; x->v8[14] = s[14]; x->v8[15] = s[15]; } #ifdef ALIGNMENT_32BIT_REQUIRED else { v128_t *v = (v128_t *) &s[0]; v128_copy(x,v); } #endif } #ifndef DATATYPES_USE_MACROS /* little functions are not macros */ void v128_set_to_zero(v128_t *x) { _v128_set_to_zero(x); } void v128_copy(v128_t *x, const v128_t *y) { _v128_copy(x, y); } void v128_xor(v128_t *z, v128_t *x, v128_t *y) { _v128_xor(z, x, y); } void v128_and(v128_t *z, v128_t *x, v128_t *y) { _v128_and(z, x, y); } void v128_or(v128_t *z, v128_t *x, v128_t *y) { _v128_or(z, x, y); } void v128_complement(v128_t *x) { _v128_complement(x); } int v128_is_eq(const v128_t *x, const v128_t *y) { return _v128_is_eq(x, y); } int v128_xor_eq(v128_t *x, const v128_t *y) { return _v128_xor_eq(x, y); } int v128_get_bit(const v128_t *x, int i) { return _v128_get_bit(x, i); } void v128_set_bit(v128_t *x, int i) { _v128_set_bit(x, i); } void v128_clear_bit(v128_t *x, int i){ _v128_clear_bit(x, i); } void v128_set_bit_to(v128_t *x, int i, int y){ _v128_set_bit_to(x, i, y); } #endif /* DATATYPES_USE_MACROS */ void v128_right_shift(v128_t *x, int index) { const int base_index = index >> 5; const int bit_index = index & 31; int i, from; uint32_t b; if (index > 127) { v128_set_to_zero(x); return; } if (bit_index == 0) { /* copy each word from left size to right side */ x->v32[4-1] = x->v32[4-1-base_index]; for (i=4-1; i > base_index; i--) x->v32[i-1] = x->v32[i-1-base_index]; } else { /* set each word to the "or" of the two bit-shifted words */ for (i = 4; i > base_index; i--) { from = i-1 - base_index; b = x->v32[from] << bit_index; if (from > 0) b |= x->v32[from-1] >> (32-bit_index); x->v32[i-1] = b; } } /* now wrap up the final portion */ for (i=0; i < base_index; i++) x->v32[i] = 0; } void v128_left_shift(v128_t *x, int index) { int i; const int base_index = index >> 5; const int bit_index = index & 31; if (index > 127) { v128_set_to_zero(x); return; } if (bit_index == 0) { for (i=0; i < 4 - base_index; i++) x->v32[i] = x->v32[i+base_index]; } else { for (i=0; i < 4 - base_index - 1; i++) x->v32[i] = (x->v32[i+base_index] >> bit_index) ^ (x->v32[i+base_index+1] << (32 - bit_index)); x->v32[4 - base_index-1] = x->v32[4-1] >> bit_index; } /* now wrap up the final portion */ for (i = 4 - base_index; i < 4; i++) x->v32[i] = 0; } int octet_string_is_eq(uint8_t *a, uint8_t *b, int len) { uint8_t *end = b + len; while (b < end) if (*a++ != *b++) return 1; return 0; } void octet_string_set_to_zero(uint8_t *s, int len) { uint8_t *end = s + len; do { *s = 0; } while (++s < end); } /* * From RFC 1521: The Base64 Alphabet * * Value Encoding Value Encoding Value Encoding Value Encoding * 0 A 17 R 34 i 51 z * 1 B 18 S 35 j 52 0 * 2 C 19 T 36 k 53 1 * 3 D 20 U 37 l 54 2 * 4 E 21 V 38 m 55 3 * 5 F 22 W 39 n 56 4 * 6 G 23 X 40 o 57 5 * 7 H 24 Y 41 p 58 6 * 8 I 25 Z 42 q 59 7 * 9 J 26 a 43 r 60 8 * 10 K 27 b 44 s 61 9 * 11 L 28 c 45 t 62 + * 12 M 29 d 46 u 63 / * 13 N 30 e 47 v * 14 O 31 f 48 w (pad) = * 15 P 32 g 49 x * 16 Q 33 h 50 y */ int base64_char_to_sextet(uint8_t c) { switch(c) { case 'A': return 0; case 'B': return 1; case 'C': return 2; case 'D': return 3; case 'E': return 4; case 'F': return 5; case 'G': return 6; case 'H': return 7; case 'I': return 8; case 'J': return 9; case 'K': return 10; case 'L': return 11; case 'M': return 12; case 'N': return 13; case 'O': return 14; case 'P': return 15; case 'Q': return 16; case 'R': return 17; case 'S': return 18; case 'T': return 19; case 'U': return 20; case 'V': return 21; case 'W': return 22; case 'X': return 23; case 'Y': return 24; case 'Z': return 25; case 'a': return 26; case 'b': return 27; case 'c': return 28; case 'd': return 29; case 'e': return 30; case 'f': return 31; case 'g': return 32; case 'h': return 33; case 'i': return 34; case 'j': return 35; case 'k': return 36; case 'l': return 37; case 'm': return 38; case 'n': return 39; case 'o': return 40; case 'p': return 41; case 'q': return 42; case 'r': return 43; case 's': return 44; case 't': return 45; case 'u': return 46; case 'v': return 47; case 'w': return 48; case 'x': return 49; case 'y': return 50; case 'z': return 51; case '0': return 52; case '1': return 53; case '2': return 54; case '3': return 55; case '4': return 56; case '5': return 57; case '6': return 58; case '7': return 59; case '8': return 60; case '9': return 61; case '+': return 62; case '/': return 63; case '=': return 64; default: break; } return -1; } /* * base64_string_to_octet_string converts a hexadecimal string * of length 2 * len to a raw octet string of length len */ int base64_string_to_octet_string(char *raw, char *base64, int len) { uint8_t x; int tmp; int base64_len; base64_len = 0; while (base64_len < len) { tmp = base64_char_to_sextet(base64[0]); if (tmp == -1) return base64_len; x = (tmp << 6); base64_len++; tmp = base64_char_to_sextet(base64[1]); if (tmp == -1) return base64_len; x |= (tmp & 0xffff); base64_len++; *raw++ = x; base64 += 2; } return base64_len; } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/math/gf2_8.c ================================================ /* * gf2_8.c * * GF(256) finite field implementation, with the representation used * in the AES cipher. * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "datatypes.h" #include "gf2_8.h" /* gf2_8_shift() moved to gf2_8.h as an inline function */ inline gf2_8 gf2_8_multiply(gf2_8 x, gf2_8 y) { gf2_8 z = 0; if (y & 1) z ^= x; x = gf2_8_shift(x); if (y & 2) z ^= x; x = gf2_8_shift(x); if (y & 4) z ^= x; x = gf2_8_shift(x); if (y & 8) z ^= x; x = gf2_8_shift(x); if (y & 16) z ^= x; x = gf2_8_shift(x); if (y & 32) z ^= x; x = gf2_8_shift(x); if (y & 64) z ^= x; x = gf2_8_shift(x); if (y & 128) z ^= x; return z; } /* this should use the euclidean algorithm */ gf2_8 gf2_8_compute_inverse(gf2_8 x) { unsigned int i; if (x == 0) return 0; /* zero is a special case */ for (i=0; i < 256; i++) if (gf2_8_multiply((gf2_8) i, x) == 1) return (gf2_8) i; return 0; } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/math/math.c ================================================ /* * math.c * * crypto math operations and data types * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "crypto_math.h" #include /* malloc() used in bitvector_alloc */ int octet_weight[256] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 }; int low_bit[256] = { -1, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; int high_bit[256] = { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; int octet_get_weight(uint8_t octet) { extern int octet_weight[256]; return octet_weight[octet]; } unsigned char v32_weight(v32_t a) { unsigned int wt = 0; wt += octet_weight[a.v8[0]]; /* note: endian-ness makes no difference */ wt += octet_weight[a.v8[1]]; wt += octet_weight[a.v8[2]]; wt += octet_weight[a.v8[3]]; return wt; } inline unsigned char v32_distance(v32_t x, v32_t y) { x.value ^= y.value; return v32_weight(x); } unsigned int v32_dot_product(v32_t a, v32_t b) { a.value &= b.value; return v32_weight(a) & 1; } /* * _bit_string returns a NULL-terminated character string suitable for * printing */ #define MAX_STRING_LENGTH 1024 char bit_string[MAX_STRING_LENGTH]; char * octet_bit_string(uint8_t x) { int mask, index; for (mask = 1, index = 0; mask < 256; mask <<= 1) if ((x & mask) == 0) bit_string[index++] = '0'; else bit_string[index++] = '1'; bit_string[index++] = 0; /* NULL terminate string */ return bit_string; } char * v16_bit_string(v16_t x) { int i, mask, index; for (i = index = 0; i < 2; i++) { for (mask = 1; mask < 256; mask <<= 1) if ((x.v8[i] & mask) == 0) bit_string[index++] = '0'; else bit_string[index++] = '1'; } bit_string[index++] = 0; /* NULL terminate string */ return bit_string; } char * v32_bit_string(v32_t x) { int i, mask, index; for (i = index = 0; i < 4; i++) { for (mask = 128; mask > 0; mask >>= 1) if ((x.v8[i] & mask) == 0) bit_string[index++] = '0'; else bit_string[index++] = '1'; } bit_string[index++] = 0; /* NULL terminate string */ return bit_string; } char * v64_bit_string(const v64_t *x) { int i, mask, index; for (i = index = 0; i < 8; i++) { for (mask = 1; mask < 256; mask <<= 1) if ((x->v8[i] & mask) == 0) bit_string[index++] = '0'; else bit_string[index++] = '1'; } bit_string[index++] = 0; /* NULL terminate string */ return bit_string; } char * v128_bit_string(v128_t *x) { int j, index; uint32_t mask; for (j=index=0; j < 4; j++) { for (mask=0x80000000; mask > 0; mask >>= 1) { if (x->v32[j] & mask) bit_string[index] = '1'; else bit_string[index] = '0'; ++index; } } bit_string[128] = 0; /* null terminate string */ return bit_string; } uint8_t nibble_to_hex_char(uint8_t nibble) { char buf[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; return buf[nibble & 0xF]; } char * octet_hex_string(uint8_t x) { bit_string[0] = nibble_to_hex_char(x >> 4); bit_string[1] = nibble_to_hex_char(x & 0xF); bit_string[2] = 0; /* null terminate string */ return bit_string; } char * octet_string_hex_string(const void *str, int length) { const uint8_t *s = str; int i; /* double length, since one octet takes two hex characters */ length *= 2; /* truncate string if it would be too long */ if (length > MAX_STRING_LENGTH) length = MAX_STRING_LENGTH-1; for (i=0; i < length; i+=2) { bit_string[i] = nibble_to_hex_char(*s >> 4); bit_string[i+1] = nibble_to_hex_char(*s++ & 0xF); } bit_string[i] = 0; /* null terminate string */ return bit_string; } char * v16_hex_string(v16_t x) { int i, j; for (i=j=0; i < 2; i++) { bit_string[j++] = nibble_to_hex_char(x.v8[i] >> 4); bit_string[j++] = nibble_to_hex_char(x.v8[i] & 0xF); } bit_string[j] = 0; /* null terminate string */ return bit_string; } char * v32_hex_string(v32_t x) { int i, j; for (i=j=0; i < 4; i++) { bit_string[j++] = nibble_to_hex_char(x.v8[i] >> 4); bit_string[j++] = nibble_to_hex_char(x.v8[i] & 0xF); } bit_string[j] = 0; /* null terminate string */ return bit_string; } char * v64_hex_string(const v64_t *x) { int i, j; for (i=j=0; i < 8; i++) { bit_string[j++] = nibble_to_hex_char(x->v8[i] >> 4); bit_string[j++] = nibble_to_hex_char(x->v8[i] & 0xF); } bit_string[j] = 0; /* null terminate string */ return bit_string; } char * v128_hex_string(v128_t *x) { int i, j; for (i=j=0; i < 16; i++) { bit_string[j++] = nibble_to_hex_char(x->v8[i] >> 4); bit_string[j++] = nibble_to_hex_char(x->v8[i] & 0xF); } bit_string[j] = 0; /* null terminate string */ return bit_string; } char * char_to_hex_string(char *x, int num_char) { int i, j; if (num_char >= 16) num_char = 16; for (i=j=0; i < num_char; i++) { bit_string[j++] = nibble_to_hex_char(x[i] >> 4); bit_string[j++] = nibble_to_hex_char(x[i] & 0xF); } bit_string[j] = 0; /* null terminate string */ return bit_string; } int hex_char_to_nibble(uint8_t c) { switch(c) { case ('0'): return 0x0; case ('1'): return 0x1; case ('2'): return 0x2; case ('3'): return 0x3; case ('4'): return 0x4; case ('5'): return 0x5; case ('6'): return 0x6; case ('7'): return 0x7; case ('8'): return 0x8; case ('9'): return 0x9; case ('a'): return 0xa; case ('A'): return 0xa; case ('b'): return 0xb; case ('B'): return 0xb; case ('c'): return 0xc; case ('C'): return 0xc; case ('d'): return 0xd; case ('D'): return 0xd; case ('e'): return 0xe; case ('E'): return 0xe; case ('f'): return 0xf; case ('F'): return 0xf; default: return -1; /* this flags an error */ } /* NOTREACHED */ return -1; /* this keeps compilers from complaining */ } int is_hex_string(char *s) { while(*s != 0) if (hex_char_to_nibble(*s++) == -1) return 0; return 1; } uint8_t hex_string_to_octet(char *s) { uint8_t x; x = (hex_char_to_nibble(s[0]) << 4) | hex_char_to_nibble(s[1] & 0xFF); return x; } /* * hex_string_to_octet_string converts a hexadecimal string * of length 2 * len to a raw octet string of length len */ int hex_string_to_octet_string(char *raw, char *hex, int len) { uint8_t x; int tmp; int hex_len; hex_len = 0; while (hex_len < len) { tmp = hex_char_to_nibble(hex[0]); if (tmp == -1) return hex_len; x = (tmp << 4); hex_len++; tmp = hex_char_to_nibble(hex[1]); if (tmp == -1) return hex_len; x |= (tmp & 0xff); hex_len++; *raw++ = x; hex += 2; } return hex_len; } v16_t hex_string_to_v16(char *s) { v16_t x; int i, j; for (i=j=0; i < 4; i += 2, j++) { x.v8[j] = (hex_char_to_nibble(s[i]) << 4) | hex_char_to_nibble(s[i+1] & 0xFF); } return x; } v32_t hex_string_to_v32(char *s) { v32_t x; int i, j; for (i=j=0; i < 8; i += 2, j++) { x.v8[j] = (hex_char_to_nibble(s[i]) << 4) | hex_char_to_nibble(s[i+1] & 0xFF); } return x; } v64_t hex_string_to_v64(char *s) { v64_t x; int i, j; for (i=j=0; i < 16; i += 2, j++) { x.v8[j] = (hex_char_to_nibble(s[i]) << 4) | hex_char_to_nibble(s[i+1] & 0xFF); } return x; } v128_t hex_string_to_v128(char *s) { v128_t x; int i, j; for (i=j=0; i < 32; i += 2, j++) { x.v8[j] = (hex_char_to_nibble(s[i]) << 4) | hex_char_to_nibble(s[i+1] & 0xFF); } return x; } /* * the matrix A[] is stored in column format, i.e., A[i] is the ith * column of the matrix */ uint8_t A_times_x_plus_b(uint8_t A[8], uint8_t x, uint8_t b) { int index = 0; unsigned mask; for (mask=1; mask < 256; mask *= 2) { if (x & mask) b^= A[index]; ++index; } return b; } inline void v16_copy_octet_string(v16_t *x, const uint8_t s[2]) { x->v8[0] = s[0]; x->v8[1] = s[1]; } inline void v32_copy_octet_string(v32_t *x, const uint8_t s[4]) { x->v8[0] = s[0]; x->v8[1] = s[1]; x->v8[2] = s[2]; x->v8[3] = s[3]; } inline void v64_copy_octet_string(v64_t *x, const uint8_t s[8]) { x->v8[0] = s[0]; x->v8[1] = s[1]; x->v8[2] = s[2]; x->v8[3] = s[3]; x->v8[4] = s[4]; x->v8[5] = s[5]; x->v8[6] = s[6]; x->v8[7] = s[7]; } void v128_copy_octet_string(v128_t *x, const uint8_t s[16]) { x->v8[0] = s[0]; x->v8[1] = s[1]; x->v8[2] = s[2]; x->v8[3] = s[3]; x->v8[4] = s[4]; x->v8[5] = s[5]; x->v8[6] = s[6]; x->v8[7] = s[7]; x->v8[8] = s[8]; x->v8[9] = s[9]; x->v8[10] = s[10]; x->v8[11] = s[11]; x->v8[12] = s[12]; x->v8[13] = s[13]; x->v8[14] = s[14]; x->v8[15] = s[15]; } #ifndef DATATYPES_USE_MACROS /* little functions are not macros */ void v128_set_to_zero(v128_t *x) { _v128_set_to_zero(x); } void v128_copy(v128_t *x, const v128_t *y) { _v128_copy(x, y); } void v128_xor(v128_t *z, v128_t *x, v128_t *y) { _v128_xor(z, x, y); } void v128_and(v128_t *z, v128_t *x, v128_t *y) { _v128_and(z, x, y); } void v128_or(v128_t *z, v128_t *x, v128_t *y) { _v128_or(z, x, y); } void v128_complement(v128_t *x) { _v128_complement(x); } int v128_is_eq(const v128_t *x, const v128_t *y) { return _v128_is_eq(x, y); } int v128_get_bit(const v128_t *x, int i) { return _v128_get_bit(x, i); } void v128_set_bit(v128_t *x, int i) { _v128_set_bit(x, i); } void v128_clear_bit(v128_t *x, int i){ _v128_clear_bit(x, i); } void v128_set_bit_to(v128_t *x, int i, int y){ _v128_set_bit_to(x, i, y); } #endif /* DATATYPES_USE_MACROS */ inline void v128_left_shift2(v128_t *x, int num_bits) { int i; int word_shift = num_bits >> 5; int bit_shift = num_bits & 31; for (i=0; i < (4-word_shift); i++) { x->v32[i] = x->v32[i+word_shift] << bit_shift; } for ( ; i < word_shift; i++) { x->v32[i] = 0; } } void v128_right_shift(v128_t *x, int index) { const int base_index = index >> 5; const int bit_index = index & 31; int i, from; uint32_t b; if (index > 127) { v128_set_to_zero(x); return; } if (bit_index == 0) { /* copy each word from left size to right side */ x->v32[4-1] = x->v32[4-1-base_index]; for (i=4-1; i > base_index; i--) x->v32[i-1] = x->v32[i-1-base_index]; } else { /* set each word to the "or" of the two bit-shifted words */ for (i = 4; i > base_index; i--) { from = i-1 - base_index; b = x->v32[from] << bit_index; if (from > 0) b |= x->v32[from-1] >> (32-bit_index); x->v32[i-1] = b; } } /* now wrap up the final portion */ for (i=0; i < base_index; i++) x->v32[i] = 0; } void v128_left_shift(v128_t *x, int index) { int i; const int base_index = index >> 5; const int bit_index = index & 31; if (index > 127) { v128_set_to_zero(x); return; } if (bit_index == 0) { for (i=0; i < 4 - base_index; i++) x->v32[i] = x->v32[i+base_index]; } else { for (i=0; i < 4 - base_index - 1; i++) x->v32[i] = (x->v32[i+base_index] << bit_index) ^ (x->v32[i+base_index+1] >> (32 - bit_index)); x->v32[4 - base_index-1] = x->v32[4-1] << bit_index; } /* now wrap up the final portion */ for (i = 4 - base_index; i < 4; i++) x->v32[i] = 0; } #if 0 void v128_add(v128_t *z, v128_t *x, v128_t *y) { /* integer addition modulo 2^128 */ #ifdef WORDS_BIGENDIAN uint64_t tmp; tmp = x->v32[3] + y->v32[3]; z->v32[3] = (uint32_t) tmp; tmp = x->v32[2] + y->v32[2] + (tmp >> 32); z->v32[2] = (uint32_t) tmp; tmp = x->v32[1] + y->v32[1] + (tmp >> 32); z->v32[1] = (uint32_t) tmp; tmp = x->v32[0] + y->v32[0] + (tmp >> 32); z->v32[0] = (uint32_t) tmp; #else /* assume little endian architecture */ uint64_t tmp; tmp = htonl(x->v32[3]) + htonl(y->v32[3]); z->v32[3] = ntohl((uint32_t) tmp); tmp = htonl(x->v32[2]) + htonl(y->v32[2]) + htonl(tmp >> 32); z->v32[2] = ntohl((uint32_t) tmp); tmp = htonl(x->v32[1]) + htonl(y->v32[1]) + htonl(tmp >> 32); z->v32[1] = ntohl((uint32_t) tmp); tmp = htonl(x->v32[0]) + htonl(y->v32[0]) + htonl(tmp >> 32); z->v32[0] = ntohl((uint32_t) tmp); #endif /* WORDS_BIGENDIAN */ } #endif int octet_string_is_eq(uint8_t *a, uint8_t *b, int len) { uint8_t *end = b + len; while (b < end) if (*a++ != *b++) return 1; return 0; } void octet_string_set_to_zero(uint8_t *s, int len) { uint8_t *end = s + len; do { *s = 0; } while (++s < end); } /* functions manipulating bit_vector_t */ #define BITVECTOR_MAX_WORDS 5 int bitvector_alloc(bitvector_t *v, unsigned long length) { unsigned long l = (length + bytes_per_word - 1) / bytes_per_word; int i; /* allocate memory, then set parameters */ if (l > BITVECTOR_MAX_WORDS) return -1; else l = BITVECTOR_MAX_WORDS; v->word = malloc(l); if (v->word == NULL) return -1; v->length = length; /* initialize bitvector to zero */ for (i=0; i < (length >> 5); i++) { v->word = 0; } return 0; } void bitvector_set_bit(bitvector_t *v, int bit_index) { v->word[(bit_index >> 5)] |= (1 << (bit_index & 31)); } int bitvector_get_bit(const bitvector_t *v, int bit_index) { return ((v->word[(bit_index >> 5)]) >> (bit_index & 31)) & 1; } #include int bitvector_print_hex(const bitvector_t *v, FILE *stream) { int i; int m = v->length >> 5; int n = v->length & 31; char string[9]; uint32_t tmp; /* if length isn't a multiple of four, we can't hex_print */ if (n & 3) return -1; /* if the length is zero, do nothing */ if (v->length == 0) return 0; /* * loop over words from most significant to least significant - */ for (i=m; i > 0; i++) { char *str = string + 7; tmp = v->word[i]; /* null terminate string */ string[8] = 0; /* loop over nibbles */ *str-- = nibble_to_hex_char(tmp & 0xf); tmp >>= 4; *str-- = nibble_to_hex_char(tmp & 0xf); tmp >>= 4; *str-- = nibble_to_hex_char(tmp & 0xf); tmp >>= 4; *str-- = nibble_to_hex_char(tmp & 0xf); tmp >>= 4; *str-- = nibble_to_hex_char(tmp & 0xf); tmp >>= 4; *str-- = nibble_to_hex_char(tmp & 0xf); tmp >>= 4; *str-- = nibble_to_hex_char(tmp & 0xf); tmp >>= 4; *str-- = nibble_to_hex_char(tmp & 0xf); /* now print stream */ fprintf(stream, string); } return 0; } int hex_string_length(char *s) { int count = 0; /* ignore leading zeros */ while ((*s != 0) && *s == '0') s++; /* count remaining characters */ while (*s != 0) { if (hex_char_to_nibble(*s++) == -1) return -1; count++; } return count; } int bitvector_set_from_hex(bitvector_t *v, char *string) { int num_hex_chars, m, n, i, j; uint32_t tmp; num_hex_chars = hex_string_length(string); if (num_hex_chars == -1) return -1; /* set length */ v->length = num_hex_chars * 4; /* * at this point, we should subtract away a bit if the high * bit of the first character is zero, but we ignore that * for now and assume that we're four-bit aligned - DAM */ m = num_hex_chars / 8; /* number of words */ n = num_hex_chars % 8; /* number of nibbles in last word */ /* if the length is greater than the bitvector, return an error */ if (m > BITVECTOR_MAX_WORDS) return -1; /* * loop over words from most significant - first word is a special * case */ if (n) { tmp = 0; for (i=0; i < n; i++) { tmp = hex_char_to_nibble(*string++); tmp <<= 4; } v->word[m] = tmp; } /* now loop over the rest of the words */ for (i=m-1; i >= 0; i--) { tmp = 0; for (j=0; j < 8; j++) { tmp = hex_char_to_nibble(*string++); tmp <<= 4; } v->word[i] = tmp; } return 0; } /* functions below not yet tested! */ int v32_low_bit(v32_t *w) { int value; value = low_bit[w->v8[0]]; if (value != -1) return value; value = low_bit[w->v8[1]]; if (value != -1) return value + 8; value = low_bit[w->v8[2]]; if (value != -1) return value + 16; value = low_bit[w->v8[3]]; if (value == -1) return -1; return value + 24; } /* high_bit not done yet */ ================================================ FILE: deps/pjsip/third_party/srtp/crypto/math/stat.c ================================================ /* * stats.c * * statistical tests for randomness (FIPS 140-2, Section 4.9) * * David A. McGrew * Cisco Systems, Inc. */ #include "stat.h" debug_module_t mod_stat = { 0, /* debugging is off by default */ (char *)"stat test" /* printable module name */ }; /* * each test assumes that 20,000 bits (2500 octets) of data is * provided as input */ #define STAT_TEST_DATA_LEN 2500 err_status_t stat_test_monobit(uint8_t *data) { uint8_t *data_end = data + STAT_TEST_DATA_LEN; uint16_t ones_count; ones_count = 0; while (data < data_end) { ones_count += octet_get_weight(*data); data++; } debug_print(mod_stat, "bit count: %d", ones_count); if ((ones_count < 9725) || (ones_count > 10275)) return err_status_algo_fail; return err_status_ok; } err_status_t stat_test_poker(uint8_t *data) { int i; uint8_t *data_end = data + STAT_TEST_DATA_LEN; double poker; uint16_t f[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; while (data < data_end) { f[*data & 0x0f]++; /* increment freq. count for low nibble */ f[(*data) >> 4]++; /* increment freq. count for high nibble */ data++; } poker = 0.0; for (i=0; i < 16; i++) poker += (double) f[i] * f[i]; poker *= (16.0 / 5000.0); poker -= 5000.0; debug_print(mod_stat, "poker test: %f\n", poker); if ((poker < 2.16) || (poker > 46.17)) return err_status_algo_fail; return err_status_ok; } /* * runs[i] holds the number of runs of size (i-1) */ err_status_t stat_test_runs(uint8_t *data) { uint8_t *data_end = data + STAT_TEST_DATA_LEN; uint16_t runs[6] = { 0, 0, 0, 0, 0, 0 }; uint16_t gaps[6] = { 0, 0, 0, 0, 0, 0 }; uint16_t lo_value[6] = { 2315, 1114, 527, 240, 103, 103 }; uint16_t hi_value[6] = { 2685, 1386, 723, 384, 209, 209 }; int state = 0; uint16_t mask; int i; /* * the state variable holds the number of bits in the * current run (or gap, if negative) */ while (data < data_end) { /* loop over the bits of this byte */ for (mask = 1; mask < 256; mask <<= 1) { if (*data & mask) { /* next bit is a one */ if (state > 0) { /* prefix is a run, so increment the run-count */ state++; /* check for long runs */ if (state > 25) { debug_print(mod_stat, ">25 runs: %d", state); return err_status_algo_fail; } } else if (state < 0) { /* prefix is a gap */ if (state < -25) { debug_print(mod_stat, ">25 gaps: %d", state); return err_status_algo_fail; /* long-runs test failed */ } if (state < -6) { state = -6; /* group together gaps > 5 */ } gaps[-1-state]++; /* increment gap count */ state = 1; /* set state at one set bit */ } else { /* state is zero; this happens only at initialization */ state = 1; } } else { /* next bit is a zero */ if (state > 0) { /* prefix is a run */ if (state > 25) { debug_print(mod_stat, ">25 runs (2): %d", state); return err_status_algo_fail; /* long-runs test failed */ } if (state > 6) { state = 6; /* group together runs > 5 */ } runs[state-1]++; /* increment run count */ state = -1; /* set state at one zero bit */ } else if (state < 0) { /* prefix is a gap, so increment gap-count (decrement state) */ state--; /* check for long gaps */ if (state < -25) { debug_print(mod_stat, ">25 gaps (2): %d", state); return err_status_algo_fail; } } else { /* state is zero; this happens only at initialization */ state = -1; } } } /* move along to next octet */ data++; } if (mod_stat.on) { debug_print(mod_stat, "runs test", NULL); for (i=0; i < 6; i++) debug_print(mod_stat, " runs[]: %d", runs[i]); for (i=0; i < 6; i++) debug_print(mod_stat, " gaps[]: %d", gaps[i]); } /* check run and gap counts against the fixed limits */ for (i=0; i < 6; i++) if ( (runs[i] < lo_value[i] ) || (runs[i] > hi_value[i]) || (gaps[i] < lo_value[i] ) || (gaps[i] > hi_value[i])) return err_status_algo_fail; return err_status_ok; } /* * the function stat_test_rand_source applys the FIPS-140-2 statistical * tests to the random source defined by rs * */ #define RAND_SRC_BUF_OCTETS 50 /* this value MUST divide 2500! */ err_status_t stat_test_rand_source(rand_source_func_t get_rand_bytes) { int i; double poker; uint8_t *data, *data_end; uint16_t f[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; uint8_t buffer[RAND_SRC_BUF_OCTETS]; err_status_t status; int ones_count = 0; uint16_t runs[6] = { 0, 0, 0, 0, 0, 0 }; uint16_t gaps[6] = { 0, 0, 0, 0, 0, 0 }; uint16_t lo_value[6] = { 2315, 1114, 527, 240, 103, 103 }; uint16_t hi_value[6] = { 2685, 1386, 723, 384, 209, 209 }; int state = 0; uint16_t mask; /* counters for monobit, poker, and runs tests are initialized above */ /* main loop: fill buffer, update counters for stat tests */ for (i=0; i < 2500; i+=RAND_SRC_BUF_OCTETS) { /* fill data buffer */ status = get_rand_bytes(buffer, RAND_SRC_BUF_OCTETS); if (status) { debug_print(mod_stat, "couldn't get rand bytes: %d",status); return status; } #if 0 debug_print(mod_stat, "%s", octet_string_hex_string(buffer, RAND_SRC_BUF_OCTETS)); #endif data = buffer; data_end = data + RAND_SRC_BUF_OCTETS; while (data < data_end) { /* update monobit test counter */ ones_count += octet_get_weight(*data); /* update poker test counters */ f[*data & 0x0f]++; /* increment freq. count for low nibble */ f[(*data) >> 4]++; /* increment freq. count for high nibble */ /* update runs test counters */ /* loop over the bits of this byte */ for (mask = 1; mask < 256; mask <<= 1) { if (*data & mask) { /* next bit is a one */ if (state > 0) { /* prefix is a run, so increment the run-count */ state++; /* check for long runs */ if (state > 25) { debug_print(mod_stat, ">25 runs (3): %d", state); return err_status_algo_fail; } } else if (state < 0) { /* prefix is a gap */ if (state < -25) { debug_print(mod_stat, ">25 gaps (3): %d", state); return err_status_algo_fail; /* long-runs test failed */ } if (state < -6) { state = -6; /* group together gaps > 5 */ } gaps[-1-state]++; /* increment gap count */ state = 1; /* set state at one set bit */ } else { /* state is zero; this happens only at initialization */ state = 1; } } else { /* next bit is a zero */ if (state > 0) { /* prefix is a run */ if (state > 25) { debug_print(mod_stat, ">25 runs (4): %d", state); return err_status_algo_fail; /* long-runs test failed */ } if (state > 6) { state = 6; /* group together runs > 5 */ } runs[state-1]++; /* increment run count */ state = -1; /* set state at one zero bit */ } else if (state < 0) { /* prefix is a gap, so increment gap-count (decrement state) */ state--; /* check for long gaps */ if (state < -25) { debug_print(mod_stat, ">25 gaps (4): %d", state); return err_status_algo_fail; } } else { /* state is zero; this happens only at initialization */ state = -1; } } } /* advance data pointer */ data++; } } /* check to see if test data is within bounds */ /* check monobit test data */ debug_print(mod_stat, "stat: bit count: %d", ones_count); if ((ones_count < 9725) || (ones_count > 10275)) { debug_print(mod_stat, "stat: failed monobit test %d", ones_count); return err_status_algo_fail; } /* check poker test data */ poker = 0.0; for (i=0; i < 16; i++) poker += (double) f[i] * f[i]; poker *= (16.0 / 5000.0); poker -= 5000.0; debug_print(mod_stat, "stat: poker test: %f", poker); if ((poker < 2.16) || (poker > 46.17)) { debug_print(mod_stat, "stat: failed poker test", NULL); return err_status_algo_fail; } /* check run and gap counts against the fixed limits */ for (i=0; i < 6; i++) if ((runs[i] < lo_value[i] ) || (runs[i] > hi_value[i]) || (gaps[i] < lo_value[i] ) || (gaps[i] > hi_value[i])) { debug_print(mod_stat, "stat: failed run/gap test", NULL); return err_status_algo_fail; } debug_print(mod_stat, "passed random stat test", NULL); return err_status_ok; } err_status_t stat_test_rand_source_with_repetition(rand_source_func_t source, unsigned num_trials) { unsigned int i; err_status_t err = err_status_algo_fail; for (i=0; i < num_trials; i++) { err = stat_test_rand_source(source); if (err == err_status_ok) { return err_status_ok; } debug_print(mod_stat, "failed stat test (try number %d)\n", i); } return err; } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/replay/rdb.c ================================================ /* * rdb.c * * Implements a replay database for packet security * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "rdb.h" /* * this implementation of a replay database works as follows: * * window_start is the index of the first packet in the window * bitmask a bit-buffer, containing the most recently entered * index as the leftmost bit * */ /* rdb_init initalizes rdb */ err_status_t rdb_init(rdb_t *rdb) { v128_set_to_zero(&rdb->bitmask); rdb->window_start = 0; return err_status_ok; } /* * rdb_check checks to see if index appears in rdb */ err_status_t rdb_check(const rdb_t *rdb, uint32_t index) { /* if the index appears after (or at very end of) the window, its good */ if (index >= rdb->window_start + rdb_bits_in_bitmask) return err_status_ok; /* if the index appears before the window, its bad */ if (index < rdb->window_start) return err_status_replay_old; /* otherwise, the index appears within the window, so check the bitmask */ if (v128_get_bit(&rdb->bitmask, (index - rdb->window_start)) == 1) return err_status_replay_fail; /* otherwise, the index is okay */ return err_status_ok; } /* * rdb_add_index adds index to rdb_t (and does *not* check if * index appears in db) * * this function should be called only after rdb_check has * indicated that the index does not appear in the rdb, e.g., a mutex * should protect the rdb between these calls */ err_status_t rdb_add_index(rdb_t *rdb, uint32_t index) { int delta; /* here we *assume* that index > rdb->window_start */ delta = (index - rdb->window_start); if (delta < rdb_bits_in_bitmask) { /* if the index is within the window, set the appropriate bit */ v128_set_bit(&rdb->bitmask, delta); } else { delta -= rdb_bits_in_bitmask - 1; /* shift the window forward by delta bits*/ v128_left_shift(&rdb->bitmask, delta); v128_set_bit(&rdb->bitmask, rdb_bits_in_bitmask-1); rdb->window_start += delta; } return err_status_ok; } err_status_t rdb_increment(rdb_t *rdb) { if (rdb->window_start++ > 0x7fffffff) return err_status_key_expired; return err_status_ok; } uint32_t rdb_get_value(const rdb_t *rdb) { return rdb->window_start; } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/replay/rdbx.c ================================================ /* * rdbx.c * * a replay database with extended range, using a rollover counter * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "rdbx.h" #define rdbx_high_bit_in_bitmask 127 /* * from draft-ietf-avt-srtp-00.txt: * * A receiver reconstructs the index i of a packet with sequence * number s using the estimate * * i = 65,536 * t + s, * * where t is chosen from the set { r-1, r, r+1 } such that i is * closest to the value 65,536 * r + s_l. If the value r+1 is used, * then the rollover counter r in the cryptographic context is * incremented by one (if the packet containing s is authentic). */ /* * rdbx implementation notes * * A xtd_seq_num_t is essentially a sequence number for which some of * the data on the wire are implicit. It logically consists of a * rollover counter and a sequence number; the sequence number is the * explicit part, and the rollover counter is the implicit part. * * Upon receiving a sequence_number (e.g. in a newly received SRTP * packet), the complete xtd_seq_num_t can be estimated by using a * local xtd_seq_num_t as a basis. This is done using the function * index_guess(&local, &guess, seq_from_packet). This function * returns the difference of the guess and the local value. The local * xtd_seq_num_t can be moved forward to the guess using the function * index_advance(&guess, delta), where delta is the difference. * * * A rdbx_t consists of a xtd_seq_num_t and a bitmask. The index is highest * sequence number that has been received, and the bitmask indicates * which of the recent indicies have been received as well. The * highest bit in the bitmask corresponds to the index in the bitmask. */ void index_init(xtd_seq_num_t *pi) { #ifdef NO_64BIT_MATH *pi = make64(0,0); #else *pi = 0; #endif } void index_advance(xtd_seq_num_t *pi, sequence_number_t s) { #ifdef NO_64BIT_MATH /* a > ~b means a+b will generate a carry */ /* s is uint16 here */ *pi = make64(high32(*pi) + (s > ~low32(*pi) ? 1 : 0),low32(*pi) + s); #else *pi += s; #endif } /* * index_guess(local, guess, s) * * given a xtd_seq_num_t local (which represents the last * known-to-be-good received xtd_seq_num_t) and a sequence number s * (from a newly arrived packet), sets the contents of *guess to * contain the best guess of the packet index to which s corresponds, * and returns the difference between *guess and *local * * nota bene - the output is a signed integer, DON'T cast it to a * unsigned integer! */ int index_guess(const xtd_seq_num_t *local, xtd_seq_num_t *guess, sequence_number_t s) { #ifdef NO_64BIT_MATH uint32_t local_roc = ((high32(*local) << 16) | (low32(*local) >> 16)); uint16_t local_seq = (uint16_t) (low32(*local)); #else uint32_t local_roc = (uint32_t)(*local >> 16); uint16_t local_seq = (uint16_t) *local; #endif #ifdef NO_64BIT_MATH uint32_t guess_roc = ((high32(*guess) << 16) | (low32(*guess) >> 16)); uint16_t guess_seq = (uint16_t) (low32(*guess)); #else uint32_t guess_roc = (uint32_t)(*guess >> 16); uint16_t guess_seq = (uint16_t) *guess; #endif int difference; if (local_seq < seq_num_median) { if (s - local_seq > seq_num_median) { guess_roc = local_roc - 1; difference = seq_num_max - s + local_seq; } else { guess_roc = local_roc; difference = s - local_seq; } } else { if (local_seq - seq_num_median > s) { guess_roc = local_roc+1; difference = seq_num_max - local_seq + s; } else { difference = s - local_seq; guess_roc = local_roc; } } guess_seq = s; /* Note: guess_roc is 32 bits, so this generates a 48-bit result! */ #ifdef NO_64BIT_MATH *guess = make64(guess_roc >> 16, (guess_roc << 16) | guess_seq); #else *guess = (((uint64_t) guess_roc) << 16) | guess_seq; #endif return difference; } /* * rdbx * */ /* * rdbx_init(&r) initalizes the rdbx_t pointed to by r */ err_status_t rdbx_init(rdbx_t *rdbx) { v128_set_to_zero(&rdbx->bitmask); index_init(&rdbx->index); return err_status_ok; } /* * rdbx_check(&r, delta) checks to see if the xtd_seq_num_t * which is at rdbx->index + delta is in the rdb */ err_status_t rdbx_check(const rdbx_t *rdbx, int delta) { if (delta > 0) { /* if delta is positive, it's good */ return err_status_ok; } else if (rdbx_high_bit_in_bitmask + delta < 0) { /* if delta is lower than the bitmask, it's bad */ return err_status_replay_old; } else if (v128_get_bit(&rdbx->bitmask, rdbx_high_bit_in_bitmask + delta) == 1) { /* delta is within the window, so check the bitmask */ return err_status_replay_fail; } /* otherwise, the index is okay */ return err_status_ok; } /* * rdbx_add_index adds the xtd_seq_num_t at rdbx->window_start + d to * replay_db (and does *not* check if that xtd_seq_num_t appears in db) * * this function should be called only after replay_check has * indicated that the index does not appear in the rdbx, e.g., a mutex * should protect the rdbx between these calls if need be */ err_status_t rdbx_add_index(rdbx_t *rdbx, int delta) { if (delta > 0) { /* shift forward by delta */ index_advance(&rdbx->index, delta); v128_left_shift(&rdbx->bitmask, delta); v128_set_bit(&rdbx->bitmask, 127); } else { /* delta is in window, so flip bit in bitmask */ v128_set_bit(&rdbx->bitmask, -delta); } /* note that we need not consider the case that delta == 0 */ return err_status_ok; } /* * rdbx_estimate_index(rdbx, guess, s) * * given an rdbx and a sequence number s (from a newly arrived packet), * sets the contents of *guess to contain the best guess of the packet * index to which s corresponds, and returns the difference between * *guess and the locally stored synch info */ int rdbx_estimate_index(const rdbx_t *rdbx, xtd_seq_num_t *guess, sequence_number_t s) { /* * if the sequence number and rollover counter in the rdbx are * non-zero, then use the index_guess(...) function, otherwise, just * set the rollover counter to zero (since the index_guess(...) * function might incorrectly guess that the rollover counter is * 0xffffffff) */ #ifdef NO_64BIT_MATH /* seq_num_median = 0x8000 */ if (high32(rdbx->index) > 0 || low32(rdbx->index) > seq_num_median) #else if (rdbx->index > seq_num_median) #endif return index_guess(&rdbx->index, guess, s); #ifdef NO_64BIT_MATH *guess = make64(0,(uint32_t) s); #else *guess = s; #endif #ifdef NO_64BIT_MATH return s - (uint16_t) low32(rdbx->index); #else return s - (uint16_t) rdbx->index; #endif } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/replay/ut_sim.c ================================================ /* * ut_sim.c * * an unreliable transport simulator * (for testing replay databases and suchlike) * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "ut_sim.h" int ut_compar(const void *a, const void *b) { return rand() > (RAND_MAX/2) ? -1 : 1; } void ut_init(ut_connection *utc) { int i; utc->index = 0; for (i=0; i < UT_BUF; i++) utc->buffer[i] = i; qsort(utc->buffer, UT_BUF, sizeof(uint32_t), ut_compar); utc->index = UT_BUF - 1; } uint32_t ut_next_index(ut_connection *utc) { uint32_t tmp; tmp = utc->buffer[0]; utc->index++; utc->buffer[0] = utc->index; qsort(utc->buffer, UT_BUF, sizeof(uint32_t), ut_compar); return tmp; } #ifdef UT_TEST #include int main() { uint32_t i, irecvd, idiff; ut_connection utc; ut_init(&utc); for (i=0; i < 1000; i++) { irecvd = ut_next_index(&utc); idiff = i - irecvd; printf("%lu\t%lu\t%d\n", i, irecvd, idiff); } return 0; } #endif ================================================ FILE: deps/pjsip/third_party/srtp/crypto/rng/ctr_prng.c ================================================ /* * ctr_prng.c * * counter mode based pseudorandom source * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright(c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "prng.h" /* single, global prng structure */ ctr_prng_t ctr_prng; err_status_t ctr_prng_init(rand_source_func_t random_source) { uint8_t tmp_key[32]; err_status_t status; /* initialize output count to zero */ ctr_prng.octet_count = 0; /* set random source */ ctr_prng.rand = random_source; /* initialize secret key from random source */ status = random_source(tmp_key, 32); if (status) return status; /* initialize aes ctr context with random key */ status = aes_icm_context_init(&ctr_prng.state, tmp_key); if (status) return status; return err_status_ok; } err_status_t ctr_prng_get_octet_string(void *dest, uint32_t len) { err_status_t status; /* * if we need to re-initialize the prng, do so now * * avoid 32-bit overflows by subtracting instead of adding */ if (ctr_prng.octet_count > MAX_PRNG_OUT_LEN - len) { status = ctr_prng_init(ctr_prng.rand); if (status) return status; } ctr_prng.octet_count += len; /* * write prng output */ status = aes_icm_output(&ctr_prng.state, (uint8_t*)dest, len); if (status) return status; return err_status_ok; } err_status_t ctr_prng_deinit(void) { /* nothing */ return err_status_ok; } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/rng/prng.c ================================================ /* * prng.c * * pseudorandom source * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright(c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "prng.h" /* single, global prng structure */ x917_prng_t x917_prng; err_status_t x917_prng_init(rand_source_func_t random_source) { v128_t tmp_key; err_status_t status; /* initialize output count to zero */ x917_prng.octet_count = 0; /* set random source */ x917_prng.rand = random_source; /* initialize secret key from random source */ status = random_source((uint8_t *)&tmp_key, 16); if (status) return status; /* expand aes key */ aes_expand_encryption_key(&tmp_key, x917_prng.key); /* initialize prng state from random source */ status = x917_prng.rand((uint8_t *)&x917_prng.state, 16); if (status) return status; return err_status_ok; } err_status_t x917_prng_get_octet_string(uint8_t *dest, uint32_t len) { uint32_t t; v128_t buffer; uint32_t i, tail_len; err_status_t status; /* * if we need to re-initialize the prng, do so now * * avoid overflows by subtracting instead of adding */ if (x917_prng.octet_count > MAX_PRNG_OUT_LEN - len) { status = x917_prng_init(x917_prng.rand); if (status) return status; } x917_prng.octet_count += len; /* find out the time */ t = (uint32_t)time(NULL); /* loop until we have output enough data */ for (i=0; i < len/16; i++) { /* exor time into state */ x917_prng.state.v32[0] ^= t; /* copy state into buffer */ v128_copy(&buffer, &x917_prng.state); /* apply aes to buffer */ aes_encrypt(&buffer, x917_prng.key); /* write data to output */ *dest++ = buffer.v8[0]; *dest++ = buffer.v8[1]; *dest++ = buffer.v8[2]; *dest++ = buffer.v8[3]; *dest++ = buffer.v8[4]; *dest++ = buffer.v8[5]; *dest++ = buffer.v8[6]; *dest++ = buffer.v8[7]; *dest++ = buffer.v8[8]; *dest++ = buffer.v8[9]; *dest++ = buffer.v8[10]; *dest++ = buffer.v8[11]; *dest++ = buffer.v8[12]; *dest++ = buffer.v8[13]; *dest++ = buffer.v8[14]; *dest++ = buffer.v8[15]; /* exor time into buffer */ buffer.v32[0] ^= t; /* encrypt buffer */ aes_encrypt(&buffer, x917_prng.key); /* copy buffer into state */ v128_copy(&x917_prng.state, &buffer); } /* if we need to output any more octets, we'll do so now */ tail_len = len % 16; if (tail_len) { /* exor time into state */ x917_prng.state.v32[0] ^= t; /* copy value into buffer */ v128_copy(&buffer, &x917_prng.state); /* apply aes to buffer */ aes_encrypt(&buffer, x917_prng.key); /* write data to output */ for (i=0; i < tail_len; i++) { *dest++ = buffer.v8[i]; } /* now update the state one more time */ /* exor time into buffer */ buffer.v32[0] ^= t; /* encrypt buffer */ aes_encrypt(&buffer, x917_prng.key); /* copy buffer into state */ v128_copy(&x917_prng.state, &buffer); } return err_status_ok; } err_status_t x917_prng_deinit(void) { return err_status_ok; } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/rng/rand_linux_kernel.c ================================================ /* * rand_linux_kernel.c * * implements a random source using Linux kernel functions * * Marcus Sundberg * Ingate Systems AB */ /* * * Copyright(c) 2005 Ingate Systems AB * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the author(s) nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "config.h" #include "rand_source.h" err_status_t rand_source_init(void) { return err_status_ok; } err_status_t rand_source_get_octet_string(void *dest, uint32_t len) { get_random_bytes(dest, len); return err_status_ok; } err_status_t rand_source_deinit(void) { return err_status_ok; } ================================================ FILE: deps/pjsip/third_party/srtp/crypto/rng/rand_source.c ================================================ /* * rand_source.c * * implements a random source based on /dev/random * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright(c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "srtp_config.h" #if defined(DEV_URANDOM) || defined(PJ_DEV_URANDOM) # include /* for open() */ # include /* for close() */ #elif (_MSC_VER >= 1400) #define _CRT_RAND_S # include # include #else # include #endif #include "rand_source.h" /* * global dev_rand_fdes is file descriptor for /dev/random * * This variable is also used to indicate that the random source has * been initialized. When this variable is set to the value of the * #define RAND_SOURCE_NOT_READY, it indicates that the random source * is not ready to be used. The value of the #define * RAND_SOURCE_READY is for use whenever that variable is used as an * indicator of the state of the random source, but not as a file * descriptor. */ #define RAND_SOURCE_NOT_READY (-1) #define RAND_SOURCE_READY (17) static int dev_random_fdes = RAND_SOURCE_NOT_READY; err_status_t rand_source_init(void) { if (dev_random_fdes >= 0) { /* already open */ return err_status_ok; } #ifdef DEV_URANDOM /* open random source for reading */ dev_random_fdes = open(DEV_URANDOM, O_RDONLY); if (dev_random_fdes < 0) return err_status_init_fail; #elif defined(PJ_DEV_URANDOM) /* open random source for reading */ dev_random_fdes = open(PJ_DEV_URANDOM, O_RDONLY); if (dev_random_fdes < 0) { err_report(3,"Ugh: /dev/urandom not present, using rand() instead"); return err_status_ok; /* it's ok, it'll fallback to using rand() */ } #elif (_MSC_VER >= 1400) dev_random_fdes = RAND_SOURCE_READY; #else /* no random source available; let the user know */ err_report(err_level_info, "WARNING: no real random source present!\n"); dev_random_fdes = RAND_SOURCE_READY; #endif return err_status_ok; } err_status_t rand_source_get_octet_string(void *dest, uint32_t len) { /* * read len octets from /dev/random to dest, and * check return value to make sure enough octets were * written */ #ifdef DEV_URANDOM if (read(dev_random_fdes, dest, len) != len) return err_status_fail; #elif 0 && (_MSC_VER >= 1400) /* disabled rand_s, causing assertion 'rand_s not supported' in vs8 */ unsigned int *dst = dest; while (len) { unsigned int val = 0; errno_t err = rand_s(&val); if (err != 0) { return err_status_fail; } *dst++ = val; len--; } #else uint8_t *dst = (uint8_t *)dest; #ifdef PJ_DEV_URANDOM /* First try with /dev/urandom, if it's opened */ if (dev_random_fdes >= 0) { if (read(dev_random_fdes, dest, len) == len) return err_status_ok; /* success */ } #endif /* Generic C-library (rand()) version */ /* This is a random source of last resort */ while (len) { int val = rand(); /* rand() returns 0-32767 (ugh) */ /* Is this a good enough way to get random bytes? It is if it passes FIPS-140... */ *dst++ = val & 0xff; len--; } #endif return err_status_ok; } err_status_t rand_source_deinit(void) { #ifndef PJ_DEV_URANDOM if (dev_random_fdes < 0) return err_status_dealloc_fail; /* well, we haven't really failed, * * but there is something wrong */ #endif #if defined(DEV_URANDOM) || defined(PJ_DEV_URANDOM) if (dev_random_fdes >= 0) close(dev_random_fdes); dev_random_fdes = RAND_SOURCE_NOT_READY; #endif return err_status_ok; } ================================================ FILE: deps/pjsip/third_party/srtp/doc/Doxyfile ================================================ # Doxyfile 1.3-rc3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # General configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = libSRTP # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 1.3.22 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, # Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en # (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, # Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian. OUTPUT_LANGUAGE = English # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these class will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = NO # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show # all inherited members of a class in the documentation of that class # as if those members were ordinary class members. Constructors, # destructors and assignment operators of the base classes will not be # shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. It is allowed to use relative paths in the argument list. STRIP_FROM_PATH = # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower case letters. If set to YES upper case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # users are adviced to set this option to NO. CASE_SENSE_NAMES = YES # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explict @brief command for a brief description. JAVADOC_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # reimplements. INHERIT_DOCS = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 3 # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consist of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. # For instance some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources # only. Doxygen will then generate output that is more tailored for Java. # For instance namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = intro.txt ../include/srtp.h ../crypto/include/crypto_types.h ../crypto/include/err.h ../crypto/include/crypto.h crypto_kernel.txt # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp # *.h++ *.idl *.odl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories # that are symbolic links (a Unix filesystem feature) are excluded from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. EXCLUDE_PATTERNS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. INPUT_FILTER = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = NO # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output dir. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non empty doxygen will try to run # the html help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the Html help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = letter # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = header.tex # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimised for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assigments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_XML = NO # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_PREDEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse the # parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::addtions related to external references #--------------------------------------------------------------------------- # The TAGFILES tag can be used to specify one or more tagfiles. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or # super classes. Setting the tag to NO turns the diagrams off. Note that this # option is superceded by the HAVE_DOT option below. This is only a fallback. It is # recommended to install and use dot, since it yield more powerful graphs. CLASS_DIAGRAMS = YES # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found on the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_WIDTH = 1024 # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_HEIGHT = 1024 # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermedate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::addtions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO # The CGI_NAME tag should be the name of the CGI script that # starts the search engine (doxysearch) with the correct parameters. # A script with this name will be generated by doxygen. CGI_NAME = search.cgi # The CGI_URL tag should be the absolute URL to the directory where the # cgi binaries are located. See the documentation of your http daemon for # details. CGI_URL = # The DOC_URL tag should be the absolute URL to the directory where the # documentation is located. If left blank the absolute path to the # documentation, with file:// prepended to it, will be used. DOC_URL = # The DOC_ABSPATH tag should be the absolute path to the directory where the # documentation is located. If left blank the directory on the local machine # will be used. DOC_ABSPATH = # The BIN_ABSPATH tag must point to the directory where the doxysearch binary # is installed. BIN_ABSPATH = /usr/local/bin/ # The EXT_DOC_PATHS tag can be used to specify one or more paths to # documentation generated for other projects. This allows doxysearch to search # the documentation for these projects as well. EXT_DOC_PATHS = ================================================ FILE: deps/pjsip/third_party/srtp/doc/Makefile ================================================ # Makefile for libSRTP documentation # # David A. McGrew # Cisco Systems, Inc. # # This makefile does not use the autoconf system; we don't really need # it. We just run doxygen then latex. If you don't have either of # these, then there is no way that you can make your own # documentation. Of course, you can just go online at pick up the # documentation from http://srtp.sourceforge.net. srcdir = . top_srcdir = .. top_builddir = ../ # Determine the version of the library version = $(shell cat $(top_srcdir)/VERSION) .PHONY: libsrtpdoc cryptodoc clean libsrtpdoc: @if test ! -e Doxyfile; then \ echo "*** Sorry, can't build doc outside source dir"; exit 1; \ fi sed 's/LIBSRTPVERSION/$(version)/' header.template > header.tex doxygen sed 's/\subsection/\section/' latex/index.tex > latex/index.tmp mv latex/index.tmp latex/index.tex cd latex; make cp latex/refman.pdf libsrtp.pdf cryptodoc: clean doxygen crypto.dox cd latex; make cp latex/refman.pdf crypto.pdf clean: rm -rf latex/ header.tex for a in * ; do \ if [ -f "$$a~" ] ; then rm -f $$a~; fi; \ done; ================================================ FILE: deps/pjsip/third_party/srtp/doc/Makefile.in ================================================ # Makefile for libSRTP documentation # # David A. McGrew # Cisco Systems, Inc. # # This makefile does not use the autoconf system; we don't really need # it. We just run doxygen then latex. If you don't have either of # these, then there is no way that you can make your own # documentation. Of course, you can just go online at pick up the # documentation from http://srtp.sourceforge.net. srcdir = @srcdir@ top_srcdir = @top_srcdir@ top_builddir = @top_builddir@ VPATH = @srcdir@ # Determine the version of the library version = $(shell cat $(top_srcdir)/VERSION) .PHONY: libsrtpdoc cryptodoc clean libsrtpdoc: @if test ! -e Doxyfile; then \ echo "*** Sorry, can't build doc outside source dir"; exit 1; \ fi sed 's/LIBSRTPVERSION/$(version)/' header.template > header.tex doxygen sed 's/\subsection/\section/' latex/index.tex > latex/index.tmp mv latex/index.tmp latex/index.tex cd latex; make cp latex/refman.pdf libsrtp.pdf cryptodoc: clean doxygen crypto.dox cd latex; make cp latex/refman.pdf crypto.pdf clean: rm -rf latex/ header.tex for a in * ; do \ if [ -f "$$a~" ] ; then rm -f $$a~; fi; \ done; ================================================ FILE: deps/pjsip/third_party/srtp/doc/crypto_kernel.txt ================================================ /** @defgroup CryptoKernel Cryptographic Kernel All of the cryptographic functions are contained in a kernel. */ /** @defgroup CipherImplementations Ciphers @ingroup CryptoKernel @brief A generic cipher type enables cipher agility, that is, the ability to write code that runs with multiple cipher types. Ciphers can be used through the crypto kernel, or can be accessed directly, if need be. @{ */ /** * @brief Allocates a cipher of a particular type. * @warning May be implemented as a macro. */ err_status_t cipher_type_alloc(cipher_type_t *ctype, cipher_t **cipher, unsigned key_len); /** * @brief Initialized a cipher to use a particular key. May * be invoked more than once on the same cipher. * @warning May be implemented as a macro. */ err_status_t cipher_init(cipher_t *cipher, const uint8_t *key); /** * @brief Sets the initialization vector of a given cipher. * @warning May be implemented as a macro. */ err_status_t cipher_set_iv(cipher_t *cipher, void *iv); /** * @brief Encrypts a buffer with a given cipher. * @warning May be implemented as a macro. */ err_status_t cipher_encrypt(cipher_t *cipher, void *buf, unsigned int *len); /** * @brief Sets a buffer to the keystream generated by the cipher. * @warning May be implemented as a macro. */ err_status_t cipher_output(cipher_t *c, uint8_t *buffer, int num_octets_to_output); /** * @brief Deallocates a cipher. * @warning May be implemented as a macro. */ err_status_t cipher_dealloc(cipher_t *cipher); /** * @} */ */ ================================================ FILE: deps/pjsip/third_party/srtp/doc/header.template ================================================ % header.tex % % header file for the libSRTP documentation - based on the header % file generated by doxygen, with the initial chapters of the % original libSRTP documentation tacked on % \documentclass[letterpaper]{book} \usepackage{makeidx} \usepackage{fancyhdr} \usepackage{graphicx} \usepackage{multicol} \usepackage{float} \usepackage{textcomp} \usepackage{alltt} \usepackage{times} \usepackage{graphicx} \ifx\pdfoutput\undefined \usepackage[ps2pdf, pagebackref=true, colorlinks=true, linkcolor=blue ]{hyperref} \else \usepackage[pdftex, pagebackref=true, colorlinks=true, linkcolor=blue ]{hyperref} \fi \usepackage{doxygen} \makeindex \setcounter{tocdepth}{1} \renewcommand{\footrulewidth}{0.4pt} % these lengths are from DAM \textwidth = 6.5 in %\textheight = 9 in \oddsidemargin = 0.0 in \evensidemargin = 0.0 in \topmargin = 0.0 in \headheight = 0.0 in %\headsep = 0.0 in \parskip = 0.2in \parindent = 0.0in % these header and footer definitions from DAM \lhead{libSRTP} \chead{} \rhead{\rightmark} %\rhead{\slshape } \lfoot{} \cfoot{ \thepage } \rfoot{} %\fancyhead[LE,RO]{\rightmark } %\fancyhead[LO,RE]{\slshape } % let's use the palatino font \fontfamily{ppl} \selectfont \begin{document} \begin{titlepage} \vspace*{4cm} %\begin{center} {\Huge libSRTP LIBSRTPVERSION Overview and Reference Manual\\ \hrulefill }\\ \vspace*{0cm} \begin{flushright} {\Large David A. McGrew \\ \texttt{mcgrew@cisco.com} }\\ \vspace*{0.5cm} \end{flushright} %\end{center} %\includegraphics[scale=.8]{phone} \end{titlepage} \clearemptydoublepage \vspace*{3cm} {\LARGE Preface} \vspace{1cm} The original implementation and documentation of libSRTP was written by David McGrew of Cisco Systems, Inc. in order to promote the use, understanding, and interoperability of Secure RTP. Michael Jerris contributed support for building under MSVC. Andris Pavenis contributed many important fixes. Brian West contributed changes to enable dynamic linking. Yves Shumann reported documentation bugs. Randell Jesup contributed a working SRTCP implementation and other fixes. Alex Vanzella and Will Clark contributed changes so that the AES ICM implementation can be used for ISMA media encryption. Steve Underwood contributed x86\_64 portability changes. We also give thanks to Fredrik Thulin, Brian Weis, Mark Baugher, Jeff Chan, Bill Simon, Douglas Smith, Bill May, Richard Preistley, Joe Tardo and others for contributions, comments, and corrections. This reference material in this documenation was generated using the \texttt{doxygen} utility for automatic documentation of source code. \copyright 2001-2005 by David A. McGrew, Cisco Systems, Inc. \thispagestyle{empty} \clearemptydoublepage \pagenumbering{roman} \tableofcontents %\clearemptydoublepage \clearemptydoublepage \pagenumbering{arabic} ================================================ FILE: deps/pjsip/third_party/srtp/doc/intro.txt ================================================ /** @mainpage Introduction to libSRTP This document describes libSRTP, the Open Source Secure RTP library from Cisco Systems, Inc. RTP is the Real-time Transport Protocol, an IETF standard for the transport of real-time data such as telephony, audio, and video, defined by RFC1889. Secure RTP (SRTP) is an RTP profile for providing confidentiality to RTP data and authentication to the RTP header and payload. SRTP is an IETF Proposed Standard, and is defined in RFC 3711, and was developed in the IETF Audio/Video Transport (AVT) Working Group. This library supports all of the mandatory features of SRTP, but not all of the optional features. See the @ref Features section for more detailed information. This document is organized as follows. The first chapter provides background material on SRTP and overview of libSRTP. The following chapters provide a detailed reference to the libSRTP API and related functions. The reference material is created automatically (using the doxygen utility) from comments embedded in some of the C header files. The documentation is organized into modules in order to improve its clarity. These modules do not directly correspond to files. An underlying cryptographic kernel provides much of the basic functionality of libSRTP, but is mostly undocumented because it does its work behind the scenes. @section LICENSE License and Disclaimer libSRTP is distributed under the following license, which is included in the source code distribution. It is reproduced in the manual in case you got the library from another source. @latexonly \begin{quote} Copyright (c) 2001-2005 Cisco Systems, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \begin{itemize} \item Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \item Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \item Neither the name of the Cisco Systems, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. \end{itemize} THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \end{quote} @endlatexonly @section Features Supported Features This library supports all of the mandatory-to-implement features of SRTP (as defined by the most recent Internet Draft). Some of these features can be selected (or de-selected) at run time by setting an appropriate policy; this is done using the structure srtp_policy_t. Some other behaviors of the protocol can be adapted by defining an approriate event handler for the exceptional events; see the @ref SRTPevents section. Some options that are not included in the specification are supported. Most notably, the TMMH authentication function is included, though it was removed from the SRTP Internet Draft during the summer of 2002. @latexonly Some options that are described in the SRTP specification are not supported. This includes \begin{itemize} \item the Master Key Index (MKI), \item key derivation rates other than zero, \item the cipher F8, \item anti-replay lists with sizes other than 128, \item the use of the packet index to select between master keys. \end{itemize} @endlatexonly The user should be aware that it is possible to misuse this libary, and that the result may be that the security level it provides is inadequate. If you are implementing a feature using this library, you will want to read the Security Considerations section of the Internet Draft. In addition, it is important that you read and understand the terms outlined in the @ref LICENSE section. @section Installing Installing and Building libSRTP @latexonly To install libSRTP, download the latest release of the distribution from \texttt{srtp.sourceforge.net}. The format of the names of the distributions are \texttt{srtp-A.B.C.tgz}, where \texttt{A} is the version number, \texttt{B} is the major release number, \texttt{C} is the minor release number, and \texttt{tgz} is the file extension\footnote{The extension \texttt{.tgz} is identical to \texttt{tar.gz}, and indicates a compressed tar file.} You probably want to get the most recent release. Unpack the distribution and extract the source files; the directory into which the soruce files will go is named \texttt{srtp}. libSRTP uses the GNU \texttt{autoconf} and \texttt{make} utilities\footnote{BSD make will not work; if both versions of make are on your platform, you can invoke GNU make as \texttt{gmake}.}. In the \texttt{srtp} directory, run the configure script and then make: \begin{verbatim} ./configure [ options ] make \end{verbatim} The configure script accepts the following options: \begin{quote} \begin{description} \item[--help] provides a usage summary. \item[--disable-debug] compiles libSRTP without the runtime dynamic debugging system. \item[--enable-generic-aesicm] compile in changes for ismacryp \item[--enable-syslog] use syslog for error reporting. \item[--disable-stdout] diables stdout for error reporting. \item[--enable-console] use \texttt{/dev/console} for error reporting \item[--gdoi] use GDOI key management (disabled at present). \end{description} \end{quote} By default, dynamic debbuging is enabled and stdout is used for debugging. You can use the configure options to have the debugging output sent to syslog or the system console. Alternatively, you can define ERR\_REPORTING\_FILE in \texttt{include/conf.h} to be any other file that can be opened by libSRTP, and debug messages will be sent to it. This package has been tested on the following platforms: Mac OS X (powerpc-apple-darwin1.4), Cygwin (i686-pc-cygwin), Solaris (sparc-sun-solaris2.6), RedHat Linux 7.1 and 9 (i686-pc-linux), and OpenBSD (sparc-unknown-openbsd2.7). @endlatexonly @section Applications Applications @latexonly Several test drivers and a simple and portable srtp application are included in the \texttt{test/} subdirectory. \begin{center} \begin{tabular}{ll} \hline Test driver & Function tested \\ \hline kernel\_driver & crypto kernel (ciphers, auth funcs, rng) \\ srtp\_driver & srtp in-memory tests (does not use the network) \\ rdbx\_driver & rdbx (extended replay database) \\ roc\_driver & extended sequence number functions \\ replay\_driver & replay database \\ cipher\_driver & ciphers \\ auth\_driver & hash functions \\ \hline \end{tabular} \end{center} The app rtpw is a simple rtp application which reads words from /usr/dict/words and then sends them out one at a time using [s]rtp. Manual srtp keying uses the -k option; automated key management using gdoi will be added later. The usage for rtpw is \texttt{rtpw [[-d $<$debug$>$]* [-k $<$key$>$ [-a][-e]] [-s | -r] dest\_ip dest\_port][-l]} Either the -s (sender) or -r (receiver) option must be chosen. The values dest\_ip, dest\_port are the IP address and UDP port to which the dictionary will be sent, respectively. The options are: \begin{center} \begin{tabular}{ll} -s & (S)RTP sender - causes app to send words \\ -r & (S)RTP receive - causes app to receve words \\ -k $<$key$>$ & use SRTP master key $<$key$>$, where the key is a hexadecimal value (without the leading "0x") \\ -e & encrypt/decrypt (for data confidentiality) (requires use of -k option as well)\\ -a & message authentication (requires use of -k option as well) \\ -l & list the avaliable debug modules \\ -d $<$debug$>$ & turn on debugging for module $<$debug$>$ \\ \end{tabular} \end{center} In order to get a random 30-byte value for use as a key/salt pair, you can use the \texttt{rand\_gen} utility in the \texttt{test/} subdirectory. An example of an SRTP session using two rtpw programs follows: \begin{verbatim} [sh1] set k=`test/rand_gen -n 30` [sh1] echo $k c1eec3717da76195bb878578790af71c4ee9f859e197a414a78d5abc7451 [sh1]$ test/rtpw -s -k $k -ea 0.0.0.0 9999 Security services: confidentiality message authentication set master key/salt to C1EEC3717DA76195BB878578790AF71C/4EE9F859E197A414A78D5ABC7451 setting SSRC to 2078917053 sending word: A sending word: a sending word: aa sending word: aal sending word: aalii sending word: aam sending word: Aani sending word: aardvark ... [sh2] set k=c1eec3717da76195bb878578790af71c4ee9f859e197a414a78d5abc7451 [sh2]$ test/rtpw -r -k $k -ea 0.0.0.0 9999 security services: confidentiality message authentication set master key/salt to C1EEC3717DA76195BB878578790AF71C/4EE9F859E197A414A78D5ABC7451 19 octets received from SSRC 2078917053 word: A 19 octets received from SSRC 2078917053 word: a 20 octets received from SSRC 2078917053 word: aa 21 octets received from SSRC 2078917053 word: aal ... \end{verbatim} @endlatexonly @section Review Secure RTP Background In this section we review SRTP and introduce some terms that are used in libSRTP. An RTP session is defined by a pair of destination transport addresses, that is, a network address plus a pair of UDP ports for RTP and RTCP. RTCP, the RTP control protocol, is used to coordinate between the participants in an RTP session, e.g. to provide feedback from receivers to senders. An @e SRTP @e session is similarly defined; it is just an RTP session for which the SRTP profile is being used. An SRTP session consists of the traffic sent to the SRTP or SRTCP destination transport addresses. Each participant in a session is identified by a synchronization source (SSRC) identifier. Some participants may not send any SRTP traffic; they are called receivers, even though they send out SRTCP traffic, such as receiver reports. RTP allows multiple sources to send RTP and RTCP traffic during the same session. The synchronization source identifier (SSRC) is used to distinguish these sources. In libSRTP, we call the SRTP and SRTCP traffic from a particular source a @e stream. Each stream has its own SSRC, sequence number, rollover counter, and other data. A particular choice of options, cryptographic mechanisms, and keys is called a @e policy. Each stream within a session can have a distinct policy applied to it. A session policy is a collection of stream policies. A single policy can be used for all of the streams in a given session, though the case in which a single @e key is shared across multiple streams requires care. When key sharing is used, the SSRC values that identify the streams @b must be distinct. This requirement can be enforced by using the convention that each SRTP and SRTCP key is used for encryption by only a single sender. In other words, the key is shared only across streams that originate from a particular device (of course, other SRTP participants will need to use the key for decryption). libSRTP supports this enforcement by detecting the case in which a key is used for both inbound and outbound data. @section Overview libSRTP Overview libSRTP provides functions for protecting RTP and RTCP. RTP packets can be encrypted and authenticated (using the srtp_protect() function), turning them into SRTP packets. Similarly, SRTP packets can be decrypted and have their authentication verified (using the srtp_unprotect() function), turning them into RTP packets. Similar functions apply security to RTCP packets. The typedef srtp_stream_t points to a structure holding all of the state associated with an SRTP stream, including the keys and parameters for cipher and message authentication functions and the anti-replay data. A particular srtp_stream_t holds the information needed to protect a particular RTP and RTCP stream. This datatype is intentionally opaque in order to better seperate the libSRTP API from its implementation. Within an SRTP session, there can be multiple streams, each originating from a particular sender. Each source uses a distinct stream context to protect the RTP and RTCP stream that it is originating. The typedef srtp_t points to a structure holding all of the state associated with an SRTP session. There can be multiple stream contexts associated with a single srtp_t. A stream context cannot exist indepent from an srtp_t, though of course an srtp_t can be created that contains only a single stream context. A device participating in an SRTP session must have a stream context for each source in that session, so that it can process the data that it receives from each sender. In libSRTP, a session is created using the function srtp_create(). The policy to be implemented in the session is passed into this function as an srtp_policy_t structure. A single one of these structures describes the policy of a single stream. These structures can also be linked together to form an entire session policy. A linked list of srtp_policy_t structures is equivalent to a session policy. In such a policy, we refer to a single srtp_policy_t as an @e element. An srtp_policy_t strucutre contains two crypto_policy_t structures that describe the cryptograhic policies for RTP and RTCP, as well as the SRTP master key and the SSRC value. The SSRC describes what to protect (e.g. which stream), and the crypto_policy_t structures describe how to protect it. The key is contained in a policy element because it simplifies the interface to the library. In many cases, it is desirable to use the same cryptographic policies across all of the streams in a session, but to use a distinct key for each stream. A crypto_policy_t structure can be initialized by using either the crypto_policy_set_rtp_default() or crypto_policy_set_rtcp_default() functions, which set a crypto policy structure to the default policies for RTP and RTCP protection, respectively. @section Example Example Code This section provides a simple example of how to use libSRTP. The example code lacks error checking, but is functional. Here we assume that the value ssrc is already set to describe the SSRC of the stream that we are sending, and that the functions get_rtp_packet() and send_srtp_packet() are available to us. The former puts an RTP packet into the buffer and returns the number of octets written to that buffer. The latter sends the RTP packet in the buffer, given the length as its second argument. @verbatim srtp_t session; srtp_policy_t policy; uint8_t key[30]; // initialize libSRTP srtp_init(); // set policy to describe a policy for an SRTP stream crypto_policy_set_rtp_default(&policy.rtp); crypto_policy_set_rtcp_default(&policy.rtcp); policy.ssrc = ssrc; policy.key = key; policy.next = NULL; // set key to random value crypto_get_random(key, 30); // allocate and initialize the SRTP session srtp_create(&session, policy); // main loop: get rtp packets, send srtp packets while (1) { char rtp_buffer[2048]; unsigned len; len = get_rtp_packet(rtp_buffer); srtp_protect(session, rtp_buffer, &len); send_srtp_packet(rtp_buffer, len); } @endverbatim @section ISMAcryp ISMA Encryption Support The Internet Streaming Media Alliance (ISMA) specifies a way to pre-encrypt a media file prior to streaming. This method is an alternative to SRTP encryption, which is potentially useful when a particular media file will be streamed multiple times. The specification is available online at http://www.isma.tv/specreq.nsf/SpecRequest. libSRTP provides the encryption and decryption functions needed for ISMAcryp in the library @t libaesicm.a, which is included in the default Makefile target. This library is used by the MPEG4IP project; see http://mpeg4ip.sourceforge.net/. Note that ISMAcryp does not provide authentication for RTP nor RTCP, nor confidentiality for RTCP. ISMAcryp RECOMMENDS the use of SRTP message authentication for ISMAcryp streams while using ISMAcryp encryption to protect the media itself. */ ================================================ FILE: deps/pjsip/third_party/srtp/doc/references.txt ================================================ SRTP and ICM References September, 2005 This document provides references for the various cryptographic functions used in libSRTP and libaesicm. Secure RTP is defined in RFC 3711, which is included in this distribution for convenience. The counter mode definition is in Section 4.1.1 of the SRTP draft. SHA-1 is defined in FIPS-180-1, available online at the NIST website. HMAC is defined in RFC2104, and HMAC-SHA1 test vectors are available in RFC2202, which are available online at http://www.ietf.org/rfc/ ICM is defined by draft-irtf-cfrg-icm-00.txt, and its application in ISMAcryp (the Internet Streaming Media Alliance 1.0 Encryption and Authentication) is defined in that specification. It is available from http://www.isma.tv/. ================================================ FILE: deps/pjsip/third_party/srtp/include/getopt_s.h ================================================ /* * getopt.h * * interface to a minimal implementation of the getopt() function, * written so that test applications that use that function can run on * non-POSIX platforms * */ /* * * Copyright (c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef GETOPT_S_H #define GETOPT_S_H /* * getopt_s(), optarg_s, and optind_s are small, locally defined * versions of the POSIX standard getopt() interface. */ int getopt_s(int argc, char * const argv[], const char *optstring); extern char *optarg_s; /* defined in getopt.c */ extern int optind_s; /* defined in getopt.c */ #endif /* GETOPT_S_H */ ================================================ FILE: deps/pjsip/third_party/srtp/include/rtp.h ================================================ /* * rtp.h * * rtp interface for srtp reference implementation * * David A. McGrew * Cisco Systems, Inc. * * data types: * * rtp_msg_t an rtp message (the data that goes on the wire) * rtp_sender_t sender side socket and rtp info * rtp_receiver_t receiver side socket and rtp info * */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef RTP_H #define RTP_H #ifdef HAVE_NETINET_IN_H # include #elif defined HAVE_WINSOCK2_H # include #endif #include "srtp.h" typedef struct rtp_sender_ctx_t *rtp_sender_t; typedef struct rtp_receiver_ctx_t *rtp_receiver_t; unsigned int rtp_sendto(rtp_sender_t sender, const void* msg, int len); unsigned int rtp_recvfrom(rtp_receiver_t receiver, void *msg, int *len); int rtp_receiver_init(rtp_receiver_t rcvr, int socket, struct sockaddr_in addr, unsigned int ssrc); int rtp_sender_init(rtp_sender_t sender, int socket, struct sockaddr_in addr, unsigned int ssrc); /* * srtp_sender_init(...) initializes an rtp_sender_t */ int srtp_sender_init(rtp_sender_t rtp_ctx, /* structure to be init'ed */ struct sockaddr_in name, /* socket name */ sec_serv_t security_services, /* sec. servs. to be used */ unsigned char *input_key /* master key/salt in hex */ ); int srtp_receiver_init(rtp_receiver_t rtp_ctx, /* structure to be init'ed */ struct sockaddr_in name, /* socket name */ sec_serv_t security_services, /* sec. servs. to be used */ unsigned char *input_key /* master key/salt in hex */ ); int rtp_sender_init_srtp(rtp_sender_t sender, const srtp_policy_t *policy); int rtp_receiver_init_srtp(rtp_receiver_t sender, const srtp_policy_t *policy); rtp_sender_t rtp_sender_alloc(); rtp_receiver_t rtp_receiver_alloc(); /* * RTP_HEADER_LEN indicates the size of an RTP header */ #define RTP_HEADER_LEN 12 /* * RTP_MAX_BUF_LEN defines the largest RTP packet in the rtp.c implementation */ #define RTP_MAX_BUF_LEN 16384 #endif /* RTP_H */ ================================================ FILE: deps/pjsip/third_party/srtp/include/rtp_priv.h ================================================ /* * rtp_priv.h * * private, internal header file for RTP * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef RTP_PRIV_H #define RTP_PRIV_H #include "srtp_priv.h" #include "rtp.h" typedef srtp_hdr_t rtp_hdr_t; typedef struct { srtp_hdr_t header; char body[RTP_MAX_BUF_LEN]; } rtp_msg_t; typedef struct rtp_sender_ctx_t { rtp_msg_t message; int socket; srtp_ctx_t *srtp_ctx; struct sockaddr_in addr; /* reciever's address */ } rtp_sender_ctx_t; typedef struct rtp_receiver_ctx_t { rtp_msg_t message; int socket; srtp_ctx_t *srtp_ctx; struct sockaddr_in addr; /* receiver's address */ } rtp_receiver_ctx_t; #endif /* RTP_PRIV_H */ ================================================ FILE: deps/pjsip/third_party/srtp/include/srtp.h ================================================ /* * srtp.h * * interface to libsrtp * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef SRTP_H #define SRTP_H #ifdef __cplusplus extern "C" { #endif #ifdef _MSC_VER # ifdef WIN64 # pragma pack(8) # else # pragma pack(4) # endif #endif #include "crypto_kernel.h" /** * @defgroup SRTP Secure RTP * * @brief libSRTP provides functions for protecting RTP and RTCP. See * Section @ref Overview for an introduction to the use of the library. * * @{ */ /* * SRTP_MASTER_KEY_LEN is the nominal master key length supported by libSRTP */ #define SRTP_MASTER_KEY_LEN 30 /* * SRTP_MAX_KEY_LEN is the maximum key length supported by libSRTP */ #define SRTP_MAX_KEY_LEN 64 /* * SRTP_MAX_TAG_LEN is the maximum tag length supported by libSRTP */ #define SRTP_MAX_TAG_LEN 12 /** * SRTP_MAX_TRAILER_LEN is the maximum length of the SRTP trailer * (authentication tag and MKI) supported by libSRTP. This value is * the maximum number of octets that will be added to an RTP packet by * srtp_protect(). * * @brief the maximum number of octets added by srtp_protect(). */ #define SRTP_MAX_TRAILER_LEN SRTP_MAX_TAG_LEN /* * nota bene: since libSRTP doesn't support the use of the MKI, the * SRTP_MAX_TRAILER_LEN value is just the maximum tag length */ /** * @brief sec_serv_t describes a set of security services. * * A sec_serv_t enumeration is used to describe the particular * security services that will be applied by a particular crypto * policy (or other mechanism). */ typedef enum { sec_serv_none = 0, /**< no services */ sec_serv_conf = 1, /**< confidentiality */ sec_serv_auth = 2, /**< authentication */ sec_serv_conf_and_auth = 3 /**< confidentiality and authentication */ } sec_serv_t; /** * @brief crypto_policy_t describes a particular crypto policy that * can be applied to an SRTP stream. * * A crypto_policy_t describes a particular cryptographic policy that * can be applied to an SRTP or SRTCP stream. An SRTP session policy * consists of a list of these policies, one for each SRTP stream * in the session. */ typedef struct crypto_policy_t { cipher_type_id_t cipher_type; /**< An integer representing * the type of cipher. */ int cipher_key_len; /**< The length of the cipher key * in octets. */ auth_type_id_t auth_type; /**< An integer representing the * authentication function. */ int auth_key_len; /**< The length of the authentication * function key in octets. */ int auth_tag_len; /**< The length of the authentication * tag in octets. */ sec_serv_t sec_serv; /**< The flag indicating the security * services to be applied. */ } crypto_policy_t; /** * @brief ssrc_type_t describes the type of an SSRC. * * An ssrc_type_t enumeration is used to indicate a type of SSRC. See * @ref srtp_policy_t for more informataion. */ typedef enum { ssrc_undefined = 0, /**< Indicates an undefined SSRC type. */ ssrc_specific = 1, /**< Indicates a specific SSRC value */ ssrc_any_inbound = 2, /**< Indicates any inbound SSRC value (i.e. a value that is used in the function srtp_unprotect()) */ ssrc_any_outbound = 3 /**< Indicates any outbound SSRC value (i.e. a value that is used in the function srtp_protect()) */ } ssrc_type_t; /** * @brief An ssrc_t represents a particular SSRC value, or a `wildcard' SSRC. * * An ssrc_t represents a particular SSRC value (if its type is * ssrc_specific), or a wildcard SSRC value that will match all * outbound SSRCs (if its type is ssrc_any_outbound) or all inbound * SSRCs (if its type is ssrc_any_inbound). * */ typedef struct { ssrc_type_t type; /**< The type of this particular SSRC */ unsigned int value; /**< The value of this SSRC, if it is not a wildcard */ } ssrc_t; /** * @brief represents the policy for an SRTP session. * * A single srtp_policy_t struct represents the policy for a single * SRTP stream, and a linked list of these elements represents the * policy for an entire SRTP session. Each element contains the SRTP * and SRTCP crypto policies for that stream, a pointer to the SRTP * master key for that stream, the SSRC describing that stream, or a * flag indicating a `wildcard' SSRC value, and a `next' field that * holds a pointer to the next element in the list of policy elements, * or NULL if it is the last element. * * The wildcard value SSRC_ANY_INBOUND matches any SSRC from an * inbound stream that for which there is no explicit SSRC entry in * another policy element. Similarly, the value SSRC_ANY_OUTBOUND * will matches any SSRC from an outbound stream that does not appear * in another policy element. Note that wildcard SSRCs &b cannot be * used to match both inbound and outbound traffic. This restriction * is intentional, and it allows libSRTP to ensure that no security * lapses result from accidental re-use of SSRC values during key * sharing. * * * @warning The final element of the list @b must have its `next' pointer * set to NULL. */ typedef struct srtp_policy_t { ssrc_t ssrc; /**< The SSRC value of stream, or the * flags SSRC_ANY_INBOUND or * SSRC_ANY_OUTBOUND if key sharing * is used for this policy element. */ crypto_policy_t rtp; /**< SRTP crypto policy. */ crypto_policy_t rtcp; /**< SRTCP crypto policy. */ unsigned char *key; /**< Pointer to the SRTP master key for * this stream. */ struct srtp_policy_t *next; /**< Pointer to next stream policy. */ } srtp_policy_t; /** * @brief An srtp_t points to an SRTP session structure. * * The typedef srtp_t is a pointer to a structure that represents * an SRTP session. This datatype is intentially opaque in * order to separate the interface from the implementation. * * An SRTP session consists of all of the traffic sent to the RTP and * RTCP destination transport addresses, using the RTP/SAVP (Secure * Audio/Video Profile). A session can be viewed as a set of SRTP * streams, each of which originates with a different participant. */ typedef struct srtp_ctx_t *srtp_t; /** * @brief An srtp_stream_t points to an SRTP stream structure. * * The typedef srtp_stream_t is a pointer to a structure that * represents an SRTP stream. This datatype is intentionally * opaque in order to separate the interface from the implementation. * * An SRTP stream consists of all of the traffic sent to an SRTP * session by a single participant. A session can be viewed as * a set of streams. * */ typedef struct srtp_stream_ctx_t *srtp_stream_t; /** * @brief srtp_init() initializes the srtp library. * * @warning This function @b must be called before any other srtp * functions. */ err_status_t srtp_init(void); /** * @brief srtp_deinit() deinitializes the srtp library. * * @warning This function @b must be called on quitting application or * after srtp is no longer used. */ err_status_t srtp_deinit(void); /** * @brief srtp_protect() is the Secure RTP sender-side packet processing * function. * * The function call srtp_protect(ctx, rtp_hdr, len_ptr) applies SRTP * protection to the RTP packet rtp_hdr (which has length *len_ptr) using * the SRTP context ctx. If err_status_ok is returned, then rtp_hdr * points to the resulting SRTP packet and *len_ptr is the number of * octets in that packet; otherwise, no assumptions should be made * about the value of either data elements. * * The sequence numbers of the RTP packets presented to this function * need not be consecutive, but they @b must be out of order by less * than 2^15 = 32,768 packets. * * @warning This function assumes that it can write the authentication * tag into the location in memory immediately following the RTP * packet, and assumes that the RTP packet is aligned on a 32-bit * boundary. * * @param ctx is the SRTP context to use in processing the packet. * * @param rtp_hdr is a pointer to the RTP packet (before the call); after * the function returns, it points to the srtp packet. * * @param len_ptr is a pointer to the length in octets of the complete * RTP packet (header and body) before the function call, and of the * complete SRTP packet after the call, if err_status_ok was returned. * Otherwise, the value of the data to which it points is undefined. * * @return * - err_status_ok no problems * - err_status_replay_fail rtp sequence number was non-increasing * - @e other failure in cryptographic mechanisms */ err_status_t srtp_protect(srtp_t ctx, void *rtp_hdr, int *len_ptr); /** * @brief srtp_unprotect() is the Secure RTP receiver-side packet * processing function. * * The function call srtp_unprotect(ctx, srtp_hdr, len_ptr) verifies * the Secure RTP protection of the SRTP packet pointed to by srtp_hdr * (which has length *len_ptr), using the SRTP context ctx. If * err_status_ok is returned, then srtp_hdr points to the resulting * RTP packet and *len_ptr is the number of octets in that packet; * otherwise, no assumptions should be made about the value of either * data elements. * * The sequence numbers of the RTP packets presented to this function * need not be consecutive, but they @b must be out of order by less * than 2^15 = 32,768 packets. * * @warning This function assumes that the SRTP packet is aligned on a * 32-bit boundary. * * @param ctx is a pointer to the srtp_t which applies to the * particular packet. * * @param srtp_hdr is a pointer to the header of the SRTP packet * (before the call). after the function returns, it points to the * rtp packet if err_status_ok was returned; otherwise, the value of * the data to which it points is undefined. * * @param len_ptr is a pointer to the length in octets of the complete * srtp packet (header and body) before the function call, and of the * complete rtp packet after the call, if err_status_ok was returned. * Otherwise, the value of the data to which it points is undefined. * * @return * - err_status_ok if the RTP packet is valid. * - err_status_auth_fail if the SRTP packet failed the message * authentication check. * - err_status_replay_fail if the SRTP packet is a replay (e.g. packet has * already been processed and accepted). * - [other] if there has been an error in the cryptographic mechanisms. * */ err_status_t srtp_unprotect(srtp_t ctx, void *srtp_hdr, int *len_ptr); /** * @brief srtp_create() allocates and initializes an SRTP session. * The function call srtp_create(session, policy, key) allocates and * initializes an SRTP session context, applying the given policy and * key. * * @param session is the SRTP session to which the policy is to be added. * * @param policy is the srtp_policy_t struct that describes the policy * for the session. The struct may be a single element, or it may be * the head of a list, in which case each element of the list is * processed. It may also be NULL, in which case streams should be added * later using srtp_add_stream(). The final element of the list @b must * have its `next' field set to NULL. * * @return * - err_status_ok if creation succeded. * - err_status_alloc_fail if allocation failed. * - err_status_init_fail if initialization failed. */ err_status_t srtp_create(srtp_t *session, const srtp_policy_t *policy); /** * @brief srtp_add_stream() allocates and initializes an SRTP stream * within a given SRTP session. * * The function call srtp_add_stream(session, policy) allocates and * initializes a new SRTP stream within a given, previously created * session, applying the policy given as the other argument to that * stream. * * @return values: * - err_status_ok if stream creation succeded. * - err_status_alloc_fail if stream allocation failed * - err_status_init_fail if stream initialization failed. */ err_status_t srtp_add_stream(srtp_t session, const srtp_policy_t *policy); /** * @brief srtp_remove_stream() deallocates an SRTP stream. * * The function call srtp_remove_stream(session, ssrc) removes * the SRTP stream with the SSRC value ssrc from the SRTP session * context given by the argument session. * * @param session is the SRTP session from which the stream * will be removed. * * @param ssrc is the SSRC value of the stream to be removed. * * @warning Wildcard SSRC values cannot be removed from a * session. * * @return * - err_status_ok if the stream deallocation succeded. * - [other] otherwise. * */ err_status_t srtp_remove_stream(srtp_t session, unsigned int ssrc); /** * @brief crypto_policy_set_rtp_default() sets a crypto policy * structure to the SRTP default policy for RTP protection. * * @param p is a pointer to the policy structure to be set * * The function call crypto_policy_set_rtp_default(&p) sets the * crypto_policy_t at location p to the SRTP default policy for RTP * protection, as defined in the specification. This function is a * convenience that helps to avoid dealing directly with the policy * data structure. You are encouraged to initialize policy elements * with this function call. Doing so may allow your code to be * forward compatible with later versions of libSRTP that include more * elements in the crypto_policy_t datatype. * * @return void. * */ void crypto_policy_set_rtp_default(crypto_policy_t *p); /** * @brief crypto_policy_set_rtcp_default() sets a crypto policy * structure to the SRTP default policy for RTCP protection. * * @param p is a pointer to the policy structure to be set * * The function call crypto_policy_set_rtcp_default(&p) sets the * crypto_policy_t at location p to the SRTP default policy for RTCP * protection, as defined in the specification. This function is a * convenience that helps to avoid dealing directly with the policy * data structure. You are encouraged to initialize policy elements * with this function call. Doing so may allow your code to be * forward compatible with later versions of libSRTP that include more * elements in the crypto_policy_t datatype. * * @return void. * */ void crypto_policy_set_rtcp_default(crypto_policy_t *p); /** * @brief crypto_policy_set_aes_cm_128_hmac_sha1_80() sets a crypto * policy structure to the SRTP default policy for RTP protection. * * @param p is a pointer to the policy structure to be set * * The function crypto_policy_set_aes_cm_128_hmac_sha1_80() is a * synonym for crypto_policy_set_rtp_default(). It conforms to the * naming convention used in * http://www.ietf.org/internet-drafts/draft-ietf-mmusic-sdescriptions-12.txt * * @return void. * */ #define crypto_policy_set_aes_cm_128_hmac_sha1_80(p) crypto_policy_set_rtp_default(p) /** * @brief crypto_policy_set_aes_cm_128_hmac_sha1_32() sets a crypto * policy structure to a short-authentication tag policy * * @param p is a pointer to the policy structure to be set * * The function call crypto_policy_set_aes_cm_128_hmac_sha1_32(&p) * sets the crypto_policy_t at location p to use policy * AES_CM_128_HMAC_SHA1_32 as defined in * draft-ietf-mmusic-sdescriptions-12.txt. This policy uses AES-128 * Counter Mode encryption and HMAC-SHA1 authentication, with an * authentication tag that is only 32 bits long. This length is * considered adequate only for protecting audio and video media that * use a stateless playback function. See Section 7.5 of RFC 3711 * (http://www.ietf.org/rfc/rfc3711.txt). * * This function is a convenience that helps to avoid dealing directly * with the policy data structure. You are encouraged to initialize * policy elements with this function call. Doing so may allow your * code to be forward compatible with later versions of libSRTP that * include more elements in the crypto_policy_t datatype. * * @warning This crypto policy is intended for use in SRTP, but not in * SRTCP. It is recommended that a policy that uses longer * authentication tags be used for SRTCP. See Section 7.5 of RFC 3711 * (http://www.ietf.org/rfc/rfc3711.txt). * * @return void. * */ void crypto_policy_set_aes_cm_128_hmac_sha1_32(crypto_policy_t *p); /** * @brief crypto_policy_set_aes_cm_128_null_auth() sets a crypto * policy structure to an encryption-only policy * * @param p is a pointer to the policy structure to be set * * The function call crypto_policy_set_aes_cm_128_null_auth(&p) sets * the crypto_policy_t at location p to use the SRTP default cipher * (AES-128 Counter Mode), but to use no authentication method. This * policy is NOT RECOMMENDED unless it is unavoidable; see Section 7.5 * of RFC 3711 (http://www.ietf.org/rfc/rfc3711.txt). * * This function is a convenience that helps to avoid dealing directly * with the policy data structure. You are encouraged to initialize * policy elements with this function call. Doing so may allow your * code to be forward compatible with later versions of libSRTP that * include more elements in the crypto_policy_t datatype. * * @warning This policy is NOT RECOMMENDED for SRTP unless it is * unavoidable, and it is NOT RECOMMENDED at all for SRTCP; see * Section 7.5 of RFC 3711 (http://www.ietf.org/rfc/rfc3711.txt). * * @return void. * */ void crypto_policy_set_aes_cm_128_null_auth(crypto_policy_t *p); /** * @brief crypto_policy_set_null_cipher_hmac_sha1_80() sets a crypto * policy structure to an authentication-only policy * * @param p is a pointer to the policy structure to be set * * The function call crypto_policy_set_null_cipher_hmac_sha1_80(&p) * sets the crypto_policy_t at location p to use HMAC-SHA1 with an 80 * bit authentication tag to provide message authentication, but to * use no encryption. This policy is NOT RECOMMENDED for SRTP unless * there is a requirement to forego encryption. * * This function is a convenience that helps to avoid dealing directly * with the policy data structure. You are encouraged to initialize * policy elements with this function call. Doing so may allow your * code to be forward compatible with later versions of libSRTP that * include more elements in the crypto_policy_t datatype. * * @warning This policy is NOT RECOMMENDED for SRTP unless there is a * requirement to forego encryption. * * @return void. * */ void crypto_policy_set_null_cipher_hmac_sha1_80(crypto_policy_t *p); /** * @brief srtp_dealloc() deallocates storage for an SRTP session * context. * * The function call srtp_dealloc(s) deallocates storage for the * SRTP session context s. This function should be called no more * than one time for each of the contexts allocated by the function * srtp_create(). * * @param s is the srtp_t for the session to be deallocated. * * @return * - err_status_ok if there no problems. * - err_status_dealloc_fail a memory deallocation failure occured. */ err_status_t srtp_dealloc(srtp_t s); /* * @brief identifies a particular SRTP profile * * An srtp_profile_t enumeration is used to identify a particular SRTP * profile (that is, a set of algorithms and parameters). These * profiles are defined in the DTLS-SRTP draft. */ typedef enum { srtp_profile_reserved = 0, srtp_profile_aes128_cm_sha1_80 = 1, srtp_profile_aes128_cm_sha1_32 = 2, srtp_profile_aes256_cm_sha1_80 = 3, srtp_profile_aes256_cm_sha1_32 = 4, srtp_profile_null_sha1_80 = 5, srtp_profile_null_sha1_32 = 6, } srtp_profile_t; /** * @brief crypto_policy_set_from_profile_for_rtp() sets a crypto policy * structure to the appropriate value for RTP based on an srtp_profile_t * * @param p is a pointer to the policy structure to be set * * The function call crypto_policy_set_rtp_default(&policy, profile) * sets the crypto_policy_t at location policy to the policy for RTP * protection, as defined by the srtp_profile_t profile. * * This function is a convenience that helps to avoid dealing directly * with the policy data structure. You are encouraged to initialize * policy elements with this function call. Doing so may allow your * code to be forward compatible with later versions of libSRTP that * include more elements in the crypto_policy_t datatype. * * @return values * - err_status_ok no problems were encountered * - err_status_bad_param the profile is not supported * */ err_status_t crypto_policy_set_from_profile_for_rtp(crypto_policy_t *policy, srtp_profile_t profile); /** * @brief crypto_policy_set_from_profile_for_rtcp() sets a crypto policy * structure to the appropriate value for RTCP based on an srtp_profile_t * * @param p is a pointer to the policy structure to be set * * The function call crypto_policy_set_rtcp_default(&policy, profile) * sets the crypto_policy_t at location policy to the policy for RTCP * protection, as defined by the srtp_profile_t profile. * * This function is a convenience that helps to avoid dealing directly * with the policy data structure. You are encouraged to initialize * policy elements with this function call. Doing so may allow your * code to be forward compatible with later versions of libSRTP that * include more elements in the crypto_policy_t datatype. * * @return values * - err_status_ok no problems were encountered * - err_status_bad_param the profile is not supported * */ err_status_t crypto_policy_set_from_profile_for_rtcp(crypto_policy_t *policy, srtp_profile_t profile); /** * @brief returns the master key length for a given SRTP profile */ unsigned int srtp_profile_get_master_key_length(srtp_profile_t profile); /** * @brief returns the master salt length for a given SRTP profile */ unsigned int srtp_profile_get_master_salt_length(srtp_profile_t profile); /** * @brief appends the salt to the key * * The function call append_salt_to_key(k, klen, s, slen) * copies the string s to the location at klen bytes following * the location k. * * @warning There must be at least bytes_in_salt + bytes_in_key bytes * available at the location pointed to by key. * */ void append_salt_to_key(unsigned char *key, unsigned int bytes_in_key, unsigned char *salt, unsigned int bytes_in_salt); /** * @} */ /** * @defgroup SRTCP Secure RTCP * @ingroup SRTP * * @brief Secure RTCP functions are used to protect RTCP traffic. * * RTCP is the control protocol for RTP. libSRTP protects RTCP * traffic in much the same way as it does RTP traffic. The function * srtp_protect_rtcp() applies cryptographic protections to outbound * RTCP packets, and srtp_unprotect_rtcp() verifies the protections on * inbound RTCP packets. * * A note on the naming convention: srtp_protect_rtcp() has an srtp_t * as its first argument, and thus has `srtp_' as its prefix. The * trailing `_rtcp' indicates the protocol on which it acts. * * @{ */ /** * @brief srtp_protect_rtcp() is the Secure RTCP sender-side packet * processing function. * * The function call srtp_protect_rtcp(ctx, rtp_hdr, len_ptr) applies * SRTCP protection to the RTCP packet rtcp_hdr (which has length * *len_ptr) using the SRTP session context ctx. If err_status_ok is * returned, then rtp_hdr points to the resulting SRTCP packet and * *len_ptr is the number of octets in that packet; otherwise, no * assumptions should be made about the value of either data elements. * * @warning This function assumes that it can write the authentication * tag into the location in memory immediately following the RTCP * packet, and assumes that the RTCP packet is aligned on a 32-bit * boundary. * * @param ctx is the SRTP context to use in processing the packet. * * @param rtcp_hdr is a pointer to the RTCP packet (before the call); after * the function returns, it points to the srtp packet. * * @param pkt_octet_len is a pointer to the length in octets of the * complete RTCP packet (header and body) before the function call, * and of the complete SRTCP packet after the call, if err_status_ok * was returned. Otherwise, the value of the data to which it points * is undefined. * * @return * - err_status_ok if there were no problems. * - [other] if there was a failure in * the cryptographic mechanisms. */ err_status_t srtp_protect_rtcp(srtp_t ctx, void *rtcp_hdr, int *pkt_octet_len); /** * @brief srtp_unprotect_rtcp() is the Secure RTCP receiver-side packet * processing function. * * The function call srtp_unprotect_rtcp(ctx, srtp_hdr, len_ptr) * verifies the Secure RTCP protection of the SRTCP packet pointed to * by srtcp_hdr (which has length *len_ptr), using the SRTP session * context ctx. If err_status_ok is returned, then srtcp_hdr points * to the resulting RTCP packet and *len_ptr is the number of octets * in that packet; otherwise, no assumptions should be made about the * value of either data elements. * * @warning This function assumes that the SRTCP packet is aligned on a * 32-bit boundary. * * @param ctx is a pointer to the srtp_t which applies to the * particular packet. * * @param srtcp_hdr is a pointer to the header of the SRTCP packet * (before the call). After the function returns, it points to the * rtp packet if err_status_ok was returned; otherwise, the value of * the data to which it points is undefined. * * @param pkt_octet_len is a pointer to the length in octets of the * complete SRTCP packet (header and body) before the function call, * and of the complete rtp packet after the call, if err_status_ok was * returned. Otherwise, the value of the data to which it points is * undefined. * * @return * - err_status_ok if the RTCP packet is valid. * - err_status_auth_fail if the SRTCP packet failed the message * authentication check. * - err_status_replay_fail if the SRTCP packet is a replay (e.g. has * already been processed and accepted). * - [other] if there has been an error in the cryptographic mechanisms. * */ err_status_t srtp_unprotect_rtcp(srtp_t ctx, void *srtcp_hdr, int *pkt_octet_len); /** * @} */ /** * @defgroup SRTPevents SRTP events and callbacks * @ingroup SRTP * * @brief libSRTP can use a user-provided callback function to * handle events. * * * libSRTP allows a user to provide a callback function to handle * events that need to be dealt with outside of the data plane (see * the enum srtp_event_t for a description of these events). Dealing * with these events is not a strict necessity; they are not * security-critical, but the application may suffer if they are not * handled. The function srtp_set_event_handler() is used to provide * the callback function. * * A default event handler that merely reports on the events as they * happen is included. It is also possible to set the event handler * function to NULL, in which case all events will just be silently * ignored. * * @{ */ /** * @brief srtp_event_t defines events that need to be handled * * The enum srtp_event_t defines events that need to be handled * outside the `data plane', such as SSRC collisions and * key expirations. * * When a key expires or the maximum number of packets has been * reached, an SRTP stream will enter an `expired' state in which no * more packets can be protected or unprotected. When this happens, * it is likely that you will want to either deallocate the stream * (using srtp_stream_dealloc()), and possibly allocate a new one. * * When an SRTP stream expires, the other streams in the same session * are unaffected, unless key sharing is used by that stream. In the * latter case, all of the streams in the session will expire. */ typedef enum { event_ssrc_collision, /**< * An SSRC collision occured. */ event_key_soft_limit, /**< An SRTP stream reached the soft key * usage limit and will expire soon. */ event_key_hard_limit, /**< An SRTP stream reached the hard * key usage limit and has expired. */ event_packet_index_limit /**< An SRTP stream reached the hard * packet limit (2^48 packets). */ } srtp_event_t; /** * @brief srtp_event_data_t is the structure passed as a callback to * the event handler function * * The struct srtp_event_data_t holds the data passed to the event * handler function. */ typedef struct srtp_event_data_t { srtp_t session; /**< The session in which the event happend. */ srtp_stream_t stream; /**< The stream in which the event happend. */ srtp_event_t event; /**< An enum indicating the type of event. */ } srtp_event_data_t; /** * @brief srtp_event_handler_func_t is the function prototype for * the event handler. * * The typedef srtp_event_handler_func_t is the prototype for the * event handler function. It has as its only argument an * srtp_event_data_t which describes the event that needs to be handled. * There can only be a single, global handler for all events in * libSRTP. */ typedef void (srtp_event_handler_func_t)(srtp_event_data_t *data); /** * @brief sets the event handler to the function supplied by the caller. * * The function call srtp_install_event_handler(func) sets the event * handler function to the value func. The value NULL is acceptable * as an argument; in this case, events will be ignored rather than * handled. * * @param func is a pointer to a fuction that takes an srtp_event_data_t * pointer as an argument and returns void. This function * will be used by libSRTP to handle events. */ err_status_t srtp_install_event_handler(srtp_event_handler_func_t func); /** * @} */ /* in host order, so outside the #if */ #define SRTCP_E_BIT 0x80000000 /* for byte-access */ #define SRTCP_E_BYTE_BIT 0x80 #define SRTCP_INDEX_MASK 0x7fffffff #ifdef _MSC_VER #pragma pack() #endif #ifdef __cplusplus } #endif #endif /* SRTP_H */ ================================================ FILE: deps/pjsip/third_party/srtp/include/srtp_priv.h ================================================ /* * srtp_priv.h * * private internal data structures and functions for libSRTP * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006 Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef SRTP_PRIV_H #define SRTP_PRIV_H #include "srtp.h" #include "rdbx.h" #include "rdb.h" #include "integers.h" /* * an srtp_hdr_t represents the srtp header * * in this implementation, an srtp_hdr_t is assumed to be 32-bit aligned * * (note that this definition follows that of RFC 1889 Appendix A, but * is not identical) */ #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable:4214) // bit field types other than int #endif #ifndef WORDS_BIGENDIAN /* * srtp_hdr_t represents an RTP or SRTP header. The bit-fields in * this structure should be declared "unsigned int" instead of * "unsigned char", but doing so causes the MS compiler to not * fully pack the bit fields. */ typedef struct { unsigned char cc:4; /* CSRC count */ unsigned char x:1; /* header extension flag */ unsigned char p:1; /* padding flag */ unsigned char version:2; /* protocol version */ unsigned char pt:7; /* payload type */ unsigned char m:1; /* marker bit */ uint16_t seq; /* sequence number */ uint32_t ts; /* timestamp */ uint32_t ssrc; /* synchronization source */ } srtp_hdr_t; #else /* BIG_ENDIAN */ typedef struct { unsigned char version:2; /* protocol version */ unsigned char p:1; /* padding flag */ unsigned char x:1; /* header extension flag */ unsigned char cc:4; /* CSRC count */ unsigned char m:1; /* marker bit */ unsigned pt:7; /* payload type */ uint16_t seq; /* sequence number */ uint32_t ts; /* timestamp */ uint32_t ssrc; /* synchronization source */ } srtp_hdr_t; #endif typedef struct { uint16_t profile_specific; /* profile-specific info */ uint16_t length; /* number of 32-bit words in extension */ } srtp_hdr_xtnd_t; /* * srtcp_hdr_t represents a secure rtcp header * * in this implementation, an srtcp header is assumed to be 32-bit * alinged */ #ifndef WORDS_BIGENDIAN typedef struct { unsigned char rc:5; /* reception report count */ unsigned char p:1; /* padding flag */ unsigned char version:2; /* protocol version */ unsigned char pt:8; /* payload type */ uint16_t len; /* length */ uint32_t ssrc; /* synchronization source */ } srtcp_hdr_t; typedef struct { unsigned int index:31; /* srtcp packet index in network order! */ unsigned int e:1; /* encrypted? 1=yes */ /* optional mikey/etc go here */ /* and then the variable-length auth tag */ } srtcp_trailer_t; #else /* BIG_ENDIAN */ typedef struct { unsigned char version:2; /* protocol version */ unsigned char p:1; /* padding flag */ unsigned char rc:5; /* reception report count */ unsigned char pt:8; /* payload type */ uint16_t len; /* length */ uint32_t ssrc; /* synchronization source */ } srtcp_hdr_t; typedef struct { unsigned int version:2; /* protocol version */ unsigned int p:1; /* padding flag */ unsigned int count:5; /* varies by packet type */ unsigned int pt:8; /* payload type */ uint16_t length; /* len of uint32s of packet less header */ } rtcp_common_t; typedef struct { unsigned int e:1; /* encrypted? 1=yes */ unsigned int index:31; /* srtcp packet index */ /* optional mikey/etc go here */ /* and then the variable-length auth tag */ } srtcp_trailer_t; #endif #ifdef _MSC_VER # pragma warning( pop ) #endif /* * the following declarations are libSRTP internal functions */ /* * srtp_get_stream(ssrc) returns a pointer to the stream corresponding * to ssrc, or NULL if no stream exists for that ssrc */ srtp_stream_t srtp_get_stream(srtp_t srtp, uint32_t ssrc); /* * srtp_stream_init_keys(s, k) (re)initializes the srtp_stream_t s by * deriving all of the needed keys using the KDF and the key k. */ err_status_t srtp_stream_init_keys(srtp_stream_t srtp, const void *key); /* * libsrtp internal datatypes */ typedef enum direction_t { dir_unknown = 0, dir_srtp_sender = 1, dir_srtp_receiver = 2 } direction_t; /* * an srtp_stream_t has its own SSRC, encryption key, authentication * key, sequence number, and replay database * * note that the keys might not actually be unique, in which case the * cipher_t and auth_t pointers will point to the same structures */ typedef struct srtp_stream_ctx_t { uint32_t ssrc; cipher_t *rtp_cipher; auth_t *rtp_auth; rdbx_t rtp_rdbx; sec_serv_t rtp_services; cipher_t *rtcp_cipher; auth_t *rtcp_auth; rdb_t rtcp_rdb; sec_serv_t rtcp_services; key_limit_ctx_t *limit; direction_t direction; struct srtp_stream_ctx_t *next; /* linked list of streams */ } srtp_stream_ctx_t; /* * an srtp_ctx_t holds a stream list and a service description */ typedef struct srtp_ctx_t { srtp_stream_ctx_t *stream_list; /* linked list of streams */ srtp_stream_ctx_t *stream_template; /* act as template for other streams */ } srtp_ctx_t; /* * srtp_handle_event(srtp, srtm, evnt) calls the event handling * function, if there is one. * * This macro is not included in the documentation as it is * an internal-only function. */ #define srtp_handle_event(srtp, strm, evnt) \ if(srtp_event_handler) { \ srtp_event_data_t data; \ data.session = srtp; \ data.stream = strm; \ data.event = evnt; \ srtp_event_handler(&data); \ } #endif /* SRTP_PRIV_H */ ================================================ FILE: deps/pjsip/third_party/srtp/include/ut_sim.h ================================================ /* * ut-sim.h * * an unreliable transport simulator * (for testing replay databases and suchlike) * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef UT_SIM_H #define UT_SIM_H #include "integers.h" /* for uint32_t */ #define UT_BUF 160 /* maximum amount of packet reorder */ typedef struct { uint32_t index; uint32_t buffer[UT_BUF]; } ut_connection; /* * ut_init(&u) initializes the ut_connection * * this function should always be the first one called on a new * ut_connection */ void ut_init(ut_connection *utc); /* * ut_next_index(&u) returns the next index from the simulated * unreliable connection */ uint32_t ut_next_index(ut_connection *utc); #endif /* UT_SIM_H */ ================================================ FILE: deps/pjsip/third_party/srtp/install-sh ================================================ #!/bin/sh # install - install a program, script, or datafile # This comes from X11R5 (mit/util/scripts/install.sh). # # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. It can only install one file at a time, a restriction # shared with many OS's install programs. # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else true fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: chmodcmd="" else instcmd=mkdir fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then true else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else true fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else true fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else true fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else true fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 ================================================ FILE: deps/pjsip/third_party/srtp/pjlib/srtp_err.c ================================================ /* $Id: srtp_err.c 1907 2008-04-03 22:03:14Z bennylp $ */ /* * Copyright (C) 2003-2007 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "err.h" #include /* Redirect libsrtp error to PJ_LOG */ err_reporting_level_t err_level = err_level_none; err_status_t err_reporting_init(char *ident) { return err_status_ok; } void err_report(int priority, char *format, ...) { va_list args; #if PJ_LOG_MAX_LEVEL >= 1 if (priority <= err_level) { va_start(args, format); pj_log("libsrtp", priority, format, args); va_end(args); } #endif } void err_reporting_set_level(err_reporting_level_t lvl) { err_level = lvl; } ================================================ FILE: deps/pjsip/third_party/srtp/srtp/srtp.c ================================================ /* * srtp.c * * the secure real-time transport protocol * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "srtp_priv.h" #include "aes_icm.h" /* aes_icm is used in the KDF */ #include "alloc.h" /* for crypto_alloc() */ #ifndef SRTP_KERNEL # include # ifdef HAVE_NETINET_IN_H # include # elif defined(HAVE_WINSOCK2_H) # include # endif #endif /* ! SRTP_KERNEL */ extern cipher_type_t aes_icm; extern auth_type_t tmmhv2; /* the debug module for srtp */ debug_module_t mod_srtp = { 0, /* debugging is off by default */ "srtp" /* printable name for module */ }; #define octets_in_rtp_header 12 #define uint32s_in_rtp_header 3 #define octets_in_rtcp_header 8 #define uint32s_in_rtcp_header 2 err_status_t srtp_stream_alloc(srtp_stream_ctx_t **str_ptr, const srtp_policy_t *p) { srtp_stream_ctx_t *str; err_status_t stat; /* * This function allocates the stream context, rtp and rtcp ciphers * and auth functions, and key limit structure. If there is a * failure during allocation, we free all previously allocated * memory and return a failure code. The code could probably * be improved, but it works and should be clear. */ /* allocate srtp stream and set str_ptr */ str = (srtp_stream_ctx_t *) crypto_alloc(sizeof(srtp_stream_ctx_t)); if (str == NULL) return err_status_alloc_fail; *str_ptr = str; /* allocate cipher */ stat = crypto_kernel_alloc_cipher(p->rtp.cipher_type, &str->rtp_cipher, p->rtp.cipher_key_len); if (stat) { crypto_free(str); return stat; } /* allocate auth function */ stat = crypto_kernel_alloc_auth(p->rtp.auth_type, &str->rtp_auth, p->rtp.auth_key_len, p->rtp.auth_tag_len); if (stat) { cipher_dealloc(str->rtp_cipher); crypto_free(str); return stat; } /* allocate key limit structure */ str->limit = (key_limit_ctx_t*) crypto_alloc(sizeof(key_limit_ctx_t)); if (str->limit == NULL) { auth_dealloc(str->rtp_auth); cipher_dealloc(str->rtp_cipher); crypto_free(str); return err_status_alloc_fail; } /* * ...and now the RTCP-specific initialization - first, allocate * the cipher */ stat = crypto_kernel_alloc_cipher(p->rtcp.cipher_type, &str->rtcp_cipher, p->rtcp.cipher_key_len); if (stat) { auth_dealloc(str->rtp_auth); cipher_dealloc(str->rtp_cipher); crypto_free(str->limit); crypto_free(str); return stat; } /* allocate auth function */ stat = crypto_kernel_alloc_auth(p->rtcp.auth_type, &str->rtcp_auth, p->rtcp.auth_key_len, p->rtcp.auth_tag_len); if (stat) { cipher_dealloc(str->rtcp_cipher); auth_dealloc(str->rtp_auth); cipher_dealloc(str->rtp_cipher); crypto_free(str->limit); crypto_free(str); return stat; } return err_status_ok; } err_status_t srtp_stream_dealloc(srtp_t session, srtp_stream_ctx_t *stream) { err_status_t status; /* * we use a conservative deallocation strategy - if any deallocation * fails, then we report that fact without trying to deallocate * anything else */ /* deallocate cipher, if it is not the same as that in template */ if (session->stream_template && stream->rtp_cipher == session->stream_template->rtp_cipher) { /* do nothing */ } else { status = cipher_dealloc(stream->rtp_cipher); if (status) return status; } /* deallocate auth function, if it is not the same as that in template */ if (session->stream_template && stream->rtp_auth == session->stream_template->rtp_auth) { /* do nothing */ } else { status = auth_dealloc(stream->rtp_auth); if (status) return status; } /* deallocate key usage limit, if it is not the same as that in template */ if (session->stream_template && stream->limit == session->stream_template->limit) { /* do nothing */ } else { crypto_free(stream->limit); } /* * deallocate rtcp cipher, if it is not the same as that in * template */ if (session->stream_template && stream->rtcp_cipher == session->stream_template->rtcp_cipher) { /* do nothing */ } else { status = cipher_dealloc(stream->rtcp_cipher); if (status) return status; } /* * deallocate rtcp auth function, if it is not the same as that in * template */ if (session->stream_template && stream->rtcp_auth == session->stream_template->rtcp_auth) { /* do nothing */ } else { status = auth_dealloc(stream->rtcp_auth); if (status) return status; } /* deallocate srtp stream context */ crypto_free(stream); return err_status_ok; } /* * srtp_stream_clone(stream_template, new) allocates a new stream and * initializes it using the cipher and auth of the stream_template * * the only unique data in a cloned stream is the replay database and * the SSRC */ err_status_t srtp_stream_clone(const srtp_stream_ctx_t *stream_template, uint32_t ssrc, srtp_stream_ctx_t **str_ptr) { err_status_t status; srtp_stream_ctx_t *str; debug_print(mod_srtp, "cloning stream (SSRC: 0x%08x)", ssrc); /* allocate srtp stream and set str_ptr */ str = (srtp_stream_ctx_t *) crypto_alloc(sizeof(srtp_stream_ctx_t)); if (str == NULL) return err_status_alloc_fail; *str_ptr = str; /* set cipher and auth pointers to those of the template */ str->rtp_cipher = stream_template->rtp_cipher; str->rtp_auth = stream_template->rtp_auth; str->rtcp_cipher = stream_template->rtcp_cipher; str->rtcp_auth = stream_template->rtcp_auth; /* set key limit to point to that of the template */ status = key_limit_clone(stream_template->limit, &str->limit); if (status) return status; /* initialize replay databases */ rdbx_init(&str->rtp_rdbx); rdb_init(&str->rtcp_rdb); /* set ssrc to that provided */ str->ssrc = ssrc; /* set direction and security services */ str->direction = stream_template->direction; str->rtp_services = stream_template->rtp_services; str->rtcp_services = stream_template->rtcp_services; /* defensive coding */ str->next = NULL; return err_status_ok; } /* * key derivation functions, internal to libSRTP * * srtp_kdf_t is a key derivation context * * srtp_kdf_init(&kdf, k) initializes kdf with the key k * * srtp_kdf_generate(&kdf, l, kl, keylen) derives the key * corresponding to label l and puts it into kl; the length * of the key in octets is provided as keylen. this function * should be called once for each subkey that is derived. * * srtp_kdf_clear(&kdf) zeroizes the kdf state */ typedef enum { label_rtp_encryption = 0x00, label_rtp_msg_auth = 0x01, label_rtp_salt = 0x02, label_rtcp_encryption = 0x03, label_rtcp_msg_auth = 0x04, label_rtcp_salt = 0x05 } srtp_prf_label; /* * srtp_kdf_t represents a key derivation function. The SRTP * default KDF is the only one implemented at present. */ typedef struct { aes_icm_ctx_t c; /* cipher used for key derivation */ } srtp_kdf_t; err_status_t srtp_kdf_init(srtp_kdf_t *kdf, const uint8_t key[30]) { aes_icm_context_init(&kdf->c, key); return err_status_ok; } err_status_t srtp_kdf_generate(srtp_kdf_t *kdf, srtp_prf_label label, uint8_t *key, int length) { v128_t nonce; /* set eigth octet of nonce to

    *
  1. The own ZID, should always be the same (in theroy we coud use different local ZIDs), hex value of random data
  2. *
  3. the partner's (remote) ZID, hex value of random data
  4. *
  5. the flag byte (bit field, explanation see below and in ZIDRecordDb.h: *
      *
    • Valid = 0x1;
    • *
    • SASVerified = 0x2;
    • *
    • RS1Valid = 0x4;
    • *
    • RS2Valid = 0x8;
    • *
    • MITMKeyAvailable= 0x10;
    • *
    • inUse = 0x20;
    • *
    *
  6. *
  7. RS1 value, hex field, 32 binary bytes
  8. *
  9. RS1 last used timestamp (Unix epoch), decimal, currently not used, always 0
  10. *
  11. RS1 Time-To-Live timestamp (Unix epoch), decimal, if -1: valid for ever, otherwise the time it's not longer valid
  12. *
  13. RS2 value, hex field, 32 binary bytes
  14. *
  15. RS2 last used timestamp (Unix epoch), decimal, currently not used, always 0
  16. *
  17. RS2 Time-To-Live timestamp (Unix epoch), decimal, if -1: valid for ever, otherwise the time it's not longer valid
  18. *
  19. trusted PBX shared key value, hex field, 32 binary bytes, valid only if MITMKeyAvailable bit is set
  20. *
  21. trusted PBX shared key last used timestamp (Unix epoch), decimal, currently not used, always 0
  22. *
  23. Secure since timestamp (Unix epoch), decimal, shows time ZRTP created this ZID record
  24. *
  25. Name, may be empty
  26. *
* * @param stmt a void pointer to a sqlite3 statement (SQL cursor) * * @param output that will get the peer's name. The returned name will * be truncated to 200 bytes * * @return void pointer to statment if successful, this is the same pointer as * the @c stmt input parameter. The function returns @c NULL if either * no more record is available or it got another error. */ virtual void *readNextRecord(void *stmt, std::string *output) =0; virtual void closeOpenStatment(void *stmt) =0; }; /** * @} */ #endif ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZIDCacheDb.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #ifndef _ZIDCACHEDB_H_ #define _ZIDCACHEDB_H_ /** * @file ZIDCacheDb.h * @brief ZID cache management * * A ZID file stores (caches) some data that helps ZRTP to achives its * key continuity feature. See @c ZIDRecordDb for further info which data * the ZID file contains. * * @ingroup GNU_ZRTP * @{ */ /** * This class implements a ZID (ZRTP Identifiers) file. * * The interface defintion @c ZIDCache.h contains the method documentation. * The ZID cache file holds information about peers. * * @author: Werner Dittmann */ class __EXPORT ZIDCacheDb: public ZIDCache { private: void *zidFile; unsigned char associatedZid[IDENTIFIER_LEN]; dbCacheOps_t cacheOps; char errorBuffer[DB_CACHE_ERR_BUFF_SIZE]; void createZIDFile(char* name); void formatOutput(remoteZidRecord_t *remZid, const char *nameBuffer, std::string *output); public: ZIDCacheDb(): zidFile(NULL) { getDbCacheOps(&cacheOps); }; ~ZIDCacheDb(); int open(char *name); bool isOpen() { return (zidFile != NULL); }; void close(); ZIDRecord *getRecord(unsigned char *zid); unsigned int saveRecord(ZIDRecord *zidRecord); const unsigned char* getZid() { return associatedZid; }; int32_t getPeerName(const uint8_t *peerZid, std::string *name); void putPeerName(const uint8_t *peerZid, const std::string name); void cleanup(); void *prepareReadAll(); void *readNextRecord(void *stmt, std::string *name); void closeOpenStatment(void *stmt); }; /** * @} */ #endif ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZIDCacheFile.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #ifndef _ZIDCACHEFILE_H_ #define _ZIDCACHEFILE_H_ /** * @file ZIDCacheFile.h * @brief ZID cache management * * A ZID file stores (caches) some data that helps ZRTP to achives its * key continuity feature. See @c ZIDRecord for further info which data * the ZID file contains. * * @ingroup GNU_ZRTP * @{ */ /** * This class implements a ZID (ZRTP Identifiers) file. * * The interface defintion @c ZIDCache.h contains the method documentation. * The ZID cache file holds information about peers. * * @author: Werner Dittmann */ class __EXPORT ZIDCacheFile: public ZIDCache { private: FILE* zidFile; unsigned char associatedZid[IDENTIFIER_LEN]; void createZIDFile(char* name); void checkDoMigration(char* name); public: ZIDCacheFile(): zidFile(NULL) {}; ~ZIDCacheFile(); int open(char *name); bool isOpen() { return (zidFile != NULL); }; void close(); ZIDRecord *getRecord(unsigned char *zid); unsigned int saveRecord(ZIDRecord *zidRecord); const unsigned char* getZid() { return associatedZid; }; int32_t getPeerName(const uint8_t *peerZid, std::string *name); void putPeerName(const uint8_t *peerZid, const std::string name); // Not implemented for file based cache void cleanup() {}; void *prepareReadAll() { return NULL; }; void *readNextRecord(void *stmt, std::string *output) { return NULL; }; void closeOpenStatment(void *stmt) {} }; /** * @} */ #endif ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZIDRecord.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZIDRECORD_H_ #define _ZIDRECORD_H_ #include #include /** * @file ZIDRecord.h * @brief ZID cache record management * * A ZID record stores (caches) ZID (ZRTP ID) specific data that helps ZRTP * to achives its key continuity feature. Please refer to the ZRTP * specification to get detailed information about the ZID. * * @ingroup GNU_ZRTP * @{ */ /** * These length are fixed for ZRTP. See RFC 6189. */ #define IDENTIFIER_LEN 12 #define RS_LENGTH 32 #define FILE_TYPE_RECORD 1 #define SQLITE_TYPE_RECORD 2 #if defined(__cplusplus) /** * Interface for classes that implement a ZID cache record. * * The ZID cache record holds data about a peer. According to ZRTP specification * we use a ZID to identify a peer. ZRTP uses the RS (Retained Secret) data * to construct shared secrets. * * @author: Werner Dittmann */ class __EXPORT ZIDRecord { public: /** * @brief Destructor. * Define a virtual destructor to enable cleanup in derived classes. */ virtual ~ZIDRecord() {}; /** * @brief Set the @c ZID in the record. * * Set the ZID in this record before calling read or save. */ virtual void setZid(const unsigned char *zid) =0; /** * @brief Set @c valid flag in RS1 */ virtual void setRs1Valid() =0; /** * @brief Reset @c valid flag in RS1 */ virtual void resetRs1Valid() =0; /** * @brief Check @c valid flag in RS1 */ virtual bool isRs1Valid() =0; /** * @brief Set @c valid flag in RS2 */ virtual void setRs2Valid() =0; /** * @brief Reset @c valid flag in RS2 */ virtual void resetRs2Valid() =0; /** * @brief Check @c valid flag in RS2 */ virtual bool isRs2Valid() =0; /** * @brief Set MITM key available */ virtual void setMITMKeyAvailable() =0; /** * @brief Reset MITM key available */ virtual void resetMITMKeyAvailable() =0; /** * @brief Check MITM key available is set */ virtual bool isMITMKeyAvailable() =0; /** * @brief Mark this as own ZID record */ virtual void setOwnZIDRecord() =0; /** * @brief Reset own ZID record marker */ virtual void resetOwnZIDRecord() =0; /** * @brief Check own ZID record marker */ virtual bool isOwnZIDRecord() =0; /** * @brief Set SAS for this ZID as verified */ virtual void setSasVerified() =0; /** * @brief Reset SAS for this ZID as verified */ virtual void resetSasVerified() =0; /** * @brief Check if SAS for this ZID was verified */ virtual bool isSasVerified() =0; /** * @brief Return the ZID for this record */ virtual const uint8_t* getIdentifier() =0; /** * @brief Check if RS1 is still valid * * Returns true if RS1 is still valid, false otherwise. * * @return * Returns true is RS1 is not expired (valid), false otherwise. */ virtual bool isRs1NotExpired() =0; /** * @brief Returns pointer to RS1 data. */ virtual const unsigned char* getRs1() =0; /** * @brief Check if RS2 is still valid * * Returns true if RS2 is still valid, false otherwise. * * @return * Returns true is RS2 is not expired (valid), false otherwise. */ virtual bool isRs2NotExpired() =0; /** * @brief Returns pointer to RS1 data. */ virtual const unsigned char* getRs2() =0; /** * @brief Sets new RS1 data and associated expiration value. * * If the expiration value is >0 or -1 the method stores the new * RS1. Before it stores the new RS1 it shifts the exiting RS1 * into RS2 (together with its expiration time). Then it computes * the expiration time of the and stores the result together with * the new RS1. * * If the expiration value is -1 then this RS will never expire. * * If the expiration value is 0 then the expiration value of a * stored RS1 is cleared and no new RS1 value is stored. Also RS2 * is left unchanged. * * @param data * Points to the new RS1 data. * @param expire * The expiration interval in seconds. Default is -1. * */ virtual void setNewRs1(const unsigned char* data, int32_t expire =-1) =0; /** * @brief Set MiTM key data. * */ virtual void setMiTMData(const unsigned char* data) =0; /** * @brief Get MiTM key data. * */ virtual const unsigned char* getMiTMData() =0; virtual int getRecordType() =0; /** * @brief Get the secure since field * * Returns the secure since field or 0 if no such field is available. Secure since * uses the unixepoch. */ virtual int64_t getSecureSince() =0; }; #endif /* (__cplusplus) */ #endif ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZIDRecordDb.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZIDRECORDDB_H_ #define _ZIDRECORDDB_H_ /** * @file ZIDRecordDb.h * @brief ZID cache record management * * A ZID record stores (caches) ZID (ZRTP ID) specific data that helps ZRTP * to achives its key continuity feature. Please refer to the ZRTP * specification to get detailed information about the ZID. * * @ingroup GNU_ZRTP * @{ */ #include #include #include #define TIME_LENGTH 8 // 64 bit, can hold time on 64 bit systems /** * Internal structure that holds the non-key data of a remote ZID record. * * The data storage backends use this structure to get or to fill in data * to store in or that was read from the data store. * * Some notes regarding the timestamps: the structure uses 64 bit variables to * store a timestamp. The relevant SQL SELECT / UPDATE / INSERT statements and * the relevant must take care of this. * * The methods shall use the standard C time() call to get the current time in * seconds since Unix epoch (see time() documentation). */ typedef struct { uint8_t identifier[IDENTIFIER_LEN]; /* < the peer's ZID or own ZID */ uint32_t flags; uint8_t rs1[RS_LENGTH]; int64_t rs1LastUse; int64_t rs1Ttl; uint8_t rs2[RS_LENGTH]; int64_t rs2LastUse; int64_t rs2Ttl; uint8_t mitmKey[RS_LENGTH]; int64_t mitmLastUse; int64_t secureSince; uint32_t preshCounter; } remoteZidRecord_t; /* * The flag field stores the following bitflags */ static const uint32_t Valid = 0x1; static const uint32_t SASVerified = 0x2; static const uint32_t RS1Valid = 0x4; static const uint32_t RS2Valid = 0x8; static const uint32_t MITMKeyAvailable = 0x10; static const uint32_t inUse = 0x20; /** * Internal structure that holds the non-key data of a ZID name record. * * The flags field currently just uses the @c Valid bit. * * See comment on @c remoteZidRecord_t above. */ typedef struct { uint32_t flags; char *name; int32_t nameLength; } zidNameRecord_t; #if defined(__cplusplus) /** * This class implements the ZID record. * * The ZID record holds data about a peer. According to ZRTP specification * we use a ZID to identify a peer. ZRTP uses the RS (Retained Secret) data * to construct shared secrets. *

* NOTE: ZIDRecordDb has ZIDCacheDb as friend. ZIDCacheDb knows about the private * data of ZIDRecord - please keep both classes synchronized. * * @author: Werner Dittmann */ class __EXPORT ZIDRecordDb: public ZIDRecord { friend class ZIDCacheDb; private: remoteZidRecord_t record; remoteZidRecord_t* getRecordData() {return &record; } int getRecordLength() {return sizeof(remoteZidRecord_t); } bool isValid() { return ((record.flags & Valid) == Valid); } void setValid() { record.flags |= Valid; } public: /* * @brief The default constructor, */ ZIDRecordDb() { memset(&record, 0, sizeof(remoteZidRecord_t)); } /** * Set the @c ZID in the record. * * Set the ZID in this record before calling read or save. */ void setZid(const unsigned char *zid) { memcpy(record.identifier, zid, IDENTIFIER_LEN); } /** * Set @c valid flag in RS1 */ void setRs1Valid() { record.flags |= RS1Valid; } /** * reset @c valid flag in RS1 */ void resetRs1Valid() { record.flags &= ~RS1Valid; } /** * Check @c valid flag in RS1 */ bool isRs1Valid() { return ((record.flags & RS1Valid) == RS1Valid); } /** * Set @c valid flag in RS2 */ void setRs2Valid() { record.flags |= RS2Valid; } /** * Reset @c valid flag in RS2 */ void resetRs2Valid() { record.flags &= ~RS2Valid; } /** * Check @c valid flag in RS2 */ bool isRs2Valid() { return ((record.flags & RS2Valid) == RS2Valid); } /** * Set MITM key available */ void setMITMKeyAvailable() { record.flags |= MITMKeyAvailable; } /** * Reset MITM key available */ void resetMITMKeyAvailable() { record.flags &= ~MITMKeyAvailable; } /** * Check MITM key available is set */ bool isMITMKeyAvailable() { return ((record.flags & MITMKeyAvailable) == MITMKeyAvailable); } /** * Mark this as own ZID record - not used in this DB cache backend */ void setOwnZIDRecord() {} /** * Reset own ZID record marker */ void resetOwnZIDRecord(){} /** * Check own ZID record marker */ bool isOwnZIDRecord() { return false; } // in this DB cahe implementation a record is always 'remote' /** * Set SAS for this ZID as verified */ void setSasVerified() { record.flags |= SASVerified; } /** * Reset SAS for this ZID as verified */ void resetSasVerified() { record.flags &= ~SASVerified; } /** * Check if SAS for this ZID was verified */ bool isSasVerified() { return ((record.flags & SASVerified) == SASVerified); } /** * Return the ZID for this record */ const uint8_t* getIdentifier() {return record.identifier; } /** * Check if RS1 is still valid * * Returns true if RS1 is still valid, false otherwise. * * @return * Returns true is RS1 is not expired (valid), false otherwise. */ bool isRs1NotExpired(); /** * Returns pointer to RS1 data. */ const unsigned char* getRs1() { return record.rs1; } /** * Check if RS2 is still valid * * Returns true if RS2 is still valid, false otherwise. * * @return * Returns true is RS2 is not expired (valid), false otherwise. */ bool isRs2NotExpired(); /** * Returns pointer to RS1 data. */ const unsigned char* getRs2() { return record.rs2; } /** * Sets new RS1 data and associated expiration value. * * If the expiration value is >0 or -1 the method stores the new * RS1. Before it stores the new RS1 it shifts the exiting RS1 * into RS2 (together with its expiration time). Then it computes * the expiration time of the and stores the result together with * the new RS1. * * If the expiration value is -1 then this RS will never expire. * * If the expiration value is 0 then the expiration value of a * stored RS1 is cleared and no new RS1 value is stored. Also RS2 * is left unchanged. * * @param data * Points to the new RS1 data. * @param expire * The expiration interval in seconds. Default is -1. * */ void setNewRs1(const unsigned char* data, int32_t expire =-1); /** * Set MiTM key data. * */ void setMiTMData(const unsigned char* data); /** * Get MiTM key data. * */ const unsigned char* getMiTMData() {return record.mitmKey; } int getRecordType() {return SQLITE_TYPE_RECORD; } int64_t getSecureSince() { return record.secureSince; } }; #endif /* (__cplusplus) */ #endif ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZIDRecordFile.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZIDRECORDFILE_H_ #define _ZIDRECORDFILE_H_ /** * @file ZIDRecordFile.h * @brief ZID cache record management * * A ZID record stores (caches) ZID (ZRTP ID) specific data that helps ZRTP * to achives its key continuity feature. Please refer to the ZRTP * specification to get detailed information about the ZID. * * @ingroup GNU_ZRTP * @{ */ #include #include #include #define TIME_LENGTH 8 // 64 bit, can hold time on 64 bit systems /** * This is the recod structure of version 1 ZID records. * * This is not longer in use - only during migration. */ typedef struct zidrecord1 { char recValid; //!< if 1 record is valid, if 0: invalid char ownZid; //!< if >1 record contains own ZID, usually 1st record char rs1Valid; //!< if 1 RS1 contains valid data char rs2Valid; //!< if 1 RS2 contains valid data unsigned char identifier[IDENTIFIER_LEN]; ///< the peer's ZID or own ZID unsigned char rs1Data[RS_LENGTH], rs2Data[RS_LENGTH]; ///< the peer's RS data } zidrecord1_t; /** * This is the recod structure of version 2 ZID records. */ typedef struct zidrecord2 { char version; ///< version number of file format, this is #2 char flags; ///< bit field holding various flags, see below char filler1; ///< round up to next 32 bit char filler2; ///< round up to next 32 bit unsigned char identifier[IDENTIFIER_LEN]; ///< the peer's ZID or own ZID unsigned char rs1Interval[TIME_LENGTH]; ///< expiration time of RS1; -1 means indefinite unsigned char rs1Data[RS_LENGTH]; ///< the peer's RS2 data unsigned char rs2Interval[TIME_LENGTH]; ///< expiration time of RS2; -1 means indefinite unsigned char rs2Data[RS_LENGTH]; ///< the peer's RS2 data unsigned char mitmKey[RS_LENGTH]; ///< MiTM key if available } zidrecord2_t; static const int Valid = 0x1; static const int SASVerified = 0x2; static const int RS1Valid = 0x4; static const int RS2Valid = 0x8; static const int MITMKeyAvailable = 0x10; static const int OwnZIDRecord = 0x20; /** * This class implements the ZID record. * * The ZID record holds data about a peer. According to ZRTP specification * we use a ZID to identify a peer. ZRTP uses the RS (Retained Secret) data * to construct shared secrets. *

* NOTE: ZIDRecord has ZIDFile as friend. ZIDFile knows about the private * data of ZIDRecord - please keep both classes synchronized. * * @author: Werner Dittmann */ class __EXPORT ZIDRecordFile: public ZIDRecord { friend class ZIDCacheFile; private: zidrecord2_t record; unsigned long position; /** * Functions for I/O availabe for ZID file handling * * These functions are private, thus only friends may use it. */ void setPosition(long pos) {position = pos;} long getPosition() {return position; } zidrecord2_t* getRecordData() {return &record; } int getRecordLength() {return sizeof(zidrecord2_t); } bool isValid() { return ((record.flags & Valid) == Valid); } void setValid() { record.flags |= Valid; } public: /* * @brief The default constructor, */ ZIDRecordFile() { memset(&record, 0, sizeof(zidrecord2_t)); record.version = 2; } /** * @brief Set the @c ZID in the record. * * Set the ZID in this record before calling read or save. */ void setZid(const unsigned char *zid) { memcpy(record.identifier, zid, IDENTIFIER_LEN); } /** * @brief Set @c valid flag in RS1 */ void setRs1Valid() { record.flags |= RS1Valid; } /** * @brief Reset @c valid flag in RS1 */ void resetRs1Valid() { record.flags &= ~RS1Valid; } /** * @brief Check @c valid flag in RS1 */ bool isRs1Valid() { return ((record.flags & RS1Valid) == RS1Valid); } /** * @brief Set @c valid flag in RS2 */ void setRs2Valid() { record.flags |= RS2Valid; } /** * @brief Reset @c valid flag in RS2 */ void resetRs2Valid() { record.flags &= ~RS2Valid; } /** * @brief Check @c valid flag in RS2 */ bool isRs2Valid() { return ((record.flags & RS2Valid) == RS2Valid); } /** * @brief Set MITM key available */ void setMITMKeyAvailable() { record.flags |= MITMKeyAvailable; } /** * @brief Reset MITM key available */ void resetMITMKeyAvailable() { record.flags &= ~MITMKeyAvailable; } /** * @brief Check MITM key available is set */ bool isMITMKeyAvailable() { return ((record.flags & MITMKeyAvailable) == MITMKeyAvailable); } /** * @brief Mark this as own ZID record */ void setOwnZIDRecord() { record.flags = OwnZIDRecord; } /** * @brief Reset own ZID record marker */ void resetOwnZIDRecord(){ record.flags = 0; } /** * @brief Check own ZID record marker */ bool isOwnZIDRecord() { return (record.flags == OwnZIDRecord); } // no other flag allowed if own ZID /** * @brief Set SAS for this ZID as verified */ void setSasVerified() { record.flags |= SASVerified; } /** * @brief Reset SAS for this ZID as verified */ void resetSasVerified() { record.flags &= ~SASVerified; } /** * @brief Check if SAS for this ZID was verified */ bool isSasVerified() { return ((record.flags & SASVerified) == SASVerified); } /** * @brief Return the ZID for this record */ const uint8_t* getIdentifier() {return record.identifier; } /** * @brief Check if RS1 is still valid * * Returns true if RS1 is still valid, false otherwise. * * @return * Returns true is RS1 is not expired (valid), false otherwise. */ bool isRs1NotExpired(); /** * @brief Returns pointer to RS1 data. */ const unsigned char* getRs1() { return record.rs1Data; } /** * @brief Check if RS2 is still valid * * Returns true if RS2 is still valid, false otherwise. * * @return * Returns true is RS2 is not expired (valid), false otherwise. */ bool isRs2NotExpired(); /** * @brief Returns pointer to RS1 data. */ const unsigned char* getRs2() { return record.rs2Data; } /** * @brief Sets new RS1 data and associated expiration value. * * If the expiration value is >0 or -1 the method stores the new * RS1. Before it stores the new RS1 it shifts the exiting RS1 * into RS2 (together with its expiration time). Then it computes * the expiration time of the and stores the result together with * the new RS1. * * If the expiration value is -1 then this RS will never expire. * * If the expiration value is 0 then the expiration value of a * stored RS1 is cleared and no new RS1 value is stored. Also RS2 * is left unchanged. * * @param data * Points to the new RS1 data. * @param expire * The expiration interval in seconds. Default is -1. * */ void setNewRs1(const unsigned char* data, int32_t expire =-1); /** * @brief Set MiTM key data. * */ void setMiTMData(const unsigned char* data); /** * @brief Get MiTM key data. * */ const unsigned char* getMiTMData() {return record.mitmKey; } int getRecordType() {return FILE_TYPE_RECORD; } /** * @brief Get Secure since date. * * The file based cache implementation does not support this datum, thus return 0 * */ int64_t getSecureSince() { return 0; } }; #endif // ZIDRECORDSMALL ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZRtp.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTP_H_ #define _ZRTP_H_ /** * @file ZRtp.h * @brief The ZRTP main engine * @defgroup GNU_ZRTP The GNU ZRTP C++ implementation * @{ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ZRTP_OPENSSL #include #include #else #include #endif #ifndef SHA256_DIGEST_LENGTH #define SHA256_DIGEST_LENGTH 32 #endif // Prepare to support digest algorithms up to 512 bit (64 bytes) #define MAX_DIGEST_LENGTH 64 #define IMPL_MAX_DIGEST_LENGTH 64 // max. number of parallel supported ZRTP protocol versions. #define MAX_ZRTP_VERSIONS 2 // currently only 1.10 supported #define SUPPORTED_ZRTP_VERSIONS 1 // Integer representation of highest supported ZRTP protocol version #define HIGHEST_ZRTP_VERION 12 class __EXPORT ZrtpStateClass; class ZrtpDH; class ZRtp; /** * The main ZRTP class. * * This is the main class of the RTP/SRTP independent part of the GNU * ZRTP. It handles the ZRTP HMAC, DH, and other data management. The * user of this class needs to know only a few methods and needs to * provide only a few external functions to connect to a Timer * mechanism and to send data via RTP and SRTP. Refer to the * ZrtpCallback class to get detailed information regading the * callback methods required by GNU RTP. * * The class ZrtpQueue is the GNU ccRTP specific implementation that * extends standard ccRTP RTP provide ZRTP support. Refer to the * documentation of ZrtpQueue to get more information about the usage * of ZRtp and associated classes. * * The main entry into the ZRTP class is the processExtensionHeader() * method. * * This class does not directly handle the protocol states, timers, * and packet resend. The protocol state engine is responsible for * these actions. * * Example how to use ZRtp: *

 *    zrtpEngine = new ZRtp((uint8_t*)ownZid, (ZrtpCallback*)this, idString);
 *    zrtpEngine->startZrtpEngine();
 *
* @see ZrtpCallback * * @author Werner Dittmann */ class __EXPORT ZRtp { public: typedef enum _secrets { Rs1 = 1, Rs2 = 2, Pbx = 4, Aux = 8 } secrets; typedef struct _zrtpInfo { int32_t secretsCached; int32_t secretsMatched; int32_t secretsMatchedDH; const char *hash; const char *cipher; const char *pubKey; const char *sasType; const char *authLength; } zrtpInfo; /** * Faster access to Hello packets with different versions. */ typedef struct _HelloPacketVersion { int32_t version; ZrtpPacketHello* packet; uint8_t helloHash[IMPL_MAX_DIGEST_LENGTH]; } HelloPacketVersion; /** * Constructor intializes all relevant data but does not start the * engine. */ ZRtp(uint8_t* myZid, ZrtpCallback* cb, std::string id, ZrtpConfigure* config, bool mitmm= false, bool sasSignSupport= false); /** * Destructor cleans up. */ ~ZRtp(); /** * Kick off the ZRTP protocol engine. * * This method calls the ZrtpStateClass#evInitial() state of the state * engine. After this call we are able to process ZRTP packets * from our peer and to process them. */ void startZrtpEngine(); /** * Stop ZRTP security. * */ void stopZrtp(); /** * Process ZRTP message. * * The method takes the data and forwards it to the ZRTP state engine for further * processing. It's the caller's duty to check the ZRTP CRC and the ZRTP magic * cookie before calling this function. * * @param extHeader * A pointer to the first byte of the ZRTP message. Refer to RFC6189. * @param peerSSRC * The peer's SSRC. * @param length * of the received data packet, this includes the RTP like header * and the ZRTP CRC field - used to do santity checks. * * @return * Code indicating further packet handling, see description above. */ void processZrtpMessage(uint8_t *extHeader, uint32_t peerSSRC, size_t length); /** * Process a timeout event. * * We got a timeout from the timeout provider. Forward it to the * protocol state engine. * */ void processTimeout(); /** * Check for and handle GoClear ZRTP packet header. * * This method checks if this is a GoClear packet. If not, just return * false. Otherwise handle it according to the specification. * * @param extHeader * A pointer to the first byte of the extension header. Refer to * RFC3550. * @return * False if not a GoClear, true otherwise. */ bool handleGoClear(uint8_t *extHeader); /** * Set the auxilliary secret. * * Use this method to set the auxilliary secret data. Refer to ZRTP * specification, chapter 4.3 ff * * @param data * Points to the secret data. * @param length * Length of the auxilliary secrect in bytes */ void setAuxSecret(uint8_t* data, int32_t length); /** * Check current state of the ZRTP state engine * * @param state * The state to check. * @return * Returns true id ZRTP engine is in the given state, false otherwise. */ bool inState(int32_t state); /** * Set SAS as verified. * * Call this method if the user confirmed (verfied) the SAS. ZRTP * remembers this together with the retained secrets data. */ void SASVerified(); /** * Reset the SAS verfied flag for the current active user's retained secrets. * */ void resetSASVerified(); /** * Get the ZRTP Hello Hash data. * * Use this method to get the ZRTP Hello hash data. The method * returns the data as a string containing the ZRTP protocol version and * hex-digits. * * The index defines which Hello packet to use. Each supported ZRTP procol version * uses a different Hello packet and thus computes different hashes. * * Refer to ZRTP specification, chapter 8. * * @param index * Hello hash of the Hello packet identfied by index. Index must be 0 <= index < MAX_ZRTP_VERSIONS. * * @return * a std::string formatted according to RFC6189 section 8 without the leading 'a=zrtp-hash:' * SDP attribute identifier. The hello hash is available immediately after class instantiation. * * @see getNumberSupportedVersions() */ std::string getHelloHash(int index); /** * Get the peer's ZRTP Hello Hash data. * * Use this method to get the peer's ZRTP Hello Hash data. The method * returns the data as a string containing the ZRTP protocol version and * hex-digits. * * The peer's hello hash is available only after ZRTP received a hello. If * no data is available the function returns an empty string. * * Refer to ZRTP specification, chapter 8. * * @return * a std:string containing the Hello version and the hello hash as hex digits. */ std::string getPeerHelloHash(); /** * Get Multi-stream parameters. * * Deprecated - use getMultiStrParams(ZRtp **zrtpMaster); * * Use this method to get the Multi-stream that were computed during * the ZRTP handshake. An application may use these parameters to * enable multi-stream processing for an associated SRTP session. * * Refer to chapter 4.4.2 in the ZRTP specification for further details * and restriction how and when to use multi-stream mode. * * @return * a string that contains the multi-stream parameters. The application * must not modify the contents of this string, it is opaque data. The * application may hand over this string to a new ZrtpQueue instance * to enable multi-stream processing for this ZrtpQueue. * If ZRTP was not started or ZRTP is not yet in secure state the method * returns an empty string. */ DEPRECATED std::string getMultiStrParams() {return getMultiStrParams(NULL); } /** * Set Multi-stream parameters. * * Deprecated - use setMultiStrParams(std::string parameters, ZRtp* zrtpMaster); * * Use this method to set the parameters required to enable Multi-stream * processing of ZRTP. The multi-stream parameters must be set before the * application starts the ZRTP protocol engine. * * Refer to chapter 4.4.2 in the ZRTP specification for further details * of multi-stream mode. * * @param parameters * A string that contains the multi-stream parameters that this * new ZrtpQueue instanace shall use. See also * getMultiStrParams() */ DEPRECATED void setMultiStrParams(std::string parameters) { setMultiStrParams(parameters, NULL);} /** * Get Multi-stream parameters. * * Use this method to get the Multi-stream that were computed during * the ZRTP handshake. An application may use these parameters to * enable multi-stream processing for an associated SRTP session. * * Refer to chapter 4.4.2 in the ZRTP specification for further details * and restriction how and when to use multi-stream mode. * * @param zrtpMaster * Where the function returns the pointer of the ZRTP master stream. * @return * a string that contains the multi-stream parameters. The application * must not modify the contents of this string, it is opaque data. The * application may hand over this string to a new ZrtpQueue instance * to enable multi-stream processing for this ZrtpQueue. * If ZRTP was not started or ZRTP is not yet in secure state the method * returns an empty string. */ std::string getMultiStrParams(ZRtp **zrtpMaster); /** * Set Multi-stream parameters. * * Use this method to set the parameters required to enable Multi-stream * processing of ZRTP. The multi-stream parameters must be set before the * application starts the ZRTP protocol engine. * * Refer to chapter 4.4.2 in the ZRTP specification for further details * of multi-stream mode. * * @param parameters * A string that contains the multi-stream parameters that this * new ZrtpQueue instanace shall use. See also * getMultiStrParams(ZRtp **zrtpMaster) * @param zrtpMaster * The pointer of the ZRTP master stream. */ void setMultiStrParams(std::string parameters, ZRtp* zrtpMaster); /** * Check if this ZRTP session is a Multi-stream session. * * Use this method to check if this ZRTP instance uses multi-stream. * Refer to chapters 4.2 and 4.4.2 in the ZRTP. * * @return * True if multi-stream is used, false otherwise. */ bool isMultiStream(); /** * Check if the other ZRTP client supports Multi-stream. * * Use this method to check if the other ZRTP client supports * Multi-stream mode. * * @return * True if multi-stream is available, false otherwise. */ bool isMultiStreamAvailable(); /** * Accept a PBX enrollment request. * * If a PBX service asks to enroll the PBX trusted MitM key and the user * accepts this request, for example by pressing an OK button, the client * application shall call this method and set the parameter * accepted to true. If the user does not accept the request * set the parameter to false. * * @param accepted * True if the enrollment request is accepted, false otherwise. */ void acceptEnrollment(bool accepted); /** * Check the state of the enrollment mode. * * If true then we will set the enrollment flag (E) in the confirm * packets and perform the enrollment actions. A MitM (PBX) enrollment service * started this ZRTP session. Can be set to true only if mitmMode is also true. * * @return status of the enrollmentMode flag. */ bool isEnrollmentMode(); /** * Set the state of the enrollment mode. * * If true then we will set the enrollment flag (E) in the confirm * packets and perform the enrollment actions. A MitM (PBX) enrollment * service must sets this mode to true. * * Can be set to true only if mitmMode is also true. * * @param enrollmentMode defines the new state of the enrollmentMode flag */ void setEnrollmentMode(bool enrollmentMode); /** * Check if a peer's cache entry has a vaild MitM key. * * If true then the other peer ha a valid MtiM key, i.e. the peer has performed * the enrollment procedure. A PBX ZRTP Back-2-Back application can use this function * to check which of the peers is enrolled. * * @return True if the other peer has a valid Mitm key (is enrolled). */ bool isPeerEnrolled(); /** * Send the SAS relay packet. * * The method creates and sends a SAS relay packet according to the ZRTP * specifications. Usually only a MitM capable user agent (PBX) uses this * function. * * @param sh the full SAS hash value, 32 bytes * @param render the SAS rendering algorithm */ bool sendSASRelayPacket(uint8_t* sh, std::string render); /** * Get the commited SAS rendering algorithm for this ZRTP session. * * @return the commited SAS rendering algorithm */ std::string getSasType(); /** * Get the computed SAS hash for this ZRTP session. * * A PBX ZRTP back-to-Back function uses this function to get the SAS * hash of an enrolled client to construct the SAS relay packet for * the other client. * * @return a pointer to the byte array that contains the full * SAS hash. */ uint8_t* getSasHash(); /** * Set signature data. * * This functions stores signature data and transmitts it during ZRTP * processing to the other party as part of the Confirm packets. Refer to * chapters 5.7 and 7.2. * * The signature data must be set before ZRTP the application calls * start(). * * @param data * The signature data including the signature type block. The method * copies this data into the Confirm packet at signature type block. * @param length * The length of the signature data in bytes. This length must be * multiple of 4. * @return * True if the method stored the data, false otherwise. */ bool setSignatureData(uint8_t* data, int32_t length); /** * Get signature data. * * This functions returns a pointer to the signature data that was receivied * during ZRTP processing. Refer to chapters 5.7 and 7.2. * * The returned pointer points to volatile data that is valid only during the * checkSASSignature() callback funtion. The application must copy * the signature data if it will be used after the callback function returns. * * The signature data can be retrieved after ZRTP enters secure state. * start(). * * @return * Pointer to signature data. */ const uint8_t* getSignatureData(); /** * Get length of signature data in number of bytes. * * This functions returns the length of signature data that was receivied * during ZRTP processing. Refer to chapters 5.7 and 7.2. * * @return * Length in bytes of the received signature data. The method returns * zero if no signature data is avilable. */ int32_t getSignatureLength(); /** * Emulate a Conf2Ack packet. * * This method emulates a Conf2Ack packet. According to ZRTP specification * the first valid SRTP packet that the Initiator receives must switch * on secure mode. Refer to chapter 4 in the specificaton * */ void conf2AckSecure(); /** * Get other party's ZID (ZRTP Identifier) data * * This functions returns the other party's ZID that was receivied * during ZRTP processing. * * The ZID data can be retrieved after ZRTP receive the first Hello * packet from the other party. The application may call this method * for example during SAS processing in showSAS(...) user callback * method. * * @param data * Pointer to a data buffer. This buffer must have a size of * at least 12 bytes (96 bit) (ZRTP Identifier, see chap. 4.9) * @return * Number of bytes copied into the data buffer - must be equivalent * to 96 bit, usually 12 bytes. */ int32_t getPeerZid(uint8_t* data); /** * Returns a pointer to the gather detailed information structure. * * This structure contains some detailed information about the negotiated * algorithms, the chached and matched shared secrets. */ const zrtpInfo *getDetailInfo(); /** * Get peer's client id. * * @return the peer's client id or an empty @c string if not set. */ std::string getPeerClientId(); /** * Get peer's protocl version string. * * @return the peer's protocol version or an empty @c string if not set. */ std::string getPeerProtcolVersion(); /** * Get number of supported ZRTP protocol versions. * * @return the number of supported ZRTP protocol versions. */ int32_t getNumberSupportedVersions() {return SUPPORTED_ZRTP_VERSIONS;} /** * Get negotiated ZRTP protocol version. * * @return the integer representation of the negotiated ZRTP protocol version. */ int32_t getCurrentProtocolVersion() {return currentHelloPacket->getVersionInt();} /** * Validate the RS2 data if necessary. * * The cache functions stores the RS2 data but does not set its valid flag. The * application may decide to set this flag. */ void setRs2Valid(); /** * Get the secure since field * * Returns the secure since field or 0 if no such field is available. Secure since * uses the unixepoch. */ int64_t getSecureSince(); /** * Set the resend counter of timer T1 - T1 controls the Hello packets. * * This overwrites the standard value of 20 retries. Setting to <0 means * 'indefinite', counter values less then 10 are ignored. * * Applications may set the resend counter based on network or some other * conditions. Applications may set this value any time and it's in effect * for the current call. Setting the counter after the hello phase has no * effect. */ void setT1Resend(int32_t counter); /** * Set the extended resend counter of timer T1 - T1 controls the Hello packets. * * More retries to extend time, see RFC6189 chap. 6. This overwrites the standard * value of 60 extended retiries. * * Applications may set the resend counter based on network or some other * conditions. */ void setT1ResendExtend(int32_t counter); /** * Set the time capping of timer T1 - T1 controls the Hello packets. * * Values <50ms are not set. */ void setT1Capping(int32_t capping); /** * Set the resend counter of timer T2 - T2 controls other (post-Hello) packets. * * This overwrites the standard value of 10 retiries. Setting to <0 means * 'indefinite', counter values less then 10 are ignored. * * Applications may set the resend counter based on network or some other * conditions. Applications may set this value any time and it's in effect * for the current call. Setting the counter after tZRTP enetered secure state * has no effect. */ void setT2Resend(int32_t counter); /** * Set the time capping of timer T2 - T2 controls other (post-Hello) packets. * * Values <150ms are not set. */ void setT2Capping(int32_t capping); /** * @brief Get required buffer size to get all 32-bit statistic counters of ZRTP * * @param streamNm stream, if not specified the default is @c AudioStream * * @return number of 32 bit integer elements required or < 0 on error */ int getNumberOfCountersZrtp(); /** * @brief Read statistic counters of ZRTP * * @param buffer Pointer to buffer of 32-bit integers. The buffer must be able to * hold at least getNumberOfCountersZrtp() 32-bit integers * @param streamNm stream, if not specified the default is @c AudioStream * * @return number of 32-bit counters returned in buffer or < 0 on error */ int getCountersZrtp(int32_t* counters); private: typedef union _hashCtx { SkeinCtx_t skeinCtx; #ifdef ZRTP_OPENSSL SHA256_CTX sha256Ctx; SHA512_CTX sha384Ctx; #else sha256_ctx sha256Ctx; sha384_ctx sha384Ctx; #endif } HashCtx; friend class ZrtpStateClass; /** * The state engine takes care of protocol processing. */ ZrtpStateClass* stateEngine; /** * This is my ZID that I send to the peer. */ uint8_t ownZid[IDENTIFIER_LEN]; /** * The peer's ZID */ uint8_t peerZid[IDENTIFIER_LEN]; /** * The callback class provides me with the interface to send * data and to deal with timer management of the hosting system. */ ZrtpCallback* callback; /** * My active Diffie-Helman context */ ZrtpDH* dhContext; /** * The computed DH shared secret */ uint8_t* DHss; /** * My computed public key */ uint8_t pubKeyBytes[400]; /** * Length off public key */ // int32_t pubKeyLen; /** * My Role in the game */ Role myRole; /** * The human readable SAS value */ std::string SAS; /** * The SAS hash for signaling and alike. Refer to chapters * 4.5 and 7 how sasHash, sasValue and the SAS string are derived. */ uint8_t sasHash[MAX_DIGEST_LENGTH]; /** * The ids for the retained and other shared secrets */ uint8_t rs1IDr[MAX_DIGEST_LENGTH]; uint8_t rs2IDr[MAX_DIGEST_LENGTH]; uint8_t auxSecretIDr[MAX_DIGEST_LENGTH]; uint8_t pbxSecretIDr[MAX_DIGEST_LENGTH]; uint8_t rs1IDi[MAX_DIGEST_LENGTH]; uint8_t rs2IDi[MAX_DIGEST_LENGTH]; uint8_t auxSecretIDi[MAX_DIGEST_LENGTH]; uint8_t pbxSecretIDi[MAX_DIGEST_LENGTH]; /** * pointers to aux secret storage and length of aux secret */ uint8_t* auxSecret; int32_t auxSecretLength; /** * Record if valid rs1 and/or rs1 were found in the * retaind secret cache. */ bool rs1Valid; bool rs2Valid; /** * My hvi */ uint8_t hvi[MAX_DIGEST_LENGTH]; /** * The peer's hvi */ uint8_t peerHvi[8*ZRTP_WORD_SIZE]; /** * Context to compute the SHA256 hash of selected messages. * Used to compute the s0, refer to chapter 4.4.1.4 */ void* msgShaContext; /** * Commited Hash, Cipher, and public key algorithms */ AlgorithmEnum* hash; AlgorithmEnum* cipher; AlgorithmEnum* pubKey; /** * The selected SAS type. */ AlgorithmEnum* sasType; /** * The selected SAS type. */ AlgorithmEnum* authLength; /** * The Hash images as defined in chapter 5.1.1 (H0 is a random value, * not stored here). Need full SHA 256 lenght to store hash value but * only the leftmost 128 bits are used in computations and comparisons. */ uint8_t H0[IMPL_MAX_DIGEST_LENGTH]; uint8_t H1[IMPL_MAX_DIGEST_LENGTH]; uint8_t H2[IMPL_MAX_DIGEST_LENGTH]; uint8_t H3[IMPL_MAX_DIGEST_LENGTH]; uint8_t peerHelloHash[IMPL_MAX_DIGEST_LENGTH]; uint8_t peerHelloVersion[ZRTP_WORD_SIZE + 1]; // +1 for nul byte // We get the peer's H? from the message where length is defined as 8 words uint8_t peerH0[8*ZRTP_WORD_SIZE]; uint8_t peerH1[8*ZRTP_WORD_SIZE]; uint8_t peerH2[8*ZRTP_WORD_SIZE]; uint8_t peerH3[8*ZRTP_WORD_SIZE]; /** * The SHA256 hash over selected messages */ uint8_t messageHash[MAX_DIGEST_LENGTH]; /** * The s0 */ uint8_t s0[MAX_DIGEST_LENGTH]; /** * The new Retained Secret */ uint8_t newRs1[MAX_DIGEST_LENGTH]; /** * The GoClear HMAC keys and confirm HMAC key */ uint8_t hmacKeyI[MAX_DIGEST_LENGTH]; uint8_t hmacKeyR[MAX_DIGEST_LENGTH]; /** * The Initiator's srtp key and salt */ uint8_t srtpKeyI[MAX_DIGEST_LENGTH]; uint8_t srtpSaltI[MAX_DIGEST_LENGTH]; /** * The Responder's srtp key and salt */ uint8_t srtpKeyR[MAX_DIGEST_LENGTH]; uint8_t srtpSaltR[MAX_DIGEST_LENGTH]; /** * The keys used to encrypt/decrypt the confirm message */ uint8_t zrtpKeyI[MAX_DIGEST_LENGTH]; uint8_t zrtpKeyR[MAX_DIGEST_LENGTH]; HashCtx hashCtx; /** * Pointers to negotiated hash and HMAC functions */ void (*hashFunction)(unsigned char *data, unsigned int data_length, unsigned char *digest); void (*hashListFunction)(unsigned char *data[], unsigned int data_length[], unsigned char *digest); void (*hmacFunction)(uint8_t* key, uint32_t key_length, uint8_t* data, int32_t data_length, uint8_t* mac, uint32_t* mac_length); void (*hmacListFunction)( uint8_t* key, uint32_t key_length, uint8_t* data[], uint32_t data_length[], uint8_t* mac, uint32_t* mac_length ); void* (*createHashCtx)(void* ctx); void (*closeHashCtx)(void* ctx, unsigned char* digest); void (*hashCtxFunction)(void* ctx, unsigned char* data, unsigned int dataLength); void (*hashCtxListFunction)(void* ctx, unsigned char* dataChunks[], unsigned int dataChunkLength[]); int32_t hashLength; // Funtion pointers to implicit hash and hmac functions void (*hashFunctionImpl)(unsigned char *data, unsigned int data_length, unsigned char *digest); void (*hashListFunctionImpl)(unsigned char *data[], unsigned int data_length[], unsigned char *digest); void (*hmacFunctionImpl)(uint8_t* key, uint32_t key_length, uint8_t* data, int32_t data_length, uint8_t* mac, uint32_t* mac_length); void (*hmacListFunctionImpl)( uint8_t* key, uint32_t key_length, uint8_t* data[], uint32_t data_length[], uint8_t* mac, uint32_t* mac_length ); int32_t hashLengthImpl; /** * The ZRTP Session Key * Refer to chapter 5.4.1.4 */ uint8_t zrtpSession[MAX_DIGEST_LENGTH]; /** * True if this ZRTP instance uses multi-stream mode. */ bool multiStream; /** * True if the other ZRTP client supports multi-stream mode. */ bool multiStreamAvailable; /** * Enable MitM (PBX) enrollment * * If set to true then ZRTP honors the PBX enrollment flag in * Commit packets and calls the appropriate user callback * methods. If the parameter is set to false ZRTP ignores the PBX * enrollment flags. */ bool enableMitmEnrollment; /** * True if a valid trusted MitM key of the other peer is available, i.e. enrolled. */ bool peerIsEnrolled; /** * Set to true if the Hello packet contained the M-flag (MitM flag). * We use this later to check some stuff for SAS Relay processing */ bool mitmSeen; /** * Temporarily store computed pbxSecret, if user accepts enrollment then * it will copied to our ZID record of the PBX (MitM) */ uint8_t* pbxSecretTmp; uint8_t pbxSecretTmpBuffer[MAX_DIGEST_LENGTH]; /** * If true then we will set the enrollment flag (E) in the confirm * packets. Set to true if the PBX enrollment service started this ZRTP * session. Can be set to true only if mitmMode is also true. */ bool enrollmentMode; /** * Configuration data which algorithms to use. */ ZrtpConfigure configureAlgos; /** * Pre-initialized packets. */ ZrtpPacketHello zrtpHello_11; ZrtpPacketHello zrtpHello_12; // Prepare for ZRTP protocol version 1.2 ZrtpPacketHelloAck zrtpHelloAck; ZrtpPacketConf2Ack zrtpConf2Ack; ZrtpPacketClearAck zrtpClearAck; ZrtpPacketGoClear zrtpGoClear; ZrtpPacketError zrtpError; ZrtpPacketErrorAck zrtpErrorAck; ZrtpPacketDHPart zrtpDH1; ZrtpPacketDHPart zrtpDH2; ZrtpPacketCommit zrtpCommit; ZrtpPacketConfirm zrtpConfirm1; ZrtpPacketConfirm zrtpConfirm2; ZrtpPacketPingAck zrtpPingAck; ZrtpPacketSASrelay zrtpSasRelay; ZrtpPacketRelayAck zrtpRelayAck; HelloPacketVersion helloPackets[MAX_ZRTP_VERSIONS + 1]; int32_t highestZrtpVersion; /// Pointer to Hello packet sent to partner, initialized in ZRtp, modified by ZrtpStateClass ZrtpPacketHello* currentHelloPacket; /** * ZID cache record */ ZIDRecord *zidRec; /** * Save record * * If false don't save record until user vrified and confirmed the SAS. */ bool saveZidRecord; /** * Random IV data to encrypt the confirm data, 128 bit for AES */ uint8_t randomIV[16]; uint8_t tempMsgBuffer[1024]; int32_t lengthOfMsgData; /** * Variables to store signature data. Includes the signature type block */ const uint8_t* signatureData; // will be set when needed int32_t signatureLength; // overall length in bytes /** * Is true if the other peer signaled SAS signature support in its Hello packet. */ bool signSasSeen; uint32_t peerSSRC; // peer's SSRC, required to setup PingAck packet zrtpInfo detailInfo; // filled with some more detailded information if application would like to know std::string peerClientId; // store the peer's client Id ZRtp* masterStream; // This is the master stream in case this is a multi-stream std::vector peerNonces; // Store nonces we got from our partner. Using std::string // just simplifies memnory management, nonces are binary data, not strings :-) /** * Enable or disable paranoid mode. * * The Paranoid mode controls the behaviour and handling of the SAS verify flag. If * Panaoid mode is set to flase then ZRtp applies the normal handling. If Paranoid * mode is set to true then the handling is: * *
    *
  • Force the SAS verify flag to be false at srtpSecretsOn() callback. This gives * the user interface (UI) the indication to handle the SAS as not verified. * See implementation note below.
  • *
  • Don't set the SAS verify flag in the Confirm packets, thus the other * also must report the SAS as not verified.
  • *
  • ignore the SASVerified() function, thus do not set the SAS to verified * in the ZRTP cache.
  • *
  • Disable the Trusted PBX MitM feature. Just send the SASRelay packet * but do not process the relayed data. This protects the user from a malicious * "trusted PBX".
  • *
* ZRtp performs alls other steps during the ZRTP negotiations as usual, in particular it * computes, compares, uses, and stores the retained secrets. This avoids unnecessary warning * messages. The user may enable or disable the Paranoid mode on a call-by-call basis without * breaking the key continuity data. * * Implementation note:
* An application shall always display the SAS code if the SAS verify flag is false. * The application shall also use mechanisms to remind the user to compare the SAS code, for * example useing larger fonts, different colours and other display features. */ bool paranoidMode; /** * Find the best Hash algorithm that is offered in Hello. * * Find the best, that is the strongest, Hash algorithm that our peer * offers in its Hello packet. * * @param hello * The Hello packet. * @return * The Enum that identifies the best offered Hash algortihm. Return * mandatory algorithm if no match was found. */ AlgorithmEnum* findBestHash(ZrtpPacketHello *hello); /** * Find the best symmetric cipher algorithm that is offered in Hello. * * Find the best, that is the strongest, cipher algorithm that our peer * offers in its Hello packet. * * @param hello * The Hello packet. * @param pk * The id of the selected public key algorithm * @return * The Enum that identifies the best offered Cipher algorithm. Return * mandatory algorithm if no match was found. */ AlgorithmEnum* findBestCipher(ZrtpPacketHello *hello, AlgorithmEnum* pk); /** * Find the best Public Key algorithm that is offered in Hello. * * Find the best, that is the strongest, public key algorithm that our peer * offers in its Hello packet. * * @param hello * The Hello packet. * @return * The Enum that identifies the best offered Public Key algorithm. Return * mandatory algorithm if no match was found. */ AlgorithmEnum* findBestPubkey(ZrtpPacketHello *hello); /** * Find the best SAS algorithm that is offered in Hello. * * Find the best, that is the strongest, SAS algorithm that our peer * offers in its Hello packet. The method works as definied in RFC 6189, * chapter 4.1.2. * * The list of own supported public key algorithms must follow the rules * defined in RFC 6189, chapter 4.1.2, thus the order in the list must go * from fastest to slowest. * * @param hello * The Hello packet. * @return * The Enum that identifies the best offered SAS algorithm. Return * mandatory algorithm if no match was found. */ AlgorithmEnum* findBestSASType(ZrtpPacketHello* hello); /** * Find the best authentication length that is offered in Hello. * * Find the best, that is the strongest, authentication length that our peer * offers in its Hello packet. * * @param hello * The Hello packet. * @return * The Enum that identifies the best offered authentication length. Return * mandatory algorithm if no match was found. */ AlgorithmEnum* findBestAuthLen(ZrtpPacketHello* hello); /** * Check if MultiStream mode is offered in Hello. * * Find the best, that is the strongest, authentication length that our peer * offers in its Hello packet. * * @param hello * The Hello packet. * @return * True if multi stream mode is available, false otherwise. */ bool checkMultiStream(ZrtpPacketHello* hello); /** * Checks if Hello packet contains a strong (384bit) hash based on selection policy. * * The function currently implements the nonNist policy only: * If the public key algorithm is a non-NIST ECC algorithm this function prefers * non-NIST HASH algorithms (Skein etc). * * If Hello packet does not contain a strong hash then this functions returns @c NULL. * * @param hello The Hello packet. * @param algoName name of selected PK algorithm * @return @c hash algorithm if found in Hello packet, @c NULL otherwise. */ AlgorithmEnum* getStrongHashOffered(ZrtpPacketHello *hello, int32_t algoName); /** * Checks if Hello packet offers a strong (256bit) symmetric cipher based on selection policy. * * The function currently implements the nonNist policy only: * If the public key algorithm is a non-NIST ECC algorithm this function prefers * non-NIST symmetric cipher algorithms (Twofish etc). * * If Hello packet does not contain a symmetric cipher then this functions returns @c NULL. * @param hello The Hello packet. * @param algoName name of selected PK algorithm * @return @c hash algorithm if found in Hello packet, @c NULL otherwise. * * @return @c cipher algorithm if found in Hello packet, @c NULL otherwise. */ AlgorithmEnum* getStrongCipherOffered(ZrtpPacketHello *hello, int32_t algoName); /** * Checks if Hello packet contains a hash based on selection policy. * * The function currently implements the nonNist policy only: * If the public key algorithm is a non-NIST ECC algorithm this function prefers * non-NIST HASH algorithms (Skein etc). * * @param hello The Hello packet. * @param algoName name of selected PK algorithm * @return @c hash algorithm found in Hello packet. */ AlgorithmEnum* getHashOffered(ZrtpPacketHello *hello, int32_t algoName); /** * Checks if Hello packet offers a symmetric cipher based on selection policy. * * The function currently implements the nonNist policy only: * If the public key algorithm is a non-NIST ECC algorithm this function prefers * non-NIST symmetric cipher algorithms (Twofish etc). * * @param hello The Hello packet. * @param algoName name of selected PK algorithm * @return non-NIST @c cipher algorithm if found in Hello packet, @c NULL otherwise */ AlgorithmEnum* getCipherOffered(ZrtpPacketHello *hello, int32_t algoName); /** * Checks if Hello packet offers a SRTP authentication length based on selection policy. * * The function currently implements the nonNist policy only: * If the public key algorithm is a non-NIST ECC algorithm this function prefers * non-NIST algorithms (Skein etc). * * @param hello The Hello packet. * @param algoName algoName name of selected PK algorithm * @return @c authLen algorithm found in Hello packet */ AlgorithmEnum* getAuthLenOffered(ZrtpPacketHello *hello, int32_t algoName); /** * Save the computed MitM secret to the ZID record of the peer */ void writeEnrollmentPBX(); /** * Compute my hvi value according to ZRTP specification. */ void computeHvi(ZrtpPacketDHPart* dh, ZrtpPacketHello *hello); void computeSharedSecretSet(ZIDRecord *zidRec); void computeAuxSecretIds(); void computeSRTPKeys(); void KDF(uint8_t* key, uint32_t keyLength, uint8_t* label, int32_t labelLength, uint8_t* context, int32_t contextLength, int32_t L, uint8_t* output); void generateKeysInitiator(ZrtpPacketDHPart *dhPart, ZIDRecord *zidRec); void generateKeysResponder(ZrtpPacketDHPart *dhPart, ZIDRecord *zidRec); void generateKeysMultiStream(); void computePBXSecret(); void setNegotiatedHash(AlgorithmEnum* hash); /* * The following methods are helper functions for ZrtpStateClass. * ZrtpStateClass calls them to prepare packets, send data, report * problems, etc. */ /** * Send a ZRTP packet. * * The state engines calls this method to send a packet via the RTP * stack. * * @param packet * Points to the ZRTP packet. * @return * zero if sending failed, one if packet was send */ int32_t sendPacketZRTP(ZrtpPacketBase *packet); /** * Activate a Timer using the host callback. * * @param tm * The time in milliseconds. * @return * zero if activation failed, one if timer was activated */ int32_t activateTimer(int32_t tm); /** * Cancel the active Timer using the host callback. * * @return * zero if activation failed, one if timer was activated */ int32_t cancelTimer(); /** * Prepare a Hello packet. * * Just take the preinitialized Hello packet and return it. No * further processing required. * * @return * A pointer to the initialized Hello packet. */ ZrtpPacketHello* prepareHello(); /** * Prepare a HelloAck packet. * * Just take the preinitialized HelloAck packet and return it. No * further processing required. * * @return * A pointer to the initialized HelloAck packet. */ ZrtpPacketHelloAck* prepareHelloAck(); /** * Prepare a Commit packet. * * We have received a Hello packet from our peer. Check the offers * it makes to us and select the most appropriate. Using the * selected values prepare a Commit packet and return it to protocol * state engine. * * @param hello * Points to the received Hello packet * @param errMsg * Points to an integer that can hold a ZRTP error code. * @return * A pointer to the prepared Commit packet */ ZrtpPacketCommit* prepareCommit(ZrtpPacketHello *hello, uint32_t* errMsg); /** * Prepare a Commit packet for Multi Stream mode. * * Using the selected values prepare a Commit packet and return it to protocol * state engine. * * @param hello * Points to the received Hello packet * @return * A pointer to the prepared Commit packet for multi stream mode */ ZrtpPacketCommit* prepareCommitMultiStream(ZrtpPacketHello *hello); /** * Prepare the DHPart1 packet. * * This method prepares a DHPart1 packet. The input to the method is always * a Commit packet received from the peer. Also we a in the role of the * Responder. * * When we receive a Commit packet we get the selected ciphers, hashes, etc * and cross-check if this is ok. Then we need to initialize a set of DH * keys according to the selected cipher. Using this data we prepare our DHPart1 * packet. */ ZrtpPacketDHPart* prepareDHPart1(ZrtpPacketCommit *commit, uint32_t* errMsg); /** * Prepare the DHPart2 packet. * * This method prepares a DHPart2 packet. The input to the method is always * a DHPart1 packet received from the peer. Our peer sends the DH1Part as * response to our Commit packet. Thus we are in the role of the * Initiator. * */ ZrtpPacketDHPart* prepareDHPart2(ZrtpPacketDHPart* dhPart1, uint32_t* errMsg); /** * Prepare the Confirm1 packet. * * This method prepare the Confirm1 packet. The input to this method is the * DHPart2 packect received from our peer. The peer sends the DHPart2 packet * as response of our DHPart1. Here we are in the role of the Responder * */ ZrtpPacketConfirm* prepareConfirm1(ZrtpPacketDHPart* dhPart2, uint32_t* errMsg); /** * Prepare the Confirm1 packet in multi stream mode. * * This method prepares the Confirm1 packet. The state engine call this method * if multi stream mode is selected and a Commit packet was received. The input to * this method is the Commit. * Here we are in the role of the Responder * */ ZrtpPacketConfirm* prepareConfirm1MultiStream(ZrtpPacketCommit* commit, uint32_t* errMsg); /** * Prepare the Confirm2 packet. * * This method prepare the Confirm2 packet. The input to this method is the * Confirm1 packet received from our peer. The peer sends the Confirm1 packet * as response of our DHPart2. Here we are in the role of the Initiator */ ZrtpPacketConfirm* prepareConfirm2(ZrtpPacketConfirm* confirm1, uint32_t* errMsg); /** * Prepare the Confirm2 packet in multi stream mode. * * This method prepares the Confirm2 packet. The state engine call this method if * multi stream mode is active and in state CommitSent. The input to this method is * the Confirm1 packet received from our peer. The peer sends the Confirm1 packet * as response of our Commit packet in multi stream mode. * Here we are in the role of the Initiator */ ZrtpPacketConfirm* prepareConfirm2MultiStream(ZrtpPacketConfirm* confirm1, uint32_t* errMsg); /** * Prepare the Conf2Ack packet. * * This method prepare the Conf2Ack packet. The input to this method is the * Confirm2 packet received from our peer. The peer sends the Confirm2 packet * as response of our Confirm1. Here we are in the role of the Initiator */ ZrtpPacketConf2Ack* prepareConf2Ack(ZrtpPacketConfirm* confirm2, uint32_t* errMsg); /** * Prepare the ErrorAck packet. * * This method prepares the ErrorAck packet. The input to this method is the * Error packet received from the peer. */ ZrtpPacketErrorAck* prepareErrorAck(ZrtpPacketError* epkt); /** * Prepare the Error packet. * * This method prepares the Error packet. The input to this method is the * error code to be included into the message. */ ZrtpPacketError* prepareError(uint32_t errMsg); /** * Prepare a ClearAck packet. * * This method checks if the GoClear message is valid. If yes then switch * off SRTP processing, stop sending of RTP packets (pause transmit) and * inform the user about the fact. Only if user confirms the GoClear message * normal RTP processing is resumed. * * @return * NULL if GoClear could not be authenticated, a ClearAck packet * otherwise. */ ZrtpPacketClearAck* prepareClearAck(ZrtpPacketGoClear* gpkt); /** * Prepare the ErrorAck packet. * * This method prepares the ErrorAck packet. The input to this method is the * Error packet received from the peer. */ ZrtpPacketPingAck* preparePingAck(ZrtpPacketPing* ppkt); /** * Prepare the RelayAck packet. * * This method prepares the RelayAck packet. The input to this method is the * SASrelay packet received from the peer. */ ZrtpPacketRelayAck* prepareRelayAck(ZrtpPacketSASrelay* srly, uint32_t* errMsg); /** * Prepare a GoClearAck packet w/o HMAC * * Prepare a GoCLear packet without a HMAC but with a short error message. * This type of GoClear is used if something went wrong during the ZRTP * negotiation phase. * * @return * A goClear packet without HMAC */ ZrtpPacketGoClear* prepareGoClear(uint32_t errMsg = 0); /** * Compare the hvi values. * * Compare a received Commit packet with our Commit packet and returns * which Commit packt is "more important". See chapter 5.2 to get further * information how to compare Commit packets. * * @param commit * Pointer to the peer's commit packet we just received. * @return * <0 if our Commit packet is "less important" * >0 if our Commit is "more important" * 0 shouldn't happen because we compare crypto hashes */ int32_t compareCommit(ZrtpPacketCommit *commit); /** * Verify the H2 hash image. * * Verifies the H2 hash contained in a received commit message. * This functions just verifies H2 but does not store it. * * @param commit * Pointer to the peer's commit packet we just received. * @return * true if H2 is ok and verified * false if H2 could not be verified */ bool verifyH2(ZrtpPacketCommit *commit); /** * Send information messages to the hosting environment. * * The ZRTP implementation uses this method to send information messages * to the host. Along with the message ZRTP provides a severity indicator * that defines: Info, Warning, Error, Alert. Refer to the MessageSeverity * enum in the ZrtpCallback class. * * @param severity * This defines the message's severity * @param subCode * The subcode identifying the reason. * @see ZrtpCodes#MessageSeverity */ void sendInfo(GnuZrtpCodes::MessageSeverity severity, int32_t subCode); /** * ZRTP state engine calls this if the negotiation failed. * * ZRTP calls this method in case ZRTP negotiation failed. The parameters * show the severity as well as some explanatory text. * * @param severity * This defines the message's severity * @param subCode * The subcode identifying the reason. * @see ZrtpCodes#MessageSeverity */ void zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity, int32_t subCode); /** * ZRTP state engine calls this method if the other side does not support ZRTP. * * If the other side does not answer the ZRTP Hello packets then * ZRTP calls this method, * */ void zrtpNotSuppOther(); /** * Signal SRTP secrets are ready. * * This method calls a callback method to inform the host that the SRTP * secrets are ready. * * @param part * Defines for which part (sender or receiver) to switch on security * @return * Returns false if something went wrong during initialization of SRTP * context. Propagate error back to state engine. */ bool srtpSecretsReady(EnableSecurity part); /** * Switch off SRTP secrets. * * This method calls a callback method to inform the host that the SRTP * secrets shall be cleared. * * @param part * Defines for which part (sender or receiver) to clear */ void srtpSecretsOff(EnableSecurity part); /** * ZRTP state engine calls these methods to enter or leave its * synchronization mutex. */ void synchEnter(); void synchLeave(); /** * Helper function to store ZRTP message data in a temporary buffer * * This functions first clears the temporary buffer, then stores * the packet's data to it. We use this to check the packet's HMAC * after we received the HMAC key in to following packet. * * @param data * Pointer to the packet's ZRTP message */ void storeMsgTemp(ZrtpPacketBase* pkt); /** * Helper function to check a ZRTP message HMAC * * This function gets a HMAC key and uses it to compute a HMAC * with this key and the stored data of a previous received ZRTP * message. It compares the computed HMAC and the HMAC stored in * the received message and returns the result. * * @param key * Pointer to the HMAC key. * @return * Returns true if the computed HMAC and the stored HMAC match, * false otherwise. */ bool checkMsgHmac(uint8_t* key); /** * Set the client ID for ZRTP Hello message. * * The user of ZRTP must set its id to identify itself in the * ZRTP HELLO message. The maximum length is 16 characters. Shorter * id string are allowed, they will be filled with blanks. A longer id * is truncated to 16 characters. * * The identifier is set in the Hello packet of ZRTP. Thus only after * setting the identifier ZRTP can compute the HMAC and the final * helloHash. * * @param id * The client's id * @param hpv * Pointer to hello packet version structure. */ void setClientId(std::string id, HelloPacketVersion* hpv); /** * Check and set a nonce. * * The function first checks if the nonce is already in use (was seen) in this ZRTP * session. Refer to 4.4.3.1. * * @param nonce * The nonce to check and to store if not already seen. * * @return * True if the the nonce was stroed, thus not yet seen. */ bool checkAndSetNonce(uint8_t* nonce); }; /** * @} */ #endif // ZRTP ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpCWrapper.h ================================================ /* This file defines the GNU ZRTP C-to-C++ wrapper. Copyright (C) 2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ZRTPCWRAPPER_H #define ZRTPCWRAPPER_H /** * * @file ZrtpCWrapper.h * @brief The GNU ZRTP C-to-C++ wrapper. * * To avoid any include of C++ header files some structure, defines, and * enumerations are repeated in this file. Refer to the inline comments if * you modify the file. * * @ingroup GNU_ZRTP * @{ * * @see ZRtp */ #include /** * Defines to specify the role a ZRTP peer has. * * According to the ZRTP specification the role determines which keys to * use to encrypt or decrypt SRTP data. * *
    *
  • The Initiator encrypts SRTP data using the keyInitiator and the * saltInitiator data, the Responder uses these data to decrypt. *
  • *
  • The Responder encrypts SRTP data using the keyResponder and the * saltResponder data, the Initiator uses these data to decrypt. *
  • *
*/ /* * Keep the following defines in sync with Role enumeration in ZrtpCallback.h */ #define Responder 1 /*!< This client is in ZRTP Responder mode */ #define Initiator 2 /*!< This client is in ZRTP Initiator mode */ #define CRC_SIZE 4 /*!< Size of CRC code of a ZRTP packet */ #define ZRTP_MAGIC 0x5a525450 /*!< The magic code that identifies a ZRTP packet */ #define MAX_ZRTP_SIZE 3072 /*!< The biggest ZRTP packet ever possible */ /* * IMPORTANT: keep the following enums in synch with ZrtpCodes. We copy them here * to avoid any C++ header includes and defines. The protocol states are located * ZrtpStateClass.h . */ /** * This enum defines the information message severity. * * The ZRTP implementation issues information messages to inform the user * about ongoing processing, unusual behavior, or alerts in case of severe * problems. Each main severity code a number of sub-codes exist that * specify the exact nature of the problem. * * An application gets message severity codes and the associated sub-codes * via the ZrtpUserCallback#showMessage method. * * The severity levels and their meaning are: * *
*
Info
keeps the user informed about ongoing processing and * security setup. The enumeration InfoCodes defines the subcodes. *
*
Warning
is an information about some security issues, e.g. if * an AES 256 encryption is request but only DH 3072 as public key scheme * is supported. ZRTP will establish a secure session (SRTP). The * enumeration WarningCodes defines the sub-codes. *
*
Severe
is used if an error occured during ZRTP protocol usage. * In case of Severe ZRTP will not establish a secure session. * The enumeration SevereCodes defines the sub-codes. *
*
Zrtp
shows a ZRTP security problem. Refer to the enumeration * ZrtpErrorCodes for sub-codes. GNU ZRTP of course will not * establish a secure session. *
*
* */ enum zrtp_MessageSeverity { zrtp_Info = 1, /*!< Just an info message */ zrtp_Warning, /*!< A Warning message - security can be established */ zrtp_Severe, /*!< Severe error, security will not be established */ zrtp_ZrtpError /*!< ZRTP error, security will not be established */ }; /** * Sub-codes for Info */ enum zrtp_InfoCodes { zrtp_InfoHelloReceived = 1, /*!< Hello received, preparing a Commit */ zrtp_InfoCommitDHGenerated, /*!< Commit: Generated a public DH key */ zrtp_InfoRespCommitReceived, /*!< Responder: Commit received, preparing DHPart1 */ zrtp_InfoDH1DHGenerated, /*!< DH1Part: Generated a public DH key */ zrtp_InfoInitDH1Received, /*!< Initiator: DHPart1 received, preparing DHPart2 */ zrtp_InfoRespDH2Received, /*!< Responder: DHPart2 received, preparing Confirm1 */ zrtp_InfoInitConf1Received, /*!< Initiator: Confirm1 received, preparing Confirm2 */ zrtp_InfoRespConf2Received, /*!< Responder: Confirm2 received, preparing Conf2Ack */ zrtp_InfoRSMatchFound, /*!< At least one retained secrets matches - security OK */ zrtp_InfoSecureStateOn, /*!< Entered secure state */ zrtp_InfoSecureStateOff /*!< No more security for this session */ }; /** * Sub-codes for Warning */ enum zrtp_WarningCodes { zrtp_WarningDHAESmismatch = 1, /*!< Commit contains an AES256 cipher but does not offer a Diffie-Helman 4096 */ zrtp_WarningGoClearReceived, /*!< Received a GoClear message */ zrtp_WarningDHShort, /*!< Hello offers an AES256 cipher but does not offer a Diffie-Helman 4096 */ zrtp_WarningNoRSMatch, /*!< No retained shared secrets available - must verify SAS */ zrtp_WarningCRCmismatch, /*!< Internal ZRTP packet checksum mismatch - packet dropped */ zrtp_WarningSRTPauthError, /*!< Dropping packet because SRTP authentication failed! */ zrtp_WarningSRTPreplayError, /*!< Dropping packet because SRTP replay check failed! */ zrtp_WarningNoExpectedRSMatch /*!< Valid retained shared secrets availabe but no matches found - must verify SAS */ }; /** * Sub-codes for Severe */ enum zrtp_SevereCodes { zrtp_SevereHelloHMACFailed = 1, /*!< Hash HMAC check of Hello failed! */ zrtp_SevereCommitHMACFailed, /*!< Hash HMAC check of Commit failed! */ zrtp_SevereDH1HMACFailed, /*!< Hash HMAC check of DHPart1 failed! */ zrtp_SevereDH2HMACFailed, /*!< Hash HMAC check of DHPart2 failed! */ zrtp_SevereCannotSend, /*!< Cannot send data - connection or peer down? */ zrtp_SevereProtocolError, /*!< Internal protocol error occured! */ zrtp_SevereNoTimer, /*!< Cannot start a timer - internal resources exhausted? */ zrtp_SevereTooMuchRetries /*!< Too much retries during ZRTP negotiation - connection or peer down? */ }; /** * Error codes according to the ZRTP specification chapter 6.9 * * GNU ZRTP uses these error codes in two ways: to fill the appropriate * field ing the ZRTP Error packet and as sub-code in * ZrtpUserCallback#showMessage(). GNU ZRTP uses thes error codes also * to report received Error packts, in this case the sub-codes are their * negative values. * * The enumeration member comments are copied from the ZRTP specification. */ enum zrtp_ZrtpErrorCodes { zrtp_MalformedPacket = 0x10, /*!< Malformed packet (CRC OK, but wrong structure) */ zrtp_CriticalSWError = 0x20, /*!< Critical software error */ zrtp_UnsuppZRTPVersion = 0x30, /*!< Unsupported ZRTP version */ zrtp_HelloCompMismatch = 0x40, /*!< Hello components mismatch */ zrtp_UnsuppHashType = 0x51, /*!< Hash type not supported */ zrtp_UnsuppCiphertype = 0x52, /*!< Cipher type not supported */ zrtp_UnsuppPKExchange = 0x53, /*!< Public key exchange not supported */ zrtp_UnsuppSRTPAuthTag = 0x54, /*!< SRTP auth. tag not supported */ zrtp_UnsuppSASScheme = 0x55, /*!< SAS scheme not supported */ zrtp_NoSharedSecret = 0x56, /*!< No shared secret available, DH mode required */ zrtp_DHErrorWrongPV = 0x61, /*!< DH Error: bad pvi or pvr ( == 1, 0, or p-1) */ zrtp_DHErrorWrongHVI = 0x62, /*!< DH Error: hvi != hashed data */ zrtp_SASuntrustedMiTM = 0x63, /*!< Received relayed SAS from untrusted MiTM */ zrtp_ConfirmHMACWrong = 0x70, /*!< Auth. Error: Bad Confirm pkt HMAC */ zrtp_NonceReused = 0x80, /*!< Nonce reuse */ zrtp_EqualZIDHello = 0x90, /*!< Equal ZIDs in Hello */ zrtp_GoCleatNotAllowed = 0x100, /*!< GoClear packet received, but not allowed */ zrtp_IgnorePacket = 0x7fffffff /*!< Internal state, not reported */ }; /** * Information codes for the Enrollment user callbacks. */ enum zrtp_InfoEnrollment { zrtp_EnrollmentRequest, //!< Aks user to confirm or deny an Enrollemnt request zrtp_EnrollmentCanceled, //!< User did not confirm the PBX enrollement zrtp_EnrollmentFailed, //!< Enrollment process failed, no PBX secret available zrtp_EnrollmentOk //!< Enrollment process for this PBX was ok }; /* The ZRTP protocol states */ enum zrtpStates { Initial, /*!< Initial state after starting the state engine */ Detect, /*!< State sending Hello, try to detect answer message */ AckDetected, /*!< HelloAck received */ AckSent, /*!< HelloAck sent after Hello received */ WaitCommit, /*!< Wait for a Commit message */ CommitSent, /*!< Commit message sent */ WaitDHPart2, /*!< Wait for a DHPart2 message */ WaitConfirm1, /*!< Wait for a Confirm1 message */ WaitConfirm2, /*!< Wait for a confirm2 message */ WaitConfAck, /*!< Wait for Conf2Ack */ WaitClearAck, /*!< Wait for clearAck - not used */ SecureState, /*!< This is the secure state - SRTP active */ WaitErrorAck, /*!< Wait for ErrorAck message */ numberOfStates /*!< Gives total number of protocol states */ }; /*! The algorihms that we support in SRTP and that ZRTP can negotiate. */ typedef enum { zrtp_Aes = 1, /*!< Use AES as symmetrical cipher algorithm */ zrtp_TwoFish, /*!< Use TwoFish as symmetrical cipher algorithm */ zrtp_Sha1, /*!< Use Sha1 as authentication algorithm */ zrtp_Skein /*!< Use Skein as authentication algorithm */ } zrtp_SrtpAlgorithms; /** * This structure contains pointers to the SRTP secrets and the role info. * * About the role and what the meaning of the role is refer to the * of the enum Role. The pointers to the secrets are valid as long as * the ZRtp object is active. To use these data after the ZRtp object's * lifetime you may copy the data into a save place. */ typedef struct c_srtpSecrets { zrtp_SrtpAlgorithms symEncAlgorithm;/*!< symmetrical cipher algorithm */ const uint8_t* keyInitiator; /*!< Initiator's key */ int32_t initKeyLen; /*!< Initiator's key length */ const uint8_t* saltInitiator; /*!< Initiator's salt */ int32_t initSaltLen; /*!< Initiator's salt length */ const uint8_t* keyResponder; /*!< Responder's key */ int32_t respKeyLen; /*!< Responder's key length */ const uint8_t* saltResponder; /*!< Responder's salt */ int32_t respSaltLen; /*!< Responder's salt length */ zrtp_SrtpAlgorithms authAlgorithm; /*!< SRTP authentication algorithm */ int32_t srtpAuthTagLen; /*!< SRTP authentication length */ char* sas; /*!< The SAS string */ int32_t role; /*!< ZRTP role of this client */ } C_SrtpSecret_t; /* * Keep the following defines in sync with enum EnableSecurity in ZrtpCallback.h */ #define ForReceiver 1 /*!< Enable security for SRTP receiver */ #define ForSender 2 /*!< Enable security for SRTP sender */ #ifdef __cplusplus #ifdef __GNUC__ #pragma GCC visibility push(default) #endif extern "C" { typedef class ZRtp ZRtp; typedef class ZrtpCallbackWrapper ZrtpCallbackWrapper; typedef class ZrtpConfigure ZrtpConfigure; #else typedef struct ZRtp ZRtp; typedef struct ZrtpCallbackWrapper ZrtpCallbackWrapper; typedef struct ZrtpConfigure ZrtpConfigure; #endif typedef struct zrtpContext { ZRtp* zrtpEngine; /*!< Holds the real ZRTP engine */ ZrtpCallbackWrapper* zrtpCallback; /*!< Help class Callback wrapper */ ZrtpConfigure* configure; /*!< Optional configuration data */ ZRtp* zrtpMaster; /*!< Holds the master ZRTP stream in case this is a multi-stream */ void* userData; /*!< User data, set by application */ } ZrtpContext; /** * This structure defines the callback functions required by GNU ZRTP. * * The RTP stack specific part must implement the callback methods. * The generic part of GNU ZRTP uses these mehtods * to communicate with the specific part, for example to send data * via the RTP/SRTP stack, to set timers and cancel timer and so on. * * The generiy part of GNU ZRTP needs only a few callback methods to * be implemented by the specific part. * * @author Werner Dittmann */ /** * The following methods define the GNU ZRTP callback interface. * For detailed documentation refer to file ZrtpCallback.h, each C * method has "zrtp_" prepended to the C++ name. * * @see ZrtpCallback */ typedef struct zrtp_Callbacks { /** * Send a ZRTP packet via RTP. * * ZRTP calls this method to send a ZRTP packet via the RTP session. * The ZRTP packet will have to be created using the provided ZRTP message. * * @param ctx * Pointer to the opaque ZrtpContext structure. * @param data * Points to ZRTP message to send. * @param length * The length in bytes of the data * @return * zero if sending failed, one if packet was sent */ int32_t (*zrtp_sendDataZRTP) (ZrtpContext* ctx, const uint8_t* data, int32_t length ) ; /** * Activate timer. * * @param ctx * Pointer to the opaque ZrtpContext structure. * @param time * The time in ms for the timer * @return * zero if activation failed, one if timer was activated */ int32_t (*zrtp_activateTimer) (ZrtpContext* ctx, int32_t time ) ; /** * Cancel the active timer. * * @param ctx * Pointer to the opaque ZrtpContext structure. * @return * zero if cancel action failed, one if timer was canceled */ int32_t (*zrtp_cancelTimer)(ZrtpContext* ctx) ; /** * Send information messages to the hosting environment. * * The ZRTP implementation uses this method to send information * messages to the host. Along with the message ZRTP provides a * severity indicator that defines: Info, Warning, Error, * Alert. Refer to the MessageSeverity enum above. * * @param ctx * Pointer to the opaque ZrtpContext structure. * @param severity * This defines the message's severity * @param subCode * The subcode identifying the reason. * @see ZrtpCodes#MessageSeverity */ void (*zrtp_sendInfo) (ZrtpContext* ctx, int32_t severity, int32_t subCode ) ; /** * SRTP crypto data ready for the sender or receiver. * * The ZRTP implementation calls this method right after all SRTP * secrets are computed and ready to be used. The parameter points * to a structure that contains pointers to the SRTP secrets and a * enum Role. The called method (the implementation * of this abstract method) must either copy the pointers to the SRTP * data or the SRTP data itself to a save place. The SrtpSecret_t * structure is destroyed after the callback method returns to the * ZRTP implementation. * * The SRTP data themselves are obtained in the ZRtp object and are * valid as long as the ZRtp object is active. TheZRtp's * destructor clears the secrets. Thus the called method needs to * save the pointers only, ZRtp takes care of the data. * * The implementing class may enable SRTP processing in this * method or delay it to srtpSecertsOn(). * * @param ctx * Pointer to the opaque ZrtpContext structure. * @param secrets A pointer to a SrtpSecret_t structure that * contains all necessary data. * * @param part for which part (Sender or Receiver) this data is * valid. * * @return Returns false if something went wrong during * initialization of SRTP context, for example memory shortage. */ int32_t (*zrtp_srtpSecretsReady) (ZrtpContext* ctx, C_SrtpSecret_t* secrets, int32_t part ) ; /** * Switch off the security for the defined part. * * @param ctx * Pointer to the opaque ZrtpContext structure. * @param part Defines for which part (sender or receiver) to * switch on security */ void (*zrtp_srtpSecretsOff) (ZrtpContext* ctx, int32_t part ) ; /** * Switch on the security. * * ZRTP calls this method after it has computed the SAS and check * if it is verified or not. In addition ZRTP provides information * about the cipher algorithm and key length for the SRTP session. * * This method must enable SRTP processing if it was not enabled * during sertSecretsReady(). * * @param ctx * Pointer to the opaque ZrtpContext structure. * @param c The name of the used cipher algorithm and mode, or * NULL * * @param s The SAS string * * @param verified if verified is true then SAS was * verified by both parties during a previous call. */ void (*zrtp_rtpSecretsOn) (ZrtpContext* ctx, char* c, char* s, int32_t verified ) ; /** * This method handles GoClear requests. * * According to the ZRTP specification the user must be informed about * a GoClear request because the ZRTP implementation switches off security * if it could authenticate the GoClear packet. * * Note: GoClear is not yet implemented in GNU ZRTP. * * @param ctx * Pointer to the opaque ZrtpContext structure. */ void (*zrtp_handleGoClear)(ZrtpContext* ctx) ; /** * Handle ZRTP negotiation failed. * * ZRTP calls this method in case ZRTP negotiation failed. The * parameters show the severity as well as the reason. * * @param ctx * Pointer to the opaque ZrtpContext structure. * @param severity * This defines the message's severity * @param subCode * The subcode identifying the reason. * @see ZrtpCodes#MessageSeverity */ void (*zrtp_zrtpNegotiationFailed) (ZrtpContext* ctx, int32_t severity, int32_t subCode ) ; /** * ZRTP calls this method if the other side does not support ZRTP. * * @param ctx * Pointer to the opaque ZrtpContext structure. * If the other side does not answer the ZRTP Hello packets then * ZRTP calls this method, * */ void (*zrtp_zrtpNotSuppOther)(ZrtpContext* ctx) ; /** * Enter synchronization mutex. * * GNU ZRTP requires one mutex to synchronize its * processing. Because mutex implementations depend on the * underlying infrastructure, for example operating system or * thread implementation, GNU ZRTP delegates mutex handling to the * specific part of its implementation. * * @param ctx * Pointer to the opaque ZrtpContext structure. */ void (*zrtp_synchEnter)(ZrtpContext* ctx) ; /** * Leave synchronization mutex. * * @param ctx * Pointer to the opaque ZrtpContext structure. */ void (*zrtp_synchLeave)(ZrtpContext* ctx) ; /** * Inform about a PBX enrollment request. * * Please refer to chapter 8.3 ff to get more details about PBX * enrollment and SAS relay. * * Note: PBX enrollement is not yet fully supported by GNU * ZRTP. * * @param ctx * Pointer to the opaque ZrtpContext structure. * @param info Give some information to the user about the PBX * requesting an enrollment. */ void (*zrtp_zrtpAskEnrollment) (ZrtpContext* ctx, int32_t info) ; /** * Inform about PBX enrollment result. * * Informs the use about the acceptance or denial of an PBX enrollment * request * * Note: PBX enrollement is not yet fully supported by GNU * ZRTP. * * @param ctx * Pointer to the opaque ZrtpContext structure. * @param info Give some information to the user about the result * of an enrollment. */ void (*zrtp_zrtpInformEnrollment) (ZrtpContext* ctx, int32_t info ) ; /** * Request a SAS signature. * * After ZRTP was able to compute the Short Authentication String * (SAS) it calls this method. The client may now use an * approriate method to sign the SAS. The client may use * ZrtpQueue#setSignatureData() to store the signature data and * enable signature transmission to the other peer. Refer to * chapter 8.2 of ZRTP specification. * * Note: SAS signing is not yet fully supported by GNU * ZRTP. * * @param ctx * Pointer to the opaque ZrtpContext structure. * @param sas * Pointer to the 32 byte SAS hash to sign. * */ void (*zrtp_signSAS)(ZrtpContext* ctx, uint8_t* sas) ; /** * ZRTPQueue calls this method to request a SAS signature check. * * After ZRTP received a SAS signature in one of the Confirm packets it * call this method. The client may use getSignatureLength() * and getSignatureData()of ZrtpQueue to get the signature * data and perform the signature check. Refer to chapter 8.2 of ZRTP * specification. * * If the signature check fails the client may return false to ZRTP. In * this case ZRTP signals an error to the other peer and terminates * the ZRTP handshake. * * Note: SAS signing is not yet fully supported by GNU * ZRTP. * * @param ctx * Pointer to the opaque ZrtpContext structure. * @param sas * Pointer to the 32 byte SAS hash that was signed by the other peer. * @return * true if the signature was ok, false otherwise. * */ int32_t (*zrtp_checkSASSignature) (ZrtpContext* ctx, uint8_t* sas ) ; } zrtp_Callbacks; /** * Create the GNU ZRTP C wrapper. * * This wrapper implements the C interface to the C++ based GNU ZRTP. * @returns * Pointer to the ZrtpContext */ ZrtpContext* zrtp_CreateWrapper(void); /** * Initialize the ZRTP protocol engine. * * This method initialized the GNU ZRTP protocol engine. An application * calls this method to actually create the ZRTP protocol engine and * initialize its configuration data. This method does not start the * protocol engine. * * If an application requires a specific algorithm configuration then it * must set the algorithm configuration data before it initializes the * ZRTP protocol engine. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param cb * The callback structure that holds the addresses of the callback * methods. * @param id * A C string that holds the ZRTP client id, only the first 16 chars * are used. * @param zidFilename * The name of the ZID file. This file holds some parameters and * other data like additional shared secrets. * @param userData * A pointer to user data. The wrapper just stores this pointer in * the ZrtpContext and the application may use it for its purposes. * @param mitmMode * A trusted Mitm (PBX) must set this to true. The ZRTP engine sets * the M Flag in the Hello packet to announce a trusted MitM. * @returns * Pointer to the ZrtpContext * * @see zrtp_InitializeConfig */ void zrtp_initializeZrtpEngine(ZrtpContext* zrtpContext, zrtp_Callbacks *cb, const char* id, const char* zidFilename, void* userData, int32_t mitmMode); /** * Destroy the ZRTP wrapper and its underlying objects. */ void zrtp_DestroyWrapper (ZrtpContext* zrtpContext); /** * Computes the ZRTP checksum over a received ZRTP packet buffer and * compares the result with received checksum. * * @param buffer * Pointer to ZRTP packet buffer * @param length * Length of the packet buffer excluding received CRC data * @param crc * The received CRC data. * @returns * True if CRC matches, false otherwise. */ int32_t zrtp_CheckCksum(uint8_t* buffer, uint16_t length, uint32_t crc); /** * Computes the ZRTP checksum over a newly created ZRTP packet buffer. * * @param buffer * Pointer to the created ZRTP packet buffer * @param length * Length of the packet buffer * @returns * The computed CRC. */ uint32_t zrtp_GenerateCksum(uint8_t* buffer, uint16_t length); /** * Prepares the ZRTP checksum for appending to ZRTP packet. * @param crc * The computed CRC data. * @returns * Prepared CRC data in host order */ uint32_t zrtp_EndCksum(uint32_t crc); /** * Kick off the ZRTP protocol engine. * * This method calls the ZrtpStateClass#evInitial() state of the state * engine. After this call we are able to process ZRTP packets * from our peer and to process them. * * NOTE: application shall never call this method directly but use the * appropriate method provided by the RTP implementation. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. */ void zrtp_startZrtpEngine(ZrtpContext* zrtpContext); /** * Stop ZRTP security. * * NOTE: An application shall never call this method directly but use the * appropriate method provided by the RTP implementation. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. */ void zrtp_stopZrtpEngine(ZrtpContext* zrtpContext); /** * Process RTP extension header. * * This method expects to get a pointer to the message part of * a ZRTP packet. * * NOTE: An application shall never call this method directly. Only * the module that implements the RTP binding shall use this method * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param extHeader * A pointer to the first byte of the ZRTP message part. * @param peerSSRC * The peer's SSRC. * @param length of the received data packet - used to do santity checks. * * @return * Code indicating further packet handling, see description above. */ void zrtp_processZrtpMessage(ZrtpContext* zrtpContext, uint8_t *extHeader, uint32_t peerSSRC, size_t length); /** * Process a timeout event. * * We got a timeout from the timeout provider. Forward it to the * protocol state engine. * * NOTE: application shall never call this method directly. Only * the module that implements the RTP binding shall use this method * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. */ void zrtp_processTimeout(ZrtpContext* zrtpContext); /* * Check for and handle GoClear ZRTP packet header. * * This method checks if this is a GoClear packet. If not, just return * false. Otherwise handle it according to the specification. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param extHeader * A pointer to the first byte of the extension header. Refer to * RFC3550. * @return * False if not a GoClear, true otherwise. * int32_t zrtp_handleGoClear(ZrtpContext* zrtpContext, uint8_t *extHeader); */ /** * Set the auxilliary secret. * * Use this method to set the auxilliary secret data. Refer to ZRTP * specification, chapter 4.3 ff * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param data * Points to the secret data. * @param length * Length of the auxilliary secrect in bytes */ void zrtp_setAuxSecret(ZrtpContext* zrtpContext, uint8_t* data, int32_t length); /** * Check current state of the ZRTP state engine * * NOTE: application usually don't call this method. Only * the m-odule that implements the RTP binding shall use this method * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param state * The state to check. * @return * Returns true if ZRTP engine is in the given state, false otherwise. */ int32_t zrtp_inState(ZrtpContext* zrtpContext, int32_t state); /** * Set SAS as verified. * * Call this method if the user confirmed (verfied) the SAS. ZRTP * remembers this together with the retained secrets data. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. */ void zrtp_SASVerified(ZrtpContext* zrtpContext); /** * Reset the SAS verfied flag for the current active user's retained secrets. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. */ void zrtp_resetSASVerified(ZrtpContext* zrtpContext); /** * Get the ZRTP Hello Hash data. * * Use this method to get the ZRTP Hello Hash data. The method * returns the data as a string containing the ZRTP protocol version and * hex-digits. * The index defines which Hello packet to use. Each supported ZRTP procol version * uses a different Hello packet and thus computes different hashes. * * Refer to ZRTP specification, chapter 8. * * @param index * Hello hash of the Hello packet identfied by index. Index must be 0 <= index < zrtp_getNumberSupportedVersions(). * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * * @return * a pointer to a C-string that contains the Hello hash formatted according to RFC6189 section 8 * without the leading 'a=zrtp-hash:' SDP attribute identifier. The hello hash is available * immediately after @c zrtp_CreateWrapper. The caller must @c free() if it does not use the * hello hash C-string anymore. * * @see zrtp_getNumberSupportedVersions() */ char* zrtp_getHelloHash(ZrtpContext* zrtpContext, int32_t index); /** * Get the peer's ZRTP Hello Hash data. * * Use this method to get the peer's ZRTP Hello Hash data. The method * returns the data as a string containing the ZRTP protocol version and * hex-digits. * * The peer's hello hash is available only after ZRTP received a hello. If * no data is available the function returns an empty string. * * Refer to ZRTP specification, chapter 8. * * @return * a C-string containing the Hello version and the hello hash as hex digits. The caller * must @c free() if it does not use the hello hash C-string anymore. */ char* zrtp_getPeerHelloHash(ZrtpContext* zrtpContext); /** * Get the peer's previously associated name. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @return * a heap allocated char array that contains the name. * If ZRTP was not started or there was no name set the method * returns NULL. The user is responsible for freeing the returned * memory. */ char* zrtp_getPeerName(ZrtpContext* zrtpContext); /** * Associate a name with the peer. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param name * Char array containing the name to be associated. */ void zrtp_putPeerName(ZrtpContext* zrtpContext, const char* name); /** * Get Multi-stream parameters. * * Use this method to get the Multi-stream parameters that were computed * during the ZRTP handshake. An application may use these parameters to * enable multi-stream processing for an associated SRTP session. * * The application must not modify the contents of returned char array, it * is opaque data. The application may hand over this string to a new ZRTP * instance to enable multi-stream processing for this new session. * * Refer to chapter 4.4.2 in the ZRTP specification for further details * and restriction how and when to use multi-stream mode. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param length * Pointer to an integer that receives the length of the char array * @return * a char array that contains the multi-stream parameters. * If ZRTP was not started or ZRTP is not yet in secure state the method * returns NULL and a length of 0. The caller must @c free() if it does not * use the data anymore, e.g. after using it in zrtp_setMultiStrParams */ char* zrtp_getMultiStrParams(ZrtpContext* zrtpContext, int32_t *length); /** * Set Multi-stream parameters. * * Use this method to set the parameters required to enable Multi-stream * processing of ZRTP. The multi-stream parameters must be set before the * application starts the ZRTP protocol engine. * * Refer to chapter 4.4.2 in the ZRTP specification for further details * of multi-stream mode. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure of the multi-media (slave) ZRTP session * @param length * The integer that contains the length of the char array * @param parameters * A char array that contains the multi-stream parameters that this * new ZRTP instance shall use. See also * getMultiStrParams() * @param master * Pointer to the opaque ZrtpContext structure of the master ZRTP stream. */ void zrtp_setMultiStrParams(ZrtpContext* zrtpContext, char* parameters, int32_t length, ZrtpContext* master); /** * Check if this ZRTP session is a Multi-stream session. * * Use this method to check if this ZRTP instance uses multi-stream. * Refer to chapters 4.2 and 4.4.2 in the ZRTP. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @return * True if multi-stream is used, false otherwise. */ int32_t zrtp_isMultiStream(ZrtpContext* zrtpContext); /** * Check if the other ZRTP client supports Multi-stream. * * Use this method to check if the other ZRTP client supports * Multi-stream mode. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @return * True if multi-stream is available, false otherwise. */ int32_t zrtp_isMultiStreamAvailable(ZrtpContext* zrtpContext); /** * Accept a PBX enrollment request. * * If a PBX service asks to enroll the PBX trusted MitM key and the user * accepts this request, for example by pressing an OK button, the client * application shall call this method and set the parameter * accepted to true. If the user does not accept the request * set the parameter to false. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param accepted * True if the enrollment request is accepted, false otherwise. */ void zrtp_acceptEnrollment(ZrtpContext* zrtpContext, int32_t accepted); /** * Check the state of the enrollment mode. * * If true then we will set the enrollment flag (E) in the confirm * packets and performs the enrollment actions. A MitM (PBX) enrollment service * started this ZRTP session. Can be set to true only if mitmMode is also true. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @return status of the enrollmentMode flag. */ int32_t zrtp_isEnrollmentMode(ZrtpContext* zrtpContext); /** * Check the state of the enrollment mode. * * If true then we will set the enrollment flag (E) in the confirm * packets and perform the enrollment actions. A MitM (PBX) enrollment * service must sets this mode to true. * * Can be set to true only if mitmMode is also true. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param enrollmentMode defines the new state of the enrollmentMode flag */ void zrtp_setEnrollmentMode(ZrtpContext* zrtpContext, int32_t enrollmentMode); /** * Check if a peer's cache entry has a vaild MitM key. * * If true then the other peer ha a valid MtiM key, i.e. the peer has performed * the enrollment procedure. A PBX ZRTP Back-2-Back application can use this function * to check which of the peers is enrolled. * * @return True if the other peer has a valid Mitm key (is enrolled). */ int32_t isPeerEnrolled(ZrtpContext* zrtpContext); /** * Send the SAS relay packet. * * The method creates and sends a SAS relay packet according to the ZRTP * specifications. Usually only a MitM capable user agent (PBX) uses this * function. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param sh the full SAS hash value * @param render the SAS rendering algorithm */ int32_t zrtp_sendSASRelayPacket(ZrtpContext* zrtpContext, uint8_t* sh, char* render); /** * Get the commited SAS rendering algorithm for this ZRTP session. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @return the commited SAS rendering algorithm. The caller must @c free() the buffer * if it does not use the string anymore. */ const char* zrtp_getSasType(ZrtpContext* zrtpContext); /** * Get the computed SAS hash for this ZRTP session. * * A PBX ZRTP back-to-Back function uses this function to get the SAS * hash of an enrolled client to construct the SAS relay packet for * the other client. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @return a pointer to the byte array that contains the full * SAS hash. */ uint8_t* zrtp_getSasHash(ZrtpContext* zrtpContext); /** * Set signature data * * This functions stores signature data and transmitts it during ZRTP * processing to the other party as part of the Confirm packets. Refer to * chapters 5.7 and 7.2. * * The signature data must be set before ZRTP the application calls * start(). * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param data * The signature data including the signature type block. The method * copies this data into the Confirm packet at signature type block. * @param length * The length of the signature data in bytes. This length must be * multiple of 4. * @return * True if the method stored the data, false otherwise. */ int32_t zrtp_setSignatureData(ZrtpContext* zrtpContext, uint8_t* data, int32_t length); /** * Get signature data * * This functions returns signature data that was receivied during ZRTP * processing. Refer to chapters 5.7 and 7.2. * * The signature data can be retrieved after ZRTP enters secure state. * start(). * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @return * Number of bytes copied into the data buffer */ const uint8_t* zrtp_getSignatureData(ZrtpContext* zrtpContext); /** * Get length of signature data * * This functions returns the length of signature data that was receivied * during ZRTP processing. Refer to chapters 5.7 and 7.2. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @return * Length in bytes of the received signature data. The method returns * zero if no signature data avilable. */ int32_t zrtp_getSignatureLength(ZrtpContext* zrtpContext); /** * Emulate a Conf2Ack packet. * * This method emulates a Conf2Ack packet. According to ZRTP specification * the first valid SRTP packet that the Initiator receives must switch * on secure mode. Refer to chapter 4 in the specificaton * * NOTE: application shall never call this method directly. Only * the module that implements the RTP binding shall use this method * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. */ void zrtp_conf2AckSecure(ZrtpContext* zrtpContext); /** * Get other party's ZID (ZRTP Identifier) data * * This functions returns the other party's ZID that was receivied * during ZRTP processing. * * The ZID data can be retrieved after ZRTP receive the first Hello * packet from the other party. The application may call this method * for example during SAS processing in showSAS(...) user callback * method. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param data * Pointer to a data buffer. This buffer must have a size of * at least 12 bytes (96 bit) (ZRTP Identifier, see chap. 4.9) * @return * Number of bytes copied into the data buffer - must be equivalent * to 12 bytes. */ int32_t zrtp_getPeerZid(ZrtpContext* zrtpContext, uint8_t* data); /** * Get number of supported ZRTP protocol versions. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * * @return the number of supported ZRTP protocol versions or -1 in case * of an error, for example non-initialized data. */ int32_t zrtp_getNumberSupportedVersions(ZrtpContext* zrtpContext); /** * Get negotiated ZRTP protocol versions. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * * @return the integer representation of the negotiated ZRTP protocol version * of -1 in case of an error, for example non-initialized data. */ int32_t zrtp_getCurrentProtocolVersion(ZrtpContext* zrtpContext); /** * This enumerations list all configurable algorithm types. */ /* Keep in synch with enumeration in ZrtpConfigure.h */ typedef enum zrtp_AlgoTypes { zrtp_HashAlgorithm = 1, zrtp_CipherAlgorithm, zrtp_PubKeyAlgorithm, zrtp_SasType, zrtp_AuthLength } Zrtp_AlgoTypes; /** * Initialize the GNU ZRTP Configure data. * * Initializing and setting a ZRTP configuration is optional. GNU ZRTP * uses a sensible default if an application does not define its own * ZRTP configuration. * * If an application initialize th configure data it must set the * configuration data. * * The ZRTP specification, chapters 5.1.2 through 5.1.6 defines the * algorithm names and their meaning. * * The current ZRTP implementation implements all mandatory algorithms * plus a set of the optional algorithms. An application shall use * @c zrtp_getAlgorithmNames to get the names of the available algorithms. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @returns * Pointer to the ZrtpConfCtx * * @see zrtp_getAlgorithmNames */ int32_t zrtp_InitializeConfig (ZrtpContext* zrtpContext); /** * Get names of all available algorithmes of a given algorithm type. * * The algorithm names are as specified in the ZRTP specification, chapters * 5.1.2 through 5.1.6 . * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param type * The algorithm type. * @returns * A NULL terminated array of character pointers. */ char** zrtp_getAlgorithmNames(ZrtpContext* zrtpContext, Zrtp_AlgoTypes type); /** * Free storage used to store the algorithm names. * * If an application does not longer require the algoritm names it should * free the space. * * @param names * The NULL terminated array of character pointers. */ void zrtp_freeAlgorithmNames(char** names); /** * Convenience function that sets a pre-defined standard configuration. * * The standard configuration consists of the following algorithms: *
    *
  • Hash: SHA256
  • *
  • Symmetric Cipher: AES 128, AES 256
  • *
  • Public Key Algorithm: DH2048, DH3027, MultiStream
  • *
  • SAS type: libase 32
  • *
  • SRTP Authentication lengths: 32, 80
  • *
* * @param zrtpContext * Pointer to the opaque ZrtpContext structure. */ void zrtp_setStandardConfig(ZrtpContext* zrtpContext); /** * Convenience function that sets the mandatory algorithms only. * * Mandatory algorithms are: *
    *
  • Hash: SHA256
  • *
  • Symmetric Cipher: AES 128
  • *
  • Public Key Algorithm: DH3027, MultiStream
  • *
  • SAS type: libase 32
  • *
  • SRTP Authentication lengths: 32, 80
  • *
* * @param zrtpContext * Pointer to the opaque ZrtpContext structure. */ void zrtp_setMandatoryOnly(ZrtpContext* zrtpContext); /** * Clear all configuration data. * * The functions clears all configuration data. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. */ void zrtp_confClear(ZrtpContext* zrtpContext); /** * Add an algorithm to configuration data. * * Adds the specified algorithm to the configuration data. * If no free configuration data slot is available the * function does not add the algorithm and returns -1. The * methods appends the algorithm to the existing algorithms. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param algoType * Specifies which algorithm type to select * @param algo * The name of the algorithm to add. * @return * Number of free configuration data slots or -1 on error */ int32_t zrtp_addAlgo(ZrtpContext* zrtpContext, Zrtp_AlgoTypes algoType, const char* algo); /** * Add an algorithm to configuration data at given index * * Adds the specified algorithm to the configuration data vector * at a given index. If the index is larger than the actual size * of the configuration vector the method just appends the algorithm. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param algoType * Specifies which algorithm type to select * @param algo * The name of the algorithm to add. * @param index * The index where to add the algorihm * @return * Number of free configuration data slots or -1 on error */ int32_t zrtp_addAlgoAt(ZrtpContext* zrtpContext, Zrtp_AlgoTypes algoType, const char* algo, int32_t index); /** * Remove a algorithm from configuration data. * * Removes the specified algorithm from configuration data. If * the algorithm was not configured previously the function does * not modify the configuration data and returns the number of * free configuration data slots. * * If an application removes all algorithms then ZRTP does not * include any algorithm into the hello message and falls back * to a predefined mandatory algorithm. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param algoType * Specifies which algorithm type to select * @param algo * The name of the algorithm to remove. * @return * Number of free configuration slots or -1 on error */ int32_t zrtp_removeAlgo(ZrtpContext* zrtpContext, Zrtp_AlgoTypes algoType, const char* algo); /** * Returns the number of configured algorithms. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param algoType * Specifies which algorithm type to select * @return * The number of configured algorithms (used configuration * data slots) or -1 on error */ int32_t zrtp_getNumConfiguredAlgos(ZrtpContext* zrtpContext, Zrtp_AlgoTypes algoType); /** * Returns the identifier of the algorithm at index. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param algoType * Specifies which algorithm type to select * @param index * The index in the list of the algorihm type * @return * A pointer to the algorithm name. If the index * does not point to a configured slot then the function * returns NULL. * */ const char* zrtp_getAlgoAt(ZrtpContext* zrtpContext, Zrtp_AlgoTypes algoType, int32_t index); /** * Checks if the configuration data of the algorihm type already contains * a specific algorithms. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param algoType * Specifies which algorithm type to select * @param algo * The name of the algorithm to check * @return * True if the algorithm was found, false otherwise. * */ int32_t zrtp_containsAlgo(ZrtpContext* zrtpContext, Zrtp_AlgoTypes algoType, const char* algo); /** * Enables or disables trusted MitM processing. * * For further details of trusted MitM processing refer to ZRTP * specification, chapter 7.3 * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param yesNo * If set to true then trusted MitM processing is enabled. */ void zrtp_setTrustedMitM(ZrtpContext* zrtpContext, int32_t yesNo); /** * Check status of trusted MitM processing. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @return * Returns true if trusted MitM processing is enabled. */ int32_t zrtp_isTrustedMitM(ZrtpContext* zrtpContext); /** * Enables or disables SAS signature processing. * * For further details of trusted MitM processing refer to ZRTP * specification, chapter 7.2 * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @param yesNo * If true then certificate processing is enabled. */ void zrtp_setSasSignature(ZrtpContext* zrtpContext, int32_t yesNo); /** * Check status of SAS signature processing. * * @param zrtpContext * Pointer to the opaque ZrtpContext structure. * @return * Returns true if certificate processing is enabled. */ int32_t zrtp_isSasSignature(ZrtpContext* zrtpContext); #ifdef __cplusplus } #ifdef __GNUC__ #pragma GCC visibility pop #endif #endif /** * @} */ #endif ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpCallback.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPCALLBACK_H_ #define _ZRTPCALLBACK_H_ /** * @file ZrtpCallback.h * @brief Callback interface between ZRTP and the RTP stack implementation * @ingroup GNU_ZRTP * @{ */ #include #include #include #include /** * This enum defines which role a ZRTP peer has. * * According to the ZRTP specification the role determines which keys to * use to encrypt or decrypt SRTP data. * *
    *
  • The Initiator encrypts SRTP data using the keyInitiator and the * saltInitiator data, the Responder uses these data to decrypt. *
  • *
  • The Responder encrypts SRTP data using the keyResponder and the * saltResponder data, the Initiator uses these data to decrypt. *
  • *
*/ typedef enum { NoRole = 0, ///< ZRTP role not yet set Responder = 1, ///< This client is in ZRTP Responder mode Initiator ///< This client is in ZRTP Initiator mode } Role; /// The algorihms that we support in SRTP and that ZRTP can negotiate. typedef enum { None, Aes = 1, ///< Use AES as symmetrical cipher algorithm TwoFish, ///< Use TwoFish as symmetrical cipher algorithm Sha1, ///< Use Sha1 as authentication algorithm Skein ///< Use Skein as authentication algorithm } SrtpAlgorithms; /** * This structure contains pointers to the SRTP secrets and the role info. * * About the role and what the meaning of the role is refer to the * of the enum Role. The pointers to the secrets are valid as long as * the ZRtp object is active. To use these data after the ZRtp object's * lifetime you may copy the data into a save place. The destructor * of ZRtp clears the data. */ typedef struct srtpSecrets { SrtpAlgorithms symEncAlgorithm; ///< symmetrical cipher algorithm const uint8_t* keyInitiator; ///< Initiator's key int32_t initKeyLen; ///< Initiator's key length const uint8_t* saltInitiator; ///< Initiator's salt int32_t initSaltLen; ///< Initiator's salt length const uint8_t* keyResponder; ///< Responder's key int32_t respKeyLen; ///< Responder's key length const uint8_t* saltResponder; ///< Responder's salt int32_t respSaltLen; ///< Responder's salt length SrtpAlgorithms authAlgorithm; ///< SRTP authentication algorithm int32_t srtpAuthTagLen; ///< SRTP authentication length std::string sas; ///< The SAS string Role role; ///< ZRTP role of this client } SrtpSecret_t; enum EnableSecurity { ForReceiver = 1, ///< Enable security for SRTP receiver ForSender = 2 ///< Enable security for SRTP sender }; /** * This abstract class defines the callback functions required by GNU ZRTP. * * This class is a pure abstract class, aka Interface in Java, that * defines the callback interface that the specific part of a GNU ZRTP * must implement. The generic part of GNU ZRTP uses these mehtods * to communicate with the specific part, for example to send data * via the RTP/SRTP stack, to set timers and cancel timer and so on. * * The generiy part of GNU ZRTP needs only a few callback methods to * be implemented by the specific part. * * @author Werner Dittmann */ class __EXPORT ZrtpCallback { public: virtual ~ZrtpCallback() {}; protected: friend class ZRtp; /** * Send a ZRTP packet via RTP. * * ZRTP calls this method to send a ZRTP packet via the RTP session. * * @param data * Points to ZRTP packet to send. The packet already contains a 4 bytes * storage at the end to store CRC. * @param length * The length in bytes of the data, including the CRC storage. * @return * zero if sending failed, one if packet was send */ virtual int32_t sendDataZRTP(const uint8_t* data, int32_t length) =0; /** * Activate timer. * * @param time * The time in ms for the timer * @return * zero if activation failed, one if timer was activated */ virtual int32_t activateTimer(int32_t time) =0; /** * Cancel the active timer. * * @return * zero if cancel action failed, one if timer was canceled */ virtual int32_t cancelTimer() =0; /** * Send information messages to the hosting environment. * * The ZRTP implementation uses this method to send information * messages to the host. Along with the message ZRTP provides a * severity indicator that defines: Info, Warning, Error, * Alert. Refer to the MessageSeverity enum above. * * @param severity * This defines the message's severity * @param subCode * The subcode identifying the reason. * @see ZrtpCodes#MessageSeverity */ virtual void sendInfo(GnuZrtpCodes::MessageSeverity severity, int32_t subCode) =0; /** * SRTP crypto data ready for the sender or receiver. * * The ZRTP implementation calls this method right after all SRTP * secrets are computed and ready to be used. The parameter points * to a structure that contains pointers to the SRTP secrets and a * enum Role. The called method (the implementation * of this abstract method) must either copy the pointers to the SRTP * data or the SRTP data itself to a save place. The SrtpSecret_t * structure is destroyed after the callback method returns to the * ZRTP implementation. * * The SRTP data themselfs are ontained in the ZRtp object and are * valid as long as the ZRtp object is active. TheZRtp's * destructor clears the secrets. Thus the called method needs to * save the pointers only, ZRtp takes care of the data. * * The implementing class may enable SRTP processing in this * method or delay it to srtpSecertsOn(). * * @param secrets A pointer to a SrtpSecret_t structure that * contains all necessary data. * * @param part for which part (Sender or Receiver) this data is * valid. * * @return Returns false if something went wrong during * initialization of SRTP context, for example memory shortage. */ virtual bool srtpSecretsReady(SrtpSecret_t* secrets, EnableSecurity part) =0; /** * Switch off the security for the defined part. * * @param part Defines for which part (sender or receiver) to * switch on security */ virtual void srtpSecretsOff(EnableSecurity part) =0; /** * Switch on the security. * * ZRTP calls this method after it has computed the SAS and check * if it is verified or not. In addition ZRTP provides information * about the cipher algorithm and key length for the SRTP session. * * This method must enable SRTP processing if it was not enabled * during sertSecretsReady(). * * @param c The name of the used cipher algorithm and mode, or * NULL * * @param s The SAS string * * @param verified if verified is true then SAS was * verified by both parties during a previous call. */ virtual void srtpSecretsOn(std::string c, std::string s, bool verified) =0; /** * This method handles GoClear requests. * * According to the ZRTP specification the user must be informed about * a GoClear request because the ZRTP implementation switches off security * if it could authenticate the GoClear packet. * * Note: GoClear is not yet implemented in GNU ZRTP. * */ virtual void handleGoClear() =0; /** * Handle ZRTP negotiation failed. * * ZRTP calls this method in case ZRTP negotiation failed. The * parameters show the severity as well as the reason. * * @param severity * This defines the message's severity * @param subCode * The subcode identifying the reason. * @see ZrtpCodes#MessageSeverity */ virtual void zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity, int32_t subCode) =0; /** * ZRTP calls this method if the other side does not support ZRTP. * * If the other side does not answer the ZRTP Hello packets then * ZRTP calls this method, * */ virtual void zrtpNotSuppOther() =0; /** * Enter synchronization mutex. * * GNU ZRTP requires one mutes to synchronize its * processing. Because mutex implementations depend on the * underlying infrastructure, for example operating system or * thread implementation, GNU ZRTP delegates mutex handling to the * spcific part of its implementation. */ virtual void synchEnter() =0; /** * Leave synchronization mutex. */ virtual void synchLeave() =0; /** * Inform about a PBX enrollment request. * * Please refer to chapter 8.3 ff to get more details about PBX * enrollment and SAS relay. * * Note: PBX enrollement is not yet fully supported by GNU * ZRTP. * * @param info Give some information to the user about the PBX * requesting an enrollment. */ virtual void zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info) =0; /** * Inform about PBX enrollment result. * * Informs the use about the acceptance or denial of an PBX enrollment * request * * Note: PBX enrollement is not yet fully supported by GNU * ZRTP. * * @param info information to the user about the result * of an enrollment. */ virtual void zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info) =0; /** * Request a SAS signature. * * After ZRTP was able to compute the Short Authentication String * (SAS) it calls this method. The client may now use an * approriate method to sign the SAS. The client may use * ZrtpQueue#setSignatureData() to store the signature data an * enable signature transmission to the other peer. Refer to * chapter 8.2 of ZRTP specification. * * Note: SAS signing is not yet fully supported by GNU * ZRTP. * * @param sasHash * The SAS hash to sign. * */ virtual void signSAS(uint8_t* sasHash) =0; /** * ZRTPQueue calls this method to request a SAS signature check. * * After ZRTP received a SAS signature in one of the Confirm packets it * call this method. The client may use getSignatureLength() * and getSignatureData()of ZrtpQueue to get the signature * data and perform the signature check. Refer to chapter 8.2 of ZRTP * specification. * * If the signature check fails the client may return false to ZRTP. In * this case ZRTP signals an error to the other peer and terminates * the ZRTP handshake. * * Note: SAS signing is not yet fully supported by GNU * ZRTP. * * @param sasHash * The SAS hash that was signed by the other peer. * @return * true if the signature was ok, false otherwise. * */ virtual bool checkSASSignature(uint8_t* sasHash) =0; }; #endif // ZRTPCALLBACK /** * @} */ /** EMACS ** * Local variables: * mode: c++ * c-default-style: ellemtel * c-basic-offset: 4 * End: */ ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpCallbackWrapper.h ================================================ /* This class maps the ZRTP C++ callback methods to C callback methods. Copyright (C) 2010-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ZRTPCALLBACKWRAPPER_H #define ZRTPCALLBACKWRAPPER_H #include #include #include #include /** * * @file ZrtpCallbackWrapper.h * @brief C-Wrapper helper * * This is a helper class for for the C wrapper and implements * the GNU ZRTP callback interface. For detailed documentation about * the callback method refer to file ZrtpCallback * @ingroup GNU_ZRTP * @{ * * @see ZrtpCallback * @see ZrtpCWrapper */ class __EXPORT ZrtpCallbackWrapper : public ZrtpCallback { public: /** * Construct a class that implements ZrtpCallback and uses a C structure * to call C functions that implement the callbacks. * * @param cb * The C callback structure that hold the addresses of the C methods * that implement the actual callback functions. * @param ctx * Pointer to the ZrtpContext */ ZrtpCallbackWrapper(zrtp_Callbacks* cb, ZrtpContext* ctx); int32_t sendDataZRTP ( const unsigned char* data, int32_t length ); int32_t activateTimer ( int32_t time ); int32_t cancelTimer(); void sendInfo ( GnuZrtpCodes::MessageSeverity severity, int32_t subCode ); bool srtpSecretsReady ( SrtpSecret_t* secrets, EnableSecurity part ); void srtpSecretsOff ( EnableSecurity part ); void srtpSecretsOn ( std::string c, std::string s, bool verified ); void handleGoClear(); void zrtpNegotiationFailed ( GnuZrtpCodes::MessageSeverity severity, int32_t subCode ); void zrtpNotSuppOther(); void synchEnter(); void synchLeave(); void zrtpAskEnrollment (GnuZrtpCodes::InfoEnrollment info ); void zrtpInformEnrollment (GnuZrtpCodes::InfoEnrollment info ); void signSAS (uint8_t* sasHash ); bool checkSASSignature (uint8_t* sasHash ); private: void init(); zrtp_Callbacks *c_callbacks; ZrtpContext* zrtpCtx; }; /** * @} */ #endif // ZRTPCALLBACKWRAPPER_H ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpCodes.h ================================================ /** @file ZrtpCodes.h */ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the Lesser GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPCODES_H_ #define _ZRTPCODES_H_ /** * @file ZrtpCodes.h * @brief The ZRTP info, warning, error codes, and other contants and enums that applications may use. * @ingroup GNU_ZRTP * @{ */ namespace GnuZrtpCodes { /** * \namespace GnuZrtpCodes * * This enum defines the information message severity. * * The ZRTP implementation issues information messages to inform the user * about ongoing processing, unusual behavior, or alerts in case of severe * problems. Each main severity code a number of sub-codes exist that * specify the exact nature of the problem. * * An application gets message severity codes and the associated sub-codes * via the ZrtpUserCallback#showMessage method. * * The severity levels and their meaning are: * *
*
Info
keeps the user informed about ongoing processing and * security setup. The enumeration InfoCodes defines the subcodes. *
*
Warning
is an information about some security issues, e.g. if * an AES 256 encryption is request but only DH 3072 as public key scheme * is supported. ZRTP will establish a secure session (SRTP). The * enumeration WarningCodes defines the sub-codes. *
*
Severe
is used if an error occured during ZRTP protocol usage. * In case of Severe ZRTP will not establish a secure session. * The enumeration SevereCodes defines the sub-codes. *
*
Zrtp
shows a ZRTP security problem. Refer to the enumeration * ZrtpErrorCodes for sub-codes. GNU ZRTP of course will not * establish a secure session. *
*
* */ enum MessageSeverity { Info = 1, Warning, Severe, ZrtpError }; /** * Sub-codes for Info */ enum InfoCodes { InfoHelloReceived = 1, //!< Hello received and prepared a Commit, ready to get peer's hello hash InfoCommitDHGenerated, //!< Commit: Generated a public DH key InfoRespCommitReceived, //!< Responder: Commit received, preparing DHPart1 InfoDH1DHGenerated, //!< DH1Part: Generated a public DH key InfoInitDH1Received, //!< Initiator: DHPart1 received, preparing DHPart2 InfoRespDH2Received, //!< Responder: DHPart2 received, preparing Confirm1 InfoInitConf1Received, //!< Initiator: Confirm1 received, preparing Confirm2 InfoRespConf2Received, //!< Responder: Confirm2 received, preparing Conf2Ack InfoRSMatchFound, //!< At least one retained secrets matches - security OK InfoSecureStateOn, //!< Entered secure state InfoSecureStateOff //!< No more security for this session }; /** * Sub-codes for Warning */ enum WarningCodes { WarningDHAESmismatch = 1, //!< Commit contains an AES256 cipher but does not offer a Diffie-Helman 4096 - not used DH4096 was discarded WarningGoClearReceived, //!< Received a GoClear message WarningDHShort, //!< Hello offers an AES256 cipher but does not offer a Diffie-Helman 4096- not used DH4096 was discarded WarningNoRSMatch, //!< No retained shared secrets available - must verify SAS WarningCRCmismatch, //!< Internal ZRTP packet checksum mismatch - packet dropped WarningSRTPauthError, //!< Dropping packet because SRTP authentication failed! WarningSRTPreplayError, //!< Dropping packet because SRTP replay check failed! WarningNoExpectedRSMatch, //!< Valid retained shared secrets availabe but no matches found - must verify SAS WarningNoExpectedAuxMatch //!< Our AUX secret was set but the other peer's AUX secret does not match ours }; /** * Sub-codes for Severe */ enum SevereCodes { SevereHelloHMACFailed = 1, //!< Hash HMAC check of Hello failed! SevereCommitHMACFailed, //!< Hash HMAC check of Commit failed! SevereDH1HMACFailed, //!< Hash HMAC check of DHPart1 failed! SevereDH2HMACFailed, //!< Hash HMAC check of DHPart2 failed! SevereCannotSend, //!< Cannot send data - connection or peer down? SevereProtocolError, //!< Internal protocol error occured! SevereNoTimer, //!< Cannot start a timer - internal resources exhausted? SevereTooMuchRetries //!< Too much retries during ZRTP negotiation - connection or peer down? }; /** * Error codes according to the ZRTP specification chapter 6.9 * * GNU ZRTP uses these error codes in two ways: to fill the appropriate * field ing the ZRTP Error packet and as sub-code in * ZrtpUserCallback#showMessage(). GNU ZRTP uses thes error codes also * to report received Error packts, in this case the sub-codes are their * negative values. * * The enumeration member comments are copied from the ZRTP specification. */ enum ZrtpErrorCodes { MalformedPacket = 0x10, //!< Malformed packet (CRC OK, but wrong structure) CriticalSWError = 0x20, //!< Critical software error UnsuppZRTPVersion = 0x30, //!< Unsupported ZRTP version HelloCompMismatch = 0x40, //!< Hello components mismatch UnsuppHashType = 0x51, //!< Hash type not supported UnsuppCiphertype = 0x52, //!< Cipher type not supported UnsuppPKExchange = 0x53, //!< Public key exchange not supported UnsuppSRTPAuthTag = 0x54, //!< SRTP auth. tag not supported UnsuppSASScheme = 0x55, //!< SAS scheme not supported NoSharedSecret = 0x56, //!< No shared secret available, DH mode required DHErrorWrongPV = 0x61, //!< DH Error: bad pvi or pvr ( == 1, 0, or p-1) DHErrorWrongHVI = 0x62, //!< DH Error: hvi != hashed data SASuntrustedMiTM = 0x63, //!< Received relayed SAS from untrusted MiTM ConfirmHMACWrong = 0x70, //!< Auth. Error: Bad Confirm pkt HMAC NonceReused = 0x80, //!< Nonce reuse EqualZIDHello = 0x90, //!< Equal ZIDs in Hello GoCleatNotAllowed = 0x100, //!< GoClear packet received, but not allowed IgnorePacket = 0x7fffffff }; /** * Information codes for the Enrollment user callbacks. */ enum InfoEnrollment { EnrollmentRequest = 0, //!< Aks user to confirm or deny an Enrollemnt request EnrollmentReconfirm, //!< User already enrolled, ask re-confirmation EnrollmentCanceled, //!< User did not confirm the PBX enrollement EnrollmentFailed, //!< Enrollment process failed, no PBX secret available EnrollmentOk //!< Enrollment process for this PBX was ok }; /** * Offsets into the ZRTP counter array. * */ //!< How many Hello packet retries in detect state #define HelloRetry 0 //!< How many Hello packet retries in Ack sent state #define HelloRetryAck 1 //!< How many Commit packet retries #define CommitRetry 2 //!< How many DhPart2 packet retries #define DhPart2Retry 3 //!< How many Confirm2 packet retries #define Confirm2Retry 4 //!< How many Error packet retries #define ErrorRetry 5 } /** * @brief Codes and structure for SRTP error trace data */ #define RTP_HEADER_LENGTH 12 typedef enum { DecodeError = 1, ReplayError = 2, AuthError = 3 } SrtpErrorType; /** * @brief Trace data of SRTP packet in case of unprotect error. */ typedef struct _SrtpErrorData { SrtpErrorType errorType; uint32_t rtpHeader[RTP_HEADER_LENGTH / sizeof(uint32_t)]; size_t length; uint64_t guessedIndex; } SrtpErrorData; /** * @} */ #endif ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpConfigure.h ================================================ /* Copyright (C) 2009 - 2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* * Authors: Werner Dittmann */ #ifndef _ZRTPCONFIGURE_H_ #define _ZRTPCONFIGURE_H_ /** * @file ZrtpConfigure.h * @brief The ZRTP configure functions * @ingroup GNU_ZRTP * @{ */ #include #include #include #include #include #include #include /** * This enumerations list all configurable algorithm types. */ enum AlgoTypes { Invalid = 0, HashAlgorithm = 1, CipherAlgorithm, PubKeyAlgorithm, SasType, AuthLength }; typedef void(*encrypt_t)(uint8_t*, int32_t, uint8_t*, uint8_t*, int32_t); typedef void(*decrypt_t)(uint8_t*, int32_t, uint8_t*, uint8_t*, int32_t); /** * The algorithm enumration class. * * This simple class is just a container of an algorithm's name and * its associated algorithm type. We use this class together with the * EnumBase class to implement a Java-like enum class functionality * (not fully, but OK for our use case). * * An application shall use the get / check methods to retrieve information. */ class AlgorithmEnum { public: /** * Create an AlgorithmEnum object. * * @param type * Defines the algorithm type * @param name * Set the names of the algorithm. The name is copied * and the call may reuse the space. * @param klen * The key length for this algorihm in byte, for example 16 or 32 * @param ra * A human readable short string that describes the algorihm. * @param en * Pointer to the encryption function of this algorithn * @param de * Pointer to the decryption funtions of this algorithm. * @param alId * The algorithm id used by SRTP to identify an algorithm type, for * example Skein, Sha1, Aes, ... * * @see AlgoTypes */ AlgorithmEnum(const AlgoTypes type, const char* name, int32_t klen, const char* ra, encrypt_t en, decrypt_t de, SrtpAlgorithms alId); /** * AlgorithmEnum destructor */ ~AlgorithmEnum(); /** * Get the algorihm's name * * @returns * Algorithm's name as null terminated C-string. The * application must not free this memory. */ const char* getName(); /** * Get the algorihm's readable name * * @returns * Algorithm's readable name as null terminated C-string. The * application must not free this memory. */ const char* getReadable(); /** * Get the algorihm's key length. * * @returns * An integer definig the key length in bytes. */ int getKeylen(); /** * Get the algorihm's integer id. * * @returns * An integer that defines the algorithm. */ SrtpAlgorithms getAlgoId(); /** * Get the algorihm's key length. * * @returns * An integer definig the key length in bytes. */ encrypt_t getEncrypt(); /** * Get the algorihm's key length. * * @returns * An integer definig the key length in bytes. */ decrypt_t getDecrypt(); /** * Get the algorithm type of this AlgorithmEnum object. * * @returns * The algorithm type. * * @see AlgoTypes */ AlgoTypes getAlgoType(); /** * Check if this AlgorithmEnum object is valid * * @returns * @c true if the object is valid, @c false otherwise */ bool isValid(); private: AlgoTypes algoType; std::string algoName; int32_t keyLen; std::string readable; encrypt_t encrypt; decrypt_t decrypt; SrtpAlgorithms algoId; }; /** * EnumBase provides methods to store and access algorithm enumerations of * a specific algorithm type. * * An application shall use the get / check methods to retrieve information * from the preset Algorithm Enumerations. * * @see AlgoTypes * @see zrtpHashes * @see zrtpSymCiphers * @see zrtpPubKeys * @see zrtpSasTypes * @see zrtpAuthLengths */ class __EXPORT EnumBase { public: /** * Get an AlgorithmEnum by its name * * @param name * The name of the AlgorithmEnum to search. * @returns * The AlgorithmEnum if found or an invalid AlgorithmEnum if the name * was not found */ AlgorithmEnum& getByName(const char* name); /** * Return all names of all currently stored AlgorithmEnums * * @return * A C++ std::list of C++ std::strings that contain the names. */ std::list* getAllNames(); /** * Get the number of currently stored AlgorithmEnums * * @return * The number of currently stored AlgorithmEnums */ int getSize(); /** * Get the AlgoTypes to which this EnumBase belongs. * * @return * The AlgoTypes of this EnumBase. * @see AlgoTypes. */ AlgoTypes getAlgoType(); /** * Return the AlgorithmEnum by its ordinal number * * @param ord * The ordinal number of the AlgorithmEnum. * @return * The AlgorithmEnum if found, an invalid Algorithm otherwise. */ AlgorithmEnum& getByOrdinal(int ord); /** * Get the ordinal number of an AlgorithmEnum * * @param algo * Return toe ordinal numer of this AlgorithmEnum. * * @return * Return the ordinal number of this AlgorithmEnum if found, * -1 otherwise. */ int getOrdinal(AlgorithmEnum& algo); protected: EnumBase(AlgoTypes algo); ~EnumBase(); void insert(const char* name); void insert(const char* name, int32_t klen, const char* ra, encrypt_t en, decrypt_t de, SrtpAlgorithms alId); private: AlgoTypes algoType; std::vector algos; }; /** * The enumaration subclasses that contain the supported algorithm enumerations. */ class __EXPORT HashEnum : public EnumBase { public: HashEnum(); ~HashEnum(); }; class __EXPORT SymCipherEnum : public EnumBase { public: SymCipherEnum(); ~SymCipherEnum(); }; class __EXPORT PubKeyEnum : public EnumBase { public: PubKeyEnum(); ~PubKeyEnum(); }; class __EXPORT SasTypeEnum : public EnumBase { public: SasTypeEnum(); ~SasTypeEnum(); }; class __EXPORT AuthLengthEnum : public EnumBase { public: AuthLengthEnum(); ~AuthLengthEnum(); }; extern __EXPORT HashEnum zrtpHashes; extern __EXPORT SymCipherEnum zrtpSymCiphers; extern __EXPORT PubKeyEnum zrtpPubKeys; extern __EXPORT SasTypeEnum zrtpSasTypes; extern __EXPORT AuthLengthEnum zrtpAuthLengths; /** * ZRTP configuration data. * * This class contains data and functions to set ZRTP configuration data. * An application may use this class to set configuration information for * ZRTP. ZRTP uses this configuration information to announce various * algorithms via its Hello message. An application may use this class to * restrict or allow use of algorithms. * * The constructor does not set any algorithms, thus it is an empty * configuration. An application may use this empty configuration and * hand it over to ZRTP. In this case ZRTP does not announce any algorithms * in its Hello message and uses mandatory algorithms only. * * An application can configure implemented algorithms only. */ class __EXPORT ZrtpConfigure { public: ZrtpConfigure(); /* Creates Configuration data */ ~ZrtpConfigure(); /** * Define the algorithm selection policies. */ typedef enum _policies { Standard = 1, PreferNonNist = 2 } Policy; /** * Set the maximum number of algorithms per algorithm type that an application can * configure. */ static const int maxNoOfAlgos = 7; /** * Convenience function that sets a pre-defined standard configuration. * * The standard configuration consists of the following algorithms: *
    *
  • Hash: SHA256
  • *
  • Symmetric Cipher: AES 128, AES 256
  • *
  • Public Key Algorithm: DH2048, DH3027, MultiStream
  • *
  • SAS type: libase 32
  • *
  • SRTP Authentication lengths: 32, 80
  • *
*/ void setStandardConfig(); /** * Convenience function that sets the mandatory algorithms only. * * Mandatory algorithms are: *
    *
  • Hash: SHA256
  • *
  • Symmetric Cipher: AES 128
  • *
  • Public Key Algorithm: DH3027, MultiStream
  • *
  • SAS type: libase 32
  • *
  • SRTP Authentication lengths: 32, 80
  • *
*/ void setMandatoryOnly(); /** * Clear all configuration data. * * The functions clears all configuration data. */ void clear(); /** * Add an algorithm to configuration data. * * Adds the specified algorithm to the configuration data. * If no free configuration data slot is available the * function does not add the algorithm and returns -1. The * methods appends the algorithm to the existing algorithms. * * @param algoType * Specifies which algorithm type to select * @param algo * The enumeration of the algorithm to add. * @return * Number of free configuration data slots or -1 on error */ int32_t addAlgo(AlgoTypes algoType, AlgorithmEnum& algo); /** * Add an algorithm to configuration data at given index. * * Adds the specified algorithm to the configuration data vector * at a given index. If the index is larger than the actual size * of the configuration vector the method just appends the algorithm. * * @param algoType * Specifies which algorithm type to select * @param algo * The enumeration of the algorithm to add. * @param index * The index where to add the algorihm * @return * Number of free configuration data slots or -1 on error */ int32_t addAlgoAt(AlgoTypes algoType, AlgorithmEnum& algo, int32_t index); /** * Remove a algorithm from configuration data. * * Removes the specified algorithm from configuration data. If * the algorithm was not configured previously the function does * not modify the configuration data and returns the number of * free configuration data slots. * * If an application removes all algorithms then ZRTP does not * include any algorithm into the hello message and falls back * to a predefined mandatory algorithm. * * @param algoType * Specifies which algorithm type to select * @param algo * The enumeration of the algorithm to remove. * @return * Number of free configuration slots. */ int32_t removeAlgo(AlgoTypes algoType, AlgorithmEnum& algo); /** * Returns the number of configured algorithms. * * @param algoType * Specifies which algorithm type to select * @return * The number of configured algorithms (used configuration * data slots) */ int32_t getNumConfiguredAlgos(AlgoTypes algoType); /** * Returns the identifier of the algorithm at index. * * @param algoType * Specifies which algorithm type to select * @param index * The index in the list of the algorihm type * @return * A pointer the the algorithm enumeration. If the index * does not point to a configured slot then the function * returns NULL. * */ AlgorithmEnum& getAlgoAt(AlgoTypes algoType, int32_t index); /** * Checks if the configuration data of the algorihm type already contains * a specific algorithms. * * @param algoType * Specifies which algorithm type to select * @param algo * The algorithm to check * @return * True if the algorithm was found, false otherwise. * */ bool containsAlgo(AlgoTypes algoType, AlgorithmEnum& algo); /** * Enables or disables trusted MitM processing. * * For further details of trusted MitM processing refer to ZRTP * specification, chapter 7.3 * * @param yesNo * If set to true then trusted MitM processing is enabled. */ void setTrustedMitM(bool yesNo); /** * Check status of trusted MitM processing. * * @return * Returns true if trusted MitM processing is enabled. */ bool isTrustedMitM(); /** * Enables or disables SAS signature processing. * * For further details of trusted MitM processing refer to ZRTP * specification, chapter 7.2 * * @param yesNo * If set to true then certificate processing is enabled. */ void setSasSignature(bool yesNo); /** * Check status of SAS signature processing. * * @return * Returns true if certificate processing is enabled. */ bool isSasSignature(); /** * Enables or disables paranoid mode. * * For further explanation of paranoid mode refer to the documentation * of ZRtp class. * * @param yesNo * If set to true then paranoid mode is enabled. */ void setParanoidMode(bool yesNo); /** * Check status of paranoid mode. * * @return * Returns true if paranoid mode is enabled. */ bool isParanoidMode(); /// Helper function to print some internal data void printConfiguredAlgos(AlgoTypes algoTyp); Policy getSelectionPolicy() {return selectionPolicy;} void setSelectionPolicy(Policy pol) {selectionPolicy = pol;} private: std::vector hashes; std::vector symCiphers; std::vector publicKeyAlgos; std::vector sasTypes; std::vector authLengths; bool enableTrustedMitM; bool enableSasSignature; bool enableParanoidMode; AlgorithmEnum& getAlgoAt(std::vector& a, int32_t index); int32_t addAlgo(std::vector& a, AlgorithmEnum& algo); int32_t addAlgoAt(std::vector& a, AlgorithmEnum& algo, int32_t index); int32_t removeAlgo(std::vector& a, AlgorithmEnum& algo); int32_t getNumConfiguredAlgos(std::vector& a); bool containsAlgo(std::vector& a, AlgorithmEnum& algo); std::vector& getEnum(AlgoTypes algoType); void printConfiguredAlgos(std::vector& a); Policy selectionPolicy; protected: public: }; /** * @} */ #endif /** EMACS ** * Local variables: * mode: c++ * c-default-style: ellemtel * c-basic-offset: 4 * End: */ ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpCrc32.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPCRC32_H_ #define _ZRTPCRC32_H_ /** * * @file ZrtpCrc32.h * @brief Methods to compute the CRC32 checksum for ZRTP packets * * @ingroup GNU_ZRTP * @{ * * @see ZrtpCallback */ /** * Check if a buffer matches a given CRC32 checksum. * * @param buffer * Pointer to the data buffer. * @param length * Length in bytes of the data buffer. * @param crc32 * The CRC32 checksum. * * @return * @c true if the CRC32 checksum matches the computed checksum of the * buffer, @c false otherwise. */ bool zrtpCheckCksum(uint8_t *buffer, uint16_t length, uint32_t crc32); /** * Generate a CRC32 checksum of a data buffer * * @param buffer * Pointer to the buffer. * @param length * Lenght of the buffer in bytes. * * @return * A preliminary CRC32 checksum */ uint32_t zrtpGenerateCksum(uint8_t *buffer, uint16_t length); /** * Close CRC32 computation. * * @param crc32 * A preliminary CRC32 checksum. * * @return * The ready to use CRC32 checksum in host order. */ uint32_t zrtpEndCksum(uint32_t crc32); /** * @} */ #endif ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpPacketBase.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* * Authors: Werner Dittmann */ #ifndef _ZRTPPACKETBASE_H_ #define _ZRTPPACKETBASE_H_ /** * @file ZrtpPacketBase.h * @brief The ZRTP message header class * * This class defines the ZRTP message header and provides access and * check methods. * * @ingroup GNU_ZRTP * @{ */ #include #include #include #include #include #include #include #include #include // #define DEBUGOUT(deb) deb #define DEBUGOUT(deb) /* * This is the unique ZRTP ID in network order (PZ) */ const uint16_t zrtpId = 0x505a; /** * This is the base class for all ZRTP packets * * All other ZRTP packet classes inherit from this class. It does not have * an implementation of its own. * * The standard constructors of the subclasses usually initialize the @c allocate * field with their fixed data array which is large enough to hold all message * data. If an implementation needs to change this to use dynamic memory * allocation only that line in the subclasses must be changed and the destructors * should take care of memory management. * * @author Werner Dittmann */ class __EXPORT ZrtpPacketBase { private: protected: void* allocated; ///< Pointer to ZRTP message data zrtpPacketHeader_t* zrtpHeader; ///< points to the fixed ZRTP header structure public: /** * Destructor is empty */ virtual ~ZrtpPacketBase() {}; /** * Get pointer to ZRTP header * * @return * Pointer to ZRTP header structure. */ const uint8_t* getHeaderBase() { return (const uint8_t*)zrtpHeader; }; /** * Check is this is a ZRTP message * * @return * @c true if check was ok */ bool isZrtpPacket() { return (zrtpNtohs(zrtpHeader->zrtpId) == zrtpId); }; /** * Get the length in words of the ZRTP message * * @return * The length in words */ uint16_t getLength() { return zrtpNtohs(zrtpHeader->length); }; /** * Return pointer to fixed length message type ASCII data * * @return * Pointer to ASCII character array */ uint8_t* getMessageType() { return zrtpHeader->messageType; }; /** * Set the lenght field in the ZRTP header * * @param len * The length of the ZRTP message in words, host order */ void setLength(uint16_t len) { zrtpHeader->length = zrtpHtons(len); }; /** * Copy the message type ASCII data to ZRTP message type field * * @param msg * Pointer to message type ASCII character array */ void setMessageType(uint8_t *msg) { memcpy(zrtpHeader->messageType, msg, sizeof(zrtpHeader->messageType)); }; /** * Initializes the ZRTP Id field */ void setZrtpId() { zrtpHeader->zrtpId = zrtpHtons(zrtpId); } }; /** * @} */ #endif // ZRTPPACKETBASE ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpPacketClearAck.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPPACKETCLEARACK_H_ #define _ZRTPPACKETCLEARACK_H_ /** * @file ZrtpPacketClearAck.h * @brief The ZRTP ClearAck message * * @ingroup GNU_ZRTP * @{ */ #include /** * Implement the ClearAck packet - Currently not used * * The ZRTP simple message ClearAck. The implementation sends this * after switching to clear mode (non-SRTP mode). * * @author Werner Dittmann */ class __EXPORT ZrtpPacketClearAck : public ZrtpPacketBase { public: ZrtpPacketClearAck(); /// Creates a ClearAck packet with default data ZrtpPacketClearAck(uint8_t* data); /// Creates a ClearAck packet from received data virtual ~ZrtpPacketClearAck(); private: ClearAckPacket_t data; }; /** * @} */ #endif // ZRTPPACKETCLEARACK ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpPacketCommit.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* * Authors: Werner Dittmann */ #ifndef _ZRTPPACKETCOMMIT_H_ #define _ZRTPPACKETCOMMIT_H_ /** * @file ZrtpPacketCommit.h * @brief The ZRTP Commit message * * @ingroup GNU_ZRTP * @{ */ #include // PRSH here only for completeness. We don't support PRSH in the other ZRTP parts. #define COMMIT_DH_EX 29 #define COMMIT_MULTI 25 #define COMMIT_PRSH 27 /** * Implement the Commit packet. * * The ZRTP message Commit. The ZRTP implementation sends or receives * this message to commit the crypto parameters offered during a Hello * message. * * * @author Werner Dittmann */ class __EXPORT ZrtpPacketCommit : public ZrtpPacketBase { protected: Commit_t* commitHeader; ///< Points to Commit message part public: typedef enum _commitType { DhExchange = 1, MultiStream = 2 } commitType; /// Creates a Commit packet with default data ZrtpPacketCommit(); /// Creates a Commit packet from received data ZrtpPacketCommit(uint8_t* data); /// Normal destructor virtual ~ZrtpPacketCommit(); /// Get pointer to hash algorithm type field, a fixed length character array uint8_t* getHashType() { return commitHeader->hash; }; /// Get pointer to cipher algorithm type field, a fixed length character array uint8_t* getCipherType() { return commitHeader->cipher; }; /// Get pointer to SRTP authentication algorithm type field, a fixed length character array uint8_t* getAuthLen() { return commitHeader->authlengths; }; /// Get pointer to key agreement algorithm type field, a fixed length character array uint8_t* getPubKeysType() { return commitHeader->pubkey; }; /// Get pointer to SAS algorithm type field, a fixed length character array uint8_t* getSasType() { return commitHeader->sas; }; /// Get pointer to ZID field, a fixed length byte array uint8_t* getZid() { return commitHeader->zid; }; /// Get pointer to HVI field, a fixed length byte array uint8_t* getHvi() { return commitHeader->hvi; }; /// Get pointer to NONCE field, a fixed length byte array, overlaps HVI field uint8_t* getNonce() { return commitHeader->hvi; }; /// Get pointer to hashH2 field, a fixed length byte array uint8_t* getH2() { return commitHeader->hashH2; }; /// Get pointer to MAC field, a fixed length byte array uint8_t* getHMAC() { return commitHeader->hmac; }; /// Get pointer to MAC field during multi-stream mode, a fixed length byte array uint8_t* getHMACMulti() { return commitHeader->hmac-4*ZRTP_WORD_SIZE; }; /// Check if packet length makes sense. bool isLengthOk(commitType type) {int32_t len = getLength(); return ((type == DhExchange) ? len == COMMIT_DH_EX : len == COMMIT_MULTI);} /// Set hash algorithm type field, fixed length character field void setHashType(uint8_t* text) { memcpy(commitHeader->hash, text, ZRTP_WORD_SIZE); }; /// Set cipher algorithm type field, fixed length character field void setCipherType(uint8_t* text) { memcpy(commitHeader->cipher, text, ZRTP_WORD_SIZE); }; /// Set SRTP authentication algorithm algorithm type field, fixed length character field void setAuthLen(uint8_t* text) { memcpy(commitHeader->authlengths, text, ZRTP_WORD_SIZE); }; /// Set key agreement algorithm type field, fixed length character field void setPubKeyType(uint8_t* text) { memcpy(commitHeader->pubkey, text, ZRTP_WORD_SIZE); }; /// Set SAS algorithm type field, fixed length character field void setSasType(uint8_t* text) { memcpy(commitHeader->sas, text, ZRTP_WORD_SIZE); }; /// Set ZID field, a fixed length byte array void setZid(uint8_t* text) { memcpy(commitHeader->zid, text, sizeof(commitHeader->zid)); }; /// Set HVI field, a fixed length byte array void setHvi(uint8_t* text) { memcpy(commitHeader->hvi, text, sizeof(commitHeader->hvi)); }; /// Set conce field, a fixed length byte array, overlapping HVI field void setNonce(uint8_t* text); /// Set hashH2 field, a fixed length byte array void setH2(uint8_t* hash) { memcpy(commitHeader->hashH2, hash, sizeof(commitHeader->hashH2)); }; /// Set MAC field, a fixed length byte array void setHMAC(uint8_t* hash) { memcpy(commitHeader->hmac, hash, sizeof(commitHeader->hmac)); }; /// Set MAC field during multi-stream mode, a fixed length byte array void setHMACMulti(uint8_t* hash) { memcpy(commitHeader->hmac-4*ZRTP_WORD_SIZE, hash, sizeof(commitHeader->hmac)); }; private: CommitPacket_t data; }; /** * @} */ #endif // ZRTPPACKETCOMMIT ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpPacketConf2Ack.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPPACKETCON2FACK_H_ #define _ZRTPPACKETCON2FACK_H_ /** * @file ZrtpPacketConf2Ack.h * @brief The ZRTP Conf2Ack message * * @ingroup GNU_ZRTP * @{ */ #include /** * Implement the Conf2Ack packet. * * The ZRTP simple message Conf2Ack. The implementation sends this * after receiving and checking the Confirm2 message. * * @author Werner Dittmann */ class __EXPORT ZrtpPacketConf2Ack : public ZrtpPacketBase { public: /// Creates a Conf2Ack packet with default data ZrtpPacketConf2Ack(); ///Creates a Conf2Ack packet from received data ZrtpPacketConf2Ack(char* data); /// Normal destructor virtual ~ZrtpPacketConf2Ack(); private: Conf2AckPacket_t data; }; /** * @} */ #endif // ZRTPPACKETCONF2ACK ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpPacketConfirm.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPPACKETCONFIRM_H_ #define _ZRTPPACKETCONFIRM_H_ /** * @file ZrtpPacketConfirm.h * @brief The ZRTP Confirm message * * @ingroup GNU_ZRTP * @{ */ #include /** * Implement the Confirm packet. * * The ZRTP message Confirm. The implementation sends this * to confirm the switch to SRTP (encrypted) mode. The contents of * the Confirm message are encrypted, thus the implementation * can check if the secret keys work. * * @author Werner Dittmann */ class __EXPORT ZrtpPacketConfirm : public ZrtpPacketBase { private: Confirm_t* confirmHeader; ///< Point to the Confirm message part public: /// Creates a Confirm packet with default data ZrtpPacketConfirm(); /// Creates a Confirm packet with default data and a given signature length ZrtpPacketConfirm(uint32_t sl); /// Creates a Confirm packet from received data ZrtpPacketConfirm(uint8_t* d); /// Normal destructor virtual ~ZrtpPacketConfirm(); /// Check if SAS verify flag is set const bool isSASFlag() { return (confirmHeader->flags & 0x4) == 0x4 ? true : false; } /// Check if PBXEnrollment flag is set const bool isPBXEnrollment() { return (confirmHeader->flags & 0x8) == 0x8 ? true : false; } /// Get pointer to filler bytes (contains one bit of signature length) const uint8_t* getFiller() { return confirmHeader->filler; } /// Get pointer to IV data, fixed byte array const uint8_t* getIv() { return confirmHeader->iv; } /// Get pointer to MAC data, fixed byte array const uint8_t* getHmac() { return confirmHeader->hmac; } /// Get Expiration time data const uint32_t getExpTime() { return zrtpNtohl(confirmHeader->expTime); } /// Get pointer to initial hash chain (H0) data, fixed byte array uint8_t* getHashH0() { return confirmHeader->hashH0; } /// Get pointer to signature data, variable length, refer to getSignatureLength() const uint8_t* getSignatureData() { return ((uint8_t*)&confirmHeader->expTime) + 4; } /// get the signature length in words int32_t getSignatureLength(); /// Check if packet length makes sense. Confirm packets are 19 words at minumum bool isLengthOk() {return (getLength() >= 19); } bool isSignatureLengthOk(); /// set SAS verified flag void setSASFlag() { confirmHeader->flags |= 0x4; } /// set setPBXEnrollment flag void setPBXEnrollment() { confirmHeader->flags |= 0x8; } /// Set MAC data, fixed length byte array void setHmac(uint8_t* text) { memcpy(confirmHeader->hmac, text, sizeof(confirmHeader->hmac)); } /// Set IV data, fixed length byte array void setIv(uint8_t* text) { memcpy(confirmHeader->iv, text, sizeof(confirmHeader->iv)); } /// Set expiration time data void setExpTime(uint32_t t) { confirmHeader->expTime = zrtpHtonl(t); } /// Set initial hash chain (H0) data, fixed length byte array void setHashH0(uint8_t* t) { memcpy(confirmHeader->hashH0, t, sizeof(confirmHeader->hashH0)); } /// Set signature data, length of the signature data in bytes and must be a multiple of 4. bool setSignatureData(uint8_t* data, int32_t length); /// Set signature length in words bool setSignatureLength(uint32_t sl); private: void initialize(); // Confirm packet is of variable length. It maximum size is 524 words: // - 11 words fixed size // - up to 513 words variable part, depending if signature is present and its length. // This leads to a maximum of 4*524=2096 bytes. uint8_t data[2100]; // large enough to hold a full blown Confirm packet }; /** * @} */ #endif // ZRTPPACKETCONFIRM ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpPacketDHPart.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPPACKETDHPART_H_ #define _ZRTPPACKETDHPART_H_ /** * @file ZrtpPacketDHPart.h * @brief The ZRTP DHPart message * * @ingroup GNU_ZRTP * @{ */ #include /** * Implement the DHPart packet. * * The ZRTP message DHPart. The implementation sends this * to exchange the Diffie-Helman public keys and the shared * secrets between the two parties. * * @author Werner Dittmann */ class __EXPORT ZrtpPacketDHPart : public ZrtpPacketBase { protected: uint8_t *pv; ///< points to public key value inside DH message DHPart_t* DHPartHeader; ///< points to DH message structure int32_t dhLength; ///< length of DH message, DH message has variable length public: /// Creates a DHPart packet no data, must use setPubKeyType(...) ZrtpPacketDHPart(); /// Creates a DHPart packet with default data and a give public key type ZrtpPacketDHPart(const char* pkt); /// Creates a DHPart packet from received data ZrtpPacketDHPart(uint8_t* data); /// Standard destructor virtual ~ZrtpPacketDHPart(); /// Get pointer to public key value, variable length byte array uint8_t* getPv() { return pv; } /// Get pointer to first retained secretd id, fixed length byte array uint8_t* getRs1Id() { return DHPartHeader->rs1Id; }; /// Get pointer to second retained secretd id, fixed length byte array uint8_t* getRs2Id() { return DHPartHeader->rs2Id; }; /// Get pointer to additional retained secretd id, fixed length byte array uint8_t* getAuxSecretId() { return DHPartHeader->auxSecretId; }; /// Get pointer to PBX retained secretd id, fixed length byte array uint8_t* getPbxSecretId() { return DHPartHeader->pbxSecretId; }; /// Get pointer to first hash (H1) for hash chain, fixed length byte array uint8_t* getH1() { return DHPartHeader->hashH1; }; /// Get pointer to HMAC, fixed length byte array uint8_t* getHMAC() { return pv+dhLength; }; /// Check if packet length makes sense. DHPart packets are 29 words at minumum, using E255 bool isLengthOk() {return (getLength() >= 29);} /// Setpublic key value, variable length byte array void setPv(uint8_t* text) { memcpy(pv, text, dhLength); }; /// Set first retained secretd id, fixed length byte array void setRs1Id(uint8_t* text) { memcpy(DHPartHeader->rs1Id, text, sizeof(DHPartHeader->rs1Id)); }; /// Set second retained secretd id, fixed length byte array void setRs2Id(uint8_t* text) { memcpy(DHPartHeader->rs2Id, text, sizeof(DHPartHeader->rs2Id)); }; /// Set additional retained secretd id, fixed length byte array void setAuxSecretId(uint8_t* t) { memcpy(DHPartHeader->auxSecretId, t, sizeof(DHPartHeader->auxSecretId)); }; /// Set PBX retained secretd id, fixed length byte array void setPbxSecretId(uint8_t* t) { memcpy(DHPartHeader->pbxSecretId,t, sizeof(DHPartHeader->pbxSecretId)); }; /// Set first hash (H1) of hash chain, fixed length byte array void setH1(uint8_t* t) { memcpy(DHPartHeader->hashH1, t, sizeof(DHPartHeader->hashH1)); }; /// Set key agreement type, fixed size character array void setPubKeyType(const char* pkt); /// Set first MAC, fixed length byte array void setHMAC(uint8_t* t) { memcpy(pv+dhLength, t, 2*ZRTP_WORD_SIZE); }; private: void initialize(); // SupportedPubKeys pktype; // DHPart packet is of variable length. It maximum size is 141 words: // - 13 words fixed sizze // - up to 128 words variable part, depending on DH algorithm // leads to a maximum of 4*141=564 bytes. uint8_t data[768]; // large enough to hold a full blown DHPart packet }; /** * @} */ #endif // ZRTPPACKETDHPART ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpPacketError.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPPACKETERROR_H_ #define _ZRTPPACKETERROR_H_ /** * @file ZrtpPacketError.h * @brief The ZRTP Error message * * @ingroup GNU_ZRTP * @{ */ #include /** * Implement the Error packet. * * The ZRTP simple message Error. The implementation sends this * after detecting an error. * * @author Werner Dittmann */ class __EXPORT ZrtpPacketError : public ZrtpPacketBase { protected: Error_t* errorHeader; ///< Points to Error message public: /// Creates a Error packet with default data ZrtpPacketError(); /// Creates a Error packet from received data ZrtpPacketError(uint8_t* data); virtual ~ZrtpPacketError(); /// Get the error code from Error message uint32_t getErrorCode() { return zrtpNtohl(errorHeader->errorCode); }; /// Set error code in Error message void setErrorCode(uint32_t code) {errorHeader->errorCode = zrtpHtonl(code); }; private: ErrorPacket_t data; }; /** * @} */ #endif // ZRTPPACKETERROR ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpPacketErrorAck.h ================================================ /* Copyright (C) 2007-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPPACKETERRORACK_H_ #define _ZRTPPACKETERRORACK_H_ /** * @file ZrtpPacketErrorAck.h * @brief The ZRTP ErrorAck message * * @ingroup GNU_ZRTP * @{ */ #include /** * Implement the ErrorAck packet. * * The ZRTP simple message ErrorAck. The implementation sends this * after receiving and checking the Error message. * * @author Werner Dittmann */ class __EXPORT ZrtpPacketErrorAck : public ZrtpPacketBase { public: /// Creates a ErrorAck packet with default data ZrtpPacketErrorAck(); /// Creates a ErrorAck packet from received data ZrtpPacketErrorAck(uint8_t* data); virtual ~ZrtpPacketErrorAck(); private: ErrorAckPacket_t data; }; /** * @} */ #endif // _ZRTPPACKETERRORACK_H_ ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpPacketGoClear.h ================================================ /* Copyright (C) 2006-2007 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPPACKETGOCLEAR_H_ #define _ZRTPPACKETGOCLEAR_H_ /** * @file ZrtpPacketGoClear.h * @brief The ZRTP GoClear message * * GNU ZRTP does not implement GoClear feature * @ingroup GNU_ZRTP * @{ */ #include /** * Implement the GoClear packet. * * The ZRTP message GoClear. The implementation sends this * to order the peer to switch to clear mode (non-SRTP mode). * * @author Werner Dittmann */ class __EXPORT ZrtpPacketGoClear : public ZrtpPacketBase { protected: GoClear_t* clearHeader; public: /// Creates a GoCLear packet with default data ZrtpPacketGoClear(); /// Creates a GoClear packet from received data ZrtpPacketGoClear(uint8_t* data); virtual ~ZrtpPacketGoClear(); /// Not used const uint8_t* getClearHmac() { return clearHeader->clearHmac; }; /// Not used void setClearHmac(uint8_t *text) { memcpy(clearHeader->clearHmac, text, 32); }; /// Not used void clrClearHmac() { memset(clearHeader->clearHmac, 0, 32); }; private: GoClearPacket_t data; }; /** * @} */ #endif // ZRTPPACKETGOCLEAR ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpPacketHello.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPPACKETHELLO_H_ #define _ZRTPPACKETHELLO_H_ /** * @file ZrtpPacketHello.h * @brief The ZRTP Hello message * * @ingroup GNU_ZRTP * @{ */ #include #define HELLO_FIXED_PART_LEN 22 /** * Implement the Hello packet. * * The ZRTP Hello message. The implementation sends this * to start the ZRTP negotiation sequence. The Hello message * offers crypto methods and parameters to the other party. The * other party selects methods and parameters it can support * and uses the Commit message to commit these. * * @author Werner Dittmann */ class __EXPORT ZrtpPacketHello : public ZrtpPacketBase { protected: Hello_t* helloHeader; ///< Point to the Hello message part int32_t nHash, ///< number of hash algorithms offered nCipher, ///< number of cipher algorithms offered nPubkey, ///< number of key agreement algorithms offered nSas, ///< number of SAS algorithms offered nAuth; ///< number of SRTP authentication algorithms offered int32_t oHash, ///< offsets in bytes to hash algorithm names oCipher, ///< offsets in bytes to cipher algorithm names oPubkey, ///< offsets in bytes to key agreement algorithm names oSas, ///< offsets in bytes to SAS algorithm names oAuth, ///< offsets in bytes to SRTP authentication algorithm names oHmac; ///< offsets in bytes to MAC of Hello message public: /// Creates a Hello packet with default data ZrtpPacketHello(); /// Creates a Hello packet from received data ZrtpPacketHello(uint8_t *data); virtual ~ZrtpPacketHello(); /** * Set configure data and populate Hello message data. * * Fill in the offered Algorithm names and compute all offset to * names and MAC. An application must call this method on Hello message * objects created with the standard constructor (with default data) * before the application can use most of the getter and setter methods. * * @param config * Pointer to ZrtpConfigure data. */ void configureHello(ZrtpConfigure* config); /// Get version number from Hello message, fixed ASCII character array uint8_t* getVersion() { return helloHeader->version; }; /// Get version number from Hello message as integer, only relvant digits converted int32_t getVersionInt(); /// Get client id from Hello message, fixed ASCII character array uint8_t* getClientId() { return helloHeader->clientId; }; /// Get H3 hash from Hello message, fixed byte array uint8_t* getH3() { return helloHeader->hashH3; }; /// Get client ZID from Hello message, fixed bytes array uint8_t* getZid() { return helloHeader->zid; }; /// Set version sting in Hello message, fixed ASCII character array void setVersion(const uint8_t *text) { memcpy(helloHeader->version, text,ZRTP_WORD_SIZE ); } /// Set client id in Hello message, fixed ASCII character array void setClientId(const uint8_t *t) { memcpy(helloHeader->clientId, t, sizeof(helloHeader->clientId)); } /// Set H3 hash in Hello message, fixed byte array void setH3(uint8_t *hash) { memcpy(helloHeader->hashH3, hash, sizeof(helloHeader->hashH3)); } /// Set client ZID in Hello message, fixed bytes array void setZid(uint8_t *text) { memcpy(helloHeader->zid, text, sizeof(helloHeader->zid)); } /// Check passive mode (mode not implemented) bool isPassive() { return (helloHeader->flags & 0x10) == 0x10 ? true : false; }; /// Check if MitM flag is set bool isMitmMode() { return (helloHeader->flags & 0x20) == 0x20 ? true : false; }; /// Check if SAS sign flag is set bool isSasSign() { return (helloHeader->flags & 0x40) == 0x40 ? true : false; }; /// Get hash algorithm name at position n, fixed ASCII character array uint8_t* getHashType(int32_t n) { return ((uint8_t*)helloHeader)+oHash+(n*ZRTP_WORD_SIZE); } /// Get ciper algorithm name at position n, fixed ASCII character array uint8_t* getCipherType(int32_t n) { return ((uint8_t*)helloHeader)+oCipher+(n*ZRTP_WORD_SIZE); } /// Get SRTP authentication algorithm name at position n, fixed ASCII character array uint8_t* getAuthLen(int32_t n) { return ((uint8_t*)helloHeader)+oAuth+(n*ZRTP_WORD_SIZE); } /// Get key agreement algorithm name at position n, fixed ASCII character array uint8_t* getPubKeyType(int32_t n) { return ((uint8_t*)helloHeader)+oPubkey+(n*ZRTP_WORD_SIZE); } /// Get SAS algorithm name at position n, fixed ASCII character array uint8_t* getSasType(int32_t n) { return ((uint8_t*)helloHeader)+oSas+(n*ZRTP_WORD_SIZE); } /// Get Hello MAC, fixed byte array uint8_t* getHMAC() { return ((uint8_t*)helloHeader)+oHmac; } /// Set hash algorithm name at position n, fixed ASCII character array void setHashType(int32_t n, int8_t* t) { memcpy(((uint8_t*)helloHeader)+oHash+(n*ZRTP_WORD_SIZE), t, ZRTP_WORD_SIZE); } /// Set ciper algorithm name at position n, fixed ASCII character array void setCipherType(int32_t n, int8_t* t) { memcpy(((uint8_t*)helloHeader)+oCipher+(n*ZRTP_WORD_SIZE), t, ZRTP_WORD_SIZE); } /// Set SRTP authentication algorithm name at position n, fixed ASCII character array void setAuthLen(int32_t n, int8_t* t) { memcpy(((uint8_t*)helloHeader)+oAuth+(n*ZRTP_WORD_SIZE), t, ZRTP_WORD_SIZE); } /// Set key agreement algorithm name at position n, fixed ASCII character array void setPubKeyType(int32_t n, int8_t* t) { memcpy(((uint8_t*)helloHeader)+oPubkey+(n*ZRTP_WORD_SIZE), t, ZRTP_WORD_SIZE); } /// Set SAS algorithm name at position n, fixed ASCII character array void setSasType(int32_t n, int8_t* t) { memcpy(((uint8_t*)helloHeader)+oSas+(n*ZRTP_WORD_SIZE), t, ZRTP_WORD_SIZE); } /// Set Hello MAC, fixed byte array void setHMAC(uint8_t* t) { memcpy(((uint8_t*)helloHeader)+oHmac, t, 2*ZRTP_WORD_SIZE); } /// Get number of offered hash algorithms int32_t getNumHashes() {return nHash; } /// Get number of offered cipher algorithms int32_t getNumCiphers() {return nCipher; } /// Get number of offered key agreement algorithms int32_t getNumPubKeys() {return nPubkey; } /// Get number of offered SAS algorithms int32_t getNumSas() {return nSas; } /// Get number of offered SRTP authentication algorithms int32_t getNumAuth() {return nAuth; } /// set MitM flag void setMitmMode() {helloHeader->flags |= 0x20; } /// set SAS sign flag void setSasSign() {helloHeader->flags |= 0x40; } /// Check if packet length matches bool isLengthOk() {return (computedLength == getLength());} private: uint32_t computedLength; // Hello packet is of variable length. It maximum size is 46 words: // - 20 words fixed sizze // - up to 35 words variable part, depending on number of algorithms // leads to a maximum of 4*55=220 bytes. uint8_t data[256]; // large enough to hold a full blown Hello packet }; /** * @} */ #endif // ZRTPPACKETHELLO ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpPacketHelloAck.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPPACKETHELLOACK_H_ #define _ZRTPPACKETHELLOACK_H_ /** * @file ZrtpPacketHelloAck.h * @brief The ZRTP HelloAck message * * @ingroup GNU_ZRTP * @{ */ #include /** * Implement the HelloAck packet. * * The ZRTP simple message HelloAck. The implementation sends this * after receiving a Hello packet. * * @author Werner Dittmann */ class __EXPORT ZrtpPacketHelloAck : public ZrtpPacketBase { public: /// Creates a HelloAck packet with default data ZrtpPacketHelloAck(); /// Creates a HelloAck packet from received data ZrtpPacketHelloAck(uint8_t* data); virtual ~ZrtpPacketHelloAck(); private: HelloAckPacket_t data; }; /** * @} */ #endif // ZRTPPACKETHELLOACK ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpPacketPing.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPPACKETPING_H_ #define _ZRTPPACKETPING_H_ /** * @file ZrtpPacketPing.h * @brief The ZRTP Ping message * * @ingroup GNU_ZRTP * @{ */ #include /** * Implement the PingAck packet. * * The ZRTP simple message PingAck. * * @author Werner Dittmann */ class __EXPORT ZrtpPacketPing : public ZrtpPacketBase { protected: Ping_t* pingHeader; ///< Point the the Ping message public: /// Creates a Ping message with default data ZrtpPacketPing(); /// Creates a Ping message from received data ZrtpPacketPing(uint8_t* data); virtual ~ZrtpPacketPing(); /// Set ZRTP protocol version field, fixed ASCII character array void setVersion(uint8_t *text) { memcpy(pingHeader->version, text,ZRTP_WORD_SIZE ); } /// Get the endpoit hash, fixed byte array uint8_t* getEpHash() { return pingHeader->epHash; } private: PingPacket_t data; }; /** * @} */ #endif // ZRTPPACKETCLEARACK ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpPacketPingAck.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPPACKETPINGACK_H_ #define _ZRTPPACKETPINGACK_H_ #include /** * @file ZrtpPacketPingAck.h * @brief The ZRTP PingAck message * * @ingroup GNU_ZRTP * @{ */ /** * Implement the PingAck packet. * * The ZRTP simple message PingAck. * * @author Werner Dittmann */ class __EXPORT ZrtpPacketPingAck : public ZrtpPacketBase { protected: PingAck_t* pingAckHeader; ///< Points to PingAck message public: /// Creates a PingAck message with default data ZrtpPacketPingAck(); /// Creates a PingAck message from received data ZrtpPacketPingAck(uint8_t* data); virtual ~ZrtpPacketPingAck(); /// Get SSRC from PingAck message uint32_t getSSRC() { return zrtpNtohl(pingAckHeader->ssrc); }; /// Set ZRTP protocol version field, fixed ASCII character array void setVersion(uint8_t *text) { memcpy(pingAckHeader->version, text, ZRTP_WORD_SIZE ); } /// Set SSRC in PingAck message void setSSRC(uint32_t data) {pingAckHeader->ssrc = zrtpHtonl(data); }; /// Set remote endpoint hash, fixed byte array void setRemoteEpHash(uint8_t *hash) { memcpy(pingAckHeader->remoteEpHash, hash, sizeof(pingAckHeader->remoteEpHash)); } /// Set local endpoint hash, fixed byte array void setLocalEpHash(uint8_t *hash) { memcpy(pingAckHeader->localEpHash, hash, sizeof(pingAckHeader->localEpHash)); } private: PingAckPacket_t data; }; /** * @} */ #endif // ZRTPPACKETCLEARACK ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpPacketRelayAck.h ================================================ /* Copyright (C) 2007-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPPACKETRELAYACK_H_ #define _ZRTPPACKETRELAYACK_H_ /** * @file ZrtpPacketRelayAck.h * @brief The ZRTP RelayAck message * * @ingroup GNU_ZRTP * @{ */ #include /** * Implement the RelayAck packet. * * The ZRTP simple message RelayAck. The implementation sends this * after receiving and checking the SASrelay message. * * @author Werner Dittmann */ class __EXPORT ZrtpPacketRelayAck : public ZrtpPacketBase { public: /// Creates a RelayAck packet with default data ZrtpPacketRelayAck(); /// Creates a RelayAck packet from received data ZrtpPacketRelayAck(uint8_t* data); virtual ~ZrtpPacketRelayAck(); private: RelayAckPacket_t data; }; /** * @} */ #endif // _ZRTPPACKETRELAYACK_H_ ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpPacketSASrelay.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPPACKETSASRELAY_H_ #define _ZRTPPACKETSASRELAY_H_ /** * @file ZrtpPacketSASrelay.h * @brief The ZRTP SAS Relay message * * @ingroup GNU_ZRTP * @{ */ #include /** * Implement the Confirm packet. * * The ZRTP message Confirm. The implementation sends this * to confirm the switch to SRTP (encrypted) mode. The contents of * the Confirm message are encrypted, thus the implementation * can check if the secret keys work. * * @author Werner Dittmann */ class __EXPORT ZrtpPacketSASrelay : public ZrtpPacketBase { private: SASrelay_t* sasRelayHeader; ///< Point to the Confirm message part public: /// Creates a Confirm packet with default data ZrtpPacketSASrelay(); /// Creates a Confirm packet with default data and a given signature length ZrtpPacketSASrelay(uint32_t sl); /// Creates a Confirm packet from received data ZrtpPacketSASrelay(uint8_t* d); /// Normal destructor virtual ~ZrtpPacketSASrelay(); /// Check is SAS verify flag is set const bool isSASFlag() { return (sasRelayHeader->flags & 0x4) == 0x4 ? true : false; } /// Get pointer to filler bytes (contains one bit of signature length) const uint8_t* getFiller() { return sasRelayHeader->filler; } /// Get pointer to IV data, fixed byte array const uint8_t* getIv() { return sasRelayHeader->iv; } /// Get pointer to MAC data, fixed byte array const uint8_t* getHmac() { return sasRelayHeader->hmac; } /// Get pointer to new SAS rendering algorithm, fixed byte array const uint8_t* getSasAlgo() {return sasRelayHeader->sas; } /// Get pointer to new SAS hash data, fixed byte array const uint8_t* getTrustedSas() { return sasRelayHeader->trustedSasHash; } /// get the signature length in words uint32_t getSignatureLength(); /// Check if packet length makes sense. SAS rely packets are 19 words at minumum, they are similar to Confirm bool isLengthOk() {return (getLength() >= 19);} /// set SAS verified flag void setSASFlag() { sasRelayHeader->flags |= 0x4; } /// Set MAC data, fixed length byte array void setHmac(uint8_t* text) { memcpy(sasRelayHeader->hmac, text, sizeof(sasRelayHeader->hmac)); } /// Set IV data, fixed length byte array void setIv(uint8_t* text) { memcpy(sasRelayHeader->iv, text, sizeof(sasRelayHeader->iv)); } /// Set SAS rendering algorithm, fixed length byte array void setSasAlgo(uint8_t* text) { memcpy(sasRelayHeader->sas, text, sizeof(sasRelayHeader->sas)); } /// Set SAS hash data, fixed length byte array void setTrustedSas(uint8_t* text) { memcpy(sasRelayHeader->trustedSasHash, text, sizeof(sasRelayHeader->trustedSasHash)); } /// Set signature length in words void setSignatureLength(uint32_t sl); private: void initialize(); // Confirm packet is of variable length. It maximum size is 524 words: // - 11 words fixed size // - up to 513 words variable part, depending if signature is present and its length. // This leads to a maximum of 4*524=2096 bytes. uint8_t data[2100]; // large enough to hold a full blown Confirm packet }; /** * @} */ #endif // ZRTPPACKETSASRELAY ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpSdesStream.h ================================================ /* Copyright (C) 2012-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPSDESSTREAM_H_ #define _ZRTPSDESSTREAM_H_ /** * @file ZrtpSdesStream.h * @brief The ZRTP main engine * @defgroup GNU_ZRTP The GNU ZRTP C++ implementation * @{ * * This class implements SDES and provides a simple to use API for applications. * * This SDES implementation currently supports only two SDES algorithms and it does * not support optional parameters such as lifetime or MKI parameters. Also session * parameters are not supported. Most applications that use SDES don't use these * optional parameters. * * It is not necessary to explicitly start the SDES stream. The class initiates * the SRTP after it created and parsed all necessary SDES crypto strings. * * Because SDES works together with the signaling protocol, for example SIP, it is * important to adhere to a defined flow. The following pseudo code snippet depicts * such a flow. Applications shall follow this flow. * *
 *
 *     Inviter                           Answerer
 *    (Offerer)
 *
 * ZrtpSdesStream inv;                 ZrtpSdesStream answ;
 *
 * // create/get own SDES data
 * inv.createSdes(...);
 * inv.getCryptoMixAttribute(...)
 *
 * // prepare SIP/SDP offer, send
 * // it to answerer
 *                                    // receive SIP/SDP, get
 *                                    // SDES data, parse/set it
 *                                    answ.setCryptoMixAttribute(...)
 *                                    answ.parseSdes(...)
 *
 *                                    // create/get own SDES data
 *                                    answ.getCryptoMixAttribute(...)
 *                                    answ.createSdes(...)
 *
 *                                    // prepare SIP/SDP answer,
 *                                    // send to offerer
 * // receive SIP/SDP answer, get
 * // SDES data, parse, set mix algo
 * // if availabe
 * inv.setCryptoMixAttribute(...)
 * inv.parseSdes(...)
 *
 * ...                                ...
 *
 * inv.outgoingRtp(...)
 *                                    answ.incomingRtp(...)
 *
 *                                    answ.outgoingRtp(...)
 * inv.incomingRtp(...)
 *
* * To use SDES without the new crypto mix feature just do not use the crypto mix functions. * An application may always send crypto mix attributes. If the answerer does not support this * feature it does not send back a selected algorithm and the offerer cannot set an algorithm. * Thus the crypto mix feature is not used. * * @author Werner Dittmann */ #include #include class CryptoContext; class CryptoContextCtrl; /* * These functions support 256 bit encryption algorithms. */ #define MAX_KEY_LEN 32 #define MAX_SALT_LEN 14 #define MAX_DIGEST_LENGTH 64 /** * Maximum length of a raw crypto string. */ #define MAX_CRYPT_STRING_LEN 200 class __EXPORT ZrtpSdesStream { public: /** * Supported SDES crypto suites. */ typedef enum { AES_CM_128_HMAC_SHA1_32 = 0, AES_CM_128_HMAC_SHA1_80 } sdesSuites; /** * SDES stream state */ typedef enum { STREAM_INITALIZED = 1, OUT_PROFILE_READY, IN_PROFILE_READY, SDES_SRTP_ACTIVE } sdesZrtpStates; typedef enum { MIX_NONE = 0, MIX_HMAC_SHA, MIX_MAC_SKEIN } sdesHmacTypeMix; /** * @brief Create and SDES/ZRTP stream. * * This method creates an SDES stream with capabilities to handle RTP, * RTCP, SRTP, and SRTCP packets. * * @param suite defines which crypto suite to use for this stream. The values are * @c AES_CM_128_HMAC_SHA1_80 or @c AES_CM_128_HMAC_SHA1_32. */ ZrtpSdesStream(const sdesSuites suite =AES_CM_128_HMAC_SHA1_32); ~ZrtpSdesStream(); /** * @brief Close an SDES/ZRTP stream. * * Close the stream and return allocated memory to the pool. */ void close(); /** * @brief Creates an SDES crypto string for the SDES/ZRTP stream. * * Creates the crypto string that the application can use in the SDP fields of * SIP INVITE or SIP 200 OK. * * An INVITE-ing application shall call this function at the same point when * it calls the functions to get the @c zrtp-hash string and shall insert the * created crypto string into the SDP. * * An answering application shall call this function directly @b after it called * @c sdesZrtpStreamParseSdes. This usually at the same point when it gets the * @c zrtp-hash from the SDP parameters and forwards it to @c libzrtp. The * answering application's SRTP environment is now ready. * * @param cryptoString output buffer that receives the crypto string in raw * format, without the any signaling prefix, for example * @c a=crypto:. The function terminates the crypto string * with a @c nul byte * * @param maxLen length of the crypto string buffer. On return it contains the * actual length of the crypto string. * * @param sipInvite the inviter (offerer) must set this to @c true, the answerer must * set it to @c false. * * @return @c true if data could be created, @c false otherwise. */ bool createSdes(char *cryptoString, size_t *maxLen, bool sipInvite); /** * @brief Parses an SDES crypto string for the SDES/ZRTP stream. * * Parses a SDES crypto string that the application received in a SIP INVITE * or SIP 200 OK. * * An INVITE-ing (offerer) application shall call this function right after it received * the 200 OK from the answering application and must call this function with the * @c sipInvite parameter set to @c true. The offerer's SRTP is now ready for use. * * The answering application calls this function after it received the INVITE and * extracted the crypto string from the SDP and must call this function with the * @c sipInvite parameter set to @c false. * * @param cryptoString the received crypto sting in raw format, * without any signaling prefix, for example @c a=crypto: * * @param length length of the crypto string to parse. If the length is * @c zero then the function uses @c strlen to compute * the length. * * @param sipInvite the inviter (offerer) must set this to @c true, the answerer must * set it to @c false. * * @return @c true if data could be created, @c false otherwise. */ bool parseSdes(const char *cryptoString, size_t length, bool sipInvite); /** * @brief Get Crypto Mix attribute string * * The offerer calls this method to get a string of @b all supported crypto mix algorithms * and shall send this list to the answerer. * * The answerer calls this function only @b after it received the crypto mix string and @b after * calling @c setCryptoMixAttribute(...). The method returns only one (the selected) * crypto mix algorithm and the answerer must send this to the offerer, for example in 200 OK. * * @param algoNames buffer to store the nul terminated crypto mix algorithm names. * The buffer must be long enough to hold at least the name of the mandatory * algorithm HMAC-SHA-384. * * @param length length of buffer * * @return Length of algorithm names (excluding nul byte) or zero if crypto mix not supported or * enabled. */ int getCryptoMixAttribute(char *algoNames, size_t length); /** * @brief Set Crypto Mix attribute string * * The method checks if it the string contains an supported algorithm and selects one algorithm. * * The offerer calls this method @b after it received the selected algorithm in the answer. * * The answerer must call this method @b before it calls the @c getCryptoMixAttribute() method. * * @param algoNames buffer that contains the received crypto mix algorithm names. * The buffer must be nul terminated. * * @return @c false if none of the offered algorithms is supported. */ bool setCryptoMixAttribute(const char *algoNames); /* * ******** Outgoing RTP/RTCP packet handling */ /** * @brief Process an outgoing RTP packet * * This function processes an outgoing RTP packet. Depending on the state * the packet is either: * - not encrypted if neither SDES nor ZRTP are active or supported by the * other client. This is the standard case if the stream was just initialized. * - encrypted with SDES provided key data. This is the case if the application * called both @c sdesZrtpStreamCreateSdes and @c sdesZrtpStreamParseSdes * functions to properly setup the SDES key data. * * @param packet the buffer that contains the RTP packet. After processing, the * encrypted packet is stored in the same buffer. The buffer must * big enough to hold the additional SRTP data, depending on the * SRTP profile these are usually 4 - 20 bytes. * * @param length length of the RTP packet * * @param newLength to an integer that get the new length of the packet including SRTP data. * * @return * - @c true if encryption is successful, app shall send packet to the recipient. * - @c false if there was an error during encryption, don't send the packet. */ bool outgoingRtp(uint8_t *packet, size_t length, size_t *newLength); /** * @brief Process an outgoing RTCP packet * * This function works in the same way as @c outgoingRtp. * * @param packet the buffer that contains the RTCP packet. After processing, the * encrypted packet is stored in the same buffer. The buffer must * big enough to hold the additional SRTP data, depending on the * SRTP profile these are usually 8 - 20 bytes. * * @param length length of the RTP packet * * @param newLength to an integer that get the new length of the packet including SRTP data. * * @return * - @c true if encryption is successful, app shall send packet to the recipient. * - @c false if there was an error during encryption, don't send the packet. */ bool outgoingRtcp(uint8_t *packet, size_t length, size_t *newLength); /* * ******** Incoming SRTP/SRTCP packet handling */ /** * @brief Process an incoming RTP or SRTP packet * * This function processes an incoming RTP/SRTP packet. Depending on the state * the packet is either: * - not decrypted if SDES is not active or supported by the * other client. This is the standard case if the stream was just initialized. * - decrypted with SDES provided key data. This is the case if the application * called both @c sdesZrtpStreamCreateSdes and @c sdesZrtpStreamParseSdes * functions to properly setup the SDES key data. * * If the @c errorData pointer is not @c NULL then this function fills the data structure * in case of an error return. The caller may store and evaluate this data to further * trace the problem. * * @param packet the buffer that contains the RTP/SRTP packet. After processing, * the decrypted packet is stored in the same buffer. * * @param length length of the RTP packet * * @param newLength to an integer that get the new length of the packet excluding SRTCP data. * * @param errorData Pointer to @c errorData structure or @c NULL, default is @c NULL * * @return * - 1: success, * - 0: SRTP/RTP packet decode error * - -1: SRTP authentication failed, * - -2: SRTP replay check failed */ int incomingRtp(uint8_t *packet, size_t length, size_t *newLength, SrtpErrorData* errorData=NULL); /** * @brief Process an incoming RTCP or SRTCP packet * * This function works in the same way as @c incomingRtp. * * @param packet the buffer that contains the RTCP/SRTCP packet. After processing, * the decrypted packet is stored in the same buffer. * * @param length length of the RTCP packet * * @param newLength to an integer that get the new length of the packet excluding SRTCP data. * * @return * - 1: success, * - -1: SRTCP authentication failed, * - -2: SRTCP replay check failed */ int incomingSrtcp(uint8_t *packet, size_t length, size_t *newLength); /** * @brief Process an outgoing ZRTP packet. * * Works like @c outgoingRtp, refer to that documentation. * * @param packet the buffer that contains the ZRTP packet. * * @param length length of the ZRTP packet * * @param newLength to an integer that get the new length of the packet including SRTP data. * * @return * - @c true if encryption is successful, app shall send packet to the recipient. * - @c false if there was an error during encryption, don't send the packet. */ bool outgoingZrtpTunnel(uint8_t *packet, size_t length, size_t *newLength); /** * @brief Process an incoming ZRTP packet * * Works like @c incomingRtp, refer to that documentation. * * @param packet the buffer that contains the ZRTP/SRTP packet. After processing, * the decrypted packet is stored in the same buffer. * * @param length length of the RTP packet * * @param newLength to an integer that get the new length of the packet excluding SRTCP data. * * @param errorData Pointer to @c errorData structure or @c NULL, default is @c NULL * * @return * - 1: success, * - 0: SRTP/RTP packet decode error * - -1: SRTP authentication failed, * - -2: SRTP replay check failed */ int incomingZrtpTunnel(uint8_t *packet, size_t length, size_t *newLength, SrtpErrorData* errorData=NULL); /** * @brief Return state of SDES stream. * * @return state of stream. */ sdesZrtpStates getState() {return state;} /** * @brief Return SDES crypto mixer HMAC type. * * @return HMAC type */ sdesHmacTypeMix getHmacTypeMix() {return cryptoMixHashType;} /** * @brief Return name of active cipher algorithm. * * @return point to name of cipher algorithm. */ const char* getCipher(); /** * @brief Return name of active SRTP authentication algorithm. * * @return point to name of authentication algorithm. */ const char* getAuthAlgo(); /* * ******** Lower layer functions */ private: /** * @brief Create an SRTP crypto context and the according SDES crypto string. * * This lower layer method creates an SDES crypto string. It selects a valid * crypto suite, generates the key and salt data, converts these into base 64 * and returns the crypto string in raw format without any signaling prefixes. * * The output string has the following format: * @verbatim * 1 AES_CM_128_HMAC_SHA1_32 inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj * @endverbatim * * Applications usually don't use this method directly. Applications shall * use the SDES stream functions. * * Depending on the crypto suite the overall length of the crypto string * is variable. For a normal AES_128_CM suite the minumum lenth is 73 * characters, a AES_256_CM suite results in 97 characters (not counting * any signaling prefixes). * * @param cryptoString points to a char output buffer that receives the * crypto string in the raw format, without the any * signaling prefix, for example @c a=crypto: in case * of SDP signaling. The function terminates the * crypto string with a @c nul byte * * @param maxLen points to an integer. On input this integer specifies the * length of the output buffer. If @c maxLen is smaller than * the resulting crypto string the function returns an error * conde. On return the functions sets @c maxLen to the * actual length of the resultig crypto string. * * @param tag the value of the @c tag field in the crypto string. The * answerer must use this input to make sure that the tag value * in the answer matches the value in the offer. See RFC 4568, * section 5.1.2. * If the tag value is @c -1 the function sets the tag to @c 1. * * @return @c true if data could be created, @c false * otherwise. */ bool createSdesProfile(char *cryptoString, size_t *maxLen); /** * @brief Parse and check an offered SDES crypto string and create SRTP crypto context. * * The method parses an offered SDES crypto string and checks if it is * valid. Next it checks if the string contains a supported crypto suite * and if the key and salt lengths match the selected crypto suite. * * Applications usually don't use this method directly. Applications shall * use the SDES stream functions. * * @b NOTE: This function does not support the optional parameters lifetime, * MKI, and session parameters. While it can parse liftime and MKI theiy are * not evaluated and used. If these parameters are used in the input crypto * string the function return @c false. * * @param cryptoString points to the crypto sting in raw format, * without any signaling prefix, for example @c a=crypto: in case of * SDP signaling. * * @param length length of the crypto string to parse. If the length is * @c zero then the function uses @c strlen to compute the length. * * @param parsedSuite the function sets this to the @c sdesSuites enumerator of * the parsed crypto suite. The answerer shall use this as input to * @c createSdesProfile to make sure that it creates the same crypto suite. * See RFC 4568, section 5.1.2 * * @param tag the function sets this to the @c tag value of the parsed crypto * string. The answerer must use this as input to @c createSdesProfile * to make sure that it creates the correct tag in the crypto string. * See RFC 4568, section 5.1.2 * * @return @c true if checks were ok, @c false * otherwise. */ bool parseCreateSdesProfile(const char *cryptoString, size_t length, sdesSuites *parsedSuite, int32_t *tag); /** * @brief Create the SRTP contexts after all SDES creation and parsing is done. * * @param sipInvite if this is set to @c true (not zero) then the method * computes the key data for the inviting SIP application (offerer) and * for the answerer otherwise. */ void createSrtpContexts(bool sipInvite); /** * @brief Compute the mixed keys if SDES mixing attribute is set. * * The method takes the parsed or created SDES key material and computes the mixed keys and salt. * It replaces the existing key material with the new data. * * @param sipInvite if this is set to @c true (not zero) then the method * computes the key data for the inviting SIP application (offerer) and * for the answerer otherwise. */ void computeMixedKeys(bool sipInvite); sdesZrtpStates state; sdesSuites suite; int32_t tag; CryptoContext *recvSrtp; //!< The SRTP context for this stream CryptoContextCtrl *recvSrtcp; //!< The SRTCP context for this stream CryptoContext *sendSrtp; //!< The SRTP context for this stream CryptoContextCtrl *sendSrtcp; //!< The SRTCP context for this stream uint32_t srtcpIndex; //!< the local SRTCP index CryptoContext *recvZrtpTunnel; //!< The SRTP context for sender ZRTP tunnel CryptoContext *sendZrtpTunnel; //!< The SRTP context for receiver ZRTP tunnel int32_t cryptoMixHashLength; sdesHmacTypeMix cryptoMixHashType; // Variables for crypto that this client creates and sends to the other client, filled during SDES create uint8_t localKeySalt[((MAX_KEY_LEN + MAX_SALT_LEN + 3)/4)*4]; //!< Some buffer for key and salt, multiple of 4 int localKeyLenBytes; int localSaltLenBytes; int localCipher; int localAuthn; int localAuthKeyLen; int localTagLength; // Variables for crypto that this client receives from the other client, filled during SDES parse uint8_t remoteKeySalt[((MAX_KEY_LEN + MAX_SALT_LEN + 3)/4)*4]; //!< Some buffer for key and salt, multiple of 4 int remoteKeyLenBytes; int remoteSaltLenBytes; int remoteCipher; int remoteAuthn; int remoteAuthKeyLen; int remoteTagLength; }; #endif ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpStateClass.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPSTATECLASS_H_ #define _ZRTPSTATECLASS_H_ /** * @file ZrtpStateClass.h * @brief The ZRTP state handling class * * @ingroup GNU_ZRTP * @{ */ #include #include #include /** * The ZRTP states * * Depending on the role of this state engine and the actual protocl flow * not all states are processed during a ZRTP handshake. */ enum zrtpStates { Initial, ///< Initial state after starting the state engine Detect, ///< State sending Hello, try to detect answer message AckDetected, ///< HelloAck received AckSent, ///< HelloAck sent after Hello received WaitCommit, ///< Wait for a Commit message CommitSent, ///< Commit message sent WaitDHPart2, ///< Wait for a DHPart2 message WaitConfirm1, ///< Wait for a Confirm1 message WaitConfirm2, ///< Wait for a confirm2 message WaitConfAck, ///< Wait for Conf2Ack WaitClearAck, ///< Wait for clearAck - not used SecureState, ///< This is the secure state - SRTP active WaitErrorAck, ///< Wait for ErrorAck message numberOfStates ///< Gives total number of protocol states }; enum EventReturnCodes { Fail = 0, ///< ZRTP event processing failed. Done = 1 ///< Event processing ok. }; enum EventDataType { ZrtpInitial = 1, ///< Initial event, enter Initial state ZrtpClose, ///< Close event, shut down state engine ZrtpPacket, ///< Normal ZRTP message event, process according to state Timer, ///< Timer event ErrorPkt ///< Error packet event }; enum SecureSubStates { Normal, WaitSasRelayAck, numberofSecureSubStates }; /// A ZRTP state event typedef struct Event { EventDataType type; ///< Type of event size_t length; ///< length of the message data uint8_t* packet; ///< Event data if availabe, usually a ZRTP message } Event_t; /** * The ZRTP timer structure. * * This structure holds all necessary data to compute the timer for * the protocol timers. The state engine allocate one structure for * each timer. ZRTP uses two timers, T1 and T2, to monitor protocol * timeouts. As a slight misuse but to make overall handling a bit * simpler this structure also contains the resend counter. This is * possible in ZRTP because it uses a simple timeout strategy. */ typedef struct zrtpTimer { int32_t time, ///< Current timeout value start, ///< Start value for timeout increment, ///< increment timeout after each timeout event (not used anymore) capping, ///< Maximum timeout value counter, ///< Current number of timeouts maxResend; ///< Maximum number of timeout resends } zrtpTimer_t; class ZRtp; /** * This class is the ZRTP protocol state engine. * * This class is responsible to handle the ZRTP protocol. It does not * handle the ZRTP HMAC, DH, and other data management. This is done in * class ZRtp, which is the parent of this class. * * The methods of this class implement the ZRTP state actions. * */ class __EXPORT ZrtpStateClass { private: ZRtp* parent; ///< The ZRTP implmentation ZrtpStates* engine; ///< The state switching engine Event_t* event; ///< Current event to process /** * The last packet that was sent. * * If we are Initiator then resend this packet in case of * timeout. */ ZrtpPacketBase* sentPacket; /** * Points to prepared Commit packet after receiving a Hello packet */ ZrtpPacketCommit* commitPkt; zrtpTimer_t T1; ///< The Hello message timeout timer zrtpTimer_t T2; ///< Timeout timer for other messages int32_t t1Resend; ///< configurable resend counter for T1 (Hello packets) int32_t t1ResendExtend; ///< configurable extended resend counter for T1 (Hello packets) int32_t t2Resend; ///< configurable resend counter for T2 (other packets) /* * If this is set to true the protocol engine handle the multi-stream * variant of ZRTP. Refer to chapter 5.4.2 in the ZRTP specification. */ bool multiStream; // Secure substate to handle SAS relay packets SecureSubStates secSubstate; /** * Secure Sub state WaitSasRelayAck. * * This state belongs to the secure substates and handles * SAS Relay Ack. * * When entering this transition function * - sentPacket contains Error packet, Error timer active * * Possible events in this state are: * - timeout for sent SAS Relay packet: causes a resend check and repeat sending * of packet * - SASRelayAck: Stop timer and switch to secure substate Normal. */ bool subEvWaitRelayAck(); /** * Hello packet version sent to other partner */ int32_t sentVersion; int32_t retryCounters[ErrorRetry+1]; // TODO adjust public: /// Create a ZrtpStateClass ZrtpStateClass(ZRtp *p); ~ZrtpStateClass(); /// Check if in a specified state bool inState(const int32_t state) { return engine->inState(state); }; /// Switch to the specified state void nextState(int32_t state) { engine->nextState(state); }; /// Process an event, the main entry point into the state engine void processEvent(Event_t *ev); /** * The state event handling methods. * * Refer to the protocol state diagram for further documentation. */ /// Initial event state void evInitial(); /// Detect state void evDetect(); /// HelloAck detected state void evAckDetected(); /// HelloAck sent state void evAckSent(); /// Wait for Commit message void evWaitCommit(); /// Commit sent state void evCommitSent(); /// Wait for DHPart2 message void evWaitDHPart2(); /// Wait for Confirm2 message void evWaitConfirm1(); /// Wait for Confirm2 message void evWaitConfirm2(); /// Wait for ConfAck message void evWaitConfAck(); /// Wait for ClearAck message (not used) void evWaitClearAck(); /// Secure reached state void evSecureState(); /// Wait for ErrorAck message void evWaitErrorAck(); /** * Initialize and activate a timer. * * @param t * The ZRTP timer structure to use for the timer. * @return * 1 timer was activated * 0 activation failed */ int32_t startTimer(zrtpTimer_t *t); /** * Compute and set the next timeout value. * * @param t * The ZRTP timer structure to use for the timer. * @return * 1 timer was activated * 0 activation failed * -1 resend counter exceeded */ int32_t nextTimer(zrtpTimer_t *t); /** * Cancel the active timer. * * @return * 1 timer was canceled * 0 cancelation failed */ int32_t cancelTimer() {return parent->cancelTimer(); }; /** * Prepare and send an Error packet. * * Preparse an Error packet and sends it. It stores the Error * packet in the sentPacket variable to enable resending. The * method switches to protocol state Initial. */ void sendErrorPacket(uint32_t errorCode); /** * Set status if an error occured while sending a ZRTP packet. * * This functions clears data and set the state to Initial after the engine * detected a problem while sending a ZRTP packet. * * @return * Fail code */ void sendFailed(); /** * Set status if a timer problems occure. * * This functions clears data and set state to Initial after a timer * error occured. Either no timer available or resend counter exceedeed. * * @return * Fail code */ void timerFailed(int32_t subCode); /** * Set multi-stream mode flag. * * This functions set the multi-stream mode. The protocol * engine will run the multi-stream mode variant of the ZRTP * protocol if this flag is set to true. * * @param multi * Set the multi-stream mode flag to true or false. */ void setMultiStream(bool multi); /** * Status of multi-stream mode flag. * * This functions returns the value of the multi-stream mode flag. * * @return * Value of the multi-stream mode flag. */ bool isMultiStream(); /** * Send a SAS relay packet. * * the functions stores sends the SAS relay packet and stores the pointer in * the sentPacket variable to enable resending. * * The method switches to secure substate WaitSasRelayAck. * * @param relay * Pointer to the SAS relay packet. */ void sendSASRelay(ZrtpPacketSASrelay* relay); /** * Set the resend counter of timer T1 - T1 controls the Hello packets. */ void setT1Resend(int32_t counter) {T1.maxResend = counter;} /** * Set the time capping of timer T1 - T1 controls the Hello packets. */ void setT1Capping(int32_t capping) {T1.capping = capping;} /** * Set the extended resend counter of timer T1 - T1 controls the Hello packets. * * More retries to extend time, see chap. 6 */ void setT1ResendExtend(int32_t counter) {t1ResendExtend = counter;} /** * Set the resend counter of timer T2 - T2 controls other (post-Hello) packets. */ void setT2Resend(int32_t counter) {T2.maxResend = counter;} /** * Set the time capping of timer T2 - T2 controls other (post-Hello) packets. */ void setT2Capping(int32_t capping) {T2.capping = capping;} /** * @brief Get required buffer size to get all 32-bit retry counters * * @param streamNm stream, if not specified the default is @c AudioStream * * @return number of 32 bit integer elements required or < 0 on error */ int getNumberOfRetryCounters(); /** * @brief Read retry counters * * @param buffer Pointer to buffer of 32-bit integers. The buffer must be able to * hold at least getNumberOfRetryCounters() 32-bit integers * @param streamNm stream, if not specified the default is @c AudioStream * * @return number of 32-bit counters returned in buffer or < 0 on error */ int getRetryCounters(int32_t* counters); }; /** * @} */ #endif // _ZRTPSTATECLASS_H_ ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpStates.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* * Authors: Werner Dittmann */ #ifndef _ZRTPSTATES_H_ #define _ZRTPSTATES_H_ /** * @file ZrtpStates.h * @brief The ZRTP state switching class * * @ingroup GNU_ZRTP * @{ */ #include #include #include #include class __EXPORT ZrtpStateClass; /** * This structure hold the state name as enum (int) number and the pointer to * the functions that handles the various triggers that can occur in a state. */ typedef struct { int32_t stateName; ///< The state number void (ZrtpStateClass::* handler)(void); ///< The state handler } state_t; /** * Implement a simple state switching. * * This class provides functions that manage the states and the event handler * functions. Its a very simple implementation. * * @author Werner Dittmann */ class __EXPORT ZrtpStates { public: /// Create an initialize state switching ZrtpStates(state_t* const zstates, const int32_t numStates, const int32_t initialState): numStates(numStates), states(zstates), state(initialState) {} /// Call a state handler int32_t processEvent(ZrtpStateClass& zsc) { (zsc.*states[state].handler)(); return 0; } /// Check if in specified state bool inState(const int32_t s) { return ((s == state)); } /// Set the next state void nextState(int32_t s) { state = s; } private: const int32_t numStates; const state_t* states; int32_t state; ZrtpStates(); }; /** * @} */ #endif //ZRTPSTATES ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpTextData.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* * Authors: Werner Dittmann */ #ifndef _ZRTPTEXTDATA_H_ #define _ZRTPTEXTDATA_H_ /** * @file ZrtpTextData.h * @brief The ZRTP ASCII texts - extern references * * @ingroup GNU_ZRTP * @{ */ #include /** * The extern references to the global data. * * @author Werner Dittmann */ extern char zrtpBuildInfo[]; extern char clientId[]; extern char zrtpVersion_11[]; extern char zrtpVersion_12[]; /** * */ extern char HelloMsg[]; extern char HelloAckMsg[]; extern char CommitMsg[]; extern char DHPart1Msg[]; extern char DHPart2Msg[]; extern char Confirm1Msg[]; extern char Confirm2Msg[]; extern char Conf2AckMsg[]; extern char ErrorMsg[]; extern char ErrorAckMsg[]; extern char GoClearMsg[]; extern char ClearAckMsg[]; extern char PingMsg[]; extern char PingAckMsg[]; extern char SasRelayMsg[]; extern char RelayAckMsg[]; /** * */ extern char responder[]; extern char initiator[]; extern char iniMasterKey[]; extern char iniMasterSalt[]; extern char respMasterKey[]; extern char respMasterSalt[]; extern char iniHmacKey[]; extern char respHmacKey[]; extern char retainedSec[]; extern char iniZrtpKey[]; extern char respZrtpKey[]; extern char sasString[]; extern char KDFString[]; extern char zrtpSessionKey[]; extern char zrtpMsk[]; extern char zrtpTrustedMitm[]; extern char s256[]; extern char s384[]; extern char skn2[]; extern char skn3[]; extern const char* mandatoryHash; extern char aes3[]; extern char aes2[]; extern char aes1[]; extern char two3[]; extern char two2[]; extern char two1[]; extern const char* mandatoryCipher; extern char dh2k[]; extern char dh3k[]; extern char ec25[]; extern char ec38[]; extern char e255[]; extern char e414[]; extern char mult[]; extern const char* mandatoryPubKey; extern char b32[]; extern char b256[]; extern const char* mandatorySasType; extern char hs32[]; extern char hs80[]; extern char sk32[]; extern char sk64[]; extern const char* mandatoryAuthLen_1; extern const char* mandatoryAuthLen_2; extern const char* sas256WordsOdd[]; extern const char* sas256WordsEven[]; /** * @} */ #endif // _ZRTPTEXTDATA_H_ ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/ZrtpUserCallback.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTPUSERCALLBACK_H_ #define _ZRTPUSERCALLBACK_H_ /** * @file ZrtpUserCallback.h * @brief The ZRTP UserCallback class * * @ingroup GNU_ZRTP * @{ */ #include #include #include /** * Application callback methods. * * The ccRTP specific part of GNU ZRTP uses these callback methods * to report ZRTP events to the application. This class implements a * default behaviour for each callback method, usually just a return. * * An application may extend this class and overload methods * to implement its own behaviour. The application must register its * callback class using ZrtpQueue#setUserCallback(). * * CAVEAT
* All methods of the user callback class and classes that * extend this class run in the context of the RTP thread. Thus it is * of paramount importance to keep the execution time of the methods * as short as possible. * * @author Werner Dittmann */ class __EXPORT ZrtpUserCallback { public: /// Create the stadard user callback class. ZrtpUserCallback() {} virtual ~ZrtpUserCallback() {}; /** * Inform user interface that security is active now. * * ZRTP calls this method if the sender and the receiver are * in secure mode now. * * @param cipher * Name and mode of cipher used to encrypt the SRTP stream */ virtual void secureOn(std::string cipher) { return; } /** * Inform user interface that security is not active any more. * * ZRTP calls this method if either the sender or the receiver * left secure mode. * */ virtual void secureOff() { return; } /** * Show the Short Authentication String (SAS) on user interface. * * ZRTP calls this method to display the SAS and inform about the SAS * verification status. The user interface shall enable a SAS verfication * button (or similar UI element). The user shall click on this UI * element after he/she confirmed the SAS code with the partner. * * @param sas * The string containing the SAS. * @param verified * If verified is true then SAS was verified by both * parties during a previous call, otherwise it is set to false. */ virtual void showSAS(std::string sas, bool verified) { return; } /** * Inform the user that ZRTP received "go clear" message from its peer. * * On receipt of a go clear message the user is requested to confirm * a switch to unsecure (clear) modus. Until the user confirms ZRTP * (and the underlying RTP) does not send any data. */ virtual void confirmGoClear() { return; } /** * Show some information to user. * * ZRTP calls this method to display some information to the user. * Along with the message ZRTP provides a severity indicator that * defines: Info, Warning, Error, and Alert. Refer to the * MessageSeverity enum in ZrtpCodes.h. The * UI may use this indicator to highlight messages or alike. * * @param sev * Severity of the message. * @param subCode * The subcode identifying the reason. */ virtual void showMessage(GnuZrtpCodes::MessageSeverity sev, int32_t subCode) { return; } /** * ZRTPQueue calls this if the negotiation failed. * * ZRTPQueue calls this method in case ZRTP negotiation failed. The * parameters show the severity as well as some explanatory text. * Refer to the MessageSeverity enum above. * * @param severity * This defines the message's severity * @param subCode * The subcode identifying the reason. */ virtual void zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity, int32_t subCode) { return; } /** * ZRTPQueue calls this method if the other side does not support ZRTP. * * If the other side does not answer the ZRTP Hello packets then * ZRTP calls this method. * */ virtual void zrtpNotSuppOther() { return; } /** * ZRTPQueue calls this method to inform about a PBX enrollment request. * * Please refer to chapter 8.3 ff to get more details about PBX enrollment * and SAS relay. * * @param info * Give some information to the user about the PBX requesting an * enrollment. * */ virtual void zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info) { return; } /** * ZRTPQueue calls this method to inform about PBX enrollment result. * * Informs the use about the acceptance or denial of an PBX enrollment * request * * @param info * Give some information to the user about the result of an * enrollment. * */ virtual void zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info) { return; } /** * ZRTPQueue calls this method to request a SAS signature. * * After ZRTP core was able to compute the Short Authentication String * (SAS) it calls this method. The client may now use an approriate * method to sign the SAS. The client may use * setSignatureData() of ZrtpQueue to store the signature * data an enable signature transmission to the other peer. Refer * to chapter 8.2 of ZRTP specification. * * @param sasHash * Pointer to the 32 byte SAS hash to be signed. * @see ZrtpQueue#setSignatureData * */ virtual void signSAS(uint8_t* sasHash) { return; } /** * ZRTPQueue calls this method to request a SAS signature check. * * After ZRTP received a SAS signature in one of the Confirm packets it * call this method. The client may use getSignatureLength() * and getSignatureData()of ZrtpQueue to get the signature * data and perform the signature check. Refer to chapter 8.2 of ZRTP * specification. * * If the signature check fails the client may return false to ZRTP. In * this case ZRTP signals an error to the other peer and terminates * the ZRTP handshake. * * @param sasHash * Pointer to the 32 byte SAS hash that was signed by the other peer. * @return * true if the signature was ok, false otherwise. * */ virtual bool checkSASSignature(uint8_t* sasHash) { return true; } }; #endif ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/zrtpB64Decode.h ================================================ /* cdecode.h - c header for a base64 decoding algorithm This is part of the libb64 project, and has been placed in the public domain. For details, see http://sourceforge.net/projects/libb64 */ #ifndef BASE64_CDECODE_H #define BASE64_CDECODE_H #include #if defined(__cplusplus) extern "C" { #endif typedef enum { step_a, step_b, step_c, step_d } base64_decodestep; typedef struct { base64_decodestep step; char plainchar; } base64_decodestate; void base64_init_decodestate(base64_decodestate* state_in); int base64_decode_value(char value_in); int base64_decode_block(const char* code_in, const int length_in, uint8_t *plaintext_out, base64_decodestate* state_in); #if defined(__cplusplus) } #endif #endif /* BASE64_CDECODE_H */ ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/zrtpB64Encode.h ================================================ /* cencode.h - c header for a base64 encoding algorithm This is part of the libb64 project, and has been placed in the public domain. For details, see http://sourceforge.net/projects/libb64 */ #ifndef BASE64_CENCODE_H #define BASE64_CENCODE_H #include #if defined(__cplusplus) extern "C" { #endif typedef enum { step_A, step_B, step_C } base64_encodestep; typedef struct { base64_encodestep step; char result; int stepcount; int lineLength; } base64_encodestate; void base64_init_encodestate(base64_encodestate* state_in, int lineLength); char base64_encode_value(const int8_t value_in); int base64_encode_block(const uint8_t *plaintext_in, int length_in, char* code_out, base64_encodestate* state_in); int base64_encode_blockend(char *code_out, base64_encodestate* state_in); #if defined(__cplusplus) } #endif #endif /* BASE64_CENCODE_H */ ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/zrtpCacheDbBackend.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _ZRTP_CACHE_DB_BACKEND_H_ #define _ZRTP_CACHE_DB_BACKEND_H_ #include #if defined(__cplusplus) extern "C" { #endif #define DB_CACHE_ERR_BUFF_SIZE 1000 /** * Set of accessible operations of database ZRTP cache implementaion. * * The only public method of the database ZRTP implementation is * getDbCacheOps(...) that fills in this call structure. This mechanism * decouples the database implementations from libzrtp and possible other * clients. * * Some implementation notes: *
    *
  • All data storage methods return 0 (zero) if the call was successful. *
  • *
  • The @c errString parameter points to a buffer of at least @c * DB_CACHE_ERR_BUFF_SIZE character. In case of an error methods shall * store detailed, human readable information in this buffer. Use @c * snprintf or similar functions to format the data. If this parameter * is @c NULL then methods must not return an error string. *
  • *
  • The methods cast the @c void to the type they need. Be aware that the * open functions requires a pointer to a pointer. *
  • *
* * * */ typedef struct { /** * @brief Open the cache. * * @param name String that identifies the database or data storage. * * @param pdb Pointer to an internal structure that the database * implementation requires. * * @param errString Pointer to a character buffer, see implementation * notes above. */ int (*openCache)(const char* name, void **pdb, char *errString); /** * Close the cache. * * @param db Pointer to an internal structure that the database * implementation requires. */ int (*closeCache)(void *db); /** * @brief Read a local ZID from the database. * * The cache database may implement methods to generate and store local * ZRTP identifiers (ZID) and optionally link them with account * information. The account information data is the key to the request * local ZID. If the application does not provide account information data * the method implmentation shall use a standard predfined string that * does not collide with usual account information. * * The SQLite backend uses the string @c "_STANDARD_" in this case and * sets a specific type field. * * The first call to this method with a specific account information * generates a ZID, stores it in the database usind the account * information as key, and returns the ZID to the application. Any * subsequent call with the same account information return the same local * ZID. * * @param db Pointer to an internal structure that the database * implementation requires. * * @param localZid Pointer to a buffer of at least @c IDENTIFIER_LEN @c * uint8_t bytes. The method stores the local ZID in this * buffer. * * @param accountInfo Pointer to an account information string or @c NULL * if explicit account information is not required. * * @param errString Pointer to a character buffer, see implementation * notes above. */ int (*readLocalZid)(void *db, uint8_t *localZid, const char *accountInfo, char *errString); /** * @brief Read a remote ZID data structure. * * The method uses @c remoteZid and @c localZid as keys to read the remote * ZID record. If a record does not exist in the database the method * clears the @c flags field in the @c remoteZidRecord_t structure and * returns without error. The application must check the flags if the * method found a valid record. * * @param db Pointer to an internal structure that the database * implementation requires. * * @param localZid Pointer to a buffer of at least @c IDENTIFIER_LEN @c * uint8_t bytes. The buffer must contain the local ZID. * * @param remoteZid Pointer to a buffer of at least @c IDENTIFIER_LEN @c * uint8_t bytes. The buffer must contain the remote ZID. * * @param remZid Pointer to the @c remoteZidRecord_t structure. The method * fills this structure with data it read from the database. * * @param errString Pointer to a character buffer, see implementation * notes above. */ int (*readRemoteZidRecord)(void *db, const uint8_t *remoteZid, const uint8_t *localZid, remoteZidRecord_t *remZid, char* errString); /** * @brief Update an existing remote ZID data structure. * * The method uses @c remoteZid and @c localZid as keys to update an * existing remote ZID record. * * @b NOTE: application must use this methods only if * @c readRemoteZidRecord (see above) returned a @b valid record. If * @c readRemoteZidRecord returned an invalid record then no such * record exists in the database and the application must use the * @c insertRemoteZidRecord (see below). * * @param db Pointer to an internal structure that the database * implementation requires. * * @param localZid Pointer to a buffer of at least @c IDENTIFIER_LEN @c * uint8_t bytes. The buffer must contain the local ZID. * * @param remoteZid Pointer to a buffer of at least @c IDENTIFIER_LEN @c * uint8_t bytes. The buffer must contain the remote ZID. * * @param remZid Pointer to the @c remoteZidRecord_t structure. The method * gets data from this structure and stores it in the * database. * * @param errString Pointer to a character buffer, see implementation * notes above. */ int (*updateRemoteZidRecord)(void *db, const uint8_t *remoteZid, const uint8_t *localZid, const remoteZidRecord_t *remZid, char* errString); /** * @brief Insert a new remote ZID data structure. * * The method uses @c remoteZid and @c localZid as keys to insert a new * remote ZID record. * * @b NOTE: application must use this methods only if @c * readRemoteZidRecord (see above) returned an @b invalid * record. Refer to note. * * @param db Pointer to an internal structure that the database * implementation requires. * * @param localZid Pointer to a buffer of at least @c IDENTIFIER_LEN @c * uint8_t bytes. The buffer must contain the local ZID. * * @param remoteZid Pointer to a buffer of at least @c IDENTIFIER_LEN @c * uint8_t bytes. The buffer must contain the remote ZID. * * @param remZid Pointer to the @c remoteZidRecord_t structure. The method * gets data from this structure and stores it in the * database. * * @param errString Pointer to a character buffer, see implementation * notes above. */ int (*insertRemoteZidRecord)(void *db, const uint8_t *remoteZid, const uint8_t *localZid, const remoteZidRecord_t *remZid, char* errString); /** * @brief Read a remote ZID name. * * The method uses @c remoteZid, @c localZid, and @c accountInfo as keys * to read the remote ZID name. If a record does not exist in the database * the method clears the @c flags field in the @c zidNameRecord_t structure and * returns without error. The application must check the flags if the * method found a valid record. * * @param vdb Pointer to an internal structure that the database * implementation requires. * * @param localZid Pointer to a buffer of at least @c IDENTIFIER_LEN @c * uint8_t bytes. The buffer must contain the local ZID. * * @param remoteZid Pointer to a buffer of at least @c IDENTIFIER_LEN @c * uint8_t bytes. The buffer must contain the remote ZID. * * @param accountInfo Pointer to an account information string or @c NULL * if explicit account information is not required. * * @param zidName Pointer to the @c zidNameRecord_t structure. The method * returns the data in this structure. * * @param errString Pointer to a character buffer, see implementation * notes above. */ int (*readZidNameRecord)(void *vdb, const uint8_t *remoteZid, const uint8_t *localZid, const char *accountInfo, zidNameRecord_t *zidName, char* errString); /** * @brief Update an existing remote ZID data structure. * * The method uses @c remoteZid and @c localZid as keys to update an * existing remote ZID record. * * @b NOTE: application must use this methods only if * @c readZidName (see above) returned a @b valid record. If * @c readZidName returned an invalid record then no such * record exists in the database and the application must use the * @c insertZidNameRecord (see below). * * @param vdb Pointer to an internal structure that the database * implementation requires. * * @param localZid Pointer to a buffer of at least @c IDENTIFIER_LEN @c * uint8_t bytes. The buffer must contain the local ZID. * * @param remoteZid Pointer to a buffer of at least @c IDENTIFIER_LEN @c * uint8_t bytes. The buffer must contain the remote ZID. * * @param accountInfo Pointer to an account information string or @c NULL * if explicit account information is not required. * * @param zidName Pointer to the @c zidNameRecord_t structure. The method * gets data from this structure and stores it in the * database. * * @param errString Pointer to a character buffer, see implementation * notes above. */ int (*updateZidNameRecord)(void *vdb, const uint8_t *remoteZid, const uint8_t *localZid, const char *accountInfo, zidNameRecord_t *zidName, char* errString); /** * @brief Insert a new ZID name record. * * The method uses @c remoteZid, @c localZid, and @c accountInfo as keys to * insert a new ZID name record. * * @b NOTE: application must use this methods only if @c readZidName * (see above) returned an @b invalid record. * * @param db Pointer to an internal structure that the database * implementation requires. * * @param localZid Pointer to a buffer of at least @c IDENTIFIER_LEN @c * uint8_t bytes. The buffer must contain the local ZID. * * @param remoteZid Pointer to a buffer of at least @c IDENTIFIER_LEN @c * uint8_t bytes. The buffer must contain the remote ZID. * * @param accountInfo Pointer to an account information string or @c NULL * if explicit account information is not required. * * @param zidName Pointer to the @c zidNameRecord_t structure. The method * gets data from this structure and stores it in the * database. * * @param errString Pointer to a character buffer, see implementation * notes above. */ int (*insertZidNameRecord)(void *vdb, const uint8_t *remoteZid, const uint8_t *localZid, const char *accountInfo, zidNameRecord_t *zidName, char* errString); /** * @brief Clean the cache. * * The function drops and re-creates all tables in the database. This removes all stored * data. The application must not call this while a ZRTP call is active. Also the application * must get the local ZID again. * * @param db Pointer to an internal structure that the database * implementation requires. * * @param errString Pointer to a character buffer, see implementation * notes above. */ int (*cleanCache)(void *db, char* errString); /** * @brief Prepare a SQL cursor to read all records from the remote (peer) ZID table. * * The function creates a SQL cursor (prepares a statement in sqlite3 parlance) to * read all records from the table that contains the remote (peers') ZID data. * * This functions returns a pointer to the SQL cursor or @c NULL if it fails to * create a cursor. * * @param db Pointer to an internal structure that the database * implementation requires. * * @param errString Pointer to a character buffer, see implementation * notes above. * * @return a void pointer to the sqlite3 statment (SQL cursor) or @c NULL */ void *(*prepareReadAllZid)(void *db, char *errString); /** * @brief Read next ZID record from and SQL cursor. * * The function reads the next ZID record from a SQL cursor. If it cannot read a * record or encounters an error the function closes the cursor and returns @c NULL. * In this case the function must not use the SQL cursor pointer again. * * @param db Pointer to an internal structure that the database * implementation requires. * * @param stmt a void pointer to a sqlite3 statement (SQL cursor) * * @param remZid Pointer to the @c remoteZidRecord_t structure. The method * fills this structure with data it read from the database. * * @param errString Pointer to a character buffer, see implementation * notes above. * * @return void pointer to statment if successful, this is the same pointer as * the @c stmt input parameter. The function returns @c NULL if either * no more record is available or it got another error. */ void *(*readNextZidRecord)(void *db, void *stmt, remoteZidRecord_t *remZid, char* errString); /** * @brief Close sqlite3 statment (SQL cursor) * * This functions closes (finalizes) an open sqlite3 statment. Usually the * @c readNextZidRecord closes the statment if no more record is available. However, an * application may decide not to read every record. In this case it @b must close the * sqlite3 statment * * @param stmt a void pointer to a sqlite3 statement (SQL cursor) */ void (*closeStatement)(void *vstmt); } dbCacheOps_t; void getDbCacheOps(dbCacheOps_t *ops); #if defined(__cplusplus) } #endif #endif /* _ZRTP_CACHE_DB_BACKEND_H_*/ ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/libzrtpcpp/zrtpPacket.h ================================================ /* Copyright (C) 2006-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* * Authors: Werner Dittmann */ #ifndef ZRTPPACKET_H #define ZRTPPACKET_H /** * * @file zrtpPacket.h * @brief The data structures and definitions for ZRTP messages * * This include file defines the ZRTP message structures. Refer to * chapter 5 of the ZRTP specification which defines the ZRTP messages and * the transport format. * * @ingroup GNU_ZRTP * @{ */ #include /** * The following defines match the ZRTP specification, chapter 5 */ #define ZRTP_MAGIC 0x5a525450 #define ZRTP_WORD_SIZE 4 #define CRC_SIZE 4 #define TYPE_SIZE (2*ZRTP_WORD_SIZE) #define CLIENT_ID_SIZE (4*ZRTP_WORD_SIZE) #define HASH_IMAGE_SIZE (8*ZRTP_WORD_SIZE) #define ZID_SIZE (3*ZRTP_WORD_SIZE) #define HVI_SIZE (8*ZRTP_WORD_SIZE) #define HMAC_SIZE (2*ZRTP_WORD_SIZE) #define ID_SIZE (2*ZRTP_WORD_SIZE) #define IV_SIZE (4*ZRTP_WORD_SIZE) #define PING_HASH_SIZE (2*ZRTP_WORD_SIZE) /** * The ZRTP message header * * A complete ZRTP message always consists of the ZRTP header * and a message specific part. This specific part may have a variable * length. The length field includes the header. */ typedef struct zrtpPacketHeader { uint16_t zrtpId; ///< Id to identify the message, always 0x505a uint16_t length; ///< Length of the ZRTP message in words uint8_t messageType[TYPE_SIZE]; ///< 2 word (8 octest) message type in ASCII } zrtpPacketHeader_t; /** * Hello message, fixed part. * * The complete Hello message consists of ZRTP message header, Hello fixed * part and a variable part. The Hello class initializes the variable part. */ typedef struct Hello { uint8_t version[ZRTP_WORD_SIZE]; ///< Announces the ZRTP protocol version uint8_t clientId[CLIENT_ID_SIZE]; ///< A 4 word ASCII identifier of the ZRTP client uint8_t hashH3[HASH_IMAGE_SIZE]; ///< The last hash of the hash chain (chap. 9) uint8_t zid[ZID_SIZE]; ///< ZID - 3 word identifier for the ZRTP endpoint uint8_t flags; ///< flag bits (chap 7.2) uint8_t lengths[3]; ///< number of algorithms present } Hello_t; /** * The complete ZRTP Hello message. */ typedef struct HelloPacket { zrtpPacketHeader_t hdr; ///< ZRTP Header Hello_t hello; ///< Fixed part of Hello message } HelloPacket_t; /** * HelloAck message. * * The complete HelloAck message consists of ZRTP message header and * the CRC which is the only HelloAck specific data. */ typedef struct HelloAckPacket { zrtpPacketHeader_t hdr; ///< ZRTP Header uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message } HelloAckPacket_t; /** * Commit message * * There are three subtypes of Commit messages, each of which * has a fixed size. The data structure defines the maximum * Commit message. During the ZRTP protocol the implementation * uses fileds according to the use case (DH handshake, * Multi-stream handshake) and adjusts the length. */ typedef struct Commit { uint8_t hashH2[HASH_IMAGE_SIZE]; ///< The second hash of the hash chain (chap. 9) uint8_t zid[ZID_SIZE]; ///< ZID - 3 word identifier for the ZRTP endpoint uint8_t hash[ZRTP_WORD_SIZE]; ///< Commited hash algorithm uint8_t cipher[ZRTP_WORD_SIZE]; ///< Commited symmetrical cipher algorithm uint8_t authlengths[ZRTP_WORD_SIZE]; ///< Commited SRTP authentication algorithm uint8_t pubkey[ZRTP_WORD_SIZE]; ///< Commited key agreement algorithm uint8_t sas[ZRTP_WORD_SIZE]; ///< Commited SAS algorithm uint8_t hvi[HVI_SIZE]; ///< Hash value Initiator - chap 4.4.1.1 uint8_t hmac[HMAC_SIZE]; ///< MAC of the Commit message } Commit_t; /** * The complete ZRTP Commit message. */ typedef struct CommitPacket { zrtpPacketHeader_t hdr; ///< ZRTP Header Commit_t commit; ///< Commit message uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message } CommitPacket_t; /** * DHPart1 and DHPart2 messages * * The DHPart messages have a variable length. The following struct * defines the fixed part only. The DHPart class initializes the * variable part. */ typedef struct DHPart { uint8_t hashH1[HASH_IMAGE_SIZE]; ///< The first hash of the hash chain (chap. 9) uint8_t rs1Id[ID_SIZE]; ///< Id of first retained secret uint8_t rs2Id[ID_SIZE]; ///< Id of second retained secret uint8_t auxSecretId[ID_SIZE]; ///< Id of additional (auxilliary) secret uint8_t pbxSecretId[ID_SIZE]; ///< Id of PBX secret (chap 7.3.1) } DHPart_t; /** * The complete ZRTP DHPart message. */ typedef struct DHPartPacket { zrtpPacketHeader_t hdr; ///< ZRTP Header DHPart_t dhPart; ///< DHPart message fixed part } DHPartPacket_t; /** * Confirm1 and Confirm2 messages * * The Confirm message have a variable length. The following struct * defines the fixed part only. The Confirm class initializes the * variable part. * * ZRTP encrypts a part of the Confirm messages, starting at @c hashH0 * and includes the variable part. */ typedef struct Confirm { uint8_t hmac[HMAC_SIZE]; ///< MAC over the encrypted part of Commit message uint8_t iv[IV_SIZE]; ///< IV for CFB mode to encrypt part of Commit uint8_t hashH0[HASH_IMAGE_SIZE]; ///< starting hash of hash chain (chap. 9) uint8_t filler[2]; ///< Filler bytes uint8_t sigLength; ///< Length of an optional signature length (chap 7.2) uint8_t flags; ///< various flags to control behaviour uint32_t expTime; ///< Expiration time of retained secrets (chap 4.9) } Confirm_t; /** * The complete ZRTP Confirm message. */ typedef struct ConfirmPacket { zrtpPacketHeader_t hdr; ///< ZRTP Header Confirm_t confirm; ///< Confirm message fixed part } ConfirmPacket_t; /** * Conf2Ack message. * * The complete Conf2Ack message consists of ZRTP message header and * the CRC which is the only Conf2Ack specific data. */ typedef struct Conf2AckPacket { zrtpPacketHeader_t hdr; ///< ZRTP Header uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message } Conf2AckPacket_t; /** * The GoClear message is currently not used in * GNU ZRTP C++ - not support for GoClear. */ typedef struct GoClear { uint8_t clearHmac[HMAC_SIZE]; ///< no used } GoClear_t; /** * The complete ZRTP GoClear message - no used. */ typedef struct GoClearPacket { zrtpPacketHeader_t hdr; ///< ZRTP Header GoClear_t goClear; ///< not used uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message } GoClearPacket_t; /** * The ClearAck message is currently not used in * GNU ZRTP C++ - not support for GoClear. */ typedef struct ClearAckPacket { zrtpPacketHeader_t hdr; ///< ZRTP Header uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message } ClearAckPacket_t; /** * The Error message */ typedef struct Error { uint32_t errorCode; ///< Error code, see chap 5.9 } Error_t; /** * The complete ZRTP Error message. */ typedef struct ErrorPacket { zrtpPacketHeader_t hdr; ///< ZRTP Header Error_t error; ///< Error message part uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message } ErrorPacket_t; /** * ErrorAck message. * * The complete ErrorAck message consists of ZRTP message header and * the CRC which is the only ErrorAck specific data. */ typedef struct ErrorAckPacket { zrtpPacketHeader_t hdr; ///< ZRTP Header uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message } ErrorAckPacket_t; /** * Ping message. * * The Ping message has a fixed size. */ typedef struct Ping { uint8_t version[ZRTP_WORD_SIZE]; ///< The ZRTP protocol version uint8_t epHash[PING_HASH_SIZE]; ///< End point hash, see chap 5.16 } Ping_t; /** * The complete ZRTP Ping message. */ typedef struct PingPacket { zrtpPacketHeader_t hdr; ///< ZRTP Header Ping_t ping; ///< Ping message part uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message } PingPacket_t; /** * PingAck message. * * The PingAck message has a fixed size. */ typedef struct PingAck { uint8_t version[ZRTP_WORD_SIZE]; ///< The ZRTP protocol version uint8_t localEpHash[PING_HASH_SIZE]; ///< Local end point hash, see chap 5.16 uint8_t remoteEpHash[PING_HASH_SIZE]; ///< Remote end point hash, see chap 5.16 uint32_t ssrc; ///< SSRC copied from the Ping message (RTP packet part) } PingAck_t; /** * The complete ZRTP PingAck message. */ typedef struct PingAckPacket { zrtpPacketHeader_t hdr; ///< ZRTP Header PingAck_t pingAck; ///< PingAck message part uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message } PingAckPacket_t; /** * SASrelay message * * The SASrelay message has a variable length. The following struct * defines the fixed part only. The SASrelay class initializes the * variable part. * * ZRTP encrypts a part of the SASrelay message, starting at @c hashH0 * and includes the variable part. */ typedef struct SASrelay { uint8_t hmac[HMAC_SIZE]; ///< MAC over the encrypted part of Commit message uint8_t iv[IV_SIZE]; ///< IV for CFB mode to encrypt part of Commit uint8_t filler[2]; ///< Filler bytes uint8_t sigLength; ///< Length of an optional signature length (chap 7.2) uint8_t flags; ///< various flags to control behaviour uint8_t sas[ZRTP_WORD_SIZE]; ///< SAS algorithm to use uint8_t trustedSasHash[HASH_IMAGE_SIZE]; ///< New trusted SAS hash for enrolled client } SASrelay_t; /** * The complete ZRTP SASrelay message. */ typedef struct SASrelayPacket { zrtpPacketHeader_t hdr; ///< ZRTP Header SASrelay_t sasrelay; ///< SASrelay message fixed part } SASrelayPacket_t; /** * RelayAck message. * * The complete RelayAck message consists of ZRTP message header and * the CRC which is the only RelayAck specific data. */ typedef struct RelayAckPacket { zrtpPacketHeader_t hdr; ///< ZRTP Header uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message } RelayAckPacket_t; #endif // ZRTPPACKET_H /** * @} */ /** EMACS ** * Local variables: * mode: c++ * c-default-style: ellemtel * c-basic-offset: 4 * End: */ ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/zrtpB64Decode.c ================================================ /* * cdecoder.c - c source to a base64 decoding algorithm implementation * * This is part of the libb64 project, and has been placed in the public domain. * For details, see http://sourceforge.net/projects/libb64 */ #include int base64_decode_value(char value_in) { static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; static const char decoding_size = sizeof(decoding); value_in -= 43; if (value_in < 0 || value_in > decoding_size) return -1; return decoding[(int)value_in]; } void base64_init_decodestate(base64_decodestate* state_in) { state_in->step = step_a; state_in->plainchar = 0; } int base64_decode_block(const char* code_in, const int length_in, uint8_t *plaintext_out, base64_decodestate* state_in) { const char* codechar = code_in; uint8_t *plainchar = plaintext_out; char fragment; *plainchar = state_in->plainchar; switch (state_in->step) { while (1) { case step_a: do { if (codechar == code_in+length_in) { state_in->step = step_a; state_in->plainchar = *plainchar; return plainchar - plaintext_out; } fragment = (char)base64_decode_value(*codechar++); } while (fragment < 0); *plainchar = (fragment & 0x03f) << 2; case step_b: do { if (codechar == code_in+length_in) { state_in->step = step_b; state_in->plainchar = *plainchar; return plainchar - plaintext_out; } fragment = (char)base64_decode_value(*codechar++); } while (fragment < 0); *plainchar++ |= (fragment & 0x030) >> 4; *plainchar = (fragment & 0x00f) << 4; case step_c: do { if (codechar == code_in+length_in) { state_in->step = step_c; state_in->plainchar = *plainchar; return plainchar - plaintext_out; } fragment = (char)base64_decode_value(*codechar++); } while (fragment < 0); *plainchar++ |= (fragment & 0x03c) >> 2; *plainchar = (fragment & 0x003) << 6; case step_d: do { if (codechar == code_in+length_in) { state_in->step = step_d; state_in->plainchar = *plainchar; return plainchar - plaintext_out; } fragment = (char)base64_decode_value(*codechar++); } while (fragment < 0); *plainchar++ |= (fragment & 0x03f); } } /* control should not reach here */ return plainchar - plaintext_out; } ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/zrtpB64Encode.c ================================================ /* cencoder.c - c source to a base64 encoding algorithm implementation This is part of the libb64 project, and has been placed in the public domain. For details, see http://sourceforge.net/projects/libb64 */ #include const int CHARS_PER_LINE = 72; void base64_init_encodestate(base64_encodestate* state_in, int lineLength) { state_in->step = step_A; state_in->result = 0; state_in->stepcount = 0; if (lineLength < 0) state_in->lineLength = CHARS_PER_LINE / 4; else state_in->lineLength = (lineLength+3) / 4; } char base64_encode_value(const int8_t value_in) { static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; if (value_in > 63) return '='; return encoding[(int)value_in]; } int base64_encode_block(const uint8_t *plaintext_in, int length_in, char* code_out, base64_encodestate* state_in) { const uint8_t *plainchar = plaintext_in; const uint8_t *const plaintextend = plaintext_in + length_in; char* codechar = code_out; char result; char fragment; result = state_in->result; switch (state_in->step) { while (1) { case step_A: if (plainchar == plaintextend) { state_in->result = result; state_in->step = step_A; return codechar - code_out; } fragment = *plainchar++; result = (fragment & 0x0fc) >> 2; *codechar++ = base64_encode_value(result); result = (fragment & 0x003) << 4; case step_B: if (plainchar == plaintextend) { state_in->result = result; state_in->step = step_B; return codechar - code_out; } fragment = *plainchar++; result |= (fragment & 0x0f0) >> 4; *codechar++ = base64_encode_value(result); result = (fragment & 0x00f) << 2; case step_C: if (plainchar == plaintextend) { state_in->result = result; state_in->step = step_C; return codechar - code_out; } fragment = *plainchar++; result |= (fragment & 0x0c0) >> 6; *codechar++ = base64_encode_value(result); result = (fragment & 0x03f) >> 0; *codechar++ = base64_encode_value(result); if (state_in->lineLength > 0) { state_in->stepcount++; if (state_in->stepcount == state_in->lineLength) { *codechar++ = '\n'; state_in->stepcount = 0; } } } } /* control should not reach here */ return codechar - code_out; } int base64_encode_blockend(char* code_out, base64_encodestate* state_in) { char* codechar = code_out; switch (state_in->step) { case step_B: *codechar++ = base64_encode_value(state_in->result); *codechar++ = '='; *codechar++ = '='; break; case step_C: *codechar++ = base64_encode_value(state_in->result); *codechar++ = '='; break; case step_A: break; } if (state_in->lineLength > 0) *codechar++ = '\n'; return codechar - code_out; } ================================================ FILE: deps/pjsip/third_party/zsrtp/zrtp/zrtp/zrtpCacheSqliteBackend.c ================================================ /* Copyright (C) 2012-2013 Werner Dittmann This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* * Authors: Werner Dittmann */ #include #include #include #include #include #include #include #include #include #include /* Some ported SQLite3 libs do not support the _v2 variants */ #define SQLITE_USE_V2 #ifdef SQLITE_USE_V2 #define SQLITE_PREPARE sqlite3_prepare_v2 #else #define SQLITE_PREPARE sqlite3_prepare #endif #if defined(_WIN32) || defined(_WIN64) # define snprintf _snprintf #else # include # include # include #endif #ifdef TRANSACTIONS static const char *beginTransactionSql = "BEGIN TRANSACTION;"; static const char *commitTransactionSql = "COMMIT;"; #endif /* * The database backend uses the following definitions if it implements the localZid storage. */ /* The type field in zrtpIdOwn stores the following values */ static const int32_t localZidStandard = 1; /* this local ZID is not tied to a specific account */ static const int32_t localZidWithAccount = 2; /* Default data for account info if none specified */ static const char *defaultAccountString = "_STANDARD_"; /* ***************************************************************************** * The SQLite master table. * * Used to check if we have valid ZRTP cache tables. */ static char *lookupTables = "SELECT name FROM sqlite_master WHERE type='table' AND name='zrtpIdOwn';"; /* ***************************************************************************** * SQL statements to process the zrtpIdOwn table. */ static const char *dropZrtpIdOwn = "DROP TABLE zrtpIdOwn;"; /* SQLite doesn't care about the VARCHAR length. */ static char *createZrtpIdOwn = "CREATE TABLE zrtpIdOwn(localZid CHAR(18), type INTEGER, accountInfo VARCHAR(1000));"; static char *selectZrtpIdOwn = "SELECT localZid FROM zrtpIdOwn WHERE type = ?1 AND accountInfo = ?2;"; static char *insertZrtpIdOwn = "INSERT INTO zrtpIdOwn (localZid, type, accountInfo) VALUES (?1, ?2, ?3);"; /* ***************************************************************************** * SQL statements to process the remoteId table. */ static const char *dropZrtpIdRemote = "DROP TABLE zrtpIdRemote;"; static const char *createZrtpIdRemote = "CREATE TABLE zrtpIdRemote " "(remoteZid CHAR(16), localZid CHAR(16), flags INTEGER," "rs1 BLOB(32), rs1LastUsed TIMESTAMP, rs1TimeToLive TIMESTAMP," "rs2 BLOB(32), rs2LastUsed TIMESTAMP, rs2TimeToLive TIMESTAMP," "mitmKey BLOB(32), mitmLastUsed TIMESTAMP, secureSince TIMESTAMP, preshCounter INTEGER);"; static const char *selectZrtpIdRemoteAll = "SELECT flags," "rs1, strftime('%s', rs1LastUsed, 'unixepoch'), strftime('%s', rs1TimeToLive, 'unixepoch')," "rs2, strftime('%s', rs2LastUsed, 'unixepoch'), strftime('%s', rs2TimeToLive, 'unixepoch')," "mitmKey, strftime('%s', mitmLastUsed, 'unixepoch'), strftime('%s', secureSince, 'unixepoch')," "preshCounter " "FROM zrtpIdRemote WHERE remoteZid=?1 AND localZid=?2;"; static const char *insertZrtpIdRemote = "INSERT INTO zrtpIdRemote " "(remoteZid, localZid, flags," "rs1, rs1LastUsed, rs1TimeToLive," "rs2, rs2LastUsed, rs2TimeToLive," "mitmKey, mitmLastUsed, secureSince, preshCounter)" "VALUES" "(?1, ?12, ?2," "?3, strftime('%s', ?4, 'unixepoch'), strftime('%s', ?5, 'unixepoch')," "?6, strftime('%s', ?7, 'unixepoch'), strftime('%s', ?8, 'unixepoch')," "?9, strftime('%s', ?10, 'unixepoch'), strftime('%s', ?11, 'unixepoch'), ?13);"; static const char *updateZrtpIdRemote = "UPDATE zrtpIdRemote SET " "flags=?2," "rs1=?3, rs1LastUsed=strftime('%s', ?4, 'unixepoch'), rs1TimeToLive=strftime('%s', ?5, 'unixepoch')," "rs2=?6, rs2LastUsed=strftime('%s', ?7, 'unixepoch'), rs2TimeToLive=strftime('%s', ?8, 'unixepoch')," "mitmKey=?9, mitmLastUsed=strftime('%s', ?10, 'unixepoch')," "secureSince=strftime('%s', ?11, 'unixepoch'), preshCounter=?13 " "WHERE remoteZid=?1 AND localZid=?12;"; static const char *selectZrtpIdRemoteAllNoCondition = "SELECT flags," "rs1, strftime('%s', rs1LastUsed, 'unixepoch'), strftime('%s', rs1TimeToLive, 'unixepoch')," "rs2, strftime('%s', rs2LastUsed, 'unixepoch'), strftime('%s', rs2TimeToLive, 'unixepoch')," "mitmKey, strftime('%s', mitmLastUsed, 'unixepoch'), strftime('%s', secureSince, 'unixepoch')," "preshCounter, remoteZid " "FROM zrtpIdRemote ORDER BY secureSince DESC;"; /* ***************************************************************************** * SQL statements to process the name table. * * The name tables holds free format information and binds it to the combination * of local, remote ZIDs and an optional account information. */ static const char *dropZrtpNames = "DROP TABLE zrtpNames;"; static const char *createZrtpNames = "CREATE TABLE zrtpNames " "(remoteZid CHAR(16), localZid CHAR(16), flags INTEGER, " "lastUpdate TIMESTAMP, accountInfo VARCHAR(1000), name VARCHAR(1000));"; static const char *selectZrtpNames = "SELECT flags, strftime('%s', lastUpdate, 'unixepoch'), name " "FROM zrtpNames " "WHERE remoteZid=?1 AND localZid=?2 AND accountInfo=?3;"; static const char *insertZrtpNames = "INSERT INTO zrtpNames " "(remoteZid, localZid, flags, lastUpdate, accountInfo, name)" "VALUES" "(?1, ?2, ?4, strftime('%s', ?5, 'unixepoch'), ?3, ?6);"; static const char *updateZrtpNames = "UPDATE zrtpNames SET " "flags=?4," "lastUpdate=strftime('%s', ?5, 'unixepoch'), name=?6 " "WHERE remoteZid=?1 AND localZid=?2 AND accountInfo=?3;"; /* ***************************************************************************** * A few helping macros. * These macros require some names/patterns in the methods that use these * macros: * * ERRMSG requires: * - a variable with name "db" is the pointer to sqlite3 * - a char* with name "errString" points to a buffer of at least SQL_CACHE_ERR_BUFF_SIZE chars * * SQLITE_CHK requires: * - a cleanup label, the macro goes to that label in case of error * - an integer (int) variable with name "rc" that stores return codes from sqlite * - ERRMSG */ #define ERRMSG {if (errString) snprintf(errString, (size_t)DB_CACHE_ERR_BUFF_SIZE, \ "SQLite3 error: %s, line: %d, error message: %s\n", __FILE__, __LINE__, sqlite3_errmsg(db));} #define SQLITE_CHK(func) { \ rc = (func); \ if(rc != SQLITE_OK) { \ ERRMSG; \ goto cleanup; \ } \ } static int b64Encode(const uint8_t *binData, int32_t binLength, char *b64Data, int32_t b64Length) { base64_encodestate _state; int codelength; base64_init_encodestate(&_state, 0); codelength = base64_encode_block(binData, binLength, b64Data, &_state); codelength += base64_encode_blockend(b64Data+codelength, &_state); return codelength; } static int b64Decode(const char *b64Data, int32_t b64length, uint8_t *binData, int32_t binLength) { base64_decodestate _state; int codelength; base64_init_decodestate(&_state); codelength = base64_decode_block(b64Data, b64length, binData, &_state); return codelength; } #ifdef TRANSACTIONS static int beginTransaction(sqlite3 *db, char* errString) { sqlite3_stmt *stmt; int rc; SQLITE_CHK(SQLITE_PREPARE(db, beginTransactionSql, strlen(beginTransactionSql)+1, &stmt, NULL)); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { ERRMSG; return rc; } return SQLITE_OK; cleanup: sqlite3_finalize(stmt); return rc; } static int commitTransaction(sqlite3 *db, char* errString) { sqlite3_stmt *stmt; int rc; SQLITE_CHK(SQLITE_PREPARE(db, commitTransactionSql, strlen(commitTransactionSql)+1, &stmt, NULL)); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { ERRMSG; return rc; } return SQLITE_OK; cleanup: sqlite3_finalize(stmt); return rc; } #endif /** * Initialize remote ZID and remote name tables. * * First drop the remote ZID and remote name tables and create them again. * All information regarding remote peers is lost. */ static int initializeRemoteTables(sqlite3 *db, char* errString) { sqlite3_stmt * stmt; int rc; /* First drop them, just to be on the save side * Ignore errors, there is nothing to drop on empty DB. If ZrtpIdOwn was * deleted using DB admin command then we need to drop the remote id table * and names also to have a clean state. */ rc = SQLITE_PREPARE(db, dropZrtpIdRemote, strlen(dropZrtpIdRemote)+1, &stmt, NULL); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); rc = SQLITE_PREPARE(db, dropZrtpNames, strlen(dropZrtpNames)+1, &stmt, NULL); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); SQLITE_CHK(SQLITE_PREPARE(db, createZrtpIdRemote, strlen(createZrtpIdRemote)+1, &stmt, NULL)); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { ERRMSG; return rc; } SQLITE_CHK(SQLITE_PREPARE(db, createZrtpNames, strlen(createZrtpNames)+1, &stmt, NULL)); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { ERRMSG; return rc; } return 0; cleanup: sqlite3_finalize(stmt); return rc; } /** * Create ZRTP cache tables in database. * * openCache calls this function if it cannot find the table zrtpId_own. This indicates * that no ZRTP cache tables are available in the database. */ static int createTables(sqlite3 *db, char* errString) { sqlite3_stmt * stmt; int rc; /* no ZRTP cache tables were found - create them, first the OwnId table */ SQLITE_CHK(SQLITE_PREPARE(db, createZrtpIdOwn, strlen(createZrtpIdOwn)+1, &stmt, NULL)); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { ERRMSG; return rc; } return initializeRemoteTables(db, errString); cleanup: sqlite3_finalize(stmt); return rc; } static int insertRemoteZidRecord(void *vdb, const uint8_t *remoteZid, const uint8_t *localZid, const remoteZidRecord_t *remZid, char* errString) { sqlite3 *db = (sqlite3*)vdb; sqlite3_stmt *stmt; int rc = 0; char b64RemoteZid[IDENTIFIER_LEN*2] = {0}; char b64LocalZid[IDENTIFIER_LEN*2] = {0}; /* Get B64 code for remoteZid first */ b64Encode(remoteZid, IDENTIFIER_LEN, b64RemoteZid, IDENTIFIER_LEN*2); /* Get B64 code for localZid now */ b64Encode(localZid, IDENTIFIER_LEN, b64LocalZid, IDENTIFIER_LEN*2); SQLITE_CHK(SQLITE_PREPARE(db, insertZrtpIdRemote, strlen(insertZrtpIdRemote)+1, &stmt, NULL)); /* For *_bind_* methods: column index starts with 1 (one), not zero */ SQLITE_CHK(sqlite3_bind_text(stmt, 1, b64RemoteZid, strlen(b64RemoteZid), SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_text(stmt, 12, b64LocalZid, strlen(b64LocalZid), SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_int(stmt, 2, remZid->flags)); SQLITE_CHK(sqlite3_bind_blob(stmt, 3, remZid->rs1, RS_LENGTH, SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_int64(stmt, 4, remZid->rs1LastUse)); SQLITE_CHK(sqlite3_bind_int64(stmt, 5, remZid->rs1Ttl)); SQLITE_CHK(sqlite3_bind_blob(stmt, 6, remZid->rs2, RS_LENGTH, SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_int64(stmt, 7, remZid->rs2LastUse)); SQLITE_CHK(sqlite3_bind_int64(stmt, 8, remZid->rs2Ttl)); SQLITE_CHK(sqlite3_bind_blob(stmt, 9, remZid->mitmKey, RS_LENGTH, SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_int64(stmt, 10, remZid->mitmLastUse)); SQLITE_CHK(sqlite3_bind_int64(stmt, 11, remZid->secureSince)); SQLITE_CHK(sqlite3_bind_int(stmt, 13, remZid->preshCounter)); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { ERRMSG; return rc; } return SQLITE_OK; cleanup: sqlite3_finalize(stmt); return rc; } static int updateRemoteZidRecord(void *vdb, const uint8_t *remoteZid, const uint8_t *localZid, const remoteZidRecord_t *remZid, char* errString) { sqlite3 *db = (sqlite3*)vdb; sqlite3_stmt *stmt; int rc; char b64RemoteZid[IDENTIFIER_LEN*2] = {0}; char b64LocalZid[IDENTIFIER_LEN*2] = {0}; /* Get B64 code for remoteZid first */ b64Encode(remoteZid, IDENTIFIER_LEN, b64RemoteZid, IDENTIFIER_LEN*2); /* Get B64 code for localZid now */ b64Encode(localZid, IDENTIFIER_LEN, b64LocalZid, IDENTIFIER_LEN*2); SQLITE_CHK(SQLITE_PREPARE(db, updateZrtpIdRemote, strlen(updateZrtpIdRemote)+1, &stmt, NULL)); /* For *_bind_* methods: column index starts with 1 (one), not zero */ /* Select for update with the following keys */ SQLITE_CHK(sqlite3_bind_text(stmt, 1, b64RemoteZid, strlen(b64RemoteZid), SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_text(stmt, 12, b64LocalZid, strlen(b64LocalZid), SQLITE_STATIC)); /* Update the following values */ SQLITE_CHK(sqlite3_bind_int(stmt, 2, remZid->flags)); SQLITE_CHK(sqlite3_bind_blob(stmt, 3, remZid->rs1, RS_LENGTH, SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_int64(stmt, 4, remZid->rs1LastUse)); SQLITE_CHK(sqlite3_bind_int64(stmt, 5, remZid->rs1Ttl)); SQLITE_CHK(sqlite3_bind_blob(stmt, 6, remZid->rs2, RS_LENGTH, SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_int64(stmt, 7, remZid->rs2LastUse)); SQLITE_CHK(sqlite3_bind_int64(stmt, 8, remZid->rs2Ttl)); SQLITE_CHK(sqlite3_bind_blob(stmt, 9, remZid->mitmKey, RS_LENGTH, SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_int64(stmt, 10, remZid->mitmLastUse)); SQLITE_CHK(sqlite3_bind_int64(stmt, 11, remZid->secureSince)); SQLITE_CHK(sqlite3_bind_int(stmt, 13, remZid->preshCounter)); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { ERRMSG; return rc; } return SQLITE_OK; cleanup: sqlite3_finalize(stmt); return rc; } static int readRemoteZidRecord(void *vdb, const uint8_t *remoteZid, const uint8_t *localZid, remoteZidRecord_t *remZid, char* errString) { sqlite3 *db = (sqlite3*)vdb; sqlite3_stmt *stmt; int rc; int found = 0; char b64RemoteZid[IDENTIFIER_LEN*2] = {0}; char b64LocalZid[IDENTIFIER_LEN*2] = {0}; /* Get B64 code for remoteZid */ b64Encode(remoteZid, IDENTIFIER_LEN, b64RemoteZid, IDENTIFIER_LEN*2); /* Get B64 code for localZid */ b64Encode(localZid, IDENTIFIER_LEN, b64LocalZid, IDENTIFIER_LEN*2); SQLITE_CHK(SQLITE_PREPARE(db, selectZrtpIdRemoteAll, strlen(selectZrtpIdRemoteAll)+1, &stmt, NULL)); SQLITE_CHK(sqlite3_bind_text(stmt, 1, b64RemoteZid, strlen(b64RemoteZid), SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_text(stmt, 2, b64LocalZid, strlen(b64LocalZid), SQLITE_STATIC)); /* Getting data from result set: column index starts with 0 (zero), not one */ while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { remZid->flags = sqlite3_column_int(stmt, 0); memcpy(remZid->rs1, sqlite3_column_blob(stmt, 1), RS_LENGTH); remZid->rs1LastUse = sqlite3_column_int64(stmt, 2); remZid->rs1Ttl = sqlite3_column_int64(stmt, 3); memcpy(remZid->rs2, sqlite3_column_blob(stmt, 4), RS_LENGTH); remZid->rs2LastUse = sqlite3_column_int64(stmt, 5); remZid->rs2Ttl = sqlite3_column_int64(stmt, 6); memcpy(remZid->mitmKey, sqlite3_column_blob(stmt, 7), RS_LENGTH); remZid->mitmLastUse = sqlite3_column_int64(stmt, 8); remZid->secureSince = sqlite3_column_int64(stmt, 9); remZid->preshCounter = sqlite3_column_int(stmt, 10); found++; } sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { ERRMSG; return rc; } if (found == 0) { remZid->flags = 0; } else if (found > 1) { if (errString) snprintf(errString, DB_CACHE_ERR_BUFF_SIZE, "ZRTP cache inconsistent. More than one remote ZID found: %d\n", found); return 1; } return SQLITE_OK; cleanup: sqlite3_finalize(stmt); return rc; } static int readLocalZid(void *vdb, uint8_t *localZid, const char *accountInfo, char *errString) { sqlite3 *db = (sqlite3*)vdb; sqlite3_stmt *stmt; char *zidBase64Text; int rc = 0; int found = 0; int type = localZidWithAccount; if (accountInfo == NULL || !strcmp(accountInfo, defaultAccountString)) { accountInfo = defaultAccountString; type = localZidStandard; } /* Find a localZid record for this combination */ SQLITE_CHK(SQLITE_PREPARE(db, selectZrtpIdOwn, strlen(selectZrtpIdOwn)+1, &stmt, NULL)); SQLITE_CHK(sqlite3_bind_int(stmt, 1, type)); SQLITE_CHK(sqlite3_bind_text(stmt, 2, accountInfo, strlen(accountInfo), SQLITE_STATIC)); /* Loop over result set and count it. However, use only the localZid of first row */ while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { if (found == 0) { zidBase64Text = (char *)sqlite3_column_text(stmt, 0); b64Decode(zidBase64Text, strlen(zidBase64Text), localZid, IDENTIFIER_LEN); } found++; } sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { ERRMSG; return rc; } /* No matching record found, create new local ZID for this combination and store in DB */ if (found == 0) { char b64zid[IDENTIFIER_LEN+IDENTIFIER_LEN] = {0}; int b64len = 0; /* create a 12 byte random value, convert to base 64, insert in zrtpIdOwn table */ randomZRTP(localZid, IDENTIFIER_LEN); b64len = b64Encode(localZid, IDENTIFIER_LEN, b64zid, IDENTIFIER_LEN+IDENTIFIER_LEN); SQLITE_CHK(SQLITE_PREPARE(db, insertZrtpIdOwn, strlen(insertZrtpIdOwn)+1, &stmt, NULL)); SQLITE_CHK(sqlite3_bind_text(stmt, 1, b64zid, b64len, SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_int(stmt, 2, type)); SQLITE_CHK(sqlite3_bind_text(stmt, 3, accountInfo, strlen(accountInfo), SQLITE_STATIC)); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { ERRMSG; return rc; } } else if (found > 1) { if (errString) snprintf(errString, DB_CACHE_ERR_BUFF_SIZE, "ZRTP cache inconsistent. Found %d matching local ZID for account: %s\n", found, accountInfo); return 1; } return SQLITE_OK; cleanup: sqlite3_finalize(stmt); return rc; } /* * SQLite use the following table structure to manage some internal data * * CREATE TABLE sqlite_master ( * type TEXT, * name TEXT, * tbl_name TEXT, * rootpage INTEGER, * sql TEXT * ); */ static int openCache(const char* name, void **vpdb, char *errString) { sqlite3_stmt *stmt; int found = 0; sqlite3 **pdb = (sqlite3**)vpdb; sqlite3 *db; #if !defined(_WIN32) && !defined(_WIN64) /* try to create the file with 0600 permissions */ int fd = open(name, O_CREAT | O_RDWR, 0600); if (fd != -1) { close(fd); } #endif #ifdef SQLITE_USE_V2 int rc = sqlite3_open_v2(name, pdb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL); #else int rc = sqlite3_open(name, pdb); #endif db = *pdb; if (rc) { ERRMSG; return(rc); } /* check if ZRTP cache tables are already available, look if zrtpIdOwn is available */ SQLITE_CHK(SQLITE_PREPARE(db, lookupTables, strlen(lookupTables)+1, &stmt, NULL)); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc == SQLITE_ROW) { found++; } else if (rc != SQLITE_DONE) { ERRMSG; return rc; } /* If table zrtpOwnId not found then we have an empty cache DB */ if (found == 0) { rc = createTables(db, errString); if (rc) return rc; } return SQLITE_OK; cleanup: sqlite3_finalize(stmt); return rc; } static int closeCache(void *vdb) { sqlite3 *db = (sqlite3*)vdb; sqlite3_close(db); return SQLITE_OK; } static int clearCache(void *vdb, char *errString) { sqlite3 *db = (sqlite3*)vdb; sqlite3_stmt * stmt; int rc; rc = SQLITE_PREPARE(db, dropZrtpIdOwn, strlen(dropZrtpIdOwn)+1, &stmt, NULL); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); rc = createTables(db, errString); if (rc) return rc; return SQLITE_OK; } static int insertZidNameRecord(void *vdb, const uint8_t *remoteZid, const uint8_t *localZid, const char *accountInfo, zidNameRecord_t *zidName, char* errString) { sqlite3 *db = (sqlite3*)vdb; sqlite3_stmt *stmt; int rc = 0; char b64RemoteZid[IDENTIFIER_LEN*2] = {0}; char b64LocalZid[IDENTIFIER_LEN*2] = {0}; if (accountInfo == NULL) { accountInfo = defaultAccountString; } /* Get B64 code for remoteZid */ b64Encode(remoteZid, IDENTIFIER_LEN, b64RemoteZid, IDENTIFIER_LEN*2); /* Get B64 code for localZid */ b64Encode(localZid, IDENTIFIER_LEN, b64LocalZid, IDENTIFIER_LEN*2); SQLITE_CHK(SQLITE_PREPARE(db, insertZrtpNames, strlen(insertZrtpNames)+1, &stmt, NULL)); /* For *_bind_* methods: column index starts with 1 (one), not zero */ SQLITE_CHK(sqlite3_bind_text(stmt, 1, b64RemoteZid, strlen(b64RemoteZid), SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_text(stmt, 2, b64LocalZid, strlen(b64LocalZid), SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_text(stmt, 3, accountInfo, strlen(accountInfo), SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_int(stmt, 4, zidName->flags)); SQLITE_CHK(sqlite3_bind_int64(stmt, 5, (int64_t)time(NULL))); if (zidName->name != NULL) { SQLITE_CHK(sqlite3_bind_text(stmt, 6, zidName->name, strlen(zidName->name), SQLITE_STATIC)); } else { SQLITE_CHK(sqlite3_bind_text(stmt, 6, "_NO_NAME_", 9, SQLITE_STATIC)); } rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { ERRMSG; return rc; } return SQLITE_OK; cleanup: sqlite3_finalize(stmt); return rc; } static int updateZidNameRecord(void *vdb, const uint8_t *remoteZid, const uint8_t *localZid, const char *accountInfo, zidNameRecord_t *zidName, char* errString) { sqlite3 *db = (sqlite3*)vdb; sqlite3_stmt *stmt; int rc = 0; char b64RemoteZid[IDENTIFIER_LEN*2] = {0}; char b64LocalZid[IDENTIFIER_LEN*2] = {0}; if (accountInfo == NULL) { accountInfo = defaultAccountString; } /* Get B64 code for remoteZid */ b64Encode(remoteZid, IDENTIFIER_LEN, b64RemoteZid, IDENTIFIER_LEN*2); /* Get B64 code for localZid */ b64Encode(localZid, IDENTIFIER_LEN, b64LocalZid, IDENTIFIER_LEN*2); SQLITE_CHK(SQLITE_PREPARE(db, updateZrtpNames, strlen(updateZrtpNames)+1, &stmt, NULL)); /* For *_bind_* methods: column index starts with 1 (one), not zero */ /* Select for update with the following values */ SQLITE_CHK(sqlite3_bind_text(stmt, 1, b64RemoteZid, strlen(b64RemoteZid), SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_text(stmt, 2, b64LocalZid, strlen(b64LocalZid), SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_text(stmt, 3, accountInfo, strlen(accountInfo), SQLITE_STATIC)); /* Update the following vaulues */ SQLITE_CHK(sqlite3_bind_int(stmt, 4, zidName->flags)); SQLITE_CHK(sqlite3_bind_int64(stmt, 5, (int64_t)time(NULL))); if (zidName->name != NULL) { SQLITE_CHK(sqlite3_bind_text(stmt, 6, zidName->name, strlen(zidName->name), SQLITE_STATIC)); } else { SQLITE_CHK(sqlite3_bind_text(stmt, 6, "_NO_NAME_", 9, SQLITE_STATIC)); } rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { ERRMSG; return rc; } return SQLITE_OK; cleanup: sqlite3_finalize(stmt); return rc; } static int readZidNameRecord(void *vdb, const uint8_t *remoteZid, const uint8_t *localZid, const char *accountInfo, zidNameRecord_t *zidName, char* errString) { sqlite3 *db = (sqlite3*)vdb; sqlite3_stmt *stmt; int rc; int found = 0; char b64RemoteZid[IDENTIFIER_LEN*2] = {0}; char b64LocalZid[IDENTIFIER_LEN*2] = {0}; if (accountInfo == NULL) { accountInfo = defaultAccountString; } /* Get B64 code for remoteZid */ b64Encode(remoteZid, IDENTIFIER_LEN, b64RemoteZid, IDENTIFIER_LEN*2); /* Get B64 code for localZid */ b64Encode(localZid, IDENTIFIER_LEN, b64LocalZid, IDENTIFIER_LEN*2); SQLITE_CHK(SQLITE_PREPARE(db, selectZrtpNames, strlen(selectZrtpNames)+1, &stmt, NULL)); SQLITE_CHK(sqlite3_bind_text(stmt, 1, b64RemoteZid, strlen(b64RemoteZid), SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_text(stmt, 2, b64LocalZid, strlen(b64LocalZid), SQLITE_STATIC)); SQLITE_CHK(sqlite3_bind_text(stmt, 3, accountInfo, strlen(accountInfo), SQLITE_STATIC)); /* Getting data from result set: column index starts with 0 (zero), not one */ while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { zidName->flags = sqlite3_column_int(stmt, 0); strncpy(zidName->name, (const char*)sqlite3_column_text(stmt, 2), zidName->nameLength); zidName->nameLength = sqlite3_column_bytes(stmt, 2); /* Return number of bytes in string */ found++; } sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { ERRMSG; return rc; } if (found == 0) zidName->flags = 0; else if (found > 1) { if (errString) snprintf(errString, DB_CACHE_ERR_BUFF_SIZE, "ZRTP name cache inconsistent. More than one ZID name found: %d\n", found); return 1; } return SQLITE_OK; cleanup: sqlite3_finalize(stmt); return rc; } static void *prepareReadAllZid(void *vdb, char *errString) { sqlite3 *db = (sqlite3*)vdb; sqlite3_stmt *stmt; int rc; SQLITE_CHK(SQLITE_PREPARE(db, selectZrtpIdRemoteAllNoCondition, strlen(selectZrtpIdRemoteAllNoCondition)+1, &stmt, NULL)); return stmt; cleanup: sqlite3_finalize(stmt); return NULL; } static void *readNextZidRecord(void *vdb, void *vstmt, remoteZidRecord_t *remZid, char* errString) { sqlite3 *db = (sqlite3*)vdb; sqlite3_stmt *stmt; char *zidBase64Text; int rc; if (vstmt == NULL) return NULL; stmt = (sqlite3_stmt*)vstmt; /* Getting data from result set: column index starts with 0 (zero), not one */ if ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { remZid->flags = sqlite3_column_int(stmt, 0); memcpy(remZid->rs1, sqlite3_column_blob(stmt, 1), RS_LENGTH); remZid->rs1LastUse = sqlite3_column_int64(stmt, 2); remZid->rs1Ttl = sqlite3_column_int64(stmt, 3); memcpy(remZid->rs2, sqlite3_column_blob(stmt, 4), RS_LENGTH); remZid->rs2LastUse = sqlite3_column_int64(stmt, 5); remZid->rs2Ttl = sqlite3_column_int64(stmt, 6); memcpy(remZid->mitmKey, sqlite3_column_blob(stmt, 7), RS_LENGTH); remZid->mitmLastUse = sqlite3_column_int64(stmt, 8); remZid->secureSince = sqlite3_column_int64(stmt, 9); remZid->preshCounter = sqlite3_column_int(stmt, 10); zidBase64Text = (char *)sqlite3_column_text(stmt, 11); b64Decode(zidBase64Text, strlen(zidBase64Text), remZid->identifier, IDENTIFIER_LEN); return stmt; } sqlite3_finalize(stmt); if (rc != SQLITE_DONE) ERRMSG; return NULL; } static void closeStatement(void *vstmt) { sqlite3_stmt *stmt; if (vstmt == NULL) return; stmt = (sqlite3_stmt*)vstmt; sqlite3_finalize(stmt); } void getDbCacheOps(dbCacheOps_t *ops) { ops->openCache = openCache; ops->closeCache = closeCache; ops->cleanCache = clearCache; ops->readLocalZid = readLocalZid; ops->readRemoteZidRecord = readRemoteZidRecord; ops->updateRemoteZidRecord = updateRemoteZidRecord; ops->insertRemoteZidRecord = insertRemoteZidRecord; ops->readZidNameRecord = readZidNameRecord; ops->updateZidNameRecord = updateZidNameRecord; ops->insertZidNameRecord = insertZidNameRecord; ops->prepareReadAllZid = prepareReadAllZid; ops->readNextZidRecord = readNextZidRecord; ops->closeStatement = closeStatement; } ================================================ FILE: deps/pjsip/version.mak ================================================ # Don't change the "export PJ_VERSION_xxx" style, they are parsed by setup.py export PJ_VERSION_MAJOR := 2 export PJ_VERSION_MINOR := 4 export PJ_VERSION_REV := 5 export PJ_VERSION_SUFFIX := -svn export PJ_VERSION := $(PJ_VERSION_MAJOR).$(PJ_VERSION_MINOR) ifneq ($(PJ_VERSION_REV),) export PJ_VERSION := $(PJ_VERSION).$(PJ_VERSION_REV) endif ifneq ($(PJ_VERSION_SUFFIX),) export PJ_VERSION := $(PJ_VERSION)$(PJ_VERSION_SUFFIX) endif ================================================ FILE: docs/Dependencies.txt ================================================ Dependencies for SIP SIMPLE client SDK -------------------------------------- Home page: http://sipsimpleclient.org * python http://python.org 2.7 * python-application http://pypi.python.org/simple/python-application >=2.8.0 * python-cjson http://pypi.python.org/pypi/python-cjson/ >=1.0.5 * python-dateutil http://niemeyer.net/python-dateutil >=1.4 * python-eventlib http://download.ag-projects.com/SipClient >=0.1.0 * python-greenlet http://pypi.python.org/pypi/greenlet >=0.3.2 * python-gnutls http://pypi.python.org/simple/python-gnutls >=1.1.9 * python-lxml http://codespeak.net/lxml >=2.1.2 * python-otr http://pypi.python.org/pypi/python-otr/ >=1.2.0 * python-msrplib http://download.ag-projects.com/MSRP >=0.20.0 * python-xcaplib http://download.ag-projects.com/XCAP >=1.0.17 * cython http://www.cython.org >=0.19.0 * dnspython http://www.dnspython.org >=1.9.0 * twisted http://twistedmatrix.com/trac >=8.1.0 * zope-interface http://www.zope.org >=3.3.1 Other libraries: * ffmpeg (libavformat, libswscale, libavcodec, libavutil) (>= 2.7) * libx264 (recent snapshot) * libvpx (>= 1.3.0) * libasund2 * libuuid * libsqlite3 * pkg-config * openssl To compile the video dependencies (ffmpeg / libx264 / libvpx): export SIPSIMPLE_FFMPEG_PATH=$HOME/work/ag-projects/video/local NOTE: yasm is required in order to enable asm optimizations. It does not come preinstalled on OSX, so it has to be manually installed. (brew install yasm will do) NOTE: libx264 and libvpx can be installed also using Homebrew on OSX. For ffmpeg: ./configure --enable-shared --disable-static --enable-memalign-hack --enable-gpl --enable-libx264 --disable-avdevice --disable-swresample --disable-postproc --prefix=$SIPSIMPLE_FFMPEG_PATH make make install ================================================ FILE: docs/DeveloperGuide.txt ================================================ Developer guide --------------- The software allows you to create elegant real-time communications applications without having to read the +1200 RFC documents behind it. As a library with a high-level API, the toolkit can be used to add voice, IM and Presence functionality to any network devices as long as Python/C environment is supported by such platform. The developer guide is available at: http://sipsimpleclient.org/projects/sipsimpleclient/wiki/SipMiddlewareApi ================================================ FILE: docs/Install.debian ================================================ SIP SIMPLE client SDK installation on Debian -------------------------------------------- Home page: http://sipsimpleclient.org This document describes the installation procedure on Debian operating systems from the official public repository maintained by AG Projects. Configure Repository -------------------- Install the AG Projects debian software signing key: wget http://download.ag-projects.com/agp-debian-gpg.key sudo apt-key add agp-debian-gpg.key Add these lines to /etc/apt/sources.list: Debian Unstable (Sid) --------------------- deb http://ag-projects.com/debian unstable main deb-src http://ag-projects.com/debian unstable main Debian Stable (Wheezy) ----------------------- deb http://ag-projects.com/debian stable main deb-src http://ag-projects.com/debian stable main Update the list of available packages: sudo apt-get update Install SIP SIMPLE client SDK: sudo apt-get install python-sipsimple Install the Command Line Clients: sudo apt-get install sipclients Creating Debian Packages ------------------------ Install the building dependencies: sudo apt-get install cython cython-dbg python-setuptools devscripts \ debhelper python-all-dev python-all-dbg libasound2-dev libssl-dev libsqlite3-dev For adding Video support: sudo apt-get install libv4l-dev libavcodec-dev libavformat-dev libavutil-dev \ libswscale-dev libswresample-dev libx264-dev libvpx-dev libavcodec-extra Note: do NOT use the Debian-Multimedia repositories. For Ubuntu variants: sudo apt-get install libv4l-dev libavcodec-dev libavformat-dev libavutil-dev \ libswscale-dev libswresample-dev libx264-dev libvpx-dev libavcodec-extra53 Obtain the source code using darcs as described in Install.linux document. Create under each repository a clean distribution file: python setup.py sdist Go to the ./dist directory and untar the file created at the step above. Go to the newly created directory and type: debuild -us -uc The .deb and related files are built in the upper directory. ================================================ FILE: docs/Install.linux ================================================ SIP SIMPLE client SDK installation on Linux ------------------------------------------- Home page: http://sipsimpleclient.org This document described the installation procedure on Linux operating systems. Step 1. Prerequisites --------------------- Both i386 or amd64 architectures are supported. Install the C compiling environment, Python 2.7 and the development version for the following packages: * openssl * ffmpeg Step 2. Install dependencies ---------------------------- See Dependencies.txt for detailed description of the required libraries and their minimum version number. Use the appropriate package manager for your Linux distribution to install the following packages, notice the minimum version numbers. You can use the easy_install script provided by the python-setuptools package to install the packages: sudo pip install -U cython dnspython lxml python-gnutls python-otr \ python-application twisted python-dateutil greenlet Step 3. Install SIP SIMPLE client SDK ------------------------------------- The SDK consists of four parts: 1. python-eventlib 2. XCAP library 3. MSRP library 4. SIP SIMPLE library # Eventlib if [ -d python-eventlib ]; then cd python-eventlib darcs pull -a sudo python setup.py install else darcs get http://devel.ag-projects.com/repositories/python-eventlib cd python-eventlib sudo python setup.py install fi cd .. # XCAP library if [ -d python-xcaplib ]; then cd python-xcaplib darcs pull -a sudo python setup.py install else darcs get http://devel.ag-projects.com/repositories/python-xcaplib cd python-xcaplib sudo python setup.py install fi cd .. # MSRP library if [ -d python-msrplib ]; then cd python-msrplib darcs pull -a sudo python setup.py install else darcs get http://devel.ag-projects.com/repositories/python-msrplib cd python-msrplib sudo python setup.py install fi cd .. # SIP SIMPLE if [ -d python-sipsimple ]; then cd python-sipsimple darcs pull -a else darcs get --set-scripts-executable http://devel.ag-projects.com/repositories/python-sipsimple fi cd.. cd python-sipsimple python setup.py build_ext --pjsip-clean-compile sudo python setup.py install Additional, you can install the command line interface scripts that can be used to test the SDK. if [ -d sipclients ]; then cd sipclients darcs pull -a else darcs get --set-scripts-executable http://devel.ag-projects.com/repositories/sipclients fi cd.. sudo python setup.py install ================================================ FILE: docs/Install.osx ================================================ Installation procedure for SIP SIMPLE client SDK on MacOSX >= 10.11 ------------------------------------------------------------------- Home page: http://sipsimpleclient.org This document describes the installation procedure on MacOSX >= 10.11 The installation procedure consists of the steps described below: Step 1. Prerequisites Step 2. Install Dependencies Step 3. Install SIP SIMPLE client SDK Step 1. Prerequisites --------------------- * MacOSX >= 10.12 * Apple Developer tools (XCode 9) * Homebrew from http://brew.sh * darcs version control tool * Python pip installer * Python virtualenv and virtualenvwrapper modules Step 2. Install Dependencies ---------------------------- See Dependencies.txt for detailed description of the required libraries and their minimum version number. A. Install the C dependencies, the software will be installed under /usr/local folder by default with Homebrew: brew install darcs gnutls yasm pkg-config x264 libvpx mpfr libmpc NOTE: Install ffmpeg without SecureTransport support (or you app will be rejected from the Mac App Store: brew install -s ffmpeg --without-securetransport B. Build and install the Python dependencies The procedure below relies on the standard Python interpreter 2.7 that comes with MacOSX and Xcode version 9. A 64bit build will be produced. This guide assumes all software is being installed in a virtualenv (except for the packages installed with Homebrew, of course). Install pip: sudo easy_install pip Install virtualenv and virtualenvwrapper: sudo -H pip install virtualenv --upgrade --ignore-installed six sudo -H pip install virtualenvwrapper --upgrade --ignore-installed six The above are installed in /Library/Python/2.7/site-packages Add to ~/.bashrc: export WORKON_HOME=$HOME/.virtualenvs export PIP_VIRTUALENV_BASE=$WORKON_HOME export PIP_RESPECT_VIRTUALENV=true [[ -f /usr/share/virtualenvwrapper/virtualenvwrapper_lazy.sh ]] && source /usr/share/virtualenvwrapper/virtualenvwrapper_lazy.sh [[ -f /usr/local/bin/virtualenvwrapper_lazy.sh ]] && source /usr/local/bin/virtualenvwrapper_lazy.sh Creating a virtual environment for python-simple mkvirtualenv -p $(which python2.7) sipsimple You'll be dropped right into it. If you want to exit it: deactivate And to activate the virtualenv again: workon sipsimple Install python dependencies: pip install -U python-gnutls python-otr dnspython twisted \ python-application cython python-dateutil greenlet lxml All packages are installed in: ~/.virtualenvs/sipsimple/lib/python2.7/site-packages/ Step 3. Build and install SIP SIMPLE client SDK ----------------------------------------------- Enter virtual environment: workon sipsimple The SDK consists of four parts: 1. python-eventlib 2. XCAP library 3. MSRP library 4. SIP SIMPLE library # Eventlet if [ -d python-eventlib ]; then cd python-eventlib darcs pull -a else darcs get http://devel.ag-projects.com/repositories/python-eventlib cd python-eventlib fi pip install . cd .. # XCAP library if [ -d python-xcaplib ]; then cd python-xcaplib darcs pull -a else darcs get http://devel.ag-projects.com/repositories/python-xcaplib cd python-xcaplib fi pip install . cd .. # MSRP library if [ -d python-msrplib ]; then cd python-msrplib darcs pull -a else darcs get http://devel.ag-projects.com/repositories/python-msrplib cd python-msrplib fi pip install . cd .. # SIP SIMPLE library if [ -d python-sipsimple ]; then cd python-sipsimple darcs pull -a else darcs get --set-scripts-executable http://devel.ag-projects.com/repositories/python-sipsimple cd python-sipsimple fi Build and install SIP SIMPLE library: python setup.py build_ext --pjsip-clean-compile python setup.py install Additional, you can install the command line interface scripts that can be used to test the SDK capabilities. if [ -d sipclients ]; then cd sipclients darcs pull -a else darcs get --set-scripts-executable http://devel.ag-projects.com/repositories/sipclients cd sipclients fi python setup.py install cd .. ================================================ FILE: docs/Install.rasbian ================================================ SIP SIMPLE client SDK installation on Rasbian --------------------------------------------- Home page: http://sipsimpleclient.org This document described the installation procedure on Rasbian Linux operating system on a Rasberry Pi (armhf architecture). Note. Acoustic Echo Cancellation is not available on arm architecture Install building dependencies ----------------------------- sudo apt-get install darcs devscripts libgmp3-dev cython python-lxml python-dnspython \ python-twisted python-dateutil python-greenlet libv4l-dev libavcodec-dev pkg-config \ libavformat-dev libavutil-dev libswscale-dev libx264-dev libvpx-dev python-all-dev \ libsqlite3-dev uuid-dev python-all-dbg cython-dbg libasound2-dev libssl-dev \ libmpfr-dev libmpfr-doc libmpc-dev libffi-dev dh-python python-enum34 \ python-cryptography python-gnutls python-gnutls python-gmpy2 -y Build the debian packages ------------------------- Obtain the source code from AG Projects using darcs command: darcs get --set-scripts-executable http://devel.ag-projects.com/repositories/project For each of the projects listed bellow: * python-application * python-otr * python-eventlib * python-xcap * python-msrp * python-sipsimple * sipclients Enter the directory where each project has been downloaded and prepare the build directory: python setup.py sdist Go to the ./dist directory and untar the file created at the step above. Go to the newly created directory and type: debuild -us -uc --no-sign The .deb and related files are built in one of the upper directories. The end result looks like the following: * python-application_2.8.0_all.deb * python-eventlib_0.2.5_all.deb * python-msrplib_0.20.0_all.deb * python-otr_1.2.2_all.deb * python-sipsimple_3.5.0_armhf.deb * python-xcaplib_1.2.2_all.deb * sipclients_3.5.3_all.deb Use dpkg -i package_name.deb command to install the software. Prebuild binary packages ------------------------ You can download and install prebuild binary packages from: http://download.ag-projects.com/SipClient/Raspberry/ ================================================ FILE: docs/Install.ubuntu ================================================ SIP SIMPLE client SDK installation on Ubuntu -------------------------------------------- Home page: http://sipsimpleclient.org This document describes the installation procedure on Ubuntu operating systems from the official public repository maintained by AG Projects. Configure Repository -------------------- Install the AG Projects debian software signing key: wget http://download.ag-projects.com/agp-debian-gpg.key sudo apt-key add agp-debian-gpg.key Add the repository to /etc/apt/sources.list (run commands as root): echo "deb http://ag-projects.com/ubuntu `lsb_release -c -s` main" >> /etc/apt/sources.list echo "deb-src http://ag-projects.com/ubuntu `lsb_release -c -s` main" >> /etc/apt/sources.list Update the list of available packages: sudo apt-get update Install SIP SIMPLE client SDK: sudo apt-get install python-sipsimple Install the Command Line Clients: sudo apt-get install sipclients ================================================ FILE: docs/Install.windows ================================================ SIP SIMPLE client SDK installation on Microsoft Windows ------------------------------------------------------- Home page: http://sipsimpleclient.org This document describes the installation procedure on Microsoft Windows operating systems. Supported versions are Windows XP or higher. Step 1. Prerequisites --------------------- First download and install darcs version control utility from darcs.net: http://darcs.net/binaries/windows/darcs-2.8.1-win1.msi The building process is designed to work with the MSYS2 environment and the MinGW-w64 compiler toolchain. Other approaches might work but they are not tested nor supported. MSYS2: MSYS2 is a minimal Unix-like environment for Windows. It includes a port of the 'pacman' package manager (ported from Arch Linux) and a pretty extensive collection of packages which can be easily installed. It contains packages for the MinGW-w64 toolchain, for 32 and 64 bit architectures. MinGW-w64: MinGW is a minimal compilation toolchain for Windows. It contains GCC and allows for compilation of native applications for Windows. The original MinGW project (available at http://mingw.org, henceforth referred to as 'MinGW32') didn't support 64 bit Windows so the MinGW-w64 project was created. It contains support for both 32 and 64 bit architectures and generally it's better maintained than MinGW32. Step 1.1. Installing MSYS2 and MinGW-w64 ---------------------------------------- The following instructions will guide you through the installation of a MSYS2 and MinGW-w64 installation for a 32 bit Windows system. A 64 bit build is definitely possible but hasn't been tested. * Go to http://msys2.github.io and download the 32 bit (i686) MSYS2 installer * Run it and install it on C:\msys2 (or any other path, but note it has to be ASCII only and with no spaces! The no spaces restriction also applies to the paths of the packages that will be built during the installation) * Once the process is finished, check the box to "Run MSYS2 32bit now", a new terminal window will appear * Upgrade the system packages by following the instructions from: http://msys2.github.io * Install the compiler toolchain and assorted tools: pacman -S mingw-w64-i686-toolchain make mingw-w64-i686-libtool autoconf automake-wrapper After MSYS2 was installed a new menu group was created on the Window start menu: "MSYS2 32bit", which contains 3 links: MinGW-w64 Win32 Shell, MinGW-w64 Win64 Shell and MSYS2 Shell. We'll use "MinGW-w64 Win32 Shell" from now on, since it will contain the 32 bit MinGW-w64 toolchain in the path. Step 1.2. Installing Python 2.7 ------------------------------- In order to avoid problems derived from using multiple C runtimes, we are going to install Python 2.7 using pacman. The Python binaries that are available for download on Python's official website are compiled with Visual Studio, and if we use MinGW to compile libraries which are then used by Python modules we are at risk because the resulting application would load 2 different C runtimes. * Install Python 2.7 by running: pacman -S mingw-w64-i686-python2 * Install pip/setuptools: pacman -S mingw-w64-i686-python2-pip NOTE: At the time of this writing the Python REPL needs to be started with "python -i" due to a problem with the ncurses libraries. Step 2. Install dependencies ---------------------------- See Dependencies.txt for detailed description of the required libraries and their minimum version number. * Install required dependencies with pacman: pacman -S mingw-w64-i686-gnutls mingw-w64-i686-python2-lxml mingw-w64-i686-ffmpeg mingw-w64-i686-sqlite3 msys2-w32api-headers mingw-w64-i686-gmp mingw-w64-i686-mpc mingw-w64-i686-mpfr * Install the Python package dependencies: pip install cython dnspython twisted python-dateutil greenlet python-application python-cjson python-gnutls python-otr Step 2.1. Install python-otr dependencies (if installation fails) ----------------------------------------------------------------- At the time of this writing installing python-otr straight from PyPI is not possible due to some of its dependencies having problems in the MinGW toolchain. Installing these from source works though, so let's do that. * Install Mercurial and Git: pacman -S mercurial git * Install cffi: hg clone https://bitbucket.org/cffi/cffi cd cffi pip install . * Install cryptography: git clone https://github.com/pyca/cryptography.git cd cryptography pip install . Note: if pip install fails, with: distutils.errors.DistutilsPlatformError: VC 6.0 is not supported by this module check: https://github.com/pypa/setuptools/issues/1118 (PeterMosmans' answer) * Install gmpy2: git clone https://github.com/aleaxit/gmpy.git cd gmpy pip install . * Install python-otr: pip install python-otr Step 2.2. Build and install some extra packages ----------------------------------------------- mingw-w64-intsafe-headers * Get the package code: darcs get http://devel.ag-projects.com/repositories/windows/mingw-w64-intsafe-headers * Enter the directory and build+install the package: cd mingw-w64-intsafe-headers && makepkg-mingw -cfiL --nocheck --nosign mingw-w64-dshow-baseclasses * Get the package code: darcs get http://devel.ag-projects.com/repositories/windows/mingw-w64-dshow-baseclasses * Enter the directory and build+install the package: cd mingw-w64-dshow-baseclasses && makepkg-mingw -cfiL --nocheck --nosign Step 3. Install SIP SIMPLE client SDK ------------------------------------- The SDK consists of four parts: 1. python-eventlib 2. XCAP library 3. MSRP library 4. SIP SIMPLE library itself Make sure that the path where you download the software below does not contain any space in its name. # Eventlib if [ -d python-eventlib ]; then cd python-eventlib darcs pull -a else darcs get http://devel.ag-projects.com/repositories/python-eventlib cd python-eventlib fi pip install . cd .. # XCAP library if [ -d python-xcaplib ]; then cd python-xcaplib darcs pull -a else darcs get http://devel.ag-projects.com/repositories/python-xcaplib cd python-xcaplib fi pip install . cd .. # MSRP library if [ -d python-msrplib ]; then cd python-msrplib darcs pull -a else darcs get http://devel.ag-projects.com/repositories/python-msrplib cd python-msrplib fi pip install . cd .. # SIP SIMPLE if [ -d python-sipsimple ]; then cd python-sipsimple darcs pull -a else darcs get --set-scripts-executable http://devel.ag-projects.com/repositories/python-sipsimple cd python-sipsimple fi ./build_inplace --pjsip-clean-compile pip install . cd .. The software has been installed in C:\msys2\mingw32\lib\python2.7\site-packages ================================================ FILE: docs/Licenses.txt ================================================ This document describes the licenses for the SIP SIMPLE client SDK and its dependencies. SIP SIMPLE Client SDK --------------------- SIP SIMPLE Client SDK is licensed under GPLv3. Libxml2 ------- Libxml2 is the XML C parser and toolkit developed for the Gnome project (but usable outside of the Gnome platform), it is available under the MIT License. Libxslt ------- Libxslt is a language for transforming XML documents into other XML documents. It is available under the MIT License. GnuTLS ------ GnuTLS is a project that aims to develop a library which provides a secure layer, over a reliable transport layer. Currently the GnuTLS library implements the proposed standards by the IETF's TLS working group. The library is licensed under the GNU Lesser General Public License version 2.1. Setuptools ---------- Download, build, install, upgrade, and uninstall Python packages. It is available under the Python Software Foundation License. Cython ------ A language that makes writing C extensions for the Python language as easy as Python itself. Cython is available under the open source Apache License. Python-application ------------------ A collection of modules that are useful when building python applications. Their purpose is to eliminate the need to divert resources into implementing the small tasks that every application needs to do in order to run successfully and focus instead on the application logic itself. Is available under GNU Library or Lesser General Public License (LGPL). Dnspython --------- A DNS toolkit for Python. It supports almost all record types. It can be used for queries, zone transfers, and dynamic updates. Dnspython originated at Nominum where it was developed to facilitate the testing of DNS software. Nominum has generously allowed it to be open sourced under a BSD-style license. Lxml ---- Lxml is the most feature-rich and easy-to-use library for working with XML and HTML in the Python language. A Pythonic binding for the libxml2 and libxslt libraries. It is unique in that it combines the speed and feature completeness of these libraries with the simplicity of a native Python API, mostly compatible but superior to the well-known ElementTree API. The lxml library is shipped under a BSD license. Twisted ------- A networking engine written in Python, supporting numerous protocols. It contains a web server, numerous chat clients, chat servers, mail servers, and more. The source code is available under a proprietary license (non GPL). Pylib ----- The pylib is a development support library featuring py.test, ad-hoc distributed execution, micro-threads (greenlets) and uniform local path and svn abstractions. Is provided under MIT License. Python-gnutls ------------- Python wrapper for gnutls available under either GPL or Lesser General Public License (LGPL). Eventlet_twisted ---------------- Eventlet makes asynchronous programming look like synchronous, thus achieving higher signal-to-noise ratio than traditional twisted programs have. It is available under the MIT License. Python-xcaplib -------------- XCAP protocol, defined in RFC 4825, allows a client to read, write, and modify application configuration data stored in XML format on a server. XCAP maps XML document sub-trees and element attributes to HTTP URIs, so that these components can be directly accessed by HTTP. An XCAP server used by XCAP clients to store data like presence policy in combination with a SIP Presence server that supports PUBLISH/SUBSCRIBE/NOTIFY SIP methods can provide a complete SIP SIMPLE solution. The XCAP client example script provided by this package can be used to manage documents on an XCAP server. The software is available under either GPL or Lesser General Public License (LGPL). Pjlib ----- A small footprint, high performance, ultra portable abstraction library and framework, used by PJSIP and PJMEDIA. PJLIB is about the only library that PJLIB-UTIL, PJMEDIA, and PJSIP should depend, as it provides complete abstraction not only to Operating System dependent features, but it is also designed to abstract LIBC and provides some useful data structures too. A cross-platforms portability and framework library. Available under GPL or an alternative non-GPL license. Pjlib-util ---------- PJLIB-UTIL is an auxiliary library providing supports for PJMEDIA and PJSIP. Some of the functions/components in this library: small footprint XML parsing, STUN client library, asynchronous/caching DNS resolver, hashing/encryption functions, etc. Available under GPL or an alternative non-GPL license. Pjnat ----- PJNATH is a new library, available in the SVN trunk, for helping the applications with NAT traversal. It implements the latest specification of Session Traversal Utilities for NAT (STUN), Obtaining Relay Addresses from STUN (TURN), and Interactive Connectivity Establishment (ICE). Available under GPL or alternative non-GPL license. Pjmedia ------- PJMEDIA is a complementary library for PJSIP to build a complete, full-featured SIP user agent applications such as softphones/hardphones, gateways, or B2BUA. Available under GPL or an alternative non-GPL license. Pjmedia-codec ------------- A placeholder library to support various multimedia codecs. Available under GPL or an alternative non-GPL license. Pjsip ----- A SIP protocol stack, the main bulding block for this project. Available under GPL or an alternative non-GPL license. The non-GPL license is available from its licensor for a one time fee. Eventlet -------- Eventlet is a networking library written in Python. It achieves high scalability by using non-blocking io while at the same time retaining high programmer usability by using coroutines to make the non-blocking io operations appear blocking at the source code level. It is available under MIT license. Greenlet -------- Lightweight in-process concurrent programming. The "greenlet" package is a spin-off of Stackless, a version of CPython that supports micro-threads called "tasklets". Tasklets run pseudo-concurrently (typically in a single or a few OS-level threads) and are synchronized with data exchanges on "channels". It is provided under MIT license. ================================================ FILE: docs/Uninstall.txt ================================================ http://sipsimpleclient.org/projects/sipsimpleclient/wiki/SipInstallation ================================================ FILE: setup.py ================================================ #!/usr/bin/python2 import glob import os from distutils.core import setup from distutils.extension import Extension from setup_pjsip import PJSIP_build_ext def find_packages(root): return [directory.replace(os.path.sep, '.') for directory, sub_dirs, files in os.walk(root) if '__init__.py' in files] class PackageInfo(object): def __init__(self, info_file): with open(info_file) as f: exec(f.read(), self.__dict__) self.__dict__.pop('__builtins__', None) def __getattribute__(self, name): # this is here to silence the IDE about missing attributes return super(PackageInfo, self).__getattribute__(name) package_info = PackageInfo(os.path.join('sipsimple', '__info__.py')) setup( name=package_info.__project__, version=package_info.__version__, description=package_info.__summary__, license=package_info.__license__, url=package_info.__webpage__, author=package_info.__author__, author_email=package_info.__email__, platforms=["Platform Independent"], classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Telecommunications Industry", "License :: OSI Approved :: GNU General Public License (GPL)", "Operating System :: OS Independent", "Programming Language :: C", "Programming Language :: Cython", "Programming Language :: Python" ], packages=find_packages('sipsimple'), package_data={ 'sipsimple.payloads': ['xml-schemas/*'] }, ext_modules=[ Extension(name="sipsimple.core._core", sources=["sipsimple/core/_core.pyx", "sipsimple/core/_core.pxd"] + glob.glob(os.path.join("sipsimple", "core", "_core.*.pxi"))), Extension(name="sipsimple.util._sha1", sources=["sipsimple/util/_sha1.pyx"], depends=["sipsimple/util/_sha1.h"]) ], cmdclass={ 'build_ext': PJSIP_build_ext }, provides=['sipsimple'] ) ================================================ FILE: setup_pjsip.py ================================================ import errno import itertools import os import platform import re import shutil import subprocess import sys if sys.platform.startswith('linux'): sys_platform = 'linux' elif sys.platform.startswith('freebsd'): sys_platform = 'freebsd' else: sys_platform = sys.platform # Hack to set environment variables before importing distutils # modules that will fetch them and set the compiler and linker # to be used. -Saul if sys_platform == "darwin": min_osx_version = "10.13" try: osx_sdk_path = subprocess.check_output(["xcodebuild", "-version", "-sdk", "macosx", "Path"]).strip() except subprocess.CalledProcessError as e: raise RuntimeError("Could not locate SDK path: %s" % str(e)) # OpenSSL (must be installed with Homebrew) ossl_cflags = "-I/usr/local/opt/openssl/include" ossl_ldflags = "-L/usr/local/opt/openssl/lib" # SQLite (must be installed with Homebrew) sqlite_cflags = "-I/usr/local/opt/sqlite/include" sqlite_ldflags = "-L/usr/local/opt/sqlite/lib" # Prepare final flags arch_flags = "-arch x86_64" local_cflags = " %s %s %s -mmacosx-version-min=%s -isysroot %s" % (arch_flags, ossl_cflags, sqlite_cflags, min_osx_version, osx_sdk_path) local_ldflags = " %s %s %s -isysroot %s" % (arch_flags, ossl_ldflags, sqlite_ldflags, osx_sdk_path) os.environ['CFLAGS'] = os.environ.get('CFLAGS', '') + local_cflags os.environ['LDFLAGS'] = os.environ.get('LDFLAGS', '') + local_ldflags os.environ['ARCHFLAGS'] = arch_flags os.environ['MACOSX_DEPLOYMENT_TARGET'] = min_osx_version from distutils import log from distutils.dir_util import copy_tree from distutils.errors import DistutilsError from Cython.Distutils import build_ext class PJSIP_build_ext(build_ext): config_site = ["#define PJ_SCANNER_USE_BITWISE 0", "#define PJSIP_SAFE_MODULE 0", "#define PJSIP_MAX_PKT_LEN 262144", "#define PJSIP_UNESCAPE_IN_PLACE 1", "#define PJMEDIA_AUDIO_DEV_HAS_COREAUDIO %d" % (1 if sys_platform=="darwin" else 0), "#define PJMEDIA_AUDIO_DEV_HAS_ALSA %d" % (1 if sys_platform=="linux" else 0), "#define PJMEDIA_AUDIO_DEV_HAS_WMME %d" % (1 if sys_platform=="win32" else 0), "#define PJMEDIA_HAS_SPEEX_AEC 0", "#define PJMEDIA_HAS_WEBRTC_AEC %d" % (1 if re.match('i\d86|x86|x86_64', platform.machine()) else 0), "#define PJMEDIA_RTP_PT_TELEPHONE_EVENTS 101", "#define PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR \"101\"", "#define PJMEDIA_STREAM_ENABLE_KA PJMEDIA_STREAM_KA_EMPTY_RTP", "#define PJMEDIA_STREAM_VAD_SUSPEND_MSEC 0", "#define PJMEDIA_CODEC_MAX_SILENCE_PERIOD -1", "#define PJ_ICE_MAX_CHECKS 256", "#define PJ_LOG_MAX_LEVEL 6", "#define PJ_IOQUEUE_MAX_HANDLES 1024", "#define PJ_DNS_RESOLVER_MAX_TTL 0", "#define PJ_DNS_RESOLVER_INVALID_TTL 0", "#define PJSIP_TRANSPORT_IDLE_TIME 7200", "#define PJ_ENABLE_EXTRA_CHECK 1", "#define PJSIP_DONT_SWITCH_TO_TCP 1", "#define PJMEDIA_VIDEO_DEV_HAS_SDL 0", "#define PJMEDIA_VIDEO_DEV_HAS_AVI 0", "#define PJMEDIA_VIDEO_DEV_HAS_FB 1", "#define PJMEDIA_VIDEO_DEV_HAS_V4L2 %d" % (1 if sys_platform=="linux" else 0), "#define PJMEDIA_VIDEO_DEV_HAS_AVF %d" % (1 if sys_platform=="darwin" else 0), "#define PJMEDIA_VIDEO_DEV_HAS_DSHOW %d" % (1 if sys_platform=="win32" else 0), "#define PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC 1", "#define PJMEDIA_VIDEO_DEV_HAS_NULL 1"] user_options = build_ext.user_options user_options.extend([ ("pjsip-clean-compile", None, "Clean PJSIP tree before compilation"), ("pjsip-disable-assertions", None, "Disable assertion checks within PJSIP"), ("pjsip-verbose-build", None, "Print output of PJSIP compilation process") ]) boolean_options = build_ext.boolean_options boolean_options.extend(["pjsip-clean-compile", "pjsip-disable-assertions", "pjsip-verbose-build"]) @staticmethod def distutils_exec_process(cmdline, silent=True, input=None, **kwargs): """Execute a subprocess and returns the returncode, stdout buffer and stderr buffer. Optionally prints stdout and stderr while running.""" try: sub = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs) stdout, stderr = sub.communicate(input=input) returncode = sub.returncode if not silent: sys.stdout.write(stdout) sys.stderr.write(stderr) except OSError, e: if e.errno == errno.ENOENT: raise RuntimeError('"%s" is not present on this system' % cmdline[0]) else: raise if returncode != 0: raise RuntimeError('Got return value %d while executing "%s", stderr output was:\n%s' % (returncode, " ".join(cmdline), stderr.rstrip("\n"))) return stdout @staticmethod def get_make_cmd(): if sys_platform == "freebsd": return "gmake" else: return "make" @staticmethod def get_opts_from_string(line, prefix): """Returns all options that have a particular prefix on a commandline""" chunks = [chunk.strip() for chunk in line.split()] return [chunk[len(prefix):] for chunk in chunks if chunk.startswith(prefix)] @classmethod def get_makefile_variables(cls, makefile): """Returns all variables in a makefile as a dict""" stdout = cls.distutils_exec_process([cls.get_make_cmd(), "-f", makefile, "-pR", makefile], silent=True) return dict(tup for tup in re.findall("(^[a-zA-Z]\w+)\s*:?=\s*(.*)$", stdout, re.MULTILINE)) @classmethod def makedirs(cls, path): try: os.makedirs(path) except OSError, e: if e.errno==errno.EEXIST and os.path.isdir(path) and os.access(path, os.R_OK | os.W_OK | os.X_OK): return raise def initialize_options(self): build_ext.initialize_options(self) self.pjsip_clean_compile = 0 self.pjsip_verbose_build = 0 self.pjsip_dir = os.path.join(os.path.dirname(__file__), "deps", "pjsip") def configure_pjsip(self): log.info("Configuring PJSIP") with open(os.path.join(self.build_dir, "pjlib", "include", "pj", "config_site.h"), "wb") as f: f.write("\n".join(self.config_site+[""])) cflags = "-DNDEBUG -g -fPIC -fno-omit-frame-pointer -fno-strict-aliasing -Wno-unused-label" if self.debug or hasattr(sys, 'gettotalrefcount'): log.info("PJSIP will be built without optimizations") cflags += " -O0" else: cflags += " -O2" env = os.environ.copy() env['CFLAGS'] = ' '.join(x for x in (cflags, env.get('CFLAGS', None)) if x) if sys_platform == "win32": cmd = ["bash", "configure"] else: cmd = ["./configure"] cmd.extend(["--disable-g7221-codec"]) ffmpeg_path = env.get("SIPSIMPLE_FFMPEG_PATH", None) if ffmpeg_path is not None: cmd.append("--with-ffmpeg=%s" % os.path.abspath(os.path.expanduser(ffmpeg_path))) libvpx_path = env.get("SIPSIMPLE_LIBVPX_PATH", None) if libvpx_path is not None: cmd.append("--with-vpx=%s" % os.path.abspath(os.path.expanduser(libvpx_path))) self.distutils_exec_process(cmd, silent=not self.pjsip_verbose_build, cwd=self.build_dir, env=env) if "#define PJ_HAS_SSL_SOCK 1\n" not in open(os.path.join(self.build_dir, "pjlib", "include", "pj", "compat", "os_auto.h")).readlines(): os.remove(os.path.join(self.build_dir, "build.mak")) raise DistutilsError("PJSIP TLS support was disabled, OpenSSL development files probably not present on this system") def compile_pjsip(self): log.info("Compiling PJSIP") self.distutils_exec_process([self.get_make_cmd()], silent=not self.pjsip_verbose_build, cwd=self.build_dir) def clean_pjsip(self): log.info("Cleaning PJSIP") try: shutil.rmtree(self.build_dir) except OSError, e: if e.errno == errno.ENOENT: return raise def update_extension(self, extension): build_mak_vars = self.get_makefile_variables(os.path.join(self.build_dir, "build.mak")) extension.include_dirs = self.get_opts_from_string(build_mak_vars["PJ_CFLAGS"], "-I") extension.library_dirs = self.get_opts_from_string(build_mak_vars["PJ_LDFLAGS"], "-L") extension.libraries = self.get_opts_from_string(build_mak_vars["PJ_LDLIBS"], "-l") extension.define_macros = [tuple(define.split("=", 1)) for define in self.get_opts_from_string(build_mak_vars["PJ_CFLAGS"], "-D")] extension.define_macros.append(("PJ_SVN_REVISION", open(os.path.join(self.build_dir, "base_rev"), "r").read().strip())) extension.define_macros.append(("__PYX_FORCE_INIT_THREADS", 1)) extension.extra_compile_args.append("-Wno-unused-function") # silence warning if sys_platform == "darwin": extension.define_macros.append(("MACOSX_DEPLOYMENT_TARGET", min_osx_version)) frameworks = re.findall("-framework (\S+)(?:\s|$)", build_mak_vars["PJ_LDLIBS"]) extension.extra_link_args = list(itertools.chain(*(("-framework", val) for val in frameworks))) extension.extra_link_args.append("-mmacosx-version-min=%s" % min_osx_version) extension.extra_compile_args.append("-mmacosx-version-min=%s" % min_osx_version) extension.library_dirs.append("%s/usr/lib" % osx_sdk_path) extension.include_dirs.append("%s/usr/include" % osx_sdk_path) extension.depends = build_mak_vars["PJ_LIB_FILES"].split() self.libraries = extension.depends[:] def cython_sources(self, sources, extension): log.info("Compiling Cython extension %s" % extension.name) if extension.name == "sipsimple.core._core": self.build_dir = os.path.join(self.build_temp, "pjsip") if self.pjsip_clean_compile: self.clean_pjsip() copy_tree(self.pjsip_dir, self.build_dir, verbose=0) if not os.path.exists(os.path.join(self.build_dir, "build.mak")): self.configure_pjsip() self.update_extension(extension) self.compile_pjsip() return build_ext.cython_sources(self, sources, extension) ================================================ FILE: sipsimple/__info__.py ================================================ """Package information""" __project__ = "python-sipsimple" __summary__ = "SIP SIMPLE implementation for Python" __webpage__ = "https://sipsimpleclient.org" __version__ = "3.6.0" __author__ = "AG Projects" __email__ = "support@ag-projects.com" __license__ = "GPL" __copyright__ = "Copyright 2008-2021 {}".format(__author__) ================================================ FILE: sipsimple/__init__.py ================================================ from sipsimple.__info__ import __project__, __summary__, __webpage__, __version__, __author__, __email__, __license__, __copyright__ from sipsimple.logging import log # This will initialize and make our logger available early, before anything else is loaded ================================================ FILE: sipsimple/account/__init__.py ================================================ """ Implements a SIP Account management system that allows the definition of multiple SIP accounts and their properties. """ __all__ = ['Account', 'BonjourAccount', 'AccountManager'] from itertools import chain from threading import Lock from application.notification import IObserver, NotificationCenter, NotificationData from application.python import Null from application.python.decorator import execute_once from application.python.descriptor import classproperty from application.python.types import Singleton from application.system import host as Host from eventlib import coros, proc from gnutls.crypto import X509Certificate, X509PrivateKey from gnutls.interfaces.twisted import X509Credentials from zope.interface import implements from sipsimple.account.bonjour import BonjourServices, _bonjour from sipsimple.account.publication import PresencePublisher, DialogPublisher from sipsimple.account.registration import Registrar from sipsimple.account.subscription import MWISubscriber, PresenceWinfoSubscriber, DialogWinfoSubscriber, PresenceSubscriber, SelfPresenceSubscriber, DialogSubscriber from sipsimple.account.xcap import XCAPManager from sipsimple.core import Credentials, SIPURI, ContactURIFactory from sipsimple.configuration import ConfigurationManager, Setting, SettingsGroup, SettingsObject, SettingsObjectID from sipsimple.configuration.datatypes import AudioCodecList, MSRPConnectionModel, MSRPRelayAddress, MSRPTransport, NonNegativeInteger, Path, SIPAddress, SIPProxyAddress, SRTPKeyNegotiation, STUNServerAddressList, VideoCodecList, XCAPRoot from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.payloads import ParserError from sipsimple.payloads.messagesummary import MessageSummary from sipsimple.payloads.pidf import PIDFDocument from sipsimple.payloads.rlsnotify import RLSNotify from sipsimple.payloads.watcherinfo import WatcherInfoDocument from sipsimple.threading import call_in_thread from sipsimple.threading.green import call_in_green_thread, run_in_green_thread from sipsimple.util import user_info class AuthSettings(SettingsGroup): username = Setting(type=str, default=None, nillable=True) password = Setting(type=str, default='') class SIPSettings(SettingsGroup): always_use_my_proxy = Setting(type=bool, default=False) outbound_proxy = Setting(type=SIPProxyAddress, default=None, nillable=True) register = Setting(type=bool, default=True) register_interval = Setting(type=NonNegativeInteger, default=3600) subscribe_interval = Setting(type=NonNegativeInteger, default=3600) publish_interval = Setting(type=NonNegativeInteger, default=3600) class SRTPEncryptionSettings(SettingsGroup): enabled = Setting(type=bool, default=True) key_negotiation = Setting(type=SRTPKeyNegotiation, default='opportunistic') class RTPSettings(SettingsGroup): audio_codec_list = Setting(type=AudioCodecList, default=None, nillable=True) video_codec_list = Setting(type=VideoCodecList, default=None, nillable=True) encryption = SRTPEncryptionSettings class NATTraversalSettings(SettingsGroup): use_ice = Setting(type=bool, default=False) stun_server_list = Setting(type=STUNServerAddressList, default=None, nillable=True) msrp_relay = Setting(type=MSRPRelayAddress, default=None, nillable=True) use_msrp_relay_for_outbound = Setting(type=bool, default=False) class MessageSummarySettings(SettingsGroup): enabled = Setting(type=bool, default=False) voicemail_uri = Setting(type=SIPAddress, default=None, nillable=True) class XCAPSettings(SettingsGroup): enabled = Setting(type=bool, default=False) discovered = Setting(type=bool, default=False) xcap_root = Setting(type=XCAPRoot, default=None, nillable=True) class PresenceSettings(SettingsGroup): enabled = Setting(type=bool, default=False) class TLSSettings(SettingsGroup): certificate = Setting(type=Path, default=None, nillable=True) verify_server = Setting(type=bool, default=False) class MSRPSettings(SettingsGroup): transport = Setting(type=MSRPTransport, default='tls') connection_model = Setting(type=MSRPConnectionModel, default='relay') class Account(SettingsObject): """ Object representing a SIP account. Contains configuration settings and attributes for accessing SIP related objects. When the account is active, it will register, publish its presence and subscribe to watcher-info events depending on its settings. If the object is un-pickled and its enabled flag was set, it will automatically activate. When the save method is called, depending on the value of the enabled flag, the account will activate/deactivate. Notifications sent by instances of Account: * CFGSettingsObjectWasCreated * CFGSettingsObjectWasActivated * CFGSettingsObjectWasDeleted * CFGSettingsObjectDidChange * SIPAccountWillActivate * SIPAccountDidActivate * SIPAccountWillDeactivate * SIPAccountDidDeactivate """ implements(IObserver) __group__ = 'Accounts' __id__ = SettingsObjectID(type=SIPAddress) id = __id__ enabled = Setting(type=bool, default=False) display_name = Setting(type=unicode, default=None, nillable=True) auth = AuthSettings sip = SIPSettings rtp = RTPSettings nat_traversal = NATTraversalSettings message_summary = MessageSummarySettings msrp = MSRPSettings presence = PresenceSettings xcap = XCAPSettings tls = TLSSettings def __new__(cls, id): with AccountManager.load.lock: if not AccountManager.load.called: raise RuntimeError("cannot instantiate %s before calling AccountManager.load" % cls.__name__) return SettingsObject.__new__(cls, id) def __init__(self, id): self.contact = ContactURIFactory() self.xcap_manager = XCAPManager(self) self._started = False self._deleted = False self._active = False self._activation_lock = coros.Semaphore(1) self._registrar = Registrar(self) self._mwi_subscriber = MWISubscriber(self) self._pwi_subscriber = PresenceWinfoSubscriber(self) self._dwi_subscriber = DialogWinfoSubscriber(self) self._presence_subscriber = PresenceSubscriber(self) self._self_presence_subscriber = SelfPresenceSubscriber(self) self._dialog_subscriber = DialogSubscriber(self) self._presence_publisher = PresencePublisher(self) self._dialog_publisher = DialogPublisher(self) self._mwi_voicemail_uri = None self._pwi_version = None self._dwi_version = None self._presence_version = None self._dialog_version = None def start(self): if self._started or self._deleted: return self._started = True notification_center = NotificationCenter() notification_center.add_observer(self, name='CFGSettingsObjectDidChange', sender=self) notification_center.add_observer(self, name='CFGSettingsObjectDidChange', sender=SIPSimpleSettings()) notification_center.add_observer(self, name='XCAPManagerDidDiscoverServerCapabilities', sender=self.xcap_manager) notification_center.add_observer(self, sender=self._mwi_subscriber) notification_center.add_observer(self, sender=self._pwi_subscriber) notification_center.add_observer(self, sender=self._dwi_subscriber) notification_center.add_observer(self, sender=self._presence_subscriber) notification_center.add_observer(self, sender=self._self_presence_subscriber) notification_center.add_observer(self, sender=self._dialog_subscriber) self.xcap_manager.init() if self.enabled: self._activate() def stop(self): if not self._started: return self._started = False self._deactivate() notification_center = NotificationCenter() notification_center.remove_observer(self, name='CFGSettingsObjectDidChange', sender=self) notification_center.remove_observer(self, name='CFGSettingsObjectDidChange', sender=SIPSimpleSettings()) notification_center.remove_observer(self, name='XCAPManagerDidDiscoverServerCapabilities', sender=self.xcap_manager) notification_center.remove_observer(self, sender=self._mwi_subscriber) notification_center.remove_observer(self, sender=self._pwi_subscriber) notification_center.remove_observer(self, sender=self._dwi_subscriber) notification_center.remove_observer(self, sender=self._presence_subscriber) notification_center.remove_observer(self, sender=self._self_presence_subscriber) notification_center.remove_observer(self, sender=self._dialog_subscriber) def delete(self): if self._deleted: return self._deleted = True self.stop() self._registrar = None self._mwi_subscriber = None self._pwi_subscriber = None self._dwi_subscriber = None self._presence_subscriber = None self._self_presence_subscriber = None self._dialog_subscriber = None self._presence_publisher = None self._dialog_publisher = None self.xcap_manager = None SettingsObject.delete(self) @run_in_green_thread def reregister(self): if self._started: self._registrar.reregister() @run_in_green_thread def resubscribe(self): if self._started: self._mwi_subscriber.resubscribe() self._pwi_subscriber.resubscribe() self._dwi_subscriber.resubscribe() self._presence_subscriber.resubscribe() self._self_presence_subscriber.resubscribe() self._dialog_subscriber.resubscribe() @property def credentials(self): return Credentials(self.auth.username or self.id.username, self.auth.password) @property def registered(self): try: return self._registrar.registered except AttributeError: return False @property def mwi_active(self): try: return self._mwi_subscriber.subscribed except AttributeError: return False @property def tls_credentials(self): # This property can be optimized to cache the credentials it loads from disk, # however this is not a time consuming operation (~ 3000 req/sec). -Luci settings = SIPSimpleSettings() if self.tls.certificate is not None: certificate_data = open(self.tls.certificate.normalized).read() certificate = X509Certificate(certificate_data) private_key = X509PrivateKey(certificate_data) else: certificate = None private_key = None if settings.tls.ca_list is not None: # we should read all certificates in the file, rather than just the first -Luci trusted = [X509Certificate(open(settings.tls.ca_list.normalized).read())] else: trusted = [] credentials = X509Credentials(certificate, private_key, trusted) credentials.verify_peer = self.tls.verify_server return credentials @property def uri(self): return SIPURI(user=self.id.username, host=self.id.domain) @property def voicemail_uri(self): return self._mwi_voicemail_uri or self.message_summary.voicemail_uri @property def presence_state(self): try: return self._presence_publisher.state except AttributeError: return None @presence_state.setter def presence_state(self, state): try: self._presence_publisher.state = state except AttributeError: pass @property def dialog_state(self): try: return self._dialog_publisher.state except AttributeError: return None @dialog_state.setter def dialog_state(self, state): try: self._dialog_publisher.state = state except AttributeError: pass def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) @run_in_green_thread def _NH_CFGSettingsObjectDidChange(self, notification): if self._started and 'enabled' in notification.data.modified: if self.enabled: self._activate() else: self._deactivate() def _NH_XCAPManagerDidDiscoverServerCapabilities(self, notification): if self._started and self.xcap.discovered is False: self.xcap.discovered = True self.save() notification.center.post_notification('SIPAccountDidDiscoverXCAPSupport', sender=self) def _NH_MWISubscriberDidDeactivate(self, notification): self._mwi_voicemail_uri = None def _NH_MWISubscriptionGotNotify(self, notification): if notification.data.body and notification.data.content_type == MessageSummary.content_type: try: message_summary = MessageSummary.parse(notification.data.body) except ParserError: pass else: self._mwi_voicemail_uri = message_summary.message_account and SIPAddress(message_summary.message_account.replace('sip:', '', 1)) or None notification.center.post_notification('SIPAccountGotMessageSummary', sender=self, data=NotificationData(message_summary=message_summary)) def _NH_PresenceWinfoSubscriptionGotNotify(self, notification): if notification.data.body and notification.data.content_type == WatcherInfoDocument.content_type: try: watcher_info = WatcherInfoDocument.parse(notification.data.body) watcher_list = watcher_info['sip:' + self.id] except (ParserError, KeyError): pass else: if watcher_list.package != 'presence': return if self._pwi_version is None: if watcher_info.state == 'partial': self._pwi_subscriber.resubscribe() elif watcher_info.version <= self._pwi_version: return elif watcher_info.state == 'partial' and watcher_info.version > self._pwi_version + 1: self._pwi_subscriber.resubscribe() self._pwi_version = watcher_info.version data = NotificationData(version=watcher_info.version, state=watcher_info.state, watcher_list=watcher_list) notification.center.post_notification('SIPAccountGotPresenceWinfo', sender=self, data=data) def _NH_PresenceWinfoSubscriptionDidEnd(self, notification): self._pwi_version = None def _NH_PresenceWinfoSubscriptionDidFail(self, notification): self._pwi_version = None def _NH_DialogWinfoSubscriptionGotNotify(self, notification): if notification.data.body and notification.data.content_type == WatcherInfoDocument.content_type: try: watcher_info = WatcherInfoDocument.parse(notification.data.body) watcher_list = watcher_info['sip:' + self.id] except (ParserError, KeyError): pass else: if watcher_list.package != 'dialog': return if self._dwi_version is None: if watcher_info.state == 'partial': self._dwi_subscriber.resubscribe() elif watcher_info.version <= self._dwi_version: return elif watcher_info.state == 'partial' and watcher_info.version > self._dwi_version + 1: self._dwi_subscriber.resubscribe() self._dwi_version = watcher_info.version data = NotificationData(version=watcher_info.version, state=watcher_info.state, watcher_list=watcher_list) notification.center.post_notification('SIPAccountGotDialogWinfo', sender=self, data=data) def _NH_DialogWinfoSubscriptionDidEnd(self, notification): self._dwi_version = None def _NH_DialogWinfoSubscriptionDidFail(self, notification): self._dwi_version = None def _NH_PresenceSubscriptionGotNotify(self, notification): if notification.data.body and notification.data.content_type == RLSNotify.content_type: try: rls_notify = RLSNotify.parse('{content_type}\r\n\r\n{body}'.format(content_type=notification.data.headers['Content-Type'], body=notification.data.body)) except ParserError: pass else: if rls_notify.uri != self.xcap_manager.rls_presence_uri: return if self._presence_version is None: if not rls_notify.full_state: self._presence_subscriber.resubscribe() elif rls_notify.version <= self._presence_version: return elif not rls_notify.full_state and rls_notify.version > self._presence_version + 1: self._presence_subscriber.resubscribe() self._presence_version = rls_notify.version data = NotificationData(version=rls_notify.version, full_state=rls_notify.full_state, resource_map=dict((resource.uri, resource) for resource in rls_notify)) notification.center.post_notification('SIPAccountGotPresenceState', sender=self, data=data) def _NH_PresenceSubscriptionDidEnd(self, notification): self._presence_version = None def _NH_PresenceSubscriptionDidFail(self, notification): self._presence_version = None def _NH_SelfPresenceSubscriptionGotNotify(self, notification): if notification.data.body and notification.data.content_type == PIDFDocument.content_type: try: pidf_doc = PIDFDocument.parse(notification.data.body) except ParserError: pass else: if pidf_doc.entity.partition('sip:')[2] != self.id: return notification.center.post_notification('SIPAccountGotSelfPresenceState', sender=self, data=NotificationData(pidf=pidf_doc)) def _NH_DialogSubscriptionGotNotify(self, notification): if notification.data.body and notification.data.content_type == RLSNotify.content_type: try: rls_notify = RLSNotify.parse('{content_type}\r\n\r\n{body}'.format(content_type=notification.data.headers['Content-Type'], body=notification.data.body)) except ParserError: pass else: if rls_notify.uri != self.xcap_manager.rls_dialog_uri: return if self._dialog_version is None: if not rls_notify.full_state: self._dialog_subscriber.resubscribe() elif rls_notify.version <= self._dialog_version: return elif not rls_notify.full_state and rls_notify.version > self._dialog_version + 1: self._dialog_subscriber.resubscribe() self._dialog_version = rls_notify.version data = NotificationData(version=rls_notify.version, full_state=rls_notify.full_state, resource_map=dict((resource.uri, resource) for resource in rls_notify)) notification.center.post_notification('SIPAccountGotDialogState', sender=self, data=data) def _NH_DialogSubscriptionDidEnd(self, notification): self._dialog_version = None def _NH_DialogSubscriptionDidFail(self, notification): self._dialog_version = None def _activate(self): with self._activation_lock: if self._active: return notification_center = NotificationCenter() notification_center.post_notification('SIPAccountWillActivate', sender=self) self._active = True self._registrar.start() self._mwi_subscriber.start() self._pwi_subscriber.start() self._dwi_subscriber.start() self._presence_subscriber.start() self._self_presence_subscriber.start() self._dialog_subscriber.start() self._presence_publisher.start() self._dialog_publisher.start() if self.xcap.enabled: self.xcap_manager.start() notification_center.post_notification('SIPAccountDidActivate', sender=self) def _deactivate(self): with self._activation_lock: if not self._active: return notification_center = NotificationCenter() notification_center.post_notification('SIPAccountWillDeactivate', sender=self) self._active = False handlers = [self._registrar, self._mwi_subscriber, self._pwi_subscriber, self._dwi_subscriber, self._presence_subscriber, self._self_presence_subscriber, self._dialog_subscriber, self._presence_publisher, self._dialog_publisher, self.xcap_manager] proc.waitall([proc.spawn(handler.stop) for handler in handlers]) notification_center.post_notification('SIPAccountDidDeactivate', sender=self) def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.id) def __setstate__(self, data): # This restores the password from its previous location as a top level setting # after it was moved under the auth group. SettingsObject.__setstate__(self, data) if not data.get('auth', {}).get('password') and data.get('password'): self.auth.password = data.pop('password') self.save() class BonjourMSRPSettings(SettingsGroup): transport = Setting(type=MSRPTransport, default='tcp') class BonjourAccountEnabledSetting(Setting): def __get__(self, obj, objtype): if obj is None: return self return _bonjour.available and self.values.get(obj, self.default) def __set__(self, obj, value): if not _bonjour.available: raise RuntimeError('mdns support is not available') Setting.__set__(self, obj, value) class BonjourAccount(SettingsObject): """ Object representing a bonjour account. Contains configuration settings and attributes for accessing bonjour related options. When the account is active, it will send broadcast its contact address on the LAN. If the object is un-pickled and its enabled flag was set, it will automatically activate. When the save method is called, depending on the value of the enabled flag, the account will activate/deactivate. Notifications sent by instances of Account: * CFGSettingsObjectWasCreated * CFGSettingsObjectWasActivated * CFGSettingsObjectWasDeleted * CFGSettingsObjectDidChange * SIPAccountWillActivate * SIPAccountDidActivate * SIPAccountWillDeactivate * SIPAccountDidDeactivate """ implements(IObserver) __group__ = 'Accounts' __id__ = SIPAddress('bonjour@local') id = property(lambda self: self.__id__) enabled = BonjourAccountEnabledSetting(type=bool, default=True) display_name = Setting(type=unicode, default=user_info.fullname, nillable=False) msrp = BonjourMSRPSettings presence = PresenceSettings rtp = RTPSettings tls = TLSSettings def __new__(cls): with AccountManager.load.lock: if not AccountManager.load.called: raise RuntimeError("cannot instantiate %s before calling AccountManager.load" % cls.__name__) return SettingsObject.__new__(cls) def __init__(self): self.contact = ContactURIFactory() self.credentials = None self._started = False self._active = False self._activation_lock = coros.Semaphore(1) self._bonjour_services = BonjourServices(self) # initialize fake settings (these are here to make the bonjour account quack like a duck) self.nat_traversal = NATTraversalSettings() self.nat_traversal.use_ice = False self.nat_traversal.msrp_relay = None self.nat_traversal.use_msrp_relay_for_outbound = False self.xcap = XCAPSettings() self.xcap.enabled = False self.xcap.discovered = False self.xcap.xcap_root = None def __repr__(self): return '%s()' % self.__class__.__name__ def start(self): if self._started: return self._started = True notification_center = NotificationCenter() notification_center.add_observer(self, name='CFGSettingsObjectDidChange', sender=self) notification_center.add_observer(self, name='CFGSettingsObjectDidChange', sender=SIPSimpleSettings()) self._bonjour_services.start() if self.enabled: self._activate() def stop(self): if not self._started: return self._started = False self._deactivate() self._bonjour_services.stop() notification_center = NotificationCenter() notification_center.remove_observer(self, name='CFGSettingsObjectDidChange', sender=self) notification_center.remove_observer(self, name='CFGSettingsObjectDidChange', sender=SIPSimpleSettings()) @classproperty def mdns_available(cls): return _bonjour.available @property def registered(self): return False @property def tls_credentials(self): # This property can be optimized to cache the credentials it loads from disk, # however this is not a time consuming operation (~ 3000 req/sec). -Luci settings = SIPSimpleSettings() if self.tls.certificate is not None: certificate_data = open(self.tls.certificate.normalized).read() certificate = X509Certificate(certificate_data) private_key = X509PrivateKey(certificate_data) else: certificate = None private_key = None if settings.tls.ca_list is not None: # we should read all certificates in the file, rather than just the first -Luci trusted = [X509Certificate(open(settings.tls.ca_list.normalized).read())] else: trusted = [] credentials = X509Credentials(certificate, private_key, trusted) credentials.verify_peer = self.tls.verify_server return credentials @property def uri(self): return SIPURI(user=self.contact.username, host=Host.default_ip or '127.0.0.1') @property def presence_state(self): return self._bonjour_services.presence_state @presence_state.setter def presence_state(self, state): self._bonjour_services.presence_state = state def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) @run_in_green_thread def _NH_CFGSettingsObjectDidChange(self, notification): if self._started: if 'enabled' in notification.data.modified: if self.enabled: self._activate() else: self._deactivate() elif self.enabled: if 'display_name' in notification.data.modified: self._bonjour_services.update_registrations() if {'sip.transport_list', 'tls.certificate'}.intersection(notification.data.modified): self._bonjour_services.update_registrations() self._bonjour_services.restart_discovery() def _activate(self): with self._activation_lock: if self._active: return notification_center = NotificationCenter() notification_center.post_notification('SIPAccountWillActivate', sender=self) self._active = True self._bonjour_services.activate() notification_center.post_notification('SIPAccountDidActivate', sender=self) def _deactivate(self): with self._activation_lock: if not self._active: return notification_center = NotificationCenter() notification_center.post_notification('SIPAccountWillDeactivate', sender=self) self._active = False self._bonjour_services.deactivate() notification_center.post_notification('SIPAccountDidDeactivate', sender=self) class AccountManager(object): """ This is a singleton object which manages all the SIP accounts. It is also used to manage the default account (the one used for outbound sessions) using the default_account attribute: manager = AccountManager() manager.default_account = manager.get_account('alice@example.net') The following notifications are sent: * SIPAccountManagerDidRemoveAccount * SIPAccountManagerDidAddAccount * SIPAccountManagerDidChangeDefaultAccount """ __metaclass__ = Singleton implements(IObserver) def __init__(self): self._lock = Lock() self.accounts = {} notification_center = NotificationCenter() notification_center.add_observer(self, name='CFGSettingsObjectWasActivated') notification_center.add_observer(self, name='CFGSettingsObjectWasCreated') @execute_once def load(self): """ Load all accounts from the configuration. The accounts will not be started until the start method is called. """ configuration = ConfigurationManager() bonjour_account = BonjourAccount() names = configuration.get_names([Account.__group__]) [Account(id) for id in names if id != bonjour_account.id] default_account = self.default_account if default_account is None or not default_account.enabled: try: self.default_account = (account for account in self.accounts.itervalues() if account.enabled).next() except StopIteration: self.default_account = None def start(self): """ Start the accounts, which will determine the ones with the enabled flag set to activate. """ notification_center = NotificationCenter() notification_center.post_notification('SIPAccountManagerWillStart', sender=self) proc.waitall([proc.spawn(account.start) for account in self.accounts.itervalues()]) notification_center.post_notification('SIPAccountManagerDidStart', sender=self) def stop(self): """ Stop the accounts, which will determine the ones that were enabled to deactivate. This method returns only once the accounts were stopped successfully or they timed out trying. """ notification_center = NotificationCenter() notification_center.post_notification('SIPAccountManagerWillEnd', sender=self) proc.waitall([proc.spawn(account.stop) for account in self.accounts.itervalues()]) notification_center.post_notification('SIPAccountManagerDidEnd', sender=self) def has_account(self, id): return id in self.accounts def get_account(self, id): return self.accounts[id] def get_accounts(self): return self.accounts.values() def iter_accounts(self): return self.accounts.itervalues() def find_account(self, contact_uri): # compare contact_address with account contact exact_matches = (account for account in self.accounts.itervalues() if account.enabled and account.contact.username==contact_uri.user) # compare username in contact URI with account username loose_matches = (account for account in self.accounts.itervalues() if account.enabled and account.id.username==contact_uri.user) return chain(exact_matches, loose_matches, [None]).next() def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_CFGSettingsObjectWasActivated(self, notification): if isinstance(notification.sender, Account) or (isinstance(notification.sender, BonjourAccount) and _bonjour.available): account = notification.sender self.accounts[account.id] = account notification.center.add_observer(self, sender=account, name='CFGSettingsObjectDidChange') notification.center.add_observer(self, sender=account, name='CFGSettingsObjectWasDeleted') notification.center.post_notification('SIPAccountManagerDidAddAccount', sender=self, data=NotificationData(account=account)) from sipsimple.application import SIPApplication if SIPApplication.running: call_in_green_thread(account.start) def _NH_CFGSettingsObjectWasCreated(self, notification): if isinstance(notification.sender, Account): account = notification.sender if account.enabled and self.default_account is None: self.default_account = account def _NH_CFGSettingsObjectWasDeleted(self, notification): account = notification.sender del self.accounts[account.id] notification.center.remove_observer(self, sender=account, name='CFGSettingsObjectDidChange') notification.center.remove_observer(self, sender=account, name='CFGSettingsObjectWasDeleted') notification.center.post_notification('SIPAccountManagerDidRemoveAccount', sender=self, data=NotificationData(account=account)) def _NH_CFGSettingsObjectDidChange(self, notification): account = notification.sender if '__id__' in notification.data.modified: modified_id = notification.data.modified['__id__'] self.accounts[modified_id.new] = self.accounts.pop(modified_id.old) if 'enabled' in notification.data.modified: if account.enabled and self.default_account is None: self.default_account = account elif not account.enabled and self.default_account is account: try: self.default_account = (account for account in self.accounts.itervalues() if account.enabled).next() except StopIteration: self.default_account = None @property def default_account(self): settings = SIPSimpleSettings() return self.accounts.get(settings.default_account, None) @default_account.setter def default_account(self, account): if account is not None and not account.enabled: raise ValueError("account %s is not enabled" % account.id) notification_center = NotificationCenter() settings = SIPSimpleSettings() with self._lock: old_account = self.accounts.get(settings.default_account, None) if account is old_account: return if account is None: settings.default_account = None else: settings.default_account = account.id settings.save() # we need to post the notification in the file-io thread in order to have it serialized after the # SIPAccountManagerDidAddAccount notification that is triggered when the account is saved the first # time, because save is executed in the file-io thread while this runs in the current thread. -Dan call_in_thread('file-io', notification_center.post_notification, 'SIPAccountManagerDidChangeDefaultAccount', sender=self, data=NotificationData(old_account=old_account, account=account)) ================================================ FILE: sipsimple/account/bonjour/__init__.py ================================================ """Implements Bonjour service handlers""" __all__ = ['BonjourServices'] import re import uuid from threading import Lock from weakref import WeakKeyDictionary from application.notification import IObserver, NotificationCenter, NotificationData from application.python import Null from eventlib import api, coros, proc from eventlib.green import select from twisted.internet import reactor from zope.interface import implements from sipsimple import log from sipsimple.account.bonjour import _bonjour from sipsimple.core import FrozenSIPURI, SIPCoreError, NoGRUU from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.threading import call_in_twisted_thread, run_in_twisted_thread from sipsimple.threading.green import Command class RestartSelect(Exception): pass class BonjourFile(object): __instances__ = WeakKeyDictionary() def __new__(cls, file): if cls is BonjourFile: raise TypeError("BonjourFile cannot be instantiated directly") instance = cls.__instances__.get(file) if instance is None: instance = object.__new__(cls) instance.file = file instance.active = False cls.__instances__[file] = instance return instance def fileno(self): return self.file.fileno() if not self.closed else -1 def close(self): self.file.close() self.file = None @property def closed(self): return self.file is None @classmethod def find_by_file(cls, file): """Return the instance matching the given DNSServiceRef file""" try: return cls.__instances__[file] except KeyError: raise KeyError("cannot find a %s matching the given DNSServiceRef file" % cls.__name__) class BonjourDiscoveryFile(BonjourFile): def __new__(cls, file, transport): instance = BonjourFile.__new__(cls, file) instance.transport = transport return instance class BonjourRegistrationFile(BonjourFile): def __new__(cls, file, transport): instance = BonjourFile.__new__(cls, file) instance.transport = transport return instance class BonjourResolutionFile(BonjourFile): def __new__(cls, file, discovery_file, service_description): instance = BonjourFile.__new__(cls, file) instance.discovery_file = discovery_file instance.service_description = service_description return instance @property def transport(self): return self.discovery_file.transport class BonjourServiceDescription(object): def __init__(self, name, type, domain): self.name = name self.type = type self.domain = domain def __repr__(self): return "%s(%r, %r, %r)" % (self.__class__.__name__, self.name, self.type, self.domain) def __hash__(self): return hash(self.name) def __eq__(self, other): if isinstance(other, BonjourServiceDescription): return self.name==other.name and self.type==other.type and self.domain==other.domain return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal class BonjourNeighbourPresence(object): def __init__(self, state, note): self.state = state self.note = note class BonjourNeighbourRecord(object): def __init__(self, service_description, host, txtrecord): self.id = txtrecord.get('instance_id', None) self.name = txtrecord.get('name', '').decode('utf-8') or None self.host = re.match(r'^(?P.*?)(\.local)?\.?$', host).group('host') self.uri = FrozenSIPURI.parse(txtrecord.get('contact', service_description.name)) self.presence = BonjourNeighbourPresence(txtrecord.get('state', txtrecord.get('status', None)), txtrecord.get('note', '').decode('utf-8') or None) # status is read for legacy (remove later) -Dan class BonjourServices(object): implements(IObserver) def __init__(self, account): self.account = account self._started = False self._files = [] self._neighbours = {} self._command_channel = coros.queue() self._select_proc = None self._discover_timer = None self._register_timer = None self._update_timer = None self._lock = Lock() self.__dict__['presence_state'] = None def start(self): notification_center = NotificationCenter() notification_center.add_observer(self, name='NetworkConditionsDidChange') self._select_proc = proc.spawn(self._process_files) proc.spawn(self._handle_commands) def stop(self): notification_center = NotificationCenter() notification_center.remove_observer(self, name='NetworkConditionsDidChange') self._select_proc.kill() self._command_channel.send_exception(api.GreenletExit) def activate(self): self._started = True self._command_channel.send(Command('register')) self._command_channel.send(Command('discover')) def deactivate(self): command = Command('stop') self._command_channel.send(command) command.wait() self._started = False def restart_discovery(self): self._command_channel.send(Command('discover')) def restart_registration(self): self._command_channel.send(Command('unregister')) self._command_channel.send(Command('register')) def update_registrations(self): self._command_channel.send(Command('update_registrations')) @property def presence_state(self): return self.__dict__['presence_state'] @presence_state.setter def presence_state(self, state): if state is not None and not isinstance(state, BonjourPresenceState): raise ValueError("state must be a BonjourPresenceState instance or None") with self._lock: old_state = self.__dict__['presence_state'] self.__dict__['presence_state'] = state if state != old_state: call_in_twisted_thread(self.update_registrations) def _register_cb(self, file, flags, error_code, name, regtype, domain): notification_center = NotificationCenter() file = BonjourRegistrationFile.find_by_file(file) if error_code == _bonjour.kDNSServiceErr_NoError: notification_center.post_notification('BonjourAccountRegistrationDidSucceed', sender=self.account, data=NotificationData(name=name, transport=file.transport)) else: error = _bonjour.BonjourError(error_code) notification_center.post_notification('BonjourAccountRegistrationDidFail', sender=self.account, data=NotificationData(reason=str(error), transport=file.transport)) self._files.remove(file) self._select_proc.kill(RestartSelect) file.close() if self._register_timer is None: self._register_timer = reactor.callLater(1, self._command_channel.send, Command('register')) def _browse_cb(self, file, flags, interface_index, error_code, service_name, regtype, reply_domain): notification_center = NotificationCenter() file = BonjourDiscoveryFile.find_by_file(file) service_description = BonjourServiceDescription(service_name, regtype, reply_domain) if error_code != _bonjour.kDNSServiceErr_NoError: error = _bonjour.BonjourError(error_code) notification_center.post_notification('BonjourAccountDiscoveryDidFail', sender=self.account, data=NotificationData(reason=str(error), transport=file.transport)) removed_files = [file] + [f for f in self._files if isinstance(f, BonjourResolutionFile) and f.discovery_file==file] for f in removed_files: self._files.remove(f) self._select_proc.kill(RestartSelect) for f in removed_files: f.close() if self._discover_timer is None: self._discover_timer = reactor.callLater(1, self._command_channel.send, Command('discover')) return if reply_domain != 'local.': return if flags & _bonjour.kDNSServiceFlagsAdd: try: resolution_file = (f for f in self._files if isinstance(f, BonjourResolutionFile) and f.discovery_file==file and f.service_description==service_description).next() except StopIteration: try: resolution_file = _bonjour.DNSServiceResolve(0, interface_index, service_name, regtype, reply_domain, self._resolve_cb) except _bonjour.BonjourError, e: notification_center.post_notification('BonjourAccountDiscoveryFailure', sender=self.account, data=NotificationData(error=str(e), transport=file.transport)) else: resolution_file = BonjourResolutionFile(resolution_file, discovery_file=file, service_description=service_description) self._files.append(resolution_file) self._select_proc.kill(RestartSelect) else: try: resolution_file = (f for f in self._files if isinstance(f, BonjourResolutionFile) and f.discovery_file==file and f.service_description==service_description).next() except StopIteration: pass else: self._files.remove(resolution_file) self._select_proc.kill(RestartSelect) resolution_file.close() service_description = resolution_file.service_description if service_description in self._neighbours: record = self._neighbours.pop(service_description) notification_center.post_notification('BonjourAccountDidRemoveNeighbour', sender=self.account, data=NotificationData(neighbour=service_description, record=record)) def _resolve_cb(self, file, flags, interface_index, error_code, fullname, host_target, port, txtrecord): notification_center = NotificationCenter() settings = SIPSimpleSettings() file = BonjourResolutionFile.find_by_file(file) if error_code == _bonjour.kDNSServiceErr_NoError: service_description = file.service_description try: record = BonjourNeighbourRecord(service_description, host_target, _bonjour.TXTRecord.parse(txtrecord)) except SIPCoreError: pass else: transport = record.uri.transport supported_transport = transport in settings.sip.transport_list and (transport!='tls' or self.account.tls.certificate is not None) if not supported_transport and service_description in self._neighbours: record = self._neighbours.pop(service_description) notification_center.post_notification('BonjourAccountDidRemoveNeighbour', sender=self.account, data=NotificationData(neighbour=service_description, record=record)) elif supported_transport: try: our_contact_uri = self.account.contact[NoGRUU, transport] except KeyError: return if record.uri != our_contact_uri: had_neighbour = service_description in self._neighbours self._neighbours[service_description] = record notification_name = 'BonjourAccountDidUpdateNeighbour' if had_neighbour else 'BonjourAccountDidAddNeighbour' notification_data = NotificationData(neighbour=service_description, record=record) notification_center.post_notification(notification_name, sender=self.account, data=notification_data) else: self._files.remove(file) self._select_proc.kill(RestartSelect) file.close() error = _bonjour.BonjourError(error_code) notification_center.post_notification('BonjourAccountDiscoveryFailure', sender=self.account, data=NotificationData(error=str(error), transport=file.transport)) # start a new resolve process here? -Dan def _process_files(self): while True: try: ready = select.select([f for f in self._files if not f.active and not f.closed], [], [])[0] except RestartSelect: continue else: for file in ready: file.active = True self._command_channel.send(Command('process_results', files=[f for f in ready if not f.closed])) def _handle_commands(self): while True: command = self._command_channel.wait() if self._started: handler = getattr(self, '_CH_%s' % command.name) handler(command) def _CH_unregister(self, command): if self._register_timer is not None and self._register_timer.active(): self._register_timer.cancel() self._register_timer = None if self._update_timer is not None and self._update_timer.active(): self._update_timer.cancel() self._update_timer = None old_files = [] for file in (f for f in self._files[:] if isinstance(f, BonjourRegistrationFile)): old_files.append(file) self._files.remove(file) self._select_proc.kill(RestartSelect) for file in old_files: file.close() notification_center = NotificationCenter() for transport in set(file.transport for file in self._files): notification_center.post_notification('BonjourAccountRegistrationDidEnd', sender=self.account, data=NotificationData(transport=transport)) command.signal() def _CH_register(self, command): notification_center = NotificationCenter() settings = SIPSimpleSettings() if self._register_timer is not None and self._register_timer.active(): self._register_timer.cancel() self._register_timer = None supported_transports = set(transport for transport in settings.sip.transport_list if transport!='tls' or self.account.tls.certificate is not None) registered_transports = set(file.transport for file in self._files if isinstance(file, BonjourRegistrationFile)) missing_transports = supported_transports - registered_transports added_transports = set() for transport in missing_transports: notification_center.post_notification('BonjourAccountWillRegister', sender=self.account, data=NotificationData(transport=transport)) try: contact = self.account.contact[NoGRUU, transport] instance_id = str(uuid.UUID(settings.instance_id)) txtdata = dict(txtvers=1, name=self.account.display_name.encode('utf-8'), contact="<%s>" % str(contact), instance_id=instance_id) state = self.account.presence_state if self.account.presence.enabled and state is not None: txtdata['state'] = state.state txtdata['note'] = state.note.encode('utf-8') file = _bonjour.DNSServiceRegister(name=str(contact), regtype="_sipuri._%s" % (transport if transport == 'udp' else 'tcp'), port=contact.port, callBack=self._register_cb, txtRecord=_bonjour.TXTRecord(items=txtdata)) except (_bonjour.BonjourError, KeyError), e: notification_center.post_notification('BonjourAccountRegistrationDidFail', sender=self.account, data=NotificationData(reason=str(e), transport=transport)) else: self._files.append(BonjourRegistrationFile(file, transport)) added_transports.add(transport) if added_transports: self._select_proc.kill(RestartSelect) if added_transports != missing_transports: self._register_timer = reactor.callLater(1, self._command_channel.send, Command('register', command.event)) else: command.signal() def _CH_update_registrations(self, command): notification_center = NotificationCenter() settings = SIPSimpleSettings() if self._update_timer is not None and self._update_timer.active(): self._update_timer.cancel() self._update_timer = None available_transports = settings.sip.transport_list old_files = [] for file in (f for f in self._files[:] if isinstance(f, BonjourRegistrationFile) and f.transport not in available_transports): old_files.append(file) self._files.remove(file) self._select_proc.kill(RestartSelect) for file in old_files: file.close() update_failure = False for file in (f for f in self._files if isinstance(f, BonjourRegistrationFile)): try: contact = self.account.contact[NoGRUU, file.transport] instance_id = str(uuid.UUID(settings.instance_id)) txtdata = dict(txtvers=1, name=self.account.display_name.encode('utf-8'), contact="<%s>" % str(contact), instance_id=instance_id) state = self.account.presence_state if self.account.presence.enabled and state is not None: txtdata['state'] = state.state txtdata['note'] = state.note.encode('utf-8') _bonjour.DNSServiceUpdateRecord(file.file, None, flags=0, rdata=_bonjour.TXTRecord(items=txtdata), ttl=0) except (_bonjour.BonjourError, KeyError), e: notification_center.post_notification('BonjourAccountRegistrationUpdateDidFail', sender=self.account, data=NotificationData(reason=str(e), transport=file.transport)) update_failure = True self._command_channel.send(Command('register')) if update_failure: self._update_timer = reactor.callLater(1, self._command_channel.send, Command('update_registrations', command.event)) else: command.signal() def _CH_discover(self, command): notification_center = NotificationCenter() settings = SIPSimpleSettings() if self._discover_timer is not None and self._discover_timer.active(): self._discover_timer.cancel() self._discover_timer = None supported_transports = set(transport for transport in settings.sip.transport_list if transport!='tls' or self.account.tls.certificate is not None) discoverable_transports = set('tcp' if transport=='tls' else transport for transport in supported_transports) old_files = [] for file in (f for f in self._files[:] if isinstance(f, (BonjourDiscoveryFile, BonjourResolutionFile)) and f.transport not in discoverable_transports): old_files.append(file) self._files.remove(file) self._select_proc.kill(RestartSelect) for file in old_files: file.close() for service_description in [service for service, record in self._neighbours.iteritems() if record.uri.transport not in supported_transports]: record = self._neighbours.pop(service_description) notification_center.post_notification('BonjourAccountDidRemoveNeighbour', sender=self.account, data=NotificationData(neighbour=service_description, record=record)) discovered_transports = set(file.transport for file in self._files if isinstance(file, BonjourDiscoveryFile)) missing_transports = discoverable_transports - discovered_transports added_transports = set() for transport in missing_transports: notification_center.post_notification('BonjourAccountWillInitiateDiscovery', sender=self.account, data=NotificationData(transport=transport)) try: file = _bonjour.DNSServiceBrowse(regtype="_sipuri._%s" % transport, callBack=self._browse_cb) except _bonjour.BonjourError, e: notification_center.post_notification('BonjourAccountDiscoveryDidFail', sender=self.account, data=NotificationData(reason=str(e), transport=transport)) else: self._files.append(BonjourDiscoveryFile(file, transport)) added_transports.add(transport) if added_transports: self._select_proc.kill(RestartSelect) if added_transports != missing_transports: self._discover_timer = reactor.callLater(1, self._command_channel.send, Command('discover', command.event)) else: command.signal() def _CH_process_results(self, command): for file in (f for f in command.files if not f.closed): try: _bonjour.DNSServiceProcessResult(file.file) except: # Should we close the file? The documentation doesn't say anything about this. -Luci log.exception() for file in command.files: file.active = False self._files = [f for f in self._files if not f.closed] self._select_proc.kill(RestartSelect) def _CH_stop(self, command): if self._discover_timer is not None and self._discover_timer.active(): self._discover_timer.cancel() self._discover_timer = None if self._register_timer is not None and self._register_timer.active(): self._register_timer.cancel() self._register_timer = None if self._update_timer is not None and self._update_timer.active(): self._update_timer.cancel() self._update_timer = None files = self._files neighbours = self._neighbours self._files = [] self._select_proc.kill(RestartSelect) self._neighbours = {} for file in files: file.close() notification_center = NotificationCenter() for neighbour, record in neighbours.iteritems(): notification_center.post_notification('BonjourAccountDidRemoveNeighbour', sender=self.account, data=NotificationData(neighbour=neighbour, record=record)) for transport in set(file.transport for file in files): notification_center.post_notification('BonjourAccountRegistrationDidEnd', sender=self.account, data=NotificationData(transport=transport)) command.signal() @run_in_twisted_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_NetworkConditionsDidChange(self, notification): if self._files: self.restart_discovery() self.restart_registration() class BonjourPresenceState(object): def __init__(self, state, note=None): self.state = state self.note = note or u'' def __eq__(self, other): if isinstance(other, BonjourPresenceState): return self.state == other.state and self.note == other.note return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal ================================================ FILE: sipsimple/account/bonjour/_bonjour.py ================================================ ################################################################################ # # Copyright (c) 2007-2008 Christopher J. Stawarz # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # ################################################################################ """ Pure-Python interface to Apple Bonjour and compatible DNS-SD libraries pybonjour provides a pure-Python interface (via ctypes) to Apple Bonjour and compatible DNS-SD libraries (such as Avahi). It allows Python scripts to take advantage of Zero Configuration Networking (Zeroconf) to register, discover, and resolve services on both local and wide-area networks. Since pybonjour is implemented in pure Python, scripts that use it can easily be ported to Mac OS X, Windows, Linux, and other systems that run Bonjour. Note on strings: Internally, all strings used in DNS-SD are UTF-8 strings. String arguments passed to the DNS-SD functions provided by pybonjour must be either unicode instances or str instances that can be converted to unicode using the default encoding. (Passing a non-convertible str will result in an exception.) Strings returned from pybonjour (either directly from API functions or passed to application callbacks) are always unicode instances. """ __author__ = 'Christopher Stawarz ' __version__ = '1.1.1' __revision__ = int('$Revision: 6125 $'.split()[1]) import ctypes import os import re import socket import sys from application.python import Null ################################################################################ # # Global setup # ################################################################################ class _DummyLock(object): @staticmethod def acquire(): pass @staticmethod def release(): pass _global_lock = _DummyLock() if sys.platform == 'win32': # Need to use the stdcall variants try: _libdnssd = ctypes.windll.dnssd except: _libdnssd = _CFunc = Null available = False else: _CFunc = ctypes.WINFUNCTYPE available = True else: if sys.platform == 'darwin': _libdnssd = 'libSystem.B.dylib' else: _libdnssd = 'libdns_sd.so.1' # If libdns_sd is actually Avahi's Bonjour compatibility # layer, silence its annoying warning messages, and use a real # RLock as the global lock, since the compatibility layer # isn't thread safe. try: ctypes.cdll.LoadLibrary('libavahi-client.so.3') except OSError: pass else: os.environ['AVAHI_COMPAT_NOWARN'] = '1' from eventlib.green.threading import RLock _global_lock = RLock() try: _libdnssd = ctypes.cdll.LoadLibrary(_libdnssd) except: _libdnssd = _CFunc = Null available = False else: _CFunc = ctypes.CFUNCTYPE available = True ################################################################################ # # Constants # ################################################################################ # # General flags # kDNSServiceFlagsMoreComing = 0x1 kDNSServiceFlagsAdd = 0x2 kDNSServiceFlagsDefault = 0x4 kDNSServiceFlagsNoAutoRename = 0x8 kDNSServiceFlagsShared = 0x10 kDNSServiceFlagsUnique = 0x20 kDNSServiceFlagsBrowseDomains = 0x40 kDNSServiceFlagsRegistrationDomains = 0x80 kDNSServiceFlagsLongLivedQuery = 0x100 kDNSServiceFlagsAllowRemoteQuery = 0x200 kDNSServiceFlagsForceMulticast = 0x400 kDNSServiceFlagsReturnCNAME = 0x800 # # Service classes # kDNSServiceClass_IN = 1 # # Service types # kDNSServiceType_A = 1 kDNSServiceType_NS = 2 kDNSServiceType_MD = 3 kDNSServiceType_MF = 4 kDNSServiceType_CNAME = 5 kDNSServiceType_SOA = 6 kDNSServiceType_MB = 7 kDNSServiceType_MG = 8 kDNSServiceType_MR = 9 kDNSServiceType_NULL = 10 kDNSServiceType_WKS = 11 kDNSServiceType_PTR = 12 kDNSServiceType_HINFO = 13 kDNSServiceType_MINFO = 14 kDNSServiceType_MX = 15 kDNSServiceType_TXT = 16 kDNSServiceType_RP = 17 kDNSServiceType_AFSDB = 18 kDNSServiceType_X25 = 19 kDNSServiceType_ISDN = 20 kDNSServiceType_RT = 21 kDNSServiceType_NSAP = 22 kDNSServiceType_NSAP_PTR = 23 kDNSServiceType_SIG = 24 kDNSServiceType_KEY = 25 kDNSServiceType_PX = 26 kDNSServiceType_GPOS = 27 kDNSServiceType_AAAA = 28 kDNSServiceType_LOC = 29 kDNSServiceType_NXT = 30 kDNSServiceType_EID = 31 kDNSServiceType_NIMLOC = 32 kDNSServiceType_SRV = 33 kDNSServiceType_ATMA = 34 kDNSServiceType_NAPTR = 35 kDNSServiceType_KX = 36 kDNSServiceType_CERT = 37 kDNSServiceType_A6 = 38 kDNSServiceType_DNAME = 39 kDNSServiceType_SINK = 40 kDNSServiceType_OPT = 41 kDNSServiceType_TKEY = 249 kDNSServiceType_TSIG = 250 kDNSServiceType_IXFR = 251 kDNSServiceType_AXFR = 252 kDNSServiceType_MAILB = 253 kDNSServiceType_MAILA = 254 kDNSServiceType_ANY = 255 # # Error codes # kDNSServiceErr_NoError = 0 kDNSServiceErr_Unknown = -65537 kDNSServiceErr_NoSuchName = -65538 kDNSServiceErr_NoMemory = -65539 kDNSServiceErr_BadParam = -65540 kDNSServiceErr_BadReference = -65541 kDNSServiceErr_BadState = -65542 kDNSServiceErr_BadFlags = -65543 kDNSServiceErr_Unsupported = -65544 kDNSServiceErr_NotInitialized = -65545 kDNSServiceErr_AlreadyRegistered = -65547 kDNSServiceErr_NameConflict = -65548 kDNSServiceErr_Invalid = -65549 kDNSServiceErr_Firewall = -65550 kDNSServiceErr_Incompatible = -65551 kDNSServiceErr_BadInterfaceIndex = -65552 kDNSServiceErr_Refused = -65553 kDNSServiceErr_NoSuchRecord = -65554 kDNSServiceErr_NoAuth = -65555 kDNSServiceErr_NoSuchKey = -65556 kDNSServiceErr_NATTraversal = -65557 kDNSServiceErr_DoubleNAT = -65558 kDNSServiceErr_BadTime = -65559 # # Other constants # kDNSServiceMaxServiceName = 64 kDNSServiceMaxDomainName = 1005 kDNSServiceInterfaceIndexAny = 0 kDNSServiceInterfaceIndexLocalOnly = -1 ################################################################################ # # Error handling # ################################################################################ class BonjourError(Exception): """ Exception representing an error returned by the DNS-SD library. The errorCode attribute contains the actual integer error code returned. """ _errmsg = { kDNSServiceErr_NoSuchName: 'no such name', kDNSServiceErr_NoMemory: 'no memory', kDNSServiceErr_BadParam: 'bad param', kDNSServiceErr_BadReference: 'bad reference', kDNSServiceErr_BadState: 'bad state', kDNSServiceErr_BadFlags: 'bad flags', kDNSServiceErr_Unsupported: 'unsupported', kDNSServiceErr_NotInitialized: 'not initialized', kDNSServiceErr_AlreadyRegistered: 'already registered', kDNSServiceErr_NameConflict: 'name conflict', kDNSServiceErr_Invalid: 'invalid', kDNSServiceErr_Firewall: 'firewall', kDNSServiceErr_Incompatible: 'incompatible', kDNSServiceErr_BadInterfaceIndex: 'bad interface index', kDNSServiceErr_Refused: 'refused', kDNSServiceErr_NoSuchRecord: 'no such record', kDNSServiceErr_NoAuth: 'no auth', kDNSServiceErr_NoSuchKey: 'no such key', kDNSServiceErr_NATTraversal: 'NAT traversal', kDNSServiceErr_DoubleNAT: 'double NAT', kDNSServiceErr_BadTime: 'bad time', } @classmethod def _errcheck(cls, result, func, args): if result != kDNSServiceErr_NoError: raise cls(result) return args def __init__(self, errorCode): self.errorCode = errorCode Exception.__init__(self, (errorCode, self._errmsg.get(errorCode, 'unknown'))) ################################################################################ # # Data types # ################################################################################ class _utf8_char_p(ctypes.c_char_p): @classmethod def from_param(cls, obj): if (obj is not None) and (not isinstance(obj, cls)): if not isinstance(obj, basestring): raise TypeError('parameter must be a string type instance') if not isinstance(obj, unicode): obj = unicode(obj) obj = obj.encode('utf-8') return ctypes.c_char_p.from_param(obj) def decode(self): if self.value is None: return None return self.value.decode('utf-8') class _utf8_char_p_non_null(_utf8_char_p): @classmethod def from_param(cls, obj): if obj is None: raise ValueError('parameter cannot be None') return _utf8_char_p.from_param(obj) _DNSServiceFlags = ctypes.c_uint32 _DNSServiceErrorType = ctypes.c_int32 class DNSRecordRef(ctypes.c_void_p): """ A DNSRecordRef pointer. DO NOT CREATE INSTANCES OF THIS CLASS! Only instances returned by the DNS-SD library are valid. Using others will likely cause the Python interpreter to crash. Application code should not use any of the methods of this class. The only valid use of a DNSRecordRef instance is as an argument to a DNS-SD function. To compare two DNSRecordRef instances for equality, use '==' rather than 'is'. """ @classmethod def from_param(cls, obj): if type(obj) is not cls: raise TypeError("expected '%s', got '%s'" % (cls.__name__, type(obj).__name__)) if obj.value is None: raise ValueError('invalid %s instance' % cls.__name__) return obj def __eq__(self, other): return ((type(other) is type(self)) and (other.value == self.value)) def __ne__(self, other): return not (other == self) def __hash__(self): return hash(self.value) def _invalidate(self): self.value = None def _valid(self): return (self.value is not None) class _DNSRecordRef_or_null(DNSRecordRef): @classmethod def from_param(cls, obj): if obj is None: return obj return DNSRecordRef.from_param(obj) class DNSServiceRef(DNSRecordRef): """ A DNSServiceRef pointer. DO NOT CREATE INSTANCES OF THIS CLASS! Only instances returned by the DNS-SD library are valid. Using others will likely cause the Python interpreter to crash. An instance of this class represents an active connection to the mDNS daemon. The connection remains open until the close() method is called (which also terminates the associated browse, resolve, etc.). Note that this method is *not* called automatically when the instance is deallocated; therefore, application code must be certain to call close() when the connection is no longer needed. The primary use of a DNSServiceRef instance is in conjunction with select() or poll() to determine when a response from the daemon is available. When the file descriptor returned by fileno() is ready for reading, a reply from the daemon is available and should be processed by passing the DNSServiceRef instance to DNSServiceProcessResult(), which will invoke the appropriate application callback function. (Note that the file descriptor should never be read from or written to directly.) The DNSServiceRef class supports the context management protocol introduced in Python 2.5, meaning applications can use the 'with' statement to ensure that DNSServiceRef instances are closed regardless of whether an exception occurs, e.g. sdRef = DNSServiceBrowse(...) with sdRef: # sdRef will be closed regardless of how this block is # exited ... To compare two DNSServiceRef instances for equality, use '==' rather than 'is'. """ def __init__(self, *args, **kwargs): DNSRecordRef.__init__(self, *args, **kwargs) # Since callback functions are called asynchronously, we need # to hold onto references to them for as long as they're in # use. Otherwise, Python could deallocate them before we call # DNSServiceProcessResult(), meaning the Bonjour library would # dereference freed memory when it tried to invoke the # callback. self._callbacks = [] # A DNSRecordRef is invalidated if DNSServiceRefDeallocate() # is called on the corresponding DNSServiceRef, so we need to # keep track of all our record refs and invalidate them when # we're closed. self._record_refs = [] def __enter__(self): return self def __exit__(self, type, value, traceback): self.close() def _add_callback(self, cb): self._callbacks.append(cb) def _add_record_ref(self, ref): self._record_refs.append(ref) def close(self): """ Close the connection to the mDNS daemon and terminate any associated browse, resolve, etc. operations. """ if self._valid(): for ref in self._record_refs: ref._invalidate() del self._record_refs _global_lock.acquire() try: _DNSServiceRefDeallocate(self) finally: _global_lock.release() self._invalidate() del self._callbacks def fileno(self): """ Return the file descriptor associated with this connection. This descriptor should never be read from or written to directly. It should only be passed to select() or poll() to determine when a response from the mDNS daemon is available. """ _global_lock.acquire() try: fd = _DNSServiceRefSockFD(self) finally: _global_lock.release() return fd _DNSServiceDomainEnumReply = _CFunc( None, DNSServiceRef, # sdRef _DNSServiceFlags, # flags ctypes.c_uint32, # interfaceIndex _DNSServiceErrorType, # errorCode _utf8_char_p, # replyDomain ctypes.c_void_p, # context ) _DNSServiceRegisterReply = _CFunc( None, DNSServiceRef, # sdRef _DNSServiceFlags, # flags _DNSServiceErrorType, # errorCode _utf8_char_p, # name _utf8_char_p, # regtype _utf8_char_p, # domain ctypes.c_void_p, # context ) _DNSServiceBrowseReply = _CFunc( None, DNSServiceRef, # sdRef _DNSServiceFlags, # flags ctypes.c_uint32, # interfaceIndex _DNSServiceErrorType, # errorCode _utf8_char_p, # serviceName _utf8_char_p, # regtype _utf8_char_p, # replyDomain ctypes.c_void_p, # context ) _DNSServiceResolveReply = _CFunc( None, DNSServiceRef, # sdRef _DNSServiceFlags, # flags ctypes.c_uint32, # interfaceIndex _DNSServiceErrorType, # errorCode _utf8_char_p, # fullname _utf8_char_p, # hosttarget ctypes.c_uint16, # port ctypes.c_uint16, # txtLen ctypes.c_void_p, # txtRecord (not null-terminated, so c_void_p) ctypes.c_void_p, # context ) _DNSServiceRegisterRecordReply = _CFunc( None, DNSServiceRef, # sdRef DNSRecordRef, # RecordRef _DNSServiceFlags, # flags _DNSServiceErrorType, # errorCode ctypes.c_void_p, # context ) _DNSServiceQueryRecordReply = _CFunc( None, DNSServiceRef, # sdRef _DNSServiceFlags, # flags ctypes.c_uint32, # interfaceIndex _DNSServiceErrorType, # errorCode _utf8_char_p, # fullname ctypes.c_uint16, # rrtype ctypes.c_uint16, # rrclass ctypes.c_uint16, # rdlen ctypes.c_void_p, # rdata ctypes.c_uint32, # ttl ctypes.c_void_p, # context ) ################################################################################ # # Low-level function bindings # ################################################################################ def _create_function_bindings(): ERRCHECK = True NO_ERRCHECK = False OUTPARAM = (lambda index: index) NO_OUTPARAM = None specs = { #'funcname': #( # return_type, # errcheck, # outparam, # ( # param_1_type, # param_2_type, # ... # param_n_type, # )), 'DNSServiceRefSockFD': ( ctypes.c_int, NO_ERRCHECK, NO_OUTPARAM, ( DNSServiceRef, # sdRef )), 'DNSServiceProcessResult': ( _DNSServiceErrorType, ERRCHECK, NO_OUTPARAM, ( DNSServiceRef, # sdRef )), 'DNSServiceRefDeallocate': ( None, NO_ERRCHECK, NO_OUTPARAM, ( DNSServiceRef, # sdRef )), 'DNSServiceEnumerateDomains': ( _DNSServiceErrorType, ERRCHECK, OUTPARAM(0), ( ctypes.POINTER(DNSServiceRef), # sdRef _DNSServiceFlags, # flags ctypes.c_uint32, # interfaceIndex _DNSServiceDomainEnumReply, # callBack ctypes.c_void_p, # context )), 'DNSServiceRegister': ( _DNSServiceErrorType, ERRCHECK, OUTPARAM(0), ( ctypes.POINTER(DNSServiceRef), # sdRef _DNSServiceFlags, # flags ctypes.c_uint32, # interfaceIndex _utf8_char_p, # name _utf8_char_p_non_null, # regtype _utf8_char_p, # domain _utf8_char_p, # host ctypes.c_uint16, # port ctypes.c_uint16, # txtLen ctypes.c_void_p, # txtRecord _DNSServiceRegisterReply, # callBack ctypes.c_void_p, # context )), 'DNSServiceAddRecord': ( _DNSServiceErrorType, ERRCHECK, OUTPARAM(1), ( DNSServiceRef, # sdRef ctypes.POINTER(DNSRecordRef), # RecordRef _DNSServiceFlags, # flags ctypes.c_uint16, # rrtype ctypes.c_uint16, # rdlen ctypes.c_void_p, # rdata ctypes.c_uint32, # ttl )), 'DNSServiceUpdateRecord': ( _DNSServiceErrorType, ERRCHECK, NO_OUTPARAM, ( DNSServiceRef, # sdRef _DNSRecordRef_or_null, # RecordRef _DNSServiceFlags, # flags ctypes.c_uint16, # rdlen ctypes.c_void_p, # rdata ctypes.c_uint32, # ttl )), 'DNSServiceRemoveRecord': ( _DNSServiceErrorType, ERRCHECK, NO_OUTPARAM, ( DNSServiceRef, # sdRef DNSRecordRef, # RecordRef _DNSServiceFlags, # flags )), 'DNSServiceBrowse': ( _DNSServiceErrorType, ERRCHECK, OUTPARAM(0), ( ctypes.POINTER(DNSServiceRef), # sdRef _DNSServiceFlags, # flags ctypes.c_uint32, # interfaceIndex _utf8_char_p_non_null, # regtype _utf8_char_p, # domain _DNSServiceBrowseReply, # callBack ctypes.c_void_p, # context )), 'DNSServiceResolve': ( _DNSServiceErrorType, ERRCHECK, OUTPARAM(0), ( ctypes.POINTER(DNSServiceRef), # sdRef _DNSServiceFlags, # flags ctypes.c_uint32, # interfaceIndex _utf8_char_p_non_null, # name _utf8_char_p_non_null, # regtype _utf8_char_p_non_null, # domain _DNSServiceResolveReply, # callBack ctypes.c_void_p, # context )), 'DNSServiceCreateConnection': ( _DNSServiceErrorType, ERRCHECK, OUTPARAM(0), ( ctypes.POINTER(DNSServiceRef), # sdRef )), 'DNSServiceRegisterRecord': ( _DNSServiceErrorType, ERRCHECK, OUTPARAM(1), ( DNSServiceRef, # sdRef ctypes.POINTER(DNSRecordRef), # RecordRef _DNSServiceFlags, # flags ctypes.c_uint32, # interfaceIndex _utf8_char_p_non_null, # fullname ctypes.c_uint16, # rrtype ctypes.c_uint16, # rrclass ctypes.c_uint16, # rdlen ctypes.c_void_p, # rdata ctypes.c_uint32, # ttl _DNSServiceRegisterRecordReply, # callBack ctypes.c_void_p, # context )), 'DNSServiceQueryRecord': ( _DNSServiceErrorType, ERRCHECK, OUTPARAM(0), ( ctypes.POINTER(DNSServiceRef), # sdRef _DNSServiceFlags, # flags ctypes.c_uint32, # interfaceIndex _utf8_char_p_non_null, # fullname ctypes.c_uint16, # rrtype ctypes.c_uint16, # rrclass _DNSServiceQueryRecordReply, # callBack ctypes.c_void_p, # context )), 'DNSServiceReconfirmRecord': ( None, # _DNSServiceErrorType in more recent versions NO_ERRCHECK, NO_OUTPARAM, ( _DNSServiceFlags, # flags ctypes.c_uint32, # interfaceIndex _utf8_char_p_non_null, # fullname ctypes.c_uint16, # rrtype ctypes.c_uint16, # rrclass ctypes.c_uint16, # rdlen ctypes.c_void_p, # rdata )), 'DNSServiceConstructFullName': ( ctypes.c_int, ERRCHECK, OUTPARAM(0), ( ctypes.c_char * kDNSServiceMaxDomainName, # fullName _utf8_char_p, # service _utf8_char_p_non_null, # regtype _utf8_char_p_non_null, # domain )), } for name, (restype, errcheck, outparam, argtypes) in specs.iteritems(): prototype = _CFunc(restype, *argtypes) paramflags = [1] * len(argtypes) if outparam is not None: paramflags[outparam] = 2 paramflags = tuple((val,) for val in paramflags) func = prototype((name, _libdnssd), paramflags) if errcheck: func.errcheck = BonjourError._errcheck globals()['_' + name] = func # preset these so that pyflakes doesn't complain _DNSServiceRefDeallocate = None _DNSServiceRefSockFD = None _DNSServiceProcessResult = None _DNSServiceEnumerateDomains = None _DNSServiceRegister = None _DNSServiceAddRecord = None _DNSServiceUpdateRecord = None _DNSServiceRemoveRecord = None _DNSServiceBrowse = None _DNSServiceResolve = None _DNSServiceCreateConnection = None _DNSServiceRegisterRecord = None _DNSServiceQueryRecord = None _DNSServiceReconfirmRecord = None _DNSServiceConstructFullName = None # Only need to do this once _create_function_bindings() del _create_function_bindings ################################################################################ # # Internal utility types and functions # ################################################################################ class _NoDefault(object): def __repr__(self): return '' def check(self, obj): if obj is self: raise ValueError('required parameter value missing') _NO_DEFAULT = _NoDefault() def _string_to_length_and_void_p(string): if isinstance(string, TXTRecord): string = str(string) void_p = ctypes.cast(ctypes.c_char_p(string), ctypes.c_void_p) return len(string), void_p def _length_and_void_p_to_string(length, void_p): char_p = ctypes.cast(void_p, ctypes.POINTER(ctypes.c_char)) return ''.join(char_p[i] for i in xrange(length)) ################################################################################ # # High-level functions # ################################################################################ def DNSServiceProcessResult( sdRef, ): """ Read a reply from the daemon, calling the appropriate application callback. This call will block until the daemon's response is received. Use sdRef in conjunction with select() or poll() to determine the presence of a response from the server before calling this function to process the reply without blocking. Call this function at any point if it is acceptable to block until the daemon's response arrives. Note that the client is responsible for ensuring that DNSServiceProcessResult() is called whenever there is a reply from the daemon; the daemon may terminate its connection with a client that does not process the daemon's responses. sdRef: A DNSServiceRef returned by any of the DNSService calls that take a callback parameter. """ _global_lock.acquire() try: _DNSServiceProcessResult(sdRef) finally: _global_lock.release() def DNSServiceEnumerateDomains( flags, interfaceIndex = kDNSServiceInterfaceIndexAny, callBack = None, ): """ Asynchronously enumerate domains available for browsing and registration. The enumeration MUST be cancelled by closing the returned DNSServiceRef when no more domains are to be found. flags: Possible values are: kDNSServiceFlagsBrowseDomains to enumerate domains recommended for browsing. kDNSServiceFlagsRegistrationDomains to enumerate domains recommended for registration. interfaceIndex: If non-zero, specifies the interface on which to look for domains. Most applications will pass kDNSServiceInterfaceIndexAny (0) to enumerate domains on all interfaces. callBack: The function to be called when a domain is found or the call asynchronously fails. Its signature should be callBack(sdRef, flags, interfaceIndex, errorCode, replyDomain). return value: A DNSServiceRef instance. Callback Parameters: sdRef: The DNSServiceRef returned by DNSServiceEnumerateDomains(). flags: Possible values are: kDNSServiceFlagsMoreComing kDNSServiceFlagsAdd kDNSServiceFlagsDefault interfaceIndex: Specifies the interface on which the domain exists. errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise indicates the failure that occurred (in which case other parameters are undefined). replyDomain: The name of the domain. """ @_DNSServiceDomainEnumReply def _callback(sdRef, flags, interfaceIndex, errorCode, replyDomain, context): if callBack is not None: callBack(sdRef, flags, interfaceIndex, errorCode, replyDomain.decode()) _global_lock.acquire() try: sdRef = _DNSServiceEnumerateDomains(flags, interfaceIndex, _callback, None) finally: _global_lock.release() sdRef._add_callback(_callback) return sdRef def DNSServiceRegister( flags = 0, interfaceIndex = kDNSServiceInterfaceIndexAny, name = None, regtype = _NO_DEFAULT, domain = None, host = None, port = _NO_DEFAULT, txtRecord = '', callBack = None, ): """ Register a service that is discovered via DNSServiceBrowse() and DNSServiceResolve() calls. flags: Indicates the renaming behavior on name conflict. Most applications will pass 0. interfaceIndex: If non-zero, specifies the interface on which to register the service. Most applications will pass kDNSServiceInterfaceIndexAny (0) to register on all available interfaces. name: If not None, specifies the service name to be registered. Most applications will not specify a name, in which case the computer name is used. (This name is communicated to the client via the callback.) If a name is specified, it must be 1-63 bytes of UTF-8 text. If the name is longer than 63 bytes, it will be automatically truncated to a legal length, unless the flag kDNSServiceFlagsNoAutoRename is set, in which case a BonjourError exception will be thrown. regtype: The service type followed by the protocol, separated by a dot (e.g. "_ftp._tcp"). The service type must be an underscore, followed by 1-14 characters, which may be letters, digits, or hyphens. The transport protocol must be "_tcp" or "_udp". New service types should be registered at . domain: If not None, specifies the domain on which to advertise the service. Most applications will not specify a domain, instead automatically registering in the default domain(s). host: If not None, specifies the SRV target host name. Most applications will not specify a host, instead automatically using the machine's default host name(s). Note that specifying a host name does NOT create an address record for that host; the application is responsible for ensuring that the appropriate address record exists, or creating it via DNSServiceRegisterRecord(). port: The port, in host (not network) byte order, on which the service accepts connections. Pass 0 for a "placeholder" service (i.e. a service that will not be discovered by browsing, but will cause a name conflict if another client tries to register that same name). Most clients will not use placeholder services. txtRecord: The TXT record rdata. If not None, txtRecord must be either a TXTRecord instance or a string containing a properly formatted DNS TXT record, i.e. ... callBack: The function to be called when the registration completes or asynchronously fails. Its signature should be callBack(sdRef, flags, errorCode, name, regtype, domain). The client MAY pass None for the callback, in which case the client will NOT be notified of the default values picked on its behalf, and the client will NOT be notified of any asynchronous errors (e.g. out of memory errors, etc.) that may prevent the registration of the service. The client may NOT pass the flag kDNSServiceFlagsNoAutoRename if the callback is None. The client may still deregister the service at any time by closing the returned DNSServiceRef. return value: A DNSServiceRef instance. The registration will remain active indefinitely until the client terminates it by closing the DNSServiceRef. Callback Parameters: sdRef: The DNSServiceRef returned by DNSServiceRegister(). flags: Currently unused, reserved for future use. errorCode: Will be kDNSServiceErr_NoError on success, otherwise will indicate the failure that occurred (including name conflicts, if the kDNSServiceFlagsNoAutoRename flag was used when registering). Other parameters are undefined if an error occurred. name: The service name registered. (If the application did not specify a name in DNSServiceRegister(), this indicates what name was automatically chosen.) regtype: The type of service registered, as it was passed to the callout. domain: The domain on which the service was registered. (If the application did not specify a domain in DNSServiceRegister(), this indicates the default domain on which the service was registered.) """ _NO_DEFAULT.check(regtype) _NO_DEFAULT.check(port) port = socket.htons(port) if not txtRecord: txtLen, txtRecord = 1, '\0' else: txtLen, txtRecord = _string_to_length_and_void_p(txtRecord) @_DNSServiceRegisterReply def _callback(sdRef, flags, errorCode, name, regtype, domain, context): if callBack is not None: callBack(sdRef, flags, errorCode, name.decode(), regtype.decode(), domain.decode()) _global_lock.acquire() try: sdRef = _DNSServiceRegister(flags, interfaceIndex, name, regtype, domain, host, port, txtLen, txtRecord, _callback, None) finally: _global_lock.release() sdRef._add_callback(_callback) return sdRef def DNSServiceAddRecord( sdRef, flags = 0, rrtype = _NO_DEFAULT, rdata = _NO_DEFAULT, ttl = 0, ): """ Add a record to a registered service. The name of the record will be the same as the registered service's name. The record can later be updated or deregistered by passing the DNSRecordRef returned by this function to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). Note that DNSServiceAddRecord/UpdateRecord/RemoveRecord are NOT thread-safe with respect to a single DNSServiceRef. If you plan to have multiple threads in your program simultaneously add, update, or remove records from the same DNSServiceRef, then it's the caller's responsibility to use a lock or take similar appropriate precautions to serialize those calls. sdRef: A DNSServiceRef returned by DNSServiceRegister(). flags: Currently ignored, reserved for future use. rrtype: The type of the record (e.g. kDNSServiceType_TXT, kDNSServiceType_SRV, etc.). rdata: A string containing the raw rdata to be contained in the added resource record. ttl: The time to live of the resource record, in seconds. Pass 0 to use a default value. return value: A DNSRecordRef instance, which may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). If sdRef is closed, the DNSRecordRef is also invalidated and may not be used further. """ _NO_DEFAULT.check(rrtype) _NO_DEFAULT.check(rdata) rdlen, rdata = _string_to_length_and_void_p(rdata) _global_lock.acquire() try: RecordRef = _DNSServiceAddRecord(sdRef, flags, rrtype, rdlen, rdata, ttl) finally: _global_lock.release() sdRef._add_record_ref(RecordRef) return RecordRef def DNSServiceUpdateRecord( sdRef, RecordRef = None, flags = 0, rdata = _NO_DEFAULT, ttl = 0, ): """ Update a registered resource record. The record must either be: - The primary txt record of a service registered via DNSServiceRegister(), or - A record added to a registered service via DNSServiceAddRecord(), or - An individual record registered by DNSServiceRegisterRecord() sdRef: A DNSServiceRef returned by DNSServiceRegister() or DNSServiceCreateConnection(). RecordRef: A DNSRecordRef returned by DNSServiceAddRecord(), or None to update the service's primary txt record. flags: Currently ignored, reserved for future use. rdata: A string containing the new rdata to be contained in the updated resource record. ttl: The time to live of the updated resource record, in seconds. """ _NO_DEFAULT.check(rdata) rdlen, rdata = _string_to_length_and_void_p(rdata) _global_lock.acquire() try: _DNSServiceUpdateRecord(sdRef, RecordRef, flags, rdlen, rdata, ttl) finally: _global_lock.release() def DNSServiceRemoveRecord( sdRef, RecordRef, flags = 0, ): """ Remove a record previously added to a service record set via DNSServiceAddRecord(), or deregister a record registered individually via DNSServiceRegisterRecord(). sdRef: A DNSServiceRef returned by DNSServiceRegister() (if the record being removed was registered via DNSServiceAddRecord()) or by DNSServiceCreateConnection() (if the record being removed was registered via DNSServiceRegisterRecord()). recordRef: A DNSRecordRef returned by DNSServiceAddRecord() or DNSServiceRegisterRecord(). flags: Currently ignored, reserved for future use. """ _global_lock.acquire() try: _DNSServiceRemoveRecord(sdRef, RecordRef, flags) finally: _global_lock.release() RecordRef._invalidate() def DNSServiceBrowse( flags = 0, interfaceIndex = kDNSServiceInterfaceIndexAny, regtype = _NO_DEFAULT, domain = None, callBack = None, ): """ Browse for instances of a service. flags: Currently ignored, reserved for future use. interfaceIndex: If non-zero, specifies the interface on which to browse for services. Most applications will pass kDNSServiceInterfaceIndexAny (0) to browse on all available interfaces. regtype: The service type being browsed for followed by the protocol, separated by a dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". domain: If not None, specifies the domain on which to browse for services. Most applications will not specify a domain, instead browsing on the default domain(s). callBack: The function to be called when an instance of the service being browsed for is found, or if the call asynchronously fails. Its signature should be callBack(sdRef, flags, interfaceIndex, errorCode, serviceName, regtype, replyDomain). return value: A DNSServiceRef instance. The browse operation will run indefinitely until the client terminates it by closing the DNSServiceRef. Callback Parameters: sdRef: The DNSServiceRef returned by DNSServiceBrowse(). flags: Possible values are kDNSServiceFlagsMoreComing and kDNSServiceFlagsAdd. interfaceIndex: The interface on which the service is advertised. This index should be passed to DNSServiceResolve() when resolving the service. errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will indicate the failure that occurred. Other parameters are undefined if an error occurred. serviceName: The discovered service name. This name should be displayed to the user and stored for subsequent use in the DNSServiceResolve() call. regtype: The service type, which is usually (but not always) the same as was passed to DNSServiceBrowse(). One case where the discovered service type may not be the same as the requested service type is when using subtypes: The client may want to browse for only those ftp servers that allow anonymous connections. The client will pass the string "_ftp._tcp,_anon" to DNSServiceBrowse(), but the type of the service that's discovered is simply "_ftp._tcp". The regtype for each discovered service instance should be stored along with the name, so that it can be passed to DNSServiceResolve() when the service is later resolved. replyDomain: The domain of the discovered service instance. This may or may not be the same as the domain that was passed to DNSServiceBrowse(). The domain for each discovered service instance should be stored along with the name, so that it can be passed to DNSServiceResolve() when the service is later resolved. """ _NO_DEFAULT.check(regtype) @_DNSServiceBrowseReply def _callback(sdRef, flags, interfaceIndex, errorCode, serviceName, regtype, replyDomain, context): if callBack is not None: callBack(sdRef, flags, interfaceIndex, errorCode, serviceName.decode(), regtype.decode(), replyDomain.decode()) _global_lock.acquire() try: sdRef = _DNSServiceBrowse(flags, interfaceIndex, regtype, domain, _callback, None) finally: _global_lock.release() sdRef._add_callback(_callback) return sdRef def DNSServiceResolve( flags = 0, interfaceIndex = _NO_DEFAULT, name = _NO_DEFAULT, regtype = _NO_DEFAULT, domain = _NO_DEFAULT, callBack = None, ): """ Resolve a service name discovered via DNSServiceBrowse() to a target host name, port number, and txt record. Note: Applications should NOT use DNSServiceResolve() solely for txt record monitoring; use DNSServiceQueryRecord() instead, as it is more efficient for this task. Note: When the desired results have been returned, the client MUST terminate the resolve by closing the returned DNSServiceRef. Note: DNSServiceResolve() behaves correctly for typical services that have a single SRV record and a single TXT record. To resolve non-standard services with multiple SRV or TXT records, DNSServiceQueryRecord() should be used. flags: Currently ignored, reserved for future use. interfaceIndex: The interface on which to resolve the service. If this resolve call is as a result of a currently active DNSServiceBrowse() operation, then the interfaceIndex should be the index reported in the browse callback. If this resolve call is using information previously saved (e.g. in a preference file) for later use, then use kDNSServiceInterfaceIndexAny (0), because the desired service may now be reachable via a different physical interface. name: The name of the service instance to be resolved, as reported to the DNSServiceBrowse() callback. regtype: The type of the service instance to be resolved, as reported to the DNSServiceBrowse() callback. domain: The domain of the service instance to be resolved, as reported to the DNSServiceBrowse() callback. callBack: The function to be called when a result is found, or if the call asynchronously fails. Its signature should be callBack(sdRef, flags, interfaceIndex, errorCode, fullname, hosttarget, port, txtRecord). return value: A DNSServiceRef instance. The resolve operation will run indefinitely until the client terminates it by closing the DNSServiceRef. Callback Parameters: sdRef: The DNSServiceRef returned by DNSServiceResolve(). flags: Currently unused, reserved for future use. interfaceIndex: The interface on which the service was resolved. errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will indicate the failure that occurred. Other parameters are undefined if an error occurred. fullname: The full service domain name, in the form ... hosttarget: The target hostname of the machine providing the service. port: The port, in host (not network) byte order, on which connections are accepted for this service. txtRecord: A string containing the service's primary txt record, in standard txt record format. """ _NO_DEFAULT.check(interfaceIndex) _NO_DEFAULT.check(name) _NO_DEFAULT.check(regtype) _NO_DEFAULT.check(domain) @_DNSServiceResolveReply def _callback(sdRef, flags, interfaceIndex, errorCode, fullname, hosttarget, port, txtLen, txtRecord, context): if callBack is not None: port = socket.ntohs(port) txtRecord = _length_and_void_p_to_string(txtLen, txtRecord) callBack(sdRef, flags, interfaceIndex, errorCode, fullname.decode(), hosttarget.decode(), port, txtRecord) _global_lock.acquire() try: sdRef = _DNSServiceResolve(flags, interfaceIndex, name, regtype, domain, _callback, None) finally: _global_lock.release() sdRef._add_callback(_callback) return sdRef def DNSServiceCreateConnection(): """ Create a connection to the daemon allowing efficient registration of multiple individual records. return value: A DNSServiceRef instance. Closing it severs the connection and deregisters all records registered on this connection. """ _global_lock.acquire() try: sdRef = _DNSServiceCreateConnection() finally: _global_lock.release() return sdRef def DNSServiceRegisterRecord( sdRef, flags, interfaceIndex = kDNSServiceInterfaceIndexAny, fullname = _NO_DEFAULT, rrtype = _NO_DEFAULT, rrclass = kDNSServiceClass_IN, rdata = _NO_DEFAULT, ttl = 0, callBack = None, ): """ Register an individual resource record on a connected DNSServiceRef. Note that name conflicts occurring for records registered via this call must be handled by the client in the callback. sdRef: A DNSServiceRef returned by DNSServiceCreateConnection(). flags: Possible values are kDNSServiceFlagsShared or kDNSServiceFlagsUnique. interfaceIndex: If non-zero, specifies the interface on which to register the record. Passing kDNSServiceInterfaceIndexAny (0) causes the record to be registered on all interfaces. fullname: The full domain name of the resource record. rrtype: The numerical type of the resource record (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc.). rrclass: The class of the resource record (usually kDNSServiceClass_IN). rdata: A string containing the raw rdata, as it is to appear in the DNS record. ttl: The time to live of the resource record, in seconds. Pass 0 to use a default value. callBack: The function to be called when a result is found, or if the call asynchronously fails (e.g. because of a name conflict). Its signature should be callBack(sdRef, RecordRef, flags, errorCode). return value: A DNSRecordRef instance, which may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). (To deregister ALL records registered on a single connected DNSServiceRef and deallocate each of their corresponding DNSRecordRefs, close the DNSServiceRef.) Callback Parameters: sdRef: The connected DNSServiceRef returned by DNSServiceCreateConnection(). RecordRef: The DNSRecordRef returned by DNSServiceRegisterRecord(). flags: Currently unused, reserved for future use. errorCode: Will be kDNSServiceErr_NoError on success, otherwise will indicate the failure that occurred (including name conflicts). Other parameters are undefined if an error occurred. """ _NO_DEFAULT.check(fullname) _NO_DEFAULT.check(rrtype) _NO_DEFAULT.check(rdata) rdlen, rdata = _string_to_length_and_void_p(rdata) @_DNSServiceRegisterRecordReply def _callback(sdRef, RecordRef, flags, errorCode, context): if callBack is not None: callBack(sdRef, RecordRef, flags, errorCode) _global_lock.acquire() try: RecordRef = _DNSServiceRegisterRecord(sdRef, flags, interfaceIndex, fullname, rrtype, rrclass, rdlen, rdata, ttl, _callback, None) finally: _global_lock.release() sdRef._add_callback(_callback) sdRef._add_record_ref(RecordRef) return RecordRef def DNSServiceQueryRecord( flags = 0, interfaceIndex = kDNSServiceInterfaceIndexAny, fullname = _NO_DEFAULT, rrtype = _NO_DEFAULT, rrclass = kDNSServiceClass_IN, callBack = None, ): """ Query for an arbitrary DNS record. flags: Pass kDNSServiceFlagsLongLivedQuery to create a "long-lived" unicast query in a non-local domain. Without setting this flag, unicast queries will be one-shot; that is, only answers available at the time of the call will be returned. By setting this flag, answers (including Add and Remove events) that become available after the initial call is made will generate callbacks. This flag has no effect on link-local multicast queries. interfaceIndex: If non-zero, specifies the interface on which to issue the query. Passing kDNSServiceInterfaceIndexAny (0) causes the name to be queried for on all interfaces. fullname: The full domain name of the resource record to be queried for. rrtype: The numerical type of the resource record to be queried for (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc.). rrclass: The class of the resource record (usually kDNSServiceClass_IN). callBack: The function to be called when a result is found, or if the call asynchronously fails. Its signature should be callBack(sdRef, flags, interfaceIndex, errorCode, fullname, rrtype, rrclass, rdata, ttl). return value: A DNSServiceRef instance. The query operation will run indefinitely until the client terminates it by closing the DNSServiceRef. Callback Parameters: sdRef: The DNSServiceRef returned by DNSServiceQueryRecord(). flags: Possible values are kDNSServiceFlagsMoreComing and kDNSServiceFlagsAdd. The Add flag is NOT set for PTR records with a ttl of 0, i.e. "Remove" events. interfaceIndex: The interface on which the query was resolved. errorCode: Will be kDNSServiceErr_NoError on success, otherwise will indicate the failure that occurred. Other parameters are undefined if an error occurred. fullname: The resource record's full domain name. rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc.). rrclass: The class of the resource record (usually kDNSServiceClass_IN). rdata: A string containing the raw rdata of the resource record. ttl: The resource record's time to live, in seconds. """ _NO_DEFAULT.check(fullname) _NO_DEFAULT.check(rrtype) @_DNSServiceQueryRecordReply def _callback(sdRef, flags, interfaceIndex, errorCode, fullname, rrtype, rrclass, rdlen, rdata, ttl, context): if callBack is not None: rdata = _length_and_void_p_to_string(rdlen, rdata) callBack(sdRef, flags, interfaceIndex, errorCode, fullname.decode(), rrtype, rrclass, rdata, ttl) _global_lock.acquire() try: sdRef = _DNSServiceQueryRecord(flags, interfaceIndex, fullname, rrtype, rrclass, _callback, None) finally: _global_lock.release() sdRef._add_callback(_callback) return sdRef def DNSServiceReconfirmRecord( flags = 0, interfaceIndex = kDNSServiceInterfaceIndexAny, fullname = _NO_DEFAULT, rrtype = _NO_DEFAULT, rrclass = kDNSServiceClass_IN, rdata = _NO_DEFAULT, ): """ Instruct the daemon to verify the validity of a resource record that appears to be out of date (e.g. because tcp connection to a service's target failed). Causes the record to be flushed from the daemon's cache (as well as all other daemons' caches on the network) if the record is determined to be invalid. flags: Currently unused, reserved for future use. interfaceIndex: If non-zero, specifies the interface of the record in question. Passing kDNSServiceInterfaceIndexAny (0) causes all instances of this record to be reconfirmed. fullname: The resource record's full domain name. rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc.). rrclass: The class of the resource record (usually kDNSServiceClass_IN). rdata: A string containing the raw rdata of the resource record. """ _NO_DEFAULT.check(fullname) _NO_DEFAULT.check(rrtype) _NO_DEFAULT.check(rdata) rdlen, rdata = _string_to_length_and_void_p(rdata) _global_lock.acquire() try: _DNSServiceReconfirmRecord(flags, interfaceIndex, fullname, rrtype, rrclass, rdlen, rdata) finally: _global_lock.release() def DNSServiceConstructFullName( service = None, regtype = _NO_DEFAULT, domain = _NO_DEFAULT, ): """ Concatenate a three-part domain name (as returned by a callback function) into a properly-escaped full domain name. Note that callback functions already escape strings where necessary. service: The service name; any dots or backslashes must NOT be escaped. May be None (to construct a PTR record name, e.g. "_ftp._tcp.apple.com."). regtype: The service type followed by the protocol, separated by a dot (e.g. "_ftp._tcp"). domain: The domain name, e.g. "apple.com.". Literal dots or backslashes, if any, must be escaped, e.g. "1st\. Floor.apple.com." return value: The resulting full domain name. """ _NO_DEFAULT.check(regtype) _NO_DEFAULT.check(domain) _global_lock.acquire() try: fullName = _DNSServiceConstructFullName(service, regtype, domain) finally: _global_lock.release() return fullName.value.decode('utf-8') ################################################################################ # # TXTRecord class # ################################################################################ class TXTRecord(object): """ A mapping representing a DNS TXT record. The TXT record's name=value entries are stored as key/value pairs in the mapping. Although keys can be accessed in a case-insensitive fashion (meaning txt['foo'] and txt['FoO'] refer to the same value), key case is preserved in the wire representation of the record (so txt['FoO'] = 'bar' will generate a FoO=bar entry in the TXT record). Key order is also preserved, so keys appear in the wire format in the order in which they were created. Note that in addition to being valid as a txtRecord parameter to DNSServiceRegister(), a TXTRecord instance can be used in place of a resource record data string (i.e. rdata parameter) with any function that accepts one. """ def __init__(self, items={}, strict=True): """ Create a new TXTRecord instance, initializing it with the contents of items. If strict is true, then strict conformance to the DNS TXT record format will be enforced, and attempts to add a name containing invalid characters or a name/value pair whose wire representation is longer than 255 bytes will raise a ValueError exception. """ self.strict = strict self._names = [] self._items = {} for name, value in items.iteritems(): self[name] = value def __contains__(self, name): 'Return True if name is a key in the record, False otherwise' return (name.lower() in self._items) def __iter__(self): 'Return an iterator over name/value pairs' for name in self._names: yield self._items[name] def __len__(self): 'Return the number of name/value pairs' return len(self._names) def __nonzero__(self): 'Return False if the record is empty, True otherwise' return bool(self._items) def __str__(self): """ Return the wire representation of the TXT record as a string. If self.strict is false, any name/value pair whose wire length if greater than 255 bytes will be truncated to 255 bytes. If the record is empty, '\\0' is returned. """ if not self: return '\0' parts = [] for name, value in self: if value is None: item = name else: item = '%s=%s' % (name, value) if (not self.strict) and (len(item) > 255): item = item[:255] parts.append(chr(len(item))) parts.append(item) return ''.join(parts) def __getitem__(self, name): """ Return the value associated with name. The value is either None (meaning name has no associated value) or an str instance (which may be of length 0). Raises KeyError if name is not a key. """ return self._items[name.lower()][1] # Require one or more printable ASCII characters (0x20-0x7E), # excluding '=' (0x3D) _valid_name_re = re.compile(r'^[ -<>-~]+$') def __setitem__(self, name, value): """ Add a name/value pair to the record. If value is None, then name will have no associated value. If value is a unicode instance, it will be encoded as a UTF-8 string. Otherwise, value will be converted to an str instance. """ stored_name = name name = name.lower() length = len(name) if value is not None: if isinstance(value, unicode): value = value.encode('utf-8') else: value = str(value) length += 1 + len(value) if self.strict and (length > 255): raise ValueError('name=value string must be 255 bytes or less') if name not in self._items: if self.strict and (self._valid_name_re.match(stored_name) is None): raise ValueError("invalid name: '%s'" % stored_name) self._names.append(name) self._items[name] = (stored_name, value) def __delitem__(self, name): """ Remove name and its corresponding value from the record. Raises KeyError if name is not a key. """ name = name.lower() del self._items[name] self._names.remove(name) def get(self, name, default=None): return self._items.get(name.lower(), (name, default))[1] @classmethod def parse(cls, data, strict=False): """ Given a string data containing the wire representation of a DNS TXT record, parse it and return a TXTRecord instance. The strict parameter is passed to the TXTRecord constructor. """ txt = cls(strict=strict) while data: length = ord(data[0]) item = data[1:length+1].split('=', 1) # Add the item only if the name is non-empty and there are # no existing items with the same name if item[0] and (item[0] not in txt): if len(item) == 1: txt[item[0]] = None else: txt[item[0]] = item[1] data = data[length+1:] return txt ================================================ FILE: sipsimple/account/publication.py ================================================ """Implements the publisher handlers""" __all__ = ['Publisher', 'PresencePublisher', 'DialogPublisher'] import random from abc import ABCMeta, abstractproperty from threading import Lock from time import time from application.notification import IObserver, NotificationCenter, NotificationData from application.python import Null, limit from application.python.types import MarkerType from eventlib import coros, proc from twisted.internet import reactor from zope.interface import implements from sipsimple.core import FromHeader, Publication, PublicationETagError, RouteHeader, SIPURI, SIPCoreError from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.lookup import DNSLookup, DNSLookupError from sipsimple.payloads.dialoginfo import DialogInfoDocument from sipsimple.payloads.pidf import PIDFDocument from sipsimple.threading import run_in_twisted_thread from sipsimple.threading.green import Command, run_in_green_thread Command.register_defaults('publish', refresh_interval=None) class SameState: __metaclass__ = MarkerType class SIPPublicationDidFail(Exception): def __init__(self, data): self.data = data class SIPPublicationDidNotEnd(Exception): def __init__(self, data): self.data = data class PublicationError(Exception): def __init__(self, error, retry_after, refresh_interval=None): self.error = error self.retry_after = retry_after self.refresh_interval = refresh_interval class PublisherNickname(dict): def __missing__(self, name): return self.setdefault(name, name[:-9] if name.endswith('Publisher') else name) def __get__(self, obj, objtype): return self[objtype.__name__] def __set__(self, obj, value): raise AttributeError('cannot set attribute') def __delete__(self, obj): raise AttributeError('cannot delete attribute') class Publisher(object): __metaclass__ = ABCMeta __nickname__ = PublisherNickname() __transports__ = frozenset(['tls', 'tcp', 'udp']) implements(IObserver) def __init__(self, account): self.account = account self.started = False self.active = False self.publishing = False self._lock = Lock() self._command_proc = None self._command_channel = coros.queue() self._data_channel = coros.queue() self._publication = None self._dns_wait = 1 self._publish_wait = 1 self._publication_timer = None self.__dict__['state'] = None @abstractproperty def event(self): return None @abstractproperty def payload_type(self): return None @property def extra_headers(self): return [] @property def state(self): return self.__dict__['state'] @state.setter def state(self, state): if state is not None and not isinstance(state, self.payload_type.root_element): raise ValueError("state must be a %s document or None" % self.payload_type.root_element.__name__) with self._lock: old_state = self.__dict__['state'] self.__dict__['state'] = state if state == old_state: return self._publish(state) def start(self): if self.started: return self.started = True notification_center = NotificationCenter() notification_center.add_observer(self, sender=self) notification_center.post_notification(self.__class__.__name__ + 'WillStart', sender=self) notification_center.add_observer(self, name='CFGSettingsObjectDidChange', sender=self.account) notification_center.add_observer(self, name='CFGSettingsObjectDidChange', sender=SIPSimpleSettings()) notification_center.add_observer(self, name='NetworkConditionsDidChange') self._command_proc = proc.spawn(self._run) notification_center.post_notification(self.__class__.__name__ + 'DidStart', sender=self) notification_center.remove_observer(self, sender=self) def stop(self): if not self.started: return self.started = False self.active = False notification_center = NotificationCenter() notification_center.add_observer(self, sender=self) notification_center.post_notification(self.__class__.__name__ + 'WillEnd', sender=self) notification_center.remove_observer(self, name='CFGSettingsObjectDidChange', sender=self.account) notification_center.remove_observer(self, name='CFGSettingsObjectDidChange', sender=SIPSimpleSettings()) notification_center.remove_observer(self, name='NetworkConditionsDidChange') command = Command('terminate') self._command_channel.send(command) command.wait() self._command_proc = None notification_center.post_notification(self.__class__.__name__ + 'DidDeactivate', sender=self) notification_center.post_notification(self.__class__.__name__ + 'DidEnd', sender=self) notification_center.remove_observer(self, sender=self) def activate(self): if not self.started: raise RuntimeError("not started") self.active = True self._command_channel.send(Command('publish', state=self.state)) notification_center = NotificationCenter() notification_center.post_notification(self.__class__.__name__ + 'DidActivate', sender=self) def deactivate(self): if not self.started: raise RuntimeError("not started") self.active = False self._command_channel.send(Command('unpublish')) notification_center = NotificationCenter() notification_center.post_notification(self.__class__.__name__ + 'DidDeactivate', sender=self) @run_in_twisted_thread def _publish(self, state): if not self.active: return if state is None: self._command_channel.send(Command('unpublish')) else: self._command_channel.send(Command('publish', state=state)) def _run(self): while True: command = self._command_channel.wait() handler = getattr(self, '_CH_%s' % command.name) handler(command) def _CH_publish(self, command): if command.state is None or self._publication is None and command.state is SameState: command.signal() return notification_center = NotificationCenter() settings = SIPSimpleSettings() if self._publication_timer is not None and self._publication_timer.active(): self._publication_timer.cancel() self._publication_timer = None if self._publication is None: duration = command.refresh_interval or self.account.sip.publish_interval from_header = FromHeader(self.account.uri, self.account.display_name) self._publication = Publication(from_header, self.event, self.payload_type.content_type, credentials=self.account.credentials, duration=duration, extra_headers=self.extra_headers) notification_center.add_observer(self, sender=self._publication) notification_center.post_notification(self.__class__.__name__ + 'WillPublish', sender=self, data=NotificationData(state=command.state, duration=duration)) else: notification_center.post_notification(self.__class__.__name__ + 'WillRefresh', sender=self, data=NotificationData(state=command.state)) try: # Lookup routes valid_transports = self.__transports__.intersection(settings.sip.transport_list) if self.account.sip.outbound_proxy is not None and self.account.sip.outbound_proxy.transport in valid_transports: uri = SIPURI(host=self.account.sip.outbound_proxy.host, port=self.account.sip.outbound_proxy.port, parameters={'transport': self.account.sip.outbound_proxy.transport}) else: uri = SIPURI(host=self.account.id.domain) lookup = DNSLookup() try: routes = lookup.lookup_sip_proxy(uri, valid_transports).wait() except DNSLookupError, e: retry_after = random.uniform(self._dns_wait, 2*self._dns_wait) self._dns_wait = limit(2*self._dns_wait, max=30) raise PublicationError('DNS lookup failed: %s' % e, retry_after=retry_after) else: self._dns_wait = 1 body = None if command.state is SameState else command.state.toxml() # Publish by trying each route in turn publish_timeout = time() + 30 for route in routes: remaining_time = publish_timeout-time() if remaining_time > 0: try: try: self._publication.publish(body, RouteHeader(route.uri), timeout=limit(remaining_time, min=1, max=10)) except ValueError as e: # this happens for an initial PUBLISH with body=None raise PublicationError(str(e), retry_after=0) except PublicationETagError: state = self.state # access self.state only once to avoid race conditions if state is not None: self._publication.publish(state.toxml(), RouteHeader(route.uri), timeout=limit(remaining_time, min=1, max=10)) else: command.signal() return except SIPCoreError: raise PublicationError('Internal error', retry_after=5) try: while True: notification = self._data_channel.wait() if notification.name == 'SIPPublicationDidSucceed': break if notification.name == 'SIPPublicationDidEnd': raise PublicationError('Publication expired', retry_after=0) # publication expired while we were trying to re-publish except SIPPublicationDidFail, e: if e.data.code == 407: # Authentication failed, so retry the publication in some time raise PublicationError('Authentication failed', retry_after=random.uniform(60, 120)) elif e.data.code == 412: raise PublicationError('Conditional request failed', retry_after=0) elif e.data.code == 423: # Get the value of the Min-Expires header if e.data.min_expires is not None and e.data.min_expires > self.account.sip.publish_interval: refresh_interval = e.data.min_expires else: refresh_interval = None raise PublicationError('Interval too short', retry_after=random.uniform(60, 120), refresh_interval=refresh_interval) elif e.data.code in (405, 406, 489): raise PublicationError('Method or event not supported', retry_after=3600) else: # Otherwise just try the next route continue else: self.publishing = True self._publish_wait = 1 command.signal() break else: # There are no more routes to try, reschedule the publication retry_after = random.uniform(self._publish_wait, 2*self._publish_wait) self._publish_wait = limit(self._publish_wait*2, max=30) raise PublicationError('No more routes to try', retry_after=retry_after) except PublicationError, e: self.publishing = False notification_center.remove_observer(self, sender=self._publication) def publish(): if self.active: self._command_channel.send(Command('publish', event=command.event, state=self.state, refresh_interval=e.refresh_interval)) else: command.signal() self._publication_timer = None self._publication_timer = reactor.callLater(e.retry_after, publish) self._publication = None notification_center.post_notification(self.__nickname__ + 'PublicationDidFail', sender=self, data=NotificationData(reason=e.error)) else: notification_center.post_notification(self.__nickname__ + 'PublicationDidSucceed', sender=self) def _CH_unpublish(self, command): # Cancel any timer which would restart the publication process if self._publication_timer is not None and self._publication_timer.active(): self._publication_timer.cancel() self._publication_timer = None publishing = self.publishing self.publishing = False if self._publication is not None: notification_center = NotificationCenter() if publishing: self._publication.end(timeout=2) try: while True: notification = self._data_channel.wait() if notification.name == 'SIPPublicationDidEnd': break except (SIPPublicationDidFail, SIPPublicationDidNotEnd): notification_center.post_notification(self.__nickname__ + 'PublicationDidNotEnd', sender=self) else: notification_center.post_notification(self.__nickname__ + 'PublicationDidEnd', sender=self) notification_center.remove_observer(self, sender=self._publication) self._publication = None command.signal() def _CH_terminate(self, command): self._CH_unpublish(command) raise proc.ProcExit @run_in_twisted_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_SIPPublicationDidSucceed(self, notification): if notification.sender is self._publication: self._data_channel.send(notification) def _NH_SIPPublicationDidFail(self, notification): if notification.sender is self._publication: self._data_channel.send_exception(SIPPublicationDidFail(notification.data)) def _NH_SIPPublicationDidEnd(self, notification): if notification.sender is self._publication: self._data_channel.send(notification) def _NH_SIPPublicationDidNotEnd(self, notification): if notification.sender is self._publication: self._data_channel.send_exception(SIPPublicationDidNotEnd(notification.data)) def _NH_SIPPublicationWillExpire(self, notification): if notification.sender is self._publication: self._publish(SameState) @run_in_green_thread def _NH_CFGSettingsObjectDidChange(self, notification): if not self.started: return if 'enabled' in notification.data.modified: return # global account activation is handled separately by the account itself elif 'presence.enabled' in notification.data.modified: if self.account.presence.enabled: self.activate() else: self.deactivate() elif self.active and {'__id__', 'auth.password', 'auth.username', 'sip.outbound_proxy', 'sip.transport_list', 'sip.publish_interval'}.intersection(notification.data.modified): self._command_channel.send(Command('unpublish')) self._command_channel.send(Command('publish', state=self.state)) def _NH_NetworkConditionsDidChange(self, notification): if self.active: self._command_channel.send(Command('unpublish')) self._command_channel.send(Command('publish', state=self.state)) class PresencePublisher(Publisher): """A publisher for presence state""" @property def event(self): return 'presence' @property def payload_type(self): return PIDFDocument def _NH_PresencePublisherDidStart(self, notification): if self.account.presence.enabled: self.activate() class DialogPublisher(Publisher): """A publisher for dialog info state""" @property def event(self): return 'dialog' @property def payload_type(self): return DialogInfoDocument def _NH_DialogPublisherDidStart(self, notification): if self.account.presence.enabled: self.activate() ================================================ FILE: sipsimple/account/registration.py ================================================ """Implements the registration handler""" __all__ = ['Registrar'] import random from time import time from application.notification import IObserver, NotificationCenter, NotificationData from application.python import Null, limit from eventlib import coros, proc from twisted.internet import reactor from zope.interface import implements from sipsimple.core import ContactHeader, FromHeader, Header, Registration, RouteHeader, SIPURI, SIPCoreError, NoGRUU from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.lookup import DNSLookup, DNSLookupError from sipsimple.threading import run_in_twisted_thread from sipsimple.threading.green import Command, run_in_green_thread Command.register_defaults('register', refresh_interval=None) class SIPRegistrationDidFail(Exception): def __init__(self, data): self.data = data class SIPRegistrationDidNotEnd(Exception): def __init__(self, data): self.data = data class RegistrationError(Exception): def __init__(self, error, retry_after, refresh_interval=None): self.error = error self.retry_after = retry_after self.refresh_interval = refresh_interval class Registrar(object): implements(IObserver) def __init__(self, account): self.account = account self.started = False self.active = False self.registered = False self._command_proc = None self._command_channel = coros.queue() self._data_channel = coros.queue() self._registration = None self._dns_wait = 1 self._register_wait = 1 self._registration_timer = None def start(self): if self.started: return self.started = True notification_center = NotificationCenter() notification_center.add_observer(self, name='CFGSettingsObjectDidChange', sender=self.account) notification_center.add_observer(self, name='CFGSettingsObjectDidChange', sender=SIPSimpleSettings()) notification_center.add_observer(self, name='NetworkConditionsDidChange') self._command_proc = proc.spawn(self._run) if self.account.sip.register: self.activate() def stop(self): if not self.started: return self.started = False self.active = False notification_center = NotificationCenter() notification_center.remove_observer(self, name='CFGSettingsObjectDidChange', sender=self.account) notification_center.remove_observer(self, name='CFGSettingsObjectDidChange', sender=SIPSimpleSettings()) notification_center.remove_observer(self, name='NetworkConditionsDidChange') command = Command('terminate') self._command_channel.send(command) command.wait() self._command_proc = None def activate(self): if not self.started: raise RuntimeError("not started") self.active = True self._command_channel.send(Command('register')) def deactivate(self): if not self.started: raise RuntimeError("not started") self.active = False self._command_channel.send(Command('unregister')) def reregister(self): if self.active: self._command_channel.send(Command('unregister')) self._command_channel.send(Command('register')) def _run(self): while True: command = self._command_channel.wait() handler = getattr(self, '_CH_%s' % command.name) handler(command) def _CH_register(self, command): notification_center = NotificationCenter() settings = SIPSimpleSettings() if self._registration_timer is not None and self._registration_timer.active(): self._registration_timer.cancel() self._registration_timer = None # Initialize the registration if self._registration is None: duration = command.refresh_interval or self.account.sip.register_interval self._registration = Registration(FromHeader(self.account.uri, self.account.display_name), credentials=self.account.credentials, duration=duration, extra_headers=[Header('Supported', 'gruu')]) notification_center.add_observer(self, sender=self._registration) notification_center.post_notification('SIPAccountWillRegister', sender=self.account) else: notification_center.post_notification('SIPAccountRegistrationWillRefresh', sender=self.account) try: # Lookup routes if self.account.sip.outbound_proxy is not None and self.account.sip.outbound_proxy.transport in settings.sip.transport_list: uri = SIPURI(host=self.account.sip.outbound_proxy.host, port=self.account.sip.outbound_proxy.port, parameters={'transport': self.account.sip.outbound_proxy.transport}) else: uri = SIPURI(host=self.account.id.domain) lookup = DNSLookup() try: routes = lookup.lookup_sip_proxy(uri, settings.sip.transport_list).wait() except DNSLookupError, e: retry_after = random.uniform(self._dns_wait, 2*self._dns_wait) self._dns_wait = limit(2*self._dns_wait, max=30) raise RegistrationError('DNS lookup failed: %s' % e, retry_after=retry_after) else: self._dns_wait = 1 # Register by trying each route in turn register_timeout = time() + 30 for route in routes: remaining_time = register_timeout-time() if remaining_time > 0: try: contact_uri = self.account.contact[NoGRUU, route] except KeyError: continue contact_header = ContactHeader(contact_uri) contact_header.parameters['+sip.instance'] = '"<%s>"' % settings.instance_id if self.account.nat_traversal.use_ice: contact_header.parameters['+sip.ice'] = None route_header = RouteHeader(route.uri) try: self._registration.register(contact_header, route_header, timeout=limit(remaining_time, min=1, max=10)) except SIPCoreError: raise RegistrationError('Internal error', retry_after=5) try: while True: notification = self._data_channel.wait() if notification.name == 'SIPRegistrationDidSucceed': break if notification.name == 'SIPRegistrationDidEnd': raise RegistrationError('Registration expired', retry_after=0) # registration expired while we were trying to re-register except SIPRegistrationDidFail, e: notification_data = NotificationData(code=e.data.code, reason=e.data.reason, registration=self._registration, registrar=route) notification_center.post_notification('SIPAccountRegistrationGotAnswer', sender=self.account, data=notification_data) if e.data.code == 401: # Authentication failed, so retry the registration in some time raise RegistrationError('Authentication failed', retry_after=random.uniform(60, 120)) elif e.data.code == 423: # Get the value of the Min-Expires header if e.data.min_expires is not None and e.data.min_expires > self.account.sip.register_interval: refresh_interval = e.data.min_expires else: refresh_interval = None raise RegistrationError('Interval too short', retry_after=random.uniform(60, 120), refresh_interval=refresh_interval) else: # Otherwise just try the next route continue else: notification_data = NotificationData(code=notification.data.code, reason=notification.data.reason, registration=self._registration, registrar=route) notification_center.post_notification('SIPAccountRegistrationGotAnswer', sender=self.account, data=notification_data) self.registered = True # Save GRUU try: header = next(header for header in notification.data.contact_header_list if header.parameters.get('+sip.instance', '').strip('"<>') == settings.instance_id) except StopIteration: self.account.contact.public_gruu = None self.account.contact.temporary_gruu = None else: public_gruu = header.parameters.get('pub-gruu', None) temporary_gruu = header.parameters.get('temp-gruu', None) try: self.account.contact.public_gruu = SIPURI.parse(public_gruu.strip('"')) except (AttributeError, SIPCoreError): self.account.contact.public_gruu = None try: self.account.contact.temporary_gruu = SIPURI.parse(temporary_gruu.strip('"')) except (AttributeError, SIPCoreError): self.account.contact.temporary_gruu = None notification_data = NotificationData(contact_header=notification.data.contact_header, contact_header_list=notification.data.contact_header_list, expires=notification.data.expires_in, registrar=route) notification_center.post_notification('SIPAccountRegistrationDidSucceed', sender=self.account, data=notification_data) self._register_wait = 1 command.signal() break else: # There are no more routes to try, reschedule the registration retry_after = random.uniform(self._register_wait, 2*self._register_wait) self._register_wait = limit(self._register_wait*2, max=30) raise RegistrationError('No more routes to try', retry_after=retry_after) except RegistrationError, e: self.registered = False notification_center.remove_observer(self, sender=self._registration) notification_center.post_notification('SIPAccountRegistrationDidFail', sender=self.account, data=NotificationData(error=e.error, retry_after=e.retry_after)) def register(): if self.active: self._command_channel.send(Command('register', command.event, refresh_interval=e.refresh_interval)) self._registration_timer = None self._registration_timer = reactor.callLater(e.retry_after, register) self._registration = None self.account.contact.public_gruu = None self.account.contact.temporary_gruu = None def _CH_unregister(self, command): # Cancel any timer which would restart the registration process if self._registration_timer is not None and self._registration_timer.active(): self._registration_timer.cancel() self._registration_timer = None registered = self.registered self.registered = False if self._registration is not None: notification_center = NotificationCenter() if registered: self._registration.end(timeout=2) try: while True: notification = self._data_channel.wait() if notification.name == 'SIPRegistrationDidEnd': break except (SIPRegistrationDidFail, SIPRegistrationDidNotEnd), e: notification_center.post_notification('SIPAccountRegistrationDidNotEnd', sender=self.account, data=NotificationData(code=e.data.code, reason=e.data.reason, registration=self._registration)) else: notification_center.post_notification('SIPAccountRegistrationDidEnd', sender=self.account, data=NotificationData(registration=self._registration)) notification_center.remove_observer(self, sender=self._registration) self._registration = None self.account.contact.public_gruu = None self.account.contact.temporary_gruu = None command.signal() def _CH_terminate(self, command): self._CH_unregister(command) raise proc.ProcExit @run_in_twisted_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_SIPRegistrationDidSucceed(self, notification): if notification.sender is self._registration: self._data_channel.send(notification) def _NH_SIPRegistrationDidFail(self, notification): if notification.sender is self._registration: self._data_channel.send_exception(SIPRegistrationDidFail(notification.data)) def _NH_SIPRegistrationDidEnd(self, notification): if notification.sender is self._registration: self._data_channel.send(notification) def _NH_SIPRegistrationDidNotEnd(self, notification): if notification.sender is self._registration: self._data_channel.send_exception(SIPRegistrationDidNotEnd(notification.data)) def _NH_SIPRegistrationWillExpire(self, notification): if self.active: self._command_channel.send(Command('register')) @run_in_green_thread def _NH_CFGSettingsObjectDidChange(self, notification): if not self.started: return if 'enabled' in notification.data.modified: return # global account activation is handled separately by the account itself elif 'sip.register' in notification.data.modified: if self.account.sip.register: self.activate() else: self.deactivate() elif self.active and {'__id__', 'auth.password', 'auth.username', 'nat_traversal.use_ice', 'sip.outbound_proxy', 'sip.transport_list', 'sip.register_interval'}.intersection(notification.data.modified): self._command_channel.send(Command('unregister')) self._command_channel.send(Command('register')) def _NH_NetworkConditionsDidChange(self, notification): if self.active: self._command_channel.send(Command('unregister')) self._command_channel.send(Command('register')) ================================================ FILE: sipsimple/account/subscription.py ================================================ """Implements the subscription handlers""" __all__ = ['Subscriber', 'MWISubscriber', 'PresenceWinfoSubscriber', 'DialogWinfoSubscriber', 'PresenceSubscriber', 'SelfPresenceSubscriber', 'DialogSubscriber'] import random from abc import ABCMeta, abstractproperty from time import time from application.notification import IObserver, NotificationCenter, NotificationData from application.python import Null, limit from eventlib import coros, proc from twisted.internet import reactor from zope.interface import implements from sipsimple.core import ContactHeader, FromHeader, Header, RouteHeader, SIPURI, Subscription, ToHeader, SIPCoreError, NoGRUU from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.lookup import DNSLookup, DNSLookupError from sipsimple.threading import run_in_twisted_thread from sipsimple.threading.green import Command, run_in_green_thread Command.register_defaults('subscribe', refresh_interval=None) class SIPSubscriptionDidFail(Exception): def __init__(self, data): self.data = data class SubscriptionError(Exception): def __init__(self, error, retry_after, refresh_interval=None): self.error = error self.retry_after = retry_after self.refresh_interval = refresh_interval class InterruptSubscription(Exception): pass class TerminateSubscription(Exception): pass class Content(object): def __init__(self, body, type): self.body = body self.type = type class SubscriberNickname(dict): def __missing__(self, name): return self.setdefault(name, name[:-10] if name.endswith('Subscriber') else name) def __get__(self, obj, objtype): return self[objtype.__name__] def __set__(self, obj, value): raise AttributeError('cannot set attribute') def __delete__(self, obj): raise AttributeError('cannot delete attribute') class Subscriber(object): __metaclass__ = ABCMeta __nickname__ = SubscriberNickname() __transports__ = frozenset(['tls', 'tcp', 'udp']) implements(IObserver) def __init__(self, account): self.account = account self.started = False self.active = False self.subscribed = False self._command_proc = None self._command_channel = coros.queue() self._data_channel = coros.queue() self._subscription = None self._subscription_proc = None self._subscription_timer = None @abstractproperty def event(self): return None @property def subscription_uri(self): return self.account.id @property def content(self): return Content(None, None) @property def extra_headers(self): return [] def start(self): if self.started: return self.started = True notification_center = NotificationCenter() notification_center.add_observer(self, sender=self) notification_center.post_notification(self.__class__.__name__ + 'WillStart', sender=self) notification_center.add_observer(self, name='NetworkConditionsDidChange') self._command_proc = proc.spawn(self._run) notification_center.post_notification(self.__class__.__name__ + 'DidStart', sender=self) notification_center.remove_observer(self, sender=self) def stop(self): if not self.started: return self.started = False self.active = False notification_center = NotificationCenter() notification_center.add_observer(self, sender=self) notification_center.post_notification(self.__class__.__name__ + 'WillEnd', sender=self) notification_center.remove_observer(self, name='NetworkConditionsDidChange') command = Command('terminate') self._command_channel.send(command) command.wait() self._command_proc = None notification_center.post_notification(self.__class__.__name__ + 'DidDeactivate', sender=self) notification_center.post_notification(self.__class__.__name__ + 'DidEnd', sender=self) notification_center.remove_observer(self, sender=self) def activate(self): if not self.started: raise RuntimeError("not started") self.active = True self._command_channel.send(Command('subscribe')) notification_center = NotificationCenter() notification_center.post_notification(self.__class__.__name__ + 'DidActivate', sender=self) def deactivate(self): if not self.started: raise RuntimeError("not started") self.active = False self._command_channel.send(Command('unsubscribe')) notification_center = NotificationCenter() notification_center.post_notification(self.__class__.__name__ + 'DidDeactivate', sender=self) def resubscribe(self): if self.active: self._command_channel.send(Command('subscribe')) def _run(self): while True: command = self._command_channel.wait() handler = getattr(self, '_CH_%s' % command.name) handler(command) def _CH_subscribe(self, command): if self._subscription_timer is not None and self._subscription_timer.active(): self._subscription_timer.cancel() self._subscription_timer = None if self._subscription_proc is not None: subscription_proc = self._subscription_proc subscription_proc.kill(InterruptSubscription) subscription_proc.wait() self._subscription_proc = proc.spawn(self._subscription_handler, command) def _CH_unsubscribe(self, command): # Cancel any timer which would restart the subscription process if self._subscription_timer is not None and self._subscription_timer.active(): self._subscription_timer.cancel() self._subscription_timer = None if self._subscription_proc is not None: subscription_proc = self._subscription_proc subscription_proc.kill(TerminateSubscription) subscription_proc.wait() self._subscription_proc = None command.signal() def _CH_terminate(self, command): self._CH_unsubscribe(command) raise proc.ProcExit def _subscription_handler(self, command): notification_center = NotificationCenter() settings = SIPSimpleSettings() subscription_uri = self.subscription_uri refresh_interval = command.refresh_interval or self.account.sip.subscribe_interval valid_transports = self.__transports__.intersection(settings.sip.transport_list) try: # Lookup routes if self.account.sip.outbound_proxy is not None and self.account.sip.outbound_proxy.transport in valid_transports: uri = SIPURI(host=self.account.sip.outbound_proxy.host, port=self.account.sip.outbound_proxy.port, parameters={'transport': self.account.sip.outbound_proxy.transport}) elif self.account.sip.always_use_my_proxy: uri = SIPURI(host=self.account.id.domain) else: uri = SIPURI(host=subscription_uri.domain) lookup = DNSLookup() try: routes = lookup.lookup_sip_proxy(uri, valid_transports).wait() except DNSLookupError, e: raise SubscriptionError('DNS lookup failed: %s' % e, retry_after=random.uniform(15, 30)) subscription_uri = SIPURI(user=subscription_uri.username, host=subscription_uri.domain) content = self.content timeout = time() + 30 for route in routes: remaining_time = timeout - time() if remaining_time > 0: try: contact_uri = self.account.contact[NoGRUU, route] except KeyError: continue subscription = Subscription(subscription_uri, FromHeader(self.account.uri, self.account.display_name), ToHeader(subscription_uri), ContactHeader(contact_uri), self.event, RouteHeader(route.uri), credentials=self.account.credentials, refresh=refresh_interval) notification_center.add_observer(self, sender=subscription) try: subscription.subscribe(body=content.body, content_type=content.type, extra_headers=self.extra_headers, timeout=limit(remaining_time, min=1, max=5)) except SIPCoreError: notification_center.remove_observer(self, sender=subscription) raise SubscriptionError('Internal error', retry_after=5) self._subscription = subscription try: while True: notification = self._data_channel.wait() if notification.name == 'SIPSubscriptionDidStart': break except SIPSubscriptionDidFail, e: notification_center.remove_observer(self, sender=subscription) self._subscription = None if e.data.code == 407: # Authentication failed, so retry the subscription in some time raise SubscriptionError('Authentication failed', retry_after=random.uniform(60, 120)) elif e.data.code == 423: # Get the value of the Min-Expires header if e.data.min_expires is not None and e.data.min_expires > self.account.sip.subscribe_interval: refresh_interval = e.data.min_expires else: refresh_interval = None raise SubscriptionError('Interval too short', retry_after=random.uniform(60, 120), refresh_interval=refresh_interval) elif e.data.code in (405, 406, 489): raise SubscriptionError('Method or event not supported', retry_after=3600) elif e.data.code == 1400: raise SubscriptionError(e.data.reason, retry_after=3600) else: # Otherwise just try the next route continue else: self.subscribed = True command.signal() break else: # There are no more routes to try, reschedule the subscription raise SubscriptionError('No more routes to try', retry_after=random.uniform(60, 180)) # At this point it is subscribed. Handle notifications and ending/failures. notification_center.post_notification(self.__nickname__ + 'SubscriptionDidStart', sender=self) try: while True: notification = self._data_channel.wait() if notification.name == 'SIPSubscriptionGotNotify': notification_center.post_notification(self.__nickname__ + 'SubscriptionGotNotify', sender=self, data=notification.data) elif notification.name == 'SIPSubscriptionDidEnd': notification_center.post_notification(self.__nickname__ + 'SubscriptionDidEnd', sender=self, data=NotificationData(originator='remote')) if self.active: self._command_channel.send(Command('subscribe')) break except SIPSubscriptionDidFail: notification_center.post_notification(self.__nickname__ + 'SubscriptionDidFail', sender=self) if self.active: self._command_channel.send(Command('subscribe')) notification_center.remove_observer(self, sender=self._subscription) except InterruptSubscription, e: if not self.subscribed: command.signal(e) if self._subscription is not None: notification_center.remove_observer(self, sender=self._subscription) try: self._subscription.end(timeout=2) except SIPCoreError: pass finally: notification_center.post_notification(self.__nickname__ + 'SubscriptionDidEnd', sender=self, data=NotificationData(originator='local')) except TerminateSubscription, e: if not self.subscribed: command.signal(e) if self._subscription is not None: try: self._subscription.end(timeout=2) except SIPCoreError: pass else: try: while True: notification = self._data_channel.wait() if notification.name == 'SIPSubscriptionDidEnd': break except SIPSubscriptionDidFail: pass finally: notification_center.remove_observer(self, sender=self._subscription) notification_center.post_notification(self.__nickname__ + 'SubscriptionDidEnd', sender=self, data=NotificationData(originator='local')) except SubscriptionError, e: def subscribe(): if self.active: self._command_channel.send(Command('subscribe', command.event, refresh_interval=e.refresh_interval)) self._subscription_timer = None self._subscription_timer = reactor.callLater(e.retry_after, subscribe) notification_center.post_notification(self.__nickname__ + 'SubscriptionDidFail', sender=self) finally: self.subscribed = False self._subscription = None self._subscription_proc = None @run_in_twisted_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_SIPSubscriptionDidStart(self, notification): if notification.sender is self._subscription: self._data_channel.send(notification) def _NH_SIPSubscriptionDidEnd(self, notification): if notification.sender is self._subscription: self._data_channel.send(notification) def _NH_SIPSubscriptionDidFail(self, notification): if notification.sender is self._subscription: self._data_channel.send_exception(SIPSubscriptionDidFail(notification.data)) def _NH_SIPSubscriptionGotNotify(self, notification): if notification.sender is self._subscription: self._data_channel.send(notification) def _NH_NetworkConditionsDidChange(self, notification): if self.active: self._command_channel.send(Command('subscribe')) class MWISubscriber(Subscriber): """Message Waiting Indicator subscriber""" @property def event(self): return 'message-summary' @property def subscription_uri(self): return self.account.message_summary.voicemail_uri or self.account.id def _NH_MWISubscriberWillStart(self, notification): notification.center.add_observer(self, name='CFGSettingsObjectDidChange', sender=self.account) notification.center.add_observer(self, name='CFGSettingsObjectDidChange', sender=SIPSimpleSettings()) def _NH_MWISubscriberWillEnd(self, notification): notification.center.remove_observer(self, name='CFGSettingsObjectDidChange', sender=self.account) notification.center.remove_observer(self, name='CFGSettingsObjectDidChange', sender=SIPSimpleSettings()) def _NH_MWISubscriberDidStart(self, notification): if self.account.message_summary.enabled: self.activate() @run_in_green_thread def _NH_CFGSettingsObjectDidChange(self, notification): if not self.started: return if 'enabled' in notification.data.modified: return # global account activation is handled separately by the account itself elif 'message_summary.enabled' in notification.data.modified: if self.account.message_summary.enabled: self.activate() else: self.deactivate() elif self.active and {'__id__', 'auth.password', 'auth.username', 'message_summary.voicemail_uri', 'sip.always_use_my_proxy', 'sip.outbound_proxy', 'sip.subscribe_interval', 'sip.transport_list'}.intersection(notification.data.modified): self._command_channel.send(Command('subscribe')) class AbstractPresenceSubscriber(Subscriber): """Abstract class defining behavior for all presence subscribers""" __transports__ = frozenset(['tls', 'tcp']) def _NH_AbstractPresenceSubscriberWillStart(self, notification): notification.center.add_observer(self, name='SIPAccountDidDiscoverXCAPSupport', sender=self.account) notification.center.add_observer(self, name='CFGSettingsObjectDidChange', sender=self.account) notification.center.add_observer(self, name='CFGSettingsObjectDidChange', sender=SIPSimpleSettings()) def _NH_AbstractPresenceSubscriberWillEnd(self, notification): notification.center.remove_observer(self, name='SIPAccountDidDiscoverXCAPSupport', sender=self.account) notification.center.remove_observer(self, name='CFGSettingsObjectDidChange', sender=self.account) notification.center.remove_observer(self, name='CFGSettingsObjectDidChange', sender=SIPSimpleSettings()) def _NH_AbstractPresenceSubscriberDidStart(self, notification): if self.account.presence.enabled and self.account.xcap.discovered: self.activate() def _NH_SIPAccountDidDiscoverXCAPSupport(self, notification): if self.account.presence.enabled and not self.active: self.activate() @run_in_green_thread def _NH_CFGSettingsObjectDidChange(self, notification): if not self.started or not self.account.xcap.discovered: return if 'enabled' in notification.data.modified: return # global account activation is handled separately by the account itself elif 'presence.enabled' in notification.data.modified: if self.account.presence.enabled: self.activate() else: self.deactivate() elif self.active and {'__id__', 'auth.password', 'auth.username', 'sip.always_use_my_proxy', 'sip.outbound_proxy', 'sip.subscribe_interval', 'sip.transport_list'}.intersection(notification.data.modified): self._command_channel.send(Command('subscribe')) class PresenceWinfoSubscriber(AbstractPresenceSubscriber): """Presence Watcher Info subscriber""" _NH_PresenceWinfoSubscriberWillStart = AbstractPresenceSubscriber._NH_AbstractPresenceSubscriberWillStart _NH_PresenceWinfoSubscriberWillEnd = AbstractPresenceSubscriber._NH_AbstractPresenceSubscriberWillEnd _NH_PresenceWinfoSubscriberDidStart = AbstractPresenceSubscriber._NH_AbstractPresenceSubscriberDidStart @property def event(self): return 'presence.winfo' class DialogWinfoSubscriber(AbstractPresenceSubscriber): """Dialog Watcher Info subscriber""" _NH_DialogWinfoSubscriberWillStart = AbstractPresenceSubscriber._NH_AbstractPresenceSubscriberWillStart _NH_DialogWinfoSubscriberWillEnd = AbstractPresenceSubscriber._NH_AbstractPresenceSubscriberWillEnd _NH_DialogWinfoSubscriberDidStart = AbstractPresenceSubscriber._NH_AbstractPresenceSubscriberDidStart @property def event(self): return 'dialog.winfo' class PresenceSubscriber(AbstractPresenceSubscriber): """Presence subscriber""" _NH_PresenceSubscriberWillStart = AbstractPresenceSubscriber._NH_AbstractPresenceSubscriberWillStart _NH_PresenceSubscriberWillEnd = AbstractPresenceSubscriber._NH_AbstractPresenceSubscriberWillEnd _NH_PresenceSubscriberDidStart = AbstractPresenceSubscriber._NH_AbstractPresenceSubscriberDidStart @property def event(self): return 'presence' @property def subscription_uri(self): return self.account.xcap_manager.rls_presence_uri @property def extra_headers(self): return [Header('Supported', 'eventlist')] class SelfPresenceSubscriber(AbstractPresenceSubscriber): """Self presence subscriber""" _NH_SelfPresenceSubscriberWillStart = AbstractPresenceSubscriber._NH_AbstractPresenceSubscriberWillStart _NH_SelfPresenceSubscriberWillEnd = AbstractPresenceSubscriber._NH_AbstractPresenceSubscriberWillEnd _NH_SelfPresenceSubscriberDidStart = AbstractPresenceSubscriber._NH_AbstractPresenceSubscriberDidStart @property def event(self): return 'presence' @property def subscription_uri(self): return self.account.id class DialogSubscriber(AbstractPresenceSubscriber): """Dialog subscriber""" _NH_DialogSubscriberWillStart = AbstractPresenceSubscriber._NH_AbstractPresenceSubscriberWillStart _NH_DialogSubscriberWillEnd = AbstractPresenceSubscriber._NH_AbstractPresenceSubscriberWillEnd _NH_DialogSubscriberDidStart = AbstractPresenceSubscriber._NH_AbstractPresenceSubscriberDidStart @property def event(self): return 'dialog' @property def subscription_uri(self): return self.account.xcap_manager.rls_dialog_uri @property def extra_headers(self): return [Header('Supported', 'eventlist')] ================================================ FILE: sipsimple/account/xcap/__init__.py ================================================ """High-level management of XCAP documents based on OMA specifications""" __all__ = ['Group', 'Contact', 'ContactURI', 'EventHandling', 'Policy', 'Icon', 'OfflineStatus', 'XCAPManager', 'XCAPTransaction'] import base64 import cPickle import os import random import socket import weakref from cStringIO import StringIO from collections import OrderedDict from datetime import datetime from itertools import chain from operator import attrgetter from urllib2 import URLError from application.notification import IObserver, NotificationCenter, NotificationData from application.python import Null from application.python.decorator import execute_once from eventlib import api, coros, proc from eventlib.green.httplib import BadStatusLine from twisted.internet.error import ConnectionLost from xcaplib.green import XCAPClient from xcaplib.error import HTTPError from zope.interface import implements from sipsimple import log from sipsimple.account.subscription import Subscriber, Content from sipsimple.account.xcap.storage import IXCAPStorage, XCAPStorageError from sipsimple.configuration.datatypes import SIPAddress from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.lookup import DNSLookup, DNSLookupError from sipsimple.payloads import ParserError, IterateTypes, IterateIDs, IterateItems, All from sipsimple.payloads import addressbook, commonpolicy, dialogrules, omapolicy, pidf, prescontent, presrules, resourcelists, rlsservices, xcapcaps, xcapdiff from sipsimple.payloads import rpid; del rpid # needs to be imported to register its namespace from sipsimple.threading import run_in_twisted_thread from sipsimple.threading.green import Command, Worker, run_in_green_thread class XCAPError(Exception): pass class FetchRequiredError(XCAPError): pass class Document(object): name = None application = None payload_type = None default_namespace = None global_tree = None filename = None cached = True def __init__(self, manager): self.manager = weakref.proxy(manager) self.content = None self.etag = None self.fetch_time = datetime.fromtimestamp(0) self.update_time = datetime.fromtimestamp(0) self.dirty = False self.supported = False def __nonzero__(self): return self.content is not None @property def dirty(self): return self.__dict__['dirty'] or (self.content is not None and self.content.__dirty__) @dirty.setter def dirty(self, dirty): if self.content is not None and not dirty: self.content.__dirty__ = dirty self.__dict__['dirty'] = dirty @property def relative_url(self): return self.url[len(self.manager.xcap_root):].lstrip('/') @property def url(self): return self.manager.client.get_url(self.application, None, globaltree=self.global_tree, filename=self.filename) def load_from_cache(self): if not self.cached: return try: document = StringIO(self.manager.storage.load(self.name)) self.etag = document.readline().strip() or None self.content = self.payload_type.parse(document) self.__dict__['dirty'] = False except (XCAPStorageError, ParserError): self.etag = None self.content = None self.dirty = False self.fetch_time = datetime.utcnow() def initialize(self, server_caps): self.supported = self.application in server_caps.auids if not self.supported: self.reset() def reset(self): if self.cached and self.content is not None: try: self.manager.storage.delete(self.name) except XCAPStorageError: pass self.content = None self.etag = None self.dirty = False def fetch(self): try: document = self.manager.client.get(self.application, etagnot=self.etag, globaltree=self.global_tree, headers={'Accept': self.payload_type.content_type}, filename=self.filename) self.content = self.payload_type.parse(document) self.etag = document.etag self.__dict__['dirty'] = False except (BadStatusLine, ConnectionLost, URLError, socket.error), e: raise XCAPError("failed to fetch %s document: %s" % (self.name, e)) except HTTPError, e: if e.status == 404: # Not Found if self.content is not None: self.reset() self.fetch_time = datetime.utcnow() elif e.status != 304: # Other than Not Modified: raise XCAPError("failed to fetch %s document: %s" % (self.name, e)) except ParserError, e: raise XCAPError("failed to parse %s document: %s" % (self.name, e)) else: self.fetch_time = datetime.utcnow() if self.cached: try: self.manager.storage.save(self.name, self.etag + os.linesep + document) except XCAPStorageError: pass def update(self): if not self.dirty: return data = self.content.toxml() if self.content is not None else None try: kw = dict(etag=self.etag) if self.etag is not None else dict(etagnot='*') if data is not None: response = self.manager.client.put(self.application, data, globaltree=self.global_tree, filename=self.filename, headers={'Content-Type': self.payload_type.content_type}, **kw) else: response = self.manager.client.delete(self.application, data, globaltree=self.global_tree, filename=self.filename, **kw) except (BadStatusLine, ConnectionLost, URLError), e: raise XCAPError("failed to update %s document: %s" % (self.name, e)) except HTTPError, e: if e.status == 412: # Precondition Failed raise FetchRequiredError("document %s was modified externally" % self.name) elif e.status == 404 and data is None: # attempted to delete a document that did't exist in the first place pass else: raise XCAPError("failed to update %s document: %s" % (self.name, e)) self.etag = response.etag if data is not None else None self.dirty = False self.update_time = datetime.utcnow() if self.cached: try: if data is not None: self.manager.storage.save(self.name, self.etag + os.linesep + data) else: self.manager.storage.delete(self.name) except XCAPStorageError: pass class DialogRulesDocument(Document): name = 'dialog-rules' application = 'org.openxcap.dialog-rules' payload_type = dialogrules.DialogRulesDocument default_namespace = dialogrules.namespace global_tree = False filename = 'index' class PresRulesDocument(Document): name = 'pres-rules' application = 'org.openmobilealliance.pres-rules' payload_type = presrules.PresRulesDocument default_namespace = presrules.namespace global_tree = False filename = 'index' class ResourceListsDocument(Document): name = 'resource-lists' application = 'resource-lists' payload_type = resourcelists.ResourceListsDocument default_namespace = resourcelists.namespace global_tree = False filename = 'index' def update(self): if self.content is not None: sipsimple_addressbook = self.content['sipsimple_addressbook'] groups = ItemCollection(sipsimple_addressbook[addressbook.Group, IterateItems]) contacts = ItemCollection(sipsimple_addressbook[addressbook.Contact, IterateItems]) policies = ItemCollection(sipsimple_addressbook[addressbook.Policy, IterateItems]) for group, missing_id in ((group, missing_id) for group in groups for missing_id in [id for id in group.contacts if id not in contacts]): group.contacts.remove(missing_id) if any(item.__dirty__ for item in chain(contacts, policies)): oma_grantedcontacts = self.content['oma_grantedcontacts'] oma_blockedcontacts = self.content['oma_blockedcontacts'] dialog_grantedcontacts = self.content['dialog_grantedcontacts'] dialog_blockedcontacts = self.content['dialog_blockedcontacts'] sipsimple_presence_rls = self.content['sipsimple_presence_rls'] sipsimple_dialog_rls = self.content['sipsimple_dialog_rls'] all_contact_uris = set(uri.uri for contact in contacts for uri in contact.uris) contact_allow_presence_uris = set(uri.uri for contact in contacts for uri in contact.uris if contact.presence.policy=='allow') contact_block_presence_uris = set(uri.uri for contact in contacts for uri in contact.uris if contact.presence.policy=='block') contact_allow_dialog_uris = set(uri.uri for contact in contacts for uri in contact.uris if contact.dialog.policy=='allow') contact_block_dialog_uris = set(uri.uri for contact in contacts for uri in contact.uris if contact.dialog.policy=='block') contact_subscribe_presence_uris = set(uri.uri for contact in contacts for uri in contact.uris if contact.presence.subscribe==True) contact_subscribe_dialog_uris = set(uri.uri for contact in contacts for uri in contact.uris if contact.dialog.subscribe==True) policy_allow_presence_uris = set(policy.uri for policy in policies if policy.presence.policy=='allow') policy_block_presence_uris = set(policy.uri for policy in policies if policy.presence.policy=='block') policy_allow_dialog_uris = set(policy.uri for policy in policies if policy.dialog.policy=='allow') policy_block_dialog_uris = set(policy.uri for policy in policies if policy.dialog.policy=='block') policy_subscribe_presence_uris = set(policy.uri for policy in policies if policy.presence.subscribe==True) policy_subscribe_dialog_uris = set(policy.uri for policy in policies if policy.dialog.subscribe==True) allowed_presence_uris = contact_allow_presence_uris - contact_block_presence_uris | policy_allow_presence_uris - policy_block_presence_uris - all_contact_uris blocked_presence_uris = contact_block_presence_uris | policy_block_presence_uris - all_contact_uris allowed_dialog_uris = contact_allow_dialog_uris - contact_block_dialog_uris | policy_allow_dialog_uris - policy_block_dialog_uris - all_contact_uris blocked_dialog_uris = contact_block_dialog_uris | policy_block_dialog_uris - all_contact_uris subscribe_presence_uris = contact_subscribe_presence_uris | policy_subscribe_presence_uris - all_contact_uris subscribe_dialog_uris = contact_subscribe_dialog_uris | policy_subscribe_dialog_uris - all_contact_uris if allowed_presence_uris != set(entry.uri for entry in oma_grantedcontacts): oma_grantedcontacts.clear() oma_grantedcontacts.update(resourcelists.Entry(uri) for uri in allowed_presence_uris) if blocked_presence_uris != set(entry.uri for entry in oma_blockedcontacts): oma_blockedcontacts.clear() oma_blockedcontacts.update(resourcelists.Entry(uri) for uri in blocked_presence_uris) if allowed_dialog_uris != set(entry.uri for entry in dialog_grantedcontacts): dialog_grantedcontacts.clear() dialog_grantedcontacts.update(resourcelists.Entry(uri) for uri in allowed_dialog_uris) if blocked_dialog_uris != set(entry.uri for entry in dialog_blockedcontacts): dialog_blockedcontacts.clear() dialog_blockedcontacts.update(resourcelists.Entry(uri) for uri in blocked_dialog_uris) if subscribe_presence_uris != set(entry.uri for entry in sipsimple_presence_rls): sipsimple_presence_rls.clear() sipsimple_presence_rls.update(resourcelists.Entry(uri) for uri in subscribe_presence_uris) if subscribe_dialog_uris != set(entry.uri for entry in sipsimple_dialog_rls): sipsimple_dialog_rls.clear() sipsimple_dialog_rls.update(resourcelists.Entry(uri) for uri in subscribe_dialog_uris) super(ResourceListsDocument, self).update() class RLSServicesDocument(Document): name = 'rls-services' application = 'rls-services' payload_type = rlsservices.RLSServicesDocument default_namespace = rlsservices.namespace global_tree = False filename = 'index' class XCAPCapsDocument(Document): name = 'xcap-caps' application = 'xcap-caps' payload_type = xcapcaps.XCAPCapabilitiesDocument default_namespace = xcapcaps.namespace global_tree = True filename = 'index' cached = False def initialize(self): self.supported = True class StatusIconDocument(Document): name = 'status-icon' application = 'org.openmobilealliance.pres-content' payload_type = prescontent.PresenceContentDocument default_namespace = prescontent.namespace global_tree = False filename = 'oma_status-icon/index' class PIDFManipulationDocument(Document): name = 'pidf-manipulation' application = 'pidf-manipulation' payload_type = pidf.PIDFDocument default_namespace = pidf.pidf_namespace global_tree = False filename = 'index' class ItemCollection(object): def __init__(self, items): self.items = OrderedDict((item.id, item) for item in items) def __getitem__(self, key): return self.items[key] def __contains__(self, key): return key in self.items def __iter__(self): return self.items.itervalues() def __reversed__(self): return (self[id] for id in reversed(self.items)) def __len__(self): return len(self.items) def __eq__(self, other): if isinstance(other, ItemCollection): return self.items == other.items return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.items.values()) def ids(self): return self.items.keys() def iterids(self): return self.items.iterkeys() def get(self, key, default=None): return self.items.get(key, default) def add(self, item): self.items[item.id] = item def remove(self, item): del self.items[item.id] class ContactList(ItemCollection): pass class ContactURIList(ItemCollection): def __init__(self, items, default=None): super(ContactURIList, self).__init__(items) self.default = default def __eq__(self, other): if isinstance(other, ContactURIList): return self.items == other.items and self.default == other.default return NotImplemented def __repr__(self): return "%s(%r, default=%r)" % (self.__class__.__name__, self.items.values(), self.default) class Group(object): def __init__(self, id, name, contacts, **attributes): self.id = id self.name = name self.contacts = contacts self.attributes = attributes def __eq__(self, other): if isinstance(other, Group): return self is other or (self.id == other.id and self.name == other.name and self.contacts.ids() == other.contacts.ids() and self.attributes == other.attributes) return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal def __setattr__(self, name, value): if name == 'contacts' and not isinstance(value, ContactList): value = ContactList(value) object.__setattr__(self, name, value) class ContactURI(object): def __init__(self, id, uri, type, **attributes): self.id = id self.uri = uri self.type = type self.attributes = attributes def __eq__(self, other): if isinstance(other, ContactURI): return self is other or (self.id == other.id and self.uri == other.uri and self.type == other.type and self.attributes == other.attributes) return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal class EventHandling(object): def __init__(self, policy, subscribe): self.policy = policy self.subscribe = subscribe def __eq__(self, other): if isinstance(other, EventHandling): return self is other or (self.policy == other.policy and self.subscribe == other.subscribe) return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.policy, self.subscribe) class Contact(object): def __init__(self, id, name, uris, presence_handling=None, dialog_handling=None, **attributes): self.id = id self.name = name self.uris = uris self.dialog = dialog_handling or EventHandling(policy='default', subscribe=False) self.presence = presence_handling or EventHandling(policy='default', subscribe=False) self.attributes = attributes def __eq__(self, other): if isinstance(other, Contact): return self is other or (self.id == other.id and self.name == other.name and self.uris == other.uris and self.dialog == other.dialog and self.presence == other.presence and self.attributes == other.attributes) return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal def __setattr__(self, name, value): if name == 'uris' and not isinstance(value, ContactURIList): value = ContactURIList(value) object.__setattr__(self, name, value) class Policy(object): def __init__(self, id, uri, name, presence_handling=None, dialog_handling=None, **attributes): self.id = id self.uri = uri self.name = name self.dialog = dialog_handling or EventHandling(policy='default', subscribe=False) self.presence = presence_handling or EventHandling(policy='default', subscribe=False) self.attributes = attributes def __eq__(self, other): if isinstance(other, Policy): return self is other or (self.id == other.id and self.uri == other.uri and self.name == other.name and self.dialog == other.dialog and self.presence == other.presence and self.attributes == other.attributes) return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal class Addressbook(object): def __init__(self, contacts, groups, policies): self.contacts = contacts self.groups = groups self.policies = policies def __eq__(self, other): if isinstance(other, Addressbook): return self is other or (self.contacts == other.contacts and self.groups == other.groups and self.policies == other.policies) return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal @classmethod def from_payload(cls, payload): def payload_to_contact(payload): uris = ContactURIList((ContactURI(uri.id, uri.uri, uri.type, **(uri.attributes or {})) for uri in payload.uris), default=payload.uris.default) presence_handling = EventHandling(payload.presence.policy.value, payload.presence.subscribe.value) dialog_handling = EventHandling(payload.dialog.policy.value, payload.dialog.subscribe.value) return Contact(payload.id, payload.name.value, uris, presence_handling, dialog_handling, **(payload.attributes or {})) def payload_to_group(payload): return Group(payload.id, payload.name.value, [contacts[contact_id] for contact_id in payload.contacts], **(payload.attributes or {})) def payload_to_policy(payload): presence_handling = EventHandling(payload.presence.policy.value, payload.presence.subscribe.value) dialog_handling = EventHandling(payload.dialog.policy.value, payload.dialog.subscribe.value) return Policy(payload.id, payload.uri, payload.name.value, presence_handling, dialog_handling, **(payload.attributes or {})) contacts = ItemCollection(payload_to_contact(item) for item in payload[addressbook.Contact, IterateItems]) groups = ItemCollection(payload_to_group(item) for item in payload[addressbook.Group, IterateItems]) policies = ItemCollection(payload_to_policy(item) for item in payload[addressbook.Policy, IterateItems]) return cls(contacts, groups, policies) class PresenceRules(object): def __init__(self, default_policy): self.default_policy = default_policy def __eq__(self, other): if isinstance(other, PresenceRules): return self is other or (self.default_policy == other.default_policy) return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal @classmethod def from_payload(cls, default_rule): default_policy = next(item for item in default_rule.actions if isinstance(item, presrules.SubHandling)).value return cls(default_policy) class DialogRules(object): def __init__(self, default_policy): self.default_policy = default_policy def __eq__(self, other): if isinstance(other, DialogRules): return self is other or (self.default_policy == other.default_policy) return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal @classmethod def from_payload(cls, default_rule): if default_rule is not None: default_policy = next(item for item in default_rule.actions if isinstance(item, dialogrules.SubHandling)).value else: default_policy = None return cls(default_policy) class Icon(object): __mimetypes__ = ('image/jpeg', 'image/png', 'image/gif') def __init__(self, data, mime_type, description=None): self.data = data self.mime_type = mime_type self.description = description self.url = None self.etag = None def __eq__(self, other): if isinstance(other, Icon): return self is other or (self.data == other.data and self.mime_type == other.mime_type and self.description == other.description) return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal def __setattr__(self, name, value): if name == 'mime_type' and value not in self.__mimetypes__: raise ValueError("invalid mime type: '%s'. Should be one of: %s" % (value, ', '.join(self.__mimetypes__))) object.__setattr__(self, name, value) @classmethod def from_payload(cls, payload): try: data = base64.decodestring(payload.data.value) except Exception: return None else: description = payload.description.value if payload.description else None return cls(data, payload.mime_type.value, description) class OfflineStatus(object): __slots__ = ('pidf',) def __init__(self, pidf): self.pidf = pidf def __setattr__(self, name, value): if name == 'pidf' and not isinstance(value, pidf.PIDF): raise ValueError("pidf must be a PIDF payload") object.__setattr__(self, name, value) def __eq__(self, other): if isinstance(other, OfflineStatus): return self is other or (self.pidf == other.pidf) return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal def __getstate__(self): return {'pidf': self.pidf.toxml()} def __setstate__(self, state): self.pidf = pidf.PIDFDocument.parse(state['pidf']) class Operation(object): __params__ = () def __init__(self, **params): for name, value in params.iteritems(): setattr(self, name, value) for param in set(self.__params__).difference(params): raise ValueError("missing operation parameter: '%s'" % param) self.applied = False self.timestamp = datetime.utcnow() class NormalizeOperation(Operation): __params__ = () class AddContactOperation(Operation): __params__ = ('contact',) class UpdateContactOperation(Operation): __params__ = ('contact', 'attributes') class RemoveContactOperation(Operation): __params__ = ('contact',) class AddContactURIOperation(Operation): __params__ = ('contact', 'uri') class UpdateContactURIOperation(Operation): __params__ = ('contact', 'uri', 'attributes') class RemoveContactURIOperation(Operation): __params__ = ('contact', 'uri') class AddGroupOperation(Operation): __params__ = ('group',) class UpdateGroupOperation(Operation): __params__ = ('group', 'attributes') class RemoveGroupOperation(Operation): __params__ = ('group',) class AddGroupMemberOperation(Operation): __params__ = ('group', 'contact') class RemoveGroupMemberOperation(Operation): __params__ = ('group', 'contact') class AddPolicyOperation(Operation): __params__ = ('policy',) class UpdatePolicyOperation(Operation): __params__ = ('policy', 'attributes') class RemovePolicyOperation(Operation): __params__ = ('policy',) class SetDefaultPresencePolicyOperation(Operation): __params__ = ('policy',) class SetDefaultDialogPolicyOperation(Operation): __params__ = ('policy',) class SetStatusIconOperation(Operation): __params__ = ('icon',) class SetOfflineStatusOperation(Operation): __params__ = ('status',) class XCAPSubscriber(Subscriber): __transports__ = frozenset(['tls', 'tcp']) @property def event(self): return 'xcap-diff' @property def content(self): rlist = resourcelists.List() for document in (doc for doc in self.account.xcap_manager.documents if doc.supported): rlist.add(resourcelists.Entry(document.relative_url)) return Content(resourcelists.ResourceLists([rlist]).toxml(), resourcelists.ResourceListsDocument.content_type) class XCAPManager(object): implements(IObserver) def __init__(self, account): from sipsimple.application import SIPApplication if SIPApplication.storage is None: raise RuntimeError("SIPApplication.storage must be defined before instantiating XCAPManager") storage = SIPApplication.storage.xcap_storage_factory(account.id) if not IXCAPStorage.providedBy(storage): raise TypeError("storage must implement the IXCAPStorage interface") self.account = account self.storage = storage self.storage_factory = SIPApplication.storage.xcap_storage_factory self.client = None self.command_proc = None self.command_channel = coros.queue() self.last_fetch_time = datetime.fromtimestamp(0) self.last_update_time = datetime.fromtimestamp(0) self.not_executed_fetch = None self.state = 'stopped' self.timer = None self.transaction_level = 0 self.xcap_subscriber = None self.server_caps = XCAPCapsDocument(self) self.dialog_rules = DialogRulesDocument(self) self.pidf_manipulation = PIDFManipulationDocument(self) self.pres_rules = PresRulesDocument(self) self.resource_lists = ResourceListsDocument(self) self.rls_services = RLSServicesDocument(self) self.status_icon = StatusIconDocument(self) for document in self.documents: document.load_from_cache() try: journal = self.storage.load('journal') except XCAPStorageError: self.journal = [] else: try: self.journal = cPickle.loads(journal) except Exception: self.journal = [] for operation in self.journal: operation.applied = False notification_center = NotificationCenter() notification_center.add_observer(self, sender=account, name='CFGSettingsObjectDidChange') notification_center.add_observer(self, sender=account, name='CFGSettingsObjectWasDeleted') @property def state(self): return self.__dict__['state'] @state.setter def state(self, value): old_value = self.__dict__.get('state', Null) self.__dict__['state'] = value if old_value != value and old_value is not Null: notification_center = NotificationCenter() notification_center.post_notification('XCAPManagerDidChangeState', sender=self, data=NotificationData(prev_state=old_value, state=value)) @property def documents(self): return [self.resource_lists, self.rls_services, self.pres_rules, self.dialog_rules, self.pidf_manipulation, self.status_icon] @property def document_names(self): return [document.name for document in self.documents] @property def xcap_root(self): return getattr(self.client, 'root', None) @property def rls_presence_uri(self): return SIPAddress('%s+presence@%s' % (self.account.id.username, self.account.id.domain)) @property def rls_dialog_uri(self): return SIPAddress('%s+dialog@%s' % (self.account.id.username, self.account.id.domain)) @execute_once def init(self): """ Initializes the XCAP manager before it can be started. Needs to be called before any other method and in a green thread. """ self.command_proc = proc.spawn(self._run) def start(self): """ Starts the XCAP manager. This method needs to be called in a green thread. """ command = Command('start') self.command_channel.send(command) command.wait() def stop(self): """ Stops the XCAP manager. This method blocks until all the operations are stopped and needs to be called in a green thread. """ command = Command('stop') self.command_channel.send(command) command.wait() def transaction(self): return XCAPTransaction(self) @run_in_twisted_thread def start_transaction(self): self.transaction_level += 1 @run_in_twisted_thread def commit_transaction(self): if self.transaction_level == 0: return self.transaction_level -= 1 if self.transaction_level == 0 and self.journal: self._save_journal() self.command_channel.send(Command('update')) def add_contact(self, contact): self._schedule_operation(AddContactOperation(contact=contact)) def update_contact(self, contact, attributes): self._schedule_operation(UpdateContactOperation(contact=contact, attributes=attributes)) def remove_contact(self, contact): self._schedule_operation(RemoveContactOperation(contact=contact)) def add_contact_uri(self, contact, uri): self._schedule_operation(AddContactURIOperation(contact=contact, uri=uri)) def update_contact_uri(self, contact, uri, attributes): self._schedule_operation(UpdateContactURIOperation(contact=contact, uri=uri, attributes=attributes)) def remove_contact_uri(self, contact, uri): self._schedule_operation(RemoveContactURIOperation(contact=contact, uri=uri)) def add_group(self, group): self._schedule_operation(AddGroupOperation(group=group)) def update_group(self, group, attributes): self._schedule_operation(UpdateGroupOperation(group=group, attributes=attributes)) def remove_group(self, group): self._schedule_operation(RemoveGroupOperation(group=group)) def add_group_member(self, group, contact): self._schedule_operation(AddGroupMemberOperation(group=group, contact=contact)) def remove_group_member(self, group, contact): self._schedule_operation(RemoveGroupMemberOperation(group=group, contact=contact)) def add_policy(self, policy): self._schedule_operation(AddPolicyOperation(policy=policy)) def update_policy(self, policy, attributes): self._schedule_operation(UpdatePolicyOperation(policy=policy, attributes=attributes)) def remove_policy(self, policy): self._schedule_operation(RemovePolicyOperation(policy=policy)) def set_default_presence_policy(self, policy): self._schedule_operation(SetDefaultPresencePolicyOperation(policy=presrules.SubHandlingValue(policy))) def set_default_dialog_policy(self, policy): self._schedule_operation(SetDefaultDialogPolicyOperation(policy=dialogrules.SubHandlingValue(policy))) def set_status_icon(self, icon): self._schedule_operation(SetStatusIconOperation(icon=icon)) def set_offline_status(self, status): self._schedule_operation(SetOfflineStatusOperation(status=status)) @run_in_twisted_thread def _schedule_operation(self, operation): self.journal.append(operation) if self.transaction_level == 0: self._save_journal() self.command_channel.send(Command('update')) def _run(self): while True: command = self.command_channel.wait() try: handler = getattr(self, '_CH_%s' % command.name) handler(command) except: self.command_proc = None raise # Command handlers # def _CH_start(self, command): if self.state != 'stopped': command.signal() return self.state = 'initializing' self.xcap_subscriber = XCAPSubscriber(self.account) notification_center = NotificationCenter() notification_center.post_notification('XCAPManagerWillStart', sender=self) notification_center.add_observer(self, sender=self.xcap_subscriber) notification_center.add_observer(self, sender=SIPSimpleSettings(), name='CFGSettingsObjectDidChange') self.xcap_subscriber.start() self.command_channel.send(Command('initialize')) notification_center.post_notification('XCAPManagerDidStart', sender=self) command.signal() def _CH_stop(self, command): if self.state in ('stopped', 'terminated'): command.signal() return notification_center = NotificationCenter() notification_center.post_notification('XCAPManagerWillEnd', sender=self) notification_center.remove_observer(self, sender=self.xcap_subscriber) notification_center.remove_observer(self, sender=SIPSimpleSettings(), name='CFGSettingsObjectDidChange') if self.timer is not None and self.timer.active(): self.timer.cancel() self.timer = None self.xcap_subscriber.stop() self.xcap_subscriber = None self.client = None self.state = 'stopped' self._save_journal() notification_center.post_notification('XCAPManagerDidEnd', sender=self) command.signal() def _CH_cleanup(self, command): if self.state != 'stopped': command.signal() return try: self.storage.purge() except XCAPStorageError: pass self.journal = [] self.state = 'terminated' command.signal() raise proc.ProcExit def _CH_initialize(self, command): self.state = 'initializing' if self.timer is not None and self.timer.active(): self.timer.cancel() self.timer = None if self.account.xcap.xcap_root: self.client = XCAPClient(self.account.xcap.xcap_root, self.account.id, password=self.account.auth.password) else: try: lookup = DNSLookup() xcap_root = random.choice(lookup.lookup_xcap_server(self.account.uri).wait()) except DNSLookupError: self.timer = self._schedule_command(60, Command('initialize', command.event)) return else: self.client = XCAPClient(xcap_root, self.account.id, password=self.account.auth.password) try: self.server_caps.fetch() except XCAPError: self.timer = self._schedule_command(60, Command('initialize', command.event)) return else: if self.server_caps.content is None: # XCAP server must always return some content for xcap-caps self.timer = self._schedule_command(60, Command('initialize', command.event)) return if not set(self.server_caps.content.auids).issuperset(('resource-lists', 'rls-services', 'org.openmobilealliance.pres-rules')): # Server must support at least resource-lists, rls-services and org.openmobilealliance.pres-rules self.timer = self._schedule_command(3600, Command('initialize', command.event)) return self.server_caps.initialize() for document in self.documents: document.initialize(self.server_caps.content) notification_center = NotificationCenter() notification_center.post_notification('XCAPManagerDidDiscoverServerCapabilities', sender=self, data=NotificationData(auids=self.server_caps.content.auids)) self.state = 'fetching' self.command_channel.send(Command('fetch', documents=set(self.document_names))) self.xcap_subscriber.activate() def _CH_reload(self, command): if self.state == 'terminated': command.signal() return if '__id__' in command.modified: try: self.storage.purge() except XCAPStorageError: pass self.storage = self.storage_factory(self.account.id) self.journal = [] self._save_journal() if {'__id__', 'xcap.xcap_root'}.intersection(command.modified): for document in self.documents: document.reset() if self.state == 'stopped': command.signal() return if {'__id__', 'auth.username', 'auth.password', 'xcap.xcap_root'}.intersection(command.modified): self.state = 'initializing' self.command_channel.send(Command('initialize')) else: self.xcap_subscriber.resubscribe() command.signal() def _CH_fetch(self, command): if self.state not in ('insync', 'fetching'): if self.not_executed_fetch is not None: command.documents.update(self.not_executed_fetch.documents) self.not_executed_fetch = command return if self.not_executed_fetch is not None: command.documents.update(self.not_executed_fetch.documents) self.not_executed_fetch = None self.state = 'fetching' if self.timer is not None and self.timer.active(): command.documents.update(self.timer.command.documents) self.timer.cancel() self.timer = None try: self._fetch_documents(command.documents) except XCAPError: self.timer = self._schedule_command(60, Command('fetch', command.event, documents=command.documents)) return if not self.journal and self.last_fetch_time > datetime.fromtimestamp(0) and all(doc.fetch_time < command.timestamp for doc in self.documents): self.last_fetch_time = datetime.utcnow() self.state = 'insync' return else: self.last_fetch_time = datetime.utcnow() self.state = 'updating' if not self.journal or type(self.journal[0]) is not NormalizeOperation: self.journal.insert(0, NormalizeOperation()) self.command_channel.send(Command('update', command.event)) def _CH_update(self, command): if self.state not in ('insync', 'updating'): return if self.transaction_level != 0: return self.state = 'updating' if self.timer is not None and self.timer.active(): self.timer.cancel() self.timer = None journal = self.journal[:] for operation in (operation for operation in journal if not operation.applied): handler = getattr(self, '_OH_%s' % operation.__class__.__name__) try: handler(operation) except Exception: # Error while applying operation, needs to be logged -Luci log.exception() operation.applied = True api.sleep(0) # Operations are quite CPU intensive try: for document in (doc for doc in self.documents if doc.dirty and doc.supported): document.update() except FetchRequiredError: for document in (doc for doc in self.documents if doc.dirty and doc.supported): document.reset() for operation in journal: operation.applied = False self.state = 'fetching' self.command_channel.send(Command('fetch', documents=set(self.document_names))) # Try to fetch them all just in case except XCAPError: self.timer = self._schedule_command(60, Command('update')) else: del self.journal[:len(journal)] if not self.journal: self.state = 'insync' if any(max(doc.update_time, doc.fetch_time) > self.last_update_time for doc in self.documents): self._load_data() self.last_update_time = datetime.utcnow() command.signal() if self.not_executed_fetch is not None: self.command_channel.send(self.not_executed_fetch) self.not_executed_fetch = None self._save_journal() # Operation handlers # def _OH_NormalizeOperation(self, operation): # Normalize resource-lists # if self.resource_lists.content is None: self.resource_lists.content = resourcelists.ResourceLists() resource_lists = self.resource_lists.content try: oma_buddylist = resource_lists['oma_buddylist'] except KeyError: oma_buddylist = resourcelists.List(name='oma_buddylist') resource_lists.add(oma_buddylist) try: oma_grantedcontacts = resource_lists['oma_grantedcontacts'] except KeyError: oma_grantedcontacts = resourcelists.List(name='oma_grantedcontacts') resource_lists.add(oma_grantedcontacts) try: oma_blockedcontacts = resource_lists['oma_blockedcontacts'] except KeyError: oma_blockedcontacts = resourcelists.List(name='oma_blockedcontacts') resource_lists.add(oma_blockedcontacts) try: oma_allcontacts = resource_lists['oma_allcontacts'] except KeyError: oma_allcontacts = resourcelists.List(name='oma_allcontacts') oma_allcontacts.add(resourcelists.External(self.resource_lists.url + '/~~' + resource_lists.get_xpath(oma_buddylist))) oma_allcontacts.add(resourcelists.External(self.resource_lists.url + '/~~' + resource_lists.get_xpath(oma_grantedcontacts))) oma_allcontacts.add(resourcelists.External(self.resource_lists.url + '/~~' + resource_lists.get_xpath(oma_blockedcontacts))) resource_lists.add(oma_allcontacts) try: dialog_grantedcontacts = resource_lists['dialog_grantedcontacts'] except KeyError: dialog_grantedcontacts = resourcelists.List(name='dialog_grantedcontacts') resource_lists.add(dialog_grantedcontacts) try: dialog_blockedcontacts = resource_lists['dialog_blockedcontacts'] except KeyError: dialog_blockedcontacts = resourcelists.List(name='dialog_blockedcontacts') resource_lists.add(dialog_blockedcontacts) try: sipsimple_presence_rls = resource_lists['sipsimple_presence_rls'] except KeyError: sipsimple_presence_rls = resourcelists.List(name='sipsimple_presence_rls') resource_lists.add(sipsimple_presence_rls) try: sipsimple_dialog_rls = resource_lists['sipsimple_dialog_rls'] except KeyError: sipsimple_dialog_rls = resourcelists.List(name='sipsimple_dialog_rls') resource_lists.add(sipsimple_dialog_rls) try: sipsimple_addressbook = resource_lists['sipsimple_addressbook'] except KeyError: sipsimple_addressbook = resourcelists.List(name='sipsimple_addressbook') resource_lists.add(sipsimple_addressbook) for cls in (cls for cls in sipsimple_addressbook[IterateTypes] if cls not in (addressbook.Contact, addressbook.Group, addressbook.Policy)): del sipsimple_addressbook[cls, All] for cls in (cls for cls in oma_grantedcontacts[IterateTypes] if cls is not resourcelists.Entry): del oma_grantedcontacts[cls, All] for cls in (cls for cls in oma_blockedcontacts[IterateTypes] if cls is not resourcelists.Entry): del oma_blockedcontacts[cls, All] for cls in (cls for cls in dialog_grantedcontacts[IterateTypes] if cls is not resourcelists.Entry): del dialog_grantedcontacts[cls, All] for cls in (cls for cls in dialog_blockedcontacts[IterateTypes] if cls is not resourcelists.Entry): del dialog_blockedcontacts[cls, All] for cls in (cls for cls in sipsimple_presence_rls[IterateTypes] if cls is not resourcelists.Entry): del sipsimple_presence_rls[cls, All] for cls in (cls for cls in sipsimple_dialog_rls[IterateTypes] if cls is not resourcelists.Entry): del sipsimple_dialog_rls[cls, All] groups = ItemCollection(sipsimple_addressbook[addressbook.Group, IterateItems]) contacts = ItemCollection(sipsimple_addressbook[addressbook.Contact, IterateItems]) policies = ItemCollection(sipsimple_addressbook[addressbook.Policy, IterateItems]) for group, missing_id in [(group, missing_id) for group in groups for missing_id in (id for id in group.contacts if id not in contacts)]: group.contacts.remove(missing_id) all_contact_uris = set(uri.uri for contact in contacts for uri in contact.uris) contact_allow_presence_uris = set(uri.uri for contact in contacts for uri in contact.uris if contact.presence.policy=='allow') contact_block_presence_uris = set(uri.uri for contact in contacts for uri in contact.uris if contact.presence.policy=='block') contact_allow_dialog_uris = set(uri.uri for contact in contacts for uri in contact.uris if contact.dialog.policy=='allow') contact_block_dialog_uris = set(uri.uri for contact in contacts for uri in contact.uris if contact.dialog.policy=='block') contact_subscribe_presence_uris = set(uri.uri for contact in contacts for uri in contact.uris if contact.presence.subscribe==True) contact_subscribe_dialog_uris = set(uri.uri for contact in contacts for uri in contact.uris if contact.dialog.subscribe==True) policy_allow_presence_uris = set(policy.uri for policy in policies if policy.presence.policy=='allow') policy_block_presence_uris = set(policy.uri for policy in policies if policy.presence.policy=='block') policy_allow_dialog_uris = set(policy.uri for policy in policies if policy.dialog.policy=='allow') policy_block_dialog_uris = set(policy.uri for policy in policies if policy.dialog.policy=='block') policy_subscribe_presence_uris = set(policy.uri for policy in policies if policy.presence.subscribe==True) policy_subscribe_dialog_uris = set(policy.uri for policy in policies if policy.dialog.subscribe==True) allowed_presence_uris = contact_allow_presence_uris - contact_block_presence_uris | policy_allow_presence_uris - policy_block_presence_uris - all_contact_uris blocked_presence_uris = contact_block_presence_uris | policy_block_presence_uris - all_contact_uris allowed_dialog_uris = contact_allow_dialog_uris - contact_block_dialog_uris | policy_allow_dialog_uris - policy_block_dialog_uris - all_contact_uris blocked_dialog_uris = contact_block_dialog_uris | policy_block_dialog_uris - all_contact_uris subscribe_presence_uris = contact_subscribe_presence_uris | policy_subscribe_presence_uris - all_contact_uris subscribe_dialog_uris = contact_subscribe_dialog_uris | policy_subscribe_dialog_uris - all_contact_uris if allowed_presence_uris != set(entry.uri for entry in oma_grantedcontacts): oma_grantedcontacts.clear() oma_grantedcontacts.update(resourcelists.Entry(uri) for uri in allowed_presence_uris) if blocked_presence_uris != set(entry.uri for entry in oma_blockedcontacts): oma_blockedcontacts.clear() oma_blockedcontacts.update(resourcelists.Entry(uri) for uri in blocked_presence_uris) if allowed_dialog_uris != set(entry.uri for entry in dialog_grantedcontacts): dialog_grantedcontacts.clear() dialog_grantedcontacts.update(resourcelists.Entry(uri) for uri in allowed_dialog_uris) if blocked_dialog_uris != set(entry.uri for entry in dialog_blockedcontacts): dialog_blockedcontacts.clear() dialog_blockedcontacts.update(resourcelists.Entry(uri) for uri in blocked_dialog_uris) if subscribe_presence_uris != set(entry.uri for entry in sipsimple_presence_rls): sipsimple_presence_rls.clear() sipsimple_presence_rls.update(resourcelists.Entry(uri) for uri in subscribe_presence_uris) if subscribe_dialog_uris != set(entry.uri for entry in sipsimple_dialog_rls): sipsimple_dialog_rls.clear() sipsimple_dialog_rls.update(resourcelists.Entry(uri) for uri in subscribe_dialog_uris) # Normalize rls-services # if self.rls_services.content is None: self.rls_services.content = rlsservices.RLSServices() rls_services = self.rls_services.content rls_presence_uri = 'sip:' + self.rls_presence_uri rls_dialog_uri = 'sip:' + self.rls_dialog_uri rls_presence_list = rlsservices.ResourceList(self.resource_lists.url + '/~~' + resource_lists.get_xpath(sipsimple_presence_rls)) rls_dialog_list = rlsservices.ResourceList(self.resource_lists.url + '/~~' + resource_lists.get_xpath(sipsimple_dialog_rls)) try: rls_presence_service = rls_services[rls_presence_uri] except KeyError: rls_presence_service = rlsservices.Service(rls_presence_uri, list=rls_presence_list, packages=['presence']) rls_services.add(rls_presence_service) else: if rls_presence_service.list != rls_presence_list: rls_presence_service.list = rls_presence_list if list(rls_presence_service.packages) != ['presence']: rls_presence_service.packages = ['presence'] try: rls_dialog_service = rls_services[rls_dialog_uri] except KeyError: rls_dialog_service = rlsservices.Service(rls_dialog_uri, list=rls_dialog_list, packages=['dialog']) rls_services.add(rls_dialog_service) else: if rls_dialog_service.list != rls_dialog_list: rls_dialog_service.list = rls_dialog_list if list(rls_dialog_service.packages) != ['dialog']: rls_dialog_service.packages = ['dialog'] # Normalize pres-rules # if self.pres_rules.content is None: self.pres_rules.content = presrules.PresRules() def fix_subhandling(rule, valid_values=[]): subhandling_elements = sorted((item for item in rule.actions if isinstance(item, presrules.SubHandling)), key=attrgetter('value.priority')) if not subhandling_elements: subhandling_elements = [presrules.SubHandling('block')] # spec specifies that missing SubHandling means block rule.actions.update(subhandling_elements) subhandling = subhandling_elements.pop() for item in subhandling_elements: # remove any extraneous SubHandling elements rule.actions.remove(item) if subhandling.value not in valid_values: subhandling.value = valid_values[0] pres_rules = self.pres_rules.content oma_grantedcontacts_ref = omapolicy.ExternalList([self.resource_lists.url + '/~~' + resource_lists.get_xpath(oma_grantedcontacts)]) oma_blockedcontacts_ref = omapolicy.ExternalList([self.resource_lists.url + '/~~' + resource_lists.get_xpath(oma_blockedcontacts)]) try: wp_prs_grantedcontacts = pres_rules['wp_prs_grantedcontacts'] except KeyError: wp_prs_grantedcontacts = commonpolicy.Rule('wp_prs_grantedcontacts', conditions=[oma_grantedcontacts_ref], actions=[presrules.SubHandling('allow')]) pres_rules.add(wp_prs_grantedcontacts) else: fix_subhandling(wp_prs_grantedcontacts, valid_values=['allow']) if list(wp_prs_grantedcontacts.conditions) != [oma_grantedcontacts_ref]: wp_prs_grantedcontacts.conditions = [oma_grantedcontacts_ref] if wp_prs_grantedcontacts.transformations: wp_prs_grantedcontacts.transformations = None try: wp_prs_blockedcontacts = pres_rules['wp_prs_blockedcontacts'] except KeyError: wp_prs_blockedcontacts = commonpolicy.Rule('wp_prs_blockedcontacts', conditions=[oma_blockedcontacts_ref], actions=[presrules.SubHandling('polite-block')]) pres_rules.add(wp_prs_blockedcontacts) else: fix_subhandling(wp_prs_blockedcontacts, valid_values=['polite-block']) if list(wp_prs_blockedcontacts.conditions) != [oma_blockedcontacts_ref]: wp_prs_blockedcontacts.conditions = [oma_blockedcontacts_ref] if wp_prs_blockedcontacts.transformations: wp_prs_blockedcontacts.transformations = None wp_prs_unlisted = pres_rules.get('wp_prs_unlisted', None) wp_prs_allow_unlisted = pres_rules.get('wp_prs_allow_unlisted', None) if wp_prs_unlisted is not None and wp_prs_allow_unlisted is not None: pres_rules.remove(wp_prs_allow_unlisted) wp_prs_allow_unlisted = None wp_prs_unlisted_rule = wp_prs_unlisted or wp_prs_allow_unlisted if wp_prs_unlisted_rule is None: wp_prs_unlisted = commonpolicy.Rule('wp_prs_unlisted', conditions=[omapolicy.OtherIdentity()], actions=[presrules.SubHandling('confirm')]) pres_rules.add(wp_prs_unlisted) wp_prs_unlisted_rule = wp_prs_unlisted else: if wp_prs_unlisted_rule is wp_prs_unlisted: fix_subhandling(wp_prs_unlisted_rule, valid_values=['confirm', 'block', 'polite-block']) else: fix_subhandling(wp_prs_unlisted_rule, valid_values=['allow']) if list(wp_prs_unlisted_rule.conditions) != [omapolicy.OtherIdentity()]: wp_prs_unlisted_rule.conditions = [omapolicy.OtherIdentity()] if wp_prs_unlisted_rule.transformations: wp_prs_unlisted_rule.transformations = None match_anonymous = omapolicy.AnonymousRequest() try: wp_prs_block_anonymous = pres_rules['wp_prs_block_anonymous'] except KeyError: wp_prs_block_anonymous = commonpolicy.Rule('wp_prs_block_anonymous', conditions=[match_anonymous], actions=[presrules.SubHandling('block')]) pres_rules.add(wp_prs_block_anonymous) else: fix_subhandling(wp_prs_block_anonymous, valid_values=['block', 'polite-block']) if list(wp_prs_block_anonymous.conditions) != [match_anonymous]: wp_prs_block_anonymous.conditions = [match_anonymous] if wp_prs_block_anonymous.transformations: wp_prs_block_anonymous.transformations = None match_self = commonpolicy.Identity([commonpolicy.IdentityOne('sip:' + self.account.id)]) try: wp_prs_allow_own = pres_rules['wp_prs_allow_own'] except KeyError: wp_prs_allow_own = commonpolicy.Rule('wp_prs_allow_own', conditions=[match_self], actions=[presrules.SubHandling('allow')]) pres_rules.add(wp_prs_allow_own) else: fix_subhandling(wp_prs_allow_own, valid_values=['allow']) if list(wp_prs_allow_own.conditions) != [match_self]: wp_prs_allow_own.conditions = [match_self] if wp_prs_allow_own.transformations: wp_prs_allow_own.transformations = None # Remove any other rules all_rule_names = set(pres_rules[IterateIDs]) known_rule_names = {'wp_prs_grantedcontacts', 'wp_prs_blockedcontacts', 'wp_prs_unlisted', 'wp_prs_allow_unlisted', 'wp_prs_block_anonymous', 'wp_prs_allow_own'} for name in all_rule_names - known_rule_names: del pres_rules[name] del fix_subhandling # Normalize dialog-rules # if self.dialog_rules.supported: if self.dialog_rules.content is None: self.dialog_rules.content = dialogrules.DialogRules() elif self.dialog_rules.content.element.nsmap.get('dr') != dialogrules.namespace: # TODO: this elif branch should be removed in a later version as it is self.dialog_rules.content = dialogrules.DialogRules() # only used to discard documents created with the old namespace. -Dan def fix_subhandling(rule, valid_values=()): subhandling_elements = sorted((item for item in rule.actions if isinstance(item, dialogrules.SubHandling)), key=attrgetter('value.priority')) if not subhandling_elements: subhandling_elements = [dialogrules.SubHandling('block')] # spec specifies that missing SubHandling means block rule.actions.update(subhandling_elements) subhandling = subhandling_elements.pop() for item in subhandling_elements: # remove any extraneous SubHandling elements rule.actions.remove(item) if subhandling.value not in valid_values: subhandling.value = valid_values[0] dialog_rules = self.dialog_rules.content dialog_grantedcontacts_ref = omapolicy.ExternalList([self.resource_lists.url + '/~~' + resource_lists.get_xpath(dialog_grantedcontacts)]) dialog_blockedcontacts_ref = omapolicy.ExternalList([self.resource_lists.url + '/~~' + resource_lists.get_xpath(dialog_blockedcontacts)]) try: wp_dlg_grantedcontacts = dialog_rules['wp_dlg_grantedcontacts'] except KeyError: wp_dlg_grantedcontacts = commonpolicy.Rule('wp_dlg_grantedcontacts', conditions=[dialog_grantedcontacts_ref], actions=[dialogrules.SubHandling('allow')]) dialog_rules.add(wp_dlg_grantedcontacts) else: fix_subhandling(wp_dlg_grantedcontacts, valid_values=['allow']) if list(wp_dlg_grantedcontacts.conditions) != [dialog_grantedcontacts_ref]: wp_dlg_grantedcontacts.conditions = [dialog_grantedcontacts_ref] if wp_dlg_grantedcontacts.transformations: wp_dlg_grantedcontacts.transformations = None try: wp_dlg_blockedcontacts = dialog_rules['wp_dlg_blockedcontacts'] except KeyError: wp_dlg_blockedcontacts = commonpolicy.Rule('wp_dlg_blockedcontacts', conditions=[dialog_blockedcontacts_ref], actions=[dialogrules.SubHandling('polite-block')]) dialog_rules.add(wp_dlg_blockedcontacts) else: fix_subhandling(wp_dlg_blockedcontacts, valid_values=['polite-block']) if list(wp_dlg_blockedcontacts.conditions) != [dialog_blockedcontacts_ref]: wp_dlg_blockedcontacts.conditions = [dialog_blockedcontacts_ref] if wp_dlg_blockedcontacts.transformations: wp_dlg_blockedcontacts.transformations = None wp_dlg_unlisted = dialog_rules.get('wp_dlg_unlisted', None) wp_dlg_allow_unlisted = dialog_rules.get('wp_dlg_allow_unlisted', None) if wp_dlg_unlisted is not None and wp_dlg_allow_unlisted is not None: dialog_rules.remove(wp_dlg_allow_unlisted) wp_dlg_allow_unlisted = None wp_dlg_unlisted_rule = wp_dlg_unlisted or wp_dlg_allow_unlisted if wp_dlg_unlisted_rule is None: wp_dlg_unlisted = commonpolicy.Rule('wp_dlg_unlisted', conditions=[omapolicy.OtherIdentity()], actions=[dialogrules.SubHandling('confirm')]) dialog_rules.add(wp_dlg_unlisted) wp_dlg_unlisted_rule = wp_dlg_unlisted else: if wp_dlg_unlisted_rule is wp_dlg_unlisted: fix_subhandling(wp_dlg_unlisted_rule, valid_values=['confirm', 'block', 'polite-block']) else: fix_subhandling(wp_dlg_unlisted_rule, valid_values=['allow']) if list(wp_dlg_unlisted_rule.conditions) != [omapolicy.OtherIdentity()]: wp_dlg_unlisted_rule.conditions = [omapolicy.OtherIdentity()] if wp_dlg_unlisted_rule.transformations: wp_dlg_unlisted_rule.transformations = None match_anonymous = omapolicy.AnonymousRequest() try: wp_dlg_block_anonymous = dialog_rules['wp_dlg_block_anonymous'] except KeyError: wp_dlg_block_anonymous = commonpolicy.Rule('wp_dlg_block_anonymous', conditions=[match_anonymous], actions=[dialogrules.SubHandling('block')]) dialog_rules.add(wp_dlg_block_anonymous) else: fix_subhandling(wp_dlg_block_anonymous, valid_values=['block', 'polite-block']) if list(wp_dlg_block_anonymous.conditions) != [match_anonymous]: wp_dlg_block_anonymous.conditions = [match_anonymous] if wp_dlg_block_anonymous.transformations: wp_dlg_block_anonymous.transformations = None match_self = commonpolicy.Identity([commonpolicy.IdentityOne('sip:' + self.account.id)]) try: wp_dlg_allow_own = dialog_rules['wp_dlg_allow_own'] except KeyError: wp_dlg_allow_own = commonpolicy.Rule('wp_dlg_allow_own', conditions=[match_self], actions=[dialogrules.SubHandling('allow')]) dialog_rules.add(wp_dlg_allow_own) else: fix_subhandling(wp_dlg_allow_own, valid_values=['allow']) if list(wp_dlg_allow_own.conditions) != [match_self]: wp_dlg_allow_own.conditions = [match_self] if wp_dlg_allow_own.transformations: wp_dlg_allow_own.transformations = None # Remove any other rules all_rule_names = set(dialog_rules[IterateIDs]) known_rule_names = {'wp_dlg_grantedcontacts', 'wp_dlg_blockedcontacts', 'wp_dlg_unlisted', 'wp_dlg_allow_unlisted', 'wp_dlg_block_anonymous', 'wp_dlg_allow_own'} for name in all_rule_names - known_rule_names: del dialog_rules[name] # Normalize status icon # if self.status_icon.supported and self.status_icon.content is not None: content = self.status_icon.content if None in (content.encoding, content.mime_type) or content.encoding.value.lower() != 'base64' or content.mime_type.value.lower() not in Icon.__mimetypes__: self.status_icon.content = None self.status_icon.dirty = True def _OH_AddContactOperation(self, operation): sipsimple_addressbook = self.resource_lists.content['sipsimple_addressbook'] contact = operation.contact presence_handling = addressbook.PresenceHandling(contact.presence.policy, contact.presence.subscribe) dialog_handling = addressbook.DialogHandling(contact.dialog.policy, contact.dialog.subscribe) xml_contact = addressbook.Contact(contact.id, contact.name, presence_handling=presence_handling, dialog_handling=dialog_handling) for uri in contact.uris: contact_uri = addressbook.ContactURI(uri.id, uri.uri, uri.type) contact_uri.attributes = addressbook.ContactURI.attributes.type(uri.attributes) xml_contact.uris.add(contact_uri) xml_contact.uris.default = contact.uris.default xml_contact.attributes = addressbook.Contact.attributes.type(contact.attributes) sipsimple_addressbook.add(xml_contact) def _OH_UpdateContactOperation(self, operation): sipsimple_addressbook = self.resource_lists.content['sipsimple_addressbook'] try: contact = sipsimple_addressbook[addressbook.Contact, operation.contact.id] except KeyError: return attributes = dict(operation.attributes) attributes.pop('id', None) # id is never modified attributes.pop('uris', None) # uris are modified using dedicated methods if 'name' in attributes: contact.name = attributes.pop('name') if 'uris.default' in attributes: contact.uris.default = attributes.pop('uris.default') if 'presence.policy' in attributes: contact.presence.policy = attributes.pop('presence.policy') if 'presence.subscribe' in attributes: contact.presence.subscribe = attributes.pop('presence.subscribe') if 'dialog.policy' in attributes: contact.dialog.policy = attributes.pop('dialog.policy') if 'dialog.subscribe' in attributes: contact.dialog.subscribe = attributes.pop('dialog.subscribe') if contact.attributes is None: contact.attributes = addressbook.Contact.attributes.type() contact.attributes.update(attributes) def _OH_RemoveContactOperation(self, operation): sipsimple_addressbook = self.resource_lists.content['sipsimple_addressbook'] for group in (group for group in sipsimple_addressbook[addressbook.Group, IterateItems] if operation.contact.id in group.contacts): group.contacts.remove(operation.contact.id) try: del sipsimple_addressbook[addressbook.Contact, operation.contact.id] except KeyError: pass def _OH_AddContactURIOperation(self, operation): sipsimple_addressbook = self.resource_lists.content['sipsimple_addressbook'] try: contact = sipsimple_addressbook[addressbook.Contact, operation.contact.id] except KeyError: return uri = addressbook.ContactURI(operation.uri.id, operation.uri.uri, operation.uri.type) uri.attributes = addressbook.ContactURI.attributes.type(operation.uri.attributes) contact.uris.add(uri) def _OH_UpdateContactURIOperation(self, operation): sipsimple_addressbook = self.resource_lists.content['sipsimple_addressbook'] try: contact = sipsimple_addressbook[addressbook.Contact, operation.contact.id] uri = contact.uris[operation.uri.id] except KeyError: return attributes = dict(operation.attributes) attributes.pop('id', None) # id is never modified if 'uri' in attributes: uri.uri = attributes.pop('uri') if 'type' in attributes: uri.type = attributes.pop('type') if uri.attributes is None: uri.attributes = addressbook.ContactURI.attributes.type() uri.attributes.update(attributes) def _OH_RemoveContactURIOperation(self, operation): sipsimple_addressbook = self.resource_lists.content['sipsimple_addressbook'] try: contact = sipsimple_addressbook[addressbook.Contact, operation.contact.id] del contact.uris[operation.uri.id] except KeyError: pass def _OH_AddGroupOperation(self, operation): sipsimple_addressbook = self.resource_lists.content['sipsimple_addressbook'] group = addressbook.Group(operation.group.id, operation.group.name, [contact.id for contact in operation.group.contacts]) group.attributes = addressbook.Group.attributes.type(operation.group.attributes) sipsimple_addressbook.add(group) def _OH_UpdateGroupOperation(self, operation): sipsimple_addressbook = self.resource_lists.content['sipsimple_addressbook'] try: group = sipsimple_addressbook[addressbook.Group, operation.group.id] except KeyError: return attributes = dict(operation.attributes) attributes.pop('id', None) # id is never modified attributes.pop('contacts', None) # contacts are added/removed using dedicated methods if 'name' in attributes: group.name = attributes.pop('name') if group.attributes is None: group.attributes = addressbook.Group.attributes.type() group.attributes.update(attributes) def _OH_RemoveGroupOperation(self, operation): sipsimple_addressbook = self.resource_lists.content['sipsimple_addressbook'] try: del sipsimple_addressbook[addressbook.Group, operation.group.id] except KeyError: pass def _OH_AddGroupMemberOperation(self, operation): sipsimple_addressbook = self.resource_lists.content['sipsimple_addressbook'] try: group = sipsimple_addressbook[addressbook.Group, operation.group.id] except KeyError: return if operation.contact.id in group.contacts: return group.contacts.add(operation.contact.id) def _OH_RemoveGroupMemberOperation(self, operation): sipsimple_addressbook = self.resource_lists.content['sipsimple_addressbook'] try: group = sipsimple_addressbook[addressbook.Group, operation.group.id] group.contacts.remove(operation.contact.id) except KeyError: return def _OH_AddPolicyOperation(self, operation): sipsimple_addressbook = self.resource_lists.content['sipsimple_addressbook'] presence_handling = addressbook.PresenceHandling(operation.policy.presence.policy, operation.policy.presence.subscribe) dialog_handling = addressbook.DialogHandling(operation.policy.dialog.policy, operation.policy.dialog.subscribe) policy = addressbook.Policy(operation.policy.id, operation.policy.uri, operation.policy.name, presence_handling=presence_handling, dialog_handling=dialog_handling) policy.attributes = addressbook.Policy.attributes.type(operation.policy.attributes) sipsimple_addressbook.add(policy) def _OH_UpdatePolicyOperation(self, operation): sipsimple_addressbook = self.resource_lists.content['sipsimple_addressbook'] try: policy = sipsimple_addressbook[addressbook.Policy, operation.policy.id] except KeyError: return attributes = dict(operation.attributes) attributes.pop('id', None) # id is never modified if 'uri' in attributes: policy.uri = attributes.pop('uri') if 'name' in attributes: policy.name = attributes.pop('name') if 'presence.policy' in attributes: policy.presence.policy = attributes.pop('presence.policy') if 'presence.subscribe' in attributes: policy.presence.subscribe = attributes.pop('presence.subscribe') if 'dialog.policy' in attributes: policy.dialog.policy = attributes.pop('dialog.policy') if 'dialog.subscribe' in attributes: policy.dialog.subscribe = attributes.pop('dialog.subscribe') if policy.attributes is None: policy.attributes = addressbook.Policy.attributes.type() policy.attributes.update(attributes) def _OH_RemovePolicyOperation(self, operation): sipsimple_addressbook = self.resource_lists.content['sipsimple_addressbook'] try: del sipsimple_addressbook[addressbook.Policy, operation.policy.id] except KeyError: pass def _OH_SetStatusIconOperation(self, operation): if not self.status_icon.supported: return icon = operation.icon if icon is None or not icon.data: self.status_icon.dirty = self.status_icon.content is not None self.status_icon.content = None else: content = prescontent.PresenceContent(data=base64.encodestring(icon.data), mime_type=icon.mime_type, encoding='base64', description=icon.description) if self.status_icon.content == content: return self.status_icon.content = content def _OH_SetOfflineStatusOperation(self, operation): pidf = operation.status.pidf if operation.status is not None else None if not self.pidf_manipulation.supported or pidf == self.pidf_manipulation.content: return self.pidf_manipulation.content = pidf self.pidf_manipulation.dirty = True def _OH_SetDefaultPresencePolicyOperation(self, operation): pres_rules = self.pres_rules.content if operation.policy == 'allow': rule_id, other_rule_id = 'wp_prs_allow_unlisted', 'wp_prs_unlisted' else: rule_id, other_rule_id = 'wp_prs_unlisted', 'wp_prs_allow_unlisted' try: del pres_rules[other_rule_id] except KeyError: rule = pres_rules[rule_id] subhandling = next(item for item in rule.actions if isinstance(item, presrules.SubHandling)) subhandling.value = operation.policy else: rule = commonpolicy.Rule(rule_id, conditions=[omapolicy.OtherIdentity()], actions=[presrules.SubHandling(operation.policy)]) pres_rules.add(rule) def _OH_SetDefaultDialogPolicyOperation(self, operation): if not self.dialog_rules.supported: return dialog_rules = self.dialog_rules.content if operation.policy == 'allow': rule_id, other_rule_id = 'wp_dlg_allow_unlisted', 'wp_dlg_unlisted' else: rule_id, other_rule_id = 'wp_dlg_unlisted', 'wp_dlg_allow_unlisted' try: del dialog_rules[other_rule_id] except KeyError: rule = dialog_rules[rule_id] subhandling = next(item for item in rule.actions if isinstance(item, dialogrules.SubHandling)) subhandling.value = operation.policy else: rule = commonpolicy.Rule(rule_id, conditions=[omapolicy.OtherIdentity()], actions=[dialogrules.SubHandling(operation.policy)]) dialog_rules.add(rule) # Notification handlers # @run_in_twisted_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) @run_in_green_thread def _NH_CFGSettingsObjectDidChange(self, notification): if {'__id__', 'xcap.xcap_root', 'auth.username', 'auth.password', 'sip.subscribe_interval', 'sip.transport_list'}.intersection(notification.data.modified): self.command_channel.send(Command('reload', modified=notification.data.modified)) if 'enabled' in notification.data.modified: return # global account activation is handled separately by the account itself if self.account.enabled and 'xcap.enabled' in notification.data.modified: if self.account.xcap.enabled: self.start() else: self.stop() def _NH_CFGSettingsObjectWasDeleted(self, notification): notification.center.remove_observer(self, sender=self.account, name='CFGSettingsObjectDidChange') notification.center.remove_observer(self, sender=self.account, name='CFGSettingsObjectWasDeleted') self.command_channel.send(Command('stop')) self.command_channel.send(Command('cleanup')) def _NH_XCAPSubscriptionDidStart(self, notification): self.command_channel.send(Command('fetch', documents=set(self.document_names))) def _NH_XCAPSubscriptionDidFail(self, notification): self.command_channel.send(Command('fetch', documents=set(self.document_names))) def _NH_XCAPSubscriptionGotNotify(self, notification): if notification.data.content_type == xcapdiff.XCAPDiffDocument.content_type: try: xcap_diff = xcapdiff.XCAPDiffDocument.parse(notification.data.body) except ParserError: self.command_channel.send(Command('fetch', documents=set(self.document_names))) else: applications = set(child.selector.auid for child in xcap_diff if isinstance(child, xcapdiff.Document)) documents = set(document.name for document in self.documents if document.application in applications) self.command_channel.send(Command('fetch', documents=documents)) def _load_data(self): addressbook = Addressbook.from_payload(self.resource_lists.content['sipsimple_addressbook']) default_presence_rule = self.pres_rules.content.get('wp_prs_unlisted', None) or self.pres_rules.content.get('wp_prs_allow_unlisted', None) if self.dialog_rules.supported: default_dialog_rule = self.dialog_rules.content.get('wp_dlg_unlisted', None) or self.dialog_rules.content.get('wp_dlg_allow_unlisted', None) else: default_dialog_rule = None presence_rules = PresenceRules.from_payload(default_presence_rule) dialog_rules = DialogRules.from_payload(default_dialog_rule) if self.status_icon.supported and self.status_icon.content: status_icon = Icon.from_payload(self.status_icon.content) status_icon.url = self.status_icon.url status_icon.etag = self.status_icon.etag else: status_icon = None if self.pidf_manipulation.supported and self.pidf_manipulation.content: offline_status = OfflineStatus(self.pidf_manipulation.content) else: offline_status = None data=NotificationData(addressbook=addressbook, presence_rules=presence_rules, dialog_rules=dialog_rules, status_icon=status_icon, offline_status=offline_status) NotificationCenter().post_notification('XCAPManagerDidReloadData', sender=self, data=data) def _fetch_documents(self, documents): workers = [Worker.spawn(document.fetch) for document in (doc for doc in self.documents if doc.name in documents and doc.supported)] try: while workers: worker = workers.pop() worker.wait() finally: for worker in workers: worker.wait_ex() def _save_journal(self): try: self.storage.save('journal', cPickle.dumps(self.journal)) except XCAPStorageError: pass def _schedule_command(self, timeout, command): from twisted.internet import reactor timer = reactor.callLater(timeout, self.command_channel.send, command) timer.command = command return timer class XCAPTransaction(object): def __init__(self, xcap_manager): self.xcap_manager = xcap_manager def __enter__(self): self.xcap_manager.start_transaction() return self def __exit__(self, type, value, traceback): self.xcap_manager.commit_transaction() ================================================ FILE: sipsimple/account/xcap/storage/__init__.py ================================================ """Base definitions for concrete implementations of XCAP storage backends""" __all__ = ['IXCAPStorage', 'XCAPStorageError'] from zope.interface import Interface class XCAPStorageError(Exception): """Base error to be used by backends implementing IXCAPStorage.""" class IXCAPStorage(Interface): """Interface describing a storage backend for XCAP data.""" def load(name): """ Load and return the data corresponding to the given name by using whatever means employed by the backend implementation. """ def save(name, data): """ Store the data associated with name by using whatever means employed by the backend implementation. """ def delete(name): """Delete the data associated with name.""" def purge(): """Delete all the data stored by the backend.""" ================================================ FILE: sipsimple/account/xcap/storage/file.py ================================================ """XCAP backend for storing data in files""" __all__ = ["FileStorage"] import errno import os import platform import random from application.system import makedirs, openfile, unlink from zope.interface import implements from sipsimple.account.xcap.storage import IXCAPStorage, XCAPStorageError class FileStorage(object): """Implementation of an XCAP backend that stores data in files.""" implements(IXCAPStorage) def __init__(self, directory, account_id): """Initialize the storage for the specified directory and account ID""" self.directory = directory self.account_id = account_id self.names = set() def load(self, name): """Read the file given by name and return its content.""" try: document = open(os.path.join(self.directory, self.account_id, name)).read() except (IOError, OSError), e: raise XCAPStorageError("failed to load XCAP data for %s/%s: %s" % (self.account_id, name, str(e))) else: self.names.add(name) return document def save(self, name, data): """Write the data in a file identified by name.""" filename = os.path.join(self.directory, self.account_id, name) tmp_filename = '%s.%d.%08X' % (filename, os.getpid(), random.getrandbits(32)) try: makedirs(os.path.join(self.directory, self.account_id)) file = openfile(tmp_filename, 'wb', permissions=0600) file.write(data) file.close() if platform.system() == 'Windows': # os.rename does not work on Windows if the destination file already exists. # It seems there is no atomic way to do this on Windows. unlink(filename) os.rename(tmp_filename, filename) except (IOError, OSError), e: raise XCAPStorageError("failed to save XCAP data for %s/%s: %s" % (self.account_id, name, str(e))) else: self.names.add(name) def delete(self, name): """Delete the data stored in the file identified by name""" try: os.unlink(os.path.join(self.directory, self.account_id, name)) except OSError, e: if e.errno == errno.ENOENT: self.names.discard(name) return raise XCAPStorageError("failed to delete XCAP data for %s/%s: %s" % (self.account_id, name, str(e))) else: self.names.remove(name) def purge(self): """Delete all the files stored by the backend""" failed = [] for name in self.names: try: os.unlink(os.path.join(self.directory, self.account_id, name)) except OSError, e: if e.errno == errno.ENOENT: continue failed.append(name) self.names.clear() try: os.rmdir(os.path.join(self.directory, self.account_id)) except OSError: pass if failed: raise XCAPStorageError("the following files could not be deleted for %s: %s" % (self.account_id, ', '.join(failed))) ================================================ FILE: sipsimple/account/xcap/storage/memory.py ================================================ """XCAP backend for storing data in memory""" __all__ = ["MemoryStorage"] from zope.interface import implements from sipsimple.account.xcap.storage import IXCAPStorage, XCAPStorageError class MemoryStorage(object): """Implementation of an XCAP backend that stores data in memory""" implements(IXCAPStorage) def __init__(self, account_id): """Initialize the backend for the specified account ID""" self.account_id = account_id self.data = {} def load(self, name): """Return the data given by name""" try: return self.data[name] except KeyError: raise XCAPStorageError("missing entry: %s/%s" % (self.account_id, name)) def save(self, name, data): """Store the data under a key given by name""" self.data[name] = data def delete(self, name): """Delete the data identified by name""" self.data.pop(name, None) def purge(self): """Delete all the data that is stored in the backend""" self.data.clear() ================================================ FILE: sipsimple/addressbook.py ================================================ """Implementation of an addressbook management system""" from __future__ import absolute_import __all__ = ['AddressbookManager', 'Contact', 'ContactURI', 'Group', 'Policy', 'SharedSetting', 'ContactExtension', 'ContactURIExtension', 'GroupExtension', 'PolicyExtension'] from functools import reduce from operator import attrgetter from random import randint from threading import Lock from time import time from zope.interface import implements from application.notification import IObserver, NotificationCenter, NotificationData from application.python import Null from application.python.decorator import execute_once from application.python.types import Singleton, MarkerType from application.python.weakref import defaultweakobjectmap from sipsimple import log from sipsimple.account import xcap, AccountManager from sipsimple.configuration import ConfigurationManager, ObjectNotFoundError, DuplicateIDError, PersistentKey, ModifiedValue, ModifiedList from sipsimple.configuration import AbstractSetting, RuntimeSetting, SettingsObjectImmutableID, SettingsGroup, SettingsGroupMeta, SettingsState, ItemCollection, ItemManagement from sipsimple.payloads.addressbook import PolicyValue, ElementAttributes from sipsimple.payloads.datatypes import ID from sipsimple.payloads.resourcelists import ResourceListsDocument from sipsimple.threading import run_in_thread def unique_id(prefix='id'): return "%s%d%06d" % (prefix, time()*1e6, randint(0, 999999)) def recursive_getattr(obj, name): return reduce(getattr, name.split('.'), obj) class Local(object): __metaclass__ = MarkerType class Remote(object): def __init__(self, account, xcap_object): self.account = account self.xcap_object = xcap_object def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.account, self.xcap_object) class Setting(AbstractSetting): """ Descriptor representing a setting in an addressbook object. Unlike a standard Setting, this one will only use the default value as a template to fill in a missing value and explicitly set it when saving if it was not specified explicitly prior to that. """ def __init__(self, type, default=None, nillable=False): if default is None and not nillable: raise TypeError("default must be specified if object is not nillable") self.type = type self.default = default self.nillable = nillable self.values = defaultweakobjectmap(lambda: default) self.oldvalues = defaultweakobjectmap(lambda: default) self.dirty = defaultweakobjectmap(bool) self.lock = Lock() def __get__(self, obj, objtype): if obj is None: return self with self.lock: return self.values[obj] def __set__(self, obj, value): if value is None and not self.nillable: raise ValueError("setting attribute is not nillable") if value is not None and not isinstance(value, self.type): value = self.type(value) with self.lock: self.values[obj] = value self.dirty[obj] = value != self.oldvalues[obj] def __getstate__(self, obj): with self.lock: value = self.values[obj] if value is None: pass elif issubclass(self.type, bool): value = u'true' if value else u'false' elif issubclass(self.type, (int, long, basestring)): value = unicode(value) elif hasattr(value, '__getstate__'): value = value.__getstate__() else: value = unicode(value) return value def __setstate__(self, obj, value): if value is None and not self.nillable: raise ValueError("setting attribute is not nillable") if value is None: pass elif issubclass(self.type, bool): if value.lower() in ('true', 'yes', 'on', '1'): value = True elif value.lower() in ('false', 'no', 'off', '0'): value = False else: raise ValueError("invalid boolean value: %s" % (value,)) elif issubclass(self.type, (int, long, basestring)): value = self.type(value) elif hasattr(self.type, '__setstate__'): object = self.type.__new__(self.type) object.__setstate__(value) value = object else: value = self.type(value) with self.lock: self.oldvalues[obj] = self.values[obj] = value self.dirty[obj] = False def get_modified(self, obj): with self.lock: try: if self.dirty[obj]: return ModifiedValue(old=self.oldvalues[obj], new=self.values[obj]) else: return None finally: self.oldvalues[obj] = self.values[obj] self.dirty[obj] = False def get_old(self, obj): with self.lock: return self.oldvalues[obj] def undo(self, obj): with self.lock: self.values[obj] = self.oldvalues[obj] self.dirty[obj] = False class SharedSetting(Setting): """A setting that is shared by being also stored remotely in XCAP""" __namespace__ = None @classmethod def set_namespace(cls, namespace): """ Set the XML namespace to be used for the extra shared attributes of a contact, when storing it in XCAP """ if cls.__namespace__ is not None: raise RuntimeError("namespace already set to %s" % cls.__namespace__) cls.__namespace__ = namespace class ApplicationElementAttributes(ElementAttributes): _xml_namespace = 'urn:%s:xml:ns:addressbook' % namespace ResourceListsDocument.unregister_namespace(ElementAttributes._xml_namespace) ResourceListsDocument.register_namespace(ApplicationElementAttributes._xml_namespace, prefix=namespace.rpartition(':')[2]) for cls, attribute_name in ((cls, name) for cls in ResourceListsDocument.element_map.values() for name, elem in cls._xml_element_children.items() if elem.type is ElementAttributes): cls.unregister_extension(attribute_name) cls.register_extension(attribute_name, ApplicationElementAttributes) class AddressbookKey(object): def __init__(self, section): self.group = 'Addressbook' self.section = section def __get__(self, obj, objtype): if obj is None: return [self.group, self.section] else: return [self.group, self.section, PersistentKey(obj.__id__)] def __set__(self, obj, value): raise AttributeError('cannot set attribute') def __delete__(self, obj): raise AttributeError('cannot delete attribute') class MultiAccountTransaction(object): def __init__(self, accounts): self.accounts = accounts def __enter__(self): for account in self.accounts: account.xcap_manager.start_transaction() return self def __exit__(self, exc_type, exc_value, traceback): for account in self.accounts: account.xcap_manager.commit_transaction() def __iter__(self): return iter(self.accounts) class XCAPGroup(xcap.Group): """An XCAP Group with attributes normalized to unicode""" __attributes__ = set() def __init__(self, id, name, contacts, **attributes): normalized_attributes = dict((name, unicode(value) if value is not None else None) for name, value in attributes.iteritems() if name in self.__attributes__) contacts = [XCAPContact.normalize(contact) for contact in contacts] super(XCAPGroup, self).__init__(id, name, contacts, **normalized_attributes) @classmethod def normalize(cls, group): return cls(group.id, group.name, group.contacts, **group.attributes) def get_modified(self, modified_keys): names = {'name'} attributes = dict((name, getattr(self, name)) for name in names.intersection(modified_keys)) attributes.update((name, self.attributes[name]) for name in self.__attributes__.intersection(modified_keys)) return attributes class XCAPContactURI(xcap.ContactURI): """An XCAP ContactURI with attributes normalized to unicode""" __attributes__ = set() def __init__(self, id, uri, type, **attributes): normalized_attributes = dict((name, unicode(value) if value is not None else None) for name, value in attributes.iteritems() if name in self.__attributes__) super(XCAPContactURI, self).__init__(id, uri, type, **normalized_attributes) @classmethod def normalize(cls, uri): return cls(uri.id, uri.uri, uri.type, **uri.attributes) def get_modified(self, modified_keys): names = {'uri', 'type'} attributes = dict((name, getattr(self, name)) for name in names.intersection(modified_keys)) attributes.update((name, self.attributes[name]) for name in self.__attributes__.intersection(modified_keys)) return attributes class XCAPContact(xcap.Contact): """An XCAP Contact with attributes normalized to unicode""" __attributes__ = set() def __init__(self, id, name, uris, presence_handling=None, dialog_handling=None, **attributes): normalized_attributes = dict((name, unicode(value) if value is not None else None) for name, value in attributes.iteritems() if name in self.__attributes__) uris = xcap.ContactURIList((XCAPContactURI.normalize(uri) for uri in uris), default=getattr(uris, 'default', None)) super(XCAPContact, self).__init__(id, name, uris, presence_handling, dialog_handling, **normalized_attributes) @classmethod def normalize(cls, contact): return cls(contact.id, contact.name, contact.uris, contact.presence, contact.dialog, **contact.attributes) def get_modified(self, modified_keys): names = {'name', 'uris.default', 'presence.policy', 'presence.subscribe', 'dialog.policy', 'dialog.subscribe'} attributes = dict((name, recursive_getattr(self, name)) for name in names.intersection(modified_keys)) attributes.update((name, self.attributes[name]) for name in self.__attributes__.intersection(modified_keys)) return attributes class XCAPPolicy(xcap.Policy): """An XCAP Policy with attributes normalized to unicode""" __attributes__ = set() def __init__(self, id, uri, name, presence_handling=None, dialog_handling=None, **attributes): normalized_attributes = dict((name, unicode(value) if value is not None else None) for name, value in attributes.iteritems() if name in self.__attributes__) super(XCAPPolicy, self).__init__(id, uri, name, presence_handling, dialog_handling, **normalized_attributes) @classmethod def normalize(cls, policy): return cls(policy.id, policy.uri, policy.name, policy.presence, policy.dialog, **policy.attributes) def get_modified(self, modified_keys): names = {'uri', 'name', 'presence.policy', 'presence.subscribe', 'dialog.policy', 'dialog.subscribe'} attributes = dict((name, recursive_getattr(self, name)) for name in names.intersection(modified_keys)) attributes.update((name, self.attributes[name]) for name in self.__attributes__.intersection(modified_keys)) return attributes class ContactListDescriptor(AbstractSetting): def __init__(self): self.values = defaultweakobjectmap(ContactList) self.oldvalues = defaultweakobjectmap(ContactList) self.lock = Lock() def __get__(self, obj, objtype): if obj is None: return self with self.lock: return self.values[obj] def __set__(self, obj, value): if value is None: raise ValueError("setting attribute is not nillable") elif not isinstance(value, ContactList): value = ContactList(value) with self.lock: self.values[obj] = value def __getstate__(self, obj): with self.lock: return self.values[obj].__getstate__() def __setstate__(self, obj, value): if value is None: raise ValueError("setting attribute is not nillable") object = ContactList.__new__(ContactList) object.__setstate__(value) with self.lock: self.values[obj] = object self.oldvalues[obj] = ContactList(object) def get_modified(self, obj): with self.lock: old = self.oldvalues[obj] new = self.values[obj] with new.lock: old_ids = set(old.ids()) new_ids = set(new.ids()) added_contacts = [new[id] for id in new_ids - old_ids] removed_contacts = [old[id] for id in old_ids - new_ids] try: if added_contacts or removed_contacts: return ModifiedList(added=added_contacts, removed=removed_contacts, modified=None) else: return None finally: self.oldvalues[obj] = ContactList(new) def get_old(self, obj): with self.lock: return self.oldvalues[obj] def undo(self, obj): with self.lock: self.values[obj] = ContactList(self.oldvalues[obj]) class ContactList(object): def __new__(cls, contacts=None): instance = object.__new__(cls) instance.lock = Lock() return instance def __init__(self, contacts=None): self.contacts = dict((contact.id, contact) for contact in contacts or [] if contact.__state__ != 'deleted') def __getitem__(self, key): return self.contacts[key] def __contains__(self, key): return key in self.contacts def __iter__(self): return iter(sorted(self.contacts.values(), key=attrgetter('id'))) def __reversed__(self): return iter(sorted(self.contacts.values(), key=attrgetter('id'), reverse=True)) __hash__ = None def __len__(self): return len(self.contacts) def __eq__(self, other): if isinstance(other, ContactList): return self.contacts == other.contacts return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal def __repr__(self): return "%s(%r)" % (self.__class__.__name__, sorted(self.contacts.values(), key=attrgetter('id'))) def __getstate__(self): return self.contacts.keys() def __setstate__(self, value): addressbook_manager = AddressbookManager() for id in [id for id in value if not addressbook_manager.has_contact(id)]: value.remove(id) with self.lock: self.contacts = dict((id, addressbook_manager.get_contact(id)) for id in value) def ids(self): return sorted(self.contacts.keys()) def add(self, contact): if contact.__state__ == 'deleted': return with self.lock: self.contacts[contact.id] = contact def remove(self, contact): with self.lock: self.contacts.pop(contact.id, None) class Group(SettingsState): __key__ = AddressbookKey('Groups') __id__ = SettingsObjectImmutableID(type=ID) id = __id__ name = Setting(type=unicode, default='') contacts = ContactListDescriptor() def __new__(cls, id=None): with AddressbookManager.load.lock: if not AddressbookManager.load.called: raise RuntimeError("cannot instantiate %s before calling AddressbookManager.load" % cls.__name__) if id is None: id = unique_id() elif not isinstance(id, basestring): raise TypeError("id needs to be a string or unicode object") instance = SettingsState.__new__(cls) instance.__id__ = id instance.__state__ = 'new' instance.__xcapgroup__ = None configuration = ConfigurationManager() try: data = configuration.get(instance.__key__) except ObjectNotFoundError: pass else: instance.__setstate__(data) instance.__state__ = 'loaded' instance.__xcapgroup__ = instance.__toxcap__() return instance def __establish__(self): if self.__state__ == 'loaded': self.__state__ = 'active' notification_center = NotificationCenter() notification_center.post_notification('AddressbookGroupWasActivated', sender=self) def __repr__(self): return "%s(id=%r)" % (self.__class__.__name__, self.id) def __toxcap__(self): xcap_contacts = [contact.__xcapcontact__ for contact in self.contacts] attributes = dict((name, getattr(self, name)) for name, attr in vars(self.__class__).iteritems() if isinstance(attr, SharedSetting)) return XCAPGroup(self.id, self.name, xcap_contacts, **attributes) @run_in_thread('file-io') def _internal_save(self, originator): if self.__state__ == 'deleted': return for contact in [contact for contact in self.contacts if contact.__state__ == 'deleted']: self.contacts.remove(contact) modified_settings = self.get_modified() if not modified_settings and self.__state__ != 'new': return account_manager = AccountManager() configuration = ConfigurationManager() notification_center = NotificationCenter() if originator is Local: originator_account = None previous_xcapgroup = self.__xcapgroup__ else: originator_account = originator.account previous_xcapgroup = originator.xcap_object xcap_accounts = [account for account in account_manager.get_accounts() if account.xcap.discovered] self.__xcapgroup__ = self.__toxcap__() if self.__state__ == 'new': configuration.update(self.__key__, self.__getstate__()) self.__state__ = 'active' for account in (account for account in xcap_accounts if account is not originator_account): account.xcap_manager.add_group(self.__xcapgroup__) modified_data = None notification_center.post_notification('AddressbookGroupWasActivated', sender=self) notification_center.post_notification('AddressbookGroupWasCreated', sender=self) elif all(isinstance(self.__settings__[key], RuntimeSetting) for key in modified_settings): notification_center.post_notification('AddressbookGroupDidChange', sender=self, data=NotificationData(modified=modified_settings)) return else: configuration.update(self.__key__, self.__getstate__()) attributes = self.__xcapgroup__.get_modified(modified_settings) if 'contacts' in modified_settings: added_contacts = [contact.__xcapcontact__ for contact in modified_settings['contacts'].added] removed_contacts = [contact.__xcapcontact__ for contact in modified_settings['contacts'].removed] else: added_contacts = [] removed_contacts = [] if self.__xcapgroup__ != previous_xcapgroup: outofsync_accounts = xcap_accounts elif originator is Local: outofsync_accounts = [] else: outofsync_accounts = list(account for account in xcap_accounts if account is not originator_account) with MultiAccountTransaction(outofsync_accounts): for account in outofsync_accounts: xcap_manager = account.xcap_manager for xcapcontact in added_contacts: xcap_manager.add_group_member(self.__xcapgroup__, xcapcontact) for xcapcontact in removed_contacts: xcap_manager.remove_group_member(self.__xcapgroup__, xcapcontact) if attributes: xcap_manager.update_group(self.__xcapgroup__, attributes) notification_center.post_notification('AddressbookGroupDidChange', sender=self, data=NotificationData(modified=modified_settings)) modified_data = modified_settings try: configuration.save() except Exception, e: log.exception() notification_center.post_notification('CFGManagerSaveFailed', sender=configuration, data=NotificationData(object=self, operation='save', modified=modified_data, exception=e)) @run_in_thread('file-io') def _internal_delete(self, originator): if self.__state__ == 'deleted': return self.__state__ = 'deleted' configuration = ConfigurationManager() account_manager = AccountManager() notification_center = NotificationCenter() if originator is Local: originator_account = None else: originator_account = originator.account configuration.delete(self.__key__) for account in (account for account in account_manager.iter_accounts() if account.xcap.discovered and account is not originator_account): account.xcap_manager.remove_group(self.__xcapgroup__) notification_center.post_notification('AddressbookGroupWasDeleted', sender=self) try: configuration.save() except Exception, e: log.exception() notification_center.post_notification('CFGManagerSaveFailed', sender=configuration, data=NotificationData(object=self, operation='delete', exception=e)) def save(self): """ Store the group into persistent storage (local and xcap). This method will post the AddressbookGroupWasCreated and AddressbookGroupWasActivated notifications on the first save or a AddressbookGroupDidChange notification on subsequent saves, regardless of whether the contact has been saved to persistent storage or not. A CFGManagerSaveFailed notification is posted if saving to the persistent configuration storage fails. """ self._internal_save(originator=Local) def delete(self): """Remove the group from the persistent storage.""" self._internal_delete(originator=Local) def clone(self, new_id=None): """Create a copy of this group and all its sub-settings.""" raise NotImplementedError @classmethod def register_extension(cls, extension): """ Register an extension for this class. All Settings and SettingsGroups defined in the extension will be added to this class, overwriting any attributes with the same name. Other attributes in the extension are ignored. """ if not issubclass(extension, GroupExtension): raise TypeError("expected subclass of GroupExtension, got %r" % (extension,)) for name in dir(extension): attribute = getattr(extension, name, None) if isinstance(attribute, SharedSetting): if SharedSetting.__namespace__ is None: raise RuntimeError("cannot use SharedSetting attributes without first calling SharedSetting.set_namespace") XCAPGroup.__attributes__.add(name) if isinstance(attribute, (AbstractSetting, SettingsGroupMeta)): setattr(cls, name, attribute) class GroupExtension(object): """Base class for extensions of Groups""" def __new__(cls, *args, **kw): raise TypeError("GroupExtension subclasses cannot be instantiated") class ContactURI(SettingsState): __id__ = SettingsObjectImmutableID(type=ID) id = __id__ uri = Setting(type=unicode, default='') type = Setting(type=unicode, default=None, nillable=True) def __new__(cls, id=None, **state): if id is None: id = unique_id() elif not isinstance(id, basestring): raise TypeError("id needs to be a string or unicode object") instance = SettingsState.__new__(cls) instance.__id__ = id instance.__setstate__(state) return instance def __repr__(self): return "%s(id=%r)" % (self.__class__.__name__, self.id) def __toxcap__(self): attributes = dict((name, getattr(self, name)) for name, attr in vars(self.__class__).iteritems() if isinstance(attr, SharedSetting)) return XCAPContactURI(self.id, self.uri, self.type, **attributes) @classmethod def register_extension(cls, extension): """ Register an extension for this class. All Settings and SettingsGroups defined in the extension will be added to this class, overwriting any attributes with the same name. Other attributes in the extension are ignored. """ if not issubclass(extension, ContactURIExtension): raise TypeError("expected subclass of ContactURIExtension, got %r" % (extension,)) for name in dir(extension): attribute = getattr(extension, name, None) if isinstance(attribute, SharedSetting): if SharedSetting.__namespace__ is None: raise RuntimeError("cannot use SharedSetting attributes without first calling SharedSetting.set_namespace") XCAPContactURI.__attributes__.add(name) if isinstance(attribute, (AbstractSetting, SettingsGroupMeta)): setattr(cls, name, attribute) class ContactURIExtension(object): """Base class for extensions of ContactURIs""" def __new__(cls, *args, **kw): raise TypeError("ContactURIExtension subclasses cannot be instantiated") class DefaultContactURI(Setting): def __init__(self): super(DefaultContactURI, self).__init__(type=str, default=None, nillable=True) def __get__(self, obj, objtype): value = super(DefaultContactURI, self).__get__(obj, objtype) return value if value in (self, None) else obj._item_map.get(value) def __set__(self, obj, value): if value is not None: if not isinstance(value, ContactURI): raise TypeError("the default URI must be a ContactURI instance or None") with obj._lock: if value.id not in obj._item_map: raise ValueError("the default URI can only be set to one of the URIs of the contact") super(DefaultContactURI, self).__set__(obj, value.id) else: super(DefaultContactURI, self).__set__(obj, None) def get_modified(self, obj): modified_value = super(DefaultContactURI, self).get_modified(obj) if modified_value is not None: old_uri = obj._item_map.old.get(modified_value.old) if modified_value.old is not None else None new_uri = obj._item_map.get(modified_value.new) if modified_value.new is not None else None modified_value = ModifiedValue(old=old_uri, new=new_uri) return modified_value def get_old(self, obj): value = super(DefaultContactURI, self).get_old(obj) return value if value is None else obj._item_map.old.get(value) class ContactURIManagement(ItemManagement): def remove_item(self, item, collection): if collection.default is item: collection.default = None def set_items(self, items, collection): if collection.default is not None and collection.default not in items: collection.default = None class ContactURIList(ItemCollection): _item_type = ContactURI _item_management = ContactURIManagement() default = DefaultContactURI() class DialogSettings(SettingsGroup): policy = Setting(type=PolicyValue, default='default') subscribe = Setting(type=bool, default=False) class PresenceSettings(SettingsGroup): policy = Setting(type=PolicyValue, default='default') subscribe = Setting(type=bool, default=False) class Contact(SettingsState): __key__ = AddressbookKey('Contacts') __id__ = SettingsObjectImmutableID(type=ID) id = __id__ name = Setting(type=unicode, default='') uris = ContactURIList dialog = DialogSettings presence = PresenceSettings def __new__(cls, id=None): with AddressbookManager.load.lock: if not AddressbookManager.load.called: raise RuntimeError("cannot instantiate %s before calling AddressbookManager.load" % cls.__name__) if id is None: id = unique_id() elif not isinstance(id, basestring): raise TypeError("id needs to be a string or unicode object") instance = SettingsState.__new__(cls) instance.__id__ = id instance.__state__ = 'new' instance.__xcapcontact__ = None configuration = ConfigurationManager() try: data = configuration.get(instance.__key__) except ObjectNotFoundError: pass else: instance.__setstate__(data) instance.__state__ = 'loaded' instance.__xcapcontact__ = instance.__toxcap__() return instance def __establish__(self): if self.__state__ == 'loaded': self.__state__ = 'active' notification_center = NotificationCenter() notification_center.post_notification('AddressbookContactWasActivated', sender=self) def __repr__(self): return "%s(id=%r)" % (self.__class__.__name__, self.id) def __toxcap__(self): contact_uris = xcap.ContactURIList((uri.__toxcap__() for uri in self.uris), default=self.uris.default.id if self.uris.default is not None else None) dialog_handling = xcap.EventHandling(self.dialog.policy, self.dialog.subscribe) presence_handling = xcap.EventHandling(self.presence.policy, self.presence.subscribe) attributes = dict((name, getattr(self, name)) for name, attr in vars(self.__class__).iteritems() if isinstance(attr, SharedSetting)) return XCAPContact(self.id, self.name, contact_uris, presence_handling, dialog_handling, **attributes) @run_in_thread('file-io') def _internal_save(self, originator): if self.__state__ == 'deleted': return modified_settings = self.get_modified() if not modified_settings and self.__state__ != 'new': return account_manager = AccountManager() configuration = ConfigurationManager() notification_center = NotificationCenter() if originator is Local: originator_account = None previous_xcapcontact = self.__xcapcontact__ else: originator_account = originator.account previous_xcapcontact = originator.xcap_object xcap_accounts = [account for account in account_manager.get_accounts() if account.xcap.discovered] self.__xcapcontact__ = self.__toxcap__() if self.__state__ == 'new': configuration.update(self.__key__, self.__getstate__()) self.__state__ = 'active' for account in (account for account in xcap_accounts if account is not originator_account): account.xcap_manager.add_contact(self.__xcapcontact__) modified_data = None notification_center.post_notification('AddressbookContactWasActivated', sender=self) notification_center.post_notification('AddressbookContactWasCreated', sender=self) elif all(isinstance(self.__settings__[key], RuntimeSetting) for key in modified_settings): notification_center.post_notification('AddressbookContactDidChange', sender=self, data=NotificationData(modified=modified_settings)) return else: configuration.update(self.__key__, self.__getstate__()) contact_attributes = self.__xcapcontact__.get_modified(modified_settings) if 'uris' in modified_settings: xcap_uris = self.__xcapcontact__.uris added_uris = [xcap_uris[uri.id] for uri in modified_settings['uris'].added] removed_uris = [uri.__toxcap__() for uri in modified_settings['uris'].removed] modified_uris = dict((xcap_uris[id], xcap_uris[id].get_modified(changemap)) for id, changemap in modified_settings['uris'].modified.iteritems()) else: added_uris = [] removed_uris = [] modified_uris = {} if self.__xcapcontact__ != previous_xcapcontact: outofsync_accounts = xcap_accounts elif originator is Local: outofsync_accounts = [] else: outofsync_accounts = list(account for account in xcap_accounts if account is not originator_account) with MultiAccountTransaction(outofsync_accounts): for account in outofsync_accounts: xcap_manager = account.xcap_manager for xcapuri in added_uris: xcap_manager.add_contact_uri(self.__xcapcontact__, xcapuri) for xcapuri in removed_uris: xcap_manager.remove_contact_uri(self.__xcapcontact__, xcapuri) for xcapuri, uri_attributes in modified_uris.iteritems(): xcap_manager.update_contact_uri(self.__xcapcontact__, xcapuri, uri_attributes) if contact_attributes: xcap_manager.update_contact(self.__xcapcontact__, contact_attributes) notification_center.post_notification('AddressbookContactDidChange', sender=self, data=NotificationData(modified=modified_settings)) modified_data = modified_settings try: configuration.save() except Exception, e: log.exception() notification_center.post_notification('CFGManagerSaveFailed', sender=configuration, data=NotificationData(object=self, operation='save', modified=modified_data, exception=e)) @run_in_thread('file-io') def _internal_delete(self, originator): if self.__state__ == 'deleted': return self.__state__ = 'deleted' configuration = ConfigurationManager() account_manager = AccountManager() addressbook_manager = AddressbookManager() notification_center = NotificationCenter() if originator is Local: originator_account = None else: originator_account = originator.account configuration.delete(self.__key__) xcap_accounts = [account for account in account_manager.get_accounts() if account.xcap.discovered] with MultiAccountTransaction(xcap_accounts): for group in (group for group in addressbook_manager.get_groups() if self.id in group.contacts): group.contacts.remove(self) group.save() for account in (account for account in xcap_accounts if account is not originator_account): account.xcap_manager.remove_contact(self.__xcapcontact__) notification_center.post_notification('AddressbookContactWasDeleted', sender=self) try: configuration.save() except Exception, e: log.exception() notification_center.post_notification('CFGManagerSaveFailed', sender=configuration, data=NotificationData(object=self, operation='delete', exception=e)) def save(self): """ Store the contact into persistent storage (local and xcap). This method will post the AddressbookContactWasCreated and AddressbookContactWasActivated notifications on the first save or a AddressbookContactDidChange notification on subsequent saves, regardless of whether the contact has been saved to persistent storage or not. A CFGManagerSaveFailed notification is posted if saving to the persistent configuration storage fails. """ self._internal_save(originator=Local) def delete(self): """Remove the contact from the persistent storage.""" self._internal_delete(originator=Local) def clone(self, new_id=None): """Create a copy of this contact and all its sub-settings.""" raise NotImplementedError @classmethod def register_extension(cls, extension): """ Register an extension for this class. All Settings and SettingsGroups defined in the extension will be added to this class, overwriting any attributes with the same name. Other attributes in the extension are ignored. """ if not issubclass(extension, ContactExtension): raise TypeError("expected subclass of ContactExtension, got %r" % (extension,)) for name in dir(extension): attribute = getattr(extension, name, None) if isinstance(attribute, SharedSetting): if SharedSetting.__namespace__ is None: raise RuntimeError("cannot use SharedSetting attributes without first calling SharedSetting.set_namespace") XCAPContact.__attributes__.add(name) if isinstance(attribute, (AbstractSetting, SettingsGroupMeta)): setattr(cls, name, attribute) class ContactExtension(object): """Base class for extensions of Contacts""" def __new__(cls, *args, **kw): raise TypeError("ContactExtension subclasses cannot be instantiated") class Policy(SettingsState): __key__ = AddressbookKey('Policies') __id__ = SettingsObjectImmutableID(type=ID) id = __id__ uri = Setting(type=unicode, default='') name = Setting(type=unicode, default='') dialog = DialogSettings presence = PresenceSettings def __new__(cls, id=None): with AddressbookManager.load.lock: if not AddressbookManager.load.called: raise RuntimeError("cannot instantiate %s before calling AddressbookManager.load" % cls.__name__) if id is None: id = unique_id() elif not isinstance(id, basestring): raise TypeError("id needs to be a string or unicode object") instance = SettingsState.__new__(cls) instance.__id__ = id instance.__state__ = 'new' instance.__xcappolicy__ = None configuration = ConfigurationManager() try: data = configuration.get(instance.__key__) except ObjectNotFoundError: pass else: instance.__setstate__(data) instance.__state__ = 'loaded' instance.__xcappolicy__ = instance.__toxcap__() return instance def __establish__(self): if self.__state__ == 'loaded': self.__state__ = 'active' notification_center = NotificationCenter() notification_center.post_notification('AddressbookPolicyWasActivated', sender=self) def __repr__(self): return "%s(id=%r)" % (self.__class__.__name__, self.id) def __toxcap__(self): dialog_handling = xcap.EventHandling(self.dialog.policy, self.dialog.subscribe) presence_handling = xcap.EventHandling(self.presence.policy, self.presence.subscribe) attributes = dict((name, getattr(self, name)) for name, attr in vars(self.__class__).iteritems() if isinstance(attr, SharedSetting)) return XCAPPolicy(self.id, self.uri, self.name, presence_handling, dialog_handling, **attributes) @run_in_thread('file-io') def _internal_save(self, originator): if self.__state__ == 'deleted': return modified_settings = self.get_modified() if not modified_settings and self.__state__ != 'new': return account_manager = AccountManager() configuration = ConfigurationManager() notification_center = NotificationCenter() if originator is Local: originator_account = None previous_xcappolicy = self.__xcappolicy__ else: originator_account = originator.account previous_xcappolicy = originator.xcap_object xcap_accounts = [account for account in account_manager.get_accounts() if account.xcap.discovered] self.__xcappolicy__ = self.__toxcap__() if self.__state__ == 'new': configuration.update(self.__key__, self.__getstate__()) self.__state__ = 'active' for account in (account for account in xcap_accounts if account is not originator_account): account.xcap_manager.add_policy(self.__xcappolicy__) modified_data = None notification_center.post_notification('AddressbookPolicyWasActivated', sender=self) notification_center.post_notification('AddressbookPolicyWasCreated', sender=self) elif all(isinstance(self.__settings__[key], RuntimeSetting) for key in modified_settings): notification_center.post_notification('AddressbookPolicyDidChange', sender=self, data=NotificationData(modified=modified_settings)) return else: configuration.update(self.__key__, self.__getstate__()) attributes = self.__xcappolicy__.get_modified(modified_settings) if self.__xcappolicy__ != previous_xcappolicy: outofsync_accounts = xcap_accounts elif originator is Local: outofsync_accounts = [] else: outofsync_accounts = list(account for account in xcap_accounts if account is not originator_account) for account in outofsync_accounts: account.xcap_manager.update_policy(self.__xcappolicy__, attributes) notification_center.post_notification('AddressbookPolicyDidChange', sender=self, data=NotificationData(modified=modified_settings)) modified_data = modified_settings try: configuration.save() except Exception, e: log.exception() notification_center.post_notification('CFGManagerSaveFailed', sender=configuration, data=NotificationData(object=self, operation='save', modified=modified_data, exception=e)) @run_in_thread('file-io') def _internal_delete(self, originator): if self.__state__ == 'deleted': return self.__state__ = 'deleted' configuration = ConfigurationManager() account_manager = AccountManager() notification_center = NotificationCenter() if originator is Local: originator_account = None else: originator_account = originator.account configuration.delete(self.__key__) for account in (account for account in account_manager.iter_accounts() if account.xcap.discovered and account is not originator_account): account.xcap_manager.remove_policy(self.__xcappolicy__) notification_center.post_notification('AddressbookPolicyWasDeleted', sender=self) try: configuration.save() except Exception, e: log.exception() notification_center.post_notification('CFGManagerSaveFailed', sender=configuration, data=NotificationData(object=self, operation='delete', exception=e)) def save(self): """ Store the policy into persistent storage (local and xcap). It will post the AddressbookPolicyWasCreated and AddressbookPolicyWasActivated notifications on the first save or a AddressbookPolicyDidChange notification on subsequent saves, regardless of whether the policy has been saved to persistent storage or not. A CFGManagerSaveFailed notification is posted if saving to the persistent configuration storage fails. """ self._internal_save(originator=Local) def delete(self): """Remove the policy from the persistent storage.""" self._internal_delete(originator=Local) def clone(self, new_id=None): """Create a copy of this policy and all its sub-settings.""" raise NotImplementedError @classmethod def register_extension(cls, extension): """ Register an extension for this class. All Settings and SettingsGroups defined in the extension will be added to this class, overwriting any attributes with the same name. Other attributes in the extension are ignored. """ if not issubclass(extension, PolicyExtension): raise TypeError("expected subclass of PolicyExtension, got %r" % (extension,)) for name in dir(extension): attribute = getattr(extension, name, None) if isinstance(attribute, SharedSetting): if SharedSetting.__namespace__ is None: raise RuntimeError("cannot use SharedSetting attributes without first calling SharedSetting.set_namespace") XCAPPolicy.__attributes__.add(name) if isinstance(attribute, (AbstractSetting, SettingsGroupMeta)): setattr(cls, name, attribute) class PolicyExtension(object): """Base class for extensions of Policies""" def __new__(cls, *args, **kw): raise TypeError("PolicyExtension subclasses cannot be instantiated") class AddressbookManager(object): __metaclass__ = Singleton implements(IObserver) def __init__(self): self.contacts = {} self.groups = {} self.policies = {} self.__xcapaddressbook__ = None notification_center = NotificationCenter() notification_center.add_observer(self, name='AddressbookContactWasActivated') notification_center.add_observer(self, name='AddressbookContactWasDeleted') notification_center.add_observer(self, name='AddressbookGroupWasActivated') notification_center.add_observer(self, name='AddressbookGroupWasDeleted') notification_center.add_observer(self, name='AddressbookPolicyWasActivated') notification_center.add_observer(self, name='AddressbookPolicyWasDeleted') notification_center.add_observer(self, name='SIPAccountDidDiscoverXCAPSupport') notification_center.add_observer(self, name='XCAPManagerDidReloadData') @execute_once def load(self): configuration = ConfigurationManager() # temporary workaround to migrate contacts to the new format. to be removed later. -Dan if 'Contacts' in configuration.data or 'ContactGroups' in configuration.data: account_manager = AccountManager() old_data = dict(contacts=configuration.data.pop('Contacts', {}), groups=configuration.data.pop('ContactGroups', {})) if any(account.enabled and account.xcap.enabled and account.xcap.discovered for account in account_manager.get_accounts()): self.__old_data = old_data else: self.__migrate_contacts(old_data) return [Contact(id=id) for id in configuration.get_names(Contact.__key__)] [Group(id=id) for id in configuration.get_names(Group.__key__)] [Policy(id=id) for id in configuration.get_names(Policy.__key__)] def start(self): pass def stop(self): pass def has_contact(self, id): return id in self.contacts def get_contact(self, id): return self.contacts[id] def get_contacts(self): return self.contacts.values() def has_group(self, id): return id in self.groups def get_group(self, id): return self.groups[id] def get_groups(self): return self.groups.values() def has_policy(self, id): return id in self.policies def get_policy(self, id): return self.policies[id] def get_policies(self): return self.policies.values() @classmethod def transaction(cls): account_manager = AccountManager() xcap_accounts = [account for account in account_manager.get_accounts() if account.xcap.discovered] return MultiAccountTransaction(xcap_accounts) def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_AddressbookContactWasActivated(self, notification): contact = notification.sender self.contacts[contact.id] = contact notification.center.post_notification('AddressbookManagerDidAddContact', sender=self, data=NotificationData(contact=contact)) def _NH_AddressbookContactWasDeleted(self, notification): contact = notification.sender del self.contacts[contact.id] notification.center.post_notification('AddressbookManagerDidRemoveContact', sender=self, data=NotificationData(contact=contact)) def _NH_AddressbookGroupWasActivated(self, notification): group = notification.sender self.groups[group.id] = group notification.center.post_notification('AddressbookManagerDidAddGroup', sender=self, data=NotificationData(group=group)) def _NH_AddressbookGroupWasDeleted(self, notification): group = notification.sender del self.groups[group.id] notification.center.post_notification('AddressbookManagerDidRemoveGroup', sender=self, data=NotificationData(group=group)) def _NH_AddressbookPolicyWasActivated(self, notification): policy = notification.sender self.policies[policy.id] = policy notification.center.post_notification('AddressbookManagerDidAddPolicy', sender=self, data=NotificationData(policy=policy)) def _NH_AddressbookPolicyWasDeleted(self, notification): policy = notification.sender del self.policies[policy.id] notification.center.post_notification('AddressbookManagerDidRemovePolicy', sender=self, data=NotificationData(policy=policy)) @run_in_thread('file-io') def _NH_SIPAccountDidDiscoverXCAPSupport(self, notification): xcap_manager = notification.sender.xcap_manager with xcap_manager.transaction(): for contact in self.contacts.values(): xcap_manager.add_contact(contact.__xcapcontact__) for group in self.groups.values(): xcap_manager.add_group(group.__xcapgroup__) for policy in self.policies.values(): xcap_manager.add_policy(policy.__xcappolicy__) @run_in_thread('file-io') def _NH_XCAPManagerDidReloadData(self, notification): if notification.data.addressbook == self.__xcapaddressbook__: return self.__xcapaddressbook__ = notification.data.addressbook xcap_manager = notification.sender xcap_contacts = notification.data.addressbook.contacts xcap_groups = notification.data.addressbook.groups xcap_policies = notification.data.addressbook.policies account_manager = AccountManager() xcap_accounts = [account for account in account_manager.get_accounts() if account.xcap.discovered] # temporary workaround to migrate contacts to the new format. to be removed later. -Dan if hasattr(self, '_AddressbookManager__old_data'): old_data = self.__old_data del self.__old_data if not xcap_contacts and not xcap_groups: self.__migrate_contacts(old_data) return with MultiAccountTransaction(xcap_accounts): # because groups depend on contacts, operation order is add/update contacts, add/update/remove groups & policies, remove contacts -Dan for xcap_contact in xcap_contacts: xcap_contact = XCAPContact.normalize(xcap_contact) try: contact = self.contacts[xcap_contact.id] except KeyError: try: contact = Contact(xcap_contact.id) except DuplicateIDError: log.exception() continue contact.name = xcap_contact.name contact.presence.policy = xcap_contact.presence.policy contact.presence.subscribe = xcap_contact.presence.subscribe contact.dialog.policy = xcap_contact.dialog.policy contact.dialog.subscribe = xcap_contact.dialog.subscribe for name, value in xcap_contact.attributes.iteritems(): setattr(contact, name, value) for xcap_uri in xcap_contact.uris: xcap_uri = XCAPContactURI.normalize(xcap_uri) try: uri = contact.uris[xcap_uri.id] except KeyError: try: uri = ContactURI(xcap_uri.id) except DuplicateIDError: log.exception() continue contact.uris.add(uri) uri.uri = xcap_uri.uri uri.type = xcap_uri.type for name, value in xcap_uri.attributes.iteritems(): setattr(uri, name, value) for uri in (uri for uri in list(contact.uris) if uri.id not in xcap_contact.uris): contact.uris.remove(uri) contact.uris.default = contact.uris.get(xcap_contact.uris.default, None) contact._internal_save(originator=Remote(xcap_manager.account, xcap_contact)) for xcap_group in xcap_groups: xcap_group = XCAPGroup.normalize(xcap_group) try: group = self.groups[xcap_group.id] except KeyError: try: group = Group(xcap_group.id) except DuplicateIDError: log.exception() continue group.name = xcap_group.name for name, value in xcap_group.attributes.iteritems(): setattr(group, name, value) old_contact_ids = set(group.contacts.ids()) new_contact_ids = set(xcap_group.contacts.ids()) for contact in (self.contacts[id] for id in new_contact_ids - old_contact_ids): group.contacts.add(contact) for contact in (group.contacts[id] for id in old_contact_ids - new_contact_ids): group.contacts.remove(contact) group._internal_save(originator=Remote(xcap_manager.account, xcap_group)) for xcap_policy in xcap_policies: xcap_policy = XCAPPolicy.normalize(xcap_policy) try: policy = self.policies[xcap_policy.id] except KeyError: try: policy = Policy(xcap_policy.id) except DuplicateIDError: log.exception() continue policy.uri = xcap_policy.uri policy.name = xcap_policy.name policy.presence.policy = xcap_policy.presence.policy policy.presence.subscribe = xcap_policy.presence.subscribe policy.dialog.policy = xcap_policy.dialog.policy policy.dialog.subscribe = xcap_policy.dialog.subscribe for name, value in xcap_policy.attributes.iteritems(): setattr(policy, name, value) policy._internal_save(originator=Remote(xcap_manager.account, xcap_policy)) originator = Remote(xcap_manager.account, None) for policy in (policy for policy in self.policies.values() if policy.id not in xcap_policies): policy._internal_delete(originator=originator) for group in (group for group in self.groups.values() if group.id not in xcap_groups): group._internal_delete(originator=originator) for contact in (contact for contact in self.contacts.values() if contact.id not in xcap_contacts): contact._internal_delete(originator=originator) def __migrate_contacts(self, old_data): account_manager = AccountManager() xcap_accounts = [account for account in account_manager.get_accounts() if account.xcap.discovered] with MultiAccountTransaction(xcap_accounts): # restore the old contacts and groups old_groups = old_data['groups'] old_contacts = old_data['contacts'] group_idmap = {} for group_id, group_state in old_groups.iteritems(): group_idmap[group_id] = group = Group() for name, value in group_state.iteritems(): try: setattr(group, name, value) except (ValueError, TypeError): pass for account_id, account_contacts in old_contacts.iteritems(): for group_id, contact_map in account_contacts.iteritems(): for uri, contact_data in contact_map.iteritems(): contact = Contact() for name, value in contact_data.iteritems(): try: setattr(contact, name, value) except (ValueError, TypeError): pass contact.uris.add(ContactURI(uri=uri)) contact.save() group = group_idmap.get(group_id, Null) group.contacts.add(contact) for group in group_idmap.itervalues(): group.save() ================================================ FILE: sipsimple/application.py ================================================ """ Implements a high-level application responsible for starting and stopping various sub-systems required to implement a fully featured SIP User Agent application. """ from __future__ import absolute_import __all__ = ["SIPApplication"] import os from application.notification import IObserver, NotificationCenter, NotificationData from application.python import Null from application.python.descriptor import classproperty from application.python.types import Singleton from eventlib import proc from operator import attrgetter from threading import RLock, Thread from twisted.internet import reactor from uuid import uuid4 from xcaplib import client as xcap_client from zope.interface import implements from sipsimple.account import AccountManager from sipsimple.addressbook import AddressbookManager from sipsimple.audio import AudioDevice, RootAudioBridge from sipsimple.configuration import ConfigurationManager from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.core import AudioMixer, Engine from sipsimple.lookup import DNSManager from sipsimple.session import SessionManager from sipsimple.storage import ISIPSimpleStorage, ISIPSimpleApplicationDataStorage from sipsimple.threading import ThreadManager, run_in_thread, run_in_twisted_thread from sipsimple.threading.green import run_in_green_thread from sipsimple.video import VideoDevice class ApplicationAttribute(object): def __init__(self, value): self.value = value def __get__(self, obj, objtype): return self.value def __set__(self, obj, value): self.value = value def __delete__(self, obj): raise AttributeError('cannot delete attribute') class SIPApplication(object): __metaclass__ = Singleton implements(IObserver) storage = ApplicationAttribute(value=None) engine = ApplicationAttribute(value=None) thread = ApplicationAttribute(value=None) state = ApplicationAttribute(value=None) alert_audio_device = ApplicationAttribute(value=None) alert_audio_bridge = ApplicationAttribute(value=None) voice_audio_device = ApplicationAttribute(value=None) voice_audio_bridge = ApplicationAttribute(value=None) video_device = ApplicationAttribute(value=None) _lock = ApplicationAttribute(value=RLock()) _timer = ApplicationAttribute(value=None) _stop_pending = ApplicationAttribute(value=False) running = classproperty(lambda cls: cls.state == 'started') alert_audio_mixer = classproperty(lambda cls: cls.alert_audio_bridge.mixer if cls.alert_audio_bridge else None) voice_audio_mixer = classproperty(lambda cls: cls.voice_audio_bridge.mixer if cls.voice_audio_bridge else None) def start(self, storage): if not ISIPSimpleStorage.providedBy(storage): raise TypeError("storage must implement the ISIPSimpleStorage interface") with self._lock: if self.state is not None: raise RuntimeError("SIPApplication cannot be started from '%s' state" % self.state) self.state = 'starting' self.engine = Engine() self.storage = storage thread_manager = ThreadManager() thread_manager.start() configuration_manager = ConfigurationManager() addressbook_manager = AddressbookManager() account_manager = AccountManager() # load configuration and initialize core try: configuration_manager.start() SIPSimpleSettings() account_manager.load() addressbook_manager.load() except: self.engine = None self.state = None self.storage = None raise # run the reactor thread self.thread = Thread(name='Reactor Thread', target=self._run_reactor) self.thread.start() def stop(self): with self._lock: if self.state in (None, 'stopping', 'stopped'): return elif self.state == 'starting': self._stop_pending = True return self.state = 'stopping' notification_center = NotificationCenter() notification_center.post_notification('SIPApplicationWillEnd', sender=self) self._shutdown_subsystems() def _run_reactor(self): from eventlib.twistedutil import join_reactor; del join_reactor # imported for the side effect of making the twisted reactor green notification_center = NotificationCenter() notification_center.post_notification('SIPApplicationWillStart', sender=self) with self._lock: stop_pending = self._stop_pending if stop_pending: self.state = 'stopping' if stop_pending: notification_center.post_notification('SIPApplicationWillEnd', sender=self) else: self._initialize_core() reactor.run(installSignalHandlers=False) with self._lock: self.state = 'stopped' notification_center.post_notification('SIPApplicationDidEnd', sender=self) def _initialize_core(self): notification_center = NotificationCenter() settings = SIPSimpleSettings() # initialize core options = dict(# general user_agent=settings.user_agent, # SIP detect_sip_loops=True, udp_port=settings.sip.udp_port if 'udp' in settings.sip.transport_list else None, tcp_port=settings.sip.tcp_port if 'tcp' in settings.sip.transport_list else None, tls_port=None, # TLS tls_verify_server=False, tls_ca_file=None, tls_cert_file=None, tls_privkey_file=None, # rtp rtp_port_range=(settings.rtp.port_range.start, settings.rtp.port_range.end), # audio codecs=list(settings.rtp.audio_codec_list), # video video_codecs=list(settings.rtp.video_codec_list), # logging log_level=settings.logs.pjsip_level if settings.logs.trace_pjsip else 0, trace_sip=settings.logs.trace_sip) notification_center.add_observer(self, sender=self.engine) self.engine.start(**options) def _initialize_tls(self): settings = SIPSimpleSettings() account_manager = AccountManager() account = account_manager.default_account if account is not None: try: self.engine.set_tls_options(port=settings.sip.tls_port, verify_server=account.tls.verify_server, ca_file=settings.tls.ca_list.normalized if settings.tls.ca_list else None, cert_file=account.tls.certificate.normalized if account.tls.certificate else None, privkey_file=account.tls.certificate.normalized if account.tls.certificate else None) except Exception, e: notification_center = NotificationCenter() notification_center.post_notification('SIPApplicationFailedToStartTLS', sender=self, data=NotificationData(error=e)) @run_in_green_thread def _initialize_subsystems(self): notification_center = NotificationCenter() with self._lock: stop_pending = self._stop_pending if stop_pending: self.state = 'stopping' if stop_pending: notification_center.post_notification('SIPApplicationWillEnd', sender=self) # stop the subsystems we already started: threads, engine and reactor self.engine.stop() self.engine.join(timeout=5) thread_manager = ThreadManager() thread_manager.stop() reactor.stop() return account_manager = AccountManager() addressbook_manager = AddressbookManager() dns_manager = DNSManager() session_manager = SessionManager() settings = SIPSimpleSettings() xcap_client.DEFAULT_HEADERS = {'User-Agent': settings.user_agent} # initialize TLS self._initialize_tls() # initialize PJSIP internal resolver self.engine.set_nameservers(dns_manager.nameservers) # initialize audio objects alert_device = settings.audio.alert_device if alert_device not in (None, u'system_default') and alert_device not in self.engine.output_devices: alert_device = u'system_default' input_device = settings.audio.input_device if input_device not in (None, u'system_default') and input_device not in self.engine.input_devices: input_device = u'system_default' output_device = settings.audio.output_device if output_device not in (None, u'system_default') and output_device not in self.engine.output_devices: output_device = u'system_default' tail_length = settings.audio.echo_canceller.tail_length if settings.audio.echo_canceller.enabled else 0 voice_mixer = AudioMixer(input_device, output_device, settings.audio.sample_rate, tail_length) voice_mixer.muted = settings.audio.muted self.voice_audio_device = AudioDevice(voice_mixer) self.voice_audio_bridge = RootAudioBridge(voice_mixer) self.voice_audio_bridge.add(self.voice_audio_device) alert_mixer = AudioMixer(None, alert_device, settings.audio.sample_rate, 0) if settings.audio.silent: alert_mixer.output_volume = 0 self.alert_audio_device = AudioDevice(alert_mixer) self.alert_audio_bridge = RootAudioBridge(alert_mixer) self.alert_audio_bridge.add(self.alert_audio_device) settings.audio.input_device = voice_mixer.input_device settings.audio.output_device = voice_mixer.output_device settings.audio.alert_device = alert_mixer.output_device # initialize video self.video_device = VideoDevice(settings.video.device, settings.video.resolution, settings.video.framerate) self.video_device.muted = settings.video.muted settings.video.device = self.video_device.name self.engine.set_video_options(settings.video.resolution, settings.video.framerate, settings.video.max_bitrate) self.engine.set_h264_options(settings.video.h264.profile, settings.video.h264.level) # initialize instance id if not settings.instance_id: settings.instance_id = uuid4().urn # initialize path for ZRTP cache file if ISIPSimpleApplicationDataStorage.providedBy(self.storage): self.engine.zrtp_cache = os.path.join(self.storage.directory, 'zrtp.db') # save settings in case something was modified during startup settings.save() # initialize middleware components dns_manager.start() account_manager.start() addressbook_manager.start() session_manager.start() notification_center.add_observer(self, name='CFGSettingsObjectDidChange') notification_center.add_observer(self, name='DNSNameserversDidChange') notification_center.add_observer(self, name='SystemIPAddressDidChange') notification_center.add_observer(self, name='SystemDidWakeUpFromSleep') with self._lock: self.state = 'started' stop_pending = self._stop_pending notification_center.post_notification('SIPApplicationDidStart', sender=self) if stop_pending: self.stop() @run_in_green_thread def _shutdown_subsystems(self): # cleanup internals if self._timer is not None and self._timer.active(): self._timer.cancel() self._timer = None # shutdown middleware components dns_manager = DNSManager() account_manager = AccountManager() addressbook_manager = AddressbookManager() session_manager = SessionManager() procs = [proc.spawn(dns_manager.stop), proc.spawn(account_manager.stop), proc.spawn(addressbook_manager.stop), proc.spawn(session_manager.stop)] proc.waitall(procs) # stop video device self.video_device.producer.close() # shutdown engine self.engine.stop() self.engine.join(timeout=5) # stop threads thread_manager = ThreadManager() thread_manager.stop() # stop the reactor reactor.stop() def _network_conditions_changed(self): if self.running and self._timer is None: def notify(): if self.running: settings = SIPSimpleSettings() if 'tcp' in settings.sip.transport_list: self.engine.set_tcp_port(None) self.engine.set_tcp_port(settings.sip.tcp_port) if 'tls' in settings.sip.transport_list: self._initialize_tls() notification_center = NotificationCenter() notification_center.post_notification('NetworkConditionsDidChange', sender=self) self._timer = None self._timer = reactor.callLater(5, notify) @run_in_twisted_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_SIPEngineDidStart(self, notification): self._initialize_subsystems() def _NH_SIPEngineDidFail(self, notification): with self._lock: if self.state == 'stopping': return self.state = 'stopping' notification.center.post_notification('SIPApplicationWillEnd', sender=self) # # In theory we need to stop the subsystems here, based on what subsystems are already running according to our state, # but in practice the majority of those subsystems need the engine even to stop and the engine has failed. # # Even the ThreadManager might have threads that try to execute operations on the engine, which could block indefinitely # waiting for an answer that will no longer arrive, thus blocking the ThreadManager stop operation. # # As a result the safest thing to do is to just stop the engine thread and the reactor, which means in this case we # will not cleanup properly (the engine thread should already have ended as a result of the failure, so stopping it # is technically a no-op). # self.engine.stop() self.engine.join(timeout=5) reactor.stop() def _NH_SIPEngineGotException(self, notification): notification.center.post_notification('SIPApplicationGotFatalError', sender=self, data=notification.data) @run_in_thread('device-io') def _NH_CFGSettingsObjectDidChange(self, notification): settings = SIPSimpleSettings() account_manager = AccountManager() if notification.sender is settings: if 'audio.sample_rate' in notification.data.modified: alert_device = settings.audio.alert_device if alert_device not in (None, u'system_default') and alert_device not in self.engine.output_devices: alert_device = u'system_default' input_device = settings.audio.input_device if input_device not in (None, u'system_default') and input_device not in self.engine.input_devices: input_device = u'system_default' output_device = settings.audio.output_device if output_device not in (None, u'system_default') and output_device not in self.engine.output_devices: output_device = u'system_default' tail_length = settings.audio.echo_canceller.tail_length if settings.audio.echo_canceller.enabled else 0 voice_mixer = AudioMixer(input_device, output_device, settings.audio.sample_rate, tail_length) voice_mixer.muted = settings.audio.muted self.voice_audio_device = AudioDevice(voice_mixer) self.voice_audio_bridge = RootAudioBridge(voice_mixer) self.voice_audio_bridge.add(self.voice_audio_device) alert_mixer = AudioMixer(None, alert_device, settings.audio.sample_rate, 0) self.alert_audio_device = AudioDevice(alert_mixer) self.alert_audio_bridge = RootAudioBridge(alert_mixer) self.alert_audio_bridge.add(self.alert_audio_device) if settings.audio.silent: alert_mixer.output_volume = 0 settings.audio.input_device = voice_mixer.input_device settings.audio.output_device = voice_mixer.output_device settings.audio.alert_device = alert_mixer.output_device settings.save() else: if {'audio.input_device', 'audio.output_device', 'audio.alert_device', 'audio.echo_canceller.enabled', 'audio.echo_canceller.tail_length'}.intersection(notification.data.modified): input_device = settings.audio.input_device if input_device not in (None, u'system_default') and input_device not in self.engine.input_devices: input_device = u'system_default' output_device = settings.audio.output_device if output_device not in (None, u'system_default') and output_device not in self.engine.output_devices: output_device = u'system_default' tail_length = settings.audio.echo_canceller.tail_length if settings.audio.echo_canceller.enabled else 0 if (input_device, output_device, tail_length) != attrgetter('input_device', 'output_device', 'ec_tail_length')(self.voice_audio_bridge.mixer): self.voice_audio_bridge.mixer.set_sound_devices(input_device, output_device, tail_length) settings.audio.input_device = self.voice_audio_bridge.mixer.input_device settings.audio.output_device = self.voice_audio_bridge.mixer.output_device settings.save() alert_device = settings.audio.alert_device if alert_device not in (None, u'system_default') and alert_device not in self.engine.output_devices: alert_device = u'system_default' if alert_device != self.alert_audio_bridge.mixer.output_device: self.alert_audio_bridge.mixer.set_sound_devices(None, alert_device, 0) settings.audio.alert_device = self.alert_audio_bridge.mixer.output_device settings.save() if 'audio.muted' in notification.data.modified: self.voice_audio_bridge.mixer.muted = settings.audio.muted if 'audio.silent' in notification.data.modified: if settings.audio.silent: self.alert_audio_bridge.mixer.output_volume = 0 else: self.alert_audio_bridge.mixer.output_volume = 100 if 'video.muted' in notification.data.modified: self.video_device.muted = settings.video.muted if {'video.h264.profile', 'video.h264.level'}.intersection(notification.data.modified): self.engine.set_h264_options(settings.video.h264.profile, settings.video.h264.level) if {'video.device', 'video.resolution', 'video.framerate', 'video.max_bitrate'}.intersection(notification.data.modified): if {'video.device', 'video.resolution', 'video.framerate'}.intersection(notification.data.modified) or settings.video.device != self.video_device.name: self.video_device.set_camera(settings.video.device, settings.video.resolution, settings.video.framerate) settings.video.device = self.video_device.name settings.save() self.engine.set_video_options(settings.video.resolution, settings.video.framerate, settings.video.max_bitrate) if 'user_agent' in notification.data.modified: self.engine.user_agent = settings.user_agent if 'sip.udp_port' in notification.data.modified: self.engine.set_udp_port(settings.sip.udp_port) if 'sip.tcp_port' in notification.data.modified: self.engine.set_tcp_port(settings.sip.tcp_port) if {'sip.tls_port', 'tls.ca_list', 'default_account'}.intersection(notification.data.modified): self._initialize_tls() if 'rtp.port_range' in notification.data.modified: self.engine.rtp_port_range = (settings.rtp.port_range.start, settings.rtp.port_range.end) if 'rtp.audio_codec_list' in notification.data.modified: self.engine.codecs = list(settings.rtp.audio_codec_list) if 'logs.trace_sip' in notification.data.modified: self.engine.trace_sip = settings.logs.trace_sip if {'logs.trace_pjsip', 'logs.pjsip_level'}.intersection(notification.data.modified): self.engine.log_level = settings.logs.pjsip_level if settings.logs.trace_pjsip else 0 elif notification.sender is account_manager.default_account: if {'tls.verify_server', 'tls.certificate'}.intersection(notification.data.modified): self._initialize_tls() @run_in_thread('device-io') def _NH_DefaultAudioDeviceDidChange(self, notification): if None in (self.voice_audio_bridge, self.alert_audio_bridge): return settings = SIPSimpleSettings() current_input_device = self.voice_audio_bridge.mixer.input_device current_output_device = self.voice_audio_bridge.mixer.output_device current_alert_device = self.alert_audio_bridge.mixer.output_device ec_tail_length = self.voice_audio_bridge.mixer.ec_tail_length if notification.data.changed_input and u'system_default' in (current_input_device, settings.audio.input_device): self.voice_audio_bridge.mixer.set_sound_devices(u'system_default', current_output_device, ec_tail_length) if notification.data.changed_output and u'system_default' in (current_output_device, settings.audio.output_device): self.voice_audio_bridge.mixer.set_sound_devices(current_input_device, u'system_default', ec_tail_length) if notification.data.changed_output and u'system_default' in (current_alert_device, settings.audio.alert_device): self.alert_audio_bridge.mixer.set_sound_devices(None, u'system_default', 0) @run_in_thread('device-io') def _NH_AudioDevicesDidChange(self, notification): old_devices = set(notification.data.old_devices) new_devices = set(notification.data.new_devices) removed_devices = old_devices - new_devices if not removed_devices: return input_device = self.voice_audio_bridge.mixer.input_device output_device = self.voice_audio_bridge.mixer.output_device alert_device = self.alert_audio_bridge.mixer.output_device if self.voice_audio_bridge.mixer.real_input_device in removed_devices: input_device = u'system_default' if new_devices else None if self.voice_audio_bridge.mixer.real_output_device in removed_devices: output_device = u'system_default' if new_devices else None if self.alert_audio_bridge.mixer.real_output_device in removed_devices: alert_device = u'system_default' if new_devices else None self.voice_audio_bridge.mixer.set_sound_devices(input_device, output_device, self.voice_audio_bridge.mixer.ec_tail_length) self.alert_audio_bridge.mixer.set_sound_devices(None, alert_device, 0) settings = SIPSimpleSettings() settings.audio.input_device = self.voice_audio_bridge.mixer.input_device settings.audio.output_device = self.voice_audio_bridge.mixer.output_device settings.audio.alert_device = self.alert_audio_bridge.mixer.output_device settings.save() @run_in_thread('device-io') def _NH_VideoDevicesDidChange(self, notification): old_devices = set(notification.data.old_devices) new_devices = set(notification.data.new_devices) removed_devices = old_devices - new_devices if not removed_devices: return device = self.video_device.name if self.video_device.real_name in removed_devices: device = u'system_default' if new_devices else None settings = SIPSimpleSettings() self.video_device.set_camera(device, settings.video.resolution, settings.video.framerate) settings.video.device = self.video_device.name settings.save() def _NH_DNSNameserversDidChange(self, notification): if self.running: self.engine.set_nameservers(notification.data.nameservers) notification.center.post_notification('NetworkConditionsDidChange', sender=self) def _NH_SystemIPAddressDidChange(self, notification): self._network_conditions_changed() def _NH_SystemDidWakeUpFromSleep(self, notification): self._network_conditions_changed() ================================================ FILE: sipsimple/audio.py ================================================ """Audio support""" from __future__ import absolute_import __all__ = ['IAudioPort', 'AudioDevice', 'AudioBridge', 'RootAudioBridge', 'AudioConference', 'WavePlayer', 'WavePlayerError', 'WaveRecorder'] import os import weakref from functools import partial from itertools import combinations from threading import RLock from application.notification import IObserver, NotificationCenter, NotificationData, ObserverWeakrefProxy from application.system import makedirs from eventlib import coros from twisted.internet import reactor from zope.interface import Attribute, Interface, implements from sipsimple.core import MixerPort, RecordingWaveFile, SIPCoreError, WaveFile from sipsimple.threading import run_in_twisted_thread from sipsimple.threading.green import Command, run_in_green_thread, run_in_waitable_green_thread class WavePlayerError(Exception): pass class IAudioPort(Interface): """ Interface describing an object which can produce and/or consume audio data. If an object cannot produce audio data, its producer_slot attribute must be None; similarly, if an object cannot consume audio data, its consumer_slot attribute must be None. As part of the interface, whenever an IAudioPort implementation changes its slot attributes, it must send a AudioPortDidChangeSlots notification with the following attributes in the notification data: * consumer_slot_changed * producer_slot_changed * old_consumer_slot (only required if consumer_slot_changed is True) * new_consumer_slot (only required if consumer_slot_changed is True) * old_producer_slot (only required if producer_slot_changed is True) * new_producer_slot (only required if producer_slot_changed is True) All attributes of this interface are read-only. """ mixer = Attribute("The mixer that is responsible for mixing the audio data to/from this audio port") consumer_slot = Attribute("The slot to which audio data can be written") producer_slot = Attribute("The slot from which audio data can be read") class AudioDevice(object): """ Objects of this class represent an audio device which can be used in an AudioBridge as they implement the IAudioPort interface. Since a mixer is connected to an audio device which provides the mixer's clock, an AudioDevice constructed for a specific mixer represents the device that mixer is using. """ implements(IAudioPort) def __init__(self, mixer, input_muted=False, output_muted=False): self.mixer = mixer self.__dict__['input_muted'] = input_muted self.__dict__['output_muted'] = output_muted @property def consumer_slot(self): return 0 if not self.output_muted else None @property def producer_slot(self): return 0 if not self.input_muted else None @property def input_muted(self): return self.__dict__['input_muted'] @input_muted.setter def input_muted(self, value): if not isinstance(value, bool): raise ValueError('illegal value for input_muted property: %r' % (value,)) if value == self.input_muted: return old_producer_slot = self.producer_slot self.__dict__['input_muted'] = value notification_center = NotificationCenter() notification_center.post_notification('AudioPortDidChangeSlots', sender=self, data=NotificationData(consumer_slot_changed=False, producer_slot_changed=True, old_producer_slot=old_producer_slot, new_producer_slot=self.producer_slot)) @property def output_muted(self): return self.__dict__['output_muted'] @output_muted.setter def output_muted(self, value): if not isinstance(value, bool): raise ValueError('illegal value for output_muted property: %r' % (value,)) if value == self.output_muted: return old_consumer_slot = self.consumer_slot self.__dict__['output_muted'] = value notification_center = NotificationCenter() notification_center.post_notification('AudioPortDidChangeSlots', sender=self, data=NotificationData(consumer_slot_changed=True, producer_slot_changed=False, old_consumer_slot=old_consumer_slot, new_consumer_slot=self.consumer_slot)) class AudioBridge(object): """ An AudioBridge is a container for objects providing the IAudioPort interface. It connects all such objects in a full-mesh such that all audio producers are connected to all consumers. AudioBridge implements the IAudioPort interface which means a bridge can contain another bridge. This must be done such that the resulting structure is a tree (i.e. no loops are allowed). All leafs of the tree will be connected as if they were the children of a single bridge. """ implements(IAudioPort, IObserver) def __init__(self, mixer): self._lock = RLock() self.ports = set() self.mixer = mixer self.multiplexer = MixerPort(mixer) self.demultiplexer = MixerPort(mixer) self.multiplexer.start() self.demultiplexer.start() notification_center = NotificationCenter() notification_center.add_observer(ObserverWeakrefProxy(self), name='AudioPortDidChangeSlots') def __del__(self): self.multiplexer.stop() self.demultiplexer.stop() if len(self.ports) >= 2: for port1, port2 in ((wr1(), wr2()) for wr1, wr2 in combinations(self.ports, 2)): if port1 is None or port2 is None: continue if port1.producer_slot is not None and port2.consumer_slot is not None: self.mixer.disconnect_slots(port1.producer_slot, port2.consumer_slot) if port2.producer_slot is not None and port1.consumer_slot is not None: self.mixer.disconnect_slots(port2.producer_slot, port1.consumer_slot) self.ports.clear() def __contains__(self, port): return weakref.ref(port) in self.ports @property def consumer_slot(self): return self.demultiplexer.slot if self.demultiplexer.is_active else None @property def producer_slot(self): return self.multiplexer.slot if self.multiplexer.is_active else None def add(self, port): with self._lock: if not IAudioPort.providedBy(port): raise TypeError("expected object implementing IAudioPort, got %s" % port.__class__.__name__) if port.mixer is not self.mixer: raise ValueError("expected port with Mixer %r, got %r" % (self.mixer, port.mixer)) if weakref.ref(port) in self.ports: return if port.consumer_slot is not None and self.demultiplexer.slot is not None: self.mixer.connect_slots(self.demultiplexer.slot, port.consumer_slot) if port.producer_slot is not None and self.multiplexer.slot is not None: self.mixer.connect_slots(port.producer_slot, self.multiplexer.slot) for other in (wr() for wr in self.ports): if other is None: continue if other.producer_slot is not None and port.consumer_slot is not None: self.mixer.connect_slots(other.producer_slot, port.consumer_slot) if port.producer_slot is not None and other.consumer_slot is not None: self.mixer.connect_slots(port.producer_slot, other.consumer_slot) # This hack is required because a weakly referenced object keeps a # strong reference to weak references of itself and thus to any # callbacks registered in those weak references. To be more # precise, we don't want the port to have a strong reference to # ourselves. -Luci self.ports.add(weakref.ref(port, partial(self._remove_port, weakref.ref(self)))) def remove(self, port): with self._lock: if weakref.ref(port) not in self.ports: raise ValueError("port %r is not part of this bridge" % port) if port.consumer_slot is not None and self.demultiplexer.slot is not None: self.mixer.disconnect_slots(self.demultiplexer.slot, port.consumer_slot) if port.producer_slot is not None and self.multiplexer.slot is not None: self.mixer.disconnect_slots(port.producer_slot, self.multiplexer.slot) for other in (wr() for wr in self.ports): if other is None: continue if other.producer_slot is not None and port.consumer_slot is not None: self.mixer.disconnect_slots(other.producer_slot, port.consumer_slot) if port.producer_slot is not None and other.consumer_slot is not None: self.mixer.disconnect_slots(port.producer_slot, other.consumer_slot) self.ports.remove(weakref.ref(port)) def stop(self): with self._lock: for port1 in (wr() for wr in self.ports): if port1 is None: continue for port2 in (wr() for wr in self.ports): if port2 is None or port2 is port1: continue if port1.producer_slot is not None and port2.consumer_slot is not None: self.mixer.disconnect_slots(port1.producer_slot, port2.consumer_slot) if port2.producer_slot is not None and port1.consumer_slot is not None: self.mixer.disconnect_slots(port2.producer_slot, port1.consumer_slot) self.ports.clear() self.multiplexer.stop() self.demultiplexer.stop() def handle_notification(self, notification): with self._lock: if weakref.ref(notification.sender) not in self.ports: return if notification.data.consumer_slot_changed: if notification.data.old_consumer_slot is not None and self.demultiplexer.slot is not None: self.mixer.disconnect_slots(self.demultiplexer.slot, notification.data.old_consumer_slot) if notification.data.new_consumer_slot is not None and self.demultiplexer.slot is not None: self.mixer.connect_slots(self.demultiplexer.slot, notification.data.new_consumer_slot) for other in (wr() for wr in self.ports): if other is None or other is notification.sender or other.producer_slot is None: continue if notification.data.old_consumer_slot is not None: self.mixer.disconnect_slots(other.producer_slot, notification.data.old_consumer_slot) if notification.data.new_consumer_slot is not None: self.mixer.connect_slots(other.producer_slot, notification.data.new_consumer_slot) if notification.data.producer_slot_changed: if notification.data.old_producer_slot is not None and self.multiplexer.slot is not None: self.mixer.disconnect_slots(notification.data.old_producer_slot, self.multiplexer.slot) if notification.data.new_producer_slot is not None and self.multiplexer.slot is not None: self.mixer.connect_slots(notification.data.new_producer_slot, self.multiplexer.slot) for other in (wr() for wr in self.ports): if other is None or other is notification.sender or other.consumer_slot is None: continue if notification.data.old_producer_slot is not None: self.mixer.disconnect_slots(notification.data.old_producer_slot, other.consumer_slot) if notification.data.new_producer_slot is not None: self.mixer.connect_slots(notification.data.new_producer_slot, other.consumer_slot) @staticmethod def _remove_port(selfwr, portwr): self = selfwr() if self is not None: with self._lock: self.ports.discard(portwr) class RootAudioBridge(object): """ A RootAudioBridge is a container for objects providing the IAudioPort interface. It connects all such objects in a full-mesh such that all audio producers are connected to all consumers. The difference between a RootAudioBridge and an AudioBridge is that the RootAudioBridge does not implement the IAudioPort interface. This makes it more efficient. """ implements(IObserver) def __init__(self, mixer): self.mixer = mixer self.ports = set() self._lock = RLock() notification_center = NotificationCenter() notification_center.add_observer(ObserverWeakrefProxy(self), name='AudioPortDidChangeSlots') def __del__(self): if len(self.ports) >= 2: for port1, port2 in ((wr1(), wr2()) for wr1, wr2 in combinations(self.ports, 2)): if port1 is None or port2 is None: continue if port1.producer_slot is not None and port2.consumer_slot is not None: self.mixer.disconnect_slots(port1.producer_slot, port2.consumer_slot) if port2.producer_slot is not None and port1.consumer_slot is not None: self.mixer.disconnect_slots(port2.producer_slot, port1.consumer_slot) self.ports.clear() def __contains__(self, port): return weakref.ref(port) in self.ports def add(self, port): with self._lock: if not IAudioPort.providedBy(port): raise TypeError("expected object implementing IAudioPort, got %s" % port.__class__.__name__) if port.mixer is not self.mixer: raise ValueError("expected port with Mixer %r, got %r" % (self.mixer, port.mixer)) if weakref.ref(port) in self.ports: return for other in (wr() for wr in self.ports): if other is None: continue if other.producer_slot is not None and port.consumer_slot is not None: self.mixer.connect_slots(other.producer_slot, port.consumer_slot) if port.producer_slot is not None and other.consumer_slot is not None: self.mixer.connect_slots(port.producer_slot, other.consumer_slot) # This hack is required because a weakly referenced object keeps a # strong reference to weak references of itself and thus to any # callbacks registered in those weak references. To be more # precise, we don't want the port to have a strong reference to # ourselves. -Luci self.ports.add(weakref.ref(port, partial(self._remove_port, weakref.ref(self)))) def remove(self, port): with self._lock: if weakref.ref(port) not in self.ports: raise ValueError("port %r is not part of this bridge" % port) for other in (wr() for wr in self.ports): if other is None: continue if other.producer_slot is not None and port.consumer_slot is not None: self.mixer.disconnect_slots(other.producer_slot, port.consumer_slot) if port.producer_slot is not None and other.consumer_slot is not None: self.mixer.disconnect_slots(port.producer_slot, other.consumer_slot) self.ports.remove(weakref.ref(port)) def handle_notification(self, notification): with self._lock: if weakref.ref(notification.sender) not in self.ports: return if notification.data.consumer_slot_changed: for other in (wr() for wr in self.ports): if other is None or other is notification.sender or other.producer_slot is None: continue if notification.data.old_consumer_slot is not None: self.mixer.disconnect_slots(other.producer_slot, notification.data.old_consumer_slot) if notification.data.new_consumer_slot is not None: self.mixer.connect_slots(other.producer_slot, notification.data.new_consumer_slot) if notification.data.producer_slot_changed: for other in (wr() for wr in self.ports): if other is None or other is notification.sender or other.consumer_slot is None: continue if notification.data.old_producer_slot is not None: self.mixer.disconnect_slots(notification.data.old_producer_slot, other.consumer_slot) if notification.data.new_producer_slot is not None: self.mixer.connect_slots(notification.data.new_producer_slot, other.consumer_slot) @staticmethod def _remove_port(selfwr, portwr): self = selfwr() if self is not None: with self._lock: self.ports.discard(portwr) class AudioConference(object): def __init__(self): from sipsimple.application import SIPApplication mixer = SIPApplication.voice_audio_mixer self.bridge = RootAudioBridge(mixer) self.device = AudioDevice(mixer) self.on_hold = False self.streams = [] self._lock = RLock() self.bridge.add(self.device) def add(self, stream): with self._lock: if stream in self.streams: return stream.bridge.remove(stream.device) self.bridge.add(stream.bridge) self.streams.append(stream) def remove(self, stream): with self._lock: self.streams.remove(stream) self.bridge.remove(stream.bridge) stream.bridge.add(stream.device) def hold(self): with self._lock: if self.on_hold: return self.bridge.remove(self.device) self.on_hold = True def unhold(self): with self._lock: if not self.on_hold: return self.bridge.add(self.device) self.on_hold = False class WavePlayer(object): """ An object capable of playing a WAV file. It can be used as part of an AudioBridge as it implements the IAudioPort interface. """ implements(IAudioPort, IObserver) def __init__(self, mixer, filename, volume=100, loop_count=1, pause_time=0, initial_delay=0): self.mixer = mixer self.filename = filename self.initial_delay = initial_delay self.loop_count = loop_count self.pause_time = pause_time self.volume = volume self._channel = None self._current_loop = 0 self._state = 'stopped' self._wave_file = None @property def is_active(self): return self._state == "started" @property def consumer_slot(self): return None @property def producer_slot(self): return self._wave_file.slot if self._wave_file else None def start(self): self.play() @run_in_green_thread # run stop in a green thread in order to be similar with start/play. this avoids start/stop running out of order. def stop(self): if self._state != 'started': return self._channel.send(Command('stop')) @run_in_waitable_green_thread def play(self): if self._state != 'stopped': raise WavePlayerError('already playing') self._state = 'started' self._channel = coros.queue() self._current_loop = 0 if self.initial_delay: reactor.callLater(self.initial_delay, self._channel.send, Command('play')) else: self._channel.send(Command('play')) self._run().wait() @run_in_waitable_green_thread def _run(self): notification_center = NotificationCenter() try: while True: command = self._channel.wait() if command.name == 'play': self._wave_file = WaveFile(self.mixer, self.filename) notification_center.add_observer(self, sender=self._wave_file, name='WaveFileDidFinishPlaying') self._wave_file.volume = self.volume try: self._wave_file.start() except SIPCoreError, e: notification_center.post_notification('WavePlayerDidFail', sender=self, data=NotificationData(error=e)) raise WavePlayerError(e) else: if self._current_loop == 0: notification_center.post_notification('WavePlayerDidStart', sender=self) notification_center.post_notification('AudioPortDidChangeSlots', sender=self, data=NotificationData(consumer_slot_changed=False, producer_slot_changed=True, old_producer_slot=None, new_producer_slot=self._wave_file.slot)) elif command.name == 'reschedule': self._current_loop += 1 notification_center.remove_observer(self, sender=self._wave_file, name='WaveFileDidFinishPlaying') self._wave_file = None notification_center.post_notification('AudioPortDidChangeSlots', sender=self, data=NotificationData(consumer_slot_changed=False, producer_slot_changed=True, old_producer_slot=None, new_producer_slot=None)) if self.loop_count == 0 or self._current_loop < self.loop_count: reactor.callLater(self.pause_time, self._channel.send, Command('play')) else: notification_center.post_notification('WavePlayerDidEnd', sender=self) break elif command.name == 'stop': if self._wave_file is not None: notification_center.remove_observer(self, sender=self._wave_file, name='WaveFileDidFinishPlaying') self._wave_file.stop() self._wave_file = None notification_center.post_notification('AudioPortDidChangeSlots', sender=self, data=NotificationData(consumer_slot_changed=False, producer_slot_changed=True, old_producer_slot=None, new_producer_slot=None)) notification_center.post_notification('WavePlayerDidEnd', sender=self) break finally: self._channel = None self._state = 'stopped' @run_in_twisted_thread def handle_notification(self, notification): if self._channel is not None: self._channel.send(Command('reschedule')) class WaveRecorder(object): """ An object capable of recording to a WAV file. It can be used as part of an AudioBridge as it implements the IAudioPort interface. """ implements(IAudioPort) def __init__(self, mixer, filename): self.mixer = mixer self.filename = filename self._recording_wave_file = None @property def is_active(self): return bool(self._recording_wave_file and self._recording_wave_file.is_active) @property def consumer_slot(self): return self._recording_wave_file.slot if self._recording_wave_file else None @property def producer_slot(self): return None def start(self): # There is still a race condition here in that the directory can be removed # before the PJSIP opens the file. There's nothing that can be done about # it as long as PJSIP doesn't accept an already open file descriptor. -Luci makedirs(os.path.dirname(self.filename)) self._recording_wave_file = RecordingWaveFile(self.mixer, self.filename) self._recording_wave_file.start() notification_center = NotificationCenter() notification_center.post_notification('AudioPortDidChangeSlots', sender=self, data=NotificationData(consumer_slot_changed=True, producer_slot_changed=False, old_consumer_slot=None, new_consumer_slot=self._recording_wave_file.slot)) def stop(self): old_slot = self.consumer_slot self._recording_wave_file.stop() self._recording_wave_file = None notification_center = NotificationCenter() notification_center.post_notification('AudioPortDidChangeSlots', sender=self, data=NotificationData(consumer_slot_changed=True, producer_slot_changed=False, old_consumer_slot=old_slot, new_consumer_slot=None)) ================================================ FILE: sipsimple/configuration/__init__.py ================================================ """Generic configuration management""" __all__ = ['ConfigurationManager', 'ConfigurationError', 'ObjectNotFoundError', 'DuplicateIDError', 'SettingsObjectID', 'SettingsObjectImmutableID', 'AbstractSetting', 'Setting', 'CorrelatedSetting', 'RuntimeSetting', 'SettingsStateMeta', 'SettingsState', 'SettingsGroup', 'ItemCollection', 'SettingsObject', 'SettingsObjectExtension', 'DefaultValue', 'ModifiedValue', 'ModifiedList', 'PersistentKey', 'ItemContainer', 'ItemManagement'] from abc import ABCMeta, abstractmethod from itertools import chain from operator import attrgetter from threading import Lock from weakref import WeakSet from application.notification import NotificationCenter, NotificationData from application.python.descriptor import isdescriptor from application.python.types import Singleton from application.python.weakref import weakobjectmap from sipsimple import log from sipsimple.threading import run_in_thread ## Exceptions class ConfigurationError(Exception): pass class ObjectNotFoundError(ConfigurationError): pass class DuplicateIDError(ValueError): pass ## Structure markers class PersistentKey(unicode): def __repr__(self): return "%s(%s)" % (self.__class__.__name__, unicode.__repr__(self)) class ItemContainer(dict): def __repr__(self): return "%s(%s)" % (self.__class__.__name__, dict.__repr__(self)) ## ConfigurationManager class ConfigurationManager(object): """ Singleton class used for storing and retrieving options, organized in sections. A section contains a list of objects, each with an assigned name which allows access to the object. """ __metaclass__ = Singleton def __init__(self): self.backend = None self.data = None def start(self): """ Initialize the ConfigurationManager to use the specified backend. This method can only be called once, with an object which provides IBackend. The other methods of the object cannot be used unless this method was called. """ from sipsimple.application import SIPApplication from sipsimple.configuration.backend import IConfigurationBackend if self.backend is not None: raise RuntimeError("ConfigurationManager already started") if SIPApplication.storage is None: raise RuntimeError("SIPApplication.storage must be defined before starting the ConfigurationManager") backend = SIPApplication.storage.configuration_backend if not IConfigurationBackend.providedBy(backend): raise TypeError("SIPApplication.storage.configuration_backend must implement the IConfigurationBackend interface") self.data = backend.load() self.backend = backend def update(self, key, data): """ Save the object's data under the tree path specified by key (a list of strings). Cannot be called before start(). """ if self.backend is None: raise RuntimeError("ConfigurationManager cannot be used unless started") if not key: raise KeyError("key cannot be empty") self._update(self.data, list(key), data) def rename(self, old_key, new_key): """ Rename the object identified by old_key to new_key (list of strings). Cannot be called before start(). """ if self.backend is None: raise RuntimeError("ConfigurationManager cannot be used unless started") if not old_key or not new_key: raise KeyError("old_key and/or new_key cannot be empty") try: data = self._pop(self.data, list(old_key)) except KeyError: raise ObjectNotFoundError("object %s does not exist" % '/'.join(old_key)) self._insert(self.data, list(new_key), data) def delete(self, key): """ Delete the object in the tree path specified by key (list of strings). Cannot be called before start(). """ if self.backend is None: raise RuntimeError("ConfigurationManager cannot be used unless started") if not key: raise KeyError("key cannot be empty") try: self._pop(self.data, list(key)) except KeyError: pass def get(self, key): """ Get the object in the tree path specified by key (list of strings). Raises ObjectNotFoundError if the object does not exist. Cannot be called before start(). """ if self.backend is None: raise RuntimeError("ConfigurationManager cannot be used unless started") if not key: raise KeyError("key cannot be empty") try: return self._get(self.data, list(key)) except KeyError: raise ObjectNotFoundError("object %s does not exist" % '/'.join(key)) def get_names(self, key): """ Get all the names under the specified key (a list of strings). Returns a list containing the names. Cannot be called before start(). """ if self.backend is None: raise RuntimeError("ConfigurationManager cannot be used unless started") if not key: raise KeyError("key cannot be empty") try: data = self._get(self.data, list(key)) return data.keys() except KeyError: return [] def save(self): """ Flush the modified objects. Cannot be called before start(). """ if self.backend is None: raise RuntimeError("ConfigurationManager cannot be used unless started") self.backend.save(self.data) def _get(self, data_tree, key): subtree_key = key.pop(0) data_subtree = data_tree[subtree_key] if key: return self._get(data_subtree, key) else: return data_subtree def _insert(self, data_tree, key, data): subtree_key = key.pop(0) data_subtree = data_tree.setdefault(subtree_key, {}) if key: self._insert(data_subtree, key, data) else: data_subtree.update(data) def _pop(self, data_tree, key): subtree_key = key.pop(0) data_subtree = data_tree[subtree_key] if key: data = self._pop(data_subtree, key) if not isinstance(subtree_key, PersistentKey) and not data_subtree: del data_tree[subtree_key] return data else: return data_tree.pop(subtree_key) def _update(self, data_tree, key, data): subtree_key = key.pop(0) data_subtree = data_tree.setdefault(subtree_key, {}) if key: self._update(data_subtree, key, data) else: self._update_dict(data_subtree, data) if not isinstance(subtree_key, PersistentKey) and not data_subtree: del data_tree[subtree_key] def _update_dict(self, old_data, new_data): for key, value in new_data.iteritems(): if value is DefaultValue: old_data.pop(key, None) elif isinstance(value, ItemContainer): if key not in old_data or type(old_data[key]) is not dict: old_data[key] = {} key_subtree = old_data[key] for removed_key in set(key_subtree) - set(value): del key_subtree[removed_key] self._update_dict(key_subtree, value) if not key_subtree: del old_data[key] elif type(value) is dict: if key in old_data and type(old_data[key]) is not dict: del old_data[key] self._update_dict(old_data.setdefault(key, {}), value) if not old_data[key]: del old_data[key] else: old_data[key] = value # Descriptors and base classes used for representing configuration settings class DefaultValue(object): """ This object can be set as the value for a setting and it will reset the setting to the default value. """ class ModifiedValue(object): """ Instances of this class represent the state (the old and new values) of settings. """ __slots__ = ('old', 'new') def __init__(self, old, new): self.old = old self.new = new def __repr__(self): return '%s(old=%r, new=%r)' % (self.__class__.__name__, self.old, self.new) class ModifiedList(object): """ Represents the modified state (added, removed, modified) of list like settings. """ __slots__ = ('added', 'removed', 'modified') def __init__(self, added, removed, modified): self.added = added self.removed = removed self.modified = modified def __repr__(self): return '%s(added=%r, removed=%r, modified=%r)' % (self.__class__.__name__, self.added, self.removed, self.modified) class SettingsObjectID(object): """ Descriptor for dynamic configuration object IDs. """ def __init__(self, type): self.type = type self.values = weakobjectmap() self.oldvalues = weakobjectmap() self.dirty = weakobjectmap() self.lock = Lock() def __get__(self, obj, objtype): return self if obj is None else self.values[obj] def __set__(self, obj, value): with self.lock: if not isinstance(value, self.type): value = self.type(value) if obj in self.values and self.values[obj] == value: return if obj in self.oldvalues and self.oldvalues[obj] == value: self.values[obj] = self.oldvalues[obj] self.dirty[obj] = False return try: other_obj = (key for key, val in chain(self.values.iteritems(), self.oldvalues.iteritems()) if val==value).next() except StopIteration: pass else: raise DuplicateIDError('SettingsObject ID already used by another %s' % other_obj.__class__.__name__) if obj in self.values: self.values[obj] = value self.dirty[obj] = True else: self.values[obj] = self.oldvalues[obj] = value self.dirty[obj] = False def __delete__(self, obj): raise AttributeError('cannot delete attribute') def get_modified(self, obj): """ Returns a ModifiedValue instance with references to the old and new values or None if not modified. """ with self.lock: try: if self.dirty.get(obj, False): return ModifiedValue(old=self.oldvalues[obj], new=self.values[obj]) else: return None finally: self.oldvalues[obj] = self.values[obj] self.dirty[obj] = False def get_old(self, obj): return self.oldvalues[obj] def undo(self, obj): with self.lock: self.values[obj] = self.oldvalues[obj] self.dirty[obj] = False class SettingsObjectImmutableID(object): """ Descriptor for immutable runtime allocated configuration object IDs. """ def __init__(self, type): self.type = type self.values = weakobjectmap() self.lock = Lock() def __get__(self, obj, objtype): return self if obj is None else self.values[obj] def __set__(self, obj, value): with self.lock: if obj in self.values: raise AttributeError('attribute is read-only') if not isinstance(value, self.type): value = self.type(value) try: other_obj = (key for key, val in self.values.iteritems() if val==value).next() except StopIteration: pass else: raise DuplicateIDError('SettingsObject ID already used by another %s' % other_obj.__class__.__name__) self.values[obj] = value def __delete__(self, obj): raise AttributeError('cannot delete attribute') class AbstractSetting(object): """Abstract base class for setting type descriptors""" __metaclass__ = ABCMeta @abstractmethod def __get__(self, obj, objtype): raise NotImplementedError @abstractmethod def __set__(self, obj, value): raise NotImplementedError def __delete__(self, obj): raise AttributeError('cannot delete attribute') @abstractmethod def __getstate__(self, obj): raise NotImplementedError @abstractmethod def __setstate__(self, obj, value): raise NotImplementedError @abstractmethod def get_modified(self, obj): raise NotImplementedError @abstractmethod def get_old(self, obj): raise NotImplementedError @abstractmethod def undo(self, obj): raise NotImplementedError class Setting(AbstractSetting): """ Descriptor representing a setting in a configuration object. If a setting is set to the object DefaultValue, it will be reset to the default. Also, only Setting attributes with nillable=True can be assigned the value None. All other values are passed to the type specified. """ def __init__(self, type, default=None, nillable=False): if default is None and not nillable: raise TypeError("default must be specified if object is not nillable") self.type = type self.default = default self.nillable = nillable self.values = weakobjectmap() self.oldvalues = weakobjectmap() self.dirty = weakobjectmap() self.lock = Lock() def __get__(self, obj, objtype): if obj is None: return self return self.values.get(obj, self.default) def __set__(self, obj, value): with self.lock: if value is None and not self.nillable: raise ValueError("setting attribute is not nillable") if value is DefaultValue: if obj in self.values: self.values.pop(obj) self.dirty[obj] = obj in self.oldvalues return if value is not None and not isinstance(value, self.type): value = self.type(value) if obj in self.values and self.values[obj] == value: return self.values[obj] = value self.dirty[obj] = value != self.oldvalues.get(obj, DefaultValue) # if value changes from implicit default (DefaultValue) to explicit default (self.default) we mark it as dirty def __getstate__(self, obj): value = self.values.get(obj, DefaultValue) if value in (None, DefaultValue): pass elif issubclass(self.type, bool): value = u'true' if value else u'false' elif issubclass(self.type, (int, long, basestring)): value = unicode(value) elif hasattr(value, '__getstate__'): value = value.__getstate__() else: value = unicode(value) return value def __setstate__(self, obj, value): with self.lock: if value is None and not self.nillable: raise ValueError("setting attribute is not nillable") if value is None: pass elif issubclass(self.type, bool): if value.lower() in ('true', 'yes', 'on', '1'): value = True elif value.lower() in ('false', 'no', 'off', '0'): value = False else: raise ValueError("invalid boolean value: %s" % (value,)) elif issubclass(self.type, (int, long, basestring)): value = self.type(value) elif hasattr(self.type, '__setstate__'): object = self.type.__new__(self.type) object.__setstate__(value) value = object else: value = self.type(value) self.oldvalues[obj] = self.values[obj] = value self.dirty[obj] = False def get_modified(self, obj): """ Returns a ModifiedValue instance with references to the old and new values or None if not modified. """ with self.lock: try: if self.dirty.get(obj, False): # if the object is dirty because it switched from implicit default to explicit default, ModifiedValue will have old==new==self.default (see __set__) return ModifiedValue(old=self.oldvalues.get(obj, self.default), new=self.values.get(obj, self.default)) else: return None finally: try: self.oldvalues[obj] = self.values[obj] except KeyError: self.oldvalues.pop(obj, None) self.dirty[obj] = False def get_old(self, obj): return self.oldvalues.get(obj, self.default) def undo(self, obj): with self.lock: if obj in self.oldvalues: self.values[obj] = self.oldvalues[obj] else: self.values.pop(obj, None) self.dirty[obj] = False class CorrelatedSetting(Setting): """ Descriptor representing a setting in a configuration object that is correlated with another setting on the same configuration object. Sibling is the name of the sibling setting and validator is a callable that will receive the setting value and the sibling setting value and should raise an exception if the setting value is not acceptable relative to the sibling setting value. If a setting is set to the object DefaultValue, it will be reset to the default. Also, only Setting attributes with nillable=True can be assigned the value None. All other values are passed to the type specified. """ correlation_lock = Lock() def __init__(self, type, sibling, validator, default=None, nillable=False): Setting.__init__(self, type, default, nillable) self.sibling = sibling self.validator = validator def __set__(self, obj, value): with self.correlation_lock: sibling_value = getattr(obj, self.sibling) self.validator(value, sibling_value) Setting.__set__(self, obj, value) class RuntimeSetting(Setting): """ Descriptor representing a runtime setting in a configuration object. Unlike the standard setting, the runtime setting is never written to nor read from the configuration file. It resides in memory only and its lifetime ends when the application exits. """ def __getstate__(self, obj): return DefaultValue def __setstate__(self, obj, value): pass class SettingsIndexer(object): __slots__ = ('__object__',) def __init__(self, object): self.__object__ = object def __getitem__(self, key): return reduce(getattr, key.split('.'), self.__object__.__class__) class SettingsStateMeta(type): __established__ = WeakSet() def __call__(cls, *args, **kw): instance = super(SettingsStateMeta, cls).__call__(*args, **kw) if hasattr(instance, '__establish__') and instance not in cls.__established__: cls.__established__.add(instance) instance.__establish__() return instance class SettingsState(object): """ This class represents configuration objects which can be saved and restored. """ __metaclass__ = SettingsStateMeta @property def __settings__(self): return SettingsIndexer(self) def get_modified(self): """ Returns a dictionary containing the settings which have been changed. The keys are the full paths to the attributes (from this object), which are mapped to a ModifiedValue instance with references to the old and new values. """ modified = {} for name in dir(self.__class__): attribute = getattr(self.__class__, name, None) if isinstance(attribute, SettingsGroupMeta): modified_settings = getattr(self, name).get_modified() modified.update(dict((name+'.'+k if k else name, v) for k,v in modified_settings.iteritems())) elif isinstance(attribute, AbstractSetting): modified_value = attribute.get_modified(self) if modified_value is not None: modified[name] = modified_value return modified def clone(self): """ Create a copy of this object and all its sub settings. """ raise NotImplementedError def update(self, object): """ Update the settings and sub-settings of this settings object using the ones in the specified object. """ raise NotImplementedError def __getstate__(self): state = {} for name in dir(self.__class__): attribute = getattr(self.__class__, name, None) if isinstance(attribute, SettingsGroupMeta): state[name] = getattr(self, name).__getstate__() elif isinstance(attribute, AbstractSetting): state[name] = attribute.__getstate__(self) return state def __setstate__(self, state): configuration_manager = ConfigurationManager() notification_center = NotificationCenter() for name, value in state.iteritems(): attribute = getattr(self.__class__, name, None) if isinstance(attribute, SettingsGroupMeta): group = getattr(self, name) try: group.__setstate__(value) except ValueError, e: notification_center.post_notification('CFGManagerLoadFailed', sender=configuration_manager, data=NotificationData(attribute=name, container=self, error=e)) elif isinstance(attribute, AbstractSetting): try: attribute.__setstate__(self, value) except ValueError, e: notification_center.post_notification('CFGManagerLoadFailed', sender=configuration_manager, data=NotificationData(attribute=name, container=self, error=e)) class SettingsGroupMeta(SettingsStateMeta): """ Metaclass for SettingsGroup and its subclasses which allows them to be used as descriptor instances. """ def __init__(cls, name, bases, dct): super(SettingsGroupMeta, cls).__init__(name, bases, dct) cls.values = weakobjectmap() def __get__(cls, obj, objtype): if obj is None: return cls try: return cls.values[obj] except KeyError: return cls.values.setdefault(obj, cls()) def __set__(cls, obj, value): raise AttributeError("cannot overwrite group of settings") def __delete__(self, obj): raise AttributeError('cannot delete group of settings') class SettingsGroup(SettingsState): """ Base class for settings groups, i.e. non-leaf and non-root nodes in the configuration tree. All SettingsGroup subclasses are descriptor instances which return an instance of the subclass type when accessed. All SettingsGroup instances are created without passing any arguments to the constructor. class ContainedGroup(SettingsGroup): pass class ContainingGroup(SettingsGroup): subgroup = ContainedGroup """ __metaclass__ = SettingsGroupMeta class ItemMap(dict): def __init__(self, *args, **kw): super(ItemMap, self).__init__(*args, **kw) self.old = dict(self) def get_modified(self): new_ids = set(self) old_ids = set(self.old) added_items = [self[id] for id in new_ids - old_ids] removed_items = [self.old[id] for id in old_ids - new_ids] modified_items = dict((id, modified) for id, modified in ((id, self[id].get_modified()) for id in new_ids & old_ids) if modified) for item in added_items: item.get_modified() # reset the dirty flag of the added items and sync their old and new values if added_items or removed_items or modified_items: self.old = dict(self) return ModifiedList(added=added_items, removed=removed_items, modified=modified_items) else: return None class ItemManagement(object): def add_item(self, item, collection): pass def remove_item(self, item, collection): pass def set_items(self, items, collection): pass class ItemCollectionMeta(SettingsGroupMeta): def __init__(cls, name, bases, dct): if cls._item_type is not None and not issubclass(cls._item_type, SettingsState): raise TypeError('_item_type must be a subclass of SettingsState') if cls._item_type is not None and not isinstance(getattr(cls._item_type, 'id', None), (SettingsObjectID, SettingsObjectImmutableID)): raise ValueError('the type in _item_type must have an id attribute of type SettingsObjectID or SettingsObjectImmutableID') if not isinstance(cls._item_management, ItemManagement): raise TypeError('_item_management must be an instance of a subclass of ItemManagement') super(ItemCollectionMeta, cls).__init__(name, bases, dct) def __set__(cls, obj, items): if not all(isinstance(item, cls._item_type) for item in items): raise TypeError("items must be instances of %s" % cls._item_type.__name__) if set(item.id for item in items).intersection(name for name in dir(cls) if isinstance(getattr(cls, name, None), (SettingsGroupMeta, AbstractSetting))): raise ValueError("item IDs cannot overlap with static setting names") collection = cls.__get__(obj, obj.__class__) with collection._lock: collection._item_management.set_items(items, collection) collection._item_map.clear() collection._item_map.update((item.id, item) for item in items) def __delete__(self, obj): raise AttributeError('cannot delete item collection') class ItemCollection(SettingsGroup): """A SettingsGroup that also contains a dynamic collection of sub-setting""" __metaclass__ = ItemCollectionMeta _item_type = None _item_management = ItemManagement() def __init__(self): self._item_map = ItemMap() self._lock = Lock() def __getitem__(self, key): return self._item_map[key] def __contains__(self, key): return key in self._item_map def __iter__(self): return iter(sorted(self._item_map.values(), key=attrgetter('id'))) def __reversed__(self): return iter(sorted(self._item_map.values(), key=attrgetter('id'), reverse=True)) __hash__ = None def __len__(self): return len(self._item_map) def __getstate__(self): with self._lock: state = ItemContainer((id, item.__getstate__()) for id, item in self._item_map.iteritems()) state.update(super(ItemCollection, self).__getstate__()) return state def __setstate__(self, state): with self._lock: super(ItemCollection, self).__setstate__(state) setting_names = set(name for name in dir(self.__class__) if isinstance(getattr(self.__class__, name, None), (SettingsGroupMeta, AbstractSetting))) self._item_map = ItemMap((id, self._item_type(id, **item_state)) for id, item_state in state.iteritems() if id not in setting_names) def get_modified(self): with self._lock: modified_settings = super(ItemCollection, self).get_modified() modified_items = self._item_map.get_modified() if modified_items is not None: modified_settings[None] = modified_items return modified_settings def ids(self): return sorted(self._item_map.keys()) def get(self, key, default=None): return self._item_map.get(key, default) def add(self, item): if not isinstance(item, self._item_type): raise TypeError("item must be an instances of %s" % self._item_type.__name__) if item.id in set(name for name in dir(self.__class__) if isinstance(getattr(self.__class__, name, None), (SettingsGroupMeta, AbstractSetting))): raise ValueError("item IDs cannot overlap with static setting names") with self._lock: self._item_management.add_item(item, self) self._item_map[item.id] = item def remove(self, item): with self._lock: self._item_management.remove_item(item, self) self._item_map.pop(item.id, None) class ConditionalSingleton(type): """A conditional singleton based on cls.__id__ being static or not""" def __init__(cls, name, bases, dic): super(ConditionalSingleton, cls).__init__(name, bases, dic) cls.__instlock__ = Lock() cls.__instance__ = None def __call__(cls, *args, **kw): if isinstance(cls.__id__, basestring): if args or kw: raise TypeError("cannot have arguments for %s because it is a singleton" % cls.__name__) with cls.__instlock__: if cls.__instance__ is None: cls.__instance__ = super(ConditionalSingleton, cls).__call__(*args, **kw) return cls.__instance__ else: return super(ConditionalSingleton, cls).__call__(*args, **kw) class SettingsObjectMeta(SettingsStateMeta, ConditionalSingleton): """Metaclass to singleton-ize SettingsObject subclasses with static ids""" def __init__(cls, name, bases, dic): if not (cls.__id__ is None or isinstance(cls.__id__, basestring) or isdescriptor(cls.__id__)): raise TypeError("%s.__id__ must be None, a string instance or a descriptor" % name) super(SettingsObjectMeta, cls).__init__(name, bases, dic) class SettingsObject(SettingsState): """ Subclass for top-level configuration objects. These objects are identifiable by either a global id (set in the __id__ attribute of the class) or a local id passed as the sole argument when instantiating SettingsObjects. For SettingsObject subclasses which are meant to be used exclusively with a local id, the class attribute __id__ should be left to the value None; if __init__ is defined, it would have to accept exactly one argument: id. The local id takes precedence over the one specified as a class attribute. Note: __init__ and __new__ will be called not only when a new object is created (i.e. there weren't any settings saved in the configuration), but also when the object is retrieved from the configuration. """ __metaclass__ = SettingsObjectMeta __group__ = None __id__ = None def __new__(cls, id=None): id = id or cls.__id__ if id is None: raise ValueError("id is required for instantiating %s" % cls.__name__) if not isinstance(id, basestring): raise TypeError("id needs to be a string instance") configuration = ConfigurationManager() instance = SettingsState.__new__(cls) instance.__id__ = id instance.__state__ = 'new' try: data = configuration.get(instance.__key__) except ObjectNotFoundError: pass else: instance.__setstate__(data) instance.__state__ = 'loaded' return instance def __establish__(self): if self.__state__ == 'loaded' or self.__instance__ is not None: self.__state__ = 'active' notification_center = NotificationCenter() notification_center.post_notification('CFGSettingsObjectWasActivated', sender=self) @property def __key__(self): if isinstance(self.__class__.__id__, (SettingsObjectID, SettingsObjectImmutableID)): id_key = PersistentKey(self.__id__) else: id_key = unicode(self.__id__) if self.__group__ is not None: return [self.__group__, id_key] else: return [id_key] @property def __oldkey__(self): if isinstance(self.__class__.__id__, SettingsObjectID): id_key = PersistentKey(self.__class__.__id__.get_old(self)) elif isinstance(self.__class__.__id__, SettingsObjectImmutableID): id_key = PersistentKey(self.__id__) else: id_key = unicode(self.__id__) if self.__group__ is not None: return [self.__group__, id_key] else: return [id_key] @run_in_thread('file-io') def save(self): """ Use the ConfigurationManager to store the object under its id in the specified group or top-level otherwise, depending on whether group is None. This method will also post a CFGSettingsObjectDidChange notification, regardless of whether the settings have been saved to persistent storage or not. If the save does fail, a CFGManagerSaveFailed notification is posted as well. """ if self.__state__ == 'deleted': return configuration = ConfigurationManager() notification_center = NotificationCenter() oldkey = self.__oldkey__ # save this here as get_modified will reset it modified_id = self.__class__.__id__.get_modified(self) if isinstance(self.__class__.__id__, SettingsObjectID) else None modified_settings = self.get_modified() if not modified_id and not modified_settings and self.__state__ != 'new': return if self.__state__ == 'new': configuration.update(self.__key__, self.__getstate__()) self.__state__ = 'active' notification_center.post_notification('CFGSettingsObjectWasActivated', sender=self) notification_center.post_notification('CFGSettingsObjectWasCreated', sender=self) modified_data = None elif not modified_id and all(isinstance(self.__settings__[key], RuntimeSetting) for key in modified_settings): notification_center.post_notification('CFGSettingsObjectDidChange', sender=self, data=NotificationData(modified=modified_settings)) return else: if modified_id: configuration.rename(oldkey, self.__key__) if modified_settings: configuration.update(self.__key__, self.__getstate__()) modified_data = modified_settings or {} if modified_id: modified_data['__id__'] = modified_id notification_center.post_notification('CFGSettingsObjectDidChange', sender=self, data=NotificationData(modified=modified_data)) try: configuration.save() except Exception, e: log.exception() notification_center.post_notification('CFGManagerSaveFailed', sender=configuration, data=NotificationData(object=self, operation='save', modified=modified_data, exception=e)) @run_in_thread('file-io') def delete(self): """ Remove this object from the persistent configuration. """ if self.__id__ is self.__class__.__id__: raise TypeError("cannot delete %s instance with default id" % self.__class__.__name__) if self.__state__ == 'deleted': return self.__state__ = 'deleted' configuration = ConfigurationManager() notification_center = NotificationCenter() configuration.delete(self.__oldkey__) # we need the key that wasn't yet saved notification_center.post_notification('CFGSettingsObjectWasDeleted', sender=self) try: configuration.save() except Exception, e: log.exception() notification_center.post_notification('CFGManagerSaveFailed', sender=configuration, data=NotificationData(object=self, operation='delete', exception=e)) def clone(self, new_id): """ Create a copy of this object and all its sub settings. """ raise NotImplementedError @classmethod def register_extension(cls, extension): """ Register an extension of this SettingsObject. All Settings and SettingsGroups defined in the extension will be added to this SettingsObject, overwriting any attributes with the same name. Other attributes in the extension are ignored. """ if not issubclass(extension, SettingsObjectExtension): raise TypeError("expected subclass of SettingsObjectExtension, got %r" % (extension,)) for name in dir(extension): attribute = getattr(extension, name, None) if isinstance(attribute, (AbstractSetting, SettingsGroupMeta)): setattr(cls, name, attribute) class SettingsObjectExtension(object): """ Base class for extensions of SettingsObjects. """ def __new__(self, *args, **kwargs): raise TypeError("SettingsObjectExtension subclasses cannot be instantiated") ================================================ FILE: sipsimple/configuration/backend/__init__.py ================================================ """Base definitions for concrete implementations of configuration backends""" __all__ = ['ConfigurationBackendError', 'IConfigurationBackend'] from zope.interface import Interface class ConfigurationBackendError(Exception): """Base error for use by backends implementing IConfigurationBackend.""" class IConfigurationBackend(Interface): """ Interface describing a backend used for storing and retrieving configuration data. The data kept by the backend is a dictionary whose keys are unicode strings and values are one of four types: (a) a dictionary conforming to this definition; (b) a unicode string; (c) a list of unicode strings; (d) the value None. """ def load(): """ Load the configuration data using whatever means employed by the backend implementation and return a dictionary conforming to the definition in this interface. """ def save(data): """ Given a dictionary conforming to the definition in this interface, save the data using whatever means employed by the backend implementation. """ ================================================ FILE: sipsimple/configuration/backend/file.py ================================================ """Configuration backend for storing settings in a simple plain text format""" __all__ = ["FileParserError", "FileBuilderError", "FileBackend"] import errno import os import re import platform import random from collections import deque from application.system import makedirs, openfile, unlink from zope.interface import implements from sipsimple.configuration.backend import IConfigurationBackend, ConfigurationBackendError class FileParserError(ConfigurationBackendError): """Error raised when the configuration file cannot be parsed.""" class FileBuilderError(ConfigurationBackendError): """Error raised when the configuration data cannot be saved.""" class GroupState(object): """ Internal class used for keeping track of the containing groups while parsing. """ def __init__(self, indentation): self.indentation = indentation self.data = {} class Line(object): """Internal representation of lines in a configuration file""" def __init__(self, indentation, name, separator, value): self.indentation = indentation self.name = name self.separator = separator self.value = value def __repr__(self): return "%s(%r, %r, %r, %r)" % (self.__class__.__name__, self.indentation, self.name, self.separator, self.value) class FileBackend(object): """ Implementation of a configuration backend that stores data in a simple plain text format. """ implements(IConfigurationBackend) escape_characters_re = re.compile(ur"""[,"'=: #\\\t\x0b\x0c\n\r]""") def __init__(self, filename, encoding='utf-8'): """ Initialize the configuration backend with the specified file. The file is not read at this time, but rather each time the load method is called. """ self.filename = filename self.encoding = encoding def load(self): """ Read the file configured with this backend and parse it, returning a dictionary conforming to the IConfigurationBackend specification. """ try: file = open(self.filename) except IOError, e: if e.errno == errno.ENOENT: return {} else: raise ConfigurationBackendError("failed to read configuration file: %s" % str(e)) state_stack = deque() state_stack.appendleft(GroupState(-1)) for lineno, line in enumerate(file, 1): line = self._parse_line(line, lineno) if not line.name: continue # find the container for this declaration while state_stack[0].indentation >= line.indentation: state_stack.popleft() if line.separator == u':': new_group_state = GroupState(line.indentation) state_stack[0].data[line.name] = new_group_state.data state_stack.appendleft(new_group_state) elif line.separator == u'=': state_stack[0].data[line.name] = line.value return state_stack[-1].data def save(self, data): """ Given a dictionary conforming to the IConfigurationBackend specification, write the data to the file configured with this backend in a format suitable to be read back using load(). """ lines = self._build_group(data, 0) config_directory = os.path.dirname(self.filename) tmp_filename = '%s.%d.%08X' % (self.filename, os.getpid(), random.getrandbits(32)) try: if config_directory: makedirs(config_directory) file = openfile(tmp_filename, 'wb', permissions=0600) file.write((os.linesep.join(lines)+os.linesep).encode(self.encoding)) file.close() if platform.system() == 'Windows': # os.rename does not work on Windows if the destination file already exists. # It seems there is no atomic way to do this on Windows. unlink(self.filename) os.rename(tmp_filename, self.filename) except (IOError, OSError), e: raise ConfigurationBackendError("failed to write configuration file: %s" % str(e)) def _parse_line(self, line, lineno): def advance_to_next_token(line): counter = 0 while line and line[0].isspace(): line.popleft() counter += 1 if line and line[0] == u'#': line.clear() return counter def token_iterator(line, delimiter=''): quote_char = None while line: if quote_char is None and line[0] in delimiter: break char = line.popleft() if char in u"'\"": if quote_char is None: quote_char = char continue elif quote_char == char: quote_char = None continue else: yield char elif char == u'\\': if not line: raise FileParserError("unexpected `\\' at end of line %d" % lineno) char = line.popleft() if char == 'n': yield u'\n' elif char == 'r': yield u'\r' else: yield char elif quote_char is None and char == u'#': line.clear() break elif quote_char is None and char.isspace(): break else: yield char if quote_char is not None: raise FileParserError("missing ending quote at line %d" % lineno) line = deque(line.rstrip().decode(self.encoding)) indentation = advance_to_next_token(line) if not line: return Line(indentation, None, None, None) name = u''.join(token_iterator(line, delimiter=u':=')) advance_to_next_token(line) if not line or line[0] not in u':=': raise FileParserError("expected one of `:' or `=' at line %d" % lineno) if not name: raise FileParserError("missing setting/section name at line %d" % lineno) separator = line.popleft() advance_to_next_token(line) if not line: return Line(indentation, name, separator, None) elif separator == u':': raise FileParserError("unexpected characters after `:' at line %d" % lineno) value = None value_list = None while line: value = u''.join(token_iterator(line, delimiter=u',')) advance_to_next_token(line) if line: if line[0] == u',': line.popleft() advance_to_next_token(line) if value_list is None: value_list = [] else: raise FileParserError("unexpected characters after value at line %d" % lineno) if value_list is not None: value_list.append(value) value = value_list if value_list is not None else value return Line(indentation, name, separator, value) def _build_group(self, group, indentation): setting_lines = [] group_lines = [] indent_spaces = u' '*4*indentation for name, data in sorted(group.iteritems()): if data is None: setting_lines.append(u'%s%s =' % (indent_spaces, self._escape(name))) elif type(data) is dict: group_lines.append(u'%s%s:' % (indent_spaces, self._escape(name))) group_lines.extend(self._build_group(data, indentation+1)) group_lines.append(u'') elif type(data) is list: list_value = u', '.join(self._escape(item) for item in data) if len(data) == 1: list_value += u',' setting_lines.append(u'%s%s = %s' % (indent_spaces, self._escape(name), list_value)) elif type(data) is unicode: setting_lines.append(u'%s%s = %s' % (indent_spaces, self._escape(name), self._escape(data))) else: raise FileBuilderError("expected unicode, dict or list object, got %s" % type(data).__name__) return setting_lines + group_lines def _escape(self, value): if value == u'': return u'""' elif self.escape_characters_re.search(value): return u'"%s"' % value.replace(u'\\', u'\\\\').replace(u'"', u'\\"').replace(u'\n', u'\\n').replace(u'\r', u'\\r') else: return value ================================================ FILE: sipsimple/configuration/backend/memory.py ================================================ """Configuration backend for storing settings in memory""" __all__ = ["MemoryBackend"] from zope.interface import implements from sipsimple.configuration.backend import IConfigurationBackend class MemoryBackend(object): """Implementation of a configuration backend that stores data in memory.""" implements(IConfigurationBackend) def __init__(self): self.data = {} def load(self): return self.data def save(self, data): self.data = data ================================================ FILE: sipsimple/configuration/datatypes.py ================================================ """Definitions of datatypes for use in configuration settings""" __all__ = [ # Base datatypes 'List', # Generic datatypes 'ContentType', 'ContentTypeList', 'CountryCode', 'NonNegativeInteger', 'PositiveInteger', 'SIPAddress', # Custom datatypes 'PJSIPLogLevel', # Audio datatypes 'AudioCodecList', 'SampleRate', # Video datatypes 'H264Profile', 'VideoResolution', 'VideoCodecList', # Address and transport datatypes 'Port', 'PortRange', 'Hostname', 'DomainList', 'EndpointAddress', 'EndpointIPAddress', 'MSRPRelayAddress', 'SIPProxyAddress', 'STUNServerAddress', 'STUNServerAddressList', 'XCAPRoot', 'MSRPConnectionModel', 'MSRPTransport', 'SIPTransport', 'SIPTransportList', # SRTP encryption 'SRTPKeyNegotiation', # Path datatypes 'Path'] import locale import os import re import urlparse from operator import itemgetter # Base datatypes class List(object): type = unicode def __init__(self, values=()): self.values = [item if isinstance(item, self.type) else self.type(item) for item in values] def __getstate__(self): state = [] for item in self: if item is None: pass elif issubclass(self.type, bool): item = u'true' if item else u'false' elif issubclass(self.type, (int, long, basestring)): item = unicode(item) elif hasattr(item, '__getstate__'): item = item.__getstate__() if type(item) is not unicode: raise TypeError("Expected unicode type for list member, got %s" % item.__class__.__name__) else: item = unicode(item) state.append(item) return state def __setstate__(self, state): if not isinstance(state, list): state = [state] values = [] for item in state: if item is None: pass elif issubclass(self.type, bool): if item.lower() in ('true', 'yes', 'on', '1'): item = True elif item.lower() in ('false', 'no', 'off', '0'): item = False else: raise ValueError("invalid boolean value: %s" % (item,)) elif issubclass(self.type, (int, long, basestring)): item = self.type(item) elif hasattr(self.type, '__setstate__'): object = self.type.__new__(self.type) object.__setstate__(item) item = object else: item = self.type(item) values.append(item) self.values = values def __add__(self, other): if isinstance(other, List): return self.__class__(self.values + other.values) else: return self.__class__(self.values + other) def __radd__(self, other): if isinstance(other, List): return self.__class__(other.values + self.values) else: return self.__class__(other + self.values) def __mul__(self, other): return self.__class__(self.values * other) def __rmul__(self, other): return self.__class__(other * self.values) def __eq__(self, other): if isinstance(other, List): return self.values == other.values else: return self.values == other def __ne__(self, other): return not self.__eq__(other) __hash__ = None def __iter__(self): return iter(self.values) def __contains__(self, value): return value in self.values def __getitem__(self, key): return self.values[key] def __len__(self): return len(self.values) def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.values) def __str__(self): return ', '.join(str(item) for item in self) def __unicode__(self): return u', '.join(unicode(item) for item in self) # Generic datatypes class ContentType(str): def __new__(cls, value): value = str(value) if value == '*': return value try: type, subtype = value.split('/') except ValueError: raise ValueError("illegal content-type: %s" % value) else: if type == '*': raise ValueError("illegal content-type: %s" % value) return value class ContentTypeList(List): type = ContentType class CountryCode(str): code_pattern = re.compile(r'[1-9][0-9]*') def __new__(cls, value): value = str(value) if cls.code_pattern.match(value) is None: raise ValueError("illegal country code: %s" % value) return value class NonNegativeInteger(int): def __new__(cls, value): value = int(value) if value < 0: raise ValueError("non-negative int expected, found %d" % value) return value class PositiveInteger(int): def __new__(cls, value): value = int(value) if value <= 0: raise ValueError("positive int expected, found %d" % value) return value class SIPAddress(str): def __new__(cls, address): address = str(address) address = address.replace('@', '%40', address.count('@')-1) try: username, domain = address.split('@') Hostname(domain) except ValueError: raise ValueError("illegal SIP address: %s, must be in user@domain format" % address) return super(SIPAddress, cls).__new__(cls, address) username = property(lambda self: self.split('@')[0]) domain = property(lambda self: self.split('@')[1]) # Custom datatypes class PJSIPLogLevel(int): def __new__(cls, value): value = int(value) if not (0 <= value <= 5): raise ValueError("expected an integer number between 0 and 5, found %d" % value) return value class CodecList(List): type = str available_values = None # to be defined in a subclass @property def values(self): return self.__dict__['values'] @values.setter def values(self, values): if not set(values).issubset(self.available_values): raise ValueError("illegal codec values: %s" % ', '.join(values)) self.__dict__['values'] = values # Audio datatypes class AudioCodecList(CodecList): available_values = {'opus', 'speex', 'G722', 'GSM', 'iLBC', 'PCMU', 'PCMA'} class SampleRate(int): valid_values = (16000, 32000, 44100, 48000) def __new__(cls, value): value = int(value) if value not in cls.valid_values: raise ValueError("illegal sample rate: %d" % value) return value # Video datatypes class H264Profile(str): valid_values = ('baseline', 'main', 'high') def __new__(cls, value): if value.lower() not in cls.valid_values: raise ValueError('invalid value, must be one of: {}'.format(', '.join(cls.valid_values))) return super(H264Profile, cls).__new__(cls, value.lower()) class VideoResolution(tuple): width = property(itemgetter(0)) height = property(itemgetter(1)) def __new__(cls, value): if isinstance(value, tuple): width, height = value elif isinstance(value, basestring): width, height = value.split('x') else: raise ValueError('invalid value: %r' % value) return super(VideoResolution, cls).__new__(cls, (int(width), int(height))) def __repr__(self): return '%s(%d, %d)' % (self.__class__.__name__, self.width, self.height) def __str__(self): return '%dx%d' % (self.width, self.height) def __unicode__(self): return u'%dx%d' % (self.width, self.height) class VideoCodecList(CodecList): available_values = {'H264', 'VP8'} # Address and transport datatypes class Port(int): def __new__(cls, value): value = int(value) if not (0 <= value <= 65535): raise ValueError("illegal port value: %s" % value) return value class PortRange(object): def __init__(self, start, end): self.start = Port(start) self.end = Port(end) if self.start == 0: raise ValueError("illegal port value: 0") if self.end == 0: raise ValueError("illegal port value: 0") if self.start > self.end: raise ValueError("illegal port range: start port (%d) cannot be larger than end port (%d)" % (self.start, self.end)) def __getstate__(self): return unicode(self) def __setstate__(self, state): self.__init__(*state.split('-')) def __eq__(self, other): if isinstance(other, PortRange): return self.start == other.start and self.end == other.end else: return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal __hash__ = None def __repr__(self): return '%s(start=%r, end=%r)' % (self.__class__.__name__, self.start, self.end) def __str__(self): return '%d-%d' % (self.start, self.end) def __unicode__(self): return u'%d-%d' % (self.start, self.end) class Hostname(str): _host_re = re.compile(r"^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*)$") def __new__(cls, value): value = str(value) if not cls._host_re.match(value): raise ValueError("illegal hostname or ip address: %s" % value) return value class IPAddress(str): _ip_re = re.compile(r"^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$") def __new__(cls, value): value = str(value) if not cls._ip_re.match(value): raise ValueError("illegal IP address: %s" % value) return value class DomainList(List): type = str _domain_re = re.compile(r"^[a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*$") @property def values(self): return self.__dict__['values'] @values.setter def values(self, values): for value in values: if self._domain_re.match(value) is None: raise ValueError("illegal domain: %s" % value) self.__dict__['values'] = values class EndpointAddress(object): _description_re = re.compile(r"^(?P(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*))(:(?P\d+))?$") default_port = 0 def __init__(self, host, port=None): self.host = Hostname(host) self.port = Port(port if port is not None else self.default_port) if self.port == 0: raise ValueError("illegal port value: 0") def __getstate__(self): return unicode(self) def __setstate__(self, state): match = self._description_re.match(state) if match is None: raise ValueError("illegal endpoint address: %s" % state) self.__init__(**match.groupdict()) def __eq__(self, other): if isinstance(other, EndpointAddress): return self.host == other.host and self.port == other.port else: return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal __hash__ = None def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.host, self.port) def __str__(self): return '%s:%d' % (self.host, self.port) def __unicode__(self): return u'%s:%d' % (self.host, self.port) @classmethod def from_description(cls, description): if not description: return None match = cls._description_re.match(description) if match is None: raise ValueError("illegal endpoint address: %s" % description) return cls(**match.groupdict()) class EndpointIPAddress(EndpointAddress): _description_re = re.compile(r"^(?P\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(:(?P\d+))?$") def __init__(self, host, port=None): self.host = IPAddress(host) self.port = Port(port if port is not None else self.default_port) if self.port == 0: raise ValueError("illegal port value: 0") def __setstate__(self, state): match = self._description_re.match(state) if match is None: raise ValueError("illegal value: %s, must be an IP address" % state) self.__init__(**match.groupdict()) @classmethod def from_description(cls, description): if not description: return None match = cls._description_re.match(description) if match is None: raise ValueError("illegal value: %s, must be an IP address" % description) return cls(**match.groupdict()) class MSRPRelayAddress(object): _description_re = re.compile(r"^(?P(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*))(:(?P\d+))?(;transport=(?P.+))?$") def __init__(self, host, port=2855, transport='tls'): self.host = Hostname(host) self.port = Port(port) self.transport = MSRPTransport(transport) def __getstate__(self): return unicode(self) def __setstate__(self, state): match = self._description_re.match(state) if match is None: raise ValueError("illegal MSRP relay address: %s" % state) self.__init__(**dict((k, v) for k, v in match.groupdict().iteritems() if v is not None)) def __eq__(self, other): if isinstance(other, MSRPRelayAddress): return self.host == other.host and self.port == other.port and self.transport == other.transport else: return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal __hash__ = None def __repr__(self): return '%s(%r, port=%r, transport=%r)' % (self.__class__.__name__, self.host, self.port, self.transport) def __str__(self): return '%s:%d;transport=%s' % (self.host, self.port, self.transport) def __unicode__(self): return u'%s:%d;transport=%s' % (self.host, self.port, self.transport) @classmethod def from_description(cls, description): if not description: return None match = cls._description_re.match(description) if match is None: raise ValueError("illegal MSRP relay address: %s" % description) return cls(**dict((k, v) for k, v in match.groupdict().iteritems() if v is not None)) class SIPProxyAddress(object): _description_re = re.compile(r"^(?P(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*))(:(?P\d+))?(;transport=(?P.+))?$") def __init__(self, host, port=5060, transport='udp'): self.host = Hostname(host) self.port = Port(port) if self.port == 0: raise ValueError("illegal port value: 0") self.transport = SIPTransport(transport) def __getstate__(self): return unicode(self) def __setstate__(self, state): match = self._description_re.match(state) if match is None: raise ValueError("illegal SIP proxy address: %s" % state) self.__init__(**dict((k, v) for k, v in match.groupdict().iteritems() if v is not None)) def __eq__(self, other): if isinstance(other, SIPProxyAddress): return self.host == other.host and self.port == other.port and self.transport == other.transport else: return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal __hash__ = None def __repr__(self): return '%s(%r, port=%r, transport=%r)' % (self.__class__.__name__, self.host, self.port, self.transport) def __str__(self): return '%s:%d;transport=%s' % (self.host, self.port, self.transport) def __unicode__(self): return u'%s:%d;transport=%s' % (self.host, self.port, self.transport) @classmethod def from_description(cls, description): if not description: return None match = cls._description_re.match(description) if match is None: raise ValueError("illegal SIP proxy address: %s" % description) return cls(**dict((k, v) for k, v in match.groupdict().iteritems() if v is not None)) class STUNServerAddress(object): _description_re = re.compile(r"^(?P(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*))(:(?P\d+))?$") default_port = 3478 def __init__(self, host, port=default_port): self.host = Hostname(host) self.port = Port(port) def __getstate__(self): return unicode(self) def __setstate__(self, state): match = self._description_re.match(state) if match is None: raise ValueError("illegal STUN server address: %s" % state) self.__init__(**dict((k, v) for k, v in match.groupdict().iteritems() if v is not None)) def __eq__(self, other): if isinstance(other, STUNServerAddress): return self.host == other.host and self.port == other.port else: return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal __hash__ = None def __repr__(self): return '%s(%r, port=%r)' % (self.__class__.__name__, self.host, self.port) def __str__(self): return '%s:%d' % (self.host, self.port) def __unicode__(self): return u'%s:%d' % (self.host, self.port) @classmethod def from_description(cls, description): if not description: return None match = cls._description_re.match(description) if match is None: raise ValueError("illegal STUN server address: %s" % description) return cls(**dict((k, v) for k, v in match.groupdict().iteritems() if v is not None)) class STUNServerAddressList(List): type = STUNServerAddress class XCAPRoot(str): def __new__(cls, value): value = str(value) uri = urlparse.urlparse(value) if uri.scheme not in (u'http', u'https'): raise ValueError("illegal XCAP root scheme (http and https only): %s" % uri.scheme) if uri.params: raise ValueError("XCAP root must not contain parameters: %s" % (uri.params,)) if uri.query: raise ValueError("XCAP root must not contain query component: %s" % (uri.query,)) if uri.fragment: raise ValueError("XCAP root must not contain fragment component: %s" % (uri.fragment,)) # check port and hostname Hostname(uri.hostname) if uri.port is not None: port = Port(uri.port) if port == 0: raise ValueError("illegal port value: 0") return value class MSRPConnectionModel(str): available_values = ('relay', 'acm') def __new__(cls, value): value = str(value) if value not in cls.available_values: raise ValueError("illegal value for MSRP NAT model: %s" % value) return value class MSRPTransport(str): available_values = ('tls', 'tcp') def __new__(cls, value): value = str(value) if value not in cls.available_values: raise ValueError("illegal value for MSRP transport: %s" % value) return value class SIPTransport(str): available_values = ('udp', 'tcp', 'tls') def __new__(cls, value): value = str(value) if value not in cls.available_values: raise ValueError("illegal value for SIP transport: %s" % value) return value class SIPTransportList(List): type = SIPTransport available_values = SIPTransport.available_values class SRTPKeyNegotiation(str): available_values = ('opportunistic', 'sdes_optional', 'sdes_mandatory', 'zrtp') def __new__(cls, value): value = str(value) if value not in cls.available_values: raise ValueError("illegal value for SRTP key negotiation: %s" % value) return value # Path datatypes class Path(unicode): def __new__(cls, path): return super(Path, cls).__new__(cls, os.path.normpath(path)) @property def normalized(self): if not self.startswith('~'): return self encoding = locale.getpreferredencoding() or 'ascii' return os.path.expanduser(self.encode(encoding)).decode(encoding) ================================================ FILE: sipsimple/configuration/settings.py ================================================ """ SIP SIMPLE settings. Definition of general (non-account related) settings. """ from sipsimple import __version__ from sipsimple.configuration import CorrelatedSetting, RuntimeSetting, Setting, SettingsGroup, SettingsObject from sipsimple.configuration.datatypes import NonNegativeInteger, PJSIPLogLevel from sipsimple.configuration.datatypes import AudioCodecList, SampleRate, VideoCodecList from sipsimple.configuration.datatypes import Port, PortRange, SIPTransportList from sipsimple.configuration.datatypes import Path from sipsimple.configuration.datatypes import H264Profile, VideoResolution __all__ = ['SIPSimpleSettings'] class EchoCancellerSettings(SettingsGroup): enabled = Setting(type=bool, default=True) tail_length = Setting(type=NonNegativeInteger, default=2) class AudioSettings(SettingsGroup): alert_device = Setting(type=unicode, default=u'system_default', nillable=True) input_device = Setting(type=unicode, default=u'system_default', nillable=True) output_device = Setting(type=unicode, default=u'system_default', nillable=True) sample_rate = Setting(type=SampleRate, default=44100) muted = RuntimeSetting(type=bool, default=False) silent = Setting(type=bool, default=False) echo_canceller = EchoCancellerSettings class H264Settings(SettingsGroup): profile = Setting(type=H264Profile, default='baseline') level = Setting(type=str, default='3.1') class VideoSettings(SettingsGroup): device = Setting(type=unicode, default=u'system_default', nillable=True) resolution = Setting(type=VideoResolution, default=VideoResolution('1280x720')) framerate = Setting(type=int, default=25) max_bitrate = Setting(type=float, default=None, nillable=True) muted = RuntimeSetting(type=bool, default=False) h264 = H264Settings class ChatSettings(SettingsGroup): pass class ScreenSharingSettings(SettingsGroup): pass class FileTransferSettings(SettingsGroup): directory = Setting(type=Path, default=Path('~/Downloads')) class LogsSettings(SettingsGroup): trace_msrp = Setting(type=bool, default=False) trace_sip = Setting(type=bool, default=False) trace_pjsip = Setting(type=bool, default=False) pjsip_level = Setting(type=PJSIPLogLevel, default=5) class RTPSettings(SettingsGroup): port_range = Setting(type=PortRange, default=PortRange(50000, 50500)) timeout = Setting(type=NonNegativeInteger, default=30) audio_codec_list = Setting(type=AudioCodecList, default=AudioCodecList(('opus', 'G722', 'PCMU', 'PCMA'))) video_codec_list = Setting(type=VideoCodecList, default=VideoCodecList(('H264', 'VP8'))) def sip_port_validator(port, sibling_port): if port == sibling_port != 0: raise ValueError("the TCP and TLS ports must be different") class SIPSettings(SettingsGroup): invite_timeout = Setting(type=NonNegativeInteger, default=90, nillable=True) udp_port = Setting(type=Port, default=0) tcp_port = CorrelatedSetting(type=Port, sibling='tls_port', validator=sip_port_validator, default=0) tls_port = CorrelatedSetting(type=Port, sibling='tcp_port', validator=sip_port_validator, default=0) transport_list = Setting(type=SIPTransportList, default=SIPTransportList(('tls', 'tcp', 'udp'))) class TLSSettings(SettingsGroup): ca_list = Setting(type=Path, default=None, nillable=True) class SIPSimpleSettings(SettingsObject): __id__ = 'SIPSimpleSettings' default_account = Setting(type=str, default='bonjour@local', nillable=True) user_agent = Setting(type=str, default='sipsimple %s' % __version__) instance_id = Setting(type=str, default='') audio = AudioSettings video = VideoSettings chat = ChatSettings screen_sharing = ScreenSharingSettings file_transfer = FileTransferSettings logs = LogsSettings rtp = RTPSettings sip = SIPSettings tls = TLSSettings ================================================ FILE: sipsimple/core/__init__.py ================================================ from sipsimple.core._core import * from sipsimple.core._engine import * from sipsimple.core._helpers import * from sipsimple.core._primitives import * required_revision = 181 if CORE_REVISION != required_revision: raise ImportError("Wrong SIP core revision %d (expected %d)" % (CORE_REVISION, required_revision)) del required_revision ================================================ FILE: sipsimple/core/_core.error.pxi ================================================ class SIPCoreError(Exception): pass class PJSIPError(SIPCoreError): def __init__(self, message, status): self.status = status SIPCoreError.__init__(self, "%s: %s" % (message, _pj_status_to_str(status))) @property def errno(self): # PJ_STATUS - PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE*2 return 0 if self.status == 0 else self.status - 120000 class PJSIPTLSError(PJSIPError): pass class SIPCoreInvalidStateError(SIPCoreError): pass ================================================ FILE: sipsimple/core/_core.event.pxi ================================================ # C types cdef struct _core_event: _core_event *prev _core_event *next int is_log int level void *data int len cdef struct _handler: _handler *next _handler *prev int func(object obj) except -1 void *obj cdef struct _handler_queue: _handler *head _handler *tail # callback functions cdef void _cb_log(int level, char_ptr_const data, int len): cdef _core_event *event event = <_core_event *> malloc(sizeof(_core_event)) if event != NULL: event.data = malloc(len) if event.data == NULL: free(event) return event.is_log = 1 event.level = level memcpy(event.data, data, len) event.len = len if _event_queue_append(event) != 0: free(event.data) free(event) # functions cdef int _add_event(object event_name, dict params) except -1: cdef tuple data cdef _core_event *event cdef int status event = <_core_event *> malloc(sizeof(_core_event)) if event == NULL: raise MemoryError() data = (event_name, params) event.is_log = 0 event.data = data status = _event_queue_append(event) if status != 0: raise PJSIPError("Could not obtain lock", status) Py_INCREF(data) return 0 cdef int _event_queue_append(_core_event *event): global _event_queue_head, _event_queue_tail, _event_queue_lock cdef int locked = 0, status event.next = NULL if _event_queue_lock != NULL: status = pj_mutex_lock(_event_queue_lock) if status != 0: return status locked = 1 if _event_queue_head == NULL: event.prev = NULL _event_queue_head = event _event_queue_tail = event else: _event_queue_tail.next = event event.prev = _event_queue_tail _event_queue_tail = event if locked: pj_mutex_unlock(_event_queue_lock) return 0 cdef list _get_clear_event_queue(): global _re_log, _event_queue_head, _event_queue_tail, _event_queue_lock cdef object events = [] cdef _core_event *event cdef _core_event *event_free cdef object event_tup cdef object event_params, log_msg, log_match cdef int locked = 0 if _event_queue_lock != NULL: status = pj_mutex_lock(_event_queue_lock) if status != 0: return status locked = 1 event = _event_queue_head _event_queue_head = _event_queue_tail = NULL if locked: pj_mutex_unlock(_event_queue_lock) while event != NULL: if event.is_log: log_msg = PyString_FromStringAndSize( event.data, event.len) event_params = dict(level=event.level, message=log_msg) events.append(("SIPEngineLog", event_params)) else: event_tup = event.data Py_DECREF(event_tup) events.append(event_tup) event_free = event event = event.next free(event_free) return events cdef int _add_handler(int func(object obj) except -1, object obj, _handler_queue *queue) except -1: cdef _handler *handler handler = <_handler *> malloc(sizeof(_handler)) if handler == NULL: raise MemoryError() handler.func = func handler.obj = obj handler.next = NULL if queue.head == NULL: handler.prev = NULL queue.head = handler queue.tail = handler else: queue.tail.next = handler handler.prev = queue.tail queue.tail = handler return 0 cdef int _remove_handler(object obj, _handler_queue *queue) except -1: cdef _handler *handler cdef _handler *handler_free handler = queue.head while handler != NULL: if handler.obj == obj: if handler.prev != NULL: handler.prev.next = handler.next if handler.next != NULL: handler.next.prev = handler.prev if queue.head == handler: queue.head = handler.next if queue.tail == handler: queue.tail = handler.prev handler_free = handler handler = handler.next free(handler_free) else: handler = handler.next return 0 cdef int _process_handler_queue(PJSIPUA ua, _handler_queue *queue) except -1: cdef _handler *handler cdef _handler *handler_free handler = queue.head queue.head = queue.tail = NULL while handler != NULL: try: handler.func( handler.obj) except: ua._handle_exception(1) handler_free = handler handler = handler.next free(handler_free) return 0 # globals cdef pj_mutex_t *_event_queue_lock = NULL cdef _core_event *_event_queue_head = NULL cdef _core_event *_event_queue_tail = NULL cdef _handler_queue _post_poll_handler_queue _post_poll_handler_queue.head = NULL _post_poll_handler_queue.tail = NULL cdef _handler_queue _dealloc_handler_queue _dealloc_handler_queue.head = NULL _dealloc_handler_queue.tail = NULL ================================================ FILE: sipsimple/core/_core.headers.pxi ================================================ # Classes # cdef object BaseHeader_richcmp(object self, object other, int op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseHeader): return NotImplemented if op == 2: return self.name == other.name and self.body == other.body else: return self.name != other.name or self.body != other.body cdef class BaseHeader: normal_type = Header frozen_type = FrozenHeader def __init__(self, *args, **kwargs): raise TypeError("BaseHeader cannot be instantiated directly") def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.name, self.body) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseHeader_richcmp(self, other, op) def Header_new(cls, BaseHeader header): return cls(header.name, header.body) cdef class Header(BaseHeader): def __init__(self, str name not None, str body not None): self.name = name self.body = body property name: def __get__(self): return self._name def __set__(self, str name not None): self._name = name property body: def __get__(self): return self._body def __set__(self, str body not None): self._body = body new = classmethod(Header_new) del Header_new def FrozenHeader_new(cls, BaseHeader header): if isinstance(header, cls): return header return cls(header.name, header.body) cdef class FrozenHeader(BaseHeader): def __init__(self, str name not None, str body not None): self.name = name self.body = body def __hash__(self): return hash((self.name, self.body)) def __richcmp__(self, other, op): return BaseHeader_richcmp(self, other, op) new = classmethod(FrozenHeader_new) del FrozenHeader_new class ContentType(str): def __init__(self, value): if '' in value.partition('/'): raise ValueError('invalid content type') @property def type(self): return self.partition('/')[0] @property def subtype(self): return self.partition('/')[2] cdef object BaseContentTypeHeader_richcmp(object self, object other, object op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseContentTypeHeader): return NotImplemented if op == 2: return self.content_type == other.content_type and self.parameters == other.parameters else: return self.content_type != other.content_type and self.parameters != other.parameters cdef class BaseContentTypeHeader: normal_type = ContentTypeHeader frozen_type = FrozenContentTypeHeader def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.content_type, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __unicode__(self): return unicode(self.__str__(), encoding='utf-8') def __richcmp__(self, other, op): return BaseContentTypeHeader_richcmp(self, other, op) property name: def __get__(self): return "Content-Type" property body: def __get__(self): if self.parameters: parameters = ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) for name, value in self.parameters.iteritems()]) else: parameters = "" return self.content_type + parameters def ContentTypeHeader_new(cls, BaseContentTypeHeader header): return cls(header.content_type, dict(header.parameters)) cdef class ContentTypeHeader(BaseContentTypeHeader): def __init__(self, str content_type, dict parameters=None): self.content_type = content_type self.parameters = parameters if parameters is not None else {} property content_type: def __get__(self): try: return ContentType(self._content_type) except ValueError: return None def __set__(self, str content_type): self._content_type = content_type property parameters: def __get__(self): return self._parameters def __set__(self, dict parameters not None): self._parameters = parameters new = classmethod(ContentTypeHeader_new) del ContentTypeHeader_new def FrozenContentTypeHeader_new(cls, BaseContentTypeHeader header): if isinstance(header, cls): return header return cls(header.content_type, frozendict(header.parameters)) cdef class FrozenContentTypeHeader(BaseContentTypeHeader): def __init__(self, str content_type, frozendict parameters not None=frozendict()): if not self.initialized: self._content_type = content_type self.parameters = parameters self.initialized = 1 property content_type: def __get__(self): try: return ContentType(self._content_type) except ValueError: return None def __hash__(self): return hash((self.content_type, self.parameters)) def __richcmp__(self, other, op): return BaseContentTypeHeader_richcmp(self, other, op) new = classmethod(FrozenContentTypeHeader_new) del FrozenContentTypeHeader_new cdef object BaseContactHeader_richcmp(object self, object other, object op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseContactHeader): return NotImplemented if op == 2: return self.name == other.name and self.uri == other.uri and self.display_name == other.display_name and self.parameters == other.parameters else: return self.name != other.name or self.uri != other.uri or self.display_name != other.display_name or self.parameters != other.parameters cdef class BaseContactHeader: normal_type = ContactHeader frozen_type = FrozenContactHeader def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r, %r, %r)" % (self.__class__.__name__, self.uri, self.display_name, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __unicode__(self): return unicode(self.__str__(), encoding='utf-8') def __richcmp__(self, other, op): return BaseContactHeader_richcmp(self, other, op) property name: def __get__(self): return "Contact" property body: def __get__(self): if self.uri is None: return "*" if self.parameters: parameters = ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) for name, value in self.parameters.iteritems()]) else: parameters = "" if self.display_name: return '"%s" <%s>%s' % (self.display_name.encode('utf-8'), self.uri, parameters) else: return '<%s>%s' % (self.uri, parameters) def ContactHeader_new(cls, BaseContactHeader header): return cls(SIPURI.new(header.uri), header.display_name, dict(header.parameters)) cdef class ContactHeader(BaseContactHeader): def __init__(self, SIPURI uri, unicode display_name=None, dict parameters=None): if uri is None and (display_name is not None or parameters not in (None, {})): raise ValueError("uri cannot be None if display_name or parameters are specified") self.uri = uri self.display_name = display_name self.parameters = parameters if parameters is not None else {} property uri: def __get__(self): return self._uri def __set__(self, SIPURI uri): if uri is None and (self.display_name is not None or self.parameters != {}): raise ValueError("uri cannot be None if display_name or parameters are specified") self._uri = uri property display_name: def __get__(self): return self._display_name def __set__(self, unicode display_name): if self.uri is None and display_name is not None: raise ValueError("display_name cannot be specified if uri is None") self._display_name = display_name property parameters: def __get__(self): return self._parameters def __set__(self, dict parameters not None): if self.uri is None and parameters != {}: raise ValueError("parameters cannot be specified if uri is None") self._parameters = parameters property q: def __get__(self): value = self.parameters.get("q", None) if value is not None: value = float(value) return value def __set__(self, object value): if value is None: self.parameters.pop("q", None) else: if self.uri is None: raise ValueError("parameters cannot be specified if uri is None") self.parameters["q"] = str(float(value)) property expires: def __get__(self): value = self.parameters.get("expires", None) if value is not None: value = int(value) return value def __set__(self, object value): if value is None: self.parameters.pop("expires", None) else: if self.uri is None: raise ValueError("parameters cannot be specified if uri is None") self.parameters["expires"] = str(int(value)) new = classmethod(ContactHeader_new) del ContactHeader_new def FrozenContactHeader_new(cls, BaseContactHeader header): if isinstance(header, cls): return header return cls(FrozenSIPURI.new(header.uri), header.display_name, frozendict(header.parameters)) cdef class FrozenContactHeader(BaseContactHeader): def __init__(self, FrozenSIPURI uri, unicode display_name=None, frozendict parameters not None=frozendict()): if not self.initialized: if uri is None and (display_name is not None or parameters not in (None, {})): raise ValueError("uri cannot be None if display_name or parameters are specified") self.uri = uri self.display_name = display_name self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.uri, self.display_name, self.parameters)) def __richcmp__(self, other, op): return BaseContactHeader_richcmp(self, other, op) property q: def __get__(self): value = self.parameters.get("q", None) if value is not None: value = float(value) return value property expires: def __get__(self): value = self.parameters.get("expires", None) if value is not None: value = int(value) return value new = classmethod(FrozenContactHeader_new) del FrozenContactHeader_new cdef object BaseIdentityHeader_richcmp(object self, object other, int op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseIdentityHeader): return NotImplemented if op == 2: return self.name == other.name and self.uri == other.uri and self.display_name == other.display_name and self.parameters == other.parameters else: return self.name != other.name or self.uri != other.uri or self.display_name != other.display_name or self.parameters != other.parameters cdef class BaseIdentityHeader: def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r, %r, %r)" % (self.__class__.__name__, self.uri, self.display_name, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __unicode__(self): return unicode(self.__str__(), encoding='utf-8') def __richcmp__(self, other, op): return BaseIdentityHeader_richcmp(self, other, op) property body: def __get__(self): if self.parameters: parameters = ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) for name, value in self.parameters.iteritems()]) else: parameters = "" if self.display_name: return '"%s" <%s>%s' % (self.display_name.encode('utf-8'), self.uri, parameters) else: return '<%s>%s' % (self.uri, parameters) def IdentityHeader_new(cls, BaseIdentityHeader contact_header): return cls(SIPURI.new(contact_header.uri), contact_header.display_name, dict(contact_header.parameters)) cdef class IdentityHeader(BaseIdentityHeader): property uri: def __get__(self): return self._uri def __set__(self, SIPURI uri not None): self._uri = uri property parameters: def __get__(self): return self._parameters def __set__(self, dict parameters not None): self._parameters = parameters new = classmethod(IdentityHeader_new) del IdentityHeader_new def FrozenIdentityHeader_new(cls, BaseIdentityHeader contact_header): if isinstance(contact_header, cls): return contact_header return cls(FrozenSIPURI.new(contact_header.uri), contact_header.display_name, frozendict(contact_header.parameters)) cdef class FrozenIdentityHeader(BaseIdentityHeader): def __hash__(self): return hash((self.uri, self.display_name, self.parameters)) def __richcmp__(self, other, op): return BaseIdentityHeader_richcmp(self, other, op) new = classmethod(FrozenIdentityHeader_new) del FrozenIdentityHeader_new cdef class FromHeader(IdentityHeader): normal_type = FromHeader frozen_type = FrozenFromHeader def __init__(self, SIPURI uri not None, unicode display_name=None, dict parameters=None): self.uri = uri self.display_name = display_name self.parameters = parameters if parameters is not None else {} property tag: def __get__(self): return self.parameters.get("tag", None) def __set__(self, str value): if value is None: self.parameters.pop("tag", None) else: self.parameters["tag"] = value property name: def __get__(self): return "From" cdef class FrozenFromHeader(FrozenIdentityHeader): normal_type = FromHeader frozen_type = FrozenFromHeader def __init__(self, FrozenSIPURI uri not None, unicode display_name=None, frozendict parameters not None=frozendict()): if not self.initialized: self.uri = uri self.display_name = display_name self.parameters = parameters self.initialized = 1 property tag: def __get__(self): return self.parameters.get("tag", None) property name: def __get__(self): return "From" cdef class ToHeader(IdentityHeader): normal_type = ToHeader frozen_type = FrozenToHeader def __init__(self, SIPURI uri not None, unicode display_name=None, dict parameters=None): self.uri = uri self.display_name = display_name self.parameters = parameters if parameters is not None else {} property tag: def __get__(self): return self.parameters.get("tag", None) def __set__(self, str value): if value is None: self.parameters.pop("tag", None) else: self.parameters["tag"] = value property name: def __get__(self): return "To" cdef class FrozenToHeader(FrozenIdentityHeader): normal_type = ToHeader frozen_type = FrozenToHeader def __init__(self, FrozenSIPURI uri not None, unicode display_name=None, frozendict parameters not None=frozendict()): if not self.initialized: self.uri = uri self.display_name = display_name self.parameters = parameters self.initialized = 1 property tag: def __get__(self): return self.parameters.get("tag", None) property name: def __get__(self): return "To" cdef class RouteHeader(IdentityHeader): normal_type = RouteHeader frozen_type = FrozenRouteHeader def __init__(self, SIPURI uri not None, unicode display_name=None, dict parameters=None): self.uri = uri self.display_name = display_name self.parameters = parameters if parameters is not None else {} property name: def __get__(self): return "Route" cdef class FrozenRouteHeader(FrozenIdentityHeader): normal_type = RouteHeader frozen_type = FrozenRouteHeader def __init__(self, FrozenSIPURI uri not None, unicode display_name=None, frozendict parameters not None=frozendict()): if not self.initialized: self.uri = uri self.display_name = display_name self.parameters = parameters self.initialized = 1 property name: def __get__(self): return "Route" cdef class RecordRouteHeader(IdentityHeader): normal_type = RecordRouteHeader frozen_type = FrozenRecordRouteHeader def __init__(self, SIPURI uri not None, unicode display_name=None, dict parameters=None): self.uri = uri self.display_name = display_name self.parameters = parameters if parameters is not None else {} property name: def __get__(self): return "Record-Route" cdef class FrozenRecordRouteHeader(FrozenIdentityHeader): normal_type = RecordRouteHeader frozen_type = FrozenRecordRouteHeader def __init__(self, FrozenSIPURI uri not None, unicode display_name=None, frozendict parameters not None=frozendict()): if not self.initialized: self.uri = uri self.display_name = display_name self.parameters = parameters self.initialized = 1 property name: def __get__(self): return "Record-Route" cdef object BaseRetryAfterHeader_richcmp(object self, object other, int op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseRetryAfterHeader): return NotImplemented if op == 2: return self.seconds == other.seconds and self.comment == other.comment and self.parameters == other.parameters else: return self.seconds != other.seconds or self.comment != other.comment or self.parameters != other.parameters cdef class BaseRetryAfterHeader: normal_type = RetryAfterHeader frozen_type = FrozenRetryAfterHeader def __init__(self, *args, **kwargs): raise TypeError("BaseRetryAfterHeader cannot be instantiated directly") def __repr__(self): return "%s(%r, %r, %r)" % (self.__class__.__name__, self.seconds, self.comment, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseRetryAfterHeader_richcmp(self, other, op) property name: def __get__(self): return "Retry-After" property body: def __get__(self): string = str(self.seconds) if self.comment is not None: string += " (%s)" % self.comment if self.parameters: string += ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) for name, value in self.parameters.iteritems()]) return string def RetryAfterHeader_new(cls, BaseRetryAfterHeader header): return cls(header.seconds, header.comment, dict(header.parameters)) cdef class RetryAfterHeader(BaseRetryAfterHeader): def __init__(self, int seconds, str comment=None, dict parameters=None): self.seconds = seconds self.comment = comment self.parameters = parameters if parameters is not None else {} property parameters: def __get__(self): return self._parameters def __set__(self, dict parameters not None): self._parameters = parameters property duration: def __get__(self): value = self.parameters.get("duration", None) if value is not None: value = int(value) return value def __set__(self, object value): if value is None: self.parameters.pop("duration", None) else: self.parameters["duration"] = str(int(value)) new = classmethod(RetryAfterHeader_new) del RetryAfterHeader_new def FrozenRetryAfterHeader_new(cls, BaseRetryAfterHeader header): if isinstance(header, cls): return header return cls(header.seconds, header.comment, frozendict(header.parameters)) cdef class FrozenRetryAfterHeader(BaseRetryAfterHeader): def __init__(self, int seconds, str comment=None, frozendict parameters not None=frozendict()): if not self.initialized: self.seconds = seconds self.comment = comment self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.seconds, self.comment, self.parameters)) def __richcmp__(self, other, op): return BaseRetryAfterHeader_richcmp(self, other, op) property duration: def __get__(self): value = self.parameters.get("duration", None) if value is not None: value = int(value) return value new = classmethod(FrozenRetryAfterHeader_new) del FrozenRetryAfterHeader_new cdef object BaseViaHeader_richcmp(object self, object other, int op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseViaHeader): return NotImplemented if op == 2: return self.transport == other.transport and self.host == other.host and self.port == other.port and self.parameters == other.parameters else: return self.transport != other.transport or self.host != other.host or self.port != other.port or self.parameters != other.parameters cdef class BaseViaHeader: normal_type = ViaHeader frozen_type = FrozenViaHeader def __init__(self, *args, **kwargs): raise TypeError("BaseViaHeader cannot be instantiated directly") def __repr__(self): return "%s(%r, %r, %r, %r)" % (self.__class__.__name__, self.transport, self.host, self.port, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseViaHeader_richcmp(self, other, op) property name: def __get__(self): return "Via" property body: def __get__(self): string = "SIP/2.0/%s %s:%d" % (self.transport, self.host, self.port) if self.parameters: string += ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) for name, value in self.parameters.iteritems()]) return string def ViaHeader_new(cls, BaseViaHeader header): return cls(header.transport, header.host, header.port, dict(header.parameters)) cdef class ViaHeader(BaseViaHeader): def __init__(self, str transport not None, str host not None, int port=5060, dict parameters=None): self.transport = transport self.host = host self.port = port self.parameters = parameters if parameters is not None else {} property transport: def __get__(self): return self._transport def __set__(self, str transport not None): self._transport = transport property host: def __get__(self): return self._host def __set__(self, str host not None): self._host = host property port: def __get__(self): return self._port def __set__(self, int port): if not (0 < port <= 65535): raise ValueError("Invalid port: %d" % port) self._port = port property parameters: def __get__(self): return self._parameters def __set__(self, dict parameters not None): self._parameters = parameters property ttl: def __get__(self): value = self.parameters.get("ttl", None) if value is not None: value = int(value) return value def __set__(self, object value): if value is None: self.parameters.pop("ttl", None) else: self.parameters["ttl"] = str(int(value)) property maddr: def __get__(self): return self.parameters.get("maddr", None) def __set__(self, str value): if value is None: self.parameters.pop("maddr", None) else: self.parameters["maddr"] = value property received: def __get__(self): return self.parameters.get("received", None) def __set__(self, str value): if value is None: self.parameters.pop("received", None) else: self.parameters["received"] = value property branch: def __get__(self): return self.parameters.get("branch", None) def __set__(self, str value): if value is None: self.parameters.pop("branch", None) else: self.parameters["branch"] = value property compression: def __get__(self): return self.parameters.get("compression", None) def __set__(self, str value): if value is None: self.parameters.pop("compression", None) else: self.parameters["compression"] = value property rport: def __get__(self): value = self.parameters.get("rport", None) if value is not None: value = int(value) return value def __set__(self, object value): if value is None: self.parameters.pop("rport", None) else: self.parameters["rport"] = str(int(value)) new = classmethod(ViaHeader_new) del ViaHeader_new def FrozenViaHeader_new(cls, BaseViaHeader header): if isinstance(header, cls): return header return cls(header.transport, header.host, header.port, frozendict(header.parameters)) cdef class FrozenViaHeader(BaseViaHeader): def __init__(self, str transport not None, str host not None, int port=5060, frozendict parameters not None=frozendict()): if not self.initialized: if not (0 < port <= 65535): raise ValueError("Invalid port: %d" % port) self.transport = transport self.host = host self.port = port self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.transport, self.host, self.port, self.parameters)) def __richcmp__(self, other, op): return BaseViaHeader_richcmp(self, other, op) property ttl: def __get__(self): value = self.parameters.get("ttl", None) if value is not None: value = int(value) return value property maddr: def __get__(self): return self.parameters.get("maddr", None) property received: def __get__(self): return self.parameters.get("received", None) property branch: def __get__(self): return self.parameters.get("branch", None) property compression: def __get__(self): return self.parameters.get("compression", None) property rport: def __get__(self): value = self.parameters.get("rport", None) if value is not None: value = int(value) return value new = classmethod(FrozenViaHeader_new) del FrozenViaHeader_new cdef object BaseWarningHeader_richcmp(object self, object other, int op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseWarningHeader): return NotImplemented if op == 2: return self.code == other.code and self.agent == other.agent and self.text == other.text else: return self.code != other.code or self.agent != other.agent or self.text != other.text cdef class BaseWarningHeader: normal_type = WarningHeader frozen_type = FrozenWarningHeader def __init__(self, *args, **kwargs): raise TypeError("BaseWarningHeader cannot be instantiated directly") def __repr__(self): return "%s(%r, %r, %r)" % (self.__class__.__name__, self.code, self.agent, self.text) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseWarningHeader_richcmp(self, other, op) property name: def __get__(self): return "Warning" property body: def __get__(self): return '%d %s "%s"' % (self.code, self.agent, self.text) def WarningHeader_new(cls, BaseWarningHeader header): return cls(header.code, header.agent, header.text) cdef class WarningHeader(BaseWarningHeader): def __init__(self, int code, str agent not None, str text not None): self.code = code self.agent = agent self.text = text property code: def __get__(self): return self._code def __set__(self, int code): if code < 100 or code > 999: raise ValueError("code needs to be a 3 digit number") self._code = code property agent: def __get__(self): return self._agent def __set__(self, str agent not None): self._agent = agent property text: def __get__(self): return self._text def __set__(self, str text not None): self._text = text new = classmethod(WarningHeader_new) del WarningHeader_new def FrozenWarningHeader_new(cls, BaseWarningHeader header): if isinstance(header, cls): return header return cls(header.code, header.agent, header.text) cdef class FrozenWarningHeader(BaseWarningHeader): def __init__(self, int code, str agent not None, str text not None): if not self.initialized: if code < 100 or code > 999: raise ValueError("code needs to be a 3 digit number") self.code = code self.agent = agent self.text = text self.initialized = 1 def __hash__(self): return hash((self.code, self.agent, self.text)) def __richcmp__(self, other, op): return BaseWarningHeader_richcmp(self, other, op) new = classmethod(FrozenWarningHeader_new) del FrozenWarningHeader_new cdef object BaseEventHeader_richcmp(object self, object other, object op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseEventHeader): return NotImplemented if op == 2: return self.event == other.event and self.parameters == other.parameters else: return self.event != other.event or self.parameters != other.parameters cdef class BaseEventHeader: normal_type = EventHeader frozen_type = FrozenEventHeader def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.event, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseEventHeader_richcmp(self, other, op) property name: def __get__(self): return "Event" property body: def __get__(self): if self.parameters: parameters = ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) for name, value in self.parameters.iteritems()]) else: parameters = "" return self.event + parameters def EventHeader_new(cls, BaseEventHeader header): return cls(header.event, dict(header.parameters)) cdef class EventHeader(BaseEventHeader): def __init__(self, str event not None, dict parameters=None): self.event = event self.parameters = parameters if parameters is not None else {} property id: def __get__(self): return self._parameters.get("id", None) def __set__(self, str id): if id is None: self._parameters.pop("id", None) else: self._parameters["id"] = id property parameters: def __get__(self): return self._parameters.copy() def __set__(self, dict parameters not None): self._parameters = parameters new = classmethod(EventHeader_new) del EventHeader_new def FrozenEventHeader_new(cls, BaseEventHeader header): if isinstance(header, cls): return header return cls(header.event, frozendict(header.parameters)) cdef class FrozenEventHeader(BaseEventHeader): def __init__(self, str event not None, frozendict parameters not None=frozendict()): if not self.initialized: self.event = event self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.event, self.parameters)) def __richcmp__(self, other, op): return BaseEventHeader_richcmp(self, other, op) property id: def __get__(self): return self.parameters.get("id", None) new = classmethod(FrozenEventHeader_new) del FrozenEventHeader_new cdef object BaseSubscriptionStateHeader_richcmp(object self, object other, object op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseSubscriptionStateHeader): return NotImplemented if op == 2: return self.state == other.state and self.parameters == other.parameters else: return self.state != other.state or self.parameters != other.parameters cdef class BaseSubscriptionStateHeader: normal_type = SubscriptionStateHeader frozen_type = FrozenSubscriptionStateHeader def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.state, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseSubscriptionStateHeader_richcmp(self, other, op) property name: def __get__(self): return "SubscriptionState" property body: def __get__(self): if self.parameters: parameters = ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) for name, value in self.parameters.iteritems()]) else: parameters = "" return self.state + parameters def SubscriptionStateHeader_new(cls, BaseSubscriptionStateHeader header): return cls(header.state, dict(header.parameters)) cdef class SubscriptionStateHeader(BaseSubscriptionStateHeader): def __init__(self, str state not None, dict parameters=None): self.state = state self.parameters = parameters if parameters is not None else {} property reason: def __get__(self): return self._parameters.get("reason", None) def __set__(self, str reason): if reason is None: self._parameters.pop("reason", None) else: self._parameters["reason"] = reason property expires: def __get__(self): return int(self._parameters.get("expires", None)) def __set__(self, object expires): cdef int expires_i if expires is None: self._parameters.pop("expires", None) else: expires_i = expires self._parameters["expires"] = str(expires_i) property retry_after: def __get__(self): return int(self._parameters.get("retry-after", None)) def __set__(self, object retry_after): cdef int retry_after_i if retry_after is None: self._parameters.pop("retry-after", None) else: retry_after_i = retry_after self._parameters["retry-after"] = str(retry_after_i) property parameters: def __get__(self): return self._parameters.copy() def __set__(self, dict parameters not None): self._parameters = parameters new = classmethod(SubscriptionStateHeader_new) del SubscriptionStateHeader_new def FrozenSubscriptionStateHeader_new(cls, BaseSubscriptionStateHeader header): if isinstance(header, cls): return header return cls(header.state, frozendict(header.parameters)) cdef class FrozenSubscriptionStateHeader(BaseSubscriptionStateHeader): def __init__(self, str state not None, frozendict parameters not None=frozendict()): if not self.initialized: self.state = state self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.state, self.parameters)) def __richcmp__(self, other, op): return BaseSubscriptionStateHeader_richcmp(self, other, op) property reason: def __get__(self): return self.parameters.get("reason", None) property expires: def __get__(self): expires = self.parameters.get("expires", None) if expires is not None: expires = int(expires) return expires property retry_after: def __get__(self): retry_after = self.parameters.get("retry-after", None) if retry_after is not None: retry_after = int(retry_after) return retry_after new = classmethod(FrozenSubscriptionStateHeader_new) del FrozenSubscriptionStateHeader_new cdef object BaseReasonHeader_richcmp(object self, object other, object op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseSubscriptionStateHeader): return NotImplemented if op == 2: return self.protocol == other.protocol and self.parameters == other.parameters else: return self.protocol != other.protocol or self.parameters != other.parameters cdef class BaseReasonHeader: normal_type = ReasonHeader frozen_type = FrozenReasonHeader def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.protocol, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseReasonHeader_richcmp(self, other, op) property name: def __get__(self): return "Reason" property body: def __get__(self): if self.parameters: parameters = " ;" + " ;".join(["%s%s" % (name, "" if value is None else "=%s" % value) for name, value in self.parameters.iteritems()]) else: parameters = "" return self.protocol + parameters def ReasonHeader_new(cls, BaseReasonHeader header): return cls(header.state, dict(header.parameters)) cdef class ReasonHeader(BaseReasonHeader): def __init__(self, str protocol not None, dict parameters=None): self.protocol = protocol self.parameters = parameters if parameters is not None else {} property cause: def __get__(self): cause = self.parameters.get("cause", None) if cause is not None: cause = int(cause) return cause def __set__(self, object cause): cdef int cause_i if cause is None: self.parameters.pop("cause", None) else: cause_i = int(cause) self.parameters["cause"] = str(cause_i) property text: def __get__(self): text = self.parameters.get("text", None) if text is not None: text = text.strip().strip('"') return text def __set__(self, str text): if text is None: self.parameters.pop("text", None) else: if not text.startswith('"'): text = '"'+text if not text[1:].endswith('"'): text = text+'"' self.parameters["text"] = text new = classmethod(ReasonHeader_new) del ReasonHeader_new def FrozenReasonHeader_new(cls, BaseReasonHeader header): if isinstance(header, cls): return header return cls(header.protocol, frozendict(header.parameters)) cdef class FrozenReasonHeader(BaseReasonHeader): def __init__(self, str protocol not None, frozendict parameters not None=frozendict()): if not self.initialized: self.protocol = protocol self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.protocol, self.parameters)) def __richcmp__(self, other, op): return BaseReasonHeader_richcmp(self, other, op) property cause: def __get__(self): cause = self.parameters.get("cause", None) if cause is not None: cause = int(cause) return cause property text: def __get__(self): text = self.parameters.get("text", None) if text is not None: text = text.strip().strip('"') return text new = classmethod(FrozenReasonHeader_new) del FrozenReasonHeader_new cdef object BaseReferToHeader_richcmp(object self, object other, object op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseReferToHeader): return NotImplemented if op == 2: return self.uri == other.uri and self.parameters == other.parameters else: return self.uri != other.uri or self.parameters != other.parameters cdef class BaseReferToHeader: normal_type = ReferToHeader frozen_type = FrozenReferToHeader def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.uri, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseReferToHeader_richcmp(self, other, op) property name: def __get__(self): return "Refer-To" property body: def __get__(self): if self.parameters: parameters = ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) for name, value in self.parameters.iteritems()]) else: parameters = "" return "<%s>%s" % (self.uri, parameters) def ReferToHeader_new(cls, BaseReferToHeader header): return cls(header.uri, dict(header.parameters)) cdef class ReferToHeader(BaseReferToHeader): def __init__(self, str uri not None, dict parameters=None): self.uri = uri self.parameters = parameters if parameters is not None else {} property parameters: def __get__(self): return self._parameters def __set__(self, dict parameters not None): self._parameters = parameters new = classmethod(ReferToHeader_new) del ReferToHeader_new def FrozenReferToHeader_new(cls, BaseReferToHeader header): if isinstance(header, cls): return header return cls(header.uri, frozendict(header.parameters)) cdef class FrozenReferToHeader(BaseReferToHeader): def __init__(self, str uri not None, frozendict parameters not None=frozendict()): if not self.initialized: self.uri = uri self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.uri, self.parameters)) def __richcmp__(self, other, op): return BaseReferToHeader_richcmp(self, other, op) new = classmethod(FrozenReferToHeader_new) del FrozenReferToHeader_new cdef object BaseSubjectHeader_richcmp(object self, object other, int op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseSubjectHeader): return NotImplemented if op == 2: return self.subject == other.subject else: return self.subject != other.subject cdef class BaseSubjectHeader: def __init__(self, *args, **kwargs): raise TypeError("%s cannot be instantiated directly" % self.__class__.__name__) def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.subject) def __str__(self): return "%s: %s" % (self.name, self.body) def __unicode__(self): return unicode(self.__str__(), encoding='utf-8') def __richcmp__(self, other, op): return BaseSubjectHeader_richcmp(self, other, op) property name: def __get__(self): return "Subject" property body: def __get__(self): return self.subject.encode('utf-8') def SubjectHeader_new(cls, BaseSubjectHeader subject_header): if isinstance(subject_header, cls): return subject_header return cls(subject_header.subject) cdef class SubjectHeader(BaseSubjectHeader): def __init__(self, unicode subject=None): if subject is None: raise ValueError('subject must be specified') self.subject = subject new = classmethod(SubjectHeader_new) del SubjectHeader_new def FrozenSubjectHeader_new(cls, BaseSubjectHeader subject_header): if isinstance(subject_header, cls): return subject_header return cls(subject_header.subject) cdef class FrozenSubjectHeader(BaseSubjectHeader): def __init__(self, unicode subject=None): if not self.initialized: if subject is None: raise ValueError('subject must be specified') self.subject = subject self.initialized = 1 def __hash__(self): return hash((self.subject)) def __richcmp__(self, other, op): return BaseSubjectHeader_richcmp(self, other, op) new = classmethod(FrozenSubjectHeader_new) del FrozenSubjectHeader_new cdef object BaseReplacesHeader_richcmp(object self, object other, int op) with gil: if op not in (2, 3): return NotImplemented if not isinstance(other, BaseReplacesHeader): return NotImplemented if op == 2: return self.call_id == other.call_id and self.from_tag == other.from_tag and self.to_tag == other.to_tag and self.early_only == other.early_only and self.parameters == other.parameters else: return self.call_id != other.call_id or self.from_tag != other.from_tag or self.to_tag != other.to_tag or self.early_only != other.early_only or self.parameters != other.parameters cdef class BaseReplacesHeader: normal_type = ReplacesHeader frozen_type = FrozenReplacesHeader def __init__(self, *args, **kwargs): raise TypeError("BaseReplacesHeader cannot be instantiated directly") def __repr__(self): return "%s(%r, %r, %r, %r, %r)" % (self.__class__.__name__, self.call_id, self.from_tag, self.to_tag, self.early_only, self.parameters) def __str__(self): return "%s: %s" % (self.name, self.body) def __richcmp__(self, other, op): return BaseReplacesHeader_richcmp(self, other, op) property name: def __get__(self): return "Replaces" property body: def __get__(self): string = "%s;from-tag=%s;to-tag=%s" % (self.call_id, self.from_tag, self.to_tag) if self.early_only: string += ";early-only" if self.parameters: string += ";" + ";".join(["%s%s" % (name, "" if value is None else "=%s" % value) for name, value in self.parameters.iteritems()]) return string def ReplacesHeader_new(cls, BaseReplacesHeader header): return cls(header.call_id, header.from_tag, header.to_tag, header.early_only, dict(header.parameters)) cdef class ReplacesHeader(BaseReplacesHeader): def __init__(self, str call_id not None, str from_tag not None, str to_tag not None, int early_only=0, dict parameters=None): self.call_id = call_id self.from_tag = from_tag self.to_tag = to_tag self.early_only = early_only self.parameters = parameters if parameters is not None else {} property parameters: def __get__(self): return self._parameters def __set__(self, dict parameters not None): self._parameters = parameters new = classmethod(ReplacesHeader_new) del ReplacesHeader_new def FrozenReplacesHeader_new(cls, BaseReplacesHeader header): if isinstance(header, cls): return header return cls(header.call_id, header.header.from_tag, header.to_tag, header.early_only, frozendict(header.parameters)) cdef class FrozenReplacesHeader(BaseReplacesHeader): def __init__(self, str call_id not None, str from_tag not None, str to_tag not None, int early_only=0, frozendict parameters not None=frozendict()): if not self.initialized: self.call_id = call_id self.from_tag = from_tag self.to_tag = to_tag self.early_only = early_only self.parameters = parameters self.initialized = 1 def __hash__(self): return hash((self.call_id, self.from_tag, self.to_tag, self.early_only, self.parameters)) def __richcmp__(self, other, op): return BaseReplacesHeader_richcmp(self, other, op) new = classmethod(FrozenReplacesHeader_new) del FrozenReplacesHeader_new # Factory functions # cdef Header Header_create(pjsip_generic_string_hdr *header): return Header(_pj_str_to_str(header.name), _pj_str_to_str(header.hvalue)) cdef FrozenHeader FrozenHeader_create(pjsip_generic_string_hdr *header): return FrozenHeader(_pj_str_to_str(header.name), _pj_str_to_str(header.hvalue)) cdef ContactHeader ContactHeader_create(pjsip_contact_hdr *header): cdef pjsip_name_addr* name_addr if header.star: return ContactHeader(None) else: uri = SIPURI_create(pjsip_uri_get_uri(header.uri)) name_addr = header.uri if name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) if header.q1000 != -1: parameters["q"] = str(float(header.q1000)/1000) if header.expires != -1: parameters["expires"] = str(header.expires) return ContactHeader(uri, display_name, parameters) cdef FrozenContactHeader FrozenContactHeader_create(pjsip_contact_hdr *header): cdef pjsip_name_addr* name_addr if header.star: return FrozenContactHeader(None) else: uri = FrozenSIPURI_create(pjsip_uri_get_uri(header.uri)) name_addr = header.uri if name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) if header.q1000 != -1: parameters["q"] = str(float(header.q1000)/1000) if header.expires != -1: parameters["expires"] = str(header.expires) return FrozenContactHeader(uri, display_name, frozendict(parameters)) cdef ContentTypeHeader ContentTypeHeader_create(pjsip_ctype_hdr *header): type_str = _pj_str_to_str(header.media.type) subtype_str = _pj_str_to_str(header.media.subtype) parameters = _pjsip_param_to_dict(&header.media.param) if subtype_str: content_type = "%s/%s" % (type_str, subtype_str) else: content_type = type_str return ContentTypeHeader(content_type, parameters) cdef FrozenContentTypeHeader FrozenContentTypeHeader_create(pjsip_ctype_hdr *header): type_str = _pj_str_to_str(header.media.type) subtype_str = _pj_str_to_str(header.media.subtype) parameters = _pjsip_param_to_dict(&header.media.param) if subtype_str: content_type = "%s/%s" % (type_str, subtype_str) else: content_type = type_str return FrozenContentTypeHeader(content_type, frozendict(parameters)) cdef FromHeader FromHeader_create(pjsip_fromto_hdr *header): cdef pjsip_name_addr* name_addr uri = SIPURI_create(pjsip_uri_get_uri(header.uri)) name_addr = header.uri if name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) if header.tag.slen > 0: parameters["tag"] = _pj_str_to_str(header.tag) return FromHeader(uri, display_name, parameters) cdef FrozenFromHeader FrozenFromHeader_create(pjsip_fromto_hdr *header): cdef pjsip_name_addr* name_addr uri = FrozenSIPURI_create(pjsip_uri_get_uri(header.uri)) name_addr = header.uri if name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) if header.tag.slen > 0: parameters["tag"] = _pj_str_to_str(header.tag) return FrozenFromHeader(uri, display_name, frozendict(parameters)) cdef ToHeader ToHeader_create(pjsip_fromto_hdr *header): cdef pjsip_name_addr* name_addr uri = SIPURI_create(pjsip_uri_get_uri(header.uri)) name_addr = header.uri if name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) if header.tag.slen > 0: parameters["tag"] = _pj_str_to_str(header.tag) return ToHeader(uri, display_name, parameters) cdef FrozenToHeader FrozenToHeader_create(pjsip_fromto_hdr *header): cdef pjsip_name_addr* name_addr uri = FrozenSIPURI_create(pjsip_uri_get_uri(header.uri)) name_addr = header.uri if name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) if header.tag.slen > 0: parameters["tag"] = _pj_str_to_str(header.tag) return FrozenToHeader(uri, display_name, frozendict(parameters)) cdef RouteHeader RouteHeader_create(pjsip_routing_hdr *header): uri = SIPURI_create(pjsip_uri_get_uri(&header.name_addr)) if header.name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(header.name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) return RouteHeader(uri, display_name, parameters) cdef FrozenRouteHeader FrozenRouteHeader_create(pjsip_routing_hdr *header): uri = FrozenSIPURI_create(pjsip_uri_get_uri(&header.name_addr)) if header.name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(header.name_addr.display), encoding='utf-8') else: display_name = None parameters = frozendict(_pjsip_param_to_dict(&header.other_param)) return FrozenRouteHeader(uri, display_name, parameters) cdef RecordRouteHeader RecordRouteHeader_create(pjsip_routing_hdr *header): uri = SIPURI_create(pjsip_uri_get_uri(&header.name_addr)) if header.name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(header.name_addr.display), encoding='utf-8') else: display_name = None parameters = _pjsip_param_to_dict(&header.other_param) return RecordRouteHeader(uri, display_name, parameters) cdef FrozenRecordRouteHeader FrozenRecordRouteHeader_create(pjsip_routing_hdr *header): uri = FrozenSIPURI_create(pjsip_uri_get_uri(&header.name_addr)) if header.name_addr.display.slen > 0: display_name = unicode(_pj_str_to_str(header.name_addr.display), encoding='utf-8') else: display_name = None parameters = frozendict(_pjsip_param_to_dict(&header.other_param)) return FrozenRecordRouteHeader(uri, display_name, parameters) cdef RetryAfterHeader RetryAfterHeader_create(pjsip_retry_after_hdr *header): seconds = header.ivalue if header.comment.slen > 0: comment = _pj_str_to_str(header.comment) else: comment = None parameters = _pjsip_param_to_dict(&header.param) return RetryAfterHeader(seconds, comment, parameters) cdef FrozenRetryAfterHeader FrozenRetryAfterHeader_create(pjsip_retry_after_hdr *header): seconds = header.ivalue if header.comment.slen > 0: comment = _pj_str_to_str(header.comment) else: comment = None parameters = frozendict(_pjsip_param_to_dict(&header.param)) return FrozenRetryAfterHeader(seconds, comment, parameters) cdef ViaHeader ViaHeader_create(pjsip_via_hdr *header): transport = _pj_str_to_str(header.transport) host = _pj_str_to_str(header.sent_by.host) port = header.sent_by.port or 5060 parameters = _pjsip_param_to_dict(&header.other_param) if header.ttl_param != -1: parameters["ttl"] = header.ttl_param if header.rport_param != -1: parameters["rport"] = header.rport_param if header.maddr_param.slen > 0: parameters["maddr"] = _pj_str_to_str(header.maddr_param) if header.recvd_param.slen > 0: parameters["received"] = _pj_str_to_str(header.recvd_param) if header.branch_param.slen > 0: parameters["branch"] = _pj_str_to_str(header.branch_param) return ViaHeader(transport, host, port, parameters) cdef FrozenViaHeader FrozenViaHeader_create(pjsip_via_hdr *header): transport = _pj_str_to_str(header.transport) host = _pj_str_to_str(header.sent_by.host) port = header.sent_by.port or 5060 parameters = _pjsip_param_to_dict(&header.other_param) if header.ttl_param != -1: parameters["ttl"] = header.ttl_param if header.rport_param != -1: parameters["rport"] = header.rport_param if header.maddr_param.slen > 0: parameters["maddr"] = _pj_str_to_str(header.maddr_param) if header.recvd_param.slen > 0: parameters["received"] = _pj_str_to_str(header.recvd_param) if header.branch_param.slen > 0: parameters["branch"] = _pj_str_to_str(header.branch_param) return FrozenViaHeader(transport, host, port, frozendict(parameters)) cdef EventHeader EventHeader_create(pjsip_event_hdr *header): cdef dict parameters parameters = _pjsip_param_to_dict(&header.other_param) if header.id_param.slen != 0: parameters["id"] = _pj_str_to_str(header.id_param) return EventHeader(_pj_str_to_str(header.event_type), parameters) cdef FrozenEventHeader FrozenEventHeader_create(pjsip_event_hdr *header): cdef dict parameters parameters = _pjsip_param_to_dict(&header.other_param) if header.id_param.slen != 0: parameters["id"] = _pj_str_to_str(header.id_param) return FrozenEventHeader(_pj_str_to_str(header.event_type), frozendict(parameters)) cdef SubscriptionStateHeader SubscriptionStateHeader_create(pjsip_sub_state_hdr *header): cdef dict parameters parameters = _pjsip_param_to_dict(&header.other_param) if header.reason_param.slen != 0: parameters["reason"] = _pj_str_to_str(header.reason_param) if header.expires_param != -1: parameters["expires"] = str(header.expires_param) if header.retry_after != -1: parameters["retry-after"] = str(header.retry_after) return SubscriptionStateHeader(_pj_str_to_str(header.sub_state), parameters) cdef FrozenSubscriptionStateHeader FrozenSubscriptionStateHeader_create(pjsip_sub_state_hdr *header): cdef dict parameters parameters = _pjsip_param_to_dict(&header.other_param) if header.reason_param.slen != 0: parameters["reason"] = _pj_str_to_str(header.reason_param) if header.expires_param != -1: parameters["expires"] = str(header.expires_param) if header.retry_after != -1: parameters["retry-after"] = str(header.retry_after) return FrozenSubscriptionStateHeader(_pj_str_to_str(header.sub_state), frozendict(parameters)) cdef ReferToHeader ReferToHeader_create(pjsip_generic_string_hdr *header): cdef dict parameters cdef str value value = _pj_str_to_str((header).hvalue) uri, sep, params_str = value.partition('>') if sep: uri += '>' parameters = dict([(name, value or None) for name, sep, value in [param.partition('=') for param in params_str.split(';') if param]]) return ReferToHeader(uri, parameters) cdef FrozenReferToHeader FrozenReferToHeader_create(pjsip_generic_string_hdr *header): cdef dict parameters cdef str value value = _pj_str_to_str((header).hvalue) uri, sep, params_str = value.partition('>') if sep: uri += '>' parameters = dict([(name, value or None) for name, sep, value in [param.partition('=') for param in params_str.split(';') if param]]) return FrozenReferToHeader(uri, frozendict(parameters)) cdef SubjectHeader SubjectHeader_create(pjsip_generic_string_hdr *header): subject = unicode(_pj_str_to_str((header).hvalue), encoding='utf-8') return SubjectHeader(subject) cdef FrozenSubjectHeader FrozenSubjectHeader_create(pjsip_generic_string_hdr *header): subject = unicode(_pj_str_to_str((header).hvalue), encoding='utf-8') return FrozenSubjectHeader(subject) cdef ReplacesHeader ReplacesHeader_create(pjsip_replaces_hdr *header): cdef dict parameters = _pjsip_param_to_dict(&header.other_param) call_id = _pj_str_to_str(header.call_id) from_tag = _pj_str_to_str(header.from_tag) to_tag = _pj_str_to_str(header.to_tag) early_only = int(header.early_only) return ReplacesHeader(call_id, from_tag, to_tag, early_only, parameters) cdef FrozenReplacesHeader FrozenReplacesHeader_create(pjsip_replaces_hdr *header): cdef dict parameters = _pjsip_param_to_dict(&header.other_param) call_id = _pj_str_to_str(header.call_id) from_tag = _pj_str_to_str(header.from_tag) to_tag = _pj_str_to_str(header.to_tag) early_only = int(header.early_only) return FrozenReplacesHeader(call_id, from_tag, to_tag, early_only, frozendict(parameters)) ================================================ FILE: sipsimple/core/_core.helper.pxi ================================================ import re import urllib # Classes # cdef class BaseCredentials: def __cinit__(self, *args, **kwargs): global _Credentials_scheme_digest, _Credentials_realm_wildcard self._credentials.scheme = _Credentials_scheme_digest.pj_str def __init__(self, str username not None, str password not None, str realm='*', bint digest=False): if self.__class__ is BaseCredentials: raise TypeError("BaseCredentials cannot be instantiated directly") self.username = username self.realm = realm self.password = password self.digest = digest def __repr__(self): return "%s(username=%r, password=%r, realm=%r, digest=%r)" % (self.__class__.__name__, self.username, self.password, self.realm, self.digest) def __str__(self): return '<%s for "%s@%s">' % (self.__class__.__name__, self.username, self.realm) cdef pjsip_cred_info* get_cred_info(self): return &self._credentials cdef class Credentials(BaseCredentials): property username: def __get__(self): return self._username def __set__(self, str username not None): _str_to_pj_str(username, &self._credentials.username) self._username = username property realm: def __get__(self): return self._realm def __set__(self, str realm not None): _str_to_pj_str(realm, &self._credentials.realm) self._realm = realm property password: def __get__(self): return self._password def __set__(self, str password not None): _str_to_pj_str(password, &self._credentials.data) self._password = password property digest: def __get__(self): return self._digest def __set__(self, bint digest): self._credentials.data_type = PJSIP_CRED_DATA_DIGEST if digest else PJSIP_CRED_DATA_PLAIN_PASSWD self._digest = digest @classmethod def new(cls, BaseCredentials credentials): return cls(credentials.username, credentials.password, credentials.realm, credentials.digest) cdef class FrozenCredentials(BaseCredentials): def __init__(self, str username not None, str password not None, str realm='*', bint digest=False): if not self.initialized: self.username = username self.realm = realm self.password = password self.digest = digest _str_to_pj_str(self.username, &self._credentials.username) _str_to_pj_str(self.realm, &self._credentials.realm) _str_to_pj_str(self.password, &self._credentials.data) self._credentials.data_type = PJSIP_CRED_DATA_DIGEST if digest else PJSIP_CRED_DATA_PLAIN_PASSWD self.initialized = 1 else: raise TypeError("{0.__class__.__name__} is read-only".format(self)) def __hash__(self): return hash((self.username, self.realm, self.password, self.digest)) @classmethod def new(cls, BaseCredentials credentials): if isinstance(credentials, FrozenCredentials): return credentials return cls(credentials.username, credentials.password, credentials.realm, credentials.digest) cdef class BaseSIPURI: def __init__(self, object host not None, object user=None, object password=None, object port=None, bint secure=False, dict parameters=None, dict headers=None): if self.__class__ is BaseSIPURI: raise TypeError("BaseSIPURI cannot be instantiated directly") self.host = host self.user = user self.password = password self.port = port self.secure = secure self.parameters = parameters if parameters is not None else {} self.headers = headers if headers is not None else {} property transport: def __get__(self): return self.parameters.get('transport', 'udp') def __set__(self, str transport not None): if transport.lower() == 'udp': self.parameters.pop('transport', None) else: self.parameters['transport'] = transport def __reduce__(self): return self.__class__, (self.host, self.user, self.password, self.port, self.secure, self.parameters, self.headers), None def __repr__(self): return "%s(%r, %r, %r, %r, %r, %r, %r)" % (self.__class__.__name__, self.host, self.user, self.password, self.port, self.secure, self.parameters, self.headers) def __str__(self): cdef object string = self.host if self.port: string = "%s:%d" % (string, self.port) if self.user is not None: if self.password is not None: string = "%s:%s@%s" % (self.user, self.password, string) else: string = "%s@%s" % (self.user, string) if self.parameters: string += ";" + ";".join(["%s%s" % (name, ("" if val is None else "="+urllib.quote(val, safe="()[]-_.!~*'/:&+$"))) for name, val in self.parameters.iteritems()]) if self.headers: string += "?" + "&".join(["%s%s" % (name, ("" if val is None else "="+urllib.quote(val, safe="()[]-_.!~*'/:?+$"))) for name, val in self.headers.iteritems()]) if self.secure: string = "sips:" + string else: string = "sip:" + string return string def __richcmp__(self, other, op): if not isinstance(other, BaseSIPURI): return NotImplemented if op == 2: # 2 is == return all(getattr(self, name) == getattr(other, name) for name in ("user", "password", "host", "port", "secure", "parameters", "headers")) elif op == 3: # 3 is != return any(getattr(self, name) != getattr(other, name) for name in ("user", "password", "host", "port", "secure", "parameters", "headers")) else: operator_map = {0: '<', 1: '<=', 2: '==', 3: '!=', 4: '>', 5: '>='} raise TypeError("unorderable types: {0.__class__.__name__}() {2} {1.__class__.__name__}()".format(self, other, operator_map[op])) def matches(self, address): match = re.match(r'^((?Psip|sips):)?(?P.+?)(@(?P.+?)(:(?P\d+?))?)?(;(?P.+?))?(\?(?P.+?))?$', address) if match is None: return False components = match.groupdict() if components['scheme'] is not None: expected_scheme = 'sips' if self.secure else 'sip' if components['scheme'] != expected_scheme: return False if components['username'] != self.user: return False if components['domain'] is not None and components['domain'] != self.host: return False if components['port'] is not None and int(components['port']) != self.port: return False if components['parameters']: parameters = dict([(name, value) for name, sep, value in [param.partition('=') for param in components['parameters'].split(';')]]) expected_parameters = dict([(name, str(value) if value is not None else None) for name, value in self.parameters.iteritems() if name in parameters]) if parameters != expected_parameters: return False if components['headers']: headers = dict([(name, value) for name, sep, value in [header.partition('=') for header in components['headers'].split('&')]]) expected_headers = dict([(name, str(value) if value is not None else None) for name, value in self.headers.iteritems() if name in headers]) if headers != expected_headers: return False return True cdef class SIPURI(BaseSIPURI): property port: def __get__(self): return self._port def __set__(self, object port): if port is not None: port = int(port) if not (0 < port <= 65535): raise ValueError("Invalid port: %d" % port) self._port = port @classmethod def new(cls, BaseSIPURI sipuri): return cls(user=sipuri.user, password=sipuri.password, host=sipuri.host, port=sipuri.port, secure=sipuri.secure, parameters=dict(sipuri.parameters), headers=dict(sipuri.headers)) @classmethod def parse(cls, object uri_str): if not isinstance(uri_str, basestring): raise TypeError('a string or unicode is required') cdef bytes uri_bytes = str(uri_str) cdef pjsip_uri *uri = NULL cdef pj_pool_t *pool = NULL cdef pj_str_t tmp cdef char buffer[4096] pool = pj_pool_create_on_buf("SIPURI_parse", buffer, sizeof(buffer)) if pool == NULL: raise SIPCoreError("Could not allocate memory pool") pj_strdup2_with_null(pool, &tmp, uri_bytes) uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0) if uri == NULL: raise SIPCoreError("Not a valid SIP URI: %s" % uri_str) return SIPURI_create(pjsip_uri_get_uri(uri)) cdef class FrozenSIPURI(BaseSIPURI): def __init__(self, object host not None, object user=None, object password=None, object port=None, bint secure=False, frozendict parameters not None=frozendict(), frozendict headers not None=frozendict()): if not self.initialized: if port is not None: port = int(port) if not (0 < port <= 65535): raise ValueError("Invalid port: %d" % port) self.host = host self.user = user self.password = password self.port = port self.secure = secure self.parameters = parameters self.headers = headers self.initialized = 1 else: raise TypeError("{0.__class__.__name__} is read-only".format(self)) property transport: def __get__(self): return self.parameters.get('transport', 'udp') def __hash__(self): return hash((self.user, self.password, self.host, self.port, self.secure, self.parameters, self.headers)) def __richcmp__(self, other, op): # since we define __hash__, __richcmp__ is not inherited (see https://docs.python.org/2/c-api/typeobj.html#c.PyTypeObject.tp_richcompare) if not isinstance(other, BaseSIPURI): return NotImplemented if op == 2: # 2 is == return all(getattr(self, name) == getattr(other, name) for name in ("user", "password", "host", "port", "secure", "parameters", "headers")) elif op == 3: # 3 is != return any(getattr(self, name) != getattr(other, name) for name in ("user", "password", "host", "port", "secure", "parameters", "headers")) else: operator_map = {0: '<', 1: '<=', 2: '==', 3: '!=', 4: '>', 5: '>='} raise TypeError("unorderable types: {0.__class__.__name__}() {2} {1.__class__.__name__}()".format(self, other, operator_map[op])) @classmethod def new(cls, BaseSIPURI sipuri): if isinstance(sipuri, FrozenSIPURI): return sipuri return cls(user=sipuri.user, password=sipuri.password, host=sipuri.host, port=sipuri.port, secure=sipuri.secure, parameters=frozendict(sipuri.parameters), headers=frozendict(sipuri.headers)) @classmethod def parse(cls, object uri_str): if not isinstance(uri_str, basestring): raise TypeError('a string or unicode is required') cdef bytes uri_bytes = str(uri_str) cdef pjsip_uri *uri = NULL cdef pj_pool_t *pool = NULL cdef pj_str_t tmp cdef char buffer[4096] pool = pj_pool_create_on_buf("FrozenSIPURI_parse", buffer, sizeof(buffer)) if pool == NULL: raise SIPCoreError("Could not allocate memory pool") pj_strdup2_with_null(pool, &tmp, uri_bytes) uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0) if uri == NULL: raise SIPCoreError("Not a valid SIP URI: %s" % uri_str) return FrozenSIPURI_create(pjsip_uri_get_uri(uri)) # Factory functions # cdef dict _pj_sipuri_to_dict(pjsip_sip_uri *uri): cdef object scheme cdef pj_str_t *scheme_str cdef pjsip_param *param cdef object parameters = {} cdef object headers = {} cdef object kwargs = dict(parameters=parameters, headers=headers) kwargs["host"] = _pj_str_to_str(uri.host) scheme = _pj_str_to_str(pjsip_uri_get_scheme(uri)[0]) if scheme == "sip": kwargs["secure"] = False elif scheme == "sips": kwargs["secure"] = True else: raise SIPCoreError("Not a sip(s) URI") if uri.user.slen > 0: kwargs["user"] = _pj_str_to_str(uri.user) if uri.passwd.slen > 0: kwargs["password"] = _pj_str_to_str(uri.passwd) if uri.port > 0: kwargs["port"] = uri.port if uri.user_param.slen > 0: parameters["user"] = _pj_str_to_str(uri.user_param) if uri.method_param.slen > 0: parameters["method"] = _pj_str_to_str(uri.method_param) if uri.transport_param.slen > 0: parameters["transport"] = _pj_str_to_str(uri.transport_param) if uri.ttl_param != -1: parameters["ttl"] = uri.ttl_param if uri.lr_param != 0: parameters["lr"] = None if uri.maddr_param.slen > 0: parameters["maddr"] = _pj_str_to_str(uri.maddr_param) param = ( &uri.other_param).next while param != &uri.other_param: if param.value.slen == 0: parameters[_pj_str_to_str(param.name)] = None else: parameters[_pj_str_to_str(param.name)] = _pj_str_to_str(param.value) param = ( param).next param = ( &uri.header_param).next while param != &uri.header_param: if param.value.slen == 0: headers[_pj_str_to_str(param.name)] = None else: headers[_pj_str_to_str(param.name)] = _pj_str_to_str(param.value) param = ( param).next return kwargs cdef SIPURI SIPURI_create(pjsip_sip_uri *uri): cdef dict kwargs = _pj_sipuri_to_dict(uri) return SIPURI(**kwargs) cdef FrozenSIPURI FrozenSIPURI_create(pjsip_sip_uri *uri): cdef dict kwargs = _pj_sipuri_to_dict(uri) kwargs["parameters"] = frozendict(kwargs["parameters"]) kwargs["headers"] = frozendict(kwargs["headers"]) return FrozenSIPURI(**kwargs) # Globals # cdef PJSTR _Credentials_scheme_digest = PJSTR("digest") ================================================ FILE: sipsimple/core/_core.invitation.pxi ================================================ import weakref from errno import EADDRNOTAVAIL, ENETUNREACH from operator import itemgetter # classes cdef class SDPPayloads: def __init__(self): self.proposed_local = None self.proposed_remote = None self.active_local = None self.active_remote = None cdef class StateCallbackTimer(Timer): def __init__(self, state, sub_state, rdata, tdata, originator): self.state = state self.sub_state = sub_state self.rdata = rdata self.tdata = tdata self.originator = originator cdef class SDPCallbackTimer(Timer): def __init__(self, int status, active_local, active_remote): self.status = status self.active_local = active_local self.active_remote = active_remote cdef class TransferStateCallbackTimer(Timer): def __init__(self, state, code, reason): self.state = state self.code = code self.reason = reason cdef class TransferResponseCallbackTimer(Timer): def __init__(self, method, rdata): self.method = method self.rdata = rdata cdef class TransferRequestCallbackTimer(Timer): def __init__(self, rdata): self.rdata = rdata class DialogID(tuple): call_id = property(itemgetter(0)) local_tag = property(itemgetter(1)) remote_tag = property(itemgetter(2)) def __new__(cls, call_id, local_tag, remote_tag): return tuple.__new__(cls, (call_id, local_tag, remote_tag)) def __repr__(self): return 'DialogID(call_id=%r, local_tag=%r, remote_tag=%r)' % self cdef class Invitation: expire_warning_time = 30 def __cinit__(self, *args, **kwargs): cdef int status self.weakref = weakref.ref(self) Py_INCREF(self.weakref) status = pj_mutex_create_recursive(_get_ua()._pjsip_endpoint._pool, "invitation_lock", &self._lock) if status != 0: raise PJSIPError("failed to create lock", status) pj_list_init( &self._route_set) self._invite_session = NULL self._dialog = NULL self._reinvite_transaction = NULL self._transfer_usage = NULL self._sdp_neg_status = -1 self._failed_response = 0 self._timer = None self._transfer_timeout_timer = None self._transfer_refresh_timer = None self.from_header = None self.to_header = None self.request_uri = None self.route_header = None self.local_contact_header = None self.remote_contact_header = None self.credentials = None self.sdp = SDPPayloads() self.remote_user_agent = None self.state = None self.sub_state = None self.transport = None self.transfer_state = None self.direction = None self.call_id = None self.peer_address = None cdef int init_incoming(self, PJSIPUA ua, pjsip_rx_data *rdata, unsigned int inv_options) except -1: cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_sdp_session_ptr_const sdp cdef pjsip_dialog *replaced_dialog = NULL cdef pjsip_tpselector tp_sel cdef pjsip_tx_data *tdata = NULL cdef PJSTR contact_str cdef char *error_message with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: # Validate replaces header with nogil: status = pjsip_replaces_verify_request(rdata, &replaced_dialog, 0, &tdata) if status != 0: if tdata != NULL: pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, tdata, NULL, NULL) else: pjsip_endpt_respond_stateless(ua._pjsip_endpoint._obj, rdata, 500, NULL, NULL, NULL) if status != 0: return 0 self.direction = "incoming" self.transport = rdata.tp_info.transport.type_name.lower() self.request_uri = FrozenSIPURI_create( pjsip_uri_get_uri(rdata.msg_info.msg.line.req.uri)) if _is_valid_ip(pj_AF_INET(), self.request_uri.host): self.local_contact_header = FrozenContactHeader(self.request_uri) else: self.local_contact_header = FrozenContactHeader(FrozenSIPURI(host=_pj_str_to_str(rdata.tp_info.transport.local_name.host), user=self.request_uri.user, port=rdata.tp_info.transport.local_name.port, parameters=(frozendict(transport=self.transport) if self.transport != "udp" else frozendict()))) contact_str = PJSTR(str(self.local_contact_header.body)) tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT tp_sel.u.transport = rdata.tp_info.transport with nogil: status = pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(), rdata, &contact_str.pj_str, &self._dialog) if status != 0: error_message = "Could not create dialog for new INVITE session" else: pjsip_dlg_set_transport(self._dialog, &tp_sel) status = pjsip_inv_create_uas(self._dialog, rdata, NULL, inv_options, &self._invite_session) pjsip_dlg_dec_lock(self._dialog) if status != 0: error_message = "Could not create new INVITE session" else: status = pjsip_inv_initial_answer(self._invite_session, rdata, 100, NULL, NULL, &tdata) if status != 0: error_message = "Could not create initial (unused) response to INVITE" else: pjsip_tx_data_dec_ref(tdata) if status != 0: raise PJSIPError(error_message, status) if self._invite_session.neg != NULL: if pjmedia_sdp_neg_get_state(self._invite_session.neg) == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER: pjmedia_sdp_neg_get_neg_remote(self._invite_session.neg, &sdp) self.sdp.proposed_remote = FrozenSDPSession_create(sdp) self._invite_session.sdp_neg_flags = PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE self._invite_session.mod_data[ua._module.id] = self.weakref self.call_id = _pj_str_to_str(self._dialog.call_id.id) self.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) event_dict = dict(obj=self, prev_state=self.state, state="incoming", originator="remote") _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) self.state = "incoming" self.remote_user_agent = event_dict['headers']['User-Agent'].body if 'User-Agent' in event_dict['headers'] else None try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: pass _add_event("SIPInvitationChangedState", event_dict) self.from_header = FrozenFromHeader_create(rdata.msg_info.from_hdr) self.to_header = FrozenToHeader_create(rdata.msg_info.to_hdr) except: if self._invite_session != NULL: with nogil: pjsip_inv_terminate(self._invite_session, 500, 0) self._invite_session = NULL elif self._dialog != NULL: with nogil: pjsip_dlg_terminate(self._dialog) self._dialog = NULL else: with nogil: status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 500, NULL, &tdata) if status != 0: error_message = "Could not create response" else: status = pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, tdata, NULL, NULL) if status != 0: pjsip_tx_data_dec_ref(tdata) error_message = "Could not send response" if status != 0: raise PJSIPError(error_message, status) raise finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int process_incoming_transfer(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: global _incoming_transfer_cb global _event_hdr_name cdef int status, status2 cdef dict rdata_dict = dict(obj=self) cdef pjsip_tx_data *tdata cdef pjsip_transaction *initial_tsx cdef Timer timer cdef char *error_message if self._transfer_usage != NULL: with nogil: status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 480, NULL, &tdata) if status != 0: error_message = "Could not create response" else: status = pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, tdata, NULL, NULL) if status != 0: pjsip_tx_data_dec_ref(tdata) error_message = "Could not send response" if status != 0: raise PJSIPError(error_message, status) return 0 _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) try: refer_to_hdr = rdata_dict["headers"]["Refer-To"] SIPURI.parse(refer_to_hdr.uri) except (KeyError, SIPCoreError): with nogil: status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 400, NULL, &tdata) if status != 0: error_message = "Could not create response" else: status = pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, tdata, NULL, NULL) if status != 0: pjsip_tx_data_dec_ref(tdata) error_message = "Could not send response" if status != 0: raise PJSIPError(error_message, status) return 0 try: self._set_transfer_state("INCOMING") _add_event("SIPInvitationTransferNewIncoming", rdata_dict) # PJSIP event framework needs an Event header, even if it's not needed for REFER, so we insert a fake one event_header = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_event_hdr_name.pj_str, NULL) if event_header == NULL: event_header = pjsip_event_hdr_create(rdata.tp_info.pool) event_header.event_type = _refer_event.pj_str pjsip_msg_add_hdr(rdata.msg_info.msg, event_header) initial_tsx = pjsip_rdata_get_tsx(rdata) with nogil: status = pjsip_evsub_create_uas(self._dialog, &_incoming_transfer_cb, rdata, 0, &self._transfer_usage) if status != 0: pjsip_tsx_terminate(initial_tsx, 500) error_message = "Could not create incoming REFER session" else: self._transfer_usage_role = PJSIP_ROLE_UAS pjsip_evsub_set_mod_data(self._transfer_usage, ua._event_module.id, self.weakref) status = pjsip_dlg_create_response(self._dialog, rdata, 202, NULL, &tdata) if status != 0: pjsip_tsx_terminate(initial_tsx, 500) error_message = "Could not create response for incoming REFER" else: pjsip_evsub_update_expires(self._transfer_usage, 90) status = pjsip_dlg_send_response(self._dialog, initial_tsx, tdata) if status != 0: status2 = pjsip_dlg_modify_response(self._dialog, tdata, 500, NULL) if status2 != 0: error_message = "Could not modify response" status = status2 else: pjsip_tx_data_dec_ref(tdata) # pjsip_dlg_modify_response() increases ref count unnecessarily error_message = "Could not send response" if status != 0: raise PJSIPError(error_message, status) except PJSIPError, e: code = 0 reason = e.args[0] if self._transfer_usage != NULL: with nogil: pjsip_evsub_terminate(self._transfer_usage, 0) # Manually trigger the state callback since we handle the timeout ourselves state_timer = TransferStateCallbackTimer("TERMINATED", code, reason) state_timer.schedule(0, self._transfer_cb_state, self) raise else: self._set_transfer_state("ACTIVE") _add_event("SIPInvitationTransferDidStart", dict(obj=self)) timer = Timer() timer.schedule(0, self._start_incoming_transfer, self) return 0 cdef int process_incoming_options(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: cdef pjsip_tx_data *tdata cdef pjsip_transaction *initial_tsx cdef int status cdef char *error_message initial_tsx = pjsip_rdata_get_tsx(rdata) with nogil: status = pjsip_dlg_create_response(self._dialog, rdata, 200, NULL, &tdata) if status != 0: pjsip_tsx_terminate(initial_tsx, 500) error_message = "Could not create response for incoming OPTIONS" else: status = pjsip_dlg_send_response(self._dialog, initial_tsx, tdata) if status != 0: error_message = "Could not send response" if status != 0: raise PJSIPError(error_message, status) def send_invite(self, SIPURI request_uri not None, FromHeader from_header not None, ToHeader to_header not None, RouteHeader route_header not None, ContactHeader contact_header not None, SDPSession sdp not None, Credentials credentials=None, list extra_headers not None=list(), timeout=None): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_sdp_session *local_sdp cdef pjsip_cred_info *cred_info cdef pjsip_replaces_hdr *pj_replaces_hdr cdef pjsip_route_hdr *route_set cdef pjsip_tx_data *tdata cdef PJSIPUA ua cdef PJSTR contact_str cdef PJSTR from_header_str cdef PJSTR to_header_str cdef PJSTR request_uri_str ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: route_set = &self._route_set if self.state is not None: raise SIPCoreInvalidStateError('Can only transition to the "outgoing" state from the "None" state, currently in the "%s" state' % self.state) if timeout is not None and timeout <= 0: raise ValueError("Timeout value must be positive") self.transport = route_header.uri.transport self.direction = "outgoing" self.credentials = FrozenCredentials.new(credentials) if credentials is not None else None self.request_uri = FrozenSIPURI.new(request_uri) self.route_header = FrozenRouteHeader.new(route_header) self.route_header.uri.parameters.dict["lr"] = None # always send lr parameter in Route header self.route_header.uri.parameters.dict["hide"] = None # always hide Route header self.local_contact_header = FrozenContactHeader.new(contact_header) self.sdp.proposed_local = FrozenSDPSession.new(sdp) if sdp is not None else None from_header_parameters = from_header.parameters.copy() from_header_parameters.pop("tag", None) from_header.parameters = {} from_header_str = PJSTR(from_header.body) to_header_parameters = to_header.parameters.copy() to_header_parameters.pop("tag", None) to_header.parameters = {} to_header_str = PJSTR(to_header.body) contact_str = PJSTR(str(self.local_contact_header.body)) request_uri_str = PJSTR(str(request_uri)) with nogil: status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from_header_str.pj_str, &contact_str.pj_str, &to_header_str.pj_str, &request_uri_str.pj_str, &self._dialog) if status != 0: raise PJSIPError("Could not create dialog for outgoing INVITE session", status) with nogil: pjsip_dlg_inc_lock(self._dialog) if contact_header.expires is not None: self._dialog.local.contact.expires = contact_header.expires if contact_header.q is not None: self._dialog.local.contact.q1000 = int(contact_header.q*1000) contact_parameters = contact_header.parameters.copy() contact_parameters.pop("q", None) contact_parameters.pop("expires", None) _dict_to_pjsip_param(contact_parameters, &self._dialog.local.contact.other_param, self._dialog.pool) _dict_to_pjsip_param(from_header_parameters, &self._dialog.local.info.other_param, self._dialog.pool) _dict_to_pjsip_param(to_header_parameters, &self._dialog.remote.info.other_param, self._dialog.pool) self.from_header = FrozenFromHeader_create(self._dialog.local.info) self.to_header = FrozenToHeader.new(to_header) self.call_id = _pj_str_to_str(self._dialog.call_id.id) local_sdp = self.sdp.proposed_local.get_sdp_session() if sdp is not None else NULL with nogil: status = pjsip_inv_create_uac(self._dialog, local_sdp, 0, &self._invite_session) if status != 0: raise PJSIPError("Could not create outgoing INVITE session", status) self._invite_session.sdp_neg_flags = PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE self._invite_session.mod_data[ua._module.id] = self.weakref if self.credentials is not None: cred_info = self.credentials.get_cred_info() with nogil: status = pjsip_auth_clt_set_credentials(&self._dialog.auth_sess, 1, cred_info) if status != 0: raise PJSIPError("Could not set credentials for INVITE session", status) _BaseRouteHeader_to_pjsip_route_hdr(self.route_header, &self._route_header, self._dialog.pool) pj_list_insert_after( &self._route_set, &self._route_header) with nogil: status = pjsip_dlg_set_route_set(self._dialog, route_set) if status != 0: raise PJSIPError("Could not set route for INVITE session", status) with nogil: status = pjsip_inv_invite(self._invite_session, &tdata) if status != 0: raise PJSIPError("Could not create INVITE message", status) replaces_headers = [header for header in extra_headers if isinstance(header, BaseReplacesHeader)] if len(replaces_headers) > 1: raise SIPCoreError("Only one Replaces header is allowed") try: replaces_header = replaces_headers[0] except IndexError: pass else: extra_headers.remove(replaces_header) pj_replaces_hdr = pjsip_replaces_hdr_create(self._dialog.pool) _str_to_pj_str(replaces_header.call_id, &pj_replaces_hdr.call_id) _str_to_pj_str(replaces_header.to_tag, &pj_replaces_hdr.to_tag) _str_to_pj_str(replaces_header.from_tag, &pj_replaces_hdr.from_tag) _dict_to_pjsip_param(replaces_header.parameters, &pj_replaces_hdr.other_param, self._dialog.pool) pjsip_msg_add_hdr(tdata.msg, pj_replaces_hdr) _add_headers_to_tdata(tdata, extra_headers) with nogil: status = pjsip_inv_send_msg(self._invite_session, tdata) if status != 0: raise PJSIPError("Could not send initial INVITE", status) if timeout is not None: self._timer = Timer() self._timer.schedule(timeout, self._cb_timer_disconnect, self) with nogil: pjsip_dlg_dec_lock(self._dialog) except Exception, e: if isinstance(e, PJSIPError) and e.errno == EADDRNOTAVAIL: self._invite_session = NULL pjsip_dlg_dec_lock(self._dialog) self._dialog = NULL raise if self._invite_session != NULL: pjsip_inv_terminate(self._invite_session, 500, 0) self._invite_session = NULL elif self._dialog != NULL: pjsip_dlg_dec_lock(self._dialog) self._dialog = NULL raise finally: with nogil: pj_mutex_unlock(lock) def send_response(self, int code, str reason=None, BaseContactHeader contact_header=None, BaseSDPSession sdp=None, list extra_headers not None=list()): cdef int status cdef int clean_tdata = 0 cdef pj_mutex_t *lock = self._lock cdef pj_str_t reason_str cdef pjmedia_sdp_session_ptr_const lsdp = NULL cdef pjmedia_sdp_session *local_sdp cdef pjsip_inv_session *invite_session cdef pjsip_msg_body *body cdef pjsip_tx_data *tdata cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: invite_session = self._invite_session if reason is not None: _str_to_pj_str(reason, &reason_str) if self.state not in ("incoming", "early", "connected"): raise SIPCoreInvalidStateError('Can only send response from the "incoming", "early" and "connected" states current in the "%s" state.' % self.state) if self.state == "early" and self.direction != "incoming": raise SIPCoreInvalidStateError('Cannot send response in the "early" state for an outgoing INVITE') if self.state == "connected" and self.sub_state not in ("received_proposal", "received_proposal_request"): raise SIPCoreInvalidStateError('Cannot send response in the "connected" state if a proposal has not been received') if contact_header is not None: self._update_contact_header(contact_header) if 200 <= code < 300 and sdp is None: raise SIPCoreError("Local SDP needs to be set for a positive response") if code >= 300 and sdp is not None: raise SIPCoreError("Local SDP cannot be specified for a negative response") self.sdp.proposed_local = FrozenSDPSession.new(sdp) if sdp is not None else None local_sdp = self.sdp.proposed_local.get_sdp_session() if sdp is not None else NULL if sdp is not None and self.sdp.proposed_remote is None: # There was no remote proposal, this is a reply with an offer with nogil: status = pjmedia_sdp_neg_modify_local_offer(self._dialog.pool, invite_session.neg, local_sdp) if status != 0: raise PJSIPError("Could not modify local SDP offer", status) # Retrieve the "fixed" offer from negotiator pjmedia_sdp_neg_get_neg_local(invite_session.neg, &lsdp) local_sdp = lsdp with nogil: status = pjsip_inv_answer(invite_session, code, &reason_str if reason is not None else NULL, local_sdp, &tdata) if status != 0: raise PJSIPError("Could not create %d reply to INVITE" % code, status) _add_headers_to_tdata(tdata, extra_headers) with nogil: status = pjsip_inv_send_msg(invite_session, tdata) if status != 0: exc = PJSIPError("Could not send %d response" % code, status) if sdp is not None and self.sdp.proposed_remote is not None and exc.errno in (EADDRNOTAVAIL, ENETUNREACH): self._failed_response = 1 raise exc self._failed_response = 0 finally: with nogil: pj_mutex_unlock(lock) def send_reinvite(self, BaseContactHeader contact_header=None, BaseSDPSession sdp=None, list extra_headers not None=list()): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_sdp_session *local_sdp cdef pjsip_inv_session *invite_session cdef pjsip_tx_data *tdata cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: invite_session = self._invite_session if self.state != "connected": raise SIPCoreError('Can only send re-INVITE in "connected" state, not "%s" state' % self.state) if self.sub_state != "normal": raise SIPCoreError('Can only send re-INVITE if no another re-INVITE transaction is active') if contact_header is not None: self._update_contact_header(contact_header) self.sdp.proposed_local = FrozenSDPSession.new(sdp) if sdp is not None else self.sdp.active_local local_sdp = self.sdp.proposed_local.get_sdp_session() with nogil: status = pjsip_inv_reinvite(invite_session, NULL, local_sdp, &tdata) if status != 0: raise PJSIPError("Could not create re-INVITE message", status) _add_headers_to_tdata(tdata, extra_headers) with nogil: status = pjsip_inv_send_msg(invite_session, tdata) if status != 0: raise PJSIPError("Could not send re-INVITE", status) self._failed_response = 0 # TODO: use a callback tiner here instead? self._reinvite_transaction = self._invite_session.invite_tsx self.sub_state = "sent_proposal" event_dict = dict(obj=self, prev_state="connected", state="connected", prev_sub_state="normal", sub_state="sent_proposal", originator="local") _pjsip_msg_to_dict(tdata.msg, event_dict) _add_event("SIPInvitationChangedState", event_dict) finally: with nogil: pj_mutex_unlock(lock) def cancel_reinvite(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjsip_inv_session *invite_session cdef pjsip_tx_data *tdata cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: invite_session = self._invite_session if not self.sub_state == "sent_proposal": raise SIPCoreError("re-INVITE can only be cancelled if INVITE session is in 'sent_proposal' sub state") if self._invite_session == NULL: raise SIPCoreError("INVITE session is not active") if self._reinvite_transaction == NULL: raise SIPCoreError("there is no active re-INVITE transaction") with nogil: status = pjsip_inv_cancel_reinvite(invite_session, &tdata) if status != 0: raise PJSIPError("Could not create message to CANCEL re-INVITE transaction", status) if tdata != NULL: with nogil: status = pjsip_inv_send_msg(invite_session, tdata) if status != 0: raise PJSIPError("Could not send %s" % _pj_str_to_str(tdata.msg.line.req.method.name), status) finally: with nogil: pj_mutex_unlock(lock) def transfer(self, SIPURI target_uri, object replaced_dialog_id=None, list extra_headers not None=list()): global _refer_event global _refer_method cdef int status cdef PJSIPUA ua cdef pj_mutex_t *lock = self._lock cdef pjsip_method refer_method cdef pjsip_tx_data *tdata cdef dict tdata_dict = dict(obj=self) ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state != "connected": raise SIPCoreError('Can only start transfer in "connected" state, not "%s" state' % self.state) if self._transfer_usage != NULL: raise SIPCoreError('Another transfer is in progress') with nogil: status = pjsip_evsub_create_uac(self._dialog, &_transfer_cb, &_refer_event.pj_str, PJSIP_EVSUB_NO_EVENT_ID, &self._transfer_usage) if status != 0: raise PJSIPError("Could not create REFER", status) self._transfer_usage_role = PJSIP_ROLE_UAC pjsip_evsub_set_mod_data(self._transfer_usage, ua._event_module.id, self.weakref) pjsip_method_init_np(&refer_method, &_refer_method.pj_str) with nogil: status = pjsip_evsub_initiate(self._transfer_usage, &refer_method, -1, &tdata) if status != 0: raise PJSIPError("Could not create REFER message", status) if replaced_dialog_id is not None and None not in replaced_dialog_id: target_uri.headers["Replaces"] = "%s;from-tag=%s;to-tag=%s" % replaced_dialog_id refer_to_header = ReferToHeader(str(target_uri)) _add_headers_to_tdata(tdata, [refer_to_header, Header('Referred-By', str(self.local_identity.uri))]) _add_headers_to_tdata(tdata, extra_headers) # We can't remove the Event header or PJSIP will fail to match responses to this request _remove_headers_from_tdata(tdata, ["Expires"]) with nogil: status = pjsip_evsub_send_request(self._transfer_usage, tdata) if status != 0: raise PJSIPError("Could not send REFER message", status) _pjsip_msg_to_dict(tdata.msg, tdata_dict) _add_event("SIPInvitationTransferNewOutgoing", tdata_dict) self._transfer_timeout_timer = Timer() self._transfer_timeout_timer.schedule(90, self._transfer_cb_timeout_timer, self) finally: with nogil: pj_mutex_unlock(lock) def notify_transfer_progress(self, int code, str reason=None): cdef int status cdef PJSIPUA ua cdef pj_mutex_t *lock = self._lock ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._transfer_usage == NULL: raise SIPCoreError("No transfer is in progress") if self._transfer_usage_role != PJSIP_ROLE_UAS: raise SIPCoreError("Transfer progress can only be notified by the transfer UAS") self._set_sipfrag_payload(code, reason) if 200 <= code < 700: self._terminate_transfer_uas() else: self._send_notify() finally: with nogil: pj_mutex_unlock(lock) def end(self, list extra_headers not None=list(), timeout=None): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjsip_inv_session *invite_session cdef pjsip_tx_data *tdata cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: invite_session = self._invite_session if self.state == "disconnected": return if self.state == "disconnecting": raise SIPCoreError('INVITE session is already in the "disconnecting" state') if self._invite_session == NULL: raise SIPCoreError("INVITE session is not active") if self.state not in ("outgoing", "early", "connecting", "connected"): raise SIPCoreError('Can only end the INVITE dialog from the "outgoing", "early", "connecting" and "connected" states' + 'current in the "%s" state.' % self.state) if self.state == "early" and self.direction != "outgoing": raise SIPCoreError('Cannot end incoming INVITE dialog while in the "early" state') if timeout is not None and timeout <= 0: raise ValueError("Timeout value cannot be negative") # End ongoing transfer self._terminate_transfer() with nogil: status = pjsip_inv_end_session(invite_session, 0, NULL, &tdata) if status != 0: raise PJSIPError("Could not create message to end INVITE session", status) if tdata != NULL: _add_headers_to_tdata(tdata, extra_headers) with nogil: status = pjsip_inv_send_msg(invite_session, tdata) if status != 0: raise PJSIPError("Could not send %s" % _pj_str_to_str(tdata.msg.line.req.method.name), status) if self._timer is not None: self._timer.cancel() self._timer = None if timeout is not None and timeout > 0: self._timer = Timer() self._timer.schedule(timeout, self._cb_timer_disconnect, self) event_dict = dict(obj=self, prev_state=self.state, state="disconnecting", originator="local") if self.state == "connected": event_dict["prev_sub_state"] = self.sub_state self.state = "disconnecting" self.sub_state = None if tdata != NULL: _pjsip_msg_to_dict(tdata.msg, event_dict) _add_event("SIPInvitationChangedState", event_dict) finally: with nogil: pj_mutex_unlock(lock) property local_identity: def __get__(self): if self.direction == 'outgoing': return self.from_header elif self.direction == 'incoming': return self.to_header else: return None property remote_identity: def __get__(self): if self.direction == 'incoming': return self.from_header elif self.direction == 'outgoing': return self.to_header else: return None property dialog_id: def __get__(self): local_tag = remote_tag = None if self.local_identity is not None: local_tag = self.local_identity.tag if self.remote_identity is not None: remote_tag = self.remote_identity.tag return DialogID(self.call_id, local_tag, remote_tag) cdef PJSIPUA _check_ua(self): try: return _get_ua() except: self.state = "disconnected" self.sub_state = None self._dialog = NULL self._invite_session = NULL self._reinvite_transaction = NULL cdef int _do_dealloc(self) except -1: cdef int status cdef pj_mutex_t *lock = self._lock cdef pjsip_inv_session *invite_session cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: invite_session = self._invite_session if self._invite_session != NULL: self._invite_session.mod_data[ua._module.id] = NULL if self.state != "disconnecting": with nogil: pjsip_inv_terminate(invite_session, 481, 0) self._dialog = NULL self._invite_session = NULL self._reinvite_transaction = NULL if self._timer is not None: self._timer.cancel() self._timer = None finally: with nogil: pj_mutex_unlock(lock) return 0 def __dealloc__(self): cdef Timer timer self._do_dealloc() if self._lock != NULL: pj_mutex_destroy(self._lock) timer = Timer() try: timer.schedule(60, deallocate_weakref, self.weakref) except SIPCoreError: pass cdef int _update_contact_header(self, BaseContactHeader contact_header) except -1: # The PJSIP functions called here don't do much, so there is no need to call them # without the gil. cdef pj_str_t contact_str_pj cdef pjsip_uri *contact contact_str = str(contact_header.uri) if contact_header.display_name: contact_str = "%s <%s>" % (contact_header.display_name.encode('utf-8'), contact_str) pj_strdup2_with_null(self._dialog.pool, &contact_str_pj, contact_str) contact = pjsip_parse_uri(self._dialog.pool, contact_str_pj.ptr, contact_str_pj.slen, PJSIP_PARSE_URI_AS_NAMEADDR) if contact == NULL: raise SIPCoreError("Not a valid Contact header: %s" % contact_str) self._dialog.local.contact = pjsip_contact_hdr_create(self._dialog.pool) self._dialog.local.contact.uri = contact if contact_header.expires is not None: self._dialog.local.contact.expires = contact_header.expires if contact_header.q is not None: self._dialog.local.contact.q1000 = int(contact_header.q*1000) parameters = contact_header.parameters.copy() parameters.pop("q", None) parameters.pop("expires", None) _dict_to_pjsip_param(parameters, &self._dialog.local.contact.other_param, self._dialog.pool) self.local_contact_header = FrozenContactHeader.new(contact_header) return 0 cdef int _fail(self, PJSIPUA ua) except -1: cdef Timer timer ua._handle_exception(0) if self._transfer_usage != NULL: with nogil: pjsip_evsub_terminate(self._transfer_usage, 0) pjsip_evsub_set_mod_data(self._transfer_usage, ua._event_module.id, NULL) if self._transfer_timeout_timer is not None: self._transfer_timeout_timer.cancel() self._transfer_timeout_timer = None if self._transfer_refresh_timer is not None: self._transfer_refresh_timer.cancel() self._transfer_refresh_timer = None self._transfer_usage = NULL _add_event("SIPInvitationTransferDidFail", dict(obj=self, code=0, reason="internal error")) self._invite_session.mod_data[ua._module.id] = NULL if self.state != "disconnected": event_dict = dict(obj=self, prev_state=self.state, state="disconnected", originator="local", code=0, reason="internal error", disconnect_reason="internal error") if self.state == "connected": event_dict["prev_sub_state"] = self.sub_state self.state = "disconnected" self.sub_state = None _add_event("SIPInvitationChangedState", event_dict) # calling do_dealloc from within a callback makes PJSIP crash # the handler will be executed after pjsip_endpt_handle_events returns timer = Timer() timer.schedule(0, self._cb_postpoll_fail, self) return 0 cdef int _cb_state(self, StateCallbackTimer timer) except -1: cdef int status cdef bint pjsip_error = False cdef pj_mutex_t *lock = self._lock cdef pjmedia_sdp_session_ptr_const sdp cdef pjsip_inv_session *invite_session cdef object state cdef object sub_state cdef object rdata cdef object tdata cdef object originator cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: invite_session = self._invite_session state = timer.state sub_state = timer.sub_state rdata = timer.rdata tdata = timer.tdata originator = timer.originator if state != "early" and state == self.state and sub_state == self.sub_state: return 0 if state == "connected": if self.state == "connecting" and self._sdp_neg_status != 0: self.end() return 0 if state == "disconnected" and self.state != "disconnecting": # the invite session may have been destroyed if it failed if not self._invite_session: return 0 # we either sent a cancel or a negative reply to an incoming INVITE if self._invite_session.cancelling or (self.state in ("incoming", "early") and self.direction == "incoming" and rdata is None): # we caused the disconnect so send the transition to the disconnecting state pjsip_error = True event_dict = dict(obj=self, prev_state=self.state, state="disconnecting", originator="local") self.state = "disconnecting" _add_event("SIPInvitationChangedState", event_dict) if self.direction == "outgoing" and state in ('connecting', 'connected') and self.state in ('outgoing', 'early') and rdata is not None: self.to_header = rdata['headers']['To'] if self.direction == "incoming" and state in ('connecting', 'connected') and self.state in ('incoming', 'early') and tdata is not None: self.to_header = tdata['headers']['To'] event_dict = dict(obj=self, prev_state=self.state, state=state) if self.state == "connected": event_dict["prev_sub_state"] = self.sub_state if state == "connected": event_dict["sub_state"] = sub_state event_dict["originator"] = originator if rdata is not None: event_dict.update(rdata) if tdata is not None: event_dict.update(tdata) if rdata is None and tdata is None: event_dict['headers'] = dict() event_dict['body'] = None if self.remote_user_agent is None and state in ('connecting', 'connected') and rdata is not None: if 'User-Agent' in event_dict['headers']: self.remote_user_agent = event_dict['headers']['User-Agent'].body elif 'Server' in event_dict['headers']: self.remote_user_agent = event_dict['headers']['Server'].body if state not in ('disconnecting', 'disconnected') and rdata is not None: try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: pass if state == "connected": if sub_state == "received_proposal": self._reinvite_transaction = self._invite_session.invite_tsx if pjmedia_sdp_neg_get_state(self._invite_session.neg) == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER: pjmedia_sdp_neg_get_neg_remote(self._invite_session.neg, &sdp) self.sdp.proposed_remote = FrozenSDPSession_create(sdp) elif sub_state == "sent_proposal": if pjmedia_sdp_neg_get_state(self._invite_session.neg) == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER: pjmedia_sdp_neg_get_neg_local(self._invite_session.neg, &sdp) self.sdp.proposed_local = FrozenSDPSession_create(sdp) elif sub_state == "received_proposal_request": self._reinvite_transaction = self._invite_session.invite_tsx if pjmedia_sdp_neg_get_state(self._invite_session.neg) == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER: pjmedia_sdp_neg_get_neg_local(self._invite_session.neg, &sdp) self.sdp.proposed_local = FrozenSDPSession_create(sdp) elif self.sub_state in ("received_proposal", "sent_proposal", "received_proposal_request"): if (rdata, tdata) == (None, None): event_dict['code'] = 408 event_dict['reason'] = 'Request Timeout' if pjmedia_sdp_neg_get_state(self._invite_session.neg) in (PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER): pjmedia_sdp_neg_cancel_offer(self._invite_session.neg) self._reinvite_transaction = NULL if state == "disconnected": event_dict["disconnect_reason"] = "user request" if not pjsip_error else "internal error" event_dict["code"] = self._invite_session.cause if self._invite_session.cause > 0: event_dict["reason"] = _pj_str_to_str(self._invite_session.cause_text) else: event_dict["reason"] = "" if not self._invite_session.cancelling and rdata is None and self._invite_session.cause > 0: # pjsip internally generates 408 and 503 if self._invite_session.cause == 408: if self.direction == "incoming" and self.state == "connecting": event_dict["disconnect_reason"] = "missing ACK" else: event_dict["disconnect_reason"] = "timeout" else: event_dict["disconnect_reason"] = _pj_str_to_str(self._invite_session.cause_text) elif self._invite_session.cancelling and rdata is None and self._invite_session.cause == 408 and self.state == "disconnecting": # silly pjsip sets cancelling field when we call pjsip_inv_end_session in end even if we send a BYE event_dict["disconnect_reason"] = "timeout" elif rdata is not None and 'Reason' in event_dict['headers']: try: reason = event_dict['headers']['Reason'].text if reason: event_dict["disconnect_reason"] = reason except (ValueError, IndexError): pass if self._transfer_usage != NULL: with nogil: pjsip_evsub_terminate(self._transfer_usage, 0) pjsip_evsub_set_mod_data(self._transfer_usage, ua._event_module.id, NULL) if self._transfer_timeout_timer is not None: self._transfer_timeout_timer.cancel() self._transfer_timeout_timer = None if self._transfer_refresh_timer is not None: self._transfer_refresh_timer.cancel() self._transfer_refresh_timer = None self._transfer_usage = NULL _add_event("SIPInvitationTransferDidFail", dict(obj=self, code=0, reason="invite dialog ended")) self._invite_session.mod_data[ua._module.id] = NULL self._invite_session = NULL self._dialog = NULL if self._timer is not None: self._timer.cancel() self._timer = None elif state in ("early", "connecting") and self._timer is not None: self._timer.cancel() self._timer = None self.state = state self.sub_state = sub_state _add_event("SIPInvitationChangedState", event_dict) finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _cb_sdp_done(self, SDPCallbackTimer timer) except -1: cdef int status cdef pj_mutex_t *lock = self._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._failed_response == 1: return 0 self._sdp_neg_status = timer.status self.sdp.proposed_local = None self.sdp.proposed_remote = None if timer.status == 0: self.sdp.active_local = timer.active_local self.sdp.active_remote = timer.active_remote if self.state in ["disconnecting", "disconnected"]: return 0 event_dict = dict(obj=self, succeeded=timer.status == 0) if timer.status == 0: event_dict["local_sdp"] = self.sdp.active_local event_dict["remote_sdp"] = self.sdp.active_remote else: event_dict["error"] = _pj_status_to_str(timer.status) _add_event("SIPInvitationGotSDPUpdate", event_dict) finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _cb_timer_disconnect(self, timer) except -1: cdef pjsip_inv_session *invite_session = self._invite_session with nogil: pjsip_inv_terminate(invite_session, 408, 1) cdef int _cb_postpoll_fail(self, timer) except -1: self._do_dealloc() cdef int _start_incoming_transfer(self, timer) except -1: cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: self._set_sipfrag_payload(100, "Trying") self._send_notify() finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _terminate_transfer(self) except -1: if self._transfer_usage == NULL: return 0 if self._transfer_usage_role == PJSIP_ROLE_UAC: self._terminate_transfer_uac() else: self._terminate_transfer_uas() cdef int _terminate_transfer_uac(self) except -1: cdef pjsip_tx_data *tdata cdef int status cdef TransferStateCallbackTimer state_timer try: with nogil: status = pjsip_evsub_initiate(self._transfer_usage, NULL, 0, &tdata) if status != 0: raise PJSIPError("Could not create SUBSCRIBE message", status) with nogil: status = pjsip_evsub_send_request(self._transfer_usage, tdata) if status != 0: raise PJSIPError("Could not send SUBSCRIBE message", status) if self._transfer_timeout_timer is not None: self._transfer_timeout_timer.cancel() self._transfer_timeout_timer = None if self._transfer_refresh_timer is not None: self._transfer_refresh_timer.cancel() self._transfer_refresh_timer = None self._transfer_timeout_timer = Timer() self._transfer_timeout_timer.schedule(1, self._transfer_cb_timeout_timer, self) except PJSIPError, e: if self._transfer_usage != NULL: code = 0 reason = e.args[0] with nogil: pjsip_evsub_terminate(self._transfer_usage, 0) # Manually trigger the state callback since we handle the timeout ourselves state_timer = TransferStateCallbackTimer("TERMINATED", code, reason) state_timer.schedule(0, self._transfer_cb_state, self) cdef int _terminate_transfer_uas(self) except -1: global sipfrag_re cdef int code cdef TransferStateCallbackTimer state_timer if self.transfer_state == "TERMINATED": return 0 self._set_transfer_state("TERMINATED") self._send_notify() with nogil: pjsip_evsub_terminate(self._transfer_usage, 0) match = sipfrag_re.match(self._sipfrag_payload.str) code = int(match.group('code')) reason = match.group('reason') state_timer = TransferStateCallbackTimer("TERMINATED", code, reason) state_timer.schedule(0, self._transfer_cb_state, self) cdef int _set_transfer_state(self, str state) except -1: cdef str prev_state prev_state = self.transfer_state self.transfer_state = state if prev_state != state: _add_event("SIPInvitationTransferChangedState", dict(obj=self, prev_state=prev_state, state=state)) cdef int _set_sipfrag_payload(self, int code, str status) except -1: cdef str content if status is None: try: status = sip_status_messages[code] except IndexError: status = "Unknown" content = "SIP/2.0 %d %s\r\n" % (code, status) self._sipfrag_payload = PJSTR(content) cdef int _send_notify(self) except -1: cdef pjsip_evsub_state state cdef pj_str_t *reason_p = NULL cdef pjsip_tx_data *tdata cdef int status cdef dict _sipfrag_version = dict(version="2.0") cdef PJSTR _content_type = PJSTR("message") cdef PJSTR _content_subtype = PJSTR("sipfrag") cdef PJSTR noresource = PJSTR("noresource") cdef PJSTR content if self.transfer_state == "ACTIVE": state = PJSIP_EVSUB_STATE_ACTIVE else: state = PJSIP_EVSUB_STATE_TERMINATED reason_p = &noresource.pj_str with nogil: status = pjsip_evsub_notify(self._transfer_usage, state, NULL, reason_p, &tdata) if status != 0: raise PJSIPError("Could not create NOTIFY request", status) if self.transfer_state in ("ACTIVE", "TERMINATED"): tdata.msg.body = pjsip_msg_body_create(tdata.pool, &_content_type.pj_str, &_content_subtype.pj_str, &self._sipfrag_payload.pj_str) _dict_to_pjsip_param(_sipfrag_version, &tdata.msg.body.content_type.param, tdata.pool) with nogil: status = pjsip_evsub_send_request(self._transfer_usage, tdata) if status != 0: raise PJSIPError("Could not send NOTIFY request", status) return 0 cdef int _transfer_cb_timeout_timer(self, timer) except -1: global sip_status_messages cdef int code cdef str reason cdef int status cdef TransferStateCallbackTimer state_timer cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._transfer_usage != NULL: code = PJSIP_SC_TSX_TIMEOUT reason = sip_status_messages[PJSIP_SC_TSX_TIMEOUT] with nogil: pjsip_evsub_terminate(self._transfer_usage, 0) # Manually trigger the state callback since we handle the timeout ourselves state_timer = TransferStateCallbackTimer("TERMINATED", code, reason) state_timer.schedule(0, self._transfer_cb_state, self) finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _transfer_cb_refresh_timer(self, timer) except -1: cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: self._terminate_transfer() finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _transfer_cb_state(self, TransferStateCallbackTimer timer) except -1: cdef int status cdef str prev_state cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: prev_state = self.transfer_state self._set_transfer_state(timer.state) if timer.state == "ACCEPTED" and prev_state == "SENT": _add_event("SIPInvitationTransferDidStart", dict(obj=self)) elif timer.state == "TERMINATED": # If a NOTIFY is rejected with 408 or 481 PJSIP will erase the subscription if self._transfer_usage != NULL: pjsip_evsub_set_mod_data(self._transfer_usage, ua._event_module.id, NULL) if self._transfer_timeout_timer is not None: self._transfer_timeout_timer.cancel() self._transfer_timeout_timer = None if self._transfer_refresh_timer is not None: self._transfer_refresh_timer.cancel() self._transfer_refresh_timer = None self._transfer_usage = NULL if timer.code/100 == 2: _add_event("SIPInvitationTransferDidEnd", dict(obj=self)) else: _add_event("SIPInvitationTransferDidFail", dict(obj=self, code=timer.code, reason=timer.reason)) finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _transfer_cb_response(self, TransferResponseCallbackTimer timer) except -1: cdef int expires cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._transfer_timeout_timer is not None: self._transfer_timeout_timer.cancel() self._transfer_timeout_timer = None finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _transfer_cb_notify(self, TransferRequestCallbackTimer timer) except -1: cdef pj_time_val refresh cdef int expires cdef dict notify_dict = dict(obj=self) cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: sub_state_hdr = timer.rdata["headers"].get("Subscription-State", None) if self.transfer_state != "TERMINATED" and sub_state_hdr is not None and sub_state_hdr.expires > 0: if self._transfer_refresh_timer is not None: self._transfer_refresh_timer.cancel() self._transfer_refresh_timer = None expires = max(1, sub_state_hdr.expires - self.expire_warning_time, sub_state_hdr.expires/2) self._transfer_refresh_timer = Timer() self._transfer_refresh_timer.schedule(expires, self._transfer_cb_refresh_timer, self) notify_dict["request_uri"] = timer.rdata["request_uri"] notify_dict["from_header"] = timer.rdata["headers"].get("From", None) notify_dict["to_header"] = timer.rdata["headers"].get("To", None) notify_dict["headers"] = timer.rdata["headers"] notify_dict["body"] = timer.rdata["body"] content_type = notify_dict["headers"].get("Content-Type", None) notify_dict["content_type"] = content_type.content_type if content_type else None event = notify_dict["headers"].get("Event", None) notify_dict["event"] = event.event if event else None _add_event("SIPInvitationTransferGotNotify", notify_dict) finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _transfer_cb_server_timeout(self, timer) except -1: cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: self._terminate_transfer() finally: with nogil: pj_mutex_unlock(lock) return 0 # Callback functions # cdef void _Invitation_cb_state(pjsip_inv_session *inv, pjsip_event *e) with gil: cdef pjsip_rx_data *rdata = NULL cdef pjsip_tx_data *tdata = NULL cdef object state cdef object rdata_dict = None cdef object tdata_dict = None cdef object originator = None cdef Invitation invitation cdef PJSIPUA ua cdef StateCallbackTimer timer try: ua = _get_ua() except: return try: if inv.state == PJSIP_INV_STATE_INCOMING: return if inv.mod_data[ua._module.id] != NULL: invitation = ( inv.mod_data[ua._module.id])() if invitation is None: return state = pjsip_inv_state_name(inv.state).lower() sub_state = None if state == "calling": state = "outgoing" elif state == "confirmed": state = "connected" sub_state = "normal" elif state == "disconnctd": state = "disconnected" if e != NULL: if e.type == PJSIP_EVENT_TSX_STATE and e.body.tsx_state.type == PJSIP_EVENT_TX_MSG: tdata = e.body.tsx_state.src.tdata if (tdata.msg.type == PJSIP_RESPONSE_MSG and tdata.msg.line.status.code == 487 and state == "disconnected" and invitation.state in ["incoming", "early"]): return elif e.type == PJSIP_EVENT_RX_MSG: rdata = e.body.rx_msg.rdata elif e.type == PJSIP_EVENT_TSX_STATE and e.body.tsx_state.type == PJSIP_EVENT_RX_MSG: if (inv.state != PJSIP_INV_STATE_CONFIRMED or e.body.tsx_state.src.rdata.msg_info.msg.type == PJSIP_REQUEST_MSG): rdata = e.body.tsx_state.src.rdata elif e.type == PJSIP_EVENT_TSX_STATE and e.body.tsx_state.type == PJSIP_EVENT_TRANSPORT_ERROR and e.body.tsx_state.tsx.role == PJSIP_ROLE_UAC: # A transport error occurred, fake a local reply rdata_dict = dict() rdata_dict["code"] = 408 rdata_dict["reason"] = "Transport Error" rdata_dict["headers"] = dict() rdata_dict["body"] = None originator = "local" if rdata != NULL: if invitation.peer_address is None: invitation.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: invitation.peer_address.ip = rdata.pkt_info.src_name invitation.peer_address.port = rdata.pkt_info.src_port rdata_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) originator = "remote" if tdata != NULL: tdata_dict = dict() _pjsip_msg_to_dict(tdata.msg, tdata_dict) originator = "local" try: timer = StateCallbackTimer(state, sub_state, rdata_dict, tdata_dict, originator) timer.schedule(0, invitation._cb_state, invitation) except: invitation._fail(ua) except: ua._handle_exception(1) cdef void _Invitation_cb_sdp_done(pjsip_inv_session *inv, int status) with gil: cdef Invitation invitation cdef PJSIPUA ua cdef SDPCallbackTimer timer cdef pjmedia_sdp_session_ptr_const sdp try: ua = _get_ua() except: return try: if inv.mod_data[ua._module.id] != NULL: invitation = ( inv.mod_data[ua._module.id])() if invitation is None: return if status == 0: if pjmedia_sdp_neg_get_active_local(invitation._invite_session.neg, &sdp) == 0: local_sdp = SDPSession_create(sdp) else: local_sdp = None if pjmedia_sdp_neg_get_active_remote(invitation._invite_session.neg, &sdp) == 0: remote_sdp = SDPSession_create(sdp) else: remote_sdp = None if local_sdp is None or remote_sdp is None: active_local = None active_remote = None else: if len(local_sdp.media) > len(remote_sdp.media): local_sdp.media = local_sdp.media[:len(remote_sdp.media)] if len(remote_sdp.media) > len(local_sdp.media): remote_sdp.media = remote_sdp.media[:len(local_sdp.media)] for index, local_media in enumerate(local_sdp.media): remote_media = remote_sdp.media[index] if not local_media.port and remote_media.port: remote_media.port = 0 if not remote_media.port and local_media.port: local_media.port = 0 active_local = FrozenSDPSession.new(local_sdp) active_remote = FrozenSDPSession.new(remote_sdp) else: active_local = None active_remote = None try: timer = SDPCallbackTimer(status, active_local, active_remote) timer.schedule(0, invitation._cb_sdp_done, invitation) except: invitation._fail(ua) except: ua._handle_exception(1) cdef int _Invitation_cb_rx_reinvite(pjsip_inv_session *inv, pjmedia_sdp_session_ptr_const offer, pjsip_rx_data *rdata) with gil: cdef int status cdef pjsip_tx_data *answer_tdata cdef object rdata_dict = None cdef Invitation invitation cdef PJSIPUA ua cdef StateCallbackTimer timer try: ua = _get_ua() except: return 1 try: if inv.mod_data[ua._module.id] != NULL: invitation = ( inv.mod_data[ua._module.id])() if invitation is None: return 1 if invitation.peer_address is None: invitation.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: invitation.peer_address.ip = rdata.pkt_info.src_name invitation.peer_address.port = rdata.pkt_info.src_port rdata_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) with nogil: status = pjsip_inv_initial_answer(inv, rdata, 100, NULL, NULL, &answer_tdata) if status != 0: raise PJSIPError("Could not create initial (unused) response to re-INVITE", status) with nogil: pjsip_tx_data_dec_ref(answer_tdata) if offer != NULL: sub_state = "received_proposal" else: sub_state = "received_proposal_request" try: timer = StateCallbackTimer("connected", sub_state, rdata_dict, None, "remote") timer.schedule(0, invitation._cb_state, invitation) except: invitation._fail(ua) return 1 return 0 except: ua._handle_exception(1) return 1 cdef void _Invitation_cb_tsx_state_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e) with gil: cdef pjsip_rx_data *rdata = NULL cdef pjsip_tx_data *tdata = NULL cdef object rdata_dict = None cdef object tdata_dict = None cdef object originator = None cdef Invitation invitation cdef PJSIPUA ua cdef StateCallbackTimer timer cdef TransferRequestCallbackTimer transfer_timer try: ua = _get_ua() except: return try: if tsx == NULL or e == NULL: return if e.type == PJSIP_EVENT_TSX_STATE and e.body.tsx_state.type == PJSIP_EVENT_RX_MSG: rdata = e.body.tsx_state.src.rdata if e.type == PJSIP_EVENT_TSX_STATE and e.body.tsx_state.type == PJSIP_EVENT_TX_MSG: tdata = e.body.tsx_state.src.tdata if inv.mod_data[ua._module.id] != NULL: invitation = ( inv.mod_data[ua._module.id])() if invitation is None: return if rdata != NULL: if invitation.peer_address is None: invitation.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: invitation.peer_address.ip = rdata.pkt_info.src_name invitation.peer_address.port = rdata.pkt_info.src_port if ((tsx.state == PJSIP_TSX_STATE_TERMINATED or tsx.state == PJSIP_TSX_STATE_COMPLETED) and (inv.neg != NULL and pjmedia_sdp_neg_get_state(inv.neg) in (PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER, PJMEDIA_SDP_NEG_STATE_DONE)) and invitation._reinvite_transaction != NULL and invitation._reinvite_transaction == tsx): if rdata != NULL: rdata_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) originator = "remote" if tdata != NULL: tdata_dict = dict() _pjsip_msg_to_dict(tdata.msg, tdata_dict) originator = "local" try: timer = StateCallbackTimer("connected", "normal", rdata_dict, tdata_dict, originator) timer.schedule(0, invitation._cb_state, invitation) except: invitation._fail(ua) elif (invitation.state in ("incoming", "early") and invitation.direction == "incoming" and rdata != NULL and rdata.msg_info.msg.type == PJSIP_REQUEST_MSG and rdata.msg_info.msg.line.req.method.id == PJSIP_CANCEL_METHOD): rdata_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) originator = "remote" try: timer = StateCallbackTimer("disconnected", None, rdata_dict, None, originator) timer.schedule(0, invitation._cb_state, invitation) except: invitation._fail(ua) elif (tsx.role == PJSIP_ROLE_UAS and tsx.state == PJSIP_TSX_STATE_TRYING and rdata != NULL and rdata.msg_info.msg.type == PJSIP_REQUEST_MSG and _pj_str_to_str(tsx.method.name) == "REFER"): invitation.process_incoming_transfer(ua, rdata) elif (tsx.role == PJSIP_ROLE_UAS and tsx.state == PJSIP_TSX_STATE_TRYING and rdata != NULL and rdata.msg_info.msg.type == PJSIP_REQUEST_MSG and tsx.method.id == PJSIP_OPTIONS_METHOD): invitation.process_incoming_options(ua, rdata) except: ua._handle_exception(1) cdef void _Invitation_cb_new(pjsip_inv_session *inv, pjsip_event *e) with gil: # As far as I can tell this is never actually called! pass cdef void _Invitation_transfer_cb_state(pjsip_evsub *sub, pjsip_event *event) with gil: cdef void *invitation_void cdef Invitation invitation cdef object state cdef int code = 0 cdef dict event_dict = dict() cdef str reason = None cdef pjsip_rx_data *rdata = NULL cdef PJSIPUA ua try: ua = _get_ua() except: return try: invitation_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if invitation_void == NULL: return invitation = ( invitation_void)() if invitation is None: return state = pjsip_evsub_get_state_name(sub) if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and (event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED or event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_TERMINATED)): if state == "TERMINATED": if event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC: code = event.body.tsx_state.tsx.status_code reason = _pj_str_to_str(event.body.tsx_state.tsx.status_text) else: reason = "Referral has expired" if event.body.tsx_state.type == PJSIP_EVENT_RX_MSG and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "NOTIFY": # Extract code and reason from the sipfrag payload rdata = event.body.tsx_state.src.rdata if rdata != NULL: _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) if event_dict.get('body', None) is not None: match = sipfrag_re.match(event_dict['body']) if match: code = int(match.group('code')) reason = match.group('reason') try: timer = TransferStateCallbackTimer(state, code, reason) timer.schedule(0, invitation._transfer_cb_state, invitation) except: invitation._fail(ua) except: ua._handle_exception(1) cdef void _Invitation_transfer_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil: cdef void *invitation_void cdef Invitation invitation cdef pjsip_rx_data *rdata cdef PJSIPUA ua try: ua = _get_ua() except: return try: invitation_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if invitation_void == NULL: return invitation = ( invitation_void)() if invitation is None: return if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.type == PJSIP_EVENT_RX_MSG and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED and _pj_str_to_str(event.body.tsx_state.tsx.method.name) in ("REFER", "SUBSCRIBE") and event.body.tsx_state.tsx.status_code/100 == 2): rdata = event.body.tsx_state.src.rdata if rdata != NULL: rdata_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) try: timer = TransferResponseCallbackTimer(_pj_str_to_str(event.body.tsx_state.tsx.method.name), rdata_dict) timer.schedule(0, invitation._transfer_cb_response, invitation) except: invitation._fail(ua) except: ua._handle_exception(1) cdef void _Invitation_transfer_cb_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil: cdef void *invitation_void cdef Invitation invitation cdef TransferRequestCallbackTimer timer cdef PJSIPUA ua try: ua = _get_ua() except: return try: invitation_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if invitation_void == NULL: return invitation = ( invitation_void)() if invitation is None: return if rdata != NULL: rdata_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) try: timer = TransferRequestCallbackTimer(rdata_dict) timer.schedule(0, invitation._transfer_cb_notify, invitation) except: invitation._fail(ua) except: ua._handle_exception(1) cdef void _Invitation_transfer_cb_refresh(pjsip_evsub *sub) with gil: # We want to handle the refresh timer oursevles, ignore the PJSIP provided timer pass cdef void _Invitation_transfer_in_cb_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil: cdef void *invitation_void cdef dict rdata_dict cdef pjsip_expires_hdr *expires_header cdef Invitation invitation cdef Timer timer cdef PJSIPUA ua try: ua = _get_ua() except: return try: invitation_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if invitation_void == NULL: p_st_code[0] = 481 return invitation = ( invitation_void)() if invitation is None: p_st_code[0] = 481 return expires_header = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_EXPIRES, NULL) if expires_header != NULL and expires_header.ivalue == 0: try: timer = Timer() timer.schedule(0, invitation._terminate_transfer, invitation) except: invitation._fail(ua) p_st_code[0] = 200 return p_st_code[0] = 501 except: ua._handle_exception(1) cdef void _Invitation_transfer_in_cb_server_timeout(pjsip_evsub *sub) with gil: cdef void *invitation_void cdef Invitation invitation cdef Timer timer cdef PJSIPUA ua try: ua = _get_ua() except: return try: invitation_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if invitation_void == NULL: return invitation = ( invitation_void)() if invitation is None: return try: timer = Timer() timer.schedule(0, invitation._transfer_cb_server_timeout, invitation) except: invitation._fail(ua) except: ua._handle_exception(1) cdef void _Invitation_transfer_in_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil: cdef void *invitation_void cdef Invitation invitation cdef PJSIPUA ua cdef pjsip_rx_data *rdata cdef dict event_dict cdef int code cdef str reason cdef TransferStateCallbackTimer timer try: ua = _get_ua() except: return try: invitation_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if invitation_void == NULL: return invitation = ( invitation_void)() if invitation is None: return if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "NOTIFY" and event.body.tsx_state.tsx.state in (PJSIP_TSX_STATE_COMPLETED, PJSIP_TSX_STATE_TERMINATED)): code = event.body.tsx_state.tsx.status_code reason = _pj_str_to_str(event.body.tsx_state.tsx.status_text) if code in (408, 481) or code/100==7: # Be careful! PJSIP will erase the subscription timer = TransferStateCallbackTimer("TERMINATED", code, reason) timer.schedule(0, invitation._transfer_cb_state, invitation) except: ua._handle_exception(1) # Globals # cdef pjsip_inv_callback _inv_cb _inv_cb.on_state_changed = _Invitation_cb_state _inv_cb.on_media_update = _Invitation_cb_sdp_done _inv_cb.on_rx_reinvite = _Invitation_cb_rx_reinvite _inv_cb.on_tsx_state_changed = _Invitation_cb_tsx_state_changed _inv_cb.on_new_session = _Invitation_cb_new cdef pjsip_evsub_user _transfer_cb _transfer_cb.on_evsub_state = _Invitation_transfer_cb_state _transfer_cb.on_tsx_state = _Invitation_transfer_cb_tsx _transfer_cb.on_rx_notify = _Invitation_transfer_cb_notify _transfer_cb.on_client_refresh = _Invitation_transfer_cb_refresh cdef pjsip_evsub_user _incoming_transfer_cb _incoming_transfer_cb.on_rx_refresh = _Invitation_transfer_in_cb_rx_refresh _incoming_transfer_cb.on_server_timeout = _Invitation_transfer_in_cb_server_timeout _incoming_transfer_cb.on_tsx_state = _Invitation_transfer_in_cb_tsx ================================================ FILE: sipsimple/core/_core.lib.pxi ================================================ import sys # classes cdef class PJLIB: def __cinit__(self): cdef int status status = pj_init() if status != 0: raise PJSIPError("Could not initialize PJLIB", status) self._init_done = 1 status = pjlib_util_init() if status != 0: raise PJSIPError("Could not initialize PJLIB-UTIL", status) status = pjnath_init() if status != 0: raise PJSIPError("Could not initialize PJNATH", status) def __dealloc__(self): if self._init_done: with nogil: pj_shutdown() cdef class PJCachingPool: def __cinit__(self): pj_caching_pool_init(&self._obj, &pj_pool_factory_default_policy, 0) self._init_done = 1 def __dealloc__(self): if self._init_done: pj_caching_pool_destroy(&self._obj) cdef class PJSIPEndpoint: def __cinit__(self, PJCachingPool caching_pool, ip_address, udp_port, tcp_port, tls_port, tls_verify_server, tls_ca_file, tls_cert_file, tls_privkey_file, int tls_timeout): cdef pj_dns_resolver *resolver cdef pjsip_tpmgr *tpmgr cdef int status if ip_address is not None and not _is_valid_ip(pj_AF_INET(), ip_address): raise ValueError("Not a valid IPv4 address: %s" % ip_address) self._local_ip_used = ip_address status = pjsip_endpt_create(&caching_pool._obj.factory, "core", &self._obj) if status != 0: raise PJSIPError("Could not initialize PJSIP endpoint", status) self._pool = pjsip_endpt_create_pool(self._obj, "PJSIPEndpoint", 4096, 4096) if self._pool == NULL: raise SIPCoreError("Could not allocate memory pool") status = pjsip_tsx_layer_init_module(self._obj) if status != 0: raise PJSIPError("Could not initialize transaction layer module", status) status = pjsip_ua_init_module(self._obj, NULL) # TODO: handle forking if status != 0: raise PJSIPError("Could not initialize common dialog layer module", status) status = pjsip_evsub_init_module(self._obj) if status != 0: raise PJSIPError("Could not initialize event subscription module", status) status = pjsip_100rel_init_module(self._obj) if status != 0: raise PJSIPError("Could not initialize 100rel module", status) status = pjsip_replaces_init_module(self._obj) if status != 0: raise PJSIPError("Could not initialize replaces module", status) status = pjsip_inv_usage_init(self._obj, &_inv_cb) if status != 0: raise PJSIPError("Could not initialize invitation module", status) status = pjsip_endpt_create_resolver(self._obj, &resolver) if status != 0: raise PJSIPError("Could not create fake DNS resolver for endpoint", status) status = pjsip_endpt_set_resolver(self._obj, resolver) if status != 0: raise PJSIPError("Could not set fake DNS resolver on endpoint", status) tpmgr = pjsip_endpt_get_tpmgr(self._obj) if tpmgr == NULL: raise SIPCoreError("Could not get the transport manager") status = pjsip_tpmgr_set_state_cb(tpmgr, _transport_state_cb) if status != 0: raise PJSIPError("Could not set transport state callback", status) if udp_port is not None: self._start_udp_transport(udp_port) if tcp_port is not None: self._start_tcp_transport(tcp_port) self._tls_verify_server = int(tls_verify_server) if tls_ca_file is not None: self._tls_ca_file = PJSTR(tls_ca_file.encode(sys.getfilesystemencoding())) if tls_cert_file is not None: self._tls_cert_file = PJSTR(tls_cert_file.encode(sys.getfilesystemencoding())) if tls_privkey_file is not None: self._tls_privkey_file = PJSTR(tls_privkey_file.encode(sys.getfilesystemencoding())) if tls_timeout < 0: raise ValueError("Invalid TLS timeout value: %d" % tls_timeout) self._tls_timeout = tls_timeout if tls_port is not None: self._start_tls_transport(tls_port) cdef int _make_local_addr(self, pj_sockaddr_in *local_addr, object ip_address, int port) except -1: cdef pj_str_t local_ip_pj cdef pj_str_t *local_ip_p = NULL cdef int status if not (0 <= port <= 65535): raise SIPCoreError("Invalid port: %d" % port) if ip_address is not None and ip_address is not "0.0.0.0": local_ip_p = &local_ip_pj _str_to_pj_str(ip_address, local_ip_p) status = pj_sockaddr_in_init(local_addr, local_ip_p, port) if status != 0: raise PJSIPError("Could not create local address", status) return 0 cdef int _start_udp_transport(self, int port) except -1: cdef pj_sockaddr_in local_addr self._make_local_addr(&local_addr, self._local_ip_used, port) status = pjsip_udp_transport_start(self._obj, &local_addr, NULL, 1, &self._udp_transport) if status != 0: raise PJSIPError("Could not create UDP transport", status) return 0 cdef int _stop_udp_transport(self) except -1: pjsip_transport_shutdown(self._udp_transport) self._udp_transport = NULL return 0 cdef int _start_tcp_transport(self, int port) except -1: cdef pj_sockaddr_in local_addr self._make_local_addr(&local_addr, self._local_ip_used, port) status = pjsip_tcp_transport_start2(self._obj, &local_addr, NULL, 1, &self._tcp_transport) if status != 0: raise PJSIPError("Could not create TCP transport", status) return 0 cdef int _stop_tcp_transport(self) except -1: self._tcp_transport.destroy(self._tcp_transport) self._tcp_transport = NULL return 0 cdef int _start_tls_transport(self, port) except -1: cdef pj_sockaddr_in local_addr cdef pjsip_tls_setting tls_setting self._make_local_addr(&local_addr, self._local_ip_used, port) pjsip_tls_setting_default(&tls_setting) # The following value needs to be reasonably low, as TLS negotiation hogs the PJSIP polling loop tls_setting.timeout.sec = self._tls_timeout / 1000 tls_setting.timeout.msec = self._tls_timeout % 1000 if self._tls_ca_file is not None: tls_setting.ca_list_file = self._tls_ca_file.pj_str if self._tls_cert_file is not None: tls_setting.cert_file = self._tls_cert_file.pj_str if self._tls_privkey_file is not None: tls_setting.privkey_file = self._tls_privkey_file.pj_str tls_setting.method = PJSIP_SSLV23_METHOD tls_setting.verify_server = self._tls_verify_server status = pjsip_tls_transport_start(self._obj, &tls_setting, &local_addr, NULL, 1, &self._tls_transport) if status in (PJSIP_TLS_EUNKNOWN, PJSIP_TLS_EINVMETHOD, PJSIP_TLS_ECACERT, PJSIP_TLS_ECERTFILE, PJSIP_TLS_EKEYFILE, PJSIP_TLS_ECIPHER, PJSIP_TLS_ECTX): raise PJSIPTLSError("Could not create TLS transport", status) elif status != 0: raise PJSIPError("Could not create TLS transport", status) return 0 cdef int _stop_tls_transport(self) except -1: self._tls_transport.destroy(self._tls_transport) self._tls_transport = NULL return 0 cdef int _set_dns_nameservers(self, list servers) except -1: cdef int num_servers = len(servers) cdef pj_str_t *pj_servers cdef int status cdef pj_dns_resolver *resolver if num_servers == 0: return 0 resolver = pjsip_endpt_get_resolver(self._obj) if resolver == NULL: raise SIPCoreError("Could not get DNS resolver on endpoint") pj_servers = malloc(sizeof(pj_str_t)*num_servers) if pj_servers == NULL: raise MemoryError() for i, ns in enumerate(servers): _str_to_pj_str(ns, &pj_servers[i]) status = pj_dns_resolver_set_ns(resolver, num_servers, pj_servers, NULL) free(pj_servers) if status != 0: raise PJSIPError("Could not set nameservers on DNS resolver", status) return 0 def __dealloc__(self): cdef pjsip_tpmgr *tpmgr tpmgr = pjsip_endpt_get_tpmgr(self._obj) if tpmgr != NULL: pjsip_tpmgr_set_state_cb(tpmgr, NULL) if self._udp_transport != NULL: self._stop_udp_transport() if self._tcp_transport != NULL: self._stop_tcp_transport() if self._tls_transport != NULL: self._stop_tls_transport() if self._pool != NULL: pjsip_endpt_release_pool(self._obj, self._pool) if self._obj != NULL: with nogil: pjsip_endpt_destroy(self._obj) cdef class PJMEDIAEndpoint: def __cinit__(self, PJCachingPool caching_pool): cdef int status status = pjmedia_endpt_create(&caching_pool._obj.factory, NULL, 1, &self._obj) if status != 0: raise PJSIPError("Could not create PJMEDIA endpoint", status) self._pool = pjmedia_endpt_create_pool(self._obj, "PJMEDIAEndpoint", 4096, 4096) if self._pool == NULL: raise SIPCoreError("Could not allocate memory pool") self._audio_subsystem_init(caching_pool) self._video_subsystem_init(caching_pool) def __dealloc__(self): self._audio_subsystem_shutdown() self._video_subsystem_shutdown() if self._pool != NULL: pj_pool_release(self._pool) if self._obj != NULL: with nogil: pjmedia_endpt_destroy(self._obj) cdef void _audio_subsystem_init(self, PJCachingPool caching_pool): cdef int status cdef pjmedia_audio_codec_config audio_codec_cfg pjmedia_audio_codec_config_default(&audio_codec_cfg) audio_codec_cfg.speex.option = PJMEDIA_SPEEX_NO_NB audio_codec_cfg.ilbc.mode = 30 status = pjmedia_codec_register_audio_codecs(self._obj, &audio_codec_cfg) if status != 0: raise PJSIPError("Could not initialize audio codecs", status) self._has_audio_codecs = 1 cdef void _audio_subsystem_shutdown(self): pass cdef void _video_subsystem_init(self, PJCachingPool caching_pool): cdef int status status = pjmedia_video_format_mgr_create(self._pool, 64, 0, NULL) if status != 0: raise PJSIPError("Could not initialize video format manager", status) status = pjmedia_converter_mgr_create(self._pool, NULL) if status != 0: raise PJSIPError("Could not initialize converter manager", status) status = pjmedia_event_mgr_create(self._pool, 0, NULL) if status != 0: raise PJSIPError("Could not initialize event manager", status) status = pjmedia_vid_codec_mgr_create(self._pool, NULL) if status != 0: raise PJSIPError("Could not initialize video codec manager", status) status = pjmedia_codec_ffmpeg_vid_init(NULL, &caching_pool._obj.factory) if status != 0: raise PJSIPError("Could not initialize ffmpeg video codecs", status) self._has_ffmpeg_video = 1 status = pjmedia_codec_vpx_init(NULL, &caching_pool._obj.factory) if status != 0: raise PJSIPError("Could not initialize vpx video codecs", status) self._has_vpx = 1 status = pjmedia_vid_dev_subsys_init(&caching_pool._obj.factory) if status != 0: raise PJSIPError("Could not initialize video subsystem", status) self._has_video = 1 cdef void _video_subsystem_shutdown(self): if self._has_video: pjmedia_vid_dev_subsys_shutdown() if self._has_ffmpeg_video: pjmedia_codec_ffmpeg_vid_deinit() if self._has_vpx: pjmedia_codec_vpx_deinit() if pjmedia_vid_codec_mgr_instance() != NULL: pjmedia_vid_codec_mgr_destroy(NULL) if pjmedia_event_mgr_instance() != NULL: pjmedia_event_mgr_destroy(NULL) if pjmedia_converter_mgr_instance() != NULL: pjmedia_converter_mgr_destroy(NULL) if pjmedia_video_format_mgr_instance() != NULL: pjmedia_video_format_mgr_destroy(NULL) cdef list _get_codecs(self): cdef unsigned int count = PJMEDIA_CODEC_MGR_MAX_CODECS cdef pjmedia_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS] cdef unsigned int prio[PJMEDIA_CODEC_MGR_MAX_CODECS] cdef int i cdef list retval cdef int status status = pjmedia_codec_mgr_enum_codecs(pjmedia_endpt_get_codec_mgr(self._obj), &count, info, prio) if status != 0: raise PJSIPError("Could not get available codecs", status) retval = list() for i from 0 <= i < count: retval.append((prio[i], _pj_str_to_str(info[i].encoding_name), info[i].channel_cnt, info[i].clock_rate)) return retval cdef list _get_all_codecs(self): cdef list codecs cdef tuple codec_data codecs = self._get_codecs() return list(set([codec_data[1] for codec_data in codecs])) cdef list _get_current_codecs(self): cdef list codecs cdef tuple codec_data cdef list retval codecs = [codec_data for codec_data in self._get_codecs() if codec_data[0] > 0] codecs.sort(reverse=True) retval = list(set([codec_data[1] for codec_data in codecs])) return retval cdef int _set_codecs(self, list req_codecs) except -1: cdef object new_codecs cdef object all_codecs cdef object codec_set cdef list codecs cdef tuple codec_data cdef str codec cdef int sample_rate cdef int channel_count cdef str codec_name cdef int prio cdef list codec_prio cdef pj_str_t codec_pj new_codecs = set(req_codecs) if len(new_codecs) != len(req_codecs): raise ValueError("Requested codec list contains doubles") all_codecs = set(self._get_all_codecs()) codec_set = new_codecs.difference(all_codecs) if len(codec_set) > 0: raise SIPCoreError("Unknown codec(s): %s" % ", ".join(codec_set)) # reverse the codec data tuples so that we can easily sort on sample rate # to make sure that bigger sample rates get higher priority codecs = [list(reversed(codec_data)) for codec_data in self._get_codecs()] codecs.sort(reverse=True) codec_prio = list() for codec in req_codecs: for sample_rate, channel_count, codec_name, prio in codecs: if codec == codec_name and channel_count == 1: codec_prio.append("%s/%d/%d" % (codec_name, sample_rate, channel_count)) for prio, codec in enumerate(reversed(codec_prio)): _str_to_pj_str(codec, &codec_pj) status = pjmedia_codec_mgr_set_codec_priority(pjmedia_endpt_get_codec_mgr(self._obj), &codec_pj, prio + 1) if status != 0: raise PJSIPError("Could not set codec priority", status) for sample_rate, channel_count, codec_name, prio in codecs: if codec_name not in req_codecs or channel_count > 1: codec = "%s/%d/%d" % (codec_name, sample_rate, channel_count) _str_to_pj_str(codec, &codec_pj) status = pjmedia_codec_mgr_set_codec_priority(pjmedia_endpt_get_codec_mgr(self._obj), &codec_pj, 0) if status != 0: raise PJSIPError("Could not set codec priority", status) return 0 cdef list _get_video_codecs(self): cdef unsigned int count = PJMEDIA_VID_CODEC_MGR_MAX_CODECS cdef pjmedia_vid_codec_info info[PJMEDIA_VID_CODEC_MGR_MAX_CODECS] cdef unsigned int prio[PJMEDIA_VID_CODEC_MGR_MAX_CODECS] cdef int i cdef list retval cdef int status status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, prio) if status != 0: raise PJSIPError("Could not get available video codecs", status) retval = list() for i from 0 <= i < count: if info[i].packings & PJMEDIA_VID_PACKING_PACKETS: retval.append((prio[i], _pj_str_to_str(info[i].encoding_name), info[i].pt)) return retval cdef list _get_all_video_codecs(self): cdef list codecs cdef tuple codec_data codecs = self._get_video_codecs() return list(set([codec_data[1] for codec_data in codecs])) cdef list _get_current_video_codecs(self): cdef list codecs cdef tuple codec_data cdef list retval codecs = [codec_data for codec_data in self._get_video_codecs() if codec_data[0] > 0] codecs.sort(reverse=True) retval = list(set([codec_data[1] for codec_data in codecs])) return retval cdef int _set_video_codecs(self, list req_codecs) except -1: cdef object new_codecs cdef object codec_set cdef list codecs cdef tuple codec_data cdef str codec cdef int payload_type cdef str codec_name cdef int prio cdef list codec_prio cdef pj_str_t codec_pj new_codecs = set(req_codecs) if len(new_codecs) != len(req_codecs): raise ValueError("Requested video codec list contains doubles") codec_set = new_codecs.difference(set(self._get_all_video_codecs())) if len(codec_set) > 0: raise SIPCoreError("Unknown video codec(s): %s" % ", ".join(codec_set)) codecs = self._get_video_codecs() codec_prio = list() for codec in req_codecs: for prio, codec_name, payload_type in codecs: if codec == codec_name: codec_prio.append("%s/%d" % (codec_name, payload_type)) for prio, codec in enumerate(reversed(codec_prio)): _str_to_pj_str(codec, &codec_pj) status = pjmedia_vid_codec_mgr_set_codec_priority(NULL, &codec_pj, prio + 1) if status != 0: raise PJSIPError("Could not set video codec priority", status) for prio, codec_name, payload_type in codecs: if codec_name not in req_codecs: codec = "%s/%d" % (codec_name, payload_type) _str_to_pj_str(codec, &codec_pj) status = pjmedia_vid_codec_mgr_set_codec_priority(NULL, &codec_pj, 0) if status != 0: raise PJSIPError("Could not set video codec priority", status) return 0 cdef void _set_h264_options(self, str profile, int level): global h264_profiles_map, h264_profile_level_id, h264_packetization_mode cdef unsigned int count = PJMEDIA_VID_CODEC_MGR_MAX_CODECS cdef pjmedia_vid_codec_info info[PJMEDIA_VID_CODEC_MGR_MAX_CODECS] cdef pjmedia_vid_codec_param vparam cdef unsigned int prio[PJMEDIA_VID_CODEC_MGR_MAX_CODECS] cdef int i cdef int status cdef PJSTR h264_profile_level_id_value cdef PJSTR h264_packetization_mode_value = PJSTR("1") # TODO; make it configurable? try: profile_n = h264_profiles_map[profile] except KeyError: raise ValueError("invalid profile specified: %s" % profile) h264_profile_level_id_value = PJSTR("%xe0%x" % (profile_n, level)) # use common subset (e0) status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, prio) if status != 0: raise PJSIPError("Could not get available video codecs", status) for i from 0 <= i < count: if not (info[i].packings & PJMEDIA_VID_PACKING_PACKETS): continue if _pj_str_to_str(info[i].encoding_name) != 'H264': continue status = pjmedia_vid_codec_mgr_get_default_param(NULL, &info[i], &vparam) if status != 0: continue # 2 format parameters are currently defined for H264: profile-level-id and packetization-mode vparam.dec_fmtp.param[0].name = h264_profile_level_id.pj_str vparam.dec_fmtp.param[0].val = h264_profile_level_id_value.pj_str vparam.dec_fmtp.param[1].name = h264_packetization_mode.pj_str vparam.dec_fmtp.param[1].val = h264_packetization_mode_value.pj_str vparam.dec_fmtp.cnt = 2 status = pjmedia_vid_codec_mgr_set_default_param(NULL, &info[i], &vparam) if status != 0: raise PJSIPError("Could not set H264 options", status) cdef void _set_video_options(self, tuple max_resolution, int max_framerate, float max_bitrate): cdef unsigned int count = PJMEDIA_VID_CODEC_MGR_MAX_CODECS cdef pjmedia_vid_codec_info info[PJMEDIA_VID_CODEC_MGR_MAX_CODECS] cdef pjmedia_vid_codec_param vparam cdef unsigned int prio[PJMEDIA_VID_CODEC_MGR_MAX_CODECS] cdef int i cdef int status max_width, max_height = max_resolution status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, prio) if status != 0: raise PJSIPError("Could not get available video codecs", status) for i from 0 <= i < count: if not (info[i].packings & PJMEDIA_VID_PACKING_PACKETS): continue status = pjmedia_vid_codec_mgr_get_default_param(NULL, &info[i], &vparam) if status != 0: continue # Max resolution vparam.enc_fmt.det.vid.size.w = max_width vparam.enc_fmt.det.vid.size.h = max_height vparam.dec_fmt.det.vid.size.w = max_width vparam.dec_fmt.det.vid.size.h = max_height # Max framerate vparam.enc_fmt.det.vid.fps.num = max_framerate vparam.enc_fmt.det.vid.fps.denum = 1 vparam.dec_fmt.det.vid.fps.num = 10 vparam.dec_fmt.det.vid.fps.denum = 1 # Average and max bitrate (set to 0 for 'unlimited') vparam.enc_fmt.det.vid.avg_bps = int(max_bitrate * 1e6) vparam.enc_fmt.det.vid.max_bps = int(max_bitrate * 1e6) vparam.dec_fmt.det.vid.avg_bps = 0 vparam.dec_fmt.det.vid.max_bps = 0 status = pjmedia_vid_codec_mgr_set_default_param(NULL, &info[i], &vparam) if status != 0: raise PJSIPError("Could not set video options", status) cdef void _transport_state_cb(pjsip_transport *tp, pjsip_transport_state state, pjsip_transport_state_info_ptr_const info) with gil: cdef PJSIPUA ua cdef str local_address cdef str remote_address cdef char buf[PJ_INET6_ADDRSTRLEN] cdef dict event_dict try: ua = _get_ua() except: return if pj_sockaddr_has_addr(&tp.local_addr): pj_sockaddr_print(&tp.local_addr, buf, 512, 0) local_address = '%s:%d' % (PyString_FromString(buf), pj_sockaddr_get_port(&tp.local_addr)) else: local_address = None remote_address = '%s:%d' % (_pj_str_to_str(tp.remote_name.host), tp.remote_name.port) event_dict = dict(transport=tp.type_name.lower(), local_address=local_address, remote_address=remote_address) if state == PJSIP_TP_STATE_CONNECTED: _add_event("SIPEngineTransportDidConnect", event_dict) else: event_dict['reason'] = _pj_status_to_str(info.status) _add_event("SIPEngineTransportDidDisconnect", event_dict) # globals cdef PJSTR h264_profile_level_id = PJSTR("profile-level-id") cdef PJSTR h264_packetization_mode = PJSTR("packetization-mode") cdef dict h264_profiles_map = dict(baseline=66, main=77, high=100) ================================================ FILE: sipsimple/core/_core.mediatransport.pxi ================================================ import sys from errno import EADDRINUSE # classes cdef class RTPTransport: def __cinit__(self, *args, **kwargs): cdef int status cdef pj_pool_t *pool cdef bytes pool_name cdef char* c_pool_name cdef PJSIPUA ua ua = _get_ua() pool_name = b"RTPTransport_%d" % id(self) self.weakref = weakref.ref(self) Py_INCREF(self.weakref) self._af = pj_AF_INET() status = pj_mutex_create_recursive(ua._pjsip_endpoint._pool, "rtp_transport_lock", &self._lock) if status != 0: raise PJSIPError("failed to create lock", status) pool = ua.create_memory_pool(pool_name, 4096, 4096) self._pool = pool self.state = "NULL" def __init__(self, encryption=None, use_ice=False, ice_stun_address=None, ice_stun_port=PJ_STUN_PORT): cdef PJSIPUA ua = _get_ua() if self.state != "NULL": raise SIPCoreError("RTPTransport.__init__() was already called") self._rtp_valid_pair = None self._encryption = encryption self.use_ice = use_ice self.ice_stun_address = ice_stun_address self.ice_stun_port = ice_stun_port def __dealloc__(self): cdef PJSIPUA ua cdef pjmedia_transport *transport cdef Timer timer try: ua = _get_ua() except: return transport = self._obj if transport != NULL: transport.user_data = NULL if self._wrapped_transport != NULL: self._wrapped_transport.user_data = NULL with nogil: pjmedia_transport_media_stop(transport) pjmedia_transport_close(transport) self._obj = NULL self._wrapped_transport = NULL ua.release_memory_pool(self._pool) self._pool = NULL if self._lock != NULL: pj_mutex_destroy(self._lock) timer = Timer() try: timer.schedule(60, deallocate_weakref, self.weakref) except SIPCoreError: pass cdef PJSIPUA _check_ua(self): cdef PJSIPUA ua try: ua = _get_ua() return ua except: self.state = "INVALID" self._obj = NULL self._wrapped_transport = NULL self._pool = NULL return None cdef void _get_info(self, pjmedia_transport_info *info): cdef int status cdef pjmedia_transport *transport transport = self._obj with nogil: pjmedia_transport_info_init(info) status = pjmedia_transport_get_info(transport, info) if status != 0: raise PJSIPError("Could not get transport info", status) property local_rtp_port: def __get__(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_transport_info info cdef PJSIPUA ua ua = self._check_ua() if ua is None: return None with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state in ["NULL", "WAIT_STUN", "INVALID"]: return None self._get_info(&info) if pj_sockaddr_has_addr(&info.sock_info.rtp_addr_name): return pj_sockaddr_get_port(&info.sock_info.rtp_addr_name) else: return None finally: with nogil: pj_mutex_unlock(lock) property local_rtp_address: def __get__(self): cdef char buf[PJ_INET6_ADDRSTRLEN] cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_transport_info info cdef PJSIPUA ua ua = self._check_ua() if ua is None: return None with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state in ["NULL", "WAIT_STUN", "INVALID"]: return None self._get_info(&info) if pj_sockaddr_has_addr(&info.sock_info.rtp_addr_name): return pj_sockaddr_print(&info.sock_info.rtp_addr_name, buf, PJ_INET6_ADDRSTRLEN, 0) else: return None finally: with nogil: pj_mutex_unlock(lock) property local_rtp_candidate: def __get__(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return None with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._rtp_valid_pair: return self._rtp_valid_pair.local_candidate return None finally: with nogil: pj_mutex_unlock(lock) property remote_rtp_port: def __get__(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_transport_info info cdef PJSIPUA ua ua = self._check_ua() if ua is None: return None with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state in ["NULL", "WAIT_STUN", "INVALID"]: return None if self._ice_active() and self._rtp_valid_pair: return self._rtp_valid_pair.remote_candidate.port self._get_info(&info) if pj_sockaddr_has_addr(&info.src_rtp_name): return pj_sockaddr_get_port(&info.src_rtp_name) else: return None finally: with nogil: pj_mutex_unlock(lock) property remote_rtp_address: def __get__(self): cdef char buf[PJ_INET6_ADDRSTRLEN] cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_transport_info info cdef PJSIPUA ua ua = self._check_ua() if ua is None: return None with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state in ["NULL", "WAIT_STUN", "INVALID"]: return None if self._ice_active() and self._rtp_valid_pair: return self._rtp_valid_pair.remote_candidate.address self._get_info(&info) if pj_sockaddr_has_addr(&info.src_rtp_name): return pj_sockaddr_print(&info.src_rtp_name, buf, PJ_INET6_ADDRSTRLEN, 0) else: return None finally: with nogil: pj_mutex_unlock(lock) property remote_rtp_candidate: def __get__(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return None with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._rtp_valid_pair: return self._rtp_valid_pair.remote_candidate return None finally: with nogil: pj_mutex_unlock(lock) property srtp_active: def __get__(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_srtp_info *srtp_info cdef pjmedia_transport_info info cdef PJSIPUA ua ua = self._check_ua() if ua is None: return False with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state in ["NULL", "WAIT_STUN", "INVALID"]: return False self._get_info(&info) srtp_info = pjmedia_transport_info_get_spc_info(&info, PJMEDIA_TRANSPORT_TYPE_SRTP) if srtp_info != NULL: return bool(srtp_info.active) return False finally: with nogil: pj_mutex_unlock(lock) property srtp_cipher: def __get__(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_srtp_info *srtp_info cdef pjmedia_transport_info info cdef PJSIPUA ua ua = self._check_ua() if ua is None: return None with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state in ["NULL", "WAIT_STUN", "INVALID"]: return None self._get_info(&info) srtp_info = pjmedia_transport_info_get_spc_info(&info, PJMEDIA_TRANSPORT_TYPE_SRTP) if srtp_info == NULL or not bool(srtp_info.active): return None return _pj_str_to_str(srtp_info.tx_policy.name) finally: with nogil: pj_mutex_unlock(lock) property zrtp_active: def __get__(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_zrtp_info *zrtp_info cdef pjmedia_transport_info info cdef PJSIPUA ua ua = self._check_ua() if ua is None: return False with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state in ["NULL", "WAIT_STUN", "INVALID"]: return False self._get_info(&info) zrtp_info = pjmedia_transport_info_get_spc_info(&info, PJMEDIA_TRANSPORT_TYPE_ZRTP) if zrtp_info != NULL: return bool(zrtp_info.active) return False finally: with nogil: pj_mutex_unlock(lock) cdef int _ice_active(self): # this function needs to be called with the lock held cdef pjmedia_transport_info info cdef pjmedia_ice_transport_info *ice_info if self.state in ["NULL", "WAIT_STUN", "INVALID"]: return 0 self._get_info(&info) ice_info = pjmedia_transport_info_get_spc_info(&info, PJMEDIA_TRANSPORT_TYPE_ICE) if ice_info != NULL and ice_info.sess_state == PJ_ICE_STRANS_STATE_RUNNING: return 1 return 0 property ice_active: def __get__(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return False with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: return bool(self._ice_active()) finally: with nogil: pj_mutex_unlock(lock) cdef int _init_local_sdp(self, BaseSDPSession local_sdp, BaseSDPSession remote_sdp, int sdp_index): cdef int status cdef pj_pool_t *pool cdef pjmedia_sdp_session *pj_local_sdp cdef pjmedia_sdp_session *pj_remote_sdp cdef pjmedia_transport *transport pool = self._pool transport = self._obj pj_local_sdp = local_sdp.get_sdp_session() if remote_sdp is not None: pj_remote_sdp = remote_sdp.get_sdp_session() else: pj_remote_sdp = NULL if sdp_index < 0: raise ValueError("sdp_index argument cannot be negative") if sdp_index >= pj_local_sdp.media_count: raise ValueError("sdp_index argument out of range") with nogil: status = pjmedia_transport_media_create(transport, pool, 0, pj_remote_sdp, sdp_index) if status != 0: raise PJSIPError("Could not create media transport", status) return 0 def set_LOCAL(self, SDPSession local_sdp, int sdp_index): cdef int status cdef pj_mutex_t *lock = self._lock _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if local_sdp is None: raise SIPCoreError("local_sdp argument cannot be None") if self.state == "LOCAL": return if self.state != "INIT": raise SIPCoreError('set_LOCAL can only be called in the "INIT" state, current state is "%s"' % self.state) self._init_local_sdp(local_sdp, None, sdp_index) self.state = "LOCAL" finally: with nogil: pj_mutex_unlock(lock) def set_REMOTE(self, BaseSDPSession local_sdp, BaseSDPSession remote_sdp, int sdp_index): cdef int status cdef pj_mutex_t *lock = self._lock _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if None in [local_sdp, remote_sdp]: raise SIPCoreError("SDP arguments cannot be None") if self.state == "REMOTE": return if self.state != "INIT": raise SIPCoreError('set_REMOTE can only be called in the "INIT" state, current state is "%s"' % self.state) self._init_local_sdp(local_sdp, remote_sdp, sdp_index) self.state = "REMOTE" finally: with nogil: pj_mutex_unlock(lock) def set_ESTABLISHED(self, BaseSDPSession local_sdp, BaseSDPSession remote_sdp, int sdp_index): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_sdp_session *pj_local_sdp cdef pjmedia_sdp_session *pj_remote_sdp cdef pjmedia_transport *transport = self._obj _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: transport = self._obj if None in [local_sdp, remote_sdp]: raise SIPCoreError("SDP arguments cannot be None") pj_local_sdp = local_sdp.get_sdp_session() pj_remote_sdp = remote_sdp.get_sdp_session() if self.state == "ESTABLISHED": return if self.state not in ["LOCAL", "REMOTE"]: raise SIPCoreError('set_ESTABLISHED can only be called in the "INIT" and "LOCAL" states, ' + 'current state is "%s"' % self.state) with nogil: status = pjmedia_transport_media_start(transport, self._pool, pj_local_sdp, pj_remote_sdp, sdp_index) if status != 0: raise PJSIPError("Could not start media transport", status) self.state = "ESTABLISHED" finally: with nogil: pj_mutex_unlock(lock) def set_INIT(self): global _ice_cb cdef int af cdef int i cdef int status cdef int port cdef pj_caching_pool *caching_pool cdef pj_ice_strans_cfg ice_cfg cdef pj_ice_strans *ice_st cdef pj_ice_strans_state ice_state cdef pj_mutex_t *lock = self._lock cdef pj_str_t local_ip cdef pj_str_t *local_ip_address cdef pjmedia_endpt *media_endpoint cdef pjmedia_srtp_setting srtp_setting cdef pjmedia_transport **transport_address cdef pjmedia_transport *wrapped_transport cdef pjsip_endpoint *sip_endpoint cdef bytes zid_file cdef char *c_zid_file cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: af = self._af caching_pool = &ua._caching_pool._obj media_endpoint = ua._pjmedia_endpoint._obj sip_endpoint = ua._pjsip_endpoint._obj transport_address = &self._obj if self.state == "INIT": return if self.state in ["LOCAL", "ESTABLISHED"]: with nogil: status = pjmedia_transport_media_stop(transport_address[0]) if status != 0: raise PJSIPError("Could not stop media transport", status) self.state = "INIT" elif self.state == "NULL": if ua.ip_address is None: local_ip_address = NULL else: _str_to_pj_str(ua.ip_address, &local_ip) local_ip_address = &local_ip if self.use_ice: with nogil: pj_ice_strans_cfg_default(&ice_cfg) ice_cfg.af = self._af with nogil: pj_stun_config_init(&ice_cfg.stun_cfg, &caching_pool.factory, 0, pjmedia_endpt_get_ioqueue(media_endpoint), pjsip_endpt_get_timer_heap(sip_endpoint)) if self.ice_stun_address is not None: _str_to_pj_str(self.ice_stun_address, &ice_cfg.stun.server) ice_cfg.stun.port = self.ice_stun_port # IIRC we can't choose the port for ICE with nogil: status = pj_sockaddr_init(ice_cfg.af, &ice_cfg.stun.cfg.bound_addr, local_ip_address, 0) if status != 0: raise PJSIPError("Could not init ICE bound address", status) with nogil: status = pjmedia_ice_create2(media_endpoint, NULL, 2, &ice_cfg, &_ice_cb, 0, transport_address) if status != 0: raise PJSIPError("Could not create ICE media transport", status) else: status = PJ_EBUG for i in xrange(ua._rtp_port_index, ua._rtp_port_index + ua._rtp_port_usable_count, 2): port = ua._rtp_port_start + i % ua._rtp_port_usable_count with nogil: status = pjmedia_transport_udp_create3(media_endpoint, af, NULL, local_ip_address, port, 0, transport_address) if status != PJ_ERRNO_START_SYS + EADDRINUSE: ua._rtp_port_index = (i + 2) % ua._rtp_port_usable_count break if status != 0: raise PJSIPError("Could not create UDP/RTP media transport", status) self._obj.user_data = self.weakref if self._encryption is not None: wrapped_transport = self._wrapped_transport = self._obj self._obj = NULL if self._encryption.startswith('sdes'): with nogil: pjmedia_srtp_setting_default(&srtp_setting) if self._encryption == 'sdes_mandatory': srtp_setting.use = PJMEDIA_SRTP_MANDATORY with nogil: status = pjmedia_transport_srtp_create(media_endpoint, wrapped_transport, &srtp_setting, transport_address) if status != 0: with nogil: pjmedia_transport_close(wrapped_transport) self._wrapped_transport = NULL raise PJSIPError("Could not create SRTP media transport", status) elif self._encryption == 'zrtp': with nogil: status = pjmedia_transport_zrtp_create(media_endpoint, pjsip_endpt_get_timer_heap(sip_endpoint), wrapped_transport, transport_address, 1) if status == 0: zid_file = ua.zrtp_cache.encode(sys.getfilesystemencoding()) c_zid_file = zid_file with nogil: # Auto-enable is deactivated status = pjmedia_transport_zrtp_initialize(self._obj, c_zid_file, 0, &_zrtp_cb) if status != 0: with nogil: pjmedia_transport_close(wrapped_transport) self._wrapped_transport = NULL raise PJSIPError("Could not create ZRTP media transport", status) else: raise RuntimeError('invalid SRTP key negotiation specified: %s' % self._encryption) self._obj.user_data = self.weakref if not self.use_ice or self.ice_stun_address is None: self.state = "INIT" _add_event("RTPTransportDidInitialize", dict(obj=self)) else: self.state = "WAIT_STUN" if self.use_ice: _add_event("RTPTransportICENegotiationStateDidChange", dict(obj=self, prev_state="NULL", state="GATHERING")) ice_st = pjmedia_ice_get_strans(transport_address[0]) if ice_st != NULL: ice_state = pj_ice_strans_get_state(ice_st) if ice_state == PJ_ICE_STRANS_STATE_READY: _add_event("RTPTransportICENegotiationStateDidChange", dict(obj=self, prev_state="GATHERING", state="GATHERING_COMPLETE")) else: raise SIPCoreError('set_INIT can only be called in the "NULL", "LOCAL" and "ESTABLISHED" states, ' + 'current state is "%s"' % self.state) finally: with nogil: pj_mutex_unlock(lock) def set_zrtp_sas_verified(self, verified): cdef int status cdef int c_verified cdef pj_mutex_t *lock = self._lock cdef pjmedia_zrtp_info *zrtp_info cdef pjmedia_transport_info info cdef PJSIPUA ua ua = self._check_ua() if ua is None: return False with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state in ["NULL", "WAIT_STUN", "INVALID"]: return False self._get_info(&info) zrtp_info = pjmedia_transport_info_get_spc_info(&info, PJMEDIA_TRANSPORT_TYPE_ZRTP) if zrtp_info == NULL or not bool(zrtp_info.active): return False c_verified = int(verified) with nogil: pjmedia_transport_zrtp_setSASVerified(self._obj, c_verified) return True finally: with nogil: pj_mutex_unlock(lock) def set_zrtp_enabled(self, enabled, object master_stream): cdef int status cdef int c_enabled cdef pj_mutex_t *lock = self._lock cdef pjmedia_zrtp_info *zrtp_info cdef pjmedia_transport_info info cdef PJSIPUA ua cdef bytes multistream_params cdef char *c_multistream_params cdef int length cdef RTPTransport master_transport ua = self._check_ua() if ua is None: return with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state in ["NULL", "WAIT_STUN", "INVALID"]: return self._get_info(&info) zrtp_info = pjmedia_transport_info_get_spc_info(&info, PJMEDIA_TRANSPORT_TYPE_ZRTP) if zrtp_info == NULL: return if master_stream is not None: master_transport = master_stream._rtp_transport assert master_transport is not None # extract the multistream parameters multistream_params = master_transport.zrtp_multistream_parameters if multistream_params: # set multistream mode in ourselves c_multistream_params = multistream_params length = len(multistream_params) with nogil: pjmedia_transport_zrtp_setMultiStreamParameters(self._obj, c_multistream_params, length, master_transport._obj) c_enabled = int(enabled) with nogil: pjmedia_transport_zrtp_setEnableZrtp(self._obj, c_enabled) finally: with nogil: pj_mutex_unlock(lock) property zrtp_multistream_parameters: def __get__(self): cdef int status cdef char* c_name cdef pj_mutex_t *lock = self._lock cdef pjmedia_zrtp_info *zrtp_info cdef pjmedia_transport_info info cdef PJSIPUA ua cdef char *multistr_params cdef int length ua = self._check_ua() if ua is None: return None with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state in ["NULL", "WAIT_STUN", "INVALID"]: return None self._get_info(&info) zrtp_info = pjmedia_transport_info_get_spc_info(&info, PJMEDIA_TRANSPORT_TYPE_ZRTP) if zrtp_info == NULL or not bool(zrtp_info.active): return None with nogil: multistr_params = pjmedia_transport_zrtp_getMultiStreamParameters(self._obj, &length) if length > 0: ret = PyString_FromStringAndSize(multistr_params, length) free(multistr_params) return ret else: return None finally: with nogil: pj_mutex_unlock(lock) property zrtp_cipher: def __get__(self): cdef int status cdef char* c_name cdef pj_mutex_t *lock = self._lock cdef pjmedia_zrtp_info *zrtp_info cdef pjmedia_transport_info info cdef PJSIPUA ua ua = self._check_ua() if ua is None: return None with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state in ["NULL", "WAIT_STUN", "INVALID"]: return None self._get_info(&info) zrtp_info = pjmedia_transport_info_get_spc_info(&info, PJMEDIA_TRANSPORT_TYPE_ZRTP) if zrtp_info == NULL or not bool(zrtp_info.active): return None return PyString_FromString(zrtp_info.cipher) finally: with nogil: pj_mutex_unlock(lock) property zrtp_peer_name: def __get__(self): cdef int status cdef char* c_name cdef pj_mutex_t *lock = self._lock cdef pjmedia_zrtp_info *zrtp_info cdef pjmedia_transport_info info cdef PJSIPUA ua ua = self._check_ua() if ua is None: return '' with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state in ["NULL", "WAIT_STUN", "INVALID"]: return '' self._get_info(&info) zrtp_info = pjmedia_transport_info_get_spc_info(&info, PJMEDIA_TRANSPORT_TYPE_ZRTP) if zrtp_info == NULL or not bool(zrtp_info.active): return '' with nogil: c_name = pjmedia_transport_zrtp_getPeerName(self._obj) if c_name == NULL: return '' else: name = PyUnicode_FromString(c_name) or u'' free(c_name) return name finally: with nogil: pj_mutex_unlock(lock) def __set__(self, basestring name): cdef int status cdef char* c_name cdef pj_mutex_t *lock = self._lock cdef pjmedia_zrtp_info *zrtp_info cdef pjmedia_transport_info info cdef PJSIPUA ua ua = self._check_ua() if ua is None: return with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state in ["NULL", "WAIT_STUN", "INVALID"]: return self._get_info(&info) zrtp_info = pjmedia_transport_info_get_spc_info(&info, PJMEDIA_TRANSPORT_TYPE_ZRTP) if zrtp_info == NULL or not bool(zrtp_info.active): return name = name.encode('utf-8') c_name = name with nogil: pjmedia_transport_zrtp_putPeerName(self._obj, c_name) finally: with nogil: pj_mutex_unlock(lock) property zrtp_peer_id: def __get__(self): cdef int status cdef unsigned char name[12] # IDENTIFIER_LEN, 96bits cdef pj_mutex_t *lock = self._lock cdef pjmedia_zrtp_info *zrtp_info cdef pjmedia_transport_info info cdef PJSIPUA ua ua = self._check_ua() if ua is None: return None with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state in ["NULL", "WAIT_STUN", "INVALID"]: return None self._get_info(&info) zrtp_info = pjmedia_transport_info_get_spc_info(&info, PJMEDIA_TRANSPORT_TYPE_ZRTP) if zrtp_info == NULL or not bool(zrtp_info.active): return None with nogil: status = pjmedia_transport_zrtp_getPeerZid(self._obj, name) if status <= 0: return None else: return PyString_FromStringAndSize(name, 12) finally: with nogil: pj_mutex_unlock(lock) def update_local_sdp(self, SDPSession local_sdp, BaseSDPSession remote_sdp=None, int sdp_index=0): cdef int status cdef pj_pool_t *pool cdef pjmedia_sdp_session *pj_local_sdp cdef pjmedia_sdp_session *pj_remote_sdp cdef pjmedia_transport *transport cdef SDPMediaStream local_media pool = self._pool transport = self._obj pj_local_sdp = local_sdp.get_sdp_session() if remote_sdp is not None: pj_remote_sdp = remote_sdp.get_sdp_session() else: pj_remote_sdp = NULL if sdp_index < 0: raise ValueError("sdp_index argument cannot be negative") if sdp_index >= pj_local_sdp.media_count: raise ValueError("sdp_index argument out of range") # Remove ICE and SRTP/ZRTP related attributes from SDP, they will be added by pjmedia_transport_encode_sdp local_media = local_sdp.media[sdp_index] local_media.attributes = [ attr for attr in local_media.attributes if attr.name not in ('crypto', 'zrtp-hash', 'ice-ufrag', 'ice-pwd', 'ice-mismatch', 'candidate', 'remote-candidates')] pj_local_sdp = local_sdp.get_sdp_session() with nogil: status = pjmedia_transport_encode_sdp(transport, pool, pj_local_sdp, pj_remote_sdp, sdp_index) if status != 0: raise PJSIPError("Could not update SDP for media transport", status) local_sdp._update() return 0 cdef class MediaCheckTimer(Timer): def __init__(self, media_check_interval): self.media_check_interval = media_check_interval cdef class SDPInfo: def __init__(self, BaseSDPMediaStream local_media=None, BaseSDPSession local_sdp=None, BaseSDPSession remote_sdp=None, int index=0): self.local_media = local_media self.local_sdp = local_sdp self.remote_sdp = remote_sdp self.index = index property local_media: def __get__(self): return self._local_media def __set__(self, local_media): if local_media is not None: local_media = SDPMediaStream.new(local_media) self._local_media = local_media property local_sdp: def __get__(self): return self._local_sdp def __set__(self, local_sdp): if local_sdp is not None: local_sdp = SDPSession.new(local_sdp) self._local_sdp = local_sdp property remote_sdp: def __get__(self): return self._remote_sdp def __set__(self, remote_sdp): if remote_sdp is not None: remote_sdp = SDPSession.new(remote_sdp) self._remote_sdp = remote_sdp cdef class AudioTransport: def __cinit__(self, *args, **kwargs): cdef int status cdef pj_pool_t *pool cdef bytes pool_name cdef char* c_pool_name cdef PJSIPUA ua ua = _get_ua() pool_name = b"AudioTransport_%d" % id(self) self.weakref = weakref.ref(self) Py_INCREF(self.weakref) status = pj_mutex_create_recursive(ua._pjsip_endpoint._pool, "audio_transport_lock", &self._lock) if status != 0: raise PJSIPError("failed to create lock", status) pool = ua.create_memory_pool(pool_name, 4096, 4096) self._pool = pool self._slot = -1 self._timer = None self._volume = 100 def __init__(self, AudioMixer mixer, RTPTransport transport, BaseSDPSession remote_sdp=None, int sdp_index=0, enable_silence_detection=False, list codecs=None): cdef int status cdef pj_pool_t *pool cdef pjmedia_endpt *media_endpoint cdef pjmedia_sdp_media *local_media_c cdef pjmedia_sdp_session *local_sdp_c cdef pj_sockaddr *addr cdef pjmedia_transport_info info cdef list global_codecs cdef SDPMediaStream local_media cdef SDPSession local_sdp cdef PJSIPUA ua ua = _get_ua() media_endpoint = ua._pjmedia_endpoint._obj pool = self._pool if self.transport is not None: raise SIPCoreError("AudioTransport.__init__() was already called") if mixer is None: raise ValueError("mixer argument may not be None") if transport is None: raise ValueError("transport argument cannot be None") if sdp_index < 0: raise ValueError("sdp_index argument cannot be negative") if transport.state != "INIT": raise SIPCoreError('RTPTransport object provided is not in the "INIT" state, but in the "%s" state' % transport.state) self._vad = int(bool(enable_silence_detection)) self.mixer = mixer self.transport = transport transport._get_info(&info) global_codecs = ua._pjmedia_endpoint._get_current_codecs() if codecs is None: codecs = global_codecs try: ua._pjmedia_endpoint._set_codecs(codecs) addr = &info.sock_info.rtp_addr_name with nogil: status = pjmedia_endpt_create_base_sdp(media_endpoint, pool, NULL, addr, &local_sdp_c) if status != 0: raise PJSIPError("Could not generate base SDP", status) with nogil: status = pjmedia_endpt_create_audio_sdp(media_endpoint, pool, &info.sock_info, 0, &local_media_c) if status != 0: raise PJSIPError("Could not generate SDP audio stream", status) # Create a 'fake' SDP, which only contains the audio stream, then the m line is extracted because the full # SDP is built by the Session local_sdp_c.media_count = 1 local_sdp_c.media[0] = local_media_c finally: ua._pjmedia_endpoint._set_codecs(global_codecs) local_sdp = SDPSession_create(local_sdp_c) local_media = local_sdp.media[0] if remote_sdp is None: self._is_offer = 1 self.transport.set_LOCAL(local_sdp, 0) else: self._is_offer = 0 if sdp_index != 0: local_sdp.media = [None] * (sdp_index+1) local_sdp.media[sdp_index] = local_media self.transport.set_REMOTE(local_sdp, remote_sdp, sdp_index) self._sdp_info = SDPInfo(local_media, local_sdp, remote_sdp, sdp_index) def __dealloc__(self): cdef PJSIPUA ua cdef Timer timer try: ua = _get_ua() except: return if self._obj != NULL: self.stop() ua.release_memory_pool(self._pool) self._pool = NULL if self._lock != NULL: pj_mutex_destroy(self._lock) timer = Timer() try: timer.schedule(60, deallocate_weakref, self.weakref) except SIPCoreError: pass cdef PJSIPUA _check_ua(self): cdef PJSIPUA ua try: ua = _get_ua() return ua except: self._obj = NULL self._pool = NULL return None property is_active: def __get__(self): self._check_ua() return bool(self._obj != NULL) property is_started: def __get__(self): return bool(self._is_started) property codec: def __get__(self): self._check_ua() if self._obj == NULL: return None else: return _pj_str_to_str(self._stream_info.fmt.encoding_name) property sample_rate: def __get__(self): self._check_ua() if self._obj == NULL: return None else: return self._stream_info.fmt.clock_rate property enable_silence_detection: def __get__(self): return bool(self._vad) property statistics: def __get__(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_rtcp_stat stat cdef pjmedia_stream *stream cdef dict statistics = dict() cdef PJSIPUA ua ua = self._check_ua() if ua is None: return None with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: stream = self._obj if stream == NULL: return None with nogil: status = pjmedia_stream_get_stat(stream, &stat) if status != 0: raise PJSIPError("Could not get RTP statistics", status) statistics["rtt"] = _pj_math_stat_to_dict(&stat.rtt) statistics["rx"] = _pjmedia_rtcp_stream_stat_to_dict(&stat.rx) statistics["tx"] = _pjmedia_rtcp_stream_stat_to_dict(&stat.tx) return statistics finally: with nogil: pj_mutex_unlock(lock) property volume: def __get__(self): return self._volume def __set__(self, value): cdef int slot cdef int status cdef int volume cdef pj_mutex_t *lock = self._lock cdef pjmedia_conf *conf_bridge cdef PJSIPUA ua ua = self._check_ua() if ua is not None: with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: conf_bridge = self.mixer._obj slot = self._slot if value < 0: raise ValueError("volume attribute cannot be negative") if ua is not None and self._obj != NULL: volume = int(value * 1.28 - 128) with nogil: status = pjmedia_conf_adjust_rx_level(conf_bridge, slot, volume) if status != 0: raise PJSIPError("Could not set volume of audio transport", status) self._volume = value finally: if ua is not None: with nogil: pj_mutex_unlock(lock) property slot: def __get__(self): self._check_ua() if self._slot == -1: return None else: return self._slot def get_local_media(self, BaseSDPSession remote_sdp=None, int index=0, direction="sendrecv"): global valid_sdp_directions cdef int status cdef pj_mutex_t *lock = self._lock cdef object direction_attr cdef SDPAttribute attr cdef SDPSession local_sdp cdef SDPMediaStream local_media cdef pjmedia_sdp_media *c_local_media _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: is_offer = remote_sdp is None if is_offer and direction not in valid_sdp_directions: raise SIPCoreError("Unknown direction: %s" % direction) self._sdp_info.index = index local_sdp = self._sdp_info.local_sdp local_media = self._sdp_info.local_media local_sdp.media = [None] * (index+1) local_sdp.media[index] = local_media self.transport.update_local_sdp(local_sdp, remote_sdp, index) # updating the local SDP might have modified the connection line if local_sdp.connection is not None and local_media.connection is None: local_media.connection = SDPConnection.new(local_sdp.connection) local_media.attributes = [ attr for attr in local_media.attributes if attr.name not in valid_sdp_directions] if is_offer: direction_attr = direction else: if self.direction is None or "recv" in self.direction: direction_attr = "sendrecv" else: direction_attr = "sendonly" local_media.attributes.append(SDPAttribute(direction_attr, "")) for attribute in local_media.attributes: if attribute.name == 'rtcp': attribute.value = attribute.value.split(' ', 1)[0] self._sdp_info.local_media = local_media return local_media finally: with nogil: pj_mutex_unlock(lock) def start(self, BaseSDPSession local_sdp, BaseSDPSession remote_sdp, int sdp_index, int timeout=30): cdef int status cdef object desired_state cdef pj_mutex_t *lock = self._lock cdef pj_pool_t *pool cdef pjmedia_endpt *media_endpoint cdef pjmedia_port *media_port cdef pjmedia_sdp_media *local_media cdef pjmedia_sdp_session *pj_local_sdp cdef pjmedia_sdp_session *pj_remote_sdp cdef pjmedia_stream **stream_address cdef pjmedia_stream_info *stream_info_address cdef pjmedia_transport *transport cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: pool = self._pool media_endpoint = ua._pjmedia_endpoint._obj stream_address = &self._obj stream_info_address = &self._stream_info transport = self.transport._obj if self._is_started: raise SIPCoreError("This AudioTransport was already started once") desired_state = ("LOCAL" if self._is_offer else "REMOTE") if self.transport.state != desired_state: raise SIPCoreError('RTPTransport object provided is not in the "%s" state, but in the "%s" state' % (desired_state, self.transport.state)) if None in [local_sdp, remote_sdp]: raise ValueError("SDP arguments cannot be None") pj_local_sdp = local_sdp.get_sdp_session() pj_remote_sdp = remote_sdp.get_sdp_session() if sdp_index < 0: raise ValueError("sdp_index argument cannot be negative") if local_sdp.media[sdp_index].port == 0 or remote_sdp.media[sdp_index].port == 0: raise SIPCoreError("Cannot start a rejected audio stream") if timeout < 0: raise ValueError("timeout value cannot be negative") self.transport.set_ESTABLISHED(local_sdp, remote_sdp, sdp_index) with nogil: status = pjmedia_stream_info_from_sdp(stream_info_address, pool, media_endpoint, pj_local_sdp, pj_remote_sdp, sdp_index) if status != 0: raise PJSIPError("Could not parse SDP for audio session", status) if self._stream_info.param == NULL: raise SIPCoreError("Could not parse SDP for audio session") self._stream_info.param.setting.vad = self._vad self._stream_info.use_ka = 1 with nogil: status = pjmedia_stream_create(media_endpoint, pool, stream_info_address, transport, NULL, stream_address) if status != 0: raise PJSIPError("Could not initialize RTP for audio session", status) with nogil: status = pjmedia_stream_set_dtmf_callback(stream_address[0], _AudioTransport_cb_dtmf, self.weakref) if status != 0: with nogil: pjmedia_stream_destroy(stream_address[0]) self._obj = NULL raise PJSIPError("Could not set DTMF callback for audio session", status) with nogil: status = pjmedia_stream_start(stream_address[0]) if status != 0: with nogil: pjmedia_stream_destroy(stream_address[0]) self._obj = NULL raise PJSIPError("Could not start RTP for audio session", status) with nogil: status = pjmedia_stream_get_port(stream_address[0], &media_port) if status != 0: with nogil: pjmedia_stream_destroy(stream_address[0]) self._obj = NULL raise PJSIPError("Could not get audio port for audio session", status) try: self._slot = self.mixer._add_port(ua, pool, media_port) if self._volume != 100: self.volume = self._volume except: with nogil: pjmedia_stream_destroy(stream_address[0]) self._obj = NULL raise self.update_direction(local_sdp.media[sdp_index].direction) self._sdp_info.local_media = local_sdp.media[sdp_index] self._sdp_info.local_sdp = local_sdp self._sdp_info.remote_sdp = remote_sdp self._sdp_info.index = sdp_index self._is_started = 1 if timeout > 0: self._timer = MediaCheckTimer(timeout) self._timer.schedule(timeout, self._cb_check_rtp, self) self.mixer.reset_ec() finally: with nogil: pj_mutex_unlock(lock) def stop(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_stream *stream cdef PJSIPUA ua ua = self._check_ua() if ua is not None: with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: stream = self._obj if self._timer is not None: self._timer.cancel() self._timer = None if self._obj == NULL: return self._obj = NULL self.mixer._remove_port(ua, self._slot) with nogil: pjmedia_stream_destroy(stream) self.transport.set_INIT() finally: if ua is not None: with nogil: pj_mutex_unlock(lock) def update_direction(self, direction): cdef int status cdef pj_mutex_t *lock = self._lock _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._obj == NULL: raise SIPCoreError("Stream is not active") if direction not in valid_sdp_directions: raise SIPCoreError("Unknown direction: %s" % direction) if direction != self.direction: self.mixer.reset_ec() self.direction = direction finally: with nogil: pj_mutex_unlock(lock) def update_sdp(self, local_sdp, remote_sdp, index): cdef int status cdef pj_mutex_t *lock = self._lock _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._obj == NULL: raise SIPCoreError("Stream is not active") self._sdp_info.local_media = local_sdp.media[index] self._sdp_info.local_sdp = local_sdp self._sdp_info.remote_sdp = remote_sdp self._sdp_info.index = index finally: with nogil: pj_mutex_unlock(lock) def send_dtmf(self, digit): cdef int status cdef pj_mutex_t *lock = self._lock cdef pj_str_t digit_pj cdef pjmedia_stream *stream cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: stream = self._obj if self._obj == NULL: raise SIPCoreError("Stream is not active") if len(digit) != 1 or digit not in "0123456789*#ABCD": raise SIPCoreError("Not a valid DTMF digit: %s" % digit) _str_to_pj_str(digit, &digit_pj) if not self._stream_info.tx_event_pt < 0: # If the remote doesn't support telephone-event just don't send DTMF with nogil: status = pjmedia_stream_dial_dtmf(stream, &digit_pj) if status != 0: raise PJSIPError("Could not send DTMF digit on audio stream", status) finally: with nogil: pj_mutex_unlock(lock) cdef int _cb_check_rtp(self, MediaCheckTimer timer) except -1: cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_rtcp_stat stat cdef pjmedia_stream *stream with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: stream = self._obj if stream == NULL: return 0 if self._timer is None: return 0 self._timer = None with nogil: status = pjmedia_stream_get_stat(stream, &stat) if status == 0: if self._packets_received == stat.rx.pkt and self.direction == "sendrecv": _add_event("RTPAudioTransportDidTimeout", dict(obj=self)) self._packets_received = stat.rx.pkt if timer.media_check_interval > 0: self._timer = MediaCheckTimer(timer.media_check_interval) self._timer.schedule(timer.media_check_interval, self._cb_check_rtp, self) finally: with nogil: pj_mutex_unlock(lock) cdef class VideoTransport: def __cinit__(self, *args, **kwargs): cdef int status cdef pj_pool_t *pool cdef bytes pool_name cdef PJSIPUA ua ua = _get_ua() endpoint = ua._pjsip_endpoint._obj pool_name = b"VideoTransport_%d" % id(self) self.weakref = weakref.ref(self) Py_INCREF(self.weakref) pool = ua.create_memory_pool(pool_name, 4096, 4096) self._pool = pool status = pj_mutex_create_recursive(pool, "video_transport_lock", &self._lock) if status != 0: raise PJSIPError("failed to create lock", status) self._timer = None def __init__(self, RTPTransport transport, BaseSDPSession remote_sdp=None, int sdp_index=0, list codecs=None): cdef int status cdef pj_pool_t *pool cdef pjmedia_endpt *media_endpoint cdef pjmedia_sdp_media *local_media_c cdef pjmedia_sdp_session *local_sdp_c cdef pjmedia_transport_info info cdef pj_sockaddr *addr cdef list global_codecs cdef SDPMediaStream local_media cdef SDPSession local_sdp cdef PJSIPUA ua ua = _get_ua() media_endpoint = ua._pjmedia_endpoint._obj pool = self._pool if self.transport is not None: raise SIPCoreError("VideoTransport.__init__() was already called") if transport is None: raise ValueError("transport argument cannot be None") if sdp_index < 0: raise ValueError("sdp_index argument cannot be negative") if transport.state != "INIT": raise SIPCoreError('RTPTransport object provided is not in the "INIT" state, but in the "%s" state' % transport.state) self.transport = transport transport._get_info(&info) global_codecs = ua._pjmedia_endpoint._get_current_video_codecs() if codecs is None: codecs = global_codecs try: ua._pjmedia_endpoint._set_video_codecs(codecs) addr = &(info.sock_info.rtp_addr_name) with nogil: status = pjmedia_endpt_create_base_sdp(media_endpoint, pool, NULL, addr, &local_sdp_c) if status != 0: raise PJSIPError("Could not generate base SDP", status) with nogil: status = pjmedia_endpt_create_video_sdp(media_endpoint, pool, &info.sock_info, 0, &local_media_c) if status != 0: raise PJSIPError("Could not generate SDP video stream", status) # Create a 'fake' SDP, which only contains the video stream, then the m line is extracted because the full # SDP is built by the Session local_sdp_c.media_count = 1 local_sdp_c.media[0] = local_media_c finally: ua._pjmedia_endpoint._set_video_codecs(global_codecs) local_sdp = SDPSession_create(local_sdp_c) local_media = local_sdp.media[0] if remote_sdp is None: self._is_offer = 1 self.transport.set_LOCAL(local_sdp, 0) else: self._is_offer = 0 if sdp_index != 0: local_sdp.media = [None] * (sdp_index+1) local_sdp.media[sdp_index] = local_media self.transport.set_REMOTE(local_sdp, remote_sdp, sdp_index) self._sdp_info = SDPInfo(local_media, local_sdp, remote_sdp, sdp_index) self.local_video = None self.remote_video = None def __dealloc__(self): cdef PJSIPUA ua cdef Timer timer try: ua = _get_ua() except SIPCoreError: return if self._obj != NULL: self.stop() if self._lock != NULL: pj_mutex_destroy(self._lock) ua.release_memory_pool(self._pool) self._pool = NULL timer = Timer() try: timer.schedule(60, deallocate_weakref, self.weakref) except SIPCoreError: pass cdef PJSIPUA _check_ua(self): cdef PJSIPUA ua try: ua = _get_ua() return ua except: self._obj = NULL self._pool = NULL return None property is_active: def __get__(self): self._check_ua() return bool(self._obj != NULL) property is_started: def __get__(self): return bool(self._is_started) property codec: def __get__(self): self._check_ua() if self._obj == NULL: return None else: return _pj_str_to_str(self._stream_info.codec_info.encoding_name) property sample_rate: def __get__(self): self._check_ua() if self._obj == NULL: return None else: return self._stream_info.codec_info.clock_rate property statistics: def __get__(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_rtcp_stat stat cdef pjmedia_vid_stream *stream cdef dict statistics = dict() cdef PJSIPUA ua ua = self._check_ua() if ua is None: return None with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: stream = self._obj if stream == NULL: return None with nogil: status = pjmedia_vid_stream_get_stat(stream, &stat) if status != 0: raise PJSIPError("Could not get RTP statistics", status) statistics["rtt"] = _pj_math_stat_to_dict(&stat.rtt) statistics["rx"] = _pjmedia_rtcp_stream_stat_to_dict(&stat.rx) statistics["tx"] = _pjmedia_rtcp_stream_stat_to_dict(&stat.tx) return statistics finally: with nogil: pj_mutex_unlock(lock) def get_local_media(self, BaseSDPSession remote_sdp=None, int index=0, direction="sendrecv"): global valid_sdp_directions cdef int status cdef pj_mutex_t *lock = self._lock cdef object direction_attr cdef SDPAttribute attr cdef SDPSession local_sdp cdef SDPMediaStream local_media cdef pjmedia_sdp_media *c_local_media _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: is_offer = remote_sdp is None if is_offer and direction not in valid_sdp_directions: raise SIPCoreError("Unknown direction: %s" % direction) self._sdp_info.index = index local_sdp = self._sdp_info.local_sdp local_media = self._sdp_info.local_media local_sdp.media = [None] * (index+1) local_sdp.media[index] = local_media self.transport.update_local_sdp(local_sdp, remote_sdp, index) # updating the local SDP might have modified the connection line if local_sdp.connection is not None and local_media.connection is None: local_media.connection = SDPConnection.new(local_sdp.connection) local_media.attributes = [ attr for attr in local_media.attributes if attr.name not in valid_sdp_directions] if is_offer: direction_attr = direction else: if self.direction is None or "recv" in self.direction: direction_attr = "sendrecv" else: direction_attr = "sendonly" local_media.attributes.append(SDPAttribute(direction_attr, "")) for attribute in local_media.attributes: if attribute.name == 'rtcp': attribute.value = attribute.value.split(' ', 1)[0] return local_media finally: with nogil: pj_mutex_unlock(lock) def start(self, BaseSDPSession local_sdp, BaseSDPSession remote_sdp, int sdp_index, int timeout=30): cdef int status cdef object desired_state cdef pj_mutex_t *lock = self._lock cdef pj_pool_t *pool cdef pjmedia_endpt *media_endpoint cdef pjmedia_sdp_media *local_media cdef pjmedia_sdp_session *pj_local_sdp cdef pjmedia_sdp_session *pj_remote_sdp cdef pjmedia_vid_stream *stream cdef pjmedia_vid_stream_info *stream_info cdef pjmedia_transport *transport cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: pool = self._pool media_endpoint = ua._pjmedia_endpoint._obj stream_info = &self._stream_info transport = self.transport._obj if self._is_started: raise SIPCoreError("This VideoTransport was already started once") desired_state = ("LOCAL" if self._is_offer else "REMOTE") if self.transport.state != desired_state: raise SIPCoreError('RTPTransport object provided is not in the "%s" state, but in the "%s" state' % (desired_state, self.transport.state)) if None in (local_sdp, remote_sdp): raise ValueError("SDP arguments cannot be None") pj_local_sdp = local_sdp.get_sdp_session() pj_remote_sdp = remote_sdp.get_sdp_session() if sdp_index < 0: raise ValueError("sdp_index argument cannot be negative") if local_sdp.media[sdp_index].port == 0 or remote_sdp.media[sdp_index].port == 0: raise SIPCoreError("Cannot start a rejected video stream") if timeout < 0: raise ValueError("timeout value cannot be negative") self.transport.set_ESTABLISHED(local_sdp, remote_sdp, sdp_index) with nogil: status = pjmedia_vid_stream_info_from_sdp(stream_info, pool, media_endpoint, pj_local_sdp, pj_remote_sdp, sdp_index) if status != 0: raise PJSIPError("Could not parse SDP for video session", status) if self._stream_info.codec_param == NULL: raise SIPCoreError("Could not parse SDP for video session") self._stream_info.use_ka = 1 with nogil: status = pjmedia_vid_stream_create(media_endpoint, pool, stream_info, transport, NULL, &stream) if status != 0: raise PJSIPError("Could not initialize RTP for video session", status) self._obj = stream with nogil: status = pjmedia_vid_stream_start(stream) if status != 0: with nogil: pjmedia_vid_stream_destroy(stream) self._obj = NULL raise PJSIPError("Could not start RTP for video session", status) with nogil: pjmedia_vid_stream_send_rtcp_sdes(stream) try: local_video = LocalVideoStream_create(stream) remote_video = RemoteVideoStream_create(stream, self._remote_video_event_handler) except PJSIPError: with nogil: pjmedia_vid_stream_destroy(stream) self._obj = NULL self.local_video = None self.remote_video = None raise self.local_video = local_video self.remote_video = remote_video self.update_direction(local_sdp.media[sdp_index].direction) self._sdp_info.local_media = local_sdp.media[sdp_index] self._sdp_info.local_sdp = local_sdp self._sdp_info.remote_sdp = remote_sdp self._sdp_info.index = sdp_index self._is_started = 1 if timeout > 0: self._timer = MediaCheckTimer(timeout) self._timer.schedule(timeout, self._cb_check_rtp, self) finally: with nogil: pj_mutex_unlock(lock) def stop(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_vid_stream *stream cdef PJSIPUA ua ua = self._check_ua() if ua is not None: with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: stream = self._obj if self._timer is not None: self._timer.cancel() self._timer = None if self._obj == NULL: return self._obj = NULL if self.local_video is not None: self.local_video.close() self.local_video = None if self.remote_video is not None: self.remote_video.close() self.remote_video = None with nogil: pjmedia_vid_stream_send_rtcp_bye(stream) pjmedia_vid_stream_destroy(stream) self.transport.set_INIT() finally: if ua is not None: with nogil: pj_mutex_unlock(lock) def update_direction(self, direction): global valid_sdp_directions cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_vid_stream *stream _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: stream = self._obj if self._obj == NULL: raise SIPCoreError("Stream is not active") if direction not in valid_sdp_directions: raise SIPCoreError("Unknown direction: %s" % direction) self.direction = direction finally: with nogil: pj_mutex_unlock(lock) def update_sdp(self, local_sdp, remote_sdp, index): cdef int status cdef pj_mutex_t *lock = self._lock _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._obj == NULL: raise SIPCoreError("Stream is not active") self._sdp_info.local_media = local_sdp.media[index] self._sdp_info.local_sdp = local_sdp self._sdp_info.remote_sdp = remote_sdp self._sdp_info.index = index finally: with nogil: pj_mutex_unlock(lock) def pause(self, direction="both"): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_vid_stream *stream cdef pjmedia_dir pj_dir _get_ua() if direction not in ("incoming", "outgoing", "both"): raise ValueError("direction can only be one of 'incoming', 'outgoing' or 'both'") if direction == "incoming": pj_dir = PJMEDIA_DIR_RENDER elif direction == "outgoing": pj_dir = PJMEDIA_DIR_CAPTURE else: pj_dir = PJMEDIA_DIR_CAPTURE_RENDER with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: stream = self._obj if self._obj == NULL: raise SIPCoreError("Stream is not active") with nogil: status = pjmedia_vid_stream_pause(stream, pj_dir) if status != 0: raise PJSIPError("failed to pause video stream", status) finally: with nogil: pj_mutex_unlock(lock) def resume(self, direction="both"): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_vid_stream *stream cdef pjmedia_dir pj_dir _get_ua() if direction not in ("incoming", "outgoing", "both"): raise ValueError("direction can only be one of 'incoming', 'outgoing' or 'both'") if direction == "incoming": pj_dir = PJMEDIA_DIR_RENDER elif direction == "outgoing": pj_dir = PJMEDIA_DIR_CAPTURE else: pj_dir = PJMEDIA_DIR_CAPTURE_RENDER with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: stream = self._obj if self._obj == NULL: raise SIPCoreError("Stream is not active") with nogil: status = pjmedia_vid_stream_resume(stream, pj_dir) if status != 0: raise PJSIPError("failed to resume video stream", status) finally: with nogil: pj_mutex_unlock(lock) def send_keyframe(self): cdef pj_mutex_t *lock = self._lock cdef pjmedia_vid_stream *stream _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: stream = self._obj if stream != NULL: # Do not check for errors, it's OK if we can't send it pjmedia_vid_stream_send_keyframe(stream) finally: with nogil: pj_mutex_unlock(lock) def request_keyframe(self): cdef pj_mutex_t *lock = self._lock cdef pjmedia_vid_stream *stream _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: stream = self._obj if stream != NULL: # Do not check for errors, it's OK if we can't send it pjmedia_vid_stream_send_rtcp_pli(stream) finally: with nogil: pj_mutex_unlock(lock) cdef int _cb_check_rtp(self, MediaCheckTimer timer) except -1: cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_rtcp_stat stat cdef pjmedia_vid_stream *stream with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: stream = self._obj if stream == NULL: return 0 if self._timer is None: return 0 self._timer = None with nogil: status = pjmedia_vid_stream_get_stat(stream, &stat) if status == 0: if self._packets_received == stat.rx.pkt and self.direction == "sendrecv": _add_event("RTPVideoTransportDidTimeout", dict(obj=self)) self._packets_received = stat.rx.pkt if timer.media_check_interval > 0: self._timer = MediaCheckTimer(timer.media_check_interval) self._timer.schedule(timer.media_check_interval, self._cb_check_rtp, self) finally: with nogil: pj_mutex_unlock(lock) def _remote_video_event_handler(self, str name, object data): if name == "FORMAT_CHANGED": size, framerate = data _add_event("RTPVideoTransportRemoteFormatDidChange", dict(obj=self, size=size, framerate=framerate)) elif name == "RECEIVED_KEYFRAME": _add_event("RTPVideoTransportReceivedKeyFrame", dict(obj=self)) elif name == "MISSED_KEYFRAME": _add_event("RTPVideoTransportMissedKeyFrame", dict(obj=self)) elif name == "REQUESTED_KEYFRAME": _add_event("RTPVideoTransportRequestedKeyFrame", dict(obj=self)) cdef class ICECandidate: def __init__(self, component, cand_type, address, port, priority, rel_addr=''): self.component = component self.type = cand_type self.address = address self.port = port self.priority = priority self.rel_address = rel_addr def __str__(self): return '(%s) %s:%d%s priority=%d type=%s' % (self.component, self.address, self.port, ' rel_addr=%s' % self.rel_address if self.rel_address else '', self.priority, self.type.lower()) cdef ICECandidate ICECandidate_create(pj_ice_sess_cand *cand): cdef char buf[PJ_INET6_ADDRSTRLEN] cdef str address cdef str cand_type cdef int port if cand.type == PJ_ICE_CAND_TYPE_HOST: cand_type = 'HOST' elif cand.type == PJ_ICE_CAND_TYPE_SRFLX: cand_type = 'SRFLX' elif cand.type == PJ_ICE_CAND_TYPE_PRFLX: cand_type = 'PRFLX' elif cand.type == PJ_ICE_CAND_TYPE_RELAYED: cand_type = 'RELAY' else: cand_type = 'UNKNOWN' pj_sockaddr_print(&cand.addr, buf, PJ_INET6_ADDRSTRLEN, 0) address = PyString_FromString(buf) port = pj_sockaddr_get_port(&cand.addr) if pj_sockaddr_has_addr(&cand.rel_addr): pj_sockaddr_print(&cand.rel_addr, buf, PJ_INET6_ADDRSTRLEN, 0) rel_addr = PyString_FromString(buf) else: rel_addr = '' return ICECandidate('RTP' if cand.comp_id==1 else 'RTCP', cand_type, address, port, cand.prio, rel_addr) cdef class ICECheck: def __init__(self, local_candidate, remote_candidate, state, nominated): self.local_candidate = local_candidate self.remote_candidate = remote_candidate self.state = state self.nominated = nominated def __str__(self): return '%s:%d -> %s:%d (%s, %s)' % (self.local_candidate.address, self.local_candidate.port, self.remote_candidate.address, self.remote_candidate.port, self.state.lower(), 'nominated' if self.nominated else 'not nominated') cdef ICECheck ICECheck_create(pj_ice_sess_check *check): cdef str state cdef ICECandidate lcand cdef ICECandidate rcand if check.state == PJ_ICE_SESS_CHECK_STATE_FROZEN: state = 'FROZEN' elif check.state == PJ_ICE_SESS_CHECK_STATE_WAITING: state = 'WAITING' elif check.state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS: state = 'IN_PROGRESS' elif check.state == PJ_ICE_SESS_CHECK_STATE_SUCCEEDED: state = 'SUCCEEDED' elif check.state == PJ_ICE_SESS_CHECK_STATE_FAILED: state = 'FAILED' else: state = 'UNKNOWN' lcand = ICECandidate_create(check.lcand) rcand = ICECandidate_create(check.rcand) return ICECheck(lcand, rcand, state, bool(check.nominated)) cdef ICECheck _get_rtp_valid_pair(pj_ice_strans *ice_st): cdef pj_ice_sess_check_ptr_const ice_check ice_check = pj_ice_strans_get_valid_pair(ice_st, 1) if ice_check == NULL: return None return ICECheck_create(ice_check) # helper functions cdef dict _pj_math_stat_to_dict(pj_math_stat *stat): cdef dict retval = dict() retval["count"] = stat.n retval["max"] = stat.max retval["min"] = stat.min retval["last"] = stat.last retval["avg"] = stat.mean return retval cdef dict _pjmedia_rtcp_stream_stat_to_dict(pjmedia_rtcp_stream_stat *stream_stat): cdef dict retval = dict() retval["packets"] = stream_stat.pkt retval["bytes"] = stream_stat.bytes retval["packets_discarded"] = stream_stat.discard retval["packets_lost"] = stream_stat.loss retval["packets_reordered"] = stream_stat.reorder retval["packets_duplicate"] = stream_stat.dup retval["loss_period"] = _pj_math_stat_to_dict(&stream_stat.loss_period) retval["burst_loss"] = bool(stream_stat.loss_type.burst) retval["random_loss"] = bool(stream_stat.loss_type.random) retval["jitter"] = _pj_math_stat_to_dict(&stream_stat.jitter) return retval cdef str _ice_state_to_str(int state): if state == PJ_ICE_STRANS_STATE_NULL: return 'NULL' elif state == PJ_ICE_STRANS_STATE_INIT: return 'GATHERING' elif state == PJ_ICE_STRANS_STATE_READY: return 'GATHERING_COMPLETE' elif state == PJ_ICE_STRANS_STATE_SESS_READY: return 'NEGOTIATION_START' elif state == PJ_ICE_STRANS_STATE_NEGO: return 'NEGOTIATING' elif state == PJ_ICE_STRANS_STATE_RUNNING: return 'RUNNING' elif state == PJ_ICE_STRANS_STATE_FAILED: return 'FAILED' else: return 'UNKNOWN' cdef dict _extract_ice_session_data(pj_ice_sess *ice_sess): cdef dict data = dict() cdef pj_ice_sess_cand *cand cdef pj_ice_sess_check *check # Process local candidates local_candidates = [] for i in range(ice_sess.lcand_cnt): cand = &ice_sess.lcand[i] local_candidates.append(ICECandidate_create(cand)) data['local_candidates'] = local_candidates # Process remote candidates remote_candidates = [] for i in range(ice_sess.rcand_cnt): cand = &ice_sess.rcand[i] remote_candidates.append(ICECandidate_create(cand)) data['remote_candidates'] = remote_candidates # Process valid pairs valid_pairs = [] for i in range(ice_sess.comp_cnt): check = ice_sess.comp[i].valid_check valid_pairs.append(ICECheck_create(check)) data['valid_pairs'] = valid_pairs # Process valid list valid_list = [] for i in range(ice_sess.valid_list.count): check = &ice_sess.valid_list.checks[i] valid_list.append(ICECheck_create(check)) data['valid_list'] = valid_list return data cdef object _extract_rtp_transport(pjmedia_transport *tp): cdef void *rtp_transport_ptr = NULL if tp != NULL: rtp_transport_ptr = tp.user_data if rtp_transport_ptr == NULL: return None return ( rtp_transport_ptr)() # callback functions cdef void _RTPTransport_cb_ice_complete(pjmedia_transport *tp, pj_ice_strans_op op, int status) with gil: # Despite the name this callback is not only called when ICE negotiation ends, it depends on the # op parameter cdef int lock_status cdef double duration cdef pj_ice_strans *ice_st cdef pj_ice_sess *ice_sess cdef pj_time_val tv, start_time cdef pj_mutex_t *lock cdef RTPTransport rtp_transport cdef PJSIPUA ua try: ua = _get_ua() except: return rtp_transport = _extract_rtp_transport(tp) if rtp_transport is None: return lock = rtp_transport._lock with nogil: lock_status = pj_mutex_lock(lock) if lock_status != 0: raise PJSIPError("failed to acquire lock", status) try: if op == PJ_ICE_STRANS_OP_NEGOTIATION: if status == 0: ice_st = pjmedia_ice_get_strans(tp) if ice_st == NULL: return ice_sess = pj_ice_strans_get_session(ice_st) if ice_sess == NULL: return start_time = pj_ice_strans_get_start_time(ice_st) pj_gettimeofday(&tv) tv.sec -= start_time.sec tv.msec -= start_time.msec pj_time_val_normalize(&tv) duration = (tv.sec*1000 + tv.msec)/1000.0 data = _extract_ice_session_data(ice_sess) rtp_transport._rtp_valid_pair = _get_rtp_valid_pair(ice_st) _add_event("RTPTransportICENegotiationDidSucceed", dict(obj=rtp_transport, duration=duration, local_candidates=data['local_candidates'], remote_candidates=data['remote_candidates'], valid_pairs=data['valid_pairs'], valid_list=data['valid_list'])) else: rtp_transport._rtp_valid_pair = None _add_event("RTPTransportICENegotiationDidFail", dict(obj=rtp_transport, reason=_pj_status_to_str(status))) elif op == PJ_ICE_STRANS_OP_INIT: if status == 0: rtp_transport.state = "INIT" _add_event("RTPTransportDidInitialize", dict(obj=rtp_transport)) else: rtp_transport.state = "INVALID" _add_event("RTPTransportDidFail", dict(obj=rtp_transport, reason=_pj_status_to_str(status))) else: # silence compiler warning pass finally: with nogil: pj_mutex_unlock(lock) cdef void _RTPTransport_cb_ice_state(pjmedia_transport *tp, pj_ice_strans_state prev, pj_ice_strans_state curr) with gil: cdef int status cdef pj_mutex_t *lock cdef RTPTransport rtp_transport cdef PJSIPUA ua try: ua = _get_ua() except: return rtp_transport = _extract_rtp_transport(tp) if rtp_transport is None: return lock = rtp_transport._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: _add_event("RTPTransportICENegotiationStateDidChange", dict(obj=rtp_transport, prev_state=_ice_state_to_str(prev), state=_ice_state_to_str(curr))) finally: with nogil: pj_mutex_unlock(lock) cdef void _RTPTransport_cb_ice_stop(pjmedia_transport *tp, char *reason, int err) with gil: cdef int status cdef pj_mutex_t *lock cdef RTPTransport rtp_transport cdef PJSIPUA ua try: ua = _get_ua() except: return rtp_transport = _extract_rtp_transport(tp) if rtp_transport is None: return lock = rtp_transport._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: rtp_transport._rtp_valid_pair = None _reason = reason if _reason != b"media stop requested": _add_event("RTPTransportICENegotiationDidFail", dict(obj=rtp_transport, reason=_reason)) finally: with nogil: pj_mutex_unlock(lock) cdef void _RTPTransport_cb_zrtp_secure_on(pjmedia_transport *tp, char* cipher) with gil: cdef RTPTransport rtp_transport cdef PJSIPUA ua try: ua = _get_ua() except: return try: rtp_transport = _extract_rtp_transport(tp) if rtp_transport is None: return _add_event("RTPTransportZRTPSecureOn", dict(obj=rtp_transport, cipher=bytes(cipher))) except: ua._handle_exception(1) cdef void _RTPTransport_cb_zrtp_secure_off(pjmedia_transport *tp) with gil: cdef RTPTransport rtp_transport cdef PJSIPUA ua try: ua = _get_ua() except: return try: rtp_transport = _extract_rtp_transport(tp) if rtp_transport is None: return _add_event("RTPTransportZRTPSecureOff", dict(obj=rtp_transport)) except: ua._handle_exception(1) cdef void _RTPTransport_cb_zrtp_show_sas(pjmedia_transport *tp, char* sas, int verified) with gil: cdef RTPTransport rtp_transport cdef PJSIPUA ua try: ua = _get_ua() except: return try: rtp_transport = _extract_rtp_transport(tp) if rtp_transport is None: return _add_event("RTPTransportZRTPReceivedSAS", dict(obj=rtp_transport, sas=str(sas), verified=bool(verified))) except: ua._handle_exception(1) cdef void _RTPTransport_cb_zrtp_confirm_goclear(pjmedia_transport *tp) with gil: cdef RTPTransport rtp_transport cdef PJSIPUA ua try: ua = _get_ua() except: return try: rtp_transport = _extract_rtp_transport(tp) if rtp_transport is None: return # TODO: not yet implemented by PJSIP's ZRTP transport except: ua._handle_exception(1) cdef void _RTPTransport_cb_zrtp_show_message(pjmedia_transport *tp, int severity, int sub_code) with gil: global zrtp_message_levels, zrtp_error_messages cdef RTPTransport rtp_transport cdef PJSIPUA ua try: ua = _get_ua() except: return try: rtp_transport = _extract_rtp_transport(tp) if rtp_transport is None: return level = zrtp_message_levels.get(severity, 1) message = zrtp_error_messages[level].get(sub_code, 'Unknown') _add_event("RTPTransportZRTPLog", dict(obj=rtp_transport, level=level, message=message)) except: ua._handle_exception(1) cdef void _RTPTransport_cb_zrtp_negotiation_failed(pjmedia_transport *tp, int severity, int sub_code) with gil: global zrtp_message_levels, zrtp_error_messages cdef RTPTransport rtp_transport cdef PJSIPUA ua try: ua = _get_ua() except: return try: rtp_transport = _extract_rtp_transport(tp) if rtp_transport is None: return level = zrtp_message_levels.get(severity, 1) reason = zrtp_error_messages[level].get(sub_code, 'Unknown') _add_event("RTPTransportZRTPNegotiationFailed", dict(obj=rtp_transport, reason=reason)) except: ua._handle_exception(1) cdef void _RTPTransport_cb_zrtp_not_supported_by_other(pjmedia_transport *tp) with gil: cdef RTPTransport rtp_transport cdef PJSIPUA ua try: ua = _get_ua() except: return try: rtp_transport = _extract_rtp_transport(tp) if rtp_transport is None: return _add_event("RTPTransportZRTPNotSupportedByRemote", dict(obj=rtp_transport)) except: ua._handle_exception(1) cdef void _RTPTransport_cb_zrtp_ask_enrollment(pjmedia_transport *tp, int info) with gil: cdef RTPTransport rtp_transport cdef PJSIPUA ua try: ua = _get_ua() except: return try: rtp_transport = _extract_rtp_transport(tp) if rtp_transport is None: return # TODO: implement PBX enrollment except: ua._handle_exception(1) cdef void _RTPTransport_cb_zrtp_inform_enrollment(pjmedia_transport *tp, int info) with gil: cdef RTPTransport rtp_transport cdef PJSIPUA ua try: ua = _get_ua() except: return try: rtp_transport = _extract_rtp_transport(tp) if rtp_transport is None: return # TODO: implement PBX enrollment except: ua._handle_exception(1) cdef void _AudioTransport_cb_dtmf(pjmedia_stream *stream, void *user_data, int digit) with gil: cdef AudioTransport audio_stream = ( user_data)() cdef PJSIPUA ua try: ua = _get_ua() except: return if audio_stream is None: return try: _add_event("RTPAudioStreamGotDTMF", dict(obj=audio_stream, digit=chr(digit))) except: ua._handle_exception(1) # globals cdef pjmedia_ice_cb _ice_cb _ice_cb.on_ice_complete = _RTPTransport_cb_ice_complete _ice_cb.on_ice_state = _RTPTransport_cb_ice_state _ice_cb.on_ice_stop = _RTPTransport_cb_ice_stop valid_sdp_directions = ("sendrecv", "sendonly", "recvonly", "inactive") # ZRTP cdef pjmedia_zrtp_cb _zrtp_cb _zrtp_cb.secure_on = _RTPTransport_cb_zrtp_secure_on _zrtp_cb.secure_off = _RTPTransport_cb_zrtp_secure_off _zrtp_cb.show_sas = _RTPTransport_cb_zrtp_show_sas _zrtp_cb.confirm_go_clear = _RTPTransport_cb_zrtp_confirm_goclear _zrtp_cb.show_message = _RTPTransport_cb_zrtp_show_message _zrtp_cb.negotiation_failed = _RTPTransport_cb_zrtp_negotiation_failed _zrtp_cb.not_supported_by_other = _RTPTransport_cb_zrtp_not_supported_by_other _zrtp_cb.ask_enrollment = _RTPTransport_cb_zrtp_ask_enrollment _zrtp_cb.inform_enrollment = _RTPTransport_cb_zrtp_inform_enrollment _zrtp_cb.sign_sas = NULL _zrtp_cb.check_sas_signature = NULL # Keep these aligned with ZrtpCodes.h cdef dict zrtp_message_levels = {1: 'INFO', 2: 'WARNING', 3: 'SEVERE', 4: 'ERROR'} cdef dict zrtp_error_messages = { 'INFO': { 0: "Unknown", 1: "Hello received and prepared a Commit, ready to get peer's hello hash", #InfoHelloReceived 2: "Commit: Generated a public DH key", #InfoCommitDHGenerated 3: "Responder: Commit received, preparing DHPart1", #InfoRespCommitReceived 4: "DH1Part: Generated a public DH key", #InfoDH1DHGenerated 5: "Initiator: DHPart1 received, preparing DHPart2", #InfoInitDH1Received 6: "Responder: DHPart2 received, preparing Confirm1", #InfoRespDH2Received 7: "Initiator: Confirm1 received, preparing Confirm2", #InfoInitConf1Received 8: "Responder: Confirm2 received, preparing Conf2Ack", #InfoRespConf2Received 9: "At least one retained secrets matches - security OK", #InfoRSMatchFound 10: "Entered secure state", #InfoSecureStateOn 11: "No more security for this session", #InfoSecureStateOff }, 'WARNING': { 0: "Unknown", 1: "WarningDHAESmismatch = 1, //!< Commit contains an AES256 cipher but does not offer a Diffie-Helman 4096 - not used DH4096 was discarded", #WarningDHAESmismatch 2: "Received a GoClear message", #WarningGoClearReceived 3: "Hello offers an AES256 cipher but does not offer a Diffie-Helman 4096- not used DH4096 was discarded", #WarningDHShort 4: "No retained shared secrets available - must verify SAS", #WarningNoRSMatch 5: "Internal ZRTP packet checksum mismatch - packet dropped", #WarningCRCmismatch 6: "Dropping packet because SRTP authentication failed!", #WarningSRTPauthError 7: "Dropping packet because SRTP replay check failed!", #WarningSRTPreplayError 8: "Valid retained shared secrets availabe but no matches found - must verify SAS", #WarningNoExpectedRSMatch }, 'SEVERE': { 0: "Unknown", 1: "Hash HMAC check of Hello failed!", #SevereHelloHMACFailed 2: "Hash HMAC check of Commit failed!", #SevereCommitHMACFailed 3: "Hash HMAC check of DHPart1 failed!", #SevereDH1HMACFailed 4: "Hash HMAC check of DHPart2 failed!", #SevereDH2HMACFailed 5: "Cannot send data - connection or peer down?", #SevereCannotSend 6: "Internal protocol error occured!", #SevereProtocolError 7: "Cannot start a timer - internal resources exhausted?", #SevereNoTimer 8: "Too much retries during ZRTP negotiation - connection or peer down?", #SevereTooMuchRetries }, 'ERROR': { 0x00: "Unknown", 0x10: "Malformed packet (CRC OK, but wrong structure)", #MalformedPacket 0x20: "Critical software error", #CriticalSWError 0x30: "Unsupported ZRTP version", #UnsuppZRTPVersion 0x40: "Hello components mismatch", #HelloCompMismatch 0x51: "Hash type not supported", #UnsuppHashType 0x52: "Cipher type not supported", #UnsuppCiphertype 0x53: "Public key exchange not supported", #UnsuppPKExchange 0x54: "SRTP auth. tag not supported", #UnsuppSRTPAuthTag 0x55: "SAS scheme not supported", #UnsuppSASScheme 0x56: "No shared secret available, DH mode required", #NoSharedSecret 0x61: "DH Error: bad pvi or pvr ( == 1, 0, or p-1)", #DHErrorWrongPV 0x62: "DH Error: hvi != hashed data", #DHErrorWrongHVI 0x63: "Received relayed SAS from untrusted MiTM", #SASuntrustedMiTM 0x70: "Auth. Error: Bad Confirm pkt HMAC", #ConfirmHMACWrong 0x80: "Nonce reuse", #NonceReused 0x90: "Equal ZIDs in Hello", #EqualZIDHello 0x100: "GoClear packet received, but not allowed", #GoCleatNotAllowed 0x7fffffff: "Packet ignored", #IgnorePacket } } ================================================ FILE: sipsimple/core/_core.pxd ================================================ # cython: language_level=2 cdef extern from *: ctypedef char *char_ptr_const "const char *" enum: PJ_SVN_REV "PJ_SVN_REVISION" # system imports from libc.stdlib cimport malloc, free from libc.string cimport memcpy # Python C imports from cpython.float cimport PyFloat_AsDouble from cpython.ref cimport Py_INCREF, Py_DECREF from cpython.string cimport PyString_FromString, PyString_FromStringAndSize, PyString_AsString, PyString_Size cdef extern from "Python.h": object PyUnicode_FromString(const char *u) # PJSIP imports cdef extern from "pjlib.h": # constants enum: PJ_ERR_MSG_SIZE enum: PJ_ERRNO_START_SYS PJ_EBUG PJ_ETOOMANY enum: PJ_MAX_OBJ_NAME # init / shutdown int pj_init() nogil void pj_shutdown() nogil # version char *pj_get_version() nogil # string struct pj_str_t: char *ptr int slen ctypedef pj_str_t *pj_str_ptr_const "const pj_str_t *" # errors pj_str_t pj_strerror(int statcode, char *buf, int bufsize) nogil # logging enum: PJ_LOG_MAX_LEVEL enum pj_log_decoration: PJ_LOG_HAS_YEAR PJ_LOG_HAS_MONTH PJ_LOG_HAS_DAY_OF_MON PJ_LOG_HAS_TIME PJ_LOG_HAS_MICRO_SEC PJ_LOG_HAS_SENDER PJ_LOG_HAS_INDENT void pj_log_set_decor(int decor) nogil int pj_log_get_level() nogil void pj_log_set_level(int level) nogil void pj_log_set_log_func(void func(int level, char_ptr_const data, int len)) nogil # memory management struct pj_pool_t struct pj_pool_factory_policy: pass pj_pool_factory_policy pj_pool_factory_default_policy struct pj_pool_factory: pass struct pj_caching_pool: pj_pool_factory factory void pj_caching_pool_init(pj_caching_pool *ch_pool, pj_pool_factory_policy *policy, int max_capacity) nogil void pj_caching_pool_destroy(pj_caching_pool *ch_pool) nogil void *pj_pool_alloc(pj_pool_t *pool, int size) nogil void pj_pool_reset(pj_pool_t *pool) nogil pj_pool_t *pj_pool_create_on_buf(char *name, void *buf, int size) nogil pj_str_t *pj_strdup2_with_null(pj_pool_t *pool, pj_str_t *dst, char *src) nogil void pj_pool_release(pj_pool_t *pool) nogil # threads enum: PJ_THREAD_DESC_SIZE struct pj_mutex_t struct pj_rwmutex_t struct pj_thread_t int pj_mutex_create_simple(pj_pool_t *pool, char *name, pj_mutex_t **mutex) nogil int pj_mutex_create_recursive(pj_pool_t *pool, char *name, pj_mutex_t **mutex) nogil int pj_mutex_lock(pj_mutex_t *mutex) nogil int pj_mutex_unlock(pj_mutex_t *mutex) nogil int pj_mutex_destroy(pj_mutex_t *mutex) nogil int pj_rwmutex_create(pj_pool_t *pool, char *name, pj_rwmutex_t **mutex) nogil int pj_rwmutex_lock_read(pj_rwmutex_t *mutex) nogil int pj_rwmutex_lock_write(pj_rwmutex_t *mutex) nogil int pj_rwmutex_unlock_read(pj_rwmutex_t *mutex) nogil int pj_rwmutex_unlock_write(pj_rwmutex_t *mutex) nogil int pj_rwmutex_destroy(pj_rwmutex_t *mutex) nogil int pj_thread_is_registered() nogil int pj_thread_register(char *thread_name, long *thread_desc, pj_thread_t **thread) nogil # sockets enum: PJ_INET6_ADDRSTRLEN struct pj_ioqueue_t struct pj_addr_hdr: unsigned int sa_family struct pj_sockaddr_in: pass struct pj_sockaddr_in6: pass ctypedef union pj_sockaddr: pj_addr_hdr addr pj_sockaddr_in ipv4 pj_sockaddr_in6 ipv6 ctypedef pj_sockaddr *pj_sockaddr_ptr_const "const pj_sockaddr *" int pj_AF_INET() nogil int pj_AF_INET6() nogil int pj_sockaddr_in_init(pj_sockaddr_in *addr, pj_str_t *cp, int port) nogil int pj_sockaddr_get_port(pj_sockaddr *addr) nogil char *pj_sockaddr_print(pj_sockaddr *addr, char *buf, int size, unsigned int flags) nogil int pj_sockaddr_has_addr(pj_sockaddr *addr) nogil int pj_sockaddr_init(int af, pj_sockaddr *addr, pj_str_t *cp, unsigned int port) nogil int pj_inet_pton(int af, pj_str_t *src, void *dst) nogil # dns struct pj_dns_resolver int pj_dns_resolver_set_ns(pj_dns_resolver *resolver, unsigned count, pj_str_t *servers, int *ports) nogil # time struct pj_time_val: long sec long msec void pj_gettimeofday(pj_time_val *tv) nogil void pj_time_val_normalize(pj_time_val *tv) nogil # timers struct pj_timer_heap_t struct pj_timer_entry: void *user_data int id pj_timer_entry *pj_timer_entry_init(pj_timer_entry *entry, int id, void *user_data, void cb(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) with gil) nogil # lists struct pj_list: void *prev void *next void pj_list_init(pj_list *node) nogil void pj_list_insert_after(pj_list *pos, pj_list *node) nogil # random void pj_srand(unsigned int seed) nogil # maths struct pj_math_stat: int n int max int min int last int mean cdef extern from "pjlib-util.h": # init int pjlib_util_init() nogil cdef extern from "pjnath.h": # init int pjnath_init() nogil # STUN enum: PJ_STUN_PORT struct pj_stun_config: pass struct pj_stun_sock_cfg: pj_sockaddr bound_addr void pj_stun_config_init(pj_stun_config *cfg, pj_pool_factory *factory, unsigned int options, pj_ioqueue_t *ioqueue, pj_timer_heap_t *timer_heap) nogil # NAT detection struct pj_stun_nat_detect_result: int status char *status_text char *nat_type_name ctypedef pj_stun_nat_detect_result *pj_stun_nat_detect_result_ptr_const "const pj_stun_nat_detect_result *" int pj_stun_detect_nat_type(pj_sockaddr_in *server, pj_stun_config *stun_cfg, void *user_data, void pj_stun_nat_detect_cb(void *user_data, pj_stun_nat_detect_result_ptr_const res) with gil) nogil # ICE struct pj_ice_strans struct pj_ice_sess_cand: int type int comp_id int prio pj_sockaddr addr pj_sockaddr rel_addr struct pj_ice_sess_check: pj_ice_sess_cand *lcand pj_ice_sess_cand *rcand int state int nominated ctypedef pj_ice_sess_check *pj_ice_sess_check_ptr_const "const pj_ice_sess_check *" struct pj_ice_sess_comp: pj_ice_sess_check *valid_check struct pj_ice_sess_checklist: int count pj_ice_sess_check *checks struct pj_ice_sess: int comp_cnt pj_ice_sess_comp *comp int lcand_cnt pj_ice_sess_cand *lcand int rcand_cnt pj_ice_sess_cand *rcand pj_ice_sess_checklist valid_list struct pj_ice_strans_cfg_stun: pj_stun_sock_cfg cfg pj_str_t server unsigned int port struct pj_ice_strans_cfg: int af pj_stun_config stun_cfg pj_ice_strans_cfg_stun stun enum pj_ice_strans_op: PJ_ICE_STRANS_OP_INIT PJ_ICE_STRANS_OP_NEGOTIATION enum pj_ice_strans_state: PJ_ICE_STRANS_STATE_NULL PJ_ICE_STRANS_STATE_INIT PJ_ICE_STRANS_STATE_READY PJ_ICE_STRANS_STATE_SESS_READY PJ_ICE_STRANS_STATE_NEGO PJ_ICE_STRANS_STATE_RUNNING PJ_ICE_STRANS_STATE_FAILED enum pj_ice_cand_type: PJ_ICE_CAND_TYPE_HOST PJ_ICE_CAND_TYPE_SRFLX PJ_ICE_CAND_TYPE_PRFLX PJ_ICE_CAND_TYPE_RELAYED enum pj_ice_sess_check_state: PJ_ICE_SESS_CHECK_STATE_FROZEN PJ_ICE_SESS_CHECK_STATE_WAITING PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS PJ_ICE_SESS_CHECK_STATE_SUCCEEDED PJ_ICE_SESS_CHECK_STATE_FAILED struct pjmedia_ice_transport_info: int active pj_ice_strans_state sess_state void pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg) nogil pj_ice_sess* pj_ice_strans_get_session(pj_ice_strans *ice_st) pj_time_val pj_ice_strans_get_start_time(pj_ice_strans *ice_st) pj_ice_strans_state pj_ice_strans_get_state(pj_ice_strans *ice_st) pj_ice_sess_check_ptr_const pj_ice_strans_get_valid_pair(pj_ice_strans *ice_st, unsigned comp_id) cdef extern from "pjmedia.h": enum: PJMEDIA_ENOSNDREC PJMEDIA_ENOSNDPLAY enum: PJMEDIA_AUD_DEFAULT_CAPTURE_DEV PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV PJMEDIA_AUD_DEV_CAP_EC PJMEDIA_AUD_DEV_CAP_EC_TAIL enum: PJMEDIA_VID_DEFAULT_CAPTURE_DEV PJMEDIA_VID_DEFAULT_RENDER_DEV PJMEDIA_VID_INVALID_DEV enum pjmedia_dir: PJMEDIA_DIR_NONE PJMEDIA_DIR_ENCODING PJMEDIA_DIR_DECODING PJMEDIA_DIR_ENCODING_DECODING PJMEDIA_DIR_PLAYBACK = PJMEDIA_DIR_DECODING PJMEDIA_DIR_RENDER = PJMEDIA_DIR_DECODING PJMEDIA_DIR_CAPTURE = PJMEDIA_DIR_ENCODING PJMEDIA_DIR_CAPTURE_PLAYBACK = PJMEDIA_DIR_ENCODING_DECODING PJMEDIA_DIR_CAPTURE_RENDER = PJMEDIA_DIR_ENCODING_DECODING enum pjmedia_type: PJMEDIA_TYPE_NONE PJMEDIA_TYPE_NONEYPE_AUDIO PJMEDIA_TYPE_VIDEO PJMEDIA_TYPE_APPLICATION PJMEDIA_TYPE_UNKNOWN struct pjmedia_rect_size: unsigned int w unsigned int h struct pjmedia_ratio: int num int denum # frame struct pjmedia_frame: void *buf int size ctypedef pjmedia_frame *pjmedia_frame_ptr_const "const pjmedia_frame *" # codec manager struct pjmedia_codec_mgr enum: PJMEDIA_CODEC_MGR_MAX_CODECS struct pjmedia_codec_info: pj_str_t encoding_name unsigned int clock_rate unsigned int channel_cnt int pjmedia_codec_mgr_enum_codecs(pjmedia_codec_mgr *mgr, unsigned int *count, pjmedia_codec_info *info, unsigned int *prio) nogil int pjmedia_codec_mgr_set_codec_priority(pjmedia_codec_mgr *mgr, pj_str_t *codec_id, unsigned int prio) nogil # transport manager struct pjsip_tpmgr struct pjsip_transport_state_info: int status enum pjsip_transport_state: PJSIP_TP_STATE_CONNECTED PJSIP_TP_STATE_DISCONNECTED ctypedef pjsip_transport_state_info *pjsip_transport_state_info_ptr_const "const pjsip_transport_state_info *" ctypedef void (*pjsip_tp_state_callback)(pjsip_transport *tp, pjsip_transport_state state, pjsip_transport_state_info_ptr_const info) with gil int pjsip_tpmgr_set_state_cb(pjsip_tpmgr *mgr, pjsip_tp_state_callback cb) # formats enum pjmedia_format_detail_type: PJMEDIA_FORMAT_DETAIL_NONE PJMEDIA_FORMAT_DETAIL_AUDIO PJMEDIA_FORMAT_DETAIL_VIDEO PJMEDIA_FORMAT_DETAIL_MAX struct pjmedia_audio_format_detail: unsigned int clock_rate unsigned int channel_count unsigned int frame_time_usec unsigned int baseits_per_sample unsigned int avg_bps unsigned int max_bps struct pjmedia_video_format_detail: pjmedia_rect_size size pjmedia_ratio fps unsigned int avg_bps unsigned int max_bps cdef union pjmedia_format_union: pjmedia_audio_format_detail aud pjmedia_video_format_detail vid char user[1] struct pjmedia_format: unsigned int id pjmedia_type type pjmedia_format_detail_type detail_type pjmedia_format_union det ctypedef pjmedia_format *pjmedia_format_ptr_const "const pjmedia_format *" enum: PJMEDIA_CODEC_MAX_FMTP_CNT struct _param: pj_str_t name pj_str_t val struct pjmedia_codec_fmtp: int cnt _param param[PJMEDIA_CODEC_MAX_FMTP_CNT] # video codec parameters enum pjmedia_vid_packing: PJMEDIA_VID_PACKING_PACKETS struct pjmedia_vid_codec_param: pjmedia_dir dir pjmedia_vid_packing packing pjmedia_format enc_fmt pjmedia_codec_fmtp enc_fmtp pjmedia_format dec_fmt pjmedia_codec_fmtp dec_fmtp ctypedef pjmedia_vid_codec_param *pjmedia_vid_codec_param_ptr_const "const pjmedia_vid_codec_param *" # video codec manager enum: PJMEDIA_VID_CODEC_MGR_MAX_CODECS struct pjmedia_vid_codec_info: pj_str_t encoding_name unsigned int pt unsigned int clock_rate unsigned int packings ctypedef pjmedia_vid_codec_info *pjmedia_vid_codec_info_ptr_const "const pjmedia_vid_codec_info *" struct pjmedia_vid_codec_mgr int pjmedia_vid_codec_mgr_create(pj_pool_t *pool, pjmedia_vid_codec_mgr **mgr) nogil void pjmedia_vid_codec_mgr_destroy(pjmedia_vid_codec_mgr *mgr) nogil pjmedia_vid_codec_mgr* pjmedia_vid_codec_mgr_instance() nogil int pjmedia_vid_codec_mgr_enum_codecs(pjmedia_vid_codec_mgr *mgr, unsigned int *count, pjmedia_vid_codec_info *info, unsigned int *prio) nogil int pjmedia_vid_codec_mgr_set_codec_priority(pjmedia_vid_codec_mgr *mgr, pj_str_t *codec_id, unsigned int prio) nogil int pjmedia_vid_codec_mgr_get_default_param(pjmedia_vid_codec_mgr *mgr, pjmedia_vid_codec_info_ptr_const info, pjmedia_vid_codec_param *param) nogil int pjmedia_vid_codec_mgr_set_default_param(pjmedia_vid_codec_mgr *mgr, pjmedia_vid_codec_info_ptr_const info, pjmedia_vid_codec_param_ptr_const param) nogil # video format manager struct pjmedia_video_format_mgr int pjmedia_video_format_mgr_create(pj_pool_t *pool, unsigned int max_fmt, unsigned int options, pjmedia_video_format_mgr **p_mgr) nogil void pjmedia_video_format_mgr_destroy(pjmedia_video_format_mgr *mgr) nogil pjmedia_video_format_mgr* pjmedia_video_format_mgr_instance() nogil # converter manager struct pjmedia_converter_mgr int pjmedia_converter_mgr_create(pj_pool_t *pool, pjmedia_converter_mgr **mgr) nogil void pjmedia_converter_mgr_destroy(pjmedia_converter_mgr *mgr) nogil pjmedia_converter_mgr* pjmedia_converter_mgr_instance() nogil # event manager struct pjmedia_event_mgr enum pjmedia_event_type: PJMEDIA_EVENT_FMT_CHANGED PJMEDIA_EVENT_KEYFRAME_FOUND PJMEDIA_EVENT_KEYFRAME_MISSING PJMEDIA_EVENT_KEYFRAME_REQUESTED struct pjmedia_event_fmt_changed_data: pjmedia_dir dir pjmedia_format new_fmt cdef union pjmedia_event_data_union: pjmedia_event_fmt_changed_data fmt_changed void* ptr struct pjmedia_event: pjmedia_event_type type pjmedia_event_data_union data int pjmedia_event_mgr_create(pj_pool_t *pool, unsigned int options, pjmedia_event_mgr **mgr) nogil void pjmedia_event_mgr_destroy(pjmedia_event_mgr *mgr) nogil pjmedia_event_mgr* pjmedia_event_mgr_instance() nogil ctypedef int pjmedia_event_cb(pjmedia_event *event, void *user_data) int pjmedia_event_subscribe(pjmedia_event_mgr *mgr, pjmedia_event_cb *cb, void *user_data, void *epub) nogil int pjmedia_event_unsubscribe(pjmedia_event_mgr *mgr, pjmedia_event_cb *cb, void *user_data, void *epub) nogil # endpoint struct pjmedia_endpt int pjmedia_endpt_create(pj_pool_factory *pf, pj_ioqueue_t *ioqueue, int worker_cnt, pjmedia_endpt **p_endpt) nogil pj_pool_t *pjmedia_endpt_create_pool(pjmedia_endpt *endpt, char *pool_name, int initial, int increment) nogil int pjmedia_endpt_destroy(pjmedia_endpt *endpt) nogil pj_ioqueue_t *pjmedia_endpt_get_ioqueue(pjmedia_endpt *endpt) nogil pjmedia_codec_mgr *pjmedia_endpt_get_codec_mgr(pjmedia_endpt *endpt) nogil pjsip_tpmgr* pjsip_endpt_get_tpmgr(pjsip_endpoint *endpt) # sound devices struct pjmedia_aud_dev_info: char *name int input_count int output_count struct pjmedia_aud_param: pjmedia_dir dir int play_id int rec_id int clock_rate int channel_count int samples_per_frame int bits_per_sample int flags int ec_enabled int ec_tail_ms struct pjmedia_aud_stream int pjmedia_aud_dev_count() nogil int pjmedia_aud_dev_get_info(int index, pjmedia_aud_dev_info *info) nogil int pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm, pjmedia_aud_param *param) nogil enum pjmedia_aud_dev_event: PJMEDIA_AUD_DEV_DEFAULT_INPUT_CHANGED PJMEDIA_AUD_DEV_DEFAULT_OUTPUT_CHANGED PJMEDIA_AUD_DEV_LIST_WILL_REFRESH PJMEDIA_AUD_DEV_LIST_DID_REFRESH ctypedef void (*pjmedia_aud_dev_observer_callback)(pjmedia_aud_dev_event event) int pjmedia_aud_dev_set_observer_cb(pjmedia_aud_dev_observer_callback cb) nogil int pjmedia_aud_dev_default_param(int index, pjmedia_aud_param *param) nogil int pjmedia_aud_dev_refresh() nogil # sound port struct pjmedia_port_info: pjmedia_format fmt struct pjmedia_port: pjmedia_port_info info struct pjmedia_snd_port struct pjmedia_snd_port_param: pjmedia_aud_param base ctypedef pjmedia_snd_port_param *pjmedia_snd_port_param_ptr_const "const pjmedia_snd_port_param *" int pjmedia_snd_port_create2(pj_pool_t *pool, pjmedia_snd_port_param_ptr_const prm, pjmedia_snd_port **p_port) nogil void pjmedia_snd_port_param_default(pjmedia_snd_port_param *prm) int pjmedia_snd_port_connect(pjmedia_snd_port *snd_port, pjmedia_port *port) nogil int pjmedia_snd_port_disconnect(pjmedia_snd_port *snd_port) nogil int pjmedia_snd_port_set_ec(pjmedia_snd_port *snd_port, pj_pool_t *pool, unsigned int tail_ms, int options) nogil int pjmedia_snd_port_reset_ec_state(pjmedia_snd_port *snd_port) nogil int pjmedia_snd_port_destroy(pjmedia_snd_port *snd_port) nogil pjmedia_aud_stream *pjmedia_snd_port_get_snd_stream(pjmedia_snd_port *snd_port) nogil int pjmedia_null_port_create(pj_pool_t *pool, unsigned int sampling_rate, unsigned int channel_count, unsigned int samples_per_frame, unsigned int bits_per_sample, pjmedia_port **p_port) nogil int pjmedia_mixer_port_create(pj_pool_t *pool, unsigned int sampling_rate, unsigned int channel_count, unsigned int samples_per_frame, unsigned int bits_per_sample, pjmedia_port **p_port) nogil # master port struct pjmedia_master_port int pjmedia_master_port_create(pj_pool_t *pool, pjmedia_port *u_port, pjmedia_port *d_port, unsigned int options, pjmedia_master_port **p_m) nogil int pjmedia_master_port_start(pjmedia_master_port *m) nogil int pjmedia_master_port_destroy(pjmedia_master_port *m, int destroy_ports) nogil # conference bridge enum pjmedia_conf_option: PJMEDIA_CONF_NO_DEVICE struct pjmedia_conf int pjmedia_conf_create(pj_pool_t *pool, int max_slots, int sampling_rate, int channel_count, int samples_per_frame, int bits_per_sample, int options, pjmedia_conf **p_conf) nogil int pjmedia_conf_destroy(pjmedia_conf *conf) nogil pjmedia_port *pjmedia_conf_get_master_port(pjmedia_conf *conf) nogil int pjmedia_conf_add_port(pjmedia_conf *conf, pj_pool_t *pool, pjmedia_port *strm_port, pj_str_t *name, unsigned int *p_slot) nogil int pjmedia_conf_remove_port(pjmedia_conf *conf, unsigned int slot) nogil int pjmedia_conf_connect_port(pjmedia_conf *conf, unsigned int src_slot, unsigned int sink_slot, int level) nogil int pjmedia_conf_disconnect_port(pjmedia_conf *conf, unsigned int src_slot, unsigned int sink_slot) nogil int pjmedia_conf_adjust_rx_level(pjmedia_conf *conf, unsigned slot, int adj_level) nogil int pjmedia_conf_adjust_tx_level(pjmedia_conf *conf, unsigned slot, int adj_level) nogil # video devices enum pjmedia_vid_dev_cap: PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS enum pjmedia_vid_dev_wnd_flag: PJMEDIA_VID_DEV_WND_BORDER PJMEDIA_VID_DEV_WND_RESIZABLE struct pjmedia_vid_dev_info: int id char *name char *driver int dir cdef struct pjmedia_hwnd_win: void *hwnd cdef struct pjmedia_hwnd_x11: void *window void *display cdef struct pjmedia_hwnd_cocoa: void *window cdef struct pjmedia_hwnd_ios: void *window cdef union pjmedia_hwnd_union: pjmedia_hwnd_win win pjmedia_hwnd_x11 x11 pjmedia_hwnd_cocoa cocoa pjmedia_hwnd_ios ios void *window struct pjmedia_vid_dev_hwnd: pjmedia_hwnd_union info struct pjmedia_vid_dev_param: pjmedia_dir dir int cap_id int rend_id unsigned int flags pjmedia_format fmt pjmedia_vid_dev_hwnd window pjmedia_rect_size disp_size int window_hide unsigned int window_flags struct pjmedia_vid_dev_stream int pjmedia_vid_dev_count() nogil int pjmedia_vid_dev_get_info(int index, pjmedia_vid_dev_info *info) nogil int pjmedia_vid_dev_subsys_init(pj_pool_factory *pf) nogil int pjmedia_vid_dev_subsys_shutdown() nogil int pjmedia_vid_dev_default_param(pj_pool_t *pool, int id, pjmedia_vid_dev_param *param) nogil int pjmedia_vid_dev_stream_get_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, void *value) nogil int pjmedia_vid_dev_stream_set_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, void *value) nogil int pjmedia_vid_dev_stream_get_param(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_param *param) nogil int pjmedia_vid_dev_refresh() nogil int pjmedia_vid_dev_lookup(char_ptr_const drv_name, char_ptr_const dev_name, int *id) # video stream struct pjmedia_vid_stream_info: pjmedia_vid_codec_param *codec_param pjmedia_vid_codec_info codec_info int use_ka struct pjmedia_vid_stream ctypedef pjmedia_vid_stream *pjmedia_vid_stream_ptr_const "const pjmedia_vid_stream *" int pjmedia_vid_stream_get_stat(pjmedia_vid_stream_ptr_const stream, pjmedia_rtcp_stat *stat) nogil int pjmedia_vid_stream_get_info(pjmedia_vid_stream_ptr_const stream, pjmedia_vid_stream_info *info) nogil int pjmedia_vid_stream_info_from_sdp(pjmedia_vid_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt, pjmedia_sdp_session *local, pjmedia_sdp_session *remote, unsigned int stream_idx) nogil int pjmedia_vid_stream_create(pjmedia_endpt *endpt, pj_pool_t *pool, pjmedia_vid_stream_info *info, pjmedia_transport *tp, void *user_data, pjmedia_vid_stream **p_stream) nogil int pjmedia_vid_stream_start(pjmedia_vid_stream *stream) nogil int pjmedia_vid_stream_destroy(pjmedia_vid_stream *stream) nogil int pjmedia_vid_stream_get_port(pjmedia_vid_stream *stream, pjmedia_dir dir, pjmedia_port **p_port) nogil int pjmedia_vid_stream_pause(pjmedia_vid_stream *stream, pjmedia_dir dir) nogil int pjmedia_vid_stream_resume(pjmedia_vid_stream *stream, pjmedia_dir dir) nogil int pjmedia_vid_stream_send_keyframe(pjmedia_vid_stream *stream) nogil int pjmedia_vid_stream_send_rtcp_sdes(pjmedia_vid_stream *stream) nogil int pjmedia_vid_stream_send_rtcp_bye(pjmedia_vid_stream *stream) nogil int pjmedia_vid_stream_send_rtcp_pli(pjmedia_vid_stream *stream) nogil # video port struct pjmedia_vid_port struct pjmedia_vid_port_param: pjmedia_vid_dev_param vidparam int active void pjmedia_vid_port_param_default(pjmedia_vid_port_param *prm) nogil ctypedef pjmedia_vid_port_param *pjmedia_vid_port_param_ptr_const "const pjmedia_vid_port_param *" int pjmedia_vid_port_create(pj_pool_t *pool, pjmedia_vid_port_param_ptr_const prm, pjmedia_vid_port **p_vp) nogil int pjmedia_vid_port_start(pjmedia_vid_port *vid_port) nogil int pjmedia_vid_port_is_running(pjmedia_vid_port *vid_port) nogil int pjmedia_vid_port_stop(pjmedia_vid_port *vid_port) nogil int pjmedia_vid_port_connect(pjmedia_vid_port *vid_port, pjmedia_port *port, int destroy) nogil pjmedia_port *pjmedia_vid_port_get_passive_port(pjmedia_vid_port *vid_port) nogil int pjmedia_vid_port_disconnect(pjmedia_vid_port *vid_port) nogil void pjmedia_vid_port_destroy(pjmedia_vid_port *vid_port) nogil pjmedia_vid_dev_stream *pjmedia_vid_port_get_stream(pjmedia_vid_port *vid_port) nogil # video tee int pjmedia_vid_tee_create(pj_pool_t *pool, pjmedia_format_ptr_const fmt, unsigned int max_dst_cnt, pjmedia_port **p_vid_tee) nogil int pjmedia_vid_tee_add_dst_port2(pjmedia_port *vid_tee, unsigned int option, pjmedia_port *port) nogil int pjmedia_vid_tee_remove_dst_port(pjmedia_port *vid_tee, pjmedia_port *port) nogil # sdp enum: PJMEDIA_MAX_SDP_FMT enum: PJMEDIA_MAX_SDP_ATTR enum: PJMEDIA_MAX_SDP_MEDIA enum: PJMEDIA_MAX_SDP_BANDW struct pjmedia_sdp_attr: pj_str_t name pj_str_t value struct pjmedia_sdp_bandw: pj_str_t modifier unsigned int value struct pjmedia_sdp_conn: pj_str_t net_type pj_str_t addr_type pj_str_t addr struct pjmedia_sdp_media_desc: pj_str_t media unsigned int port unsigned int port_count pj_str_t transport unsigned int fmt_count pj_str_t fmt[PJMEDIA_MAX_SDP_FMT] struct pjmedia_sdp_media: pjmedia_sdp_media_desc desc pjmedia_sdp_conn *conn unsigned int attr_count pjmedia_sdp_attr *attr[PJMEDIA_MAX_SDP_ATTR] unsigned int bandw_count pjmedia_sdp_bandw *bandw[PJMEDIA_MAX_SDP_BANDW] struct pjmedia_sdp_session_origin: pj_str_t user unsigned int id unsigned int version pj_str_t net_type pj_str_t addr_type pj_str_t addr struct pjmedia_sdp_session_time: unsigned int start unsigned int stop struct pjmedia_sdp_session: pjmedia_sdp_session_origin origin pj_str_t name pjmedia_sdp_conn *conn pjmedia_sdp_session_time time unsigned int attr_count pjmedia_sdp_attr *attr[PJMEDIA_MAX_SDP_ATTR] unsigned int bandw_count pjmedia_sdp_bandw *bandw[PJMEDIA_MAX_SDP_BANDW] unsigned int media_count pjmedia_sdp_media *media[PJMEDIA_MAX_SDP_MEDIA] ctypedef pjmedia_sdp_session *pjmedia_sdp_session_ptr_const "const pjmedia_sdp_session *" pjmedia_sdp_media *pjmedia_sdp_media_clone(pj_pool_t *pool, pjmedia_sdp_media *rhs) nogil pjmedia_sdp_session *pjmedia_sdp_session_clone(pj_pool_t *pool, pjmedia_sdp_session_ptr_const sdp) nogil int pjmedia_sdp_print(pjmedia_sdp_session_ptr_const sdp, char *buf, int length) int pjmedia_sdp_parse(pj_pool_t *pool, char *buf, int length, pjmedia_sdp_session **p_sdp) nogil # sdp negotiation enum pjmedia_sdp_neg_state: PJMEDIA_SDP_NEG_STATE_NULL PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER PJMEDIA_SDP_NEG_STATE_WAIT_NEGO PJMEDIA_SDP_NEG_STATE_DONE struct pjmedia_sdp_neg int pjmedia_sdp_neg_create_w_local_offer(pj_pool_t *pool, pjmedia_sdp_session_ptr_const local, pjmedia_sdp_neg **neg) nogil int pjmedia_sdp_neg_create_w_remote_offer(pj_pool_t *pool, pjmedia_sdp_session_ptr_const initial, pjmedia_sdp_session_ptr_const remote, pjmedia_sdp_neg **neg) nogil int pjmedia_sdp_neg_set_local_answer(pj_pool_t *pool, pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const local) nogil int pjmedia_sdp_neg_set_remote_answer(pj_pool_t *pool, pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const remote) nogil int pjmedia_sdp_neg_set_remote_offer(pj_pool_t *pool, pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const remote) nogil int pjmedia_sdp_neg_get_neg_remote(pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const *remote) nogil int pjmedia_sdp_neg_get_neg_local(pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const *local) nogil int pjmedia_sdp_neg_get_active_remote(pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const *remote) nogil int pjmedia_sdp_neg_get_active_local(pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const *local) nogil int pjmedia_sdp_neg_modify_local_offer (pj_pool_t *pool, pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const local) nogil int pjmedia_sdp_neg_cancel_offer(pjmedia_sdp_neg *neg) nogil int pjmedia_sdp_neg_negotiate(pj_pool_t *poll, pjmedia_sdp_neg *neg, int allow_asym) nogil pjmedia_sdp_neg_state pjmedia_sdp_neg_get_state(pjmedia_sdp_neg *neg) nogil char *pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_state state) nogil # transport enum pjmedia_transport_type: PJMEDIA_TRANSPORT_TYPE_ICE PJMEDIA_TRANSPORT_TYPE_SRTP PJMEDIA_TRANSPORT_TYPE_ZRTP struct pjmedia_sock_info: pj_sockaddr rtp_addr_name ctypedef pjmedia_sock_info *pjmedia_sock_info_ptr_const "const pjmedia_sock_info *" struct pjmedia_transport: char *name pjmedia_transport_type type void *user_data struct pjmedia_transport_specific_info: pjmedia_transport_type type char *buffer struct pjmedia_transport_info: pjmedia_sock_info sock_info pj_sockaddr src_rtp_name int specific_info_cnt pjmedia_transport_specific_info *spc_info void pjmedia_transport_info_init(pjmedia_transport_info *info) nogil int pjmedia_transport_udp_create3(pjmedia_endpt *endpt, int af, char *name, pj_str_t *addr, int port, unsigned int options, pjmedia_transport **p_tp) nogil int pjmedia_transport_get_info(pjmedia_transport *tp, pjmedia_transport_info *info) nogil void *pjmedia_transport_info_get_spc_info(pjmedia_transport_info *info, pjmedia_transport_type type) int pjmedia_transport_close(pjmedia_transport *tp) nogil int pjmedia_transport_media_create(pjmedia_transport *tp, pj_pool_t *sdp_pool, unsigned int options, pjmedia_sdp_session *rem_sdp, unsigned int media_index) nogil int pjmedia_transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *sdp, pjmedia_sdp_session *rem_sdp, unsigned int media_index) nogil int pjmedia_transport_media_start(pjmedia_transport *tp, pj_pool_t *tmp_pool, pjmedia_sdp_session *sdp_local, pjmedia_sdp_session *sdp_remote, unsigned int media_index) nogil int pjmedia_transport_media_stop(pjmedia_transport *tp) nogil int pjmedia_endpt_create_sdp(pjmedia_endpt *endpt, pj_pool_t *pool, unsigned int stream_cnt, pjmedia_sock_info *sock_info, pjmedia_sdp_session **p_sdp) nogil int pjmedia_endpt_create_base_sdp(pjmedia_endpt *endpt, pj_pool_t *pool, pj_str_ptr_const sess_name, pj_sockaddr_ptr_const origin, pjmedia_sdp_session **p_sdp) nogil int pjmedia_endpt_create_audio_sdp(pjmedia_endpt *endpt, pj_pool_t *pool, pjmedia_sock_info_ptr_const si, unsigned int options, pjmedia_sdp_media **p_media) nogil int pjmedia_endpt_create_video_sdp(pjmedia_endpt *endpt, pj_pool_t *pool, pjmedia_sock_info_ptr_const si, unsigned int options, pjmedia_sdp_media **p_media) nogil # SRTP struct pjmedia_srtp_crypto: pj_str_t key pj_str_t name unsigned int flags struct pjmedia_srtp_info: int active pjmedia_srtp_crypto tx_policy enum pjmedia_srtp_use: PJMEDIA_SRTP_MANDATORY struct pjmedia_srtp_setting: pjmedia_srtp_use use void pjmedia_srtp_setting_default(pjmedia_srtp_setting *opt) nogil int pjmedia_transport_srtp_create(pjmedia_endpt *endpt, pjmedia_transport *tp, pjmedia_srtp_setting *opt, pjmedia_transport **p_tp) nogil # ZRTP struct pjmedia_zrtp_info: int active char cipher[128] struct pjmedia_zrtp_cb: void secure_on(pjmedia_transport *tp, char* cipher) with gil void secure_off(pjmedia_transport *tp) with gil void show_sas(pjmedia_transport *tp, char* sas, int verified) with gil void confirm_go_clear(pjmedia_transport *tp) with gil void show_message(pjmedia_transport *tp, int severity, int subCode) with gil void negotiation_failed(pjmedia_transport *tp, int severity, int subCode) with gil void not_supported_by_other(pjmedia_transport *tp) with gil void ask_enrollment(pjmedia_transport *tp, int info) with gil void inform_enrollment(pjmedia_transport *tp, int info) with gil void sign_sas(pjmedia_transport *tp, unsigned char* sas) with gil int check_sas_signature(pjmedia_transport *tp, unsigned char* sas) with gil int pjmedia_transport_zrtp_create(pjmedia_endpt *endpt, pj_timer_heap_t *timer_heap, pjmedia_transport *tp, pjmedia_transport **p_tp, int close_slave) nogil int pjmedia_transport_zrtp_initialize(pjmedia_transport *tp, char_ptr_const zidFilename, int autoEnable, pjmedia_zrtp_cb *zrtp_cb) nogil void pjmedia_transport_zrtp_setSASVerified(pjmedia_transport *tp, int verified) nogil char* pjmedia_transport_zrtp_getPeerName(pjmedia_transport *tp) nogil void pjmedia_transport_zrtp_putPeerName(pjmedia_transport *tp, const char *name) nogil int pjmedia_transport_zrtp_getPeerZid(pjmedia_transport *tp, unsigned char* data) nogil void pjmedia_transport_zrtp_setEnableZrtp(pjmedia_transport *tp, int onOff) nogil char* pjmedia_transport_zrtp_getMultiStreamParameters(pjmedia_transport *tp, int *length) nogil void pjmedia_transport_zrtp_setMultiStreamParameters(pjmedia_transport *tp, const char *parameters, int length, pjmedia_transport *master_tp) nogil # ICE struct pjmedia_ice_cb: void on_ice_complete(pjmedia_transport *tp, pj_ice_strans_op op, int status) with gil void on_ice_state(pjmedia_transport *tp, pj_ice_strans_state prev, pj_ice_strans_state curr) with gil void on_ice_stop(pjmedia_transport *tp, char *reason, int err) with gil int pjmedia_ice_create2(pjmedia_endpt *endpt, char *name, unsigned int comp_cnt, pj_ice_strans_cfg *cfg, pjmedia_ice_cb *cb, unsigned int options, pjmedia_transport **p_tp) nogil pj_ice_strans *pjmedia_ice_get_strans(pjmedia_transport *tp) with gil # stream struct pjmedia_codec_param_setting: unsigned int vad struct pjmedia_codec_param: pjmedia_codec_param_setting setting struct pjmedia_stream_info: pjmedia_codec_info fmt pjmedia_codec_param *param unsigned int tx_event_pt int use_ka struct pjmedia_rtcp_stream_stat_loss_type: unsigned int burst unsigned int random struct pjmedia_rtcp_stream_stat: unsigned int pkt unsigned int bytes unsigned int discard unsigned int loss unsigned int reorder unsigned int dup pj_math_stat loss_period pjmedia_rtcp_stream_stat_loss_type loss_type pj_math_stat jitter struct pjmedia_rtcp_stat: pjmedia_rtcp_stream_stat tx pjmedia_rtcp_stream_stat rx pj_math_stat rtt struct pjmedia_stream int pjmedia_stream_info_from_sdp(pjmedia_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt, pjmedia_sdp_session *local, pjmedia_sdp_session *remote, unsigned int stream_idx) nogil int pjmedia_stream_create(pjmedia_endpt *endpt, pj_pool_t *pool, pjmedia_stream_info *info, pjmedia_transport *tp, void *user_data, pjmedia_stream **p_stream) nogil int pjmedia_stream_destroy(pjmedia_stream *stream) nogil int pjmedia_stream_get_port(pjmedia_stream *stream, pjmedia_port **p_port) nogil int pjmedia_stream_start(pjmedia_stream *stream) nogil int pjmedia_stream_dial_dtmf(pjmedia_stream *stream, pj_str_t *ascii_digit) nogil int pjmedia_stream_set_dtmf_callback(pjmedia_stream *stream, void cb(pjmedia_stream *stream, void *user_data, int digit) with gil, void *user_data) nogil int pjmedia_stream_pause(pjmedia_stream *stream, pjmedia_dir dir) nogil int pjmedia_stream_resume(pjmedia_stream *stream, pjmedia_dir dir) nogil int pjmedia_stream_get_stat(pjmedia_stream *stream, pjmedia_rtcp_stat *stat) nogil # wav player enum: PJMEDIA_FILE_NO_LOOP int pjmedia_port_destroy(pjmedia_port *port) nogil int pjmedia_wav_player_port_create(pj_pool_t *pool, char *filename, unsigned int ptime, unsigned int flags, unsigned int buff_size, pjmedia_port **p_port) nogil int pjmedia_wav_player_set_eof_cb(pjmedia_port *port, void *user_data, int cb(pjmedia_port *port, void *usr_data) with gil) nogil int pjmedia_wav_player_port_set_pos(pjmedia_port *port, unsigned int offset) nogil # wav recorder enum pjmedia_file_writer_option: PJMEDIA_FILE_WRITE_PCM int pjmedia_wav_writer_port_create(pj_pool_t *pool, char *filename, unsigned int clock_rate, unsigned int channel_count, unsigned int samples_per_frame, unsigned int bits_per_sample, unsigned int flags, int buff_size, pjmedia_port **p_port) nogil # tone generator enum: PJMEDIA_TONEGEN_MAX_DIGITS struct pjmedia_tone_desc: short freq1 short freq2 short on_msec short off_msec short volume short flags struct pjmedia_tone_digit: char digit short on_msec short off_msec short volume int pjmedia_tonegen_create(pj_pool_t *pool, unsigned int clock_rate, unsigned int channel_count, unsigned int samples_per_frame, unsigned int bits_per_sample, unsigned int options, pjmedia_port **p_port) nogil int pjmedia_tonegen_play(pjmedia_port *tonegen, unsigned int count, pjmedia_tone_desc *tones, unsigned int options) nogil int pjmedia_tonegen_play_digits(pjmedia_port *tonegen, unsigned int count, pjmedia_tone_digit *digits, unsigned int options) nogil int pjmedia_tonegen_stop(pjmedia_port *tonegen) nogil int pjmedia_tonegen_is_busy(pjmedia_port *tonegen) nogil cdef extern from "pjmedia_videodev.h": ctypedef void (*pjmedia_vid_dev_fb_frame_cb)(pjmedia_frame_ptr_const frame, pjmedia_rect_size size, void *user_data) int pjmedia_vid_dev_fb_set_callback(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_fb_frame_cb cb, void *user_data) cdef extern from "pjmedia-codec.h": # codecs enum: PJMEDIA_SPEEX_NO_NB struct speex_options: unsigned int option int quality int complexity struct ilbc_options: unsigned mode struct pjmedia_audio_codec_config: speex_options speex ilbc_options ilbc void pjmedia_audio_codec_config_default(pjmedia_audio_codec_config *cfg) int pjmedia_codec_register_audio_codecs(pjmedia_endpt *endpt, const pjmedia_audio_codec_config *c) nogil int pjmedia_codec_ffmpeg_vid_init(pjmedia_vid_codec_mgr *mgr, pj_pool_factory *pf) nogil int pjmedia_codec_ffmpeg_vid_deinit() nogil int pjmedia_codec_vpx_init(pjmedia_vid_codec_mgr *mgr, pj_pool_factory *pf) nogil int pjmedia_codec_vpx_deinit() nogil cdef extern from "pjsip.h": # messages enum pjsip_status_code: PJSIP_SC_TSX_TIMEOUT PJSIP_SC_TSX_TRANSPORT_ERROR PJSIP_TLS_EUNKNOWN PJSIP_TLS_EINVMETHOD PJSIP_TLS_ECACERT PJSIP_TLS_ECERTFILE PJSIP_TLS_EKEYFILE PJSIP_TLS_ECIPHER PJSIP_TLS_ECTX enum pjsip_uri_context_e: PJSIP_URI_IN_CONTACT_HDR struct pjsip_param: pj_str_t name pj_str_t value struct pjsip_uri struct pjsip_sip_uri: pj_str_t user pj_str_t passwd pj_str_t host int port pj_str_t user_param pj_str_t method_param pj_str_t transport_param int ttl_param int lr_param pj_str_t maddr_param pjsip_param other_param pjsip_param header_param struct pjsip_name_addr: pj_str_t display pjsip_uri *uri struct pjsip_media_type: pj_str_t type pj_str_t subtype pjsip_param param enum pjsip_method_e: PJSIP_OPTIONS_METHOD PJSIP_CANCEL_METHOD PJSIP_OTHER_METHOD struct pjsip_method: pjsip_method_e id pj_str_t name struct pjsip_host_port: pj_str_t host int port enum pjsip_hdr_e: PJSIP_H_VIA PJSIP_H_CALL_ID PJSIP_H_CONTACT PJSIP_H_CSEQ PJSIP_H_EXPIRES PJSIP_H_FROM struct pjsip_hdr: pjsip_hdr_e type pj_str_t name ctypedef pjsip_hdr *pjsip_hdr_ptr_const "const pjsip_hdr*" struct pjsip_generic_array_hdr: unsigned int count pj_str_t *values struct pjsip_generic_string_hdr: pj_str_t name pj_str_t hvalue struct pjsip_cid_hdr: pj_str_t id struct pjsip_contact_hdr: int star pjsip_uri *uri int q1000 int expires pjsip_param other_param struct pjsip_clen_hdr: int len struct pjsip_ctype_hdr: pjsip_media_type media struct pjsip_cseq_hdr: int cseq pjsip_method method struct pjsip_generic_int_hdr: int ivalue ctypedef pjsip_generic_int_hdr pjsip_expires_hdr struct pjsip_fromto_hdr: pjsip_uri *uri pj_str_t tag pjsip_param other_param struct pjsip_routing_hdr: pjsip_name_addr name_addr pjsip_param other_param ctypedef pjsip_routing_hdr pjsip_route_hdr struct pjsip_retry_after_hdr: int ivalue pjsip_param param pj_str_t comment struct pjsip_via_hdr: pj_str_t transport pjsip_host_port sent_by int ttl_param int rport_param pj_str_t maddr_param pj_str_t recvd_param pj_str_t branch_param pjsip_param other_param pj_str_t comment enum: PJSIP_MAX_ACCEPT_COUNT struct pjsip_msg_body: pjsip_media_type content_type void *data unsigned int len struct pjsip_request_line: pjsip_method method pjsip_uri *uri struct pjsip_status_line: int code pj_str_t reason union pjsip_msg_line: pjsip_request_line req pjsip_status_line status enum pjsip_msg_type_e: PJSIP_REQUEST_MSG PJSIP_RESPONSE_MSG struct pjsip_msg: pjsip_msg_type_e type pjsip_msg_line line pjsip_hdr hdr pjsip_msg_body *body struct pjsip_buffer: char *start char *cur struct pjsip_tx_data_tp_info: char *dst_name int dst_port pjsip_transport *transport struct pjsip_tx_data: pjsip_msg *msg pj_pool_t *pool pjsip_buffer buf pjsip_tx_data_tp_info tp_info struct pjsip_rx_data_tp_info: pj_pool_t *pool pjsip_transport *transport struct pjsip_rx_data_pkt_info: pj_time_val timestamp char *packet int len char *src_name int src_port struct pjsip_rx_data_msg_info: pjsip_msg *msg pjsip_fromto_hdr *from_hdr "from" pjsip_fromto_hdr *to_hdr "to" pjsip_via_hdr *via struct pjsip_rx_data: pjsip_rx_data_pkt_info pkt_info pjsip_rx_data_tp_info tp_info pjsip_rx_data_msg_info msg_info void *pjsip_hdr_clone(pj_pool_t *pool, void *hdr) nogil void pjsip_msg_add_hdr(pjsip_msg *msg, pjsip_hdr *hdr) nogil void *pjsip_msg_find_hdr(pjsip_msg *msg, pjsip_hdr_e type, void *start) nogil void *pjsip_msg_find_hdr_by_name(pjsip_msg *msg, pj_str_t *name, void *start) nogil void *pjsip_msg_find_remove_hdr_by_name(pjsip_msg *msg, pj_str_t *name, void *start) nogil pjsip_generic_string_hdr *pjsip_generic_string_hdr_create(pj_pool_t *pool, pj_str_t *hname, pj_str_t *hvalue) nogil pjsip_contact_hdr *pjsip_contact_hdr_create(pj_pool_t *pool) nogil pjsip_expires_hdr *pjsip_expires_hdr_create(pj_pool_t *pool, int value) nogil pjsip_msg_body *pjsip_msg_body_create(pj_pool_t *pool, pj_str_t *type, pj_str_t *subtype, pj_str_t *text) nogil pjsip_msg_body *pjsip_msg_body_clone(pj_pool_t *pool, const pjsip_msg_body *body) nogil pjsip_route_hdr *pjsip_route_hdr_init(pj_pool_t *pool, void *mem) nogil void pjsip_sip_uri_init(pjsip_sip_uri *url, int secure) nogil int pjsip_tx_data_dec_ref(pjsip_tx_data *tdata) nogil void pjsip_tx_data_add_ref(pjsip_tx_data *tdata) nogil pj_str_t *pjsip_uri_get_scheme(pjsip_uri *uri) nogil void *pjsip_uri_get_uri(pjsip_uri *uri) nogil int pjsip_uri_print(pjsip_uri_context_e context, void *uri, char *buf, unsigned int size) nogil int PJSIP_URI_SCHEME_IS_SIP(pjsip_sip_uri *uri) nogil enum: PJSIP_PARSE_URI_AS_NAMEADDR pjsip_uri *pjsip_parse_uri(pj_pool_t *pool, char *buf, unsigned int size, unsigned int options) nogil void pjsip_method_init_np(pjsip_method *m, pj_str_t *str) nogil pj_str_t *pjsip_get_status_text(int status_code) nogil int pjsip_print_body(pjsip_msg_body *msg_body, char **buf, int *len) # module enum pjsip_module_priority: PJSIP_MOD_PRIORITY_APPLICATION PJSIP_MOD_PRIORITY_DIALOG_USAGE PJSIP_MOD_PRIORITY_TRANSPORT_LAYER struct pjsip_event struct pjsip_transaction struct pjsip_module: pj_str_t name int id int priority int on_rx_request(pjsip_rx_data *rdata) with gil int on_rx_response(pjsip_rx_data *rdata) with gil int on_tx_request(pjsip_tx_data *tdata) with gil int on_tx_response(pjsip_tx_data *tdata) with gil void on_tsx_state(pjsip_transaction *tsx, pjsip_event *event) with gil # endpoint struct pjsip_endpoint int pjsip_endpt_create(pj_pool_factory *pf, char *name, pjsip_endpoint **endpt) nogil void pjsip_endpt_destroy(pjsip_endpoint *endpt) nogil pj_pool_t *pjsip_endpt_create_pool(pjsip_endpoint *endpt, char *pool_name, int initial, int increment) nogil void pjsip_endpt_release_pool(pjsip_endpoint *endpt, pj_pool_t *pool) nogil int pjsip_endpt_handle_events(pjsip_endpoint *endpt, pj_time_val *max_timeout) nogil int pjsip_endpt_register_module(pjsip_endpoint *endpt, pjsip_module *module) nogil int pjsip_endpt_schedule_timer(pjsip_endpoint *endpt, pj_timer_entry *entry, pj_time_val *delay) nogil void pjsip_endpt_cancel_timer(pjsip_endpoint *endpt, pj_timer_entry *entry) nogil enum: PJSIP_H_ACCEPT PJSIP_H_ALLOW PJSIP_H_SUPPORTED pjsip_hdr_ptr_const pjsip_endpt_get_capability(pjsip_endpoint *endpt, int htype, pj_str_t *hname) nogil int pjsip_endpt_add_capability(pjsip_endpoint *endpt, pjsip_module *mod, int htype, pj_str_t *hname, unsigned count, pj_str_t *tags) nogil int pjsip_endpt_create_response(pjsip_endpoint *endpt, pjsip_rx_data *rdata, int st_code, pj_str_t *st_text, pjsip_tx_data **p_tdata) nogil int pjsip_endpt_send_response2(pjsip_endpoint *endpt, pjsip_rx_data *rdata, pjsip_tx_data *tdata, void *token, void *cb) nogil int pjsip_endpt_respond_stateless(pjsip_endpoint *endpt, pjsip_rx_data *rdata, int st_code, pj_str_t *st_text, pjsip_hdr *hdr_list, pjsip_msg_body *body) nogil int pjsip_endpt_create_request(pjsip_endpoint *endpt, pjsip_method *method, pj_str_t *target, pj_str_t *frm, pj_str_t *to, pj_str_t *contact, pj_str_t *call_id, int cseq,pj_str_t *text, pjsip_tx_data **p_tdata) nogil pj_timer_heap_t *pjsip_endpt_get_timer_heap(pjsip_endpoint *endpt) nogil int pjsip_endpt_create_resolver(pjsip_endpoint *endpt, pj_dns_resolver **p_resv) nogil int pjsip_endpt_set_resolver(pjsip_endpoint *endpt, pj_dns_resolver *resv) nogil pj_dns_resolver* pjsip_endpt_get_resolver(pjsip_endpoint *endpt) nogil # transports enum pjsip_ssl_method: PJSIP_TLSV1_METHOD PJSIP_SSLV23_METHOD struct pjsip_transport: char *type_name pj_sockaddr local_addr pjsip_host_port local_name pjsip_host_port remote_name struct pjsip_tpfactory: pjsip_host_port addr_name int destroy(pjsip_tpfactory *factory) nogil struct pjsip_tls_setting: pj_str_t ca_list_file pj_str_t cert_file pj_str_t privkey_file int method int verify_server pj_time_val timeout enum pjsip_tpselector_type: PJSIP_TPSELECTOR_TRANSPORT union pjsip_tpselector_u: pjsip_transport *transport struct pjsip_tpselector: pjsip_tpselector_type type pjsip_tpselector_u u int pjsip_transport_shutdown(pjsip_transport *tp) nogil int pjsip_udp_transport_start(pjsip_endpoint *endpt, pj_sockaddr_in *local, pjsip_host_port *a_name, unsigned int async_cnt, pjsip_transport **p_transport) nogil int pjsip_tcp_transport_start2(pjsip_endpoint *endpt, pj_sockaddr_in *local, pjsip_host_port *a_name, unsigned int async_cnt, pjsip_tpfactory **p_tpfactory) nogil int pjsip_tls_transport_start(pjsip_endpoint *endpt, pjsip_tls_setting *opt, pj_sockaddr_in *local, pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory) nogil void pjsip_tls_setting_default(pjsip_tls_setting *tls_opt) nogil int pjsip_transport_shutdown(pjsip_transport *tp) nogil # transaction layer enum pjsip_role_e: PJSIP_ROLE_UAC PJSIP_ROLE_UAS enum pjsip_tsx_state_e: PJSIP_TSX_STATE_TRYING PJSIP_TSX_STATE_PROCEEDING PJSIP_TSX_STATE_COMPLETED PJSIP_TSX_STATE_TERMINATED struct pjsip_transaction: int status_code pj_str_t status_text pjsip_role_e role pjsip_tx_data *last_tx pjsip_tsx_state_e state void **mod_data pjsip_method method int pjsip_tsx_layer_init_module(pjsip_endpoint *endpt) nogil int pjsip_tsx_create_key(pj_pool_t *pool, pj_str_t *key, pjsip_role_e role, pjsip_method *method, pjsip_rx_data *rdata) nogil pjsip_transaction *pjsip_tsx_layer_find_tsx(pj_str_t *key, int lock) nogil int pjsip_tsx_create_uac(pjsip_module *tsx_user, pjsip_tx_data *tdata, pjsip_transaction **p_tsx) nogil int pjsip_tsx_terminate(pjsip_transaction *tsx, int code) nogil int pjsip_tsx_send_msg(pjsip_transaction *tsx, pjsip_tx_data *tdata) nogil pjsip_transaction *pjsip_rdata_get_tsx(pjsip_rx_data *rdata) nogil int pjsip_tsx_create_uas(pjsip_module *tsx_user, pjsip_rx_data *rdata, pjsip_transaction **p_tsx) nogil void pjsip_tsx_recv_msg(pjsip_transaction *tsx, pjsip_rx_data *rdata) nogil # event enum pjsip_event_id_e: PJSIP_EVENT_TSX_STATE PJSIP_EVENT_RX_MSG PJSIP_EVENT_TX_MSG PJSIP_EVENT_TRANSPORT_ERROR PJSIP_EVENT_TIMER union pjsip_event_body_tsx_state_src: pjsip_rx_data *rdata pjsip_tx_data *tdata struct pjsip_event_body_tsx_state: pjsip_event_body_tsx_state_src src pjsip_transaction *tsx pjsip_event_id_e type struct pjsip_event_body_rx_msg: pjsip_rx_data *rdata union pjsip_event_body: pjsip_event_body_tsx_state tsx_state pjsip_event_body_rx_msg rx_msg struct pjsip_event: pjsip_event_id_e type pjsip_event_body body int pjsip_endpt_send_request(pjsip_endpoint *endpt, pjsip_tx_data *tdata, int timeout, void *token, void cb(void *token, pjsip_event *e) with gil) nogil # auth enum: PJSIP_EFAILEDCREDENTIAL enum pjsip_cred_data_type: PJSIP_CRED_DATA_PLAIN_PASSWD PJSIP_CRED_DATA_DIGEST struct pjsip_cred_info: pj_str_t realm pj_str_t scheme pj_str_t username pjsip_cred_data_type data_type pj_str_t data struct pjsip_auth_clt_sess: pass int pjsip_auth_clt_init(pjsip_auth_clt_sess *sess, pjsip_endpoint *endpt, pj_pool_t *pool, unsigned int options) nogil int pjsip_auth_clt_set_credentials(pjsip_auth_clt_sess *sess, int cred_cnt, pjsip_cred_info *c) nogil int pjsip_auth_clt_reinit_req(pjsip_auth_clt_sess *sess, pjsip_rx_data *rdata, pjsip_tx_data *old_request, pjsip_tx_data **new_request) nogil # dialog layer ctypedef pjsip_module pjsip_user_agent struct pjsip_dlg_party: pjsip_contact_hdr *contact pjsip_fromto_hdr *info struct pjsip_dialog: pjsip_auth_clt_sess auth_sess pjsip_cid_hdr *call_id pj_pool_t *pool pjsip_dlg_party local pjsip_dlg_party remote struct pjsip_ua_init_param: pjsip_dialog *on_dlg_forked(pjsip_dialog *first_set, pjsip_rx_data *res) nogil int pjsip_ua_init_module(pjsip_endpoint *endpt, pjsip_ua_init_param *prm) nogil pjsip_user_agent *pjsip_ua_instance() nogil int pjsip_dlg_create_uac(pjsip_user_agent *ua, pj_str_t *local_uri, pj_str_t *local_contact, pj_str_t *remote_uri, pj_str_t *target, pjsip_dialog **p_dlg) nogil int pjsip_dlg_set_route_set(pjsip_dialog *dlg, pjsip_route_hdr *route_set) nogil int pjsip_dlg_create_uas_and_inc_lock(pjsip_user_agent *ua, pjsip_rx_data *rdata, pj_str_t *contact, pjsip_dialog **p_dlg) nogil int pjsip_dlg_terminate(pjsip_dialog *dlg) nogil int pjsip_dlg_set_transport(pjsip_dialog *dlg, pjsip_tpselector *sel) nogil int pjsip_dlg_respond(pjsip_dialog *dlg, pjsip_rx_data *rdata, int st_code, pj_str_t *st_text, pjsip_hdr *hdr_list, pjsip_msg_body *body) nogil int pjsip_dlg_create_response(pjsip_dialog *dlg, pjsip_rx_data *rdata, int st_code, pj_str_t *st_text, pjsip_tx_data **tdata) nogil int pjsip_dlg_modify_response(pjsip_dialog *dlg, pjsip_tx_data *tdata, int st_code, pj_str_t *st_text) nogil int pjsip_dlg_send_response(pjsip_dialog *dlg, pjsip_transaction *tsx, pjsip_tx_data *tdata) nogil void pjsip_dlg_inc_lock(pjsip_dialog *dlg) nogil void pjsip_dlg_dec_lock(pjsip_dialog *dlg) nogil int pjsip_dlg_inc_session(pjsip_dialog *dlg, pjsip_module *mod) nogil int pjsip_dlg_dec_session(pjsip_dialog *dlg, pjsip_module *mod) nogil cdef extern from "pjsip-simple/evsub_msg.h": struct pjsip_event_hdr: pj_str_t event_type pj_str_t id_param pjsip_param other_param struct pjsip_sub_state_hdr: pj_str_t sub_state pj_str_t reason_param int expires_param int retry_after pjsip_param other_param pjsip_event_hdr *pjsip_event_hdr_create(pj_pool_t *pool) nogil cdef extern from "pjsip_simple.h": # subscribe / notify enum: PJSIP_EVSUB_NO_EVENT_ID enum pjsip_evsub_state: PJSIP_EVSUB_STATE_PENDING PJSIP_EVSUB_STATE_ACTIVE PJSIP_EVSUB_STATE_TERMINATED struct pjsip_evsub struct pjsip_evsub_user: void on_evsub_state(pjsip_evsub *sub, pjsip_event *event) with gil void on_tsx_state(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil void on_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil void on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text,pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil void on_client_refresh(pjsip_evsub *sub) with gil void on_server_timeout(pjsip_evsub *sub) with gil int pjsip_evsub_init_module(pjsip_endpoint *endpt) nogil int pjsip_evsub_register_pkg(pjsip_module *pkg_mod, pj_str_t *event_name, unsigned int expires, unsigned int accept_cnt, pj_str_t *accept) nogil int pjsip_evsub_create_uac(pjsip_dialog *dlg, pjsip_evsub_user *user_cb, pj_str_t *event, int option, pjsip_evsub **p_evsub) nogil int pjsip_evsub_create_uas(pjsip_dialog *dlg, pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, unsigned int option, pjsip_evsub **p_evsub) nogil int pjsip_evsub_initiate(pjsip_evsub *sub, void *method, unsigned int expires, pjsip_tx_data **p_tdata) nogil int pjsip_evsub_send_request(pjsip_evsub *sub, pjsip_tx_data *tdata) nogil int pjsip_evsub_terminate(pjsip_evsub *sub, int notify) nogil char *pjsip_evsub_get_state_name(pjsip_evsub *sub) nogil void pjsip_evsub_set_mod_data(pjsip_evsub *sub, int mod_id, void *data) nogil void *pjsip_evsub_get_mod_data(pjsip_evsub *sub, int mod_id) nogil void pjsip_evsub_update_expires(pjsip_evsub *sub, int interval) nogil void pjsip_evsub_set_timer(pjsip_evsub *sub, int timer_id, int seconds) nogil pjsip_hdr *pjsip_evsub_get_allow_events_hdr(pjsip_module *m) nogil int pjsip_evsub_notify(pjsip_evsub *sub, pjsip_evsub_state state, pj_str_t *state_str, pj_str_t *reason, pjsip_tx_data **p_tdata) nogil cdef extern from "pjsip_ua.h": # 100rel / PRACK int pjsip_100rel_init_module(pjsip_endpoint *endpt) nogil # invite sessions enum pjsip_inv_option: PJSIP_INV_SUPPORT_100REL enum pjsip_inv_state: PJSIP_INV_STATE_INCOMING PJSIP_INV_STATE_CONFIRMED enum pjmedia_mod_offer_flag: PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE struct pjsip_inv_session: pjsip_inv_state state void **mod_data pjmedia_sdp_neg *neg unsigned sdp_neg_flags int cause pj_str_t cause_text int cancelling pjsip_transaction *invite_tsx struct pjsip_inv_callback: void on_state_changed(pjsip_inv_session *inv, pjsip_event *e) with gil void on_new_session(pjsip_inv_session *inv, pjsip_event *e) with gil void on_tsx_state_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e) with gil void on_media_update(pjsip_inv_session *inv, int status) with gil int on_rx_reinvite(pjsip_inv_session *inv, pjmedia_sdp_session_ptr_const offer, pjsip_rx_data *rdata) with gil int pjsip_inv_usage_init(pjsip_endpoint *endpt, pjsip_inv_callback *cb) nogil int pjsip_inv_terminate(pjsip_inv_session *inv, int st_code, int notify) nogil int pjsip_inv_end_session(pjsip_inv_session *inv, int st_code, pj_str_t *st_text, pjsip_tx_data **p_tdata) nogil int pjsip_inv_cancel_reinvite(pjsip_inv_session *inv, pjsip_tx_data **p_tdata) nogil int pjsip_inv_send_msg(pjsip_inv_session *inv, pjsip_tx_data *tdata) nogil int pjsip_inv_verify_request(pjsip_rx_data *rdata, unsigned int *options, pjmedia_sdp_session *sdp, pjsip_dialog *dlg, pjsip_endpoint *endpt, pjsip_tx_data **tdata) nogil int pjsip_inv_create_uas(pjsip_dialog *dlg, pjsip_rx_data *rdata, pjmedia_sdp_session *local_sdp, unsigned int options, pjsip_inv_session **p_inv) nogil int pjsip_inv_initial_answer(pjsip_inv_session *inv, pjsip_rx_data *rdata, int st_code, pj_str_t *st_text, pjmedia_sdp_session *sdp, pjsip_tx_data **p_tdata) nogil int pjsip_inv_answer(pjsip_inv_session *inv, int st_code, pj_str_t *st_text, pjmedia_sdp_session *local_sdp, pjsip_tx_data **p_tdata) nogil int pjsip_inv_create_uac(pjsip_dialog *dlg, pjmedia_sdp_session *local_sdp, unsigned int options, pjsip_inv_session **p_inv) nogil int pjsip_inv_invite(pjsip_inv_session *inv, pjsip_tx_data **p_tdata) nogil char *pjsip_inv_state_name(pjsip_inv_state state) nogil int pjsip_inv_reinvite(pjsip_inv_session *inv, pj_str_t *new_contact, pjmedia_sdp_session *new_offer, pjsip_tx_data **p_tdata) nogil int pjsip_create_sdp_body(pj_pool_t *pool, pjmedia_sdp_session *sdp, pjsip_msg_body **p_body) nogil # Replaces struct pjsip_replaces_hdr: pj_str_t call_id pj_str_t to_tag pj_str_t from_tag int early_only pjsip_param other_param pjsip_replaces_hdr *pjsip_replaces_hdr_create(pj_pool_t *pool) nogil int pjsip_replaces_verify_request(pjsip_rx_data *rdata, pjsip_dialog **p_dlg, int lock_dlg, pjsip_tx_data **p_tdata) nogil int pjsip_replaces_init_module(pjsip_endpoint *endpt) nogil # declarations # core.util cdef class frozenlist(object): # attributes cdef int initialized cdef list list cdef long hash cdef class frozendict(object): # attributes cdef int initialized cdef dict dict cdef long hash cdef class PJSTR(object): # attributes cdef pj_str_t pj_str cdef object str # core.lib cdef class PJLIB(object): # attributes cdef int _init_done cdef class PJCachingPool(object): # attributes cdef pj_caching_pool _obj cdef int _init_done cdef class PJSIPEndpoint(object): # attributes cdef pjsip_endpoint *_obj cdef pj_pool_t *_pool cdef pjsip_transport *_udp_transport cdef pjsip_tpfactory *_tcp_transport cdef pjsip_tpfactory *_tls_transport cdef int _tls_verify_server cdef PJSTR _tls_ca_file cdef PJSTR _tls_cert_file cdef PJSTR _tls_privkey_file cdef object _local_ip_used cdef int _tls_timeout # private methods cdef int _make_local_addr(self, pj_sockaddr_in *local_addr, object ip_address, int port) except -1 cdef int _start_udp_transport(self, int port) except -1 cdef int _stop_udp_transport(self) except -1 cdef int _start_tcp_transport(self, int port) except -1 cdef int _stop_tcp_transport(self) except -1 cdef int _start_tls_transport(self, port) except -1 cdef int _stop_tls_transport(self) except -1 cdef int _set_dns_nameservers(self, list servers) except -1 cdef class PJMEDIAEndpoint(object): # attributes cdef pjmedia_endpt *_obj cdef pj_pool_t *_pool cdef int _has_audio_codecs cdef int _has_video cdef int _has_ffmpeg_video cdef int _has_vpx # private methods cdef list _get_codecs(self) cdef list _get_all_codecs(self) cdef list _get_current_codecs(self) cdef int _set_codecs(self, list req_codecs) except -1 cdef list _get_video_codecs(self) cdef list _get_all_video_codecs(self) cdef list _get_current_video_codecs(self) cdef int _set_video_codecs(self, list req_codecs) except -1 cdef void _audio_subsystem_init(self, PJCachingPool caching_pool) cdef void _audio_subsystem_shutdown(self) cdef void _video_subsystem_init(self, PJCachingPool caching_pool) cdef void _video_subsystem_shutdown(self) cdef void _set_h264_options(self, str profile, int level) cdef void _set_video_options(self, tuple max_resolution, int max_framerate, float max_bitrate) # core.helper cdef class BaseCredentials(object): # attributes cdef pjsip_cred_info _credentials # private methods cdef pjsip_cred_info* get_cred_info(self) cdef class Credentials(BaseCredentials): # attributes cdef str _username cdef str _realm cdef str _password cdef bint _digest cdef class FrozenCredentials(BaseCredentials): # attributes cdef int initialized cdef readonly str username cdef readonly str realm cdef readonly str password cdef readonly bint digest cdef class BaseSIPURI(object): pass cdef class SIPURI(BaseSIPURI): # attributes cdef public object user cdef public object password cdef public object host cdef public bint secure cdef public dict parameters cdef public dict headers cdef object _port cdef class FrozenSIPURI(BaseSIPURI): # attributes cdef int initialized cdef readonly object user cdef readonly object password cdef readonly object host cdef readonly object port cdef readonly bint secure cdef readonly frozendict parameters cdef readonly frozendict headers cdef SIPURI SIPURI_create(pjsip_sip_uri *base_uri) cdef FrozenSIPURI FrozenSIPURI_create(pjsip_sip_uri *base_uri) # core.headers cdef class BaseHeader(object): pass cdef class Header(BaseHeader): # attributes cdef str _name cdef str _body cdef class FrozenHeader(BaseHeader): # attributes cdef readonly str name cdef readonly str body cdef class BaseContactHeader(object): pass cdef class ContactHeader(BaseContactHeader): # attributes cdef SIPURI _uri cdef unicode _display_name cdef dict _parameters cdef class FrozenContactHeader(BaseContactHeader): # attributes cdef int initialized cdef readonly FrozenSIPURI uri cdef readonly unicode display_name cdef readonly frozendict parameters cdef class BaseContentTypeHeader(object): pass cdef class ContentTypeHeader(BaseContentTypeHeader): # attributes cdef str _content_type cdef dict _parameters cdef class FrozenContentTypeHeader(BaseContentTypeHeader): # attributes cdef int initialized cdef readonly str _content_type cdef readonly frozendict parameters cdef class BaseIdentityHeader(object): pass cdef class IdentityHeader(BaseIdentityHeader): # attributes cdef SIPURI _uri cdef public unicode display_name cdef dict _parameters cdef class FrozenIdentityHeader(BaseIdentityHeader): # attributes cdef int initialized cdef readonly FrozenSIPURI uri cdef readonly unicode display_name cdef readonly frozendict parameters cdef class FromHeader(IdentityHeader): pass cdef class FrozenFromHeader(FrozenIdentityHeader): pass cdef class ToHeader(IdentityHeader): pass cdef class FrozenToHeader(FrozenIdentityHeader): pass cdef class RouteHeader(IdentityHeader): pass cdef class FrozenRouteHeader(FrozenIdentityHeader): pass cdef class RecordRouteHeader(IdentityHeader): pass cdef class FrozenRecordRouteHeader(FrozenIdentityHeader): pass cdef class BaseRetryAfterHeader(object): pass cdef class RetryAfterHeader(BaseRetryAfterHeader): # attributes cdef public int seconds cdef public str comment cdef dict _parameters cdef class FrozenRetryAfterHeader(BaseRetryAfterHeader): # attributes cdef int initialized cdef readonly int seconds cdef readonly str comment cdef readonly frozendict parameters cdef class BaseViaHeader(object): pass cdef class ViaHeader(BaseViaHeader): # attributes cdef str _transport cdef str _host cdef int _port cdef dict _parameters cdef class FrozenViaHeader(BaseViaHeader): # attributes cdef int initialized cdef readonly str transport cdef readonly str host cdef readonly int port cdef readonly frozendict parameters cdef class BaseWarningHeader(object): pass cdef class WarningHeader(BaseWarningHeader): # attributes cdef int _code cdef str _agent cdef str _text cdef class FrozenWarningHeader(BaseWarningHeader): # attributes cdef int initialized cdef readonly int code cdef readonly str agent cdef readonly str text cdef class BaseEventHeader(object): pass cdef class EventHeader(BaseEventHeader): # attributes cdef public event cdef dict _parameters cdef class FrozenEventHeader(BaseEventHeader): # attributes cdef int initialized cdef readonly str event cdef readonly frozendict parameters cdef class BaseSubscriptionStateHeader(object): pass cdef class SubscriptionStateHeader(BaseSubscriptionStateHeader): # attributes cdef public state cdef dict _parameters cdef class FrozenSubscriptionStateHeader(BaseSubscriptionStateHeader): # attributes cdef int initialized cdef readonly str state cdef readonly frozendict parameters cdef class BaseReasonHeader(object): pass cdef class ReasonHeader(BaseReasonHeader): # attributes cdef public str protocol cdef public dict parameters cdef class FrozenReasonHeader(BaseReasonHeader): # attributes cdef int initialized cdef readonly str protocol cdef readonly frozendict parameters cdef class BaseReferToHeader(object): pass cdef class ReferToHeader(BaseReferToHeader): # attributes cdef public str uri cdef dict _parameters cdef class FrozenReferToHeader(BaseReferToHeader): # attributes cdef int initialized cdef readonly str uri cdef readonly frozendict parameters cdef class BaseSubjectHeader(object): pass cdef class SubjectHeader(BaseSubjectHeader): # attributes cdef public unicode subject cdef class FrozenSubjectHeader(BaseSubjectHeader): # attributes cdef int initialized cdef readonly unicode subject cdef class BaseReplacesHeader(object): pass cdef class ReplacesHeader(BaseReplacesHeader): # attributes cdef public str call_id cdef public str from_tag cdef public str to_tag cdef public int early_only cdef dict _parameters cdef class FrozenReplacesHeader(BaseReplacesHeader): # attributes cdef int initialized cdef readonly str call_id cdef readonly str from_tag cdef readonly str to_tag cdef readonly int early_only cdef readonly frozendict parameters cdef Header Header_create(pjsip_generic_string_hdr *header) cdef FrozenHeader FrozenHeader_create(pjsip_generic_string_hdr *header) cdef ContactHeader ContactHeader_create(pjsip_contact_hdr *header) cdef FrozenContactHeader FrozenContactHeader_create(pjsip_contact_hdr *header) cdef ContentTypeHeader ContentTypeHeader_create(pjsip_ctype_hdr *header) cdef FrozenContentTypeHeader FrozenContentTypeHeader_create(pjsip_ctype_hdr *header) cdef FromHeader FromHeader_create(pjsip_fromto_hdr *header) cdef FrozenFromHeader FrozenFromHeader_create(pjsip_fromto_hdr *header) cdef ToHeader ToHeader_create(pjsip_fromto_hdr *header) cdef FrozenToHeader FrozenToHeader_create(pjsip_fromto_hdr *header) cdef RouteHeader RouteHeader_create(pjsip_routing_hdr *header) cdef FrozenRouteHeader FrozenRouteHeader_create(pjsip_routing_hdr *header) cdef RecordRouteHeader RecordRouteHeader_create(pjsip_routing_hdr *header) cdef FrozenRecordRouteHeader FrozenRecordRouteHeader_create(pjsip_routing_hdr *header) cdef RetryAfterHeader RetryAfterHeader_create(pjsip_retry_after_hdr *header) cdef FrozenRetryAfterHeader FrozenRetryAfterHeader_create(pjsip_retry_after_hdr *header) cdef ViaHeader ViaHeader_create(pjsip_via_hdr *header) cdef FrozenViaHeader FrozenViaHeader_create(pjsip_via_hdr *header) cdef EventHeader EventHeader_create(pjsip_event_hdr *header) cdef FrozenEventHeader FrozenEventHeader_create(pjsip_event_hdr *header) cdef SubscriptionStateHeader SubscriptionStateHeader_create(pjsip_sub_state_hdr *header) cdef FrozenSubscriptionStateHeader FrozenSubscriptionStateHeader_create(pjsip_sub_state_hdr *header) cdef ReferToHeader ReferToHeader_create(pjsip_generic_string_hdr *header) cdef FrozenReferToHeader FrozenReferToHeader_create(pjsip_generic_string_hdr *header) cdef SubjectHeader SubjectHeader_create(pjsip_generic_string_hdr *header) cdef FrozenSubjectHeader FrozenSubjectHeader_create(pjsip_generic_string_hdr *header) cdef ReplacesHeader ReplacesHeader_create(pjsip_replaces_hdr *header) cdef FrozenReplacesHeader FrozenReplacesHeader_create(pjsip_replaces_hdr *header) # core.util cdef int _str_to_pj_str(object string, pj_str_t *pj_str) except -1 cdef object _pj_str_to_str(pj_str_t pj_str) cdef object _pj_status_to_str(int status) cdef object _pj_status_to_def(int status) cdef dict _pjsip_param_to_dict(pjsip_param *param_list) cdef int _dict_to_pjsip_param(object params, pjsip_param *param_list, pj_pool_t *pool) cdef int _pjsip_msg_to_dict(pjsip_msg *msg, dict info_dict) except -1 cdef int _is_valid_ip(int af, object ip) except -1 cdef int _get_ip_version(object ip) except -1 cdef int _add_headers_to_tdata(pjsip_tx_data *tdata, object headers) except -1 cdef int _remove_headers_from_tdata(pjsip_tx_data *tdata, object headers) except -1 cdef int _BaseSIPURI_to_pjsip_sip_uri(BaseSIPURI uri, pjsip_sip_uri *pj_uri, pj_pool_t *pool) except -1 cdef int _BaseRouteHeader_to_pjsip_route_hdr(BaseIdentityHeader header, pjsip_route_hdr *pj_header, pj_pool_t *pool) except -1 # core.ua ctypedef int (*timer_callback)(object, object) except -1 cdef class Timer(object): # attributes cdef int _scheduled cdef double schedule_time cdef timer_callback callback cdef object obj # private methods cdef int schedule(self, float delay, timer_callback callback, object obj) except -1 cdef int cancel(self) except -1 cdef int call(self) except -1 cdef class PJSIPThread(object): # attributes cdef pj_thread_t *_obj cdef long _thread_desc[PJ_THREAD_DESC_SIZE] cdef class PJSIPUA(object): # attributes cdef object _threads cdef object _event_handler cdef list _timers cdef PJLIB _pjlib cdef PJCachingPool _caching_pool cdef PJSIPEndpoint _pjsip_endpoint cdef PJMEDIAEndpoint _pjmedia_endpoint cdef pjsip_module _module cdef PJSTR _module_name cdef pjsip_module _opus_fix_module cdef PJSTR _opus_fix_module_name cdef pjsip_module _trace_module cdef PJSTR _trace_module_name cdef pjsip_module _ua_tag_module cdef PJSTR _ua_tag_module_name cdef pjsip_module _event_module cdef PJSTR _event_module_name cdef int _trace_sip cdef int _detect_sip_loops cdef int _enable_colorbar_device cdef PJSTR _user_agent cdef object _events cdef object _sent_messages cdef object _ip_address cdef int _rtp_port_start cdef int _rtp_port_count cdef int _rtp_port_usable_count cdef int _rtp_port_index cdef pj_stun_config _stun_cfg cdef int _fatal_error cdef set _incoming_events cdef set _incoming_requests cdef pj_rwmutex_t *audio_change_rwlock cdef pj_mutex_t *video_lock cdef list old_devices cdef list old_video_devices cdef object _zrtp_cache # private methods cdef object _get_sound_devices(self, int is_output) cdef object _get_default_sound_device(self, int is_output) cdef object _get_video_devices(self) cdef object _get_default_video_device(self) cdef int _poll_log(self) except -1 cdef int _handle_exception(self, int is_fatal) except -1 cdef int _check_self(self) except -1 cdef int _check_thread(self) except -1 cdef int _add_timer(self, Timer timer) except -1 cdef int _remove_timer(self, Timer timer) except -1 cdef int _cb_rx_request(self, pjsip_rx_data *rdata) except 0 cdef pj_pool_t* create_memory_pool(self, bytes name, int initial_size, int resize_size) cdef void release_memory_pool(self, pj_pool_t* pool) cdef void reset_memory_pool(self, pj_pool_t* pool) cdef int _PJSIPUA_cb_rx_request(pjsip_rx_data *rdata) with gil cdef void _cb_detect_nat_type(void *user_data, pj_stun_nat_detect_result_ptr_const res) with gil cdef int _cb_opus_fix_tx(pjsip_tx_data *tdata) with gil cdef int _cb_trace_rx(pjsip_rx_data *rdata) with gil cdef int _cb_trace_tx(pjsip_tx_data *tdata) with gil cdef int _cb_add_user_agent_hdr(pjsip_tx_data *tdata) with gil cdef int _cb_add_server_hdr(pjsip_tx_data *tdata) with gil cdef PJSIPUA _get_ua() cdef int deallocate_weakref(object weak_ref, object timer) except -1 with gil # core.sound cdef class AudioMixer(object): # attributes cdef int _input_volume cdef int _output_volume cdef bint _muted cdef pj_mutex_t *_lock cdef pj_pool_t *_conf_pool cdef pj_pool_t *_snd_pool cdef pjmedia_conf *_obj cdef pjmedia_master_port *_master_port cdef pjmedia_port *_null_port cdef pjmedia_snd_port *_snd cdef list _connected_slots cdef readonly int ec_tail_length cdef readonly int sample_rate cdef readonly int slot_count cdef readonly int used_slot_count cdef readonly unicode input_device cdef readonly unicode output_device cdef readonly unicode real_input_device cdef readonly unicode real_output_device # private methods cdef void _start_sound_device(self, PJSIPUA ua, unicode input_device, unicode output_device, int ec_tail_length) cdef void _stop_sound_device(self, PJSIPUA ua) cdef int _add_port(self, PJSIPUA ua, pj_pool_t *pool, pjmedia_port *port) except -1 with gil cdef int _remove_port(self, PJSIPUA ua, unsigned int slot) except -1 with gil cdef int _cb_postpoll_stop_sound(self, timer) except -1 cdef class ToneGenerator(object): # attributes cdef int _slot cdef int _volume cdef pj_mutex_t *_lock cdef pj_pool_t *_pool cdef pjmedia_port *_obj cdef Timer _timer cdef readonly AudioMixer mixer # private methods cdef PJSIPUA _get_ua(self, int raise_exception) cdef int _stop(self, PJSIPUA ua) except -1 cdef int _cb_check_done(self, timer) except -1 cdef class RecordingWaveFile(object): # attributes cdef int _slot cdef int _was_started cdef pj_mutex_t *_lock cdef pj_pool_t *_pool cdef pjmedia_port *_port cdef readonly str filename cdef readonly AudioMixer mixer # private methods cdef PJSIPUA _check_ua(self) cdef int _stop(self, PJSIPUA ua) except -1 cdef class WaveFile(object): # attributes cdef object __weakref__ cdef object weakref cdef int _slot cdef int _volume cdef pj_mutex_t *_lock cdef pj_pool_t *_pool cdef pjmedia_port *_port cdef readonly str filename cdef readonly AudioMixer mixer # private methods cdef PJSIPUA _check_ua(self) cdef int _stop(self, PJSIPUA ua, int notify) except -1 cdef int _cb_eof(self, timer) except -1 cdef class MixerPort(object): cdef int _slot cdef int _was_started cdef pj_mutex_t *_lock cdef pj_pool_t *_pool cdef pjmedia_port *_port cdef readonly AudioMixer mixer # private methods cdef PJSIPUA _check_ua(self) cdef int _stop(self, PJSIPUA ua) except -1 cdef int _AudioMixer_dealloc_handler(object obj) except -1 cdef int cb_play_wav_eof(pjmedia_port *port, void *user_data) with gil # core.video cdef class VideoFrame(object): cdef readonly str data cdef readonly int width cdef readonly int height cdef class VideoConsumer(object): cdef pjmedia_port *consumer_port cdef pjmedia_vid_port *_video_port cdef pj_pool_t *_pool cdef pj_mutex_t *_lock cdef int _running cdef int _closed cdef VideoProducer _producer cdef object __weakref__ cdef object weakref cdef void _set_producer(self, VideoProducer producer) cdef class VideoProducer(object): cdef pjmedia_port *producer_port cdef pjmedia_vid_port *_video_port cdef pj_pool_t *_pool cdef pj_mutex_t *_lock cdef int _running cdef int _started cdef int _closed cdef object _consumers cdef void _add_consumer(self, VideoConsumer consumer) cdef void _remove_consumer(self, VideoConsumer consumer) cdef class VideoCamera(VideoProducer): cdef pjmedia_port *_video_tee cdef readonly unicode name cdef readonly unicode real_name cdef void _start(self) cdef void _stop(self) cdef class FrameBufferVideoRenderer(VideoConsumer): cdef pjmedia_vid_dev_stream *_video_stream cdef object _frame_handler cdef _initialize(self, VideoProducer producer) cdef void _destroy_video_port(self) cdef void _start(self) cdef void _stop(self) cdef class LocalVideoStream(VideoConsumer): cdef void _initialize(self, pjmedia_port *media_port) cdef class RemoteVideoStream(VideoProducer): cdef pjmedia_vid_stream *_video_stream cdef object _event_handler cdef void _initialize(self, pjmedia_vid_stream *stream) cdef LocalVideoStream_create(pjmedia_vid_stream *stream) cdef RemoteVideoStream_create(pjmedia_vid_stream *stream, format_change_handler=*) cdef int RemoteVideoStream_on_event(pjmedia_event *event, void *user_data) with gil cdef void _start_video_port(pjmedia_vid_port *port) cdef void _stop_video_port(pjmedia_vid_port *port) # core.event cdef struct _core_event cdef struct _handler_queue cdef int _event_queue_append(_core_event *event) cdef void _cb_log(int level, char_ptr_const data, int len) cdef int _add_event(object event_name, dict params) except -1 cdef list _get_clear_event_queue() cdef int _add_handler(int func(object obj) except -1, object obj, _handler_queue *queue) except -1 cdef int _remove_handler(object obj, _handler_queue *queue) except -1 cdef int _process_handler_queue(PJSIPUA ua, _handler_queue *queue) except -1 # core.request cdef class EndpointAddress(object): # attributes cdef readonly bytes ip cdef readonly int port cdef class Request(object): # attributes cdef readonly object state cdef PJSTR _method cdef readonly EndpointAddress peer_address cdef readonly FrozenCredentials credentials cdef readonly FrozenFromHeader from_header cdef readonly FrozenToHeader to_header cdef readonly FrozenSIPURI request_uri cdef readonly FrozenContactHeader contact_header cdef readonly FrozenRouteHeader route_header cdef PJSTR _call_id cdef readonly int cseq cdef readonly frozenlist extra_headers cdef PJSTR _content_type cdef PJSTR _content_subtype cdef PJSTR _body cdef pjsip_tx_data *_tdata cdef pjsip_transaction *_tsx cdef pjsip_auth_clt_sess _auth cdef pjsip_route_hdr _route_header cdef int _need_auth cdef pj_timer_entry _timer cdef int _timer_active cdef int _expire_rest cdef object _expire_time cdef object _timeout # private methods cdef PJSIPUA _get_ua(self) cdef int _cb_tsx_state(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef int _cb_timer(self, PJSIPUA ua) except -1 cdef class IncomingRequest(object): # attributes cdef readonly str state cdef pjsip_transaction *_tsx cdef pjsip_tx_data *_tdata cdef readonly EndpointAddress peer_address # methods cdef int init(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef void _Request_cb_tsx_state(pjsip_transaction *tsx, pjsip_event *event) with gil cdef void _Request_cb_timer(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) with gil # core.referral cdef class Referral(object): # attributes cdef pjsip_evsub *_obj cdef pjsip_dialog *_dlg cdef pjsip_route_hdr _route_header cdef pj_list _route_set cdef int _create_subscription cdef readonly object state cdef pj_timer_entry _timeout_timer cdef int _timeout_timer_active cdef pj_timer_entry _refresh_timer cdef int _refresh_timer_active cdef readonly EndpointAddress peer_address cdef readonly FrozenFromHeader from_header cdef readonly FrozenToHeader to_header cdef readonly FrozenReferToHeader refer_to_header cdef readonly FrozenRouteHeader route_header cdef readonly FrozenCredentials credentials cdef readonly FrozenContactHeader local_contact_header cdef readonly FrozenContactHeader remote_contact_header cdef readonly int refresh cdef readonly frozenlist extra_headers cdef pj_time_val _request_timeout cdef int _want_end cdef int _term_code cdef object _term_reason # private methods cdef PJSIPUA _get_ua(self) cdef int _update_contact_header(self, BaseContactHeader contact_header) except -1 cdef int _cancel_timers(self, PJSIPUA ua, int cancel_timeout, int cancel_refresh) except -1 cdef int _send_refer(self, PJSIPUA ua, pj_time_val *timeout, FrozenReferToHeader refer_to_header, frozenlist extra_headers) except -1 cdef int _send_subscribe(self, PJSIPUA ua, int expires, pj_time_val *timeout, frozenlist extra_headers) except -1 cdef int _cb_state(self, PJSIPUA ua, object state, int code, str reason) except -1 cdef int _cb_got_response(self, PJSIPUA ua, pjsip_rx_data *rdata, str method) except -1 cdef int _cb_notify(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef int _cb_timeout_timer(self, PJSIPUA ua) cdef int _cb_refresh_timer(self, PJSIPUA ua) cdef class IncomingReferral(object): cdef pjsip_evsub *_obj cdef pjsip_dialog *_dlg cdef pjsip_tx_data *_initial_response cdef pjsip_transaction *_initial_tsx cdef pj_time_val _expires_time cdef int _create_subscription cdef readonly str state cdef readonly EndpointAddress peer_address cdef readonly FrozenContactHeader local_contact_header cdef readonly FrozenContactHeader remote_contact_header cdef PJSTR _content cdef int init(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef PJSIPUA _get_ua(self, int raise_exception) cdef int _set_content(self, int code, str reason) except -1 cdef int _set_state(self, str state) except -1 cdef int _send_initial_response(self, int code) except -1 cdef int _send_notify(self) except -1 cdef int _terminate(self, PJSIPUA ua, int do_cleanup) except -1 cdef int _cb_rx_refresh(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef int _cb_server_timeout(self, PJSIPUA ua) except -1 cdef int _cb_tsx(self, PJSIPUA ua, pjsip_event *event) except -1 cdef void _Referral_cb_state(pjsip_evsub *sub, pjsip_event *event) with gil cdef void _Referral_cb_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil cdef void _Referral_cb_refresh(pjsip_evsub *sub) with gil cdef void _IncomingReferral_cb_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil cdef void _IncomingReferral_cb_server_timeout(pjsip_evsub *sub) with gil cdef void _IncomingReferral_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil # core.subscription cdef class Subscription(object): # attributes cdef pjsip_evsub *_obj cdef pjsip_dialog *_dlg cdef pjsip_route_hdr _route_header cdef pj_list _route_set cdef pj_timer_entry _timeout_timer cdef int _timeout_timer_active cdef pj_timer_entry _refresh_timer cdef int _refresh_timer_active cdef readonly object state cdef readonly EndpointAddress peer_address cdef readonly FrozenFromHeader from_header cdef readonly FrozenToHeader to_header cdef readonly FrozenContactHeader contact_header cdef readonly object event cdef readonly FrozenRouteHeader route_header cdef readonly FrozenCredentials credentials cdef readonly int refresh cdef readonly frozenlist extra_headers cdef readonly object body cdef readonly object content_type cdef readonly str call_id cdef pj_time_val _subscribe_timeout cdef int _want_end cdef int _term_code cdef object _term_reason cdef int _expires # private methods cdef PJSIPUA _get_ua(self) cdef int _cancel_timers(self, PJSIPUA ua, int cancel_timeout, int cancel_refresh) except -1 cdef int _send_subscribe(self, PJSIPUA ua, int expires, pj_time_val *timeout, object extra_headers, object content_type, object body) except -1 cdef int _cb_state(self, PJSIPUA ua, object state, int code, object reason, dict headers) except -1 cdef int _cb_got_response(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef int _cb_notify(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef int _cb_timeout_timer(self, PJSIPUA ua) cdef int _cb_refresh_timer(self, PJSIPUA ua) cdef class IncomingSubscription(object): # attributes cdef pjsip_evsub *_obj cdef pjsip_dialog *_dlg cdef PJSTR _content_type cdef PJSTR _content_subtype cdef PJSTR _content cdef pjsip_tx_data *_initial_response cdef pjsip_transaction *_initial_tsx cdef int _expires cdef readonly str state cdef readonly str event cdef readonly str call_id cdef readonly EndpointAddress peer_address # methods cdef int _set_state(self, str state) except -1 cdef PJSIPUA _get_ua(self, int raise_exception) cdef int init(self, PJSIPUA ua, pjsip_rx_data *rdata, str event) except -1 cdef int _send_initial_response(self, int code) except -1 cdef int _send_notify(self, str reason=*) except -1 cdef int _terminate(self, PJSIPUA ua, str reason, int do_cleanup) except -1 cdef int _cb_rx_refresh(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef int _cb_server_timeout(self, PJSIPUA ua) except -1 cdef int _cb_tsx(self, PJSIPUA ua, pjsip_event *event) except -1 cdef void _Subscription_cb_state(pjsip_evsub *sub, pjsip_event *event) with gil cdef void _Subscription_cb_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil cdef void _Subscription_cb_refresh(pjsip_evsub *sub) with gil cdef void _IncomingSubscription_cb_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil cdef void _IncomingSubscription_cb_server_timeout(pjsip_evsub *sub) with gil cdef void _IncomingSubscription_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil # core.sdp cdef class BaseSDPConnection(object): # attributes cdef pjmedia_sdp_conn _sdp_connection # private methods cdef pjmedia_sdp_conn* get_sdp_connection(self) cdef class SDPConnection(BaseSDPConnection): # attributes cdef str _address cdef str _net_type cdef str _address_type cdef class FrozenSDPConnection(BaseSDPConnection): # attributes cdef int initialized cdef readonly str address cdef readonly str net_type cdef readonly str address_type cdef class SDPAttributeList(list): pass cdef class FrozenSDPAttributeList(frozenlist): pass cdef class SDPBandwidthInfoList(list): pass cdef class FrozenSDPBandwidthInfoList(frozenlist): pass cdef class BaseSDPSession(object): # attributes cdef pjmedia_sdp_session _sdp_session # private methods cdef pjmedia_sdp_session* get_sdp_session(self) cdef class SDPSession(BaseSDPSession): # attributes cdef str _address cdef str _user cdef str _net_type cdef str _address_type cdef str _name cdef str _info cdef SDPConnection _connection cdef list _attributes cdef list _bandwidth_info cdef list _media # private methods cdef int _update(self) except -1 cdef class FrozenSDPSession(BaseSDPSession): # attributes cdef int initialized cdef readonly str address cdef readonly unsigned int id cdef readonly unsigned int version cdef readonly str user cdef readonly str net_type cdef readonly str address_type cdef readonly str name cdef readonly str info cdef readonly FrozenSDPConnection connection cdef readonly int start_time cdef readonly int stop_time cdef readonly FrozenSDPAttributeList attributes cdef readonly FrozenSDPBandwidthInfoList bandwidth_info cdef readonly frozenlist media cdef class BaseSDPMediaStream(object): # attributes cdef pjmedia_sdp_media _sdp_media # private methods cdef pjmedia_sdp_media* get_sdp_media(self) cdef class SDPMediaStream(BaseSDPMediaStream): # attributes cdef str _media cdef str _transport cdef list _formats cdef list _codec_list cdef str _info cdef SDPConnection _connection cdef SDPAttributeList _attributes cdef SDPBandwidthInfoList _bandwidth_info # private methods cdef int _update(self, SDPMediaStream media) except -1 cdef class FrozenSDPMediaStream(BaseSDPMediaStream): # attributes cdef int initialized cdef readonly str media cdef readonly int port cdef readonly str transport cdef readonly int port_count cdef readonly frozenlist formats cdef readonly frozenlist codec_list cdef readonly str info cdef readonly FrozenSDPConnection connection cdef readonly FrozenSDPAttributeList attributes cdef readonly FrozenSDPBandwidthInfoList bandwidth_info cdef class BaseSDPAttribute(object): # attributes cdef pjmedia_sdp_attr _sdp_attribute # private methods cdef pjmedia_sdp_attr* get_sdp_attribute(self) cdef class SDPAttribute(BaseSDPAttribute): # attributes cdef str _name cdef str _value cdef class FrozenSDPAttribute(BaseSDPAttribute): # attributes cdef int initialized cdef readonly str name cdef readonly str value cdef class BaseSDPBandwidthInfo(object): # attributes cdef pjmedia_sdp_bandw _sdp_bandwidth_info # private methods cdef pjmedia_sdp_bandw* get_sdp_bandwidth_info(self) cdef class SDPBandwidthInfo(BaseSDPBandwidthInfo): # attributes cdef str _modifier cdef int _value cdef class FrozenSDPBandwidthInfo(BaseSDPBandwidthInfo): # attributes cdef int initialized cdef readonly str modifier cdef readonly int value cdef SDPSession SDPSession_create(pjmedia_sdp_session_ptr_const pj_session) cdef FrozenSDPSession FrozenSDPSession_create(pjmedia_sdp_session_ptr_const pj_session) cdef SDPMediaStream SDPMediaStream_create(pjmedia_sdp_media *pj_media) cdef FrozenSDPMediaStream FrozenSDPMediaStream_create(pjmedia_sdp_media *pj_media) cdef SDPConnection SDPConnection_create(pjmedia_sdp_conn *pj_conn) cdef FrozenSDPConnection FrozenSDPConnection_create(pjmedia_sdp_conn *pj_conn) cdef SDPAttribute SDPAttribute_create(pjmedia_sdp_attr *pj_attr) cdef FrozenSDPAttribute FrozenSDPAttribute_create(pjmedia_sdp_attr *pj_attr) cdef SDPBandwidthInfo SDPBandwidthInfo_create(pjmedia_sdp_bandw *pj_bandw) cdef FrozenSDPBandwidthInfo FrozenSDPBandwidthInfo_create(pjmedia_sdp_bandw *pj_bandw) cdef class SDPNegotiator(object): # attributes cdef pjmedia_sdp_neg* _neg cdef pj_pool_t *_pool # core.invitation cdef class SDPPayloads: cdef readonly FrozenSDPSession proposed_local cdef readonly FrozenSDPSession proposed_remote cdef readonly FrozenSDPSession active_local cdef readonly FrozenSDPSession active_remote cdef class StateCallbackTimer(Timer): cdef object state cdef object sub_state cdef object rdata cdef object tdata cdef object originator cdef class SDPCallbackTimer(Timer): cdef int status cdef object active_local cdef object active_remote cdef class TransferStateCallbackTimer(Timer): cdef object state cdef object code cdef object reason cdef class TransferResponseCallbackTimer(Timer): cdef object method cdef object rdata cdef class TransferRequestCallbackTimer(Timer): cdef object rdata cdef class Invitation(object): # attributes cdef object __weakref__ cdef object weakref cdef int _sdp_neg_status cdef int _failed_response cdef pj_list _route_set cdef pj_mutex_t *_lock cdef pjsip_inv_session *_invite_session cdef pjsip_evsub *_transfer_usage cdef pjsip_role_e _transfer_usage_role cdef pjsip_dialog *_dialog cdef pjsip_route_hdr _route_header cdef pjsip_transaction *_reinvite_transaction cdef PJSTR _sipfrag_payload cdef Timer _timer cdef Timer _transfer_timeout_timer cdef Timer _transfer_refresh_timer cdef readonly str call_id cdef readonly str direction cdef readonly str remote_user_agent cdef readonly str state cdef readonly str sub_state cdef readonly str transport cdef readonly str transfer_state cdef readonly EndpointAddress peer_address cdef readonly FrozenCredentials credentials cdef readonly FrozenContactHeader local_contact_header cdef readonly FrozenContactHeader remote_contact_header cdef readonly FrozenFromHeader from_header cdef readonly FrozenToHeader to_header cdef readonly FrozenSIPURI request_uri cdef readonly FrozenRouteHeader route_header cdef readonly SDPPayloads sdp # private methods cdef int init_incoming(self, PJSIPUA ua, pjsip_rx_data *rdata, unsigned int inv_options) except -1 cdef int process_incoming_transfer(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef int process_incoming_options(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef PJSIPUA _check_ua(self) cdef int _do_dealloc(self) except -1 cdef int _update_contact_header(self, BaseContactHeader contact_header) except -1 cdef int _fail(self, PJSIPUA ua) except -1 cdef int _cb_state(self, StateCallbackTimer timer) except -1 cdef int _cb_sdp_done(self, SDPCallbackTimer timer) except -1 cdef int _cb_timer_disconnect(self, timer) except -1 cdef int _cb_postpoll_fail(self, timer) except -1 cdef int _start_incoming_transfer(self, timer) except -1 cdef int _terminate_transfer(self) except -1 cdef int _terminate_transfer_uac(self) except -1 cdef int _terminate_transfer_uas(self) except -1 cdef int _set_transfer_state(self, str state) except -1 cdef int _set_sipfrag_payload(self, int code, str reason) except -1 cdef int _send_notify(self) except -1 cdef int _transfer_cb_timeout_timer(self, timer) except -1 cdef int _transfer_cb_refresh_timer(self, timer) except -1 cdef int _transfer_cb_state(self, TransferStateCallbackTimer timer) except -1 cdef int _transfer_cb_response(self, TransferResponseCallbackTimer timer) except -1 cdef int _transfer_cb_notify(self, TransferRequestCallbackTimer timer) except -1 cdef int _transfer_cb_server_timeout(self, timer) except -1 cdef void _Invitation_cb_state(pjsip_inv_session *inv, pjsip_event *e) with gil cdef void _Invitation_cb_sdp_done(pjsip_inv_session *inv, int status) with gil cdef int _Invitation_cb_rx_reinvite(pjsip_inv_session *inv, pjmedia_sdp_session_ptr_const offer, pjsip_rx_data *rdata) with gil cdef void _Invitation_cb_tsx_state_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e) with gil cdef void _Invitation_cb_new(pjsip_inv_session *inv, pjsip_event *e) with gil cdef void _Invitation_transfer_cb_state(pjsip_evsub *sub, pjsip_event *event) with gil cdef void _Invitation_transfer_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil cdef void _Invitation_transfer_cb_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil cdef void _Invitation_transfer_cb_refresh(pjsip_evsub *sub) with gil cdef void _Invitation_transfer_in_cb_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil cdef void _Invitation_transfer_in_cb_server_timeout(pjsip_evsub *sub) with gil # core.mediatransport cdef class ICECandidate(object): # attributes cdef readonly str component cdef readonly str type cdef readonly str address cdef readonly int port cdef readonly int priority cdef readonly str rel_address cdef class ICECheck(object): cdef readonly ICECandidate local_candidate cdef readonly ICECandidate remote_candidate cdef readonly str state cdef readonly int nominated cdef class RTPTransport(object): # attributes cdef object __weakref__ cdef object weakref cdef int _af cdef pj_mutex_t *_lock cdef pj_pool_t *_pool cdef pjmedia_transport *_obj cdef pjmedia_transport *_wrapped_transport cdef ICECheck _rtp_valid_pair cdef str _encryption cdef readonly object ice_stun_address cdef readonly object ice_stun_port cdef readonly object state cdef readonly object use_ice # private methods cdef PJSIPUA _check_ua(self) cdef void _get_info(self, pjmedia_transport_info *info) cdef int _init_local_sdp(self, BaseSDPSession local_sdp, BaseSDPSession remote_sdp, int sdp_index) cdef int _ice_active(self) cdef class MediaCheckTimer(Timer): # attributes cdef int media_check_interval cdef class SDPInfo(object): # attributes cdef BaseSDPMediaStream _local_media cdef BaseSDPSession _local_sdp cdef BaseSDPSession _remote_sdp cdef public int index cdef class AudioTransport(object): # attributes cdef object __weakref__ cdef object weakref cdef int _is_offer cdef int _is_started cdef int _slot cdef int _volume cdef unsigned int _packets_received cdef unsigned int _vad cdef pj_mutex_t *_lock cdef pj_pool_t *_pool cdef pjmedia_stream *_obj cdef pjmedia_stream_info _stream_info cdef Timer _timer cdef readonly object direction cdef readonly AudioMixer mixer cdef readonly RTPTransport transport cdef SDPInfo _sdp_info # private methods cdef PJSIPUA _check_ua(self) cdef int _cb_check_rtp(self, MediaCheckTimer timer) except -1 cdef class VideoTransport(object): # attributes cdef object __weakref__ cdef object weakref cdef int _is_offer cdef int _is_started cdef unsigned int _packets_received cdef pj_mutex_t *_lock cdef pj_pool_t *_pool cdef pjmedia_vid_stream *_obj cdef pjmedia_vid_stream_info _stream_info cdef Timer _timer cdef readonly object direction cdef readonly RTPTransport transport cdef SDPInfo _sdp_info cdef readonly LocalVideoStream local_video cdef readonly RemoteVideoStream remote_video # private methods cdef PJSIPUA _check_ua(self) cdef int _cb_check_rtp(self, MediaCheckTimer timer) except -1 cdef void _RTPTransport_cb_ice_complete(pjmedia_transport *tp, pj_ice_strans_op op, int status) with gil cdef void _RTPTransport_cb_ice_state(pjmedia_transport *tp, pj_ice_strans_state prev, pj_ice_strans_state curr) with gil cdef void _RTPTransport_cb_ice_stop(pjmedia_transport *tp, char *reason, int err) with gil cdef void _RTPTransport_cb_zrtp_secure_on(pjmedia_transport *tp, char* cipher) with gil cdef void _RTPTransport_cb_zrtp_secure_off(pjmedia_transport *tp) with gil cdef void _RTPTransport_cb_zrtp_show_sas(pjmedia_transport *tp, char* sas, int verified) with gil cdef void _RTPTransport_cb_zrtp_confirm_goclear(pjmedia_transport *tp) with gil cdef void _RTPTransport_cb_zrtp_show_message(pjmedia_transport *tp, int severity, int subCode) with gil cdef void _RTPTransport_cb_zrtp_negotiation_failed(pjmedia_transport *tp, int severity, int subCode) with gil cdef void _RTPTransport_cb_zrtp_not_supported_by_other(pjmedia_transport *tp) with gil cdef void _RTPTransport_cb_zrtp_ask_enrollment(pjmedia_transport *tp, int info) with gil cdef void _RTPTransport_cb_zrtp_inform_enrollment(pjmedia_transport *tp, int info) with gil cdef void _AudioTransport_cb_dtmf(pjmedia_stream *stream, void *user_data, int digit) with gil cdef ICECandidate ICECandidate_create(pj_ice_sess_cand *cand) cdef ICECheck ICECheck_create(pj_ice_sess_check *check) cdef str _ice_state_to_str(int state) cdef dict _extract_ice_session_data(pj_ice_sess *ice_sess) cdef object _extract_rtp_transport(pjmedia_transport *tp) cdef dict _pj_math_stat_to_dict(pj_math_stat *stat) cdef dict _pjmedia_rtcp_stream_stat_to_dict(pjmedia_rtcp_stream_stat *stream_stat) ================================================ FILE: sipsimple/core/_core.pyx ================================================ include "_core.error.pxi" include "_core.lib.pxi" include "_core.sound.pxi" include "_core.video.pxi" include "_core.util.pxi" include "_core.ua.pxi" include "_core.event.pxi" include "_core.request.pxi" include "_core.helper.pxi" include "_core.headers.pxi" include "_core.subscription.pxi" include "_core.invitation.pxi" include "_core.referral.pxi" include "_core.sdp.pxi" include "_core.mediatransport.pxi" # constants PJ_VERSION = pj_get_version() PJ_SVN_REVISION = int(PJ_SVN_REV) CORE_REVISION = 181 # exports __all__ = ["PJ_VERSION", "PJ_SVN_REVISION", "CORE_REVISION", "SIPCoreError", "PJSIPError", "PJSIPTLSError", "SIPCoreInvalidStateError", "AudioMixer", "ToneGenerator", "RecordingWaveFile", "WaveFile", "MixerPort", "VideoCamera", "FrameBufferVideoRenderer", "sip_status_messages", "BaseCredentials", "Credentials", "FrozenCredentials", "BaseSIPURI", "SIPURI", "FrozenSIPURI", "BaseHeader", "Header", "FrozenHeader", "BaseContactHeader", "ContactHeader", "FrozenContactHeader", "BaseContentTypeHeader", "ContentType", "ContentTypeHeader", "FrozenContentTypeHeader", "BaseIdentityHeader", "IdentityHeader", "FrozenIdentityHeader", "FromHeader", "FrozenFromHeader", "ToHeader", "FrozenToHeader", "RouteHeader", "FrozenRouteHeader", "RecordRouteHeader", "FrozenRecordRouteHeader", "BaseRetryAfterHeader", "RetryAfterHeader", "FrozenRetryAfterHeader", "BaseViaHeader", "ViaHeader", "FrozenViaHeader", "BaseWarningHeader", "WarningHeader", "FrozenWarningHeader", "BaseEventHeader", "EventHeader", "FrozenEventHeader", "BaseSubscriptionStateHeader", "SubscriptionStateHeader", "FrozenSubscriptionStateHeader", "BaseReasonHeader", "ReasonHeader", "FrozenReasonHeader", "BaseReferToHeader", "ReferToHeader", "FrozenReferToHeader", "BaseSubjectHeader", "SubjectHeader", "FrozenSubjectHeader", "BaseReplacesHeader", "ReplacesHeader", "FrozenReplacesHeader", "Request", "Referral", "sipfrag_re", "Subscription", "Invitation", "DialogID", "SDPSession", "FrozenSDPSession", "SDPMediaStream", "FrozenSDPMediaStream", "SDPConnection", "FrozenSDPConnection", "SDPAttribute", "FrozenSDPAttribute", "SDPNegotiator", "RTPTransport", "AudioTransport", "VideoTransport"] ================================================ FILE: sipsimple/core/_core.referral.pxi ================================================ import re cdef class Referral: expire_warning_time = 30 def __cinit__(self, *args, **kwargs): self.state = "NULL" pj_timer_entry_init(&self._timeout_timer, 0, self, _Referral_cb_timer) self._timeout_timer_active = 0 pj_timer_entry_init(&self._refresh_timer, 1, self, _Referral_cb_timer) self._refresh_timer_active = 0 self.extra_headers = frozenlist() self.peer_address = None self._create_subscription = 1 self.local_contact_header = None self.remote_contact_header = None def __init__(self, SIPURI request_uri not None, FromHeader from_header not None, ToHeader to_header not None, ReferToHeader refer_to_header not None, ContactHeader contact_header not None, RouteHeader route_header not None, Credentials credentials=None): global _refer_cb global _refer_event cdef PJSTR from_header_str cdef PJSTR to_header_str cdef PJSTR contact_str cdef PJSTR request_uri_str cdef pjsip_cred_info *cred_info cdef PJSIPUA ua = _get_ua() cdef int status if self._obj != NULL or self.state != "NULL": raise SIPCoreError("Referral.__init__() was already called") self.local_contact_header = FrozenContactHeader.new(contact_header) self.route_header = FrozenRouteHeader.new(route_header) self.route_header.uri.parameters.dict["lr"] = None # always send lr parameter in Route header self.route_header.uri.parameters.dict["hide"] = None # always hide Route header if credentials is not None: self.credentials = FrozenCredentials.new(credentials) from_header_parameters = from_header.parameters.copy() from_header_parameters.pop("tag", None) from_header.parameters = {} from_header_str = PJSTR(from_header.body) to_header_parameters = to_header.parameters.copy() to_header_parameters.pop("tag", None) to_header.parameters = {} to_header_str = PJSTR(to_header.body) contact_str = PJSTR(str(contact_header.body)) request_uri_str = PJSTR(str(request_uri)) with nogil: status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from_header_str.pj_str, &contact_str.pj_str, &to_header_str.pj_str, &request_uri_str.pj_str, &self._dlg) if status != 0: raise PJSIPError("Could not create dialog for REFER", status) # Increment dialog session count so that it's never destroyed by PJSIP with nogil: status = pjsip_dlg_inc_session(self._dlg, &ua._module) if contact_header.expires is not None: self._dlg.local.contact.expires = contact_header.expires if contact_header.q is not None: self._dlg.local.contact.q1000 = int(contact_header.q*1000) contact_parameters = contact_header.parameters.copy() contact_parameters.pop("q", None) contact_parameters.pop("expires", None) _dict_to_pjsip_param(contact_parameters, &self._dlg.local.contact.other_param, self._dlg.pool) _dict_to_pjsip_param(from_header_parameters, &self._dlg.local.info.other_param, self._dlg.pool) _dict_to_pjsip_param(to_header_parameters, &self._dlg.remote.info.other_param, self._dlg.pool) self.from_header = FrozenFromHeader_create(self._dlg.local.info) self.to_header = FrozenToHeader.new(to_header) self.refer_to_header = FrozenReferToHeader.new(refer_to_header) with nogil: status = pjsip_evsub_create_uac(self._dlg, &_refer_cb, &_refer_event.pj_str, PJSIP_EVSUB_NO_EVENT_ID, &self._obj) if status != 0: raise PJSIPError("Could not create REFER", status) pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, self) _BaseRouteHeader_to_pjsip_route_hdr(self.route_header, &self._route_header, self._dlg.pool) pj_list_init( &self._route_set) pj_list_insert_after( &self._route_set, &self._route_header) with nogil: status = pjsip_dlg_set_route_set(self._dlg, &self._route_set) if status != 0: raise PJSIPError("Could not set route on REFER", status) if self.credentials is not None: cred_info = self.credentials.get_cred_info() with nogil: status = pjsip_auth_clt_set_credentials(&self._dlg.auth_sess, 1, cred_info) if status != 0: raise PJSIPError("Could not set credentials for REFER", status) def __dealloc__(self): cdef PJSIPUA ua = self._get_ua() if ua is not None: self._cancel_timers(ua, 1, 1) if self._obj != NULL: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) with nogil: pjsip_evsub_terminate(self._obj, 0) self._obj = NULL if self._dlg != NULL: with nogil: pjsip_dlg_dec_session(self._dlg, &ua._module) self._dlg = NULL def send_refer(self, int create_subscription=1, list extra_headers not None=list(), object timeout=None): cdef PJSIPUA ua = self._get_ua() with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "NULL": raise SIPCoreError('This method may only be called in the "NULL" state') if timeout is not None: if timeout <= 0: raise ValueError("Timeout value cannot be negative") self._request_timeout.sec = int(timeout) self._request_timeout.msec = (timeout * 1000) % 1000 else: self._request_timeout.sec = 0 self._request_timeout.msec = 0 if extra_headers is not None: self.extra_headers = frozenlist([header.frozen_type.new(header) for header in extra_headers]) self._create_subscription = create_subscription self._send_refer(ua, &self._request_timeout, self.refer_to_header, self.extra_headers) _add_event("SIPReferralWillStart", dict(obj=self)) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def refresh(self, ContactHeader contact_header=None, list extra_headers not None=list(), object timeout=None): cdef PJSIPUA ua = self._get_ua() with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state not in ("ACCEPTED", "ACTIVE", "PENDING"): raise SIPCoreError('This method may only be called in the "ACCEPTED", "ACTIVE" or "PENDING" states') if timeout is not None: if timeout <= 0: raise ValueError("Timeout value cannot be negative") self._request_timeout.sec = int(timeout) self._request_timeout.msec = (timeout * 1000) % 1000 else: self._request_timeout.sec = 0 self._request_timeout.msec = 0 if contact_header is not None: self._update_contact_header(contact_header) if extra_headers is not None: self.extra_headers = frozenlist([header.frozen_type.new(header) for header in extra_headers]) self._send_subscribe(ua, 600, &self._request_timeout, self.extra_headers) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def end(self, object timeout=None): cdef pj_time_val end_timeout cdef PJSIPUA ua = self._get_ua() with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state == "TERMINATED": return if self.state == "NULL": raise SIPCoreError('This method may not be called in the "NULL" state') if timeout is not None: if timeout <= 0: raise ValueError("Timeout value cannot be negative") end_timeout.sec = int(timeout) end_timeout.msec = (timeout * 1000) % 1000 else: end_timeout.sec = 0 end_timeout.msec = 0 self._want_end = 1 self._cancel_timers(ua, 1, 1) _add_event("SIPReferralWillEnd", dict(obj=self)) try: self._send_subscribe(ua, 0, &end_timeout, frozenlist([])) except PJSIPError, e: self._term_reason = e.args[0] if self._obj != NULL: pjsip_evsub_terminate(self._obj, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) cdef PJSIPUA _get_ua(self): cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: self._obj = NULL self._timeout_timer_active = 0 self._refresh_timer_active = 0 self.state = "TERMINATED" return None else: return ua cdef int _update_contact_header(self, BaseContactHeader contact_header) except -1: # The PJSIP functions called here don't do much, so there is no need to call them # without the gil. cdef pj_str_t contact_str_pj cdef pjsip_uri *contact contact_str = str(contact_header.uri) if contact_header.display_name: contact_str = "%s <%s>" % (contact_header.display_name.encode('utf-8'), contact_str) pj_strdup2_with_null(self._dlg.pool, &contact_str_pj, contact_str) contact = pjsip_parse_uri(self._dlg.pool, contact_str_pj.ptr, contact_str_pj.slen, PJSIP_PARSE_URI_AS_NAMEADDR) if contact == NULL: raise SIPCoreError("Not a valid Contact header: %s" % contact_str) self._dlg.local.contact = pjsip_contact_hdr_create(self._dlg.pool) self._dlg.local.contact.uri = contact if contact_header.expires is not None: self._dlg.local.contact.expires = contact_header.expires if contact_header.q is not None: self._dlg.local.contact.q1000 = int(contact_header.q*1000) parameters = contact_header.parameters.copy() parameters.pop("q", None) parameters.pop("expires", None) _dict_to_pjsip_param(parameters, &self._dlg.local.contact.other_param, self._dlg.pool) self.local_contact_header = FrozenContactHeader.new(contact_header) return 0 cdef int _cancel_timers(self, PJSIPUA ua, int cancel_timeout, int cancel_refresh) except -1: if cancel_timeout and self._timeout_timer_active: pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timeout_timer) self._timeout_timer_active = 0 if cancel_refresh and self._refresh_timer_active: pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._refresh_timer) self._refresh_timer_active = 0 cdef int _send_refer(self, PJSIPUA ua, pj_time_val *timeout, FrozenReferToHeader refer_to_header, frozenlist extra_headers) except -1: global _refer_method cdef pjsip_method refer_method cdef pjsip_tx_data *tdata cdef int status pjsip_method_init_np(&refer_method, &_refer_method.pj_str) with nogil: status = pjsip_evsub_initiate(self._obj, &refer_method, -1, &tdata) if status != 0: raise PJSIPError("Could not create REFER message", status) _add_headers_to_tdata(tdata, [refer_to_header, Header('Referred-By', str(self.from_header.uri))]) _add_headers_to_tdata(tdata, extra_headers) if not self._create_subscription: _add_headers_to_tdata(tdata, [Header('Refer-Sub', 'false')]) # We can't remove the Event header or PJSIP will fail to match responses to this request _remove_headers_from_tdata(tdata, ["Expires"]) with nogil: status = pjsip_evsub_send_request(self._obj, tdata) if status != 0: raise PJSIPError("Could not send REFER message", status) if timeout.sec or timeout.msec: status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timeout_timer, timeout) if status == 0: self._timeout_timer_active = 1 cdef int _send_subscribe(self, PJSIPUA ua, int expires, pj_time_val *timeout, frozenlist extra_headers) except -1: cdef pjsip_tx_data *tdata cdef int status with nogil: status = pjsip_evsub_initiate(self._obj, NULL, expires, &tdata) if status != 0: raise PJSIPError("Could not create SUBSCRIBE message", status) _add_headers_to_tdata(tdata, extra_headers) with nogil: status = pjsip_evsub_send_request(self._obj, tdata) if status != 0: raise PJSIPError("Could not send SUBSCRIBE message", status) self._cancel_timers(ua, 1, 0) if timeout.sec or timeout.msec: status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timeout_timer, timeout) if status == 0: self._timeout_timer_active = 1 cdef int _cb_state(self, PJSIPUA ua, object state, int code, str reason) except -1: # PJSIP holds the dialog lock when this callback is entered cdef object prev_state = self.state cdef int status self.state = state if state == "ACCEPTED" and prev_state == "SENT": _add_event("SIPReferralDidStart", dict(obj=self)) if not self._create_subscription: # Terminate the subscription self._want_end = 1 _add_event("SIPReferralWillEnd", dict(obj=self)) with nogil: pjsip_evsub_terminate(self._obj, 1) elif state == "TERMINATED": pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) self._cancel_timers(ua, 1, 1) self._obj = NULL if self._want_end: _add_event("SIPReferralDidEnd", dict(obj=self)) else: if self._term_reason is not None: _add_event("SIPReferralDidFail", dict(obj=self, code=self._term_code, reason=self._term_reason)) elif code/100 == 2: _add_event("SIPReferralDidEnd", dict(obj=self)) else: _add_event("SIPReferralDidFail", dict(obj=self, code=code, reason=reason)) if prev_state != state: _add_event("SIPReferralChangedState", dict(obj=self, prev_state=prev_state, state=state)) cdef int _cb_got_response(self, PJSIPUA ua, pjsip_rx_data *rdata, str method) except -1: # PJSIP holds the dialog lock when this callback is entered global _refer_sub_hdr_name cdef int expires cdef int status cdef dict event_dict = dict() cdef pj_time_val refresh cdef pjsip_generic_int_hdr *expires_hdr cdef pjsip_generic_string_hdr *refer_sub_header self.to_header = FrozenToHeader_create(rdata.msg_info.to_hdr) if self.state != "TERMINATED" and not self._want_end: self._cancel_timers(ua, 1, 0) if method == "REFER": refer_sub_header = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_refer_sub_hdr_name.pj_str, NULL); if not self._create_subscription: if not (refer_sub_header != NULL and _pj_str_to_str(refer_sub_header.hvalue) == "false"): self._create_subscription = 1 elif method == "SUBSCRIBE": # For the REFER method the expires value will be taken from the NOTIFY Subscription-State header expires_hdr = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_EXPIRES, NULL) if expires_hdr != NULL and not self._refresh_timer_active: expires = expires_hdr.ivalue refresh.sec = max(1, expires - self.expire_warning_time, expires/2) refresh.msec = 0 status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._refresh_timer, &refresh) if status == 0: self._refresh_timer_active = 1 if self.state != "TERMINATED": _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: pass cdef int _cb_notify(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: # PJSIP holds the dialog lock when this callback is entered global _subscription_state_hdr_name cdef pjsip_sub_state_hdr *sub_state_hdr cdef pj_time_val refresh cdef int expires cdef dict event_dict = dict() cdef dict notify_dict = dict(obj=self) sub_state_hdr = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_subscription_state_hdr_name.pj_str, NULL) if self.state != "TERMINATED" and sub_state_hdr != NULL and sub_state_hdr.expires_param > 0 and not self._refresh_timer_active: expires = sub_state_hdr.expires_param refresh.sec = max(1, expires - self.expire_warning_time, expires/2) refresh.msec = 0 status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._refresh_timer, &refresh) if status == 0: self._refresh_timer_active = 1 _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) if self.state != "TERMINATED": try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: pass notify_dict["request_uri"] = event_dict["request_uri"] notify_dict["from_header"] = event_dict["headers"].get("From", None) notify_dict["to_header"] = event_dict["headers"].get("To", None) notify_dict["headers"] = event_dict["headers"] notify_dict["body"] = event_dict["body"] content_type = notify_dict["headers"].get("Content-Type", None) notify_dict["content_type"] = content_type.content_type if content_type else None event = notify_dict["headers"].get("Event", None) notify_dict["event"] = event.event if event else None _add_event("SIPReferralGotNotify", notify_dict) cdef int _cb_timeout_timer(self, PJSIPUA ua): # Timer callback, dialog lock is not held by PJSIP global sip_status_messages with nogil: pjsip_dlg_inc_lock(self._dlg) try: self._term_code = PJSIP_SC_TSX_TIMEOUT self._term_reason = sip_status_messages[PJSIP_SC_TSX_TIMEOUT] if self._obj != NULL: with nogil: pjsip_evsub_terminate(self._obj, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) cdef int _cb_refresh_timer(self, PJSIPUA ua): # Timer callback, dialog lock is not held by PJSIP with nogil: pjsip_dlg_inc_lock(self._dlg) try: self._send_subscribe(ua, 600, &self._request_timeout, self.extra_headers) except PJSIPError, e: self._term_reason = e.args[0] if self._obj != NULL: with nogil: pjsip_evsub_terminate(self._obj, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) cdef class IncomingReferral: def __cinit__(self): self.state = None self.peer_address = None self._create_subscription = 1 self.local_contact_header = None self.remote_contact_header = None def __dealloc__(self): cdef PJSIPUA ua = self._get_ua(0) self._initial_response = NULL self._initial_tsx = NULL if self._obj != NULL: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) with nogil: pjsip_evsub_terminate(self._obj, 0) self._obj = NULL if self._dlg != NULL and ua is not None: with nogil: pjsip_dlg_dec_session(self._dlg, &ua._module) self._dlg = NULL cdef int init(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: global _incoming_refer_subs_cb global _event_hdr_name global _refer_event global _refer_to_hdr_name global _refer_sub_hdr_name cdef int status cdef str transport cdef FrozenSIPURI request_uri cdef FrozenContactHeader contact_header cdef PJSTR contact_str cdef dict event_dict cdef pjsip_generic_string_hdr *refer_to_header cdef pjsip_generic_string_hdr *refer_sub_header cdef pjsip_tpselector tp_sel cdef pjsip_event_hdr *event_header refer_to_header = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_refer_to_hdr_name.pj_str, NULL); if refer_to_header == NULL: with nogil: status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 400, NULL, &self._initial_response) if status != 0: raise PJSIPError("Could not create response", status) with nogil: status = pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, self._initial_response, NULL, NULL) if status != 0: with nogil: pjsip_tx_data_dec_ref(self._initial_response) raise PJSIPError("Could not send response", status) return 0 # If there is a Ref-Sub header and it contains 'false', don't establish a subscription refer_sub_header = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_refer_sub_hdr_name.pj_str, NULL); if refer_sub_header != NULL and _pj_str_to_str(refer_sub_header.hvalue) == "false": self._create_subscription = 0 self._set_state("incoming") self.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) event_dict = dict(obj=self, prev_state=self.state, state="incoming") _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: # Contact header is required with nogil: status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 400, NULL, &self._initial_response) if status != 0: raise PJSIPError("Could not create response", status) with nogil: status = pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, self._initial_response, NULL, NULL) if status != 0: with nogil: pjsip_tx_data_dec_ref(self._initial_response) raise PJSIPError("Could not send response", status) return 0 event_dict["refer_to"] = event_dict["headers"].get("Refer-To") transport = rdata.tp_info.transport.type_name.lower() request_uri = event_dict["request_uri"] if _is_valid_ip(pj_AF_INET(), request_uri.host): self.local_contact_header = FrozenContactHeader(request_uri) else: self.local_contact_header = FrozenContactHeader(FrozenSIPURI(host=_pj_str_to_str(rdata.tp_info.transport.local_name.host), user=request_uri.user, port=rdata.tp_info.transport.local_name.port, parameters=(frozendict(transport=transport) if transport != "udp" else frozendict()))) contact_str = PJSTR(self.local_contact_header.body) with nogil: status = pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(), rdata, &contact_str.pj_str, &self._dlg) if status != 0: with nogil: status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 400, NULL, &self._initial_response) if status != 0: raise PJSIPError("Could not create response", status) with nogil: status = pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, self._initial_response, NULL, NULL) if status != 0: with nogil: pjsip_tx_data_dec_ref(self._initial_response) raise PJSIPError("Could not send response", status) return 0 # Increment dialog session count so that it's never destroyed by PJSIP with nogil: status = pjsip_dlg_inc_session(self._dlg, &ua._module) if status != 0: pjsip_dlg_dec_lock(self._dlg) raise PJSIPError("Could not increment dialog session count", status) # PJSIP event framework needs an Event header, even if it's not needed for REFER, so we insert a fake one event_header = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_event_hdr_name.pj_str, NULL) if event_header == NULL: event_header = pjsip_event_hdr_create(rdata.tp_info.pool) event_header.event_type = _refer_event.pj_str pjsip_msg_add_hdr(rdata.msg_info.msg, event_header) self._initial_tsx = pjsip_rdata_get_tsx(rdata) with nogil: status = pjsip_evsub_create_uas(self._dlg, &_incoming_refer_subs_cb, rdata, 0, &self._obj) pjsip_dlg_dec_lock(self._dlg) if status != 0: with nogil: pjsip_tsx_terminate(self._initial_tsx, 500) self._initial_tsx = NULL self._dlg = NULL raise PJSIPError("Could not create incoming REFER session", status) pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, self) with nogil: status = pjsip_dlg_create_response(self._dlg, rdata, 500, NULL, &self._initial_response) if status != 0: with nogil: pjsip_tsx_terminate(self._initial_tsx, 500) self._initial_tsx = NULL raise PJSIPError("Could not create response for incoming REFER", status) _add_event("SIPIncomingReferralGotRefer", event_dict) return 0 def accept(self, int code=202, int duration=180): cdef PJSIPUA ua = self._get_ua(1) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "incoming": raise SIPCoreInvalidStateError('Can only accept an incoming REFER in the "incoming" state, '+ 'object is currently in the "%s" state' % self.state) pjsip_evsub_update_expires(self._obj, duration) self._send_initial_response(code) self._set_state("active") if not self._create_subscription: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) with nogil: pjsip_evsub_terminate(self._obj, 0) self._obj = NULL self._set_state("terminated") _add_event("SIPIncomingReferralDidEnd", dict(obj=self)) else: self._set_content(100, "Trying") self._send_notify() finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def reject(self, int code): cdef PJSIPUA ua = self._get_ua(1) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "incoming": raise SIPCoreInvalidStateError('Can only reject an incoming REFER in the "incoming" state, '+ 'object is currently in the "%s" state' % self.state) if not (300 <= code < 700): raise ValueError("Invalid negative SIP response code: %d" % code) self._send_initial_response(code) pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) with nogil: pjsip_evsub_terminate(self._obj, 0) self._obj = NULL self._set_state("terminated") _add_event("SIPIncomingReferralDidEnd", dict(obj=self)) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def send_notify(self, int code, str status=None): cdef PJSIPUA ua = self._get_ua(1) cdef str content with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "active": raise SIPCoreInvalidStateError('Can only send NOTIFY for a REFER session in the "active" state, ' 'object is currently in the "%s" state' % self.state) self._set_content(code, status) self._send_notify() finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def end(self, int code, str status=None): cdef PJSIPUA ua = self._get_ua(0) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state == "terminated": return if self.state not in ("pending", "active"): raise SIPCoreInvalidStateError('Can only end an incoming REFER session in the "pending" or '+ '"active" state, object is currently in the "%s" state' % self.state) self._set_content(code, status) self._terminate(ua, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) cdef PJSIPUA _get_ua(self, int raise_exception): cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: self._obj = NULL self._set_state("terminated") if raise_exception: raise else: return None else: return ua cdef int _set_content(self, int code, str reason) except -1: cdef str content if reason is None: try: reason = sip_status_messages[code] except IndexError: reason = "Unknown" content = "SIP/2.0 %d %s\r\n" % (code, reason) self._content = PJSTR(content) cdef int _set_state(self, str state) except -1: cdef str prev_state prev_state = self.state self.state = state if prev_state != state and prev_state is not None: _add_event("SIPIncomingReferralChangedState", dict(obj=self, prev_state=prev_state, state=state)) cdef int _send_initial_response(self, int code) except -1: cdef int status with nogil: status = pjsip_dlg_modify_response(self._dlg, self._initial_response, code, NULL) if status != 0: raise PJSIPError("Could not modify response", status) # pjsip_dlg_modify_response() increases ref count unncessarily with nogil: pjsip_tx_data_dec_ref(self._initial_response) if not self._create_subscription: _add_headers_to_tdata(self._initial_response, [Header('Refer-Sub', 'false')]) with nogil: status = pjsip_dlg_send_response(self._dlg, self._initial_tsx, self._initial_response) if status != 0: raise PJSIPError("Could not send response", status) self._initial_response = NULL self._initial_tsx = NULL cdef int _send_notify(self) except -1: cdef pjsip_evsub_state state cdef pj_str_t *reason_p cdef pjsip_tx_data *tdata cdef int status cdef dict _sipfrag_version = dict(version="2.0") cdef PJSTR _content_type = PJSTR("message") cdef PJSTR _content_subtype = PJSTR("sipfrag") cdef PJSTR reason = PJSTR("noresource") reason_p = NULL if self.state == "pending": state = PJSIP_EVSUB_STATE_PENDING elif self.state == "active": state = PJSIP_EVSUB_STATE_ACTIVE else: state = PJSIP_EVSUB_STATE_TERMINATED reason_p = &reason.pj_str with nogil: status = pjsip_evsub_notify(self._obj, state, NULL, reason_p, &tdata) if status != 0: raise PJSIPError("Could not create NOTIFY request", status) if self.state in ("active", "terminated"): tdata.msg.body = pjsip_msg_body_create(tdata.pool, &_content_type.pj_str, &_content_subtype.pj_str, &self._content.pj_str) _dict_to_pjsip_param(_sipfrag_version, &tdata.msg.body.content_type.param, tdata.pool) with nogil: status = pjsip_evsub_send_request(self._obj, tdata) if status != 0: raise PJSIPError("Could not send NOTIFY request", status) event_dict = dict(obj=self) _pjsip_msg_to_dict(tdata.msg, event_dict) _add_event("SIPIncomingReferralSentNotify", event_dict) return 0 cdef int _terminate(self, PJSIPUA ua, int do_cleanup) except -1: cdef int status self._set_state("terminated") self._send_notify() if do_cleanup: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) self._obj = NULL _add_event("SIPIncomingReferralDidEnd", dict(obj=self)) cdef int _cb_rx_refresh(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: # PJSIP holds the dialog lock when this callback is entered cdef int status cdef pjsip_expires_hdr *expires_header cdef int expires cdef dict event_dict event_dict = dict(obj=self) _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) expires_header = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_EXPIRES, NULL) if expires_header == NULL: self._expires_time.sec = 600 self._expires_time.msec = 0 else: if expires_header.ivalue == 0: _add_event("SIPIncomingReferralGotUnsubscribe", event_dict) # cleanup will be done by _cb_tsx self._terminate(ua, 0) return 200 else: expires = min(expires_header.ivalue, 600) self._expires_time.sec = expires self._expires_time.msec = 0 _add_event("SIPIncomingReferralGotRefreshingSubscribe", event_dict) # Last NOTIFY will be resent self._send_notify() if self.state == "active": return 200 else: return 202 cdef int _cb_server_timeout(self, PJSIPUA ua) except -1: # PJSIP holds the dialog lock when this callback is entered self._terminate(ua, 1) cdef int _cb_tsx(self, PJSIPUA ua, pjsip_event *event) except -1: # PJSIP holds the dialog lock when this callback is entered cdef pjsip_rx_data *rdata cdef dict event_dict cdef int status_code if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "NOTIFY" and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED): event_dict = dict(obj=self) rdata = event.body.tsx_state.src.rdata if rdata != NULL: if self.peer_address is None: self.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: self.peer_address.ip = rdata.pkt_info.src_name self.peer_address.port = rdata.pkt_info.src_port status_code = event.body.tsx_state.tsx.status_code if event.body.tsx_state.type==PJSIP_EVENT_RX_MSG and status_code/100==2: _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: pass _add_event("SIPIncomingReferralNotifyDidSucceed", event_dict) else: if event.body.tsx_state.type == PJSIP_EVENT_RX_MSG: _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) else: event_dict["code"] = status_code event_dict["reason"] = _pj_str_to_str(event.body.tsx_state.tsx.status_text) _add_event("SIPIncomingReferralNotifyDidFail", event_dict) if status_code in (408, 481) or status_code/100==7: # PJSIP will terminate the subscription and the dialog will be destroyed self._terminate(ua, 1) elif (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "NOTIFY" and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_TERMINATED): event_dict = dict(obj=self) status_code = event.body.tsx_state.tsx.status_code if status_code == 408: # Local timeout, PJSIP will terminate the subscription and the dialog will be destroyed event_dict["code"] = status_code event_dict["reason"] = _pj_str_to_str(event.body.tsx_state.tsx.status_text) _add_event("SIPIncomingReferralNotifyDidFail", event_dict) self._terminate(ua, 1) elif (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAS and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "REFER" and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED and event.body.tsx_state.type == PJSIP_EVENT_TX_MSG): event_dict = dict(obj=self) _pjsip_msg_to_dict(event.body.tsx_state.src.tdata.msg, event_dict) _add_event("SIPIncomingReferralAnsweredRefer", event_dict) if self.state == "terminated" and self._obj != NULL: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) self._obj = NULL cdef void _Referral_cb_state(pjsip_evsub *sub, pjsip_event *event) with gil: cdef void *referral_void cdef Referral referral cdef object state cdef int code = 0 cdef dict event_dict = dict() cdef str reason = None cdef pjsip_rx_data *rdata = NULL cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: return referral = referral_void state = pjsip_evsub_get_state_name(sub) if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and (event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED or event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_TERMINATED)): if state == "TERMINATED": if event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC: code = event.body.tsx_state.tsx.status_code reason = _pj_str_to_str(event.body.tsx_state.tsx.status_text) else: reason = "Referral has expired" if event.body.tsx_state.type == PJSIP_EVENT_RX_MSG and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "NOTIFY": # Extract code and reason from the sipfrag payload rdata = event.body.tsx_state.src.rdata if rdata != NULL: _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) if event_dict.get('body', None) is not None: match = sipfrag_re.match(event_dict['body']) if match: code = int(match.group('code')) reason = match.group('reason') referral._cb_state(ua, state, code, reason) except: ua._handle_exception(1) cdef void _Referral_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil: cdef void *referral_void cdef Referral referral cdef pjsip_rx_data *rdata cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: return referral = referral_void if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.type == PJSIP_EVENT_RX_MSG and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED and _pj_str_to_str(event.body.tsx_state.tsx.method.name) in ("REFER", "SUBSCRIBE") and event.body.tsx_state.tsx.status_code/100 == 2): rdata = event.body.tsx_state.src.rdata if rdata != NULL: if referral.peer_address is None: referral.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: referral.peer_address.ip = rdata.pkt_info.src_name referral.peer_address.port = rdata.pkt_info.src_port referral._cb_got_response(ua, rdata, _pj_str_to_str(event.body.tsx_state.tsx.method.name)) except: ua._handle_exception(1) cdef void _Referral_cb_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil: cdef void *referral_void cdef Referral referral cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: return referral = referral_void if rdata != NULL: if referral.peer_address is None: referral.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: referral.peer_address.ip = rdata.pkt_info.src_name referral.peer_address.port = rdata.pkt_info.src_port referral._cb_notify(ua, rdata) except: ua._handle_exception(1) cdef void _Referral_cb_refresh(pjsip_evsub *sub) with gil: # We want to handle the refresh timer oursevles, ignore the PJSIP provided timer pass cdef void _Referral_cb_timer(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) with gil: cdef Referral referral cdef PJSIPUA ua try: ua = _get_ua() except: return try: if entry.user_data != NULL: referral = entry.user_data if entry.id == 1: referral._refresh_timer_active = 0 referral._cb_refresh_timer(ua) else: referral._timeout_timer_active = 0 referral._cb_timeout_timer(ua) except: ua._handle_exception(1) cdef void _IncomingReferral_cb_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil: cdef void *referral_void cdef IncomingReferral referral cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: p_st_code[0] = 481 return referral = referral_void if rdata != NULL: if referral.peer_address is None: referral.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: referral.peer_address.ip = rdata.pkt_info.src_name referral.peer_address.port = rdata.pkt_info.src_port p_st_code[0] = referral._cb_rx_refresh(ua, rdata) except: ua._handle_exception(1) cdef void _IncomingReferral_cb_server_timeout(pjsip_evsub *sub) with gil: cdef void *referral_void cdef IncomingReferral referral cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: return referral = referral_void referral._cb_server_timeout(ua) except: ua._handle_exception(1) cdef void _IncomingReferral_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil: cdef void *referral_void cdef IncomingReferral referral cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: return referral = referral_void referral._cb_tsx(ua, event) except: ua._handle_exception(1) # Globals # cdef pjsip_evsub_user _refer_cb _refer_cb.on_evsub_state = _Referral_cb_state _refer_cb.on_tsx_state = _Referral_cb_tsx _refer_cb.on_rx_notify = _Referral_cb_notify _refer_cb.on_client_refresh = _Referral_cb_refresh cdef pjsip_evsub_user _incoming_refer_subs_cb _incoming_refer_subs_cb.on_rx_refresh = _IncomingReferral_cb_rx_refresh _incoming_refer_subs_cb.on_server_timeout = _IncomingReferral_cb_server_timeout _incoming_refer_subs_cb.on_tsx_state = _IncomingReferral_cb_tsx sipfrag_re = re.compile(r'^SIP/2\.0\s+(?P\d{3})\s+(?P[ a-zA-Z0-9_-]+)') cdef PJSTR _refer_method = PJSTR("REFER") cdef PJSTR _refer_event = PJSTR("refer") cdef PJSTR _refer_to_hdr_name = PJSTR("Refer-To") cdef PJSTR _refer_sub_hdr_name = PJSTR("Refer-Sub") cdef PJSTR _subscription_state_hdr_name = PJSTR("Subscription-State") ================================================ FILE: sipsimple/core/_core.request.pxi ================================================ from datetime import datetime, timedelta cdef class EndpointAddress: def __init__(self, ip, port): self.ip = ip self.port = port def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.ip, self.port) def __str__(self): return "%s:%d" % (self.ip, self.port) cdef class Request: expire_warning_time = 30 # properties property method: def __get__(self): return self._method.str property call_id: def __get__(self): return self._call_id.str property content_type: def __get__(self): if self._content_type is None: return None else: return "/".join([self._content_type.str, self._content_subtype.str]) property body: def __get__(self): if self._body is None: return None else: return self._body.str property expires_in: def __get__(self): cdef object dt self._get_ua() if self.state != "EXPIRING" or self._expire_time is None: return 0 else: dt = self._expire_time - datetime.now() return max(0, dt.seconds) # public methods def __cinit__(self, *args, **kwargs): self.state = "INIT" self.peer_address = None pj_timer_entry_init(&self._timer, 0, self, _Request_cb_timer) self._timer_active = 0 def __init__(self, method, SIPURI request_uri not None, FromHeader from_header not None, ToHeader to_header not None, RouteHeader route_header not None, Credentials credentials=None, ContactHeader contact_header=None, call_id=None, cseq=None, object extra_headers=None, content_type=None, body=None): cdef pjsip_method method_pj cdef PJSTR from_header_str cdef PJSTR to_header_str cdef PJSTR request_uri_str cdef PJSTR contact_header_str cdef pj_str_t *contact_header_pj = NULL cdef pj_str_t *call_id_pj = NULL cdef object content_type_spl cdef pjsip_hdr *hdr cdef pjsip_contact_hdr *contact_hdr cdef pjsip_cid_hdr *cid_hdr cdef pjsip_cseq_hdr *cseq_hdr cdef int status cdef PJSIPUA ua = _get_ua() if self._tsx != NULL or self.state != "INIT": raise SIPCoreError("Request.__init__() was already called") if cseq is not None and cseq < 0: raise ValueError("cseq argument cannot be negative") if extra_headers is not None: header_names = set([header.name for header in extra_headers]) if "Route" in header_names: raise ValueError("Route should be specified with route_header argument, not extra_headers") if "Content-Type" in header_names: raise ValueError("Content-Type should be specified with content_type argument, not extra_headers") else: header_names = () if content_type is not None and body is None: raise ValueError("Cannot specify a content_type without a body") if content_type is None and body is not None: raise ValueError("Cannot specify a body without a content_type") self._method = PJSTR(method) pjsip_method_init_np(&method_pj, &self._method.pj_str) if credentials is not None: self.credentials = FrozenCredentials.new(credentials) from_header_str = PJSTR(from_header.body) self.to_header = FrozenToHeader.new(to_header) to_header_str = PJSTR(to_header.body) self.request_uri = FrozenSIPURI.new(request_uri) request_uri_str = PJSTR(str(request_uri)) self.route_header = FrozenRouteHeader.new(route_header) self.route_header.uri.parameters.dict["lr"] = None # always send lr parameter in Route header self.route_header.uri.parameters.dict["hide"] = None # always hide Route header if contact_header is not None: self.contact_header = FrozenContactHeader.new(contact_header) contact_parameters = contact_header.parameters.copy() contact_parameters.pop("q", None) contact_parameters.pop("expires", None) contact_header.parameters = {} contact_header_str = PJSTR(contact_header.body) contact_header_pj = &contact_header_str.pj_str if call_id is not None: self._call_id = PJSTR(call_id) call_id_pj = &self._call_id.pj_str if cseq is None: self.cseq = -1 else: self.cseq = cseq if extra_headers is None: self.extra_headers = frozenlist() else: self.extra_headers = frozenlist([header.frozen_type.new(header) for header in extra_headers]) if body is not None: content_type_spl = content_type.split("/", 1) self._content_type = PJSTR(content_type_spl[0]) self._content_subtype = PJSTR(content_type_spl[1]) self._body = PJSTR(body) status = pjsip_endpt_create_request(ua._pjsip_endpoint._obj, &method_pj, &request_uri_str.pj_str, &from_header_str.pj_str, &to_header_str.pj_str, contact_header_pj, call_id_pj, self.cseq, NULL, &self._tdata) if status != 0: raise PJSIPError("Could not create request", status) if body is not None: self._tdata.msg.body = pjsip_msg_body_create(self._tdata.pool, &self._content_type.pj_str, &self._content_subtype.pj_str, &self._body.pj_str) hdr = ( &self._tdata.msg.hdr).next while hdr != &self._tdata.msg.hdr: if _pj_str_to_str(hdr.name) in header_names: raise ValueError("Cannot override %s header value in extra_headers" % _pj_str_to_str(hdr.name)) if hdr.type == PJSIP_H_CONTACT: contact_hdr = hdr _dict_to_pjsip_param(contact_parameters, &contact_hdr.other_param, self._tdata.pool) elif hdr.type == PJSIP_H_CALL_ID: cid_hdr = hdr self._call_id = PJSTR(_pj_str_to_str(cid_hdr.id)) elif hdr.type == PJSIP_H_CSEQ: cseq_hdr = hdr self.cseq = cseq_hdr.cseq elif hdr.type == PJSIP_H_FROM: self.from_header = FrozenFromHeader_create( hdr) else: pass hdr = ( hdr).next _BaseRouteHeader_to_pjsip_route_hdr(self.route_header, &self._route_header, self._tdata.pool) pjsip_msg_add_hdr(self._tdata.msg, &self._route_header) _add_headers_to_tdata(self._tdata, self.extra_headers) if self.credentials is not None: status = pjsip_auth_clt_init(&self._auth, ua._pjsip_endpoint._obj, self._tdata.pool, 0) if status != 0: raise PJSIPError("Could not init authentication credentials", status) status = pjsip_auth_clt_set_credentials(&self._auth, 1, self.credentials.get_cred_info()) if status != 0: raise PJSIPError("Could not set authentication credentials", status) self._need_auth = 1 else: self._need_auth = 0 status = pjsip_tsx_create_uac(&ua._module, self._tdata, &self._tsx) if status != 0: raise PJSIPError("Could not create transaction for request", status) self._tsx.mod_data[ua._module.id] = self def __dealloc__(self): cdef PJSIPUA ua = self._get_ua() if self._tsx != NULL: self._tsx.mod_data[ua._module.id] = NULL if self._tsx.state < PJSIP_TSX_STATE_COMPLETED: pjsip_tsx_terminate(self._tsx, 500) self._tsx = NULL if self._tdata != NULL: pjsip_tx_data_dec_ref(self._tdata) self._tdata = NULL if self._timer_active: pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timer) self._timer_active = 0 def send(self, timeout=None): cdef pj_time_val timeout_pj cdef int status cdef PJSIPUA ua = self._get_ua() if self.state != "INIT": raise SIPCoreError('This method may only be called in the "INIT" state, current state is "%s"' % self.state) if timeout is not None: if timeout <= 0: raise ValueError("Timeout value cannot be negative") timeout_pj.sec = int(timeout) timeout_pj.msec = (timeout * 1000) % 1000 self._timeout = timeout status = pjsip_tsx_send_msg(self._tsx, self._tdata) if status != 0: raise PJSIPError("Could not send request", status) pjsip_tx_data_add_ref(self._tdata) if timeout: status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timer, &timeout_pj) if status == 0: self._timer_active = 1 self.state = "IN_PROGRESS" def end(self): cdef PJSIPUA ua = self._get_ua() if self.state == "IN_PROGRESS": pjsip_tsx_terminate(self._tsx, 408) elif self.state == "EXPIRING": pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timer) self._timer_active = 0 self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) # private methods cdef PJSIPUA _get_ua(self): cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: self._tsx = NULL self._tdata = NULL self._timer_active = 0 self.state = "TERMINATED" return None else: return ua cdef int _cb_tsx_state(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: cdef pjsip_tx_data *tdata_auth cdef pjsip_transaction *tsx_auth cdef pjsip_cseq_hdr *cseq cdef dict event_dict cdef int expires = -1 cdef SIPURI contact_uri cdef dict contact_params cdef pj_time_val timeout_pj cdef int status if rdata != NULL: self.to_header = FrozenToHeader_create(rdata.msg_info.to_hdr) if self.peer_address is None: self.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: self.peer_address.ip = rdata.pkt_info.src_name self.peer_address.port = rdata.pkt_info.src_port if self._tsx.state == PJSIP_TSX_STATE_PROCEEDING: if rdata == NULL: return 0 event_dict = dict(obj=self) _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) _add_event("SIPRequestGotProvisionalResponse", event_dict) elif self._tsx.state == PJSIP_TSX_STATE_COMPLETED: if self._timer_active: pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timer) self._timer_active = 0 if self._need_auth and self._tsx.status_code in [401, 407]: self._need_auth = 0 status = pjsip_auth_clt_reinit_req(&self._auth, rdata, self._tdata, &tdata_auth) if status != 0: _add_event("SIPRequestDidFail", dict(obj=self, code=0, reason="Could not add auth data to request %s" % _pj_status_to_str(status))) self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) return 0 cseq = pjsip_msg_find_hdr(tdata_auth.msg, PJSIP_H_CSEQ, NULL) if cseq != NULL: cseq.cseq += 1 self.cseq = cseq.cseq status = pjsip_tsx_create_uac(&ua._module, tdata_auth, &tsx_auth) if status != 0: pjsip_tx_data_dec_ref(tdata_auth) _add_event("SIPRequestDidFail", dict(obj=self, code=0, reason="Could not create transaction for request with auth %s" % _pj_status_to_str(status))) self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) return 0 self._tsx.mod_data[ua._module.id] = NULL self._tsx = tsx_auth self._tsx.mod_data[ua._module.id] = self status = pjsip_tsx_send_msg(self._tsx, tdata_auth) if status != 0: pjsip_tx_data_dec_ref(tdata_auth) _add_event("SIPRequestDidFail", dict(obj=self, code=0, reason="Could not send request with auth %s" % _pj_status_to_str(status))) self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) return 0 elif self._timeout is not None: timeout_pj.sec = int(self._timeout) timeout_pj.msec = (self._timeout * 1000) % 1000 status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timer, &timeout_pj) if status == 0: self._timer_active = 1 else: event_dict = dict(obj=self) if rdata != NULL: # This shouldn't happen, but safety fist! _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) if self._tsx.status_code / 100 == 2: if rdata != NULL: if "Expires" in event_dict["headers"]: expires = event_dict["headers"]["Expires"] elif self.contact_header is not None: for contact_header in event_dict["headers"].get("Contact", []): if contact_header.uri == self.contact_header.uri and contact_header.expires is not None: expires = contact_header.expires if expires == -1: expires = 0 for header in self.extra_headers: if header.name == "Expires": try: expires = int(header.body) except ValueError: pass break event_dict["expires"] = expires self._expire_time = datetime.now() + timedelta(seconds=expires) _add_event("SIPRequestDidSucceed", event_dict) else: expires = 0 _add_event("SIPRequestDidFail", event_dict) if expires == 0: self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) else: timeout_pj.sec = max(1, expires - self.expire_warning_time, expires/2) timeout_pj.msec = 0 status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timer, &timeout_pj) if status == 0: self._timer_active = 1 self.state = "EXPIRING" self._expire_rest = max(1, expires - timeout_pj.sec) else: self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) elif self._tsx.state == PJSIP_TSX_STATE_TERMINATED: if self.state == "IN_PROGRESS": if self._timer_active: pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timer) self._timer_active = 0 _add_event("SIPRequestDidFail", dict(obj=self, code=self._tsx.status_code, reason=_pj_str_to_str(self._tsx.status_text))) self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) self._tsx.mod_data[ua._module.id] = NULL self._tsx = NULL else: pass cdef int _cb_timer(self, PJSIPUA ua) except -1: cdef pj_time_val expires cdef int status if self.state == "IN_PROGRESS": pjsip_tsx_terminate(self._tsx, 408) elif self.state == "EXPIRING": if self._expire_rest > 0: _add_event("SIPRequestWillExpire", dict(obj=self, expires=self._expire_rest)) expires.sec = self._expire_rest expires.msec = 0 self._expire_rest = 0 status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timer, &expires) if status == 0: self._timer_active = 1 else: self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) else: self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) return 0 cdef class IncomingRequest: def __cinit__(self, *args, **kwargs): self.peer_address = None def __dealloc__(self): cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: return if self._tsx != NULL: pjsip_tsx_terminate(self._tsx, 500) self._tsx = NULL if self._tdata != NULL: pjsip_tx_data_dec_ref(self._tdata) self._tdata = NULL def answer(self, int code, str reason=None, object extra_headers=None): cdef bytes reason_bytes cdef dict event_dict cdef int status cdef PJSIPUA ua = _get_ua() if self.state != "incoming": raise SIPCoreInvalidStateError('Can only answer an incoming request in the "incoming" state, ' 'object is currently in the "%s" state' % self.state) if code < 200 or code >= 700: raise ValueError("Invalid SIP final response code: %d" % code) self._tdata.msg.line.status.code = code if reason is None: self._tdata.msg.line.status.reason = pjsip_get_status_text(code)[0] else: reason_bytes = reason.encode() pj_strdup2_with_null(self._tdata.pool, &self._tdata.msg.line.status.reason, reason_bytes) if extra_headers is not None: _add_headers_to_tdata(self._tdata, extra_headers) event_dict = dict(obj=self) _pjsip_msg_to_dict(self._tdata.msg, event_dict) status = pjsip_tsx_send_msg(self._tsx, self._tdata) if status != 0: raise PJSIPError("Could not send response", status) self.state = "answered" self._tdata = NULL self._tsx = NULL _add_event("SIPIncomingRequestSentResponse", event_dict) cdef int init(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: cdef dict event_dict cdef int status status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 500, NULL, &self._tdata) if status != 0: raise PJSIPError("Could not create response", status) status = pjsip_tsx_create_uas(&ua._module, rdata, &self._tsx) if status != 0: pjsip_tx_data_dec_ref(self._tdata) self._tdata = NULL raise PJSIPError("Could not create transaction for incoming request", status) pjsip_tsx_recv_msg(self._tsx, rdata) self.state = "incoming" self.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) event_dict = dict(obj=self) _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) _add_event("SIPIncomingRequestGotRequest", event_dict) # callback functions cdef void _Request_cb_tsx_state(pjsip_transaction *tsx, pjsip_event *event) with gil: cdef PJSIPUA ua cdef void *req_ptr cdef Request req cdef pjsip_rx_data *rdata = NULL try: ua = _get_ua() except: return try: req_ptr = tsx.mod_data[ua._module.id] if req_ptr != NULL: req = req_ptr if event.type == PJSIP_EVENT_RX_MSG: rdata = event.body.rx_msg.rdata elif event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.type == PJSIP_EVENT_RX_MSG: rdata = event.body.tsx_state.src.rdata req._cb_tsx_state(ua, rdata) except: ua._handle_exception(1) cdef void _Request_cb_timer(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) with gil: cdef PJSIPUA ua cdef Request req try: ua = _get_ua() except: return try: if entry.user_data != NULL: req = entry.user_data req._timer_active = 0 req._cb_timer(ua) except: ua._handle_exception(1) ================================================ FILE: sipsimple/core/_core.sdp.pxi ================================================ import re from application.python.descriptor import WriteOnceAttribute cdef object BaseSDPSession_richcmp(object self, object other, int op) with gil: cdef int eq = 1 if op not in [2,3]: return NotImplemented if not isinstance(other, BaseSDPSession): return NotImplemented for attr in ("id", "version", "user", "net_type", "address_type", "address", "address", "name", "connection", "start_time", "stop_time", "attributes", "bandwidth_info", "media"): if getattr(self, attr) != getattr(other, attr): eq = 0 break if op == 2: return bool(eq) else: return not eq cdef pjmedia_sdp_session* _parse_sdp_session(str sdp): cdef int status cdef pjmedia_sdp_session *sdp_session status = pjmedia_sdp_parse(_get_ua()._pjsip_endpoint._pool, PyString_AsString(sdp), PyString_Size(sdp), &sdp_session) if status != 0: raise PJSIPError("failed to parse SDP", status) return sdp_session cdef class BaseSDPSession: def __init__(self, *args, **kwargs): raise TypeError("BaseSDPSession cannot be instantiated directly") def __repr__(self): return "%s(%r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r)" % (self.__class__.__name__, self.address, self.id, self.version, self.user, self.net_type, self.address_type, self.name, self.connection, self.start_time, self.stop_time, self.attributes, self.bandwidth_info, self.media) def __str__(self): cdef char cbuf[2048] cdef int buf_len buf_len = pjmedia_sdp_print(self.get_sdp_session(), cbuf, sizeof(cbuf)) if buf_len > -1: return PyString_FromStringAndSize(cbuf, buf_len) return '' def __richcmp__(self, other, op): return BaseSDPSession_richcmp(self, other, op) cdef pjmedia_sdp_session* get_sdp_session(self): self._sdp_session.media_count = len(self.media) for index, m in enumerate(self.media): if m is not None: self._sdp_session.media[index] = (m).get_sdp_media() else: self._sdp_session.media[index] = NULL self._sdp_session.attr_count = len(self.attributes) for index, attr in enumerate(self.attributes): self._sdp_session.attr[index] = (attr).get_sdp_attribute() self._sdp_session.bandw_count = len(self.bandwidth_info) for index, info in enumerate(self.bandwidth_info): self._sdp_session.bandw[index] = (info).get_sdp_bandwidth_info() return &self._sdp_session property has_ice_attributes: def __get__(self): return set([attr.name for attr in self.attributes]).issuperset(['ice-pwd', 'ice-ufrag']) cdef class SDPSession(BaseSDPSession): def __init__(self, str address not None, object id=None, object version=None, str user not None="-", str net_type not None="IN", str address_type not None="IP4", str name not None=" ", SDPConnection connection=None, unsigned long start_time=0, unsigned long stop_time=0, list attributes=None, list bandwidth_info=None, list media=None): cdef unsigned int version_id = 2208988800UL cdef pj_time_val tv pj_gettimeofday(&tv) version_id += tv.sec self.address = address self.id = id if id is not None else version_id self.version = version if version is not None else version_id self.user = user self.net_type = net_type self.address_type = address_type self.name = name self.connection = connection self.start_time = start_time self.stop_time = stop_time self.attributes = attributes if attributes is not None else [] self.bandwidth_info = bandwidth_info if bandwidth_info is not None else [] self.media = media if media is not None else [] @classmethod def new(cls, BaseSDPSession sdp_session): connection = SDPConnection.new(sdp_session.connection) if (sdp_session.connection is not None) else None attributes = [SDPAttribute.new(attr) for attr in sdp_session.attributes] bandwidth_info = [SDPBandwidthInfo.new(info) for info in sdp_session.bandwidth_info] media = [SDPMediaStream.new(m) if m is not None else None for m in sdp_session.media] return cls(sdp_session.address, sdp_session.id, sdp_session.version, sdp_session.user, sdp_session.net_type, sdp_session.address_type, sdp_session.name, connection, sdp_session.start_time, sdp_session.stop_time, attributes, bandwidth_info, media) @classmethod def parse(cls, str sdp): cdef pjmedia_sdp_session *sdp_session sdp_session = _parse_sdp_session(sdp) return SDPSession_create(sdp_session) property address: def __get__(self): return self._address def __set__(self, str address not None): _str_to_pj_str(address, &self._sdp_session.origin.addr) self._address = address property id: def __get__(self): return self._sdp_session.origin.id def __set__(self, unsigned int id): self._sdp_session.origin.id = id property version: def __get__(self): return self._sdp_session.origin.version def __set__(self, unsigned int version): self._sdp_session.origin.version = version property user: def __get__(self): return self._user def __set__(self, str user not None): _str_to_pj_str(user, &self._sdp_session.origin.user) self._user = user property net_type: def __get__(self): return self._net_type def __set__(self, str net_type not None): _str_to_pj_str(net_type, &self._sdp_session.origin.net_type) self._net_type = net_type property address_type: def __get__(self): return self._address_type def __set__(self, str address_type not None): _str_to_pj_str(address_type, &self._sdp_session.origin.addr_type) self._address_type = address_type property name: def __get__(self): return self._name def __set__(self, str name not None): _str_to_pj_str(name, &self._sdp_session.name) self._name = name property connection: def __get__(self): return self._connection def __set__(self, SDPConnection connection): if connection is None: self._sdp_session.conn = NULL else: self._sdp_session.conn = connection.get_sdp_connection() self._connection = connection property start_time: def __get__(self): return self._sdp_session.time.start def __set__(self, unsigned long start_time): self._sdp_session.time.start = start_time property stop_time: def __get__(self): return self._sdp_session.time.stop def __set__(self, unsigned long stop_time): self._sdp_session.time.stop = stop_time property attributes: def __get__(self): return self._attributes def __set__(self, list attributes not None): if len(attributes) > PJMEDIA_MAX_SDP_ATTR: raise SIPCoreError("Too many attributes") for attr in attributes: if not isinstance(attr, SDPAttribute): raise TypeError("Items in SDPSession attribute list must be SDPAttribute instancess") if not isinstance(attributes, SDPAttributeList): attributes = SDPAttributeList(attributes) self._attributes = attributes property bandwidth_info: def __get__(self): return self._bandwidth_info def __set__(self, list infos not None): if len(infos) > PJMEDIA_MAX_SDP_BANDW: raise SIPCoreError("Too many bandwidth info attributes") for info in infos: if not isinstance(info, SDPBandwidthInfo): raise TypeError("Items in SDPSession attribute list must be SDPBandwidthInfo instancess") if not isinstance(infos, SDPBandwidthInfoList): infos = SDPBandwidthInfoList(infos) self._bandwidth_info = infos property media: def __get__(self): return self._media def __set__(self, list media not None): if len(media) > PJMEDIA_MAX_SDP_MEDIA: raise SIPCoreError("Too many media objects") for m in media: if m is not None and not isinstance(m, SDPMediaStream): raise TypeError("Items in SDPSession media list must be SDPMediaStream instancess") self._media = media cdef int _update(self) except -1: cdef SDPSession session cdef SDPMediaStream media, old_media session = SDPSession_create(&(self)._sdp_session) if len(self._media) != len(session._media): raise ValueError("Number of media streams in SDPSession got changed") if len(self._attributes) > len(session._attributes): raise ValueError("Number of attributes in SDPSession got reduced") for attr in ("id", "version", "user", "net_type", "address_type", "address", "name", "start_time", "stop_time"): setattr(self, attr, getattr(session, attr)) if session._connection is None: self.connection = None elif self._connection is None or self._connection != session._connection: self.connection = session._connection for index, attribute in enumerate(session._attributes): try: old_attribute = self._attributes[index] except IndexError: self._attributes.append(attribute) else: if old_attribute != attribute: self._attributes[index] = attribute for index, info in enumerate(session._bandwidth_info): try: old_info = self._bandwidth_info[index] except IndexError: self._bandwidth_info.append(info) else: if old_info != info: self._bandwidth_info[index] = info for index, media in enumerate(session._media): old_media = self._media[index] if old_media is not None: old_media._update(media) cdef class FrozenSDPSession(BaseSDPSession): def __init__(self, str address not None, object id=None, object version=None, str user not None="-", str net_type not None="IN", str address_type not None="IP4", str name not None=" ", FrozenSDPConnection connection=None, unsigned long start_time=0, unsigned long stop_time=0, frozenlist attributes not None=frozenlist(), frozenlist bandwidth_info not None=frozenlist(), frozenlist media not None=frozenlist()): cdef unsigned int version_id = 2208988800UL cdef pj_time_val tv if not self.initialized: if len(attributes) > PJMEDIA_MAX_SDP_ATTR: raise SIPCoreError("Too many attributes") for attr in attributes: if not isinstance(attr, FrozenSDPAttribute): raise TypeError("Items in FrozenSDPSession attribute list must be FrozenSDPAttribute instances") if len(bandwidth_info) > PJMEDIA_MAX_SDP_BANDW: raise SIPCoreError("Too many bandwidth info attributes") for info in bandwidth_info: if not isinstance(info, FrozenSDPBandwidthInfo): raise TypeError("Items in FrozenSDPSession bandwidth info attribute list must be FrozenSDPBandwidthInfo instances") if len(media) > PJMEDIA_MAX_SDP_MEDIA: raise SIPCoreError("Too many media objects") for m in media: if not isinstance(m, FrozenSDPMediaStream): raise TypeError("Items in FrozenSDPSession media list must be FrozenSDPMediaStream instancess") pj_gettimeofday(&tv) version_id += tv.sec self.address = address _str_to_pj_str(address, &self._sdp_session.origin.addr) self.id = id if id is not None else version_id self._sdp_session.origin.id = id if id is not None else version_id self.version = version if version is not None else version_id self._sdp_session.origin.version = version if version is not None else version_id self.user = user _str_to_pj_str(user, &self._sdp_session.origin.user) self.net_type = net_type _str_to_pj_str(net_type, &self._sdp_session.origin.net_type) self.address_type = address_type _str_to_pj_str(address_type, &self._sdp_session.origin.addr_type) self.name = name _str_to_pj_str(name, &self._sdp_session.name) self.connection = connection if connection is None: self._sdp_session.conn = NULL else: self._sdp_session.conn = connection.get_sdp_connection() self.start_time = start_time self._sdp_session.time.start = start_time self.stop_time = stop_time self._sdp_session.time.stop = stop_time self.attributes = FrozenSDPAttributeList(attributes) if not isinstance(attributes, FrozenSDPAttributeList) else attributes self.bandwidth_info = FrozenSDPBandwidthInfoList(bandwidth_info) if not isinstance(bandwidth_info, FrozenSDPBandwidthInfo) else bandwidth_info self.media = media self.initialized = 1 @classmethod def new(cls, BaseSDPSession sdp_session): if isinstance(sdp_session, FrozenSDPSession): return sdp_session connection = FrozenSDPConnection.new(sdp_session.connection) if (sdp_session.connection is not None) else None attributes = frozenlist([FrozenSDPAttribute.new(attr) for attr in sdp_session.attributes]) bandwidth_info = frozenlist([FrozenSDPBandwidthInfo.new(info) for info in sdp_session.bandwidth_info]) media = frozenlist([FrozenSDPMediaStream.new(m) for m in sdp_session.media]) return cls(sdp_session.address, sdp_session.id, sdp_session.version, sdp_session.user, sdp_session.net_type, sdp_session.address_type, sdp_session.name, connection, sdp_session.start_time, sdp_session.stop_time, attributes, bandwidth_info, media) @classmethod def parse(cls, str sdp): cdef pjmedia_sdp_session *sdp_session sdp_session = _parse_sdp_session(sdp) return FrozenSDPSession_create(sdp_session) def __hash__(self): return hash((self.address, self.id, self.version, self.user, self.net_type, self.address_type, self.name, self.connection, self.start_time, self.stop_time, self.attributes, self.bandwidth_info, self.media)) def __richcmp__(self, other, op): return BaseSDPSession_richcmp(self, other, op) class MediaCodec(object): name = WriteOnceAttribute() rate = WriteOnceAttribute() def __init__(self, name, rate): self.name = name self.rate = int(rate) def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.name, self.rate) def __str__(self): return "%s/%s" % (self.name, self.rate) def __hash__(self): return hash(self.name) def __eq__(self, other): if isinstance(other, MediaCodec): return self.name.lower() == other.name.lower() and self.rate == other.rate elif isinstance(other, basestring): if '/' in other: return self.__str__().lower() == other.lower() else: return self.name.lower() == other.lower() return False def __ne__(self, other): return not self.__eq__(other) cdef object BaseSDPMediaStream_richcmp(object self, object other, int op) with gil: cdef int eq = 1 if op not in [2,3]: return NotImplemented if not isinstance(other, BaseSDPMediaStream): return NotImplemented for attr in ("media", "port", "port_count", "transport", "formats", "connection", "attributes", "bandwidth_info"): if getattr(self, attr) != getattr(other, attr): eq = 0 break if op == 2: return bool(eq) else: return not eq cdef class BaseSDPMediaStream: rtpmap_re = re.compile(r"""^(?P\d+)\s+(?P[-\w]+)/(?P\d+)(?:/\w+)?$""", re.IGNORECASE | re.MULTILINE) rtp_mappings = { 0: MediaCodec('PCMU', 8000), 3: MediaCodec('GSM', 8000), 4: MediaCodec('G723', 8000), 5: MediaCodec('DVI4', 8000), 6: MediaCodec('DVI4', 16000), 7: MediaCodec('LPC', 8000), 8: MediaCodec('PCMA', 8000), 9: MediaCodec('G722', 8000), 10: MediaCodec('L16', 44100), # 2 channels 11: MediaCodec('L16', 44100), # 1 channel 12: MediaCodec('QCELP', 8000), 13: MediaCodec('CN', 8000), 14: MediaCodec('MPA', 90000), 15: MediaCodec('G728', 8000), 16: MediaCodec('DVI4', 11025), 17: MediaCodec('DVI4', 22050), 18: MediaCodec('G729', 8000)} def __init__(self, *args, **kwargs): raise TypeError("BaseSDPMediaStream cannot be instantiated directly") def __repr__(self): return "%s(%r, %r, %r, %r, %r, %r, %r, %r)" % (self.__class__.__name__, self.media, self.port, self.transport, self.port_count, self.formats, self.connection, self.attributes, self.bandwidth_info) def __richcmp__(self, other, op): return BaseSDPMediaStream_richcmp(self, other, op) property direction: def __get__(self): for attribute in self.attributes: if attribute.name in ("sendrecv", "sendonly", "recvonly", "inactive"): return attribute.name return "sendrecv" property has_ice_attributes: def __get__(self): return set([attr.name for attr in self.attributes]).issuperset(['ice-pwd', 'ice-ufrag']) property has_ice_candidates: def __get__(self): return 'candidate' in self.attributes cdef pjmedia_sdp_media* get_sdp_media(self): self._sdp_media.attr_count = len(self.attributes) for index, attr in enumerate(self.attributes): self._sdp_media.attr[index] = (attr).get_sdp_attribute() self._sdp_media.bandw_count = len(self.bandwidth_info) for index, info in enumerate(self.bandwidth_info): self._sdp_media.bandw[index] = (info).get_sdp_bandwidth_info() return &self._sdp_media cdef class SDPMediaStream(BaseSDPMediaStream): def __init__(self, str media not None, int port, str transport not None, int port_count=1, list formats=None, SDPConnection connection=None, list attributes=None, list bandwidth_info=None): self.media = media self.port = port self.transport = transport self.port_count = port_count self.formats = formats if formats is not None else [] self.connection = connection self.attributes = attributes if attributes is not None else [] self.bandwidth_info = bandwidth_info if bandwidth_info is not None else [] @classmethod def new(cls, BaseSDPMediaStream sdp_media): connection = SDPConnection.new(sdp_media.connection) if (sdp_media.connection is not None) else None attributes = [SDPAttribute.new(attr) for attr in sdp_media.attributes] bandwidth_info = [SDPBandwidthInfo.new(bi) for bi in sdp_media.bandwidth_info] return cls(sdp_media.media, sdp_media.port, sdp_media.transport, sdp_media.port_count, list(sdp_media.formats), connection, attributes, bandwidth_info) property media: def __get__(self): return self._media def __set__(self, str media not None): _str_to_pj_str(media, &self._sdp_media.desc.media) self._media = media property port: def __get__(self): return self._sdp_media.desc.port def __set__(self, int port): self._sdp_media.desc.port = port property transport: def __get__(self): return self._transport def __set__(self, str transport not None): _str_to_pj_str(transport, &self._sdp_media.desc.transport) self._transport = transport property port_count: def __get__(self): return self._sdp_media.desc.port_count def __set__(self, int port_count): self._sdp_media.desc.port_count = port_count property formats: def __get__(self): return self._formats def __set__(self, list formats not None): if len(formats) > PJMEDIA_MAX_SDP_FMT: raise SIPCoreError("Too many formats") self._sdp_media.desc.fmt_count = len(formats) for index, format in enumerate(formats): _str_to_pj_str(format, &self._sdp_media.desc.fmt[index]) self._formats = formats property codec_list: def __get__(self): return self._codec_list property connection: def __get__(self): return self._connection def __set__(self, SDPConnection connection): if connection is None: self._sdp_media.conn = NULL else: self._sdp_media.conn = connection.get_sdp_connection() self._connection = connection property attributes: def __get__(self): return self._attributes def __set__(self, list attributes not None): if len(attributes) > PJMEDIA_MAX_SDP_ATTR: raise SIPCoreError("Too many attributes") for attr in attributes: if not isinstance(attr, SDPAttribute): raise TypeError("Items in SDPMediaStream attribute list must be SDPAttribute instances") if not isinstance(attributes, SDPAttributeList): attributes = SDPAttributeList(attributes) self._attributes = attributes if self._media in ("audio", "video"): rtp_mappings = self.rtp_mappings.copy() rtpmap_lines = '\n'.join([attr.value for attr in attributes if attr.name=='rtpmap']) # iterators are not supported -Dan rtpmap_codecs = dict([(int(type), MediaCodec(name, rate)) for type, name, rate in self.rtpmap_re.findall(rtpmap_lines)]) rtp_mappings.update(rtpmap_codecs) self._codec_list = [rtp_mappings.get(int(format), MediaCodec('Unknown', 0)) for format in self.formats] else: self._codec_list = list() property bandwidth_info: def __get__(self): return self._bandwidth_info def __set__(self, list infos not None): if len(infos) > PJMEDIA_MAX_SDP_BANDW: raise SIPCoreError("Too many bandwidth information attributes") for info in infos: if not isinstance(info, SDPBandwidthInfo): raise TypeError("Items in SDPMediaStream bandwidth_info list must be SDPBandwidthInfo instances") if not isinstance(infos, SDPBandwidthInfoList): infos = SDPBandwidthInfoList(infos) self._bandwidth_info = infos cdef int _update(self, SDPMediaStream media) except -1: if len(self._attributes) > len(media._attributes): raise ValueError("Number of attributes in SDPMediaStream got reduced") if len(self._bandwidth_info) > len(media._bandwidth_info): raise ValueError("Number of bandwidth info attributes in SDPMediaStream got reduced") for attr in ("media", "port", "transport", "port_count", "formats"): setattr(self, attr, getattr(media, attr)) if media._connection is None: self.connection = None elif self._connection is None or self._connection != media.connection: self.connection = media._connection for index, attribute in enumerate(media._attributes): try: old_attribute = self._attributes[index] except IndexError: self._attributes.append(attribute) else: if old_attribute != attribute: self._attributes[index] = attribute for index, info in enumerate(media._bandwidth_info): try: old_info = self._bandwidth_info[index] except IndexError: self._bandwidth_info.append(info) else: if old_info != info: self._bandwidth_info[index] = info cdef class FrozenSDPMediaStream(BaseSDPMediaStream): def __init__(self, str media not None, int port, str transport not None, int port_count=1, frozenlist formats not None=frozenlist(), FrozenSDPConnection connection=None, frozenlist attributes not None=frozenlist(), frozenlist bandwidth_info not None=frozenlist()): if not self.initialized: if len(formats) > PJMEDIA_MAX_SDP_FMT: raise SIPCoreError("Too many formats") if len(attributes) > PJMEDIA_MAX_SDP_ATTR: raise SIPCoreError("Too many attributes") for attr in attributes: if not isinstance(attr, FrozenSDPAttribute): raise TypeError("Items in FrozenSDPMediaStream attribute list must be FrozenSDPAttribute instances") if len(bandwidth_info) > PJMEDIA_MAX_SDP_BANDW: raise SIPCoreError("Too many bandwidth info attributes") for info in bandwidth_info: if not isinstance(info, FrozenSDPBandwidthInfo): raise TypeError("Items in FrozenSDPMediaStream bandwidth info list must be FrozenSDPBandwidthInfo instances") self.media = media _str_to_pj_str(media, &self._sdp_media.desc.media) self.port = port self._sdp_media.desc.port = port self.transport = transport _str_to_pj_str(transport, &self._sdp_media.desc.transport) self.port_count = port_count self._sdp_media.desc.port_count = port_count self.formats = formats self._sdp_media.desc.fmt_count = len(self.formats) for index, format in enumerate(self.formats): _str_to_pj_str(format, &self._sdp_media.desc.fmt[index]) self.connection = connection if connection is None: self._sdp_media.conn = NULL else: self._sdp_media.conn = connection.get_sdp_connection() self.attributes = FrozenSDPAttributeList(attributes) if not isinstance(attributes, FrozenSDPAttributeList) else attributes if self.media in ("audio", "video"): rtp_mappings = self.rtp_mappings.copy() rtpmap_lines = '\n'.join([attr.value for attr in attributes if attr.name=='rtpmap']) # iterators are not supported -Dan rtpmap_codecs = dict([(int(type), MediaCodec(name, rate)) for type, name, rate in self.rtpmap_re.findall(rtpmap_lines)]) rtp_mappings.update(rtpmap_codecs) self.codec_list = frozenlist([rtp_mappings.get(int(format) if format.isdigit() else None, MediaCodec('Unknown', 0)) for format in self.formats]) else: self.codec_list = frozenlist() self.bandwidth_info = FrozenSDPBandwidthInfoList(bandwidth_info) if not isinstance(bandwidth_info, FrozenSDPBandwidthInfoList) else bandwidth_info self.initialized = 1 @classmethod def new(cls, BaseSDPMediaStream sdp_media): if isinstance(sdp_media, FrozenSDPMediaStream): return sdp_media connection = FrozenSDPConnection.new(sdp_media.connection) if (sdp_media.connection is not None) else None attributes = frozenlist([FrozenSDPAttribute.new(attr) for attr in sdp_media.attributes]) bandwidth_info = frozenlist([FrozenSDPBandwidthInfo.new(info) for info in sdp_media.bandwidth_info]) return cls(sdp_media.media, sdp_media.port, sdp_media.transport, sdp_media.port_count, frozenlist(sdp_media.formats), connection, attributes, bandwidth_info) def __hash__(self): return hash((self.media, self.port, self.transport, self.port_count, self.formats, self.connection, self.attributes, self.bandwidth_info)) def __richcmp__(self, other, op): return BaseSDPMediaStream_richcmp(self, other, op) cdef object BaseSDPConnection_richcmp(object self, object other, int op) with gil: cdef int eq = 1 if op not in [2,3]: return NotImplemented if not isinstance(other, BaseSDPConnection): return NotImplemented for attr in ("net_type", "address_type", "address"): if getattr(self, attr) != getattr(other, attr): eq = 0 break if op == 2: return bool(eq) else: return not eq cdef class BaseSDPConnection: def __init__(self, *args, **kwargs): raise TypeError("BaseSDPConnection cannot be instantiated directly") def __repr__(self): return "%s(%r, %r, %r)" % (self.__class__.__name__, self.address, self.net_type, self.address_type) def __richcmp__(self, other, op): return BaseSDPConnection_richcmp(self, other, op) cdef pjmedia_sdp_conn* get_sdp_connection(self): return &self._sdp_connection cdef class SDPConnection(BaseSDPConnection): def __init__(self, str address not None, str net_type not None="IN", str address_type not None="IP4"): self.address = address self.net_type = net_type self.address_type = address_type @classmethod def new(cls, BaseSDPConnection sdp_connection): return cls(sdp_connection.address, sdp_connection.net_type, sdp_connection.address_type) property address: def __get__(self): return self._address def __set__(self, str address not None): _str_to_pj_str(address, &self._sdp_connection.addr) self._address = address property net_type: def __get__(self): return self._net_type def __set__(self, str net_type not None): _str_to_pj_str(net_type, &self._sdp_connection.net_type) self._net_type = net_type property address_type: def __get__(self): return self._address_type def __set__(self, str address_type not None): _str_to_pj_str(address_type, &self._sdp_connection.addr_type) self._address_type = address_type cdef class FrozenSDPConnection(BaseSDPConnection): def __init__(self, str address not None, str net_type not None="IN", str address_type not None="IP4"): if not self.initialized: _str_to_pj_str(address, &self._sdp_connection.addr) _str_to_pj_str(net_type, &self._sdp_connection.net_type) _str_to_pj_str(address_type, &self._sdp_connection.addr_type) self.address = address self.net_type = net_type self.address_type = address_type self.initialized = 1 @classmethod def new(cls, BaseSDPConnection sdp_connection): if isinstance(sdp_connection, FrozenSDPConnection): return sdp_connection return cls(sdp_connection.address, sdp_connection.net_type, sdp_connection.address_type) def __hash__(self): return hash((self.address, self.net_type, self.address_type)) def __richcmp__(self, other, op): return BaseSDPConnection_richcmp(self, other, op) cdef class SDPAttributeList(list): def __contains__(self, item): if isinstance(item, BaseSDPAttribute): return list.__contains__(self, item) else: return item in [attr.name for attr in self] def getall(self, name): return [attr.value for attr in self if attr.name == name] def getfirst(self, name, default=None): for attr in self: if attr.name == name: return attr.value return default cdef class FrozenSDPAttributeList(frozenlist): def __contains__(self, item): if isinstance(item, BaseSDPAttribute): return list.__contains__(self, item) else: return item in [attr.name for attr in self] def getall(self, name): return [attr.value for attr in self if attr.name == name] def getfirst(self, name, default=None): for attr in self: if attr.name == name: return attr.value return default cdef object BaseSDPAttribute_richcmp(object self, object other, int op) with gil: cdef int eq = 1 if op not in [2,3]: return NotImplemented if not isinstance(other, BaseSDPAttribute): return NotImplemented for attr in ("name", "value"): if getattr(self, attr) != getattr(other, attr): eq = 0 break if op == 2: return bool(eq) else: return not eq cdef class BaseSDPAttribute: def __init__(self, *args, **kwargs): raise TypeError("BaseSDPAttribute cannot be instantiated directly") def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.name, self.value) def __richcmp__(self, other, op): return BaseSDPAttribute_richcmp(self, other, op) cdef pjmedia_sdp_attr* get_sdp_attribute(self): return &self._sdp_attribute cdef class SDPAttribute(BaseSDPAttribute): def __init__(self, str name not None, str value not None): self.name = name self.value = value @classmethod def new(cls, BaseSDPAttribute sdp_attribute): return cls(sdp_attribute.name, sdp_attribute.value) property name: def __get__(self): return self._name def __set__(self, str name not None): _str_to_pj_str(name, &self._sdp_attribute.name) self._name = name property value: def __get__(self): return self._value def __set__(self, str value not None): _str_to_pj_str(value, &self._sdp_attribute.value) self._value = value cdef class FrozenSDPAttribute(BaseSDPAttribute): def __init__(self, str name not None, str value not None): if not self.initialized: _str_to_pj_str(name, &self._sdp_attribute.name) _str_to_pj_str(value, &self._sdp_attribute.value) self.name = name self.value = value self.initialized = 1 @classmethod def new(cls, BaseSDPAttribute sdp_attribute): if isinstance(sdp_attribute, FrozenSDPAttribute): return sdp_attribute return cls(sdp_attribute.name, sdp_attribute.value) def __hash__(self): return hash((self.name, self.value)) def __richcmp__(self, other, op): return BaseSDPAttribute_richcmp(self, other, op) cdef class SDPBandwidthInfoList(list): def __contains__(self, item): if isinstance(item, BaseSDPBandwidthInfo): return list.__contains__(self, item) else: return item in [attr.name for attr in self] cdef class FrozenSDPBandwidthInfoList(frozenlist): def __contains__(self, item): if isinstance(item, BaseSDPBandwidthInfo): return list.__contains__(self, item) else: return item in [info.modifier for info in self] cdef object BaseSDPBandwidthInfo_richcmp(object self, object other, int op) with gil: cdef int eq = 1 if op not in [2,3]: return NotImplemented if not isinstance(other, BaseSDPBandwidthInfo): return NotImplemented for attr in ("modifier", "value"): if getattr(self, attr) != getattr(other, attr): eq = 0 break if op == 2: return bool(eq) else: return not eq cdef class BaseSDPBandwidthInfo: def __init__(self, *args, **kwargs): raise TypeError("BaseSDPBandwidthInfo cannot be instantiated directly") def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.modifier, self.value) def __richcmp__(self, other, op): return BaseSDPBandwidthInfo_richcmp(self, other, op) cdef pjmedia_sdp_bandw* get_sdp_bandwidth_info(self): return &self._sdp_bandwidth_info cdef class SDPBandwidthInfo(BaseSDPBandwidthInfo): def __init__(self, str modifier not None, object value not None): self.modifier = modifier self.value = value @classmethod def new(cls, BaseSDPBandwidthInfo sdp_bandwidth_info): return cls(sdp_bandwidth_info.modifier, sdp_bandwidth_info.value) property modifier: def __get__(self): return self._modifier def __set__(self, str modifier not None): _str_to_pj_str(modifier, &self._sdp_bandwidth_info.modifier) self._modifier = modifier property value: def __get__(self): return self._value def __set__(self, object value not None): self._value = value self._sdp_bandwidth_info.value = self._value cdef class FrozenSDPBandwidthInfo(BaseSDPBandwidthInfo): def __init__(self, str modifier not None, object value not None): if not self.initialized: _str_to_pj_str(modifier, &self._sdp_bandwidth_info.modifier) self.modifier = modifier self._sdp_bandwidth_info.value = value self.value = value self.initialized = 1 @classmethod def new(cls, BaseSDPBandwidthInfo sdp_bandwidth_info): if isinstance(sdp_bandwidth_info, FrozenSDPBandwidthInfo): return sdp_bandwidth_info return cls(sdp_bandwidth_info.modifier, sdp_bandwidth_info.value) def __hash__(self): return hash((self.modifier, self.value)) def __richcmp__(self, other, op): return BaseSDPBandwidthInfo_richcmp(self, other, op) # Factory functions # cdef SDPSession SDPSession_create(pjmedia_sdp_session_ptr_const pj_session): cdef SDPConnection connection = None cdef int i if pj_session.conn != NULL: connection = SDPConnection_create(pj_session.conn) return SDPSession(_pj_str_to_str(pj_session.origin.addr), pj_session.origin.id, pj_session.origin.version, _pj_str_to_str(pj_session.origin.user), _pj_str_to_str(pj_session.origin.net_type), _pj_str_to_str(pj_session.origin.addr_type), _pj_str_to_str(pj_session.name), connection, pj_session.time.start, pj_session.time.stop, [SDPAttribute_create(pj_session.attr[i]) for i in range(pj_session.attr_count)], [SDPBandwidthInfo_create(pj_session.bandw[i]) for i in range(pj_session.bandw_count)], [SDPMediaStream_create(pj_session.media[i]) if pj_session.media[i] != NULL else None for i in range(pj_session.media_count)]) cdef FrozenSDPSession FrozenSDPSession_create(pjmedia_sdp_session_ptr_const pj_session): cdef FrozenSDPConnection connection = None cdef int i if pj_session.conn != NULL: connection = FrozenSDPConnection_create(pj_session.conn) return FrozenSDPSession(_pj_str_to_str(pj_session.origin.addr), pj_session.origin.id, pj_session.origin.version, _pj_str_to_str(pj_session.origin.user), _pj_str_to_str(pj_session.origin.net_type), _pj_str_to_str(pj_session.origin.addr_type), _pj_str_to_str(pj_session.name), connection, pj_session.time.start, pj_session.time.stop, frozenlist([FrozenSDPAttribute_create(pj_session.attr[i]) for i in range(pj_session.attr_count)]), frozenlist([FrozenSDPBandwidthInfo_create(pj_session.bandw[i]) for i in range(pj_session.bandw_count)]), frozenlist([FrozenSDPMediaStream_create(pj_session.media[i]) if pj_session.media[i] != NULL else None for i in range(pj_session.media_count)])) cdef SDPMediaStream SDPMediaStream_create(pjmedia_sdp_media *pj_media): cdef SDPConnection connection = None cdef int i if pj_media.conn != NULL: connection = SDPConnection_create(pj_media.conn) return SDPMediaStream(_pj_str_to_str(pj_media.desc.media), pj_media.desc.port, _pj_str_to_str(pj_media.desc.transport), pj_media.desc.port_count, [_pj_str_to_str(pj_media.desc.fmt[i]) for i in range(pj_media.desc.fmt_count)], connection, [SDPAttribute_create(pj_media.attr[i]) for i in range(pj_media.attr_count)], [SDPBandwidthInfo_create(pj_media.bandw[i]) for i in range(pj_media.bandw_count)]) cdef FrozenSDPMediaStream FrozenSDPMediaStream_create(pjmedia_sdp_media *pj_media): cdef FrozenSDPConnection connection = None cdef int i if pj_media.conn != NULL: connection = FrozenSDPConnection_create(pj_media.conn) return FrozenSDPMediaStream(_pj_str_to_str(pj_media.desc.media), pj_media.desc.port, _pj_str_to_str(pj_media.desc.transport), pj_media.desc.port_count, frozenlist([_pj_str_to_str(pj_media.desc.fmt[i]) for i in range(pj_media.desc.fmt_count)]), connection, frozenlist([FrozenSDPAttribute_create(pj_media.attr[i]) for i in range(pj_media.attr_count)]), frozenlist([FrozenSDPBandwidthInfo_create(pj_media.bandw[i]) for i in range(pj_media.bandw_count)])) cdef SDPConnection SDPConnection_create(pjmedia_sdp_conn *pj_conn): return SDPConnection(_pj_str_to_str(pj_conn.addr), _pj_str_to_str(pj_conn.net_type), _pj_str_to_str(pj_conn.addr_type)) cdef FrozenSDPConnection FrozenSDPConnection_create(pjmedia_sdp_conn *pj_conn): return FrozenSDPConnection(_pj_str_to_str(pj_conn.addr), _pj_str_to_str(pj_conn.net_type), _pj_str_to_str(pj_conn.addr_type)) cdef SDPAttribute SDPAttribute_create(pjmedia_sdp_attr *pj_attr): return SDPAttribute(_pj_str_to_str(pj_attr.name), _pj_str_to_str(pj_attr.value)) cdef FrozenSDPAttribute FrozenSDPAttribute_create(pjmedia_sdp_attr *pj_attr): return FrozenSDPAttribute(_pj_str_to_str(pj_attr.name), _pj_str_to_str(pj_attr.value)) cdef SDPBandwidthInfo SDPBandwidthInfo_create(pjmedia_sdp_bandw *pj_bandw): return SDPBandwidthInfo(_pj_str_to_str(pj_bandw.modifier), int(pj_bandw.value)) cdef FrozenSDPBandwidthInfo FrozenSDPBandwidthInfo_create(pjmedia_sdp_bandw *pj_bandw): return FrozenSDPBandwidthInfo(_pj_str_to_str(pj_bandw.modifier), int(pj_bandw.value)) # SDP negotiator # cdef class SDPNegotiator: def __cinit__(self, *args, **kwargs): cdef pj_pool_t *pool cdef bytes pool_name cdef PJSIPUA ua ua = _get_ua() pool_name = b"SDPNegotiator_%d" % id(self) pool = ua.create_memory_pool(pool_name, 4096, 4096) self._pool = pool self._neg = NULL def __dealloc__(self): cdef PJSIPUA ua try: ua = _get_ua() except: return ua.release_memory_pool(self._pool) self._pool = NULL @classmethod def create_with_local_offer(cls, BaseSDPSession sdp_session): cdef int status cdef pjmedia_sdp_neg *neg cdef pj_pool_t *pool cdef SDPNegotiator obj obj = cls() pool = obj._pool status = pjmedia_sdp_neg_create_w_local_offer(pool, sdp_session.get_sdp_session(), &neg) if status != 0: raise PJSIPError("failed to create SDPNegotiator with local offer", status) obj._neg = neg return obj @classmethod def create_with_remote_offer(cls, BaseSDPSession sdp_session): cdef int status cdef pjmedia_sdp_neg *neg cdef pj_pool_t *pool cdef SDPNegotiator obj obj = cls() pool = obj._pool status = pjmedia_sdp_neg_create_w_remote_offer(pool, NULL, sdp_session.get_sdp_session(), &neg) if status != 0: raise PJSIPError("failed to create SDPNegotiator with remote offer", status) obj._neg = neg return obj def __repr__(self): return "%s, state=%s" % (self.__class__.__name__, self.state) property state: def __get__(self): if self._neg == NULL: return None return PyString_FromString(pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_get_state(self._neg))) property active_local: def __get__(self): cdef int status cdef pjmedia_sdp_session_ptr_const sdp if self._neg == NULL: return None status = pjmedia_sdp_neg_get_active_local(self._neg, &sdp) if status != 0: return None return FrozenSDPSession_create(sdp) property active_remote: def __get__(self): cdef int status cdef pjmedia_sdp_session_ptr_const sdp if self._neg == NULL: return None status = pjmedia_sdp_neg_get_active_remote(self._neg, &sdp) if status != 0: return None return FrozenSDPSession_create(sdp) property current_local: def __get__(self): cdef int status cdef pjmedia_sdp_session_ptr_const sdp if self._neg == NULL: return None status = pjmedia_sdp_neg_get_neg_local(self._neg, &sdp) if status != 0: return None return FrozenSDPSession_create(sdp) property current_remote: def __get__(self): cdef int status cdef pjmedia_sdp_session_ptr_const sdp if self._neg == NULL: return None status = pjmedia_sdp_neg_get_neg_remote(self._neg, &sdp) if status != 0: return None return FrozenSDPSession_create(sdp) def _check_self(self): if self._neg == NULL: raise RuntimeError('SDPNegotiator was not properly initialized') def set_local_answer(self, BaseSDPSession sdp_session): self._check_self() cdef int status cdef pj_pool_t *pool = self._pool status = pjmedia_sdp_neg_set_local_answer(pool, self._neg, sdp_session.get_sdp_session()) if status != 0: raise PJSIPError("failed to set local answer", status) def set_local_offer(self, BaseSDPSession sdp_session): # PJSIP has an asymmetric API here. This function will modify the local session with the new SDP and treat it as a local offer. self._check_self() cdef int status cdef pj_pool_t *pool = self._pool status = pjmedia_sdp_neg_modify_local_offer(pool, self._neg, sdp_session.get_sdp_session()) if status != 0: raise PJSIPError("failed to set local offer", status) def set_remote_answer(self, BaseSDPSession sdp_session): self._check_self() cdef int status cdef pj_pool_t *pool = self._pool status = pjmedia_sdp_neg_set_remote_answer(pool, self._neg, sdp_session.get_sdp_session()) if status != 0: raise PJSIPError("failed to set remote answer", status) def set_remote_offer(self, BaseSDPSession sdp_session): self._check_self() cdef int status cdef pj_pool_t *pool = self._pool status = pjmedia_sdp_neg_set_remote_offer(pool, self._neg, sdp_session.get_sdp_session()) if status != 0: raise PJSIPError("failed to set remote offer", status) def cancel_offer(self): self._check_self() cdef int status status = pjmedia_sdp_neg_cancel_offer(self._neg) if status != 0: raise PJSIPError("failed to cancel offer", status) def negotiate(self): self._check_self() cdef int status cdef pj_pool_t *pool = self._pool status = pjmedia_sdp_neg_negotiate(pool, self._neg, 0) if status != 0: raise PJSIPError("SDP negotiation failed", status) ================================================ FILE: sipsimple/core/_core.sound.pxi ================================================ import sys cdef class AudioMixer: def __cinit__(self, *args, **kwargs): cdef int status self._connected_slots = list() self._input_volume = 100 self._output_volume = 100 status = pj_mutex_create_recursive(_get_ua()._pjsip_endpoint._pool, "audio_mixer_lock", &self._lock) if status != 0: raise PJSIPError("failed to create lock", status) def __init__(self, unicode input_device, unicode output_device, int sample_rate, int ec_tail_length, int slot_count=254): global _dealloc_handler_queue cdef int status cdef pj_pool_t *conf_pool cdef pj_pool_t *snd_pool cdef pjmedia_conf **conf_bridge_address cdef pjmedia_port **null_port_address cdef bytes conf_pool_name, snd_pool_name cdef PJSIPUA ua ua = _get_ua() conf_bridge_address = &self._obj null_port_address = &self._null_port if self._obj != NULL: raise SIPCoreError("AudioMixer.__init__() was already called") if ec_tail_length < 0: raise ValueError("ec_tail_length argument cannot be negative") if sample_rate <= 0: raise ValueError("sample_rate argument should be a non-negative integer") if sample_rate % 50: raise ValueError("sample_rate argument should be dividable by 50") self.sample_rate = sample_rate self.slot_count = slot_count conf_pool_name = b"AudioMixer_%d" % id(self) conf_pool = ua.create_memory_pool(conf_pool_name, 4096, 4096) self._conf_pool = conf_pool snd_pool_name = b"AudioMixer_snd_%d" % id(self) snd_pool = ua.create_memory_pool(snd_pool_name, 4096, 4096) self._snd_pool = snd_pool with nogil: status = pjmedia_conf_create(conf_pool, slot_count+1, sample_rate, 1, sample_rate / 50, 16, PJMEDIA_CONF_NO_DEVICE, conf_bridge_address) if status != 0: raise PJSIPError("Could not create audio mixer", status) with nogil: status = pjmedia_null_port_create(conf_pool, sample_rate, 1, sample_rate / 50, 16, null_port_address) if status != 0: raise PJSIPError("Could not create null audio port", status) self._start_sound_device(ua, input_device, output_device, ec_tail_length) if not (input_device is None and output_device is None): self._stop_sound_device(ua) _add_handler(_AudioMixer_dealloc_handler, self, &_dealloc_handler_queue) # properties property input_volume: def __get__(self): return self._input_volume def __set__(self, int value): cdef int status cdef int volume cdef pj_mutex_t *lock = self._lock cdef pjmedia_conf *conf_bridge cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: pass with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: conf_bridge = self._obj if value < 0: raise ValueError("input_volume attribute cannot be negative") if ua is not None: volume = int(value * 1.28 - 128) with nogil: status = pjmedia_conf_adjust_rx_level(conf_bridge, 0, volume) if status != 0: raise PJSIPError("Could not set input volume of sound device", status) if value > 0 and self._muted: self._muted = False self._input_volume = value finally: with nogil: pj_mutex_unlock(lock) property output_volume: def __get__(self): return self._output_volume def __set__(self, int value): cdef int status cdef int volume cdef pj_mutex_t *lock = self._lock cdef pjmedia_conf *conf_bridge cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: pass with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: conf_bridge = self._obj if value < 0: raise ValueError("output_volume attribute cannot be negative") if ua is not None: volume = int(value * 1.28 - 128) with nogil: status = pjmedia_conf_adjust_tx_level(conf_bridge, 0, volume) if status != 0: raise PJSIPError("Could not set output volume of sound device", status) self._output_volume = value finally: with nogil: pj_mutex_unlock(lock) property muted: def __get__(self): return self._muted def __set__(self, bint muted): cdef int status cdef int volume cdef pj_mutex_t *lock = self._lock cdef pjmedia_conf *conf_bridge cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: pass with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: conf_bridge = self._obj if muted == self._muted: return if ua is not None: if muted: volume = -128 else: volume = int(self._input_volume * 1.28 - 128) with nogil: status = pjmedia_conf_adjust_rx_level(conf_bridge, 0, volume) if status != 0: raise PJSIPError("Could not set input volume of sound device", status) self._muted = muted finally: with nogil: pj_mutex_unlock(lock) property connected_slots: def __get__(self): return sorted(self._connected_slots) # public methods def set_sound_devices(self, unicode input_device, unicode output_device, int ec_tail_length): cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if ec_tail_length < 0: raise ValueError("ec_tail_length argument cannot be negative") self._stop_sound_device(ua) self._start_sound_device(ua, input_device, output_device, ec_tail_length) if self.used_slot_count == 0 and not (input_device is None and output_device is None): self._stop_sound_device(ua) finally: with nogil: pj_mutex_unlock(lock) def connect_slots(self, int src_slot, int dst_slot): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_conf *conf_bridge cdef tuple connection cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: conf_bridge = self._obj if src_slot < 0: raise ValueError("src_slot argument cannot be negative") if dst_slot < 0: raise ValueError("dst_slot argument cannot be negative") connection = (src_slot, dst_slot) if connection in self._connected_slots: return with nogil: status = pjmedia_conf_connect_port(conf_bridge, src_slot, dst_slot, 0) if status != 0: raise PJSIPError("Could not connect slots on audio mixer", status) self._connected_slots.append(connection) finally: with nogil: pj_mutex_unlock(lock) def disconnect_slots(self, int src_slot, int dst_slot): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_conf *conf_bridge cdef tuple connection cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: conf_bridge = self._obj if src_slot < 0: raise ValueError("src_slot argument cannot be negative") if dst_slot < 0: raise ValueError("dst_slot argument cannot be negative") connection = (src_slot, dst_slot) if connection not in self._connected_slots: return with nogil: status = pjmedia_conf_disconnect_port(conf_bridge, src_slot, dst_slot) if status != 0: raise PJSIPError("Could not disconnect slots on audio mixer", status) self._connected_slots.remove(connection) finally: with nogil: pj_mutex_unlock(lock) def reset_ec(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._snd == NULL: return with nogil: pjmedia_snd_port_reset_ec_state(self._snd) finally: with nogil: pj_mutex_unlock(lock) # private methods cdef void _start_sound_device(self, PJSIPUA ua, unicode input_device, unicode output_device, int ec_tail_length): cdef int idx cdef int input_device_i = -99 cdef int output_device_i = -99 cdef int sample_rate = self.sample_rate cdef int status cdef pj_pool_t *conf_pool cdef pj_pool_t *snd_pool cdef pjmedia_conf *conf_bridge cdef pjmedia_master_port **master_port_address cdef pjmedia_port *null_port cdef pjmedia_aud_dev_info dev_info cdef pjmedia_snd_port **snd_port_address cdef pjmedia_aud_param aud_param cdef pjmedia_snd_port_param port_param conf_bridge = self._obj conf_pool = self._conf_pool snd_pool = self._snd_pool master_port_address = &self._master_port null_port = self._null_port sample_rate = self.sample_rate snd_port_address = &self._snd with nogil: status = pj_rwmutex_lock_read(ua.audio_change_rwlock) if status != 0: raise PJSIPError('Audio change lock could not be acquired for read', status) try: dev_count = pjmedia_aud_dev_count() if dev_count == 0: input_device = None output_device = None if input_device == u"system_default": input_device_i = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV if output_device == u"system_default": output_device_i = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV if ((input_device_i == -99 and input_device is not None) or (output_device_i == -99 and output_device is not None)): for i in range(dev_count): with nogil: status = pjmedia_aud_dev_get_info(i, &dev_info) if status != 0: raise PJSIPError("Could not get audio device info", status) if (input_device is not None and input_device_i == -99 and dev_info.input_count > 0 and decode_device_name(dev_info.name) == input_device): input_device_i = i if (output_device is not None and output_device_i == -99 and dev_info.output_count > 0 and decode_device_name(dev_info.name) == output_device): output_device_i = i if input_device_i == -99 and input_device is not None: input_device_i = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV if output_device_i == -99 and output_device is not None: output_device_i = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV if input_device is None and output_device is None: with nogil: status = pjmedia_master_port_create(conf_pool, null_port, pjmedia_conf_get_master_port(conf_bridge), 0, master_port_address) if status != 0: raise PJSIPError("Could not create master port for dummy sound device", status) with nogil: status = pjmedia_master_port_start(master_port_address[0]) if status != 0: raise PJSIPError("Could not start master port for dummy sound device", status) else: pjmedia_snd_port_param_default(&port_param) idx = input_device_i if input_device is not None else output_device_i with nogil: status = pjmedia_aud_dev_default_param(idx, &port_param.base) if status != 0: raise PJSIPError("Could not get default parameters for audio device", status) if input_device is None: port_param.base.dir = PJMEDIA_DIR_PLAYBACK port_param.base.play_id = output_device_i elif output_device is None: port_param.base.dir = PJMEDIA_DIR_CAPTURE port_param.base.rec_id = input_device_i else: port_param.base.dir = PJMEDIA_DIR_CAPTURE_PLAYBACK port_param.base.play_id = output_device_i port_param.base.rec_id = input_device_i port_param.base.channel_count = 1 port_param.base.clock_rate = sample_rate port_param.base.samples_per_frame = sample_rate / 50 port_param.base.bits_per_sample = 16 port_param.base.flags |= (PJMEDIA_AUD_DEV_CAP_EC | PJMEDIA_AUD_DEV_CAP_EC_TAIL) port_param.base.ec_enabled = 1 port_param.base.ec_tail_ms = ec_tail_length with nogil: status = pjmedia_snd_port_create2(snd_pool, &port_param, snd_port_address) if status == PJMEDIA_ENOSNDPLAY: ua.reset_memory_pool(snd_pool) self._start_sound_device(ua, input_device, None, ec_tail_length) return elif status == PJMEDIA_ENOSNDREC: ua.reset_memory_pool(snd_pool) self._start_sound_device(ua, None, output_device, ec_tail_length) return elif status != 0: raise PJSIPError("Could not create sound device", status) with nogil: status = pjmedia_snd_port_connect(snd_port_address[0], pjmedia_conf_get_master_port(conf_bridge)) if status != 0: self._stop_sound_device(ua) raise PJSIPError("Could not connect sound device", status) if input_device_i == PJMEDIA_AUD_DEFAULT_CAPTURE_DEV or output_device_i == PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV: with nogil: status = pjmedia_aud_stream_get_param(pjmedia_snd_port_get_snd_stream(snd_port_address[0]), &aud_param) if status != 0: self._stop_sound_device(ua) raise PJSIPError("Could not get sounds device info", status) if input_device_i == PJMEDIA_AUD_DEFAULT_CAPTURE_DEV: with nogil: status = pjmedia_aud_dev_get_info(aud_param.rec_id, &dev_info) if status != 0: raise PJSIPError("Could not get audio device info", status) self.real_input_device = decode_device_name(dev_info.name) if output_device_i == PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV: with nogil: status = pjmedia_aud_dev_get_info(aud_param.play_id, &dev_info) if status != 0: raise PJSIPError("Could not get audio device info", status) self.real_output_device = decode_device_name(dev_info.name) if input_device_i != PJMEDIA_AUD_DEFAULT_CAPTURE_DEV: self.real_input_device = input_device if output_device_i != PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV: self.real_output_device = output_device self.input_device = input_device self.output_device = output_device self.ec_tail_length = ec_tail_length finally: with nogil: pj_rwmutex_unlock_read(ua.audio_change_rwlock) cdef void _stop_sound_device(self, PJSIPUA ua): cdef pjmedia_master_port *master_port cdef pjmedia_snd_port *snd_port master_port = self._master_port snd_port = self._snd if self._snd != NULL: with nogil: pjmedia_snd_port_destroy(snd_port) self._snd = NULL ua.reset_memory_pool(self._snd_pool) if self._master_port != NULL: with nogil: pjmedia_master_port_destroy(master_port, 0) self._master_port = NULL cdef int _add_port(self, PJSIPUA ua, pj_pool_t *pool, pjmedia_port *port) except -1 with gil: cdef int input_device_i cdef int output_device_i cdef unsigned int slot cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_conf* conf_bridge with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: conf_bridge = self._obj with nogil: status = pjmedia_conf_add_port(conf_bridge, pool, port, NULL, &slot) if status != 0: raise PJSIPError("Could not add audio object to audio mixer", status) self.used_slot_count += 1 if self.used_slot_count == 1 and not (self.input_device is None and self.output_device is None) and self._snd == NULL: self._start_sound_device(ua, self.input_device, self.output_device, self.ec_tail_length) return slot finally: with nogil: pj_mutex_unlock(lock) cdef int _remove_port(self, PJSIPUA ua, unsigned int slot) except -1 with gil: cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_conf* conf_bridge cdef tuple connection cdef Timer timer with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: conf_bridge = self._obj with nogil: status = pjmedia_conf_remove_port(conf_bridge, slot) if status != 0: raise PJSIPError("Could not remove audio object from audio mixer", status) self._connected_slots = [connection for connection in self._connected_slots if slot not in connection] self.used_slot_count -= 1 if self.used_slot_count == 0 and not (self.input_device is None and self.output_device is None): timer = Timer() timer.schedule(0, self._cb_postpoll_stop_sound, self) return 0 finally: with nogil: pj_mutex_unlock(lock) cdef int _cb_postpoll_stop_sound(self, timer) except -1: cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.used_slot_count == 0: self._stop_sound_device(ua) finally: with nogil: pj_mutex_unlock(lock) def __dealloc__(self): global _dealloc_handler_queue cdef PJSIPUA ua cdef pjmedia_conf *conf_bridge = self._obj cdef pjmedia_port *null_port = self._null_port _remove_handler(self, &_dealloc_handler_queue) try: ua = _get_ua() except: return self._stop_sound_device(ua) if self._null_port != NULL: with nogil: pjmedia_port_destroy(null_port) self._null_port = NULL if self._obj != NULL: with nogil: pjmedia_conf_destroy(conf_bridge) self._obj = NULL ua.release_memory_pool(self._conf_pool) self._conf_pool = NULL ua.release_memory_pool(self._snd_pool) self._snd_pool = NULL if self._lock != NULL: pj_mutex_destroy(self._lock) cdef class ToneGenerator: # properties property volume: def __get__(self): return self._volume def __set__(self, value): cdef int slot cdef int volume cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_conf *conf_bridge cdef PJSIPUA ua ua = self._get_ua(0) if ua is not None: with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: conf_bridge = self.mixer._obj slot = self._slot if value < 0: raise ValueError("volume attribute cannot be negative") if ua is not None and self._slot != -1: volume = int(value * 1.28 - 128) with nogil: status = pjmedia_conf_adjust_rx_level(conf_bridge, slot, volume) if status != 0: raise PJSIPError("Could not set volume of tone generator", status) self._volume = value finally: if ua is not None: with nogil: pj_mutex_unlock(lock) property slot: def __get__(self): self._get_ua(0) if self._slot == -1: return None else: return self._slot property is_active: def __get__(self): self._get_ua(0) return bool(self._slot != -1) property is_busy: def __get__(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_port *port cdef PJSIPUA ua ua = self._get_ua(0) if ua is None: return False with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: port = self._obj if self._obj == NULL: return False with nogil: status = pjmedia_tonegen_is_busy(port) return bool(status) finally: with nogil: pj_mutex_unlock(lock) # public methods def __cinit__(self, *args, **kwargs): cdef int status cdef pj_pool_t *pool cdef bytes pool_name cdef PJSIPUA ua ua = _get_ua() status = pj_mutex_create_recursive(ua._pjsip_endpoint._pool, "tone_generator_lock", &self._lock) if status != 0: raise PJSIPError("failed to create lock", status) pool_name = b"ToneGenerator_%d" % id(self) pool = ua.create_memory_pool(pool_name, 4096, 4096) self._pool = pool self._slot = -1 self._timer = None self._volume = 100 def __init__(self, AudioMixer mixer): cdef int sample_rate cdef int status cdef pj_pool_t *pool cdef pjmedia_port **port_address cdef PJSIPUA ua ua = _get_ua() pool = self._pool port_address = &self._obj sample_rate = mixer.sample_rate if self._obj != NULL: raise SIPCoreError("ToneGenerator.__init__() was already called") if mixer is None: raise ValueError("mixer argument may not be None") self.mixer = mixer with nogil: status = pjmedia_tonegen_create(pool, sample_rate, 1, sample_rate / 50, 16, 0, port_address) if status != 0: raise PJSIPError("Could not create tone generator", status) def start(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._get_ua(1) with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._slot != -1: return self._slot = self.mixer._add_port(ua, self._pool, self._obj) if self._volume != 100: self.volume = self._volume finally: with nogil: pj_mutex_unlock(lock) def stop(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._get_ua(0) if ua is None: return with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._slot == -1: return self._stop(ua) finally: with nogil: pj_mutex_unlock(lock) def __dealloc__(self): cdef pjmedia_port *port = self._obj cdef PJSIPUA ua ua = self._get_ua(0) if ua is None: return self._stop(ua) if self._obj != NULL: with nogil: pjmedia_tonegen_stop(port) self._obj = NULL ua.release_memory_pool(self._pool) self._pool = NULL if self._lock != NULL: pj_mutex_destroy(self._lock) def play_tones(self, object tones): cdef unsigned int count = 0 cdef int duration cdef int freq1 cdef int freq2 cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_port *port cdef pjmedia_tone_desc tones_arr[PJMEDIA_TONEGEN_MAX_DIGITS] cdef PJSIPUA ua ua = self._get_ua(1) with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: port = self._obj if self._slot == -1: raise SIPCoreError("ToneGenerator has not yet been started") for freq1, freq2, duration in tones: if freq1 == 0 and count > 0: tones_arr[count-1].off_msec += duration else: if count >= PJMEDIA_TONEGEN_MAX_DIGITS: raise SIPCoreError("Too many tones") tones_arr[count].freq1 = freq1 tones_arr[count].freq2 = freq2 tones_arr[count].on_msec = duration tones_arr[count].off_msec = 0 tones_arr[count].volume = 0 tones_arr[count].flags = 0 count += 1 if count > 0: with nogil: status = pjmedia_tonegen_play(port, count, tones_arr, 0) if status != 0 and status != PJ_ETOOMANY: raise PJSIPError("Could not playback tones", status) if self._timer is None: self._timer = Timer() self._timer.schedule(0.250, self._cb_check_done, self) finally: with nogil: pj_mutex_unlock(lock) def play_dtmf(self, str digit): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_port *port cdef pjmedia_tone_digit tone cdef PJSIPUA ua ua = self._get_ua(1) with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: port = self._obj if self._slot == -1: raise SIPCoreError("ToneGenerator has not yet been started") tone.digit = ord(digit) tone.on_msec = 200 tone.off_msec = 50 tone.volume = 0 with nogil: status = pjmedia_tonegen_play_digits(port, 1, &tone, 0) if status != 0 and status != PJ_ETOOMANY: raise PJSIPError("Could not playback DTMF tone", status) if self._timer is None: self._timer = Timer() self._timer.schedule(0.250, self._cb_check_done, self) finally: with nogil: pj_mutex_unlock(lock) # private methods cdef PJSIPUA _get_ua(self, int raise_exception): cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: self._obj = NULL self._pool = NULL self._slot = -1 self._timer = None if raise_exception: raise else: return None else: return ua cdef int _stop(self, PJSIPUA ua) except -1: if self._timer is not None: self._timer.cancel() self._timer = None if self._slot != -1: self.mixer._remove_port(ua, self._slot) self._slot = -1 return 0 cdef int _cb_check_done(self, timer) except -1: cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_port *port with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: port = self._obj with nogil: status = pjmedia_tonegen_is_busy(port) if status: self._timer = Timer() self._timer.schedule(0.250, self._cb_check_done, self) else: self._timer = None _add_event("ToneGeneratorDidFinishPlaying", dict(obj=self)) finally: with nogil: pj_mutex_unlock(lock) cdef class RecordingWaveFile: def __cinit__(self, *args, **kwargs): cdef int status status = pj_mutex_create_recursive(_get_ua()._pjsip_endpoint._pool, "recording_wave_file_lock", &self._lock) if status != 0: raise PJSIPError("failed to create lock", status) self._slot = -1 def __init__(self, AudioMixer mixer, filename): if self.filename is not None: raise SIPCoreError("RecordingWaveFile.__init__() was already called") if mixer is None: raise ValueError("mixer argument may not be None") if filename is None: raise ValueError("filename argument may not be None") if not isinstance(filename, basestring): raise TypeError("file argument must be str or unicode") if isinstance(filename, unicode): filename = filename.encode(sys.getfilesystemencoding()) self.mixer = mixer self.filename = filename cdef PJSIPUA _check_ua(self): cdef PJSIPUA ua try: ua = _get_ua() return ua except: self._pool = NULL self._port = NULL self._slot = -1 return None property is_active: def __get__(self): self._check_ua() return self._slot != -1 property slot: def __get__(self): self._check_ua() if self._slot == -1: return None else: return self._slot def start(self): cdef char *filename cdef int sample_rate cdef int status cdef pj_mutex_t *lock = self._lock cdef pj_pool_t *pool cdef pjmedia_port **port_address cdef bytes pool_name cdef char* c_pool_name cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: filename = PyString_AsString(self.filename) pool_name = b"RecordingWaveFile_%d" % id(self) port_address = &self._port sample_rate = self.mixer.sample_rate if self._was_started: raise SIPCoreError("This RecordingWaveFile was already started once") pool = ua.create_memory_pool(pool_name, 4096, 4096) self._pool = pool try: with nogil: status = pjmedia_wav_writer_port_create(pool, filename, sample_rate, 1, sample_rate / 50, 16, PJMEDIA_FILE_WRITE_PCM, 0, port_address) if status != 0: raise PJSIPError("Could not create WAV file", status) self._slot = self.mixer._add_port(ua, self._pool, self._port) except: self.stop() raise self._was_started = 1 finally: with nogil: pj_mutex_unlock(lock) def stop(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: self._stop(ua) finally: with nogil: pj_mutex_unlock(lock) cdef int _stop(self, PJSIPUA ua) except -1: cdef pjmedia_port *port = self._port if self._slot != -1: self.mixer._remove_port(ua, self._slot) self._slot = -1 if self._port != NULL: with nogil: pjmedia_port_destroy(port) self._port = NULL ua.release_memory_pool(self._pool) self._pool = NULL return 0 def __dealloc__(self): cdef PJSIPUA ua try: ua = _get_ua() except: return self._stop(ua) if self._lock != NULL: pj_mutex_destroy(self._lock) cdef class WaveFile: def __cinit__(self, *args, **kwargs): cdef int status self.weakref = weakref.ref(self) Py_INCREF(self.weakref) status = pj_mutex_create_recursive(_get_ua()._pjsip_endpoint._pool, "wave_file_lock", &self._lock) if status != 0: raise PJSIPError("failed to create lock", status) self._slot = -1 self._volume = 100 def __init__(self, AudioMixer mixer, filename): if self.filename is not None: raise SIPCoreError("WaveFile.__init__() was already called") if mixer is None: raise ValueError("mixer argument may not be None") if filename is None: raise ValueError("filename argument may not be None") if not isinstance(filename, basestring): raise TypeError("file argument must be str or unicode") if isinstance(filename, unicode): filename = filename.encode(sys.getfilesystemencoding()) self.mixer = mixer self.filename = filename cdef PJSIPUA _check_ua(self): cdef PJSIPUA ua try: ua = _get_ua() return ua except: self._pool = NULL self._port = NULL self._slot = -1 return None property is_active: def __get__(self): self._check_ua() return self._port != NULL property slot: def __get__(self): self._check_ua() if self._slot == -1: return None else: return self._slot property volume: def __get__(self): return self._volume def __set__(self, value): cdef int slot cdef int status cdef int volume cdef pj_mutex_t *lock = self._lock cdef pjmedia_conf *conf_bridge cdef PJSIPUA ua ua = self._check_ua() if ua is not None: with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: conf_bridge = self.mixer._obj slot = self._slot if value < 0: raise ValueError("volume attribute cannot be negative") if ua is not None and self._slot != -1: volume = int(value * 1.28 - 128) with nogil: status = pjmedia_conf_adjust_rx_level(conf_bridge, slot, volume) if status != 0: raise PJSIPError("Could not set volume of .wav file", status) self._volume = value finally: if ua is not None: with nogil: pj_mutex_unlock(lock) def start(self): cdef char *filename cdef int status cdef void *weakref cdef pj_pool_t *pool cdef pj_mutex_t *lock = self._lock cdef pjmedia_port **port_address cdef bytes pool_name cdef char* c_pool_name cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: filename = PyString_AsString(self.filename) port_address = &self._port weakref = self.weakref if self._port != NULL: raise SIPCoreError("WAV file is already playing") pool_name = b"WaveFile_%d" % id(self) pool = ua.create_memory_pool(pool_name, 4096, 4096) self._pool = pool try: with nogil: status = pjmedia_wav_player_port_create(pool, filename, 0, PJMEDIA_FILE_NO_LOOP, 0, port_address) if status != 0: raise PJSIPError("Could not open WAV file", status) with nogil: status = pjmedia_wav_player_set_eof_cb(port_address[0], weakref, cb_play_wav_eof) if status != 0: raise PJSIPError("Could not set WAV EOF callback", status) self._slot = self.mixer._add_port(ua, self._pool, self._port) if self._volume != 100: self.volume = self._volume except: self._stop(ua, 0) raise finally: with nogil: pj_mutex_unlock(lock) cdef int _stop(self, PJSIPUA ua, int notify) except -1: cdef int status cdef int was_active cdef pj_pool_t *pool cdef pjmedia_port *port port = self._port was_active = 0 if self._slot != -1: was_active = 1 self.mixer._remove_port(ua, self._slot) self._slot = -1 if self._port != NULL: with nogil: pjmedia_port_destroy(port) self._port = NULL was_active = 1 ua.release_memory_pool(self._pool) self._pool = NULL if notify and was_active: _add_event("WaveFileDidFinishPlaying", dict(obj=self)) def stop(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: self._stop(ua, 1) finally: with nogil: pj_mutex_unlock(lock) def __dealloc__(self): cdef PJSIPUA ua cdef Timer timer try: ua = _get_ua() except: return self._stop(ua, 0) timer = Timer() try: timer.schedule(60, deallocate_weakref, self.weakref) except SIPCoreError: pass if self._lock != NULL: pj_mutex_destroy(self._lock) cdef int _cb_eof(self, timer) except -1: cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: self._stop(ua, 1) finally: with nogil: pj_mutex_unlock(lock) cdef class MixerPort: def __cinit__(self, *args, **kwargs): cdef int status status = pj_mutex_create_recursive(_get_ua()._pjsip_endpoint._pool, "mixer_port_lock", &self._lock) if status != 0: raise PJSIPError("failed to create lock", status) self._slot = -1 def __init__(self, AudioMixer mixer): if self.mixer is not None: raise SIPCoreError("MixerPort.__init__() was already called") if mixer is None: raise ValueError("mixer argument may not be None") self.mixer = mixer cdef PJSIPUA _check_ua(self): cdef PJSIPUA ua try: ua = _get_ua() return ua except: self._pool = NULL self._port = NULL self._slot = -1 return None property is_active: def __get__(self): self._check_ua() return self._slot != -1 property slot: def __get__(self): self._check_ua() if self._slot == -1: return None else: return self._slot def start(self): cdef int sample_rate cdef int status cdef pj_mutex_t *lock = self._lock cdef pj_pool_t *pool cdef pjmedia_port **port_address cdef bytes pool_name cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: pool_name = b"MixerPort_%d" % id(self) port_address = &self._port sample_rate = self.mixer.sample_rate if self._was_started: raise SIPCoreError("This MixerPort was already started once") pool = ua.create_memory_pool(pool_name, 4096, 4096) self._pool = pool try: with nogil: status = pjmedia_mixer_port_create(pool, sample_rate, 1, sample_rate / 50, 16, port_address) if status != 0: raise PJSIPError("Could not create WAV file", status) self._slot = self.mixer._add_port(ua, self._pool, self._port) except: self.stop() raise self._was_started = 1 finally: with nogil: pj_mutex_unlock(lock) def stop(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: self._stop(ua) finally: with nogil: pj_mutex_unlock(lock) cdef int _stop(self, PJSIPUA ua) except -1: cdef pj_pool_t *pool cdef pjmedia_port *port pool = self._pool port = self._port if self._slot != -1: self.mixer._remove_port(ua, self._slot) self._slot = -1 if self._port != NULL: with nogil: pjmedia_port_destroy(port) self._port = NULL ua.release_memory_pool(self._pool) self._pool = NULL return 0 def __dealloc__(self): cdef PJSIPUA ua try: ua = _get_ua() except: return self._stop(ua) if self._lock != NULL: pj_mutex_destroy(self._lock) # callback functions cdef int _AudioMixer_dealloc_handler(object obj) except -1: cdef int status cdef AudioMixer mixer = obj cdef PJSIPUA ua ua = _get_ua() status = pj_mutex_lock(mixer._lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: mixer._stop_sound_device(ua) mixer._connected_slots = list() mixer.used_slot_count = 0 finally: pj_mutex_unlock(mixer._lock) cdef int cb_play_wav_eof(pjmedia_port *port, void *user_data) with gil: cdef Timer timer cdef WaveFile wav_file wav_file = ( user_data)() if wav_file is not None: timer = Timer() timer.schedule(0, wav_file._cb_eof, wav_file) # do not return PJ_SUCCESS because if you do pjsip will access the just deallocated port return 1 ================================================ FILE: sipsimple/core/_core.subscription.pxi ================================================ import re cdef class Subscription: expire_warning_time = 30 #public methods def __cinit__(self, *args, **kwargs): self.state = "NULL" pj_timer_entry_init(&self._timeout_timer, 0, self, _Subscription_cb_timer) self._timeout_timer_active = 0 pj_timer_entry_init(&self._refresh_timer, 1, self, _Subscription_cb_timer) self._refresh_timer_active = 0 self.extra_headers = frozenlist() self.peer_address = None self.call_id = None def __init__(self, SIPURI request_uri not None, FromHeader from_header not None, ToHeader to_header not None, ContactHeader contact_header not None, object event, RouteHeader route_header not None, Credentials credentials=None, int refresh=300): global _subs_cb cdef PJSTR from_header_str cdef PJSTR to_header_str cdef PJSTR contact_str cdef PJSTR request_uri_str cdef pj_str_t event_pj cdef pjsip_cred_info *cred_info cdef PJSIPUA ua = _get_ua() cdef int status if self._obj != NULL or self.state != "NULL": raise SIPCoreError("Subscription.__init__() was already called") if refresh <= 0: raise ValueError("refresh argument needs to be a non-negative integer") if event not in ua._events.iterkeys(): raise ValueError('Unknown event "%s"' % event) self.contact_header = FrozenContactHeader.new(contact_header) self.event = event self.route_header = FrozenRouteHeader.new(route_header) self.route_header.uri.parameters.dict["lr"] = None # always send lr parameter in Route header self.route_header.uri.parameters.dict["hide"] = None # always hide Route header if credentials is not None: self.credentials = FrozenCredentials.new(credentials) self.refresh = refresh from_header_parameters = from_header.parameters.copy() from_header_parameters.pop("tag", None) from_header.parameters = {} from_header_str = PJSTR(from_header.body) to_header_parameters = to_header.parameters.copy() to_header_parameters.pop("tag", None) to_header.parameters = {} to_header_str = PJSTR(to_header.body) contact_str = PJSTR(str(contact_header.body)) request_uri_str = PJSTR(str(request_uri)) _str_to_pj_str(self.event, &event_pj) with nogil: status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from_header_str.pj_str, &contact_str.pj_str, &to_header_str.pj_str, &request_uri_str.pj_str, &self._dlg) if status != 0: raise PJSIPError("Could not create dialog for SUBSCRIBE", status) # Increment dialog session count so that it's never destroyed by PJSIP with nogil: status = pjsip_dlg_inc_session(self._dlg, &ua._module) if status != 0: raise PJSIPError("Could not increment dialog session count", status) self.call_id = _pj_str_to_str(self._dlg.call_id.id) if contact_header.expires is not None: self._dlg.local.contact.expires = contact_header.expires if contact_header.q is not None: self._dlg.local.contact.q1000 = int(contact_header.q*1000) contact_parameters = contact_header.parameters.copy() contact_parameters.pop("q", None) contact_parameters.pop("expires", None) _dict_to_pjsip_param(contact_parameters, &self._dlg.local.contact.other_param, self._dlg.pool) _dict_to_pjsip_param(from_header_parameters, &self._dlg.local.info.other_param, self._dlg.pool) _dict_to_pjsip_param(to_header_parameters, &self._dlg.remote.info.other_param, self._dlg.pool) self.from_header = FrozenFromHeader_create(self._dlg.local.info) self.to_header = FrozenToHeader.new(to_header) with nogil: status = pjsip_evsub_create_uac(self._dlg, &_subs_cb, &event_pj, PJSIP_EVSUB_NO_EVENT_ID, &self._obj) if status != 0: raise PJSIPError("Could not create SUBSCRIBE", status) pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, self) _BaseRouteHeader_to_pjsip_route_hdr(self.route_header, &self._route_header, self._dlg.pool) pj_list_init( &self._route_set) pj_list_insert_after( &self._route_set, &self._route_header) with nogil: status = pjsip_dlg_set_route_set(self._dlg, &self._route_set) if status != 0: raise PJSIPError("Could not set route on SUBSCRIBE", status) if self.credentials is not None: cred_info = self.credentials.get_cred_info() with nogil: status = pjsip_auth_clt_set_credentials(&self._dlg.auth_sess, 1, cred_info) if status != 0: raise PJSIPError("Could not set credentials for SUBSCRIBE", status) def __dealloc__(self): cdef PJSIPUA ua = self._get_ua() if ua is not None: self._cancel_timers(ua, 1, 1) if self._obj != NULL: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) with nogil: pjsip_evsub_terminate(self._obj, 0) self._obj = NULL if self._dlg != NULL and ua is not None: with nogil: pjsip_dlg_dec_session(self._dlg, &ua._module) self._dlg = NULL def subscribe(self, list extra_headers not None=list(), object content_type=None, object body=None, object timeout=None): cdef object prev_state = self.state cdef PJSIPUA ua = self._get_ua() with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state == "TERMINATED": raise SIPCoreError('This method may not be called in the "TERMINATED" state') if (content_type is not None and body is None) or (content_type is None and body is not None): raise ValueError("Both or none of content_type and body arguments need to be specified") if timeout is not None: if timeout <= 0: raise ValueError("Timeout value cannot be negative") self._subscribe_timeout.sec = int(timeout) self._subscribe_timeout.msec = (timeout * 1000) % 1000 else: self._subscribe_timeout.sec = 0 self._subscribe_timeout.msec = 0 if extra_headers is not None: self.extra_headers = frozenlist([header.frozen_type.new(header) for header in extra_headers]) self.content_type = content_type self.body = body self._send_subscribe(ua, self.refresh, &self._subscribe_timeout, self.extra_headers, content_type, body) self._cancel_timers(ua, 0, 1) if prev_state == "NULL": _add_event("SIPSubscriptionWillStart", dict(obj=self)) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def end(self, object timeout=None): cdef pj_time_val end_timeout cdef PJSIPUA ua = self._get_ua() with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state == "TERMINATED": return if self.state == "NULL": raise SIPCoreError('This method may not be called in the "NULL" state') if timeout is not None: if timeout <= 0: raise ValueError("Timeout value cannot be negative") end_timeout.sec = int(timeout) end_timeout.msec = (timeout * 1000) % 1000 else: end_timeout.sec = 0 end_timeout.msec = 0 self._want_end = 1 self._cancel_timers(ua, 1, 1) _add_event("SIPSubscriptionWillEnd", dict(obj=self)) try: self._send_subscribe(ua, 0, &end_timeout, self.extra_headers, None, None) except PJSIPError, e: self._term_reason = e.args[0] if self._obj != NULL: with nogil: pjsip_evsub_terminate(self._obj, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) # private methods cdef PJSIPUA _get_ua(self): cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: self._obj = NULL self._timeout_timer_active = 0 self._refresh_timer_active = 0 self.state = "TERMINATED" return None else: return ua cdef int _cancel_timers(self, PJSIPUA ua, int cancel_timeout, int cancel_refresh) except -1: if cancel_timeout and self._timeout_timer_active: pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timeout_timer) self._timeout_timer_active = 0 if cancel_refresh and self._refresh_timer_active: pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._refresh_timer) self._refresh_timer_active = 0 cdef int _send_subscribe(self, PJSIPUA ua, int expires, pj_time_val *timeout, object extra_headers, object content_type, object body) except -1: cdef pjsip_tx_data *tdata cdef pj_str_t body_pj cdef object content_type_spl cdef PJSTR content_type_str cdef PJSTR content_subtype_str cdef int status if body is not None: content_type_spl = content_type.split("/") if len(content_type_spl) != 2: raise ValueError('Supplied content_type argument does not contain a "/" character') content_type_str = PJSTR(content_type_spl[0]) content_subtype_str = PJSTR(content_type_spl[1]) _str_to_pj_str(body, &body_pj) with nogil: status = pjsip_evsub_initiate(self._obj, NULL, expires, &tdata) if status != 0: raise PJSIPError("Could not create SUBSCRIBE message", status) _add_headers_to_tdata(tdata, extra_headers) if body is not None: tdata.msg.body = pjsip_msg_body_create(tdata.pool, &content_type_str.pj_str, &content_subtype_str.pj_str, &body_pj) with nogil: status = pjsip_evsub_send_request(self._obj, tdata) if status != 0: raise PJSIPError("Could not send SUBSCRIBE message", status) self._cancel_timers(ua, 1, 0) if timeout.sec or timeout.msec: status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timeout_timer, timeout) if status == 0: self._timeout_timer_active = 1 self._expires = self.refresh # callback methods cdef int _cb_state(self, PJSIPUA ua, object state, int code, object reason, dict headers) except -1: # PJSIP holds the dialog lock when this callback is entered cdef object prev_state = self.state cdef int expires cdef int status cdef pj_time_val end_timeout self.state = state if state == "ACCEPTED" and prev_state == "SENT": try: contact_header = headers['Contact'][0] except LookupError: self._term_code = 1400 self._term_reason = "Contact header missing" with nogil: pjsip_evsub_terminate(self._obj, 1) return 0 _add_event("SIPSubscriptionDidStart", dict(obj=self)) try: expires = int(headers["Expires"]) except (KeyError, ValueError): return 0 if expires == 0: self._want_end = 1 self._cancel_timers(ua, 1, 1) end_timeout.sec = 1 end_timeout.msec = 0 _add_event("SIPSubscriptionWillEnd", dict(obj=self)) try: self._send_subscribe(ua, 0, &end_timeout, self.extra_headers, None, None) except PJSIPError, e: self._term_reason = e.args[0] if self._obj != NULL: with nogil: pjsip_evsub_terminate(self._obj, 1) return 0 elif state == "TERMINATED": pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) self._cancel_timers(ua, 1, 1) self._obj = NULL if self._want_end: _add_event("SIPSubscriptionDidEnd", dict(obj=self)) else: min_expires = headers.get('Min-Expires') if self._term_reason is not None: _add_event("SIPSubscriptionDidFail", dict(obj=self, code=self._term_code, reason=self._term_reason, min_expires=min_expires)) else: subscription_state = headers.get('Subscription-State') if subscription_state is not None and subscription_state.state == 'terminated': reason = subscription_state.reason _add_event("SIPSubscriptionDidFail", dict(obj=self, code=code, reason=reason, min_expires=min_expires)) if prev_state != state: _add_event("SIPSubscriptionChangedState", dict(obj=self, prev_state=prev_state, state=state)) cdef int _cb_got_response(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: # PJSIP holds the dialog lock when this callback is entered cdef dict event_dict = dict() cdef int expires = self._expires cdef int status cdef pj_time_val refresh _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) self.to_header = FrozenToHeader_create(rdata.msg_info.to_hdr) if self.state != "TERMINATED": try: contact_header = event_dict["headers"]["Contact"][0] except LookupError: return 0 try: expires = int(event_dict["headers"]["Expires"]) except (KeyError, ValueError): expires = self._expires if expires == 0: return 0 if self.state != "TERMINATED" and not self._want_end: self._cancel_timers(ua, 1, 0) refresh.sec = max(1, expires - self.expire_warning_time, expires/2) refresh.msec = 0 status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._refresh_timer, &refresh) if status == 0: self._refresh_timer_active = 1 cdef int _cb_notify(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: # PJSIP holds the dialog lock when this callback is entered cdef dict event_dict = dict() cdef dict notify_dict = dict(obj=self) _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) body = event_dict["body"] content_type = event_dict["headers"].get("Content-Type", None) event = event_dict["headers"].get("Event", None) if event is None or event.event != self.event or (body is not None and content_type is not None and content_type.content_type not in ua.events[event.event]): return 0 notify_dict["request_uri"] = event_dict["request_uri"] notify_dict["from_header"] = event_dict["headers"].get("From", None) notify_dict["to_header"] = event_dict["headers"].get("To", None) notify_dict["headers"] = event_dict["headers"] notify_dict["body"] = body notify_dict["content_type"] = content_type.content_type if content_type and body else None notify_dict["event"] = event.event _add_event("SIPSubscriptionGotNotify", notify_dict) cdef int _cb_timeout_timer(self, PJSIPUA ua): # Timer callback, dialog lock is not held by PJSIP global sip_status_messages with nogil: pjsip_dlg_inc_lock(self._dlg) try: self._term_code = PJSIP_SC_TSX_TIMEOUT self._term_reason = sip_status_messages[PJSIP_SC_TSX_TIMEOUT] if self._obj != NULL: with nogil: pjsip_evsub_terminate(self._obj, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) cdef int _cb_refresh_timer(self, PJSIPUA ua): # Timer callback, dialog lock is not held by PJSIP with nogil: pjsip_dlg_inc_lock(self._dlg) try: self._send_subscribe(ua, self.refresh, &self._subscribe_timeout, self.extra_headers, self.content_type, self.body) except PJSIPError, e: self._term_reason = e.args[0] if self._obj != NULL: with nogil: pjsip_evsub_terminate(self._obj, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) cdef class IncomingSubscription: # properties property content_type: def __get__(self): if self._content_type is None: return None return "%s/%s" % (self._content_type.str, self._content_subtype.str) property content: def __get__(self): if self._content is None: return None return self._content.str def __cinit__(self): self.state = None self.peer_address = None self.call_id = None def __dealloc__(self): cdef PJSIPUA ua = self._get_ua(0) self._initial_response = NULL self._initial_tsx = NULL if self._obj != NULL: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) with nogil: pjsip_evsub_terminate(self._obj, 0) self._obj = NULL if self._dlg != NULL and ua is not None: with nogil: pjsip_dlg_dec_session(self._dlg, &ua._module) self._dlg = NULL cdef int init(self, PJSIPUA ua, pjsip_rx_data *rdata, str event) except -1: global _incoming_subs_cb cdef int status cdef str transport cdef FrozenSIPURI request_uri cdef FrozenContactHeader contact_header cdef PJSTR contact_str cdef dict event_dict cdef pjsip_expires_hdr *expires_header cdef char *error_message expires_header = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_EXPIRES, NULL) if expires_header == NULL: self._expires = 3600 else: self._expires = min(expires_header.ivalue, 3600) self._set_state("incoming") self.event = event self.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) event_dict = dict(obj=self) _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) transport = rdata.tp_info.transport.type_name.lower() request_uri = event_dict["request_uri"] if _is_valid_ip(pj_AF_INET(), request_uri.host): contact_header = FrozenContactHeader(request_uri) else: contact_header = FrozenContactHeader(FrozenSIPURI(host=_pj_str_to_str(rdata.tp_info.transport.local_name.host), user=request_uri.user, port=rdata.tp_info.transport.local_name.port, parameters=(frozendict(transport=transport) if transport != "udp" else frozendict()))) contact_str = PJSTR(str(contact_header.body)) with nogil: status = pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(), rdata, &contact_str.pj_str, &self._dlg) if status != 0: error_message = "Could not create dialog for incoming SUBSCRIBE" else: pjsip_dlg_inc_session(self._dlg, &ua._module) # Increment dialog session count so it's never destroyed by PJSIP # setting the transport to rdata.tp_info.transport doesn't work as the NOTIFY has to be sent to the Contact URI and the transports can conflict if status != 0: raise PJSIPError(error_message, status) self._initial_tsx = pjsip_rdata_get_tsx(rdata) self.call_id = _pj_str_to_str(self._dlg.call_id.id) with nogil: status = pjsip_evsub_create_uas(self._dlg, &_incoming_subs_cb, rdata, 0, &self._obj) pjsip_dlg_dec_lock(self._dlg) if status != 0: pjsip_tsx_terminate(self._initial_tsx, 500) self._initial_tsx = NULL self._dlg = NULL error_message = "Could not create incoming SUBSCRIBE session" else: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, self) status = pjsip_dlg_create_response(self._dlg, rdata, 500, NULL, &self._initial_response) if status != 0: pjsip_tsx_terminate(self._initial_tsx, 500) self._initial_tsx = NULL error_message = "Could not create response for incoming SUBSCRIBE" if status != 0: raise PJSIPError(error_message, status) _add_event("SIPIncomingSubscriptionGotSubscribe", event_dict) return 0 def reject(self, int code): cdef PJSIPUA ua = self._get_ua(1) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "incoming": raise SIPCoreInvalidStateError('Can only reject an incoming SUBSCRIBE in the "incoming" state, '+ 'object is currently in the "%s" state' % self.state) if not (300 <= code < 700): raise ValueError("Invalid negative SIP response code: %d" % code) self._send_initial_response(code) pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) with nogil: pjsip_evsub_terminate(self._obj, 0) self._obj = NULL self._set_state("terminated") _add_event("SIPIncomingSubscriptionDidEnd", dict(obj=self)) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def accept_pending(self): cdef PJSIPUA ua = self._get_ua(1) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "incoming": raise SIPCoreInvalidStateError('Can only accept an incoming SUBSCRIBE as pending in the "incoming" state, '+ 'object is currently in the "%s" state' % self.state) self._send_initial_response(202) self._set_state("pending") if self._expires > 0: self._send_notify() else: # cleanup will be done by _cb_tsx self._terminate(ua, "timeout", 0) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def accept(self, str content_type=None, str content=None): global _re_content_type cdef object content_type_match cdef PJSIPUA ua = self._get_ua(1) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state not in ("incoming", "pending"): raise SIPCoreInvalidStateError('Can only accept an incoming SUBSCRIBE in the "incoming" or "pending" state, object is currently in the "%s" state' % self.state) if (content_type is None and content is not None) or (content_type is not None and content is None): raise ValueError('Either both or neither of the "content_type" and "content" arguments should be specified') if content_type is not None: content_type_match = _re_content_type.match(content_type) if content_type_match is None: raise ValueError("content_type parameter is not properly formatted") self._content_type = PJSTR(content_type_match.group(1)) self._content_subtype = PJSTR(content_type_match.group(2)) self._content = PJSTR(content) if self.state == "incoming": self._send_initial_response(200) self._set_state("active") if self._expires > 0: self._send_notify() else: # cleanup will be done by _cb_tsx self._terminate(ua, "timeout", 0) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def push_content(self, str content_type not None, str content not None): global _re_content_type cdef object content_type_match cdef PJSIPUA ua = self._get_ua(1) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "active": raise SIPCoreInvalidStateError('Can only push the content for a SUBSCRIBE session in the "active" state, ' 'object is currently in the "%s" state' % self.state) content_type_match = _re_content_type.match(content_type) if content_type_match is None: raise ValueError("content_type parameter is not properly formatted") self._content_type = PJSTR(content_type_match.group(1)) self._content_subtype = PJSTR(content_type_match.group(2)) self._content = PJSTR(content) self._send_notify() finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def end(self, reason=None): cdef PJSIPUA ua = self._get_ua(0) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state == "terminated": return if self.state not in ("pending", "active"): raise SIPCoreInvalidStateError('Can only end an incoming SUBSCRIBE session in the "pending" or '+ '"active" state, object is currently in the "%s" state' % self.state) self._terminate(ua, reason, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) cdef int _set_state(self, str state) except -1: cdef str prev_state prev_state = self.state self.state = state if prev_state != state and prev_state is not None: _add_event("SIPIncomingSubscriptionChangedState", dict(obj=self, prev_state=prev_state, state=state)) cdef PJSIPUA _get_ua(self, int raise_exception): cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: self._obj = NULL self._initial_response = NULL self._initial_tsx = NULL self._set_state("terminated") if raise_exception: raise else: return None else: return ua cdef int _send_initial_response(self, int code) except -1: cdef PJSIPUA ua = self._get_ua(1) cdef int status with nogil: status = pjsip_dlg_modify_response(self._dlg, self._initial_response, code, NULL) if status != 0: raise PJSIPError("Could not modify response", status) # pjsip_dlg_modify_response() increases ref count unncessarily with nogil: pjsip_tx_data_dec_ref(self._initial_response) if code / 100 == 2: pjsip_msg_add_hdr(self._initial_response.msg, pjsip_expires_hdr_create(self._initial_response.pool, self._expires)) with nogil: status = pjsip_dlg_send_response(self._dlg, self._initial_tsx, self._initial_response) if status != 0: raise PJSIPError("Could not send response", status) self._initial_response = NULL self._initial_tsx = NULL if self._expires > 0: with nogil: # Start TIMER_TYPE_UAS_TIMEOUT, which PJSIP doesn't do for the initial SUBSCRIBE pjsip_evsub_set_timer(self._obj, 2, self._expires) cdef int _send_notify(self, str reason=None) except -1: cdef pjsip_evsub_state state cdef pj_str_t reason_pj cdef pj_str_t *reason_p cdef pjsip_tx_data *tdata cdef int status reason_p = NULL if self.state == "pending": state = PJSIP_EVSUB_STATE_PENDING elif self.state == "active": state = PJSIP_EVSUB_STATE_ACTIVE else: state = PJSIP_EVSUB_STATE_TERMINATED if reason is not None: _str_to_pj_str(reason, &reason_pj) reason_p = &reason_pj with nogil: status = pjsip_evsub_notify(self._obj, state, NULL, reason_p, &tdata) if status != 0: raise PJSIPError("Could not create NOTIFY request", status) if self.state == "active" and None not in (self._content_type, self._content_subtype, self._content): tdata.msg.body = pjsip_msg_body_create(tdata.pool, &self._content_type.pj_str, &self._content_subtype.pj_str, &self._content.pj_str) with nogil: status = pjsip_evsub_send_request(self._obj, tdata) if status != 0: raise PJSIPError("Could not send NOTIFY request", status) event_dict = dict(obj=self) _pjsip_msg_to_dict(tdata.msg, event_dict) _add_event("SIPIncomingSubscriptionSentNotify", event_dict) return 0 cdef int _terminate(self, PJSIPUA ua, str reason, int do_cleanup) except -1: cdef int status self._set_state("terminated") try: self._send_notify(reason) except SIPCoreError: pass if do_cleanup: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) self._obj = NULL _add_event("SIPIncomingSubscriptionDidEnd", dict(obj=self)) # callback methods cdef int _cb_rx_refresh(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: # PJSIP holds the dialog lock when this callback is entered cdef int status cdef pjsip_expires_hdr *expires_header cdef int expires cdef dict event_dict event_dict = dict(obj=self) _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) expires_header = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_EXPIRES, NULL) if expires_header == NULL: self._expires = 3600 else: if expires_header.ivalue == 0: _add_event("SIPIncomingSubscriptionGotUnsubscribe", event_dict) # cleanup will be done by _cb_tsx self._terminate(ua, None, 0) return 200 else: self._expires = min(expires_header.ivalue, 3600) _add_event("SIPIncomingSubscriptionGotRefreshingSubscribe", event_dict) try: self._send_notify() except SIPCoreError, e: _add_event("SIPIncomingSubscriptionNotifyDidFail", dict(obj=self, code=0, reason=e.args[0])) if self.state == "active": return 200 else: return 202 cdef int _cb_server_timeout(self, PJSIPUA ua) except -1: # PJSIP holds the dialog lock when this callback is entered _add_event("SIPIncomingSubscriptionDidTimeout", dict(obj=self)) self._terminate(ua, "timeout", 1) cdef int _cb_tsx(self, PJSIPUA ua, pjsip_event *event) except -1: # PJSIP holds the dialog lock when this callback is entered cdef pjsip_rx_data *rdata cdef dict event_dict cdef int status_code if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "NOTIFY" and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED): event_dict = dict(obj=self) rdata = event.body.tsx_state.src.rdata if rdata != NULL: if self.peer_address is None: self.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: self.peer_address.ip = rdata.pkt_info.src_name self.peer_address.port = rdata.pkt_info.src_port status_code = event.body.tsx_state.tsx.status_code if event.body.tsx_state.type==PJSIP_EVENT_RX_MSG and status_code/100==2: _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) _add_event("SIPIncomingSubscriptionNotifyDidSucceed", event_dict) else: if event.body.tsx_state.type == PJSIP_EVENT_RX_MSG: _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) else: event_dict["code"] = status_code event_dict["reason"] = _pj_str_to_str(event.body.tsx_state.tsx.status_text) _add_event("SIPIncomingSubscriptionNotifyDidFail", event_dict) if status_code in (408, 481) or status_code/100==7: # PJSIP will terminate the subscription and the dialog will be destroyed self._terminate(ua, None, 1) elif (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "NOTIFY" and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_TERMINATED): event_dict = dict(obj=self) status_code = event.body.tsx_state.tsx.status_code if status_code == 408: # Local timeout, PJSIP will terminate the subscription and the dialog will be destroyed event_dict["code"] = status_code event_dict["reason"] = _pj_str_to_str(event.body.tsx_state.tsx.status_text) _add_event("SIPIncomingSubscriptionNotifyDidFail", event_dict) self._terminate(ua, None, 1) elif (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAS and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "SUBSCRIBE" and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED and event.body.tsx_state.type == PJSIP_EVENT_TX_MSG): event_dict = dict(obj=self) _pjsip_msg_to_dict(event.body.tsx_state.src.tdata.msg, event_dict) _add_event("SIPIncomingSubscriptionAnsweredSubscribe", event_dict) if self.state == "terminated" and self._obj != NULL: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) self._obj = NULL # callback functions cdef void _Subscription_cb_state(pjsip_evsub *sub, pjsip_event *event) with gil: cdef void *subscription_void cdef Subscription subscription cdef object state cdef int code = 0 cdef object reason = None cdef pjsip_rx_data *rdata = NULL cdef PJSIPUA ua try: ua = _get_ua() except: return try: subscription_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if subscription_void == NULL: return subscription = subscription_void state = pjsip_evsub_get_state_name(sub) if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and (event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED or event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_TERMINATED)): if state == "TERMINATED": if event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC: code = event.body.tsx_state.tsx.status_code reason = _pj_str_to_str(event.body.tsx_state.tsx.status_text) else: code = 0 reason = None if event.body.tsx_state.type == PJSIP_EVENT_RX_MSG and _pj_str_to_str(event.body.tsx_state.tsx.method.name) in ("SUBSCRIBE", "NOTIFY"): rdata = event.body.tsx_state.src.rdata headers_dict = dict() if rdata != NULL: rdata_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) headers_dict = rdata_dict.get('headers', {}) subscription._cb_state(ua, state, code, reason, headers_dict) except: ua._handle_exception(1) cdef void _Subscription_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil: cdef void *subscription_void cdef Subscription subscription cdef pjsip_rx_data *rdata cdef PJSIPUA ua try: ua = _get_ua() except: return try: subscription_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if subscription_void == NULL: return subscription = subscription_void if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.type == PJSIP_EVENT_RX_MSG and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "SUBSCRIBE" and event.body.tsx_state.tsx.status_code / 100 == 2): rdata = event.body.tsx_state.src.rdata if rdata != NULL: if subscription.peer_address is None: subscription.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: subscription.peer_address.ip = rdata.pkt_info.src_name subscription.peer_address.port = rdata.pkt_info.src_port subscription._cb_got_response(ua, rdata) except: ua._handle_exception(1) cdef void _Subscription_cb_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil: cdef void *subscription_void cdef Subscription subscription cdef PJSIPUA ua try: ua = _get_ua() except: return try: subscription_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if subscription_void == NULL: return subscription = subscription_void if rdata != NULL: if subscription.peer_address is None: subscription.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: subscription.peer_address.ip = rdata.pkt_info.src_name subscription.peer_address.port = rdata.pkt_info.src_port subscription._cb_notify(ua, rdata) except: ua._handle_exception(1) cdef void _Subscription_cb_refresh(pjsip_evsub *sub) with gil: # We want to handle the refresh timer oursevles, ignore the PJSIP provided timer pass cdef void _Subscription_cb_timer(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) with gil: cdef Subscription subscription cdef PJSIPUA ua try: ua = _get_ua() except: return try: if entry.user_data != NULL: subscription = entry.user_data if subscription._dlg == NULL: return if entry.id == 1: subscription._refresh_timer_active = 0 subscription._cb_refresh_timer(ua) else: subscription._timeout_timer_active = 0 subscription._cb_timeout_timer(ua) except: ua._handle_exception(1) cdef void _IncomingSubscription_cb_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil: cdef void *subscription_void cdef IncomingSubscription subscription cdef PJSIPUA ua try: ua = _get_ua() except: return try: subscription_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if subscription_void == NULL: p_st_code[0] = 481 return subscription = subscription_void if rdata != NULL: if subscription.peer_address is None: subscription.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: subscription.peer_address.ip = rdata.pkt_info.src_name subscription.peer_address.port = rdata.pkt_info.src_port p_st_code[0] = subscription._cb_rx_refresh(ua, rdata) except: ua._handle_exception(1) cdef void _IncomingSubscription_cb_server_timeout(pjsip_evsub *sub) with gil: cdef void *subscription_void cdef IncomingSubscription subscription cdef PJSIPUA ua try: ua = _get_ua() except: return try: subscription_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if subscription_void == NULL: return subscription = subscription_void subscription._cb_server_timeout(ua) except: ua._handle_exception(1) cdef void _IncomingSubscription_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil: cdef void *subscription_void cdef IncomingSubscription subscription cdef PJSIPUA ua try: ua = _get_ua() except: return try: subscription_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if subscription_void == NULL: return subscription = subscription_void subscription._cb_tsx(ua, event) except: ua._handle_exception(1) # globals cdef pjsip_evsub_user _subs_cb _subs_cb.on_evsub_state = _Subscription_cb_state _subs_cb.on_tsx_state = _Subscription_cb_tsx _subs_cb.on_rx_notify = _Subscription_cb_notify _subs_cb.on_client_refresh = _Subscription_cb_refresh cdef pjsip_evsub_user _incoming_subs_cb _incoming_subs_cb.on_rx_refresh = _IncomingSubscription_cb_rx_refresh _incoming_subs_cb.on_server_timeout = _IncomingSubscription_cb_server_timeout _incoming_subs_cb.on_tsx_state = _IncomingSubscription_cb_tsx _re_content_type = re.compile("^([a-zA-Z0-9\-.!%*_+`'~]+)\/([a-zA-Z0-9\-.!%*_+`'~]+)$") ================================================ FILE: sipsimple/core/_core.ua.pxi ================================================ import errno import heapq import re import random import sys import time import traceback import os import tempfile cdef class Timer: cdef int schedule(self, float delay, timer_callback callback, object obj) except -1: cdef PJSIPUA ua = _get_ua() if delay < 0: raise ValueError("delay must be a non-negative number") if callback == NULL: raise ValueError("callback must be non-NULL") if self._scheduled: raise RuntimeError("already scheduled") self.schedule_time = PyFloat_AsDouble(time.time() + delay) self.callback = callback self.obj = obj ua._add_timer(self) self._scheduled = 1 return 0 cdef int cancel(self) except -1: cdef PJSIPUA ua = _get_ua() if not self._scheduled: return 0 ua._remove_timer(self) self._scheduled = 0 return 0 cdef int call(self) except -1: self._scheduled = 0 self.callback(self.obj, self) def __richcmp__(self, other, op): cdef double diff if not isinstance(self, Timer) or not isinstance(other, Timer): return NotImplemented diff = (self).schedule_time - (other).schedule_time if op == 0: # < return diff < 0.0 elif op == 1: # <= return diff <= 0.0 elif op == 2: # == return diff == 0.0 elif op == 3: # != return diff != 0.0 elif op == 4: # > return diff > 0.0 elif op == 5: # >= return diff >= 0.0 return cdef class PJSIPUA: def __cinit__(self, *args, **kwargs): global _ua if _ua != NULL: raise SIPCoreError("Can only have one PJSUPUA instance at the same time") _ua = self self._threads = [] self._timers = list() self._events = {} self._incoming_events = set() self._incoming_requests = set() self._sent_messages = set() def __init__(self, event_handler, *args, **kwargs): global _event_queue_lock cdef str event cdef str method cdef list accept_types cdef int status cdef PJSTR message_method = PJSTR("MESSAGE") cdef PJSTR refer_method = PJSTR("REFER") cdef PJSTR str_norefersub = PJSTR("norefersub") cdef PJSTR str_gruu = PJSTR("gruu") self._event_handler = event_handler if kwargs["log_level"] < 0 or kwargs["log_level"] > PJ_LOG_MAX_LEVEL: raise ValueError("Log level should be between 0 and %d" % PJ_LOG_MAX_LEVEL) pj_log_set_level(kwargs["log_level"]) pj_log_set_decor(PJ_LOG_HAS_YEAR | PJ_LOG_HAS_MONTH | PJ_LOG_HAS_DAY_OF_MON | PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_SENDER | PJ_LOG_HAS_INDENT) pj_log_set_log_func(_cb_log) self._pjlib = PJLIB() pj_srand(random.getrandbits(32)) # rely on python seed for now self._caching_pool = PJCachingPool() self._pjmedia_endpoint = PJMEDIAEndpoint(self._caching_pool) self._pjsip_endpoint = PJSIPEndpoint(self._caching_pool, kwargs["ip_address"], kwargs["udp_port"], kwargs["tcp_port"], kwargs["tls_port"], kwargs["tls_verify_server"], kwargs["tls_ca_file"], kwargs["tls_cert_file"], kwargs["tls_privkey_file"], kwargs["tls_timeout"]) status = pj_mutex_create_simple(self._pjsip_endpoint._pool, "event_queue_lock", &_event_queue_lock) if status != 0: raise PJSIPError("Could not initialize event queue mutex", status) self._ip_address = kwargs["ip_address"] self.codecs = kwargs["codecs"] self.video_codecs = kwargs["video_codecs"] self._module_name = PJSTR("mod-core") self._module.name = self._module_name.pj_str self._module.id = -1 self._module.priority = PJSIP_MOD_PRIORITY_APPLICATION self._module.on_rx_request = _PJSIPUA_cb_rx_request self._module.on_tsx_state = _Request_cb_tsx_state status = pjsip_endpt_register_module(self._pjsip_endpoint._obj, &self._module) if status != 0: raise PJSIPError("Could not load application module", status) status = pjsip_endpt_add_capability(self._pjsip_endpoint._obj, &self._module, PJSIP_H_ALLOW, NULL, 1, &message_method.pj_str) if status != 0: raise PJSIPError("Could not add MESSAGE method to supported methods", status) status = pjsip_endpt_add_capability(self._pjsip_endpoint._obj, &self._module, PJSIP_H_ALLOW, NULL, 1, &refer_method.pj_str) if status != 0: raise PJSIPError("Could not add REFER method to supported methods", status) status = pjsip_endpt_add_capability(self._pjsip_endpoint._obj, NULL, PJSIP_H_SUPPORTED, NULL, 1, &str_norefersub.pj_str) if status != 0: raise PJSIPError("Could not add 'norefsub' to Supported header", status) status = pjsip_endpt_add_capability(self._pjsip_endpoint._obj, NULL, PJSIP_H_SUPPORTED, NULL, 1, &str_gruu.pj_str) if status != 0: raise PJSIPError("Could not add 'gruu' to Supported header", status) self._trace_sip = int(bool(kwargs["trace_sip"])) self._detect_sip_loops = int(bool(kwargs["detect_sip_loops"])) self._enable_colorbar_device = int(bool(kwargs["enable_colorbar_device"])) self._opus_fix_module_name = PJSTR("mod-core-opus-fix") self._opus_fix_module.name = self._opus_fix_module_name.pj_str self._opus_fix_module.id = -1 self._opus_fix_module.priority = PJSIP_MOD_PRIORITY_TRANSPORT_LAYER+1 self._opus_fix_module.on_rx_request = _cb_opus_fix_rx self._opus_fix_module.on_rx_response = _cb_opus_fix_rx self._opus_fix_module.on_tx_request = _cb_opus_fix_tx self._opus_fix_module.on_tx_response = _cb_opus_fix_tx status = pjsip_endpt_register_module(self._pjsip_endpoint._obj, &self._opus_fix_module) if status != 0: raise PJSIPError("Could not load opus-fix module", status) self._trace_module_name = PJSTR("mod-core-sip-trace") self._trace_module.name = self._trace_module_name.pj_str self._trace_module.id = -1 self._trace_module.priority = 0 self._trace_module.on_rx_request = _cb_trace_rx self._trace_module.on_rx_response = _cb_trace_rx self._trace_module.on_tx_request = _cb_trace_tx self._trace_module.on_tx_response = _cb_trace_tx status = pjsip_endpt_register_module(self._pjsip_endpoint._obj, &self._trace_module) if status != 0: raise PJSIPError("Could not load sip trace module", status) self._ua_tag_module_name = PJSTR("mod-core-ua-tag") self._ua_tag_module.name = self._ua_tag_module_name.pj_str self._ua_tag_module.id = -1 self._ua_tag_module.priority = PJSIP_MOD_PRIORITY_TRANSPORT_LAYER+1 self._ua_tag_module.on_tx_request = _cb_add_user_agent_hdr self._ua_tag_module.on_tx_response = _cb_add_server_hdr status = pjsip_endpt_register_module(self._pjsip_endpoint._obj, &self._ua_tag_module) if status != 0: raise PJSIPError("Could not load User-Agent/Server header tagging module", status) self._event_module_name = PJSTR("mod-core-events") self._event_module.name = self._event_module_name.pj_str self._event_module.id = -1 self._event_module.priority = PJSIP_MOD_PRIORITY_DIALOG_USAGE status = pjsip_endpt_register_module(self._pjsip_endpoint._obj, &self._event_module) if status != 0: raise PJSIPError("Could not load events module", status) status = pjmedia_aud_dev_set_observer_cb(_cb_audio_dev_process_event); if status != 0: raise PJSIPError("Could not set audio_change callbacks", status) status = pj_rwmutex_create(self._pjsip_endpoint._pool, "ua_audio_change_rwlock", &self.audio_change_rwlock) if status != 0: raise PJSIPError("Could not initialize audio change rwmutex", status) status = pj_mutex_create_recursive(self._pjsip_endpoint._pool, "ua_video_lock", &self.video_lock) if status != 0: raise PJSIPError("Could not initialize video mutex", status) self._user_agent = PJSTR(kwargs["user_agent"]) for event, accept_types in kwargs["events"].iteritems(): self.add_event(event, accept_types) for event in kwargs["incoming_events"]: if event not in self._events.iterkeys(): raise ValueError('Event "%s" is not known' % event) self._incoming_events.add(event) for method in kwargs["incoming_requests"]: method = method.upper() if method in ("ACK", "BYE", "INVITE", "REFER", "SUBSCRIBE"): raise ValueError('Handling incoming "%s" requests is not allowed' % method) self._incoming_requests.add(method) self.rtp_port_range = kwargs["rtp_port_range"] self.zrtp_cache = kwargs["zrtp_cache"] pj_stun_config_init(&self._stun_cfg, &self._caching_pool._obj.factory, 0, pjmedia_endpt_get_ioqueue(self._pjmedia_endpoint._obj), pjsip_endpt_get_timer_heap(self._pjsip_endpoint._obj)) property trace_sip: def __get__(self): self._check_self() return bool(self._trace_sip) def __set__(self, value): self._check_self() self._trace_sip = int(bool(value)) property detect_sip_loops: def __get__(self): self._check_self() return bool(self._detect_sip_loops) def __set__(self, value): self._check_self() self._detect_sip_loops = int(bool(value)) property enable_colorbar_device: def __get__(self): self._check_self() return bool(self._enable_colorbar_device) def __set__(self, value): self._check_self() self._enable_colorbar_device = int(bool(value)) self.refresh_video_devices() property events: def __get__(self): self._check_self() return self._events.copy() property ip_address: def __get__(self): self._check_self() return self._ip_address def add_event(self, object event, list accept_types): cdef pj_str_t event_pj cdef pj_str_t accept_types_pj[PJSIP_MAX_ACCEPT_COUNT] cdef int index cdef object accept_type cdef int accept_cnt = len(accept_types) cdef int status self._check_self() if accept_cnt == 0: raise SIPCoreError("Need at least one of accept_types") if accept_cnt > PJSIP_MAX_ACCEPT_COUNT: raise SIPCoreError("Too many accept_types") _str_to_pj_str(event, &event_pj) for index, accept_type in enumerate(accept_types): _str_to_pj_str(accept_type, &accept_types_pj[index]) status = pjsip_evsub_register_pkg(&self._event_module, &event_pj, 3600, accept_cnt, accept_types_pj) if status != 0: raise PJSIPError("Could not register event package", status) self._events[event] = accept_types[:] property incoming_events: def __get__(self): self._check_self() return self._incoming_events.copy() def add_incoming_event(self, str event): self._check_self() if event not in self._events.iterkeys(): raise ValueError('Event "%s" is not known' % event) self._incoming_events.add(event) def remove_incoming_event(self, str event): self._check_self() if event not in self._events.iterkeys(): raise ValueError('Event "%s" is not known' % event) self._incoming_events.discard(event) property incoming_requests: def __get__(self): self._check_self() return self._incoming_requests.copy() def add_incoming_request(self, object value): cdef str method self._check_self() method = value.upper() if method in ("ACK", "BYE", "INVITE", "REFER", "SUBSCRIBE"): raise ValueError('Handling incoming "%s" requests is not allowed' % method) self._incoming_requests.add(method) def remove_incoming_request(self, object value): cdef str method self._check_self() method = value.upper() if method in ("ACK", "BYE", "INVITE", "REFER", "SUBSCRIBE"): raise ValueError('Handling incoming "%s" requests is not allowed' % method) self._incoming_requests.discard(method) cdef pj_pool_t* create_memory_pool(self, bytes name, int initial_size, int resize_size): cdef pj_pool_t *pool cdef char *c_pool_name cdef pjsip_endpoint *endpoint c_pool_name = name endpoint = self._pjsip_endpoint._obj with nogil: pool = pjsip_endpt_create_pool(endpoint, c_pool_name, initial_size, resize_size) if pool == NULL: raise SIPCoreError("Could not allocate memory pool") return pool cdef void release_memory_pool(self, pj_pool_t* pool): cdef pjsip_endpoint *endpoint endpoint = self._pjsip_endpoint._obj if pool != NULL: with nogil: pjsip_endpt_release_pool(endpoint, pool) cdef void reset_memory_pool(self, pj_pool_t* pool): if pool != NULL: with nogil: pj_pool_reset(pool) cdef object _get_sound_devices(self, int is_output): cdef int count cdef pjmedia_aud_dev_info info cdef list retval = list() cdef int status with nogil: status = pj_rwmutex_lock_read(self.audio_change_rwlock) if status != 0: raise PJSIPError('Could not acquire audio_change_rwlock', status) try: for i in range(pjmedia_aud_dev_count()): with nogil: status = pjmedia_aud_dev_get_info(i, &info) if status != 0: raise PJSIPError("Could not get audio device info", status) if is_output: count = info.output_count else: count = info.input_count if count: retval.append(decode_device_name(info.name)) return retval finally: pj_rwmutex_unlock_read(self.audio_change_rwlock) cdef object _get_default_sound_device(self, int is_output): cdef pjmedia_aud_dev_info info cdef int dev_id cdef int status with nogil: status = pj_rwmutex_lock_read(self.audio_change_rwlock) if status != 0: raise SIPCoreError('Could not acquire audio_change_rwlock', status) try: if is_output: dev_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV else: dev_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV with nogil: status = pjmedia_aud_dev_get_info(dev_id, &info) if status != 0: raise PJSIPError("Could not get audio device info", status) return decode_device_name(info.name) finally: pj_rwmutex_unlock_read(self.audio_change_rwlock) property default_output_device: def __get__(self): self._check_self() return self._get_default_sound_device(1) property default_input_device: def __get__(self): self._check_self() return self._get_default_sound_device(0) property output_devices: def __get__(self): self._check_self() return self._get_sound_devices(1) property input_devices: def __get__(self): self._check_self() return self._get_sound_devices(0) property sound_devices: def __get__(self): self._check_self() cdef int count cdef pjmedia_aud_dev_info info cdef list retval = list() cdef int status with nogil: status = pj_rwmutex_lock_read(self.audio_change_rwlock) if status != 0: raise SIPCoreError('Could not acquire audio_change_rwlock', status) try: for i in range(pjmedia_aud_dev_count()): with nogil: status = pjmedia_aud_dev_get_info(i, &info) if status == 0: retval.append(decode_device_name(info.name)) return retval finally: pj_rwmutex_unlock_read(self.audio_change_rwlock) def refresh_sound_devices(self): self._check_self() cdef int status cdef dict event_dict self.old_devices = self.sound_devices with nogil: status = pj_rwmutex_lock_write(self.audio_change_rwlock) if status != 0: raise SIPCoreError('Could not acquire audio_change_rwlock', status) with nogil: pjmedia_aud_dev_refresh() status = pj_rwmutex_unlock_write(self.audio_change_rwlock) if status != 0: raise SIPCoreError('Could not release audio_change_rwlock', status) event_dict = dict() event_dict["old_devices"] = self.old_devices event_dict["new_devices"] = self.sound_devices _add_event("AudioDevicesDidChange", event_dict) cdef object _get_video_devices(self): cdef pjmedia_vid_dev_info info cdef list retval = list() cdef int direction cdef int status for i in range(pjmedia_vid_dev_count()): with nogil: status = pjmedia_vid_dev_get_info(i, &info) if status != 0: raise PJSIPError("Could not get video device info", status) direction = info.dir if direction in (PJMEDIA_DIR_CAPTURE, PJMEDIA_DIR_CAPTURE_PLAYBACK): if (not self._enable_colorbar_device and bytes(info.driver) == "Colorbar") or bytes(info.driver) == "Null": continue retval.append(decode_device_name(info.name)) return retval cdef object _get_default_video_device(self): cdef pjmedia_vid_dev_info info cdef int status with nogil: status = pjmedia_vid_dev_get_info(PJMEDIA_VID_DEFAULT_CAPTURE_DEV, &info) if status != 0: raise PJSIPError("Could not get default video device info", status) if (not self._enable_colorbar_device and bytes(info.driver) == "Colorbar") or bytes(info.driver) == "Null": raise SIPCoreError("Could not get default video device") return decode_device_name(info.name) def refresh_video_devices(self): self._check_self() cdef int status cdef dict event_dict self.old_video_devices = self.video_devices with nogil: pjmedia_vid_dev_refresh() event_dict = dict() event_dict["old_devices"] = self.old_video_devices event_dict["new_devices"] = self.video_devices _add_event("VideoDevicesDidChange", event_dict) property default_video_device: def __get__(self): self._check_self() return self._get_default_video_device() property video_devices: def __get__(self): self._check_self() return self._get_video_devices() property available_codecs: def __get__(self): self._check_self() return self._pjmedia_endpoint._get_all_codecs() property codecs: def __get__(self): self._check_self() return self._pjmedia_endpoint._get_current_codecs() def __set__(self, value): self._check_self() self._pjmedia_endpoint._set_codecs(value) property available_video_codecs: def __get__(self): self._check_self() return self._pjmedia_endpoint._get_all_video_codecs() property video_codecs: def __get__(self): self._check_self() return self._pjmedia_endpoint._get_current_video_codecs() def __set__(self, value): self._check_self() self._pjmedia_endpoint._set_video_codecs(value) property udp_port: def __get__(self): self._check_self() if self._pjsip_endpoint._udp_transport == NULL: return None return self._pjsip_endpoint._udp_transport.local_name.port def set_udp_port(self, value): cdef int port self._check_self() if value is None: if self._pjsip_endpoint._udp_transport == NULL: return self._pjsip_endpoint._stop_udp_transport() else: port = value if not (0 <= port <= 65535): raise ValueError("Not a valid UDP port: %d" % value) if self._pjsip_endpoint._udp_transport != NULL: if port == self._pjsip_endpoint._udp_transport.local_name.port: return self._pjsip_endpoint._stop_udp_transport() self._pjsip_endpoint._start_udp_transport(port) property tcp_port: def __get__(self): self._check_self() if self._pjsip_endpoint._tcp_transport == NULL: return None return self._pjsip_endpoint._tcp_transport.addr_name.port def set_tcp_port(self, value): cdef int port self._check_self() if value is None: if self._pjsip_endpoint._tcp_transport == NULL: return self._pjsip_endpoint._stop_tcp_transport() else: port = value if not (0 <= port <= 65535): raise ValueError("Not a valid TCP port: %d" % value) if self._pjsip_endpoint._tcp_transport != NULL: if port == self._pjsip_endpoint._tcp_transport.addr_name.port: return self._pjsip_endpoint._stop_tcp_transport() self._pjsip_endpoint._start_tcp_transport(port) property tls_port: def __get__(self): self._check_self() if self._pjsip_endpoint._tls_transport == NULL: return None return self._pjsip_endpoint._tls_transport.addr_name.port property rtp_port_range: def __get__(self): self._check_self() return (self._rtp_port_start, self._rtp_port_start + self._rtp_port_count) def __set__(self, value): cdef int _rtp_port_start cdef int _rtp_port_stop cdef int _rtp_port_count cdef int _rtp_port_usable_count cdef int port self._check_self() for port in value: if not (0 <= port <= 65535): raise SIPCoreError("RTP port range values should be between 0 and 65535") _rtp_port_start, _rtp_port_stop = value _rtp_port_count = _rtp_port_stop - _rtp_port_start _rtp_port_usable_count = _rtp_port_count - _rtp_port_count % 2 # we need an even number of ports, so we won't use the last one if an odd number is provided if _rtp_port_usable_count < 2: raise SIPCoreError("RTP port range should contain at least 2 ports") self._rtp_port_start = _rtp_port_start self._rtp_port_count = _rtp_port_count self._rtp_port_usable_count = _rtp_port_usable_count self._rtp_port_index = 0 property user_agent: def __get__(self): self._check_self() return self._user_agent.str def __set__(self, value): self._check_self() self._user_agent = PJSTR("value") property log_level: def __get__(self): self._check_self() return pj_log_get_level() def __set__(self, value): self._check_self() if value < 0 or value > PJ_LOG_MAX_LEVEL: raise ValueError("Log level should be between 0 and %d" % PJ_LOG_MAX_LEVEL) pj_log_set_level(value) property tls_verify_server: def __get__(self): self._check_self() return bool(self._pjsip_endpoint._tls_verify_server) property tls_ca_file: def __get__(self): self._check_self() if self._pjsip_endpoint._tls_ca_file is None: return None else: return self._pjsip_endpoint._tls_ca_file.str property tls_cert_file: def __get__(self): self._check_self() if self._pjsip_endpoint._tls_cert_file is None: return None else: return self._pjsip_endpoint._tls_cert_file.str property tls_privkey_file: def __get__(self): self._check_self() if self._pjsip_endpoint._tls_privkey_file is None: return None else: return self._pjsip_endpoint._tls_privkey_file.str property tls_timeout: def __get__(self): self._check_self() return self._pjsip_endpoint._tls_timeout def set_tls_options(self, port=None, verify_server=False, ca_file=None, cert_file=None, privkey_file=None, int timeout=3000): cdef int c_port self._check_self() if port is None: if self._pjsip_endpoint._tls_transport == NULL: return self._pjsip_endpoint._stop_tls_transport() else: c_port = port if not (0 <= c_port <= 65535): raise ValueError("Not a valid TCP port: %d" % port) if ca_file is not None and not os.path.isfile(ca_file): raise ValueError("Cannot find the specified CA file: %s" % ca_file) if cert_file is not None and not os.path.isfile(cert_file): raise ValueError("Cannot find the specified certificate file: %s" % cert_file) if privkey_file is not None and not os.path.isfile(privkey_file): raise ValueError("Cannot find the specified private key file: %s" % privkey_file) if timeout < 0: raise ValueError("Invalid TLS timeout value: %d" % timeout) if self._pjsip_endpoint._tls_transport != NULL: self._pjsip_endpoint._stop_tls_transport() self._pjsip_endpoint._tls_verify_server = int(bool(verify_server)) if ca_file is None: self._pjsip_endpoint._tls_ca_file = None else: self._pjsip_endpoint._tls_ca_file = PJSTR(ca_file.encode(sys.getfilesystemencoding())) if cert_file is None: self._pjsip_endpoint._tls_cert_file = None else: self._pjsip_endpoint._tls_cert_file = PJSTR(cert_file.encode(sys.getfilesystemencoding())) if privkey_file is None: self._pjsip_endpoint._tls_privkey_file = None else: self._pjsip_endpoint._tls_privkey_file = PJSTR(privkey_file.encode(sys.getfilesystemencoding())) self._pjsip_endpoint._tls_timeout = timeout self._pjsip_endpoint._start_tls_transport(c_port) def detect_nat_type(self, stun_server_address, stun_server_port=PJ_STUN_PORT, object user_data=None): cdef pj_str_t stun_server_address_pj cdef pj_sockaddr_in stun_server cdef int status self._check_self() if not _is_valid_ip(pj_AF_INET(), stun_server_address): raise ValueError("Not a valid IPv4 address: %s" % stun_server_address) _str_to_pj_str(stun_server_address, &stun_server_address_pj) status = pj_sockaddr_in_init(&stun_server, &stun_server_address_pj, stun_server_port) if status != 0: raise PJSIPError("Could not init STUN server address", status) status = pj_stun_detect_nat_type(&stun_server, &self._stun_cfg, user_data, _cb_detect_nat_type) if status != 0: raise PJSIPError("Could not start NAT type detection", status) Py_INCREF(user_data) def set_nameservers(self, list nameservers): self._check_self() return self._pjsip_endpoint._set_dns_nameservers([n for n in nameservers if _re_ipv4.match(n)]) def set_h264_options(self, profile, level): self._check_self() self._pjmedia_endpoint._set_h264_options(str(profile), int(level.replace('.', ''))) def set_video_options(self, max_resolution, int max_framerate, object max_bitrate): self._check_self() self._pjmedia_endpoint._set_video_options(tuple(max_resolution), max_framerate, max_bitrate or 0.0) property zrtp_cache: def __get__(self): self._check_self() return self._zrtp_cache def __set__(self, value): self._check_self() if value is None: value = os.path.join(tempfile.gettempdir(), 'zrtp_cache_%d.db' % os.getpid()) self._zrtp_cache = value def __dealloc__(self): self.dealloc() def dealloc(self): global _ua, _dealloc_handler_queue, _event_queue_lock if _ua == NULL: return self._check_thread() pjmedia_aud_dev_set_observer_cb(NULL) if self.audio_change_rwlock != NULL: pj_rwmutex_destroy(self.audio_change_rwlock) self.audio_change_rwlock = NULL if self.video_lock != NULL: pj_mutex_destroy(self.video_lock) self.video_lock = NULL _process_handler_queue(self, &_dealloc_handler_queue) if _event_queue_lock != NULL: pj_mutex_lock(_event_queue_lock) pj_mutex_destroy(_event_queue_lock) _event_queue_lock = NULL self._pjsip_endpoint = None self._pjmedia_endpoint = None self._caching_pool = None self._pjlib = None _ua = NULL self._poll_log() cdef int _poll_log(self) except -1: cdef object event_name cdef dict event_params cdef list events events = _get_clear_event_queue() for event_name, event_params in events: self._event_handler(event_name, **event_params) def poll(self): global _post_poll_handler_queue cdef int status cdef double now cdef object retval = None cdef float max_timeout cdef pj_time_val pj_max_timeout cdef list timers cdef Timer timer self._check_self() max_timeout = 0.100 while self._timers: if not (self._timers[0])._scheduled: # timer was cancelled heapq.heappop(self._timers) else: max_timeout = min(max((self._timers[0]).schedule_time - time.time(), 0.0), max_timeout) break pj_max_timeout.sec = int(max_timeout) pj_max_timeout.msec = int(max_timeout * 1000) % 1000 with nogil: status = pjsip_endpt_handle_events(self._pjsip_endpoint._obj, &pj_max_timeout) IF UNAME_SYSNAME == "Darwin": if status not in [0, PJ_ERRNO_START_SYS + errno.EBADF]: raise PJSIPError("Error while handling events", status) ELSE: if status != 0: raise PJSIPError("Error while handling events", status) _process_handler_queue(self, &_post_poll_handler_queue) timers = list() now = time.time() while self._timers: if not (self._timers[0])._scheduled: # timer was cancelled heapq.heappop(self._timers) elif (self._timers[0]).schedule_time <= now: # timer needs to be processed timer = heapq.heappop(self._timers) timers.append(timer) else: break for timer in timers: timer.call() self._poll_log() if self._fatal_error: return True else: return False cdef int _handle_exception(self, int is_fatal) except -1: cdef object exc_type cdef object exc_val cdef object exc_tb exc_type, exc_val, exc_tb = sys.exc_info() if is_fatal: self._fatal_error = is_fatal _add_event("SIPEngineGotException", dict(type=exc_type, value=exc_val, traceback="".join(traceback.format_exception(exc_type, exc_val, exc_tb)))) return 0 cdef int _check_self(self) except -1: global _ua if _ua == NULL: raise SIPCoreError("The PJSIPUA is no longer running") self._check_thread() cdef int _check_thread(self) except -1: if not pj_thread_is_registered(): self._threads.append(PJSIPThread()) return 0 cdef int _add_timer(self, Timer timer) except -1: heapq.heappush(self._timers, timer) return 0 cdef int _remove_timer(self, Timer timer) except -1: # Don't remove it from the heap, just mark it as not scheduled timer._scheduled = 0 return 0 cdef int _cb_rx_request(self, pjsip_rx_data *rdata) except 0: global _event_hdr_name cdef int status cdef int bad_request cdef pjsip_tx_data *tdata = NULL cdef pjsip_hdr_ptr_const hdr_add cdef IncomingRequest request cdef Invitation inv cdef IncomingSubscription sub cdef IncomingReferral ref cdef list extra_headers cdef dict event_dict cdef dict message_params cdef pj_str_t tsx_key cdef pjsip_via_hdr *top_via cdef pjsip_via_hdr *via cdef pjsip_transaction *tsx = NULL cdef unsigned int options = PJSIP_INV_SUPPORT_100REL cdef pjsip_event_hdr *event_hdr cdef object method_name = _pj_str_to_str(rdata.msg_info.msg.line.req.method.name) if method_name != "ACK": if self._detect_sip_loops: # Temporarily trick PJSIP into believing the last Via header is actually the first top_via = via = rdata.msg_info.via while True: rdata.msg_info.via = via via = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_VIA, ( via).next) if via == NULL: break status = pjsip_tsx_create_key(rdata.tp_info.pool, &tsx_key, PJSIP_ROLE_UAC, &rdata.msg_info.msg.line.req.method, rdata) rdata.msg_info.via = top_via if status != 0: raise PJSIPError("Could not generate transaction key for incoming request", status) tsx = pjsip_tsx_layer_find_tsx(&tsx_key, 0) if tsx != NULL: status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 482, NULL, &tdata) if status != 0: raise PJSIPError("Could not create response", status) elif method_name in self._incoming_requests: request = IncomingRequest() request.init(self, rdata) elif method_name == "OPTIONS": status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 200, NULL, &tdata) if status != 0: raise PJSIPError("Could not create response", status) for hdr_type in [PJSIP_H_ALLOW, PJSIP_H_ACCEPT, PJSIP_H_SUPPORTED]: hdr_add = pjsip_endpt_get_capability(self._pjsip_endpoint._obj, hdr_type, NULL) if hdr_add != NULL: pjsip_msg_add_hdr(tdata.msg, pjsip_hdr_clone(tdata.pool, hdr_add)) elif method_name == "INVITE": status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, self._pjsip_endpoint._obj, &tdata) if status == 0: inv = Invitation() inv.init_incoming(self, rdata, options) elif method_name == "SUBSCRIBE": event_hdr = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_event_hdr_name.pj_str, NULL) if event_hdr == NULL or _pj_str_to_str(event_hdr.event_type) not in self._incoming_events: status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 489, NULL, &tdata) if status != 0: raise PJSIPError("Could not create response", status) else: sub = IncomingSubscription() sub.init(self, rdata, _pj_str_to_str(event_hdr.event_type)) elif method_name == "REFER": ref = IncomingReferral() ref.init(self, rdata) elif method_name == "MESSAGE": bad_request = 0 extra_headers = list() message_params = dict() event_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) message_params["request_uri"] = event_dict["request_uri"] message_params["from_header"] = event_dict["headers"].get("From", None) message_params["to_header"] = event_dict["headers"].get("To", None) message_params["headers"] = event_dict["headers"] message_params["body"] = event_dict["body"] content_type = message_params["headers"].get("Content-Type", None) if content_type is not None: message_params["content_type"] = content_type.content_type if message_params["headers"].get("Content-Length", 0) > 0 and message_params["body"] is None: bad_request = 1 extra_headers.append(WarningHeader(399, "local", "Missing body")) else: message_params["content_type"] = None if message_params["headers"].get("Content-Length", 0) > 0 and message_params["body"] is None: bad_request = 1 extra_headers.append(WarningHeader(399, "local", "Missing Content-Type header")) if bad_request: status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 400, NULL, &tdata) if status != 0: raise PJSIPError("Could not create response", status) _add_headers_to_tdata(tdata, extra_headers) else: _add_event("SIPEngineGotMessage", message_params) status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 200, NULL, &tdata) if status != 0: raise PJSIPError("Could not create response", status) elif method_name != "ACK": status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 405, NULL, &tdata) if status != 0: raise PJSIPError("Could not create response", status) if tdata != NULL: status = pjsip_endpt_send_response2(self._pjsip_endpoint._obj, rdata, tdata, NULL, NULL) if status != 0: pjsip_tx_data_dec_ref(tdata) raise PJSIPError("Could not send response", status) return 1 cdef class PJSIPThread: def __cinit__(self): cdef object thread_name = "python_%d" % id(self) cdef int status status = pj_thread_register(thread_name, self._thread_desc, &self._obj) if status != 0: raise PJSIPError("Error while registering thread", status) # callback functions cdef void _cb_audio_dev_process_event(pjmedia_aud_dev_event event) with gil: cdef PJSIPUA ua event_dict = dict() try: ua = _get_ua() except: return try: if event in (PJMEDIA_AUD_DEV_DEFAULT_INPUT_CHANGED, PJMEDIA_AUD_DEV_DEFAULT_OUTPUT_CHANGED): event_dict["changed_input"] = event == PJMEDIA_AUD_DEV_DEFAULT_INPUT_CHANGED event_dict["changed_output"] = event == PJMEDIA_AUD_DEV_DEFAULT_OUTPUT_CHANGED _add_event("DefaultAudioDeviceDidChange", event_dict) elif event == PJMEDIA_AUD_DEV_LIST_WILL_REFRESH: ua.old_devices = ua.sound_devices with nogil: status = pj_rwmutex_lock_write(ua.audio_change_rwlock) if status != 0: raise SIPCoreError('Could not acquire audio_change_rwlock for writing', status) elif event == PJMEDIA_AUD_DEV_LIST_DID_REFRESH: with nogil: status = pj_rwmutex_unlock_write(ua.audio_change_rwlock) if status != 0: raise SIPCoreError('Could not release the audio_change_rwlock', status) event_dict["old_devices"] = ua.old_devices event_dict["new_devices"] = ua.sound_devices _add_event("AudioDevicesDidChange", event_dict) except: ua._handle_exception(1) cdef void _cb_detect_nat_type(void *user_data, pj_stun_nat_detect_result_ptr_const res) with gil: cdef PJSIPUA ua cdef dict event_dict cdef object user_data_obj = user_data Py_DECREF(user_data_obj) try: ua = _get_ua() except: return try: event_dict = dict() event_dict["succeeded"] = res.status == 0 event_dict["user_data"] = user_data_obj if res.status == 0: event_dict["nat_type"] = res.nat_type_name else: event_dict["error"] = res.status_text _add_event("SIPEngineDetectedNATType", event_dict) except: ua._handle_exception(0) cdef int _PJSIPUA_cb_rx_request(pjsip_rx_data *rdata) with gil: cdef PJSIPUA ua try: ua = _get_ua() except: return 0 try: return ua._cb_rx_request(rdata) except: ua._handle_exception(0) cdef int _cb_opus_fix_tx(pjsip_tx_data *tdata) with gil: cdef PJSIPUA ua cdef pjsip_msg_body *body cdef pjsip_msg_body *new_body cdef pjmedia_sdp_session *sdp cdef pjmedia_sdp_media *media cdef pjmedia_sdp_attr *attr cdef int i cdef int j cdef pj_str_t new_value try: ua = _get_ua() except: return 0 try: if tdata != NULL and tdata.msg != NULL: body = tdata.msg.body if body != NULL and _pj_str_to_str(body.content_type.type).lower() == "application" and _pj_str_to_str(body.content_type.subtype).lower() == "sdp": new_body = pjsip_msg_body_clone(tdata.pool, body) sdp = new_body.data for i in range(sdp.media_count): media = sdp.media[i] if _pj_str_to_str(media.desc.media).lower() != "audio": continue for j in range(media.attr_count): attr = media.attr[j] if _pj_str_to_str(attr.name).lower() != "rtpmap": continue attr_value = _pj_str_to_str(attr.value).lower() pos = attr_value.find("opus") if pos == -1: continue # this is the opus rtpmap attribute opus_line = attr_value[:pos] + "opus/48000/2" new_value.slen = len(opus_line) new_value.ptr = pj_pool_alloc(tdata.pool, new_value.slen) memcpy(new_value.ptr, PyString_AsString(opus_line), new_value.slen) attr.value = new_value break tdata.msg.body = new_body except: ua._handle_exception(0) return 0 cdef int _cb_opus_fix_rx(pjsip_rx_data *rdata) with gil: cdef PJSIPUA ua cdef pjsip_msg_body *body cdef int pos1 cdef int pos2 cdef char *body_ptr try: ua = _get_ua() except: return 0 try: if rdata != NULL and rdata.msg_info.msg != NULL: body = rdata.msg_info.msg.body if body != NULL and _pj_str_to_str(body.content_type.type).lower() == "application" and _pj_str_to_str(body.content_type.subtype).lower() == "sdp": body_ptr = body.data body_str = PyString_FromStringAndSize(body_ptr, body.len).lower() pos1 = body_str.find("opus/48000") if pos1 != -1: pos2 = body_str.find("opus/48000/2") if pos2 != -1: memcpy(body_ptr + pos2 + 11, '1', 1) else: # old opus, we must make it fail memcpy(body_ptr + pos1 + 5, 'XXXXX', 5) except: ua._handle_exception(0) return 0 cdef int _cb_trace_rx(pjsip_rx_data *rdata) with gil: cdef PJSIPUA ua try: ua = _get_ua() except: return 0 try: if ua._trace_sip: _add_event("SIPEngineSIPTrace", dict(received=True, source_ip=rdata.pkt_info.src_name, source_port=rdata.pkt_info.src_port, destination_ip=_pj_str_to_str(rdata.tp_info.transport.local_name.host), destination_port=rdata.tp_info.transport.local_name.port, data=PyString_FromStringAndSize(rdata.pkt_info.packet, rdata.pkt_info.len), transport=rdata.tp_info.transport.type_name)) except: ua._handle_exception(0) return 0 cdef int _cb_trace_tx(pjsip_tx_data *tdata) with gil: cdef PJSIPUA ua try: ua = _get_ua() except: return 0 try: if ua._trace_sip: _add_event("SIPEngineSIPTrace", dict(received=False, source_ip=_pj_str_to_str(tdata.tp_info.transport.local_name.host), source_port=tdata.tp_info.transport.local_name.port, destination_ip=tdata.tp_info.dst_name, destination_port=tdata.tp_info.dst_port, data=PyString_FromStringAndSize(tdata.buf.start, tdata.buf.cur - tdata.buf.start), transport=tdata.tp_info.transport.type_name)) except: ua._handle_exception(0) return 0 cdef int _cb_add_user_agent_hdr(pjsip_tx_data *tdata) with gil: cdef PJSIPUA ua cdef pjsip_hdr *hdr cdef void *found_hdr try: ua = _get_ua() except: return 0 try: found_hdr = pjsip_msg_find_hdr_by_name(tdata.msg, &_user_agent_hdr_name.pj_str, NULL) if found_hdr == NULL: hdr = pjsip_generic_string_hdr_create(tdata.pool, &_user_agent_hdr_name.pj_str, &ua._user_agent.pj_str) if hdr == NULL: raise SIPCoreError('Could not add "User-Agent" header to outgoing request') pjsip_msg_add_hdr(tdata.msg, hdr) except: ua._handle_exception(0) return 0 cdef int _cb_add_server_hdr(pjsip_tx_data *tdata) with gil: cdef PJSIPUA ua cdef pjsip_hdr *hdr cdef void *found_hdr try: ua = _get_ua() except: return 0 try: found_hdr = pjsip_msg_find_hdr_by_name(tdata.msg, &_server_hdr_name.pj_str, NULL) if found_hdr == NULL: hdr = pjsip_generic_string_hdr_create(tdata.pool, &_server_hdr_name.pj_str, &ua._user_agent.pj_str) if hdr == NULL: raise SIPCoreError('Could not add "Server" header to outgoing response') pjsip_msg_add_hdr(tdata.msg, hdr) except: ua._handle_exception(0) return 0 # functions cdef PJSIPUA _get_ua(): global _ua cdef PJSIPUA ua if _ua == NULL: raise SIPCoreError("PJSIPUA is not instantiated") ua = _ua ua._check_thread() return ua cdef int deallocate_weakref(object weak_ref, object timer) except -1 with gil: Py_DECREF(weak_ref) # globals cdef void *_ua = NULL cdef PJSTR _user_agent_hdr_name = PJSTR("User-Agent") cdef PJSTR _server_hdr_name = PJSTR("Server") cdef PJSTR _event_hdr_name = PJSTR("Event") cdef object _re_ipv4 = re.compile(r"^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$") ================================================ FILE: sipsimple/core/_core.util.pxi ================================================ import platform import re import sys from application.version import Version cdef class PJSTR: def __cinit__(self, str): self.str = str _str_to_pj_str(str, &self.pj_str) def __str__(self): return self.str cdef class SIPStatusMessages: cdef object _default_status def __cinit__(self, *args, **kwargs): self._default_status = _pj_str_to_str(pjsip_get_status_text(0)[0]) def __getitem__(self, int val): cdef object _status _status = _pj_str_to_str(pjsip_get_status_text(val)[0]) if _status == self._default_status: raise IndexError("Unknown SIP response code: %d" % val) return _status cdef class frozenlist: def __cinit__(self, *args, **kw): self.list = list() self.initialized = 0 self.hash = 0 def __init__(self, *args, **kw): if not self.initialized: self.list = list(*args, **kw) self.initialized = 1 self.hash = hash(tuple(self.list)) def __reduce__(self): return (self.__class__, (self.list,), None) def __repr__(self): return "frozenlist(%r)" % self.list def __len__(self): return self.list.__len__() def __hash__(self): return self.hash def __iter__(self): return self.list.__iter__() def __cmp__(self, frozenlist other): return self.list.__cmp__(other.list) def __richcmp__(frozenlist self, other, op): if isinstance(other, frozenlist): other = (other).list if op == 0: return self.list.__cmp__(other) < 0 elif op == 1: return self.list.__cmp__(other) <= 0 elif op == 2: return self.list.__eq__(other) elif op == 3: return self.list.__ne__(other) elif op == 4: return self.list.__cmp__(other) > 0 elif op == 5: return self.list.__cmp__(other) >= 0 else: return NotImplemented def __contains__(self, item): return self.list.__contains__(item) def __getitem__(self, key): return self.list.__getitem__(key) def __add__(first, second): if isinstance(first, frozenlist): first = (first).list if isinstance(second, frozenlist): second = (second).list return frozenlist(first+second) def __mul__(first, second): if isinstance(first, frozenlist): first = (first).list if isinstance(second, frozenlist): second = (second).list return frozenlist(first*second) def __reversed__(self): return self.list.__reversed__() def count(self, elem): return self.list.count(elem) def index(self, elem): return self.list.index(elem) cdef class frozendict: def __cinit__(self, *args, **kw): self.dict = dict() self.initialized = 0 def __init__(self, *args, **kw): if not self.initialized: self.dict = dict(*args, **kw) self.initialized = 1 self.hash = hash(tuple(self.dict.iteritems())) def __reduce__(self): return (self.__class__, (self.dict,), None) def __repr__(self): return "frozendict(%r)" % self.dict def __len__(self): return self.dict.__len__() def __hash__(self): return self.hash def __iter__(self): return self.dict.__iter__() def __cmp__(self, frozendict other): return self.dict.__cmp__(other.dict) def __richcmp__(frozendict self, other, op): if isinstance(other, frozendict): other = (other).dict if op == 0: return self.dict.__cmp__(other) < 0 elif op == 1: return self.dict.__cmp__(other) <= 0 elif op == 2: return self.dict.__eq__(other) elif op == 3: return self.dict.__ne__(other) elif op == 4: return self.dict.__cmp__(other) > 0 elif op == 5: return self.dict.__cmp__(other) >= 0 else: return NotImplemented def __contains__(self, item): return self.dict.__contains__(item) def __getitem__(self, key): return self.dict.__getitem__(key) def copy(self): return self def get(self, *args): return self.dict.get(*args) def has_key(self, key): return self.dict.has_key(key) def items(self): return self.dict.items() def iteritems(self): return self.dict.iteritems() def iterkeys(self): return self.dict.iterkeys() def itervalues(self): return self.dict.itervalues() def keys(self): return self.dict.keys() def values(self): return self.dict.values() # functions cdef int _str_to_pj_str(object string, pj_str_t *pj_str) except -1: pj_str.ptr = PyString_AsString(string) pj_str.slen = len(string) cdef object _pj_str_to_str(pj_str_t pj_str): return PyString_FromStringAndSize(pj_str.ptr, pj_str.slen) cdef object _pj_status_to_str(int status): cdef char buf[PJ_ERR_MSG_SIZE] return _pj_str_to_str(pj_strerror(status, buf, PJ_ERR_MSG_SIZE)) cdef object _pj_status_to_def(int status): return _re_pj_status_str_def.match(_pj_status_to_str(status)).group(1) cdef dict _pjsip_param_to_dict(pjsip_param *param_list): cdef pjsip_param *param cdef dict retval = dict() param = ( param_list).next while param != param_list: if param.value.slen == 0: retval[_pj_str_to_str(param.name)] = None else: retval[_pj_str_to_str(param.name)] = _pj_str_to_str(param.value) param = ( param).next return retval cdef int _dict_to_pjsip_param(object params, pjsip_param *param_list, pj_pool_t *pool): cdef pjsip_param *param = NULL for name, value in params.iteritems(): param = pj_pool_alloc(pool, sizeof(pjsip_param)) if param == NULL: return -1 _str_to_pj_str(name, ¶m.name) if value is None: param.value.slen = 0 else: _str_to_pj_str(value, ¶m.value) pj_list_insert_after( param_list, param) return 0 cdef int _pjsip_msg_to_dict(pjsip_msg *msg, dict info_dict) except -1: cdef pjsip_msg_body *body cdef pjsip_hdr *header cdef pjsip_generic_array_hdr *array_header cdef pjsip_ctype_hdr *ctype_header cdef pjsip_cseq_hdr *cseq_header cdef char *buf cdef int buf_len, i, status headers = {} header = ( &msg.hdr).next while header != &msg.hdr: header_name = _pj_str_to_str(header.name) header_data = None multi_header = False if header_name in ("Accept", "Allow", "Require", "Supported", "Unsupported", "Allow-Events"): array_header = header header_data = [] for i from 0 <= i < array_header.count: header_data.append(_pj_str_to_str(array_header.values[i])) elif header_name == "Contact": multi_header = True header_data = FrozenContactHeader_create( header) elif header_name == "Content-Length": header_data = ( header).len elif header_name == "Content-Type": header_data = FrozenContentTypeHeader_create( header) elif header_name == "CSeq": cseq_header = header header_data = (cseq_header.cseq, _pj_str_to_str(cseq_header.method.name)) elif header_name in ("Expires", "Max-Forwards", "Min-Expires"): header_data = ( header).ivalue elif header_name == "From": header_data = FrozenFromHeader_create( header) elif header_name == "To": header_data = FrozenToHeader_create( header) elif header_name == "Route": multi_header = True header_data = FrozenRouteHeader_create( header) elif header_name == "Reason": value = _pj_str_to_str((header).hvalue) protocol, sep, params_str = value.partition(';') params = frozendict([(name, value or None) for name, sep, value in [param.partition('=') for param in params_str.split(';')]]) header_data = FrozenReasonHeader(protocol, params) elif header_name == "Record-Route": multi_header = True header_data = FrozenRecordRouteHeader_create( header) elif header_name == "Retry-After": header_data = FrozenRetryAfterHeader_create( header) elif header_name == "Via": multi_header = True header_data = FrozenViaHeader_create( header) elif header_name == "Warning": match = _re_warning_hdr.match(_pj_str_to_str((header).hvalue)) if match is not None: warning_params = match.groupdict() warning_params['code'] = int(warning_params['code']) header_data = FrozenWarningHeader(**warning_params) elif header_name == "Event": header_data = FrozenEventHeader_create( header) elif header_name == "Subscription-State": header_data = FrozenSubscriptionStateHeader_create( header) elif header_name == "Refer-To": header_data = FrozenReferToHeader_create( header) elif header_name == "Subject": header_data = FrozenSubjectHeader_create( header) elif header_name == "Replaces": header_data = FrozenReplacesHeader_create( header) # skip the following headers: elif header_name not in ("Authorization", "Proxy-Authenticate", "Proxy-Authorization", "WWW-Authenticate"): header_data = FrozenHeader(header_name, _pj_str_to_str(( header).hvalue)) if header_data is not None: if multi_header: headers.setdefault(header_name, []).append(header_data) else: if header_name not in headers: headers[header_name] = header_data header = ( header).next info_dict["headers"] = headers body = msg.body if body == NULL: info_dict["body"] = None else: status = pjsip_print_body(body, &buf, &buf_len) if status != 0: info_dict["body"] = None else: info_dict["body"] = PyString_FromStringAndSize(buf, buf_len) if msg.type == PJSIP_REQUEST_MSG: info_dict["method"] = _pj_str_to_str(msg.line.req.method.name) # You need to call pjsip_uri_get_uri on the request URI if the message is for transmitting, # but it isn't required if message is one received. Otherwise, a seg fault occurs. Don't ask. info_dict["request_uri"] = FrozenSIPURI_create(pjsip_uri_get_uri(msg.line.req.uri)) else: info_dict["code"] = msg.line.status.code info_dict["reason"] = _pj_str_to_str(msg.line.status.reason) return 0 cdef int _is_valid_ip(int af, object ip) except -1: cdef char buf[16] cdef pj_str_t src cdef int status _str_to_pj_str(ip, &src) status = pj_inet_pton(af, &src, buf) if status == 0: return 1 else: return 0 cdef int _get_ip_version(object ip) except -1: if _is_valid_ip(pj_AF_INET(), ip): return pj_AF_INET() elif _is_valid_ip(pj_AF_INET6(), ip): return pj_AF_INET() else: return 0 cdef int _add_headers_to_tdata(pjsip_tx_data *tdata, object headers) except -1: cdef pj_str_t name_pj, value_pj cdef pjsip_hdr *hdr for header in headers: _str_to_pj_str(header.name, &name_pj) _str_to_pj_str(header.body, &value_pj) hdr = pjsip_generic_string_hdr_create(tdata.pool, &name_pj, &value_pj) pjsip_msg_add_hdr(tdata.msg, hdr) cdef int _remove_headers_from_tdata(pjsip_tx_data *tdata, object headers) except -1: cdef pj_str_t header_name_pj cdef pjsip_hdr *hdr for header in headers: _str_to_pj_str(header, &header_name_pj) hdr = pjsip_msg_find_remove_hdr_by_name(tdata.msg, &header_name_pj, NULL) cdef int _BaseSIPURI_to_pjsip_sip_uri(BaseSIPURI uri, pjsip_sip_uri *pj_uri, pj_pool_t *pool) except -1: cdef pjsip_param *param pjsip_sip_uri_init(pj_uri, uri.secure) if uri.user: _str_to_pj_str(uri.user, &pj_uri.user) if uri.password: _str_to_pj_str(uri.password, &pj_uri.passwd) if uri.host: _str_to_pj_str(uri.host, &pj_uri.host) if uri.port: pj_uri.port = uri.port for name, value in uri.parameters.iteritems(): if name == "lr": pj_uri.lr_param = 1 elif name == "maddr": _str_to_pj_str(value, &pj_uri.maddr_param) elif name == "method": _str_to_pj_str(value, &pj_uri.method_param) elif name == "transport": _str_to_pj_str(value, &pj_uri.transport_param) elif name == "ttl": pj_uri.ttl_param = int(value) elif name == "user": _str_to_pj_str(value, &pj_uri.user_param) else: param = pj_pool_alloc(pool, sizeof(pjsip_param)) _str_to_pj_str(name, ¶m.name) if value is None: param.value.slen = 0 else: _str_to_pj_str(value, ¶m.value) pj_list_insert_after( &pj_uri.other_param, param) _dict_to_pjsip_param(uri.headers, &pj_uri.header_param, pool) return 0 cdef int _BaseRouteHeader_to_pjsip_route_hdr(BaseIdentityHeader header, pjsip_route_hdr *pj_header, pj_pool_t *pool) except -1: cdef pjsip_param *param cdef pjsip_sip_uri *sip_uri pjsip_route_hdr_init(NULL, pj_header) sip_uri = pj_pool_alloc(pool, sizeof(pjsip_sip_uri)) _BaseSIPURI_to_pjsip_sip_uri(header.uri, sip_uri, pool) pj_header.name_addr.uri = sip_uri if header.display_name: _str_to_pj_str(header.display_name.encode('utf-8'), &pj_header.name_addr.display) _dict_to_pjsip_param(header.parameters, &pj_header.other_param, pool) return 0 def _get_device_name_encoding(): if sys.platform == 'win32': encoding = 'mbcs' elif sys.platform.startswith('linux2') and Version.parse(platform.release()) < Version(2,6,31): encoding = 'latin1' else: encoding = 'utf-8' return encoding _device_name_encoding = _get_device_name_encoding() def decode_device_name(device_name): # ignore decoding errors, some systems (I'm looking at you, OSX), seem to misbehave return device_name.decode(_device_name_encoding, 'ignore') # globals cdef object _re_pj_status_str_def = re.compile("^.*\((.*)\)$") cdef object _re_warning_hdr = re.compile('(?P[0-9]{3}) (?P.*?) "(?P.*?)"') sip_status_messages = SIPStatusMessages() ================================================ FILE: sipsimple/core/_core.video.pxi ================================================ import weakref cdef class VideoProducer: def __init__(self): self._consumers = set() def __cinit__(self, *args, **kwargs): cdef PJSIPUA ua cdef pj_pool_t *pool cdef int status cdef bytes lock_name, pool_name ua = _get_ua() lock_name = b"VideoProducer_lock_%d" % id(self) pool_name = b"VideoProducer_pool_%d" % id(self) pool = ua.create_memory_pool(pool_name, 4096, 4096) self._pool = pool status = pj_mutex_create_recursive(pool, lock_name, &self._lock) if status != 0: raise PJSIPError("Could not create lock", status) self._running = 0 self._started = 0 self._closed = 0 def __dealloc__(self): # cython will always call the __dealloc__ method of the parent class *after* the child's # __dealloc__ was executed cdef PJSIPUA ua try: ua = _get_ua() except: return if self._lock != NULL: pj_mutex_destroy(self._lock) ua.release_memory_pool(self._pool) property consumers: def __get__(self): cdef pj_mutex_t *lock lock = self._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: return self._consumers.copy() finally: with nogil: pj_mutex_unlock(lock) property closed: def __get__(self): cdef pj_mutex_t *lock lock = self._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: return bool(self._closed) finally: with nogil: pj_mutex_unlock(lock) cdef void _add_consumer(self, VideoConsumer consumer): raise NotImplementedError cdef void _remove_consumer(self, VideoConsumer consumer): raise NotImplementedError def start(self): raise NotImplementedError def stop(self): raise NotImplementedError def close(self): raise NotImplementedError cdef class VideoConsumer: def __init__(self): self._producer = None def __cinit__(self, *args, **kwargs): cdef PJSIPUA ua cdef pj_pool_t *pool cdef int status cdef bytes lock_name, pool_name self.weakref = weakref.ref(self) Py_INCREF(self.weakref) ua = _get_ua() lock_name = b"VideoConsumer_lock_%d" % id(self) pool_name = b"VideoConsumer_pool_%d" % id(self) pool = ua.create_memory_pool(pool_name, 4096, 4096) self._pool = pool status = pj_mutex_create_recursive(pool, lock_name, &self._lock) if status != 0: raise PJSIPError("Could not create lock", status) self._running = 0 self._closed = 0 def __dealloc__(self): # cython will always call the __dealloc__ method of the parent class *after* the child's # __dealloc__ was executed cdef PJSIPUA ua cdef Timer timer try: ua = _get_ua() except: return if self._lock != NULL: pj_mutex_destroy(self._lock) ua.release_memory_pool(self._pool) timer = Timer() try: timer.schedule(60, deallocate_weakref, self.weakref) except SIPCoreError: pass property producer: def __get__(self): cdef pj_mutex_t *lock lock = self._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._closed: return None return self._producer finally: with nogil: pj_mutex_unlock(lock) def __set__(self, value): cdef PJSIPUA ua cdef pj_mutex_t *global_lock cdef pj_mutex_t *lock try: ua = _get_ua() except: return global_lock = ua.video_lock lock = self._lock with nogil: status = pj_mutex_lock(global_lock) if status != 0: raise PJSIPError("failed to acquire global video lock", status) with nogil: status = pj_mutex_lock(lock) if status != 0: pj_mutex_unlock(global_lock) raise PJSIPError("failed to acquire lock", status) try: if self._closed: return self._set_producer(value) finally: with nogil: pj_mutex_unlock(lock) pj_mutex_unlock(global_lock) property closed: def __get__(self): cdef pj_mutex_t *lock lock = self._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: return bool(self._closed) finally: with nogil: pj_mutex_unlock(lock) cdef void _set_producer(self, VideoProducer producer): # Called to set producer, which can be None. Must set self._producer. # No need to hold the lock or check for closed state. raise NotImplementedError def close(self): raise NotImplementedError cdef class VideoCamera(VideoProducer): # NOTE: we use a video tee to be able to send the video to multiple consumers at the same # time. The video tee, however, is not thread-safe, so we need to make sure the source port # is stopped before adding or removing a destination port. def __init__(self, unicode device, object resolution, int fps): cdef pjmedia_vid_port_param vp_param cdef pjmedia_vid_dev_info vdi cdef pjmedia_vid_port *video_port cdef pjmedia_port *video_tee cdef pjmedia_format fmt cdef pj_mutex_t *lock cdef pj_pool_t *pool cdef int status cdef int device_id cdef int dev_count cdef int width cdef int height cdef PJSIPUA ua super(VideoCamera, self).__init__() ua = _get_ua() lock = self._lock pool = self._pool with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._video_port != NULL: raise SIPCoreError("VideoCamera.__init__() was already called") dev_count = pjmedia_vid_dev_count() if dev_count == 0: raise SIPCoreError("no video devices available") if device is None: status = pjmedia_vid_dev_lookup("Null", "Null video device", &device_id) if status != 0: raise PJSIPError("Could not get capture video device index", status) else: device_id = PJMEDIA_VID_DEFAULT_CAPTURE_DEV # Find the device matching the name if device != u"system_default": for i in range(dev_count): with nogil: status = pjmedia_vid_dev_get_info(i, &vdi) if status != 0: continue if vdi.dir in (PJMEDIA_DIR_CAPTURE, PJMEDIA_DIR_CAPTURE_PLAYBACK) and decode_device_name(vdi.name) == device: device_id = vdi.id break with nogil: status = pjmedia_vid_dev_get_info(device_id, &vdi) if status != 0: raise PJSIPError("Could not get video device info", status) if not ua.enable_colorbar_device and bytes(vdi.driver) == "Colorbar": raise SIPCoreError("no video devices available") if bytes(vdi.driver) in ("Colorbar", "Null"): # override camera fps fps = 5 self.name = device self.real_name = decode_device_name(vdi.name) if device is not None else None pjmedia_vid_port_param_default(&vp_param) with nogil: status = pjmedia_vid_dev_default_param(pool, device_id, &vp_param.vidparam) if status != 0: raise PJSIPError("Could not get video device default parameters", status) # Create capture video port vp_param.active = 1 vp_param.vidparam.dir = PJMEDIA_DIR_CAPTURE # Set maximum possible resolution vp_param.vidparam.fmt.det.vid.size.w = resolution.width vp_param.vidparam.fmt.det.vid.size.h = resolution.height # Set maximum fps vp_param.vidparam.fmt.det.vid.fps.num = fps vp_param.vidparam.fmt.det.vid.fps.denum = 1 with nogil: status = pjmedia_vid_port_create(pool, &vp_param, &video_port) if status != 0: raise PJSIPError("Could not create capture video port", status) self._video_port = video_port # Get format info fmt = vp_param.vidparam.fmt # Create video tee with nogil: status = pjmedia_vid_tee_create(pool, &fmt, 255, &video_tee) if status != 0: raise PJSIPError("Could not create video tee", status) self._video_tee = video_tee # Connect capture and video tee ports with nogil: status = pjmedia_vid_port_connect(video_port, video_tee, 0) if status != 0: raise PJSIPError("Could not connect video capture and tee ports", status) self.producer_port = self._video_tee finally: with nogil: pj_mutex_unlock(lock) property framerate: def __get__(self): cdef int status cdef pj_mutex_t *lock cdef pjmedia_vid_dev_stream *stream cdef pjmedia_vid_dev_param param cdef PJSIPUA ua ua = _get_ua() lock = self._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) if self._closed: return None stream = pjmedia_vid_port_get_stream(self._video_port) if stream == NULL: return None try: with nogil: status = pjmedia_vid_dev_stream_get_param(stream, ¶m) if status != 0: return None return float(param.fmt.det.vid.fps.num) / param.fmt.det.vid.fps.denum finally: with nogil: pj_mutex_unlock(lock) property framesize: def __get__(self): cdef int status cdef pj_mutex_t *lock cdef pjmedia_vid_dev_stream *stream cdef pjmedia_vid_dev_param param cdef PJSIPUA ua ua = _get_ua() lock = self._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) if self._closed: return (-1, -1) stream = pjmedia_vid_port_get_stream(self._video_port) if stream == NULL: return (-1, -1) try: with nogil: status = pjmedia_vid_dev_stream_get_param(stream, ¶m) if status != 0: return (-1, -1) return (param.fmt.det.vid.size.w, param.fmt.det.vid.size.h) finally: with nogil: pj_mutex_unlock(lock) def start(self): cdef int status cdef pj_mutex_t *lock lock = self._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._closed: raise SIPCoreError("video device is closed") if self._started: return if self._consumers: self._start() self._started = 1 finally: with nogil: pj_mutex_unlock(lock) def stop(self): cdef int status cdef pj_mutex_t *lock lock = self._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._closed: raise SIPCoreError("video device is closed") if not self._started: return self._stop() self._started = 0 finally: with nogil: pj_mutex_unlock(lock) def close(self): cdef int status cdef pj_mutex_t *lock cdef pj_mutex_t *global_lock cdef PJSIPUA ua try: ua = _get_ua() except: return global_lock = ua.video_lock lock = self._lock with nogil: status = pj_mutex_lock(global_lock) if status != 0: raise PJSIPError("failed to acquire global video lock", status) with nogil: status = pj_mutex_lock(lock) if status != 0: pj_mutex_unlock(global_lock) raise PJSIPError("failed to acquire lock", status) try: if self._closed: return self.stop() for c in self._consumers.copy(): c.producer = None self._closed = 1 if self._video_port != NULL: with nogil: pjmedia_vid_port_stop(self._video_port) pjmedia_vid_port_disconnect(self._video_port) pjmedia_vid_port_destroy(self._video_port) if self._video_tee != NULL: pjmedia_port_destroy(self._video_tee) self._video_port = NULL self._video_tee = NULL finally: with nogil: pj_mutex_unlock(lock) pj_mutex_unlock(global_lock) cdef void _add_consumer(self, VideoConsumer consumer): cdef int status cdef pj_mutex_t *lock cdef pjmedia_port *consumer_port cdef pjmedia_port *producer_port lock = self._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._closed: raise SIPCoreError("video device is closed") if consumer in self._consumers: return consumer_port = consumer.consumer_port producer_port = self.producer_port with nogil: status = pjmedia_vid_tee_add_dst_port2(producer_port, 0, consumer_port) if status != 0: raise PJSIPError("Could not connect video consumer with producer", status) self._consumers.add(consumer) if self._started: self._start() finally: with nogil: pj_mutex_unlock(lock) cdef void _remove_consumer(self, VideoConsumer consumer): cdef int status cdef pj_mutex_t *lock lock = self._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._closed: raise SIPCoreError("video device is closed") if consumer not in self._consumers: return consumer_port = consumer.consumer_port producer_port = self.producer_port with nogil: status = pjmedia_vid_tee_remove_dst_port(producer_port, consumer_port) if status != 0: raise PJSIPError("Could not disconnect video consumer from producer", status) self._consumers.remove(consumer) if not self._consumers: self._stop() finally: with nogil: pj_mutex_unlock(lock) cdef void _start(self): # No need to hold the lock, this function is always called with it held if self._running: return _start_video_port(self._video_port) self._running = 1 cdef void _stop(self): # No need to hold the lock, this function is always called with it held if not self._running: return _stop_video_port(self._video_port) self._running = 0 def __dealloc__(self): self.close() cdef class LocalVideoStream(VideoConsumer): cdef void _initialize(self, pjmedia_port *media_port): self.consumer_port = media_port self._running = 1 self._closed = 0 cdef void _set_producer(self, VideoProducer producer): old_producer = self._producer if old_producer is producer: return if old_producer is not None and not old_producer.closed: old_producer._remove_consumer(self) self._producer = producer if producer is not None: producer._add_consumer(self) def close(self): cdef int status cdef pj_mutex_t *global_lock cdef pj_mutex_t *lock cdef PJSIPUA ua try: ua = _get_ua() except: return global_lock = ua.video_lock lock = self._lock with nogil: status = pj_mutex_lock(global_lock) if status != 0: raise PJSIPError("failed to acquire global video lock", status) with nogil: status = pj_mutex_lock(lock) if status != 0: pj_mutex_unlock(global_lock) raise PJSIPError("failed to acquire lock", status) try: if self._closed: return self._set_producer(None) self._closed = 1 finally: with nogil: pj_mutex_unlock(lock) pj_mutex_unlock(global_lock) cdef LocalVideoStream_create(pjmedia_vid_stream *stream): cdef pjmedia_port *media_port cdef int status with nogil: status = pjmedia_vid_stream_get_port(stream, PJMEDIA_DIR_ENCODING, &media_port) if status != 0: raise PJSIPError("failed to get video stream port", status) if media_port == NULL: raise ValueError("invalid media port") obj = LocalVideoStream() obj._initialize(media_port) return obj cdef class RemoteVideoStream(VideoProducer): def __init__(self, object event_handler=None): super(RemoteVideoStream, self).__init__() if event_handler is not None and not callable(event_handler): raise TypeError("event_handler must be a callable or None") self._event_handler = event_handler cdef void _initialize(self, pjmedia_vid_stream *stream): cdef pjmedia_port *media_port cdef int status cdef void* ptr with nogil: status = pjmedia_vid_stream_get_port(stream, PJMEDIA_DIR_DECODING, &media_port) if status != 0: raise PJSIPError("failed to get video stream port", status) if media_port == NULL: raise ValueError("invalid media port") self._video_stream = stream ptr = self with nogil: pjmedia_event_subscribe(NULL, &RemoteVideoStream_on_event, ptr, media_port); # TODO: we cannot use a tee here, because the remote video is a passive port, we have a pjmedia_port, not a # pjmedia_vid_port, so, for now, only one consumer is allowed self.producer_port = media_port self._running = 1 self._closed = 0 property framerate: def __get__(self): cdef int status cdef pj_mutex_t *lock cdef pjmedia_vid_stream *stream cdef pjmedia_vid_stream_info info cdef PJSIPUA ua ua = _get_ua() lock = self._lock stream = self._video_stream with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) if self._closed: return 0 try: with nogil: status = pjmedia_vid_stream_get_info(stream, &info) if status != 0: return 0 return float(info.codec_param.dec_fmt.det.vid.fps.num) / info.codec_param.dec_fmt.det.vid.fps.denum finally: with nogil: pj_mutex_unlock(lock) property framesize: def __get__(self): cdef int status cdef pj_mutex_t *lock cdef pjmedia_vid_stream *stream cdef pjmedia_vid_stream_info info cdef PJSIPUA ua ua = _get_ua() lock = self._lock stream = self._video_stream with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) if self._closed: return (-1, -1) try: with nogil: status = pjmedia_vid_stream_get_info(stream, &info) if status != 0: return (-1, -1) return (info.codec_param.dec_fmt.det.vid.size.w, info.codec_param.dec_fmt.det.vid.size.h) finally: with nogil: pj_mutex_unlock(lock) def start(self): pass def stop(self): pass def close(self): cdef int status cdef pj_mutex_t *global_lock cdef pj_mutex_t *lock cdef PJSIPUA ua cdef VideoConsumer consumer cdef void* ptr cdef pjmedia_port *media_port try: ua = _get_ua() except: return global_lock = ua.video_lock lock = self._lock with nogil: status = pj_mutex_lock(global_lock) if status != 0: raise PJSIPError("failed to acquire global video lock", status) with nogil: status = pj_mutex_lock(lock) if status != 0: pj_mutex_unlock(global_lock) raise PJSIPError("failed to acquire lock", status) try: if self._closed: return if self._consumers: consumer = self._consumers.pop() consumer.producer = None ptr = self media_port = self.producer_port with nogil: pjmedia_event_unsubscribe(NULL, &RemoteVideoStream_on_event, ptr, media_port) self._closed = 1 self._event_handler = None finally: with nogil: pj_mutex_unlock(lock) pj_mutex_unlock(global_lock) cdef void _add_consumer(self, VideoConsumer consumer): cdef int status cdef pj_mutex_t *lock cdef pjmedia_port *producer_port cdef pjmedia_vid_port *consumer_port cdef PJSIPUA ua ua = _get_ua() lock = self._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._closed: raise SIPCoreError("producer is closed") if consumer in self._consumers: return if self._consumers: raise SIPCoreError("another consumer is already attached to this producer") consumer_port = consumer._video_port producer_port = self.producer_port with nogil: status = pjmedia_vid_port_connect(consumer_port, producer_port, 0) if status != 0: raise PJSIPError("Could not connect video consumer with producer", status) self._consumers.add(consumer) finally: with nogil: pj_mutex_unlock(lock) cdef void _remove_consumer(self, VideoConsumer consumer): cdef int status cdef pj_mutex_t *lock cdef PJSIPUA ua cdef pjmedia_vid_port *consumer_port ua = _get_ua() lock = self._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._closed: raise SIPCoreError("producer is closed") if consumer not in self._consumers: return consumer_port = consumer._video_port with nogil: status = pjmedia_vid_port_disconnect(consumer_port) if status != 0: raise PJSIPError("Could not disconnect video consumer from producer", status) self._consumers.remove(consumer) finally: with nogil: pj_mutex_unlock(lock) cdef class FrameBufferVideoRenderer(VideoConsumer): def __init__(self, frame_handler): super(FrameBufferVideoRenderer, self).__init__() if not callable(frame_handler): raise TypeError('frame_handler must be callable') self._frame_handler = frame_handler cdef _initialize(self, VideoProducer producer): cdef pjmedia_vid_port_param vp_param cdef pjmedia_vid_port *video_port cdef pjmedia_vid_dev_stream *video_stream cdef pjmedia_format fmt cdef pjmedia_port *consumer_port cdef pj_pool_t *pool cdef pj_mutex_t *lock cdef int status cdef int index cdef void *ptr lock = self._lock pool = self._pool with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._video_port != NULL: raise RuntimeError("object already initialized") if not isinstance(producer, (VideoCamera, RemoteVideoStream)): raise TypeError("unsupported producer type: %s" % producer.__class__) status = pjmedia_vid_dev_lookup("FrameBuffer", "FrameBuffer renderer", &index) if status != 0: raise PJSIPError("Could not get render video device index", status) pjmedia_vid_port_param_default(&vp_param) with nogil: status = pjmedia_vid_dev_default_param(pool, index, &vp_param.vidparam) if status != 0: raise PJSIPError("Could not get render video device default parameters", status) fmt = producer.producer_port.info.fmt vp_param.active = 0 if isinstance(producer, VideoCamera) else 1 vp_param.vidparam.dir = PJMEDIA_DIR_RENDER; vp_param.vidparam.fmt = fmt vp_param.vidparam.disp_size = fmt.det.vid.size vp_param.vidparam.flags = 0 with nogil: status = pjmedia_vid_port_create(pool, &vp_param, &video_port) if status != 0: raise PJSIPError("Could not create consumer video port", status) self._video_port = video_port if not vp_param.active: with nogil: consumer_port = pjmedia_vid_port_get_passive_port(video_port) if consumer_port == NULL: raise SIPCoreError("Could not get passive video port") else: consumer_port = NULL self.consumer_port = consumer_port with nogil: video_stream = pjmedia_vid_port_get_stream(video_port) if video_stream == NULL: raise SIPCoreError("invalid video device stream") self._video_stream = video_stream ptr = self.weakref status = pjmedia_vid_dev_fb_set_callback(video_stream, FrameBufferVideoRenderer_frame_handler, ptr) if status != 0: raise PJSIPError("Could not set frame handler callback", status) finally: with nogil: pj_mutex_unlock(lock) cdef void _set_producer(self, VideoProducer producer): old_producer = self._producer if old_producer is producer: return if old_producer is not None: self._stop() old_producer._remove_consumer(self) self._destroy_video_port() self._producer = producer if producer is not None: self._initialize(producer) producer._add_consumer(self) self._start() def close(self): cdef int status cdef pj_mutex_t *global_lock cdef pj_mutex_t *lock cdef PJSIPUA ua cdef void* ptr try: ua = _get_ua() except: return global_lock = ua.video_lock lock = self._lock with nogil: status = pj_mutex_lock(global_lock) if status != 0: raise PJSIPError("failed to acquire global video lock", status) with nogil: status = pj_mutex_lock(lock) if status != 0: pj_mutex_unlock(global_lock) raise PJSIPError("failed to acquire lock", status) try: if self._closed: return self._set_producer(None) self._stop() self._closed = 1 self._destroy_video_port() self._frame_handler = None finally: with nogil: pj_mutex_unlock(lock) pj_mutex_unlock(global_lock) cdef void _destroy_video_port(self): # No need to hold the lock, this function is always called with it held cdef pjmedia_vid_port *video_port video_port = self._video_port if video_port != NULL: with nogil: pjmedia_vid_port_stop(video_port) pjmedia_vid_port_disconnect(video_port) pjmedia_vid_port_destroy(video_port) self._video_port = NULL cdef void _start(self): # No need to hold the lock, this function is always called with it held if self._running: return _start_video_port(self._video_port) self._running = 1 cdef void _stop(self): # No need to hold the lock, this function is always called with it held if not self._running: return _stop_video_port(self._video_port) self._running = 0 def __dealloc__(self): self.close() cdef RemoteVideoStream_create(pjmedia_vid_stream *stream, format_change_handler=None): obj = RemoteVideoStream(format_change_handler) obj._initialize(stream) return obj cdef void _start_video_port(pjmedia_vid_port *port): cdef int status with nogil: status = pjmedia_vid_port_start(port) if status != 0: raise PJSIPError("Could not start video port", status) cdef void _stop_video_port(pjmedia_vid_port *port): cdef int status with nogil: status = pjmedia_vid_port_stop(port) if status != 0: raise PJSIPError("Could not stop video port", status) cdef class VideoFrame: def __init__(self, str data, int width, int height): self.data = data self.width = width self.height = height property size: def __get__(self): return (self.width, self.height) cdef void FrameBufferVideoRenderer_frame_handler(pjmedia_frame_ptr_const frame, pjmedia_rect_size size, void *user_data) with gil: cdef PJSIPUA ua cdef FrameBufferVideoRenderer rend try: ua = _get_ua() except: return if user_data == NULL: return rend = ( user_data)() if rend is None: return if rend._frame_handler is not None: data = PyString_FromStringAndSize(frame.buf, frame.size) rend._frame_handler(VideoFrame(data, size.w, size.h)) cdef int RemoteVideoStream_on_event(pjmedia_event *event, void *user_data) with gil: cdef PJSIPUA ua cdef RemoteVideoStream stream cdef pjmedia_format fmt try: ua = _get_ua() except: return 0 if user_data == NULL: return 0 stream = user_data if stream._event_handler is not None: if event.type == PJMEDIA_EVENT_FMT_CHANGED: fmt = event.data.fmt_changed.new_fmt size = (fmt.det.vid.size.w, fmt.det.vid.size.h) fps = 1.0*fmt.det.vid.fps.num/fmt.det.vid.fps.denum stream._event_handler('FORMAT_CHANGED', (size, fps)) elif event.type == PJMEDIA_EVENT_KEYFRAME_FOUND: stream._event_handler('RECEIVED_KEYFRAME', None) elif event.type == PJMEDIA_EVENT_KEYFRAME_MISSING: stream._event_handler('MISSED_KEYFRAME', None) elif event.type == PJMEDIA_EVENT_KEYFRAME_REQUESTED: stream._event_handler('REQUESTED_KEYFRAME', None) else: # Pacify compiler pass return 0 ================================================ FILE: sipsimple/core/_engine.py ================================================ """ Implements a mechanism for starting the SIP core engine based on PJSIP (http://pjsip.org) stack. """ __all__ = ["Engine"] import sys import traceback import atexit from application.notification import NotificationCenter, NotificationData from application.python.types import Singleton from threading import Thread, RLock from sipsimple import log, __version__ from sipsimple.core._core import PJSIPUA, PJ_VERSION, PJ_SVN_REVISION, SIPCoreError class Engine(Thread): __metaclass__ = Singleton default_start_options = {"ip_address": None, "udp_port": 0, "tcp_port": None, "tls_port": None, "tls_verify_server": False, "tls_ca_file": None, "tls_cert_file": None, "tls_privkey_file": None, "tls_timeout": 3000, "user_agent": "sipsimple-%s-pjsip-%s-r%s" % (__version__, PJ_VERSION, PJ_SVN_REVISION), "log_level": 0, "trace_sip": False, "detect_sip_loops": True, "rtp_port_range": (50000, 50500), "zrtp_cache": None, "codecs": ["G722", "speex", "PCMU", "PCMA"], "video_codecs": ["H264", "H263-1998", "VP8"], "enable_colorbar_device": False, "events": {"conference": ["application/conference-info+xml"], "message-summary": ["application/simple-message-summary"], "presence": ["multipart/related", "application/rlmi+xml", "application/pidf+xml"], "presence.winfo": ["application/watcherinfo+xml"], "dialog": ["multipart/related", "application/rlmi+xml", "application/dialog-info+xml"], "dialog.winfo": ["application/watcherinfo+xml"], "refer": ["message/sipfrag;version=2.0"], "xcap-diff": ["application/xcap-diff+xml"]}, "incoming_events": set(), "incoming_requests": set()} def __init__(self): self.notification_center = NotificationCenter() self._thread_started = False self._thread_stopping = False self._lock = RLock() self._options = None atexit.register(self.stop) super(Engine, self).__init__() self.daemon = True @property def is_running(self): return (hasattr(self, "_ua") and hasattr(self, "_thread_started") and self._thread_started and not self._thread_stopping) def __dir__(self): if hasattr(self, '_ua'): ua_attributes = [attr for attr in dir(self._ua) if not attr.startswith('__') and attr != 'poll'] else: ua_attributes = [] return sorted(set(dir(self.__class__) + self.__dict__.keys() + ua_attributes)) def __getattr__(self, attr): if attr not in ["_ua", "poll"] and hasattr(self, "_ua") and attr in dir(self._ua): return getattr(self._ua, attr) raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, attr)) def __setattr__(self, attr, value): if attr not in ["_ua", "poll"] and hasattr(self, "_ua") and attr in dir(self._ua): setattr(self._ua, attr, value) return object.__setattr__(self, attr, value) def start(self, **kwargs): with self._lock: if self._thread_started: raise SIPCoreError("Worker thread was already started once") self._options = kwargs self._thread_started = True super(Engine, self).start() def stop(self): with self._lock: if self._thread_stopping: return if self._thread_started: self._thread_stopping = True # worker thread def run(self): self.notification_center.post_notification('SIPEngineWillStart', sender=self) init_options = Engine.default_start_options.copy() init_options.update(self._options) try: self._ua = PJSIPUA(self._handle_event, **init_options) except Exception: log.exception('Exception occurred while starting the Engine') exc_type, exc_val, exc_tb = sys.exc_info() exc_tb = "".join(traceback.format_exception(exc_type, exc_val, exc_tb)) self.notification_center.post_notification('SIPEngineGotException', sender=self, data=NotificationData(type=exc_type, value=exc_val, traceback=exc_tb)) self.notification_center.post_notification('SIPEngineDidFail', sender=self) return else: self.notification_center.post_notification('SIPEngineDidStart', sender=self) failed = False while not self._thread_stopping: try: failed = self._ua.poll() except: log.exception('Exception occurred while running the Engine') exc_type, exc_val, exc_tb = sys.exc_info() self.notification_center.post_notification('SIPEngineGotException', sender=self, data=NotificationData(type=exc_type, value=exc_val, traceback="".join(traceback.format_exception(exc_type, exc_val, exc_tb)))) failed = True if failed: self.notification_center.post_notification('SIPEngineDidFail', sender=self) break if not failed: self.notification_center.post_notification('SIPEngineWillEnd', sender=self) self._ua.dealloc() del self._ua self.notification_center.post_notification('SIPEngineDidEnd', sender=self) def _handle_event(self, event_name, **kwargs): sender = kwargs.pop("obj", None) if sender is None: sender = self self.notification_center.post_notification(event_name, sender, NotificationData(**kwargs)) ================================================ FILE: sipsimple/core/_helpers.py ================================================ """Miscellaneous SIP related helpers""" import random import socket import string from application.python.types import MarkerType from application.system import host from sipsimple.core._core import SIPURI from sipsimple.core._engine import Engine __all__ = ['Route', 'ContactURIFactory', 'NoGRUU', 'PublicGRUU', 'TemporaryGRUU', 'PublicGRUUIfAvailable', 'TemporaryGRUUIfAvailable'] class Route(object): _default_ports = dict(udp=5060, tcp=5060, tls=5061) def __init__(self, address, port=None, transport='udp'): self.address = address self.port = port self.transport = transport @property def address(self): return self._address @address.setter def address(self, address): try: socket.inet_aton(address) except: raise ValueError('illegal address: %s' % address) self._address = address @property def port(self): if self._port is None: return 5061 if self.transport == 'tls' else 5060 else: return self._port @port.setter def port(self, port): port = int(port) if port is not None else None if port is not None and not (0 < port < 65536): raise ValueError('illegal port value: %d' % port) self._port = port @property def transport(self): return self._transport @transport.setter def transport(self, transport): if transport not in ('udp', 'tcp', 'tls'): raise ValueError('illegal transport value: %s' % transport) self._transport = transport @property def uri(self): port = None if self._default_ports[self.transport] == self.port else self.port parameters = {} if self.transport == 'udp' else {'transport': self.transport} return SIPURI(host=self.address, port=port, parameters=parameters) def __repr__(self): return '{0.__class__.__name__}({0.address!r}, port={0.port!r}, transport={0.transport!r})'.format(self) def __str__(self): return str(self.uri) class ContactURIType(MarkerType): pass class NoGRUU: __metaclass__ = ContactURIType class PublicGRUU: __metaclass__ = ContactURIType class TemporaryGRUU: __metaclass__ = ContactURIType class PublicGRUUIfAvailable: __metaclass__ = ContactURIType class TemporaryGRUUIfAvailable: __metaclass__ = ContactURIType class ContactURIFactory(object): def __init__(self, username=None): self.username = username or ''.join(random.sample(string.digits, 8)) self.public_gruu = None self.temporary_gruu = None def __repr__(self): return '{0.__class__.__name__}(username={0.username!r})'.format(self) def __getitem__(self, key): if isinstance(key, tuple): contact_type, key = key if not isinstance(contact_type, ContactURIType): raise KeyError("unsupported contact type: %r" % contact_type) else: contact_type = NoGRUU if not isinstance(key, (basestring, Route)): raise KeyError("key must be a transport name or Route instance") transport = key if isinstance(key, basestring) else key.transport parameters = {} if transport == 'udp' else {'transport': transport} if contact_type is PublicGRUU: if self.public_gruu is None: raise KeyError("could not get Public GRUU") uri = SIPURI.new(self.public_gruu) elif contact_type is TemporaryGRUU: if self.temporary_gruu is None: raise KeyError("could not get Temporary GRUU") uri = SIPURI.new(self.temporary_gruu) elif contact_type is PublicGRUUIfAvailable and self.public_gruu is not None: uri = SIPURI.new(self.public_gruu) elif contact_type is TemporaryGRUUIfAvailable and self.temporary_gruu is not None: uri = SIPURI.new(self.temporary_gruu) else: ip = host.default_ip if isinstance(key, basestring) else host.outgoing_ip_for(key.address) if ip is None: raise KeyError("could not get outgoing IP address") port = getattr(Engine(), '%s_port' % transport, None) if port is None: raise KeyError("unsupported transport: %s" % transport) uri = SIPURI(user=self.username, host=ip, port=port) uri.parameters.update(parameters) return uri ================================================ FILE: sipsimple/core/_primitives.py ================================================ """ Implements a high-level mechanism for SIP methods that can be used for non-session based operations like REGISTER, SUBSCRIBE, PUBLISH and MESSAGE. """ __all__ = ["Message", "Registration", "Publication", "PublicationError", "PublicationETagError"] from threading import RLock from application.notification import IObserver, NotificationCenter, NotificationData from application.python import Null from zope.interface import implements from sipsimple.core._core import ContactHeader, Header, Request, RouteHeader, SIPCoreError, SIPURI, ToHeader class Registration(object): implements(IObserver) def __init__(self, from_header, credentials=None, duration=300, extra_headers=None): self.from_header = from_header self.credentials = credentials self.duration = duration self.extra_headers = extra_headers or [] self._current_request = None self._last_request = None self._unregistering = False self._lock = RLock() is_registered = property(lambda self: self._last_request is not None) contact_uri = property(lambda self: None if self._last_request is None else self._last_request.contact_uri) expires_in = property(lambda self: 0 if self._last_request is None else self._last_request.expires_in) peer_address = property(lambda self: None if self._last_request is None else self._last_request.peer_address) def register(self, contact_header, route_header, timeout=None): with self._lock: try: self._make_and_send_request(contact_header, route_header, timeout, True) except SIPCoreError, e: notification_center = NotificationCenter() notification_center.post_notification('SIPRegistrationDidFail', sender=self, data=NotificationData(code=0, reason=e.args[0], route_header=route_header)) def end(self, timeout=None): with self._lock: if self._last_request is None: return notification_center = NotificationCenter() notification_center.post_notification('SIPRegistrationWillEnd', sender=self) try: self._make_and_send_request(ContactHeader.new(self._last_request.contact_header), RouteHeader.new(self._last_request.route_header), timeout, False) except SIPCoreError, e: notification_center.post_notification('SIPRegistrationDidNotEnd', sender=self, data=NotificationData(code=0, reason=e.args[0])) def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_SIPRequestDidSucceed(self, notification): request = notification.sender with self._lock: if request is not self._current_request: return self._current_request = None if self._unregistering: if self._last_request is not None: self._last_request.end() self._last_request = None notification.center.post_notification('SIPRegistrationDidEnd', sender=self, data=NotificationData(expired=False)) else: self._last_request = request try: contact_header_list = notification.data.headers["Contact"] except KeyError: contact_header_list = [] notification.center.post_notification('SIPRegistrationDidSucceed', sender=self, data=NotificationData(code=notification.data.code, reason=notification.data.reason, contact_header=request.contact_header, contact_header_list=contact_header_list, expires_in=notification.data.expires, route_header=request.route_header)) def _NH_SIPRequestDidFail(self, notification): request = notification.sender with self._lock: if request is not self._current_request: return self._current_request = None if self._unregistering: notification.center.post_notification('SIPRegistrationDidNotEnd', sender=self, data=NotificationData(code=notification.data.code, reason=notification.data.reason)) else: if hasattr(notification.data, 'headers'): min_expires = notification.data.headers.get('Min-Expires', None) else: min_expires = None notification.center.post_notification('SIPRegistrationDidFail', sender=self, data=NotificationData(code=notification.data.code, reason=notification.data.reason, route_header=request.route_header, min_expires=min_expires)) def _NH_SIPRequestWillExpire(self, notification): with self._lock: if notification.sender is not self._last_request: return notification.center.post_notification('SIPRegistrationWillExpire', sender=self, data=NotificationData(expires=notification.data.expires)) def _NH_SIPRequestDidEnd(self, notification): request = notification.sender with self._lock: notification.center.remove_observer(self, sender=request) if request is not self._last_request: return self._last_request = None if self._current_request is not None: self._current_request.end() self._current_request = None notification.center.post_notification('SIPRegistrationDidEnd', sender=self, data=NotificationData(expired=True)) def _make_and_send_request(self, contact_header, route_header, timeout, do_register): notification_center = NotificationCenter() prev_request = self._current_request or self._last_request if prev_request is not None: call_id = prev_request.call_id cseq = prev_request.cseq + 1 else: call_id = None cseq = 1 extra_headers = [] extra_headers.append(Header("Expires", str(int(self.duration) if do_register else 0))) extra_headers.extend(self.extra_headers) request = Request("REGISTER", SIPURI(self.from_header.uri.host), self.from_header, ToHeader.new(self.from_header), route_header, credentials=self.credentials, contact_header=contact_header, call_id=call_id, cseq=cseq, extra_headers=extra_headers) notification_center.add_observer(self, sender=request) if self._current_request is not None: # we are trying to send something already, cancel whatever it is self._current_request.end() self._current_request = None try: request.send(timeout=timeout) except: notification_center.remove_observer(self, sender=request) raise self._unregistering = not do_register self._current_request = request class Message(object): implements(IObserver) def __init__(self, from_header, to_header, route_header, content_type, body, credentials=None, extra_headers=None): self._request = Request("MESSAGE", to_header.uri, from_header, to_header, route_header, credentials=credentials, extra_headers=extra_headers, content_type=content_type, body=body) self._lock = RLock() from_header = property(lambda self: self._request.from_header) to_header = property(lambda self: self._request.to_header) route_header = property(lambda self: self._request.route_header) content_type = property(lambda self: self._request.content_type) body = property(lambda self: self._request.body) credentials = property(lambda self: self._request.credentials) is_sent = property(lambda self: self._request.state != "INIT") in_progress = property(lambda self: self._request.state == "IN_PROGRESS") peer_address = property(lambda self: self._request.peer_address) def send(self, timeout=None): notification_center = NotificationCenter() with self._lock: if self.is_sent: raise RuntimeError("This MESSAGE was already sent") notification_center.add_observer(self, sender=self._request) try: self._request.send(timeout) except: notification_center.remove_observer(self, sender=self._request) raise def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_SIPRequestDidSucceed(self, notification): if notification.data.expires: # this shouldn't happen really notification.sender.end() notification.center.post_notification('SIPMessageDidSucceed', sender=self, data=notification.data) def _NH_SIPRequestDidFail(self, notification): notification.center.post_notification('SIPMessageDidFail', sender=self, data=notification.data) def _NH_SIPRequestDidEnd(self, notification): notification.center.remove_observer(self, sender=notification.sender) class PublicationError(Exception): pass class PublicationETagError(PublicationError): pass class Publication(object): implements(IObserver) def __init__(self, from_header, event, content_type, credentials=None, duration=300, extra_headers=None): self.from_header = from_header self.event = event self.content_type = content_type self.credentials = credentials self.duration = duration self.extra_headers = extra_headers or [] self._last_etag = None self._current_request = None self._last_request = None self._unpublishing = False self._lock = RLock() is_published = property(lambda self: self._last_request is not None) expires_in = property(lambda self: 0 if self._last_request is None else self._last_request.expires_in) peer_address = property(lambda self: None if self._last_request is None else self._last_request.peer_address) def publish(self, body, route_header, timeout=None): with self._lock: if body is None: if self._last_request is None: raise ValueError("Need body for initial PUBLISH") elif self._last_etag is None: raise PublicationETagError("Cannot refresh, last ETag was invalid") self._make_and_send_request(body, route_header, timeout, True) def end(self, timeout=None): with self._lock: if self._last_request is None: return notification_center = NotificationCenter() notification_center.post_notification('SIPPublicationWillEnd', sender=self) try: self._make_and_send_request(None, RouteHeader.new(self._last_request.route_header), timeout, False) except SIPCoreError, e: notification_center.post_notification('SIPPublicationDidNotEnd', sender=self, data=NotificationData(code=0, reason=e.args[0])) def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_SIPRequestDidSucceed(self, notification): request = notification.sender with self._lock: if request is not self._current_request: return self._current_request = None if self._unpublishing: if self._last_request is not None: self._last_request.end() self._last_request = None self._last_etag = None notification.center.post_notification('SIPPublicationDidEnd', sender=self, data=NotificationData(expired=False)) else: self._last_request = request self._last_etag = notification.data.headers["SIP-ETag"].body if "SIP-ETag" in notification.data.headers else None # TODO: add more data? notification.center.post_notification('SIPPublicationDidSucceed', sender=self, data=NotificationData(code=notification.data.code, reason=notification.data.reason, expires_in=notification.data.expires, route_header=request.route_header)) def _NH_SIPRequestDidFail(self, notification): request = notification.sender with self._lock: if request is not self._current_request: return self._current_request = None if notification.data.code == 412: self._last_etag = None if self._unpublishing: notification.center.post_notification('SIPPublicationDidNotEnd', sender=self, data=NotificationData(code=notification.data.code, reason=notification.data.reason)) else: notification.center.post_notification('SIPPublicationDidFail', sender=self, data=NotificationData(code=notification.data.code, reason=notification.data.reason, route_header=request.route_header)) def _NH_SIPRequestWillExpire(self, notification): with self._lock: if notification.sender is not self._last_request: return notification.center.post_notification('SIPPublicationWillExpire', sender=self, data=NotificationData(expires=notification.data.expires)) def _NH_SIPRequestDidEnd(self, notification): with self._lock: notification.center.remove_observer(self, sender=notification.sender) if notification.sender is not self._last_request: return self._last_request = None if self._current_request is not None: self._current_request.end() self._current_request = None self._last_etag = None notification.center.post_notification('SIPPublicationDidEnd', sender=self, data=NotificationData(expired=True)) def _make_and_send_request(self, body, route_header, timeout, do_publish): notification_center = NotificationCenter() extra_headers = [] extra_headers.append(Header("Event", self.event)) extra_headers.append(Header("Expires", str(int(self.duration) if do_publish else 0))) if self._last_etag is not None: extra_headers.append(Header("SIP-If-Match", self._last_etag)) extra_headers.extend(self.extra_headers) content_type = (self.content_type if body is not None else None) request = Request("PUBLISH", self.from_header.uri, self.from_header, ToHeader.new(self.from_header), route_header, credentials=self.credentials, cseq=1, extra_headers=extra_headers, content_type=content_type, body=body) notification_center.add_observer(self, sender=request) if self._current_request is not None: # we are trying to send something already, cancel whatever it is self._current_request.end() self._current_request = None try: request.send(timeout=timeout) except: notification_center.remove_observer(self, sender=request) raise self._unpublishing = not do_publish self._current_request = request ================================================ FILE: sipsimple/logging.py ================================================ from __future__ import absolute_import from application import log # Use a named logger for sipsimple logging log = log.get_logger('sipsimple') ================================================ FILE: sipsimple/lookup.py ================================================ """ Implements DNS lookups in the context of SIP, STUN and MSRP relay based on RFC3263 and related standards. This can be used to determine the next hop(s) and failover for routing of SIP messages and reservation of network resources prior the starting of a SIP session. """ from __future__ import absolute_import import re from itertools import chain from time import time from urlparse import urlparse # patch dns.entropy module which is not thread-safe import dns import sys from functools import partial from random import randint, randrange dns.entropy = dns.__class__('dns.entropy') dns.entropy.__file__ = dns.__file__.replace('__init__.py', 'entropy.py') dns.entropy.__builtins__ = dns.__builtins__ dns.entropy.random_16 = partial(randrange, 2**16) dns.entropy.between = randint sys.modules['dns.entropy'] = dns.entropy del partial, randint, randrange, sys # replace standard select and socket modules with versions from eventlib from eventlib import coros, proc from eventlib.green import select from eventlib.green import socket import dns.name import dns.resolver import dns.query dns.resolver.socket = socket dns.query.socket = socket dns.query.select = select dns.query._set_polling_backend(dns.query._select_for) from application.notification import IObserver, NotificationCenter, NotificationData from application.python import Null, limit from application.python.decorator import decorator, preserve_signature from application.python.types import Singleton from dns import exception, rdatatype from twisted.internet import reactor from zope.interface import implements from sipsimple.core import Route from sipsimple.threading import run_in_twisted_thread from sipsimple.threading.green import Command, InterruptCommand, run_in_waitable_green_thread def domain_iterator(domain): """ A generator which returns the domain and its parent domains. """ while domain not in ('.', ''): yield domain domain = (domain.split('.', 1)+[''])[1] @decorator def post_dns_lookup_notifications(func): @preserve_signature(func) def wrapper(obj, *args, **kwargs): notification_center = NotificationCenter() try: result = func(obj, *args, **kwargs) except DNSLookupError, e: notification_center.post_notification('DNSLookupDidFail', sender=obj, data=NotificationData(error=str(e))) raise else: notification_center.post_notification('DNSLookupDidSucceed', sender=obj, data=NotificationData(result=result)) return result return wrapper class DNSLookupError(Exception): """ The error raised by DNSLookup when a lookup cannot be performed. """ class DNSCache(object): """ A simple DNS cache which uses twisted's timers to invalidate its expired data. """ def __init__(self): self.data = {} def get(self, key): return self.data.get(key, None) def put(self, key, value): expiration = value.expiration-time() if expiration > 0: self.data[key] = value reactor.callLater(limit(expiration, max=3600), self.data.pop, key, None) def flush(self, key=None): if key is not None: self.data.pop(key, None) else: self.data = {} class InternalResolver(dns.resolver.Resolver): def __init__(self, *args, **kw): super(InternalResolver, self).__init__(*args, **kw) if self.domain.to_text().endswith('local.'): self.domain = dns.name.root self.search = [item for item in self.search if not item.to_text().endswith('local.')] class DNSResolver(dns.resolver.Resolver): """ The resolver used by DNSLookup. The lifetime setting on it applies to all the queries made on this resolver. Each time a query is performed, its duration is subtracted from the lifetime value. """ def __init__(self): dns.resolver.Resolver.__init__(self, configure=False) dns_manager = DNSManager() self.search = dns_manager.search self.domain = dns_manager.domain self.nameservers = dns_manager.nameservers def query(self, *args, **kw): start_time = time() try: return dns.resolver.Resolver.query(self, *args, **kw) finally: self.lifetime -= min(self.lifetime, time()-start_time) class SRVResult(object): """ Internal object used to save the result of SRV queries. """ def __init__(self, priority, weight, port, address): self.priority = priority self.weight = weight self.port = port self.address = address class NAPTRResult(object): """ Internal object used to save the result of NAPTR queries. """ def __init__(self, service, order, preference, priority, weight, port, address): self.service = service self.order = order self.preference = preference self.priority = priority self.weight = weight self.port = port self.address = address class DNSLookup(object): cache = DNSCache() @run_in_waitable_green_thread @post_dns_lookup_notifications def lookup_service(self, uri, service, timeout=3.0, lifetime=15.0): """ Performs an SRV query to determine the servers used for the specified service from the domain in uri.host. If this fails and falling back is supported, also performs an A query on uri.host, returning the default port of the service along with the IP addresses in the answer. The services supported are `stun' and 'msrprelay'. The DNSLookupDidSucceed notification contains a result attribute which is a list of (address, port) tuples. The DNSLookupDidFail notification contains an error attribute describing the error encountered. """ service_srv_record_map = {"stun": ("_stun._udp", 3478, False), "msrprelay": ("_msrps._tcp", 2855, True)} log_context = dict(context='lookup_service', service=service, uri=uri) try: service_prefix, service_port, service_fallback = service_srv_record_map[service] except KeyError: raise DNSLookupError("Unknown service: %s" % service) try: # If the host part of the URI is an IP address, we will not do any lookup if re.match("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", uri.host): return [(uri.host, uri.port or service_port)] resolver = DNSResolver() resolver.cache = self.cache resolver.timeout = timeout resolver.lifetime = lifetime record_name = '%s.%s' % (service_prefix, uri.host) services = self._lookup_srv_records(resolver, [record_name], log_context=log_context) if services[record_name]: return [(result.address, result.port) for result in services[record_name]] elif service_fallback: addresses = self._lookup_a_records(resolver, [uri.host], log_context=log_context) if addresses[uri.host]: return [(addr, service_port) for addr in addresses[uri.host]] except dns.resolver.Timeout: raise DNSLookupError('Timeout in lookup for %s servers for domain %s' % (service, uri.host)) else: raise DNSLookupError('No %s servers found for domain %s' % (service, uri.host)) @run_in_waitable_green_thread @post_dns_lookup_notifications def lookup_sip_proxy(self, uri, supported_transports, timeout=3.0, lifetime=15.0): """ Performs an RFC 3263 compliant lookup of transport/ip/port combinations for a particular SIP URI. As arguments it takes a SIPURI object and a list of supported transports, in order of preference of the application. It returns a list of Route objects that can be used in order of preference. The DNSLookupDidSucceed notification contains a result attribute which is a list of Route objects. The DNSLookupDidFail notification contains an error attribute describing the error encountered. """ naptr_service_transport_map = {"sips+d2t": "tls", "sip+d2t": "tcp", "sip+d2u": "udp"} transport_service_map = {"udp": "_sip._udp", "tcp": "_sip._tcp", "tls": "_sips._tcp"} log_context = dict(context='lookup_sip_proxy', uri=uri) if not supported_transports: raise DNSLookupError("No transports are supported") supported_transports = [transport.lower() for transport in supported_transports] unknown_transports = set(supported_transports).difference(transport_service_map) if unknown_transports: raise DNSLookupError("Unknown transports: %s" % ', '.join(unknown_transports)) try: # If the host part of the URI is an IP address, we will not do any lookup if re.match("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", uri.host): transport = 'tls' if uri.secure else uri.transport.lower() if transport not in supported_transports: raise DNSLookupError("Transport %s dictated by URI is not supported" % transport) port = uri.port or (5061 if transport=='tls' else 5060) return [Route(address=uri.host, port=port, transport=transport)] resolver = DNSResolver() resolver.cache = self.cache resolver.timeout = timeout resolver.lifetime = lifetime # If the port is specified in the URI, we will only do an A lookup if uri.port: transport = 'tls' if uri.secure else uri.transport.lower() if transport not in supported_transports: raise DNSLookupError("Transport %s dictated by URI is not supported" % transport) addresses = self._lookup_a_records(resolver, [uri.host], log_context=log_context) if addresses[uri.host]: return [Route(address=addr, port=uri.port, transport=transport) for addr in addresses[uri.host]] # If the transport was already set as a parameter on the SIP URI, only do SRV lookups elif 'transport' in uri.parameters: transport = uri.parameters['transport'].lower() if transport not in supported_transports: raise DNSLookupError("Requested lookup for URI with %s transport, but it is not supported" % transport) if uri.secure and transport != 'tls': raise DNSLookupError("Requested lookup for SIPS URI, but with %s transport parameter" % transport) record_name = '%s.%s' % (transport_service_map[transport], uri.host) services = self._lookup_srv_records(resolver, [record_name], log_context=log_context) if services[record_name]: return [Route(address=result.address, port=result.port, transport=transport) for result in services[record_name]] else: # If SRV lookup fails, try A lookup addresses = self._lookup_a_records(resolver, [uri.host], log_context=log_context) port = 5061 if transport=='tls' else 5060 if addresses[uri.host]: return [Route(address=addr, port=port, transport=transport) for addr in addresses[uri.host]] # Otherwise, it means we don't have a numeric IP address, a port isn't specified and neither is a transport. So we have to do a full NAPTR lookup else: # If the URI is a SIPS URI, we only support the TLS transport. if uri.secure: if 'tls' not in supported_transports: raise DNSLookupError("Requested lookup for SIPS URI, but TLS transport is not supported") supported_transports = ['tls'] # First try NAPTR lookup naptr_services = [service for service, transport in naptr_service_transport_map.iteritems() if transport in supported_transports] try: pointers = self._lookup_naptr_record(resolver, uri.host, naptr_services, log_context=log_context) except dns.resolver.Timeout: pointers = [] if pointers: return [Route(address=result.address, port=result.port, transport=naptr_service_transport_map[result.service]) for result in pointers] else: # If that fails, try SRV lookup routes = [] for transport in supported_transports: record_name = '%s.%s' % (transport_service_map[transport], uri.host) try: services = self._lookup_srv_records(resolver, [record_name], log_context=log_context) except dns.resolver.Timeout: continue if services[record_name]: routes.extend(Route(address=result.address, port=result.port, transport=transport) for result in services[record_name]) if routes: return routes else: # If SRV lookup fails, try A lookup transport = 'tls' if uri.secure else 'udp' if transport in supported_transports: addresses = self._lookup_a_records(resolver, [uri.host], log_context=log_context) port = 5061 if transport=='tls' else 5060 if addresses[uri.host]: return [Route(address=addr, port=port, transport=transport) for addr in addresses[uri.host]] except dns.resolver.Timeout: raise DNSLookupError("Timeout in lookup for routes for SIP URI %s" % uri) else: raise DNSLookupError("No routes found for SIP URI %s" % uri) @run_in_waitable_green_thread @post_dns_lookup_notifications def lookup_xcap_server(self, uri, timeout=3.0, lifetime=15.0): """ Performs a TXT query against xcap. and returns all results that look like HTTP URIs. """ log_context = dict(context='lookup_xcap_server', uri=uri) notification_center = NotificationCenter() try: # If the host part of the URI is an IP address, we cannot not do any lookup if re.match("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", uri.host): raise DNSLookupError("Cannot perform DNS query because the host is an IP address") resolver = DNSResolver() resolver.cache = self.cache resolver.timeout = timeout resolver.lifetime = lifetime record_name = 'xcap.%s' % uri.host results = [] try: answer = resolver.query(record_name, rdatatype.TXT) except dns.resolver.Timeout, e: notification_center.post_notification('DNSLookupTrace', sender=self, data=NotificationData(query_type='TXT', query_name=str(record_name), nameservers=resolver.nameservers, answer=None, error=e, **log_context)) raise except exception.DNSException, e: notification_center.post_notification('DNSLookupTrace', sender=self, data=NotificationData(query_type='TXT', query_name=str(record_name), nameservers=resolver.nameservers, answer=None, error=e, **log_context)) else: notification_center.post_notification('DNSLookupTrace', sender=self, data=NotificationData(query_type='TXT', query_name=str(record_name), nameservers=resolver.nameservers, answer=answer, error=None, **log_context)) for result_uri in list(chain(*(r.strings for r in answer.rrset))): parsed_uri = urlparse(result_uri) if parsed_uri.scheme in ('http', 'https') and parsed_uri.netloc: results.append(result_uri) if not results: raise DNSLookupError('No XCAP servers found for domain %s' % uri.host) return results except dns.resolver.Timeout: raise DNSLookupError('Timeout in lookup for XCAP servers for domain %s' % uri.host) def _lookup_a_records(self, resolver, hostnames, additional_records=[], log_context={}): notification_center = NotificationCenter() additional_addresses = dict((rset.name.to_text(), rset) for rset in additional_records if rset.rdtype == rdatatype.A) addresses = {} for hostname in hostnames: if hostname in additional_addresses: addresses[hostname] = [r.address for r in additional_addresses[hostname]] else: try: answer = resolver.query(hostname, rdatatype.A) except dns.resolver.Timeout, e: notification_center.post_notification('DNSLookupTrace', sender=self, data=NotificationData(query_type='A', query_name=str(hostname), nameservers=resolver.nameservers, answer=None, error=e, **log_context)) raise except exception.DNSException, e: notification_center.post_notification('DNSLookupTrace', sender=self, data=NotificationData(query_type='A', query_name=str(hostname), nameservers=resolver.nameservers, answer=None, error=e, **log_context)) addresses[hostname] = [] else: notification_center.post_notification('DNSLookupTrace', sender=self, data=NotificationData(query_type='A', query_name=str(hostname), nameservers=resolver.nameservers, answer=answer, error=None, **log_context)) addresses[hostname] = [r.address for r in answer.rrset] return addresses def _lookup_srv_records(self, resolver, srv_names, additional_records=[], log_context={}): notification_center = NotificationCenter() additional_services = dict((rset.name.to_text(), rset) for rset in additional_records if rset.rdtype == rdatatype.SRV) services = {} for srv_name in srv_names: services[srv_name] = [] if srv_name in additional_services: addresses = self._lookup_a_records(resolver, [r.target.to_text() for r in additional_services[srv_name]], additional_records) for record in additional_services[srv_name]: services[srv_name].extend(SRVResult(record.priority, record.weight, record.port, addr) for addr in addresses.get(record.target.to_text(), ())) else: try: answer = resolver.query(srv_name, rdatatype.SRV) except dns.resolver.Timeout, e: notification_center.post_notification('DNSLookupTrace', sender=self, data=NotificationData(query_type='SRV', query_name=str(srv_name), nameservers=resolver.nameservers, answer=None, error=e, **log_context)) raise except exception.DNSException, e: notification_center.post_notification('DNSLookupTrace', sender=self, data=NotificationData(query_type='SRV', query_name=str(srv_name), nameservers=resolver.nameservers, answer=None, error=e, **log_context)) else: notification_center.post_notification('DNSLookupTrace', sender=self, data=NotificationData(query_type='SRV', query_name=str(srv_name), nameservers=resolver.nameservers, answer=answer, error=None, **log_context)) addresses = self._lookup_a_records(resolver, [r.target.to_text() for r in answer.rrset], answer.response.additional, log_context) for record in answer.rrset: services[srv_name].extend(SRVResult(record.priority, record.weight, record.port, addr) for addr in addresses.get(record.target.to_text(), ())) services[srv_name].sort(key=lambda result: (result.priority, -result.weight)) return services def _lookup_naptr_record(self, resolver, domain, services, log_context={}): notification_center = NotificationCenter() pointers = [] try: answer = resolver.query(domain, rdatatype.NAPTR) except dns.resolver.Timeout, e: notification_center.post_notification('DNSLookupTrace', sender=self, data=NotificationData(query_type='NAPTR', query_name=str(domain), nameservers=resolver.nameservers, answer=None, error=e, **log_context)) raise except exception.DNSException, e: notification_center.post_notification('DNSLookupTrace', sender=self, data=NotificationData(query_type='NAPTR', query_name=str(domain), nameservers=resolver.nameservers, answer=None, error=e, **log_context)) else: notification_center.post_notification('DNSLookupTrace', sender=self, data=NotificationData(query_type='NAPTR', query_name=str(domain), nameservers=resolver.nameservers, answer=answer, error=None, **log_context)) records = [r for r in answer.rrset if r.service.lower() in services] services = self._lookup_srv_records(resolver, [r.replacement.to_text() for r in records], answer.response.additional, log_context) for record in records: pointers.extend(NAPTRResult(record.service.lower(), record.order, record.preference, r.priority, r.weight, r.port, r.address) for r in services.get(record.replacement.to_text(), ())) pointers.sort(key=lambda result: (result.order, result.preference)) return pointers class DNSManager(object): __metaclass__ = Singleton implements(IObserver) def __init__(self): default_resolver = InternalResolver() self.search = default_resolver.search self.domain = default_resolver.domain self.nameservers = default_resolver.nameservers self.google_nameservers = ['8.8.8.8', '8.8.4.4'] self.probed_domain = 'sip2sip.info.' self._channel = coros.queue() self._proc = None self._timer = None self._wakeup_timer = None notification_center = NotificationCenter() notification_center.add_observer(self, name='SystemIPAddressDidChange') notification_center.add_observer(self, name='SystemDidWakeUpFromSleep') @property def nameservers(self): return self.__dict__['nameservers'] @nameservers.setter def nameservers(self, value): old_value = self.__dict__.get('nameservers', Null) self.__dict__['nameservers'] = value if old_value is Null: NotificationCenter().post_notification('DNSResolverDidInitialize', sender=self, data=NotificationData(nameservers=value)) elif value != old_value: NotificationCenter().post_notification('DNSNameserversDidChange', sender=self, data=NotificationData(nameservers=value)) def start(self): self._proc = proc.spawn(self._run) self._channel.send(Command('probe_dns')) def stop(self): if self._proc is not None: self._proc.kill() self._proc = None if self._timer is not None and self._timer.active(): self._timer.cancel() self._timer = None if self._wakeup_timer is not None and self._wakeup_timer.active(): self._wakeup_timer.cancel() self._wakeup_timer = None def _run(self): while True: try: command = self._channel.wait() handler = getattr(self, '_CH_%s' % command.name) handler(command) except InterruptCommand: pass def _CH_probe_dns(self, command): if self._timer is not None and self._timer.active(): self._timer.cancel() self._timer = None resolver = InternalResolver() self.domain = resolver.domain self.search = resolver.search local_nameservers = resolver.nameservers # probe local resolver resolver.timeout = 1 resolver.lifetime = 3 try: answer = resolver.query(self.probed_domain, rdatatype.NAPTR) if not any(record.rdtype == rdatatype.NAPTR for record in answer.rrset): raise exception.DNSException("No NAPTR records found") answer = resolver.query("_sip._udp.%s" % self.probed_domain, rdatatype.SRV) if not any(record.rdtype == rdatatype.SRV for record in answer.rrset): raise exception.DNSException("No SRV records found") except (dns.resolver.Timeout, exception.DNSException): pass else: self.nameservers = resolver.nameservers return # local resolver failed. probe google resolver resolver.nameservers = self.google_nameservers resolver.timeout = 2 resolver.lifetime = 4 try: answer = resolver.query(self.probed_domain, rdatatype.NAPTR) if not any(record.rdtype == rdatatype.NAPTR for record in answer.rrset): raise exception.DNSException("No NAPTR records found") except (dns.resolver.Timeout, exception.DNSException): pass else: self.nameservers = resolver.nameservers return # google resolver failed. fallback to local resolver and schedule another probe for later self.nameservers = local_nameservers self._timer = reactor.callLater(15, self._channel.send, Command('probe_dns')) @run_in_twisted_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_SystemIPAddressDidChange(self, notification): self._proc.kill(InterruptCommand) self._channel.send(Command('probe_dns')) def _NH_SystemDidWakeUpFromSleep(self, notification): if self._wakeup_timer is None: def wakeup_action(): self._proc.kill(InterruptCommand) self._channel.send(Command('probe_dns')) self._wakeup_timer = None self._wakeup_timer = reactor.callLater(5, wakeup_action) # wait for system to stabilize ================================================ FILE: sipsimple/payloads/README.txt ================================================ SIP SIMPLE client Payloads -------------------------- These applications provide functionality that are required for implementing a feature-rich SIP SIMPLE client. These applications are used for parsing and generating bodies carried using PUBLISH, SUBSCRIBE and NOTIFY methods designed for asynchronous event notifications, to convey in near real-time, information between SIP end-points. An example of such information is presence, which in its basic form it provides user availability information based on end-user choice. In its advanced form, presence can provide rich state information including but not limited to user mood, geo-location, environment, noise level and the type of communication desired. The information can be disseminated based on a granular policy which allows end-users to decide who has access to which part of the published information. addressbook.py High-level implementation of an addressbook stored in XCAP documents. The contacts, groups and their attributes are stored in resource lists and OMA extensions described in OMA-TS-Presence_SIMPLE_XDM are used for describing policy and RLS services by references to resource lists. Multiple client instances can synchronize this addressbook using XCAP-diff event package. watcherinfo.py (RFC3857 and RFC3858) Parses NOTIFY body for presence.winfo event. Used for keeping track of watchers that subscribed to our presentity. Based on this information the the user can manage its presence policy. To retrieve this information the SIP client must subscribe to its own address for event presence.winfo. resourcelists.py (RFC4826) Parses and generates XML documents for constructing resource lists documents. Used for server side storage of presence related buddy lists using XCAP protocol. The SIP clients maintain the resource-lists on the XCAP server which provides persisten storage and aggregation point for multiple devices. rlsservices.py (RFC4826) Parses and generates XML documents for constructing rls-services documents. Used for delegating presence related works to the server. The client build rls-services lists with buddies and instructs the server to subscribe to the sip uris indicated in the lists. This way the client can save bandwidth as the server performs the signalling for subscription and collection of notifications and provides consolidated answer to the sip user agent. rlmi.py, rlsnotify.py (RFC4482) Document handling for NOTIFY body for Resource Lists Contact Information. policy.py (RFC4745) Generic data types to be used in policy applications. presrules.py (RFC5025) Parses and generates authorization rules in XML format for presence or other applications. Authorization rules are stored on the XCAP server. The presence rules are generated either based on user initiative or as a response to a new subscription signaled by a change in the watcherinfo application. omapolicy.py (OMA-TS-Presence_SIMPLE_XDM-V1_1) Conditions extension handling according to OMA-TS-Presence_SIMPLE_XDM-V1_1. This module provides an extension to common policy defined in RFC4745 to support condition extensions defined by OMA. prescontent.py (OMA-TS-Presence_SIMPLE_XDM-V1_1) Generates presence content application documents according to OMA TS Presence SIMPLE Content. directory.py (OMA Core Specifications) Parses xcap-directory messages according to OMA TS XDM Core. dialoginfo.py (RFC4235) Parses and produces dialog-info messages according to RFC4235. dialogrules.py Parses and produces Dialog Authorization Rules documents. As there is no RFC for this, common-policy format from RFC 4745 is used. Subscription Handling has been taken from RFC 5025. pidf.py (RFC3863 and RFC3379) This module provides classes to parse and generate PIDF documents, and also uses the XML Application extensibility API to allow extensions to PIDF. It is used to parse NOTIFY body for presence event and generates rich presence state information for use with PUBLISH. Used to generate own presence information and to parse the state of buddy lists entries we have subscribed to. A SIP client typically instantiates a new pidf object for itself and for each buddy it SUBSCRIBEs to and update each object when a NOTIFY is received. The list of buddys is maintained using resourcelists application. rpid.py (RFC4480) This module provides an extension to PIDF to support rich presence. presdm.py (RFC4479) This module provides an extension to the PIDF to support the data module defined in RFC4479. cipid.py (RFC4482) This module provides an extension to PIDF to provide person related information. caps.py (RFC5196) This module provides capabilities application: displays OPTIONS request-like information as an extension to the PIDF. xcapcaps.py (RFC4825) Support for parsing and building xcap-caps documents, as defined by RFC4825. xcapdiff.py (RFC5874) Parses NOTIFY body for xcap-diff event. Used to detect changes in XCAP documents changed by other device configured for the same presentity. iscomposing.py (RFC3994) This module parses and generates isComposing messages according to RFC3994. It's used mainly in chat environments to indicate the other party that the user is actually typing a message. conference.py (RFC4575) This module implements conference-info payload parsing and generating for describing information about conference participants and related resources. messagesummary.py (RFC3842) This module implements a parser and generator for message-summary payload, which is used to indicate missed calls or voice mail recordings. ================================================ FILE: sipsimple/payloads/__init__.py ================================================ __all__ = ['ParserError', 'BuilderError', 'ValidationError', 'IterateTypes', 'IterateIDs', 'IterateItems', 'All', 'parse_qname', 'XMLDocument', 'XMLAttribute', 'XMLElementID', 'XMLElementChild', 'XMLElementChoiceChild', 'XMLStringChoiceChild', 'XMLElement', 'XMLRootElement', 'XMLSimpleElement', 'XMLStringElement', 'XMLLocalizedStringElement', 'XMLBooleanElement', 'XMLByteElement', 'XMLUnsignedByteElement', 'XMLShortElement', 'XMLUnsignedShortElement', 'XMLIntElement', 'XMLUnsignedIntElement', 'XMLLongElement', 'XMLUnsignedLongElement', 'XMLIntegerElement', 'XMLPositiveIntegerElement', 'XMLNegativeIntegerElement', 'XMLNonNegativeIntegerElement', 'XMLNonPositiveIntegerElement', 'XMLDecimalElement', 'XMLDateTimeElement', 'XMLAnyURIElement', 'XMLEmptyElement', 'XMLEmptyElementRegistryType', 'XMLListElement', 'XMLListRootElement', 'XMLStringListElement'] import os import sys import urllib from collections import defaultdict, deque from copy import deepcopy from decimal import Decimal from itertools import chain, izip from weakref import WeakValueDictionary from application.python import Null from application.python.descriptor import classproperty from application.python.types import MarkerType from application.python.weakref import weakobjectmap from lxml import etree from sipsimple.payloads.datatypes import Boolean, Byte, UnsignedByte, Short, UnsignedShort, Int, UnsignedInt, Long, UnsignedLong from sipsimple.payloads.datatypes import PositiveInteger, NegativeInteger, NonNegativeInteger, NonPositiveInteger, DateTime, AnyURI from sipsimple.util import All ## Exceptions class ParserError(Exception): pass class BuilderError(Exception): pass class ValidationError(ParserError): pass ## Markers class IterateTypes: __metaclass__ = MarkerType class IterateIDs: __metaclass__ = MarkerType class IterateItems: __metaclass__ = MarkerType class StoredAttribute: __metaclass__ = MarkerType ## Utilities def parse_qname(qname): if qname[0] == '{': qname = qname[1:] return qname.split('}') else: return None, qname ## XMLDocument class XMLDocumentType(type): def __init__(cls, name, bases, dct): cls.nsmap = {} cls.schema_map = {} cls.element_map = {} cls.root_element = None cls.schema = None cls.parser = None for base in reversed(bases): if hasattr(base, 'element_map'): cls.element_map.update(base.element_map) if hasattr(base, 'schema_map'): cls.schema_map.update(base.schema_map) if hasattr(base, 'nsmap'): cls.nsmap.update(base.nsmap) cls._update_schema() def __setattr__(cls, name, value): if name == 'schema_path': if cls is not XMLDocument: raise AttributeError("%s can only be changed on XMLDocument" % name) super(XMLDocumentType, cls).__setattr__(name, value) def update_schema(document_class): document_class._update_schema() for document_subclass in document_class.__subclasses__(): update_schema(document_subclass) update_schema(XMLDocument) else: super(XMLDocumentType, cls).__setattr__(name, value) def _update_schema(cls): if cls.schema_map: location_map = {ns: urllib.quote(os.path.abspath(os.path.join(cls.schema_path, schema_file)).replace('\\', '//')) for ns, schema_file in cls.schema_map.iteritems()} schema = """ %s """ % '\r\n'.join('' % (namespace, schema_location) for namespace, schema_location in location_map.iteritems()) cls.schema = etree.XMLSchema(etree.XML(schema)) cls.parser = etree.XMLParser(schema=cls.schema, remove_blank_text=True) else: cls.schema = None cls.parser = etree.XMLParser(remove_blank_text=True) class XMLDocument(object): __metaclass__ = XMLDocumentType encoding = 'UTF-8' content_type = None schema_path = os.path.join(os.path.dirname(__file__), 'xml-schemas') @classmethod def parse(cls, document): try: if isinstance(document, str): xml = etree.XML(document, parser=cls.parser) elif isinstance(document, unicode): xml = etree.XML(document.encode('utf-8'), parser=cls.parser) else: xml = etree.parse(document, parser=cls.parser).getroot() if cls.schema is not None: cls.schema.assertValid(xml) return cls.root_element.from_element(xml, xml_document=cls) except (etree.DocumentInvalid, etree.XMLSyntaxError, ValueError), e: raise ParserError(str(e)) @classmethod def build(cls, root_element, encoding=None, pretty_print=False, validate=True): if type(root_element) is not cls.root_element: raise TypeError("can only build XML documents from root elements of type %s" % cls.root_element.__name__) element = root_element.to_element() if validate and cls.schema is not None: cls.schema.assertValid(element) # Cleanup namespaces and move element NS mappings to the global scope. normalized_element = etree.Element(element.tag, attrib=element.attrib, nsmap=dict(chain(element.nsmap.iteritems(), cls.nsmap.iteritems()))) normalized_element.text = element.text normalized_element.tail = element.tail normalized_element.extend(deepcopy(child) for child in element) etree.cleanup_namespaces(normalized_element) return etree.tostring(normalized_element, encoding=encoding or cls.encoding, method='xml', xml_declaration=True, pretty_print=pretty_print) @classmethod def create(cls, build_kw={}, **kw): return cls.build(cls.root_element(**kw), **build_kw) @classmethod def register_element(cls, xml_class): cls.element_map[xml_class.qname] = xml_class for child in cls.__subclasses__(): child.register_element(xml_class) @classmethod def get_element(cls, qname, default=None): return cls.element_map.get(qname, default) @classmethod def register_namespace(cls, namespace, prefix=None, schema=None): if prefix in cls.nsmap: raise ValueError("prefix %s is already registered in %s" % (prefix, cls.__name__)) if namespace in cls.nsmap.itervalues(): raise ValueError("namespace %s is already registered in %s" % (namespace, cls.__name__)) cls.nsmap[prefix] = namespace if schema is not None: cls.schema_map[namespace] = schema cls._update_schema() for child in cls.__subclasses__(): child.register_namespace(namespace, prefix, schema) @classmethod def unregister_namespace(cls, namespace): try: prefix = (prefix for prefix in cls.nsmap if cls.nsmap[prefix]==namespace).next() except StopIteration: raise KeyError("namespace %s is not registered in %s" % (namespace, cls.__name__)) del cls.nsmap[prefix] schema = cls.schema_map.pop(namespace, None) if schema is not None: cls._update_schema() for child in cls.__subclasses__(): try: child.unregister_namespace(namespace) except KeyError: pass ## Children descriptors class XMLAttribute(object): def __init__(self, name, xmlname=None, type=unicode, default=None, required=False, test_equal=True, onset=None, ondel=None): self.name = name self.xmlname = xmlname or name self.type = type self.default = default self.__xmlparse__ = getattr(type, '__xmlparse__', lambda value: value) self.__xmlbuild__ = getattr(type, '__xmlbuild__', unicode) self.required = required self.test_equal = test_equal self.onset = onset self.ondel = ondel self.values = weakobjectmap() def __get__(self, obj, objtype): if obj is None: return self try: return self.values[obj] except KeyError: value = self.values.setdefault(obj, self.default) if value is not None: obj.element.set(self.xmlname, self.build(value)) return value def __set__(self, obj, value): if value is not None and not isinstance(value, self.type): value = self.type(value) old_value = self.values.get(obj, self.default) if value == old_value: return if value is not None: obj.element.set(self.xmlname, self.build(value)) else: obj.element.attrib.pop(self.xmlname, None) self.values[obj] = value obj.__dirty__ = True if self.onset: self.onset(obj, self, value) def __delete__(self, obj): obj.element.attrib.pop(self.xmlname, None) try: value = self.values.pop(obj) except KeyError: pass else: if value != self.default: obj.__dirty__ = True if self.ondel: self.ondel(obj, self) def parse(self, xmlvalue): return self.__xmlparse__(xmlvalue) def build(self, value): return self.__xmlbuild__(value) class XMLElementID(XMLAttribute): """An XMLAttribute that represents the ID of an element (immutable).""" def __set__(self, obj, value): if obj in self.values: raise AttributeError("An XML element ID cannot be changed") super(XMLElementID, self).__set__(obj, value) def __delete__(self, obj): raise AttributeError("An XML element ID cannot be deleted") class XMLElementChild(object): def __init__(self, name, type, required=False, test_equal=True, onset=None, ondel=None): self.name = name self.type = type self.required = required self.test_equal = test_equal self.onset = onset self.ondel = ondel self.values = weakobjectmap() def __get__(self, obj, objtype): if obj is None: return self try: return self.values[obj] except KeyError: return None def __set__(self, obj, value): if value is not None and not isinstance(value, self.type): value = self.type(value) same_value = False old_value = self.values.get(obj) if value is old_value: return elif value is not None and value == old_value: value.__dirty__ = old_value.__dirty__ same_value = True if old_value is not None: obj.element.remove(old_value.element) if value is not None: obj._insert_element(value.element) self.values[obj] = value if not same_value: obj.__dirty__ = True if self.onset: self.onset(obj, self, value) def __delete__(self, obj): try: old_value = self.values.pop(obj) except KeyError: pass else: if old_value is not None: obj.element.remove(old_value.element) obj.__dirty__ = True if self.ondel: self.ondel(obj, self) class XMLElementChoiceChildWrapper(object): __slots__ = ('descriptor', 'type') def __init__(self, descriptor, type): self.descriptor = descriptor self.type = type def __getattribute__(self, name): if name in ('descriptor', 'type', 'register_extension', 'unregister_extension'): return super(XMLElementChoiceChildWrapper, self).__getattribute__(name) else: return self.descriptor.__getattribute__(name) def __setattr__(self, name, value): if name in ('descriptor', 'type'): super(XMLElementChoiceChildWrapper, self).__setattr__(name, value) else: setattr(self.descriptor, name, value) def __dir__(self): return dir(self.descriptor) + ['descriptor', 'type', 'register_extension', 'unregister_extension'] def register_extension(self, type): if self.extension_type is None: raise ValueError("The %s XML choice element of %s does not support extensions" % (self.name, self.type.__name__)) if not issubclass(type, XMLElement) or not issubclass(type, self.extension_type): raise TypeError("type is not a subclass of XMLElement and/or %s: %s" % (self.extension_type.__name__, type.__name__)) if type in self.types: raise ValueError("%s is already registered as a choice extension" % type.__name__) self.types.add(type) self.type._xml_children_qname_map[type.qname] = (self.descriptor, type) for child_class in self.type.__subclasses__(): child_class._xml_children_qname_map[type.qname] = (self.descriptor, type) def unregister_extension(self, type): if self.extension_type is None: raise ValueError("The %s XML choice element of %s does not support extensions" % (self.name, self.type.__name__)) try: self.types.remove(type) except ValueError: raise ValueError("%s is not a registered choice extension on %s" % (type.__name__, self.type.__name__)) del self.type._xml_children_qname_map[type.qname] for child_class in self.type.__subclasses__(): del child_class._xml_children_qname_map[type.qname] class XMLElementChoiceChild(object): def __init__(self, name, types, extension_type=None, required=False, test_equal=True, onset=None, ondel=None): self.name = name self.types = set(types) self.extension_type = extension_type self.required = required self.test_equal = test_equal self.onset = onset self.ondel = ondel self.values = weakobjectmap() def __get__(self, obj, objtype): if obj is None: return XMLElementChoiceChildWrapper(self, objtype) try: return self.values[obj] except KeyError: return None def __set__(self, obj, value): if value is not None and type(value) not in self.types: raise TypeError("%s is not an acceptable type for %s" % (value.__class__.__name__, obj.__class__.__name__)) same_value = False old_value = self.values.get(obj) if value is old_value: return elif value is not None and value == old_value: value.__dirty__ = old_value.__dirty__ same_value = True if old_value is not None: obj.element.remove(old_value.element) if value is not None: obj._insert_element(value.element) self.values[obj] = value if not same_value: obj.__dirty__ = True if self.onset: self.onset(obj, self, value) def __delete__(self, obj): try: old_value = self.values.pop(obj) except KeyError: pass else: if old_value is not None: obj.element.remove(old_value.element) obj.__dirty__ = True if self.ondel: self.ondel(obj, self) class XMLStringChoiceChild(XMLElementChoiceChild): """ A choice between keyword strings from a registry, custom strings from the other type and custom extensions. This descriptor will accept and return strings instead of requiring XMLElement instances for the values in the registry and the other type. Check XMLEmptyElementRegistryType for a metaclass for building registries of XMLEmptyElement classes for keywords. """ def __init__(self, name, registry=None, other_type=None, extension_type=None): self.registry = registry self.other_type = other_type self.extension_type = extension_type types = registry.classes if registry is not None else () types += (other_type,) if other_type is not None else () super(XMLStringChoiceChild, self).__init__(name, types, extension_type=extension_type, required=True, test_equal=True) def __get__(self, obj, objtype): value = super(XMLStringChoiceChild, self).__get__(obj, objtype) if obj is None or objtype is StoredAttribute or value is None or isinstance(value, self.extension_type or ()): return value else: return unicode(value) def __set__(self, obj, value): if isinstance(value, basestring): if self.registry is not None and value in self.registry.names: value = self.registry.class_map[value]() elif self.other_type is not None: value = self.other_type.from_string(value) super(XMLStringChoiceChild, self).__set__(obj, value) ## XMLElement base classes class XMLElementBase(object): """ This class is used as a common ancestor for XML elements and provides the means for super() to find at least dummy implementations for the methods that are supposed to be implemented by subclasses, even when they are not implemented by any other ancestor class. This is necessary in order to simplify access to these methods when multiple inheritance is involved and none or only some of the classes implement them. The methods declared here should to be implemented in subclasses as necessary. """ def __get_dirty__(self): return False def __set_dirty__(self, dirty): return def _build_element(self): return def _parse_element(self, element): return class XMLElementType(type): def __init__(cls, name, bases, dct): super(XMLElementType, cls).__init__(name, bases, dct) # set dictionary of xml attributes and xml child elements cls._xml_attributes = {} cls._xml_element_children = {} cls._xml_children_qname_map = {} for base in reversed(bases): if hasattr(base, '_xml_attributes'): cls._xml_attributes.update(base._xml_attributes) if hasattr(base, '_xml_element_children') and hasattr(base, '_xml_children_qname_map'): cls._xml_element_children.update(base._xml_element_children) cls._xml_children_qname_map.update(base._xml_children_qname_map) for name, value in dct.iteritems(): if isinstance(value, XMLElementID): if cls._xml_id is not None: raise AttributeError("Only one XMLElementID attribute can be defined in the %s class" % cls.__name__) cls._xml_id = value cls._xml_attributes[value.name] = value elif isinstance(value, XMLAttribute): cls._xml_attributes[value.name] = value elif isinstance(value, XMLElementChild): cls._xml_element_children[value.name] = value cls._xml_children_qname_map[value.type.qname] = (value, value.type) elif isinstance(value, XMLElementChoiceChild): cls._xml_element_children[value.name] = value for type in value.types: cls._xml_children_qname_map[type.qname] = (value, type) # register class in its XMLDocument if cls._xml_document is not None: cls._xml_document.register_element(cls) class XMLElement(XMLElementBase): __metaclass__ = XMLElementType _xml_tag = None # To be defined in subclass _xml_namespace = None # To be defined in subclass _xml_document = None # To be defined in subclass _xml_extension_type = None # Can be defined in subclass _xml_id = None # Can be defined in subclass, or will be set by the metaclass to the XMLElementID attribute (if present) _xml_children_order = {} # Can be defined in subclass # dynamically generated _xml_attributes = {} _xml_element_children = {} _xml_children_qname_map = {} qname = classproperty(lambda cls: '{%s}%s' % (cls._xml_namespace, cls._xml_tag)) def __init__(self): self.element = etree.Element(self.qname, nsmap=self._xml_document.nsmap) self.__dirty__ = True def __get_dirty__(self): return (self.__dict__['__dirty__'] or any(child.__dirty__ for child in (getattr(self, name) for name in self._xml_element_children) if child is not None) or super(XMLElement, self).__get_dirty__()) def __set_dirty__(self, dirty): super(XMLElement, self).__set_dirty__(dirty) if not dirty: for child in (child for child in (getattr(self, name) for name in self._xml_element_children) if child is not None): child.__dirty__ = dirty self.__dict__['__dirty__'] = dirty __dirty__ = property(__get_dirty__, __set_dirty__) def check_validity(self): # check attributes for name, attribute in self._xml_attributes.iteritems(): # if attribute has default but it was not set, will also be added with this occasion value = getattr(self, name, None) if attribute.required and value is None: raise ValidationError("required attribute %s of %s is not set" % (name, self.__class__.__name__)) # check element children for name, element_child in self._xml_element_children.iteritems(): # if child has default but it was not set, will also be added with this occasion child = getattr(self, name, None) if child is None and element_child.required: raise ValidationError("element child %s of %s is not set" % (name, self.__class__.__name__)) def to_element(self): try: self.check_validity() except ValidationError, e: raise BuilderError(str(e)) # build element children for name in self._xml_element_children: descriptor = getattr(self.__class__, name) child = descriptor.__get__(self, StoredAttribute) if child is not None: child.to_element() self._build_element() return self.element @classmethod def from_element(cls, element, xml_document=None): obj = cls.__new__(cls) obj._xml_document = xml_document if xml_document is not None else cls._xml_document obj.element = element # set known attributes for name, attribute in cls._xml_attributes.iteritems(): xmlvalue = element.get(attribute.xmlname, None) if xmlvalue is not None: try: setattr(obj, name, attribute.parse(xmlvalue)) except (ValueError, TypeError): raise ValidationError("got illegal value for attribute %s of %s: %s" % (name, cls.__name__, xmlvalue)) # set element children for child in element: element_child, type = cls._xml_children_qname_map.get(child.tag, (None, None)) if element_child is not None: try: value = type.from_element(child, xml_document=obj._xml_document) except ValidationError: pass # we should accept partially valid documents else: setattr(obj, element_child.name, value) obj._parse_element(element) obj.check_validity() obj.__dirty__ = False return obj @classmethod def _register_xml_attribute(cls, attribute, element): cls._xml_element_children[attribute] = element cls._xml_children_qname_map[element.type.qname] = (element, element.type) for subclass in cls.__subclasses__(): subclass._register_xml_attribute(attribute, element) @classmethod def _unregister_xml_attribute(cls, attribute): element = cls._xml_element_children.pop(attribute) del cls._xml_children_qname_map[element.type.qname] for subclass in cls.__subclasses__(): subclass._unregister_xml_attribute(attribute) @classmethod def register_extension(cls, attribute, type, test_equal=True): if cls._xml_extension_type is None: raise ValueError("XMLElement type %s does not support extensions (requested extension type %s)" % (cls.__name__, type.__name__)) elif not issubclass(type, cls._xml_extension_type): raise TypeError("XMLElement type %s only supports extensions of type %s (requested extension type %s)" % (cls.__name__, cls._xml_extension_type, type.__name__)) elif hasattr(cls, attribute): raise ValueError("XMLElement type %s already has an attribute named %s (requested extension type %s)" % (cls.__name__, attribute, type.__name__)) extension = XMLElementChild(attribute, type=type, required=False, test_equal=test_equal) setattr(cls, attribute, extension) cls._register_xml_attribute(attribute, extension) @classmethod def unregister_extension(cls, attribute): if cls._xml_extension_type is None: raise ValueError("XMLElement type %s does not support extensions" % cls.__name__) cls._unregister_xml_attribute(attribute) delattr(cls, attribute) def _insert_element(self, element): if element in self.element: return order = self._xml_children_order.get(element.tag, self._xml_children_order.get(None, sys.maxint)) for i in xrange(len(self.element)): child_order = self._xml_children_order.get(self.element[i].tag, self._xml_children_order.get(None, sys.maxint)) if child_order > order: position = i break else: position = len(self.element) self.element.insert(position, element) def __eq__(self, other): if isinstance(other, XMLElement): if self is other: return True for name, attribute in self._xml_attributes.iteritems(): if attribute.test_equal: if not hasattr(other, name) or getattr(self, name) != getattr(other, name): return False for name, element_child in self._xml_element_children.iteritems(): if element_child.test_equal: if not hasattr(other, name) or getattr(self, name) != getattr(other, name): return False try: __eq__ = super(XMLElement, self).__eq__ except AttributeError: return True else: return __eq__(other) elif self._xml_id is not None: return self._xml_id == other else: return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal def __hash__(self): if self._xml_id is not None: return hash(self._xml_id) else: return object.__hash__(self) class XMLRootElementType(XMLElementType): def __init__(cls, name, bases, dct): super(XMLRootElementType, cls).__init__(name, bases, dct) if cls._xml_document is not None: if cls._xml_document.root_element is not None: raise TypeError('there is already a root element registered for %s' % cls.__name__) cls._xml_document.root_element = cls class XMLRootElement(XMLElement): __metaclass__ = XMLRootElementType def __init__(self): XMLElement.__init__(self) self.__cache__ = WeakValueDictionary({self.element: self}) @classmethod def from_element(cls, element, xml_document=None): obj = super(XMLRootElement, cls).from_element(element, xml_document) obj.__cache__ = WeakValueDictionary({obj.element: obj}) return obj @classmethod def parse(cls, document): return cls._xml_document.parse(document) def toxml(self, encoding=None, pretty_print=False, validate=True): return self._xml_document.build(self, encoding=encoding, pretty_print=pretty_print, validate=validate) def xpath(self, xpath, namespaces=None): result = [] try: nodes = self.element.xpath(xpath, namespaces=namespaces) except etree.XPathError: raise ValueError("illegal XPath expression") for element in (node for node in nodes if isinstance(node, etree._Element)): if element in self.__cache__: result.append(self.__cache__[element]) continue if element is self.element: self.__cache__[element] = self result.append(self) continue for ancestor in element.iterancestors(): if ancestor in self.__cache__: container = self.__cache__[ancestor] break else: container = self notvisited = deque([container]) visited = set() while notvisited: container = notvisited.popleft() self.__cache__[container.element] = container if isinstance(container, XMLListMixin): children = set(child for child in container if isinstance(child, XMLElement) and child not in visited) visited.update(children) notvisited.extend(children) for child in container._xml_element_children: value = getattr(container, child) if value is not None and value not in visited: visited.add(value) notvisited.append(value) if element in self.__cache__: result.append(self.__cache__[element]) return result def get_xpath(self, element): raise NotImplementedError def find_parent(self, element): raise NotImplementedError ## Mixin classes class ThisClass(object): """ Special marker class that is used to indicate that an XMLListElement subclass can be an item of itself. This is necessary because a class cannot reference itself when defining _xml_item_type """ class XMLListMixinType(type): def __init__(cls, name, bases, dct): super(XMLListMixinType, cls).__init__(name, bases, dct) if '_xml_item_type' in dct: cls._xml_item_type = cls._xml_item_type # trigger __setattr__ def __setattr__(cls, name, value): if name == '_xml_item_type': if value is ThisClass: value = cls elif isinstance(value, tuple) and ThisClass in value: value = tuple(cls if type is ThisClass else type for type in value) if value is None: cls._xml_item_element_types = () cls._xml_item_extension_types = () else: item_types = value if isinstance(value, tuple) else (value,) cls._xml_item_element_types = tuple(type for type in item_types if issubclass(type, XMLElement)) cls._xml_item_extension_types = tuple(type for type in item_types if not issubclass(type, XMLElement)) super(XMLListMixinType, cls).__setattr__(name, value) class XMLListMixin(XMLElementBase): """A mixin representing a list of other XML elements""" __metaclass__ = XMLListMixinType _xml_item_type = None def __new__(cls, *args, **kw): if cls._xml_item_type is None: raise TypeError("The %s class cannot be instantiated because it doesn't define the _xml_item_type attribute" % cls.__name__) instance = super(XMLListMixin, cls).__new__(cls) instance._element_map = {} instance._xmlid_map = defaultdict(dict) return instance def __contains__(self, item): return item in self._element_map.itervalues() def __iter__(self): return (self._element_map[element] for element in self.element if element in self._element_map) def __len__(self): return len(self._element_map) def __repr__(self): return '%s(%r)' % (self.__class__.__name__, list(self)) def __eq__(self, other): if isinstance(other, XMLListMixin): return self is other or (len(self) == len(other) and all(self_item == other_item for self_item, other_item in izip(self, other))) else: return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal def __getitem__(self, key): if key is IterateTypes: return (cls for cls, mapping in self._xmlid_map.iteritems() if mapping) if not isinstance(key, tuple): raise KeyError(key) try: cls, id = key except ValueError: raise KeyError(key) if id is IterateIDs: return self._xmlid_map[cls].iterkeys() elif id is IterateItems: return self._xmlid_map[cls].itervalues() else: return self._xmlid_map[cls][id] def __delitem__(self, key): if not isinstance(key, tuple): raise KeyError(key) try: cls, id = key except ValueError: raise KeyError(key) if id is All: for item in self._xmlid_map[cls].values(): self.remove(item) else: self.remove(self._xmlid_map[cls][id]) def __get_dirty__(self): return any(item.__dirty__ for item in self._element_map.itervalues()) or super(XMLListMixin, self).__get_dirty__() def __set_dirty__(self, dirty): super(XMLListMixin, self).__set_dirty__(dirty) if not dirty: for item in self._element_map.itervalues(): item.__dirty__ = dirty def _parse_element(self, element): super(XMLListMixin, self)._parse_element(element) self._element_map.clear() self._xmlid_map.clear() for child in element[:]: child_class = self._xml_document.get_element(child.tag, type(None)) if child_class in self._xml_item_element_types or issubclass(child_class, self._xml_item_extension_types): try: value = child_class.from_element(child, xml_document=self._xml_document) except ValidationError: pass else: if value._xml_id is not None and value._xml_id in self._xmlid_map[child_class]: element.remove(child) else: if value._xml_id is not None: self._xmlid_map[child_class][value._xml_id] = value self._element_map[value.element] = value def _build_element(self): super(XMLListMixin, self)._build_element() for child in self._element_map.itervalues(): child.to_element() def add(self, item): if not (item.__class__ in self._xml_item_element_types or isinstance(item, self._xml_item_extension_types)): raise TypeError("%s cannot add items of type %s" % (self.__class__.__name__, item.__class__.__name__)) same_value = False if item._xml_id is not None and item._xml_id in self._xmlid_map[item.__class__]: old_item = self._xmlid_map[item.__class__][item._xml_id] if item is old_item: return elif item == old_item: item.__dirty__ = old_item.__dirty__ same_value = True self.element.remove(old_item.element) del self._xmlid_map[item.__class__][item._xml_id] del self._element_map[old_item.element] self._insert_element(item.element) if item._xml_id is not None: self._xmlid_map[item.__class__][item._xml_id] = item self._element_map[item.element] = item if not same_value: self.__dirty__ = True def remove(self, item): self.element.remove(item.element) if item._xml_id is not None: del self._xmlid_map[item.__class__][item._xml_id] del self._element_map[item.element] self.__dirty__ = True def update(self, sequence): for item in sequence: self.add(item) def clear(self): for item in self._element_map.values(): self.remove(item) ## Element types class XMLSimpleElement(XMLElement): _xml_value_type = None # To be defined in subclass def __new__(cls, *args, **kw): if cls._xml_value_type is None: raise TypeError("The %s class cannot be instantiated because it doesn't define the _xml_value_type attribute" % cls.__name__) return super(XMLSimpleElement, cls).__new__(cls) def __init__(self, value): XMLElement.__init__(self) self.value = value def __eq__(self, other): if isinstance(other, XMLSimpleElement): return self is other or self.value == other.value else: return self.value == other def __nonzero__(self): return bool(self.value) def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.value) def __str__(self): return str(self.value) def __unicode__(self): return unicode(self.value) @property def value(self): return self.__dict__['value'] @value.setter def value(self, value): if not isinstance(value, self._xml_value_type): value = self._xml_value_type(value) if self.__dict__.get('value', Null) == value: return self.__dict__['value'] = value self.__dirty__ = True def _parse_element(self, element): super(XMLSimpleElement, self)._parse_element(element) value = element.text or u'' if hasattr(self._xml_value_type, '__xmlparse__'): self.value = self._xml_value_type.__xmlparse__(value) else: self.value = self._xml_value_type(value) def _build_element(self): super(XMLSimpleElement, self)._build_element() if hasattr(self.value, '__xmlbuild__'): self.element.text = self.value.__xmlbuild__() else: self.element.text = unicode(self.value) class XMLStringElement(XMLSimpleElement): _xml_value_type = unicode # Can be overwritten in subclasses def __len__(self): return len(self.value) class XMLLocalizedStringElement(XMLStringElement): lang = XMLAttribute('lang', xmlname='{http://www.w3.org/XML/1998/namespace}lang', type=str, required=False, test_equal=True) def __init__(self, value, lang=None): XMLStringElement.__init__(self, value) self.lang = lang def __eq__(self, other): if isinstance(other, XMLLocalizedStringElement): return self is other or (self.lang == other.lang and self.value == other.value) elif self.lang is None: return XMLStringElement.__eq__(self, other) else: return NotImplemented def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.value, self.lang) def _parse_element(self, element): super(XMLLocalizedStringElement, self)._parse_element(element) self.lang = element.get('{http://www.w3.org/XML/1998/namespace}lang', None) class XMLBooleanElement(XMLSimpleElement): _xml_value_type = Boolean class XMLByteElement(XMLSimpleElement): _xml_value_type = Byte class XMLUnsignedByteElement(XMLSimpleElement): _xml_value_type = UnsignedByte class XMLShortElement(XMLSimpleElement): _xml_value_type = Short class XMLUnsignedShortElement(XMLSimpleElement): _xml_value_type = UnsignedShort class XMLIntElement(XMLSimpleElement): _xml_value_type = Int class XMLUnsignedIntElement(XMLSimpleElement): _xml_value_type = UnsignedInt class XMLLongElement(XMLSimpleElement): _xml_value_type = Long class XMLUnsignedLongElement(XMLSimpleElement): _xml_value_type = UnsignedLong class XMLIntegerElement(XMLSimpleElement): _xml_value_type = int class XMLPositiveIntegerElement(XMLSimpleElement): _xml_value_type = PositiveInteger class XMLNegativeIntegerElement(XMLSimpleElement): _xml_value_type = NegativeInteger class XMLNonNegativeIntegerElement(XMLSimpleElement): _xml_value_type = NonNegativeInteger class XMLNonPositiveIntegerElement(XMLSimpleElement): _xml_value_type = NonPositiveInteger class XMLDecimalElement(XMLSimpleElement): _xml_value_type = Decimal class XMLDateTimeElement(XMLSimpleElement): _xml_value_type = DateTime class XMLAnyURIElement(XMLStringElement): _xml_value_type = AnyURI class XMLEmptyElement(XMLElement): def __repr__(self): return '%s()' % self.__class__.__name__ def __eq__(self, other): return type(self) is type(other) or NotImplemented def __hash__(self): return hash(self.__class__) class XMLEmptyElementRegistryType(type): """A metaclass for building registries of XMLEmptyElement subclasses from names""" def __init__(cls, name, bases, dct): super(XMLEmptyElementRegistryType, cls).__init__(name, bases, dct) typename = getattr(cls, '__typename__', name.partition('Registry')[0]).capitalize() class BaseElementType(XMLEmptyElement): def __str__(self): return self._xml_tag def __unicode__(self): return unicode(self._xml_tag) cls.__basetype__ = BaseElementType cls.__basetype__.__name__ = 'Base%sType' % typename cls.class_map = {} for name in cls.names: class ElementType(BaseElementType): _xml_tag = name _xml_namespace = cls._xml_namespace _xml_document = cls._xml_document _xml_id = name ElementType.__name__ = typename + name.title().translate(None, '-_') cls.class_map[name] = ElementType cls.classes = tuple(cls.class_map[name] for name in cls.names) ## Created using mixins class XMLListElementType(XMLElementType, XMLListMixinType): pass class XMLListRootElementType(XMLRootElementType, XMLListMixinType): pass class XMLListElement(XMLElement, XMLListMixin): __metaclass__ = XMLListElementType def __nonzero__(self): if self._xml_attributes or self._xml_element_children: return True else: return len(self._element_map) != 0 class XMLListRootElement(XMLRootElement, XMLListMixin): __metaclass__ = XMLListRootElementType def __nonzero__(self): if self._xml_attributes or self._xml_element_children: return True else: return len(self._element_map) != 0 class XMLStringListElementType(XMLListElementType): def __init__(cls, name, bases, dct): if cls._xml_item_type is not None: raise TypeError("The %s class should not define _xml_item_type, but define _xml_item_registry, _xml_item_other_type and _xml_item_extension_type instead" % cls.__name__) types = cls._xml_item_registry.classes if cls._xml_item_registry is not None else () types += tuple(type for type in (cls._xml_item_other_type, cls._xml_item_extension_type) if type is not None) cls._xml_item_type = types or None super(XMLStringListElementType, cls).__init__(name, bases, dct) class XMLStringListElement(XMLListElement): __metaclass__ = XMLStringListElementType _xml_item_registry = None _xml_item_other_type = None _xml_item_extension_type = None def __contains__(self, item): if isinstance(item, basestring): if self._xml_item_registry is not None and item in self._xml_item_registry.names: item = self._xml_item_registry.class_map[item]() elif self._xml_item_other_type is not None: item = self._xml_item_other_type.from_string(item) return item in self._element_map.itervalues() def __iter__(self): return (item if isinstance(item, self._xml_item_extension_types) else unicode(item) for item in super(XMLStringListElement, self).__iter__()) def add(self, item): if isinstance(item, basestring): if self._xml_item_registry is not None and item in self._xml_item_registry.names: item = self._xml_item_registry.class_map[item]() elif self._xml_item_other_type is not None: item = self._xml_item_other_type.from_string(item) super(XMLStringListElement, self).add(item) def remove(self, item): if isinstance(item, basestring): if self._xml_item_registry is not None and item in self._xml_item_registry.names: xmlitem = self._xml_item_registry.class_map[item]() try: item = (entry for entry in super(XMLStringListElement, self).__iter__() if entry == xmlitem).next() except StopIteration: raise KeyError(item) elif self._xml_item_other_type is not None: xmlitem = self._xml_item_other_type.from_string(item) try: item = (entry for entry in super(XMLStringListElement, self).__iter__() if entry == xmlitem).next() except StopIteration: raise KeyError(item) super(XMLStringListElement, self).remove(item) ================================================ FILE: sipsimple/payloads/addressbook.py ================================================ """Addressbook related payload elements""" __all__ = ['namespace', 'Group', 'Contact', 'ContactURI', 'Policy', 'ElementExtension', 'ElementAttributes'] from application.python import Null from lxml import etree from sipsimple.payloads import XMLElement, XMLListElement, XMLStringElement, XMLBooleanElement, XMLElementID, XMLAttribute, XMLElementChild from sipsimple.payloads import IterateIDs, IterateItems, All from sipsimple.payloads.datatypes import AnyURI, ID from sipsimple.payloads.resourcelists import ResourceListsDocument, ListElement namespace = 'urn:ag-projects:xml:ns:addressbook' ResourceListsDocument.register_namespace(namespace, prefix='addressbook', schema='addressbook.xsd') class ElementExtension(object): pass class Name(XMLStringElement): _xml_tag = 'name' _xml_namespace = namespace _xml_document = ResourceListsDocument class ContactID(XMLStringElement): _xml_tag = 'contact_id' _xml_namespace = namespace _xml_document = ResourceListsDocument _xml_value_type = ID class ContactList(XMLListElement): _xml_tag = 'contacts' _xml_namespace = namespace _xml_document = ResourceListsDocument _xml_item_type = ContactID def __init__(self, contacts=[]): XMLListElement.__init__(self) self.update(contacts) def __contains__(self, item): if isinstance(item, basestring): item = ContactID(item) return super(ContactList, self).__contains__(item) def __iter__(self): return (item.value for item in super(ContactList, self).__iter__()) def add(self, item): if isinstance(item, basestring): item = ContactID(item) super(ContactList, self).add(item) def remove(self, item): if isinstance(item, basestring): try: item = (entry for entry in super(ContactList, self).__iter__() if entry.value == item).next() except StopIteration: raise KeyError(item) super(ContactList, self).remove(item) class Group(XMLElement, ListElement): _xml_tag = 'group' _xml_namespace = namespace _xml_extension_type = ElementExtension _xml_document = ResourceListsDocument id = XMLElementID('id', type=ID, required=True, test_equal=True) name = XMLElementChild('name', type=Name, required=True, test_equal=True) contacts = XMLElementChild('contacts', type=ContactList, required=True, test_equal=True) def __init__(self, id, name, contacts=[]): XMLElement.__init__(self) self.id = id self.name = name self.contacts = ContactList(contacts) def __unicode__(self): return unicode(self.name) def __repr__(self): return '%s(%r, %r, contacts=%r)' % (self.__class__.__name__, self.id, self.name, list(self.contacts)) class ContactURI(XMLElement): _xml_tag = 'uri' _xml_namespace = namespace _xml_extension_type = ElementExtension _xml_document = ResourceListsDocument id = XMLElementID('id', type=ID, required=True, test_equal=True) uri = XMLAttribute('uri', type=AnyURI, required=True, test_equal=True) type = XMLAttribute('type', type=unicode, required=False, test_equal=True) def __init__(self, id, uri, type=None): XMLElement.__init__(self) self.id = id self.uri = uri self.type = type def __unicode__(self): return unicode(self.uri) def __repr__(self): return '%s(%r, %r, %r)' % (self.__class__.__name__, self.id, self.uri, self.type) class ContactURIList(XMLListElement): _xml_tag = 'uris' _xml_namespace = namespace _xml_document = ResourceListsDocument _xml_item_type = ContactURI default = XMLAttribute('default', type=str, required=False, test_equal=True) def __init__(self, uris=[], default=None): XMLListElement.__init__(self) self.update(uris) self.default = default def __getitem__(self, key): if key is IterateIDs: return self._xmlid_map[ContactURI].iterkeys() elif key is IterateItems: return self._xmlid_map[ContactURI].itervalues() else: return self._xmlid_map[ContactURI][key] def __delitem__(self, key): if key is All: for item in self._xmlid_map[ContactURI].values(): self.remove(item) else: self.remove(self._xmlid_map[ContactURI][key]) def get(self, key, default=None): return self._xmlid_map[ContactURI].get(key, default) class PolicyValue(str): def __new__(cls, value): if value not in ('allow', 'block', 'default'): raise ValueError("Invalid policy value: %s" % value) return super(PolicyValue, cls).__new__(cls, value) class PolicyString(XMLStringElement): _xml_tag = 'policy' _xml_namespace = namespace _xml_document = ResourceListsDocument _xml_value_type = PolicyValue class Subscribe(XMLBooleanElement): _xml_tag = 'subscribe' _xml_namespace = namespace _xml_document = ResourceListsDocument class DialogHandling(XMLElement): _xml_tag = 'dialog' _xml_namespace = namespace _xml_document = ResourceListsDocument policy = XMLElementChild('policy', type=PolicyString, required=True, test_equal=True) subscribe = XMLElementChild('subscribe', type=Subscribe, required=True, test_equal=True) def __init__(self, policy, subscribe): XMLElement.__init__(self) self.policy = policy self.subscribe = subscribe def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.policy, self.subscribe) class PresenceHandling(XMLElement): _xml_tag = 'presence' _xml_namespace = namespace _xml_document = ResourceListsDocument policy = XMLElementChild('policy', type=PolicyString, required=True, test_equal=True) subscribe = XMLElementChild('subscribe', type=Subscribe, required=True, test_equal=True) def __init__(self, policy, subscribe): XMLElement.__init__(self) self.policy = policy self.subscribe = subscribe def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.policy, self.subscribe) class Contact(XMLElement, ListElement): _xml_tag = 'contact' _xml_namespace = namespace _xml_extension_type = ElementExtension _xml_document = ResourceListsDocument id = XMLElementID('id', type=ID, required=True, test_equal=True) name = XMLElementChild('name', type=Name, required=True, test_equal=True) uris = XMLElementChild('uris', type=ContactURIList, required=True, test_equal=True) dialog = XMLElementChild('dialog', type=DialogHandling, required=True, test_equal=True) presence = XMLElementChild('presence', type=PresenceHandling, required=True, test_equal=True) def __init__(self, id, name, uris=[], presence_handling=None, dialog_handling=None): XMLElement.__init__(self) self.id = id self.name = name self.uris = uris self.dialog = dialog_handling or DialogHandling('default', False) self.presence = presence_handling or PresenceHandling('default', False) def __repr__(self): return '%s(%r, %r, %r, %r, %r)' % (self.__class__.__name__, self.id, self.name, list(self.uris), self.presence, self.dialog) class Policy(XMLElement, ListElement): _xml_tag = 'policy-element' _xml_namespace = namespace _xml_extension_type = ElementExtension _xml_document = ResourceListsDocument id = XMLElementID('id', type=ID, required=True, test_equal=True) uri = XMLAttribute('uri', type=AnyURI, required=True, test_equal=True) name = XMLElementChild('name', type=Name, required=True, test_equal=True) dialog = XMLElementChild('dialog', type=DialogHandling, required=True, test_equal=True) presence = XMLElementChild('presence', type=PresenceHandling, required=True, test_equal=True) def __init__(self, id, uri, name='', presence_handling=None, dialog_handling=None): XMLElement.__init__(self) self.id = id self.uri = uri self.name = name self.dialog = dialog_handling or DialogHandling('default', False) self.presence = presence_handling or PresenceHandling('default', False) def __unicode__(self): return unicode(self.uri) def __repr__(self): return '%s(%r, %r, %r, %r, %r)' % (self.__class__.__name__, self.id, self.uri, self.name, self.presence, self.dialog) # # Extensions # class ElementAttributes(XMLElement, ElementExtension): _xml_tag = 'attributes' _xml_namespace = 'urn:ag-projects:sipsimple:xml:ns:addressbook' _xml_document = ResourceListsDocument def __init__(self, iterable=(), **attributes): XMLElement.__init__(self) self._attributes = dict() self.update(iterable, **attributes) def _parse_element(self, element): self._attributes = dict() attribute_tag = '{%s}attribute' % self._xml_namespace for child in (child for child in element if child.tag == attribute_tag): if 'nil' in child.attrib: self[child.attrib['name']] = None else: self[child.attrib['name']] = unicode(child.text or u'') def _build_element(self): self.element.clear() attribute_tag = '{%s}attribute' % self._xml_namespace for key, value in self.iteritems(): child = etree.SubElement(self.element, attribute_tag, nsmap=self._xml_document.nsmap) child.attrib['name'] = key if value is None: child.attrib['nil'] = 'true' else: child.text = value def __contains__(self, key): return key in self._attributes def __iter__(self): return iter(self._attributes) def __len__(self): return len(self._attributes) def __getitem__(self, key): return self._attributes[key] def __setitem__(self, key, value): if self._attributes.get(key, Null) == value: return self._attributes[key] = value self.__dirty__ = True def __delitem__(self, key): del self._attributes[key] self.__dirty__ = True def __repr__(self): return "%s(%r)" % (self.__class__.__name__, dict(self)) def clear(self): if self._attributes: self._attributes.clear() self.__dirty__ = True def get(self, key, default=None): return self._attributes.get(key, default) def has_key(self, key): return key in self._attributes def items(self): return self._attributes.items() def iteritems(self): return self._attributes.iteritems() def iterkeys(self): return self._attributes.iterkeys() def itervalues(self): return self._attributes.itervalues() def keys(self): return self._attributes.keys() def pop(self, key, *args): value = self._attributes.pop(key, *args) if not args or value is not args[0]: self.__dirty__ = True return value def popitem(self): value = self._attributes.popitem() self.__dirty__ = True return value def setdefault(self, key, default=None): value = self._attributes.setdefault(key, default) if value is default: self.__dirty__ = True return value def update(self, iterable=(), **attributes): self._attributes.update(iterable, **attributes) if iterable or attributes: self.__dirty__ = True ResourceListsDocument.register_namespace(ElementAttributes._xml_namespace, prefix='sipsimple') Group.register_extension('attributes', ElementAttributes) Contact.register_extension('attributes', ElementAttributes) ContactURI.register_extension('attributes', ElementAttributes) Policy.register_extension('attributes', ElementAttributes) ================================================ FILE: sipsimple/payloads/caps.py ================================================ # This module is partially broken. It breaks the core assumption of the # payloads infrastructure, that an element qname is unique inside a given # application. Fortunately, the elements with duplicate qnames are used # as child elements for other elements, which are not affected by the # problem as each element keeps it's own qname mapping for its children # qnames. The problem only affects different elements with the same qname # that are used in list elements, as currently the list element uses the # application's qname mapping to find the classes and that mapping is # broken when multiple elements with the same qname are defined. # In other words, this module works, but the application qname mapping # that is generated by it is broken. # # -Dan """ User Agent Capability Extension handling according to RFC5196 This module provides an extension to PIDF to describe a user-agent capabilities in the PIDF documents. """ __all__ = ['namespace', 'Audio', 'Application', 'Data', 'Control', 'Video', 'Video', 'Text', 'Message', 'Type', 'Automata', 'Class', 'Duplex', 'Description', 'EventPackages', 'Priority', 'Methods', 'Extensions', 'Scheme', 'Schemes', 'Actor', 'IsFocus', 'Languages', 'Language', 'ServiceCapabilities', 'Mobility', 'DeviceCapabilities', 'ServiceCapabilitiesExtension', 'EventPackagesExtension', 'PriorityExtension', 'MethodsExtension', 'ExtensionsExtension', 'DeviceCapabilitiesExtension', 'MobilityExtension', # Extensions 'FileTransfer', 'ScreenSharingServer', 'ScreenSharingClient'] from sipsimple.payloads import XMLStringElement, XMLLocalizedStringElement, XMLBooleanElement, XMLElement, XMLEmptyElement from sipsimple.payloads import XMLElementChild, XMLListElement, XMLStringListElement, XMLAttribute, XMLEmptyElementRegistryType from sipsimple.payloads.pidf import PIDFDocument, ServiceExtension, Service, DeviceExtension, Device namespace = "urn:ietf:params:xml:ns:pidf:caps" PIDFDocument.register_namespace(namespace, prefix='caps', schema='caps.xsd') # Marker mixins class EventPackagesExtension(object): pass class PriorityExtension(object): pass class MethodsExtension(object): pass class ExtensionsExtension(object): pass class MobilityExtension(object): pass class DeviceCapabilitiesExtension(object): pass class ServiceCapabilitiesExtension(object): pass class ContentTypeValue(str): def __new__(cls, value): if len(value.split('/')) != 2: raise ValueError("illegal value for Content-Type: %s" % value) return str.__new__(cls, value) class Audio(XMLBooleanElement): _xml_tag = 'audio' _xml_namespace = namespace _xml_document = PIDFDocument class Application(XMLBooleanElement): _xml_tag = 'application' _xml_namespace = namespace _xml_document = PIDFDocument class Data(XMLBooleanElement): _xml_tag = 'data' _xml_namespace = namespace _xml_document = PIDFDocument class Control(XMLBooleanElement): _xml_tag = 'control' _xml_namespace = namespace _xml_document = PIDFDocument class Video(XMLBooleanElement): _xml_tag = 'video' _xml_namespace = namespace _xml_document = PIDFDocument class Text(XMLBooleanElement): _xml_tag = 'text' _xml_namespace = namespace _xml_document = PIDFDocument class Message(XMLBooleanElement): _xml_tag = 'message' _xml_namespace = namespace _xml_document = PIDFDocument class Automata(XMLBooleanElement): _xml_tag = 'automata' _xml_namespace = namespace _xml_document = PIDFDocument class Type(XMLStringElement): _xml_tag = 'type' _xml_namespace = namespace _xml_document = PIDFDocument _xml_value_type = ContentTypeValue class ClassRegistry(object): __metaclass__ = XMLEmptyElementRegistryType _xml_namespace = namespace _xml_document = PIDFDocument names = ('business', 'personal') class ClassSupported(XMLStringListElement): _xml_tag = 'supported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_registry = ClassRegistry def __init__(self, supported=[]): XMLStringListElement.__init__(self) self.update(supported) class ClassNotSupported(XMLStringListElement): _xml_tag = 'notsupported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_registry = ClassRegistry def __init__(self, not_supported=[]): XMLStringListElement.__init__(self) self.update(not_supported) class Class(XMLElement): _xml_tag = 'class' _xml_namespace = namespace _xml_document = PIDFDocument supported = XMLElementChild('supported', type=ClassSupported, required=False, test_equal=True) not_supported = XMLElementChild('not_supported', type=ClassNotSupported, required=False, test_equal=True) def __init__(self, supported=None, not_supported=None): XMLElement.__init__(self) self.supported = supported self.not_supported = not_supported class DuplexRegistry(object): __metaclass__ = XMLEmptyElementRegistryType _xml_namespace = namespace _xml_document = PIDFDocument names = ('full', 'half', 'receive-only', 'send-only') class DuplexSupported(XMLStringListElement): _xml_tag = 'supported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_registry = DuplexRegistry def __init__(self, supported=[]): XMLStringListElement.__init__(self) self.update(supported) class DuplexNotSupported(XMLStringListElement): _xml_tag = 'notsupported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_registry = DuplexRegistry def __init__(self, not_supported=[]): XMLStringListElement.__init__(self) self.update(not_supported) class Duplex(XMLElement): _xml_tag = 'duplex' _xml_namespace = namespace _xml_document = PIDFDocument supported = XMLElementChild('supported', type=DuplexSupported, required=False, test_equal=True) not_supported = XMLElementChild('not_supported', type=DuplexNotSupported, required=False, test_equal=True) def __init__(self, supported=None, not_supported=None): XMLElement.__init__(self) self.supported = supported self.not_supported = not_supported class EventRegistry(object): __metaclass__ = XMLEmptyElementRegistryType _xml_namespace = namespace _xml_document = PIDFDocument names = ('conference', 'dialog', 'kpml', 'message-summary', 'poc-settings', 'presence', 'reg', 'refer', 'Siemens-RTP-Stats', 'spirits-INDPs', 'spirits-user-prof', 'winfo') class EventSupported(XMLStringListElement): _xml_tag = 'supported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_registry = EventRegistry def __init__(self, supported=[]): XMLStringListElement.__init__(self) self.update(supported) class EventNotSupported(XMLStringListElement): _xml_tag = 'notsupported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_registry = EventRegistry def __init__(self, not_supported=[]): XMLStringListElement.__init__(self) self.update(not_supported) class EventPackages(XMLElement): _xml_tag = 'event-packages' _xml_namespace = namespace _xml_document = PIDFDocument _xml_extension_type = EventPackagesExtension supported = XMLElementChild('supported', type=EventSupported, required=False, test_equal=True) not_supported = XMLElementChild('not_supported', type=EventNotSupported, required=False, test_equal=True) def __init__(self, supported=None, not_supported=None): XMLElement.__init__(self) self.supported = supported self.not_supported = not_supported class PriorityLowerthan(XMLEmptyElement): _xml_tag = 'lowerthan' _xml_namespace = namespace _xml_document = PIDFDocument maxvalue = XMLAttribute('maxvalue', type=int, required=True, test_equal=True) def __init__(self, maxvalue): XMLEmptyElement.__init__(self) self.maxvalue = maxvalue class PriorityHigherthan(XMLEmptyElement): _xml_tag = 'higherthan' _xml_namespace = namespace _xml_document = PIDFDocument minvalue = XMLAttribute('minvalue', type=int, required=True, test_equal=True) def __init__(self, minvalue): XMLEmptyElement.__init__(self) self.minvalue = minvalue class PriorityEquals(XMLEmptyElement): _xml_tag = 'equals' _xml_namespace = namespace _xml_document = PIDFDocument value = XMLAttribute('value', type=int, required=True, test_equal=True) def __init__(self, value): XMLEmptyElement.__init__(self) self.value = value class PriorityRange(XMLEmptyElement): _xml_tag = 'range' _xml_namespace = namespace _xml_document = PIDFDocument maxvalue = XMLAttribute('maxvalue', type=int, required=True, test_equal=True) minvalue = XMLAttribute('minvalue', type=int, required=True, test_equal=True) def __init__(self, maxvalue, minvalue): XMLEmptyElement.__init__(self) self.maxvalue = maxvalue self.minvalue = minvalue class PrioritySupported(XMLListElement): _xml_tag = 'supported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_type = (PriorityLowerthan, PriorityHigherthan, PriorityEquals, PriorityRange) def __init__(self, supported=[]): XMLListElement.__init__(self) self.update(supported) class PriorityNotSupported(XMLListElement): _xml_tag = 'notsupported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_type = (PriorityLowerthan, PriorityHigherthan, PriorityEquals, PriorityRange) def __init__(self, not_supported=[]): XMLListElement.__init__(self) self.update(not_supported) class Priority(XMLElement): _xml_tag = 'priority' _xml_namespace = namespace _xml_document = PIDFDocument _xml_extension_type = PriorityExtension supported = XMLElementChild('supported', type=PrioritySupported, required=False, test_equal=True) not_supported = XMLElementChild('not_supported', type=PriorityNotSupported, required=False, test_equal=True) def __init__(self, supported=None, not_supported=None): XMLElement.__init__(self) self.supported = supported self.not_supported = not_supported class MethodRegistry(object): __metaclass__ = XMLEmptyElementRegistryType _xml_namespace = namespace _xml_document = PIDFDocument names = ('ACK', 'BYE', 'CANCEL', 'INFO', 'INVITE', 'MESSAGE', 'NOTIFY', 'OPTIONS', 'PRACK', 'PUBLISH', 'REFER', 'REGISTER', 'SUBSCRIBE', 'UPDATE') class MethodSupported(XMLStringListElement): _xml_tag = 'supported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_registry = MethodRegistry def __init__(self, supported=[]): XMLStringListElement.__init__(self) self.update(supported) class MethodNotSupported(XMLStringListElement): _xml_tag = 'notsupported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_registry = MethodRegistry def __init__(self, not_supported=[]): XMLStringListElement.__init__(self) self.update(not_supported) class Methods(XMLElement): _xml_tag = 'methods' _xml_namespace = namespace _xml_document = PIDFDocument _xml_extension_type = MethodsExtension supported = XMLElementChild('supported', type=MethodSupported, required=False, test_equal=True) not_supported = XMLElementChild('not_supported', type=MethodNotSupported, required=False, test_equal=True) def __init__(self, supported=None, not_supported=None): XMLElement.__init__(self) self.supported = supported self.not_supported = not_supported class ExtensionRegistry(object): __metaclass__ = XMLEmptyElementRegistryType _xml_namespace = namespace _xml_document = PIDFDocument names = ('rel100', 'early-session', 'eventlist', 'from-change', 'gruu', 'histinfo', 'join', 'norefsub', 'path', 'precondition', 'pref', 'privacy', 'recipient-list-invite', 'recipient-list-subscribe', 'replaces', 'resource-priority', 'sdp-anat', 'sec-agree', 'tdialog', 'timer') class ExtensionSupported(XMLStringListElement): _xml_tag = 'supported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_registry = ExtensionRegistry def __init__(self, supported=[]): XMLStringListElement.__init__(self) self.update(supported) class ExtensionNotSupported(XMLStringListElement): _xml_tag = 'notsupported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_registry = ExtensionRegistry def __init__(self, not_supported=[]): XMLStringListElement.__init__(self) self.update(not_supported) class Extensions(XMLElement): _xml_tag = 'extensions' _xml_namespace = namespace _xml_document = PIDFDocument _xml_extension_type = ExtensionsExtension supported = XMLElementChild('supported', type=ExtensionSupported, required=False, test_equal=True) not_supported = XMLElementChild('not_supported', type=ExtensionNotSupported, required=False, test_equal=True) def __init__(self, supported=None, not_supported=None): XMLElement.__init__(self) self.supported = supported self.not_supported = not_supported class Scheme(XMLStringElement): _xml_tag = 's' _xml_namespace = namespace _xml_document = PIDFDocument class SchemeSupported(XMLListElement): _xml_tag = 'supported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_type = Scheme def __init__(self, supported=[]): XMLListElement.__init__(self) self.update(supported) class SchemeNotSupported(XMLListElement): _xml_tag = 'notsupported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_type = Scheme def __init__(self, not_supported=[]): XMLStringListElement.__init__(self) self.update(not_supported) class Schemes(XMLElement): _xml_tag = 'schemes' _xml_namespace = namespace _xml_document = PIDFDocument supported = XMLElementChild('supported', type=SchemeSupported, required=False, test_equal=True) not_supported = XMLElementChild('not_supported', type=SchemeNotSupported, required=False, test_equal=True) def __init__(self, supported=None, not_supported=None): XMLElement.__init__(self) self.supported = supported self.not_supported = not_supported class ActorRegistry(object): __metaclass__ = XMLEmptyElementRegistryType _xml_namespace = namespace _xml_document = PIDFDocument names = ('principal', 'attendant', 'msg-taker', 'information') class ActorSupported(XMLStringListElement): _xml_tag = 'supported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_registry = ActorRegistry def __init__(self, supported=[]): XMLStringListElement.__init__(self) self.update(supported) class ActorNotSupported(XMLStringListElement): _xml_tag = 'notsupported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_registry = ActorRegistry def __init__(self, not_supported=[]): XMLStringListElement.__init__(self) self.update(not_supported) class Actor(XMLElement): _xml_tag = 'actor' _xml_namespace = namespace _xml_document = PIDFDocument supported = XMLElementChild('supported', type=ActorSupported, required=False, test_equal=True) not_supported = XMLElementChild('not_supported', type=ActorNotSupported, required=False, test_equal=True) def __init__(self, supported=None, not_supported=None): XMLElement.__init__(self) self.supported = supported self.not_supported = not_supported class Language(XMLStringElement): _xml_tag = 'l' _xml_namespace = namespace _xml_document = PIDFDocument class LanguageSupported(XMLListElement): _xml_tag = 'supported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_type = Language def __init__(self, supported=[]): XMLListElement.__init__(self) self.update(supported) def __iter__(self): return (unicode(item) for item in super(LanguageSupported, self).__iter__()) def add(self, item): if isinstance(item, basestring): item = Language(item) super(LanguageSupported, self).add(item) def remove(self, item): if isinstance(item, basestring): try: item = (entry for entry in super(LanguageSupported, self).__iter__() if entry == item).next() except StopIteration: raise KeyError(item) super(LanguageSupported, self).remove(item) class LanguageNotSupported(XMLListElement): _xml_tag = 'notsupported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_type = Language def __init__(self, not_supported=[]): XMLListElement.__init__(self) self.update(not_supported) def __iter__(self): return (unicode(item) for item in super(LanguageNotSupported, self).__iter__()) def add(self, item): if isinstance(item, basestring): item = Language(item) super(LanguageNotSupported, self).add(item) def remove(self, item): if isinstance(item, basestring): try: item = (entry for entry in super(LanguageNotSupported, self).__iter__() if entry == item).next() except StopIteration: raise KeyError(item) super(LanguageNotSupported, self).remove(item) class Languages(XMLElement): _xml_tag = 'languages' _xml_namespace = namespace _xml_document = PIDFDocument supported = XMLElementChild('supported', type=LanguageSupported, required=False, test_equal=True) not_supported = XMLElementChild('not_supported', type=LanguageNotSupported, required=False, test_equal=True) def __init__(self, supported=None, not_supported=None): XMLElement.__init__(self) self.supported = supported self.not_supported = not_supported class Description(XMLLocalizedStringElement): _xml_tag = 'description' _xml_namespace = namespace _xml_document = PIDFDocument class IsFocus(XMLBooleanElement): _xml_tag = 'isfocus' _xml_namespace = namespace _xml_document = PIDFDocument class ServiceCapabilities(XMLListElement, ServiceExtension): _xml_tag = 'servcaps' _xml_namespace = namespace _xml_document = PIDFDocument _xml_extension_type = ServiceCapabilitiesExtension _xml_item_type = Description _xml_children_order = {Actor.qname: 0, Application.qname: 1, Audio.qname: 2, Automata.qname: 3, Class.qname: 4, Control.qname: 5, Data.qname: 6, Description.qname: 7, Duplex.qname: 8, EventPackages.qname: 9, Extensions.qname: 10, IsFocus.qname: 11, Message.qname: 12, Methods.qname: 13, Languages.qname: 14, Priority.qname: 15, Schemes.qname: 16, Text.qname: 17, Type.qname: 18, Video.qname: 19, None: 20} audio = XMLElementChild('audio', type=Audio, required=False, test_equal=True) application = XMLElementChild('application', type=Application, required=False, test_equal=True) data = XMLElementChild('data', type=Data, required=False, test_equal=True) control = XMLElementChild('control', type=Control, required=False, test_equal=True) video = XMLElementChild('video', type=Video, required=False, test_equal=True) text = XMLElementChild('text', type=Text, required=False, test_equal=True) message = XMLElementChild('message', type=Message, required=False, test_equal=True) mime_type = XMLElementChild('mime_type', type=Type, required=False, test_equal=True) automata = XMLElementChild('automata', type=Automata, required=False, test_equal=True) communication_class = XMLElementChild('communication_class', type=Class, required=False, test_equal=True) duplex = XMLElementChild('duplex', type=Duplex, required=False, test_equal=True) event_packages = XMLElementChild('event_packages', type=EventPackages, required=False, test_equal=True) priority = XMLElementChild('priority', type=Priority, required=False, test_equal=True) methods = XMLElementChild('methods', type=Methods, required=False, test_equal=True) extensions = XMLElementChild('extensions', type=Extensions, required=False, test_equal=True) schemes = XMLElementChild('schemes', type=Schemes, required=False, test_equal=True) actor = XMLElementChild('actor', type=Actor, required=False, test_equal=True) is_focus = XMLElementChild('is_focus', type=IsFocus, required=False, test_equal=True) languages = XMLElementChild('languages', type=Languages, required=False, test_equal=True) def __init__(self, audio=None, application=None, data=None, control=None, video=None, text=None, message=None, mime_type=None, automata=None, communication_class=None, duplex=None, event_packages=None, priority=None, methods=None, extensions=None, schemes=None, actor=None, is_focus=None, languages=None, descriptions=[]): XMLListElement.__init__(self) self.audio = audio self.application = application self.data = data self.control = control self.video = video self.text = text self.message = message self.mime_type = mime_type self.automata = automata self.communication_class = communication_class self.duplex = duplex self.event_packages = event_packages self.priority = priority self.methods = methods self.extensions = extensions self.schemes = schemes self.actor = actor self.is_focus = is_focus self.languages = languages self.update(descriptions) Service.register_extension('capabilities', type=ServiceCapabilities) class MobilityRegistry(object): __metaclass__ = XMLEmptyElementRegistryType _xml_namespace = namespace _xml_document = PIDFDocument names = ('fixed', 'mobile') class MobilitySupported(XMLStringListElement): _xml_tag = 'supported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_registry = MobilityRegistry def __init__(self, supported=[]): XMLStringListElement.__init__(self) self.update(supported) class MobilityNotSupported(XMLStringListElement): _xml_tag = 'notsupported' _xml_namespace = namespace _xml_document = PIDFDocument _xml_item_registry = MobilityRegistry def __init__(self, not_supported=[]): XMLStringListElement.__init__(self) self.update(not_supported) class Mobility(XMLElement): _xml_tag = 'mobility' _xml_namespace = namespace _xml_document = PIDFDocument _xml_extension_type = MobilityExtension supported = XMLElementChild('supported', type=MobilitySupported, required=False, test_equal=True) not_supported = XMLElementChild('not_supported', type=MobilityNotSupported, required=False, test_equal=True) def __init__(self, supported=None, not_supported=None): XMLElement.__init__(self) self.supported = supported() self.not_supported = not_supported class DeviceCapabilities(XMLListElement, DeviceExtension): _xml_tag = 'devcaps' _xml_namespace = namespace _xml_document = PIDFDocument _xml_extension_type = DeviceCapabilitiesExtension _xml_item_type = Description mobility = XMLElementChild('mobility', type=Mobility, required=False, test_equal=True) def __init__(self, mobility=None, descriptions=[]): XMLListElement.__init__(self) self.mobility = mobility self.update(descriptions) def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.mobility, list(self)) Device.register_extension('capabilities', type=DeviceCapabilities) # # Extensions # agp_caps_namespace = 'urn:ag-projects:xml:ns:pidf:caps' PIDFDocument.register_namespace(agp_caps_namespace, prefix='agp-caps') class FileTransfer(XMLBooleanElement, ServiceCapabilitiesExtension): _xml_tag = 'file-transfer' _xml_namespace = agp_caps_namespace _xml_document = PIDFDocument class ScreenSharingServer(XMLBooleanElement, ServiceCapabilitiesExtension): _xml_tag = 'screen-sharing-server' _xml_namespace = agp_caps_namespace _xml_document = PIDFDocument class ScreenSharingClient(XMLBooleanElement, ServiceCapabilitiesExtension): _xml_tag = 'screen-sharing-client' _xml_namespace = agp_caps_namespace _xml_document = PIDFDocument ServiceCapabilities.register_extension('file_transfer', type=FileTransfer) ServiceCapabilities.register_extension('screen_sharing_server', type=ScreenSharingServer) ServiceCapabilities.register_extension('screen_sharing_client', type=ScreenSharingClient) ================================================ FILE: sipsimple/payloads/cipid.py ================================================ """ CIPID handling according to RFC4482 This module provides an extension to PIDF to provide additional contact information about a presentity. """ from sipsimple.payloads import XMLAnyURIElement, XMLLocalizedStringElement from sipsimple.payloads.pidf import PIDFDocument, ServiceExtension, PersonExtension, Service, Person __all__ = ['namespace', 'Card', 'DisplayName', 'Homepage', 'Icon', 'Map', 'Sound'] namespace = "urn:ietf:params:xml:ns:pidf:cipid" PIDFDocument.register_namespace(namespace, prefix='c', schema='cipid.xsd') class Card(XMLAnyURIElement, PersonExtension, ServiceExtension): _xml_tag = 'card' _xml_namespace = namespace _xml_document = PIDFDocument Person.register_extension('card', type=Card) Service.register_extension('card', type=Card) class DisplayName(XMLLocalizedStringElement, PersonExtension, ServiceExtension): _xml_tag = 'display-name' _xml_namespace = namespace _xml_document = PIDFDocument Person.register_extension('display_name', type=DisplayName) Service.register_extension('display_name', type=DisplayName) class Homepage(XMLAnyURIElement, PersonExtension, ServiceExtension): _xml_tag = 'homepage' _xml_namespace = namespace _xml_document = PIDFDocument Person.register_extension('homepage', type=Homepage) Service.register_extension('homepage', type=Homepage) class Icon(XMLAnyURIElement, PersonExtension, ServiceExtension): _xml_tag = 'icon' _xml_namespace = namespace _xml_document = PIDFDocument Person.register_extension('icon', type=Icon) Service.register_extension('icon', type=Icon) class Map(XMLAnyURIElement, PersonExtension, ServiceExtension): _xml_tag = 'map' _xml_namespace = namespace _xml_document = PIDFDocument Person.register_extension('map', type=Map) Service.register_extension('map', type=Map) class Sound(XMLAnyURIElement, PersonExtension, ServiceExtension): _xml_tag = 'sound' _xml_namespace = namespace _xml_document = PIDFDocument Person.register_extension('sound', type=Sound) Service.register_extension('sound', type=Sound) ================================================ FILE: sipsimple/payloads/commonpolicy.py ================================================ """ Generic data types to be used in policy applications, according to RFC4745. """ __all__ = ['namespace', 'CommonPolicyDocument', 'ConditionElement', 'ActionElement', 'TransformationElement', 'RuleExtension', 'IdentityOne', 'IdentityExcept', 'IdentityMany', 'Identity', 'Validity', 'Conditions', 'Actions', 'Transformations', 'Rule', 'RuleSet', # Extensions 'FalseCondition', 'RuleDisplayName'] from sipsimple.payloads import ValidationError, XMLDocument, XMLElement, XMLListElement, XMLListRootElement, XMLAttribute, XMLElementID, XMLElementChild, XMLLocalizedStringElement, XMLDateTimeElement from sipsimple.payloads import IterateIDs, IterateItems, All from sipsimple.payloads.datatypes import AnyURI, ID namespace = 'urn:ietf:params:xml:ns:common-policy' class CommonPolicyDocument(XMLDocument): content_type = 'application/auth-policy+xml' CommonPolicyDocument.register_namespace(namespace, prefix='cp', schema='common-policy.xsd') ## Mixin types for extensibility class ConditionElement(object): pass class ActionElement(object): pass class TransformationElement(object): pass class RuleExtension(object): pass ## Elements class IdentityOne(XMLElement): _xml_tag = 'one' _xml_namespace = namespace _xml_document = CommonPolicyDocument id = XMLElementID('id', type=AnyURI, required=True, test_equal=True) def __init__(self, id): XMLElement.__init__(self) self.id = id def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.id) def __str__(self): return self.id def matches(self, uri): return self.id == uri class IdentityExcept(XMLElement): _xml_tag = 'except' _xml_namespace = namespace _xml_document = CommonPolicyDocument def _onset_id(self, attribute, value): if value is not None: self.domain = None id = XMLAttribute('id', type=str, required=False, test_equal=True, onset=_onset_id) del _onset_id def _onset_domain(self, attribute, value): if value is not None: self.id = None domain = XMLAttribute('domain', type=str, required=False, test_equal=True, onset=_onset_domain) del _onset_domain def __init__(self, id=None, domain=None): XMLElement.__init__(self) self.id = id self.domain = domain def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.id, self.domain) def __str__(self): if self.id is not None: return self.id else: return self.domain def matches(self, uri): if self.id is not None: return self.id != uri else: return [self.domain] != uri.split('@', 1)[1:] class IdentityMany(XMLListElement): _xml_tag = 'many' _xml_namespace = namespace _xml_document = CommonPolicyDocument _xml_children_order = {IdentityExcept.qname: 0} _xml_item_type = IdentityExcept domain = XMLAttribute('domain', type=str, required=False, test_equal=True) def __init__(self, domain=None, exceptions=[]): XMLListElement.__init__(self) self.domain = domain self.update(exceptions) def __repr__(self): return '%s(%r, %s)' % (self.__class__.__name__, self.domain, list(self)) def matches(self, uri): if self.domain is not None: if self.domain != uri.partition('@')[2]: return False for child in self: if not child.matches(uri): return False return True class Identity(XMLListElement): _xml_tag = 'identity' _xml_namespace = namespace _xml_document = CommonPolicyDocument _xml_item_type = (IdentityOne, IdentityMany) def __init__(self, identities=[]): XMLListElement.__init__(self) self.update(identities) def matches(self, uri): for child in self: if child.matches(uri): return True return False class Sphere(XMLElement): _xml_tag = 'sphere' _xml_namespace = namespace _xml_document = CommonPolicyDocument value = XMLAttribute('value', type=str, required=True, test_equal=True) def __init__(self, value): XMLElement.__init__(self) self.value = value def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.value) class ValidFrom(XMLDateTimeElement): _xml_tag = 'from' _xml_namespace = namespace _xml_document = CommonPolicyDocument class ValidUntil(XMLDateTimeElement): _xml_tag = 'until' _xml_namespace = namespace _xml_document = CommonPolicyDocument class ValidityInterval(object): def __init__(self, from_timestamp, until_timestamp): self.valid_from = ValidFrom(from_timestamp) self.valid_until = ValidUntil(until_timestamp) def __eq__(self, other): if isinstance(other, ValidityInterval): return self is other or (self.valid_from == other.valid_from and self.valid_until == other.valid_until) return NotImplemented def __ne__(self, other): if isinstance(other, ValidityInterval): return self is not other and (self.valid_from != other.valid_from or self.valid_until != other.valid_until) return NotImplemented @classmethod def from_elements(cls, from_element, until_element, xml_document=None): instance = object.__new__(cls) instance.valid_from = ValidFrom.from_element(from_element, xml_document) instance.valid_until = ValidUntil.from_element(until_element, xml_document) return instance class Validity(XMLListElement): _xml_tag = 'validity' _xml_namespace = namespace _xml_document = CommonPolicyDocument _xml_item_type = ValidityInterval def __init__(self, children=[]): XMLListElement.__init__(self) self.update(children) def _parse_element(self, element): iterator = iter(element) for first_child in iterator: second_child = iterator.next() if first_child.tag == '{%s}from' % self._xml_namespace and second_child.tag == '{%s}until' % self._xml_namespace: try: item = ValidityInterval.from_elements(first_child, second_child, xml_document=self._xml_document) except: pass else: self._element_map[item.valid_from.element] = item def _build_element(self): for child in self: child.valid_from.to_element() child.valid_until.to_element() def add(self, item): if not isinstance(item, ValidityInterval): raise TypeError("Validity element must be a ValidityInterval instance") self._insert_element(item.valid_from.element) self._insert_element(item.valid_until.element) self._element_map[item.valid_from.element] = item self.__dirty__ = True def remove(self, item): self.element.remove(item.valid_from.element) self.element.remove(item.valid_until.element) del self._element_map[item.valid_from.element] self.__dirty__ = True def check_validity(self): if not self: raise ValidationError("cannot have Validity element without any children") super(Validity, self).check_validity(self) class Conditions(XMLListElement): _xml_tag = 'conditions' _xml_namespace = namespace _xml_document = CommonPolicyDocument _xml_children_order = {Identity.qname: 0, Sphere.qname: 1, Validity.qname: 2} _xml_item_type = (Identity, Sphere, Validity, ConditionElement) def __init__(self, conditions=[]): XMLListElement.__init__(self) self.update(conditions) class Actions(XMLListElement): _xml_tag = 'actions' _xml_namespace = namespace _xml_document = CommonPolicyDocument _xml_item_type = ActionElement def __init__(self, actions=[]): XMLListElement.__init__(self) self.update(actions) class Transformations(XMLListElement): _xml_tag = 'transformations' _xml_namespace = namespace _xml_document = CommonPolicyDocument _xml_item_type = TransformationElement def __init__(self, transformations=[]): XMLListElement.__init__(self) self.update(transformations) class Rule(XMLElement): _xml_tag = 'rule' _xml_namespace = namespace _xml_extension_type = RuleExtension _xml_document = CommonPolicyDocument _xml_children_order = {Conditions.qname: 0, Actions.qname: 1, Transformations.qname: 2} id = XMLElementID('id', type=ID, required=True, test_equal=True) conditions = XMLElementChild('conditions', type=Conditions, required=False, test_equal=True) actions = XMLElementChild('actions', type=Actions, required=False, test_equal=True) transformations = XMLElementChild('transformations', type=Transformations, required=False, test_equal=True) def __init__(self, id, conditions=None, actions=None, transformations=None): XMLElement.__init__(self) self.id = id self.conditions = conditions self.actions = actions self.transformations = transformations def __repr__(self): return '%s(%r, %r, %r, %r)' % (self.__class__.__name__, self.id, self.conditions, self.actions, self.transformations) class RuleSet(XMLListRootElement): _xml_tag = 'ruleset' _xml_namespace = namespace _xml_document = CommonPolicyDocument _xml_item_type = Rule def __init__(self, rules=[]): XMLListRootElement.__init__(self) self.update(rules) def __getitem__(self, key): if key is IterateIDs: return self._xmlid_map[Rule].iterkeys() elif key is IterateItems: return self._xmlid_map[Rule].itervalues() else: return self._xmlid_map[Rule][key] def __delitem__(self, key): if key is All: for item in self._xmlid_map[Rule].values(): self.remove(item) else: self.remove(self._xmlid_map[Rule][key]) def get(self, key, default=None): return self._xmlid_map[Rule].get(key, default) # # Extensions # agp_cp_namespace = 'urn:ag-projects:xml:ns:common-policy' CommonPolicyDocument.register_namespace(agp_cp_namespace, prefix='agp-cp') # A condition element in the AG Projects namespace, it will always be evaluated to false # because it's not understood by servers class FalseCondition(XMLElement, ConditionElement): _xml_tag = 'false-condition' _xml_namespace = agp_cp_namespace _xml_document = CommonPolicyDocument class RuleDisplayName(XMLLocalizedStringElement, RuleExtension): _xml_tag = 'display-name' _xml_namespace = agp_cp_namespace _xml_document = CommonPolicyDocument Rule.register_extension('display_name', RuleDisplayName) ================================================ FILE: sipsimple/payloads/conference.py ================================================ # This module is currently broken. It breaks the core assumption of the # payloads infrastructure, that there is only one element with a given qname # for every given application. # Currently this module defines at least 2 different elements with the same # qname (tag=entry) which are used inside list elements. As a result, when # the list element tries to lookup the class to use for a given qname, only # one list element will get the class right, the other will get a wrong class # because the list element uses the application qname map to determine the # class and that mapping can only contain 1 mapping from a given qname to a # class. Since the class it obtains is not the right one, it will be ignored # as it doesn't match the known item types for that list element and the # corresponding xml data will also be ignored. # # To make matters even worse, this module subclasses XMLElement classes # without changing their qname, which in turn generates even more overlapping # classes for a given qname. At least according to the xml schema, all these # subclasses (which seem to be defined in order to impose some restrictions # in different cases), seem to be unnecessary. The schema only defines one # type with a string element that has no resctrictions and is to be used # in all the places. The code however tries to use a variation of the type # with restrictions in different places and fails as the correct class cannot # be identified anymore (see for example all the UrisType subclasses or the # multiple classes to define purpose elements). # # -Dan # """Parses and produces conference-info messages according to RFC4575.""" __all__ = ['namespace', 'ConferenceDocument', 'ConferenceDescription', 'ConfUris', 'ConfUrisEntry', 'ServiceUris', 'ServiceUrisEntry', 'UrisTypeModified', 'UrisTypeEntry', 'AvailableMedia', 'AvailableMediaEntry', 'Users', 'User', 'UserExtension', 'AssociatedAors', 'Roles', 'Role', 'Endpoint', 'CallInfo', 'Sip', 'Referred', 'JoiningInfo', 'DisconnectionInfo', 'HostInfo', 'HostInfoUris', 'ConferenceState', 'SidebarsByRef', 'SidebarsByVal', 'Conference', 'ConferenceDescriptionExtension', # Extensions 'FileResource', 'FileResources', 'Resources'] from sipsimple.payloads import ValidationError, XMLDocument, XMLRootElement, XMLStringElement, XMLBooleanElement, XMLDateTimeElement, XMLUnsignedIntElement, XMLAnyURIElement from sipsimple.payloads import XMLElementChild, XMLElement, XMLListElement, XMLAttribute namespace = 'urn:ietf:params:xml:ns:conference-info' class ConferenceDocument(XMLDocument): content_type = "application/conference-info+xml" ConferenceDocument.register_namespace(namespace, prefix=None, schema='conference.xsd') # Marker mixins class UserExtension(object): pass class ConferenceDescriptionExtension(object): pass class State(str): def __new__(cls, value): if value not in ('full', 'partial', 'deleted'): raise ValueError("illegal value for state") return str.__new__(cls, value) class Version(str): def __new__(cls, value): return str.__new__(cls, int(value)) class When(XMLDateTimeElement): _xml_tag = 'when' _xml_namespace = namespace _xml_document = ConferenceDocument class Reason(XMLStringElement): _xml_tag = 'reason' _xml_namespace = namespace _xml_document = ConferenceDocument class By(XMLStringElement): _xml_tag = 'by' _xml_namespace = namespace _xml_document = ConferenceDocument class ExecutionType(XMLElement): _xml_tag = None # To be set by the subclass _xml_namespace = namespace _xml_document = ConferenceDocument when = XMLElementChild('when', type=When, required=False, test_equal=True) reason = XMLElementChild('reason', type=Reason, required=False, test_equal=True) by = XMLElementChild('by', type=By, required=False, test_equal=True) def __init__(self, when=None, reason=None, by=None): XMLElement.__init__(self) self.when = when self.reason = reason self.by = by class URI(XMLAnyURIElement): _xml_tag = 'uri' _xml_namespace = namespace _xml_document = ConferenceDocument class DisplayText(XMLStringElement): _xml_tag = 'display-text' _xml_namespace = namespace _xml_document = ConferenceDocument class UrisTypePurpose(XMLStringElement): _xml_tag = 'purpose' _xml_namespace = namespace _xml_document = ConferenceDocument class UrisTypeModified(ExecutionType): _xml_tag = 'modified' class UrisTypeEntry(XMLElement): _xml_tag = 'entry' _xml_namespace = namespace _xml_document = ConferenceDocument state = XMLAttribute('state', type=State, required=False, test_equal=False) uri = XMLElementChild('uri', type=URI, required=True, test_equal=True) display_text = XMLElementChild('display_text', type=DisplayText, required=False, test_equal=True) purpose = XMLElementChild('purpose', type=UrisTypePurpose, required=False, test_equal=True) modified = XMLElementChild('modified', type=UrisTypeModified, required=False, test_equal=True) def __init__(self, uri, state=None, display_text=None, purpose=None, modified=None): XMLElement.__init__(self) self.uri = uri self.state = state self.display_text = display_text self.purpose = purpose self.modified = modified class Subject(XMLStringElement): _xml_tag = 'subject' _xml_namespace = namespace _xml_document = ConferenceDocument class FreeText(XMLStringElement): _xml_tag = 'free-text' _xml_namespace = namespace _xml_document = ConferenceDocument class Keywords(XMLStringElement): _xml_tag = 'keywords' _xml_namespace = namespace _xml_document = ConferenceDocument class ConfUrisPurposeValue(str): def __new__(cls, value): if value not in ('participation', 'streaming'): raise ValueError("illegal value for purpose element") return str.__new__(cls, value) class ConfUrisPurpose(UrisTypePurpose): _xml_value_type = ConfUrisPurposeValue class ConfUrisEntry(UrisTypeEntry): purpose = XMLElementChild('purpose', type=ConfUrisPurpose, required=False, test_equal=True) class ConfUris(XMLListElement): _xml_tag = 'conf-uris' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_item_type = ConfUrisEntry def __init__(self, entries=[]): XMLListElement.__init__(self) self.update(entries) class ServiceUrisPurposeValue(str): def __new__(cls, value): if value not in ('web-page', 'recording', 'event'): raise ValueError("illegal value for purpose element") return str.__new__(cls, value) class ServiceUrisPurpose(UrisTypePurpose): _xml_value_type = ServiceUrisPurposeValue class ServiceUrisEntry(UrisTypeEntry): purpose = XMLElementChild('purpose', type=ServiceUrisPurpose, required=False, test_equal=True) class ServiceUris(XMLListElement): _xml_tag = 'service-uris' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_item_type = ServiceUrisEntry def __init__(self, entries=[]): XMLListElement.__init__(self) self.update(entries) class MaximumUserCount(XMLUnsignedIntElement): _xml_tag = 'maximum-user-count' _xml_namespace = namespace _xml_document = ConferenceDocument class MediaTypeValue(str): def __new__(cls, value): if value not in ('audio', 'video', 'text', 'message'): raise ValueError("illegal value for type element") return str.__new__(cls, value) class MediaType(XMLStringElement): _xml_tag = 'type' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_value_type = MediaTypeValue class MediaTypeStatusValue(str): def __new__(cls, value): if value not in ('sendrecv', 'sendonly', 'recvonly', 'inactive'): raise ValueError("illegal value for status element") return str.__new__(cls, value) class MediaTypeStatus(XMLStringElement): _xml_tag = 'status' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_value_type = MediaTypeStatusValue class AvailableMediaEntry(XMLElement): _xml_tag = 'entry' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_children_order = {DisplayText.qname: 0, MediaType.qname: 1, MediaTypeStatus.qname: 2, None: 3} label = XMLAttribute('label', type=str, required=True, test_equal=False) media_type = XMLElementChild('media_type', type=MediaType, required=True, test_equal=True) display_text = XMLElementChild('display_text', type=DisplayText, required=False, test_equal=True) status = XMLElementChild('status', type=MediaTypeStatus, required=False, test_equal=True) def __init__(self, label, media_type, display_text=None, status=None): XMLElement.__init__(self) self.label = label self.media_type = media_type self.display_text = display_text self.status = status class AvailableMedia(XMLListElement): _xml_tag = 'available-media' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_item_type = AvailableMediaEntry def __init__(self, entries=[]): XMLListElement.__init__(self) self.update(entries) class ConferenceDescription(XMLElement): _xml_tag = 'conference-description' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_extension_type = ConferenceDescriptionExtension display_text = XMLElementChild('display_text', type=DisplayText, required=False, test_equal=True) subject = XMLElementChild('subject', type=Subject, required=False, test_equal=True) free_text = XMLElementChild('free_text', type=FreeText, required=False, test_equal=True) keywords = XMLElementChild('keywords', type=Keywords, required=False, test_equal=True) conf_uris = XMLElementChild('conf_uris', type=ConfUris, required=False, test_equal=True) service_uris = XMLElementChild('service_uris', type=ServiceUris, required=False, test_equal=True) maximum_user_count = XMLElementChild('maximum_user_count', type=MaximumUserCount, required=False, test_equal=True) available_media = XMLElementChild('available_media', type=AvailableMedia, required=False, test_equal=True) def __init__(self, display_text=None, subject=None, free_text=None, keywords=None, conf_uris=None, service_uris=None, maximum_user_count=None, available_media=None): XMLElement.__init__(self) self.display_text = display_text self.subject = subject self.free_text = free_text self.keywords = keywords self.conf_uris = conf_uris self.service_uris = service_uris self.maximum_user_count = maximum_user_count self.available_media = available_media class WebPage(XMLStringElement): _xml_tag = 'web-page' _xml_namespace = namespace _xml_document = ConferenceDocument class HostInfoUris(XMLListElement): _xml_tag = 'uris' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_item_type = UrisTypeEntry def __init__(self, entries=[]): XMLListElement.__init__(self) self.update(entries) class HostInfo(XMLElement): _xml_tag = 'host-info' _xml_namespace = namespace _xml_document = ConferenceDocument display_text = XMLElementChild('display_text', type=DisplayText, required=False, test_equal=True) web_page = XMLElementChild('web_page', type=WebPage, required=False, test_equal=True) uris = XMLElementChild('uris', type=HostInfoUris, required=False, test_equal=True) def __init__(self, display_text=None, web_page=None, uris=None): XMLElement.__init__(self) self.display_text = display_text self.web_page = web_page self.uris = uris class UserCount(XMLUnsignedIntElement): _xml_tag = 'user-count' _xml_namespace = namespace _xml_document = ConferenceDocument class Active(XMLBooleanElement): _xml_tag = 'active' _xml_namespace = namespace _xml_document = ConferenceDocument class Locked(XMLBooleanElement): _xml_tag = 'locked' _xml_namespace = namespace _xml_document = ConferenceDocument class ConferenceState(XMLElement): _xml_tag = 'conference-state' _xml_namespace = namespace _xml_document = ConferenceDocument user_count = XMLElementChild('user_count', type=UserCount, required=False, test_equal=True) active = XMLElementChild('active', type=Active, required=False, test_equal=True) locked = XMLElementChild('locked', type=Locked, required=False, test_equal=True) def __init__(self, user_count=None, active=None, locked=None): XMLElement.__init__(self) self.user_count = user_count self.active = active self.locked = locked class AssociatedAors(XMLListElement): _xml_tag = 'associated-aors' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_item_type = UrisTypeEntry def __init__(self, entries=[]): XMLListElement.__init__(self) self.update(entries) class Role(XMLStringElement): _xml_tag = 'entry' _xml_namespace = namespace _xml_document = ConferenceDocument class Roles(XMLListElement): _xml_tag = 'roles' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_item_type = Role def __init__(self, roles=[]): XMLListElement.__init__(self) self.update(roles) class Languages(XMLStringElement): _xml_tag = 'languages' _xml_namespace = namespace _xml_document = ConferenceDocument class CascadedFocus(XMLStringElement): _xml_tag = 'cascaded-focus' _xml_namespace = namespace _xml_document = ConferenceDocument class Referred(ExecutionType): _xml_tag = 'referred' class EndpointStatusValue(str): def __new__(cls, value): if value not in ('connected', 'disconnected', 'on-hold', 'muted-via-focus', 'pending', 'alerting', 'dialing-in', 'dialing-out', 'disconnecting'): raise ValueError("illegal value for status element") return str.__new__(cls, value) class EndpointStatus(XMLStringElement): _xml_tag = 'status' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_value_type = EndpointStatusValue class JoiningMethodValue(str): def __new__(cls, value): if value not in ('dialed-in', 'dialed-out', 'focus-owner'): raise ValueError("illegal value for joining method element") return str.__new__(cls, value) class JoiningMethod(XMLStringElement): _xml_tag = 'joining-method' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_value_type = JoiningMethodValue class JoiningInfo(ExecutionType): _xml_tag = 'joining-info' class DisconnectionMethodValue(str): def __new__(cls, value): if value not in ('departed', 'booted', 'failed', 'busy'): raise ValueError("illegal value for disconnection method element") return str.__new__(cls, value) class DisconnectionMethod(XMLStringElement): _xml_tag = 'disconnection-method' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_value_type = DisconnectionMethodValue class DisconnectionInfo(ExecutionType): _xml_tag = 'disconnection-info' class Label(XMLStringElement): _xml_tag = 'label' _xml_namespace = namespace _xml_document = ConferenceDocument class SrcId(XMLStringElement): _xml_tag = 'src-id' _xml_namespace = namespace _xml_document = ConferenceDocument class Media(XMLElement): _xml_tag = 'media' _xml_namespace = namespace _xml_document = ConferenceDocument id = XMLAttribute('id', type=str, required=True, test_equal=False) display_text = XMLElementChild('display_text', type=DisplayText, required=False, test_equal=True) media_type = XMLElementChild('media_type', type=MediaType, required=False, test_equal=True) label = XMLElementChild('label', type=Label, required=False, test_equal=True) src_id = XMLElementChild('src_id', type=SrcId, required=False, test_equal=True) status = XMLElementChild('status', type=MediaTypeStatus, required=False, test_equal=True) def __init__(self, id, display_text=None, media_type=None, label=None, src_id=None, status=None): XMLElement.__init__(self) self.id = id self.display_text = display_text self.media_type = media_type self.label = label self.src_id = src_id self.status = status class CallId(XMLStringElement): _xml_tag = 'call-id' _xml_namespace = namespace _xml_document = ConferenceDocument class FromTag(XMLStringElement): _xml_tag = 'from-tag' _xml_namespace = namespace _xml_document = ConferenceDocument class ToTag(XMLStringElement): _xml_tag = 'to-tag' _xml_namespace = namespace _xml_document = ConferenceDocument class Sip(XMLElement): _xml_tag = 'sip' _xml_namespace = namespace _xml_document = ConferenceDocument display_text = XMLElementChild('display_text', type=DisplayText, required=False, test_equal=True) call_id = XMLElementChild('call_id', type=CallId, required=False, test_equal=True) from_tag = XMLElementChild('from_tag', type=FromTag, required=False, test_equal=True) to_tag = XMLElementChild('to_tag', type=ToTag, required=False, test_equal=True) def __init__(self, display_text=None, call_id=None, from_tag=None, to_tag=None): XMLElement.__init__(self) self.display_text = display_text self.call_id = call_id self.from_tag = from_tag self.to_tag = to_tag class CallInfo(XMLElement): _xml_tag = 'call-info' _xml_namespace = namespace _xml_document = ConferenceDocument sip = XMLElementChild('sip', type=Sip, required=False, test_equal=True) def __init__(self, sip=None): XMLElement.__init__(self) self.sip = sip class Endpoint(XMLListElement): _xml_tag = 'endpoint' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_item_type = Media entity = XMLAttribute('entity', type=str, required=True, test_equal=False) state = XMLAttribute('state', type=State, required=False, test_equal=False) display_text = XMLElementChild('display_text', type=DisplayText, required=False, test_equal=True) referred = XMLElementChild('referred', type=Referred, required=False, test_equal=True) status = XMLElementChild('status', type=EndpointStatus, required=False, test_equal=True) joining_method = XMLElementChild('joining_method', type=JoiningMethod, required=False, test_equal=True) joining_info = XMLElementChild('joining_info', type=JoiningInfo, required=False, test_equal=True) disconnection_method = XMLElementChild('disconnection_method', type=DisconnectionMethod, required=False, test_equal=True) disconnection_info = XMLElementChild('disconnection_info', type=DisconnectionInfo, required=False, test_equal=True) call_info = XMLElementChild('call_info', type=CallInfo, required=False, test_equal=True) def __init__(self, entity, state='full', display_text=None, referred=None, status=None, joining_method=None, joining_info=None, disconnection_method=None, disconnection_info=None, call_info=None, media=[]): XMLListElement.__init__(self) self.entity = entity self.state = state self.display_text = display_text self.referred = referred self.status = status self.joining_method = joining_method self.joining_info = joining_info self.disconnection_method = disconnection_method self.disconnection_info = disconnection_info self.call_info = call_info self.update(media) def __repr__(self): args = ('entity', 'state', 'display_text', 'referred', 'status', 'joining_method', 'joining_info', 'disconnection_method', 'disconnection_info', 'call_info') return "%s(%s, media=%r)" % (self.__class__.__name__, ', '.join("%s=%r" % (name, getattr(self, name)) for name in args), list(self)) class User(XMLListElement): _xml_tag = 'user' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_item_type = Endpoint _xml_extension_type = UserExtension entity = XMLAttribute('entity', type=str, required=True, test_equal=False) state = XMLAttribute('state', type=State, required=False, test_equal=False) display_text = XMLElementChild('display_text', type=DisplayText, required=False, test_equal=True) associated_aors = XMLElementChild('associated_aors', type=AssociatedAors, required=False, test_equal=True) roles = XMLElementChild('roles', type=Roles, required=False, test_equal=True) languages = XMLElementChild('languages', type=Languages, required=False, test_equal=True) cascaded_focus = XMLElementChild('cascaded_focus', type=CascadedFocus, required=False, test_equal=True) def __init__(self, entity, state='full', display_text=None, associated_aors=None, roles=None, languages=None, cascaded_focus=None, endpoints=[]): XMLListElement.__init__(self) self.entity = entity self.state = state self.display_text = display_text self.associated_aors = associated_aors self.roles = roles self.languages = languages self.cascaded_focus = cascaded_focus self.update(endpoints) def __repr__(self): args = ('entity', 'state', 'display_text', 'associated_aors', 'roles', 'languages', 'cascaded_focus') return "%s(%s, endpoints=%r)" % (self.__class__.__name__, ', '.join("%s=%r" % (name, getattr(self, name)) for name in args), list(self)) class Users(XMLListElement): _xml_tag = 'users' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_item_type = User state = XMLAttribute('state', type=State, required=False, test_equal=False) def __init__(self, state='full', users=[]): XMLListElement.__init__(self) self.state = state self.update(users) def __repr__(self): return "%s(state=%r, users=%r)" % (self.__class__.__name__, self.state, list(self)) class SidebarsByRef(XMLListElement): _xml_tag = 'sidebars-by-ref' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_item_type = UrisTypeEntry def __init__(self, entries=[]): XMLListElement.__init__(self) self.update(entries) class SidebarsByVal(XMLListElement): _xml_tag = 'sidebars-by-val' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_item_type = None # will be set later, after the item type is defined below state = XMLAttribute('state', type=State, required=False, test_equal=False) def __init__(self, state='full', entries=[]): XMLListElement.__init__(self) self.state = state self.update(entries) def __repr__(self): return "%s(state=%r, entries=%r)" % (self.__class__.__name__, self.state, list(self)) class SidebarsByValEntry(XMLElement): _xml_tag = 'entry' _xml_namespace = namespace _xml_document = ConferenceDocument entity = XMLAttribute('entity', type=str, required=True, test_equal=False) state = XMLAttribute('state', type=State, required=False, test_equal=False) conference_description = XMLElementChild('conference_description', type=ConferenceDescription, required=False, test_equal=True) host_info = XMLElementChild('host_info', type=HostInfo, required=False, test_equal=True) conference_state = XMLElementChild('conference_state', type=ConferenceState, required=False, test_equal=True) users = XMLElementChild('users', type=Users, required=False, test_equal=True) sidebars_by_ref = XMLElementChild('sidebars_by_ref', type=SidebarsByRef, required=False, test_equal=True) sidebars_by_val = XMLElementChild('sidebars_by_val', type=SidebarsByVal, required=False, test_equal=True) def __init__(self, entity, state='full', version=None, conference_description=None, host_info=None, conference_state=None, users=None, sidebars_by_ref=None, sidebars_by_val=None): XMLElement.__init__(self) self.entity = entity self.state = state self.version = version self.conference_description = conference_description self.host_info = host_info self.conference_state = conference_state self.users = users self.sidebars_by_ref = sidebars_by_ref self.sidebars_by_val = sidebars_by_val if self.state == "full" and (self.conference_description is None or self.users is None): raise ValidationError("A full conference document must at least include the and child elements.") SidebarsByVal._xml_item_type = SidebarsByValEntry class Conference(XMLRootElement): _xml_tag = 'conference-info' _xml_namespace = namespace _xml_document = ConferenceDocument _xml_children_order = {ConferenceDescription.qname: 0, HostInfo.qname: 1, ConferenceState.qname: 2, Users.qname: 3, SidebarsByRef.qname: 4, SidebarsByVal.qname: 5, None: 6} entity = XMLAttribute('entity', type=str, required=True, test_equal=False) state = XMLAttribute('state', type=State, required=False, test_equal=False) version = XMLAttribute('version', type=Version, required=False, test_equal=False) conference_description = XMLElementChild('conference_description', type=ConferenceDescription, required=False, test_equal=True) host_info = XMLElementChild('host_info', type=HostInfo, required=False, test_equal=True) conference_state = XMLElementChild('conference_state', type=ConferenceState, required=False, test_equal=True) users = XMLElementChild('users', type=Users, required=False, test_equal=True) sidebars_by_ref = XMLElementChild('sidebars_by_ref', type=SidebarsByRef, required=False, test_equal=True) sidebars_by_val = XMLElementChild('sidebars_by_val', type=SidebarsByVal, required=False, test_equal=True) def __init__(self, entity, state='full', version=None, conference_description=None, host_info=None, conference_state=None, users=None, sidebars_by_ref=None, sidebars_by_val=None): XMLRootElement.__init__(self) self.entity = entity self.state = state self.version = version self.conference_description = conference_description self.host_info = host_info self.conference_state = conference_state self.users = users self.sidebars_by_ref = sidebars_by_ref self.sidebars_by_val = sidebars_by_val if self.state == "full" and (self.conference_description is None or self.users is None): raise ValidationError("A full conference document must at least include the and child elements.") # # Extensions # agp_conf_namespace = 'urn:ag-projects:xml:ns:conference-info' ConferenceDocument.register_namespace(agp_conf_namespace, prefix='agp-conf') class FileResource(XMLElement): _xml_tag = 'file' _xml_namespace = agp_conf_namespace _xml_document = ConferenceDocument name = XMLAttribute('name', type=unicode, required=True, test_equal=False) hash = XMLAttribute('hash', type=str, required=True, test_equal=False) size = XMLAttribute('size', type=int, required=True, test_equal=False) sender = XMLAttribute('sender', type=unicode, required=True, test_equal=False) status = XMLAttribute('status', type=str, required=True, test_equal=False) def __init__(self, name, hash, size, sender, status): XMLElement.__init__(self) self.name = name self.hash = hash self.size = size self.sender = sender self.status = status class FileResources(XMLListElement): _xml_tag = 'files' _xml_namespace = agp_conf_namespace _xml_document = ConferenceDocument _xml_item_type = FileResource def __init__(self, files=[]): XMLListElement.__init__(self) self.update(files) class Resources(XMLElement, ConferenceDescriptionExtension): _xml_tag = 'resources' _xml_namespace = agp_conf_namespace _xml_document = ConferenceDocument files = XMLElementChild('files', type=FileResources, required=False, test_equal=True) def __init__(self, files=None): XMLElement.__init__(self) self.files = files ConferenceDescription.register_extension('resources', Resources) class ScreenImageURL(XMLStringElement, UserExtension): _xml_tag = 'screen_image_url' _xml_namespace = agp_conf_namespace _xml_document = ConferenceDocument User.register_extension('screen_image_url', ScreenImageURL) ================================================ FILE: sipsimple/payloads/datatypes.py ================================================ """Data types used for simple XML elements and for XML attributes""" __all__ = ['Boolean', 'DateTime', 'Byte', 'UnsignedByte', 'Short', 'UnsignedShort', 'Int', 'UnsignedInt', 'Long', 'UnsignedLong', 'PositiveInteger', 'NegativeInteger', 'NonNegativeInteger', 'NonPositiveInteger', 'ID', 'AnyURI', 'SIPURI', 'XCAPURI'] import re import urllib import urlparse from sipsimple.util import ISOTimestamp class Boolean(int): def __new__(cls, value): return int.__new__(cls, bool(value)) def __repr__(self): return 'True' if self else 'False' __str__ = __repr__ @classmethod def __xmlparse__(cls, value): if value in ('True', 'true'): return int.__new__(cls, 1) elif value in ('False', 'false'): return int.__new__(cls, 0) else: raise ValueError("Invalid boolean string representation: %s" % value) def __xmlbuild__(self): return u'true' if self else u'false' class DateTime(ISOTimestamp): pass class Byte(int): def __new__(cls, value): instance = int.__new__(cls, value) if not (-128 <= instance <= 127): raise ValueError("integer number must be a signed 8bit value") return instance class UnsignedByte(int): def __new__(cls, value): instance = int.__new__(cls, value) if not (0 <= instance <= 255): raise ValueError("integer number must be an unsigned 8bit value") return instance class Short(int): def __new__(cls, value): instance = int.__new__(cls, value) if not (-32768 <= instance <= 32767): raise ValueError("integer number must be a signed 16bit value") return instance class UnsignedShort(int): def __new__(cls, value): instance = int.__new__(cls, value) if not (0 <= instance <= 65535): raise ValueError("integer number must be an unsigned 16bit value") return instance class Int(long): def __new__(cls, value): instance = long.__new__(cls, value) if not (-2147483648 <= instance <= 2147483647): raise ValueError("integer number must be a signed 32bit value") return instance class UnsignedInt(long): def __new__(cls, value): instance = long.__new__(cls, value) if not (0 <= instance <= 4294967295): raise ValueError("integer number must be an unsigned 32bit value") return instance class Long(long): def __new__(cls, value): instance = long.__new__(cls, value) if not (-9223372036854775808 <= instance <= 9223372036854775807): raise ValueError("integer number must be a signed 64bit value") return instance class UnsignedLong(long): def __new__(cls, value): instance = long.__new__(cls, value) if not (0 <= instance <= 18446744073709551615): raise ValueError("integer number must be an unsigned 64bit value") return instance class PositiveInteger(long): def __new__(cls, value): instance = long.__new__(cls, value) if instance <= 0: raise ValueError("integer number must be a positive value") return instance class NegativeInteger(long): def __new__(cls, value): instance = long.__new__(cls, value) if instance >= 0: raise ValueError("integer number must be a negative value") return instance class NonNegativeInteger(long): def __new__(cls, value): instance = long.__new__(cls, value) if instance < 0: raise ValueError("integer number must be a non-negative value") return instance class NonPositiveInteger(long): def __new__(cls, value): instance = long.__new__(cls, value) if instance > 0: raise ValueError("integer number must be a non-positive value") return instance class ID(str): _id_regex = re.compile(r'^[a-z_][a-z0-9_.-]*$', re.I) def __new__(cls, value): if not cls._id_regex.match(value): raise ValueError("illegal ID value: %s" % value) return str.__new__(cls, value) class AnyURI(unicode): @classmethod def __xmlparse__(cls, value): return cls.__new__(cls, urllib.unquote(value).decode('utf-8')) def __xmlbuild__(self): return urllib.quote(self.encode('utf-8')) class SIPURI(AnyURI): _path_regex = re.compile(r'^((?P[^:@]+)(:(?P[^@]+))?@)?(?P.*)$') def __new__(cls, value): instance = AnyURI.__new__(cls, value) uri = urlparse.urlparse(instance) if uri.scheme not in ('sip', 'sips'): raise ValueError("illegal scheme for SIP URI: %s" % uri.scheme) instance.scheme = uri.scheme instance.__dict__.update(cls._path_regex.match(uri.path).groupdict()) instance.params = {} if uri.params: params = (param.split('=', 1) for param in uri.params.split(';')) for param in params: if not param[0]: raise ValueError("illegal SIP URI parameter name: %s" % param[0]) if len(param) == 1: param.append(None) elif '=' in param[1]: raise ValueError("illegal SIP URI parameter value: %s" % param[1]) instance.params[param[0]] = param[1] if uri.query: try: instance.headers = dict(header.split('=') for header in uri.query.split('&')) except ValueError: raise ValueError("illegal SIP URI headers: %s" % uri.query) else: for name, value in instance.headers.iteritems(): if not name or not value: raise ValueError("illegal URI header: %s=%s" % (name, value)) else: instance.headers = {} return instance class XCAPURI(AnyURI): _path_regex = re.compile(r'^(?P/(([^/]+)/)*)?(?P[^/]+)/((?Pglobal)|(users/(?P[^/]+)))/(?P~?(([^~]+~)|([^~]+))*)(/~~(?P.*))?$') def __new__(cls, value): instance = AnyURI.__new__(cls, value) uri = urlparse.urlparse(instance) if uri.scheme not in ('http', 'https', ''): raise ValueError("illegal scheme for XCAP URI: %s" % uri.scheme) instance.scheme = uri.scheme instance.username = uri.username instance.password = uri.password instance.hostname = uri.hostname instance.port = uri.port instance.__dict__.update(cls._path_regex.match(uri.path).groupdict()) instance.globaltree = instance.globaltree is not None if uri.query: try: instance.query = dict(header.split('=') for header in uri.query.split('&')) except ValueError: raise ValueError("illegal XCAP URI query string: %s" % uri.query) else: for name, value in instance.query.iteritems(): if not name or not value: raise ValueError("illegal XCAP URI query parameter: %s=%s" % (name, value)) else: instance.query = {} return instance relative = property(lambda self: self.scheme == '') ================================================ FILE: sipsimple/payloads/dialoginfo.py ================================================ """Parses and produces dialog-info messages according to RFC4235.""" __all__ = ['namespace', 'DialogInfoDocument', 'DialogState', 'Replaces', 'ReferredBy', 'Identity', 'Param', 'Target', 'Local', 'Remote', 'Dialog', 'DialogInfo'] from sipsimple.payloads import XMLDocument, XMLListRootElement, XMLListElement, XMLStringElement, XMLNonNegativeIntegerElement, XMLElementChild, XMLEmptyElement, XMLElement, XMLElementID, XMLAttribute from sipsimple.payloads import IterateIDs, IterateItems, All namespace = 'urn:ietf:params:xml:ns:dialog-info' class DialogInfoDocument(XMLDocument): content_type = "application/dialog-info+xml" DialogInfoDocument.register_namespace(namespace, prefix=None, schema='dialog-info.xsd') # Attribute value types class StateValue(str): def __new__(cls, value): if value not in ('full', 'partial'): raise ValueError("illegal value for state") return str.__new__(cls, value) class VersionValue(int): def __new__(cls, value): value = int.__new__(cls, value) if value < 0: raise ValueError("illegal value for version") return value class DirectionValue(str): def __new__(cls, value): if value not in ('initiator', 'recipient'): raise ValueError("illegal value for direction") return str.__new__(cls, value) class DialogEventValue(str): def __new__(cls, value): if value not in ('rejected', 'cancelled', 'replaced', 'local-bye', 'remote-bye', 'error', 'timeout'): raise ValueError("illegal value for dialog state event") return str.__new__(cls, value) class DialogStateValue(str): def __new__(cls, value): if value not in ('trying', 'proceeding', 'early', 'confirmed', 'terminated'): raise ValueError("illegal value for dialog state") return str.__new__(cls, value) class CodeValue(int): def __new__(cls, value): value = int.__new__(cls, value) if value < 100 or value > 699: raise ValueError("illegal value for code") return value # Elements class CallId(XMLStringElement): _xml_tag = 'call-id' _xml_namespace = namespace _xml_document = DialogInfoDocument class LocalTag(XMLStringElement): _xml_tag = 'local-tag' _xml_namespace = namespace _xml_document = DialogInfoDocument class RemoteTag(XMLStringElement): _xml_tag = 'remote-tag' _xml_namespace = namespace _xml_document = DialogInfoDocument class DialogState(XMLStringElement): _xml_tag = 'state' _xml_namespace = namespace _xml_document = DialogInfoDocument _xml_value_type = DialogStateValue code = XMLAttribute('code', type=int, required=False, test_equal=True) event = XMLAttribute('event', type=DialogEventValue, required=False, test_equal=True) class Duration(XMLNonNegativeIntegerElement): _xml_tag = 'duration' _xml_namespace = namespace _xml_document = DialogInfoDocument class Replaces(XMLEmptyElement): _xml_tag = 'replaces' _xml_namespace = namespace _xml_document = DialogInfoDocument call_id = XMLAttribute('call_id', xmlname='call-id', type=str, required=True, test_equal=True) local_tag = XMLAttribute('local_tag', xmlname='local-tag', type=str, required=True, test_equal=True) remote_tag = XMLAttribute('remote_tag', xmlname='remote-tag', type=str, required=True, test_equal=True) def __init__(self, call_id, local_tag, remote_tag): XMLEmptyElement.__init__(self) self.call_id = call_id self.local_tag = local_tag self.remote_tag = remote_tag class ReferredBy(XMLStringElement): _xml_tag = 'referred-by' _xml_namespace = namespace _xml_document = DialogInfoDocument display = XMLAttribute('display', type=str, required=False, test_equal=True) class Identity(XMLStringElement): _xml_tag = 'identity' _xml_namespace = namespace _xml_document = DialogInfoDocument display = XMLAttribute('display', type=str, required=False, test_equal=True) class Param(XMLEmptyElement): _xml_tag = 'param' _xml_namespace = namespace _xml_document = DialogInfoDocument pname = XMLAttribute('pname', type=str, required=True, test_equal=True) pval = XMLAttribute('pval', type=str, required=True, test_equal=True) def __init__(self, pname, pval): XMLEmptyElement.__init__(self) self.pname = pname self.pval = pval class Target(XMLListElement): _xml_tag = 'target' _xml_namespace = namespace _xml_document = DialogInfoDocument _xml_item_type = Param uri = XMLAttribute('uri', type=str, required=True, test_equal=True) def __init__(self, uri, params=[]): self.uri = uri self.update(params) class Participant(XMLElement): identity = XMLElementChild('identity', type=Identity, required=False, test_equal=True) target = XMLElementChild('target', type=Target, required=False, test_equal=True) def __init__(self, identity=None, target=None): XMLElement.__init__(self) self.identity = identity self.target = target class Local(Participant): _xml_tag = 'local' _xml_namespace = namespace _xml_document = DialogInfoDocument class Remote(Participant): _xml_tag = 'remote' _xml_namespace = namespace _xml_document = DialogInfoDocument class Dialog(XMLElement): _xml_tag = 'dialog' _xml_namespace = namespace _xml_document = DialogInfoDocument id = XMLElementID('id', type=str, required=True, test_equal=True) call_id = XMLAttribute('call_id', xmlname='call-id', type=str, required=False, test_equal=True) local_tag = XMLAttribute('local_tag', xmlname='local-tag', type=str, required=False, test_equal=True) remote_tag = XMLAttribute('remote_tag', xmlname='remote-tag', type=str, required=False, test_equal=True) direction = XMLAttribute('direction', type=DirectionValue, required=False, test_equal=True) state = XMLElementChild('state', type=DialogState, required=True, test_equal=True) duration = XMLElementChild('duration', type=Duration, required=False, test_equal=True) replaces = XMLElementChild('replaces', type=Replaces, required=False, test_equal=True) referred_by = XMLElementChild('referred_by', type=ReferredBy, required=False, test_equal=True) local = XMLElementChild('local', type=Local, required=False, test_equal=True) remote = XMLElementChild('remote', type=Remote, required=False, test_equal=True) def __init__(self, id, state, call_id=None, local_tag=None, remote_tag=None, direction=None, duration=None, replaces=None, referred_by=None, local=None, remote=None): XMLElement.__init__(self) self.id = id self.state = state self.call_id = call_id self.local_tag = local_tag self.remote_tag = remote_tag self.direction = direction self.duration = duration self.replaces = replaces self.referred_by = referred_by self.local = local self.remote = remote class DialogInfo(XMLListRootElement): _xml_tag = 'dialog-info' _xml_namespace = namespace _xml_document = DialogInfoDocument _xml_children_order = {Dialog.qname: 0, None: 1} _xml_item_type = Dialog version = XMLAttribute('version', type=VersionValue, required=True, test_equal=True) state = XMLAttribute('state', type=StateValue, required=True, test_equal=True) entity = XMLAttribute('entity', type=str, required=True, test_equal=True) def __init__(self, version, state, entity, dialogs=[]): XMLListRootElement.__init__(self) self.version = version self.state = state self.entity = entity self.update(dialogs) def __getitem__(self, key): if key is IterateIDs: return self._xmlid_map[Dialog].iterkeys() elif key is IterateItems: return self._xmlid_map[Dialog].itervalues() else: return self._xmlid_map[Dialog][key] def __delitem__(self, key): if key is All: for item in self._xmlid_map[Dialog].values(): self.remove(item) else: self.remove(self._xmlid_map[Dialog][key]) def get(self, key, default=None): return self._xmlid_map[Dialog].get(key, default) ================================================ FILE: sipsimple/payloads/dialogrules.py ================================================ """ Parses and produces Dialog Authorization Rules documents. As there is no RFC for this we'll use common-policy format (RFC 4745). NOTE: Subscription Handling has been taken from RFC 5025. """ __all__ = ['namespace', 'DialogRulesDocument', 'SubHandling', 'DialogRules'] from sipsimple.payloads import commonpolicy, omapolicy from sipsimple.payloads import XMLStringElement namespace = 'urn:ag-projects:xml:ns:dialog-rules' class DialogRulesDocument(commonpolicy.CommonPolicyDocument): pass DialogRulesDocument.register_namespace(namespace, prefix='dr', schema='dialog-rules.xsd') DialogRulesDocument.register_namespace(omapolicy.namespace, prefix='ocp', schema='oma-common-policy.xsd') DialogRulesDocument.register_element(omapolicy.AnonymousRequest) DialogRulesDocument.register_element(omapolicy.OtherIdentity) DialogRulesDocument.register_element(omapolicy.Entry) DialogRulesDocument.register_element(omapolicy.ExternalList) class SubHandlingValue(str): __prioritymap__ = {'block': 0, 'confirm': 10, 'polite-block': 20, 'allow': 30} def __new__(cls, value): if value not in cls.__prioritymap__: raise ValueError("illegal value for SubHandling element") return str.__new__(cls, value) @property def priority(self): return self.__prioritymap__[self] class SubHandling(XMLStringElement, commonpolicy.ActionElement): _xml_tag = 'sub-handling' _xml_namespace = namespace _xml_document = DialogRulesDocument _xml_value_type = SubHandlingValue class DialogRules(commonpolicy.RuleSet): _xml_document = DialogRulesDocument ================================================ FILE: sipsimple/payloads/directory.py ================================================ """Parses xcap-directory messages according to OMA TS XDM Core 1.1""" __all__ = ['namespace', 'XCAPDirectoryDocument', 'Folder', 'Entry', 'ErrorCode'] from sipsimple.payloads import XMLDocument, XMLListRootElement, XMLStringElement, XMLListElement, XMLAttribute, XMLElementChild from sipsimple.payloads.datatypes import DateTime, AnyURI namespace = 'urn:oma:xml:xdm:xcap-directory' class XCAPDirectoryDocument(XMLDocument): content_type = "application/vnd.oma.xcap-directory+xml" XCAPDirectoryDocument.register_namespace(namespace, prefix=None, schema='xcap-directory.xsd') # Attribute value types class SizeValue(int): def __new__(cls, value): value = int.__new__(cls, value) if value <= 0: raise ValueError("illegal value for size") return value # Elements class Entry(XMLStringElement): _xml_tag = 'entry' _xml_namespace = namespace _xml_document = XCAPDirectoryDocument uri = XMLAttribute('uri', type=AnyURI, required=True, test_equal=True) etag = XMLAttribute('etag', type=str, required=True, test_equal=True) last_modified = XMLAttribute('last_modified', xmlname='last-modified', type=DateTime, required=False, test_equal=True) size = XMLAttribute('size', type=SizeValue, required=False, test_equal=True) class ErrorCode(XMLStringElement): _xml_tag = 'error-code' _xml_namespace = namespace _xml_document = XCAPDirectoryDocument class Folder(XMLListElement): _xml_tag = 'folder' _xml_namespace = namespace _xml_document = XCAPDirectoryDocument _xml_item_type = Entry auid = XMLAttribute('auid', type=str, required=True, test_equal=True) error_code = XMLElementChild('error_code', type=ErrorCode, required=False, test_equal=True, onset=lambda self, descriptor, value: self.clear() if value is not None else None) def __init__(self, auid, entries=[], error_code=None): if error_code is not None and entries: raise ValueError("Cannot set both an error code and add entries at the same time") XMLListElement.__init__(self) self.auid = auid self.error_code = error_code self.update(entries) def add(self, entry): if self.error_code is not None: raise ValueError("Cannot add an entry when error_code is set") super(Folder, self).add(entry) class XCAPDirectory(XMLListRootElement): _xml_tag = 'xcap-directory' _xml_namespace = namespace _xml_document = XCAPDirectoryDocument _xml_item_type = Folder def __init__(self, folders=[]): XMLListRootElement.__init__(self) self.update(folders) ================================================ FILE: sipsimple/payloads/imdn.py ================================================ """Parses and produces IMDN messages according to RFC5438.""" __all__ = ['namespace', 'IMDNDocument', 'MessageId', 'DateTime', 'RecipientUri', 'OriginalRecipientUri', 'Subject', 'Delivered', 'Failed', 'Displayed', 'Processed', 'Stored', 'Error', 'Forbidden', 'DeliveryStatus', 'DisplayStatus', 'ProcessingStatus', 'DeliveryNotification', 'DisplayNotification', 'ProcessingNotification', 'IMDNMessage'] from sipsimple.payloads import XMLDocument, XMLRootElement, XMLAnyURIElement, XMLStringElement, XMLElementChild, XMLElementChoiceChild, XMLEmptyElement, XMLElement namespace = 'urn:ietf:params:xml:ns:imdn' class IMDNDocument(XMLDocument): content_type = "message/imdn+xml" IMDNDocument.register_namespace(namespace, prefix=None, schema='imdn.xsd') # Elements class MessageId(XMLStringElement): _xml_tag = 'message-id' _xml_namespace = namespace _xml_document = IMDNDocument class DateTime(XMLStringElement): _xml_tag = 'datetime' _xml_namespace = namespace _xml_document = IMDNDocument class RecipientUri(XMLAnyURIElement): _xml_tag = 'recipient-uri' _xml_namespace = namespace _xml_document = IMDNDocument class OriginalRecipientUri(XMLAnyURIElement): _xml_tag = 'original-recipient-uri' _xml_namespace = namespace _xml_document = IMDNDocument class Subject(XMLStringElement): _xml_tag = 'subject' _xml_namespace = namespace _xml_document = IMDNDocument class StatusElement(XMLEmptyElement): _xml_namespace = namespace _xml_document = IMDNDocument def __init__(self): XMLEmptyElement.__init__(self) def __str__(self): return "%s" % (self._xml_tag) class Delivered(StatusElement): _xml_tag = 'delivered' class Failed(StatusElement): _xml_tag = 'failed' class Displayed(StatusElement): _xml_tag = 'displayed' class Processed(StatusElement): _xml_tag = 'processed' class Stored(StatusElement): _xml_tag = 'stored' class Error(StatusElement): _xml_tag = 'error' class Forbidden(StatusElement): _xml_tag = 'forbidden' class Status(XMLElement): _xml_tag = 'status' _xml_namespace = namespace _xml_document = IMDNDocument def __init__(self, status): XMLElement.__init__(self) if isinstance(status, basestring) and status not in ('delivered', 'failed', 'displayed', 'processed', 'stored', 'forbidden', 'error'): raise ValueError("illegal value for status") self.status = status if not isinstance(status, basestring) else eval(status.title())() def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self.status) def __str__(self): return "%s" % (self.status) class DeliveryStatus(Status): status = XMLElementChoiceChild('status', types=(Delivered, Failed, Forbidden, Error), required=True, test_equal=True) class DisplayStatus(Status): status = XMLElementChoiceChild('status', types=(Displayed, Forbidden, Error), required=True, test_equal=True) class ProcessingStatus(Status): status = XMLElementChoiceChild('status', types=(Processed, Stored, Forbidden, Error), required=True, test_equal=True) class NotificationType(XMLElement): _xml_namespace = namespace _xml_document = IMDNDocument def __init__(self, status): XMLElement.__init__(self) self.status = status class DeliveryNotification(NotificationType): _xml_tag = 'delivery-notification' status = XMLElementChild('status', type=DeliveryStatus, required=True, test_equal=True) class DisplayNotification(NotificationType): _xml_tag = 'display-notification' status = XMLElementChild('status', type=DisplayStatus, required=True, test_equal=True) class ProcessingNotification(NotificationType): _xml_tag = 'processing-notification' status = XMLElementChild('status', type=ProcessingStatus, required=True, test_equal=True) # document class IMDNMessage(XMLRootElement): _xml_tag = 'imdn' _xml_namespace = namespace _xml_document = IMDNDocument _xml_children_order = {MessageId.qname: 0, DateTime.qname: 1, RecipientUri.qname: 2, OriginalRecipientUri.qname: 3, Subject.qname: 4, DeliveryNotification.qname: 5, DisplayNotification.qname: 6, ProcessingNotification.qname: 7, None: 8} message_id = XMLElementChild('message_id', type=MessageId, required=True, test_equal=True) datetime = XMLElementChild('datetime', type=DateTime, required=True, test_equal=True) recipient_uri = XMLElementChild('recipient_uri', type=RecipientUri, required=False, test_equal=True) original_recipient_uri = XMLElementChild('original_recipient_uri', type=OriginalRecipientUri, required=False, test_equal=True) subject = XMLElementChild('subject', type=Subject, required=False, test_equal=True) notification = XMLElementChoiceChild('notification', types=(DisplayNotification, DeliveryNotification, ProcessingNotification), required=True, test_equal=True) def __init__(self, message_id=None, datetime=None, recipient_uri=None, original_recipient_uri=None, subject=None, notification=None): XMLRootElement.__init__(self) self.message_id = message_id self.datetime = datetime self.recipient_uri = recipient_uri self.original_recipient_uri = original_recipient_uri if original_recipient_uri is not None else recipient_uri self.subject = subject self.notification = notification ================================================ FILE: sipsimple/payloads/iscomposing.py ================================================ """Parses and produces isComposing messages according to RFC3994.""" __all__ = ['namespace', 'IsComposingDocument', 'State', 'LastActive', 'ContentType', 'Refresh', 'IsComposingMessage'] from sipsimple.payloads import XMLDocument, XMLRootElement, XMLStringElement, XMLPositiveIntegerElement, XMLDateTimeElement, XMLElementChild namespace = 'urn:ietf:params:xml:ns:im-iscomposing' class IsComposingDocument(XMLDocument): content_type = "application/im-iscomposing+xml" IsComposingDocument.register_namespace(namespace, prefix=None, schema='im-iscomposing.xsd') # Attribute value types class StateValue(str): def __new__(cls, value): if value not in ('active', 'idle'): value = 'idle' return str.__new__(cls, value) # Elements class State(XMLStringElement): _xml_tag = 'state' _xml_namespace = namespace _xml_document = IsComposingDocument _xml_value_type = StateValue class LastActive(XMLDateTimeElement): _xml_tag = 'lastactive' _xml_namespace = namespace _xml_document = IsComposingDocument class ContentType(XMLStringElement): _xml_tag = 'contenttype' _xml_namespace = namespace _xml_document = IsComposingDocument class Refresh(XMLPositiveIntegerElement): _xml_tag = 'refresh' _xml_namespace = namespace _xml_document = IsComposingDocument class IsComposingMessage(XMLRootElement): _xml_tag = 'isComposing' _xml_namespace = namespace _xml_document = IsComposingDocument _xml_children_order = {State.qname: 0, LastActive.qname: 1, ContentType.qname: 2, Refresh.qname: 3, None: 4} state = XMLElementChild('state', type=State, required=True, test_equal=True) last_active = XMLElementChild('last_active', type=LastActive, required=False, test_equal=True) content_type = XMLElementChild('content_type', type=ContentType, required=False, test_equal=True) refresh = XMLElementChild('refresh', type=Refresh, required=False, test_equal=True) def __init__(self, state=None, last_active=None, content_type=None, refresh=None): XMLRootElement.__init__(self) self.state = state self.last_active = last_active self.content_type = content_type self.refresh = refresh ================================================ FILE: sipsimple/payloads/messagesummary.py ================================================ """ Message summary and Message Waiting Indication handling according to RFC3842 """ import re from cStringIO import StringIO from application.configuration.datatypes import Boolean from sipsimple.payloads import ValidationError class MessageSummary(object): content_type = "application/simple-message-summary" def __init__(self, messages_waiting=False, message_account=None, summaries=None, optional_headers=None): self.messages_waiting = messages_waiting self.message_account = message_account self.summaries = summaries if summaries is not None else {} self.optional_headers = optional_headers if optional_headers is not None else [] @classmethod def parse(cls, content): message = StringIO(content) summary = cls() tmp_headers = [] for line in message: if line == '\r\n': if tmp_headers: summary.optional_headers.append(tmp_headers) tmp_headers = [] else: field, sep, rest = line.partition(':') if not field and not rest: raise ValidationError("incorrect line format") field = field.strip() rest = rest.strip() if field.lower() == "messages-waiting": summary.messages_waiting = Boolean(rest) elif field.lower() == "message-account": summary.message_account = rest elif field.lower() in {"voice-message", "fax-message", "pager-message", "multimedia-message", "text-message", "none"}: m = re.match("((\d+)/(\d+))( \((\d+)/(\d+)\))?", rest) if m: summary.summaries[field.lower()] = dict(new_messages=m.groups()[1], old_messages=m.groups()[2], new_urgent_messages=m.groups()[4] or 0, old_urgent_messages=m.groups()[5] or 0) else: raise ValidationError("invalid message context class") else: tmp_headers.append(line.strip()) if tmp_headers: summary.optional_headers.append(tmp_headers) tmp_headers = [] return summary def to_string(self): data = "Messages-Waiting: %s\r\n" % 'yes' if self.messages_waiting else 'no' if self.message_account: data += "Message-Account: %s\r\n" % self.message_account if self.summaries: for k, v in self.summaries.iteritems(): data += "%s: %s/%s (%s/%s)\r\n" % (k.title(), v['new_messages'], v['old_messages'], v['new_urgent_messages'], v['old_urgent_messages']) if self.optional_headers: data += "\r\n" for headers in self.optional_headers: for h in headers: data += "%s\r\n" % h data += "\r\n" return data ================================================ FILE: sipsimple/payloads/omapolicy.py ================================================ """ Conditions extension handling according to OMA-TS-Presence_SIMPLE_XDM-V1_1 This module provides an extension to RFC4745 (Common Policy) to support condition extensions defined by OMA. """ __all__ = ['namespace', 'OtherIdentity', 'ExternalList', 'AnonymousRequest'] from sipsimple.payloads import XMLElement, XMLEmptyElement, XMLListElement, XMLElementID from sipsimple.payloads.datatypes import AnyURI from sipsimple.payloads.commonpolicy import ConditionElement from sipsimple.payloads.presrules import PresRulesDocument namespace = 'urn:oma:xml:xdm:common-policy' PresRulesDocument.register_namespace(namespace, prefix='ocp', schema='oma-common-policy.xsd') class OtherIdentity(XMLEmptyElement, ConditionElement): _xml_tag = 'other-identity' _xml_namespace = namespace _xml_document = PresRulesDocument class Entry(XMLElement): _xml_tag = 'entry' _xml_namespace = namespace _xml_document = PresRulesDocument uri = XMLElementID('uri', xmlname='anc', type=AnyURI, required=True, test_equal=True) def __init__(self, uri): XMLElement.__init__(self) self.uri = uri def __unicode__(self): return self.uri def __str__(self): return str(self.uri) class ExternalList(XMLListElement, ConditionElement): _xml_tag = 'external-list' _xml_namespace = namespace _xml_document = PresRulesDocument _xml_item_type = Entry def __init__(self, entries=[]): XMLListElement.__init__(self) self.update(entries) def __iter__(self): return (unicode(item) for item in super(ExternalList, self).__iter__()) def __repr__(self): return '%s(%r)' % (self.__class__.__name__, list(self)) def add(self, item): if isinstance(item, basestring): item = Entry(item) super(ExternalList, self).add(item) def remove(self, item): if isinstance(item, basestring): try: item = (entry for entry in super(ExternalList, self).__iter__() if entry == item).next() except StopIteration: raise KeyError(item) super(ExternalList, self).remove(item) class AnonymousRequest(XMLEmptyElement, ConditionElement): _xml_tag = 'anonymous-request' _xml_namespace = namespace _xml_document = PresRulesDocument ================================================ FILE: sipsimple/payloads/pidf.py ================================================ """PIDF handling according to RFC3863 and RFC4479""" __all__ = ['pidf_namespace', 'dm_namespace', 'PIDFDocument', 'ServiceExtension', 'DeviceExtension', 'PersonExtension', 'StatusExtension', 'Note', 'DeviceID', 'Status', 'Basic', 'Contact', 'ServiceTimestamp', 'Service', 'DeviceTimestamp', 'Device', 'PersonTimestamp', 'Person', 'PIDF', # Extensions 'ExtendedStatus', 'StatusType', 'DeviceInfo'] from itertools import izip from application.python.weakref import weakobjectmap from sipsimple.payloads import ValidationError, XMLDocument, XMLListRootElement, XMLListElement, XMLElement, XMLAttribute, XMLElementID, XMLElementChild from sipsimple.payloads import XMLStringElement, XMLLocalizedStringElement, XMLDateTimeElement, XMLAnyURIElement from sipsimple.payloads.datatypes import AnyURI, ID, DateTime pidf_namespace = 'urn:ietf:params:xml:ns:pidf' dm_namespace = 'urn:ietf:params:xml:ns:pidf:data-model' class PIDFDocument(XMLDocument): content_type = 'application/pidf+xml' PIDFDocument.register_namespace(pidf_namespace, prefix=None, schema='pidf.xsd') PIDFDocument.register_namespace(dm_namespace, prefix='dm', schema='data-model.xsd') ## Marker mixin class ServiceExtension(object): pass class ServiceItemExtension(object): pass class DeviceExtension(object): pass class PersonExtension(object): pass class StatusExtension(object): pass ## Attribute value types class BasicStatusValue(str): def __new__(cls, value): if value not in ('closed', 'open'): raise ValueError('illegal BasicStatusValue') return str.__new__(cls, value) ## General elements class Note(unicode): def __new__(cls, value, lang=None): instance = unicode.__new__(cls, value) instance.lang = lang return instance def __repr__(self): return "%s(%s, lang=%r)" % (self.__class__.__name__, unicode.__repr__(self), self.lang) def __eq__(self, other): if isinstance(other, Note): return unicode.__eq__(self, other) and self.lang == other.lang elif isinstance(other, basestring): return self.lang is None and unicode.__eq__(self, other) else: return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal class PIDFNote(XMLLocalizedStringElement): _xml_tag = 'note' _xml_namespace = pidf_namespace _xml_document = PIDFDocument def __unicode__(self): return Note(self.value, self.lang) class DMNote(XMLLocalizedStringElement): _xml_tag = 'note' _xml_namespace = dm_namespace _xml_document = PIDFDocument def __unicode__(self): return Note(self.value, self.lang) class NoteMap(object): """Descriptor to be used for _note_map attributes on XML elements with notes""" def __init__(self): self.object_map = weakobjectmap() def __get__(self, obj, type): if obj is None: return self try: return self.object_map[obj] except KeyError: return self.object_map.setdefault(obj, {}) def __set__(self, obj, value): raise AttributeError("cannot set attribute") def __delete__(self, obj): raise AttributeError("cannot delete attribute") class NoteList(object): def __init__(self, xml_element, note_type): self.xml_element = xml_element self.note_type = note_type def __contains__(self, item): if isinstance(item, Note): item = self.note_type(item, item.lang) elif isinstance(item, basestring): item = self.note_type(item) return item in self.xml_element._note_map.itervalues() def __iter__(self): return (unicode(self.xml_element._note_map[element]) for element in self.xml_element.element if element in self.xml_element._note_map) def __len__(self): return len(self.xml_element._note_map) def __eq__(self, other): if isinstance(other, NoteList): return self is other or (len(self) == len(other) and all(self_item == other_item for self_item, other_item in izip(self, other))) else: return NotImplemented def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal def _parse_element(self, element): self.xml_element._note_map.clear() for child in element: if child.tag == self.note_type.qname: try: note = self.note_type.from_element(child, xml_document=self.xml_element._xml_document) except ValidationError: pass else: self.xml_element._note_map[note.element] = note def _build_element(self): for note in self.xml_element._note_map.itervalues(): note.to_element() def add(self, item): if isinstance(item, Note): item = self.note_type(item, item.lang) elif isinstance(item, basestring): item = self.note_type(item) if type(item) is not self.note_type: raise TypeError("%s cannot add notes of type %s" % (self.xml_element.__class__.__name__, item.__class__.__name__)) self.xml_element._insert_element(item.element) self.xml_element._note_map[item.element] = item self.xml_element.__dirty__ = True def remove(self, item): if isinstance(item, Note): try: item = (entry for entry in self.xml_element._note_map.itervalues() if unicode(entry) == item).next() except StopIteration: raise KeyError(item) elif isinstance(item, basestring): try: item = (entry for entry in self.xml_element._note_map.itervalues() if entry == item).next() except StopIteration: raise KeyError(item) if type(item) is not self.note_type: raise KeyError(item) self.xml_element.element.remove(item.element) del self.xml_element._note_map[item.element] self.xml_element.__dirty__ = True def update(self, sequence): for item in sequence: self.add(item) def clear(self): for item in self.xml_element._note_map.values(): self.remove(item) class DeviceID(XMLStringElement): _xml_tag = 'deviceID' _xml_namespace = dm_namespace _xml_document = PIDFDocument ## Service elements class Basic(XMLStringElement): _xml_tag = 'basic' _xml_namespace = pidf_namespace _xml_document = PIDFDocument _xml_value_type = BasicStatusValue class Status(XMLElement): _xml_tag = 'status' _xml_namespace = pidf_namespace _xml_document = PIDFDocument _xml_extension_type = StatusExtension _xml_children_order = {Basic.qname: 0} basic = XMLElementChild('basic', type=Basic, required=False, test_equal=True) def __init__(self, basic=None): XMLElement.__init__(self) self.basic = basic def check_validity(self): if len(self.element) == 0: raise ValidationError("Status objects must have at least one child") super(Status, self).check_validity() def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.basic) class Contact(XMLAnyURIElement): _xml_tag = 'contact' _xml_namespace = pidf_namespace _xml_document = PIDFDocument priority = XMLAttribute('priority', type=float, required=False, test_equal=False) class ServiceTimestamp(XMLDateTimeElement): _xml_tag = 'timestamp' _xml_namespace = pidf_namespace _xml_document = PIDFDocument class Service(XMLListElement): _xml_tag = 'tuple' _xml_namespace = pidf_namespace _xml_document = PIDFDocument _xml_extension_type = ServiceExtension _xml_item_type = (DeviceID, ServiceItemExtension) _xml_children_order = {Status.qname: 0, None: 1, Contact.qname: 2, PIDFNote.qname: 3, ServiceTimestamp.qname: 4} id = XMLElementID('id', type=ID, required=True, test_equal=True) status = XMLElementChild('status', type=Status, required=True, test_equal=True) contact = XMLElementChild('contact', type=Contact, required=False, test_equal=True) timestamp = XMLElementChild('timestamp', type=ServiceTimestamp, required=False, test_equal=True) _note_map = NoteMap() def __init__(self, id, notes=[], status=None, contact=None, timestamp=None): XMLListElement.__init__(self) self.id = id self.status = status self.contact = contact self.timestamp = timestamp self.notes.update(notes) @property def notes(self): return NoteList(self, PIDFNote) def __eq__(self, other): if isinstance(other, Service): return super(Service, self).__eq__(other) and self.notes == other.notes else: return self.id == other def __repr__(self): return '%s(%r, %r, %r, %r, %r)' % (self.__class__.__name__, self.id, list(self.notes), self.status, self.contact, self.timestamp) def _parse_element(self, element): super(Service, self)._parse_element(element) self.notes._parse_element(element) def _build_element(self): super(Service, self)._build_element() self.notes._build_element() class DeviceTimestamp(XMLDateTimeElement): _xml_tag = 'timestamp' _xml_namespace = dm_namespace _xml_document = PIDFDocument class Device(XMLElement): _xml_tag = 'device' _xml_namespace = dm_namespace _xml_document = PIDFDocument _xml_extension_type = DeviceExtension _xml_children_order = {None: 0, DeviceID.qname: 1, DMNote.qname: 2, DeviceTimestamp.qname: 3} id = XMLElementID('id', type=ID, required=True, test_equal=True) device_id = XMLElementChild('device_id', type=DeviceID, required=False, test_equal=True) timestamp = XMLElementChild('timestamp', type=DeviceTimestamp, required=False, test_equal=True) _note_map = NoteMap() def __init__(self, id, device_id=None, notes=[], timestamp=None): XMLElement.__init__(self) self.id = id self.device_id = device_id self.timestamp = timestamp self.notes.update(notes) @property def notes(self): return NoteList(self, DMNote) def __eq__(self, other): if isinstance(other, Device): return super(Device, self).__eq__(other) and self.notes == other.notes else: return self.id == other def __repr__(self): return '%s(%r, %r, %r, %r)' % (self.__class__.__name__, self.id, self.device_id, list(self.notes), self.timestamp) def _parse_element(self, element): super(Device, self)._parse_element(element) self.notes._parse_element(element) def _build_element(self): super(Device, self)._build_element() self.notes._build_element() class PersonTimestamp(XMLDateTimeElement): _xml_tag = 'timestamp' _xml_namespace = dm_namespace _xml_document = PIDFDocument class Person(XMLElement): _xml_tag = 'person' _xml_namespace = dm_namespace _xml_document = PIDFDocument _xml_extension_type = PersonExtension _xml_children_order = {None: 0, DMNote.qname: 1, PersonTimestamp.qname: 2} id = XMLElementID('id', type=ID, required=True, test_equal=True) timestamp = XMLElementChild('timestamp', type=PersonTimestamp, required=False, test_equal=True) _note_map = NoteMap() def __init__(self, id, notes=[], timestamp=None): XMLElement.__init__(self) self.id = id self.timestamp = timestamp self.notes.update(notes) @property def notes(self): return NoteList(self, DMNote) def __eq__(self, other): if isinstance(other, Person): return super(Person, self).__eq__(other) and self.notes == other.notes else: return self.id == other def __repr__(self): return '%s(%r, %r, %r)' % (self.__class__.__name__, self.id, list(self.notes), self.timestamp) def _parse_element(self, element): super(Person, self)._parse_element(element) self.notes._parse_element(element) def _build_element(self): super(Person, self)._build_element() self.notes._build_element() class PIDF(XMLListRootElement): _xml_tag = 'presence' _xml_namespace = pidf_namespace _xml_document = PIDFDocument _xml_children_order = {Service.qname: 0, PIDFNote.qname: 1, Person.qname: 2, Device.qname: 3} _xml_item_type = (Service, PIDFNote, Person, Device) entity = XMLAttribute('entity', type=AnyURI, required=True, test_equal=True) services = property(lambda self: (item for item in self if type(item) is Service)) notes = property(lambda self: (item for item in self if type(item) is Note)) persons = property(lambda self: (item for item in self if type(item) is Person)) devices = property(lambda self: (item for item in self if type(item) is Device)) def __init__(self, entity, elements=[]): XMLListRootElement.__init__(self) self.entity = entity self.update(elements) def __contains__(self, item): if isinstance(item, Note): item = PIDFNote(item, item.lang) return super(PIDF, self).__contains__(item) def __iter__(self): return (unicode(item) if type(item) is PIDFNote else item for item in super(PIDF, self).__iter__()) def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.entity, list(self)) def add(self, item): if isinstance(item, Note): item = PIDFNote(item, item.lang) super(PIDF, self).add(item) def remove(self, item): if isinstance(item, Note): try: item = (entry for entry in super(PIDF, self).__iter__() if type(entry) is PIDFNote and unicode(entry) == item).next() except StopIteration: raise KeyError(item) super(PIDF, self).remove(item) # # Extensions # agp_pidf_namespace = 'urn:ag-projects:xml:ns:pidf' PIDFDocument.register_namespace(agp_pidf_namespace, prefix='agp-pidf') class ExtendedStatusValue(str): def __new__(cls, value): if value not in ('available', 'offline', 'away', 'busy'): raise ValueError("illegal value for extended status") return str.__new__(cls, value) class ExtendedStatus(XMLStringElement, StatusExtension): _xml_tag = 'extended' _xml_namespace = agp_pidf_namespace _xml_document = PIDFDocument _xml_value_type = ExtendedStatusValue class StatusType(XMLStringElement, StatusExtension): _xml_tag = 'type' _xml_namespace = agp_pidf_namespace _xml_document = PIDFDocument Status.register_extension('extended', type=ExtendedStatus) Status.register_extension('type', type=StatusType) class Description(XMLStringElement): _xml_tag = 'description' _xml_namespace = agp_pidf_namespace _xml_document = PIDFDocument class UserAgent(XMLStringElement): _xml_tag = 'user-agent' _xml_namespace = agp_pidf_namespace _xml_document = PIDFDocument class TimeOffset(XMLStringElement): _xml_tag = 'time-offset' _xml_namespace = agp_pidf_namespace _xml_document = PIDFDocument description = XMLAttribute('description', type=unicode, required=False, test_equal=True) def __init__(self, value=None, description=None): if value is None: value = DateTime.now().utcoffset().seconds / 60 XMLStringElement.__init__(self, str(value)) self.description = description def __int__(self): return int(self.value) class DeviceInfo(XMLElement, ServiceExtension): _xml_tag = 'device-info' _xml_namespace = agp_pidf_namespace _xml_document = PIDFDocument _xml_children_order = {Description.qname: 0, UserAgent.qname: 1} id = XMLElementID('id', type=str, required=True, test_equal=True) description = XMLElementChild('description', type=Description, required=False, test_equal=True) user_agent = XMLElementChild('user_agent', type=UserAgent, required=False, test_equal=True) time_offset = XMLElementChild('time_offset', type=TimeOffset, required=False, test_equal=True) def __init__(self, id, description=None, user_agent=None, time_offset=None): XMLElement.__init__(self) self.id = id self.description = description self.user_agent = user_agent self.time_offset = time_offset def __repr__(self): return '%s(%r, %r, %r, %r)' % (self.__class__.__name__, self.id, self.description, self.user_agent, self.time_offset) Service.register_extension('device_info', type=DeviceInfo) ================================================ FILE: sipsimple/payloads/prescontent.py ================================================ """Generates presence content application documents according to OMA TS Presence SIMPLE Content""" __all__ = ['namespace', 'PresenceContentDocument', 'MIMEType', 'Encoding', 'Description', 'Data', 'PresenceContent'] from sipsimple.payloads import XMLDocument, XMLRootElement, XMLStringElement, XMLLocalizedStringElement, XMLElementChild namespace = 'urn:oma:xml:prs:pres-content' class PresenceContentDocument(XMLDocument): content_type = "application/vnd.oma.pres-content+xml" PresenceContentDocument.register_namespace(namespace, prefix=None, schema='oma-pres-content.xsd') # Elements class MIMEType(XMLStringElement): _xml_tag = 'mime-type' _xml_namespace = namespace _xml_document = PresenceContentDocument class Encoding(XMLStringElement): _xml_tag = 'encoding' _xml_namespace = namespace _xml_document = PresenceContentDocument class Description(XMLLocalizedStringElement): _xml_tag = 'description' _xml_namespace = namespace _xml_document = PresenceContentDocument class Data(XMLStringElement): _xml_tag = 'data' _xml_namespace = namespace _xml_document = PresenceContentDocument class PresenceContent(XMLRootElement): _xml_tag = 'content' _xml_namespace = namespace _xml_document = PresenceContentDocument _xml_children_order = {MIMEType.qname: 0, Encoding.qname: 1, Description.qname: 2, Data.qname: 3, None: 4} mime_type = XMLElementChild('mime_type', type=MIMEType, required=False, test_equal=True) encoding = XMLElementChild('encoding', type=Encoding, required=False, test_equal=True) description = XMLElementChild('description', type=Description, required=False, test_equal=True) data = XMLElementChild('data', type=Data, required=True, test_equal=True) def __init__(self, data, mime_type=None, encoding=None, description=None): XMLRootElement.__init__(self) self.data = data self.mime_type = mime_type self.encoding = encoding self.description = description ================================================ FILE: sipsimple/payloads/presrules.py ================================================ """Parses and produces Presence Authorization Rules documents according to RFC5025.""" __all__ = ['namespace', 'PresRulesDocument', 'SubHandling', 'DeviceID', 'Class', 'All', 'ProvideDevices', 'OccurenceID', 'ProvidePersons', 'ServiceURI', 'ServiceURIScheme', 'ProvideServices', 'ProvideActivities', 'ProvideClass', 'ProvideDeviceID', 'ProvideMood', 'ProvidePlaceIs', 'ProvidePlaceType', 'ProvidePrivacy', 'ProvideRelationship', 'ProvideStatusIcon', 'ProvideSphere', 'ProvideTimeOffset', 'ProvideUserInput', 'ProvideUnknownAttribute', 'ProvideAllAttributes', 'PresRules'] from sipsimple.payloads import XMLListElement, XMLStringElement, XMLBooleanElement, XMLEmptyElement, XMLAttribute from sipsimple.payloads import commonpolicy from sipsimple.util import All namespace = 'urn:ietf:params:xml:ns:pres-rules' class PresRulesDocument(commonpolicy.CommonPolicyDocument): pass PresRulesDocument.register_namespace(namespace, prefix='pr', schema='pres-rules.xsd') ## Marker mixins class ProvideDeviceElement(object): pass class ProvidePersonElement(object): pass class ProvideServiceElement(object): pass ## Attribute value types class SubHandlingValue(str): __prioritymap__ = {'block': 0, 'confirm': 10, 'polite-block': 20, 'allow': 30} def __new__(cls, value): if value not in cls.__prioritymap__: raise ValueError("illegal value for SubHandling element") return str.__new__(cls, value) @property def priority(self): return self.__prioritymap__[self] class ProvideUserInputValue(str): def __new__(cls, value): if value not in ('false', 'bare', 'thresholds', 'full'): raise ValueError("illegal value for ProvideUserInput element") return str.__new__(cls, value) ## Action Elements class SubHandling(XMLStringElement, commonpolicy.ActionElement): _xml_tag = 'sub-handling' _xml_namespace = namespace _xml_document = PresRulesDocument _xml_value_type = SubHandlingValue ## Transformation Elements class Class(XMLStringElement): _xml_tag = 'class' _xml_namespace = namespace _xml_document = PresRulesDocument class OccurenceID(XMLStringElement): _xml_tag = 'occurence-id' _xml_namespace = namespace _xml_document = PresRulesDocument ## Devices element class DeviceID(XMLStringElement): _xml_tag = 'deviceID' _xml_namespace = namespace _xml_document = PresRulesDocument class AllDevices(XMLEmptyElement): _xml_tag = 'all-devices' _xml_namespace = namespace _xml_document = PresRulesDocument class ProvideDevices(XMLListElement, commonpolicy.TransformationElement): _xml_tag = 'provide-devices' _xml_namespace = namespace _xml_document = PresRulesDocument _xml_item_type = (DeviceID, OccurenceID, Class, AllDevices, ProvideDeviceElement) def __init__(self, provides=[]): XMLListElement.__init__(self) self.update(provides) def __contains__(self, item): if item == All: item = AllDevices() return super(ProvideDevices, self).__contains__(item) def __iter__(self): return (All if type(item) is AllDevices else item for item in super(ProvideDevices, self).__iter__()) def add(self, item): if item == All: item = AllDevices() if type(item) is AllDevices: self.clear() else: try: self.remove(All) except KeyError: pass super(ProvideDevices, self).add(item) def remove(self, item): if item == All: try: item = (item for item in super(ProvideDevices, self).__iter__() if type(item) is AllDevices).next() except StopIteration: raise KeyError(item) super(ProvideDevices, self).remove(item) ## Persons elmeent class AllPersons(XMLEmptyElement): _xml_tag = 'all-persons' _xml_namespace = namespace _xml_document = PresRulesDocument class ProvidePersons(XMLListElement, commonpolicy.TransformationElement): _xml_tag = 'provide-persons' _xml_namespace = namespace _xml_document = PresRulesDocument _xml_item_type = (OccurenceID, Class, AllPersons, ProvidePersonElement) def __init__(self, provides=[]): XMLListElement.__init__(self) self.update(provides) def __contains__(self, item): if item == All: item = AllPersons() return super(ProvidePersons, self).__contains__(item) def __iter__(self): return (All if type(item) is AllPersons else item for item in super(ProvidePersons, self).__iter__()) def add(self, item): if item == All: item = AllPersons() if type(item) is AllPersons: self.clear() else: try: self.remove(All) except KeyError: pass super(ProvidePersons, self).add(item) def remove(self, item): if item == All: try: item = (item for item in super(ProvidePersons, self).__iter__() if type(item) is AllPersons).next() except StopIteration: raise KeyError(item) super(ProvidePersons, self).remove(item) ## Service elements class ServiceURI(XMLStringElement): _xml_tag = 'service-uri' _xml_namespace = namespace _xml_document = PresRulesDocument class ServiceURIScheme(XMLStringElement): _xml_tag = 'service-uri-scheme' _xml_namespace = namespace _xml_document = PresRulesDocument class AllServices(XMLEmptyElement): _xml_tag = 'all-services' _xml_namespace = namespace _xml_document = PresRulesDocument class ProvideServices(XMLListElement, commonpolicy.TransformationElement): _xml_tag = 'provide-services' _xml_namespace = namespace _xml_document = PresRulesDocument _xml_item_type = (ServiceURI, ServiceURIScheme, OccurenceID, Class, AllServices, ProvideServiceElement) def __init__(self, provides=[]): XMLListElement.__init__(self) self.update(provides) def __contains__(self, item): if item == All: item = AllServices() return super(ProvideServices, self).__contains__(item) def __iter__(self): return (All if type(item) is AllServices else item for item in super(ProvideServices, self).__iter__()) def add(self, item): if item == All: item = AllServices() if type(item) is AllServices: self.clear() else: try: self.remove(All) except KeyError: pass super(ProvideServices, self).add(item) def remove(self, item): if item == All: try: item = (item for item in super(ProvideServices, self).__iter__() if type(item) is AllServices).next() except StopIteration: raise KeyError(item) super(ProvideServices, self).remove(item) ## Transformation elements class ProvideActivities(XMLBooleanElement, commonpolicy.TransformationElement): _xml_tag = 'provide-activities' _xml_namespace = namespace _xml_document = PresRulesDocument class ProvideClass(XMLBooleanElement, commonpolicy.TransformationElement): _xml_tag = 'provide-class' _xml_namespace = namespace _xml_document = PresRulesDocument class ProvideDeviceID(XMLBooleanElement, commonpolicy.TransformationElement): _xml_tag = 'provide-deviceID' _xml_namespace = namespace _xml_document = PresRulesDocument class ProvideMood(XMLBooleanElement, commonpolicy.TransformationElement): _xml_tag = 'provide-mood' _xml_namespace = namespace _xml_document = PresRulesDocument class ProvidePlaceIs(XMLBooleanElement, commonpolicy.TransformationElement): _xml_tag = 'provide-place-is' _xml_namespace = namespace _xml_document = PresRulesDocument class ProvidePlaceType(XMLBooleanElement, commonpolicy.TransformationElement): _xml_tag = 'provide-place-type' _xml_namespace = namespace _xml_document = PresRulesDocument class ProvidePrivacy(XMLBooleanElement, commonpolicy.TransformationElement): _xml_tag = 'provide-privacy' _xml_namespace = namespace _xml_document = PresRulesDocument class ProvideRelationship(XMLBooleanElement, commonpolicy.TransformationElement): _xml_tag = 'provide-relationship' _xml_namespace = namespace _xml_document = PresRulesDocument class ProvideStatusIcon(XMLBooleanElement, commonpolicy.TransformationElement): _xml_tag = 'provide-status-icon' _xml_namespace = namespace _xml_document = PresRulesDocument class ProvideSphere(XMLBooleanElement, commonpolicy.TransformationElement): _xml_tag = 'provide-sphere' _xml_namespace = namespace _xml_document = PresRulesDocument class ProvideTimeOffset(XMLBooleanElement, commonpolicy.TransformationElement): _xml_tag = 'provide-time-offset' _xml_namespace = namespace _xml_document = PresRulesDocument class ProvideUserInput(XMLStringElement, commonpolicy.TransformationElement): _xml_tag = 'provide-user-input' _xml_namespace = namespace _xml_document = PresRulesDocument _xml_value_type = ProvideUserInputValue class ProvideUnknownAttribute(XMLBooleanElement, commonpolicy.TransformationElement): _xml_tag = 'provide-unknown-attribute' _xml_namespace = namespace _xml_document = PresRulesDocument name = XMLAttribute('name', type=str, required=True, test_equal=True) ns = XMLAttribute('ns', type=str, required=True, test_equal=True) def __init__(self, ns, name, value): XMLBooleanElement.__init__(self, value) self.ns = ns self.name = name def __repr__(self): return '%s(%r, %r, %r)' % (self.__class__.__name__, self.ns, self.name, self.value) class ProvideAllAttributes(XMLEmptyElement, commonpolicy.TransformationElement): _xml_tag = 'provide-all-attributes' _xml_namespace = namespace _xml_document = PresRulesDocument class PresRules(commonpolicy.RuleSet): _xml_document = PresRulesDocument ================================================ FILE: sipsimple/payloads/resourcelists.py ================================================ """Resource lists (rfc4826) handling""" __all__ = ['namespace', 'ResourceListsDocument', 'DisplayName', 'Entry', 'EntryRef', 'External', 'List', 'ResourceLists', # Extensions 'EntryAttributes'] from collections import deque from lxml import etree from xml.sax.saxutils import quoteattr from application.python import Null from sipsimple.payloads import XMLDocument, XMLListRootElement, XMLElement, XMLListElement, XMLLocalizedStringElement, XMLElementID, XMLElementChild, ThisClass from sipsimple.payloads import IterateIDs, IterateItems, All from sipsimple.payloads.datatypes import AnyURI namespace = 'urn:ietf:params:xml:ns:resource-lists' # excerpt from the RFC: # # attribute "name" - optional, unique among the same level # body: optional , the sequence of entry/list/entry-ref/external # # attribute xml:lang - optional # body: utf8 string # # attribute "uri" - mandatory, unique among all other within the same parent # body: optional # # attribute "ref" - mandatory, unique among all other within the same parent # body: optional # ref is a relative URI that resolves into # # attribute "anchor" - mandatory, unique among all other anchor in within the same parent # anchor must be an absolute http uri that resolves into class ResourceListsDocument(XMLDocument): content_type = 'application/resource-lists+xml' ResourceListsDocument.register_namespace(namespace, prefix='rl', schema='resourcelists.xsd') ## Marker mixins class ListElement(object): pass class EntryExtension(object): pass ## Elements class DisplayName(XMLLocalizedStringElement): _xml_tag = 'display-name' _xml_namespace = namespace _xml_document = ResourceListsDocument class Entry(XMLElement): _xml_tag = 'entry' _xml_namespace = namespace _xml_extension_type = EntryExtension _xml_document = ResourceListsDocument _xml_children_order = {DisplayName.qname: 0} uri = XMLElementID('uri', type=AnyURI, required=True, test_equal=True) display_name = XMLElementChild('display_name', type=DisplayName, required=False, test_equal=False) def __init__(self, uri, display_name=None): XMLElement.__init__(self) self.uri = uri self.display_name = display_name def __unicode__(self): return self.display_name and u'"%s" <%s>' % (self.display_name, self.uri) or self.uri def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.uri, self.display_name) class EntryRef(XMLElement): _xml_tag = 'entry-ref' _xml_namespace = namespace _xml_document = ResourceListsDocument _xml_children_order = {DisplayName.qname: 0} ref = XMLElementID('ref', type=AnyURI, required=True, test_equal=True) display_name = XMLElementChild('display_name', type=DisplayName, required=False, test_equal=False) def __init__(self, ref, display_name=None): XMLElement.__init__(self) self.ref = ref self.display_name = display_name def __unicode__(self): return self.display_name and '"%s" <%s>' % (self.display_name, self.ref) or self.ref def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.ref, self.display_name) class External(XMLElement): _xml_tag = 'external' _xml_namespace = namespace _xml_document = ResourceListsDocument _xml_children_order = {DisplayName.qname: 0} anchor = XMLElementID('anchor', type=AnyURI, required=True, test_equal=True) display_name = XMLElementChild('display_name', type=DisplayName, required=False, test_equal=False) def __init__(self, anchor, display_name=None): XMLElement.__init__(self) self.anchor = anchor self.display_name = display_name def __unicode__(self): return self.display_name and '"%s" <%s>' % (self.display_name, self.anchor) or self.anchor def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.anchor, self.display_name) List = ThisClass # a List can contain items of its own kind class List(XMLListElement): _xml_tag = 'list' _xml_namespace = namespace _xml_document = ResourceListsDocument _xml_children_order = {DisplayName.qname: 0, Entry.qname: 1, EntryRef.qname: 1, External.qname: 1} _xml_item_type = (Entry, EntryRef, External, List, ListElement) name = XMLElementID('name', type=unicode, required=False, test_equal=True) display_name = XMLElementChild('display_name', type=DisplayName, required=False, test_equal=False) def __init__(self, entries=[], name=None, display_name=None): XMLListElement.__init__(self) self.name = name self.display_name = display_name self.update(entries) def __repr__(self): return '%s(%s, %r, %r)' % (self.__class__.__name__, list(self), self.name, self.display_name) def __unicode__(self): name = u'List element' if self.name is not None: name += u' %s' % self.name if self.display_name is not None: name += u' (%s)' % self.display_name return name List._xml_children_order[List.qname] = 1 # cannot self reference in declaration class ResourceLists(XMLListRootElement): _xml_tag = 'resource-lists' _xml_namespace = namespace _xml_document = ResourceListsDocument _xml_children_order = {List.qname: 0} _xml_item_type = List def __init__(self, lists=[]): XMLListRootElement.__init__(self) self.update(lists) def __getitem__(self, key): if key is IterateIDs: return self._xmlid_map[List].iterkeys() elif key is IterateItems: return self._xmlid_map[List].itervalues() else: return self._xmlid_map[List][key] def __delitem__(self, key): if key is All: for item in self._xmlid_map[List].values(): self.remove(item) else: self.remove(self._xmlid_map[List][key]) def get(self, key, default=None): return self._xmlid_map[List].get(key, default) def get_xpath(self, element): if not isinstance(element, (List, Entry, EntryRef, External, ResourceLists)): raise ValueError('can only find xpath for List, Entry, EntryRef or External elements') nsmap = dict((namespace, prefix) for prefix, namespace in self._xml_document.nsmap.iteritems()) nsmap[self._xml_namespace] = None xpath_nsmap = {} root_xpath = '/' + self._xml_tag if element is self: return root_xpath notexpanded = deque([self]) visited = set(notexpanded) parents = {self: None} obj = None while notexpanded: list = notexpanded.popleft() for child in list: if child is element: parents[child] = list obj = child notexpanded.clear() break elif isinstance(child, List) and child not in visited: parents[child] = list notexpanded.append(child) visited.add(child) if obj is None: return None components = [] while obj is not self: prefix = nsmap[obj._xml_namespace] if prefix: name = '%s:%s' % (prefix, obj._xml_tag) xpath_nsmap[obj._xml_namespace] = prefix else: name = obj._xml_tag if isinstance(obj, List): if obj.name is not None: components.append('/%s[@%s=%s]' % (name, List.name.xmlname, quoteattr(obj.name))) else: siblings = [l for l in parents[obj] if isinstance(l, List)] components.append('/%s[%d]' % (name, siblings.index(obj)+1)) elif isinstance(obj, Entry): components.append('/%s[@%s=%s]' % (name, Entry.uri.xmlname, quoteattr(obj.uri))) elif isinstance(obj, EntryRef): components.append('/%s[@%s=%s]' % (name, EntryRef.ref.xmlname, quoteattr(obj.ref))) elif isinstance(obj, External): components.append('/%s[@%s=%s]' % (name, External.anchor.xmlname, quoteattr(obj.anchor))) obj = parents[obj] components.reverse() return root_xpath + ''.join(components) + ('?' + ''.join('xmlns(%s=%s)' % (prefix, namespace) for namespace, prefix in xpath_nsmap.iteritems()) if xpath_nsmap else '') def find_parent(self, element): if not isinstance(element, (List, Entry, EntryRef, External)): raise ValueError('can only find parent for List, Entry, EntryRef or External elements') if element is self: return None notexpanded = deque([self]) visited = set(notexpanded) while notexpanded: list = notexpanded.popleft() for child in list: if child is element: return list elif isinstance(child, List) and child not in visited: notexpanded.append(child) visited.add(child) return None # # Extensions # class EntryAttributes(XMLElement, EntryExtension): _xml_tag = 'attributes' _xml_namespace = 'urn:ag-projects:xml:ns:resource-lists' _xml_document = ResourceListsDocument def __init__(self, iterable=(), **attributes): XMLElement.__init__(self) self._attributes = dict() self.update(iterable, **attributes) def _parse_element(self, element): self._attributes = dict() attribute_tag = '{%s}attribute' % self._xml_namespace for child in (child for child in element if child.tag == attribute_tag): if 'nil' in child.attrib: self[child.attrib['name']] = None else: self[child.attrib['name']] = unicode(child.text or u'') def _build_element(self): self.element.clear() attribute_tag = '{%s}attribute' % self._xml_namespace for key, value in self.iteritems(): child = etree.SubElement(self.element, attribute_tag, nsmap=self._xml_document.nsmap) child.attrib['name'] = key if value is None: child.attrib['nil'] = 'true' else: child.text = value def __contains__(self, key): return key in self._attributes def __iter__(self): return iter(self._attributes) def __len__(self): return len(self._attributes) def __getitem__(self, key): return self._attributes[key] def __setitem__(self, key, value): if self._attributes.get(key, Null) == value: return self._attributes[key] = value self.__dirty__ = True def __delitem__(self, key): del self._attributes[key] self.__dirty__ = True def clear(self): if self._attributes: self._attributes.clear() self.__dirty__ = True def get(self, key, default=None): return self._attributes.get(key, default) def has_key(self, key): return key in self._attributes def items(self): return self._attributes.items() def iteritems(self): return self._attributes.iteritems() def iterkeys(self): return self._attributes.iterkeys() def itervalues(self): return self._attributes.itervalues() def keys(self): return self._attributes.keys() def pop(self, key, *args): value = self._attributes.pop(key, *args) if not args or value is not args[0]: self.__dirty__ = True return value def popitem(self): value = self._attributes.popitem() self.__dirty__ = True return value def setdefault(self, key, default=None): value = self._attributes.setdefault(key, default) if value is default: self.__dirty__ = True return value def update(self, iterable=(), **attributes): self._attributes.update(iterable, **attributes) if iterable or attributes: self.__dirty__ = True ResourceListsDocument.register_namespace(EntryAttributes._xml_namespace, prefix='agp-rl') Entry.register_extension('attributes', EntryAttributes) ================================================ FILE: sipsimple/payloads/rlmi.py ================================================ """RLMI document handling as described in RFC 4662""" __all__ = ['namespace', 'RLMIDocument', 'Name', 'Instance', 'Resource', 'List'] from sipsimple.payloads import XMLDocument, XMLElement, XMLListElement, XMLListRootElement, XMLLocalizedStringElement, XMLElementID, XMLAttribute from sipsimple.payloads.datatypes import AnyURI, Boolean, UnsignedInt namespace = 'urn:ietf:params:xml:ns:rlmi' class RLMIDocument(XMLDocument): content_type = 'application/rlmi+xml' RLMIDocument.register_namespace(namespace, prefix=None, schema='rlmi.xsd') class StateValue(str): def __new__(cls, value): if value not in ('active', 'pending', 'terminated'): raise ValueError("Invalid state value: %s" % value) return str.__new__(cls, value) class Name(XMLLocalizedStringElement): _xml_tag = 'name' _xml_namespace = namespace _xml_document = RLMIDocument class Instance(XMLElement): _xml_tag = 'instance' _xml_namespace = namespace _xml_document = RLMIDocument id = XMLElementID('id', type=str, required=True, test_equal=True) state = XMLAttribute('state', type=StateValue, required=True, test_equal=True) reason = XMLAttribute('reason', type=str, required=False, test_equal=True) cid = XMLAttribute('cid', type=str, required=False, test_equal=True) def __init__(self, id, state, reason=None, cid=None): XMLElement.__init__(self) self.id = id self.state = state self.reason = reason self.cid = cid def __repr__(self): return '%s(%r, state=%r, reason=%r, cid=%r)' % (self.__class__.__name__, self.id, self.state, self.reason, self.cid) class Resource(XMLListElement): _xml_tag = 'resource' _xml_namespace = namespace _xml_document = RLMIDocument _xml_item_type = (Name, Instance) uri = XMLElementID('uri', type=AnyURI, required=True, test_equal=True) def __init__(self, uri, items=[]): XMLElement.__init__(self) self.uri = uri self.update(items) def __repr__(self): return '%s(%r, items=%r)' % (self.__class__.__name__, self.uri, list(self)) class List(XMLListRootElement): _xml_tag = 'list' _xml_namespace = namespace _xml_document = RLMIDocument _xml_item_type = (Name, Resource) uri = XMLElementID('uri', type=AnyURI, required=True, test_equal=True) version = XMLAttribute('version', type=UnsignedInt, required=True, test_equal=True) full_state = XMLAttribute('full_state', xmlname='fullState', type=Boolean, required=True, test_equal=True) cid = XMLAttribute('cid', type=str, required=False, test_equal=True) def __init__(self, uri, version, full_state, cid=None, items=[]): XMLListElement.__init__(self) self.uri = uri self.version = version self.full_state = full_state self.cid = cid self.update(items) def __repr__(self): return '%s(%r, version=%r, full_state=%r, cid=%r, items=%r)' % (self.__class__.__name__, self.uri, self.version, self.full_state, self.cid, list(self)) ================================================ FILE: sipsimple/payloads/rlsnotify.py ================================================ """Payload of the RLS notify messages.""" __all__ = ['RLSNotify'] import email from sipsimple.payloads import IterateItems, ParserError from sipsimple.payloads import rlmi, pidf from sipsimple.payloads import rpid; rpid # needs to be imported to register its namespace class ResourceURI(unicode): def __eq__(self, other): return super(ResourceURI, self).__eq__(other) or self.rpartition('sip:')[2] == other def __ne__(self, other): equal = self.__eq__(other) return NotImplemented if equal is NotImplemented else not equal class Resource(object): __prioritymap__ = dict(active=10, pending=20, terminated=30) def __init__(self, uri, name=None, state=None, reason=None, pidf_list=None): self.uri = ResourceURI(uri) self.name = name self.state = state self.reason = reason self.pidf_list = pidf_list or [] @classmethod def from_payload(cls, xml_element, payload_map): try: name = next(element for element in xml_element if isinstance(element, rlmi.Name)) except StopIteration: name = None instances = list(xml_element[rlmi.Instance, IterateItems]) if len(instances) == 0: state = None reason = None elif len(instances) == 1: instance = instances[0] state = instance.state reason = instance.reason else: instance = sorted(instances, key=lambda item: cls.__prioritymap__[item.state])[0] state = instance.state reason = instance.reason pidf_list = [] for instance in (instance for instance in instances if instance.cid is not None): try: payload = payload_map['<%s>' % instance.cid].get_payload() except KeyError: continue try: pidf_list.append(pidf.PIDFDocument.parse(payload)) except ParserError: pass return cls(xml_element.uri, name, state, reason, pidf_list) class RLSNotify(object): """The payload from RLS notify messages""" content_type = 'multipart/related' def __init__(self, uri, version, full_state, resources): self.uri = ResourceURI(uri) self.version = version self.full_state = full_state self.resources = resources def __iter__(self): return iter(self.resources) def __len__(self): return len(self.resources) @classmethod def parse(cls, payload): message = email.message_from_string(payload) if message.get_content_type() != cls.content_type: raise ParserError("expected multipart/related content, got %s" % message.get_content_type()) payloads = message.get_payload() if len(payloads) == 0: raise ParserError("multipart/related body contains no parts") payload_map = dict((payload['Content-ID'], payload) for payload in payloads if payload['Content-ID'] is not None) root_id = message.get_param('start') root_type = message.get_param('type', '').lower() if root_id is not None: try: root = payload_map[root_id] except KeyError: raise ParserError('cannot find root element') else: root = payloads[0] if root_type != rlmi.RLMIDocument.content_type != root.get_content_type(): raise ParserError("the multipart/related root element must be of type %s" % rlmi.RLMIDocument.content_type) rlmi_document = rlmi.RLMIDocument.parse(root.get_payload()) resources = [Resource.from_payload(xml_element, payload_map) for xml_element in rlmi_document[rlmi.Resource, IterateItems]] return cls(rlmi_document.uri, rlmi_document.version, rlmi_document.full_state, resources) ================================================ FILE: sipsimple/payloads/rlsservices.py ================================================ """RFC4826 compliant parser/builder for application/rls-services+xml documents.""" __all__ = ['namespace', 'RLSServicesDocument', 'Packages', 'ResourceList', 'List', 'Service', 'RLSServices'] from sipsimple.payloads import XMLListRootElement, XMLElement, XMLListElement, XMLStringElement, XMLAnyURIElement, XMLElementID, XMLElementChild, XMLElementChoiceChild from sipsimple.payloads import IterateIDs, IterateItems, All from sipsimple.payloads import resourcelists from sipsimple.payloads.datatypes import AnyURI namespace = 'urn:ietf:params:xml:ns:rls-services' class RLSServicesDocument(resourcelists.ResourceListsDocument): content_type = 'application/rls-services+xml' RLSServicesDocument.register_namespace(namespace, prefix=None, schema='rlsservices.xsd') ## Marker mixins class PackagesElement(object): pass ## Elements class Package(XMLStringElement): _xml_tag = 'package' _xml_namespace = namespace _xml_document = RLSServicesDocument class Packages(XMLListElement): _xml_tag = 'packages' _xml_namespace = namespace _xml_document = RLSServicesDocument _xml_children_order = {Package.qname: 0} _xml_item_type = (Package, PackagesElement) def __init__(self, packages=[]): XMLListElement.__init__(self) self.update(packages) def __iter__(self): return (unicode(item) if type(item) is Package else item for item in super(Packages, self).__iter__()) def add(self, item): if isinstance(item, basestring): item = Package(item) super(Packages, self).add(item) def remove(self, item): if isinstance(item, basestring): package = Package(item) try: item = (entry for entry in super(Packages, self).__iter__() if entry == package).next() except StopIteration: raise KeyError(item) super(Packages, self).remove(item) class ResourceList(XMLAnyURIElement): _xml_tag = 'resource-list' _xml_namespace = namespace _xml_document = RLSServicesDocument # This is identical to the list element in resourcelists, except for the # namespace. We'll redefine the xml tag just for readability purposes. class List(resourcelists.List): _xml_tag = 'list' _xml_namespace = namespace _xml_document = RLSServicesDocument class Service(XMLElement): _xml_tag = 'service' _xml_namespace = namespace _xml_document = RLSServicesDocument _xml_children_order = {List.qname: 0, ResourceList.qname: 0, Packages.qname: 1} uri = XMLElementID('uri', type=AnyURI, required=True, test_equal=True) list = XMLElementChoiceChild('list', types=(ResourceList, List), required=True, test_equal=True) packages = XMLElementChild('packages', type=Packages, required=False, test_equal=True) def __init__(self, uri, list=None, packages=None): XMLElement.__init__(self) self.uri = uri self.list = list if list is not None else List() self.packages = packages if packages is not None else Packages() def __repr__(self): return '%s(%r, %r, %r)' % (self.__class__.__name__, self.uri, self.list, self.packages) class RLSServices(XMLListRootElement): _xml_tag = 'rls-services' _xml_namespace = namespace _xml_document = RLSServicesDocument _xml_children_order = {Service.qname: 0} _xml_item_type = Service def __init__(self, services=[]): XMLListRootElement.__init__(self) self.update(services) def __getitem__(self, key): if key is IterateIDs: return self._xmlid_map[Service].iterkeys() elif key is IterateItems: return self._xmlid_map[Service].itervalues() else: return self._xmlid_map[Service][key] def __delitem__(self, key): if key is All: for item in self._xmlid_map[Service].values(): self.remove(item) else: self.remove(self._xmlid_map[Service][key]) def get(self, key, default=None): return self._xmlid_map[Service].get(key, default) ================================================ FILE: sipsimple/payloads/rpid.py ================================================ """ RPID handling according to RFC4480 This module provides an extension to PIDF to support rich presence. """ __all__ = ['namespace', 'ActivityElement', 'MoodElement', 'PlaceTypeElement', 'PrivacyElement', 'RelationshipElement', 'ServiceClassElement', 'SphereElement', 'Note', 'Other', 'Activities', 'Mood', 'PlaceIs', 'AudioPlaceInformation', 'VideoPlaceInformation', 'TextPlaceInformation', 'PlaceType', 'AudioPrivacy', 'TextPrivacy', 'VideoPrivacy', 'Privacy', 'Relationship', 'ServiceClass', 'Sphere', 'StatusIcon', 'TimeOffset', 'UserInput', 'Class'] from lxml import etree from sipsimple.payloads import ValidationError, XMLElementType, XMLEmptyElementRegistryType, XMLAttribute, XMLElementChild, XMLStringChoiceChild from sipsimple.payloads import XMLElement, XMLEmptyElement, XMLStringElement, XMLLocalizedStringElement, XMLStringListElement from sipsimple.payloads.pidf import PIDFDocument, ServiceExtension, PersonExtension, DeviceExtension, Note, NoteMap, NoteList, Service, Person, Device from sipsimple.payloads.datatypes import UnsignedLong, DateTime, ID namespace = 'urn:ietf:params:xml:ns:pidf:rpid' PIDFDocument.register_namespace(namespace, prefix='rpid', schema='rpid.xsd') ## Marker mixins class ActivityElement(object): pass class MoodElement(object): pass class PlaceTypeElement(object): pass class PrivacyElement(object): pass class RelationshipElement(object): pass class ServiceClassElement(object): pass class SphereElement(object): pass ## Attribute value types class AudioPlaceValue(str): def __new__(cls, value): if value not in ('noisy', 'ok', 'quiet', 'unknown'): raise ValueError("illegal value for audio place-is") return str.__new__(cls, value) class VideoPlaceValue(str): def __new__(cls, value): if value not in ('toobright', 'ok', 'dark', 'unknown'): raise ValueError("illegal value for video place-is") return str.__new__(cls, value) class TextPlaceValue(str): def __new__(cls, value): if value not in ('uncomfortable', 'inappropriate', 'ok', 'unknown'): raise ValueError("illegal value for text place-is") return str.__new__(cls, value) class UserInputValue(str): def __new__(cls, value): if value not in ('active', 'idle'): raise ValueError("illegal value for user-input") return str.__new__(cls, value) ## Elements class RPIDNote(XMLLocalizedStringElement): _xml_tag = 'note' _xml_namespace = namespace _xml_document = PIDFDocument def __unicode__(self): return Note(self.value, self.lang) @classmethod def from_string(cls, value): if isinstance(value, Note): return cls(value, value.lang) elif isinstance(value, basestring): return cls(value) else: raise ValueError("expected str/unicode instance, got %s instead" % value.__class__.__name__) class RPIDOther(XMLLocalizedStringElement): _xml_tag = 'other' _xml_namespace = namespace _xml_document = PIDFDocument def __unicode__(self): return Other(self.value, self.lang) @classmethod def from_string(cls, value): if isinstance(value, Other): return cls(value, value.lang) elif isinstance(value, basestring): return cls(value) else: raise ValueError("expected str/unicode instance, got %s instead" % value.__class__.__name__) class Other(Note): pass class ActivityRegistry(object): __metaclass__ = XMLEmptyElementRegistryType _xml_namespace = namespace _xml_document = PIDFDocument names = ('appointment', 'away', 'breakfast', 'busy', 'dinner', 'holiday', 'in-transit', 'looking-for-work', 'meal', 'meeting', 'on-the-phone', 'performance', 'permanent-absence', 'playing', 'presentation', 'shopping', 'sleeping', 'spectator', 'steering', 'travel', 'tv', 'vacation', 'working', 'worship', 'unknown') class Activities(XMLStringListElement, PersonExtension): _xml_tag = 'activities' _xml_namespace = namespace _xml_document = PIDFDocument _xml_children_order = {RPIDNote.qname: 0} _xml_item_registry = ActivityRegistry _xml_item_other_type = RPIDOther _xml_item_extension_type = ActivityElement id = XMLAttribute('id', type=str, required=False, test_equal=True) since = XMLAttribute('since', xmlname='from', type=DateTime, required=False, test_equal=True) until = XMLAttribute('until', type=DateTime, required=False, test_equal=True) _note_map = NoteMap() def __init__(self, id=None, since=None, until=None, activities=[], notes=[]): XMLElement.__init__(self) self.id = id self.since = since self.until = until self.update(activities) self.notes.update(notes) @property def notes(self): return NoteList(self, RPIDNote) def __eq__(self, other): if isinstance(other, Activities): return super(Activities, self).__eq__(other) and self.notes == other.notes else: return NotImplemented def __repr__(self): return '%s(%r, %r, %r, %r, %r)' % (self.__class__.__name__, self.id, self.since, self.until, list(self), list(self.notes)) def _parse_element(self, element): super(Activities, self)._parse_element(element) self.notes._parse_element(element) def _build_element(self): super(Activities, self)._build_element() self.notes._build_element() def add(self, activity): if isinstance(activity, basestring): if activity in self._xml_item_registry.names: activity = self._xml_item_registry.class_map[activity]() else: activity = self._xml_item_other_type.from_string(activity) unknown_activity = self._xml_item_registry.class_map['unknown']() if activity == unknown_activity or unknown_activity in self._element_map.itervalues(): self.clear() super(Activities, self).add(activity) def check_validity(self): if not self: raise ValidationError("Activity element must have at least one value") super(Activities, self).check_validity() Person.register_extension('activities', type=Activities) class MoodRegistry(object): __metaclass__ = XMLEmptyElementRegistryType _xml_namespace = namespace _xml_document = PIDFDocument names = ('afraid', 'amazed', 'angry', 'annoyed', 'anxious', 'ashamed', 'bored', 'brave', 'calm', 'cold', 'confused', 'contended', 'cranky', 'curious', 'depressed', 'disappointed', 'disgusted', 'distracted', 'embarrassed', 'excited', 'flirtatious', 'frustrated', 'grumpy', 'guilty', 'happy', 'hot', 'humbled', 'humiliated', 'hungry', 'hurt', 'impressed', 'in_awe', 'in_love', 'indignant', 'interested', 'invisible', 'jealous', 'lonely', 'mean', 'moody', 'nervous', 'neutral', 'offended', 'playful', 'proud', 'relieved', 'remorseful', 'restless', 'sad', 'sarcastic', 'serious', 'shocked', 'shy', 'sick', 'sleepy', 'stressed', 'surprised', 'thirsty', 'worried', 'unknown') class Mood(XMLStringListElement, PersonExtension): _xml_tag = 'mood' _xml_namespace = namespace _xml_document = PIDFDocument _xml_extension_type = MoodElement _xml_children_order = {RPIDNote.qname: 0} _xml_item_registry = MoodRegistry _xml_item_other_type = RPIDOther _xml_item_extension_type = MoodElement id = XMLAttribute('id', type=str, required=False, test_equal=True) since = XMLAttribute('since', xmlname='from', type=DateTime, required=False, test_equal=True) until = XMLAttribute('until', type=DateTime, required=False, test_equal=True) _note_map = NoteMap() def __init__(self, id=None, since=None, until=None, moods=[], notes=[]): XMLElement.__init__(self) self.id = id self.since = since self.until = until self.update(moods) self.notes.update(notes) @property def notes(self): return NoteList(self, RPIDNote) def __eq__(self, other): if isinstance(other, Mood): return super(Mood, self).__eq__(other) and self.notes == other.notes else: return NotImplemented def __repr__(self): return '%s(%r, %r, %r, %r, %r)' % (self.__class__.__name__, self.id, self.since, self.until, list(self), list(self.notes)) def _parse_element(self, element): super(Mood, self)._parse_element(element) self.notes._parse_element(element) def _build_element(self): super(Mood, self)._build_element() self.notes._build_element() def add(self, mood): if isinstance(mood, basestring): if mood in self._xml_item_registry.names: mood = self._xml_item_registry.class_map[mood]() else: mood = self._xml_item_other_type.from_string(mood) unknown_mood = self._xml_item_registry.class_map['unknown']() if mood == unknown_mood or unknown_mood in self._element_map.itervalues(): self.clear() super(Mood, self).add(mood) def check_validity(self): if not self: raise ValidationError("Mood element must have at least one value") super(Mood, self).check_validity() Person.register_extension('mood', type=Mood) class AudioPlaceInformation(XMLStringElement): _xml_tag = 'audio' _xml_namespace = namespace _xml_document = PIDFDocument _xml_value_type = AudioPlaceValue class VideoPlaceInformation(XMLStringElement): _xml_tag = 'video' _xml_namespace = namespace _xml_document = PIDFDocument _xml_value_type = VideoPlaceValue class TextPlaceInformation(XMLStringElement): _xml_tag = 'text' _xml_namespace = namespace _xml_document = PIDFDocument _xml_value_type = TextPlaceValue class PlaceIs(XMLElement, PersonExtension): _xml_tag = 'place-is' _xml_namespace = namespace _xml_document = PIDFDocument _xml_children_order = {RPIDNote.qname: 0, AudioPlaceInformation.qname: 1, VideoPlaceInformation.qname: 2, TextPlaceInformation.qname: 3} id = XMLAttribute('id', type=str, required=False, test_equal=True) since = XMLAttribute('since', xmlname='from', type=DateTime, required=False, test_equal=True) until = XMLAttribute('until', type=DateTime, required=False, test_equal=True) audio = XMLElementChild('audio', type=AudioPlaceInformation, required=False, test_equal=True) video = XMLElementChild('video', type=VideoPlaceInformation, required=False, test_equal=True) text = XMLElementChild('text', type=TextPlaceInformation, required=False, test_equal=True) _note_map = NoteMap() def __init__(self, id=None, since=None, until=None, audio=None, video=None, text=None, notes=[]): XMLElement.__init__(self) self.id = id self.since = since self.until = until self.audio = audio self.video = video self.text = text self.notes.update(notes) @property def notes(self): return NoteList(self, RPIDNote) def __eq__(self, other): if isinstance(other, PlaceIs): return super(PlaceIs, self).__eq__(other) and self.notes == other.notes else: return NotImplemented def __repr__(self): return '%s(%r, %r, %r, %r, %r, %r, %r)' % (self.__class__.__name__, self.id, self.since, self.until, self.audio, self.video, self.text, list(self.notes)) def _parse_element(self, element): self.notes._parse_element(element) def _build_element(self): self.notes._build_element() Person.register_extension('place_is', type=PlaceIs) class PlaceType(XMLElement, PersonExtension): _xml_tag = 'place-type' _xml_namespace = namespace _xml_document = PIDFDocument _xml_children_order = {RPIDNote.qname: 0} id = XMLAttribute('id', type=str, required=False, test_equal=True) since = XMLAttribute('since', xmlname='from', type=DateTime, required=False, test_equal=True) until = XMLAttribute('until', type=DateTime, required=False, test_equal=True) value = XMLStringChoiceChild('value', other_type=RPIDOther, extension_type=PlaceTypeElement) _note_map = NoteMap() def __init__(self, id=None, since=None, until=None, placetype=None, notes=[]): super(PlaceType, self).__init__() self.id = id self.since = since self.until = until self.value = placetype self.notes.update(notes) @property def notes(self): return NoteList(self, RPIDNote) def __eq__(self, other): if isinstance(other, PlaceType): return super(PlaceType, self).__eq__(other) and self.notes == other.notes else: return NotImplemented def __repr__(self): return '%s(%r, %r, %r, %r, %r)' % (self.__class__.__name__, self.id, self.since, self.until, self.value, list(self.notes)) def _parse_element(self, element): self.notes._parse_element(element) def _build_element(self): self.notes._build_element() Person.register_extension('place_type', type=PlaceType) class AudioPrivacy(XMLEmptyElement): _xml_tag = 'audio' _xml_namespace = namespace _xml_document = PIDFDocument def __init__(self, private=True): XMLEmptyElement.__init__(self) def __new__(cls, private=True): if not private: return None return XMLEmptyElement.__new__(cls) class TextPrivacy(XMLEmptyElement): _xml_tag = 'text' _xml_namespace = namespace _xml_document = PIDFDocument def __init__(self, private=True): XMLEmptyElement.__init__(self) def __new__(cls, private=True): if not private: return None return XMLEmptyElement.__new__(cls) class VideoPrivacy(XMLEmptyElement): _xml_tag = 'video' _xml_namespace = namespace _xml_document = PIDFDocument def __init__(self, private=True): XMLEmptyElement.__init__(self) def __new__(cls, private=True): if not private: return None return XMLEmptyElement.__new__(cls) class PrivacyType(XMLElementType): def __init__(cls, name, bases, dct): super(PrivacyType, cls).__init__(name, bases, dct) child_attributes = (getattr(cls, name) for name in dir(cls) if type(getattr(cls, name)) is XMLElementChild) cls._privacy_attributes = tuple(attr.name for attr in child_attributes if attr.name in ('audio', 'text', 'video') or issubclass(attr.type, PrivacyElement)) class Privacy(XMLElement, PersonExtension): __metaclass__ = PrivacyType _xml_tag = 'privacy' _xml_namespace = namespace _xml_document = PIDFDocument _xml_children_order = {RPIDNote.qname: 0, AudioPrivacy.qname: 1, TextPrivacy.qname: 2, VideoPrivacy.qname: 3} id = XMLAttribute('id', type=str, required=False, test_equal=True) since = XMLAttribute('since', xmlname='from', type=DateTime, required=False, test_equal=True) until = XMLAttribute('until', type=DateTime, required=False, test_equal=True) audio = XMLElementChild('audio', type=AudioPrivacy, required=False, test_equal=True) text = XMLElementChild('text', type=TextPrivacy, required=False, test_equal=True) video = XMLElementChild('video', type=VideoPrivacy, required=False, test_equal=True) unknown = property(lambda self: all(getattr(self, name) is None for name in self._privacy_attributes)) _note_map = NoteMap() def __init__(self, id=None, since=None, until=None, notes=[], audio=False, text=False, video=False): super(Privacy, self).__init__() self.id = id self.since = since self.until = until self.audio = audio self.text = text self.video = video self.notes.update(notes) @property def notes(self): return NoteList(self, RPIDNote) def __eq__(self, other): if isinstance(other, Privacy): return super(Privacy, self).__eq__(other) and self.notes == other.notes else: return NotImplemented def __repr__(self): return '%s(%r, %r, %r, %r, %r, %r, %r)' % (self.__class__.__name__, self.id, self.since, self.until, list(self.notes), self.audio, self.text, self.video) def _parse_element(self, element): self.notes._parse_element(element) def _build_element(self): if self.unknown: if self.element.find('{%s}unknown' % self._xml_namespace) is None: etree.SubElement(self.element, '{%s}unknown' % self._xml_namespace, nsmap=self._xml_document.nsmap) else: unknown_element = self.element.find('{%s}unknown' % self._xml_namespace) if unknown_element is not None: self.element.remove(unknown_element) self.notes._build_element() Person.register_extension('privacy', type=Privacy) class RelationshipRegistry(object): __metaclass__ = XMLEmptyElementRegistryType _xml_namespace = namespace _xml_document = PIDFDocument names = ('assistant', 'associate', 'family', 'friend', 'self', 'supervisor', 'unknown') class Relationship(XMLElement, ServiceExtension): _xml_tag = 'relationship' _xml_namespace = namespace _xml_document = PIDFDocument _xml_children_order = {RPIDNote: 0} value = XMLStringChoiceChild('value', registry=RelationshipRegistry, other_type=RPIDOther, extension_type=RelationshipElement) _note_map = NoteMap() def __init__(self, relationship='self', notes=[]): XMLElement.__init__(self) self.value = relationship self.notes.update(notes) @property def notes(self): return NoteList(self, RPIDNote) def __eq__(self, other): if isinstance(other, Relationship): return super(Relationship, self).__eq__(other) and self.notes == other.notes else: return NotImplemented def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.value, list(self.notes)) def _parse_element(self, element): self.notes._parse_element(element) def _build_element(self): self.notes._build_element() Service.register_extension('relationship', type=Relationship) class ServiceClassRegistry(object): __metaclass__ = XMLEmptyElementRegistryType _xml_namespace = namespace _xml_document = PIDFDocument names = ('courier', 'electronic', 'freight', 'in-person', 'postal', 'unknown') class ServiceClass(XMLElement, ServiceExtension): _xml_tag = 'service-class' _xml_namespace = namespace _xml_document = PIDFDocument value = XMLStringChoiceChild('value', registry=ServiceClassRegistry, extension_type=ServiceClassElement) _note_map = NoteMap() def __init__(self, service_class=None, notes=[]): XMLElement.__init__(self) self.value = service_class self.notes.update(notes) @property def notes(self): return NoteList(self, RPIDNote) def __eq__(self, other): if isinstance(other, ServiceClass): return super(ServiceClass, self).__eq__(other) and self.notes == other.notes else: return NotImplemented def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.value, list(self.notes)) def _parse_element(self, element): self.notes._parse_element(element) def _build_element(self): self.notes._build_element() Service.register_extension('service_class', type=ServiceClass) class SphereRegistry(object): __metaclass__ = XMLEmptyElementRegistryType _xml_namespace = namespace _xml_document = PIDFDocument names = ('home', 'work', 'unknown') class Sphere(XMLElement, PersonExtension): _xml_tag = 'sphere' _xml_namespace = namespace _xml_document = PIDFDocument id = XMLAttribute('id', type=ID, required=False, test_equal=True) since = XMLAttribute('since', xmlname='from', type=DateTime, required=False, test_equal=True) until = XMLAttribute('until', type=DateTime, required=False, test_equal=True) value = XMLStringChoiceChild('value', registry=SphereRegistry, extension_type=SphereElement) def __init__(self, value=None, id=None, since=None, until=None): XMLElement.__init__(self) self.id = id self.since = since self.until = until self.value = value def __repr__(self): return '%s(%r, %r, %r, %r)' % (self.__class__.__name__, self.value, self.id, self.since, self.until) Person.register_extension('sphere', type=Sphere) class StatusIcon(XMLStringElement, ServiceExtension, PersonExtension): _xml_tag = 'status-icon' _xml_namespace = namespace _xml_document = PIDFDocument id = XMLAttribute('id', type=str, required=False, test_equal=True) since = XMLAttribute('since', xmlname='from', type=DateTime, required=False, test_equal=True) until = XMLAttribute('until', type=DateTime, required=False, test_equal=True) def __init__(self, value=None, id=None, since=None, until=None): XMLStringElement.__init__(self, value) self.id = id self.since = since self.until = until Person.register_extension('status_icon', type=StatusIcon) Service.register_extension('status_icon', type=StatusIcon) class TimeOffset(XMLStringElement, PersonExtension): _xml_tag = 'time-offset' _xml_namespace = namespace _xml_document = PIDFDocument id = XMLAttribute('id', type=str, required=False, test_equal=True) since = XMLAttribute('since', xmlname='from', type=DateTime, required=False, test_equal=True) until = XMLAttribute('until', type=DateTime, required=False, test_equal=True) description = XMLAttribute('description', type=str, required=False, test_equal=True) def __init__(self, value=None, id=None, since=None, until=None, description=None): if value is None: value = DateTime.now().utcoffset().seconds / 60 XMLStringElement.__init__(self, str(value)) self.id = id self.since = since self.until = until self.description = description def __int__(self): return int(self.value) Person.register_extension('time_offset', type=TimeOffset) class UserInput(XMLStringElement, ServiceExtension, PersonExtension, DeviceExtension): _xml_tag = 'user-input' _xml_namespace = namespace _xml_document = PIDFDocument _xml_value_type = UserInputValue id = XMLAttribute('id', type=str, required=False, test_equal=True) last_input = XMLAttribute('last_input', xmlname='last-input', type=DateTime, required=False, test_equal=True) idle_threshold = XMLAttribute('idle_threshold', xmlname='idle-threshold', type=UnsignedLong, required=False, test_equal=True) def __init__(self, value='active', id=None, last_input=None, idle_threshold=None): XMLStringElement.__init__(self, value) self.id = id self.last_input = last_input self.idle_threshold = idle_threshold Service.register_extension('user_input', type=UserInput) Person.register_extension('user_input', type=UserInput) Device.register_extension('user_input', type=UserInput) class Class(XMLStringElement, ServiceExtension, PersonExtension, DeviceExtension): _xml_tag = 'class' _xml_namespace = namespace _xml_document = PIDFDocument Service.register_extension('rpid_class', type=Class) Person.register_extension('rpid_class', type=Class) Device.register_extension('rpid_class', type=Class) ================================================ FILE: sipsimple/payloads/watcherinfo.py ================================================ """Parses application/watcherinfo+xml documents according to RFC3857 and RFC3858.""" __all__ = ['namespace', 'NeedFullUpdateError', 'WatcherInfoDocument', 'Watcher', 'WatcherList', 'WatcherInfo'] from sipsimple.payloads import XMLDocument, XMLAnyURIElement, XMLListElement, XMLListRootElement, XMLElementID, XMLAttribute from sipsimple.payloads import IterateIDs, IterateItems, All from sipsimple.payloads.datatypes import NonNegativeInteger, UnsignedLong, SIPURI namespace = 'urn:ietf:params:xml:ns:watcherinfo' class NeedFullUpdateError(Exception): pass class WatcherInfoDocument(XMLDocument): content_type = 'application/watcherinfo+xml' WatcherInfoDocument.register_namespace(namespace, prefix=None, schema='watcherinfo.xsd') ## Attribute value types class WatcherStatus(str): def __new__(cls, value): if value not in ('pending', 'active', 'waiting', 'terminated'): raise ValueError('illegal status value for watcher') return str.__new__(cls, value) class WatcherEvent(str): def __new__(cls, value): if value not in ('subscribe', 'approved', 'deactivated', 'probation', 'rejected', 'timeout', 'giveup', 'noresource'): raise ValueError('illegal event value for watcher') return str.__new__(cls, value) class WatcherInfoState(str): def __new__(cls, value): if value not in ('full', 'partial'): raise ValueError('illegal state value for watcherinfo') return str.__new__(cls, value) ## XMLElements class Watcher(XMLAnyURIElement): """ Definition for a watcher in a watcherinfo document Provides the attributes: * id * status * event * display_name * expiration * duration * sipuri Can be transformed to a string with the format DISPLAY_NAME . """ _xml_tag = 'watcher' _xml_namespace = namespace _xml_document = WatcherInfoDocument _xml_value_type = SIPURI id = XMLElementID('id', type=str, required=True, test_equal=True) status = XMLAttribute('status', type=WatcherStatus, required=True, test_equal=True) event = XMLAttribute('event', type=WatcherEvent, required=True, test_equal=True) display_name = XMLAttribute('display_name', xmlname='display-name', type=str, required=False, test_equal=True) expiration = XMLAttribute('expiration', type=UnsignedLong, required=False, test_equal=False) duration = XMLAttribute('duration', xmlname='duration-subscribed', type=UnsignedLong, required=False, test_equal=False) sipuri = XMLAnyURIElement.value def __init__(self, sipuri, id, status, event, display_name=None, expiration=None, duration=None): XMLAnyURIElement.__init__(self) self.sipuri = sipuri self.id = id self.status = status self.event = event self.display_name = display_name self.expiration = expiration self.duration = duration def __repr__(self): return '%s(%r, %r, %r, %r, %r, %r, %r)' % (self.__class__.__name__, self.sipuri, self.id, self.status, self.event, self.display_name, self.expiration, self.duration) def __str__(self): return '"%s" <%s>' % (self.display_name, self.sipuri) if self.display_name else self.sipuri class WatcherList(XMLListElement): """ Definition for a list of watchers in a watcherinfo document It behaves like a list in that it can be indexed by a number, can be iterated and counted. It also provides the properties pending, active and terminated which are generators returning Watcher objects with the corresponding status. """ _xml_tag = 'watcher-list' _xml_namespace = namespace _xml_document = WatcherInfoDocument _xml_children_order = {Watcher.qname: 0} _xml_item_type = Watcher resource = XMLElementID('resource', type=SIPURI, required=True, test_equal=True) package = XMLAttribute('package', type=str, required=True, test_equal=True) def __init__(self, resource, package, watchers=[]): XMLListElement.__init__(self) self.resource = resource self.package = package self.update(watchers) def __repr__(self): return '%s(%r, %r, %r)' % (self.__class__.__name__, self.resource, self.package, list(self)) def __getitem__(self, key): if key is IterateIDs: return self._xmlid_map[Watcher].iterkeys() elif key is IterateItems: return self._xmlid_map[Watcher].itervalues() else: return self._xmlid_map[Watcher][key] def __delitem__(self, key): if key is All: for item in self._xmlid_map[Watcher].values(): self.remove(item) else: self.remove(self._xmlid_map[Watcher][key]) def get(self, key, default=None): return self._xmlid_map[Watcher].get(key, default) pending = property(lambda self: (watcher for watcher in self if watcher.status == 'pending')) waiting = property(lambda self: (watcher for watcher in self if watcher.status == 'waiting')) active = property(lambda self: (watcher for watcher in self if watcher.status == 'active')) terminated = property(lambda self: (watcher for watcher in self if watcher.status == 'terminated')) class WatcherInfo(XMLListRootElement): """ Definition for watcher info: a list of WatcherList elements The user agent instantiates this class once it subscribes to a *.winfo event and calls its update() method with the application/watcherinfo+xml documents it receives via NOTIFY. The watchers can be accessed in two ways: 1. via the wlists property, which returns a list of WatcherList elements; 2. via the pending, active and terminated properties, which return dictionaries, mapping WatcherList objects to lists of Watcher objects. Since WatcherList objects can be compared for equality to SIP URI strings, representing the presentity to which the watchers have subscribed, the dictionaries can also be indexed by such strings. """ _xml_tag = 'watcherinfo' _xml_namespace = namespace _xml_document = WatcherInfoDocument _xml_children_order = {WatcherList.qname: 0} _xml_item_type = WatcherList version = XMLAttribute('version', type=NonNegativeInteger, required=True, test_equal=True) state = XMLAttribute('state', type=WatcherInfoState, required=True, test_equal=True) def __init__(self, version=0, state='full', wlists=[]): XMLListRootElement.__init__(self) self.version = version self.state = state self.update(wlists) def __repr__(self): return '%s(%r, %r, %r)' % (self.__class__.__name__, self.version, self.state, list(self)) def __getitem__(self, key): if key is IterateIDs: return self._xmlid_map[WatcherList].iterkeys() elif key is IterateItems: return self._xmlid_map[WatcherList].itervalues() else: return self._xmlid_map[WatcherList][key] def __delitem__(self, key): if key is All: for item in self._xmlid_map[WatcherList].values(): self.remove(item) else: self.remove(self._xmlid_map[WatcherList][key]) def get(self, key, default=None): return self._xmlid_map[WatcherList].get(key, default) wlists = property(lambda self: self._element_map.values()) pending = property(lambda self: dict((wlist, list(wlist.pending)) for wlist in self._element_map.itervalues())) waiting = property(lambda self: dict((wlist, list(wlist.waiting)) for wlist in self._element_map.itervalues())) active = property(lambda self: dict((wlist, list(wlist.active)) for wlist in self._element_map.itervalues())) terminated = property(lambda self: dict((wlist, list(wlist.terminated)) for wlist in self._element_map.itervalues())) ================================================ FILE: sipsimple/payloads/xcapcaps.py ================================================ """Support for parsing and building xcap-caps documents, as defined by RFC4825.""" __all__ = ['XCAPCapabilitiesDocument', 'AUIDS', 'Extensions', 'Namespaces', 'XCAPCapabilities'] from sipsimple.payloads import XMLDocument, XMLElementChild, XMLListElement, XMLRootElement, XMLStringElement namespace = 'urn:ietf:params:xml:ns:xcap-caps' class XCAPCapabilitiesDocument(XMLDocument): content_type = 'application/xcap-caps+xml' XCAPCapabilitiesDocument.register_namespace(namespace, prefix=None, schema='xcap-caps.xsd') ## Elements class AUID(XMLStringElement): _xml_tag = 'auid' _xml_namespace = namespace _xml_document = XCAPCapabilitiesDocument class AUIDS(XMLListElement): _xml_tag = 'auids' _xml_namespace = namespace _xml_document = XCAPCapabilitiesDocument _xml_item_type = AUID def __init__(self, children=[]): XMLListElement.__init__(self) self.update(children) def __iter__(self): return (unicode(item) for item in super(AUIDS, self).__iter__()) def add(self, item): if isinstance(item, basestring): item = AUID(item) super(AUIDS, self).add(item) def remove(self, item): if isinstance(item, basestring): try: item = (entry for entry in super(AUIDS, self).__iter__() if entry == item).next() except StopIteration: raise KeyError(item) super(AUIDS, self).remove(item) class Extension(XMLStringElement): _xml_tag = 'extension' _xml_namespace = namespace _xml_document = XCAPCapabilitiesDocument class Extensions(XMLListElement): _xml_tag = 'extensions' _xml_namespace = namespace _xml_document = XCAPCapabilitiesDocument _xml_item_type = Extension def __init__(self, children=[]): XMLListElement.__init__(self) self.update(children) def __iter__(self): return (unicode(item) for item in super(Extensions, self).__iter__()) def add(self, item): if isinstance(item, basestring): item = Extension(item) super(Extensions, self).add(item) def remove(self, item): if isinstance(item, basestring): try: item = (entry for entry in super(Extensions, self).__iter__() if entry == item).next() except StopIteration: raise KeyError(item) super(Extensions, self).remove(item) class Namespace(XMLStringElement): _xml_tag = 'extension' _xml_namespace = namespace _xml_document = XCAPCapabilitiesDocument class Namespaces(XMLListElement): _xml_tag = 'namespaces' _xml_namespace = namespace _xml_document = XCAPCapabilitiesDocument _xml_item_type = Namespace def __init__(self, children=[]): XMLListElement.__init__(self) self.update(children) def __iter__(self): return (unicode(item) for item in super(Namespaces, self).__iter__()) def add(self, item): if isinstance(item, basestring): item = Namespace(item) super(Namespaces, self).add(item) def remove(self, item): if isinstance(item, basestring): try: item = (entry for entry in super(Namespaces, self).__iter__() if entry == item).next() except StopIteration: raise KeyError(item) super(Namespaces, self).remove(item) class XCAPCapabilities(XMLRootElement): _xml_tag = 'xcap-caps' _xml_namespace = namespace _xml_document = XCAPCapabilitiesDocument _xml_children_order = {AUIDS.qname: 0, Extensions.qname: 1, Namespaces.qname: 2} auids = XMLElementChild('auids', type=AUIDS, required=True, test_equal=True) extensions = XMLElementChild('extensions', type=Extensions, required=False, test_equal=True) namespaces = XMLElementChild('namespaces', type=Namespaces, required=True, test_equal=True) def __init__(self, auids=[], extensions=[], namespaces=[]): XMLRootElement.__init__(self) self.auids = AUIDS(auids) self.extensions = Extensions(extensions) self.namespaces = Namespaces(namespaces) def __repr__(self): return '%s(%r, %r, %r)' % (self.__class__.__name__, self.auids, self.extensions, self.namespaces) ================================================ FILE: sipsimple/payloads/xcapdiff.py ================================================ """ This module allows parsing and building xcap-diff documents according to RFC 5874. """ __all__ = ['namespace', 'XCAPDiffDocument', 'BodyNotChanged', 'Document', 'Element', 'Attribute', 'XCAPDiff'] from sipsimple.payloads import XMLDocument, XMLElement, XMLListRootElement, XMLStringElement, XMLEmptyElement, XMLAttribute, XMLElementID, XMLElementChild from sipsimple.payloads.datatypes import Boolean, XCAPURI namespace = 'urn:ietf:params:xml:ns:xcap-diff' class XCAPDiffDocument(XMLDocument): content_type = 'application/xcap-diff+xml' XCAPDiffDocument.register_namespace(namespace, prefix=None, schema='xcapdiff.xsd') class BodyNotChanged(XMLEmptyElement): _xml_tag = 'body-not-changed' _xml_namespace = namespace _xml_document = XCAPDiffDocument class Document(XMLElement): _xml_tag = 'document' _xml_namespace = namespace _xml_document = XCAPDiffDocument selector = XMLElementID('selector', xmlname='sel', type=XCAPURI, required=True, test_equal=True) new_etag = XMLAttribute('new_etag', xmlname='new-etag', type=str, required=False, test_equal=True) previous_etag = XMLAttribute('previous_etag', xmlname='previous-etag', type=str, required=False, test_equal=True) body_not_changed = XMLElementChild('body_not_changed', type=BodyNotChanged, required=False, test_equal=True) def __init__(self, selector, new_etag=None, previous_etag=None): XMLElement.__init__(self) self.selector = selector self.new_etag = new_etag self.previous_etag = previous_etag def __repr__(self): return '%s(%r, %r, %r)' % (self.__class__.__name__, self.selector, self.new_etag, self.previous_etag) @property def empty_body(self): return self.body_not_changed is not None @empty_body.setter def empty_body(self, body_not_changed): if body_not_changed: self.body_not_changed = BodyNotChanged() else: self.body_not_changed = None class Element(XMLElement): _xml_tag = 'element' _xml_namespace = namespace _xml_document = XCAPDiffDocument selector = XMLElementID('selector', xmlname='sel', type=XCAPURI, required=True, test_equal=True) exists = XMLAttribute('exists', type=Boolean, required=False, test_equal=True) def __init__(self, selector, exists=None): XMLElement.__init__(self) self.selector = selector self.exists = exists def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.selector, self.exists) class Attribute(XMLStringElement): _xml_tag = 'attribute' _xml_namespace = namespace _xml_document = XCAPDiffDocument selector = XMLElementID('selector', xmlname='sel', type=XCAPURI, required=True, test_equal=True) exists = XMLAttribute('exists', type=Boolean, required=False, test_equal=True) def __init__(self, selector, exists=None): XMLStringElement.__init__(self) self.selector = selector self.exists = exists def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.selector, self.exists) class XCAPDiff(XMLListRootElement): _xml_tag = 'xcap-diff' _xml_namespace = namespace _xml_document = XCAPDiffDocument _xml_item_type = (Document, Element, Attribute) xcap_root = XMLElementID('xcap_root', xmlname='xcap-root', type=str, required=True, test_equal=True) def __init__(self, xcap_root, children=[]): XMLListRootElement.__init__(self) self.xcap_root = xcap_root self.update(children) def __repr__(self): return '%s(%r, %r)' % (self.__class__.__name__, self.xcap_root, list(self)) ================================================ FILE: sipsimple/payloads/xml-schemas/addressbook.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/caps.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/cipid.xsd ================================================ Describes CIPID tuple extensions for PIDF. ================================================ FILE: sipsimple/payloads/xml-schemas/common-policy.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/common-schema.xsd ================================================ Timestamp type Device ID, a URN Note type ================================================ FILE: sipsimple/payloads/xml-schemas/conference.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/data-model.xsd ================================================ Device ID, a URN Contains information about the device Contains information about the human user Characteristic and status information ================================================ FILE: sipsimple/payloads/xml-schemas/dialog-info.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/dialog-rules.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/im-iscomposing.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/imdn.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/oma-common-policy.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/oma-pres-content.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/patchops.xsd ================================================ ]> ================================================ FILE: sipsimple/payloads/xml-schemas/pidf.xsd ================================================ This attribute may be used on any element within an optional PIDF extension to indicate that the corresponding element must be understood by the PIDF processor if the enclosing optional element is to be handled. ================================================ FILE: sipsimple/payloads/xml-schemas/pres-rules.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/resourcelists.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/rlmi.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/rlsservices.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/rpid.xsd ================================================ Describes what the person is currently doing, expressed as an enumeration of activity-describing elements. A person can be engaged in multiple activities at the same time, e.g., traveling and having a meal. Describes the class of the service, device or person. Describes the mood of the presentity. Describes the type of place the person is currently at. Indicates which type of communication third parties in the vicinity of the presentity are unlikely to be able to intercept accidentally or intentionally. Designates the type of relationship an alternate contact has with the presentity. Designates the type of service offered. Designates the current state and role that the person plays. A URI pointing to an image (icon) representing the current status of the person or service. Describes the number of minutes of offset from UTC at the user's current location. Records the user-input or usage state of the service or device. ================================================ FILE: sipsimple/payloads/xml-schemas/watcherinfo.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/xcap-caps.xsd ================================================ Root element for xcap-caps List of supported AUID. List of supported extensions. List of supported namespaces. AUID Type Extension Type Namespace type ================================================ FILE: sipsimple/payloads/xml-schemas/xcap-directory.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/xcapdiff.xsd ================================================ ================================================ FILE: sipsimple/payloads/xml-schemas/xml.xsd ================================================ See http://www.w3.org/XML/1998/namespace.html and http://www.w3.org/TR/REC-xml for information about this namespace. This schema document describes the XML namespace, in a form suitable for import by other schema documents. Note that local names in this namespace are intended to be defined only by the World Wide Web Consortium or its subgroups. The following names are currently defined in this namespace and should not be used with conflicting semantics by any Working Group, specification, or document instance: base (as an attribute name): denotes an attribute whose value provides a URI to be used as the base for interpreting any relative URIs in the scope of the element on which it appears; its value is inherited. This name is reserved by virtue of its definition in the XML Base specification. lang (as an attribute name): denotes an attribute whose value is a language code for the natural language of the content of any element; its value is inherited. This name is reserved by virtue of its definition in the XML specification. space (as an attribute name): denotes an attribute whose value is a keyword indicating what whitespace processing discipline is intended for the content of the element; its value is inherited. This name is reserved by virtue of its definition in the XML specification. Father (in any context at all): denotes Jon Bosak, the chair of the original XML Working Group. This name is reserved by the following decision of the W3C XML Plenary and XML Coordination groups: In appreciation for his vision, leadership and dedication the W3C XML Plenary on this 10th day of February, 2000 reserves for Jon Bosak in perpetuity the XML name xml:Father This schema defines attributes and an attribute group suitable for use by schemas wishing to allow xml:base, xml:lang or xml:space attributes on elements they define. To enable this, such a schema must import this schema for the XML namespace, e.g. as follows: <schema . . .> . . . <import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/03/xml.xsd"/> Subsequently, qualified reference to any of the attributes or the group defined below will have the desired effect, e.g. <type . . .> . . . <attributeGroup ref="xml:specialAttrs"/> will define a type which will schema-validate an instance element with any of those attributes In keeping with the XML Schema WG's standard versioning policy, this schema document will persist at http://www.w3.org/2001/03/xml.xsd. At the date of issue it can also be found at http://www.w3.org/2001/xml.xsd. The schema document at that URI may however change in the future, in order to remain compatible with the latest version of XML Schema itself. In other words, if the XML Schema namespace changes, the version of this document at http://www.w3.org/2001/xml.xsd will change accordingly; the version at http://www.w3.org/2001/03/xml.xsd will not change. In due course, we should install the relevant ISO 2- and 3-letter codes as the enumerated possible values . . . See http://www.w3.org/TR/xmlbase/ for information about this attribute. ================================================ FILE: sipsimple/session.py ================================================ """ Implements an asynchronous notification based mechanism for establishment, modification and termination of sessions using Session Initiation Protocol (SIP) standardized in RFC3261. """ from __future__ import absolute_import __all__ = ['Session', 'SessionManager'] import random from threading import RLock from time import time from application.notification import IObserver, Notification, NotificationCenter, NotificationData from application.python import Null, limit from application.python.decorator import decorator, preserve_signature from application.python.types import Singleton from application.system import host from eventlib import api, coros, proc from twisted.internet import reactor from zope.interface import implements from sipsimple import log from sipsimple.account import AccountManager, BonjourAccount from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.core import DialogID, Engine, Invitation, Referral, Subscription, PJSIPError, SIPCoreError, SIPCoreInvalidStateError, SIPURI, sip_status_messages, sipfrag_re from sipsimple.core import ContactHeader, FromHeader, Header, ReasonHeader, ReferToHeader, ReplacesHeader, RouteHeader, ToHeader, WarningHeader from sipsimple.core import SDPConnection, SDPMediaStream, SDPSession from sipsimple.core import PublicGRUU, PublicGRUUIfAvailable, NoGRUU from sipsimple.lookup import DNSLookup, DNSLookupError from sipsimple.payloads import ParserError from sipsimple.payloads.conference import ConferenceDocument from sipsimple.streams import MediaStreamRegistry, InvalidStreamError, UnknownStreamError from sipsimple.threading import run_in_twisted_thread from sipsimple.threading.green import Command, run_in_green_thread from sipsimple.util import ISOTimestamp class InvitationDisconnectedError(Exception): def __init__(self, invitation, data): self.invitation = invitation self.data = data class MediaStreamDidNotInitializeError(Exception): def __init__(self, stream, data): self.stream = stream self.data = data class MediaStreamDidFailError(Exception): def __init__(self, stream, data): self.stream = stream self.data = data class SubscriptionError(Exception): def __init__(self, error, timeout, **attributes): self.error = error self.timeout = timeout self.attributes = attributes class SIPSubscriptionDidFail(Exception): def __init__(self, data): self.data = data class InterruptSubscription(Exception): pass class TerminateSubscription(Exception): pass class ReferralError(Exception): def __init__(self, error, code=0): self.error = error self.code = code class TerminateReferral(Exception): pass class SIPReferralDidFail(Exception): def __init__(self, data): self.data = data class IllegalStateError(RuntimeError): pass class IllegalDirectionError(RuntimeError): pass class SIPInvitationTransferDidFail(Exception): def __init__(self, data): self.data = data @decorator def transition_state(required_state, new_state): def state_transitioner(func): @preserve_signature(func) def wrapper(obj, *args, **kwargs): with obj._lock: if obj.state != required_state: raise IllegalStateError('cannot call %s in %s state' % (func.__name__, obj.state)) obj.state = new_state return func(obj, *args, **kwargs) return wrapper return state_transitioner @decorator def check_state(required_states): def state_checker(func): @preserve_signature(func) def wrapper(obj, *args, **kwargs): if obj.state not in required_states: raise IllegalStateError('cannot call %s in %s state' % (func.__name__, obj.state)) return func(obj, *args, **kwargs) return wrapper return state_checker @decorator def check_transfer_state(direction, state): def state_checker(func): @preserve_signature(func) def wrapper(obj, *args, **kwargs): if obj.transfer_handler.direction != direction: raise IllegalDirectionError('cannot transfer in %s direction' % obj.transfer_handler.direction) if obj.transfer_handler.state != state: raise IllegalStateError('cannot transfer in %s state' % obj.transfer_handler.state) return func(obj, *args, **kwargs) return wrapper return state_checker class AddParticipantOperation(object): pass class RemoveParticipantOperation(object): pass class ReferralHandler(object): implements(IObserver) def __init__(self, session, participant_uri, operation): self.participant_uri = participant_uri if not isinstance(self.participant_uri, SIPURI): if not self.participant_uri.startswith(('sip:', 'sips:')): self.participant_uri = 'sip:%s' % self.participant_uri try: self.participant_uri = SIPURI.parse(self.participant_uri) except SIPCoreError: notification_center = NotificationCenter() if operation is AddParticipantOperation: notification_center.post_notification('SIPConferenceDidNotAddParticipant', sender=session, data=NotificationData(participant=self.participant_uri, code=0, reason='invalid participant URI')) else: notification_center.post_notification('SIPConferenceDidNotRemoveParticipant', sender=session, data=NotificationData(participant=self.participant_uri, code=0, reason='invalid participant URI')) return self.session = session self.operation = operation self.active = False self.route = None self._channel = coros.queue() self._referral = None def start(self): notification_center = NotificationCenter() if not self.session.remote_focus: if self.operation is AddParticipantOperation: notification_center.post_notification('SIPConferenceDidNotAddParticipant', sender=self.session, data=NotificationData(participant=self.participant_uri, code=0, reason='remote endpoint is not a focus')) else: notification_center.post_notification('SIPConferenceDidNotRemoveParticipant', sender=self.session, data=NotificationData(participant=self.participant_uri, code=0, reason='remote endpoint is not a focus')) self.session = None return notification_center.add_observer(self, sender=self.session) notification_center.add_observer(self, name='NetworkConditionsDidChange') proc.spawn(self._run) def _run(self): notification_center = NotificationCenter() settings = SIPSimpleSettings() try: # Lookup routes account = self.session.account if account is BonjourAccount(): uri = SIPURI.new(self.session._invitation.remote_contact_header.uri) elif account.sip.outbound_proxy is not None and account.sip.outbound_proxy.transport in settings.sip.transport_list: uri = SIPURI(host=account.sip.outbound_proxy.host, port=account.sip.outbound_proxy.port, parameters={'transport': account.sip.outbound_proxy.transport}) elif account.sip.always_use_my_proxy: uri = SIPURI(host=account.id.domain) else: uri = SIPURI.new(self.session.remote_identity.uri) lookup = DNSLookup() try: routes = lookup.lookup_sip_proxy(uri, settings.sip.transport_list).wait() except DNSLookupError, e: timeout = random.uniform(15, 30) raise ReferralError(error='DNS lookup failed: %s' % e) target_uri = SIPURI.new(self.session.remote_identity.uri) timeout = time() + 30 for route in routes: self.route = route remaining_time = timeout - time() if remaining_time > 0: try: contact_uri = account.contact[NoGRUU, route] except KeyError: continue refer_to_header = ReferToHeader(str(self.participant_uri)) refer_to_header.parameters['method'] = 'INVITE' if self.operation is AddParticipantOperation else 'BYE' referral = Referral(target_uri, FromHeader(account.uri, account.display_name), ToHeader(target_uri), refer_to_header, ContactHeader(contact_uri), RouteHeader(route.uri), account.credentials) notification_center.add_observer(self, sender=referral) try: referral.send_refer(timeout=limit(remaining_time, min=1, max=5)) except SIPCoreError: notification_center.remove_observer(self, sender=referral) timeout = 5 raise ReferralError(error='Internal error') self._referral = referral try: while True: notification = self._channel.wait() if notification.name == 'SIPReferralDidStart': break except SIPReferralDidFail, e: notification_center.remove_observer(self, sender=referral) self._referral = None if e.data.code in (403, 405): raise ReferralError(error=sip_status_messages[e.data.code], code=e.data.code) else: # Otherwise just try the next route continue else: break else: self.route = None raise ReferralError(error='No more routes to try') # At this point it is subscribed. Handle notifications and ending/failures. try: self.active = True while True: notification = self._channel.wait() if notification.name == 'SIPReferralGotNotify': if notification.data.event == 'refer' and notification.data.body: match = sipfrag_re.match(notification.data.body) if match: code = int(match.group('code')) reason = match.group('reason') if code/100 > 2: continue if self.operation is AddParticipantOperation: notification_center.post_notification('SIPConferenceGotAddParticipantProgress', sender=self.session, data=NotificationData(participant=self.participant_uri, code=code, reason=reason)) else: notification_center.post_notification('SIPConferenceGotRemoveParticipantProgress', sender=self.session, data=NotificationData(participant=self.participant_uri, code=code, reason=reason)) elif notification.name == 'SIPReferralDidEnd': break except SIPReferralDidFail, e: notification_center.remove_observer(self, sender=self._referral) raise ReferralError(error=e.data.reason, code=e.data.code) else: notification_center.remove_observer(self, sender=self._referral) if self.operation is AddParticipantOperation: notification_center.post_notification('SIPConferenceDidAddParticipant', sender=self.session, data=NotificationData(participant=self.participant_uri)) else: notification_center.post_notification('SIPConferenceDidRemoveParticipant', sender=self.session, data=NotificationData(participant=self.participant_uri)) finally: self.active = False except TerminateReferral: if self._referral is not None: try: self._referral.end(timeout=2) except SIPCoreError: pass else: try: while True: notification = self._channel.wait() if notification.name == 'SIPReferralDidEnd': break except SIPReferralDidFail: pass finally: notification_center.remove_observer(self, sender=self._referral) if self.operation is AddParticipantOperation: notification_center.post_notification('SIPConferenceDidNotAddParticipant', sender=self.session, data=NotificationData(participant=self.participant_uri, code=0, reason='error')) else: notification_center.post_notification('SIPConferenceDidNotRemoveParticipant', sender=self.session, data=NotificationData(participant=self.participant_uri, code=0, reason='error')) except ReferralError, e: if self.operation is AddParticipantOperation: notification_center.post_notification('SIPConferenceDidNotAddParticipant', sender=self.session, data=NotificationData(participant=self.participant_uri, code=e.code, reason=e.error)) else: notification_center.post_notification('SIPConferenceDidNotRemoveParticipant', sender=self.session, data=NotificationData(participant=self.participant_uri, code=e.code, reason=e.error)) finally: notification_center.remove_observer(self, sender=self.session) notification_center.remove_observer(self, name='NetworkConditionsDidChange') self.session = None self._referral = None def _refresh(self): try: contact_header = ContactHeader(self.session.account.contact[NoGRUU, self.route]) except KeyError: pass else: try: self._referral.refresh(contact_header=contact_header, timeout=2) except (SIPCoreError, SIPCoreInvalidStateError): pass @run_in_twisted_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_SIPReferralDidStart(self, notification): self._channel.send(notification) def _NH_SIPReferralDidEnd(self, notification): self._channel.send(notification) def _NH_SIPReferralDidFail(self, notification): self._channel.send_exception(SIPReferralDidFail(notification.data)) def _NH_SIPReferralGotNotify(self, notification): self._channel.send(notification) def _NH_SIPSessionDidFail(self, notification): self._channel.send_exception(TerminateReferral()) def _NH_SIPSessionWillEnd(self, notification): self._channel.send_exception(TerminateReferral()) def _NH_NetworkConditionsDidChange(self, notification): if self.active: self._refresh() class ConferenceHandler(object): implements(IObserver) def __init__(self, session): self.session = session self.active = False self.subscribed = False self._command_proc = None self._command_channel = coros.queue() self._data_channel = coros.queue() self._subscription = None self._subscription_proc = None self._subscription_timer = None notification_center = NotificationCenter() notification_center.add_observer(self, sender=self.session) notification_center.add_observer(self, name='NetworkConditionsDidChange') self._command_proc = proc.spawn(self._run) @run_in_green_thread def add_participant(self, participant_uri): referral_handler = ReferralHandler(self.session, participant_uri, AddParticipantOperation) referral_handler.start() @run_in_green_thread def remove_participant(self, participant_uri): referral_handler = ReferralHandler(self.session, participant_uri, RemoveParticipantOperation) referral_handler.start() def _run(self): while True: command = self._command_channel.wait() handler = getattr(self, '_CH_%s' % command.name) handler(command) def _activate(self): self.active = True command = Command('subscribe') self._command_channel.send(command) return command def _deactivate(self): self.active = False command = Command('unsubscribe') self._command_channel.send(command) return command def _resubscribe(self): command = Command('subscribe') self._command_channel.send(command) return command def _terminate(self): notification_center = NotificationCenter() notification_center.remove_observer(self, sender=self.session) notification_center.remove_observer(self, name='NetworkConditionsDidChange') self._deactivate() command = Command('terminate') self._command_channel.send(command) command.wait() self.session = None def _CH_subscribe(self, command): if self._subscription_timer is not None and self._subscription_timer.active(): self._subscription_timer.cancel() self._subscription_timer = None if self._subscription_proc is not None: subscription_proc = self._subscription_proc subscription_proc.kill(InterruptSubscription) subscription_proc.wait() self._subscription_proc = proc.spawn(self._subscription_handler, command) def _CH_unsubscribe(self, command): # Cancel any timer which would restart the subscription process if self._subscription_timer is not None and self._subscription_timer.active(): self._subscription_timer.cancel() self._subscription_timer = None if self._subscription_proc is not None: subscription_proc = self._subscription_proc subscription_proc.kill(TerminateSubscription) subscription_proc.wait() self._subscription_proc = None command.signal() def _CH_terminate(self, command): command.signal() raise proc.ProcExit() def _subscription_handler(self, command): notification_center = NotificationCenter() settings = SIPSimpleSettings() try: # Lookup routes account = self.session.account if account is BonjourAccount(): uri = SIPURI.new(self.session._invitation.remote_contact_header.uri) elif account.sip.outbound_proxy is not None and account.sip.outbound_proxy.transport in settings.sip.transport_list: uri = SIPURI(host=account.sip.outbound_proxy.host, port=account.sip.outbound_proxy.port, parameters={'transport': account.sip.outbound_proxy.transport}) elif account.sip.always_use_my_proxy: uri = SIPURI(host=account.id.domain) else: uri = SIPURI.new(self.session.remote_identity.uri) lookup = DNSLookup() try: routes = lookup.lookup_sip_proxy(uri, settings.sip.transport_list).wait() except DNSLookupError, e: timeout = random.uniform(15, 30) raise SubscriptionError(error='DNS lookup failed: %s' % e, timeout=timeout) target_uri = SIPURI.new(self.session.remote_identity.uri) default_interval = 600 if account is BonjourAccount() else account.sip.subscribe_interval refresh_interval = getattr(command, 'refresh_interval', default_interval) timeout = time() + 30 for route in routes: remaining_time = timeout - time() if remaining_time > 0: try: contact_uri = account.contact[NoGRUU, route] except KeyError: continue subscription = Subscription(target_uri, FromHeader(account.uri, account.display_name), ToHeader(target_uri), ContactHeader(contact_uri), 'conference', RouteHeader(route.uri), credentials=account.credentials, refresh=refresh_interval) notification_center.add_observer(self, sender=subscription) try: subscription.subscribe(timeout=limit(remaining_time, min=1, max=5)) except SIPCoreError: notification_center.remove_observer(self, sender=subscription) timeout = 5 raise SubscriptionError(error='Internal error', timeout=timeout) self._subscription = subscription try: while True: notification = self._data_channel.wait() if notification.sender is subscription and notification.name == 'SIPSubscriptionDidStart': break except SIPSubscriptionDidFail, e: notification_center.remove_observer(self, sender=subscription) self._subscription = None if e.data.code == 407: # Authentication failed, so retry the subscription in some time timeout = random.uniform(60, 120) raise SubscriptionError(error='Authentication failed', timeout=timeout) elif e.data.code == 423: # Get the value of the Min-Expires header timeout = random.uniform(60, 120) if e.data.min_expires is not None and e.data.min_expires > refresh_interval: raise SubscriptionError(error='Interval too short', timeout=timeout, min_expires=e.data.min_expires) else: raise SubscriptionError(error='Interval too short', timeout=timeout) elif e.data.code in (405, 406, 489, 1400): command.signal(e) return else: # Otherwise just try the next route continue else: self.subscribed = True command.signal() break else: # There are no more routes to try, reschedule the subscription timeout = random.uniform(60, 180) raise SubscriptionError(error='No more routes to try', timeout=timeout) # At this point it is subscribed. Handle notifications and ending/failures. try: while True: notification = self._data_channel.wait() if notification.sender is not self._subscription: continue if notification.name == 'SIPSubscriptionGotNotify': if notification.data.event == 'conference' and notification.data.body: try: conference_info = ConferenceDocument.parse(notification.data.body) except ParserError: pass else: notification_center.post_notification('SIPSessionGotConferenceInfo', sender=self.session, data=NotificationData(conference_info=conference_info)) elif notification.name == 'SIPSubscriptionDidEnd': break except SIPSubscriptionDidFail: self._command_channel.send(Command('subscribe')) notification_center.remove_observer(self, sender=self._subscription) except InterruptSubscription, e: if not self.subscribed: command.signal(e) if self._subscription is not None: notification_center.remove_observer(self, sender=self._subscription) try: self._subscription.end(timeout=2) except SIPCoreError: pass except TerminateSubscription, e: if not self.subscribed: command.signal(e) if self._subscription is not None: try: self._subscription.end(timeout=2) except SIPCoreError: pass else: try: while True: notification = self._data_channel.wait() if notification.sender is self._subscription and notification.name == 'SIPSubscriptionDidEnd': break except SIPSubscriptionDidFail: pass finally: notification_center.remove_observer(self, sender=self._subscription) except SubscriptionError, e: if 'min_expires' in e.attributes: command = Command('subscribe', command.event, refresh_interval=e.attributes['min_expires']) else: command = Command('subscribe', command.event) self._subscription_timer = reactor.callLater(e.timeout, self._command_channel.send, command) finally: self.subscribed = False self._subscription = None self._subscription_proc = None @run_in_twisted_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_SIPSubscriptionDidStart(self, notification): self._data_channel.send(notification) def _NH_SIPSubscriptionDidEnd(self, notification): self._data_channel.send(notification) def _NH_SIPSubscriptionDidFail(self, notification): self._data_channel.send_exception(SIPSubscriptionDidFail(notification.data)) def _NH_SIPSubscriptionGotNotify(self, notification): self._data_channel.send(notification) def _NH_SIPSessionDidStart(self, notification): if self.session.remote_focus: self._activate() @run_in_green_thread def _NH_SIPSessionDidFail(self, notification): self._terminate() @run_in_green_thread def _NH_SIPSessionDidEnd(self, notification): self._terminate() def _NH_SIPSessionDidRenegotiateStreams(self, notification): if self.session.remote_focus and not self.active: self._activate() elif not self.session.remote_focus and self.active: self._deactivate() def _NH_NetworkConditionsDidChange(self, notification): if self.active: self._resubscribe() class TransferInfo(object): def __init__(self, referred_by=None, replaced_dialog_id=None): self.referred_by = referred_by self.replaced_dialog_id = replaced_dialog_id class TransferHandler(object): implements(IObserver) def __init__(self, session): self.state = None self.direction = None self.new_session = None self.session = session notification_center = NotificationCenter() notification_center.add_observer(self, sender=self.session) notification_center.add_observer(self, sender=self.session._invitation) self._command_channel = coros.queue() self._data_channel = coros.queue() self._proc = proc.spawn(self._run) def _run(self): while True: command = self._command_channel.wait() handler = getattr(self, '_CH_%s' % command.name) handler(command) self.direction = None self.state = None def _CH_incoming_transfer(self, command): self.direction = 'incoming' notification_center = NotificationCenter() refer_to_hdr = command.data.headers.get('Refer-To') target = SIPURI.parse(refer_to_hdr.uri) referred_by_hdr = command.data.headers.get('Referred-By', None) if referred_by_hdr is not None: origin = referred_by_hdr.body else: origin = str(self.session.remote_identity.uri) try: while True: try: notification = self._data_channel.wait() except SIPInvitationTransferDidFail: self.state = 'failed' return else: if notification.name == 'SIPInvitationTransferDidStart': self.state = 'starting' refer_to_uri = SIPURI.new(target) refer_to_uri.headers = {} refer_to_uri.parameters = {} notification_center.post_notification('SIPSessionTransferNewIncoming', self.session, NotificationData(transfer_destination=refer_to_uri)) elif notification.name == 'SIPSessionTransferDidStart': break elif notification.name == 'SIPSessionTransferDidFail': self.state = 'failed' try: self.session._invitation.notify_transfer_progress(notification.data.code, notification.data.reason) except SIPCoreError: return while True: try: notification = self._data_channel.wait() except SIPInvitationTransferDidFail: return self.state = 'started' transfer_info = TransferInfo(referred_by=origin) try: replaces_hdr = target.headers.pop('Replaces') call_id, rest = replaces_hdr.split(';', 1) params = dict((item.split('=') for item in rest.split(';'))) to_tag = params.get('to-tag') from_tag = params.get('from-tag') except (KeyError, ValueError): pass else: transfer_info.replaced_dialog_id = DialogID(call_id, local_tag=from_tag, remote_tag=to_tag) settings = SIPSimpleSettings() account = self.session.account if account is BonjourAccount(): uri = target elif account.sip.outbound_proxy is not None and account.sip.outbound_proxy.transport in settings.sip.transport_list: uri = SIPURI(host=account.sip.outbound_proxy.host, port=account.sip.outbound_proxy.port, parameters={'transport': account.sip.outbound_proxy.transport}) elif account.sip.always_use_my_proxy: uri = SIPURI(host=account.id.domain) else: uri = target lookup = DNSLookup() try: routes = lookup.lookup_sip_proxy(uri, settings.sip.transport_list).wait() except DNSLookupError, e: self.state = 'failed' notification_center.post_notification('SIPSessionTransferDidFail', sender=self.session, data=NotificationData(code=0, reason="DNS lookup failed: {}".format(e))) try: self.session._invitation.notify_transfer_progress(480) except SIPCoreError: return while True: try: self._data_channel.wait() except SIPInvitationTransferDidFail: return return self.new_session = Session(account) notification_center = NotificationCenter() notification_center.add_observer(self, sender=self.new_session) self.new_session.connect(ToHeader(target), routes=routes, streams=[MediaStreamRegistry.AudioStream()], transfer_info=transfer_info) while True: try: notification = self._data_channel.wait() except SIPInvitationTransferDidFail: return if notification.name == 'SIPInvitationTransferDidEnd': return except proc.ProcExit: if self.new_session is not None: notification_center.remove_observer(self, sender=self.new_session) self.new_session = None raise def _CH_outgoing_transfer(self, command): self.direction = 'outgoing' notification_center = NotificationCenter() self.state = 'starting' while True: try: notification = self._data_channel.wait() except SIPInvitationTransferDidFail, e: self.state = 'failed' notification_center.post_notification('SIPSessionTransferDidFail', sender=self.session, data=NotificationData(code=e.data.code, reason=e.data.reason)) return if notification.name == 'SIPInvitationTransferDidStart': self.state = 'started' notification_center.post_notification('SIPSessionTransferDidStart', sender=self.session) elif notification.name == 'SIPInvitationTransferDidEnd': self.state = 'ended' self.session.end() notification_center.post_notification('SIPSessionTransferDidEnd', sender=self.session) return def _terminate(self): notification_center = NotificationCenter() notification_center.remove_observer(self, sender=self.session._invitation) notification_center.remove_observer(self, sender=self.session) self._proc.kill() self._proc = None self._command_channel = None self._data_channel = None self.session = None @run_in_twisted_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_SIPInvitationTransferNewIncoming(self, notification): self._command_channel.send(Command('incoming_transfer', data=notification.data)) def _NH_SIPInvitationTransferNewOutgoing(self, notification): self._command_channel.send(Command('outgoing_transfer', data=notification.data)) def _NH_SIPInvitationTransferDidStart(self, notification): self._data_channel.send(notification) def _NH_SIPInvitationTransferDidFail(self, notification): self._data_channel.send_exception(SIPInvitationTransferDidFail(notification.data)) def _NH_SIPInvitationTransferDidEnd(self, notification): self._data_channel.send(notification) def _NH_SIPInvitationTransferGotNotify(self, notification): if notification.data.event == 'refer' and notification.data.body: match = sipfrag_re.match(notification.data.body) if match: code = int(match.group('code')) reason = match.group('reason') notification.center.post_notification('SIPSessionTransferGotProgress', sender=self.session, data=NotificationData(code=code, reason=reason)) def _NH_SIPSessionTransferDidStart(self, notification): if notification.sender is self.session and self.state == 'starting': self._data_channel.send(notification) def _NH_SIPSessionTransferDidFail(self, notification): if notification.sender is self.session and self.state == 'starting': self._data_channel.send(notification) def _NH_SIPSessionGotRingIndication(self, notification): if notification.sender is self.new_session and self.session is not None: try: self.session._invitation.notify_transfer_progress(180) except SIPCoreError: pass def _NH_SIPSessionGotProvisionalResponse(self, notification): if notification.sender is self.new_session and self.session is not None: try: self.session._invitation.notify_transfer_progress(notification.data.code, notification.data.reason) except SIPCoreError: pass def _NH_SIPSessionDidStart(self, notification): if notification.sender is self.new_session: notification.center.remove_observer(self, sender=notification.sender) self.new_session = None if self.session is not None: notification.center.post_notification('SIPSessionTransferDidEnd', sender=self.session) if self.state == 'started': try: self.session._invitation.notify_transfer_progress(200) except SIPCoreError: pass self.state = 'ended' self.session.end() def _NH_SIPSessionDidEnd(self, notification): if notification.sender is self.new_session: # If any stream fails to start we won't get SIPSessionDidFail, we'll get here instead notification.center.remove_observer(self, sender=notification.sender) self.new_session = None if self.session is not None: notification.center.post_notification('SIPSessionTransferDidFail', sender=self.session, data=NotificationData(code=500, reason='internal error')) if self.state == 'started': try: self.session._invitation.notify_transfer_progress(500) except SIPCoreError: pass self.state = 'failed' else: self._terminate() def _NH_SIPSessionDidFail(self, notification): if notification.sender is self.new_session: notification.center.remove_observer(self, sender=notification.sender) self.new_session = None if self.session is not None: notification.center.post_notification('SIPSessionTransferDidFail', sender=self.session, data=NotificationData(code=notification.data.code or 500, reason=notification.data.reason)) if self.state == 'started': try: self.session._invitation.notify_transfer_progress(notification.data.code or 500, notification.data.reason) except SIPCoreError: pass self.state = 'failed' else: self._terminate() class OptionalTag(str): def __eq__(self, other): return other is None or super(OptionalTag, self).__eq__(other) def __ne__(self, other): return not self == other def __repr__(self): return '{}({})'.format(self.__class__.__name__, super(OptionalTag, self).__repr__()) class SessionReplaceHandler(object): implements(IObserver) def __init__(self, session): self.session = session def start(self): notification_center = NotificationCenter() notification_center.add_observer(self, sender=self.session) notification_center.add_observer(self, sender=self.session.replaced_session) def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_SIPSessionDidStart(self, notification): notification.center.remove_observer(self, sender=self.session) notification.center.remove_observer(self, sender=self.session.replaced_session) self.session.replaced_session.end() self.session.replaced_session = None self.session = None def _NH_SIPSessionDidFail(self, notification): if notification.sender is self.session: notification.center.remove_observer(self, sender=self.session) notification.center.remove_observer(self, sender=self.session.replaced_session) self.session.replaced_session = None self.session = None _NH_SIPSessionDidEnd = _NH_SIPSessionDidFail class Session(object): implements(IObserver) media_stream_timeout = 15 short_reinvite_timeout = 5 def __init__(self, account): self.account = account self.direction = None self.end_time = None self.on_hold = False self.proposed_streams = None self.route = None self.state = None self.start_time = None self.streams = None self.transport = None self.local_focus = False self.remote_focus = False self.greenlet = None self.conference = None self.replaced_session = None self.transfer_handler = None self.transfer_info = None self._channel = coros.queue() self._hold_in_progress = False self._invitation = None self._local_identity = None self._remote_identity = None self._lock = RLock() def init_incoming(self, invitation, data): notification_center = NotificationCenter() remote_sdp = invitation.sdp.proposed_remote self.proposed_streams = [] if remote_sdp: for index, media_stream in enumerate(remote_sdp.media): if media_stream.port != 0: for stream_type in MediaStreamRegistry: try: stream = stream_type.new_from_sdp(self, remote_sdp, index) except UnknownStreamError: continue except InvalidStreamError as e: log.error("Invalid stream: {}".format(e)) break except Exception as e: log.exception("Exception occurred while setting up stream from SDP: {}".format(e)) break else: stream.index = index self.proposed_streams.append(stream) break self.direction = 'incoming' self.state = 'incoming' self.transport = invitation.transport self._invitation = invitation self.conference = ConferenceHandler(self) self.transfer_handler = TransferHandler(self) if 'isfocus' in invitation.remote_contact_header.parameters: self.remote_focus = True if 'Referred-By' in data.headers or 'Replaces' in data.headers: self.transfer_info = TransferInfo() if 'Referred-By' in data.headers: self.transfer_info.referred_by = data.headers['Referred-By'].body if 'Replaces' in data.headers: replaces_header = data.headers.get('Replaces') # Because we only allow the remote tag to be optional, it can only match established dialogs and early outgoing dialogs, but not early incoming dialogs, # which according to RFC3891 should be rejected with 481 (which will happen automatically by never matching them). if replaces_header.early_only or replaces_header.from_tag == '0': replaced_dialog_id = DialogID(replaces_header.call_id, local_tag=replaces_header.to_tag, remote_tag=OptionalTag(replaces_header.from_tag)) else: replaced_dialog_id = DialogID(replaces_header.call_id, local_tag=replaces_header.to_tag, remote_tag=replaces_header.from_tag) session_manager = SessionManager() try: replaced_session = next(session for session in session_manager.sessions if session.dialog_id == replaced_dialog_id) except StopIteration: invitation.send_response(481) return else: # Any matched dialog at this point is either established, terminated or early outgoing. if replaced_session.state in ('terminating', 'terminated'): invitation.send_response(603) return elif replaced_session.dialog_id.remote_tag is not None and replaces_header.early_only: # The replaced dialog is established, but the early-only flag is set invitation.send_response(486) return self.replaced_session = replaced_session self.transfer_info.replaced_dialog_id = replaced_dialog_id replace_handler = SessionReplaceHandler(self) replace_handler.start() notification_center.add_observer(self, sender=invitation) notification_center.post_notification('SIPSessionNewIncoming', sender=self, data=NotificationData(streams=self.proposed_streams[:], headers=data.headers)) @transition_state(None, 'connecting') @run_in_green_thread def connect(self, to_header, routes, streams, is_focus=False, transfer_info=None, extra_headers=None): self.greenlet = api.getcurrent() notification_center = NotificationCenter() settings = SIPSimpleSettings() connected = False received_code = 0 received_reason = None unhandled_notifications = [] extra_headers = extra_headers or [] if {'to', 'from', 'via', 'contact', 'route', 'record-route'}.intersection(header.name.lower() for header in extra_headers): raise RuntimeError('invalid header in extra_headers: To, From, Via, Contact, Route and Record-Route headers are not allowed') self.direction = 'outgoing' self.proposed_streams = streams self.route = routes[0] self.transport = self.route.transport self.local_focus = is_focus self._invitation = Invitation() self._local_identity = FromHeader(self.account.uri, self.account.display_name) self._remote_identity = to_header self.conference = ConferenceHandler(self) self.transfer_handler = TransferHandler(self) self.transfer_info = transfer_info notification_center.add_observer(self, sender=self._invitation) notification_center.post_notification('SIPSessionNewOutgoing', sender=self, data=NotificationData(streams=streams[:])) for stream in self.proposed_streams: notification_center.add_observer(self, sender=stream) stream.initialize(self, direction='outgoing') try: wait_count = len(self.proposed_streams) while wait_count > 0: notification = self._channel.wait() if notification.name == 'MediaStreamDidInitialize': wait_count -= 1 try: contact_uri = self.account.contact[PublicGRUUIfAvailable, self.route] local_ip = host.outgoing_ip_for(self.route.address) if local_ip is None: raise ValueError("could not get outgoing IP address") except (KeyError, ValueError), e: for stream in self.proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() self._fail(originator='local', code=480, reason=sip_status_messages[480], error=str(e)) return connection = SDPConnection(local_ip) local_sdp = SDPSession(local_ip, name=settings.user_agent) for index, stream in enumerate(self.proposed_streams): stream.index = index media = stream.get_local_media(remote_sdp=None, index=index) if media.connection is None or (media.connection is not None and not media.has_ice_attributes and not media.has_ice_candidates): media.connection = connection local_sdp.media.append(media) from_header = FromHeader(self.account.uri, self.account.display_name) route_header = RouteHeader(self.route.uri) contact_header = ContactHeader(contact_uri) if is_focus: contact_header.parameters['isfocus'] = None if self.transfer_info is not None: if self.transfer_info.referred_by is not None: extra_headers.append(Header('Referred-By', self.transfer_info.referred_by)) if self.transfer_info.replaced_dialog_id is not None: dialog_id = self.transfer_info.replaced_dialog_id extra_headers.append(ReplacesHeader(dialog_id.call_id, dialog_id.local_tag, dialog_id.remote_tag)) self._invitation.send_invite(to_header.uri, from_header, to_header, route_header, contact_header, local_sdp, self.account.credentials, extra_headers) try: with api.timeout(settings.sip.invite_timeout): while True: notification = self._channel.wait() if notification.name == 'SIPInvitationGotSDPUpdate': if notification.data.succeeded: local_sdp = notification.data.local_sdp remote_sdp = notification.data.remote_sdp break else: for stream in self.proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() self._fail(originator='remote', code=0, reason=None, error='SDP negotiation failed: %s' % notification.data.error) return elif notification.name == 'SIPInvitationChangedState': if notification.data.state == 'early': if notification.data.code == 180: notification_center.post_notification('SIPSessionGotRingIndication', self) notification_center.post_notification('SIPSessionGotProvisionalResponse', self, NotificationData(code=notification.data.code, reason=notification.data.reason)) elif notification.data.state == 'connecting': received_code = notification.data.code received_reason = notification.data.reason elif notification.data.state == 'connected': if not connected: connected = True notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local', method='INVITE', code=received_code, reason=received_reason)) else: unhandled_notifications.append(notification) elif notification.data.state == 'disconnected': raise InvitationDisconnectedError(notification.sender, notification.data) except api.TimeoutError: self.end() return notification_center.post_notification('SIPSessionWillStart', sender=self) stream_map = dict((stream.index, stream) for stream in self.proposed_streams) for index, local_media in enumerate(local_sdp.media): remote_media = remote_sdp.media[index] stream = stream_map[index] if remote_media.port: # TODO: check if port is also 0 in local_sdp. In that case PJSIP disabled the stream because # negotiation failed. If there are more streams, however, the negotiation is considered successful as a # whole, so while we built a normal SDP, PJSIP modified it and sent it to the other side. That's kind io # OK, but we cannot really start the stream. -Saul stream.start(local_sdp, remote_sdp, index) else: notification_center.remove_observer(self, sender=stream) self.proposed_streams.remove(stream) del stream_map[stream.index] stream.deactivate() stream.end() removed_streams = [stream for stream in self.proposed_streams if stream.index >= len(local_sdp.media)] for stream in removed_streams: notification_center.remove_observer(self, sender=stream) self.proposed_streams.remove(stream) del stream_map[stream.index] stream.deactivate() stream.end() invitation_notifications = [] with api.timeout(self.media_stream_timeout): wait_count = len(self.proposed_streams) while wait_count > 0: notification = self._channel.wait() if notification.name == 'MediaStreamDidStart': wait_count -= 1 elif notification.name == 'SIPInvitationChangedState': invitation_notifications.append(notification) for notification in invitation_notifications: self._channel.send(notification) while not connected or self._channel: notification = self._channel.wait() if notification.name == 'SIPInvitationChangedState': if notification.data.state == 'early': if notification.data.code == 180: notification_center.post_notification('SIPSessionGotRingIndication', self) notification_center.post_notification('SIPSessionGotProvisionalResponse', self, NotificationData(code=notification.data.code, reason=notification.data.reason)) elif notification.data.state == 'connecting': received_code = notification.data.code received_reason = notification.data.reason elif notification.data.state == 'connected': if not connected: connected = True notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local', method='INVITE', code=received_code, reason=received_reason)) else: unhandled_notifications.append(notification) elif notification.data.state == 'disconnected': raise InvitationDisconnectedError(notification.sender, notification.data) except (MediaStreamDidNotInitializeError, MediaStreamDidFailError, api.TimeoutError), e: for stream in self.proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() if isinstance(e, api.TimeoutError): error = 'media stream timed-out while starting' elif isinstance(e, MediaStreamDidNotInitializeError): error = 'media stream did not initialize: %s' % e.data.reason else: error = 'media stream failed: %s' % e.data.reason self._fail(originator='local', code=0, reason=None, error=error) except InvitationDisconnectedError, e: notification_center.remove_observer(self, sender=self._invitation) for stream in self.proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() self.state = 'terminated' # As weird as it may sound, PJSIP accepts a BYE even without receiving a final response to the INVITE if e.data.prev_state in ('connecting', 'connected') or getattr(e.data, 'method', None) == 'BYE': notification_center.post_notification('SIPSessionWillEnd', self, NotificationData(originator=e.data.originator)) if e.data.originator == 'remote': notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method=e.data.method, code=200, reason=sip_status_messages[200])) self.end_time = ISOTimestamp.now() notification_center.post_notification('SIPSessionDidEnd', self, NotificationData(originator=e.data.originator, end_reason=e.data.disconnect_reason)) else: if e.data.originator == 'remote': notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local', method='INVITE', code=e.data.code, reason=e.data.reason)) code = e.data.code reason = e.data.reason elif e.data.disconnect_reason == 'timeout': code = 408 reason = 'timeout' else: # TODO: we should know *exactly* when there are set -Saul code = getattr(e.data, 'code', 0) reason = getattr(e.data, 'reason', 'Session disconnected') if e.data.originator == 'remote' and code // 100 == 3: redirect_identities = e.data.headers.get('Contact', []) else: redirect_identities = None notification_center.post_notification('SIPSessionDidFail', self, NotificationData(originator=e.data.originator, code=code, reason=reason, failure_reason=e.data.disconnect_reason, redirect_identities=redirect_identities)) self.greenlet = None except SIPCoreError, e: for stream in self.proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() self._fail(originator='local', code=0, reason=None, error='SIP core error: %s' % str(e)) else: self.greenlet = None self.state = 'connected' self.streams = self.proposed_streams self.proposed_streams = None self.start_time = ISOTimestamp.now() any_stream_ice = any(getattr(stream, 'ice_active', False) for stream in self.streams) if any_stream_ice: self._reinvite_after_ice() notification_center.post_notification('SIPSessionDidStart', self, NotificationData(streams=self.streams[:])) for notification in unhandled_notifications: self.handle_notification(notification) if self._hold_in_progress: self._send_hold() def _reinvite_after_ice(self): # This function does not do any error checking, it's designed to be called at the end of connect and add_stream self.state = 'sending_proposal' self.greenlet = api.getcurrent() notification_center = NotificationCenter() local_sdp = SDPSession.new(self._invitation.sdp.active_local) local_sdp.version += 1 for index, stream in enumerate(self.streams): local_sdp.media[index] = stream.get_local_media(remote_sdp=None, index=index) self._invitation.send_reinvite(sdp=local_sdp) received_invitation_state = False received_sdp_update = False try: with api.timeout(self.short_reinvite_timeout): while not received_invitation_state or not received_sdp_update: notification = self._channel.wait() if notification.name == 'SIPInvitationGotSDPUpdate': received_sdp_update = True if notification.data.succeeded: local_sdp = notification.data.local_sdp remote_sdp = notification.data.remote_sdp for index, stream in enumerate(self.streams): stream.update(local_sdp, remote_sdp, index) else: return elif notification.name == 'SIPInvitationChangedState': if notification.data.state == 'connected' and notification.data.sub_state == 'normal': received_invitation_state = True notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local', method='INVITE', code=notification.data.code, reason=notification.data.reason)) elif notification.data.state == 'disconnected': self.end() return except Exception: pass finally: self.state = 'connected' self.greenlet = None @check_state(['incoming', 'received_proposal']) @run_in_green_thread def send_ring_indication(self): try: self._invitation.send_response(180) except SIPCoreInvalidStateError: pass # The INVITE session might have already been cancelled; ignore the error @transition_state('incoming', 'accepting') @run_in_green_thread def accept(self, streams, is_focus=False, extra_headers=None): self.greenlet = api.getcurrent() notification_center = NotificationCenter() settings = SIPSimpleSettings() self.local_focus = is_focus connected = False unhandled_notifications = [] extra_headers = extra_headers or [] if {'to', 'from', 'via', 'contact', 'route', 'record-route'}.intersection(header.name.lower() for header in extra_headers): raise RuntimeError('invalid header in extra_headers: To, From, Via, Contact, Route and Record-Route headers are not allowed') if self.proposed_streams: for stream in self.proposed_streams: if stream in streams: notification_center.add_observer(self, sender=stream) stream.initialize(self, direction='incoming') else: for index, stream in enumerate(streams): notification_center.add_observer(self, sender=stream) stream.index = index stream.initialize(self, direction='outgoing') self.proposed_streams = streams wait_count = len(self.proposed_streams) try: while wait_count > 0: notification = self._channel.wait() if notification.name == 'MediaStreamDidInitialize': wait_count -= 1 remote_sdp = self._invitation.sdp.proposed_remote sdp_connection = remote_sdp.connection or (media.connection for media in remote_sdp.media if media.connection is not None).next() local_ip = host.outgoing_ip_for(sdp_connection.address) if sdp_connection.address != '0.0.0.0' else sdp_connection.address if local_ip is None: for stream in self.proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() self._fail(originator='local', code=500, reason=sip_status_messages[500], error='could not get local IP address') return connection = SDPConnection(local_ip) local_sdp = SDPSession(local_ip, name=settings.user_agent) if remote_sdp: stream_map = dict((stream.index, stream) for stream in self.proposed_streams) for index, media in enumerate(remote_sdp.media): stream = stream_map.get(index, None) if stream is not None: media = stream.get_local_media(remote_sdp=remote_sdp, index=index) if not media.has_ice_attributes and not media.has_ice_candidates: media.connection = connection else: media = SDPMediaStream.new(media) media.connection = connection media.port = 0 media.attributes = [] media.bandwidth_info = [] local_sdp.media.append(media) else: for index, stream in enumerate(self.proposed_streams): stream.index = index media = stream.get_local_media(remote_sdp=None, index=index) if media.connection is None or (media.connection is not None and not media.has_ice_attributes and not media.has_ice_candidates): media.connection = connection local_sdp.media.append(media) contact_header = ContactHeader.new(self._invitation.local_contact_header) try: local_contact_uri = self.account.contact[PublicGRUU, self._invitation.transport] except KeyError: pass else: contact_header.uri = local_contact_uri if is_focus: contact_header.parameters['isfocus'] = None self._invitation.send_response(200, contact_header=contact_header, sdp=local_sdp, extra_headers=extra_headers) notification_center.post_notification('SIPSessionWillStart', sender=self) # Local and remote SDPs will be set after the 200 OK is sent while True: notification = self._channel.wait() if notification.name == 'SIPInvitationGotSDPUpdate': if notification.data.succeeded: local_sdp = notification.data.local_sdp remote_sdp = notification.data.remote_sdp break else: if not connected: # we could not have got a SIPInvitationGotSDPUpdate if we did not get an ACK connected = True notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=200, reason=sip_status_messages[200], ack_received=True)) for stream in self.proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() self._fail(originator='remote', code=0, reason=None, error='SDP negotiation failed: %s' % notification.data.error) return elif notification.name == 'SIPInvitationChangedState': if notification.data.state == 'connected': if not connected: connected = True notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=200, reason=sip_status_messages[200], ack_received=True)) elif notification.data.prev_state == 'connected': unhandled_notifications.append(notification) elif notification.data.state == 'disconnected': raise InvitationDisconnectedError(notification.sender, notification.data) wait_count = 0 stream_map = dict((stream.index, stream) for stream in self.proposed_streams) for index, local_media in enumerate(local_sdp.media): remote_media = remote_sdp.media[index] stream = stream_map.get(index, None) if stream is not None: if remote_media.port: wait_count += 1 stream.start(local_sdp, remote_sdp, index) else: notification_center.remove_observer(self, sender=stream) self.proposed_streams.remove(stream) del stream_map[stream.index] stream.deactivate() stream.end() removed_streams = [stream for stream in self.proposed_streams if stream.index >= len(local_sdp.media)] for stream in removed_streams: notification_center.remove_observer(self, sender=stream) self.proposed_streams.remove(stream) del stream_map[stream.index] stream.deactivate() stream.end() with api.timeout(self.media_stream_timeout): while wait_count > 0 or not connected or self._channel: notification = self._channel.wait() if notification.name == 'MediaStreamDidStart': wait_count -= 1 elif notification.name == 'SIPInvitationChangedState': if notification.data.state == 'connected': if not connected: connected = True notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=200, reason='OK', ack_received=True)) elif notification.data.prev_state == 'connected': unhandled_notifications.append(notification) elif notification.data.state == 'disconnected': raise InvitationDisconnectedError(notification.sender, notification.data) else: unhandled_notifications.append(notification) except (MediaStreamDidNotInitializeError, MediaStreamDidFailError, api.TimeoutError), e: if self._invitation.state == 'connecting': ack_received = False if isinstance(e, api.TimeoutError) and wait_count == 0 else 'unknown' # pjsip's invite session object does not inform us whether the ACK was received or not notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=200, reason='OK', ack_received=ack_received)) elif self._invitation.state == 'connected' and not connected: # we didn't yet get to process the SIPInvitationChangedState (state -> connected) notification notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=200, reason='OK', ack_received=True)) for stream in self.proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() reason_header = None if isinstance(e, api.TimeoutError): if wait_count > 0: error = 'media stream timed-out while starting' else: error = 'No ACK received' reason_header = ReasonHeader('SIP') reason_header.cause = 500 reason_header.text = 'Missing ACK' elif isinstance(e, MediaStreamDidNotInitializeError): error = 'media stream did not initialize: %s' % e.data.reason reason_header = ReasonHeader('SIP') reason_header.cause = 500 reason_header.text = 'media stream did not initialize' else: error = 'media stream failed: %s' % e.data.reason reason_header = ReasonHeader('SIP') reason_header.cause = 500 reason_header.text = 'media stream failed to start' self.start_time = ISOTimestamp.now() if self._invitation.state in ('incoming', 'early'): self._fail(originator='local', code=500, reason=sip_status_messages[500], error=error, reason_header=reason_header) else: self._fail(originator='local', code=0, reason=None, error=error, reason_header=reason_header) except InvitationDisconnectedError, e: notification_center.remove_observer(self, sender=self._invitation) for stream in self.proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() self.state = 'terminated' if e.data.prev_state in ('incoming', 'early'): notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=487, reason='Session Cancelled', ack_received='unknown')) notification_center.post_notification('SIPSessionDidFail', self, NotificationData(originator='remote', code=487, reason='Session Cancelled', failure_reason=e.data.disconnect_reason, redirect_identities=None)) elif e.data.prev_state == 'connecting' and e.data.disconnect_reason == 'missing ACK': notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=200, reason='OK', ack_received=False)) notification_center.post_notification('SIPSessionDidFail', self, NotificationData(originator='local', code=200, reason=sip_status_messages[200], failure_reason=e.data.disconnect_reason, redirect_identities=None)) else: notification_center.post_notification('SIPSessionWillEnd', self, NotificationData(originator='remote')) notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method=getattr(e.data, 'method', 'INVITE'), code=200, reason='OK')) self.end_time = ISOTimestamp.now() notification_center.post_notification('SIPSessionDidEnd', self, NotificationData(originator='remote', end_reason=e.data.disconnect_reason)) self.greenlet = None except SIPCoreInvalidStateError: # the only reason for which this error can be thrown is if invitation.send_response was called after the INVITE session was cancelled by the remote party notification_center.remove_observer(self, sender=self._invitation) for stream in self.proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() self.greenlet = None self.state = 'terminated' notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=487, reason='Session Cancelled', ack_received='unknown')) notification_center.post_notification('SIPSessionDidFail', self, NotificationData(originator='remote', code=487, reason='Session Cancelled', failure_reason='user request', redirect_identities=None)) except SIPCoreError, e: for stream in self.proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() self._fail(originator='local', code=500, reason=sip_status_messages[500], error='SIP core error: %s' % str(e)) else: self.greenlet = None self.state = 'connected' self.streams = self.proposed_streams self.proposed_streams = None self.start_time = ISOTimestamp.now() notification_center.post_notification('SIPSessionDidStart', self, NotificationData(streams=self.streams[:])) for notification in unhandled_notifications: self.handle_notification(notification) if self._hold_in_progress: self._send_hold() finally: self.greenlet = None @transition_state('incoming', 'terminating') @run_in_green_thread def reject(self, code=603, reason=None): self.greenlet = api.getcurrent() notification_center = NotificationCenter() try: self._invitation.send_response(code, reason) with api.timeout(1): while True: notification = self._channel.wait() if notification.name == 'SIPInvitationChangedState': if notification.data.state == 'disconnected': ack_received = notification.data.disconnect_reason != 'missing ACK' notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=code, reason=sip_status_messages[code], ack_received=ack_received)) break except SIPCoreInvalidStateError: # the only reason for which this error can be thrown is if invitation.send_response was called after the INVITE session was cancelled by the remote party self.greenlet = None except SIPCoreError, e: self._fail(originator='local', code=500, reason=sip_status_messages[500], error='SIP core error: %s' % str(e)) except api.TimeoutError: notification_center.remove_observer(self, sender=self._invitation) self.greenlet = None self.state = 'terminated' notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=code, reason=sip_status_messages[code], ack_received=False)) notification_center.post_notification('SIPSessionDidFail', self, NotificationData(originator='local', code=code, reason=sip_status_messages[code], failure_reason='timeout', redirect_identities=None)) else: notification_center.remove_observer(self, sender=self._invitation) self.greenlet = None self.state = 'terminated' notification_center.post_notification('SIPSessionDidFail', self, NotificationData(originator='local', code=code, reason=sip_status_messages[code], failure_reason='user request', redirect_identities=None)) finally: self.greenlet = None @transition_state('received_proposal', 'accepting_proposal') @run_in_green_thread def accept_proposal(self, streams): self.greenlet = api.getcurrent() notification_center = NotificationCenter() unhandled_notifications = [] streams = [stream for stream in streams if stream in self.proposed_streams] for stream in streams: notification_center.add_observer(self, sender=stream) stream.initialize(self, direction='incoming') try: wait_count = len(streams) while wait_count > 0: notification = self._channel.wait() if notification.name == 'MediaStreamDidInitialize': wait_count -= 1 local_sdp = SDPSession.new(self._invitation.sdp.active_local) local_sdp.version += 1 remote_sdp = self._invitation.sdp.proposed_remote connection = SDPConnection(local_sdp.address) stream_map = dict((stream.index, stream) for stream in streams) for index, media in enumerate(remote_sdp.media): stream = stream_map.get(index, None) if stream is not None: media = stream.get_local_media(remote_sdp=remote_sdp, index=index) if not media.has_ice_attributes and not media.has_ice_candidates: media.connection = connection if index < len(local_sdp.media): local_sdp.media[index] = media else: local_sdp.media.append(media) elif index >= len(local_sdp.media): # actually == is sufficient media = SDPMediaStream.new(media) media.connection = connection media.port = 0 media.attributes = [] media.bandwidth_info = [] local_sdp.media.append(media) self._invitation.send_response(200, sdp=local_sdp) prev_on_hold_streams = set(stream for stream in self.streams if stream.hold_supported and stream.on_hold_by_remote) received_invitation_state = False received_sdp_update = False while not received_invitation_state or not received_sdp_update: notification = self._channel.wait() if notification.name == 'SIPInvitationGotSDPUpdate': received_sdp_update = True if notification.data.succeeded: local_sdp = notification.data.local_sdp remote_sdp = notification.data.remote_sdp for stream in self.streams: stream.update(local_sdp, remote_sdp, stream.index) else: self._fail_proposal(originator='remote', error='SDP negotiation failed: %s' % notification.data.error) return elif notification.name == 'SIPInvitationChangedState': if notification.data.state == 'connected' and notification.data.sub_state == 'normal': notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=200, reason=sip_status_messages[200], ack_received='unknown')) received_invitation_state = True elif notification.data.state == 'disconnected': raise InvitationDisconnectedError(notification.sender, notification.data) on_hold_streams = set(stream for stream in self.streams if stream.hold_supported and stream.on_hold_by_remote) if on_hold_streams != prev_on_hold_streams: hold_supported_streams = (stream for stream in self.streams if stream.hold_supported) notification_center.post_notification('SIPSessionDidChangeHoldState', self, NotificationData(originator='remote', on_hold=bool(on_hold_streams), partial=bool(on_hold_streams) and any(not stream.on_hold_by_remote for stream in hold_supported_streams))) for stream in streams: # TODO: check if port is 0 in local_sdp. In that case PJSIP disabled the stream because # negotiation failed. If there are more streams, however, the negotiation is considered successful as a # whole, so while we built a normal SDP, PJSIP modified it and sent it to the other side. That's kind of # OK, but we cannot really start the stream. -Saul stream.start(local_sdp, remote_sdp, stream.index) with api.timeout(self.media_stream_timeout): wait_count = len(streams) while wait_count > 0 or self._channel: notification = self._channel.wait() if notification.name == 'MediaStreamDidStart': wait_count -= 1 else: unhandled_notifications.append(notification) except api.TimeoutError: self._fail_proposal(originator='remote', error='media stream timed-out while starting') except MediaStreamDidNotInitializeError as e: self._fail_proposal(originator='remote', error='media stream did not initialize: {.data.reason}'.format(e)) except MediaStreamDidFailError as e: self._fail_proposal(originator='remote', error='media stream failed: {.data.reason}'.format(e)) except InvitationDisconnectedError, e: self._fail_proposal(originator='remote', error='session ended') notification = Notification('SIPInvitationChangedState', e.invitation, e.data) notification.center = notification_center self.handle_notification(notification) except SIPCoreError, e: self._fail_proposal(originator='remote', error='SIP core error: %s' % str(e)) else: proposed_streams = self.proposed_streams self.proposed_streams = None self.streams = self.streams + streams self.greenlet = None self.state = 'connected' notification_center.post_notification('SIPSessionProposalAccepted', self, NotificationData(originator='remote', accepted_streams=streams, proposed_streams=proposed_streams)) notification_center.post_notification('SIPSessionDidRenegotiateStreams', self, NotificationData(originator='remote', added_streams=streams, removed_streams=[])) for notification in unhandled_notifications: self.handle_notification(notification) if self._hold_in_progress: self._send_hold() finally: self.greenlet = None @transition_state('received_proposal', 'rejecting_proposal') @run_in_green_thread def reject_proposal(self, code=488, reason=None): self.greenlet = api.getcurrent() notification_center = NotificationCenter() try: self._invitation.send_response(code, reason) with api.timeout(1, None): while True: notification = self._channel.wait() if notification.name == 'SIPInvitationChangedState': if notification.data.state == 'connected' and notification.data.sub_state == 'normal': notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=code, reason=sip_status_messages[code], ack_received='unknown')) break except SIPCoreError, e: self._fail_proposal(originator='remote', error='SIP core error: %s' % str(e)) else: proposed_streams = self.proposed_streams self.proposed_streams = None self.greenlet = None self.state = 'connected' notification_center.post_notification('SIPSessionProposalRejected', self, NotificationData(originator='remote', code=code, reason=sip_status_messages[code], proposed_streams=proposed_streams)) if self._hold_in_progress: self._send_hold() finally: self.greenlet = None def add_stream(self, stream): self.add_streams([stream]) @transition_state('connected', 'sending_proposal') @run_in_green_thread def add_streams(self, streams): streams = list(set(streams).difference(self.streams)) if not streams: self.state = 'connected' return self.greenlet = api.getcurrent() notification_center = NotificationCenter() settings = SIPSimpleSettings() unhandled_notifications = [] self.proposed_streams = streams for stream in self.proposed_streams: notification_center.add_observer(self, sender=stream) stream.initialize(self, direction='outgoing') try: wait_count = len(self.proposed_streams) while wait_count > 0: notification = self._channel.wait() if notification.name == 'MediaStreamDidInitialize': wait_count -= 1 elif notification.name == 'SIPInvitationChangedState': # This is actually the only reason for which this notification could be received if notification.data.state == 'connected' and notification.data.sub_state == 'received_proposal': self._fail_proposal(originator='local', error='received stream proposal') self.handle_notification(notification) return local_sdp = SDPSession.new(self._invitation.sdp.active_local) local_sdp.version += 1 for stream in self.proposed_streams: # Try to reuse a disabled media stream to avoid an ever-growing SDP try: index = next(index for index, media in enumerate(local_sdp.media) if media.port == 0) reuse_media = True except StopIteration: index = len(local_sdp.media) reuse_media = False stream.index = index media = stream.get_local_media(remote_sdp=None, index=index) if reuse_media: local_sdp.media[index] = media else: local_sdp.media.append(media) self._invitation.send_reinvite(sdp=local_sdp) notification_center.post_notification('SIPSessionNewProposal', sender=self, data=NotificationData(originator='local', proposed_streams=self.proposed_streams[:])) received_invitation_state = False received_sdp_update = False try: with api.timeout(settings.sip.invite_timeout): while not received_invitation_state or not received_sdp_update: notification = self._channel.wait() if notification.name == 'SIPInvitationGotSDPUpdate': received_sdp_update = True if notification.data.succeeded: local_sdp = notification.data.local_sdp remote_sdp = notification.data.remote_sdp for s in self.streams: s.update(local_sdp, remote_sdp, s.index) else: self._fail_proposal(originator='local', error='SDP negotiation failed: %s' % notification.data.error) return elif notification.name == 'SIPInvitationChangedState': if notification.data.state == 'connected' and notification.data.sub_state == 'normal': received_invitation_state = True notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local', method='INVITE', code=notification.data.code, reason=notification.data.reason)) if notification.data.code >= 300: proposed_streams = self.proposed_streams for stream in proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() self.proposed_streams = None self.greenlet = None self.state = 'connected' notification_center.post_notification('SIPSessionProposalRejected', self, NotificationData(originator='local', code=notification.data.code, reason=notification.data.reason, proposed_streams=proposed_streams)) return elif notification.data.state == 'disconnected': raise InvitationDisconnectedError(notification.sender, notification.data) except api.TimeoutError: self.cancel_proposal() return accepted_streams = [] for stream in self.proposed_streams: try: remote_media = remote_sdp.media[stream.index] except IndexError: self._fail_proposal(originator='local', error='SDP media missing in answer') return else: if remote_media.port: stream.start(local_sdp, remote_sdp, stream.index) accepted_streams.append(stream) else: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() with api.timeout(self.media_stream_timeout): wait_count = len(accepted_streams) while wait_count > 0: notification = self._channel.wait() if notification.name == 'MediaStreamDidStart': wait_count -= 1 except api.TimeoutError: self._fail_proposal(originator='local', error='media stream timed-out while starting') except MediaStreamDidNotInitializeError as e: self._fail_proposal(originator='local', error='media stream did not initialize: {.data.reason}'.format(e)) except MediaStreamDidFailError as e: self._fail_proposal(originator='local', error='media stream failed: {.data.reason}'.format(e)) except InvitationDisconnectedError, e: self._fail_proposal(originator='local', error='session ended') notification = Notification('SIPInvitationChangedState', e.invitation, e.data) notification.center = notification_center self.handle_notification(notification) except SIPCoreError, e: self._fail_proposal(originator='local', error='SIP core error: %s' % str(e)) else: self.greenlet = None self.state = 'connected' self.streams += accepted_streams proposed_streams = self.proposed_streams self.proposed_streams = None any_stream_ice = any(getattr(stream, 'ice_active', False) for stream in accepted_streams) if any_stream_ice: self._reinvite_after_ice() notification_center.post_notification('SIPSessionProposalAccepted', self, NotificationData(originator='local', accepted_streams=accepted_streams, proposed_streams=proposed_streams)) notification_center.post_notification('SIPSessionDidRenegotiateStreams', self, NotificationData(originator='local', added_streams=accepted_streams, removed_streams=[])) for notification in unhandled_notifications: self.handle_notification(notification) if self._hold_in_progress: self._send_hold() finally: self.greenlet = None def remove_stream(self, stream): self.remove_streams([stream]) @transition_state('connected', 'sending_proposal') @run_in_green_thread def remove_streams(self, streams): streams = list(set(streams).intersection(self.streams)) if not streams: self.state = 'connected' return self.greenlet = api.getcurrent() notification_center = NotificationCenter() unhandled_notifications = [] try: local_sdp = SDPSession.new(self._invitation.sdp.active_local) local_sdp.version += 1 for stream in streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() self.streams.remove(stream) media = local_sdp.media[stream.index] media.port = 0 media.attributes = [] media.bandwidth_info = [] self._invitation.send_reinvite(sdp=local_sdp) received_invitation_state = False received_sdp_update = False with api.timeout(self.short_reinvite_timeout): while not received_invitation_state or not received_sdp_update: notification = self._channel.wait() if notification.name == 'SIPInvitationGotSDPUpdate': received_sdp_update = True if notification.data.succeeded: local_sdp = notification.data.local_sdp remote_sdp = notification.data.remote_sdp for s in self.streams: s.update(local_sdp, remote_sdp, s.index) elif notification.name == 'SIPInvitationChangedState': if notification.data.state == 'connected' and notification.data.sub_state == 'normal': received_invitation_state = True notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local', method='INVITE', code=notification.data.code, reason=notification.data.reason)) if not (200 <= notification.data.code < 300): break elif notification.data.state == 'disconnected': raise InvitationDisconnectedError(notification.sender, notification.data) except InvitationDisconnectedError, e: for stream in streams: stream.end() self.greenlet = None notification = Notification('SIPInvitationChangedState', e.invitation, e.data) notification.center = notification_center self.handle_notification(notification) except (api.TimeoutError, MediaStreamDidFailError, SIPCoreError): for stream in streams: stream.end() self.end() else: for stream in streams: stream.end() self.greenlet = None self.state = 'connected' notification_center.post_notification('SIPSessionDidRenegotiateStreams', self, NotificationData(originator='local', added_streams=[], removed_streams=streams)) for notification in unhandled_notifications: self.handle_notification(notification) if self._hold_in_progress: self._send_hold() finally: self.greenlet = None @transition_state('sending_proposal', 'cancelling_proposal') @run_in_green_thread def cancel_proposal(self): if self.greenlet is not None: api.kill(self.greenlet, api.GreenletExit()) self.greenlet = api.getcurrent() notification_center = NotificationCenter() try: self._invitation.cancel_reinvite() while True: try: notification = self._channel.wait() except MediaStreamDidFailError: continue if notification.name == 'SIPInvitationChangedState': if notification.data.state == 'connected' and notification.data.sub_state == 'normal': notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=notification.data.code, reason=notification.data.reason)) if notification.data.code == 487: proposed_streams = self.proposed_streams or [] for stream in proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() self.proposed_streams = None self.state = 'connected' notification_center.post_notification('SIPSessionProposalRejected', self, NotificationData(originator='local', code=notification.data.code, reason=notification.data.reason, proposed_streams=proposed_streams)) elif notification.data.code == 200: self.end() elif notification.data.state == 'disconnected': raise InvitationDisconnectedError(notification.sender, notification.data) break except SIPCoreError, e: notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local', code=0, reason=None, failure_reason='SIP core error: %s' % str(e), redirect_identities=None)) proposed_streams = self.proposed_streams or [] for stream in proposed_streams: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() self.proposed_streams = None self.greenlet = None self.state = 'connected' notification_center.post_notification('SIPSessionProposalRejected', self, NotificationData(originator='local', code=0, reason='SIP core error: %s' % str(e), proposed_streams=proposed_streams)) except InvitationDisconnectedError, e: for stream in self.proposed_streams or []: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() self.proposed_streams = None self.greenlet = None notification = Notification('SIPInvitationChangedState', e.invitation, e.data) notification.center = notification_center self.handle_notification(notification) else: for stream in self.proposed_streams or []: notification_center.remove_observer(self, sender=stream) stream.deactivate() stream.end() self.proposed_streams = None self.greenlet = None self.state = 'connected' finally: self.greenlet = None if self._hold_in_progress: self._send_hold() @run_in_green_thread def hold(self): if self.on_hold or self._hold_in_progress: return self._hold_in_progress = True streams = (self.streams or []) + (self.proposed_streams or []) if not streams: return for stream in streams: stream.hold() if self.state == 'connected': self._send_hold() @run_in_green_thread def unhold(self): if not self.on_hold and not self._hold_in_progress: return self._hold_in_progress = False streams = (self.streams or []) + (self.proposed_streams or []) if not streams: return for stream in streams: stream.unhold() if self.state == 'connected': self._send_unhold() @run_in_green_thread def end(self): if self.state in (None, 'terminating', 'terminated'): return if self.greenlet is not None: api.kill(self.greenlet, api.GreenletExit()) self.greenlet = None notification_center = NotificationCenter() if self._invitation is None: # The invitation was not yet constructed self.state = 'terminated' notification_center.post_notification('SIPSessionDidFail', self, NotificationData(originator='local', code=487, reason='Session Cancelled', failure_reason='user request', redirect_identities=None)) return elif self._invitation.state is None: # The invitation was built but never sent streams = (self.streams or []) + (self.proposed_streams or []) for stream in streams[:]: try: notification_center.remove_observer(self, sender=stream) except KeyError: streams.remove(stream) else: stream.deactivate() stream.end() self.state = 'terminated' notification_center.post_notification('SIPSessionDidFail', self, NotificationData(originator='local', code=487, reason='Session Cancelled', failure_reason='user request', redirect_identities=None)) return invitation_state = self._invitation.state if invitation_state in ('disconnecting', 'disconnected'): return self.greenlet = api.getcurrent() self.state = 'terminating' if invitation_state == 'connected': notification_center.post_notification('SIPSessionWillEnd', self, NotificationData(originator='local')) streams = (self.streams or []) + (self.proposed_streams or []) for stream in streams[:]: try: notification_center.remove_observer(self, sender=stream) except KeyError: streams.remove(stream) else: stream.deactivate() cancelling = invitation_state != 'connected' and self.direction == 'outgoing' try: self._invitation.end(timeout=1) while True: try: notification = self._channel.wait() except MediaStreamDidFailError: continue if notification.name == 'SIPInvitationChangedState' and notification.data.state == 'disconnected': if notification.data.disconnect_reason in ('internal error', 'missing ACK'): pass elif notification.data.disconnect_reason == 'timeout': notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local' if self.direction=='outgoing' else 'remote', method='INVITE', code=408, reason='Timeout')) elif cancelling: notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local', method='INVITE', code=notification.data.code, reason=notification.data.reason)) elif hasattr(notification.data, 'method'): notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method=notification.data.method, code=200, reason=sip_status_messages[200])) elif notification.data.disconnect_reason == 'user request': notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local', method='BYE', code=notification.data.code, reason=notification.data.reason)) break except SIPCoreError, e: if cancelling: notification_center.post_notification('SIPSessionDidFail', self, NotificationData(originator='local', code=0, reason=None, failure_reason='SIP core error: %s' % str(e), redirect_identities=None)) else: self.end_time = ISOTimestamp.now() notification_center.post_notification('SIPSessionDidEnd', self, NotificationData(originator='local', end_reason='SIP core error: %s' % str(e))) except InvitationDisconnectedError, e: # As it weird as it may sound, PJSIP accepts a BYE even without receiving a final response to the INVITE if e.data.prev_state == 'connected': if e.data.originator == 'remote': notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator=e.data.originator, method=e.data.method, code=200, reason=sip_status_messages[200])) self.end_time = ISOTimestamp.now() notification_center.post_notification('SIPSessionDidEnd', self, NotificationData(originator=e.data.originator, end_reason=e.data.disconnect_reason)) elif getattr(e.data, 'method', None) == 'BYE' and e.data.originator == 'remote': notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator=e.data.originator, method=e.data.method, code=200, reason=sip_status_messages[200])) notification_center.post_notification('SIPSessionDidFail', self, NotificationData(originator=e.data.originator, code=0, reason=None, failure_reason=e.data.disconnect_reason, redirect_identities=None)) else: if e.data.originator == 'remote': code = e.data.code reason = e.data.reason elif e.data.disconnect_reason == 'timeout': code = 408 reason = 'timeout' else: code = 0 reason = None if e.data.originator == 'remote' and code // 100 == 3: redirect_identities = e.data.headers.get('Contact', []) else: redirect_identities = None notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local', method='INVITE', code=code, reason=reason)) notification_center.post_notification('SIPSessionDidFail', self, NotificationData(originator=e.data.originator, code=code, reason=reason, failure_reason=e.data.disconnect_reason, redirect_identities=redirect_identities)) else: if cancelling: notification_center.post_notification('SIPSessionDidFail', self, NotificationData(originator='local', code=487, reason='Session Cancelled', failure_reason='user request', redirect_identities=None)) else: self.end_time = ISOTimestamp.now() notification_center.post_notification('SIPSessionDidEnd', self, NotificationData(originator='local', end_reason='user request')) finally: for stream in streams: stream.end() notification_center.remove_observer(self, sender=self._invitation) self.greenlet = None self.state = 'terminated' @check_state(['connected']) @check_transfer_state(None, None) @run_in_twisted_thread def transfer(self, target_uri, replaced_session=None): notification_center = NotificationCenter() notification_center.post_notification('SIPSessionTransferNewOutgoing', self, NotificationData(transfer_destination=target_uri)) try: self._invitation.transfer(target_uri, replaced_session.dialog_id if replaced_session is not None else None) except SIPCoreError, e: notification_center.post_notification('SIPSessionTransferDidFail', sender=self, data=NotificationData(code=500, reason=str(e))) @check_state(['connected', 'received_proposal', 'sending_proposal', 'accepting_proposal', 'rejecting_proposal', 'cancelling_proposal']) @check_transfer_state('incoming', 'starting') def accept_transfer(self): notification_center = NotificationCenter() notification_center.post_notification('SIPSessionTransferDidStart', sender=self) @check_state(['connected', 'received_proposal', 'sending_proposal', 'accepting_proposal', 'rejecting_proposal', 'cancelling_proposal']) @check_transfer_state('incoming', 'starting') def reject_transfer(self, code=603, reason=None): notification_center = NotificationCenter() notification_center.post_notification('SIPSessionTransferDidFail', self, NotificationData(code=code, reason=reason or sip_status_messages[code])) @property def dialog_id(self): return self._invitation.dialog_id if self._invitation is not None else None @property def local_identity(self): if self._invitation is not None and self._invitation.local_identity is not None: return self._invitation.local_identity else: return self._local_identity @property def peer_address(self): return self._invitation.peer_address if self._invitation is not None else None @property def remote_identity(self): if self._invitation is not None and self._invitation.remote_identity is not None: return self._invitation.remote_identity else: return self._remote_identity @property def remote_user_agent(self): return self._invitation.remote_user_agent if self._invitation is not None else None def _cancel_hold(self): notification_center = NotificationCenter() try: self._invitation.cancel_reinvite() while True: try: notification = self._channel.wait() except MediaStreamDidFailError: continue if notification.name == 'SIPInvitationChangedState': if notification.data.state == 'connected' and notification.data.sub_state == 'normal': notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=notification.data.code, reason=notification.data.reason)) if notification.data.code == 200: self.end() return False elif notification.data.state == 'disconnected': raise InvitationDisconnectedError(notification.sender, notification.data) break except SIPCoreError, e: notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local', code=0, reason=None, failure_reason='SIP core error: %s' % str(e), redirect_identities=None)) except InvitationDisconnectedError, e: self.greenlet = None notification = Notification('SIPInvitationChangedState', e.invitation, e.data) notification.center = notification_center self.handle_notification(notification) return False return True def _send_hold(self): self.state = 'sending_proposal' self.greenlet = api.getcurrent() notification_center = NotificationCenter() unhandled_notifications = [] try: local_sdp = SDPSession.new(self._invitation.sdp.active_local) local_sdp.version += 1 for stream in self.streams: local_sdp.media[stream.index] = stream.get_local_media(remote_sdp=None, index=stream.index) self._invitation.send_reinvite(sdp=local_sdp) received_invitation_state = False received_sdp_update = False with api.timeout(self.short_reinvite_timeout): while not received_invitation_state or not received_sdp_update: notification = self._channel.wait() if notification.name == 'SIPInvitationGotSDPUpdate': received_sdp_update = True if notification.data.succeeded: local_sdp = notification.data.local_sdp remote_sdp = notification.data.remote_sdp for stream in self.streams: stream.update(local_sdp, remote_sdp, stream.index) elif notification.name == 'SIPInvitationChangedState': if notification.data.state == 'connected' and notification.data.sub_state == 'normal': received_invitation_state = True notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local', method='INVITE', code=notification.data.code, reason=notification.data.reason)) elif notification.data.state == 'disconnected': raise InvitationDisconnectedError(notification.sender, notification.data) except InvitationDisconnectedError, e: self.greenlet = None notification = Notification('SIPInvitationChangedState', e.invitation, e.data) notification.center = notification_center self.handle_notification(notification) return except api.TimeoutError: if not self._cancel_hold(): return except SIPCoreError: pass self.greenlet = None self.on_hold = True self.state = 'connected' hold_supported_streams = (stream for stream in self.streams if stream.hold_supported) notification_center.post_notification('SIPSessionDidChangeHoldState', self, NotificationData(originator='local', on_hold=True, partial=any(not stream.on_hold_by_local for stream in hold_supported_streams))) for notification in unhandled_notifications: self.handle_notification(notification) if self._hold_in_progress: self._hold_in_progress = False else: for stream in self.streams: stream.unhold() self._send_unhold() def _send_unhold(self): self.state = 'sending_proposal' self.greenlet = api.getcurrent() notification_center = NotificationCenter() unhandled_notifications = [] try: local_sdp = SDPSession.new(self._invitation.sdp.active_local) local_sdp.version += 1 for stream in self.streams: local_sdp.media[stream.index] = stream.get_local_media(remote_sdp=None, index=stream.index) self._invitation.send_reinvite(sdp=local_sdp) received_invitation_state = False received_sdp_update = False with api.timeout(self.short_reinvite_timeout): while not received_invitation_state or not received_sdp_update: notification = self._channel.wait() if notification.name == 'SIPInvitationGotSDPUpdate': received_sdp_update = True if notification.data.succeeded: local_sdp = notification.data.local_sdp remote_sdp = notification.data.remote_sdp for stream in self.streams: stream.update(local_sdp, remote_sdp, stream.index) elif notification.name == 'SIPInvitationChangedState': if notification.data.state == 'connected' and notification.data.sub_state == 'normal': received_invitation_state = True notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local', method='INVITE', code=notification.data.code, reason=notification.data.reason)) elif notification.data.state == 'disconnected': raise InvitationDisconnectedError(notification.sender, notification.data) except InvitationDisconnectedError, e: self.greenlet = None notification = Notification('SIPInvitationChangedState', e.invitation, e.data) notification.center = notification_center self.handle_notification(notification) return except api.TimeoutError: if not self._cancel_hold(): return except SIPCoreError: pass self.greenlet = None self.on_hold = False self.state = 'connected' notification_center.post_notification('SIPSessionDidChangeHoldState', self, NotificationData(originator='local', on_hold=False, partial=False)) for notification in unhandled_notifications: self.handle_notification(notification) if self._hold_in_progress: for stream in self.streams: stream.hold() self._send_hold() def _fail(self, originator, code, reason, error, reason_header=None): notification_center = NotificationCenter() prev_inv_state = self._invitation.state self.state = 'terminating' if prev_inv_state not in (None, 'incoming', 'outgoing', 'early', 'connecting'): notification_center.post_notification('SIPSessionWillEnd', self, NotificationData(originator=originator)) if self._invitation.state not in (None, 'disconnecting', 'disconnected'): try: if self._invitation.direction == 'incoming' and self._invitation.state in ('incoming', 'early'): if 400<=code<=699 and reason is not None: self._invitation.send_response(code, extra_headers=[reason_header] if reason_header is not None else []) else: self._invitation.end(extra_headers=[reason_header] if reason_header is not None else []) with api.timeout(1): while True: notification = self._channel.wait() if notification.name == 'SIPInvitationChangedState' and notification.data.state == 'disconnected': if prev_inv_state in ('connecting', 'connected'): if notification.data.disconnect_reason in ('timeout', 'missing ACK'): sip_code = 200 sip_reason = 'OK' originator = 'local' elif hasattr(notification.data, 'method'): sip_code = 200 sip_reason = 'OK' originator = 'remote' else: sip_code = notification.data.code sip_reason = notification.data.reason originator = 'local' notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator=originator, method='BYE', code=sip_code, reason=sip_reason)) elif self._invitation.direction == 'incoming' and prev_inv_state in ('incoming', 'early'): ack_received = notification.data.disconnect_reason != 'missing ACK' notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=code, reason=reason, ack_received=ack_received)) elif self._invitation.direction == 'outgoing' and prev_inv_state in ('outgoing', 'early'): notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local', method='INVITE', code=487, reason='Session Cancelled')) break except SIPCoreError: pass except api.TimeoutError: if prev_inv_state in ('connecting', 'connected'): notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='local', method='BYE', code=408, reason=sip_status_messages[408])) notification_center.remove_observer(self, sender=self._invitation) self.state = 'terminated' notification_center.post_notification('SIPSessionDidFail', self, NotificationData(originator=originator, code=code, reason=reason, failure_reason=error, redirect_identities=None)) self.greenlet = None def _fail_proposal(self, originator, error): notification_center = NotificationCenter() for stream in self.proposed_streams: try: notification_center.remove_observer(self, sender=stream) except KeyError: # _fail_proposal can be called from reject_proposal, which means the stream will # not have been initialized or the session registered as an observer for it. pass else: stream.deactivate() stream.end() if originator == 'remote' and self._invitation.sub_state == 'received_proposal': try: self._invitation.send_response(488 if self.proposed_streams else 500) except SIPCoreError: pass else: notification_center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=500, reason=sip_status_messages[500], ack_received='unknown')) notification_center.post_notification('SIPSessionHadProposalFailure', self, NotificationData(originator=originator, failure_reason=error, proposed_streams=self.proposed_streams[:])) self.state = 'connected' self.proposed_streams = None self.greenlet = None @run_in_green_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_SIPInvitationChangedState(self, notification): if self.state == 'terminated': return if notification.data.originator == 'remote' and notification.data.state not in ('disconnecting', 'disconnected'): contact_header = notification.data.headers.get('Contact', None) if contact_header and 'isfocus' in contact_header[0].parameters: self.remote_focus = True if self.greenlet is not None: if notification.data.state == 'disconnected' and notification.data.prev_state != 'disconnecting': self._channel.send_exception(InvitationDisconnectedError(notification.sender, notification.data)) else: self._channel.send(notification) else: self.greenlet = api.getcurrent() unhandled_notifications = [] try: if notification.data.state == 'connected' and notification.data.sub_state == 'received_proposal': self.state = 'received_proposal' try: proposed_remote_sdp = self._invitation.sdp.proposed_remote active_remote_sdp = self._invitation.sdp.active_remote if len(proposed_remote_sdp.media) < len(active_remote_sdp.media): engine = Engine() self._invitation.send_response(488, extra_headers=[WarningHeader(399, engine.user_agent, 'Streams cannot be deleted from the SDP')]) self.state = 'connected' notification.center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=488, reason=sip_status_messages[488], ack_received='unknown')) return for stream in self.streams: if not stream.validate_update(proposed_remote_sdp, stream.index): engine = Engine() self._invitation.send_response(488, extra_headers=[WarningHeader(399, engine.user_agent, 'Failed to update media stream index %d' % stream.index)]) self.state = 'connected' notification.center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=488, reason=sip_status_messages[488], ack_received='unknown')) return added_media_indexes = set() removed_media_indexes = set() reused_media_indexes = set() for index, media_stream in enumerate(proposed_remote_sdp.media): if index >= len(active_remote_sdp.media): added_media_indexes.add(index) elif media_stream.port == 0 and active_remote_sdp.media[index].port > 0: removed_media_indexes.add(index) elif media_stream.port > 0 and active_remote_sdp.media[index].port == 0: reused_media_indexes.add(index) elif media_stream.media != active_remote_sdp.media[index].media: added_media_indexes.add(index) removed_media_indexes.add(index) if added_media_indexes | reused_media_indexes and removed_media_indexes: engine = Engine() self._invitation.send_response(488, extra_headers=[WarningHeader(399, engine.user_agent, 'Both removing AND adding a media stream is currently not supported')]) self.state = 'connected' notification.center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=488, reason=sip_status_messages[488], ack_received='unknown')) return elif added_media_indexes | reused_media_indexes: self.proposed_streams = [] for index in added_media_indexes | reused_media_indexes: media_stream = proposed_remote_sdp.media[index] if media_stream.port != 0: for stream_type in MediaStreamRegistry: try: stream = stream_type.new_from_sdp(self, proposed_remote_sdp, index) except UnknownStreamError: continue except InvalidStreamError as e: log.error("Invalid stream: {}".format(e)) break except Exception as e: log.exception("Exception occurred while setting up stream from SDP: {}".format(e)) break else: stream.index = index self.proposed_streams.append(stream) break if self.proposed_streams: self._invitation.send_response(100) notification.center.post_notification('SIPSessionNewProposal', sender=self, data=NotificationData(originator='remote', proposed_streams=self.proposed_streams[:])) else: self._invitation.send_response(488) self.state = 'connected' notification.center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=488, reason=sip_status_messages[488], ack_received='unknown')) return else: local_sdp = SDPSession.new(self._invitation.sdp.active_local) local_sdp.version += 1 removed_streams = [stream for stream in self.streams if stream.index in removed_media_indexes] prev_on_hold_streams = set(stream for stream in self.streams if stream.hold_supported and stream.on_hold_by_remote) for stream in removed_streams: notification.center.remove_observer(self, sender=stream) stream.deactivate() media = local_sdp.media[stream.index] media.port = 0 media.attributes = [] media.bandwidth_info = [] for stream in self.streams: local_sdp.media[stream.index] = stream.get_local_media(remote_sdp=proposed_remote_sdp, index=stream.index) try: self._invitation.send_response(200, sdp=local_sdp) except PJSIPError: for stream in removed_streams: self.streams.remove(stream) stream.end() if removed_streams: self.end() return else: try: self._invitation.send_response(488) except PJSIPError: self.end() return else: for stream in removed_streams: self.streams.remove(stream) stream.end() notification.center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=200, reason=sip_status_messages[200], ack_received='unknown')) received_invitation_state = False received_sdp_update = False while not received_sdp_update or not received_invitation_state or self._channel: notification = self._channel.wait() if notification.name == 'SIPInvitationGotSDPUpdate': received_sdp_update = True if notification.data.succeeded: local_sdp = notification.data.local_sdp remote_sdp = notification.data.remote_sdp for stream in self.streams: stream.update(local_sdp, remote_sdp, stream.index) elif notification.name == 'SIPInvitationChangedState': if notification.data.state == 'connected' and notification.data.sub_state == 'normal': received_invitation_state = True elif notification.data.state == 'disconnected': raise InvitationDisconnectedError(notification.sender, notification.data) else: unhandled_notifications.append(notification) else: unhandled_notifications.append(notification) on_hold_streams = set(stream for stream in self.streams if stream.hold_supported and stream.on_hold_by_remote) if on_hold_streams != prev_on_hold_streams: hold_supported_streams = (stream for stream in self.streams if stream.hold_supported) notification.center.post_notification('SIPSessionDidChangeHoldState', self, NotificationData(originator='remote', on_hold=bool(on_hold_streams), partial=bool(on_hold_streams) and any(not stream.on_hold_by_remote for stream in hold_supported_streams))) if removed_media_indexes: notification.center.post_notification('SIPSessionDidRenegotiateStreams', self, NotificationData(originator='remote', added_streams=[], removed_streams=removed_streams)) except InvitationDisconnectedError, e: self.greenlet = None self.state = 'connected' notification = Notification('SIPInvitationChangedState', e.invitation, e.data) notification.center = NotificationCenter() self.handle_notification(notification) except SIPCoreError: self.end() else: self.state = 'connected' elif notification.data.state == 'connected' and notification.data.sub_state == 'received_proposal_request': self.state = 'received_proposal_request' try: # An empty proposal was received, generate an offer self._invitation.send_response(100) local_sdp = SDPSession.new(self._invitation.sdp.active_local) local_sdp.version += 1 connection_address = host.outgoing_ip_for(self._invitation.peer_address.ip) if local_sdp.connection is not None: local_sdp.connection.address = connection_address for index, stream in enumerate(self.streams): stream.reset(index) media = stream.get_local_media(remote_sdp=None, index=index) if media.connection is not None: media.connection.address = connection_address local_sdp.media[stream.index] = media self._invitation.send_response(200, sdp=local_sdp) notification.center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=200, reason=sip_status_messages[200], ack_received='unknown')) received_invitation_state = False received_sdp_update = False while not received_sdp_update or not received_invitation_state or self._channel: notification = self._channel.wait() if notification.name == 'SIPInvitationGotSDPUpdate': received_sdp_update = True if notification.data.succeeded: local_sdp = notification.data.local_sdp remote_sdp = notification.data.remote_sdp for stream in self.streams: stream.update(local_sdp, remote_sdp, stream.index) elif notification.name == 'SIPInvitationChangedState': if notification.data.state == 'connected' and notification.data.sub_state == 'normal': received_invitation_state = True elif notification.data.state == 'disconnected': raise InvitationDisconnectedError(notification.sender, notification.data) else: unhandled_notifications.append(notification) else: unhandled_notifications.append(notification) except InvitationDisconnectedError, e: self.greenlet = None self.state = 'connected' notification = Notification('SIPInvitationChangedState', e.invitation, e.data) notification.center = NotificationCenter() self.handle_notification(notification) except SIPCoreError: raise # FIXME else: self.state = 'connected' elif notification.data.prev_state == notification.data.state == 'connected' and notification.data.prev_sub_state == 'received_proposal' and notification.data.sub_state == 'normal': if notification.data.originator == 'local' and notification.data.code == 487: proposed_streams = self.proposed_streams self.proposed_streams = None self.state = 'connected' notification.center.post_notification('SIPSessionProposalRejected', self, NotificationData(originator='remote', code=notification.data.code, reason=notification.data.reason, proposed_streams=proposed_streams)) if self._hold_in_progress: self._send_hold() elif notification.data.state == 'disconnected': if self.state == 'incoming': self.state = 'terminated' if notification.data.originator == 'remote': notification.center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator='remote', method='INVITE', code=487, reason='Session Cancelled', ack_received='unknown')) notification.center.post_notification('SIPSessionDidFail', self, NotificationData(originator='remote', code=487, reason='Session Cancelled', failure_reason=notification.data.disconnect_reason, redirect_identities=None)) else: # There must have been an error involved notification.center.post_notification('SIPSessionDidFail', self, NotificationData(originator='local', code=0, reason=None, failure_reason=notification.data.disconnect_reason, redirect_identities=None)) else: self.state = 'terminated' notification.center.post_notification('SIPSessionWillEnd', self, NotificationData(originator=notification.data.originator)) for stream in self.streams: notification.center.remove_observer(self, sender=stream) stream.deactivate() stream.end() if notification.data.originator == 'remote': if hasattr(notification.data, 'method'): notification.center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator=notification.data.originator, method=notification.data.method, code=200, reason=sip_status_messages[200])) else: notification.center.post_notification('SIPSessionDidProcessTransaction', self, NotificationData(originator=notification.data.originator, method='INVITE', code=notification.data.code, reason=notification.data.reason)) self.end_time = ISOTimestamp.now() notification.center.post_notification('SIPSessionDidEnd', self, NotificationData(originator=notification.data.originator, end_reason=notification.data.disconnect_reason)) notification.center.remove_observer(self, sender=self._invitation) finally: self.greenlet = None for notification in unhandled_notifications: self.handle_notification(notification) def _NH_SIPInvitationGotSDPUpdate(self, notification): if self.greenlet is not None: self._channel.send(notification) def _NH_MediaStreamDidInitialize(self, notification): if self.greenlet is not None: self._channel.send(notification) def _NH_RTPStreamDidEnableEncryption(self, notification): if notification.sender.type != 'audio': return audio_stream = notification.sender if audio_stream.encryption.type == 'ZRTP': # start ZRTP on the video stream, if applicable try: video_stream = next(stream for stream in self.streams or [] if stream.type=='video') except StopIteration: return if video_stream.encryption.type == 'ZRTP' and not video_stream.encryption.active: video_stream.encryption.zrtp._enable(audio_stream) def _NH_MediaStreamDidStart(self, notification): stream = notification.sender if stream.type == 'audio' and stream.encryption.type == 'ZRTP': stream.encryption.zrtp._enable() elif stream.type == 'video' and stream.encryption.type == 'ZRTP': # start ZRTP on the video stream, if applicable try: audio_stream = next(stream for stream in self.streams or [] if stream.type=='audio') except StopIteration: pass else: if audio_stream.encryption.type == 'ZRTP' and audio_stream.encryption.active: stream.encryption.zrtp._enable(audio_stream) if self.greenlet is not None: self._channel.send(notification) def _NH_MediaStreamDidNotInitialize(self, notification): if self.greenlet is not None and self.state not in ('terminating', 'terminated'): self._channel.send_exception(MediaStreamDidNotInitializeError(notification.sender, notification.data)) def _NH_MediaStreamDidFail(self, notification): if self.greenlet is not None: if self.state not in ('terminating', 'terminated'): self._channel.send_exception(MediaStreamDidFailError(notification.sender, notification.data)) else: stream = notification.sender if self.streams == [stream]: self.end() else: try: self.remove_stream(stream) except IllegalStateError: self.end() class SessionManager(object): __metaclass__ = Singleton implements(IObserver) def __init__(self): self.sessions = [] self.state = None self._channel = coros.queue() def start(self): self.state = 'starting' notification_center = NotificationCenter() notification_center.post_notification('SIPSessionManagerWillStart', sender=self) notification_center.add_observer(self, 'SIPInvitationChangedState') notification_center.add_observer(self, 'SIPSessionNewIncoming') notification_center.add_observer(self, 'SIPSessionNewOutgoing') notification_center.add_observer(self, 'SIPSessionDidFail') notification_center.add_observer(self, 'SIPSessionDidEnd') self.state = 'started' notification_center.post_notification('SIPSessionManagerDidStart', sender=self) def stop(self): self.state = 'stopping' notification_center = NotificationCenter() notification_center.post_notification('SIPSessionManagerWillEnd', sender=self) for session in self.sessions: session.end() while self.sessions: self._channel.wait() notification_center.remove_observer(self, 'SIPInvitationChangedState') notification_center.remove_observer(self, 'SIPSessionNewIncoming') notification_center.remove_observer(self, 'SIPSessionNewOutgoing') notification_center.remove_observer(self, 'SIPSessionDidFail') notification_center.remove_observer(self, 'SIPSessionDidEnd') self.state = 'stopped' notification_center.post_notification('SIPSessionManagerDidEnd', sender=self) @run_in_twisted_thread def handle_notification(self, notification): if notification.name == 'SIPInvitationChangedState' and notification.data.state == 'incoming': account_manager = AccountManager() account = account_manager.find_account(notification.data.request_uri) if account is None: notification.sender.send_response(404) return notification.sender.send_response(100) session = Session(account) session.init_incoming(notification.sender, notification.data) elif notification.name in ('SIPSessionNewIncoming', 'SIPSessionNewOutgoing'): self.sessions.append(notification.sender) elif notification.name in ('SIPSessionDidFail', 'SIPSessionDidEnd'): self.sessions.remove(notification.sender) if self.state == 'stopping': self._channel.send(notification) ================================================ FILE: sipsimple/storage.py ================================================ """Definitions and implementations of storage backends""" __all__ = ['ISIPSimpleStorage', 'ISIPSimpleApplicationDataStorage', 'FileStorage', 'MemoryStorage'] import os from functools import partial from zope.interface import Attribute, Interface, implements from sipsimple.account.xcap.storage.file import FileStorage as XCAPFileStorage from sipsimple.account.xcap.storage.memory import MemoryStorage as XCAPMemoryStorage from sipsimple.configuration.backend.file import FileBackend as ConfigurationFileBackend from sipsimple.configuration.backend.memory import MemoryBackend as ConfigurationMemoryBackend class ISIPSimpleStorage(Interface): """Interface describing the backends used for storage throughout SIP Simple""" configuration_backend = Attribute("The backend used for the configuration") xcap_storage_factory = Attribute("The factory used to create XCAP storage backends for each account") class ISIPSimpleApplicationDataStorage(Interface): """Interface describing the directory used for application data storage """ directory = Attribute("The directory used for application data") class FileStorage(object): """Store/read SIP Simple data to/from files""" implements(ISIPSimpleStorage, ISIPSimpleApplicationDataStorage) def __init__(self, directory): self.configuration_backend = ConfigurationFileBackend(os.path.join(directory, 'config')) self.xcap_storage_factory = partial(XCAPFileStorage, os.path.join(directory, 'xcap')) self.directory = directory class MemoryStorage(object): """Store/read SIP Simple data to/from memory""" implements(ISIPSimpleStorage) def __init__(self): self.configuration_backend = ConfigurationMemoryBackend() self.xcap_storage_factory = XCAPMemoryStorage ================================================ FILE: sipsimple/streams/__init__.py ================================================ """ This module automatically registers media streams to a stream registry allowing for a plug and play mechanism of various types of media negotiated in a SIP session that can be added to this library by using a generic API. For actual implementations see rtp/* and msrp/* that have media stream implementations based on their respective RTP and MSRP protocols. """ __all__ = ['StreamError', 'InvalidStreamError', 'UnknownStreamError', 'IMediaStream', 'MediaStreamRegistry', 'MediaStreamType'] from operator import attrgetter from zope.interface import Interface, Attribute class StreamError(Exception): pass class InvalidStreamError(StreamError): pass class UnknownStreamError(StreamError): pass # The MediaStream interface # class IMediaStream(Interface): type = Attribute("A string identifying the stream type (ex: audio, video, ...)") priority = Attribute("An integer value indicating the stream priority relative to the other streams types (higher numbers have higher priority).") session = Attribute("Session object to which this stream is attached") hold_supported = Attribute("True if the stream supports hold") on_hold_by_local = Attribute("True if the stream is on hold by the local party") on_hold_by_remote = Attribute("True if the stream is on hold by the remote") on_hold = Attribute("True if either on_hold_by_local or on_hold_by_remote is true") # this should be a classmethod, but zopeinterface complains if we decorate it with @classmethod -Dan def new_from_sdp(cls, session, remote_sdp, stream_index): pass def get_local_media(self, for_offer): pass def initialize(self, session, direction): pass def start(self, local_sdp, remote_sdp, stream_index): pass def deactivate(self): pass def end(self): pass def validate_update(self, remote_sdp, stream_index): pass def update(self, local_sdp, remote_sdp, stream_index): pass def hold(self): pass def unhold(self): pass def reset(self, stream_index): pass # The MediaStream registry # class StreamDescriptor(object): def __init__(self, type): self.type = type def __get__(self, obj, owner): return self if obj is None else obj.get(self.type) def __set__(self, obj, value): raise AttributeError('cannot set attribute') def __delete__(self, obj): raise AttributeError('cannot delete attribute') class MediaStreamRegistry(object): def __init__(self): self.__types__ = [] def __iter__(self): return iter(self.__types__) def add(self, cls): if cls.type is not None and cls.priority is not None and cls not in self.__types__: self.__types__.append(cls) self.__types__.sort(key=attrgetter('priority'), reverse=True) setattr(self.__class__, cls.type.title().translate(None, ' -_') + 'Stream', StreamDescriptor(cls.type)) def get(self, type): try: return next(cls for cls in self.__types__ if cls.type == type) except StopIteration: raise UnknownStreamError("unknown stream type: %s" % type) MediaStreamRegistry = MediaStreamRegistry() class MediaStreamType(type): """Metaclass for MediaStream classes that automatically adds them to the media stream registry""" type = None priority = None def __init__(cls, name, bases, dictionary): super(MediaStreamType, cls).__init__(name, bases, dictionary) MediaStreamRegistry.add(cls) # Import the submodules in order for them to register the streams they define in MediaStreamRegistry from sipsimple.streams import msrp, rtp ================================================ FILE: sipsimple/streams/msrp/__init__.py ================================================ """ Handling of MSRP media streams according to RFC4975, RFC4976, RFC5547 and RFC3994. """ __all__ = ['MSRPStreamError', 'MSRPStreamBase'] import traceback from application.notification import NotificationCenter, NotificationData, IObserver from application.python import Null from application.system import host from twisted.internet.error import ConnectionDone from zope.interface import implements from eventlib import api from msrplib.connect import DirectConnector, DirectAcceptor, RelayConnection, MSRPRelaySettings from msrplib.protocol import URI from msrplib.session import contains_mime_type from sipsimple.account import Account, BonjourAccount from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.core import SDPAttribute, SDPConnection, SDPMediaStream from sipsimple.streams import IMediaStream, MediaStreamType, StreamError from sipsimple.threading.green import run_in_green_thread class MSRPStreamError(StreamError): pass class MSRPStreamBase(object): __metaclass__ = MediaStreamType implements(IMediaStream, IObserver) # Attributes that need to be defined by each MSRP stream type type = None priority = None msrp_session_class = None media_type = None accept_types = None accept_wrapped_types = None # These attributes are always False for any MSRP stream hold_supported = False on_hold = False on_hold_by_local = False on_hold_by_remote = False def __new__(cls, *args, **kw): if cls is MSRPStreamBase: raise TypeError("MSRPStreamBase cannot be instantiated directly") return object.__new__(cls) def __init__(self, direction='sendrecv'): self.direction = direction self.greenlet = None self.local_media = None self.remote_media = None self.msrp = None # Placeholder for the MSRPTransport that will be set when started self.msrp_connector = None self.cpim_enabled = None # Boolean value. None means it was not negotiated yet self.session = None self.msrp_session = None self.shutting_down = False self.local_role = None self.remote_role = None self.transport = None self.remote_accept_types = None self.remote_accept_wrapped_types = None self._initialize_done = False self._done = False self._failure_reason = None @property def local_uri(self): msrp = self.msrp or self.msrp_connector return msrp.local_uri if msrp is not None else None def _create_local_media(self, uri_path): transport = "TCP/TLS/MSRP" if uri_path[-1].use_tls else "TCP/MSRP" attributes = [SDPAttribute("path", " ".join(str(uri) for uri in uri_path))] if self.direction not in [None, 'sendrecv']: attributes.append(SDPAttribute(self.direction, '')) if self.accept_types is not None: attributes.append(SDPAttribute("accept-types", " ".join(self.accept_types))) if self.accept_wrapped_types is not None: attributes.append(SDPAttribute("accept-wrapped-types", " ".join(self.accept_wrapped_types))) attributes.append(SDPAttribute("setup", self.local_role)) local_ip = uri_path[-1].host connection = SDPConnection(local_ip) return SDPMediaStream(self.media_type, uri_path[-1].port or 2855, transport, connection=connection, formats=["*"], attributes=attributes) # The public API (the IMediaStream interface) # noinspection PyUnusedLocal def get_local_media(self, remote_sdp=None, index=0): return self.local_media def new_from_sdp(self, session, remote_sdp, stream_index): raise NotImplementedError @run_in_green_thread def initialize(self, session, direction): self.greenlet = api.getcurrent() notification_center = NotificationCenter() notification_center.add_observer(self, sender=self) try: self.session = session self.transport = self.session.account.msrp.transport outgoing = direction == 'outgoing' logger = NotificationProxyLogger() if self.session.account is BonjourAccount(): if outgoing: self.msrp_connector = DirectConnector(logger=logger) self.local_role = 'active' else: if self.transport == 'tls' and None in (self.session.account.tls_credentials.cert, self.session.account.tls_credentials.key): raise MSRPStreamError("Cannot accept MSRP connection without a TLS certificate") self.msrp_connector = DirectAcceptor(logger=logger) self.local_role = 'passive' else: if self.session.account.msrp.connection_model == 'relay': if not outgoing and self.remote_role in ('actpass', 'passive'): # 'passive' not allowed by the RFC but play nice for interoperability. -Saul self.msrp_connector = DirectConnector(logger=logger, use_sessmatch=True) self.local_role = 'active' elif outgoing and not self.session.account.nat_traversal.use_msrp_relay_for_outbound: self.msrp_connector = DirectConnector(logger=logger, use_sessmatch=True) self.local_role = 'active' else: if self.session.account.nat_traversal.msrp_relay is None: relay_host = relay_port = None else: if self.transport != self.session.account.nat_traversal.msrp_relay.transport: raise MSRPStreamError("MSRP relay transport conflicts with MSRP transport setting") relay_host = self.session.account.nat_traversal.msrp_relay.host relay_port = self.session.account.nat_traversal.msrp_relay.port relay = MSRPRelaySettings(domain=self.session.account.uri.host, username=self.session.account.uri.user, password=self.session.account.credentials.password, host=relay_host, port=relay_port, use_tls=self.transport=='tls') self.msrp_connector = RelayConnection(relay, 'passive', logger=logger, use_sessmatch=True) self.local_role = 'actpass' if outgoing else 'passive' else: if not outgoing and self.remote_role in ('actpass', 'passive'): # 'passive' not allowed by the RFC but play nice for interoperability. -Saul self.msrp_connector = DirectConnector(logger=logger, use_sessmatch=True) self.local_role = 'active' else: if not outgoing and self.transport == 'tls' and None in (self.session.account.tls_credentials.cert, self.session.account.tls_credentials.key): raise MSRPStreamError("Cannot accept MSRP connection without a TLS certificate") self.msrp_connector = DirectAcceptor(logger=logger, use_sessmatch=True) self.local_role = 'actpass' if outgoing else 'passive' full_local_path = self.msrp_connector.prepare(local_uri=URI(host=host.default_ip, port=0, use_tls=self.transport=='tls', credentials=self.session.account.tls_credentials)) self.local_media = self._create_local_media(full_local_path) except Exception, e: notification_center.post_notification('MediaStreamDidNotInitialize', sender=self, data=NotificationData(reason=str(e))) else: notification_center.post_notification('MediaStreamDidInitialize', sender=self) finally: self._initialize_done = True self.greenlet = None # noinspection PyUnusedLocal @run_in_green_thread def start(self, local_sdp, remote_sdp, stream_index): self.greenlet = api.getcurrent() notification_center = NotificationCenter() context = 'sdp_negotiation' try: remote_media = remote_sdp.media[stream_index] self.remote_media = remote_media self.remote_accept_types = remote_media.attributes.getfirst('accept-types', '').split() self.remote_accept_wrapped_types = remote_media.attributes.getfirst('accept-wrapped-types', '').split() self.cpim_enabled = contains_mime_type(self.accept_types, 'message/cpim') and contains_mime_type(self.remote_accept_types, 'message/cpim') remote_uri_path = remote_media.attributes.getfirst('path') if remote_uri_path is None: raise AttributeError("remote SDP media does not have 'path' attribute") full_remote_path = [URI.parse(uri) for uri in remote_uri_path.split()] remote_transport = 'tls' if full_remote_path[0].use_tls else 'tcp' if self.transport != remote_transport: raise MSRPStreamError("remote transport ('%s') different from local transport ('%s')" % (remote_transport, self.transport)) if isinstance(self.session.account, Account) and self.local_role == 'actpass': remote_setup = remote_media.attributes.getfirst('setup', 'passive') if remote_setup == 'passive': # If actpass is offered connectors are always started as passive # We need to switch to active if the remote answers with passive if self.session.account.msrp.connection_model == 'relay': self.msrp_connector.mode = 'active' else: local_uri = self.msrp_connector.local_uri logger = self.msrp_connector.logger self.msrp_connector = DirectConnector(logger=logger, use_sessmatch=True) self.msrp_connector.prepare(local_uri) context = 'start' self.msrp = self.msrp_connector.complete(full_remote_path) if self.msrp_session_class is not None: self.msrp_session = self.msrp_session_class(self.msrp, accept_types=self.accept_types, on_incoming_cb=self._handle_incoming, automatic_reports=False) self.msrp_connector = None except Exception, e: self._failure_reason = str(e) notification_center.post_notification('MediaStreamDidFail', sender=self, data=NotificationData(context=context, reason=self._failure_reason)) else: notification_center.post_notification('MediaStreamDidStart', sender=self) finally: self.greenlet = None def deactivate(self): self.shutting_down = True @run_in_green_thread def end(self): if self._done: return self._done = True notification_center = NotificationCenter() if not self._initialize_done: # we are in the middle of initialize() try: msrp_connector = self.msrp_connector if self.greenlet is not None: api.kill(self.greenlet) if msrp_connector is not None: msrp_connector.cleanup() finally: notification_center.post_notification('MediaStreamDidNotInitialize', sender=self, data=NotificationData(reason='Interrupted')) notification_center.remove_observer(self, sender=self) self.msrp_connector = None self.greenlet = None else: notification_center.post_notification('MediaStreamWillEnd', sender=self) msrp = self.msrp msrp_session = self.msrp_session msrp_connector = self.msrp_connector try: if self.greenlet is not None: api.kill(self.greenlet) if msrp_session is not None: msrp_session.shutdown() elif msrp is not None: msrp.loseConnection(wait=False) if msrp_connector is not None: msrp_connector.cleanup() finally: notification_center.post_notification('MediaStreamDidEnd', sender=self, data=NotificationData(error=self._failure_reason)) notification_center.remove_observer(self, sender=self) self.msrp = None self.msrp_session = None self.msrp_connector = None self.session = None self.greenlet = None # noinspection PyMethodMayBeStatic,PyUnusedLocal def validate_update(self, remote_sdp, stream_index): return True # TODO def update(self, local_sdp, remote_sdp, stream_index): pass # TODO def hold(self): pass def unhold(self): pass def reset(self, stream_index): pass # Internal IObserver interface def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) # Internal message handlers def _handle_incoming(self, chunk=None, error=None): notification_center = NotificationCenter() if error is not None: if self.shutting_down and isinstance(error.value, ConnectionDone): return self._failure_reason = error.getErrorMessage() notification_center.post_notification('MediaStreamDidFail', sender=self, data=NotificationData(context='reading', reason=self._failure_reason)) elif chunk is not None: method_handler = getattr(self, '_handle_%s' % chunk.method, None) if method_handler is not None: method_handler(chunk) def _handle_REPORT(self, chunk): pass def _handle_SEND(self, chunk): pass # temporary solution. to be replaced later by a better logging system in msrplib -Dan # class ChunkInfo(object): __slots__ = 'content_type', 'header', 'footer', 'data' def __init__(self, content_type, header='', footer='', data=''): self.content_type = content_type self.header = header self.footer = footer self.data = data def __repr__(self): return "{0.__class__.__name__}(content_type={0.content_type!r}, header={0.header!r}, footer={0.footer!r}, data={0.data!r})".format(self) @property def content(self): return self.header + self.data + self.footer @property def normalized_content(self): if not self.data: return self.header + self.footer elif self.content_type == 'message/cpim': headers, sep, body = self.data.partition('\r\n\r\n') if not sep: return self.header + self.data + self.footer mime_headers, mime_sep, mime_body = body.partition('\n\n') if not mime_sep: return self.header + self.data + self.footer for mime_header in mime_headers.lower().splitlines(): if mime_header.startswith('content-type:'): wrapped_content_type = mime_header[13:].partition(';')[0].strip() break else: wrapped_content_type = None if wrapped_content_type is None or wrapped_content_type == 'application/im-iscomposing+xml' or wrapped_content_type.startswith(('text/', 'message/')): data = self.data else: data = headers + sep + mime_headers + mime_sep + '<<>>' return self.header + data + self.footer elif self.content_type is None or self.content_type == 'application/im-iscomposing+xml' or self.content_type.startswith(('text/', 'message/')): return self.header + self.data + self.footer else: return self.header + '<<>>' + self.footer class NotificationProxyLogger(object): def __init__(self): from application import log self.level = log.level self.notification_center = NotificationCenter() self.log_settings = SIPSimpleSettings().logs def received_chunk(self, data, transport): if self.log_settings.trace_msrp: chunk_info = ChunkInfo(data.content_type, header=data.chunk_header, footer=data.chunk_footer, data=data.data) notification_data = NotificationData(direction='incoming', local_address=transport.getHost(), remote_address=transport.getPeer(), data=chunk_info.normalized_content, illegal=False) self.notification_center.post_notification('MSRPTransportTrace', sender=transport, data=notification_data) def sent_chunk(self, data, transport): if self.log_settings.trace_msrp: chunk_info = ChunkInfo(data.content_type, header=data.encoded_header, footer=data.encoded_footer, data=data.data) notification_data = NotificationData(direction='outgoing', local_address=transport.getHost(), remote_address=transport.getPeer(), data=chunk_info.normalized_content, illegal=False) self.notification_center.post_notification('MSRPTransportTrace', sender=transport, data=notification_data) def received_illegal_data(self, data, transport): if self.log_settings.trace_msrp: notification_data = NotificationData(direction='incoming', local_address=transport.getHost(), remote_address=transport.getPeer(), data=data, illegal=True) self.notification_center.post_notification('MSRPTransportTrace', sender=transport, data=notification_data) def debug(self, message, *args, **kw): pass def info(self, message, *args, **kw): if self.log_settings.trace_msrp: self.notification_center.post_notification('MSRPLibraryLog', data=NotificationData(message=message % args if args else message, level=self.level.INFO)) def warning(self, message, *args, **kw): if self.log_settings.trace_msrp: self.notification_center.post_notification('MSRPLibraryLog', data=NotificationData(message=message % args if args else message, level=self.level.WARNING)) warn = warning def error(self, message, *args, **kw): if self.log_settings.trace_msrp: self.notification_center.post_notification('MSRPLibraryLog', data=NotificationData(message=message % args if args else message, level=self.level.ERROR)) def exception(self, message='', *args, **kw): if self.log_settings.trace_msrp: message = message % args if args else message exception = traceback.format_exc() self.notification_center.post_notification('MSRPLibraryLog', data=NotificationData(message=message + '\n' + exception if message else exception, level=self.level.ERROR)) def critical(self, message, *args, **kw): if self.log_settings.trace_msrp: self.notification_center.post_notification('MSRPLibraryLog', data=NotificationData(message=message % args if args else message, level=self.level.CRITICAL)) fatal = critical from sipsimple.streams.msrp import chat, filetransfer, screensharing ================================================ FILE: sipsimple/streams/msrp/chat.py ================================================ """ This module provides classes to parse and generate SDP related to SIP sessions that negotiate Instant Messaging, including CPIM as defined in RFC3862 """ import cPickle as pickle import codecs import os import random import re from application.python.descriptor import WriteOnceAttribute from application.notification import IObserver, NotificationCenter, NotificationData from application.python import Null from application.python.types import Singleton from application.system import openfile from collections import defaultdict from email.message import Message as EmailMessage from email.parser import Parser as EmailParser from eventlib.coros import queue from eventlib.proc import spawn, ProcExit from functools import partial from msrplib.protocol import FailureReportHeader, SuccessReportHeader, UseNicknameHeader from msrplib.session import MSRPSession, contains_mime_type from otr import OTRSession, OTRTransport, OTRState, SMPStatus from otr.cryptography import DSAPrivateKey from otr.exceptions import IgnoreMessage, UnencryptedMessage, EncryptedMessageError, OTRError from zope.interface import implements from sipsimple.core import SIPURI, BaseSIPURI from sipsimple.payloads import ParserError from sipsimple.payloads.iscomposing import IsComposingDocument, State, LastActive, Refresh, ContentType from sipsimple.storage import ISIPSimpleApplicationDataStorage from sipsimple.streams import InvalidStreamError, UnknownStreamError from sipsimple.streams.msrp import MSRPStreamError, MSRPStreamBase from sipsimple.threading import run_in_thread, run_in_twisted_thread from sipsimple.threading.green import run_in_green_thread from sipsimple.util import MultilingualText, ISOTimestamp __all__ = ['ChatStream', 'ChatStreamError', 'ChatIdentity', 'CPIMPayload', 'CPIMHeader', 'CPIMNamespace', 'CPIMParserError', 'OTRState', 'SMPStatus'] class OTRTrustedPeer(object): fingerprint = WriteOnceAttribute() # in order to be hashable this needs to be immutable def __init__(self, fingerprint, description=u'', **kw): if not isinstance(fingerprint, basestring): raise TypeError("fingerprint must be a string") self.fingerprint = fingerprint self.description = description self.__dict__.update(kw) def __hash__(self): return hash(self.fingerprint) def __eq__(self, other): if isinstance(other, OTRTrustedPeer): return self.fingerprint == other.fingerprint elif isinstance(other, basestring): return self.fingerprint == other else: return NotImplemented def __ne__(self, other): return not (self == other) def __repr__(self): return "{0.__class__.__name__}({0.fingerprint!r}, description={0.description!r})".format(self) def __reduce__(self): return self.__class__, (self.fingerprint,), self.__dict__ class OTRTrustedPeerSet(object): def __init__(self, iterable=()): self.__data__ = {} self.update(iterable) def __repr__(self): return "{}({})".format(self.__class__.__name__, self.__data__.values()) def __contains__(self, item): return item in self.__data__ def __getitem__(self, item): return self.__data__[item] def __iter__(self): return self.__data__.itervalues() def __len__(self): return len(self.__data__) def get(self, item, default=None): return self.__data__.get(item, default) def add(self, item): if not isinstance(item, OTRTrustedPeer): raise TypeError("item should be and instance of OTRTrustedPeer") self.__data__[item.fingerprint] = item def remove(self, item): del self.__data__[item] def discard(self, item): self.__data__.pop(item, None) def update(self, iterable=()): for item in iterable: self.add(item) class OTRCache(object): __metaclass__ = Singleton def __init__(self): from sipsimple.application import SIPApplication if SIPApplication.storage is None: raise RuntimeError("Cannot access the OTR cache before SIPApplication.storage is defined") if ISIPSimpleApplicationDataStorage.providedBy(SIPApplication.storage): self.key_file = os.path.join(SIPApplication.storage.directory, 'otr.key') self.trusted_file = os.path.join(SIPApplication.storage.directory, 'otr.trusted') try: self.private_key = DSAPrivateKey.load(self.key_file) if self.private_key.key_size != 1024: raise ValueError except (EnvironmentError, ValueError): self.private_key = DSAPrivateKey.generate() self.private_key.save(self.key_file) try: self.trusted_peers = pickle.load(open(self.trusted_file, 'rb')) if not isinstance(self.trusted_peers, OTRTrustedPeerSet) or not all(isinstance(item, OTRTrustedPeer) for item in self.trusted_peers): raise ValueError("invalid OTR trusted peers file") except Exception: self.trusted_peers = OTRTrustedPeerSet() self.save() else: self.key_file = self.trusted_file = None self.private_key = DSAPrivateKey.generate() self.trusted_peers = OTRTrustedPeerSet() # def generate_private_key(self): # self.private_key = DSAPrivateKey.generate() # if self.key_file: # self.private_key.save(self.key_file) @run_in_thread('file-io') def save(self): if self.trusted_file is not None: with openfile(self.trusted_file, 'wb', permissions=0600) as trusted_file: pickle.dump(self.trusted_peers, trusted_file) class OTREncryption(object): implements(IObserver) def __init__(self, stream): self.stream = stream self.otr_cache = OTRCache() self.otr_session = OTRSession(self.otr_cache.private_key, self.stream, supported_versions={3}) # we need at least OTR-v3 for question based SMP notification_center = NotificationCenter() notification_center.add_observer(self, sender=stream) notification_center.add_observer(self, sender=self.otr_session) @property def active(self): try: return self.otr_session.encrypted except AttributeError: return False @property def cipher(self): return 'AES-128-CTR' if self.active else None @property def key_fingerprint(self): try: return self.otr_session.local_private_key.public_key.fingerprint except AttributeError: return None @property def peer_fingerprint(self): try: return self.otr_session.remote_public_key.fingerprint except AttributeError: return None @property def peer_name(self): try: return self.__dict__['peer_name'] except KeyError: trusted_peer = self.otr_cache.trusted_peers.get(self.peer_fingerprint, None) if trusted_peer is None: return u'' else: return self.__dict__.setdefault('peer_name', trusted_peer.description) @peer_name.setter def peer_name(self, name): old_name = self.peer_name new_name = self.__dict__['peer_name'] = name if old_name != new_name: trusted_peer = self.otr_cache.trusted_peers.get(self.peer_fingerprint, None) if trusted_peer is not None: trusted_peer.description = new_name self.otr_cache.save() notification_center = NotificationCenter() notification_center.post_notification("ChatStreamOTRPeerNameChanged", sender=self.stream, data=NotificationData(name=name)) @property def verified(self): return self.peer_fingerprint in self.otr_cache.trusted_peers @verified.setter def verified(self, value): peer_fingerprint = self.peer_fingerprint old_verified = peer_fingerprint in self.otr_cache.trusted_peers new_verified = bool(value) if peer_fingerprint is None or new_verified == old_verified: return if new_verified: self.otr_cache.trusted_peers.add(OTRTrustedPeer(peer_fingerprint, description=self.peer_name)) else: self.otr_cache.trusted_peers.remove(peer_fingerprint) self.otr_cache.save() notification_center = NotificationCenter() notification_center.post_notification("ChatStreamOTRVerifiedStateChanged", sender=self.stream, data=NotificationData(verified=new_verified)) @run_in_twisted_thread def start(self): if self.otr_session is not None: self.otr_session.start() @run_in_twisted_thread def stop(self): if self.otr_session is not None: self.otr_session.stop() @run_in_twisted_thread def smp_verify(self, secret, question=None): self.otr_session.smp_verify(secret, question) @run_in_twisted_thread def smp_answer(self, secret): self.otr_session.smp_answer(secret) @run_in_twisted_thread def smp_abort(self): self.otr_session.smp_abort() def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_MediaStreamDidStart(self, notification): if self.stream.start_otr: self.otr_session.start() def _NH_MediaStreamDidEnd(self, notification): notification.center.remove_observer(self, sender=self.stream) notification.center.remove_observer(self, sender=self.otr_session) self.otr_session.stop() self.otr_session = None self.stream = None _NH_MediaStreamDidNotInitialize = _NH_MediaStreamDidEnd def _NH_OTRSessionStateChanged(self, notification): notification.center.post_notification('ChatStreamOTREncryptionStateChanged', sender=self.stream, data=notification.data) def _NH_OTRSessionSMPVerificationDidStart(self, notification): notification.center.post_notification('ChatStreamSMPVerificationDidStart', sender=self.stream, data=notification.data) def _NH_OTRSessionSMPVerificationDidNotStart(self, notification): notification.center.post_notification('ChatStreamSMPVerificationDidNotStart', sender=self.stream, data=notification.data) def _NH_OTRSessionSMPVerificationDidEnd(self, notification): notification.center.post_notification('ChatStreamSMPVerificationDidEnd', sender=self.stream, data=notification.data) class ChatStreamError(MSRPStreamError): pass class ChatStream(MSRPStreamBase): type = 'chat' priority = 1 msrp_session_class = MSRPSession media_type = 'message' accept_types = ['message/cpim', 'text/*', 'image/*', 'application/im-iscomposing+xml'] accept_wrapped_types = ['text/*', 'image/*', 'application/im-iscomposing+xml'] prefer_cpim = True start_otr = True def __init__(self): super(ChatStream, self).__init__(direction='sendrecv') self.message_queue = queue() self.sent_messages = set() self.incoming_queue = defaultdict(list) self.message_queue_thread = None self.encryption = OTREncryption(self) @classmethod def new_from_sdp(cls, session, remote_sdp, stream_index): remote_stream = remote_sdp.media[stream_index] if remote_stream.media != 'message': raise UnknownStreamError expected_transport = 'TCP/TLS/MSRP' if session.account.msrp.transport=='tls' else 'TCP/MSRP' if remote_stream.transport != expected_transport: raise InvalidStreamError("expected %s transport in chat stream, got %s" % (expected_transport, remote_stream.transport)) if remote_stream.formats != ['*']: raise InvalidStreamError("wrong format list specified") stream = cls() stream.remote_role = remote_stream.attributes.getfirst('setup', 'active') if remote_stream.direction != 'sendrecv': raise InvalidStreamError("Unsupported direction for chat stream: %s" % remote_stream.direction) remote_accept_types = remote_stream.attributes.getfirst('accept-types') if remote_accept_types is None: raise InvalidStreamError("remote SDP media does not have 'accept-types' attribute") if not any(contains_mime_type(cls.accept_types, mime_type) for mime_type in remote_accept_types.split()): raise InvalidStreamError("no compatible media types found") return stream @property def local_identity(self): try: return ChatIdentity(self.session.local_identity.uri, self.session.local_identity.display_name) except AttributeError: return None @property def remote_identity(self): try: return ChatIdentity(self.session.remote_identity.uri, self.session.remote_identity.display_name) except AttributeError: return None @property def private_messages_allowed(self): return 'private-messages' in self.chatroom_capabilities @property def nickname_allowed(self): return 'nickname' in self.chatroom_capabilities @property def chatroom_capabilities(self): try: if self.cpim_enabled and self.session.remote_focus: return ' '.join(self.remote_media.attributes.getall('chatroom')).split() except AttributeError: pass return [] def _NH_MediaStreamDidStart(self, notification): self.message_queue_thread = spawn(self._message_queue_handler) def _NH_MediaStreamDidNotInitialize(self, notification): message_queue, self.message_queue = self.message_queue, queue() while message_queue: message = message_queue.wait() if message.notify_progress: data = NotificationData(message_id=message.id, message=None, code=0, reason='Stream was closed') notification.center.post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data) def _NH_MediaStreamDidEnd(self, notification): if self.message_queue_thread is not None: self.message_queue_thread.kill() else: message_queue, self.message_queue = self.message_queue, queue() while message_queue: message = message_queue.wait() if message.notify_progress: data = NotificationData(message_id=message.id, message=None, code=0, reason='Stream ended') notification.center.post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data) def _handle_REPORT(self, chunk): # in theory, REPORT can come with Byte-Range which would limit the scope of the REPORT to the part of the message. if chunk.message_id in self.sent_messages: self.sent_messages.remove(chunk.message_id) notification_center = NotificationCenter() data = NotificationData(message_id=chunk.message_id, message=chunk, code=chunk.status.code, reason=chunk.status.comment) if chunk.status.code == 200: notification_center.post_notification('ChatStreamDidDeliverMessage', sender=self, data=data) else: notification_center.post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data) def _handle_SEND(self, chunk): if chunk.size == 0: # keep-alive self.msrp_session.send_report(chunk, 200, 'OK') return content_type = chunk.content_type.lower() if not contains_mime_type(self.accept_types, content_type): self.msrp_session.send_report(chunk, 413, 'Unwanted Message') return if chunk.contflag == '#': self.incoming_queue.pop(chunk.message_id, None) self.msrp_session.send_report(chunk, 200, 'OK') return elif chunk.contflag == '+': self.incoming_queue[chunk.message_id].append(chunk.data) self.msrp_session.send_report(chunk, 200, 'OK') return else: data = ''.join(self.incoming_queue.pop(chunk.message_id, [])) + chunk.data if content_type == 'message/cpim': try: payload = CPIMPayload.decode(data) except CPIMParserError: self.msrp_session.send_report(chunk, 400, 'CPIM Parser Error') return else: message = Message(**{name: getattr(payload, name) for name in Message.__slots__}) if not contains_mime_type(self.accept_wrapped_types, message.content_type): self.msrp_session.send_report(chunk, 413, 'Unwanted Message') return if message.timestamp is None: message.timestamp = ISOTimestamp.now() if message.sender is None: message.sender = self.remote_identity private = self.session.remote_focus and len(message.recipients) == 1 and message.recipients[0] != self.remote_identity else: payload = SimplePayload.decode(data, content_type) message = Message(payload.content, payload.content_type, sender=self.remote_identity, recipients=[self.local_identity], timestamp=ISOTimestamp.now()) private = False try: message.content = self.encryption.otr_session.handle_input(message.content, message.content_type) except IgnoreMessage: self.msrp_session.send_report(chunk, 200, 'OK') return except UnencryptedMessage: encrypted = False encryption_active = True except EncryptedMessageError, e: self.msrp_session.send_report(chunk, 400, str(e)) notification_center = NotificationCenter() notification_center.post_notification('ChatStreamOTRError', sender=self, data=NotificationData(error=str(e))) return except OTRError, e: self.msrp_session.send_report(chunk, 200, 'OK') notification_center = NotificationCenter() notification_center.post_notification('ChatStreamOTRError', sender=self, data=NotificationData(error=str(e))) return else: encrypted = encryption_active = self.encryption.active if payload.charset is not None: message.content = message.content.decode(payload.charset) elif payload.content_type.startswith('text/'): message.content.decode('utf8') notification_center = NotificationCenter() if message.content_type.lower() == IsComposingDocument.content_type: try: document = IsComposingDocument.parse(message.content) except ParserError as e: self.msrp_session.send_report(chunk, 400, str(e)) return self.msrp_session.send_report(chunk, 200, 'OK') data = NotificationData(state=document.state.value, refresh=document.refresh.value if document.refresh is not None else 120, content_type=document.content_type.value if document.content_type is not None else None, last_active=document.last_active.value if document.last_active is not None else None, sender=message.sender, recipients=message.recipients, private=private, encrypted=encrypted, encryption_active=encryption_active) notification_center.post_notification('ChatStreamGotComposingIndication', sender=self, data=data) else: self.msrp_session.send_report(chunk, 200, 'OK') data = NotificationData(message=message, private=private, encrypted=encrypted, encryption_active=encryption_active) notification_center.post_notification('ChatStreamGotMessage', sender=self, data=data) def _on_transaction_response(self, message_id, response): if message_id in self.sent_messages and response.code != 200: self.sent_messages.remove(message_id) data = NotificationData(message_id=message_id, message=response, code=response.code, reason=response.comment) NotificationCenter().post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data) def _on_nickname_transaction_response(self, message_id, response): notification_center = NotificationCenter() if response.code == 200: notification_center.post_notification('ChatStreamDidSetNickname', sender=self, data=NotificationData(message_id=message_id, response=response)) else: notification_center.post_notification('ChatStreamDidNotSetNickname', sender=self, data=NotificationData(message_id=message_id, message=response, code=response.code, reason=response.comment)) def _message_queue_handler(self): notification_center = NotificationCenter() try: while True: message = self.message_queue.wait() if self.msrp_session is None: if message.notify_progress: data = NotificationData(message_id=message.id, message=None, code=0, reason='Stream ended') notification_center.post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data) break try: if isinstance(message.content, unicode): message.content = message.content.encode('utf8') charset = 'utf8' else: charset = None if not isinstance(message, QueuedOTRInternalMessage): try: message.content = self.encryption.otr_session.handle_output(message.content, message.content_type) except OTRError, e: raise ChatStreamError(str(e)) message.sender = message.sender or self.local_identity message.recipients = message.recipients or [self.remote_identity] # check if we MUST use CPIM need_cpim = (message.sender != self.local_identity or message.recipients != [self.remote_identity] or message.courtesy_recipients or message.subject or message.timestamp or message.required or message.additional_headers) if need_cpim or not contains_mime_type(self.remote_accept_types, message.content_type): if not contains_mime_type(self.remote_accept_wrapped_types, message.content_type): raise ChatStreamError('Unsupported content_type for outgoing message: %r' % message.content_type) if not self.cpim_enabled: raise ChatStreamError('Additional message meta-data cannot be sent, because the CPIM wrapper is not used') if not self.private_messages_allowed and message.recipients != [self.remote_identity]: raise ChatStreamError('The remote end does not support private messages') if message.timestamp is None: message.timestamp = ISOTimestamp.now() payload = CPIMPayload(charset=charset, **{name: getattr(message, name) for name in Message.__slots__}) elif self.prefer_cpim and self.cpim_enabled and contains_mime_type(self.remote_accept_wrapped_types, message.content_type): if message.timestamp is None: message.timestamp = ISOTimestamp.now() payload = CPIMPayload(charset=charset, **{name: getattr(message, name) for name in Message.__slots__}) else: payload = SimplePayload(message.content, message.content_type, charset) except ChatStreamError, e: if message.notify_progress: data = NotificationData(message_id=message.id, message=None, code=0, reason=e.args[0]) notification_center.post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data) continue else: content, content_type = payload.encode() message_id = message.id notify_progress = message.notify_progress report = 'yes' if notify_progress else 'no' chunk = self.msrp_session.make_message(content, content_type=content_type, message_id=message_id) chunk.add_header(FailureReportHeader(report)) chunk.add_header(SuccessReportHeader(report)) try: self.msrp_session.send_chunk(chunk, response_cb=partial(self._on_transaction_response, message_id)) except Exception, e: if notify_progress: data = NotificationData(message_id=message_id, message=None, code=0, reason=str(e)) notification_center.post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data) except ProcExit: if notify_progress: data = NotificationData(message_id=message_id, message=None, code=0, reason='Stream ended') notification_center.post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data) raise else: if notify_progress: self.sent_messages.add(message_id) notification_center.post_notification('ChatStreamDidSendMessage', sender=self, data=NotificationData(message=chunk)) finally: self.message_queue_thread = None while self.sent_messages: message_id = self.sent_messages.pop() data = NotificationData(message_id=message_id, message=None, code=0, reason='Stream ended') notification_center.post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data) message_queue, self.message_queue = self.message_queue, queue() while message_queue: message = message_queue.wait() if message.notify_progress: data = NotificationData(message_id=message.id, message=None, code=0, reason='Stream ended') notification_center.post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data) @run_in_twisted_thread def _enqueue_message(self, message): if self._done: if message.notify_progress: data = NotificationData(message_id=message.id, message=None, code=0, reason='Stream ended') NotificationCenter().post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data) else: self.message_queue.send(message) @run_in_green_thread def _set_local_nickname(self, nickname, message_id): if self.msrp_session is None: # should we generate ChatStreamDidNotSetNickname here? return chunk = self.msrp.make_request('NICKNAME') chunk.add_header(UseNicknameHeader(nickname or u'')) try: self.msrp_session.send_chunk(chunk, response_cb=partial(self._on_nickname_transaction_response, message_id)) except Exception, e: self._failure_reason = str(e) NotificationCenter().post_notification('MediaStreamDidFail', sender=self, data=NotificationData(context='sending', reason=self._failure_reason)) def inject_otr_message(self, data): message = QueuedOTRInternalMessage(data) self._enqueue_message(message) def send_message(self, content, content_type='text/plain', recipients=None, courtesy_recipients=None, subject=None, timestamp=None, required=None, additional_headers=None): message = QueuedMessage(content, content_type, recipients=recipients, courtesy_recipients=courtesy_recipients, subject=subject, timestamp=timestamp, required=required, additional_headers=additional_headers, notify_progress=True) self._enqueue_message(message) return message.id def send_composing_indication(self, state, refresh=None, last_active=None, recipients=None): content = IsComposingDocument.create(state=State(state), refresh=Refresh(refresh) if refresh is not None else None, last_active=LastActive(last_active) if last_active is not None else None, content_type=ContentType('text')) message = QueuedMessage(content, IsComposingDocument.content_type, recipients=recipients, notify_progress=False) self._enqueue_message(message) return message.id def set_local_nickname(self, nickname): if not self.nickname_allowed: raise ChatStreamError('Setting nickname is not supported') message_id = '%x' % random.getrandbits(64) self._set_local_nickname(nickname, message_id) return message_id OTRTransport.register(ChatStream) # Chat related objects, including CPIM support as defined in RFC3862 # class ChatIdentity(object): _format_re = re.compile(r'^(?:"?(?P[^<]*[^"\s])"?)?\s*<(?Psips?:.+)>$') def __init__(self, uri, display_name=None): self.uri = uri self.display_name = display_name def __eq__(self, other): if isinstance(other, ChatIdentity): return self.uri.user == other.uri.user and self.uri.host == other.uri.host elif isinstance(other, BaseSIPURI): return self.uri.user == other.user and self.uri.host == other.host elif isinstance(other, basestring): try: other_uri = SIPURI.parse(other) except Exception: return False else: return self.uri.user == other_uri.user and self.uri.host == other_uri.host else: return NotImplemented def __ne__(self, other): return not (self == other) def __repr__(self): return '{0.__class__.__name__}(uri={0.uri!r}, display_name={0.display_name!r})'.format(self) def __str__(self): return self.__unicode__().encode('utf-8') def __unicode__(self): if self.display_name: return u'{0.display_name} <{0.uri}>'.format(self) else: return u'<{0.uri}>'.format(self) @classmethod def parse(cls, value): match = cls._format_re.match(value) if match is None: raise ValueError('Cannot parse identity value: %r' % value) return cls(SIPURI.parse(match.group('uri')), match.group('display_name')) class Message(object): __slots__ = 'content', 'content_type', 'sender', 'recipients', 'courtesy_recipients', 'subject', 'timestamp', 'required', 'additional_headers' def __init__(self, content, content_type, sender=None, recipients=None, courtesy_recipients=None, subject=None, timestamp=None, required=None, additional_headers=None): self.content = content self.content_type = content_type self.sender = sender self.recipients = recipients or [] self.courtesy_recipients = courtesy_recipients or [] self.subject = subject self.timestamp = ISOTimestamp(timestamp) if timestamp is not None else None self.required = required or [] self.additional_headers = additional_headers or [] class QueuedMessage(Message): __slots__ = 'id', 'notify_progress' def __init__(self, content, content_type, sender=None, recipients=None, courtesy_recipients=None, subject=None, timestamp=None, required=None, additional_headers=None, id=None, notify_progress=True): super(QueuedMessage, self).__init__(content, content_type, sender, recipients, courtesy_recipients, subject, timestamp, required, additional_headers) self.id = id or '%x' % random.getrandbits(64) self.notify_progress = notify_progress class QueuedOTRInternalMessage(QueuedMessage): def __init__(self, content): super(QueuedOTRInternalMessage, self).__init__(content, 'text/plain', notify_progress=False) class SimplePayload(object): def __init__(self, content, content_type, charset=None): if not isinstance(content, bytes): raise TypeError("content should be an instance of bytes") self.content = content self.content_type = content_type self.charset = charset def encode(self): if self.charset is not None: return self.content, '{0.content_type}; charset="{0.charset}"'.format(self) else: return self.content, str(self.content_type) @classmethod def decode(cls, content, content_type): if not isinstance(content, bytes): raise TypeError("content should be an instance of bytes") type_helper = EmailParser().parsestr('Content-Type: {}'.format(content_type)) content_type = type_helper.get_content_type() charset = type_helper.get_content_charset() return cls(content, content_type, charset) class CPIMPayload(object): standard_namespace = u'urn:ietf:params:cpim-headers:' headers_re = re.compile(r'(?:([^:]+?)\.)?(.+?):\s*(.+?)(?:\r\n|$)') subject_re = re.compile(r'^(?:;lang=([a-z]{1,8}(?:-[a-z0-9]{1,8})*)\s+)?(.*)$') namespace_re = re.compile(r'^(?:(\S+) ?)?<(.*)>$') def __init__(self, content, content_type, charset=None, sender=None, recipients=None, courtesy_recipients=None, subject=None, timestamp=None, required=None, additional_headers=None): if not isinstance(content, bytes): raise TypeError("content should be an instance of bytes") self.content = content self.content_type = content_type self.charset = charset self.sender = sender self.recipients = recipients or [] self.courtesy_recipients = courtesy_recipients or [] self.subject = subject if isinstance(subject, (MultilingualText, type(None))) else MultilingualText(subject) self.timestamp = ISOTimestamp(timestamp) if timestamp is not None else None self.required = required or [] self.additional_headers = additional_headers or [] def encode(self): namespaces = {u'': CPIMNamespace(self.standard_namespace)} header_list = [] if self.sender is not None: header_list.append(u'From: {}'.format(self.sender)) header_list.extend(u'To: {}'.format(recipient) for recipient in self.recipients) header_list.extend(u'cc: {}'.format(recipient) for recipient in self.courtesy_recipients) if self.subject is not None: header_list.append(u'Subject: {}'.format(self.subject)) header_list.extend(u'Subject:;lang={} {}'.format(language, translation) for language, translation in self.subject.translations.iteritems()) if self.timestamp is not None: header_list.append(u'DateTime: {}'.format(self.timestamp)) if self.required: header_list.append(u'Required: {}'.format(','.join(self.required))) for header in self.additional_headers: if namespaces.get(header.namespace.prefix) != header.namespace: if header.namespace.prefix: header_list.append(u'NS: {0.namespace.prefix} <{0.namespace}>'.format(header)) else: header_list.append(u'NS: <{0.namespace}>'.format(header)) namespaces[header.namespace.prefix] = header.namespace if header.namespace.prefix: header_list.append(u'{0.namespace.prefix}.{0.name}: {0.value}'.format(header)) else: header_list.append(u'{0.name}: {0.value}'.format(header)) headers = '\r\n'.join(header.encode('cpim-header') for header in header_list) mime_message = EmailMessage() mime_message.set_payload(self.content) mime_message.set_type(self.content_type) if self.charset is not None: mime_message.set_param('charset', self.charset) return headers + '\r\n\r\n' + mime_message.as_string(), 'message/cpim' @classmethod def decode(cls, message): if not isinstance(message, bytes): raise TypeError("message should be an instance of bytes") headers, separator, body = message.partition('\r\n\r\n') if not separator: raise CPIMParserError('Invalid CPIM message') sender = None recipients = [] courtesy_recipients = [] subject = None timestamp = None required = [] additional_headers = [] namespaces = {u'': CPIMNamespace(cls.standard_namespace)} subjects = {} for prefix, name, value in cls.headers_re.findall(headers): namespace = namespaces.get(prefix) if namespace is None or '.' in name: continue try: value = value.decode('cpim-header') if namespace == cls.standard_namespace: if name == 'From': sender = ChatIdentity.parse(value) elif name == 'To': recipients.append(ChatIdentity.parse(value)) elif name == 'cc': courtesy_recipients.append(ChatIdentity.parse(value)) elif name == 'Subject': match = cls.subject_re.match(value) if match is None: raise ValueError('Illegal Subject header: %r' % value) lang, subject = match.groups() # language tags must be ASCII subjects[str(lang) if lang is not None else None] = subject elif name == 'DateTime': timestamp = ISOTimestamp(value) elif name == 'Required': required.extend(re.split(r'\s*,\s*', value)) elif name == 'NS': match = cls.namespace_re.match(value) if match is None: raise ValueError('Illegal NS header: %r' % value) prefix, uri = match.groups() namespaces[prefix] = CPIMNamespace(uri, prefix) else: additional_headers.append(CPIMHeader(name, namespace, value)) else: additional_headers.append(CPIMHeader(name, namespace, value)) except ValueError: pass if None in subjects: subject = MultilingualText(subjects.pop(None), **subjects) elif subjects: subject = MultilingualText(**subjects) mime_message = EmailParser().parsestr(body) content_type = mime_message.get_content_type() if content_type is None: raise CPIMParserError("CPIM message missing Content-Type MIME header") content = mime_message.get_payload() # If the content_type is 'message/imdn+xml' the parser thinks it is a multipart message, hence the result of get_payload # will be a list. Since it is not actually a list we take first item in the list and get the payload again # --Tijmen if content_type == 'message/imdn+xml': content = content[0].get_payload() charset = mime_message.get_content_charset() return cls(content, content_type, charset, sender, recipients, courtesy_recipients, subject, timestamp, required, additional_headers) class CPIMParserError(StandardError): pass class CPIMNamespace(unicode): def __new__(cls, value, prefix=u''): obj = unicode.__new__(cls, value) obj.prefix = prefix return obj class CPIMHeader(object): def __init__(self, name, namespace, value): self.name = name self.namespace = namespace self.value = value class CPIMCodec(codecs.Codec): character_map = {c: u'\\u{:04x}'.format(c) for c in range(32) + [127]} character_map[ord(u'\\')] = u'\\\\' @classmethod def encode(cls, input, errors='strict'): return input.translate(cls.character_map).encode('utf-8', errors), len(input) @classmethod def decode(cls, input, errors='strict'): return input.decode('utf-8', errors).encode('raw-unicode-escape', errors).decode('unicode-escape', errors), len(input) def cpim_codec_search(name): if name.lower() in ('cpim-header', 'cpim_header'): return codecs.CodecInfo(name='CPIM-header', encode=CPIMCodec.encode, decode=CPIMCodec.decode, incrementalencoder=codecs.IncrementalEncoder, incrementaldecoder=codecs.IncrementalDecoder, streamwriter=codecs.StreamWriter, streamreader=codecs.StreamReader) codecs.register(cpim_codec_search) del cpim_codec_search ================================================ FILE: sipsimple/streams/msrp/filetransfer.py ================================================ """ This module provides classes to parse and generate SDP related to SIP sessions that negotiate File Transfer. """ __all__ = ['FileTransferStream', 'FileSelector'] import cPickle as pickle import hashlib import mimetypes import os import random import re import time import uuid from abc import ABCMeta, abstractmethod from application.notification import NotificationCenter, NotificationData, IObserver from application.python.threadpool import ThreadPool, run_in_threadpool from application.python.types import MarkerType from application.system import FileExistsError, makedirs, openfile, unlink from itertools import count from msrplib.protocol import FailureReportHeader, SuccessReportHeader, ContentTypeHeader, IntegerHeaderType, MSRPNamedHeader, HeaderParsingError from msrplib.session import MSRPSession from msrplib.transport import make_response from Queue import Queue from threading import Event, Lock from zope.interface import implements from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.core import SDPAttribute from sipsimple.storage import ISIPSimpleApplicationDataStorage from sipsimple.streams import InvalidStreamError, UnknownStreamError from sipsimple.streams.msrp import MSRPStreamBase from sipsimple.threading import run_in_twisted_thread, run_in_thread from sipsimple.util import sha1 HASH = type(hashlib.sha1()) class RandomID: __metaclass__ = MarkerType class FileSelectorHash(str): _hash_re = re.compile(r'^sha-1(:[0-9A-F]{2}){20}$') _byte_re = re.compile(r'..') def __new__(cls, value): if isinstance(value, str): if value.startswith('sha1:'): # backward compatibility hack (sort of). value = 'sha-1' + value[len('sha1'):] if not cls._hash_re.match(value): raise ValueError("Invalid hash value: {!r}".format(value)) return super(FileSelectorHash, cls).__new__(cls, value) elif isinstance(value, (HASH, sha1)): return super(FileSelectorHash, cls).__new__(cls, cls.encode_hash(value)) else: raise ValueError("Invalid hash value: {!r}".format(value)) def __eq__(self, other): if isinstance(other, str): return super(FileSelectorHash, self).__eq__(other) elif isinstance(other, (HASH, sha1)) and other.name.lower() == 'sha1': return super(FileSelectorHash, self).__eq__(self.encode_hash(other)) else: return NotImplemented def __ne__(self, other): return not self == other @classmethod def encode_hash(cls, hash_instance): if hash_instance.name.lower() != 'sha1': raise TypeError("Invalid hash type: {.name} (only sha1 hashes are supported).".format(hash_instance)) # unexpected as it may be, using a regular expression is the fastest method to do this return 'sha-1:' + ':'.join(cls._byte_re.findall(hash_instance.hexdigest().upper())) class FileSelector(object): _name_re = re.compile(r'name:"([^"]+)"') _size_re = re.compile(r'size:(\d+)') _type_re = re.compile(r'type:([^ ]+)') _hash_re = re.compile(r'hash:([^ ]+)') def __init__(self, name=None, type=None, size=None, hash=None, fd=None): # If present, hash should be a sha1 object or a string in the form: sha-1:72:24:5F:E8:65:3D:DA:F3:71:36:2F:86:D4:71:91:3E:E4:A2:CE:2E # According to the specification, only sha1 is supported ATM. self.name = name self.type = type self.size = size self.hash = hash self.fd = fd @property def hash(self): return self.__dict__['hash'] @hash.setter def hash(self, value): self.__dict__['hash'] = None if value is None else FileSelectorHash(value) @classmethod def parse(cls, string): name_match = cls._name_re.search(string) size_match = cls._size_re.search(string) type_match = cls._type_re.search(string) hash_match = cls._hash_re.search(string) name = name_match and name_match.group(1).decode('utf-8') size = size_match and int(size_match.group(1)) type = type_match and type_match.group(1) hash = hash_match and hash_match.group(1) return cls(name, type, size, hash) @classmethod def for_file(cls, path, type=None, hash=None): name = unicode(path) fd = open(name, 'rb') size = os.fstat(fd.fileno()).st_size if type is None: mime_type, encoding = mimetypes.guess_type(name) if encoding is not None: type = 'application/x-%s' % encoding elif mime_type is not None: type = mime_type else: type = 'application/octet-stream' return cls(name, type, size, hash, fd) @property def sdp_repr(self): items = [('name', self.name and '"%s"' % os.path.basename(self.name).encode('utf-8')), ('type', self.type), ('size', self.size), ('hash', self.hash)] return ' '.join('%s:%s' % (name, value) for name, value in items if value is not None) class UniqueFilenameGenerator(object): @classmethod def generate(cls, name): yield name prefix, extension = os.path.splitext(name) for x in count(1): yield "%s-%d%s" % (prefix, x, extension) class FileMetadataEntry(object): def __init__(self, hash, filename, partial_hash=None): self.hash = hash self.filename = filename self.mtime = os.path.getmtime(self.filename) self.partial_hash = partial_hash @classmethod def from_selector(cls, file_selector): return cls(file_selector.hash.lower(), file_selector.name) class FileTransfersMetadata(object): __filename__ = 'transfer_metadata' __lifetime__ = 60*60*24*7 def __init__(self): self.data = {} self.lock = Lock() self.loaded = False self.directory = None def _load(self): if self.loaded: return from sipsimple.application import SIPApplication if ISIPSimpleApplicationDataStorage.providedBy(SIPApplication.storage): self.directory = SIPApplication.storage.directory if self.directory is not None: try: with open(os.path.join(self.directory, self.__filename__), 'rb') as f: data = pickle.loads(f.read()) except Exception: data = {} now = time.time() for hash, entry in data.items(): try: mtime = os.path.getmtime(entry.filename) except OSError: data.pop(hash) else: if mtime != entry.mtime or now - mtime > self.__lifetime__: data.pop(hash) self.data.update(data) self.loaded = True @run_in_thread('file-io') def _save(self, data): if self.directory is not None: with open(os.path.join(self.directory, self.__filename__), 'wb') as f: f.write(data) def __enter__(self): self.lock.acquire() self._load() return self.data def __exit__(self, exc_type, exc_val, exc_tb): if None is exc_type is exc_val is exc_tb: self._save(pickle.dumps(self.data)) self.lock.release() class FileTransferHandler(object): __metaclass__ = ABCMeta implements(IObserver) threadpool = ThreadPool(name='FileTransfers', min_threads=0, max_threads=100) threadpool.start() def __init__(self): self.stream = None self.session = None self._started = False self._session_started = False self._initialize_done = False self._initialize_successful = False def initialize(self, stream, session): self.stream = stream self.session = session notification_center = NotificationCenter() notification_center.add_observer(self, sender=stream) notification_center.add_observer(self, sender=session) notification_center.add_observer(self, sender=self) @property def filename(self): return self.stream.file_selector.name if self.stream is not None else None @abstractmethod def start(self): raise NotImplementedError @abstractmethod def end(self): raise NotImplementedError @abstractmethod def process_chunk(self, chunk): raise NotImplementedError def __terminate(self): notification_center = NotificationCenter() notification_center.remove_observer(self, sender=self.stream) notification_center.remove_observer(self, sender=self.session) notification_center.remove_observer(self, sender=self) try: self.stream.file_selector.fd.close() except AttributeError: # when self.stream.file_selector.fd is None pass except IOError: # we can get this if we try to close while another thread is reading from it pass self.stream = None self.session = None @run_in_twisted_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, None) if handler is not None: handler(notification) def _NH_MediaStreamDidNotInitialize(self, notification): if not self._initialize_done: self.end() self.__terminate() def _NH_MediaStreamDidStart(self, notification): self._started = True self.start() def _NH_MediaStreamWillEnd(self, notification): if self._started: self.end() elif self._session_started: notification.center.post_notification('FileTransferHandlerDidEnd', sender=self, data=NotificationData(error=True, reason='Refused')) def _NH_SIPSessionWillStart(self, notification): self._session_started = True def _NH_SIPSessionDidFail(self, notification): if not self._session_started and self._initialize_successful: if notification.data.code == 487: reason = 'Cancelled' else: reason = notification.data.reason or 'Failed' notification.center.post_notification('FileTransferHandlerDidEnd', sender=self, data=NotificationData(error=True, reason=reason)) def _NH_FileTransferHandlerDidInitialize(self, notification): self._initialize_done = True self._initialize_successful = True def _NH_FileTransferHandlerDidNotInitialize(self, notification): self._initialize_done = True self._initialize_successful = False def _NH_FileTransferHandlerDidEnd(self, notification): self.__terminate() class OffsetHeader(MSRPNamedHeader): name = 'Offset' type = IntegerHeaderType class EndTransfer: __metaclass__ = MarkerType class IncomingFileTransferHandler(FileTransferHandler): metadata = FileTransfersMetadata() def __init__(self): super(IncomingFileTransferHandler, self).__init__() self.hash = sha1() self.queue = Queue() self.offset = 0 self.received_chunks = 0 @property def save_directory(self): return self.__dict__.get('save_directory') @save_directory.setter def save_directory(self, value): if self.stream is not None: raise AttributeError('cannot set save_directory, transfer is in progress') self.__dict__['save_directory'] = value def initialize(self, stream, session): super(IncomingFileTransferHandler, self).initialize(stream, session) try: directory = self.save_directory or SIPSimpleSettings().file_transfer.directory.normalized makedirs(directory) with self.metadata as metadata: try: prev_file = metadata.pop(stream.file_selector.hash.lower()) mtime = os.path.getmtime(prev_file.filename) if mtime != prev_file.mtime: raise ValueError('file was modified') filename = os.path.join(directory, os.path.basename(stream.file_selector.name)) try: os.link(prev_file.filename, filename) except (AttributeError, OSError): stream.file_selector.name = prev_file.filename else: stream.file_selector.name = filename unlink(prev_file.filename) stream.file_selector.fd = openfile(stream.file_selector.name, 'ab') # open doesn't seek to END in append mode on win32 until first write, but openfile does self.offset = stream.file_selector.fd.tell() self.hash = prev_file.partial_hash except (KeyError, EnvironmentError, ValueError): for name in UniqueFilenameGenerator.generate(os.path.join(directory, os.path.basename(stream.file_selector.name))): try: stream.file_selector.fd = openfile(name, 'xb') except FileExistsError: continue else: stream.file_selector.name = name break except Exception, e: NotificationCenter().post_notification('FileTransferHandlerDidNotInitialize', sender=self, data=NotificationData(reason=str(e))) else: NotificationCenter().post_notification('FileTransferHandlerDidInitialize', sender=self) def end(self): self.queue.put(EndTransfer) def process_chunk(self, chunk): if chunk.method == 'SEND': if not self.received_chunks and chunk.byte_range.start == 1: self.stream.file_selector.fd.truncate(0) self.stream.file_selector.fd.seek(0) self.hash = sha1() self.offset = 0 self.received_chunks += 1 self.queue.put(chunk) elif chunk.method == 'FILE_OFFSET': if self.received_chunks > 0: response = make_response(chunk, 413, 'Unwanted message') else: offset = self.stream.file_selector.fd.tell() response = make_response(chunk, 200, 'OK') response.add_header(OffsetHeader(offset)) self.stream.msrp_session.send_chunk(response) @run_in_threadpool(FileTransferHandler.threadpool) def start(self): notification_center = NotificationCenter() notification_center.post_notification('FileTransferHandlerDidStart', sender=self) file_selector = self.stream.file_selector fd = file_selector.fd while True: chunk = self.queue.get() if chunk is EndTransfer: break try: fd.write(chunk.data) except EnvironmentError, e: fd.close() notification_center.post_notification('FileTransferHandlerError', sender=self, data=NotificationData(error=str(e))) notification_center.post_notification('FileTransferHandlerDidEnd', sender=self, data=NotificationData(error=True, reason=str(e))) return self.hash.update(chunk.data) self.offset += chunk.size transferred_bytes = chunk.byte_range.start + chunk.size - 1 total_bytes = file_selector.size = chunk.byte_range.total notification_center.post_notification('FileTransferHandlerProgress', sender=self, data=NotificationData(transferred_bytes=transferred_bytes, total_bytes=total_bytes)) if transferred_bytes == total_bytes: break fd.close() # Transfer is finished if self.offset != self.stream.file_selector.size: notification_center.post_notification('FileTransferHandlerDidEnd', sender=self, data=NotificationData(error=True, reason='Incomplete file')) return if self.hash != self.stream.file_selector.hash: unlink(self.filename) # something got corrupted, better delete the file notification_center.post_notification('FileTransferHandlerDidEnd', sender=self, data=NotificationData(error=True, reason='File hash mismatch')) return notification_center.post_notification('FileTransferHandlerDidEnd', sender=self, data=NotificationData(error=False, reason=None)) def _NH_MediaStreamDidNotInitialize(self, notification): if self.stream.file_selector.fd is not None: position = self.stream.file_selector.fd.tell() self.stream.file_selector.fd.close() if position == 0: unlink(self.stream.file_selector.name) super(IncomingFileTransferHandler, self)._NH_MediaStreamDidNotInitialize(notification) def _NH_FileTransferHandlerDidEnd(self, notification): if notification.data.error and self.stream.file_selector.hash is not None: if os.path.getsize(self.stream.file_selector.name) == 0: unlink(self.stream.file_selector.name) else: with self.metadata as metadata: entry = FileMetadataEntry.from_selector(self.stream.file_selector) entry.partial_hash = self.hash metadata[entry.hash] = entry super(IncomingFileTransferHandler, self)._NH_FileTransferHandlerDidEnd(notification) class OutgoingFileTransferHandler(FileTransferHandler): file_part_size = 64*1024 def __init__(self): super(OutgoingFileTransferHandler, self).__init__() self.stop_event = Event() self.finished_event = Event() self.file_offset_event = Event() self.message_id = '%x' % random.getrandbits(64) self.offset = 0 def initialize(self, stream, session): super(OutgoingFileTransferHandler, self).initialize(stream, session) if stream.file_selector.fd is None: NotificationCenter().post_notification('FileTransferHandlerDidNotInitialize', sender=self, data=NotificationData(reason='file descriptor not specified')) return if stream.file_selector.size == 0: NotificationCenter().post_notification('FileTransferHandlerDidNotInitialize', sender=self, data=NotificationData(reason='file is empty')) return if stream.file_selector.hash is None: self._calculate_file_hash() else: NotificationCenter().post_notification('FileTransferHandlerDidInitialize', sender=self) @run_in_threadpool(FileTransferHandler.threadpool) def _calculate_file_hash(self): file_hash = hashlib.sha1() processed = 0 notification_center = NotificationCenter() notification_center.post_notification('FileTransferHandlerHashProgress', sender=self, data=NotificationData(processed=0, total=self.stream.file_selector.size)) file_selector = self.stream.file_selector fd = file_selector.fd while not self.stop_event.is_set(): try: content = fd.read(self.file_part_size) except EnvironmentError, e: fd.close() notification_center.post_notification('FileTransferHandlerDidNotInitialize', sender=self, data=NotificationData(reason=str(e))) return if not content: file_selector.hash = file_hash notification_center.post_notification('FileTransferHandlerDidInitialize', sender=self) break file_hash.update(content) processed += len(content) notification_center.post_notification('FileTransferHandlerHashProgress', sender=self, data=NotificationData(processed=processed, total=file_selector.size)) else: fd.close() notification_center.post_notification('FileTransferHandlerDidNotInitialize', sender=self, data=NotificationData(reason='Interrupted transfer')) def end(self): self.stop_event.set() self.file_offset_event.set() # in case we are busy waiting on it @run_in_threadpool(FileTransferHandler.threadpool) def start(self): notification_center = NotificationCenter() notification_center.post_notification('FileTransferHandlerDidStart', sender=self) if self.stream.file_offset_supported: self._send_file_offset_chunk() self.file_offset_event.wait() finished = False failure_reason = None fd = self.stream.file_selector.fd fd.seek(self.offset) try: while not self.stop_event.is_set(): try: data = fd.read(self.file_part_size) except EnvironmentError, e: failure_reason = str(e) break if not data: finished = True break self._send_chunk(data) finally: fd.close() if not finished: notification_center.post_notification('FileTransferHandlerDidEnd', sender=self, data=NotificationData(error=True, reason=failure_reason or 'Interrupted transfer')) return # Wait until the stream ends or we get all reports self.stop_event.wait() if self.finished_event.is_set(): notification_center.post_notification('FileTransferHandlerDidEnd', sender=self, data=NotificationData(error=False, reason=None)) else: notification_center.post_notification('FileTransferHandlerDidEnd', sender=self, data=NotificationData(error=True, reason='Incomplete transfer')) def _on_transaction_response(self, response): if self.stop_event.is_set(): return if response.code != 200: NotificationCenter().post_notification('FileTransferHandlerError', sender=self, data=NotificationData(error=response.comment)) self.end() @run_in_twisted_thread def _send_chunk(self, data): if self.stop_event.is_set(): return data_len = len(data) chunk = self.stream.msrp.make_send_request(message_id=self.message_id, data=data, start=self.offset+1, end=self.offset+data_len, length=self.stream.file_selector.size) chunk.add_header(ContentTypeHeader(self.stream.file_selector.type)) chunk.add_header(SuccessReportHeader('yes')) chunk.add_header(FailureReportHeader('yes')) try: self.stream.msrp_session.send_chunk(chunk, response_cb=self._on_transaction_response) except Exception, e: NotificationCenter().post_notification('FileTransferHandlerError', sender=self, data=NotificationData(error=str(e))) else: self.offset += data_len @run_in_twisted_thread def _send_file_offset_chunk(self): def response_cb(response): if not self.stop_event.is_set() and response.code == 200: try: offset = response.headers['Offset'].decoded except (KeyError, HeaderParsingError): offset = 0 self.offset = offset self.file_offset_event.set() if self.stop_event.is_set(): self.file_offset_event.set() return chunk = self.stream.msrp.make_request('FILE_OFFSET') # TODO: _ is illegal in MSRP method names according to RFC 4975 try: self.stream.msrp_session.send_chunk(chunk, response_cb=response_cb) except Exception, e: NotificationCenter().post_notification('FileTransferHandlerError', sender=self, data=NotificationData(error=str(e))) def process_chunk(self, chunk): # here we process the REPORT chunks notification_center = NotificationCenter() if chunk.status.code == 200: transferred_bytes = chunk.byte_range.end total_bytes = chunk.byte_range.total notification_center.post_notification('FileTransferHandlerProgress', sender=self, data=NotificationData(transferred_bytes=transferred_bytes, total_bytes=total_bytes)) if transferred_bytes == total_bytes: self.finished_event.set() self.end() else: notification_center.post_notification('FileTransferHandlerError', sender=self, data=NotificationData(error=chunk.status.comment)) self.end() class FileTransferMSRPSession(MSRPSession): def _handle_incoming_FILE_OFFSET(self, chunk): self._on_incoming_cb(chunk) class FileTransferStream(MSRPStreamBase): type = 'file-transfer' priority = 10 msrp_session_class = FileTransferMSRPSession media_type = 'message' accept_types = ['*'] accept_wrapped_types = None IncomingTransferHandler = IncomingFileTransferHandler OutgoingTransferHandler = OutgoingFileTransferHandler def __init__(self, file_selector, direction, transfer_id=RandomID): if direction not in ('sendonly', 'recvonly'): raise ValueError("direction must be one of 'sendonly' or 'recvonly'") super(FileTransferStream, self).__init__(direction=direction) self.file_selector = file_selector self.transfer_id = transfer_id if transfer_id is not RandomID else str(uuid.uuid4()) if direction == 'sendonly': self.handler = self.OutgoingTransferHandler() else: self.handler = self.IncomingTransferHandler() @classmethod def new_from_sdp(cls, session, remote_sdp, stream_index): remote_stream = remote_sdp.media[stream_index] if remote_stream.media != 'message' or 'file-selector' not in remote_stream.attributes: raise UnknownStreamError expected_transport = 'TCP/TLS/MSRP' if session.account.msrp.transport == 'tls' else 'TCP/MSRP' if remote_stream.transport != expected_transport: raise InvalidStreamError("expected %s transport in file transfer stream, got %s" % (expected_transport, remote_stream.transport)) if remote_stream.formats != ['*']: raise InvalidStreamError("wrong format list specified") try: file_selector = FileSelector.parse(remote_stream.attributes.getfirst('file-selector')) except Exception as e: raise InvalidStreamError("error parsing file-selector: {}".format(e)) transfer_id = remote_stream.attributes.getfirst('file-transfer-id', None) if remote_stream.direction == 'sendonly': stream = cls(file_selector, 'recvonly', transfer_id) elif remote_stream.direction == 'recvonly': stream = cls(file_selector, 'sendonly', transfer_id) else: raise InvalidStreamError("wrong stream direction specified") stream.remote_role = remote_stream.attributes.getfirst('setup', 'active') return stream def initialize(self, session, direction): self._initialize_args = session, direction NotificationCenter().add_observer(self, sender=self.handler) self.handler.initialize(self, session) def _create_local_media(self, uri_path): local_media = super(FileTransferStream, self)._create_local_media(uri_path) local_media.attributes.append(SDPAttribute('file-selector', self.file_selector.sdp_repr)) local_media.attributes.append(SDPAttribute('x-file-offset', '')) if self.transfer_id is not None: local_media.attributes.append(SDPAttribute('file-transfer-id', self.transfer_id)) return local_media @property def file_offset_supported(self): try: return 'x-file-offset' in self.remote_media.attributes except AttributeError: return False @run_in_twisted_thread def _NH_FileTransferHandlerDidInitialize(self, notification): session, direction = self._initialize_args del self._initialize_args if not self._done: super(FileTransferStream, self).initialize(session, direction) @run_in_twisted_thread def _NH_FileTransferHandlerDidNotInitialize(self, notification): del self._initialize_args if not self._done: notification.center.post_notification('MediaStreamDidNotInitialize', sender=self, data=notification.data) @run_in_twisted_thread def _NH_FileTransferHandlerError(self, notification): self._failure_reason = notification.data.error notification.center.post_notification('MediaStreamDidFail', sender=self, data=NotificationData(context='transferring', reason=self._failure_reason)) def _NH_MediaStreamDidNotInitialize(self, notification): notification.center.remove_observer(self, sender=self.handler) def _NH_MediaStreamWillEnd(self, notification): notification.center.remove_observer(self, sender=self.handler) def _handle_REPORT(self, chunk): # in theory, REPORT can come with Byte-Range which would limit the scope of the REPORT to the part of the message. self.handler.process_chunk(chunk) def _handle_SEND(self, chunk): notification_center = NotificationCenter() if chunk.size == 0: # keep-alive self.msrp_session.send_report(chunk, 200, 'OK') return if self.direction=='sendonly': self.msrp_session.send_report(chunk, 413, 'Unwanted Message') return if chunk.content_type.lower() == 'message/cpim': # In order to properly support the CPIM wrapper, msrplib needs to be refactored. -Luci self.msrp_session.send_report(chunk, 415, 'Invalid Content-Type') self._failure_reason = "CPIM wrapper is not supported" notification_center.post_notification('MediaStreamDidFail', sender=self, data=NotificationData(context='reading', reason=self._failure_reason)) return try: self.msrp_session.send_report(chunk, 200, 'OK') except Exception: pass # Best effort approach: even if we couldn't send the REPORT keep writing the chunks, we might have them all -Saul self.handler.process_chunk(chunk) def _handle_FILE_OFFSET(self, chunk): if self.direction != 'recvonly': response = make_response(chunk, 413, 'Unwanted message') self.msrp_session.send_chunk(response) return self.handler.process_chunk(chunk) ================================================ FILE: sipsimple/streams/msrp/screensharing.py ================================================ """ This module provides classes to parse and generate SDP related to SIP sessions that negotiate Screen Sharing. """ __all__ = ['ScreenSharingStream', 'VNCConnectionError', 'ScreenSharingHandler', 'ScreenSharingServerHandler', 'ScreenSharingViewerHandler', 'InternalVNCViewerHandler', 'InternalVNCServerHandler', 'ExternalVNCViewerHandler', 'ExternalVNCServerHandler'] from abc import ABCMeta, abstractmethod, abstractproperty from application.notification import NotificationCenter, NotificationData, IObserver from application.python.descriptor import WriteOnceAttribute from eventlib.coros import queue from eventlib.greenio import GreenSocket from eventlib.proc import spawn from eventlib.util import tcp_socket, set_reuse_addr from msrplib.protocol import FailureReportHeader, SuccessReportHeader, ContentTypeHeader from msrplib.transport import make_response, make_report from twisted.internet.error import ConnectionDone from zope.interface import implements from sipsimple.core import SDPAttribute from sipsimple.streams import InvalidStreamError, UnknownStreamError from sipsimple.streams.msrp import MSRPStreamBase from sipsimple.threading import run_in_twisted_thread class VNCConnectionError(Exception): pass class ScreenSharingHandler(object): __metaclass__ = ABCMeta implements(IObserver) def __init__(self): self.incoming_msrp_queue = None self.outgoing_msrp_queue = None self.msrp_reader_thread = None self.msrp_writer_thread = None def initialize(self, stream): self.incoming_msrp_queue = stream.incoming_queue self.outgoing_msrp_queue = stream.outgoing_queue NotificationCenter().add_observer(self, sender=stream) @abstractproperty def type(self): raise NotImplementedError @abstractmethod def _msrp_reader(self): raise NotImplementedError @abstractmethod def _msrp_writer(self): raise NotImplementedError def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, None) if handler is not None: handler(notification) def _NH_MediaStreamDidStart(self, notification): self.msrp_reader_thread = spawn(self._msrp_reader) self.msrp_writer_thread = spawn(self._msrp_writer) def _NH_MediaStreamWillEnd(self, notification): notification.center.remove_observer(self, sender=notification.sender) if self.msrp_reader_thread is not None: self.msrp_reader_thread.kill() self.msrp_reader_thread = None if self.msrp_writer_thread is not None: self.msrp_writer_thread.kill() self.msrp_writer_thread = None class ScreenSharingServerHandler(ScreenSharingHandler): type = property(lambda self: 'passive') class ScreenSharingViewerHandler(ScreenSharingHandler): type = property(lambda self: 'active') class InternalVNCViewerHandler(ScreenSharingViewerHandler): @run_in_twisted_thread def send(self, data): self.outgoing_msrp_queue.send(data) def _msrp_reader(self): notification_center = NotificationCenter() while True: data = self.incoming_msrp_queue.wait() notification_center.post_notification('ScreenSharingStreamGotData', sender=self, data=NotificationData(data=data)) def _msrp_writer(self): pass class InternalVNCServerHandler(ScreenSharingServerHandler): @run_in_twisted_thread def send(self, data): self.outgoing_msrp_queue.send(data) def _msrp_reader(self): notification_center = NotificationCenter() while True: data = self.incoming_msrp_queue.wait() notification_center.post_notification('ScreenSharingStreamGotData', sender=self, data=NotificationData(data=data)) def _msrp_writer(self): pass class ExternalVNCViewerHandler(ScreenSharingViewerHandler): address = ('localhost', 0) connect_timeout = 5 def __init__(self): super(ExternalVNCViewerHandler, self).__init__() self.vnc_starter_thread = None self.vnc_socket = GreenSocket(tcp_socket()) set_reuse_addr(self.vnc_socket) self.vnc_socket.settimeout(self.connect_timeout) self.vnc_socket.bind(self.address) self.vnc_socket.listen(1) self.address = self.vnc_socket.getsockname() def _msrp_reader(self): while True: try: data = self.incoming_msrp_queue.wait() self.vnc_socket.sendall(data) except Exception, e: self.msrp_reader_thread = None # avoid issues caused by the notification handler killing this greenlet during post_notification NotificationCenter().post_notification('ScreenSharingHandlerDidFail', sender=self, data=NotificationData(context='sending', reason=str(e))) break def _msrp_writer(self): while True: try: data = self.vnc_socket.recv(2048) if not data: raise VNCConnectionError("connection with the VNC viewer was closed") self.outgoing_msrp_queue.send(data) except Exception, e: self.msrp_writer_thread = None # avoid issues caused by the notification handler killing this greenlet during post_notification NotificationCenter().post_notification('ScreenSharingHandlerDidFail', sender=self, data=NotificationData(context='reading', reason=str(e))) break def _start_vnc_connection(self): try: sock, addr = self.vnc_socket.accept() self.vnc_socket.close() self.vnc_socket = sock self.vnc_socket.settimeout(None) except Exception, e: self.vnc_starter_thread = None # avoid issues caused by the notification handler killing this greenlet during post_notification NotificationCenter().post_notification('ScreenSharingHandlerDidFail', sender=self, data=NotificationData(context='connecting', reason=str(e))) else: self.msrp_reader_thread = spawn(self._msrp_reader) self.msrp_writer_thread = spawn(self._msrp_writer) finally: self.vnc_starter_thread = None def _NH_MediaStreamDidStart(self, notification): self.vnc_starter_thread = spawn(self._start_vnc_connection) def _NH_MediaStreamWillEnd(self, notification): if self.vnc_starter_thread is not None: self.vnc_starter_thread.kill() self.vnc_starter_thread = None super(ExternalVNCViewerHandler, self)._NH_MediaStreamWillEnd(notification) self.vnc_socket.close() class ExternalVNCServerHandler(ScreenSharingServerHandler): address = ('localhost', 5900) connect_timeout = 5 def __init__(self): super(ExternalVNCServerHandler, self).__init__() self.vnc_starter_thread = None self.vnc_socket = None def _msrp_reader(self): while True: try: data = self.incoming_msrp_queue.wait() self.vnc_socket.sendall(data) except Exception, e: self.msrp_reader_thread = None # avoid issues caused by the notification handler killing this greenlet during post_notification NotificationCenter().post_notification('ScreenSharingHandlerDidFail', sender=self, data=NotificationData(context='sending', reason=str(e))) break def _msrp_writer(self): while True: try: data = self.vnc_socket.recv(2048) if not data: raise VNCConnectionError("connection to the VNC server was closed") self.outgoing_msrp_queue.send(data) except Exception, e: self.msrp_writer_thread = None # avoid issues caused by the notification handler killing this greenlet during post_notification NotificationCenter().post_notification('ScreenSharingHandlerDidFail', sender=self, data=NotificationData(context='reading', reason=str(e))) break def _start_vnc_connection(self): try: self.vnc_socket = GreenSocket(tcp_socket()) self.vnc_socket.settimeout(self.connect_timeout) self.vnc_socket.connect(self.address) self.vnc_socket.settimeout(None) except Exception, e: self.vnc_starter_thread = None # avoid issues caused by the notification handler killing this greenlet during post_notification NotificationCenter().post_notification('ScreenSharingHandlerDidFail', sender=self, data=NotificationData(context='connecting', reason=str(e))) else: self.msrp_reader_thread = spawn(self._msrp_reader) self.msrp_writer_thread = spawn(self._msrp_writer) finally: self.vnc_starter_thread = None def _NH_MediaStreamDidStart(self, notification): self.vnc_starter_thread = spawn(self._start_vnc_connection) def _NH_MediaStreamWillEnd(self, notification): if self.vnc_starter_thread is not None: self.vnc_starter_thread.kill() self.vnc_starter_thread = None super(ExternalVNCServerHandler, self)._NH_MediaStreamWillEnd(notification) if self.vnc_socket is not None: self.vnc_socket.close() class ScreenSharingStream(MSRPStreamBase): type = 'screen-sharing' priority = 1 media_type = 'application' accept_types = ['application/x-rfb'] accept_wrapped_types = None ServerHandler = InternalVNCServerHandler ViewerHandler = InternalVNCViewerHandler handler = WriteOnceAttribute() def __init__(self, mode): if mode not in ('viewer', 'server'): raise ValueError("mode should be 'viewer' or 'server' not '%s'" % mode) super(ScreenSharingStream, self).__init__(direction='sendrecv') self.handler = self.ViewerHandler() if mode=='viewer' else self.ServerHandler() self.incoming_queue = queue() self.outgoing_queue = queue() self.msrp_reader_thread = None self.msrp_writer_thread = None @classmethod def new_from_sdp(cls, session, remote_sdp, stream_index): remote_stream = remote_sdp.media[stream_index] if remote_stream.media != 'application': raise UnknownStreamError accept_types = remote_stream.attributes.getfirst('accept-types', None) if accept_types is None or 'application/x-rfb' not in accept_types.split(): raise UnknownStreamError expected_transport = 'TCP/TLS/MSRP' if session.account.msrp.transport=='tls' else 'TCP/MSRP' if remote_stream.transport != expected_transport: raise InvalidStreamError("expected %s transport in chat stream, got %s" % (expected_transport, remote_stream.transport)) if remote_stream.formats != ['*']: raise InvalidStreamError("wrong format list specified") remote_rfbsetup = remote_stream.attributes.getfirst('rfbsetup', 'active') if remote_rfbsetup == 'active': stream = cls(mode='server') elif remote_rfbsetup == 'passive': stream = cls(mode='viewer') else: raise InvalidStreamError("unknown rfbsetup attribute in the remote screen sharing stream") stream.remote_role = remote_stream.attributes.getfirst('setup', 'active') return stream def _create_local_media(self, uri_path): local_media = super(ScreenSharingStream, self)._create_local_media(uri_path) local_media.attributes.append(SDPAttribute('rfbsetup', self.handler.type)) return local_media def _msrp_reader(self): while True: try: chunk = self.msrp.read_chunk() if chunk.method in (None, 'REPORT'): continue elif chunk.method == 'SEND': if chunk.content_type in self.accept_types: self.incoming_queue.send(chunk.data) response = make_response(chunk, 200, 'OK') report = make_report(chunk, 200, 'OK') else: response = make_response(chunk, 415, 'Invalid Content-Type') report = None else: response = make_response(chunk, 501, 'Unknown method') report = None if response is not None: self.msrp.write_chunk(response) if report is not None: self.msrp.write_chunk(report) except Exception, e: self.msrp_reader_thread = None # avoid issues caused by the notification handler killing this greenlet during post_notification if self.shutting_down and isinstance(e, ConnectionDone): break self._failure_reason = str(e) NotificationCenter().post_notification('MediaStreamDidFail', sender=self, data=NotificationData(context='reading', reason=self._failure_reason)) break def _msrp_writer(self): while True: try: data = self.outgoing_queue.wait() chunk = self.msrp.make_send_request(data=data) chunk.add_header(SuccessReportHeader('no')) chunk.add_header(FailureReportHeader('partial')) chunk.add_header(ContentTypeHeader('application/x-rfb')) self.msrp.write_chunk(chunk) except Exception, e: self.msrp_writer_thread = None # avoid issues caused by the notification handler killing this greenlet during post_notification if self.shutting_down and isinstance(e, ConnectionDone): break self._failure_reason = str(e) NotificationCenter().post_notification('MediaStreamDidFail', sender=self, data=NotificationData(context='sending', reason=self._failure_reason)) break def _NH_MediaStreamDidInitialize(self, notification): notification.center.add_observer(self, sender=self.handler) self.handler.initialize(self) def _NH_MediaStreamDidStart(self, notification): self.msrp_reader_thread = spawn(self._msrp_reader) self.msrp_writer_thread = spawn(self._msrp_writer) def _NH_MediaStreamWillEnd(self, notification): notification.center.remove_observer(self, sender=self.handler) if self.msrp_reader_thread is not None: self.msrp_reader_thread.kill() self.msrp_reader_thread = None if self.msrp_writer_thread is not None: self.msrp_writer_thread.kill() self.msrp_writer_thread = None def _NH_ScreenSharingHandlerDidFail(self, notification): self._failure_reason = notification.data.reason notification.center.post_notification('MediaStreamDidFail', sender=self, data=notification.data) ================================================ FILE: sipsimple/streams/rtp/__init__.py ================================================ """ Handling of RTP media streams according to RFC3550, RFC3605, RFC3581, RFC2833 and RFC3711, RFC3489 and RFC5245. """ __all__ = ['RTPStream'] import weakref from abc import ABCMeta, abstractmethod from application.notification import IObserver, NotificationCenter, NotificationData, ObserverWeakrefProxy from application.python import Null from threading import RLock from zope.interface import implements from sipsimple.account import BonjourAccount from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.core import RTPTransport, SIPCoreError, SIPURI from sipsimple.lookup import DNSLookup from sipsimple.streams import IMediaStream, InvalidStreamError, MediaStreamType, UnknownStreamError from sipsimple.threading import run_in_thread class ZRTPStreamOptions(object): implements(IObserver) def __init__(self, stream): self._stream = stream self.__dict__['master'] = None self.__dict__['sas'] = None self.__dict__['verified'] = False self.__dict__['peer_name'] = '' @property def sas(self): if self.master is not None: return self.master.encryption.zrtp.sas return self.__dict__['sas'] @property def verified(self): if self.master is not None: return self.master.encryption.zrtp.verified return self.__dict__['verified'] @verified.setter def verified(self, verified): if self.__dict__['verified'] == verified: return if self.sas is None: raise AttributeError('Cannot verify peer before SAS is received') if self.master is not None: self.master.encryption.zrtp.verified = verified else: rtp_transport = self._stream._rtp_transport if rtp_transport is not None: @run_in_thread('file-io') def update_verified(rtp_transport, verified): rtp_transport.set_zrtp_sas_verified(verified) notification_center = NotificationCenter() notification_center.post_notification('RTPStreamZRTPVerifiedStateChanged', sender=self._stream, data=NotificationData(verified=verified)) self.__dict__['verified'] = verified update_verified(rtp_transport, verified) @property def peer_id(self): if self.master is not None: return self.master.encryption.zrtp.peer_id rtp_transport = self._stream._rtp_transport if rtp_transport is None: return None return rtp_transport.zrtp_peer_id @property def peer_name(self): if self.master is not None: return self.master.encryption.zrtp.peer_name return self.__dict__['peer_name'] @peer_name.setter def peer_name(self, name): if self.__dict__['peer_name'] == name: return if self.master is not None: self.master.encryption.zrtp.peer_name = name else: rtp_transport = self._stream._rtp_transport if rtp_transport is not None: @run_in_thread('file-io') def update_name(rtp_transport, name): rtp_transport.zrtp_peer_name = name notification_center = NotificationCenter() notification_center.post_notification('RTPStreamZRTPPeerNameChanged', sender=self._stream, data=NotificationData(name=name)) self.__dict__['peer_name'] = name update_name(rtp_transport, name) @property def master(self): return self.__dict__['master'] @master.setter def master(self, master): old_master = self.__dict__['master'] if old_master is master: return notification_center = NotificationCenter() if old_master is not None: notification_center.remove_observer(self, sender=old_master) if master is not None: notification_center.add_observer(self, sender=master) self.__dict__['master'] = master def _enable(self, master_stream=None): rtp_transport = self._stream._rtp_transport if rtp_transport is None: return if master_stream is not None and not (master_stream.encryption.active and master_stream.encryption.type == 'ZRTP'): raise RuntimeError('Master stream must have ZRTP encryption activated') rtp_transport.set_zrtp_enabled(True, master_stream) self.master = master_stream def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_RTPStreamZRTPReceivedSAS(self, notification): # ZRTP begins on the audio stream, so this notification will only be processed # by the other streams self.__dict__['sas'] = notification.data.sas self.__dict__['verified'] = notification.data.verified self.__dict__['peer_name'] = notification.data.peer_name notification.center.post_notification(notification.name, sender=self._stream, data=notification.data) def _NH_RTPStreamZRTPVerifiedStateChanged(self, notification): self.__dict__['verified'] = notification.data.verified notification.center.post_notification(notification.name, sender=self._stream, data=notification.data) def _NH_RTPStreamZRTPPeerNameChanged(self, notification): self.__dict__['peer_name'] = notification.data.name notification.center.post_notification(notification.name, sender=self._stream, data=notification.data) def _NH_MediaStreamDidEnd(self, notification): self.master = None class RTPStreamEncryption(object): implements(IObserver) def __init__(self, stream): self._stream_ref = weakref.ref(stream) # Keep a weak reference before the stream is initialized to avoid a memory cycle that would delay releasing audio resources self._stream = None # We will store the actual reference once it's initialized and we're guaranteed to get MediaStreamDidEnd and do the cleanup self._rtp_transport = None self.__dict__['type'] = None self.__dict__['zrtp'] = None notification_center = NotificationCenter() notification_center.add_observer(ObserverWeakrefProxy(self), name='MediaStreamDidInitialize') notification_center.add_observer(ObserverWeakrefProxy(self), name='MediaStreamDidNotInitialize') @property def active(self): rtp_transport = self._rtp_transport if rtp_transport is None: return False if self.type == 'SRTP/SDES': return rtp_transport.srtp_active elif self.type == 'ZRTP': return rtp_transport.zrtp_active return False @property def cipher(self): rtp_transport = self._rtp_transport if rtp_transport is None: return None if self.type == 'SRTP/SDES': return rtp_transport.srtp_cipher elif self.type == 'ZRTP': return rtp_transport.zrtp_cipher return None @property def type(self): return self.__dict__['type'] @property def zrtp(self): zrtp = self.__dict__['zrtp'] if zrtp is None: raise RuntimeError('ZRTP options have not been initialized') return zrtp def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_MediaStreamDidInitialize(self, notification): stream = notification.sender if stream is self._stream_ref(): self._stream = stream self._rtp_transport = stream._rtp_transport notification.center.remove_observer(ObserverWeakrefProxy(self), name='MediaStreamDidInitialize') notification.center.remove_observer(ObserverWeakrefProxy(self), name='MediaStreamDidNotInitialize') notification.center.add_observer(self, sender=self._stream) notification.center.add_observer(self, sender=self._rtp_transport) encryption = stream._srtp_encryption or '' if encryption.startswith('sdes'): self.__dict__['type'] = 'SRTP/SDES' elif encryption == 'zrtp': self.__dict__['type'] = 'ZRTP' self.__dict__['zrtp'] = ZRTPStreamOptions(stream) def _NH_MediaStreamDidNotInitialize(self, notification): if notification.sender is self._stream_ref(): notification.center.remove_observer(ObserverWeakrefProxy(self), name='MediaStreamDidInitialize') notification.center.remove_observer(ObserverWeakrefProxy(self), name='MediaStreamDidNotInitialize') self._stream_ref = None self._stream = None def _NH_MediaStreamDidStart(self, notification): if self.type == 'SRTP/SDES': stream = notification.sender if self.active: notification.center.post_notification('RTPStreamDidEnableEncryption', sender=stream) else: reason = 'Not supported by remote' notification.center.post_notification('RTPStreamDidNotEnableEncryption', sender=stream, data=NotificationData(reason=reason)) def _NH_MediaStreamDidEnd(self, notification): notification.center.remove_observer(self, sender=self._stream) notification.center.remove_observer(self, sender=self._rtp_transport) self._stream = None self._stream_ref = None self._rtp_transport = None self.__dict__['type'] = None self.__dict__['zrtp'] = None def _NH_RTPTransportZRTPSecureOn(self, notification): stream = self._stream with stream._lock: if stream.state == "ENDED": return notification.center.post_notification('RTPStreamDidEnableEncryption', sender=stream) def _NH_RTPTransportZRTPSecureOff(self, notification): # We should never get here because we don't allow disabling encryption -Saul pass def _NH_RTPTransportZRTPReceivedSAS(self, notification): stream = self._stream with stream._lock: if stream.state == "ENDED": return self.zrtp.__dict__['sas'] = sas = notification.data.sas self.zrtp.__dict__['verified'] = verified = notification.data.verified self.zrtp.__dict__['peer_name'] = peer_name = notification.sender.zrtp_peer_name notification.center.post_notification('RTPStreamZRTPReceivedSAS', sender=stream, data=NotificationData(sas=sas, verified=verified, peer_name=peer_name)) def _NH_RTPTransportZRTPLog(self, notification): stream = self._stream with stream._lock: if stream.state == "ENDED": return notification.center.post_notification('RTPStreamZRTPLog', sender=stream, data=notification.data) def _NH_RTPTransportZRTPNegotiationFailed(self, notification): stream = self._stream with stream._lock: if stream.state == "ENDED": return reason = 'Negotiation failed: %s' % notification.data.reason notification.center.post_notification('RTPStreamDidNotEnableEncryption', sender=stream, data=NotificationData(reason=reason)) def _NH_RTPTransportZRTPNotSupportedByRemote(self, notification): stream = self._stream with stream._lock: if stream.state == "ENDED": return reason = 'ZRTP not supported by remote' notification.center.post_notification('RTPStreamDidNotEnableEncryption', sender=stream, data=NotificationData(reason=reason)) class RTPStreamType(ABCMeta, MediaStreamType): pass class RTPStream(object): __metaclass__ = RTPStreamType implements(IMediaStream, IObserver) type = None priority = None hold_supported = True def __init__(self): self.notification_center = NotificationCenter() self.on_hold_by_local = False self.on_hold_by_remote = False self.direction = None self.state = "NULL" self.session = None self.encryption = RTPStreamEncryption(self) self._transport = None self._hold_request = None self._ice_state = "NULL" self._lock = RLock() self._rtp_transport = None self._try_ice = False self._srtp_encryption = None self._remote_rtp_address_sdp = None self._remote_rtp_port_sdp = None self._initialized = False self._done = False self._failure_reason = None @property def codec(self): return self._transport.codec if self._transport else None @property def sample_rate(self): return self._transport.sample_rate if self._transport else None @property def statistics(self): return self._transport.statistics if self._transport else None @property def local_rtp_address(self): return self._rtp_transport.local_rtp_address if self._rtp_transport else None @property def local_rtp_port(self): return self._rtp_transport.local_rtp_port if self._rtp_transport else None @property def local_rtp_candidate(self): return self._rtp_transport.local_rtp_candidate if self._rtp_transport else None @property def remote_rtp_address(self): if self._ice_state == "IN_USE": return self._rtp_transport.remote_rtp_address if self._rtp_transport else None return self._remote_rtp_address_sdp if self._rtp_transport else None @property def remote_rtp_port(self): if self._ice_state == "IN_USE": return self._rtp_transport.remote_rtp_port if self._rtp_transport else None return self._remote_rtp_port_sdp if self._rtp_transport else None @property def remote_rtp_candidate(self): return self._rtp_transport.remote_rtp_candidate if self._rtp_transport else None @property def ice_active(self): return self._ice_state == "IN_USE" @property def on_hold(self): return self.on_hold_by_local or self.on_hold_by_remote @abstractmethod def start(self, local_sdp, remote_sdp, stream_index): raise NotImplementedError @abstractmethod def update(self, local_sdp, remote_sdp, stream_index): raise NotImplementedError @abstractmethod def validate_update(self, remote_sdp, stream_index): raise NotImplementedError @abstractmethod def deactivate(self): raise NotImplementedError @abstractmethod def end(self): raise NotImplementedError @abstractmethod def reset(self, stream_index): raise NotImplementedError def hold(self): with self._lock: if self.on_hold_by_local or self._hold_request == 'hold': return if self.state == "ESTABLISHED" and self.direction != "inactive": self._pause() self._hold_request = 'hold' def unhold(self): with self._lock: if (not self.on_hold_by_local and self._hold_request != 'hold') or self._hold_request == 'unhold': return if self.state == "ESTABLISHED" and self._hold_request == 'hold': self._resume() self._hold_request = None if self._hold_request == 'hold' else 'unhold' @classmethod def new_from_sdp(cls, session, remote_sdp, stream_index): # TODO: actually validate the SDP settings = SIPSimpleSettings() remote_stream = remote_sdp.media[stream_index] if remote_stream.media != cls.type: raise UnknownStreamError if remote_stream.transport not in ('RTP/AVP', 'RTP/SAVP'): raise InvalidStreamError("expected RTP/AVP or RTP/SAVP transport in %s stream, got %s" % (cls.type, remote_stream.transport)) local_encryption_policy = session.account.rtp.encryption.key_negotiation if session.account.rtp.encryption.enabled else None if local_encryption_policy == "sdes_mandatory" and not "crypto" in remote_stream.attributes: raise InvalidStreamError("SRTP/SDES is locally mandatory but it's not remotely enabled") if remote_stream.transport == 'RTP/SAVP' and "crypto" in remote_stream.attributes and local_encryption_policy not in ("opportunistic", "sdes_optional", "sdes_mandatory"): raise InvalidStreamError("SRTP/SDES is remotely mandatory but it's not locally enabled") account_preferred_codecs = getattr(session.account.rtp, '%s_codec_list' % cls.type) general_codecs = getattr(settings.rtp, '%s_codec_list' % cls.type) supported_codecs = account_preferred_codecs or general_codecs if not any(codec for codec in remote_stream.codec_list if codec in supported_codecs): raise InvalidStreamError("no compatible codecs found") stream = cls() stream._incoming_remote_sdp = remote_sdp stream._incoming_stream_index = stream_index return stream def initialize(self, session, direction): with self._lock: if self.state != "NULL": raise RuntimeError("%sStream.initialize() may only be called in the NULL state" % self.type.capitalize()) self.state = "INITIALIZING" self.session = session local_encryption_policy = session.account.rtp.encryption.key_negotiation if session.account.rtp.encryption.enabled else None if hasattr(self, "_incoming_remote_sdp") and hasattr(self, '_incoming_stream_index'): # ICE attributes could come at the session level or at the media level remote_stream = self._incoming_remote_sdp.media[self._incoming_stream_index] self._try_ice = self.session.account.nat_traversal.use_ice and ((remote_stream.has_ice_attributes or self._incoming_remote_sdp.has_ice_attributes) and remote_stream.has_ice_candidates) if "zrtp-hash" in remote_stream.attributes: incoming_stream_encryption = 'zrtp' elif "crypto" in remote_stream.attributes: incoming_stream_encryption = 'sdes_mandatory' if remote_stream.transport == 'RTP/SAVP' else 'sdes_optional' else: incoming_stream_encryption = None if incoming_stream_encryption is not None and local_encryption_policy == 'opportunistic': self._srtp_encryption = incoming_stream_encryption else: self._srtp_encryption = 'zrtp' if local_encryption_policy == 'opportunistic' else local_encryption_policy else: self._try_ice = self.session.account.nat_traversal.use_ice self._srtp_encryption = 'zrtp' if local_encryption_policy == 'opportunistic' else local_encryption_policy if self._try_ice: if self.session.account.nat_traversal.stun_server_list: stun_servers = list((server.host, server.port) for server in self.session.account.nat_traversal.stun_server_list) self._init_rtp_transport(stun_servers) elif not isinstance(self.session.account, BonjourAccount): dns_lookup = DNSLookup() self.notification_center.add_observer(self, sender=dns_lookup) dns_lookup.lookup_service(SIPURI(self.session.account.id.domain), "stun") else: self._init_rtp_transport() def get_local_media(self, remote_sdp=None, index=0): with self._lock: if self.state not in ("INITIALIZED", "WAIT_ICE", "ESTABLISHED"): raise RuntimeError("%sStream.get_local_media() may only be called in the INITIALIZED, WAIT_ICE or ESTABLISHED states" % self.type.capitalize()) if remote_sdp is None: # offer old_direction = self._transport.direction if old_direction is None: new_direction = "sendrecv" elif "send" in old_direction: new_direction = ("sendonly" if (self._hold_request == 'hold' or (self._hold_request is None and self.on_hold_by_local)) else "sendrecv") else: new_direction = ("inactive" if (self._hold_request == 'hold' or (self._hold_request is None and self.on_hold_by_local)) else "recvonly") else: new_direction = None return self._transport.get_local_media(remote_sdp, index, new_direction) # Notifications def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def _NH_DNSLookupDidFail(self, notification): self.notification_center.remove_observer(self, sender=notification.sender) with self._lock: if self.state == "ENDED": return self._init_rtp_transport() def _NH_DNSLookupDidSucceed(self, notification): self.notification_center.remove_observer(self, sender=notification.sender) with self._lock: if self.state == "ENDED": return self._init_rtp_transport(notification.data.result) def _NH_RTPTransportDidInitialize(self, notification): rtp_transport = notification.sender with self._lock: if self.state == "ENDED": self.notification_center.remove_observer(self, sender=rtp_transport) return del self._rtp_args del self._stun_servers remote_sdp = self.__dict__.pop('_incoming_remote_sdp', None) stream_index = self.__dict__.pop('_incoming_stream_index', None) try: if remote_sdp is not None: transport = self._create_transport(rtp_transport, remote_sdp=remote_sdp, stream_index=stream_index) self._save_remote_sdp_rtp_info(remote_sdp, stream_index) else: transport = self._create_transport(rtp_transport) except SIPCoreError, e: self.state = "ENDED" self.notification_center.remove_observer(self, sender=rtp_transport) self.notification_center.post_notification('MediaStreamDidNotInitialize', sender=self, data=NotificationData(reason=e.args[0])) return self._rtp_transport = rtp_transport self._transport = transport self.notification_center.add_observer(self, sender=transport) self._initialized = True self.state = "INITIALIZED" self.notification_center.post_notification('MediaStreamDidInitialize', sender=self) def _NH_RTPTransportDidFail(self, notification): self.notification_center.remove_observer(self, sender=notification.sender) with self._lock: if self.state == "ENDED": return self._try_next_rtp_transport(notification.data.reason) def _NH_RTPTransportICENegotiationStateDidChange(self, notification): with self._lock: if self._ice_state != "NULL" or self.state not in ("INITIALIZING", "INITIALIZED", "WAIT_ICE"): return self.notification_center.post_notification('RTPStreamICENegotiationStateDidChange', sender=self, data=notification.data) def _NH_RTPTransportICENegotiationDidSucceed(self, notification): with self._lock: if self.state != "WAIT_ICE": return self._ice_state = "IN_USE" self.state = 'ESTABLISHED' self.notification_center.post_notification('RTPStreamICENegotiationDidSucceed', sender=self, data=notification.data) self.notification_center.post_notification('MediaStreamDidStart', sender=self) def _NH_RTPTransportICENegotiationDidFail(self, notification): with self._lock: if self.state != "WAIT_ICE": return self._ice_state = "FAILED" self.state = 'ESTABLISHED' self.notification_center.post_notification('RTPStreamICENegotiationDidFail', sender=self, data=notification.data) self.notification_center.post_notification('MediaStreamDidStart', sender=self) # Private methods def _init_rtp_transport(self, stun_servers=None): self._rtp_args = dict() self._rtp_args["encryption"] = self._srtp_encryption self._rtp_args["use_ice"] = self._try_ice self._stun_servers = [(None, None)] if stun_servers: self._stun_servers.extend(reversed(stun_servers)) self._try_next_rtp_transport() def _try_next_rtp_transport(self, failure_reason=None): if self._stun_servers: stun_address, stun_port = self._stun_servers.pop() try: rtp_transport = RTPTransport(ice_stun_address=stun_address, ice_stun_port=stun_port, **self._rtp_args) except SIPCoreError, e: self._try_next_rtp_transport(e.args[0]) else: self.notification_center.add_observer(self, sender=rtp_transport) try: rtp_transport.set_INIT() except SIPCoreError, e: self.notification_center.remove_observer(self, sender=rtp_transport) self._try_next_rtp_transport(e.args[0]) else: self.state = "ENDED" self.notification_center.post_notification('MediaStreamDidNotInitialize', sender=self, data=NotificationData(reason=failure_reason)) def _save_remote_sdp_rtp_info(self, remote_sdp, index): connection = remote_sdp.media[index].connection or remote_sdp.connection self._remote_rtp_address_sdp = connection.address self._remote_rtp_port_sdp = remote_sdp.media[index].port @abstractmethod def _create_transport(self, rtp_transport, remote_sdp=None, stream_index=None): raise NotImplementedError @abstractmethod def _check_hold(self, direction, is_initial): raise NotImplementedError @abstractmethod def _pause(self): raise NotImplementedError @abstractmethod def _resume(self): raise NotImplementedError from sipsimple.streams.rtp import audio, video ================================================ FILE: sipsimple/streams/rtp/audio.py ================================================ __all__ = ['AudioStream'] from application.notification import NotificationCenter, NotificationData from zope.interface import implements from sipsimple.audio import AudioBridge, AudioDevice, IAudioPort, WaveRecorder from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.core import AudioTransport, PJSIPError, SIPCoreError from sipsimple.streams.rtp import RTPStream class AudioStream(RTPStream): implements(IAudioPort) type = 'audio' priority = 1 def __init__(self): super(AudioStream, self).__init__() from sipsimple.application import SIPApplication self.mixer = SIPApplication.voice_audio_mixer self.bridge = AudioBridge(self.mixer) self.device = AudioDevice(self.mixer) self._audio_rec = None self.bridge.add(self.device) @property def muted(self): return self.__dict__.get('muted', False) @muted.setter def muted(self, value): if not isinstance(value, bool): raise ValueError("illegal value for muted property: %r" % (value,)) if value == self.muted: return old_producer_slot = self.producer_slot self.__dict__['muted'] = value notification_center = NotificationCenter() data = NotificationData(consumer_slot_changed=False, producer_slot_changed=True, old_producer_slot=old_producer_slot, new_producer_slot=self.producer_slot) notification_center.post_notification('AudioPortDidChangeSlots', sender=self, data=data) @property def consumer_slot(self): return self._transport.slot if self._transport else None @property def producer_slot(self): return self._transport.slot if self._transport and not self.muted else None @property def recorder(self): return self._audio_rec def start(self, local_sdp, remote_sdp, stream_index): with self._lock: if self.state != "INITIALIZED": raise RuntimeError("AudioStream.start() may only be called in the INITIALIZED state") settings = SIPSimpleSettings() self._transport.start(local_sdp, remote_sdp, stream_index, timeout=settings.rtp.timeout) self._save_remote_sdp_rtp_info(remote_sdp, stream_index) self._check_hold(self._transport.direction, True) if self._try_ice and self._ice_state == "NULL": self.state = 'WAIT_ICE' else: self.state = 'ESTABLISHED' self.notification_center.post_notification('MediaStreamDidStart', sender=self) def validate_update(self, remote_sdp, stream_index): with self._lock: # TODO: implement return True def update(self, local_sdp, remote_sdp, stream_index): with self._lock: connection = remote_sdp.media[stream_index].connection or remote_sdp.connection if not self._rtp_transport.ice_active and (connection.address != self._remote_rtp_address_sdp or self._remote_rtp_port_sdp != remote_sdp.media[stream_index].port): settings = SIPSimpleSettings() if self._audio_rec is not None: self.bridge.remove(self._audio_rec) old_consumer_slot = self.consumer_slot old_producer_slot = self.producer_slot self.notification_center.remove_observer(self, sender=self._transport) self._transport.stop() try: self._transport = AudioTransport(self.mixer, self._rtp_transport, remote_sdp, stream_index, codecs=list(self.session.account.rtp.audio_codec_list or settings.rtp.audio_codec_list)) except SIPCoreError, e: self.state = "ENDED" self._failure_reason = e.args[0] self.notification_center.post_notification('MediaStreamDidFail', sender=self, data=NotificationData(context='update', reason=self._failure_reason)) return self.notification_center.add_observer(self, sender=self._transport) self._transport.start(local_sdp, remote_sdp, stream_index, timeout=settings.rtp.timeout) self.notification_center.post_notification('AudioPortDidChangeSlots', sender=self, data=NotificationData(consumer_slot_changed=True, producer_slot_changed=True, old_consumer_slot=old_consumer_slot, new_consumer_slot=self.consumer_slot, old_producer_slot=old_producer_slot, new_producer_slot=self.producer_slot)) if connection.address == '0.0.0.0' and remote_sdp.media[stream_index].direction == 'sendrecv': self._transport.update_direction('recvonly') self._check_hold(self._transport.direction, False) self.notification_center.post_notification('RTPStreamDidChangeRTPParameters', sender=self) else: new_direction = local_sdp.media[stream_index].direction self._transport.update_direction(new_direction) self._check_hold(new_direction, False) self._save_remote_sdp_rtp_info(remote_sdp, stream_index) self._transport.update_sdp(local_sdp, remote_sdp, stream_index) self._hold_request = None def deactivate(self): with self._lock: self.bridge.stop() def end(self): with self._lock: if self.state == "ENDED" or self._done: return self._done = True if not self._initialized: self.state = "ENDED" self.notification_center.post_notification('MediaStreamDidNotInitialize', sender=self, data=NotificationData(reason='Interrupted')) return self.notification_center.post_notification('MediaStreamWillEnd', sender=self) if self._transport is not None: if self._audio_rec is not None: self._stop_recording() self.notification_center.remove_observer(self, sender=self._transport) self.notification_center.remove_observer(self, sender=self._rtp_transport) self._transport.stop() self._transport = None self._rtp_transport = None self.state = "ENDED" self.notification_center.post_notification('MediaStreamDidEnd', sender=self, data=NotificationData(error=self._failure_reason)) self.session = None def reset(self, stream_index): with self._lock: if self.direction == "inactive" and not self.on_hold_by_local: new_direction = "sendrecv" self._transport.update_direction(new_direction) self._check_hold(new_direction, False) # TODO: do a full reset, re-creating the AudioTransport, so that a new offer # would contain all codecs and ICE would be renegotiated -Saul def send_dtmf(self, digit): with self._lock: if self.state != "ESTABLISHED": raise RuntimeError("AudioStream.send_dtmf() cannot be used in %s state" % self.state) try: self._transport.send_dtmf(digit) except PJSIPError, e: if not e.args[0].endswith("(PJ_ETOOMANY)"): raise def start_recording(self, filename): with self._lock: if self.state == "ENDED": raise RuntimeError("AudioStream.start_recording() may not be called in the ENDED state") if self._audio_rec is not None: raise RuntimeError("Already recording audio to a file") self._audio_rec = WaveRecorder(self.mixer, filename) if self.state == "ESTABLISHED": self._check_recording() def stop_recording(self): with self._lock: if self._audio_rec is None: raise RuntimeError("Not recording any audio") self._stop_recording() def _NH_RTPAudioStreamGotDTMF(self, notification): notification.center.post_notification('AudioStreamGotDTMF', sender=self, data=NotificationData(digit=notification.data.digit)) def _NH_RTPAudioTransportDidTimeout(self, notification): notification.center.post_notification('RTPStreamDidTimeout', sender=self) # Private methods # def _create_transport(self, rtp_transport, remote_sdp=None, stream_index=None): settings = SIPSimpleSettings() codecs = list(self.session.account.rtp.audio_codec_list or settings.rtp.audio_codec_list) return AudioTransport(self.mixer, rtp_transport, remote_sdp=remote_sdp, sdp_index=stream_index or 0, codecs=codecs) def _check_hold(self, direction, is_initial): was_on_hold_by_local = self.on_hold_by_local was_on_hold_by_remote = self.on_hold_by_remote was_inactive = self.direction == "inactive" self.direction = direction inactive = self.direction == "inactive" self.on_hold_by_local = was_on_hold_by_local if inactive else direction == "sendonly" self.on_hold_by_remote = "send" not in direction if (is_initial or was_on_hold_by_local or was_inactive) and not inactive and not self.on_hold_by_local and self._hold_request != 'hold': self._resume() if not was_on_hold_by_local and self.on_hold_by_local: self.notification_center.post_notification('RTPStreamDidChangeHoldState', sender=self, data=NotificationData(originator="local", on_hold=True)) if was_on_hold_by_local and not self.on_hold_by_local: self.notification_center.post_notification('RTPStreamDidChangeHoldState', sender=self, data=NotificationData(originator="local", on_hold=False)) if not was_on_hold_by_remote and self.on_hold_by_remote: self.notification_center.post_notification('RTPStreamDidChangeHoldState', sender=self, data=NotificationData(originator="remote", on_hold=True)) if was_on_hold_by_remote and not self.on_hold_by_remote: self.notification_center.post_notification('RTPStreamDidChangeHoldState', sender=self, data=NotificationData(originator="remote", on_hold=False)) if self._audio_rec is not None: self._check_recording() def _check_recording(self): if not self._audio_rec.is_active: self.notification_center.post_notification('AudioStreamWillStartRecording', sender=self, data=NotificationData(filename=self._audio_rec.filename)) try: self._audio_rec.start() except SIPCoreError, e: self._audio_rec = None self.notification_center.post_notification('AudioStreamDidStopRecording', sender=self, data=NotificationData(filename=self._audio_rec.filename, reason=e.args[0])) return self.notification_center.post_notification('AudioStreamDidStartRecording', sender=self, data=NotificationData(filename=self._audio_rec.filename)) if not self.on_hold: self.bridge.add(self._audio_rec) elif self._audio_rec in self.bridge: self.bridge.remove(self._audio_rec) def _stop_recording(self): self.notification_center.post_notification('AudioStreamWillStopRecording', sender=self, data=NotificationData(filename=self._audio_rec.filename)) try: if self._audio_rec.is_active: self._audio_rec.stop() finally: self.notification_center.post_notification('AudioStreamDidStopRecording', sender=self, data=NotificationData(filename=self._audio_rec.filename)) self._audio_rec = None def _pause(self): self.bridge.remove(self) def _resume(self): self.bridge.add(self) ================================================ FILE: sipsimple/streams/rtp/video.py ================================================ __all__ = ['VideoStream'] from application.notification import NotificationData from zope.interface import implements from sipsimple.configuration.settings import SIPSimpleSettings from sipsimple.core import VideoTransport from sipsimple.streams import InvalidStreamError from sipsimple.streams.rtp import RTPStream from sipsimple.threading import call_in_thread, run_in_twisted_thread from sipsimple.util import ExponentialTimer from sipsimple.video import IVideoProducer class VideoStream(RTPStream): implements(IVideoProducer) type = 'video' priority = 1 def __init__(self): super(VideoStream, self).__init__() from sipsimple.application import SIPApplication self.device = SIPApplication.video_device self._keyframe_timer = None @property def producer(self): return self._transport.remote_video if self._transport else None @classmethod def new_from_sdp(cls, session, remote_sdp, stream_index): stream = super(VideoStream, cls).new_from_sdp(session, remote_sdp, stream_index) if stream.device.producer is None: raise InvalidStreamError("no video support available") if not stream.validate_update(remote_sdp, stream_index): raise InvalidStreamError("no valid SDP") return stream def initialize(self, session, direction): super(VideoStream, self).initialize(session, direction) self.notification_center.add_observer(self, name='VideoDeviceDidChangeCamera') def start(self, local_sdp, remote_sdp, stream_index): with self._lock: if self.state != "INITIALIZED": raise RuntimeError("VideoStream.start() may only be called in the INITIALIZED state") settings = SIPSimpleSettings() self._transport.start(local_sdp, remote_sdp, stream_index, timeout=settings.rtp.timeout) self._transport.local_video.producer = self.device.producer self._save_remote_sdp_rtp_info(remote_sdp, stream_index) self._check_hold(self._transport.direction, True) if self._try_ice and self._ice_state == "NULL": self.state = 'WAIT_ICE' else: self._send_keyframes() self.state = 'ESTABLISHED' self.notification_center.post_notification('MediaStreamDidStart', sender=self) def validate_update(self, remote_sdp, stream_index): with self._lock: remote_media = remote_sdp.media[stream_index] if 'H264' in remote_media.codec_list: rtpmap = next(attr for attr in remote_media.attributes if attr.name=='rtpmap' and 'h264' in attr.value.lower()) payload_type = rtpmap.value.partition(' ')[0] has_profile_level_id = any('profile-level-id' in attr.value.lower() for attr in remote_media.attributes if attr.name=='fmtp' and attr.value.startswith(payload_type + ' ')) if not has_profile_level_id: return False return True def update(self, local_sdp, remote_sdp, stream_index): with self._lock: new_direction = local_sdp.media[stream_index].direction self._check_hold(new_direction, False) self._transport.update_direction(new_direction) self._save_remote_sdp_rtp_info(remote_sdp, stream_index) self._transport.update_sdp(local_sdp, remote_sdp, stream_index) self._hold_request = None def deactivate(self): with self._lock: self.notification_center.discard_observer(self, name='VideoDeviceDidChangeCamera') def end(self): with self._lock: if self.state == "ENDED" or self._done: return self._done = True if not self._initialized: self.state = "ENDED" self.notification_center.post_notification('MediaStreamDidNotInitialize', sender=self, data=NotificationData(reason='Interrupted')) return if self._keyframe_timer is not None: self._keyframe_timer.stop() self.notification_center.remove_observer(self, sender=self._keyframe_timer) self._keyframe_timer = None self.notification_center.post_notification('MediaStreamWillEnd', sender=self) if self._transport is not None: self.notification_center.remove_observer(self, sender=self._transport) self.notification_center.remove_observer(self, sender=self._rtp_transport) call_in_thread('device-io', self._transport.stop) self._transport = None self._rtp_transport = None self.state = "ENDED" self.notification_center.post_notification('MediaStreamDidEnd', sender=self, data=NotificationData(error=self._failure_reason)) self.session = None def reset(self, stream_index): pass def _NH_RTPTransportICENegotiationDidSucceed(self, notification): with self._lock: if self.state == "WAIT_ICE": self._send_keyframes() super(VideoStream, self)._NH_RTPTransportICENegotiationDidSucceed(notification) def _NH_RTPTransportICENegotiationDidFail(self, notification): with self._lock: if self.state == "WAIT_ICE": self._send_keyframes() super(VideoStream, self)._NH_RTPTransportICENegotiationDidFail(notification) def _NH_RTPVideoTransportDidTimeout(self, notification): self.notification_center.post_notification('RTPStreamDidTimeout', sender=self) def _NH_RTPVideoTransportRemoteFormatDidChange(self, notification): self.notification_center.post_notification('VideoStreamRemoteFormatDidChange', sender=self, data=notification.data) def _NH_RTPVideoTransportReceivedKeyFrame(self, notification): self.notification_center.post_notification('VideoStreamReceivedKeyFrame', sender=self, data=notification.data) def _NH_RTPVideoTransportMissedKeyFrame(self, notification): self._transport.request_keyframe() self.notification_center.post_notification('VideoStreamMissedKeyFrame', sender=self, data=notification.data) def _NH_RTPVideoTransportRequestedKeyFrame(self, notification): self._transport.send_keyframe() self.notification_center.post_notification('VideoStreamRequestedKeyFrame', sender=self, data=notification.data) def _NH_VideoDeviceDidChangeCamera(self, notification): new_camera = notification.data.new_camera if self._transport is not None and self._transport.local_video is not None: self._transport.local_video.producer = new_camera def _NH_ExponentialTimerDidTimeout(self, notification): if self._transport is not None: self._transport.send_keyframe() def _create_transport(self, rtp_transport, remote_sdp=None, stream_index=None): settings = SIPSimpleSettings() codecs = list(self.session.account.rtp.video_codec_list or settings.rtp.video_codec_list) return VideoTransport(rtp_transport, remote_sdp=remote_sdp, sdp_index=stream_index or 0, codecs=codecs) def _check_hold(self, direction, is_initial): was_on_hold_by_local = self.on_hold_by_local was_on_hold_by_remote = self.on_hold_by_remote self.direction = direction inactive = self.direction == "inactive" self.on_hold_by_local = was_on_hold_by_local if inactive else direction == "sendonly" self.on_hold_by_remote = "send" not in direction if self.on_hold_by_local or self.on_hold_by_remote: self._pause() elif not self.on_hold_by_local and not self.on_hold_by_remote and (was_on_hold_by_local or was_on_hold_by_remote): self._resume() if not was_on_hold_by_local and self.on_hold_by_local: self.notification_center.post_notification('RTPStreamDidChangeHoldState', sender=self, data=NotificationData(originator="local", on_hold=True)) if was_on_hold_by_local and not self.on_hold_by_local: self.notification_center.post_notification('RTPStreamDidChangeHoldState', sender=self, data=NotificationData(originator="local", on_hold=False)) if not was_on_hold_by_remote and self.on_hold_by_remote: self.notification_center.post_notification('RTPStreamDidChangeHoldState', sender=self, data=NotificationData(originator="remote", on_hold=True)) if was_on_hold_by_remote and not self.on_hold_by_remote: self.notification_center.post_notification('RTPStreamDidChangeHoldState', sender=self, data=NotificationData(originator="remote", on_hold=False)) @run_in_twisted_thread def _send_keyframes(self): if self._keyframe_timer is None: self._keyframe_timer = ExponentialTimer() self.notification_center.add_observer(self, sender=self._keyframe_timer) self._keyframe_timer.start(0.5, immediate=True, iterations=5) def _pause(self): self._transport.pause() def _resume(self): self._transport.resume() self._send_keyframes() self._transport.request_keyframe() ================================================ FILE: sipsimple/threading/__init__.py ================================================ """Thread management""" __all__ = ["ThreadManager", "run_in_thread", "call_in_thread", "run_in_twisted_thread", "call_in_twisted_thread"] from application.python import Null from application.python.decorator import decorator, preserve_signature from application.python.queue import EventQueue from application.python.types import Singleton from threading import Lock, current_thread from twisted.python import threadable from sipsimple import log class CallFunctionEvent(object): __slots__ = ('function', 'args', 'kw') def __init__(self, function, args, kw): self.function = function self.args = args self.kw = kw class ThreadManager(object): __metaclass__ = Singleton def __init__(self): self.threads = {} self.lock = Lock() def _event_handler(self, event): handler = getattr(self, '_EH_%s' % event.__class__.__name__, Null) handler(event) def _EH_CallFunctionEvent(self, event): try: event.function(*event.args, **event.kw) except: log.exception('Exception occurred while calling %r in the %r thread' % (event.function, current_thread().name)) def start(self): pass def stop(self): with self.lock: threads = self.threads.values() self.threads = {} for thread in threads: thread.stop() for thread in threads: thread.join() def get_thread(self, thread_id): with self.lock: try: thread = self.threads[thread_id] except KeyError: self.threads[thread_id] = thread = EventQueue(handler=self._event_handler, name=thread_id) thread.start() return thread def stop_thread(self, thread_id): if thread_id == 'thread-ops': raise RuntimeError("Won't stop internal 'thread-ops' thread") thread = self.threads.pop(thread_id) thread.stop() call_in_thread('thread-ops', thread.join) @decorator def run_in_thread(thread_id, scheduled=False): def thread_decorator(function): @preserve_signature(function) def wrapper(*args, **kw): thread_manager = ThreadManager() thread = thread_manager.get_thread(thread_id) if thread is current_thread() and not scheduled: function(*args, **kw) else: thread.put(CallFunctionEvent(function, args, kw)) return wrapper return thread_decorator def call_in_thread(thread_id, function, *args, **kw): thread_manager = ThreadManager() thread = thread_manager.get_thread(thread_id) if thread is current_thread(): function(*args, **kw) else: thread.put(CallFunctionEvent(function, args, kw)) def schedule_in_thread(thread_id, function, *args, **kw): thread_manager = ThreadManager() thread = thread_manager.get_thread(thread_id) thread.put(CallFunctionEvent(function, args, kw)) @decorator def run_in_twisted_thread(func): @preserve_signature(func) def wrapper(*args, **kwargs): if threadable.isInIOThread(): func(*args, **kwargs) else: from twisted.internet import reactor reactor.callFromThread(func, *args, **kwargs) return wrapper def call_in_twisted_thread(func, *args, **kwargs): if threadable.isInIOThread(): func(*args, **kwargs) else: from twisted.internet import reactor reactor.callFromThread(func, *args, **kwargs) ================================================ FILE: sipsimple/threading/green.py ================================================ """Green thread utilities""" __all__ = ["Command", "InterruptCommand", "run_in_green_thread", "run_in_waitable_green_thread", "call_in_green_thread", "Worker"] import sys from application.python.decorator import decorator, preserve_signature from datetime import datetime from eventlib import coros from eventlib.twistedutil import callInGreenThread from twisted.python import threadable class modulelocal(object): __locals__ = {} def __get__(self, obj, objtype): frame = sys._getframe().f_back while frame.f_globals['__name__'] == __name__: frame = frame.f_back module_name = frame.f_globals['__name__'] return self.__locals__.setdefault(module_name, {}) def __set__(self, obj, value): raise AttributeError('cannot set attribute') def __delete__(self, obj): raise AttributeError('cannot delete attribute') class Command(object): __defaults__ = modulelocal() def __init__(self, name, event=None, timestamp=None, **kw): self.name = name self.event = event or coros.event() self.timestamp = timestamp or datetime.utcnow() self.__dict__.update(self.__defaults__.get(name, {})) self.__dict__.update(kw) @classmethod def register_defaults(cls, name, **kw): cls.__defaults__[name] = kw def signal(self, result=None): if isinstance(result, BaseException): self.event.send_exception(result) else: self.event.send(result) def wait(self): return self.event.wait() class InterruptCommand(Exception): pass @decorator def run_in_green_thread(func): @preserve_signature(func) def wrapper(*args, **kw): if threadable.isInIOThread(): callInGreenThread(func, *args, **kw) else: from twisted.internet import reactor reactor.callFromThread(callInGreenThread, func, *args, **kw) return wrapper def call_in_green_thread(func, *args, **kw): if threadable.isInIOThread(): callInGreenThread(func, *args, **kw) else: from twisted.internet import reactor reactor.callFromThread(callInGreenThread, func, *args, **kw) @decorator def run_in_waitable_green_thread(func): @preserve_signature(func) def wrapper(*args, **kw): event = coros.event() def wrapped_func(): try: result = func(*args, **kw) except: event.send_exception(*sys.exc_info()) else: event.send(result) if threadable.isInIOThread(): callInGreenThread(wrapped_func) else: from twisted.internet import reactor reactor.callFromThread(callInGreenThread, wrapped_func) return event return wrapper class Worker(object): def __init__(self, func, *args, **kw): self.func = func self.args = args self.kw = kw self.event = coros.event() self._started = False def __run__(self): try: result = self.func(*self.args, **self.kw) except: self.event.send_exception(*sys.exc_info()) else: self.event.send(result) def start(self): if self._started: raise RuntimeError("worker has already been started") if not threadable.isInIOThread(): raise RuntimeError("worker can only be started in the IO thread") self._started = True callInGreenThread(self.__run__) def wait(self): if not self._started: raise RuntimeError("worker has not been started") return self.event.wait() def wait_ex(self): if not self._started: raise RuntimeError("worker has not been started") try: return self.event.wait() except Exception as e: return e @classmethod def spawn(cls, func, *args, **kw): worker = cls(func, *args, **kw) worker.start() return worker ================================================ FILE: sipsimple/util/__init__.py ================================================ """Implements utilities commonly used in various parts of the library""" from __future__ import absolute_import __all__ = ["All", "Any", "ExponentialTimer", "ISOTimestamp", "MultilingualText", "user_info", "sha1"] import os import platform import sys import dateutil.parser from application.notification import NotificationCenter from application.python.types import Singleton, MarkerType from datetime import datetime from dateutil.tz import tzlocal, tzutc from twisted.internet import reactor from sipsimple.util._sha1 import sha1 # Utility classes # class All(object): __metaclass__ = MarkerType class Any(object): __metaclass__ = MarkerType class ISOTimestamp(datetime): def __new__(cls, *args, **kw): if len(args) == 1: value = args[0] if isinstance(value, cls): return value elif isinstance(value, basestring): value = dateutil.parser.parse(value) return cls(value.year, value.month, value.day, value.hour, value.minute, value.second, value.microsecond, value.tzinfo) elif isinstance(value, datetime): return cls(value.year, value.month, value.day, value.hour, value.minute, value.second, value.microsecond, value.tzinfo or tzlocal()) else: return datetime.__new__(cls, *args, **kw) else: if len(args) < 8 and 'tzinfo' not in kw: kw['tzinfo'] = tzlocal() return datetime.__new__(cls, *args, **kw) def __str__(self): return self.isoformat() @classmethod def now(cls): return cls(datetime.now(tzlocal())) @classmethod def utcnow(cls): return cls(datetime.now(tzutc())) class MultilingualText(unicode): def __new__(cls, *args, **translations): if len(args) > 1: raise TypeError("%s.__new__ takes at most 1 positional argument (%d given)" % (cls.__name__, len(args))) default = args[0] if args else translations.get('en', u'') obj = unicode.__new__(cls, default) obj.translations = translations return obj def get_translation(self, language): return self.translations.get(language, self) class ExponentialTimer(object): def __init__(self): self._timer = None self._limit_timer = None self._interval = 0 self._iterations = None def _step(self): if self._iterations is not None: self._iterations -= 1 if self._iterations == 0: self.stop() else: self._interval *= 2 self._timer = reactor.callLater(self._interval, self._step) NotificationCenter().post_notification('ExponentialTimerDidTimeout', sender=self) @property def active(self): return self._timer is not None def start(self, base_interval, immediate=False, iterations=None, time_limit=None): assert base_interval > 0 assert iterations is None or iterations > 0 assert time_limit is None or time_limit > 0 if self._timer is not None: self.stop() self._interval = base_interval / 2.0 if immediate else base_interval self._iterations = iterations if time_limit is not None: self._limit_timer = reactor.callLater(time_limit, self.stop) self._timer = reactor.callLater(0 if immediate else base_interval, self._step) def stop(self): if self._timer is not None and self._timer.active(): self._timer.cancel() if self._limit_timer is not None and self._limit_timer.active(): self._limit_timer.cancel() self._timer = None self._limit_timer = None # Utility objects # class UserInfo(object): __metaclass__ = Singleton def __repr__(self): return ''.format(self) @property def username(self): if platform.system() == 'Windows': name = os.getenv('USERNAME') else: import pwd name = pwd.getpwuid(os.getuid()).pw_name return name.decode(sys.getfilesystemencoding()) @property def fullname(self): if platform.system() == 'Windows': name = os.getenv('USERNAME') else: import pwd name = pwd.getpwuid(os.getuid()).pw_gecos.split(',', 1)[0] or pwd.getpwuid(os.getuid()).pw_name return name.decode(sys.getfilesystemencoding()) user_info = UserInfo() del UserInfo ================================================ FILE: sipsimple/util/_sha1.h ================================================ #ifndef __SHA1_H #define __SHA1_H #include #include #ifdef __cplusplus extern "C" { #endif #define SHA1_BLOCK_SIZE 64 #define SHA1_DIGEST_SIZE 20 typedef struct { uint32_t state[SHA1_DIGEST_SIZE/4]; // state variables uint64_t count; // 64-bit block count uint8_t block[SHA1_BLOCK_SIZE]; // data block buffer uint32_t index; // index into buffer } sha1_context; void sha1_init(sha1_context *context); void sha1_update(sha1_context *context, const uint8_t *data, const size_t len); void sha1_digest(sha1_context *context, uint8_t *digest); #ifdef __cplusplus } #endif /* Reads a 32-bit integer, in network, big-endian, byte order */ #define READ_UINT32(p) \ ( (((uint32_t) (p)[0]) << 24) \ | (((uint32_t) (p)[1]) << 16) \ | (((uint32_t) (p)[2]) << 8) \ | ((uint32_t) (p)[3])) #define ROTL32(n,x) (((x)<<(n)) | ((x)>>((-(n)&31)))) /* A block, treated as a sequence of 32-bit words. */ #define SHA1_DATA_LENGTH 16 /* The SHA f()-functions. The f1 and f3 functions can be optimized to save one boolean operation each - thanks to Rich Schroeppel, rcs@cs.arizona.edu for discovering this */ /* FIXME: Can save a temporary in f3 by using ( (x & y) + (z & (x ^ y)) ), and then, in the round, compute one of the terms and add it into the destination word before computing the second term. Credits to George Spelvin for pointing this out. Unfortunately, gcc doesn't seem to be smart enough to take advantage of this. */ /* #define f1(x,y,z) ( ( x & y ) | ( ~x & z ) ) Rounds 0-19 */ #define f1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) /* Rounds 0-19 */ #define f2(x,y,z) ( x ^ y ^ z ) /* Rounds 20-39 */ /* #define f3(x,y,z) ( ( x & y ) | ( x & z ) | ( y & z ) ) Rounds 40-59 */ #define f3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) /* Rounds 40-59 */ #define f4 f2 /* The SHA Mysterious Constants */ #define K1 0x5A827999L /* Rounds 0-19 */ #define K2 0x6ED9EBA1L /* Rounds 20-39 */ #define K3 0x8F1BBCDCL /* Rounds 40-59 */ #define K4 0xCA62C1D6L /* Rounds 60-79 */ /* The initial expanding function. The hash function is defined over an 80-word expanded input array W, where the first 16 are copies of the input data, and the remaining 64 are defined by W[ i ] = W[ i - 16 ] ^ W[ i - 14 ] ^ W[ i - 8 ] ^ W[ i - 3 ] This implementation generates these values on the fly in a circular buffer - thanks to Colin Plumb, colin@nyx10.cs.du.edu for this optimization. The updated SHA changes the expanding function by adding a rotate of 1 bit. Thanks to Jim Gillogly, jim@rand.org, and an anonymous contributor for this information */ #define expand(W,i) ( W[ i & 15 ] = \ ROTL32( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \ W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) ) /* The prototype SHA sub-round. The fundamental sub-round is: a' = e + ROTL32( 5, a ) + f( b, c, d ) + k + data; b' = a; c' = ROTL32( 30, b ); d' = c; e' = d; but this is implemented by unrolling the loop 5 times and renaming the variables ( e, a, b, c, d ) = ( a', b', c', d', e' ) each iteration. This code is then replicated 20 times for each of the 4 functions, using the next 20 values from the W[] array each time */ #define subRound(a, b, c, d, e, f, k, data) \ ( e += ROTL32( 5, a ) + f( b, c, d ) + k + data, b = ROTL32( 30, b ) ) /* Perform the SHA transformation. Note that this code, like MD5, seems to break some optimizing compilers due to the complexity of the expressions and the size of the basic block. It may be necessary to split it into sections, e.g. based on the four subrounds. */ static void sha1_compress(uint32_t *state, const uint8_t *input) { uint32_t data[SHA1_DATA_LENGTH]; uint32_t A, B, C, D, E; /* Local vars */ int i; for (i = 0; i < SHA1_DATA_LENGTH; i++, input+= 4) { data[i] = READ_UINT32(input); } /* Set up first buffer and local data buffer */ A = state[0]; B = state[1]; C = state[2]; D = state[3]; E = state[4]; /* Heavy mangling, in 4 sub-rounds of 20 interations each. */ subRound(A, B, C, D, E, f1, K1, data[ 0]); subRound(E, A, B, C, D, f1, K1, data[ 1]); subRound(D, E, A, B, C, f1, K1, data[ 2]); subRound(C, D, E, A, B, f1, K1, data[ 3]); subRound(B, C, D, E, A, f1, K1, data[ 4]); subRound(A, B, C, D, E, f1, K1, data[ 5]); subRound(E, A, B, C, D, f1, K1, data[ 6]); subRound(D, E, A, B, C, f1, K1, data[ 7]); subRound(C, D, E, A, B, f1, K1, data[ 8]); subRound(B, C, D, E, A, f1, K1, data[ 9]); subRound(A, B, C, D, E, f1, K1, data[10]); subRound(E, A, B, C, D, f1, K1, data[11]); subRound(D, E, A, B, C, f1, K1, data[12]); subRound(C, D, E, A, B, f1, K1, data[13]); subRound(B, C, D, E, A, f1, K1, data[14]); subRound(A, B, C, D, E, f1, K1, data[15]); subRound(E, A, B, C, D, f1, K1, expand(data, 16)); subRound(D, E, A, B, C, f1, K1, expand(data, 17)); subRound(C, D, E, A, B, f1, K1, expand(data, 18)); subRound(B, C, D, E, A, f1, K1, expand(data, 19)); subRound(A, B, C, D, E, f2, K2, expand(data, 20)); subRound(E, A, B, C, D, f2, K2, expand(data, 21)); subRound(D, E, A, B, C, f2, K2, expand(data, 22)); subRound(C, D, E, A, B, f2, K2, expand(data, 23)); subRound(B, C, D, E, A, f2, K2, expand(data, 24)); subRound(A, B, C, D, E, f2, K2, expand(data, 25)); subRound(E, A, B, C, D, f2, K2, expand(data, 26)); subRound(D, E, A, B, C, f2, K2, expand(data, 27)); subRound(C, D, E, A, B, f2, K2, expand(data, 28)); subRound(B, C, D, E, A, f2, K2, expand(data, 29)); subRound(A, B, C, D, E, f2, K2, expand(data, 30)); subRound(E, A, B, C, D, f2, K2, expand(data, 31)); subRound(D, E, A, B, C, f2, K2, expand(data, 32)); subRound(C, D, E, A, B, f2, K2, expand(data, 33)); subRound(B, C, D, E, A, f2, K2, expand(data, 34)); subRound(A, B, C, D, E, f2, K2, expand(data, 35)); subRound(E, A, B, C, D, f2, K2, expand(data, 36)); subRound(D, E, A, B, C, f2, K2, expand(data, 37)); subRound(C, D, E, A, B, f2, K2, expand(data, 38)); subRound(B, C, D, E, A, f2, K2, expand(data, 39)); subRound(A, B, C, D, E, f3, K3, expand(data, 40)); subRound(E, A, B, C, D, f3, K3, expand(data, 41)); subRound(D, E, A, B, C, f3, K3, expand(data, 42)); subRound(C, D, E, A, B, f3, K3, expand(data, 43)); subRound(B, C, D, E, A, f3, K3, expand(data, 44)); subRound(A, B, C, D, E, f3, K3, expand(data, 45)); subRound(E, A, B, C, D, f3, K3, expand(data, 46)); subRound(D, E, A, B, C, f3, K3, expand(data, 47)); subRound(C, D, E, A, B, f3, K3, expand(data, 48)); subRound(B, C, D, E, A, f3, K3, expand(data, 49)); subRound(A, B, C, D, E, f3, K3, expand(data, 50)); subRound(E, A, B, C, D, f3, K3, expand(data, 51)); subRound(D, E, A, B, C, f3, K3, expand(data, 52)); subRound(C, D, E, A, B, f3, K3, expand(data, 53)); subRound(B, C, D, E, A, f3, K3, expand(data, 54)); subRound(A, B, C, D, E, f3, K3, expand(data, 55)); subRound(E, A, B, C, D, f3, K3, expand(data, 56)); subRound(D, E, A, B, C, f3, K3, expand(data, 57)); subRound(C, D, E, A, B, f3, K3, expand(data, 58)); subRound(B, C, D, E, A, f3, K3, expand(data, 59)); subRound(A, B, C, D, E, f4, K4, expand(data, 60)); subRound(E, A, B, C, D, f4, K4, expand(data, 61)); subRound(D, E, A, B, C, f4, K4, expand(data, 62)); subRound(C, D, E, A, B, f4, K4, expand(data, 63)); subRound(B, C, D, E, A, f4, K4, expand(data, 64)); subRound(A, B, C, D, E, f4, K4, expand(data, 65)); subRound(E, A, B, C, D, f4, K4, expand(data, 66)); subRound(D, E, A, B, C, f4, K4, expand(data, 67)); subRound(C, D, E, A, B, f4, K4, expand(data, 68)); subRound(B, C, D, E, A, f4, K4, expand(data, 69)); subRound(A, B, C, D, E, f4, K4, expand(data, 70)); subRound(E, A, B, C, D, f4, K4, expand(data, 71)); subRound(D, E, A, B, C, f4, K4, expand(data, 72)); subRound(C, D, E, A, B, f4, K4, expand(data, 73)); subRound(B, C, D, E, A, f4, K4, expand(data, 74)); subRound(A, B, C, D, E, f4, K4, expand(data, 75)); subRound(E, A, B, C, D, f4, K4, expand(data, 76)); subRound(D, E, A, B, C, f4, K4, expand(data, 77)); subRound(C, D, E, A, B, f4, K4, expand(data, 78)); subRound(B, C, D, E, A, f4, K4, expand(data, 79)); /* Build message digest */ state[0] += A; state[1] += B; state[2] += C; state[3] += D; state[4] += E; } void sha1_init(sha1_context *context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count = 0; context->index = 0; } void sha1_update(sha1_context *context, const uint8_t *data, size_t length) { if (context->index) { unsigned int left = SHA1_BLOCK_SIZE - context->index; if (length < left) { memcpy(context->block + context->index, data, length); context->index += length; return; } else { memcpy(context->block + context->index, data, left); sha1_compress(context->state, context->block); context->count++; data += left; length -= left; } } while (length >= SHA1_BLOCK_SIZE) { sha1_compress(context->state, data); context->count++; data += SHA1_BLOCK_SIZE; length -= SHA1_BLOCK_SIZE; } memcpy(context->block, data, length); context->index = length; } void sha1_digest(sha1_context *context, uint8_t *digest) { uint64_t bit_count; unsigned int i; i = context->index; context->block[i++] = 0x80; if (i > SHA1_BLOCK_SIZE - 8) { // no room for length in this block. process it, then pad another one memset(context->block + i, 0, SHA1_BLOCK_SIZE - i); sha1_compress(context->state, context->block); memset(context->block, 0, SHA1_BLOCK_SIZE - 8); } else { memset(context->block + i, 0, SHA1_BLOCK_SIZE - 8 - i); } bit_count = (context->count << 9) | (context->index << 3); context->block[56] = (bit_count >> 56) & 0xff; context->block[57] = (bit_count >> 48) & 0xff; context->block[58] = (bit_count >> 40) & 0xff; context->block[59] = (bit_count >> 32) & 0xff; context->block[60] = (bit_count >> 24) & 0xff; context->block[61] = (bit_count >> 16) & 0xff; context->block[62] = (bit_count >> 8) & 0xff; context->block[63] = (bit_count ) & 0xff; sha1_compress(context->state, context->block); digest[ 0] = (context->state[0] >> 24) & 0xff; digest[ 1] = (context->state[0] >> 16) & 0xff; digest[ 2] = (context->state[0] >> 8) & 0xff; digest[ 3] = (context->state[0] ) & 0xff; digest[ 4] = (context->state[1] >> 24) & 0xff; digest[ 5] = (context->state[1] >> 16) & 0xff; digest[ 6] = (context->state[1] >> 8) & 0xff; digest[ 7] = (context->state[1] ) & 0xff; digest[ 8] = (context->state[2] >> 24) & 0xff; digest[ 9] = (context->state[2] >> 16) & 0xff; digest[10] = (context->state[2] >> 8) & 0xff; digest[11] = (context->state[2] ) & 0xff; digest[12] = (context->state[3] >> 24) & 0xff; digest[13] = (context->state[3] >> 16) & 0xff; digest[14] = (context->state[3] >> 8) & 0xff; digest[15] = (context->state[3] ) & 0xff; digest[16] = (context->state[4] >> 24) & 0xff; digest[17] = (context->state[4] >> 16) & 0xff; digest[18] = (context->state[4] >> 8) & 0xff; digest[19] = (context->state[4] ) & 0xff; } #endif /* __SHA1_H */ ================================================ FILE: sipsimple/util/_sha1.pyx ================================================ # cython: language_level=2 __all__ = ['sha1'] from libc.stddef cimport size_t from libc.stdint cimport uint8_t, uint32_t, uint64_t from libc.string cimport memcpy from cpython.buffer cimport PyObject_CheckBuffer, PyObject_GetBuffer, PyBuffer_Release from cpython.string cimport PyString_FromStringAndSize, PyString_AS_STRING from cpython.unicode cimport PyUnicode_Check cdef extern from "_sha1.h": enum: SHA1_BLOCK_SIZE = 64 SHA1_DIGEST_SIZE = 20 ctypedef struct sha1_context: uint32_t state[SHA1_DIGEST_SIZE/4] # state variables uint64_t count # 64-bit block count uint8_t block[SHA1_BLOCK_SIZE] # data block buffer uint32_t index # index into buffer cdef void sha1_init(sha1_context *context) cdef void sha1_update(sha1_context *context, const uint8_t *data, size_t length) cdef void sha1_digest(sha1_context *context, uint8_t *digest) cdef class sha1(object): cdef sha1_context context def __cinit__(self, *args, **kw): sha1_init(&self.context) def __init__(self, data=''): self.update(data) property name: def __get__(self): return 'sha1' property block_size: def __get__(self): return SHA1_BLOCK_SIZE property digest_size: def __get__(self): return SHA1_DIGEST_SIZE def __reduce__(self): state_variables = [self.context.state[i] for i in range(sizeof(self.context.state)/4)] block = PyString_FromStringAndSize(self.context.block, self.context.index) return self.__class__, (), (state_variables, self.context.count, block) def __setstate__(self, state): state_variables, count, block = state for i, number in enumerate(state_variables): self.context.state[i] = number self.context.count = count self.context.index = len(block) memcpy(self.context.block, PyString_AS_STRING(block), self.context.index) def copy(self): cdef sha1 instance = self.__class__() instance.context = self.context return instance def update(self, data): cdef Py_buffer view if PyObject_CheckBuffer(data): PyObject_GetBuffer(data, &view, 0) if view.ndim > 1: raise BufferError('Buffer must be single dimension') sha1_update(&self.context, view.buf, view.len) PyBuffer_Release(&view) elif PyUnicode_Check(data): raise TypeError('Unicode-objects must be encoded before hashing') else: raise TypeError('object supporting the buffer API required') def digest(self): cdef sha1_context context_copy cdef uint8_t digest[SHA1_DIGEST_SIZE] context_copy = self.context sha1_digest(&context_copy, digest) return PyString_FromStringAndSize(digest, SHA1_DIGEST_SIZE) def hexdigest(self): return self.digest().encode('hex') ================================================ FILE: sipsimple/video.py ================================================ """Video support""" from __future__ import absolute_import __all__ = ['IVideoProducer', 'VideoDevice', 'VideoError'] from application.notification import NotificationCenter, NotificationData from zope.interface import Attribute, Interface, implements from sipsimple.core import SIPCoreError, VideoCamera class IVideoProducer(Interface): """ Interface describing an object which can produce video data. All attributes of this interface are read-only. """ producer = Attribute("The core producer object which can be connected to a consumer") class VideoError(Exception): pass class VideoDevice(object): implements(IVideoProducer) def __init__(self, device_name, resolution, framerate): self._camera = self._open_camera(device_name, resolution, framerate) self._camera.start() def _open_camera(self, device_name, resolution, framerate): try: return VideoCamera(device_name, resolution, framerate) except SIPCoreError: try: return VideoCamera(u'system_default', resolution, framerate) except SIPCoreError: return VideoCamera(None, resolution, framerate) def set_camera(self, device_name, resolution, framerate): old_camera = self._camera old_camera.close() new_camera = self._open_camera(device_name, resolution, framerate) if not self.muted: new_camera.start() self._camera = new_camera notification_center = NotificationCenter() notification_center.post_notification('VideoDeviceDidChangeCamera', sender=self, data=NotificationData(old_camera=old_camera, new_camera=new_camera)) @property def producer(self): return self._camera @property def name(self): return self._camera.name @property def real_name(self): return self._camera.real_name @property def muted(self): return self.__dict__.get('muted', False) @muted.setter def muted(self, value): if not isinstance(value, bool): raise ValueError('illegal value for muted property: %r' % (value,)) if value == self.muted: return if value: self._camera.stop() else: self._camera.start() self.__dict__['muted'] = value